From ffe24f128e5580f909816b748d3d2a7038793212 Mon Sep 17 00:00:00 2001 From: Vlad Durnea Date: Thu, 2 Apr 2026 11:51:49 +0300 Subject: [PATCH] claude code drop from npm --- QueryEngine.ts | 1295 ++++ README.md | 463 ++ Task.ts | 125 + Tool.ts | 792 +++ assistant/sessionHistory.ts | 87 + bootstrap/state.ts | 1758 ++++++ bridge/bridgeApi.ts | 539 ++ bridge/bridgeConfig.ts | 48 + bridge/bridgeDebug.ts | 135 + bridge/bridgeEnabled.ts | 202 + bridge/bridgeMain.ts | 2999 +++++++++ bridge/bridgeMessaging.ts | 461 ++ bridge/bridgePermissionCallbacks.ts | 43 + bridge/bridgePointer.ts | 210 + bridge/bridgeStatusUtil.ts | 163 + bridge/bridgeUI.ts | 530 ++ bridge/capacityWake.ts | 56 + bridge/codeSessionApi.ts | 168 + bridge/createSession.ts | 384 ++ bridge/debugUtils.ts | 141 + bridge/envLessBridgeConfig.ts | 165 + bridge/flushGate.ts | 71 + bridge/inboundAttachments.ts | 175 + bridge/inboundMessages.ts | 80 + bridge/initReplBridge.ts | 569 ++ bridge/jwtUtils.ts | 256 + bridge/pollConfig.ts | 110 + bridge/pollConfigDefaults.ts | 82 + bridge/remoteBridgeCore.ts | 1008 +++ bridge/replBridge.ts | 2406 +++++++ bridge/replBridgeHandle.ts | 36 + bridge/replBridgeTransport.ts | 370 ++ bridge/sessionIdCompat.ts | 57 + bridge/sessionRunner.ts | 550 ++ bridge/trustedDevice.ts | 210 + bridge/types.ts | 262 + bridge/workSecret.ts | 127 + buddy/CompanionSprite.tsx | 371 ++ buddy/companion.ts | 133 + buddy/prompt.ts | 36 + buddy/sprites.ts | 514 ++ buddy/types.ts | 148 + buddy/useBuddyNotification.tsx | 98 + cli/exit.ts | 31 + cli/handlers/agents.ts | 70 + cli/handlers/auth.ts | 330 + cli/handlers/autoMode.ts | 170 + cli/handlers/mcp.tsx | 362 ++ cli/handlers/plugins.ts | 878 +++ cli/handlers/util.tsx | 110 + cli/ndjsonSafeStringify.ts | 32 + cli/print.ts | 5594 +++++++++++++++++ cli/remoteIO.ts | 255 + cli/structuredIO.ts | 859 +++ cli/transports/HybridTransport.ts | 282 + cli/transports/SSETransport.ts | 711 +++ cli/transports/SerialBatchEventUploader.ts | 275 + cli/transports/WebSocketTransport.ts | 800 +++ cli/transports/WorkerStateUploader.ts | 131 + cli/transports/ccrClient.ts | 998 +++ cli/transports/transportUtils.ts | 45 + cli/update.ts | 422 ++ commands.ts | 754 +++ commands/add-dir/add-dir.tsx | 126 + commands/add-dir/index.ts | 11 + commands/add-dir/validation.ts | 110 + commands/advisor.ts | 109 + commands/agents/agents.tsx | 12 + commands/agents/index.ts | 10 + commands/ant-trace/index.js | 1 + commands/autofix-pr/index.js | 1 + commands/backfill-sessions/index.js | 1 + commands/branch/branch.ts | 296 + commands/branch/index.ts | 14 + commands/break-cache/index.js | 1 + commands/bridge-kick.ts | 200 + commands/bridge/bridge.tsx | 509 ++ commands/bridge/index.ts | 26 + commands/brief.ts | 130 + commands/btw/btw.tsx | 243 + commands/btw/index.ts | 13 + commands/bughunter/index.js | 1 + commands/chrome/chrome.tsx | 285 + commands/chrome/index.ts | 13 + commands/clear/caches.ts | 144 + commands/clear/clear.ts | 7 + commands/clear/conversation.ts | 251 + commands/clear/index.ts | 19 + commands/color/color.ts | 93 + commands/color/index.ts | 16 + commands/commit-push-pr.ts | 158 + commands/commit.ts | 92 + commands/compact/compact.ts | 287 + commands/compact/index.ts | 15 + commands/config/config.tsx | 7 + commands/config/index.ts | 11 + commands/context/context-noninteractive.ts | 325 + commands/context/context.tsx | 64 + commands/context/index.ts | 24 + commands/copy/copy.tsx | 371 ++ commands/copy/index.ts | 15 + commands/cost/cost.ts | 24 + commands/cost/index.ts | 23 + commands/createMovedToPluginCommand.ts | 65 + commands/ctx_viz/index.js | 1 + commands/debug-tool-call/index.js | 1 + commands/desktop/desktop.tsx | 9 + commands/desktop/index.ts | 26 + commands/diff/diff.tsx | 9 + commands/diff/index.ts | 8 + commands/doctor/doctor.tsx | 7 + commands/doctor/index.ts | 12 + commands/effort/effort.tsx | 183 + commands/effort/index.ts | 13 + commands/env/index.js | 1 + commands/exit/exit.tsx | 33 + commands/exit/index.ts | 12 + commands/export/export.tsx | 91 + commands/export/index.ts | 11 + commands/extra-usage/extra-usage-core.ts | 118 + .../extra-usage/extra-usage-noninteractive.ts | 16 + commands/extra-usage/extra-usage.tsx | 17 + commands/extra-usage/index.ts | 31 + commands/fast/fast.tsx | 269 + commands/fast/index.ts | 26 + commands/feedback/feedback.tsx | 25 + commands/feedback/index.ts | 26 + commands/files/files.ts | 19 + commands/files/index.ts | 12 + commands/good-claude/index.js | 1 + commands/heapdump/heapdump.ts | 17 + commands/heapdump/index.ts | 12 + commands/help/help.tsx | 11 + commands/help/index.ts | 10 + commands/hooks/hooks.tsx | 13 + commands/hooks/index.ts | 11 + commands/ide/ide.tsx | 646 ++ commands/ide/index.ts | 11 + commands/init-verifiers.ts | 262 + commands/init.ts | 256 + commands/insights.ts | 3200 ++++++++++ commands/install-github-app/ApiKeyStep.tsx | 231 + .../CheckExistingSecretStep.tsx | 190 + .../install-github-app/CheckGitHubStep.tsx | 15 + .../install-github-app/ChooseRepoStep.tsx | 211 + commands/install-github-app/CreatingStep.tsx | 65 + commands/install-github-app/ErrorStep.tsx | 85 + .../ExistingWorkflowStep.tsx | 103 + .../install-github-app/InstallAppStep.tsx | 94 + commands/install-github-app/OAuthFlowStep.tsx | 276 + commands/install-github-app/SuccessStep.tsx | 96 + commands/install-github-app/WarningsStep.tsx | 73 + commands/install-github-app/index.ts | 13 + .../install-github-app/install-github-app.tsx | 587 ++ .../install-github-app/setupGitHubActions.ts | 325 + commands/install-slack-app/index.ts | 12 + .../install-slack-app/install-slack-app.ts | 30 + commands/install.tsx | 300 + commands/issue/index.js | 1 + commands/keybindings/index.ts | 13 + commands/keybindings/keybindings.ts | 53 + commands/login/index.ts | 14 + commands/login/login.tsx | 104 + commands/logout/index.ts | 10 + commands/logout/logout.tsx | 82 + commands/mcp/addCommand.ts | 280 + commands/mcp/index.ts | 12 + commands/mcp/mcp.tsx | 85 + commands/mcp/xaaIdpCommand.ts | 266 + commands/memory/index.ts | 10 + commands/memory/memory.tsx | 90 + commands/mobile/index.ts | 11 + commands/mobile/mobile.tsx | 274 + commands/mock-limits/index.js | 1 + commands/model/index.ts | 16 + commands/model/model.tsx | 297 + commands/oauth-refresh/index.js | 1 + commands/onboarding/index.js | 1 + commands/output-style/index.ts | 11 + commands/output-style/output-style.tsx | 7 + commands/passes/index.ts | 22 + commands/passes/passes.tsx | 24 + commands/perf-issue/index.js | 1 + commands/permissions/index.ts | 11 + commands/permissions/permissions.tsx | 10 + commands/plan/index.ts | 11 + commands/plan/plan.tsx | 122 + commands/plugin/AddMarketplace.tsx | 162 + commands/plugin/BrowseMarketplace.tsx | 802 +++ commands/plugin/DiscoverPlugins.tsx | 781 +++ commands/plugin/ManageMarketplaces.tsx | 838 +++ commands/plugin/ManagePlugins.tsx | 2215 +++++++ commands/plugin/PluginErrors.tsx | 124 + commands/plugin/PluginOptionsDialog.tsx | 357 ++ commands/plugin/PluginOptionsFlow.tsx | 135 + commands/plugin/PluginSettings.tsx | 1072 ++++ commands/plugin/PluginTrustWarning.tsx | 32 + commands/plugin/UnifiedInstalledCell.tsx | 565 ++ commands/plugin/ValidatePlugin.tsx | 98 + commands/plugin/index.tsx | 11 + commands/plugin/parseArgs.ts | 103 + commands/plugin/plugin.tsx | 7 + commands/plugin/pluginDetailsHelpers.tsx | 117 + commands/plugin/usePagination.ts | 171 + commands/pr_comments/index.ts | 50 + commands/privacy-settings/index.ts | 14 + .../privacy-settings/privacy-settings.tsx | 58 + commands/rate-limit-options/index.ts | 19 + .../rate-limit-options/rate-limit-options.tsx | 210 + commands/release-notes/index.ts | 11 + commands/release-notes/release-notes.ts | 50 + commands/reload-plugins/index.ts | 18 + commands/reload-plugins/reload-plugins.ts | 61 + commands/remote-env/index.ts | 15 + commands/remote-env/remote-env.tsx | 7 + commands/remote-setup/api.ts | 182 + commands/remote-setup/index.ts | 20 + commands/remote-setup/remote-setup.tsx | 187 + commands/rename/generateSessionName.ts | 67 + commands/rename/index.ts | 12 + commands/rename/rename.ts | 87 + commands/reset-limits/index.js | 4 + commands/resume/index.ts | 12 + commands/resume/resume.tsx | 275 + commands/review.ts | 57 + commands/review/UltrareviewOverageDialog.tsx | 96 + commands/review/reviewRemote.ts | 316 + commands/review/ultrareviewCommand.tsx | 58 + commands/review/ultrareviewEnabled.ts | 14 + commands/rewind/index.ts | 13 + commands/rewind/rewind.ts | 13 + commands/sandbox-toggle/index.ts | 50 + commands/sandbox-toggle/sandbox-toggle.tsx | 83 + commands/security-review.ts | 243 + commands/session/index.ts | 16 + commands/session/session.tsx | 140 + commands/share/index.js | 1 + commands/skills/index.ts | 10 + commands/skills/skills.tsx | 8 + commands/stats/index.ts | 10 + commands/stats/stats.tsx | 7 + commands/status/index.ts | 12 + commands/status/status.tsx | 8 + commands/statusline.tsx | 24 + commands/stickers/index.ts | 11 + commands/stickers/stickers.ts | 16 + commands/summary/index.js | 1 + commands/tag/index.ts | 12 + commands/tag/tag.tsx | 215 + commands/tasks/index.ts | 11 + commands/tasks/tasks.tsx | 8 + commands/teleport/index.js | 1 + commands/terminalSetup/index.ts | 23 + commands/terminalSetup/terminalSetup.tsx | 531 ++ commands/theme/index.ts | 10 + commands/theme/theme.tsx | 57 + commands/thinkback-play/index.ts | 17 + commands/thinkback-play/thinkback-play.ts | 43 + commands/thinkback/index.ts | 13 + commands/thinkback/thinkback.tsx | 554 ++ commands/ultraplan.tsx | 471 ++ commands/upgrade/index.ts | 16 + commands/upgrade/upgrade.tsx | 38 + commands/usage/index.ts | 9 + commands/usage/usage.tsx | 7 + commands/version.ts | 22 + commands/vim/index.ts | 11 + commands/vim/vim.ts | 38 + commands/voice/index.ts | 20 + commands/voice/voice.ts | 150 + components/AgentProgressLine.tsx | 136 + components/App.tsx | 56 + components/ApproveApiKey.tsx | 123 + components/AutoModeOptInDialog.tsx | 142 + components/AutoUpdater.tsx | 198 + components/AutoUpdaterWrapper.tsx | 91 + components/AwsAuthStatusBox.tsx | 82 + components/BaseTextInput.tsx | 136 + components/BashModeProgress.tsx | 56 + components/BridgeDialog.tsx | 401 ++ components/BypassPermissionsModeDialog.tsx | 87 + components/ChannelDowngradeDialog.tsx | 102 + components/ClaudeCodeHint/PluginHintMenu.tsx | 78 + components/ClaudeInChromeOnboarding.tsx | 121 + components/ClaudeMdExternalIncludesDialog.tsx | 137 + components/ClickableImageRef.tsx | 73 + components/CompactSummary.tsx | 118 + components/ConfigurableShortcutHint.tsx | 57 + components/ConsoleOAuthFlow.tsx | 631 ++ components/ContextSuggestions.tsx | 47 + components/ContextVisualization.tsx | 489 ++ components/CoordinatorAgentStatus.tsx | 273 + components/CostThresholdDialog.tsx | 50 + components/CtrlOToExpand.tsx | 51 + components/CustomSelect/SelectMulti.tsx | 213 + components/CustomSelect/index.ts | 3 + components/CustomSelect/option-map.ts | 50 + .../CustomSelect/select-input-option.tsx | 488 ++ components/CustomSelect/select-option.tsx | 68 + components/CustomSelect/select.tsx | 690 ++ .../CustomSelect/use-multi-select-state.ts | 414 ++ components/CustomSelect/use-select-input.ts | 287 + .../CustomSelect/use-select-navigation.ts | 653 ++ components/CustomSelect/use-select-state.ts | 157 + components/DesktopHandoff.tsx | 193 + .../DesktopUpsell/DesktopUpsellStartup.tsx | 171 + components/DevBar.tsx | 49 + components/DevChannelsDialog.tsx | 105 + components/DiagnosticsDisplay.tsx | 95 + components/EffortCallout.tsx | 265 + components/EffortIndicator.ts | 42 + components/ExitFlow.tsx | 48 + components/ExportDialog.tsx | 128 + components/FallbackToolUseErrorMessage.tsx | 116 + components/FallbackToolUseRejectedMessage.tsx | 16 + components/FastIcon.tsx | 46 + components/Feedback.tsx | 592 ++ components/FeedbackSurvey/FeedbackSurvey.tsx | 174 + .../FeedbackSurvey/FeedbackSurveyView.tsx | 108 + .../FeedbackSurvey/TranscriptSharePrompt.tsx | 88 + .../FeedbackSurvey/submitTranscriptShare.ts | 112 + .../FeedbackSurvey/useDebouncedDigitInput.ts | 82 + .../FeedbackSurvey/useFeedbackSurvey.tsx | 296 + components/FeedbackSurvey/useMemorySurvey.tsx | 213 + .../FeedbackSurvey/usePostCompactSurvey.tsx | 206 + components/FeedbackSurvey/useSurveyState.tsx | 100 + components/FileEditToolDiff.tsx | 181 + components/FileEditToolUpdatedMessage.tsx | 124 + components/FileEditToolUseRejectedMessage.tsx | 170 + components/FilePathLink.tsx | 43 + components/FullscreenLayout.tsx | 637 ++ components/GlobalSearchDialog.tsx | 343 + components/HelpV2/Commands.tsx | 82 + components/HelpV2/General.tsx | 23 + components/HelpV2/HelpV2.tsx | 184 + components/HighlightedCode.tsx | 190 + components/HighlightedCode/Fallback.tsx | 193 + components/HistorySearchDialog.tsx | 118 + components/IdeAutoConnectDialog.tsx | 154 + components/IdeOnboardingDialog.tsx | 167 + components/IdeStatusIndicator.tsx | 58 + components/IdleReturnDialog.tsx | 118 + components/InterruptedByUser.tsx | 15 + components/InvalidConfigDialog.tsx | 156 + components/InvalidSettingsDialog.tsx | 89 + components/KeybindingWarnings.tsx | 55 + components/LanguagePicker.tsx | 86 + components/LogSelector.tsx | 1575 +++++ components/LogoV2/AnimatedAsterisk.tsx | 50 + components/LogoV2/AnimatedClawd.tsx | 124 + components/LogoV2/ChannelsNotice.tsx | 266 + components/LogoV2/Clawd.tsx | 240 + components/LogoV2/CondensedLogo.tsx | 161 + components/LogoV2/EmergencyTip.tsx | 58 + components/LogoV2/Feed.tsx | 112 + components/LogoV2/FeedColumn.tsx | 59 + components/LogoV2/GuestPassesUpsell.tsx | 70 + components/LogoV2/LogoV2.tsx | 543 ++ components/LogoV2/Opus1mMergeNotice.tsx | 55 + components/LogoV2/OverageCreditUpsell.tsx | 166 + components/LogoV2/VoiceModeNotice.tsx | 68 + components/LogoV2/WelcomeV2.tsx | 433 ++ components/LogoV2/feedConfigs.tsx | 92 + .../LspRecommendationMenu.tsx | 88 + components/MCPServerApprovalDialog.tsx | 115 + components/MCPServerDesktopImportDialog.tsx | 203 + components/MCPServerDialogCopy.tsx | 15 + components/MCPServerMultiselectDialog.tsx | 133 + .../ManagedSettingsSecurityDialog.tsx | 149 + .../ManagedSettingsSecurityDialog/utils.ts | 144 + components/Markdown.tsx | 236 + components/MarkdownTable.tsx | 322 + components/MemoryUsageIndicator.tsx | 37 + components/Message.tsx | 627 ++ components/MessageModel.tsx | 43 + components/MessageResponse.tsx | 78 + components/MessageRow.tsx | 383 ++ components/MessageSelector.tsx | 831 +++ components/MessageTimestamp.tsx | 63 + components/Messages.tsx | 834 +++ components/ModelPicker.tsx | 448 ++ components/NativeAutoUpdater.tsx | 193 + .../NotebookEditToolUseRejectedMessage.tsx | 92 + components/OffscreenFreeze.tsx | 44 + components/Onboarding.tsx | 244 + components/OutputStylePicker.tsx | 112 + components/PackageManagerAutoUpdater.tsx | 104 + components/Passes/Passes.tsx | 184 + components/PrBadge.tsx | 97 + components/PressEnterToContinue.tsx | 15 + components/PromptInput/HistorySearchInput.tsx | 51 + components/PromptInput/IssueFlagBanner.tsx | 12 + components/PromptInput/Notifications.tsx | 332 + components/PromptInput/PromptInput.tsx | 2339 +++++++ components/PromptInput/PromptInputFooter.tsx | 191 + .../PromptInput/PromptInputFooterLeftSide.tsx | 517 ++ .../PromptInputFooterSuggestions.tsx | 293 + .../PromptInput/PromptInputHelpMenu.tsx | 358 ++ .../PromptInput/PromptInputModeIndicator.tsx | 93 + .../PromptInput/PromptInputQueuedCommands.tsx | 117 + .../PromptInput/PromptInputStashNotice.tsx | 25 + .../PromptInput/SandboxPromptFooterHint.tsx | 64 + components/PromptInput/ShimmeredInput.tsx | 143 + components/PromptInput/VoiceIndicator.tsx | 137 + components/PromptInput/inputModes.ts | 33 + components/PromptInput/inputPaste.ts | 90 + .../PromptInput/useMaybeTruncateInput.ts | 58 + .../PromptInput/usePromptInputPlaceholder.ts | 76 + components/PromptInput/useShowFastIconHint.ts | 31 + components/PromptInput/useSwarmBanner.ts | 155 + components/PromptInput/utils.ts | 60 + components/QuickOpenDialog.tsx | 244 + components/RemoteCallout.tsx | 76 + components/RemoteEnvironmentDialog.tsx | 340 + components/ResumeTask.tsx | 268 + components/SandboxViolationExpandedView.tsx | 99 + components/ScrollKeybindingHandler.tsx | 1012 +++ components/SearchBox.tsx | 72 + components/SentryErrorBoundary.ts | 28 + components/SessionBackgroundHint.tsx | 108 + components/SessionPreview.tsx | 194 + components/Settings/Config.tsx | 1822 ++++++ components/Settings/Settings.tsx | 137 + components/Settings/Status.tsx | 241 + components/Settings/Usage.tsx | 377 ++ components/ShowInIDEPrompt.tsx | 170 + components/SkillImprovementSurvey.tsx | 152 + components/Spinner.tsx | 562 ++ components/Spinner/FlashingChar.tsx | 61 + components/Spinner/GlimmerMessage.tsx | 328 + components/Spinner/ShimmerChar.tsx | 36 + components/Spinner/SpinnerAnimationRow.tsx | 265 + components/Spinner/SpinnerGlyph.tsx | 80 + components/Spinner/TeammateSpinnerLine.tsx | 233 + components/Spinner/TeammateSpinnerTree.tsx | 272 + components/Spinner/index.ts | 10 + components/Spinner/teammateSelectHint.ts | 1 + components/Spinner/useShimmerAnimation.ts | 31 + components/Spinner/useStalledAnimation.ts | 75 + components/Spinner/utils.ts | 84 + components/Stats.tsx | 1228 ++++ components/StatusLine.tsx | 324 + components/StatusNotices.tsx | 55 + components/StructuredDiff.tsx | 190 + components/StructuredDiff/Fallback.tsx | 487 ++ components/StructuredDiff/colorDiff.ts | 37 + components/StructuredDiffList.tsx | 30 + components/TagTabs.tsx | 139 + components/TaskListV2.tsx | 378 ++ components/TeammateViewHeader.tsx | 82 + components/TeleportError.tsx | 189 + components/TeleportProgress.tsx | 140 + components/TeleportRepoMismatchDialog.tsx | 104 + components/TeleportResumeWrapper.tsx | 167 + components/TeleportStash.tsx | 116 + components/TextInput.tsx | 124 + components/ThemePicker.tsx | 333 + components/ThinkingToggle.tsx | 153 + components/TokenWarning.tsx | 179 + components/ToolUseLoader.tsx | 42 + components/TrustDialog/TrustDialog.tsx | 290 + components/TrustDialog/utils.ts | 245 + components/ValidationErrorsList.tsx | 148 + components/VimTextInput.tsx | 140 + components/VirtualMessageList.tsx | 1082 ++++ components/WorkflowMultiselectDialog.tsx | 128 + components/WorktreeExitDialog.tsx | 231 + components/agents/AgentDetail.tsx | 220 + components/agents/AgentEditor.tsx | 178 + components/agents/AgentNavigationFooter.tsx | 26 + components/agents/AgentsList.tsx | 440 ++ components/agents/AgentsMenu.tsx | 800 +++ components/agents/ColorPicker.tsx | 112 + components/agents/ModelSelector.tsx | 68 + components/agents/ToolSelector.tsx | 562 ++ components/agents/agentFileUtils.ts | 272 + components/agents/generateAgent.ts | 197 + .../new-agent-creation/CreateAgentWizard.tsx | 97 + .../wizard-steps/ColorStep.tsx | 84 + .../wizard-steps/ConfirmStep.tsx | 378 ++ .../wizard-steps/ConfirmStepWrapper.tsx | 74 + .../wizard-steps/DescriptionStep.tsx | 123 + .../wizard-steps/GenerateStep.tsx | 143 + .../wizard-steps/LocationStep.tsx | 80 + .../wizard-steps/MemoryStep.tsx | 113 + .../wizard-steps/MethodStep.tsx | 80 + .../wizard-steps/ModelStep.tsx | 52 + .../wizard-steps/PromptStep.tsx | 128 + .../wizard-steps/ToolsStep.tsx | 61 + .../wizard-steps/TypeStep.tsx | 103 + components/agents/types.ts | 27 + components/agents/utils.ts | 18 + components/agents/validateAgent.ts | 109 + components/design-system/Byline.tsx | 77 + components/design-system/Dialog.tsx | 138 + components/design-system/Divider.tsx | 149 + components/design-system/FuzzyPicker.tsx | 312 + .../design-system/KeyboardShortcutHint.tsx | 81 + components/design-system/ListItem.tsx | 244 + components/design-system/LoadingState.tsx | 94 + components/design-system/Pane.tsx | 77 + components/design-system/ProgressBar.tsx | 86 + components/design-system/Ratchet.tsx | 80 + components/design-system/StatusIcon.tsx | 95 + components/design-system/Tabs.tsx | 340 + components/design-system/ThemeProvider.tsx | 170 + components/design-system/ThemedBox.tsx | 156 + components/design-system/ThemedText.tsx | 124 + components/design-system/color.ts | 30 + components/diff/DiffDetailView.tsx | 281 + components/diff/DiffDialog.tsx | 383 ++ components/diff/DiffFileList.tsx | 292 + components/grove/Grove.tsx | 463 ++ components/hooks/HooksConfigMenu.tsx | 578 ++ components/hooks/PromptDialog.tsx | 90 + components/hooks/SelectEventMode.tsx | 127 + components/hooks/SelectHookMode.tsx | 112 + components/hooks/SelectMatcherMode.tsx | 144 + components/hooks/ViewHookMode.tsx | 199 + components/mcp/CapabilitiesSection.tsx | 61 + components/mcp/ElicitationDialog.tsx | 1169 ++++ components/mcp/MCPAgentServerMenu.tsx | 183 + components/mcp/MCPListPanel.tsx | 504 ++ components/mcp/MCPReconnect.tsx | 167 + components/mcp/MCPRemoteServerMenu.tsx | 649 ++ components/mcp/MCPSettings.tsx | 398 ++ components/mcp/MCPStdioServerMenu.tsx | 177 + components/mcp/MCPToolDetailView.tsx | 212 + components/mcp/MCPToolListView.tsx | 141 + components/mcp/McpParsingWarnings.tsx | 213 + components/mcp/index.ts | 9 + components/mcp/utils/reconnectHelpers.tsx | 49 + components/memory/MemoryFileSelector.tsx | 438 ++ .../memory/MemoryUpdateNotification.tsx | 45 + components/messageActions.tsx | 450 ++ components/messages/AdvisorMessage.tsx | 158 + .../AssistantRedactedThinkingMessage.tsx | 31 + components/messages/AssistantTextMessage.tsx | 270 + .../messages/AssistantThinkingMessage.tsx | 86 + .../messages/AssistantToolUseMessage.tsx | 368 ++ components/messages/AttachmentMessage.tsx | 536 ++ .../messages/CollapsedReadSearchContent.tsx | 484 ++ .../messages/CompactBoundaryMessage.tsx | 18 + components/messages/GroupedToolUseContent.tsx | 58 + .../messages/HighlightedThinkingText.tsx | 162 + components/messages/HookProgressMessage.tsx | 116 + components/messages/PlanApprovalMessage.tsx | 222 + components/messages/RateLimitMessage.tsx | 161 + components/messages/ShutdownMessage.tsx | 132 + components/messages/SystemAPIErrorMessage.tsx | 141 + components/messages/SystemTextMessage.tsx | 827 +++ components/messages/TaskAssignmentMessage.tsx | 76 + .../messages/UserAgentNotificationMessage.tsx | 83 + components/messages/UserBashInputMessage.tsx | 58 + components/messages/UserBashOutputMessage.tsx | 54 + components/messages/UserChannelMessage.tsx | 137 + components/messages/UserCommandMessage.tsx | 108 + components/messages/UserImageMessage.tsx | 59 + .../UserLocalCommandOutputMessage.tsx | 167 + .../messages/UserMemoryInputMessage.tsx | 75 + components/messages/UserPlanMessage.tsx | 42 + components/messages/UserPromptMessage.tsx | 80 + .../messages/UserResourceUpdateMessage.tsx | 121 + components/messages/UserTeammateMessage.tsx | 206 + components/messages/UserTextMessage.tsx | 275 + .../RejectedPlanMessage.tsx | 31 + .../RejectedToolUseMessage.tsx | 16 + .../UserToolCanceledMessage.tsx | 16 + .../UserToolErrorMessage.tsx | 103 + .../UserToolRejectMessage.tsx | 95 + .../UserToolResultMessage.tsx | 106 + .../UserToolSuccessMessage.tsx | 104 + .../messages/UserToolResultMessage/utils.tsx | 44 + .../messages/nullRenderingAttachments.ts | 70 + components/messages/teamMemCollapsed.tsx | 140 + components/messages/teamMemSaved.ts | 19 + .../AskUserQuestionPermissionRequest.tsx | 645 ++ .../PreviewBox.tsx | 229 + .../PreviewQuestionView.tsx | 328 + .../QuestionNavigationBar.tsx | 178 + .../QuestionView.tsx | 465 ++ .../SubmitQuestionsView.tsx | 144 + .../use-multiple-choice-state.ts | 179 + .../BashPermissionRequest.tsx | 482 ++ .../bashToolUseOptions.tsx | 147 + .../ComputerUseApproval.tsx | 441 ++ .../EnterPlanModePermissionRequest.tsx | 122 + .../ExitPlanModePermissionRequest.tsx | 768 +++ .../permissions/FallbackPermissionRequest.tsx | 333 + .../FileEditPermissionRequest.tsx | 182 + .../FilePermissionDialog.tsx | 204 + .../FilePermissionDialog/ideDiffConfig.ts | 42 + .../permissionOptions.tsx | 177 + .../useFilePermissionDialog.ts | 212 + .../usePermissionHandler.ts | 185 + .../FileWritePermissionRequest.tsx | 161 + .../FileWriteToolDiff.tsx | 89 + .../FilesystemPermissionRequest.tsx | 115 + .../NotebookEditPermissionRequest.tsx | 166 + .../NotebookEditToolDiff.tsx | 235 + .../PermissionDecisionDebugInfo.tsx | 460 ++ components/permissions/PermissionDialog.tsx | 72 + .../permissions/PermissionExplanation.tsx | 272 + components/permissions/PermissionPrompt.tsx | 336 + components/permissions/PermissionRequest.tsx | 217 + .../permissions/PermissionRequestTitle.tsx | 66 + .../permissions/PermissionRuleExplanation.tsx | 121 + .../PowerShellPermissionRequest.tsx | 235 + .../powershellToolUseOptions.tsx | 91 + .../permissions/SandboxPermissionRequest.tsx | 163 + .../SedEditPermissionRequest.tsx | 230 + .../SkillPermissionRequest.tsx | 369 ++ .../WebFetchPermissionRequest.tsx | 258 + components/permissions/WorkerBadge.tsx | 49 + .../permissions/WorkerPendingPermission.tsx | 105 + components/permissions/hooks.ts | 209 + .../permissions/rules/AddPermissionRules.tsx | 180 + .../rules/AddWorkspaceDirectory.tsx | 340 + .../rules/PermissionRuleDescription.tsx | 76 + .../permissions/rules/PermissionRuleInput.tsx | 138 + .../permissions/rules/PermissionRuleList.tsx | 1179 ++++ .../permissions/rules/RecentDenialsTab.tsx | 207 + .../rules/RemoveWorkspaceDirectory.tsx | 110 + components/permissions/rules/WorkspaceTab.tsx | 150 + .../permissions/shellPermissionHelpers.tsx | 164 + .../permissions/useShellPermissionFeedback.ts | 148 + components/permissions/utils.ts | 25 + components/sandbox/SandboxConfigTab.tsx | 45 + components/sandbox/SandboxDependenciesTab.tsx | 120 + components/sandbox/SandboxDoctorSection.tsx | 46 + components/sandbox/SandboxOverridesTab.tsx | 193 + components/sandbox/SandboxSettings.tsx | 296 + components/shell/ExpandShellOutputContext.tsx | 36 + components/shell/OutputLine.tsx | 118 + components/shell/ShellProgressMessage.tsx | 150 + components/shell/ShellTimeDisplay.tsx | 74 + components/skills/SkillsMenu.tsx | 237 + components/tasks/AsyncAgentDetailDialog.tsx | 229 + components/tasks/BackgroundTask.tsx | 345 + components/tasks/BackgroundTaskStatus.tsx | 429 ++ components/tasks/BackgroundTasksDialog.tsx | 652 ++ components/tasks/DreamDetailDialog.tsx | 251 + .../tasks/InProcessTeammateDetailDialog.tsx | 266 + .../tasks/RemoteSessionDetailDialog.tsx | 904 +++ components/tasks/RemoteSessionProgress.tsx | 243 + components/tasks/ShellDetailDialog.tsx | 404 ++ components/tasks/ShellProgress.tsx | 87 + components/tasks/renderToolActivity.tsx | 33 + components/tasks/taskStatusUtils.tsx | 107 + components/teams/TeamStatus.tsx | 80 + components/teams/TeamsDialog.tsx | 715 +++ components/ui/OrderedList.tsx | 71 + components/ui/OrderedListItem.tsx | 45 + components/ui/TreeSelect.tsx | 397 ++ components/wizard/WizardDialogLayout.tsx | 65 + components/wizard/WizardNavigationFooter.tsx | 24 + components/wizard/WizardProvider.tsx | 213 + components/wizard/index.ts | 9 + components/wizard/useWizard.ts | 13 + constants/apiLimits.ts | 94 + constants/betas.ts | 52 + constants/common.ts | 33 + constants/cyberRiskInstruction.ts | 24 + constants/errorIds.ts | 15 + constants/figures.ts | 45 + constants/files.ts | 156 + constants/github-app.ts | 144 + constants/keys.ts | 11 + constants/messages.ts | 1 + constants/oauth.ts | 234 + constants/outputStyles.ts | 216 + constants/product.ts | 76 + constants/prompts.ts | 914 +++ constants/spinnerVerbs.ts | 204 + constants/system.ts | 95 + constants/systemPromptSections.ts | 68 + constants/toolLimits.ts | 56 + constants/tools.ts | 112 + constants/turnCompletionVerbs.ts | 12 + constants/xml.ts | 86 + context.ts | 189 + context/QueuedMessageContext.tsx | 63 + context/fpsMetrics.tsx | 30 + context/mailbox.tsx | 38 + context/modalContext.tsx | 58 + context/notifications.tsx | 240 + context/overlayContext.tsx | 151 + context/promptOverlayContext.tsx | 125 + context/stats.tsx | 220 + context/voice.tsx | 88 + coordinator/coordinatorMode.ts | 369 ++ cost-tracker.ts | 323 + costHook.ts | 22 + dialogLaunchers.tsx | 133 + entrypoints/agentSdkTypes.ts | 443 ++ entrypoints/cli.tsx | 303 + entrypoints/init.ts | 340 + entrypoints/mcp.ts | 196 + entrypoints/sandboxTypes.ts | 156 + entrypoints/sdk/controlSchemas.ts | 663 ++ entrypoints/sdk/coreSchemas.ts | 1889 ++++++ entrypoints/sdk/coreTypes.ts | 62 + history.ts | 464 ++ hooks/fileSuggestions.ts | 811 +++ .../useAutoModeUnavailableNotification.ts | 56 + .../useCanSwitchToExistingSubscription.tsx | 60 + .../useDeprecationWarningNotification.tsx | 44 + hooks/notifs/useFastModeNotification.tsx | 162 + hooks/notifs/useIDEStatusIndicator.tsx | 186 + hooks/notifs/useInstallMessages.tsx | 26 + .../useLspInitializationNotification.tsx | 143 + hooks/notifs/useMcpConnectivityStatus.tsx | 88 + .../notifs/useModelMigrationNotifications.tsx | 52 + .../notifs/useNpmDeprecationNotification.tsx | 25 + .../usePluginAutoupdateNotification.tsx | 83 + hooks/notifs/usePluginInstallationStatus.tsx | 128 + .../useRateLimitWarningNotification.tsx | 114 + hooks/notifs/useSettingsErrors.tsx | 69 + hooks/notifs/useStartupNotification.ts | 41 + .../notifs/useTeammateShutdownNotification.ts | 78 + hooks/renderPlaceholder.ts | 51 + hooks/toolPermission/PermissionContext.ts | 388 ++ .../handlers/coordinatorHandler.ts | 65 + .../handlers/interactiveHandler.ts | 536 ++ .../handlers/swarmWorkerHandler.ts | 159 + hooks/toolPermission/permissionLogging.ts | 238 + hooks/unifiedSuggestions.ts | 202 + hooks/useAfterFirstRender.ts | 17 + hooks/useApiKeyVerification.ts | 84 + hooks/useArrowKeyHistory.tsx | 229 + hooks/useAssistantHistory.ts | 250 + hooks/useAwaySummary.ts | 125 + hooks/useBackgroundTaskNavigation.ts | 251 + hooks/useBlink.ts | 34 + hooks/useCanUseTool.tsx | 204 + hooks/useCancelRequest.ts | 276 + hooks/useChromeExtensionNotification.tsx | 50 + hooks/useClaudeCodeHintRecommendation.tsx | 129 + hooks/useClipboardImageHint.ts | 77 + hooks/useCommandKeybindings.tsx | 108 + hooks/useCommandQueue.ts | 15 + hooks/useCopyOnSelect.ts | 98 + hooks/useDeferredHookMessages.ts | 46 + hooks/useDiffData.ts | 110 + hooks/useDiffInIDE.ts | 379 ++ hooks/useDirectConnect.ts | 229 + hooks/useDoublePress.ts | 62 + hooks/useDynamicConfig.ts | 22 + hooks/useElapsedTime.ts | 37 + hooks/useExitOnCtrlCD.ts | 95 + hooks/useExitOnCtrlCDWithKeybindings.ts | 24 + hooks/useFileHistorySnapshotInit.ts | 25 + hooks/useGlobalKeybindings.tsx | 249 + hooks/useHistorySearch.ts | 303 + hooks/useIDEIntegration.tsx | 70 + hooks/useIdeAtMentioned.ts | 76 + hooks/useIdeConnectionStatus.ts | 33 + hooks/useIdeLogging.ts | 41 + hooks/useIdeSelection.ts | 150 + hooks/useInboxPoller.ts | 969 +++ hooks/useInputBuffer.ts | 132 + hooks/useIssueFlagBanner.ts | 133 + hooks/useLogMessages.ts | 119 + hooks/useLspPluginRecommendation.tsx | 194 + hooks/useMailboxBridge.ts | 21 + hooks/useMainLoopModel.ts | 34 + hooks/useManagePlugins.ts | 304 + hooks/useMemoryUsage.ts | 39 + hooks/useMergedClients.ts | 23 + hooks/useMergedCommands.ts | 15 + hooks/useMergedTools.ts | 44 + hooks/useMinDisplayTime.ts | 35 + hooks/useNotifyAfterTimeout.ts | 65 + hooks/useOfficialMarketplaceNotification.tsx | 48 + hooks/usePasteHandler.ts | 285 + hooks/usePluginRecommendationBase.tsx | 105 + hooks/usePrStatus.ts | 106 + hooks/usePromptSuggestion.ts | 177 + hooks/usePromptsFromClaudeInChrome.tsx | 71 + hooks/useQueueProcessor.ts | 68 + hooks/useRemoteSession.ts | 605 ++ hooks/useReplBridge.tsx | 723 +++ hooks/useSSHSession.ts | 241 + hooks/useScheduledTasks.ts | 139 + hooks/useSearchInput.ts | 364 ++ hooks/useSessionBackgrounding.ts | 158 + hooks/useSettings.ts | 17 + hooks/useSettingsChange.ts | 25 + hooks/useSkillImprovementSurvey.ts | 105 + hooks/useSkillsChange.ts | 62 + hooks/useSwarmInitialization.ts | 81 + hooks/useSwarmPermissionPoller.ts | 330 + hooks/useTaskListWatcher.ts | 221 + hooks/useTasksV2.ts | 250 + hooks/useTeammateViewAutoExit.ts | 63 + hooks/useTeleportResume.tsx | 85 + hooks/useTerminalSize.ts | 15 + hooks/useTextInput.ts | 529 ++ hooks/useTimeout.ts | 14 + hooks/useTurnDiffs.ts | 213 + hooks/useTypeahead.tsx | 1385 ++++ hooks/useUpdateNotification.ts | 34 + hooks/useVimInput.ts | 316 + hooks/useVirtualScroll.ts | 721 +++ hooks/useVoice.ts | 1144 ++++ hooks/useVoiceEnabled.ts | 25 + hooks/useVoiceIntegration.tsx | 677 ++ ink.ts | 85 + ink/Ansi.tsx | 292 + ink/bidi.ts | 139 + ink/clearTerminal.ts | 74 + ink/colorize.ts | 231 + ink/components/AlternateScreen.tsx | 80 + ink/components/App.tsx | 658 ++ ink/components/AppContext.ts | 21 + ink/components/Box.tsx | 214 + ink/components/Button.tsx | 192 + ink/components/ClockContext.tsx | 112 + ink/components/CursorDeclarationContext.ts | 32 + ink/components/ErrorOverview.tsx | 109 + ink/components/Link.tsx | 42 + ink/components/Newline.tsx | 39 + ink/components/NoSelect.tsx | 68 + ink/components/RawAnsi.tsx | 57 + ink/components/ScrollBox.tsx | 237 + ink/components/Spacer.tsx | 20 + ink/components/StdinContext.ts | 49 + ink/components/TerminalFocusContext.tsx | 52 + ink/components/TerminalSizeContext.tsx | 7 + ink/components/Text.tsx | 254 + ink/constants.ts | 2 + ink/dom.ts | 484 ++ ink/events/click-event.ts | 38 + ink/events/dispatcher.ts | 233 + ink/events/emitter.ts | 39 + ink/events/event-handlers.ts | 73 + ink/events/event.ts | 11 + ink/events/focus-event.ts | 21 + ink/events/input-event.ts | 205 + ink/events/keyboard-event.ts | 51 + ink/events/terminal-event.ts | 107 + ink/events/terminal-focus-event.ts | 19 + ink/focus.ts | 181 + ink/frame.ts | 124 + ink/get-max-width.ts | 27 + ink/hit-test.ts | 130 + ink/hooks/use-animation-frame.ts | 57 + ink/hooks/use-app.ts | 8 + ink/hooks/use-declared-cursor.ts | 73 + ink/hooks/use-input.ts | 92 + ink/hooks/use-interval.ts | 67 + ink/hooks/use-search-highlight.ts | 53 + ink/hooks/use-selection.ts | 104 + ink/hooks/use-stdin.ts | 8 + ink/hooks/use-tab-status.ts | 72 + ink/hooks/use-terminal-focus.ts | 16 + ink/hooks/use-terminal-title.ts | 31 + ink/hooks/use-terminal-viewport.ts | 96 + ink/ink.tsx | 1723 +++++ ink/instances.ts | 10 + ink/layout/engine.ts | 6 + ink/layout/geometry.ts | 97 + ink/layout/node.ts | 152 + ink/layout/yoga.ts | 308 + ink/line-width-cache.ts | 24 + ink/log-update.ts | 773 +++ ink/measure-element.ts | 23 + ink/measure-text.ts | 47 + ink/node-cache.ts | 54 + ink/optimizer.ts | 93 + ink/output.ts | 797 +++ ink/parse-keypress.ts | 801 +++ ink/reconciler.ts | 512 ++ ink/render-border.ts | 231 + ink/render-node-to-output.ts | 1462 +++++ ink/render-to-screen.ts | 231 + ink/renderer.ts | 178 + ink/root.ts | 184 + ink/screen.ts | 1486 +++++ ink/searchHighlight.ts | 93 + ink/selection.ts | 917 +++ ink/squash-text-nodes.ts | 92 + ink/stringWidth.ts | 222 + ink/styles.ts | 771 +++ ink/supports-hyperlinks.ts | 57 + ink/tabstops.ts | 46 + ink/terminal-focus-state.ts | 47 + ink/terminal-querier.ts | 212 + ink/terminal.ts | 248 + ink/termio.ts | 42 + ink/termio/ansi.ts | 75 + ink/termio/csi.ts | 319 + ink/termio/dec.ts | 60 + ink/termio/esc.ts | 67 + ink/termio/osc.ts | 493 ++ ink/termio/parser.ts | 394 ++ ink/termio/sgr.ts | 308 + ink/termio/tokenize.ts | 319 + ink/termio/types.ts | 236 + ink/useTerminalNotification.ts | 126 + ink/warn.ts | 9 + ink/widest-line.ts | 19 + ink/wrap-text.ts | 74 + ink/wrapAnsi.ts | 20 + interactiveHelpers.tsx | 366 ++ keybindings/KeybindingContext.tsx | 243 + keybindings/KeybindingProviderSetup.tsx | 308 + keybindings/defaultBindings.ts | 340 + keybindings/loadUserBindings.ts | 472 ++ keybindings/match.ts | 120 + keybindings/parser.ts | 203 + keybindings/reservedShortcuts.ts | 127 + keybindings/resolver.ts | 244 + keybindings/schema.ts | 236 + keybindings/shortcutFormat.ts | 63 + keybindings/template.ts | 52 + keybindings/useKeybinding.ts | 196 + keybindings/useShortcutDisplay.ts | 59 + keybindings/validate.ts | 498 ++ main.tsx | 4684 ++++++++++++++ memdir/findRelevantMemories.ts | 141 + memdir/memdir.ts | 507 ++ memdir/memoryAge.ts | 53 + memdir/memoryScan.ts | 94 + memdir/memoryTypes.ts | 271 + memdir/paths.ts | 278 + memdir/teamMemPaths.ts | 292 + memdir/teamMemPrompts.ts | 100 + migrations/migrateAutoUpdatesToSettings.ts | 61 + ...rateBypassPermissionsAcceptedToSettings.ts | 40 + ...ateEnableAllProjectMcpServersToSettings.ts | 118 + migrations/migrateFennecToOpus.ts | 45 + migrations/migrateLegacyOpusToCurrent.ts | 57 + migrations/migrateOpusToOpus1m.ts | 43 + ...plBridgeEnabledToRemoteControlAtStartup.ts | 22 + migrations/migrateSonnet1mToSonnet45.ts | 48 + migrations/migrateSonnet45ToSonnet46.ts | 67 + .../resetAutoModeOptInForDefaultOffer.ts | 51 + migrations/resetProToOpusDefault.ts | 51 + moreright/useMoreRight.tsx | 26 + native-ts/color-diff/index.ts | 999 +++ native-ts/file-index/index.ts | 370 ++ native-ts/yoga-layout/enums.ts | 134 + native-ts/yoga-layout/index.ts | 2578 ++++++++ outputStyles/loadOutputStylesDir.ts | 98 + plugins/builtinPlugins.ts | 159 + plugins/bundled/index.ts | 23 + projectOnboardingState.ts | 83 + public/claude-files.png | Bin 0 -> 738607 bytes public/leak-tweet.png | Bin 0 -> 416660 bytes query.ts | 1729 +++++ query/config.ts | 46 + query/deps.ts | 40 + query/stopHooks.ts | 473 ++ query/tokenBudget.ts | 93 + remote/RemoteSessionManager.ts | 343 + remote/SessionsWebSocket.ts | 404 ++ remote/remotePermissionBridge.ts | 78 + remote/sdkMessageAdapter.ts | 302 + replLauncher.tsx | 23 + schemas/hooks.ts | 222 + screens/Doctor.tsx | 575 ++ screens/REPL.tsx | 5006 +++++++++++++++ screens/ResumeConversation.tsx | 399 ++ server/createDirectConnectSession.ts | 88 + server/directConnectManager.ts | 213 + server/types.ts | 57 + services/AgentSummary/agentSummary.ts | 179 + services/MagicDocs/magicDocs.ts | 254 + services/MagicDocs/prompts.ts | 127 + services/PromptSuggestion/promptSuggestion.ts | 523 ++ services/PromptSuggestion/speculation.ts | 991 +++ services/SessionMemory/prompts.ts | 324 + services/SessionMemory/sessionMemory.ts | 495 ++ services/SessionMemory/sessionMemoryUtils.ts | 207 + services/analytics/config.ts | 38 + services/analytics/datadog.ts | 307 + services/analytics/firstPartyEventLogger.ts | 449 ++ .../firstPartyEventLoggingExporter.ts | 806 +++ services/analytics/growthbook.ts | 1155 ++++ services/analytics/index.ts | 173 + services/analytics/metadata.ts | 973 +++ services/analytics/sink.ts | 114 + services/analytics/sinkKillswitch.ts | 25 + services/api/adminRequests.ts | 119 + services/api/bootstrap.ts | 141 + services/api/claude.ts | 3419 ++++++++++ services/api/client.ts | 389 ++ services/api/dumpPrompts.ts | 226 + services/api/emptyUsage.ts | 22 + services/api/errorUtils.ts | 260 + services/api/errors.ts | 1207 ++++ services/api/filesApi.ts | 748 +++ services/api/firstTokenDate.ts | 60 + services/api/grove.ts | 357 ++ services/api/logging.ts | 788 +++ services/api/metricsOptOut.ts | 159 + services/api/overageCreditGrant.ts | 137 + services/api/promptCacheBreakDetection.ts | 727 +++ services/api/referral.ts | 281 + services/api/sessionIngress.ts | 514 ++ services/api/ultrareviewQuota.ts | 38 + services/api/usage.ts | 63 + services/api/withRetry.ts | 822 +++ services/autoDream/autoDream.ts | 324 + services/autoDream/config.ts | 21 + services/autoDream/consolidationLock.ts | 140 + services/autoDream/consolidationPrompt.ts | 65 + services/awaySummary.ts | 74 + services/claudeAiLimits.ts | 515 ++ services/claudeAiLimitsHook.ts | 23 + services/compact/apiMicrocompact.ts | 153 + services/compact/autoCompact.ts | 351 ++ services/compact/compact.ts | 1705 +++++ services/compact/compactWarningHook.ts | 16 + services/compact/compactWarningState.ts | 18 + services/compact/grouping.ts | 63 + services/compact/microCompact.ts | 530 ++ services/compact/postCompactCleanup.ts | 77 + services/compact/prompt.ts | 374 ++ services/compact/sessionMemoryCompact.ts | 630 ++ services/compact/timeBasedMCConfig.ts | 43 + services/diagnosticTracking.ts | 397 ++ services/extractMemories/extractMemories.ts | 615 ++ services/extractMemories/prompts.ts | 154 + services/internalLogging.ts | 90 + services/lsp/LSPClient.ts | 447 ++ services/lsp/LSPDiagnosticRegistry.ts | 386 ++ services/lsp/LSPServerInstance.ts | 511 ++ services/lsp/LSPServerManager.ts | 420 ++ services/lsp/config.ts | 79 + services/lsp/manager.ts | 289 + services/lsp/passiveFeedback.ts | 328 + services/mcp/InProcessTransport.ts | 63 + services/mcp/MCPConnectionManager.tsx | 73 + services/mcp/SdkControlTransport.ts | 136 + services/mcp/auth.ts | 2465 ++++++++ services/mcp/channelAllowlist.ts | 76 + services/mcp/channelNotification.ts | 316 + services/mcp/channelPermissions.ts | 240 + services/mcp/claudeai.ts | 164 + services/mcp/client.ts | 3348 ++++++++++ services/mcp/config.ts | 1578 +++++ services/mcp/elicitationHandler.ts | 313 + services/mcp/envExpansion.ts | 38 + services/mcp/headersHelper.ts | 138 + services/mcp/mcpStringUtils.ts | 106 + services/mcp/normalization.ts | 23 + services/mcp/oauthPort.ts | 78 + services/mcp/officialRegistry.ts | 72 + services/mcp/types.ts | 258 + services/mcp/useManageMCPConnections.ts | 1141 ++++ services/mcp/utils.ts | 575 ++ services/mcp/vscodeSdkMcp.ts | 112 + services/mcp/xaa.ts | 511 ++ services/mcp/xaaIdpLogin.ts | 487 ++ services/mcpServerApproval.tsx | 41 + services/mockRateLimits.ts | 882 +++ services/notifier.ts | 156 + services/oauth/auth-code-listener.ts | 211 + services/oauth/client.ts | 566 ++ services/oauth/crypto.ts | 23 + services/oauth/getOauthProfile.ts | 53 + services/oauth/index.ts | 198 + services/plugins/PluginInstallationManager.ts | 184 + services/plugins/pluginCliCommands.ts | 344 + services/plugins/pluginOperations.ts | 1088 ++++ services/policyLimits/index.ts | 663 ++ services/policyLimits/types.ts | 27 + services/preventSleep.ts | 165 + services/rateLimitMessages.ts | 344 + services/rateLimitMocking.ts | 144 + services/remoteManagedSettings/index.ts | 638 ++ .../remoteManagedSettings/securityCheck.tsx | 74 + services/remoteManagedSettings/syncCache.ts | 112 + .../remoteManagedSettings/syncCacheState.ts | 96 + services/remoteManagedSettings/types.ts | 31 + services/settingsSync/index.ts | 581 ++ services/settingsSync/types.ts | 67 + services/teamMemorySync/index.ts | 1256 ++++ services/teamMemorySync/secretScanner.ts | 324 + services/teamMemorySync/teamMemSecretGuard.ts | 44 + services/teamMemorySync/types.ts | 156 + services/teamMemorySync/watcher.ts | 387 ++ services/tips/tipHistory.ts | 17 + services/tips/tipRegistry.ts | 686 ++ services/tips/tipScheduler.ts | 58 + services/tokenEstimation.ts | 495 ++ .../toolUseSummary/toolUseSummaryGenerator.ts | 112 + services/tools/StreamingToolExecutor.ts | 530 ++ services/tools/toolExecution.ts | 1745 +++++ services/tools/toolHooks.ts | 650 ++ services/tools/toolOrchestration.ts | 188 + services/vcr.ts | 406 ++ services/voice.ts | 525 ++ services/voiceKeyterms.ts | 106 + services/voiceStreamSTT.ts | 544 ++ setup.ts | 477 ++ skills/bundled/batch.ts | 124 + skills/bundled/claudeApi.ts | 196 + skills/bundled/claudeApiContent.ts | 75 + skills/bundled/claudeInChrome.ts | 34 + skills/bundled/debug.ts | 103 + skills/bundled/index.ts | 79 + skills/bundled/keybindings.ts | 339 + skills/bundled/loop.ts | 92 + skills/bundled/loremIpsum.ts | 282 + skills/bundled/remember.ts | 82 + skills/bundled/scheduleRemoteAgents.ts | 447 ++ skills/bundled/simplify.ts | 69 + skills/bundled/skillify.ts | 197 + skills/bundled/stuck.ts | 79 + skills/bundled/updateConfig.ts | 475 ++ skills/bundled/verify.ts | 30 + skills/bundled/verifyContent.ts | 13 + skills/bundledSkills.ts | 220 + skills/loadSkillsDir.ts | 1086 ++++ skills/mcpSkillBuilders.ts | 44 + state/AppState.tsx | 200 + state/AppStateStore.ts | 569 ++ state/onChangeAppState.ts | 171 + state/selectors.ts | 76 + state/store.ts | 34 + state/teammateViewHelpers.ts | 141 + tasks.ts | 39 + tasks/DreamTask/DreamTask.ts | 157 + .../InProcessTeammateTask.tsx | 126 + tasks/InProcessTeammateTask/types.ts | 121 + tasks/LocalAgentTask/LocalAgentTask.tsx | 683 ++ tasks/LocalMainSessionTask.ts | 479 ++ tasks/LocalShellTask/LocalShellTask.tsx | 523 ++ tasks/LocalShellTask/guards.ts | 41 + tasks/LocalShellTask/killShellTasks.ts | 76 + tasks/RemoteAgentTask/RemoteAgentTask.tsx | 856 +++ tasks/pillLabel.ts | 82 + tasks/stopTask.ts | 100 + tasks/types.ts | 46 + tools.ts | 389 ++ tools/AgentTool/AgentTool.tsx | 1398 ++++ tools/AgentTool/UI.tsx | 872 +++ tools/AgentTool/agentColorManager.ts | 66 + tools/AgentTool/agentDisplay.ts | 104 + tools/AgentTool/agentMemory.ts | 177 + tools/AgentTool/agentMemorySnapshot.ts | 197 + tools/AgentTool/agentToolUtils.ts | 686 ++ .../built-in/claudeCodeGuideAgent.ts | 205 + tools/AgentTool/built-in/exploreAgent.ts | 83 + .../AgentTool/built-in/generalPurposeAgent.ts | 34 + tools/AgentTool/built-in/planAgent.ts | 92 + tools/AgentTool/built-in/statuslineSetup.ts | 144 + tools/AgentTool/built-in/verificationAgent.ts | 152 + tools/AgentTool/builtInAgents.ts | 72 + tools/AgentTool/constants.ts | 12 + tools/AgentTool/forkSubagent.ts | 210 + tools/AgentTool/loadAgentsDir.ts | 755 +++ tools/AgentTool/prompt.ts | 287 + tools/AgentTool/resumeAgent.ts | 265 + tools/AgentTool/runAgent.ts | 973 +++ .../AskUserQuestionTool.tsx | 266 + tools/AskUserQuestionTool/prompt.ts | 44 + tools/BashTool/BashTool.tsx | 1144 ++++ tools/BashTool/BashToolResultMessage.tsx | 191 + tools/BashTool/UI.tsx | 185 + tools/BashTool/bashCommandHelpers.ts | 265 + tools/BashTool/bashPermissions.ts | 2621 ++++++++ tools/BashTool/bashSecurity.ts | 2592 ++++++++ tools/BashTool/commandSemantics.ts | 140 + tools/BashTool/commentLabel.ts | 13 + tools/BashTool/destructiveCommandWarning.ts | 102 + tools/BashTool/modeValidation.ts | 115 + tools/BashTool/pathValidation.ts | 1303 ++++ tools/BashTool/prompt.ts | 369 ++ tools/BashTool/readOnlyValidation.ts | 1990 ++++++ tools/BashTool/sedEditParser.ts | 322 + tools/BashTool/sedValidation.ts | 684 ++ tools/BashTool/shouldUseSandbox.ts | 153 + tools/BashTool/toolName.ts | 2 + tools/BashTool/utils.ts | 223 + tools/BriefTool/BriefTool.ts | 204 + tools/BriefTool/UI.tsx | 101 + tools/BriefTool/attachments.ts | 110 + tools/BriefTool/prompt.ts | 22 + tools/BriefTool/upload.ts | 174 + tools/ConfigTool/ConfigTool.ts | 467 ++ tools/ConfigTool/UI.tsx | 38 + tools/ConfigTool/constants.ts | 1 + tools/ConfigTool/prompt.ts | 93 + tools/ConfigTool/supportedSettings.ts | 211 + tools/EnterPlanModeTool/EnterPlanModeTool.ts | 126 + tools/EnterPlanModeTool/UI.tsx | 33 + tools/EnterPlanModeTool/constants.ts | 1 + tools/EnterPlanModeTool/prompt.ts | 170 + tools/EnterWorktreeTool/EnterWorktreeTool.ts | 127 + tools/EnterWorktreeTool/UI.tsx | 20 + tools/EnterWorktreeTool/constants.ts | 1 + tools/EnterWorktreeTool/prompt.ts | 30 + tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts | 493 ++ tools/ExitPlanModeTool/UI.tsx | 82 + tools/ExitPlanModeTool/constants.ts | 2 + tools/ExitPlanModeTool/prompt.ts | 29 + tools/ExitWorktreeTool/ExitWorktreeTool.ts | 329 + tools/ExitWorktreeTool/UI.tsx | 25 + tools/ExitWorktreeTool/constants.ts | 1 + tools/ExitWorktreeTool/prompt.ts | 32 + tools/FileEditTool/FileEditTool.ts | 625 ++ tools/FileEditTool/UI.tsx | 289 + tools/FileEditTool/constants.ts | 11 + tools/FileEditTool/prompt.ts | 28 + tools/FileEditTool/types.ts | 85 + tools/FileEditTool/utils.ts | 775 +++ tools/FileReadTool/FileReadTool.ts | 1183 ++++ tools/FileReadTool/UI.tsx | 185 + tools/FileReadTool/imageProcessor.ts | 94 + tools/FileReadTool/limits.ts | 92 + tools/FileReadTool/prompt.ts | 49 + tools/FileWriteTool/FileWriteTool.ts | 434 ++ tools/FileWriteTool/UI.tsx | 405 ++ tools/FileWriteTool/prompt.ts | 18 + tools/GlobTool/GlobTool.ts | 198 + tools/GlobTool/UI.tsx | 63 + tools/GlobTool/prompt.ts | 7 + tools/GrepTool/GrepTool.ts | 577 ++ tools/GrepTool/UI.tsx | 201 + tools/GrepTool/prompt.ts | 18 + tools/LSPTool/LSPTool.ts | 860 +++ tools/LSPTool/UI.tsx | 228 + tools/LSPTool/formatters.ts | 592 ++ tools/LSPTool/prompt.ts | 21 + tools/LSPTool/schemas.ts | 215 + tools/LSPTool/symbolContext.ts | 90 + .../ListMcpResourcesTool.ts | 123 + tools/ListMcpResourcesTool/UI.tsx | 29 + tools/ListMcpResourcesTool/prompt.ts | 20 + tools/MCPTool/MCPTool.ts | 77 + tools/MCPTool/UI.tsx | 403 ++ tools/MCPTool/classifyForCollapse.ts | 604 ++ tools/MCPTool/prompt.ts | 3 + tools/McpAuthTool/McpAuthTool.ts | 215 + tools/NotebookEditTool/NotebookEditTool.ts | 490 ++ tools/NotebookEditTool/UI.tsx | 93 + tools/NotebookEditTool/constants.ts | 2 + tools/NotebookEditTool/prompt.ts | 3 + tools/PowerShellTool/PowerShellTool.tsx | 1001 +++ tools/PowerShellTool/UI.tsx | 131 + tools/PowerShellTool/clmTypes.ts | 211 + tools/PowerShellTool/commandSemantics.ts | 142 + tools/PowerShellTool/commonParameters.ts | 30 + .../destructiveCommandWarning.ts | 109 + tools/PowerShellTool/gitSafety.ts | 176 + tools/PowerShellTool/modeValidation.ts | 404 ++ tools/PowerShellTool/pathValidation.ts | 2049 ++++++ tools/PowerShellTool/powershellPermissions.ts | 1648 +++++ tools/PowerShellTool/powershellSecurity.ts | 1090 ++++ tools/PowerShellTool/prompt.ts | 145 + tools/PowerShellTool/readOnlyValidation.ts | 1823 ++++++ tools/PowerShellTool/toolName.ts | 2 + tools/REPLTool/constants.ts | 46 + tools/REPLTool/primitiveTools.ts | 39 + .../ReadMcpResourceTool.ts | 158 + tools/ReadMcpResourceTool/UI.tsx | 37 + tools/ReadMcpResourceTool/prompt.ts | 16 + tools/RemoteTriggerTool/RemoteTriggerTool.ts | 161 + tools/RemoteTriggerTool/UI.tsx | 17 + tools/RemoteTriggerTool/prompt.ts | 15 + tools/ScheduleCronTool/CronCreateTool.ts | 157 + tools/ScheduleCronTool/CronDeleteTool.ts | 95 + tools/ScheduleCronTool/CronListTool.ts | 97 + tools/ScheduleCronTool/UI.tsx | 60 + tools/ScheduleCronTool/prompt.ts | 135 + tools/SendMessageTool/SendMessageTool.ts | 917 +++ tools/SendMessageTool/UI.tsx | 31 + tools/SendMessageTool/constants.ts | 1 + tools/SendMessageTool/prompt.ts | 49 + tools/SkillTool/SkillTool.ts | 1108 ++++ tools/SkillTool/UI.tsx | 128 + tools/SkillTool/constants.ts | 1 + tools/SkillTool/prompt.ts | 241 + tools/SleepTool/prompt.ts | 17 + .../SyntheticOutputTool.ts | 163 + tools/TaskCreateTool/TaskCreateTool.ts | 138 + tools/TaskCreateTool/constants.ts | 1 + tools/TaskCreateTool/prompt.ts | 56 + tools/TaskGetTool/TaskGetTool.ts | 128 + tools/TaskGetTool/constants.ts | 1 + tools/TaskGetTool/prompt.ts | 24 + tools/TaskListTool/TaskListTool.ts | 116 + tools/TaskListTool/constants.ts | 1 + tools/TaskListTool/prompt.ts | 49 + tools/TaskOutputTool/TaskOutputTool.tsx | 584 ++ tools/TaskOutputTool/constants.ts | 1 + tools/TaskStopTool/TaskStopTool.ts | 131 + tools/TaskStopTool/UI.tsx | 41 + tools/TaskStopTool/prompt.ts | 8 + tools/TaskUpdateTool/TaskUpdateTool.ts | 406 ++ tools/TaskUpdateTool/constants.ts | 1 + tools/TaskUpdateTool/prompt.ts | 77 + tools/TeamCreateTool/TeamCreateTool.ts | 240 + tools/TeamCreateTool/UI.tsx | 6 + tools/TeamCreateTool/constants.ts | 1 + tools/TeamCreateTool/prompt.ts | 113 + tools/TeamDeleteTool/TeamDeleteTool.ts | 139 + tools/TeamDeleteTool/UI.tsx | 20 + tools/TeamDeleteTool/constants.ts | 1 + tools/TeamDeleteTool/prompt.ts | 16 + tools/TodoWriteTool/TodoWriteTool.ts | 115 + tools/TodoWriteTool/constants.ts | 1 + tools/TodoWriteTool/prompt.ts | 184 + tools/ToolSearchTool/ToolSearchTool.ts | 471 ++ tools/ToolSearchTool/constants.ts | 1 + tools/ToolSearchTool/prompt.ts | 121 + tools/WebFetchTool/UI.tsx | 72 + tools/WebFetchTool/WebFetchTool.ts | 318 + tools/WebFetchTool/preapproved.ts | 166 + tools/WebFetchTool/prompt.ts | 46 + tools/WebFetchTool/utils.ts | 530 ++ tools/WebSearchTool/UI.tsx | 101 + tools/WebSearchTool/WebSearchTool.ts | 435 ++ tools/WebSearchTool/prompt.ts | 34 + tools/shared/gitOperationTracking.ts | 277 + tools/shared/spawnMultiAgent.ts | 1093 ++++ tools/testing/TestingPermissionTool.tsx | 74 + tools/utils.ts | 40 + types/command.ts | 216 + .../v1/claude_code_internal_event.ts | 865 +++ types/generated/events_mono/common/v1/auth.ts | 100 + .../v1/growthbook_experiment_event.ts | 223 + types/generated/google/protobuf/timestamp.ts | 187 + types/hooks.ts | 290 + types/ids.ts | 44 + types/logs.ts | 330 + types/permissions.ts | 441 ++ types/plugin.ts | 363 ++ types/textInputTypes.ts | 387 ++ upstreamproxy/relay.ts | 455 ++ upstreamproxy/upstreamproxy.ts | 285 + utils/CircularBuffer.ts | 84 + utils/Cursor.ts | 1530 +++++ utils/QueryGuard.ts | 121 + utils/Shell.ts | 474 ++ utils/ShellCommand.ts | 465 ++ utils/abortController.ts | 99 + utils/activityManager.ts | 164 + utils/advisor.ts | 145 + utils/agentContext.ts | 178 + utils/agentId.ts | 99 + utils/agentSwarmsEnabled.ts | 44 + utils/agenticSessionSearch.ts | 307 + utils/analyzeContext.ts | 1382 ++++ utils/ansiToPng.ts | 334 + utils/ansiToSvg.ts | 272 + utils/api.ts | 718 +++ utils/apiPreconnect.ts | 71 + utils/appleTerminalBackup.ts | 124 + utils/argumentSubstitution.ts | 145 + utils/array.ts | 13 + utils/asciicast.ts | 239 + utils/attachments.ts | 3997 ++++++++++++ utils/attribution.ts | 393 ++ utils/auth.ts | 2002 ++++++ utils/authFileDescriptor.ts | 196 + utils/authPortable.ts | 19 + utils/autoModeDenials.ts | 26 + utils/autoRunIssue.tsx | 122 + utils/autoUpdater.ts | 561 ++ utils/aws.ts | 74 + utils/awsAuthStatusManager.ts | 81 + utils/background/remote/preconditions.ts | 235 + utils/background/remote/remoteSession.ts | 98 + utils/backgroundHousekeeping.ts | 94 + utils/bash/ParsedCommand.ts | 318 + utils/bash/ShellSnapshot.ts | 582 ++ utils/bash/ast.ts | 2679 ++++++++ utils/bash/bashParser.ts | 4436 +++++++++++++ utils/bash/bashPipeCommand.ts | 294 + utils/bash/commands.ts | 1339 ++++ utils/bash/heredoc.ts | 733 +++ utils/bash/parser.ts | 230 + utils/bash/prefix.ts | 204 + utils/bash/registry.ts | 53 + utils/bash/shellCompletion.ts | 259 + utils/bash/shellPrefix.ts | 28 + utils/bash/shellQuote.ts | 304 + utils/bash/shellQuoting.ts | 128 + utils/bash/specs/alias.ts | 14 + utils/bash/specs/index.ts | 18 + utils/bash/specs/nohup.ts | 13 + utils/bash/specs/pyright.ts | 91 + utils/bash/specs/sleep.ts | 13 + utils/bash/specs/srun.ts | 31 + utils/bash/specs/time.ts | 13 + utils/bash/specs/timeout.ts | 20 + utils/bash/treeSitterAnalysis.ts | 506 ++ utils/betas.ts | 434 ++ utils/billing.ts | 78 + utils/binaryCheck.ts | 53 + utils/browser.ts | 68 + utils/bufferedWriter.ts | 100 + utils/bundledMode.ts | 22 + utils/caCerts.ts | 115 + utils/caCertsConfig.ts | 88 + utils/cachePaths.ts | 38 + utils/classifierApprovals.ts | 88 + utils/classifierApprovalsHook.ts | 17 + utils/claudeCodeHints.ts | 193 + utils/claudeDesktop.ts | 152 + utils/claudeInChrome/chromeNativeHost.ts | 527 ++ utils/claudeInChrome/common.ts | 540 ++ utils/claudeInChrome/mcpServer.ts | 293 + utils/claudeInChrome/prompt.ts | 83 + utils/claudeInChrome/setup.ts | 400 ++ utils/claudeInChrome/setupPortable.ts | 233 + utils/claudeInChrome/toolRendering.tsx | 262 + utils/claudemd.ts | 1479 +++++ utils/cleanup.ts | 602 ++ utils/cleanupRegistry.ts | 25 + utils/cliArgs.ts | 60 + utils/cliHighlight.ts | 54 + utils/codeIndexing.ts | 206 + utils/collapseBackgroundBashNotifications.ts | 84 + utils/collapseHookSummaries.ts | 59 + utils/collapseReadSearch.ts | 1109 ++++ utils/collapseTeammateShutdowns.ts | 55 + utils/combinedAbortSignal.ts | 47 + utils/commandLifecycle.ts | 21 + utils/commitAttribution.ts | 961 +++ utils/completionCache.ts | 166 + utils/computerUse/appNames.ts | 196 + utils/computerUse/cleanup.ts | 86 + utils/computerUse/common.ts | 61 + utils/computerUse/computerUseLock.ts | 215 + utils/computerUse/drainRunLoop.ts | 79 + utils/computerUse/escHotkey.ts | 54 + utils/computerUse/executor.ts | 658 ++ utils/computerUse/gates.ts | 72 + utils/computerUse/hostAdapter.ts | 69 + utils/computerUse/inputLoader.ts | 30 + utils/computerUse/mcpServer.ts | 106 + utils/computerUse/setup.ts | 53 + utils/computerUse/swiftLoader.ts | 23 + utils/computerUse/toolRendering.tsx | 125 + utils/computerUse/wrapper.tsx | 336 + utils/concurrentSessions.ts | 204 + utils/config.ts | 1817 ++++++ utils/configConstants.ts | 21 + utils/contentArray.ts | 51 + utils/context.ts | 221 + utils/contextAnalysis.ts | 272 + utils/contextSuggestions.ts | 235 + utils/controlMessageCompat.ts | 32 + utils/conversationRecovery.ts | 597 ++ utils/cron.ts | 308 + utils/cronJitterConfig.ts | 75 + utils/cronScheduler.ts | 565 ++ utils/cronTasks.ts | 458 ++ utils/cronTasksLock.ts | 195 + utils/crossProjectResume.ts | 75 + utils/crypto.ts | 13 + utils/cwd.ts | 32 + utils/debug.ts | 268 + utils/debugFilter.ts | 157 + utils/deepLink/banner.ts | 123 + utils/deepLink/parseDeepLink.ts | 170 + utils/deepLink/protocolHandler.ts | 136 + utils/deepLink/registerProtocol.ts | 348 + utils/deepLink/terminalLauncher.ts | 557 ++ utils/deepLink/terminalPreference.ts | 54 + utils/desktopDeepLink.ts | 236 + utils/detectRepository.ts | 178 + utils/diagLogs.ts | 94 + utils/diff.ts | 177 + utils/directMemberMessage.ts | 69 + utils/displayTags.ts | 51 + utils/doctorContextWarnings.ts | 265 + utils/doctorDiagnostic.ts | 625 ++ utils/dxt/helpers.ts | 88 + utils/dxt/zip.ts | 226 + utils/earlyInput.ts | 191 + utils/editor.ts | 183 + utils/effort.ts | 329 + utils/embeddedTools.ts | 29 + utils/env.ts | 347 + utils/envDynamic.ts | 151 + utils/envUtils.ts | 183 + utils/envValidation.ts | 38 + utils/errorLogSink.ts | 235 + utils/errors.ts | 238 + utils/exampleCommands.ts | 184 + utils/execFileNoThrow.ts | 150 + utils/execFileNoThrowPortable.ts | 89 + utils/execSyncWrapper.ts | 38 + utils/exportRenderer.tsx | 98 + utils/extraUsage.ts | 23 + utils/fastMode.ts | 532 ++ utils/file.ts | 584 ++ utils/fileHistory.ts | 1115 ++++ utils/fileOperationAnalytics.ts | 71 + utils/filePersistence/filePersistence.ts | 287 + utils/filePersistence/outputsScanner.ts | 126 + utils/fileRead.ts | 102 + utils/fileReadCache.ts | 96 + utils/fileStateCache.ts | 142 + utils/findExecutable.ts | 17 + utils/fingerprint.ts | 76 + utils/forkedAgent.ts | 689 ++ utils/format.ts | 308 + utils/formatBriefTimestamp.ts | 81 + utils/fpsTracker.ts | 47 + utils/frontmatterParser.ts | 370 ++ utils/fsOperations.ts | 770 +++ utils/fullscreen.ts | 202 + utils/generatedFiles.ts | 136 + utils/generators.ts | 88 + utils/genericProcessUtils.ts | 184 + utils/getWorktreePaths.ts | 70 + utils/getWorktreePathsPortable.ts | 27 + utils/ghPrStatus.ts | 106 + utils/git.ts | 926 +++ utils/git/gitConfigParser.ts | 277 + utils/git/gitFilesystem.ts | 699 ++ utils/git/gitignore.ts | 99 + utils/gitDiff.ts | 532 ++ utils/gitSettings.ts | 18 + utils/github/ghAuthStatus.ts | 29 + utils/githubRepoPathMapping.ts | 162 + utils/glob.ts | 130 + utils/gracefulShutdown.ts | 529 ++ utils/groupToolUses.ts | 182 + utils/handlePromptSubmit.ts | 610 ++ utils/hash.ts | 46 + utils/headlessProfiler.ts | 178 + utils/heapDumpService.ts | 303 + utils/heatmap.ts | 198 + utils/highlightMatch.tsx | 28 + utils/hooks.ts | 5022 +++++++++++++++ utils/hooks/AsyncHookRegistry.ts | 309 + utils/hooks/apiQueryHookHelper.ts | 141 + utils/hooks/execAgentHook.ts | 339 + utils/hooks/execHttpHook.ts | 242 + utils/hooks/execPromptHook.ts | 211 + utils/hooks/fileChangedWatcher.ts | 191 + utils/hooks/hookEvents.ts | 192 + utils/hooks/hookHelpers.ts | 83 + utils/hooks/hooksConfigManager.ts | 400 ++ utils/hooks/hooksConfigSnapshot.ts | 133 + utils/hooks/hooksSettings.ts | 271 + utils/hooks/postSamplingHooks.ts | 70 + utils/hooks/registerFrontmatterHooks.ts | 67 + utils/hooks/registerSkillHooks.ts | 64 + utils/hooks/sessionHooks.ts | 447 ++ utils/hooks/skillImprovement.ts | 267 + utils/hooks/ssrfGuard.ts | 294 + utils/horizontalScroll.ts | 137 + utils/http.ts | 136 + utils/hyperlink.ts | 39 + utils/iTermBackup.ts | 73 + utils/ide.ts | 1494 +++++ utils/idePathConversion.ts | 90 + utils/idleTimeout.ts | 53 + utils/imagePaste.ts | 416 ++ utils/imageResizer.ts | 880 +++ utils/imageStore.ts | 167 + utils/imageValidation.ts | 104 + utils/immediateCommand.ts | 15 + utils/inProcessTeammateHelpers.ts | 102 + utils/ink.ts | 26 + utils/intl.ts | 94 + utils/jetbrains.ts | 191 + utils/json.ts | 277 + utils/jsonRead.ts | 16 + utils/keyboardShortcuts.ts | 14 + utils/lazySchema.ts | 8 + utils/listSessionsImpl.ts | 454 ++ utils/localInstaller.ts | 162 + utils/lockfile.ts | 43 + utils/log.ts | 362 ++ utils/logoV2Utils.ts | 350 ++ utils/mailbox.ts | 73 + utils/managedEnv.ts | 199 + utils/managedEnvConstants.ts | 191 + utils/markdown.ts | 381 ++ utils/markdownConfigLoader.ts | 600 ++ utils/mcp/dateTimeParser.ts | 121 + utils/mcp/elicitationValidation.ts | 336 + utils/mcpInstructionsDelta.ts | 130 + utils/mcpOutputStorage.ts | 189 + utils/mcpValidation.ts | 208 + utils/mcpWebSocketTransport.ts | 200 + utils/memoize.ts | 269 + utils/memory/types.ts | 12 + utils/memory/versions.ts | 8 + utils/memoryFileDetection.ts | 289 + utils/messagePredicates.ts | 8 + utils/messageQueueManager.ts | 547 ++ utils/messages.ts | 5512 ++++++++++++++++ utils/messages/mappers.ts | 290 + utils/messages/systemInit.ts | 96 + utils/model/agent.ts | 157 + utils/model/aliases.ts | 25 + utils/model/antModels.ts | 64 + utils/model/bedrock.ts | 265 + utils/model/check1mAccess.ts | 72 + utils/model/configs.ts | 118 + utils/model/contextWindowUpgradeCheck.ts | 47 + utils/model/deprecation.ts | 101 + utils/model/model.ts | 618 ++ utils/model/modelAllowlist.ts | 170 + utils/model/modelCapabilities.ts | 118 + utils/model/modelOptions.ts | 540 ++ utils/model/modelStrings.ts | 166 + utils/model/modelSupportOverrides.ts | 50 + utils/model/providers.ts | 40 + utils/model/validateModel.ts | 159 + utils/modelCost.ts | 231 + utils/modifiers.ts | 36 + utils/mtls.ts | 179 + utils/nativeInstaller/download.ts | 523 ++ utils/nativeInstaller/index.ts | 18 + utils/nativeInstaller/installer.ts | 1708 +++++ utils/nativeInstaller/packageManagers.ts | 336 + utils/nativeInstaller/pidLock.ts | 433 ++ utils/notebook.ts | 224 + utils/objectGroupBy.ts | 18 + utils/pasteStore.ts | 104 + utils/path.ts | 155 + utils/pdf.ts | 300 + utils/pdfUtils.ts | 70 + utils/peerAddress.ts | 21 + utils/permissions/PermissionMode.ts | 141 + .../PermissionPromptToolResultSchema.ts | 127 + utils/permissions/PermissionResult.ts | 35 + utils/permissions/PermissionRule.ts | 40 + utils/permissions/PermissionUpdate.ts | 389 ++ utils/permissions/PermissionUpdateSchema.ts | 78 + utils/permissions/autoModeState.ts | 39 + utils/permissions/bashClassifier.ts | 61 + .../bypassPermissionsKillswitch.ts | 155 + utils/permissions/classifierDecision.ts | 98 + utils/permissions/classifierShared.ts | 39 + utils/permissions/dangerousPatterns.ts | 80 + utils/permissions/denialTracking.ts | 45 + utils/permissions/filesystem.ts | 1777 ++++++ utils/permissions/getNextPermissionMode.ts | 101 + utils/permissions/pathValidation.ts | 485 ++ utils/permissions/permissionExplainer.ts | 250 + utils/permissions/permissionRuleParser.ts | 198 + utils/permissions/permissionSetup.ts | 1532 +++++ utils/permissions/permissions.ts | 1486 +++++ utils/permissions/permissionsLoader.ts | 296 + utils/permissions/shadowedRuleDetection.ts | 234 + utils/permissions/shellRuleMatching.ts | 228 + utils/permissions/yoloClassifier.ts | 1495 +++++ utils/planModeV2.ts | 95 + utils/plans.ts | 397 ++ utils/platform.ts | 150 + utils/plugins/addDirPluginSettings.ts | 71 + utils/plugins/cacheUtils.ts | 196 + utils/plugins/dependencyResolver.ts | 305 + utils/plugins/fetchTelemetry.ts | 135 + utils/plugins/gitAvailability.ts | 69 + utils/plugins/headlessPluginInstall.ts | 174 + utils/plugins/hintRecommendation.ts | 164 + utils/plugins/installCounts.ts | 292 + utils/plugins/installedPluginsManager.ts | 1268 ++++ utils/plugins/loadPluginAgents.ts | 348 + utils/plugins/loadPluginCommands.ts | 946 +++ utils/plugins/loadPluginHooks.ts | 287 + utils/plugins/loadPluginOutputStyles.ts | 178 + utils/plugins/lspPluginIntegration.ts | 387 ++ utils/plugins/lspRecommendation.ts | 374 ++ utils/plugins/managedPlugins.ts | 27 + utils/plugins/marketplaceHelpers.ts | 592 ++ utils/plugins/marketplaceManager.ts | 2643 ++++++++ utils/plugins/mcpPluginIntegration.ts | 634 ++ utils/plugins/mcpbHandler.ts | 968 +++ utils/plugins/officialMarketplace.ts | 25 + utils/plugins/officialMarketplaceGcs.ts | 216 + .../officialMarketplaceStartupCheck.ts | 439 ++ utils/plugins/orphanedPluginFilter.ts | 114 + utils/plugins/parseMarketplaceInput.ts | 162 + utils/plugins/performStartupChecks.tsx | 70 + utils/plugins/pluginAutoupdate.ts | 284 + utils/plugins/pluginBlocklist.ts | 127 + utils/plugins/pluginDirectories.ts | 178 + utils/plugins/pluginFlagging.ts | 208 + utils/plugins/pluginIdentifier.ts | 123 + utils/plugins/pluginInstallationHelpers.ts | 595 ++ utils/plugins/pluginLoader.ts | 3302 ++++++++++ utils/plugins/pluginOptionsStorage.ts | 400 ++ utils/plugins/pluginPolicy.ts | 20 + utils/plugins/pluginStartupCheck.ts | 341 + utils/plugins/pluginVersioning.ts | 157 + utils/plugins/reconciler.ts | 265 + utils/plugins/refresh.ts | 215 + utils/plugins/schemas.ts | 1681 +++++ utils/plugins/validatePlugin.ts | 903 +++ utils/plugins/walkPluginMarkdown.ts | 69 + utils/plugins/zipCache.ts | 406 ++ utils/plugins/zipCacheAdapters.ts | 164 + utils/powershell/dangerousCmdlets.ts | 185 + utils/powershell/parser.ts | 1804 ++++++ utils/powershell/staticPrefix.ts | 316 + utils/preflightChecks.tsx | 151 + utils/privacyLevel.ts | 55 + utils/process.ts | 68 + utils/processUserInput/processBashCommand.tsx | 140 + .../processUserInput/processSlashCommand.tsx | 922 +++ utils/processUserInput/processTextPrompt.ts | 100 + utils/processUserInput/processUserInput.ts | 605 ++ utils/profilerBase.ts | 46 + utils/promptCategory.ts | 49 + utils/promptEditor.ts | 188 + utils/promptShellExecution.ts | 183 + utils/proxy.ts | 426 ++ utils/queryContext.ts | 179 + utils/queryHelpers.ts | 552 ++ utils/queryProfiler.ts | 301 + utils/queueProcessor.ts | 95 + utils/readEditContext.ts | 227 + utils/readFileInRange.ts | 383 ++ utils/releaseNotes.ts | 360 ++ utils/renderOptions.ts | 77 + utils/ripgrep.ts | 679 ++ utils/sandbox/sandbox-adapter.ts | 985 +++ utils/sandbox/sandbox-ui-utils.ts | 12 + utils/sanitization.ts | 91 + utils/screenshotClipboard.ts | 121 + utils/sdkEventQueue.ts | 134 + utils/secureStorage/fallbackStorage.ts | 70 + utils/secureStorage/index.ts | 17 + utils/secureStorage/keychainPrefetch.ts | 116 + utils/secureStorage/macOsKeychainHelpers.ts | 111 + utils/secureStorage/macOsKeychainStorage.ts | 231 + utils/secureStorage/plainTextStorage.ts | 84 + utils/semanticBoolean.ts | 29 + utils/semanticNumber.ts | 36 + utils/semver.ts | 59 + utils/sequential.ts | 56 + utils/sessionActivity.ts | 133 + utils/sessionEnvVars.ts | 22 + utils/sessionEnvironment.ts | 166 + utils/sessionFileAccessHooks.ts | 250 + utils/sessionIngressAuth.ts | 140 + utils/sessionRestore.ts | 551 ++ utils/sessionStart.ts | 232 + utils/sessionState.ts | 150 + utils/sessionStorage.ts | 5105 +++++++++++++++ utils/sessionStoragePortable.ts | 793 +++ utils/sessionTitle.ts | 129 + utils/sessionUrl.ts | 64 + utils/set.ts | 53 + utils/settings/allErrors.ts | 32 + utils/settings/applySettingsChange.ts | 92 + utils/settings/changeDetector.ts | 488 ++ utils/settings/constants.ts | 202 + utils/settings/internalWrites.ts | 37 + utils/settings/managedPath.ts | 34 + utils/settings/mdm/constants.ts | 81 + utils/settings/mdm/rawRead.ts | 130 + utils/settings/mdm/settings.ts | 316 + utils/settings/permissionValidation.ts | 262 + utils/settings/pluginOnlyPolicy.ts | 60 + utils/settings/schemaOutput.ts | 8 + utils/settings/settings.ts | 1015 +++ utils/settings/settingsCache.ts | 80 + utils/settings/toolValidationConfig.ts | 103 + utils/settings/types.ts | 1148 ++++ utils/settings/validateEditTool.ts | 45 + utils/settings/validation.ts | 265 + utils/settings/validationTips.ts | 164 + utils/shell/bashProvider.ts | 255 + utils/shell/outputLimits.ts | 14 + utils/shell/powershellDetection.ts | 107 + utils/shell/powershellProvider.ts | 123 + utils/shell/prefix.ts | 367 ++ utils/shell/readOnlyCommandValidation.ts | 1893 ++++++ utils/shell/resolveDefaultShell.ts | 14 + utils/shell/shellProvider.ts | 33 + utils/shell/shellToolUtils.ts | 22 + utils/shell/specPrefix.ts | 241 + utils/shellConfig.ts | 167 + utils/sideQuery.ts | 222 + utils/sideQuestion.ts | 155 + utils/signal.ts | 43 + utils/sinks.ts | 16 + utils/skills/skillChangeDetector.ts | 311 + utils/slashCommandParsing.ts | 60 + utils/sleep.ts | 84 + utils/sliceAnsi.ts | 91 + utils/slowOperations.ts | 286 + utils/standaloneAgent.ts | 23 + utils/startupProfiler.ts | 194 + utils/staticRender.tsx | 116 + utils/stats.ts | 1061 ++++ utils/statsCache.ts | 434 ++ utils/status.tsx | 362 ++ utils/statusNoticeDefinitions.tsx | 198 + utils/statusNoticeHelpers.ts | 20 + utils/stream.ts | 76 + utils/streamJsonStdoutGuard.ts | 123 + utils/streamlinedTransform.ts | 201 + utils/stringUtils.ts | 235 + utils/subprocessEnv.ts | 99 + utils/suggestions/commandSuggestions.ts | 567 ++ utils/suggestions/directoryCompletion.ts | 263 + utils/suggestions/shellHistoryCompletion.ts | 119 + utils/suggestions/skillUsageTracking.ts | 55 + utils/suggestions/slackChannelSuggestions.ts | 209 + utils/swarm/It2SetupPrompt.tsx | 380 ++ utils/swarm/backends/ITermBackend.ts | 370 ++ utils/swarm/backends/InProcessBackend.ts | 339 + utils/swarm/backends/PaneBackendExecutor.ts | 354 ++ utils/swarm/backends/TmuxBackend.ts | 764 +++ utils/swarm/backends/detection.ts | 128 + utils/swarm/backends/it2Setup.ts | 245 + utils/swarm/backends/registry.ts | 464 ++ utils/swarm/backends/teammateModeSnapshot.ts | 87 + utils/swarm/backends/types.ts | 311 + utils/swarm/constants.ts | 33 + utils/swarm/inProcessRunner.ts | 1552 +++++ utils/swarm/leaderPermissionBridge.ts | 54 + utils/swarm/permissionSync.ts | 928 +++ utils/swarm/reconnection.ts | 119 + utils/swarm/spawnInProcess.ts | 328 + utils/swarm/spawnUtils.ts | 146 + utils/swarm/teamHelpers.ts | 683 ++ utils/swarm/teammateInit.ts | 129 + utils/swarm/teammateLayoutManager.ts | 107 + utils/swarm/teammateModel.ts | 10 + utils/swarm/teammatePromptAddendum.ts | 18 + utils/systemDirectories.ts | 74 + utils/systemPrompt.ts | 123 + utils/systemPromptType.ts | 14 + utils/systemTheme.ts | 119 + utils/taggedId.ts | 54 + utils/task/TaskOutput.ts | 390 ++ utils/task/diskOutput.ts | 451 ++ utils/task/framework.ts | 308 + utils/task/outputFormatting.ts | 38 + utils/task/sdkProgress.ts | 36 + utils/tasks.ts | 862 +++ utils/teamDiscovery.ts | 81 + utils/teamMemoryOps.ts | 88 + utils/teammate.ts | 292 + utils/teammateContext.ts | 96 + utils/teammateMailbox.ts | 1183 ++++ utils/telemetry/betaSessionTracing.ts | 491 ++ utils/telemetry/bigqueryExporter.ts | 252 + utils/telemetry/events.ts | 75 + utils/telemetry/instrumentation.ts | 825 +++ utils/telemetry/logger.ts | 26 + utils/telemetry/perfettoTracing.ts | 1120 ++++ utils/telemetry/pluginTelemetry.ts | 289 + utils/telemetry/sessionTracing.ts | 927 +++ utils/telemetry/skillLoadedEvent.ts | 39 + utils/telemetryAttributes.ts | 71 + utils/teleport.tsx | 1226 ++++ utils/teleport/api.ts | 466 ++ utils/teleport/environmentSelection.ts | 77 + utils/teleport/environments.ts | 120 + utils/teleport/gitBundle.ts | 292 + utils/tempfile.ts | 31 + utils/terminal.ts | 131 + utils/terminalPanel.ts | 191 + utils/textHighlighting.ts | 166 + utils/theme.ts | 639 ++ utils/thinking.ts | 162 + utils/timeouts.ts | 39 + utils/tmuxSocket.ts | 427 ++ utils/todo/types.ts | 18 + utils/tokenBudget.ts | 73 + utils/tokens.ts | 261 + utils/toolErrors.ts | 132 + utils/toolPool.ts | 79 + utils/toolResultStorage.ts | 1040 +++ utils/toolSchemaCache.ts | 26 + utils/toolSearch.ts | 756 +++ utils/transcriptSearch.ts | 202 + utils/treeify.ts | 170 + utils/truncate.ts | 179 + utils/ultraplan/ccrSession.ts | 349 + utils/ultraplan/keyword.ts | 127 + utils/unaryLogging.ts | 39 + utils/undercover.ts | 89 + utils/user.ts | 194 + utils/userAgent.ts | 10 + utils/userPromptKeywords.ts | 27 + utils/uuid.ts | 27 + utils/warningHandler.ts | 121 + utils/which.ts | 82 + utils/windowsPaths.ts | 173 + utils/withResolvers.ts | 13 + utils/words.ts | 800 +++ utils/workloadContext.ts | 57 + utils/worktree.ts | 1519 +++++ utils/worktreeModeEnabled.ts | 11 + utils/xdg.ts | 65 + utils/xml.ts | 16 + utils/yaml.ts | 15 + utils/zodToJsonSchema.ts | 23 + vim/motions.ts | 82 + vim/operators.ts | 556 ++ vim/textObjects.ts | 186 + vim/transitions.ts | 490 ++ vim/types.ts | 199 + voice/voiceModeEnabled.ts | 54 + 1905 files changed, 513700 insertions(+) create mode 100644 QueryEngine.ts create mode 100644 README.md create mode 100644 Task.ts create mode 100644 Tool.ts create mode 100644 assistant/sessionHistory.ts create mode 100644 bootstrap/state.ts create mode 100644 bridge/bridgeApi.ts create mode 100644 bridge/bridgeConfig.ts create mode 100644 bridge/bridgeDebug.ts create mode 100644 bridge/bridgeEnabled.ts create mode 100644 bridge/bridgeMain.ts create mode 100644 bridge/bridgeMessaging.ts create mode 100644 bridge/bridgePermissionCallbacks.ts create mode 100644 bridge/bridgePointer.ts create mode 100644 bridge/bridgeStatusUtil.ts create mode 100644 bridge/bridgeUI.ts create mode 100644 bridge/capacityWake.ts create mode 100644 bridge/codeSessionApi.ts create mode 100644 bridge/createSession.ts create mode 100644 bridge/debugUtils.ts create mode 100644 bridge/envLessBridgeConfig.ts create mode 100644 bridge/flushGate.ts create mode 100644 bridge/inboundAttachments.ts create mode 100644 bridge/inboundMessages.ts create mode 100644 bridge/initReplBridge.ts create mode 100644 bridge/jwtUtils.ts create mode 100644 bridge/pollConfig.ts create mode 100644 bridge/pollConfigDefaults.ts create mode 100644 bridge/remoteBridgeCore.ts create mode 100644 bridge/replBridge.ts create mode 100644 bridge/replBridgeHandle.ts create mode 100644 bridge/replBridgeTransport.ts create mode 100644 bridge/sessionIdCompat.ts create mode 100644 bridge/sessionRunner.ts create mode 100644 bridge/trustedDevice.ts create mode 100644 bridge/types.ts create mode 100644 bridge/workSecret.ts create mode 100644 buddy/CompanionSprite.tsx create mode 100644 buddy/companion.ts create mode 100644 buddy/prompt.ts create mode 100644 buddy/sprites.ts create mode 100644 buddy/types.ts create mode 100644 buddy/useBuddyNotification.tsx create mode 100644 cli/exit.ts create mode 100644 cli/handlers/agents.ts create mode 100644 cli/handlers/auth.ts create mode 100644 cli/handlers/autoMode.ts create mode 100644 cli/handlers/mcp.tsx create mode 100644 cli/handlers/plugins.ts create mode 100644 cli/handlers/util.tsx create mode 100644 cli/ndjsonSafeStringify.ts create mode 100644 cli/print.ts create mode 100644 cli/remoteIO.ts create mode 100644 cli/structuredIO.ts create mode 100644 cli/transports/HybridTransport.ts create mode 100644 cli/transports/SSETransport.ts create mode 100644 cli/transports/SerialBatchEventUploader.ts create mode 100644 cli/transports/WebSocketTransport.ts create mode 100644 cli/transports/WorkerStateUploader.ts create mode 100644 cli/transports/ccrClient.ts create mode 100644 cli/transports/transportUtils.ts create mode 100644 cli/update.ts create mode 100644 commands.ts create mode 100644 commands/add-dir/add-dir.tsx create mode 100644 commands/add-dir/index.ts create mode 100644 commands/add-dir/validation.ts create mode 100644 commands/advisor.ts create mode 100644 commands/agents/agents.tsx create mode 100644 commands/agents/index.ts create mode 100644 commands/ant-trace/index.js create mode 100644 commands/autofix-pr/index.js create mode 100644 commands/backfill-sessions/index.js create mode 100644 commands/branch/branch.ts create mode 100644 commands/branch/index.ts create mode 100644 commands/break-cache/index.js create mode 100644 commands/bridge-kick.ts create mode 100644 commands/bridge/bridge.tsx create mode 100644 commands/bridge/index.ts create mode 100644 commands/brief.ts create mode 100644 commands/btw/btw.tsx create mode 100644 commands/btw/index.ts create mode 100644 commands/bughunter/index.js create mode 100644 commands/chrome/chrome.tsx create mode 100644 commands/chrome/index.ts create mode 100644 commands/clear/caches.ts create mode 100644 commands/clear/clear.ts create mode 100644 commands/clear/conversation.ts create mode 100644 commands/clear/index.ts create mode 100644 commands/color/color.ts create mode 100644 commands/color/index.ts create mode 100644 commands/commit-push-pr.ts create mode 100644 commands/commit.ts create mode 100644 commands/compact/compact.ts create mode 100644 commands/compact/index.ts create mode 100644 commands/config/config.tsx create mode 100644 commands/config/index.ts create mode 100644 commands/context/context-noninteractive.ts create mode 100644 commands/context/context.tsx create mode 100644 commands/context/index.ts create mode 100644 commands/copy/copy.tsx create mode 100644 commands/copy/index.ts create mode 100644 commands/cost/cost.ts create mode 100644 commands/cost/index.ts create mode 100644 commands/createMovedToPluginCommand.ts create mode 100644 commands/ctx_viz/index.js create mode 100644 commands/debug-tool-call/index.js create mode 100644 commands/desktop/desktop.tsx create mode 100644 commands/desktop/index.ts create mode 100644 commands/diff/diff.tsx create mode 100644 commands/diff/index.ts create mode 100644 commands/doctor/doctor.tsx create mode 100644 commands/doctor/index.ts create mode 100644 commands/effort/effort.tsx create mode 100644 commands/effort/index.ts create mode 100644 commands/env/index.js create mode 100644 commands/exit/exit.tsx create mode 100644 commands/exit/index.ts create mode 100644 commands/export/export.tsx create mode 100644 commands/export/index.ts create mode 100644 commands/extra-usage/extra-usage-core.ts create mode 100644 commands/extra-usage/extra-usage-noninteractive.ts create mode 100644 commands/extra-usage/extra-usage.tsx create mode 100644 commands/extra-usage/index.ts create mode 100644 commands/fast/fast.tsx create mode 100644 commands/fast/index.ts create mode 100644 commands/feedback/feedback.tsx create mode 100644 commands/feedback/index.ts create mode 100644 commands/files/files.ts create mode 100644 commands/files/index.ts create mode 100644 commands/good-claude/index.js create mode 100644 commands/heapdump/heapdump.ts create mode 100644 commands/heapdump/index.ts create mode 100644 commands/help/help.tsx create mode 100644 commands/help/index.ts create mode 100644 commands/hooks/hooks.tsx create mode 100644 commands/hooks/index.ts create mode 100644 commands/ide/ide.tsx create mode 100644 commands/ide/index.ts create mode 100644 commands/init-verifiers.ts create mode 100644 commands/init.ts create mode 100644 commands/insights.ts create mode 100644 commands/install-github-app/ApiKeyStep.tsx create mode 100644 commands/install-github-app/CheckExistingSecretStep.tsx create mode 100644 commands/install-github-app/CheckGitHubStep.tsx create mode 100644 commands/install-github-app/ChooseRepoStep.tsx create mode 100644 commands/install-github-app/CreatingStep.tsx create mode 100644 commands/install-github-app/ErrorStep.tsx create mode 100644 commands/install-github-app/ExistingWorkflowStep.tsx create mode 100644 commands/install-github-app/InstallAppStep.tsx create mode 100644 commands/install-github-app/OAuthFlowStep.tsx create mode 100644 commands/install-github-app/SuccessStep.tsx create mode 100644 commands/install-github-app/WarningsStep.tsx create mode 100644 commands/install-github-app/index.ts create mode 100644 commands/install-github-app/install-github-app.tsx create mode 100644 commands/install-github-app/setupGitHubActions.ts create mode 100644 commands/install-slack-app/index.ts create mode 100644 commands/install-slack-app/install-slack-app.ts create mode 100644 commands/install.tsx create mode 100644 commands/issue/index.js create mode 100644 commands/keybindings/index.ts create mode 100644 commands/keybindings/keybindings.ts create mode 100644 commands/login/index.ts create mode 100644 commands/login/login.tsx create mode 100644 commands/logout/index.ts create mode 100644 commands/logout/logout.tsx create mode 100644 commands/mcp/addCommand.ts create mode 100644 commands/mcp/index.ts create mode 100644 commands/mcp/mcp.tsx create mode 100644 commands/mcp/xaaIdpCommand.ts create mode 100644 commands/memory/index.ts create mode 100644 commands/memory/memory.tsx create mode 100644 commands/mobile/index.ts create mode 100644 commands/mobile/mobile.tsx create mode 100644 commands/mock-limits/index.js create mode 100644 commands/model/index.ts create mode 100644 commands/model/model.tsx create mode 100644 commands/oauth-refresh/index.js create mode 100644 commands/onboarding/index.js create mode 100644 commands/output-style/index.ts create mode 100644 commands/output-style/output-style.tsx create mode 100644 commands/passes/index.ts create mode 100644 commands/passes/passes.tsx create mode 100644 commands/perf-issue/index.js create mode 100644 commands/permissions/index.ts create mode 100644 commands/permissions/permissions.tsx create mode 100644 commands/plan/index.ts create mode 100644 commands/plan/plan.tsx create mode 100644 commands/plugin/AddMarketplace.tsx create mode 100644 commands/plugin/BrowseMarketplace.tsx create mode 100644 commands/plugin/DiscoverPlugins.tsx create mode 100644 commands/plugin/ManageMarketplaces.tsx create mode 100644 commands/plugin/ManagePlugins.tsx create mode 100644 commands/plugin/PluginErrors.tsx create mode 100644 commands/plugin/PluginOptionsDialog.tsx create mode 100644 commands/plugin/PluginOptionsFlow.tsx create mode 100644 commands/plugin/PluginSettings.tsx create mode 100644 commands/plugin/PluginTrustWarning.tsx create mode 100644 commands/plugin/UnifiedInstalledCell.tsx create mode 100644 commands/plugin/ValidatePlugin.tsx create mode 100644 commands/plugin/index.tsx create mode 100644 commands/plugin/parseArgs.ts create mode 100644 commands/plugin/plugin.tsx create mode 100644 commands/plugin/pluginDetailsHelpers.tsx create mode 100644 commands/plugin/usePagination.ts create mode 100644 commands/pr_comments/index.ts create mode 100644 commands/privacy-settings/index.ts create mode 100644 commands/privacy-settings/privacy-settings.tsx create mode 100644 commands/rate-limit-options/index.ts create mode 100644 commands/rate-limit-options/rate-limit-options.tsx create mode 100644 commands/release-notes/index.ts create mode 100644 commands/release-notes/release-notes.ts create mode 100644 commands/reload-plugins/index.ts create mode 100644 commands/reload-plugins/reload-plugins.ts create mode 100644 commands/remote-env/index.ts create mode 100644 commands/remote-env/remote-env.tsx create mode 100644 commands/remote-setup/api.ts create mode 100644 commands/remote-setup/index.ts create mode 100644 commands/remote-setup/remote-setup.tsx create mode 100644 commands/rename/generateSessionName.ts create mode 100644 commands/rename/index.ts create mode 100644 commands/rename/rename.ts create mode 100644 commands/reset-limits/index.js create mode 100644 commands/resume/index.ts create mode 100644 commands/resume/resume.tsx create mode 100644 commands/review.ts create mode 100644 commands/review/UltrareviewOverageDialog.tsx create mode 100644 commands/review/reviewRemote.ts create mode 100644 commands/review/ultrareviewCommand.tsx create mode 100644 commands/review/ultrareviewEnabled.ts create mode 100644 commands/rewind/index.ts create mode 100644 commands/rewind/rewind.ts create mode 100644 commands/sandbox-toggle/index.ts create mode 100644 commands/sandbox-toggle/sandbox-toggle.tsx create mode 100644 commands/security-review.ts create mode 100644 commands/session/index.ts create mode 100644 commands/session/session.tsx create mode 100644 commands/share/index.js create mode 100644 commands/skills/index.ts create mode 100644 commands/skills/skills.tsx create mode 100644 commands/stats/index.ts create mode 100644 commands/stats/stats.tsx create mode 100644 commands/status/index.ts create mode 100644 commands/status/status.tsx create mode 100644 commands/statusline.tsx create mode 100644 commands/stickers/index.ts create mode 100644 commands/stickers/stickers.ts create mode 100644 commands/summary/index.js create mode 100644 commands/tag/index.ts create mode 100644 commands/tag/tag.tsx create mode 100644 commands/tasks/index.ts create mode 100644 commands/tasks/tasks.tsx create mode 100644 commands/teleport/index.js create mode 100644 commands/terminalSetup/index.ts create mode 100644 commands/terminalSetup/terminalSetup.tsx create mode 100644 commands/theme/index.ts create mode 100644 commands/theme/theme.tsx create mode 100644 commands/thinkback-play/index.ts create mode 100644 commands/thinkback-play/thinkback-play.ts create mode 100644 commands/thinkback/index.ts create mode 100644 commands/thinkback/thinkback.tsx create mode 100644 commands/ultraplan.tsx create mode 100644 commands/upgrade/index.ts create mode 100644 commands/upgrade/upgrade.tsx create mode 100644 commands/usage/index.ts create mode 100644 commands/usage/usage.tsx create mode 100644 commands/version.ts create mode 100644 commands/vim/index.ts create mode 100644 commands/vim/vim.ts create mode 100644 commands/voice/index.ts create mode 100644 commands/voice/voice.ts create mode 100644 components/AgentProgressLine.tsx create mode 100644 components/App.tsx create mode 100644 components/ApproveApiKey.tsx create mode 100644 components/AutoModeOptInDialog.tsx create mode 100644 components/AutoUpdater.tsx create mode 100644 components/AutoUpdaterWrapper.tsx create mode 100644 components/AwsAuthStatusBox.tsx create mode 100644 components/BaseTextInput.tsx create mode 100644 components/BashModeProgress.tsx create mode 100644 components/BridgeDialog.tsx create mode 100644 components/BypassPermissionsModeDialog.tsx create mode 100644 components/ChannelDowngradeDialog.tsx create mode 100644 components/ClaudeCodeHint/PluginHintMenu.tsx create mode 100644 components/ClaudeInChromeOnboarding.tsx create mode 100644 components/ClaudeMdExternalIncludesDialog.tsx create mode 100644 components/ClickableImageRef.tsx create mode 100644 components/CompactSummary.tsx create mode 100644 components/ConfigurableShortcutHint.tsx create mode 100644 components/ConsoleOAuthFlow.tsx create mode 100644 components/ContextSuggestions.tsx create mode 100644 components/ContextVisualization.tsx create mode 100644 components/CoordinatorAgentStatus.tsx create mode 100644 components/CostThresholdDialog.tsx create mode 100644 components/CtrlOToExpand.tsx create mode 100644 components/CustomSelect/SelectMulti.tsx create mode 100644 components/CustomSelect/index.ts create mode 100644 components/CustomSelect/option-map.ts create mode 100644 components/CustomSelect/select-input-option.tsx create mode 100644 components/CustomSelect/select-option.tsx create mode 100644 components/CustomSelect/select.tsx create mode 100644 components/CustomSelect/use-multi-select-state.ts create mode 100644 components/CustomSelect/use-select-input.ts create mode 100644 components/CustomSelect/use-select-navigation.ts create mode 100644 components/CustomSelect/use-select-state.ts create mode 100644 components/DesktopHandoff.tsx create mode 100644 components/DesktopUpsell/DesktopUpsellStartup.tsx create mode 100644 components/DevBar.tsx create mode 100644 components/DevChannelsDialog.tsx create mode 100644 components/DiagnosticsDisplay.tsx create mode 100644 components/EffortCallout.tsx create mode 100644 components/EffortIndicator.ts create mode 100644 components/ExitFlow.tsx create mode 100644 components/ExportDialog.tsx create mode 100644 components/FallbackToolUseErrorMessage.tsx create mode 100644 components/FallbackToolUseRejectedMessage.tsx create mode 100644 components/FastIcon.tsx create mode 100644 components/Feedback.tsx create mode 100644 components/FeedbackSurvey/FeedbackSurvey.tsx create mode 100644 components/FeedbackSurvey/FeedbackSurveyView.tsx create mode 100644 components/FeedbackSurvey/TranscriptSharePrompt.tsx create mode 100644 components/FeedbackSurvey/submitTranscriptShare.ts create mode 100644 components/FeedbackSurvey/useDebouncedDigitInput.ts create mode 100644 components/FeedbackSurvey/useFeedbackSurvey.tsx create mode 100644 components/FeedbackSurvey/useMemorySurvey.tsx create mode 100644 components/FeedbackSurvey/usePostCompactSurvey.tsx create mode 100644 components/FeedbackSurvey/useSurveyState.tsx create mode 100644 components/FileEditToolDiff.tsx create mode 100644 components/FileEditToolUpdatedMessage.tsx create mode 100644 components/FileEditToolUseRejectedMessage.tsx create mode 100644 components/FilePathLink.tsx create mode 100644 components/FullscreenLayout.tsx create mode 100644 components/GlobalSearchDialog.tsx create mode 100644 components/HelpV2/Commands.tsx create mode 100644 components/HelpV2/General.tsx create mode 100644 components/HelpV2/HelpV2.tsx create mode 100644 components/HighlightedCode.tsx create mode 100644 components/HighlightedCode/Fallback.tsx create mode 100644 components/HistorySearchDialog.tsx create mode 100644 components/IdeAutoConnectDialog.tsx create mode 100644 components/IdeOnboardingDialog.tsx create mode 100644 components/IdeStatusIndicator.tsx create mode 100644 components/IdleReturnDialog.tsx create mode 100644 components/InterruptedByUser.tsx create mode 100644 components/InvalidConfigDialog.tsx create mode 100644 components/InvalidSettingsDialog.tsx create mode 100644 components/KeybindingWarnings.tsx create mode 100644 components/LanguagePicker.tsx create mode 100644 components/LogSelector.tsx create mode 100644 components/LogoV2/AnimatedAsterisk.tsx create mode 100644 components/LogoV2/AnimatedClawd.tsx create mode 100644 components/LogoV2/ChannelsNotice.tsx create mode 100644 components/LogoV2/Clawd.tsx create mode 100644 components/LogoV2/CondensedLogo.tsx create mode 100644 components/LogoV2/EmergencyTip.tsx create mode 100644 components/LogoV2/Feed.tsx create mode 100644 components/LogoV2/FeedColumn.tsx create mode 100644 components/LogoV2/GuestPassesUpsell.tsx create mode 100644 components/LogoV2/LogoV2.tsx create mode 100644 components/LogoV2/Opus1mMergeNotice.tsx create mode 100644 components/LogoV2/OverageCreditUpsell.tsx create mode 100644 components/LogoV2/VoiceModeNotice.tsx create mode 100644 components/LogoV2/WelcomeV2.tsx create mode 100644 components/LogoV2/feedConfigs.tsx create mode 100644 components/LspRecommendation/LspRecommendationMenu.tsx create mode 100644 components/MCPServerApprovalDialog.tsx create mode 100644 components/MCPServerDesktopImportDialog.tsx create mode 100644 components/MCPServerDialogCopy.tsx create mode 100644 components/MCPServerMultiselectDialog.tsx create mode 100644 components/ManagedSettingsSecurityDialog/ManagedSettingsSecurityDialog.tsx create mode 100644 components/ManagedSettingsSecurityDialog/utils.ts create mode 100644 components/Markdown.tsx create mode 100644 components/MarkdownTable.tsx create mode 100644 components/MemoryUsageIndicator.tsx create mode 100644 components/Message.tsx create mode 100644 components/MessageModel.tsx create mode 100644 components/MessageResponse.tsx create mode 100644 components/MessageRow.tsx create mode 100644 components/MessageSelector.tsx create mode 100644 components/MessageTimestamp.tsx create mode 100644 components/Messages.tsx create mode 100644 components/ModelPicker.tsx create mode 100644 components/NativeAutoUpdater.tsx create mode 100644 components/NotebookEditToolUseRejectedMessage.tsx create mode 100644 components/OffscreenFreeze.tsx create mode 100644 components/Onboarding.tsx create mode 100644 components/OutputStylePicker.tsx create mode 100644 components/PackageManagerAutoUpdater.tsx create mode 100644 components/Passes/Passes.tsx create mode 100644 components/PrBadge.tsx create mode 100644 components/PressEnterToContinue.tsx create mode 100644 components/PromptInput/HistorySearchInput.tsx create mode 100644 components/PromptInput/IssueFlagBanner.tsx create mode 100644 components/PromptInput/Notifications.tsx create mode 100644 components/PromptInput/PromptInput.tsx create mode 100644 components/PromptInput/PromptInputFooter.tsx create mode 100644 components/PromptInput/PromptInputFooterLeftSide.tsx create mode 100644 components/PromptInput/PromptInputFooterSuggestions.tsx create mode 100644 components/PromptInput/PromptInputHelpMenu.tsx create mode 100644 components/PromptInput/PromptInputModeIndicator.tsx create mode 100644 components/PromptInput/PromptInputQueuedCommands.tsx create mode 100644 components/PromptInput/PromptInputStashNotice.tsx create mode 100644 components/PromptInput/SandboxPromptFooterHint.tsx create mode 100644 components/PromptInput/ShimmeredInput.tsx create mode 100644 components/PromptInput/VoiceIndicator.tsx create mode 100644 components/PromptInput/inputModes.ts create mode 100644 components/PromptInput/inputPaste.ts create mode 100644 components/PromptInput/useMaybeTruncateInput.ts create mode 100644 components/PromptInput/usePromptInputPlaceholder.ts create mode 100644 components/PromptInput/useShowFastIconHint.ts create mode 100644 components/PromptInput/useSwarmBanner.ts create mode 100644 components/PromptInput/utils.ts create mode 100644 components/QuickOpenDialog.tsx create mode 100644 components/RemoteCallout.tsx create mode 100644 components/RemoteEnvironmentDialog.tsx create mode 100644 components/ResumeTask.tsx create mode 100644 components/SandboxViolationExpandedView.tsx create mode 100644 components/ScrollKeybindingHandler.tsx create mode 100644 components/SearchBox.tsx create mode 100644 components/SentryErrorBoundary.ts create mode 100644 components/SessionBackgroundHint.tsx create mode 100644 components/SessionPreview.tsx create mode 100644 components/Settings/Config.tsx create mode 100644 components/Settings/Settings.tsx create mode 100644 components/Settings/Status.tsx create mode 100644 components/Settings/Usage.tsx create mode 100644 components/ShowInIDEPrompt.tsx create mode 100644 components/SkillImprovementSurvey.tsx create mode 100644 components/Spinner.tsx create mode 100644 components/Spinner/FlashingChar.tsx create mode 100644 components/Spinner/GlimmerMessage.tsx create mode 100644 components/Spinner/ShimmerChar.tsx create mode 100644 components/Spinner/SpinnerAnimationRow.tsx create mode 100644 components/Spinner/SpinnerGlyph.tsx create mode 100644 components/Spinner/TeammateSpinnerLine.tsx create mode 100644 components/Spinner/TeammateSpinnerTree.tsx create mode 100644 components/Spinner/index.ts create mode 100644 components/Spinner/teammateSelectHint.ts create mode 100644 components/Spinner/useShimmerAnimation.ts create mode 100644 components/Spinner/useStalledAnimation.ts create mode 100644 components/Spinner/utils.ts create mode 100644 components/Stats.tsx create mode 100644 components/StatusLine.tsx create mode 100644 components/StatusNotices.tsx create mode 100644 components/StructuredDiff.tsx create mode 100644 components/StructuredDiff/Fallback.tsx create mode 100644 components/StructuredDiff/colorDiff.ts create mode 100644 components/StructuredDiffList.tsx create mode 100644 components/TagTabs.tsx create mode 100644 components/TaskListV2.tsx create mode 100644 components/TeammateViewHeader.tsx create mode 100644 components/TeleportError.tsx create mode 100644 components/TeleportProgress.tsx create mode 100644 components/TeleportRepoMismatchDialog.tsx create mode 100644 components/TeleportResumeWrapper.tsx create mode 100644 components/TeleportStash.tsx create mode 100644 components/TextInput.tsx create mode 100644 components/ThemePicker.tsx create mode 100644 components/ThinkingToggle.tsx create mode 100644 components/TokenWarning.tsx create mode 100644 components/ToolUseLoader.tsx create mode 100644 components/TrustDialog/TrustDialog.tsx create mode 100644 components/TrustDialog/utils.ts create mode 100644 components/ValidationErrorsList.tsx create mode 100644 components/VimTextInput.tsx create mode 100644 components/VirtualMessageList.tsx create mode 100644 components/WorkflowMultiselectDialog.tsx create mode 100644 components/WorktreeExitDialog.tsx create mode 100644 components/agents/AgentDetail.tsx create mode 100644 components/agents/AgentEditor.tsx create mode 100644 components/agents/AgentNavigationFooter.tsx create mode 100644 components/agents/AgentsList.tsx create mode 100644 components/agents/AgentsMenu.tsx create mode 100644 components/agents/ColorPicker.tsx create mode 100644 components/agents/ModelSelector.tsx create mode 100644 components/agents/ToolSelector.tsx create mode 100644 components/agents/agentFileUtils.ts create mode 100644 components/agents/generateAgent.ts create mode 100644 components/agents/new-agent-creation/CreateAgentWizard.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/ColorStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/LocationStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/MemoryStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/MethodStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/ModelStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/PromptStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/ToolsStep.tsx create mode 100644 components/agents/new-agent-creation/wizard-steps/TypeStep.tsx create mode 100644 components/agents/types.ts create mode 100644 components/agents/utils.ts create mode 100644 components/agents/validateAgent.ts create mode 100644 components/design-system/Byline.tsx create mode 100644 components/design-system/Dialog.tsx create mode 100644 components/design-system/Divider.tsx create mode 100644 components/design-system/FuzzyPicker.tsx create mode 100644 components/design-system/KeyboardShortcutHint.tsx create mode 100644 components/design-system/ListItem.tsx create mode 100644 components/design-system/LoadingState.tsx create mode 100644 components/design-system/Pane.tsx create mode 100644 components/design-system/ProgressBar.tsx create mode 100644 components/design-system/Ratchet.tsx create mode 100644 components/design-system/StatusIcon.tsx create mode 100644 components/design-system/Tabs.tsx create mode 100644 components/design-system/ThemeProvider.tsx create mode 100644 components/design-system/ThemedBox.tsx create mode 100644 components/design-system/ThemedText.tsx create mode 100644 components/design-system/color.ts create mode 100644 components/diff/DiffDetailView.tsx create mode 100644 components/diff/DiffDialog.tsx create mode 100644 components/diff/DiffFileList.tsx create mode 100644 components/grove/Grove.tsx create mode 100644 components/hooks/HooksConfigMenu.tsx create mode 100644 components/hooks/PromptDialog.tsx create mode 100644 components/hooks/SelectEventMode.tsx create mode 100644 components/hooks/SelectHookMode.tsx create mode 100644 components/hooks/SelectMatcherMode.tsx create mode 100644 components/hooks/ViewHookMode.tsx create mode 100644 components/mcp/CapabilitiesSection.tsx create mode 100644 components/mcp/ElicitationDialog.tsx create mode 100644 components/mcp/MCPAgentServerMenu.tsx create mode 100644 components/mcp/MCPListPanel.tsx create mode 100644 components/mcp/MCPReconnect.tsx create mode 100644 components/mcp/MCPRemoteServerMenu.tsx create mode 100644 components/mcp/MCPSettings.tsx create mode 100644 components/mcp/MCPStdioServerMenu.tsx create mode 100644 components/mcp/MCPToolDetailView.tsx create mode 100644 components/mcp/MCPToolListView.tsx create mode 100644 components/mcp/McpParsingWarnings.tsx create mode 100644 components/mcp/index.ts create mode 100644 components/mcp/utils/reconnectHelpers.tsx create mode 100644 components/memory/MemoryFileSelector.tsx create mode 100644 components/memory/MemoryUpdateNotification.tsx create mode 100644 components/messageActions.tsx create mode 100644 components/messages/AdvisorMessage.tsx create mode 100644 components/messages/AssistantRedactedThinkingMessage.tsx create mode 100644 components/messages/AssistantTextMessage.tsx create mode 100644 components/messages/AssistantThinkingMessage.tsx create mode 100644 components/messages/AssistantToolUseMessage.tsx create mode 100644 components/messages/AttachmentMessage.tsx create mode 100644 components/messages/CollapsedReadSearchContent.tsx create mode 100644 components/messages/CompactBoundaryMessage.tsx create mode 100644 components/messages/GroupedToolUseContent.tsx create mode 100644 components/messages/HighlightedThinkingText.tsx create mode 100644 components/messages/HookProgressMessage.tsx create mode 100644 components/messages/PlanApprovalMessage.tsx create mode 100644 components/messages/RateLimitMessage.tsx create mode 100644 components/messages/ShutdownMessage.tsx create mode 100644 components/messages/SystemAPIErrorMessage.tsx create mode 100644 components/messages/SystemTextMessage.tsx create mode 100644 components/messages/TaskAssignmentMessage.tsx create mode 100644 components/messages/UserAgentNotificationMessage.tsx create mode 100644 components/messages/UserBashInputMessage.tsx create mode 100644 components/messages/UserBashOutputMessage.tsx create mode 100644 components/messages/UserChannelMessage.tsx create mode 100644 components/messages/UserCommandMessage.tsx create mode 100644 components/messages/UserImageMessage.tsx create mode 100644 components/messages/UserLocalCommandOutputMessage.tsx create mode 100644 components/messages/UserMemoryInputMessage.tsx create mode 100644 components/messages/UserPlanMessage.tsx create mode 100644 components/messages/UserPromptMessage.tsx create mode 100644 components/messages/UserResourceUpdateMessage.tsx create mode 100644 components/messages/UserTeammateMessage.tsx create mode 100644 components/messages/UserTextMessage.tsx create mode 100644 components/messages/UserToolResultMessage/RejectedPlanMessage.tsx create mode 100644 components/messages/UserToolResultMessage/RejectedToolUseMessage.tsx create mode 100644 components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx create mode 100644 components/messages/UserToolResultMessage/UserToolErrorMessage.tsx create mode 100644 components/messages/UserToolResultMessage/UserToolRejectMessage.tsx create mode 100644 components/messages/UserToolResultMessage/UserToolResultMessage.tsx create mode 100644 components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx create mode 100644 components/messages/UserToolResultMessage/utils.tsx create mode 100644 components/messages/nullRenderingAttachments.ts create mode 100644 components/messages/teamMemCollapsed.tsx create mode 100644 components/messages/teamMemSaved.ts create mode 100644 components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.tsx create mode 100644 components/permissions/AskUserQuestionPermissionRequest/PreviewBox.tsx create mode 100644 components/permissions/AskUserQuestionPermissionRequest/PreviewQuestionView.tsx create mode 100644 components/permissions/AskUserQuestionPermissionRequest/QuestionNavigationBar.tsx create mode 100644 components/permissions/AskUserQuestionPermissionRequest/QuestionView.tsx create mode 100644 components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx create mode 100644 components/permissions/AskUserQuestionPermissionRequest/use-multiple-choice-state.ts create mode 100644 components/permissions/BashPermissionRequest/BashPermissionRequest.tsx create mode 100644 components/permissions/BashPermissionRequest/bashToolUseOptions.tsx create mode 100644 components/permissions/ComputerUseApproval/ComputerUseApproval.tsx create mode 100644 components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx create mode 100644 components/permissions/ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.tsx create mode 100644 components/permissions/FallbackPermissionRequest.tsx create mode 100644 components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx create mode 100644 components/permissions/FilePermissionDialog/FilePermissionDialog.tsx create mode 100644 components/permissions/FilePermissionDialog/ideDiffConfig.ts create mode 100644 components/permissions/FilePermissionDialog/permissionOptions.tsx create mode 100644 components/permissions/FilePermissionDialog/useFilePermissionDialog.ts create mode 100644 components/permissions/FilePermissionDialog/usePermissionHandler.ts create mode 100644 components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx create mode 100644 components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx create mode 100644 components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx create mode 100644 components/permissions/NotebookEditPermissionRequest/NotebookEditPermissionRequest.tsx create mode 100644 components/permissions/NotebookEditPermissionRequest/NotebookEditToolDiff.tsx create mode 100644 components/permissions/PermissionDecisionDebugInfo.tsx create mode 100644 components/permissions/PermissionDialog.tsx create mode 100644 components/permissions/PermissionExplanation.tsx create mode 100644 components/permissions/PermissionPrompt.tsx create mode 100644 components/permissions/PermissionRequest.tsx create mode 100644 components/permissions/PermissionRequestTitle.tsx create mode 100644 components/permissions/PermissionRuleExplanation.tsx create mode 100644 components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx create mode 100644 components/permissions/PowerShellPermissionRequest/powershellToolUseOptions.tsx create mode 100644 components/permissions/SandboxPermissionRequest.tsx create mode 100644 components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx create mode 100644 components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx create mode 100644 components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx create mode 100644 components/permissions/WorkerBadge.tsx create mode 100644 components/permissions/WorkerPendingPermission.tsx create mode 100644 components/permissions/hooks.ts create mode 100644 components/permissions/rules/AddPermissionRules.tsx create mode 100644 components/permissions/rules/AddWorkspaceDirectory.tsx create mode 100644 components/permissions/rules/PermissionRuleDescription.tsx create mode 100644 components/permissions/rules/PermissionRuleInput.tsx create mode 100644 components/permissions/rules/PermissionRuleList.tsx create mode 100644 components/permissions/rules/RecentDenialsTab.tsx create mode 100644 components/permissions/rules/RemoveWorkspaceDirectory.tsx create mode 100644 components/permissions/rules/WorkspaceTab.tsx create mode 100644 components/permissions/shellPermissionHelpers.tsx create mode 100644 components/permissions/useShellPermissionFeedback.ts create mode 100644 components/permissions/utils.ts create mode 100644 components/sandbox/SandboxConfigTab.tsx create mode 100644 components/sandbox/SandboxDependenciesTab.tsx create mode 100644 components/sandbox/SandboxDoctorSection.tsx create mode 100644 components/sandbox/SandboxOverridesTab.tsx create mode 100644 components/sandbox/SandboxSettings.tsx create mode 100644 components/shell/ExpandShellOutputContext.tsx create mode 100644 components/shell/OutputLine.tsx create mode 100644 components/shell/ShellProgressMessage.tsx create mode 100644 components/shell/ShellTimeDisplay.tsx create mode 100644 components/skills/SkillsMenu.tsx create mode 100644 components/tasks/AsyncAgentDetailDialog.tsx create mode 100644 components/tasks/BackgroundTask.tsx create mode 100644 components/tasks/BackgroundTaskStatus.tsx create mode 100644 components/tasks/BackgroundTasksDialog.tsx create mode 100644 components/tasks/DreamDetailDialog.tsx create mode 100644 components/tasks/InProcessTeammateDetailDialog.tsx create mode 100644 components/tasks/RemoteSessionDetailDialog.tsx create mode 100644 components/tasks/RemoteSessionProgress.tsx create mode 100644 components/tasks/ShellDetailDialog.tsx create mode 100644 components/tasks/ShellProgress.tsx create mode 100644 components/tasks/renderToolActivity.tsx create mode 100644 components/tasks/taskStatusUtils.tsx create mode 100644 components/teams/TeamStatus.tsx create mode 100644 components/teams/TeamsDialog.tsx create mode 100644 components/ui/OrderedList.tsx create mode 100644 components/ui/OrderedListItem.tsx create mode 100644 components/ui/TreeSelect.tsx create mode 100644 components/wizard/WizardDialogLayout.tsx create mode 100644 components/wizard/WizardNavigationFooter.tsx create mode 100644 components/wizard/WizardProvider.tsx create mode 100644 components/wizard/index.ts create mode 100644 components/wizard/useWizard.ts create mode 100644 constants/apiLimits.ts create mode 100644 constants/betas.ts create mode 100644 constants/common.ts create mode 100644 constants/cyberRiskInstruction.ts create mode 100644 constants/errorIds.ts create mode 100644 constants/figures.ts create mode 100644 constants/files.ts create mode 100644 constants/github-app.ts create mode 100644 constants/keys.ts create mode 100644 constants/messages.ts create mode 100644 constants/oauth.ts create mode 100644 constants/outputStyles.ts create mode 100644 constants/product.ts create mode 100644 constants/prompts.ts create mode 100644 constants/spinnerVerbs.ts create mode 100644 constants/system.ts create mode 100644 constants/systemPromptSections.ts create mode 100644 constants/toolLimits.ts create mode 100644 constants/tools.ts create mode 100644 constants/turnCompletionVerbs.ts create mode 100644 constants/xml.ts create mode 100644 context.ts create mode 100644 context/QueuedMessageContext.tsx create mode 100644 context/fpsMetrics.tsx create mode 100644 context/mailbox.tsx create mode 100644 context/modalContext.tsx create mode 100644 context/notifications.tsx create mode 100644 context/overlayContext.tsx create mode 100644 context/promptOverlayContext.tsx create mode 100644 context/stats.tsx create mode 100644 context/voice.tsx create mode 100644 coordinator/coordinatorMode.ts create mode 100644 cost-tracker.ts create mode 100644 costHook.ts create mode 100644 dialogLaunchers.tsx create mode 100644 entrypoints/agentSdkTypes.ts create mode 100644 entrypoints/cli.tsx create mode 100644 entrypoints/init.ts create mode 100644 entrypoints/mcp.ts create mode 100644 entrypoints/sandboxTypes.ts create mode 100644 entrypoints/sdk/controlSchemas.ts create mode 100644 entrypoints/sdk/coreSchemas.ts create mode 100644 entrypoints/sdk/coreTypes.ts create mode 100644 history.ts create mode 100644 hooks/fileSuggestions.ts create mode 100644 hooks/notifs/useAutoModeUnavailableNotification.ts create mode 100644 hooks/notifs/useCanSwitchToExistingSubscription.tsx create mode 100644 hooks/notifs/useDeprecationWarningNotification.tsx create mode 100644 hooks/notifs/useFastModeNotification.tsx create mode 100644 hooks/notifs/useIDEStatusIndicator.tsx create mode 100644 hooks/notifs/useInstallMessages.tsx create mode 100644 hooks/notifs/useLspInitializationNotification.tsx create mode 100644 hooks/notifs/useMcpConnectivityStatus.tsx create mode 100644 hooks/notifs/useModelMigrationNotifications.tsx create mode 100644 hooks/notifs/useNpmDeprecationNotification.tsx create mode 100644 hooks/notifs/usePluginAutoupdateNotification.tsx create mode 100644 hooks/notifs/usePluginInstallationStatus.tsx create mode 100644 hooks/notifs/useRateLimitWarningNotification.tsx create mode 100644 hooks/notifs/useSettingsErrors.tsx create mode 100644 hooks/notifs/useStartupNotification.ts create mode 100644 hooks/notifs/useTeammateShutdownNotification.ts create mode 100644 hooks/renderPlaceholder.ts create mode 100644 hooks/toolPermission/PermissionContext.ts create mode 100644 hooks/toolPermission/handlers/coordinatorHandler.ts create mode 100644 hooks/toolPermission/handlers/interactiveHandler.ts create mode 100644 hooks/toolPermission/handlers/swarmWorkerHandler.ts create mode 100644 hooks/toolPermission/permissionLogging.ts create mode 100644 hooks/unifiedSuggestions.ts create mode 100644 hooks/useAfterFirstRender.ts create mode 100644 hooks/useApiKeyVerification.ts create mode 100644 hooks/useArrowKeyHistory.tsx create mode 100644 hooks/useAssistantHistory.ts create mode 100644 hooks/useAwaySummary.ts create mode 100644 hooks/useBackgroundTaskNavigation.ts create mode 100644 hooks/useBlink.ts create mode 100644 hooks/useCanUseTool.tsx create mode 100644 hooks/useCancelRequest.ts create mode 100644 hooks/useChromeExtensionNotification.tsx create mode 100644 hooks/useClaudeCodeHintRecommendation.tsx create mode 100644 hooks/useClipboardImageHint.ts create mode 100644 hooks/useCommandKeybindings.tsx create mode 100644 hooks/useCommandQueue.ts create mode 100644 hooks/useCopyOnSelect.ts create mode 100644 hooks/useDeferredHookMessages.ts create mode 100644 hooks/useDiffData.ts create mode 100644 hooks/useDiffInIDE.ts create mode 100644 hooks/useDirectConnect.ts create mode 100644 hooks/useDoublePress.ts create mode 100644 hooks/useDynamicConfig.ts create mode 100644 hooks/useElapsedTime.ts create mode 100644 hooks/useExitOnCtrlCD.ts create mode 100644 hooks/useExitOnCtrlCDWithKeybindings.ts create mode 100644 hooks/useFileHistorySnapshotInit.ts create mode 100644 hooks/useGlobalKeybindings.tsx create mode 100644 hooks/useHistorySearch.ts create mode 100644 hooks/useIDEIntegration.tsx create mode 100644 hooks/useIdeAtMentioned.ts create mode 100644 hooks/useIdeConnectionStatus.ts create mode 100644 hooks/useIdeLogging.ts create mode 100644 hooks/useIdeSelection.ts create mode 100644 hooks/useInboxPoller.ts create mode 100644 hooks/useInputBuffer.ts create mode 100644 hooks/useIssueFlagBanner.ts create mode 100644 hooks/useLogMessages.ts create mode 100644 hooks/useLspPluginRecommendation.tsx create mode 100644 hooks/useMailboxBridge.ts create mode 100644 hooks/useMainLoopModel.ts create mode 100644 hooks/useManagePlugins.ts create mode 100644 hooks/useMemoryUsage.ts create mode 100644 hooks/useMergedClients.ts create mode 100644 hooks/useMergedCommands.ts create mode 100644 hooks/useMergedTools.ts create mode 100644 hooks/useMinDisplayTime.ts create mode 100644 hooks/useNotifyAfterTimeout.ts create mode 100644 hooks/useOfficialMarketplaceNotification.tsx create mode 100644 hooks/usePasteHandler.ts create mode 100644 hooks/usePluginRecommendationBase.tsx create mode 100644 hooks/usePrStatus.ts create mode 100644 hooks/usePromptSuggestion.ts create mode 100644 hooks/usePromptsFromClaudeInChrome.tsx create mode 100644 hooks/useQueueProcessor.ts create mode 100644 hooks/useRemoteSession.ts create mode 100644 hooks/useReplBridge.tsx create mode 100644 hooks/useSSHSession.ts create mode 100644 hooks/useScheduledTasks.ts create mode 100644 hooks/useSearchInput.ts create mode 100644 hooks/useSessionBackgrounding.ts create mode 100644 hooks/useSettings.ts create mode 100644 hooks/useSettingsChange.ts create mode 100644 hooks/useSkillImprovementSurvey.ts create mode 100644 hooks/useSkillsChange.ts create mode 100644 hooks/useSwarmInitialization.ts create mode 100644 hooks/useSwarmPermissionPoller.ts create mode 100644 hooks/useTaskListWatcher.ts create mode 100644 hooks/useTasksV2.ts create mode 100644 hooks/useTeammateViewAutoExit.ts create mode 100644 hooks/useTeleportResume.tsx create mode 100644 hooks/useTerminalSize.ts create mode 100644 hooks/useTextInput.ts create mode 100644 hooks/useTimeout.ts create mode 100644 hooks/useTurnDiffs.ts create mode 100644 hooks/useTypeahead.tsx create mode 100644 hooks/useUpdateNotification.ts create mode 100644 hooks/useVimInput.ts create mode 100644 hooks/useVirtualScroll.ts create mode 100644 hooks/useVoice.ts create mode 100644 hooks/useVoiceEnabled.ts create mode 100644 hooks/useVoiceIntegration.tsx create mode 100644 ink.ts create mode 100644 ink/Ansi.tsx create mode 100644 ink/bidi.ts create mode 100644 ink/clearTerminal.ts create mode 100644 ink/colorize.ts create mode 100644 ink/components/AlternateScreen.tsx create mode 100644 ink/components/App.tsx create mode 100644 ink/components/AppContext.ts create mode 100644 ink/components/Box.tsx create mode 100644 ink/components/Button.tsx create mode 100644 ink/components/ClockContext.tsx create mode 100644 ink/components/CursorDeclarationContext.ts create mode 100644 ink/components/ErrorOverview.tsx create mode 100644 ink/components/Link.tsx create mode 100644 ink/components/Newline.tsx create mode 100644 ink/components/NoSelect.tsx create mode 100644 ink/components/RawAnsi.tsx create mode 100644 ink/components/ScrollBox.tsx create mode 100644 ink/components/Spacer.tsx create mode 100644 ink/components/StdinContext.ts create mode 100644 ink/components/TerminalFocusContext.tsx create mode 100644 ink/components/TerminalSizeContext.tsx create mode 100644 ink/components/Text.tsx create mode 100644 ink/constants.ts create mode 100644 ink/dom.ts create mode 100644 ink/events/click-event.ts create mode 100644 ink/events/dispatcher.ts create mode 100644 ink/events/emitter.ts create mode 100644 ink/events/event-handlers.ts create mode 100644 ink/events/event.ts create mode 100644 ink/events/focus-event.ts create mode 100644 ink/events/input-event.ts create mode 100644 ink/events/keyboard-event.ts create mode 100644 ink/events/terminal-event.ts create mode 100644 ink/events/terminal-focus-event.ts create mode 100644 ink/focus.ts create mode 100644 ink/frame.ts create mode 100644 ink/get-max-width.ts create mode 100644 ink/hit-test.ts create mode 100644 ink/hooks/use-animation-frame.ts create mode 100644 ink/hooks/use-app.ts create mode 100644 ink/hooks/use-declared-cursor.ts create mode 100644 ink/hooks/use-input.ts create mode 100644 ink/hooks/use-interval.ts create mode 100644 ink/hooks/use-search-highlight.ts create mode 100644 ink/hooks/use-selection.ts create mode 100644 ink/hooks/use-stdin.ts create mode 100644 ink/hooks/use-tab-status.ts create mode 100644 ink/hooks/use-terminal-focus.ts create mode 100644 ink/hooks/use-terminal-title.ts create mode 100644 ink/hooks/use-terminal-viewport.ts create mode 100644 ink/ink.tsx create mode 100644 ink/instances.ts create mode 100644 ink/layout/engine.ts create mode 100644 ink/layout/geometry.ts create mode 100644 ink/layout/node.ts create mode 100644 ink/layout/yoga.ts create mode 100644 ink/line-width-cache.ts create mode 100644 ink/log-update.ts create mode 100644 ink/measure-element.ts create mode 100644 ink/measure-text.ts create mode 100644 ink/node-cache.ts create mode 100644 ink/optimizer.ts create mode 100644 ink/output.ts create mode 100644 ink/parse-keypress.ts create mode 100644 ink/reconciler.ts create mode 100644 ink/render-border.ts create mode 100644 ink/render-node-to-output.ts create mode 100644 ink/render-to-screen.ts create mode 100644 ink/renderer.ts create mode 100644 ink/root.ts create mode 100644 ink/screen.ts create mode 100644 ink/searchHighlight.ts create mode 100644 ink/selection.ts create mode 100644 ink/squash-text-nodes.ts create mode 100644 ink/stringWidth.ts create mode 100644 ink/styles.ts create mode 100644 ink/supports-hyperlinks.ts create mode 100644 ink/tabstops.ts create mode 100644 ink/terminal-focus-state.ts create mode 100644 ink/terminal-querier.ts create mode 100644 ink/terminal.ts create mode 100644 ink/termio.ts create mode 100644 ink/termio/ansi.ts create mode 100644 ink/termio/csi.ts create mode 100644 ink/termio/dec.ts create mode 100644 ink/termio/esc.ts create mode 100644 ink/termio/osc.ts create mode 100644 ink/termio/parser.ts create mode 100644 ink/termio/sgr.ts create mode 100644 ink/termio/tokenize.ts create mode 100644 ink/termio/types.ts create mode 100644 ink/useTerminalNotification.ts create mode 100644 ink/warn.ts create mode 100644 ink/widest-line.ts create mode 100644 ink/wrap-text.ts create mode 100644 ink/wrapAnsi.ts create mode 100644 interactiveHelpers.tsx create mode 100644 keybindings/KeybindingContext.tsx create mode 100644 keybindings/KeybindingProviderSetup.tsx create mode 100644 keybindings/defaultBindings.ts create mode 100644 keybindings/loadUserBindings.ts create mode 100644 keybindings/match.ts create mode 100644 keybindings/parser.ts create mode 100644 keybindings/reservedShortcuts.ts create mode 100644 keybindings/resolver.ts create mode 100644 keybindings/schema.ts create mode 100644 keybindings/shortcutFormat.ts create mode 100644 keybindings/template.ts create mode 100644 keybindings/useKeybinding.ts create mode 100644 keybindings/useShortcutDisplay.ts create mode 100644 keybindings/validate.ts create mode 100644 main.tsx create mode 100644 memdir/findRelevantMemories.ts create mode 100644 memdir/memdir.ts create mode 100644 memdir/memoryAge.ts create mode 100644 memdir/memoryScan.ts create mode 100644 memdir/memoryTypes.ts create mode 100644 memdir/paths.ts create mode 100644 memdir/teamMemPaths.ts create mode 100644 memdir/teamMemPrompts.ts create mode 100644 migrations/migrateAutoUpdatesToSettings.ts create mode 100644 migrations/migrateBypassPermissionsAcceptedToSettings.ts create mode 100644 migrations/migrateEnableAllProjectMcpServersToSettings.ts create mode 100644 migrations/migrateFennecToOpus.ts create mode 100644 migrations/migrateLegacyOpusToCurrent.ts create mode 100644 migrations/migrateOpusToOpus1m.ts create mode 100644 migrations/migrateReplBridgeEnabledToRemoteControlAtStartup.ts create mode 100644 migrations/migrateSonnet1mToSonnet45.ts create mode 100644 migrations/migrateSonnet45ToSonnet46.ts create mode 100644 migrations/resetAutoModeOptInForDefaultOffer.ts create mode 100644 migrations/resetProToOpusDefault.ts create mode 100644 moreright/useMoreRight.tsx create mode 100644 native-ts/color-diff/index.ts create mode 100644 native-ts/file-index/index.ts create mode 100644 native-ts/yoga-layout/enums.ts create mode 100644 native-ts/yoga-layout/index.ts create mode 100644 outputStyles/loadOutputStylesDir.ts create mode 100644 plugins/builtinPlugins.ts create mode 100644 plugins/bundled/index.ts create mode 100644 projectOnboardingState.ts create mode 100644 public/claude-files.png create mode 100644 public/leak-tweet.png create mode 100644 query.ts create mode 100644 query/config.ts create mode 100644 query/deps.ts create mode 100644 query/stopHooks.ts create mode 100644 query/tokenBudget.ts create mode 100644 remote/RemoteSessionManager.ts create mode 100644 remote/SessionsWebSocket.ts create mode 100644 remote/remotePermissionBridge.ts create mode 100644 remote/sdkMessageAdapter.ts create mode 100644 replLauncher.tsx create mode 100644 schemas/hooks.ts create mode 100644 screens/Doctor.tsx create mode 100644 screens/REPL.tsx create mode 100644 screens/ResumeConversation.tsx create mode 100644 server/createDirectConnectSession.ts create mode 100644 server/directConnectManager.ts create mode 100644 server/types.ts create mode 100644 services/AgentSummary/agentSummary.ts create mode 100644 services/MagicDocs/magicDocs.ts create mode 100644 services/MagicDocs/prompts.ts create mode 100644 services/PromptSuggestion/promptSuggestion.ts create mode 100644 services/PromptSuggestion/speculation.ts create mode 100644 services/SessionMemory/prompts.ts create mode 100644 services/SessionMemory/sessionMemory.ts create mode 100644 services/SessionMemory/sessionMemoryUtils.ts create mode 100644 services/analytics/config.ts create mode 100644 services/analytics/datadog.ts create mode 100644 services/analytics/firstPartyEventLogger.ts create mode 100644 services/analytics/firstPartyEventLoggingExporter.ts create mode 100644 services/analytics/growthbook.ts create mode 100644 services/analytics/index.ts create mode 100644 services/analytics/metadata.ts create mode 100644 services/analytics/sink.ts create mode 100644 services/analytics/sinkKillswitch.ts create mode 100644 services/api/adminRequests.ts create mode 100644 services/api/bootstrap.ts create mode 100644 services/api/claude.ts create mode 100644 services/api/client.ts create mode 100644 services/api/dumpPrompts.ts create mode 100644 services/api/emptyUsage.ts create mode 100644 services/api/errorUtils.ts create mode 100644 services/api/errors.ts create mode 100644 services/api/filesApi.ts create mode 100644 services/api/firstTokenDate.ts create mode 100644 services/api/grove.ts create mode 100644 services/api/logging.ts create mode 100644 services/api/metricsOptOut.ts create mode 100644 services/api/overageCreditGrant.ts create mode 100644 services/api/promptCacheBreakDetection.ts create mode 100644 services/api/referral.ts create mode 100644 services/api/sessionIngress.ts create mode 100644 services/api/ultrareviewQuota.ts create mode 100644 services/api/usage.ts create mode 100644 services/api/withRetry.ts create mode 100644 services/autoDream/autoDream.ts create mode 100644 services/autoDream/config.ts create mode 100644 services/autoDream/consolidationLock.ts create mode 100644 services/autoDream/consolidationPrompt.ts create mode 100644 services/awaySummary.ts create mode 100644 services/claudeAiLimits.ts create mode 100644 services/claudeAiLimitsHook.ts create mode 100644 services/compact/apiMicrocompact.ts create mode 100644 services/compact/autoCompact.ts create mode 100644 services/compact/compact.ts create mode 100644 services/compact/compactWarningHook.ts create mode 100644 services/compact/compactWarningState.ts create mode 100644 services/compact/grouping.ts create mode 100644 services/compact/microCompact.ts create mode 100644 services/compact/postCompactCleanup.ts create mode 100644 services/compact/prompt.ts create mode 100644 services/compact/sessionMemoryCompact.ts create mode 100644 services/compact/timeBasedMCConfig.ts create mode 100644 services/diagnosticTracking.ts create mode 100644 services/extractMemories/extractMemories.ts create mode 100644 services/extractMemories/prompts.ts create mode 100644 services/internalLogging.ts create mode 100644 services/lsp/LSPClient.ts create mode 100644 services/lsp/LSPDiagnosticRegistry.ts create mode 100644 services/lsp/LSPServerInstance.ts create mode 100644 services/lsp/LSPServerManager.ts create mode 100644 services/lsp/config.ts create mode 100644 services/lsp/manager.ts create mode 100644 services/lsp/passiveFeedback.ts create mode 100644 services/mcp/InProcessTransport.ts create mode 100644 services/mcp/MCPConnectionManager.tsx create mode 100644 services/mcp/SdkControlTransport.ts create mode 100644 services/mcp/auth.ts create mode 100644 services/mcp/channelAllowlist.ts create mode 100644 services/mcp/channelNotification.ts create mode 100644 services/mcp/channelPermissions.ts create mode 100644 services/mcp/claudeai.ts create mode 100644 services/mcp/client.ts create mode 100644 services/mcp/config.ts create mode 100644 services/mcp/elicitationHandler.ts create mode 100644 services/mcp/envExpansion.ts create mode 100644 services/mcp/headersHelper.ts create mode 100644 services/mcp/mcpStringUtils.ts create mode 100644 services/mcp/normalization.ts create mode 100644 services/mcp/oauthPort.ts create mode 100644 services/mcp/officialRegistry.ts create mode 100644 services/mcp/types.ts create mode 100644 services/mcp/useManageMCPConnections.ts create mode 100644 services/mcp/utils.ts create mode 100644 services/mcp/vscodeSdkMcp.ts create mode 100644 services/mcp/xaa.ts create mode 100644 services/mcp/xaaIdpLogin.ts create mode 100644 services/mcpServerApproval.tsx create mode 100644 services/mockRateLimits.ts create mode 100644 services/notifier.ts create mode 100644 services/oauth/auth-code-listener.ts create mode 100644 services/oauth/client.ts create mode 100644 services/oauth/crypto.ts create mode 100644 services/oauth/getOauthProfile.ts create mode 100644 services/oauth/index.ts create mode 100644 services/plugins/PluginInstallationManager.ts create mode 100644 services/plugins/pluginCliCommands.ts create mode 100644 services/plugins/pluginOperations.ts create mode 100644 services/policyLimits/index.ts create mode 100644 services/policyLimits/types.ts create mode 100644 services/preventSleep.ts create mode 100644 services/rateLimitMessages.ts create mode 100644 services/rateLimitMocking.ts create mode 100644 services/remoteManagedSettings/index.ts create mode 100644 services/remoteManagedSettings/securityCheck.tsx create mode 100644 services/remoteManagedSettings/syncCache.ts create mode 100644 services/remoteManagedSettings/syncCacheState.ts create mode 100644 services/remoteManagedSettings/types.ts create mode 100644 services/settingsSync/index.ts create mode 100644 services/settingsSync/types.ts create mode 100644 services/teamMemorySync/index.ts create mode 100644 services/teamMemorySync/secretScanner.ts create mode 100644 services/teamMemorySync/teamMemSecretGuard.ts create mode 100644 services/teamMemorySync/types.ts create mode 100644 services/teamMemorySync/watcher.ts create mode 100644 services/tips/tipHistory.ts create mode 100644 services/tips/tipRegistry.ts create mode 100644 services/tips/tipScheduler.ts create mode 100644 services/tokenEstimation.ts create mode 100644 services/toolUseSummary/toolUseSummaryGenerator.ts create mode 100644 services/tools/StreamingToolExecutor.ts create mode 100644 services/tools/toolExecution.ts create mode 100644 services/tools/toolHooks.ts create mode 100644 services/tools/toolOrchestration.ts create mode 100644 services/vcr.ts create mode 100644 services/voice.ts create mode 100644 services/voiceKeyterms.ts create mode 100644 services/voiceStreamSTT.ts create mode 100644 setup.ts create mode 100644 skills/bundled/batch.ts create mode 100644 skills/bundled/claudeApi.ts create mode 100644 skills/bundled/claudeApiContent.ts create mode 100644 skills/bundled/claudeInChrome.ts create mode 100644 skills/bundled/debug.ts create mode 100644 skills/bundled/index.ts create mode 100644 skills/bundled/keybindings.ts create mode 100644 skills/bundled/loop.ts create mode 100644 skills/bundled/loremIpsum.ts create mode 100644 skills/bundled/remember.ts create mode 100644 skills/bundled/scheduleRemoteAgents.ts create mode 100644 skills/bundled/simplify.ts create mode 100644 skills/bundled/skillify.ts create mode 100644 skills/bundled/stuck.ts create mode 100644 skills/bundled/updateConfig.ts create mode 100644 skills/bundled/verify.ts create mode 100644 skills/bundled/verifyContent.ts create mode 100644 skills/bundledSkills.ts create mode 100644 skills/loadSkillsDir.ts create mode 100644 skills/mcpSkillBuilders.ts create mode 100644 state/AppState.tsx create mode 100644 state/AppStateStore.ts create mode 100644 state/onChangeAppState.ts create mode 100644 state/selectors.ts create mode 100644 state/store.ts create mode 100644 state/teammateViewHelpers.ts create mode 100644 tasks.ts create mode 100644 tasks/DreamTask/DreamTask.ts create mode 100644 tasks/InProcessTeammateTask/InProcessTeammateTask.tsx create mode 100644 tasks/InProcessTeammateTask/types.ts create mode 100644 tasks/LocalAgentTask/LocalAgentTask.tsx create mode 100644 tasks/LocalMainSessionTask.ts create mode 100644 tasks/LocalShellTask/LocalShellTask.tsx create mode 100644 tasks/LocalShellTask/guards.ts create mode 100644 tasks/LocalShellTask/killShellTasks.ts create mode 100644 tasks/RemoteAgentTask/RemoteAgentTask.tsx create mode 100644 tasks/pillLabel.ts create mode 100644 tasks/stopTask.ts create mode 100644 tasks/types.ts create mode 100644 tools.ts create mode 100644 tools/AgentTool/AgentTool.tsx create mode 100644 tools/AgentTool/UI.tsx create mode 100644 tools/AgentTool/agentColorManager.ts create mode 100644 tools/AgentTool/agentDisplay.ts create mode 100644 tools/AgentTool/agentMemory.ts create mode 100644 tools/AgentTool/agentMemorySnapshot.ts create mode 100644 tools/AgentTool/agentToolUtils.ts create mode 100644 tools/AgentTool/built-in/claudeCodeGuideAgent.ts create mode 100644 tools/AgentTool/built-in/exploreAgent.ts create mode 100644 tools/AgentTool/built-in/generalPurposeAgent.ts create mode 100644 tools/AgentTool/built-in/planAgent.ts create mode 100644 tools/AgentTool/built-in/statuslineSetup.ts create mode 100644 tools/AgentTool/built-in/verificationAgent.ts create mode 100644 tools/AgentTool/builtInAgents.ts create mode 100644 tools/AgentTool/constants.ts create mode 100644 tools/AgentTool/forkSubagent.ts create mode 100644 tools/AgentTool/loadAgentsDir.ts create mode 100644 tools/AgentTool/prompt.ts create mode 100644 tools/AgentTool/resumeAgent.ts create mode 100644 tools/AgentTool/runAgent.ts create mode 100644 tools/AskUserQuestionTool/AskUserQuestionTool.tsx create mode 100644 tools/AskUserQuestionTool/prompt.ts create mode 100644 tools/BashTool/BashTool.tsx create mode 100644 tools/BashTool/BashToolResultMessage.tsx create mode 100644 tools/BashTool/UI.tsx create mode 100644 tools/BashTool/bashCommandHelpers.ts create mode 100644 tools/BashTool/bashPermissions.ts create mode 100644 tools/BashTool/bashSecurity.ts create mode 100644 tools/BashTool/commandSemantics.ts create mode 100644 tools/BashTool/commentLabel.ts create mode 100644 tools/BashTool/destructiveCommandWarning.ts create mode 100644 tools/BashTool/modeValidation.ts create mode 100644 tools/BashTool/pathValidation.ts create mode 100644 tools/BashTool/prompt.ts create mode 100644 tools/BashTool/readOnlyValidation.ts create mode 100644 tools/BashTool/sedEditParser.ts create mode 100644 tools/BashTool/sedValidation.ts create mode 100644 tools/BashTool/shouldUseSandbox.ts create mode 100644 tools/BashTool/toolName.ts create mode 100644 tools/BashTool/utils.ts create mode 100644 tools/BriefTool/BriefTool.ts create mode 100644 tools/BriefTool/UI.tsx create mode 100644 tools/BriefTool/attachments.ts create mode 100644 tools/BriefTool/prompt.ts create mode 100644 tools/BriefTool/upload.ts create mode 100644 tools/ConfigTool/ConfigTool.ts create mode 100644 tools/ConfigTool/UI.tsx create mode 100644 tools/ConfigTool/constants.ts create mode 100644 tools/ConfigTool/prompt.ts create mode 100644 tools/ConfigTool/supportedSettings.ts create mode 100644 tools/EnterPlanModeTool/EnterPlanModeTool.ts create mode 100644 tools/EnterPlanModeTool/UI.tsx create mode 100644 tools/EnterPlanModeTool/constants.ts create mode 100644 tools/EnterPlanModeTool/prompt.ts create mode 100644 tools/EnterWorktreeTool/EnterWorktreeTool.ts create mode 100644 tools/EnterWorktreeTool/UI.tsx create mode 100644 tools/EnterWorktreeTool/constants.ts create mode 100644 tools/EnterWorktreeTool/prompt.ts create mode 100644 tools/ExitPlanModeTool/ExitPlanModeV2Tool.ts create mode 100644 tools/ExitPlanModeTool/UI.tsx create mode 100644 tools/ExitPlanModeTool/constants.ts create mode 100644 tools/ExitPlanModeTool/prompt.ts create mode 100644 tools/ExitWorktreeTool/ExitWorktreeTool.ts create mode 100644 tools/ExitWorktreeTool/UI.tsx create mode 100644 tools/ExitWorktreeTool/constants.ts create mode 100644 tools/ExitWorktreeTool/prompt.ts create mode 100644 tools/FileEditTool/FileEditTool.ts create mode 100644 tools/FileEditTool/UI.tsx create mode 100644 tools/FileEditTool/constants.ts create mode 100644 tools/FileEditTool/prompt.ts create mode 100644 tools/FileEditTool/types.ts create mode 100644 tools/FileEditTool/utils.ts create mode 100644 tools/FileReadTool/FileReadTool.ts create mode 100644 tools/FileReadTool/UI.tsx create mode 100644 tools/FileReadTool/imageProcessor.ts create mode 100644 tools/FileReadTool/limits.ts create mode 100644 tools/FileReadTool/prompt.ts create mode 100644 tools/FileWriteTool/FileWriteTool.ts create mode 100644 tools/FileWriteTool/UI.tsx create mode 100644 tools/FileWriteTool/prompt.ts create mode 100644 tools/GlobTool/GlobTool.ts create mode 100644 tools/GlobTool/UI.tsx create mode 100644 tools/GlobTool/prompt.ts create mode 100644 tools/GrepTool/GrepTool.ts create mode 100644 tools/GrepTool/UI.tsx create mode 100644 tools/GrepTool/prompt.ts create mode 100644 tools/LSPTool/LSPTool.ts create mode 100644 tools/LSPTool/UI.tsx create mode 100644 tools/LSPTool/formatters.ts create mode 100644 tools/LSPTool/prompt.ts create mode 100644 tools/LSPTool/schemas.ts create mode 100644 tools/LSPTool/symbolContext.ts create mode 100644 tools/ListMcpResourcesTool/ListMcpResourcesTool.ts create mode 100644 tools/ListMcpResourcesTool/UI.tsx create mode 100644 tools/ListMcpResourcesTool/prompt.ts create mode 100644 tools/MCPTool/MCPTool.ts create mode 100644 tools/MCPTool/UI.tsx create mode 100644 tools/MCPTool/classifyForCollapse.ts create mode 100644 tools/MCPTool/prompt.ts create mode 100644 tools/McpAuthTool/McpAuthTool.ts create mode 100644 tools/NotebookEditTool/NotebookEditTool.ts create mode 100644 tools/NotebookEditTool/UI.tsx create mode 100644 tools/NotebookEditTool/constants.ts create mode 100644 tools/NotebookEditTool/prompt.ts create mode 100644 tools/PowerShellTool/PowerShellTool.tsx create mode 100644 tools/PowerShellTool/UI.tsx create mode 100644 tools/PowerShellTool/clmTypes.ts create mode 100644 tools/PowerShellTool/commandSemantics.ts create mode 100644 tools/PowerShellTool/commonParameters.ts create mode 100644 tools/PowerShellTool/destructiveCommandWarning.ts create mode 100644 tools/PowerShellTool/gitSafety.ts create mode 100644 tools/PowerShellTool/modeValidation.ts create mode 100644 tools/PowerShellTool/pathValidation.ts create mode 100644 tools/PowerShellTool/powershellPermissions.ts create mode 100644 tools/PowerShellTool/powershellSecurity.ts create mode 100644 tools/PowerShellTool/prompt.ts create mode 100644 tools/PowerShellTool/readOnlyValidation.ts create mode 100644 tools/PowerShellTool/toolName.ts create mode 100644 tools/REPLTool/constants.ts create mode 100644 tools/REPLTool/primitiveTools.ts create mode 100644 tools/ReadMcpResourceTool/ReadMcpResourceTool.ts create mode 100644 tools/ReadMcpResourceTool/UI.tsx create mode 100644 tools/ReadMcpResourceTool/prompt.ts create mode 100644 tools/RemoteTriggerTool/RemoteTriggerTool.ts create mode 100644 tools/RemoteTriggerTool/UI.tsx create mode 100644 tools/RemoteTriggerTool/prompt.ts create mode 100644 tools/ScheduleCronTool/CronCreateTool.ts create mode 100644 tools/ScheduleCronTool/CronDeleteTool.ts create mode 100644 tools/ScheduleCronTool/CronListTool.ts create mode 100644 tools/ScheduleCronTool/UI.tsx create mode 100644 tools/ScheduleCronTool/prompt.ts create mode 100644 tools/SendMessageTool/SendMessageTool.ts create mode 100644 tools/SendMessageTool/UI.tsx create mode 100644 tools/SendMessageTool/constants.ts create mode 100644 tools/SendMessageTool/prompt.ts create mode 100644 tools/SkillTool/SkillTool.ts create mode 100644 tools/SkillTool/UI.tsx create mode 100644 tools/SkillTool/constants.ts create mode 100644 tools/SkillTool/prompt.ts create mode 100644 tools/SleepTool/prompt.ts create mode 100644 tools/SyntheticOutputTool/SyntheticOutputTool.ts create mode 100644 tools/TaskCreateTool/TaskCreateTool.ts create mode 100644 tools/TaskCreateTool/constants.ts create mode 100644 tools/TaskCreateTool/prompt.ts create mode 100644 tools/TaskGetTool/TaskGetTool.ts create mode 100644 tools/TaskGetTool/constants.ts create mode 100644 tools/TaskGetTool/prompt.ts create mode 100644 tools/TaskListTool/TaskListTool.ts create mode 100644 tools/TaskListTool/constants.ts create mode 100644 tools/TaskListTool/prompt.ts create mode 100644 tools/TaskOutputTool/TaskOutputTool.tsx create mode 100644 tools/TaskOutputTool/constants.ts create mode 100644 tools/TaskStopTool/TaskStopTool.ts create mode 100644 tools/TaskStopTool/UI.tsx create mode 100644 tools/TaskStopTool/prompt.ts create mode 100644 tools/TaskUpdateTool/TaskUpdateTool.ts create mode 100644 tools/TaskUpdateTool/constants.ts create mode 100644 tools/TaskUpdateTool/prompt.ts create mode 100644 tools/TeamCreateTool/TeamCreateTool.ts create mode 100644 tools/TeamCreateTool/UI.tsx create mode 100644 tools/TeamCreateTool/constants.ts create mode 100644 tools/TeamCreateTool/prompt.ts create mode 100644 tools/TeamDeleteTool/TeamDeleteTool.ts create mode 100644 tools/TeamDeleteTool/UI.tsx create mode 100644 tools/TeamDeleteTool/constants.ts create mode 100644 tools/TeamDeleteTool/prompt.ts create mode 100644 tools/TodoWriteTool/TodoWriteTool.ts create mode 100644 tools/TodoWriteTool/constants.ts create mode 100644 tools/TodoWriteTool/prompt.ts create mode 100644 tools/ToolSearchTool/ToolSearchTool.ts create mode 100644 tools/ToolSearchTool/constants.ts create mode 100644 tools/ToolSearchTool/prompt.ts create mode 100644 tools/WebFetchTool/UI.tsx create mode 100644 tools/WebFetchTool/WebFetchTool.ts create mode 100644 tools/WebFetchTool/preapproved.ts create mode 100644 tools/WebFetchTool/prompt.ts create mode 100644 tools/WebFetchTool/utils.ts create mode 100644 tools/WebSearchTool/UI.tsx create mode 100644 tools/WebSearchTool/WebSearchTool.ts create mode 100644 tools/WebSearchTool/prompt.ts create mode 100644 tools/shared/gitOperationTracking.ts create mode 100644 tools/shared/spawnMultiAgent.ts create mode 100644 tools/testing/TestingPermissionTool.tsx create mode 100644 tools/utils.ts create mode 100644 types/command.ts create mode 100644 types/generated/events_mono/claude_code/v1/claude_code_internal_event.ts create mode 100644 types/generated/events_mono/common/v1/auth.ts create mode 100644 types/generated/events_mono/growthbook/v1/growthbook_experiment_event.ts create mode 100644 types/generated/google/protobuf/timestamp.ts create mode 100644 types/hooks.ts create mode 100644 types/ids.ts create mode 100644 types/logs.ts create mode 100644 types/permissions.ts create mode 100644 types/plugin.ts create mode 100644 types/textInputTypes.ts create mode 100644 upstreamproxy/relay.ts create mode 100644 upstreamproxy/upstreamproxy.ts create mode 100644 utils/CircularBuffer.ts create mode 100644 utils/Cursor.ts create mode 100644 utils/QueryGuard.ts create mode 100644 utils/Shell.ts create mode 100644 utils/ShellCommand.ts create mode 100644 utils/abortController.ts create mode 100644 utils/activityManager.ts create mode 100644 utils/advisor.ts create mode 100644 utils/agentContext.ts create mode 100644 utils/agentId.ts create mode 100644 utils/agentSwarmsEnabled.ts create mode 100644 utils/agenticSessionSearch.ts create mode 100644 utils/analyzeContext.ts create mode 100644 utils/ansiToPng.ts create mode 100644 utils/ansiToSvg.ts create mode 100644 utils/api.ts create mode 100644 utils/apiPreconnect.ts create mode 100644 utils/appleTerminalBackup.ts create mode 100644 utils/argumentSubstitution.ts create mode 100644 utils/array.ts create mode 100644 utils/asciicast.ts create mode 100644 utils/attachments.ts create mode 100644 utils/attribution.ts create mode 100644 utils/auth.ts create mode 100644 utils/authFileDescriptor.ts create mode 100644 utils/authPortable.ts create mode 100644 utils/autoModeDenials.ts create mode 100644 utils/autoRunIssue.tsx create mode 100644 utils/autoUpdater.ts create mode 100644 utils/aws.ts create mode 100644 utils/awsAuthStatusManager.ts create mode 100644 utils/background/remote/preconditions.ts create mode 100644 utils/background/remote/remoteSession.ts create mode 100644 utils/backgroundHousekeeping.ts create mode 100644 utils/bash/ParsedCommand.ts create mode 100644 utils/bash/ShellSnapshot.ts create mode 100644 utils/bash/ast.ts create mode 100644 utils/bash/bashParser.ts create mode 100644 utils/bash/bashPipeCommand.ts create mode 100644 utils/bash/commands.ts create mode 100644 utils/bash/heredoc.ts create mode 100644 utils/bash/parser.ts create mode 100644 utils/bash/prefix.ts create mode 100644 utils/bash/registry.ts create mode 100644 utils/bash/shellCompletion.ts create mode 100644 utils/bash/shellPrefix.ts create mode 100644 utils/bash/shellQuote.ts create mode 100644 utils/bash/shellQuoting.ts create mode 100644 utils/bash/specs/alias.ts create mode 100644 utils/bash/specs/index.ts create mode 100644 utils/bash/specs/nohup.ts create mode 100644 utils/bash/specs/pyright.ts create mode 100644 utils/bash/specs/sleep.ts create mode 100644 utils/bash/specs/srun.ts create mode 100644 utils/bash/specs/time.ts create mode 100644 utils/bash/specs/timeout.ts create mode 100644 utils/bash/treeSitterAnalysis.ts create mode 100644 utils/betas.ts create mode 100644 utils/billing.ts create mode 100644 utils/binaryCheck.ts create mode 100644 utils/browser.ts create mode 100644 utils/bufferedWriter.ts create mode 100644 utils/bundledMode.ts create mode 100644 utils/caCerts.ts create mode 100644 utils/caCertsConfig.ts create mode 100644 utils/cachePaths.ts create mode 100644 utils/classifierApprovals.ts create mode 100644 utils/classifierApprovalsHook.ts create mode 100644 utils/claudeCodeHints.ts create mode 100644 utils/claudeDesktop.ts create mode 100644 utils/claudeInChrome/chromeNativeHost.ts create mode 100644 utils/claudeInChrome/common.ts create mode 100644 utils/claudeInChrome/mcpServer.ts create mode 100644 utils/claudeInChrome/prompt.ts create mode 100644 utils/claudeInChrome/setup.ts create mode 100644 utils/claudeInChrome/setupPortable.ts create mode 100644 utils/claudeInChrome/toolRendering.tsx create mode 100644 utils/claudemd.ts create mode 100644 utils/cleanup.ts create mode 100644 utils/cleanupRegistry.ts create mode 100644 utils/cliArgs.ts create mode 100644 utils/cliHighlight.ts create mode 100644 utils/codeIndexing.ts create mode 100644 utils/collapseBackgroundBashNotifications.ts create mode 100644 utils/collapseHookSummaries.ts create mode 100644 utils/collapseReadSearch.ts create mode 100644 utils/collapseTeammateShutdowns.ts create mode 100644 utils/combinedAbortSignal.ts create mode 100644 utils/commandLifecycle.ts create mode 100644 utils/commitAttribution.ts create mode 100644 utils/completionCache.ts create mode 100644 utils/computerUse/appNames.ts create mode 100644 utils/computerUse/cleanup.ts create mode 100644 utils/computerUse/common.ts create mode 100644 utils/computerUse/computerUseLock.ts create mode 100644 utils/computerUse/drainRunLoop.ts create mode 100644 utils/computerUse/escHotkey.ts create mode 100644 utils/computerUse/executor.ts create mode 100644 utils/computerUse/gates.ts create mode 100644 utils/computerUse/hostAdapter.ts create mode 100644 utils/computerUse/inputLoader.ts create mode 100644 utils/computerUse/mcpServer.ts create mode 100644 utils/computerUse/setup.ts create mode 100644 utils/computerUse/swiftLoader.ts create mode 100644 utils/computerUse/toolRendering.tsx create mode 100644 utils/computerUse/wrapper.tsx create mode 100644 utils/concurrentSessions.ts create mode 100644 utils/config.ts create mode 100644 utils/configConstants.ts create mode 100644 utils/contentArray.ts create mode 100644 utils/context.ts create mode 100644 utils/contextAnalysis.ts create mode 100644 utils/contextSuggestions.ts create mode 100644 utils/controlMessageCompat.ts create mode 100644 utils/conversationRecovery.ts create mode 100644 utils/cron.ts create mode 100644 utils/cronJitterConfig.ts create mode 100644 utils/cronScheduler.ts create mode 100644 utils/cronTasks.ts create mode 100644 utils/cronTasksLock.ts create mode 100644 utils/crossProjectResume.ts create mode 100644 utils/crypto.ts create mode 100644 utils/cwd.ts create mode 100644 utils/debug.ts create mode 100644 utils/debugFilter.ts create mode 100644 utils/deepLink/banner.ts create mode 100644 utils/deepLink/parseDeepLink.ts create mode 100644 utils/deepLink/protocolHandler.ts create mode 100644 utils/deepLink/registerProtocol.ts create mode 100644 utils/deepLink/terminalLauncher.ts create mode 100644 utils/deepLink/terminalPreference.ts create mode 100644 utils/desktopDeepLink.ts create mode 100644 utils/detectRepository.ts create mode 100644 utils/diagLogs.ts create mode 100644 utils/diff.ts create mode 100644 utils/directMemberMessage.ts create mode 100644 utils/displayTags.ts create mode 100644 utils/doctorContextWarnings.ts create mode 100644 utils/doctorDiagnostic.ts create mode 100644 utils/dxt/helpers.ts create mode 100644 utils/dxt/zip.ts create mode 100644 utils/earlyInput.ts create mode 100644 utils/editor.ts create mode 100644 utils/effort.ts create mode 100644 utils/embeddedTools.ts create mode 100644 utils/env.ts create mode 100644 utils/envDynamic.ts create mode 100644 utils/envUtils.ts create mode 100644 utils/envValidation.ts create mode 100644 utils/errorLogSink.ts create mode 100644 utils/errors.ts create mode 100644 utils/exampleCommands.ts create mode 100644 utils/execFileNoThrow.ts create mode 100644 utils/execFileNoThrowPortable.ts create mode 100644 utils/execSyncWrapper.ts create mode 100644 utils/exportRenderer.tsx create mode 100644 utils/extraUsage.ts create mode 100644 utils/fastMode.ts create mode 100644 utils/file.ts create mode 100644 utils/fileHistory.ts create mode 100644 utils/fileOperationAnalytics.ts create mode 100644 utils/filePersistence/filePersistence.ts create mode 100644 utils/filePersistence/outputsScanner.ts create mode 100644 utils/fileRead.ts create mode 100644 utils/fileReadCache.ts create mode 100644 utils/fileStateCache.ts create mode 100644 utils/findExecutable.ts create mode 100644 utils/fingerprint.ts create mode 100644 utils/forkedAgent.ts create mode 100644 utils/format.ts create mode 100644 utils/formatBriefTimestamp.ts create mode 100644 utils/fpsTracker.ts create mode 100644 utils/frontmatterParser.ts create mode 100644 utils/fsOperations.ts create mode 100644 utils/fullscreen.ts create mode 100644 utils/generatedFiles.ts create mode 100644 utils/generators.ts create mode 100644 utils/genericProcessUtils.ts create mode 100644 utils/getWorktreePaths.ts create mode 100644 utils/getWorktreePathsPortable.ts create mode 100644 utils/ghPrStatus.ts create mode 100644 utils/git.ts create mode 100644 utils/git/gitConfigParser.ts create mode 100644 utils/git/gitFilesystem.ts create mode 100644 utils/git/gitignore.ts create mode 100644 utils/gitDiff.ts create mode 100644 utils/gitSettings.ts create mode 100644 utils/github/ghAuthStatus.ts create mode 100644 utils/githubRepoPathMapping.ts create mode 100644 utils/glob.ts create mode 100644 utils/gracefulShutdown.ts create mode 100644 utils/groupToolUses.ts create mode 100644 utils/handlePromptSubmit.ts create mode 100644 utils/hash.ts create mode 100644 utils/headlessProfiler.ts create mode 100644 utils/heapDumpService.ts create mode 100644 utils/heatmap.ts create mode 100644 utils/highlightMatch.tsx create mode 100644 utils/hooks.ts create mode 100644 utils/hooks/AsyncHookRegistry.ts create mode 100644 utils/hooks/apiQueryHookHelper.ts create mode 100644 utils/hooks/execAgentHook.ts create mode 100644 utils/hooks/execHttpHook.ts create mode 100644 utils/hooks/execPromptHook.ts create mode 100644 utils/hooks/fileChangedWatcher.ts create mode 100644 utils/hooks/hookEvents.ts create mode 100644 utils/hooks/hookHelpers.ts create mode 100644 utils/hooks/hooksConfigManager.ts create mode 100644 utils/hooks/hooksConfigSnapshot.ts create mode 100644 utils/hooks/hooksSettings.ts create mode 100644 utils/hooks/postSamplingHooks.ts create mode 100644 utils/hooks/registerFrontmatterHooks.ts create mode 100644 utils/hooks/registerSkillHooks.ts create mode 100644 utils/hooks/sessionHooks.ts create mode 100644 utils/hooks/skillImprovement.ts create mode 100644 utils/hooks/ssrfGuard.ts create mode 100644 utils/horizontalScroll.ts create mode 100644 utils/http.ts create mode 100644 utils/hyperlink.ts create mode 100644 utils/iTermBackup.ts create mode 100644 utils/ide.ts create mode 100644 utils/idePathConversion.ts create mode 100644 utils/idleTimeout.ts create mode 100644 utils/imagePaste.ts create mode 100644 utils/imageResizer.ts create mode 100644 utils/imageStore.ts create mode 100644 utils/imageValidation.ts create mode 100644 utils/immediateCommand.ts create mode 100644 utils/inProcessTeammateHelpers.ts create mode 100644 utils/ink.ts create mode 100644 utils/intl.ts create mode 100644 utils/jetbrains.ts create mode 100644 utils/json.ts create mode 100644 utils/jsonRead.ts create mode 100644 utils/keyboardShortcuts.ts create mode 100644 utils/lazySchema.ts create mode 100644 utils/listSessionsImpl.ts create mode 100644 utils/localInstaller.ts create mode 100644 utils/lockfile.ts create mode 100644 utils/log.ts create mode 100644 utils/logoV2Utils.ts create mode 100644 utils/mailbox.ts create mode 100644 utils/managedEnv.ts create mode 100644 utils/managedEnvConstants.ts create mode 100644 utils/markdown.ts create mode 100644 utils/markdownConfigLoader.ts create mode 100644 utils/mcp/dateTimeParser.ts create mode 100644 utils/mcp/elicitationValidation.ts create mode 100644 utils/mcpInstructionsDelta.ts create mode 100644 utils/mcpOutputStorage.ts create mode 100644 utils/mcpValidation.ts create mode 100644 utils/mcpWebSocketTransport.ts create mode 100644 utils/memoize.ts create mode 100644 utils/memory/types.ts create mode 100644 utils/memory/versions.ts create mode 100644 utils/memoryFileDetection.ts create mode 100644 utils/messagePredicates.ts create mode 100644 utils/messageQueueManager.ts create mode 100644 utils/messages.ts create mode 100644 utils/messages/mappers.ts create mode 100644 utils/messages/systemInit.ts create mode 100644 utils/model/agent.ts create mode 100644 utils/model/aliases.ts create mode 100644 utils/model/antModels.ts create mode 100644 utils/model/bedrock.ts create mode 100644 utils/model/check1mAccess.ts create mode 100644 utils/model/configs.ts create mode 100644 utils/model/contextWindowUpgradeCheck.ts create mode 100644 utils/model/deprecation.ts create mode 100644 utils/model/model.ts create mode 100644 utils/model/modelAllowlist.ts create mode 100644 utils/model/modelCapabilities.ts create mode 100644 utils/model/modelOptions.ts create mode 100644 utils/model/modelStrings.ts create mode 100644 utils/model/modelSupportOverrides.ts create mode 100644 utils/model/providers.ts create mode 100644 utils/model/validateModel.ts create mode 100644 utils/modelCost.ts create mode 100644 utils/modifiers.ts create mode 100644 utils/mtls.ts create mode 100644 utils/nativeInstaller/download.ts create mode 100644 utils/nativeInstaller/index.ts create mode 100644 utils/nativeInstaller/installer.ts create mode 100644 utils/nativeInstaller/packageManagers.ts create mode 100644 utils/nativeInstaller/pidLock.ts create mode 100644 utils/notebook.ts create mode 100644 utils/objectGroupBy.ts create mode 100644 utils/pasteStore.ts create mode 100644 utils/path.ts create mode 100644 utils/pdf.ts create mode 100644 utils/pdfUtils.ts create mode 100644 utils/peerAddress.ts create mode 100644 utils/permissions/PermissionMode.ts create mode 100644 utils/permissions/PermissionPromptToolResultSchema.ts create mode 100644 utils/permissions/PermissionResult.ts create mode 100644 utils/permissions/PermissionRule.ts create mode 100644 utils/permissions/PermissionUpdate.ts create mode 100644 utils/permissions/PermissionUpdateSchema.ts create mode 100644 utils/permissions/autoModeState.ts create mode 100644 utils/permissions/bashClassifier.ts create mode 100644 utils/permissions/bypassPermissionsKillswitch.ts create mode 100644 utils/permissions/classifierDecision.ts create mode 100644 utils/permissions/classifierShared.ts create mode 100644 utils/permissions/dangerousPatterns.ts create mode 100644 utils/permissions/denialTracking.ts create mode 100644 utils/permissions/filesystem.ts create mode 100644 utils/permissions/getNextPermissionMode.ts create mode 100644 utils/permissions/pathValidation.ts create mode 100644 utils/permissions/permissionExplainer.ts create mode 100644 utils/permissions/permissionRuleParser.ts create mode 100644 utils/permissions/permissionSetup.ts create mode 100644 utils/permissions/permissions.ts create mode 100644 utils/permissions/permissionsLoader.ts create mode 100644 utils/permissions/shadowedRuleDetection.ts create mode 100644 utils/permissions/shellRuleMatching.ts create mode 100644 utils/permissions/yoloClassifier.ts create mode 100644 utils/planModeV2.ts create mode 100644 utils/plans.ts create mode 100644 utils/platform.ts create mode 100644 utils/plugins/addDirPluginSettings.ts create mode 100644 utils/plugins/cacheUtils.ts create mode 100644 utils/plugins/dependencyResolver.ts create mode 100644 utils/plugins/fetchTelemetry.ts create mode 100644 utils/plugins/gitAvailability.ts create mode 100644 utils/plugins/headlessPluginInstall.ts create mode 100644 utils/plugins/hintRecommendation.ts create mode 100644 utils/plugins/installCounts.ts create mode 100644 utils/plugins/installedPluginsManager.ts create mode 100644 utils/plugins/loadPluginAgents.ts create mode 100644 utils/plugins/loadPluginCommands.ts create mode 100644 utils/plugins/loadPluginHooks.ts create mode 100644 utils/plugins/loadPluginOutputStyles.ts create mode 100644 utils/plugins/lspPluginIntegration.ts create mode 100644 utils/plugins/lspRecommendation.ts create mode 100644 utils/plugins/managedPlugins.ts create mode 100644 utils/plugins/marketplaceHelpers.ts create mode 100644 utils/plugins/marketplaceManager.ts create mode 100644 utils/plugins/mcpPluginIntegration.ts create mode 100644 utils/plugins/mcpbHandler.ts create mode 100644 utils/plugins/officialMarketplace.ts create mode 100644 utils/plugins/officialMarketplaceGcs.ts create mode 100644 utils/plugins/officialMarketplaceStartupCheck.ts create mode 100644 utils/plugins/orphanedPluginFilter.ts create mode 100644 utils/plugins/parseMarketplaceInput.ts create mode 100644 utils/plugins/performStartupChecks.tsx create mode 100644 utils/plugins/pluginAutoupdate.ts create mode 100644 utils/plugins/pluginBlocklist.ts create mode 100644 utils/plugins/pluginDirectories.ts create mode 100644 utils/plugins/pluginFlagging.ts create mode 100644 utils/plugins/pluginIdentifier.ts create mode 100644 utils/plugins/pluginInstallationHelpers.ts create mode 100644 utils/plugins/pluginLoader.ts create mode 100644 utils/plugins/pluginOptionsStorage.ts create mode 100644 utils/plugins/pluginPolicy.ts create mode 100644 utils/plugins/pluginStartupCheck.ts create mode 100644 utils/plugins/pluginVersioning.ts create mode 100644 utils/plugins/reconciler.ts create mode 100644 utils/plugins/refresh.ts create mode 100644 utils/plugins/schemas.ts create mode 100644 utils/plugins/validatePlugin.ts create mode 100644 utils/plugins/walkPluginMarkdown.ts create mode 100644 utils/plugins/zipCache.ts create mode 100644 utils/plugins/zipCacheAdapters.ts create mode 100644 utils/powershell/dangerousCmdlets.ts create mode 100644 utils/powershell/parser.ts create mode 100644 utils/powershell/staticPrefix.ts create mode 100644 utils/preflightChecks.tsx create mode 100644 utils/privacyLevel.ts create mode 100644 utils/process.ts create mode 100644 utils/processUserInput/processBashCommand.tsx create mode 100644 utils/processUserInput/processSlashCommand.tsx create mode 100644 utils/processUserInput/processTextPrompt.ts create mode 100644 utils/processUserInput/processUserInput.ts create mode 100644 utils/profilerBase.ts create mode 100644 utils/promptCategory.ts create mode 100644 utils/promptEditor.ts create mode 100644 utils/promptShellExecution.ts create mode 100644 utils/proxy.ts create mode 100644 utils/queryContext.ts create mode 100644 utils/queryHelpers.ts create mode 100644 utils/queryProfiler.ts create mode 100644 utils/queueProcessor.ts create mode 100644 utils/readEditContext.ts create mode 100644 utils/readFileInRange.ts create mode 100644 utils/releaseNotes.ts create mode 100644 utils/renderOptions.ts create mode 100644 utils/ripgrep.ts create mode 100644 utils/sandbox/sandbox-adapter.ts create mode 100644 utils/sandbox/sandbox-ui-utils.ts create mode 100644 utils/sanitization.ts create mode 100644 utils/screenshotClipboard.ts create mode 100644 utils/sdkEventQueue.ts create mode 100644 utils/secureStorage/fallbackStorage.ts create mode 100644 utils/secureStorage/index.ts create mode 100644 utils/secureStorage/keychainPrefetch.ts create mode 100644 utils/secureStorage/macOsKeychainHelpers.ts create mode 100644 utils/secureStorage/macOsKeychainStorage.ts create mode 100644 utils/secureStorage/plainTextStorage.ts create mode 100644 utils/semanticBoolean.ts create mode 100644 utils/semanticNumber.ts create mode 100644 utils/semver.ts create mode 100644 utils/sequential.ts create mode 100644 utils/sessionActivity.ts create mode 100644 utils/sessionEnvVars.ts create mode 100644 utils/sessionEnvironment.ts create mode 100644 utils/sessionFileAccessHooks.ts create mode 100644 utils/sessionIngressAuth.ts create mode 100644 utils/sessionRestore.ts create mode 100644 utils/sessionStart.ts create mode 100644 utils/sessionState.ts create mode 100644 utils/sessionStorage.ts create mode 100644 utils/sessionStoragePortable.ts create mode 100644 utils/sessionTitle.ts create mode 100644 utils/sessionUrl.ts create mode 100644 utils/set.ts create mode 100644 utils/settings/allErrors.ts create mode 100644 utils/settings/applySettingsChange.ts create mode 100644 utils/settings/changeDetector.ts create mode 100644 utils/settings/constants.ts create mode 100644 utils/settings/internalWrites.ts create mode 100644 utils/settings/managedPath.ts create mode 100644 utils/settings/mdm/constants.ts create mode 100644 utils/settings/mdm/rawRead.ts create mode 100644 utils/settings/mdm/settings.ts create mode 100644 utils/settings/permissionValidation.ts create mode 100644 utils/settings/pluginOnlyPolicy.ts create mode 100644 utils/settings/schemaOutput.ts create mode 100644 utils/settings/settings.ts create mode 100644 utils/settings/settingsCache.ts create mode 100644 utils/settings/toolValidationConfig.ts create mode 100644 utils/settings/types.ts create mode 100644 utils/settings/validateEditTool.ts create mode 100644 utils/settings/validation.ts create mode 100644 utils/settings/validationTips.ts create mode 100644 utils/shell/bashProvider.ts create mode 100644 utils/shell/outputLimits.ts create mode 100644 utils/shell/powershellDetection.ts create mode 100644 utils/shell/powershellProvider.ts create mode 100644 utils/shell/prefix.ts create mode 100644 utils/shell/readOnlyCommandValidation.ts create mode 100644 utils/shell/resolveDefaultShell.ts create mode 100644 utils/shell/shellProvider.ts create mode 100644 utils/shell/shellToolUtils.ts create mode 100644 utils/shell/specPrefix.ts create mode 100644 utils/shellConfig.ts create mode 100644 utils/sideQuery.ts create mode 100644 utils/sideQuestion.ts create mode 100644 utils/signal.ts create mode 100644 utils/sinks.ts create mode 100644 utils/skills/skillChangeDetector.ts create mode 100644 utils/slashCommandParsing.ts create mode 100644 utils/sleep.ts create mode 100644 utils/sliceAnsi.ts create mode 100644 utils/slowOperations.ts create mode 100644 utils/standaloneAgent.ts create mode 100644 utils/startupProfiler.ts create mode 100644 utils/staticRender.tsx create mode 100644 utils/stats.ts create mode 100644 utils/statsCache.ts create mode 100644 utils/status.tsx create mode 100644 utils/statusNoticeDefinitions.tsx create mode 100644 utils/statusNoticeHelpers.ts create mode 100644 utils/stream.ts create mode 100644 utils/streamJsonStdoutGuard.ts create mode 100644 utils/streamlinedTransform.ts create mode 100644 utils/stringUtils.ts create mode 100644 utils/subprocessEnv.ts create mode 100644 utils/suggestions/commandSuggestions.ts create mode 100644 utils/suggestions/directoryCompletion.ts create mode 100644 utils/suggestions/shellHistoryCompletion.ts create mode 100644 utils/suggestions/skillUsageTracking.ts create mode 100644 utils/suggestions/slackChannelSuggestions.ts create mode 100644 utils/swarm/It2SetupPrompt.tsx create mode 100644 utils/swarm/backends/ITermBackend.ts create mode 100644 utils/swarm/backends/InProcessBackend.ts create mode 100644 utils/swarm/backends/PaneBackendExecutor.ts create mode 100644 utils/swarm/backends/TmuxBackend.ts create mode 100644 utils/swarm/backends/detection.ts create mode 100644 utils/swarm/backends/it2Setup.ts create mode 100644 utils/swarm/backends/registry.ts create mode 100644 utils/swarm/backends/teammateModeSnapshot.ts create mode 100644 utils/swarm/backends/types.ts create mode 100644 utils/swarm/constants.ts create mode 100644 utils/swarm/inProcessRunner.ts create mode 100644 utils/swarm/leaderPermissionBridge.ts create mode 100644 utils/swarm/permissionSync.ts create mode 100644 utils/swarm/reconnection.ts create mode 100644 utils/swarm/spawnInProcess.ts create mode 100644 utils/swarm/spawnUtils.ts create mode 100644 utils/swarm/teamHelpers.ts create mode 100644 utils/swarm/teammateInit.ts create mode 100644 utils/swarm/teammateLayoutManager.ts create mode 100644 utils/swarm/teammateModel.ts create mode 100644 utils/swarm/teammatePromptAddendum.ts create mode 100644 utils/systemDirectories.ts create mode 100644 utils/systemPrompt.ts create mode 100644 utils/systemPromptType.ts create mode 100644 utils/systemTheme.ts create mode 100644 utils/taggedId.ts create mode 100644 utils/task/TaskOutput.ts create mode 100644 utils/task/diskOutput.ts create mode 100644 utils/task/framework.ts create mode 100644 utils/task/outputFormatting.ts create mode 100644 utils/task/sdkProgress.ts create mode 100644 utils/tasks.ts create mode 100644 utils/teamDiscovery.ts create mode 100644 utils/teamMemoryOps.ts create mode 100644 utils/teammate.ts create mode 100644 utils/teammateContext.ts create mode 100644 utils/teammateMailbox.ts create mode 100644 utils/telemetry/betaSessionTracing.ts create mode 100644 utils/telemetry/bigqueryExporter.ts create mode 100644 utils/telemetry/events.ts create mode 100644 utils/telemetry/instrumentation.ts create mode 100644 utils/telemetry/logger.ts create mode 100644 utils/telemetry/perfettoTracing.ts create mode 100644 utils/telemetry/pluginTelemetry.ts create mode 100644 utils/telemetry/sessionTracing.ts create mode 100644 utils/telemetry/skillLoadedEvent.ts create mode 100644 utils/telemetryAttributes.ts create mode 100644 utils/teleport.tsx create mode 100644 utils/teleport/api.ts create mode 100644 utils/teleport/environmentSelection.ts create mode 100644 utils/teleport/environments.ts create mode 100644 utils/teleport/gitBundle.ts create mode 100644 utils/tempfile.ts create mode 100644 utils/terminal.ts create mode 100644 utils/terminalPanel.ts create mode 100644 utils/textHighlighting.ts create mode 100644 utils/theme.ts create mode 100644 utils/thinking.ts create mode 100644 utils/timeouts.ts create mode 100644 utils/tmuxSocket.ts create mode 100644 utils/todo/types.ts create mode 100644 utils/tokenBudget.ts create mode 100644 utils/tokens.ts create mode 100644 utils/toolErrors.ts create mode 100644 utils/toolPool.ts create mode 100644 utils/toolResultStorage.ts create mode 100644 utils/toolSchemaCache.ts create mode 100644 utils/toolSearch.ts create mode 100644 utils/transcriptSearch.ts create mode 100644 utils/treeify.ts create mode 100644 utils/truncate.ts create mode 100644 utils/ultraplan/ccrSession.ts create mode 100644 utils/ultraplan/keyword.ts create mode 100644 utils/unaryLogging.ts create mode 100644 utils/undercover.ts create mode 100644 utils/user.ts create mode 100644 utils/userAgent.ts create mode 100644 utils/userPromptKeywords.ts create mode 100644 utils/uuid.ts create mode 100644 utils/warningHandler.ts create mode 100644 utils/which.ts create mode 100644 utils/windowsPaths.ts create mode 100644 utils/withResolvers.ts create mode 100644 utils/words.ts create mode 100644 utils/workloadContext.ts create mode 100644 utils/worktree.ts create mode 100644 utils/worktreeModeEnabled.ts create mode 100644 utils/xdg.ts create mode 100644 utils/xml.ts create mode 100644 utils/yaml.ts create mode 100644 utils/zodToJsonSchema.ts create mode 100644 vim/motions.ts create mode 100644 vim/operators.ts create mode 100644 vim/textObjects.ts create mode 100644 vim/transitions.ts create mode 100644 vim/types.ts create mode 100644 voice/voiceModeEnabled.ts diff --git a/QueryEngine.ts b/QueryEngine.ts new file mode 100644 index 0000000..0a80c61 --- /dev/null +++ b/QueryEngine.ts @@ -0,0 +1,1295 @@ +import { feature } from 'bun:bundle' +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs' +import { randomUUID } from 'crypto' +import last from 'lodash-es/last.js' +import { + getSessionId, + isSessionPersistenceDisabled, +} from 'src/bootstrap/state.js' +import type { + PermissionMode, + SDKCompactBoundaryMessage, + SDKMessage, + SDKPermissionDenial, + SDKStatus, + SDKUserMessageReplay, +} from 'src/entrypoints/agentSdkTypes.js' +import { accumulateUsage, updateUsage } from 'src/services/api/claude.js' +import type { NonNullableUsage } from 'src/services/api/logging.js' +import { EMPTY_USAGE } from 'src/services/api/logging.js' +import stripAnsi from 'strip-ansi' +import type { Command } from './commands.js' +import { getSlashCommandToolSkills } from './commands.js' +import { + LOCAL_COMMAND_STDERR_TAG, + LOCAL_COMMAND_STDOUT_TAG, +} from './constants/xml.js' +import { + getModelUsage, + getTotalAPIDuration, + getTotalCost, +} from './cost-tracker.js' +import type { CanUseToolFn } from './hooks/useCanUseTool.js' +import { loadMemoryPrompt } from './memdir/memdir.js' +import { hasAutoMemPathOverride } from './memdir/paths.js' +import { query } from './query.js' +import { categorizeRetryableAPIError } from './services/api/errors.js' +import type { MCPServerConnection } from './services/mcp/types.js' +import type { AppState } from './state/AppState.js' +import { type Tools, type ToolUseContext, toolMatchesName } from './Tool.js' +import type { AgentDefinition } from './tools/AgentTool/loadAgentsDir.js' +import { SYNTHETIC_OUTPUT_TOOL_NAME } from './tools/SyntheticOutputTool/SyntheticOutputTool.js' +import type { Message } from './types/message.js' +import type { OrphanedPermission } from './types/textInputTypes.js' +import { createAbortController } from './utils/abortController.js' +import type { AttributionState } from './utils/commitAttribution.js' +import { getGlobalConfig } from './utils/config.js' +import { getCwd } from './utils/cwd.js' +import { isBareMode, isEnvTruthy } from './utils/envUtils.js' +import { getFastModeState } from './utils/fastMode.js' +import { + type FileHistoryState, + fileHistoryEnabled, + fileHistoryMakeSnapshot, +} from './utils/fileHistory.js' +import { + cloneFileStateCache, + type FileStateCache, +} from './utils/fileStateCache.js' +import { headlessProfilerCheckpoint } from './utils/headlessProfiler.js' +import { registerStructuredOutputEnforcement } from './utils/hooks/hookHelpers.js' +import { getInMemoryErrors } from './utils/log.js' +import { countToolCalls, SYNTHETIC_MESSAGES } from './utils/messages.js' +import { + getMainLoopModel, + parseUserSpecifiedModel, +} from './utils/model/model.js' +import { loadAllPluginsCacheOnly } from './utils/plugins/pluginLoader.js' +import { + type ProcessUserInputContext, + processUserInput, +} from './utils/processUserInput/processUserInput.js' +import { fetchSystemPromptParts } from './utils/queryContext.js' +import { setCwd } from './utils/Shell.js' +import { + flushSessionStorage, + recordTranscript, +} from './utils/sessionStorage.js' +import { asSystemPrompt } from './utils/systemPromptType.js' +import { resolveThemeSetting } from './utils/systemTheme.js' +import { + shouldEnableThinkingByDefault, + type ThinkingConfig, +} from './utils/thinking.js' + +// Lazy: MessageSelector.tsx pulls React/ink; only needed for message filtering at query time +/* eslint-disable @typescript-eslint/no-require-imports */ +const messageSelector = + (): typeof import('src/components/MessageSelector.js') => + require('src/components/MessageSelector.js') + +import { + localCommandOutputToSDKAssistantMessage, + toSDKCompactMetadata, +} from './utils/messages/mappers.js' +import { + buildSystemInitMessage, + sdkCompatToolName, +} from './utils/messages/systemInit.js' +import { + getScratchpadDir, + isScratchpadEnabled, +} from './utils/permissions/filesystem.js' +/* eslint-enable @typescript-eslint/no-require-imports */ +import { + handleOrphanedPermission, + isResultSuccessful, + normalizeMessage, +} from './utils/queryHelpers.js' + +// Dead code elimination: conditional import for coordinator mode +/* eslint-disable @typescript-eslint/no-require-imports */ +const getCoordinatorUserContext: ( + mcpClients: ReadonlyArray<{ name: string }>, + scratchpadDir?: string, +) => { [k: string]: string } = feature('COORDINATOR_MODE') + ? require('./coordinator/coordinatorMode.js').getCoordinatorUserContext + : () => ({}) +/* eslint-enable @typescript-eslint/no-require-imports */ + +// Dead code elimination: conditional import for snip compaction +/* eslint-disable @typescript-eslint/no-require-imports */ +const snipModule = feature('HISTORY_SNIP') + ? (require('./services/compact/snipCompact.js') as typeof import('./services/compact/snipCompact.js')) + : null +const snipProjection = feature('HISTORY_SNIP') + ? (require('./services/compact/snipProjection.js') as typeof import('./services/compact/snipProjection.js')) + : null +/* eslint-enable @typescript-eslint/no-require-imports */ + +export type QueryEngineConfig = { + cwd: string + tools: Tools + commands: Command[] + mcpClients: MCPServerConnection[] + agents: AgentDefinition[] + canUseTool: CanUseToolFn + getAppState: () => AppState + setAppState: (f: (prev: AppState) => AppState) => void + initialMessages?: Message[] + readFileCache: FileStateCache + customSystemPrompt?: string + appendSystemPrompt?: string + userSpecifiedModel?: string + fallbackModel?: string + thinkingConfig?: ThinkingConfig + maxTurns?: number + maxBudgetUsd?: number + taskBudget?: { total: number } + jsonSchema?: Record + verbose?: boolean + replayUserMessages?: boolean + /** Handler for URL elicitations triggered by MCP tool -32042 errors. */ + handleElicitation?: ToolUseContext['handleElicitation'] + includePartialMessages?: boolean + setSDKStatus?: (status: SDKStatus) => void + abortController?: AbortController + orphanedPermission?: OrphanedPermission + /** + * Snip-boundary handler: receives each yielded system message plus the + * current mutableMessages store. Returns undefined if the message is not a + * snip boundary; otherwise returns the replayed snip result. Injected by + * ask() when HISTORY_SNIP is enabled so feature-gated strings stay inside + * the gated module (keeps QueryEngine free of excluded strings and testable + * despite feature() returning false under bun test). SDK-only: the REPL + * keeps full history for UI scrollback and projects on demand via + * projectSnippedView; QueryEngine truncates here to bound memory in long + * headless sessions (no UI to preserve). + */ + snipReplay?: ( + yieldedSystemMsg: Message, + store: Message[], + ) => { messages: Message[]; executed: boolean } | undefined +} + +/** + * QueryEngine owns the query lifecycle and session state for a conversation. + * It extracts the core logic from ask() into a standalone class that can be + * used by both the headless/SDK path and (in a future phase) the REPL. + * + * One QueryEngine per conversation. Each submitMessage() call starts a new + * turn within the same conversation. State (messages, file cache, usage, etc.) + * persists across turns. + */ +export class QueryEngine { + private config: QueryEngineConfig + private mutableMessages: Message[] + private abortController: AbortController + private permissionDenials: SDKPermissionDenial[] + private totalUsage: NonNullableUsage + private hasHandledOrphanedPermission = false + private readFileState: FileStateCache + // Turn-scoped skill discovery tracking (feeds was_discovered on + // tengu_skill_tool_invocation). Must persist across the two + // processUserInputContext rebuilds inside submitMessage, but is cleared + // at the start of each submitMessage to avoid unbounded growth across + // many turns in SDK mode. + private discoveredSkillNames = new Set() + private loadedNestedMemoryPaths = new Set() + + constructor(config: QueryEngineConfig) { + this.config = config + this.mutableMessages = config.initialMessages ?? [] + this.abortController = config.abortController ?? createAbortController() + this.permissionDenials = [] + this.readFileState = config.readFileCache + this.totalUsage = EMPTY_USAGE + } + + async *submitMessage( + prompt: string | ContentBlockParam[], + options?: { uuid?: string; isMeta?: boolean }, + ): AsyncGenerator { + const { + cwd, + commands, + tools, + mcpClients, + verbose = false, + thinkingConfig, + maxTurns, + maxBudgetUsd, + taskBudget, + canUseTool, + customSystemPrompt, + appendSystemPrompt, + userSpecifiedModel, + fallbackModel, + jsonSchema, + getAppState, + setAppState, + replayUserMessages = false, + includePartialMessages = false, + agents = [], + setSDKStatus, + orphanedPermission, + } = this.config + + this.discoveredSkillNames.clear() + setCwd(cwd) + const persistSession = !isSessionPersistenceDisabled() + const startTime = Date.now() + + // Wrap canUseTool to track permission denials + const wrappedCanUseTool: CanUseToolFn = async ( + tool, + input, + toolUseContext, + assistantMessage, + toolUseID, + forceDecision, + ) => { + const result = await canUseTool( + tool, + input, + toolUseContext, + assistantMessage, + toolUseID, + forceDecision, + ) + + // Track denials for SDK reporting + if (result.behavior !== 'allow') { + this.permissionDenials.push({ + tool_name: sdkCompatToolName(tool.name), + tool_use_id: toolUseID, + tool_input: input, + }) + } + + return result + } + + const initialAppState = getAppState() + const initialMainLoopModel = userSpecifiedModel + ? parseUserSpecifiedModel(userSpecifiedModel) + : getMainLoopModel() + + const initialThinkingConfig: ThinkingConfig = thinkingConfig + ? thinkingConfig + : shouldEnableThinkingByDefault() !== false + ? { type: 'adaptive' } + : { type: 'disabled' } + + headlessProfilerCheckpoint('before_getSystemPrompt') + // Narrow once so TS tracks the type through the conditionals below. + const customPrompt = + typeof customSystemPrompt === 'string' ? customSystemPrompt : undefined + const { + defaultSystemPrompt, + userContext: baseUserContext, + systemContext, + } = await fetchSystemPromptParts({ + tools, + mainLoopModel: initialMainLoopModel, + additionalWorkingDirectories: Array.from( + initialAppState.toolPermissionContext.additionalWorkingDirectories.keys(), + ), + mcpClients, + customSystemPrompt: customPrompt, + }) + headlessProfilerCheckpoint('after_getSystemPrompt') + const userContext = { + ...baseUserContext, + ...getCoordinatorUserContext( + mcpClients, + isScratchpadEnabled() ? getScratchpadDir() : undefined, + ), + } + + // When an SDK caller provides a custom system prompt AND has set + // CLAUDE_COWORK_MEMORY_PATH_OVERRIDE, inject the memory-mechanics prompt. + // The env var is an explicit opt-in signal — the caller has wired up + // a memory directory and needs Claude to know how to use it (which + // Write/Edit tools to call, MEMORY.md filename, loading semantics). + // The caller can layer their own policy text via appendSystemPrompt. + const memoryMechanicsPrompt = + customPrompt !== undefined && hasAutoMemPathOverride() + ? await loadMemoryPrompt() + : null + + const systemPrompt = asSystemPrompt([ + ...(customPrompt !== undefined ? [customPrompt] : defaultSystemPrompt), + ...(memoryMechanicsPrompt ? [memoryMechanicsPrompt] : []), + ...(appendSystemPrompt ? [appendSystemPrompt] : []), + ]) + + // Register function hook for structured output enforcement + const hasStructuredOutputTool = tools.some(t => + toolMatchesName(t, SYNTHETIC_OUTPUT_TOOL_NAME), + ) + if (jsonSchema && hasStructuredOutputTool) { + registerStructuredOutputEnforcement(setAppState, getSessionId()) + } + + let processUserInputContext: ProcessUserInputContext = { + messages: this.mutableMessages, + // Slash commands that mutate the message array (e.g. /force-snip) + // call setMessages(fn). In interactive mode this writes back to + // AppState; in print mode we write back to mutableMessages so the + // rest of the query loop (push at :389, snapshot at :392) sees + // the result. The second processUserInputContext below (after + // slash-command processing) keeps the no-op — nothing else calls + // setMessages past that point. + setMessages: fn => { + this.mutableMessages = fn(this.mutableMessages) + }, + onChangeAPIKey: () => {}, + handleElicitation: this.config.handleElicitation, + options: { + commands, + debug: false, // we use stdout, so don't want to clobber it + tools, + verbose, + mainLoopModel: initialMainLoopModel, + thinkingConfig: initialThinkingConfig, + mcpClients, + mcpResources: {}, + ideInstallationStatus: null, + isNonInteractiveSession: true, + customSystemPrompt, + appendSystemPrompt, + agentDefinitions: { activeAgents: agents, allAgents: [] }, + theme: resolveThemeSetting(getGlobalConfig().theme), + maxBudgetUsd, + }, + getAppState, + setAppState, + abortController: this.abortController, + readFileState: this.readFileState, + nestedMemoryAttachmentTriggers: new Set(), + loadedNestedMemoryPaths: this.loadedNestedMemoryPaths, + dynamicSkillDirTriggers: new Set(), + discoveredSkillNames: this.discoveredSkillNames, + setInProgressToolUseIDs: () => {}, + setResponseLength: () => {}, + updateFileHistoryState: ( + updater: (prev: FileHistoryState) => FileHistoryState, + ) => { + setAppState(prev => { + const updated = updater(prev.fileHistory) + if (updated === prev.fileHistory) return prev + return { ...prev, fileHistory: updated } + }) + }, + updateAttributionState: ( + updater: (prev: AttributionState) => AttributionState, + ) => { + setAppState(prev => { + const updated = updater(prev.attribution) + if (updated === prev.attribution) return prev + return { ...prev, attribution: updated } + }) + }, + setSDKStatus, + } + + // Handle orphaned permission (only once per engine lifetime) + if (orphanedPermission && !this.hasHandledOrphanedPermission) { + this.hasHandledOrphanedPermission = true + for await (const message of handleOrphanedPermission( + orphanedPermission, + tools, + this.mutableMessages, + processUserInputContext, + )) { + yield message + } + } + + const { + messages: messagesFromUserInput, + shouldQuery, + allowedTools, + model: modelFromUserInput, + resultText, + } = await processUserInput({ + input: prompt, + mode: 'prompt', + setToolJSX: () => {}, + context: { + ...processUserInputContext, + messages: this.mutableMessages, + }, + messages: this.mutableMessages, + uuid: options?.uuid, + isMeta: options?.isMeta, + querySource: 'sdk', + }) + + // Push new messages, including user input and any attachments + this.mutableMessages.push(...messagesFromUserInput) + + // Update params to reflect updates from processing /slash commands + const messages = [...this.mutableMessages] + + // Persist the user's message(s) to transcript BEFORE entering the query + // loop. The for-await below only calls recordTranscript when ask() yields + // an assistant/user/compact_boundary message — which doesn't happen until + // the API responds. If the process is killed before that (e.g. user clicks + // Stop in cowork seconds after send), the transcript is left with only + // queue-operation entries; getLastSessionLog filters those out, returns + // null, and --resume fails with "No conversation found". Writing now makes + // the transcript resumable from the point the user message was accepted, + // even if no API response ever arrives. + // + // --bare / SIMPLE: fire-and-forget. Scripted calls don't --resume after + // kill-mid-request. The await is ~4ms on SSD, ~30ms under disk contention + // — the single largest controllable critical-path cost after module eval. + // Transcript is still written (for post-hoc debugging); just not blocking. + if (persistSession && messagesFromUserInput.length > 0) { + const transcriptPromise = recordTranscript(messages) + if (isBareMode()) { + void transcriptPromise + } else { + await transcriptPromise + if ( + isEnvTruthy(process.env.CLAUDE_CODE_EAGER_FLUSH) || + isEnvTruthy(process.env.CLAUDE_CODE_IS_COWORK) + ) { + await flushSessionStorage() + } + } + } + + // Filter messages that should be acknowledged after transcript + const replayableMessages = messagesFromUserInput.filter( + msg => + (msg.type === 'user' && + !msg.isMeta && // Skip synthetic caveat messages + !msg.toolUseResult && // Skip tool results (they'll be acked from query) + messageSelector().selectableUserMessagesFilter(msg)) || // Skip non-user-authored messages (task notifications, etc.) + (msg.type === 'system' && msg.subtype === 'compact_boundary'), // Always ack compact boundaries + ) + const messagesToAck = replayUserMessages ? replayableMessages : [] + + // Update the ToolPermissionContext based on user input processing (as necessary) + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...prev.toolPermissionContext, + alwaysAllowRules: { + ...prev.toolPermissionContext.alwaysAllowRules, + command: allowedTools, + }, + }, + })) + + const mainLoopModel = modelFromUserInput ?? initialMainLoopModel + + // Recreate after processing the prompt to pick up updated messages and + // model (from slash commands). + processUserInputContext = { + messages, + setMessages: () => {}, + onChangeAPIKey: () => {}, + handleElicitation: this.config.handleElicitation, + options: { + commands, + debug: false, + tools, + verbose, + mainLoopModel, + thinkingConfig: initialThinkingConfig, + mcpClients, + mcpResources: {}, + ideInstallationStatus: null, + isNonInteractiveSession: true, + customSystemPrompt, + appendSystemPrompt, + theme: resolveThemeSetting(getGlobalConfig().theme), + agentDefinitions: { activeAgents: agents, allAgents: [] }, + maxBudgetUsd, + }, + getAppState, + setAppState, + abortController: this.abortController, + readFileState: this.readFileState, + nestedMemoryAttachmentTriggers: new Set(), + loadedNestedMemoryPaths: this.loadedNestedMemoryPaths, + dynamicSkillDirTriggers: new Set(), + discoveredSkillNames: this.discoveredSkillNames, + setInProgressToolUseIDs: () => {}, + setResponseLength: () => {}, + updateFileHistoryState: processUserInputContext.updateFileHistoryState, + updateAttributionState: processUserInputContext.updateAttributionState, + setSDKStatus, + } + + headlessProfilerCheckpoint('before_skills_plugins') + // Cache-only: headless/SDK/CCR startup must not block on network for + // ref-tracked plugins. CCR populates the cache via CLAUDE_CODE_SYNC_PLUGIN_INSTALL + // (headlessPluginInstall) or CLAUDE_CODE_PLUGIN_SEED_DIR before this runs; + // SDK callers that need fresh source can call /reload-plugins. + const [skills, { enabled: enabledPlugins }] = await Promise.all([ + getSlashCommandToolSkills(getCwd()), + loadAllPluginsCacheOnly(), + ]) + headlessProfilerCheckpoint('after_skills_plugins') + + yield buildSystemInitMessage({ + tools, + mcpClients, + model: mainLoopModel, + permissionMode: initialAppState.toolPermissionContext + .mode as PermissionMode, // TODO: avoid the cast + commands, + agents, + skills, + plugins: enabledPlugins, + fastMode: initialAppState.fastMode, + }) + + // Record when system message is yielded for headless latency tracking + headlessProfilerCheckpoint('system_message_yielded') + + if (!shouldQuery) { + // Return the results of local slash commands. + // Use messagesFromUserInput (not replayableMessages) for command output + // because selectableUserMessagesFilter excludes local-command-stdout tags. + for (const msg of messagesFromUserInput) { + if ( + msg.type === 'user' && + typeof msg.message.content === 'string' && + (msg.message.content.includes(`<${LOCAL_COMMAND_STDOUT_TAG}>`) || + msg.message.content.includes(`<${LOCAL_COMMAND_STDERR_TAG}>`) || + msg.isCompactSummary) + ) { + yield { + type: 'user', + message: { + ...msg.message, + content: stripAnsi(msg.message.content), + }, + session_id: getSessionId(), + parent_tool_use_id: null, + uuid: msg.uuid, + timestamp: msg.timestamp, + isReplay: !msg.isCompactSummary, + isSynthetic: msg.isMeta || msg.isVisibleInTranscriptOnly, + } as SDKUserMessageReplay + } + + // Local command output — yield as a synthetic assistant message so + // RC renders it as assistant-style text rather than a user bubble. + // Emitted as assistant (not the dedicated SDKLocalCommandOutputMessage + // system subtype) so mobile clients + session-ingress can parse it. + if ( + msg.type === 'system' && + msg.subtype === 'local_command' && + typeof msg.content === 'string' && + (msg.content.includes(`<${LOCAL_COMMAND_STDOUT_TAG}>`) || + msg.content.includes(`<${LOCAL_COMMAND_STDERR_TAG}>`)) + ) { + yield localCommandOutputToSDKAssistantMessage(msg.content, msg.uuid) + } + + if (msg.type === 'system' && msg.subtype === 'compact_boundary') { + yield { + type: 'system', + subtype: 'compact_boundary' as const, + session_id: getSessionId(), + uuid: msg.uuid, + compact_metadata: toSDKCompactMetadata(msg.compactMetadata), + } as SDKCompactBoundaryMessage + } + } + + if (persistSession) { + await recordTranscript(messages) + if ( + isEnvTruthy(process.env.CLAUDE_CODE_EAGER_FLUSH) || + isEnvTruthy(process.env.CLAUDE_CODE_IS_COWORK) + ) { + await flushSessionStorage() + } + } + + yield { + type: 'result', + subtype: 'success', + is_error: false, + duration_ms: Date.now() - startTime, + duration_api_ms: getTotalAPIDuration(), + num_turns: messages.length - 1, + result: resultText ?? '', + stop_reason: null, + session_id: getSessionId(), + total_cost_usd: getTotalCost(), + usage: this.totalUsage, + modelUsage: getModelUsage(), + permission_denials: this.permissionDenials, + fast_mode_state: getFastModeState( + mainLoopModel, + initialAppState.fastMode, + ), + uuid: randomUUID(), + } + return + } + + if (fileHistoryEnabled() && persistSession) { + messagesFromUserInput + .filter(messageSelector().selectableUserMessagesFilter) + .forEach(message => { + void fileHistoryMakeSnapshot( + (updater: (prev: FileHistoryState) => FileHistoryState) => { + setAppState(prev => ({ + ...prev, + fileHistory: updater(prev.fileHistory), + })) + }, + message.uuid, + ) + }) + } + + // Track current message usage (reset on each message_start) + let currentMessageUsage: NonNullableUsage = EMPTY_USAGE + let turnCount = 1 + let hasAcknowledgedInitialMessages = false + // Track structured output from StructuredOutput tool calls + let structuredOutputFromTool: unknown + // Track the last stop_reason from assistant messages + let lastStopReason: string | null = null + // Reference-based watermark so error_during_execution's errors[] is + // turn-scoped. A length-based index breaks when the 100-entry ring buffer + // shift()s during the turn — the index slides. If this entry is rotated + // out, lastIndexOf returns -1 and we include everything (safe fallback). + const errorLogWatermark = getInMemoryErrors().at(-1) + // Snapshot count before this query for delta-based retry limiting + const initialStructuredOutputCalls = jsonSchema + ? countToolCalls(this.mutableMessages, SYNTHETIC_OUTPUT_TOOL_NAME) + : 0 + + for await (const message of query({ + messages, + systemPrompt, + userContext, + systemContext, + canUseTool: wrappedCanUseTool, + toolUseContext: processUserInputContext, + fallbackModel, + querySource: 'sdk', + maxTurns, + taskBudget, + })) { + // Record assistant, user, and compact boundary messages + if ( + message.type === 'assistant' || + message.type === 'user' || + (message.type === 'system' && message.subtype === 'compact_boundary') + ) { + // Before writing a compact boundary, flush any in-memory-only + // messages up through the preservedSegment tail. Attachments and + // progress are now recorded inline (their switch cases below), but + // this flush still matters for the preservedSegment tail walk. + // If the SDK subprocess restarts before then (claude-desktop kills + // between turns), tailUuid points to a never-written message → + // applyPreservedSegmentRelinks fails its tail→head walk → returns + // without pruning → resume loads full pre-compact history. + if ( + persistSession && + message.type === 'system' && + message.subtype === 'compact_boundary' + ) { + const tailUuid = message.compactMetadata?.preservedSegment?.tailUuid + if (tailUuid) { + const tailIdx = this.mutableMessages.findLastIndex( + m => m.uuid === tailUuid, + ) + if (tailIdx !== -1) { + await recordTranscript(this.mutableMessages.slice(0, tailIdx + 1)) + } + } + } + messages.push(message) + if (persistSession) { + // Fire-and-forget for assistant messages. claude.ts yields one + // assistant message per content block, then mutates the last + // one's message.usage/stop_reason on message_delta — relying on + // the write queue's 100ms lazy jsonStringify. Awaiting here + // blocks ask()'s generator, so message_delta can't run until + // every block is consumed; the drain timer (started at block 1) + // elapses first. Interactive CC doesn't hit this because + // useLogMessages.ts fire-and-forgets. enqueueWrite is + // order-preserving so fire-and-forget here is safe. + if (message.type === 'assistant') { + void recordTranscript(messages) + } else { + await recordTranscript(messages) + } + } + + // Acknowledge initial user messages after first transcript recording + if (!hasAcknowledgedInitialMessages && messagesToAck.length > 0) { + hasAcknowledgedInitialMessages = true + for (const msgToAck of messagesToAck) { + if (msgToAck.type === 'user') { + yield { + type: 'user', + message: msgToAck.message, + session_id: getSessionId(), + parent_tool_use_id: null, + uuid: msgToAck.uuid, + timestamp: msgToAck.timestamp, + isReplay: true, + } as SDKUserMessageReplay + } + } + } + } + + if (message.type === 'user') { + turnCount++ + } + + switch (message.type) { + case 'tombstone': + // Tombstone messages are control signals for removing messages, skip them + break + case 'assistant': + // Capture stop_reason if already set (synthetic messages). For + // streamed responses, this is null at content_block_stop time; + // the real value arrives via message_delta (handled below). + if (message.message.stop_reason != null) { + lastStopReason = message.message.stop_reason + } + this.mutableMessages.push(message) + yield* normalizeMessage(message) + break + case 'progress': + this.mutableMessages.push(message) + // Record inline so the dedup loop in the next ask() call sees it + // as already-recorded. Without this, deferred progress interleaves + // with already-recorded tool_results in mutableMessages, and the + // dedup walk freezes startingParentUuid at the wrong message — + // forking the chain and orphaning the conversation on resume. + if (persistSession) { + messages.push(message) + void recordTranscript(messages) + } + yield* normalizeMessage(message) + break + case 'user': + this.mutableMessages.push(message) + yield* normalizeMessage(message) + break + case 'stream_event': + if (message.event.type === 'message_start') { + // Reset current message usage for new message + currentMessageUsage = EMPTY_USAGE + currentMessageUsage = updateUsage( + currentMessageUsage, + message.event.message.usage, + ) + } + if (message.event.type === 'message_delta') { + currentMessageUsage = updateUsage( + currentMessageUsage, + message.event.usage, + ) + // Capture stop_reason from message_delta. The assistant message + // is yielded at content_block_stop with stop_reason=null; the + // real value only arrives here (see claude.ts message_delta + // handler). Without this, result.stop_reason is always null. + if (message.event.delta.stop_reason != null) { + lastStopReason = message.event.delta.stop_reason + } + } + if (message.event.type === 'message_stop') { + // Accumulate current message usage into total + this.totalUsage = accumulateUsage( + this.totalUsage, + currentMessageUsage, + ) + } + + if (includePartialMessages) { + yield { + type: 'stream_event' as const, + event: message.event, + session_id: getSessionId(), + parent_tool_use_id: null, + uuid: randomUUID(), + } + } + + break + case 'attachment': + this.mutableMessages.push(message) + // Record inline (same reason as progress above). + if (persistSession) { + messages.push(message) + void recordTranscript(messages) + } + + // Extract structured output from StructuredOutput tool calls + if (message.attachment.type === 'structured_output') { + structuredOutputFromTool = message.attachment.data + } + // Handle max turns reached signal from query.ts + else if (message.attachment.type === 'max_turns_reached') { + if (persistSession) { + if ( + isEnvTruthy(process.env.CLAUDE_CODE_EAGER_FLUSH) || + isEnvTruthy(process.env.CLAUDE_CODE_IS_COWORK) + ) { + await flushSessionStorage() + } + } + yield { + type: 'result', + subtype: 'error_max_turns', + duration_ms: Date.now() - startTime, + duration_api_ms: getTotalAPIDuration(), + is_error: true, + num_turns: message.attachment.turnCount, + stop_reason: lastStopReason, + session_id: getSessionId(), + total_cost_usd: getTotalCost(), + usage: this.totalUsage, + modelUsage: getModelUsage(), + permission_denials: this.permissionDenials, + fast_mode_state: getFastModeState( + mainLoopModel, + initialAppState.fastMode, + ), + uuid: randomUUID(), + errors: [ + `Reached maximum number of turns (${message.attachment.maxTurns})`, + ], + } + return + } + // Yield queued_command attachments as SDK user message replays + else if ( + replayUserMessages && + message.attachment.type === 'queued_command' + ) { + yield { + type: 'user', + message: { + role: 'user' as const, + content: message.attachment.prompt, + }, + session_id: getSessionId(), + parent_tool_use_id: null, + uuid: message.attachment.source_uuid || message.uuid, + timestamp: message.timestamp, + isReplay: true, + } as SDKUserMessageReplay + } + break + case 'stream_request_start': + // Don't yield stream request start messages + break + case 'system': { + // Snip boundary: replay on our store to remove zombie messages and + // stale markers. The yielded boundary is a signal, not data to push — + // the replay produces its own equivalent boundary. Without this, + // markers persist and re-trigger on every turn, and mutableMessages + // never shrinks (memory leak in long SDK sessions). The subtype + // check lives inside the injected callback so feature-gated strings + // stay out of this file (excluded-strings check). + const snipResult = this.config.snipReplay?.( + message, + this.mutableMessages, + ) + if (snipResult !== undefined) { + if (snipResult.executed) { + this.mutableMessages.length = 0 + this.mutableMessages.push(...snipResult.messages) + } + break + } + this.mutableMessages.push(message) + // Yield compact boundary messages to SDK + if ( + message.subtype === 'compact_boundary' && + message.compactMetadata + ) { + // Release pre-compaction messages for GC. The boundary was just + // pushed so it's the last element. query.ts already uses + // getMessagesAfterCompactBoundary() internally, so only + // post-boundary messages are needed going forward. + const mutableBoundaryIdx = this.mutableMessages.length - 1 + if (mutableBoundaryIdx > 0) { + this.mutableMessages.splice(0, mutableBoundaryIdx) + } + const localBoundaryIdx = messages.length - 1 + if (localBoundaryIdx > 0) { + messages.splice(0, localBoundaryIdx) + } + + yield { + type: 'system', + subtype: 'compact_boundary' as const, + session_id: getSessionId(), + uuid: message.uuid, + compact_metadata: toSDKCompactMetadata(message.compactMetadata), + } + } + if (message.subtype === 'api_error') { + yield { + type: 'system', + subtype: 'api_retry' as const, + attempt: message.retryAttempt, + max_retries: message.maxRetries, + retry_delay_ms: message.retryInMs, + error_status: message.error.status ?? null, + error: categorizeRetryableAPIError(message.error), + session_id: getSessionId(), + uuid: message.uuid, + } + } + // Don't yield other system messages in headless mode + break + } + case 'tool_use_summary': + // Yield tool use summary messages to SDK + yield { + type: 'tool_use_summary' as const, + summary: message.summary, + preceding_tool_use_ids: message.precedingToolUseIds, + session_id: getSessionId(), + uuid: message.uuid, + } + break + } + + // Check if USD budget has been exceeded + if (maxBudgetUsd !== undefined && getTotalCost() >= maxBudgetUsd) { + if (persistSession) { + if ( + isEnvTruthy(process.env.CLAUDE_CODE_EAGER_FLUSH) || + isEnvTruthy(process.env.CLAUDE_CODE_IS_COWORK) + ) { + await flushSessionStorage() + } + } + yield { + type: 'result', + subtype: 'error_max_budget_usd', + duration_ms: Date.now() - startTime, + duration_api_ms: getTotalAPIDuration(), + is_error: true, + num_turns: turnCount, + stop_reason: lastStopReason, + session_id: getSessionId(), + total_cost_usd: getTotalCost(), + usage: this.totalUsage, + modelUsage: getModelUsage(), + permission_denials: this.permissionDenials, + fast_mode_state: getFastModeState( + mainLoopModel, + initialAppState.fastMode, + ), + uuid: randomUUID(), + errors: [`Reached maximum budget ($${maxBudgetUsd})`], + } + return + } + + // Check if structured output retry limit exceeded (only on user messages) + if (message.type === 'user' && jsonSchema) { + const currentCalls = countToolCalls( + this.mutableMessages, + SYNTHETIC_OUTPUT_TOOL_NAME, + ) + const callsThisQuery = currentCalls - initialStructuredOutputCalls + const maxRetries = parseInt( + process.env.MAX_STRUCTURED_OUTPUT_RETRIES || '5', + 10, + ) + if (callsThisQuery >= maxRetries) { + if (persistSession) { + if ( + isEnvTruthy(process.env.CLAUDE_CODE_EAGER_FLUSH) || + isEnvTruthy(process.env.CLAUDE_CODE_IS_COWORK) + ) { + await flushSessionStorage() + } + } + yield { + type: 'result', + subtype: 'error_max_structured_output_retries', + duration_ms: Date.now() - startTime, + duration_api_ms: getTotalAPIDuration(), + is_error: true, + num_turns: turnCount, + stop_reason: lastStopReason, + session_id: getSessionId(), + total_cost_usd: getTotalCost(), + usage: this.totalUsage, + modelUsage: getModelUsage(), + permission_denials: this.permissionDenials, + fast_mode_state: getFastModeState( + mainLoopModel, + initialAppState.fastMode, + ), + uuid: randomUUID(), + errors: [ + `Failed to provide valid structured output after ${maxRetries} attempts`, + ], + } + return + } + } + } + + // Stop hooks yield progress/attachment messages AFTER the assistant + // response (via yield* handleStopHooks in query.ts). Since #23537 pushes + // those to `messages` inline, last(messages) can be a progress/attachment + // instead of the assistant — which makes textResult extraction below + // return '' and -p mode emit a blank line. Allowlist to assistant|user: + // isResultSuccessful handles both (user with all tool_result blocks is a + // valid successful terminal state). + const result = messages.findLast( + m => m.type === 'assistant' || m.type === 'user', + ) + // Capture for the error_during_execution diagnostic — isResultSuccessful + // is a type predicate (message is Message), so inside the false branch + // `result` narrows to never and these accesses don't typecheck. + const edeResultType = result?.type ?? 'undefined' + const edeLastContentType = + result?.type === 'assistant' + ? (last(result.message.content)?.type ?? 'none') + : 'n/a' + + // Flush buffered transcript writes before yielding result. + // The desktop app kills the CLI process immediately after receiving the + // result message, so any unflushed writes would be lost. + if (persistSession) { + if ( + isEnvTruthy(process.env.CLAUDE_CODE_EAGER_FLUSH) || + isEnvTruthy(process.env.CLAUDE_CODE_IS_COWORK) + ) { + await flushSessionStorage() + } + } + + if (!isResultSuccessful(result, lastStopReason)) { + yield { + type: 'result', + subtype: 'error_during_execution', + duration_ms: Date.now() - startTime, + duration_api_ms: getTotalAPIDuration(), + is_error: true, + num_turns: turnCount, + stop_reason: lastStopReason, + session_id: getSessionId(), + total_cost_usd: getTotalCost(), + usage: this.totalUsage, + modelUsage: getModelUsage(), + permission_denials: this.permissionDenials, + fast_mode_state: getFastModeState( + mainLoopModel, + initialAppState.fastMode, + ), + uuid: randomUUID(), + // Diagnostic prefix: these are what isResultSuccessful() checks — if + // the result type isn't assistant-with-text/thinking or user-with- + // tool_result, and stop_reason isn't end_turn, that's why this fired. + // errors[] is turn-scoped via the watermark; previously it dumped the + // entire process's logError buffer (ripgrep timeouts, ENOENT, etc). + errors: (() => { + const all = getInMemoryErrors() + const start = errorLogWatermark + ? all.lastIndexOf(errorLogWatermark) + 1 + : 0 + return [ + `[ede_diagnostic] result_type=${edeResultType} last_content_type=${edeLastContentType} stop_reason=${lastStopReason}`, + ...all.slice(start).map(_ => _.error), + ] + })(), + } + return + } + + // Extract the text result based on message type + let textResult = '' + let isApiError = false + + if (result.type === 'assistant') { + const lastContent = last(result.message.content) + if ( + lastContent?.type === 'text' && + !SYNTHETIC_MESSAGES.has(lastContent.text) + ) { + textResult = lastContent.text + } + isApiError = Boolean(result.isApiErrorMessage) + } + + yield { + type: 'result', + subtype: 'success', + is_error: isApiError, + duration_ms: Date.now() - startTime, + duration_api_ms: getTotalAPIDuration(), + num_turns: turnCount, + result: textResult, + stop_reason: lastStopReason, + session_id: getSessionId(), + total_cost_usd: getTotalCost(), + usage: this.totalUsage, + modelUsage: getModelUsage(), + permission_denials: this.permissionDenials, + structured_output: structuredOutputFromTool, + fast_mode_state: getFastModeState( + mainLoopModel, + initialAppState.fastMode, + ), + uuid: randomUUID(), + } + } + + interrupt(): void { + this.abortController.abort() + } + + getMessages(): readonly Message[] { + return this.mutableMessages + } + + getReadFileState(): FileStateCache { + return this.readFileState + } + + getSessionId(): string { + return getSessionId() + } + + setModel(model: string): void { + this.config.userSpecifiedModel = model + } +} + +/** + * Sends a single prompt to the Claude API and returns the response. + * Assumes that claude is being used non-interactively -- will not + * ask the user for permissions or further input. + * + * Convenience wrapper around QueryEngine for one-shot usage. + */ +export async function* ask({ + commands, + prompt, + promptUuid, + isMeta, + cwd, + tools, + mcpClients, + verbose = false, + thinkingConfig, + maxTurns, + maxBudgetUsd, + taskBudget, + canUseTool, + mutableMessages = [], + getReadFileCache, + setReadFileCache, + customSystemPrompt, + appendSystemPrompt, + userSpecifiedModel, + fallbackModel, + jsonSchema, + getAppState, + setAppState, + abortController, + replayUserMessages = false, + includePartialMessages = false, + handleElicitation, + agents = [], + setSDKStatus, + orphanedPermission, +}: { + commands: Command[] + prompt: string | Array + promptUuid?: string + isMeta?: boolean + cwd: string + tools: Tools + verbose?: boolean + mcpClients: MCPServerConnection[] + thinkingConfig?: ThinkingConfig + maxTurns?: number + maxBudgetUsd?: number + taskBudget?: { total: number } + canUseTool: CanUseToolFn + mutableMessages?: Message[] + customSystemPrompt?: string + appendSystemPrompt?: string + userSpecifiedModel?: string + fallbackModel?: string + jsonSchema?: Record + getAppState: () => AppState + setAppState: (f: (prev: AppState) => AppState) => void + getReadFileCache: () => FileStateCache + setReadFileCache: (cache: FileStateCache) => void + abortController?: AbortController + replayUserMessages?: boolean + includePartialMessages?: boolean + handleElicitation?: ToolUseContext['handleElicitation'] + agents?: AgentDefinition[] + setSDKStatus?: (status: SDKStatus) => void + orphanedPermission?: OrphanedPermission +}): AsyncGenerator { + const engine = new QueryEngine({ + cwd, + tools, + commands, + mcpClients, + agents, + canUseTool, + getAppState, + setAppState, + initialMessages: mutableMessages, + readFileCache: cloneFileStateCache(getReadFileCache()), + customSystemPrompt, + appendSystemPrompt, + userSpecifiedModel, + fallbackModel, + thinkingConfig, + maxTurns, + maxBudgetUsd, + taskBudget, + jsonSchema, + verbose, + handleElicitation, + replayUserMessages, + includePartialMessages, + setSDKStatus, + abortController, + orphanedPermission, + ...(feature('HISTORY_SNIP') + ? { + snipReplay: (yielded: Message, store: Message[]) => { + if (!snipProjection!.isSnipBoundaryMessage(yielded)) + return undefined + return snipModule!.snipCompactIfNeeded(store, { force: true }) + }, + } + : {}), + }) + + try { + yield* engine.submitMessage(prompt, { + uuid: promptUuid, + isMeta, + }) + } finally { + setReadFileCache(engine.getReadFileState()) + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec3a91d --- /dev/null +++ b/README.md @@ -0,0 +1,463 @@ +# Claude Code's Entire Source Code Got Leaked via a Sourcemap in npm, Let's Talk About It + +> **PS:** I've also published this [breakdown on my blog](https://kuber.studio/blog/AI/Claude-Code's-Entire-Source-Code-Got-Leaked-via-a-Sourcemap-in-npm,-Let's-Talk-About-it) with a better reading experience and UX :) + +> There's also a non-zero chance this repo might be taken down, so if you want to play around with it later, or archive it yourself, feel free to fork it & bookmark the external blog link ! + +Earlier today (March 31st, 2026) - Chaofan Shou on X discovered something that Anthropic probably didn't want the world to see: the **entire source code** of Claude Code, Anthropic's official AI coding CLI, was sitting in plain sight on the npm registry via a sourcemap file bundled into the published package. + +[![The tweet announcing the leak](https://raw.githubusercontent.com/kuberwastaken/claude-code/main/public/leak-tweet.png)](https://raw.githubusercontent.com/kuberwastaken/claude-code/main/public/leak-tweet.png) + +This repository is a backup of that leaked source, and this README is a full breakdown of what's in it, how the leak happened and most importantly, the things we now know that were never meant to be public. + +Let's get into it. + +## How Did This Even Happen? + +This is the part that honestly made me go "...really?" + +When you publish a JavaScript/TypeScript package to npm, the build toolchain often generates **source map files** (`.map` files). These files are a bridge between the minified/bundled production code and the original source, they exist so that when something crashes in production the stack trace can point you to the *actual* line of code in the *original* file, not some unintelligible line 1, column 48293 of a minified blob. + +But the fun part is **source maps contain the original source code**. The actual, literal, raw source code, embedded as strings inside a JSON file. + +The structure of a `.map` file looks something like this: + +```json +{ + "version": 3, + "sources": ["../src/main.tsx", "../src/tools/BashTool.ts", "..."], + "sourcesContent": ["// The ENTIRE original source code of each file", "..."], + "mappings": "AAAA,SAAS,OAAO..." +} +``` + +That `sourcesContent` array? That's everything. +Every file. Every comment. Every internal constant. Every system prompt. All of it, sitting right there in a JSON file that npm happily serves to anyone who runs `npm pack` or even just browses the package contents. + +This is not a novel attack vector. It's happened before and honestly it'll happen again. + +The mistake is almost always the same: someone forgets to add `*.map` to their `.npmignore` or doesn't configure their bundler to skip source map generation for production builds. With Bun's bundler (which Claude Code uses), source maps are generated by default unless you explicitly turn them off. + +[![Claude Code source files exposed in npm package](https://raw.githubusercontent.com/kuberwastaken/claude-code/main/public/claude-files.png)](https://raw.githubusercontent.com/kuberwastaken/claude-code/main/public/claude-files.png) + +The funniest part is, there's an entire system called ["Undercover Mode"](#undercover-mode--do-not-blow-your-cover) specifically designed to prevent Anthropic's internal information from leaking. + +They built a whole subsystem to stop their AI from accidentally revealing internal codenames in git commits... and then shipped the entire source in a `.map` file, likely by Claude. + +--- + +## What's Claude Under The Hood? + +If you've been living under a rock, Claude Code is Anthropic's official CLI tool for coding with Claude and the most popular AI coding agent. + +From the outside, it looks like a polished but relatively simple CLI. + +From the inside, It's a **785KB [`main.tsx`](https://github.com/kuberwastaken/claude-code/blob/main/main.tsx)** entry point, a custom React terminal renderer, 40+ tools, a multi-agent orchestration system, a background memory consolidation engine called "dream," and much more + +Enough yapping, here's some parts about the source code that are genuinely cool that I found after an afternoon deep dive: + +--- + +## BUDDY - A Tamagotchi Inside Your Terminal + +I am not making this up. + +Claude Code has a full **Tamagotchi-style companion pet system** called "Buddy." A **deterministic gacha system** with species rarity, shiny variants, procedurally generated stats, and a soul description written by Claude on first hatch like OpenClaw. + +The entire thing lives in [`buddy/`](https://github.com/kuberwastaken/claude-code/tree/main/buddy) and is gated behind the `BUDDY` compile-time feature flag. + +### The Gacha System + +Your buddy's species is determined by a **Mulberry32 PRNG**, a fast 32-bit pseudo-random number generator seeded from your `userId` hash with the salt `'friend-2026-401'`: + +```typescript +// Mulberry32 PRNG - deterministic, reproducible per-user +function mulberry32(seed: number): () => number { + return function() { + seed |= 0; seed = seed + 0x6D2B79F5 | 0; + var t = Math.imul(seed ^ seed >>> 15, 1 | seed); + t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t; + return ((t ^ t >>> 14) >>> 0) / 4294967296; + } +} +``` + +Same user always gets the same buddy. + +### 18 Species (Obfuscated in Code) + +The species names are hidden via `String.fromCharCode()` arrays - Anthropic clearly didn't want these showing up in string searches. Decoded, the full species list is: + +| Rarity | Species | +|--------|---------| +| **Common** (60%) | Pebblecrab, Dustbunny, Mossfrog, Twigling, Dewdrop, Puddlefish | +| **Uncommon** (25%) | Cloudferret, Gustowl, Bramblebear, Thornfox | +| **Rare** (10%) | Crystaldrake, Deepstag, Lavapup | +| **Epic** (4%) | Stormwyrm, Voidcat, Aetherling | +| **Legendary** (1%) | Cosmoshale, Nebulynx | + +On top of that, there's a **1% shiny chance** completely independent of rarity. So a Shiny Legendary Nebulynx has a **0.01%** chance of being rolled. Dang. + +### Stats, Eyes, Hats, and Soul + +Each buddy gets procedurally generated: +- **5 stats**: `DEBUGGING`, `PATIENCE`, `CHAOS`, `WISDOM`, `SNARK` (0-100 each) +- **6 possible eye styles** and **8 hat options** (some gated by rarity) +- **A "soul"** as mentioned, the personality generated by Claude on first hatch, written in character + +The sprites are rendered as **5-line-tall, 12-character-wide ASCII art** with multiple animation frames. There are idle animations, reaction animations, and they sit next to your input prompt. + +### The Lore + +The code references April 1-7, 2026 as a **teaser window** (so probably for easter?), with a full launch gated for May 2026. The companion has a system prompt that tells Claude: + +``` +A small {species} named {name} sits beside the user's input box and +occasionally comments in a speech bubble. You're not {name} - it's a +separate watcher. +``` + +So it's not just cosmetic - the buddy has its own personality and can respond when addressed by name. I really do hope they ship it. + +--- + +## KAIROS - "Always-On Claude" + +Inside [`assistant/`](https://github.com/kuberwastaken/claude-code/tree/main/assistant), there's an entire mode called **KAIROS** i.e. a persistent, always-running Claude assistant that doesn't wait for you to type. It watches, logs, and **proactively** acts on things it notices. + +This is gated behind the `PROACTIVE` / `KAIROS` compile-time feature flags and is completely absent from external builds. + +### How It Works + +KAIROS maintains **append-only daily log files** - it writes observations, decisions, and actions throughout the day. On a regular interval, it receives `` prompts that let it decide whether to act proactively or stay quiet. + +The system has a **15-second blocking budget**, any proactive action that would block the user's workflow for more than 15 seconds gets deferred. This is Claude trying to be helpful without being annoying. + +### Brief Mode + +When KAIROS is active, there's a special output mode called **Brief**, extremely concise responses designed for a persistent assistant that shouldn't flood your terminal. Think of it as the difference between a chatty friend and a professional assistant who only speaks when they have something valuable to say. + +### Exclusive Tools + +KAIROS gets tools that regular Claude Code doesn't have: + +| Tool | What It Does | +|------|-------------| +| **SendUserFile** | Push files directly to the user (notifications, summaries) | +| **PushNotification** | Send push notifications to the user's device | +| **SubscribePR** | Subscribe to and monitor pull request activity | + + --- + +## ULTRAPLAN - 30-Minute Remote Planning Sessions + +Here's one that's wild from an infrastructure perspective. + +**ULTRAPLAN** is a mode where Claude Code offloads a complex planning task to a **remote Cloud Container Runtime (CCR) session** running **Opus 4.6**, gives it up to **30 minutes** to think, and lets you approve the result from your browser. + +The basic flow: + +1. Claude Code identifies a task that needs deep planning +2. It spins up a remote CCR session via the `tengu_ultraplan_model` config +3. Your terminal shows a polling state - checking every **3 seconds** for the result +4. Meanwhile, a browser-based UI lets you watch the planning happen and approve/reject it +5. When approved, there's a special sentinel value `__ULTRAPLAN_TELEPORT_LOCAL__` that "teleports" the result back to your local terminal + +--- + +## The "Dream" System - Claude Literally Dreams + +Okay this is genuinely one of the coolest things in here. + +Claude Code has a system called **autoDream** ([`services/autoDream/`](https://github.com/kuberwastaken/claude-code/tree/main/services/autoDream)) - a background memory consolidation engine that runs as a **forked subagent**. The naming is very intentional. It's Claude... dreaming. + +This is extremely funny because [I had the same idea for LITMUS last week - OpenClaw subagents creatively having leisure time to find fun new papers](https://github.com/Kuberwastaken/litmus) + +### The Three-Gate Trigger + +The dream doesn't just run whenever it feels like it. It has a **three-gate trigger system**: + +1. **Time gate**: 24 hours since last dream +2. **Session gate**: At least 5 sessions since last dream +3. **Lock gate**: Acquires a consolidation lock (prevents concurrent dreams) + +All three must pass. This prevents both over-dreaming and under-dreaming. + +### The Four Phases + +When it runs, the dream follows four strict phases from the prompt in [`consolidationPrompt.ts`](https://github.com/kuberwastaken/claude-code/blob/main/services/autoDream/consolidationPrompt.ts): + +**Phase 1 - Orient**: `ls` the memory directory, read `MEMORY.md`, skim existing topic files to improve. + +**Phase 2 - Gather Recent Signal**: Find new information worth persisting. Sources in priority: daily logs → drifted memories → transcript search. + +**Phase 3 - Consolidate**: Write or update memory files. Convert relative dates to absolute. Delete contradicted facts. + +**Phase 4 - Prune and Index**: Keep `MEMORY.md` under 200 lines AND ~25KB. Remove stale pointers. Resolve contradictions. + +The prompt literally says: + +> *"You are performing a dream - a reflective pass over your memory files. Synthesize what you've learned recently into durable, well-organized memories so that future sessions can orient quickly."* + +The dream subagent gets **read-only bash** - it can look at your project but not modify anything. It's purely a memory consolidation pass. + +--- + +## Undercover Mode - "Do Not Blow Your Cover" + + +This one is fascinating from a corporate strategy perspective. + +Anthropic employees (identified by `USER_TYPE === 'ant'`) use Claude Code on public/open-source repositories. **Undercover Mode** ([`utils/undercover.ts`](https://github.com/kuberwastaken/claude-code/blob/main/utils/undercover.ts)) prevents the AI from accidentally revealing internal information in commits and PRs. + +When active, it injects this into the system prompt: + +``` +## UNDERCOVER MODE - CRITICAL + +You are operating UNDERCOVER in a PUBLIC/OPEN-SOURCE repository. Your commit +messages, PR titles, and PR bodies MUST NOT contain ANY Anthropic-internal +information. Do not blow your cover. + +NEVER include in commit messages or PR descriptions: +- Internal model codenames (animal names like Capybara, Tengu, etc.) +- Unreleased model version numbers (e.g., opus-4-7, sonnet-4-8) +- Internal repo or project names +- Internal tooling, Slack channels, or short links (e.g., go/cc, #claude-code-…) +- The phrase "Claude Code" or any mention that you are an AI +- Co-Authored-By lines or any other attribution +``` + +The activation logic: +- `CLAUDE_CODE_UNDERCOVER=1` forces it ON (even in internal repos) +- Otherwise it's **automatic**: active UNLESS the repo remote matches an internal allowlist +- There is **NO force-OFF** - *"if we're not confident we're in an internal repo, we stay undercover."* + +So this confirms: +1. **Anthropic employees actively use Claude Code to contribute to open-source** - and the AI is told to hide that it's an AI +2. **Internal model codenames are animal names** - Capybara, Tengu, etc. +3. **"Tengu"** appears hundreds of times as a prefix for feature flags and analytics events - it's almost certainly **Claude Code's internal project codename** + +All of this is dead-code-eliminated from external builds. But source maps don't care about dead code elimination. + +Makes me wonder how much are they internally causing havoc to open source repos + +--- + +## Multi-Agent Orchestration - "Coordinator Mode" + + +Claude Code has a full **multi-agent orchestration system** in [`coordinator/`](https://github.com/kuberwastaken/claude-code/tree/main/coordinator), activated via `CLAUDE_CODE_COORDINATOR_MODE=1`. + +When enabled, Claude Code transforms from a single agent into a **coordinator** that spawns, directs, and manages multiple worker agents in parallel. The coordinator system prompt in [`coordinatorMode.ts`](https://github.com/kuberwastaken/claude-code/blob/main/coordinator/coordinatorMode.ts) is a masterclass in multi-agent design: + +| Phase | Who | Purpose | +|-------|-----|---------| +| **Research** | Workers (parallel) | Investigate codebase, find files, understand problem | +| **Synthesis** | **Coordinator** | Read findings, understand the problem, craft specs | +| **Implementation** | Workers | Make targeted changes per spec, commit | +| **Verification** | Workers | Test changes work | + +The prompt **explicitly** teaches parallelism: + +> *"Parallelism is your superpower. Workers are async. Launch independent workers concurrently whenever possible - don't serialize work that can run simultaneously."* + +Workers communicate via `` XML messages. There's a shared **scratchpad directory** (gated behind `tengu_scratch`) for cross-worker durable knowledge sharing. And the prompt has this gem banning lazy delegation: + +> *Do NOT say "based on your findings" - read the actual findings and specify exactly what to do.* + +The system also includes **Agent Teams/Swarm** capabilities (`tengu_amber_flint` feature gate) with in-process teammates using `AsyncLocalStorage` for context isolation, process-based teammates using tmux/iTerm2 panes, team memory synchronization, and color assignments for visual distinction. + +--- + +## Fast Mode is Internally Called "Penguin Mode" + +Yeah, they really called it Penguin Mode. The API endpoint in [`utils/fastMode.ts`](https://github.com/kuberwastaken/claude-code/blob/main/utils/fastMode.ts) is literally: + +```typescript +const endpoint = `${getOauthConfig().BASE_API_URL}/api/claude_code_penguin_mode` +``` + +The config key is `penguinModeOrgEnabled`. The kill-switch is `tengu_penguins_off`. The analytics event on failure is `tengu_org_penguin_mode_fetch_failed`. Penguins all the way down. + +--- + +## The System Prompt Architecture + +The system prompt isn't a single string like most apps have - it's built from **modular, cached sections** composed at runtime in [`constants/`](https://github.com/kuberwastaken/claude-code/tree/main/constants). + +The architecture uses a `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` marker that splits the prompt into: +- **Static sections** - cacheable across organizations (things that don't change per user) +- **Dynamic sections** - user/session-specific content that breaks cache when changed + +There's a function called `DANGEROUS_uncachedSystemPromptSection()` for volatile sections you explicitly want to break cache. The naming convention alone tells you someone learned this lesson the hard way. + +### The Cyber Risk Instruction + +One particularly interesting section is the `CYBER_RISK_INSTRUCTION` in [`constants/cyberRiskInstruction.ts`](https://github.com/kuberwastaken/claude-code/blob/main/constants/cyberRiskInstruction.ts), which has a massive warning header: + +``` +IMPORTANT: DO NOT MODIFY THIS INSTRUCTION WITHOUT SAFEGUARDS TEAM REVIEW +This instruction is owned by the Safeguards team (David Forsythe, Kyla Guru) +``` + +So now we know exactly who at Anthropic owns the security boundary decisions and that it's governed by named individuals on a specific team. The instruction itself draws clear lines: authorized security testing is fine, destructive techniques and supply chain compromise are not. + +--- + +## The Full Tool Registry - 40+ Tools + +Claude Code's tool system lives in [`tools/`](https://github.com/kuberwastaken/claude-code/tree/main/tools).Here's the complete list: + +| Tool | What It Does | +|------|-------------| +| **AgentTool** | Spawn child agents/subagents | +| **BashTool** / **PowerShellTool** | Shell execution (with optional sandboxing) | +| **FileReadTool** / **FileEditTool** / **FileWriteTool** | File operations | +| **GlobTool** / **GrepTool** | File search (uses native `bfs`/`ugrep` when available) | +| **WebFetchTool** / **WebSearchTool** / **WebBrowserTool** | Web access | +| **NotebookEditTool** | Jupyter notebook editing | +| **SkillTool** | Invoke user-defined skills | +| **REPLTool** | Interactive VM shell (bare mode) | +| **LSPTool** | Language Server Protocol communication | +| **AskUserQuestionTool** | Prompt user for input | +| **EnterPlanModeTool** / **ExitPlanModeV2Tool** | Plan mode control | +| **BriefTool** | Upload/summarize files to claude.ai | +| **SendMessageTool** / **TeamCreateTool** / **TeamDeleteTool** | Agent swarm management | +| **TaskCreateTool** / **TaskGetTool** / **TaskListTool** / **TaskUpdateTool** / **TaskOutputTool** / **TaskStopTool** | Background task management | +| **TodoWriteTool** | Write todos (legacy) | +| **ListMcpResourcesTool** / **ReadMcpResourceTool** | MCP resource access | +| **SleepTool** | Async delays | +| **SnipTool** | History snippet extraction | +| **ToolSearchTool** | Tool discovery | +| **ListPeersTool** | List peer agents (UDS inbox) | +| **MonitorTool** | Monitor MCP servers | +| **EnterWorktreeTool** / **ExitWorktreeTool** | Git worktree management | +| **ScheduleCronTool** | Schedule cron jobs | +| **RemoteTriggerTool** | Trigger remote agents | +| **WorkflowTool** | Execute workflow scripts | +| **ConfigTool** | Modify settings (**internal only**) | +| **TungstenTool** | Advanced features (**internal only**) | +| **SendUserFile** / **PushNotification** / **SubscribePR** | KAIROS-exclusive tools | + +Tools are registered via `getAllBaseTools()` and filtered by feature gates, user type, environment flags, and permission deny rules. There's a **tool schema cache** ([`toolSchemaCache.ts`](https://github.com/kuberwastaken/claude-code/blob/main/tools/toolSchemaCache.ts)) that caches JSON schemas for prompt efficiency. + +--- + +## The Permission and Security System + +Claude Code's permission system in [`tools/permissions/`](https://github.com/kuberwastaken/claude-code/tree/main/tools/permissions) is far more sophisticated than "allow/deny": + +**Permission Modes**: `default` (interactive prompts), `auto` (ML-based auto-approval via transcript classifier), `bypass` (skip checks), `yolo` (deny all - ironically named) + +**Risk Classification**: Every tool action is classified as **LOW**, **MEDIUM**, or **HIGH** risk. There's a **YOLO classifier** - a fast ML-based permission decision system that decides automatically. + +**Protected Files**: `.gitconfig`, `.bashrc`, `.zshrc`, `.mcp.json`, `.claude.json` and others are guarded from automatic editing. + +**Path Traversal Prevention**: URL-encoded traversals, Unicode normalization attacks, backslash injection, case-insensitive path manipulation - all handled. + +**Permission Explainer**: A separate LLM call explains tool risks to the user before they approve. When Claude says "this command will modify your git config" - that explanation is itself generated by Claude. + +--- + +## Hidden Beta Headers and Unreleased API Features + +The [`constants/betas.ts`](https://github.com/kuberwastaken/claude-code/blob/main/constants/betas.ts) file reveals every beta feature Claude Code negotiates with the API: + +```typescript +'interleaved-thinking-2025-05-14' // Extended thinking +'context-1m-2025-08-07' // 1M token context window +'structured-outputs-2025-12-15' // Structured output format +'web-search-2025-03-05' // Web search +'advanced-tool-use-2025-11-20' // Advanced tool use +'effort-2025-11-24' // Effort level control +'task-budgets-2026-03-13' // Task budget management +'prompt-caching-scope-2026-01-05' // Prompt cache scoping +'fast-mode-2026-02-01' // Fast mode (Penguin) +'redact-thinking-2026-02-12' // Redacted thinking +'token-efficient-tools-2026-03-28' // Token-efficient tool schemas +'afk-mode-2026-01-31' // AFK mode +'cli-internal-2026-02-09' // Internal-only (ant) +'advisor-tool-2026-03-01' // Advisor tool +'summarize-connector-text-2026-03-13' // Connector text summarization +``` + +`redact-thinking`, `afk-mode`, and `advisor-tool` are also not released. + +--- + +## Feature Gating - Internal vs. External Builds + +This is one of the most architecturally interesting parts of the codebase. + +Claude Code uses **compile-time feature flags** via Bun's `feature()` function from `bun:bundle`. The bundler **constant-folds** these and **dead-code-eliminates** the gated branches from external builds. The complete list of known flags: + +| Flag | What It Gates | +|------|--------------| +| `PROACTIVE` / `KAIROS` | Always-on assistant mode | +| `KAIROS_BRIEF` | Brief command | +| `BRIDGE_MODE` | Remote control via claude.ai | +| `DAEMON` | Background daemon mode | +| `VOICE_MODE` | Voice input | +| `WORKFLOW_SCRIPTS` | Workflow automation | +| `COORDINATOR_MODE` | Multi-agent orchestration | +| `TRANSCRIPT_CLASSIFIER` | AFK mode (ML auto-approval) | +| `BUDDY` | Companion pet system | +| `NATIVE_CLIENT_ATTESTATION` | Client attestation | +| `HISTORY_SNIP` | History snipping | +| `EXPERIMENTAL_SKILL_SEARCH` | Skill discovery | + +Additionally, `USER_TYPE === 'ant'` gates Anthropic-internal features: staging API access (`claude-ai.staging.ant.dev`), internal beta headers, Undercover mode, the `/security-review` command, `ConfigTool`, `TungstenTool`, and debug prompt dumping to `~/.config/claude/dump-prompts/`. + +**GrowthBook** handles runtime feature gating with aggressively cached values. Feature flags prefixed with `tengu_` control everything from fast mode to memory consolidation. Many checks use `getFeatureValue_CACHED_MAY_BE_STALE()` to avoid blocking the main loop - stale data is considered acceptable for feature gates. + +--- + +## Other Notable Findings + +### The Upstream Proxy +The [`upstreamproxy/`](https://github.com/kuberwastaken/claude-code/tree/main/upstreamproxy) directory contains a container-aware proxy relay that uses **`prctl(PR_SET_DUMPABLE, 0)`** to prevent same-UID ptrace of heap memory. It reads session tokens from `/run/ccr/session_token` in CCR containers, downloads CA certificates, and starts a local CONNECT→WebSocket relay. Anthropic API, GitHub, npmjs.org, and pypi.org are explicitly excluded from proxying. + +### Bridge Mode +A JWT-authenticated bridge system in [`bridge/`](https://github.com/kuberwastaken/claude-code/tree/main/bridge) for integrating with claude.ai. Supports work modes: `'single-session'` | `'worktree'` | `'same-dir'`. Includes trusted device tokens for elevated security tiers. + +### Model Codenames in Migrations +The [`migrations/`](https://github.com/kuberwastaken/claude-code/tree/main/migrations) directory reveals the internal codename history: +- `migrateFennecToOpus` - **"Fennec"** (the fox) was an Opus codename +- `migrateSonnet1mToSonnet45` - Sonnet with 1M context became Sonnet 4.5 +- `migrateSonnet45ToSonnet46` - Sonnet 4.5 → Sonnet 4.6 +- `resetProToOpusDefault` - Pro users were reset to Opus at some point + +### Attribution Header +Every API request includes: +``` +x-anthropic-billing-header: cc_version={VERSION}.{FINGERPRINT}; + cc_entrypoint={ENTRYPOINT}; cch={ATTESTATION_PLACEHOLDER}; cc_workload={WORKLOAD}; +``` +The `NATIVE_CLIENT_ATTESTATION` feature lets Bun's HTTP stack overwrite the `cch=00000` placeholder with a computed hash - essentially a client authenticity check so Anthropic can verify the request came from a real Claude Code install. + +### Computer Use - "Chicago" +Claude Code includes a full Computer Use implementation, internally codenamed **"Chicago"**, built on `@ant/computer-use-mcp`. It provides screenshot capture, click/keyboard input, and coordinate transformation. Gated to Max/Pro subscriptions (with an ant bypass for internal users). + +### Pricing +For anyone wondering - all pricing in [`utils/modelCost.ts`](https://github.com/kuberwastaken/claude-code/blob/main/utils/modelCost.ts) matches [Anthropic's public pricing](https://docs.anthropic.com/en/docs/about-claude/models) exactly. Nothing newsworthy there. + +--- + +## Final Thoughts + +This is, without exaggeration, one of the most comprehensive looks we've ever gotten at how *the* production AI coding assistant works under the hood. Through the actual source code. + +A few things stand out: + +**The engineering is genuinely impressive.** This isn't a weekend project wrapped in a CLI. The multi-agent coordination, the dream system, the three-gate trigger architecture, the compile-time feature elimination - these are deeply considered systems. + +**There's a LOT more coming.** KAIROS (always-on Claude), ULTRAPLAN (30-minute remote planning), the Buddy companion, coordinator mode, agent swarms, workflow scripts - the codebase is significantly ahead of the public release. Most of these are feature-gated and invisible in external builds. + +**The internal culture shows.** Animal codenames (Tengu, Fennec, Capybara), playful feature names (Penguin Mode, Dream System), a Tamagotchi pet system with gacha mechanics. Some people at Anthropic is having fun. + +If there's one takeaway this has, it's that security is hard. But `.npmignore` is harder, apparently :P + +--- + +A writeup by [Kuber Mehta](https://kuber.studio/) \ No newline at end of file diff --git a/Task.ts b/Task.ts new file mode 100644 index 0000000..196caf3 --- /dev/null +++ b/Task.ts @@ -0,0 +1,125 @@ +import { randomBytes } from 'crypto' +import type { AppState } from './state/AppState.js' +import type { AgentId } from './types/ids.js' +import { getTaskOutputPath } from './utils/task/diskOutput.js' + +export type TaskType = + | 'local_bash' + | 'local_agent' + | 'remote_agent' + | 'in_process_teammate' + | 'local_workflow' + | 'monitor_mcp' + | 'dream' + +export type TaskStatus = + | 'pending' + | 'running' + | 'completed' + | 'failed' + | 'killed' + +/** + * True when a task is in a terminal state and will not transition further. + * Used to guard against injecting messages into dead teammates, evicting + * finished tasks from AppState, and orphan-cleanup paths. + */ +export function isTerminalTaskStatus(status: TaskStatus): boolean { + return status === 'completed' || status === 'failed' || status === 'killed' +} + +export type TaskHandle = { + taskId: string + cleanup?: () => void +} + +export type SetAppState = (f: (prev: AppState) => AppState) => void + +export type TaskContext = { + abortController: AbortController + getAppState: () => AppState + setAppState: SetAppState +} + +// Base fields shared by all task states +export type TaskStateBase = { + id: string + type: TaskType + status: TaskStatus + description: string + toolUseId?: string + startTime: number + endTime?: number + totalPausedMs?: number + outputFile: string + outputOffset: number + notified: boolean +} + +export type LocalShellSpawnInput = { + command: string + description: string + timeout?: number + toolUseId?: string + agentId?: AgentId + /** UI display variant: description-as-label, dialog title, status bar pill. */ + kind?: 'bash' | 'monitor' +} + +// What getTaskByType dispatches for: kill. spawn/render were never +// called polymorphically (removed in #22546). All six kill implementations +// use only setAppState — getAppState/abortController were dead weight. +export type Task = { + name: string + type: TaskType + kill(taskId: string, setAppState: SetAppState): Promise +} + +// Task ID prefixes +const TASK_ID_PREFIXES: Record = { + local_bash: 'b', // Keep as 'b' for backward compatibility + local_agent: 'a', + remote_agent: 'r', + in_process_teammate: 't', + local_workflow: 'w', + monitor_mcp: 'm', + dream: 'd', +} + +// Get task ID prefix +function getTaskIdPrefix(type: TaskType): string { + return TASK_ID_PREFIXES[type] ?? 'x' +} + +// Case-insensitive-safe alphabet (digits + lowercase) for task IDs. +// 36^8 ≈ 2.8 trillion combinations, sufficient to resist brute-force symlink attacks. +const TASK_ID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz' + +export function generateTaskId(type: TaskType): string { + const prefix = getTaskIdPrefix(type) + const bytes = randomBytes(8) + let id = prefix + for (let i = 0; i < 8; i++) { + id += TASK_ID_ALPHABET[bytes[i]! % TASK_ID_ALPHABET.length] + } + return id +} + +export function createTaskStateBase( + id: string, + type: TaskType, + description: string, + toolUseId?: string, +): TaskStateBase { + return { + id, + type, + status: 'pending', + description, + toolUseId, + startTime: Date.now(), + outputFile: getTaskOutputPath(id), + outputOffset: 0, + notified: false, + } +} diff --git a/Tool.ts b/Tool.ts new file mode 100644 index 0000000..205cac0 --- /dev/null +++ b/Tool.ts @@ -0,0 +1,792 @@ +import type { + ToolResultBlockParam, + ToolUseBlockParam, +} from '@anthropic-ai/sdk/resources/index.mjs' +import type { + ElicitRequestURLParams, + ElicitResult, +} from '@modelcontextprotocol/sdk/types.js' +import type { UUID } from 'crypto' +import type { z } from 'zod/v4' +import type { Command } from './commands.js' +import type { CanUseToolFn } from './hooks/useCanUseTool.js' +import type { ThinkingConfig } from './utils/thinking.js' + +export type ToolInputJSONSchema = { + [x: string]: unknown + type: 'object' + properties?: { + [x: string]: unknown + } +} + +import type { Notification } from './context/notifications.js' +import type { + MCPServerConnection, + ServerResource, +} from './services/mcp/types.js' +import type { + AgentDefinition, + AgentDefinitionsResult, +} from './tools/AgentTool/loadAgentsDir.js' +import type { + AssistantMessage, + AttachmentMessage, + Message, + ProgressMessage, + SystemLocalCommandMessage, + SystemMessage, + UserMessage, +} from './types/message.js' +// Import permission types from centralized location to break import cycles +// Import PermissionResult from centralized location to break import cycles +import type { + AdditionalWorkingDirectory, + PermissionMode, + PermissionResult, +} from './types/permissions.js' +// Import tool progress types from centralized location to break import cycles +import type { + AgentToolProgress, + BashProgress, + MCPProgress, + REPLToolProgress, + SkillToolProgress, + TaskOutputProgress, + ToolProgressData, + WebSearchProgress, +} from './types/tools.js' +import type { FileStateCache } from './utils/fileStateCache.js' +import type { DenialTrackingState } from './utils/permissions/denialTracking.js' +import type { SystemPrompt } from './utils/systemPromptType.js' +import type { ContentReplacementState } from './utils/toolResultStorage.js' + +// Re-export progress types for backwards compatibility +export type { + AgentToolProgress, + BashProgress, + MCPProgress, + REPLToolProgress, + SkillToolProgress, + TaskOutputProgress, + WebSearchProgress, +} + +import type { SpinnerMode } from './components/Spinner.js' +import type { QuerySource } from './constants/querySource.js' +import type { SDKStatus } from './entrypoints/agentSdkTypes.js' +import type { AppState } from './state/AppState.js' +import type { + HookProgress, + PromptRequest, + PromptResponse, +} from './types/hooks.js' +import type { AgentId } from './types/ids.js' +import type { DeepImmutable } from './types/utils.js' +import type { AttributionState } from './utils/commitAttribution.js' +import type { FileHistoryState } from './utils/fileHistory.js' +import type { Theme, ThemeName } from './utils/theme.js' + +export type QueryChainTracking = { + chainId: string + depth: number +} + +export type ValidationResult = + | { result: true } + | { + result: false + message: string + errorCode: number + } + +export type SetToolJSXFn = ( + args: { + jsx: React.ReactNode | null + shouldHidePromptInput: boolean + shouldContinueAnimation?: true + showSpinner?: boolean + isLocalJSXCommand?: boolean + isImmediate?: boolean + /** Set to true to clear a local JSX command (e.g., from its onDone callback) */ + clearLocalJSX?: boolean + } | null, +) => void + +// Import tool permission types from centralized location to break import cycles +import type { ToolPermissionRulesBySource } from './types/permissions.js' + +// Re-export for backwards compatibility +export type { ToolPermissionRulesBySource } + +// Apply DeepImmutable to the imported type +export type ToolPermissionContext = DeepImmutable<{ + mode: PermissionMode + additionalWorkingDirectories: Map + alwaysAllowRules: ToolPermissionRulesBySource + alwaysDenyRules: ToolPermissionRulesBySource + alwaysAskRules: ToolPermissionRulesBySource + isBypassPermissionsModeAvailable: boolean + isAutoModeAvailable?: boolean + strippedDangerousRules?: ToolPermissionRulesBySource + /** When true, permission prompts are auto-denied (e.g., background agents that can't show UI) */ + shouldAvoidPermissionPrompts?: boolean + /** When true, automated checks (classifier, hooks) are awaited before showing the permission dialog (coordinator workers) */ + awaitAutomatedChecksBeforeDialog?: boolean + /** Stores the permission mode before model-initiated plan mode entry, so it can be restored on exit */ + prePlanMode?: PermissionMode +}> + +export const getEmptyToolPermissionContext: () => ToolPermissionContext = + () => ({ + mode: 'default', + additionalWorkingDirectories: new Map(), + alwaysAllowRules: {}, + alwaysDenyRules: {}, + alwaysAskRules: {}, + isBypassPermissionsModeAvailable: false, + }) + +export type CompactProgressEvent = + | { + type: 'hooks_start' + hookType: 'pre_compact' | 'post_compact' | 'session_start' + } + | { type: 'compact_start' } + | { type: 'compact_end' } + +export type ToolUseContext = { + options: { + commands: Command[] + debug: boolean + mainLoopModel: string + tools: Tools + verbose: boolean + thinkingConfig: ThinkingConfig + mcpClients: MCPServerConnection[] + mcpResources: Record + isNonInteractiveSession: boolean + agentDefinitions: AgentDefinitionsResult + maxBudgetUsd?: number + /** Custom system prompt that replaces the default system prompt */ + customSystemPrompt?: string + /** Additional system prompt appended after the main system prompt */ + appendSystemPrompt?: string + /** Override querySource for analytics tracking */ + querySource?: QuerySource + /** Optional callback to get the latest tools (e.g., after MCP servers connect mid-query) */ + refreshTools?: () => Tools + } + abortController: AbortController + readFileState: FileStateCache + getAppState(): AppState + setAppState(f: (prev: AppState) => AppState): void + /** + * Always-shared setAppState for session-scoped infrastructure (background + * tasks, session hooks). Unlike setAppState, which is no-op for async agents + * (see createSubagentContext), this always reaches the root store so agents + * at any nesting depth can register/clean up infrastructure that outlives + * a single turn. Only set by createSubagentContext; main-thread contexts + * fall back to setAppState. + */ + setAppStateForTasks?: (f: (prev: AppState) => AppState) => void + /** + * Optional handler for URL elicitations triggered by tool call errors (-32042). + * In print/SDK mode, this delegates to structuredIO.handleElicitation. + * In REPL mode, this is undefined and the queue-based UI path is used. + */ + handleElicitation?: ( + serverName: string, + params: ElicitRequestURLParams, + signal: AbortSignal, + ) => Promise + setToolJSX?: SetToolJSXFn + addNotification?: (notif: Notification) => void + /** Append a UI-only system message to the REPL message list. Stripped at the + * normalizeMessagesForAPI boundary — the Exclude<> makes that type-enforced. */ + appendSystemMessage?: ( + msg: Exclude, + ) => void + /** Send an OS-level notification (iTerm2, Kitty, Ghostty, bell, etc.) */ + sendOSNotification?: (opts: { + message: string + notificationType: string + }) => void + nestedMemoryAttachmentTriggers?: Set + /** + * CLAUDE.md paths already injected as nested_memory attachments this + * session. Dedup for memoryFilesToAttachments — readFileState is an LRU + * that evicts entries in busy sessions, so its .has() check alone can + * re-inject the same CLAUDE.md dozens of times. + */ + loadedNestedMemoryPaths?: Set + dynamicSkillDirTriggers?: Set + /** Skill names surfaced via skill_discovery this session. Telemetry only (feeds was_discovered). */ + discoveredSkillNames?: Set + userModified?: boolean + setInProgressToolUseIDs: (f: (prev: Set) => Set) => void + /** Only wired in interactive (REPL) contexts; SDK/QueryEngine don't set this. */ + setHasInterruptibleToolInProgress?: (v: boolean) => void + setResponseLength: (f: (prev: number) => number) => void + /** Ant-only: push a new API metrics entry for OTPS tracking. + * Called by subagent streaming when a new API request starts. */ + pushApiMetricsEntry?: (ttftMs: number) => void + setStreamMode?: (mode: SpinnerMode) => void + onCompactProgress?: (event: CompactProgressEvent) => void + setSDKStatus?: (status: SDKStatus) => void + openMessageSelector?: () => void + updateFileHistoryState: ( + updater: (prev: FileHistoryState) => FileHistoryState, + ) => void + updateAttributionState: ( + updater: (prev: AttributionState) => AttributionState, + ) => void + setConversationId?: (id: UUID) => void + agentId?: AgentId // Only set for subagents; use getSessionId() for session ID. Hooks use this to distinguish subagent calls. + agentType?: string // Subagent type name. For the main thread's --agent type, hooks fall back to getMainThreadAgentType(). + /** When true, canUseTool must always be called even when hooks auto-approve. + * Used by speculation for overlay file path rewriting. */ + requireCanUseTool?: boolean + messages: Message[] + fileReadingLimits?: { + maxTokens?: number + maxSizeBytes?: number + } + globLimits?: { + maxResults?: number + } + toolDecisions?: Map< + string, + { + source: string + decision: 'accept' | 'reject' + timestamp: number + } + > + queryTracking?: QueryChainTracking + /** Callback factory for requesting interactive prompts from the user. + * Returns a prompt callback bound to the given source name. + * Only available in interactive (REPL) contexts. */ + requestPrompt?: ( + sourceName: string, + toolInputSummary?: string | null, + ) => (request: PromptRequest) => Promise + toolUseId?: string + criticalSystemReminder_EXPERIMENTAL?: string + /** When true, preserve toolUseResult on messages even for subagents. + * Used by in-process teammates whose transcripts are viewable by the user. */ + preserveToolUseResults?: boolean + /** Local denial tracking state for async subagents whose setAppState is a + * no-op. Without this, the denial counter never accumulates and the + * fallback-to-prompting threshold is never reached. Mutable — the + * permissions code updates it in place. */ + localDenialTracking?: DenialTrackingState + /** + * Per-conversation-thread content replacement state for the tool result + * budget. When present, query.ts applies the aggregate tool result budget. + * Main thread: REPL provisions once (never resets — stale UUID keys + * are inert). Subagents: createSubagentContext clones the parent's state + * by default (cache-sharing forks need identical decisions), or + * resumeAgentBackground threads one reconstructed from sidechain records. + */ + contentReplacementState?: ContentReplacementState + /** + * Parent's rendered system prompt bytes, frozen at turn start. + * Used by fork subagents to share the parent's prompt cache — re-calling + * getSystemPrompt() at fork-spawn time can diverge (GrowthBook cold→warm) + * and bust the cache. See forkSubagent.ts. + */ + renderedSystemPrompt?: SystemPrompt +} + +// Re-export ToolProgressData from centralized location +export type { ToolProgressData } + +export type Progress = ToolProgressData | HookProgress + +export type ToolProgress

= { + toolUseID: string + data: P +} + +export function filterToolProgressMessages( + progressMessagesForMessage: ProgressMessage[], +): ProgressMessage[] { + return progressMessagesForMessage.filter( + (msg): msg is ProgressMessage => + msg.data?.type !== 'hook_progress', + ) +} + +export type ToolResult = { + data: T + newMessages?: ( + | UserMessage + | AssistantMessage + | AttachmentMessage + | SystemMessage + )[] + // contextModifier is only honored for tools that aren't concurrency safe. + contextModifier?: (context: ToolUseContext) => ToolUseContext + /** MCP protocol metadata (structuredContent, _meta) to pass through to SDK consumers */ + mcpMeta?: { + _meta?: Record + structuredContent?: Record + } +} + +export type ToolCallProgress

= ( + progress: ToolProgress

, +) => void + +// Type for any schema that outputs an object with string keys +export type AnyObject = z.ZodType<{ [key: string]: unknown }> + +/** + * Checks if a tool matches the given name (primary name or alias). + */ +export function toolMatchesName( + tool: { name: string; aliases?: string[] }, + name: string, +): boolean { + return tool.name === name || (tool.aliases?.includes(name) ?? false) +} + +/** + * Finds a tool by name or alias from a list of tools. + */ +export function findToolByName(tools: Tools, name: string): Tool | undefined { + return tools.find(t => toolMatchesName(t, name)) +} + +export type Tool< + Input extends AnyObject = AnyObject, + Output = unknown, + P extends ToolProgressData = ToolProgressData, +> = { + /** + * Optional aliases for backwards compatibility when a tool is renamed. + * The tool can be looked up by any of these names in addition to its primary name. + */ + aliases?: string[] + /** + * One-line capability phrase used by ToolSearch for keyword matching. + * Helps the model find this tool via keyword search when it's deferred. + * 3–10 words, no trailing period. + * Prefer terms not already in the tool name (e.g. 'jupyter' for NotebookEdit). + */ + searchHint?: string + call( + args: z.infer, + context: ToolUseContext, + canUseTool: CanUseToolFn, + parentMessage: AssistantMessage, + onProgress?: ToolCallProgress

, + ): Promise> + description( + input: z.infer, + options: { + isNonInteractiveSession: boolean + toolPermissionContext: ToolPermissionContext + tools: Tools + }, + ): Promise + readonly inputSchema: Input + // Type for MCP tools that can specify their input schema directly in JSON Schema format + // rather than converting from Zod schema + readonly inputJSONSchema?: ToolInputJSONSchema + // Optional because TungstenTool doesn't define this. TODO: Make it required. + // When we do that, we can also go through and make this a bit more type-safe. + outputSchema?: z.ZodType + inputsEquivalent?(a: z.infer, b: z.infer): boolean + isConcurrencySafe(input: z.infer): boolean + isEnabled(): boolean + isReadOnly(input: z.infer): boolean + /** Defaults to false. Only set when the tool performs irreversible operations (delete, overwrite, send). */ + isDestructive?(input: z.infer): boolean + /** + * What should happen when the user submits a new message while this tool + * is running. + * + * - `'cancel'` — stop the tool and discard its result + * - `'block'` — keep running; the new message waits + * + * Defaults to `'block'` when not implemented. + */ + interruptBehavior?(): 'cancel' | 'block' + /** + * Returns information about whether this tool use is a search or read operation + * that should be collapsed into a condensed display in the UI. Examples include + * file searching (Grep, Glob), file reading (Read), and bash commands like find, + * grep, wc, etc. + * + * Returns an object indicating whether the operation is a search or read operation: + * - `isSearch: true` for search operations (grep, find, glob patterns) + * - `isRead: true` for read operations (cat, head, tail, file read) + * - `isList: true` for directory-listing operations (ls, tree, du) + * - All can be false if the operation shouldn't be collapsed + */ + isSearchOrReadCommand?(input: z.infer): { + isSearch: boolean + isRead: boolean + isList?: boolean + } + isOpenWorld?(input: z.infer): boolean + requiresUserInteraction?(): boolean + isMcp?: boolean + isLsp?: boolean + /** + * When true, this tool is deferred (sent with defer_loading: true) and requires + * ToolSearch to be used before it can be called. + */ + readonly shouldDefer?: boolean + /** + * When true, this tool is never deferred — its full schema appears in the + * initial prompt even when ToolSearch is enabled. For MCP tools, set via + * `_meta['anthropic/alwaysLoad']`. Use for tools the model must see on + * turn 1 without a ToolSearch round-trip. + */ + readonly alwaysLoad?: boolean + /** + * For MCP tools: the server and tool names as received from the MCP server (unnormalized). + * Present on all MCP tools regardless of whether `name` is prefixed (mcp__server__tool) + * or unprefixed (CLAUDE_AGENT_SDK_MCP_NO_PREFIX mode). + */ + mcpInfo?: { serverName: string; toolName: string } + readonly name: string + /** + * Maximum size in characters for tool result before it gets persisted to disk. + * When exceeded, the result is saved to a file and Claude receives a preview + * with the file path instead of the full content. + * + * Set to Infinity for tools whose output must never be persisted (e.g. Read, + * where persisting creates a circular Read→file→Read loop and the tool + * already self-bounds via its own limits). + */ + maxResultSizeChars: number + /** + * When true, enables strict mode for this tool, which causes the API to + * more strictly adhere to tool instructions and parameter schemas. + * Only applied when the tengu_tool_pear is enabled. + */ + readonly strict?: boolean + + /** + * Called on copies of tool_use input before observers see it (SDK stream, + * transcript, canUseTool, PreToolUse/PostToolUse hooks). Mutate in place + * to add legacy/derived fields. Must be idempotent. The original API-bound + * input is never mutated (preserves prompt cache). Not re-applied when a + * hook/permission returns a fresh updatedInput — those own their shape. + */ + backfillObservableInput?(input: Record): void + + /** + * Determines if this tool is allowed to run with this input in the current context. + * It informs the model of why the tool use failed, and does not directly display any UI. + * @param input + * @param context + */ + validateInput?( + input: z.infer, + context: ToolUseContext, + ): Promise + + /** + * Determines if the user is asked for permission. Only called after validateInput() passes. + * General permission logic is in permissions.ts. This method contains tool-specific logic. + * @param input + * @param context + */ + checkPermissions( + input: z.infer, + context: ToolUseContext, + ): Promise + + // Optional method for tools that operate on a file path + getPath?(input: z.infer): string + + /** + * Prepare a matcher for hook `if` conditions (permission-rule patterns like + * "git *" from "Bash(git *)"). Called once per hook-input pair; any + * expensive parsing happens here. Returns a closure that is called per + * hook pattern. If not implemented, only tool-name-level matching works. + */ + preparePermissionMatcher?( + input: z.infer, + ): Promise<(pattern: string) => boolean> + + prompt(options: { + getToolPermissionContext: () => Promise + tools: Tools + agents: AgentDefinition[] + allowedAgentTypes?: string[] + }): Promise + userFacingName(input: Partial> | undefined): string + userFacingNameBackgroundColor?( + input: Partial> | undefined, + ): keyof Theme | undefined + /** + * Transparent wrappers (e.g. REPL) delegate all rendering to their progress + * handler, which emits native-looking blocks for each inner tool call. + * The wrapper itself shows nothing. + */ + isTransparentWrapper?(): boolean + /** + * Returns a short string summary of this tool use for display in compact views. + * @param input The tool input + * @returns A short string summary, or null to not display + */ + getToolUseSummary?(input: Partial> | undefined): string | null + /** + * Returns a human-readable present-tense activity description for spinner display. + * Example: "Reading src/foo.ts", "Running bun test", "Searching for pattern" + * @param input The tool input + * @returns Activity description string, or null to fall back to tool name + */ + getActivityDescription?( + input: Partial> | undefined, + ): string | null + /** + * Returns a compact representation of this tool use for the auto-mode + * security classifier. Examples: `ls -la` for Bash, `/tmp/x: new content` + * for Edit. Return '' to skip this tool in the classifier transcript + * (e.g. tools with no security relevance). May return an object to avoid + * double-encoding when the caller JSON-wraps the value. + */ + toAutoClassifierInput(input: z.infer): unknown + mapToolResultToToolResultBlockParam( + content: Output, + toolUseID: string, + ): ToolResultBlockParam + /** + * Optional. When omitted, the tool result renders nothing (same as returning + * null). Omit for tools whose results are surfaced elsewhere (e.g., TodoWrite + * updates the todo panel, not the transcript). + */ + renderToolResultMessage?( + content: Output, + progressMessagesForMessage: ProgressMessage

[], + options: { + style?: 'condensed' + theme: ThemeName + tools: Tools + verbose: boolean + isTranscriptMode?: boolean + isBriefOnly?: boolean + /** Original tool_use input, when available. Useful for compact result + * summaries that reference what was requested (e.g. "Sent to #foo"). */ + input?: unknown + }, + ): React.ReactNode + /** + * Flattened text of what renderToolResultMessage shows IN TRANSCRIPT + * MODE (verbose=true, isTranscriptMode=true). For transcript search + * indexing: the index counts occurrences in this string, the highlight + * overlay scans the actual screen buffer. For count ≡ highlight, this + * must return the text that ends up visible — not the model-facing + * serialization from mapToolResultToToolResultBlockParam (which adds + * system-reminders, persisted-output wrappers). + * + * Chrome can be skipped (under-count is fine). "Found 3 files in 12ms" + * isn't worth indexing. Phantoms are not fine — text that's claimed + * here but doesn't render is a count≠highlight bug. + * + * Optional: omitted → field-name heuristic in transcriptSearch.ts. + * Drift caught by test/utils/transcriptSearch.renderFidelity.test.tsx + * which renders sample outputs and flags text that's indexed-but-not- + * rendered (phantom) or rendered-but-not-indexed (under-count warning). + */ + extractSearchText?(out: Output): string + /** + * Render the tool use message. Note that `input` is partial because we render + * the message as soon as possible, possibly before tool parameters have fully + * streamed in. + */ + renderToolUseMessage( + input: Partial>, + options: { theme: ThemeName; verbose: boolean; commands?: Command[] }, + ): React.ReactNode + /** + * Returns true when the non-verbose rendering of this output is truncated + * (i.e., clicking to expand would reveal more content). Gates + * click-to-expand in fullscreen — only messages where verbose actually + * shows more get a hover/click affordance. Unset means never truncated. + */ + isResultTruncated?(output: Output): boolean + /** + * Renders an optional tag to display after the tool use message. + * Used for additional metadata like timeout, model, resume ID, etc. + * Returns null to not display anything. + */ + renderToolUseTag?(input: Partial>): React.ReactNode + /** + * Optional. When omitted, no progress UI is shown while the tool runs. + */ + renderToolUseProgressMessage?( + progressMessagesForMessage: ProgressMessage

[], + options: { + tools: Tools + verbose: boolean + terminalSize?: { columns: number; rows: number } + inProgressToolCallCount?: number + isTranscriptMode?: boolean + }, + ): React.ReactNode + renderToolUseQueuedMessage?(): React.ReactNode + /** + * Optional. When omitted, falls back to . + * Only define this for tools that need custom rejection UI (e.g., file edits + * that show the rejected diff). + */ + renderToolUseRejectedMessage?( + input: z.infer, + options: { + columns: number + messages: Message[] + style?: 'condensed' + theme: ThemeName + tools: Tools + verbose: boolean + progressMessagesForMessage: ProgressMessage

[] + isTranscriptMode?: boolean + }, + ): React.ReactNode + /** + * Optional. When omitted, falls back to . + * Only define this for tools that need custom error UI (e.g., search tools + * that show "File not found" instead of the raw error). + */ + renderToolUseErrorMessage?( + result: ToolResultBlockParam['content'], + options: { + progressMessagesForMessage: ProgressMessage

[] + tools: Tools + verbose: boolean + isTranscriptMode?: boolean + }, + ): React.ReactNode + + /** + * Renders multiple parallel instances of this tool as a group. + * @returns React node to render, or null to fall back to individual rendering + */ + /** + * Renders multiple tool uses as a group (non-verbose mode only). + * In verbose mode, individual tool uses render at their original positions. + * @returns React node to render, or null to fall back to individual rendering + */ + renderGroupedToolUse?( + toolUses: Array<{ + param: ToolUseBlockParam + isResolved: boolean + isError: boolean + isInProgress: boolean + progressMessages: ProgressMessage

[] + result?: { + param: ToolResultBlockParam + output: unknown + } + }>, + options: { + shouldAnimate: boolean + tools: Tools + }, + ): React.ReactNode | null +} + +/** + * A collection of tools. Use this type instead of `Tool[]` to make it easier + * to track where tool sets are assembled, passed, and filtered across the codebase. + */ +export type Tools = readonly Tool[] + +/** + * Methods that `buildTool` supplies a default for. A `ToolDef` may omit these; + * the resulting `Tool` always has them. + */ +type DefaultableToolKeys = + | 'isEnabled' + | 'isConcurrencySafe' + | 'isReadOnly' + | 'isDestructive' + | 'checkPermissions' + | 'toAutoClassifierInput' + | 'userFacingName' + +/** + * Tool definition accepted by `buildTool`. Same shape as `Tool` but with the + * defaultable methods optional — `buildTool` fills them in so callers always + * see a complete `Tool`. + */ +export type ToolDef< + Input extends AnyObject = AnyObject, + Output = unknown, + P extends ToolProgressData = ToolProgressData, +> = Omit, DefaultableToolKeys> & + Partial, DefaultableToolKeys>> + +/** + * Type-level spread mirroring `{ ...TOOL_DEFAULTS, ...def }`. For each + * defaultable key: if D provides it (required), D's type wins; if D omits + * it or has it optional (inherited from Partial<> in the constraint), the + * default fills in. All other keys come from D verbatim — preserving arity, + * optional presence, and literal types exactly as `satisfies Tool` did. + */ +type BuiltTool = Omit & { + [K in DefaultableToolKeys]-?: K extends keyof D + ? undefined extends D[K] + ? ToolDefaults[K] + : D[K] + : ToolDefaults[K] +} + +/** + * Build a complete `Tool` from a partial definition, filling in safe defaults + * for the commonly-stubbed methods. All tool exports should go through this so + * that defaults live in one place and callers never need `?.() ?? default`. + * + * Defaults (fail-closed where it matters): + * - `isEnabled` → `true` + * - `isConcurrencySafe` → `false` (assume not safe) + * - `isReadOnly` → `false` (assume writes) + * - `isDestructive` → `false` + * - `checkPermissions` → `{ behavior: 'allow', updatedInput }` (defer to general permission system) + * - `toAutoClassifierInput` → `''` (skip classifier — security-relevant tools must override) + * - `userFacingName` → `name` + */ +const TOOL_DEFAULTS = { + isEnabled: () => true, + isConcurrencySafe: (_input?: unknown) => false, + isReadOnly: (_input?: unknown) => false, + isDestructive: (_input?: unknown) => false, + checkPermissions: ( + input: { [key: string]: unknown }, + _ctx?: ToolUseContext, + ): Promise => + Promise.resolve({ behavior: 'allow', updatedInput: input }), + toAutoClassifierInput: (_input?: unknown) => '', + userFacingName: (_input?: unknown) => '', +} + +// The defaults type is the ACTUAL shape of TOOL_DEFAULTS (optional params so +// both 0-arg and full-arg call sites type-check — stubs varied in arity and +// tests relied on that), not the interface's strict signatures. +type ToolDefaults = typeof TOOL_DEFAULTS + +// D infers the concrete object-literal type from the call site. The +// constraint provides contextual typing for method parameters; `any` in +// constraint position is structural and never leaks into the return type. +// BuiltTool mirrors runtime `{...TOOL_DEFAULTS, ...def}` at the type level. +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type AnyToolDef = ToolDef + +export function buildTool(def: D): BuiltTool { + // The runtime spread is straightforward; the `as` bridges the gap between + // the structural-any constraint and the precise BuiltTool return. The + // type semantics are proven by the 0-error typecheck across all 60+ tools. + return { + ...TOOL_DEFAULTS, + userFacingName: () => def.name, + ...def, + } as BuiltTool +} diff --git a/assistant/sessionHistory.ts b/assistant/sessionHistory.ts new file mode 100644 index 0000000..9e1ddc5 --- /dev/null +++ b/assistant/sessionHistory.ts @@ -0,0 +1,87 @@ +import axios from 'axios' +import { getOauthConfig } from '../constants/oauth.js' +import type { SDKMessage } from '../entrypoints/agentSdkTypes.js' +import { logForDebugging } from '../utils/debug.js' +import { getOAuthHeaders, prepareApiRequest } from '../utils/teleport/api.js' + +export const HISTORY_PAGE_SIZE = 100 + +export type HistoryPage = { + /** Chronological order within the page. */ + events: SDKMessage[] + /** Oldest event ID in this page → before_id cursor for next-older page. */ + firstId: string | null + /** true = older events exist. */ + hasMore: boolean +} + +type SessionEventsResponse = { + data: SDKMessage[] + has_more: boolean + first_id: string | null + last_id: string | null +} + +export type HistoryAuthCtx = { + baseUrl: string + headers: Record +} + +/** Prepare auth + headers + base URL once, reuse across pages. */ +export async function createHistoryAuthCtx( + sessionId: string, +): Promise { + const { accessToken, orgUUID } = await prepareApiRequest() + return { + baseUrl: `${getOauthConfig().BASE_API_URL}/v1/sessions/${sessionId}/events`, + headers: { + ...getOAuthHeaders(accessToken), + 'anthropic-beta': 'ccr-byoc-2025-07-29', + 'x-organization-uuid': orgUUID, + }, + } +} + +async function fetchPage( + ctx: HistoryAuthCtx, + params: Record, + label: string, +): Promise { + const resp = await axios + .get(ctx.baseUrl, { + headers: ctx.headers, + params, + timeout: 15000, + validateStatus: () => true, + }) + .catch(() => null) + if (!resp || resp.status !== 200) { + logForDebugging(`[${label}] HTTP ${resp?.status ?? 'error'}`) + return null + } + return { + events: Array.isArray(resp.data.data) ? resp.data.data : [], + firstId: resp.data.first_id, + hasMore: resp.data.has_more, + } +} + +/** + * Newest page: last `limit` events, chronological, via anchor_to_latest. + * has_more=true means older events exist. + */ +export async function fetchLatestEvents( + ctx: HistoryAuthCtx, + limit = HISTORY_PAGE_SIZE, +): Promise { + return fetchPage(ctx, { limit, anchor_to_latest: true }, 'fetchLatestEvents') +} + +/** Older page: events immediately before `beforeId` cursor. */ +export async function fetchOlderEvents( + ctx: HistoryAuthCtx, + beforeId: string, + limit = HISTORY_PAGE_SIZE, +): Promise { + return fetchPage(ctx, { limit, before_id: beforeId }, 'fetchOlderEvents') +} diff --git a/bootstrap/state.ts b/bootstrap/state.ts new file mode 100644 index 0000000..d7199e5 --- /dev/null +++ b/bootstrap/state.ts @@ -0,0 +1,1758 @@ +import type { BetaMessageStreamParams } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs' +import type { Attributes, Meter, MetricOptions } from '@opentelemetry/api' +import type { logs } from '@opentelemetry/api-logs' +import type { LoggerProvider } from '@opentelemetry/sdk-logs' +import type { MeterProvider } from '@opentelemetry/sdk-metrics' +import type { BasicTracerProvider } from '@opentelemetry/sdk-trace-base' +import { realpathSync } from 'fs' +import sumBy from 'lodash-es/sumBy.js' +import { cwd } from 'process' +import type { HookEvent, ModelUsage } from 'src/entrypoints/agentSdkTypes.js' +import type { AgentColorName } from 'src/tools/AgentTool/agentColorManager.js' +import type { HookCallbackMatcher } from 'src/types/hooks.js' +// Indirection for browser-sdk build (package.json "browser" field swaps +// crypto.ts for crypto.browser.ts). Pure leaf re-export of node:crypto — +// zero circular-dep risk. Path-alias import bypasses bootstrap-isolation +// (rule only checks ./ and / prefixes); explicit disable documents intent. +// eslint-disable-next-line custom-rules/bootstrap-isolation +import { randomUUID } from 'src/utils/crypto.js' +import type { ModelSetting } from 'src/utils/model/model.js' +import type { ModelStrings } from 'src/utils/model/modelStrings.js' +import type { SettingSource } from 'src/utils/settings/constants.js' +import { resetSettingsCache } from 'src/utils/settings/settingsCache.js' +import type { PluginHookMatcher } from 'src/utils/settings/types.js' +import { createSignal } from 'src/utils/signal.js' + +// Union type for registered hooks - can be SDK callbacks or native plugin hooks +type RegisteredHookMatcher = HookCallbackMatcher | PluginHookMatcher + +import type { SessionId } from 'src/types/ids.js' + +// DO NOT ADD MORE STATE HERE - BE JUDICIOUS WITH GLOBAL STATE + +// dev: true on entries that came via --dangerously-load-development-channels. +// The allowlist gate checks this per-entry (not the session-wide +// hasDevChannels bit) so passing both flags doesn't let the dev dialog's +// acceptance leak allowlist-bypass to the --channels entries. +export type ChannelEntry = + | { kind: 'plugin'; name: string; marketplace: string; dev?: boolean } + | { kind: 'server'; name: string; dev?: boolean } + +export type AttributedCounter = { + add(value: number, additionalAttributes?: Attributes): void +} + +type State = { + originalCwd: string + // Stable project root - set once at startup (including by --worktree flag), + // never updated by mid-session EnterWorktreeTool. + // Use for project identity (history, skills, sessions) not file operations. + projectRoot: string + totalCostUSD: number + totalAPIDuration: number + totalAPIDurationWithoutRetries: number + totalToolDuration: number + turnHookDurationMs: number + turnToolDurationMs: number + turnClassifierDurationMs: number + turnToolCount: number + turnHookCount: number + turnClassifierCount: number + startTime: number + lastInteractionTime: number + totalLinesAdded: number + totalLinesRemoved: number + hasUnknownModelCost: boolean + cwd: string + modelUsage: { [modelName: string]: ModelUsage } + mainLoopModelOverride: ModelSetting | undefined + initialMainLoopModel: ModelSetting + modelStrings: ModelStrings | null + isInteractive: boolean + kairosActive: boolean + // When true, ensureToolResultPairing throws on mismatch instead of + // repairing with synthetic placeholders. HFI opts in at startup so + // trajectories fail fast rather than conditioning the model on fake + // tool_results. + strictToolResultPairing: boolean + sdkAgentProgressSummariesEnabled: boolean + userMsgOptIn: boolean + clientType: string + sessionSource: string | undefined + questionPreviewFormat: 'markdown' | 'html' | undefined + flagSettingsPath: string | undefined + flagSettingsInline: Record | null + allowedSettingSources: SettingSource[] + sessionIngressToken: string | null | undefined + oauthTokenFromFd: string | null | undefined + apiKeyFromFd: string | null | undefined + // Telemetry state + meter: Meter | null + sessionCounter: AttributedCounter | null + locCounter: AttributedCounter | null + prCounter: AttributedCounter | null + commitCounter: AttributedCounter | null + costCounter: AttributedCounter | null + tokenCounter: AttributedCounter | null + codeEditToolDecisionCounter: AttributedCounter | null + activeTimeCounter: AttributedCounter | null + statsStore: { observe(name: string, value: number): void } | null + sessionId: SessionId + // Parent session ID for tracking session lineage (e.g., plan mode -> implementation) + parentSessionId: SessionId | undefined + // Logger state + loggerProvider: LoggerProvider | null + eventLogger: ReturnType | null + // Meter provider state + meterProvider: MeterProvider | null + // Tracer provider state + tracerProvider: BasicTracerProvider | null + // Agent color state + agentColorMap: Map + agentColorIndex: number + // Last API request for bug reports + lastAPIRequest: Omit | null + // Messages from the last API request (ant-only; reference, not clone). + // Captures the exact post-compaction, CLAUDE.md-injected message set sent + // to the API so /share's serialized_conversation.json reflects reality. + lastAPIRequestMessages: BetaMessageStreamParams['messages'] | null + // Last auto-mode classifier request(s) for /share transcript + lastClassifierRequests: unknown[] | null + // CLAUDE.md content cached by context.ts for the auto-mode classifier. + // Breaks the yoloClassifier → claudemd → filesystem → permissions cycle. + cachedClaudeMdContent: string | null + // In-memory error log for recent errors + inMemoryErrorLog: Array<{ error: string; timestamp: string }> + // Session-only plugins from --plugin-dir flag + inlinePlugins: Array + // Explicit --chrome / --no-chrome flag value (undefined = not set on CLI) + chromeFlagOverride: boolean | undefined + // Use cowork_plugins directory instead of plugins (--cowork flag or env var) + useCoworkPlugins: boolean + // Session-only bypass permissions mode flag (not persisted) + sessionBypassPermissionsMode: boolean + // Session-only flag gating the .claude/scheduled_tasks.json watcher + // (useScheduledTasks). Set by cronScheduler.start() when the JSON has + // entries, or by CronCreateTool. Not persisted. + scheduledTasksEnabled: boolean + // Session-only cron tasks created via CronCreate with durable: false. + // Fire on schedule like file-backed tasks but are never written to + // .claude/scheduled_tasks.json — they die with the process. Typed via + // SessionCronTask below (not importing from cronTasks.ts keeps + // bootstrap a leaf of the import DAG). + sessionCronTasks: SessionCronTask[] + // Teams created this session via TeamCreate. cleanupSessionTeams() + // removes these on gracefulShutdown so subagent-created teams don't + // persist on disk forever (gh-32730). TeamDelete removes entries to + // avoid double-cleanup. Lives here (not teamHelpers.ts) so + // resetStateForTests() clears it between tests. + sessionCreatedTeams: Set + // Session-only trust flag for home directory (not persisted to disk) + // When running from home dir, trust dialog is shown but not saved to disk. + // This flag allows features requiring trust to work during the session. + sessionTrustAccepted: boolean + // Session-only flag to disable session persistence to disk + sessionPersistenceDisabled: boolean + // Track if user has exited plan mode in this session (for re-entry guidance) + hasExitedPlanMode: boolean + // Track if we need to show the plan mode exit attachment (one-time notification) + needsPlanModeExitAttachment: boolean + // Track if we need to show the auto mode exit attachment (one-time notification) + needsAutoModeExitAttachment: boolean + // Track if LSP plugin recommendation has been shown this session (only show once) + lspRecommendationShownThisSession: boolean + // SDK init event state - jsonSchema for structured output + initJsonSchema: Record | null + // Registered hooks - SDK callbacks and plugin native hooks + registeredHooks: Partial> | null + // Cache for plan slugs: sessionId -> wordSlug + planSlugCache: Map + // Track teleported session for reliability logging + teleportedSessionInfo: { + isTeleported: boolean + hasLoggedFirstMessage: boolean + sessionId: string | null + } | null + // Track invoked skills for preservation across compaction + // Keys are composite: `${agentId ?? ''}:${skillName}` to prevent cross-agent overwrites + invokedSkills: Map< + string, + { + skillName: string + skillPath: string + content: string + invokedAt: number + agentId: string | null + } + > + // Track slow operations for dev bar display (ant-only) + slowOperations: Array<{ + operation: string + durationMs: number + timestamp: number + }> + // SDK-provided betas (e.g., context-1m-2025-08-07) + sdkBetas: string[] | undefined + // Main thread agent type (from --agent flag or settings) + mainThreadAgentType: string | undefined + // Remote mode (--remote flag) + isRemoteMode: boolean + // Direct connect server URL (for display in header) + directConnectServerUrl: string | undefined + // System prompt section cache state + systemPromptSectionCache: Map + // Last date emitted to the model (for detecting midnight date changes) + lastEmittedDate: string | null + // Additional directories from --add-dir flag (for CLAUDE.md loading) + additionalDirectoriesForClaudeMd: string[] + // Channel server allowlist from --channels flag (servers whose channel + // notifications should register this session). Parsed once in main.tsx — + // the tag decides trust model: 'plugin' → marketplace verification + + // allowlist, 'server' → allowlist always fails (schema is plugin-only). + // Either kind needs entry.dev to bypass allowlist. + allowedChannels: ChannelEntry[] + // True if any entry in allowedChannels came from + // --dangerously-load-development-channels (so ChannelsNotice can name the + // right flag in policy-blocked messages) + hasDevChannels: boolean + // Dir containing the session's `.jsonl`; null = derive from originalCwd. + sessionProjectDir: string | null + // Cached prompt cache 1h TTL allowlist from GrowthBook (session-stable) + promptCache1hAllowlist: string[] | null + // Cached 1h TTL user eligibility (session-stable). Latched on first + // evaluation so mid-session overage flips don't change the cache_control + // TTL, which would bust the server-side prompt cache. + promptCache1hEligible: boolean | null + // Sticky-on latch for AFK_MODE_BETA_HEADER. Once auto mode is first + // activated, keep sending the header for the rest of the session so + // Shift+Tab toggles don't bust the ~50-70K token prompt cache. + afkModeHeaderLatched: boolean | null + // Sticky-on latch for FAST_MODE_BETA_HEADER. Once fast mode is first + // enabled, keep sending the header so cooldown enter/exit doesn't + // double-bust the prompt cache. The `speed` body param stays dynamic. + fastModeHeaderLatched: boolean | null + // Sticky-on latch for the cache-editing beta header. Once cached + // microcompact is first enabled, keep sending the header so mid-session + // GrowthBook/settings toggles don't bust the prompt cache. + cacheEditingHeaderLatched: boolean | null + // Sticky-on latch for clearing thinking from prior tool loops. Triggered + // when >1h since last API call (confirmed cache miss — no cache-hit + // benefit to keeping thinking). Once latched, stays on so the newly-warmed + // thinking-cleared cache isn't busted by flipping back to keep:'all'. + thinkingClearLatched: boolean | null + // Current prompt ID (UUID) correlating a user prompt with subsequent OTel events + promptId: string | null + // Last API requestId for the main conversation chain (not subagents). + // Updated after each successful API response for main-session queries. + // Read at shutdown to send cache eviction hints to inference. + lastMainRequestId: string | undefined + // Timestamp (Date.now()) of the last successful API call completion. + // Used to compute timeSinceLastApiCallMs in tengu_api_success for + // correlating cache misses with idle time (cache TTL is ~5min). + lastApiCompletionTimestamp: number | null + // Set to true after compaction (auto or manual /compact). Consumed by + // logAPISuccess to tag the first post-compaction API call so we can + // distinguish compaction-induced cache misses from TTL expiry. + pendingPostCompaction: boolean +} + +// ALSO HERE - THINK THRICE BEFORE MODIFYING +function getInitialState(): State { + // Resolve symlinks in cwd to match behavior of shell.ts setCwd + // This ensures consistency with how paths are sanitized for session storage + let resolvedCwd = '' + if ( + typeof process !== 'undefined' && + typeof process.cwd === 'function' && + typeof realpathSync === 'function' + ) { + const rawCwd = cwd() + try { + resolvedCwd = realpathSync(rawCwd).normalize('NFC') + } catch { + // File Provider EPERM on CloudStorage mounts (lstat per path component). + resolvedCwd = rawCwd.normalize('NFC') + } + } + const state: State = { + originalCwd: resolvedCwd, + projectRoot: resolvedCwd, + totalCostUSD: 0, + totalAPIDuration: 0, + totalAPIDurationWithoutRetries: 0, + totalToolDuration: 0, + turnHookDurationMs: 0, + turnToolDurationMs: 0, + turnClassifierDurationMs: 0, + turnToolCount: 0, + turnHookCount: 0, + turnClassifierCount: 0, + startTime: Date.now(), + lastInteractionTime: Date.now(), + totalLinesAdded: 0, + totalLinesRemoved: 0, + hasUnknownModelCost: false, + cwd: resolvedCwd, + modelUsage: {}, + mainLoopModelOverride: undefined, + initialMainLoopModel: null, + modelStrings: null, + isInteractive: false, + kairosActive: false, + strictToolResultPairing: false, + sdkAgentProgressSummariesEnabled: false, + userMsgOptIn: false, + clientType: 'cli', + sessionSource: undefined, + questionPreviewFormat: undefined, + sessionIngressToken: undefined, + oauthTokenFromFd: undefined, + apiKeyFromFd: undefined, + flagSettingsPath: undefined, + flagSettingsInline: null, + allowedSettingSources: [ + 'userSettings', + 'projectSettings', + 'localSettings', + 'flagSettings', + 'policySettings', + ], + // Telemetry state + meter: null, + sessionCounter: null, + locCounter: null, + prCounter: null, + commitCounter: null, + costCounter: null, + tokenCounter: null, + codeEditToolDecisionCounter: null, + activeTimeCounter: null, + statsStore: null, + sessionId: randomUUID() as SessionId, + parentSessionId: undefined, + // Logger state + loggerProvider: null, + eventLogger: null, + // Meter provider state + meterProvider: null, + tracerProvider: null, + // Agent color state + agentColorMap: new Map(), + agentColorIndex: 0, + // Last API request for bug reports + lastAPIRequest: null, + lastAPIRequestMessages: null, + // Last auto-mode classifier request(s) for /share transcript + lastClassifierRequests: null, + cachedClaudeMdContent: null, + // In-memory error log for recent errors + inMemoryErrorLog: [], + // Session-only plugins from --plugin-dir flag + inlinePlugins: [], + // Explicit --chrome / --no-chrome flag value (undefined = not set on CLI) + chromeFlagOverride: undefined, + // Use cowork_plugins directory instead of plugins + useCoworkPlugins: false, + // Session-only bypass permissions mode flag (not persisted) + sessionBypassPermissionsMode: false, + // Scheduled tasks disabled until flag or dialog enables them + scheduledTasksEnabled: false, + sessionCronTasks: [], + sessionCreatedTeams: new Set(), + // Session-only trust flag (not persisted to disk) + sessionTrustAccepted: false, + // Session-only flag to disable session persistence to disk + sessionPersistenceDisabled: false, + // Track if user has exited plan mode in this session + hasExitedPlanMode: false, + // Track if we need to show the plan mode exit attachment + needsPlanModeExitAttachment: false, + // Track if we need to show the auto mode exit attachment + needsAutoModeExitAttachment: false, + // Track if LSP plugin recommendation has been shown this session + lspRecommendationShownThisSession: false, + // SDK init event state + initJsonSchema: null, + registeredHooks: null, + // Cache for plan slugs + planSlugCache: new Map(), + // Track teleported session for reliability logging + teleportedSessionInfo: null, + // Track invoked skills for preservation across compaction + invokedSkills: new Map(), + // Track slow operations for dev bar display + slowOperations: [], + // SDK-provided betas + sdkBetas: undefined, + // Main thread agent type + mainThreadAgentType: undefined, + // Remote mode + isRemoteMode: false, + ...(process.env.USER_TYPE === 'ant' + ? { + replBridgeActive: false, + } + : {}), + // Direct connect server URL + directConnectServerUrl: undefined, + // System prompt section cache state + systemPromptSectionCache: new Map(), + // Last date emitted to the model + lastEmittedDate: null, + // Additional directories from --add-dir flag (for CLAUDE.md loading) + additionalDirectoriesForClaudeMd: [], + // Channel server allowlist from --channels flag + allowedChannels: [], + hasDevChannels: false, + // Session project dir (null = derive from originalCwd) + sessionProjectDir: null, + // Prompt cache 1h allowlist (null = not yet fetched from GrowthBook) + promptCache1hAllowlist: null, + // Prompt cache 1h eligibility (null = not yet evaluated) + promptCache1hEligible: null, + // Beta header latches (null = not yet triggered) + afkModeHeaderLatched: null, + fastModeHeaderLatched: null, + cacheEditingHeaderLatched: null, + thinkingClearLatched: null, + // Current prompt ID + promptId: null, + lastMainRequestId: undefined, + lastApiCompletionTimestamp: null, + pendingPostCompaction: false, + } + + return state +} + +// AND ESPECIALLY HERE +const STATE: State = getInitialState() + +export function getSessionId(): SessionId { + return STATE.sessionId +} + +export function regenerateSessionId( + options: { setCurrentAsParent?: boolean } = {}, +): SessionId { + if (options.setCurrentAsParent) { + STATE.parentSessionId = STATE.sessionId + } + // Drop the outgoing session's plan-slug entry so the Map doesn't + // accumulate stale keys. Callers that need to carry the slug across + // (REPL.tsx clearContext) read it before calling clearConversation. + STATE.planSlugCache.delete(STATE.sessionId) + // Regenerated sessions live in the current project: reset projectDir to + // null so getTranscriptPath() derives from originalCwd. + STATE.sessionId = randomUUID() as SessionId + STATE.sessionProjectDir = null + return STATE.sessionId +} + +export function getParentSessionId(): SessionId | undefined { + return STATE.parentSessionId +} + +/** + * Atomically switch the active session. `sessionId` and `sessionProjectDir` + * always change together — there is no separate setter for either, so they + * cannot drift out of sync (CC-34). + * + * @param projectDir — directory containing `.jsonl`. Omit (or + * pass `null`) for sessions in the current project — the path will derive + * from originalCwd at read time. Pass `dirname(transcriptPath)` when the + * session lives in a different project directory (git worktrees, + * cross-project resume). Every call resets the project dir; it never + * carries over from the previous session. + */ +export function switchSession( + sessionId: SessionId, + projectDir: string | null = null, +): void { + // Drop the outgoing session's plan-slug entry so the Map stays bounded + // across repeated /resume. Only the current session's slug is ever read + // (plans.ts getPlanSlug defaults to getSessionId()). + STATE.planSlugCache.delete(STATE.sessionId) + STATE.sessionId = sessionId + STATE.sessionProjectDir = projectDir + sessionSwitched.emit(sessionId) +} + +const sessionSwitched = createSignal<[id: SessionId]>() + +/** + * Register a callback that fires when switchSession changes the active + * sessionId. bootstrap can't import listeners directly (DAG leaf), so + * callers register themselves. concurrentSessions.ts uses this to keep the + * PID file's sessionId in sync with --resume. + */ +export const onSessionSwitch = sessionSwitched.subscribe + +/** + * Project directory the current session's transcript lives in, or `null` if + * the session was created in the current project (common case — derive from + * originalCwd). See `switchSession()`. + */ +export function getSessionProjectDir(): string | null { + return STATE.sessionProjectDir +} + +export function getOriginalCwd(): string { + return STATE.originalCwd +} + +/** + * Get the stable project root directory. + * Unlike getOriginalCwd(), this is never updated by mid-session EnterWorktreeTool + * (so skills/history stay stable when entering a throwaway worktree). + * It IS set at startup by --worktree, since that worktree is the session's project. + * Use for project identity (history, skills, sessions) not file operations. + */ +export function getProjectRoot(): string { + return STATE.projectRoot +} + +export function setOriginalCwd(cwd: string): void { + STATE.originalCwd = cwd.normalize('NFC') +} + +/** + * Only for --worktree startup flag. Mid-session EnterWorktreeTool must NOT + * call this — skills/history should stay anchored to where the session started. + */ +export function setProjectRoot(cwd: string): void { + STATE.projectRoot = cwd.normalize('NFC') +} + +export function getCwdState(): string { + return STATE.cwd +} + +export function setCwdState(cwd: string): void { + STATE.cwd = cwd.normalize('NFC') +} + +export function getDirectConnectServerUrl(): string | undefined { + return STATE.directConnectServerUrl +} + +export function setDirectConnectServerUrl(url: string): void { + STATE.directConnectServerUrl = url +} + +export function addToTotalDurationState( + duration: number, + durationWithoutRetries: number, +): void { + STATE.totalAPIDuration += duration + STATE.totalAPIDurationWithoutRetries += durationWithoutRetries +} + +export function resetTotalDurationStateAndCost_FOR_TESTS_ONLY(): void { + STATE.totalAPIDuration = 0 + STATE.totalAPIDurationWithoutRetries = 0 + STATE.totalCostUSD = 0 +} + +export function addToTotalCostState( + cost: number, + modelUsage: ModelUsage, + model: string, +): void { + STATE.modelUsage[model] = modelUsage + STATE.totalCostUSD += cost +} + +export function getTotalCostUSD(): number { + return STATE.totalCostUSD +} + +export function getTotalAPIDuration(): number { + return STATE.totalAPIDuration +} + +export function getTotalDuration(): number { + return Date.now() - STATE.startTime +} + +export function getTotalAPIDurationWithoutRetries(): number { + return STATE.totalAPIDurationWithoutRetries +} + +export function getTotalToolDuration(): number { + return STATE.totalToolDuration +} + +export function addToToolDuration(duration: number): void { + STATE.totalToolDuration += duration + STATE.turnToolDurationMs += duration + STATE.turnToolCount++ +} + +export function getTurnHookDurationMs(): number { + return STATE.turnHookDurationMs +} + +export function addToTurnHookDuration(duration: number): void { + STATE.turnHookDurationMs += duration + STATE.turnHookCount++ +} + +export function resetTurnHookDuration(): void { + STATE.turnHookDurationMs = 0 + STATE.turnHookCount = 0 +} + +export function getTurnHookCount(): number { + return STATE.turnHookCount +} + +export function getTurnToolDurationMs(): number { + return STATE.turnToolDurationMs +} + +export function resetTurnToolDuration(): void { + STATE.turnToolDurationMs = 0 + STATE.turnToolCount = 0 +} + +export function getTurnToolCount(): number { + return STATE.turnToolCount +} + +export function getTurnClassifierDurationMs(): number { + return STATE.turnClassifierDurationMs +} + +export function addToTurnClassifierDuration(duration: number): void { + STATE.turnClassifierDurationMs += duration + STATE.turnClassifierCount++ +} + +export function resetTurnClassifierDuration(): void { + STATE.turnClassifierDurationMs = 0 + STATE.turnClassifierCount = 0 +} + +export function getTurnClassifierCount(): number { + return STATE.turnClassifierCount +} + +export function getStatsStore(): { + observe(name: string, value: number): void +} | null { + return STATE.statsStore +} + +export function setStatsStore( + store: { observe(name: string, value: number): void } | null, +): void { + STATE.statsStore = store +} + +/** + * Marks that an interaction occurred. + * + * By default the actual Date.now() call is deferred until the next Ink render + * frame (via flushInteractionTime()) so we avoid calling Date.now() on every + * single keypress. + * + * Pass `immediate = true` when calling from React useEffect callbacks or + * other code that runs *after* the Ink render cycle has already flushed. + * Without it the timestamp stays stale until the next render, which may never + * come if the user is idle (e.g. permission dialog waiting for input). + */ +let interactionTimeDirty = false + +export function updateLastInteractionTime(immediate?: boolean): void { + if (immediate) { + flushInteractionTime_inner() + } else { + interactionTimeDirty = true + } +} + +/** + * If an interaction was recorded since the last flush, update the timestamp + * now. Called by Ink before each render cycle so we batch many keypresses into + * a single Date.now() call. + */ +export function flushInteractionTime(): void { + if (interactionTimeDirty) { + flushInteractionTime_inner() + } +} + +function flushInteractionTime_inner(): void { + STATE.lastInteractionTime = Date.now() + interactionTimeDirty = false +} + +export function addToTotalLinesChanged(added: number, removed: number): void { + STATE.totalLinesAdded += added + STATE.totalLinesRemoved += removed +} + +export function getTotalLinesAdded(): number { + return STATE.totalLinesAdded +} + +export function getTotalLinesRemoved(): number { + return STATE.totalLinesRemoved +} + +export function getTotalInputTokens(): number { + return sumBy(Object.values(STATE.modelUsage), 'inputTokens') +} + +export function getTotalOutputTokens(): number { + return sumBy(Object.values(STATE.modelUsage), 'outputTokens') +} + +export function getTotalCacheReadInputTokens(): number { + return sumBy(Object.values(STATE.modelUsage), 'cacheReadInputTokens') +} + +export function getTotalCacheCreationInputTokens(): number { + return sumBy(Object.values(STATE.modelUsage), 'cacheCreationInputTokens') +} + +export function getTotalWebSearchRequests(): number { + return sumBy(Object.values(STATE.modelUsage), 'webSearchRequests') +} + +let outputTokensAtTurnStart = 0 +let currentTurnTokenBudget: number | null = null +export function getTurnOutputTokens(): number { + return getTotalOutputTokens() - outputTokensAtTurnStart +} +export function getCurrentTurnTokenBudget(): number | null { + return currentTurnTokenBudget +} +let budgetContinuationCount = 0 +export function snapshotOutputTokensForTurn(budget: number | null): void { + outputTokensAtTurnStart = getTotalOutputTokens() + currentTurnTokenBudget = budget + budgetContinuationCount = 0 +} +export function getBudgetContinuationCount(): number { + return budgetContinuationCount +} +export function incrementBudgetContinuationCount(): void { + budgetContinuationCount++ +} + +export function setHasUnknownModelCost(): void { + STATE.hasUnknownModelCost = true +} + +export function hasUnknownModelCost(): boolean { + return STATE.hasUnknownModelCost +} + +export function getLastMainRequestId(): string | undefined { + return STATE.lastMainRequestId +} + +export function setLastMainRequestId(requestId: string): void { + STATE.lastMainRequestId = requestId +} + +export function getLastApiCompletionTimestamp(): number | null { + return STATE.lastApiCompletionTimestamp +} + +export function setLastApiCompletionTimestamp(timestamp: number): void { + STATE.lastApiCompletionTimestamp = timestamp +} + +/** Mark that a compaction just occurred. The next API success event will + * include isPostCompaction=true, then the flag auto-resets. */ +export function markPostCompaction(): void { + STATE.pendingPostCompaction = true +} + +/** Consume the post-compaction flag. Returns true once after compaction, + * then returns false until the next compaction. */ +export function consumePostCompaction(): boolean { + const was = STATE.pendingPostCompaction + STATE.pendingPostCompaction = false + return was +} + +export function getLastInteractionTime(): number { + return STATE.lastInteractionTime +} + +// Scroll drain suspension — background intervals check this before doing work +// so they don't compete with scroll frames for the event loop. Set by +// ScrollBox scrollBy/scrollTo, cleared SCROLL_DRAIN_IDLE_MS after the last +// scroll event. Module-scope (not in STATE) — ephemeral hot-path flag, no +// test-reset needed since the debounce timer self-clears. +let scrollDraining = false +let scrollDrainTimer: ReturnType | undefined +const SCROLL_DRAIN_IDLE_MS = 150 + +/** Mark that a scroll event just happened. Background intervals gate on + * getIsScrollDraining() and skip their work until the debounce clears. */ +export function markScrollActivity(): void { + scrollDraining = true + if (scrollDrainTimer) clearTimeout(scrollDrainTimer) + scrollDrainTimer = setTimeout(() => { + scrollDraining = false + scrollDrainTimer = undefined + }, SCROLL_DRAIN_IDLE_MS) + scrollDrainTimer.unref?.() +} + +/** True while scroll is actively draining (within 150ms of last event). + * Intervals should early-return when this is set — the work picks up next + * tick after scroll settles. */ +export function getIsScrollDraining(): boolean { + return scrollDraining +} + +/** Await this before expensive one-shot work (network, subprocess) that could + * coincide with scroll. Resolves immediately if not scrolling; otherwise + * polls at the idle interval until the flag clears. */ +export async function waitForScrollIdle(): Promise { + while (scrollDraining) { + // bootstrap-isolation forbids importing sleep() from src/utils/ + // eslint-disable-next-line no-restricted-syntax + await new Promise(r => setTimeout(r, SCROLL_DRAIN_IDLE_MS).unref?.()) + } +} + +export function getModelUsage(): { [modelName: string]: ModelUsage } { + return STATE.modelUsage +} + +export function getUsageForModel(model: string): ModelUsage | undefined { + return STATE.modelUsage[model] +} + +/** + * Gets the model override set from the --model CLI flag or after the user + * updates their configured model. + */ +export function getMainLoopModelOverride(): ModelSetting | undefined { + return STATE.mainLoopModelOverride +} + +export function getInitialMainLoopModel(): ModelSetting { + return STATE.initialMainLoopModel +} + +export function setMainLoopModelOverride( + model: ModelSetting | undefined, +): void { + STATE.mainLoopModelOverride = model +} + +export function setInitialMainLoopModel(model: ModelSetting): void { + STATE.initialMainLoopModel = model +} + +export function getSdkBetas(): string[] | undefined { + return STATE.sdkBetas +} + +export function setSdkBetas(betas: string[] | undefined): void { + STATE.sdkBetas = betas +} + +export function resetCostState(): void { + STATE.totalCostUSD = 0 + STATE.totalAPIDuration = 0 + STATE.totalAPIDurationWithoutRetries = 0 + STATE.totalToolDuration = 0 + STATE.startTime = Date.now() + STATE.totalLinesAdded = 0 + STATE.totalLinesRemoved = 0 + STATE.hasUnknownModelCost = false + STATE.modelUsage = {} + STATE.promptId = null +} + +/** + * Sets cost state values for session restore. + * Called by restoreCostStateForSession in cost-tracker.ts. + */ +export function setCostStateForRestore({ + totalCostUSD, + totalAPIDuration, + totalAPIDurationWithoutRetries, + totalToolDuration, + totalLinesAdded, + totalLinesRemoved, + lastDuration, + modelUsage, +}: { + totalCostUSD: number + totalAPIDuration: number + totalAPIDurationWithoutRetries: number + totalToolDuration: number + totalLinesAdded: number + totalLinesRemoved: number + lastDuration: number | undefined + modelUsage: { [modelName: string]: ModelUsage } | undefined +}): void { + STATE.totalCostUSD = totalCostUSD + STATE.totalAPIDuration = totalAPIDuration + STATE.totalAPIDurationWithoutRetries = totalAPIDurationWithoutRetries + STATE.totalToolDuration = totalToolDuration + STATE.totalLinesAdded = totalLinesAdded + STATE.totalLinesRemoved = totalLinesRemoved + + // Restore per-model usage breakdown + if (modelUsage) { + STATE.modelUsage = modelUsage + } + + // Adjust startTime to make wall duration accumulate + if (lastDuration) { + STATE.startTime = Date.now() - lastDuration + } +} + +// Only used in tests +export function resetStateForTests(): void { + if (process.env.NODE_ENV !== 'test') { + throw new Error('resetStateForTests can only be called in tests') + } + Object.entries(getInitialState()).forEach(([key, value]) => { + STATE[key as keyof State] = value as never + }) + outputTokensAtTurnStart = 0 + currentTurnTokenBudget = null + budgetContinuationCount = 0 + sessionSwitched.clear() +} + +// You shouldn't use this directly. See src/utils/model/modelStrings.ts::getModelStrings() +export function getModelStrings(): ModelStrings | null { + return STATE.modelStrings +} + +// You shouldn't use this directly. See src/utils/model/modelStrings.ts +export function setModelStrings(modelStrings: ModelStrings): void { + STATE.modelStrings = modelStrings +} + +// Test utility function to reset model strings for re-initialization. +// Separate from setModelStrings because we only want to accept 'null' in tests. +export function resetModelStringsForTestingOnly() { + STATE.modelStrings = null +} + +export function setMeter( + meter: Meter, + createCounter: (name: string, options: MetricOptions) => AttributedCounter, +): void { + STATE.meter = meter + + // Initialize all counters using the provided factory + STATE.sessionCounter = createCounter('claude_code.session.count', { + description: 'Count of CLI sessions started', + }) + STATE.locCounter = createCounter('claude_code.lines_of_code.count', { + description: + "Count of lines of code modified, with the 'type' attribute indicating whether lines were added or removed", + }) + STATE.prCounter = createCounter('claude_code.pull_request.count', { + description: 'Number of pull requests created', + }) + STATE.commitCounter = createCounter('claude_code.commit.count', { + description: 'Number of git commits created', + }) + STATE.costCounter = createCounter('claude_code.cost.usage', { + description: 'Cost of the Claude Code session', + unit: 'USD', + }) + STATE.tokenCounter = createCounter('claude_code.token.usage', { + description: 'Number of tokens used', + unit: 'tokens', + }) + STATE.codeEditToolDecisionCounter = createCounter( + 'claude_code.code_edit_tool.decision', + { + description: + 'Count of code editing tool permission decisions (accept/reject) for Edit, Write, and NotebookEdit tools', + }, + ) + STATE.activeTimeCounter = createCounter('claude_code.active_time.total', { + description: 'Total active time in seconds', + unit: 's', + }) +} + +export function getMeter(): Meter | null { + return STATE.meter +} + +export function getSessionCounter(): AttributedCounter | null { + return STATE.sessionCounter +} + +export function getLocCounter(): AttributedCounter | null { + return STATE.locCounter +} + +export function getPrCounter(): AttributedCounter | null { + return STATE.prCounter +} + +export function getCommitCounter(): AttributedCounter | null { + return STATE.commitCounter +} + +export function getCostCounter(): AttributedCounter | null { + return STATE.costCounter +} + +export function getTokenCounter(): AttributedCounter | null { + return STATE.tokenCounter +} + +export function getCodeEditToolDecisionCounter(): AttributedCounter | null { + return STATE.codeEditToolDecisionCounter +} + +export function getActiveTimeCounter(): AttributedCounter | null { + return STATE.activeTimeCounter +} + +export function getLoggerProvider(): LoggerProvider | null { + return STATE.loggerProvider +} + +export function setLoggerProvider(provider: LoggerProvider | null): void { + STATE.loggerProvider = provider +} + +export function getEventLogger(): ReturnType | null { + return STATE.eventLogger +} + +export function setEventLogger( + logger: ReturnType | null, +): void { + STATE.eventLogger = logger +} + +export function getMeterProvider(): MeterProvider | null { + return STATE.meterProvider +} + +export function setMeterProvider(provider: MeterProvider | null): void { + STATE.meterProvider = provider +} +export function getTracerProvider(): BasicTracerProvider | null { + return STATE.tracerProvider +} +export function setTracerProvider(provider: BasicTracerProvider | null): void { + STATE.tracerProvider = provider +} + +export function getIsNonInteractiveSession(): boolean { + return !STATE.isInteractive +} + +export function getIsInteractive(): boolean { + return STATE.isInteractive +} + +export function setIsInteractive(value: boolean): void { + STATE.isInteractive = value +} + +export function getClientType(): string { + return STATE.clientType +} + +export function setClientType(type: string): void { + STATE.clientType = type +} + +export function getSdkAgentProgressSummariesEnabled(): boolean { + return STATE.sdkAgentProgressSummariesEnabled +} + +export function setSdkAgentProgressSummariesEnabled(value: boolean): void { + STATE.sdkAgentProgressSummariesEnabled = value +} + +export function getKairosActive(): boolean { + return STATE.kairosActive +} + +export function setKairosActive(value: boolean): void { + STATE.kairosActive = value +} + +export function getStrictToolResultPairing(): boolean { + return STATE.strictToolResultPairing +} + +export function setStrictToolResultPairing(value: boolean): void { + STATE.strictToolResultPairing = value +} + +// Field name 'userMsgOptIn' avoids excluded-string substrings ('BriefTool', +// 'SendUserMessage' — case-insensitive). All callers are inside feature() +// guards so these accessors don't need their own (matches getKairosActive). +export function getUserMsgOptIn(): boolean { + return STATE.userMsgOptIn +} + +export function setUserMsgOptIn(value: boolean): void { + STATE.userMsgOptIn = value +} + +export function getSessionSource(): string | undefined { + return STATE.sessionSource +} + +export function setSessionSource(source: string): void { + STATE.sessionSource = source +} + +export function getQuestionPreviewFormat(): 'markdown' | 'html' | undefined { + return STATE.questionPreviewFormat +} + +export function setQuestionPreviewFormat(format: 'markdown' | 'html'): void { + STATE.questionPreviewFormat = format +} + +export function getAgentColorMap(): Map { + return STATE.agentColorMap +} + +export function getFlagSettingsPath(): string | undefined { + return STATE.flagSettingsPath +} + +export function setFlagSettingsPath(path: string | undefined): void { + STATE.flagSettingsPath = path +} + +export function getFlagSettingsInline(): Record | null { + return STATE.flagSettingsInline +} + +export function setFlagSettingsInline( + settings: Record | null, +): void { + STATE.flagSettingsInline = settings +} + +export function getSessionIngressToken(): string | null | undefined { + return STATE.sessionIngressToken +} + +export function setSessionIngressToken(token: string | null): void { + STATE.sessionIngressToken = token +} + +export function getOauthTokenFromFd(): string | null | undefined { + return STATE.oauthTokenFromFd +} + +export function setOauthTokenFromFd(token: string | null): void { + STATE.oauthTokenFromFd = token +} + +export function getApiKeyFromFd(): string | null | undefined { + return STATE.apiKeyFromFd +} + +export function setApiKeyFromFd(key: string | null): void { + STATE.apiKeyFromFd = key +} + +export function setLastAPIRequest( + params: Omit | null, +): void { + STATE.lastAPIRequest = params +} + +export function getLastAPIRequest(): Omit< + BetaMessageStreamParams, + 'messages' +> | null { + return STATE.lastAPIRequest +} + +export function setLastAPIRequestMessages( + messages: BetaMessageStreamParams['messages'] | null, +): void { + STATE.lastAPIRequestMessages = messages +} + +export function getLastAPIRequestMessages(): + | BetaMessageStreamParams['messages'] + | null { + return STATE.lastAPIRequestMessages +} + +export function setLastClassifierRequests(requests: unknown[] | null): void { + STATE.lastClassifierRequests = requests +} + +export function getLastClassifierRequests(): unknown[] | null { + return STATE.lastClassifierRequests +} + +export function setCachedClaudeMdContent(content: string | null): void { + STATE.cachedClaudeMdContent = content +} + +export function getCachedClaudeMdContent(): string | null { + return STATE.cachedClaudeMdContent +} + +export function addToInMemoryErrorLog(errorInfo: { + error: string + timestamp: string +}): void { + const MAX_IN_MEMORY_ERRORS = 100 + if (STATE.inMemoryErrorLog.length >= MAX_IN_MEMORY_ERRORS) { + STATE.inMemoryErrorLog.shift() // Remove oldest error + } + STATE.inMemoryErrorLog.push(errorInfo) +} + +export function getAllowedSettingSources(): SettingSource[] { + return STATE.allowedSettingSources +} + +export function setAllowedSettingSources(sources: SettingSource[]): void { + STATE.allowedSettingSources = sources +} + +export function preferThirdPartyAuthentication(): boolean { + // IDE extension should behave as 1P for authentication reasons. + return getIsNonInteractiveSession() && STATE.clientType !== 'claude-vscode' +} + +export function setInlinePlugins(plugins: Array): void { + STATE.inlinePlugins = plugins +} + +export function getInlinePlugins(): Array { + return STATE.inlinePlugins +} + +export function setChromeFlagOverride(value: boolean | undefined): void { + STATE.chromeFlagOverride = value +} + +export function getChromeFlagOverride(): boolean | undefined { + return STATE.chromeFlagOverride +} + +export function setUseCoworkPlugins(value: boolean): void { + STATE.useCoworkPlugins = value + resetSettingsCache() +} + +export function getUseCoworkPlugins(): boolean { + return STATE.useCoworkPlugins +} + +export function setSessionBypassPermissionsMode(enabled: boolean): void { + STATE.sessionBypassPermissionsMode = enabled +} + +export function getSessionBypassPermissionsMode(): boolean { + return STATE.sessionBypassPermissionsMode +} + +export function setScheduledTasksEnabled(enabled: boolean): void { + STATE.scheduledTasksEnabled = enabled +} + +export function getScheduledTasksEnabled(): boolean { + return STATE.scheduledTasksEnabled +} + +export type SessionCronTask = { + id: string + cron: string + prompt: string + createdAt: number + recurring?: boolean + /** + * When set, the task was created by an in-process teammate (not the team lead). + * The scheduler routes fires to that teammate's pendingUserMessages queue + * instead of the main REPL command queue. Session-only — never written to disk. + */ + agentId?: string +} + +export function getSessionCronTasks(): SessionCronTask[] { + return STATE.sessionCronTasks +} + +export function addSessionCronTask(task: SessionCronTask): void { + STATE.sessionCronTasks.push(task) +} + +/** + * Returns the number of tasks actually removed. Callers use this to skip + * downstream work (e.g. the disk read in removeCronTasks) when all ids + * were accounted for here. + */ +export function removeSessionCronTasks(ids: readonly string[]): number { + if (ids.length === 0) return 0 + const idSet = new Set(ids) + const remaining = STATE.sessionCronTasks.filter(t => !idSet.has(t.id)) + const removed = STATE.sessionCronTasks.length - remaining.length + if (removed === 0) return 0 + STATE.sessionCronTasks = remaining + return removed +} + +export function setSessionTrustAccepted(accepted: boolean): void { + STATE.sessionTrustAccepted = accepted +} + +export function getSessionTrustAccepted(): boolean { + return STATE.sessionTrustAccepted +} + +export function setSessionPersistenceDisabled(disabled: boolean): void { + STATE.sessionPersistenceDisabled = disabled +} + +export function isSessionPersistenceDisabled(): boolean { + return STATE.sessionPersistenceDisabled +} + +export function hasExitedPlanModeInSession(): boolean { + return STATE.hasExitedPlanMode +} + +export function setHasExitedPlanMode(value: boolean): void { + STATE.hasExitedPlanMode = value +} + +export function needsPlanModeExitAttachment(): boolean { + return STATE.needsPlanModeExitAttachment +} + +export function setNeedsPlanModeExitAttachment(value: boolean): void { + STATE.needsPlanModeExitAttachment = value +} + +export function handlePlanModeTransition( + fromMode: string, + toMode: string, +): void { + // If switching TO plan mode, clear any pending exit attachment + // This prevents sending both plan_mode and plan_mode_exit when user toggles quickly + if (toMode === 'plan' && fromMode !== 'plan') { + STATE.needsPlanModeExitAttachment = false + } + + // If switching out of plan mode, trigger the plan_mode_exit attachment + if (fromMode === 'plan' && toMode !== 'plan') { + STATE.needsPlanModeExitAttachment = true + } +} + +export function needsAutoModeExitAttachment(): boolean { + return STATE.needsAutoModeExitAttachment +} + +export function setNeedsAutoModeExitAttachment(value: boolean): void { + STATE.needsAutoModeExitAttachment = value +} + +export function handleAutoModeTransition( + fromMode: string, + toMode: string, +): void { + // Auto↔plan transitions are handled by prepareContextForPlanMode (auto may + // stay active through plan if opted in) and ExitPlanMode (restores mode). + // Skip both directions so this function only handles direct auto transitions. + if ( + (fromMode === 'auto' && toMode === 'plan') || + (fromMode === 'plan' && toMode === 'auto') + ) { + return + } + const fromIsAuto = fromMode === 'auto' + const toIsAuto = toMode === 'auto' + + // If switching TO auto mode, clear any pending exit attachment + // This prevents sending both auto_mode and auto_mode_exit when user toggles quickly + if (toIsAuto && !fromIsAuto) { + STATE.needsAutoModeExitAttachment = false + } + + // If switching out of auto mode, trigger the auto_mode_exit attachment + if (fromIsAuto && !toIsAuto) { + STATE.needsAutoModeExitAttachment = true + } +} + +// LSP plugin recommendation session tracking +export function hasShownLspRecommendationThisSession(): boolean { + return STATE.lspRecommendationShownThisSession +} + +export function setLspRecommendationShownThisSession(value: boolean): void { + STATE.lspRecommendationShownThisSession = value +} + +// SDK init event state +export function setInitJsonSchema(schema: Record): void { + STATE.initJsonSchema = schema +} + +export function getInitJsonSchema(): Record | null { + return STATE.initJsonSchema +} + +export function registerHookCallbacks( + hooks: Partial>, +): void { + if (!STATE.registeredHooks) { + STATE.registeredHooks = {} + } + + // `registerHookCallbacks` may be called multiple times, so we need to merge (not overwrite) + for (const [event, matchers] of Object.entries(hooks)) { + const eventKey = event as HookEvent + if (!STATE.registeredHooks[eventKey]) { + STATE.registeredHooks[eventKey] = [] + } + STATE.registeredHooks[eventKey]!.push(...matchers) + } +} + +export function getRegisteredHooks(): Partial< + Record +> | null { + return STATE.registeredHooks +} + +export function clearRegisteredHooks(): void { + STATE.registeredHooks = null +} + +export function clearRegisteredPluginHooks(): void { + if (!STATE.registeredHooks) { + return + } + + const filtered: Partial> = {} + for (const [event, matchers] of Object.entries(STATE.registeredHooks)) { + // Keep only callback hooks (those without pluginRoot) + const callbackHooks = matchers.filter(m => !('pluginRoot' in m)) + if (callbackHooks.length > 0) { + filtered[event as HookEvent] = callbackHooks + } + } + + STATE.registeredHooks = Object.keys(filtered).length > 0 ? filtered : null +} + +export function resetSdkInitState(): void { + STATE.initJsonSchema = null + STATE.registeredHooks = null +} + +export function getPlanSlugCache(): Map { + return STATE.planSlugCache +} + +export function getSessionCreatedTeams(): Set { + return STATE.sessionCreatedTeams +} + +// Teleported session tracking for reliability logging +export function setTeleportedSessionInfo(info: { + sessionId: string | null +}): void { + STATE.teleportedSessionInfo = { + isTeleported: true, + hasLoggedFirstMessage: false, + sessionId: info.sessionId, + } +} + +export function getTeleportedSessionInfo(): { + isTeleported: boolean + hasLoggedFirstMessage: boolean + sessionId: string | null +} | null { + return STATE.teleportedSessionInfo +} + +export function markFirstTeleportMessageLogged(): void { + if (STATE.teleportedSessionInfo) { + STATE.teleportedSessionInfo.hasLoggedFirstMessage = true + } +} + +// Invoked skills tracking for preservation across compaction +export type InvokedSkillInfo = { + skillName: string + skillPath: string + content: string + invokedAt: number + agentId: string | null +} + +export function addInvokedSkill( + skillName: string, + skillPath: string, + content: string, + agentId: string | null = null, +): void { + const key = `${agentId ?? ''}:${skillName}` + STATE.invokedSkills.set(key, { + skillName, + skillPath, + content, + invokedAt: Date.now(), + agentId, + }) +} + +export function getInvokedSkills(): Map { + return STATE.invokedSkills +} + +export function getInvokedSkillsForAgent( + agentId: string | undefined | null, +): Map { + const normalizedId = agentId ?? null + const filtered = new Map() + for (const [key, skill] of STATE.invokedSkills) { + if (skill.agentId === normalizedId) { + filtered.set(key, skill) + } + } + return filtered +} + +export function clearInvokedSkills( + preservedAgentIds?: ReadonlySet, +): void { + if (!preservedAgentIds || preservedAgentIds.size === 0) { + STATE.invokedSkills.clear() + return + } + for (const [key, skill] of STATE.invokedSkills) { + if (skill.agentId === null || !preservedAgentIds.has(skill.agentId)) { + STATE.invokedSkills.delete(key) + } + } +} + +export function clearInvokedSkillsForAgent(agentId: string): void { + for (const [key, skill] of STATE.invokedSkills) { + if (skill.agentId === agentId) { + STATE.invokedSkills.delete(key) + } + } +} + +// Slow operations tracking for dev bar +const MAX_SLOW_OPERATIONS = 10 +const SLOW_OPERATION_TTL_MS = 10000 + +export function addSlowOperation(operation: string, durationMs: number): void { + if (process.env.USER_TYPE !== 'ant') return + // Skip tracking for editor sessions (user editing a prompt file in $EDITOR) + // These are intentionally slow since the user is drafting text + if (operation.includes('exec') && operation.includes('claude-prompt-')) { + return + } + const now = Date.now() + // Remove stale operations + STATE.slowOperations = STATE.slowOperations.filter( + op => now - op.timestamp < SLOW_OPERATION_TTL_MS, + ) + // Add new operation + STATE.slowOperations.push({ operation, durationMs, timestamp: now }) + // Keep only the most recent operations + if (STATE.slowOperations.length > MAX_SLOW_OPERATIONS) { + STATE.slowOperations = STATE.slowOperations.slice(-MAX_SLOW_OPERATIONS) + } +} + +const EMPTY_SLOW_OPERATIONS: ReadonlyArray<{ + operation: string + durationMs: number + timestamp: number +}> = [] + +export function getSlowOperations(): ReadonlyArray<{ + operation: string + durationMs: number + timestamp: number +}> { + // Most common case: nothing tracked. Return a stable reference so the + // caller's setState() can bail via Object.is instead of re-rendering at 2fps. + if (STATE.slowOperations.length === 0) { + return EMPTY_SLOW_OPERATIONS + } + const now = Date.now() + // Only allocate a new array when something actually expired; otherwise keep + // the reference stable across polls while ops are still fresh. + if ( + STATE.slowOperations.some(op => now - op.timestamp >= SLOW_OPERATION_TTL_MS) + ) { + STATE.slowOperations = STATE.slowOperations.filter( + op => now - op.timestamp < SLOW_OPERATION_TTL_MS, + ) + if (STATE.slowOperations.length === 0) { + return EMPTY_SLOW_OPERATIONS + } + } + // Safe to return directly: addSlowOperation() reassigns STATE.slowOperations + // before pushing, so the array held in React state is never mutated. + return STATE.slowOperations +} + +export function getMainThreadAgentType(): string | undefined { + return STATE.mainThreadAgentType +} + +export function setMainThreadAgentType(agentType: string | undefined): void { + STATE.mainThreadAgentType = agentType +} + +export function getIsRemoteMode(): boolean { + return STATE.isRemoteMode +} + +export function setIsRemoteMode(value: boolean): void { + STATE.isRemoteMode = value +} + +// System prompt section accessors + +export function getSystemPromptSectionCache(): Map { + return STATE.systemPromptSectionCache +} + +export function setSystemPromptSectionCacheEntry( + name: string, + value: string | null, +): void { + STATE.systemPromptSectionCache.set(name, value) +} + +export function clearSystemPromptSectionState(): void { + STATE.systemPromptSectionCache.clear() +} + +// Last emitted date accessors (for detecting midnight date changes) + +export function getLastEmittedDate(): string | null { + return STATE.lastEmittedDate +} + +export function setLastEmittedDate(date: string | null): void { + STATE.lastEmittedDate = date +} + +export function getAdditionalDirectoriesForClaudeMd(): string[] { + return STATE.additionalDirectoriesForClaudeMd +} + +export function setAdditionalDirectoriesForClaudeMd( + directories: string[], +): void { + STATE.additionalDirectoriesForClaudeMd = directories +} + +export function getAllowedChannels(): ChannelEntry[] { + return STATE.allowedChannels +} + +export function setAllowedChannels(entries: ChannelEntry[]): void { + STATE.allowedChannels = entries +} + +export function getHasDevChannels(): boolean { + return STATE.hasDevChannels +} + +export function setHasDevChannels(value: boolean): void { + STATE.hasDevChannels = value +} + +export function getPromptCache1hAllowlist(): string[] | null { + return STATE.promptCache1hAllowlist +} + +export function setPromptCache1hAllowlist(allowlist: string[] | null): void { + STATE.promptCache1hAllowlist = allowlist +} + +export function getPromptCache1hEligible(): boolean | null { + return STATE.promptCache1hEligible +} + +export function setPromptCache1hEligible(eligible: boolean | null): void { + STATE.promptCache1hEligible = eligible +} + +export function getAfkModeHeaderLatched(): boolean | null { + return STATE.afkModeHeaderLatched +} + +export function setAfkModeHeaderLatched(v: boolean): void { + STATE.afkModeHeaderLatched = v +} + +export function getFastModeHeaderLatched(): boolean | null { + return STATE.fastModeHeaderLatched +} + +export function setFastModeHeaderLatched(v: boolean): void { + STATE.fastModeHeaderLatched = v +} + +export function getCacheEditingHeaderLatched(): boolean | null { + return STATE.cacheEditingHeaderLatched +} + +export function setCacheEditingHeaderLatched(v: boolean): void { + STATE.cacheEditingHeaderLatched = v +} + +export function getThinkingClearLatched(): boolean | null { + return STATE.thinkingClearLatched +} + +export function setThinkingClearLatched(v: boolean): void { + STATE.thinkingClearLatched = v +} + +/** + * Reset beta header latches to null. Called on /clear and /compact so a + * fresh conversation gets fresh header evaluation. + */ +export function clearBetaHeaderLatches(): void { + STATE.afkModeHeaderLatched = null + STATE.fastModeHeaderLatched = null + STATE.cacheEditingHeaderLatched = null + STATE.thinkingClearLatched = null +} + +export function getPromptId(): string | null { + return STATE.promptId +} + +export function setPromptId(id: string | null): void { + STATE.promptId = id +} + diff --git a/bridge/bridgeApi.ts b/bridge/bridgeApi.ts new file mode 100644 index 0000000..052bd4f --- /dev/null +++ b/bridge/bridgeApi.ts @@ -0,0 +1,539 @@ +import axios from 'axios' + +import { debugBody, extractErrorDetail } from './debugUtils.js' +import { + BRIDGE_LOGIN_INSTRUCTION, + type BridgeApiClient, + type BridgeConfig, + type PermissionResponseEvent, + type WorkResponse, +} from './types.js' + +type BridgeApiDeps = { + baseUrl: string + getAccessToken: () => string | undefined + runnerVersion: string + onDebug?: (msg: string) => void + /** + * Called on 401 to attempt OAuth token refresh. Returns true if refreshed, + * in which case the request is retried once. Injected because + * handleOAuth401Error from utils/auth.ts transitively pulls in config.ts → + * file.ts → permissions/filesystem.ts → sessionStorage.ts → commands.ts + * (~1300 modules). Daemon callers using env-var tokens omit this — their + * tokens don't refresh, so 401 goes straight to BridgeFatalError. + */ + onAuth401?: (staleAccessToken: string) => Promise + /** + * Returns the trusted device token to send as X-Trusted-Device-Token on + * bridge API calls. Bridge sessions have SecurityTier=ELEVATED on the + * server (CCR v2); when the server's enforcement flag is on, + * ConnectBridgeWorker requires a trusted device at JWT-issuance. + * Optional — when absent or returning undefined, the header is omitted + * and the server falls through to its flag-off/no-op path. The CLI-side + * gate is tengu_sessions_elevated_auth_enforcement (see trustedDevice.ts). + */ + getTrustedDeviceToken?: () => string | undefined +} + +const BETA_HEADER = 'environments-2025-11-01' + +/** Allowlist pattern for server-provided IDs used in URL path segments. */ +const SAFE_ID_PATTERN = /^[a-zA-Z0-9_-]+$/ + +/** + * Validate that a server-provided ID is safe to interpolate into a URL path. + * Prevents path traversal (e.g. `../../admin`) and injection via IDs that + * contain slashes, dots, or other special characters. + */ +export function validateBridgeId(id: string, label: string): string { + if (!id || !SAFE_ID_PATTERN.test(id)) { + throw new Error(`Invalid ${label}: contains unsafe characters`) + } + return id +} + +/** Fatal bridge errors that should not be retried (e.g. auth failures). */ +export class BridgeFatalError extends Error { + readonly status: number + /** Server-provided error type, e.g. "environment_expired". */ + readonly errorType: string | undefined + constructor(message: string, status: number, errorType?: string) { + super(message) + this.name = 'BridgeFatalError' + this.status = status + this.errorType = errorType + } +} + +export function createBridgeApiClient(deps: BridgeApiDeps): BridgeApiClient { + function debug(msg: string): void { + deps.onDebug?.(msg) + } + + let consecutiveEmptyPolls = 0 + const EMPTY_POLL_LOG_INTERVAL = 100 + + function getHeaders(accessToken: string): Record { + const headers: Record = { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + 'anthropic-version': '2023-06-01', + 'anthropic-beta': BETA_HEADER, + 'x-environment-runner-version': deps.runnerVersion, + } + const deviceToken = deps.getTrustedDeviceToken?.() + if (deviceToken) { + headers['X-Trusted-Device-Token'] = deviceToken + } + return headers + } + + function resolveAuth(): string { + const accessToken = deps.getAccessToken() + if (!accessToken) { + throw new Error(BRIDGE_LOGIN_INSTRUCTION) + } + return accessToken + } + + /** + * Execute an OAuth-authenticated request with a single retry on 401. + * On 401, attempts token refresh via handleOAuth401Error (same pattern as + * withRetry.ts for v1/messages). If refresh succeeds, retries the request + * once with the new token. If refresh fails or the retry also returns 401, + * the 401 response is returned for handleErrorStatus to throw BridgeFatalError. + */ + async function withOAuthRetry( + fn: (accessToken: string) => Promise<{ status: number; data: T }>, + context: string, + ): Promise<{ status: number; data: T }> { + const accessToken = resolveAuth() + const response = await fn(accessToken) + + if (response.status !== 401) { + return response + } + + if (!deps.onAuth401) { + debug(`[bridge:api] ${context}: 401 received, no refresh handler`) + return response + } + + // Attempt token refresh — matches the pattern in withRetry.ts + debug(`[bridge:api] ${context}: 401 received, attempting token refresh`) + const refreshed = await deps.onAuth401(accessToken) + if (refreshed) { + debug(`[bridge:api] ${context}: Token refreshed, retrying request`) + const newToken = resolveAuth() + const retryResponse = await fn(newToken) + if (retryResponse.status !== 401) { + return retryResponse + } + debug(`[bridge:api] ${context}: Retry after refresh also got 401`) + } else { + debug(`[bridge:api] ${context}: Token refresh failed`) + } + + // Refresh failed — return 401 for handleErrorStatus to throw + return response + } + + return { + async registerBridgeEnvironment( + config: BridgeConfig, + ): Promise<{ environment_id: string; environment_secret: string }> { + debug( + `[bridge:api] POST /v1/environments/bridge bridgeId=${config.bridgeId}`, + ) + + const response = await withOAuthRetry( + (token: string) => + axios.post<{ + environment_id: string + environment_secret: string + }>( + `${deps.baseUrl}/v1/environments/bridge`, + { + machine_name: config.machineName, + directory: config.dir, + branch: config.branch, + git_repo_url: config.gitRepoUrl, + // Advertise session capacity so claude.ai/code can show + // "2/4 sessions" badges and only block the picker when + // actually at capacity. Backends that don't yet accept + // this field will silently ignore it. + max_sessions: config.maxSessions, + // worker_type lets claude.ai filter environments by origin + // (e.g. assistant picker only shows assistant-mode workers). + // Desktop cowork app sends "cowork"; we send a distinct value. + metadata: { worker_type: config.workerType }, + // Idempotent re-registration: if we have a backend-issued + // environment_id from a prior session (--session-id resume), + // send it back so the backend reattaches instead of creating + // a new env. The backend may still hand back a fresh ID if + // the old one expired — callers must compare the response. + ...(config.reuseEnvironmentId && { + environment_id: config.reuseEnvironmentId, + }), + }, + { + headers: getHeaders(token), + timeout: 15_000, + validateStatus: status => status < 500, + }, + ), + 'Registration', + ) + + handleErrorStatus(response.status, response.data, 'Registration') + debug( + `[bridge:api] POST /v1/environments/bridge -> ${response.status} environment_id=${response.data.environment_id}`, + ) + debug( + `[bridge:api] >>> ${debugBody({ machine_name: config.machineName, directory: config.dir, branch: config.branch, git_repo_url: config.gitRepoUrl, max_sessions: config.maxSessions, metadata: { worker_type: config.workerType } })}`, + ) + debug(`[bridge:api] <<< ${debugBody(response.data)}`) + return response.data + }, + + async pollForWork( + environmentId: string, + environmentSecret: string, + signal?: AbortSignal, + reclaimOlderThanMs?: number, + ): Promise { + validateBridgeId(environmentId, 'environmentId') + + // Save and reset so errors break the "consecutive empty" streak. + // Restored below when the response is truly empty. + const prevEmptyPolls = consecutiveEmptyPolls + consecutiveEmptyPolls = 0 + + const response = await axios.get( + `${deps.baseUrl}/v1/environments/${environmentId}/work/poll`, + { + headers: getHeaders(environmentSecret), + params: + reclaimOlderThanMs !== undefined + ? { reclaim_older_than_ms: reclaimOlderThanMs } + : undefined, + timeout: 10_000, + signal, + validateStatus: status => status < 500, + }, + ) + + handleErrorStatus(response.status, response.data, 'Poll') + + // Empty body or null = no work available + if (!response.data) { + consecutiveEmptyPolls = prevEmptyPolls + 1 + if ( + consecutiveEmptyPolls === 1 || + consecutiveEmptyPolls % EMPTY_POLL_LOG_INTERVAL === 0 + ) { + debug( + `[bridge:api] GET .../work/poll -> ${response.status} (no work, ${consecutiveEmptyPolls} consecutive empty polls)`, + ) + } + return null + } + + debug( + `[bridge:api] GET .../work/poll -> ${response.status} workId=${response.data.id} type=${response.data.data?.type}${response.data.data?.id ? ` sessionId=${response.data.data.id}` : ''}`, + ) + debug(`[bridge:api] <<< ${debugBody(response.data)}`) + return response.data + }, + + async acknowledgeWork( + environmentId: string, + workId: string, + sessionToken: string, + ): Promise { + validateBridgeId(environmentId, 'environmentId') + validateBridgeId(workId, 'workId') + + debug(`[bridge:api] POST .../work/${workId}/ack`) + + const response = await axios.post( + `${deps.baseUrl}/v1/environments/${environmentId}/work/${workId}/ack`, + {}, + { + headers: getHeaders(sessionToken), + timeout: 10_000, + validateStatus: s => s < 500, + }, + ) + + handleErrorStatus(response.status, response.data, 'Acknowledge') + debug(`[bridge:api] POST .../work/${workId}/ack -> ${response.status}`) + }, + + async stopWork( + environmentId: string, + workId: string, + force: boolean, + ): Promise { + validateBridgeId(environmentId, 'environmentId') + validateBridgeId(workId, 'workId') + + debug(`[bridge:api] POST .../work/${workId}/stop force=${force}`) + + const response = await withOAuthRetry( + (token: string) => + axios.post( + `${deps.baseUrl}/v1/environments/${environmentId}/work/${workId}/stop`, + { force }, + { + headers: getHeaders(token), + timeout: 10_000, + validateStatus: s => s < 500, + }, + ), + 'StopWork', + ) + + handleErrorStatus(response.status, response.data, 'StopWork') + debug(`[bridge:api] POST .../work/${workId}/stop -> ${response.status}`) + }, + + async deregisterEnvironment(environmentId: string): Promise { + validateBridgeId(environmentId, 'environmentId') + + debug(`[bridge:api] DELETE /v1/environments/bridge/${environmentId}`) + + const response = await withOAuthRetry( + (token: string) => + axios.delete( + `${deps.baseUrl}/v1/environments/bridge/${environmentId}`, + { + headers: getHeaders(token), + timeout: 10_000, + validateStatus: s => s < 500, + }, + ), + 'Deregister', + ) + + handleErrorStatus(response.status, response.data, 'Deregister') + debug( + `[bridge:api] DELETE /v1/environments/bridge/${environmentId} -> ${response.status}`, + ) + }, + + async archiveSession(sessionId: string): Promise { + validateBridgeId(sessionId, 'sessionId') + + debug(`[bridge:api] POST /v1/sessions/${sessionId}/archive`) + + const response = await withOAuthRetry( + (token: string) => + axios.post( + `${deps.baseUrl}/v1/sessions/${sessionId}/archive`, + {}, + { + headers: getHeaders(token), + timeout: 10_000, + validateStatus: s => s < 500, + }, + ), + 'ArchiveSession', + ) + + // 409 = already archived (idempotent, not an error) + if (response.status === 409) { + debug( + `[bridge:api] POST /v1/sessions/${sessionId}/archive -> 409 (already archived)`, + ) + return + } + + handleErrorStatus(response.status, response.data, 'ArchiveSession') + debug( + `[bridge:api] POST /v1/sessions/${sessionId}/archive -> ${response.status}`, + ) + }, + + async reconnectSession( + environmentId: string, + sessionId: string, + ): Promise { + validateBridgeId(environmentId, 'environmentId') + validateBridgeId(sessionId, 'sessionId') + + debug( + `[bridge:api] POST /v1/environments/${environmentId}/bridge/reconnect session_id=${sessionId}`, + ) + + const response = await withOAuthRetry( + (token: string) => + axios.post( + `${deps.baseUrl}/v1/environments/${environmentId}/bridge/reconnect`, + { session_id: sessionId }, + { + headers: getHeaders(token), + timeout: 10_000, + validateStatus: s => s < 500, + }, + ), + 'ReconnectSession', + ) + + handleErrorStatus(response.status, response.data, 'ReconnectSession') + debug(`[bridge:api] POST .../bridge/reconnect -> ${response.status}`) + }, + + async heartbeatWork( + environmentId: string, + workId: string, + sessionToken: string, + ): Promise<{ lease_extended: boolean; state: string }> { + validateBridgeId(environmentId, 'environmentId') + validateBridgeId(workId, 'workId') + + debug(`[bridge:api] POST .../work/${workId}/heartbeat`) + + const response = await axios.post<{ + lease_extended: boolean + state: string + last_heartbeat: string + ttl_seconds: number + }>( + `${deps.baseUrl}/v1/environments/${environmentId}/work/${workId}/heartbeat`, + {}, + { + headers: getHeaders(sessionToken), + timeout: 10_000, + validateStatus: s => s < 500, + }, + ) + + handleErrorStatus(response.status, response.data, 'Heartbeat') + debug( + `[bridge:api] POST .../work/${workId}/heartbeat -> ${response.status} lease_extended=${response.data.lease_extended} state=${response.data.state}`, + ) + return response.data + }, + + async sendPermissionResponseEvent( + sessionId: string, + event: PermissionResponseEvent, + sessionToken: string, + ): Promise { + validateBridgeId(sessionId, 'sessionId') + + debug( + `[bridge:api] POST /v1/sessions/${sessionId}/events type=${event.type}`, + ) + + const response = await axios.post( + `${deps.baseUrl}/v1/sessions/${sessionId}/events`, + { events: [event] }, + { + headers: getHeaders(sessionToken), + timeout: 10_000, + validateStatus: s => s < 500, + }, + ) + + handleErrorStatus( + response.status, + response.data, + 'SendPermissionResponseEvent', + ) + debug( + `[bridge:api] POST /v1/sessions/${sessionId}/events -> ${response.status}`, + ) + debug(`[bridge:api] >>> ${debugBody({ events: [event] })}`) + debug(`[bridge:api] <<< ${debugBody(response.data)}`) + }, + } +} + +function handleErrorStatus( + status: number, + data: unknown, + context: string, +): void { + if (status === 200 || status === 204) { + return + } + const detail = extractErrorDetail(data) + const errorType = extractErrorTypeFromData(data) + switch (status) { + case 401: + throw new BridgeFatalError( + `${context}: Authentication failed (401)${detail ? `: ${detail}` : ''}. ${BRIDGE_LOGIN_INSTRUCTION}`, + 401, + errorType, + ) + case 403: + throw new BridgeFatalError( + isExpiredErrorType(errorType) + ? 'Remote Control session has expired. Please restart with `claude remote-control` or /remote-control.' + : `${context}: Access denied (403)${detail ? `: ${detail}` : ''}. Check your organization permissions.`, + 403, + errorType, + ) + case 404: + throw new BridgeFatalError( + detail ?? + `${context}: Not found (404). Remote Control may not be available for this organization.`, + 404, + errorType, + ) + case 410: + throw new BridgeFatalError( + detail ?? + 'Remote Control session has expired. Please restart with `claude remote-control` or /remote-control.', + 410, + errorType ?? 'environment_expired', + ) + case 429: + throw new Error(`${context}: Rate limited (429). Polling too frequently.`) + default: + throw new Error( + `${context}: Failed with status ${status}${detail ? `: ${detail}` : ''}`, + ) + } +} + +/** Check whether an error type string indicates a session/environment expiry. */ +export function isExpiredErrorType(errorType: string | undefined): boolean { + if (!errorType) { + return false + } + return errorType.includes('expired') || errorType.includes('lifetime') +} + +/** + * Check whether a BridgeFatalError is a suppressible 403 permission error. + * These are 403 errors for scopes like 'external_poll_sessions' or operations + * like StopWork that fail because the user's role lacks 'environments:manage'. + * They don't affect core functionality and shouldn't be shown to users. + */ +export function isSuppressible403(err: BridgeFatalError): boolean { + if (err.status !== 403) { + return false + } + return ( + err.message.includes('external_poll_sessions') || + err.message.includes('environments:manage') + ) +} + +function extractErrorTypeFromData(data: unknown): string | undefined { + if (data && typeof data === 'object') { + if ( + 'error' in data && + data.error && + typeof data.error === 'object' && + 'type' in data.error && + typeof data.error.type === 'string' + ) { + return data.error.type + } + } + return undefined +} diff --git a/bridge/bridgeConfig.ts b/bridge/bridgeConfig.ts new file mode 100644 index 0000000..02f0876 --- /dev/null +++ b/bridge/bridgeConfig.ts @@ -0,0 +1,48 @@ +/** + * Shared bridge auth/URL resolution. Consolidates the ant-only + * CLAUDE_BRIDGE_* dev overrides that were previously copy-pasted across + * a dozen files — inboundAttachments, BriefTool/upload, bridgeMain, + * initReplBridge, remoteBridgeCore, daemon workers, /rename, + * /remote-control. + * + * Two layers: *Override() returns the ant-only env var (or undefined); + * the non-Override versions fall through to the real OAuth store/config. + * Callers that compose with a different auth source (e.g. daemon workers + * using IPC auth) use the Override getters directly. + */ + +import { getOauthConfig } from '../constants/oauth.js' +import { getClaudeAIOAuthTokens } from '../utils/auth.js' + +/** Ant-only dev override: CLAUDE_BRIDGE_OAUTH_TOKEN, else undefined. */ +export function getBridgeTokenOverride(): string | undefined { + return ( + (process.env.USER_TYPE === 'ant' && + process.env.CLAUDE_BRIDGE_OAUTH_TOKEN) || + undefined + ) +} + +/** Ant-only dev override: CLAUDE_BRIDGE_BASE_URL, else undefined. */ +export function getBridgeBaseUrlOverride(): string | undefined { + return ( + (process.env.USER_TYPE === 'ant' && process.env.CLAUDE_BRIDGE_BASE_URL) || + undefined + ) +} + +/** + * Access token for bridge API calls: dev override first, then the OAuth + * keychain. Undefined means "not logged in". + */ +export function getBridgeAccessToken(): string | undefined { + return getBridgeTokenOverride() ?? getClaudeAIOAuthTokens()?.accessToken +} + +/** + * Base URL for bridge API calls: dev override first, then the production + * OAuth config. Always returns a URL. + */ +export function getBridgeBaseUrl(): string { + return getBridgeBaseUrlOverride() ?? getOauthConfig().BASE_API_URL +} diff --git a/bridge/bridgeDebug.ts b/bridge/bridgeDebug.ts new file mode 100644 index 0000000..4d0f422 --- /dev/null +++ b/bridge/bridgeDebug.ts @@ -0,0 +1,135 @@ +import { logForDebugging } from '../utils/debug.js' +import { BridgeFatalError } from './bridgeApi.js' +import type { BridgeApiClient } from './types.js' + +/** + * Ant-only fault injection for manually testing bridge recovery paths. + * + * Real failure modes this targets (BQ 2026-03-12, 7-day window): + * poll 404 not_found_error — 147K sessions/week, dead onEnvironmentLost gate + * ws_closed 1002/1006 — 22K sessions/week, zombie poll after close + * register transient failure — residual: network blips during doReconnect + * + * Usage: /bridge-kick from the REPL while Remote Control is + * connected, then tail debug.log to watch the recovery machinery react. + * + * Module-level state is intentional here: one bridge per REPL process, the + * /bridge-kick slash command has no other way to reach into initBridgeCore's + * closures, and teardown clears the slot. + */ + +/** One-shot fault to inject on the next matching api call. */ +type BridgeFault = { + method: + | 'pollForWork' + | 'registerBridgeEnvironment' + | 'reconnectSession' + | 'heartbeatWork' + /** Fatal errors go through handleErrorStatus → BridgeFatalError. Transient + * errors surface as plain axios rejections (5xx / network). Recovery code + * distinguishes the two: fatal → teardown, transient → retry/backoff. */ + kind: 'fatal' | 'transient' + status: number + errorType?: string + /** Remaining injections. Decremented on consume; removed at 0. */ + count: number +} + +export type BridgeDebugHandle = { + /** Invoke the transport's permanent-close handler directly. Tests the + * ws_closed → reconnectEnvironmentWithSession escalation (#22148). */ + fireClose: (code: number) => void + /** Call reconnectEnvironmentWithSession() — same as SIGUSR2 but + * reachable from the slash command. */ + forceReconnect: () => void + /** Queue a fault for the next N calls to the named api method. */ + injectFault: (fault: BridgeFault) => void + /** Abort the at-capacity sleep so an injected poll fault lands + * immediately instead of up to 10min later. */ + wakePollLoop: () => void + /** env/session IDs for the debug.log grep. */ + describe: () => string +} + +let debugHandle: BridgeDebugHandle | null = null +const faultQueue: BridgeFault[] = [] + +export function registerBridgeDebugHandle(h: BridgeDebugHandle): void { + debugHandle = h +} + +export function clearBridgeDebugHandle(): void { + debugHandle = null + faultQueue.length = 0 +} + +export function getBridgeDebugHandle(): BridgeDebugHandle | null { + return debugHandle +} + +export function injectBridgeFault(fault: BridgeFault): void { + faultQueue.push(fault) + logForDebugging( + `[bridge:debug] Queued fault: ${fault.method} ${fault.kind}/${fault.status}${fault.errorType ? `/${fault.errorType}` : ''} ×${fault.count}`, + ) +} + +/** + * Wrap a BridgeApiClient so each call first checks the fault queue. If a + * matching fault is queued, throw the specified error instead of calling + * through. Delegates everything else to the real client. + * + * Only called when USER_TYPE === 'ant' — zero overhead in external builds. + */ +export function wrapApiForFaultInjection( + api: BridgeApiClient, +): BridgeApiClient { + function consume(method: BridgeFault['method']): BridgeFault | null { + const idx = faultQueue.findIndex(f => f.method === method) + if (idx === -1) return null + const fault = faultQueue[idx]! + fault.count-- + if (fault.count <= 0) faultQueue.splice(idx, 1) + return fault + } + + function throwFault(fault: BridgeFault, context: string): never { + logForDebugging( + `[bridge:debug] Injecting ${fault.kind} fault into ${context}: status=${fault.status} errorType=${fault.errorType ?? 'none'}`, + ) + if (fault.kind === 'fatal') { + throw new BridgeFatalError( + `[injected] ${context} ${fault.status}`, + fault.status, + fault.errorType, + ) + } + // Transient: mimic an axios rejection (5xx / network). No .status on + // the error itself — that's how the catch blocks distinguish. + throw new Error(`[injected transient] ${context} ${fault.status}`) + } + + return { + ...api, + async pollForWork(envId, secret, signal, reclaimMs) { + const f = consume('pollForWork') + if (f) throwFault(f, 'Poll') + return api.pollForWork(envId, secret, signal, reclaimMs) + }, + async registerBridgeEnvironment(config) { + const f = consume('registerBridgeEnvironment') + if (f) throwFault(f, 'Registration') + return api.registerBridgeEnvironment(config) + }, + async reconnectSession(envId, sessionId) { + const f = consume('reconnectSession') + if (f) throwFault(f, 'ReconnectSession') + return api.reconnectSession(envId, sessionId) + }, + async heartbeatWork(envId, workId, token) { + const f = consume('heartbeatWork') + if (f) throwFault(f, 'Heartbeat') + return api.heartbeatWork(envId, workId, token) + }, + } +} diff --git a/bridge/bridgeEnabled.ts b/bridge/bridgeEnabled.ts new file mode 100644 index 0000000..b6eec41 --- /dev/null +++ b/bridge/bridgeEnabled.ts @@ -0,0 +1,202 @@ +import { feature } from 'bun:bundle' +import { + checkGate_CACHED_OR_BLOCKING, + getDynamicConfig_CACHED_MAY_BE_STALE, + getFeatureValue_CACHED_MAY_BE_STALE, +} from '../services/analytics/growthbook.js' +// Namespace import breaks the bridgeEnabled → auth → config → bridgeEnabled +// cycle — authModule.foo is a live binding, so by the time the helpers below +// call it, auth.js is fully loaded. Previously used require() for the same +// deferral, but require() hits a CJS cache that diverges from the ESM +// namespace after mock.module() (daemon/auth.test.ts), breaking spyOn. +import * as authModule from '../utils/auth.js' +import { isEnvTruthy } from '../utils/envUtils.js' +import { lt } from '../utils/semver.js' + +/** + * Runtime check for bridge mode entitlement. + * + * Remote Control requires a claude.ai subscription (the bridge auths to CCR + * with the claude.ai OAuth token). isClaudeAISubscriber() excludes + * Bedrock/Vertex/Foundry, apiKeyHelper/gateway deployments, env-var API keys, + * and Console API logins — none of which have the OAuth token CCR needs. + * See github.com/deshaw/anthropic-issues/issues/24. + * + * The `feature('BRIDGE_MODE')` guard ensures the GrowthBook string literal + * is only referenced when bridge mode is enabled at build time. + */ +export function isBridgeEnabled(): boolean { + // Positive ternary pattern — see docs/feature-gating.md. + // Negative pattern (if (!feature(...)) return) does not eliminate + // inline string literals from external builds. + return feature('BRIDGE_MODE') + ? isClaudeAISubscriber() && + getFeatureValue_CACHED_MAY_BE_STALE('tengu_ccr_bridge', false) + : false +} + +/** + * Blocking entitlement check for Remote Control. + * + * Returns cached `true` immediately (fast path). If the disk cache says + * `false` or is missing, awaits GrowthBook init and fetches the fresh + * server value (slow path, max ~5s), then writes it to disk. + * + * Use at entitlement gates where a stale `false` would unfairly block access. + * For user-facing error paths, prefer `getBridgeDisabledReason()` which gives + * a specific diagnostic. For render-body UI visibility checks, use + * `isBridgeEnabled()` instead. + */ +export async function isBridgeEnabledBlocking(): Promise { + return feature('BRIDGE_MODE') + ? isClaudeAISubscriber() && + (await checkGate_CACHED_OR_BLOCKING('tengu_ccr_bridge')) + : false +} + +/** + * Diagnostic message for why Remote Control is unavailable, or null if + * it's enabled. Call this instead of a bare `isBridgeEnabledBlocking()` + * check when you need to show the user an actionable error. + * + * The GrowthBook gate targets on organizationUUID, which comes from + * config.oauthAccount — populated by /api/oauth/profile during login. + * That endpoint requires the user:profile scope. Tokens without it + * (setup-token, CLAUDE_CODE_OAUTH_TOKEN env var, or pre-scope-expansion + * logins) leave oauthAccount unpopulated, so the gate falls back to + * false and users see a dead-end "not enabled" message with no hint + * that re-login would fix it. See CC-1165 / gh-33105. + */ +export async function getBridgeDisabledReason(): Promise { + if (feature('BRIDGE_MODE')) { + if (!isClaudeAISubscriber()) { + return 'Remote Control requires a claude.ai subscription. Run `claude auth login` to sign in with your claude.ai account.' + } + if (!hasProfileScope()) { + return 'Remote Control requires a full-scope login token. Long-lived tokens (from `claude setup-token` or CLAUDE_CODE_OAUTH_TOKEN) are limited to inference-only for security reasons. Run `claude auth login` to use Remote Control.' + } + if (!getOauthAccountInfo()?.organizationUuid) { + return 'Unable to determine your organization for Remote Control eligibility. Run `claude auth login` to refresh your account information.' + } + if (!(await checkGate_CACHED_OR_BLOCKING('tengu_ccr_bridge'))) { + return 'Remote Control is not yet enabled for your account.' + } + return null + } + return 'Remote Control is not available in this build.' +} + +// try/catch: main.tsx:5698 calls isBridgeEnabled() while defining the Commander +// program, before enableConfigs() runs. isClaudeAISubscriber() → getGlobalConfig() +// throws "Config accessed before allowed" there. Pre-config, no OAuth token can +// exist anyway — false is correct. Same swallow getFeatureValue_CACHED_MAY_BE_STALE +// already does at growthbook.ts:775-780. +function isClaudeAISubscriber(): boolean { + try { + return authModule.isClaudeAISubscriber() + } catch { + return false + } +} +function hasProfileScope(): boolean { + try { + return authModule.hasProfileScope() + } catch { + return false + } +} +function getOauthAccountInfo(): ReturnType< + typeof authModule.getOauthAccountInfo +> { + try { + return authModule.getOauthAccountInfo() + } catch { + return undefined + } +} + +/** + * Runtime check for the env-less (v2) REPL bridge path. + * Returns true when the GrowthBook flag `tengu_bridge_repl_v2` is enabled. + * + * This gates which implementation initReplBridge uses — NOT whether bridge + * is available at all (see isBridgeEnabled above). Daemon/print paths stay + * on the env-based implementation regardless of this gate. + */ +export function isEnvLessBridgeEnabled(): boolean { + return feature('BRIDGE_MODE') + ? getFeatureValue_CACHED_MAY_BE_STALE('tengu_bridge_repl_v2', false) + : false +} + +/** + * Kill-switch for the `cse_*` → `session_*` client-side retag shim. + * + * The shim exists because compat/convert.go:27 validates TagSession and the + * claude.ai frontend routes on `session_*`, while v2 worker endpoints hand out + * `cse_*`. Once the server tags by environment_kind and the frontend accepts + * `cse_*` directly, flip this to false to make toCompatSessionId a no-op. + * Defaults to true — the shim stays active until explicitly disabled. + */ +export function isCseShimEnabled(): boolean { + return feature('BRIDGE_MODE') + ? getFeatureValue_CACHED_MAY_BE_STALE( + 'tengu_bridge_repl_v2_cse_shim_enabled', + true, + ) + : true +} + +/** + * Returns an error message if the current CLI version is below the + * minimum required for the v1 (env-based) Remote Control path, or null if the + * version is fine. The v2 (env-less) path uses checkEnvLessBridgeMinVersion() + * in envLessBridgeConfig.ts instead — the two implementations have independent + * version floors. + * + * Uses cached (non-blocking) GrowthBook config. If GrowthBook hasn't + * loaded yet, the default '0.0.0' means the check passes — a safe fallback. + */ +export function checkBridgeMinVersion(): string | null { + // Positive pattern — see docs/feature-gating.md. + // Negative pattern (if (!feature(...)) return) does not eliminate + // inline string literals from external builds. + if (feature('BRIDGE_MODE')) { + const config = getDynamicConfig_CACHED_MAY_BE_STALE<{ + minVersion: string + }>('tengu_bridge_min_version', { minVersion: '0.0.0' }) + if (config.minVersion && lt(MACRO.VERSION, config.minVersion)) { + return `Your version of Claude Code (${MACRO.VERSION}) is too old for Remote Control.\nVersion ${config.minVersion} or higher is required. Run \`claude update\` to update.` + } + } + return null +} + +/** + * Default for remoteControlAtStartup when the user hasn't explicitly set it. + * When the CCR_AUTO_CONNECT build flag is present (ant-only) and the + * tengu_cobalt_harbor GrowthBook gate is on, all sessions connect to CCR by + * default — the user can still opt out by setting remoteControlAtStartup=false + * in config (explicit settings always win over this default). + * + * Defined here rather than in config.ts to avoid a direct + * config.ts → growthbook.ts import cycle (growthbook.ts → user.ts → config.ts). + */ +export function getCcrAutoConnectDefault(): boolean { + return feature('CCR_AUTO_CONNECT') + ? getFeatureValue_CACHED_MAY_BE_STALE('tengu_cobalt_harbor', false) + : false +} + +/** + * Opt-in CCR mirror mode — every local session spawns an outbound-only + * Remote Control session that receives forwarded events. Separate from + * getCcrAutoConnectDefault (bidirectional Remote Control). Env var wins for + * local opt-in; GrowthBook controls rollout. + */ +export function isCcrMirrorEnabled(): boolean { + return feature('CCR_MIRROR') + ? isEnvTruthy(process.env.CLAUDE_CODE_CCR_MIRROR) || + getFeatureValue_CACHED_MAY_BE_STALE('tengu_ccr_mirror', false) + : false +} diff --git a/bridge/bridgeMain.ts b/bridge/bridgeMain.ts new file mode 100644 index 0000000..7aeacaf --- /dev/null +++ b/bridge/bridgeMain.ts @@ -0,0 +1,2999 @@ +import { feature } from 'bun:bundle' +import { randomUUID } from 'crypto' +import { hostname, tmpdir } from 'os' +import { basename, join, resolve } from 'path' +import { getRemoteSessionUrl } from '../constants/product.js' +import { shutdownDatadog } from '../services/analytics/datadog.js' +import { shutdown1PEventLogging } from '../services/analytics/firstPartyEventLogger.js' +import { checkGate_CACHED_OR_BLOCKING } from '../services/analytics/growthbook.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, + logEventAsync, +} from '../services/analytics/index.js' +import { isInBundledMode } from '../utils/bundledMode.js' +import { logForDebugging } from '../utils/debug.js' +import { logForDiagnosticsNoPII } from '../utils/diagLogs.js' +import { isEnvTruthy, isInProtectedNamespace } from '../utils/envUtils.js' +import { errorMessage } from '../utils/errors.js' +import { truncateToWidth } from '../utils/format.js' +import { logError } from '../utils/log.js' +import { sleep } from '../utils/sleep.js' +import { createAgentWorktree, removeAgentWorktree } from '../utils/worktree.js' +import { + BridgeFatalError, + createBridgeApiClient, + isExpiredErrorType, + isSuppressible403, + validateBridgeId, +} from './bridgeApi.js' +import { formatDuration } from './bridgeStatusUtil.js' +import { createBridgeLogger } from './bridgeUI.js' +import { createCapacityWake } from './capacityWake.js' +import { describeAxiosError } from './debugUtils.js' +import { createTokenRefreshScheduler } from './jwtUtils.js' +import { getPollIntervalConfig } from './pollConfig.js' +import { toCompatSessionId, toInfraSessionId } from './sessionIdCompat.js' +import { createSessionSpawner, safeFilenameId } from './sessionRunner.js' +import { getTrustedDeviceToken } from './trustedDevice.js' +import { + BRIDGE_LOGIN_ERROR, + type BridgeApiClient, + type BridgeConfig, + type BridgeLogger, + DEFAULT_SESSION_TIMEOUT_MS, + type SessionDoneStatus, + type SessionHandle, + type SessionSpawner, + type SessionSpawnOpts, + type SpawnMode, +} from './types.js' +import { + buildCCRv2SdkUrl, + buildSdkUrl, + decodeWorkSecret, + registerWorker, + sameSessionId, +} from './workSecret.js' + +export type BackoffConfig = { + connInitialMs: number + connCapMs: number + connGiveUpMs: number + generalInitialMs: number + generalCapMs: number + generalGiveUpMs: number + /** SIGTERM→SIGKILL grace period on shutdown. Default 30s. */ + shutdownGraceMs?: number + /** stopWorkWithRetry base delay (1s/2s/4s backoff). Default 1000ms. */ + stopWorkBaseDelayMs?: number +} + +const DEFAULT_BACKOFF: BackoffConfig = { + connInitialMs: 2_000, + connCapMs: 120_000, // 2 minutes + connGiveUpMs: 600_000, // 10 minutes + generalInitialMs: 500, + generalCapMs: 30_000, + generalGiveUpMs: 600_000, // 10 minutes +} + +/** Status update interval for the live display (ms). */ +const STATUS_UPDATE_INTERVAL_MS = 1_000 +const SPAWN_SESSIONS_DEFAULT = 32 + +/** + * GrowthBook gate for multi-session spawn modes (--spawn / --capacity / --create-session-in-dir). + * Sibling of tengu_ccr_bridge_multi_environment (multiple envs per host:dir) — + * this one enables multiple sessions per environment. + * Rollout staged via targeting rules: ants first, then gradual external. + * + * Uses the blocking gate check so a stale disk-cache miss doesn't unfairly + * deny access. The fast path (cache has true) is still instant; only the + * cold-start path awaits the server fetch, and that fetch also seeds the + * disk cache for next time. + */ +async function isMultiSessionSpawnEnabled(): Promise { + return checkGate_CACHED_OR_BLOCKING('tengu_ccr_bridge_multi_session') +} + +/** + * Returns the threshold for detecting system sleep/wake in the poll loop. + * Must exceed the max backoff cap — otherwise normal backoff delays trigger + * false sleep detection (resetting the error budget indefinitely). Using + * 2× the connection backoff cap, matching the pattern in WebSocketTransport + * and replBridge. + */ +function pollSleepDetectionThresholdMs(backoff: BackoffConfig): number { + return backoff.connCapMs * 2 +} + +/** + * Returns the args that must precede CLI flags when spawning a child claude + * process. In compiled binaries, process.execPath is the claude binary itself + * and args go directly to it. In npm installs (node running cli.js), + * process.execPath is the node runtime — the child spawn must pass the script + * path as the first arg, otherwise node interprets --sdk-url as a node option + * and exits with "bad option: --sdk-url". See anthropics/claude-code#28334. + */ +function spawnScriptArgs(): string[] { + if (isInBundledMode() || !process.argv[1]) { + return [] + } + return [process.argv[1]] +} + +/** Attempt to spawn a session; returns error string if spawn throws. */ +function safeSpawn( + spawner: SessionSpawner, + opts: SessionSpawnOpts, + dir: string, +): SessionHandle | string { + try { + return spawner.spawn(opts, dir) + } catch (err) { + const errMsg = errorMessage(err) + logError(new Error(`Session spawn failed: ${errMsg}`)) + return errMsg + } +} + +export async function runBridgeLoop( + config: BridgeConfig, + environmentId: string, + environmentSecret: string, + api: BridgeApiClient, + spawner: SessionSpawner, + logger: BridgeLogger, + signal: AbortSignal, + backoffConfig: BackoffConfig = DEFAULT_BACKOFF, + initialSessionId?: string, + getAccessToken?: () => string | undefined | Promise, +): Promise { + // Local abort controller so that onSessionDone can stop the poll loop. + // Linked to the incoming signal so external aborts also work. + const controller = new AbortController() + if (signal.aborted) { + controller.abort() + } else { + signal.addEventListener('abort', () => controller.abort(), { once: true }) + } + const loopSignal = controller.signal + + const activeSessions = new Map() + const sessionStartTimes = new Map() + const sessionWorkIds = new Map() + // Compat-surface ID (session_*) computed once at spawn and cached so + // cleanup and status-update ticks use the same key regardless of whether + // the tengu_bridge_repl_v2_cse_shim_enabled gate flips mid-session. + const sessionCompatIds = new Map() + // Session ingress JWTs for heartbeat auth, keyed by sessionId. + // Stored separately from handle.accessToken because the token refresh + // scheduler overwrites that field with the OAuth token (~3h55m in). + const sessionIngressTokens = new Map() + const sessionTimers = new Map>() + const completedWorkIds = new Set() + const sessionWorktrees = new Map< + string, + { + worktreePath: string + worktreeBranch?: string + gitRoot?: string + hookBased?: boolean + } + >() + // Track sessions killed by the timeout watchdog so onSessionDone can + // distinguish them from server-initiated or shutdown interrupts. + const timedOutSessions = new Set() + // Sessions that already have a title (server-set or bridge-derived) so + // onFirstUserMessage doesn't clobber a user-assigned --name / web rename. + // Keyed by compatSessionId to match logger.setSessionTitle's key. + const titledSessions = new Set() + // Signal to wake the at-capacity sleep early when a session completes, + // so the bridge can immediately accept new work. + const capacityWake = createCapacityWake(loopSignal) + + /** + * Heartbeat all active work items. + * Returns 'ok' if at least one heartbeat succeeded, 'auth_failed' if any + * got a 401/403 (JWT expired — re-queued via reconnectSession so the next + * poll delivers fresh work), or 'failed' if all failed for other reasons. + */ + async function heartbeatActiveWorkItems(): Promise< + 'ok' | 'auth_failed' | 'fatal' | 'failed' + > { + let anySuccess = false + let anyFatal = false + const authFailedSessions: string[] = [] + for (const [sessionId] of activeSessions) { + const workId = sessionWorkIds.get(sessionId) + const ingressToken = sessionIngressTokens.get(sessionId) + if (!workId || !ingressToken) { + continue + } + try { + await api.heartbeatWork(environmentId, workId, ingressToken) + anySuccess = true + } catch (err) { + logForDebugging( + `[bridge:heartbeat] Failed for sessionId=${sessionId} workId=${workId}: ${errorMessage(err)}`, + ) + if (err instanceof BridgeFatalError) { + logEvent('tengu_bridge_heartbeat_error', { + status: + err.status as unknown as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + error_type: (err.status === 401 || err.status === 403 + ? 'auth_failed' + : 'fatal') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + if (err.status === 401 || err.status === 403) { + authFailedSessions.push(sessionId) + } else { + // 404/410 = environment expired or deleted — no point retrying + anyFatal = true + } + } + } + } + // JWT expired → trigger server-side re-dispatch. Without this, work stays + // ACK'd out of the Redis PEL and poll returns empty forever (CC-1263). + // The existingHandle path below delivers the fresh token to the child. + // sessionId is already in the format /bridge/reconnect expects: it comes + // from work.data.id, which matches the server's EnvironmentInstance store + // (cse_* under the compat gate, session_* otherwise). + for (const sessionId of authFailedSessions) { + logger.logVerbose( + `Session ${sessionId} token expired — re-queuing via bridge/reconnect`, + ) + try { + await api.reconnectSession(environmentId, sessionId) + logForDebugging( + `[bridge:heartbeat] Re-queued sessionId=${sessionId} via bridge/reconnect`, + ) + } catch (err) { + logger.logError( + `Failed to refresh session ${sessionId} token: ${errorMessage(err)}`, + ) + logForDebugging( + `[bridge:heartbeat] reconnectSession(${sessionId}) failed: ${errorMessage(err)}`, + { level: 'error' }, + ) + } + } + if (anyFatal) { + return 'fatal' + } + if (authFailedSessions.length > 0) { + return 'auth_failed' + } + return anySuccess ? 'ok' : 'failed' + } + + // Sessions spawned with CCR v2 env vars. v2 children cannot use OAuth + // tokens (CCR worker endpoints validate the JWT's session_id claim, + // register_worker.go:32), so onRefresh triggers server re-dispatch + // instead — the next poll delivers fresh work with a new JWT via the + // existingHandle path below. + const v2Sessions = new Set() + + // Proactive token refresh: schedules a timer 5min before the session + // ingress JWT expires. v1 delivers OAuth directly; v2 calls + // reconnectSession to trigger server re-dispatch (CC-1263: without + // this, v2 daemon sessions silently die at ~5h since the server does + // not auto-re-dispatch ACK'd work on lease expiry). + const tokenRefresh = getAccessToken + ? createTokenRefreshScheduler({ + getAccessToken, + onRefresh: (sessionId, oauthToken) => { + const handle = activeSessions.get(sessionId) + if (!handle) { + return + } + if (v2Sessions.has(sessionId)) { + logger.logVerbose( + `Refreshing session ${sessionId} token via bridge/reconnect`, + ) + void api + .reconnectSession(environmentId, sessionId) + .catch((err: unknown) => { + logger.logError( + `Failed to refresh session ${sessionId} token: ${errorMessage(err)}`, + ) + logForDebugging( + `[bridge:token] reconnectSession(${sessionId}) failed: ${errorMessage(err)}`, + { level: 'error' }, + ) + }) + } else { + handle.updateAccessToken(oauthToken) + } + }, + label: 'bridge', + }) + : null + const loopStartTime = Date.now() + // Track all in-flight cleanup promises (stopWork, worktree removal) so + // the shutdown sequence can await them before process.exit(). + const pendingCleanups = new Set>() + function trackCleanup(p: Promise): void { + pendingCleanups.add(p) + void p.finally(() => pendingCleanups.delete(p)) + } + let connBackoff = 0 + let generalBackoff = 0 + let connErrorStart: number | null = null + let generalErrorStart: number | null = null + let lastPollErrorTime: number | null = null + let statusUpdateTimer: ReturnType | null = null + // Set by BridgeFatalError and give-up paths so the shutdown block can + // skip the resume message (resume is impossible after env expiry/auth + // failure/sustained connection errors). + let fatalExit = false + + logForDebugging( + `[bridge:work] Starting poll loop spawnMode=${config.spawnMode} maxSessions=${config.maxSessions} environmentId=${environmentId}`, + ) + logForDiagnosticsNoPII('info', 'bridge_loop_started', { + max_sessions: config.maxSessions, + spawn_mode: config.spawnMode, + }) + + // For ant users, show where session debug logs will land so they can tail them. + // sessionRunner.ts uses the same base path. File appears once a session spawns. + if (process.env.USER_TYPE === 'ant') { + let debugGlob: string + if (config.debugFile) { + const ext = config.debugFile.lastIndexOf('.') + debugGlob = + ext > 0 + ? `${config.debugFile.slice(0, ext)}-*${config.debugFile.slice(ext)}` + : `${config.debugFile}-*` + } else { + debugGlob = join(tmpdir(), 'claude', 'bridge-session-*.log') + } + logger.setDebugLogPath(debugGlob) + } + + logger.printBanner(config, environmentId) + + // Seed the logger's session count + spawn mode before any render. Without + // this, setAttached() below renders with the logger's default sessionMax=1, + // showing "Capacity: 0/1" until the status ticker kicks in (which is gated + // by !initialSessionId and only starts after the poll loop picks up work). + logger.updateSessionCount(0, config.maxSessions, config.spawnMode) + + // If an initial session was pre-created, show its URL from the start so + // the user can click through immediately (matching /remote-control behavior). + if (initialSessionId) { + logger.setAttached(initialSessionId) + } + + /** Refresh the inline status display. Shows idle or active depending on state. */ + function updateStatusDisplay(): void { + // Push the session count (no-op when maxSessions === 1) so the + // next renderStatusLine tick shows the current count. + logger.updateSessionCount( + activeSessions.size, + config.maxSessions, + config.spawnMode, + ) + + // Push per-session activity into the multi-session display. + for (const [sid, handle] of activeSessions) { + const act = handle.currentActivity + if (act) { + logger.updateSessionActivity(sessionCompatIds.get(sid) ?? sid, act) + } + } + + if (activeSessions.size === 0) { + logger.updateIdleStatus() + return + } + + // Show the most recently started session that is still actively working. + // Sessions whose current activity is 'result' or 'error' are between + // turns — the CLI emitted its result but the process stays alive waiting + // for the next user message. Skip updating so the status line keeps + // whatever state it had (Attached / session title). + const [sessionId, handle] = [...activeSessions.entries()].pop()! + const startTime = sessionStartTimes.get(sessionId) + if (!startTime) return + + const activity = handle.currentActivity + if (!activity || activity.type === 'result' || activity.type === 'error') { + // Session is between turns — keep current status (Attached/titled). + // In multi-session mode, still refresh so bullet-list activities stay current. + if (config.maxSessions > 1) logger.refreshDisplay() + return + } + + const elapsed = formatDuration(Date.now() - startTime) + + // Build trail from recent tool activities (last 5) + const trail = handle.activities + .filter(a => a.type === 'tool_start') + .slice(-5) + .map(a => a.summary) + + logger.updateSessionStatus(sessionId, elapsed, activity, trail) + } + + /** Start the status display update ticker. */ + function startStatusUpdates(): void { + stopStatusUpdates() + // Call immediately so the first transition (e.g. Connecting → Ready) + // happens without delay, avoiding concurrent timer races. + updateStatusDisplay() + statusUpdateTimer = setInterval( + updateStatusDisplay, + STATUS_UPDATE_INTERVAL_MS, + ) + } + + /** Stop the status display update ticker. */ + function stopStatusUpdates(): void { + if (statusUpdateTimer) { + clearInterval(statusUpdateTimer) + statusUpdateTimer = null + } + } + + function onSessionDone( + sessionId: string, + startTime: number, + handle: SessionHandle, + ): (status: SessionDoneStatus) => void { + return (rawStatus: SessionDoneStatus): void => { + const workId = sessionWorkIds.get(sessionId) + activeSessions.delete(sessionId) + sessionStartTimes.delete(sessionId) + sessionWorkIds.delete(sessionId) + sessionIngressTokens.delete(sessionId) + const compatId = sessionCompatIds.get(sessionId) ?? sessionId + sessionCompatIds.delete(sessionId) + logger.removeSession(compatId) + titledSessions.delete(compatId) + v2Sessions.delete(sessionId) + // Clear per-session timeout timer + const timer = sessionTimers.get(sessionId) + if (timer) { + clearTimeout(timer) + sessionTimers.delete(sessionId) + } + // Clear token refresh timer + tokenRefresh?.cancel(sessionId) + // Wake the at-capacity sleep so the bridge can accept new work immediately + capacityWake.wake() + + // If the session was killed by the timeout watchdog, treat it as a + // failed session (not a server/shutdown interrupt) so we still call + // stopWork and archiveSession below. + const wasTimedOut = timedOutSessions.delete(sessionId) + const status: SessionDoneStatus = + wasTimedOut && rawStatus === 'interrupted' ? 'failed' : rawStatus + const durationMs = Date.now() - startTime + + logForDebugging( + `[bridge:session] sessionId=${sessionId} workId=${workId ?? 'unknown'} exited status=${status} duration=${formatDuration(durationMs)}`, + ) + logEvent('tengu_bridge_session_done', { + status: + status as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + duration_ms: durationMs, + }) + logForDiagnosticsNoPII('info', 'bridge_session_done', { + status, + duration_ms: durationMs, + }) + + // Clear the status display before printing final log + logger.clearStatus() + stopStatusUpdates() + + // Build error message from stderr if available + const stderrSummary = + handle.lastStderr.length > 0 ? handle.lastStderr.join('\n') : undefined + let failureMessage: string | undefined + + switch (status) { + case 'completed': + logger.logSessionComplete(sessionId, durationMs) + break + case 'failed': + // Skip failure log during shutdown — the child exits non-zero when + // killed, which is expected and not a real failure. + // Also skip for timeout-killed sessions — the timeout watchdog + // already logged a clear timeout message. + if (!wasTimedOut && !loopSignal.aborted) { + failureMessage = stderrSummary ?? 'Process exited with error' + logger.logSessionFailed(sessionId, failureMessage) + logError(new Error(`Bridge session failed: ${failureMessage}`)) + } + break + case 'interrupted': + logger.logVerbose(`Session ${sessionId} interrupted`) + break + } + + // Notify the server that this work item is done. Skip for interrupted + // sessions — interrupts are either server-initiated (the server already + // knows) or caused by bridge shutdown (which calls stopWork() separately). + if (status !== 'interrupted' && workId) { + trackCleanup( + stopWorkWithRetry( + api, + environmentId, + workId, + logger, + backoffConfig.stopWorkBaseDelayMs, + ), + ) + completedWorkIds.add(workId) + } + + // Clean up worktree if one was created for this session + const wt = sessionWorktrees.get(sessionId) + if (wt) { + sessionWorktrees.delete(sessionId) + trackCleanup( + removeAgentWorktree( + wt.worktreePath, + wt.worktreeBranch, + wt.gitRoot, + wt.hookBased, + ).catch((err: unknown) => + logger.logVerbose( + `Failed to remove worktree ${wt.worktreePath}: ${errorMessage(err)}`, + ), + ), + ) + } + + // Lifecycle decision: in multi-session mode, keep the bridge running + // after a session completes. In single-session mode, abort the poll + // loop so the bridge exits cleanly. + if (status !== 'interrupted' && !loopSignal.aborted) { + if (config.spawnMode !== 'single-session') { + // Multi-session: archive the completed session so it doesn't linger + // as stale in the web UI. archiveSession is idempotent (409 if already + // archived), so double-archiving at shutdown is safe. + // sessionId arrived as cse_* from the work poll (infrastructure-layer + // tag). archiveSession hits /v1/sessions/{id}/archive which is the + // compat surface and validates TagSession (session_*). Re-tag — same + // UUID underneath. + trackCleanup( + api + .archiveSession(compatId) + .catch((err: unknown) => + logger.logVerbose( + `Failed to archive session ${sessionId}: ${errorMessage(err)}`, + ), + ), + ) + logForDebugging( + `[bridge:session] Session ${status}, returning to idle (multi-session mode)`, + ) + } else { + // Single-session: coupled lifecycle — tear down environment + logForDebugging( + `[bridge:session] Session ${status}, aborting poll loop to tear down environment`, + ) + controller.abort() + return + } + } + + if (!loopSignal.aborted) { + startStatusUpdates() + } + } + } + + // Start the idle status display immediately — unless we have a pre-created + // session, in which case setAttached() already set up the display and the + // poll loop will start status updates when it picks up the session. + if (!initialSessionId) { + startStatusUpdates() + } + + while (!loopSignal.aborted) { + // Fetched once per iteration — the GrowthBook cache refreshes every + // 5 min, so a loop running at the at-capacity rate picks up config + // changes within one sleep cycle. + const pollConfig = getPollIntervalConfig() + + try { + const work = await api.pollForWork( + environmentId, + environmentSecret, + loopSignal, + pollConfig.reclaim_older_than_ms, + ) + + // Log reconnection if we were previously disconnected + const wasDisconnected = + connErrorStart !== null || generalErrorStart !== null + if (wasDisconnected) { + const disconnectedMs = + Date.now() - (connErrorStart ?? generalErrorStart ?? Date.now()) + logger.logReconnected(disconnectedMs) + logForDebugging( + `[bridge:poll] Reconnected after ${formatDuration(disconnectedMs)}`, + ) + logEvent('tengu_bridge_reconnected', { + disconnected_ms: disconnectedMs, + }) + } + + connBackoff = 0 + generalBackoff = 0 + connErrorStart = null + generalErrorStart = null + lastPollErrorTime = null + + // Null response = no work available in the queue. + // Add a minimum delay to avoid hammering the server. + if (!work) { + // Use live check (not a snapshot) since sessions can end during poll. + const atCap = activeSessions.size >= config.maxSessions + if (atCap) { + const atCapMs = pollConfig.multisession_poll_interval_ms_at_capacity + // Heartbeat loops WITHOUT polling. When at-capacity polling is also + // enabled (atCapMs > 0), the loop tracks a deadline and breaks out + // to poll at that interval — heartbeat and poll compose instead of + // one suppressing the other. We break out to poll when: + // - Poll deadline reached (atCapMs > 0 only) + // - Auth fails (JWT expired → poll refreshes tokens) + // - Capacity wake fires (session ended → poll for new work) + // - Loop aborted (shutdown) + if (pollConfig.non_exclusive_heartbeat_interval_ms > 0) { + logEvent('tengu_bridge_heartbeat_mode_entered', { + active_sessions: activeSessions.size, + heartbeat_interval_ms: + pollConfig.non_exclusive_heartbeat_interval_ms, + }) + // Deadline computed once at entry — GB updates to atCapMs don't + // shift an in-flight deadline (next entry picks up the new value). + const pollDeadline = atCapMs > 0 ? Date.now() + atCapMs : null + let hbResult: 'ok' | 'auth_failed' | 'fatal' | 'failed' = 'ok' + let hbCycles = 0 + while ( + !loopSignal.aborted && + activeSessions.size >= config.maxSessions && + (pollDeadline === null || Date.now() < pollDeadline) + ) { + // Re-read config each cycle so GrowthBook updates take effect + const hbConfig = getPollIntervalConfig() + if (hbConfig.non_exclusive_heartbeat_interval_ms <= 0) break + + // Capture capacity signal BEFORE the async heartbeat call so + // a session ending during the HTTP request is caught by the + // subsequent sleep (instead of being lost to a replaced controller). + const cap = capacityWake.signal() + + hbResult = await heartbeatActiveWorkItems() + if (hbResult === 'auth_failed' || hbResult === 'fatal') { + cap.cleanup() + break + } + + hbCycles++ + await sleep( + hbConfig.non_exclusive_heartbeat_interval_ms, + cap.signal, + ) + cap.cleanup() + } + + // Determine exit reason for telemetry + const exitReason = + hbResult === 'auth_failed' || hbResult === 'fatal' + ? hbResult + : loopSignal.aborted + ? 'shutdown' + : activeSessions.size < config.maxSessions + ? 'capacity_changed' + : pollDeadline !== null && Date.now() >= pollDeadline + ? 'poll_due' + : 'config_disabled' + logEvent('tengu_bridge_heartbeat_mode_exited', { + reason: + exitReason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + heartbeat_cycles: hbCycles, + active_sessions: activeSessions.size, + }) + if (exitReason === 'poll_due') { + // bridgeApi throttles empty-poll logs (EMPTY_POLL_LOG_INTERVAL=100) + // so the once-per-10min poll_due poll is invisible at counter=2. + // Log it here so verification runs see both endpoints in the debug log. + logForDebugging( + `[bridge:poll] Heartbeat poll_due after ${hbCycles} cycles — falling through to pollForWork`, + ) + } + + // On auth_failed or fatal, sleep before polling to avoid a tight + // poll+heartbeat loop. Auth_failed: heartbeatActiveWorkItems + // already called reconnectSession — the sleep gives the server + // time to propagate the re-queue. Fatal (404/410): may be a + // single work item GCd while the environment is still valid. + // Use atCapMs if enabled, else the heartbeat interval as a floor + // (guaranteed > 0 here) so heartbeat-only configs don't tight-loop. + if (hbResult === 'auth_failed' || hbResult === 'fatal') { + const cap = capacityWake.signal() + await sleep( + atCapMs > 0 + ? atCapMs + : pollConfig.non_exclusive_heartbeat_interval_ms, + cap.signal, + ) + cap.cleanup() + } + } else if (atCapMs > 0) { + // Heartbeat disabled: slow poll as liveness signal. + const cap = capacityWake.signal() + await sleep(atCapMs, cap.signal) + cap.cleanup() + } + } else { + const interval = + activeSessions.size > 0 + ? pollConfig.multisession_poll_interval_ms_partial_capacity + : pollConfig.multisession_poll_interval_ms_not_at_capacity + await sleep(interval, loopSignal) + } + continue + } + + // At capacity — we polled to keep the heartbeat alive, but cannot + // accept new work right now. We still enter the switch below so that + // token refreshes for existing sessions are processed (the case + // 'session' handler checks for existing sessions before the inner + // capacity guard). + const atCapacityBeforeSwitch = activeSessions.size >= config.maxSessions + + // Skip work items that have already been completed and stopped. + // The server may re-deliver stale work before processing our stop + // request, which would otherwise cause a duplicate session spawn. + if (completedWorkIds.has(work.id)) { + logForDebugging( + `[bridge:work] Skipping already-completed workId=${work.id}`, + ) + // Respect capacity throttle — without a sleep here, persistent stale + // redeliveries would tight-loop at poll-request speed (the !work + // branch above is the only sleep, and work != null skips it). + if (atCapacityBeforeSwitch) { + const cap = capacityWake.signal() + if (pollConfig.non_exclusive_heartbeat_interval_ms > 0) { + await heartbeatActiveWorkItems() + await sleep( + pollConfig.non_exclusive_heartbeat_interval_ms, + cap.signal, + ) + } else if (pollConfig.multisession_poll_interval_ms_at_capacity > 0) { + await sleep( + pollConfig.multisession_poll_interval_ms_at_capacity, + cap.signal, + ) + } + cap.cleanup() + } else { + await sleep(1000, loopSignal) + } + continue + } + + // Decode the work secret for session spawning and to extract the JWT + // used for the ack call below. + let secret + try { + secret = decodeWorkSecret(work.secret) + } catch (err) { + const errMsg = errorMessage(err) + logger.logError( + `Failed to decode work secret for workId=${work.id}: ${errMsg}`, + ) + logEvent('tengu_bridge_work_secret_failed', {}) + // Can't ack (needs the JWT we failed to decode). stopWork uses OAuth, + // so it's callable here — prevents XAUTOCLAIM from re-delivering this + // poisoned item every reclaim_older_than_ms cycle. + completedWorkIds.add(work.id) + trackCleanup( + stopWorkWithRetry( + api, + environmentId, + work.id, + logger, + backoffConfig.stopWorkBaseDelayMs, + ), + ) + // Respect capacity throttle before retrying — without a sleep here, + // repeated decode failures at capacity would tight-loop at + // poll-request speed (work != null skips the !work sleep above). + if (atCapacityBeforeSwitch) { + const cap = capacityWake.signal() + if (pollConfig.non_exclusive_heartbeat_interval_ms > 0) { + await heartbeatActiveWorkItems() + await sleep( + pollConfig.non_exclusive_heartbeat_interval_ms, + cap.signal, + ) + } else if (pollConfig.multisession_poll_interval_ms_at_capacity > 0) { + await sleep( + pollConfig.multisession_poll_interval_ms_at_capacity, + cap.signal, + ) + } + cap.cleanup() + } + continue + } + + // Explicitly acknowledge after committing to handle the work — NOT + // before. The at-capacity guard inside case 'session' can break + // without spawning; acking there would permanently lose the work. + // Ack failures are non-fatal: server re-delivers, and existingHandle + // / completedWorkIds paths handle the dedup. + const ackWork = async (): Promise => { + logForDebugging(`[bridge:work] Acknowledging workId=${work.id}`) + try { + await api.acknowledgeWork( + environmentId, + work.id, + secret.session_ingress_token, + ) + } catch (err) { + logForDebugging( + `[bridge:work] Acknowledge failed workId=${work.id}: ${errorMessage(err)}`, + ) + } + } + + const workType: string = work.data.type + switch (work.data.type) { + case 'healthcheck': + await ackWork() + logForDebugging('[bridge:work] Healthcheck received') + logger.logVerbose('Healthcheck received') + break + case 'session': { + const sessionId = work.data.id + try { + validateBridgeId(sessionId, 'session_id') + } catch { + await ackWork() + logger.logError(`Invalid session_id received: ${sessionId}`) + break + } + + // If the session is already running, deliver the fresh token so + // the child process can reconnect its WebSocket with the new + // session ingress token. This handles the case where the server + // re-dispatches work for an existing session after the WS drops. + const existingHandle = activeSessions.get(sessionId) + if (existingHandle) { + existingHandle.updateAccessToken(secret.session_ingress_token) + sessionIngressTokens.set(sessionId, secret.session_ingress_token) + sessionWorkIds.set(sessionId, work.id) + // Re-schedule next refresh from the fresh JWT's expiry. onRefresh + // branches on v2Sessions so both v1 and v2 are safe here. + tokenRefresh?.schedule(sessionId, secret.session_ingress_token) + logForDebugging( + `[bridge:work] Updated access token for existing sessionId=${sessionId} workId=${work.id}`, + ) + await ackWork() + break + } + + // At capacity — token refresh for existing sessions is handled + // above, but we cannot spawn new ones. The post-switch capacity + // sleep will throttle the loop; just break here. + if (activeSessions.size >= config.maxSessions) { + logForDebugging( + `[bridge:work] At capacity (${activeSessions.size}/${config.maxSessions}), cannot spawn new session for workId=${work.id}`, + ) + break + } + + await ackWork() + const spawnStartTime = Date.now() + + // CCR v2 path: register this bridge as the session worker, get the + // epoch, and point the child at /v1/code/sessions/{id}. The child + // already has the full v2 client (SSETransport + CCRClient) — same + // code path environment-manager launches in containers. + // + // v1 path: Session-Ingress WebSocket. Uses config.sessionIngressUrl + // (not secret.api_base_url, which may point to a remote proxy tunnel + // that doesn't know about locally-created sessions). + let sdkUrl: string + let useCcrV2 = false + let workerEpoch: number | undefined + // Server decides per-session via the work secret; env var is the + // ant-dev override (e.g. forcing v2 before the server flag is on). + if ( + secret.use_code_sessions === true || + isEnvTruthy(process.env.CLAUDE_BRIDGE_USE_CCR_V2) + ) { + sdkUrl = buildCCRv2SdkUrl(config.apiBaseUrl, sessionId) + // Retry once on transient failure (network blip, 500) before + // permanently giving up and killing the session. + for (let attempt = 1; attempt <= 2; attempt++) { + try { + workerEpoch = await registerWorker( + sdkUrl, + secret.session_ingress_token, + ) + useCcrV2 = true + logForDebugging( + `[bridge:session] CCR v2: registered worker sessionId=${sessionId} epoch=${workerEpoch} attempt=${attempt}`, + ) + break + } catch (err) { + const errMsg = errorMessage(err) + if (attempt < 2) { + logForDebugging( + `[bridge:session] CCR v2: registerWorker attempt ${attempt} failed, retrying: ${errMsg}`, + ) + await sleep(2_000, loopSignal) + if (loopSignal.aborted) break + continue + } + logger.logError( + `CCR v2 worker registration failed for session ${sessionId}: ${errMsg}`, + ) + logError(new Error(`registerWorker failed: ${errMsg}`)) + completedWorkIds.add(work.id) + trackCleanup( + stopWorkWithRetry( + api, + environmentId, + work.id, + logger, + backoffConfig.stopWorkBaseDelayMs, + ), + ) + } + } + if (!useCcrV2) break + } else { + sdkUrl = buildSdkUrl(config.sessionIngressUrl, sessionId) + } + + // In worktree mode, on-demand sessions get an isolated git worktree + // so concurrent sessions don't interfere with each other's file + // changes. The pre-created initial session (if any) runs in + // config.dir so the user's first session lands in the directory they + // invoked `rc` from — matching the old single-session UX. + // In same-dir and single-session modes, all sessions share config.dir. + // Capture spawnMode before the await below — the `w` key handler + // mutates config.spawnMode directly, and createAgentWorktree can + // take 1-2s, so reading config.spawnMode after the await can + // produce contradictory analytics (spawn_mode:'same-dir', in_worktree:true). + const spawnModeAtDecision = config.spawnMode + let sessionDir = config.dir + let worktreeCreateMs = 0 + if ( + spawnModeAtDecision === 'worktree' && + (initialSessionId === undefined || + !sameSessionId(sessionId, initialSessionId)) + ) { + const wtStart = Date.now() + try { + const wt = await createAgentWorktree( + `bridge-${safeFilenameId(sessionId)}`, + ) + worktreeCreateMs = Date.now() - wtStart + sessionWorktrees.set(sessionId, { + worktreePath: wt.worktreePath, + worktreeBranch: wt.worktreeBranch, + gitRoot: wt.gitRoot, + hookBased: wt.hookBased, + }) + sessionDir = wt.worktreePath + logForDebugging( + `[bridge:session] Created worktree for sessionId=${sessionId} at ${wt.worktreePath}`, + ) + } catch (err) { + const errMsg = errorMessage(err) + logger.logError( + `Failed to create worktree for session ${sessionId}: ${errMsg}`, + ) + logError(new Error(`Worktree creation failed: ${errMsg}`)) + completedWorkIds.add(work.id) + trackCleanup( + stopWorkWithRetry( + api, + environmentId, + work.id, + logger, + backoffConfig.stopWorkBaseDelayMs, + ), + ) + break + } + } + + logForDebugging( + `[bridge:session] Spawning sessionId=${sessionId} sdkUrl=${sdkUrl}`, + ) + + // compat-surface session_* form for logger/Sessions-API calls. + // Work poll returns cse_* under v2 compat; convert before spawn so + // the onFirstUserMessage callback can close over it. + const compatSessionId = toCompatSessionId(sessionId) + + const spawnResult = safeSpawn( + spawner, + { + sessionId, + sdkUrl, + accessToken: secret.session_ingress_token, + useCcrV2, + workerEpoch, + onFirstUserMessage: text => { + // Server-set titles (--name, web rename) win. fetchSessionTitle + // runs concurrently; if it already populated titledSessions, + // skip. If it hasn't resolved yet, the derived title sticks — + // acceptable since the server had no title at spawn time. + if (titledSessions.has(compatSessionId)) return + titledSessions.add(compatSessionId) + const title = deriveSessionTitle(text) + logger.setSessionTitle(compatSessionId, title) + logForDebugging( + `[bridge:title] derived title for ${compatSessionId}: ${title}`, + ) + void import('./createSession.js') + .then(({ updateBridgeSessionTitle }) => + updateBridgeSessionTitle(compatSessionId, title, { + baseUrl: config.apiBaseUrl, + }), + ) + .catch(err => + logForDebugging( + `[bridge:title] failed to update title for ${compatSessionId}: ${err}`, + { level: 'error' }, + ), + ) + }, + }, + sessionDir, + ) + if (typeof spawnResult === 'string') { + logger.logError( + `Failed to spawn session ${sessionId}: ${spawnResult}`, + ) + // Clean up worktree if one was created for this session + const wt = sessionWorktrees.get(sessionId) + if (wt) { + sessionWorktrees.delete(sessionId) + trackCleanup( + removeAgentWorktree( + wt.worktreePath, + wt.worktreeBranch, + wt.gitRoot, + wt.hookBased, + ).catch((err: unknown) => + logger.logVerbose( + `Failed to remove worktree ${wt.worktreePath}: ${errorMessage(err)}`, + ), + ), + ) + } + completedWorkIds.add(work.id) + trackCleanup( + stopWorkWithRetry( + api, + environmentId, + work.id, + logger, + backoffConfig.stopWorkBaseDelayMs, + ), + ) + break + } + const handle = spawnResult + + const spawnDurationMs = Date.now() - spawnStartTime + logEvent('tengu_bridge_session_started', { + active_sessions: activeSessions.size, + spawn_mode: + spawnModeAtDecision as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + in_worktree: sessionWorktrees.has(sessionId), + spawn_duration_ms: spawnDurationMs, + worktree_create_ms: worktreeCreateMs, + inProtectedNamespace: isInProtectedNamespace(), + }) + logForDiagnosticsNoPII('info', 'bridge_session_started', { + spawn_mode: spawnModeAtDecision, + in_worktree: sessionWorktrees.has(sessionId), + spawn_duration_ms: spawnDurationMs, + worktree_create_ms: worktreeCreateMs, + }) + + activeSessions.set(sessionId, handle) + sessionWorkIds.set(sessionId, work.id) + sessionIngressTokens.set(sessionId, secret.session_ingress_token) + sessionCompatIds.set(sessionId, compatSessionId) + + const startTime = Date.now() + sessionStartTimes.set(sessionId, startTime) + + // Use a generic prompt description since we no longer get startup_context + logger.logSessionStart(sessionId, `Session ${sessionId}`) + + // Compute the actual debug file path (mirrors sessionRunner.ts logic) + const safeId = safeFilenameId(sessionId) + let sessionDebugFile: string | undefined + if (config.debugFile) { + const ext = config.debugFile.lastIndexOf('.') + if (ext > 0) { + sessionDebugFile = `${config.debugFile.slice(0, ext)}-${safeId}${config.debugFile.slice(ext)}` + } else { + sessionDebugFile = `${config.debugFile}-${safeId}` + } + } else if (config.verbose || process.env.USER_TYPE === 'ant') { + sessionDebugFile = join( + tmpdir(), + 'claude', + `bridge-session-${safeId}.log`, + ) + } + + if (sessionDebugFile) { + logger.logVerbose(`Debug log: ${sessionDebugFile}`) + } + + // Register in the sessions Map before starting status updates so the + // first render tick shows the correct count and bullet list in sync. + logger.addSession( + compatSessionId, + getRemoteSessionUrl(compatSessionId, config.sessionIngressUrl), + ) + + // Start live status updates and transition to "Attached" state. + startStatusUpdates() + logger.setAttached(compatSessionId) + + // One-shot title fetch. If the session already has a title (set via + // --name, web rename, or /remote-control), display it and mark as + // titled so the first-user-message fallback doesn't overwrite it. + // Otherwise onFirstUserMessage derives one from the first prompt. + void fetchSessionTitle(compatSessionId, config.apiBaseUrl) + .then(title => { + if (title && activeSessions.has(sessionId)) { + titledSessions.add(compatSessionId) + logger.setSessionTitle(compatSessionId, title) + logForDebugging( + `[bridge:title] server title for ${compatSessionId}: ${title}`, + ) + } + }) + .catch(err => + logForDebugging( + `[bridge:title] failed to fetch title for ${compatSessionId}: ${err}`, + { level: 'error' }, + ), + ) + + // Start per-session timeout watchdog + const timeoutMs = + config.sessionTimeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS + if (timeoutMs > 0) { + const timer = setTimeout( + onSessionTimeout, + timeoutMs, + sessionId, + timeoutMs, + logger, + timedOutSessions, + handle, + ) + sessionTimers.set(sessionId, timer) + } + + // Schedule proactive token refresh before the JWT expires. + // onRefresh branches on v2Sessions: v1 delivers OAuth to the + // child, v2 triggers server re-dispatch via reconnectSession. + if (useCcrV2) { + v2Sessions.add(sessionId) + } + tokenRefresh?.schedule(sessionId, secret.session_ingress_token) + + void handle.done.then(onSessionDone(sessionId, startTime, handle)) + break + } + default: + await ackWork() + // Gracefully ignore unknown work types. The backend may send new + // types before the bridge client is updated. + logForDebugging( + `[bridge:work] Unknown work type: ${workType}, skipping`, + ) + break + } + + // When at capacity, throttle the loop. The switch above still runs so + // existing-session token refreshes are processed, but we sleep here + // to avoid busy-looping. Include the capacity wake signal so the + // sleep is interrupted immediately when a session completes. + if (atCapacityBeforeSwitch) { + const cap = capacityWake.signal() + if (pollConfig.non_exclusive_heartbeat_interval_ms > 0) { + await heartbeatActiveWorkItems() + await sleep( + pollConfig.non_exclusive_heartbeat_interval_ms, + cap.signal, + ) + } else if (pollConfig.multisession_poll_interval_ms_at_capacity > 0) { + await sleep( + pollConfig.multisession_poll_interval_ms_at_capacity, + cap.signal, + ) + } + cap.cleanup() + } + } catch (err) { + if (loopSignal.aborted) { + break + } + + // Fatal errors (401/403) — no point retrying, auth won't fix itself + if (err instanceof BridgeFatalError) { + fatalExit = true + // Server-enforced expiry gets a clean status message, not an error + if (isExpiredErrorType(err.errorType)) { + logger.logStatus(err.message) + } else if (isSuppressible403(err)) { + // Cosmetic 403 errors (e.g., external_poll_sessions scope, + // environments:manage permission) — don't show to user + logForDebugging(`[bridge:work] Suppressed 403 error: ${err.message}`) + } else { + logger.logError(err.message) + logError(err) + } + logEvent('tengu_bridge_fatal_error', { + status: err.status, + error_type: + err.errorType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + logForDiagnosticsNoPII( + isExpiredErrorType(err.errorType) ? 'info' : 'error', + 'bridge_fatal_error', + { status: err.status, error_type: err.errorType }, + ) + break + } + + const errMsg = describeAxiosError(err) + + if (isConnectionError(err) || isServerError(err)) { + const now = Date.now() + + // Detect system sleep/wake: if the gap since the last poll error + // greatly exceeds the expected backoff, the machine likely slept. + // Reset error tracking so the bridge retries with a fresh budget. + if ( + lastPollErrorTime !== null && + now - lastPollErrorTime > pollSleepDetectionThresholdMs(backoffConfig) + ) { + logForDebugging( + `[bridge:work] Detected system sleep (${Math.round((now - lastPollErrorTime) / 1000)}s gap), resetting error budget`, + ) + logForDiagnosticsNoPII('info', 'bridge_poll_sleep_detected', { + gapMs: now - lastPollErrorTime, + }) + connErrorStart = null + connBackoff = 0 + generalErrorStart = null + generalBackoff = 0 + } + lastPollErrorTime = now + + if (!connErrorStart) { + connErrorStart = now + } + const elapsed = now - connErrorStart + if (elapsed >= backoffConfig.connGiveUpMs) { + logger.logError( + `Server unreachable for ${Math.round(elapsed / 60_000)} minutes, giving up.`, + ) + logEvent('tengu_bridge_poll_give_up', { + error_type: + 'connection' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + elapsed_ms: elapsed, + }) + logForDiagnosticsNoPII('error', 'bridge_poll_give_up', { + error_type: 'connection', + elapsed_ms: elapsed, + }) + fatalExit = true + break + } + + // Reset the other track when switching error types + generalErrorStart = null + generalBackoff = 0 + + connBackoff = connBackoff + ? Math.min(connBackoff * 2, backoffConfig.connCapMs) + : backoffConfig.connInitialMs + const delay = addJitter(connBackoff) + logger.logVerbose( + `Connection error, retrying in ${formatDelay(delay)} (${Math.round(elapsed / 1000)}s elapsed): ${errMsg}`, + ) + logger.updateReconnectingStatus( + formatDelay(delay), + formatDuration(elapsed), + ) + // The poll_due heartbeat-loop exit leaves a healthy lease exposed to + // this backoff path. Heartbeat before each sleep so /poll outages + // (the VerifyEnvironmentSecretAuth DB path heartbeat was introduced + // to avoid) don't kill the 300s lease TTL. No-op when activeSessions + // is empty or heartbeat is disabled. + if (getPollIntervalConfig().non_exclusive_heartbeat_interval_ms > 0) { + await heartbeatActiveWorkItems() + } + await sleep(delay, loopSignal) + } else { + const now = Date.now() + + // Sleep detection for general errors (same logic as connection errors) + if ( + lastPollErrorTime !== null && + now - lastPollErrorTime > pollSleepDetectionThresholdMs(backoffConfig) + ) { + logForDebugging( + `[bridge:work] Detected system sleep (${Math.round((now - lastPollErrorTime) / 1000)}s gap), resetting error budget`, + ) + logForDiagnosticsNoPII('info', 'bridge_poll_sleep_detected', { + gapMs: now - lastPollErrorTime, + }) + connErrorStart = null + connBackoff = 0 + generalErrorStart = null + generalBackoff = 0 + } + lastPollErrorTime = now + + if (!generalErrorStart) { + generalErrorStart = now + } + const elapsed = now - generalErrorStart + if (elapsed >= backoffConfig.generalGiveUpMs) { + logger.logError( + `Persistent errors for ${Math.round(elapsed / 60_000)} minutes, giving up.`, + ) + logEvent('tengu_bridge_poll_give_up', { + error_type: + 'general' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + elapsed_ms: elapsed, + }) + logForDiagnosticsNoPII('error', 'bridge_poll_give_up', { + error_type: 'general', + elapsed_ms: elapsed, + }) + fatalExit = true + break + } + + // Reset the other track when switching error types + connErrorStart = null + connBackoff = 0 + + generalBackoff = generalBackoff + ? Math.min(generalBackoff * 2, backoffConfig.generalCapMs) + : backoffConfig.generalInitialMs + const delay = addJitter(generalBackoff) + logger.logVerbose( + `Poll failed, retrying in ${formatDelay(delay)} (${Math.round(elapsed / 1000)}s elapsed): ${errMsg}`, + ) + logger.updateReconnectingStatus( + formatDelay(delay), + formatDuration(elapsed), + ) + if (getPollIntervalConfig().non_exclusive_heartbeat_interval_ms > 0) { + await heartbeatActiveWorkItems() + } + await sleep(delay, loopSignal) + } + } + } + + // Clean up + stopStatusUpdates() + logger.clearStatus() + + const loopDurationMs = Date.now() - loopStartTime + logEvent('tengu_bridge_shutdown', { + active_sessions: activeSessions.size, + loop_duration_ms: loopDurationMs, + }) + logForDiagnosticsNoPII('info', 'bridge_shutdown', { + active_sessions: activeSessions.size, + loop_duration_ms: loopDurationMs, + }) + + // Graceful shutdown: kill active sessions, report them as interrupted, + // archive sessions, then deregister the environment so the web UI shows + // the bridge as offline. + + // Collect all session IDs to archive on exit. This includes: + // 1. Active sessions (snapshot before killing — onSessionDone clears maps) + // 2. The initial auto-created session (may never have had work dispatched) + // api.archiveSession is idempotent (409 if already archived), so + // double-archiving is safe. + const sessionsToArchive = new Set(activeSessions.keys()) + if (initialSessionId) { + sessionsToArchive.add(initialSessionId) + } + // Snapshot before killing — onSessionDone clears sessionCompatIds. + const compatIdSnapshot = new Map(sessionCompatIds) + + if (activeSessions.size > 0) { + logForDebugging( + `[bridge:shutdown] Shutting down ${activeSessions.size} active session(s)`, + ) + logger.logStatus( + `Shutting down ${activeSessions.size} active session(s)\u2026`, + ) + + // Snapshot work IDs before killing — onSessionDone clears the maps when + // each child exits, so we need a copy for the stopWork calls below. + const shutdownWorkIds = new Map(sessionWorkIds) + + for (const [sessionId, handle] of activeSessions.entries()) { + logForDebugging( + `[bridge:shutdown] Sending SIGTERM to sessionId=${sessionId}`, + ) + handle.kill() + } + + const timeout = new AbortController() + await Promise.race([ + Promise.allSettled([...activeSessions.values()].map(h => h.done)), + sleep(backoffConfig.shutdownGraceMs ?? 30_000, timeout.signal), + ]) + timeout.abort() + + // SIGKILL any processes that didn't respond to SIGTERM within the grace window + for (const [sid, handle] of activeSessions.entries()) { + logForDebugging(`[bridge:shutdown] Force-killing stuck sessionId=${sid}`) + handle.forceKill() + } + + // Clear any remaining session timeout and refresh timers + for (const timer of sessionTimers.values()) { + clearTimeout(timer) + } + sessionTimers.clear() + tokenRefresh?.cancelAll() + + // Clean up any remaining worktrees from active sessions. + // Snapshot and clear the map first so onSessionDone (which may fire + // during the await below when handle.done resolves) won't try to + // remove the same worktrees again. + if (sessionWorktrees.size > 0) { + const remainingWorktrees = [...sessionWorktrees.values()] + sessionWorktrees.clear() + logForDebugging( + `[bridge:shutdown] Cleaning up ${remainingWorktrees.length} worktree(s)`, + ) + await Promise.allSettled( + remainingWorktrees.map(wt => + removeAgentWorktree( + wt.worktreePath, + wt.worktreeBranch, + wt.gitRoot, + wt.hookBased, + ), + ), + ) + } + + // Stop all active work items so the server knows they're done + await Promise.allSettled( + [...shutdownWorkIds.entries()].map(([sessionId, workId]) => { + return api + .stopWork(environmentId, workId, true) + .catch(err => + logger.logVerbose( + `Failed to stop work ${workId} for session ${sessionId}: ${errorMessage(err)}`, + ), + ) + }), + ) + } + + // Ensure all in-flight cleanup (stopWork, worktree removal) from + // onSessionDone completes before deregistering — otherwise + // process.exit() can kill them mid-flight. + if (pendingCleanups.size > 0) { + await Promise.allSettled([...pendingCleanups]) + } + + // In single-session mode with a known session, leave the session and + // environment alive so `claude remote-control --session-id=` can resume. + // The backend GCs stale environments via a 4h TTL (BRIDGE_LAST_POLL_TTL). + // Archiving the session or deregistering the environment would make the + // printed resume command a lie — deregister deletes Firestore + Redis stream. + // Skip when the loop exited fatally (env expired, auth failed, give-up) — + // resume is impossible in those cases and the message would contradict the + // error already printed. + // feature('KAIROS') gate: --session-id is ant-only; without the gate, + // revert to the pre-PR behavior (archive + deregister on every shutdown). + if ( + feature('KAIROS') && + config.spawnMode === 'single-session' && + initialSessionId && + !fatalExit + ) { + logger.logStatus( + `Resume this session by running \`claude remote-control --continue\``, + ) + logForDebugging( + `[bridge:shutdown] Skipping archive+deregister to allow resume of session ${initialSessionId}`, + ) + return + } + + // Archive all known sessions so they don't linger as idle/running on the + // server after the bridge goes offline. + if (sessionsToArchive.size > 0) { + logForDebugging( + `[bridge:shutdown] Archiving ${sessionsToArchive.size} session(s)`, + ) + await Promise.allSettled( + [...sessionsToArchive].map(sessionId => + api + .archiveSession( + compatIdSnapshot.get(sessionId) ?? toCompatSessionId(sessionId), + ) + .catch(err => + logger.logVerbose( + `Failed to archive session ${sessionId}: ${errorMessage(err)}`, + ), + ), + ), + ) + } + + // Deregister the environment so the web UI shows the bridge as offline + // and the Redis stream is cleaned up. + try { + await api.deregisterEnvironment(environmentId) + logForDebugging( + `[bridge:shutdown] Environment deregistered, bridge offline`, + ) + logger.logVerbose('Environment deregistered.') + } catch (err) { + logger.logVerbose(`Failed to deregister environment: ${errorMessage(err)}`) + } + + // Clear the crash-recovery pointer — the env is gone, pointer would be + // stale. The early return above (resumable SIGINT shutdown) skips this, + // leaving the pointer as a backup for the printed --session-id hint. + const { clearBridgePointer } = await import('./bridgePointer.js') + await clearBridgePointer(config.dir) + + logger.logVerbose('Environment offline.') +} + +const CONNECTION_ERROR_CODES = new Set([ + 'ECONNREFUSED', + 'ECONNRESET', + 'ETIMEDOUT', + 'ENETUNREACH', + 'EHOSTUNREACH', +]) + +export function isConnectionError(err: unknown): boolean { + if ( + err && + typeof err === 'object' && + 'code' in err && + typeof err.code === 'string' && + CONNECTION_ERROR_CODES.has(err.code) + ) { + return true + } + return false +} + +/** Detect HTTP 5xx errors from axios (code: 'ERR_BAD_RESPONSE'). */ +export function isServerError(err: unknown): boolean { + return ( + !!err && + typeof err === 'object' && + 'code' in err && + typeof err.code === 'string' && + err.code === 'ERR_BAD_RESPONSE' + ) +} + +/** Add ±25% jitter to a delay value. */ +function addJitter(ms: number): number { + return Math.max(0, ms + ms * 0.25 * (2 * Math.random() - 1)) +} + +function formatDelay(ms: number): string { + return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${Math.round(ms)}ms` +} + +/** + * Retry stopWork with exponential backoff (3 attempts, 1s/2s/4s). + * Ensures the server learns the work item ended, preventing server-side zombies. + */ +async function stopWorkWithRetry( + api: BridgeApiClient, + environmentId: string, + workId: string, + logger: BridgeLogger, + baseDelayMs = 1000, +): Promise { + const MAX_ATTEMPTS = 3 + + for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { + try { + await api.stopWork(environmentId, workId, false) + logForDebugging( + `[bridge:work] stopWork succeeded for workId=${workId} on attempt ${attempt}/${MAX_ATTEMPTS}`, + ) + return + } catch (err) { + // Auth/permission errors won't be fixed by retrying + if (err instanceof BridgeFatalError) { + if (isSuppressible403(err)) { + logForDebugging( + `[bridge:work] Suppressed stopWork 403 for ${workId}: ${err.message}`, + ) + } else { + logger.logError(`Failed to stop work ${workId}: ${err.message}`) + } + logForDiagnosticsNoPII('error', 'bridge_stop_work_failed', { + attempts: attempt, + fatal: true, + }) + return + } + const errMsg = errorMessage(err) + if (attempt < MAX_ATTEMPTS) { + const delay = addJitter(baseDelayMs * Math.pow(2, attempt - 1)) + logger.logVerbose( + `Failed to stop work ${workId} (attempt ${attempt}/${MAX_ATTEMPTS}), retrying in ${formatDelay(delay)}: ${errMsg}`, + ) + await sleep(delay) + } else { + logger.logError( + `Failed to stop work ${workId} after ${MAX_ATTEMPTS} attempts: ${errMsg}`, + ) + logForDiagnosticsNoPII('error', 'bridge_stop_work_failed', { + attempts: MAX_ATTEMPTS, + }) + } + } + } +} + +function onSessionTimeout( + sessionId: string, + timeoutMs: number, + logger: BridgeLogger, + timedOutSessions: Set, + handle: SessionHandle, +): void { + logForDebugging( + `[bridge:session] sessionId=${sessionId} timed out after ${formatDuration(timeoutMs)}`, + ) + logEvent('tengu_bridge_session_timeout', { + timeout_ms: timeoutMs, + }) + logger.logSessionFailed( + sessionId, + `Session timed out after ${formatDuration(timeoutMs)}`, + ) + timedOutSessions.add(sessionId) + handle.kill() +} + +export type ParsedArgs = { + verbose: boolean + sandbox: boolean + debugFile?: string + sessionTimeoutMs?: number + permissionMode?: string + name?: string + /** Value passed to --spawn (if any); undefined if no --spawn flag was given. */ + spawnMode: SpawnMode | undefined + /** Value passed to --capacity (if any); undefined if no --capacity flag was given. */ + capacity: number | undefined + /** --[no-]create-session-in-dir override; undefined = use default (on). */ + createSessionInDir: boolean | undefined + /** Resume an existing session instead of creating a new one. */ + sessionId?: string + /** Resume the last session in this directory (reads bridge-pointer.json). */ + continueSession: boolean + help: boolean + error?: string +} + +const SPAWN_FLAG_VALUES = ['session', 'same-dir', 'worktree'] as const + +function parseSpawnValue(raw: string | undefined): SpawnMode | string { + if (raw === 'session') return 'single-session' + if (raw === 'same-dir') return 'same-dir' + if (raw === 'worktree') return 'worktree' + return `--spawn requires one of: ${SPAWN_FLAG_VALUES.join(', ')} (got: ${raw ?? ''})` +} + +function parseCapacityValue(raw: string | undefined): number | string { + const n = raw === undefined ? NaN : parseInt(raw, 10) + if (isNaN(n) || n < 1) { + return `--capacity requires a positive integer (got: ${raw ?? ''})` + } + return n +} + +export function parseArgs(args: string[]): ParsedArgs { + let verbose = false + let sandbox = false + let debugFile: string | undefined + let sessionTimeoutMs: number | undefined + let permissionMode: string | undefined + let name: string | undefined + let help = false + let spawnMode: SpawnMode | undefined + let capacity: number | undefined + let createSessionInDir: boolean | undefined + let sessionId: string | undefined + let continueSession = false + + for (let i = 0; i < args.length; i++) { + const arg = args[i]! + if (arg === '--help' || arg === '-h') { + help = true + } else if (arg === '--verbose' || arg === '-v') { + verbose = true + } else if (arg === '--sandbox') { + sandbox = true + } else if (arg === '--no-sandbox') { + sandbox = false + } else if (arg === '--debug-file' && i + 1 < args.length) { + debugFile = resolve(args[++i]!) + } else if (arg.startsWith('--debug-file=')) { + debugFile = resolve(arg.slice('--debug-file='.length)) + } else if (arg === '--session-timeout' && i + 1 < args.length) { + sessionTimeoutMs = parseInt(args[++i]!, 10) * 1000 + } else if (arg.startsWith('--session-timeout=')) { + sessionTimeoutMs = + parseInt(arg.slice('--session-timeout='.length), 10) * 1000 + } else if (arg === '--permission-mode' && i + 1 < args.length) { + permissionMode = args[++i]! + } else if (arg.startsWith('--permission-mode=')) { + permissionMode = arg.slice('--permission-mode='.length) + } else if (arg === '--name' && i + 1 < args.length) { + name = args[++i]! + } else if (arg.startsWith('--name=')) { + name = arg.slice('--name='.length) + } else if ( + feature('KAIROS') && + arg === '--session-id' && + i + 1 < args.length + ) { + sessionId = args[++i]! + if (!sessionId) { + return makeError('--session-id requires a value') + } + } else if (feature('KAIROS') && arg.startsWith('--session-id=')) { + sessionId = arg.slice('--session-id='.length) + if (!sessionId) { + return makeError('--session-id requires a value') + } + } else if (feature('KAIROS') && (arg === '--continue' || arg === '-c')) { + continueSession = true + } else if (arg === '--spawn' || arg.startsWith('--spawn=')) { + if (spawnMode !== undefined) { + return makeError('--spawn may only be specified once') + } + const raw = arg.startsWith('--spawn=') + ? arg.slice('--spawn='.length) + : args[++i] + const v = parseSpawnValue(raw) + if (v === 'single-session' || v === 'same-dir' || v === 'worktree') { + spawnMode = v + } else { + return makeError(v) + } + } else if (arg === '--capacity' || arg.startsWith('--capacity=')) { + if (capacity !== undefined) { + return makeError('--capacity may only be specified once') + } + const raw = arg.startsWith('--capacity=') + ? arg.slice('--capacity='.length) + : args[++i] + const v = parseCapacityValue(raw) + if (typeof v === 'number') capacity = v + else return makeError(v) + } else if (arg === '--create-session-in-dir') { + createSessionInDir = true + } else if (arg === '--no-create-session-in-dir') { + createSessionInDir = false + } else { + return makeError( + `Unknown argument: ${arg}\nRun 'claude remote-control --help' for usage.`, + ) + } + } + + // Note: gate check for --spawn/--capacity/--create-session-in-dir is in bridgeMain + // (gate-aware error). Flag cross-validation happens here. + + // --capacity only makes sense for multi-session modes. + if (spawnMode === 'single-session' && capacity !== undefined) { + return makeError( + `--capacity cannot be used with --spawn=session (single-session mode has fixed capacity 1).`, + ) + } + + // --session-id / --continue resume a specific session on its original + // environment; incompatible with spawn-related flags (which configure + // fresh session creation), and mutually exclusive with each other. + if ( + (sessionId || continueSession) && + (spawnMode !== undefined || + capacity !== undefined || + createSessionInDir !== undefined) + ) { + return makeError( + `--session-id and --continue cannot be used with --spawn, --capacity, or --create-session-in-dir.`, + ) + } + if (sessionId && continueSession) { + return makeError(`--session-id and --continue cannot be used together.`) + } + + return { + verbose, + sandbox, + debugFile, + sessionTimeoutMs, + permissionMode, + name, + spawnMode, + capacity, + createSessionInDir, + sessionId, + continueSession, + help, + } + + function makeError(error: string): ParsedArgs { + return { + verbose, + sandbox, + debugFile, + sessionTimeoutMs, + permissionMode, + name, + spawnMode, + capacity, + createSessionInDir, + sessionId, + continueSession, + help, + error, + } + } +} + +async function printHelp(): Promise { + // Use EXTERNAL_PERMISSION_MODES for help text — internal modes (bubble) + // are ant-only and auto is feature-gated; they're still accepted by validation. + const { EXTERNAL_PERMISSION_MODES } = await import('../types/permissions.js') + const modes = EXTERNAL_PERMISSION_MODES.join(', ') + const showServer = await isMultiSessionSpawnEnabled() + const serverOptions = showServer + ? ` --spawn Spawn mode: same-dir, worktree, session + (default: same-dir) + --capacity Max concurrent sessions in worktree or + same-dir mode (default: ${SPAWN_SESSIONS_DEFAULT}) + --[no-]create-session-in-dir Pre-create a session in the current + directory; in worktree mode this session + stays in cwd while on-demand sessions get + isolated worktrees (default: on) +` + : '' + const serverDescription = showServer + ? ` + Remote Control runs as a persistent server that accepts multiple concurrent + sessions in the current directory. One session is pre-created on start so + you have somewhere to type immediately. Use --spawn=worktree to isolate + each on-demand session in its own git worktree, or --spawn=session for + the classic single-session mode (exits when that session ends). Press 'w' + during runtime to toggle between same-dir and worktree. +` + : '' + const serverNote = showServer + ? ` - Worktree mode requires a git repository or WorktreeCreate/WorktreeRemove hooks +` + : '' + const help = ` +Remote Control - Connect your local environment to claude.ai/code + +USAGE + claude remote-control [options] +OPTIONS + --name Name for the session (shown in claude.ai/code) +${ + feature('KAIROS') + ? ` -c, --continue Resume the last session in this directory + --session-id Resume a specific session by ID (cannot be + used with spawn flags or --continue) +` + : '' +} --permission-mode Permission mode for spawned sessions + (${modes}) + --debug-file Write debug logs to file + -v, --verbose Enable verbose output + -h, --help Show this help +${serverOptions} +DESCRIPTION + Remote Control allows you to control sessions on your local device from + claude.ai/code (https://claude.ai/code). Run this command in the + directory you want to work in, then connect from the Claude app or web. +${serverDescription} +NOTES + - You must be logged in with a Claude account that has a subscription + - Run \`claude\` first in the directory to accept the workspace trust dialog +${serverNote}` + // biome-ignore lint/suspicious/noConsole: intentional help output + console.log(help) +} + +const TITLE_MAX_LEN = 80 + +/** Derive a session title from a user message: first line, truncated. */ +function deriveSessionTitle(text: string): string { + // Collapse whitespace — newlines/tabs would break the single-line status display. + const flat = text.replace(/\s+/g, ' ').trim() + return truncateToWidth(flat, TITLE_MAX_LEN) +} + +/** + * One-shot fetch of a session's title via GET /v1/sessions/{id}. + * + * Uses `getBridgeSession` from createSession.ts (ccr-byoc headers + org UUID) + * rather than the environments-level bridgeApi client, whose headers make the + * Sessions API return 404. Returns undefined if the session has no title yet + * or the fetch fails — the caller falls back to deriving a title from the + * first user message. + */ +async function fetchSessionTitle( + compatSessionId: string, + baseUrl: string, +): Promise { + const { getBridgeSession } = await import('./createSession.js') + const session = await getBridgeSession(compatSessionId, { baseUrl }) + return session?.title || undefined +} + +export async function bridgeMain(args: string[]): Promise { + const parsed = parseArgs(args) + + if (parsed.help) { + await printHelp() + return + } + if (parsed.error) { + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error(`Error: ${parsed.error}`) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + + const { + verbose, + sandbox, + debugFile, + sessionTimeoutMs, + permissionMode, + name, + spawnMode: parsedSpawnMode, + capacity: parsedCapacity, + createSessionInDir: parsedCreateSessionInDir, + sessionId: parsedSessionId, + continueSession, + } = parsed + // Mutable so --continue can set it from the pointer file. The #20460 + // resume flow below then treats it the same as an explicit --session-id. + let resumeSessionId = parsedSessionId + // When --continue found a pointer, this is the directory it came from + // (may be a worktree sibling, not `dir`). On resume-flow deterministic + // failure, clear THIS file so --continue doesn't keep hitting the same + // dead session. Undefined for explicit --session-id (leaves pointer alone). + let resumePointerDir: string | undefined + + const usedMultiSessionFeature = + parsedSpawnMode !== undefined || + parsedCapacity !== undefined || + parsedCreateSessionInDir !== undefined + + // Validate permission mode early so the user gets an error before + // the bridge starts polling for work. + if (permissionMode !== undefined) { + const { PERMISSION_MODES } = await import('../types/permissions.js') + const valid: readonly string[] = PERMISSION_MODES + if (!valid.includes(permissionMode)) { + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error( + `Error: Invalid permission mode '${permissionMode}'. Valid modes: ${valid.join(', ')}`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + } + + const dir = resolve('.') + + // The bridge fast-path bypasses init.ts, so we must enable config reading + // before any code that transitively calls getGlobalConfig() + const { enableConfigs, checkHasTrustDialogAccepted } = await import( + '../utils/config.js' + ) + enableConfigs() + + // Initialize analytics and error reporting sinks. The bridge bypasses the + // setup() init flow, so we call initSinks() directly to attach sinks here. + const { initSinks } = await import('../utils/sinks.js') + initSinks() + + // Gate-aware validation: --spawn / --capacity / --create-session-in-dir require + // the multi-session gate. parseArgs has already validated flag combinations; + // here we only check the gate since that requires an async GrowthBook call. + // Runs after enableConfigs() (GrowthBook cache reads global config) and after + // initSinks() so the denial event can be enqueued. + const multiSessionEnabled = await isMultiSessionSpawnEnabled() + if (usedMultiSessionFeature && !multiSessionEnabled) { + await logEventAsync('tengu_bridge_multi_session_denied', { + used_spawn: parsedSpawnMode !== undefined, + used_capacity: parsedCapacity !== undefined, + used_create_session_in_dir: parsedCreateSessionInDir !== undefined, + }) + // logEventAsync only enqueues — process.exit() discards buffered events. + // Flush explicitly, capped at 500ms to match gracefulShutdown.ts. + // (sleep() doesn't unref its timer, but process.exit() follows immediately + // so the ref'd timer can't delay shutdown.) + await Promise.race([ + Promise.all([shutdown1PEventLogging(), shutdownDatadog()]), + sleep(500, undefined, { unref: true }), + ]).catch(() => {}) + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error( + 'Error: Multi-session Remote Control is not enabled for your account yet.', + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + + // Set the bootstrap CWD so that trust checks, project config lookups, and + // git utilities (getBranch, getRemoteUrl) resolve against the correct path. + const { setOriginalCwd, setCwdState } = await import('../bootstrap/state.js') + setOriginalCwd(dir) + setCwdState(dir) + + // The bridge bypasses main.tsx (which renders the interactive TrustDialog via showSetupScreens), + // so we must verify trust was previously established by a normal `claude` session. + if (!checkHasTrustDialogAccepted()) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error( + `Error: Workspace not trusted. Please run \`claude\` in ${dir} first to review and accept the workspace trust dialog.`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + + // Resolve auth + const { clearOAuthTokenCache, checkAndRefreshOAuthTokenIfNeeded } = + await import('../utils/auth.js') + const { getBridgeAccessToken, getBridgeBaseUrl } = await import( + './bridgeConfig.js' + ) + + const bridgeToken = getBridgeAccessToken() + if (!bridgeToken) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(BRIDGE_LOGIN_ERROR) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + + // First-time remote dialog — explain what bridge does and get consent + const { + getGlobalConfig, + saveGlobalConfig, + getCurrentProjectConfig, + saveCurrentProjectConfig, + } = await import('../utils/config.js') + if (!getGlobalConfig().remoteDialogSeen) { + const readline = await import('readline') + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log( + '\nRemote Control lets you access this CLI session from the web (claude.ai/code)\nor the Claude app, so you can pick up where you left off on any device.\n\nYou can disconnect remote access anytime by running /remote-control again.\n', + ) + const answer = await new Promise(resolve => { + rl.question('Enable Remote Control? (y/n) ', resolve) + }) + rl.close() + saveGlobalConfig(current => { + if (current.remoteDialogSeen) return current + return { ...current, remoteDialogSeen: true } + }) + if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') { + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(0) + } + } + + // --continue: resolve the most recent session from the crash-recovery + // pointer and chain into the #20460 --session-id flow. Worktree-aware: + // checks current dir first (fast path, zero exec), then fans out to git + // worktree siblings if that misses — the REPL bridge writes to + // getOriginalCwd() which EnterWorktreeTool/activeWorktreeSession can + // point at a worktree while the user's shell is at the repo root. + // KAIROS-gated at parseArgs — continueSession is always false in external + // builds, so this block tree-shakes. + if (feature('KAIROS') && continueSession) { + const { readBridgePointerAcrossWorktrees } = await import( + './bridgePointer.js' + ) + const found = await readBridgePointerAcrossWorktrees(dir) + if (!found) { + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error( + `Error: No recent session found in this directory or its worktrees. Run \`claude remote-control\` to start a new one.`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + const { pointer, dir: pointerDir } = found + const ageMin = Math.round(pointer.ageMs / 60_000) + const ageStr = ageMin < 60 ? `${ageMin}m` : `${Math.round(ageMin / 60)}h` + const fromWt = pointerDir !== dir ? ` from worktree ${pointerDir}` : '' + // biome-ignore lint/suspicious/noConsole: intentional info output + console.error( + `Resuming session ${pointer.sessionId} (${ageStr} ago)${fromWt}\u2026`, + ) + resumeSessionId = pointer.sessionId + // Track where the pointer came from so the #20460 exit(1) paths below + // clear the RIGHT file on deterministic failure — otherwise --continue + // would keep hitting the same dead session. May be a worktree sibling. + resumePointerDir = pointerDir + } + + // In production, baseUrl is the Anthropic API (from OAuth config). + // CLAUDE_BRIDGE_BASE_URL overrides this for ant local dev only. + const baseUrl = getBridgeBaseUrl() + + // For non-localhost targets, require HTTPS to protect credentials. + if ( + baseUrl.startsWith('http://') && + !baseUrl.includes('localhost') && + !baseUrl.includes('127.0.0.1') + ) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error( + 'Error: Remote Control base URL uses HTTP. Only HTTPS or localhost HTTP is allowed.', + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + + // Session ingress URL for WebSocket connections. In production this is the + // same as baseUrl (Envoy routes /v1/session_ingress/* to session-ingress). + // Locally, session-ingress runs on a different port (9413) than the + // contain-provide-api (8211), so CLAUDE_BRIDGE_SESSION_INGRESS_URL must be + // set explicitly. Ant-only, matching CLAUDE_BRIDGE_BASE_URL. + const sessionIngressUrl = + process.env.USER_TYPE === 'ant' && + process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL + ? process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL + : baseUrl + + const { getBranch, getRemoteUrl, findGitRoot } = await import( + '../utils/git.js' + ) + + // Precheck worktree availability for the first-run dialog and the `w` + // toggle. Unconditional so we know upfront whether worktree is an option. + const { hasWorktreeCreateHook } = await import('../utils/hooks.js') + const worktreeAvailable = hasWorktreeCreateHook() || findGitRoot(dir) !== null + + // Load saved per-project spawn-mode preference. Gated by multiSessionEnabled + // so a GrowthBook rollback cleanly reverts users to single-session — + // otherwise a saved pref would silently re-enable multi-session behavior + // (worktree isolation, 32 max sessions, w toggle) despite the gate being off. + // Also guard against a stale worktree pref left over from when this dir WAS + // a git repo (or the user copied config) — clear it on disk so the warning + // doesn't repeat on every launch. + let savedSpawnMode = multiSessionEnabled + ? getCurrentProjectConfig().remoteControlSpawnMode + : undefined + if (savedSpawnMode === 'worktree' && !worktreeAvailable) { + // biome-ignore lint/suspicious/noConsole: intentional warning output + console.error( + 'Warning: Saved spawn mode is worktree but this directory is not a git repository. Falling back to same-dir.', + ) + savedSpawnMode = undefined + saveCurrentProjectConfig(current => { + if (current.remoteControlSpawnMode === undefined) return current + return { ...current, remoteControlSpawnMode: undefined } + }) + } + + // First-run spawn-mode choice: ask once per project when the choice is + // meaningful (gate on, both modes available, no explicit override, not + // resuming). Saves to ProjectConfig so subsequent runs skip this. + if ( + multiSessionEnabled && + !savedSpawnMode && + worktreeAvailable && + parsedSpawnMode === undefined && + !resumeSessionId && + process.stdin.isTTY + ) { + const readline = await import('readline') + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }) + // biome-ignore lint/suspicious/noConsole: intentional dialog output + console.log( + `\nClaude Remote Control is launching in spawn mode which lets you create new sessions in this project from Claude Code on Web or your Mobile app. Learn more here: https://code.claude.com/docs/en/remote-control\n\n` + + `Spawn mode for this project:\n` + + ` [1] same-dir \u2014 sessions share the current directory (default)\n` + + ` [2] worktree \u2014 each session gets an isolated git worktree\n\n` + + `This can be changed later or explicitly set with --spawn=same-dir or --spawn=worktree.\n`, + ) + const answer = await new Promise(resolve => { + rl.question('Choose [1/2] (default: 1): ', resolve) + }) + rl.close() + const chosen: 'same-dir' | 'worktree' = + answer.trim() === '2' ? 'worktree' : 'same-dir' + savedSpawnMode = chosen + logEvent('tengu_bridge_spawn_mode_chosen', { + spawn_mode: + chosen as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + saveCurrentProjectConfig(current => { + if (current.remoteControlSpawnMode === chosen) return current + return { ...current, remoteControlSpawnMode: chosen } + }) + } + + // Determine effective spawn mode. + // Precedence: resume > explicit --spawn > saved project pref > gate default + // - resuming via --continue / --session-id: always single-session (resume + // targets one specific session in its original directory) + // - explicit --spawn flag: use that value directly (does not persist) + // - saved ProjectConfig.remoteControlSpawnMode: set by first-run dialog or `w` + // - default with gate on: same-dir (persistent multi-session, shared cwd) + // - default with gate off: single-session (unchanged legacy behavior) + // Track how spawn mode was determined, for rollout analytics. + type SpawnModeSource = 'resume' | 'flag' | 'saved' | 'gate_default' + let spawnModeSource: SpawnModeSource + let spawnMode: SpawnMode + if (resumeSessionId) { + spawnMode = 'single-session' + spawnModeSource = 'resume' + } else if (parsedSpawnMode !== undefined) { + spawnMode = parsedSpawnMode + spawnModeSource = 'flag' + } else if (savedSpawnMode !== undefined) { + spawnMode = savedSpawnMode + spawnModeSource = 'saved' + } else { + spawnMode = multiSessionEnabled ? 'same-dir' : 'single-session' + spawnModeSource = 'gate_default' + } + const maxSessions = + spawnMode === 'single-session' + ? 1 + : (parsedCapacity ?? SPAWN_SESSIONS_DEFAULT) + // Pre-create an empty session on start so the user has somewhere to type + // immediately, running in the current directory (exempted from worktree + // creation in the spawn loop). On by default; --no-create-session-in-dir + // opts out for a pure on-demand server where every session is isolated. + // The effectiveResumeSessionId guard at the creation site handles the + // resume case (skip creation when resume succeeded; fall through to + // fresh creation on env-mismatch fallback). + const preCreateSession = parsedCreateSessionInDir ?? true + + // Without --continue: a leftover pointer means the previous run didn't + // shut down cleanly (crash, kill -9, terminal closed). Clear it so the + // stale env doesn't linger past its relevance. Runs in all modes + // (clearBridgePointer is a no-op when no file exists) — covers the + // gate-transition case where a user crashed in single-session mode then + // starts fresh in worktree mode. Only single-session mode writes new + // pointers. + if (!resumeSessionId) { + const { clearBridgePointer } = await import('./bridgePointer.js') + await clearBridgePointer(dir) + } + + // Worktree mode requires either git or WorktreeCreate/WorktreeRemove hooks. + // Only reachable via explicit --spawn=worktree (default is same-dir); + // saved worktree pref was already guarded above. + if (spawnMode === 'worktree' && !worktreeAvailable) { + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error( + `Error: Worktree mode requires a git repository or WorktreeCreate hooks configured. Use --spawn=session for single-session mode.`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + + const branch = await getBranch() + const gitRepoUrl = await getRemoteUrl() + const machineName = hostname() + const bridgeId = randomUUID() + + const { handleOAuth401Error } = await import('../utils/auth.js') + const api = createBridgeApiClient({ + baseUrl, + getAccessToken: getBridgeAccessToken, + runnerVersion: MACRO.VERSION, + onDebug: logForDebugging, + onAuth401: handleOAuth401Error, + getTrustedDeviceToken, + }) + + // When resuming a session via --session-id, fetch it to learn its + // environment_id and reuse that for registration (idempotent on the + // backend). Left undefined otherwise — the backend rejects + // client-generated UUIDs and will allocate a fresh environment. + // feature('KAIROS') gate: --session-id is ant-only; parseArgs already + // rejects the flag when the gate is off, so resumeSessionId is always + // undefined here in external builds — this guard is for tree-shaking. + let reuseEnvironmentId: string | undefined + if (feature('KAIROS') && resumeSessionId) { + try { + validateBridgeId(resumeSessionId, 'sessionId') + } catch { + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error( + `Error: Invalid session ID "${resumeSessionId}". Session IDs must not contain unsafe characters.`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + // Proactively refresh the OAuth token — getBridgeSession uses raw axios + // without the withOAuthRetry 401-refresh logic. An expired-but-present + // token would otherwise produce a misleading "not found" error. + await checkAndRefreshOAuthTokenIfNeeded() + clearOAuthTokenCache() + const { getBridgeSession } = await import('./createSession.js') + const session = await getBridgeSession(resumeSessionId, { + baseUrl, + getAccessToken: getBridgeAccessToken, + }) + if (!session) { + // Session gone on server → pointer is stale. Clear it so the user + // isn't re-prompted next launch. (Explicit --session-id leaves the + // pointer alone — it's an independent file they may not even have.) + // resumePointerDir may be a worktree sibling — clear THAT file. + if (resumePointerDir) { + const { clearBridgePointer } = await import('./bridgePointer.js') + await clearBridgePointer(resumePointerDir) + } + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error( + `Error: Session ${resumeSessionId} not found. It may have been archived or expired, or your login may have lapsed (run \`claude /login\`).`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + if (!session.environment_id) { + if (resumePointerDir) { + const { clearBridgePointer } = await import('./bridgePointer.js') + await clearBridgePointer(resumePointerDir) + } + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error( + `Error: Session ${resumeSessionId} has no environment_id. It may never have been attached to a bridge.`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + reuseEnvironmentId = session.environment_id + logForDebugging( + `[bridge:init] Resuming session ${resumeSessionId} on environment ${reuseEnvironmentId}`, + ) + } + + const config: BridgeConfig = { + dir, + machineName, + branch, + gitRepoUrl, + maxSessions, + spawnMode, + verbose, + sandbox, + bridgeId, + workerType: 'claude_code', + environmentId: randomUUID(), + reuseEnvironmentId, + apiBaseUrl: baseUrl, + sessionIngressUrl, + debugFile, + sessionTimeoutMs, + } + + logForDebugging( + `[bridge:init] bridgeId=${bridgeId}${reuseEnvironmentId ? ` reuseEnvironmentId=${reuseEnvironmentId}` : ''} dir=${dir} branch=${branch} gitRepoUrl=${gitRepoUrl} machine=${machineName}`, + ) + logForDebugging( + `[bridge:init] apiBaseUrl=${baseUrl} sessionIngressUrl=${sessionIngressUrl}`, + ) + logForDebugging( + `[bridge:init] sandbox=${sandbox}${debugFile ? ` debugFile=${debugFile}` : ''}`, + ) + + // Register the bridge environment before entering the poll loop. + let environmentId: string + let environmentSecret: string + try { + const reg = await api.registerBridgeEnvironment(config) + environmentId = reg.environment_id + environmentSecret = reg.environment_secret + } catch (err) { + logEvent('tengu_bridge_registration_failed', { + status: err instanceof BridgeFatalError ? err.status : undefined, + }) + // Registration failures are fatal — print a clean message instead of a stack trace. + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error( + err instanceof BridgeFatalError && err.status === 404 + ? 'Remote Control environments are not available for your account.' + : `Error: ${errorMessage(err)}`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + + // Tracks whether the --session-id resume flow completed successfully. + // Used below to skip fresh session creation and seed initialSessionId. + // Cleared on env mismatch so we gracefully fall back to a new session. + let effectiveResumeSessionId: string | undefined + if (feature('KAIROS') && resumeSessionId) { + if (reuseEnvironmentId && environmentId !== reuseEnvironmentId) { + // Backend returned a different environment_id — the original env + // expired or was reaped. Reconnect won't work against the new env + // (session is bound to the old one). Log to sentry for visibility + // and fall through to fresh session creation on the new env. + logError( + new Error( + `Bridge resume env mismatch: requested ${reuseEnvironmentId}, backend returned ${environmentId}. Falling back to fresh session.`, + ), + ) + // biome-ignore lint/suspicious/noConsole: intentional warning output + console.warn( + `Warning: Could not resume session ${resumeSessionId} — its environment has expired. Creating a fresh session instead.`, + ) + // Don't deregister — we're going to use this new environment. + // effectiveResumeSessionId stays undefined → fresh session path below. + } else { + // Force-stop any stale worker instances for this session and re-queue + // it so our poll loop picks it up. Must happen after registration so + // the backend knows a live worker exists for the environment. + // + // The pointer stores a session_* ID but /bridge/reconnect looks + // sessions up by their infra tag (cse_*) when ccr_v2_compat_enabled + // is on. Try both; the conversion is a no-op if already cse_*. + const infraResumeId = toInfraSessionId(resumeSessionId) + const reconnectCandidates = + infraResumeId === resumeSessionId + ? [resumeSessionId] + : [resumeSessionId, infraResumeId] + let reconnected = false + let lastReconnectErr: unknown + for (const candidateId of reconnectCandidates) { + try { + await api.reconnectSession(environmentId, candidateId) + logForDebugging( + `[bridge:init] Session ${candidateId} re-queued via bridge/reconnect`, + ) + effectiveResumeSessionId = resumeSessionId + reconnected = true + break + } catch (err) { + lastReconnectErr = err + logForDebugging( + `[bridge:init] reconnectSession(${candidateId}) failed: ${errorMessage(err)}`, + ) + } + } + if (!reconnected) { + const err = lastReconnectErr + + // Do NOT deregister on transient reconnect failure — at this point + // environmentId IS the session's own environment. Deregistering + // would make retry impossible. The backend's 4h TTL cleans up. + const isFatal = err instanceof BridgeFatalError + // Clear pointer only on fatal reconnect failure. Transient failures + // ("try running the same command again") should keep the pointer so + // next launch re-prompts — that IS the retry mechanism. + if (resumePointerDir && isFatal) { + const { clearBridgePointer } = await import('./bridgePointer.js') + await clearBridgePointer(resumePointerDir) + } + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error( + isFatal + ? `Error: ${errorMessage(err)}` + : `Error: Failed to reconnect session ${resumeSessionId}: ${errorMessage(err)}\nThe session may still be resumable — try running the same command again.`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + } + } + + logForDebugging( + `[bridge:init] Registered, server environmentId=${environmentId}`, + ) + const startupPollConfig = getPollIntervalConfig() + logEvent('tengu_bridge_started', { + max_sessions: config.maxSessions, + has_debug_file: !!config.debugFile, + sandbox: config.sandbox, + verbose: config.verbose, + heartbeat_interval_ms: + startupPollConfig.non_exclusive_heartbeat_interval_ms, + spawn_mode: + config.spawnMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + spawn_mode_source: + spawnModeSource as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + multi_session_gate: multiSessionEnabled, + pre_create_session: preCreateSession, + worktree_available: worktreeAvailable, + }) + logForDiagnosticsNoPII('info', 'bridge_started', { + max_sessions: config.maxSessions, + sandbox: config.sandbox, + spawn_mode: config.spawnMode, + }) + + const spawner = createSessionSpawner({ + execPath: process.execPath, + scriptArgs: spawnScriptArgs(), + env: process.env, + verbose, + sandbox, + debugFile, + permissionMode, + onDebug: logForDebugging, + onActivity: (sessionId, activity) => { + logForDebugging( + `[bridge:activity] sessionId=${sessionId} ${activity.type} ${activity.summary}`, + ) + }, + onPermissionRequest: (sessionId, request, _accessToken) => { + logForDebugging( + `[bridge:perm] sessionId=${sessionId} tool=${request.request.tool_name} request_id=${request.request_id} (not auto-approving)`, + ) + }, + }) + + const logger = createBridgeLogger({ verbose }) + const { parseGitHubRepository } = await import('../utils/detectRepository.js') + const ownerRepo = gitRepoUrl ? parseGitHubRepository(gitRepoUrl) : null + // Use the repo name from the parsed owner/repo, or fall back to the dir basename + const repoName = ownerRepo ? ownerRepo.split('/').pop()! : basename(dir) + logger.setRepoInfo(repoName, branch) + + // `w` toggle is available iff we're in a multi-session mode AND worktree + // is a valid option. When unavailable, the mode suffix and hint are hidden. + const toggleAvailable = spawnMode !== 'single-session' && worktreeAvailable + if (toggleAvailable) { + // Safe cast: spawnMode is not single-session (checked above), and the + // saved-worktree-in-non-git guard + exit check above ensure worktree + // is only reached when available. + logger.setSpawnModeDisplay(spawnMode as 'same-dir' | 'worktree') + } + + // Listen for keys: space toggles QR code, w toggles spawn mode + const onStdinData = (data: Buffer): void => { + if (data[0] === 0x03 || data[0] === 0x04) { + // Ctrl+C / Ctrl+D — trigger graceful shutdown + process.emit('SIGINT') + return + } + if (data[0] === 0x20 /* space */) { + logger.toggleQr() + return + } + if (data[0] === 0x77 /* 'w' */) { + if (!toggleAvailable) return + const newMode: 'same-dir' | 'worktree' = + config.spawnMode === 'same-dir' ? 'worktree' : 'same-dir' + config.spawnMode = newMode + logEvent('tengu_bridge_spawn_mode_toggled', { + spawn_mode: + newMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + logger.logStatus( + newMode === 'worktree' + ? 'Spawn mode: worktree (new sessions get isolated git worktrees)' + : 'Spawn mode: same-dir (new sessions share the current directory)', + ) + logger.setSpawnModeDisplay(newMode) + logger.refreshDisplay() + saveCurrentProjectConfig(current => { + if (current.remoteControlSpawnMode === newMode) return current + return { ...current, remoteControlSpawnMode: newMode } + }) + return + } + } + if (process.stdin.isTTY) { + process.stdin.setRawMode(true) + process.stdin.resume() + process.stdin.on('data', onStdinData) + } + + const controller = new AbortController() + const onSigint = (): void => { + logForDebugging('[bridge:shutdown] SIGINT received, shutting down') + controller.abort() + } + const onSigterm = (): void => { + logForDebugging('[bridge:shutdown] SIGTERM received, shutting down') + controller.abort() + } + process.on('SIGINT', onSigint) + process.on('SIGTERM', onSigterm) + + // Auto-create an empty session so the user has somewhere to type + // immediately (matching /remote-control behavior). Controlled by + // preCreateSession: on by default; --no-create-session-in-dir opts out. + // When a --session-id resume succeeded, skip creation entirely — the + // session already exists and bridge/reconnect has re-queued it. + // When resume was requested but failed on env mismatch, effectiveResumeSessionId + // is undefined, so we fall through to fresh session creation (honoring the + // "Creating a fresh session instead" warning printed above). + let initialSessionId: string | null = + feature('KAIROS') && effectiveResumeSessionId + ? effectiveResumeSessionId + : null + if (preCreateSession && !(feature('KAIROS') && effectiveResumeSessionId)) { + const { createBridgeSession } = await import('./createSession.js') + try { + initialSessionId = await createBridgeSession({ + environmentId, + title: name, + events: [], + gitRepoUrl, + branch, + signal: controller.signal, + baseUrl, + getAccessToken: getBridgeAccessToken, + permissionMode, + }) + if (initialSessionId) { + logForDebugging( + `[bridge:init] Created initial session ${initialSessionId}`, + ) + } + } catch (err) { + logForDebugging( + `[bridge:init] Session creation failed (non-fatal): ${errorMessage(err)}`, + ) + } + } + + // Crash-recovery pointer: write immediately so kill -9 at any point + // after this leaves a recoverable trail. Covers both fresh sessions and + // resumed ones (so a second crash after resume is still recoverable). + // Cleared when runBridgeLoop falls through to archive+deregister; left in + // place on the SIGINT resumable-shutdown return (backup for when the user + // closes the terminal before copying the printed --session-id hint). + // Refreshed hourly so a 5h+ session that crashes still has a fresh + // pointer (staleness checks file mtime, backend TTL is rolling-from-poll). + let pointerRefreshTimer: ReturnType | null = null + // Single-session only: --continue forces single-session mode on resume, + // so a pointer written in multi-session mode would contradict the user's + // config when they try to resume. The resumable-shutdown path is also + // gated to single-session (line ~1254) so the pointer would be orphaned. + if (initialSessionId && spawnMode === 'single-session') { + const { writeBridgePointer } = await import('./bridgePointer.js') + const pointerPayload = { + sessionId: initialSessionId, + environmentId, + source: 'standalone' as const, + } + await writeBridgePointer(config.dir, pointerPayload) + pointerRefreshTimer = setInterval( + writeBridgePointer, + 60 * 60 * 1000, + config.dir, + pointerPayload, + ) + // Don't let the interval keep the process alive on its own. + pointerRefreshTimer.unref?.() + } + + try { + await runBridgeLoop( + config, + environmentId, + environmentSecret, + api, + spawner, + logger, + controller.signal, + undefined, + initialSessionId ?? undefined, + async () => { + // Clear the memoized OAuth token cache so we re-read from secure + // storage, picking up tokens refreshed by child processes. + clearOAuthTokenCache() + // Proactively refresh the token if it's expired on disk too. + await checkAndRefreshOAuthTokenIfNeeded() + return getBridgeAccessToken() + }, + ) + } finally { + if (pointerRefreshTimer !== null) { + clearInterval(pointerRefreshTimer) + } + process.off('SIGINT', onSigint) + process.off('SIGTERM', onSigterm) + process.stdin.off('data', onStdinData) + if (process.stdin.isTTY) { + process.stdin.setRawMode(false) + } + process.stdin.pause() + } + + // The bridge bypasses init.ts (and its graceful shutdown handler), so we + // must exit explicitly. + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(0) +} + +// ─── Headless bridge (daemon worker) ──────────────────────────────────────── + +/** + * Thrown by runBridgeHeadless for configuration issues the supervisor should + * NOT retry (trust not accepted, worktree unavailable, http-not-https). The + * daemon worker catches this and exits with EXIT_CODE_PERMANENT so the + * supervisor parks the worker instead of respawning it on backoff. + */ +export class BridgeHeadlessPermanentError extends Error { + constructor(message: string) { + super(message) + this.name = 'BridgeHeadlessPermanentError' + } +} + +export type HeadlessBridgeOpts = { + dir: string + name?: string + spawnMode: 'same-dir' | 'worktree' + capacity: number + permissionMode?: string + sandbox: boolean + sessionTimeoutMs?: number + createSessionOnStart: boolean + getAccessToken: () => string | undefined + onAuth401: (failedToken: string) => Promise + log: (s: string) => void +} + +/** + * Non-interactive bridge entrypoint for the `remoteControl` daemon worker. + * + * Linear subset of bridgeMain(): no readline dialogs, no stdin key handlers, + * no TUI, no process.exit(). Config comes from the caller (daemon.json), auth + * comes via IPC (supervisor's AuthManager), logs go to the worker's stdout + * pipe. Throws on fatal errors — the worker catches and maps permanent vs + * transient to the right exit code. + * + * Resolves cleanly when `signal` aborts and the poll loop tears down. + */ +export async function runBridgeHeadless( + opts: HeadlessBridgeOpts, + signal: AbortSignal, +): Promise { + const { dir, log } = opts + + // Worker inherits the supervisor's CWD. chdir first so git utilities + // (getBranch/getRemoteUrl) — which read from bootstrap CWD state set + // below — resolve against the right repo. + process.chdir(dir) + const { setOriginalCwd, setCwdState } = await import('../bootstrap/state.js') + setOriginalCwd(dir) + setCwdState(dir) + + const { enableConfigs, checkHasTrustDialogAccepted } = await import( + '../utils/config.js' + ) + enableConfigs() + const { initSinks } = await import('../utils/sinks.js') + initSinks() + + if (!checkHasTrustDialogAccepted()) { + throw new BridgeHeadlessPermanentError( + `Workspace not trusted: ${dir}. Run \`claude\` in that directory first to accept the trust dialog.`, + ) + } + + if (!opts.getAccessToken()) { + // Transient — supervisor's AuthManager may pick up a token on next cycle. + throw new Error(BRIDGE_LOGIN_ERROR) + } + + const { getBridgeBaseUrl } = await import('./bridgeConfig.js') + const baseUrl = getBridgeBaseUrl() + if ( + baseUrl.startsWith('http://') && + !baseUrl.includes('localhost') && + !baseUrl.includes('127.0.0.1') + ) { + throw new BridgeHeadlessPermanentError( + 'Remote Control base URL uses HTTP. Only HTTPS or localhost HTTP is allowed.', + ) + } + const sessionIngressUrl = + process.env.USER_TYPE === 'ant' && + process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL + ? process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL + : baseUrl + + const { getBranch, getRemoteUrl, findGitRoot } = await import( + '../utils/git.js' + ) + const { hasWorktreeCreateHook } = await import('../utils/hooks.js') + + if (opts.spawnMode === 'worktree') { + const worktreeAvailable = + hasWorktreeCreateHook() || findGitRoot(dir) !== null + if (!worktreeAvailable) { + throw new BridgeHeadlessPermanentError( + `Worktree mode requires a git repository or WorktreeCreate hooks. Directory ${dir} has neither.`, + ) + } + } + + const branch = await getBranch() + const gitRepoUrl = await getRemoteUrl() + const machineName = hostname() + const bridgeId = randomUUID() + + const config: BridgeConfig = { + dir, + machineName, + branch, + gitRepoUrl, + maxSessions: opts.capacity, + spawnMode: opts.spawnMode, + verbose: false, + sandbox: opts.sandbox, + bridgeId, + workerType: 'claude_code', + environmentId: randomUUID(), + apiBaseUrl: baseUrl, + sessionIngressUrl, + sessionTimeoutMs: opts.sessionTimeoutMs, + } + + const api = createBridgeApiClient({ + baseUrl, + getAccessToken: opts.getAccessToken, + runnerVersion: MACRO.VERSION, + onDebug: log, + onAuth401: opts.onAuth401, + getTrustedDeviceToken, + }) + + let environmentId: string + let environmentSecret: string + try { + const reg = await api.registerBridgeEnvironment(config) + environmentId = reg.environment_id + environmentSecret = reg.environment_secret + } catch (err) { + // Transient — let supervisor backoff-retry. + throw new Error(`Bridge registration failed: ${errorMessage(err)}`) + } + + const spawner = createSessionSpawner({ + execPath: process.execPath, + scriptArgs: spawnScriptArgs(), + env: process.env, + verbose: false, + sandbox: opts.sandbox, + permissionMode: opts.permissionMode, + onDebug: log, + }) + + const logger = createHeadlessBridgeLogger(log) + logger.printBanner(config, environmentId) + + let initialSessionId: string | undefined + if (opts.createSessionOnStart) { + const { createBridgeSession } = await import('./createSession.js') + try { + const sid = await createBridgeSession({ + environmentId, + title: opts.name, + events: [], + gitRepoUrl, + branch, + signal, + baseUrl, + getAccessToken: opts.getAccessToken, + permissionMode: opts.permissionMode, + }) + if (sid) { + initialSessionId = sid + log(`created initial session ${sid}`) + } + } catch (err) { + log(`session pre-creation failed (non-fatal): ${errorMessage(err)}`) + } + } + + await runBridgeLoop( + config, + environmentId, + environmentSecret, + api, + spawner, + logger, + signal, + undefined, + initialSessionId, + async () => opts.getAccessToken(), + ) +} + +/** BridgeLogger adapter that routes everything to a single line-log fn. */ +function createHeadlessBridgeLogger(log: (s: string) => void): BridgeLogger { + const noop = (): void => {} + return { + printBanner: (cfg, envId) => + log( + `registered environmentId=${envId} dir=${cfg.dir} spawnMode=${cfg.spawnMode} capacity=${cfg.maxSessions}`, + ), + logSessionStart: (id, _prompt) => log(`session start ${id}`), + logSessionComplete: (id, ms) => log(`session complete ${id} (${ms}ms)`), + logSessionFailed: (id, err) => log(`session failed ${id}: ${err}`), + logStatus: log, + logVerbose: log, + logError: s => log(`error: ${s}`), + logReconnected: ms => log(`reconnected after ${ms}ms`), + addSession: (id, _url) => log(`session attached ${id}`), + removeSession: id => log(`session detached ${id}`), + updateIdleStatus: noop, + updateReconnectingStatus: noop, + updateSessionStatus: noop, + updateSessionActivity: noop, + updateSessionCount: noop, + updateFailedStatus: noop, + setSpawnModeDisplay: noop, + setRepoInfo: noop, + setDebugLogPath: noop, + setAttached: noop, + setSessionTitle: noop, + clearStatus: noop, + toggleQr: noop, + refreshDisplay: noop, + } +} diff --git a/bridge/bridgeMessaging.ts b/bridge/bridgeMessaging.ts new file mode 100644 index 0000000..98ece03 --- /dev/null +++ b/bridge/bridgeMessaging.ts @@ -0,0 +1,461 @@ +/** + * Shared transport-layer helpers for bridge message handling. + * + * Extracted from replBridge.ts so both the env-based core (initBridgeCore) + * and the env-less core (initEnvLessBridgeCore) can use the same ingress + * parsing, control-request handling, and echo-dedup machinery. + * + * Everything here is pure — no closure over bridge-specific state. All + * collaborators (transport, sessionId, UUID sets, callbacks) are passed + * as params. + */ + +import { randomUUID } from 'crypto' +import type { SDKMessage } from '../entrypoints/agentSdkTypes.js' +import type { + SDKControlRequest, + SDKControlResponse, +} from '../entrypoints/sdk/controlTypes.js' +import type { SDKResultSuccess } from '../entrypoints/sdk/coreTypes.js' +import { logEvent } from '../services/analytics/index.js' +import { EMPTY_USAGE } from '../services/api/emptyUsage.js' +import type { Message } from '../types/message.js' +import { normalizeControlMessageKeys } from '../utils/controlMessageCompat.js' +import { logForDebugging } from '../utils/debug.js' +import { stripDisplayTagsAllowEmpty } from '../utils/displayTags.js' +import { errorMessage } from '../utils/errors.js' +import type { PermissionMode } from '../utils/permissions/PermissionMode.js' +import { jsonParse } from '../utils/slowOperations.js' +import type { ReplBridgeTransport } from './replBridgeTransport.js' + +// ─── Type guards ───────────────────────────────────────────────────────────── + +/** Type predicate for parsed WebSocket messages. SDKMessage is a + * discriminated union on `type` — validating the discriminant is + * sufficient for the predicate; callers narrow further via the union. */ +export function isSDKMessage(value: unknown): value is SDKMessage { + return ( + value !== null && + typeof value === 'object' && + 'type' in value && + typeof value.type === 'string' + ) +} + +/** Type predicate for control_response messages from the server. */ +export function isSDKControlResponse( + value: unknown, +): value is SDKControlResponse { + return ( + value !== null && + typeof value === 'object' && + 'type' in value && + value.type === 'control_response' && + 'response' in value + ) +} + +/** Type predicate for control_request messages from the server. */ +export function isSDKControlRequest( + value: unknown, +): value is SDKControlRequest { + return ( + value !== null && + typeof value === 'object' && + 'type' in value && + value.type === 'control_request' && + 'request_id' in value && + 'request' in value + ) +} + +/** + * True for message types that should be forwarded to the bridge transport. + * The server only wants user/assistant turns and slash-command system events; + * everything else (tool_result, progress, etc.) is internal REPL chatter. + */ +export function isEligibleBridgeMessage(m: Message): boolean { + // Virtual messages (REPL inner calls) are display-only — bridge/SDK + // consumers see the REPL tool_use/result which summarizes the work. + if ((m.type === 'user' || m.type === 'assistant') && m.isVirtual) { + return false + } + return ( + m.type === 'user' || + m.type === 'assistant' || + (m.type === 'system' && m.subtype === 'local_command') + ) +} + +/** + * Extract title-worthy text from a Message for onUserMessage. Returns + * undefined for messages that shouldn't title the session: non-user, meta + * (nudges), tool results, compact summaries, non-human origins (task + * notifications, channel messages), or pure display-tag content + * (, , etc.). + * + * Synthetic interrupts ([Request interrupted by user]) are NOT filtered here — + * isSyntheticMessage lives in messages.ts (heavy import, pulls command + * registry). The initialMessages path in initReplBridge checks it; the + * writeMessages path reaching an interrupt as the *first* message is + * implausible (an interrupt implies a prior prompt already flowed through). + */ +export function extractTitleText(m: Message): string | undefined { + if (m.type !== 'user' || m.isMeta || m.toolUseResult || m.isCompactSummary) + return undefined + if (m.origin && m.origin.kind !== 'human') return undefined + const content = m.message.content + let raw: string | undefined + if (typeof content === 'string') { + raw = content + } else { + for (const block of content) { + if (block.type === 'text') { + raw = block.text + break + } + } + } + if (!raw) return undefined + const clean = stripDisplayTagsAllowEmpty(raw) + return clean || undefined +} + +// ─── Ingress routing ───────────────────────────────────────────────────────── + +/** + * Parse an ingress WebSocket message and route it to the appropriate handler. + * Ignores messages whose UUID is in recentPostedUUIDs (echoes of what we sent) + * or in recentInboundUUIDs (re-deliveries we've already forwarded — e.g. + * server replayed history after a transport swap lost the seq-num cursor). + */ +export function handleIngressMessage( + data: string, + recentPostedUUIDs: BoundedUUIDSet, + recentInboundUUIDs: BoundedUUIDSet, + onInboundMessage: ((msg: SDKMessage) => void | Promise) | undefined, + onPermissionResponse?: ((response: SDKControlResponse) => void) | undefined, + onControlRequest?: ((request: SDKControlRequest) => void) | undefined, +): void { + try { + const parsed: unknown = normalizeControlMessageKeys(jsonParse(data)) + + // control_response is not an SDKMessage — check before the type guard + if (isSDKControlResponse(parsed)) { + logForDebugging('[bridge:repl] Ingress message type=control_response') + onPermissionResponse?.(parsed) + return + } + + // control_request from the server (initialize, set_model, can_use_tool). + // Must respond promptly or the server kills the WS (~10-14s timeout). + if (isSDKControlRequest(parsed)) { + logForDebugging( + `[bridge:repl] Inbound control_request subtype=${parsed.request.subtype}`, + ) + onControlRequest?.(parsed) + return + } + + if (!isSDKMessage(parsed)) return + + // Check for UUID to detect echoes of our own messages + const uuid = + 'uuid' in parsed && typeof parsed.uuid === 'string' + ? parsed.uuid + : undefined + + if (uuid && recentPostedUUIDs.has(uuid)) { + logForDebugging( + `[bridge:repl] Ignoring echo: type=${parsed.type} uuid=${uuid}`, + ) + return + } + + // Defensive dedup: drop inbound prompts we've already forwarded. The + // SSE seq-num carryover (lastTransportSequenceNum) is the primary fix + // for history-replay; this catches edge cases where that negotiation + // fails (server ignores from_sequence_num, transport died before + // receiving any frames, etc). + if (uuid && recentInboundUUIDs.has(uuid)) { + logForDebugging( + `[bridge:repl] Ignoring re-delivered inbound: type=${parsed.type} uuid=${uuid}`, + ) + return + } + + logForDebugging( + `[bridge:repl] Ingress message type=${parsed.type}${uuid ? ` uuid=${uuid}` : ''}`, + ) + + if (parsed.type === 'user') { + if (uuid) recentInboundUUIDs.add(uuid) + logEvent('tengu_bridge_message_received', { + is_repl: true, + }) + // Fire-and-forget — handler may be async (attachment resolution). + void onInboundMessage?.(parsed) + } else { + logForDebugging( + `[bridge:repl] Ignoring non-user inbound message: type=${parsed.type}`, + ) + } + } catch (err) { + logForDebugging( + `[bridge:repl] Failed to parse ingress message: ${errorMessage(err)}`, + ) + } +} + +// ─── Server-initiated control requests ─────────────────────────────────────── + +export type ServerControlRequestHandlers = { + transport: ReplBridgeTransport | null + sessionId: string + /** + * When true, all mutable requests (interrupt, set_model, set_permission_mode, + * set_max_thinking_tokens) reply with an error instead of false-success. + * initialize still replies success — the server kills the connection otherwise. + * Used by the outbound-only bridge mode and the SDK's /bridge subpath so claude.ai sees a + * proper error instead of "action succeeded but nothing happened locally". + */ + outboundOnly?: boolean + onInterrupt?: () => void + onSetModel?: (model: string | undefined) => void + onSetMaxThinkingTokens?: (maxTokens: number | null) => void + onSetPermissionMode?: ( + mode: PermissionMode, + ) => { ok: true } | { ok: false; error: string } +} + +const OUTBOUND_ONLY_ERROR = + 'This session is outbound-only. Enable Remote Control locally to allow inbound control.' + +/** + * Respond to inbound control_request messages from the server. The server + * sends these for session lifecycle events (initialize, set_model) and + * for turn-level coordination (interrupt, set_max_thinking_tokens). If we + * don't respond, the server hangs and kills the WS after ~10-14s. + * + * Previously a closure inside initBridgeCore's onWorkReceived; now takes + * collaborators as params so both cores can use it. + */ +export function handleServerControlRequest( + request: SDKControlRequest, + handlers: ServerControlRequestHandlers, +): void { + const { + transport, + sessionId, + outboundOnly, + onInterrupt, + onSetModel, + onSetMaxThinkingTokens, + onSetPermissionMode, + } = handlers + if (!transport) { + logForDebugging( + '[bridge:repl] Cannot respond to control_request: transport not configured', + ) + return + } + + let response: SDKControlResponse + + // Outbound-only: reply error for mutable requests so claude.ai doesn't show + // false success. initialize must still succeed (server kills the connection + // if it doesn't — see comment above). + if (outboundOnly && request.request.subtype !== 'initialize') { + response = { + type: 'control_response', + response: { + subtype: 'error', + request_id: request.request_id, + error: OUTBOUND_ONLY_ERROR, + }, + } + const event = { ...response, session_id: sessionId } + void transport.write(event) + logForDebugging( + `[bridge:repl] Rejected ${request.request.subtype} (outbound-only) request_id=${request.request_id}`, + ) + return + } + + switch (request.request.subtype) { + case 'initialize': + // Respond with minimal capabilities — the REPL handles + // commands, models, and account info itself. + response = { + type: 'control_response', + response: { + subtype: 'success', + request_id: request.request_id, + response: { + commands: [], + output_style: 'normal', + available_output_styles: ['normal'], + models: [], + account: {}, + pid: process.pid, + }, + }, + } + break + + case 'set_model': + onSetModel?.(request.request.model) + response = { + type: 'control_response', + response: { + subtype: 'success', + request_id: request.request_id, + }, + } + break + + case 'set_max_thinking_tokens': + onSetMaxThinkingTokens?.(request.request.max_thinking_tokens) + response = { + type: 'control_response', + response: { + subtype: 'success', + request_id: request.request_id, + }, + } + break + + case 'set_permission_mode': { + // The callback returns a policy verdict so we can send an error + // control_response without importing isAutoModeGateEnabled / + // isBypassPermissionsModeDisabled here (bootstrap-isolation). If no + // callback is registered (daemon context, which doesn't wire this — + // see daemonBridge.ts), return an error verdict rather than a silent + // false-success: the mode is never actually applied in that context, + // so success would lie to the client. + const verdict = onSetPermissionMode?.(request.request.mode) ?? { + ok: false, + error: + 'set_permission_mode is not supported in this context (onSetPermissionMode callback not registered)', + } + if (verdict.ok) { + response = { + type: 'control_response', + response: { + subtype: 'success', + request_id: request.request_id, + }, + } + } else { + response = { + type: 'control_response', + response: { + subtype: 'error', + request_id: request.request_id, + error: verdict.error, + }, + } + } + break + } + + case 'interrupt': + onInterrupt?.() + response = { + type: 'control_response', + response: { + subtype: 'success', + request_id: request.request_id, + }, + } + break + + default: + // Unknown subtype — respond with error so the server doesn't + // hang waiting for a reply that never comes. + response = { + type: 'control_response', + response: { + subtype: 'error', + request_id: request.request_id, + error: `REPL bridge does not handle control_request subtype: ${request.request.subtype}`, + }, + } + } + + const event = { ...response, session_id: sessionId } + void transport.write(event) + logForDebugging( + `[bridge:repl] Sent control_response for ${request.request.subtype} request_id=${request.request_id} result=${response.response.subtype}`, + ) +} + +// ─── Result message (for session archival on teardown) ─────────────────────── + +/** + * Build a minimal `SDKResultSuccess` message for session archival. + * The server needs this event before a WS close to trigger archival. + */ +export function makeResultMessage(sessionId: string): SDKResultSuccess { + return { + type: 'result', + subtype: 'success', + duration_ms: 0, + duration_api_ms: 0, + is_error: false, + num_turns: 0, + result: '', + stop_reason: null, + total_cost_usd: 0, + usage: { ...EMPTY_USAGE }, + modelUsage: {}, + permission_denials: [], + session_id: sessionId, + uuid: randomUUID(), + } +} + +// ─── BoundedUUIDSet (echo-dedup ring buffer) ───────────────────────────────── + +/** + * FIFO-bounded set backed by a circular buffer. Evicts the oldest entry + * when capacity is reached, keeping memory usage constant at O(capacity). + * + * Messages are added in chronological order, so evicted entries are always + * the oldest. The caller relies on external ordering (the hook's + * lastWrittenIndexRef) as the primary dedup — this set is a secondary + * safety net for echo filtering and race-condition dedup. + */ +export class BoundedUUIDSet { + private readonly capacity: number + private readonly ring: (string | undefined)[] + private readonly set = new Set() + private writeIdx = 0 + + constructor(capacity: number) { + this.capacity = capacity + this.ring = new Array(capacity) + } + + add(uuid: string): void { + if (this.set.has(uuid)) return + // Evict the entry at the current write position (if occupied) + const evicted = this.ring[this.writeIdx] + if (evicted !== undefined) { + this.set.delete(evicted) + } + this.ring[this.writeIdx] = uuid + this.set.add(uuid) + this.writeIdx = (this.writeIdx + 1) % this.capacity + } + + has(uuid: string): boolean { + return this.set.has(uuid) + } + + clear(): void { + this.set.clear() + this.ring.fill(undefined) + this.writeIdx = 0 + } +} diff --git a/bridge/bridgePermissionCallbacks.ts b/bridge/bridgePermissionCallbacks.ts new file mode 100644 index 0000000..feaee66 --- /dev/null +++ b/bridge/bridgePermissionCallbacks.ts @@ -0,0 +1,43 @@ +import type { PermissionUpdate } from '../utils/permissions/PermissionUpdateSchema.js' + +type BridgePermissionResponse = { + behavior: 'allow' | 'deny' + updatedInput?: Record + updatedPermissions?: PermissionUpdate[] + message?: string +} + +type BridgePermissionCallbacks = { + sendRequest( + requestId: string, + toolName: string, + input: Record, + toolUseId: string, + description: string, + permissionSuggestions?: PermissionUpdate[], + blockedPath?: string, + ): void + sendResponse(requestId: string, response: BridgePermissionResponse): void + /** Cancel a pending control_request so the web app can dismiss its prompt. */ + cancelRequest(requestId: string): void + onResponse( + requestId: string, + handler: (response: BridgePermissionResponse) => void, + ): () => void // returns unsubscribe +} + +/** Type predicate for validating a parsed control_response payload + * as a BridgePermissionResponse. Checks the required `behavior` + * discriminant rather than using an unsafe `as` cast. */ +function isBridgePermissionResponse( + value: unknown, +): value is BridgePermissionResponse { + if (!value || typeof value !== 'object') return false + return ( + 'behavior' in value && + (value.behavior === 'allow' || value.behavior === 'deny') + ) +} + +export { isBridgePermissionResponse } +export type { BridgePermissionCallbacks, BridgePermissionResponse } diff --git a/bridge/bridgePointer.ts b/bridge/bridgePointer.ts new file mode 100644 index 0000000..c32befc --- /dev/null +++ b/bridge/bridgePointer.ts @@ -0,0 +1,210 @@ +import { mkdir, readFile, stat, unlink, writeFile } from 'fs/promises' +import { dirname, join } from 'path' +import { z } from 'zod/v4' +import { logForDebugging } from '../utils/debug.js' +import { isENOENT } from '../utils/errors.js' +import { getWorktreePathsPortable } from '../utils/getWorktreePathsPortable.js' +import { lazySchema } from '../utils/lazySchema.js' +import { + getProjectsDir, + sanitizePath, +} from '../utils/sessionStoragePortable.js' +import { jsonParse, jsonStringify } from '../utils/slowOperations.js' + +/** + * Upper bound on worktree fanout. git worktree list is naturally bounded + * (50 is a LOT), but this caps the parallel stat() burst and guards against + * pathological setups. Above this, --continue falls back to current-dir-only. + */ +const MAX_WORKTREE_FANOUT = 50 + +/** + * Crash-recovery pointer for Remote Control sessions. + * + * Written immediately after a bridge session is created, periodically + * refreshed during the session, and cleared on clean shutdown. If the + * process dies unclean (crash, kill -9, terminal closed), the pointer + * persists. On next startup, `claude remote-control` detects it and offers + * to resume via the --session-id flow from #20460. + * + * Staleness is checked against the file's mtime (not an embedded timestamp) + * so that a periodic re-write with the same content serves as a refresh — + * matches the backend's rolling BRIDGE_LAST_POLL_TTL (4h) semantics. A + * bridge that's been polling for 5+ hours and then crashes still has a + * fresh pointer as long as the refresh ran within the window. + * + * Scoped per working directory (alongside transcript JSONL files) so two + * concurrent bridges in different repos don't clobber each other. + */ + +export const BRIDGE_POINTER_TTL_MS = 4 * 60 * 60 * 1000 + +const BridgePointerSchema = lazySchema(() => + z.object({ + sessionId: z.string(), + environmentId: z.string(), + source: z.enum(['standalone', 'repl']), + }), +) + +export type BridgePointer = z.infer> + +export function getBridgePointerPath(dir: string): string { + return join(getProjectsDir(), sanitizePath(dir), 'bridge-pointer.json') +} + +/** + * Write the pointer. Also used to refresh mtime during long sessions — + * calling with the same IDs is a cheap no-content-change write that bumps + * the staleness clock. Best-effort — a crash-recovery file must never + * itself cause a crash. Logs and swallows on error. + */ +export async function writeBridgePointer( + dir: string, + pointer: BridgePointer, +): Promise { + const path = getBridgePointerPath(dir) + try { + await mkdir(dirname(path), { recursive: true }) + await writeFile(path, jsonStringify(pointer), 'utf8') + logForDebugging(`[bridge:pointer] wrote ${path}`) + } catch (err: unknown) { + logForDebugging(`[bridge:pointer] write failed: ${err}`, { level: 'warn' }) + } +} + +/** + * Read the pointer and its age (ms since last write). Operates directly + * and handles errors — no existence check (CLAUDE.md TOCTOU rule). Returns + * null on any failure: missing file, corrupted JSON, schema mismatch, or + * stale (mtime > 4h ago). Stale/invalid pointers are deleted so they don't + * keep re-prompting after the backend has already GC'd the env. + */ +export async function readBridgePointer( + dir: string, +): Promise<(BridgePointer & { ageMs: number }) | null> { + const path = getBridgePointerPath(dir) + let raw: string + let mtimeMs: number + try { + // stat for mtime (staleness anchor), then read. Two syscalls, but both + // are needed — mtime IS the data we return, not a TOCTOU guard. + mtimeMs = (await stat(path)).mtimeMs + raw = await readFile(path, 'utf8') + } catch { + return null + } + + const parsed = BridgePointerSchema().safeParse(safeJsonParse(raw)) + if (!parsed.success) { + logForDebugging(`[bridge:pointer] invalid schema, clearing: ${path}`) + await clearBridgePointer(dir) + return null + } + + const ageMs = Math.max(0, Date.now() - mtimeMs) + if (ageMs > BRIDGE_POINTER_TTL_MS) { + logForDebugging(`[bridge:pointer] stale (>4h mtime), clearing: ${path}`) + await clearBridgePointer(dir) + return null + } + + return { ...parsed.data, ageMs } +} + +/** + * Worktree-aware read for `--continue`. The REPL bridge writes its pointer + * to `getOriginalCwd()` which EnterWorktreeTool/activeWorktreeSession can + * mutate to a worktree path — but `claude remote-control --continue` runs + * with `resolve('.')` = shell CWD. This fans out across git worktree + * siblings to find the freshest pointer, matching /resume's semantics. + * + * Fast path: checks `dir` first. Only shells out to `git worktree list` if + * that misses — the common case (pointer in launch dir) is one stat, zero + * exec. Fanout reads run in parallel; capped at MAX_WORKTREE_FANOUT. + * + * Returns the pointer AND the dir it was found in, so the caller can clear + * the right file on resume failure. + */ +export async function readBridgePointerAcrossWorktrees( + dir: string, +): Promise<{ pointer: BridgePointer & { ageMs: number }; dir: string } | null> { + // Fast path: current dir. Covers standalone bridge (always matches) and + // REPL bridge when no worktree mutation happened. + const here = await readBridgePointer(dir) + if (here) { + return { pointer: here, dir } + } + + // Fanout: scan worktree siblings. getWorktreePathsPortable has a 5s + // timeout and returns [] on any error (not a git repo, git not installed). + const worktrees = await getWorktreePathsPortable(dir) + if (worktrees.length <= 1) return null + if (worktrees.length > MAX_WORKTREE_FANOUT) { + logForDebugging( + `[bridge:pointer] ${worktrees.length} worktrees exceeds fanout cap ${MAX_WORKTREE_FANOUT}, skipping`, + ) + return null + } + + // Dedupe against `dir` so we don't re-stat it. sanitizePath normalizes + // case/separators so worktree-list output matches our fast-path key even + // on Windows where git may emit C:/ vs stored c:/. + const dirKey = sanitizePath(dir) + const candidates = worktrees.filter(wt => sanitizePath(wt) !== dirKey) + + // Parallel stat+read. Each readBridgePointer is a stat() that ENOENTs + // for worktrees with no pointer (cheap) plus a ~100-byte read for the + // rare ones that have one. Promise.all → latency ≈ slowest single stat. + const results = await Promise.all( + candidates.map(async wt => { + const p = await readBridgePointer(wt) + return p ? { pointer: p, dir: wt } : null + }), + ) + + // Pick freshest (lowest ageMs). The pointer stores environmentId so + // resume reconnects to the right env regardless of which worktree + // --continue was invoked from. + let freshest: { + pointer: BridgePointer & { ageMs: number } + dir: string + } | null = null + for (const r of results) { + if (r && (!freshest || r.pointer.ageMs < freshest.pointer.ageMs)) { + freshest = r + } + } + if (freshest) { + logForDebugging( + `[bridge:pointer] fanout found pointer in worktree ${freshest.dir} (ageMs=${freshest.pointer.ageMs})`, + ) + } + return freshest +} + +/** + * Delete the pointer. Idempotent — ENOENT is expected when the process + * shut down clean previously. + */ +export async function clearBridgePointer(dir: string): Promise { + const path = getBridgePointerPath(dir) + try { + await unlink(path) + logForDebugging(`[bridge:pointer] cleared ${path}`) + } catch (err: unknown) { + if (!isENOENT(err)) { + logForDebugging(`[bridge:pointer] clear failed: ${err}`, { + level: 'warn', + }) + } + } +} + +function safeJsonParse(raw: string): unknown { + try { + return jsonParse(raw) + } catch { + return null + } +} diff --git a/bridge/bridgeStatusUtil.ts b/bridge/bridgeStatusUtil.ts new file mode 100644 index 0000000..90de462 --- /dev/null +++ b/bridge/bridgeStatusUtil.ts @@ -0,0 +1,163 @@ +import { + getClaudeAiBaseUrl, + getRemoteSessionUrl, +} from '../constants/product.js' +import { stringWidth } from '../ink/stringWidth.js' +import { formatDuration, truncateToWidth } from '../utils/format.js' +import { getGraphemeSegmenter } from '../utils/intl.js' + +/** Bridge status state machine states. */ +export type StatusState = + | 'idle' + | 'attached' + | 'titled' + | 'reconnecting' + | 'failed' + +/** How long a tool activity line stays visible after last tool_start (ms). */ +export const TOOL_DISPLAY_EXPIRY_MS = 30_000 + +/** Interval for the shimmer animation tick (ms). */ +export const SHIMMER_INTERVAL_MS = 150 + +export function timestamp(): string { + const now = new Date() + const h = String(now.getHours()).padStart(2, '0') + const m = String(now.getMinutes()).padStart(2, '0') + const s = String(now.getSeconds()).padStart(2, '0') + return `${h}:${m}:${s}` +} + +export { formatDuration, truncateToWidth as truncatePrompt } + +/** Abbreviate a tool activity summary for the trail display. */ +export function abbreviateActivity(summary: string): string { + return truncateToWidth(summary, 30) +} + +/** Build the connect URL shown when the bridge is idle. */ +export function buildBridgeConnectUrl( + environmentId: string, + ingressUrl?: string, +): string { + const baseUrl = getClaudeAiBaseUrl(undefined, ingressUrl) + return `${baseUrl}/code?bridge=${environmentId}` +} + +/** + * Build the session URL shown when a session is attached. Delegates to + * getRemoteSessionUrl for the cse_→session_ prefix translation, then appends + * the v1-specific ?bridge={environmentId} query. + */ +export function buildBridgeSessionUrl( + sessionId: string, + environmentId: string, + ingressUrl?: string, +): string { + return `${getRemoteSessionUrl(sessionId, ingressUrl)}?bridge=${environmentId}` +} + +/** Compute the glimmer index for a reverse-sweep shimmer animation. */ +export function computeGlimmerIndex( + tick: number, + messageWidth: number, +): number { + const cycleLength = messageWidth + 20 + return messageWidth + 10 - (tick % cycleLength) +} + +/** + * Split text into three segments by visual column position for shimmer rendering. + * + * Uses grapheme segmentation and `stringWidth` so the split is correct for + * multi-byte characters, emoji, and CJK glyphs. + * + * Returns `{ before, shimmer, after }` strings. Both renderers (chalk in + * bridgeUI.ts and React/Ink in bridge.tsx) apply their own coloring to + * these segments. + */ +export function computeShimmerSegments( + text: string, + glimmerIndex: number, +): { before: string; shimmer: string; after: string } { + const messageWidth = stringWidth(text) + const shimmerStart = glimmerIndex - 1 + const shimmerEnd = glimmerIndex + 1 + + // When shimmer is offscreen, return all text as "before" + if (shimmerStart >= messageWidth || shimmerEnd < 0) { + return { before: text, shimmer: '', after: '' } + } + + // Split into at most 3 segments by visual column position + const clampedStart = Math.max(0, shimmerStart) + let colPos = 0 + let before = '' + let shimmer = '' + let after = '' + for (const { segment } of getGraphemeSegmenter().segment(text)) { + const segWidth = stringWidth(segment) + if (colPos + segWidth <= clampedStart) { + before += segment + } else if (colPos > shimmerEnd) { + after += segment + } else { + shimmer += segment + } + colPos += segWidth + } + + return { before, shimmer, after } +} + +/** Computed bridge status label and color from connection state. */ +export type BridgeStatusInfo = { + label: + | 'Remote Control failed' + | 'Remote Control reconnecting' + | 'Remote Control active' + | 'Remote Control connecting\u2026' + color: 'error' | 'warning' | 'success' +} + +/** Derive a status label and color from the bridge connection state. */ +export function getBridgeStatus({ + error, + connected, + sessionActive, + reconnecting, +}: { + error: string | undefined + connected: boolean + sessionActive: boolean + reconnecting: boolean +}): BridgeStatusInfo { + if (error) return { label: 'Remote Control failed', color: 'error' } + if (reconnecting) + return { label: 'Remote Control reconnecting', color: 'warning' } + if (sessionActive || connected) + return { label: 'Remote Control active', color: 'success' } + return { label: 'Remote Control connecting\u2026', color: 'warning' } +} + +/** Footer text shown when bridge is idle (Ready state). */ +export function buildIdleFooterText(url: string): string { + return `Code everywhere with the Claude app or ${url}` +} + +/** Footer text shown when a session is active (Connected state). */ +export function buildActiveFooterText(url: string): string { + return `Continue coding in the Claude app or ${url}` +} + +/** Footer text shown when the bridge has failed. */ +export const FAILED_FOOTER_TEXT = 'Something went wrong, please try again' + +/** + * Wrap text in an OSC 8 terminal hyperlink. Zero visual width for layout purposes. + * strip-ansi (used by stringWidth) correctly strips these sequences, so + * countVisualLines in bridgeUI.ts remains accurate. + */ +export function wrapWithOsc8Link(text: string, url: string): string { + return `\x1b]8;;${url}\x07${text}\x1b]8;;\x07` +} diff --git a/bridge/bridgeUI.ts b/bridge/bridgeUI.ts new file mode 100644 index 0000000..5149839 --- /dev/null +++ b/bridge/bridgeUI.ts @@ -0,0 +1,530 @@ +import chalk from 'chalk' +import { toString as qrToString } from 'qrcode' +import { + BRIDGE_FAILED_INDICATOR, + BRIDGE_READY_INDICATOR, + BRIDGE_SPINNER_FRAMES, +} from '../constants/figures.js' +import { stringWidth } from '../ink/stringWidth.js' +import { logForDebugging } from '../utils/debug.js' +import { + buildActiveFooterText, + buildBridgeConnectUrl, + buildBridgeSessionUrl, + buildIdleFooterText, + FAILED_FOOTER_TEXT, + formatDuration, + type StatusState, + TOOL_DISPLAY_EXPIRY_MS, + timestamp, + truncatePrompt, + wrapWithOsc8Link, +} from './bridgeStatusUtil.js' +import type { + BridgeConfig, + BridgeLogger, + SessionActivity, + SpawnMode, +} from './types.js' + +const QR_OPTIONS = { + type: 'utf8' as const, + errorCorrectionLevel: 'L' as const, + small: true, +} + +/** Generate a QR code and return its lines. */ +async function generateQr(url: string): Promise { + const qr = await qrToString(url, QR_OPTIONS) + return qr.split('\n').filter((line: string) => line.length > 0) +} + +export function createBridgeLogger(options: { + verbose: boolean + write?: (s: string) => void +}): BridgeLogger { + const write = options.write ?? ((s: string) => process.stdout.write(s)) + const verbose = options.verbose + + // Track how many status lines are currently displayed at the bottom + let statusLineCount = 0 + + // Status state machine + let currentState: StatusState = 'idle' + let currentStateText = 'Ready' + let repoName = '' + let branch = '' + let debugLogPath = '' + + // Connect URL (built in printBanner with correct base for staging/prod) + let connectUrl = '' + let cachedIngressUrl = '' + let cachedEnvironmentId = '' + let activeSessionUrl: string | null = null + + // QR code lines for the current URL + let qrLines: string[] = [] + let qrVisible = false + + // Tool activity for the second status line + let lastToolSummary: string | null = null + let lastToolTime = 0 + + // Session count indicator (shown when multi-session mode is enabled) + let sessionActive = 0 + let sessionMax = 1 + // Spawn mode shown in the session-count line + gates the `w` hint + let spawnModeDisplay: 'same-dir' | 'worktree' | null = null + let spawnMode: SpawnMode = 'single-session' + + // Per-session display info for the multi-session bullet list (keyed by compat sessionId) + const sessionDisplayInfo = new Map< + string, + { title?: string; url: string; activity?: SessionActivity } + >() + + // Connecting spinner state + let connectingTimer: ReturnType | null = null + let connectingTick = 0 + + /** + * Count how many visual terminal rows a string occupies, accounting for + * line wrapping. Each `\n` is one row, and content wider than the terminal + * wraps to additional rows. + */ + function countVisualLines(text: string): number { + // eslint-disable-next-line custom-rules/prefer-use-terminal-size + const cols = process.stdout.columns || 80 // non-React CLI context + let count = 0 + // Split on newlines to get logical lines + for (const logical of text.split('\n')) { + if (logical.length === 0) { + // Empty segment between consecutive \n — counts as 1 row + count++ + continue + } + const width = stringWidth(logical) + count += Math.max(1, Math.ceil(width / cols)) + } + // The trailing \n in "line\n" produces an empty last element — don't count it + // because the cursor sits at the start of the next line, not a new visual row. + if (text.endsWith('\n')) { + count-- + } + return count + } + + /** Write a status line and track its visual line count. */ + function writeStatus(text: string): void { + write(text) + statusLineCount += countVisualLines(text) + } + + /** Clear any currently displayed status lines. */ + function clearStatusLines(): void { + if (statusLineCount <= 0) return + logForDebugging(`[bridge:ui] clearStatusLines count=${statusLineCount}`) + // Move cursor up to the start of the status block, then erase everything below + write(`\x1b[${statusLineCount}A`) // cursor up N lines + write('\x1b[J') // erase from cursor to end of screen + statusLineCount = 0 + } + + /** Print a permanent log line, clearing status first and restoring after. */ + function printLog(line: string): void { + clearStatusLines() + write(line) + } + + /** Regenerate the QR code with the given URL. */ + function regenerateQr(url: string): void { + generateQr(url) + .then(lines => { + qrLines = lines + renderStatusLine() + }) + .catch(e => { + logForDebugging(`QR code generation failed: ${e}`, { level: 'error' }) + }) + } + + /** Render the connecting spinner line (shown before first updateIdleStatus). */ + function renderConnectingLine(): void { + clearStatusLines() + + const frame = + BRIDGE_SPINNER_FRAMES[connectingTick % BRIDGE_SPINNER_FRAMES.length]! + let suffix = '' + if (repoName) { + suffix += chalk.dim(' \u00b7 ') + chalk.dim(repoName) + } + if (branch) { + suffix += chalk.dim(' \u00b7 ') + chalk.dim(branch) + } + writeStatus( + `${chalk.yellow(frame)} ${chalk.yellow('Connecting')}${suffix}\n`, + ) + } + + /** Start the connecting spinner. Stopped by first updateIdleStatus(). */ + function startConnecting(): void { + stopConnecting() + renderConnectingLine() + connectingTimer = setInterval(() => { + connectingTick++ + renderConnectingLine() + }, 150) + } + + /** Stop the connecting spinner. */ + function stopConnecting(): void { + if (connectingTimer) { + clearInterval(connectingTimer) + connectingTimer = null + } + } + + /** Render and write the current status lines based on state. */ + function renderStatusLine(): void { + if (currentState === 'reconnecting' || currentState === 'failed') { + // These states are handled separately (updateReconnectingStatus / + // updateFailedStatus). Return before clearing so callers like toggleQr + // and setSpawnModeDisplay don't blank the display during these states. + return + } + + clearStatusLines() + + const isIdle = currentState === 'idle' + + // QR code above the status line + if (qrVisible) { + for (const line of qrLines) { + writeStatus(`${chalk.dim(line)}\n`) + } + } + + // Determine indicator and colors based on state + const indicator = BRIDGE_READY_INDICATOR + const indicatorColor = isIdle ? chalk.green : chalk.cyan + const baseColor = isIdle ? chalk.green : chalk.cyan + const stateText = baseColor(currentStateText) + + // Build the suffix with repo and branch + let suffix = '' + if (repoName) { + suffix += chalk.dim(' \u00b7 ') + chalk.dim(repoName) + } + // In worktree mode each session gets its own branch, so showing the + // bridge's branch would be misleading. + if (branch && spawnMode !== 'worktree') { + suffix += chalk.dim(' \u00b7 ') + chalk.dim(branch) + } + + if (process.env.USER_TYPE === 'ant' && debugLogPath) { + writeStatus( + `${chalk.yellow('[ANT-ONLY] Logs:')} ${chalk.dim(debugLogPath)}\n`, + ) + } + writeStatus(`${indicatorColor(indicator)} ${stateText}${suffix}\n`) + + // Session count and per-session list (multi-session mode only) + if (sessionMax > 1) { + const modeHint = + spawnMode === 'worktree' + ? 'New sessions will be created in an isolated worktree' + : 'New sessions will be created in the current directory' + writeStatus( + ` ${chalk.dim(`Capacity: ${sessionActive}/${sessionMax} \u00b7 ${modeHint}`)}\n`, + ) + for (const [, info] of sessionDisplayInfo) { + const titleText = info.title + ? truncatePrompt(info.title, 35) + : chalk.dim('Attached') + const titleLinked = wrapWithOsc8Link(titleText, info.url) + const act = info.activity + const showAct = act && act.type !== 'result' && act.type !== 'error' + const actText = showAct + ? chalk.dim(` ${truncatePrompt(act.summary, 40)}`) + : '' + writeStatus(` ${titleLinked}${actText} +`) + } + } + + // Mode line for spawn modes with a single slot (or true single-session mode) + if (sessionMax === 1) { + const modeText = + spawnMode === 'single-session' + ? 'Single session \u00b7 exits when complete' + : spawnMode === 'worktree' + ? `Capacity: ${sessionActive}/1 \u00b7 New sessions will be created in an isolated worktree` + : `Capacity: ${sessionActive}/1 \u00b7 New sessions will be created in the current directory` + writeStatus(` ${chalk.dim(modeText)}\n`) + } + + // Tool activity line for single-session mode + if ( + sessionMax === 1 && + !isIdle && + lastToolSummary && + Date.now() - lastToolTime < TOOL_DISPLAY_EXPIRY_MS + ) { + writeStatus(` ${chalk.dim(truncatePrompt(lastToolSummary, 60))}\n`) + } + + // Blank line separator before footer + const url = activeSessionUrl ?? connectUrl + if (url) { + writeStatus('\n') + const footerText = isIdle + ? buildIdleFooterText(url) + : buildActiveFooterText(url) + const qrHint = qrVisible + ? chalk.dim.italic('space to hide QR code') + : chalk.dim.italic('space to show QR code') + const toggleHint = spawnModeDisplay + ? chalk.dim.italic(' \u00b7 w to toggle spawn mode') + : '' + writeStatus(`${chalk.dim(footerText)}\n`) + writeStatus(`${qrHint}${toggleHint}\n`) + } + } + + return { + printBanner(config: BridgeConfig, environmentId: string): void { + cachedIngressUrl = config.sessionIngressUrl + cachedEnvironmentId = environmentId + connectUrl = buildBridgeConnectUrl(environmentId, cachedIngressUrl) + regenerateQr(connectUrl) + + if (verbose) { + write(chalk.dim(`Remote Control`) + ` v${MACRO.VERSION}\n`) + } + if (verbose) { + if (config.spawnMode !== 'single-session') { + write(chalk.dim(`Spawn mode: `) + `${config.spawnMode}\n`) + write( + chalk.dim(`Max concurrent sessions: `) + `${config.maxSessions}\n`, + ) + } + write(chalk.dim(`Environment ID: `) + `${environmentId}\n`) + } + if (config.sandbox) { + write(chalk.dim(`Sandbox: `) + `${chalk.green('Enabled')}\n`) + } + write('\n') + + // Start connecting spinner — first updateIdleStatus() will stop it + startConnecting() + }, + + logSessionStart(sessionId: string, prompt: string): void { + if (verbose) { + const short = truncatePrompt(prompt, 80) + printLog( + chalk.dim(`[${timestamp()}]`) + + ` Session started: ${chalk.white(`"${short}"`)} (${chalk.dim(sessionId)})\n`, + ) + } + }, + + logSessionComplete(sessionId: string, durationMs: number): void { + printLog( + chalk.dim(`[${timestamp()}]`) + + ` Session ${chalk.green('completed')} (${formatDuration(durationMs)}) ${chalk.dim(sessionId)}\n`, + ) + }, + + logSessionFailed(sessionId: string, error: string): void { + printLog( + chalk.dim(`[${timestamp()}]`) + + ` Session ${chalk.red('failed')}: ${error} ${chalk.dim(sessionId)}\n`, + ) + }, + + logStatus(message: string): void { + printLog(chalk.dim(`[${timestamp()}]`) + ` ${message}\n`) + }, + + logVerbose(message: string): void { + if (verbose) { + printLog(chalk.dim(`[${timestamp()}] ${message}`) + '\n') + } + }, + + logError(message: string): void { + printLog(chalk.red(`[${timestamp()}] Error: ${message}`) + '\n') + }, + + logReconnected(disconnectedMs: number): void { + printLog( + chalk.dim(`[${timestamp()}]`) + + ` ${chalk.green('Reconnected')} after ${formatDuration(disconnectedMs)}\n`, + ) + }, + + setRepoInfo(repo: string, branchName: string): void { + repoName = repo + branch = branchName + }, + + setDebugLogPath(path: string): void { + debugLogPath = path + }, + + updateIdleStatus(): void { + stopConnecting() + + currentState = 'idle' + currentStateText = 'Ready' + lastToolSummary = null + lastToolTime = 0 + activeSessionUrl = null + regenerateQr(connectUrl) + renderStatusLine() + }, + + setAttached(sessionId: string): void { + stopConnecting() + currentState = 'attached' + currentStateText = 'Connected' + lastToolSummary = null + lastToolTime = 0 + // Multi-session: keep footer/QR on the environment connect URL so users + // can spawn more sessions. Per-session links are in the bullet list. + if (sessionMax <= 1) { + activeSessionUrl = buildBridgeSessionUrl( + sessionId, + cachedEnvironmentId, + cachedIngressUrl, + ) + regenerateQr(activeSessionUrl) + } + renderStatusLine() + }, + + updateReconnectingStatus(delayStr: string, elapsedStr: string): void { + stopConnecting() + clearStatusLines() + currentState = 'reconnecting' + + // QR code above the status line + if (qrVisible) { + for (const line of qrLines) { + writeStatus(`${chalk.dim(line)}\n`) + } + } + + const frame = + BRIDGE_SPINNER_FRAMES[connectingTick % BRIDGE_SPINNER_FRAMES.length]! + connectingTick++ + writeStatus( + `${chalk.yellow(frame)} ${chalk.yellow('Reconnecting')} ${chalk.dim('\u00b7')} ${chalk.dim(`retrying in ${delayStr}`)} ${chalk.dim('\u00b7')} ${chalk.dim(`disconnected ${elapsedStr}`)}\n`, + ) + }, + + updateFailedStatus(error: string): void { + stopConnecting() + clearStatusLines() + currentState = 'failed' + + let suffix = '' + if (repoName) { + suffix += chalk.dim(' \u00b7 ') + chalk.dim(repoName) + } + if (branch) { + suffix += chalk.dim(' \u00b7 ') + chalk.dim(branch) + } + + writeStatus( + `${chalk.red(BRIDGE_FAILED_INDICATOR)} ${chalk.red('Remote Control Failed')}${suffix}\n`, + ) + writeStatus(`${chalk.dim(FAILED_FOOTER_TEXT)}\n`) + + if (error) { + writeStatus(`${chalk.red(error)}\n`) + } + }, + + updateSessionStatus( + _sessionId: string, + _elapsed: string, + activity: SessionActivity, + _trail: string[], + ): void { + // Cache tool activity for the second status line + if (activity.type === 'tool_start') { + lastToolSummary = activity.summary + lastToolTime = Date.now() + } + renderStatusLine() + }, + + clearStatus(): void { + stopConnecting() + clearStatusLines() + }, + + toggleQr(): void { + qrVisible = !qrVisible + renderStatusLine() + }, + + updateSessionCount(active: number, max: number, mode: SpawnMode): void { + if (sessionActive === active && sessionMax === max && spawnMode === mode) + return + sessionActive = active + sessionMax = max + spawnMode = mode + // Don't re-render here — the status ticker calls renderStatusLine + // on its own cadence, and the next tick will pick up the new values. + }, + + setSpawnModeDisplay(mode: 'same-dir' | 'worktree' | null): void { + if (spawnModeDisplay === mode) return + spawnModeDisplay = mode + // Also sync the #21118-added spawnMode so the next render shows correct + // mode hint + branch visibility. Don't render here — matches + // updateSessionCount: called before printBanner (initial setup) and + // again from the `w` handler (which follows with refreshDisplay). + if (mode) spawnMode = mode + }, + + addSession(sessionId: string, url: string): void { + sessionDisplayInfo.set(sessionId, { url }) + }, + + updateSessionActivity(sessionId: string, activity: SessionActivity): void { + const info = sessionDisplayInfo.get(sessionId) + if (!info) return + info.activity = activity + }, + + setSessionTitle(sessionId: string, title: string): void { + const info = sessionDisplayInfo.get(sessionId) + if (!info) return + info.title = title + // Guard against reconnecting/failed — renderStatusLine clears then returns + // early for those states, which would erase the spinner/error. + if (currentState === 'reconnecting' || currentState === 'failed') return + if (sessionMax === 1) { + // Single-session: show title in the main status line too. + currentState = 'titled' + currentStateText = truncatePrompt(title, 40) + } + renderStatusLine() + }, + + removeSession(sessionId: string): void { + sessionDisplayInfo.delete(sessionId) + }, + + refreshDisplay(): void { + // Skip during reconnecting/failed — renderStatusLine clears then returns + // early for those states, which would erase the spinner/error. + if (currentState === 'reconnecting' || currentState === 'failed') return + renderStatusLine() + }, + } +} diff --git a/bridge/capacityWake.ts b/bridge/capacityWake.ts new file mode 100644 index 0000000..e58c50d --- /dev/null +++ b/bridge/capacityWake.ts @@ -0,0 +1,56 @@ +/** + * Shared capacity-wake primitive for bridge poll loops. + * + * Both replBridge.ts and bridgeMain.ts need to sleep while "at capacity" + * but wake early when either (a) the outer loop signal aborts (shutdown), + * or (b) capacity frees up (session done / transport lost). This module + * encapsulates the mutable wake-controller + two-signal merger that both + * poll loops previously duplicated byte-for-byte. + */ + +export type CapacitySignal = { signal: AbortSignal; cleanup: () => void } + +export type CapacityWake = { + /** + * Create a signal that aborts when either the outer loop signal or the + * capacity-wake controller fires. Returns the merged signal and a cleanup + * function that removes listeners when the sleep resolves normally + * (without abort). + */ + signal(): CapacitySignal + /** + * Abort the current at-capacity sleep and arm a fresh controller so the + * poll loop immediately re-checks for new work. + */ + wake(): void +} + +export function createCapacityWake(outerSignal: AbortSignal): CapacityWake { + let wakeController = new AbortController() + + function wake(): void { + wakeController.abort() + wakeController = new AbortController() + } + + function signal(): CapacitySignal { + const merged = new AbortController() + const abort = (): void => merged.abort() + if (outerSignal.aborted || wakeController.signal.aborted) { + merged.abort() + return { signal: merged.signal, cleanup: () => {} } + } + outerSignal.addEventListener('abort', abort, { once: true }) + const capSig = wakeController.signal + capSig.addEventListener('abort', abort, { once: true }) + return { + signal: merged.signal, + cleanup: () => { + outerSignal.removeEventListener('abort', abort) + capSig.removeEventListener('abort', abort) + }, + } + } + + return { signal, wake } +} diff --git a/bridge/codeSessionApi.ts b/bridge/codeSessionApi.ts new file mode 100644 index 0000000..65b46a3 --- /dev/null +++ b/bridge/codeSessionApi.ts @@ -0,0 +1,168 @@ +/** + * Thin HTTP wrappers for the CCR v2 code-session API. + * + * Separate file from remoteBridgeCore.ts so the SDK /bridge subpath can + * export createCodeSession + fetchRemoteCredentials without bundling the + * heavy CLI tree (analytics, transport, etc.). Callers supply explicit + * accessToken + baseUrl — no implicit auth or config reads. + */ + +import axios from 'axios' +import { logForDebugging } from '../utils/debug.js' +import { errorMessage } from '../utils/errors.js' +import { jsonStringify } from '../utils/slowOperations.js' +import { extractErrorDetail } from './debugUtils.js' + +const ANTHROPIC_VERSION = '2023-06-01' + +function oauthHeaders(accessToken: string): Record { + return { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + 'anthropic-version': ANTHROPIC_VERSION, + } +} + +export async function createCodeSession( + baseUrl: string, + accessToken: string, + title: string, + timeoutMs: number, + tags?: string[], +): Promise { + const url = `${baseUrl}/v1/code/sessions` + let response + try { + response = await axios.post( + url, + // bridge: {} is the positive signal for the oneof runner — omitting it + // (or sending environment_id: "") now 400s. BridgeRunner is an empty + // message today; it's a placeholder for future bridge-specific options. + { title, bridge: {}, ...(tags?.length ? { tags } : {}) }, + { + headers: oauthHeaders(accessToken), + timeout: timeoutMs, + validateStatus: s => s < 500, + }, + ) + } catch (err: unknown) { + logForDebugging( + `[code-session] Session create request failed: ${errorMessage(err)}`, + ) + return null + } + + if (response.status !== 200 && response.status !== 201) { + const detail = extractErrorDetail(response.data) + logForDebugging( + `[code-session] Session create failed ${response.status}${detail ? `: ${detail}` : ''}`, + ) + return null + } + + const data: unknown = response.data + if ( + !data || + typeof data !== 'object' || + !('session' in data) || + !data.session || + typeof data.session !== 'object' || + !('id' in data.session) || + typeof data.session.id !== 'string' || + !data.session.id.startsWith('cse_') + ) { + logForDebugging( + `[code-session] No session.id (cse_*) in response: ${jsonStringify(data).slice(0, 200)}`, + ) + return null + } + return data.session.id +} + +/** + * Credentials from POST /bridge. JWT is opaque — do not decode. + * Each /bridge call bumps worker_epoch server-side (it IS the register). + */ +export type RemoteCredentials = { + worker_jwt: string + api_base_url: string + expires_in: number + worker_epoch: number +} + +export async function fetchRemoteCredentials( + sessionId: string, + baseUrl: string, + accessToken: string, + timeoutMs: number, + trustedDeviceToken?: string, +): Promise { + const url = `${baseUrl}/v1/code/sessions/${sessionId}/bridge` + const headers = oauthHeaders(accessToken) + if (trustedDeviceToken) { + headers['X-Trusted-Device-Token'] = trustedDeviceToken + } + let response + try { + response = await axios.post( + url, + {}, + { + headers, + timeout: timeoutMs, + validateStatus: s => s < 500, + }, + ) + } catch (err: unknown) { + logForDebugging( + `[code-session] /bridge request failed: ${errorMessage(err)}`, + ) + return null + } + + if (response.status !== 200) { + const detail = extractErrorDetail(response.data) + logForDebugging( + `[code-session] /bridge failed ${response.status}${detail ? `: ${detail}` : ''}`, + ) + return null + } + + const data: unknown = response.data + if ( + data === null || + typeof data !== 'object' || + !('worker_jwt' in data) || + typeof data.worker_jwt !== 'string' || + !('expires_in' in data) || + typeof data.expires_in !== 'number' || + !('api_base_url' in data) || + typeof data.api_base_url !== 'string' || + !('worker_epoch' in data) + ) { + logForDebugging( + `[code-session] /bridge response malformed (need worker_jwt, expires_in, api_base_url, worker_epoch): ${jsonStringify(data).slice(0, 200)}`, + ) + return null + } + // protojson serializes int64 as a string to avoid JS precision loss; + // Go may also return a number depending on encoder settings. + const rawEpoch = data.worker_epoch + const epoch = typeof rawEpoch === 'string' ? Number(rawEpoch) : rawEpoch + if ( + typeof epoch !== 'number' || + !Number.isFinite(epoch) || + !Number.isSafeInteger(epoch) + ) { + logForDebugging( + `[code-session] /bridge worker_epoch invalid: ${jsonStringify(rawEpoch)}`, + ) + return null + } + return { + worker_jwt: data.worker_jwt, + api_base_url: data.api_base_url, + expires_in: data.expires_in, + worker_epoch: epoch, + } +} diff --git a/bridge/createSession.ts b/bridge/createSession.ts new file mode 100644 index 0000000..d5bc83a --- /dev/null +++ b/bridge/createSession.ts @@ -0,0 +1,384 @@ +import type { SDKMessage } from '../entrypoints/agentSdkTypes.js' +import { logForDebugging } from '../utils/debug.js' +import { errorMessage } from '../utils/errors.js' +import { extractErrorDetail } from './debugUtils.js' +import { toCompatSessionId } from './sessionIdCompat.js' + +type GitSource = { + type: 'git_repository' + url: string + revision?: string +} + +type GitOutcome = { + type: 'git_repository' + git_info: { type: 'github'; repo: string; branches: string[] } +} + +// Events must be wrapped in { type: 'event', data: } for the +// POST /v1/sessions endpoint (discriminated union format). +type SessionEvent = { + type: 'event' + data: SDKMessage +} + +/** + * Create a session on a bridge environment via POST /v1/sessions. + * + * Used by both `claude remote-control` (empty session so the user has somewhere to + * type immediately) and `/remote-control` (session pre-populated with conversation + * history). + * + * Returns the session ID on success, or null if creation fails (non-fatal). + */ +export async function createBridgeSession({ + environmentId, + title, + events, + gitRepoUrl, + branch, + signal, + baseUrl: baseUrlOverride, + getAccessToken, + permissionMode, +}: { + environmentId: string + title?: string + events: SessionEvent[] + gitRepoUrl: string | null + branch: string + signal: AbortSignal + baseUrl?: string + getAccessToken?: () => string | undefined + permissionMode?: string +}): Promise { + const { getClaudeAIOAuthTokens } = await import('../utils/auth.js') + const { getOrganizationUUID } = await import('../services/oauth/client.js') + const { getOauthConfig } = await import('../constants/oauth.js') + const { getOAuthHeaders } = await import('../utils/teleport/api.js') + const { parseGitHubRepository } = await import('../utils/detectRepository.js') + const { getDefaultBranch } = await import('../utils/git.js') + const { getMainLoopModel } = await import('../utils/model/model.js') + const { default: axios } = await import('axios') + + const accessToken = + getAccessToken?.() ?? getClaudeAIOAuthTokens()?.accessToken + if (!accessToken) { + logForDebugging('[bridge] No access token for session creation') + return null + } + + const orgUUID = await getOrganizationUUID() + if (!orgUUID) { + logForDebugging('[bridge] No org UUID for session creation') + return null + } + + // Build git source and outcome context + let gitSource: GitSource | null = null + let gitOutcome: GitOutcome | null = null + + if (gitRepoUrl) { + const { parseGitRemote } = await import('../utils/detectRepository.js') + const parsed = parseGitRemote(gitRepoUrl) + if (parsed) { + const { host, owner, name } = parsed + const revision = branch || (await getDefaultBranch()) || undefined + gitSource = { + type: 'git_repository', + url: `https://${host}/${owner}/${name}`, + revision, + } + gitOutcome = { + type: 'git_repository', + git_info: { + type: 'github', + repo: `${owner}/${name}`, + branches: [`claude/${branch || 'task'}`], + }, + } + } else { + // Fallback: try parseGitHubRepository for owner/repo format + const ownerRepo = parseGitHubRepository(gitRepoUrl) + if (ownerRepo) { + const [owner, name] = ownerRepo.split('/') + if (owner && name) { + const revision = branch || (await getDefaultBranch()) || undefined + gitSource = { + type: 'git_repository', + url: `https://github.com/${owner}/${name}`, + revision, + } + gitOutcome = { + type: 'git_repository', + git_info: { + type: 'github', + repo: `${owner}/${name}`, + branches: [`claude/${branch || 'task'}`], + }, + } + } + } + } + } + + const requestBody = { + ...(title !== undefined && { title }), + events, + session_context: { + sources: gitSource ? [gitSource] : [], + outcomes: gitOutcome ? [gitOutcome] : [], + model: getMainLoopModel(), + }, + environment_id: environmentId, + source: 'remote-control', + ...(permissionMode && { permission_mode: permissionMode }), + } + + const headers = { + ...getOAuthHeaders(accessToken), + 'anthropic-beta': 'ccr-byoc-2025-07-29', + 'x-organization-uuid': orgUUID, + } + + const url = `${baseUrlOverride ?? getOauthConfig().BASE_API_URL}/v1/sessions` + let response + try { + response = await axios.post(url, requestBody, { + headers, + signal, + validateStatus: s => s < 500, + }) + } catch (err: unknown) { + logForDebugging( + `[bridge] Session creation request failed: ${errorMessage(err)}`, + ) + return null + } + const isSuccess = response.status === 200 || response.status === 201 + + if (!isSuccess) { + const detail = extractErrorDetail(response.data) + logForDebugging( + `[bridge] Session creation failed with status ${response.status}${detail ? `: ${detail}` : ''}`, + ) + return null + } + + const sessionData: unknown = response.data + if ( + !sessionData || + typeof sessionData !== 'object' || + !('id' in sessionData) || + typeof sessionData.id !== 'string' + ) { + logForDebugging('[bridge] No session ID in response') + return null + } + + return sessionData.id +} + +/** + * Fetch a bridge session via GET /v1/sessions/{id}. + * + * Returns the session's environment_id (for `--session-id` resume) and title. + * Uses the same org-scoped headers as create/archive — the environments-level + * client in bridgeApi.ts uses a different beta header and no org UUID, which + * makes the Sessions API return 404. + */ +export async function getBridgeSession( + sessionId: string, + opts?: { baseUrl?: string; getAccessToken?: () => string | undefined }, +): Promise<{ environment_id?: string; title?: string } | null> { + const { getClaudeAIOAuthTokens } = await import('../utils/auth.js') + const { getOrganizationUUID } = await import('../services/oauth/client.js') + const { getOauthConfig } = await import('../constants/oauth.js') + const { getOAuthHeaders } = await import('../utils/teleport/api.js') + const { default: axios } = await import('axios') + + const accessToken = + opts?.getAccessToken?.() ?? getClaudeAIOAuthTokens()?.accessToken + if (!accessToken) { + logForDebugging('[bridge] No access token for session fetch') + return null + } + + const orgUUID = await getOrganizationUUID() + if (!orgUUID) { + logForDebugging('[bridge] No org UUID for session fetch') + return null + } + + const headers = { + ...getOAuthHeaders(accessToken), + 'anthropic-beta': 'ccr-byoc-2025-07-29', + 'x-organization-uuid': orgUUID, + } + + const url = `${opts?.baseUrl ?? getOauthConfig().BASE_API_URL}/v1/sessions/${sessionId}` + logForDebugging(`[bridge] Fetching session ${sessionId}`) + + let response + try { + response = await axios.get<{ environment_id?: string; title?: string }>( + url, + { headers, timeout: 10_000, validateStatus: s => s < 500 }, + ) + } catch (err: unknown) { + logForDebugging( + `[bridge] Session fetch request failed: ${errorMessage(err)}`, + ) + return null + } + + if (response.status !== 200) { + const detail = extractErrorDetail(response.data) + logForDebugging( + `[bridge] Session fetch failed with status ${response.status}${detail ? `: ${detail}` : ''}`, + ) + return null + } + + return response.data +} + +/** + * Archive a bridge session via POST /v1/sessions/{id}/archive. + * + * The CCR server never auto-archives sessions — archival is always an + * explicit client action. Both `claude remote-control` (standalone bridge) and the + * always-on `/remote-control` REPL bridge call this during shutdown to archive any + * sessions that are still alive. + * + * The archive endpoint accepts sessions in any status (running, idle, + * requires_action, pending) and returns 409 if already archived, making + * it safe to call even if the server-side runner already archived the + * session. + * + * Callers must handle errors — this function has no try/catch; 5xx, + * timeouts, and network errors throw. Archival is best-effort during + * cleanup; call sites wrap with .catch(). + */ +export async function archiveBridgeSession( + sessionId: string, + opts?: { + baseUrl?: string + getAccessToken?: () => string | undefined + timeoutMs?: number + }, +): Promise { + const { getClaudeAIOAuthTokens } = await import('../utils/auth.js') + const { getOrganizationUUID } = await import('../services/oauth/client.js') + const { getOauthConfig } = await import('../constants/oauth.js') + const { getOAuthHeaders } = await import('../utils/teleport/api.js') + const { default: axios } = await import('axios') + + const accessToken = + opts?.getAccessToken?.() ?? getClaudeAIOAuthTokens()?.accessToken + if (!accessToken) { + logForDebugging('[bridge] No access token for session archive') + return + } + + const orgUUID = await getOrganizationUUID() + if (!orgUUID) { + logForDebugging('[bridge] No org UUID for session archive') + return + } + + const headers = { + ...getOAuthHeaders(accessToken), + 'anthropic-beta': 'ccr-byoc-2025-07-29', + 'x-organization-uuid': orgUUID, + } + + const url = `${opts?.baseUrl ?? getOauthConfig().BASE_API_URL}/v1/sessions/${sessionId}/archive` + logForDebugging(`[bridge] Archiving session ${sessionId}`) + + const response = await axios.post( + url, + {}, + { + headers, + timeout: opts?.timeoutMs ?? 10_000, + validateStatus: s => s < 500, + }, + ) + + if (response.status === 200) { + logForDebugging(`[bridge] Session ${sessionId} archived successfully`) + } else { + const detail = extractErrorDetail(response.data) + logForDebugging( + `[bridge] Session archive failed with status ${response.status}${detail ? `: ${detail}` : ''}`, + ) + } +} + +/** + * Update the title of a bridge session via PATCH /v1/sessions/{id}. + * + * Called when the user renames a session via /rename while a bridge + * connection is active, so the title stays in sync on claude.ai/code. + * + * Errors are swallowed — title sync is best-effort. + */ +export async function updateBridgeSessionTitle( + sessionId: string, + title: string, + opts?: { baseUrl?: string; getAccessToken?: () => string | undefined }, +): Promise { + const { getClaudeAIOAuthTokens } = await import('../utils/auth.js') + const { getOrganizationUUID } = await import('../services/oauth/client.js') + const { getOauthConfig } = await import('../constants/oauth.js') + const { getOAuthHeaders } = await import('../utils/teleport/api.js') + const { default: axios } = await import('axios') + + const accessToken = + opts?.getAccessToken?.() ?? getClaudeAIOAuthTokens()?.accessToken + if (!accessToken) { + logForDebugging('[bridge] No access token for session title update') + return + } + + const orgUUID = await getOrganizationUUID() + if (!orgUUID) { + logForDebugging('[bridge] No org UUID for session title update') + return + } + + const headers = { + ...getOAuthHeaders(accessToken), + 'anthropic-beta': 'ccr-byoc-2025-07-29', + 'x-organization-uuid': orgUUID, + } + + // Compat gateway only accepts session_* (compat/convert.go:27). v2 callers + // pass raw cse_*; retag here so all callers can pass whatever they hold. + // Idempotent for v1's session_* and bridgeMain's pre-converted compatSessionId. + const compatId = toCompatSessionId(sessionId) + const url = `${opts?.baseUrl ?? getOauthConfig().BASE_API_URL}/v1/sessions/${compatId}` + logForDebugging(`[bridge] Updating session title: ${compatId} → ${title}`) + + try { + const response = await axios.patch( + url, + { title }, + { headers, timeout: 10_000, validateStatus: s => s < 500 }, + ) + + if (response.status === 200) { + logForDebugging(`[bridge] Session title updated successfully`) + } else { + const detail = extractErrorDetail(response.data) + logForDebugging( + `[bridge] Session title update failed with status ${response.status}${detail ? `: ${detail}` : ''}`, + ) + } + } catch (err: unknown) { + logForDebugging( + `[bridge] Session title update request failed: ${errorMessage(err)}`, + ) + } +} diff --git a/bridge/debugUtils.ts b/bridge/debugUtils.ts new file mode 100644 index 0000000..e9f7293 --- /dev/null +++ b/bridge/debugUtils.ts @@ -0,0 +1,141 @@ +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { logForDebugging } from '../utils/debug.js' +import { errorMessage } from '../utils/errors.js' +import { jsonStringify } from '../utils/slowOperations.js' + +const DEBUG_MSG_LIMIT = 2000 + +const SECRET_FIELD_NAMES = [ + 'session_ingress_token', + 'environment_secret', + 'access_token', + 'secret', + 'token', +] + +const SECRET_PATTERN = new RegExp( + `"(${SECRET_FIELD_NAMES.join('|')})"\\s*:\\s*"([^"]*)"`, + 'g', +) + +const REDACT_MIN_LENGTH = 16 + +export function redactSecrets(s: string): string { + return s.replace(SECRET_PATTERN, (_match, field: string, value: string) => { + if (value.length < REDACT_MIN_LENGTH) { + return `"${field}":"[REDACTED]"` + } + const redacted = `${value.slice(0, 8)}...${value.slice(-4)}` + return `"${field}":"${redacted}"` + }) +} + +/** Truncate a string for debug logging, collapsing newlines. */ +export function debugTruncate(s: string): string { + const flat = s.replace(/\n/g, '\\n') + if (flat.length <= DEBUG_MSG_LIMIT) { + return flat + } + return flat.slice(0, DEBUG_MSG_LIMIT) + `... (${flat.length} chars)` +} + +/** Truncate a JSON-serializable value for debug logging. */ +export function debugBody(data: unknown): string { + const raw = typeof data === 'string' ? data : jsonStringify(data) + const s = redactSecrets(raw) + if (s.length <= DEBUG_MSG_LIMIT) { + return s + } + return s.slice(0, DEBUG_MSG_LIMIT) + `... (${s.length} chars)` +} + +/** + * Extract a descriptive error message from an axios error (or any error). + * For HTTP errors, appends the server's response body message if available, + * since axios's default message only includes the status code. + */ +export function describeAxiosError(err: unknown): string { + const msg = errorMessage(err) + if (err && typeof err === 'object' && 'response' in err) { + const response = (err as { response?: { data?: unknown } }).response + if (response?.data && typeof response.data === 'object') { + const data = response.data as Record + const detail = + typeof data.message === 'string' + ? data.message + : typeof data.error === 'object' && + data.error && + 'message' in data.error && + typeof (data.error as Record).message === + 'string' + ? (data.error as Record).message + : undefined + if (detail) { + return `${msg}: ${detail}` + } + } + } + return msg +} + +/** + * Extract the HTTP status code from an axios error, if present. + * Returns undefined for non-HTTP errors (e.g. network failures). + */ +export function extractHttpStatus(err: unknown): number | undefined { + if ( + err && + typeof err === 'object' && + 'response' in err && + (err as { response?: { status?: unknown } }).response && + typeof (err as { response: { status?: unknown } }).response.status === + 'number' + ) { + return (err as { response: { status: number } }).response.status + } + return undefined +} + +/** + * Pull a human-readable message out of an API error response body. + * Checks `data.message` first, then `data.error.message`. + */ +export function extractErrorDetail(data: unknown): string | undefined { + if (!data || typeof data !== 'object') return undefined + if ('message' in data && typeof data.message === 'string') { + return data.message + } + if ( + 'error' in data && + data.error !== null && + typeof data.error === 'object' && + 'message' in data.error && + typeof data.error.message === 'string' + ) { + return data.error.message + } + return undefined +} + +/** + * Log a bridge init skip — debug message + `tengu_bridge_repl_skipped` + * analytics event. Centralizes the event name and the AnalyticsMetadata + * cast so call sites don't each repeat the 5-line boilerplate. + */ +export function logBridgeSkip( + reason: string, + debugMsg?: string, + v2?: boolean, +): void { + if (debugMsg) { + logForDebugging(debugMsg) + } + logEvent('tengu_bridge_repl_skipped', { + reason: + reason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + ...(v2 !== undefined && { v2 }), + }) +} diff --git a/bridge/envLessBridgeConfig.ts b/bridge/envLessBridgeConfig.ts new file mode 100644 index 0000000..de0cb5e --- /dev/null +++ b/bridge/envLessBridgeConfig.ts @@ -0,0 +1,165 @@ +import { z } from 'zod/v4' +import { getFeatureValue_DEPRECATED } from '../services/analytics/growthbook.js' +import { lazySchema } from '../utils/lazySchema.js' +import { lt } from '../utils/semver.js' +import { isEnvLessBridgeEnabled } from './bridgeEnabled.js' + +export type EnvLessBridgeConfig = { + // withRetry — init-phase backoff (createSession, POST /bridge, recovery /bridge) + init_retry_max_attempts: number + init_retry_base_delay_ms: number + init_retry_jitter_fraction: number + init_retry_max_delay_ms: number + // axios timeout for POST /sessions, POST /bridge, POST /archive + http_timeout_ms: number + // BoundedUUIDSet ring size (echo + re-delivery dedup) + uuid_dedup_buffer_size: number + // CCRClient worker heartbeat cadence. Server TTL is 60s — 20s gives 3× margin. + heartbeat_interval_ms: number + // ±fraction of interval — per-beat jitter to spread fleet load. + heartbeat_jitter_fraction: number + // Fire proactive JWT refresh this long before expires_in. Larger buffer = + // more frequent refresh (refresh cadence ≈ expires_in - buffer). + token_refresh_buffer_ms: number + // Archive POST timeout in teardown(). Distinct from http_timeout_ms because + // gracefulShutdown races runCleanupFunctions() against a 2s cap — a 10s + // axios timeout on a slow/stalled archive burns the whole budget on a + // request that forceExit will kill anyway. + teardown_archive_timeout_ms: number + // Deadline for onConnect after transport.connect(). If neither onConnect + // nor onClose fires before this, emit tengu_bridge_repl_connect_timeout + // — the only telemetry for the ~1% of sessions that emit `started` then + // go silent (no error, no event, just nothing). + connect_timeout_ms: number + // Semver floor for the env-less bridge path. Separate from the v1 + // tengu_bridge_min_version config so a v2-specific bug can force upgrades + // without blocking v1 (env-based) clients, and vice versa. + min_version: string + // When true, tell users their claude.ai app may be too old to see v2 + // sessions — lets us roll the v2 bridge before the app ships the new + // session-list query. + should_show_app_upgrade_message: boolean +} + +export const DEFAULT_ENV_LESS_BRIDGE_CONFIG: EnvLessBridgeConfig = { + init_retry_max_attempts: 3, + init_retry_base_delay_ms: 500, + init_retry_jitter_fraction: 0.25, + init_retry_max_delay_ms: 4000, + http_timeout_ms: 10_000, + uuid_dedup_buffer_size: 2000, + heartbeat_interval_ms: 20_000, + heartbeat_jitter_fraction: 0.1, + token_refresh_buffer_ms: 300_000, + teardown_archive_timeout_ms: 1500, + connect_timeout_ms: 15_000, + min_version: '0.0.0', + should_show_app_upgrade_message: false, +} + +// Floors reject the whole object on violation (fall back to DEFAULT) rather +// than partially trusting — same defense-in-depth as pollConfig.ts. +const envLessBridgeConfigSchema = lazySchema(() => + z.object({ + init_retry_max_attempts: z.number().int().min(1).max(10).default(3), + init_retry_base_delay_ms: z.number().int().min(100).default(500), + init_retry_jitter_fraction: z.number().min(0).max(1).default(0.25), + init_retry_max_delay_ms: z.number().int().min(500).default(4000), + http_timeout_ms: z.number().int().min(2000).default(10_000), + uuid_dedup_buffer_size: z.number().int().min(100).max(50_000).default(2000), + // Server TTL is 60s. Floor 5s prevents thrash; cap 30s keeps ≥2× margin. + heartbeat_interval_ms: z + .number() + .int() + .min(5000) + .max(30_000) + .default(20_000), + // ±fraction per beat. Cap 0.5: at max interval (30s) × 1.5 = 45s worst case, + // still under the 60s TTL. + heartbeat_jitter_fraction: z.number().min(0).max(0.5).default(0.1), + // Floor 30s prevents tight-looping. Cap 30min rejects buffer-vs-delay + // semantic inversion: ops entering expires_in-5min (the *delay until + // refresh*) instead of 5min (the *buffer before expiry*) yields + // delayMs = expires_in - buffer ≈ 5min instead of ≈4h. Both are positive + // durations so .min() alone can't distinguish; .max() catches the + // inverted value since buffer ≥ 30min is nonsensical for a multi-hour JWT. + token_refresh_buffer_ms: z + .number() + .int() + .min(30_000) + .max(1_800_000) + .default(300_000), + // Cap 2000 keeps this under gracefulShutdown's 2s cleanup race — a higher + // timeout just lies to axios since forceExit kills the socket regardless. + teardown_archive_timeout_ms: z + .number() + .int() + .min(500) + .max(2000) + .default(1500), + // Observed p99 connect is ~2-3s; 15s is ~5× headroom. Floor 5s bounds + // false-positive rate under transient slowness; cap 60s bounds how long + // a truly-stalled session stays dark. + connect_timeout_ms: z.number().int().min(5_000).max(60_000).default(15_000), + min_version: z + .string() + .refine(v => { + try { + lt(v, '0.0.0') + return true + } catch { + return false + } + }) + .default('0.0.0'), + should_show_app_upgrade_message: z.boolean().default(false), + }), +) + +/** + * Fetch the env-less bridge timing config from GrowthBook. Read once per + * initEnvLessBridgeCore call — config is fixed for the lifetime of a bridge + * session. + * + * Uses the blocking getter (not _CACHED_MAY_BE_STALE) because /remote-control + * runs well after GrowthBook init — initializeGrowthBook() resolves instantly, + * so there's no startup penalty, and we get the fresh in-memory remoteEval + * value instead of the stale-on-first-read disk cache. The _DEPRECATED suffix + * warns against startup-path usage, which this isn't. + */ +export async function getEnvLessBridgeConfig(): Promise { + const raw = await getFeatureValue_DEPRECATED( + 'tengu_bridge_repl_v2_config', + DEFAULT_ENV_LESS_BRIDGE_CONFIG, + ) + const parsed = envLessBridgeConfigSchema().safeParse(raw) + return parsed.success ? parsed.data : DEFAULT_ENV_LESS_BRIDGE_CONFIG +} + +/** + * Returns an error message if the current CLI version is below the minimum + * required for the env-less (v2) bridge path, or null if the version is fine. + * + * v2 analogue of checkBridgeMinVersion() — reads from tengu_bridge_repl_v2_config + * instead of tengu_bridge_min_version so the two implementations can enforce + * independent floors. + */ +export async function checkEnvLessBridgeMinVersion(): Promise { + const cfg = await getEnvLessBridgeConfig() + if (cfg.min_version && lt(MACRO.VERSION, cfg.min_version)) { + return `Your version of Claude Code (${MACRO.VERSION}) is too old for Remote Control.\nVersion ${cfg.min_version} or higher is required. Run \`claude update\` to update.` + } + return null +} + +/** + * Whether to nudge users toward upgrading their claude.ai app when a + * Remote Control session starts. True only when the v2 bridge is active + * AND the should_show_app_upgrade_message config bit is set — lets us + * roll the v2 bridge before the app ships the new session-list query. + */ +export async function shouldShowAppUpgradeMessage(): Promise { + if (!isEnvLessBridgeEnabled()) return false + const cfg = await getEnvLessBridgeConfig() + return cfg.should_show_app_upgrade_message +} diff --git a/bridge/flushGate.ts b/bridge/flushGate.ts new file mode 100644 index 0000000..6216334 --- /dev/null +++ b/bridge/flushGate.ts @@ -0,0 +1,71 @@ +/** + * State machine for gating message writes during an initial flush. + * + * When a bridge session starts, historical messages are flushed to the + * server via a single HTTP POST. During that flush, new messages must + * be queued to prevent them from arriving at the server interleaved + * with the historical messages. + * + * Lifecycle: + * start() → enqueue() returns true, items are queued + * end() → returns queued items for draining, enqueue() returns false + * drop() → discards queued items (permanent transport close) + * deactivate() → clears active flag without dropping items + * (transport replacement — new transport will drain) + */ +export class FlushGate { + private _active = false + private _pending: T[] = [] + + get active(): boolean { + return this._active + } + + get pendingCount(): number { + return this._pending.length + } + + /** Mark flush as in-progress. enqueue() will start queuing items. */ + start(): void { + this._active = true + } + + /** + * End the flush and return any queued items for draining. + * Caller is responsible for sending the returned items. + */ + end(): T[] { + this._active = false + return this._pending.splice(0) + } + + /** + * If flush is active, queue the items and return true. + * If flush is not active, return false (caller should send directly). + */ + enqueue(...items: T[]): boolean { + if (!this._active) return false + this._pending.push(...items) + return true + } + + /** + * Discard all queued items (permanent transport close). + * Returns the number of items dropped. + */ + drop(): number { + this._active = false + const count = this._pending.length + this._pending.length = 0 + return count + } + + /** + * Clear the active flag without dropping queued items. + * Used when the transport is replaced (onWorkReceived) — the new + * transport's flush will drain the pending items. + */ + deactivate(): void { + this._active = false + } +} diff --git a/bridge/inboundAttachments.ts b/bridge/inboundAttachments.ts new file mode 100644 index 0000000..f7c13c8 --- /dev/null +++ b/bridge/inboundAttachments.ts @@ -0,0 +1,175 @@ +/** + * Resolve file_uuid attachments on inbound bridge user messages. + * + * Web composer uploads via cookie-authed /api/{org}/upload, sends file_uuid + * alongside the message. Here we fetch each via GET /api/oauth/files/{uuid}/content + * (oauth-authed, same store), write to ~/.claude/uploads/{sessionId}/, and + * return @path refs to prepend. Claude's Read tool takes it from there. + * + * Best-effort: any failure (no token, network, non-2xx, disk) logs debug and + * skips that attachment. The message still reaches Claude, just without @path. + */ + +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs' +import axios from 'axios' +import { randomUUID } from 'crypto' +import { mkdir, writeFile } from 'fs/promises' +import { basename, join } from 'path' +import { z } from 'zod/v4' +import { getSessionId } from '../bootstrap/state.js' +import { logForDebugging } from '../utils/debug.js' +import { getClaudeConfigHomeDir } from '../utils/envUtils.js' +import { lazySchema } from '../utils/lazySchema.js' +import { getBridgeAccessToken, getBridgeBaseUrl } from './bridgeConfig.js' + +const DOWNLOAD_TIMEOUT_MS = 30_000 + +function debug(msg: string): void { + logForDebugging(`[bridge:inbound-attach] ${msg}`) +} + +const attachmentSchema = lazySchema(() => + z.object({ + file_uuid: z.string(), + file_name: z.string(), + }), +) +const attachmentsArraySchema = lazySchema(() => z.array(attachmentSchema())) + +export type InboundAttachment = z.infer> + +/** Pull file_attachments off a loosely-typed inbound message. */ +export function extractInboundAttachments(msg: unknown): InboundAttachment[] { + if (typeof msg !== 'object' || msg === null || !('file_attachments' in msg)) { + return [] + } + const parsed = attachmentsArraySchema().safeParse(msg.file_attachments) + return parsed.success ? parsed.data : [] +} + +/** + * Strip path components and keep only filename-safe chars. file_name comes + * from the network (web composer), so treat it as untrusted even though the + * composer controls it. + */ +function sanitizeFileName(name: string): string { + const base = basename(name).replace(/[^a-zA-Z0-9._-]/g, '_') + return base || 'attachment' +} + +function uploadsDir(): string { + return join(getClaudeConfigHomeDir(), 'uploads', getSessionId()) +} + +/** + * Fetch + write one attachment. Returns the absolute path on success, + * undefined on any failure. + */ +async function resolveOne(att: InboundAttachment): Promise { + const token = getBridgeAccessToken() + if (!token) { + debug('skip: no oauth token') + return undefined + } + + let data: Buffer + try { + // getOauthConfig() (via getBridgeBaseUrl) throws on a non-allowlisted + // CLAUDE_CODE_CUSTOM_OAUTH_URL — keep it inside the try so a bad + // FedStart URL degrades to "no @path" instead of crashing print.ts's + // reader loop (which has no catch around the await). + const url = `${getBridgeBaseUrl()}/api/oauth/files/${encodeURIComponent(att.file_uuid)}/content` + const response = await axios.get(url, { + headers: { Authorization: `Bearer ${token}` }, + responseType: 'arraybuffer', + timeout: DOWNLOAD_TIMEOUT_MS, + validateStatus: () => true, + }) + if (response.status !== 200) { + debug(`fetch ${att.file_uuid} failed: status=${response.status}`) + return undefined + } + data = Buffer.from(response.data) + } catch (e) { + debug(`fetch ${att.file_uuid} threw: ${e}`) + return undefined + } + + // uuid-prefix makes collisions impossible across messages and within one + // (same filename, different files). 8 chars is enough — this isn't security. + const safeName = sanitizeFileName(att.file_name) + const prefix = ( + att.file_uuid.slice(0, 8) || randomUUID().slice(0, 8) + ).replace(/[^a-zA-Z0-9_-]/g, '_') + const dir = uploadsDir() + const outPath = join(dir, `${prefix}-${safeName}`) + + try { + await mkdir(dir, { recursive: true }) + await writeFile(outPath, data) + } catch (e) { + debug(`write ${outPath} failed: ${e}`) + return undefined + } + + debug(`resolved ${att.file_uuid} → ${outPath} (${data.length} bytes)`) + return outPath +} + +/** + * Resolve all attachments on an inbound message to a prefix string of + * @path refs. Empty string if none resolved. + */ +export async function resolveInboundAttachments( + attachments: InboundAttachment[], +): Promise { + if (attachments.length === 0) return '' + debug(`resolving ${attachments.length} attachment(s)`) + const paths = await Promise.all(attachments.map(resolveOne)) + const ok = paths.filter((p): p is string => p !== undefined) + if (ok.length === 0) return '' + // Quoted form — extractAtMentionedFiles truncates unquoted @refs at the + // first space, which breaks any home dir with spaces (/Users/John Smith/). + return ok.map(p => `@"${p}"`).join(' ') + ' ' +} + +/** + * Prepend @path refs to content, whichever form it's in. + * Targets the LAST text block — processUserInputBase reads inputString + * from processedBlocks[processedBlocks.length - 1], so putting refs in + * block[0] means they're silently ignored for [text, image] content. + */ +export function prependPathRefs( + content: string | Array, + prefix: string, +): string | Array { + if (!prefix) return content + if (typeof content === 'string') return prefix + content + const i = content.findLastIndex(b => b.type === 'text') + if (i !== -1) { + const b = content[i]! + if (b.type === 'text') { + return [ + ...content.slice(0, i), + { ...b, text: prefix + b.text }, + ...content.slice(i + 1), + ] + } + } + // No text block — append one at the end so it's last. + return [...content, { type: 'text', text: prefix.trimEnd() }] +} + +/** + * Convenience: extract + resolve + prepend. No-op when the message has no + * file_attachments field (fast path — no network, returns same reference). + */ +export async function resolveAndPrepend( + msg: unknown, + content: string | Array, +): Promise> { + const attachments = extractInboundAttachments(msg) + if (attachments.length === 0) return content + const prefix = await resolveInboundAttachments(attachments) + return prependPathRefs(content, prefix) +} diff --git a/bridge/inboundMessages.ts b/bridge/inboundMessages.ts new file mode 100644 index 0000000..2c02f50 --- /dev/null +++ b/bridge/inboundMessages.ts @@ -0,0 +1,80 @@ +import type { + Base64ImageSource, + ContentBlockParam, + ImageBlockParam, +} from '@anthropic-ai/sdk/resources/messages.mjs' +import type { UUID } from 'crypto' +import type { SDKMessage } from '../entrypoints/agentSdkTypes.js' +import { detectImageFormatFromBase64 } from '../utils/imageResizer.js' + +/** + * Process an inbound user message from the bridge, extracting content + * and UUID for enqueueing. Supports both string content and + * ContentBlockParam[] (e.g. messages containing images). + * + * Normalizes image blocks from bridge clients that may use camelCase + * `mediaType` instead of snake_case `media_type` (mobile-apps#5825). + * + * Returns the extracted fields, or undefined if the message should be + * skipped (non-user type, missing/empty content). + */ +export function extractInboundMessageFields( + msg: SDKMessage, +): + | { content: string | Array; uuid: UUID | undefined } + | undefined { + if (msg.type !== 'user') return undefined + const content = msg.message?.content + if (!content) return undefined + if (Array.isArray(content) && content.length === 0) return undefined + + const uuid = + 'uuid' in msg && typeof msg.uuid === 'string' + ? (msg.uuid as UUID) + : undefined + + return { + content: Array.isArray(content) ? normalizeImageBlocks(content) : content, + uuid, + } +} + +/** + * Normalize image content blocks from bridge clients. iOS/web clients may + * send `mediaType` (camelCase) instead of `media_type` (snake_case), or + * omit the field entirely. Without normalization, the bad block poisons + * the session — every subsequent API call fails with + * "media_type: Field required". + * + * Fast-path scan returns the original array reference when no + * normalization is needed (zero allocation on the happy path). + */ +export function normalizeImageBlocks( + blocks: Array, +): Array { + if (!blocks.some(isMalformedBase64Image)) return blocks + + return blocks.map(block => { + if (!isMalformedBase64Image(block)) return block + const src = block.source as unknown as Record + const mediaType = + typeof src.mediaType === 'string' && src.mediaType + ? src.mediaType + : detectImageFormatFromBase64(block.source.data) + return { + ...block, + source: { + type: 'base64' as const, + media_type: mediaType as Base64ImageSource['media_type'], + data: block.source.data, + }, + } + }) +} + +function isMalformedBase64Image( + block: ContentBlockParam, +): block is ImageBlockParam & { source: Base64ImageSource } { + if (block.type !== 'image' || block.source?.type !== 'base64') return false + return !(block.source as unknown as Record).media_type +} diff --git a/bridge/initReplBridge.ts b/bridge/initReplBridge.ts new file mode 100644 index 0000000..85e403d --- /dev/null +++ b/bridge/initReplBridge.ts @@ -0,0 +1,569 @@ +/** + * REPL-specific wrapper around initBridgeCore. Owns the parts that read + * bootstrap state — gates, cwd, session ID, git context, OAuth, title + * derivation — then delegates to the bootstrap-free core. + * + * Split out of replBridge.ts because the sessionStorage import + * (getCurrentSessionTitle) transitively pulls in src/commands.ts → the + * entire slash command + React component tree (~1300 modules). Keeping + * initBridgeCore in a file that doesn't touch sessionStorage lets + * daemonBridge.ts import the core without bloating the Agent SDK bundle. + * + * Called via dynamic import by useReplBridge (auto-start) and print.ts + * (SDK -p mode via query.enableRemoteControl). + */ + +import { feature } from 'bun:bundle' +import { hostname } from 'os' +import { getOriginalCwd, getSessionId } from '../bootstrap/state.js' +import type { SDKMessage } from '../entrypoints/agentSdkTypes.js' +import type { SDKControlResponse } from '../entrypoints/sdk/controlTypes.js' +import { getFeatureValue_CACHED_WITH_REFRESH } from '../services/analytics/growthbook.js' +import { getOrganizationUUID } from '../services/oauth/client.js' +import { + isPolicyAllowed, + waitForPolicyLimitsToLoad, +} from '../services/policyLimits/index.js' +import type { Message } from '../types/message.js' +import { + checkAndRefreshOAuthTokenIfNeeded, + getClaudeAIOAuthTokens, + handleOAuth401Error, +} from '../utils/auth.js' +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' +import { logForDebugging } from '../utils/debug.js' +import { stripDisplayTagsAllowEmpty } from '../utils/displayTags.js' +import { errorMessage } from '../utils/errors.js' +import { getBranch, getRemoteUrl } from '../utils/git.js' +import { toSDKMessages } from '../utils/messages/mappers.js' +import { + getContentText, + getMessagesAfterCompactBoundary, + isSyntheticMessage, +} from '../utils/messages.js' +import type { PermissionMode } from '../utils/permissions/PermissionMode.js' +import { getCurrentSessionTitle } from '../utils/sessionStorage.js' +import { + extractConversationText, + generateSessionTitle, +} from '../utils/sessionTitle.js' +import { generateShortWordSlug } from '../utils/words.js' +import { + getBridgeAccessToken, + getBridgeBaseUrl, + getBridgeTokenOverride, +} from './bridgeConfig.js' +import { + checkBridgeMinVersion, + isBridgeEnabledBlocking, + isCseShimEnabled, + isEnvLessBridgeEnabled, +} from './bridgeEnabled.js' +import { + archiveBridgeSession, + createBridgeSession, + updateBridgeSessionTitle, +} from './createSession.js' +import { logBridgeSkip } from './debugUtils.js' +import { checkEnvLessBridgeMinVersion } from './envLessBridgeConfig.js' +import { getPollIntervalConfig } from './pollConfig.js' +import type { BridgeState, ReplBridgeHandle } from './replBridge.js' +import { initBridgeCore } from './replBridge.js' +import { setCseShimGate } from './sessionIdCompat.js' +import type { BridgeWorkerType } from './types.js' + +export type InitBridgeOptions = { + onInboundMessage?: (msg: SDKMessage) => void | Promise + onPermissionResponse?: (response: SDKControlResponse) => void + onInterrupt?: () => void + onSetModel?: (model: string | undefined) => void + onSetMaxThinkingTokens?: (maxTokens: number | null) => void + onSetPermissionMode?: ( + mode: PermissionMode, + ) => { ok: true } | { ok: false; error: string } + onStateChange?: (state: BridgeState, detail?: string) => void + initialMessages?: Message[] + // Explicit session name from `/remote-control `. When set, overrides + // the title derived from the conversation or /rename. + initialName?: string + // Fresh view of the full conversation at call time. Used by onUserMessage's + // count-3 derivation to call generateSessionTitle over the full conversation. + // Optional — print.ts's SDK enableRemoteControl path has no REPL message + // array; count-3 falls back to the single message text when absent. + getMessages?: () => Message[] + // UUIDs already flushed in a prior bridge session. Messages with these + // UUIDs are excluded from the initial flush to avoid poisoning the + // server (duplicate UUIDs across sessions cause the WS to be killed). + // Mutated in place — newly flushed UUIDs are added after each flush. + previouslyFlushedUUIDs?: Set + /** See BridgeCoreParams.perpetual. */ + perpetual?: boolean + /** + * When true, the bridge only forwards events outbound (no SSE inbound + * stream). Used by CCR mirror mode — local sessions visible on claude.ai + * without enabling inbound control. + */ + outboundOnly?: boolean + tags?: string[] +} + +export async function initReplBridge( + options?: InitBridgeOptions, +): Promise { + const { + onInboundMessage, + onPermissionResponse, + onInterrupt, + onSetModel, + onSetMaxThinkingTokens, + onSetPermissionMode, + onStateChange, + initialMessages, + getMessages, + previouslyFlushedUUIDs, + initialName, + perpetual, + outboundOnly, + tags, + } = options ?? {} + + // Wire the cse_ shim kill switch so toCompatSessionId respects the + // GrowthBook gate. Daemon/SDK paths skip this — shim defaults to active. + setCseShimGate(isCseShimEnabled) + + // 1. Runtime gate + if (!(await isBridgeEnabledBlocking())) { + logBridgeSkip('not_enabled', '[bridge:repl] Skipping: bridge not enabled') + return null + } + + // 1b. Minimum version check — deferred to after the v1/v2 branch below, + // since each implementation has its own floor (tengu_bridge_min_version + // for v1, tengu_bridge_repl_v2_config.min_version for v2). + + // 2. Check OAuth — must be signed in with claude.ai. Runs before the + // policy check so console-auth users get the actionable "/login" hint + // instead of a misleading policy error from a stale/wrong-org cache. + if (!getBridgeAccessToken()) { + logBridgeSkip('no_oauth', '[bridge:repl] Skipping: no OAuth tokens') + onStateChange?.('failed', '/login') + return null + } + + // 3. Check organization policy — remote control may be disabled + await waitForPolicyLimitsToLoad() + if (!isPolicyAllowed('allow_remote_control')) { + logBridgeSkip( + 'policy_denied', + '[bridge:repl] Skipping: allow_remote_control policy not allowed', + ) + onStateChange?.('failed', "disabled by your organization's policy") + return null + } + + // When CLAUDE_BRIDGE_OAUTH_TOKEN is set (ant-only local dev), the bridge + // uses that token directly via getBridgeAccessToken() — keychain state is + // irrelevant. Skip 2b/2c to preserve that decoupling: an expired keychain + // token shouldn't block a bridge connection that doesn't use it. + if (!getBridgeTokenOverride()) { + // 2a. Cross-process backoff. If N prior processes already saw this exact + // dead token (matched by expiresAt), skip silently — no event, no refresh + // attempt. The count threshold tolerates transient refresh failures (auth + // server 5xx, lockfile errors per auth.ts:1437/1444/1485): each process + // independently retries until 3 consecutive failures prove the token dead. + // Mirrors useReplBridge's MAX_CONSECUTIVE_INIT_FAILURES for in-process. + // The expiresAt key is content-addressed: /login → new token → new expiresAt + // → this stops matching without any explicit clear. + const cfg = getGlobalConfig() + if ( + cfg.bridgeOauthDeadExpiresAt != null && + (cfg.bridgeOauthDeadFailCount ?? 0) >= 3 && + getClaudeAIOAuthTokens()?.expiresAt === cfg.bridgeOauthDeadExpiresAt + ) { + logForDebugging( + `[bridge:repl] Skipping: cross-process backoff (dead token seen ${cfg.bridgeOauthDeadFailCount} times)`, + ) + return null + } + + // 2b. Proactively refresh if expired. Mirrors bridgeMain.ts:2096 — the REPL + // bridge fires at useEffect mount BEFORE any v1/messages call, making this + // usually the first OAuth request of the session. Without this, ~9% of + // registrations hit the server with a >8h-expired token → 401 → withOAuthRetry + // recovers, but the server logs a 401 we can avoid. VPN egress IPs observed + // at 30:1 401:200 when many unrelated users cluster at the 8h TTL boundary. + // + // Fresh-token cost: one memoized read + one Date.now() comparison (~µs). + // checkAndRefreshOAuthTokenIfNeeded clears its own cache in every path that + // touches the keychain (refresh success, lockfile race, throw), so no + // explicit clearOAuthTokenCache() here — that would force a blocking + // keychain spawn on the 91%+ fresh-token path. + await checkAndRefreshOAuthTokenIfNeeded() + + // 2c. Skip if token is still expired post-refresh-attempt. Env-var / FD + // tokens (auth.ts:894-917) have expiresAt=null → never trip this. But a + // keychain token whose refresh token is dead (password change, org left, + // token GC'd) has expiresAt ({ + ...c, + bridgeOauthDeadExpiresAt: deadExpiresAt, + bridgeOauthDeadFailCount: + c.bridgeOauthDeadExpiresAt === deadExpiresAt + ? (c.bridgeOauthDeadFailCount ?? 0) + 1 + : 1, + })) + return null + } + } + + // 4. Compute baseUrl — needed by both v1 (env-based) and v2 (env-less) + // paths. Hoisted above the v2 gate so both can use it. + const baseUrl = getBridgeBaseUrl() + + // 5. Derive session title. Precedence: explicit initialName → /rename + // (session storage) → last meaningful user message → generated slug. + // Cosmetic only (claude.ai session list); the model never sees it. + // Two flags: `hasExplicitTitle` (initialName or /rename — never auto- + // overwrite) vs. `hasTitle` (any title, including auto-derived — blocks + // the count-1 re-derivation but not count-3). The onUserMessage callback + // (wired to both v1 and v2 below) derives from the 1st prompt and again + // from the 3rd so mobile/web show a title that reflects more context. + // The slug fallback (e.g. "remote-control-graceful-unicorn") makes + // auto-started sessions distinguishable in the claude.ai list before the + // first prompt. + let title = `remote-control-${generateShortWordSlug()}` + let hasTitle = false + let hasExplicitTitle = false + if (initialName) { + title = initialName + hasTitle = true + hasExplicitTitle = true + } else { + const sessionId = getSessionId() + const customTitle = sessionId + ? getCurrentSessionTitle(sessionId) + : undefined + if (customTitle) { + title = customTitle + hasTitle = true + hasExplicitTitle = true + } else if (initialMessages && initialMessages.length > 0) { + // Find the last user message that has meaningful content. Skip meta + // (nudges), tool results, compact summaries ("This session is being + // continued…"), non-human origins (task notifications, channel pushes), + // and synthetic interrupts ([Request interrupted by user]) — none are + // human-authored. Same filter as extractTitleText + isSyntheticMessage. + for (let i = initialMessages.length - 1; i >= 0; i--) { + const msg = initialMessages[i]! + if ( + msg.type !== 'user' || + msg.isMeta || + msg.toolUseResult || + msg.isCompactSummary || + (msg.origin && msg.origin.kind !== 'human') || + isSyntheticMessage(msg) + ) + continue + const rawContent = getContentText(msg.message.content) + if (!rawContent) continue + const derived = deriveTitle(rawContent) + if (!derived) continue + title = derived + hasTitle = true + break + } + } + } + + // Shared by both v1 and v2 — fires on every title-worthy user message until + // it returns true. At count 1: deriveTitle placeholder immediately, then + // generateSessionTitle (Haiku, sentence-case) fire-and-forget upgrade. At + // count 3: re-generate over the full conversation. Skips entirely if the + // title is explicit (/remote-control or /rename) — re-checks + // sessionStorage at call time so /rename between messages isn't clobbered. + // Skips count 1 if initialMessages already derived (that title is fresh); + // still refreshes at count 3. v2 passes cse_*; updateBridgeSessionTitle + // retags internally. + let userMessageCount = 0 + let lastBridgeSessionId: string | undefined + let genSeq = 0 + const patch = ( + derived: string, + bridgeSessionId: string, + atCount: number, + ): void => { + hasTitle = true + title = derived + logForDebugging( + `[bridge:repl] derived title from message ${atCount}: ${derived}`, + ) + void updateBridgeSessionTitle(bridgeSessionId, derived, { + baseUrl, + getAccessToken: getBridgeAccessToken, + }).catch(() => {}) + } + // Fire-and-forget Haiku generation with post-await guards. Re-checks /rename + // (sessionStorage), v1 env-lost (lastBridgeSessionId), and same-session + // out-of-order resolution (genSeq — count-1's Haiku resolving after count-3 + // would clobber the richer title). generateSessionTitle never rejects. + const generateAndPatch = (input: string, bridgeSessionId: string): void => { + const gen = ++genSeq + const atCount = userMessageCount + void generateSessionTitle(input, AbortSignal.timeout(15_000)).then( + generated => { + if ( + generated && + gen === genSeq && + lastBridgeSessionId === bridgeSessionId && + !getCurrentSessionTitle(getSessionId()) + ) { + patch(generated, bridgeSessionId, atCount) + } + }, + ) + } + const onUserMessage = (text: string, bridgeSessionId: string): boolean => { + if (hasExplicitTitle || getCurrentSessionTitle(getSessionId())) { + return true + } + // v1 env-lost re-creates the session with a new ID. Reset the count so + // the new session gets its own count-3 derivation; hasTitle stays true + // (new session was created via getCurrentTitle(), which reads the count-1 + // title from this closure), so count-1 of the fresh cycle correctly skips. + if ( + lastBridgeSessionId !== undefined && + lastBridgeSessionId !== bridgeSessionId + ) { + userMessageCount = 0 + } + lastBridgeSessionId = bridgeSessionId + userMessageCount++ + if (userMessageCount === 1 && !hasTitle) { + const placeholder = deriveTitle(text) + if (placeholder) patch(placeholder, bridgeSessionId, userMessageCount) + generateAndPatch(text, bridgeSessionId) + } else if (userMessageCount === 3) { + const msgs = getMessages?.() + const input = msgs + ? extractConversationText(getMessagesAfterCompactBoundary(msgs)) + : text + generateAndPatch(input, bridgeSessionId) + } + // Also re-latches if v1 env-lost resets the transport's done flag past 3. + return userMessageCount >= 3 + } + + const initialHistoryCap = getFeatureValue_CACHED_WITH_REFRESH( + 'tengu_bridge_initial_history_cap', + 200, + 5 * 60 * 1000, + ) + + // Fetch orgUUID before the v1/v2 branch — both paths need it. v1 for + // environment registration; v2 for archive (which lives at the compat + // /v1/sessions/{id}/archive, not /v1/code/sessions). Without it, v2 + // archive 404s and sessions stay alive in CCR after /exit. + const orgUUID = await getOrganizationUUID() + if (!orgUUID) { + logBridgeSkip('no_org_uuid', '[bridge:repl] Skipping: no org UUID') + onStateChange?.('failed', '/login') + return null + } + + // ── GrowthBook gate: env-less bridge ────────────────────────────────── + // When enabled, skips the Environments API layer entirely (no register/ + // poll/ack/heartbeat) and connects directly via POST /bridge → worker_jwt. + // See server PR #292605 (renamed in #293280). REPL-only — daemon/print stay + // on env-based. + // + // NAMING: "env-less" is distinct from "CCR v2" (the /worker/* transport). + // The env-based path below can ALSO use CCR v2 via CLAUDE_CODE_USE_CCR_V2. + // tengu_bridge_repl_v2 gates env-less (no poll loop), not transport version. + // + // perpetual (assistant-mode session continuity via bridge-pointer.json) is + // env-coupled and not yet implemented here — fall back to env-based when set + // so KAIROS users don't silently lose cross-restart continuity. + if (isEnvLessBridgeEnabled() && !perpetual) { + const versionError = await checkEnvLessBridgeMinVersion() + if (versionError) { + logBridgeSkip( + 'version_too_old', + `[bridge:repl] Skipping: ${versionError}`, + true, + ) + onStateChange?.('failed', 'run `claude update` to upgrade') + return null + } + logForDebugging( + '[bridge:repl] Using env-less bridge path (tengu_bridge_repl_v2)', + ) + const { initEnvLessBridgeCore } = await import('./remoteBridgeCore.js') + return initEnvLessBridgeCore({ + baseUrl, + orgUUID, + title, + getAccessToken: getBridgeAccessToken, + onAuth401: handleOAuth401Error, + toSDKMessages, + initialHistoryCap, + initialMessages, + // v2 always creates a fresh server session (new cse_* id), so + // previouslyFlushedUUIDs is not passed — there's no cross-session + // UUID collision risk, and the ref persists across enable→disable→ + // re-enable cycles which would cause the new session to receive zero + // history (all UUIDs already in the set from the prior enable). + // v1 handles this by calling previouslyFlushedUUIDs.clear() on fresh + // session creation (replBridge.ts:768); v2 skips the param entirely. + onInboundMessage, + onUserMessage, + onPermissionResponse, + onInterrupt, + onSetModel, + onSetMaxThinkingTokens, + onSetPermissionMode, + onStateChange, + outboundOnly, + tags, + }) + } + + // ── v1 path: env-based (register/poll/ack/heartbeat) ────────────────── + + const versionError = checkBridgeMinVersion() + if (versionError) { + logBridgeSkip('version_too_old', `[bridge:repl] Skipping: ${versionError}`) + onStateChange?.('failed', 'run `claude update` to upgrade') + return null + } + + // Gather git context — this is the bootstrap-read boundary. + // Everything from here down is passed explicitly to bridgeCore. + const branch = await getBranch() + const gitRepoUrl = await getRemoteUrl() + const sessionIngressUrl = + process.env.USER_TYPE === 'ant' && + process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL + ? process.env.CLAUDE_BRIDGE_SESSION_INGRESS_URL + : baseUrl + + // Assistant-mode sessions advertise a distinct worker_type so the web UI + // can filter them into a dedicated picker. KAIROS guard keeps the + // assistant module out of external builds entirely. + let workerType: BridgeWorkerType = 'claude_code' + if (feature('KAIROS')) { + /* eslint-disable @typescript-eslint/no-require-imports */ + const { isAssistantMode } = + require('../assistant/index.js') as typeof import('../assistant/index.js') + /* eslint-enable @typescript-eslint/no-require-imports */ + if (isAssistantMode()) { + workerType = 'claude_code_assistant' + } + } + + // 6. Delegate. BridgeCoreHandle is a structural superset of + // ReplBridgeHandle (adds writeSdkMessages which REPL callers don't use), + // so no adapter needed — just the narrower type on the way out. + return initBridgeCore({ + dir: getOriginalCwd(), + machineName: hostname(), + branch, + gitRepoUrl, + title, + baseUrl, + sessionIngressUrl, + workerType, + getAccessToken: getBridgeAccessToken, + createSession: opts => + createBridgeSession({ + ...opts, + events: [], + baseUrl, + getAccessToken: getBridgeAccessToken, + }), + archiveSession: sessionId => + archiveBridgeSession(sessionId, { + baseUrl, + getAccessToken: getBridgeAccessToken, + // gracefulShutdown.ts:407 races runCleanupFunctions against 2s. + // Teardown also does stopWork (parallel) + deregister (sequential), + // so archive can't have the full budget. 1.5s matches v2's + // teardown_archive_timeout_ms default. + timeoutMs: 1500, + }).catch((err: unknown) => { + // archiveBridgeSession has no try/catch — 5xx/timeout/network throw + // straight through. Previously swallowed silently, making archive + // failures BQ-invisible and undiagnosable from debug logs. + logForDebugging( + `[bridge:repl] archiveBridgeSession threw: ${errorMessage(err)}`, + { level: 'error' }, + ) + }), + // getCurrentTitle is read on reconnect-after-env-lost to re-title the new + // session. /rename writes to session storage; onUserMessage mutates + // `title` directly — both paths are picked up here. + getCurrentTitle: () => getCurrentSessionTitle(getSessionId()) ?? title, + onUserMessage, + toSDKMessages, + onAuth401: handleOAuth401Error, + getPollIntervalConfig, + initialHistoryCap, + initialMessages, + previouslyFlushedUUIDs, + onInboundMessage, + onPermissionResponse, + onInterrupt, + onSetModel, + onSetMaxThinkingTokens, + onSetPermissionMode, + onStateChange, + perpetual, + }) +} + +const TITLE_MAX_LEN = 50 + +/** + * Quick placeholder title: strip display tags, take the first sentence, + * collapse whitespace, truncate to 50 chars. Returns undefined if the result + * is empty (e.g. message was only ). Replaced by + * generateSessionTitle once Haiku resolves (~1-15s). + */ +function deriveTitle(raw: string): string | undefined { + // Strip , , etc. — these appear in + // user messages when IDE/hooks inject context. stripDisplayTagsAllowEmpty + // returns '' (not the original) so pure-tag messages are skipped. + const clean = stripDisplayTagsAllowEmpty(raw) + // First sentence is usually the intent; rest is often context/detail. + // Capture group instead of lookbehind — keeps YARR JIT happy. + const firstSentence = /^(.*?[.!?])\s/.exec(clean)?.[1] ?? clean + // Collapse newlines/tabs — titles are single-line in the claude.ai list. + const flat = firstSentence.replace(/\s+/g, ' ').trim() + if (!flat) return undefined + return flat.length > TITLE_MAX_LEN + ? flat.slice(0, TITLE_MAX_LEN - 1) + '\u2026' + : flat +} diff --git a/bridge/jwtUtils.ts b/bridge/jwtUtils.ts new file mode 100644 index 0000000..030c001 --- /dev/null +++ b/bridge/jwtUtils.ts @@ -0,0 +1,256 @@ +import { logEvent } from '../services/analytics/index.js' +import { logForDebugging } from '../utils/debug.js' +import { logForDiagnosticsNoPII } from '../utils/diagLogs.js' +import { errorMessage } from '../utils/errors.js' +import { jsonParse } from '../utils/slowOperations.js' + +/** Format a millisecond duration as a human-readable string (e.g. "5m 30s"). */ +function formatDuration(ms: number): string { + if (ms < 60_000) return `${Math.round(ms / 1000)}s` + const m = Math.floor(ms / 60_000) + const s = Math.round((ms % 60_000) / 1000) + return s > 0 ? `${m}m ${s}s` : `${m}m` +} + +/** + * Decode a JWT's payload segment without verifying the signature. + * Strips the `sk-ant-si-` session-ingress prefix if present. + * Returns the parsed JSON payload as `unknown`, or `null` if the + * token is malformed or the payload is not valid JSON. + */ +export function decodeJwtPayload(token: string): unknown | null { + const jwt = token.startsWith('sk-ant-si-') + ? token.slice('sk-ant-si-'.length) + : token + const parts = jwt.split('.') + if (parts.length !== 3 || !parts[1]) return null + try { + return jsonParse(Buffer.from(parts[1], 'base64url').toString('utf8')) + } catch { + return null + } +} + +/** + * Decode the `exp` (expiry) claim from a JWT without verifying the signature. + * @returns The `exp` value in Unix seconds, or `null` if unparseable + */ +export function decodeJwtExpiry(token: string): number | null { + const payload = decodeJwtPayload(token) + if ( + payload !== null && + typeof payload === 'object' && + 'exp' in payload && + typeof payload.exp === 'number' + ) { + return payload.exp + } + return null +} + +/** Refresh buffer: request a new token before expiry. */ +const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000 + +/** Fallback refresh interval when the new token's expiry is unknown. */ +const FALLBACK_REFRESH_INTERVAL_MS = 30 * 60 * 1000 // 30 minutes + +/** Max consecutive failures before giving up on the refresh chain. */ +const MAX_REFRESH_FAILURES = 3 + +/** Retry delay when getAccessToken returns undefined. */ +const REFRESH_RETRY_DELAY_MS = 60_000 + +/** + * Creates a token refresh scheduler that proactively refreshes session tokens + * before they expire. Used by both the standalone bridge and the REPL bridge. + * + * When a token is about to expire, the scheduler calls `onRefresh` with the + * session ID and the bridge's OAuth access token. The caller is responsible + * for delivering the token to the appropriate transport (child process stdin + * for standalone bridge, WebSocket reconnect for REPL bridge). + */ +export function createTokenRefreshScheduler({ + getAccessToken, + onRefresh, + label, + refreshBufferMs = TOKEN_REFRESH_BUFFER_MS, +}: { + getAccessToken: () => string | undefined | Promise + onRefresh: (sessionId: string, oauthToken: string) => void + label: string + /** How long before expiry to fire refresh. Defaults to 5 min. */ + refreshBufferMs?: number +}): { + schedule: (sessionId: string, token: string) => void + scheduleFromExpiresIn: (sessionId: string, expiresInSeconds: number) => void + cancel: (sessionId: string) => void + cancelAll: () => void +} { + const timers = new Map>() + const failureCounts = new Map() + // Generation counter per session — incremented by schedule() and cancel() + // so that in-flight async doRefresh() calls can detect when they've been + // superseded and should skip setting follow-up timers. + const generations = new Map() + + function nextGeneration(sessionId: string): number { + const gen = (generations.get(sessionId) ?? 0) + 1 + generations.set(sessionId, gen) + return gen + } + + function schedule(sessionId: string, token: string): void { + const expiry = decodeJwtExpiry(token) + if (!expiry) { + // Token is not a decodable JWT (e.g. an OAuth token passed from the + // REPL bridge WebSocket open handler). Preserve any existing timer + // (such as the follow-up refresh set by doRefresh) so the refresh + // chain is not broken. + logForDebugging( + `[${label}:token] Could not decode JWT expiry for sessionId=${sessionId}, token prefix=${token.slice(0, 15)}…, keeping existing timer`, + ) + return + } + + // Clear any existing refresh timer — we have a concrete expiry to replace it. + const existing = timers.get(sessionId) + if (existing) { + clearTimeout(existing) + } + + // Bump generation to invalidate any in-flight async doRefresh. + const gen = nextGeneration(sessionId) + + const expiryDate = new Date(expiry * 1000).toISOString() + const delayMs = expiry * 1000 - Date.now() - refreshBufferMs + if (delayMs <= 0) { + logForDebugging( + `[${label}:token] Token for sessionId=${sessionId} expires=${expiryDate} (past or within buffer), refreshing immediately`, + ) + void doRefresh(sessionId, gen) + return + } + + logForDebugging( + `[${label}:token] Scheduled token refresh for sessionId=${sessionId} in ${formatDuration(delayMs)} (expires=${expiryDate}, buffer=${refreshBufferMs / 1000}s)`, + ) + + const timer = setTimeout(doRefresh, delayMs, sessionId, gen) + timers.set(sessionId, timer) + } + + /** + * Schedule refresh using an explicit TTL (seconds until expiry) rather + * than decoding a JWT's exp claim. Used by callers whose JWT is opaque + * (e.g. POST /v1/code/sessions/{id}/bridge returns expires_in directly). + */ + function scheduleFromExpiresIn( + sessionId: string, + expiresInSeconds: number, + ): void { + const existing = timers.get(sessionId) + if (existing) clearTimeout(existing) + const gen = nextGeneration(sessionId) + // Clamp to 30s floor — if refreshBufferMs exceeds the server's expires_in + // (e.g. very large buffer for frequent-refresh testing, or server shortens + // expires_in unexpectedly), unclamped delayMs ≤ 0 would tight-loop. + const delayMs = Math.max(expiresInSeconds * 1000 - refreshBufferMs, 30_000) + logForDebugging( + `[${label}:token] Scheduled token refresh for sessionId=${sessionId} in ${formatDuration(delayMs)} (expires_in=${expiresInSeconds}s, buffer=${refreshBufferMs / 1000}s)`, + ) + const timer = setTimeout(doRefresh, delayMs, sessionId, gen) + timers.set(sessionId, timer) + } + + async function doRefresh(sessionId: string, gen: number): Promise { + let oauthToken: string | undefined + try { + oauthToken = await getAccessToken() + } catch (err) { + logForDebugging( + `[${label}:token] getAccessToken threw for sessionId=${sessionId}: ${errorMessage(err)}`, + { level: 'error' }, + ) + } + + // If the session was cancelled or rescheduled while we were awaiting, + // the generation will have changed — bail out to avoid orphaned timers. + if (generations.get(sessionId) !== gen) { + logForDebugging( + `[${label}:token] doRefresh for sessionId=${sessionId} stale (gen ${gen} vs ${generations.get(sessionId)}), skipping`, + ) + return + } + + if (!oauthToken) { + const failures = (failureCounts.get(sessionId) ?? 0) + 1 + failureCounts.set(sessionId, failures) + logForDebugging( + `[${label}:token] No OAuth token available for refresh, sessionId=${sessionId} (failure ${failures}/${MAX_REFRESH_FAILURES})`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'bridge_token_refresh_no_oauth') + // Schedule a retry so the refresh chain can recover if the token + // becomes available again (e.g. transient cache clear during refresh). + // Cap retries to avoid spamming on genuine failures. + if (failures < MAX_REFRESH_FAILURES) { + const retryTimer = setTimeout( + doRefresh, + REFRESH_RETRY_DELAY_MS, + sessionId, + gen, + ) + timers.set(sessionId, retryTimer) + } + return + } + + // Reset failure counter on successful token retrieval + failureCounts.delete(sessionId) + + logForDebugging( + `[${label}:token] Refreshing token for sessionId=${sessionId}: new token prefix=${oauthToken.slice(0, 15)}…`, + ) + logEvent('tengu_bridge_token_refreshed', {}) + onRefresh(sessionId, oauthToken) + + // Schedule a follow-up refresh so long-running sessions stay authenticated. + // Without this, the initial one-shot timer leaves the session vulnerable + // to token expiry if it runs past the first refresh window. + const timer = setTimeout( + doRefresh, + FALLBACK_REFRESH_INTERVAL_MS, + sessionId, + gen, + ) + timers.set(sessionId, timer) + logForDebugging( + `[${label}:token] Scheduled follow-up refresh for sessionId=${sessionId} in ${formatDuration(FALLBACK_REFRESH_INTERVAL_MS)}`, + ) + } + + function cancel(sessionId: string): void { + // Bump generation to invalidate any in-flight async doRefresh. + nextGeneration(sessionId) + const timer = timers.get(sessionId) + if (timer) { + clearTimeout(timer) + timers.delete(sessionId) + } + failureCounts.delete(sessionId) + } + + function cancelAll(): void { + // Bump all generations so in-flight doRefresh calls are invalidated. + for (const sessionId of generations.keys()) { + nextGeneration(sessionId) + } + for (const timer of timers.values()) { + clearTimeout(timer) + } + timers.clear() + failureCounts.clear() + } + + return { schedule, scheduleFromExpiresIn, cancel, cancelAll } +} diff --git a/bridge/pollConfig.ts b/bridge/pollConfig.ts new file mode 100644 index 0000000..024b476 --- /dev/null +++ b/bridge/pollConfig.ts @@ -0,0 +1,110 @@ +import { z } from 'zod/v4' +import { getFeatureValue_CACHED_WITH_REFRESH } from '../services/analytics/growthbook.js' +import { lazySchema } from '../utils/lazySchema.js' +import { + DEFAULT_POLL_CONFIG, + type PollIntervalConfig, +} from './pollConfigDefaults.js' + +// .min(100) on the seek-work intervals restores the old Math.max(..., 100) +// defense-in-depth floor against fat-fingered GrowthBook values. Unlike a +// clamp, Zod rejects the whole object on violation — a config with one bad +// field falls back to DEFAULT_POLL_CONFIG entirely rather than being +// partially trusted. +// +// The at_capacity intervals use a 0-or-≥100 refinement: 0 means "disabled" +// (heartbeat-only mode), ≥100 is the fat-finger floor. Values 1–99 are +// rejected so unit confusion (ops thinks seconds, enters 10) doesn't poll +// every 10ms against the VerifyEnvironmentSecretAuth DB path. +// +// The object-level refines require at least one at-capacity liveness +// mechanism enabled: heartbeat OR the relevant poll interval. Without this, +// the hb=0, atCapMs=0 drift config (ops disables heartbeat without +// restoring at_capacity) falls through every throttle site with no sleep — +// tight-looping /poll at HTTP-round-trip speed. +const zeroOrAtLeast100 = { + message: 'must be 0 (disabled) or ≥100ms', +} +const pollIntervalConfigSchema = lazySchema(() => + z + .object({ + poll_interval_ms_not_at_capacity: z.number().int().min(100), + // 0 = no at-capacity polling. Independent of heartbeat — both can be + // enabled (heartbeat runs, periodically breaks out to poll). + poll_interval_ms_at_capacity: z + .number() + .int() + .refine(v => v === 0 || v >= 100, zeroOrAtLeast100), + // 0 = disabled; positive value = heartbeat at this interval while at + // capacity. Runs alongside at-capacity polling, not instead of it. + // Named non_exclusive to distinguish from the old heartbeat_interval_ms + // (either-or semantics in pre-#22145 clients). .default(0) so existing + // GrowthBook configs without this field parse successfully. + non_exclusive_heartbeat_interval_ms: z.number().int().min(0).default(0), + // Multisession (bridgeMain.ts) intervals. Defaults match the + // single-session values so existing configs without these fields + // preserve current behavior. + multisession_poll_interval_ms_not_at_capacity: z + .number() + .int() + .min(100) + .default( + DEFAULT_POLL_CONFIG.multisession_poll_interval_ms_not_at_capacity, + ), + multisession_poll_interval_ms_partial_capacity: z + .number() + .int() + .min(100) + .default( + DEFAULT_POLL_CONFIG.multisession_poll_interval_ms_partial_capacity, + ), + multisession_poll_interval_ms_at_capacity: z + .number() + .int() + .refine(v => v === 0 || v >= 100, zeroOrAtLeast100) + .default(DEFAULT_POLL_CONFIG.multisession_poll_interval_ms_at_capacity), + // .min(1) matches the server's ge=1 constraint (work_v1.py:230). + reclaim_older_than_ms: z.number().int().min(1).default(5000), + session_keepalive_interval_v2_ms: z + .number() + .int() + .min(0) + .default(120_000), + }) + .refine( + cfg => + cfg.non_exclusive_heartbeat_interval_ms > 0 || + cfg.poll_interval_ms_at_capacity > 0, + { + message: + 'at-capacity liveness requires non_exclusive_heartbeat_interval_ms > 0 or poll_interval_ms_at_capacity > 0', + }, + ) + .refine( + cfg => + cfg.non_exclusive_heartbeat_interval_ms > 0 || + cfg.multisession_poll_interval_ms_at_capacity > 0, + { + message: + 'at-capacity liveness requires non_exclusive_heartbeat_interval_ms > 0 or multisession_poll_interval_ms_at_capacity > 0', + }, + ), +) + +/** + * Fetch the bridge poll interval config from GrowthBook with a 5-minute + * refresh window. Validates the served JSON against the schema; falls back + * to defaults if the flag is absent, malformed, or partially-specified. + * + * Shared by bridgeMain.ts (standalone) and replBridge.ts (REPL) so ops + * can tune both poll rates fleet-wide with a single config push. + */ +export function getPollIntervalConfig(): PollIntervalConfig { + const raw = getFeatureValue_CACHED_WITH_REFRESH( + 'tengu_bridge_poll_interval_config', + DEFAULT_POLL_CONFIG, + 5 * 60 * 1000, + ) + const parsed = pollIntervalConfigSchema().safeParse(raw) + return parsed.success ? parsed.data : DEFAULT_POLL_CONFIG +} diff --git a/bridge/pollConfigDefaults.ts b/bridge/pollConfigDefaults.ts new file mode 100644 index 0000000..7a4e6d8 --- /dev/null +++ b/bridge/pollConfigDefaults.ts @@ -0,0 +1,82 @@ +/** + * Bridge poll interval defaults. Extracted from pollConfig.ts so callers + * that don't need live GrowthBook tuning (daemon via Agent SDK) can avoid + * the growthbook.ts → config.ts → file.ts → sessionStorage.ts → commands.ts + * transitive dependency chain. + */ + +/** + * Poll interval when actively seeking work (no transport / below maxSessions). + * Governs user-visible "connecting…" latency on initial work pickup and + * recovery speed after the server re-dispatches a work item. + */ +const POLL_INTERVAL_MS_NOT_AT_CAPACITY = 2000 + +/** + * Poll interval when the transport is connected. Runs independently of + * heartbeat — when both are enabled, the heartbeat loop breaks out to poll + * at this interval. Set to 0 to disable at-capacity polling entirely. + * + * Server-side constraints that bound this value: + * - BRIDGE_LAST_POLL_TTL = 4h (Redis key expiry → environment auto-archived) + * - max_poll_stale_seconds = 24h (session-creation health gate, currently disabled) + * + * 10 minutes gives 24× headroom on the Redis TTL while still picking up + * server-initiated token-rotation redispatches within one poll cycle. + * The transport auto-reconnects internally for 10 minutes on transient WS + * failures, so poll is not the recovery path — it's strictly a liveness + * signal plus a backstop for permanent close. + */ +const POLL_INTERVAL_MS_AT_CAPACITY = 600_000 + +/** + * Multisession bridge (bridgeMain.ts) poll intervals. Defaults match the + * single-session values so existing GrowthBook configs without these fields + * preserve current behavior. Ops can tune these independently via the + * tengu_bridge_poll_interval_config GB flag. + */ +const MULTISESSION_POLL_INTERVAL_MS_NOT_AT_CAPACITY = + POLL_INTERVAL_MS_NOT_AT_CAPACITY +const MULTISESSION_POLL_INTERVAL_MS_PARTIAL_CAPACITY = + POLL_INTERVAL_MS_NOT_AT_CAPACITY +const MULTISESSION_POLL_INTERVAL_MS_AT_CAPACITY = POLL_INTERVAL_MS_AT_CAPACITY + +export type PollIntervalConfig = { + poll_interval_ms_not_at_capacity: number + poll_interval_ms_at_capacity: number + non_exclusive_heartbeat_interval_ms: number + multisession_poll_interval_ms_not_at_capacity: number + multisession_poll_interval_ms_partial_capacity: number + multisession_poll_interval_ms_at_capacity: number + reclaim_older_than_ms: number + session_keepalive_interval_v2_ms: number +} + +export const DEFAULT_POLL_CONFIG: PollIntervalConfig = { + poll_interval_ms_not_at_capacity: POLL_INTERVAL_MS_NOT_AT_CAPACITY, + poll_interval_ms_at_capacity: POLL_INTERVAL_MS_AT_CAPACITY, + // 0 = disabled. When > 0, at-capacity loops send per-work-item heartbeats + // at this interval. Independent of poll_interval_ms_at_capacity — both may + // run (heartbeat periodically yields to poll). 60s gives 5× headroom under + // the server's 300s heartbeat TTL. Named non_exclusive to distinguish from + // the old heartbeat_interval_ms field (either-or semantics in pre-#22145 + // clients — heartbeat suppressed poll). Old clients ignore this key; ops + // can set both fields during rollout. + non_exclusive_heartbeat_interval_ms: 0, + multisession_poll_interval_ms_not_at_capacity: + MULTISESSION_POLL_INTERVAL_MS_NOT_AT_CAPACITY, + multisession_poll_interval_ms_partial_capacity: + MULTISESSION_POLL_INTERVAL_MS_PARTIAL_CAPACITY, + multisession_poll_interval_ms_at_capacity: + MULTISESSION_POLL_INTERVAL_MS_AT_CAPACITY, + // Poll query param: reclaim unacknowledged work items older than this. + // Matches the server's DEFAULT_RECLAIM_OLDER_THAN_MS (work_service.py:24). + // Enables picking up stale-pending work after JWT expiry, when the prior + // ack failed because the session_ingress_token was already stale. + reclaim_older_than_ms: 5000, + // 0 = disabled. When > 0, push a silent {type:'keep_alive'} frame to + // session-ingress at this interval so upstream proxies don't GC an idle + // remote-control session. 2 min is the default. _v2: bridge-only gate + // (pre-v2 clients read the old key, new clients ignore it). + session_keepalive_interval_v2_ms: 120_000, +} diff --git a/bridge/remoteBridgeCore.ts b/bridge/remoteBridgeCore.ts new file mode 100644 index 0000000..76545f6 --- /dev/null +++ b/bridge/remoteBridgeCore.ts @@ -0,0 +1,1008 @@ +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +/** + * Env-less Remote Control bridge core. + * + * "Env-less" = no Environments API layer. Distinct from "CCR v2" (the + * /worker/* transport protocol) — the env-based path (replBridge.ts) can also + * use CCR v2 transport via CLAUDE_CODE_USE_CCR_V2. This file is about removing + * the poll/dispatch layer, not about which transport protocol is underneath. + * + * Unlike initBridgeCore (env-based, ~2400 lines), this connects directly + * to the session-ingress layer without the Environments API work-dispatch + * layer: + * + * 1. POST /v1/code/sessions (OAuth, no env_id) → session.id + * 2. POST /v1/code/sessions/{id}/bridge (OAuth) → {worker_jwt, expires_in, api_base_url, worker_epoch} + * Each /bridge call bumps epoch — it IS the register. No separate /worker/register. + * 3. createV2ReplTransport(worker_jwt, worker_epoch) → SSE + CCRClient + * 4. createTokenRefreshScheduler → proactive /bridge re-call (new JWT + new epoch) + * 5. 401 on SSE → rebuild transport with fresh /bridge credentials (same seq-num) + * + * No register/poll/ack/stop/heartbeat/deregister environment lifecycle. + * The Environments API historically existed because CCR's /worker/* + * endpoints required a session_id+role=worker JWT that only the work-dispatch + * layer could mint. Server PR #292605 (renamed in #293280) adds the /bridge endpoint as a direct + * OAuth→worker_jwt exchange, making the env layer optional for REPL sessions. + * + * Gated by `tengu_bridge_repl_v2` GrowthBook flag in initReplBridge.ts. + * REPL-only — daemon/print stay on env-based. + */ + +import { feature } from 'bun:bundle' +import axios from 'axios' +import { + createV2ReplTransport, + type ReplBridgeTransport, +} from './replBridgeTransport.js' +import { buildCCRv2SdkUrl } from './workSecret.js' +import { toCompatSessionId } from './sessionIdCompat.js' +import { FlushGate } from './flushGate.js' +import { createTokenRefreshScheduler } from './jwtUtils.js' +import { getTrustedDeviceToken } from './trustedDevice.js' +import { + getEnvLessBridgeConfig, + type EnvLessBridgeConfig, +} from './envLessBridgeConfig.js' +import { + handleIngressMessage, + handleServerControlRequest, + makeResultMessage, + isEligibleBridgeMessage, + extractTitleText, + BoundedUUIDSet, +} from './bridgeMessaging.js' +import { logBridgeSkip } from './debugUtils.js' +import { logForDebugging } from '../utils/debug.js' +import { logForDiagnosticsNoPII } from '../utils/diagLogs.js' +import { isInProtectedNamespace } from '../utils/envUtils.js' +import { errorMessage } from '../utils/errors.js' +import { sleep } from '../utils/sleep.js' +import { registerCleanup } from '../utils/cleanupRegistry.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import type { ReplBridgeHandle, BridgeState } from './replBridge.js' +import type { Message } from '../types/message.js' +import type { SDKMessage } from '../entrypoints/agentSdkTypes.js' +import type { + SDKControlRequest, + SDKControlResponse, +} from '../entrypoints/sdk/controlTypes.js' +import type { PermissionMode } from '../utils/permissions/PermissionMode.js' + +const ANTHROPIC_VERSION = '2023-06-01' + +// Telemetry discriminator for ws_connected. 'initial' is the default and +// never passed to rebuildTransport (which can only be called post-init); +// Exclude<> makes that constraint explicit at both signatures. +type ConnectCause = 'initial' | 'proactive_refresh' | 'auth_401_recovery' + +function oauthHeaders(accessToken: string): Record { + return { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + 'anthropic-version': ANTHROPIC_VERSION, + } +} + +export type EnvLessBridgeParams = { + baseUrl: string + orgUUID: string + title: string + getAccessToken: () => string | undefined + onAuth401?: (staleAccessToken: string) => Promise + /** + * Converts internal Message[] → SDKMessage[] for writeMessages() and the + * initial-flush/drain paths. Injected rather than imported — mappers.ts + * transitively pulls in src/commands.ts (entire command registry + React + * tree) which would bloat bundles that don't already have it. + */ + toSDKMessages: (messages: Message[]) => SDKMessage[] + initialHistoryCap: number + initialMessages?: Message[] + onInboundMessage?: (msg: SDKMessage) => void | Promise + /** + * Fired on each title-worthy user message seen in writeMessages() until + * the callback returns true (done). Mirrors replBridge.ts's onUserMessage — + * caller derives a title and PATCHes /v1/sessions/{id} so auto-started + * sessions don't stay at the generic fallback. The caller owns the + * derive-at-count-1-and-3 policy; the transport just keeps calling until + * told to stop. sessionId is the raw cse_* — updateBridgeSessionTitle + * retags internally. + */ + onUserMessage?: (text: string, sessionId: string) => boolean + onPermissionResponse?: (response: SDKControlResponse) => void + onInterrupt?: () => void + onSetModel?: (model: string | undefined) => void + onSetMaxThinkingTokens?: (maxTokens: number | null) => void + onSetPermissionMode?: ( + mode: PermissionMode, + ) => { ok: true } | { ok: false; error: string } + onStateChange?: (state: BridgeState, detail?: string) => void + /** + * When true, skip opening the SSE read stream — only the CCRClient write + * path is activated. Threaded to createV2ReplTransport and + * handleServerControlRequest. + */ + outboundOnly?: boolean + /** Free-form tags for session categorization (e.g. ['ccr-mirror']). */ + tags?: string[] +} + +/** + * Create a session, fetch a worker JWT, connect the v2 transport. + * + * Returns null on any pre-flight failure (session create failed, /bridge + * failed, transport setup failed). Caller (initReplBridge) surfaces this + * as a generic "initialization failed" state. + */ +export async function initEnvLessBridgeCore( + params: EnvLessBridgeParams, +): Promise { + const { + baseUrl, + orgUUID, + title, + getAccessToken, + onAuth401, + toSDKMessages, + initialHistoryCap, + initialMessages, + onInboundMessage, + onUserMessage, + onPermissionResponse, + onInterrupt, + onSetModel, + onSetMaxThinkingTokens, + onSetPermissionMode, + onStateChange, + outboundOnly, + tags, + } = params + + const cfg = await getEnvLessBridgeConfig() + + // ── 1. Create session (POST /v1/code/sessions, no env_id) ─────────────── + const accessToken = getAccessToken() + if (!accessToken) { + logForDebugging('[remote-bridge] No OAuth token') + return null + } + + const createdSessionId = await withRetry( + () => + createCodeSession(baseUrl, accessToken, title, cfg.http_timeout_ms, tags), + 'createCodeSession', + cfg, + ) + if (!createdSessionId) { + onStateChange?.('failed', 'Session creation failed — see debug log') + logBridgeSkip('v2_session_create_failed', undefined, true) + return null + } + const sessionId: string = createdSessionId + logForDebugging(`[remote-bridge] Created session ${sessionId}`) + logForDiagnosticsNoPII('info', 'bridge_repl_v2_session_created') + + // ── 2. Fetch bridge credentials (POST /bridge → worker_jwt, expires_in, api_base_url) ── + const credentials = await withRetry( + () => + fetchRemoteCredentials( + sessionId, + baseUrl, + accessToken, + cfg.http_timeout_ms, + ), + 'fetchRemoteCredentials', + cfg, + ) + if (!credentials) { + onStateChange?.('failed', 'Remote credentials fetch failed — see debug log') + logBridgeSkip('v2_remote_creds_failed', undefined, true) + void archiveSession( + sessionId, + baseUrl, + accessToken, + orgUUID, + cfg.http_timeout_ms, + ) + return null + } + logForDebugging( + `[remote-bridge] Fetched bridge credentials (expires_in=${credentials.expires_in}s)`, + ) + + // ── 3. Build v2 transport (SSETransport + CCRClient) ──────────────────── + const sessionUrl = buildCCRv2SdkUrl(credentials.api_base_url, sessionId) + logForDebugging(`[remote-bridge] v2 session URL: ${sessionUrl}`) + + let transport: ReplBridgeTransport + try { + transport = await createV2ReplTransport({ + sessionUrl, + ingressToken: credentials.worker_jwt, + sessionId, + epoch: credentials.worker_epoch, + heartbeatIntervalMs: cfg.heartbeat_interval_ms, + heartbeatJitterFraction: cfg.heartbeat_jitter_fraction, + // Per-instance closure — keeps the worker JWT out of + // process.env.CLAUDE_CODE_SESSION_ACCESS_TOKEN, which mcp/client.ts + // reads ungatedly and would otherwise send to user-configured ws/http + // MCP servers. Frozen-at-construction is correct: transport is fully + // rebuilt on refresh (rebuildTransport below). + getAuthToken: () => credentials.worker_jwt, + outboundOnly, + }) + } catch (err) { + logForDebugging( + `[remote-bridge] v2 transport setup failed: ${errorMessage(err)}`, + { level: 'error' }, + ) + onStateChange?.('failed', `Transport setup failed: ${errorMessage(err)}`) + logBridgeSkip('v2_transport_setup_failed', undefined, true) + void archiveSession( + sessionId, + baseUrl, + accessToken, + orgUUID, + cfg.http_timeout_ms, + ) + return null + } + logForDebugging( + `[remote-bridge] v2 transport created (epoch=${credentials.worker_epoch})`, + ) + onStateChange?.('ready') + + // ── 4. State ──────────────────────────────────────────────────────────── + + // Echo dedup: messages we POST come back on the read stream. Seeded with + // initial message UUIDs so server echoes of flushed history are recognized. + // Both sets cover initial UUIDs — recentPostedUUIDs is a 2000-cap ring buffer + // and could evict them after enough live writes; initialMessageUUIDs is the + // unbounded fallback. Defense-in-depth; mirrors replBridge.ts. + const recentPostedUUIDs = new BoundedUUIDSet(cfg.uuid_dedup_buffer_size) + const initialMessageUUIDs = new Set() + if (initialMessages) { + for (const msg of initialMessages) { + initialMessageUUIDs.add(msg.uuid) + recentPostedUUIDs.add(msg.uuid) + } + } + + // Defensive dedup for re-delivered inbound prompts (seq-num negotiation + // edge cases, server history replay after transport swap). + const recentInboundUUIDs = new BoundedUUIDSet(cfg.uuid_dedup_buffer_size) + + // FlushGate: queue live writes while the history flush POST is in flight, + // so the server receives [history..., live...] in order. + const flushGate = new FlushGate() + + let initialFlushDone = false + let tornDown = false + let authRecoveryInFlight = false + // Latch for onUserMessage — flips true when the callback returns true + // (policy says "done deriving"). sessionId is const (no re-create path — + // rebuildTransport swaps JWT/epoch, same session), so no reset needed. + let userMessageCallbackDone = !onUserMessage + + // Telemetry: why did onConnect fire? Set by rebuildTransport before + // wireTransportCallbacks; read asynchronously by onConnect. Race-safe + // because authRecoveryInFlight serializes rebuild callers, and a fresh + // initEnvLessBridgeCore() call gets a fresh closure defaulting to 'initial'. + let connectCause: ConnectCause = 'initial' + + // Deadline for onConnect after transport.connect(). Cleared by onConnect + // (connected) and onClose (got a close — not silent). If neither fires + // before cfg.connect_timeout_ms, onConnectTimeout emits — the only + // signal for the `started → (silence)` gap. + let connectDeadline: ReturnType | undefined + function onConnectTimeout(cause: ConnectCause): void { + if (tornDown) return + logEvent('tengu_bridge_repl_connect_timeout', { + v2: true, + elapsed_ms: cfg.connect_timeout_ms, + cause: + cause as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + } + + // ── 5. JWT refresh scheduler ──────────────────────────────────────────── + // Schedule a callback 5min before expiry (per response.expires_in). On fire, + // re-fetch /bridge with OAuth → rebuild transport with fresh credentials. + // Each /bridge call bumps epoch server-side, so a JWT-only swap would leave + // the old CCRClient heartbeating with a stale epoch → 409 within 20s. + // JWT is opaque — do not decode. + const refresh = createTokenRefreshScheduler({ + refreshBufferMs: cfg.token_refresh_buffer_ms, + getAccessToken: async () => { + // Unconditionally refresh OAuth before calling /bridge — getAccessToken() + // returns expired tokens as non-null strings (doesn't check expiresAt), + // so truthiness doesn't mean valid. Pass the stale token to onAuth401 + // so handleOAuth401Error's keychain-comparison can detect parallel refresh. + const stale = getAccessToken() + if (onAuth401) await onAuth401(stale ?? '') + return getAccessToken() ?? stale + }, + onRefresh: (sid, oauthToken) => { + void (async () => { + // Laptop wake: overdue proactive timer + SSE 401 fire ~simultaneously. + // Claim the flag BEFORE the /bridge fetch so the other path skips + // entirely — prevents double epoch bump (each /bridge call bumps; if + // both fetch, the first rebuild gets a stale epoch and 409s). + if (authRecoveryInFlight || tornDown) { + logForDebugging( + '[remote-bridge] Recovery already in flight, skipping proactive refresh', + ) + return + } + authRecoveryInFlight = true + try { + const fresh = await withRetry( + () => + fetchRemoteCredentials( + sid, + baseUrl, + oauthToken, + cfg.http_timeout_ms, + ), + 'fetchRemoteCredentials (proactive)', + cfg, + ) + if (!fresh || tornDown) return + await rebuildTransport(fresh, 'proactive_refresh') + logForDebugging( + '[remote-bridge] Transport rebuilt (proactive refresh)', + ) + } catch (err) { + logForDebugging( + `[remote-bridge] Proactive refresh rebuild failed: ${errorMessage(err)}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII( + 'error', + 'bridge_repl_v2_proactive_refresh_failed', + ) + if (!tornDown) { + onStateChange?.('failed', `Refresh failed: ${errorMessage(err)}`) + } + } finally { + authRecoveryInFlight = false + } + })() + }, + label: 'remote', + }) + refresh.scheduleFromExpiresIn(sessionId, credentials.expires_in) + + // ── 6. Wire callbacks (extracted so transport-rebuild can re-wire) ────── + function wireTransportCallbacks(): void { + transport.setOnConnect(() => { + clearTimeout(connectDeadline) + logForDebugging('[remote-bridge] v2 transport connected') + logForDiagnosticsNoPII('info', 'bridge_repl_v2_transport_connected') + logEvent('tengu_bridge_repl_ws_connected', { + v2: true, + cause: + connectCause as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + if (!initialFlushDone && initialMessages && initialMessages.length > 0) { + initialFlushDone = true + // Capture current transport — if 401/teardown happens mid-flush, + // the stale .finally() must not drain the gate or signal connected. + // (Same guard pattern as replBridge.ts:1119.) + const flushTransport = transport + void flushHistory(initialMessages) + .catch(e => + logForDebugging(`[remote-bridge] flushHistory failed: ${e}`), + ) + .finally(() => { + // authRecoveryInFlight catches the v1-vs-v2 asymmetry: v1 nulls + // transport synchronously in setOnClose (replBridge.ts:1175), so + // transport !== flushTransport trips immediately. v2 doesn't null — + // transport reassigned only at rebuildTransport:346, 3 awaits deep. + // authRecoveryInFlight is set synchronously at rebuildTransport entry. + if ( + transport !== flushTransport || + tornDown || + authRecoveryInFlight + ) { + return + } + drainFlushGate() + onStateChange?.('connected') + }) + } else if (!flushGate.active) { + onStateChange?.('connected') + } + }) + + transport.setOnData((data: string) => { + handleIngressMessage( + data, + recentPostedUUIDs, + recentInboundUUIDs, + onInboundMessage, + // Remote client answered the permission prompt — the turn resumes. + // Without this the server stays on requires_action until the next + // user message or turn-end result. + onPermissionResponse + ? res => { + transport.reportState('running') + onPermissionResponse(res) + } + : undefined, + req => + handleServerControlRequest(req, { + transport, + sessionId, + onInterrupt, + onSetModel, + onSetMaxThinkingTokens, + onSetPermissionMode, + outboundOnly, + }), + ) + }) + + transport.setOnClose((code?: number) => { + clearTimeout(connectDeadline) + if (tornDown) return + logForDebugging(`[remote-bridge] v2 transport closed (code=${code})`) + logEvent('tengu_bridge_repl_ws_closed', { code, v2: true }) + // onClose fires only for TERMINAL failures: 401 (JWT invalid), + // 4090 (CCR epoch mismatch), 4091 (CCR init failed), or SSE 10-min + // reconnect budget exhausted. Transient disconnects are handled + // transparently inside SSETransport. 401 we can recover from (fetch + // fresh JWT, rebuild transport); all other codes are dead-ends. + if (code === 401 && !authRecoveryInFlight) { + void recoverFromAuthFailure() + return + } + onStateChange?.('failed', `Transport closed (code ${code})`) + }) + } + + // ── 7. Transport rebuild (shared by proactive refresh + 401 recovery) ── + // Every /bridge call bumps epoch server-side. Both refresh paths must + // rebuild the transport with the new epoch — a JWT-only swap leaves the + // old CCRClient heartbeating stale epoch → 409. SSE resumes from the old + // transport's high-water-mark seq-num so no server-side replay. + // Caller MUST set authRecoveryInFlight = true before calling (synchronously, + // before any await) and clear it in a finally. This function doesn't manage + // the flag — moving it here would be too late to prevent a double /bridge + // fetch, and each fetch bumps epoch. + async function rebuildTransport( + fresh: RemoteCredentials, + cause: Exclude, + ): Promise { + connectCause = cause + // Queue writes during rebuild — once /bridge returns, the old transport's + // epoch is stale and its next write/heartbeat 409s. Without this gate, + // writeMessages adds UUIDs to recentPostedUUIDs then writeBatch silently + // no-ops (closed uploader after 409) → permanent silent message loss. + flushGate.start() + try { + const seq = transport.getLastSequenceNum() + transport.close() + transport = await createV2ReplTransport({ + sessionUrl: buildCCRv2SdkUrl(fresh.api_base_url, sessionId), + ingressToken: fresh.worker_jwt, + sessionId, + epoch: fresh.worker_epoch, + heartbeatIntervalMs: cfg.heartbeat_interval_ms, + heartbeatJitterFraction: cfg.heartbeat_jitter_fraction, + initialSequenceNum: seq, + getAuthToken: () => fresh.worker_jwt, + outboundOnly, + }) + if (tornDown) { + // Teardown fired during the async createV2ReplTransport window. + // Don't wire/connect/schedule — we'd re-arm timers after cancelAll() + // and fire onInboundMessage into a torn-down bridge. + transport.close() + return + } + wireTransportCallbacks() + transport.connect() + connectDeadline = setTimeout( + onConnectTimeout, + cfg.connect_timeout_ms, + connectCause, + ) + refresh.scheduleFromExpiresIn(sessionId, fresh.expires_in) + // Drain queued writes into the new uploader. Runs before + // ccr.initialize() resolves (transport.connect() is fire-and-forget), + // but the uploader serializes behind the initial PUT /worker. If + // init fails (4091), events drop — but only recentPostedUUIDs + // (per-instance) is populated, so re-enabling the bridge re-flushes. + drainFlushGate() + } finally { + // End the gate on failure paths too — drainFlushGate already ended + // it on success. Queued messages are dropped (transport still dead). + flushGate.drop() + } + } + + // ── 8. 401 recovery (OAuth refresh + rebuild) ─────────────────────────── + async function recoverFromAuthFailure(): Promise { + // setOnClose already guards `!authRecoveryInFlight` but that check and + // this set must be atomic against onRefresh — claim synchronously before + // any await. Laptop wake fires both paths ~simultaneously. + if (authRecoveryInFlight) return + authRecoveryInFlight = true + onStateChange?.('reconnecting', 'JWT expired — refreshing') + logForDebugging('[remote-bridge] 401 on SSE — attempting JWT refresh') + try { + // Unconditionally try OAuth refresh — getAccessToken() returns expired + // tokens as non-null strings, so !oauthToken doesn't catch expiry. + // Pass the stale token so handleOAuth401Error's keychain-comparison + // can detect if another tab already refreshed. + const stale = getAccessToken() + if (onAuth401) await onAuth401(stale ?? '') + const oauthToken = getAccessToken() ?? stale + if (!oauthToken || tornDown) { + if (!tornDown) { + onStateChange?.('failed', 'JWT refresh failed: no OAuth token') + } + return + } + + const fresh = await withRetry( + () => + fetchRemoteCredentials( + sessionId, + baseUrl, + oauthToken, + cfg.http_timeout_ms, + ), + 'fetchRemoteCredentials (recovery)', + cfg, + ) + if (!fresh || tornDown) { + if (!tornDown) { + onStateChange?.('failed', 'JWT refresh failed after 401') + } + return + } + // If 401 interrupted the initial flush, writeBatch may have silently + // no-op'd on the closed uploader (ccr.close() ran in the SSE wrapper + // before our setOnClose callback). Reset so the new onConnect re-flushes. + // (v1 scopes initialFlushDone inside the per-transport closure at + // replBridge.ts:1027 so it resets naturally; v2 has it at outer scope.) + initialFlushDone = false + await rebuildTransport(fresh, 'auth_401_recovery') + logForDebugging('[remote-bridge] Transport rebuilt after 401') + } catch (err) { + logForDebugging( + `[remote-bridge] 401 recovery failed: ${errorMessage(err)}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'bridge_repl_v2_jwt_refresh_failed') + if (!tornDown) { + onStateChange?.('failed', `JWT refresh failed: ${errorMessage(err)}`) + } + } finally { + authRecoveryInFlight = false + } + } + + wireTransportCallbacks() + + // Start flushGate BEFORE connect so writeMessages() during handshake + // queues instead of racing the history POST. + if (initialMessages && initialMessages.length > 0) { + flushGate.start() + } + transport.connect() + connectDeadline = setTimeout( + onConnectTimeout, + cfg.connect_timeout_ms, + connectCause, + ) + + // ── 8. History flush + drain helpers ──────────────────────────────────── + function drainFlushGate(): void { + const msgs = flushGate.end() + if (msgs.length === 0) return + for (const msg of msgs) recentPostedUUIDs.add(msg.uuid) + const events = toSDKMessages(msgs).map(m => ({ + ...m, + session_id: sessionId, + })) + if (msgs.some(m => m.type === 'user')) { + transport.reportState('running') + } + logForDebugging( + `[remote-bridge] Drained ${msgs.length} queued message(s) after flush`, + ) + void transport.writeBatch(events) + } + + async function flushHistory(msgs: Message[]): Promise { + // v2 always creates a fresh server session (unconditional createCodeSession + // above) — no session reuse, no double-post risk. Unlike v1, we do NOT + // filter by previouslyFlushedUUIDs: that set persists across REPL enable/ + // disable cycles (useRef), so it would wrongly suppress history on re-enable. + const eligible = msgs.filter(isEligibleBridgeMessage) + const capped = + initialHistoryCap > 0 && eligible.length > initialHistoryCap + ? eligible.slice(-initialHistoryCap) + : eligible + if (capped.length < eligible.length) { + logForDebugging( + `[remote-bridge] Capped initial flush: ${eligible.length} -> ${capped.length} (cap=${initialHistoryCap})`, + ) + } + const events = toSDKMessages(capped).map(m => ({ + ...m, + session_id: sessionId, + })) + if (events.length === 0) return + // Mid-turn init: if Remote Control is enabled while a query is running, + // the last eligible message is a user prompt or tool_result (both 'user' + // type). Without this the init PUT's 'idle' sticks until the next user- + // type message forwards via writeMessages — which for a pure-text turn + // is never (only assistant chunks stream post-init). Check eligible (pre- + // cap), not capped: the cap may truncate to a user message even when the + // actual trailing message is assistant. + if (eligible.at(-1)?.type === 'user') { + transport.reportState('running') + } + logForDebugging(`[remote-bridge] Flushing ${events.length} history events`) + await transport.writeBatch(events) + } + + // ── 9. Teardown ─────────────────────────────────────────────────────────── + // On SIGINT/SIGTERM/⁠/exit, gracefulShutdown races runCleanupFunctions() + // against a 2s cap before forceExit kills the process. Budget accordingly: + // - archive: teardown_archive_timeout_ms (default 1500, cap 2000) + // - result write: fire-and-forget, archive latency covers the drain + // - 401 retry: only if first archive 401s, shares the same budget + async function teardown(): Promise { + if (tornDown) return + tornDown = true + refresh.cancelAll() + clearTimeout(connectDeadline) + flushGate.drop() + + // Fire the result message before archive — transport.write() only awaits + // enqueue (SerialBatchEventUploader resolves once buffered, drain is + // async). Archiving before close() gives the uploader's drain loop a + // window (typical archive ≈ 100-500ms) to POST the result without an + // explicit sleep. close() sets closed=true which interrupts drain at the + // next while-check, so close-before-archive drops the result. + transport.reportState('idle') + void transport.write(makeResultMessage(sessionId)) + + let token = getAccessToken() + let status = await archiveSession( + sessionId, + baseUrl, + token, + orgUUID, + cfg.teardown_archive_timeout_ms, + ) + + // Token is usually fresh (refresh scheduler runs 5min before expiry) but + // laptop-wake past the refresh window leaves getAccessToken() returning a + // stale string. Retry once on 401 — onAuth401 (= handleOAuth401Error) + // clears keychain cache + force-refreshes. No proactive refresh on the + // happy path: handleOAuth401Error force-refreshes even valid tokens, + // which would waste budget 99% of the time. try/catch mirrors + // recoverFromAuthFailure: keychain reads can throw (macOS locked after + // wake); an uncaught throw here would skip transport.close + telemetry. + if (status === 401 && onAuth401) { + try { + await onAuth401(token ?? '') + token = getAccessToken() + status = await archiveSession( + sessionId, + baseUrl, + token, + orgUUID, + cfg.teardown_archive_timeout_ms, + ) + } catch (err) { + logForDebugging( + `[remote-bridge] Teardown 401 retry threw: ${errorMessage(err)}`, + { level: 'error' }, + ) + } + } + + transport.close() + + const archiveStatus: ArchiveTelemetryStatus = + status === 'no_token' + ? 'skipped_no_token' + : status === 'timeout' || status === 'error' + ? 'network_error' + : status >= 500 + ? 'server_5xx' + : status >= 400 + ? 'server_4xx' + : 'ok' + + logForDebugging(`[remote-bridge] Torn down (archive=${status})`) + logForDiagnosticsNoPII('info', 'bridge_repl_v2_teardown') + logEvent( + feature('CCR_MIRROR') && outboundOnly + ? 'tengu_ccr_mirror_teardown' + : 'tengu_bridge_repl_teardown', + { + v2: true, + archive_status: + archiveStatus as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + archive_ok: typeof status === 'number' && status < 400, + archive_http_status: typeof status === 'number' ? status : undefined, + archive_timeout: status === 'timeout', + archive_no_token: status === 'no_token', + }, + ) + } + const unregister = registerCleanup(teardown) + + if (feature('CCR_MIRROR') && outboundOnly) { + logEvent('tengu_ccr_mirror_started', { + v2: true, + expires_in_s: credentials.expires_in, + }) + } else { + logEvent('tengu_bridge_repl_started', { + has_initial_messages: !!(initialMessages && initialMessages.length > 0), + v2: true, + expires_in_s: credentials.expires_in, + inProtectedNamespace: isInProtectedNamespace(), + }) + } + + // ── 10. Handle ────────────────────────────────────────────────────────── + return { + bridgeSessionId: sessionId, + environmentId: '', + sessionIngressUrl: credentials.api_base_url, + writeMessages(messages) { + const filtered = messages.filter( + m => + isEligibleBridgeMessage(m) && + !initialMessageUUIDs.has(m.uuid) && + !recentPostedUUIDs.has(m.uuid), + ) + if (filtered.length === 0) return + + // Fire onUserMessage for title derivation. Scan before the flushGate + // check — prompts are title-worthy even if they queue. Keeps calling + // on every title-worthy message until the callback returns true; the + // caller owns the policy (derive at 1st and 3rd, skip if explicit). + if (!userMessageCallbackDone) { + for (const m of filtered) { + const text = extractTitleText(m) + if (text !== undefined && onUserMessage?.(text, sessionId)) { + userMessageCallbackDone = true + break + } + } + } + + if (flushGate.enqueue(...filtered)) { + logForDebugging( + `[remote-bridge] Queued ${filtered.length} message(s) during flush`, + ) + return + } + + for (const msg of filtered) recentPostedUUIDs.add(msg.uuid) + const events = toSDKMessages(filtered).map(m => ({ + ...m, + session_id: sessionId, + })) + // v2 does not derive worker_status from events server-side (unlike v1 + // session-ingress session_status_updater.go). Push it from here so the + // CCR web session list shows Running instead of stuck on Idle. A user + // message in the batch marks turn start. CCRClient.reportState dedupes + // consecutive same-state pushes. + if (filtered.some(m => m.type === 'user')) { + transport.reportState('running') + } + logForDebugging(`[remote-bridge] Sending ${filtered.length} message(s)`) + void transport.writeBatch(events) + }, + writeSdkMessages(messages: SDKMessage[]) { + const filtered = messages.filter( + m => !m.uuid || !recentPostedUUIDs.has(m.uuid), + ) + if (filtered.length === 0) return + for (const msg of filtered) { + if (msg.uuid) recentPostedUUIDs.add(msg.uuid) + } + const events = filtered.map(m => ({ ...m, session_id: sessionId })) + void transport.writeBatch(events) + }, + sendControlRequest(request: SDKControlRequest) { + if (authRecoveryInFlight) { + logForDebugging( + `[remote-bridge] Dropping control_request during 401 recovery: ${request.request_id}`, + ) + return + } + const event = { ...request, session_id: sessionId } + if (request.request.subtype === 'can_use_tool') { + transport.reportState('requires_action') + } + void transport.write(event) + logForDebugging( + `[remote-bridge] Sent control_request request_id=${request.request_id}`, + ) + }, + sendControlResponse(response: SDKControlResponse) { + if (authRecoveryInFlight) { + logForDebugging( + '[remote-bridge] Dropping control_response during 401 recovery', + ) + return + } + const event = { ...response, session_id: sessionId } + transport.reportState('running') + void transport.write(event) + logForDebugging('[remote-bridge] Sent control_response') + }, + sendControlCancelRequest(requestId: string) { + if (authRecoveryInFlight) { + logForDebugging( + `[remote-bridge] Dropping control_cancel_request during 401 recovery: ${requestId}`, + ) + return + } + const event = { + type: 'control_cancel_request' as const, + request_id: requestId, + session_id: sessionId, + } + // Hook/classifier/channel/recheck resolved the permission locally — + // interactiveHandler calls only cancelRequest (no sendResponse) on + // those paths, so without this the server stays on requires_action. + transport.reportState('running') + void transport.write(event) + logForDebugging( + `[remote-bridge] Sent control_cancel_request request_id=${requestId}`, + ) + }, + sendResult() { + if (authRecoveryInFlight) { + logForDebugging('[remote-bridge] Dropping result during 401 recovery') + return + } + transport.reportState('idle') + void transport.write(makeResultMessage(sessionId)) + logForDebugging(`[remote-bridge] Sent result`) + }, + async teardown() { + unregister() + await teardown() + }, + } +} + +// ─── Session API (v2 /code/sessions, no env) ───────────────────────────────── + +/** Retry an async init call with exponential backoff + jitter. */ +async function withRetry( + fn: () => Promise, + label: string, + cfg: EnvLessBridgeConfig, +): Promise { + const max = cfg.init_retry_max_attempts + for (let attempt = 1; attempt <= max; attempt++) { + const result = await fn() + if (result !== null) return result + if (attempt < max) { + const base = cfg.init_retry_base_delay_ms * 2 ** (attempt - 1) + const jitter = + base * cfg.init_retry_jitter_fraction * (2 * Math.random() - 1) + const delay = Math.min(base + jitter, cfg.init_retry_max_delay_ms) + logForDebugging( + `[remote-bridge] ${label} failed (attempt ${attempt}/${max}), retrying in ${Math.round(delay)}ms`, + ) + await sleep(delay) + } + } + return null +} + +// Moved to codeSessionApi.ts so the SDK /bridge subpath can bundle them +// without pulling in this file's heavy CLI tree (analytics, transport). +export { + createCodeSession, + type RemoteCredentials, +} from './codeSessionApi.js' +import { + createCodeSession, + fetchRemoteCredentials as fetchRemoteCredentialsRaw, + type RemoteCredentials, +} from './codeSessionApi.js' +import { getBridgeBaseUrlOverride } from './bridgeConfig.js' + +// CLI-side wrapper that applies the CLAUDE_BRIDGE_BASE_URL dev override and +// injects the trusted-device token (both are env/GrowthBook reads that the +// SDK-facing codeSessionApi.ts export must stay free of). +export async function fetchRemoteCredentials( + sessionId: string, + baseUrl: string, + accessToken: string, + timeoutMs: number, +): Promise { + const creds = await fetchRemoteCredentialsRaw( + sessionId, + baseUrl, + accessToken, + timeoutMs, + getTrustedDeviceToken(), + ) + if (!creds) return null + return getBridgeBaseUrlOverride() + ? { ...creds, api_base_url: baseUrl } + : creds +} + +type ArchiveStatus = number | 'timeout' | 'error' | 'no_token' + +// Single categorical for BQ `GROUP BY archive_status`. The booleans on +// _teardown predate this and are redundant with it (except archive_timeout, +// which distinguishes ECONNABORTED from other network errors — both map to +// 'network_error' here since the dominant cause in a 1.5s window is timeout). +type ArchiveTelemetryStatus = + | 'ok' + | 'skipped_no_token' + | 'network_error' + | 'server_4xx' + | 'server_5xx' + +async function archiveSession( + sessionId: string, + baseUrl: string, + accessToken: string | undefined, + orgUUID: string, + timeoutMs: number, +): Promise { + if (!accessToken) return 'no_token' + // Archive lives at the compat layer (/v1/sessions/*, not /v1/code/sessions). + // compat.parseSessionID only accepts TagSession (session_*), so retag cse_*. + // anthropic-beta + x-organization-uuid are required — without them the + // compat gateway 404s before reaching the handler. + // + // Unlike bridgeMain.ts (which caches compatId in sessionCompatIds to keep + // in-memory titledSessions/logger keys consistent across a mid-session + // gate flip), this compatId is only a server URL path segment — no + // in-memory state. Fresh compute matches whatever the server currently + // validates: if the gate is OFF, the server has been updated to accept + // cse_* and we correctly send it. + const compatId = toCompatSessionId(sessionId) + try { + const response = await axios.post( + `${baseUrl}/v1/sessions/${compatId}/archive`, + {}, + { + headers: { + ...oauthHeaders(accessToken), + 'anthropic-beta': 'ccr-byoc-2025-07-29', + 'x-organization-uuid': orgUUID, + }, + timeout: timeoutMs, + validateStatus: () => true, + }, + ) + logForDebugging( + `[remote-bridge] Archive ${compatId} status=${response.status}`, + ) + return response.status + } catch (err) { + const msg = errorMessage(err) + logForDebugging(`[remote-bridge] Archive failed: ${msg}`) + return axios.isAxiosError(err) && err.code === 'ECONNABORTED' + ? 'timeout' + : 'error' + } +} diff --git a/bridge/replBridge.ts b/bridge/replBridge.ts new file mode 100644 index 0000000..7d7ac6a --- /dev/null +++ b/bridge/replBridge.ts @@ -0,0 +1,2406 @@ +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +import { randomUUID } from 'crypto' +import { + createBridgeApiClient, + BridgeFatalError, + isExpiredErrorType, + isSuppressible403, +} from './bridgeApi.js' +import type { BridgeConfig, BridgeApiClient } from './types.js' +import { logForDebugging } from '../utils/debug.js' +import { logForDiagnosticsNoPII } from '../utils/diagLogs.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { registerCleanup } from '../utils/cleanupRegistry.js' +import { + handleIngressMessage, + handleServerControlRequest, + makeResultMessage, + isEligibleBridgeMessage, + extractTitleText, + BoundedUUIDSet, +} from './bridgeMessaging.js' +import { + decodeWorkSecret, + buildSdkUrl, + buildCCRv2SdkUrl, + sameSessionId, +} from './workSecret.js' +import { toCompatSessionId, toInfraSessionId } from './sessionIdCompat.js' +import { updateSessionBridgeId } from '../utils/concurrentSessions.js' +import { getTrustedDeviceToken } from './trustedDevice.js' +import { HybridTransport } from '../cli/transports/HybridTransport.js' +import { + type ReplBridgeTransport, + createV1ReplTransport, + createV2ReplTransport, +} from './replBridgeTransport.js' +import { updateSessionIngressAuthToken } from '../utils/sessionIngressAuth.js' +import { isEnvTruthy, isInProtectedNamespace } from '../utils/envUtils.js' +import { validateBridgeId } from './bridgeApi.js' +import { + describeAxiosError, + extractHttpStatus, + logBridgeSkip, +} from './debugUtils.js' +import type { Message } from '../types/message.js' +import type { SDKMessage } from '../entrypoints/agentSdkTypes.js' +import type { PermissionMode } from '../utils/permissions/PermissionMode.js' +import type { + SDKControlRequest, + SDKControlResponse, +} from '../entrypoints/sdk/controlTypes.js' +import { createCapacityWake, type CapacitySignal } from './capacityWake.js' +import { FlushGate } from './flushGate.js' +import { + DEFAULT_POLL_CONFIG, + type PollIntervalConfig, +} from './pollConfigDefaults.js' +import { errorMessage } from '../utils/errors.js' +import { sleep } from '../utils/sleep.js' +import { + wrapApiForFaultInjection, + registerBridgeDebugHandle, + clearBridgeDebugHandle, + injectBridgeFault, +} from './bridgeDebug.js' + +export type ReplBridgeHandle = { + bridgeSessionId: string + environmentId: string + sessionIngressUrl: string + writeMessages(messages: Message[]): void + writeSdkMessages(messages: SDKMessage[]): void + sendControlRequest(request: SDKControlRequest): void + sendControlResponse(response: SDKControlResponse): void + sendControlCancelRequest(requestId: string): void + sendResult(): void + teardown(): Promise +} + +export type BridgeState = 'ready' | 'connected' | 'reconnecting' | 'failed' + +/** + * Explicit-param input to initBridgeCore. Everything initReplBridge reads + * from bootstrap state (cwd, session ID, git, OAuth) becomes a field here. + * A daemon caller (Agent SDK, PR 4) that never runs main.tsx fills these + * in itself. + */ +export type BridgeCoreParams = { + dir: string + machineName: string + branch: string + gitRepoUrl: string | null + title: string + baseUrl: string + sessionIngressUrl: string + /** + * Opaque string sent as metadata.worker_type. Use BridgeWorkerType for + * the two CLI-originated values; daemon callers may send any string the + * backend recognizes (it's just a filter key on the web side). + */ + workerType: string + getAccessToken: () => string | undefined + /** + * POST /v1/sessions. Injected because `createSession.ts` lazy-loads + * `auth.ts`/`model.ts`/`oauth/client.ts` and `bun --outfile` inlines + * dynamic imports — the lazy-load doesn't help, the whole REPL tree ends + * up in the Agent SDK bundle. + * + * REPL wrapper passes `createBridgeSession` from `createSession.ts`. + * Daemon wrapper passes `createBridgeSessionLean` from `sessionApi.ts` + * (HTTP-only, orgUUID+model supplied by the daemon caller). + * + * Receives `gitRepoUrl`+`branch` so the REPL wrapper can build the git + * source/outcome for claude.ai's session card. Daemon ignores them. + */ + createSession: (opts: { + environmentId: string + title: string + gitRepoUrl: string | null + branch: string + signal: AbortSignal + }) => Promise + /** + * POST /v1/sessions/{id}/archive. Same injection rationale. Best-effort; + * the callback MUST NOT throw. + */ + archiveSession: (sessionId: string) => Promise + /** + * Invoked on reconnect-after-env-lost to refresh the title. REPL wrapper + * reads session storage (picks up /rename); daemon returns the static + * title. Defaults to () => title. + */ + getCurrentTitle?: () => string + /** + * Converts internal Message[] → SDKMessage[] for writeMessages() and the + * initial-flush/drain paths. REPL wrapper passes the real toSDKMessages + * from utils/messages/mappers.ts. Daemon callers that only use + * writeSdkMessages() and pass no initialMessages can omit this — those + * code paths are unreachable. + * + * Injected rather than imported because mappers.ts transitively pulls in + * src/commands.ts via messages.ts → api.ts → prompts.ts, dragging the + * entire command registry + React tree into the Agent SDK bundle. + */ + toSDKMessages?: (messages: Message[]) => SDKMessage[] + /** + * OAuth 401 refresh handler passed to createBridgeApiClient. REPL wrapper + * passes handleOAuth401Error; daemon passes its AuthManager's handler. + * Injected because utils/auth.ts transitively pulls in the command + * registry via config.ts → file.ts → permissions/filesystem.ts → + * sessionStorage.ts → commands.ts. + */ + onAuth401?: (staleAccessToken: string) => Promise + /** + * Poll interval config getter for the work-poll heartbeat loop. REPL + * wrapper passes the GrowthBook-backed getPollIntervalConfig (allows ops + * to live-tune poll rates fleet-wide). Daemon passes a static config + * with a 60s heartbeat (5× headroom under the 300s work-lease TTL). + * Injected because growthbook.ts transitively pulls in the command + * registry via the same config.ts chain. + */ + getPollIntervalConfig?: () => PollIntervalConfig + /** + * Max initial messages to replay on connect. REPL wrapper reads from the + * tengu_bridge_initial_history_cap GrowthBook flag. Daemon passes no + * initialMessages so this is never read. Default 200 matches the flag + * default. + */ + initialHistoryCap?: number + // Same REPL-flush machinery as InitBridgeOptions — daemon omits these. + initialMessages?: Message[] + previouslyFlushedUUIDs?: Set + onInboundMessage?: (msg: SDKMessage) => void + onPermissionResponse?: (response: SDKControlResponse) => void + onInterrupt?: () => void + onSetModel?: (model: string | undefined) => void + onSetMaxThinkingTokens?: (maxTokens: number | null) => void + /** + * Returns a policy verdict so this module can emit an error control_response + * without importing the policy checks itself (bootstrap-isolation constraint). + * The callback must guard `auto` (isAutoModeGateEnabled) and + * `bypassPermissions` (isBypassPermissionsModeDisabled AND + * isBypassPermissionsModeAvailable) BEFORE calling transitionPermissionMode — + * that function's internal auto-gate check is a defensive throw, not a + * graceful guard, and its side-effect order is setAutoModeActive(true) then + * throw, which corrupts the 3-way invariant documented in src/CLAUDE.md if + * the callback lets the throw escape here. + */ + onSetPermissionMode?: ( + mode: PermissionMode, + ) => { ok: true } | { ok: false; error: string } + onStateChange?: (state: BridgeState, detail?: string) => void + /** + * Fires on each real user message to flow through writeMessages() until + * the callback returns true (done). Mirrors remoteBridgeCore.ts's + * onUserMessage so the REPL bridge can derive a session title from early + * prompts when none was set at init time (e.g. user runs /remote-control + * on an empty conversation, then types). Tool-result wrappers, meta + * messages, and display-tag-only messages are skipped. Receives + * currentSessionId so the wrapper can PATCH the title without a closure + * dance to reach the not-yet-returned handle. The caller owns the + * derive-at-count-1-and-3 policy; the transport just keeps calling until + * told to stop. Not fired for the writeSdkMessages daemon path (daemon + * sets its own title at init). Distinct from SessionSpawnOpts's + * onFirstUserMessage (spawn-bridge, PR #21250), which stays fire-once. + */ + onUserMessage?: (text: string, sessionId: string) => boolean + /** See InitBridgeOptions.perpetual. */ + perpetual?: boolean + /** + * Seeds lastTransportSequenceNum — the SSE event-stream high-water mark + * that's carried across transport swaps within one process. Daemon callers + * pass the value they persisted at shutdown so the FIRST SSE connect of a + * fresh process sends from_sequence_num and the server doesn't replay full + * history. REPL callers omit (fresh session each run → 0 is correct). + */ + initialSSESequenceNum?: number +} + +/** + * Superset of ReplBridgeHandle. Adds getSSESequenceNum for daemon callers + * that persist the SSE seq-num across process restarts and pass it back as + * initialSSESequenceNum on the next start. + */ +export type BridgeCoreHandle = ReplBridgeHandle & { + /** + * Current SSE sequence-number high-water mark. Updates as transports + * swap. Daemon callers persist this on shutdown and pass it back as + * initialSSESequenceNum on next start. + */ + getSSESequenceNum(): number +} + +/** + * Poll error recovery constants. When the work poll starts failing (e.g. + * server 500s), we use exponential backoff and give up after this timeout. + * This is deliberately long — the server is the authority on when a session + * is truly dead. As long as the server accepts our poll, we keep waiting + * for it to re-dispatch the work item. + */ +const POLL_ERROR_INITIAL_DELAY_MS = 2_000 +const POLL_ERROR_MAX_DELAY_MS = 60_000 +const POLL_ERROR_GIVE_UP_MS = 15 * 60 * 1000 + +// Monotonically increasing counter for distinguishing init calls in logs +let initSequence = 0 + +/** + * Bootstrap-free core: env registration → session creation → poll loop → + * ingress WS → teardown. Reads nothing from bootstrap/state or + * sessionStorage — all context comes from params. Caller (initReplBridge + * below, or a daemon in PR 4) has already passed entitlement gates and + * gathered git/auth/title. + * + * Returns null on registration or session-creation failure. + */ +export async function initBridgeCore( + params: BridgeCoreParams, +): Promise { + const { + dir, + machineName, + branch, + gitRepoUrl, + title, + baseUrl, + sessionIngressUrl, + workerType, + getAccessToken, + createSession, + archiveSession, + getCurrentTitle = () => title, + toSDKMessages = () => { + throw new Error( + 'BridgeCoreParams.toSDKMessages not provided. Pass it if you use writeMessages() or initialMessages — daemon callers that only use writeSdkMessages() never hit this path.', + ) + }, + onAuth401, + getPollIntervalConfig = () => DEFAULT_POLL_CONFIG, + initialHistoryCap = 200, + initialMessages, + previouslyFlushedUUIDs, + onInboundMessage, + onPermissionResponse, + onInterrupt, + onSetModel, + onSetMaxThinkingTokens, + onSetPermissionMode, + onStateChange, + onUserMessage, + perpetual, + initialSSESequenceNum = 0, + } = params + + const seq = ++initSequence + + // bridgePointer import hoisted: perpetual mode reads it before register; + // non-perpetual writes it after session create; both use clear at teardown. + const { writeBridgePointer, clearBridgePointer, readBridgePointer } = + await import('./bridgePointer.js') + + // Perpetual mode: read the crash-recovery pointer and treat it as prior + // state. The pointer is written unconditionally after session create + // (crash-recovery for all sessions); perpetual mode just skips the + // teardown clear so it survives clean exits too. Only reuse 'repl' + // pointers — a crashed standalone bridge (`claude remote-control`) + // writes source:'standalone' with a different workerType. + const rawPrior = perpetual ? await readBridgePointer(dir) : null + const prior = rawPrior?.source === 'repl' ? rawPrior : null + + logForDebugging( + `[bridge:repl] initBridgeCore #${seq} starting (initialMessages=${initialMessages?.length ?? 0}${prior ? ` perpetual prior=env:${prior.environmentId}` : ''})`, + ) + + // 5. Register bridge environment + const rawApi = createBridgeApiClient({ + baseUrl, + getAccessToken, + runnerVersion: MACRO.VERSION, + onDebug: logForDebugging, + onAuth401, + getTrustedDeviceToken, + }) + // Ant-only: interpose so /bridge-kick can inject poll/register/heartbeat + // failures. Zero cost in external builds (rawApi passes through unchanged). + const api = + process.env.USER_TYPE === 'ant' ? wrapApiForFaultInjection(rawApi) : rawApi + + const bridgeConfig: BridgeConfig = { + dir, + machineName, + branch, + gitRepoUrl, + maxSessions: 1, + spawnMode: 'single-session', + verbose: false, + sandbox: false, + bridgeId: randomUUID(), + workerType, + environmentId: randomUUID(), + reuseEnvironmentId: prior?.environmentId, + apiBaseUrl: baseUrl, + sessionIngressUrl, + } + + let environmentId: string + let environmentSecret: string + try { + const reg = await api.registerBridgeEnvironment(bridgeConfig) + environmentId = reg.environment_id + environmentSecret = reg.environment_secret + } catch (err) { + logBridgeSkip( + 'registration_failed', + `[bridge:repl] Environment registration failed: ${errorMessage(err)}`, + ) + // Stale pointer may be the cause (expired/deleted env) — clear it so + // the next start doesn't retry the same dead ID. + if (prior) { + await clearBridgePointer(dir) + } + onStateChange?.('failed', errorMessage(err)) + return null + } + + logForDebugging(`[bridge:repl] Environment registered: ${environmentId}`) + logForDiagnosticsNoPII('info', 'bridge_repl_env_registered') + logEvent('tengu_bridge_repl_env_registered', {}) + + /** + * Reconnect-in-place: if the just-registered environmentId matches what + * was requested, call reconnectSession to force-stop stale workers and + * re-queue the session. Used at init (perpetual mode — env is alive but + * idle after clean teardown) and in doReconnect() Strategy 1 (env lost + * then resurrected). Returns true on success; caller falls back to + * fresh session creation on false. + */ + async function tryReconnectInPlace( + requestedEnvId: string, + sessionId: string, + ): Promise { + if (environmentId !== requestedEnvId) { + logForDebugging( + `[bridge:repl] Env mismatch (requested ${requestedEnvId}, got ${environmentId}) — cannot reconnect in place`, + ) + return false + } + // The pointer stores what createBridgeSession returned (session_*, + // compat/convert.go:41). /bridge/reconnect is an environments-layer + // endpoint — once the server's ccr_v2_compat_enabled gate is on it + // looks sessions up by their infra tag (cse_*) and returns "Session + // not found" for the session_* costume. We don't know the gate state + // pre-poll, so try both; the re-tag is a no-op if the ID is already + // cse_* (doReconnect Strategy 1 path — currentSessionId never mutates + // to cse_* but future-proof the check). + const infraId = toInfraSessionId(sessionId) + const candidates = + infraId === sessionId ? [sessionId] : [sessionId, infraId] + for (const id of candidates) { + try { + await api.reconnectSession(environmentId, id) + logForDebugging( + `[bridge:repl] Reconnected session ${id} in place on env ${environmentId}`, + ) + return true + } catch (err) { + logForDebugging( + `[bridge:repl] reconnectSession(${id}) failed: ${errorMessage(err)}`, + ) + } + } + logForDebugging( + '[bridge:repl] reconnectSession exhausted — falling through to fresh session', + ) + return false + } + + // Perpetual init: env is alive but has no queued work after clean + // teardown. reconnectSession re-queues it. doReconnect() has the same + // call but only fires on poll 404 (env dead); + // here the env is alive but idle. + const reusedPriorSession = prior + ? await tryReconnectInPlace(prior.environmentId, prior.sessionId) + : false + if (prior && !reusedPriorSession) { + await clearBridgePointer(dir) + } + + // 6. Create session on the bridge. Initial messages are NOT included as + // session creation events because those use STREAM_ONLY persistence and + // are published before the CCR UI subscribes, so they get lost. Instead, + // initial messages are flushed via the ingress WebSocket once it connects. + + // Mutable session ID — updated when the environment+session pair is + // re-created after a connection loss. + let currentSessionId: string + + + if (reusedPriorSession && prior) { + currentSessionId = prior.sessionId + logForDebugging( + `[bridge:repl] Perpetual session reused: ${currentSessionId}`, + ) + // Server already has all initialMessages from the prior CLI run. Mark + // them as previously-flushed so the initial flush filter excludes them + // (previouslyFlushedUUIDs is a fresh Set on every CLI start). Duplicate + // UUIDs cause the server to kill the WebSocket. + if (initialMessages && previouslyFlushedUUIDs) { + for (const msg of initialMessages) { + previouslyFlushedUUIDs.add(msg.uuid) + } + } + } else { + const createdSessionId = await createSession({ + environmentId, + title, + gitRepoUrl, + branch, + signal: AbortSignal.timeout(15_000), + }) + + if (!createdSessionId) { + logForDebugging( + '[bridge:repl] Session creation failed, deregistering environment', + ) + logEvent('tengu_bridge_repl_session_failed', {}) + await api.deregisterEnvironment(environmentId).catch(() => {}) + onStateChange?.('failed', 'Session creation failed') + return null + } + + currentSessionId = createdSessionId + logForDebugging(`[bridge:repl] Session created: ${currentSessionId}`) + } + + // Crash-recovery pointer: written now so a kill -9 at any point after + // this leaves a recoverable trail. Cleared in teardown (non-perpetual) + // or left alone (perpetual mode — pointer survives clean exit too). + // `claude remote-control --continue` from the same directory will detect + // it and offer to resume. + await writeBridgePointer(dir, { + sessionId: currentSessionId, + environmentId, + source: 'repl', + }) + logForDiagnosticsNoPII('info', 'bridge_repl_session_created') + logEvent('tengu_bridge_repl_started', { + has_initial_messages: !!(initialMessages && initialMessages.length > 0), + inProtectedNamespace: isInProtectedNamespace(), + }) + + // UUIDs of initial messages. Used for dedup in writeMessages to avoid + // re-sending messages that were already flushed on WebSocket open. + const initialMessageUUIDs = new Set() + if (initialMessages) { + for (const msg of initialMessages) { + initialMessageUUIDs.add(msg.uuid) + } + } + + // Bounded ring buffer of UUIDs for messages we've already sent to the + // server via the ingress WebSocket. Serves two purposes: + // 1. Echo filtering — ignore our own messages bouncing back on the WS. + // 2. Secondary dedup in writeMessages — catch race conditions where + // the hook's index-based tracking isn't sufficient. + // + // Seeded with initialMessageUUIDs so that when the server echoes back + // the initial conversation context over the ingress WebSocket, those + // messages are recognized as echoes and not re-injected into the REPL. + // + // Capacity of 2000 covers well over any realistic echo window (echoes + // arrive within milliseconds) and any messages that might be re-encountered + // after compaction. The hook's lastWrittenIndexRef is the primary dedup; + // this is a safety net. + const recentPostedUUIDs = new BoundedUUIDSet(2000) + for (const uuid of initialMessageUUIDs) { + recentPostedUUIDs.add(uuid) + } + + // Bounded set of INBOUND prompt UUIDs we've already forwarded to the REPL. + // Defensive dedup for when the server re-delivers prompts (seq-num + // negotiation failure, server edge cases, transport swap races). The + // seq-num carryover below is the primary fix; this is the safety net. + const recentInboundUUIDs = new BoundedUUIDSet(2000) + + // 7. Start poll loop for work items — this is what makes the session + // "live" on claude.ai. When a user types there, the backend dispatches + // a work item to our environment. We poll for it, get the ingress token, + // and connect the ingress WebSocket. + // + // The poll loop keeps running: when work arrives it connects the ingress + // WebSocket, and if the WebSocket drops unexpectedly (code != 1000) it + // resumes polling to get a fresh ingress token and reconnect. + const pollController = new AbortController() + // Adapter over either HybridTransport (v1: WS reads + POST writes to + // Session-Ingress) or SSETransport+CCRClient (v2: SSE reads + POST + // writes to CCR /worker/*). The v1/v2 choice is made in onWorkReceived: + // server-driven via secret.use_code_sessions, with CLAUDE_BRIDGE_USE_CCR_V2 + // as an ant-dev override. + let transport: ReplBridgeTransport | null = null + // Bumped on every onWorkReceived. Captured in createV2ReplTransport's .then() + // closure to detect stale resolutions: if two calls race while transport is + // null, both registerWorker() (bumping server epoch), and whichever resolves + // SECOND is the correct one — but the transport !== null check gets this + // backwards (first-to-resolve installs, second discards). The generation + // counter catches it independent of transport state. + let v2Generation = 0 + // SSE sequence-number high-water mark carried across transport swaps. + // Without this, each new SSETransport starts at 0, sends no + // from_sequence_num / Last-Event-ID on its first connect, and the server + // replays the entire session event history — every prompt ever sent + // re-delivered as fresh inbound messages on every onWorkReceived. + // + // Seed only when we actually reconnected the prior session. If + // `reusedPriorSession` is false we fell through to `createSession()` — + // the caller's persisted seq-num belongs to a dead session and applying + // it to the fresh stream (starting at 1) silently drops events. Same + // hazard as doReconnect Strategy 2; same fix as the reset there. + let lastTransportSequenceNum = reusedPriorSession ? initialSSESequenceNum : 0 + // Track the current work ID so teardown can call stopWork + let currentWorkId: string | null = null + // Session ingress JWT for the current work item — used for heartbeat auth. + let currentIngressToken: string | null = null + // Signal to wake the at-capacity sleep early when the transport is lost, + // so the poll loop immediately switches back to fast polling for new work. + const capacityWake = createCapacityWake(pollController.signal) + const wakePollLoop = capacityWake.wake + const capacitySignal = capacityWake.signal + // Gates message writes during the initial flush to prevent ordering + // races where new messages arrive at the server interleaved with history. + const flushGate = new FlushGate() + + // Latch for onUserMessage — flips true when the callback returns true + // (policy says "done deriving"). If no callback, skip scanning entirely + // (daemon path — no title derivation needed). + let userMessageCallbackDone = !onUserMessage + + // Shared counter for environment re-creations, used by both + // onEnvironmentLost and the abnormal-close handler. + const MAX_ENVIRONMENT_RECREATIONS = 3 + let environmentRecreations = 0 + let reconnectPromise: Promise | null = null + + /** + * Recover from onEnvironmentLost (poll returned 404 — env was reaped + * server-side). Tries two strategies in order: + * + * 1. Reconnect-in-place: idempotent re-register with reuseEnvironmentId + * → if the backend returns the same env ID, call reconnectSession() + * to re-queue the existing session. currentSessionId stays the same; + * the URL on the user's phone stays valid; previouslyFlushedUUIDs is + * preserved so history isn't re-sent. + * + * 2. Fresh session fallback: if the backend returns a different env ID + * (original TTL-expired, e.g. laptop slept >4h) or reconnectSession() + * throws, archive the old session and create a new one on the + * now-registered env. Old behavior before #20460 primitives landed. + * + * Uses a promise-based reentrancy guard so concurrent callers share the + * same reconnection attempt. + */ + async function reconnectEnvironmentWithSession(): Promise { + if (reconnectPromise) { + return reconnectPromise + } + reconnectPromise = doReconnect() + try { + return await reconnectPromise + } finally { + reconnectPromise = null + } + } + + async function doReconnect(): Promise { + environmentRecreations++ + // Invalidate any in-flight v2 handshake — the environment is being + // recreated, so a stale transport arriving post-reconnect would be + // pointed at a dead session. + v2Generation++ + logForDebugging( + `[bridge:repl] Reconnecting after env lost (attempt ${environmentRecreations}/${MAX_ENVIRONMENT_RECREATIONS})`, + ) + + if (environmentRecreations > MAX_ENVIRONMENT_RECREATIONS) { + logForDebugging( + `[bridge:repl] Environment reconnect limit reached (${MAX_ENVIRONMENT_RECREATIONS}), giving up`, + ) + return false + } + + // Close the stale transport. Capture seq BEFORE close — if Strategy 1 + // (tryReconnectInPlace) succeeds we keep the SAME session, and the + // next transport must resume where this one left off, not replay from + // the last transport-swap checkpoint. + if (transport) { + const seq = transport.getLastSequenceNum() + if (seq > lastTransportSequenceNum) { + lastTransportSequenceNum = seq + } + transport.close() + transport = null + } + // Transport is gone — wake the poll loop out of its at-capacity + // heartbeat sleep so it can fast-poll for re-dispatched work. + wakePollLoop() + // Reset flush gate so writeMessages() hits the !transport guard + // instead of silently queuing into a dead buffer. + flushGate.drop() + + // Release the current work item (force=false — we may want the session + // back). Best-effort: the env is probably gone, so this likely 404s. + if (currentWorkId) { + const workIdBeingCleared = currentWorkId + await api + .stopWork(environmentId, workIdBeingCleared, false) + .catch(() => {}) + // When doReconnect runs concurrently with the poll loop (ws_closed + // handler case — void-called, unlike the awaited onEnvironmentLost + // path), onWorkReceived can fire during the stopWork await and set + // a fresh currentWorkId. If it did, the poll loop has already + // recovered on its own — defer to it rather than proceeding to + // archiveSession, which would destroy the session its new + // transport is connected to. + if (currentWorkId !== workIdBeingCleared) { + logForDebugging( + '[bridge:repl] Poll loop recovered during stopWork await — deferring to it', + ) + environmentRecreations = 0 + return true + } + currentWorkId = null + currentIngressToken = null + } + + // Bail out if teardown started while we were awaiting + if (pollController.signal.aborted) { + logForDebugging('[bridge:repl] Reconnect aborted by teardown') + return false + } + + // Strategy 1: idempotent re-register with the server-issued env ID. + // If the backend resurrects the same env (fresh secret), we can + // reconnect the existing session. If it hands back a different ID, the + // original env is truly gone and we fall through to a fresh session. + const requestedEnvId = environmentId + bridgeConfig.reuseEnvironmentId = requestedEnvId + try { + const reg = await api.registerBridgeEnvironment(bridgeConfig) + environmentId = reg.environment_id + environmentSecret = reg.environment_secret + } catch (err) { + bridgeConfig.reuseEnvironmentId = undefined + logForDebugging( + `[bridge:repl] Environment re-registration failed: ${errorMessage(err)}`, + ) + return false + } + // Clear before any await — a stale value would poison the next fresh + // registration if doReconnect runs again. + bridgeConfig.reuseEnvironmentId = undefined + + logForDebugging( + `[bridge:repl] Re-registered: requested=${requestedEnvId} got=${environmentId}`, + ) + + // Bail out if teardown started while we were registering + if (pollController.signal.aborted) { + logForDebugging( + '[bridge:repl] Reconnect aborted after env registration, cleaning up', + ) + await api.deregisterEnvironment(environmentId).catch(() => {}) + return false + } + + // Same race as above, narrower window: poll loop may have set up a + // transport during the registerBridgeEnvironment await. Bail before + // tryReconnectInPlace/archiveSession kill it server-side. + if (transport !== null) { + logForDebugging( + '[bridge:repl] Poll loop recovered during registerBridgeEnvironment await — deferring to it', + ) + environmentRecreations = 0 + return true + } + + // Strategy 1: same helper as perpetual init. currentSessionId stays + // the same on success; URL on mobile/web stays valid; + // previouslyFlushedUUIDs preserved (no re-flush). + if (await tryReconnectInPlace(requestedEnvId, currentSessionId)) { + logEvent('tengu_bridge_repl_reconnected_in_place', {}) + environmentRecreations = 0 + return true + } + // Env differs → TTL-expired/reaped; or reconnect failed. + // Don't deregister — we have a fresh secret for this env either way. + if (environmentId !== requestedEnvId) { + logEvent('tengu_bridge_repl_env_expired_fresh_session', {}) + } + + // Strategy 2: fresh session on the now-registered environment. + // Archive the old session first — it's orphaned (bound to a dead env, + // or reconnectSession rejected it). Don't deregister the env — we just + // got a fresh secret for it and are about to use it. + await archiveSession(currentSessionId) + + // Bail out if teardown started while we were archiving + if (pollController.signal.aborted) { + logForDebugging( + '[bridge:repl] Reconnect aborted after archive, cleaning up', + ) + await api.deregisterEnvironment(environmentId).catch(() => {}) + return false + } + + // Re-read the current title in case the user renamed the session. + // REPL wrapper reads session storage; daemon wrapper returns the + // original title (nothing to refresh). + const currentTitle = getCurrentTitle() + + // Create a new session on the now-registered environment + const newSessionId = await createSession({ + environmentId, + title: currentTitle, + gitRepoUrl, + branch, + signal: AbortSignal.timeout(15_000), + }) + + if (!newSessionId) { + logForDebugging( + '[bridge:repl] Session creation failed during reconnection', + ) + return false + } + + // Bail out if teardown started during session creation (up to 15s) + if (pollController.signal.aborted) { + logForDebugging( + '[bridge:repl] Reconnect aborted after session creation, cleaning up', + ) + await archiveSession(newSessionId) + return false + } + + currentSessionId = newSessionId + // Re-publish to the PID file so peer dedup (peerRegistry.ts) picks up the + // new ID — setReplBridgeHandle only fires at init/teardown, not reconnect. + void updateSessionBridgeId(toCompatSessionId(newSessionId)).catch(() => {}) + // Reset per-session transport state IMMEDIATELY after the session swap, + // before any await. If this runs after `await writeBridgePointer` below, + // there's a window where handle.bridgeSessionId already returns session B + // but getSSESequenceNum() still returns session A's seq — a daemon + // persistState() in that window writes {bridgeSessionId: B, seq: OLD_A}, + // which PASSES the session-ID validation check and defeats it entirely. + // + // The SSE seq-num is scoped to the session's event stream — carrying it + // over leaves the transport's lastSequenceNum stuck high (seq only + // advances when received > last), and its next internal reconnect would + // send from_sequence_num=OLD_SEQ against a stream starting at 1 → all + // events in the gap silently dropped. Inbound UUID dedup is also + // session-scoped. + lastTransportSequenceNum = 0 + recentInboundUUIDs.clear() + // Title derivation is session-scoped too: if the user typed during the + // createSession await above, the callback fired against the OLD archived + // session ID (PATCH lost) and the new session got `currentTitle` captured + // BEFORE they typed. Reset so the next prompt can re-derive. Self- + // correcting: if the caller's policy is already done (explicit title or + // count ≥ 3), it returns true on the first post-reset call and re-latches. + userMessageCallbackDone = !onUserMessage + logForDebugging(`[bridge:repl] Re-created session: ${currentSessionId}`) + + // Rewrite the crash-recovery pointer with the new IDs so a crash after + // this point resumes the right session. (The reconnect-in-place path + // above doesn't touch the pointer — same session, same env.) + await writeBridgePointer(dir, { + sessionId: currentSessionId, + environmentId, + source: 'repl', + }) + + // Clear flushed UUIDs so initial messages are re-sent to the new session. + // UUIDs are scoped per-session on the server, so re-flushing is safe. + previouslyFlushedUUIDs?.clear() + + + // Reset the counter so independent reconnections hours apart don't + // exhaust the limit — it guards against rapid consecutive failures, + // not lifetime total. + environmentRecreations = 0 + + return true + } + + // Helper: get the current OAuth access token for session ingress auth. + // Unlike the JWT path, OAuth tokens are refreshed by the standard OAuth + // flow — no proactive scheduler needed. + function getOAuthToken(): string | undefined { + return getAccessToken() + } + + // Drain any messages that were queued during the initial flush. + // Called after writeBatch completes (or fails) so queued messages + // are sent in order after the historical messages. + function drainFlushGate(): void { + const msgs = flushGate.end() + if (msgs.length === 0) return + if (!transport) { + logForDebugging( + `[bridge:repl] Cannot drain ${msgs.length} pending message(s): no transport`, + ) + return + } + for (const msg of msgs) { + recentPostedUUIDs.add(msg.uuid) + } + const sdkMessages = toSDKMessages(msgs) + const events = sdkMessages.map(sdkMsg => ({ + ...sdkMsg, + session_id: currentSessionId, + })) + logForDebugging( + `[bridge:repl] Drained ${msgs.length} pending message(s) after flush`, + ) + void transport.writeBatch(events) + } + + // Teardown reference — set after definition below. All callers are async + // callbacks that run after assignment, so the reference is always valid. + let doTeardownImpl: (() => Promise) | null = null + function triggerTeardown(): void { + void doTeardownImpl?.() + } + + /** + * Body of the transport's setOnClose callback, hoisted to initBridgeCore + * scope so /bridge-kick can fire it directly. setOnClose wraps this with + * a stale-transport guard; debugFireClose calls it bare. + * + * With autoReconnect:true, this only fires on: clean close (1000), + * permanent server rejection (4001/1002/4003), or 10-min budget + * exhaustion. Transient drops are retried internally by the transport. + */ + function handleTransportPermanentClose(closeCode: number | undefined): void { + logForDebugging( + `[bridge:repl] Transport permanently closed: code=${closeCode}`, + ) + logEvent('tengu_bridge_repl_ws_closed', { + code: closeCode, + }) + // Capture SSE seq high-water mark before nulling. When called from + // setOnClose the guard guarantees transport !== null; when fired from + // /bridge-kick it may already be null (e.g. fired twice) — skip. + if (transport) { + const closedSeq = transport.getLastSequenceNum() + if (closedSeq > lastTransportSequenceNum) { + lastTransportSequenceNum = closedSeq + } + transport = null + } + // Transport is gone — wake the poll loop out of its at-capacity + // heartbeat sleep so it's fast-polling by the time the reconnect + // below completes and the server re-queues work. + wakePollLoop() + // Reset flush state so writeMessages() hits the !transport guard + // (with a warning log) instead of silently queuing into a buffer + // that will never be drained. Unlike onWorkReceived (which + // preserves pending messages for the new transport), onClose is + // a permanent close — no new transport will drain these. + const dropped = flushGate.drop() + if (dropped > 0) { + logForDebugging( + `[bridge:repl] Dropping ${dropped} pending message(s) on transport close (code=${closeCode})`, + { level: 'warn' }, + ) + } + + if (closeCode === 1000) { + // Clean close — session ended normally. Tear down the bridge. + onStateChange?.('failed', 'session ended') + pollController.abort() + triggerTeardown() + return + } + + // Transport reconnect budget exhausted or permanent server + // rejection. By this point the env has usually been reaped + // server-side (BQ 2026-03-12: ~98% of ws_closed never recover + // via poll alone). stopWork(force=false) can't re-dispatch work + // from an archived env; reconnectEnvironmentWithSession can + // re-activate it via POST /bridge/reconnect, or fall through + // to a fresh session if the env is truly gone. The poll loop + // (already woken above) picks up the re-queued work once + // doReconnect completes. + onStateChange?.( + 'reconnecting', + `Remote Control connection lost (code ${closeCode})`, + ) + logForDebugging( + `[bridge:repl] Transport reconnect budget exhausted (code=${closeCode}), attempting env reconnect`, + ) + void reconnectEnvironmentWithSession().then(success => { + if (success) return + // doReconnect has four abort-check return-false sites for + // teardown-in-progress. Don't pollute the BQ failure signal + // or double-teardown when the user just quit. + if (pollController.signal.aborted) return + // doReconnect returns false (never throws) on genuine failure. + // The dangerous case: registerBridgeEnvironment succeeded (so + // environmentId now points at a fresh valid env) but + // createSession failed — poll loop would poll a sessionless + // env getting null work with no errors, never hitting any + // give-up path. Tear down explicitly. + logForDebugging( + '[bridge:repl] reconnectEnvironmentWithSession resolved false — tearing down', + ) + logEvent('tengu_bridge_repl_reconnect_failed', { + close_code: closeCode, + }) + onStateChange?.('failed', 'reconnection failed') + triggerTeardown() + }) + } + + // Ant-only: SIGUSR2 → force doReconnect() for manual testing. Skips the + // ~30s poll wait — fire-and-observe in the debug log immediately. + // Windows has no USR signals; `process.on` would throw there. + let sigusr2Handler: (() => void) | undefined + if (process.env.USER_TYPE === 'ant' && process.platform !== 'win32') { + sigusr2Handler = () => { + logForDebugging( + '[bridge:repl] SIGUSR2 received — forcing doReconnect() for testing', + ) + void reconnectEnvironmentWithSession() + } + process.on('SIGUSR2', sigusr2Handler) + } + + // Ant-only: /bridge-kick fault injection. handleTransportPermanentClose + // is defined below and assigned into this slot so the slash command can + // invoke it directly — the real setOnClose callback is buried inside + // wireTransport which is itself inside onWorkReceived. + let debugFireClose: ((code: number) => void) | null = null + if (process.env.USER_TYPE === 'ant') { + registerBridgeDebugHandle({ + fireClose: code => { + if (!debugFireClose) { + logForDebugging('[bridge:debug] fireClose: no transport wired yet') + return + } + logForDebugging(`[bridge:debug] fireClose(${code}) — injecting`) + debugFireClose(code) + }, + forceReconnect: () => { + logForDebugging('[bridge:debug] forceReconnect — injecting') + void reconnectEnvironmentWithSession() + }, + injectFault: injectBridgeFault, + wakePollLoop, + describe: () => + `env=${environmentId} session=${currentSessionId} transport=${transport?.getStateLabel() ?? 'null'} workId=${currentWorkId ?? 'null'}`, + }) + } + + const pollOpts = { + api, + getCredentials: () => ({ environmentId, environmentSecret }), + signal: pollController.signal, + getPollIntervalConfig, + onStateChange, + getWsState: () => transport?.getStateLabel() ?? 'null', + // REPL bridge is single-session: having any transport == at capacity. + // No need to check isConnectedStatus() — even while the transport is + // auto-reconnecting internally (up to 10 min), poll is heartbeat-only. + isAtCapacity: () => transport !== null, + capacitySignal, + onFatalError: triggerTeardown, + getHeartbeatInfo: () => { + if (!currentWorkId || !currentIngressToken) { + return null + } + return { + environmentId, + workId: currentWorkId, + sessionToken: currentIngressToken, + } + }, + // Work-item JWT expired (or work gone). The transport is useless — + // SSE reconnects and CCR writes use the same stale token. Without + // this callback the poll loop would do a 10-min at-capacity backoff, + // during which the work lease (300s TTL) expires and the server stops + // forwarding prompts → ~25-min dead window observed in daemon logs. + // Kill the transport + work state so isAtCapacity()=false; the loop + // fast-polls and picks up the server's re-dispatched work in seconds. + onHeartbeatFatal: (err: BridgeFatalError) => { + logForDebugging( + `[bridge:repl] heartbeatWork fatal (status=${err.status}) — tearing down work item for fast re-dispatch`, + ) + if (transport) { + const seq = transport.getLastSequenceNum() + if (seq > lastTransportSequenceNum) { + lastTransportSequenceNum = seq + } + transport.close() + transport = null + } + flushGate.drop() + // force=false → server re-queues. Likely already expired, but + // idempotent and makes re-dispatch immediate if not. + if (currentWorkId) { + void api + .stopWork(environmentId, currentWorkId, false) + .catch((e: unknown) => { + logForDebugging( + `[bridge:repl] stopWork after heartbeat fatal: ${errorMessage(e)}`, + ) + }) + } + currentWorkId = null + currentIngressToken = null + wakePollLoop() + onStateChange?.( + 'reconnecting', + 'Work item lease expired, fetching fresh token', + ) + }, + async onEnvironmentLost() { + const success = await reconnectEnvironmentWithSession() + if (!success) { + return null + } + return { environmentId, environmentSecret } + }, + onWorkReceived: ( + workSessionId: string, + ingressToken: string, + workId: string, + serverUseCcrV2: boolean, + ) => { + // When new work arrives while a transport is already open, the + // server has decided to re-dispatch (e.g. token rotation, server + // restart). Close the existing transport and reconnect — discarding + // the work causes a stuck 'reconnecting' state if the old WS dies + // shortly after (the server won't re-dispatch a work item it + // already delivered). + // ingressToken (JWT) is stored for heartbeat auth (both v1 and v2). + // Transport auth diverges — see the v1/v2 split below. + if (transport?.isConnectedStatus()) { + logForDebugging( + `[bridge:repl] Work received while transport connected, replacing with fresh token (workId=${workId})`, + ) + } + + logForDebugging( + `[bridge:repl] Work received: workId=${workId} workSessionId=${workSessionId} currentSessionId=${currentSessionId} match=${sameSessionId(workSessionId, currentSessionId)}`, + ) + + // Refresh the crash-recovery pointer's mtime. Staleness checks file + // mtime (not embedded timestamp) so this re-write bumps the clock — + // a 5h+ session that crashes still has a fresh pointer. Fires once + // per work dispatch (infrequent — bounded by user message rate). + void writeBridgePointer(dir, { + sessionId: currentSessionId, + environmentId, + source: 'repl', + }) + + // Reject foreign session IDs — the server shouldn't assign sessions + // from other environments. Since we create env+session as a pair, + // a mismatch indicates an unexpected server-side reassignment. + // + // Compare by underlying UUID, not by tagged-ID prefix. When CCR + // v2's compat layer serves the session, createBridgeSession gets + // session_* from the v1-facing API (compat/convert.go:41) but the + // infrastructure layer delivers cse_* in the work queue + // (container_manager.go:129). Same UUID, different tag. + if (!sameSessionId(workSessionId, currentSessionId)) { + logForDebugging( + `[bridge:repl] Rejecting foreign session: expected=${currentSessionId} got=${workSessionId}`, + ) + return + } + + currentWorkId = workId + currentIngressToken = ingressToken + + // Server decides per-session (secret.use_code_sessions from the work + // secret, threaded through runWorkPollLoop). The env var is an ant-dev + // override for forcing v2 before the server flag is on for your user — + // requires ccr_v2_compat_enabled server-side or registerWorker 404s. + // + // Kept separate from CLAUDE_CODE_USE_CCR_V2 (the child-SDK transport + // selector set by sessionRunner/environment-manager) to avoid the + // inheritance hazard in spawn mode where the parent's orchestrator + // var would leak into a v1 child. + const useCcrV2 = + serverUseCcrV2 || isEnvTruthy(process.env.CLAUDE_BRIDGE_USE_CCR_V2) + + // Auth is the one place v1 and v2 diverge hard: + // + // - v1 (Session-Ingress): accepts OAuth OR JWT. We prefer OAuth + // because the standard OAuth refresh flow handles expiry — no + // separate JWT refresh scheduler needed. + // + // - v2 (CCR /worker/*): REQUIRES the JWT. register_worker.go:32 + // validates the session_id claim, which OAuth tokens don't carry. + // The JWT from the work secret has both that claim and the worker + // role (environment_auth.py:856). JWT refresh: when it expires the + // server re-dispatches work with a fresh one, and onWorkReceived + // fires again. createV2ReplTransport stores it via + // updateSessionIngressAuthToken() before touching the network. + let v1OauthToken: string | undefined + if (!useCcrV2) { + v1OauthToken = getOAuthToken() + if (!v1OauthToken) { + logForDebugging( + '[bridge:repl] No OAuth token available for session ingress, skipping work', + ) + return + } + updateSessionIngressAuthToken(v1OauthToken) + } + logEvent('tengu_bridge_repl_work_received', {}) + + // Close the previous transport. Nullify BEFORE calling close() so + // the close callback doesn't treat the programmatic close as + // "session ended normally" and trigger a full teardown. + if (transport) { + const oldTransport = transport + transport = null + // Capture the SSE sequence high-water mark so the next transport + // resumes the stream instead of replaying from seq 0. Use max() — + // a transport that died early (never received any frames) would + // otherwise reset a non-zero mark back to 0. + const oldSeq = oldTransport.getLastSequenceNum() + if (oldSeq > lastTransportSequenceNum) { + lastTransportSequenceNum = oldSeq + } + oldTransport.close() + } + // Reset flush state — the old flush (if any) is no longer relevant. + // Preserve pending messages so they're drained after the new + // transport's flush completes (the hook has already advanced its + // lastWrittenIndex and won't re-send them). + flushGate.deactivate() + + // Closure adapter over the shared handleServerControlRequest — + // captures transport/currentSessionId so the transport.setOnData + // callback below doesn't need to thread them through. + const onServerControlRequest = (request: SDKControlRequest): void => + handleServerControlRequest(request, { + transport, + sessionId: currentSessionId, + onInterrupt, + onSetModel, + onSetMaxThinkingTokens, + onSetPermissionMode, + }) + + let initialFlushDone = false + + // Wire callbacks onto a freshly constructed transport and connect. + // Extracted so the (sync) v1 and (async) v2 construction paths can + // share the identical callback + flush machinery. + const wireTransport = (newTransport: ReplBridgeTransport): void => { + transport = newTransport + + newTransport.setOnConnect(() => { + // Guard: if transport was replaced by a newer onWorkReceived call + // while the WS was connecting, ignore this stale callback. + if (transport !== newTransport) return + + logForDebugging('[bridge:repl] Ingress transport connected') + logEvent('tengu_bridge_repl_ws_connected', {}) + + // Update the env var with the latest OAuth token so POST writes + // (which read via getSessionIngressAuthToken()) use a fresh token. + // v2 skips this — createV2ReplTransport already stored the JWT, + // and overwriting it with OAuth would break subsequent /worker/* + // requests (session_id claim check). + if (!useCcrV2) { + const freshToken = getOAuthToken() + if (freshToken) { + updateSessionIngressAuthToken(freshToken) + } + } + + // Reset teardownStarted so future teardowns are not blocked. + teardownStarted = false + + // Flush initial messages only on first connect, not on every + // WS reconnection. Re-flushing would cause duplicate messages. + // IMPORTANT: onStateChange('connected') is deferred until the + // flush completes. This prevents writeMessages() from sending + // new messages that could arrive at the server interleaved with + // the historical messages, and delays the web UI from showing + // the session as active until history is persisted. + if ( + !initialFlushDone && + initialMessages && + initialMessages.length > 0 + ) { + initialFlushDone = true + + // Cap the initial flush to the most recent N messages. The full + // history is UI-only (model doesn't see it) and large replays cause + // slow session-ingress persistence (each event is a threadstore write) + // plus elevated Firestore pressure. A 0 or negative cap disables it. + const historyCap = initialHistoryCap + const eligibleMessages = initialMessages.filter( + m => + isEligibleBridgeMessage(m) && + !previouslyFlushedUUIDs?.has(m.uuid), + ) + const cappedMessages = + historyCap > 0 && eligibleMessages.length > historyCap + ? eligibleMessages.slice(-historyCap) + : eligibleMessages + if (cappedMessages.length < eligibleMessages.length) { + logForDebugging( + `[bridge:repl] Capped initial flush: ${eligibleMessages.length} -> ${cappedMessages.length} (cap=${historyCap})`, + ) + logEvent('tengu_bridge_repl_history_capped', { + eligible_count: eligibleMessages.length, + capped_count: cappedMessages.length, + }) + } + const sdkMessages = toSDKMessages(cappedMessages) + if (sdkMessages.length > 0) { + logForDebugging( + `[bridge:repl] Flushing ${sdkMessages.length} initial message(s) via transport`, + ) + const events = sdkMessages.map(sdkMsg => ({ + ...sdkMsg, + session_id: currentSessionId, + })) + const dropsBefore = newTransport.droppedBatchCount + void newTransport + .writeBatch(events) + .then(() => { + // If any batch was dropped during this flush (SI down for + // maxConsecutiveFailures attempts), flush() still resolved + // normally but the events were NOT delivered. Don't mark + // UUIDs as flushed — keep them eligible for re-send on the + // next onWorkReceived (JWT refresh re-dispatch, line ~1144). + if (newTransport.droppedBatchCount > dropsBefore) { + logForDebugging( + `[bridge:repl] Initial flush dropped ${newTransport.droppedBatchCount - dropsBefore} batch(es) — not marking ${sdkMessages.length} UUID(s) as flushed`, + ) + return + } + if (previouslyFlushedUUIDs) { + for (const sdkMsg of sdkMessages) { + if (sdkMsg.uuid) { + previouslyFlushedUUIDs.add(sdkMsg.uuid) + } + } + } + }) + .catch(e => + logForDebugging(`[bridge:repl] Initial flush failed: ${e}`), + ) + .finally(() => { + // Guard: if transport was replaced during the flush, + // don't signal connected or drain — the new transport + // owns the lifecycle now. + if (transport !== newTransport) return + drainFlushGate() + onStateChange?.('connected') + }) + } else { + // All initial messages were already flushed (filtered by + // previouslyFlushedUUIDs). No flush POST needed — clear + // the flag and signal connected immediately. This is the + // first connect for this transport (inside !initialFlushDone), + // so no flush POST is in-flight — the flag was set before + // connect() and must be cleared here. + drainFlushGate() + onStateChange?.('connected') + } + } else if (!flushGate.active) { + // No initial messages or already flushed on first connect. + // WS auto-reconnect path — only signal connected if no flush + // POST is in-flight. If one is, .finally() owns the lifecycle. + onStateChange?.('connected') + } + }) + + newTransport.setOnData(data => { + handleIngressMessage( + data, + recentPostedUUIDs, + recentInboundUUIDs, + onInboundMessage, + onPermissionResponse, + onServerControlRequest, + ) + }) + + // Body lives at initBridgeCore scope so /bridge-kick can call it + // directly via debugFireClose. All referenced closures (transport, + // wakePollLoop, flushGate, reconnectEnvironmentWithSession, etc.) + // are already at that scope. The only lexical dependency on + // wireTransport was `newTransport.getLastSequenceNum()` — but after + // the guard below passes we know transport === newTransport. + debugFireClose = handleTransportPermanentClose + newTransport.setOnClose(closeCode => { + // Guard: if transport was replaced, ignore stale close. + if (transport !== newTransport) return + handleTransportPermanentClose(closeCode) + }) + + // Start the flush gate before connect() to cover the WS handshake + // window. Between transport assignment and setOnConnect firing, + // writeMessages() could send messages via HTTP POST before the + // initial flush starts. Starting the gate here ensures those + // calls are queued. If there are no initial messages, the gate + // stays inactive. + if ( + !initialFlushDone && + initialMessages && + initialMessages.length > 0 + ) { + flushGate.start() + } + + newTransport.connect() + } // end wireTransport + + // Bump unconditionally — ANY new transport (v1 or v2) invalidates an + // in-flight v2 handshake. Also bumped in doReconnect(). + v2Generation++ + + if (useCcrV2) { + // workSessionId is the cse_* form (infrastructure-layer ID from the + // work queue), which is what /v1/code/sessions/{id}/worker/* wants. + // The session_* form (currentSessionId) is NOT usable here — + // handler/convert.go:30 validates TagCodeSession. + const sessionUrl = buildCCRv2SdkUrl(baseUrl, workSessionId) + const thisGen = v2Generation + logForDebugging( + `[bridge:repl] CCR v2: sessionUrl=${sessionUrl} session=${workSessionId} gen=${thisGen}`, + ) + void createV2ReplTransport({ + sessionUrl, + ingressToken, + sessionId: workSessionId, + initialSequenceNum: lastTransportSequenceNum, + }).then( + t => { + // Teardown started while registerWorker was in flight. Teardown + // saw transport === null and skipped close(); installing now + // would leak CCRClient heartbeat timers and reset + // teardownStarted via wireTransport's side effects. + if (pollController.signal.aborted) { + t.close() + return + } + // onWorkReceived may have fired again while registerWorker() + // was in flight (server re-dispatch with a fresh JWT). The + // transport !== null check alone gets the race wrong when BOTH + // attempts saw transport === null — it keeps the first resolver + // (stale epoch) and discards the second (correct epoch). The + // generation check catches it regardless of transport state. + if (thisGen !== v2Generation) { + logForDebugging( + `[bridge:repl] CCR v2: discarding stale handshake gen=${thisGen} current=${v2Generation}`, + ) + t.close() + return + } + wireTransport(t) + }, + (err: unknown) => { + logForDebugging( + `[bridge:repl] CCR v2: createV2ReplTransport failed: ${errorMessage(err)}`, + { level: 'error' }, + ) + logEvent('tengu_bridge_repl_ccr_v2_init_failed', {}) + // If a newer attempt is in flight or already succeeded, don't + // touch its work item — our failure is irrelevant. + if (thisGen !== v2Generation) return + // Release the work item so the server re-dispatches immediately + // instead of waiting for its own timeout. currentWorkId was set + // above; without this, the session looks stuck to the user. + if (currentWorkId) { + void api + .stopWork(environmentId, currentWorkId, false) + .catch((e: unknown) => { + logForDebugging( + `[bridge:repl] stopWork after v2 init failure: ${errorMessage(e)}`, + ) + }) + currentWorkId = null + currentIngressToken = null + } + wakePollLoop() + }, + ) + } else { + // v1: HybridTransport (WS reads + POST writes to Session-Ingress). + // autoReconnect is true (default) — when the WS dies, the transport + // reconnects automatically with exponential backoff. POST writes + // continue during reconnection (they use getSessionIngressAuthToken() + // independently of WS state). The poll loop remains as a secondary + // fallback if the reconnect budget is exhausted (10 min). + // + // Auth: uses OAuth tokens directly instead of the JWT from the work + // secret. refreshHeaders picks up the latest OAuth token on each + // WS reconnect attempt. + const wsUrl = buildSdkUrl(sessionIngressUrl, workSessionId) + logForDebugging(`[bridge:repl] Ingress URL: ${wsUrl}`) + logForDebugging( + `[bridge:repl] Creating HybridTransport: session=${workSessionId}`, + ) + // v1OauthToken was validated non-null above (we'd have returned early). + const oauthToken = v1OauthToken ?? '' + wireTransport( + createV1ReplTransport( + new HybridTransport( + new URL(wsUrl), + { + Authorization: `Bearer ${oauthToken}`, + 'anthropic-version': '2023-06-01', + }, + workSessionId, + () => ({ + Authorization: `Bearer ${getOAuthToken() ?? oauthToken}`, + 'anthropic-version': '2023-06-01', + }), + // Cap retries so a persistently-failing session-ingress can't + // pin the uploader drain loop for the lifetime of the bridge. + // 50 attempts ≈ 20 min (15s POST timeout + 8s backoff + jitter + // per cycle at steady state). Bridge-only — 1P keeps indefinite. + { + maxConsecutiveFailures: 50, + isBridge: true, + onBatchDropped: () => { + onStateChange?.( + 'reconnecting', + 'Lost sync with Remote Control — events could not be delivered', + ) + // SI has been down ~20 min. Wake the poll loop so that when + // SI recovers, next poll → onWorkReceived → fresh transport + // → initial flush succeeds → onStateChange('connected') at + // ~line 1420. Without this, state stays 'reconnecting' even + // after SI recovers — daemon.ts:437 denies all permissions, + // useReplBridge.ts:311 keeps replBridgeSessionActive=false. + // If the env was archived during the outage, poll 404 → + // onEnvironmentLost recovery path handles it. + wakePollLoop() + }, + }, + ), + ), + ) + } + }, + } + void startWorkPollLoop(pollOpts) + + // Perpetual mode: hourly mtime refresh of the crash-recovery pointer. + // The onWorkReceived refresh only fires per user prompt — a + // daemon idle for >4h would have a stale pointer, and the next restart + // would clear it (readBridgePointer TTL check) → fresh session. The + // standalone bridge (bridgeMain.ts) has an identical hourly timer. + const pointerRefreshTimer = perpetual + ? setInterval(() => { + // doReconnect() reassigns currentSessionId/environmentId non- + // atomically (env at ~:634, session at ~:719, awaits in between). + // If this timer fires in that window, its fire-and-forget write can + // race with (and overwrite) doReconnect's own pointer write at ~:740, + // leaving the pointer at the now-archived old session. doReconnect + // writes the pointer itself, so skipping here is free. + if (reconnectPromise) return + void writeBridgePointer(dir, { + sessionId: currentSessionId, + environmentId, + source: 'repl', + }) + }, 60 * 60_000) + : null + pointerRefreshTimer?.unref?.() + + // Push a silent keep_alive frame on a fixed interval so upstream proxies + // and the session-ingress layer don't GC an otherwise-idle remote control + // session. The keep_alive type is filtered before reaching any client UI + // (Query.ts drops it; web/iOS/Android never see it in their message loop). + // Interval comes from GrowthBook (tengu_bridge_poll_interval_config + // session_keepalive_interval_v2_ms, default 120s); 0 = disabled. + const keepAliveIntervalMs = + getPollIntervalConfig().session_keepalive_interval_v2_ms + const keepAliveTimer = + keepAliveIntervalMs > 0 + ? setInterval(() => { + if (!transport) return + logForDebugging('[bridge:repl] keep_alive sent') + void transport.write({ type: 'keep_alive' }).catch((err: unknown) => { + logForDebugging( + `[bridge:repl] keep_alive write failed: ${errorMessage(err)}`, + ) + }) + }, keepAliveIntervalMs) + : null + keepAliveTimer?.unref?.() + + // Shared teardown sequence used by both cleanup registration and + // the explicit teardown() method on the returned handle. + let teardownStarted = false + doTeardownImpl = async (): Promise => { + if (teardownStarted) { + logForDebugging( + `[bridge:repl] Teardown already in progress, skipping duplicate call env=${environmentId} session=${currentSessionId}`, + ) + return + } + teardownStarted = true + const teardownStart = Date.now() + logForDebugging( + `[bridge:repl] Teardown starting: env=${environmentId} session=${currentSessionId} workId=${currentWorkId ?? 'none'} transportState=${transport?.getStateLabel() ?? 'null'}`, + ) + + if (pointerRefreshTimer !== null) { + clearInterval(pointerRefreshTimer) + } + if (keepAliveTimer !== null) { + clearInterval(keepAliveTimer) + } + if (sigusr2Handler) { + process.off('SIGUSR2', sigusr2Handler) + } + if (process.env.USER_TYPE === 'ant') { + clearBridgeDebugHandle() + debugFireClose = null + } + pollController.abort() + logForDebugging('[bridge:repl] Teardown: poll loop aborted') + + // Capture the live transport's seq BEFORE close() — close() is sync + // (just aborts the SSE fetch) and does NOT invoke onClose, so the + // setOnClose capture path never runs for explicit teardown. + // Without this, getSSESequenceNum() after teardown returns the stale + // lastTransportSequenceNum (captured at the last transport swap), and + // daemon callers persisting that value lose all events since then. + if (transport) { + const finalSeq = transport.getLastSequenceNum() + if (finalSeq > lastTransportSequenceNum) { + lastTransportSequenceNum = finalSeq + } + } + + if (perpetual) { + // Perpetual teardown is LOCAL-ONLY — do not send result, do not call + // stopWork, do not close the transport. All of those signal the + // server (and any mobile/attach subscribers) that the session is + // ending. Instead: stop polling, let the socket die with the + // process; the backend times the work-item lease back to pending on + // its own (TTL 300s). Next daemon start reads the pointer and + // reconnectSession re-queues work. + transport = null + flushGate.drop() + // Refresh the pointer mtime so that sessions lasting longer than + // BRIDGE_POINTER_TTL_MS (4h) don't appear stale on next start. + await writeBridgePointer(dir, { + sessionId: currentSessionId, + environmentId, + source: 'repl', + }) + logForDebugging( + `[bridge:repl] Teardown (perpetual): leaving env=${environmentId} session=${currentSessionId} alive on server, duration=${Date.now() - teardownStart}ms`, + ) + return + } + + // Fire the result message, then archive, THEN close. transport.write() + // only enqueues (SerialBatchEventUploader resolves on buffer-add); the + // stopWork/archive latency (~200-500ms) is the drain window for the + // result POST. Closing BEFORE archive meant relying on HybridTransport's + // void-ed 3s grace period, which nothing awaits — forceExit can kill the + // socket mid-POST. Same reorder as remoteBridgeCore.ts teardown (#22803). + const teardownTransport = transport + transport = null + flushGate.drop() + if (teardownTransport) { + void teardownTransport.write(makeResultMessage(currentSessionId)) + } + + const stopWorkP = currentWorkId + ? api + .stopWork(environmentId, currentWorkId, true) + .then(() => { + logForDebugging('[bridge:repl] Teardown: stopWork completed') + }) + .catch((err: unknown) => { + logForDebugging( + `[bridge:repl] Teardown stopWork failed: ${errorMessage(err)}`, + ) + }) + : Promise.resolve() + + // Run stopWork and archiveSession in parallel. gracefulShutdown.ts:407 + // races runCleanupFunctions() against 2s (NOT the 5s outer failsafe), + // so archive is capped at 1.5s at the injection site to stay under budget. + // archiveSession is contractually no-throw; the injected implementations + // log their own success/failure internally. + await Promise.all([stopWorkP, archiveSession(currentSessionId)]) + + teardownTransport?.close() + logForDebugging('[bridge:repl] Teardown: transport closed') + + await api.deregisterEnvironment(environmentId).catch((err: unknown) => { + logForDebugging( + `[bridge:repl] Teardown deregister failed: ${errorMessage(err)}`, + ) + }) + + // Clear the crash-recovery pointer — explicit disconnect or clean REPL + // exit means the user is done with this session. Crash/kill-9 never + // reaches this line, leaving the pointer for next-launch recovery. + await clearBridgePointer(dir) + + logForDebugging( + `[bridge:repl] Teardown complete: env=${environmentId} duration=${Date.now() - teardownStart}ms`, + ) + } + + // 8. Register cleanup for graceful shutdown + const unregister = registerCleanup(() => doTeardownImpl?.()) + + logForDebugging( + `[bridge:repl] Ready: env=${environmentId} session=${currentSessionId}`, + ) + onStateChange?.('ready') + + return { + get bridgeSessionId() { + return currentSessionId + }, + get environmentId() { + return environmentId + }, + getSSESequenceNum() { + // lastTransportSequenceNum only updates when a transport is CLOSED + // (captured at swap/onClose). During normal operation the CURRENT + // transport's live seq isn't reflected there. Merge both so callers + // (e.g. daemon persistState()) get the actual high-water mark. + const live = transport?.getLastSequenceNum() ?? 0 + return Math.max(lastTransportSequenceNum, live) + }, + sessionIngressUrl, + writeMessages(messages) { + // Filter to user/assistant messages that haven't already been sent. + // Two layers of dedup: + // - initialMessageUUIDs: messages sent as session creation events + // - recentPostedUUIDs: messages recently sent via POST + const filtered = messages.filter( + m => + isEligibleBridgeMessage(m) && + !initialMessageUUIDs.has(m.uuid) && + !recentPostedUUIDs.has(m.uuid), + ) + if (filtered.length === 0) return + + // Fire onUserMessage for title derivation. Scan before the flushGate + // check — prompts are title-worthy even if they queue behind the + // initial history flush. Keeps calling on every title-worthy message + // until the callback returns true; the caller owns the policy. + if (!userMessageCallbackDone) { + for (const m of filtered) { + const text = extractTitleText(m) + if (text !== undefined && onUserMessage?.(text, currentSessionId)) { + userMessageCallbackDone = true + break + } + } + } + + // Queue messages while the initial flush is in progress to prevent + // them from arriving at the server interleaved with history. + if (flushGate.enqueue(...filtered)) { + logForDebugging( + `[bridge:repl] Queued ${filtered.length} message(s) during initial flush`, + ) + return + } + + if (!transport) { + const types = filtered.map(m => m.type).join(',') + logForDebugging( + `[bridge:repl] Transport not configured, dropping ${filtered.length} message(s) [${types}] for session=${currentSessionId}`, + { level: 'warn' }, + ) + return + } + + // Track in the bounded ring buffer for echo filtering and dedup. + for (const msg of filtered) { + recentPostedUUIDs.add(msg.uuid) + } + + logForDebugging( + `[bridge:repl] Sending ${filtered.length} message(s) via transport`, + ) + + // Convert to SDK format and send via HTTP POST (HybridTransport). + // The web UI receives them via the subscribe WebSocket. + const sdkMessages = toSDKMessages(filtered) + const events = sdkMessages.map(sdkMsg => ({ + ...sdkMsg, + session_id: currentSessionId, + })) + void transport.writeBatch(events) + }, + writeSdkMessages(messages) { + // Daemon path: query() already yields SDKMessage, skip conversion. + // Still run echo dedup (server bounces writes back on the WS). + // No initialMessageUUIDs filter — daemon has no initial messages. + // No flushGate — daemon never starts it (no initial flush). + const filtered = messages.filter( + m => !m.uuid || !recentPostedUUIDs.has(m.uuid), + ) + if (filtered.length === 0) return + if (!transport) { + logForDebugging( + `[bridge:repl] Transport not configured, dropping ${filtered.length} SDK message(s) for session=${currentSessionId}`, + { level: 'warn' }, + ) + return + } + for (const msg of filtered) { + if (msg.uuid) recentPostedUUIDs.add(msg.uuid) + } + const events = filtered.map(m => ({ ...m, session_id: currentSessionId })) + void transport.writeBatch(events) + }, + sendControlRequest(request: SDKControlRequest) { + if (!transport) { + logForDebugging( + '[bridge:repl] Transport not configured, skipping control_request', + ) + return + } + const event = { ...request, session_id: currentSessionId } + void transport.write(event) + logForDebugging( + `[bridge:repl] Sent control_request request_id=${request.request_id}`, + ) + }, + sendControlResponse(response: SDKControlResponse) { + if (!transport) { + logForDebugging( + '[bridge:repl] Transport not configured, skipping control_response', + ) + return + } + const event = { ...response, session_id: currentSessionId } + void transport.write(event) + logForDebugging('[bridge:repl] Sent control_response') + }, + sendControlCancelRequest(requestId: string) { + if (!transport) { + logForDebugging( + '[bridge:repl] Transport not configured, skipping control_cancel_request', + ) + return + } + const event = { + type: 'control_cancel_request' as const, + request_id: requestId, + session_id: currentSessionId, + } + void transport.write(event) + logForDebugging( + `[bridge:repl] Sent control_cancel_request request_id=${requestId}`, + ) + }, + sendResult() { + if (!transport) { + logForDebugging( + `[bridge:repl] sendResult: skipping, transport not configured session=${currentSessionId}`, + ) + return + } + void transport.write(makeResultMessage(currentSessionId)) + logForDebugging( + `[bridge:repl] Sent result for session=${currentSessionId}`, + ) + }, + async teardown() { + unregister() + await doTeardownImpl?.() + logForDebugging('[bridge:repl] Torn down') + logEvent('tengu_bridge_repl_teardown', {}) + }, + } +} + +/** + * Persistent poll loop for work items. Runs in the background for the + * lifetime of the bridge connection. + * + * When a work item arrives, acknowledges it and calls onWorkReceived + * with the session ID and ingress token (which connects the ingress + * WebSocket). Then continues polling — the server will dispatch a new + * work item if the ingress WebSocket drops, allowing automatic + * reconnection without tearing down the bridge. + */ +async function startWorkPollLoop({ + api, + getCredentials, + signal, + onStateChange, + onWorkReceived, + onEnvironmentLost, + getWsState, + isAtCapacity, + capacitySignal, + onFatalError, + getPollIntervalConfig = () => DEFAULT_POLL_CONFIG, + getHeartbeatInfo, + onHeartbeatFatal, +}: { + api: BridgeApiClient + getCredentials: () => { environmentId: string; environmentSecret: string } + signal: AbortSignal + onStateChange?: (state: BridgeState, detail?: string) => void + onWorkReceived: ( + sessionId: string, + ingressToken: string, + workId: string, + useCodeSessions: boolean, + ) => void + /** Called when the environment has been deleted. Returns new credentials or null. */ + onEnvironmentLost?: () => Promise<{ + environmentId: string + environmentSecret: string + } | null> + /** Returns the current WebSocket readyState label for diagnostic logging. */ + getWsState?: () => string + /** + * Returns true when the caller cannot accept new work (transport already + * connected). When true, the loop polls at the configured at-capacity + * interval as a heartbeat only. Server-side BRIDGE_LAST_POLL_TTL is + * 4 hours — anything shorter than that is sufficient for liveness. + */ + isAtCapacity?: () => boolean + /** + * Produces a signal that aborts when capacity frees up (transport lost), + * merged with the loop signal. Used to interrupt the at-capacity sleep + * so recovery polling starts immediately. + */ + capacitySignal?: () => CapacitySignal + /** Called on unrecoverable errors (e.g. server-side expiry) to trigger full teardown. */ + onFatalError?: () => void + /** Poll interval config getter — defaults to DEFAULT_POLL_CONFIG. */ + getPollIntervalConfig?: () => PollIntervalConfig + /** + * Returns the current work ID and session ingress token for heartbeat. + * When null, heartbeat is not possible (no active work item). + */ + getHeartbeatInfo?: () => { + environmentId: string + workId: string + sessionToken: string + } | null + /** + * Called when heartbeatWork throws BridgeFatalError (401/403/404/410 — + * JWT expired or work item gone). Caller should tear down the transport + * + work state so isAtCapacity() flips to false and the loop fast-polls + * for the server's re-dispatched work item. When provided, the loop + * SKIPS the at-capacity backoff sleep (which would otherwise cause a + * ~10-minute dead window before recovery). When omitted, falls back to + * the backoff sleep to avoid a tight poll+heartbeat loop. + */ + onHeartbeatFatal?: (err: BridgeFatalError) => void +}): Promise { + const MAX_ENVIRONMENT_RECREATIONS = 3 + + logForDebugging( + `[bridge:repl] Starting work poll loop for env=${getCredentials().environmentId}`, + ) + + let consecutiveErrors = 0 + let firstErrorTime: number | null = null + let lastPollErrorTime: number | null = null + let environmentRecreations = 0 + // Set when the at-capacity sleep overruns its deadline by a large margin + // (process suspension). Consumed at the top of the next iteration to + // force one fast-poll cycle — isAtCapacity() is `transport !== null`, + // which stays true while the transport auto-reconnects, so the poll + // loop would otherwise go straight back to a 10-minute sleep on a + // transport that may be pointed at a dead socket. + let suspensionDetected = false + + while (!signal.aborted) { + // Capture credentials outside try so the catch block can detect + // whether a concurrent reconnection replaced the environment. + const { environmentId: envId, environmentSecret: envSecret } = + getCredentials() + const pollConfig = getPollIntervalConfig() + try { + const work = await api.pollForWork( + envId, + envSecret, + signal, + pollConfig.reclaim_older_than_ms, + ) + + // A successful poll proves the env is genuinely healthy — reset the + // env-loss counter so events hours apart each start fresh. Outside + // the state-change guard below because onEnvLost's success path + // already emits 'ready'; emitting again here would be a duplicate. + // (onEnvLost returning creds does NOT reset this — that would break + // oscillation protection when the new env immediately dies.) + environmentRecreations = 0 + + // Reset error tracking on successful poll + if (consecutiveErrors > 0) { + logForDebugging( + `[bridge:repl] Poll recovered after ${consecutiveErrors} consecutive error(s)`, + ) + consecutiveErrors = 0 + firstErrorTime = null + lastPollErrorTime = null + onStateChange?.('ready') + } + + if (!work) { + // Read-and-clear: after a detected suspension, skip the at-capacity + // branch exactly once. The pollForWork above already refreshed the + // server's BRIDGE_LAST_POLL_TTL; this fast cycle gives any + // re-dispatched work item a chance to land before we go back under. + const skipAtCapacityOnce = suspensionDetected + suspensionDetected = false + if (isAtCapacity?.() && capacitySignal && !skipAtCapacityOnce) { + const atCapMs = pollConfig.poll_interval_ms_at_capacity + // Heartbeat loops WITHOUT polling. When at-capacity polling is also + // enabled (atCapMs > 0), the loop tracks a deadline and breaks out + // to poll at that interval — heartbeat and poll compose instead of + // one suppressing the other. Breaks out when: + // - Poll deadline reached (atCapMs > 0 only) + // - Auth fails (JWT expired → poll refreshes tokens) + // - Capacity wake fires (transport lost → poll for new work) + // - Heartbeat config disabled (GrowthBook update) + // - Loop aborted (shutdown) + if ( + pollConfig.non_exclusive_heartbeat_interval_ms > 0 && + getHeartbeatInfo + ) { + logEvent('tengu_bridge_heartbeat_mode_entered', { + heartbeat_interval_ms: + pollConfig.non_exclusive_heartbeat_interval_ms, + }) + // Deadline computed once at entry — GB updates to atCapMs don't + // shift an in-flight deadline (next entry picks up the new value). + const pollDeadline = atCapMs > 0 ? Date.now() + atCapMs : null + let needsBackoff = false + let hbCycles = 0 + while ( + !signal.aborted && + isAtCapacity() && + (pollDeadline === null || Date.now() < pollDeadline) + ) { + const hbConfig = getPollIntervalConfig() + if (hbConfig.non_exclusive_heartbeat_interval_ms <= 0) break + + const info = getHeartbeatInfo() + if (!info) break + + // Capture capacity signal BEFORE the async heartbeat call so + // a transport loss during the HTTP request is caught by the + // subsequent sleep. + const cap = capacitySignal() + + try { + await api.heartbeatWork( + info.environmentId, + info.workId, + info.sessionToken, + ) + } catch (err) { + logForDebugging( + `[bridge:repl:heartbeat] Failed: ${errorMessage(err)}`, + ) + if (err instanceof BridgeFatalError) { + cap.cleanup() + logEvent('tengu_bridge_heartbeat_error', { + status: + err.status as unknown as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + error_type: (err.status === 401 || err.status === 403 + ? 'auth_failed' + : 'fatal') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + // JWT expired (401/403) or work item gone (404/410). + // Either way the current transport is dead — SSE + // reconnects and CCR writes will fail on the same + // stale token. If the caller gave us a recovery hook, + // tear down work state and skip backoff: isAtCapacity() + // flips to false, next outer-loop iteration fast-polls + // for the server's re-dispatched work item. Without + // the hook, backoff to avoid tight poll+heartbeat loop. + if (onHeartbeatFatal) { + onHeartbeatFatal(err) + logForDebugging( + `[bridge:repl:heartbeat] Fatal (status=${err.status}), work state cleared — fast-polling for re-dispatch`, + ) + } else { + needsBackoff = true + } + break + } + } + + hbCycles++ + await sleep( + hbConfig.non_exclusive_heartbeat_interval_ms, + cap.signal, + ) + cap.cleanup() + } + + const exitReason = needsBackoff + ? 'error' + : signal.aborted + ? 'shutdown' + : !isAtCapacity() + ? 'capacity_changed' + : pollDeadline !== null && Date.now() >= pollDeadline + ? 'poll_due' + : 'config_disabled' + logEvent('tengu_bridge_heartbeat_mode_exited', { + reason: + exitReason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + heartbeat_cycles: hbCycles, + }) + + // On auth_failed or fatal, backoff before polling to avoid a + // tight poll+heartbeat loop. Fall through to the shared sleep + // below — it's the same capacitySignal-wrapped sleep the legacy + // path uses, and both need the suspension-overrun check. + if (!needsBackoff) { + if (exitReason === 'poll_due') { + // bridgeApi throttles empty-poll logs (EMPTY_POLL_LOG_INTERVAL=100) + // so the once-per-10min poll_due poll is invisible at counter=2. + // Log it here so verification runs see both endpoints in the debug log. + logForDebugging( + `[bridge:repl] Heartbeat poll_due after ${hbCycles} cycles — falling through to pollForWork`, + ) + } + continue + } + } + // At-capacity sleep — reached by both the legacy path (heartbeat + // disabled) and the heartbeat-backoff path (needsBackoff=true). + // Merged so the suspension detector covers both; previously the + // backoff path had no overrun check and could go straight back + // under for 10 min after a laptop wake. Use atCapMs when enabled, + // else the heartbeat interval as a floor (guaranteed > 0 on the + // backoff path) so heartbeat-only configs don't tight-loop. + const sleepMs = + atCapMs > 0 + ? atCapMs + : pollConfig.non_exclusive_heartbeat_interval_ms + if (sleepMs > 0) { + const cap = capacitySignal() + const sleepStart = Date.now() + await sleep(sleepMs, cap.signal) + cap.cleanup() + // Process-suspension detector. A setTimeout overshooting its + // deadline by 60s means the process was suspended (laptop lid, + // SIGSTOP, VM pause) — even a pathological GC pause is seconds, + // not minutes. Early aborts (wakePollLoop → cap.signal) produce + // overrun < 0 and fall through. Note: this only catches sleeps + // that outlast their deadline; WebSocketTransport's ping + // interval (10s granularity) is the primary detector for shorter + // suspensions. This is the backstop for when that detector isn't + // running (transport mid-reconnect, interval stopped). + const overrun = Date.now() - sleepStart - sleepMs + if (overrun > 60_000) { + logForDebugging( + `[bridge:repl] At-capacity sleep overran by ${Math.round(overrun / 1000)}s — process suspension detected, forcing one fast-poll cycle`, + ) + logEvent('tengu_bridge_repl_suspension_detected', { + overrun_ms: overrun, + }) + suspensionDetected = true + } + } + } else { + await sleep(pollConfig.poll_interval_ms_not_at_capacity, signal) + } + continue + } + + // Decode before type dispatch — need the JWT for the explicit ack. + let secret + try { + secret = decodeWorkSecret(work.secret) + } catch (err) { + logForDebugging( + `[bridge:repl] Failed to decode work secret: ${errorMessage(err)}`, + ) + logEvent('tengu_bridge_repl_work_secret_failed', {}) + // Can't ack (needs the JWT we failed to decode). stopWork uses OAuth. + // Prevents XAUTOCLAIM re-delivering this poisoned item every cycle. + await api.stopWork(envId, work.id, false).catch(() => {}) + continue + } + + // Explicitly acknowledge to prevent redelivery. Non-fatal on failure: + // server re-delivers, and the onWorkReceived callback handles dedup. + logForDebugging(`[bridge:repl] Acknowledging workId=${work.id}`) + try { + await api.acknowledgeWork(envId, work.id, secret.session_ingress_token) + } catch (err) { + logForDebugging( + `[bridge:repl] Acknowledge failed workId=${work.id}: ${errorMessage(err)}`, + ) + } + + if (work.data.type === 'healthcheck') { + logForDebugging('[bridge:repl] Healthcheck received') + continue + } + + if (work.data.type === 'session') { + const workSessionId = work.data.id + try { + validateBridgeId(workSessionId, 'session_id') + } catch { + logForDebugging( + `[bridge:repl] Invalid session_id in work: ${workSessionId}`, + ) + continue + } + + onWorkReceived( + workSessionId, + secret.session_ingress_token, + work.id, + secret.use_code_sessions === true, + ) + logForDebugging('[bridge:repl] Work accepted, continuing poll loop') + } + } catch (err) { + if (signal.aborted) break + + // Detect permanent "environment deleted" error — no amount of + // retrying will recover. Re-register a new environment instead. + // Checked BEFORE the generic BridgeFatalError bail. pollForWork uses + // validateStatus: s => s < 500, so 404 is always wrapped into a + // BridgeFatalError by handleErrorStatus() — never an axios-shaped + // error. The poll endpoint's only path param is the env ID; 404 + // unambiguously means env-gone (no-work is a 200 with null body). + // The server sends error.type='not_found_error' (standard Anthropic + // API shape), not a bridge-specific string — but status===404 is + // the real signal and survives body-shape changes. + if ( + err instanceof BridgeFatalError && + err.status === 404 && + onEnvironmentLost + ) { + // If credentials have already been refreshed by a concurrent + // reconnection (e.g. WS close handler), the stale poll's error + // is expected — skip onEnvironmentLost and retry with fresh creds. + const currentEnvId = getCredentials().environmentId + if (envId !== currentEnvId) { + logForDebugging( + `[bridge:repl] Stale poll error for old env=${envId}, current env=${currentEnvId} — skipping onEnvironmentLost`, + ) + consecutiveErrors = 0 + firstErrorTime = null + continue + } + + environmentRecreations++ + logForDebugging( + `[bridge:repl] Environment deleted, attempting re-registration (attempt ${environmentRecreations}/${MAX_ENVIRONMENT_RECREATIONS})`, + ) + logEvent('tengu_bridge_repl_env_lost', { + attempt: environmentRecreations, + } as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS) + + if (environmentRecreations > MAX_ENVIRONMENT_RECREATIONS) { + logForDebugging( + `[bridge:repl] Environment re-registration limit reached (${MAX_ENVIRONMENT_RECREATIONS}), giving up`, + ) + onStateChange?.( + 'failed', + 'Environment deleted and re-registration limit reached', + ) + onFatalError?.() + break + } + + onStateChange?.('reconnecting', 'environment lost, recreating session') + const newCreds = await onEnvironmentLost() + // doReconnect() makes several sequential network calls (1-5s). + // If the user triggered teardown during that window, its internal + // abort checks return false — but we need to re-check here to + // avoid emitting a spurious 'failed' + onFatalError() during + // graceful shutdown. + if (signal.aborted) break + if (newCreds) { + // Credentials are updated in the outer scope via + // reconnectEnvironmentWithSession — getCredentials() will + // return the fresh values on the next poll iteration. + // Do NOT reset environmentRecreations here — onEnvLost returning + // creds only proves we tried to fix it, not that the env is + // healthy. A successful poll (above) is the reset point; if the + // new env immediately dies again we still want the limit to fire. + consecutiveErrors = 0 + firstErrorTime = null + onStateChange?.('ready') + logForDebugging( + `[bridge:repl] Re-registered environment: ${newCreds.environmentId}`, + ) + continue + } + + onStateChange?.( + 'failed', + 'Environment deleted and re-registration failed', + ) + onFatalError?.() + break + } + + // Fatal errors (401/403/404/410) — no point retrying + if (err instanceof BridgeFatalError) { + const isExpiry = isExpiredErrorType(err.errorType) + const isSuppressible = isSuppressible403(err) + logForDebugging( + `[bridge:repl] Fatal poll error: ${err.message} (status=${err.status}, type=${err.errorType ?? 'unknown'})${isSuppressible ? ' (suppressed)' : ''}`, + ) + logEvent('tengu_bridge_repl_fatal_error', { + status: err.status, + error_type: + err.errorType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + logForDiagnosticsNoPII( + isExpiry ? 'info' : 'error', + 'bridge_repl_fatal_error', + { status: err.status, error_type: err.errorType }, + ) + // Cosmetic 403 errors (e.g., external_poll_sessions scope, + // environments:manage permission) — suppress user-visible error + // but always trigger teardown so cleanup runs. + if (!isSuppressible) { + onStateChange?.( + 'failed', + isExpiry + ? 'session expired · /remote-control to reconnect' + : err.message, + ) + } + // Always trigger teardown — matches bridgeMain.ts where fatalExit=true + // is unconditional and post-loop cleanup always runs. + onFatalError?.() + break + } + + const now = Date.now() + + // Detect system sleep/wake: if the gap since the last poll error + // greatly exceeds the max backoff delay, the machine likely slept. + // Reset error tracking so we retry with a fresh budget instead of + // immediately giving up. + if ( + lastPollErrorTime !== null && + now - lastPollErrorTime > POLL_ERROR_MAX_DELAY_MS * 2 + ) { + logForDebugging( + `[bridge:repl] Detected system sleep (${Math.round((now - lastPollErrorTime) / 1000)}s gap), resetting poll error budget`, + ) + logForDiagnosticsNoPII('info', 'bridge_repl_poll_sleep_detected', { + gapMs: now - lastPollErrorTime, + }) + consecutiveErrors = 0 + firstErrorTime = null + } + lastPollErrorTime = now + + consecutiveErrors++ + if (firstErrorTime === null) { + firstErrorTime = now + } + const elapsed = now - firstErrorTime + const httpStatus = extractHttpStatus(err) + const errMsg = describeAxiosError(err) + const wsLabel = getWsState?.() ?? 'unknown' + + logForDebugging( + `[bridge:repl] Poll error (attempt ${consecutiveErrors}, elapsed ${Math.round(elapsed / 1000)}s, ws=${wsLabel}): ${errMsg}`, + ) + logEvent('tengu_bridge_repl_poll_error', { + status: httpStatus, + consecutiveErrors, + elapsedMs: elapsed, + } as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS) + + // Only transition to 'reconnecting' on the first error — stay + // there until a successful poll (avoid flickering the UI state). + if (consecutiveErrors === 1) { + onStateChange?.('reconnecting', errMsg) + } + + // Give up after continuous failures + if (elapsed >= POLL_ERROR_GIVE_UP_MS) { + logForDebugging( + `[bridge:repl] Poll failures exceeded ${POLL_ERROR_GIVE_UP_MS / 1000}s (${consecutiveErrors} errors), giving up`, + ) + logForDiagnosticsNoPII('info', 'bridge_repl_poll_give_up') + logEvent('tengu_bridge_repl_poll_give_up', { + consecutiveErrors, + elapsedMs: elapsed, + lastStatus: httpStatus, + } as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS) + onStateChange?.('failed', 'connection to server lost') + break + } + + // Exponential backoff: 2s → 4s → 8s → 16s → 32s → 60s (cap) + const backoff = Math.min( + POLL_ERROR_INITIAL_DELAY_MS * 2 ** (consecutiveErrors - 1), + POLL_ERROR_MAX_DELAY_MS, + ) + // The poll_due heartbeat-loop exit leaves a healthy lease exposed to + // this backoff path. Heartbeat before each sleep so /poll outages + // (the VerifyEnvironmentSecretAuth DB path heartbeat was introduced to + // avoid) don't kill the 300s lease TTL. + if (getPollIntervalConfig().non_exclusive_heartbeat_interval_ms > 0) { + const info = getHeartbeatInfo?.() + if (info) { + try { + await api.heartbeatWork( + info.environmentId, + info.workId, + info.sessionToken, + ) + } catch { + // Best-effort — if heartbeat also fails the lease dies, same as + // pre-poll_due behavior (where the only heartbeat-loop exits were + // ones where the lease was already dying). + } + } + } + await sleep(backoff, signal) + } + } + + logForDebugging( + `[bridge:repl] Work poll loop ended (aborted=${signal.aborted}) env=${getCredentials().environmentId}`, + ) +} + +// Exported for testing only +export { + startWorkPollLoop as _startWorkPollLoopForTesting, + POLL_ERROR_INITIAL_DELAY_MS as _POLL_ERROR_INITIAL_DELAY_MS_ForTesting, + POLL_ERROR_MAX_DELAY_MS as _POLL_ERROR_MAX_DELAY_MS_ForTesting, + POLL_ERROR_GIVE_UP_MS as _POLL_ERROR_GIVE_UP_MS_ForTesting, +} diff --git a/bridge/replBridgeHandle.ts b/bridge/replBridgeHandle.ts new file mode 100644 index 0000000..f04d745 --- /dev/null +++ b/bridge/replBridgeHandle.ts @@ -0,0 +1,36 @@ +import { updateSessionBridgeId } from '../utils/concurrentSessions.js' +import type { ReplBridgeHandle } from './replBridge.js' +import { toCompatSessionId } from './sessionIdCompat.js' + +/** + * Global pointer to the active REPL bridge handle, so callers outside + * useReplBridge's React tree (tools, slash commands) can invoke handle methods + * like subscribePR. Same one-bridge-per-process justification as bridgeDebug.ts + * — the handle's closure captures the sessionId and getAccessToken that created + * the session, and re-deriving those independently (BriefTool/upload.ts pattern) + * risks staging/prod token divergence. + * + * Set from useReplBridge.tsx when init completes; cleared on teardown. + */ + +let handle: ReplBridgeHandle | null = null + +export function setReplBridgeHandle(h: ReplBridgeHandle | null): void { + handle = h + // Publish (or clear) our bridge session ID in the session record so other + // local peers can dedup us out of their bridge list — local is preferred. + void updateSessionBridgeId(getSelfBridgeCompatId() ?? null).catch(() => {}) +} + +export function getReplBridgeHandle(): ReplBridgeHandle | null { + return handle +} + +/** + * Our own bridge session ID in the session_* compat format the API returns + * in /v1/sessions responses — or undefined if bridge isn't connected. + */ +export function getSelfBridgeCompatId(): string | undefined { + const h = getReplBridgeHandle() + return h ? toCompatSessionId(h.bridgeSessionId) : undefined +} diff --git a/bridge/replBridgeTransport.ts b/bridge/replBridgeTransport.ts new file mode 100644 index 0000000..2a844f9 --- /dev/null +++ b/bridge/replBridgeTransport.ts @@ -0,0 +1,370 @@ +import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js' +import { CCRClient } from '../cli/transports/ccrClient.js' +import type { HybridTransport } from '../cli/transports/HybridTransport.js' +import { SSETransport } from '../cli/transports/SSETransport.js' +import { logForDebugging } from '../utils/debug.js' +import { errorMessage } from '../utils/errors.js' +import { updateSessionIngressAuthToken } from '../utils/sessionIngressAuth.js' +import type { SessionState } from '../utils/sessionState.js' +import { registerWorker } from './workSecret.js' + +/** + * Transport abstraction for replBridge. Covers exactly the surface that + * replBridge.ts uses against HybridTransport so the v1/v2 choice is + * confined to the construction site. + * + * - v1: HybridTransport (WS reads + POST writes to Session-Ingress) + * - v2: SSETransport (reads) + CCRClient (writes to CCR v2 /worker/*) + * + * The v2 write path goes through CCRClient.writeEvent → SerialBatchEventUploader, + * NOT through SSETransport.write() — SSETransport.write() targets the + * Session-Ingress POST URL shape, which is wrong for CCR v2. + */ +export type ReplBridgeTransport = { + write(message: StdoutMessage): Promise + writeBatch(messages: StdoutMessage[]): Promise + close(): void + isConnectedStatus(): boolean + getStateLabel(): string + setOnData(callback: (data: string) => void): void + setOnClose(callback: (closeCode?: number) => void): void + setOnConnect(callback: () => void): void + connect(): void + /** + * High-water mark of the underlying read stream's event sequence numbers. + * replBridge reads this before swapping transports so the new one can + * resume from where the old one left off (otherwise the server replays + * the entire session history from seq 0). + * + * v1 returns 0 — Session-Ingress WS doesn't use SSE sequence numbers; + * replay-on-reconnect is handled by the server-side message cursor. + */ + getLastSequenceNum(): number + /** + * Monotonic count of batches dropped via maxConsecutiveFailures. + * Snapshot before writeBatch() and compare after to detect silent drops + * (writeBatch() resolves normally even when batches were dropped). + * v2 returns 0 — the v2 write path doesn't set maxConsecutiveFailures. + */ + readonly droppedBatchCount: number + /** + * PUT /worker state (v2 only; v1 is a no-op). `requires_action` tells + * the backend a permission prompt is pending — claude.ai shows the + * "waiting for input" indicator. REPL/daemon callers don't need this + * (user watches the REPL locally); multi-session worker callers do. + */ + reportState(state: SessionState): void + /** PUT /worker external_metadata (v2 only; v1 is a no-op). */ + reportMetadata(metadata: Record): void + /** + * POST /worker/events/{id}/delivery (v2 only; v1 is a no-op). Populates + * CCR's processing_at/processed_at columns. `received` is auto-fired by + * CCRClient on every SSE frame and is not exposed here. + */ + reportDelivery(eventId: string, status: 'processing' | 'processed'): void + /** + * Drain the write queue before close() (v2 only; v1 resolves + * immediately — HybridTransport POSTs are already awaited per-write). + */ + flush(): Promise +} + +/** + * v1 adapter: HybridTransport already has the full surface (it extends + * WebSocketTransport which has setOnConnect + getStateLabel). This is a + * no-op wrapper that exists only so replBridge's `transport` variable + * has a single type. + */ +export function createV1ReplTransport( + hybrid: HybridTransport, +): ReplBridgeTransport { + return { + write: msg => hybrid.write(msg), + writeBatch: msgs => hybrid.writeBatch(msgs), + close: () => hybrid.close(), + isConnectedStatus: () => hybrid.isConnectedStatus(), + getStateLabel: () => hybrid.getStateLabel(), + setOnData: cb => hybrid.setOnData(cb), + setOnClose: cb => hybrid.setOnClose(cb), + setOnConnect: cb => hybrid.setOnConnect(cb), + connect: () => void hybrid.connect(), + // v1 Session-Ingress WS doesn't use SSE sequence numbers; replay + // semantics are different. Always return 0 so the seq-num carryover + // logic in replBridge is a no-op for v1. + getLastSequenceNum: () => 0, + get droppedBatchCount() { + return hybrid.droppedBatchCount + }, + reportState: () => {}, + reportMetadata: () => {}, + reportDelivery: () => {}, + flush: () => Promise.resolve(), + } +} + +/** + * v2 adapter: wrap SSETransport (reads) + CCRClient (writes, heartbeat, + * state, delivery tracking). + * + * Auth: v2 endpoints validate the JWT's session_id claim (register_worker.go:32) + * and worker role (environment_auth.py:856). OAuth tokens have neither. + * This is the inverse of the v1 replBridge path, which deliberately uses OAuth. + * The JWT is refreshed when the poll loop re-dispatches work — the caller + * invokes createV2ReplTransport again with the fresh token. + * + * Registration happens here (not in the caller) so the entire v2 handshake + * is one async step. registerWorker failure propagates — replBridge will + * catch it and stay on the poll loop. + */ +export async function createV2ReplTransport(opts: { + sessionUrl: string + ingressToken: string + sessionId: string + /** + * SSE sequence-number high-water mark from the previous transport. + * Passed to the new SSETransport so its first connect() sends + * from_sequence_num / Last-Event-ID and the server resumes from where + * the old stream left off. Without this, every transport swap asks the + * server to replay the entire session history from seq 0. + */ + initialSequenceNum?: number + /** + * Worker epoch from POST /bridge response. When provided, the server + * already bumped epoch (the /bridge call IS the register — see server + * PR #293280). When omitted (v1 CCR-v2 path via replBridge.ts poll loop), + * call registerWorker as before. + */ + epoch?: number + /** CCRClient heartbeat interval. Defaults to 20s when omitted. */ + heartbeatIntervalMs?: number + /** ±fraction per-beat jitter. Defaults to 0 (no jitter) when omitted. */ + heartbeatJitterFraction?: number + /** + * When true, skip opening the SSE read stream — only the CCRClient write + * path is activated. Use for mirror-mode attachments that forward events + * but never receive inbound prompts or control requests. + */ + outboundOnly?: boolean + /** + * Per-instance auth header source. When provided, CCRClient + SSETransport + * read auth from this closure instead of the process-wide + * CLAUDE_CODE_SESSION_ACCESS_TOKEN env var. Required for callers managing + * multiple concurrent sessions — the env-var path stomps across sessions. + * When omitted, falls back to the env var (single-session callers). + */ + getAuthToken?: () => string | undefined +}): Promise { + const { + sessionUrl, + ingressToken, + sessionId, + initialSequenceNum, + getAuthToken, + } = opts + + // Auth header builder. If getAuthToken is provided, read from it + // (per-instance, multi-session safe). Otherwise write ingressToken to + // the process-wide env var (legacy single-session path — CCRClient's + // default getAuthHeaders reads it via getSessionIngressAuthHeaders). + let getAuthHeaders: (() => Record) | undefined + if (getAuthToken) { + getAuthHeaders = (): Record => { + const token = getAuthToken() + if (!token) return {} + return { Authorization: `Bearer ${token}` } + } + } else { + // CCRClient.request() and SSETransport.connect() both read auth via + // getSessionIngressAuthHeaders() → this env var. Set it before either + // touches the network. + updateSessionIngressAuthToken(ingressToken) + } + + const epoch = opts.epoch ?? (await registerWorker(sessionUrl, ingressToken)) + logForDebugging( + `[bridge:repl] CCR v2: worker sessionId=${sessionId} epoch=${epoch}${opts.epoch !== undefined ? ' (from /bridge)' : ' (via registerWorker)'}`, + ) + + // Derive SSE stream URL. Same logic as transportUtils.ts:26-33 but + // starting from an http(s) base instead of a --sdk-url that might be ws://. + const sseUrl = new URL(sessionUrl) + sseUrl.pathname = sseUrl.pathname.replace(/\/$/, '') + '/worker/events/stream' + + const sse = new SSETransport( + sseUrl, + {}, + sessionId, + undefined, + initialSequenceNum, + getAuthHeaders, + ) + let onCloseCb: ((closeCode?: number) => void) | undefined + const ccr = new CCRClient(sse, new URL(sessionUrl), { + getAuthHeaders, + heartbeatIntervalMs: opts.heartbeatIntervalMs, + heartbeatJitterFraction: opts.heartbeatJitterFraction, + // Default is process.exit(1) — correct for spawn-mode children. In-process, + // that kills the REPL. Close instead: replBridge's onClose wakes the poll + // loop, which picks up the server's re-dispatch (with fresh epoch). + onEpochMismatch: () => { + logForDebugging( + '[bridge:repl] CCR v2: epoch superseded (409) — closing for poll-loop recovery', + ) + // Close resources in a try block so the throw always executes. + // If ccr.close() or sse.close() throw, we still need to unwind + // the caller (request()) — otherwise handleEpochMismatch's `never` + // return type is violated at runtime and control falls through. + try { + ccr.close() + sse.close() + onCloseCb?.(4090) + } catch (closeErr: unknown) { + logForDebugging( + `[bridge:repl] CCR v2: error during epoch-mismatch cleanup: ${errorMessage(closeErr)}`, + { level: 'error' }, + ) + } + // Don't return — the calling request() code continues after the 409 + // branch, so callers see the logged warning and a false return. We + // throw to unwind; the uploaders catch it as a send failure. + throw new Error('epoch superseded') + }, + }) + + // CCRClient's constructor wired sse.setOnEvent → reportDelivery('received'). + // remoteIO.ts additionally sends 'processing'/'processed' via + // setCommandLifecycleListener, which the in-process query loop fires. This + // transport's only caller (replBridge/daemonBridge) has no such wiring — the + // daemon's agent child is a separate process (ProcessTransport), and its + // notifyCommandLifecycle calls fire with listener=null in its own module + // scope. So events stay at 'received' forever, and reconnectSession re-queues + // them on every daemon restart (observed: 21→24→25 phantom prompts as + // "user sent a new message while you were working" system-reminders). + // + // Fix: ACK 'processed' immediately alongside 'received'. The window between + // SSE receipt and transcript-write is narrow (queue → SDK → child stdin → + // model); a crash there loses one prompt vs. the observed N-prompt flood on + // every restart. Overwrite the constructor's wiring to do both — setOnEvent + // replaces, not appends (SSETransport.ts:658). + sse.setOnEvent(event => { + ccr.reportDelivery(event.event_id, 'received') + ccr.reportDelivery(event.event_id, 'processed') + }) + + // Both sse.connect() and ccr.initialize() are deferred to connect() below. + // replBridge's calling order is newTransport → setOnConnect → setOnData → + // setOnClose → connect(), and both calls need those callbacks wired first: + // sse.connect() opens the stream (events flow to onData/onClose immediately), + // and ccr.initialize().then() fires onConnectCb. + // + // onConnect fires once ccr.initialize() resolves. Writes go via + // CCRClient HTTP POST (SerialBatchEventUploader), not SSE, so the + // write path is ready the moment workerEpoch is set. SSE.connect() + // awaits its read loop and never resolves — don't gate on it. + // The SSE stream opens in parallel (~30ms) and starts delivering + // inbound events via setOnData; outbound doesn't need to wait for it. + let onConnectCb: (() => void) | undefined + let ccrInitialized = false + let closed = false + + return { + write(msg) { + return ccr.writeEvent(msg) + }, + async writeBatch(msgs) { + // SerialBatchEventUploader already batches internally (maxBatchSize=100); + // sequential enqueue preserves order and the uploader coalesces. + // Check closed between writes to avoid sending partial batches after + // transport teardown (epoch mismatch, SSE drop). + for (const m of msgs) { + if (closed) break + await ccr.writeEvent(m) + } + }, + close() { + closed = true + ccr.close() + sse.close() + }, + isConnectedStatus() { + // Write-readiness, not read-readiness — replBridge checks this + // before calling writeBatch. SSE open state is orthogonal. + return ccrInitialized + }, + getStateLabel() { + // SSETransport doesn't expose its state string; synthesize from + // what we can observe. replBridge only uses this for debug logging. + if (sse.isClosedStatus()) return 'closed' + if (sse.isConnectedStatus()) return ccrInitialized ? 'connected' : 'init' + return 'connecting' + }, + setOnData(cb) { + sse.setOnData(cb) + }, + setOnClose(cb) { + onCloseCb = cb + // SSE reconnect-budget exhaustion fires onClose(undefined) — map to + // 4092 so ws_closed telemetry can distinguish it from HTTP-status + // closes (SSETransport:280 passes response.status). Stop CCRClient's + // heartbeat timer before notifying replBridge. (sse.close() doesn't + // invoke this, so the epoch-mismatch path above isn't double-firing.) + sse.setOnClose(code => { + ccr.close() + cb(code ?? 4092) + }) + }, + setOnConnect(cb) { + onConnectCb = cb + }, + getLastSequenceNum() { + return sse.getLastSequenceNum() + }, + // v2 write path (CCRClient) doesn't set maxConsecutiveFailures — no drops. + droppedBatchCount: 0, + reportState(state) { + ccr.reportState(state) + }, + reportMetadata(metadata) { + ccr.reportMetadata(metadata) + }, + reportDelivery(eventId, status) { + ccr.reportDelivery(eventId, status) + }, + flush() { + return ccr.flush() + }, + connect() { + // Outbound-only: skip the SSE read stream entirely — no inbound + // events to receive, no delivery ACKs to send. Only the CCRClient + // write path (POST /worker/events) and heartbeat are needed. + if (!opts.outboundOnly) { + // Fire-and-forget — SSETransport.connect() awaits readStream() + // (the read loop) and only resolves on stream close/error. The + // spawn-mode path in remoteIO.ts does the same void discard. + void sse.connect() + } + void ccr.initialize(epoch).then( + () => { + ccrInitialized = true + logForDebugging( + `[bridge:repl] v2 transport ready for writes (epoch=${epoch}, sse=${sse.isConnectedStatus() ? 'open' : 'opening'})`, + ) + onConnectCb?.() + }, + (err: unknown) => { + logForDebugging( + `[bridge:repl] CCR v2 initialize failed: ${errorMessage(err)}`, + { level: 'error' }, + ) + // Close transport resources and notify replBridge via onClose + // so the poll loop can retry on the next work dispatch. + // Without this callback, replBridge never learns the transport + // failed to initialize and sits with transport === null forever. + ccr.close() + sse.close() + onCloseCb?.(4091) // 4091 = init failure, distinguishable from 4090 epoch mismatch + }, + ) + }, + } +} diff --git a/bridge/sessionIdCompat.ts b/bridge/sessionIdCompat.ts new file mode 100644 index 0000000..57d8d22 --- /dev/null +++ b/bridge/sessionIdCompat.ts @@ -0,0 +1,57 @@ +/** + * Session ID tag translation helpers for the CCR v2 compat layer. + * + * Lives in its own file (rather than workSecret.ts) so that sessionHandle.ts + * and replBridgeTransport.ts (bridge.mjs entry points) can import from + * workSecret.ts without pulling in these retag functions. + * + * The isCseShimEnabled kill switch is injected via setCseShimGate() to avoid + * a static import of bridgeEnabled.ts → growthbook.ts → config.ts — all + * banned from the sdk.mjs bundle (scripts/build-agent-sdk.sh). Callers that + * already import bridgeEnabled.ts register the gate; the SDK path never does, + * so the shim defaults to active (matching isCseShimEnabled()'s own default). + */ + +let _isCseShimEnabled: (() => boolean) | undefined + +/** + * Register the GrowthBook gate for the cse_ shim. Called from bridge + * init code that already imports bridgeEnabled.ts. + */ +export function setCseShimGate(gate: () => boolean): void { + _isCseShimEnabled = gate +} + +/** + * Re-tag a `cse_*` session ID to `session_*` for use with the v1 compat API. + * + * Worker endpoints (/v1/code/sessions/{id}/worker/*) want `cse_*`; that's + * what the work poll delivers. Client-facing compat endpoints + * (/v1/sessions/{id}, /v1/sessions/{id}/archive, /v1/sessions/{id}/events) + * want `session_*` — compat/convert.go:27 validates TagSession. Same UUID, + * different costume. No-op for IDs that aren't `cse_*`. + * + * bridgeMain holds one sessionId variable for both worker registration and + * session-management calls. It arrives as `cse_*` from the work poll under + * the compat gate, so archiveSession/fetchSessionTitle need this re-tag. + */ +export function toCompatSessionId(id: string): string { + if (!id.startsWith('cse_')) return id + if (_isCseShimEnabled && !_isCseShimEnabled()) return id + return 'session_' + id.slice('cse_'.length) +} + +/** + * Re-tag a `session_*` session ID to `cse_*` for infrastructure-layer calls. + * + * Inverse of toCompatSessionId. POST /v1/environments/{id}/bridge/reconnect + * lives below the compat layer: once ccr_v2_compat_enabled is on server-side, + * it looks sessions up by their infra tag (`cse_*`). createBridgeSession still + * returns `session_*` (compat/convert.go:41) and that's what bridge-pointer + * stores — so perpetual reconnect passes the wrong costume and gets "Session + * not found" back. Same UUID, wrong tag. No-op for IDs that aren't `session_*`. + */ +export function toInfraSessionId(id: string): string { + if (!id.startsWith('session_')) return id + return 'cse_' + id.slice('session_'.length) +} diff --git a/bridge/sessionRunner.ts b/bridge/sessionRunner.ts new file mode 100644 index 0000000..bc232bc --- /dev/null +++ b/bridge/sessionRunner.ts @@ -0,0 +1,550 @@ +import { type ChildProcess, spawn } from 'child_process' +import { createWriteStream, type WriteStream } from 'fs' +import { tmpdir } from 'os' +import { dirname, join } from 'path' +import { createInterface } from 'readline' +import { jsonParse, jsonStringify } from '../utils/slowOperations.js' +import { debugTruncate } from './debugUtils.js' +import type { + SessionActivity, + SessionDoneStatus, + SessionHandle, + SessionSpawner, + SessionSpawnOpts, +} from './types.js' + +const MAX_ACTIVITIES = 10 +const MAX_STDERR_LINES = 10 + +/** + * Sanitize a session ID for use in file names. + * Strips any characters that could cause path traversal (e.g. `../`, `/`) + * or other filesystem issues, replacing them with underscores. + */ +export function safeFilenameId(id: string): string { + return id.replace(/[^a-zA-Z0-9_-]/g, '_') +} + +/** + * A control_request emitted by the child CLI when it needs permission to + * execute a **specific** tool invocation (not a general capability check). + * The bridge forwards this to the server so the user can approve/deny. + */ +export type PermissionRequest = { + type: 'control_request' + request_id: string + request: { + /** Per-invocation permission check — "may I run this tool with these inputs?" */ + subtype: 'can_use_tool' + tool_name: string + input: Record + tool_use_id: string + } +} + +type SessionSpawnerDeps = { + execPath: string + /** + * Arguments that must precede the CLI flags when spawning. Empty for + * compiled binaries (where execPath is the claude binary itself); contains + * the script path (process.argv[1]) for npm installs where execPath is the + * node runtime. Without this, node sees --sdk-url as a node option and + * exits with "bad option: --sdk-url" (see anthropics/claude-code#28334). + */ + scriptArgs: string[] + env: NodeJS.ProcessEnv + verbose: boolean + sandbox: boolean + debugFile?: string + permissionMode?: string + onDebug: (msg: string) => void + onActivity?: (sessionId: string, activity: SessionActivity) => void + onPermissionRequest?: ( + sessionId: string, + request: PermissionRequest, + accessToken: string, + ) => void +} + +/** Map tool names to human-readable verbs for the status display. */ +const TOOL_VERBS: Record = { + Read: 'Reading', + Write: 'Writing', + Edit: 'Editing', + MultiEdit: 'Editing', + Bash: 'Running', + Glob: 'Searching', + Grep: 'Searching', + WebFetch: 'Fetching', + WebSearch: 'Searching', + Task: 'Running task', + FileReadTool: 'Reading', + FileWriteTool: 'Writing', + FileEditTool: 'Editing', + GlobTool: 'Searching', + GrepTool: 'Searching', + BashTool: 'Running', + NotebookEditTool: 'Editing notebook', + LSP: 'LSP', +} + +function toolSummary(name: string, input: Record): string { + const verb = TOOL_VERBS[name] ?? name + const target = + (input.file_path as string) ?? + (input.filePath as string) ?? + (input.pattern as string) ?? + (input.command as string | undefined)?.slice(0, 60) ?? + (input.url as string) ?? + (input.query as string) ?? + '' + if (target) { + return `${verb} ${target}` + } + return verb +} + +function extractActivities( + line: string, + sessionId: string, + onDebug: (msg: string) => void, +): SessionActivity[] { + let parsed: unknown + try { + parsed = jsonParse(line) + } catch { + return [] + } + + if (!parsed || typeof parsed !== 'object') { + return [] + } + + const msg = parsed as Record + const activities: SessionActivity[] = [] + const now = Date.now() + + switch (msg.type) { + case 'assistant': { + const message = msg.message as Record | undefined + if (!message) break + const content = message.content + if (!Array.isArray(content)) break + + for (const block of content) { + if (!block || typeof block !== 'object') continue + const b = block as Record + + if (b.type === 'tool_use') { + const name = (b.name as string) ?? 'Tool' + const input = (b.input as Record) ?? {} + const summary = toolSummary(name, input) + activities.push({ + type: 'tool_start', + summary, + timestamp: now, + }) + onDebug( + `[bridge:activity] sessionId=${sessionId} tool_use name=${name} ${inputPreview(input)}`, + ) + } else if (b.type === 'text') { + const text = (b.text as string) ?? '' + if (text.length > 0) { + activities.push({ + type: 'text', + summary: text.slice(0, 80), + timestamp: now, + }) + onDebug( + `[bridge:activity] sessionId=${sessionId} text "${text.slice(0, 100)}"`, + ) + } + } + } + break + } + case 'result': { + const subtype = msg.subtype as string | undefined + if (subtype === 'success') { + activities.push({ + type: 'result', + summary: 'Session completed', + timestamp: now, + }) + onDebug( + `[bridge:activity] sessionId=${sessionId} result subtype=success`, + ) + } else if (subtype) { + const errors = msg.errors as string[] | undefined + const errorSummary = errors?.[0] ?? `Error: ${subtype}` + activities.push({ + type: 'error', + summary: errorSummary, + timestamp: now, + }) + onDebug( + `[bridge:activity] sessionId=${sessionId} result subtype=${subtype} error="${errorSummary}"`, + ) + } else { + onDebug( + `[bridge:activity] sessionId=${sessionId} result subtype=undefined`, + ) + } + break + } + default: + break + } + + return activities +} + +/** + * Extract plain text from a replayed SDKUserMessage NDJSON line. Returns the + * trimmed text if this looks like a real human-authored message, otherwise + * undefined so the caller keeps waiting for the first real message. + */ +function extractUserMessageText( + msg: Record, +): string | undefined { + // Skip tool-result user messages (wrapped subagent results) and synthetic + // caveat messages — neither is human-authored. + if (msg.parent_tool_use_id != null || msg.isSynthetic || msg.isReplay) + return undefined + + const message = msg.message as Record | undefined + const content = message?.content + let text: string | undefined + if (typeof content === 'string') { + text = content + } else if (Array.isArray(content)) { + for (const block of content) { + if ( + block && + typeof block === 'object' && + (block as Record).type === 'text' + ) { + text = (block as Record).text as string | undefined + break + } + } + } + text = text?.trim() + return text ? text : undefined +} + +/** Build a short preview of tool input for debug logging. */ +function inputPreview(input: Record): string { + const parts: string[] = [] + for (const [key, val] of Object.entries(input)) { + if (typeof val === 'string') { + parts.push(`${key}="${val.slice(0, 100)}"`) + } + if (parts.length >= 3) break + } + return parts.join(' ') +} + +export function createSessionSpawner(deps: SessionSpawnerDeps): SessionSpawner { + return { + spawn(opts: SessionSpawnOpts, dir: string): SessionHandle { + // Debug file resolution: + // 1. If deps.debugFile is provided, use it with session ID suffix for uniqueness + // 2. If verbose or ant build, auto-generate a temp file path + // 3. Otherwise, no debug file + const safeId = safeFilenameId(opts.sessionId) + let debugFile: string | undefined + if (deps.debugFile) { + const ext = deps.debugFile.lastIndexOf('.') + if (ext > 0) { + debugFile = `${deps.debugFile.slice(0, ext)}-${safeId}${deps.debugFile.slice(ext)}` + } else { + debugFile = `${deps.debugFile}-${safeId}` + } + } else if (deps.verbose || process.env.USER_TYPE === 'ant') { + debugFile = join(tmpdir(), 'claude', `bridge-session-${safeId}.log`) + } + + // Transcript file: write raw NDJSON lines for post-hoc analysis. + // Placed alongside the debug file when one is configured. + let transcriptStream: WriteStream | null = null + let transcriptPath: string | undefined + if (deps.debugFile) { + transcriptPath = join( + dirname(deps.debugFile), + `bridge-transcript-${safeId}.jsonl`, + ) + transcriptStream = createWriteStream(transcriptPath, { flags: 'a' }) + transcriptStream.on('error', err => { + deps.onDebug( + `[bridge:session] Transcript write error: ${err.message}`, + ) + transcriptStream = null + }) + deps.onDebug(`[bridge:session] Transcript log: ${transcriptPath}`) + } + + const args = [ + ...deps.scriptArgs, + '--print', + '--sdk-url', + opts.sdkUrl, + '--session-id', + opts.sessionId, + '--input-format', + 'stream-json', + '--output-format', + 'stream-json', + '--replay-user-messages', + ...(deps.verbose ? ['--verbose'] : []), + ...(debugFile ? ['--debug-file', debugFile] : []), + ...(deps.permissionMode + ? ['--permission-mode', deps.permissionMode] + : []), + ] + + const env: NodeJS.ProcessEnv = { + ...deps.env, + // Strip the bridge's OAuth token so the child CC process uses + // the session access token for inference instead. + CLAUDE_CODE_OAUTH_TOKEN: undefined, + CLAUDE_CODE_ENVIRONMENT_KIND: 'bridge', + ...(deps.sandbox && { CLAUDE_CODE_FORCE_SANDBOX: '1' }), + CLAUDE_CODE_SESSION_ACCESS_TOKEN: opts.accessToken, + // v1: HybridTransport (WS reads + POST writes) to Session-Ingress. + // Harmless in v2 mode — transportUtils checks CLAUDE_CODE_USE_CCR_V2 first. + CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2: '1', + // v2: SSETransport + CCRClient to CCR's /v1/code/sessions/* endpoints. + // Same env vars environment-manager sets in the container path. + ...(opts.useCcrV2 && { + CLAUDE_CODE_USE_CCR_V2: '1', + CLAUDE_CODE_WORKER_EPOCH: String(opts.workerEpoch), + }), + } + + deps.onDebug( + `[bridge:session] Spawning sessionId=${opts.sessionId} sdkUrl=${opts.sdkUrl} accessToken=${opts.accessToken ? 'present' : 'MISSING'}`, + ) + deps.onDebug(`[bridge:session] Child args: ${args.join(' ')}`) + if (debugFile) { + deps.onDebug(`[bridge:session] Debug log: ${debugFile}`) + } + + // Pipe all three streams: stdin for control, stdout for NDJSON parsing, + // stderr for error capture and diagnostics. + const child: ChildProcess = spawn(deps.execPath, args, { + cwd: dir, + stdio: ['pipe', 'pipe', 'pipe'], + env, + windowsHide: true, + }) + + deps.onDebug( + `[bridge:session] sessionId=${opts.sessionId} pid=${child.pid}`, + ) + + const activities: SessionActivity[] = [] + let currentActivity: SessionActivity | null = null + const lastStderr: string[] = [] + let sigkillSent = false + let firstUserMessageSeen = false + + // Buffer stderr for error diagnostics + if (child.stderr) { + const stderrRl = createInterface({ input: child.stderr }) + stderrRl.on('line', line => { + // Forward stderr to bridge's stderr in verbose mode + if (deps.verbose) { + process.stderr.write(line + '\n') + } + // Ring buffer of last N lines + if (lastStderr.length >= MAX_STDERR_LINES) { + lastStderr.shift() + } + lastStderr.push(line) + }) + } + + // Parse NDJSON from child stdout + if (child.stdout) { + const rl = createInterface({ input: child.stdout }) + rl.on('line', line => { + // Write raw NDJSON to transcript file + if (transcriptStream) { + transcriptStream.write(line + '\n') + } + + // Log all messages flowing from the child CLI to the bridge + deps.onDebug( + `[bridge:ws] sessionId=${opts.sessionId} <<< ${debugTruncate(line)}`, + ) + + // In verbose mode, forward raw output to stderr + if (deps.verbose) { + process.stderr.write(line + '\n') + } + + const extracted = extractActivities( + line, + opts.sessionId, + deps.onDebug, + ) + for (const activity of extracted) { + // Maintain ring buffer + if (activities.length >= MAX_ACTIVITIES) { + activities.shift() + } + activities.push(activity) + currentActivity = activity + + deps.onActivity?.(opts.sessionId, activity) + } + + // Detect control_request and replayed user messages. + // extractActivities parses the same line but swallows parse errors + // and skips 'user' type — re-parse here is cheap (NDJSON lines are + // small) and keeps each path self-contained. + { + let parsed: unknown + try { + parsed = jsonParse(line) + } catch { + // Non-JSON line, skip detection + } + if (parsed && typeof parsed === 'object') { + const msg = parsed as Record + + if (msg.type === 'control_request') { + const request = msg.request as + | Record + | undefined + if ( + request?.subtype === 'can_use_tool' && + deps.onPermissionRequest + ) { + deps.onPermissionRequest( + opts.sessionId, + parsed as PermissionRequest, + opts.accessToken, + ) + } + // interrupt is turn-level; the child handles it internally (print.ts) + } else if ( + msg.type === 'user' && + !firstUserMessageSeen && + opts.onFirstUserMessage + ) { + const text = extractUserMessageText(msg) + if (text) { + firstUserMessageSeen = true + opts.onFirstUserMessage(text) + } + } + } + } + }) + } + + const done = new Promise(resolve => { + child.on('close', (code, signal) => { + // Close transcript stream on exit + if (transcriptStream) { + transcriptStream.end() + transcriptStream = null + } + + if (signal === 'SIGTERM' || signal === 'SIGINT') { + deps.onDebug( + `[bridge:session] sessionId=${opts.sessionId} interrupted signal=${signal} pid=${child.pid}`, + ) + resolve('interrupted') + } else if (code === 0) { + deps.onDebug( + `[bridge:session] sessionId=${opts.sessionId} completed exit_code=0 pid=${child.pid}`, + ) + resolve('completed') + } else { + deps.onDebug( + `[bridge:session] sessionId=${opts.sessionId} failed exit_code=${code} pid=${child.pid}`, + ) + resolve('failed') + } + }) + + child.on('error', err => { + deps.onDebug( + `[bridge:session] sessionId=${opts.sessionId} spawn error: ${err.message}`, + ) + resolve('failed') + }) + }) + + const handle: SessionHandle = { + sessionId: opts.sessionId, + done, + activities, + accessToken: opts.accessToken, + lastStderr, + get currentActivity(): SessionActivity | null { + return currentActivity + }, + kill(): void { + if (!child.killed) { + deps.onDebug( + `[bridge:session] Sending SIGTERM to sessionId=${opts.sessionId} pid=${child.pid}`, + ) + // On Windows, child.kill('SIGTERM') throws; use default signal. + if (process.platform === 'win32') { + child.kill() + } else { + child.kill('SIGTERM') + } + } + }, + forceKill(): void { + // Use separate flag because child.killed is set when kill() is called, + // not when the process exits. We need to send SIGKILL even after SIGTERM. + if (!sigkillSent && child.pid) { + sigkillSent = true + deps.onDebug( + `[bridge:session] Sending SIGKILL to sessionId=${opts.sessionId} pid=${child.pid}`, + ) + if (process.platform === 'win32') { + child.kill() + } else { + child.kill('SIGKILL') + } + } + }, + writeStdin(data: string): void { + if (child.stdin && !child.stdin.destroyed) { + deps.onDebug( + `[bridge:ws] sessionId=${opts.sessionId} >>> ${debugTruncate(data)}`, + ) + child.stdin.write(data) + } + }, + updateAccessToken(token: string): void { + handle.accessToken = token + // Send the fresh token to the child process via stdin. The child's + // StructuredIO handles update_environment_variables messages by + // setting process.env directly, so getSessionIngressAuthToken() + // picks up the new token on the next refreshHeaders call. + handle.writeStdin( + jsonStringify({ + type: 'update_environment_variables', + variables: { CLAUDE_CODE_SESSION_ACCESS_TOKEN: token }, + }) + '\n', + ) + deps.onDebug( + `[bridge:session] Sent token refresh via stdin for sessionId=${opts.sessionId}`, + ) + }, + } + + return handle + }, + } +} + +export { extractActivities as _extractActivitiesForTesting } diff --git a/bridge/trustedDevice.ts b/bridge/trustedDevice.ts new file mode 100644 index 0000000..a4bcf35 --- /dev/null +++ b/bridge/trustedDevice.ts @@ -0,0 +1,210 @@ +import axios from 'axios' +import memoize from 'lodash-es/memoize.js' +import { hostname } from 'os' +import { getOauthConfig } from '../constants/oauth.js' +import { + checkGate_CACHED_OR_BLOCKING, + getFeatureValue_CACHED_MAY_BE_STALE, +} from '../services/analytics/growthbook.js' +import { logForDebugging } from '../utils/debug.js' +import { errorMessage } from '../utils/errors.js' +import { isEssentialTrafficOnly } from '../utils/privacyLevel.js' +import { getSecureStorage } from '../utils/secureStorage/index.js' +import { jsonStringify } from '../utils/slowOperations.js' + +/** + * Trusted device token source for bridge (remote-control) sessions. + * + * Bridge sessions have SecurityTier=ELEVATED on the server (CCR v2). + * The server gates ConnectBridgeWorker on its own flag + * (sessions_elevated_auth_enforcement in Anthropic Main); this CLI-side + * flag controls whether the CLI sends X-Trusted-Device-Token at all. + * Two flags so rollout can be staged: flip CLI-side first (headers + * start flowing, server still no-ops), then flip server-side. + * + * Enrollment (POST /auth/trusted_devices) is gated server-side by + * account_session.created_at < 10min, so it must happen during /login. + * Token is persistent (90d rolling expiry) and stored in keychain. + * + * See anthropics/anthropic#274559 (spec), #310375 (B1b tenant RPCs), + * #295987 (B2 Python routes), #307150 (C1' CCR v2 gate). + */ + +const TRUSTED_DEVICE_GATE = 'tengu_sessions_elevated_auth_enforcement' + +function isGateEnabled(): boolean { + return getFeatureValue_CACHED_MAY_BE_STALE(TRUSTED_DEVICE_GATE, false) +} + +// Memoized — secureStorage.read() spawns a macOS `security` subprocess (~40ms). +// bridgeApi.ts calls this from getHeaders() on every poll/heartbeat/ack. +// Cache cleared after enrollment (below) and on logout (clearAuthRelatedCaches). +// +// Only the storage read is memoized — the GrowthBook gate is checked live so +// that a gate flip after GrowthBook refresh takes effect without a restart. +const readStoredToken = memoize((): string | undefined => { + // Env var takes precedence for testing/canary. + const envToken = process.env.CLAUDE_TRUSTED_DEVICE_TOKEN + if (envToken) { + return envToken + } + return getSecureStorage().read()?.trustedDeviceToken +}) + +export function getTrustedDeviceToken(): string | undefined { + if (!isGateEnabled()) { + return undefined + } + return readStoredToken() +} + +export function clearTrustedDeviceTokenCache(): void { + readStoredToken.cache?.clear?.() +} + +/** + * Clear the stored trusted device token from secure storage and the memo cache. + * Called before enrollTrustedDevice() during /login so a stale token from the + * previous account isn't sent as X-Trusted-Device-Token while enrollment is + * in-flight (enrollTrustedDevice is async — bridge API calls between login and + * enrollment completion would otherwise still read the old cached token). + */ +export function clearTrustedDeviceToken(): void { + if (!isGateEnabled()) { + return + } + const secureStorage = getSecureStorage() + try { + const data = secureStorage.read() + if (data?.trustedDeviceToken) { + delete data.trustedDeviceToken + secureStorage.update(data) + } + } catch { + // Best-effort — don't block login if storage is inaccessible + } + readStoredToken.cache?.clear?.() +} + +/** + * Enroll this device via POST /auth/trusted_devices and persist the token + * to keychain. Best-effort — logs and returns on failure so callers + * (post-login hooks) don't block the login flow. + * + * The server gates enrollment on account_session.created_at < 10min, so + * this must be called immediately after a fresh /login. Calling it later + * (e.g. lazy enrollment on /bridge 403) will fail with 403 stale_session. + */ +export async function enrollTrustedDevice(): Promise { + try { + // checkGate_CACHED_OR_BLOCKING awaits any in-flight GrowthBook re-init + // (triggered by refreshGrowthBookAfterAuthChange in login.tsx) before + // reading the gate, so we get the post-refresh value. + if (!(await checkGate_CACHED_OR_BLOCKING(TRUSTED_DEVICE_GATE))) { + logForDebugging( + `[trusted-device] Gate ${TRUSTED_DEVICE_GATE} is off, skipping enrollment`, + ) + return + } + // If CLAUDE_TRUSTED_DEVICE_TOKEN is set (e.g. by an enterprise wrapper), + // skip enrollment — the env var takes precedence in readStoredToken() so + // any enrolled token would be shadowed and never used. + if (process.env.CLAUDE_TRUSTED_DEVICE_TOKEN) { + logForDebugging( + '[trusted-device] CLAUDE_TRUSTED_DEVICE_TOKEN env var is set, skipping enrollment (env var takes precedence)', + ) + return + } + // Lazy require — utils/auth.ts transitively pulls ~1300 modules + // (config → file → permissions → sessionStorage → commands). Daemon callers + // of getTrustedDeviceToken() don't need this; only /login does. + /* eslint-disable @typescript-eslint/no-require-imports */ + const { getClaudeAIOAuthTokens } = + require('../utils/auth.js') as typeof import('../utils/auth.js') + /* eslint-enable @typescript-eslint/no-require-imports */ + const accessToken = getClaudeAIOAuthTokens()?.accessToken + if (!accessToken) { + logForDebugging('[trusted-device] No OAuth token, skipping enrollment') + return + } + // Always re-enroll on /login — the existing token may belong to a + // different account (account-switch without /logout). Skipping enrollment + // would send the old account's token on the new account's bridge calls. + const secureStorage = getSecureStorage() + + if (isEssentialTrafficOnly()) { + logForDebugging( + '[trusted-device] Essential traffic only, skipping enrollment', + ) + return + } + + const baseUrl = getOauthConfig().BASE_API_URL + let response + try { + response = await axios.post<{ + device_token?: string + device_id?: string + }>( + `${baseUrl}/api/auth/trusted_devices`, + { display_name: `Claude Code on ${hostname()} · ${process.platform}` }, + { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + timeout: 10_000, + validateStatus: s => s < 500, + }, + ) + } catch (err: unknown) { + logForDebugging( + `[trusted-device] Enrollment request failed: ${errorMessage(err)}`, + ) + return + } + + if (response.status !== 200 && response.status !== 201) { + logForDebugging( + `[trusted-device] Enrollment failed ${response.status}: ${jsonStringify(response.data).slice(0, 200)}`, + ) + return + } + + const token = response.data?.device_token + if (!token || typeof token !== 'string') { + logForDebugging( + '[trusted-device] Enrollment response missing device_token field', + ) + return + } + + try { + const storageData = secureStorage.read() + if (!storageData) { + logForDebugging( + '[trusted-device] Cannot read storage, skipping token persist', + ) + return + } + storageData.trustedDeviceToken = token + const result = secureStorage.update(storageData) + if (!result.success) { + logForDebugging( + `[trusted-device] Failed to persist token: ${result.warning ?? 'unknown'}`, + ) + return + } + readStoredToken.cache?.clear?.() + logForDebugging( + `[trusted-device] Enrolled device_id=${response.data.device_id ?? 'unknown'}`, + ) + } catch (err: unknown) { + logForDebugging( + `[trusted-device] Storage write failed: ${errorMessage(err)}`, + ) + } + } catch (err: unknown) { + logForDebugging(`[trusted-device] Enrollment error: ${errorMessage(err)}`) + } +} diff --git a/bridge/types.ts b/bridge/types.ts new file mode 100644 index 0000000..210a3bb --- /dev/null +++ b/bridge/types.ts @@ -0,0 +1,262 @@ +/** Default per-session timeout (24 hours). */ +export const DEFAULT_SESSION_TIMEOUT_MS = 24 * 60 * 60 * 1000 + +/** Reusable login guidance appended to bridge auth errors. */ +export const BRIDGE_LOGIN_INSTRUCTION = + 'Remote Control is only available with claude.ai subscriptions. Please use `/login` to sign in with your claude.ai account.' + +/** Full error printed when `claude remote-control` is run without auth. */ +export const BRIDGE_LOGIN_ERROR = + 'Error: You must be logged in to use Remote Control.\n\n' + + BRIDGE_LOGIN_INSTRUCTION + +/** Shown when the user disconnects Remote Control (via /remote-control or ultraplan launch). */ +export const REMOTE_CONTROL_DISCONNECTED_MSG = 'Remote Control disconnected.' + +// --- Protocol types for the environments API --- + +export type WorkData = { + type: 'session' | 'healthcheck' + id: string +} + +export type WorkResponse = { + id: string + type: 'work' + environment_id: string + state: string + data: WorkData + secret: string // base64url-encoded JSON + created_at: string +} + +export type WorkSecret = { + version: number + session_ingress_token: string + api_base_url: string + sources: Array<{ + type: string + git_info?: { type: string; repo: string; ref?: string; token?: string } + }> + auth: Array<{ type: string; token: string }> + claude_code_args?: Record | null + mcp_config?: unknown | null + environment_variables?: Record | null + /** + * Server-driven CCR v2 selector. Set by prepare_work_secret() when the + * session was created via the v2 compat layer (ccr_v2_compat_enabled). + * Same field the BYOC runner reads at environment-runner/sessionExecutor.ts. + */ + use_code_sessions?: boolean +} + +export type SessionDoneStatus = 'completed' | 'failed' | 'interrupted' + +export type SessionActivityType = 'tool_start' | 'text' | 'result' | 'error' + +export type SessionActivity = { + type: SessionActivityType + summary: string // e.g. "Editing src/foo.ts", "Reading package.json" + timestamp: number +} + +/** + * How `claude remote-control` chooses session working directories. + * - `single-session`: one session in cwd, bridge tears down when it ends + * - `worktree`: persistent server, every session gets an isolated git worktree + * - `same-dir`: persistent server, every session shares cwd (can stomp each other) + */ +export type SpawnMode = 'single-session' | 'worktree' | 'same-dir' + +/** + * Well-known worker_type values THIS codebase produces. Sent as + * `metadata.worker_type` at environment registration so claude.ai can filter + * the session picker by origin (e.g. assistant tab only shows assistant + * workers). The backend treats this as an opaque string — desktop cowork + * sends `"cowork"`, which isn't in this union. REPL code uses this narrow + * type for its own exhaustiveness; wire-level fields accept any string. + */ +export type BridgeWorkerType = 'claude_code' | 'claude_code_assistant' + +export type BridgeConfig = { + dir: string + machineName: string + branch: string + gitRepoUrl: string | null + maxSessions: number + spawnMode: SpawnMode + verbose: boolean + sandbox: boolean + /** Client-generated UUID identifying this bridge instance. */ + bridgeId: string + /** + * Sent as metadata.worker_type so web clients can filter by origin. + * Backend treats this as opaque — any string, not just BridgeWorkerType. + */ + workerType: string + /** Client-generated UUID for idempotent environment registration. */ + environmentId: string + /** + * Backend-issued environment_id to reuse on re-register. When set, the + * backend treats registration as a reconnect to the existing environment + * instead of creating a new one. Used by `claude remote-control + * --session-id` resume. Must be a backend-format ID — client UUIDs are + * rejected with 400. + */ + reuseEnvironmentId?: string + /** API base URL the bridge is connected to (used for polling). */ + apiBaseUrl: string + /** Session ingress base URL for WebSocket connections (may differ from apiBaseUrl locally). */ + sessionIngressUrl: string + /** Debug file path passed via --debug-file. */ + debugFile?: string + /** Per-session timeout in milliseconds. Sessions exceeding this are killed. */ + sessionTimeoutMs?: number +} + +// --- Dependency interfaces (for testability) --- + +/** + * A control_response event sent back to a session (e.g. a permission decision). + * The `subtype` is `'success'` per the SDK protocol; the inner `response` + * carries the permission decision payload (e.g. `{ behavior: 'allow' }`). + */ +export type PermissionResponseEvent = { + type: 'control_response' + response: { + subtype: 'success' + request_id: string + response: Record + } +} + +export type BridgeApiClient = { + registerBridgeEnvironment(config: BridgeConfig): Promise<{ + environment_id: string + environment_secret: string + }> + pollForWork( + environmentId: string, + environmentSecret: string, + signal?: AbortSignal, + reclaimOlderThanMs?: number, + ): Promise + acknowledgeWork( + environmentId: string, + workId: string, + sessionToken: string, + ): Promise + /** Stop a work item via the environments API. */ + stopWork(environmentId: string, workId: string, force: boolean): Promise + /** Deregister/delete the bridge environment on graceful shutdown. */ + deregisterEnvironment(environmentId: string): Promise + /** Send a permission response (control_response) to a session via the session events API. */ + sendPermissionResponseEvent( + sessionId: string, + event: PermissionResponseEvent, + sessionToken: string, + ): Promise + /** Archive a session so it no longer appears as active on the server. */ + archiveSession(sessionId: string): Promise + /** + * Force-stop stale worker instances and re-queue a session on an environment. + * Used by `--session-id` to resume a session after the original bridge died. + */ + reconnectSession(environmentId: string, sessionId: string): Promise + /** + * Send a lightweight heartbeat for an active work item, extending its lease. + * Uses SessionIngressAuth (JWT, no DB hit) instead of EnvironmentSecretAuth. + * Returns the server's response with lease status. + */ + heartbeatWork( + environmentId: string, + workId: string, + sessionToken: string, + ): Promise<{ lease_extended: boolean; state: string }> +} + +export type SessionHandle = { + sessionId: string + done: Promise + kill(): void + forceKill(): void + activities: SessionActivity[] // ring buffer of recent activities (last ~10) + currentActivity: SessionActivity | null // most recent + accessToken: string // session_ingress_token for API calls + lastStderr: string[] // ring buffer of last stderr lines + writeStdin(data: string): void // write directly to child stdin + /** Update the access token for a running session (e.g. after token refresh). */ + updateAccessToken(token: string): void +} + +export type SessionSpawnOpts = { + sessionId: string + sdkUrl: string + accessToken: string + /** When true, spawn the child with CCR v2 env vars (SSE transport + CCRClient). */ + useCcrV2?: boolean + /** Required when useCcrV2 is true. Obtained from POST /worker/register. */ + workerEpoch?: number + /** + * Fires once with the text of the first real user message seen on the + * child's stdout (via --replay-user-messages). Lets the caller derive a + * session title when none exists yet. Tool-result and synthetic user + * messages are skipped. + */ + onFirstUserMessage?: (text: string) => void +} + +export type SessionSpawner = { + spawn(opts: SessionSpawnOpts, dir: string): SessionHandle +} + +export type BridgeLogger = { + printBanner(config: BridgeConfig, environmentId: string): void + logSessionStart(sessionId: string, prompt: string): void + logSessionComplete(sessionId: string, durationMs: number): void + logSessionFailed(sessionId: string, error: string): void + logStatus(message: string): void + logVerbose(message: string): void + logError(message: string): void + /** Log a reconnection success event after recovering from connection errors. */ + logReconnected(disconnectedMs: number): void + /** Show idle status with repo/branch info and shimmer animation. */ + updateIdleStatus(): void + /** Show reconnecting status in the live display. */ + updateReconnectingStatus(delayStr: string, elapsedStr: string): void + updateSessionStatus( + sessionId: string, + elapsed: string, + activity: SessionActivity, + trail: string[], + ): void + clearStatus(): void + /** Set repository info for status line display. */ + setRepoInfo(repoName: string, branch: string): void + /** Set debug log glob shown above the status line (ant users). */ + setDebugLogPath(path: string): void + /** Transition to "Attached" state when a session starts. */ + setAttached(sessionId: string): void + /** Show failed status in the live display. */ + updateFailedStatus(error: string): void + /** Toggle QR code visibility. */ + toggleQr(): void + /** Update the " of sessions" indicator and spawn mode hint. */ + updateSessionCount(active: number, max: number, mode: SpawnMode): void + /** Update the spawn mode shown in the session-count line. Pass null to hide (single-session or toggle unavailable). */ + setSpawnModeDisplay(mode: 'same-dir' | 'worktree' | null): void + /** Register a new session for multi-session display (called after spawn succeeds). */ + addSession(sessionId: string, url: string): void + /** Update the per-session activity summary (tool being run) in the multi-session list. */ + updateSessionActivity(sessionId: string, activity: SessionActivity): void + /** + * Set a session's display title. In multi-session mode, updates the bullet list + * entry. In single-session mode, also shows the title in the main status line. + * Triggers a render (guarded against reconnecting/failed states). + */ + setSessionTitle(sessionId: string, title: string): void + /** Remove a session from the multi-session display when it ends. */ + removeSession(sessionId: string): void + /** Force a re-render of the status display (for multi-session activity refresh). */ + refreshDisplay(): void +} diff --git a/bridge/workSecret.ts b/bridge/workSecret.ts new file mode 100644 index 0000000..bbc9373 --- /dev/null +++ b/bridge/workSecret.ts @@ -0,0 +1,127 @@ +import axios from 'axios' +import { jsonParse, jsonStringify } from '../utils/slowOperations.js' +import type { WorkSecret } from './types.js' + +/** Decode a base64url-encoded work secret and validate its version. */ +export function decodeWorkSecret(secret: string): WorkSecret { + const json = Buffer.from(secret, 'base64url').toString('utf-8') + const parsed: unknown = jsonParse(json) + if ( + !parsed || + typeof parsed !== 'object' || + !('version' in parsed) || + parsed.version !== 1 + ) { + throw new Error( + `Unsupported work secret version: ${parsed && typeof parsed === 'object' && 'version' in parsed ? parsed.version : 'unknown'}`, + ) + } + const obj = parsed as Record + if ( + typeof obj.session_ingress_token !== 'string' || + obj.session_ingress_token.length === 0 + ) { + throw new Error( + 'Invalid work secret: missing or empty session_ingress_token', + ) + } + if (typeof obj.api_base_url !== 'string') { + throw new Error('Invalid work secret: missing api_base_url') + } + return parsed as WorkSecret +} + +/** + * Build a WebSocket SDK URL from the API base URL and session ID. + * Strips the HTTP(S) protocol and constructs a ws(s):// ingress URL. + * + * Uses /v2/ for localhost (direct to session-ingress, no Envoy rewrite) + * and /v1/ for production (Envoy rewrites /v1/ → /v2/). + */ +export function buildSdkUrl(apiBaseUrl: string, sessionId: string): string { + const isLocalhost = + apiBaseUrl.includes('localhost') || apiBaseUrl.includes('127.0.0.1') + const protocol = isLocalhost ? 'ws' : 'wss' + const version = isLocalhost ? 'v2' : 'v1' + const host = apiBaseUrl.replace(/^https?:\/\//, '').replace(/\/+$/, '') + return `${protocol}://${host}/${version}/session_ingress/ws/${sessionId}` +} + +/** + * Compare two session IDs regardless of their tagged-ID prefix. + * + * Tagged IDs have the form {tag}_{body} or {tag}_staging_{body}, where the + * body encodes a UUID. CCR v2's compat layer returns `session_*` to v1 API + * clients (compat/convert.go:41) but the infrastructure layer (sandbox-gateway + * work queue, work poll response) uses `cse_*` (compat/CLAUDE.md:13). Both + * have the same underlying UUID. + * + * Without this, replBridge rejects its own session as "foreign" at the + * work-received check when the ccr_v2_compat_enabled gate is on. + */ +export function sameSessionId(a: string, b: string): boolean { + if (a === b) return true + // The body is everything after the last underscore — this handles both + // `{tag}_{body}` and `{tag}_staging_{body}`. + const aBody = a.slice(a.lastIndexOf('_') + 1) + const bBody = b.slice(b.lastIndexOf('_') + 1) + // Guard against IDs with no underscore (bare UUIDs): lastIndexOf returns -1, + // slice(0) returns the whole string, and we already checked a === b above. + // Require a minimum length to avoid accidental matches on short suffixes + // (e.g. single-char tag remnants from malformed IDs). + return aBody.length >= 4 && aBody === bBody +} + +/** + * Build a CCR v2 session URL from the API base URL and session ID. + * Unlike buildSdkUrl, this returns an HTTP(S) URL (not ws://) and points at + * /v1/code/sessions/{id} — the child CC will derive the SSE stream path + * and worker endpoints from this base. + */ +export function buildCCRv2SdkUrl( + apiBaseUrl: string, + sessionId: string, +): string { + const base = apiBaseUrl.replace(/\/+$/, '') + return `${base}/v1/code/sessions/${sessionId}` +} + +/** + * Register this bridge as the worker for a CCR v2 session. + * Returns the worker_epoch, which must be passed to the child CC process + * so its CCRClient can include it in every heartbeat/state/event request. + * + * Mirrors what environment-manager does in the container path + * (api-go/environment-manager/cmd/cmd_task_run.go RegisterWorker). + */ +export async function registerWorker( + sessionUrl: string, + accessToken: string, +): Promise { + const response = await axios.post( + `${sessionUrl}/worker/register`, + {}, + { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + 'anthropic-version': '2023-06-01', + }, + timeout: 10_000, + }, + ) + // protojson serializes int64 as a string to avoid JS number precision loss; + // the Go side may also return a number depending on encoder settings. + const raw = response.data?.worker_epoch + const epoch = typeof raw === 'string' ? Number(raw) : raw + if ( + typeof epoch !== 'number' || + !Number.isFinite(epoch) || + !Number.isSafeInteger(epoch) + ) { + throw new Error( + `registerWorker: invalid worker_epoch in response: ${jsonStringify(response.data)}`, + ) + } + return epoch +} diff --git a/buddy/CompanionSprite.tsx b/buddy/CompanionSprite.tsx new file mode 100644 index 0000000..f7f1f72 --- /dev/null +++ b/buddy/CompanionSprite.tsx @@ -0,0 +1,371 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import figures from 'figures'; +import React, { useEffect, useRef, useState } from 'react'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { stringWidth } from '../ink/stringWidth.js'; +import { Box, Text } from '../ink.js'; +import { useAppState, useSetAppState } from '../state/AppState.js'; +import type { AppState } from '../state/AppStateStore.js'; +import { getGlobalConfig } from '../utils/config.js'; +import { isFullscreenActive } from '../utils/fullscreen.js'; +import type { Theme } from '../utils/theme.js'; +import { getCompanion } from './companion.js'; +import { renderFace, renderSprite, spriteFrameCount } from './sprites.js'; +import { RARITY_COLORS } from './types.js'; +const TICK_MS = 500; +const BUBBLE_SHOW = 20; // ticks → ~10s at 500ms +const FADE_WINDOW = 6; // last ~3s the bubble dims so you know it's about to go +const PET_BURST_MS = 2500; // how long hearts float after /buddy pet + +// Idle sequence: mostly rest (frame 0), occasional fidget (frames 1-2), rare blink. +// Sequence indices map to sprite frames; -1 means "blink on frame 0". +const IDLE_SEQUENCE = [0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 2, 0, 0, 0]; + +// Hearts float up-and-out over 5 ticks (~2.5s). Prepended above the sprite. +const H = figures.heart; +const PET_HEARTS = [` ${H} ${H} `, ` ${H} ${H} ${H} `, ` ${H} ${H} ${H} `, `${H} ${H} ${H} `, '· · · ']; +function wrap(text: string, width: number): string[] { + const words = text.split(' '); + const lines: string[] = []; + let cur = ''; + for (const w of words) { + if (cur.length + w.length + 1 > width && cur) { + lines.push(cur); + cur = w; + } else { + cur = cur ? `${cur} ${w}` : w; + } + } + if (cur) lines.push(cur); + return lines; +} +function SpeechBubble(t0) { + const $ = _c(31); + const { + text, + color, + fading, + tail + } = t0; + let T0; + let borderColor; + let t1; + let t2; + let t3; + let t4; + let t5; + let t6; + if ($[0] !== color || $[1] !== fading || $[2] !== text) { + const lines = wrap(text, 30); + borderColor = fading ? "inactive" : color; + T0 = Box; + t1 = "column"; + t2 = "round"; + t3 = borderColor; + t4 = 1; + t5 = 34; + let t7; + if ($[11] !== fading) { + t7 = (l, i) => {l}; + $[11] = fading; + $[12] = t7; + } else { + t7 = $[12]; + } + t6 = lines.map(t7); + $[0] = color; + $[1] = fading; + $[2] = text; + $[3] = T0; + $[4] = borderColor; + $[5] = t1; + $[6] = t2; + $[7] = t3; + $[8] = t4; + $[9] = t5; + $[10] = t6; + } else { + T0 = $[3]; + borderColor = $[4]; + t1 = $[5]; + t2 = $[6]; + t3 = $[7]; + t4 = $[8]; + t5 = $[9]; + t6 = $[10]; + } + let t7; + if ($[13] !== T0 || $[14] !== t1 || $[15] !== t2 || $[16] !== t3 || $[17] !== t4 || $[18] !== t5 || $[19] !== t6) { + t7 = {t6}; + $[13] = T0; + $[14] = t1; + $[15] = t2; + $[16] = t3; + $[17] = t4; + $[18] = t5; + $[19] = t6; + $[20] = t7; + } else { + t7 = $[20]; + } + const bubble = t7; + if (tail === "right") { + let t8; + if ($[21] !== borderColor) { + t8 = ; + $[21] = borderColor; + $[22] = t8; + } else { + t8 = $[22]; + } + let t9; + if ($[23] !== bubble || $[24] !== t8) { + t9 = {bubble}{t8}; + $[23] = bubble; + $[24] = t8; + $[25] = t9; + } else { + t9 = $[25]; + } + return t9; + } + let t8; + if ($[26] !== borderColor) { + t8 = ; + $[26] = borderColor; + $[27] = t8; + } else { + t8 = $[27]; + } + let t9; + if ($[28] !== bubble || $[29] !== t8) { + t9 = {bubble}{t8}; + $[28] = bubble; + $[29] = t8; + $[30] = t9; + } else { + t9 = $[30]; + } + return t9; +} +export const MIN_COLS_FOR_FULL_SPRITE = 100; +const SPRITE_BODY_WIDTH = 12; +const NAME_ROW_PAD = 2; // focused state wraps name in spaces: ` name ` +const SPRITE_PADDING_X = 2; +const BUBBLE_WIDTH = 36; // SpeechBubble box (34) + tail column +const NARROW_QUIP_CAP = 24; +function spriteColWidth(nameWidth: number): number { + return Math.max(SPRITE_BODY_WIDTH, nameWidth + NAME_ROW_PAD); +} + +// Width the sprite area consumes. PromptInput subtracts this so text wraps +// correctly. In fullscreen the bubble floats over scrollback (no extra +// width); in non-fullscreen it sits inline and needs BUBBLE_WIDTH more. +// Narrow terminals: 0 — REPL.tsx stacks the one-liner on its own row +// (above input in fullscreen, below in scrollback), so no reservation. +export function companionReservedColumns(terminalColumns: number, speaking: boolean): number { + if (!feature('BUDDY')) return 0; + const companion = getCompanion(); + if (!companion || getGlobalConfig().companionMuted) return 0; + if (terminalColumns < MIN_COLS_FOR_FULL_SPRITE) return 0; + const nameWidth = stringWidth(companion.name); + const bubble = speaking && !isFullscreenActive() ? BUBBLE_WIDTH : 0; + return spriteColWidth(nameWidth) + SPRITE_PADDING_X + bubble; +} +export function CompanionSprite(): React.ReactNode { + const reaction = useAppState(s => s.companionReaction); + const petAt = useAppState(s => s.companionPetAt); + const focused = useAppState(s => s.footerSelection === 'companion'); + const setAppState = useSetAppState(); + const { + columns + } = useTerminalSize(); + const [tick, setTick] = useState(0); + const lastSpokeTick = useRef(0); + // Sync-during-render (not useEffect) so the first post-pet render already + // has petStartTick=tick and petAge=0 — otherwise frame 0 is skipped. + const [{ + petStartTick, + forPetAt + }, setPetStart] = useState({ + petStartTick: 0, + forPetAt: petAt + }); + if (petAt !== forPetAt) { + setPetStart({ + petStartTick: tick, + forPetAt: petAt + }); + } + useEffect(() => { + const timer = setInterval(setT => setT((t: number) => t + 1), TICK_MS, setTick); + return () => clearInterval(timer); + }, []); + useEffect(() => { + if (!reaction) return; + lastSpokeTick.current = tick; + const timer = setTimeout(setA => setA((prev: AppState) => prev.companionReaction === undefined ? prev : { + ...prev, + companionReaction: undefined + }), BUBBLE_SHOW * TICK_MS, setAppState); + return () => clearTimeout(timer); + // eslint-disable-next-line react-hooks/exhaustive-deps -- tick intentionally captured at reaction-change, not tracked + }, [reaction, setAppState]); + if (!feature('BUDDY')) return null; + const companion = getCompanion(); + if (!companion || getGlobalConfig().companionMuted) return null; + const color = RARITY_COLORS[companion.rarity]; + const colWidth = spriteColWidth(stringWidth(companion.name)); + const bubbleAge = reaction ? tick - lastSpokeTick.current : 0; + const fading = reaction !== undefined && bubbleAge >= BUBBLE_SHOW - FADE_WINDOW; + const petAge = petAt ? tick - petStartTick : Infinity; + const petting = petAge * TICK_MS < PET_BURST_MS; + + // Narrow terminals: collapse to one-line face. When speaking, the quip + // replaces the name beside the face (no room for a bubble). + if (columns < MIN_COLS_FOR_FULL_SPRITE) { + const quip = reaction && reaction.length > NARROW_QUIP_CAP ? reaction.slice(0, NARROW_QUIP_CAP - 1) + '…' : reaction; + const label = quip ? `"${quip}"` : focused ? ` ${companion.name} ` : companion.name; + return + + {petting && {figures.heart} } + + {renderFace(companion)} + {' '} + + {label} + + + ; + } + const frameCount = spriteFrameCount(companion.species); + const heartFrame = petting ? PET_HEARTS[petAge % PET_HEARTS.length] : null; + let spriteFrame: number; + let blink = false; + if (reaction || petting) { + // Excited: cycle all fidget frames fast + spriteFrame = tick % frameCount; + } else { + const step = IDLE_SEQUENCE[tick % IDLE_SEQUENCE.length]!; + if (step === -1) { + spriteFrame = 0; + blink = true; + } else { + spriteFrame = step % frameCount; + } + } + const body = renderSprite(companion, spriteFrame).map(line => blink ? line.replaceAll(companion.eye, '-') : line); + const sprite = heartFrame ? [heartFrame, ...body] : body; + + // Name row doubles as hint row — unfocused shows dim name + ↓ discovery, + // focused shows inverse name. The enter-to-open hint lives in + // PromptInputFooter's right column so this row stays one line and the + // sprite doesn't jump up when selected. flexShrink=0 stops the + // inline-bubble row wrapper from squeezing the sprite to fit. + const spriteColumn = + {sprite.map((line, i) => + {line} + )} + + {focused ? ` ${companion.name} ` : companion.name} + + ; + if (!reaction) { + return {spriteColumn}; + } + + // Fullscreen: bubble renders separately via CompanionFloatingBubble in + // FullscreenLayout's bottomFloat slot (the bottom slot's overflowY:hidden + // would clip a position:absolute overlay here). Sprite body only. + // Non-fullscreen: bubble sits inline beside the sprite (input shrinks) + // because floating into Static scrollback can't be cleared. + if (isFullscreenActive()) { + return {spriteColumn}; + } + return + + {spriteColumn} + ; +} + +// Floating bubble overlay for fullscreen mode. Mounted in FullscreenLayout's +// bottomFloat slot (outside the overflowY:hidden clip) so it can extend into +// the ScrollBox region. CompanionSprite owns the clear-after-10s timer; this +// just reads companionReaction and renders the fade. +export function CompanionFloatingBubble() { + const $ = _c(8); + const reaction = useAppState(_temp); + let t0; + if ($[0] !== reaction) { + t0 = { + tick: 0, + forReaction: reaction + }; + $[0] = reaction; + $[1] = t0; + } else { + t0 = $[1]; + } + const [t1, setTick] = useState(t0); + const { + tick, + forReaction + } = t1; + if (reaction !== forReaction) { + setTick({ + tick: 0, + forReaction: reaction + }); + } + let t2; + let t3; + if ($[2] !== reaction) { + t2 = () => { + if (!reaction) { + return; + } + const timer = setInterval(_temp3, TICK_MS, setTick); + return () => clearInterval(timer); + }; + t3 = [reaction]; + $[2] = reaction; + $[3] = t2; + $[4] = t3; + } else { + t2 = $[3]; + t3 = $[4]; + } + useEffect(t2, t3); + if (!feature("BUDDY") || !reaction) { + return null; + } + const companion = getCompanion(); + if (!companion || getGlobalConfig().companionMuted) { + return null; + } + const t4 = tick >= BUBBLE_SHOW - FADE_WINDOW; + let t5; + if ($[5] !== reaction || $[6] !== t4) { + t5 = ; + $[5] = reaction; + $[6] = t4; + $[7] = t5; + } else { + t5 = $[7]; + } + return t5; +} +function _temp3(set) { + return set(_temp2); +} +function _temp2(s_0) { + return { + ...s_0, + tick: s_0.tick + 1 + }; +} +function _temp(s) { + return s.companionReaction; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiZmlndXJlcyIsIlJlYWN0IiwidXNlRWZmZWN0IiwidXNlUmVmIiwidXNlU3RhdGUiLCJ1c2VUZXJtaW5hbFNpemUiLCJzdHJpbmdXaWR0aCIsIkJveCIsIlRleHQiLCJ1c2VBcHBTdGF0ZSIsInVzZVNldEFwcFN0YXRlIiwiQXBwU3RhdGUiLCJnZXRHbG9iYWxDb25maWciLCJpc0Z1bGxzY3JlZW5BY3RpdmUiLCJUaGVtZSIsImdldENvbXBhbmlvbiIsInJlbmRlckZhY2UiLCJyZW5kZXJTcHJpdGUiLCJzcHJpdGVGcmFtZUNvdW50IiwiUkFSSVRZX0NPTE9SUyIsIlRJQ0tfTVMiLCJCVUJCTEVfU0hPVyIsIkZBREVfV0lORE9XIiwiUEVUX0JVUlNUX01TIiwiSURMRV9TRVFVRU5DRSIsIkgiLCJoZWFydCIsIlBFVF9IRUFSVFMiLCJ3cmFwIiwidGV4dCIsIndpZHRoIiwid29yZHMiLCJzcGxpdCIsImxpbmVzIiwiY3VyIiwidyIsImxlbmd0aCIsInB1c2giLCJTcGVlY2hCdWJibGUiLCJ0MCIsIiQiLCJfYyIsImNvbG9yIiwiZmFkaW5nIiwidGFpbCIsIlQwIiwiYm9yZGVyQ29sb3IiLCJ0MSIsInQyIiwidDMiLCJ0NCIsInQ1IiwidDYiLCJ0NyIsImwiLCJpIiwidW5kZWZpbmVkIiwibWFwIiwiYnViYmxlIiwidDgiLCJ0OSIsIk1JTl9DT0xTX0ZPUl9GVUxMX1NQUklURSIsIlNQUklURV9CT0RZX1dJRFRIIiwiTkFNRV9ST1dfUEFEIiwiU1BSSVRFX1BBRERJTkdfWCIsIkJVQkJMRV9XSURUSCIsIk5BUlJPV19RVUlQX0NBUCIsInNwcml0ZUNvbFdpZHRoIiwibmFtZVdpZHRoIiwiTWF0aCIsIm1heCIsImNvbXBhbmlvblJlc2VydmVkQ29sdW1ucyIsInRlcm1pbmFsQ29sdW1ucyIsInNwZWFraW5nIiwiY29tcGFuaW9uIiwiY29tcGFuaW9uTXV0ZWQiLCJuYW1lIiwiQ29tcGFuaW9uU3ByaXRlIiwiUmVhY3ROb2RlIiwicmVhY3Rpb24iLCJzIiwiY29tcGFuaW9uUmVhY3Rpb24iLCJwZXRBdCIsImNvbXBhbmlvblBldEF0IiwiZm9jdXNlZCIsImZvb3RlclNlbGVjdGlvbiIsInNldEFwcFN0YXRlIiwiY29sdW1ucyIsInRpY2siLCJzZXRUaWNrIiwibGFzdFNwb2tlVGljayIsInBldFN0YXJ0VGljayIsImZvclBldEF0Iiwic2V0UGV0U3RhcnQiLCJ0aW1lciIsInNldEludGVydmFsIiwic2V0VCIsInQiLCJjbGVhckludGVydmFsIiwiY3VycmVudCIsInNldFRpbWVvdXQiLCJzZXRBIiwicHJldiIsImNsZWFyVGltZW91dCIsInJhcml0eSIsImNvbFdpZHRoIiwiYnViYmxlQWdlIiwicGV0QWdlIiwiSW5maW5pdHkiLCJwZXR0aW5nIiwicXVpcCIsInNsaWNlIiwibGFiZWwiLCJmcmFtZUNvdW50Iiwic3BlY2llcyIsImhlYXJ0RnJhbWUiLCJzcHJpdGVGcmFtZSIsImJsaW5rIiwic3RlcCIsImJvZHkiLCJsaW5lIiwicmVwbGFjZUFsbCIsImV5ZSIsInNwcml0ZSIsInNwcml0ZUNvbHVtbiIsIkNvbXBhbmlvbkZsb2F0aW5nQnViYmxlIiwiX3RlbXAiLCJmb3JSZWFjdGlvbiIsIl90ZW1wMyIsInNldCIsIl90ZW1wMiIsInNfMCJdLCJzb3VyY2VzIjpbIkNvbXBhbmlvblNwcml0ZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0IFJlYWN0LCB7IHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSwgdXNlU2V0QXBwU3RhdGUgfSBmcm9tICcuLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgQXBwU3RhdGUgfSBmcm9tICcuLi9zdGF0ZS9BcHBTdGF0ZVN0b3JlLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYmFsQ29uZmlnIH0gZnJvbSAnLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgaXNGdWxsc2NyZWVuQWN0aXZlIH0gZnJvbSAnLi4vdXRpbHMvZnVsbHNjcmVlbi5qcydcbmltcG9ydCB0eXBlIHsgVGhlbWUgfSBmcm9tICcuLi91dGlscy90aGVtZS5qcydcbmltcG9ydCB7IGdldENvbXBhbmlvbiB9IGZyb20gJy4vY29tcGFuaW9uLmpzJ1xuaW1wb3J0IHsgcmVuZGVyRmFjZSwgcmVuZGVyU3ByaXRlLCBzcHJpdGVGcmFtZUNvdW50IH0gZnJvbSAnLi9zcHJpdGVzLmpzJ1xuaW1wb3J0IHsgUkFSSVRZX0NPTE9SUyB9IGZyb20gJy4vdHlwZXMuanMnXG5cbmNvbnN0IFRJQ0tfTVMgPSA1MDBcbmNvbnN0IEJVQkJMRV9TSE9XID0gMjAgLy8gdGlja3Mg4oaSIH4xMHMgYXQgNTAwbXNcbmNvbnN0IEZBREVfV0lORE9XID0gNiAvLyBsYXN0IH4zcyB0aGUgYnViYmxlIGRpbXMgc28geW91IGtub3cgaXQncyBhYm91dCB0byBnb1xuY29uc3QgUEVUX0JVUlNUX01TID0gMjUwMCAvLyBob3cgbG9uZyBoZWFydHMgZmxvYXQgYWZ0ZXIgL2J1ZGR5IHBldFxuXG4vLyBJZGxlIHNlcXVlbmNlOiBtb3N0bHkgcmVzdCAoZnJhbWUgMCksIG9jY2FzaW9uYWwgZmlkZ2V0IChmcmFtZXMgMS0yKSwgcmFyZSBibGluay5cbi8vIFNlcXVlbmNlIGluZGljZXMgbWFwIHRvIHNwcml0ZSBmcmFtZXM7IC0xIG1lYW5zIFwiYmxpbmsgb24gZnJhbWUgMFwiLlxuY29uc3QgSURMRV9TRVFVRU5DRSA9IFswLCAwLCAwLCAwLCAxLCAwLCAwLCAwLCAtMSwgMCwgMCwgMiwgMCwgMCwgMF1cblxuLy8gSGVhcnRzIGZsb2F0IHVwLWFuZC1vdXQgb3ZlciA1IHRpY2tzICh+Mi41cykuIFByZXBlbmRlZCBhYm92ZSB0aGUgc3ByaXRlLlxuY29uc3QgSCA9IGZpZ3VyZXMuaGVhcnRcbmNvbnN0IFBFVF9IRUFSVFMgPSBbXG4gIGAgICAke0h9ICAgICR7SH0gICBgLFxuICBgICAke0h9ICAke0h9ICAgJHtIfSAgYCxcbiAgYCAke0h9ICAgJHtIfSAgJHtIfSAgIGAsXG4gIGAke0h9ICAke0h9ICAgICAgJHtIfSBgLFxuICAnwrcgICAgwrcgICDCtyAgJyxcbl1cblxuZnVuY3Rpb24gd3JhcCh0ZXh0OiBzdHJpbmcsIHdpZHRoOiBudW1iZXIpOiBzdHJpbmdbXSB7XG4gIGNvbnN0IHdvcmRzID0gdGV4dC5zcGxpdCgnICcpXG4gIGNvbnN0IGxpbmVzOiBzdHJpbmdbXSA9IFtdXG4gIGxldCBjdXIgPSAnJ1xuICBmb3IgKGNvbnN0IHcgb2Ygd29yZHMpIHtcbiAgICBpZiAoY3VyLmxlbmd0aCArIHcubGVuZ3RoICsgMSA+IHdpZHRoICYmIGN1cikge1xuICAgICAgbGluZXMucHVzaChjdXIpXG4gICAgICBjdXIgPSB3XG4gICAgfSBlbHNlIHtcbiAgICAgIGN1ciA9IGN1ciA/IGAke2N1cn0gJHt3fWAgOiB3XG4gICAgfVxuICB9XG4gIGlmIChjdXIpIGxpbmVzLnB1c2goY3VyKVxuICByZXR1cm4gbGluZXNcbn1cblxuZnVuY3Rpb24gU3BlZWNoQnViYmxlKHtcbiAgdGV4dCxcbiAgY29sb3IsXG4gIGZhZGluZyxcbiAgdGFpbCxcbn06IHtcbiAgdGV4dDogc3RyaW5nXG4gIGNvbG9yOiBrZXlvZiBUaGVtZVxuICBmYWRpbmc6IGJvb2xlYW5cbiAgdGFpbDogJ2Rvd24nIHwgJ3JpZ2h0J1xufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGxpbmVzID0gd3JhcCh0ZXh0LCAzMClcbiAgY29uc3QgYm9yZGVyQ29sb3IgPSBmYWRpbmcgPyAnaW5hY3RpdmUnIDogY29sb3JcbiAgY29uc3QgYnViYmxlID0gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgYm9yZGVyU3R5bGU9XCJyb3VuZFwiXG4gICAgICBib3JkZXJDb2xvcj17Ym9yZGVyQ29sb3J9XG4gICAgICBwYWRkaW5nWD17MX1cbiAgICAgIHdpZHRoPXszNH1cbiAgICA+XG4gICAgICB7bGluZXMubWFwKChsLCBpKSA9PiAoXG4gICAgICAgIDxUZXh0XG4gICAgICAgICAga2V5PXtpfVxuICAgICAgICAgIGl0YWxpY1xuICAgICAgICAgIGRpbUNvbG9yPXshZmFkaW5nfVxuICAgICAgICAgIGNvbG9yPXtmYWRpbmcgPyAnaW5hY3RpdmUnIDogdW5kZWZpbmVkfVxuICAgICAgICA+XG4gICAgICAgICAge2x9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICkpfVxuICAgIDwvQm94PlxuICApXG4gIGlmICh0YWlsID09PSAncmlnaHQnKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiIGFsaWduSXRlbXM9XCJjZW50ZXJcIj5cbiAgICAgICAge2J1YmJsZX1cbiAgICAgICAgPFRleHQgY29sb3I9e2JvcmRlckNvbG9yfT7ilIA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBhbGlnbkl0ZW1zPVwiZmxleC1lbmRcIiBtYXJnaW5SaWdodD17MX0+XG4gICAgICB7YnViYmxlfVxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgYWxpZ25JdGVtcz1cImZsZXgtZW5kXCIgcGFkZGluZ1JpZ2h0PXs2fT5cbiAgICAgICAgPFRleHQgY29sb3I9e2JvcmRlckNvbG9yfT7ilbIgPC9UZXh0PlxuICAgICAgICA8VGV4dCBjb2xvcj17Ym9yZGVyQ29sb3J9PuKVsjwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG5cbmV4cG9ydCBjb25zdCBNSU5fQ09MU19GT1JfRlVMTF9TUFJJVEUgPSAxMDBcbmNvbnN0IFNQUklURV9CT0RZX1dJRFRIID0gMTJcbmNvbnN0IE5BTUVfUk9XX1BBRCA9IDIgLy8gZm9jdXNlZCBzdGF0ZSB3cmFwcyBuYW1lIGluIHNwYWNlczogYCBuYW1lIGBcbmNvbnN0IFNQUklURV9QQURESU5HX1ggPSAyXG5jb25zdCBCVUJCTEVfV0lEVEggPSAzNiAvLyBTcGVlY2hCdWJibGUgYm94ICgzNCkgKyB0YWlsIGNvbHVtblxuY29uc3QgTkFSUk9XX1FVSVBfQ0FQID0gMjRcblxuZnVuY3Rpb24gc3ByaXRlQ29sV2lkdGgobmFtZVdpZHRoOiBudW1iZXIpOiBudW1iZXIge1xuICByZXR1cm4gTWF0aC5tYXgoU1BSSVRFX0JPRFlfV0lEVEgsIG5hbWVXaWR0aCArIE5BTUVfUk9XX1BBRClcbn1cblxuLy8gV2lkdGggdGhlIHNwcml0ZSBhcmVhIGNvbnN1bWVzLiBQcm9tcHRJbnB1dCBzdWJ0cmFjdHMgdGhpcyBzbyB0ZXh0IHdyYXBzXG4vLyBjb3JyZWN0bHkuIEluIGZ1bGxzY3JlZW4gdGhlIGJ1YmJsZSBmbG9hdHMgb3ZlciBzY3JvbGxiYWNrIChubyBleHRyYVxuLy8gd2lkdGgpOyBpbiBub24tZnVsbHNjcmVlbiBpdCBzaXRzIGlubGluZSBhbmQgbmVlZHMgQlVCQkxFX1dJRFRIIG1vcmUuXG4vLyBOYXJyb3cgdGVybWluYWxzOiAwIOKAlCBSRVBMLnRzeCBzdGFja3MgdGhlIG9uZS1saW5lciBvbiBpdHMgb3duIHJvd1xuLy8gKGFib3ZlIGlucHV0IGluIGZ1bGxzY3JlZW4sIGJlbG93IGluIHNjcm9sbGJhY2spLCBzbyBubyByZXNlcnZhdGlvbi5cbmV4cG9ydCBmdW5jdGlvbiBjb21wYW5pb25SZXNlcnZlZENvbHVtbnMoXG4gIHRlcm1pbmFsQ29sdW1uczogbnVtYmVyLFxuICBzcGVha2luZzogYm9vbGVhbixcbik6IG51bWJlciB7XG4gIGlmICghZmVhdHVyZSgnQlVERFknKSkgcmV0dXJuIDBcbiAgY29uc3QgY29tcGFuaW9uID0gZ2V0Q29tcGFuaW9uKClcbiAgaWYgKCFjb21wYW5pb24gfHwgZ2V0R2xvYmFsQ29uZmlnKCkuY29tcGFuaW9uTXV0ZWQpIHJldHVybiAwXG4gIGlmICh0ZXJtaW5hbENvbHVtbnMgPCBNSU5fQ09MU19GT1JfRlVMTF9TUFJJVEUpIHJldHVybiAwXG4gIGNvbnN0IG5hbWVXaWR0aCA9IHN0cmluZ1dpZHRoKGNvbXBhbmlvbi5uYW1lKVxuICBjb25zdCBidWJibGUgPSBzcGVha2luZyAmJiAhaXNGdWxsc2NyZWVuQWN0aXZlKCkgPyBCVUJCTEVfV0lEVEggOiAwXG4gIHJldHVybiBzcHJpdGVDb2xXaWR0aChuYW1lV2lkdGgpICsgU1BSSVRFX1BBRERJTkdfWCArIGJ1YmJsZVxufVxuXG5leHBvcnQgZnVuY3Rpb24gQ29tcGFuaW9uU3ByaXRlKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHJlYWN0aW9uID0gdXNlQXBwU3RhdGUocyA9PiBzLmNvbXBhbmlvblJlYWN0aW9uKVxuICBjb25zdCBwZXRBdCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5jb21wYW5pb25QZXRBdClcbiAgY29uc3QgZm9jdXNlZCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5mb290ZXJTZWxlY3Rpb24gPT09ICdjb21wYW5pb24nKVxuICBjb25zdCBzZXRBcHBTdGF0ZSA9IHVzZVNldEFwcFN0YXRlKClcbiAgY29uc3QgeyBjb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCBbdGljaywgc2V0VGlja10gPSB1c2VTdGF0ZSgwKVxuICBjb25zdCBsYXN0U3Bva2VUaWNrID0gdXNlUmVmKDApXG4gIC8vIFN5bmMtZHVyaW5nLXJlbmRlciAobm90IHVzZUVmZmVjdCkgc28gdGhlIGZpcnN0IHBvc3QtcGV0IHJlbmRlciBhbHJlYWR5XG4gIC8vIGhhcyBwZXRTdGFydFRpY2s9dGljayBhbmQgcGV0QWdlPTAg4oCUIG90aGVyd2lzZSBmcmFtZSAwIGlzIHNraXBwZWQuXG4gIGNvbnN0IFt7IHBldFN0YXJ0VGljaywgZm9yUGV0QXQgfSwgc2V0UGV0U3RhcnRdID0gdXNlU3RhdGUoe1xuICAgIHBldFN0YXJ0VGljazogMCxcbiAgICBmb3JQZXRBdDogcGV0QXQsXG4gIH0pXG4gIGlmIChwZXRBdCAhPT0gZm9yUGV0QXQpIHtcbiAgICBzZXRQZXRTdGFydCh7IHBldFN0YXJ0VGljazogdGljaywgZm9yUGV0QXQ6IHBldEF0IH0pXG4gIH1cblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IHRpbWVyID0gc2V0SW50ZXJ2YWwoXG4gICAgICBzZXRUID0+IHNldFQoKHQ6IG51bWJlcikgPT4gdCArIDEpLFxuICAgICAgVElDS19NUyxcbiAgICAgIHNldFRpY2ssXG4gICAgKVxuICAgIHJldHVybiAoKSA9PiBjbGVhckludGVydmFsKHRpbWVyKVxuICB9LCBbXSlcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghcmVhY3Rpb24pIHJldHVyblxuICAgIGxhc3RTcG9rZVRpY2suY3VycmVudCA9IHRpY2tcbiAgICBjb25zdCB0aW1lciA9IHNldFRpbWVvdXQoXG4gICAgICBzZXRBID0+XG4gICAgICAgIHNldEEoKHByZXY6IEFwcFN0YXRlKSA9PlxuICAgICAgICAgIHByZXYuY29tcGFuaW9uUmVhY3Rpb24gPT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgPyBwcmV2XG4gICAgICAgICAgICA6IHsgLi4ucHJldiwgY29tcGFuaW9uUmVhY3Rpb246IHVuZGVmaW5lZCB9LFxuICAgICAgICApLFxuICAgICAgQlVCQkxFX1NIT1cgKiBUSUNLX01TLFxuICAgICAgc2V0QXBwU3RhdGUsXG4gICAgKVxuICAgIHJldHVybiAoKSA9PiBjbGVhclRpbWVvdXQodGltZXIpXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwcyAtLSB0aWNrIGludGVudGlvbmFsbHkgY2FwdHVyZWQgYXQgcmVhY3Rpb24tY2hhbmdlLCBub3QgdHJhY2tlZFxuICB9LCBbcmVhY3Rpb24sIHNldEFwcFN0YXRlXSlcblxuICBpZiAoIWZlYXR1cmUoJ0JVRERZJykpIHJldHVybiBudWxsXG4gIGNvbnN0IGNvbXBhbmlvbiA9IGdldENvbXBhbmlvbigpXG4gIGlmICghY29tcGFuaW9uIHx8IGdldEdsb2JhbENvbmZpZygpLmNvbXBhbmlvbk11dGVkKSByZXR1cm4gbnVsbFxuXG4gIGNvbnN0IGNvbG9yID0gUkFSSVRZX0NPTE9SU1tjb21wYW5pb24ucmFyaXR5XVxuICBjb25zdCBjb2xXaWR0aCA9IHNwcml0ZUNvbFdpZHRoKHN0cmluZ1dpZHRoKGNvbXBhbmlvbi5uYW1lKSlcblxuICBjb25zdCBidWJibGVBZ2UgPSByZWFjdGlvbiA/IHRpY2sgLSBsYXN0U3Bva2VUaWNrLmN1cnJlbnQgOiAwXG4gIGNvbnN0IGZhZGluZyA9XG4gICAgcmVhY3Rpb24gIT09IHVuZGVmaW5lZCAmJiBidWJibGVBZ2UgPj0gQlVCQkxFX1NIT1cgLSBGQURFX1dJTkRPV1xuXG4gIGNvbnN0IHBldEFnZSA9IHBldEF0ID8gdGljayAtIHBldFN0YXJ0VGljayA6IEluZmluaXR5XG4gIGNvbnN0IHBldHRpbmcgPSBwZXRBZ2UgKiBUSUNLX01TIDwgUEVUX0JVUlNUX01TXG5cbiAgLy8gTmFycm93IHRlcm1pbmFsczogY29sbGFwc2UgdG8gb25lLWxpbmUgZmFjZS4gV2hlbiBzcGVha2luZywgdGhlIHF1aXBcbiAgLy8gcmVwbGFjZXMgdGhlIG5hbWUgYmVzaWRlIHRoZSBmYWNlIChubyByb29tIGZvciBhIGJ1YmJsZSkuXG4gIGlmIChjb2x1bW5zIDwgTUlOX0NPTFNfRk9SX0ZVTExfU1BSSVRFKSB7XG4gICAgY29uc3QgcXVpcCA9XG4gICAgICByZWFjdGlvbiAmJiByZWFjdGlvbi5sZW5ndGggPiBOQVJST1dfUVVJUF9DQVBcbiAgICAgICAgPyByZWFjdGlvbi5zbGljZSgwLCBOQVJST1dfUVVJUF9DQVAgLSAxKSArICfigKYnXG4gICAgICAgIDogcmVhY3Rpb25cbiAgICBjb25zdCBsYWJlbCA9IHF1aXBcbiAgICAgID8gYFwiJHtxdWlwfVwiYFxuICAgICAgOiBmb2N1c2VkXG4gICAgICAgID8gYCAke2NvbXBhbmlvbi5uYW1lfSBgXG4gICAgICAgIDogY29tcGFuaW9uLm5hbWVcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBwYWRkaW5nWD17MX0gYWxpZ25TZWxmPVwiZmxleC1lbmRcIj5cbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAge3BldHRpbmcgJiYgPFRleHQgY29sb3I9XCJhdXRvQWNjZXB0XCI+e2ZpZ3VyZXMuaGVhcnR9IDwvVGV4dD59XG4gICAgICAgICAgPFRleHQgYm9sZCBjb2xvcj17Y29sb3J9PlxuICAgICAgICAgICAge3JlbmRlckZhY2UoY29tcGFuaW9uKX1cbiAgICAgICAgICA8L1RleHQ+eycgJ31cbiAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgaXRhbGljXG4gICAgICAgICAgICBkaW1Db2xvcj17IWZvY3VzZWQgJiYgIXJlYWN0aW9ufVxuICAgICAgICAgICAgYm9sZD17Zm9jdXNlZH1cbiAgICAgICAgICAgIGludmVyc2U9e2ZvY3VzZWQgJiYgIXJlYWN0aW9ufVxuICAgICAgICAgICAgY29sb3I9e1xuICAgICAgICAgICAgICByZWFjdGlvblxuICAgICAgICAgICAgICAgID8gZmFkaW5nXG4gICAgICAgICAgICAgICAgICA/ICdpbmFjdGl2ZSdcbiAgICAgICAgICAgICAgICAgIDogY29sb3JcbiAgICAgICAgICAgICAgICA6IGZvY3VzZWRcbiAgICAgICAgICAgICAgICAgID8gY29sb3JcbiAgICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICB9XG4gICAgICAgICAgPlxuICAgICAgICAgICAge2xhYmVsfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG4gIGNvbnN0IGZyYW1lQ291bnQgPSBzcHJpdGVGcmFtZUNvdW50KGNvbXBhbmlvbi5zcGVjaWVzKVxuICBjb25zdCBoZWFydEZyYW1lID0gcGV0dGluZyA/IFBFVF9IRUFSVFNbcGV0QWdlICUgUEVUX0hFQVJUUy5sZW5ndGhdIDogbnVsbFxuXG4gIGxldCBzcHJpdGVGcmFtZTogbnVtYmVyXG4gIGxldCBibGluayA9IGZhbHNlXG4gIGlmIChyZWFjdGlvbiB8fCBwZXR0aW5nKSB7XG4gICAgLy8gRXhjaXRlZDogY3ljbGUgYWxsIGZpZGdldCBmcmFtZXMgZmFzdFxuICAgIHNwcml0ZUZyYW1lID0gdGljayAlIGZyYW1lQ291bnRcbiAgfSBlbHNlIHtcbiAgICBjb25zdCBzdGVwID0gSURMRV9TRVFVRU5DRVt0aWNrICUgSURMRV9TRVFVRU5DRS5sZW5ndGhdIVxuICAgIGlmIChzdGVwID09PSAtMSkge1xuICAgICAgc3ByaXRlRnJhbWUgPSAwXG4gICAgICBibGluayA9IHRydWVcbiAgICB9IGVsc2Uge1xuICAgICAgc3ByaXRlRnJhbWUgPSBzdGVwICUgZnJhbWVDb3VudFxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IGJvZHkgPSByZW5kZXJTcHJpdGUoY29tcGFuaW9uLCBzcHJpdGVGcmFtZSkubWFwKGxpbmUgPT5cbiAgICBibGluayA/IGxpbmUucmVwbGFjZUFsbChjb21wYW5pb24uZXllLCAnLScpIDogbGluZSxcbiAgKVxuICBjb25zdCBzcHJpdGUgPSBoZWFydEZyYW1lID8gW2hlYXJ0RnJhbWUsIC4uLmJvZHldIDogYm9keVxuXG4gIC8vIE5hbWUgcm93IGRvdWJsZXMgYXMgaGludCByb3cg4oCUIHVuZm9jdXNlZCBzaG93cyBkaW0gbmFtZSArIOKGkyBkaXNjb3ZlcnksXG4gIC8vIGZvY3VzZWQgc2hvd3MgaW52ZXJzZSBuYW1lLiBUaGUgZW50ZXItdG8tb3BlbiBoaW50IGxpdmVzIGluXG4gIC8vIFByb21wdElucHV0Rm9vdGVyJ3MgcmlnaHQgY29sdW1uIHNvIHRoaXMgcm93IHN0YXlzIG9uZSBsaW5lIGFuZCB0aGVcbiAgLy8gc3ByaXRlIGRvZXNuJ3QganVtcCB1cCB3aGVuIHNlbGVjdGVkLiBmbGV4U2hyaW5rPTAgc3RvcHMgdGhlXG4gIC8vIGlubGluZS1idWJibGUgcm93IHdyYXBwZXIgZnJvbSBzcXVlZXppbmcgdGhlIHNwcml0ZSB0byBmaXQuXG4gIGNvbnN0IHNwcml0ZUNvbHVtbiA9IChcbiAgICA8Qm94XG4gICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgIGZsZXhTaHJpbms9ezB9XG4gICAgICBhbGlnbkl0ZW1zPVwiY2VudGVyXCJcbiAgICAgIHdpZHRoPXtjb2xXaWR0aH1cbiAgICA+XG4gICAgICB7c3ByaXRlLm1hcCgobGluZSwgaSkgPT4gKFxuICAgICAgICA8VGV4dCBrZXk9e2l9IGNvbG9yPXtpID09PSAwICYmIGhlYXJ0RnJhbWUgPyAnYXV0b0FjY2VwdCcgOiBjb2xvcn0+XG4gICAgICAgICAge2xpbmV9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICkpfVxuICAgICAgPFRleHRcbiAgICAgICAgaXRhbGljXG4gICAgICAgIGJvbGQ9e2ZvY3VzZWR9XG4gICAgICAgIGRpbUNvbG9yPXshZm9jdXNlZH1cbiAgICAgICAgY29sb3I9e2ZvY3VzZWQgPyBjb2xvciA6IHVuZGVmaW5lZH1cbiAgICAgICAgaW52ZXJzZT17Zm9jdXNlZH1cbiAgICAgID5cbiAgICAgICAge2ZvY3VzZWQgPyBgICR7Y29tcGFuaW9uLm5hbWV9IGAgOiBjb21wYW5pb24ubmFtZX1cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxuXG4gIGlmICghcmVhY3Rpb24pIHtcbiAgICByZXR1cm4gPEJveCBwYWRkaW5nWD17MX0+e3Nwcml0ZUNvbHVtbn08L0JveD5cbiAgfVxuXG4gIC8vIEZ1bGxzY3JlZW46IGJ1YmJsZSByZW5kZXJzIHNlcGFyYXRlbHkgdmlhIENvbXBhbmlvbkZsb2F0aW5nQnViYmxlIGluXG4gIC8vIEZ1bGxzY3JlZW5MYXlvdXQncyBib3R0b21GbG9hdCBzbG90ICh0aGUgYm90dG9tIHNsb3QncyBvdmVyZmxvd1k6aGlkZGVuXG4gIC8vIHdvdWxkIGNsaXAgYSBwb3NpdGlvbjphYnNvbHV0ZSBvdmVybGF5IGhlcmUpLiBTcHJpdGUgYm9keSBvbmx5LlxuICAvLyBOb24tZnVsbHNjcmVlbjogYnViYmxlIHNpdHMgaW5saW5lIGJlc2lkZSB0aGUgc3ByaXRlIChpbnB1dCBzaHJpbmtzKVxuICAvLyBiZWNhdXNlIGZsb2F0aW5nIGludG8gU3RhdGljIHNjcm9sbGJhY2sgY2FuJ3QgYmUgY2xlYXJlZC5cbiAgaWYgKGlzRnVsbHNjcmVlbkFjdGl2ZSgpKSB7XG4gICAgcmV0dXJuIDxCb3ggcGFkZGluZ1g9ezF9PntzcHJpdGVDb2x1bW59PC9Cb3g+XG4gIH1cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBhbGlnbkl0ZW1zPVwiZmxleC1lbmRcIiBwYWRkaW5nWD17MX0gZmxleFNocmluaz17MH0+XG4gICAgICA8U3BlZWNoQnViYmxlXG4gICAgICAgIHRleHQ9e3JlYWN0aW9ufVxuICAgICAgICBjb2xvcj17Y29sb3J9XG4gICAgICAgIGZhZGluZz17ZmFkaW5nfVxuICAgICAgICB0YWlsPVwicmlnaHRcIlxuICAgICAgLz5cbiAgICAgIHtzcHJpdGVDb2x1bW59XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuLy8gRmxvYXRpbmcgYnViYmxlIG92ZXJsYXkgZm9yIGZ1bGxzY3JlZW4gbW9kZS4gTW91bnRlZCBpbiBGdWxsc2NyZWVuTGF5b3V0J3Ncbi8vIGJvdHRvbUZsb2F0IHNsb3QgKG91dHNpZGUgdGhlIG92ZXJmbG93WTpoaWRkZW4gY2xpcCkgc28gaXQgY2FuIGV4dGVuZCBpbnRvXG4vLyB0aGUgU2Nyb2xsQm94IHJlZ2lvbi4gQ29tcGFuaW9uU3ByaXRlIG93bnMgdGhlIGNsZWFyLWFmdGVyLTEwcyB0aW1lcjsgdGhpc1xuLy8ganVzdCByZWFkcyBjb21wYW5pb25SZWFjdGlvbiBhbmQgcmVuZGVycyB0aGUgZmFkZS5cbmV4cG9ydCBmdW5jdGlvbiBDb21wYW5pb25GbG9hdGluZ0J1YmJsZSgpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCByZWFjdGlvbiA9IHVzZUFwcFN0YXRlKHMgPT4gcy5jb21wYW5pb25SZWFjdGlvbilcbiAgY29uc3QgW3sgdGljaywgZm9yUmVhY3Rpb24gfSwgc2V0VGlja10gPSB1c2VTdGF0ZSh7XG4gICAgdGljazogMCxcbiAgICBmb3JSZWFjdGlvbjogcmVhY3Rpb24sXG4gIH0pXG5cbiAgLy8gUmVzZXQgdGljayBzeW5jaHJvbm91c2x5IHdoZW4gcmVhY3Rpb24gY2hhbmdlcyAobm90IGluIHVzZUVmZmVjdCwgd2hpY2hcbiAgLy8gcnVucyBwb3N0LXJlbmRlciBhbmQgd291bGQgc2hvdyBvbmUgc3RhbGUtZmFkZWQgZnJhbWUpLiBTdG9yaW5nIHRoZVxuICAvLyByZWFjdGlvbiB0aGUgdGljayBpcyBjb3VudGluZyBGT1IgYWxvbmdzaWRlIHRoZSB0aWNrIGl0c2VsZiBtZWFucyB0aGVcbiAgLy8gZmFkZSBjb21wdXRhdGlvbiBuZXZlciBzZWVzIGEgdGljayBmcm9tIGEgcHJldmlvdXMgcmVhY3Rpb24uXG4gIGlmIChyZWFjdGlvbiAhPT0gZm9yUmVhY3Rpb24pIHtcbiAgICBzZXRUaWNrKHsgdGljazogMCwgZm9yUmVhY3Rpb246IHJlYWN0aW9uIH0pXG4gIH1cblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghcmVhY3Rpb24pIHJldHVyblxuICAgIGNvbnN0IHRpbWVyID0gc2V0SW50ZXJ2YWwoXG4gICAgICBzZXQgPT4gc2V0KHMgPT4gKHsgLi4ucywgdGljazogcy50aWNrICsgMSB9KSksXG4gICAgICBUSUNLX01TLFxuICAgICAgc2V0VGljayxcbiAgICApXG4gICAgcmV0dXJuICgpID0+IGNsZWFySW50ZXJ2YWwodGltZXIpXG4gIH0sIFtyZWFjdGlvbl0pXG5cbiAgaWYgKCFmZWF0dXJlKCdCVUREWScpIHx8ICFyZWFjdGlvbikgcmV0dXJuIG51bGxcbiAgY29uc3QgY29tcGFuaW9uID0gZ2V0Q29tcGFuaW9uKClcbiAgaWYgKCFjb21wYW5pb24gfHwgZ2V0R2xvYmFsQ29uZmlnKCkuY29tcGFuaW9uTXV0ZWQpIHJldHVybiBudWxsXG5cbiAgcmV0dXJuIChcbiAgICA8U3BlZWNoQnViYmxlXG4gICAgICB0ZXh0PXtyZWFjdGlvbn1cbiAgICAgIGNvbG9yPXtSQVJJVFlfQ09MT1JTW2NvbXBhbmlvbi5yYXJpdHldfVxuICAgICAgZmFkaW5nPXt0aWNrID49IEJVQkJMRV9TSE9XIC0gRkFERV9XSU5ET1d9XG4gICAgICB0YWlsPVwiZG93blwiXG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsT0FBT0MsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBT0MsS0FBSyxJQUFJQyxTQUFTLEVBQUVDLE1BQU0sRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDMUQsU0FBU0MsZUFBZSxRQUFRLDZCQUE2QjtBQUM3RCxTQUFTQyxXQUFXLFFBQVEsdUJBQXVCO0FBQ25ELFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDckMsU0FBU0MsV0FBVyxFQUFFQyxjQUFjLFFBQVEsc0JBQXNCO0FBQ2xFLGNBQWNDLFFBQVEsUUFBUSwyQkFBMkI7QUFDekQsU0FBU0MsZUFBZSxRQUFRLG9CQUFvQjtBQUNwRCxTQUFTQyxrQkFBa0IsUUFBUSx3QkFBd0I7QUFDM0QsY0FBY0MsS0FBSyxRQUFRLG1CQUFtQjtBQUM5QyxTQUFTQyxZQUFZLFFBQVEsZ0JBQWdCO0FBQzdDLFNBQVNDLFVBQVUsRUFBRUMsWUFBWSxFQUFFQyxnQkFBZ0IsUUFBUSxjQUFjO0FBQ3pFLFNBQVNDLGFBQWEsUUFBUSxZQUFZO0FBRTFDLE1BQU1DLE9BQU8sR0FBRyxHQUFHO0FBQ25CLE1BQU1DLFdBQVcsR0FBRyxFQUFFLEVBQUM7QUFDdkIsTUFBTUMsV0FBVyxHQUFHLENBQUMsRUFBQztBQUN0QixNQUFNQyxZQUFZLEdBQUcsSUFBSSxFQUFDOztBQUUxQjtBQUNBO0FBQ0EsTUFBTUMsYUFBYSxHQUFHLENBQUMsQ0FBQyxF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 \ No newline at end of file diff --git a/buddy/companion.ts b/buddy/companion.ts new file mode 100644 index 0000000..09c3838 --- /dev/null +++ b/buddy/companion.ts @@ -0,0 +1,133 @@ +import { getGlobalConfig } from '../utils/config.js' +import { + type Companion, + type CompanionBones, + EYES, + HATS, + RARITIES, + RARITY_WEIGHTS, + type Rarity, + SPECIES, + STAT_NAMES, + type StatName, +} from './types.js' + +// Mulberry32 — tiny seeded PRNG, good enough for picking ducks +function mulberry32(seed: number): () => number { + let a = seed >>> 0 + return function () { + a |= 0 + a = (a + 0x6d2b79f5) | 0 + let t = Math.imul(a ^ (a >>> 15), 1 | a) + t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t + return ((t ^ (t >>> 14)) >>> 0) / 4294967296 + } +} + +function hashString(s: string): number { + if (typeof Bun !== 'undefined') { + return Number(BigInt(Bun.hash(s)) & 0xffffffffn) + } + let h = 2166136261 + for (let i = 0; i < s.length; i++) { + h ^= s.charCodeAt(i) + h = Math.imul(h, 16777619) + } + return h >>> 0 +} + +function pick(rng: () => number, arr: readonly T[]): T { + return arr[Math.floor(rng() * arr.length)]! +} + +function rollRarity(rng: () => number): Rarity { + const total = Object.values(RARITY_WEIGHTS).reduce((a, b) => a + b, 0) + let roll = rng() * total + for (const rarity of RARITIES) { + roll -= RARITY_WEIGHTS[rarity] + if (roll < 0) return rarity + } + return 'common' +} + +const RARITY_FLOOR: Record = { + common: 5, + uncommon: 15, + rare: 25, + epic: 35, + legendary: 50, +} + +// One peak stat, one dump stat, rest scattered. Rarity bumps the floor. +function rollStats( + rng: () => number, + rarity: Rarity, +): Record { + const floor = RARITY_FLOOR[rarity] + const peak = pick(rng, STAT_NAMES) + let dump = pick(rng, STAT_NAMES) + while (dump === peak) dump = pick(rng, STAT_NAMES) + + const stats = {} as Record + for (const name of STAT_NAMES) { + if (name === peak) { + stats[name] = Math.min(100, floor + 50 + Math.floor(rng() * 30)) + } else if (name === dump) { + stats[name] = Math.max(1, floor - 10 + Math.floor(rng() * 15)) + } else { + stats[name] = floor + Math.floor(rng() * 40) + } + } + return stats +} + +const SALT = 'friend-2026-401' + +export type Roll = { + bones: CompanionBones + inspirationSeed: number +} + +function rollFrom(rng: () => number): Roll { + const rarity = rollRarity(rng) + const bones: CompanionBones = { + rarity, + species: pick(rng, SPECIES), + eye: pick(rng, EYES), + hat: rarity === 'common' ? 'none' : pick(rng, HATS), + shiny: rng() < 0.01, + stats: rollStats(rng, rarity), + } + return { bones, inspirationSeed: Math.floor(rng() * 1e9) } +} + +// Called from three hot paths (500ms sprite tick, per-keystroke PromptInput, +// per-turn observer) with the same userId → cache the deterministic result. +let rollCache: { key: string; value: Roll } | undefined +export function roll(userId: string): Roll { + const key = userId + SALT + if (rollCache?.key === key) return rollCache.value + const value = rollFrom(mulberry32(hashString(key))) + rollCache = { key, value } + return value +} + +export function rollWithSeed(seed: string): Roll { + return rollFrom(mulberry32(hashString(seed))) +} + +export function companionUserId(): string { + const config = getGlobalConfig() + return config.oauthAccount?.accountUuid ?? config.userID ?? 'anon' +} + +// Regenerate bones from userId, merge with stored soul. Bones never persist +// so species renames and SPECIES-array edits can't break stored companions, +// and editing config.companion can't fake a rarity. +export function getCompanion(): Companion | undefined { + const stored = getGlobalConfig().companion + if (!stored) return undefined + const { bones } = roll(companionUserId()) + // bones last so stale bones fields in old-format configs get overridden + return { ...stored, ...bones } +} diff --git a/buddy/prompt.ts b/buddy/prompt.ts new file mode 100644 index 0000000..c5782c0 --- /dev/null +++ b/buddy/prompt.ts @@ -0,0 +1,36 @@ +import { feature } from 'bun:bundle' +import type { Message } from '../types/message.js' +import type { Attachment } from '../utils/attachments.js' +import { getGlobalConfig } from '../utils/config.js' +import { getCompanion } from './companion.js' + +export function companionIntroText(name: string, species: string): string { + return `# Companion + +A small ${species} named ${name} sits beside the user's input box and occasionally comments in a speech bubble. You're not ${name} — it's a separate watcher. + +When the user addresses ${name} directly (by name), its bubble will answer. Your job in that moment is to stay out of the way: respond in ONE line or less, or just answer any part of the message meant for you. Don't explain that you're not ${name} — they know. Don't narrate what ${name} might say — the bubble handles that.` +} + +export function getCompanionIntroAttachment( + messages: Message[] | undefined, +): Attachment[] { + if (!feature('BUDDY')) return [] + const companion = getCompanion() + if (!companion || getGlobalConfig().companionMuted) return [] + + // Skip if already announced for this companion. + for (const msg of messages ?? []) { + if (msg.type !== 'attachment') continue + if (msg.attachment.type !== 'companion_intro') continue + if (msg.attachment.name === companion.name) return [] + } + + return [ + { + type: 'companion_intro', + name: companion.name, + species: companion.species, + }, + ] +} diff --git a/buddy/sprites.ts b/buddy/sprites.ts new file mode 100644 index 0000000..0150b8c --- /dev/null +++ b/buddy/sprites.ts @@ -0,0 +1,514 @@ +import type { CompanionBones, Eye, Hat, Species } from './types.js' +import { + axolotl, + blob, + cactus, + capybara, + cat, + chonk, + dragon, + duck, + ghost, + goose, + mushroom, + octopus, + owl, + penguin, + rabbit, + robot, + snail, + turtle, +} from './types.js' + +// Each sprite is 5 lines tall, 12 wide (after {E}→1char substitution). +// Multiple frames per species for idle fidget animation. +// Line 0 is the hat slot — must be blank in frames 0-1; frame 2 may use it. +const BODIES: Record = { + [duck]: [ + [ + ' ', + ' __ ', + ' <({E} )___ ', + ' ( ._> ', + ' `--´ ', + ], + [ + ' ', + ' __ ', + ' <({E} )___ ', + ' ( ._> ', + ' `--´~ ', + ], + [ + ' ', + ' __ ', + ' <({E} )___ ', + ' ( .__> ', + ' `--´ ', + ], + ], + [goose]: [ + [ + ' ', + ' ({E}> ', + ' || ', + ' _(__)_ ', + ' ^^^^ ', + ], + [ + ' ', + ' ({E}> ', + ' || ', + ' _(__)_ ', + ' ^^^^ ', + ], + [ + ' ', + ' ({E}>> ', + ' || ', + ' _(__)_ ', + ' ^^^^ ', + ], + ], + [blob]: [ + [ + ' ', + ' .----. ', + ' ( {E} {E} ) ', + ' ( ) ', + ' `----´ ', + ], + [ + ' ', + ' .------. ', + ' ( {E} {E} ) ', + ' ( ) ', + ' `------´ ', + ], + [ + ' ', + ' .--. ', + ' ({E} {E}) ', + ' ( ) ', + ' `--´ ', + ], + ], + [cat]: [ + [ + ' ', + ' /\\_/\\ ', + ' ( {E} {E}) ', + ' ( ω ) ', + ' (")_(") ', + ], + [ + ' ', + ' /\\_/\\ ', + ' ( {E} {E}) ', + ' ( ω ) ', + ' (")_(")~ ', + ], + [ + ' ', + ' /\\-/\\ ', + ' ( {E} {E}) ', + ' ( ω ) ', + ' (")_(") ', + ], + ], + [dragon]: [ + [ + ' ', + ' /^\\ /^\\ ', + ' < {E} {E} > ', + ' ( ~~ ) ', + ' `-vvvv-´ ', + ], + [ + ' ', + ' /^\\ /^\\ ', + ' < {E} {E} > ', + ' ( ) ', + ' `-vvvv-´ ', + ], + [ + ' ~ ~ ', + ' /^\\ /^\\ ', + ' < {E} {E} > ', + ' ( ~~ ) ', + ' `-vvvv-´ ', + ], + ], + [octopus]: [ + [ + ' ', + ' .----. ', + ' ( {E} {E} ) ', + ' (______) ', + ' /\\/\\/\\/\\ ', + ], + [ + ' ', + ' .----. ', + ' ( {E} {E} ) ', + ' (______) ', + ' \\/\\/\\/\\/ ', + ], + [ + ' o ', + ' .----. ', + ' ( {E} {E} ) ', + ' (______) ', + ' /\\/\\/\\/\\ ', + ], + ], + [owl]: [ + [ + ' ', + ' /\\ /\\ ', + ' (({E})({E})) ', + ' ( >< ) ', + ' `----´ ', + ], + [ + ' ', + ' /\\ /\\ ', + ' (({E})({E})) ', + ' ( >< ) ', + ' .----. ', + ], + [ + ' ', + ' /\\ /\\ ', + ' (({E})(-)) ', + ' ( >< ) ', + ' `----´ ', + ], + ], + [penguin]: [ + [ + ' ', + ' .---. ', + ' ({E}>{E}) ', + ' /( )\\ ', + ' `---´ ', + ], + [ + ' ', + ' .---. ', + ' ({E}>{E}) ', + ' |( )| ', + ' `---´ ', + ], + [ + ' .---. ', + ' ({E}>{E}) ', + ' /( )\\ ', + ' `---´ ', + ' ~ ~ ', + ], + ], + [turtle]: [ + [ + ' ', + ' _,--._ ', + ' ( {E} {E} ) ', + ' /[______]\\ ', + ' `` `` ', + ], + [ + ' ', + ' _,--._ ', + ' ( {E} {E} ) ', + ' /[______]\\ ', + ' `` `` ', + ], + [ + ' ', + ' _,--._ ', + ' ( {E} {E} ) ', + ' /[======]\\ ', + ' `` `` ', + ], + ], + [snail]: [ + [ + ' ', + ' {E} .--. ', + ' \\ ( @ ) ', + ' \\_`--´ ', + ' ~~~~~~~ ', + ], + [ + ' ', + ' {E} .--. ', + ' | ( @ ) ', + ' \\_`--´ ', + ' ~~~~~~~ ', + ], + [ + ' ', + ' {E} .--. ', + ' \\ ( @ ) ', + ' \\_`--´ ', + ' ~~~~~~ ', + ], + ], + [ghost]: [ + [ + ' ', + ' .----. ', + ' / {E} {E} \\ ', + ' | | ', + ' ~`~``~`~ ', + ], + [ + ' ', + ' .----. ', + ' / {E} {E} \\ ', + ' | | ', + ' `~`~~`~` ', + ], + [ + ' ~ ~ ', + ' .----. ', + ' / {E} {E} \\ ', + ' | | ', + ' ~~`~~`~~ ', + ], + ], + [axolotl]: [ + [ + ' ', + '}~(______)~{', + '}~({E} .. {E})~{', + ' ( .--. ) ', + ' (_/ \\_) ', + ], + [ + ' ', + '~}(______){~', + '~}({E} .. {E}){~', + ' ( .--. ) ', + ' (_/ \\_) ', + ], + [ + ' ', + '}~(______)~{', + '}~({E} .. {E})~{', + ' ( -- ) ', + ' ~_/ \\_~ ', + ], + ], + [capybara]: [ + [ + ' ', + ' n______n ', + ' ( {E} {E} ) ', + ' ( oo ) ', + ' `------´ ', + ], + [ + ' ', + ' n______n ', + ' ( {E} {E} ) ', + ' ( Oo ) ', + ' `------´ ', + ], + [ + ' ~ ~ ', + ' u______n ', + ' ( {E} {E} ) ', + ' ( oo ) ', + ' `------´ ', + ], + ], + [cactus]: [ + [ + ' ', + ' n ____ n ', + ' | |{E} {E}| | ', + ' |_| |_| ', + ' | | ', + ], + [ + ' ', + ' ____ ', + ' n |{E} {E}| n ', + ' |_| |_| ', + ' | | ', + ], + [ + ' n n ', + ' | ____ | ', + ' | |{E} {E}| | ', + ' |_| |_| ', + ' | | ', + ], + ], + [robot]: [ + [ + ' ', + ' .[||]. ', + ' [ {E} {E} ] ', + ' [ ==== ] ', + ' `------´ ', + ], + [ + ' ', + ' .[||]. ', + ' [ {E} {E} ] ', + ' [ -==- ] ', + ' `------´ ', + ], + [ + ' * ', + ' .[||]. ', + ' [ {E} {E} ] ', + ' [ ==== ] ', + ' `------´ ', + ], + ], + [rabbit]: [ + [ + ' ', + ' (\\__/) ', + ' ( {E} {E} ) ', + ' =( .. )= ', + ' (")__(") ', + ], + [ + ' ', + ' (|__/) ', + ' ( {E} {E} ) ', + ' =( .. )= ', + ' (")__(") ', + ], + [ + ' ', + ' (\\__/) ', + ' ( {E} {E} ) ', + ' =( . . )= ', + ' (")__(") ', + ], + ], + [mushroom]: [ + [ + ' ', + ' .-o-OO-o-. ', + '(__________)', + ' |{E} {E}| ', + ' |____| ', + ], + [ + ' ', + ' .-O-oo-O-. ', + '(__________)', + ' |{E} {E}| ', + ' |____| ', + ], + [ + ' . o . ', + ' .-o-OO-o-. ', + '(__________)', + ' |{E} {E}| ', + ' |____| ', + ], + ], + [chonk]: [ + [ + ' ', + ' /\\ /\\ ', + ' ( {E} {E} ) ', + ' ( .. ) ', + ' `------´ ', + ], + [ + ' ', + ' /\\ /| ', + ' ( {E} {E} ) ', + ' ( .. ) ', + ' `------´ ', + ], + [ + ' ', + ' /\\ /\\ ', + ' ( {E} {E} ) ', + ' ( .. ) ', + ' `------´~ ', + ], + ], +} + +const HAT_LINES: Record = { + none: '', + crown: ' \\^^^/ ', + tophat: ' [___] ', + propeller: ' -+- ', + halo: ' ( ) ', + wizard: ' /^\\ ', + beanie: ' (___) ', + tinyduck: ' ,> ', +} + +export function renderSprite(bones: CompanionBones, frame = 0): string[] { + const frames = BODIES[bones.species] + const body = frames[frame % frames.length]!.map(line => + line.replaceAll('{E}', bones.eye), + ) + const lines = [...body] + // Only replace with hat if line 0 is empty (some fidget frames use it for smoke etc) + if (bones.hat !== 'none' && !lines[0]!.trim()) { + lines[0] = HAT_LINES[bones.hat] + } + // Drop blank hat slot — wastes a row in the Card and ambient sprite when + // there's no hat and the frame isn't using it for smoke/antenna/etc. + // Only safe when ALL frames have blank line 0; otherwise heights oscillate. + if (!lines[0]!.trim() && frames.every(f => !f[0]!.trim())) lines.shift() + return lines +} + +export function spriteFrameCount(species: Species): number { + return BODIES[species].length +} + +export function renderFace(bones: CompanionBones): string { + const eye: Eye = bones.eye + switch (bones.species) { + case duck: + case goose: + return `(${eye}>` + case blob: + return `(${eye}${eye})` + case cat: + return `=${eye}ω${eye}=` + case dragon: + return `<${eye}~${eye}>` + case octopus: + return `~(${eye}${eye})~` + case owl: + return `(${eye})(${eye})` + case penguin: + return `(${eye}>)` + case turtle: + return `[${eye}_${eye}]` + case snail: + return `${eye}(@)` + case ghost: + return `/${eye}${eye}\\` + case axolotl: + return `}${eye}.${eye}{` + case capybara: + return `(${eye}oo${eye})` + case cactus: + return `|${eye} ${eye}|` + case robot: + return `[${eye}${eye}]` + case rabbit: + return `(${eye}..${eye})` + case mushroom: + return `|${eye} ${eye}|` + case chonk: + return `(${eye}.${eye})` + } +} diff --git a/buddy/types.ts b/buddy/types.ts new file mode 100644 index 0000000..8f1c82a --- /dev/null +++ b/buddy/types.ts @@ -0,0 +1,148 @@ +export const RARITIES = [ + 'common', + 'uncommon', + 'rare', + 'epic', + 'legendary', +] as const +export type Rarity = (typeof RARITIES)[number] + +// One species name collides with a model-codename canary in excluded-strings.txt. +// The check greps build output (not source), so runtime-constructing the value keeps +// the literal out of the bundle while the check stays armed for the actual codename. +// All species encoded uniformly; `as` casts are type-position only (erased pre-bundle). +const c = String.fromCharCode +// biome-ignore format: keep the species list compact + +export const duck = c(0x64,0x75,0x63,0x6b) as 'duck' +export const goose = c(0x67, 0x6f, 0x6f, 0x73, 0x65) as 'goose' +export const blob = c(0x62, 0x6c, 0x6f, 0x62) as 'blob' +export const cat = c(0x63, 0x61, 0x74) as 'cat' +export const dragon = c(0x64, 0x72, 0x61, 0x67, 0x6f, 0x6e) as 'dragon' +export const octopus = c(0x6f, 0x63, 0x74, 0x6f, 0x70, 0x75, 0x73) as 'octopus' +export const owl = c(0x6f, 0x77, 0x6c) as 'owl' +export const penguin = c(0x70, 0x65, 0x6e, 0x67, 0x75, 0x69, 0x6e) as 'penguin' +export const turtle = c(0x74, 0x75, 0x72, 0x74, 0x6c, 0x65) as 'turtle' +export const snail = c(0x73, 0x6e, 0x61, 0x69, 0x6c) as 'snail' +export const ghost = c(0x67, 0x68, 0x6f, 0x73, 0x74) as 'ghost' +export const axolotl = c(0x61, 0x78, 0x6f, 0x6c, 0x6f, 0x74, 0x6c) as 'axolotl' +export const capybara = c( + 0x63, + 0x61, + 0x70, + 0x79, + 0x62, + 0x61, + 0x72, + 0x61, +) as 'capybara' +export const cactus = c(0x63, 0x61, 0x63, 0x74, 0x75, 0x73) as 'cactus' +export const robot = c(0x72, 0x6f, 0x62, 0x6f, 0x74) as 'robot' +export const rabbit = c(0x72, 0x61, 0x62, 0x62, 0x69, 0x74) as 'rabbit' +export const mushroom = c( + 0x6d, + 0x75, + 0x73, + 0x68, + 0x72, + 0x6f, + 0x6f, + 0x6d, +) as 'mushroom' +export const chonk = c(0x63, 0x68, 0x6f, 0x6e, 0x6b) as 'chonk' + +export const SPECIES = [ + duck, + goose, + blob, + cat, + dragon, + octopus, + owl, + penguin, + turtle, + snail, + ghost, + axolotl, + capybara, + cactus, + robot, + rabbit, + mushroom, + chonk, +] as const +export type Species = (typeof SPECIES)[number] // biome-ignore format: keep compact + +export const EYES = ['·', '✦', '×', '◉', '@', '°'] as const +export type Eye = (typeof EYES)[number] + +export const HATS = [ + 'none', + 'crown', + 'tophat', + 'propeller', + 'halo', + 'wizard', + 'beanie', + 'tinyduck', +] as const +export type Hat = (typeof HATS)[number] + +export const STAT_NAMES = [ + 'DEBUGGING', + 'PATIENCE', + 'CHAOS', + 'WISDOM', + 'SNARK', +] as const +export type StatName = (typeof STAT_NAMES)[number] + +// Deterministic parts — derived from hash(userId) +export type CompanionBones = { + rarity: Rarity + species: Species + eye: Eye + hat: Hat + shiny: boolean + stats: Record +} + +// Model-generated soul — stored in config after first hatch +export type CompanionSoul = { + name: string + personality: string +} + +export type Companion = CompanionBones & + CompanionSoul & { + hatchedAt: number + } + +// What actually persists in config. Bones are regenerated from hash(userId) +// on every read so species renames don't break stored companions and users +// can't edit their way to a legendary. +export type StoredCompanion = CompanionSoul & { hatchedAt: number } + +export const RARITY_WEIGHTS = { + common: 60, + uncommon: 25, + rare: 10, + epic: 4, + legendary: 1, +} as const satisfies Record + +export const RARITY_STARS = { + common: '★', + uncommon: '★★', + rare: '★★★', + epic: '★★★★', + legendary: '★★★★★', +} as const satisfies Record + +export const RARITY_COLORS = { + common: 'inactive', + uncommon: 'success', + rare: 'permission', + epic: 'autoAccept', + legendary: 'warning', +} as const satisfies Record diff --git a/buddy/useBuddyNotification.tsx b/buddy/useBuddyNotification.tsx new file mode 100644 index 0000000..d6eed22 --- /dev/null +++ b/buddy/useBuddyNotification.tsx @@ -0,0 +1,98 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import React, { useEffect } from 'react'; +import { useNotifications } from '../context/notifications.js'; +import { Text } from '../ink.js'; +import { getGlobalConfig } from '../utils/config.js'; +import { getRainbowColor } from '../utils/thinking.js'; + +// Local date, not UTC — 24h rolling wave across timezones. Sustained Twitter +// buzz instead of a single UTC-midnight spike, gentler on soul-gen load. +// Teaser window: April 1-7, 2026 only. Command stays live forever after. +export function isBuddyTeaserWindow(): boolean { + if ("external" === 'ant') return true; + const d = new Date(); + return d.getFullYear() === 2026 && d.getMonth() === 3 && d.getDate() <= 7; +} +export function isBuddyLive(): boolean { + if ("external" === 'ant') return true; + const d = new Date(); + return d.getFullYear() > 2026 || d.getFullYear() === 2026 && d.getMonth() >= 3; +} +function RainbowText(t0) { + const $ = _c(2); + const { + text + } = t0; + let t1; + if ($[0] !== text) { + t1 = <>{[...text].map(_temp)}; + $[0] = text; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +// Rainbow /buddy teaser shown on startup when no companion hatched yet. +// Idle presence and reactions are handled by CompanionSprite directly. +function _temp(ch, i) { + return {ch}; +} +export function useBuddyNotification() { + const $ = _c(4); + const { + addNotification, + removeNotification + } = useNotifications(); + let t0; + let t1; + if ($[0] !== addNotification || $[1] !== removeNotification) { + t0 = () => { + if (!feature("BUDDY")) { + return; + } + const config = getGlobalConfig(); + if (config.companion || !isBuddyTeaserWindow()) { + return; + } + addNotification({ + key: "buddy-teaser", + jsx: , + priority: "immediate", + timeoutMs: 15000 + }); + return () => removeNotification("buddy-teaser"); + }; + t1 = [addNotification, removeNotification]; + $[0] = addNotification; + $[1] = removeNotification; + $[2] = t0; + $[3] = t1; + } else { + t0 = $[2]; + t1 = $[3]; + } + useEffect(t0, t1); +} +export function findBuddyTriggerPositions(text: string): Array<{ + start: number; + end: number; +}> { + if (!feature('BUDDY')) return []; + const triggers: Array<{ + start: number; + end: number; + }> = []; + const re = /\/buddy\b/g; + let m: RegExpExecArray | null; + while ((m = re.exec(text)) !== null) { + triggers.push({ + start: m.index, + end: m.index + m[0].length + }); + } + return triggers; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJ1c2VFZmZlY3QiLCJ1c2VOb3RpZmljYXRpb25zIiwiVGV4dCIsImdldEdsb2JhbENvbmZpZyIsImdldFJhaW5ib3dDb2xvciIsImlzQnVkZHlUZWFzZXJXaW5kb3ciLCJkIiwiRGF0ZSIsImdldEZ1bGxZZWFyIiwiZ2V0TW9udGgiLCJnZXREYXRlIiwiaXNCdWRkeUxpdmUiLCJSYWluYm93VGV4dCIsInQwIiwiJCIsIl9jIiwidGV4dCIsInQxIiwibWFwIiwiX3RlbXAiLCJjaCIsImkiLCJ1c2VCdWRkeU5vdGlmaWNhdGlvbiIsImFkZE5vdGlmaWNhdGlvbiIsInJlbW92ZU5vdGlmaWNhdGlvbiIsImNvbmZpZyIsImNvbXBhbmlvbiIsImtleSIsImpzeCIsInByaW9yaXR5IiwidGltZW91dE1zIiwiZmluZEJ1ZGR5VHJpZ2dlclBvc2l0aW9ucyIsIkFycmF5Iiwic3RhcnQiLCJlbmQiLCJ0cmlnZ2VycyIsInJlIiwibSIsIlJlZ0V4cEV4ZWNBcnJheSIsImV4ZWMiLCJwdXNoIiwiaW5kZXgiLCJsZW5ndGgiXSwic291cmNlcyI6WyJ1c2VCdWRkeU5vdGlmaWNhdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgUmVhY3QsIHsgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnLi4vY29udGV4dC9ub3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGdldEdsb2JhbENvbmZpZyB9IGZyb20gJy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGdldFJhaW5ib3dDb2xvciB9IGZyb20gJy4uL3V0aWxzL3RoaW5raW5nLmpzJ1xuXG4vLyBMb2NhbCBkYXRlLCBub3QgVVRDIOKAlCAyNGggcm9sbGluZyB3YXZlIGFjcm9zcyB0aW1lem9uZXMuIFN1c3RhaW5lZCBUd2l0dGVyXG4vLyBidXp6IGluc3RlYWQgb2YgYSBzaW5nbGUgVVRDLW1pZG5pZ2h0IHNwaWtlLCBnZW50bGVyIG9uIHNvdWwtZ2VuIGxvYWQuXG4vLyBUZWFzZXIgd2luZG93OiBBcHJpbCAxLTcsIDIwMjYgb25seS4gQ29tbWFuZCBzdGF5cyBsaXZlIGZvcmV2ZXIgYWZ0ZXIuXG5leHBvcnQgZnVuY3Rpb24gaXNCdWRkeVRlYXNlcldpbmRvdygpOiBib29sZWFuIHtcbiAgaWYgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcpIHJldHVybiB0cnVlXG4gIGNvbnN0IGQgPSBuZXcgRGF0ZSgpXG4gIHJldHVybiBkLmdldEZ1bGxZZWFyKCkgPT09IDIwMjYgJiYgZC5nZXRNb250aCgpID09PSAzICYmIGQuZ2V0RGF0ZSgpIDw9IDdcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzQnVkZHlMaXZlKCk6IGJvb2xlYW4ge1xuICBpZiAoXCJleHRlcm5hbFwiID09PSAnYW50JykgcmV0dXJuIHRydWVcbiAgY29uc3QgZCA9IG5ldyBEYXRlKClcbiAgcmV0dXJuIChcbiAgICBkLmdldEZ1bGxZZWFyKCkgPiAyMDI2IHx8IChkLmdldEZ1bGxZZWFyKCkgPT09IDIwMjYgJiYgZC5nZXRNb250aCgpID49IDMpXG4gIClcbn1cblxuZnVuY3Rpb24gUmFpbmJvd1RleHQoeyB0ZXh0IH06IHsgdGV4dDogc3RyaW5nIH0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICB7Wy4uLnRleHRdLm1hcCgoY2gsIGkpID0+IChcbiAgICAgICAgPFRleHQga2V5PXtpfSBjb2xvcj17Z2V0UmFpbmJvd0NvbG9yKGkpfT5cbiAgICAgICAgICB7Y2h9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICkpfVxuICAgIDwvPlxuICApXG59XG5cbi8vIFJhaW5ib3cgL2J1ZGR5IHRlYXNlciBzaG93biBvbiBzdGFydHVwIHdoZW4gbm8gY29tcGFuaW9uIGhhdGNoZWQgeWV0LlxuLy8gSWRsZSBwcmVzZW5jZSBhbmQgcmVhY3Rpb25zIGFyZSBoYW5kbGVkIGJ5IENvbXBhbmlvblNwcml0ZSBkaXJlY3RseS5cbmV4cG9ydCBmdW5jdGlvbiB1c2VCdWRkeU5vdGlmaWNhdGlvbigpOiB2b2lkIHtcbiAgY29uc3QgeyBhZGROb3RpZmljYXRpb24sIHJlbW92ZU5vdGlmaWNhdGlvbiB9ID0gdXNlTm90aWZpY2F0aW9ucygpXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoIWZlYXR1cmUoJ0JVRERZJykpIHJldHVyblxuICAgIGNvbnN0IGNvbmZpZyA9IGdldEdsb2JhbENvbmZpZygpXG4gICAgaWYgKGNvbmZpZy5jb21wYW5pb24gfHwgIWlzQnVkZHlUZWFzZXJXaW5kb3coKSkgcmV0dXJuXG4gICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgIGtleTogJ2J1ZGR5LXRlYXNlcicsXG4gICAgICBqc3g6IDxSYWluYm93VGV4dCB0ZXh0PVwiL2J1ZGR5XCIgLz4sXG4gICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICB0aW1lb3V0TXM6IDE1XzAwMCxcbiAgICB9KVxuICAgIHJldHVybiAoKSA9PiByZW1vdmVOb3RpZmljYXRpb24oJ2J1ZGR5LXRlYXNlcicpXG4gIH0sIFthZGROb3RpZmljYXRpb24sIHJlbW92ZU5vdGlmaWNhdGlvbl0pXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBmaW5kQnVkZHlUcmlnZ2VyUG9zaXRpb25zKFxuICB0ZXh0OiBzdHJpbmcsXG4pOiBBcnJheTx7IHN0YXJ0OiBudW1iZXI7IGVuZDogbnVtYmVyIH0+IHtcbiAgaWYgKCFmZWF0dXJlKCdCVUREWScpKSByZXR1cm4gW11cbiAgY29uc3QgdHJpZ2dlcnM6IEFycmF5PHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfT4gPSBbXVxuICBjb25zdCByZSA9IC9cXC9idWRkeVxcYi9nXG4gIGxldCBtOiBSZWdFeHBFeGVjQXJyYXkgfCBudWxsXG4gIHdoaWxlICgobSA9IHJlLmV4ZWModGV4dCkpICE9PSBudWxsKSB7XG4gICAgdHJpZ2dlcnMucHVzaCh7IHN0YXJ0OiBtLmluZGV4LCBlbmQ6IG0uaW5kZXggKyBtWzBdLmxlbmd0aCB9KVxuICB9XG4gIHJldHVybiB0cmlnZ2Vyc1xufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsT0FBT0MsS0FBSyxJQUFJQyxTQUFTLFFBQVEsT0FBTztBQUN4QyxTQUFTQyxnQkFBZ0IsUUFBUSw2QkFBNkI7QUFDOUQsU0FBU0MsSUFBSSxRQUFRLFdBQVc7QUFDaEMsU0FBU0MsZUFBZSxRQUFRLG9CQUFvQjtBQUNwRCxTQUFTQyxlQUFlLFFBQVEsc0JBQXNCOztBQUV0RDtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQVNDLG1CQUFtQkEsQ0FBQSxDQUFFLEVBQUUsT0FBTyxDQUFDO0VBQzdDLElBQUksVUFBVSxLQUFLLEtBQUssRUFBRSxPQUFPLElBQUk7RUFDckMsTUFBTUMsQ0FBQyxHQUFHLElBQUlDLElBQUksQ0FBQyxDQUFDO0VBQ3BCLE9BQU9ELENBQUMsQ0FBQ0UsV0FBVyxDQUFDLENBQUMsS0FBSyxJQUFJLElBQUlGLENBQUMsQ0FBQ0csUUFBUSxDQUFDLENBQUMsS0FBSyxDQUFDLElBQUlILENBQUMsQ0FBQ0ksT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDO0FBQzNFO0FBRUEsT0FBTyxTQUFTQyxXQUFXQSxDQUFBLENBQUUsRUFBRSxPQUFPLENBQUM7RUFDckMsSUFBSSxVQUFVLEtBQUssS0FBSyxFQUFFLE9BQU8sSUFBSTtFQUNyQyxNQUFNTCxDQUFDLEdBQUcsSUFBSUMsSUFBSSxDQUFDLENBQUM7RUFDcEIsT0FDRUQsQ0FBQyxDQUFDRSxXQUFXLENBQUMsQ0FBQyxHQUFHLElBQUksSUFBS0YsQ0FBQyxDQUFDRSxXQUFXLENBQUMsQ0FBQyxLQUFLLElBQUksSUFBSUYsQ0FBQyxDQUFDRyxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUU7QUFFN0U7QUFFQSxTQUFBRyxZQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxH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 \ No newline at end of file diff --git a/cli/exit.ts b/cli/exit.ts new file mode 100644 index 0000000..99e56f9 --- /dev/null +++ b/cli/exit.ts @@ -0,0 +1,31 @@ +/** + * CLI exit helpers for subcommand handlers. + * + * Consolidates the 4-5 line "print + lint-suppress + exit" block that was + * copy-pasted ~60 times across `claude mcp *` / `claude plugin *` handlers. + * The `: never` return type lets TypeScript narrow control flow at call sites + * without a trailing `return`. + */ +/* eslint-disable custom-rules/no-process-exit -- centralized CLI exit point */ + +// `return undefined as never` (not a post-exit throw) — tests spy on +// process.exit and let it return. Call sites write `return cliError(...)` +// where subsequent code would dereference narrowed-away values under mock. +// cliError uses console.error (tests spy on console.error); cliOk uses +// process.stdout.write (tests spy on process.stdout.write — Bun's console.log +// doesn't route through a spied process.stdout.write). + +/** Write an error message to stderr (if given) and exit with code 1. */ +export function cliError(msg?: string): never { + // biome-ignore lint/suspicious/noConsole: centralized CLI error output + if (msg) console.error(msg) + process.exit(1) + return undefined as never +} + +/** Write a message to stdout (if given) and exit with code 0. */ +export function cliOk(msg?: string): never { + if (msg) process.stdout.write(msg + '\n') + process.exit(0) + return undefined as never +} diff --git a/cli/handlers/agents.ts b/cli/handlers/agents.ts new file mode 100644 index 0000000..c94723b --- /dev/null +++ b/cli/handlers/agents.ts @@ -0,0 +1,70 @@ +/** + * Agents subcommand handler — prints the list of configured agents. + * Dynamically imported only when `claude agents` runs. + */ + +import { + AGENT_SOURCE_GROUPS, + compareAgentsByName, + getOverrideSourceLabel, + type ResolvedAgent, + resolveAgentModelDisplay, + resolveAgentOverrides, +} from '../../tools/AgentTool/agentDisplay.js' +import { + getActiveAgentsFromList, + getAgentDefinitionsWithOverrides, +} from '../../tools/AgentTool/loadAgentsDir.js' +import { getCwd } from '../../utils/cwd.js' + +function formatAgent(agent: ResolvedAgent): string { + const model = resolveAgentModelDisplay(agent) + const parts = [agent.agentType] + if (model) { + parts.push(model) + } + if (agent.memory) { + parts.push(`${agent.memory} memory`) + } + return parts.join(' · ') +} + +export async function agentsHandler(): Promise { + const cwd = getCwd() + const { allAgents } = await getAgentDefinitionsWithOverrides(cwd) + const activeAgents = getActiveAgentsFromList(allAgents) + const resolvedAgents = resolveAgentOverrides(allAgents, activeAgents) + + const lines: string[] = [] + let totalActive = 0 + + for (const { label, source } of AGENT_SOURCE_GROUPS) { + const groupAgents = resolvedAgents + .filter(a => a.source === source) + .sort(compareAgentsByName) + + if (groupAgents.length === 0) continue + + lines.push(`${label}:`) + for (const agent of groupAgents) { + if (agent.overriddenBy) { + const winnerSource = getOverrideSourceLabel(agent.overriddenBy) + lines.push(` (shadowed by ${winnerSource}) ${formatAgent(agent)}`) + } else { + lines.push(` ${formatAgent(agent)}`) + totalActive++ + } + } + lines.push('') + } + + if (lines.length === 0) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('No agents found.') + } else { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`${totalActive} active agents\n`) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(lines.join('\n').trimEnd()) + } +} diff --git a/cli/handlers/auth.ts b/cli/handlers/auth.ts new file mode 100644 index 0000000..c4cba5d --- /dev/null +++ b/cli/handlers/auth.ts @@ -0,0 +1,330 @@ +/* eslint-disable custom-rules/no-process-exit -- CLI subcommand handler intentionally exits */ + +import { + clearAuthRelatedCaches, + performLogout, +} from '../../commands/logout/logout.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../../services/analytics/index.js' +import { getSSLErrorHint } from '../../services/api/errorUtils.js' +import { fetchAndStoreClaudeCodeFirstTokenDate } from '../../services/api/firstTokenDate.js' +import { + createAndStoreApiKey, + fetchAndStoreUserRoles, + refreshOAuthToken, + shouldUseClaudeAIAuth, + storeOAuthAccountInfo, +} from '../../services/oauth/client.js' +import { getOauthProfileFromOauthToken } from '../../services/oauth/getOauthProfile.js' +import { OAuthService } from '../../services/oauth/index.js' +import type { OAuthTokens } from '../../services/oauth/types.js' +import { + clearOAuthTokenCache, + getAnthropicApiKeyWithSource, + getAuthTokenSource, + getOauthAccountInfo, + getSubscriptionType, + isUsing3PServices, + saveOAuthTokensIfNeeded, + validateForceLoginOrg, +} from '../../utils/auth.js' +import { saveGlobalConfig } from '../../utils/config.js' +import { logForDebugging } from '../../utils/debug.js' +import { isRunningOnHomespace } from '../../utils/envUtils.js' +import { errorMessage } from '../../utils/errors.js' +import { logError } from '../../utils/log.js' +import { getAPIProvider } from '../../utils/model/providers.js' +import { getInitialSettings } from '../../utils/settings/settings.js' +import { jsonStringify } from '../../utils/slowOperations.js' +import { + buildAccountProperties, + buildAPIProviderProperties, +} from '../../utils/status.js' + +/** + * Shared post-token-acquisition logic. Saves tokens, fetches profile/roles, + * and sets up the local auth state. + */ +export async function installOAuthTokens(tokens: OAuthTokens): Promise { + // Clear old state before saving new credentials + await performLogout({ clearOnboarding: false }) + + // Reuse pre-fetched profile if available, otherwise fetch fresh + const profile = + tokens.profile ?? (await getOauthProfileFromOauthToken(tokens.accessToken)) + if (profile) { + storeOAuthAccountInfo({ + accountUuid: profile.account.uuid, + emailAddress: profile.account.email, + organizationUuid: profile.organization.uuid, + displayName: profile.account.display_name || undefined, + hasExtraUsageEnabled: + profile.organization.has_extra_usage_enabled ?? undefined, + billingType: profile.organization.billing_type ?? undefined, + subscriptionCreatedAt: + profile.organization.subscription_created_at ?? undefined, + accountCreatedAt: profile.account.created_at, + }) + } else if (tokens.tokenAccount) { + // Fallback to token exchange account data when profile endpoint fails + storeOAuthAccountInfo({ + accountUuid: tokens.tokenAccount.uuid, + emailAddress: tokens.tokenAccount.emailAddress, + organizationUuid: tokens.tokenAccount.organizationUuid, + }) + } + + const storageResult = saveOAuthTokensIfNeeded(tokens) + clearOAuthTokenCache() + + if (storageResult.warning) { + logEvent('tengu_oauth_storage_warning', { + warning: + storageResult.warning as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + } + + // Roles and first-token-date may fail for limited-scope tokens (e.g. + // inference-only from setup-token). They're not required for core auth. + await fetchAndStoreUserRoles(tokens.accessToken).catch(err => + logForDebugging(String(err), { level: 'error' }), + ) + + if (shouldUseClaudeAIAuth(tokens.scopes)) { + await fetchAndStoreClaudeCodeFirstTokenDate().catch(err => + logForDebugging(String(err), { level: 'error' }), + ) + } else { + // API key creation is critical for Console users — let it throw. + const apiKey = await createAndStoreApiKey(tokens.accessToken) + if (!apiKey) { + throw new Error( + 'Unable to create API key. The server accepted the request but did not return a key.', + ) + } + } + + await clearAuthRelatedCaches() +} + +export async function authLogin({ + email, + sso, + console: useConsole, + claudeai, +}: { + email?: string + sso?: boolean + console?: boolean + claudeai?: boolean +}): Promise { + if (useConsole && claudeai) { + process.stderr.write( + 'Error: --console and --claudeai cannot be used together.\n', + ) + process.exit(1) + } + + const settings = getInitialSettings() + // forceLoginMethod is a hard constraint (enterprise setting) — matches ConsoleOAuthFlow behavior. + // Without it, --console selects Console; --claudeai (or no flag) selects claude.ai. + const loginWithClaudeAi = settings.forceLoginMethod + ? settings.forceLoginMethod === 'claudeai' + : !useConsole + const orgUUID = settings.forceLoginOrgUUID + + // Fast path: if a refresh token is provided via env var, skip the browser + // OAuth flow and exchange it directly for tokens. + const envRefreshToken = process.env.CLAUDE_CODE_OAUTH_REFRESH_TOKEN + if (envRefreshToken) { + const envScopes = process.env.CLAUDE_CODE_OAUTH_SCOPES + if (!envScopes) { + process.stderr.write( + 'CLAUDE_CODE_OAUTH_SCOPES is required when using CLAUDE_CODE_OAUTH_REFRESH_TOKEN.\n' + + 'Set it to the space-separated scopes the refresh token was issued with\n' + + '(e.g. "user:inference" or "user:profile user:inference user:sessions:claude_code user:mcp_servers").\n', + ) + process.exit(1) + } + + const scopes = envScopes.split(/\s+/).filter(Boolean) + + try { + logEvent('tengu_login_from_refresh_token', {}) + + const tokens = await refreshOAuthToken(envRefreshToken, { scopes }) + await installOAuthTokens(tokens) + + const orgResult = await validateForceLoginOrg() + if (!orgResult.valid) { + process.stderr.write(orgResult.message + '\n') + process.exit(1) + } + + // Mark onboarding complete — interactive paths handle this via + // the Onboarding component, but the env var path skips it. + saveGlobalConfig(current => { + if (current.hasCompletedOnboarding) return current + return { ...current, hasCompletedOnboarding: true } + }) + + logEvent('tengu_oauth_success', { + loginWithClaudeAi: shouldUseClaudeAIAuth(tokens.scopes), + }) + process.stdout.write('Login successful.\n') + process.exit(0) + } catch (err) { + logError(err) + const sslHint = getSSLErrorHint(err) + process.stderr.write( + `Login failed: ${errorMessage(err)}\n${sslHint ? sslHint + '\n' : ''}`, + ) + process.exit(1) + } + } + + const resolvedLoginMethod = sso ? 'sso' : undefined + + const oauthService = new OAuthService() + + try { + logEvent('tengu_oauth_flow_start', { loginWithClaudeAi }) + + const result = await oauthService.startOAuthFlow( + async url => { + process.stdout.write('Opening browser to sign in…\n') + process.stdout.write(`If the browser didn't open, visit: ${url}\n`) + }, + { + loginWithClaudeAi, + loginHint: email, + loginMethod: resolvedLoginMethod, + orgUUID, + }, + ) + + await installOAuthTokens(result) + + const orgResult = await validateForceLoginOrg() + if (!orgResult.valid) { + process.stderr.write(orgResult.message + '\n') + process.exit(1) + } + + logEvent('tengu_oauth_success', { loginWithClaudeAi }) + + process.stdout.write('Login successful.\n') + process.exit(0) + } catch (err) { + logError(err) + const sslHint = getSSLErrorHint(err) + process.stderr.write( + `Login failed: ${errorMessage(err)}\n${sslHint ? sslHint + '\n' : ''}`, + ) + process.exit(1) + } finally { + oauthService.cleanup() + } +} + +export async function authStatus(opts: { + json?: boolean + text?: boolean +}): Promise { + const { source: authTokenSource, hasToken } = getAuthTokenSource() + const { source: apiKeySource } = getAnthropicApiKeyWithSource() + const hasApiKeyEnvVar = + !!process.env.ANTHROPIC_API_KEY && !isRunningOnHomespace() + const oauthAccount = getOauthAccountInfo() + const subscriptionType = getSubscriptionType() + const using3P = isUsing3PServices() + const loggedIn = + hasToken || apiKeySource !== 'none' || hasApiKeyEnvVar || using3P + + // Determine auth method + let authMethod: string = 'none' + if (using3P) { + authMethod = 'third_party' + } else if (authTokenSource === 'claude.ai') { + authMethod = 'claude.ai' + } else if (authTokenSource === 'apiKeyHelper') { + authMethod = 'api_key_helper' + } else if (authTokenSource !== 'none') { + authMethod = 'oauth_token' + } else if (apiKeySource === 'ANTHROPIC_API_KEY' || hasApiKeyEnvVar) { + authMethod = 'api_key' + } else if (apiKeySource === '/login managed key') { + authMethod = 'claude.ai' + } + + if (opts.text) { + const properties = [ + ...buildAccountProperties(), + ...buildAPIProviderProperties(), + ] + let hasAuthProperty = false + for (const prop of properties) { + const value = + typeof prop.value === 'string' + ? prop.value + : Array.isArray(prop.value) + ? prop.value.join(', ') + : null + if (value === null || value === 'none') { + continue + } + hasAuthProperty = true + if (prop.label) { + process.stdout.write(`${prop.label}: ${value}\n`) + } else { + process.stdout.write(`${value}\n`) + } + } + if (!hasAuthProperty && hasApiKeyEnvVar) { + process.stdout.write('API key: ANTHROPIC_API_KEY\n') + } + if (!loggedIn) { + process.stdout.write( + 'Not logged in. Run claude auth login to authenticate.\n', + ) + } + } else { + const apiProvider = getAPIProvider() + const resolvedApiKeySource = + apiKeySource !== 'none' + ? apiKeySource + : hasApiKeyEnvVar + ? 'ANTHROPIC_API_KEY' + : null + const output: Record = { + loggedIn, + authMethod, + apiProvider, + } + if (resolvedApiKeySource) { + output.apiKeySource = resolvedApiKeySource + } + if (authMethod === 'claude.ai') { + output.email = oauthAccount?.emailAddress ?? null + output.orgId = oauthAccount?.organizationUuid ?? null + output.orgName = oauthAccount?.organizationName ?? null + output.subscriptionType = subscriptionType ?? null + } + + process.stdout.write(jsonStringify(output, null, 2) + '\n') + } + process.exit(loggedIn ? 0 : 1) +} + +export async function authLogout(): Promise { + try { + await performLogout({ clearOnboarding: false }) + } catch { + process.stderr.write('Failed to log out.\n') + process.exit(1) + } + process.stdout.write('Successfully logged out from your Anthropic account.\n') + process.exit(0) +} diff --git a/cli/handlers/autoMode.ts b/cli/handlers/autoMode.ts new file mode 100644 index 0000000..fb2c3d2 --- /dev/null +++ b/cli/handlers/autoMode.ts @@ -0,0 +1,170 @@ +/** + * Auto mode subcommand handlers — dump default/merged classifier rules and + * critique user-written rules. Dynamically imported when `claude auto-mode ...` runs. + */ + +import { errorMessage } from '../../utils/errors.js' +import { + getMainLoopModel, + parseUserSpecifiedModel, +} from '../../utils/model/model.js' +import { + type AutoModeRules, + buildDefaultExternalSystemPrompt, + getDefaultExternalAutoModeRules, +} from '../../utils/permissions/yoloClassifier.js' +import { getAutoModeConfig } from '../../utils/settings/settings.js' +import { sideQuery } from '../../utils/sideQuery.js' +import { jsonStringify } from '../../utils/slowOperations.js' + +function writeRules(rules: AutoModeRules): void { + process.stdout.write(jsonStringify(rules, null, 2) + '\n') +} + +export function autoModeDefaultsHandler(): void { + writeRules(getDefaultExternalAutoModeRules()) +} + +/** + * Dump the effective auto mode config: user settings where provided, external + * defaults otherwise. Per-section REPLACE semantics — matches how + * buildYoloSystemPrompt resolves the external template (a non-empty user + * section replaces that section's defaults entirely; an empty/absent section + * falls through to defaults). + */ +export function autoModeConfigHandler(): void { + const config = getAutoModeConfig() + const defaults = getDefaultExternalAutoModeRules() + writeRules({ + allow: config?.allow?.length ? config.allow : defaults.allow, + soft_deny: config?.soft_deny?.length + ? config.soft_deny + : defaults.soft_deny, + environment: config?.environment?.length + ? config.environment + : defaults.environment, + }) +} + +const CRITIQUE_SYSTEM_PROMPT = + 'You are an expert reviewer of auto mode classifier rules for Claude Code.\n' + + '\n' + + 'Claude Code has an "auto mode" that uses an AI classifier to decide whether ' + + 'tool calls should be auto-approved or require user confirmation. Users can ' + + 'write custom rules in three categories:\n' + + '\n' + + '- **allow**: Actions the classifier should auto-approve\n' + + '- **soft_deny**: Actions the classifier should block (require user confirmation)\n' + + "- **environment**: Context about the user's setup that helps the classifier make decisions\n" + + '\n' + + "Your job is to critique the user's custom rules for clarity, completeness, " + + 'and potential issues. The classifier is an LLM that reads these rules as ' + + 'part of its system prompt.\n' + + '\n' + + 'For each rule, evaluate:\n' + + '1. **Clarity**: Is the rule unambiguous? Could the classifier misinterpret it?\n' + + "2. **Completeness**: Are there gaps or edge cases the rule doesn't cover?\n" + + '3. **Conflicts**: Do any of the rules conflict with each other?\n' + + '4. **Actionability**: Is the rule specific enough for the classifier to act on?\n' + + '\n' + + 'Be concise and constructive. Only comment on rules that could be improved. ' + + 'If all rules look good, say so.' + +export async function autoModeCritiqueHandler(options: { + model?: string +}): Promise { + const config = getAutoModeConfig() + const hasCustomRules = + (config?.allow?.length ?? 0) > 0 || + (config?.soft_deny?.length ?? 0) > 0 || + (config?.environment?.length ?? 0) > 0 + + if (!hasCustomRules) { + process.stdout.write( + 'No custom auto mode rules found.\n\n' + + 'Add rules to your settings file under autoMode.{allow, soft_deny, environment}.\n' + + 'Run `claude auto-mode defaults` to see the default rules for reference.\n', + ) + return + } + + const model = options.model + ? parseUserSpecifiedModel(options.model) + : getMainLoopModel() + + const defaults = getDefaultExternalAutoModeRules() + const classifierPrompt = buildDefaultExternalSystemPrompt() + + const userRulesSummary = + formatRulesForCritique('allow', config?.allow ?? [], defaults.allow) + + formatRulesForCritique( + 'soft_deny', + config?.soft_deny ?? [], + defaults.soft_deny, + ) + + formatRulesForCritique( + 'environment', + config?.environment ?? [], + defaults.environment, + ) + + process.stdout.write('Analyzing your auto mode rules…\n\n') + + let response + try { + response = await sideQuery({ + querySource: 'auto_mode_critique', + model, + system: CRITIQUE_SYSTEM_PROMPT, + skipSystemPromptPrefix: true, + max_tokens: 4096, + messages: [ + { + role: 'user', + content: + 'Here is the full classifier system prompt that the auto mode classifier receives:\n\n' + + '\n' + + classifierPrompt + + '\n\n\n' + + "Here are the user's custom rules that REPLACE the corresponding default sections:\n\n" + + userRulesSummary + + '\nPlease critique these custom rules.', + }, + ], + }) + } catch (error) { + process.stderr.write( + 'Failed to analyze rules: ' + errorMessage(error) + '\n', + ) + process.exitCode = 1 + return + } + + const textBlock = response.content.find(block => block.type === 'text') + if (textBlock?.type === 'text') { + process.stdout.write(textBlock.text + '\n') + } else { + process.stdout.write('No critique was generated. Please try again.\n') + } +} + +function formatRulesForCritique( + section: string, + userRules: string[], + defaultRules: string[], +): string { + if (userRules.length === 0) return '' + const customLines = userRules.map(r => '- ' + r).join('\n') + const defaultLines = defaultRules.map(r => '- ' + r).join('\n') + return ( + '## ' + + section + + ' (custom rules replacing defaults)\n' + + 'Custom:\n' + + customLines + + '\n\n' + + 'Defaults being replaced:\n' + + defaultLines + + '\n\n' + ) +} diff --git a/cli/handlers/mcp.tsx b/cli/handlers/mcp.tsx new file mode 100644 index 0000000..e530c26 --- /dev/null +++ b/cli/handlers/mcp.tsx @@ -0,0 +1,362 @@ +/** + * MCP subcommand handlers — extracted from main.tsx for lazy loading. + * These are dynamically imported only when the corresponding `claude mcp *` command runs. + */ + +import { stat } from 'fs/promises'; +import pMap from 'p-map'; +import { cwd } from 'process'; +import React from 'react'; +import { MCPServerDesktopImportDialog } from '../../components/MCPServerDesktopImportDialog.js'; +import { render } from '../../ink.js'; +import { KeybindingSetup } from '../../keybindings/KeybindingProviderSetup.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../services/analytics/index.js'; +import { clearMcpClientConfig, clearServerTokensFromLocalStorage, getMcpClientConfig, readClientSecret, saveMcpClientSecret } from '../../services/mcp/auth.js'; +import { connectToServer, getMcpServerConnectionBatchSize } from '../../services/mcp/client.js'; +import { addMcpConfig, getAllMcpConfigs, getMcpConfigByName, getMcpConfigsByScope, removeMcpConfig } from '../../services/mcp/config.js'; +import type { ConfigScope, ScopedMcpServerConfig } from '../../services/mcp/types.js'; +import { describeMcpConfigFilePath, ensureConfigScope, getScopeLabel } from '../../services/mcp/utils.js'; +import { AppStateProvider } from '../../state/AppState.js'; +import { getCurrentProjectConfig, getGlobalConfig, saveCurrentProjectConfig } from '../../utils/config.js'; +import { isFsInaccessible } from '../../utils/errors.js'; +import { gracefulShutdown } from '../../utils/gracefulShutdown.js'; +import { safeParseJSON } from '../../utils/json.js'; +import { getPlatform } from '../../utils/platform.js'; +import { cliError, cliOk } from '../exit.js'; +async function checkMcpServerHealth(name: string, server: ScopedMcpServerConfig): Promise { + try { + const result = await connectToServer(name, server); + if (result.type === 'connected') { + return '✓ Connected'; + } else if (result.type === 'needs-auth') { + return '! Needs authentication'; + } else { + return '✗ Failed to connect'; + } + } catch (_error) { + return '✗ Connection error'; + } +} + +// mcp serve (lines 4512–4532) +export async function mcpServeHandler({ + debug, + verbose +}: { + debug?: boolean; + verbose?: boolean; +}): Promise { + const providedCwd = cwd(); + logEvent('tengu_mcp_start', {}); + try { + await stat(providedCwd); + } catch (error) { + if (isFsInaccessible(error)) { + cliError(`Error: Directory ${providedCwd} does not exist`); + } + throw error; + } + try { + const { + setup + } = await import('../../setup.js'); + await setup(providedCwd, 'default', false, false, undefined, false); + const { + startMCPServer + } = await import('../../entrypoints/mcp.js'); + await startMCPServer(providedCwd, debug ?? false, verbose ?? false); + } catch (error) { + cliError(`Error: Failed to start MCP server: ${error}`); + } +} + +// mcp remove (lines 4545–4635) +export async function mcpRemoveHandler(name: string, options: { + scope?: string; +}): Promise { + // Look up config before removing so we can clean up secure storage + const serverBeforeRemoval = getMcpConfigByName(name); + const cleanupSecureStorage = () => { + if (serverBeforeRemoval && (serverBeforeRemoval.type === 'sse' || serverBeforeRemoval.type === 'http')) { + clearServerTokensFromLocalStorage(name, serverBeforeRemoval); + clearMcpClientConfig(name, serverBeforeRemoval); + } + }; + try { + if (options.scope) { + const scope = ensureConfigScope(options.scope); + logEvent('tengu_mcp_delete', { + name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + scope: scope as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + await removeMcpConfig(name, scope); + cleanupSecureStorage(); + process.stdout.write(`Removed MCP server ${name} from ${scope} config\n`); + cliOk(`File modified: ${describeMcpConfigFilePath(scope)}`); + } + + // If no scope specified, check where the server exists + const projectConfig = getCurrentProjectConfig(); + const globalConfig = getGlobalConfig(); + + // Check if server exists in project scope (.mcp.json) + const { + servers: projectServers + } = getMcpConfigsByScope('project'); + const mcpJsonExists = !!projectServers[name]; + + // Count how many scopes contain this server + const scopes: Array> = []; + if (projectConfig.mcpServers?.[name]) scopes.push('local'); + if (mcpJsonExists) scopes.push('project'); + if (globalConfig.mcpServers?.[name]) scopes.push('user'); + if (scopes.length === 0) { + cliError(`No MCP server found with name: "${name}"`); + } else if (scopes.length === 1) { + // Server exists in only one scope, remove it + const scope = scopes[0]!; + logEvent('tengu_mcp_delete', { + name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + scope: scope as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + await removeMcpConfig(name, scope); + cleanupSecureStorage(); + process.stdout.write(`Removed MCP server "${name}" from ${scope} config\n`); + cliOk(`File modified: ${describeMcpConfigFilePath(scope)}`); + } else { + // Server exists in multiple scopes + process.stderr.write(`MCP server "${name}" exists in multiple scopes:\n`); + scopes.forEach(scope => { + process.stderr.write(` - ${getScopeLabel(scope)} (${describeMcpConfigFilePath(scope)})\n`); + }); + process.stderr.write('\nTo remove from a specific scope, use:\n'); + scopes.forEach(scope => { + process.stderr.write(` claude mcp remove "${name}" -s ${scope}\n`); + }); + cliError(); + } + } catch (error) { + cliError((error as Error).message); + } +} + +// mcp list (lines 4641–4688) +export async function mcpListHandler(): Promise { + logEvent('tengu_mcp_list', {}); + const { + servers: configs + } = await getAllMcpConfigs(); + if (Object.keys(configs).length === 0) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('No MCP servers configured. Use `claude mcp add` to add a server.'); + } else { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('Checking MCP server health...\n'); + + // Check servers concurrently + const entries = Object.entries(configs); + const results = await pMap(entries, async ([name, server]) => ({ + name, + server, + status: await checkMcpServerHealth(name, server) + }), { + concurrency: getMcpServerConnectionBatchSize() + }); + for (const { + name, + server, + status + } of results) { + // Intentionally excluding sse-ide servers here since they're internal + if (server.type === 'sse') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`${name}: ${server.url} (SSE) - ${status}`); + } else if (server.type === 'http') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`${name}: ${server.url} (HTTP) - ${status}`); + } else if (server.type === 'claudeai-proxy') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`${name}: ${server.url} - ${status}`); + } else if (!server.type || server.type === 'stdio') { + const args = Array.isArray(server.args) ? server.args : []; + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`${name}: ${server.command} ${args.join(' ')} - ${status}`); + } + } + } + // Use gracefulShutdown to properly clean up MCP server connections + // (process.exit bypasses cleanup handlers, leaving child processes orphaned) + await gracefulShutdown(0); +} + +// mcp get (lines 4694–4786) +export async function mcpGetHandler(name: string): Promise { + logEvent('tengu_mcp_get', { + name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + const server = getMcpConfigByName(name); + if (!server) { + cliError(`No MCP server found with name: ${name}`); + } + + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`${name}:`); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Scope: ${getScopeLabel(server.scope)}`); + + // Check server health + const status = await checkMcpServerHealth(name, server); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Status: ${status}`); + + // Intentionally excluding sse-ide servers here since they're internal + if (server.type === 'sse') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Type: sse`); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` URL: ${server.url}`); + if (server.headers) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(' Headers:'); + for (const [key, value] of Object.entries(server.headers)) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` ${key}: ${value}`); + } + } + if (server.oauth?.clientId || server.oauth?.callbackPort) { + const parts: string[] = []; + if (server.oauth.clientId) { + parts.push('client_id configured'); + const clientConfig = getMcpClientConfig(name, server); + if (clientConfig?.clientSecret) parts.push('client_secret configured'); + } + if (server.oauth.callbackPort) parts.push(`callback_port ${server.oauth.callbackPort}`); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` OAuth: ${parts.join(', ')}`); + } + } else if (server.type === 'http') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Type: http`); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` URL: ${server.url}`); + if (server.headers) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(' Headers:'); + for (const [key, value] of Object.entries(server.headers)) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` ${key}: ${value}`); + } + } + if (server.oauth?.clientId || server.oauth?.callbackPort) { + const parts: string[] = []; + if (server.oauth.clientId) { + parts.push('client_id configured'); + const clientConfig = getMcpClientConfig(name, server); + if (clientConfig?.clientSecret) parts.push('client_secret configured'); + } + if (server.oauth.callbackPort) parts.push(`callback_port ${server.oauth.callbackPort}`); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` OAuth: ${parts.join(', ')}`); + } + } else if (server.type === 'stdio') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Type: stdio`); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Command: ${server.command}`); + const args = Array.isArray(server.args) ? server.args : []; + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Args: ${args.join(' ')}`); + if (server.env) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(' Environment:'); + for (const [key, value] of Object.entries(server.env)) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` ${key}=${value}`); + } + } + } + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`\nTo remove this server, run: claude mcp remove "${name}" -s ${server.scope}`); + // Use gracefulShutdown to properly clean up MCP server connections + // (process.exit bypasses cleanup handlers, leaving child processes orphaned) + await gracefulShutdown(0); +} + +// mcp add-json (lines 4801–4870) +export async function mcpAddJsonHandler(name: string, json: string, options: { + scope?: string; + clientSecret?: true; +}): Promise { + try { + const scope = ensureConfigScope(options.scope); + const parsedJson = safeParseJSON(json); + + // Read secret before writing config so cancellation doesn't leave partial state + const needsSecret = options.clientSecret && parsedJson && typeof parsedJson === 'object' && 'type' in parsedJson && (parsedJson.type === 'sse' || parsedJson.type === 'http') && 'url' in parsedJson && typeof parsedJson.url === 'string' && 'oauth' in parsedJson && parsedJson.oauth && typeof parsedJson.oauth === 'object' && 'clientId' in parsedJson.oauth; + const clientSecret = needsSecret ? await readClientSecret() : undefined; + await addMcpConfig(name, parsedJson, scope); + const transportType = parsedJson && typeof parsedJson === 'object' && 'type' in parsedJson ? String(parsedJson.type || 'stdio') : 'stdio'; + if (clientSecret && parsedJson && typeof parsedJson === 'object' && 'type' in parsedJson && (parsedJson.type === 'sse' || parsedJson.type === 'http') && 'url' in parsedJson && typeof parsedJson.url === 'string') { + saveMcpClientSecret(name, { + type: parsedJson.type, + url: parsedJson.url + }, clientSecret); + } + logEvent('tengu_mcp_add', { + scope: scope as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + source: 'json' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + type: transportType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + cliOk(`Added ${transportType} MCP server ${name} to ${scope} config`); + } catch (error) { + cliError((error as Error).message); + } +} + +// mcp add-from-claude-desktop (lines 4881–4927) +export async function mcpAddFromDesktopHandler(options: { + scope?: string; +}): Promise { + try { + const scope = ensureConfigScope(options.scope); + const platform = getPlatform(); + logEvent('tengu_mcp_add', { + scope: scope as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + platform: platform as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + source: 'desktop' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + const { + readClaudeDesktopMcpServers + } = await import('../../utils/claudeDesktop.js'); + const servers = await readClaudeDesktopMcpServers(); + if (Object.keys(servers).length === 0) { + cliOk('No MCP servers found in Claude Desktop configuration or configuration file does not exist.'); + } + const { + unmount + } = await render( + + { + unmount(); + }} /> + + , { + exitOnCtrlC: true + }); + } catch (error) { + cliError((error as Error).message); + } +} + +// mcp reset-project-choices (lines 4935–4952) +export async function mcpResetChoicesHandler(): Promise { + logEvent('tengu_mcp_reset_mcpjson_choices', {}); + saveCurrentProjectConfig(current => ({ + ...current, + enabledMcpjsonServers: [], + disabledMcpjsonServers: [], + enableAllProjectMcpServers: false + })); + cliOk('All project-scoped (.mcp.json) server approvals and rejections have been reset.\n' + 'You will be prompted for approval next time you start Claude Code.'); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJzdGF0IiwicE1hcCIsImN3ZCIsIlJlYWN0IiwiTUNQU2VydmVyRGVza3RvcEltcG9ydERpYWxvZyIsInJlbmRlciIsIktleWJpbmRpbmdTZXR1cCIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsImNsZWFyTWNwQ2xpZW50Q29uZmlnIiwiY2xlYXJTZXJ2ZXJUb2tlbnNGcm9tTG9jYWxTdG9yYWdlIiwiZ2V0TWNwQ2xpZW50Q29uZmlnIiwicmVhZENsaWVudFNlY3JldCIsInNhdmVNY3BDbGllbnRTZWNyZXQiLCJjb25uZWN0VG9TZXJ2ZXIiLCJnZXRNY3BTZXJ2ZXJDb25uZWN0aW9uQmF0Y2hTaXplIiwiYWRkTWNwQ29uZmlnIiwiZ2V0QWxsTWNwQ29uZmlncyIsImdldE1jcENvbmZpZ0J5TmFtZSIsImdldE1jcENvbmZpZ3NCeVNjb3BlIiwicmVtb3ZlTWNwQ29uZmlnIiwiQ29uZmlnU2NvcGUiLCJTY29wZWRNY3BTZXJ2ZXJDb25maWciLCJkZXNjcmliZU1jcENvbmZpZ0ZpbGVQYXRoIiwiZW5zdXJlQ29uZmlnU2NvcGUiLCJnZXRTY29wZUxhYmVsIiwiQXBwU3RhdGVQcm92aWRlciIsImdldEN1cnJlbnRQcm9qZWN0Q29uZmlnIiwiZ2V0R2xvYmFsQ29uZmlnIiwic2F2ZUN1cnJlbnRQcm9qZWN0Q29uZmlnIiwiaXNGc0luYWNjZXNzaWJsZSIsImdyYWNlZnVsU2h1dGRvd24iLCJzYWZlUGFyc2VKU09OIiwiZ2V0UGxhdGZvcm0iLCJjbGlFcnJvciIsImNsaU9rIiwiY2hlY2tNY3BTZXJ2ZXJIZWFsdGgiLCJuYW1lIiwic2VydmVyIiwiUHJvbWlzZSIsInJlc3VsdCIsInR5cGUiLCJfZXJyb3IiLCJtY3BTZXJ2ZUhhbmRsZXIiLCJkZWJ1ZyIsInZlcmJvc2UiLCJwcm92aWRlZEN3ZCIsImVycm9yIiwic2V0dXAiLCJ1bmRlZmluZWQiLCJzdGFydE1DUFNlcnZlciIsIm1jcFJlbW92ZUhhbmRsZXIiLCJvcHRpb25zIiwic2NvcGUiLCJzZXJ2ZXJCZWZvcmVSZW1vdmFsIiwiY2xlYW51cFNlY3VyZVN0b3JhZ2UiLCJwcm9jZXNzIiwic3Rkb3V0Iiwid3JpdGUiLCJwcm9qZWN0Q29uZmlnIiwiZ2xvYmFsQ29uZmlnIiwic2VydmVycyIsInByb2plY3RTZXJ2ZXJzIiwibWNwSnNvbkV4aXN0cyIsInNjb3BlcyIsIkFycmF5IiwiRXhjbHVkZSIsIm1jcFNlcnZlcnMiLCJwdXNoIiwibGVuZ3RoIiwic3RkZXJyIiwiZm9yRWFjaCIsIkVycm9yIiwibWVzc2FnZSIsIm1jcExpc3RIYW5kbGVyIiwiY29uZmlncyIsIk9iamVjdCIsImtleXMiLCJjb25zb2xlIiwibG9nIiwiZW50cmllcyIsInJlc3VsdHMiLCJzdGF0dXMiLCJjb25jdXJyZW5jeSIsInVybCIsImFyZ3MiLCJpc0FycmF5IiwiY29tbWFuZCIsImpvaW4iLCJtY3BHZXRIYW5kbGVyIiwiaGVhZGVycyIsImtleSIsInZhbHVlIiwib2F1dGgiLCJjbGllbnRJZCIsImNhbGxiYWNrUG9ydCIsInBhcnRzIiwiY2xpZW50Q29uZmlnIiwiY2xpZW50U2VjcmV0IiwiZW52IiwibWNwQWRkSnNvbkhhbmRsZXIiLCJqc29uIiwicGFyc2VkSnNvbiIsIm5lZWRzU2VjcmV0IiwidHJhbnNwb3J0VHlwZSIsIlN0cmluZyIsInNvdXJjZSIsIm1jcEFkZEZyb21EZXNrdG9wSGFuZGxlciIsInBsYXRmb3JtIiwicmVhZENsYXVkZURlc2t0b3BNY3BTZXJ2ZXJzIiwidW5tb3VudCIsImV4aXRPbkN0cmxDIiwibWNwUmVzZXRDaG9pY2VzSGFuZGxlciIsImN1cnJlbnQiLCJlbmFibGVkTWNwanNvblNlcnZlcnMiLCJkaXNhYmxlZE1jcGpzb25TZXJ2ZXJzIiwiZW5hYmxlQWxsUHJvamVjdE1jcFNlcnZlcnMiXSwic291cmNlcyI6WyJtY3AudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTUNQIHN1YmNvbW1hbmQgaGFuZGxlcnMg4oCUIGV4dHJhY3RlZCBmcm9tIG1haW4udHN4IGZvciBsYXp5IGxvYWRpbmcuXG4gKiBUaGVzZSBhcmUgZHluYW1pY2FsbHkgaW1wb3J0ZWQgb25seSB3aGVuIHRoZSBjb3JyZXNwb25kaW5nIGBjbGF1ZGUgbWNwICpgIGNvbW1hbmQgcnVucy5cbiAqL1xuXG5pbXBvcnQgeyBzdGF0IH0gZnJvbSAnZnMvcHJvbWlzZXMnXG5pbXBvcnQgcE1hcCBmcm9tICdwLW1hcCdcbmltcG9ydCB7IGN3ZCB9IGZyb20gJ3Byb2Nlc3MnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBNQ1BTZXJ2ZXJEZXNrdG9wSW1wb3J0RGlhbG9nIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9NQ1BTZXJ2ZXJEZXNrdG9wSW1wb3J0RGlhbG9nLmpzJ1xuaW1wb3J0IHsgcmVuZGVyIH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgS2V5YmluZGluZ1NldHVwIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvS2V5YmluZGluZ1Byb3ZpZGVyU2V0dXAuanMnXG5pbXBvcnQge1xuICB0eXBlIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIGxvZ0V2ZW50LFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hbmFseXRpY3MvaW5kZXguanMnXG5pbXBvcnQge1xuICBjbGVhck1jcENsaWVudENvbmZpZyxcbiAgY2xlYXJTZXJ2ZXJUb2tlbnNGcm9tTG9jYWxTdG9yYWdlLFxuICBnZXRNY3BDbGllbnRDb25maWcsXG4gIHJlYWRDbGllbnRTZWNyZXQsXG4gIHNhdmVNY3BDbGllbnRTZWNyZXQsXG59IGZyb20gJy4uLy4uL3NlcnZpY2VzL21jcC9hdXRoLmpzJ1xuaW1wb3J0IHtcbiAgY29ubmVjdFRvU2VydmVyLFxuICBnZXRNY3BTZXJ2ZXJDb25uZWN0aW9uQmF0Y2hTaXplLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvY2xpZW50LmpzJ1xuaW1wb3J0IHtcbiAgYWRkTWNwQ29uZmlnLFxuICBnZXRBbGxNY3BDb25maWdzLFxuICBnZXRNY3BDb25maWdCeU5hbWUsXG4gIGdldE1jcENvbmZpZ3NCeVNjb3BlLFxuICByZW1vdmVNY3BDb25maWcsXG59IGZyb20gJy4uLy4uL3NlcnZpY2VzL21jcC9jb25maWcuanMnXG5pbXBvcnQgdHlwZSB7XG4gIENvbmZpZ1Njb3BlLFxuICBTY29wZWRNY3BTZXJ2ZXJDb25maWcsXG59IGZyb20gJy4uLy4uL3NlcnZpY2VzL21jcC90eXBlcy5qcydcbmltcG9ydCB7XG4gIGRlc2NyaWJlTWNwQ29uZmlnRmlsZVBhdGgsXG4gIGVuc3VyZUNvbmZpZ1Njb3BlLFxuICBnZXRTY29wZUxhYmVsLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvdXRpbHMuanMnXG5pbXBvcnQgeyBBcHBTdGF0ZVByb3ZpZGVyIH0gZnJvbSAnLi4vLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQge1xuICBnZXRDdXJyZW50UHJvamVjdENvbmZpZyxcbiAgZ2V0R2xvYmFsQ29uZmlnLFxuICBzYXZlQ3VycmVudFByb2plY3RDb25maWcsXG59IGZyb20gJy4uLy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGlzRnNJbmFjY2Vzc2libGUgfSBmcm9tICcuLi8uLi91dGlscy9lcnJvcnMuanMnXG5pbXBvcnQgeyBncmFjZWZ1bFNodXRkb3duIH0gZnJvbSAnLi4vLi4vdXRpbHMvZ3JhY2VmdWxTaHV0ZG93bi5qcydcbmltcG9ydCB7IHNhZmVQYXJzZUpTT04gfSBmcm9tICcuLi8uLi91dGlscy9qc29uLmpzJ1xuaW1wb3J0IHsgZ2V0UGxhdGZvcm0gfSBmcm9tICcuLi8uLi91dGlscy9wbGF0Zm9ybS5qcydcbmltcG9ydCB7IGNsaUVycm9yLCBjbGlPayB9IGZyb20gJy4uL2V4aXQuanMnXG5cbmFzeW5jIGZ1bmN0aW9uIGNoZWNrTWNwU2VydmVySGVhbHRoKFxuICBuYW1lOiBzdHJpbmcsXG4gIHNlcnZlcjogU2NvcGVkTWNwU2VydmVyQ29uZmlnLFxuKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBjb25uZWN0VG9TZXJ2ZXIobmFtZSwgc2VydmVyKVxuICAgIGlmIChyZXN1bHQudHlwZSA9PT0gJ2Nvbm5lY3RlZCcpIHtcbiAgICAgIHJldHVybiAn4pyTIENvbm5lY3RlZCdcbiAgICB9IGVsc2UgaWYgKHJlc3VsdC50eXBlID09PSAnbmVlZHMtYXV0aCcpIHtcbiAgICAgIHJldHVybiAnISBOZWVkcyBhdXRoZW50aWNhdGlvbidcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuICfinJcgRmFpbGVkIHRvIGNvbm5lY3QnXG4gICAgfVxuICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICByZXR1cm4gJ+KclyBDb25uZWN0aW9uIGVycm9yJ1xuICB9XG59XG5cbi8vIG1jcCBzZXJ2ZSAobGluZXMgNDUxMuKAkzQ1MzIpXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWNwU2VydmVIYW5kbGVyKHtcbiAgZGVidWcsXG4gIHZlcmJvc2UsXG59OiB7XG4gIGRlYnVnPzogYm9vbGVhblxuICB2ZXJib3NlPzogYm9vbGVhblxufSk6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBwcm92aWRlZEN3ZCA9IGN3ZCgpXG4gIGxvZ0V2ZW50KCd0ZW5ndV9tY3Bfc3RhcnQnLCB7fSlcblxuICB0cnkge1xuICAgIGF3YWl0IHN0YXQocHJvdmlkZWRDd2QpXG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgaWYgKGlzRnNJbmFjY2Vzc2libGUoZXJyb3IpKSB7XG4gICAgICBjbGlFcnJvcihgRXJyb3I6IERpcmVjdG9yeSAke3Byb3ZpZGVkQ3dkfSBkb2VzIG5vdCBleGlzdGApXG4gICAgfVxuICAgIHRocm93IGVycm9yXG4gIH1cblxuICB0cnkge1xuICAgIGNvbnN0IHsgc2V0dXAgfSA9IGF3YWl0IGltcG9ydCgnLi4vLi4vc2V0dXAuanMnKVxuICAgIGF3YWl0IHNldHVwKHByb3ZpZGVkQ3dkLCAnZGVmYXVsdCcsIGZhbHNlLCBmYWxzZSwgdW5kZWZpbmVkLCBmYWxzZSlcbiAgICBjb25zdCB7IHN0YXJ0TUNQU2VydmVyIH0gPSBhd2FpdCBpbXBvcnQoJy4uLy4uL2VudHJ5cG9pbnRzL21jcC5qcycpXG4gICAgYXdhaXQgc3RhcnRNQ1BTZXJ2ZXIocHJvdmlkZWRDd2QsIGRlYnVnID8/IGZhbHNlLCB2ZXJib3NlID8/IGZhbHNlKVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGNsaUVycm9yKGBFcnJvcjogRmFpbGVkIHRvIHN0YXJ0IE1DUCBzZXJ2ZXI6ICR7ZXJyb3J9YClcbiAgfVxufVxuXG4vLyBtY3AgcmVtb3ZlIChsaW5lcyA0NTQ14oCTNDYzNSlcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBtY3BSZW1vdmVIYW5kbGVyKFxuICBuYW1lOiBzdHJpbmcsXG4gIG9wdGlvbnM6IHsgc2NvcGU/OiBzdHJpbmcgfSxcbik6IFByb21pc2U8dm9pZD4ge1xuICAvLyBMb29rIHVwIGNvbmZpZyBiZWZvcmUgcmVtb3Zpbmcgc28gd2UgY2FuIGNsZWFuIHVwIHNlY3VyZSBzdG9yYWdlXG4gIGNvbnN0IHNlcnZlckJlZm9yZVJlbW92YWwgPSBnZXRNY3BDb25maWdCeU5hbWUobmFtZSlcblxuICBjb25zdCBjbGVhbnVwU2VjdXJlU3RvcmFnZSA9ICgpID0+IHtcbiAgICBpZiAoXG4gICAgICBzZXJ2ZXJCZWZvcmVSZW1vdmFsICYmXG4gICAgICAoc2VydmVyQmVmb3JlUmVtb3ZhbC50eXBlID09PSAnc3NlJyB8fFxuICAgICAgICBzZXJ2ZXJCZWZvcmVSZW1vdmFsLnR5cGUgPT09ICdodHRwJylcbiAgICApIHtcbiAgICAgIGNsZWFyU2VydmVyVG9rZW5zRnJvbUxvY2FsU3RvcmFnZShuYW1lLCBzZXJ2ZXJCZWZvcmVSZW1vdmFsKVxuICAgICAgY2xlYXJNY3BDbGllbnRDb25maWcobmFtZSwgc2VydmVyQmVmb3JlUmVtb3ZhbClcbiAgICB9XG4gIH1cblxuICB0cnkge1xuICAgIGlmIChvcHRpb25zLnNjb3BlKSB7XG4gICAgICBjb25zdCBzY29wZSA9IGVuc3VyZUNvbmZpZ1Njb3BlKG9wdGlvbnMuc2NvcGUpXG4gICAgICBsb2dFdmVudCgndGVuZ3VfbWNwX2RlbGV0ZScsIHtcbiAgICAgICAgbmFtZTogbmFtZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBzY29wZTpcbiAgICAgICAgICBzY29wZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcblxuICAgICAgYXdhaXQgcmVtb3ZlTWNwQ29uZmlnKG5hbWUsIHNjb3BlKVxuICAgICAgY2xlYW51cFNlY3VyZVN0b3JhZ2UoKVxuICAgICAgcHJvY2Vzcy5zdGRvdXQud3JpdGUoYFJlbW92ZWQgTUNQIHNlcnZlciAke25hbWV9IGZyb20gJHtzY29wZX0gY29uZmlnXFxuYClcbiAgICAgIGNsaU9rKGBGaWxlIG1vZGlmaWVkOiAke2Rlc2NyaWJlTWNwQ29uZmlnRmlsZVBhdGgoc2NvcGUpfWApXG4gICAgfVxuXG4gICAgLy8gSWYgbm8gc2NvcGUgc3BlY2lmaWVkLCBjaGVjayB3aGVyZSB0aGUgc2VydmVyIGV4aXN0c1xuICAgIGNvbnN0IHByb2plY3RDb25maWcgPSBnZXRDdXJyZW50UHJvamVjdENvbmZpZygpXG4gICAgY29uc3QgZ2xvYmFsQ29uZmlnID0gZ2V0R2xvYmFsQ29uZmlnKClcblxuICAgIC8vIENoZWNrIGlmIHNlcnZlciBleGlzdHMgaW4gcHJvamVjdCBzY29wZSAoLm1jcC5qc29uKVxuICAgIGNvbnN0IHsgc2VydmVyczogcHJvamVjdFNlcnZlcnMgfSA9IGdldE1jcENvbmZpZ3NCeVNjb3BlKCdwcm9qZWN0JylcbiAgICBjb25zdCBtY3BKc29uRXhpc3RzID0gISFwcm9qZWN0U2VydmVyc1tuYW1lXVxuXG4gICAgLy8gQ291bnQgaG93IG1hbnkgc2NvcGVzIGNvbnRhaW4gdGhpcyBzZXJ2ZXJcbiAgICBjb25zdCBzY29wZXM6IEFycmF5PEV4Y2x1ZGU8Q29uZmlnU2NvcGUsICdkeW5hbWljJz4+ID0gW11cbiAgICBpZiAocHJvamVjdENvbmZpZy5tY3BTZXJ2ZXJzPy5bbmFtZV0pIHNjb3Blcy5wdXNoKCdsb2NhbCcpXG4gICAgaWYgKG1jcEpzb25FeGlzdHMpIHNjb3Blcy5wdXNoKCdwcm9qZWN0JylcbiAgICBpZiAoZ2xvYmFsQ29uZmlnLm1jcFNlcnZlcnM/LltuYW1lXSkgc2NvcGVzLnB1c2goJ3VzZXInKVxuXG4gICAgaWYgKHNjb3Blcy5sZW5ndGggPT09IDApIHtcbiAgICAgIGNsaUVycm9yKGBObyBNQ1Agc2VydmVyIGZvdW5kIHdpdGggbmFtZTogXCIke25hbWV9XCJgKVxuICAgIH0gZWxzZSBpZiAoc2NvcGVzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgLy8gU2VydmVyIGV4aXN0cyBpbiBvbmx5IG9uZSBzY29wZSwgcmVtb3ZlIGl0XG4gICAgICBjb25zdCBzY29wZSA9IHNjb3Blc1swXSFcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9tY3BfZGVsZXRlJywge1xuICAgICAgICBuYW1lOiBuYW1lIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHNjb3BlOlxuICAgICAgICAgIHNjb3BlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KVxuXG4gICAgICBhd2FpdCByZW1vdmVNY3BDb25maWcobmFtZSwgc2NvcGUpXG4gICAgICBjbGVhbnVwU2VjdXJlU3RvcmFnZSgpXG4gICAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShcbiAgICAgICAgYFJlbW92ZWQgTUNQIHNlcnZlciBcIiR7bmFtZX1cIiBmcm9tICR7c2NvcGV9IGNvbmZpZ1xcbmAsXG4gICAgICApXG4gICAgICBjbGlPayhgRmlsZSBtb2RpZmllZDogJHtkZXNjcmliZU1jcENvbmZpZ0ZpbGVQYXRoKHNjb3BlKX1gKVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBTZXJ2ZXIgZXhpc3RzIGluIG11bHRpcGxlIHNjb3Blc1xuICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYE1DUCBzZXJ2ZXIgXCIke25hbWV9XCIgZXhpc3RzIGluIG11bHRpcGxlIHNjb3BlczpcXG5gKVxuICAgICAgc2NvcGVzLmZvckVhY2goc2NvcGUgPT4ge1xuICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICBgICAtICR7Z2V0U2NvcGVMYWJlbChzY29wZSl9ICgke2Rlc2NyaWJlTWNwQ29uZmlnRmlsZVBhdGgoc2NvcGUpfSlcXG5gLFxuICAgICAgICApXG4gICAgICB9KVxuICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoJ1xcblRvIHJlbW92ZSBmcm9tIGEgc3BlY2lmaWMgc2NvcGUsIHVzZTpcXG4nKVxuICAgICAgc2NvcGVzLmZvckVhY2goc2NvcGUgPT4ge1xuICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShgICBjbGF1ZGUgbWNwIHJlbW92ZSBcIiR7bmFtZX1cIiAtcyAke3Njb3BlfVxcbmApXG4gICAgICB9KVxuICAgICAgY2xpRXJyb3IoKVxuICAgIH1cbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjbGlFcnJvcigoZXJyb3IgYXMgRXJyb3IpLm1lc3NhZ2UpXG4gIH1cbn1cblxuLy8gbWNwIGxpc3QgKGxpbmVzIDQ2NDHigJM0Njg4KVxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG1jcExpc3RIYW5kbGVyKCk6IFByb21pc2U8dm9pZD4ge1xuICBsb2dFdmVudCgndGVuZ3VfbWNwX2xpc3QnLCB7fSlcbiAgY29uc3QgeyBzZXJ2ZXJzOiBjb25maWdzIH0gPSBhd2FpdCBnZXRBbGxNY3BDb25maWdzKClcbiAgaWYgKE9iamVjdC5rZXlzKGNvbmZpZ3MpLmxlbmd0aCA9PT0gMCkge1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db25zb2xlOjogaW50ZW50aW9uYWwgY29uc29sZSBvdXRwdXRcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgICdObyBNQ1Agc2VydmVycyBjb25maWd1cmVkLiBVc2UgYGNsYXVkZSBtY3AgYWRkYCB0byBhZGQgYSBzZXJ2ZXIuJyxcbiAgICApXG4gIH0gZWxzZSB7XG4gICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgIGNvbnNvbGUubG9nKCdDaGVja2luZyBNQ1Agc2VydmVyIGhlYWx0aC4uLlxcbicpXG5cbiAgICAvLyBDaGVjayBzZXJ2ZXJzIGNvbmN1cnJlbnRseVxuICAgIGNvbnN0IGVudHJpZXMgPSBPYmplY3QuZW50cmllcyhjb25maWdzKVxuICAgIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBwTWFwKFxuICAgICAgZW50cmllcyxcbiAgICAgIGFzeW5jIChbbmFtZSwgc2VydmVyXSkgPT4gKHtcbiAgICAgICAgbmFtZSxcbiAgICAgICAgc2VydmVyLFxuICAgICAgICBzdGF0dXM6IGF3YWl0IGNoZWNrTWNwU2VydmVySGVhbHRoKG5hbWUsIHNlcnZlciksXG4gICAgICB9KSxcbiAgICAgIHsgY29uY3VycmVuY3k6IGdldE1jcFNlcnZlckNvbm5lY3Rpb25CYXRjaFNpemUoKSB9LFxuICAgIClcblxuICAgIGZvciAoY29uc3QgeyBuYW1lLCBzZXJ2ZXIsIHN0YXR1cyB9IG9mIHJlc3VsdHMpIHtcbiAgICAgIC8vIEludGVudGlvbmFsbHkgZXhjbHVkaW5nIHNzZS1pZGUgc2VydmVycyBoZXJlIHNpbmNlIHRoZXkncmUgaW50ZXJuYWxcbiAgICAgIGlmIChzZXJ2ZXIudHlwZSA9PT0gJ3NzZScpIHtcbiAgICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgICBjb25zb2xlLmxvZyhgJHtuYW1lfTogJHtzZXJ2ZXIudXJsfSAoU1NFKSAtICR7c3RhdHVzfWApXG4gICAgICB9IGVsc2UgaWYgKHNlcnZlci50eXBlID09PSAnaHR0cCcpIHtcbiAgICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgICBjb25zb2xlLmxvZyhgJHtuYW1lfTogJHtzZXJ2ZXIudXJsfSAoSFRUUCkgLSAke3N0YXR1c31gKVxuICAgICAgfSBlbHNlIGlmIChzZXJ2ZXIudHlwZSA9PT0gJ2NsYXVkZWFpLXByb3h5Jykge1xuICAgICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgICAgIGNvbnNvbGUubG9nKGAke25hbWV9OiAke3NlcnZlci51cmx9IC0gJHtzdGF0dXN9YClcbiAgICAgIH0gZWxzZSBpZiAoIXNlcnZlci50eXBlIHx8IHNlcnZlci50eXBlID09PSAnc3RkaW8nKSB7XG4gICAgICAgIGNvbnN0IGFyZ3MgPSBBcnJheS5pc0FycmF5KHNlcnZlci5hcmdzKSA/IHNlcnZlci5hcmdzIDogW11cbiAgICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgICBjb25zb2xlLmxvZyhgJHtuYW1lfTogJHtzZXJ2ZXIuY29tbWFuZH0gJHthcmdzLmpvaW4oJyAnKX0gLSAke3N0YXR1c31gKVxuICAgICAgfVxuICAgIH1cbiAgfVxuICAvLyBVc2UgZ3JhY2VmdWxTaHV0ZG93biB0byBwcm9wZXJseSBjbGVhbiB1cCBNQ1Agc2VydmVyIGNvbm5lY3Rpb25zXG4gIC8vIChwcm9jZXNzLmV4aXQgYnlwYXNzZXMgY2xlYW51cCBoYW5kbGVycywgbGVhdmluZyBjaGlsZCBwcm9jZXNzZXMgb3JwaGFuZWQpXG4gIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMClcbn1cblxuLy8gbWNwIGdldCAobGluZXMgNDY5NOKAkzQ3ODYpXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWNwR2V0SGFuZGxlcihuYW1lOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgbG9nRXZlbnQoJ3Rlbmd1X21jcF9nZXQnLCB7XG4gICAgbmFtZTogbmFtZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICB9KVxuICBjb25zdCBzZXJ2ZXIgPSBnZXRNY3BDb25maWdCeU5hbWUobmFtZSlcbiAgaWYgKCFzZXJ2ZXIpIHtcbiAgICBjbGlFcnJvcihgTm8gTUNQIHNlcnZlciBmb3VuZCB3aXRoIG5hbWU6ICR7bmFtZX1gKVxuICB9XG5cbiAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICBjb25zb2xlLmxvZyhgJHtuYW1lfTpgKVxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gIGNvbnNvbGUubG9nKGAgIFNjb3BlOiAke2dldFNjb3BlTGFiZWwoc2VydmVyLnNjb3BlKX1gKVxuXG4gIC8vIENoZWNrIHNlcnZlciBoZWFsdGhcbiAgY29uc3Qgc3RhdHVzID0gYXdhaXQgY2hlY2tNY3BTZXJ2ZXJIZWFsdGgobmFtZSwgc2VydmVyKVxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gIGNvbnNvbGUubG9nKGAgIFN0YXR1czogJHtzdGF0dXN9YClcblxuICAvLyBJbnRlbnRpb25hbGx5IGV4Y2x1ZGluZyBzc2UtaWRlIHNlcnZlcnMgaGVyZSBzaW5jZSB0aGV5J3JlIGludGVybmFsXG4gIGlmIChzZXJ2ZXIudHlwZSA9PT0gJ3NzZScpIHtcbiAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgY29uc29sZS5sb2coYCAgVHlwZTogc3NlYClcbiAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgY29uc29sZS5sb2coYCAgVVJMOiAke3NlcnZlci51cmx9YClcbiAgICBpZiAoc2VydmVyLmhlYWRlcnMpIHtcbiAgICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db25zb2xlOjogaW50ZW50aW9uYWwgY29uc29sZSBvdXRwdXRcbiAgICAgIGNvbnNvbGUubG9nKCcgIEhlYWRlcnM6JylcbiAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKHNlcnZlci5oZWFkZXJzKSkge1xuICAgICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgICAgIGNvbnNvbGUubG9nKGAgICAgJHtrZXl9OiAke3ZhbHVlfWApXG4gICAgICB9XG4gICAgfVxuICAgIGlmIChzZXJ2ZXIub2F1dGg/LmNsaWVudElkIHx8IHNlcnZlci5vYXV0aD8uY2FsbGJhY2tQb3J0KSB7XG4gICAgICBjb25zdCBwYXJ0czogc3RyaW5nW10gPSBbXVxuICAgICAgaWYgKHNlcnZlci5vYXV0aC5jbGllbnRJZCkge1xuICAgICAgICBwYXJ0cy5wdXNoKCdjbGllbnRfaWQgY29uZmlndXJlZCcpXG4gICAgICAgIGNvbnN0IGNsaWVudENvbmZpZyA9IGdldE1jcENsaWVudENvbmZpZyhuYW1lLCBzZXJ2ZXIpXG4gICAgICAgIGlmIChjbGllbnRDb25maWc/LmNsaWVudFNlY3JldCkgcGFydHMucHVzaCgnY2xpZW50X3NlY3JldCBjb25maWd1cmVkJylcbiAgICAgIH1cbiAgICAgIGlmIChzZXJ2ZXIub2F1dGguY2FsbGJhY2tQb3J0KVxuICAgICAgICBwYXJ0cy5wdXNoKGBjYWxsYmFja19wb3J0ICR7c2VydmVyLm9hdXRoLmNhbGxiYWNrUG9ydH1gKVxuICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgY29uc29sZS5sb2coYCAgT0F1dGg6ICR7cGFydHMuam9pbignLCAnKX1gKVxuICAgIH1cbiAgfSBlbHNlIGlmIChzZXJ2ZXIudHlwZSA9PT0gJ2h0dHAnKSB7XG4gICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgIGNvbnNvbGUubG9nKGAgIFR5cGU6IGh0dHBgKVxuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db25zb2xlOjogaW50ZW50aW9uYWwgY29uc29sZSBvdXRwdXRcbiAgICBjb25zb2xlLmxvZyhgICBVUkw6ICR7c2VydmVyLnVybH1gKVxuICAgIGlmIChzZXJ2ZXIuaGVhZGVycykge1xuICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgY29uc29sZS5sb2coJyAgSGVhZGVyczonKVxuICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2VydmVyLmhlYWRlcnMpKSB7XG4gICAgICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db25zb2xlOjogaW50ZW50aW9uYWwgY29uc29sZSBvdXRwdXRcbiAgICAgICAgY29uc29sZS5sb2coYCAgICAke2tleX06ICR7dmFsdWV9YClcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKHNlcnZlci5vYXV0aD8uY2xpZW50SWQgfHwgc2VydmVyLm9hdXRoPy5jYWxsYmFja1BvcnQpIHtcbiAgICAgIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdXG4gICAgICBpZiAoc2VydmVyLm9hdXRoLmNsaWVudElkKSB7XG4gICAgICAgIHBhcnRzLnB1c2goJ2NsaWVudF9pZCBjb25maWd1cmVkJylcbiAgICAgICAgY29uc3QgY2xpZW50Q29uZmlnID0gZ2V0TWNwQ2xpZW50Q29uZmlnKG5hbWUsIHNlcnZlcilcbiAgICAgICAgaWYgKGNsaWVudENvbmZpZz8uY2xpZW50U2VjcmV0KSBwYXJ0cy5wdXNoKCdjbGllbnRfc2VjcmV0IGNvbmZpZ3VyZWQnKVxuICAgICAgfVxuICAgICAgaWYgKHNlcnZlci5vYXV0aC5jYWxsYmFja1BvcnQpXG4gICAgICAgIHBhcnRzLnB1c2goYGNhbGxiYWNrX3BvcnQgJHtzZXJ2ZXIub2F1dGguY2FsbGJhY2tQb3J0fWApXG4gICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgICBjb25zb2xlLmxvZyhgICBPQXV0aDogJHtwYXJ0cy5qb2luKCcsICcpfWApXG4gICAgfVxuICB9IGVsc2UgaWYgKHNlcnZlci50eXBlID09PSAnc3RkaW8nKSB7XG4gICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgIGNvbnNvbGUubG9nKGAgIFR5cGU6IHN0ZGlvYClcbiAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgY29uc29sZS5sb2coYCAgQ29tbWFuZDogJHtzZXJ2ZXIuY29tbWFuZH1gKVxuICAgIGNvbnN0IGFyZ3MgPSBBcnJheS5pc0FycmF5KHNlcnZlci5hcmdzKSA/IHNlcnZlci5hcmdzIDogW11cbiAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgY29uc29sZS5sb2coYCAgQXJnczogJHthcmdzLmpvaW4oJyAnKX1gKVxuICAgIGlmIChzZXJ2ZXIuZW52KSB7XG4gICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgICBjb25zb2xlLmxvZygnICBFbnZpcm9ubWVudDonKVxuICAgICAgZm9yIChjb25zdCBba2V5LCB2YWx1ZV0gb2YgT2JqZWN0LmVudHJpZXMoc2VydmVyLmVudikpIHtcbiAgICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgICBjb25zb2xlLmxvZyhgICAgICR7a2V5fT0ke3ZhbHVlfWApXG4gICAgICB9XG4gICAgfVxuICB9XG4gIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db25zb2xlOjogaW50ZW50aW9uYWwgY29uc29sZSBvdXRwdXRcbiAgY29uc29sZS5sb2coXG4gICAgYFxcblRvIHJlbW92ZSB0aGlzIHNlcnZlciwgcnVuOiBjbGF1ZGUgbWNwIHJlbW92ZSBcIiR7bmFtZX1cIiAtcyAke3NlcnZlci5zY29wZX1gLFxuICApXG4gIC8vIFVzZSBncmFjZWZ1bFNodXRkb3duIHRvIHByb3Blcmx5IGNsZWFuIHVwIE1DUCBzZXJ2ZXIgY29ubmVjdGlvbnNcbiAgLy8gKHByb2Nlc3MuZXhpdCBieXBhc3NlcyBjbGVhbnVwIGhhbmRsZXJzLCBsZWF2aW5nIGNoaWxkIHByb2Nlc3NlcyBvcnBoYW5lZClcbiAgYXdhaXQgZ3JhY2VmdWxTaHV0ZG93bigwKVxufVxuXG4vLyBtY3AgYWRkLWpzb24gKGxpbmVzIDQ4MDHigJM0ODcwKVxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIG1jcEFkZEpzb25IYW5kbGVyKFxuICBuYW1lOiBzdHJpbmcsXG4gIGpzb246IHN0cmluZyxcbiAgb3B0aW9uczogeyBzY29wZT86IHN0cmluZzsgY2xpZW50U2VjcmV0PzogdHJ1ZSB9LFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIHRyeSB7XG4gICAgY29uc3Qgc2NvcGUgPSBlbnN1cmVDb25maWdTY29wZShvcHRpb25zLnNjb3BlKVxuICAgIGNvbnN0IHBhcnNlZEpzb24gPSBzYWZlUGFyc2VKU09OKGpzb24pXG5cbiAgICAvLyBSZWFkIHNlY3JldCBiZWZvcmUgd3JpdGluZyBjb25maWcgc28gY2FuY2VsbGF0aW9uIGRvZXNuJ3QgbGVhdmUgcGFydGlhbCBzdGF0ZVxuICAgIGNvbnN0IG5lZWRzU2VjcmV0ID1cbiAgICAgIG9wdGlvbnMuY2xpZW50U2VjcmV0ICYmXG4gICAgICBwYXJzZWRKc29uICYmXG4gICAgICB0eXBlb2YgcGFyc2VkSnNvbiA9PT0gJ29iamVjdCcgJiZcbiAgICAgICd0eXBlJyBpbiBwYXJzZWRKc29uICYmXG4gICAgICAocGFyc2VkSnNvbi50eXBlID09PSAnc3NlJyB8fCBwYXJzZWRKc29uLnR5cGUgPT09ICdodHRwJykgJiZcbiAgICAgICd1cmwnIGluIHBhcnNlZEpzb24gJiZcbiAgICAgIHR5cGVvZiBwYXJzZWRKc29uLnVybCA9PT0gJ3N0cmluZycgJiZcbiAgICAgICdvYXV0aCcgaW4gcGFyc2VkSnNvbiAmJlxuICAgICAgcGFyc2VkSnNvbi5vYXV0aCAmJlxuICAgICAgdHlwZW9mIHBhcnNlZEpzb24ub2F1dGggPT09ICdvYmplY3QnICYmXG4gICAgICAnY2xpZW50SWQnIGluIHBhcnNlZEpzb24ub2F1dGhcbiAgICBjb25zdCBjbGllbnRTZWNyZXQgPSBuZWVkc1NlY3JldCA/IGF3YWl0IHJlYWRDbGllbnRTZWNyZXQoKSA6IHVuZGVmaW5lZFxuXG4gICAgYXdhaXQgYWRkTWNwQ29uZmlnKG5hbWUsIHBhcnNlZEpzb24sIHNjb3BlKVxuXG4gICAgY29uc3QgdHJhbnNwb3J0VHlwZSA9XG4gICAgICBwYXJzZWRKc29uICYmIHR5cGVvZiBwYXJzZWRKc29uID09PSAnb2JqZWN0JyAmJiAndHlwZScgaW4gcGFyc2VkSnNvblxuICAgICAgICA/IFN0cmluZyhwYXJzZWRKc29uLnR5cGUgfHwgJ3N0ZGlvJylcbiAgICAgICAgOiAnc3RkaW8nXG5cbiAgICBpZiAoXG4gICAgICBjbGllbnRTZWNyZXQgJiZcbiAgICAgIHBhcnNlZEpzb24gJiZcbiAgICAgIHR5cGVvZiBwYXJzZWRKc29uID09PSAnb2JqZWN0JyAmJlxuICAgICAgJ3R5cGUnIGluIHBhcnNlZEpzb24gJiZcbiAgICAgIChwYXJzZWRKc29uLnR5cGUgPT09ICdzc2UnIHx8IHBhcnNlZEpzb24udHlwZSA9PT0gJ2h0dHAnKSAmJlxuICAgICAgJ3VybCcgaW4gcGFyc2VkSnNvbiAmJlxuICAgICAgdHlwZW9mIHBhcnNlZEpzb24udXJsID09PSAnc3RyaW5nJ1xuICAgICkge1xuICAgICAgc2F2ZU1jcENsaWVudFNlY3JldChcbiAgICAgICAgbmFtZSxcbiAgICAgICAgeyB0eXBlOiBwYXJzZWRKc29uLnR5cGUsIHVybDogcGFyc2VkSnNvbi51cmwgfSxcbiAgICAgICAgY2xpZW50U2VjcmV0LFxuICAgICAgKVxuICAgIH1cblxuICAgIGxvZ0V2ZW50KCd0ZW5ndV9tY3BfYWRkJywge1xuICAgICAgc2NvcGU6XG4gICAgICAgIHNjb3BlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICBzb3VyY2U6XG4gICAgICAgICdqc29uJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgdHlwZTogdHJhbnNwb3J0VHlwZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgIH0pXG5cbiAgICBjbGlPayhgQWRkZWQgJHt0cmFuc3BvcnRUeXBlfSBNQ1Agc2VydmVyICR7bmFtZX0gdG8gJHtzY29wZX0gY29uZmlnYClcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjbGlFcnJvcigoZXJyb3IgYXMgRXJyb3IpLm1lc3NhZ2UpXG4gIH1cbn1cblxuLy8gbWNwIGFkZC1mcm9tLWNsYXVkZS1kZXNrdG9wIChsaW5lcyA0ODgx4oCTNDkyNylcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBtY3BBZGRGcm9tRGVza3RvcEhhbmRsZXIob3B0aW9uczoge1xuICBzY29wZT86IHN0cmluZ1xufSk6IFByb21pc2U8dm9pZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IHNjb3BlID0gZW5zdXJlQ29uZmlnU2NvcGUob3B0aW9ucy5zY29wZSlcbiAgICBjb25zdCBwbGF0Zm9ybSA9IGdldFBsYXRmb3JtKClcblxuICAgIGxvZ0V2ZW50KCd0ZW5ndV9tY3BfYWRkJywge1xuICAgICAgc2NvcGU6XG4gICAgICAgIHNjb3BlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICBwbGF0Zm9ybTpcbiAgICAgICAgcGxhdGZvcm0gYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgIHNvdXJjZTpcbiAgICAgICAgJ2Rlc2t0b3AnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgfSlcblxuICAgIGNvbnN0IHsgcmVhZENsYXVkZURlc2t0b3BNY3BTZXJ2ZXJzIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAnLi4vLi4vdXRpbHMvY2xhdWRlRGVza3RvcC5qcydcbiAgICApXG4gICAgY29uc3Qgc2VydmVycyA9IGF3YWl0IHJlYWRDbGF1ZGVEZXNrdG9wTWNwU2VydmVycygpXG5cbiAgICBpZiAoT2JqZWN0LmtleXMoc2VydmVycykubGVuZ3RoID09PSAwKSB7XG4gICAgICBjbGlPayhcbiAgICAgICAgJ05vIE1DUCBzZXJ2ZXJzIGZvdW5kIGluIENsYXVkZSBEZXNrdG9wIGNvbmZpZ3VyYXRpb24gb3IgY29uZmlndXJhdGlvbiBmaWxlIGRvZXMgbm90IGV4aXN0LicsXG4gICAgICApXG4gICAgfVxuXG4gICAgY29uc3QgeyB1bm1vdW50IH0gPSBhd2FpdCByZW5kZXIoXG4gICAgICA8QXBwU3RhdGVQcm92aWRlcj5cbiAgICAgICAgPEtleWJpbmRpbmdTZXR1cD5cbiAgICAgICAgICA8TUNQU2VydmVyRGVza3RvcEltcG9ydERpYWxvZ1xuICAgICAgICAgICAgc2VydmVycz17c2VydmVyc31cbiAgICAgICAgICAgIHNjb3BlPXtzY29wZX1cbiAgICAgICAgICAgIG9uRG9uZT17KCkgPT4ge1xuICAgICAgICAgICAgICB1bm1vdW50KClcbiAgICAgICAgICAgIH19XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9LZXliaW5kaW5nU2V0dXA+XG4gICAgICA8L0FwcFN0YXRlUHJvdmlkZXI+LFxuICAgICAgeyBleGl0T25DdHJsQzogdHJ1ZSB9LFxuICAgIClcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBjbGlFcnJvcigoZXJyb3IgYXMgRXJyb3IpLm1lc3NhZ2UpXG4gIH1cbn1cblxuLy8gbWNwIHJlc2V0LXByb2plY3QtY2hvaWNlcyAobGluZXMgNDkzNeKAkzQ5NTIpXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWNwUmVzZXRDaG9pY2VzSGFuZGxlcigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgbG9nRXZlbnQoJ3Rlbmd1X21jcF9yZXNldF9tY3Bqc29uX2Nob2ljZXMnLCB7fSlcbiAgc2F2ZUN1cnJlbnRQcm9qZWN0Q29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAuLi5jdXJyZW50LFxuICAgIGVuYWJsZWRNY3Bqc29uU2VydmVyczogW10sXG4gICAgZGlzYWJsZWRNY3Bqc29uU2VydmVyczogW10sXG4gICAgZW5hYmxlQWxsUHJvamVjdE1jcFNlcnZlcnM6IGZhbHNlLFxuICB9KSlcbiAgY2xpT2soXG4gICAgJ0FsbCBwcm9qZWN0LXNjb3BlZCAoLm1jcC5qc29uKSBzZXJ2ZXIgYXBwcm92YWxzIGFuZCByZWplY3Rpb25zIGhhdmUgYmVlbiByZXNldC5cXG4nICtcbiAgICAgICdZb3Ugd2lsbCBiZSBwcm9tcHRlZCBmb3IgYXBwcm92YWwgbmV4dCB0aW1lIHlvdSBzdGFydCBDbGF1ZGUgQ29kZS4nLFxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLFNBQVNBLElBQUksUUFBUSxhQUFhO0FBQ2xDLE9BQU9DLElBQUksTUFBTSxPQUFPO0FBQ3hCLFNBQVNDLEdBQUcsUUFBUSxTQUFTO0FBQzdCLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLDRCQUE0QixRQUFRLGtEQUFrRDtBQUMvRixTQUFTQyxNQUFNLFFBQVEsY0FBYztBQUNyQyxTQUFTQyxlQUFlLFFBQVEsOENBQThDO0FBQzlFLFNBQ0UsS0FBS0MsMERBQTBELEVBQy9EQyxRQUFRLFFBQ0gsbUNBQW1DO0FBQzFDLFNBQ0VDLG9CQUFvQixFQUNwQkMsaUNBQWlDLEVBQ2pDQyxrQkFBa0IsRUFDbEJDLGdCQUFnQixFQUNoQkMsbUJBQW1CLFFBQ2QsNEJBQTRCO0FBQ25DLFNBQ0VDLGVBQWUsRUFDZkMsK0JBQStCLFFBQzFCLDhCQUE4QjtBQUNyQyxTQUNFQyxZQUFZLEVBQ1pDLGdCQUFnQixFQUNoQkMsa0JBQWtCLEVBQ2xCQyxvQkFBb0IsRUFDcEJDLGVBQWUsUUFDViw4QkFBOEI7QUFDckMsY0FDRUMsV0FBVyxFQUNYQyxxQkFBcUIsUUFDaEIsNkJBQTZCO0FBQ3BDLFNBQ0VDLHlCQUF5QixFQUN6QkMsaUJBQWlCLEVBQ2pCQyxhQUFhLFFBQ1IsNkJBQTZCO0FBQ3BDLFNBQVNDLGdCQUFnQixRQUFRLHlCQUF5QjtBQUMxRCxTQUNFQyx1QkFBdUIsRUFDdkJDLGVBQWUsRUFDZkMsd0JBQXdCLFFBQ25CLHVCQUF1QjtBQUM5QixTQUFTQyxnQkFBZ0IsUUFBUSx1QkFBdUI7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 \ No newline at end of file diff --git a/cli/handlers/plugins.ts b/cli/handlers/plugins.ts new file mode 100644 index 0000000..9236abe --- /dev/null +++ b/cli/handlers/plugins.ts @@ -0,0 +1,878 @@ +/** + * Plugin and marketplace subcommand handlers — extracted from main.tsx for lazy loading. + * These are dynamically imported only when `claude plugin *` or `claude plugin marketplace *` runs. + */ +/* eslint-disable custom-rules/no-process-exit -- CLI subcommand handlers intentionally exit */ +import figures from 'figures' +import { basename, dirname } from 'path' +import { setUseCoworkPlugins } from '../../bootstrap/state.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + logEvent, +} from '../../services/analytics/index.js' +import { + disableAllPlugins, + disablePlugin, + enablePlugin, + installPlugin, + uninstallPlugin, + updatePluginCli, + VALID_INSTALLABLE_SCOPES, + VALID_UPDATE_SCOPES, +} from '../../services/plugins/pluginCliCommands.js' +import { getPluginErrorMessage } from '../../types/plugin.js' +import { errorMessage } from '../../utils/errors.js' +import { logError } from '../../utils/log.js' +import { clearAllCaches } from '../../utils/plugins/cacheUtils.js' +import { getInstallCounts } from '../../utils/plugins/installCounts.js' +import { + isPluginInstalled, + loadInstalledPluginsV2, +} from '../../utils/plugins/installedPluginsManager.js' +import { + createPluginId, + loadMarketplacesWithGracefulDegradation, +} from '../../utils/plugins/marketplaceHelpers.js' +import { + addMarketplaceSource, + loadKnownMarketplacesConfig, + refreshAllMarketplaces, + refreshMarketplace, + removeMarketplaceSource, + saveMarketplaceToSettings, +} from '../../utils/plugins/marketplaceManager.js' +import { loadPluginMcpServers } from '../../utils/plugins/mcpPluginIntegration.js' +import { parseMarketplaceInput } from '../../utils/plugins/parseMarketplaceInput.js' +import { + parsePluginIdentifier, + scopeToSettingSource, +} from '../../utils/plugins/pluginIdentifier.js' +import { loadAllPlugins } from '../../utils/plugins/pluginLoader.js' +import type { PluginSource } from '../../utils/plugins/schemas.js' +import { + type ValidationResult, + validateManifest, + validatePluginContents, +} from '../../utils/plugins/validatePlugin.js' +import { jsonStringify } from '../../utils/slowOperations.js' +import { plural } from '../../utils/stringUtils.js' +import { cliError, cliOk } from '../exit.js' + +// Re-export for main.tsx to reference in option definitions +export { VALID_INSTALLABLE_SCOPES, VALID_UPDATE_SCOPES } + +/** + * Helper function to handle marketplace command errors consistently. + */ +export function handleMarketplaceError(error: unknown, action: string): never { + logError(error) + cliError(`${figures.cross} Failed to ${action}: ${errorMessage(error)}`) +} + +function printValidationResult(result: ValidationResult): void { + if (result.errors.length > 0) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log( + `${figures.cross} Found ${result.errors.length} ${plural(result.errors.length, 'error')}:\n`, + ) + result.errors.forEach(error => { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` ${figures.pointer} ${error.path}: ${error.message}`) + }) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('') + } + if (result.warnings.length > 0) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log( + `${figures.warning} Found ${result.warnings.length} ${plural(result.warnings.length, 'warning')}:\n`, + ) + result.warnings.forEach(warning => { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` ${figures.pointer} ${warning.path}: ${warning.message}`) + }) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('') + } +} + +// plugin validate +export async function pluginValidateHandler( + manifestPath: string, + options: { cowork?: boolean }, +): Promise { + if (options.cowork) setUseCoworkPlugins(true) + try { + const result = await validateManifest(manifestPath) + + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`Validating ${result.fileType} manifest: ${result.filePath}\n`) + printValidationResult(result) + + // If this is a plugin manifest located inside a .claude-plugin directory, + // also validate the plugin's content files (skills, agents, commands, + // hooks). Works whether the user passed a directory or the plugin.json + // path directly. + let contentResults: ValidationResult[] = [] + if (result.fileType === 'plugin') { + const manifestDir = dirname(result.filePath) + if (basename(manifestDir) === '.claude-plugin') { + contentResults = await validatePluginContents(dirname(manifestDir)) + for (const r of contentResults) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`Validating ${r.fileType}: ${r.filePath}\n`) + printValidationResult(r) + } + } + } + + const allSuccess = result.success && contentResults.every(r => r.success) + const hasWarnings = + result.warnings.length > 0 || + contentResults.some(r => r.warnings.length > 0) + + if (allSuccess) { + cliOk( + hasWarnings + ? `${figures.tick} Validation passed with warnings` + : `${figures.tick} Validation passed`, + ) + } else { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`${figures.cross} Validation failed`) + process.exit(1) + } + } catch (error) { + logError(error) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error( + `${figures.cross} Unexpected error during validation: ${errorMessage(error)}`, + ) + process.exit(2) + } +} + +// plugin list (lines 5217–5416) +export async function pluginListHandler(options: { + json?: boolean + available?: boolean + cowork?: boolean +}): Promise { + if (options.cowork) setUseCoworkPlugins(true) + logEvent('tengu_plugin_list_command', {}) + + const installedData = loadInstalledPluginsV2() + const { getPluginEditableScopes } = await import( + '../../utils/plugins/pluginStartupCheck.js' + ) + const enabledPlugins = getPluginEditableScopes() + + const pluginIds = Object.keys(installedData.plugins) + + // Load all plugins once. The JSON and human paths both need: + // - loadErrors (to show load failures per plugin) + // - inline plugins (session-only via --plugin-dir, source='name@inline') + // which are NOT in installedData.plugins (V2 bookkeeping) — they must + // be surfaced separately or `plugin list` silently ignores --plugin-dir. + const { + enabled: loadedEnabled, + disabled: loadedDisabled, + errors: loadErrors, + } = await loadAllPlugins() + const allLoadedPlugins = [...loadedEnabled, ...loadedDisabled] + const inlinePlugins = allLoadedPlugins.filter(p => + p.source.endsWith('@inline'), + ) + // Path-level inline failures (dir doesn't exist, parse error before + // manifest is read) use source='inline[N]'. Plugin-level errors after + // manifest read use source='name@inline'. Collect both for the session + // section — these are otherwise invisible since they have no pluginId. + const inlineLoadErrors = loadErrors.filter( + e => e.source.endsWith('@inline') || e.source.startsWith('inline['), + ) + + if (options.json) { + // Create a map of plugin source to loaded plugin for quick lookup + const loadedPluginMap = new Map(allLoadedPlugins.map(p => [p.source, p])) + + const plugins: Array<{ + id: string + version: string + scope: string + enabled: boolean + installPath: string + installedAt?: string + lastUpdated?: string + projectPath?: string + mcpServers?: Record + errors?: string[] + }> = [] + + for (const pluginId of pluginIds.sort()) { + const installations = installedData.plugins[pluginId] + if (!installations || installations.length === 0) continue + + // Find loading errors for this plugin + const pluginName = parsePluginIdentifier(pluginId).name + const pluginErrors = loadErrors + .filter( + e => + e.source === pluginId || ('plugin' in e && e.plugin === pluginName), + ) + .map(getPluginErrorMessage) + + for (const installation of installations) { + // Try to find the loaded plugin to get MCP servers + const loadedPlugin = loadedPluginMap.get(pluginId) + let mcpServers: Record | undefined + + if (loadedPlugin) { + // Load MCP servers if not already cached + const servers = + loadedPlugin.mcpServers || + (await loadPluginMcpServers(loadedPlugin)) + if (servers && Object.keys(servers).length > 0) { + mcpServers = servers + } + } + + plugins.push({ + id: pluginId, + version: installation.version || 'unknown', + scope: installation.scope, + enabled: enabledPlugins.has(pluginId), + installPath: installation.installPath, + installedAt: installation.installedAt, + lastUpdated: installation.lastUpdated, + projectPath: installation.projectPath, + mcpServers, + errors: pluginErrors.length > 0 ? pluginErrors : undefined, + }) + } + } + + // Session-only plugins: scope='session', no install metadata. + // Filter from inlineLoadErrors (not loadErrors) so an installed plugin + // with the same manifest name doesn't cross-contaminate via e.plugin. + // The e.plugin fallback catches the dirName≠manifestName case: + // createPluginFromPath tags errors with `${dirName}@inline` but + // plugin.source is reassigned to `${manifest.name}@inline` afterward + // (pluginLoader.ts loadInlinePlugins), so e.source !== p.source when + // a dev checkout dir like ~/code/my-fork/ has manifest name 'cool-plugin'. + for (const p of inlinePlugins) { + const servers = p.mcpServers || (await loadPluginMcpServers(p)) + const pErrors = inlineLoadErrors + .filter( + e => e.source === p.source || ('plugin' in e && e.plugin === p.name), + ) + .map(getPluginErrorMessage) + plugins.push({ + id: p.source, + version: p.manifest.version ?? 'unknown', + scope: 'session', + enabled: p.enabled !== false, + installPath: p.path, + mcpServers: + servers && Object.keys(servers).length > 0 ? servers : undefined, + errors: pErrors.length > 0 ? pErrors : undefined, + }) + } + // Path-level inline failures (--plugin-dir /nonexistent): no LoadedPlugin + // exists so the loop above can't surface them. Mirror the human-path + // handling so JSON consumers see the failure instead of silent omission. + for (const e of inlineLoadErrors.filter(e => + e.source.startsWith('inline['), + )) { + plugins.push({ + id: e.source, + version: 'unknown', + scope: 'session', + enabled: false, + installPath: 'path' in e ? e.path : '', + errors: [getPluginErrorMessage(e)], + }) + } + + // If --available is set, also load available plugins from marketplaces + if (options.available) { + const available: Array<{ + pluginId: string + name: string + description?: string + marketplaceName: string + version?: string + source: PluginSource + installCount?: number + }> = [] + + try { + const [config, installCounts] = await Promise.all([ + loadKnownMarketplacesConfig(), + getInstallCounts(), + ]) + const { marketplaces } = + await loadMarketplacesWithGracefulDegradation(config) + + for (const { + name: marketplaceName, + data: marketplace, + } of marketplaces) { + if (marketplace) { + for (const entry of marketplace.plugins) { + const pluginId = createPluginId(entry.name, marketplaceName) + // Only include plugins that are not already installed + if (!isPluginInstalled(pluginId)) { + available.push({ + pluginId, + name: entry.name, + description: entry.description, + marketplaceName, + version: entry.version, + source: entry.source, + installCount: installCounts?.get(pluginId), + }) + } + } + } + } + } catch { + // Silently ignore marketplace loading errors + } + + cliOk(jsonStringify({ installed: plugins, available }, null, 2)) + } else { + cliOk(jsonStringify(plugins, null, 2)) + } + } + + if (pluginIds.length === 0 && inlinePlugins.length === 0) { + // inlineLoadErrors can exist with zero inline plugins (e.g. --plugin-dir + // points at a nonexistent path). Don't early-exit over them — fall + // through to the session section so the failure is visible. + if (inlineLoadErrors.length === 0) { + cliOk( + 'No plugins installed. Use `claude plugin install` to install a plugin.', + ) + } + } + + if (pluginIds.length > 0) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('Installed plugins:\n') + } + + for (const pluginId of pluginIds.sort()) { + const installations = installedData.plugins[pluginId] + if (!installations || installations.length === 0) continue + + // Find loading errors for this plugin + const pluginName = parsePluginIdentifier(pluginId).name + const pluginErrors = loadErrors.filter( + e => e.source === pluginId || ('plugin' in e && e.plugin === pluginName), + ) + + for (const installation of installations) { + const isEnabled = enabledPlugins.has(pluginId) + const status = + pluginErrors.length > 0 + ? `${figures.cross} failed to load` + : isEnabled + ? `${figures.tick} enabled` + : `${figures.cross} disabled` + const version = installation.version || 'unknown' + const scope = installation.scope + + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` ${figures.pointer} ${pluginId}`) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Version: ${version}`) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Scope: ${scope}`) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Status: ${status}`) + for (const error of pluginErrors) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Error: ${getPluginErrorMessage(error)}`) + } + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('') + } + } + + if (inlinePlugins.length > 0 || inlineLoadErrors.length > 0) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('Session-only plugins (--plugin-dir):\n') + for (const p of inlinePlugins) { + // Same dirName≠manifestName fallback as the JSON path above — error + // sources use the dir basename but p.source uses the manifest name. + const pErrors = inlineLoadErrors.filter( + e => e.source === p.source || ('plugin' in e && e.plugin === p.name), + ) + const status = + pErrors.length > 0 + ? `${figures.cross} loaded with errors` + : `${figures.tick} loaded` + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` ${figures.pointer} ${p.source}`) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Version: ${p.manifest.version ?? 'unknown'}`) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Path: ${p.path}`) + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Status: ${status}`) + for (const e of pErrors) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Error: ${getPluginErrorMessage(e)}`) + } + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('') + } + // Path-level failures: no LoadedPlugin object exists. Show them so + // `--plugin-dir /typo` doesn't just silently produce nothing. + for (const e of inlineLoadErrors.filter(e => + e.source.startsWith('inline['), + )) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log( + ` ${figures.pointer} ${e.source}: ${figures.cross} ${getPluginErrorMessage(e)}\n`, + ) + } + } + + cliOk() +} + +// marketplace add (lines 5433–5487) +export async function marketplaceAddHandler( + source: string, + options: { cowork?: boolean; sparse?: string[]; scope?: string }, +): Promise { + if (options.cowork) setUseCoworkPlugins(true) + try { + const parsed = await parseMarketplaceInput(source) + + if (!parsed) { + cliError( + `${figures.cross} Invalid marketplace source format. Try: owner/repo, https://..., or ./path`, + ) + } + + if ('error' in parsed) { + cliError(`${figures.cross} ${parsed.error}`) + } + + // Validate scope + const scope = options.scope ?? 'user' + if (scope !== 'user' && scope !== 'project' && scope !== 'local') { + cliError( + `${figures.cross} Invalid scope '${scope}'. Use: user, project, or local`, + ) + } + const settingSource = scopeToSettingSource(scope) + + let marketplaceSource = parsed + + if (options.sparse && options.sparse.length > 0) { + if ( + marketplaceSource.source === 'github' || + marketplaceSource.source === 'git' + ) { + marketplaceSource = { + ...marketplaceSource, + sparsePaths: options.sparse, + } + } else { + cliError( + `${figures.cross} --sparse is only supported for github and git marketplace sources (got: ${marketplaceSource.source})`, + ) + } + } + + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('Adding marketplace...') + + const { name, alreadyMaterialized, resolvedSource } = + await addMarketplaceSource(marketplaceSource, message => { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(message) + }) + + // Write intent to settings at the requested scope + saveMarketplaceToSettings(name, { source: resolvedSource }, settingSource) + + clearAllCaches() + + let sourceType = marketplaceSource.source + if (marketplaceSource.source === 'github') { + sourceType = + marketplaceSource.repo as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + } + logEvent('tengu_marketplace_added', { + source_type: + sourceType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + cliOk( + alreadyMaterialized + ? `${figures.tick} Marketplace '${name}' already on disk — declared in ${scope} settings` + : `${figures.tick} Successfully added marketplace: ${name} (declared in ${scope} settings)`, + ) + } catch (error) { + handleMarketplaceError(error, 'add marketplace') + } +} + +// marketplace list (lines 5497–5565) +export async function marketplaceListHandler(options: { + json?: boolean + cowork?: boolean +}): Promise { + if (options.cowork) setUseCoworkPlugins(true) + try { + const config = await loadKnownMarketplacesConfig() + const names = Object.keys(config) + + if (options.json) { + const marketplaces = names.sort().map(name => { + const marketplace = config[name] + const source = marketplace?.source + return { + name, + source: source?.source, + ...(source?.source === 'github' && { repo: source.repo }), + ...(source?.source === 'git' && { url: source.url }), + ...(source?.source === 'url' && { url: source.url }), + ...(source?.source === 'directory' && { path: source.path }), + ...(source?.source === 'file' && { path: source.path }), + installLocation: marketplace?.installLocation, + } + }) + cliOk(jsonStringify(marketplaces, null, 2)) + } + + if (names.length === 0) { + cliOk('No marketplaces configured') + } + + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('Configured marketplaces:\n') + names.forEach(name => { + const marketplace = config[name] + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` ${figures.pointer} ${name}`) + + if (marketplace?.source) { + const src = marketplace.source + if (src.source === 'github') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Source: GitHub (${src.repo})`) + } else if (src.source === 'git') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Source: Git (${src.url})`) + } else if (src.source === 'url') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Source: URL (${src.url})`) + } else if (src.source === 'directory') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Source: Directory (${src.path})`) + } else if (src.source === 'file') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(` Source: File (${src.path})`) + } + } + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log('') + }) + + cliOk() + } catch (error) { + handleMarketplaceError(error, 'list marketplaces') + } +} + +// marketplace remove (lines 5576–5598) +export async function marketplaceRemoveHandler( + name: string, + options: { cowork?: boolean }, +): Promise { + if (options.cowork) setUseCoworkPlugins(true) + try { + await removeMarketplaceSource(name) + clearAllCaches() + + logEvent('tengu_marketplace_removed', { + marketplace_name: + name as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + cliOk(`${figures.tick} Successfully removed marketplace: ${name}`) + } catch (error) { + handleMarketplaceError(error, 'remove marketplace') + } +} + +// marketplace update (lines 5609–5672) +export async function marketplaceUpdateHandler( + name: string | undefined, + options: { cowork?: boolean }, +): Promise { + if (options.cowork) setUseCoworkPlugins(true) + try { + if (name) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`Updating marketplace: ${name}...`) + + await refreshMarketplace(name, message => { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(message) + }) + + clearAllCaches() + + logEvent('tengu_marketplace_updated', { + marketplace_name: + name as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + cliOk(`${figures.tick} Successfully updated marketplace: ${name}`) + } else { + const config = await loadKnownMarketplacesConfig() + const marketplaceNames = Object.keys(config) + + if (marketplaceNames.length === 0) { + cliOk('No marketplaces configured') + } + + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.log(`Updating ${marketplaceNames.length} marketplace(s)...`) + + await refreshAllMarketplaces() + clearAllCaches() + + logEvent('tengu_marketplace_updated_all', { + count: + marketplaceNames.length as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + cliOk( + `${figures.tick} Successfully updated ${marketplaceNames.length} marketplace(s)`, + ) + } + } catch (error) { + handleMarketplaceError(error, 'update marketplace(s)') + } +} + +// plugin install (lines 5690–5721) +export async function pluginInstallHandler( + plugin: string, + options: { scope?: string; cowork?: boolean }, +): Promise { + if (options.cowork) setUseCoworkPlugins(true) + const scope = options.scope || 'user' + if (options.cowork && scope !== 'user') { + cliError('--cowork can only be used with user scope') + } + if ( + !VALID_INSTALLABLE_SCOPES.includes( + scope as (typeof VALID_INSTALLABLE_SCOPES)[number], + ) + ) { + cliError( + `Invalid scope: ${scope}. Must be one of: ${VALID_INSTALLABLE_SCOPES.join(', ')}.`, + ) + } + // _PROTO_* routes to PII-tagged plugin_name/marketplace_name BQ columns. + // Unredacted plugin arg was previously logged to general-access + // additional_metadata for all users — dropped in favor of the privileged + // column route. marketplace may be undefined (fires before resolution). + const { name, marketplace } = parsePluginIdentifier(plugin) + logEvent('tengu_plugin_install_command', { + _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + ...(marketplace && { + _PROTO_marketplace_name: + marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + }), + scope: scope as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + await installPlugin(plugin, scope as 'user' | 'project' | 'local') +} + +// plugin uninstall (lines 5738–5769) +export async function pluginUninstallHandler( + plugin: string, + options: { scope?: string; cowork?: boolean; keepData?: boolean }, +): Promise { + if (options.cowork) setUseCoworkPlugins(true) + const scope = options.scope || 'user' + if (options.cowork && scope !== 'user') { + cliError('--cowork can only be used with user scope') + } + if ( + !VALID_INSTALLABLE_SCOPES.includes( + scope as (typeof VALID_INSTALLABLE_SCOPES)[number], + ) + ) { + cliError( + `Invalid scope: ${scope}. Must be one of: ${VALID_INSTALLABLE_SCOPES.join(', ')}.`, + ) + } + const { name, marketplace } = parsePluginIdentifier(plugin) + logEvent('tengu_plugin_uninstall_command', { + _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + ...(marketplace && { + _PROTO_marketplace_name: + marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + }), + scope: scope as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + await uninstallPlugin( + plugin, + scope as 'user' | 'project' | 'local', + options.keepData, + ) +} + +// plugin enable (lines 5783–5818) +export async function pluginEnableHandler( + plugin: string, + options: { scope?: string; cowork?: boolean }, +): Promise { + if (options.cowork) setUseCoworkPlugins(true) + let scope: (typeof VALID_INSTALLABLE_SCOPES)[number] | undefined + if (options.scope) { + if ( + !VALID_INSTALLABLE_SCOPES.includes( + options.scope as (typeof VALID_INSTALLABLE_SCOPES)[number], + ) + ) { + cliError( + `Invalid scope "${options.scope}". Valid scopes: ${VALID_INSTALLABLE_SCOPES.join(', ')}`, + ) + } + scope = options.scope as (typeof VALID_INSTALLABLE_SCOPES)[number] + } + if (options.cowork && scope !== undefined && scope !== 'user') { + cliError('--cowork can only be used with user scope') + } + + // --cowork always operates at user scope + if (options.cowork && scope === undefined) { + scope = 'user' + } + + const { name, marketplace } = parsePluginIdentifier(plugin) + logEvent('tengu_plugin_enable_command', { + _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + ...(marketplace && { + _PROTO_marketplace_name: + marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + }), + scope: (scope ?? + 'auto') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + await enablePlugin(plugin, scope) +} + +// plugin disable (lines 5833–5902) +export async function pluginDisableHandler( + plugin: string | undefined, + options: { scope?: string; cowork?: boolean; all?: boolean }, +): Promise { + if (options.all && plugin) { + cliError('Cannot use --all with a specific plugin') + } + + if (!options.all && !plugin) { + cliError('Please specify a plugin name or use --all to disable all plugins') + } + + if (options.cowork) setUseCoworkPlugins(true) + + if (options.all) { + if (options.scope) { + cliError('Cannot use --scope with --all') + } + + // No _PROTO_plugin_name here — --all disables all plugins. + // Distinguishable from the specific-plugin branch by plugin_name IS NULL. + logEvent('tengu_plugin_disable_command', {}) + + await disableAllPlugins() + return + } + + let scope: (typeof VALID_INSTALLABLE_SCOPES)[number] | undefined + if (options.scope) { + if ( + !VALID_INSTALLABLE_SCOPES.includes( + options.scope as (typeof VALID_INSTALLABLE_SCOPES)[number], + ) + ) { + cliError( + `Invalid scope "${options.scope}". Valid scopes: ${VALID_INSTALLABLE_SCOPES.join(', ')}`, + ) + } + scope = options.scope as (typeof VALID_INSTALLABLE_SCOPES)[number] + } + if (options.cowork && scope !== undefined && scope !== 'user') { + cliError('--cowork can only be used with user scope') + } + + // --cowork always operates at user scope + if (options.cowork && scope === undefined) { + scope = 'user' + } + + const { name, marketplace } = parsePluginIdentifier(plugin!) + logEvent('tengu_plugin_disable_command', { + _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + ...(marketplace && { + _PROTO_marketplace_name: + marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + }), + scope: (scope ?? + 'auto') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + await disablePlugin(plugin!, scope) +} + +// plugin update (lines 5918–5948) +export async function pluginUpdateHandler( + plugin: string, + options: { scope?: string; cowork?: boolean }, +): Promise { + if (options.cowork) setUseCoworkPlugins(true) + const { name, marketplace } = parsePluginIdentifier(plugin) + logEvent('tengu_plugin_update_command', { + _PROTO_plugin_name: name as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + ...(marketplace && { + _PROTO_marketplace_name: + marketplace as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + }), + }) + + let scope: (typeof VALID_UPDATE_SCOPES)[number] = 'user' + if (options.scope) { + if ( + !VALID_UPDATE_SCOPES.includes( + options.scope as (typeof VALID_UPDATE_SCOPES)[number], + ) + ) { + cliError( + `Invalid scope "${options.scope}". Valid scopes: ${VALID_UPDATE_SCOPES.join(', ')}`, + ) + } + scope = options.scope as (typeof VALID_UPDATE_SCOPES)[number] + } + if (options.cowork && scope !== 'user') { + cliError('--cowork can only be used with user scope') + } + + await updatePluginCli(plugin, scope) +} diff --git a/cli/handlers/util.tsx b/cli/handlers/util.tsx new file mode 100644 index 0000000..03ff3cd --- /dev/null +++ b/cli/handlers/util.tsx @@ -0,0 +1,110 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * Miscellaneous subcommand handlers — extracted from main.tsx for lazy loading. + * setup-token, doctor, install + */ +/* eslint-disable custom-rules/no-process-exit -- CLI subcommand handlers intentionally exit */ + +import { cwd } from 'process'; +import React from 'react'; +import { WelcomeV2 } from '../../components/LogoV2/WelcomeV2.js'; +import { useManagePlugins } from '../../hooks/useManagePlugins.js'; +import type { Root } from '../../ink.js'; +import { Box, Text } from '../../ink.js'; +import { KeybindingSetup } from '../../keybindings/KeybindingProviderSetup.js'; +import { logEvent } from '../../services/analytics/index.js'; +import { MCPConnectionManager } from '../../services/mcp/MCPConnectionManager.js'; +import { AppStateProvider } from '../../state/AppState.js'; +import { onChangeAppState } from '../../state/onChangeAppState.js'; +import { isAnthropicAuthEnabled } from '../../utils/auth.js'; +export async function setupTokenHandler(root: Root): Promise { + logEvent('tengu_setup_token_command', {}); + const showAuthWarning = !isAnthropicAuthEnabled(); + const { + ConsoleOAuthFlow + } = await import('../../components/ConsoleOAuthFlow.js'); + await new Promise(resolve => { + root.render( + + + + {showAuthWarning && + + Warning: You already have authentication configured via + environment variable or API key helper. + + + The setup-token command will create a new OAuth token which + you can use instead. + + } + { + void resolve(); + }} mode="setup-token" startingMessage="This will guide you through long-lived (1-year) auth token setup for your Claude account. Claude subscription required." /> + + + ); + }); + root.unmount(); + process.exit(0); +} + +// DoctorWithPlugins wrapper + doctor handler +const DoctorLazy = React.lazy(() => import('../../screens/Doctor.js').then(m => ({ + default: m.Doctor +}))); +function DoctorWithPlugins(t0) { + const $ = _c(2); + const { + onDone + } = t0; + useManagePlugins(); + let t1; + if ($[0] !== onDone) { + t1 = ; + $[0] = onDone; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} +export async function doctorHandler(root: Root): Promise { + logEvent('tengu_doctor_command', {}); + await new Promise(resolve => { + root.render( + + + { + void resolve(); + }} /> + + + ); + }); + root.unmount(); + process.exit(0); +} + +// install handler +export async function installHandler(target: string | undefined, options: { + force?: boolean; +}): Promise { + const { + setup + } = await import('../../setup.js'); + await setup(cwd(), 'default', false, false, undefined, false); + const { + install + } = await import('../../commands/install.js'); + await new Promise(resolve => { + const args: string[] = []; + if (target) args.push(target); + if (options.force) args.push('--force'); + void install.call(result => { + void resolve(); + process.exit(result.includes('failed') ? 1 : 0); + }, {}, args); + }); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjd2QiLCJSZWFjdCIsIldlbGNvbWVWMiIsInVzZU1hbmFnZVBsdWdpbnMiLCJSb290IiwiQm94IiwiVGV4dCIsIktleWJpbmRpbmdTZXR1cCIsImxvZ0V2ZW50IiwiTUNQQ29ubmVjdGlvbk1hbmFnZXIiLCJBcHBTdGF0ZVByb3ZpZGVyIiwib25DaGFuZ2VBcHBTdGF0ZSIsImlzQW50aHJvcGljQXV0aEVuYWJsZWQiLCJzZXR1cFRva2VuSGFuZGxlciIsInJvb3QiLCJQcm9taXNlIiwic2hvd0F1dGhXYXJuaW5nIiwiQ29uc29sZU9BdXRoRmxvdyIsInJlc29sdmUiLCJyZW5kZXIiLCJ1bm1vdW50IiwicHJvY2VzcyIsImV4aXQiLCJEb2N0b3JMYXp5IiwibGF6eSIsInRoZW4iLCJtIiwiZGVmYXVsdCIsIkRvY3RvciIsIkRvY3RvcldpdGhQbHVnaW5zIiwidDAiLCIkIiwiX2MiLCJvbkRvbmUiLCJ0MSIsImRvY3RvckhhbmRsZXIiLCJ1bmRlZmluZWQiLCJpbnN0YWxsSGFuZGxlciIsInRhcmdldCIsIm9wdGlvbnMiLCJmb3JjZSIsInNldHVwIiwiaW5zdGFsbCIsImFyZ3MiLCJwdXNoIiwiY2FsbCIsInJlc3VsdCIsImluY2x1ZGVzIl0sInNvdXJjZXMiOlsidXRpbC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBNaXNjZWxsYW5lb3VzIHN1YmNvbW1hbmQgaGFuZGxlcnMg4oCUIGV4dHJhY3RlZCBmcm9tIG1haW4udHN4IGZvciBsYXp5IGxvYWRpbmcuXG4gKiBzZXR1cC10b2tlbiwgZG9jdG9yLCBpbnN0YWxsXG4gKi9cbi8qIGVzbGludC1kaXNhYmxlIGN1c3RvbS1ydWxlcy9uby1wcm9jZXNzLWV4aXQgLS0gQ0xJIHN1YmNvbW1hbmQgaGFuZGxlcnMgaW50ZW50aW9uYWxseSBleGl0ICovXG5cbmltcG9ydCB7IGN3ZCB9IGZyb20gJ3Byb2Nlc3MnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBXZWxjb21lVjIgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0xvZ29WMi9XZWxjb21lVjIuanMnXG5pbXBvcnQgeyB1c2VNYW5hZ2VQbHVnaW5zIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlTWFuYWdlUGx1Z2lucy5qcydcbmltcG9ydCB0eXBlIHsgUm9vdCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IEtleWJpbmRpbmdTZXR1cCB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL0tleWJpbmRpbmdQcm92aWRlclNldHVwLmpzJ1xuaW1wb3J0IHsgbG9nRXZlbnQgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hbmFseXRpY3MvaW5kZXguanMnXG5pbXBvcnQgeyBNQ1BDb25uZWN0aW9uTWFuYWdlciB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL21jcC9NQ1BDb25uZWN0aW9uTWFuYWdlci5qcydcbmltcG9ydCB7IEFwcFN0YXRlUHJvdmlkZXIgfSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IG9uQ2hhbmdlQXBwU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS9vbkNoYW5nZUFwcFN0YXRlLmpzJ1xuaW1wb3J0IHsgaXNBbnRocm9waWNBdXRoRW5hYmxlZCB9IGZyb20gJy4uLy4uL3V0aWxzL2F1dGguanMnXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzZXR1cFRva2VuSGFuZGxlcihyb290OiBSb290KTogUHJvbWlzZTx2b2lkPiB7XG4gIGxvZ0V2ZW50KCd0ZW5ndV9zZXR1cF90b2tlbl9jb21tYW5kJywge30pXG5cbiAgY29uc3Qgc2hvd0F1dGhXYXJuaW5nID0gIWlzQW50aHJvcGljQXV0aEVuYWJsZWQoKVxuICBjb25zdCB7IENvbnNvbGVPQXV0aEZsb3cgfSA9IGF3YWl0IGltcG9ydChcbiAgICAnLi4vLi4vY29tcG9uZW50cy9Db25zb2xlT0F1dGhGbG93LmpzJ1xuICApXG4gIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KHJlc29sdmUgPT4ge1xuICAgIHJvb3QucmVuZGVyKFxuICAgICAgPEFwcFN0YXRlUHJvdmlkZXIgb25DaGFuZ2VBcHBTdGF0ZT17b25DaGFuZ2VBcHBTdGF0ZX0+XG4gICAgICAgIDxLZXliaW5kaW5nU2V0dXA+XG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgICAgIDxXZWxjb21lVjIgLz5cbiAgICAgICAgICAgIHtzaG93QXV0aFdhcm5pbmcgJiYgKFxuICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj5cbiAgICAgICAgICAgICAgICAgIFdhcm5pbmc6IFlvdSBhbHJlYWR5IGhhdmUgYXV0aGVudGljYXRpb24gY29uZmlndXJlZCB2aWFcbiAgICAgICAgICAgICAgICAgIGVudmlyb25tZW50IHZhcmlhYmxlIG9yIEFQSSBrZXkgaGVscGVyLlxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj5cbiAgICAgICAgICAgICAgICAgIFRoZSBzZXR1cC10b2tlbiBjb21tYW5kIHdpbGwgY3JlYXRlIGEgbmV3IE9BdXRoIHRva2VuIHdoaWNoXG4gICAgICAgICAgICAgICAgICB5b3UgY2FuIHVzZSBpbnN0ZWFkLlxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICApfVxuICAgICAgICAgICAgPENvbnNvbGVPQXV0aEZsb3dcbiAgICAgICAgICAgICAgb25Eb25lPXsoKSA9PiB7XG4gICAgICAgICAgICAgICAgdm9pZCByZXNvbHZlKClcbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgICAgbW9kZT1cInNldHVwLXRva2VuXCJcbiAgICAgICAgICAgICAgc3RhcnRpbmdNZXNzYWdlPVwiVGhpcyB3aWxsIGd1aWRlIHlvdSB0aHJvdWdoIGxvbmctbGl2ZWQgKDEteWVhcikgYXV0aCB0b2tlbiBzZXR1cCBmb3IgeW91ciBDbGF1ZGUgYWNjb3VudC4gQ2xhdWRlIHN1YnNjcmlwdGlvbiByZXF1aXJlZC5cIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgPC9LZXliaW5kaW5nU2V0dXA+XG4gICAgICA8L0FwcFN0YXRlUHJvdmlkZXI+LFxuICAgIClcbiAgfSlcbiAgcm9vdC51bm1vdW50KClcbiAgcHJvY2Vzcy5leGl0KDApXG59XG5cbi8vIERvY3RvcldpdGhQbHVnaW5zIHdyYXBwZXIgKyBkb2N0b3IgaGFuZGxlclxuY29uc3QgRG9jdG9yTGF6eSA9IFJlYWN0LmxhenkoKCkgPT5cbiAgaW1wb3J0KCcuLi8uLi9zY3JlZW5zL0RvY3Rvci5qcycpLnRoZW4obSA9PiAoeyBkZWZhdWx0OiBtLkRvY3RvciB9KSksXG4pXG5cbmZ1bmN0aW9uIERvY3RvcldpdGhQbHVnaW5zKHtcbiAgb25Eb25lLFxufToge1xuICBvbkRvbmU6ICgpID0+IHZvaWRcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICB1c2VNYW5hZ2VQbHVnaW5zKClcbiAgcmV0dXJuIChcbiAgICA8UmVhY3QuU3VzcGVuc2UgZmFsbGJhY2s9e251bGx9PlxuICAgICAgPERvY3Rvckxhenkgb25Eb25lPXtvbkRvbmV9IC8+XG4gICAgPC9SZWFjdC5TdXNwZW5zZT5cbiAgKVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZG9jdG9ySGFuZGxlcihyb290OiBSb290KTogUHJvbWlzZTx2b2lkPiB7XG4gIGxvZ0V2ZW50KCd0ZW5ndV9kb2N0b3JfY29tbWFuZCcsIHt9KVxuXG4gIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KHJlc29sdmUgPT4ge1xuICAgIHJvb3QucmVuZGVyKFxuICAgICAgPEFwcFN0YXRlUHJvdmlkZXI+XG4gICAgICAgIDxLZXliaW5kaW5nU2V0dXA+XG4gICAgICAgICAgPE1DUENvbm5lY3Rpb25NYW5hZ2VyXG4gICAgICAgICAgICBkeW5hbWljTWNwQ29uZmlnPXt1bmRlZmluZWR9XG4gICAgICAgICAgICBpc1N0cmljdE1jcENvbmZpZz17ZmFsc2V9XG4gICAgICAgICAgPlxuICAgICAgICAgICAgPERvY3RvcldpdGhQbHVnaW5zXG4gICAgICAgICAgICAgIG9uRG9uZT17KCkgPT4ge1xuICAgICAgICAgICAgICAgIHZvaWQgcmVzb2x2ZSgpXG4gICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAvPlxuICAgICAgICAgIDwvTUNQQ29ubmVjdGlvbk1hbmFnZXI+XG4gICAgICAgIDwvS2V5YmluZGluZ1NldHVwPlxuICAgICAgPC9BcHBTdGF0ZVByb3ZpZGVyPixcbiAgICApXG4gIH0pXG4gIHJvb3QudW5tb3VudCgpXG4gIHByb2Nlc3MuZXhpdCgwKVxufVxuXG4vLyBpbnN0YWxsIGhhbmRsZXJcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbnN0YWxsSGFuZGxlcihcbiAgdGFyZ2V0OiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gIG9wdGlvbnM6IHsgZm9yY2U/OiBib29sZWFuIH0sXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgeyBzZXR1cCB9ID0gYXdhaXQgaW1wb3J0KCcuLi8uLi9zZXR1cC5qcycpXG4gIGF3YWl0IHNldHVwKGN3ZCgpLCAnZGVmYXVsdCcsIGZhbHNlLCBmYWxzZSwgdW5kZWZpbmVkLCBmYWxzZSlcbiAgY29uc3QgeyBpbnN0YWxsIH0gPSBhd2FpdCBpbXBvcnQoJy4uLy4uL2NvbW1hbmRzL2luc3RhbGwuanMnKVxuICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPihyZXNvbHZlID0+IHtcbiAgICBjb25zdCBhcmdzOiBzdHJpbmdbXSA9IFtdXG4gICAgaWYgKHRhcmdldCkgYXJncy5wdXNoKHRhcmdldClcbiAgICBpZiAob3B0aW9ucy5mb3JjZSkgYXJncy5wdXNoKCctLWZvcmNlJylcblxuICAgIHZvaWQgaW5zdGFsbC5jYWxsKFxuICAgICAgcmVzdWx0ID0+IHtcbiAgICAgICAgdm9pZCByZXNvbHZlKClcbiAgICAgICAgcHJvY2Vzcy5leGl0KHJlc3VsdC5pbmNsdWRlcygnZmFpbGVkJykgPyAxIDogMClcbiAgICAgIH0sXG4gICAgICB7fSxcbiAgICAgIGFyZ3MsXG4gICAgKVxuICB9KVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxTQUFTQSxHQUFHLFFBQVEsU0FBUztBQUM3QixPQUFPQyxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyxTQUFTLFFBQVEsc0NBQXNDO0FBQ2hFLFNBQVNDLGdCQUFnQixRQUFRLGlDQUFpQztBQUNsRSxjQUFjQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQVNDLGVBQWUsUUFBUSw4Q0FBOEM7QUFDOUUsU0FBU0MsUUFBUSxRQUFRLG1DQUFtQztBQUM1RCxTQUFTQyxvQkFBb0IsUUFBUSw0Q0FBNEM7QUFDakYsU0FBU0MsZ0JBQWdCLFFBQVEseUJBQXlCO0FBQzFELFNBQVNDLGdCQUFnQixRQUFRLGlDQUFpQztBQUNsRSxTQUFTQyxzQkFBc0IsUUFBUSxxQkFBcUI7QUFFNUQsT0FBTyxlQUFlQyxpQkFBaUJBLENBQUNDLElBQUksRUFBRVYsSUFBSSxDQUFDLEVBQUVXLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztFQUNqRVAsUUFBUSxDQUFDLDJCQUEyQixFQUFFLENBQUMsQ0FBQyxDQUFDO0VBRXpDLE1BQU1RLGVBQWUsR0FBRyxDQUFDSixzQkFBc0IsQ0FBQyxDQUFDO0VBQ2pELE1BQU07SUFBRUs7RUFBaUIsQ0FBQyxHQUFHLE1BQU0sTUFBTSxDQUN2QyxzQ0FDRixDQUFDO0VBQ0QsTUFBTSxJQUFJRixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUNHLE9BQU8sSUFBSTtJQUNqQ0osSUFBSSxDQUFDSyxNQUFNLENBQ1QsQ0FBQyxnQkFBZ0IsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDUixnQkFBZ0IsQ0FBQztBQUMzRCxRQUFRLENBQUMsZUFBZTtBQUN4QixVQUFVLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzdDLFlBQVksQ0FBQyxTQUFTO0FBQ3RCLFlBQVksQ0FBQ0ssZUFBZSxJQUNkLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxRQUFRO0FBQ3pDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUztBQUNyQztBQUNBO0FBQ0EsZ0JBQWdCLEVBQUUsSUFBSTtBQUN0QixnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVM7QUFDckM7QUFDQTtBQUNBLGdCQUFnQixFQUFFLElBQUk7QUFDdEIsY0FBYyxFQUFFLEdBQUcsQ0FDTjtBQUNiLFlBQVksQ0FBQyxnQkFBZ0IsQ0FDZixNQUFNLENBQUMsQ0FBQyxNQUFNO1lBQ1osS0FBS0UsT0FBTyxDQUFDLENBQUM7VUFDaEIsQ0FBQyxDQUFDLENBQ0YsSUFBSSxDQUFDLGFBQWEsQ0FDbEIsZUFBZSxDQUFDLHlIQUF5SDtBQUV2SixVQUFVLEVBQUUsR0FBRztBQUNmLFFBQVEsRUFBRSxlQUFlO0FBQ3pCLE1BQU0sRUFBRSxnQkFBZ0IsQ0FDcEIsQ0FBQztFQUNILENBQUMsQ0FBQztFQUNGSixJQUFJLENBQUNNLE9BQU8sQ0FBQyxDQUFDO0VBQ2RDLE9BQU8sQ0FBQ0MsSUFBSSxDQUFDLENBQUMsQ0FBQztBQUNqQjs7QUFFQTtBQUNBLE1BQU1DLFVBQVUsR0FBR3RCLEtBQUssQ0FBQ3VCLElBQUksQ0FBQyxNQUM1QixNQUFNLENBQUMseUJBQXlCLENBQUMsQ0FBQ0MsSUFBSSxDQUFDQyxDQUFDLEtBQUs7RUFBRUMsT0FBTyxFQUFFRCxDQUFDLENBQUNFO0FBQU8sQ0FBQyxDQUFDLENBQ3JFLENBQUM7QUFFRCxTQUFBQyxrQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUEyQjtJQUFBQztFQUFBLElBQUFILEVBSTFCO0VBQ0MzQixnQkFBZ0IsQ0FBQyxDQUFDO0VBQUEsSUFBQStCLEVBQUE7RUFBQSxJQUFBSCxDQUFBLFFBQUFFLE1BQUE7SUFFaEJDLEVBQUEsbUJBQTBCLFFBQUksQ0FBSixLQUFHLENBQUMsQ0FDNUIsQ0FBQyxVQUFVLENBQVNELE1BQU0sQ0FBTkEsT0FBSyxDQUFDLEdBQzVCLGlCQUFpQjtJQUFBRixDQUFBLE1BQUFFLE1BQUE7SUFBQUYsQ0FBQSxNQUFBRyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSCxDQUFBO0VBQUE7RUFBQSxPQUZqQkcsRUFFaUI7QUFBQTtBQUlyQixPQUFPLGVBQWVDLGFBQWFBLENBQUNyQixJQUFJLEVBQUVWLElBQUksQ0FBQyxFQUFFVyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7RUFDN0RQLFFBQVEsQ0FBQyxzQkFBc0IsRUFBRSxDQUFDLENBQUMsQ0FBQztFQUVwQyxNQUFNLElBQUlPLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQ0csT0FBTyxJQUFJO0lBQ2pDSixJQUFJLENBQUNLLE1BQU0sQ0FDVCxDQUFDLGdCQUFnQjtBQUN2QixRQUFRLENBQUMsZUFBZTtBQUN4QixVQUFVLENBQUMsb0JBQW9CLENBQ25CLGdCQUFnQixDQUFDLENBQUNpQixTQUFTLENBQUMsQ0FDNUIsaUJBQWlCLENBQUMsQ0FBQyxLQUFLLENBQUM7QUFFckMsWUFBWSxDQUFDLGlCQUFpQixDQUNoQixNQUFNLENBQUMsQ0FBQyxNQUFNO1lBQ1osS0FBS2xCLE9BQU8sQ0FBQyxDQUFDO1VBQ2hCLENBQUMsQ0FBQztBQUVoQixVQUFVLEVBQUUsb0JBQW9CO0FBQ2hDLFFBQVEsRUFBRSxlQUFlO0FBQ3pCLE1BQU0sRUFBRSxnQkFBZ0IsQ0FDcEIsQ0FBQztFQUNILENBQUMsQ0FBQztFQUNGSixJQUFJLENBQUNNLE9BQU8sQ0FBQyxDQUFDO0VBQ2RDLE9BQU8sQ0FBQ0MsSUFBSSxDQUFDLENBQUMsQ0FBQztBQUNqQjs7QUFFQTtBQUNBLE9BQU8sZUFBZWUsY0FBY0EsQ0FDbENDLE1BQU0sRUFBRSxNQUFNLEdBQUcsU0FBUyxFQUMxQkMsT0FBTyxFQUFFO0VBQUVDLEtBQUssQ0FBQyxFQUFFLE9BQU87QUFBQyxDQUFDLENBQzdCLEVBQUV6QixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7RUFDZixNQUFNO0lBQUUwQjtFQUFNLENBQUMsR0FBRyxNQUFNLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztFQUNoRCxNQUFNQSxLQUFLLENBQUN6QyxHQUFHLENBQUMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFb0MsU0FBUyxFQUFFLEtBQUssQ0FBQztFQUM3RCxNQUFNO0lBQUVNO0VBQVEsQ0FBQyxHQUFHLE1BQU0sTUFBTSxDQUFDLDJCQUEyQixDQUFDO0VBQzdELE1BQU0sSUFBSTNCLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQ0csT0FBTyxJQUFJO0lBQ2pDLE1BQU15QixJQUFJLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRTtJQUN6QixJQUFJTCxNQUFNLEVBQUVLLElBQUksQ0FBQ0MsSUFBSSxDQUFDTixNQUFNLENBQUM7SUFDN0IsSUFBSUMsT0FBTyxDQUFDQyxLQUFLLEVBQUVHLElBQUksQ0FBQ0MsSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUV2QyxLQUFLRixPQUFPLENBQUNHLElBQUksQ0FDZkMsTUFBTSxJQUFJO01BQ1IsS0FBSzVCLE9BQU8sQ0FBQyxDQUFDO01BQ2RHLE9BQU8sQ0FBQ0MsSUFBSSxDQUFDd0IsTUFBTSxDQUFDQyxRQUFRLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUNqRCxDQUFDLEVBQ0QsQ0FBQyxDQUFDLEVBQ0ZKLElBQ0YsQ0FBQztFQUNILENBQUMsQ0FBQztBQUNKIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/cli/ndjsonSafeStringify.ts b/cli/ndjsonSafeStringify.ts new file mode 100644 index 0000000..af570ad --- /dev/null +++ b/cli/ndjsonSafeStringify.ts @@ -0,0 +1,32 @@ +import { jsonStringify } from '../utils/slowOperations.js' + +// JSON.stringify emits U+2028/U+2029 raw (valid per ECMA-404). When the +// output is a single NDJSON line, any receiver that uses JavaScript +// line-terminator semantics (ECMA-262 §11.3 — \n \r U+2028 U+2029) to +// split the stream will cut the JSON mid-string. ProcessTransport now +// silently skips non-JSON lines rather than crashing (gh-28405), but +// the truncated fragment is still lost — the message is silently dropped. +// +// The \uXXXX form is equivalent JSON (parses to the same string) but +// can never be mistaken for a line terminator by ANY receiver. This is +// what ES2019's "Subsume JSON" proposal and Node's util.inspect do. +// +// Single regex with alternation: the callback's one dispatch per match +// is cheaper than two full-string scans. +const JS_LINE_TERMINATORS = /\u2028|\u2029/g + +function escapeJsLineTerminators(json: string): string { + return json.replace(JS_LINE_TERMINATORS, c => + c === '\u2028' ? '\\u2028' : '\\u2029', + ) +} + +/** + * JSON.stringify for one-message-per-line transports. Escapes U+2028 + * LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR so the serialized output + * cannot be broken by a line-splitting receiver. Output is still valid + * JSON and parses to the same value. + */ +export function ndjsonSafeStringify(value: unknown): string { + return escapeJsLineTerminators(jsonStringify(value)) +} diff --git a/cli/print.ts b/cli/print.ts new file mode 100644 index 0000000..6047257 --- /dev/null +++ b/cli/print.ts @@ -0,0 +1,5594 @@ +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +import { feature } from 'bun:bundle' +import { readFile, stat } from 'fs/promises' +import { dirname } from 'path' +import { + downloadUserSettings, + redownloadUserSettings, +} from 'src/services/settingsSync/index.js' +import { waitForRemoteManagedSettingsToLoad } from 'src/services/remoteManagedSettings/index.js' +import { StructuredIO } from 'src/cli/structuredIO.js' +import { RemoteIO } from 'src/cli/remoteIO.js' +import { + type Command, + formatDescriptionWithSource, + getCommandName, +} from 'src/commands.js' +import { createStreamlinedTransformer } from 'src/utils/streamlinedTransform.js' +import { installStreamJsonStdoutGuard } from 'src/utils/streamJsonStdoutGuard.js' +import type { ToolPermissionContext } from 'src/Tool.js' +import type { ThinkingConfig } from 'src/utils/thinking.js' +import { assembleToolPool, filterToolsByDenyRules } from 'src/tools.js' +import uniqBy from 'lodash-es/uniqBy.js' +import { uniq } from 'src/utils/array.js' +import { mergeAndFilterTools } from 'src/utils/toolPool.js' +import { + logEvent, + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, +} from 'src/services/analytics/index.js' +import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js' +import { logForDebugging } from 'src/utils/debug.js' +import { + logForDiagnosticsNoPII, + withDiagnosticsTiming, +} from 'src/utils/diagLogs.js' +import { toolMatchesName, type Tool, type Tools } from 'src/Tool.js' +import { + type AgentDefinition, + isBuiltInAgent, + parseAgentsFromJson, +} from 'src/tools/AgentTool/loadAgentsDir.js' +import type { Message, NormalizedUserMessage } from 'src/types/message.js' +import type { QueuedCommand } from 'src/types/textInputTypes.js' +import { + dequeue, + dequeueAllMatching, + enqueue, + hasCommandsInQueue, + peek, + subscribeToCommandQueue, + getCommandsByMaxPriority, +} from 'src/utils/messageQueueManager.js' +import { notifyCommandLifecycle } from 'src/utils/commandLifecycle.js' +import { + getSessionState, + notifySessionStateChanged, + notifySessionMetadataChanged, + setPermissionModeChangedListener, + type RequiresActionDetails, + type SessionExternalMetadata, +} from 'src/utils/sessionState.js' +import { externalMetadataToAppState } from 'src/state/onChangeAppState.js' +import { getInMemoryErrors, logError, logMCPDebug } from 'src/utils/log.js' +import { + writeToStdout, + registerProcessOutputErrorHandlers, +} from 'src/utils/process.js' +import type { Stream } from 'src/utils/stream.js' +import { EMPTY_USAGE } from 'src/services/api/logging.js' +import { + loadConversationForResume, + type TurnInterruptionState, +} from 'src/utils/conversationRecovery.js' +import type { + MCPServerConnection, + McpSdkServerConfig, + ScopedMcpServerConfig, +} from 'src/services/mcp/types.js' +import { + ChannelMessageNotificationSchema, + gateChannelServer, + wrapChannelMessage, + findChannelEntry, +} from 'src/services/mcp/channelNotification.js' +import { + isChannelAllowlisted, + isChannelsEnabled, +} from 'src/services/mcp/channelAllowlist.js' +import { parsePluginIdentifier } from 'src/utils/plugins/pluginIdentifier.js' +import { validateUuid } from 'src/utils/uuid.js' +import { fromArray } from 'src/utils/generators.js' +import { ask } from 'src/QueryEngine.js' +import type { PermissionPromptTool } from 'src/utils/queryHelpers.js' +import { + createFileStateCacheWithSizeLimit, + mergeFileStateCaches, + READ_FILE_STATE_CACHE_SIZE, +} from 'src/utils/fileStateCache.js' +import { expandPath } from 'src/utils/path.js' +import { extractReadFilesFromMessages } from 'src/utils/queryHelpers.js' +import { registerHookEventHandler } from 'src/utils/hooks/hookEvents.js' +import { executeFilePersistence } from 'src/utils/filePersistence/filePersistence.js' +import { finalizePendingAsyncHooks } from 'src/utils/hooks/AsyncHookRegistry.js' +import { + gracefulShutdown, + gracefulShutdownSync, + isShuttingDown, +} from 'src/utils/gracefulShutdown.js' +import { registerCleanup } from 'src/utils/cleanupRegistry.js' +import { createIdleTimeoutManager } from 'src/utils/idleTimeout.js' +import type { + SDKStatus, + ModelInfo, + SDKMessage, + SDKUserMessage, + SDKUserMessageReplay, + PermissionResult, + McpServerConfigForProcessTransport, + McpServerStatus, + RewindFilesResult, +} from 'src/entrypoints/agentSdkTypes.js' +import type { + StdoutMessage, + SDKControlInitializeRequest, + SDKControlInitializeResponse, + SDKControlRequest, + SDKControlResponse, + SDKControlMcpSetServersResponse, + SDKControlReloadPluginsResponse, +} from 'src/entrypoints/sdk/controlTypes.js' +import type { PermissionMode } from '@anthropic-ai/claude-agent-sdk' +import type { PermissionMode as InternalPermissionMode } from 'src/types/permissions.js' +import { cwd } from 'process' +import { getCwd } from 'src/utils/cwd.js' +import omit from 'lodash-es/omit.js' +import reject from 'lodash-es/reject.js' +import { isPolicyAllowed } from 'src/services/policyLimits/index.js' +import type { ReplBridgeHandle } from 'src/bridge/replBridge.js' +import { getRemoteSessionUrl } from 'src/constants/product.js' +import { buildBridgeConnectUrl } from 'src/bridge/bridgeStatusUtil.js' +import { extractInboundMessageFields } from 'src/bridge/inboundMessages.js' +import { resolveAndPrepend } from 'src/bridge/inboundAttachments.js' +import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js' +import { hasPermissionsToUseTool } from 'src/utils/permissions/permissions.js' +import { safeParseJSON } from 'src/utils/json.js' +import { + outputSchema as permissionToolOutputSchema, + permissionPromptToolResultToPermissionDecision, +} from 'src/utils/permissions/PermissionPromptToolResultSchema.js' +import { createAbortController } from 'src/utils/abortController.js' +import { createCombinedAbortSignal } from 'src/utils/combinedAbortSignal.js' +import { generateSessionTitle } from 'src/utils/sessionTitle.js' +import { buildSideQuestionFallbackParams } from 'src/utils/queryContext.js' +import { runSideQuestion } from 'src/utils/sideQuestion.js' +import { + processSessionStartHooks, + processSetupHooks, + takeInitialUserMessage, +} from 'src/utils/sessionStart.js' +import { + DEFAULT_OUTPUT_STYLE_NAME, + getAllOutputStyles, +} from 'src/constants/outputStyles.js' +import { TEAMMATE_MESSAGE_TAG, TICK_TAG } from 'src/constants/xml.js' +import { + getSettings_DEPRECATED, + getSettingsWithSources, +} from 'src/utils/settings/settings.js' +import { settingsChangeDetector } from 'src/utils/settings/changeDetector.js' +import { applySettingsChange } from 'src/utils/settings/applySettingsChange.js' +import { + isFastModeAvailable, + isFastModeEnabled, + isFastModeSupportedByModel, + getFastModeState, +} from 'src/utils/fastMode.js' +import { + isAutoModeGateEnabled, + getAutoModeUnavailableNotification, + getAutoModeUnavailableReason, + isBypassPermissionsModeDisabled, + transitionPermissionMode, +} from 'src/utils/permissions/permissionSetup.js' +import { + tryGenerateSuggestion, + logSuggestionOutcome, + logSuggestionSuppressed, + type PromptVariant, +} from 'src/services/PromptSuggestion/promptSuggestion.js' +import { getLastCacheSafeParams } from 'src/utils/forkedAgent.js' +import { getAccountInformation } from 'src/utils/auth.js' +import { OAuthService } from 'src/services/oauth/index.js' +import { installOAuthTokens } from 'src/cli/handlers/auth.js' +import { getAPIProvider } from 'src/utils/model/providers.js' +import type { HookCallbackMatcher } from 'src/types/hooks.js' +import { AwsAuthStatusManager } from 'src/utils/awsAuthStatusManager.js' +import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js' +import { + registerHookCallbacks, + setInitJsonSchema, + getInitJsonSchema, + setSdkAgentProgressSummariesEnabled, +} from 'src/bootstrap/state.js' +import { createSyntheticOutputTool } from 'src/tools/SyntheticOutputTool/SyntheticOutputTool.js' +import { parseSessionIdentifier } from 'src/utils/sessionUrl.js' +import { + hydrateRemoteSession, + hydrateFromCCRv2InternalEvents, + resetSessionFilePointer, + doesMessageExistInSession, + findUnresolvedToolUse, + recordAttributionSnapshot, + saveAgentSetting, + saveMode, + saveAiGeneratedTitle, + restoreSessionMetadata, +} from 'src/utils/sessionStorage.js' +import { incrementPromptCount } from 'src/utils/commitAttribution.js' +import { + setupSdkMcpClients, + connectToServer, + clearServerCache, + fetchToolsForClient, + areMcpConfigsEqual, + reconnectMcpServerImpl, +} from 'src/services/mcp/client.js' +import { + filterMcpServersByPolicy, + getMcpConfigByName, + isMcpServerDisabled, + setMcpServerEnabled, +} from 'src/services/mcp/config.js' +import { + performMCPOAuthFlow, + revokeServerTokens, +} from 'src/services/mcp/auth.js' +import { + runElicitationHooks, + runElicitationResultHooks, +} from 'src/services/mcp/elicitationHandler.js' +import { executeNotificationHooks } from 'src/utils/hooks.js' +import { + ElicitRequestSchema, + ElicitationCompleteNotificationSchema, +} from '@modelcontextprotocol/sdk/types.js' +import { getMcpPrefix } from 'src/services/mcp/mcpStringUtils.js' +import { + commandBelongsToServer, + filterToolsByServer, +} from 'src/services/mcp/utils.js' +import { setupVscodeSdkMcp } from 'src/services/mcp/vscodeSdkMcp.js' +import { getAllMcpConfigs } from 'src/services/mcp/config.js' +import { + isQualifiedForGrove, + checkGroveForNonInteractive, +} from 'src/services/api/grove.js' +import { + toInternalMessages, + toSDKRateLimitInfo, +} from 'src/utils/messages/mappers.js' +import { createModelSwitchBreadcrumbs } from 'src/utils/messages.js' +import { collectContextData } from 'src/commands/context/context-noninteractive.js' +import { LOCAL_COMMAND_STDOUT_TAG } from 'src/constants/xml.js' +import { + statusListeners, + type ClaudeAILimits, +} from 'src/services/claudeAiLimits.js' +import { + getDefaultMainLoopModel, + getMainLoopModel, + modelDisplayString, + parseUserSpecifiedModel, +} from 'src/utils/model/model.js' +import { getModelOptions } from 'src/utils/model/modelOptions.js' +import { + modelSupportsEffort, + modelSupportsMaxEffort, + EFFORT_LEVELS, + resolveAppliedEffort, +} from 'src/utils/effort.js' +import { modelSupportsAdaptiveThinking } from 'src/utils/thinking.js' +import { modelSupportsAutoMode } from 'src/utils/betas.js' +import { ensureModelStringsInitialized } from 'src/utils/model/modelStrings.js' +import { + getSessionId, + setMainLoopModelOverride, + setMainThreadAgentType, + switchSession, + isSessionPersistenceDisabled, + getIsRemoteMode, + getFlagSettingsInline, + setFlagSettingsInline, + getMainThreadAgentType, + getAllowedChannels, + setAllowedChannels, + type ChannelEntry, +} from 'src/bootstrap/state.js' +import { runWithWorkload, WORKLOAD_CRON } from 'src/utils/workloadContext.js' +import type { UUID } from 'crypto' +import { randomUUID } from 'crypto' +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs' +import type { AppState } from 'src/state/AppStateStore.js' +import { + fileHistoryRewind, + fileHistoryCanRestore, + fileHistoryEnabled, + fileHistoryGetDiffStats, +} from 'src/utils/fileHistory.js' +import { + restoreAgentFromSession, + restoreSessionStateFromLog, +} from 'src/utils/sessionRestore.js' +import { SandboxManager } from 'src/utils/sandbox/sandbox-adapter.js' +import { + headlessProfilerStartTurn, + headlessProfilerCheckpoint, + logHeadlessProfilerTurn, +} from 'src/utils/headlessProfiler.js' +import { + startQueryProfile, + logQueryProfileReport, +} from 'src/utils/queryProfiler.js' +import { asSessionId } from 'src/types/ids.js' +import { jsonStringify } from '../utils/slowOperations.js' +import { skillChangeDetector } from '../utils/skills/skillChangeDetector.js' +import { getCommands, clearCommandsCache } from '../commands.js' +import { + isBareMode, + isEnvTruthy, + isEnvDefinedFalsy, +} from '../utils/envUtils.js' +import { installPluginsForHeadless } from '../utils/plugins/headlessPluginInstall.js' +import { refreshActivePlugins } from '../utils/plugins/refresh.js' +import { loadAllPluginsCacheOnly } from '../utils/plugins/pluginLoader.js' +import { + isTeamLead, + hasActiveInProcessTeammates, + hasWorkingInProcessTeammates, + waitForTeammatesToBecomeIdle, +} from '../utils/teammate.js' +import { + readUnreadMessages, + markMessagesAsRead, + isShutdownApproved, +} from '../utils/teammateMailbox.js' +import { removeTeammateFromTeamFile } from '../utils/swarm/teamHelpers.js' +import { unassignTeammateTasks } from '../utils/tasks.js' +import { getRunningTasks } from '../utils/task/framework.js' +import { isBackgroundTask } from '../tasks/types.js' +import { stopTask } from '../tasks/stopTask.js' +import { drainSdkEvents } from '../utils/sdkEventQueue.js' +import { initializeGrowthBook } from '../services/analytics/growthbook.js' +import { errorMessage, toError } from '../utils/errors.js' +import { sleep } from '../utils/sleep.js' +import { isExtractModeActive } from '../memdir/paths.js' + +// Dead code elimination: conditional imports +/* eslint-disable @typescript-eslint/no-require-imports */ +const coordinatorModeModule = feature('COORDINATOR_MODE') + ? (require('../coordinator/coordinatorMode.js') as typeof import('../coordinator/coordinatorMode.js')) + : null +const proactiveModule = + feature('PROACTIVE') || feature('KAIROS') + ? (require('../proactive/index.js') as typeof import('../proactive/index.js')) + : null +const cronSchedulerModule = feature('AGENT_TRIGGERS') + ? (require('../utils/cronScheduler.js') as typeof import('../utils/cronScheduler.js')) + : null +const cronJitterConfigModule = feature('AGENT_TRIGGERS') + ? (require('../utils/cronJitterConfig.js') as typeof import('../utils/cronJitterConfig.js')) + : null +const cronGate = feature('AGENT_TRIGGERS') + ? (require('../tools/ScheduleCronTool/prompt.js') as typeof import('../tools/ScheduleCronTool/prompt.js')) + : null +const extractMemoriesModule = feature('EXTRACT_MEMORIES') + ? (require('../services/extractMemories/extractMemories.js') as typeof import('../services/extractMemories/extractMemories.js')) + : null +/* eslint-enable @typescript-eslint/no-require-imports */ + +const SHUTDOWN_TEAM_PROMPT = ` +You are running in non-interactive mode and cannot return a response to the user until your team is shut down. + +You MUST shut down your team before preparing your final response: +1. Use requestShutdown to ask each team member to shut down gracefully +2. Wait for shutdown approvals +3. Use the cleanup operation to clean up the team +4. Only then provide your final response to the user + +The user cannot receive your response until the team is completely shut down. + + +Shut down your team and prepare your final response for the user.` + +// Track message UUIDs received during the current session runtime +const MAX_RECEIVED_UUIDS = 10_000 +const receivedMessageUuids = new Set() +const receivedMessageUuidsOrder: UUID[] = [] + +function trackReceivedMessageUuid(uuid: UUID): boolean { + if (receivedMessageUuids.has(uuid)) { + return false // duplicate + } + receivedMessageUuids.add(uuid) + receivedMessageUuidsOrder.push(uuid) + // Evict oldest entries when at capacity + if (receivedMessageUuidsOrder.length > MAX_RECEIVED_UUIDS) { + const toEvict = receivedMessageUuidsOrder.splice( + 0, + receivedMessageUuidsOrder.length - MAX_RECEIVED_UUIDS, + ) + for (const old of toEvict) { + receivedMessageUuids.delete(old) + } + } + return true // new UUID +} + +type PromptValue = string | ContentBlockParam[] + +function toBlocks(v: PromptValue): ContentBlockParam[] { + return typeof v === 'string' ? [{ type: 'text', text: v }] : v +} + +/** + * Join prompt values from multiple queued commands into one. Strings are + * newline-joined; if any value is a block array, all values are normalized + * to blocks and concatenated. + */ +export function joinPromptValues(values: PromptValue[]): PromptValue { + if (values.length === 1) return values[0]! + if (values.every(v => typeof v === 'string')) { + return values.join('\n') + } + return values.flatMap(toBlocks) +} + +/** + * Whether `next` can be batched into the same ask() call as `head`. Only + * prompt-mode commands batch, and only when the workload tag matches (so the + * combined turn is attributed correctly) and the isMeta flag matches (so a + * proactive tick can't merge into a user prompt and lose its hidden-in- + * transcript marking when the head is spread over the merged command). + */ +export function canBatchWith( + head: QueuedCommand, + next: QueuedCommand | undefined, +): boolean { + return ( + next !== undefined && + next.mode === 'prompt' && + next.workload === head.workload && + next.isMeta === head.isMeta + ) +} + +export async function runHeadless( + inputPrompt: string | AsyncIterable, + getAppState: () => AppState, + setAppState: (f: (prev: AppState) => AppState) => void, + commands: Command[], + tools: Tools, + sdkMcpConfigs: Record, + agents: AgentDefinition[], + options: { + continue: boolean | undefined + resume: string | boolean | undefined + resumeSessionAt: string | undefined + verbose: boolean | undefined + outputFormat: string | undefined + jsonSchema: Record | undefined + permissionPromptToolName: string | undefined + allowedTools: string[] | undefined + thinkingConfig: ThinkingConfig | undefined + maxTurns: number | undefined + maxBudgetUsd: number | undefined + taskBudget: { total: number } | undefined + systemPrompt: string | undefined + appendSystemPrompt: string | undefined + userSpecifiedModel: string | undefined + fallbackModel: string | undefined + teleport: string | true | null | undefined + sdkUrl: string | undefined + replayUserMessages: boolean | undefined + includePartialMessages: boolean | undefined + forkSession: boolean | undefined + rewindFiles: string | undefined + enableAuthStatus: boolean | undefined + agent: string | undefined + workload: string | undefined + setupTrigger?: 'init' | 'maintenance' | undefined + sessionStartHooksPromise?: ReturnType + setSDKStatus?: (status: SDKStatus) => void + }, +): Promise { + if ( + process.env.USER_TYPE === 'ant' && + isEnvTruthy(process.env.CLAUDE_CODE_EXIT_AFTER_FIRST_RENDER) + ) { + process.stderr.write( + `\nStartup time: ${Math.round(process.uptime() * 1000)}ms\n`, + ) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(0) + } + + // Fire user settings download now so it overlaps with the MCP/tool setup + // below. Managed settings already started in main.tsx preAction; this gives + // user settings a similar head start. The cached promise is joined in + // installPluginsAndApplyMcpInBackground before plugin install reads + // enabledPlugins. + if ( + feature('DOWNLOAD_USER_SETTINGS') && + (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) || getIsRemoteMode()) + ) { + void downloadUserSettings() + } + + // In headless mode there is no React tree, so the useSettingsChange hook + // never runs. Subscribe directly so that settings changes (including + // managed-settings / policy updates) are fully applied. + settingsChangeDetector.subscribe(source => { + applySettingsChange(source, setAppState) + + // In headless mode, also sync the denormalized fastMode field from + // settings. The TUI manages fastMode via the UI so it skips this. + if (isFastModeEnabled()) { + setAppState(prev => { + const s = prev.settings as Record + const fastMode = s.fastMode === true && !s.fastModePerSessionOptIn + return { ...prev, fastMode } + }) + } + }) + + // Proactive activation is now handled in main.tsx before getTools() so + // SleepTool passes isEnabled() filtering. This fallback covers the case + // where CLAUDE_CODE_PROACTIVE is set but main.tsx's check didn't fire + // (e.g. env was injected by the SDK transport after argv parsing). + if ( + (feature('PROACTIVE') || feature('KAIROS')) && + proactiveModule && + !proactiveModule.isProactiveActive() && + isEnvTruthy(process.env.CLAUDE_CODE_PROACTIVE) + ) { + proactiveModule.activateProactive('command') + } + + // Periodically force a full GC to keep memory usage in check + if (typeof Bun !== 'undefined') { + const gcTimer = setInterval(Bun.gc, 1000) + gcTimer.unref() + } + + // Start headless profiler for first turn + headlessProfilerStartTurn() + headlessProfilerCheckpoint('runHeadless_entry') + + // Check Grove requirements for non-interactive consumer subscribers + if (await isQualifiedForGrove()) { + await checkGroveForNonInteractive() + } + headlessProfilerCheckpoint('after_grove_check') + + // Initialize GrowthBook so feature flags take effect in headless mode. + // Without this, the disk cache is empty and all flags fall back to defaults. + void initializeGrowthBook() + + if (options.resumeSessionAt && !options.resume) { + process.stderr.write(`Error: --resume-session-at requires --resume\n`) + gracefulShutdownSync(1) + return + } + + if (options.rewindFiles && !options.resume) { + process.stderr.write(`Error: --rewind-files requires --resume\n`) + gracefulShutdownSync(1) + return + } + + if (options.rewindFiles && inputPrompt) { + process.stderr.write( + `Error: --rewind-files is a standalone operation and cannot be used with a prompt\n`, + ) + gracefulShutdownSync(1) + return + } + + const structuredIO = getStructuredIO(inputPrompt, options) + + // When emitting NDJSON for SDK clients, any stray write to stdout (debug + // prints, dependency console.log, library banners) breaks the client's + // line-by-line JSON parser. Install a guard that diverts non-JSON lines to + // stderr so the stream stays clean. Must run before the first + // structuredIO.write below. + if (options.outputFormat === 'stream-json') { + installStreamJsonStdoutGuard() + } + + // #34044: if user explicitly set sandbox.enabled=true but deps are missing, + // isSandboxingEnabled() returns false silently. Surface the reason so users + // know their security config isn't being enforced. + const sandboxUnavailableReason = SandboxManager.getSandboxUnavailableReason() + if (sandboxUnavailableReason) { + if (SandboxManager.isSandboxRequired()) { + process.stderr.write( + `\nError: sandbox required but unavailable: ${sandboxUnavailableReason}\n` + + ` sandbox.failIfUnavailable is set — refusing to start without a working sandbox.\n\n`, + ) + gracefulShutdownSync(1) + return + } + process.stderr.write( + `\n⚠ Sandbox disabled: ${sandboxUnavailableReason}\n` + + ` Commands will run WITHOUT sandboxing. Network and filesystem restrictions will NOT be enforced.\n\n`, + ) + } else if (SandboxManager.isSandboxingEnabled()) { + // Initialize sandbox with a callback that forwards network permission + // requests to the SDK host via the can_use_tool control_request protocol. + // This must happen after structuredIO is created so we can send requests. + try { + await SandboxManager.initialize(structuredIO.createSandboxAskCallback()) + } catch (err) { + process.stderr.write(`\n❌ Sandbox Error: ${errorMessage(err)}\n`) + gracefulShutdownSync(1, 'other') + return + } + } + + if (options.outputFormat === 'stream-json' && options.verbose) { + registerHookEventHandler(event => { + const message: StdoutMessage = (() => { + switch (event.type) { + case 'started': + return { + type: 'system' as const, + subtype: 'hook_started' as const, + hook_id: event.hookId, + hook_name: event.hookName, + hook_event: event.hookEvent, + uuid: randomUUID(), + session_id: getSessionId(), + } + case 'progress': + return { + type: 'system' as const, + subtype: 'hook_progress' as const, + hook_id: event.hookId, + hook_name: event.hookName, + hook_event: event.hookEvent, + stdout: event.stdout, + stderr: event.stderr, + output: event.output, + uuid: randomUUID(), + session_id: getSessionId(), + } + case 'response': + return { + type: 'system' as const, + subtype: 'hook_response' as const, + hook_id: event.hookId, + hook_name: event.hookName, + hook_event: event.hookEvent, + output: event.output, + stdout: event.stdout, + stderr: event.stderr, + exit_code: event.exitCode, + outcome: event.outcome, + uuid: randomUUID(), + session_id: getSessionId(), + } + } + })() + void structuredIO.write(message) + }) + } + + if (options.setupTrigger) { + await processSetupHooks(options.setupTrigger) + } + + headlessProfilerCheckpoint('before_loadInitialMessages') + const appState = getAppState() + const { + messages: initialMessages, + turnInterruptionState, + agentSetting: resumedAgentSetting, + } = await loadInitialMessages(setAppState, { + continue: options.continue, + teleport: options.teleport, + resume: options.resume, + resumeSessionAt: options.resumeSessionAt, + forkSession: options.forkSession, + outputFormat: options.outputFormat, + sessionStartHooksPromise: options.sessionStartHooksPromise, + restoredWorkerState: structuredIO.restoredWorkerState, + }) + + // SessionStart hooks can emit initialUserMessage — the first user turn for + // headless orchestrator sessions where stdin is empty and additionalContext + // alone (an attachment, not a turn) would leave the REPL with nothing to + // respond to. The hook promise is awaited inside loadInitialMessages, so the + // module-level pending value is set by the time we get here. + const hookInitialUserMessage = takeInitialUserMessage() + if (hookInitialUserMessage) { + structuredIO.prependUserMessage(hookInitialUserMessage) + } + + // Restore agent setting from the resumed session (if not overridden by current --agent flag + // or settings-based agent, which would already have set mainThreadAgentType in main.tsx) + if (!options.agent && !getMainThreadAgentType() && resumedAgentSetting) { + const { agentDefinition: restoredAgent } = restoreAgentFromSession( + resumedAgentSetting, + undefined, + { activeAgents: agents, allAgents: agents }, + ) + if (restoredAgent) { + setAppState(prev => ({ ...prev, agent: restoredAgent.agentType })) + // Apply the agent's system prompt for non-built-in agents (mirrors main.tsx initial --agent path) + if (!options.systemPrompt && !isBuiltInAgent(restoredAgent)) { + const agentSystemPrompt = restoredAgent.getSystemPrompt() + if (agentSystemPrompt) { + options.systemPrompt = agentSystemPrompt + } + } + // Re-persist agent setting so future resumes maintain the agent + saveAgentSetting(restoredAgent.agentType) + } + } + + // gracefulShutdownSync schedules an async shutdown and sets process.exitCode. + // If a loadInitialMessages error path triggered it, bail early to avoid + // unnecessary work while the process winds down. + if (initialMessages.length === 0 && process.exitCode !== undefined) { + return + } + + // Handle --rewind-files: restore filesystem and exit immediately + if (options.rewindFiles) { + // File history snapshots are only created for user messages, + // so we require the target to be a user message + const targetMessage = initialMessages.find( + m => m.uuid === options.rewindFiles, + ) + + if (!targetMessage || targetMessage.type !== 'user') { + process.stderr.write( + `Error: --rewind-files requires a user message UUID, but ${options.rewindFiles} is not a user message in this session\n`, + ) + gracefulShutdownSync(1) + return + } + + const currentAppState = getAppState() + const result = await handleRewindFiles( + options.rewindFiles as UUID, + currentAppState, + setAppState, + false, + ) + if (!result.canRewind) { + process.stderr.write(`Error: ${result.error || 'Unexpected error'}\n`) + gracefulShutdownSync(1) + return + } + + // Rewind complete - exit successfully + process.stdout.write( + `Files rewound to state at message ${options.rewindFiles}\n`, + ) + gracefulShutdownSync(0) + return + } + + // Check if we need input prompt - skip if we're resuming with a valid session ID/JSONL file or using SDK URL + const hasValidResumeSessionId = + typeof options.resume === 'string' && + (Boolean(validateUuid(options.resume)) || options.resume.endsWith('.jsonl')) + const isUsingSdkUrl = Boolean(options.sdkUrl) + + if (!inputPrompt && !hasValidResumeSessionId && !isUsingSdkUrl) { + process.stderr.write( + `Error: Input must be provided either through stdin or as a prompt argument when using --print\n`, + ) + gracefulShutdownSync(1) + return + } + + if (options.outputFormat === 'stream-json' && !options.verbose) { + process.stderr.write( + 'Error: When using --print, --output-format=stream-json requires --verbose\n', + ) + gracefulShutdownSync(1) + return + } + + // Filter out MCP tools that are in the deny list + const allowedMcpTools = filterToolsByDenyRules( + appState.mcp.tools, + appState.toolPermissionContext, + ) + let filteredTools = [...tools, ...allowedMcpTools] + + // When using SDK URL, always use stdio permission prompting to delegate to the SDK + const effectivePermissionPromptToolName = options.sdkUrl + ? 'stdio' + : options.permissionPromptToolName + + // Callback for when a permission prompt is shown + const onPermissionPrompt = (details: RequiresActionDetails) => { + if (feature('COMMIT_ATTRIBUTION')) { + setAppState(prev => ({ + ...prev, + attribution: { + ...prev.attribution, + permissionPromptCount: prev.attribution.permissionPromptCount + 1, + }, + })) + } + notifySessionStateChanged('requires_action', details) + } + + const canUseTool = getCanUseToolFn( + effectivePermissionPromptToolName, + structuredIO, + () => getAppState().mcp.tools, + onPermissionPrompt, + ) + if (options.permissionPromptToolName) { + // Remove the permission prompt tool from the list of available tools. + filteredTools = filteredTools.filter( + tool => !toolMatchesName(tool, options.permissionPromptToolName!), + ) + } + + // Install errors handlers to gracefully handle broken pipes (e.g., when parent process dies) + registerProcessOutputErrorHandlers() + + headlessProfilerCheckpoint('after_loadInitialMessages') + + // Ensure model strings are initialized before generating model options. + // For Bedrock users, this waits for the profile fetch to get correct region strings. + await ensureModelStringsInitialized() + headlessProfilerCheckpoint('after_modelStrings') + + // UDS inbox store registration is deferred until after `run` is defined + // so we can pass `run` as the onEnqueue callback (see below). + + // Only `json` + `verbose` needs the full array (jsonStringify(messages) below). + // For stream-json (SDK/CCR) and default text output, only the last message is + // read for the exit code / final result. Avoid accumulating every message in + // memory for the entire session. + const needsFullArray = options.outputFormat === 'json' && options.verbose + const messages: SDKMessage[] = [] + let lastMessage: SDKMessage | undefined + // Streamlined mode transforms messages when CLAUDE_CODE_STREAMLINED_OUTPUT=true and using stream-json + // Build flag gates this out of external builds; env var is the runtime opt-in for ant builds + const transformToStreamlined = + feature('STREAMLINED_OUTPUT') && + isEnvTruthy(process.env.CLAUDE_CODE_STREAMLINED_OUTPUT) && + options.outputFormat === 'stream-json' + ? createStreamlinedTransformer() + : null + + headlessProfilerCheckpoint('before_runHeadlessStreaming') + for await (const message of runHeadlessStreaming( + structuredIO, + appState.mcp.clients, + [...commands, ...appState.mcp.commands], + filteredTools, + initialMessages, + canUseTool, + sdkMcpConfigs, + getAppState, + setAppState, + agents, + options, + turnInterruptionState, + )) { + if (transformToStreamlined) { + // Streamlined mode: transform messages and stream immediately + const transformed = transformToStreamlined(message) + if (transformed) { + await structuredIO.write(transformed) + } + } else if (options.outputFormat === 'stream-json' && options.verbose) { + await structuredIO.write(message) + } + // Should not be getting control messages or stream events in non-stream mode. + // Also filter out streamlined types since they're only produced by the transformer. + // SDK-only system events are excluded so lastMessage stays at the result + // (session_state_changed(idle) and any late task_notification drain after + // result in the finally block). + if ( + message.type !== 'control_response' && + message.type !== 'control_request' && + message.type !== 'control_cancel_request' && + !( + message.type === 'system' && + (message.subtype === 'session_state_changed' || + message.subtype === 'task_notification' || + message.subtype === 'task_started' || + message.subtype === 'task_progress' || + message.subtype === 'post_turn_summary') + ) && + message.type !== 'stream_event' && + message.type !== 'keep_alive' && + message.type !== 'streamlined_text' && + message.type !== 'streamlined_tool_use_summary' && + message.type !== 'prompt_suggestion' + ) { + if (needsFullArray) { + messages.push(message) + } + lastMessage = message + } + } + + switch (options.outputFormat) { + case 'json': + if (!lastMessage || lastMessage.type !== 'result') { + throw new Error('No messages returned') + } + if (options.verbose) { + writeToStdout(jsonStringify(messages) + '\n') + break + } + writeToStdout(jsonStringify(lastMessage) + '\n') + break + case 'stream-json': + // already logged above + break + default: + if (!lastMessage || lastMessage.type !== 'result') { + throw new Error('No messages returned') + } + switch (lastMessage.subtype) { + case 'success': + writeToStdout( + lastMessage.result.endsWith('\n') + ? lastMessage.result + : lastMessage.result + '\n', + ) + break + case 'error_during_execution': + writeToStdout(`Execution error`) + break + case 'error_max_turns': + writeToStdout(`Error: Reached max turns (${options.maxTurns})`) + break + case 'error_max_budget_usd': + writeToStdout(`Error: Exceeded USD budget (${options.maxBudgetUsd})`) + break + case 'error_max_structured_output_retries': + writeToStdout( + `Error: Failed to provide valid structured output after maximum retries`, + ) + } + } + + // Log headless latency metrics for the final turn + logHeadlessProfilerTurn() + + // Drain any in-flight memory extraction before shutdown. The response is + // already flushed above, so this adds no user-visible latency — it just + // delays process exit so gracefulShutdownSync's 5s failsafe doesn't kill + // the forked agent mid-flight. Gated by isExtractModeActive so the + // tengu_slate_thimble flag controls non-interactive extraction end-to-end. + if (feature('EXTRACT_MEMORIES') && isExtractModeActive()) { + await extractMemoriesModule!.drainPendingExtraction() + } + + gracefulShutdownSync( + lastMessage?.type === 'result' && lastMessage?.is_error ? 1 : 0, + ) +} + +function runHeadlessStreaming( + structuredIO: StructuredIO, + mcpClients: MCPServerConnection[], + commands: Command[], + tools: Tools, + initialMessages: Message[], + canUseTool: CanUseToolFn, + sdkMcpConfigs: Record, + getAppState: () => AppState, + setAppState: (f: (prev: AppState) => AppState) => void, + agents: AgentDefinition[], + options: { + verbose: boolean | undefined + jsonSchema: Record | undefined + permissionPromptToolName: string | undefined + allowedTools: string[] | undefined + thinkingConfig: ThinkingConfig | undefined + maxTurns: number | undefined + maxBudgetUsd: number | undefined + taskBudget: { total: number } | undefined + systemPrompt: string | undefined + appendSystemPrompt: string | undefined + userSpecifiedModel: string | undefined + fallbackModel: string | undefined + replayUserMessages?: boolean | undefined + includePartialMessages?: boolean | undefined + enableAuthStatus?: boolean | undefined + agent?: string | undefined + setSDKStatus?: (status: SDKStatus) => void + promptSuggestions?: boolean | undefined + workload?: string | undefined + }, + turnInterruptionState?: TurnInterruptionState, +): AsyncIterable { + let running = false + let runPhase: + | 'draining_commands' + | 'waiting_for_agents' + | 'finally_flush' + | 'finally_post_flush' + | undefined + let inputClosed = false + let shutdownPromptInjected = false + let heldBackResult: StdoutMessage | null = null + let abortController: AbortController | undefined + // Same queue sendRequest() enqueues to — one FIFO for everything. + const output = structuredIO.outbound + + // Ctrl+C in -p mode: abort the in-flight query, then shut down gracefully. + // gracefulShutdown persists session state and flushes analytics, with a + // failsafe timer that force-exits if cleanup hangs. + const sigintHandler = () => { + logForDiagnosticsNoPII('info', 'shutdown_signal', { signal: 'SIGINT' }) + if (abortController && !abortController.signal.aborted) { + abortController.abort() + } + void gracefulShutdown(0) + } + process.on('SIGINT', sigintHandler) + + // Dump run()'s state at SIGTERM so a stuck session's healthsweep can name + // the do/while(waitingForAgents) poll without reading the transcript. + registerCleanup(async () => { + const bg: Record = {} + for (const t of getRunningTasks(getAppState())) { + if (isBackgroundTask(t)) bg[t.type] = (bg[t.type] ?? 0) + 1 + } + logForDiagnosticsNoPII('info', 'run_state_at_shutdown', { + run_active: running, + run_phase: runPhase, + worker_status: getSessionState(), + internal_events_pending: structuredIO.internalEventsPending, + bg_tasks: bg, + }) + }) + + // Wire the central onChangeAppState mode-diff hook to the SDK output stream. + // This fires whenever ANY code path mutates toolPermissionContext.mode — + // Shift+Tab, ExitPlanMode dialog, /plan slash command, rewind, bridge + // set_permission_mode, the query loop, stop_task — rather than the two + // paths that previously went through a bespoke wrapper. + // The wrapper's body was fully redundant (it enqueued here AND called + // notifySessionMetadataChanged, both of which onChangeAppState now covers); + // keeping it would double-emit status messages. + setPermissionModeChangedListener(newMode => { + // Only emit for SDK-exposed modes. + if ( + newMode === 'default' || + newMode === 'acceptEdits' || + newMode === 'bypassPermissions' || + newMode === 'plan' || + newMode === (feature('TRANSCRIPT_CLASSIFIER') && 'auto') || + newMode === 'dontAsk' + ) { + output.enqueue({ + type: 'system', + subtype: 'status', + status: null, + permissionMode: newMode as PermissionMode, + uuid: randomUUID(), + session_id: getSessionId(), + }) + } + }) + + // Prompt suggestion tracking (push model) + const suggestionState: { + abortController: AbortController | null + inflightPromise: Promise | null + lastEmitted: { + text: string + emittedAt: number + promptId: PromptVariant + generationRequestId: string | null + } | null + pendingSuggestion: { + type: 'prompt_suggestion' + suggestion: string + uuid: UUID + session_id: string + } | null + pendingLastEmittedEntry: { + text: string + promptId: PromptVariant + generationRequestId: string | null + } | null + } = { + abortController: null, + inflightPromise: null, + lastEmitted: null, + pendingSuggestion: null, + pendingLastEmittedEntry: null, + } + + // Set up AWS auth status listener if enabled + let unsubscribeAuthStatus: (() => void) | undefined + if (options.enableAuthStatus) { + const authStatusManager = AwsAuthStatusManager.getInstance() + unsubscribeAuthStatus = authStatusManager.subscribe(status => { + output.enqueue({ + type: 'auth_status', + isAuthenticating: status.isAuthenticating, + output: status.output, + error: status.error, + uuid: randomUUID(), + session_id: getSessionId(), + }) + }) + } + + // Set up rate limit status listener to emit SDKRateLimitEvent for all status changes. + // Emitting for all statuses (including 'allowed') ensures consumers can clear warnings + // when rate limits reset. The upstream emitStatusChange already deduplicates via isEqual. + const rateLimitListener = (limits: ClaudeAILimits) => { + const rateLimitInfo = toSDKRateLimitInfo(limits) + if (rateLimitInfo) { + output.enqueue({ + type: 'rate_limit_event', + rate_limit_info: rateLimitInfo, + uuid: randomUUID(), + session_id: getSessionId(), + }) + } + } + statusListeners.add(rateLimitListener) + + // Messages for internal tracking, directly mutated by ask(). These messages + // include Assistant, User, Attachment, and Progress messages. + // TODO: Clean up this code to avoid passing around a mutable array. + const mutableMessages: Message[] = initialMessages + + // Seed the readFileState cache from the transcript (content the model saw, + // with message timestamps) so getChangedFiles can detect external edits. + // This cache instance must persist across ask() calls, since the edit tool + // relies on this as a global state. + let readFileState = extractReadFilesFromMessages( + initialMessages, + cwd(), + READ_FILE_STATE_CACHE_SIZE, + ) + + // Client-supplied readFileState seeds (via seed_read_state control request). + // The stdin IIFE runs concurrently with ask() — a seed arriving mid-turn + // would be lost to ask()'s clone-then-replace (QueryEngine.ts finally block) + // if written directly into readFileState. Instead, seeds land here, merge + // into getReadFileCache's view (readFileState-wins-ties: seeds fill gaps), + // and are re-applied then CLEARED in setReadFileCache. One-shot: each seed + // survives exactly one clone-replace cycle, then becomes a regular + // readFileState entry subject to compact's clear like everything else. + const pendingSeeds = createFileStateCacheWithSizeLimit( + READ_FILE_STATE_CACHE_SIZE, + ) + + // Auto-resume interrupted turns on restart so CC continues from where it + // left off without requiring the SDK to re-send the prompt. + const resumeInterruptedTurnEnv = + process.env.CLAUDE_CODE_RESUME_INTERRUPTED_TURN + if ( + turnInterruptionState && + turnInterruptionState.kind !== 'none' && + resumeInterruptedTurnEnv + ) { + logForDebugging( + `[print.ts] Auto-resuming interrupted turn (kind: ${turnInterruptionState.kind})`, + ) + + // Remove the interrupted message and its sentinel, then re-enqueue so + // the model sees it exactly once. For mid-turn interruptions, the + // deserialization layer transforms them into interrupted_prompt by + // appending a synthetic "Continue from where you left off." message. + removeInterruptedMessage(mutableMessages, turnInterruptionState.message) + enqueue({ + mode: 'prompt', + value: turnInterruptionState.message.message.content, + uuid: randomUUID(), + }) + } + + const modelOptions = getModelOptions() + const modelInfos = modelOptions.map(option => { + const modelId = option.value === null ? 'default' : option.value + const resolvedModel = + modelId === 'default' + ? getDefaultMainLoopModel() + : parseUserSpecifiedModel(modelId) + const hasEffort = modelSupportsEffort(resolvedModel) + const hasAdaptiveThinking = modelSupportsAdaptiveThinking(resolvedModel) + const hasFastMode = isFastModeSupportedByModel(option.value) + const hasAutoMode = modelSupportsAutoMode(resolvedModel) + return { + value: modelId, + displayName: option.label, + description: option.description, + ...(hasEffort && { + supportsEffort: true, + supportedEffortLevels: modelSupportsMaxEffort(resolvedModel) + ? [...EFFORT_LEVELS] + : EFFORT_LEVELS.filter(l => l !== 'max'), + }), + ...(hasAdaptiveThinking && { supportsAdaptiveThinking: true }), + ...(hasFastMode && { supportsFastMode: true }), + ...(hasAutoMode && { supportsAutoMode: true }), + } + }) + let activeUserSpecifiedModel = options.userSpecifiedModel + + function injectModelSwitchBreadcrumbs( + modelArg: string, + resolvedModel: string, + ): void { + const breadcrumbs = createModelSwitchBreadcrumbs( + modelArg, + modelDisplayString(resolvedModel), + ) + mutableMessages.push(...breadcrumbs) + for (const crumb of breadcrumbs) { + if ( + typeof crumb.message.content === 'string' && + crumb.message.content.includes(`<${LOCAL_COMMAND_STDOUT_TAG}>`) + ) { + output.enqueue({ + type: 'user', + message: crumb.message, + session_id: getSessionId(), + parent_tool_use_id: null, + uuid: crumb.uuid, + timestamp: crumb.timestamp, + isReplay: true, + } satisfies SDKUserMessageReplay) + } + } + } + + // Cache SDK MCP clients to avoid reconnecting on each run + let sdkClients: MCPServerConnection[] = [] + let sdkTools: Tools = [] + + // Track which MCP clients have had elicitation handlers registered + const elicitationRegistered = new Set() + + /** + * Register elicitation request/completion handlers on connected MCP clients + * that haven't been registered yet. SDK MCP servers are excluded because they + * route through SdkControlClientTransport. Hooks run first (matching REPL + * behavior); if no hook responds, the request is forwarded to the SDK + * consumer via the control protocol. + */ + function registerElicitationHandlers(clients: MCPServerConnection[]): void { + for (const connection of clients) { + if ( + connection.type !== 'connected' || + elicitationRegistered.has(connection.name) + ) { + continue + } + // Skip SDK MCP servers — elicitation flows through SdkControlClientTransport + if (connection.config.type === 'sdk') { + continue + } + const serverName = connection.name + + // Wrapped in try/catch because setRequestHandler throws if the client wasn't + // created with elicitation capability declared (e.g., SDK-created clients). + try { + connection.client.setRequestHandler( + ElicitRequestSchema, + async (request, extra) => { + logMCPDebug( + serverName, + `Elicitation request received in print mode: ${jsonStringify(request)}`, + ) + + const mode = request.params.mode === 'url' ? 'url' : 'form' + + logEvent('tengu_mcp_elicitation_shown', { + mode: mode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + // Run elicitation hooks first — they can provide a response programmatically + const hookResponse = await runElicitationHooks( + serverName, + request.params, + extra.signal, + ) + if (hookResponse) { + logMCPDebug( + serverName, + `Elicitation resolved by hook: ${jsonStringify(hookResponse)}`, + ) + logEvent('tengu_mcp_elicitation_response', { + mode: mode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + action: + hookResponse.action as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + return hookResponse + } + + // Delegate to SDK consumer via control protocol + const url = + 'url' in request.params + ? (request.params.url as string) + : undefined + const requestedSchema = + 'requestedSchema' in request.params + ? (request.params.requestedSchema as + | Record + | undefined) + : undefined + + const elicitationId = + 'elicitationId' in request.params + ? (request.params.elicitationId as string | undefined) + : undefined + + const rawResult = await structuredIO.handleElicitation( + serverName, + request.params.message, + requestedSchema, + extra.signal, + mode, + url, + elicitationId, + ) + + const result = await runElicitationResultHooks( + serverName, + rawResult, + extra.signal, + mode, + elicitationId, + ) + + logEvent('tengu_mcp_elicitation_response', { + mode: mode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + action: + result.action as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + return result + }, + ) + + // Surface completion notifications to SDK consumers (URL mode) + connection.client.setNotificationHandler( + ElicitationCompleteNotificationSchema, + notification => { + const { elicitationId } = notification.params + logMCPDebug( + serverName, + `Elicitation completion notification: ${elicitationId}`, + ) + void executeNotificationHooks({ + message: `MCP server "${serverName}" confirmed elicitation ${elicitationId} complete`, + notificationType: 'elicitation_complete', + }) + output.enqueue({ + type: 'system', + subtype: 'elicitation_complete', + mcp_server_name: serverName, + elicitation_id: elicitationId, + uuid: randomUUID(), + session_id: getSessionId(), + }) + }, + ) + + elicitationRegistered.add(serverName) + } catch { + // setRequestHandler throws if the client wasn't created with + // elicitation capability — skip silently + } + } + } + + async function updateSdkMcp() { + // Check if SDK MCP servers need to be updated (new servers added or removed) + const currentServerNames = new Set(Object.keys(sdkMcpConfigs)) + const connectedServerNames = new Set(sdkClients.map(c => c.name)) + + // Check if there are any differences (additions or removals) + const hasNewServers = Array.from(currentServerNames).some( + name => !connectedServerNames.has(name), + ) + const hasRemovedServers = Array.from(connectedServerNames).some( + name => !currentServerNames.has(name), + ) + // Check if any SDK clients are pending and need to be upgraded + const hasPendingSdkClients = sdkClients.some(c => c.type === 'pending') + // Check if any SDK clients failed their handshake and need to be retried. + // Without this, a client that lands in 'failed' (e.g. handshake timeout on + // a WS reconnect race) stays failed forever — its name satisfies the + // connectedServerNames diff but it contributes zero tools. + const hasFailedSdkClients = sdkClients.some(c => c.type === 'failed') + + const haveServersChanged = + hasNewServers || + hasRemovedServers || + hasPendingSdkClients || + hasFailedSdkClients + + if (haveServersChanged) { + // Clean up removed servers + for (const client of sdkClients) { + if (!currentServerNames.has(client.name)) { + if (client.type === 'connected') { + await client.cleanup() + } + } + } + + // Re-initialize all SDK MCP servers with current config + const sdkSetup = await setupSdkMcpClients( + sdkMcpConfigs, + (serverName, message) => + structuredIO.sendMcpMessage(serverName, message), + ) + sdkClients = sdkSetup.clients + sdkTools = sdkSetup.tools + + // Store SDK MCP tools in appState so subagents can access them via + // assembleToolPool. Only tools are stored here — SDK clients are already + // merged separately in the query loop (allMcpClients) and mcp_status handler. + // Use both old (connectedServerNames) and new (currentServerNames) to remove + // stale SDK tools when servers are added or removed. + const allSdkNames = uniq([...connectedServerNames, ...currentServerNames]) + setAppState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + tools: [ + ...prev.mcp.tools.filter( + t => + !allSdkNames.some(name => + t.name.startsWith(getMcpPrefix(name)), + ), + ), + ...sdkTools, + ], + }, + })) + + // Set up the special internal VSCode MCP server if necessary. + setupVscodeSdkMcp(sdkClients) + } + } + + void updateSdkMcp() + + // State for dynamically added MCP servers (via mcp_set_servers control message) + // These are separate from SDK MCP servers and support all transport types + let dynamicMcpState: DynamicMcpState = { + clients: [], + tools: [], + configs: {}, + } + + // Shared tool assembly for ask() and the get_context_usage control request. + // Closes over the mutable sdkTools/dynamicMcpState bindings so both call + // sites see late-connecting servers. + const buildAllTools = (appState: AppState): Tools => { + const assembledTools = assembleToolPool( + appState.toolPermissionContext, + appState.mcp.tools, + ) + let allTools = uniqBy( + mergeAndFilterTools( + [...tools, ...sdkTools, ...dynamicMcpState.tools], + assembledTools, + appState.toolPermissionContext.mode, + ), + 'name', + ) + if (options.permissionPromptToolName) { + allTools = allTools.filter( + tool => !toolMatchesName(tool, options.permissionPromptToolName!), + ) + } + const initJsonSchema = getInitJsonSchema() + if (initJsonSchema && !options.jsonSchema) { + const syntheticOutputResult = createSyntheticOutputTool(initJsonSchema) + if ('tool' in syntheticOutputResult) { + allTools = [...allTools, syntheticOutputResult.tool] + } + } + return allTools + } + + // Bridge handle for remote-control (SDK control message). + // Mirrors the REPL's useReplBridge hook: the handle is created when + // `remote_control` is enabled and torn down when disabled. + let bridgeHandle: ReplBridgeHandle | null = null + // Cursor into mutableMessages — tracks how far we've forwarded. + // Same index-based diff as useReplBridge's lastWrittenIndexRef. + let bridgeLastForwardedIndex = 0 + + // Forward new messages from mutableMessages to the bridge. + // Called incrementally during each turn (so claude.ai sees progress + // and stays alive during permission waits) and again after the turn. + // + // writeMessages has its own UUID-based dedup (initialMessageUUIDs, + // recentPostedUUIDs) — the index cursor here is a pre-filter to avoid + // O(n) re-scanning of already-sent messages on every call. + function forwardMessagesToBridge(): void { + if (!bridgeHandle) return + // Guard against mutableMessages shrinking (compaction truncates it). + const startIndex = Math.min( + bridgeLastForwardedIndex, + mutableMessages.length, + ) + const newMessages = mutableMessages + .slice(startIndex) + .filter(m => m.type === 'user' || m.type === 'assistant') + bridgeLastForwardedIndex = mutableMessages.length + if (newMessages.length > 0) { + bridgeHandle.writeMessages(newMessages) + } + } + + // Helper to apply MCP server changes - used by both mcp_set_servers control message + // and background plugin installation. + // NOTE: Nested function required - mutates closure state (sdkMcpConfigs, sdkClients, etc.) + let mcpChangesPromise: Promise<{ + response: SDKControlMcpSetServersResponse + sdkServersChanged: boolean + }> = Promise.resolve({ + response: { + added: [] as string[], + removed: [] as string[], + errors: {} as Record, + }, + sdkServersChanged: false, + }) + + function applyMcpServerChanges( + servers: Record, + ): Promise<{ + response: SDKControlMcpSetServersResponse + sdkServersChanged: boolean + }> { + // Serialize calls to prevent race conditions between concurrent callers + // (background plugin install and mcp_set_servers control messages) + const doWork = async (): Promise<{ + response: SDKControlMcpSetServersResponse + sdkServersChanged: boolean + }> => { + const oldSdkClientNames = new Set(sdkClients.map(c => c.name)) + + const result = await handleMcpSetServers( + servers, + { configs: sdkMcpConfigs, clients: sdkClients, tools: sdkTools }, + dynamicMcpState, + setAppState, + ) + + // Update SDK state (need to mutate sdkMcpConfigs since it's shared) + for (const key of Object.keys(sdkMcpConfigs)) { + delete sdkMcpConfigs[key] + } + Object.assign(sdkMcpConfigs, result.newSdkState.configs) + sdkClients = result.newSdkState.clients + sdkTools = result.newSdkState.tools + dynamicMcpState = result.newDynamicState + + // Keep appState.mcp.tools in sync so subagents can see SDK MCP tools. + // Use both old and new SDK client names to remove stale tools. + if (result.sdkServersChanged) { + const newSdkClientNames = new Set(sdkClients.map(c => c.name)) + const allSdkNames = uniq([...oldSdkClientNames, ...newSdkClientNames]) + setAppState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + tools: [ + ...prev.mcp.tools.filter( + t => + !allSdkNames.some(name => + t.name.startsWith(getMcpPrefix(name)), + ), + ), + ...sdkTools, + ], + }, + })) + } + + return { + response: result.response, + sdkServersChanged: result.sdkServersChanged, + } + } + + mcpChangesPromise = mcpChangesPromise.then(doWork, doWork) + return mcpChangesPromise + } + + // Build McpServerStatus[] for control responses. Shared by mcp_status and + // reload_plugins handlers. Reads closure state: sdkClients, dynamicMcpState. + function buildMcpServerStatuses(): McpServerStatus[] { + const currentAppState = getAppState() + const currentMcpClients = currentAppState.mcp.clients + const allMcpTools = uniqBy( + [...currentAppState.mcp.tools, ...dynamicMcpState.tools], + 'name', + ) + const existingNames = new Set([ + ...currentMcpClients.map(c => c.name), + ...sdkClients.map(c => c.name), + ]) + return [ + ...currentMcpClients, + ...sdkClients, + ...dynamicMcpState.clients.filter(c => !existingNames.has(c.name)), + ].map(connection => { + let config + if ( + connection.config.type === 'sse' || + connection.config.type === 'http' + ) { + config = { + type: connection.config.type, + url: connection.config.url, + headers: connection.config.headers, + oauth: connection.config.oauth, + } + } else if (connection.config.type === 'claudeai-proxy') { + config = { + type: 'claudeai-proxy' as const, + url: connection.config.url, + id: connection.config.id, + } + } else if ( + connection.config.type === 'stdio' || + connection.config.type === undefined + ) { + config = { + type: 'stdio' as const, + command: connection.config.command, + args: connection.config.args, + } + } + const serverTools = + connection.type === 'connected' + ? filterToolsByServer(allMcpTools, connection.name).map(tool => ({ + name: tool.mcpInfo?.toolName ?? tool.name, + annotations: { + readOnly: tool.isReadOnly({}) || undefined, + destructive: tool.isDestructive?.({}) || undefined, + openWorld: tool.isOpenWorld?.({}) || undefined, + }, + })) + : undefined + // Capabilities passthrough with allowlist pre-filter. The IDE reads + // experimental['claude/channel'] to decide whether to show the + // Enable-channel prompt — only echo it if channel_enable would + // actually pass the allowlist. Not a security boundary (the + // handler re-runs the full gate); just avoids dead buttons. + let capabilities: { experimental?: Record } | undefined + if ( + (feature('KAIROS') || feature('KAIROS_CHANNELS')) && + connection.type === 'connected' && + connection.capabilities.experimental + ) { + const exp = { ...connection.capabilities.experimental } + if ( + exp['claude/channel'] && + (!isChannelsEnabled() || + !isChannelAllowlisted(connection.config.pluginSource)) + ) { + delete exp['claude/channel'] + } + if (Object.keys(exp).length > 0) { + capabilities = { experimental: exp } + } + } + return { + name: connection.name, + status: connection.type, + serverInfo: + connection.type === 'connected' ? connection.serverInfo : undefined, + error: connection.type === 'failed' ? connection.error : undefined, + config, + scope: connection.config.scope, + tools: serverTools, + capabilities, + } + }) + } + + // NOTE: Nested function required - needs closure access to applyMcpServerChanges and updateSdkMcp + async function installPluginsAndApplyMcpInBackground(): Promise { + try { + // Join point for user settings (fired at runHeadless entry) and managed + // settings (fired in main.tsx preAction). downloadUserSettings() caches + // its promise so this awaits the same in-flight request. + await Promise.all([ + feature('DOWNLOAD_USER_SETTINGS') && + (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) || getIsRemoteMode()) + ? withDiagnosticsTiming('headless_user_settings_download', () => + downloadUserSettings(), + ) + : Promise.resolve(), + withDiagnosticsTiming('headless_managed_settings_wait', () => + waitForRemoteManagedSettingsToLoad(), + ), + ]) + + const pluginsInstalled = await installPluginsForHeadless() + + if (pluginsInstalled) { + await applyPluginMcpDiff() + } + } catch (error) { + logError(error) + } + } + + // Background plugin installation for all headless users + // Installs marketplaces from extraKnownMarketplaces and missing enabled plugins + // CLAUDE_CODE_SYNC_PLUGIN_INSTALL=true: resolved in run() before the first + // query so plugins are guaranteed available on the first ask(). + let pluginInstallPromise: Promise | null = null + // --bare / SIMPLE: skip plugin install. Scripted calls don't add plugins + // mid-session; the next interactive run reconciles. + if (!isBareMode()) { + if (isEnvTruthy(process.env.CLAUDE_CODE_SYNC_PLUGIN_INSTALL)) { + pluginInstallPromise = installPluginsAndApplyMcpInBackground() + } else { + void installPluginsAndApplyMcpInBackground() + } + } + + // Idle timeout management + const idleTimeout = createIdleTimeoutManager(() => !running) + + // Mutable commands and agents for hot reloading + let currentCommands = commands + let currentAgents = agents + + // Clear all plugin-related caches, reload commands/agents/hooks. + // Called after CLAUDE_CODE_SYNC_PLUGIN_INSTALL completes (before first query) + // and after non-sync background install finishes. + // refreshActivePlugins calls clearAllCaches() which is required because + // loadAllPlugins() may have run during main.tsx startup BEFORE managed + // settings were fetched. Without clearing, getCommands() would rebuild + // from a stale plugin list. + async function refreshPluginState(): Promise { + // refreshActivePlugins handles the full cache sweep (clearAllCaches), + // reloads all plugin component loaders, writes AppState.plugins + + // AppState.agentDefinitions, registers hooks, and bumps mcp.pluginReconnectKey. + const { agentDefinitions: freshAgentDefs } = + await refreshActivePlugins(setAppState) + + // Headless-specific: currentCommands/currentAgents are local mutable refs + // captured by the query loop (REPL uses AppState instead). getCommands is + // fresh because refreshActivePlugins cleared its cache. + currentCommands = await getCommands(cwd()) + + // Preserve SDK-provided agents (--agents CLI flag or SDK initialize + // control_request) — both inject via parseAgentsFromJson with + // source='flagSettings'. loadMarkdownFilesForSubdir never assigns this + // source, so it cleanly discriminates "injected, not disk-loadable". + // + // The previous filter used a negative set-diff (!freshAgentTypes.has(a)) + // which also matched plugin agents that were in the poisoned initial + // currentAgents but correctly excluded from freshAgentDefs after managed + // settings applied — leaking policy-blocked agents into the init message. + // See gh-23085: isBridgeEnabled() at Commander-definition time poisoned + // the settings cache before setEligibility(true) ran. + const sdkAgents = currentAgents.filter(a => a.source === 'flagSettings') + currentAgents = [...freshAgentDefs.allAgents, ...sdkAgents] + } + + // Re-diff MCP configs after plugin state changes. Filters to + // process-transport-supported types and carries SDK-mode servers through + // so applyMcpServerChanges' diff doesn't close their transports. + // Nested: needs closure access to sdkMcpConfigs, applyMcpServerChanges, + // updateSdkMcp. + async function applyPluginMcpDiff(): Promise { + const { servers: newConfigs } = await getAllMcpConfigs() + const supportedConfigs: Record = + {} + for (const [name, config] of Object.entries(newConfigs)) { + const type = config.type + if ( + type === undefined || + type === 'stdio' || + type === 'sse' || + type === 'http' || + type === 'sdk' + ) { + supportedConfigs[name] = config + } + } + for (const [name, config] of Object.entries(sdkMcpConfigs)) { + if (config.type === 'sdk' && !(name in supportedConfigs)) { + supportedConfigs[name] = config + } + } + const { response, sdkServersChanged } = + await applyMcpServerChanges(supportedConfigs) + if (sdkServersChanged) { + void updateSdkMcp() + } + logForDebugging( + `Headless MCP refresh: added=${response.added.length}, removed=${response.removed.length}`, + ) + } + + // Subscribe to skill changes for hot reloading + const unsubscribeSkillChanges = skillChangeDetector.subscribe(() => { + clearCommandsCache() + void getCommands(cwd()).then(newCommands => { + currentCommands = newCommands + }) + }) + + // Proactive mode: schedule a tick to keep the model looping autonomously. + // setTimeout(0) yields to the event loop so pending stdin messages + // (interrupts, user messages) are processed before the tick fires. + const scheduleProactiveTick = + feature('PROACTIVE') || feature('KAIROS') + ? () => { + setTimeout(() => { + if ( + !proactiveModule?.isProactiveActive() || + proactiveModule.isProactivePaused() || + inputClosed + ) { + return + } + const tickContent = `<${TICK_TAG}>${new Date().toLocaleTimeString()}` + enqueue({ + mode: 'prompt' as const, + value: tickContent, + uuid: randomUUID(), + priority: 'later', + isMeta: true, + }) + void run() + }, 0) + } + : undefined + + // Abort the current operation when a 'now' priority message arrives. + subscribeToCommandQueue(() => { + if (abortController && getCommandsByMaxPriority('now').length > 0) { + abortController.abort('interrupt') + } + }) + + const run = async () => { + if (running) { + return + } + + running = true + runPhase = undefined + notifySessionStateChanged('running') + idleTimeout.stop() + + headlessProfilerCheckpoint('run_entry') + // TODO(custom-tool-refactor): Should move to the init message, like browser + + await updateSdkMcp() + headlessProfilerCheckpoint('after_updateSdkMcp') + + // Resolve deferred plugin installation (CLAUDE_CODE_SYNC_PLUGIN_INSTALL). + // The promise was started eagerly so installation overlaps with other init. + // Awaiting here guarantees plugins are available before the first ask(). + // If CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS is set, races against that + // deadline and proceeds without plugins on timeout (logging an error). + if (pluginInstallPromise) { + const timeoutMs = parseInt( + process.env.CLAUDE_CODE_SYNC_PLUGIN_INSTALL_TIMEOUT_MS || '', + 10, + ) + if (timeoutMs > 0) { + const timeout = sleep(timeoutMs).then(() => 'timeout' as const) + const result = await Promise.race([pluginInstallPromise, timeout]) + if (result === 'timeout') { + logError( + new Error( + `CLAUDE_CODE_SYNC_PLUGIN_INSTALL: plugin installation timed out after ${timeoutMs}ms`, + ), + ) + logEvent('tengu_sync_plugin_install_timeout', { + timeout_ms: timeoutMs, + }) + } + } else { + await pluginInstallPromise + } + pluginInstallPromise = null + + // Refresh commands, agents, and hooks now that plugins are installed + await refreshPluginState() + + // Set up hot-reload for plugin hooks now that the initial install is done. + // In sync-install mode, setup.ts skips this to avoid racing with the install. + const { setupPluginHookHotReload } = await import( + '../utils/plugins/loadPluginHooks.js' + ) + setupPluginHookHotReload() + } + + // Only main-thread commands (agentId===undefined) — subagent + // notifications are drained by the subagent's mid-turn gate in query.ts. + // Defined outside the try block so it's accessible in the post-finally + // queue re-checks at the bottom of run(). + const isMainThread = (cmd: QueuedCommand) => cmd.agentId === undefined + + try { + let command: QueuedCommand | undefined + let waitingForAgents = false + + // Extract command processing into a named function for the do-while pattern. + // Drains the queue, batching consecutive prompt-mode commands into one + // ask() call so messages that queued up during a long turn coalesce + // into a single follow-up turn instead of N separate turns. + const drainCommandQueue = async () => { + while ((command = dequeue(isMainThread))) { + if ( + command.mode !== 'prompt' && + command.mode !== 'orphaned-permission' && + command.mode !== 'task-notification' + ) { + throw new Error( + 'only prompt commands are supported in streaming mode', + ) + } + + // Non-prompt commands (task-notification, orphaned-permission) carry + // side effects or orphanedPermission state, so they process singly. + // Prompt commands greedily collect followers with matching workload. + const batch: QueuedCommand[] = [command] + if (command.mode === 'prompt') { + while (canBatchWith(command, peek(isMainThread))) { + batch.push(dequeue(isMainThread)!) + } + if (batch.length > 1) { + command = { + ...command, + value: joinPromptValues(batch.map(c => c.value)), + uuid: batch.findLast(c => c.uuid)?.uuid ?? command.uuid, + } + } + } + const batchUuids = batch.map(c => c.uuid).filter(u => u !== undefined) + + // QueryEngine will emit a replay for command.uuid (the last uuid in + // the batch) via its messagesToAck path. Emit replays here for the + // rest so consumers that track per-uuid delivery (clank's + // asyncMessages footer, CCR) see an ack for every message they sent, + // not just the one that survived the merge. + if (options.replayUserMessages && batch.length > 1) { + for (const c of batch) { + if (c.uuid && c.uuid !== command.uuid) { + output.enqueue({ + type: 'user', + message: { role: 'user', content: c.value }, + session_id: getSessionId(), + parent_tool_use_id: null, + uuid: c.uuid, + isReplay: true, + } satisfies SDKUserMessageReplay) + } + } + } + + // Combine all MCP clients. appState.mcp is populated incrementally + // per-server by main.tsx (mirrors useManageMCPConnections). Reading + // fresh per-command means late-connecting servers are visible on the + // next turn. registerElicitationHandlers is idempotent (tracking set). + const appState = getAppState() + const allMcpClients = [ + ...appState.mcp.clients, + ...sdkClients, + ...dynamicMcpState.clients, + ] + registerElicitationHandlers(allMcpClients) + // Channel handlers for servers allowlisted via --channels at + // construction time (or enableChannel() mid-session). Runs every + // turn like registerElicitationHandlers — idempotent per-client + // (setNotificationHandler replaces, not stacks) and no-ops for + // non-allowlisted servers (one feature-flag check). + for (const client of allMcpClients) { + reregisterChannelHandlerAfterReconnect(client) + } + + const allTools = buildAllTools(appState) + + for (const uuid of batchUuids) { + notifyCommandLifecycle(uuid, 'started') + } + + // Task notifications arrive when background agents complete. + // Emit an SDK system event for SDK consumers, then fall through + // to ask() so the model sees the agent result and can act on it. + // This matches TUI behavior where useQueueProcessor always feeds + // notifications to the model regardless of coordinator mode. + if (command.mode === 'task-notification') { + const notificationText = + typeof command.value === 'string' ? command.value : '' + // Parse the XML-formatted notification + const taskIdMatch = notificationText.match( + /([^<]+)<\/task-id>/, + ) + const toolUseIdMatch = notificationText.match( + /([^<]+)<\/tool-use-id>/, + ) + const outputFileMatch = notificationText.match( + /([^<]+)<\/output-file>/, + ) + const statusMatch = notificationText.match( + /([^<]+)<\/status>/, + ) + const summaryMatch = notificationText.match( + /

([^<]+)<\/summary>/, + ) + + const isValidStatus = ( + s: string | undefined, + ): s is 'completed' | 'failed' | 'stopped' | 'killed' => + s === 'completed' || + s === 'failed' || + s === 'stopped' || + s === 'killed' + const rawStatus = statusMatch?.[1] + const status = isValidStatus(rawStatus) + ? rawStatus === 'killed' + ? 'stopped' + : rawStatus + : 'completed' + + const usageMatch = notificationText.match( + /([\s\S]*?)<\/usage>/, + ) + const usageContent = usageMatch?.[1] ?? '' + const totalTokensMatch = usageContent.match( + /(\d+)<\/total_tokens>/, + ) + const toolUsesMatch = usageContent.match( + /(\d+)<\/tool_uses>/, + ) + const durationMsMatch = usageContent.match( + /(\d+)<\/duration_ms>/, + ) + + // Only emit a task_notification SDK event when a tag is + // present — that means this is a terminal notification (completed/ + // failed/stopped). Stream events from enqueueStreamEvent carry no + // (they're progress pings); emitting them here would + // default to 'completed' and falsely close the task for SDK + // consumers. Terminal bookends are now emitted directly via + // emitTaskTerminatedSdk, so skipping statusless events is safe. + if (statusMatch) { + output.enqueue({ + type: 'system', + subtype: 'task_notification', + task_id: taskIdMatch?.[1] ?? '', + tool_use_id: toolUseIdMatch?.[1], + status, + output_file: outputFileMatch?.[1] ?? '', + summary: summaryMatch?.[1] ?? '', + usage: + totalTokensMatch && toolUsesMatch + ? { + total_tokens: parseInt(totalTokensMatch[1]!, 10), + tool_uses: parseInt(toolUsesMatch[1]!, 10), + duration_ms: durationMsMatch + ? parseInt(durationMsMatch[1]!, 10) + : 0, + } + : undefined, + session_id: getSessionId(), + uuid: randomUUID(), + }) + } + // No continue -- fall through to ask() so the model processes the result + } + + const input = command.value + + if (structuredIO instanceof RemoteIO && command.mode === 'prompt') { + logEvent('tengu_bridge_message_received', { + is_repl: false, + }) + } + + // Abort any in-flight suggestion generation and track acceptance + suggestionState.abortController?.abort() + suggestionState.abortController = null + suggestionState.pendingSuggestion = null + suggestionState.pendingLastEmittedEntry = null + if (suggestionState.lastEmitted) { + if (command.mode === 'prompt') { + // SDK user messages enqueue ContentBlockParam[], not a plain string + const inputText = + typeof input === 'string' + ? input + : ( + input.find(b => b.type === 'text') as + | { type: 'text'; text: string } + | undefined + )?.text + if (typeof inputText === 'string') { + logSuggestionOutcome( + suggestionState.lastEmitted.text, + inputText, + suggestionState.lastEmitted.emittedAt, + suggestionState.lastEmitted.promptId, + suggestionState.lastEmitted.generationRequestId, + ) + } + suggestionState.lastEmitted = null + } + } + + abortController = createAbortController() + const turnStartTime = feature('FILE_PERSISTENCE') + ? Date.now() + : undefined + + headlessProfilerCheckpoint('before_ask') + startQueryProfile() + // Per-iteration ALS context so bg agents spawned inside ask() + // inherit workload across their detached awaits. In-process cron + // stamps cmd.workload; the SDK --workload flag is options.workload. + // const-capture: TS loses `while ((command = dequeue()))` narrowing + // inside the closure. + const cmd = command + await runWithWorkload(cmd.workload ?? options.workload, async () => { + for await (const message of ask({ + commands: uniqBy( + [...currentCommands, ...appState.mcp.commands], + 'name', + ), + prompt: input, + promptUuid: cmd.uuid, + isMeta: cmd.isMeta, + cwd: cwd(), + tools: allTools, + verbose: options.verbose, + mcpClients: allMcpClients, + thinkingConfig: options.thinkingConfig, + maxTurns: options.maxTurns, + maxBudgetUsd: options.maxBudgetUsd, + taskBudget: options.taskBudget, + canUseTool, + userSpecifiedModel: activeUserSpecifiedModel, + fallbackModel: options.fallbackModel, + jsonSchema: getInitJsonSchema() ?? options.jsonSchema, + mutableMessages, + getReadFileCache: () => + pendingSeeds.size === 0 + ? readFileState + : mergeFileStateCaches(readFileState, pendingSeeds), + setReadFileCache: cache => { + readFileState = cache + for (const [path, seed] of pendingSeeds.entries()) { + const existing = readFileState.get(path) + if (!existing || seed.timestamp > existing.timestamp) { + readFileState.set(path, seed) + } + } + pendingSeeds.clear() + }, + customSystemPrompt: options.systemPrompt, + appendSystemPrompt: options.appendSystemPrompt, + getAppState, + setAppState, + abortController, + replayUserMessages: options.replayUserMessages, + includePartialMessages: options.includePartialMessages, + handleElicitation: (serverName, params, elicitSignal) => + structuredIO.handleElicitation( + serverName, + params.message, + undefined, + elicitSignal, + params.mode, + params.url, + 'elicitationId' in params ? params.elicitationId : undefined, + ), + agents: currentAgents, + orphanedPermission: cmd.orphanedPermission, + setSDKStatus: status => { + output.enqueue({ + type: 'system', + subtype: 'status', + status, + session_id: getSessionId(), + uuid: randomUUID(), + }) + }, + })) { + // Forward messages to bridge incrementally (mid-turn) so + // claude.ai sees progress and the connection stays alive + // while blocked on permission requests. + forwardMessagesToBridge() + + if (message.type === 'result') { + // Flush pending SDK events so they appear before result on the stream. + for (const event of drainSdkEvents()) { + output.enqueue(event) + } + + // Hold-back: don't emit result while background agents are running + const currentState = getAppState() + if ( + getRunningTasks(currentState).some( + t => + (t.type === 'local_agent' || + t.type === 'local_workflow') && + isBackgroundTask(t), + ) + ) { + heldBackResult = message + } else { + heldBackResult = null + output.enqueue(message) + } + } else { + // Flush SDK events (task_started, task_progress) so background + // agent progress is streamed in real-time, not batched until result. + for (const event of drainSdkEvents()) { + output.enqueue(event) + } + output.enqueue(message) + } + } + }) // end runWithWorkload + + for (const uuid of batchUuids) { + notifyCommandLifecycle(uuid, 'completed') + } + + // Forward messages to bridge after each turn + forwardMessagesToBridge() + bridgeHandle?.sendResult() + + if (feature('FILE_PERSISTENCE') && turnStartTime !== undefined) { + void executeFilePersistence( + turnStartTime, + abortController.signal, + result => { + output.enqueue({ + type: 'system' as const, + subtype: 'files_persisted' as const, + files: result.files, + failed: result.failed, + processed_at: new Date().toISOString(), + uuid: randomUUID(), + session_id: getSessionId(), + }) + }, + ) + } + + // Generate and emit prompt suggestion for SDK consumers + if ( + options.promptSuggestions && + !isEnvDefinedFalsy(process.env.CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION) + ) { + // TS narrows suggestionState to never in the while loop body; + // cast via unknown to reset narrowing. + const state = suggestionState as unknown as typeof suggestionState + state.abortController?.abort() + const localAbort = new AbortController() + suggestionState.abortController = localAbort + + const cacheSafeParams = getLastCacheSafeParams() + if (!cacheSafeParams) { + logSuggestionSuppressed( + 'sdk_no_params', + undefined, + undefined, + 'sdk', + ) + } else { + // Use a ref object so the IIFE's finally can compare against its own + // promise without a self-reference (which upsets TypeScript's flow analysis). + const ref: { promise: Promise | null } = { promise: null } + ref.promise = (async () => { + try { + const result = await tryGenerateSuggestion( + localAbort, + mutableMessages, + getAppState, + cacheSafeParams, + 'sdk', + ) + if (!result || localAbort.signal.aborted) return + const suggestionMsg = { + type: 'prompt_suggestion' as const, + suggestion: result.suggestion, + uuid: randomUUID(), + session_id: getSessionId(), + } + const lastEmittedEntry = { + text: result.suggestion, + emittedAt: Date.now(), + promptId: result.promptId, + generationRequestId: result.generationRequestId, + } + // Defer emission if the result is being held for background agents, + // so that prompt_suggestion always arrives after result. + // Only set lastEmitted when the suggestion is actually delivered + // to the consumer; deferred suggestions may be discarded before + // delivery if a new command arrives first. + if (heldBackResult) { + suggestionState.pendingSuggestion = suggestionMsg + suggestionState.pendingLastEmittedEntry = { + text: lastEmittedEntry.text, + promptId: lastEmittedEntry.promptId, + generationRequestId: lastEmittedEntry.generationRequestId, + } + } else { + suggestionState.lastEmitted = lastEmittedEntry + output.enqueue(suggestionMsg) + } + } catch (error) { + if ( + error instanceof Error && + (error.name === 'AbortError' || + error.name === 'APIUserAbortError') + ) { + logSuggestionSuppressed( + 'aborted', + undefined, + undefined, + 'sdk', + ) + return + } + logError(toError(error)) + } finally { + if (suggestionState.inflightPromise === ref.promise) { + suggestionState.inflightPromise = null + } + } + })() + suggestionState.inflightPromise = ref.promise + } + } + + // Log headless profiler metrics for this turn and start next turn + logHeadlessProfilerTurn() + logQueryProfileReport() + headlessProfilerStartTurn() + } + } + + // Use a do-while loop to drain commands and then wait for any + // background agents that are still running. When agents complete, + // their notifications are enqueued and the loop re-drains. + do { + // Drain SDK events (task_started, task_progress) before command queue + // so progress events precede task_notification on the stream. + for (const event of drainSdkEvents()) { + output.enqueue(event) + } + + runPhase = 'draining_commands' + await drainCommandQueue() + + // Check for running background tasks before exiting. + // Exclude in_process_teammate — teammates are long-lived by design + // (status: 'running' for their whole lifetime, cleaned up by the + // shutdown protocol, not by transitioning to 'completed'). Waiting + // on them here loops forever (gh-30008). Same exclusion already + // exists at useBackgroundTaskNavigation.ts:55 for the same reason; + // L1839 above is already narrower (type === 'local_agent') so it + // doesn't hit this. + waitingForAgents = false + { + const state = getAppState() + const hasRunningBg = getRunningTasks(state).some( + t => isBackgroundTask(t) && t.type !== 'in_process_teammate', + ) + const hasMainThreadQueued = peek(isMainThread) !== undefined + if (hasRunningBg || hasMainThreadQueued) { + waitingForAgents = true + if (!hasMainThreadQueued) { + runPhase = 'waiting_for_agents' + // No commands ready yet, wait for tasks to complete + await sleep(100) + } + // Loop back to drain any newly queued commands + } + } + } while (waitingForAgents) + + if (heldBackResult) { + output.enqueue(heldBackResult) + heldBackResult = null + if (suggestionState.pendingSuggestion) { + output.enqueue(suggestionState.pendingSuggestion) + // Now that the suggestion is actually delivered, record it for acceptance tracking + if (suggestionState.pendingLastEmittedEntry) { + suggestionState.lastEmitted = { + ...suggestionState.pendingLastEmittedEntry, + emittedAt: Date.now(), + } + suggestionState.pendingLastEmittedEntry = null + } + suggestionState.pendingSuggestion = null + } + } + } catch (error) { + // Emit error result message before shutting down + // Write directly to structuredIO to ensure immediate delivery + try { + await structuredIO.write({ + type: 'result', + subtype: 'error_during_execution', + duration_ms: 0, + duration_api_ms: 0, + is_error: true, + num_turns: 0, + stop_reason: null, + session_id: getSessionId(), + total_cost_usd: 0, + usage: EMPTY_USAGE, + modelUsage: {}, + permission_denials: [], + uuid: randomUUID(), + errors: [ + errorMessage(error), + ...getInMemoryErrors().map(_ => _.error), + ], + }) + } catch { + // If we can't emit the error result, continue with shutdown anyway + } + suggestionState.abortController?.abort() + gracefulShutdownSync(1) + return + } finally { + runPhase = 'finally_flush' + // Flush pending internal events before going idle + await structuredIO.flushInternalEvents() + runPhase = 'finally_post_flush' + if (!isShuttingDown()) { + notifySessionStateChanged('idle') + // Drain so the idle session_state_changed SDK event (plus any + // terminal task_notification bookends emitted during bg-agent + // teardown) reach the output stream before we block on the next + // command. The do-while drain above only runs while + // waitingForAgents; once we're here the next drain would be the + // top of the next run(), which won't come if input is idle. + for (const event of drainSdkEvents()) { + output.enqueue(event) + } + } + running = false + // Start idle timer when we finish processing and are waiting for input + idleTimeout.start() + } + + // Proactive tick: if proactive is active and queue is empty, inject a tick + if ( + (feature('PROACTIVE') || feature('KAIROS')) && + proactiveModule?.isProactiveActive() && + !proactiveModule.isProactivePaused() + ) { + if (peek(isMainThread) === undefined && !inputClosed) { + scheduleProactiveTick!() + return + } + } + + // Re-check the queue after releasing the mutex. A message may have + // arrived (and called run()) between the last dequeue() returning + // undefined and `running = false` above. In that case the caller + // saw `running === true` and returned immediately, leaving the + // message stranded in the queue with no one to process it. + if (peek(isMainThread) !== undefined) { + void run() + return + } + + // Check for unread teammate messages and process them + // This mirrors what useInboxPoller does in interactive REPL mode + // Poll until no more messages (teammates may still be working) + { + const currentAppState = getAppState() + const teamContext = currentAppState.teamContext + + if (teamContext && isTeamLead(teamContext)) { + const agentName = 'team-lead' + + // Poll for messages while teammates are active + // This is needed because teammates may send messages while we're waiting + // Keep polling until the team is shut down + const POLL_INTERVAL_MS = 500 + + while (true) { + // Check if teammates are still active + const refreshedState = getAppState() + const hasActiveTeammates = + hasActiveInProcessTeammates(refreshedState) || + (refreshedState.teamContext && + Object.keys(refreshedState.teamContext.teammates).length > 0) + + if (!hasActiveTeammates) { + logForDebugging( + '[print.ts] No more active teammates, stopping poll', + ) + break + } + + const unread = await readUnreadMessages( + agentName, + refreshedState.teamContext?.teamName, + ) + + if (unread.length > 0) { + logForDebugging( + `[print.ts] Team-lead found ${unread.length} unread messages`, + ) + + // Mark as read immediately to avoid duplicate processing + await markMessagesAsRead( + agentName, + refreshedState.teamContext?.teamName, + ) + + // Process shutdown_approved messages - remove teammates from team file + // This mirrors what useInboxPoller does in interactive mode (lines 546-606) + const teamName = refreshedState.teamContext?.teamName + for (const m of unread) { + const shutdownApproval = isShutdownApproved(m.text) + if (shutdownApproval && teamName) { + const teammateToRemove = shutdownApproval.from + logForDebugging( + `[print.ts] Processing shutdown_approved from ${teammateToRemove}`, + ) + + // Find the teammate ID by name + const teammateId = refreshedState.teamContext?.teammates + ? Object.entries(refreshedState.teamContext.teammates).find( + ([, t]) => t.name === teammateToRemove, + )?.[0] + : undefined + + if (teammateId) { + // Remove from team file + removeTeammateFromTeamFile(teamName, { + agentId: teammateId, + name: teammateToRemove, + }) + logForDebugging( + `[print.ts] Removed ${teammateToRemove} from team file`, + ) + + // Unassign tasks owned by this teammate + await unassignTeammateTasks( + teamName, + teammateId, + teammateToRemove, + 'shutdown', + ) + + // Remove from teamContext in AppState + setAppState(prev => { + if (!prev.teamContext?.teammates) return prev + if (!(teammateId in prev.teamContext.teammates)) return prev + const { [teammateId]: _, ...remainingTeammates } = + prev.teamContext.teammates + return { + ...prev, + teamContext: { + ...prev.teamContext, + teammates: remainingTeammates, + }, + } + }) + } + } + } + + // Format messages same as useInboxPoller + const formatted = unread + .map( + (m: { from: string; text: string; color?: string }) => + `<${TEAMMATE_MESSAGE_TAG} teammate_id="${m.from}"${m.color ? ` color="${m.color}"` : ''}>\n${m.text}\n`, + ) + .join('\n\n') + + // Enqueue and process + enqueue({ + mode: 'prompt', + value: formatted, + uuid: randomUUID(), + }) + void run() + return // run() will come back here after processing + } + + // No messages - check if we need to prompt for shutdown + // If input is closed and teammates are active, inject shutdown prompt once + if (inputClosed && !shutdownPromptInjected) { + shutdownPromptInjected = true + logForDebugging( + '[print.ts] Input closed with active teammates, injecting shutdown prompt', + ) + enqueue({ + mode: 'prompt', + value: SHUTDOWN_TEAM_PROMPT, + uuid: randomUUID(), + }) + void run() + return // run() will come back here after processing + } + + // Wait and check again + await sleep(POLL_INTERVAL_MS) + } + } + } + + if (inputClosed) { + // Check for active swarm that needs shutdown + const hasActiveSwarm = await (async () => { + // Wait for any working in-process team members to finish + const currentAppState = getAppState() + if (hasWorkingInProcessTeammates(currentAppState)) { + await waitForTeammatesToBecomeIdle(setAppState, currentAppState) + } + + // Re-fetch state after potential wait + const refreshedAppState = getAppState() + const refreshedTeamContext = refreshedAppState.teamContext + const hasTeamMembersNotCleanedUp = + refreshedTeamContext && + Object.keys(refreshedTeamContext.teammates).length > 0 + + return ( + hasTeamMembersNotCleanedUp || + hasActiveInProcessTeammates(refreshedAppState) + ) + })() + + if (hasActiveSwarm) { + // Team members are idle or pane-based - inject prompt to shut down team + enqueue({ + mode: 'prompt', + value: SHUTDOWN_TEAM_PROMPT, + uuid: randomUUID(), + }) + void run() + } else { + // Wait for any in-flight push suggestion before closing the output stream. + if (suggestionState.inflightPromise) { + await Promise.race([suggestionState.inflightPromise, sleep(5000)]) + } + suggestionState.abortController?.abort() + suggestionState.abortController = null + await finalizePendingAsyncHooks() + unsubscribeSkillChanges() + unsubscribeAuthStatus?.() + statusListeners.delete(rateLimitListener) + output.done() + } + } + } + + // Set up UDS inbox callback so the query loop is kicked off + // when a message arrives via the UDS socket in headless mode. + if (feature('UDS_INBOX')) { + /* eslint-disable @typescript-eslint/no-require-imports */ + const { setOnEnqueue } = require('../utils/udsMessaging.js') + /* eslint-enable @typescript-eslint/no-require-imports */ + setOnEnqueue(() => { + if (!inputClosed) { + void run() + } + }) + } + + // Cron scheduler: runs scheduled_tasks.json tasks in SDK/-p mode. + // Mirrors REPL's useScheduledTasks hook. Fired prompts enqueue + kick + // off run() directly — unlike REPL, there's no queue subscriber here + // that drains on enqueue while idle. The run() mutex makes this safe + // during an active turn: the call no-ops and the post-run recheck at + // the end of run() picks up the queued command. + let cronScheduler: import('../utils/cronScheduler.js').CronScheduler | null = + null + if ( + feature('AGENT_TRIGGERS') && + cronSchedulerModule && + cronGate?.isKairosCronEnabled() + ) { + cronScheduler = cronSchedulerModule.createCronScheduler({ + onFire: prompt => { + if (inputClosed) return + enqueue({ + mode: 'prompt', + value: prompt, + uuid: randomUUID(), + priority: 'later', + // System-generated — matches useScheduledTasks.ts REPL equivalent. + // Without this, messages.ts metaProp eval is {} → prompt leaks + // into visible transcript when cron fires mid-turn in -p mode. + isMeta: true, + // Threaded to cc_workload= in the billing-header attribution block + // so the API can serve cron requests at lower QoS. drainCommandQueue + // reads this per-iteration and hoists it into bootstrap state for + // the ask() call. + workload: WORKLOAD_CRON, + }) + void run() + }, + isLoading: () => running || inputClosed, + getJitterConfig: cronJitterConfigModule?.getCronJitterConfig, + isKilled: () => !cronGate?.isKairosCronEnabled(), + }) + cronScheduler.start() + } + + const sendControlResponseSuccess = function ( + message: SDKControlRequest, + response?: Record, + ) { + output.enqueue({ + type: 'control_response', + response: { + subtype: 'success', + request_id: message.request_id, + response: response, + }, + }) + } + + const sendControlResponseError = function ( + message: SDKControlRequest, + errorMessage: string, + ) { + output.enqueue({ + type: 'control_response', + response: { + subtype: 'error', + request_id: message.request_id, + error: errorMessage, + }, + }) + } + + // Handle unexpected permission responses by looking up the unresolved tool + // call in the transcript and executing it + const handledOrphanedToolUseIds = new Set() + structuredIO.setUnexpectedResponseCallback(async message => { + await handleOrphanedPermissionResponse({ + message, + setAppState, + handledToolUseIds: handledOrphanedToolUseIds, + onEnqueued: () => { + // The first message of a session might be the orphaned permission + // check rather than a user prompt, so kick off the loop. + void run() + }, + }) + }) + + // Track active OAuth flows per server so we can abort a previous flow + // when a new mcp_authenticate request arrives for the same server. + const activeOAuthFlows = new Map() + // Track manual callback URL submit functions for active OAuth flows. + // Used when localhost is not reachable (e.g., browser-based IDEs). + const oauthCallbackSubmitters = new Map< + string, + (callbackUrl: string) => void + >() + // Track servers where the manual callback was actually invoked (so the + // automatic reconnect path knows to skip — the extension will reconnect). + const oauthManualCallbackUsed = new Set() + // Track OAuth auth-only promises so mcp_oauth_callback_url can await + // token exchange completion. Reconnect is handled separately by the + // extension via handleAuthDone → mcp_reconnect. + const oauthAuthPromises = new Map>() + + // In-flight Anthropic OAuth flow (claude_authenticate). Single-slot: a + // second authenticate request cleans up the first. The service holds the + // PKCE verifier + localhost listener; the promise settles after + // installOAuthTokens — after it resolves, the in-process memoized token + // cache is already cleared and the next API call picks up the new creds. + let claudeOAuth: { + service: OAuthService + flow: Promise + } | null = null + + // This is essentially spawning a parallel async task- we have two + // running in parallel- one reading from stdin and adding to the + // queue to be processed and another reading from the queue, + // processing and returning the result of the generation. + // The process is complete when the input stream completes and + // the last generation of the queue has complete. + void (async () => { + let initialized = false + logForDiagnosticsNoPII('info', 'cli_message_loop_started') + for await (const message of structuredIO.structuredInput) { + // Non-user events are handled inline (no queue). started→completed in + // the same tick carries no information, so only fire completed. + // control_response is reported by StructuredIO.processLine (which also + // sees orphans that never yield here). + const eventId = 'uuid' in message ? message.uuid : undefined + if ( + eventId && + message.type !== 'user' && + message.type !== 'control_response' + ) { + notifyCommandLifecycle(eventId, 'completed') + } + + if (message.type === 'control_request') { + if (message.request.subtype === 'interrupt') { + // Track escapes for attribution (ant-only feature) + if (feature('COMMIT_ATTRIBUTION')) { + setAppState(prev => ({ + ...prev, + attribution: { + ...prev.attribution, + escapeCount: prev.attribution.escapeCount + 1, + }, + })) + } + if (abortController) { + abortController.abort() + } + suggestionState.abortController?.abort() + suggestionState.abortController = null + suggestionState.lastEmitted = null + suggestionState.pendingSuggestion = null + sendControlResponseSuccess(message) + } else if (message.request.subtype === 'end_session') { + logForDebugging( + `[print.ts] end_session received, reason=${message.request.reason ?? 'unspecified'}`, + ) + if (abortController) { + abortController.abort() + } + suggestionState.abortController?.abort() + suggestionState.abortController = null + suggestionState.lastEmitted = null + suggestionState.pendingSuggestion = null + sendControlResponseSuccess(message) + break // exits for-await → falls through to inputClosed=true drain below + } else if (message.request.subtype === 'initialize') { + // SDK MCP server names from the initialize message + // Populated by both browser and ProcessTransport sessions + if ( + message.request.sdkMcpServers && + message.request.sdkMcpServers.length > 0 + ) { + for (const serverName of message.request.sdkMcpServers) { + // Create placeholder config for SDK MCP servers + // The actual server connection is managed by the SDK Query class + sdkMcpConfigs[serverName] = { + type: 'sdk', + name: serverName, + } + } + } + + await handleInitializeRequest( + message.request, + message.request_id, + initialized, + output, + commands, + modelInfos, + structuredIO, + !!options.enableAuthStatus, + options, + agents, + getAppState, + ) + + // Enable prompt suggestions in AppState when SDK consumer opts in. + // shouldEnablePromptSuggestion() returns false for non-interactive + // sessions, but the SDK consumer explicitly requested suggestions. + if (message.request.promptSuggestions) { + setAppState(prev => { + if (prev.promptSuggestionEnabled) return prev + return { ...prev, promptSuggestionEnabled: true } + }) + } + + if ( + message.request.agentProgressSummaries && + getFeatureValue_CACHED_MAY_BE_STALE('tengu_slate_prism', true) + ) { + setSdkAgentProgressSummariesEnabled(true) + } + + initialized = true + + // If the auto-resume logic pre-enqueued a command, drain it now + // that initialize has set up systemPrompt, agents, hooks, etc. + if (hasCommandsInQueue()) { + void run() + } + } else if (message.request.subtype === 'set_permission_mode') { + const m = message.request // for typescript (TODO: use readonly types to avoid this) + setAppState(prev => ({ + ...prev, + toolPermissionContext: handleSetPermissionMode( + m, + message.request_id, + prev.toolPermissionContext, + output, + ), + isUltraplanMode: m.ultraplan ?? prev.isUltraplanMode, + })) + // handleSetPermissionMode sends the control_response; the + // notifySessionMetadataChanged that used to follow here is + // now fired by onChangeAppState (with externalized mode name). + } else if (message.request.subtype === 'set_model') { + const requestedModel = message.request.model ?? 'default' + const model = + requestedModel === 'default' + ? getDefaultMainLoopModel() + : requestedModel + activeUserSpecifiedModel = model + setMainLoopModelOverride(model) + notifySessionMetadataChanged({ model }) + injectModelSwitchBreadcrumbs(requestedModel, model) + + sendControlResponseSuccess(message) + } else if (message.request.subtype === 'set_max_thinking_tokens') { + if (message.request.max_thinking_tokens === null) { + options.thinkingConfig = undefined + } else if (message.request.max_thinking_tokens === 0) { + options.thinkingConfig = { type: 'disabled' } + } else { + options.thinkingConfig = { + type: 'enabled', + budgetTokens: message.request.max_thinking_tokens, + } + } + sendControlResponseSuccess(message) + } else if (message.request.subtype === 'mcp_status') { + sendControlResponseSuccess(message, { + mcpServers: buildMcpServerStatuses(), + }) + } else if (message.request.subtype === 'get_context_usage') { + try { + const appState = getAppState() + const data = await collectContextData({ + messages: mutableMessages, + getAppState, + options: { + mainLoopModel: getMainLoopModel(), + tools: buildAllTools(appState), + agentDefinitions: appState.agentDefinitions, + customSystemPrompt: options.systemPrompt, + appendSystemPrompt: options.appendSystemPrompt, + }, + }) + sendControlResponseSuccess(message, { ...data }) + } catch (error) { + sendControlResponseError(message, errorMessage(error)) + } + } else if (message.request.subtype === 'mcp_message') { + // Handle MCP notifications from SDK servers + const mcpRequest = message.request + const sdkClient = sdkClients.find( + client => client.name === mcpRequest.server_name, + ) + // Check client exists - dynamically added SDK servers may have + // placeholder clients with null client until updateSdkMcp() runs + if ( + sdkClient && + sdkClient.type === 'connected' && + sdkClient.client?.transport?.onmessage + ) { + sdkClient.client.transport.onmessage(mcpRequest.message) + } + sendControlResponseSuccess(message) + } else if (message.request.subtype === 'rewind_files') { + const appState = getAppState() + const result = await handleRewindFiles( + message.request.user_message_id as UUID, + appState, + setAppState, + message.request.dry_run ?? false, + ) + if (result.canRewind || message.request.dry_run) { + sendControlResponseSuccess(message, result) + } else { + sendControlResponseError( + message, + result.error ?? 'Unexpected error', + ) + } + } else if (message.request.subtype === 'cancel_async_message') { + const targetUuid = message.request.message_uuid + const removed = dequeueAllMatching(cmd => cmd.uuid === targetUuid) + sendControlResponseSuccess(message, { + cancelled: removed.length > 0, + }) + } else if (message.request.subtype === 'seed_read_state') { + // Client observed a Read that was later removed from context (e.g. + // by snip), so transcript-based seeding missed it. Queued into + // pendingSeeds; applied at the next clone-replace boundary. + try { + // expandPath: all other readFileState writers normalize (~, relative, + // session cwd vs process cwd). FileEditTool looks up by expandPath'd + // key — a verbatim client path would miss. + const normalizedPath = expandPath(message.request.path) + // Check disk mtime before reading content. If the file changed + // since the client's observation, readFile would return C_current + // but we'd store it with the client's M_observed — getChangedFiles + // then sees disk > cache.timestamp, re-reads, diffs C_current vs + // C_current = empty, emits no attachment, and the model is never + // told about the C_observed → C_current change. Skipping the seed + // makes Edit fail "file not read yet" → forces a fresh Read. + // Math.floor matches FileReadTool and getFileModificationTime. + const diskMtime = Math.floor((await stat(normalizedPath)).mtimeMs) + if (diskMtime <= message.request.mtime) { + const raw = await readFile(normalizedPath, 'utf-8') + // Strip BOM + normalize CRLF→LF to match readFileInRange and + // readFileSyncWithMetadata. FileEditTool's content-compare + // fallback (for Windows mtime bumps without content change) + // compares against LF-normalized disk reads. + const content = ( + raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw + ).replaceAll('\r\n', '\n') + pendingSeeds.set(normalizedPath, { + content, + timestamp: diskMtime, + offset: undefined, + limit: undefined, + }) + } + } catch { + // ENOENT etc — skip seeding but still succeed + } + sendControlResponseSuccess(message) + } else if (message.request.subtype === 'mcp_set_servers') { + const { response, sdkServersChanged } = await applyMcpServerChanges( + message.request.servers, + ) + sendControlResponseSuccess(message, response) + + // Connect SDK servers AFTER response to avoid deadlock + if (sdkServersChanged) { + void updateSdkMcp() + } + } else if (message.request.subtype === 'reload_plugins') { + try { + if ( + feature('DOWNLOAD_USER_SETTINGS') && + (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) || getIsRemoteMode()) + ) { + // Re-pull user settings so enabledPlugins pushed from the + // user's local CLI take effect before the cache sweep. + const applied = await redownloadUserSettings() + if (applied) { + settingsChangeDetector.notifyChange('userSettings') + } + } + + const r = await refreshActivePlugins(setAppState) + + const sdkAgents = currentAgents.filter( + a => a.source === 'flagSettings', + ) + currentAgents = [...r.agentDefinitions.allAgents, ...sdkAgents] + + // Reload succeeded — gather response data best-effort so a + // read failure doesn't mask the successful state change. + // allSettled so one failure doesn't discard the others. + let plugins: SDKControlReloadPluginsResponse['plugins'] = [] + const [cmdsR, mcpR, pluginsR] = await Promise.allSettled([ + getCommands(cwd()), + applyPluginMcpDiff(), + loadAllPluginsCacheOnly(), + ]) + if (cmdsR.status === 'fulfilled') { + currentCommands = cmdsR.value + } else { + logError(cmdsR.reason) + } + if (mcpR.status === 'rejected') { + logError(mcpR.reason) + } + if (pluginsR.status === 'fulfilled') { + plugins = pluginsR.value.enabled.map(p => ({ + name: p.name, + path: p.path, + source: p.source, + })) + } else { + logError(pluginsR.reason) + } + + sendControlResponseSuccess(message, { + commands: currentCommands + .filter(cmd => cmd.userInvocable !== false) + .map(cmd => ({ + name: getCommandName(cmd), + description: formatDescriptionWithSource(cmd), + argumentHint: cmd.argumentHint || '', + })), + agents: currentAgents.map(a => ({ + name: a.agentType, + description: a.whenToUse, + model: a.model === 'inherit' ? undefined : a.model, + })), + plugins, + mcpServers: buildMcpServerStatuses(), + error_count: r.error_count, + } satisfies SDKControlReloadPluginsResponse) + } catch (error) { + sendControlResponseError(message, errorMessage(error)) + } + } else if (message.request.subtype === 'mcp_reconnect') { + const currentAppState = getAppState() + const { serverName } = message.request + elicitationRegistered.delete(serverName) + // Config-existence gate must cover the SAME sources as the + // operations below. SDK-injected servers (query({mcpServers:{...}})) + // and dynamically-added servers were missing here, so + // toggleMcpServer/reconnect returned "Server not found" even though + // the disconnect/reconnect would have worked (gh-31339 / CC-314). + const config = + getMcpConfigByName(serverName) ?? + mcpClients.find(c => c.name === serverName)?.config ?? + sdkClients.find(c => c.name === serverName)?.config ?? + dynamicMcpState.clients.find(c => c.name === serverName)?.config ?? + currentAppState.mcp.clients.find(c => c.name === serverName) + ?.config ?? + null + if (!config) { + sendControlResponseError(message, `Server not found: ${serverName}`) + } else { + const result = await reconnectMcpServerImpl(serverName, config) + // Update appState.mcp with the new client, tools, commands, and resources + const prefix = getMcpPrefix(serverName) + setAppState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + clients: prev.mcp.clients.map(c => + c.name === serverName ? result.client : c, + ), + tools: [ + ...reject(prev.mcp.tools, t => t.name?.startsWith(prefix)), + ...result.tools, + ], + commands: [ + ...reject(prev.mcp.commands, c => + commandBelongsToServer(c, serverName), + ), + ...result.commands, + ], + resources: + result.resources && result.resources.length > 0 + ? { ...prev.mcp.resources, [serverName]: result.resources } + : omit(prev.mcp.resources, serverName), + }, + })) + // Also update dynamicMcpState so run() picks up the new tools + // on the next turn (run() reads dynamicMcpState, not appState) + dynamicMcpState = { + ...dynamicMcpState, + clients: [ + ...dynamicMcpState.clients.filter(c => c.name !== serverName), + result.client, + ], + tools: [ + ...dynamicMcpState.tools.filter( + t => !t.name?.startsWith(prefix), + ), + ...result.tools, + ], + } + if (result.client.type === 'connected') { + registerElicitationHandlers([result.client]) + reregisterChannelHandlerAfterReconnect(result.client) + sendControlResponseSuccess(message) + } else { + const errorMessage = + result.client.type === 'failed' + ? (result.client.error ?? 'Connection failed') + : `Server status: ${result.client.type}` + sendControlResponseError(message, errorMessage) + } + } + } else if (message.request.subtype === 'mcp_toggle') { + const currentAppState = getAppState() + const { serverName, enabled } = message.request + elicitationRegistered.delete(serverName) + // Gate must match the client-lookup spread below (which + // includes sdkClients and dynamicMcpState.clients). Same fix as + // mcp_reconnect above (gh-31339 / CC-314). + const config = + getMcpConfigByName(serverName) ?? + mcpClients.find(c => c.name === serverName)?.config ?? + sdkClients.find(c => c.name === serverName)?.config ?? + dynamicMcpState.clients.find(c => c.name === serverName)?.config ?? + currentAppState.mcp.clients.find(c => c.name === serverName) + ?.config ?? + null + + if (!config) { + sendControlResponseError(message, `Server not found: ${serverName}`) + } else if (!enabled) { + // Disabling: persist + disconnect (matches TUI toggleMcpServer behavior) + setMcpServerEnabled(serverName, false) + const client = [ + ...mcpClients, + ...sdkClients, + ...dynamicMcpState.clients, + ...currentAppState.mcp.clients, + ].find(c => c.name === serverName) + if (client && client.type === 'connected') { + await clearServerCache(serverName, config) + } + // Update appState.mcp to reflect disabled status and remove tools/commands/resources + const prefix = getMcpPrefix(serverName) + setAppState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + clients: prev.mcp.clients.map(c => + c.name === serverName + ? { name: serverName, type: 'disabled' as const, config } + : c, + ), + tools: reject(prev.mcp.tools, t => t.name?.startsWith(prefix)), + commands: reject(prev.mcp.commands, c => + commandBelongsToServer(c, serverName), + ), + resources: omit(prev.mcp.resources, serverName), + }, + })) + sendControlResponseSuccess(message) + } else { + // Enabling: persist + reconnect + setMcpServerEnabled(serverName, true) + const result = await reconnectMcpServerImpl(serverName, config) + // Update appState.mcp with the new client, tools, commands, and resources + // This ensures the LLM sees updated tools after enabling the server + const prefix = getMcpPrefix(serverName) + setAppState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + clients: prev.mcp.clients.map(c => + c.name === serverName ? result.client : c, + ), + tools: [ + ...reject(prev.mcp.tools, t => t.name?.startsWith(prefix)), + ...result.tools, + ], + commands: [ + ...reject(prev.mcp.commands, c => + commandBelongsToServer(c, serverName), + ), + ...result.commands, + ], + resources: + result.resources && result.resources.length > 0 + ? { ...prev.mcp.resources, [serverName]: result.resources } + : omit(prev.mcp.resources, serverName), + }, + })) + if (result.client.type === 'connected') { + registerElicitationHandlers([result.client]) + reregisterChannelHandlerAfterReconnect(result.client) + sendControlResponseSuccess(message) + } else { + const errorMessage = + result.client.type === 'failed' + ? (result.client.error ?? 'Connection failed') + : `Server status: ${result.client.type}` + sendControlResponseError(message, errorMessage) + } + } + } else if (message.request.subtype === 'channel_enable') { + const currentAppState = getAppState() + handleChannelEnable( + message.request_id, + message.request.serverName, + // Pool spread matches mcp_status — all three client sources. + [ + ...currentAppState.mcp.clients, + ...sdkClients, + ...dynamicMcpState.clients, + ], + output, + ) + } else if (message.request.subtype === 'mcp_authenticate') { + const { serverName } = message.request + const currentAppState = getAppState() + const config = + getMcpConfigByName(serverName) ?? + mcpClients.find(c => c.name === serverName)?.config ?? + currentAppState.mcp.clients.find(c => c.name === serverName) + ?.config ?? + null + if (!config) { + sendControlResponseError(message, `Server not found: ${serverName}`) + } else if (config.type !== 'sse' && config.type !== 'http') { + sendControlResponseError( + message, + `Server type "${config.type}" does not support OAuth authentication`, + ) + } else { + try { + // Abort any previous in-flight OAuth flow for this server + activeOAuthFlows.get(serverName)?.abort() + const controller = new AbortController() + activeOAuthFlows.set(serverName, controller) + + // Capture the auth URL from the callback + let resolveAuthUrl: (url: string) => void + const authUrlPromise = new Promise(resolve => { + resolveAuthUrl = resolve + }) + + // Start the OAuth flow in the background + const oauthPromise = performMCPOAuthFlow( + serverName, + config, + url => resolveAuthUrl!(url), + controller.signal, + { + skipBrowserOpen: true, + onWaitingForCallback: submit => { + oauthCallbackSubmitters.set(serverName, submit) + }, + }, + ) + + // Wait for the auth URL (or the flow to complete without needing redirect) + const authUrl = await Promise.race([ + authUrlPromise, + oauthPromise.then(() => null as string | null), + ]) + + if (authUrl) { + sendControlResponseSuccess(message, { + authUrl, + requiresUserAction: true, + }) + } else { + sendControlResponseSuccess(message, { + requiresUserAction: false, + }) + } + + // Store auth-only promise for mcp_oauth_callback_url handler. + // Don't swallow errors — the callback handler needs to detect + // auth failures and report them to the caller. + oauthAuthPromises.set(serverName, oauthPromise) + + // Handle background completion — reconnect after auth. + // When manual callback is used, skip the reconnect here; + // the extension's handleAuthDone → mcp_reconnect handles it + // (which also updates dynamicMcpState for tool registration). + const fullFlowPromise = oauthPromise + .then(async () => { + // Don't reconnect if the server was disabled during the OAuth flow + if (isMcpServerDisabled(serverName)) { + return + } + // Skip reconnect if the manual callback path was used — + // handleAuthDone will do it via mcp_reconnect (which + // updates dynamicMcpState for tool registration). + if (oauthManualCallbackUsed.has(serverName)) { + return + } + // Reconnect the server after successful auth + const result = await reconnectMcpServerImpl( + serverName, + config, + ) + const prefix = getMcpPrefix(serverName) + setAppState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + clients: prev.mcp.clients.map(c => + c.name === serverName ? result.client : c, + ), + tools: [ + ...reject(prev.mcp.tools, t => + t.name?.startsWith(prefix), + ), + ...result.tools, + ], + commands: [ + ...reject(prev.mcp.commands, c => + commandBelongsToServer(c, serverName), + ), + ...result.commands, + ], + resources: + result.resources && result.resources.length > 0 + ? { + ...prev.mcp.resources, + [serverName]: result.resources, + } + : omit(prev.mcp.resources, serverName), + }, + })) + // Also update dynamicMcpState so run() picks up the new tools + // on the next turn (run() reads dynamicMcpState, not appState) + dynamicMcpState = { + ...dynamicMcpState, + clients: [ + ...dynamicMcpState.clients.filter( + c => c.name !== serverName, + ), + result.client, + ], + tools: [ + ...dynamicMcpState.tools.filter( + t => !t.name?.startsWith(prefix), + ), + ...result.tools, + ], + } + }) + .catch(error => { + logForDebugging( + `MCP OAuth failed for ${serverName}: ${error}`, + { level: 'error' }, + ) + }) + .finally(() => { + // Clean up only if this is still the active flow + if (activeOAuthFlows.get(serverName) === controller) { + activeOAuthFlows.delete(serverName) + oauthCallbackSubmitters.delete(serverName) + oauthManualCallbackUsed.delete(serverName) + oauthAuthPromises.delete(serverName) + } + }) + void fullFlowPromise + } catch (error) { + sendControlResponseError(message, errorMessage(error)) + } + } + } else if (message.request.subtype === 'mcp_oauth_callback_url') { + const { serverName, callbackUrl } = message.request + const submit = oauthCallbackSubmitters.get(serverName) + if (submit) { + // Validate the callback URL before submitting. The submit + // callback in auth.ts silently ignores URLs missing a code + // param, which would leave the auth promise unresolved and + // block the control message loop until timeout. + let hasCodeOrError = false + try { + const parsed = new URL(callbackUrl) + hasCodeOrError = + parsed.searchParams.has('code') || + parsed.searchParams.has('error') + } catch { + // Invalid URL + } + if (!hasCodeOrError) { + sendControlResponseError( + message, + 'Invalid callback URL: missing authorization code. Please paste the full redirect URL including the code parameter.', + ) + } else { + oauthManualCallbackUsed.add(serverName) + submit(callbackUrl) + // Wait for auth (token exchange) to complete before responding. + // Reconnect is handled by the extension via handleAuthDone → + // mcp_reconnect (which updates dynamicMcpState for tools). + const authPromise = oauthAuthPromises.get(serverName) + if (authPromise) { + try { + await authPromise + sendControlResponseSuccess(message) + } catch (error) { + sendControlResponseError( + message, + error instanceof Error + ? error.message + : 'OAuth authentication failed', + ) + } + } else { + sendControlResponseSuccess(message) + } + } + } else { + sendControlResponseError( + message, + `No active OAuth flow for server: ${serverName}`, + ) + } + } else if (message.request.subtype === 'claude_authenticate') { + // Anthropic OAuth over the control channel. The SDK client owns + // the user's browser (we're headless in -p mode); we hand back + // both URLs and wait. Automatic URL → localhost listener catches + // the redirect if the browser is on this host; manual URL → the + // success page shows "code#state" for claude_oauth_callback. + const { loginWithClaudeAi } = message.request + + // Clean up any prior flow. cleanup() closes the localhost listener + // and nulls the manual resolver. The prior `flow` promise is left + // pending (AuthCodeListener.close() does not reject) but its object + // graph becomes unreachable once the server handle is released and + // is GC'd — no fd or port is held. + claudeOAuth?.service.cleanup() + + logEvent('tengu_oauth_flow_start', { + loginWithClaudeAi: loginWithClaudeAi ?? true, + }) + + const service = new OAuthService() + let urlResolver!: (urls: { + manualUrl: string + automaticUrl: string + }) => void + const urlPromise = new Promise<{ + manualUrl: string + automaticUrl: string + }>(resolve => { + urlResolver = resolve + }) + + const flow = service + .startOAuthFlow( + async (manualUrl, automaticUrl) => { + // automaticUrl is always defined when skipBrowserOpen is set; + // the signature is optional only for the existing single-arg callers. + urlResolver({ manualUrl, automaticUrl: automaticUrl! }) + }, + { + loginWithClaudeAi: loginWithClaudeAi ?? true, + skipBrowserOpen: true, + }, + ) + .then(async tokens => { + // installOAuthTokens: performLogout (clear stale state) → + // store profile → saveOAuthTokensIfNeeded → clearOAuthTokenCache + // → clearAuthRelatedCaches. After this resolves, the memoized + // getClaudeAIOAuthTokens in this process is invalidated; the + // next API call re-reads keychain/file and works. No respawn. + await installOAuthTokens(tokens) + logEvent('tengu_oauth_success', { + loginWithClaudeAi: loginWithClaudeAi ?? true, + }) + }) + .finally(() => { + service.cleanup() + if (claudeOAuth?.service === service) { + claudeOAuth = null + } + }) + + claudeOAuth = { service, flow } + + // Attach the rejection handler before awaiting so a synchronous + // startOAuthFlow failure doesn't surface as an unhandled rejection. + // The claude_oauth_callback handler re-awaits flow for the manual + // path and surfaces the real error to the client. + void flow.catch(err => + logForDebugging(`claude_authenticate flow ended: ${err}`, { + level: 'info', + }), + ) + + try { + // Race against flow: if startOAuthFlow rejects before calling + // the authURLHandler (e.g. AuthCodeListener.start() fails with + // EACCES or fd exhaustion), urlPromise would pend forever and + // wedge the stdin loop. flow resolving first is unreachable in + // practice (it's suspended on the same urls we're waiting for). + const { manualUrl, automaticUrl } = await Promise.race([ + urlPromise, + flow.then(() => { + throw new Error( + 'OAuth flow completed without producing auth URLs', + ) + }), + ]) + sendControlResponseSuccess(message, { + manualUrl, + automaticUrl, + }) + } catch (error) { + sendControlResponseError(message, errorMessage(error)) + } + } else if ( + message.request.subtype === 'claude_oauth_callback' || + message.request.subtype === 'claude_oauth_wait_for_completion' + ) { + if (!claudeOAuth) { + sendControlResponseError( + message, + 'No active claude_authenticate flow', + ) + } else { + // Inject the manual code synchronously — must happen in stdin + // message order so a subsequent claude_authenticate doesn't + // replace the service before this code lands. + if (message.request.subtype === 'claude_oauth_callback') { + claudeOAuth.service.handleManualAuthCodeInput({ + authorizationCode: message.request.authorizationCode, + state: message.request.state, + }) + } + // Detach the await — the stdin reader is serial and blocking + // here deadlocks claude_oauth_wait_for_completion: flow may + // only resolve via a future claude_oauth_callback on stdin, + // which can't be read while we're parked. Capture the binding; + // claudeOAuth is nulled in flow's own .finally. + const { flow } = claudeOAuth + void flow.then( + () => { + const accountInfo = getAccountInformation() + sendControlResponseSuccess(message, { + account: { + email: accountInfo?.email, + organization: accountInfo?.organization, + subscriptionType: accountInfo?.subscription, + tokenSource: accountInfo?.tokenSource, + apiKeySource: accountInfo?.apiKeySource, + apiProvider: getAPIProvider(), + }, + }) + }, + (error: unknown) => + sendControlResponseError(message, errorMessage(error)), + ) + } + } else if (message.request.subtype === 'mcp_clear_auth') { + const { serverName } = message.request + const currentAppState = getAppState() + const config = + getMcpConfigByName(serverName) ?? + mcpClients.find(c => c.name === serverName)?.config ?? + currentAppState.mcp.clients.find(c => c.name === serverName) + ?.config ?? + null + if (!config) { + sendControlResponseError(message, `Server not found: ${serverName}`) + } else if (config.type !== 'sse' && config.type !== 'http') { + sendControlResponseError( + message, + `Cannot clear auth for server type "${config.type}"`, + ) + } else { + await revokeServerTokens(serverName, config) + const result = await reconnectMcpServerImpl(serverName, config) + const prefix = getMcpPrefix(serverName) + setAppState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + clients: prev.mcp.clients.map(c => + c.name === serverName ? result.client : c, + ), + tools: [ + ...reject(prev.mcp.tools, t => t.name?.startsWith(prefix)), + ...result.tools, + ], + commands: [ + ...reject(prev.mcp.commands, c => + commandBelongsToServer(c, serverName), + ), + ...result.commands, + ], + resources: + result.resources && result.resources.length > 0 + ? { + ...prev.mcp.resources, + [serverName]: result.resources, + } + : omit(prev.mcp.resources, serverName), + }, + })) + sendControlResponseSuccess(message, {}) + } + } else if (message.request.subtype === 'apply_flag_settings') { + // Snapshot the current model before applying — we need to detect + // model switches so we can inject breadcrumbs and notify listeners. + const prevModel = getMainLoopModel() + + // Merge the provided settings into the in-memory flag settings + const existing = getFlagSettingsInline() ?? {} + const incoming = message.request.settings + // Shallow-merge top-level keys; getSettingsForSource handles + // the deep merge with file-based flag settings via mergeWith. + // JSON serialization drops `undefined`, so callers use `null` + // to signal "clear this key". Convert nulls to deletions so + // SettingsSchema().safeParse() doesn't reject the whole object + // (z.string().optional() accepts string | undefined, not null). + const merged = { ...existing, ...incoming } + for (const key of Object.keys(merged)) { + if (merged[key as keyof typeof merged] === null) { + delete merged[key as keyof typeof merged] + } + } + setFlagSettingsInline(merged) + // Route through notifyChange so fanOut() resets the settings cache + // before listeners run. The subscriber at :392 calls + // applySettingsChange for us. Pre-#20625 this was a direct + // applySettingsChange() call that relied on its own internal reset — + // now that the reset is centralized in fanOut, a direct call here + // would read stale cached settings and silently drop the update. + // Bonus: going through notifyChange also tells the other subscribers + // (loadPluginHooks, sandbox-adapter) about the change, which the + // previous direct call skipped. + settingsChangeDetector.notifyChange('flagSettings') + + // If the incoming settings include a model change, update the + // override so getMainLoopModel() reflects it. The override has + // higher priority than the settings cascade in + // getUserSpecifiedModelSetting(), so without this update, + // getMainLoopModel() returns the stale override and the model + // change is silently ignored (matching set_model at :2811). + if ('model' in incoming) { + if (incoming.model != null) { + setMainLoopModelOverride(String(incoming.model)) + } else { + setMainLoopModelOverride(undefined) + } + } + + // If the model changed, inject breadcrumbs so the model sees the + // mid-conversation switch, and notify metadata listeners (CCR). + const newModel = getMainLoopModel() + if (newModel !== prevModel) { + activeUserSpecifiedModel = newModel + const modelArg = incoming.model ? String(incoming.model) : 'default' + notifySessionMetadataChanged({ model: newModel }) + injectModelSwitchBreadcrumbs(modelArg, newModel) + } + + sendControlResponseSuccess(message) + } else if (message.request.subtype === 'get_settings') { + const currentAppState = getAppState() + const model = getMainLoopModel() + // modelSupportsEffort gate matches claude.ts — applied.effort must + // mirror what actually goes to the API, not just what's configured. + const effort = modelSupportsEffort(model) + ? resolveAppliedEffort(model, currentAppState.effortValue) + : undefined + sendControlResponseSuccess(message, { + ...getSettingsWithSources(), + applied: { + model, + // Numeric effort (ant-only) → null; SDK schema is string-level only. + effort: typeof effort === 'string' ? effort : null, + }, + }) + } else if (message.request.subtype === 'stop_task') { + const { task_id: taskId } = message.request + try { + await stopTask(taskId, { + getAppState, + setAppState, + }) + sendControlResponseSuccess(message, {}) + } catch (error) { + sendControlResponseError(message, errorMessage(error)) + } + } else if (message.request.subtype === 'generate_session_title') { + // Fire-and-forget so the Haiku call does not block the stdin loop + // (which would delay processing of subsequent user messages / + // interrupts for the duration of the API roundtrip). + const { description, persist } = message.request + // Reuse the live controller only if it has not already been aborted + // (e.g. by interrupt()); an aborted signal would cause queryHaiku to + // immediately throw APIUserAbortError → {title: null}. + const titleSignal = ( + abortController && !abortController.signal.aborted + ? abortController + : createAbortController() + ).signal + void (async () => { + try { + const title = await generateSessionTitle(description, titleSignal) + if (title && persist) { + try { + saveAiGeneratedTitle(getSessionId() as UUID, title) + } catch (e) { + logError(e) + } + } + sendControlResponseSuccess(message, { title }) + } catch (e) { + // Unreachable in practice — generateSessionTitle wraps its + // own body and returns null, saveAiGeneratedTitle is wrapped + // above. Propagate (not swallow) so unexpected failures are + // visible to the SDK caller (hostComms.ts catches and logs). + sendControlResponseError(message, errorMessage(e)) + } + })() + } else if (message.request.subtype === 'side_question') { + // Same fire-and-forget pattern as generate_session_title above — + // the forked agent's API roundtrip must not block the stdin loop. + // + // The snapshot captured by stopHooks (for querySource === 'sdk') + // holds the exact systemPrompt/userContext/systemContext/messages + // sent on the last main-thread turn. Reusing them gives a byte- + // identical prefix → prompt cache hit. + // + // Fallback (resume before first turn completes — no snapshot yet): + // rebuild from scratch. buildSideQuestionFallbackParams mirrors + // QueryEngine.ts:ask()'s system prompt assembly (including + // --system-prompt / --append-system-prompt) so the rebuilt prefix + // matches in the common case. May still miss the cache for + // coordinator mode or memory-mechanics extras — acceptable, the + // alternative is the side question failing entirely. + const { question } = message.request + void (async () => { + try { + const saved = getLastCacheSafeParams() + const cacheSafeParams = saved + ? { + ...saved, + // If the last turn was interrupted, the snapshot holds an + // already-aborted controller; createChildAbortController in + // createSubagentContext would propagate it and the fork + // would die before sending a request. The controller is + // not part of the cache key — swapping in a fresh one is + // safe. Same guard as generate_session_title above. + toolUseContext: { + ...saved.toolUseContext, + abortController: createAbortController(), + }, + } + : await buildSideQuestionFallbackParams({ + tools: buildAllTools(getAppState()), + commands: currentCommands, + mcpClients: [ + ...getAppState().mcp.clients, + ...sdkClients, + ...dynamicMcpState.clients, + ], + messages: mutableMessages, + readFileState, + getAppState, + setAppState, + customSystemPrompt: options.systemPrompt, + appendSystemPrompt: options.appendSystemPrompt, + thinkingConfig: options.thinkingConfig, + agents: currentAgents, + }) + const result = await runSideQuestion({ + question, + cacheSafeParams, + }) + sendControlResponseSuccess(message, { response: result.response }) + } catch (e) { + sendControlResponseError(message, errorMessage(e)) + } + })() + } else if ( + (feature('PROACTIVE') || feature('KAIROS')) && + (message.request as { subtype: string }).subtype === 'set_proactive' + ) { + const req = message.request as unknown as { + subtype: string + enabled: boolean + } + if (req.enabled) { + if (!proactiveModule!.isProactiveActive()) { + proactiveModule!.activateProactive('command') + scheduleProactiveTick!() + } + } else { + proactiveModule!.deactivateProactive() + } + sendControlResponseSuccess(message) + } else if (message.request.subtype === 'remote_control') { + if (message.request.enabled) { + if (bridgeHandle) { + // Already connected + sendControlResponseSuccess(message, { + session_url: getRemoteSessionUrl( + bridgeHandle.bridgeSessionId, + bridgeHandle.sessionIngressUrl, + ), + connect_url: buildBridgeConnectUrl( + bridgeHandle.environmentId, + bridgeHandle.sessionIngressUrl, + ), + environment_id: bridgeHandle.environmentId, + }) + } else { + // initReplBridge surfaces gate-failure reasons via + // onStateChange('failed', detail) before returning null. + // Capture so the control-response error is actionable + // ("/login", "disabled by your organization's policy", etc.) + // instead of a generic "initialization failed". + let bridgeFailureDetail: string | undefined + try { + const { initReplBridge } = await import( + 'src/bridge/initReplBridge.js' + ) + const handle = await initReplBridge({ + onInboundMessage(msg) { + const fields = extractInboundMessageFields(msg) + if (!fields) return + const { content, uuid } = fields + enqueue({ + value: content, + mode: 'prompt' as const, + uuid, + skipSlashCommands: true, + }) + void run() + }, + onPermissionResponse(response) { + // Forward bridge permission responses into the + // stdin processing loop so they resolve pending + // permission requests from the SDK consumer. + structuredIO.injectControlResponse(response) + }, + onInterrupt() { + abortController?.abort() + }, + onSetModel(model) { + const resolved = + model === 'default' ? getDefaultMainLoopModel() : model + activeUserSpecifiedModel = resolved + setMainLoopModelOverride(resolved) + }, + onSetMaxThinkingTokens(maxTokens) { + if (maxTokens === null) { + options.thinkingConfig = undefined + } else if (maxTokens === 0) { + options.thinkingConfig = { type: 'disabled' } + } else { + options.thinkingConfig = { + type: 'enabled', + budgetTokens: maxTokens, + } + } + }, + onStateChange(state, detail) { + if (state === 'failed') { + bridgeFailureDetail = detail + } + logForDebugging( + `[bridge:sdk] State change: ${state}${detail ? ` — ${detail}` : ''}`, + ) + output.enqueue({ + type: 'system' as StdoutMessage['type'], + subtype: 'bridge_state' as string, + state, + detail, + uuid: randomUUID(), + session_id: getSessionId(), + } as StdoutMessage) + }, + initialMessages: + mutableMessages.length > 0 ? mutableMessages : undefined, + }) + if (!handle) { + sendControlResponseError( + message, + bridgeFailureDetail ?? + 'Remote Control initialization failed', + ) + } else { + bridgeHandle = handle + bridgeLastForwardedIndex = mutableMessages.length + // Forward permission requests to the bridge + structuredIO.setOnControlRequestSent(request => { + handle.sendControlRequest(request) + }) + // Cancel stale bridge permission prompts when the SDK + // consumer resolves a can_use_tool request first. + structuredIO.setOnControlRequestResolved(requestId => { + handle.sendControlCancelRequest(requestId) + }) + sendControlResponseSuccess(message, { + session_url: getRemoteSessionUrl( + handle.bridgeSessionId, + handle.sessionIngressUrl, + ), + connect_url: buildBridgeConnectUrl( + handle.environmentId, + handle.sessionIngressUrl, + ), + environment_id: handle.environmentId, + }) + } + } catch (err) { + sendControlResponseError(message, errorMessage(err)) + } + } + } else { + // Disable + if (bridgeHandle) { + structuredIO.setOnControlRequestSent(undefined) + structuredIO.setOnControlRequestResolved(undefined) + await bridgeHandle.teardown() + bridgeHandle = null + } + sendControlResponseSuccess(message) + } + } else { + // Unknown control request subtype — send an error response so + // the caller doesn't hang waiting for a reply that never comes. + sendControlResponseError( + message, + `Unsupported control request subtype: ${(message.request as { subtype: string }).subtype}`, + ) + } + continue + } else if (message.type === 'control_response') { + // Replay control_response messages when replay mode is enabled + if (options.replayUserMessages) { + output.enqueue(message) + } + continue + } else if (message.type === 'keep_alive') { + // Silently ignore keep-alive messages + continue + } else if (message.type === 'update_environment_variables') { + // Handled in structuredIO.ts, but TypeScript needs the type guard + continue + } else if (message.type === 'assistant' || message.type === 'system') { + // History replay from bridge: inject into mutableMessages as + // conversation context so the model sees prior turns. + const internalMsgs = toInternalMessages([message]) + mutableMessages.push(...internalMsgs) + // Echo assistant messages back so CCR displays them + if (message.type === 'assistant' && options.replayUserMessages) { + output.enqueue(message) + } + continue + } + // After handling control, keep-alive, env-var, assistant, and system + // messages above, only user messages should remain. + if (message.type !== 'user') { + continue + } + + // First prompt message implicitly initializes if not already done. + initialized = true + + // Check for duplicate user message - skip if already processed + if (message.uuid) { + const sessionId = getSessionId() as UUID + const existsInSession = await doesMessageExistInSession( + sessionId, + message.uuid, + ) + + // Check both historical duplicates (from file) and runtime duplicates (this session) + if (existsInSession || receivedMessageUuids.has(message.uuid)) { + logForDebugging(`Skipping duplicate user message: ${message.uuid}`) + // Send acknowledgment for duplicate message if replay mode is enabled + if (options.replayUserMessages) { + logForDebugging( + `Sending acknowledgment for duplicate user message: ${message.uuid}`, + ) + output.enqueue({ + type: 'user', + message: message.message, + session_id: sessionId, + parent_tool_use_id: null, + uuid: message.uuid, + timestamp: message.timestamp, + isReplay: true, + } as SDKUserMessageReplay) + } + // Historical dup = transcript already has this turn's output, so it + // ran but its lifecycle was never closed (interrupted before ack). + // Runtime dups don't need this — the original enqueue path closes them. + if (existsInSession) { + notifyCommandLifecycle(message.uuid, 'completed') + } + // Don't enqueue duplicate messages for execution + continue + } + + // Track this UUID to prevent runtime duplicates + trackReceivedMessageUuid(message.uuid) + } + + enqueue({ + mode: 'prompt' as const, + // file_attachments rides the protobuf catchall from the web composer. + // Same-ref no-op when absent (no 'file_attachments' key). + value: await resolveAndPrepend(message, message.message.content), + uuid: message.uuid, + priority: message.priority, + }) + // Increment prompt count for attribution tracking and save snapshot + // The snapshot persists promptCount so it survives compaction + if (feature('COMMIT_ATTRIBUTION')) { + setAppState(prev => ({ + ...prev, + attribution: incrementPromptCount(prev.attribution, snapshot => { + void recordAttributionSnapshot(snapshot).catch(error => { + logForDebugging(`Attribution: Failed to save snapshot: ${error}`) + }) + }), + })) + } + void run() + } + inputClosed = true + cronScheduler?.stop() + if (!running) { + // If a push-suggestion is in-flight, wait for it to emit before closing + // the output stream (5 s safety timeout to prevent hanging). + if (suggestionState.inflightPromise) { + await Promise.race([suggestionState.inflightPromise, sleep(5000)]) + } + suggestionState.abortController?.abort() + suggestionState.abortController = null + await finalizePendingAsyncHooks() + unsubscribeSkillChanges() + unsubscribeAuthStatus?.() + statusListeners.delete(rateLimitListener) + output.done() + } + })() + + return output +} + +/** + * Creates a CanUseToolFn that incorporates a custom permission prompt tool. + * This function converts the permissionPromptTool into a CanUseToolFn that can be used in ask.tsx + */ +export function createCanUseToolWithPermissionPrompt( + permissionPromptTool: PermissionPromptTool, +): CanUseToolFn { + const canUseTool: CanUseToolFn = async ( + tool, + input, + toolUseContext, + assistantMessage, + toolUseId, + forceDecision, + ) => { + const mainPermissionResult = + forceDecision ?? + (await hasPermissionsToUseTool( + tool, + input, + toolUseContext, + assistantMessage, + toolUseId, + )) + + // If the tool is allowed or denied, return the result + if ( + mainPermissionResult.behavior === 'allow' || + mainPermissionResult.behavior === 'deny' + ) { + return mainPermissionResult + } + + // Race the permission prompt tool against the abort signal. + // + // Why we need this: The permission prompt tool may block indefinitely waiting + // for user input (e.g., via stdin or a UI dialog). If the user triggers an + // interrupt (Ctrl+C), we need to detect it even while the tool is blocked. + // Without this race, the abort check would only run AFTER the tool completes, + // which may never happen if the tool is waiting for input that will never come. + // + // The second check (combinedSignal.aborted) handles a race condition where + // abort fires after Promise.race resolves but before we reach this check. + const { signal: combinedSignal, cleanup: cleanupAbortListener } = + createCombinedAbortSignal(toolUseContext.abortController.signal) + + // Check if already aborted before starting the race + if (combinedSignal.aborted) { + cleanupAbortListener() + return { + behavior: 'deny', + message: 'Permission prompt was aborted.', + decisionReason: { + type: 'permissionPromptTool' as const, + permissionPromptToolName: tool.name, + toolResult: undefined, + }, + } + } + + const abortPromise = new Promise<'aborted'>(resolve => { + combinedSignal.addEventListener('abort', () => resolve('aborted'), { + once: true, + }) + }) + + const toolCallPromise = permissionPromptTool.call( + { + tool_name: tool.name, + input, + tool_use_id: toolUseId, + }, + toolUseContext, + canUseTool, + assistantMessage, + ) + + const raceResult = await Promise.race([toolCallPromise, abortPromise]) + cleanupAbortListener() + + if (raceResult === 'aborted' || combinedSignal.aborted) { + return { + behavior: 'deny', + message: 'Permission prompt was aborted.', + decisionReason: { + type: 'permissionPromptTool' as const, + permissionPromptToolName: tool.name, + toolResult: undefined, + }, + } + } + + // TypeScript narrowing: after the abort check, raceResult must be ToolResult + const result = raceResult as Awaited + + const permissionToolResultBlockParam = + permissionPromptTool.mapToolResultToToolResultBlockParam(result.data, '1') + if ( + !permissionToolResultBlockParam.content || + !Array.isArray(permissionToolResultBlockParam.content) || + !permissionToolResultBlockParam.content[0] || + permissionToolResultBlockParam.content[0].type !== 'text' || + typeof permissionToolResultBlockParam.content[0].text !== 'string' + ) { + throw new Error( + 'Permission prompt tool returned an invalid result. Expected a single text block param with type="text" and a string text value.', + ) + } + return permissionPromptToolResultToPermissionDecision( + permissionToolOutputSchema().parse( + safeParseJSON(permissionToolResultBlockParam.content[0].text), + ), + permissionPromptTool, + input, + toolUseContext, + ) + } + return canUseTool +} + +// Exported for testing — regression: this used to crash at construction when +// getMcpTools() was empty (before per-server connects populated appState). +export function getCanUseToolFn( + permissionPromptToolName: string | undefined, + structuredIO: StructuredIO, + getMcpTools: () => Tool[], + onPermissionPrompt?: (details: RequiresActionDetails) => void, +): CanUseToolFn { + if (permissionPromptToolName === 'stdio') { + return structuredIO.createCanUseTool(onPermissionPrompt) + } + if (!permissionPromptToolName) { + return async ( + tool, + input, + toolUseContext, + assistantMessage, + toolUseId, + forceDecision, + ) => + forceDecision ?? + (await hasPermissionsToUseTool( + tool, + input, + toolUseContext, + assistantMessage, + toolUseId, + )) + } + // Lazy lookup: MCP connects are per-server incremental in print mode, so + // the tool may not be in appState yet at init time. Resolve on first call + // (first permission prompt), by which point connects have had time to finish. + let resolved: CanUseToolFn | null = null + return async ( + tool, + input, + toolUseContext, + assistantMessage, + toolUseId, + forceDecision, + ) => { + if (!resolved) { + const mcpTools = getMcpTools() + const permissionPromptTool = mcpTools.find(t => + toolMatchesName(t, permissionPromptToolName), + ) as PermissionPromptTool | undefined + if (!permissionPromptTool) { + const error = `Error: MCP tool ${permissionPromptToolName} (passed via --permission-prompt-tool) not found. Available MCP tools: ${mcpTools.map(t => t.name).join(', ') || 'none'}` + process.stderr.write(`${error}\n`) + gracefulShutdownSync(1) + throw new Error(error) + } + if (!permissionPromptTool.inputJSONSchema) { + const error = `Error: tool ${permissionPromptToolName} (passed via --permission-prompt-tool) must be an MCP tool` + process.stderr.write(`${error}\n`) + gracefulShutdownSync(1) + throw new Error(error) + } + resolved = createCanUseToolWithPermissionPrompt(permissionPromptTool) + } + return resolved( + tool, + input, + toolUseContext, + assistantMessage, + toolUseId, + forceDecision, + ) + } +} + +async function handleInitializeRequest( + request: SDKControlInitializeRequest, + requestId: string, + initialized: boolean, + output: Stream, + commands: Command[], + modelInfos: ModelInfo[], + structuredIO: StructuredIO, + enableAuthStatus: boolean, + options: { + systemPrompt: string | undefined + appendSystemPrompt: string | undefined + agent?: string | undefined + userSpecifiedModel?: string | undefined + [key: string]: unknown + }, + agents: AgentDefinition[], + getAppState: () => AppState, +): Promise { + if (initialized) { + output.enqueue({ + type: 'control_response', + response: { + subtype: 'error', + error: 'Already initialized', + request_id: requestId, + pending_permission_requests: + structuredIO.getPendingPermissionRequests(), + }, + }) + return + } + + // Apply systemPrompt/appendSystemPrompt from stdin to avoid ARG_MAX limits + if (request.systemPrompt !== undefined) { + options.systemPrompt = request.systemPrompt + } + if (request.appendSystemPrompt !== undefined) { + options.appendSystemPrompt = request.appendSystemPrompt + } + if (request.promptSuggestions !== undefined) { + options.promptSuggestions = request.promptSuggestions + } + + // Merge agents from stdin to avoid ARG_MAX limits + if (request.agents) { + const stdinAgents = parseAgentsFromJson(request.agents, 'flagSettings') + agents.push(...stdinAgents) + } + + // Re-evaluate main thread agent after SDK agents are merged + // This allows --agent to reference agents defined via SDK + if (options.agent) { + // If main.tsx already found this agent (filesystem-defined), it already + // applied systemPrompt/model/initialPrompt. Skip to avoid double-apply. + const alreadyResolved = getMainThreadAgentType() === options.agent + const mainThreadAgent = agents.find(a => a.agentType === options.agent) + if (mainThreadAgent && !alreadyResolved) { + // Update the main thread agent type in bootstrap state + setMainThreadAgentType(mainThreadAgent.agentType) + + // Apply the agent's system prompt if user hasn't specified a custom one + // SDK agents are always custom agents (not built-in), so getSystemPrompt() takes no args + if (!options.systemPrompt && !isBuiltInAgent(mainThreadAgent)) { + const agentSystemPrompt = mainThreadAgent.getSystemPrompt() + if (agentSystemPrompt) { + options.systemPrompt = agentSystemPrompt + } + } + + // Apply the agent's model if user didn't specify one and agent has a model + if ( + !options.userSpecifiedModel && + mainThreadAgent.model && + mainThreadAgent.model !== 'inherit' + ) { + const agentModel = parseUserSpecifiedModel(mainThreadAgent.model) + setMainLoopModelOverride(agentModel) + } + + // SDK-defined agents arrive via init, so main.tsx's lookup missed them. + if (mainThreadAgent.initialPrompt) { + structuredIO.prependUserMessage(mainThreadAgent.initialPrompt) + } + } else if (mainThreadAgent?.initialPrompt) { + // Filesystem-defined agent (alreadyResolved by main.tsx). main.tsx + // handles initialPrompt for the string inputPrompt case, but when + // inputPrompt is an AsyncIterable (SDK stream-json), it can't + // concatenate — fall back to prependUserMessage here. + structuredIO.prependUserMessage(mainThreadAgent.initialPrompt) + } + } + + const settings = getSettings_DEPRECATED() + const outputStyle = settings?.outputStyle || DEFAULT_OUTPUT_STYLE_NAME + const availableOutputStyles = await getAllOutputStyles(getCwd()) + + // Get account information + const accountInfo = getAccountInformation() + if (request.hooks) { + const hooks: Partial> = {} + for (const [event, matchers] of Object.entries(request.hooks)) { + hooks[event as HookEvent] = matchers.map(matcher => { + const callbacks = matcher.hookCallbackIds.map(callbackId => { + return structuredIO.createHookCallback(callbackId, matcher.timeout) + }) + return { + matcher: matcher.matcher, + hooks: callbacks, + } + }) + } + registerHookCallbacks(hooks) + } + if (request.jsonSchema) { + setInitJsonSchema(request.jsonSchema) + } + const initResponse: SDKControlInitializeResponse = { + commands: commands + .filter(cmd => cmd.userInvocable !== false) + .map(cmd => ({ + name: getCommandName(cmd), + description: formatDescriptionWithSource(cmd), + argumentHint: cmd.argumentHint || '', + })), + agents: agents.map(agent => ({ + name: agent.agentType, + description: agent.whenToUse, + // 'inherit' is an internal sentinel; normalize to undefined for the public API + model: agent.model === 'inherit' ? undefined : agent.model, + })), + output_style: outputStyle, + available_output_styles: Object.keys(availableOutputStyles), + models: modelInfos, + account: { + email: accountInfo?.email, + organization: accountInfo?.organization, + subscriptionType: accountInfo?.subscription, + tokenSource: accountInfo?.tokenSource, + apiKeySource: accountInfo?.apiKeySource, + // getAccountInformation() returns undefined under 3P providers, so the + // other fields are all absent. apiProvider disambiguates "not logged + // in" (firstParty + tokenSource:none) from "3P, login not applicable". + apiProvider: getAPIProvider(), + }, + pid: process.pid, + } + + if (isFastModeEnabled() && isFastModeAvailable()) { + const appState = getAppState() + initResponse.fast_mode_state = getFastModeState( + options.userSpecifiedModel ?? null, + appState.fastMode, + ) + } + + output.enqueue({ + type: 'control_response', + response: { + subtype: 'success', + request_id: requestId, + response: initResponse, + }, + }) + + // After the initialize message, check the auth status- + // This will get notified of changes, but we also want to send the + // initial state. + if (enableAuthStatus) { + const authStatusManager = AwsAuthStatusManager.getInstance() + const status = authStatusManager.getStatus() + if (status) { + output.enqueue({ + type: 'auth_status', + isAuthenticating: status.isAuthenticating, + output: status.output, + error: status.error, + uuid: randomUUID(), + session_id: getSessionId(), + }) + } + } +} + +async function handleRewindFiles( + userMessageId: UUID, + appState: AppState, + setAppState: (updater: (prev: AppState) => AppState) => void, + dryRun: boolean, +): Promise { + if (!fileHistoryEnabled()) { + return { canRewind: false, error: 'File rewinding is not enabled.' } + } + if (!fileHistoryCanRestore(appState.fileHistory, userMessageId)) { + return { + canRewind: false, + error: 'No file checkpoint found for this message.', + } + } + + if (dryRun) { + const diffStats = await fileHistoryGetDiffStats( + appState.fileHistory, + userMessageId, + ) + return { + canRewind: true, + filesChanged: diffStats?.filesChanged, + insertions: diffStats?.insertions, + deletions: diffStats?.deletions, + } + } + + try { + await fileHistoryRewind( + updater => + setAppState(prev => ({ + ...prev, + fileHistory: updater(prev.fileHistory), + })), + userMessageId, + ) + } catch (error) { + return { + canRewind: false, + error: `Failed to rewind: ${errorMessage(error)}`, + } + } + + return { canRewind: true } +} + +function handleSetPermissionMode( + request: { mode: InternalPermissionMode }, + requestId: string, + toolPermissionContext: ToolPermissionContext, + output: Stream, +): ToolPermissionContext { + // Check if trying to switch to bypassPermissions mode + if (request.mode === 'bypassPermissions') { + if (isBypassPermissionsModeDisabled()) { + output.enqueue({ + type: 'control_response', + response: { + subtype: 'error', + request_id: requestId, + error: + 'Cannot set permission mode to bypassPermissions because it is disabled by settings or configuration', + }, + }) + return toolPermissionContext + } + if (!toolPermissionContext.isBypassPermissionsModeAvailable) { + output.enqueue({ + type: 'control_response', + response: { + subtype: 'error', + request_id: requestId, + error: + 'Cannot set permission mode to bypassPermissions because the session was not launched with --dangerously-skip-permissions', + }, + }) + return toolPermissionContext + } + } + + // Check if trying to switch to auto mode without the classifier gate + if ( + feature('TRANSCRIPT_CLASSIFIER') && + request.mode === 'auto' && + !isAutoModeGateEnabled() + ) { + const reason = getAutoModeUnavailableReason() + output.enqueue({ + type: 'control_response', + response: { + subtype: 'error', + request_id: requestId, + error: reason + ? `Cannot set permission mode to auto: ${getAutoModeUnavailableNotification(reason)}` + : 'Cannot set permission mode to auto', + }, + }) + return toolPermissionContext + } + + // Allow the mode switch + output.enqueue({ + type: 'control_response', + response: { + subtype: 'success', + request_id: requestId, + response: { + mode: request.mode, + }, + }, + }) + + return { + ...transitionPermissionMode( + toolPermissionContext.mode, + request.mode, + toolPermissionContext, + ), + mode: request.mode, + } +} + +/** + * IDE-triggered channel enable. Derives the ChannelEntry from the connection's + * pluginSource (IDE can't spoof kind/marketplace — we only take the server + * name), appends it to session allowedChannels, and runs the full gate. On + * gate failure, rolls back the append. On success, registers a notification + * handler that enqueues channel messages at priority:'next' — drainCommandQueue + * picks them up between turns. + * + * Intentionally does NOT register the claude/channel/permission handler that + * useManageMCPConnections sets up for interactive mode. That handler resolves + * a pending dialog inside handleInteractivePermission — but print.ts never + * calls handleInteractivePermission. When SDK permission lands on 'ask', it + * goes to the consumer's canUseTool callback over stdio; there is no CLI-side + * dialog for a remote "yes tbxkq" to resolve. If an IDE wants channel-relayed + * tool approval, that's IDE-side plumbing against its own pending-map. (Also + * gated separately by tengu_harbor_permissions — not yet shipping on + * interactive either.) + */ +function handleChannelEnable( + requestId: string, + serverName: string, + connectionPool: readonly MCPServerConnection[], + output: Stream, +): void { + const respondError = (error: string) => + output.enqueue({ + type: 'control_response', + response: { subtype: 'error', request_id: requestId, error }, + }) + + if (!(feature('KAIROS') || feature('KAIROS_CHANNELS'))) { + return respondError('channels feature not available in this build') + } + + // Only a 'connected' client has .capabilities and .client to register the + // handler on. The pool spread at the call site matches mcp_status. + const connection = connectionPool.find( + c => c.name === serverName && c.type === 'connected', + ) + if (!connection || connection.type !== 'connected') { + return respondError(`server ${serverName} is not connected`) + } + + const pluginSource = connection.config.pluginSource + const parsed = pluginSource ? parsePluginIdentifier(pluginSource) : undefined + if (!parsed?.marketplace) { + // No pluginSource or @-less source — can never pass the {plugin, + // marketplace}-keyed allowlist. Short-circuit with the same reason the + // gate would produce. + return respondError( + `server ${serverName} is not plugin-sourced; channel_enable requires a marketplace plugin`, + ) + } + + const entry: ChannelEntry = { + kind: 'plugin', + name: parsed.name, + marketplace: parsed.marketplace, + } + // Idempotency: don't double-append on repeat enable. + const prior = getAllowedChannels() + const already = prior.some( + e => + e.kind === 'plugin' && + e.name === entry.name && + e.marketplace === entry.marketplace, + ) + if (!already) setAllowedChannels([...prior, entry]) + + const gate = gateChannelServer( + serverName, + connection.capabilities, + pluginSource, + ) + if (gate.action === 'skip') { + // Rollback — only remove the entry we appended. + if (!already) setAllowedChannels(prior) + return respondError(gate.reason) + } + + const pluginId = + `${entry.name}@${entry.marketplace}` as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + logMCPDebug(serverName, 'Channel notifications registered') + logEvent('tengu_mcp_channel_enable', { plugin: pluginId }) + + // Identical enqueue shape to the interactive register block in + // useManageMCPConnections. drainCommandQueue processes it between turns — + // channel messages queue at priority 'next' and are seen by the model on + // the turn after they arrive. + connection.client.setNotificationHandler( + ChannelMessageNotificationSchema(), + async notification => { + const { content, meta } = notification.params + logMCPDebug( + serverName, + `notifications/claude/channel: ${content.slice(0, 80)}`, + ) + logEvent('tengu_mcp_channel_message', { + content_length: content.length, + meta_key_count: Object.keys(meta ?? {}).length, + entry_kind: + 'plugin' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + is_dev: false, + plugin: pluginId, + }) + enqueue({ + mode: 'prompt', + value: wrapChannelMessage(serverName, content, meta), + priority: 'next', + isMeta: true, + origin: { kind: 'channel', server: serverName }, + skipSlashCommands: true, + }) + }, + ) + + output.enqueue({ + type: 'control_response', + response: { + subtype: 'success', + request_id: requestId, + response: undefined, + }, + }) +} + +/** + * Re-register the channel notification handler after mcp_reconnect / + * mcp_toggle creates a new client. handleChannelEnable bound the handler to + * the OLD client object; allowedChannels survives the reconnect but the + * handler binding does not. Without this, channel messages silently drop + * after a reconnect while the IDE still believes the channel is live. + * + * Mirrors the interactive CLI's onConnectionAttempt in + * useManageMCPConnections, which re-gates on every new connection. Paired + * with registerElicitationHandlers at the same call sites. + * + * No-op if the server was never channel-enabled: gateChannelServer calls + * findChannelEntry internally and returns skip/session for an unlisted + * server, so reconnecting a non-channel MCP server costs one feature-flag + * check. + */ +function reregisterChannelHandlerAfterReconnect( + connection: MCPServerConnection, +): void { + if (!(feature('KAIROS') || feature('KAIROS_CHANNELS'))) return + if (connection.type !== 'connected') return + + const gate = gateChannelServer( + connection.name, + connection.capabilities, + connection.config.pluginSource, + ) + if (gate.action !== 'register') return + + const entry = findChannelEntry(connection.name, getAllowedChannels()) + const pluginId = + entry?.kind === 'plugin' + ? (`${entry.name}@${entry.marketplace}` as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS) + : undefined + + logMCPDebug( + connection.name, + 'Channel notifications re-registered after reconnect', + ) + connection.client.setNotificationHandler( + ChannelMessageNotificationSchema(), + async notification => { + const { content, meta } = notification.params + logMCPDebug( + connection.name, + `notifications/claude/channel: ${content.slice(0, 80)}`, + ) + logEvent('tengu_mcp_channel_message', { + content_length: content.length, + meta_key_count: Object.keys(meta ?? {}).length, + entry_kind: + entry?.kind as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + is_dev: entry?.dev ?? false, + plugin: pluginId, + }) + enqueue({ + mode: 'prompt', + value: wrapChannelMessage(connection.name, content, meta), + priority: 'next', + isMeta: true, + origin: { kind: 'channel', server: connection.name }, + skipSlashCommands: true, + }) + }, + ) +} + +/** + * Emits an error message in the correct format based on outputFormat. + * When using stream-json, writes JSON to stdout; otherwise writes plain text to stderr. + */ +function emitLoadError( + message: string, + outputFormat: string | undefined, +): void { + if (outputFormat === 'stream-json') { + const errorResult = { + type: 'result', + subtype: 'error_during_execution', + duration_ms: 0, + duration_api_ms: 0, + is_error: true, + num_turns: 0, + stop_reason: null, + session_id: getSessionId(), + total_cost_usd: 0, + usage: EMPTY_USAGE, + modelUsage: {}, + permission_denials: [], + uuid: randomUUID(), + errors: [message], + } + process.stdout.write(jsonStringify(errorResult) + '\n') + } else { + process.stderr.write(message + '\n') + } +} + +/** + * Removes an interrupted user message and its synthetic assistant sentinel + * from the message array. Used during gateway-triggered restarts to clean up + * the message history before re-enqueuing the interrupted prompt. + * + * @internal Exported for testing + */ +export function removeInterruptedMessage( + messages: Message[], + interruptedUserMessage: NormalizedUserMessage, +): void { + const idx = messages.findIndex(m => m.uuid === interruptedUserMessage.uuid) + if (idx !== -1) { + // Remove the user message and the sentinel that immediately follows it. + // splice safely handles the case where idx is the last element. + messages.splice(idx, 2) + } +} + +type LoadInitialMessagesResult = { + messages: Message[] + turnInterruptionState?: TurnInterruptionState + agentSetting?: string +} + +async function loadInitialMessages( + setAppState: (f: (prev: AppState) => AppState) => void, + options: { + continue: boolean | undefined + teleport: string | true | null | undefined + resume: string | boolean | undefined + resumeSessionAt: string | undefined + forkSession: boolean | undefined + outputFormat: string | undefined + sessionStartHooksPromise?: ReturnType + restoredWorkerState: Promise + }, +): Promise { + const persistSession = !isSessionPersistenceDisabled() + // Handle continue in print mode + if (options.continue) { + try { + logEvent('tengu_continue_print', {}) + + const result = await loadConversationForResume( + undefined /* sessionId */, + undefined /* file path */, + ) + if (result) { + // Match coordinator mode to the resumed session's mode + if (feature('COORDINATOR_MODE') && coordinatorModeModule) { + const warning = coordinatorModeModule.matchSessionMode(result.mode) + if (warning) { + process.stderr.write(warning + '\n') + // Refresh agent definitions to reflect the mode switch + const { + getAgentDefinitionsWithOverrides, + getActiveAgentsFromList, + } = + // eslint-disable-next-line @typescript-eslint/no-require-imports + require('../tools/AgentTool/loadAgentsDir.js') as typeof import('../tools/AgentTool/loadAgentsDir.js') + getAgentDefinitionsWithOverrides.cache.clear?.() + const freshAgentDefs = await getAgentDefinitionsWithOverrides( + getCwd(), + ) + + setAppState(prev => ({ + ...prev, + agentDefinitions: { + ...freshAgentDefs, + allAgents: freshAgentDefs.allAgents, + activeAgents: getActiveAgentsFromList(freshAgentDefs.allAgents), + }, + })) + } + } + + // Reuse the resumed session's ID + if (!options.forkSession) { + if (result.sessionId) { + switchSession( + asSessionId(result.sessionId), + result.fullPath ? dirname(result.fullPath) : null, + ) + if (persistSession) { + await resetSessionFilePointer() + } + } + } + restoreSessionStateFromLog(result, setAppState) + + // Restore session metadata so it's re-appended on exit via reAppendSessionMetadata + restoreSessionMetadata( + options.forkSession + ? { ...result, worktreeSession: undefined } + : result, + ) + + // Write mode entry for the resumed session + if (feature('COORDINATOR_MODE') && coordinatorModeModule) { + saveMode( + coordinatorModeModule.isCoordinatorMode() + ? 'coordinator' + : 'normal', + ) + } + + return { + messages: result.messages, + turnInterruptionState: result.turnInterruptionState, + agentSetting: result.agentSetting, + } + } + } catch (error) { + logError(error) + gracefulShutdownSync(1) + return { messages: [] } + } + } + + // Handle teleport in print mode + if (options.teleport) { + try { + if (!isPolicyAllowed('allow_remote_sessions')) { + throw new Error( + "Remote sessions are disabled by your organization's policy.", + ) + } + + logEvent('tengu_teleport_print', {}) + + if (typeof options.teleport !== 'string') { + throw new Error('No session ID provided for teleport') + } + + const { + checkOutTeleportedSessionBranch, + processMessagesForTeleportResume, + teleportResumeCodeSession, + validateGitState, + } = await import('src/utils/teleport.js') + await validateGitState() + const teleportResult = await teleportResumeCodeSession(options.teleport) + const { branchError } = await checkOutTeleportedSessionBranch( + teleportResult.branch, + ) + return { + messages: processMessagesForTeleportResume( + teleportResult.log, + branchError, + ), + } + } catch (error) { + logError(error) + gracefulShutdownSync(1) + return { messages: [] } + } + } + + // Handle resume in print mode (accepts session ID or URL) + // URLs are [ANT-ONLY] + if (options.resume) { + try { + logEvent('tengu_resume_print', {}) + + // In print mode - we require a valid session ID, JSONL file or URL + const parsedSessionId = parseSessionIdentifier( + typeof options.resume === 'string' ? options.resume : '', + ) + if (!parsedSessionId) { + let errorMessage = + 'Error: --resume requires a valid session ID when used with --print. Usage: claude -p --resume ' + if (typeof options.resume === 'string') { + errorMessage += `. Session IDs must be in UUID format (e.g., 550e8400-e29b-41d4-a716-446655440000). Provided value "${options.resume}" is not a valid UUID` + } + emitLoadError(errorMessage, options.outputFormat) + gracefulShutdownSync(1) + return { messages: [] } + } + + // Hydrate local transcript from remote before loading + if (isEnvTruthy(process.env.CLAUDE_CODE_USE_CCR_V2)) { + // Await restore alongside hydration so SSE catchup lands on + // restored state, not a fresh default. + const [, metadata] = await Promise.all([ + hydrateFromCCRv2InternalEvents(parsedSessionId.sessionId), + options.restoredWorkerState, + ]) + if (metadata) { + setAppState(externalMetadataToAppState(metadata)) + if (typeof metadata.model === 'string') { + setMainLoopModelOverride(metadata.model) + } + } + } else if ( + parsedSessionId.isUrl && + parsedSessionId.ingressUrl && + isEnvTruthy(process.env.ENABLE_SESSION_PERSISTENCE) + ) { + // v1: fetch session logs from Session Ingress + await hydrateRemoteSession( + parsedSessionId.sessionId, + parsedSessionId.ingressUrl, + ) + } + + // Load the conversation with the specified session ID + const result = await loadConversationForResume( + parsedSessionId.sessionId, + parsedSessionId.jsonlFile || undefined, + ) + + // hydrateFromCCRv2InternalEvents writes an empty transcript file for + // fresh sessions (writeFile(sessionFile, '') with zero events), so + // loadConversationForResume returns {messages: []} not null. Treat + // empty the same as null so SessionStart still fires. + if (!result || result.messages.length === 0) { + // For URL-based or CCR v2 resume, start with empty session (it was hydrated but empty) + if ( + parsedSessionId.isUrl || + isEnvTruthy(process.env.CLAUDE_CODE_USE_CCR_V2) + ) { + // Execute SessionStart hooks for startup since we're starting a new session + return { + messages: await (options.sessionStartHooksPromise ?? + processSessionStartHooks('startup')), + } + } else { + emitLoadError( + `No conversation found with session ID: ${parsedSessionId.sessionId}`, + options.outputFormat, + ) + gracefulShutdownSync(1) + return { messages: [] } + } + } + + // Handle resumeSessionAt feature + if (options.resumeSessionAt) { + const index = result.messages.findIndex( + m => m.uuid === options.resumeSessionAt, + ) + if (index < 0) { + emitLoadError( + `No message found with message.uuid of: ${options.resumeSessionAt}`, + options.outputFormat, + ) + gracefulShutdownSync(1) + return { messages: [] } + } + + result.messages = index >= 0 ? result.messages.slice(0, index + 1) : [] + } + + // Match coordinator mode to the resumed session's mode + if (feature('COORDINATOR_MODE') && coordinatorModeModule) { + const warning = coordinatorModeModule.matchSessionMode(result.mode) + if (warning) { + process.stderr.write(warning + '\n') + // Refresh agent definitions to reflect the mode switch + const { getAgentDefinitionsWithOverrides, getActiveAgentsFromList } = + // eslint-disable-next-line @typescript-eslint/no-require-imports + require('../tools/AgentTool/loadAgentsDir.js') as typeof import('../tools/AgentTool/loadAgentsDir.js') + getAgentDefinitionsWithOverrides.cache.clear?.() + const freshAgentDefs = await getAgentDefinitionsWithOverrides( + getCwd(), + ) + + setAppState(prev => ({ + ...prev, + agentDefinitions: { + ...freshAgentDefs, + allAgents: freshAgentDefs.allAgents, + activeAgents: getActiveAgentsFromList(freshAgentDefs.allAgents), + }, + })) + } + } + + // Reuse the resumed session's ID + if (!options.forkSession && result.sessionId) { + switchSession( + asSessionId(result.sessionId), + result.fullPath ? dirname(result.fullPath) : null, + ) + if (persistSession) { + await resetSessionFilePointer() + } + } + restoreSessionStateFromLog(result, setAppState) + + // Restore session metadata so it's re-appended on exit via reAppendSessionMetadata + restoreSessionMetadata( + options.forkSession + ? { ...result, worktreeSession: undefined } + : result, + ) + + // Write mode entry for the resumed session + if (feature('COORDINATOR_MODE') && coordinatorModeModule) { + saveMode( + coordinatorModeModule.isCoordinatorMode() ? 'coordinator' : 'normal', + ) + } + + return { + messages: result.messages, + turnInterruptionState: result.turnInterruptionState, + agentSetting: result.agentSetting, + } + } catch (error) { + logError(error) + const errorMessage = + error instanceof Error + ? `Failed to resume session: ${error.message}` + : 'Failed to resume session with --print mode' + emitLoadError(errorMessage, options.outputFormat) + gracefulShutdownSync(1) + return { messages: [] } + } + } + + // Join the SessionStart hooks promise kicked in main.tsx (or run fresh if + // it wasn't kicked — e.g. --continue with no prior session falls through + // here with sessionStartHooksPromise undefined because main.tsx guards on continue) + return { + messages: await (options.sessionStartHooksPromise ?? + processSessionStartHooks('startup')), + } +} + +function getStructuredIO( + inputPrompt: string | AsyncIterable, + options: { + sdkUrl: string | undefined + replayUserMessages?: boolean + }, +): StructuredIO { + let inputStream: AsyncIterable + if (typeof inputPrompt === 'string') { + if (inputPrompt.trim() !== '') { + // Normalize to a streaming input. + inputStream = fromArray([ + jsonStringify({ + type: 'user', + session_id: '', + message: { + role: 'user', + content: inputPrompt, + }, + parent_tool_use_id: null, + } satisfies SDKUserMessage), + ]) + } else { + // Empty string - create empty stream + inputStream = fromArray([]) + } + } else { + inputStream = inputPrompt + } + + // Use RemoteIO if sdkUrl is provided, otherwise use regular StructuredIO + return options.sdkUrl + ? new RemoteIO(options.sdkUrl, inputStream, options.replayUserMessages) + : new StructuredIO(inputStream, options.replayUserMessages) +} + +/** + * Handles unexpected permission responses by looking up the unresolved tool + * call in the transcript and enqueuing it for execution. + * + * Returns true if a permission was enqueued, false otherwise. + */ +export async function handleOrphanedPermissionResponse({ + message, + setAppState, + onEnqueued, + handledToolUseIds, +}: { + message: SDKControlResponse + setAppState: (f: (prev: AppState) => AppState) => void + onEnqueued?: () => void + handledToolUseIds: Set +}): Promise { + if ( + message.response.subtype === 'success' && + message.response.response?.toolUseID && + typeof message.response.response.toolUseID === 'string' + ) { + const permissionResult = message.response.response as PermissionResult + const { toolUseID } = permissionResult + if (!toolUseID) { + return false + } + + logForDebugging( + `handleOrphanedPermissionResponse: received orphaned control_response for toolUseID=${toolUseID} request_id=${message.response.request_id}`, + ) + + // Prevent re-processing the same orphaned tool_use. Without this guard, + // duplicate control_response deliveries (e.g. from WebSocket reconnect) + // cause the same tool to be executed multiple times, producing duplicate + // tool_use IDs in the messages array and a 400 error from the API. + // Once corrupted, every retry accumulates more duplicates. + if (handledToolUseIds.has(toolUseID)) { + logForDebugging( + `handleOrphanedPermissionResponse: skipping duplicate orphaned permission for toolUseID=${toolUseID} (already handled)`, + ) + return false + } + + const assistantMessage = await findUnresolvedToolUse(toolUseID) + if (!assistantMessage) { + logForDebugging( + `handleOrphanedPermissionResponse: no unresolved tool_use found for toolUseID=${toolUseID} (already resolved in transcript)`, + ) + return false + } + + handledToolUseIds.add(toolUseID) + logForDebugging( + `handleOrphanedPermissionResponse: enqueuing orphaned permission for toolUseID=${toolUseID} messageID=${assistantMessage.message.id}`, + ) + enqueue({ + mode: 'orphaned-permission' as const, + value: [], + orphanedPermission: { + permissionResult, + assistantMessage, + }, + }) + + onEnqueued?.() + return true + } + return false +} + +export type DynamicMcpState = { + clients: MCPServerConnection[] + tools: Tools + configs: Record +} + +/** + * Converts a process transport config to a scoped config. + * The types are structurally compatible, so we just add the scope. + */ +function toScopedConfig( + config: McpServerConfigForProcessTransport, +): ScopedMcpServerConfig { + // McpServerConfigForProcessTransport is a subset of McpServerConfig + // (it excludes IDE-specific types like sse-ide and ws-ide) + // Adding scope makes it a valid ScopedMcpServerConfig + return { ...config, scope: 'dynamic' } as ScopedMcpServerConfig +} + +/** + * State for SDK MCP servers that run in the SDK process. + */ +export type SdkMcpState = { + configs: Record + clients: MCPServerConnection[] + tools: Tools +} + +/** + * Result of handleMcpSetServers - contains new state and response data. + */ +export type McpSetServersResult = { + response: SDKControlMcpSetServersResponse + newSdkState: SdkMcpState + newDynamicState: DynamicMcpState + sdkServersChanged: boolean +} + +/** + * Handles mcp_set_servers requests by processing both SDK and process-based servers. + * SDK servers run in the SDK process; process-based servers are spawned by the CLI. + * + * Applies enterprise allowedMcpServers/deniedMcpServers policy — same filter as + * --mcp-config (see filterMcpServersByPolicy call in main.tsx). Without this, + * SDK V2 Query.setMcpServers() was a second policy bypass vector. Blocked servers + * are reported in response.errors so the SDK consumer knows why they weren't added. + */ +export async function handleMcpSetServers( + servers: Record, + sdkState: SdkMcpState, + dynamicState: DynamicMcpState, + setAppState: (f: (prev: AppState) => AppState) => void, +): Promise { + // Enforce enterprise MCP policy on process-based servers (stdio/http/sse). + // Mirrors the --mcp-config filter in main.tsx — both user-controlled injection + // paths must have the same gate. type:'sdk' servers are exempt (SDK-managed, + // CLI never spawns/connects for them — see filterMcpServersByPolicy jsdoc). + // Blocked servers go into response.errors so the SDK caller sees why. + const { allowed: allowedServers, blocked } = filterMcpServersByPolicy(servers) + const policyErrors: Record = {} + for (const name of blocked) { + policyErrors[name] = + 'Blocked by enterprise policy (allowedMcpServers/deniedMcpServers)' + } + + // Separate SDK servers from process-based servers + const sdkServers: Record = {} + const processServers: Record = {} + + for (const [name, config] of Object.entries(allowedServers)) { + if (config.type === 'sdk') { + sdkServers[name] = config + } else { + processServers[name] = config + } + } + + // Handle SDK servers + const currentSdkNames = new Set(Object.keys(sdkState.configs)) + const newSdkNames = new Set(Object.keys(sdkServers)) + const sdkAdded: string[] = [] + const sdkRemoved: string[] = [] + + const newSdkConfigs = { ...sdkState.configs } + let newSdkClients = [...sdkState.clients] + let newSdkTools = [...sdkState.tools] + + // Remove SDK servers no longer in desired state + for (const name of currentSdkNames) { + if (!newSdkNames.has(name)) { + const client = newSdkClients.find(c => c.name === name) + if (client && client.type === 'connected') { + await client.cleanup() + } + newSdkClients = newSdkClients.filter(c => c.name !== name) + const prefix = `mcp__${name}__` + newSdkTools = newSdkTools.filter(t => !t.name.startsWith(prefix)) + delete newSdkConfigs[name] + sdkRemoved.push(name) + } + } + + // Add new SDK servers as pending - they'll be upgraded to connected + // when updateSdkMcp() runs on the next query + for (const [name, config] of Object.entries(sdkServers)) { + if (!currentSdkNames.has(name)) { + newSdkConfigs[name] = config + const pendingClient: MCPServerConnection = { + type: 'pending', + name, + config: { ...config, scope: 'dynamic' as const }, + } + newSdkClients = [...newSdkClients, pendingClient] + sdkAdded.push(name) + } + } + + // Handle process-based servers + const processResult = await reconcileMcpServers( + processServers, + dynamicState, + setAppState, + ) + + return { + response: { + added: [...sdkAdded, ...processResult.response.added], + removed: [...sdkRemoved, ...processResult.response.removed], + errors: { ...policyErrors, ...processResult.response.errors }, + }, + newSdkState: { + configs: newSdkConfigs, + clients: newSdkClients, + tools: newSdkTools, + }, + newDynamicState: processResult.newState, + sdkServersChanged: sdkAdded.length > 0 || sdkRemoved.length > 0, + } +} + +/** + * Reconciles the current set of dynamic MCP servers with a new desired state. + * Handles additions, removals, and config changes. + */ +export async function reconcileMcpServers( + desiredConfigs: Record, + currentState: DynamicMcpState, + setAppState: (f: (prev: AppState) => AppState) => void, +): Promise<{ + response: SDKControlMcpSetServersResponse + newState: DynamicMcpState +}> { + const currentNames = new Set(Object.keys(currentState.configs)) + const desiredNames = new Set(Object.keys(desiredConfigs)) + + const toRemove = [...currentNames].filter(n => !desiredNames.has(n)) + const toAdd = [...desiredNames].filter(n => !currentNames.has(n)) + + // Check for config changes (same name, different config) + const toCheck = [...currentNames].filter(n => desiredNames.has(n)) + const toReplace = toCheck.filter(name => { + const currentConfig = currentState.configs[name] + const desiredConfigRaw = desiredConfigs[name] + if (!currentConfig || !desiredConfigRaw) return true + const desiredConfig = toScopedConfig(desiredConfigRaw) + return !areMcpConfigsEqual(currentConfig, desiredConfig) + }) + + const removed: string[] = [] + const added: string[] = [] + const errors: Record = {} + + let newClients = [...currentState.clients] + let newTools = [...currentState.tools] + + // Remove old servers (including ones being replaced) + for (const name of [...toRemove, ...toReplace]) { + const client = newClients.find(c => c.name === name) + const config = currentState.configs[name] + if (client && config) { + if (client.type === 'connected') { + try { + await client.cleanup() + } catch (e) { + logError(e) + } + } + // Clear the memoization cache + await clearServerCache(name, config) + } + + // Remove tools from this server + const prefix = `mcp__${name}__` + newTools = newTools.filter(t => !t.name.startsWith(prefix)) + + // Remove from clients list + newClients = newClients.filter(c => c.name !== name) + + // Track removal (only for actually removed, not replaced) + if (toRemove.includes(name)) { + removed.push(name) + } + } + + // Add new servers (including replacements) + for (const name of [...toAdd, ...toReplace]) { + const config = desiredConfigs[name] + if (!config) continue + const scopedConfig = toScopedConfig(config) + + // SDK servers are managed by the SDK process, not the CLI. + // Just track them without trying to connect. + if (config.type === 'sdk') { + added.push(name) + continue + } + + try { + const client = await connectToServer(name, scopedConfig) + newClients.push(client) + + if (client.type === 'connected') { + const serverTools = await fetchToolsForClient(client) + newTools.push(...serverTools) + } else if (client.type === 'failed') { + errors[name] = client.error || 'Connection failed' + } + + added.push(name) + } catch (e) { + const err = toError(e) + errors[name] = err.message + logError(err) + } + } + + // Build new configs + const newConfigs: Record = {} + for (const name of desiredNames) { + const config = desiredConfigs[name] + if (config) { + newConfigs[name] = toScopedConfig(config) + } + } + + const newState: DynamicMcpState = { + clients: newClients, + tools: newTools, + configs: newConfigs, + } + + // Update AppState with the new tools + setAppState(prev => { + // Get all dynamic server names (current + new) + const allDynamicServerNames = new Set([ + ...Object.keys(currentState.configs), + ...Object.keys(newConfigs), + ]) + + // Remove old dynamic tools + const nonDynamicTools = prev.mcp.tools.filter(t => { + for (const serverName of allDynamicServerNames) { + if (t.name.startsWith(`mcp__${serverName}__`)) { + return false + } + } + return true + }) + + // Remove old dynamic clients + const nonDynamicClients = prev.mcp.clients.filter(c => { + return !allDynamicServerNames.has(c.name) + }) + + return { + ...prev, + mcp: { + ...prev.mcp, + tools: [...nonDynamicTools, ...newTools], + clients: [...nonDynamicClients, ...newClients], + }, + } + }) + + return { + response: { added, removed, errors }, + newState, + } +} diff --git a/cli/remoteIO.ts b/cli/remoteIO.ts new file mode 100644 index 0000000..7d82c3e --- /dev/null +++ b/cli/remoteIO.ts @@ -0,0 +1,255 @@ +import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js' +import { PassThrough } from 'stream' +import { URL } from 'url' +import { getSessionId } from '../bootstrap/state.js' +import { getPollIntervalConfig } from '../bridge/pollConfig.js' +import { registerCleanup } from '../utils/cleanupRegistry.js' +import { setCommandLifecycleListener } from '../utils/commandLifecycle.js' +import { isDebugMode, logForDebugging } from '../utils/debug.js' +import { logForDiagnosticsNoPII } from '../utils/diagLogs.js' +import { isEnvTruthy } from '../utils/envUtils.js' +import { errorMessage } from '../utils/errors.js' +import { gracefulShutdown } from '../utils/gracefulShutdown.js' +import { logError } from '../utils/log.js' +import { writeToStdout } from '../utils/process.js' +import { getSessionIngressAuthToken } from '../utils/sessionIngressAuth.js' +import { + setSessionMetadataChangedListener, + setSessionStateChangedListener, +} from '../utils/sessionState.js' +import { + setInternalEventReader, + setInternalEventWriter, +} from '../utils/sessionStorage.js' +import { ndjsonSafeStringify } from './ndjsonSafeStringify.js' +import { StructuredIO } from './structuredIO.js' +import { CCRClient, CCRInitError } from './transports/ccrClient.js' +import { SSETransport } from './transports/SSETransport.js' +import type { Transport } from './transports/Transport.js' +import { getTransportForUrl } from './transports/transportUtils.js' + +/** + * Bidirectional streaming for SDK mode with session tracking + * Supports WebSocket transport + */ +export class RemoteIO extends StructuredIO { + private url: URL + private transport: Transport + private inputStream: PassThrough + private readonly isBridge: boolean = false + private readonly isDebug: boolean = false + private ccrClient: CCRClient | null = null + private keepAliveTimer: ReturnType | null = null + + constructor( + streamUrl: string, + initialPrompt?: AsyncIterable, + replayUserMessages?: boolean, + ) { + const inputStream = new PassThrough({ encoding: 'utf8' }) + super(inputStream, replayUserMessages) + this.inputStream = inputStream + this.url = new URL(streamUrl) + + // Prepare headers with session token if available + const headers: Record = {} + const sessionToken = getSessionIngressAuthToken() + if (sessionToken) { + headers['Authorization'] = `Bearer ${sessionToken}` + } else { + logForDebugging('[remote-io] No session ingress token available', { + level: 'error', + }) + } + + // Add environment runner version if available (set by Environment Manager) + const erVersion = process.env.CLAUDE_CODE_ENVIRONMENT_RUNNER_VERSION + if (erVersion) { + headers['x-environment-runner-version'] = erVersion + } + + // Provide a callback that re-reads the session token dynamically. + // When the parent process refreshes the token (via token file or env var), + // the transport can pick it up on reconnection. + const refreshHeaders = (): Record => { + const h: Record = {} + const freshToken = getSessionIngressAuthToken() + if (freshToken) { + h['Authorization'] = `Bearer ${freshToken}` + } + const freshErVersion = process.env.CLAUDE_CODE_ENVIRONMENT_RUNNER_VERSION + if (freshErVersion) { + h['x-environment-runner-version'] = freshErVersion + } + return h + } + + // Get appropriate transport based on URL protocol + this.transport = getTransportForUrl( + this.url, + headers, + getSessionId(), + refreshHeaders, + ) + + // Set up data callback + this.isBridge = process.env.CLAUDE_CODE_ENVIRONMENT_KIND === 'bridge' + this.isDebug = isDebugMode() + this.transport.setOnData((data: string) => { + this.inputStream.write(data) + if (this.isBridge && this.isDebug) { + writeToStdout(data.endsWith('\n') ? data : data + '\n') + } + }) + + // Set up close callback to handle connection failures + this.transport.setOnClose(() => { + // End the input stream to trigger graceful shutdown + this.inputStream.end() + }) + + // Initialize CCR v2 client (heartbeats, epoch, state reporting, event writes). + // The CCRClient constructor wires the SSE received-ack handler + // synchronously, so new CCRClient() MUST run before transport.connect() — + // otherwise early SSE frames hit an unwired onEventCallback and their + // 'received' delivery acks are silently dropped. + if (isEnvTruthy(process.env.CLAUDE_CODE_USE_CCR_V2)) { + // CCR v2 is SSE+POST by definition. getTransportForUrl returns + // SSETransport under the same env var, but the two checks live in + // different files — assert the invariant so a future decoupling + // fails loudly here instead of confusingly inside CCRClient. + if (!(this.transport instanceof SSETransport)) { + throw new Error( + 'CCR v2 requires SSETransport; check getTransportForUrl', + ) + } + this.ccrClient = new CCRClient(this.transport, this.url) + const init = this.ccrClient.initialize() + this.restoredWorkerState = init.catch(() => null) + init.catch((error: unknown) => { + logForDiagnosticsNoPII('error', 'cli_worker_lifecycle_init_failed', { + reason: error instanceof CCRInitError ? error.reason : 'unknown', + }) + logError( + new Error(`CCRClient initialization failed: ${errorMessage(error)}`), + ) + void gracefulShutdown(1, 'other') + }) + registerCleanup(async () => this.ccrClient?.close()) + + // Register internal event writer for transcript persistence. + // When set, sessionStorage writes transcript messages as CCR v2 + // internal events instead of v1 Session Ingress. + setInternalEventWriter((eventType, payload, options) => + this.ccrClient!.writeInternalEvent(eventType, payload, options), + ) + + // Register internal event readers for session resume. + // When set, hydrateFromCCRv2InternalEvents() can fetch foreground + // and subagent internal events to reconstruct conversation state. + setInternalEventReader( + () => this.ccrClient!.readInternalEvents(), + () => this.ccrClient!.readSubagentInternalEvents(), + ) + + const LIFECYCLE_TO_DELIVERY = { + started: 'processing', + completed: 'processed', + } as const + setCommandLifecycleListener((uuid, state) => { + this.ccrClient?.reportDelivery(uuid, LIFECYCLE_TO_DELIVERY[state]) + }) + setSessionStateChangedListener((state, details) => { + this.ccrClient?.reportState(state, details) + }) + setSessionMetadataChangedListener(metadata => { + this.ccrClient?.reportMetadata(metadata) + }) + } + + // Start connection only after all callbacks are wired (setOnData above, + // setOnEvent inside new CCRClient() when CCR v2 is enabled). + void this.transport.connect() + + // Push a silent keep_alive frame on a fixed interval so upstream + // proxies and the session-ingress layer don't GC an otherwise-idle + // remote control session. The keep_alive type is filtered before + // reaching any client UI (Query.ts drops it; structuredIO.ts drops it; + // web/iOS/Android never see it in their message loop). Interval comes + // from GrowthBook (tengu_bridge_poll_interval_config + // session_keepalive_interval_v2_ms, default 120s); 0 = disabled. + // Bridge-only: fixes Envoy idle timeout on bridge-topology sessions + // (#21931). byoc workers ran without this before #21931 and do not + // need it — different network path. + const keepAliveIntervalMs = + getPollIntervalConfig().session_keepalive_interval_v2_ms + if (this.isBridge && keepAliveIntervalMs > 0) { + this.keepAliveTimer = setInterval(() => { + logForDebugging('[remote-io] keep_alive sent') + void this.write({ type: 'keep_alive' }).catch(err => { + logForDebugging( + `[remote-io] keep_alive write failed: ${errorMessage(err)}`, + ) + }) + }, keepAliveIntervalMs) + this.keepAliveTimer.unref?.() + } + + // Register for graceful shutdown cleanup + registerCleanup(async () => this.close()) + + // If initial prompt is provided, send it through the input stream + if (initialPrompt) { + // Convert the initial prompt to the input stream format. + // Chunks from stdin may already contain trailing newlines, so strip + // them before appending our own to avoid double-newline issues that + // cause structuredIO to parse empty lines. String() handles both + // string chunks and Buffer objects from process.stdin. + const stream = this.inputStream + void (async () => { + for await (const chunk of initialPrompt) { + stream.write(String(chunk).replace(/\n$/, '') + '\n') + } + })() + } + } + + override flushInternalEvents(): Promise { + return this.ccrClient?.flushInternalEvents() ?? Promise.resolve() + } + + override get internalEventsPending(): number { + return this.ccrClient?.internalEventsPending ?? 0 + } + + /** + * Send output to the transport. + * In bridge mode, control_request messages are always echoed to stdout so the + * bridge parent can detect permission requests. Other messages are echoed only + * in debug mode. + */ + async write(message: StdoutMessage): Promise { + if (this.ccrClient) { + await this.ccrClient.writeEvent(message) + } else { + await this.transport.write(message) + } + if (this.isBridge) { + if (message.type === 'control_request' || this.isDebug) { + writeToStdout(ndjsonSafeStringify(message) + '\n') + } + } + } + + /** + * Clean up connections gracefully + */ + close(): void { + if (this.keepAliveTimer) { + clearInterval(this.keepAliveTimer) + this.keepAliveTimer = null + } + this.transport.close() + this.inputStream.end() + } +} diff --git a/cli/structuredIO.ts b/cli/structuredIO.ts new file mode 100644 index 0000000..366b56f --- /dev/null +++ b/cli/structuredIO.ts @@ -0,0 +1,859 @@ +import { feature } from 'bun:bundle' +import type { + ElicitResult, + JSONRPCMessage, +} from '@modelcontextprotocol/sdk/types.js' +import { randomUUID } from 'crypto' +import type { AssistantMessage } from 'src//types/message.js' +import type { + HookInput, + HookJSONOutput, + PermissionUpdate, + SDKMessage, + SDKUserMessage, +} from 'src/entrypoints/agentSdkTypes.js' +import { SDKControlElicitationResponseSchema } from 'src/entrypoints/sdk/controlSchemas.js' +import type { + SDKControlRequest, + SDKControlResponse, + StdinMessage, + StdoutMessage, +} from 'src/entrypoints/sdk/controlTypes.js' +import type { CanUseToolFn } from 'src/hooks/useCanUseTool.js' +import type { Tool, ToolUseContext } from 'src/Tool.js' +import { type HookCallback, hookJSONOutputSchema } from 'src/types/hooks.js' +import { logForDebugging } from 'src/utils/debug.js' +import { logForDiagnosticsNoPII } from 'src/utils/diagLogs.js' +import { AbortError } from 'src/utils/errors.js' +import { + type Output as PermissionToolOutput, + permissionPromptToolResultToPermissionDecision, + outputSchema as permissionToolOutputSchema, +} from 'src/utils/permissions/PermissionPromptToolResultSchema.js' +import type { + PermissionDecision, + PermissionDecisionReason, +} from 'src/utils/permissions/PermissionResult.js' +import { hasPermissionsToUseTool } from 'src/utils/permissions/permissions.js' +import { writeToStdout } from 'src/utils/process.js' +import { jsonStringify } from 'src/utils/slowOperations.js' +import { z } from 'zod/v4' +import { notifyCommandLifecycle } from '../utils/commandLifecycle.js' +import { normalizeControlMessageKeys } from '../utils/controlMessageCompat.js' +import { executePermissionRequestHooks } from '../utils/hooks.js' +import { + applyPermissionUpdates, + persistPermissionUpdates, +} from '../utils/permissions/PermissionUpdate.js' +import { + notifySessionStateChanged, + type RequiresActionDetails, + type SessionExternalMetadata, +} from '../utils/sessionState.js' +import { jsonParse } from '../utils/slowOperations.js' +import { Stream } from '../utils/stream.js' +import { ndjsonSafeStringify } from './ndjsonSafeStringify.js' + +/** + * Synthetic tool name used when forwarding sandbox network permission + * requests via the can_use_tool control_request protocol. SDK hosts + * see this as a normal tool permission prompt. + */ +export const SANDBOX_NETWORK_ACCESS_TOOL_NAME = 'SandboxNetworkAccess' + +function serializeDecisionReason( + reason: PermissionDecisionReason | undefined, +): string | undefined { + if (!reason) { + return undefined + } + + if ( + (feature('BASH_CLASSIFIER') || feature('TRANSCRIPT_CLASSIFIER')) && + reason.type === 'classifier' + ) { + return reason.reason + } + switch (reason.type) { + case 'rule': + case 'mode': + case 'subcommandResults': + case 'permissionPromptTool': + return undefined + case 'hook': + case 'asyncAgent': + case 'sandboxOverride': + case 'workingDir': + case 'safetyCheck': + case 'other': + return reason.reason + } +} + +function buildRequiresActionDetails( + tool: Tool, + input: Record, + toolUseID: string, + requestId: string, +): RequiresActionDetails { + // Per-tool summary methods may throw on malformed input; permission + // handling must not break because of a bad description. + let description: string + try { + description = + tool.getActivityDescription?.(input) ?? + tool.getToolUseSummary?.(input) ?? + tool.userFacingName(input) + } catch { + description = tool.name + } + return { + tool_name: tool.name, + action_description: description, + tool_use_id: toolUseID, + request_id: requestId, + input, + } +} + +type PendingRequest = { + resolve: (result: T) => void + reject: (error: unknown) => void + schema?: z.Schema + request: SDKControlRequest +} + +/** + * Provides a structured way to read and write SDK messages from stdio, + * capturing the SDK protocol. + */ +// Maximum number of resolved tool_use IDs to track. Once exceeded, the oldest +// entry is evicted. This bounds memory in very long sessions while keeping +// enough history to catch duplicate control_response deliveries. +const MAX_RESOLVED_TOOL_USE_IDS = 1000 + +export class StructuredIO { + readonly structuredInput: AsyncGenerator + private readonly pendingRequests = new Map>() + + // CCR external_metadata read back on worker start; null when the + // transport doesn't restore. Assigned by RemoteIO. + restoredWorkerState: Promise = + Promise.resolve(null) + + private inputClosed = false + private unexpectedResponseCallback?: ( + response: SDKControlResponse, + ) => Promise + + // Tracks tool_use IDs that have been resolved through the normal permission + // flow (or aborted by a hook). When a duplicate control_response arrives + // after the original was already handled, this Set prevents the orphan + // handler from re-processing it — which would push duplicate assistant + // messages into mutableMessages and cause a 400 "tool_use ids must be unique" + // error from the API. + private readonly resolvedToolUseIds = new Set() + private prependedLines: string[] = [] + private onControlRequestSent?: (request: SDKControlRequest) => void + private onControlRequestResolved?: (requestId: string) => void + + // sendRequest() and print.ts both enqueue here; the drain loop is the + // only writer. Prevents control_request from overtaking queued stream_events. + readonly outbound = new Stream() + + constructor( + private readonly input: AsyncIterable, + private readonly replayUserMessages?: boolean, + ) { + this.input = input + this.structuredInput = this.read() + } + + /** + * Records a tool_use ID as resolved so that late/duplicate control_response + * messages for the same tool are ignored by the orphan handler. + */ + private trackResolvedToolUseId(request: SDKControlRequest): void { + if (request.request.subtype === 'can_use_tool') { + this.resolvedToolUseIds.add(request.request.tool_use_id) + if (this.resolvedToolUseIds.size > MAX_RESOLVED_TOOL_USE_IDS) { + // Evict the oldest entry (Sets iterate in insertion order) + const first = this.resolvedToolUseIds.values().next().value + if (first !== undefined) { + this.resolvedToolUseIds.delete(first) + } + } + } + } + + /** Flush pending internal events. No-op for non-remote IO. Overridden by RemoteIO. */ + flushInternalEvents(): Promise { + return Promise.resolve() + } + + /** Internal-event queue depth. Overridden by RemoteIO; zero otherwise. */ + get internalEventsPending(): number { + return 0 + } + + /** + * Queue a user turn to be yielded before the next message from this.input. + * Works before iteration starts and mid-stream — read() re-checks + * prependedLines between each yielded message. + */ + prependUserMessage(content: string): void { + this.prependedLines.push( + jsonStringify({ + type: 'user', + session_id: '', + message: { role: 'user', content }, + parent_tool_use_id: null, + } satisfies SDKUserMessage) + '\n', + ) + } + + private async *read() { + let content = '' + + // Called once before for-await (an empty this.input otherwise skips the + // loop body entirely), then again per block. prependedLines re-check is + // inside the while so a prepend pushed between two messages in the SAME + // block still lands first. + const splitAndProcess = async function* (this: StructuredIO) { + for (;;) { + if (this.prependedLines.length > 0) { + content = this.prependedLines.join('') + content + this.prependedLines = [] + } + const newline = content.indexOf('\n') + if (newline === -1) break + const line = content.slice(0, newline) + content = content.slice(newline + 1) + const message = await this.processLine(line) + if (message) { + logForDiagnosticsNoPII('info', 'cli_stdin_message_parsed', { + type: message.type, + }) + yield message + } + } + }.bind(this) + + yield* splitAndProcess() + + for await (const block of this.input) { + content += block + yield* splitAndProcess() + } + if (content) { + const message = await this.processLine(content) + if (message) { + yield message + } + } + this.inputClosed = true + for (const request of this.pendingRequests.values()) { + // Reject all pending requests if the input stream + request.reject( + new Error('Tool permission stream closed before response received'), + ) + } + } + + getPendingPermissionRequests() { + return Array.from(this.pendingRequests.values()) + .map(entry => entry.request) + .filter(pr => pr.request.subtype === 'can_use_tool') + } + + setUnexpectedResponseCallback( + callback: (response: SDKControlResponse) => Promise, + ): void { + this.unexpectedResponseCallback = callback + } + + /** + * Inject a control_response message to resolve a pending permission request. + * Used by the bridge to feed permission responses from claude.ai into the + * SDK permission flow. + * + * Also sends a control_cancel_request to the SDK consumer so its canUseTool + * callback is aborted via the signal — otherwise the callback hangs. + */ + injectControlResponse(response: SDKControlResponse): void { + const requestId = response.response?.request_id + if (!requestId) return + const request = this.pendingRequests.get(requestId) + if (!request) return + this.trackResolvedToolUseId(request.request) + this.pendingRequests.delete(requestId) + // Cancel the SDK consumer's canUseTool callback — the bridge won. + void this.write({ + type: 'control_cancel_request', + request_id: requestId, + }) + if (response.response.subtype === 'error') { + request.reject(new Error(response.response.error)) + } else { + const result = response.response.response + if (request.schema) { + try { + request.resolve(request.schema.parse(result)) + } catch (error) { + request.reject(error) + } + } else { + request.resolve({}) + } + } + } + + /** + * Register a callback invoked whenever a can_use_tool control_request + * is written to stdout. Used by the bridge to forward permission + * requests to claude.ai. + */ + setOnControlRequestSent( + callback: ((request: SDKControlRequest) => void) | undefined, + ): void { + this.onControlRequestSent = callback + } + + /** + * Register a callback invoked when a can_use_tool control_response arrives + * from the SDK consumer (via stdin). Used by the bridge to cancel the + * stale permission prompt on claude.ai when the SDK consumer wins the race. + */ + setOnControlRequestResolved( + callback: ((requestId: string) => void) | undefined, + ): void { + this.onControlRequestResolved = callback + } + + private async processLine( + line: string, + ): Promise { + // Skip empty lines (e.g. from double newlines in piped stdin) + if (!line) { + return undefined + } + try { + const message = normalizeControlMessageKeys(jsonParse(line)) as + | StdinMessage + | SDKMessage + if (message.type === 'keep_alive') { + // Silently ignore keep-alive messages + return undefined + } + if (message.type === 'update_environment_variables') { + // Apply environment variable updates directly to process.env. + // Used by bridge session runner for auth token refresh + // (CLAUDE_CODE_SESSION_ACCESS_TOKEN) which must be readable + // by the REPL process itself, not just child Bash commands. + const keys = Object.keys(message.variables) + for (const [key, value] of Object.entries(message.variables)) { + process.env[key] = value + } + logForDebugging( + `[structuredIO] applied update_environment_variables: ${keys.join(', ')}`, + ) + return undefined + } + if (message.type === 'control_response') { + // Close lifecycle for every control_response, including duplicates + // and orphans — orphans don't yield to print.ts's main loop, so this + // is the only path that sees them. uuid is server-injected into the + // payload. + const uuid = + 'uuid' in message && typeof message.uuid === 'string' + ? message.uuid + : undefined + if (uuid) { + notifyCommandLifecycle(uuid, 'completed') + } + const request = this.pendingRequests.get(message.response.request_id) + if (!request) { + // Check if this tool_use was already resolved through the normal + // permission flow. Duplicate control_response deliveries (e.g. from + // WebSocket reconnects) arrive after the original was handled, and + // re-processing them would push duplicate assistant messages into + // the conversation, causing API 400 errors. + const responsePayload = + message.response.subtype === 'success' + ? message.response.response + : undefined + const toolUseID = responsePayload?.toolUseID + if ( + typeof toolUseID === 'string' && + this.resolvedToolUseIds.has(toolUseID) + ) { + logForDebugging( + `Ignoring duplicate control_response for already-resolved toolUseID=${toolUseID} request_id=${message.response.request_id}`, + ) + return undefined + } + if (this.unexpectedResponseCallback) { + await this.unexpectedResponseCallback(message) + } + return undefined // Ignore responses for requests we don't know about + } + this.trackResolvedToolUseId(request.request) + this.pendingRequests.delete(message.response.request_id) + // Notify the bridge when the SDK consumer resolves a can_use_tool + // request, so it can cancel the stale permission prompt on claude.ai. + if ( + request.request.request.subtype === 'can_use_tool' && + this.onControlRequestResolved + ) { + this.onControlRequestResolved(message.response.request_id) + } + + if (message.response.subtype === 'error') { + request.reject(new Error(message.response.error)) + return undefined + } + const result = message.response.response + if (request.schema) { + try { + request.resolve(request.schema.parse(result)) + } catch (error) { + request.reject(error) + } + } else { + request.resolve({}) + } + // Propagate control responses when replay is enabled + if (this.replayUserMessages) { + return message + } + return undefined + } + if ( + message.type !== 'user' && + message.type !== 'control_request' && + message.type !== 'assistant' && + message.type !== 'system' + ) { + logForDebugging(`Ignoring unknown message type: ${message.type}`, { + level: 'warn', + }) + return undefined + } + if (message.type === 'control_request') { + if (!message.request) { + exitWithMessage(`Error: Missing request on control_request`) + } + return message + } + if (message.type === 'assistant' || message.type === 'system') { + return message + } + if (message.message.role !== 'user') { + exitWithMessage( + `Error: Expected message role 'user', got '${message.message.role}'`, + ) + } + return message + } catch (error) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(`Error parsing streaming input line: ${line}: ${error}`) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + } + } + + async write(message: StdoutMessage): Promise { + writeToStdout(ndjsonSafeStringify(message) + '\n') + } + + private async sendRequest( + request: SDKControlRequest['request'], + schema: z.Schema, + signal?: AbortSignal, + requestId: string = randomUUID(), + ): Promise { + const message: SDKControlRequest = { + type: 'control_request', + request_id: requestId, + request, + } + if (this.inputClosed) { + throw new Error('Stream closed') + } + if (signal?.aborted) { + throw new Error('Request aborted') + } + this.outbound.enqueue(message) + if (request.subtype === 'can_use_tool' && this.onControlRequestSent) { + this.onControlRequestSent(message) + } + const aborted = () => { + this.outbound.enqueue({ + type: 'control_cancel_request', + request_id: requestId, + }) + // Immediately reject the outstanding promise, without + // waiting for the host to acknowledge the cancellation. + const request = this.pendingRequests.get(requestId) + if (request) { + // Track the tool_use ID as resolved before rejecting, so that a + // late response from the host is ignored by the orphan handler. + this.trackResolvedToolUseId(request.request) + request.reject(new AbortError()) + } + } + if (signal) { + signal.addEventListener('abort', aborted, { + once: true, + }) + } + try { + return await new Promise((resolve, reject) => { + this.pendingRequests.set(requestId, { + request: { + type: 'control_request', + request_id: requestId, + request, + }, + resolve: result => { + resolve(result as Response) + }, + reject, + schema, + }) + }) + } finally { + if (signal) { + signal.removeEventListener('abort', aborted) + } + this.pendingRequests.delete(requestId) + } + } + + createCanUseTool( + onPermissionPrompt?: (details: RequiresActionDetails) => void, + ): CanUseToolFn { + return async ( + tool: Tool, + input: { [key: string]: unknown }, + toolUseContext: ToolUseContext, + assistantMessage: AssistantMessage, + toolUseID: string, + forceDecision?: PermissionDecision, + ): Promise => { + const mainPermissionResult = + forceDecision ?? + (await hasPermissionsToUseTool( + tool, + input, + toolUseContext, + assistantMessage, + toolUseID, + )) + // If the tool is allowed or denied, return the result + if ( + mainPermissionResult.behavior === 'allow' || + mainPermissionResult.behavior === 'deny' + ) { + return mainPermissionResult + } + + // Run PermissionRequest hooks in parallel with the SDK permission + // prompt. In the terminal CLI, hooks race against the interactive + // prompt so that e.g. a hook with --delay 20 doesn't block the UI. + // We need the same behavior here: the SDK host (VS Code, etc.) shows + // its permission dialog immediately while hooks run in the background. + // Whichever resolves first wins; the loser is cancelled/ignored. + + // AbortController used to cancel the SDK request if a hook decides first + const hookAbortController = new AbortController() + const parentSignal = toolUseContext.abortController.signal + // Forward parent abort to our local controller + const onParentAbort = () => hookAbortController.abort() + parentSignal.addEventListener('abort', onParentAbort, { once: true }) + + try { + // Start the hook evaluation (runs in background) + const hookPromise = executePermissionRequestHooksForSDK( + tool.name, + toolUseID, + input, + toolUseContext, + mainPermissionResult.suggestions, + ).then(decision => ({ source: 'hook' as const, decision })) + + // Start the SDK permission prompt immediately (don't wait for hooks) + const requestId = randomUUID() + onPermissionPrompt?.( + buildRequiresActionDetails(tool, input, toolUseID, requestId), + ) + const sdkPromise = this.sendRequest( + { + subtype: 'can_use_tool', + tool_name: tool.name, + input, + permission_suggestions: mainPermissionResult.suggestions, + blocked_path: mainPermissionResult.blockedPath, + decision_reason: serializeDecisionReason( + mainPermissionResult.decisionReason, + ), + tool_use_id: toolUseID, + agent_id: toolUseContext.agentId, + }, + permissionToolOutputSchema(), + hookAbortController.signal, + requestId, + ).then(result => ({ source: 'sdk' as const, result })) + + // Race: hook completion vs SDK prompt response. + // The hook promise always resolves (never rejects), returning + // undefined if no hook made a decision. + const winner = await Promise.race([hookPromise, sdkPromise]) + + if (winner.source === 'hook') { + if (winner.decision) { + // Hook decided — abort the pending SDK request. + // Suppress the expected AbortError rejection from sdkPromise. + sdkPromise.catch(() => {}) + hookAbortController.abort() + return winner.decision + } + // Hook passed through (no decision) — wait for the SDK prompt + const sdkResult = await sdkPromise + return permissionPromptToolResultToPermissionDecision( + sdkResult.result, + tool, + input, + toolUseContext, + ) + } + + // SDK prompt responded first — use its result (hook still running + // in background but its result will be ignored) + return permissionPromptToolResultToPermissionDecision( + winner.result, + tool, + input, + toolUseContext, + ) + } catch (error) { + return permissionPromptToolResultToPermissionDecision( + { + behavior: 'deny', + message: `Tool permission request failed: ${error}`, + toolUseID, + }, + tool, + input, + toolUseContext, + ) + } finally { + // Only transition back to 'running' if no other permission prompts + // are pending (concurrent tool execution can have multiple in-flight). + if (this.getPendingPermissionRequests().length === 0) { + notifySessionStateChanged('running') + } + parentSignal.removeEventListener('abort', onParentAbort) + } + } + } + + createHookCallback(callbackId: string, timeout?: number): HookCallback { + return { + type: 'callback', + timeout, + callback: async ( + input: HookInput, + toolUseID: string | null, + abort: AbortSignal | undefined, + ): Promise => { + try { + const result = await this.sendRequest( + { + subtype: 'hook_callback', + callback_id: callbackId, + input, + tool_use_id: toolUseID || undefined, + }, + hookJSONOutputSchema(), + abort, + ) + return result + } catch (error) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(`Error in hook callback ${callbackId}:`, error) + return {} + } + }, + } + } + + /** + * Sends an elicitation request to the SDK consumer and returns the response. + */ + async handleElicitation( + serverName: string, + message: string, + requestedSchema?: Record, + signal?: AbortSignal, + mode?: 'form' | 'url', + url?: string, + elicitationId?: string, + ): Promise { + try { + const result = await this.sendRequest( + { + subtype: 'elicitation', + mcp_server_name: serverName, + message, + mode, + url, + elicitation_id: elicitationId, + requested_schema: requestedSchema, + }, + SDKControlElicitationResponseSchema(), + signal, + ) + return result + } catch { + return { action: 'cancel' as const } + } + } + + /** + * Creates a SandboxAskCallback that forwards sandbox network permission + * requests to the SDK host as can_use_tool control_requests. + * + * This piggybacks on the existing can_use_tool protocol with a synthetic + * tool name so that SDK hosts (VS Code, CCR, etc.) can prompt the user + * for network access without requiring a new protocol subtype. + */ + createSandboxAskCallback(): (hostPattern: { + host: string + port?: number + }) => Promise { + return async (hostPattern): Promise => { + try { + const result = await this.sendRequest( + { + subtype: 'can_use_tool', + tool_name: SANDBOX_NETWORK_ACCESS_TOOL_NAME, + input: { host: hostPattern.host }, + tool_use_id: randomUUID(), + description: `Allow network connection to ${hostPattern.host}?`, + }, + permissionToolOutputSchema(), + ) + return result.behavior === 'allow' + } catch { + // If the request fails (stream closed, abort, etc.), deny the connection + return false + } + } + } + + /** + * Sends an MCP message to an SDK server and waits for the response + */ + async sendMcpMessage( + serverName: string, + message: JSONRPCMessage, + ): Promise { + const response = await this.sendRequest<{ mcp_response: JSONRPCMessage }>( + { + subtype: 'mcp_message', + server_name: serverName, + message, + }, + z.object({ + mcp_response: z.any() as z.Schema, + }), + ) + return response.mcp_response + } +} + +function exitWithMessage(message: string): never { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(message) + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) +} + +/** + * Execute PermissionRequest hooks and return a decision if one is made. + * Returns undefined if no hook made a decision. + */ +async function executePermissionRequestHooksForSDK( + toolName: string, + toolUseID: string, + input: Record, + toolUseContext: ToolUseContext, + suggestions: PermissionUpdate[] | undefined, +): Promise { + const appState = toolUseContext.getAppState() + const permissionMode = appState.toolPermissionContext.mode + + // Iterate directly over the generator instead of using `all` + const hookGenerator = executePermissionRequestHooks( + toolName, + toolUseID, + input, + toolUseContext, + permissionMode, + suggestions, + toolUseContext.abortController.signal, + ) + + for await (const hookResult of hookGenerator) { + if ( + hookResult.permissionRequestResult && + (hookResult.permissionRequestResult.behavior === 'allow' || + hookResult.permissionRequestResult.behavior === 'deny') + ) { + const decision = hookResult.permissionRequestResult + if (decision.behavior === 'allow') { + const finalInput = decision.updatedInput || input + + // Apply permission updates if provided by hook ("always allow") + const permissionUpdates = decision.updatedPermissions ?? [] + if (permissionUpdates.length > 0) { + persistPermissionUpdates(permissionUpdates) + const currentAppState = toolUseContext.getAppState() + const updatedContext = applyPermissionUpdates( + currentAppState.toolPermissionContext, + permissionUpdates, + ) + // Update permission context via setAppState + toolUseContext.setAppState(prev => { + if (prev.toolPermissionContext === updatedContext) return prev + return { ...prev, toolPermissionContext: updatedContext } + }) + } + + return { + behavior: 'allow', + updatedInput: finalInput, + userModified: false, + decisionReason: { + type: 'hook', + hookName: 'PermissionRequest', + }, + } + } else { + // Hook denied the permission + return { + behavior: 'deny', + message: + decision.message || 'Permission denied by PermissionRequest hook', + decisionReason: { + type: 'hook', + hookName: 'PermissionRequest', + }, + } + } + } + } + + return undefined +} diff --git a/cli/transports/HybridTransport.ts b/cli/transports/HybridTransport.ts new file mode 100644 index 0000000..15500ec --- /dev/null +++ b/cli/transports/HybridTransport.ts @@ -0,0 +1,282 @@ +import axios, { type AxiosError } from 'axios' +import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js' +import { logForDebugging } from '../../utils/debug.js' +import { logForDiagnosticsNoPII } from '../../utils/diagLogs.js' +import { getSessionIngressAuthToken } from '../../utils/sessionIngressAuth.js' +import { SerialBatchEventUploader } from './SerialBatchEventUploader.js' +import { + WebSocketTransport, + type WebSocketTransportOptions, +} from './WebSocketTransport.js' + +const BATCH_FLUSH_INTERVAL_MS = 100 +// Per-attempt POST timeout. Bounds how long a single stuck POST can block +// the serialized queue. Without this, a hung connection stalls all writes. +const POST_TIMEOUT_MS = 15_000 +// Grace period for queued writes on close(). Covers a healthy POST (~100ms) +// plus headroom; best-effort, not a delivery guarantee under degraded network. +// Void-ed (nothing awaits it) so this is a last resort — replBridge teardown +// now closes AFTER archive so archive latency is the primary drain window. +// NOTE: gracefulShutdown's cleanup budget is 2s (not the 5s outer failsafe); +// 3s here exceeds it, but the process lives ~2s longer for hooks+analytics. +const CLOSE_GRACE_MS = 3000 + +/** + * Hybrid transport: WebSocket for reads, HTTP POST for writes. + * + * Write flow: + * + * write(stream_event) ─┐ + * │ (100ms timer) + * │ + * ▼ + * write(other) ────► uploader.enqueue() (SerialBatchEventUploader) + * ▲ │ + * writeBatch() ────────┘ │ serial, batched, retries indefinitely, + * │ backpressure at maxQueueSize + * ▼ + * postOnce() (single HTTP POST, throws on retryable) + * + * stream_event messages accumulate in streamEventBuffer for up to 100ms + * before enqueue (reduces POST count for high-volume content deltas). A + * non-stream write flushes any buffered stream_events first to preserve order. + * + * Serialization + retry + backpressure are delegated to SerialBatchEventUploader + * (same primitive CCR uses). At most one POST in-flight; events arriving during + * a POST batch into the next one. On failure, the uploader re-queues and retries + * with exponential backoff + jitter. If the queue fills past maxQueueSize, + * enqueue() blocks — giving awaiting callers backpressure. + * + * Why serialize? Bridge mode fires writes via `void transport.write()` + * (fire-and-forget). Without this, concurrent POSTs → concurrent Firestore + * writes to the same document → collisions → retry storms → pages oncall. + */ +export class HybridTransport extends WebSocketTransport { + private postUrl: string + private uploader: SerialBatchEventUploader + + // stream_event delay buffer — accumulates content deltas for up to + // BATCH_FLUSH_INTERVAL_MS before enqueueing (reduces POST count) + private streamEventBuffer: StdoutMessage[] = [] + private streamEventTimer: ReturnType | null = null + + constructor( + url: URL, + headers: Record = {}, + sessionId?: string, + refreshHeaders?: () => Record, + options?: WebSocketTransportOptions & { + maxConsecutiveFailures?: number + onBatchDropped?: (batchSize: number, failures: number) => void + }, + ) { + super(url, headers, sessionId, refreshHeaders, options) + const { maxConsecutiveFailures, onBatchDropped } = options ?? {} + this.postUrl = convertWsUrlToPostUrl(url) + this.uploader = new SerialBatchEventUploader({ + // Large cap — session-ingress accepts arbitrary batch sizes. Events + // naturally batch during in-flight POSTs; this just bounds the payload. + maxBatchSize: 500, + // Bridge callers use `void transport.write()` — backpressure doesn't + // apply (they don't await). A batch >maxQueueSize deadlocks (see + // SerialBatchEventUploader backpressure check). So set it high enough + // to be a memory bound only. Wire real backpressure in a follow-up + // once callers await. + maxQueueSize: 100_000, + baseDelayMs: 500, + maxDelayMs: 8000, + jitterMs: 1000, + // Optional cap so a persistently-failing server can't pin the drain + // loop for the lifetime of the process. Undefined = indefinite retry. + // replBridge sets this; the 1P transportUtils path does not. + maxConsecutiveFailures, + onBatchDropped: (batchSize, failures) => { + logForDiagnosticsNoPII( + 'error', + 'cli_hybrid_batch_dropped_max_failures', + { + batchSize, + failures, + }, + ) + onBatchDropped?.(batchSize, failures) + }, + send: batch => this.postOnce(batch), + }) + logForDebugging(`HybridTransport: POST URL = ${this.postUrl}`) + logForDiagnosticsNoPII('info', 'cli_hybrid_transport_initialized') + } + + /** + * Enqueue a message and wait for the queue to drain. Returning flush() + * preserves the contract that `await write()` resolves after the event is + * POSTed (relied on by tests and replBridge's initial flush). Fire-and-forget + * callers (`void transport.write()`) are unaffected — they don't await, + * so the later resolution doesn't add latency. + */ + override async write(message: StdoutMessage): Promise { + if (message.type === 'stream_event') { + // Delay: accumulate stream_events briefly before enqueueing. + // Promise resolves immediately — callers don't await stream_events. + this.streamEventBuffer.push(message) + if (!this.streamEventTimer) { + this.streamEventTimer = setTimeout( + () => this.flushStreamEvents(), + BATCH_FLUSH_INTERVAL_MS, + ) + } + return + } + // Immediate: flush any buffered stream_events (ordering), then this event. + await this.uploader.enqueue([...this.takeStreamEvents(), message]) + return this.uploader.flush() + } + + async writeBatch(messages: StdoutMessage[]): Promise { + await this.uploader.enqueue([...this.takeStreamEvents(), ...messages]) + return this.uploader.flush() + } + + /** Snapshot before/after writeBatch() to detect silent drops. */ + get droppedBatchCount(): number { + return this.uploader.droppedBatchCount + } + + /** + * Block until all pending events are POSTed. Used by bridge's initial + * history flush so onStateChange('connected') fires after persistence. + */ + flush(): Promise { + void this.uploader.enqueue(this.takeStreamEvents()) + return this.uploader.flush() + } + + /** Take ownership of buffered stream_events and clear the delay timer. */ + private takeStreamEvents(): StdoutMessage[] { + if (this.streamEventTimer) { + clearTimeout(this.streamEventTimer) + this.streamEventTimer = null + } + const buffered = this.streamEventBuffer + this.streamEventBuffer = [] + return buffered + } + + /** Delay timer fired — enqueue accumulated stream_events. */ + private flushStreamEvents(): void { + this.streamEventTimer = null + void this.uploader.enqueue(this.takeStreamEvents()) + } + + override close(): void { + if (this.streamEventTimer) { + clearTimeout(this.streamEventTimer) + this.streamEventTimer = null + } + this.streamEventBuffer = [] + // Grace period for queued writes — fallback. replBridge teardown now + // awaits archive between write and close (see CLOSE_GRACE_MS), so + // archive latency is the primary drain window and this is a last + // resort. Keep close() sync (returns immediately) but defer + // uploader.close() so any remaining queue gets a chance to finish. + const uploader = this.uploader + let graceTimer: ReturnType | undefined + void Promise.race([ + uploader.flush(), + new Promise(r => { + // eslint-disable-next-line no-restricted-syntax -- need timer ref for clearTimeout + graceTimer = setTimeout(r, CLOSE_GRACE_MS) + }), + ]).finally(() => { + clearTimeout(graceTimer) + uploader.close() + }) + super.close() + } + + /** + * Single-attempt POST. Throws on retryable failures (429, 5xx, network) + * so SerialBatchEventUploader re-queues and retries. Returns on success + * and on permanent failures (4xx non-429, no token) so the uploader moves on. + */ + private async postOnce(events: StdoutMessage[]): Promise { + const sessionToken = getSessionIngressAuthToken() + if (!sessionToken) { + logForDebugging('HybridTransport: No session token available for POST') + logForDiagnosticsNoPII('warn', 'cli_hybrid_post_no_token') + return + } + + const headers: Record = { + Authorization: `Bearer ${sessionToken}`, + 'Content-Type': 'application/json', + } + + let response + try { + response = await axios.post( + this.postUrl, + { events }, + { + headers, + validateStatus: () => true, + timeout: POST_TIMEOUT_MS, + }, + ) + } catch (error) { + const axiosError = error as AxiosError + logForDebugging(`HybridTransport: POST error: ${axiosError.message}`) + logForDiagnosticsNoPII('warn', 'cli_hybrid_post_network_error') + throw error + } + + if (response.status >= 200 && response.status < 300) { + logForDebugging(`HybridTransport: POST success count=${events.length}`) + return + } + + // 4xx (except 429) are permanent — drop, don't retry. + if ( + response.status >= 400 && + response.status < 500 && + response.status !== 429 + ) { + logForDebugging( + `HybridTransport: POST returned ${response.status} (permanent), dropping`, + ) + logForDiagnosticsNoPII('warn', 'cli_hybrid_post_client_error', { + status: response.status, + }) + return + } + + // 429 / 5xx — retryable. Throw so uploader re-queues and backs off. + logForDebugging( + `HybridTransport: POST returned ${response.status} (retryable)`, + ) + logForDiagnosticsNoPII('warn', 'cli_hybrid_post_retryable_error', { + status: response.status, + }) + throw new Error(`POST failed with ${response.status}`) + } +} + +/** + * Convert a WebSocket URL to the HTTP POST endpoint URL. + * From: wss://api.example.com/v2/session_ingress/ws/ + * To: https://api.example.com/v2/session_ingress/session//events + */ +function convertWsUrlToPostUrl(wsUrl: URL): string { + const protocol = wsUrl.protocol === 'wss:' ? 'https:' : 'http:' + + // Replace /ws/ with /session/ and append /events + let pathname = wsUrl.pathname + pathname = pathname.replace('/ws/', '/session/') + if (!pathname.endsWith('/events')) { + pathname = pathname.endsWith('/') + ? pathname + 'events' + : pathname + '/events' + } + + return `${protocol}//${wsUrl.host}${pathname}${wsUrl.search}` +} diff --git a/cli/transports/SSETransport.ts b/cli/transports/SSETransport.ts new file mode 100644 index 0000000..4f43dbe --- /dev/null +++ b/cli/transports/SSETransport.ts @@ -0,0 +1,711 @@ +import axios, { type AxiosError } from 'axios' +import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js' +import { logForDebugging } from '../../utils/debug.js' +import { logForDiagnosticsNoPII } from '../../utils/diagLogs.js' +import { errorMessage } from '../../utils/errors.js' +import { getSessionIngressAuthHeaders } from '../../utils/sessionIngressAuth.js' +import { sleep } from '../../utils/sleep.js' +import { jsonParse, jsonStringify } from '../../utils/slowOperations.js' +import { getClaudeCodeUserAgent } from '../../utils/userAgent.js' +import type { Transport } from './Transport.js' + +// --------------------------------------------------------------------------- +// Configuration +// --------------------------------------------------------------------------- + +const RECONNECT_BASE_DELAY_MS = 1000 +const RECONNECT_MAX_DELAY_MS = 30_000 +/** Time budget for reconnection attempts before giving up (10 minutes). */ +const RECONNECT_GIVE_UP_MS = 600_000 +/** Server sends keepalives every 15s; treat connection as dead after 45s of silence. */ +const LIVENESS_TIMEOUT_MS = 45_000 + +/** + * HTTP status codes that indicate a permanent server-side rejection. + * The transport transitions to 'closed' immediately without retrying. + */ +const PERMANENT_HTTP_CODES = new Set([401, 403, 404]) + +// POST retry configuration (matches HybridTransport) +const POST_MAX_RETRIES = 10 +const POST_BASE_DELAY_MS = 500 +const POST_MAX_DELAY_MS = 8000 + +/** Hoisted TextDecoder options to avoid per-chunk allocation in readStream. */ +const STREAM_DECODE_OPTS: TextDecodeOptions = { stream: true } + +/** Hoisted axios validateStatus callback to avoid per-request closure allocation. */ +function alwaysValidStatus(): boolean { + return true +} + +// --------------------------------------------------------------------------- +// SSE Frame Parser +// --------------------------------------------------------------------------- + +type SSEFrame = { + event?: string + id?: string + data?: string +} + +/** + * Incrementally parse SSE frames from a text buffer. + * Returns parsed frames and the remaining (incomplete) buffer. + * + * @internal exported for testing + */ +export function parseSSEFrames(buffer: string): { + frames: SSEFrame[] + remaining: string +} { + const frames: SSEFrame[] = [] + let pos = 0 + + // SSE frames are delimited by double newlines + let idx: number + while ((idx = buffer.indexOf('\n\n', pos)) !== -1) { + const rawFrame = buffer.slice(pos, idx) + pos = idx + 2 + + // Skip empty frames + if (!rawFrame.trim()) continue + + const frame: SSEFrame = {} + let isComment = false + + for (const line of rawFrame.split('\n')) { + if (line.startsWith(':')) { + // SSE comment (e.g., `:keepalive`) + isComment = true + continue + } + + const colonIdx = line.indexOf(':') + if (colonIdx === -1) continue + + const field = line.slice(0, colonIdx) + // Per SSE spec, strip one leading space after colon if present + const value = + line[colonIdx + 1] === ' ' + ? line.slice(colonIdx + 2) + : line.slice(colonIdx + 1) + + switch (field) { + case 'event': + frame.event = value + break + case 'id': + frame.id = value + break + case 'data': + // Per SSE spec, multiple data: lines are concatenated with \n + frame.data = frame.data ? frame.data + '\n' + value : value + break + // Ignore other fields (retry:, etc.) + } + } + + // Only emit frames that have data (or are pure comments which reset liveness) + if (frame.data || isComment) { + frames.push(frame) + } + } + + return { frames, remaining: buffer.slice(pos) } +} + +// --------------------------------------------------------------------------- +// Types +// --------------------------------------------------------------------------- + +type SSETransportState = + | 'idle' + | 'connected' + | 'reconnecting' + | 'closing' + | 'closed' + +/** + * Payload for `event: client_event` frames, matching the StreamClientEvent + * proto message in session_stream.proto. This is the only event type sent + * to worker subscribers — delivery_update, session_update, ephemeral_event, + * and catch_up_truncated are client-channel-only (see notifier.go and + * event_stream.go SubscriberClient guard). + */ +export type StreamClientEvent = { + event_id: string + sequence_num: number + event_type: string + source: string + payload: Record + created_at: string +} + +// --------------------------------------------------------------------------- +// SSETransport +// --------------------------------------------------------------------------- + +/** + * Transport that uses SSE for reading and HTTP POST for writing. + * + * Reads events via Server-Sent Events from the CCR v2 event stream endpoint. + * Writes events via HTTP POST with retry logic (same pattern as HybridTransport). + * + * Each `event: client_event` frame carries a StreamClientEvent proto JSON + * directly in `data:`. The transport extracts `payload` and passes it to + * `onData` as newline-delimited JSON for StructuredIO consumers. + * + * Supports automatic reconnection with exponential backoff and Last-Event-ID + * for resumption after disconnection. + */ +export class SSETransport implements Transport { + private state: SSETransportState = 'idle' + private onData?: (data: string) => void + private onCloseCallback?: (closeCode?: number) => void + private onEventCallback?: (event: StreamClientEvent) => void + private headers: Record + private sessionId?: string + private refreshHeaders?: () => Record + private readonly getAuthHeaders: () => Record + + // SSE connection state + private abortController: AbortController | null = null + private lastSequenceNum = 0 + private seenSequenceNums = new Set() + + // Reconnection state + private reconnectAttempts = 0 + private reconnectStartTime: number | null = null + private reconnectTimer: NodeJS.Timeout | null = null + + // Liveness detection + private livenessTimer: NodeJS.Timeout | null = null + + // POST URL (derived from SSE URL) + private postUrl: string + + // Runtime epoch for CCR v2 event format + + constructor( + private readonly url: URL, + headers: Record = {}, + sessionId?: string, + refreshHeaders?: () => Record, + initialSequenceNum?: number, + /** + * Per-instance auth header source. Omit to read the process-wide + * CLAUDE_CODE_SESSION_ACCESS_TOKEN (single-session callers). Required + * for concurrent multi-session callers — the env-var path is a process + * global and would stomp across sessions. + */ + getAuthHeaders?: () => Record, + ) { + this.headers = headers + this.sessionId = sessionId + this.refreshHeaders = refreshHeaders + this.getAuthHeaders = getAuthHeaders ?? getSessionIngressAuthHeaders + this.postUrl = convertSSEUrlToPostUrl(url) + // Seed with a caller-provided high-water mark so the first connect() + // sends from_sequence_num / Last-Event-ID. Without this, a fresh + // SSETransport always asks the server to replay from sequence 0 — + // the entire session history on every transport swap. + if (initialSequenceNum !== undefined && initialSequenceNum > 0) { + this.lastSequenceNum = initialSequenceNum + } + logForDebugging(`SSETransport: SSE URL = ${url.href}`) + logForDebugging(`SSETransport: POST URL = ${this.postUrl}`) + logForDiagnosticsNoPII('info', 'cli_sse_transport_initialized') + } + + /** + * High-water mark of sequence numbers seen on this stream. Callers that + * recreate the transport (e.g. replBridge onWorkReceived) read this before + * close() and pass it as `initialSequenceNum` to the next instance so the + * server resumes from the right point instead of replaying everything. + */ + getLastSequenceNum(): number { + return this.lastSequenceNum + } + + async connect(): Promise { + if (this.state !== 'idle' && this.state !== 'reconnecting') { + logForDebugging( + `SSETransport: Cannot connect, current state is ${this.state}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_sse_connect_failed') + return + } + + this.state = 'reconnecting' + const connectStartTime = Date.now() + + // Build SSE URL with sequence number for resumption + const sseUrl = new URL(this.url.href) + if (this.lastSequenceNum > 0) { + sseUrl.searchParams.set('from_sequence_num', String(this.lastSequenceNum)) + } + + // Build headers -- use fresh auth headers (supports Cookie for session keys). + // Remove stale Authorization header from this.headers when Cookie auth is used, + // since sending both confuses the auth interceptor. + const authHeaders = this.getAuthHeaders() + const headers: Record = { + ...this.headers, + ...authHeaders, + Accept: 'text/event-stream', + 'anthropic-version': '2023-06-01', + 'User-Agent': getClaudeCodeUserAgent(), + } + if (authHeaders['Cookie']) { + delete headers['Authorization'] + } + if (this.lastSequenceNum > 0) { + headers['Last-Event-ID'] = String(this.lastSequenceNum) + } + + logForDebugging(`SSETransport: Opening ${sseUrl.href}`) + logForDiagnosticsNoPII('info', 'cli_sse_connect_opening') + + this.abortController = new AbortController() + + try { + // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins + const response = await fetch(sseUrl.href, { + headers, + signal: this.abortController.signal, + }) + + if (!response.ok) { + const isPermanent = PERMANENT_HTTP_CODES.has(response.status) + logForDebugging( + `SSETransport: HTTP ${response.status}${isPermanent ? ' (permanent)' : ''}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_sse_connect_http_error', { + status: response.status, + }) + + if (isPermanent) { + this.state = 'closed' + this.onCloseCallback?.(response.status) + return + } + + this.handleConnectionError() + return + } + + if (!response.body) { + logForDebugging('SSETransport: No response body') + this.handleConnectionError() + return + } + + // Successfully connected + const connectDuration = Date.now() - connectStartTime + logForDebugging('SSETransport: Connected') + logForDiagnosticsNoPII('info', 'cli_sse_connect_connected', { + duration_ms: connectDuration, + }) + + this.state = 'connected' + this.reconnectAttempts = 0 + this.reconnectStartTime = null + this.resetLivenessTimer() + + // Read the SSE stream + await this.readStream(response.body) + } catch (error) { + if (this.abortController?.signal.aborted) { + // Intentional close + return + } + + logForDebugging( + `SSETransport: Connection error: ${errorMessage(error)}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_sse_connect_error') + this.handleConnectionError() + } + } + + /** + * Read and process the SSE stream body. + */ + // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins + private async readStream(body: ReadableStream): Promise { + const reader = body.getReader() + const decoder = new TextDecoder() + let buffer = '' + + try { + while (true) { + const { done, value } = await reader.read() + if (done) break + + buffer += decoder.decode(value, STREAM_DECODE_OPTS) + const { frames, remaining } = parseSSEFrames(buffer) + buffer = remaining + + for (const frame of frames) { + // Any frame (including keepalive comments) proves the connection is alive + this.resetLivenessTimer() + + if (frame.id) { + const seqNum = parseInt(frame.id, 10) + if (!isNaN(seqNum)) { + if (this.seenSequenceNums.has(seqNum)) { + logForDebugging( + `SSETransport: DUPLICATE frame seq=${seqNum} (lastSequenceNum=${this.lastSequenceNum}, seenCount=${this.seenSequenceNums.size})`, + { level: 'warn' }, + ) + logForDiagnosticsNoPII('warn', 'cli_sse_duplicate_sequence') + } else { + this.seenSequenceNums.add(seqNum) + // Prevent unbounded growth: once we have many entries, prune + // old sequence numbers that are well below the high-water mark. + // Only sequence numbers near lastSequenceNum matter for dedup. + if (this.seenSequenceNums.size > 1000) { + const threshold = this.lastSequenceNum - 200 + for (const s of this.seenSequenceNums) { + if (s < threshold) { + this.seenSequenceNums.delete(s) + } + } + } + } + if (seqNum > this.lastSequenceNum) { + this.lastSequenceNum = seqNum + } + } + } + + if (frame.event && frame.data) { + this.handleSSEFrame(frame.event, frame.data) + } else if (frame.data) { + // data: without event: — server is emitting the old envelope format + // or a bug. Log so incidents show as a signal instead of silent drops. + logForDebugging( + 'SSETransport: Frame has data: but no event: field — dropped', + { level: 'warn' }, + ) + logForDiagnosticsNoPII('warn', 'cli_sse_frame_missing_event_field') + } + } + } + } catch (error) { + if (this.abortController?.signal.aborted) return + logForDebugging( + `SSETransport: Stream read error: ${errorMessage(error)}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_sse_stream_read_error') + } finally { + reader.releaseLock() + } + + // Stream ended — reconnect unless we're closing + if (this.state !== 'closing' && this.state !== 'closed') { + logForDebugging('SSETransport: Stream ended, reconnecting') + this.handleConnectionError() + } + } + + /** + * Handle a single SSE frame. The event: field names the variant; data: + * carries the inner proto JSON directly (no envelope). + * + * Worker subscribers only receive client_event frames (see notifier.go) — + * any other event type indicates a server-side change that CC doesn't yet + * understand. Log a diagnostic so we notice in telemetry. + */ + private handleSSEFrame(eventType: string, data: string): void { + if (eventType !== 'client_event') { + logForDebugging( + `SSETransport: Unexpected SSE event type '${eventType}' on worker stream`, + { level: 'warn' }, + ) + logForDiagnosticsNoPII('warn', 'cli_sse_unexpected_event_type', { + event_type: eventType, + }) + return + } + + let ev: StreamClientEvent + try { + ev = jsonParse(data) as StreamClientEvent + } catch (error) { + logForDebugging( + `SSETransport: Failed to parse client_event data: ${errorMessage(error)}`, + { level: 'error' }, + ) + return + } + + const payload = ev.payload + if (payload && typeof payload === 'object' && 'type' in payload) { + const sessionLabel = this.sessionId ? ` session=${this.sessionId}` : '' + logForDebugging( + `SSETransport: Event seq=${ev.sequence_num} event_id=${ev.event_id} event_type=${ev.event_type} payload_type=${String(payload.type)}${sessionLabel}`, + ) + logForDiagnosticsNoPII('info', 'cli_sse_message_received') + // Pass the unwrapped payload as newline-delimited JSON, + // matching the format that StructuredIO/WebSocketTransport consumers expect + this.onData?.(jsonStringify(payload) + '\n') + } else { + logForDebugging( + `SSETransport: Ignoring client_event with no type in payload: event_id=${ev.event_id}`, + ) + } + + this.onEventCallback?.(ev) + } + + /** + * Handle connection errors with exponential backoff and time budget. + */ + private handleConnectionError(): void { + this.clearLivenessTimer() + + if (this.state === 'closing' || this.state === 'closed') return + + // Abort any in-flight SSE fetch + this.abortController?.abort() + this.abortController = null + + const now = Date.now() + if (!this.reconnectStartTime) { + this.reconnectStartTime = now + } + + const elapsed = now - this.reconnectStartTime + if (elapsed < RECONNECT_GIVE_UP_MS) { + // Clear any existing timer + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer) + this.reconnectTimer = null + } + + // Refresh headers before reconnecting + if (this.refreshHeaders) { + const freshHeaders = this.refreshHeaders() + Object.assign(this.headers, freshHeaders) + logForDebugging('SSETransport: Refreshed headers for reconnect') + } + + this.state = 'reconnecting' + this.reconnectAttempts++ + + const baseDelay = Math.min( + RECONNECT_BASE_DELAY_MS * Math.pow(2, this.reconnectAttempts - 1), + RECONNECT_MAX_DELAY_MS, + ) + // Add ±25% jitter + const delay = Math.max( + 0, + baseDelay + baseDelay * 0.25 * (2 * Math.random() - 1), + ) + + logForDebugging( + `SSETransport: Reconnecting in ${Math.round(delay)}ms (attempt ${this.reconnectAttempts}, ${Math.round(elapsed / 1000)}s elapsed)`, + ) + logForDiagnosticsNoPII('error', 'cli_sse_reconnect_attempt', { + reconnectAttempts: this.reconnectAttempts, + }) + + this.reconnectTimer = setTimeout(() => { + this.reconnectTimer = null + void this.connect() + }, delay) + } else { + logForDebugging( + `SSETransport: Reconnection time budget exhausted after ${Math.round(elapsed / 1000)}s`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_sse_reconnect_exhausted', { + reconnectAttempts: this.reconnectAttempts, + elapsedMs: elapsed, + }) + this.state = 'closed' + this.onCloseCallback?.() + } + } + + /** + * Bound timeout callback. Hoisted from an inline closure so that + * resetLivenessTimer (called per-frame) does not allocate a new closure + * on every SSE frame. + */ + private readonly onLivenessTimeout = (): void => { + this.livenessTimer = null + logForDebugging('SSETransport: Liveness timeout, reconnecting', { + level: 'error', + }) + logForDiagnosticsNoPII('error', 'cli_sse_liveness_timeout') + this.abortController?.abort() + this.handleConnectionError() + } + + /** + * Reset the liveness timer. If no SSE frame arrives within the timeout, + * treat the connection as dead and reconnect. + */ + private resetLivenessTimer(): void { + this.clearLivenessTimer() + this.livenessTimer = setTimeout(this.onLivenessTimeout, LIVENESS_TIMEOUT_MS) + } + + private clearLivenessTimer(): void { + if (this.livenessTimer) { + clearTimeout(this.livenessTimer) + this.livenessTimer = null + } + } + + // ----------------------------------------------------------------------- + // Write (HTTP POST) — same pattern as HybridTransport + // ----------------------------------------------------------------------- + + async write(message: StdoutMessage): Promise { + const authHeaders = this.getAuthHeaders() + if (Object.keys(authHeaders).length === 0) { + logForDebugging('SSETransport: No session token available for POST') + logForDiagnosticsNoPII('warn', 'cli_sse_post_no_token') + return + } + + const headers: Record = { + ...authHeaders, + 'Content-Type': 'application/json', + 'anthropic-version': '2023-06-01', + 'User-Agent': getClaudeCodeUserAgent(), + } + + logForDebugging( + `SSETransport: POST body keys=${Object.keys(message as Record).join(',')}`, + ) + + for (let attempt = 1; attempt <= POST_MAX_RETRIES; attempt++) { + try { + const response = await axios.post(this.postUrl, message, { + headers, + validateStatus: alwaysValidStatus, + }) + + if (response.status === 200 || response.status === 201) { + logForDebugging(`SSETransport: POST success type=${message.type}`) + return + } + + logForDebugging( + `SSETransport: POST ${response.status} body=${jsonStringify(response.data).slice(0, 200)}`, + ) + // 4xx errors (except 429) are permanent - don't retry + if ( + response.status >= 400 && + response.status < 500 && + response.status !== 429 + ) { + logForDebugging( + `SSETransport: POST returned ${response.status} (client error), not retrying`, + ) + logForDiagnosticsNoPII('warn', 'cli_sse_post_client_error', { + status: response.status, + }) + return + } + + // 429 or 5xx - retry + logForDebugging( + `SSETransport: POST returned ${response.status}, attempt ${attempt}/${POST_MAX_RETRIES}`, + ) + logForDiagnosticsNoPII('warn', 'cli_sse_post_retryable_error', { + status: response.status, + attempt, + }) + } catch (error) { + const axiosError = error as AxiosError + logForDebugging( + `SSETransport: POST error: ${axiosError.message}, attempt ${attempt}/${POST_MAX_RETRIES}`, + ) + logForDiagnosticsNoPII('warn', 'cli_sse_post_network_error', { + attempt, + }) + } + + if (attempt === POST_MAX_RETRIES) { + logForDebugging( + `SSETransport: POST failed after ${POST_MAX_RETRIES} attempts, continuing`, + ) + logForDiagnosticsNoPII('warn', 'cli_sse_post_retries_exhausted') + return + } + + const delayMs = Math.min( + POST_BASE_DELAY_MS * Math.pow(2, attempt - 1), + POST_MAX_DELAY_MS, + ) + await sleep(delayMs) + } + } + + // ----------------------------------------------------------------------- + // Transport interface + // ----------------------------------------------------------------------- + + isConnectedStatus(): boolean { + return this.state === 'connected' + } + + isClosedStatus(): boolean { + return this.state === 'closed' + } + + setOnData(callback: (data: string) => void): void { + this.onData = callback + } + + setOnClose(callback: (closeCode?: number) => void): void { + this.onCloseCallback = callback + } + + setOnEvent(callback: (event: StreamClientEvent) => void): void { + this.onEventCallback = callback + } + + close(): void { + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer) + this.reconnectTimer = null + } + this.clearLivenessTimer() + + this.state = 'closing' + this.abortController?.abort() + this.abortController = null + } +} + +// --------------------------------------------------------------------------- +// URL Conversion +// --------------------------------------------------------------------------- + +/** + * Convert an SSE URL to the HTTP POST endpoint URL. + * The SSE stream URL and POST URL share the same base; the POST endpoint + * is at `/events` (without `/stream`). + * + * From: https://api.example.com/v2/session_ingress/session//events/stream + * To: https://api.example.com/v2/session_ingress/session//events + */ +function convertSSEUrlToPostUrl(sseUrl: URL): string { + let pathname = sseUrl.pathname + // Remove /stream suffix to get the POST events endpoint + if (pathname.endsWith('/stream')) { + pathname = pathname.slice(0, -'/stream'.length) + } + return `${sseUrl.protocol}//${sseUrl.host}${pathname}` +} diff --git a/cli/transports/SerialBatchEventUploader.ts b/cli/transports/SerialBatchEventUploader.ts new file mode 100644 index 0000000..f753ca0 --- /dev/null +++ b/cli/transports/SerialBatchEventUploader.ts @@ -0,0 +1,275 @@ +import { jsonStringify } from '../../utils/slowOperations.js' + +/** + * Serial ordered event uploader with batching, retry, and backpressure. + * + * - enqueue() adds events to a pending buffer + * - At most 1 POST in-flight at a time + * - Drains up to maxBatchSize items per POST + * - New events accumulate while in-flight + * - On failure: exponential backoff (clamped), retries indefinitely + * until success or close() — unless maxConsecutiveFailures is set, + * in which case the failing batch is dropped and drain advances + * - flush() blocks until pending is empty and kicks drain if needed + * - Backpressure: enqueue() blocks when maxQueueSize is reached + */ + +/** + * Throw from config.send() to make the uploader wait a server-supplied + * duration before retrying (e.g. 429 with Retry-After). When retryAfterMs + * is set, it overrides exponential backoff for that attempt — clamped to + * [baseDelayMs, maxDelayMs] and jittered so a misbehaving server can + * neither hot-loop nor stall the client, and many sessions sharing a rate + * limit don't all pounce at the same instant. Without retryAfterMs, behaves + * like any other thrown error (exponential backoff). + */ +export class RetryableError extends Error { + constructor( + message: string, + readonly retryAfterMs?: number, + ) { + super(message) + } +} + +type SerialBatchEventUploaderConfig = { + /** Max items per POST (1 = no batching) */ + maxBatchSize: number + /** + * Max serialized bytes per POST. First item always goes in regardless of + * size; subsequent items only if cumulative JSON bytes stay under this. + * Undefined = no byte limit (count-only batching). + */ + maxBatchBytes?: number + /** Max pending items before enqueue() blocks */ + maxQueueSize: number + /** The actual HTTP call — caller controls payload format */ + send: (batch: T[]) => Promise + /** Base delay for exponential backoff (ms) */ + baseDelayMs: number + /** Max delay cap (ms) */ + maxDelayMs: number + /** Random jitter range added to retry delay (ms) */ + jitterMs: number + /** + * After this many consecutive send() failures, drop the failing batch + * and move on to the next pending item with a fresh failure budget. + * Undefined = retry indefinitely (default). + */ + maxConsecutiveFailures?: number + /** Called when a batch is dropped for hitting maxConsecutiveFailures. */ + onBatchDropped?: (batchSize: number, failures: number) => void +} + +export class SerialBatchEventUploader { + private pending: T[] = [] + private pendingAtClose = 0 + private draining = false + private closed = false + private backpressureResolvers: Array<() => void> = [] + private sleepResolve: (() => void) | null = null + private flushResolvers: Array<() => void> = [] + private droppedBatches = 0 + private readonly config: SerialBatchEventUploaderConfig + + constructor(config: SerialBatchEventUploaderConfig) { + this.config = config + } + + /** + * Monotonic count of batches dropped via maxConsecutiveFailures. Callers + * can snapshot before flush() and compare after to detect silent drops + * (flush() resolves normally even when batches were dropped). + */ + get droppedBatchCount(): number { + return this.droppedBatches + } + + /** + * Pending queue depth. After close(), returns the count at close time — + * close() clears the queue but shutdown diagnostics may read this after. + */ + get pendingCount(): number { + return this.closed ? this.pendingAtClose : this.pending.length + } + + /** + * Add events to the pending buffer. Returns immediately if space is + * available. Blocks (awaits) if the buffer is full — caller pauses + * until drain frees space. + */ + async enqueue(events: T | T[]): Promise { + if (this.closed) return + const items = Array.isArray(events) ? events : [events] + if (items.length === 0) return + + // Backpressure: wait until there's space + while ( + this.pending.length + items.length > this.config.maxQueueSize && + !this.closed + ) { + await new Promise(resolve => { + this.backpressureResolvers.push(resolve) + }) + } + + if (this.closed) return + this.pending.push(...items) + void this.drain() + } + + /** + * Block until all pending events have been sent. + * Used at turn boundaries and graceful shutdown. + */ + flush(): Promise { + if (this.pending.length === 0 && !this.draining) { + return Promise.resolve() + } + void this.drain() + return new Promise(resolve => { + this.flushResolvers.push(resolve) + }) + } + + /** + * Drop pending events and stop processing. + * Resolves any blocked enqueue() and flush() callers. + */ + close(): void { + if (this.closed) return + this.closed = true + this.pendingAtClose = this.pending.length + this.pending = [] + this.sleepResolve?.() + this.sleepResolve = null + for (const resolve of this.backpressureResolvers) resolve() + this.backpressureResolvers = [] + for (const resolve of this.flushResolvers) resolve() + this.flushResolvers = [] + } + + /** + * Drain loop. At most one instance runs at a time (guarded by this.draining). + * Sends batches serially. On failure, backs off and retries indefinitely. + */ + private async drain(): Promise { + if (this.draining || this.closed) return + this.draining = true + let failures = 0 + + try { + while (this.pending.length > 0 && !this.closed) { + const batch = this.takeBatch() + if (batch.length === 0) continue + + try { + await this.config.send(batch) + failures = 0 + } catch (err) { + failures++ + if ( + this.config.maxConsecutiveFailures !== undefined && + failures >= this.config.maxConsecutiveFailures + ) { + this.droppedBatches++ + this.config.onBatchDropped?.(batch.length, failures) + failures = 0 + this.releaseBackpressure() + continue + } + // Re-queue the failed batch at the front. Use concat (single + // allocation) instead of unshift(...batch) which shifts every + // pending item batch.length times. Only hit on failure path. + this.pending = batch.concat(this.pending) + const retryAfterMs = + err instanceof RetryableError ? err.retryAfterMs : undefined + await this.sleep(this.retryDelay(failures, retryAfterMs)) + continue + } + + // Release backpressure waiters if space opened up + this.releaseBackpressure() + } + } finally { + this.draining = false + // Notify flush waiters if queue is empty + if (this.pending.length === 0) { + for (const resolve of this.flushResolvers) resolve() + this.flushResolvers = [] + } + } + } + + /** + * Pull the next batch from pending. Respects both maxBatchSize and + * maxBatchBytes. The first item is always taken; subsequent items only + * if adding them keeps the cumulative JSON size under maxBatchBytes. + * + * Un-serializable items (BigInt, circular refs, throwing toJSON) are + * dropped in place — they can never be sent and leaving them at + * pending[0] would poison the queue and hang flush() forever. + */ + private takeBatch(): T[] { + const { maxBatchSize, maxBatchBytes } = this.config + if (maxBatchBytes === undefined) { + return this.pending.splice(0, maxBatchSize) + } + let bytes = 0 + let count = 0 + while (count < this.pending.length && count < maxBatchSize) { + let itemBytes: number + try { + itemBytes = Buffer.byteLength(jsonStringify(this.pending[count])) + } catch { + this.pending.splice(count, 1) + continue + } + if (count > 0 && bytes + itemBytes > maxBatchBytes) break + bytes += itemBytes + count++ + } + return this.pending.splice(0, count) + } + + private retryDelay(failures: number, retryAfterMs?: number): number { + const jitter = Math.random() * this.config.jitterMs + if (retryAfterMs !== undefined) { + // Jitter on top of the server's hint prevents thundering herd when + // many sessions share a rate limit and all receive the same + // Retry-After. Clamp first, then spread — same shape as the + // exponential path (effective ceiling is maxDelayMs + jitterMs). + const clamped = Math.max( + this.config.baseDelayMs, + Math.min(retryAfterMs, this.config.maxDelayMs), + ) + return clamped + jitter + } + const exponential = Math.min( + this.config.baseDelayMs * 2 ** (failures - 1), + this.config.maxDelayMs, + ) + return exponential + jitter + } + + private releaseBackpressure(): void { + const resolvers = this.backpressureResolvers + this.backpressureResolvers = [] + for (const resolve of resolvers) resolve() + } + + private sleep(ms: number): Promise { + return new Promise(resolve => { + this.sleepResolve = resolve + setTimeout( + (self, resolve) => { + self.sleepResolve = null + resolve() + }, + ms, + this, + resolve, + ) + }) + } +} diff --git a/cli/transports/WebSocketTransport.ts b/cli/transports/WebSocketTransport.ts new file mode 100644 index 0000000..f8e27ac --- /dev/null +++ b/cli/transports/WebSocketTransport.ts @@ -0,0 +1,800 @@ +import type { StdoutMessage } from 'src/entrypoints/sdk/controlTypes.js' +import type WsWebSocket from 'ws' +import { logEvent } from '../../services/analytics/index.js' +import { CircularBuffer } from '../../utils/CircularBuffer.js' +import { logForDebugging } from '../../utils/debug.js' +import { logForDiagnosticsNoPII } from '../../utils/diagLogs.js' +import { isEnvTruthy } from '../../utils/envUtils.js' +import { getWebSocketTLSOptions } from '../../utils/mtls.js' +import { + getWebSocketProxyAgent, + getWebSocketProxyUrl, +} from '../../utils/proxy.js' +import { + registerSessionActivityCallback, + unregisterSessionActivityCallback, +} from '../../utils/sessionActivity.js' +import { jsonStringify } from '../../utils/slowOperations.js' +import type { Transport } from './Transport.js' + +const KEEP_ALIVE_FRAME = '{"type":"keep_alive"}\n' + +const DEFAULT_MAX_BUFFER_SIZE = 1000 +const DEFAULT_BASE_RECONNECT_DELAY = 1000 +const DEFAULT_MAX_RECONNECT_DELAY = 30000 +/** Time budget for reconnection attempts before giving up (10 minutes). */ +const DEFAULT_RECONNECT_GIVE_UP_MS = 600_000 +const DEFAULT_PING_INTERVAL = 10000 +const DEFAULT_KEEPALIVE_INTERVAL = 300_000 // 5 minutes + +/** + * Threshold for detecting system sleep/wake. If the gap between consecutive + * reconnection attempts exceeds this, the machine likely slept. We reset + * the reconnection budget and retry — the server will reject with permanent + * close codes (4001/1002) if the session was reaped during sleep. + */ +const SLEEP_DETECTION_THRESHOLD_MS = DEFAULT_MAX_RECONNECT_DELAY * 2 // 60s + +/** + * WebSocket close codes that indicate a permanent server-side rejection. + * The transport transitions to 'closed' immediately without retrying. + */ +const PERMANENT_CLOSE_CODES = new Set([ + 1002, // protocol error — server rejected handshake (e.g. session reaped) + 4001, // session expired / not found + 4003, // unauthorized +]) + +export type WebSocketTransportOptions = { + /** When false, the transport does not attempt automatic reconnection on + * disconnect. Use this when the caller has its own recovery mechanism + * (e.g. the REPL bridge poll loop). Defaults to true. */ + autoReconnect?: boolean + /** Gates the tengu_ws_transport_* telemetry events. Set true at the + * REPL-bridge construction site so only Remote Control sessions (the + * Cloudflare-idle-timeout population) emit; print-mode workers stay + * silent. Defaults to false. */ + isBridge?: boolean +} + +type WebSocketTransportState = + | 'idle' + | 'connected' + | 'reconnecting' + | 'closing' + | 'closed' + +// Common interface between globalThis.WebSocket and ws.WebSocket +type WebSocketLike = { + close(): void + send(data: string): void + ping?(): void // Bun & ws both support this +} + +export class WebSocketTransport implements Transport { + private ws: WebSocketLike | null = null + private lastSentId: string | null = null + protected url: URL + protected state: WebSocketTransportState = 'idle' + protected onData?: (data: string) => void + private onCloseCallback?: (closeCode?: number) => void + private onConnectCallback?: () => void + private headers: Record + private sessionId?: string + private autoReconnect: boolean + private isBridge: boolean + + // Reconnection state + private reconnectAttempts = 0 + private reconnectStartTime: number | null = null + private reconnectTimer: NodeJS.Timeout | null = null + private lastReconnectAttemptTime: number | null = null + // Wall-clock of last WS data-frame activity (inbound message or outbound + // ws.send). Used to compute idle time at close — the signal for diagnosing + // proxy idle-timeout RSTs (e.g. Cloudflare 5-min). Excludes ping/pong + // control frames (proxies don't count those). + private lastActivityTime = 0 + + // Ping interval for connection health checks + private pingInterval: NodeJS.Timeout | null = null + private pongReceived = true + + // Periodic keep_alive data frames to reset proxy idle timers + private keepAliveInterval: NodeJS.Timeout | null = null + + // Message buffering for replay on reconnection + private messageBuffer: CircularBuffer + // Track which runtime's WS we're using so we can detach listeners + // with the matching API (removeEventListener vs. off). + private isBunWs = false + + // Captured at connect() time for handleOpenEvent timing. Stored as an + // instance field so the onOpen handler can be a stable class-property + // arrow function (removable in doDisconnect) instead of a closure over + // a local variable. + private connectStartTime = 0 + + private refreshHeaders?: () => Record + + constructor( + url: URL, + headers: Record = {}, + sessionId?: string, + refreshHeaders?: () => Record, + options?: WebSocketTransportOptions, + ) { + this.url = url + this.headers = headers + this.sessionId = sessionId + this.refreshHeaders = refreshHeaders + this.autoReconnect = options?.autoReconnect ?? true + this.isBridge = options?.isBridge ?? false + this.messageBuffer = new CircularBuffer(DEFAULT_MAX_BUFFER_SIZE) + } + + public async connect(): Promise { + if (this.state !== 'idle' && this.state !== 'reconnecting') { + logForDebugging( + `WebSocketTransport: Cannot connect, current state is ${this.state}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_websocket_connect_failed') + return + } + this.state = 'reconnecting' + + this.connectStartTime = Date.now() + logForDebugging(`WebSocketTransport: Opening ${this.url.href}`) + logForDiagnosticsNoPII('info', 'cli_websocket_connect_opening') + + // Start with provided headers and add runtime headers + const headers = { ...this.headers } + if (this.lastSentId) { + headers['X-Last-Request-Id'] = this.lastSentId + logForDebugging( + `WebSocketTransport: Adding X-Last-Request-Id header: ${this.lastSentId}`, + ) + } + + if (typeof Bun !== 'undefined') { + // Bun's WebSocket supports headers/proxy options but the DOM typings don't + // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins + const ws = new globalThis.WebSocket(this.url.href, { + headers, + proxy: getWebSocketProxyUrl(this.url.href), + tls: getWebSocketTLSOptions() || undefined, + } as unknown as string[]) + this.ws = ws + this.isBunWs = true + + ws.addEventListener('open', this.onBunOpen) + ws.addEventListener('message', this.onBunMessage) + ws.addEventListener('error', this.onBunError) + // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins + ws.addEventListener('close', this.onBunClose) + // 'pong' is Bun-specific — not in DOM typings. + ws.addEventListener('pong', this.onPong) + } else { + const { default: WS } = await import('ws') + const ws = new WS(this.url.href, { + headers, + agent: getWebSocketProxyAgent(this.url.href), + ...getWebSocketTLSOptions(), + }) + this.ws = ws + this.isBunWs = false + + ws.on('open', this.onNodeOpen) + ws.on('message', this.onNodeMessage) + ws.on('error', this.onNodeError) + ws.on('close', this.onNodeClose) + ws.on('pong', this.onPong) + } + } + + // --- Bun (native WebSocket) event handlers --- + // Stored as class-property arrow functions so they can be removed in + // doDisconnect(). Without removal, each reconnect orphans the old WS + // object + its 5 closures until GC, which accumulates under network + // instability. Mirrors the pattern in src/utils/mcpWebSocketTransport.ts. + + private onBunOpen = () => { + this.handleOpenEvent() + // Bun's WebSocket doesn't expose upgrade response headers, + // so replay all buffered messages. The server deduplicates by UUID. + if (this.lastSentId) { + this.replayBufferedMessages('') + } + } + + private onBunMessage = (event: MessageEvent) => { + const message = + typeof event.data === 'string' ? event.data : String(event.data) + this.lastActivityTime = Date.now() + logForDiagnosticsNoPII('info', 'cli_websocket_message_received', { + length: message.length, + }) + if (this.onData) { + this.onData(message) + } + } + + private onBunError = () => { + logForDebugging('WebSocketTransport: Error', { + level: 'error', + }) + logForDiagnosticsNoPII('error', 'cli_websocket_connect_error') + // close event fires after error — let it call handleConnectionError + } + + // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins + private onBunClose = (event: CloseEvent) => { + const isClean = event.code === 1000 || event.code === 1001 + logForDebugging( + `WebSocketTransport: Closed: ${event.code}`, + isClean ? undefined : { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_websocket_connect_closed') + this.handleConnectionError(event.code) + } + + // --- Node (ws package) event handlers --- + + private onNodeOpen = () => { + // Capture ws before handleOpenEvent() invokes onConnectCallback — if the + // callback synchronously closes the transport, this.ws becomes null. + // The old inline-closure code had this safety implicitly via closure capture. + const ws = this.ws + this.handleOpenEvent() + if (!ws) return + // Check for last-id in upgrade response headers (ws package only) + const nws = ws as unknown as WsWebSocket & { + upgradeReq?: { headers?: Record } + } + const upgradeResponse = nws.upgradeReq + if (upgradeResponse?.headers?.['x-last-request-id']) { + const serverLastId = upgradeResponse.headers['x-last-request-id'] + this.replayBufferedMessages(serverLastId) + } + } + + private onNodeMessage = (data: Buffer) => { + const message = data.toString() + this.lastActivityTime = Date.now() + logForDiagnosticsNoPII('info', 'cli_websocket_message_received', { + length: message.length, + }) + if (this.onData) { + this.onData(message) + } + } + + private onNodeError = (err: Error) => { + logForDebugging(`WebSocketTransport: Error: ${err.message}`, { + level: 'error', + }) + logForDiagnosticsNoPII('error', 'cli_websocket_connect_error') + // close event fires after error — let it call handleConnectionError + } + + private onNodeClose = (code: number, _reason: Buffer) => { + const isClean = code === 1000 || code === 1001 + logForDebugging( + `WebSocketTransport: Closed: ${code}`, + isClean ? undefined : { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_websocket_connect_closed') + this.handleConnectionError(code) + } + + // --- Shared handlers --- + + private onPong = () => { + this.pongReceived = true + } + + private handleOpenEvent(): void { + const connectDuration = Date.now() - this.connectStartTime + logForDebugging('WebSocketTransport: Connected') + logForDiagnosticsNoPII('info', 'cli_websocket_connect_connected', { + duration_ms: connectDuration, + }) + + // Reconnect success — capture attempt count + downtime before resetting. + // reconnectStartTime is null on first connect, non-null on reopen. + if (this.isBridge && this.reconnectStartTime !== null) { + logEvent('tengu_ws_transport_reconnected', { + attempts: this.reconnectAttempts, + downtimeMs: Date.now() - this.reconnectStartTime, + }) + } + + this.reconnectAttempts = 0 + this.reconnectStartTime = null + this.lastReconnectAttemptTime = null + this.lastActivityTime = Date.now() + this.state = 'connected' + this.onConnectCallback?.() + + // Start periodic pings to detect dead connections + this.startPingInterval() + + // Start periodic keep_alive data frames to reset proxy idle timers + this.startKeepaliveInterval() + + // Register callback for session activity signals + registerSessionActivityCallback(() => { + void this.write({ type: 'keep_alive' }) + }) + } + + protected sendLine(line: string): boolean { + if (!this.ws || this.state !== 'connected') { + logForDebugging('WebSocketTransport: Not connected') + logForDiagnosticsNoPII('info', 'cli_websocket_send_not_connected') + return false + } + + try { + this.ws.send(line) + this.lastActivityTime = Date.now() + return true + } catch (error) { + logForDebugging(`WebSocketTransport: Failed to send: ${error}`, { + level: 'error', + }) + logForDiagnosticsNoPII('error', 'cli_websocket_send_error') + // Don't null this.ws here — let doDisconnect() (via handleConnectionError) + // handle cleanup so listeners are removed before the WS is released. + this.handleConnectionError() + return false + } + } + + /** + * Remove all listeners attached in connect() for the given WebSocket. + * Without this, each reconnect orphans the old WS object + its closures + * until GC — these accumulate under network instability. Mirrors the + * pattern in src/utils/mcpWebSocketTransport.ts. + */ + private removeWsListeners(ws: WebSocketLike): void { + if (this.isBunWs) { + const nws = ws as unknown as globalThis.WebSocket + nws.removeEventListener('open', this.onBunOpen) + nws.removeEventListener('message', this.onBunMessage) + nws.removeEventListener('error', this.onBunError) + // eslint-disable-next-line eslint-plugin-n/no-unsupported-features/node-builtins + nws.removeEventListener('close', this.onBunClose) + // 'pong' is Bun-specific — not in DOM typings + nws.removeEventListener('pong' as 'message', this.onPong) + } else { + const nws = ws as unknown as WsWebSocket + nws.off('open', this.onNodeOpen) + nws.off('message', this.onNodeMessage) + nws.off('error', this.onNodeError) + nws.off('close', this.onNodeClose) + nws.off('pong', this.onPong) + } + } + + protected doDisconnect(): void { + // Stop pinging and keepalive when disconnecting + this.stopPingInterval() + this.stopKeepaliveInterval() + + // Unregister session activity callback + unregisterSessionActivityCallback() + + if (this.ws) { + // Remove listeners BEFORE close() so the old WS + closures can be + // GC'd promptly instead of lingering until the next mark-and-sweep. + this.removeWsListeners(this.ws) + this.ws.close() + this.ws = null + } + } + + private handleConnectionError(closeCode?: number): void { + logForDebugging( + `WebSocketTransport: Disconnected from ${this.url.href}` + + (closeCode != null ? ` (code ${closeCode})` : ''), + ) + logForDiagnosticsNoPII('info', 'cli_websocket_disconnected') + if (this.isBridge) { + // Fire on every close — including intermediate ones during a reconnect + // storm (those never surface to the onCloseCallback consumer). For the + // Cloudflare-5min-idle hypothesis: cluster msSinceLastActivity; if the + // peak sits at ~300s with closeCode 1006, that's the proxy RST. + logEvent('tengu_ws_transport_closed', { + closeCode, + msSinceLastActivity: + this.lastActivityTime > 0 ? Date.now() - this.lastActivityTime : -1, + // 'connected' = healthy drop (the Cloudflare case); 'reconnecting' = + // connect-rejection mid-storm. State isn't mutated until the branches + // below, so this reads the pre-close value. + wasConnected: this.state === 'connected', + reconnectAttempts: this.reconnectAttempts, + }) + } + this.doDisconnect() + + if (this.state === 'closing' || this.state === 'closed') return + + // Permanent codes: don't retry — server has definitively ended the session. + // Exception: 4003 (unauthorized) can be retried when refreshHeaders is + // available and returns a new token (e.g. after the parent process mints + // a fresh session ingress token during reconnection). + let headersRefreshed = false + if (closeCode === 4003 && this.refreshHeaders) { + const freshHeaders = this.refreshHeaders() + if (freshHeaders.Authorization !== this.headers.Authorization) { + Object.assign(this.headers, freshHeaders) + headersRefreshed = true + logForDebugging( + 'WebSocketTransport: 4003 received but headers refreshed, scheduling reconnect', + ) + logForDiagnosticsNoPII('info', 'cli_websocket_4003_token_refreshed') + } + } + + if ( + closeCode != null && + PERMANENT_CLOSE_CODES.has(closeCode) && + !headersRefreshed + ) { + logForDebugging( + `WebSocketTransport: Permanent close code ${closeCode}, not reconnecting`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_websocket_permanent_close', { + closeCode, + }) + this.state = 'closed' + this.onCloseCallback?.(closeCode) + return + } + + // When autoReconnect is disabled, go straight to closed state. + // The caller (e.g. REPL bridge poll loop) handles recovery. + if (!this.autoReconnect) { + this.state = 'closed' + this.onCloseCallback?.(closeCode) + return + } + + // Schedule reconnection with exponential backoff and time budget + const now = Date.now() + if (!this.reconnectStartTime) { + this.reconnectStartTime = now + } + + // Detect system sleep/wake: if the gap since our last reconnection + // attempt greatly exceeds the max delay, the machine likely slept + // (e.g. laptop lid closed). Reset the budget and retry from scratch — + // the server will reject with permanent close codes (4001/1002) if + // the session was reaped while we were asleep. + if ( + this.lastReconnectAttemptTime !== null && + now - this.lastReconnectAttemptTime > SLEEP_DETECTION_THRESHOLD_MS + ) { + logForDebugging( + `WebSocketTransport: Detected system sleep (${Math.round((now - this.lastReconnectAttemptTime) / 1000)}s gap), resetting reconnection budget`, + ) + logForDiagnosticsNoPII('info', 'cli_websocket_sleep_detected', { + gapMs: now - this.lastReconnectAttemptTime, + }) + this.reconnectStartTime = now + this.reconnectAttempts = 0 + } + this.lastReconnectAttemptTime = now + + const elapsed = now - this.reconnectStartTime + if (elapsed < DEFAULT_RECONNECT_GIVE_UP_MS) { + // Clear any existing reconnection timer to avoid duplicates + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer) + this.reconnectTimer = null + } + + // Refresh headers before reconnecting (e.g. to pick up a new session token). + // Skip if already refreshed by the 4003 path above. + if (!headersRefreshed && this.refreshHeaders) { + const freshHeaders = this.refreshHeaders() + Object.assign(this.headers, freshHeaders) + logForDebugging('WebSocketTransport: Refreshed headers for reconnect') + } + + this.state = 'reconnecting' + this.reconnectAttempts++ + + const baseDelay = Math.min( + DEFAULT_BASE_RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts - 1), + DEFAULT_MAX_RECONNECT_DELAY, + ) + // Add ±25% jitter to avoid thundering herd + const delay = Math.max( + 0, + baseDelay + baseDelay * 0.25 * (2 * Math.random() - 1), + ) + + logForDebugging( + `WebSocketTransport: Reconnecting in ${Math.round(delay)}ms (attempt ${this.reconnectAttempts}, ${Math.round(elapsed / 1000)}s elapsed)`, + ) + logForDiagnosticsNoPII('error', 'cli_websocket_reconnect_attempt', { + reconnectAttempts: this.reconnectAttempts, + }) + if (this.isBridge) { + logEvent('tengu_ws_transport_reconnecting', { + attempt: this.reconnectAttempts, + elapsedMs: elapsed, + delayMs: Math.round(delay), + }) + } + + this.reconnectTimer = setTimeout(() => { + this.reconnectTimer = null + void this.connect() + }, delay) + } else { + logForDebugging( + `WebSocketTransport: Reconnection time budget exhausted after ${Math.round(elapsed / 1000)}s for ${this.url.href}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_websocket_reconnect_exhausted', { + reconnectAttempts: this.reconnectAttempts, + elapsedMs: elapsed, + }) + this.state = 'closed' + + // Notify close callback + if (this.onCloseCallback) { + this.onCloseCallback(closeCode) + } + } + } + + close(): void { + // Clear any pending reconnection timer + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer) + this.reconnectTimer = null + } + + // Clear ping and keepalive intervals + this.stopPingInterval() + this.stopKeepaliveInterval() + + // Unregister session activity callback + unregisterSessionActivityCallback() + + this.state = 'closing' + this.doDisconnect() + } + + private replayBufferedMessages(lastId: string): void { + const messages = this.messageBuffer.toArray() + if (messages.length === 0) return + + // Find where to start replay based on server's last received message + let startIndex = 0 + if (lastId) { + const lastConfirmedIndex = messages.findIndex( + message => 'uuid' in message && message.uuid === lastId, + ) + if (lastConfirmedIndex >= 0) { + // Server confirmed messages up to lastConfirmedIndex — evict them + startIndex = lastConfirmedIndex + 1 + // Rebuild the buffer with only unconfirmed messages + const remaining = messages.slice(startIndex) + this.messageBuffer.clear() + this.messageBuffer.addAll(remaining) + if (remaining.length === 0) { + this.lastSentId = null + } + logForDebugging( + `WebSocketTransport: Evicted ${startIndex} confirmed messages, ${remaining.length} remaining`, + ) + logForDiagnosticsNoPII( + 'info', + 'cli_websocket_evicted_confirmed_messages', + { + evicted: startIndex, + remaining: remaining.length, + }, + ) + } + } + + const messagesToReplay = messages.slice(startIndex) + if (messagesToReplay.length === 0) { + logForDebugging('WebSocketTransport: No new messages to replay') + logForDiagnosticsNoPII('info', 'cli_websocket_no_messages_to_replay') + return + } + + logForDebugging( + `WebSocketTransport: Replaying ${messagesToReplay.length} buffered messages`, + ) + logForDiagnosticsNoPII('info', 'cli_websocket_messages_to_replay', { + count: messagesToReplay.length, + }) + + for (const message of messagesToReplay) { + const line = jsonStringify(message) + '\n' + const success = this.sendLine(line) + if (!success) { + this.handleConnectionError() + break + } + } + // Do NOT clear the buffer after replay — messages remain buffered until + // the server confirms receipt on the next reconnection. This prevents + // message loss if the connection drops after replay but before the server + // processes the messages. + } + + isConnectedStatus(): boolean { + return this.state === 'connected' + } + + isClosedStatus(): boolean { + return this.state === 'closed' + } + + setOnData(callback: (data: string) => void): void { + this.onData = callback + } + + setOnConnect(callback: () => void): void { + this.onConnectCallback = callback + } + + setOnClose(callback: (closeCode?: number) => void): void { + this.onCloseCallback = callback + } + + getStateLabel(): string { + return this.state + } + + async write(message: StdoutMessage): Promise { + if ('uuid' in message && typeof message.uuid === 'string') { + this.messageBuffer.add(message) + this.lastSentId = message.uuid + } + + const line = jsonStringify(message) + '\n' + + if (this.state !== 'connected') { + // Message buffered for replay when connected (if it has a UUID) + return + } + + const sessionLabel = this.sessionId ? ` session=${this.sessionId}` : '' + const detailLabel = this.getControlMessageDetailLabel(message) + + logForDebugging( + `WebSocketTransport: Sending message type=${message.type}${sessionLabel}${detailLabel}`, + ) + + this.sendLine(line) + } + + private getControlMessageDetailLabel(message: StdoutMessage): string { + if (message.type === 'control_request') { + const { request_id, request } = message + const toolName = + request.subtype === 'can_use_tool' ? request.tool_name : '' + return ` subtype=${request.subtype} request_id=${request_id}${toolName ? ` tool=${toolName}` : ''}` + } + if (message.type === 'control_response') { + const { subtype, request_id } = message.response + return ` subtype=${subtype} request_id=${request_id}` + } + return '' + } + + private startPingInterval(): void { + // Clear any existing interval + this.stopPingInterval() + + this.pongReceived = true + let lastTickTime = Date.now() + + // Send ping periodically to detect dead connections. + // If the previous ping got no pong, treat the connection as dead. + this.pingInterval = setInterval(() => { + if (this.state === 'connected' && this.ws) { + const now = Date.now() + const gap = now - lastTickTime + lastTickTime = now + + // Process-suspension detector. If the wall-clock gap between ticks + // greatly exceeds the 10s interval, the process was suspended + // (laptop lid, SIGSTOP, VM pause). setInterval does not queue + // missed ticks — it coalesces — so on wake this callback fires + // once with a huge gap. The socket is almost certainly dead: + // NAT mappings drop in 30s–5min, and the server has been + // retransmitting into the void. Don't wait for a ping/pong + // round-trip to confirm (ws.ping() on a dead socket returns + // immediately with no error — bytes go into the kernel send + // buffer). Assume dead and reconnect now. A spurious reconnect + // after a short sleep is cheap — replayBufferedMessages() handles + // it and the server dedups by UUID. + if (gap > SLEEP_DETECTION_THRESHOLD_MS) { + logForDebugging( + `WebSocketTransport: ${Math.round(gap / 1000)}s tick gap detected — process was suspended, forcing reconnect`, + ) + logForDiagnosticsNoPII( + 'info', + 'cli_websocket_sleep_detected_on_ping', + { gapMs: gap }, + ) + this.handleConnectionError() + return + } + + if (!this.pongReceived) { + logForDebugging( + 'WebSocketTransport: No pong received, connection appears dead', + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_websocket_pong_timeout') + this.handleConnectionError() + return + } + + this.pongReceived = false + try { + this.ws.ping?.() + } catch (error) { + logForDebugging(`WebSocketTransport: Ping failed: ${error}`, { + level: 'error', + }) + logForDiagnosticsNoPII('error', 'cli_websocket_ping_failed') + } + } + }, DEFAULT_PING_INTERVAL) + } + + private stopPingInterval(): void { + if (this.pingInterval) { + clearInterval(this.pingInterval) + this.pingInterval = null + } + } + + private startKeepaliveInterval(): void { + this.stopKeepaliveInterval() + + // In CCR sessions, session activity heartbeats handle keep-alives + if (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE)) { + return + } + + this.keepAliveInterval = setInterval(() => { + if (this.state === 'connected' && this.ws) { + try { + this.ws.send(KEEP_ALIVE_FRAME) + this.lastActivityTime = Date.now() + logForDebugging( + 'WebSocketTransport: Sent periodic keep_alive data frame', + ) + } catch (error) { + logForDebugging( + `WebSocketTransport: Periodic keep_alive failed: ${error}`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_websocket_keepalive_failed') + } + } + }, DEFAULT_KEEPALIVE_INTERVAL) + } + + private stopKeepaliveInterval(): void { + if (this.keepAliveInterval) { + clearInterval(this.keepAliveInterval) + this.keepAliveInterval = null + } + } +} diff --git a/cli/transports/WorkerStateUploader.ts b/cli/transports/WorkerStateUploader.ts new file mode 100644 index 0000000..37427b4 --- /dev/null +++ b/cli/transports/WorkerStateUploader.ts @@ -0,0 +1,131 @@ +import { sleep } from '../../utils/sleep.js' + +/** + * Coalescing uploader for PUT /worker (session state + metadata). + * + * - 1 in-flight PUT + 1 pending patch + * - New calls coalesce into pending (never grows beyond 1 slot) + * - On success: send pending if exists + * - On failure: exponential backoff (clamped), retries indefinitely + * until success or close(). Absorbs any pending patches before each retry. + * - No backpressure needed — naturally bounded at 2 slots + * + * Coalescing rules: + * - Top-level keys (worker_status, external_metadata) — last value wins + * - Inside external_metadata / internal_metadata — RFC 7396 merge: + * keys are added/overwritten, null values preserved (server deletes) + */ + +type WorkerStateUploaderConfig = { + send: (body: Record) => Promise + /** Base delay for exponential backoff (ms) */ + baseDelayMs: number + /** Max delay cap (ms) */ + maxDelayMs: number + /** Random jitter range added to retry delay (ms) */ + jitterMs: number +} + +export class WorkerStateUploader { + private inflight: Promise | null = null + private pending: Record | null = null + private closed = false + private readonly config: WorkerStateUploaderConfig + + constructor(config: WorkerStateUploaderConfig) { + this.config = config + } + + /** + * Enqueue a patch to PUT /worker. Coalesces with any existing pending + * patch. Fire-and-forget — callers don't need to await. + */ + enqueue(patch: Record): void { + if (this.closed) return + this.pending = this.pending ? coalescePatches(this.pending, patch) : patch + void this.drain() + } + + close(): void { + this.closed = true + this.pending = null + } + + private async drain(): Promise { + if (this.inflight || this.closed) return + if (!this.pending) return + + const payload = this.pending + this.pending = null + + this.inflight = this.sendWithRetry(payload).then(() => { + this.inflight = null + if (this.pending && !this.closed) { + void this.drain() + } + }) + } + + /** Retries indefinitely with exponential backoff until success or close(). */ + private async sendWithRetry(payload: Record): Promise { + let current = payload + let failures = 0 + while (!this.closed) { + const ok = await this.config.send(current) + if (ok) return + + failures++ + await sleep(this.retryDelay(failures)) + + // Absorb any patches that arrived during the retry + if (this.pending && !this.closed) { + current = coalescePatches(current, this.pending) + this.pending = null + } + } + } + + private retryDelay(failures: number): number { + const exponential = Math.min( + this.config.baseDelayMs * 2 ** (failures - 1), + this.config.maxDelayMs, + ) + const jitter = Math.random() * this.config.jitterMs + return exponential + jitter + } +} + +/** + * Coalesce two patches for PUT /worker. + * + * Top-level keys: overlay replaces base (last value wins). + * Metadata keys (external_metadata, internal_metadata): RFC 7396 merge + * one level deep — overlay keys are added/overwritten, null values + * preserved for server-side delete. + */ +function coalescePatches( + base: Record, + overlay: Record, +): Record { + const merged = { ...base } + + for (const [key, value] of Object.entries(overlay)) { + if ( + (key === 'external_metadata' || key === 'internal_metadata') && + merged[key] && + typeof merged[key] === 'object' && + typeof value === 'object' && + value !== null + ) { + // RFC 7396 merge — overlay keys win, nulls preserved for server + merged[key] = { + ...(merged[key] as Record), + ...(value as Record), + } + } else { + merged[key] = value + } + } + + return merged +} diff --git a/cli/transports/ccrClient.ts b/cli/transports/ccrClient.ts new file mode 100644 index 0000000..da3dc2e --- /dev/null +++ b/cli/transports/ccrClient.ts @@ -0,0 +1,998 @@ +import { randomUUID } from 'crypto' +import type { + SDKPartialAssistantMessage, + StdoutMessage, +} from 'src/entrypoints/sdk/controlTypes.js' +import { decodeJwtExpiry } from '../../bridge/jwtUtils.js' +import { logForDebugging } from '../../utils/debug.js' +import { logForDiagnosticsNoPII } from '../../utils/diagLogs.js' +import { errorMessage, getErrnoCode } from '../../utils/errors.js' +import { createAxiosInstance } from '../../utils/proxy.js' +import { + registerSessionActivityCallback, + unregisterSessionActivityCallback, +} from '../../utils/sessionActivity.js' +import { + getSessionIngressAuthHeaders, + getSessionIngressAuthToken, +} from '../../utils/sessionIngressAuth.js' +import type { + RequiresActionDetails, + SessionState, +} from '../../utils/sessionState.js' +import { sleep } from '../../utils/sleep.js' +import { getClaudeCodeUserAgent } from '../../utils/userAgent.js' +import { + RetryableError, + SerialBatchEventUploader, +} from './SerialBatchEventUploader.js' +import type { SSETransport, StreamClientEvent } from './SSETransport.js' +import { WorkerStateUploader } from './WorkerStateUploader.js' + +/** Default interval between heartbeat events (20s; server TTL is 60s). */ +const DEFAULT_HEARTBEAT_INTERVAL_MS = 20_000 + +/** + * stream_event messages accumulate in a delay buffer for up to this many ms + * before enqueue. Mirrors HybridTransport's batching window. text_delta + * events for the same content block accumulate into a single full-so-far + * snapshot per flush — each emitted event is self-contained so a client + * connecting mid-stream sees complete text, not a fragment. + */ +const STREAM_EVENT_FLUSH_INTERVAL_MS = 100 + +/** Hoisted axios validateStatus callback to avoid per-request closure allocation. */ +function alwaysValidStatus(): boolean { + return true +} + +export type CCRInitFailReason = + | 'no_auth_headers' + | 'missing_epoch' + | 'worker_register_failed' + +/** Thrown by initialize(); carries a typed reason for the diag classifier. */ +export class CCRInitError extends Error { + constructor(readonly reason: CCRInitFailReason) { + super(`CCRClient init failed: ${reason}`) + } +} + +/** + * Consecutive 401/403 with a VALID-LOOKING token before giving up. An + * expired JWT short-circuits this (exits immediately — deterministic, + * retry is futile). This threshold is for the uncertain case: token's + * exp is in the future but server says 401 (userauth down, KMS hiccup, + * clock skew). 10 × 20s heartbeat ≈ 200s to ride it out. + */ +const MAX_CONSECUTIVE_AUTH_FAILURES = 10 + +type EventPayload = { + uuid: string + type: string + [key: string]: unknown +} + +type ClientEvent = { + payload: EventPayload + ephemeral?: boolean +} + +/** + * Structural subset of a stream_event carrying a text_delta. Not a narrowing + * of SDKPartialAssistantMessage — RawMessageStreamEvent's delta is a union and + * narrowing through two levels defeats the discriminant. + */ +type CoalescedStreamEvent = { + type: 'stream_event' + uuid: string + session_id: string + parent_tool_use_id: string | null + event: { + type: 'content_block_delta' + index: number + delta: { type: 'text_delta'; text: string } + } +} + +/** + * Accumulator state for text_delta coalescing. Keyed by API message ID so + * lifetime is tied to the assistant message — cleared when the complete + * SDKAssistantMessage arrives (writeEvent), which is reliable even when + * abort/error paths skip content_block_stop/message_stop delivery. + */ +export type StreamAccumulatorState = { + /** API message ID (msg_...) → blocks[blockIndex] → chunk array. */ + byMessage: Map + /** + * {session_id}:{parent_tool_use_id} → active message ID. + * content_block_delta events don't carry the message ID (only + * message_start does), so we track which message is currently streaming + * for each scope. At most one message streams per scope at a time. + */ + scopeToMessage: Map +} + +export function createStreamAccumulator(): StreamAccumulatorState { + return { byMessage: new Map(), scopeToMessage: new Map() } +} + +function scopeKey(m: { + session_id: string + parent_tool_use_id: string | null +}): string { + return `${m.session_id}:${m.parent_tool_use_id ?? ''}` +} + +/** + * Accumulate text_delta stream_events into full-so-far snapshots per content + * block. Each flush emits ONE event per touched block containing the FULL + * accumulated text from the start of the block — a client connecting + * mid-stream receives a self-contained snapshot, not a fragment. + * + * Non-text-delta events pass through unchanged. message_start records the + * active message ID for the scope; content_block_delta appends chunks; + * the snapshot event reuses the first text_delta UUID seen for that block in + * this flush so server-side idempotency remains stable across retries. + * + * Cleanup happens in writeEvent when the complete assistant message arrives + * (reliable), not here on stop events (abort/error paths skip those). + */ +export function accumulateStreamEvents( + buffer: SDKPartialAssistantMessage[], + state: StreamAccumulatorState, +): EventPayload[] { + const out: EventPayload[] = [] + // chunks[] → snapshot already in `out` this flush. Keyed by the chunks + // array reference (stable per {messageId, index}) so subsequent deltas + // rewrite the same entry instead of emitting one event per delta. + const touched = new Map() + for (const msg of buffer) { + switch (msg.event.type) { + case 'message_start': { + const id = msg.event.message.id + const prevId = state.scopeToMessage.get(scopeKey(msg)) + if (prevId) state.byMessage.delete(prevId) + state.scopeToMessage.set(scopeKey(msg), id) + state.byMessage.set(id, []) + out.push(msg) + break + } + case 'content_block_delta': { + if (msg.event.delta.type !== 'text_delta') { + out.push(msg) + break + } + const messageId = state.scopeToMessage.get(scopeKey(msg)) + const blocks = messageId ? state.byMessage.get(messageId) : undefined + if (!blocks) { + // Delta without a preceding message_start (reconnect mid-stream, + // or message_start was in a prior buffer that got dropped). Pass + // through raw — can't produce a full-so-far snapshot without the + // prior chunks anyway. + out.push(msg) + break + } + const chunks = (blocks[msg.event.index] ??= []) + chunks.push(msg.event.delta.text) + const existing = touched.get(chunks) + if (existing) { + existing.event.delta.text = chunks.join('') + break + } + const snapshot: CoalescedStreamEvent = { + type: 'stream_event', + uuid: msg.uuid, + session_id: msg.session_id, + parent_tool_use_id: msg.parent_tool_use_id, + event: { + type: 'content_block_delta', + index: msg.event.index, + delta: { type: 'text_delta', text: chunks.join('') }, + }, + } + touched.set(chunks, snapshot) + out.push(snapshot) + break + } + default: + out.push(msg) + } + } + return out +} + +/** + * Clear accumulator entries for a completed assistant message. Called from + * writeEvent when the SDKAssistantMessage arrives — the reliable end-of-stream + * signal that fires even when abort/interrupt/error skip SSE stop events. + */ +export function clearStreamAccumulatorForMessage( + state: StreamAccumulatorState, + assistant: { + session_id: string + parent_tool_use_id: string | null + message: { id: string } + }, +): void { + state.byMessage.delete(assistant.message.id) + const scope = scopeKey(assistant) + if (state.scopeToMessage.get(scope) === assistant.message.id) { + state.scopeToMessage.delete(scope) + } +} + +type RequestResult = { ok: true } | { ok: false; retryAfterMs?: number } + +type WorkerEvent = { + payload: EventPayload + is_compaction?: boolean + agent_id?: string +} + +export type InternalEvent = { + event_id: string + event_type: string + payload: Record + event_metadata?: Record | null + is_compaction: boolean + created_at: string + agent_id?: string +} + +type ListInternalEventsResponse = { + data: InternalEvent[] + next_cursor?: string +} + +type WorkerStateResponse = { + worker?: { + external_metadata?: Record + } +} + +/** + * Manages the worker lifecycle protocol with CCR v2: + * - Epoch management: reads worker_epoch from CLAUDE_CODE_WORKER_EPOCH env var + * - Runtime state reporting: PUT /sessions/{id}/worker + * - Heartbeat: POST /sessions/{id}/worker/heartbeat for liveness detection + * + * All writes go through this.request(). + */ +export class CCRClient { + private workerEpoch = 0 + private readonly heartbeatIntervalMs: number + private readonly heartbeatJitterFraction: number + private heartbeatTimer: NodeJS.Timeout | null = null + private heartbeatInFlight = false + private closed = false + private consecutiveAuthFailures = 0 + private currentState: SessionState | null = null + private readonly sessionBaseUrl: string + private readonly sessionId: string + private readonly http = createAxiosInstance({ keepAlive: true }) + + // stream_event delay buffer — accumulates content deltas for up to + // STREAM_EVENT_FLUSH_INTERVAL_MS before enqueueing (reduces POST count + // and enables text_delta coalescing). Mirrors HybridTransport's pattern. + private streamEventBuffer: SDKPartialAssistantMessage[] = [] + private streamEventTimer: ReturnType | null = null + // Full-so-far text accumulator. Persists across flushes so each emitted + // text_delta event carries the complete text from the start of the block — + // mid-stream reconnects see a self-contained snapshot. Keyed by API message + // ID; cleared in writeEvent when the complete assistant message arrives. + private streamTextAccumulator = createStreamAccumulator() + + private readonly workerState: WorkerStateUploader + private readonly eventUploader: SerialBatchEventUploader + private readonly internalEventUploader: SerialBatchEventUploader + private readonly deliveryUploader: SerialBatchEventUploader<{ + eventId: string + status: 'received' | 'processing' | 'processed' + }> + + /** + * Called when the server returns 409 (a newer worker epoch superseded ours). + * Default: process.exit(1) — correct for spawn-mode children where the + * parent bridge re-spawns. In-process callers (replBridge) MUST override + * this to close gracefully instead; exit would kill the user's REPL. + */ + private readonly onEpochMismatch: () => never + + /** + * Auth header source. Defaults to the process-wide session-ingress token + * (CLAUDE_CODE_SESSION_ACCESS_TOKEN env var). Callers managing multiple + * concurrent sessions with distinct JWTs MUST inject this — the env-var + * path is a process global and would stomp across sessions. + */ + private readonly getAuthHeaders: () => Record + + constructor( + transport: SSETransport, + sessionUrl: URL, + opts?: { + onEpochMismatch?: () => never + heartbeatIntervalMs?: number + heartbeatJitterFraction?: number + /** + * Per-instance auth header source. Omit to read the process-wide + * CLAUDE_CODE_SESSION_ACCESS_TOKEN (single-session callers — REPL, + * daemon). Required for concurrent multi-session callers. + */ + getAuthHeaders?: () => Record + }, + ) { + this.onEpochMismatch = + opts?.onEpochMismatch ?? + (() => { + // eslint-disable-next-line custom-rules/no-process-exit + process.exit(1) + }) + this.heartbeatIntervalMs = + opts?.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS + this.heartbeatJitterFraction = opts?.heartbeatJitterFraction ?? 0 + this.getAuthHeaders = opts?.getAuthHeaders ?? getSessionIngressAuthHeaders + // Session URL: https://host/v1/code/sessions/{id} + if (sessionUrl.protocol !== 'http:' && sessionUrl.protocol !== 'https:') { + throw new Error( + `CCRClient: Expected http(s) URL, got ${sessionUrl.protocol}`, + ) + } + const pathname = sessionUrl.pathname.replace(/\/$/, '') + this.sessionBaseUrl = `${sessionUrl.protocol}//${sessionUrl.host}${pathname}` + // Extract session ID from the URL path (last segment) + this.sessionId = pathname.split('/').pop() || '' + + this.workerState = new WorkerStateUploader({ + send: body => + this.request( + 'put', + '/worker', + { worker_epoch: this.workerEpoch, ...body }, + 'PUT worker', + ).then(r => r.ok), + baseDelayMs: 500, + maxDelayMs: 30_000, + jitterMs: 500, + }) + + this.eventUploader = new SerialBatchEventUploader({ + maxBatchSize: 100, + maxBatchBytes: 10 * 1024 * 1024, + // flushStreamEventBuffer() enqueues a full 100ms window of accumulated + // stream_events in one call. A burst of mixed delta types that don't + // fold into a single snapshot could exceed the old cap (50) and deadlock + // on the SerialBatchEventUploader backpressure check. Match + // HybridTransport's bound — high enough to be memory-only. + maxQueueSize: 100_000, + send: async batch => { + const result = await this.request( + 'post', + '/worker/events', + { worker_epoch: this.workerEpoch, events: batch }, + 'client events', + ) + if (!result.ok) { + throw new RetryableError( + 'client event POST failed', + result.retryAfterMs, + ) + } + }, + baseDelayMs: 500, + maxDelayMs: 30_000, + jitterMs: 500, + }) + + this.internalEventUploader = new SerialBatchEventUploader({ + maxBatchSize: 100, + maxBatchBytes: 10 * 1024 * 1024, + maxQueueSize: 200, + send: async batch => { + const result = await this.request( + 'post', + '/worker/internal-events', + { worker_epoch: this.workerEpoch, events: batch }, + 'internal events', + ) + if (!result.ok) { + throw new RetryableError( + 'internal event POST failed', + result.retryAfterMs, + ) + } + }, + baseDelayMs: 500, + maxDelayMs: 30_000, + jitterMs: 500, + }) + + this.deliveryUploader = new SerialBatchEventUploader<{ + eventId: string + status: 'received' | 'processing' | 'processed' + }>({ + maxBatchSize: 64, + maxQueueSize: 64, + send: async batch => { + const result = await this.request( + 'post', + '/worker/events/delivery', + { + worker_epoch: this.workerEpoch, + updates: batch.map(d => ({ + event_id: d.eventId, + status: d.status, + })), + }, + 'delivery batch', + ) + if (!result.ok) { + throw new RetryableError('delivery POST failed', result.retryAfterMs) + } + }, + baseDelayMs: 500, + maxDelayMs: 30_000, + jitterMs: 500, + }) + + // Ack each received client_event so CCR can track delivery status. + // Wired here (not in initialize()) so the callback is registered the + // moment new CCRClient() returns — remoteIO must be free to call + // transport.connect() immediately after without racing the first + // SSE catch-up frame against an unwired onEventCallback. + transport.setOnEvent((event: StreamClientEvent) => { + this.reportDelivery(event.event_id, 'received') + }) + } + + /** + * Initialize the session worker: + * 1. Take worker_epoch from the argument, or fall back to + * CLAUDE_CODE_WORKER_EPOCH (set by env-manager / bridge spawner) + * 2. Report state as 'idle' + * 3. Start heartbeat timer + * + * In-process callers (replBridge) pass the epoch directly — they + * registered the worker themselves and there is no parent process + * setting env vars. + */ + async initialize(epoch?: number): Promise | null> { + const startMs = Date.now() + if (Object.keys(this.getAuthHeaders()).length === 0) { + throw new CCRInitError('no_auth_headers') + } + if (epoch === undefined) { + const rawEpoch = process.env.CLAUDE_CODE_WORKER_EPOCH + epoch = rawEpoch ? parseInt(rawEpoch, 10) : NaN + } + if (isNaN(epoch)) { + throw new CCRInitError('missing_epoch') + } + this.workerEpoch = epoch + + // Concurrent with the init PUT — neither depends on the other. + const restoredPromise = this.getWorkerState() + + const result = await this.request( + 'put', + '/worker', + { + worker_status: 'idle', + worker_epoch: this.workerEpoch, + // Clear stale pending_action/task_summary left by a prior + // worker crash — the in-session clears don't survive process restart. + external_metadata: { + pending_action: null, + task_summary: null, + }, + }, + 'PUT worker (init)', + ) + if (!result.ok) { + // 409 → onEpochMismatch may throw, but request() catches it and returns + // false. Without this check we'd continue to startHeartbeat(), leaking a + // 20s timer against a dead epoch. Throw so connect()'s rejection handler + // fires instead of the success path. + throw new CCRInitError('worker_register_failed') + } + this.currentState = 'idle' + this.startHeartbeat() + + // sessionActivity's refcount-gated timer fires while an API call or tool + // is in-flight; without a write the container lease can expire mid-wait. + // v1 wires this in WebSocketTransport per-connection. + registerSessionActivityCallback(() => { + void this.writeEvent({ type: 'keep_alive' }) + }) + + logForDebugging(`CCRClient: initialized, epoch=${this.workerEpoch}`) + logForDiagnosticsNoPII('info', 'cli_worker_lifecycle_initialized', { + epoch: this.workerEpoch, + duration_ms: Date.now() - startMs, + }) + + // Await the concurrent GET and log state_restored here, after the PUT + // has succeeded — logging inside getWorkerState() raced: if the GET + // resolved before the PUT failed, diagnostics showed both init_failed + // and state_restored for the same session. + const { metadata, durationMs } = await restoredPromise + if (!this.closed) { + logForDiagnosticsNoPII('info', 'cli_worker_state_restored', { + duration_ms: durationMs, + had_state: metadata !== null, + }) + } + return metadata + } + + // Control_requests are marked processed and not re-delivered on + // restart, so read back what the prior worker wrote. + private async getWorkerState(): Promise<{ + metadata: Record | null + durationMs: number + }> { + const startMs = Date.now() + const authHeaders = this.getAuthHeaders() + if (Object.keys(authHeaders).length === 0) { + return { metadata: null, durationMs: 0 } + } + const data = await this.getWithRetry( + `${this.sessionBaseUrl}/worker`, + authHeaders, + 'worker_state', + ) + return { + metadata: data?.worker?.external_metadata ?? null, + durationMs: Date.now() - startMs, + } + } + + /** + * Send an authenticated HTTP request to CCR. Handles auth headers, + * 409 epoch mismatch, and error logging. Returns { ok: true } on 2xx. + * On 429, reads Retry-After (integer seconds) so the uploader can honor + * the server's backoff hint instead of blindly exponentiating. + */ + private async request( + method: 'post' | 'put', + path: string, + body: unknown, + label: string, + { timeout = 10_000 }: { timeout?: number } = {}, + ): Promise { + const authHeaders = this.getAuthHeaders() + if (Object.keys(authHeaders).length === 0) return { ok: false } + + try { + const response = await this.http[method]( + `${this.sessionBaseUrl}${path}`, + body, + { + headers: { + ...authHeaders, + 'Content-Type': 'application/json', + 'anthropic-version': '2023-06-01', + 'User-Agent': getClaudeCodeUserAgent(), + }, + validateStatus: alwaysValidStatus, + timeout, + }, + ) + + if (response.status >= 200 && response.status < 300) { + this.consecutiveAuthFailures = 0 + return { ok: true } + } + if (response.status === 409) { + this.handleEpochMismatch() + } + if (response.status === 401 || response.status === 403) { + // A 401 with an expired JWT is deterministic — no retry will + // ever succeed. Check the token's own exp before burning + // wall-clock on the threshold loop. + const tok = getSessionIngressAuthToken() + const exp = tok ? decodeJwtExpiry(tok) : null + if (exp !== null && exp * 1000 < Date.now()) { + logForDebugging( + `CCRClient: session_token expired (exp=${new Date(exp * 1000).toISOString()}) — no refresh was delivered, exiting`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_worker_token_expired_no_refresh') + this.onEpochMismatch() + } + // Token looks valid but server says 401 — possible server-side + // blip (userauth down, KMS hiccup). Count toward threshold. + this.consecutiveAuthFailures++ + if (this.consecutiveAuthFailures >= MAX_CONSECUTIVE_AUTH_FAILURES) { + logForDebugging( + `CCRClient: ${this.consecutiveAuthFailures} consecutive auth failures with a valid-looking token — server-side auth unrecoverable, exiting`, + { level: 'error' }, + ) + logForDiagnosticsNoPII('error', 'cli_worker_auth_failures_exhausted') + this.onEpochMismatch() + } + } + logForDebugging(`CCRClient: ${label} returned ${response.status}`, { + level: 'warn', + }) + logForDiagnosticsNoPII('warn', 'cli_worker_request_failed', { + method, + path, + status: response.status, + }) + if (response.status === 429) { + const raw = response.headers?.['retry-after'] + const seconds = typeof raw === 'string' ? parseInt(raw, 10) : NaN + if (!isNaN(seconds) && seconds >= 0) { + return { ok: false, retryAfterMs: seconds * 1000 } + } + } + return { ok: false } + } catch (error) { + logForDebugging(`CCRClient: ${label} failed: ${errorMessage(error)}`, { + level: 'warn', + }) + logForDiagnosticsNoPII('warn', 'cli_worker_request_error', { + method, + path, + error_code: getErrnoCode(error), + }) + return { ok: false } + } + } + + /** Report worker state to CCR via PUT /sessions/{id}/worker. */ + reportState(state: SessionState, details?: RequiresActionDetails): void { + if (state === this.currentState && !details) return + this.currentState = state + this.workerState.enqueue({ + worker_status: state, + requires_action_details: details + ? { + tool_name: details.tool_name, + action_description: details.action_description, + request_id: details.request_id, + } + : null, + }) + } + + /** Report external metadata to CCR via PUT /worker. */ + reportMetadata(metadata: Record): void { + this.workerState.enqueue({ external_metadata: metadata }) + } + + /** + * Handle epoch mismatch (409 Conflict). A newer CC instance has replaced + * this one — exit immediately. + */ + private handleEpochMismatch(): never { + logForDebugging('CCRClient: Epoch mismatch (409), shutting down', { + level: 'error', + }) + logForDiagnosticsNoPII('error', 'cli_worker_epoch_mismatch') + this.onEpochMismatch() + } + + /** Start periodic heartbeat. */ + private startHeartbeat(): void { + this.stopHeartbeat() + const schedule = (): void => { + const jitter = + this.heartbeatIntervalMs * + this.heartbeatJitterFraction * + (2 * Math.random() - 1) + this.heartbeatTimer = setTimeout(tick, this.heartbeatIntervalMs + jitter) + } + const tick = (): void => { + void this.sendHeartbeat() + // stopHeartbeat nulls the timer; check after the fire-and-forget send + // but before rescheduling so close() during sendHeartbeat is honored. + if (this.heartbeatTimer === null) return + schedule() + } + schedule() + } + + /** Stop heartbeat timer. */ + private stopHeartbeat(): void { + if (this.heartbeatTimer) { + clearTimeout(this.heartbeatTimer) + this.heartbeatTimer = null + } + } + + /** Send a heartbeat via POST /sessions/{id}/worker/heartbeat. */ + private async sendHeartbeat(): Promise { + if (this.heartbeatInFlight) return + this.heartbeatInFlight = true + try { + const result = await this.request( + 'post', + '/worker/heartbeat', + { session_id: this.sessionId, worker_epoch: this.workerEpoch }, + 'Heartbeat', + { timeout: 5_000 }, + ) + if (result.ok) { + logForDebugging('CCRClient: Heartbeat sent') + } + } finally { + this.heartbeatInFlight = false + } + } + + /** + * Write a StdoutMessage as a client event via POST /sessions/{id}/worker/events. + * These events are visible to frontend clients via the SSE stream. + * Injects a UUID if missing to ensure server-side idempotency on retry. + * + * stream_event messages are held in a 100ms delay buffer and accumulated + * (text_deltas for the same content block emit a full-so-far snapshot per + * flush). A non-stream_event write flushes the buffer first so downstream + * ordering is preserved. + */ + async writeEvent(message: StdoutMessage): Promise { + if (message.type === 'stream_event') { + this.streamEventBuffer.push(message) + if (!this.streamEventTimer) { + this.streamEventTimer = setTimeout( + () => void this.flushStreamEventBuffer(), + STREAM_EVENT_FLUSH_INTERVAL_MS, + ) + } + return + } + await this.flushStreamEventBuffer() + if (message.type === 'assistant') { + clearStreamAccumulatorForMessage(this.streamTextAccumulator, message) + } + await this.eventUploader.enqueue(this.toClientEvent(message)) + } + + /** Wrap a StdoutMessage as a ClientEvent, injecting a UUID if missing. */ + private toClientEvent(message: StdoutMessage): ClientEvent { + const msg = message as unknown as Record + return { + payload: { + ...msg, + uuid: typeof msg.uuid === 'string' ? msg.uuid : randomUUID(), + } as EventPayload, + } + } + + /** + * Drain the stream_event delay buffer: accumulate text_deltas into + * full-so-far snapshots, clear the timer, enqueue the resulting events. + * Called from the timer, from writeEvent on a non-stream message, and from + * flush(). close() drops the buffer — call flush() first if you need + * delivery. + */ + private async flushStreamEventBuffer(): Promise { + if (this.streamEventTimer) { + clearTimeout(this.streamEventTimer) + this.streamEventTimer = null + } + if (this.streamEventBuffer.length === 0) return + const buffered = this.streamEventBuffer + this.streamEventBuffer = [] + const payloads = accumulateStreamEvents( + buffered, + this.streamTextAccumulator, + ) + await this.eventUploader.enqueue( + payloads.map(payload => ({ payload, ephemeral: true })), + ) + } + + /** + * Write an internal worker event via POST /sessions/{id}/worker/internal-events. + * These events are NOT visible to frontend clients — they store worker-internal + * state (transcript messages, compaction markers) needed for session resume. + */ + async writeInternalEvent( + eventType: string, + payload: Record, + { + isCompaction = false, + agentId, + }: { + isCompaction?: boolean + agentId?: string + } = {}, + ): Promise { + const event: WorkerEvent = { + payload: { + type: eventType, + ...payload, + uuid: typeof payload.uuid === 'string' ? payload.uuid : randomUUID(), + } as EventPayload, + ...(isCompaction && { is_compaction: true }), + ...(agentId && { agent_id: agentId }), + } + await this.internalEventUploader.enqueue(event) + } + + /** + * Flush pending internal events. Call between turns and on shutdown + * to ensure transcript entries are persisted. + */ + flushInternalEvents(): Promise { + return this.internalEventUploader.flush() + } + + /** + * Flush pending client events (writeEvent queue). Call before close() + * when the caller needs delivery confirmation — close() abandons the + * queue. Resolves once the uploader drains or rejects; returns + * regardless of whether individual POSTs succeeded (check server state + * separately if that matters). + */ + async flush(): Promise { + await this.flushStreamEventBuffer() + return this.eventUploader.flush() + } + + /** + * Read foreground agent internal events from + * GET /sessions/{id}/worker/internal-events. + * Returns transcript entries from the last compaction boundary, or null on failure. + * Used for session resume. + */ + async readInternalEvents(): Promise { + return this.paginatedGet('/worker/internal-events', {}, 'internal_events') + } + + /** + * Read all subagent internal events from + * GET /sessions/{id}/worker/internal-events?subagents=true. + * Returns a merged stream across all non-foreground agents, each from its + * compaction point. Used for session resume. + */ + async readSubagentInternalEvents(): Promise { + return this.paginatedGet( + '/worker/internal-events', + { subagents: 'true' }, + 'subagent_events', + ) + } + + /** + * Paginated GET with retry. Fetches all pages from a list endpoint, + * retrying each page on failure with exponential backoff + jitter. + */ + private async paginatedGet( + path: string, + params: Record, + context: string, + ): Promise { + const authHeaders = this.getAuthHeaders() + if (Object.keys(authHeaders).length === 0) return null + + const allEvents: InternalEvent[] = [] + let cursor: string | undefined + + do { + const url = new URL(`${this.sessionBaseUrl}${path}`) + for (const [k, v] of Object.entries(params)) { + url.searchParams.set(k, v) + } + if (cursor) { + url.searchParams.set('cursor', cursor) + } + + const page = await this.getWithRetry( + url.toString(), + authHeaders, + context, + ) + if (!page) return null + + allEvents.push(...(page.data ?? [])) + cursor = page.next_cursor + } while (cursor) + + logForDebugging( + `CCRClient: Read ${allEvents.length} internal events from ${path}${params.subagents ? ' (subagents)' : ''}`, + ) + return allEvents + } + + /** + * Single GET request with retry. Returns the parsed response body + * on success, null if all retries are exhausted. + */ + private async getWithRetry( + url: string, + authHeaders: Record, + context: string, + ): Promise { + for (let attempt = 1; attempt <= 10; attempt++) { + let response + try { + response = await this.http.get(url, { + headers: { + ...authHeaders, + 'anthropic-version': '2023-06-01', + 'User-Agent': getClaudeCodeUserAgent(), + }, + validateStatus: alwaysValidStatus, + timeout: 30_000, + }) + } catch (error) { + logForDebugging( + `CCRClient: GET ${url} failed (attempt ${attempt}/10): ${errorMessage(error)}`, + { level: 'warn' }, + ) + if (attempt < 10) { + const delay = + Math.min(500 * 2 ** (attempt - 1), 30_000) + Math.random() * 500 + await sleep(delay) + } + continue + } + + if (response.status >= 200 && response.status < 300) { + return response.data + } + if (response.status === 409) { + this.handleEpochMismatch() + } + logForDebugging( + `CCRClient: GET ${url} returned ${response.status} (attempt ${attempt}/10)`, + { level: 'warn' }, + ) + + if (attempt < 10) { + const delay = + Math.min(500 * 2 ** (attempt - 1), 30_000) + Math.random() * 500 + await sleep(delay) + } + } + + logForDebugging('CCRClient: GET retries exhausted', { level: 'error' }) + logForDiagnosticsNoPII('error', 'cli_worker_get_retries_exhausted', { + context, + }) + return null + } + + /** + * Report delivery status for a client-to-worker event. + * POST /v1/code/sessions/{id}/worker/events/delivery (batch endpoint) + */ + reportDelivery( + eventId: string, + status: 'received' | 'processing' | 'processed', + ): void { + void this.deliveryUploader.enqueue({ eventId, status }) + } + + /** Get the current epoch (for external use). */ + getWorkerEpoch(): number { + return this.workerEpoch + } + + /** Internal-event queue depth — shutdown-snapshot backpressure signal. */ + get internalEventsPending(): number { + return this.internalEventUploader.pendingCount + } + + /** Clean up uploaders and timers. */ + close(): void { + this.closed = true + this.stopHeartbeat() + unregisterSessionActivityCallback() + if (this.streamEventTimer) { + clearTimeout(this.streamEventTimer) + this.streamEventTimer = null + } + this.streamEventBuffer = [] + this.streamTextAccumulator.byMessage.clear() + this.streamTextAccumulator.scopeToMessage.clear() + this.workerState.close() + this.eventUploader.close() + this.internalEventUploader.close() + this.deliveryUploader.close() + } +} diff --git a/cli/transports/transportUtils.ts b/cli/transports/transportUtils.ts new file mode 100644 index 0000000..9252473 --- /dev/null +++ b/cli/transports/transportUtils.ts @@ -0,0 +1,45 @@ +import { URL } from 'url' +import { isEnvTruthy } from '../../utils/envUtils.js' +import { HybridTransport } from './HybridTransport.js' +import { SSETransport } from './SSETransport.js' +import type { Transport } from './Transport.js' +import { WebSocketTransport } from './WebSocketTransport.js' + +/** + * Helper function to get the appropriate transport for a URL. + * + * Transport selection priority: + * 1. SSETransport (SSE reads + POST writes) when CLAUDE_CODE_USE_CCR_V2 is set + * 2. HybridTransport (WS reads + POST writes) when CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2 is set + * 3. WebSocketTransport (WS reads + WS writes) — default + */ +export function getTransportForUrl( + url: URL, + headers: Record = {}, + sessionId?: string, + refreshHeaders?: () => Record, +): Transport { + if (isEnvTruthy(process.env.CLAUDE_CODE_USE_CCR_V2)) { + // v2: SSE for reads, HTTP POST for writes + // --sdk-url is the session URL (.../sessions/{id}); + // derive the SSE stream URL by appending /worker/events/stream + const sseUrl = new URL(url.href) + if (sseUrl.protocol === 'wss:') { + sseUrl.protocol = 'https:' + } else if (sseUrl.protocol === 'ws:') { + sseUrl.protocol = 'http:' + } + sseUrl.pathname = + sseUrl.pathname.replace(/\/$/, '') + '/worker/events/stream' + return new SSETransport(sseUrl, headers, sessionId, refreshHeaders) + } + + if (url.protocol === 'ws:' || url.protocol === 'wss:') { + if (isEnvTruthy(process.env.CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2)) { + return new HybridTransport(url, headers, sessionId, refreshHeaders) + } + return new WebSocketTransport(url, headers, sessionId, refreshHeaders) + } else { + throw new Error(`Unsupported protocol: ${url.protocol}`) + } +} diff --git a/cli/update.ts b/cli/update.ts new file mode 100644 index 0000000..a0cd35f --- /dev/null +++ b/cli/update.ts @@ -0,0 +1,422 @@ +import chalk from 'chalk' +import { logEvent } from 'src/services/analytics/index.js' +import { + getLatestVersion, + type InstallStatus, + installGlobalPackage, +} from 'src/utils/autoUpdater.js' +import { regenerateCompletionCache } from 'src/utils/completionCache.js' +import { + getGlobalConfig, + type InstallMethod, + saveGlobalConfig, +} from 'src/utils/config.js' +import { logForDebugging } from 'src/utils/debug.js' +import { getDoctorDiagnostic } from 'src/utils/doctorDiagnostic.js' +import { gracefulShutdown } from 'src/utils/gracefulShutdown.js' +import { + installOrUpdateClaudePackage, + localInstallationExists, +} from 'src/utils/localInstaller.js' +import { + installLatest as installLatestNative, + removeInstalledSymlink, +} from 'src/utils/nativeInstaller/index.js' +import { getPackageManager } from 'src/utils/nativeInstaller/packageManagers.js' +import { writeToStdout } from 'src/utils/process.js' +import { gte } from 'src/utils/semver.js' +import { getInitialSettings } from 'src/utils/settings/settings.js' + +export async function update() { + logEvent('tengu_update_check', {}) + writeToStdout(`Current version: ${MACRO.VERSION}\n`) + + const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest' + writeToStdout(`Checking for updates to ${channel} version...\n`) + + logForDebugging('update: Starting update check') + + // Run diagnostic to detect potential issues + logForDebugging('update: Running diagnostic') + const diagnostic = await getDoctorDiagnostic() + logForDebugging(`update: Installation type: ${diagnostic.installationType}`) + logForDebugging( + `update: Config install method: ${diagnostic.configInstallMethod}`, + ) + + // Check for multiple installations + if (diagnostic.multipleInstallations.length > 1) { + writeToStdout('\n') + writeToStdout(chalk.yellow('Warning: Multiple installations found') + '\n') + for (const install of diagnostic.multipleInstallations) { + const current = + diagnostic.installationType === install.type + ? ' (currently running)' + : '' + writeToStdout(`- ${install.type} at ${install.path}${current}\n`) + } + } + + // Display warnings if any exist + if (diagnostic.warnings.length > 0) { + writeToStdout('\n') + for (const warning of diagnostic.warnings) { + logForDebugging(`update: Warning detected: ${warning.issue}`) + + // Don't skip PATH warnings - they're always relevant + // The user needs to know that 'which claude' points elsewhere + logForDebugging(`update: Showing warning: ${warning.issue}`) + + writeToStdout(chalk.yellow(`Warning: ${warning.issue}\n`)) + + writeToStdout(chalk.bold(`Fix: ${warning.fix}\n`)) + } + } + + // Update config if installMethod is not set (but skip for package managers) + const config = getGlobalConfig() + if ( + !config.installMethod && + diagnostic.installationType !== 'package-manager' + ) { + writeToStdout('\n') + writeToStdout('Updating configuration to track installation method...\n') + let detectedMethod: 'local' | 'native' | 'global' | 'unknown' = 'unknown' + + // Map diagnostic installation type to config install method + switch (diagnostic.installationType) { + case 'npm-local': + detectedMethod = 'local' + break + case 'native': + detectedMethod = 'native' + break + case 'npm-global': + detectedMethod = 'global' + break + default: + detectedMethod = 'unknown' + } + + saveGlobalConfig(current => ({ + ...current, + installMethod: detectedMethod, + })) + writeToStdout(`Installation method set to: ${detectedMethod}\n`) + } + + // Check if running from development build + if (diagnostic.installationType === 'development') { + writeToStdout('\n') + writeToStdout( + chalk.yellow('Warning: Cannot update development build') + '\n', + ) + await gracefulShutdown(1) + } + + // Check if running from a package manager + if (diagnostic.installationType === 'package-manager') { + const packageManager = await getPackageManager() + writeToStdout('\n') + + if (packageManager === 'homebrew') { + writeToStdout('Claude is managed by Homebrew.\n') + const latest = await getLatestVersion(channel) + if (latest && !gte(MACRO.VERSION, latest)) { + writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`) + writeToStdout('\n') + writeToStdout('To update, run:\n') + writeToStdout(chalk.bold(' brew upgrade claude-code') + '\n') + } else { + writeToStdout('Claude is up to date!\n') + } + } else if (packageManager === 'winget') { + writeToStdout('Claude is managed by winget.\n') + const latest = await getLatestVersion(channel) + if (latest && !gte(MACRO.VERSION, latest)) { + writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`) + writeToStdout('\n') + writeToStdout('To update, run:\n') + writeToStdout( + chalk.bold(' winget upgrade Anthropic.ClaudeCode') + '\n', + ) + } else { + writeToStdout('Claude is up to date!\n') + } + } else if (packageManager === 'apk') { + writeToStdout('Claude is managed by apk.\n') + const latest = await getLatestVersion(channel) + if (latest && !gte(MACRO.VERSION, latest)) { + writeToStdout(`Update available: ${MACRO.VERSION} → ${latest}\n`) + writeToStdout('\n') + writeToStdout('To update, run:\n') + writeToStdout(chalk.bold(' apk upgrade claude-code') + '\n') + } else { + writeToStdout('Claude is up to date!\n') + } + } else { + // pacman, deb, and rpm don't get specific commands because they each have + // multiple frontends (pacman: yay/paru/makepkg, deb: apt/apt-get/aptitude/nala, + // rpm: dnf/yum/zypper) + writeToStdout('Claude is managed by a package manager.\n') + writeToStdout('Please use your package manager to update.\n') + } + + await gracefulShutdown(0) + } + + // Check for config/reality mismatch (skip for package-manager installs) + if ( + config.installMethod && + diagnostic.configInstallMethod !== 'not set' && + diagnostic.installationType !== 'package-manager' + ) { + const runningType = diagnostic.installationType + const configExpects = diagnostic.configInstallMethod + + // Map installation types for comparison + const typeMapping: Record = { + 'npm-local': 'local', + 'npm-global': 'global', + native: 'native', + development: 'development', + unknown: 'unknown', + } + + const normalizedRunningType = typeMapping[runningType] || runningType + + if ( + normalizedRunningType !== configExpects && + configExpects !== 'unknown' + ) { + writeToStdout('\n') + writeToStdout(chalk.yellow('Warning: Configuration mismatch') + '\n') + writeToStdout(`Config expects: ${configExpects} installation\n`) + writeToStdout(`Currently running: ${runningType}\n`) + writeToStdout( + chalk.yellow( + `Updating the ${runningType} installation you are currently using`, + ) + '\n', + ) + + // Update config to match reality + saveGlobalConfig(current => ({ + ...current, + installMethod: normalizedRunningType as InstallMethod, + })) + writeToStdout( + `Config updated to reflect current installation method: ${normalizedRunningType}\n`, + ) + } + } + + // Handle native installation updates first + if (diagnostic.installationType === 'native') { + logForDebugging( + 'update: Detected native installation, using native updater', + ) + try { + const result = await installLatestNative(channel, true) + + // Handle lock contention gracefully + if (result.lockFailed) { + const pidInfo = result.lockHolderPid + ? ` (PID ${result.lockHolderPid})` + : '' + writeToStdout( + chalk.yellow( + `Another Claude process${pidInfo} is currently running. Please try again in a moment.`, + ) + '\n', + ) + await gracefulShutdown(0) + } + + if (!result.latestVersion) { + process.stderr.write('Failed to check for updates\n') + await gracefulShutdown(1) + } + + if (result.latestVersion === MACRO.VERSION) { + writeToStdout( + chalk.green(`Claude Code is up to date (${MACRO.VERSION})`) + '\n', + ) + } else { + writeToStdout( + chalk.green( + `Successfully updated from ${MACRO.VERSION} to version ${result.latestVersion}`, + ) + '\n', + ) + await regenerateCompletionCache() + } + await gracefulShutdown(0) + } catch (error) { + process.stderr.write('Error: Failed to install native update\n') + process.stderr.write(String(error) + '\n') + process.stderr.write('Try running "claude doctor" for diagnostics\n') + await gracefulShutdown(1) + } + } + + // Fallback to existing JS/npm-based update logic + // Remove native installer symlink since we're not using native installation + // But only if user hasn't migrated to native installation + if (config.installMethod !== 'native') { + await removeInstalledSymlink() + } + + logForDebugging('update: Checking npm registry for latest version') + logForDebugging(`update: Package URL: ${MACRO.PACKAGE_URL}`) + const npmTag = channel === 'stable' ? 'stable' : 'latest' + const npmCommand = `npm view ${MACRO.PACKAGE_URL}@${npmTag} version` + logForDebugging(`update: Running: ${npmCommand}`) + const latestVersion = await getLatestVersion(channel) + logForDebugging( + `update: Latest version from npm: ${latestVersion || 'FAILED'}`, + ) + + if (!latestVersion) { + logForDebugging('update: Failed to get latest version from npm registry') + process.stderr.write(chalk.red('Failed to check for updates') + '\n') + process.stderr.write('Unable to fetch latest version from npm registry\n') + process.stderr.write('\n') + process.stderr.write('Possible causes:\n') + process.stderr.write(' • Network connectivity issues\n') + process.stderr.write(' • npm registry is unreachable\n') + process.stderr.write(' • Corporate proxy/firewall blocking npm\n') + if (MACRO.PACKAGE_URL && !MACRO.PACKAGE_URL.startsWith('@anthropic')) { + process.stderr.write( + ' • Internal/development build not published to npm\n', + ) + } + process.stderr.write('\n') + process.stderr.write('Try:\n') + process.stderr.write(' • Check your internet connection\n') + process.stderr.write(' • Run with --debug flag for more details\n') + const packageName = + MACRO.PACKAGE_URL || + (process.env.USER_TYPE === 'ant' + ? '@anthropic-ai/claude-cli' + : '@anthropic-ai/claude-code') + process.stderr.write( + ` • Manually check: npm view ${packageName} version\n`, + ) + + process.stderr.write(' • Check if you need to login: npm whoami\n') + await gracefulShutdown(1) + } + + // Check if versions match exactly, including any build metadata (like SHA) + if (latestVersion === MACRO.VERSION) { + writeToStdout( + chalk.green(`Claude Code is up to date (${MACRO.VERSION})`) + '\n', + ) + await gracefulShutdown(0) + } + + writeToStdout( + `New version available: ${latestVersion} (current: ${MACRO.VERSION})\n`, + ) + writeToStdout('Installing update...\n') + + // Determine update method based on what's actually running + let useLocalUpdate = false + let updateMethodName = '' + + switch (diagnostic.installationType) { + case 'npm-local': + useLocalUpdate = true + updateMethodName = 'local' + break + case 'npm-global': + useLocalUpdate = false + updateMethodName = 'global' + break + case 'unknown': { + // Fallback to detection if we can't determine installation type + const isLocal = await localInstallationExists() + useLocalUpdate = isLocal + updateMethodName = isLocal ? 'local' : 'global' + writeToStdout( + chalk.yellow('Warning: Could not determine installation type') + '\n', + ) + writeToStdout( + `Attempting ${updateMethodName} update based on file detection...\n`, + ) + break + } + default: + process.stderr.write( + `Error: Cannot update ${diagnostic.installationType} installation\n`, + ) + await gracefulShutdown(1) + } + + writeToStdout(`Using ${updateMethodName} installation update method...\n`) + + logForDebugging(`update: Update method determined: ${updateMethodName}`) + logForDebugging(`update: useLocalUpdate: ${useLocalUpdate}`) + + let status: InstallStatus + + if (useLocalUpdate) { + logForDebugging( + 'update: Calling installOrUpdateClaudePackage() for local update', + ) + status = await installOrUpdateClaudePackage(channel) + } else { + logForDebugging('update: Calling installGlobalPackage() for global update') + status = await installGlobalPackage() + } + + logForDebugging(`update: Installation status: ${status}`) + + switch (status) { + case 'success': + writeToStdout( + chalk.green( + `Successfully updated from ${MACRO.VERSION} to version ${latestVersion}`, + ) + '\n', + ) + await regenerateCompletionCache() + break + case 'no_permissions': + process.stderr.write( + 'Error: Insufficient permissions to install update\n', + ) + if (useLocalUpdate) { + process.stderr.write('Try manually updating with:\n') + process.stderr.write( + ` cd ~/.claude/local && npm update ${MACRO.PACKAGE_URL}\n`, + ) + } else { + process.stderr.write('Try running with sudo or fix npm permissions\n') + process.stderr.write( + 'Or consider using native installation with: claude install\n', + ) + } + await gracefulShutdown(1) + break + case 'install_failed': + process.stderr.write('Error: Failed to install update\n') + if (useLocalUpdate) { + process.stderr.write('Try manually updating with:\n') + process.stderr.write( + ` cd ~/.claude/local && npm update ${MACRO.PACKAGE_URL}\n`, + ) + } else { + process.stderr.write( + 'Or consider using native installation with: claude install\n', + ) + } + await gracefulShutdown(1) + break + case 'in_progress': + process.stderr.write( + 'Error: Another instance is currently performing an update\n', + ) + process.stderr.write('Please wait and try again later\n') + await gracefulShutdown(1) + break + } + await gracefulShutdown(0) +} diff --git a/commands.ts b/commands.ts new file mode 100644 index 0000000..10f03b2 --- /dev/null +++ b/commands.ts @@ -0,0 +1,754 @@ +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +import addDir from './commands/add-dir/index.js' +import autofixPr from './commands/autofix-pr/index.js' +import backfillSessions from './commands/backfill-sessions/index.js' +import btw from './commands/btw/index.js' +import goodClaude from './commands/good-claude/index.js' +import issue from './commands/issue/index.js' +import feedback from './commands/feedback/index.js' +import clear from './commands/clear/index.js' +import color from './commands/color/index.js' +import commit from './commands/commit.js' +import copy from './commands/copy/index.js' +import desktop from './commands/desktop/index.js' +import commitPushPr from './commands/commit-push-pr.js' +import compact from './commands/compact/index.js' +import config from './commands/config/index.js' +import { context, contextNonInteractive } from './commands/context/index.js' +import cost from './commands/cost/index.js' +import diff from './commands/diff/index.js' +import ctx_viz from './commands/ctx_viz/index.js' +import doctor from './commands/doctor/index.js' +import memory from './commands/memory/index.js' +import help from './commands/help/index.js' +import ide from './commands/ide/index.js' +import init from './commands/init.js' +import initVerifiers from './commands/init-verifiers.js' +import keybindings from './commands/keybindings/index.js' +import login from './commands/login/index.js' +import logout from './commands/logout/index.js' +import installGitHubApp from './commands/install-github-app/index.js' +import installSlackApp from './commands/install-slack-app/index.js' +import breakCache from './commands/break-cache/index.js' +import mcp from './commands/mcp/index.js' +import mobile from './commands/mobile/index.js' +import onboarding from './commands/onboarding/index.js' +import pr_comments from './commands/pr_comments/index.js' +import releaseNotes from './commands/release-notes/index.js' +import rename from './commands/rename/index.js' +import resume from './commands/resume/index.js' +import review, { ultrareview } from './commands/review.js' +import session from './commands/session/index.js' +import share from './commands/share/index.js' +import skills from './commands/skills/index.js' +import status from './commands/status/index.js' +import tasks from './commands/tasks/index.js' +import teleport from './commands/teleport/index.js' +/* eslint-disable @typescript-eslint/no-require-imports */ +const agentsPlatform = + process.env.USER_TYPE === 'ant' + ? require('./commands/agents-platform/index.js').default + : null +/* eslint-enable @typescript-eslint/no-require-imports */ +import securityReview from './commands/security-review.js' +import bughunter from './commands/bughunter/index.js' +import terminalSetup from './commands/terminalSetup/index.js' +import usage from './commands/usage/index.js' +import theme from './commands/theme/index.js' +import vim from './commands/vim/index.js' +import { feature } from 'bun:bundle' +// Dead code elimination: conditional imports +/* eslint-disable @typescript-eslint/no-require-imports */ +const proactive = + feature('PROACTIVE') || feature('KAIROS') + ? require('./commands/proactive.js').default + : null +const briefCommand = + feature('KAIROS') || feature('KAIROS_BRIEF') + ? require('./commands/brief.js').default + : null +const assistantCommand = feature('KAIROS') + ? require('./commands/assistant/index.js').default + : null +const bridge = feature('BRIDGE_MODE') + ? require('./commands/bridge/index.js').default + : null +const remoteControlServerCommand = + feature('DAEMON') && feature('BRIDGE_MODE') + ? require('./commands/remoteControlServer/index.js').default + : null +const voiceCommand = feature('VOICE_MODE') + ? require('./commands/voice/index.js').default + : null +const forceSnip = feature('HISTORY_SNIP') + ? require('./commands/force-snip.js').default + : null +const workflowsCmd = feature('WORKFLOW_SCRIPTS') + ? ( + require('./commands/workflows/index.js') as typeof import('./commands/workflows/index.js') + ).default + : null +const webCmd = feature('CCR_REMOTE_SETUP') + ? ( + require('./commands/remote-setup/index.js') as typeof import('./commands/remote-setup/index.js') + ).default + : null +const clearSkillIndexCache = feature('EXPERIMENTAL_SKILL_SEARCH') + ? ( + require('./services/skillSearch/localSearch.js') as typeof import('./services/skillSearch/localSearch.js') + ).clearSkillIndexCache + : null +const subscribePr = feature('KAIROS_GITHUB_WEBHOOKS') + ? require('./commands/subscribe-pr.js').default + : null +const ultraplan = feature('ULTRAPLAN') + ? require('./commands/ultraplan.js').default + : null +const torch = feature('TORCH') ? require('./commands/torch.js').default : null +const peersCmd = feature('UDS_INBOX') + ? ( + require('./commands/peers/index.js') as typeof import('./commands/peers/index.js') + ).default + : null +const forkCmd = feature('FORK_SUBAGENT') + ? ( + require('./commands/fork/index.js') as typeof import('./commands/fork/index.js') + ).default + : null +const buddy = feature('BUDDY') + ? ( + require('./commands/buddy/index.js') as typeof import('./commands/buddy/index.js') + ).default + : null +/* eslint-enable @typescript-eslint/no-require-imports */ +import thinkback from './commands/thinkback/index.js' +import thinkbackPlay from './commands/thinkback-play/index.js' +import permissions from './commands/permissions/index.js' +import plan from './commands/plan/index.js' +import fast from './commands/fast/index.js' +import passes from './commands/passes/index.js' +import privacySettings from './commands/privacy-settings/index.js' +import hooks from './commands/hooks/index.js' +import files from './commands/files/index.js' +import branch from './commands/branch/index.js' +import agents from './commands/agents/index.js' +import plugin from './commands/plugin/index.js' +import reloadPlugins from './commands/reload-plugins/index.js' +import rewind from './commands/rewind/index.js' +import heapDump from './commands/heapdump/index.js' +import mockLimits from './commands/mock-limits/index.js' +import bridgeKick from './commands/bridge-kick.js' +import version from './commands/version.js' +import summary from './commands/summary/index.js' +import { + resetLimits, + resetLimitsNonInteractive, +} from './commands/reset-limits/index.js' +import antTrace from './commands/ant-trace/index.js' +import perfIssue from './commands/perf-issue/index.js' +import sandboxToggle from './commands/sandbox-toggle/index.js' +import chrome from './commands/chrome/index.js' +import stickers from './commands/stickers/index.js' +import advisor from './commands/advisor.js' +import { logError } from './utils/log.js' +import { toError } from './utils/errors.js' +import { logForDebugging } from './utils/debug.js' +import { + getSkillDirCommands, + clearSkillCaches, + getDynamicSkills, +} from './skills/loadSkillsDir.js' +import { getBundledSkills } from './skills/bundledSkills.js' +import { getBuiltinPluginSkillCommands } from './plugins/builtinPlugins.js' +import { + getPluginCommands, + clearPluginCommandCache, + getPluginSkills, + clearPluginSkillsCache, +} from './utils/plugins/loadPluginCommands.js' +import memoize from 'lodash-es/memoize.js' +import { isUsing3PServices, isClaudeAISubscriber } from './utils/auth.js' +import { isFirstPartyAnthropicBaseUrl } from './utils/model/providers.js' +import env from './commands/env/index.js' +import exit from './commands/exit/index.js' +import exportCommand from './commands/export/index.js' +import model from './commands/model/index.js' +import tag from './commands/tag/index.js' +import outputStyle from './commands/output-style/index.js' +import remoteEnv from './commands/remote-env/index.js' +import upgrade from './commands/upgrade/index.js' +import { + extraUsage, + extraUsageNonInteractive, +} from './commands/extra-usage/index.js' +import rateLimitOptions from './commands/rate-limit-options/index.js' +import statusline from './commands/statusline.js' +import effort from './commands/effort/index.js' +import stats from './commands/stats/index.js' +// insights.ts is 113KB (3200 lines, includes diffLines/html rendering). Lazy +// shim defers the heavy module until /insights is actually invoked. +const usageReport: Command = { + type: 'prompt', + name: 'insights', + description: 'Generate a report analyzing your Claude Code sessions', + contentLength: 0, + progressMessage: 'analyzing your sessions', + source: 'builtin', + async getPromptForCommand(args, context) { + const real = (await import('./commands/insights.js')).default + if (real.type !== 'prompt') throw new Error('unreachable') + return real.getPromptForCommand(args, context) + }, +} +import oauthRefresh from './commands/oauth-refresh/index.js' +import debugToolCall from './commands/debug-tool-call/index.js' +import { getSettingSourceName } from './utils/settings/constants.js' +import { + type Command, + getCommandName, + isCommandEnabled, +} from './types/command.js' + +// Re-export types from the centralized location +export type { + Command, + CommandBase, + CommandResultDisplay, + LocalCommandResult, + LocalJSXCommandContext, + PromptCommand, + ResumeEntrypoint, +} from './types/command.js' +export { getCommandName, isCommandEnabled } from './types/command.js' + +// Commands that get eliminated from the external build +export const INTERNAL_ONLY_COMMANDS = [ + backfillSessions, + breakCache, + bughunter, + commit, + commitPushPr, + ctx_viz, + goodClaude, + issue, + initVerifiers, + ...(forceSnip ? [forceSnip] : []), + mockLimits, + bridgeKick, + version, + ...(ultraplan ? [ultraplan] : []), + ...(subscribePr ? [subscribePr] : []), + resetLimits, + resetLimitsNonInteractive, + onboarding, + share, + summary, + teleport, + antTrace, + perfIssue, + env, + oauthRefresh, + debugToolCall, + agentsPlatform, + autofixPr, +].filter(Boolean) + +// Declared as a function so that we don't run this until getCommands is called, +// since underlying functions read from config, which can't be read at module initialization time +const COMMANDS = memoize((): Command[] => [ + addDir, + advisor, + agents, + branch, + btw, + chrome, + clear, + color, + compact, + config, + copy, + desktop, + context, + contextNonInteractive, + cost, + diff, + doctor, + effort, + exit, + fast, + files, + heapDump, + help, + ide, + init, + keybindings, + installGitHubApp, + installSlackApp, + mcp, + memory, + mobile, + model, + outputStyle, + remoteEnv, + plugin, + pr_comments, + releaseNotes, + reloadPlugins, + rename, + resume, + session, + skills, + stats, + status, + statusline, + stickers, + tag, + theme, + feedback, + review, + ultrareview, + rewind, + securityReview, + terminalSetup, + upgrade, + extraUsage, + extraUsageNonInteractive, + rateLimitOptions, + usage, + usageReport, + vim, + ...(webCmd ? [webCmd] : []), + ...(forkCmd ? [forkCmd] : []), + ...(buddy ? [buddy] : []), + ...(proactive ? [proactive] : []), + ...(briefCommand ? [briefCommand] : []), + ...(assistantCommand ? [assistantCommand] : []), + ...(bridge ? [bridge] : []), + ...(remoteControlServerCommand ? [remoteControlServerCommand] : []), + ...(voiceCommand ? [voiceCommand] : []), + thinkback, + thinkbackPlay, + permissions, + plan, + privacySettings, + hooks, + exportCommand, + sandboxToggle, + ...(!isUsing3PServices() ? [logout, login()] : []), + passes, + ...(peersCmd ? [peersCmd] : []), + tasks, + ...(workflowsCmd ? [workflowsCmd] : []), + ...(torch ? [torch] : []), + ...(process.env.USER_TYPE === 'ant' && !process.env.IS_DEMO + ? INTERNAL_ONLY_COMMANDS + : []), +]) + +export const builtInCommandNames = memoize( + (): Set => + new Set(COMMANDS().flatMap(_ => [_.name, ...(_.aliases ?? [])])), +) + +async function getSkills(cwd: string): Promise<{ + skillDirCommands: Command[] + pluginSkills: Command[] + bundledSkills: Command[] + builtinPluginSkills: Command[] +}> { + try { + const [skillDirCommands, pluginSkills] = await Promise.all([ + getSkillDirCommands(cwd).catch(err => { + logError(toError(err)) + logForDebugging( + 'Skill directory commands failed to load, continuing without them', + ) + return [] + }), + getPluginSkills().catch(err => { + logError(toError(err)) + logForDebugging('Plugin skills failed to load, continuing without them') + return [] + }), + ]) + // Bundled skills are registered synchronously at startup + const bundledSkills = getBundledSkills() + // Built-in plugin skills come from enabled built-in plugins + const builtinPluginSkills = getBuiltinPluginSkillCommands() + logForDebugging( + `getSkills returning: ${skillDirCommands.length} skill dir commands, ${pluginSkills.length} plugin skills, ${bundledSkills.length} bundled skills, ${builtinPluginSkills.length} builtin plugin skills`, + ) + return { + skillDirCommands, + pluginSkills, + bundledSkills, + builtinPluginSkills, + } + } catch (err) { + // This should never happen since we catch at the Promise level, but defensive + logError(toError(err)) + logForDebugging('Unexpected error in getSkills, returning empty') + return { + skillDirCommands: [], + pluginSkills: [], + bundledSkills: [], + builtinPluginSkills: [], + } + } +} + +/* eslint-disable @typescript-eslint/no-require-imports */ +const getWorkflowCommands = feature('WORKFLOW_SCRIPTS') + ? ( + require('./tools/WorkflowTool/createWorkflowCommand.js') as typeof import('./tools/WorkflowTool/createWorkflowCommand.js') + ).getWorkflowCommands + : null +/* eslint-enable @typescript-eslint/no-require-imports */ + +/** + * Filters commands by their declared `availability` (auth/provider requirement). + * Commands without `availability` are treated as universal. + * This runs before `isEnabled()` so that provider-gated commands are hidden + * regardless of feature-flag state. + * + * Not memoized — auth state can change mid-session (e.g. after /login), + * so this must be re-evaluated on every getCommands() call. + */ +export function meetsAvailabilityRequirement(cmd: Command): boolean { + if (!cmd.availability) return true + for (const a of cmd.availability) { + switch (a) { + case 'claude-ai': + if (isClaudeAISubscriber()) return true + break + case 'console': + // Console API key user = direct 1P API customer (not 3P, not claude.ai). + // Excludes 3P (Bedrock/Vertex/Foundry) who don't set ANTHROPIC_BASE_URL + // and gateway users who proxy through a custom base URL. + if ( + !isClaudeAISubscriber() && + !isUsing3PServices() && + isFirstPartyAnthropicBaseUrl() + ) + return true + break + default: { + const _exhaustive: never = a + void _exhaustive + break + } + } + } + return false +} + +/** + * Loads all command sources (skills, plugins, workflows). Memoized by cwd + * because loading is expensive (disk I/O, dynamic imports). + */ +const loadAllCommands = memoize(async (cwd: string): Promise => { + const [ + { skillDirCommands, pluginSkills, bundledSkills, builtinPluginSkills }, + pluginCommands, + workflowCommands, + ] = await Promise.all([ + getSkills(cwd), + getPluginCommands(), + getWorkflowCommands ? getWorkflowCommands(cwd) : Promise.resolve([]), + ]) + + return [ + ...bundledSkills, + ...builtinPluginSkills, + ...skillDirCommands, + ...workflowCommands, + ...pluginCommands, + ...pluginSkills, + ...COMMANDS(), + ] +}) + +/** + * Returns commands available to the current user. The expensive loading is + * memoized, but availability and isEnabled checks run fresh every call so + * auth changes (e.g. /login) take effect immediately. + */ +export async function getCommands(cwd: string): Promise { + const allCommands = await loadAllCommands(cwd) + + // Get dynamic skills discovered during file operations + const dynamicSkills = getDynamicSkills() + + // Build base commands without dynamic skills + const baseCommands = allCommands.filter( + _ => meetsAvailabilityRequirement(_) && isCommandEnabled(_), + ) + + if (dynamicSkills.length === 0) { + return baseCommands + } + + // Dedupe dynamic skills - only add if not already present + const baseCommandNames = new Set(baseCommands.map(c => c.name)) + const uniqueDynamicSkills = dynamicSkills.filter( + s => + !baseCommandNames.has(s.name) && + meetsAvailabilityRequirement(s) && + isCommandEnabled(s), + ) + + if (uniqueDynamicSkills.length === 0) { + return baseCommands + } + + // Insert dynamic skills after plugin skills but before built-in commands + const builtInNames = new Set(COMMANDS().map(c => c.name)) + const insertIndex = baseCommands.findIndex(c => builtInNames.has(c.name)) + + if (insertIndex === -1) { + return [...baseCommands, ...uniqueDynamicSkills] + } + + return [ + ...baseCommands.slice(0, insertIndex), + ...uniqueDynamicSkills, + ...baseCommands.slice(insertIndex), + ] +} + +/** + * Clears only the memoization caches for commands, WITHOUT clearing skill caches. + * Use this when dynamic skills are added to invalidate cached command lists. + */ +export function clearCommandMemoizationCaches(): void { + loadAllCommands.cache?.clear?.() + getSkillToolCommands.cache?.clear?.() + getSlashCommandToolSkills.cache?.clear?.() + // getSkillIndex in skillSearch/localSearch.ts is a separate memoization layer + // built ON TOP of getSkillToolCommands/getCommands. Clearing only the inner + // caches is a no-op for the outer — lodash memoize returns the cached result + // without ever reaching the cleared inners. Must clear it explicitly. + clearSkillIndexCache?.() +} + +export function clearCommandsCache(): void { + clearCommandMemoizationCaches() + clearPluginCommandCache() + clearPluginSkillsCache() + clearSkillCaches() +} + +/** + * Filter AppState.mcp.commands to MCP-provided skills (prompt-type, + * model-invocable, loaded from MCP). These live outside getCommands() so + * callers that need MCP skills in their skill index thread them through + * separately. + */ +export function getMcpSkillCommands( + mcpCommands: readonly Command[], +): readonly Command[] { + if (feature('MCP_SKILLS')) { + return mcpCommands.filter( + cmd => + cmd.type === 'prompt' && + cmd.loadedFrom === 'mcp' && + !cmd.disableModelInvocation, + ) + } + return [] +} + +// SkillTool shows ALL prompt-based commands that the model can invoke +// This includes both skills (from /skills/) and commands (from /commands/) +export const getSkillToolCommands = memoize( + async (cwd: string): Promise => { + const allCommands = await getCommands(cwd) + return allCommands.filter( + cmd => + cmd.type === 'prompt' && + !cmd.disableModelInvocation && + cmd.source !== 'builtin' && + // Always include skills from /skills/ dirs, bundled skills, and legacy /commands/ entries + // (they all get an auto-derived description from the first line if frontmatter is missing). + // Plugin/MCP commands still require an explicit description to appear in the listing. + (cmd.loadedFrom === 'bundled' || + cmd.loadedFrom === 'skills' || + cmd.loadedFrom === 'commands_DEPRECATED' || + cmd.hasUserSpecifiedDescription || + cmd.whenToUse), + ) + }, +) + +// Filters commands to include only skills. Skills are commands that provide +// specialized capabilities for the model to use. They are identified by +// loadedFrom being 'skills', 'plugin', or 'bundled', or having disableModelInvocation set. +export const getSlashCommandToolSkills = memoize( + async (cwd: string): Promise => { + try { + const allCommands = await getCommands(cwd) + return allCommands.filter( + cmd => + cmd.type === 'prompt' && + cmd.source !== 'builtin' && + (cmd.hasUserSpecifiedDescription || cmd.whenToUse) && + (cmd.loadedFrom === 'skills' || + cmd.loadedFrom === 'plugin' || + cmd.loadedFrom === 'bundled' || + cmd.disableModelInvocation), + ) + } catch (error) { + logError(toError(error)) + // Return empty array rather than throwing - skills are non-critical + // This prevents skill loading failures from breaking the entire system + logForDebugging('Returning empty skills array due to load failure') + return [] + } + }, +) + +/** + * Commands that are safe to use in remote mode (--remote). + * These only affect local TUI state and don't depend on local filesystem, + * git, shell, IDE, MCP, or other local execution context. + * + * Used in two places: + * 1. Pre-filtering commands in main.tsx before REPL renders (prevents race with CCR init) + * 2. Preserving local-only commands in REPL's handleRemoteInit after CCR filters + */ +export const REMOTE_SAFE_COMMANDS: Set = new Set([ + session, // Shows QR code / URL for remote session + exit, // Exit the TUI + clear, // Clear screen + help, // Show help + theme, // Change terminal theme + color, // Change agent color + vim, // Toggle vim mode + cost, // Show session cost (local cost tracking) + usage, // Show usage info + copy, // Copy last message + btw, // Quick note + feedback, // Send feedback + plan, // Plan mode toggle + keybindings, // Keybinding management + statusline, // Status line toggle + stickers, // Stickers + mobile, // Mobile QR code +]) + +/** + * Builtin commands of type 'local' that ARE safe to execute when received + * over the Remote Control bridge. These produce text output that streams + * back to the mobile/web client and have no terminal-only side effects. + * + * 'local-jsx' commands are blocked by type (they render Ink UI) and + * 'prompt' commands are allowed by type (they expand to text sent to the + * model) — this set only gates 'local' commands. + * + * When adding a new 'local' command that should work from mobile, add it + * here. Default is blocked. + */ +export const BRIDGE_SAFE_COMMANDS: Set = new Set( + [ + compact, // Shrink context — useful mid-session from a phone + clear, // Wipe transcript + cost, // Show session cost + summary, // Summarize conversation + releaseNotes, // Show changelog + files, // List tracked files + ].filter((c): c is Command => c !== null), +) + +/** + * Whether a slash command is safe to execute when its input arrived over the + * Remote Control bridge (mobile/web client). + * + * PR #19134 blanket-blocked all slash commands from bridge inbound because + * `/model` from iOS was popping the local Ink picker. This predicate relaxes + * that with an explicit allowlist: 'prompt' commands (skills) expand to text + * and are safe by construction; 'local' commands need an explicit opt-in via + * BRIDGE_SAFE_COMMANDS; 'local-jsx' commands render Ink UI and stay blocked. + */ +export function isBridgeSafeCommand(cmd: Command): boolean { + if (cmd.type === 'local-jsx') return false + if (cmd.type === 'prompt') return true + return BRIDGE_SAFE_COMMANDS.has(cmd) +} + +/** + * Filter commands to only include those safe for remote mode. + * Used to pre-filter commands when rendering the REPL in --remote mode, + * preventing local-only commands from being briefly available before + * the CCR init message arrives. + */ +export function filterCommandsForRemoteMode(commands: Command[]): Command[] { + return commands.filter(cmd => REMOTE_SAFE_COMMANDS.has(cmd)) +} + +export function findCommand( + commandName: string, + commands: Command[], +): Command | undefined { + return commands.find( + _ => + _.name === commandName || + getCommandName(_) === commandName || + _.aliases?.includes(commandName), + ) +} + +export function hasCommand(commandName: string, commands: Command[]): boolean { + return findCommand(commandName, commands) !== undefined +} + +export function getCommand(commandName: string, commands: Command[]): Command { + const command = findCommand(commandName, commands) + if (!command) { + throw ReferenceError( + `Command ${commandName} not found. Available commands: ${commands + .map(_ => { + const name = getCommandName(_) + return _.aliases ? `${name} (aliases: ${_.aliases.join(', ')})` : name + }) + .sort((a, b) => a.localeCompare(b)) + .join(', ')}`, + ) + } + + return command +} + +/** + * Formats a command's description with its source annotation for user-facing UI. + * Use this in typeahead, help screens, and other places where users need to see + * where a command comes from. + * + * For model-facing prompts (like SkillTool), use cmd.description directly. + */ +export function formatDescriptionWithSource(cmd: Command): string { + if (cmd.type !== 'prompt') { + return cmd.description + } + + if (cmd.kind === 'workflow') { + return `${cmd.description} (workflow)` + } + + if (cmd.source === 'plugin') { + const pluginName = cmd.pluginInfo?.pluginManifest.name + if (pluginName) { + return `(${pluginName}) ${cmd.description}` + } + return `${cmd.description} (plugin)` + } + + if (cmd.source === 'builtin' || cmd.source === 'mcp') { + return cmd.description + } + + if (cmd.source === 'bundled') { + return `${cmd.description} (bundled)` + } + + return `${cmd.description} (${getSettingSourceName(cmd.source)})` +} diff --git a/commands/add-dir/add-dir.tsx b/commands/add-dir/add-dir.tsx new file mode 100644 index 0000000..b1a2b4d --- /dev/null +++ b/commands/add-dir/add-dir.tsx @@ -0,0 +1,126 @@ +import { c as _c } from "react/compiler-runtime"; +import chalk from 'chalk'; +import figures from 'figures'; +import React, { useEffect } from 'react'; +import { getAdditionalDirectoriesForClaudeMd, setAdditionalDirectoriesForClaudeMd } from '../../bootstrap/state.js'; +import type { LocalJSXCommandContext } from '../../commands.js'; +import { MessageResponse } from '../../components/MessageResponse.js'; +import { AddWorkspaceDirectory } from '../../components/permissions/rules/AddWorkspaceDirectory.js'; +import { Box, Text } from '../../ink.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import { applyPermissionUpdate, persistPermissionUpdate } from '../../utils/permissions/PermissionUpdate.js'; +import type { PermissionUpdateDestination } from '../../utils/permissions/PermissionUpdateSchema.js'; +import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js'; +import { addDirHelpMessage, validateDirectoryForWorkspace } from './validation.js'; +function AddDirError(t0) { + const $ = _c(10); + const { + message, + args, + onDone + } = t0; + let t1; + let t2; + if ($[0] !== onDone) { + t1 = () => { + const timer = setTimeout(onDone, 0); + return () => clearTimeout(timer); + }; + t2 = [onDone]; + $[0] = onDone; + $[1] = t1; + $[2] = t2; + } else { + t1 = $[1]; + t2 = $[2]; + } + useEffect(t1, t2); + let t3; + if ($[3] !== args) { + t3 = {figures.pointer} /add-dir {args}; + $[3] = args; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== message) { + t4 = {message}; + $[5] = message; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] !== t3 || $[8] !== t4) { + t5 = {t3}{t4}; + $[7] = t3; + $[8] = t4; + $[9] = t5; + } else { + t5 = $[9]; + } + return t5; +} +export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext, args?: string): Promise { + const directoryPath = (args ?? '').trim(); + const appState = context.getAppState(); + + // Helper to handle adding a directory (shared by both with-path and no-path cases) + const handleAddDirectory = async (path: string, remember = false) => { + const destination: PermissionUpdateDestination = remember ? 'localSettings' : 'session'; + const permissionUpdate = { + type: 'addDirectories' as const, + directories: [path], + destination + }; + + // Apply to session context + const latestAppState = context.getAppState(); + const updatedContext = applyPermissionUpdate(latestAppState.toolPermissionContext, permissionUpdate); + context.setAppState(prev => ({ + ...prev, + toolPermissionContext: updatedContext + })); + + // Update sandbox config so Bash commands can access the new directory. + // Bootstrap state is the source of truth for session-only dirs; persisted + // dirs are picked up via the settings subscription, but we refresh + // eagerly here to avoid a race when the user acts immediately. + const currentDirs = getAdditionalDirectoriesForClaudeMd(); + if (!currentDirs.includes(path)) { + setAdditionalDirectoriesForClaudeMd([...currentDirs, path]); + } + SandboxManager.refreshConfig(); + let message: string; + if (remember) { + try { + persistPermissionUpdate(permissionUpdate); + message = `Added ${chalk.bold(path)} as a working directory and saved to local settings`; + } catch (error) { + message = `Added ${chalk.bold(path)} as a working directory. Failed to save to local settings: ${error instanceof Error ? error.message : 'Unknown error'}`; + } + } else { + message = `Added ${chalk.bold(path)} as a working directory for this session`; + } + const messageWithHint = `${message} ${chalk.dim('· /permissions to manage')}`; + onDone(messageWithHint); + }; + + // When no path is provided, show AddWorkspaceDirectory input form directly + // and return to REPL after confirmation + if (!directoryPath) { + return { + onDone('Did not add a working directory.'); + }} />; + } + const result = await validateDirectoryForWorkspace(directoryPath, appState.toolPermissionContext); + if (result.resultType !== 'success') { + const message = addDirHelpMessage(result); + return onDone(message)} />; + } + return { + onDone(`Did not add ${chalk.bold(result.absolutePath)} as a working directory.`); + }} />; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjaGFsayIsImZpZ3VyZXMiLCJSZWFjdCIsInVzZUVmZmVjdCIsImdldEFkZGl0aW9uYWxEaXJlY3Rvcmllc0ZvckNsYXVkZU1kIiwic2V0QWRkaXRpb25hbERpcmVjdG9yaWVzRm9yQ2xhdWRlTWQiLCJMb2NhbEpTWENvbW1hbmRDb250ZXh0IiwiTWVzc2FnZVJlc3BvbnNlIiwiQWRkV29ya3NwYWNlRGlyZWN0b3J5IiwiQm94IiwiVGV4dCIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImFwcGx5UGVybWlzc2lvblVwZGF0ZSIsInBlcnNpc3RQZXJtaXNzaW9uVXBkYXRlIiwiUGVybWlzc2lvblVwZGF0ZURlc3RpbmF0aW9uIiwiU2FuZGJveE1hbmFnZXIiLCJhZGREaXJIZWxwTWVzc2FnZSIsInZhbGlkYXRlRGlyZWN0b3J5Rm9yV29ya3NwYWNlIiwiQWRkRGlyRXJyb3IiLCJ0MCIsIiQiLCJfYyIsIm1lc3NhZ2UiLCJhcmdzIiwib25Eb25lIiwidDEiLCJ0MiIsInRpbWVyIiwic2V0VGltZW91dCIsImNsZWFyVGltZW91dCIsInQzIiwicG9pbnRlciIsInQ0IiwidDUiLCJjYWxsIiwiY29udGV4dCIsIlByb21pc2UiLCJSZWFjdE5vZGUiLCJkaXJlY3RvcnlQYXRoIiwidHJpbSIsImFwcFN0YXRlIiwiZ2V0QXBwU3RhdGUiLCJoYW5kbGVBZGREaXJlY3RvcnkiLCJwYXRoIiwicmVtZW1iZXIiLCJkZXN0aW5hdGlvbiIsInBlcm1pc3Npb25VcGRhdGUiLCJ0eXBlIiwiY29uc3QiLCJkaXJlY3RvcmllcyIsImxhdGVzdEFwcFN0YXRlIiwidXBkYXRlZENvbnRleHQiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJzZXRBcHBTdGF0ZSIsInByZXYiLCJjdXJyZW50RGlycyIsImluY2x1ZGVzIiwicmVmcmVzaENvbmZpZyIsImJvbGQiLCJlcnJvciIsIkVycm9yIiwibWVzc2FnZVdpdGhIaW50IiwiZGltIiwicmVzdWx0IiwicmVzdWx0VHlwZSIsImFic29sdXRlUGF0aCJdLCJzb3VyY2VzIjpbImFkZC1kaXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjaGFsayBmcm9tICdjaGFsaydcbmltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgUmVhY3QsIHsgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICBnZXRBZGRpdGlvbmFsRGlyZWN0b3JpZXNGb3JDbGF1ZGVNZCxcbiAgc2V0QWRkaXRpb25hbERpcmVjdG9yaWVzRm9yQ2xhdWRlTWQsXG59IGZyb20gJy4uLy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ29udGV4dCB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZVJlc3BvbnNlIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9NZXNzYWdlUmVzcG9uc2UuanMnXG5pbXBvcnQgeyBBZGRXb3Jrc3BhY2VEaXJlY3RvcnkgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL3Blcm1pc3Npb25zL3J1bGVzL0FkZFdvcmtzcGFjZURpcmVjdG9yeS5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kT25Eb25lIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcbmltcG9ydCB7XG4gIGFwcGx5UGVybWlzc2lvblVwZGF0ZSxcbiAgcGVyc2lzdFBlcm1pc3Npb25VcGRhdGUsXG59IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25VcGRhdGUuanMnXG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb25VcGRhdGVEZXN0aW5hdGlvbiB9IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25VcGRhdGVTY2hlbWEuanMnXG5pbXBvcnQgeyBTYW5kYm94TWFuYWdlciB9IGZyb20gJy4uLy4uL3V0aWxzL3NhbmRib3gvc2FuZGJveC1hZGFwdGVyLmpzJ1xuaW1wb3J0IHtcbiAgYWRkRGlySGVscE1lc3NhZ2UsXG4gIHZhbGlkYXRlRGlyZWN0b3J5Rm9yV29ya3NwYWNlLFxufSBmcm9tICcuL3ZhbGlkYXRpb24uanMnXG5cbmZ1bmN0aW9uIEFkZERpckVycm9yKHtcbiAgbWVzc2FnZSxcbiAgYXJncyxcbiAgb25Eb25lLFxufToge1xuICBtZXNzYWdlOiBzdHJpbmdcbiAgYXJnczogc3RyaW5nXG4gIG9uRG9uZTogKCkgPT4gdm9pZFxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgLy8gV2UgbmVlZCB0byBkZWZlciBjYWxsaW5nIG9uRG9uZSB0byBhdm9pZCB0aGUgXCJyZXR1cm4gbnVsbFwiIGJ1ZyB3aGVyZVxuICAgIC8vIHRoZSBjb21wb25lbnQgdW5tb3VudHMgYmVmb3JlIFJlYWN0IGNhbiByZW5kZXIgdGhlIGVycm9yIG1lc3NhZ2UuXG4gICAgLy8gVXNpbmcgc2V0VGltZW91dCBlbnN1cmVzIHRoZSBlcnJvciBkaXNwbGF5cyBiZWZvcmUgdGhlIGNvbW1hbmQgZXhpdHMuXG4gICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KG9uRG9uZSwgMClcbiAgICByZXR1cm4gKCkgPT4gY2xlYXJUaW1lb3V0KHRpbWVyKVxuICB9LCBbb25Eb25lXSlcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgIHtmaWd1cmVzLnBvaW50ZXJ9IC9hZGQtZGlyIHthcmdzfVxuICAgICAgPC9UZXh0PlxuICAgICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgPFRleHQ+e21lc3NhZ2V9PC9UZXh0PlxuICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lLFxuICBjb250ZXh0OiBMb2NhbEpTWENvbW1hbmRDb250ZXh0LFxuICBhcmdzPzogc3RyaW5nLFxuKTogUHJvbWlzZTxSZWFjdC5SZWFjdE5vZGU+IHtcbiAgY29uc3QgZGlyZWN0b3J5UGF0aCA9IChhcmdzID8/ICcnKS50cmltKClcbiAgY29uc3QgYXBwU3RhdGUgPSBjb250ZXh0LmdldEFwcFN0YXRlKClcblxuICAvLyBIZWxwZXIgdG8gaGFuZGxlIGFkZGluZyBhIGRpcmVjdG9yeSAoc2hhcmVkIGJ5IGJvdGggd2l0aC1wYXRoIGFuZCBuby1wYXRoIGNhc2VzKVxuICBjb25zdCBoYW5kbGVBZGREaXJlY3RvcnkgPSBhc3luYyAocGF0aDogc3RyaW5nLCByZW1lbWJlciA9IGZhbHNlKSA9PiB7XG4gICAgY29uc3QgZGVzdGluYXRpb246IFBlcm1pc3Npb25VcGRhdGVEZXN0aW5hdGlvbiA9IHJlbWVtYmVyXG4gICAgICA/ICdsb2NhbFNldHRpbmdzJ1xuICAgICAgOiAnc2Vzc2lvbidcblxuICAgIGNvbnN0IHBlcm1pc3Npb25VcGRhdGUgPSB7XG4gICAgICB0eXBlOiAnYWRkRGlyZWN0b3JpZXMnIGFzIGNvbnN0LFxuICAgICAgZGlyZWN0b3JpZXM6IFtwYXRoXSxcbiAgICAgIGRlc3RpbmF0aW9uLFxuICAgIH1cblxuICAgIC8vIEFwcGx5IHRvIHNlc3Npb24gY29udGV4dFxuICAgIGNvbnN0IGxhdGVzdEFwcFN0YXRlID0gY29udGV4dC5nZXRBcHBTdGF0ZSgpXG4gICAgY29uc3QgdXBkYXRlZENvbnRleHQgPSBhcHBseVBlcm1pc3Npb25VcGRhdGUoXG4gICAgICBsYXRlc3RBcHBTdGF0ZS50b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICBwZXJtaXNzaW9uVXBkYXRlLFxuICAgIClcbiAgICBjb250ZXh0LnNldEFwcFN0YXRlKHByZXYgPT4gKHtcbiAgICAgIC4uLnByZXYsXG4gICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQ6IHVwZGF0ZWRDb250ZXh0LFxuICAgIH0pKVxuXG4gICAgLy8gVXBkYXRlIHNhbmRib3ggY29uZmlnIHNvIEJhc2ggY29tbWFuZHMgY2FuIGFjY2VzcyB0aGUgbmV3IGRpcmVjdG9yeS5cbiAgICAvLyBCb290c3RyYXAgc3RhdGUgaXMgdGhlIHNvdXJjZSBvZiB0cnV0aCBmb3Igc2Vzc2lvbi1vbmx5IGRpcnM7IHBlcnNpc3RlZFxuICAgIC8vIGRpcnMgYXJlIHBpY2tlZCB1cCB2aWEgdGhlIHNldHRpbmdzIHN1YnNjcmlwdGlvbiwgYnV0IHdlIHJlZnJlc2hcbiAgICAvLyBlYWdlcmx5IGhlcmUgdG8gYXZvaWQgYSByYWNlIHdoZW4gdGhlIHVzZXIgYWN0cyBpbW1lZGlhdGVseS5cbiAgICBjb25zdCBjdXJyZW50RGlycyA9IGdldEFkZGl0aW9uYWxEaXJlY3Rvcmllc0ZvckNsYXVkZU1kKClcbiAgICBpZiAoIWN1cnJlbnREaXJzLmluY2x1ZGVzKHBhdGgpKSB7XG4gICAgICBzZXRBZGRpdGlvbmFsRGlyZWN0b3JpZXNGb3JDbGF1ZGVNZChbLi4uY3VycmVudERpcnMsIHBhdGhdKVxuICAgIH1cbiAgICBTYW5kYm94TWFuYWdlci5yZWZyZXNoQ29uZmlnKClcblxuICAgIGxldCBtZXNzYWdlOiBzdHJpbmdcblxuICAgIGlmIChyZW1lbWJlcikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcGVyc2lzdFBlcm1pc3Npb25VcGRhdGUocGVybWlzc2lvblVwZGF0ZSlcbiAgICAgICAgbWVzc2FnZSA9IGBBZGRlZCAke2NoYWxrLmJvbGQocGF0aCl9IGFzIGEgd29ya2luZyBkaXJlY3RvcnkgYW5kIHNhdmVkIHRvIGxvY2FsIHNldHRpbmdzYFxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgbWVzc2FnZSA9IGBBZGRlZCAke2NoYWxrLmJvbGQocGF0aCl9IGFzIGEgd29ya2luZyBkaXJlY3RvcnkuIEZhaWxlZCB0byBzYXZlIHRvIGxvY2FsIHNldHRpbmdzOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogJ1Vua25vd24gZXJyb3InfWBcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgbWVzc2FnZSA9IGBBZGRlZCAke2NoYWxrLmJvbGQocGF0aCl9IGFzIGEgd29ya2luZyBkaXJlY3RvcnkgZm9yIHRoaXMgc2Vzc2lvbmBcbiAgICB9XG5cbiAgICBjb25zdCBtZXNzYWdlV2l0aEhpbnQgPSBgJHttZXNzYWdlfSAke2NoYWxrLmRpbSgnwrcgL3Blcm1pc3Npb25zIHRvIG1hbmFnZScpfWBcbiAgICBvbkRvbmUobWVzc2FnZVdpdGhIaW50KVxuICB9XG5cbiAgLy8gV2hlbiBubyBwYXRoIGlzIHByb3ZpZGVkLCBzaG93IEFkZFdvcmtzcGFjZURpcmVjdG9yeSBpbnB1dCBmb3JtIGRpcmVjdGx5XG4gIC8vIGFuZCByZXR1cm4gdG8gUkVQTCBhZnRlciBjb25maXJtYXRpb25cbiAgaWYgKCFkaXJlY3RvcnlQYXRoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxBZGRXb3Jrc3BhY2VEaXJlY3RvcnlcbiAgICAgICAgcGVybWlzc2lvbkNvbnRleHQ9e2FwcFN0YXRlLnRvb2xQZXJtaXNzaW9uQ29udGV4dH1cbiAgICAgICAgb25BZGREaXJlY3Rvcnk9e2hhbmRsZUFkZERpcmVjdG9yeX1cbiAgICAgICAgb25DYW5jZWw9eygpID0+IHtcbiAgICAgICAgICBvbkRvbmUoJ0RpZCBub3QgYWRkIGEgd29ya2luZyBkaXJlY3RvcnkuJylcbiAgICAgICAgfX1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdmFsaWRhdGVEaXJlY3RvcnlGb3JXb3Jrc3BhY2UoXG4gICAgZGlyZWN0b3J5UGF0aCxcbiAgICBhcHBTdGF0ZS50b29sUGVybWlzc2lvbkNvbnRleHQsXG4gIClcblxuICBpZiAocmVzdWx0LnJlc3VsdFR5cGUgIT09ICdzdWNjZXNzJykge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBhZGREaXJIZWxwTWVzc2FnZShyZXN1bHQpXG5cbiAgICByZXR1cm4gKFxuICAgICAgPEFkZERpckVycm9yXG4gICAgICAgIG1lc3NhZ2U9e21lc3NhZ2V9XG4gICAgICAgIGFyZ3M9e2FyZ3MgPz8gJyd9XG4gICAgICAgIG9uRG9uZT17KCkgPT4gb25Eb25lKG1lc3NhZ2UpfVxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxBZGRXb3Jrc3BhY2VEaXJlY3RvcnlcbiAgICAgIGRpcmVjdG9yeVBhdGg9e3Jlc3VsdC5hYnNvbHV0ZVBhdGh9XG4gICAgICBwZXJtaXNzaW9uQ29udGV4dD17YXBwU3RhdGUudG9vbFBlcm1pc3Npb25Db250ZXh0fVxuICAgICAgb25BZGREaXJlY3Rvcnk9e2hhbmRsZUFkZERpcmVjdG9yeX1cbiAgICAgIG9uQ2FuY2VsPXsoKSA9PiB7XG4gICAgICAgIG9uRG9uZShcbiAgICAgICAgICBgRGlkIG5vdCBhZGQgJHtjaGFsay5ib2xkKHJlc3VsdC5hYnNvbHV0ZVBhdGgpfSBhcyBhIHdvcmtpbmcgZGlyZWN0b3J5LmAsXG4gICAgICAgIClcbiAgICAgIH19XG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsT0FBT0MsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBT0MsS0FBSyxJQUFJQyxTQUFTLFFBQVEsT0FBTztBQUN4QyxTQUNFQyxtQ0FBbUMsRUFDbkNDLG1DQUFtQyxRQUM5QiwwQkFBMEI7QUFDakMsY0FBY0Msc0JBQXNCLFFBQVEsbUJBQW1CO0FBQy9ELFNBQVNDLGVBQWUsUUFBUSxxQ0FBcUM7QUFDckUsU0FBU0MscUJBQXFCLFFBQVEsNkRBQTZEO0FBQ25HLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsY0FBY0MscUJBQXFCLFFBQVEsd0JBQXdCO0FBQ25FLFNBQ0VDLHFCQUFxQixFQUNyQkMsdUJBQXVCLFFBQ2xCLDZDQUE2QztBQUNwRCxjQUFjQywyQkFBMkIsUUFBUSxtREFBbUQ7QUFDcEcsU0FBU0MsY0FBYyxRQUFRLHdDQUF3QztBQUN2RSxTQUNFQyxp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 \ No newline at end of file diff --git a/commands/add-dir/index.ts b/commands/add-dir/index.ts new file mode 100644 index 0000000..e347549 --- /dev/null +++ b/commands/add-dir/index.ts @@ -0,0 +1,11 @@ +import type { Command } from '../../commands.js' + +const addDir = { + type: 'local-jsx', + name: 'add-dir', + description: 'Add a new working directory', + argumentHint: '', + load: () => import('./add-dir.js'), +} satisfies Command + +export default addDir diff --git a/commands/add-dir/validation.ts b/commands/add-dir/validation.ts new file mode 100644 index 0000000..b3627c4 --- /dev/null +++ b/commands/add-dir/validation.ts @@ -0,0 +1,110 @@ +import chalk from 'chalk' +import { stat } from 'fs/promises' +import { dirname, resolve } from 'path' +import type { ToolPermissionContext } from '../../Tool.js' +import { getErrnoCode } from '../../utils/errors.js' +import { expandPath } from '../../utils/path.js' +import { + allWorkingDirectories, + pathInWorkingPath, +} from '../../utils/permissions/filesystem.js' + +export type AddDirectoryResult = + | { + resultType: 'success' + absolutePath: string + } + | { + resultType: 'emptyPath' + } + | { + resultType: 'pathNotFound' | 'notADirectory' + directoryPath: string + absolutePath: string + } + | { + resultType: 'alreadyInWorkingDirectory' + directoryPath: string + workingDir: string + } + +export async function validateDirectoryForWorkspace( + directoryPath: string, + permissionContext: ToolPermissionContext, +): Promise { + if (!directoryPath) { + return { + resultType: 'emptyPath', + } + } + + // resolve() strips the trailing slash expandPath can leave on absolute + // inputs, so /foo and /foo/ map to the same storage key (CC-33). + const absolutePath = resolve(expandPath(directoryPath)) + + // Check if path exists and is a directory (single syscall) + try { + const stats = await stat(absolutePath) + if (!stats.isDirectory()) { + return { + resultType: 'notADirectory', + directoryPath, + absolutePath, + } + } + } catch (e: unknown) { + const code = getErrnoCode(e) + // Match prior existsSync() semantics: treat any of these as "not found" + // rather than re-throwing. EACCES/EPERM in particular must not crash + // startup when a settings-configured additional directory is inaccessible. + if ( + code === 'ENOENT' || + code === 'ENOTDIR' || + code === 'EACCES' || + code === 'EPERM' + ) { + return { + resultType: 'pathNotFound', + directoryPath, + absolutePath, + } + } + throw e + } + + // Get current permission context + const currentWorkingDirs = allWorkingDirectories(permissionContext) + + // Check if already within an existing working directory + for (const workingDir of currentWorkingDirs) { + if (pathInWorkingPath(absolutePath, workingDir)) { + return { + resultType: 'alreadyInWorkingDirectory', + directoryPath, + workingDir, + } + } + } + + return { + resultType: 'success', + absolutePath, + } +} + +export function addDirHelpMessage(result: AddDirectoryResult): string { + switch (result.resultType) { + case 'emptyPath': + return 'Please provide a directory path.' + case 'pathNotFound': + return `Path ${chalk.bold(result.absolutePath)} was not found.` + case 'notADirectory': { + const parentDir = dirname(result.absolutePath) + return `${chalk.bold(result.directoryPath)} is not a directory. Did you mean to add the parent directory ${chalk.bold(parentDir)}?` + } + case 'alreadyInWorkingDirectory': + return `${chalk.bold(result.directoryPath)} is already accessible within the existing working directory ${chalk.bold(result.workingDir)}.` + case 'success': + return `Added ${chalk.bold(result.absolutePath)} as a working directory.` + } +} diff --git a/commands/advisor.ts b/commands/advisor.ts new file mode 100644 index 0000000..cec3feb --- /dev/null +++ b/commands/advisor.ts @@ -0,0 +1,109 @@ +import type { Command } from '../commands.js' +import type { LocalCommandCall } from '../types/command.js' +import { + canUserConfigureAdvisor, + isValidAdvisorModel, + modelSupportsAdvisor, +} from '../utils/advisor.js' +import { + getDefaultMainLoopModelSetting, + normalizeModelStringForAPI, + parseUserSpecifiedModel, +} from '../utils/model/model.js' +import { validateModel } from '../utils/model/validateModel.js' +import { updateSettingsForSource } from '../utils/settings/settings.js' + +const call: LocalCommandCall = async (args, context) => { + const arg = args.trim().toLowerCase() + const baseModel = parseUserSpecifiedModel( + context.getAppState().mainLoopModel ?? getDefaultMainLoopModelSetting(), + ) + + if (!arg) { + const current = context.getAppState().advisorModel + if (!current) { + return { + type: 'text', + value: + 'Advisor: not set\nUse "/advisor " to enable (e.g. "/advisor opus").', + } + } + if (!modelSupportsAdvisor(baseModel)) { + return { + type: 'text', + value: `Advisor: ${current} (inactive)\nThe current model (${baseModel}) does not support advisors.`, + } + } + return { + type: 'text', + value: `Advisor: ${current}\nUse "/advisor unset" to disable or "/advisor " to change.`, + } + } + + if (arg === 'unset' || arg === 'off') { + const prev = context.getAppState().advisorModel + context.setAppState(s => { + if (s.advisorModel === undefined) return s + return { ...s, advisorModel: undefined } + }) + updateSettingsForSource('userSettings', { advisorModel: undefined }) + return { + type: 'text', + value: prev + ? `Advisor disabled (was ${prev}).` + : 'Advisor already unset.', + } + } + + const normalizedModel = normalizeModelStringForAPI(arg) + const resolvedModel = parseUserSpecifiedModel(arg) + const { valid, error } = await validateModel(resolvedModel) + if (!valid) { + return { + type: 'text', + value: error + ? `Invalid advisor model: ${error}` + : `Unknown model: ${arg} (${resolvedModel})`, + } + } + + if (!isValidAdvisorModel(resolvedModel)) { + return { + type: 'text', + value: `The model ${arg} (${resolvedModel}) cannot be used as an advisor`, + } + } + + context.setAppState(s => { + if (s.advisorModel === normalizedModel) return s + return { ...s, advisorModel: normalizedModel } + }) + updateSettingsForSource('userSettings', { advisorModel: normalizedModel }) + + if (!modelSupportsAdvisor(baseModel)) { + return { + type: 'text', + value: `Advisor set to ${normalizedModel}.\nNote: Your current model (${baseModel}) does not support advisors. Switch to a supported model to use the advisor.`, + } + } + + return { + type: 'text', + value: `Advisor set to ${normalizedModel}.`, + } +} + +const advisor = { + type: 'local', + name: 'advisor', + description: 'Configure the advisor model', + argumentHint: '[|off]', + isEnabled: () => canUserConfigureAdvisor(), + get isHidden() { + return !canUserConfigureAdvisor() + }, + supportsNonInteractive: true, + load: () => Promise.resolve({ call }), +} satisfies Command + +export default advisor diff --git a/commands/agents/agents.tsx b/commands/agents/agents.tsx new file mode 100644 index 0000000..3af6273 --- /dev/null +++ b/commands/agents/agents.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; +import { AgentsMenu } from '../../components/agents/AgentsMenu.js'; +import type { ToolUseContext } from '../../Tool.js'; +import { getTools } from '../../tools.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +export async function call(onDone: LocalJSXCommandOnDone, context: ToolUseContext): Promise { + const appState = context.getAppState(); + const permissionContext = appState.toolPermissionContext; + const tools = getTools(permissionContext); + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkFnZW50c01lbnUiLCJUb29sVXNlQ29udGV4dCIsImdldFRvb2xzIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiY2FsbCIsIm9uRG9uZSIsImNvbnRleHQiLCJQcm9taXNlIiwiUmVhY3ROb2RlIiwiYXBwU3RhdGUiLCJnZXRBcHBTdGF0ZSIsInBlcm1pc3Npb25Db250ZXh0IiwidG9vbFBlcm1pc3Npb25Db250ZXh0IiwidG9vbHMiXSwic291cmNlcyI6WyJhZ2VudHMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQWdlbnRzTWVudSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvYWdlbnRzL0FnZW50c01lbnUuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xVc2VDb250ZXh0IH0gZnJvbSAnLi4vLi4vVG9vbC5qcydcbmltcG9ydCB7IGdldFRvb2xzIH0gZnJvbSAnLi4vLi4vdG9vbHMuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogVG9vbFVzZUNvbnRleHQsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICBjb25zdCBhcHBTdGF0ZSA9IGNvbnRleHQuZ2V0QXBwU3RhdGUoKVxuICBjb25zdCBwZXJtaXNzaW9uQ29udGV4dCA9IGFwcFN0YXRlLnRvb2xQZXJtaXNzaW9uQ29udGV4dFxuICBjb25zdCB0b29scyA9IGdldFRvb2xzKHBlcm1pc3Npb25Db250ZXh0KVxuXG4gIHJldHVybiA8QWdlbnRzTWVudSB0b29scz17dG9vbHN9IG9uRXhpdD17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFVBQVUsUUFBUSx1Q0FBdUM7QUFDbEUsY0FBY0MsY0FBYyxRQUFRLGVBQWU7QUFDbkQsU0FBU0MsUUFBUSxRQUFRLGdCQUFnQjtBQUN6QyxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFFbkUsT0FBTyxlQUFlQyxJQUFJQSxDQUN4QkMsTUFBTSxFQUFFRixxQkFBcUIsRUFDN0JHLE9BQU8sRUFBRUwsY0FBYyxDQUN4QixFQUFFTSxPQUFPLENBQUNSLEtBQUssQ0FBQ1MsU0FBUyxDQUFDLENBQUM7RUFDMUIsTUFBTUMsUUFBUSxHQUFHSCxPQUFPLENBQUNJLFdBQVcsQ0FBQyxDQUFDO0VBQ3RDLE1BQU1DLGlCQUFpQixHQUFHRixRQUFRLENBQUNHLHFCQUFxQjtFQUN4RCxNQUFNQyxLQUFLLEdBQUdYLFFBQVEsQ0FBQ1MsaUJBQWlCLENBQUM7RUFFekMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQ0UsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUNSLE1BQU0sQ0FBQyxHQUFHO0FBQ3JEIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/agents/index.ts b/commands/agents/index.ts new file mode 100644 index 0000000..ac43d2e --- /dev/null +++ b/commands/agents/index.ts @@ -0,0 +1,10 @@ +import type { Command } from '../../commands.js' + +const agents = { + type: 'local-jsx', + name: 'agents', + description: 'Manage agent configurations', + load: () => import('./agents.js'), +} satisfies Command + +export default agents diff --git a/commands/ant-trace/index.js b/commands/ant-trace/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/ant-trace/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/autofix-pr/index.js b/commands/autofix-pr/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/autofix-pr/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/backfill-sessions/index.js b/commands/backfill-sessions/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/backfill-sessions/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/branch/branch.ts b/commands/branch/branch.ts new file mode 100644 index 0000000..4a7c277 --- /dev/null +++ b/commands/branch/branch.ts @@ -0,0 +1,296 @@ +import { randomUUID, type UUID } from 'crypto' +import { mkdir, readFile, writeFile } from 'fs/promises' +import { getOriginalCwd, getSessionId } from '../../bootstrap/state.js' +import type { LocalJSXCommandContext } from '../../commands.js' +import { logEvent } from '../../services/analytics/index.js' +import type { LocalJSXCommandOnDone } from '../../types/command.js' +import type { + ContentReplacementEntry, + Entry, + LogOption, + SerializedMessage, + TranscriptMessage, +} from '../../types/logs.js' +import { parseJSONL } from '../../utils/json.js' +import { + getProjectDir, + getTranscriptPath, + getTranscriptPathForSession, + isTranscriptMessage, + saveCustomTitle, + searchSessionsByCustomTitle, +} from '../../utils/sessionStorage.js' +import { jsonStringify } from '../../utils/slowOperations.js' +import { escapeRegExp } from '../../utils/stringUtils.js' + +type TranscriptEntry = TranscriptMessage & { + forkedFrom?: { + sessionId: string + messageUuid: UUID + } +} + +/** + * Derive a single-line title base from the first user message. + * Collapses whitespace — multiline first messages (pasted stacks, code) + * otherwise flow into the saved title and break the resume hint. + */ +export function deriveFirstPrompt( + firstUserMessage: Extract | undefined, +): string { + const content = firstUserMessage?.message?.content + if (!content) return 'Branched conversation' + const raw = + typeof content === 'string' + ? content + : content.find( + (block): block is { type: 'text'; text: string } => + block.type === 'text', + )?.text + if (!raw) return 'Branched conversation' + return ( + raw.replace(/\s+/g, ' ').trim().slice(0, 100) || 'Branched conversation' + ) +} + +/** + * Creates a fork of the current conversation by copying from the transcript file. + * Preserves all original metadata (timestamps, gitBranch, etc.) while updating + * sessionId and adding forkedFrom traceability. + */ +async function createFork(customTitle?: string): Promise<{ + sessionId: UUID + title: string | undefined + forkPath: string + serializedMessages: SerializedMessage[] + contentReplacementRecords: ContentReplacementEntry['replacements'] +}> { + const forkSessionId = randomUUID() as UUID + const originalSessionId = getSessionId() + const projectDir = getProjectDir(getOriginalCwd()) + const forkSessionPath = getTranscriptPathForSession(forkSessionId) + const currentTranscriptPath = getTranscriptPath() + + // Ensure project directory exists + await mkdir(projectDir, { recursive: true, mode: 0o700 }) + + // Read current transcript file + let transcriptContent: Buffer + try { + transcriptContent = await readFile(currentTranscriptPath) + } catch { + throw new Error('No conversation to branch') + } + + if (transcriptContent.length === 0) { + throw new Error('No conversation to branch') + } + + // Parse all transcript entries (messages + metadata entries like content-replacement) + const entries = parseJSONL(transcriptContent) + + // Filter to only main conversation messages (exclude sidechains and non-message entries) + const mainConversationEntries = entries.filter( + (entry): entry is TranscriptMessage => + isTranscriptMessage(entry) && !entry.isSidechain, + ) + + // Content-replacement entries for the original session. These record which + // tool_result blocks were replaced with previews by the per-message budget. + // Without them in the fork JSONL, `claude -r {forkId}` reconstructs state + // with an empty replacements Map → previously-replaced results are classified + // as FROZEN and sent as full content (prompt cache miss + permanent overage). + // sessionId must be rewritten since loadTranscriptFile keys lookup by the + // session's messages' sessionId. + const contentReplacementRecords = entries + .filter( + (entry): entry is ContentReplacementEntry => + entry.type === 'content-replacement' && + entry.sessionId === originalSessionId, + ) + .flatMap(entry => entry.replacements) + + if (mainConversationEntries.length === 0) { + throw new Error('No messages to branch') + } + + // Build forked entries with new sessionId and preserved metadata + let parentUuid: UUID | null = null + const lines: string[] = [] + const serializedMessages: SerializedMessage[] = [] + + for (const entry of mainConversationEntries) { + // Create forked transcript entry preserving all original metadata + const forkedEntry: TranscriptEntry = { + ...entry, + sessionId: forkSessionId, + parentUuid, + isSidechain: false, + forkedFrom: { + sessionId: originalSessionId, + messageUuid: entry.uuid, + }, + } + + // Build serialized message for LogOption + const serialized: SerializedMessage = { + ...entry, + sessionId: forkSessionId, + } + + serializedMessages.push(serialized) + lines.push(jsonStringify(forkedEntry)) + if (entry.type !== 'progress') { + parentUuid = entry.uuid + } + } + + // Append content-replacement entry (if any) with the fork's sessionId. + // Written as a SINGLE entry (same shape as insertContentReplacement) so + // loadTranscriptFile's content-replacement branch picks it up. + if (contentReplacementRecords.length > 0) { + const forkedReplacementEntry: ContentReplacementEntry = { + type: 'content-replacement', + sessionId: forkSessionId, + replacements: contentReplacementRecords, + } + lines.push(jsonStringify(forkedReplacementEntry)) + } + + // Write the fork session file + await writeFile(forkSessionPath, lines.join('\n') + '\n', { + encoding: 'utf8', + mode: 0o600, + }) + + return { + sessionId: forkSessionId, + title: customTitle, + forkPath: forkSessionPath, + serializedMessages, + contentReplacementRecords, + } +} + +/** + * Generates a unique fork name by checking for collisions with existing session names. + * If "baseName (Branch)" already exists, tries "baseName (Branch 2)", "baseName (Branch 3)", etc. + */ +async function getUniqueForkName(baseName: string): Promise { + const candidateName = `${baseName} (Branch)` + + // Check if this exact name already exists + const existingWithExactName = await searchSessionsByCustomTitle( + candidateName, + { exact: true }, + ) + + if (existingWithExactName.length === 0) { + return candidateName + } + + // Name collision - find a unique numbered suffix + // Search for all sessions that start with the base pattern + const existingForks = await searchSessionsByCustomTitle(`${baseName} (Branch`) + + // Extract existing fork numbers to find the next available + const usedNumbers = new Set([1]) // Consider " (Branch)" as number 1 + const forkNumberPattern = new RegExp( + `^${escapeRegExp(baseName)} \\(Branch(?: (\\d+))?\\)$`, + ) + + for (const session of existingForks) { + const match = session.customTitle?.match(forkNumberPattern) + if (match) { + if (match[1]) { + usedNumbers.add(parseInt(match[1], 10)) + } else { + usedNumbers.add(1) // " (Branch)" without number is treated as 1 + } + } + } + + // Find the next available number + let nextNumber = 2 + while (usedNumbers.has(nextNumber)) { + nextNumber++ + } + + return `${baseName} (Branch ${nextNumber})` +} + +export async function call( + onDone: LocalJSXCommandOnDone, + context: LocalJSXCommandContext, + args: string, +): Promise { + const customTitle = args?.trim() || undefined + + const originalSessionId = getSessionId() + + try { + const { + sessionId, + title, + forkPath, + serializedMessages, + contentReplacementRecords, + } = await createFork(customTitle) + + // Build LogOption for resume + const now = new Date() + const firstPrompt = deriveFirstPrompt( + serializedMessages.find(m => m.type === 'user'), + ) + + // Save custom title - use provided title or firstPrompt as default + // This ensures /status and /resume show the same session name + // Always add " (Branch)" suffix to make it clear this is a branched session + // Handle collisions by adding a number suffix (e.g., " (Branch 2)", " (Branch 3)") + const baseName = title ?? firstPrompt + const effectiveTitle = await getUniqueForkName(baseName) + await saveCustomTitle(sessionId, effectiveTitle, forkPath) + + logEvent('tengu_conversation_forked', { + message_count: serializedMessages.length, + has_custom_title: !!title, + }) + + const forkLog: LogOption = { + date: now.toISOString().split('T')[0]!, + messages: serializedMessages, + fullPath: forkPath, + value: now.getTime(), + created: now, + modified: now, + firstPrompt, + messageCount: serializedMessages.length, + isSidechain: false, + sessionId, + customTitle: effectiveTitle, + contentReplacements: contentReplacementRecords, + } + + // Resume into the fork + const titleInfo = title ? ` "${title}"` : '' + const resumeHint = `\nTo resume the original: claude -r ${originalSessionId}` + const successMessage = `Branched conversation${titleInfo}. You are now in the branch.${resumeHint}` + + if (context.resume) { + await context.resume(sessionId, forkLog, 'fork') + onDone(successMessage, { display: 'system' }) + } else { + // Fallback if resume not available + onDone( + `Branched conversation${titleInfo}. Resume with: /resume ${sessionId}`, + ) + } + + return null + } catch (error) { + const message = + error instanceof Error ? error.message : 'Unknown error occurred' + onDone(`Failed to branch conversation: ${message}`) + return null + } +} diff --git a/commands/branch/index.ts b/commands/branch/index.ts new file mode 100644 index 0000000..731ff39 --- /dev/null +++ b/commands/branch/index.ts @@ -0,0 +1,14 @@ +import { feature } from 'bun:bundle' +import type { Command } from '../../commands.js' + +const branch = { + type: 'local-jsx', + name: 'branch', + // 'fork' alias only when /fork doesn't exist as its own command + aliases: feature('FORK_SUBAGENT') ? [] : ['fork'], + description: 'Create a branch of the current conversation at this point', + argumentHint: '[name]', + load: () => import('./branch.js'), +} satisfies Command + +export default branch diff --git a/commands/break-cache/index.js b/commands/break-cache/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/break-cache/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/bridge-kick.ts b/commands/bridge-kick.ts new file mode 100644 index 0000000..f8564c0 --- /dev/null +++ b/commands/bridge-kick.ts @@ -0,0 +1,200 @@ +import { getBridgeDebugHandle } from '../bridge/bridgeDebug.js' +import type { Command } from '../commands.js' +import type { LocalCommandCall } from '../types/command.js' + +/** + * Ant-only: inject bridge failure states to manually test recovery paths. + * + * /bridge-kick close 1002 — fire ws_closed with code 1002 + * /bridge-kick close 1006 — fire ws_closed with code 1006 + * /bridge-kick poll 404 — next poll throws 404/not_found_error + * /bridge-kick poll 404 — next poll throws 404 with error_type + * /bridge-kick poll 401 — next poll throws 401 (auth) + * /bridge-kick poll transient — next poll throws axios-style rejection + * /bridge-kick register fail — next register (inside doReconnect) transient-fails + * /bridge-kick register fail 3 — next 3 registers transient-fail + * /bridge-kick register fatal — next register 403s (terminal) + * /bridge-kick reconnect-session fail — POST /bridge/reconnect fails (→ Strategy 2) + * /bridge-kick heartbeat 401 — next heartbeat 401s (JWT expired) + * /bridge-kick reconnect — call doReconnect directly (= SIGUSR2) + * /bridge-kick status — print current bridge state + * + * Workflow: connect Remote Control, run a subcommand, `tail -f debug.log` + * and watch [bridge:repl] / [bridge:debug] lines for the recovery reaction. + * + * Composite sequences — the failure modes in the BQ data are chains, not + * single events. Queue faults then fire the trigger: + * + * # #22148 residual: ws_closed → register transient-blips → teardown? + * /bridge-kick register fail 2 + * /bridge-kick close 1002 + * → expect: doReconnect tries register, fails, returns false → teardown + * (demonstrates the retry gap that needs fixing) + * + * # Dead gate: poll 404/not_found_error → does onEnvironmentLost fire? + * /bridge-kick poll 404 + * → expect: tengu_bridge_repl_fatal_error (gate is dead — 147K/wk) + * after fix: tengu_bridge_repl_env_lost → doReconnect + */ + +const USAGE = `/bridge-kick + close fire ws_closed with the given code (e.g. 1002) + poll [type] next poll throws BridgeFatalError(status, type) + poll transient next poll throws axios-style rejection (5xx/net) + register fail [N] next N registers transient-fail (default 1) + register fatal next register 403s (terminal) + reconnect-session fail next POST /bridge/reconnect fails + heartbeat next heartbeat throws BridgeFatalError(status) + reconnect call reconnectEnvironmentWithSession directly + status print bridge state` + +const call: LocalCommandCall = async args => { + const h = getBridgeDebugHandle() + if (!h) { + return { + type: 'text', + value: + 'No bridge debug handle registered. Remote Control must be connected (USER_TYPE=ant).', + } + } + + const [sub, a, b] = args.trim().split(/\s+/) + + switch (sub) { + case 'close': { + const code = Number(a) + if (!Number.isFinite(code)) { + return { type: 'text', value: `close: need a numeric code\n${USAGE}` } + } + h.fireClose(code) + return { + type: 'text', + value: `Fired transport close(${code}). Watch debug.log for [bridge:repl] recovery.`, + } + } + + case 'poll': { + if (a === 'transient') { + h.injectFault({ + method: 'pollForWork', + kind: 'transient', + status: 503, + count: 1, + }) + h.wakePollLoop() + return { + type: 'text', + value: + 'Next poll will throw a transient (axios rejection). Poll loop woken.', + } + } + const status = Number(a) + if (!Number.isFinite(status)) { + return { + type: 'text', + value: `poll: need 'transient' or a status code\n${USAGE}`, + } + } + // Default to what the server ACTUALLY sends for 404 (BQ-verified), + // so `/bridge-kick poll 404` reproduces the real 147K/week state. + const errorType = + b ?? (status === 404 ? 'not_found_error' : 'authentication_error') + h.injectFault({ + method: 'pollForWork', + kind: 'fatal', + status, + errorType, + count: 1, + }) + h.wakePollLoop() + return { + type: 'text', + value: `Next poll will throw BridgeFatalError(${status}, ${errorType}). Poll loop woken.`, + } + } + + case 'register': { + if (a === 'fatal') { + h.injectFault({ + method: 'registerBridgeEnvironment', + kind: 'fatal', + status: 403, + errorType: 'permission_error', + count: 1, + }) + return { + type: 'text', + value: + 'Next registerBridgeEnvironment will 403. Trigger with close/reconnect.', + } + } + const n = Number(b) || 1 + h.injectFault({ + method: 'registerBridgeEnvironment', + kind: 'transient', + status: 503, + count: n, + }) + return { + type: 'text', + value: `Next ${n} registerBridgeEnvironment call(s) will transient-fail. Trigger with close/reconnect.`, + } + } + + case 'reconnect-session': { + h.injectFault({ + method: 'reconnectSession', + kind: 'fatal', + status: 404, + errorType: 'not_found_error', + count: 2, + }) + return { + type: 'text', + value: + 'Next 2 POST /bridge/reconnect calls will 404. doReconnect Strategy 1 falls through to Strategy 2.', + } + } + + case 'heartbeat': { + const status = Number(a) || 401 + h.injectFault({ + method: 'heartbeatWork', + kind: 'fatal', + status, + errorType: status === 401 ? 'authentication_error' : 'not_found_error', + count: 1, + }) + return { + type: 'text', + value: `Next heartbeat will ${status}. Watch for onHeartbeatFatal → work-state teardown.`, + } + } + + case 'reconnect': { + h.forceReconnect() + return { + type: 'text', + value: 'Called reconnectEnvironmentWithSession(). Watch debug.log.', + } + } + + case 'status': { + return { type: 'text', value: h.describe() } + } + + default: + return { type: 'text', value: USAGE } + } +} + +const bridgeKick = { + type: 'local', + name: 'bridge-kick', + description: 'Inject bridge failure states for manual recovery testing', + isEnabled: () => process.env.USER_TYPE === 'ant', + supportsNonInteractive: false, + load: () => Promise.resolve({ call }), +} satisfies Command + +export default bridgeKick diff --git a/commands/bridge/bridge.tsx b/commands/bridge/bridge.tsx new file mode 100644 index 0000000..02ca16b --- /dev/null +++ b/commands/bridge/bridge.tsx @@ -0,0 +1,509 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import { toString as qrToString } from 'qrcode'; +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { getBridgeAccessToken } from '../../bridge/bridgeConfig.js'; +import { checkBridgeMinVersion, getBridgeDisabledReason, isEnvLessBridgeEnabled } from '../../bridge/bridgeEnabled.js'; +import { checkEnvLessBridgeMinVersion } from '../../bridge/envLessBridgeConfig.js'; +import { BRIDGE_LOGIN_INSTRUCTION, REMOTE_CONTROL_DISCONNECTED_MSG } from '../../bridge/types.js'; +import { Dialog } from '../../components/design-system/Dialog.js'; +import { ListItem } from '../../components/design-system/ListItem.js'; +import { shouldShowRemoteCallout } from '../../components/RemoteCallout.js'; +import { useRegisterOverlay } from '../../context/overlayContext.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../services/analytics/index.js'; +import { useAppState, useSetAppState } from '../../state/AppState.js'; +import type { ToolUseContext } from '../../Tool.js'; +import type { LocalJSXCommandContext, LocalJSXCommandOnDone } from '../../types/command.js'; +import { logForDebugging } from '../../utils/debug.js'; +type Props = { + onDone: LocalJSXCommandOnDone; + name?: string; +}; + +/** + * /remote-control command — manages the bidirectional bridge connection. + * + * When enabled, sets replBridgeEnabled in AppState, which triggers + * useReplBridge in REPL.tsx to initialize the bridge connection. + * The bridge registers an environment, creates a session with the current + * conversation, polls for work, and connects an ingress WebSocket for + * bidirectional messaging between the CLI and claude.ai. + * + * Running /remote-control when already connected shows a dialog with the session + * URL and options to disconnect or continue. + */ +function BridgeToggle(t0) { + const $ = _c(10); + const { + onDone, + name + } = t0; + const setAppState = useSetAppState(); + const replBridgeConnected = useAppState(_temp); + const replBridgeEnabled = useAppState(_temp2); + const replBridgeOutboundOnly = useAppState(_temp3); + const [showDisconnectDialog, setShowDisconnectDialog] = useState(false); + let t1; + if ($[0] !== name || $[1] !== onDone || $[2] !== replBridgeConnected || $[3] !== replBridgeEnabled || $[4] !== replBridgeOutboundOnly || $[5] !== setAppState) { + t1 = () => { + if ((replBridgeConnected || replBridgeEnabled) && !replBridgeOutboundOnly) { + setShowDisconnectDialog(true); + return; + } + let cancelled = false; + (async () => { + const error = await checkBridgePrerequisites(); + if (cancelled) { + return; + } + if (error) { + logEvent("tengu_bridge_command", { + action: "preflight_failed" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + onDone(error, { + display: "system" + }); + return; + } + if (shouldShowRemoteCallout()) { + setAppState(prev => { + if (prev.showRemoteCallout) { + return prev; + } + return { + ...prev, + showRemoteCallout: true, + replBridgeInitialName: name + }; + }); + onDone("", { + display: "system" + }); + return; + } + logEvent("tengu_bridge_command", { + action: "connect" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + setAppState(prev_0 => { + if (prev_0.replBridgeEnabled && !prev_0.replBridgeOutboundOnly) { + return prev_0; + } + return { + ...prev_0, + replBridgeEnabled: true, + replBridgeExplicit: true, + replBridgeOutboundOnly: false, + replBridgeInitialName: name + }; + }); + onDone("Remote Control connecting\u2026", { + display: "system" + }); + })(); + return () => { + cancelled = true; + }; + }; + $[0] = name; + $[1] = onDone; + $[2] = replBridgeConnected; + $[3] = replBridgeEnabled; + $[4] = replBridgeOutboundOnly; + $[5] = setAppState; + $[6] = t1; + } else { + t1 = $[6]; + } + let t2; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t2 = []; + $[7] = t2; + } else { + t2 = $[7]; + } + useEffect(t1, t2); + if (showDisconnectDialog) { + let t3; + if ($[8] !== onDone) { + t3 = ; + $[8] = onDone; + $[9] = t3; + } else { + t3 = $[9]; + } + return t3; + } + return null; +} + +/** + * Dialog shown when /remote-control is used while the bridge is already connected. + * Shows the session URL and lets the user disconnect or continue. + */ +function _temp3(s_1) { + return s_1.replBridgeOutboundOnly; +} +function _temp2(s_0) { + return s_0.replBridgeEnabled; +} +function _temp(s) { + return s.replBridgeConnected; +} +function BridgeDisconnectDialog(t0) { + const $ = _c(61); + const { + onDone + } = t0; + useRegisterOverlay("bridge-disconnect-dialog"); + const setAppState = useSetAppState(); + const sessionUrl = useAppState(_temp4); + const connectUrl = useAppState(_temp5); + const sessionActive = useAppState(_temp6); + const [focusIndex, setFocusIndex] = useState(2); + const [showQR, setShowQR] = useState(false); + const [qrText, setQrText] = useState(""); + const displayUrl = sessionActive ? sessionUrl : connectUrl; + let t1; + let t2; + if ($[0] !== displayUrl || $[1] !== showQR) { + t1 = () => { + if (!showQR || !displayUrl) { + setQrText(""); + return; + } + qrToString(displayUrl, { + type: "utf8", + errorCorrectionLevel: "L", + small: true + }).then(setQrText).catch(() => setQrText("")); + }; + t2 = [showQR, displayUrl]; + $[0] = displayUrl; + $[1] = showQR; + $[2] = t1; + $[3] = t2; + } else { + t1 = $[2]; + t2 = $[3]; + } + useEffect(t1, t2); + let t3; + if ($[4] !== onDone || $[5] !== setAppState) { + t3 = function handleDisconnect() { + setAppState(_temp7); + logEvent("tengu_bridge_command", { + action: "disconnect" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + onDone(REMOTE_CONTROL_DISCONNECTED_MSG, { + display: "system" + }); + }; + $[4] = onDone; + $[5] = setAppState; + $[6] = t3; + } else { + t3 = $[6]; + } + const handleDisconnect = t3; + let t4; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t4 = function handleShowQR() { + setShowQR(_temp8); + }; + $[7] = t4; + } else { + t4 = $[7]; + } + const handleShowQR = t4; + let t5; + if ($[8] !== onDone) { + t5 = function handleContinue() { + onDone(undefined, { + display: "skip" + }); + }; + $[8] = onDone; + $[9] = t5; + } else { + t5 = $[9]; + } + const handleContinue = t5; + let t6; + let t7; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t6 = () => setFocusIndex(_temp9); + t7 = () => setFocusIndex(_temp0); + $[10] = t6; + $[11] = t7; + } else { + t6 = $[10]; + t7 = $[11]; + } + let t8; + if ($[12] !== focusIndex || $[13] !== handleContinue || $[14] !== handleDisconnect) { + t8 = { + "select:next": t6, + "select:previous": t7, + "select:accept": () => { + if (focusIndex === 0) { + handleDisconnect(); + } else { + if (focusIndex === 1) { + handleShowQR(); + } else { + handleContinue(); + } + } + } + }; + $[12] = focusIndex; + $[13] = handleContinue; + $[14] = handleDisconnect; + $[15] = t8; + } else { + t8 = $[15]; + } + let t9; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t9 = { + context: "Select" + }; + $[16] = t9; + } else { + t9 = $[16]; + } + useKeybindings(t8, t9); + let T0; + let T1; + let t10; + let t11; + let t12; + let t13; + let t14; + let t15; + let t16; + if ($[17] !== displayUrl || $[18] !== handleContinue || $[19] !== qrText || $[20] !== showQR) { + const qrLines = qrText ? qrText.split("\n").filter(_temp1) : []; + T1 = Dialog; + t14 = "Remote Control"; + t15 = handleContinue; + t16 = true; + T0 = Box; + t10 = "column"; + t11 = 1; + const t17 = displayUrl ? ` at ${displayUrl}` : ""; + if ($[30] !== t17) { + t12 = This session is available via Remote Control{t17}.; + $[30] = t17; + $[31] = t12; + } else { + t12 = $[31]; + } + t13 = showQR && qrLines.length > 0 && {qrLines.map(_temp10)}; + $[17] = displayUrl; + $[18] = handleContinue; + $[19] = qrText; + $[20] = showQR; + $[21] = T0; + $[22] = T1; + $[23] = t10; + $[24] = t11; + $[25] = t12; + $[26] = t13; + $[27] = t14; + $[28] = t15; + $[29] = t16; + } else { + T0 = $[21]; + T1 = $[22]; + t10 = $[23]; + t11 = $[24]; + t12 = $[25]; + t13 = $[26]; + t14 = $[27]; + t15 = $[28]; + t16 = $[29]; + } + const t17 = focusIndex === 0; + let t18; + if ($[32] === Symbol.for("react.memo_cache_sentinel")) { + t18 = Disconnect this session; + $[32] = t18; + } else { + t18 = $[32]; + } + let t19; + if ($[33] !== t17) { + t19 = {t18}; + $[33] = t17; + $[34] = t19; + } else { + t19 = $[34]; + } + const t20 = focusIndex === 1; + const t21 = showQR ? "Hide QR code" : "Show QR code"; + let t22; + if ($[35] !== t21) { + t22 = {t21}; + $[35] = t21; + $[36] = t22; + } else { + t22 = $[36]; + } + let t23; + if ($[37] !== t20 || $[38] !== t22) { + t23 = {t22}; + $[37] = t20; + $[38] = t22; + $[39] = t23; + } else { + t23 = $[39]; + } + const t24 = focusIndex === 2; + let t25; + if ($[40] === Symbol.for("react.memo_cache_sentinel")) { + t25 = Continue; + $[40] = t25; + } else { + t25 = $[40]; + } + let t26; + if ($[41] !== t24) { + t26 = {t25}; + $[41] = t24; + $[42] = t26; + } else { + t26 = $[42]; + } + let t27; + if ($[43] !== t19 || $[44] !== t23 || $[45] !== t26) { + t27 = {t19}{t23}{t26}; + $[43] = t19; + $[44] = t23; + $[45] = t26; + $[46] = t27; + } else { + t27 = $[46]; + } + let t28; + if ($[47] === Symbol.for("react.memo_cache_sentinel")) { + t28 = Enter to select · Esc to continue; + $[47] = t28; + } else { + t28 = $[47]; + } + let t29; + if ($[48] !== T0 || $[49] !== t10 || $[50] !== t11 || $[51] !== t12 || $[52] !== t13 || $[53] !== t27) { + t29 = {t12}{t13}{t27}{t28}; + $[48] = T0; + $[49] = t10; + $[50] = t11; + $[51] = t12; + $[52] = t13; + $[53] = t27; + $[54] = t29; + } else { + t29 = $[54]; + } + let t30; + if ($[55] !== T1 || $[56] !== t14 || $[57] !== t15 || $[58] !== t16 || $[59] !== t29) { + t30 = {t29}; + $[55] = T1; + $[56] = t14; + $[57] = t15; + $[58] = t16; + $[59] = t29; + $[60] = t30; + } else { + t30 = $[60]; + } + return t30; +} + +/** + * Check bridge prerequisites. Returns an error message if a precondition + * fails, or null if all checks pass. Awaits GrowthBook init if the disk + * cache is stale, so a user who just became entitled (e.g. upgraded to Max, + * or the flag just launched) gets an accurate result on the first try. + */ +function _temp10(line, i_1) { + return {line}; +} +function _temp1(l) { + return l.length > 0; +} +function _temp0(i_0) { + return (i_0 - 1 + 3) % 3; +} +function _temp9(i) { + return (i + 1) % 3; +} +function _temp8(prev_0) { + return !prev_0; +} +function _temp7(prev) { + if (!prev.replBridgeEnabled) { + return prev; + } + return { + ...prev, + replBridgeEnabled: false, + replBridgeExplicit: false, + replBridgeOutboundOnly: false + }; +} +function _temp6(s_1) { + return s_1.replBridgeSessionActive; +} +function _temp5(s_0) { + return s_0.replBridgeConnectUrl; +} +function _temp4(s) { + return s.replBridgeSessionUrl; +} +async function checkBridgePrerequisites(): Promise { + // Check organization policy — remote control may be disabled + const { + waitForPolicyLimitsToLoad, + isPolicyAllowed + } = await import('../../services/policyLimits/index.js'); + await waitForPolicyLimitsToLoad(); + if (!isPolicyAllowed('allow_remote_control')) { + return "Remote Control is disabled by your organization's policy."; + } + const disabledReason = await getBridgeDisabledReason(); + if (disabledReason) { + return disabledReason; + } + + // Mirror the v1/v2 branching logic in initReplBridge: env-less (v2) is used + // only when the flag is on AND the session is not perpetual. In assistant + // mode (KAIROS) useReplBridge sets perpetual=true, which forces + // initReplBridge onto the v1 path — so the prerequisite check must match. + let useV2 = isEnvLessBridgeEnabled(); + if (feature('KAIROS') && useV2) { + const { + isAssistantMode + } = await import('../../assistant/index.js'); + if (isAssistantMode()) { + useV2 = false; + } + } + const versionError = useV2 ? await checkEnvLessBridgeMinVersion() : checkBridgeMinVersion(); + if (versionError) { + return versionError; + } + if (!getBridgeAccessToken()) { + return BRIDGE_LOGIN_INSTRUCTION; + } + logForDebugging('[bridge] Prerequisites passed, enabling bridge'); + return null; +} +export async function call(onDone: LocalJSXCommandOnDone, _context: ToolUseContext & LocalJSXCommandContext, args: string): Promise { + const name = args.trim() || undefined; + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwidG9TdHJpbmciLCJxclRvU3RyaW5nIiwiUmVhY3QiLCJ1c2VFZmZlY3QiLCJ1c2VTdGF0ZSIsImdldEJyaWRnZUFjY2Vzc1Rva2VuIiwiY2hlY2tCcmlkZ2VNaW5WZXJzaW9uIiwiZ2V0QnJpZGdlRGlzYWJsZWRSZWFzb24iLCJpc0Vudkxlc3NCcmlkZ2VFbmFibGVkIiwiY2hlY2tFbnZMZXNzQnJpZGdlTWluVmVyc2lvbiIsIkJSSURHRV9MT0dJTl9JTlNUUlVDVElPTiIsIlJFTU9URV9DT05UUk9MX0RJU0NPTk5FQ1RFRF9NU0ciLCJEaWFsb2ciLCJMaXN0SXRlbSIsInNob3VsZFNob3dSZW1vdGVDYWxsb3V0IiwidXNlUmVnaXN0ZXJPdmVybGF5IiwiQm94IiwiVGV4dCIsInVzZUtleWJpbmRpbmdzIiwiQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyIsImxvZ0V2ZW50IiwidXNlQXBwU3RhdGUiLCJ1c2VTZXRBcHBTdGF0ZSIsIlRvb2xVc2VDb250ZXh0IiwiTG9jYWxKU1hDb21tYW5kQ29udGV4dCIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImxvZ0ZvckRlYnVnZ2luZyIsIlByb3BzIiwib25Eb25lIiwibmFtZSIsIkJyaWRnZVRvZ2dsZSIsInQwIiwiJCIsIl9jIiwic2V0QXBwU3RhdGUiLCJyZXBsQnJpZGdlQ29ubmVjdGVkIiwiX3RlbXAiLCJyZXBsQnJpZGdlRW5hYmxlZCIsIl90ZW1wMiIsInJlcGxCcmlkZ2VPdXRib3VuZE9ubHkiLCJfdGVtcDMiLCJzaG93RGlzY29ubmVjdERpYWxvZyIsInNldFNob3dEaXNjb25uZWN0RGlhbG9nIiwidDEiLCJjYW5jZWxsZWQiLCJlcnJvciIsImNoZWNrQnJpZGdlUHJlcmVxdWlzaXRlcyIsImFjdGlvbiIsImRpc3BsYXkiLCJwcmV2Iiwic2hvd1JlbW90ZUNhbGxvdXQiLCJyZXBsQnJpZGdlSW5pdGlhbE5hbWUiLCJwcmV2XzAiLCJyZXBsQnJpZGdlRXhwbGljaXQiLCJ0MiIsIlN5bWJvbCIsImZvciIsInQzIiwic18xIiwicyIsInNfMCIsIkJyaWRnZURpc2Nvbm5lY3REaWFsb2ciLCJzZXNzaW9uVXJsIiwiX3RlbXA0IiwiY29ubmVjdFVybCIsIl90ZW1wNSIsInNlc3Npb25BY3RpdmUiLCJfdGVtcDYiLCJmb2N1c0luZGV4Iiwic2V0Rm9jdXNJbmRleCIsInNob3dRUiIsInNldFNob3dRUiIsInFyVGV4dCIsInNldFFyVGV4dCIsImRpc3BsYXlVcmwiLCJ0eXBlIiwiZXJyb3JDb3JyZWN0aW9uTGV2ZWwiLCJzbWFsbCIsInRoZW4iLCJjYXRjaCIsImhhbmRsZURpc2Nvbm5lY3QiLCJfdGVtcDciLCJ0NCIsImhhbmRsZVNob3dRUiIsIl90ZW1wOCIsInQ1IiwiaGFuZGxlQ29udGludWUiLCJ1bmRlZmluZWQiLCJ0NiIsInQ3IiwiX3RlbXA5IiwiX3RlbXAwIiwidDgiLCJzZWxlY3Q6YWNjZXB0IiwidDkiLCJjb250ZXh0IiwiVDAiLCJUMSIsInQxMCIsInQxMSIsInQxMiIsInQxMyIsInQxNCIsInQxNSIsInQxNiIsInFyTGluZXMiLCJzcGxpdCIsImZpbHRlciIsIl90ZW1wMSIsInQxNyIsImxlbmd0aCIsIm1hcCIsIl90ZW1wMTAiLCJ0MTgiLCJ0MTkiLCJ0MjAiLCJ0MjEiLCJ0MjIiLCJ0MjMiLCJ0MjQiLCJ0MjUiLCJ0MjYiLCJ0MjciLCJ0MjgiLCJ0MjkiLCJ0MzAiLCJsaW5lIiwiaV8xIiwiaSIsImwiLCJpXzAiLCJyZXBsQnJpZGdlU2Vzc2lvbkFjdGl2ZSIsInJlcGxCcmlkZ2VDb25uZWN0VXJsIiwicmVwbEJyaWRnZVNlc3Npb25VcmwiLCJQcm9taXNlIiwid2FpdEZvclBvbGljeUxpbWl0c1RvTG9hZCIsImlzUG9saWN5QWxsb3dlZCIsImRpc2FibGVkUmVhc29uIiwidXNlVjIiLCJpc0Fzc2lzdGFudE1vZGUiLCJ2ZXJzaW9uRXJyb3IiLCJjYWxsIiwiX2NvbnRleHQiLCJhcmdzIiwiUmVhY3ROb2RlIiwidHJpbSJdLCJzb3VyY2VzIjpbImJyaWRnZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgeyB0b1N0cmluZyBhcyBxclRvU3RyaW5nIH0gZnJvbSAncXJjb2RlJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBnZXRCcmlkZ2VBY2Nlc3NUb2tlbiB9IGZyb20gJy4uLy4uL2JyaWRnZS9icmlkZ2VDb25maWcuanMnXG5pbXBvcnQge1xuICBjaGVja0JyaWRnZU1pblZlcnNpb24sXG4gIGdldEJyaWRnZURpc2FibGVkUmVhc29uLFxuICBpc0Vudkxlc3NCcmlkZ2VFbmFibGVkLFxufSBmcm9tICcuLi8uLi9icmlkZ2UvYnJpZGdlRW5hYmxlZC5qcydcbmltcG9ydCB7IGNoZWNrRW52TGVzc0JyaWRnZU1pblZlcnNpb24gfSBmcm9tICcuLi8uLi9icmlkZ2UvZW52TGVzc0JyaWRnZUNvbmZpZy5qcydcbmltcG9ydCB7XG4gIEJSSURHRV9MT0dJTl9JTlNUUlVDVElPTixcbiAgUkVNT1RFX0NPTlRST0xfRElTQ09OTkVDVEVEX01TRyxcbn0gZnJvbSAnLi4vLi4vYnJpZGdlL3R5cGVzLmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcbmltcG9ydCB7IExpc3RJdGVtIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9kZXNpZ24tc3lzdGVtL0xpc3RJdGVtLmpzJ1xuaW1wb3J0IHsgc2hvdWxkU2hvd1JlbW90ZUNhbGxvdXQgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL1JlbW90ZUNhbGxvdXQuanMnXG5pbXBvcnQgeyB1c2VSZWdpc3Rlck92ZXJsYXkgfSBmcm9tICcuLi8uLi9jb250ZXh0L292ZXJsYXlDb250ZXh0LmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZ3MgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnLi4vLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUsIHVzZVNldEFwcFN0YXRlIH0gZnJvbSAnLi4vLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xVc2VDb250ZXh0IH0gZnJvbSAnLi4vLi4vVG9vbC5qcydcbmltcG9ydCB0eXBlIHtcbiAgTG9jYWxKU1hDb21tYW5kQ29udGV4dCxcbiAgTG9jYWxKU1hDb21tYW5kT25Eb25lLFxufSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuaW1wb3J0IHsgbG9nRm9yRGVidWdnaW5nIH0gZnJvbSAnLi4vLi4vdXRpbHMvZGVidWcuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lXG4gIG5hbWU/OiBzdHJpbmdcbn1cblxuLyoqXG4gKiAvcmVtb3RlLWNvbnRyb2wgY29tbWFuZCDigJQgbWFuYWdlcyB0aGUgYmlkaXJlY3Rpb25hbCBicmlkZ2UgY29ubmVjdGlvbi5cbiAqXG4gKiBXaGVuIGVuYWJsZWQsIHNldHMgcmVwbEJyaWRnZUVuYWJsZWQgaW4gQXBwU3RhdGUsIHdoaWNoIHRyaWdnZXJzXG4gKiB1c2VSZXBsQnJpZGdlIGluIFJFUEwudHN4IHRvIGluaXRpYWxpemUgdGhlIGJyaWRnZSBjb25uZWN0aW9uLlxuICogVGhlIGJyaWRnZSByZWdpc3RlcnMgYW4gZW52aXJvbm1lbnQsIGNyZWF0ZXMgYSBzZXNzaW9uIHdpdGggdGhlIGN1cnJlbnRcbiAqIGNvbnZlcnNhdGlvbiwgcG9sbHMgZm9yIHdvcmssIGFuZCBjb25uZWN0cyBhbiBpbmdyZXNzIFdlYlNvY2tldCBmb3JcbiAqIGJpZGlyZWN0aW9uYWwgbWVzc2FnaW5nIGJldHdlZW4gdGhlIENMSSBhbmQgY2xhdWRlLmFpLlxuICpcbiAqIFJ1bm5pbmcgL3JlbW90ZS1jb250cm9sIHdoZW4gYWxyZWFkeSBjb25uZWN0ZWQgc2hvd3MgYSBkaWFsb2cgd2l0aCB0aGUgc2Vzc2lvblxuICogVVJMIGFuZCBvcHRpb25zIHRvIGRpc2Nvbm5lY3Qgb3IgY29udGludWUuXG4gKi9cbmZ1bmN0aW9uIEJyaWRnZVRvZ2dsZSh7IG9uRG9uZSwgbmFtZSB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHNldEFwcFN0YXRlID0gdXNlU2V0QXBwU3RhdGUoKVxuICBjb25zdCByZXBsQnJpZGdlQ29ubmVjdGVkID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VDb25uZWN0ZWQpXG4gIGNvbnN0IHJlcGxCcmlkZ2VFbmFibGVkID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VFbmFibGVkKVxuICBjb25zdCByZXBsQnJpZGdlT3V0Ym91bmRPbmx5ID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VPdXRib3VuZE9ubHkpXG4gIGNvbnN0IFtzaG93RGlzY29ubmVjdERpYWxvZywgc2V0U2hvd0Rpc2Nvbm5lY3REaWFsb2ddID0gdXNlU3RhdGUoZmFsc2UpXG5cbiAgLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlRXhoYXVzdGl2ZURlcGVuZGVuY2llczogYnJpZGdlIHN0YXJ0cyBvbmNlLCBzaG91bGQgbm90IHJlc3RhcnQgb24gc3RhdGUgY2hhbmdlc1xuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIC8vIElmIGFscmVhZHkgY29ubmVjdGVkIG9yIGVuYWJsZWQgaW4gZnVsbCBiaWRpcmVjdGlvbmFsIG1vZGUsIHNob3dcbiAgICAvLyBkaXNjb25uZWN0IGNvbmZpcm1hdGlvbi4gT3V0Ym91bmQtb25seSAoQ0NSIG1pcnJvcikgZG9lc24ndCBjb3VudCDigJRcbiAgICAvLyAvcmVtb3RlLWNvbnRyb2wgdXBncmFkZXMgaXQgdG8gZnVsbCBSQyBpbnN0ZWFkLlxuICAgIGlmICgocmVwbEJyaWRnZUNvbm5lY3RlZCB8fCByZXBsQnJpZGdlRW5hYmxlZCkgJiYgIXJlcGxCcmlkZ2VPdXRib3VuZE9ubHkpIHtcbiAgICAgIHNldFNob3dEaXNjb25uZWN0RGlhbG9nKHRydWUpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBsZXQgY2FuY2VsbGVkID0gZmFsc2VcbiAgICB2b2lkIChhc3luYyAoKSA9PiB7XG4gICAgICAvLyBQcmUtZmxpZ2h0IGNoZWNrcyBiZWZvcmUgZW5hYmxpbmcgKGF3YWl0cyBHcm93dGhCb29rIGluaXQgaWYgZGlza1xuICAgICAgLy8gY2FjaGUgaXMgc3RhbGUg4oCUIHNvIE1heCB1c2VycyBkb24ndCBnZXQgYSBmYWxzZSBcIm5vdCBlbmFibGVkXCIgZXJyb3IpXG4gICAgICBjb25zdCBlcnJvciA9IGF3YWl0IGNoZWNrQnJpZGdlUHJlcmVxdWlzaXRlcygpXG4gICAgICBpZiAoY2FuY2VsbGVkKSByZXR1cm5cbiAgICAgIGlmIChlcnJvcikge1xuICAgICAgICBsb2dFdmVudCgndGVuZ3VfYnJpZGdlX2NvbW1hbmQnLCB7XG4gICAgICAgICAgYWN0aW9uOlxuICAgICAgICAgICAgJ3ByZWZsaWdodF9mYWlsZWQnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIH0pXG4gICAgICAgIG9uRG9uZShlcnJvciwgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gU2hvdyBmaXJzdC10aW1lIHJlbW90ZSBkaWFsb2cgaWYgbm90IHlldCBzZWVuLlxuICAgICAgLy8gU3RvcmUgdGhlIG5hbWUgbm93IHNvIGl0J3MgaW4gQXBwU3RhdGUgd2hlbiB0aGUgY2FsbG91dCBoYW5kbGVyIGxhdGVyXG4gICAgICAvLyBlbmFibGVzIHRoZSBicmlkZ2UgKHRoZSBoYW5kbGVyIG9ubHkgc2V0cyByZXBsQnJpZGdlRW5hYmxlZCwgbm90IHRoZSBuYW1lKS5cbiAgICAgIGlmIChzaG91bGRTaG93UmVtb3RlQ2FsbG91dCgpKSB7XG4gICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgIGlmIChwcmV2LnNob3dSZW1vdGVDYWxsb3V0KSByZXR1cm4gcHJldlxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgc2hvd1JlbW90ZUNhbGxvdXQ6IHRydWUsXG4gICAgICAgICAgICByZXBsQnJpZGdlSW5pdGlhbE5hbWU6IG5hbWUsXG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgICBvbkRvbmUoJycsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIEVuYWJsZSB0aGUgYnJpZGdlIOKAlCB1c2VSZXBsQnJpZGdlIGluIFJFUEwudHN4IGhhbmRsZXMgdGhlIHJlc3Q6XG4gICAgICAvLyByZWdpc3RlcnMgZW52aXJvbm1lbnQsIGNyZWF0ZXMgc2Vzc2lvbiB3aXRoIGNvbnZlcnNhdGlvbiwgY29ubmVjdHMgV2ViU29ja2V0XG4gICAgICBsb2dFdmVudCgndGVuZ3VfYnJpZGdlX2NvbW1hbmQnLCB7XG4gICAgICAgIGFjdGlvbjpcbiAgICAgICAgICAnY29ubmVjdCcgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgIH0pXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgaWYgKHByZXYucmVwbEJyaWRnZUVuYWJsZWQgJiYgIXByZXYucmVwbEJyaWRnZU91dGJvdW5kT25seSkgcmV0dXJuIHByZXZcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgIHJlcGxCcmlkZ2VFbmFibGVkOiB0cnVlLFxuICAgICAgICAgIHJlcGxCcmlkZ2VFeHBsaWNpdDogdHJ1ZSxcbiAgICAgICAgICByZXBsQnJpZGdlT3V0Ym91bmRPbmx5OiBmYWxzZSxcbiAgICAgICAgICByZXBsQnJpZGdlSW5pdGlhbE5hbWU6IG5hbWUsXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICBvbkRvbmUoJ1JlbW90ZSBDb250cm9sIGNvbm5lY3RpbmdcXHUyMDI2Jywge1xuICAgICAgICBkaXNwbGF5OiAnc3lzdGVtJyxcbiAgICAgIH0pXG4gICAgfSkoKVxuXG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGNhbmNlbGxlZCA9IHRydWVcbiAgICB9XG4gIH0sIFtdKSAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwcyAtLSBydW4gb25jZSBvbiBtb3VudFxuXG4gIGlmIChzaG93RGlzY29ubmVjdERpYWxvZykge1xuICAgIHJldHVybiA8QnJpZGdlRGlzY29ubmVjdERpYWxvZyBvbkRvbmU9e29uRG9uZX0gLz5cbiAgfVxuXG4gIHJldHVybiBudWxsXG59XG5cbi8qKlxuICogRGlhbG9nIHNob3duIHdoZW4gL3JlbW90ZS1jb250cm9sIGlzIHVzZWQgd2hpbGUgdGhlIGJyaWRnZSBpcyBhbHJlYWR5IGNvbm5lY3RlZC5cbiAqIFNob3dzIHRoZSBzZXNzaW9uIFVSTCBhbmQgbGV0cyB0aGUgdXNlciBkaXNjb25uZWN0IG9yIGNvbnRpbnVlLlxuICovXG5mdW5jdGlvbiBCcmlkZ2VEaXNjb25uZWN0RGlhbG9nKHsgb25Eb25lIH06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgdXNlUmVnaXN0ZXJPdmVybGF5KCdicmlkZ2UtZGlzY29ubmVjdC1kaWFsb2cnKVxuICBjb25zdCBzZXRBcHBTdGF0ZSA9IHVzZVNldEFwcFN0YXRlKClcbiAgY29uc3Qgc2Vzc2lvblVybCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlU2Vzc2lvblVybClcbiAgY29uc3QgY29ubmVjdFVybCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlQ29ubmVjdFVybClcbiAgY29uc3Qgc2Vzc2lvbkFjdGl2ZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlU2Vzc2lvbkFjdGl2ZSlcbiAgY29uc3QgW2ZvY3VzSW5kZXgsIHNldEZvY3VzSW5kZXhdID0gdXNlU3RhdGUoMilcbiAgY29uc3QgW3Nob3dRUiwgc2V0U2hvd1FSXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbcXJUZXh0LCBzZXRRclRleHRdID0gdXNlU3RhdGUoJycpXG5cbiAgY29uc3QgZGlzcGxheVVybCA9IHNlc3Npb25BY3RpdmUgPyBzZXNzaW9uVXJsIDogY29ubmVjdFVybFxuXG4gIC8vIEdlbmVyYXRlIFFSIGNvZGUgd2hlbiBVUkwgY2hhbmdlcyBvciBRUiBpcyB0b2dnbGVkIG9uXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKCFzaG93UVIgfHwgIWRpc3BsYXlVcmwpIHtcbiAgICAgIHNldFFyVGV4dCgnJylcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBxclRvU3RyaW5nKGRpc3BsYXlVcmwsIHtcbiAgICAgIHR5cGU6ICd1dGY4JyxcbiAgICAgIGVycm9yQ29ycmVjdGlvbkxldmVsOiAnTCcsXG4gICAgICBzbWFsbDogdHJ1ZSxcbiAgICB9KVxuICAgICAgLnRoZW4oc2V0UXJUZXh0KVxuICAgICAgLmNhdGNoKCgpID0+IHNldFFyVGV4dCgnJykpXG4gIH0sIFtzaG93UVIsIGRpc3BsYXlVcmxdKVxuXG4gIGZ1bmN0aW9uIGhhbmRsZURpc2Nvbm5lY3QoKTogdm9pZCB7XG4gICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICBpZiAoIXByZXYucmVwbEJyaWRnZUVuYWJsZWQpIHJldHVybiBwcmV2XG4gICAgICByZXR1cm4ge1xuICAgICAgICAuLi5wcmV2LFxuICAgICAgICByZXBsQnJpZGdlRW5hYmxlZDogZmFsc2UsXG4gICAgICAgIHJlcGxCcmlkZ2VFeHBsaWNpdDogZmFsc2UsXG4gICAgICAgIHJlcGxCcmlkZ2VPdXRib3VuZE9ubHk6IGZhbHNlLFxuICAgICAgfVxuICAgIH0pXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X2JyaWRnZV9jb21tYW5kJywge1xuICAgICAgYWN0aW9uOlxuICAgICAgICAnZGlzY29ubmVjdCcgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICB9KVxuICAgIG9uRG9uZShSRU1PVEVfQ09OVFJPTF9ESVNDT05ORUNURURfTVNHLCB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0pXG4gIH1cblxuICBmdW5jdGlvbiBoYW5kbGVTaG93UVIoKTogdm9pZCB7XG4gICAgc2V0U2hvd1FSKHByZXYgPT4gIXByZXYpXG4gIH1cblxuICBmdW5jdGlvbiBoYW5kbGVDb250aW51ZSgpOiB2b2lkIHtcbiAgICBvbkRvbmUodW5kZWZpbmVkLCB7IGRpc3BsYXk6ICdza2lwJyB9KVxuICB9XG5cbiAgY29uc3QgSVRFTV9DT1VOVCA9IDNcblxuICB1c2VLZXliaW5kaW5ncyhcbiAgICB7XG4gICAgICAnc2VsZWN0Om5leHQnOiAoKSA9PiBzZXRGb2N1c0luZGV4KGkgPT4gKGkgKyAxKSAlIElURU1fQ09VTlQpLFxuICAgICAgJ3NlbGVjdDpwcmV2aW91cyc6ICgpID0+XG4gICAgICAgIHNldEZvY3VzSW5kZXgoaSA9PiAoaSAtIDEgKyBJVEVNX0NPVU5UKSAlIElURU1fQ09VTlQpLFxuICAgICAgJ3NlbGVjdDphY2NlcHQnOiAoKSA9PiB7XG4gICAgICAgIGlmIChmb2N1c0luZGV4ID09PSAwKSB7XG4gICAgICAgICAgaGFuZGxlRGlzY29ubmVjdCgpXG4gICAgICAgIH0gZWxzZSBpZiAoZm9jdXNJbmRleCA9PT0gMSkge1xuICAgICAgICAgIGhhbmRsZVNob3dRUigpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaGFuZGxlQ29udGludWUoKVxuICAgICAgICB9XG4gICAgICB9LFxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnU2VsZWN0JyB9LFxuICApXG5cbiAgY29uc3QgcXJMaW5lcyA9IHFyVGV4dCA/IHFyVGV4dC5zcGxpdCgnXFxuJykuZmlsdGVyKGwgPT4gbC5sZW5ndGggPiAwKSA6IFtdXG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nIHRpdGxlPVwiUmVtb3RlIENvbnRyb2xcIiBvbkNhbmNlbD17aGFuZGxlQ29udGludWV9IGhpZGVJbnB1dEd1aWRlPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgVGhpcyBzZXNzaW9uIGlzIGF2YWlsYWJsZSB2aWEgUmVtb3RlIENvbnRyb2xcbiAgICAgICAgICB7ZGlzcGxheVVybCA/IGAgYXQgJHtkaXNwbGF5VXJsfWAgOiAnJ30uXG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAge3Nob3dRUiAmJiBxckxpbmVzLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAge3FyTGluZXMubWFwKChsaW5lLCBpKSA9PiAoXG4gICAgICAgICAgICAgIDxUZXh0IGtleT17aX0+e2xpbmV9PC9UZXh0PlxuICAgICAgICAgICAgKSl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxMaXN0SXRlbSBpc0ZvY3VzZWQ9e2ZvY3VzSW5kZXggPT09IDB9PlxuICAgICAgICAgICAgPFRleHQ+RGlzY29ubmVjdCB0aGlzIHNlc3Npb248L1RleHQ+XG4gICAgICAgICAgPC9MaXN0SXRlbT5cbiAgICAgICAgICA8TGlzdEl0ZW0gaXNGb2N1c2VkPXtmb2N1c0luZGV4ID09PSAxfT5cbiAgICAgICAgICAgIDxUZXh0PntzaG93UVIgPyAnSGlkZSBRUiBjb2RlJyA6ICdTaG93IFFSIGNvZGUnfTwvVGV4dD5cbiAgICAgICAgICA8L0xpc3RJdGVtPlxuICAgICAgICAgIDxMaXN0SXRlbSBpc0ZvY3VzZWQ9e2ZvY3VzSW5kZXggPT09IDJ9PlxuICAgICAgICAgICAgPFRleHQ+Q29udGludWU8L1RleHQ+XG4gICAgICAgICAgPC9MaXN0SXRlbT5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPkVudGVyIHRvIHNlbGVjdCDCtyBFc2MgdG8gY29udGludWU8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuXG4vKipcbiAqIENoZWNrIGJyaWRnZSBwcmVyZXF1aXNpdGVzLiBSZXR1cm5zIGFuIGVycm9yIG1lc3NhZ2UgaWYgYSBwcmVjb25kaXRpb25cbiAqIGZhaWxzLCBvciBudWxsIGlmIGFsbCBjaGVja3MgcGFzcy4gQXdhaXRzIEdyb3d0aEJvb2sgaW5pdCBpZiB0aGUgZGlza1xuICogY2FjaGUgaXMgc3RhbGUsIHNvIGEgdXNlciB3aG8ganVzdCBiZWNhbWUgZW50aXRsZWQgKGUuZy4gdXBncmFkZWQgdG8gTWF4LFxuICogb3IgdGhlIGZsYWcganVzdCBsYXVuY2hlZCkgZ2V0cyBhbiBhY2N1cmF0ZSByZXN1bHQgb24gdGhlIGZpcnN0IHRyeS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY2hlY2tCcmlkZ2VQcmVyZXF1aXNpdGVzKCk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICAvLyBDaGVjayBvcmdhbml6YXRpb24gcG9saWN5IOKAlCByZW1vdGUgY29udHJvbCBtYXkgYmUgZGlzYWJsZWRcbiAgY29uc3QgeyB3YWl0Rm9yUG9saWN5TGltaXRzVG9Mb2FkLCBpc1BvbGljeUFsbG93ZWQgfSA9IGF3YWl0IGltcG9ydChcbiAgICAnLi4vLi4vc2VydmljZXMvcG9saWN5TGltaXRzL2luZGV4LmpzJ1xuICApXG4gIGF3YWl0IHdhaXRGb3JQb2xpY3lMaW1pdHNUb0xvYWQoKVxuICBpZiAoIWlzUG9saWN5QWxsb3dlZCgnYWxsb3dfcmVtb3RlX2NvbnRyb2wnKSkge1xuICAgIHJldHVybiBcIlJlbW90ZSBDb250cm9sIGlzIGRpc2FibGVkIGJ5IHlvdXIgb3JnYW5pemF0aW9uJ3MgcG9saWN5LlwiXG4gIH1cblxuICBjb25zdCBkaXNhYmxlZFJlYXNvbiA9IGF3YWl0IGdldEJyaWRnZURpc2FibGVkUmVhc29uKClcbiAgaWYgKGRpc2FibGVkUmVhc29uKSB7XG4gICAgcmV0dXJuIGRpc2FibGVkUmVhc29uXG4gIH1cblxuICAvLyBNaXJyb3IgdGhlIHYxL3YyIGJyYW5jaGluZyBsb2dpYyBpbiBpbml0UmVwbEJyaWRnZTogZW52LWxlc3MgKHYyKSBpcyB1c2VkXG4gIC8vIG9ubHkgd2hlbiB0aGUgZmxhZyBpcyBvbiBBTkQgdGhlIHNlc3Npb24gaXMgbm90IHBlcnBldHVhbC4gIEluIGFzc2lzdGFudFxuICAvLyBtb2RlIChLQUlST1MpIHVzZVJlcGxCcmlkZ2Ugc2V0cyBwZXJwZXR1YWw9dHJ1ZSwgd2hpY2ggZm9yY2VzXG4gIC8vIGluaXRSZXBsQnJpZGdlIG9udG8gdGhlIHYxIHBhdGgg4oCUIHNvIHRoZSBwcmVyZXF1aXNpdGUgY2hlY2sgbXVzdCBtYXRjaC5cbiAgbGV0IHVzZVYyID0gaXNFbnZMZXNzQnJpZGdlRW5hYmxlZCgpXG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSAmJiB1c2VWMikge1xuICAgIGNvbnN0IHsgaXNBc3Npc3RhbnRNb2RlIH0gPSBhd2FpdCBpbXBvcnQoJy4uLy4uL2Fzc2lzdGFudC9pbmRleC5qcycpXG4gICAgaWYgKGlzQXNzaXN0YW50TW9kZSgpKSB7XG4gICAgICB1c2VWMiA9IGZhbHNlXG4gICAgfVxuICB9XG4gIGNvbnN0IHZlcnNpb25FcnJvciA9IHVzZVYyXG4gICAgPyBhd2FpdCBjaGVja0Vudkxlc3NCcmlkZ2VNaW5WZXJzaW9uKClcbiAgICA6IGNoZWNrQnJpZGdlTWluVmVyc2lvbigpXG4gIGlmICh2ZXJzaW9uRXJyb3IpIHtcbiAgICByZXR1cm4gdmVyc2lvbkVycm9yXG4gIH1cblxuICBpZiAoIWdldEJyaWRnZUFjY2Vzc1Rva2VuKCkpIHtcbiAgICByZXR1cm4gQlJJREdFX0xPR0lOX0lOU1RSVUNUSU9OXG4gIH1cblxuICBsb2dGb3JEZWJ1Z2dpbmcoJ1ticmlkZ2VdIFByZXJlcXVpc2l0ZXMgcGFzc2VkLCBlbmFibGluZyBicmlkZ2UnKVxuICByZXR1cm4gbnVsbFxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4gIF9jb250ZXh0OiBUb29sVXNlQ29udGV4dCAmIExvY2FsSlNYQ29tbWFuZENvbnRleHQsXG4gIGFyZ3M6IHN0cmluZyxcbik6IFByb21pc2U8UmVhY3QuUmVhY3ROb2RlPiB7XG4gIGNvbnN0IG5hbWUgPSBhcmdzLnRyaW0oKSB8fCB1bmRlZmluZWRcbiAgcmV0dXJuIDxCcmlkZ2VUb2dnbGUgb25Eb25lPXtvbkRvbmV9IG5hbWU9e25hbWV9IC8+XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxPQUFPLFFBQVEsWUFBWTtBQUNwQyxTQUFTQyxRQUFRLElBQUlDLFVBQVUsUUFBUSxRQUFRO0FBQy9DLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsU0FBUyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUMzQyxTQUFTQyxvQkFBb0IsUUFBUSw4QkFBOEI7QUFDbkUsU0FDRUMscUJBQXFCLEVBQ3JCQyx1QkFBdUIsRUFDdkJDLHNCQUFzQixRQUNqQiwrQkFBK0I7QUFDdEMsU0FBU0MsNEJBQTRCLFFBQVEscUNBQXFDO0FBQ2xGLFNBQ0VDLHdCQUF3QixFQUN4QkMsK0JBQStCLFFBQzFCLHVCQUF1QjtBQUM5QixTQUFTQyxNQUFNLFFBQVEsMENBQTBDO0FBQ2pFLFNBQVNDLFFBQVEsUUFBUSw0Q0FBNEM7QUFDckUsU0FBU0MsdUJBQXVCLFFBQVEsbUNBQW1DO0FBQzNFLFNBQVNDLGtCQUFrQixRQUFRLGlD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 \ No newline at end of file diff --git a/commands/bridge/index.ts b/commands/bridge/index.ts new file mode 100644 index 0000000..5b6fc44 --- /dev/null +++ b/commands/bridge/index.ts @@ -0,0 +1,26 @@ +import { feature } from 'bun:bundle' +import { isBridgeEnabled } from '../../bridge/bridgeEnabled.js' +import type { Command } from '../../commands.js' + +function isEnabled(): boolean { + if (!feature('BRIDGE_MODE')) { + return false + } + return isBridgeEnabled() +} + +const bridge = { + type: 'local-jsx', + name: 'remote-control', + aliases: ['rc'], + description: 'Connect this terminal for remote-control sessions', + argumentHint: '[name]', + isEnabled, + get isHidden() { + return !isEnabled() + }, + immediate: true, + load: () => import('./bridge.js'), +} satisfies Command + +export default bridge diff --git a/commands/brief.ts b/commands/brief.ts new file mode 100644 index 0000000..d37ffd0 --- /dev/null +++ b/commands/brief.ts @@ -0,0 +1,130 @@ +import { feature } from 'bun:bundle' +import { z } from 'zod/v4' +import { getKairosActive, setUserMsgOptIn } from '../bootstrap/state.js' +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import type { ToolUseContext } from '../Tool.js' +import { isBriefEntitled } from '../tools/BriefTool/BriefTool.js' +import { BRIEF_TOOL_NAME } from '../tools/BriefTool/prompt.js' +import type { + Command, + LocalJSXCommandContext, + LocalJSXCommandOnDone, +} from '../types/command.js' +import { lazySchema } from '../utils/lazySchema.js' + +// Zod guards against fat-fingered GB pushes (same pattern as pollConfig.ts / +// cronScheduler.ts). A malformed config falls back to DEFAULT_BRIEF_CONFIG +// entirely rather than being partially trusted. +const briefConfigSchema = lazySchema(() => + z.object({ + enable_slash_command: z.boolean(), + }), +) +type BriefConfig = z.infer> + +const DEFAULT_BRIEF_CONFIG: BriefConfig = { + enable_slash_command: false, +} + +// No TTL — this gate controls slash-command *visibility*, not a kill switch. +// CACHED_MAY_BE_STALE still has one background-update flip (first call kicks +// off fetch; second call sees fresh value), but no additional flips after that. +// The tool-availability gate (tengu_kairos_brief in isBriefEnabled) keeps its +// 5-min TTL because that one IS a kill switch. +function getBriefConfig(): BriefConfig { + const raw = getFeatureValue_CACHED_MAY_BE_STALE( + 'tengu_kairos_brief_config', + DEFAULT_BRIEF_CONFIG, + ) + const parsed = briefConfigSchema().safeParse(raw) + return parsed.success ? parsed.data : DEFAULT_BRIEF_CONFIG +} + +const brief = { + type: 'local-jsx', + name: 'brief', + description: 'Toggle brief-only mode', + isEnabled: () => { + if (feature('KAIROS') || feature('KAIROS_BRIEF')) { + return getBriefConfig().enable_slash_command + } + return false + }, + immediate: true, + load: () => + Promise.resolve({ + async call( + onDone: LocalJSXCommandOnDone, + context: ToolUseContext & LocalJSXCommandContext, + ): Promise { + const current = context.getAppState().isBriefOnly + const newState = !current + + // Entitlement check only gates the on-transition — off is always + // allowed so a user whose GB gate flipped mid-session isn't stuck. + if (newState && !isBriefEntitled()) { + logEvent('tengu_brief_mode_toggled', { + enabled: false, + gated: true, + source: + 'slash_command' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + onDone('Brief tool is not enabled for your account', { + display: 'system', + }) + return null + } + + // Two-way: userMsgOptIn tracks isBriefOnly so the tool is available + // exactly when brief mode is on. This invalidates prompt cache on + // each toggle (tool list changes), but a stale tool list is worse — + // when /brief is enabled mid-session the model was previously left + // without the tool, emitting plain text the filter hides. + setUserMsgOptIn(newState) + + context.setAppState(prev => { + if (prev.isBriefOnly === newState) return prev + return { ...prev, isBriefOnly: newState } + }) + + logEvent('tengu_brief_mode_toggled', { + enabled: newState, + gated: false, + source: + 'slash_command' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + // The tool list change alone isn't a strong enough signal mid-session + // (model may keep emitting plain text from inertia, or keep calling a + // tool that just vanished). Inject an explicit reminder into the next + // turn's context so the transition is unambiguous. + // Skip when Kairos is active: isBriefEnabled() short-circuits on + // getKairosActive() so the tool never actually leaves the list, and + // the Kairos system prompt already mandates SendUserMessage. + // Inline wrap — importing wrapInSystemReminder from + // utils/messages.ts pulls constants/xml.ts into the bridge SDK bundle + // via this module's import chain, tripping the excluded-strings check. + const metaMessages = getKairosActive() + ? undefined + : [ + `\n${ + newState + ? `Brief mode is now enabled. Use the ${BRIEF_TOOL_NAME} tool for all user-facing output — plain text outside it is hidden from the user's view.` + : `Brief mode is now disabled. The ${BRIEF_TOOL_NAME} tool is no longer available — reply with plain text.` + }\n`, + ] + + onDone( + newState ? 'Brief-only mode enabled' : 'Brief-only mode disabled', + { display: 'system', metaMessages }, + ) + return null + }, + }), +} satisfies Command + +export default brief diff --git a/commands/btw/btw.tsx b/commands/btw/btw.tsx new file mode 100644 index 0000000..f3c1c5f --- /dev/null +++ b/commands/btw/btw.tsx @@ -0,0 +1,243 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { useInterval } from 'usehooks-ts'; +import type { CommandResultDisplay } from '../../commands.js'; +import { Markdown } from '../../components/Markdown.js'; +import { SpinnerGlyph } from '../../components/Spinner/SpinnerGlyph.js'; +import { DOWN_ARROW, UP_ARROW } from '../../constants/figures.js'; +import { getSystemPrompt } from '../../constants/prompts.js'; +import { useModalOrTerminalSize } from '../../context/modalContext.js'; +import { getSystemContext, getUserContext } from '../../context.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import ScrollBox, { type ScrollBoxHandle } from '../../ink/components/ScrollBox.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box, Text } from '../../ink.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import type { Message } from '../../types/message.js'; +import { createAbortController } from '../../utils/abortController.js'; +import { saveGlobalConfig } from '../../utils/config.js'; +import { errorMessage } from '../../utils/errors.js'; +import { type CacheSafeParams, getLastCacheSafeParams } from '../../utils/forkedAgent.js'; +import { getMessagesAfterCompactBoundary } from '../../utils/messages.js'; +import type { ProcessUserInputContext } from '../../utils/processUserInput/processUserInput.js'; +import { runSideQuestion } from '../../utils/sideQuestion.js'; +import { asSystemPrompt } from '../../utils/systemPromptType.js'; +type BtwComponentProps = { + question: string; + context: ProcessUserInputContext; + onDone: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; +}; +const CHROME_ROWS = 5; +const OUTER_CHROME_ROWS = 6; +const SCROLL_LINES = 3; +function BtwSideQuestion(t0) { + const $ = _c(25); + const { + question, + context, + onDone + } = t0; + const [response, setResponse] = useState(null); + const [error, setError] = useState(null); + const [frame, setFrame] = useState(0); + const scrollRef = useRef(null); + const { + rows + } = useModalOrTerminalSize(useTerminalSize()); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => setFrame(_temp); + $[0] = t1; + } else { + t1 = $[0]; + } + useInterval(t1, response || error ? null : 80); + let t2; + if ($[1] !== onDone) { + t2 = function handleKeyDown(e) { + if (e.key === "escape" || e.key === "return" || e.key === " " || e.ctrl && (e.key === "c" || e.key === "d")) { + e.preventDefault(); + onDone(undefined, { + display: "skip" + }); + return; + } + if (e.key === "up" || e.ctrl && e.key === "p") { + e.preventDefault(); + scrollRef.current?.scrollBy(-SCROLL_LINES); + } + if (e.key === "down" || e.ctrl && e.key === "n") { + e.preventDefault(); + scrollRef.current?.scrollBy(SCROLL_LINES); + } + }; + $[1] = onDone; + $[2] = t2; + } else { + t2 = $[2]; + } + const handleKeyDown = t2; + let t3; + let t4; + if ($[3] !== context || $[4] !== question) { + t3 = () => { + const abortController = createAbortController(); + const fetchResponse = async function fetchResponse() { + ; + try { + const cacheSafeParams = await buildCacheSafeParams(context); + const result = await runSideQuestion({ + question, + cacheSafeParams + }); + if (!abortController.signal.aborted) { + if (result.response) { + setResponse(result.response); + } else { + setError("No response received"); + } + } + } catch (t5) { + const err = t5; + if (!abortController.signal.aborted) { + setError(errorMessage(err) || "Failed to get response"); + } + } + }; + fetchResponse(); + return () => { + abortController.abort(); + }; + }; + t4 = [question, context]; + $[3] = context; + $[4] = question; + $[5] = t3; + $[6] = t4; + } else { + t3 = $[5]; + t4 = $[6]; + } + useEffect(t3, t4); + const maxContentHeight = Math.max(5, rows - CHROME_ROWS - OUTER_CHROME_ROWS); + let t5; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t5 = /btw{" "}; + $[7] = t5; + } else { + t5 = $[7]; + } + let t6; + if ($[8] !== question) { + t6 = {t5}{question}; + $[8] = question; + $[9] = t6; + } else { + t6 = $[9]; + } + let t7; + if ($[10] !== error || $[11] !== frame || $[12] !== response) { + t7 = {error ? {error} : response ? {response} : Answering...}; + $[10] = error; + $[11] = frame; + $[12] = response; + $[13] = t7; + } else { + t7 = $[13]; + } + let t8; + if ($[14] !== maxContentHeight || $[15] !== t7) { + t8 = {t7}; + $[14] = maxContentHeight; + $[15] = t7; + $[16] = t8; + } else { + t8 = $[16]; + } + let t9; + if ($[17] !== error || $[18] !== response) { + t9 = (response || error) && {UP_ARROW}/{DOWN_ARROW} to scroll · Space, Enter, or Escape to dismiss; + $[17] = error; + $[18] = response; + $[19] = t9; + } else { + t9 = $[19]; + } + let t10; + if ($[20] !== handleKeyDown || $[21] !== t6 || $[22] !== t8 || $[23] !== t9) { + t10 = {t6}{t8}{t9}; + $[20] = handleKeyDown; + $[21] = t6; + $[22] = t8; + $[23] = t9; + $[24] = t10; + } else { + t10 = $[24]; + } + return t10; +} + +/** + * Build CacheSafeParams for the side question fork. + * + * The preferred source is getLastCacheSafeParams — the exact + * systemPrompt/userContext/systemContext bytes the main thread sent on its + * last request (captured in stopHooks). Reusing them guarantees a byte- + * identical prefix and thus a prompt cache hit. We pair these with the + * current toolUseContext (for thinkingConfig/tools) and current messages + * (for up-to-date context). + * + * Fallback (first turn before stop hooks fire, or prompt-suggestion + * disabled): rebuild from scratch. This may miss the cache if the main loop + * applied buildEffectiveSystemPrompt extras (--agent, --system-prompt, + * --append-system-prompt, coordinator mode). + */ +function _temp(f) { + return f + 1; +} +function stripInProgressAssistantMessage(messages: Message[]): Message[] { + const last = messages.at(-1); + if (last?.type === 'assistant' && last.message.stop_reason === null) { + return messages.slice(0, -1); + } + return messages; +} +async function buildCacheSafeParams(context: ProcessUserInputContext): Promise { + const forkContextMessages = getMessagesAfterCompactBoundary(stripInProgressAssistantMessage(context.messages)); + const saved = getLastCacheSafeParams(); + if (saved) { + return { + systemPrompt: saved.systemPrompt, + userContext: saved.userContext, + systemContext: saved.systemContext, + toolUseContext: context, + forkContextMessages + }; + } + const [rawSystemPrompt, userContext, systemContext] = await Promise.all([getSystemPrompt(context.options.tools, context.options.mainLoopModel, [], context.options.mcpClients), getUserContext(), getSystemContext()]); + return { + systemPrompt: asSystemPrompt(rawSystemPrompt), + userContext, + systemContext, + toolUseContext: context, + forkContextMessages + }; +} +export async function call(onDone: LocalJSXCommandOnDone, context: ProcessUserInputContext, args: string): Promise { + const question = args?.trim(); + if (!question) { + onDone('Usage: /btw ', { + display: 'system' + }); + return null; + } + saveGlobalConfig(current => ({ + ...current, + btwUseCount: current.btwUseCount + 1 + })); + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZVN0YXRlIiwidXNlSW50ZXJ2YWwiLCJDb21tYW5kUmVzdWx0RGlzcGxheSIsIk1hcmtkb3duIiwiU3Bpbm5lckdseXBoIiwiRE9XTl9BUlJPVyIsIlVQX0FSUk9XIiwiZ2V0U3lzdGVtUHJvbXB0IiwidXNlTW9kYWxPclRlcm1pbmFsU2l6ZSIsImdldFN5c3RlbUNvbnRleHQiLCJnZXRVc2VyQ29udGV4dCIsInVzZVRlcm1pbmFsU2l6ZSIsIlNjcm9sbEJveCIsIlNjcm9sbEJveEhhbmRsZSIsIktleWJvYXJkRXZlbnQiLCJCb3giLCJUZXh0IiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiTWVzc2FnZSIsImNyZWF0ZUFib3J0Q29udHJvbGxlciIsInNhdmVHbG9iYWxDb25maWciLCJlcnJvck1lc3NhZ2UiLCJDYWNoZVNhZmVQYXJhbXMiLCJnZXRMYXN0Q2FjaGVTYWZlUGFyYW1zIiwiZ2V0TWVzc2FnZXNBZnRlckNvbXBhY3RCb3VuZGFyeSIsIlByb2Nlc3NVc2VySW5wdXRDb250ZXh0IiwicnVuU2lkZVF1ZXN0aW9uIiwiYXNTeXN0ZW1Qcm9tcHQiLCJCdHdDb21wb25lbnRQcm9wcyIsInF1ZXN0aW9uIiwiY29udGV4dCIsIm9uRG9uZSIsInJlc3VsdCIsIm9wdGlvbnMiLCJkaXNwbGF5IiwiQ0hST01FX1JPV1MiLCJPVVRFUl9DSFJPTUVfUk9XUyIsIlNDUk9MTF9MSU5FUyIsIkJ0d1NpZGVRdWVzdGlvbiIsInQwIiwiJCIsIl9jIiwicmVzcG9uc2UiLCJzZXRSZXNwb25zZSIsImVycm9yIiwic2V0RXJyb3IiLCJmcmFtZSIsInNldEZyYW1lIiwic2Nyb2xsUmVmIiwicm93cyIsInQxIiwiU3ltYm9sIiwiZm9yIiwiX3RlbXAiLCJ0MiIsImhhbmRsZUtleURvd24iLCJlIiwia2V5IiwiY3RybCIsInByZXZlbnREZWZhdWx0IiwidW5kZWZpbmVkIiwiY3VycmVudCIsInNjcm9sbEJ5IiwidDMiLCJ0NCIsImFib3J0Q29udHJvbGxlciIsImZldGNoUmVzcG9uc2UiLCJjYWNoZVNhZmVQYXJhbXMiLCJidWlsZENhY2hlU2FmZVBhcmFtcyIsInNpZ25hbCIsImFib3J0ZWQiLCJ0NSIsImVyciIsImFib3J0IiwibWF4Q29udGVudEhlaWdodCIsIk1hdGgiLCJtYXgiLCJ0NiIsInQ3IiwidDgiLCJ0OSIsInQxMCIsImYiLCJzdHJpcEluUHJvZ3Jlc3NBc3Npc3RhbnRNZXNzYWdlIiwibWVzc2FnZXMiLCJsYXN0IiwiYXQiLCJ0eXBlIiwibWVzc2FnZSIsInN0b3BfcmVhc29uIiwic2xpY2UiLCJQcm9taXNlIiwiZm9ya0NvbnRleHRNZXNzYWdlcyIsInNhdmVkIiwic3lzdGVtUHJvbXB0IiwidXNlckNvbnRleHQiLCJzeXN0ZW1Db250ZXh0IiwidG9vbFVzZUNvbnRleHQiLCJyYXdTeXN0ZW1Qcm9tcHQiLCJhbGwiLCJ0b29scyIsIm1haW5Mb29wTW9kZWwiLCJtY3BDbGllbnRzIiwiY2FsbCIsImFyZ3MiLCJSZWFjdE5vZGUiLCJ0cmltIiwiYnR3VXNlQ291bnQiXSwic291cmNlcyI6WyJidHcudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VJbnRlcnZhbCB9IGZyb20gJ3VzZWhvb2tzLXRzJ1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgTWFya2Rvd24gfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL01hcmtkb3duLmpzJ1xuaW1wb3J0IHsgU3Bpbm5lckdseXBoIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9TcGlubmVyL1NwaW5uZXJHbHlwaC5qcydcbmltcG9ydCB7IERPV05fQVJST1csIFVQX0FSUk9XIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL2ZpZ3VyZXMuanMnXG5pbXBvcnQgeyBnZXRTeXN0ZW1Qcm9tcHQgfSBmcm9tICcuLi8uLi9jb25zdGFudHMvcHJvbXB0cy5qcydcbmltcG9ydCB7IHVzZU1vZGFsT3JUZXJtaW5hbFNpemUgfSBmcm9tICcuLi8uLi9jb250ZXh0L21vZGFsQ29udGV4dC5qcydcbmltcG9ydCB7IGdldFN5c3RlbUNvbnRleHQsIGdldFVzZXJDb250ZXh0IH0gZnJvbSAnLi4vLi4vY29udGV4dC5qcydcbmltcG9ydCB7IHVzZVRlcm1pbmFsU2l6ZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZVRlcm1pbmFsU2l6ZS5qcydcbmltcG9ydCBTY3JvbGxCb3gsIHtcbiAgdHlwZSBTY3JvbGxCb3hIYW5kbGUsXG59IGZyb20gJy4uLy4uL2luay9jb21wb25lbnRzL1Njcm9sbEJveC5qcydcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uLy4uL2luay9ldmVudHMva2V5Ym9hcmQtZXZlbnQuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgdHlwZSB7IE1lc3NhZ2UgfSBmcm9tICcuLi8uLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHsgY3JlYXRlQWJvcnRDb250cm9sbGVyIH0gZnJvbSAnLi4vLi4vdXRpbHMvYWJvcnRDb250cm9sbGVyLmpzJ1xuaW1wb3J0IHsgc2F2ZUdsb2JhbENvbmZpZyB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGVycm9yTWVzc2FnZSB9IGZyb20gJy4uLy4uL3V0aWxzL2Vycm9ycy5qcydcbmltcG9ydCB7XG4gIHR5cGUgQ2FjaGVTYWZlUGFyYW1zLFxuICBnZXRMYXN0Q2FjaGVTYWZlUGFyYW1zLFxufSBmcm9tICcuLi8uLi91dGlscy9mb3JrZWRBZ2VudC5qcydcbmltcG9ydCB7IGdldE1lc3NhZ2VzQWZ0ZXJDb21wYWN0Qm91bmRhcnkgfSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB0eXBlIHsgUHJvY2Vzc1VzZXJJbnB1dENvbnRleHQgfSBmcm9tICcuLi8uLi91dGlscy9wcm9jZXNzVXNlcklucHV0L3Byb2Nlc3NVc2VySW5wdXQuanMnXG5pbXBvcnQgeyBydW5TaWRlUXVlc3Rpb24gfSBmcm9tICcuLi8uLi91dGlscy9zaWRlUXVlc3Rpb24uanMnXG5pbXBvcnQgeyBhc1N5c3RlbVByb21wdCB9IGZyb20gJy4uLy4uL3V0aWxzL3N5c3RlbVByb21wdFR5cGUuanMnXG5cbnR5cGUgQnR3Q29tcG9uZW50UHJvcHMgPSB7XG4gIHF1ZXN0aW9uOiBzdHJpbmdcbiAgY29udGV4dDogUHJvY2Vzc1VzZXJJbnB1dENvbnRleHRcbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWRcbn1cblxuY29uc3QgQ0hST01FX1JPV1MgPSA1XG5jb25zdCBPVVRFUl9DSFJPTUVfUk9XUyA9IDZcbmNvbnN0IFNDUk9MTF9MSU5FUyA9IDNcblxuZnVuY3Rpb24gQnR3U2lkZVF1ZXN0aW9uKHtcbiAgcXVlc3Rpb24sXG4gIGNvbnRleHQsXG4gIG9uRG9uZSxcbn06IEJ0d0NvbXBvbmVudFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3Jlc3BvbnNlLCBzZXRSZXNwb25zZV0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICBjb25zdCBbZXJyb3IsIHNldEVycm9yXSA9IHVzZVN0YXRlPHN0cmluZyB8IG51bGw+KG51bGwpXG4gIGNvbnN0IFtmcmFtZSwgc2V0RnJhbWVdID0gdXNlU3RhdGUoMClcbiAgY29uc3Qgc2Nyb2xsUmVmID0gdXNlUmVmPFNjcm9sbEJveEhhbmRsZT4obnVsbClcbiAgY29uc3QgeyByb3dzIH0gPSB1c2VNb2RhbE9yVGVybWluYWxTaXplKHVzZVRlcm1pbmFsU2l6ZSgpKVxuXG4gIC8vIEFuaW1hdGUgc3Bpbm5lciB3aGlsZSBsb2FkaW5nXG4gIHVzZUludGVydmFsKCgpID0+IHNldEZyYW1lKGYgPT4gZiArIDEpLCByZXNwb25zZSB8fCBlcnJvciA/IG51bGwgOiA4MClcblxuICBmdW5jdGlvbiBoYW5kbGVLZXlEb3duKGU6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoXG4gICAgICBlLmtleSA9PT0gJ2VzY2FwZScgfHxcbiAgICAgIGUua2V5ID09PSAncmV0dXJuJyB8fFxuICAgICAgZS5rZXkgPT09ICcgJyB8fFxuICAgICAgKGUuY3RybCAmJiAoZS5rZXkgPT09ICdjJyB8fCBlLmtleSA9PT0gJ2QnKSlcbiAgICApIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25Eb25lKHVuZGVmaW5lZCwgeyBkaXNwbGF5OiAnc2tpcCcgfSlcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBpZiAoZS5rZXkgPT09ICd1cCcgfHwgKGUuY3RybCAmJiBlLmtleSA9PT0gJ3AnKSkge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBzY3JvbGxSZWYuY3VycmVudD8uc2Nyb2xsQnkoLVNDUk9MTF9MSU5FUylcbiAgICB9XG4gICAgaWYgKGUua2V5ID09PSAnZG93bicgfHwgKGUuY3RybCAmJiBlLmtleSA9PT0gJ24nKSkge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBzY3JvbGxSZWYuY3VycmVudD8uc2Nyb2xsQnkoU0NST0xMX0xJTkVTKVxuICAgIH1cbiAgfVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc3QgYWJvcnRDb250cm9sbGVyID0gY3JlYXRlQWJvcnRDb250cm9sbGVyKClcblxuICAgIGFzeW5jIGZ1bmN0aW9uIGZldGNoUmVzcG9uc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBjYWNoZVNhZmVQYXJhbXMgPSBhd2FpdCBidWlsZENhY2hlU2FmZVBhcmFtcyhjb250ZXh0KVxuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBydW5TaWRlUXVlc3Rpb24oeyBxdWVzdGlvbiwgY2FjaGVTYWZlUGFyYW1zIH0pXG5cbiAgICAgICAgaWYgKCFhYm9ydENvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHtcbiAgICAgICAgICBpZiAocmVzdWx0LnJlc3BvbnNlKSB7XG4gICAgICAgICAgICBzZXRSZXNwb25zZShyZXN1bHQucmVzcG9uc2UpXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHNldEVycm9yKCdObyByZXNwb25zZSByZWNlaXZlZCcpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKCFhYm9ydENvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHtcbiAgICAgICAgICBzZXRFcnJvcihlcnJvck1lc3NhZ2UoZXJyKSB8fCAnRmFpbGVkIHRvIGdldCByZXNwb25zZScpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICB2b2lkIGZldGNoUmVzcG9uc2UoKVxuXG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGFib3J0Q29udHJvbGxlci5hYm9ydCgpXG4gICAgfVxuICB9LCBbcXVlc3Rpb24sIGNvbnRleHRdKVxuXG4gIGNvbnN0IG1heENvbnRlbnRIZWlnaHQgPSBNYXRoLm1heCg1LCByb3dzIC0gQ0hST01FX1JPV1MgLSBPVVRFUl9DSFJPTUVfUk9XUylcblxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgcGFkZGluZ0xlZnQ9ezJ9XG4gICAgICBtYXJnaW5Ub3A9ezF9XG4gICAgICB0YWJJbmRleD17MH1cbiAgICAgIGF1dG9Gb2N1c1xuICAgICAgb25LZXlEb3duPXtoYW5kbGVLZXlEb3dufVxuICAgID5cbiAgICAgIDxCb3g+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiIGJvbGQ+XG4gICAgICAgICAgL2J0d3snICd9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+e3F1ZXN0aW9ufTwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9IG1hcmdpbkxlZnQ9ezJ9IG1heEhlaWdodD17bWF4Q29udGVudEhlaWdodH0+XG4gICAgICAgIDxTY3JvbGxCb3ggcmVmPXtzY3JvbGxSZWZ9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBmbGV4R3Jvdz17MX0+XG4gICAgICAgICAge2Vycm9yID8gKFxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPntlcnJvcn08L1RleHQ+XG4gICAgICAgICAgKSA6IHJlc3BvbnNlID8gKFxuICAgICAgICAgICAgPE1hcmtkb3duPntyZXNwb25zZX08L01hcmtkb3duPlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgICA8U3Bpbm5lckdseXBoIGZyYW1lPXtmcmFtZX0gbWVzc2FnZUNvbG9yPVwid2FybmluZ1wiIC8+XG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPkFuc3dlcmluZy4uLjwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICl9XG4gICAgICAgIDwvU2Nyb2xsQm94PlxuICAgICAgPC9Cb3g+XG4gICAgICB7KHJlc3BvbnNlIHx8IGVycm9yKSAmJiAoXG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHtVUF9BUlJPV30ve0RPV05fQVJST1d9IHRvIHNjcm9sbCDCtyBTcGFjZSwgRW50ZXIsIG9yIEVzY2FwZSB0b1xuICAgICAgICAgICAgZGlzbWlzc1xuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuICAgIDwvQm94PlxuICApXG59XG5cbi8qKlxuICogQnVpbGQgQ2FjaGVTYWZlUGFyYW1zIGZvciB0aGUgc2lkZSBxdWVzdGlvbiBmb3JrLlxuICpcbiAqIFRoZSBwcmVmZXJyZWQgc291cmNlIGlzIGdldExhc3RDYWNoZVNhZmVQYXJhbXMg4oCUIHRoZSBleGFjdFxuICogc3lzdGVtUHJvbXB0L3VzZXJDb250ZXh0L3N5c3RlbUNvbnRleHQgYnl0ZXMgdGhlIG1haW4gdGhyZWFkIHNlbnQgb24gaXRzXG4gKiBsYXN0IHJlcXVlc3QgKGNhcHR1cmVkIGluIHN0b3BIb29rcykuIFJldXNpbmcgdGhlbSBndWFyYW50ZWVzIGEgYnl0ZS1cbiAqIGlkZW50aWNhbCBwcmVmaXggYW5kIHRodXMgYSBwcm9tcHQgY2FjaGUgaGl0LiBXZSBwYWlyIHRoZXNlIHdpdGggdGhlXG4gKiBjdXJyZW50IHRvb2xVc2VDb250ZXh0IChmb3IgdGhpbmtpbmdDb25maWcvdG9vbHMpIGFuZCBjdXJyZW50IG1lc3NhZ2VzXG4gKiAoZm9yIHVwLXRvLWRhdGUgY29udGV4dCkuXG4gKlxuICogRmFsbGJhY2sgKGZpcnN0IHR1cm4gYmVmb3JlIHN0b3AgaG9va3MgZmlyZSwgb3IgcHJvbXB0LXN1Z2dlc3Rpb25cbiAqIGRpc2FibGVkKTogcmVidWlsZCBmcm9tIHNjcmF0Y2guIFRoaXMgbWF5IG1pc3MgdGhlIGNhY2hlIGlmIHRoZSBtYWluIGxvb3BcbiAqIGFwcGxpZWQgYnVpbGRFZmZlY3RpdmVTeXN0ZW1Qcm9tcHQgZXh0cmFzICgtLWFnZW50LCAtLXN5c3RlbS1wcm9tcHQsXG4gKiAtLWFwcGVuZC1zeXN0ZW0tcHJvbXB0LCBjb29yZGluYXRvciBtb2RlKS5cbiAqL1xuZnVuY3Rpb24gc3RyaXBJblByb2dyZXNzQXNzaXN0YW50TWVzc2FnZShtZXNzYWdlczogTWVzc2FnZVtdKTogTWVzc2FnZVtdIHtcbiAgY29uc3QgbGFzdCA9IG1lc3NhZ2VzLmF0KC0xKVxuICBpZiAobGFzdD8udHlwZSA9PT0gJ2Fzc2lzdGFudCcgJiYgbGFzdC5tZXNzYWdlLnN0b3BfcmVhc29uID09PSBudWxsKSB7XG4gICAgcmV0dXJuIG1lc3NhZ2VzLnNsaWNlKDAsIC0xKVxuICB9XG4gIHJldHVybiBtZXNzYWdlc1xufVxuXG5hc3luYyBmdW5jdGlvbiBidWlsZENhY2hlU2FmZVBhcmFtcyhcbiAgY29udGV4dDogUHJvY2Vzc1VzZXJJbnB1dENvbnRleHQsXG4pOiBQcm9taXNlPENhY2hlU2FmZVBhcmFtcz4ge1xuICBjb25zdCBmb3JrQ29udGV4dE1lc3NhZ2VzID0gZ2V0TWVzc2FnZXNBZnRlckNvbXBhY3RCb3VuZGFyeShcbiAgICBzdHJpcEluUHJvZ3Jlc3NBc3Npc3RhbnRNZXNzYWdlKGNvbnRleHQubWVzc2FnZXMpLFxuICApXG4gIGNvbnN0IHNhdmVkID0gZ2V0TGFzdENhY2hlU2FmZVBhcmFtcygpXG4gIGlmIChzYXZlZCkge1xuICAgIHJldHVybiB7XG4gICAgICBzeXN0ZW1Qcm9tcHQ6IHNhdmVkLnN5c3RlbVByb21wdCxcbiAgICAgIHVzZXJDb250ZXh0OiBzYXZlZC51c2VyQ29udGV4dCxcbiAgICAgIHN5c3RlbUNvbnRleHQ6IHNhdmVkLnN5c3RlbUNvbnRleHQsXG4gICAgICB0b29sVXNlQ29udGV4dDogY29udGV4dCxcbiAgICAgIGZvcmtDb250ZXh0TWVzc2FnZXMsXG4gICAgfVxuICB9XG4gIGNvbnN0IFtyYXdTeXN0ZW1Qcm9tcHQsIHVzZXJDb250ZXh0LCBzeXN0ZW1Db250ZXh0XSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICBnZXRTeXN0ZW1Qcm9tcHQoXG4gICAgICBjb250ZXh0Lm9wdGlvbnMudG9vbHMsXG4gICAgICBjb250ZXh0Lm9wdGlvbnMubWFpbkxvb3BNb2RlbCxcbiAgICAgIFtdLFxuICAgICAgY29udGV4dC5vcHRpb25zLm1jcENsaWVudHMsXG4gICAgKSxcbiAgICBnZXRVc2VyQ29udGV4dCgpLFxuICAgIGdldFN5c3RlbUNvbnRleHQoKSxcbiAgXSlcbiAgcmV0dXJuIHtcbiAgICBzeXN0ZW1Qcm9tcHQ6IGFzU3lzdGVtUHJvbXB0KHJhd1N5c3RlbVByb21wdCksXG4gICAgdXNlckNvbnRleHQsXG4gICAgc3lzdGVtQ29udGV4dCxcbiAgICB0b29sVXNlQ29udGV4dDogY29udGV4dCxcbiAgICBmb3JrQ29udGV4dE1lc3NhZ2VzLFxuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogUHJvY2Vzc1VzZXJJbnB1dENvbnRleHQsXG4gIGFyZ3M6IHN0cmluZyxcbik6IFByb21pc2U8UmVhY3QuUmVhY3ROb2RlPiB7XG4gIGNvbnN0IHF1ZXN0aW9uID0gYXJncz8udHJpbSgpXG5cbiAgaWYgKCFxdWVzdGlvbikge1xuICAgIG9uRG9uZSgnVXNhZ2U6IC9idHcgPHlvdXIgcXVlc3Rpb24+JywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBzYXZlR2xvYmFsQ29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAuLi5jdXJyZW50LFxuICAgIGJ0d1VzZUNvdW50OiBjdXJyZW50LmJ0d1VzZUNvdW50ICsgMSxcbiAgfSkpXG5cbiAgcmV0dXJuIChcbiAgICA8QnR3U2lkZVF1ZXN0aW9uIHF1ZXN0aW9uPXtxdWVzdGlvbn0gY29udGV4dD17Y29udGV4dH0gb25Eb25lPXtvbkRvbmV9IC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsU0FBUyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ25ELFNBQVNDLFdBQVcsUUFBUSxhQUFhO0FBQ3pDLGNBQWNDLG9CQUFvQixRQUFRLG1CQUFtQjtBQUM3RCxTQUFTQyxRQUFRLFFBQVEsOEJBQThCO0FBQ3ZELFNBQVNDLFlBQVksUUFBUSwwQ0FBMEM7QUFDdkUsU0FBU0MsVUFBVSxFQUFFQyxRQUFRLFFBQVEsNEJBQTRCO0FBQ2pFLFNBQVNDLGVBQWUsUUFBUSw0QkFBNEI7QUFDNUQsU0FBU0Msc0JBQXNCLFFBQVEsK0JBQStCO0FBQ3RFLFNBQVNDLGdCQUFnQixFQUFFQyxjQUFjLFFBQVEsa0JBQWtCO0FBQ25FLFNBQVNDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDaEUsT0FBT0MsU0FBUyxJQUNkLEtBQUtDLGVBQWUsUUFDZixtQ0FBbUM7QUFDMUMsY0FBY0MsYUFBYSxRQUFRLG9DQUFvQztBQUN2RSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLGNBQWNDLHFCQUFxQixRQUFRLHdCQUF3QjtBQUNuRSxjQUFjQyxPQUFPLFFBQVEsd0JBQXdCO0FBQ3JELFNBQVNDLHFCQUFxQixRQUFRLGdDQUFnQztBQUN0RSxTQUFTQyxnQkFBZ0IsUUFBUSx1QkFBdUI7QUFDeEQsU0FBU0MsWUFBWSxRQUFRLHVCQUF1QjtBQUNwRCxTQUNFLEtBQUtDLGVBQWUsRUFDcEJDLHNCQUFzQixRQUNqQiw0QkFBNEI7QUFDbkMsU0FBU0MsK0JBQStCLFFBQVEseUJBQXlCO0FBQ3pFLGNBQWNDLHVCQUF1QixRQUFRLGtEQUFrRDtBQUMvRixTQUFTQyxlQUFlLFFBQVEsNkJBQTZCO0FBQzdELFNBQVNDLGNBQWMsUUFBUSxpQ0FBaUM7QUFFaEUsS0FBS0MsaUJBQWlCLEdBQUc7RUFDdkJDLFFBQVEsRUFBRSxNQUFNO0VBQ2hCQyxPQUFPLEVBQUVMLHVCQUF1QjtFQUNoQ00sTUFBTSxFQUFFLENBQ05DLE1BQWUsQ0FBUixFQUFFLE1BQU0sRUFDZkMsT0FBNEMsQ0FBcEMsRUFBRTtJQUFFQyxPQUFPLENBQUMsRUFBRWhDLG9CQUFvQjtFQUFDLENBQUMsRUFDNUMsR0FBRyxJQUFJO0FBQ1gsQ0FBQztBQUVELE1BQU1pQyxXQUFXLEdBQUcsQ0FBQztBQUNyQixNQUFNQyxpQkFBaUIsR0FBRyxDQUFDO0FBQzNCLE1BQU1DLFlBQVksR0FBRyxDQUFDO0FBRXRCLFNBQUFDLGdCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXlCO0lBQUFaLFFBQUE7SUFBQUMsT0FBQTtJQUFBQztFQUFBLElBQUFRLEVBSUw7RUFDbEIsT0FBQUcsUUFBQSxFQUFBQyxXQUFBLElBQWdDM0MsUUFBUSxDQUFnQixJQUFJLENBQUM7RUFDN0QsT0FBQTRDLEtBQUEsRUFBQUMsUUFBQSxJQUEwQjdDLFFBQVEsQ0FBZ0IsSUFBSSxDQUFDO0VBQ3ZELE9BQUE4QyxLQUFBLEVBQUFDLFFBQUEsSUFBMEIvQyxRQUFRLENBQUMsQ0FBQyxDQUFDO0VBQ3JDLE1BQUFnRCxTQUFBLEdBQWtCakQsTUFBTSxDQUFrQixJQUFJLENBQUM7RUFDL0M7SUFBQWtEO0VBQUEsSUFBaUJ6QyxzQkFBc0IsQ0FBQ0csZUFBZSxDQUFDLENBQUMsQ0FBQztFQUFBLElBQUF1QyxFQUFBO0VBQUEsSUFBQVYsQ0FBQSxRQUFBVyxNQUFBLENBQUFDLEdBQUE7SUFHOUNGLEVBQUEsR0FBQUEsQ0FBQSxLQUFNSCxRQUFRLENBQUNNLEtBQVUsQ0FBQztJQUFBYixDQUFBLE1BQUFVLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFWLENBQUE7RUFBQTtFQUF0Q3ZDLFdBQVcsQ0FBQ2lELEVBQTBCLEVBQUVSLFFBQWlCLElBQWpCRSxLQUE2QixHQUE3QixJQUE2QixHQUE3QixFQUE2QixDQUFDO0VBQUEsSUFBQVUsRUFBQTtFQUFBLElBQUFkLENBQUEsUUFBQVQsTUFBQTtJQUV0RXVCLEVBQUEsWUFBQUMsY0FBQUMsQ0FBQTtNQUNFLElBQ0VBLENBQUMsQ0FBQUMsR0FBSSxLQUFLLFFBQ1EsSUFBbEJELENBQUMsQ0FBQUMsR0FBSSxLQUFLLFFBQ0csSUFBYkQsQ0FBQyxDQUFBQyxHQUFJLEtBQUssR0FDa0MsSUFBM0NELENBQUMsQ0FBQUUsSUFBeUMsS0FBL0JGLENBQUMsQ0FBQUMsR0FBSSxLQUFLLEdBQW9CLElBQWJELENBQUMsQ0FBQUMsR0FBSSxLQUFLLEdBQUksQ0FBQztRQUU1Q0QsQ0FBQyxDQUFBRyxjQUFlLENBQUMsQ0FBQztRQUNsQjVCLE1BQU0sQ0FBQzZCLFNBQVMsRUFBRTtVQUFBMUIsT0FBQSxFQUFXO1FBQU8sQ0FBQyxDQUFDO1FBQUE7TUFBQTtNQUd4QyxJQUFJc0IsQ0FBQyxDQUFBQyxHQUFJLEtBQUssSUFBaUMsSUFBeEJELENBQUMsQ0FBQUUsSUFBc0IsSUFBYkYsQ0FBQyxDQUFBQyxHQUFJLEtBQUssR0FBSTtRQUM3Q0QsQ0FBQyxDQUFBRyxjQUFlLENBQUMsQ0FBQztRQUNsQlgsU0FBUyxDQUFBYSxPQUFrQixFQUFBQyxRQUFlLENBQWQsQ0FBQ3pCLFlBQVksQ0FBQztNQUFBO01BRTVDLElBQUltQixDQUFDLENBQUFDLEdBQUksS0FBSyxNQUFtQyxJQUF4QkQsQ0FBQyxDQUFBRSxJQUFzQixJQUFiRixDQUFDLENBQUFDLEdBQUksS0FBSyxHQUFJO1FBQy9DRCxDQUFDLENBQUFHLGNBQWUsQ0FBQyxDQUFDO1FBQ2xCWCxTQUFTLENBQUFhLE9BQWtCLEVBQUFDLFFBQWMsQ0FBYnpCLFlBQVksQ0FBQztNQUFBO0lBQzFDLENBQ0Y7SUFBQUcsQ0FBQSxNQUFBVCxNQUFBO0lBQUFTLENBQUEsTUFBQWMsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQWQsQ0FBQTtFQUFBO0VBbkJELE1BQUFlLGFBQUEsR0FBQUQsRUFtQkM7RUFBQSxJQUFBUyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUF4QixDQUFBLFFBQUFWLE9BQUEsSUFBQVUsQ0FBQSxRQUFBWCxRQUFBO0lBRVNrQyxFQUFBLEdBQUFBLENBQUE7TUFDUixNQUFBRSxlQUFBLEdBQXdCOUMscUJBQXFCLENBQUMsQ0FBQztNQUUvQyxNQUFBK0MsYUFBQSxrQkFBQUEsY0FBQTtRQUFBO1FBQ0U7VUFDRSxNQUFBQyxlQUFBLEdBQXdCLE1BQU1DLG9CQUFvQixDQUFDdEMsT0FBTyxDQUFDO1VBQzNELE1BQUFFLE1BQUEsR0FBZSxNQUFNTixlQUFlLENBQUM7WUFBQUcsUUFBQTtZQUFBc0M7VUFBNEIsQ0FBQyxDQUFDO1VBRW5FLElBQUksQ0FBQ0YsZUFBZSxDQUFBSSxNQUFPLENBQUFDLE9BQVE7WUFDakMsSUFBSXRDLE1BQU0sQ0FBQVUsUUFBUztjQUNqQkMsV0FBVyxDQUFDWCxNQUFNLENBQUFVLFFBQVMsQ0FBQztZQUFBO2NBRTVCRyxRQUFRLENBQUMsc0JBQXNCLENBQUM7WUFBQTtVQUNqQztRQUNGLFNBQUEwQixFQUFBO1VBQ01DLEtBQUEsQ0FBQUEsR0FBQSxDQUFBQSxDQUFBLENBQUFBLEVBQUc7VUFDVixJQUFJLENBQUNQLGVBQWUsQ0FBQUksTUFBTyxDQUFBQyxPQUFRO1lBQ2pDekIsUUFBUSxDQUFDeEIsWUFBWSxDQUFDbUQsR0FBK0IsQ0FBQyxJQUE3Qyx3QkFBNkMsQ0FBQztVQUFBO1FBQ3hEO01BQ0YsQ0FDRjtNQUVJTixhQUFhLENBQUMsQ0FBQztNQUFBLE9BRWI7UUFDTEQsZUFBZSxDQUFBUSxLQUFNLENBQUMsQ0FBQztNQUFBLENBQ3hCO0lBQUEsQ0FDRjtJQUFFVCxFQUFBLElBQUNuQyxRQUFRLEVBQUVDLE9BQU8sQ0FBQztJQUFBVSxDQUFBLE1BQUFWLE9BQUE7SUFBQVUsQ0FBQSxNQUFBWCxRQUFBO0lBQUFXLENBQUEsTUFBQXVCLEVBQUE7SUFBQXZCLENBQUEsTUFBQXdCLEVBQUE7RUFBQTtJQUFBRCxFQUFBLEdBQUF2QixDQUFBO0lBQUF3QixFQUFBLEdBQUF4QixDQUFBO0VBQUE7RUEzQnRCMUMsU0FBUyxDQUFDaUUsRUEyQlQsRUFBRUMsRUFBbUIsQ0FBQztFQUV2QixNQUFBVSxnQkFBQSxHQUF5QkMsSUFBSSxDQUFBQyxHQUFJLENBQUMsQ0FBQyxFQUFFM0IsSUFBSSxHQUFHZCxXQUFXLEdBQUdDLGlCQUFpQixDQUFDO0VBQUEsSUFBQW1DLEVBQUE7RUFBQSxJQUFBL0IsQ0FBQSxRQUFBVyxNQUFBLENBQUFDLEdBQUE7SUFZdEVtQixFQUFBLElBQUMsSUFBSSxDQUFPLEtBQVMsQ0FBVCxTQUFTLENBQUMsSUFBSSxDQUFKLEtBQUcsQ0FBQyxDQUFDLElBQ3BCLElBQUUsQ0FDVCxFQUZDLElBQUksQ0FFRTtJQUFBL0IsQ0FBQSxNQUFBK0IsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQS9CLENBQUE7RUFBQTtFQUFBLElBQUFxQyxFQUFBO0VBQUEsSUFBQXJDLENBQUEsUUFBQVgsUUFBQTtJQUhUZ0QsRUFBQSxJQUFDLEdBQUcsQ0FDRixDQUFBTixFQUVNLENBQ04sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFFMUMsU0FBTyxDQUFFLEVBQXhCLElBQUksQ0FDUCxFQUxDLEdBQUcsQ0FLRTtJQUFBVyxDQUFBLE1BQUFYLFFBQUE7SUFBQVcsQ0FBQSxNQUFBcUMsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQXJDLENBQUE7RUFBQTtFQUFBLElBQUFzQyxFQUFBO0VBQUEsSUFBQXRDLENBQUEsU0FBQUksS0FBQSxJQUFBSixDQUFBLFNBQUFNLEtBQUEsSUFBQU4sQ0FBQSxTQUFBRSxRQUFBO0lBRUpvQyxFQUFBLElBQUMsU0FBUyxDQUFNOUIsR0FBUyxDQUFUQSxVQUFRLENBQUMsQ0FBZ0IsYUFBUSxDQUFSLFFBQVEsQ0FBVyxRQUFDLENBQUQsR0FBQyxDQUMxRCxDQUFBSixLQUFLLEdBQ0osQ0FBQyxJQUFJLENBQU8sS0FBTyxDQUFQLE9BQU8sQ0FBRUEsTUFBSSxDQUFFLEVBQTFCLElBQUksQ0FRTixHQVBHRixRQUFRLEdBQ1YsQ0FBQyxRQUFRLENBQUVBLFNBQU8sQ0FBRSxFQUFuQixRQUFRLENBTVYsR0FKQyxDQUFDLEdBQUcsQ0FDRixDQUFDLFlBQVksQ0FBUUksS0FBSyxDQUFMQSxNQUFJLENBQUMsQ0FBZSxZQUFTLENBQVQsU0FBUyxHQUNsRCxDQUFDLElBQUksQ0FBTyxLQUFTLENBQVQsU0FBUyxDQUFDLFlBQVksRUFBakMsSUFBSSxDQUNQLEVBSEMsR0FBRyxDQUlOLENBQ0YsRUFYQyxTQUFTLENBV0U7SUFBQU4sQ0FBQSxPQUFBSSxLQUFBO0lBQUFKLENBQUEsT0FBQU0sS0FBQTtJQUFBTixDQUFBLE9BQUFFLFFBQUE7SUFBQUYsQ0FBQSxPQUFBc0MsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQXRDLENBQUE7RUFBQTtFQUFBLElBQUF1QyxFQUFBO0VBQUEsSUFBQXZDLENBQUEsU0FBQWtDLGdCQUFBLElBQUFsQyxDQUFBLFNBQUFzQyxFQUFBO0lBWmRDLEVBQUEsSUFBQyxHQUFHLENBQVksU0FBQyxDQUFELEdBQUMsQ0FBYyxVQUFDLENBQUQsR0FBQyxDQUFhTCxTQUFnQixDQUFoQkEsaUJBQWUsQ0FBQyxDQUMzRCxDQUFBSSxFQVdXLENBQ2IsRUFiQyxHQUFHLENBYUU7SUFBQXRDLENBQUEsT0FBQWtDLGdCQUFBO0lBQUFsQyxDQUFBLE9BQUFzQyxFQUFBO0lBQUF0QyxDQUFBLE9BQUF1QyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBdkMsQ0FBQTtFQUFBO0VBQUEsSUFBQXdDLEVBQUE7RUFBQSxJQUFBeEMsQ0FBQSxTQUFBSSxLQUFBLElBQUFKLENBQUEsU0FBQUUsUUFBQTtJQUNMc0MsRUFBQSxJQUFDdEMsUUFBaUIsSUFBakJFLEtBT0QsS0FOQyxDQUFDLEdBQUcsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUNmLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FDWHRDLFNBQU8sQ0FBRSxDQUFFRCxXQUFTLENBQUUsK0NBRXpCLEVBSEMsSUFBSSxDQUlQLEVBTEMsR0FBRyxDQU1MO0lBQUFtQyxDQUFBLE9BQUFJLEtBQUE7SUFBQUosQ0FBQSxPQUFBRSxRQUFBO0lBQUFGLENBQUEsT0FBQXdDLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUF4QyxDQUFBO0VBQUE7RUFBQSxJQUFBeUMsR0FBQTtFQUFBLElBQUF6QyxDQUFBLFNBQUFlLGFBQUEsSUFBQWYsQ0FBQSxTQUFBcUMsRUFBQSxJQUFBckMsQ0FBQSxTQUFBdUMsRUFBQSxJQUFBdkMsQ0FBQSxTQUFBd0MsRUFBQTtJQW5DSEMsR0FBQSxJQUFDLEdBQUcsQ0FDWSxhQUFRLENBQVIsUUFBUSxDQUNULFdBQUMsQ0FBRCxHQUFDLENBQ0gsU0FBQyxDQUFELEdBQUMsQ0FDRixRQUFDLENBQUQsR0FBQyxDQUNYLFNBQVMsQ0FBVCxLQUFRLENBQUMsQ0FDRTFCLFNBQWEsQ0FBYkEsY0FBWSxDQUFDLENBRXhCLENBQUFzQixFQUtLLENBQ0wsQ0FBQUUsRUFhSyxDQUNKLENBQUFDLEVBT0QsQ0FDRixFQXBDQyxHQUFHLENBb0NFO0lBQUF4QyxDQUFBLE9BQUFlLGFBQUE7SUFBQWYsQ0FBQSxPQUFBcUMsRUFBQTtJQUFBckMsQ0FBQSxPQUFBdUMsRUFBQTtJQUFBdkMsQ0FBQSxPQUFBd0MsRUFBQTtJQUFBeEMsQ0FBQSxPQUFBeUMsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQXpDLENBQUE7RUFBQTtFQUFBLE9BcENOeUMsR0FvQ007QUFBQTs7QUFJVjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUF6SEEsU0FBQTVCLE1BQUE2QixDQUFBO0VBQUEsT0FZa0NBLENBQUMsR0FBRyxDQUFDO0FBQUE7QUE4R3ZDLFNBQVNDLCtCQUErQkEsQ0FBQ0MsUUFBUSxFQUFFbEUsT0FBTyxFQUFFLENBQUMsRUFBRUEsT0FBTyxFQUFFLENBQUM7RUFDdkUsTUFBTW1FLElBQUksR0FBR0QsUUFBUSxDQUFDRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7RUFDNUIsSUFBSUQsSUFBSSxFQUFFRSxJQUFJLEtBQUssV0FBVyxJQUFJRixJQUFJLENBQUNHLE9BQU8sQ0FBQ0MsV0FBVyxLQUFLLElBQUksRUFBRTtJQUNuRSxPQUFPTCxRQUFRLENBQUNNLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7RUFDOUI7RUFDQSxPQUFPTixRQUFRO0FBQ2pCO0FBRUEsZUFBZWhCLG9CQUFvQkEsQ0FDakN0QyxPQUFPLEVBQUVMLHVCQUF1QixDQUNqQyxFQUFFa0UsT0FBTyxDQUFDckUsZUFBZSxDQUFDLENBQUM7RUFDMUIsTUFBTXNFLG1CQUFtQixHQUFHcEUsK0JBQStCLENBQ3pEMkQsK0JBQStCLENBQUNyRCxPQUFPLENBQUNzRCxRQUFRLENBQ2xELENBQUM7RUFDRCxNQUFNUyxLQUFLLEdBQUd0RSxzQkFBc0IsQ0FBQyxDQUFDO0VBQ3RDLElBQUlzRSxLQUFLLEVBQUU7SUFDVCxPQUFPO01BQ0xDLFlBQVksRUFBRUQsS0FBSyxDQUFDQyxZQUFZO01BQ2hDQyxXQUFXLEVBQUVGLEtBQUssQ0FBQ0UsV0FBVztNQUM5QkMsYUFBYSxFQUFFSCxLQUFLLENBQUNHLGFBQWE7TUFDbENDLGNBQWMsRUFBRW5FLE9BQU87TUFDdkI4RDtJQUNGLENBQUM7RUFDSDtFQUNBLE1BQU0sQ0FBQ00sZUFBZSxFQUFFSCxXQUFXLEVBQUVDLGFBQWEsQ0FBQyxHQUFHLE1BQU1MLE9BQU8sQ0FBQ1EsR0FBRyxDQUFDLENBQ3RFNUYsZUFBZSxDQUNidUIsT0FBTyxDQUFDRyxPQUFPLENBQUNtRSxLQUFLLEVBQ3JCdEUsT0FBTyxDQUFDRyxPQUFPLENBQUNvRSxhQUFhLEVBQzdCLEVBQUUsRUFDRnZFLE9BQU8sQ0FBQ0csT0FBTyxDQUFDcUUsVUFDbEIsQ0FBQyxFQUNENUYsY0FBYyxDQUFDLENBQUMsRUFDaEJELGdCQUFnQixDQUFDLENBQUMsQ0FDbkIsQ0FBQztFQUNGLE9BQU87SUFDTHFGLFlBQVksRUFBRW5FLGNBQWMsQ0FBQ3VFLGVBQWUsQ0FBQztJQUM3Q0gsV0FBVztJQUNYQyxhQUFhO0lBQ2JDLGNBQWMsRUFBRW5FLE9BQU87SUFDdkI4RDtFQUNGLENBQUM7QUFDSDtBQUVBLE9BQU8sZUFBZVcsSUFBSUEsQ0FDeEJ4RSxNQUFNLEVBQUVkLHFCQUFxQixFQUM3QmEsT0FBTyxFQUFFTCx1QkFBdUIsRUFDaEMrRSxJQUFJLEVBQUUsTUFBTSxDQUNiLEVBQUViLE9BQU8sQ0FBQzlGLEtBQUssQ0FBQzRHLFNBQVMsQ0FBQyxDQUFDO0VBQzFCLE1BQU01RSxRQUFRLEdBQUcyRSxJQUFJLEVBQUVFLElBQUksQ0FBQyxDQUFDO0VBRTdCLElBQUksQ0FBQzdFLFFBQVEsRUFBRTtJQUNiRSxNQUFNLENBQUMsNkJBQTZCLEVBQUU7TUFBRUcsT0FBTyxFQUFFO0lBQVMsQ0FBQyxDQUFDO0lBQzVELE9BQU8sSUFBSTtFQUNiO0VBRUFkLGdCQUFnQixDQUFDeUMsT0FBTyxLQUFLO0lBQzNCLEdBQUdBLE9BQU87SUFDVjhDLFdBQVcsRUFBRTlDLE9BQU8sQ0FBQzhDLFdBQVcsR0FBRztFQUNyQyxDQUFDLENBQUMsQ0FBQztFQUVILE9BQ0UsQ0FBQyxlQUFlLENBQUMsUUFBUSxDQUFDLENBQUM5RSxRQUFRLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQ0MsT0FBTyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUNDLE1BQU0sQ0FBQyxHQUFHO0FBRTdFIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/btw/index.ts b/commands/btw/index.ts new file mode 100644 index 0000000..d488871 --- /dev/null +++ b/commands/btw/index.ts @@ -0,0 +1,13 @@ +import type { Command } from '../../commands.js' + +const btw = { + type: 'local-jsx', + name: 'btw', + description: + 'Ask a quick side question without interrupting the main conversation', + immediate: true, + argumentHint: '', + load: () => import('./btw.js'), +} satisfies Command + +export default btw diff --git a/commands/bughunter/index.js b/commands/bughunter/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/bughunter/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/chrome/chrome.tsx b/commands/chrome/chrome.tsx new file mode 100644 index 0000000..c337873 --- /dev/null +++ b/commands/chrome/chrome.tsx @@ -0,0 +1,285 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useState } from 'react'; +import { type OptionWithDescription, Select } from '../../components/CustomSelect/select.js'; +import { Dialog } from '../../components/design-system/Dialog.js'; +import { Box, Text } from '../../ink.js'; +import { useAppState } from '../../state/AppState.js'; +import { isClaudeAISubscriber } from '../../utils/auth.js'; +import { openBrowser } from '../../utils/browser.js'; +import { CLAUDE_IN_CHROME_MCP_SERVER_NAME, openInChrome } from '../../utils/claudeInChrome/common.js'; +import { isChromeExtensionInstalled } from '../../utils/claudeInChrome/setup.js'; +import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'; +import { env } from '../../utils/env.js'; +import { isRunningOnHomespace } from '../../utils/envUtils.js'; +const CHROME_EXTENSION_URL = 'https://claude.ai/chrome'; +const CHROME_PERMISSIONS_URL = 'https://clau.de/chrome/permissions'; +const CHROME_RECONNECT_URL = 'https://clau.de/chrome/reconnect'; +type MenuAction = 'install-extension' | 'reconnect' | 'manage-permissions' | 'toggle-default'; +type Props = { + onDone: (result?: string) => void; + isExtensionInstalled: boolean; + configEnabled: boolean | undefined; + isClaudeAISubscriber: boolean; + isWSL: boolean; +}; +function ClaudeInChromeMenu(t0) { + const $ = _c(41); + const { + onDone, + isExtensionInstalled: installed, + configEnabled, + isClaudeAISubscriber, + isWSL + } = t0; + const mcpClients = useAppState(_temp); + const [selectKey, setSelectKey] = useState(0); + const [enabledByDefault, setEnabledByDefault] = useState(configEnabled ?? false); + const [showInstallHint, setShowInstallHint] = useState(false); + const [isExtensionInstalled, setIsExtensionInstalled] = useState(installed); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = false && isRunningOnHomespace(); + $[0] = t1; + } else { + t1 = $[0]; + } + const isHomespace = t1; + let t2; + if ($[1] !== mcpClients) { + t2 = mcpClients.find(_temp2); + $[1] = mcpClients; + $[2] = t2; + } else { + t2 = $[2]; + } + const chromeClient = t2; + const isConnected = chromeClient?.type === "connected"; + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = function openUrl(url) { + if (isHomespace) { + openBrowser(url); + } else { + openInChrome(url); + } + }; + $[3] = t3; + } else { + t3 = $[3]; + } + const openUrl = t3; + let t4; + if ($[4] !== enabledByDefault) { + t4 = function handleAction(action) { + bb22: switch (action) { + case "install-extension": + { + setSelectKey(_temp3); + setShowInstallHint(true); + openUrl(CHROME_EXTENSION_URL); + break bb22; + } + case "reconnect": + { + setSelectKey(_temp4); + isChromeExtensionInstalled().then(installed_0 => { + setIsExtensionInstalled(installed_0); + if (installed_0) { + setShowInstallHint(false); + } + }); + openUrl(CHROME_RECONNECT_URL); + break bb22; + } + case "manage-permissions": + { + setSelectKey(_temp5); + openUrl(CHROME_PERMISSIONS_URL); + break bb22; + } + case "toggle-default": + { + const newValue = !enabledByDefault; + saveGlobalConfig(current => ({ + ...current, + claudeInChromeDefaultEnabled: newValue + })); + setEnabledByDefault(newValue); + } + } + }; + $[4] = enabledByDefault; + $[5] = t4; + } else { + t4 = $[5]; + } + const handleAction = t4; + let options; + if ($[6] !== enabledByDefault || $[7] !== isExtensionInstalled) { + options = []; + const requiresExtensionSuffix = isExtensionInstalled ? "" : " (requires extension)"; + if (!isExtensionInstalled && !isHomespace) { + let t5; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + label: "Install Chrome extension", + value: "install-extension" + }; + $[9] = t5; + } else { + t5 = $[9]; + } + options.push(t5); + } + let t5; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t5 = Manage permissions; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] !== requiresExtensionSuffix) { + t6 = { + label: <>{t5}{requiresExtensionSuffix}, + value: "manage-permissions" + }; + $[11] = requiresExtensionSuffix; + $[12] = t6; + } else { + t6 = $[12]; + } + let t7; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t7 = Reconnect extension; + $[13] = t7; + } else { + t7 = $[13]; + } + let t8; + if ($[14] !== requiresExtensionSuffix) { + t8 = { + label: <>{t7}{requiresExtensionSuffix}, + value: "reconnect" + }; + $[14] = requiresExtensionSuffix; + $[15] = t8; + } else { + t8 = $[15]; + } + const t9 = `Enabled by default: ${enabledByDefault ? "Yes" : "No"}`; + let t10; + if ($[16] !== t9) { + t10 = { + label: t9, + value: "toggle-default" + }; + $[16] = t9; + $[17] = t10; + } else { + t10 = $[17]; + } + options.push(t6, t8, t10); + $[6] = enabledByDefault; + $[7] = isExtensionInstalled; + $[8] = options; + } else { + options = $[8]; + } + const isDisabled = isWSL || true && !isClaudeAISubscriber; + let t5; + if ($[18] !== onDone) { + t5 = () => onDone(); + $[18] = onDone; + $[19] = t5; + } else { + t5 = $[19]; + } + let t6; + if ($[20] === Symbol.for("react.memo_cache_sentinel")) { + t6 = Claude in Chrome works with the Chrome extension to let you control your browser directly from Claude Code. Navigate websites, fill forms, capture screenshots, record GIFs, and debug with console logs and network requests.; + $[20] = t6; + } else { + t6 = $[20]; + } + let t7; + if ($[21] !== isWSL) { + t7 = isWSL && Claude in Chrome is not supported in WSL at this time.; + $[21] = isWSL; + $[22] = t7; + } else { + t7 = $[22]; + } + let t8; + if ($[23] !== isClaudeAISubscriber) { + t8 = true && !isClaudeAISubscriber && Claude in Chrome requires a claude.ai subscription.; + $[23] = isClaudeAISubscriber; + $[24] = t8; + } else { + t8 = $[24]; + } + let t9; + if ($[25] !== handleAction || $[26] !== isConnected || $[27] !== isDisabled || $[28] !== isExtensionInstalled || $[29] !== options || $[30] !== selectKey || $[31] !== showInstallHint) { + t9 = !isDisabled && <>{!isHomespace && Status:{" "}{isConnected ? Enabled : Disabled}Extension:{" "}{isExtensionInstalled ? Installed : Not detected}}; + $[25] = options; + $[26] = t10; + $[27] = t9; + $[28] = t11; + } else { + t11 = $[28]; + } + let t12; + if ($[29] === Symbol.for("react.memo_cache_sentinel")) { + t12 = ; + $[29] = t12; + } else { + t12 = $[29]; + } + let t13; + if ($[30] !== handleKeyDown || $[31] !== t11) { + t13 = {t7}{t11}{t12}; + $[30] = handleKeyDown; + $[31] = t11; + $[32] = t13; + } else { + t13 = $[32]; + } + return t13; +} +function _temp2(c) { + return { + ...c, + copyFullResponse: true + }; +} +function _temp(block, index) { + const blockLines = countCharInString(block.code, "\n") + 1; + return { + label: truncateLine(block.code, 60), + value: index, + description: [block.lang, blockLines > 1 ? `${blockLines} lines` : undefined].filter(Boolean).join(", ") || undefined + }; +} +export const call: LocalJSXCommandCall = async (onDone, context, args) => { + const texts = collectRecentAssistantTexts(context.messages); + if (texts.length === 0) { + onDone('No assistant message to copy'); + return null; + } + + // /copy N reaches back N-1 messages (1 = latest, 2 = second-to-latest, ...) + let age = 0; + const arg = args?.trim(); + if (arg) { + const n = Number(arg); + if (!Number.isInteger(n) || n < 1) { + onDone(`Usage: /copy [N] where N is 1 (latest), 2, 3, \u2026 Got: ${arg}`); + return null; + } + if (n > texts.length) { + onDone(`Only ${texts.length} assistant ${texts.length === 1 ? 'message' : 'messages'} available to copy`); + return null; + } + age = n - 1; + } + const text = texts[age]!; + const codeBlocks = extractCodeBlocks(text); + const config = getGlobalConfig(); + if (codeBlocks.length === 0 || config.copyFullResponse) { + logEvent('tengu_copy', { + always: config.copyFullResponse, + block_count: codeBlocks.length, + message_age: age + }); + const result = await copyOrWriteToFile(text, RESPONSE_FILENAME); + onDone(result); + return null; + } + return ; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJta2RpciIsIndyaXRlRmlsZSIsIm1hcmtlZCIsIlRva2VucyIsInRtcGRpciIsImpvaW4iLCJSZWFjdCIsInVzZVJlZiIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiT3B0aW9uV2l0aERlc2NyaXB0aW9uIiwiU2VsZWN0IiwiQnlsaW5lIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJQYW5lIiwiS2V5Ym9hcmRFdmVudCIsInN0cmluZ1dpZHRoIiwic2V0Q2xpcGJvYXJkIiwiQm94IiwiVGV4dCIsImxvZ0V2ZW50IiwiTG9jYWxKU1hDb21tYW5kQ2FsbCIsIkFzc2lzdGFudE1lc3NhZ2UiLCJNZXNzYWdlIiwiZ2V0R2xvYmFsQ29uZmlnIiwic2F2ZUdsb2JhbENvbmZpZyIsImV4dHJhY3RUZXh0Q29udGVudCIsInN0cmlwUHJvbXB0WE1MVGFncyIsImNvdW50Q2hhckluU3RyaW5nIiwiQ09QWV9ESVIiLCJSRVNQT05TRV9GSUxFTkFNRSIsIk1BWF9MT09LQkFDSyIsIkNvZGVCbG9jayIsImNvZGUiLCJsYW5nIiwiZXh0cmFjdENvZGVCbG9ja3MiLCJtYXJrZG93biIsInRva2VucyIsImxleGVyIiwiYmxvY2tzIiwidG9rZW4iLCJ0eXBlIiwiY29kZVRva2VuIiwiQ29kZSIsInB1c2giLCJ0ZXh0IiwiY29sbGVjdFJlY2VudEFzc2lzdGFudFRleHRzIiwibWVzc2FnZXMiLCJ0ZXh0cyIsImkiLCJsZW5ndGgiLCJtc2ciLCJpc0FwaUVycm9yTWVzc2FnZSIsImNvbnRlbnQiLCJtZXNzYWdlIiwiQXJyYXkiLCJpc0FycmF5IiwiZmlsZUV4dGVuc2lvbiIsInNhbml0aXplZCIsInJlcGxhY2UiLCJ3cml0ZVRvRmlsZSIsImZpbGVuYW1lIiwiUHJvbWlzZSIsImZpbGVQYXRoIiwicmVjdXJzaXZlIiwiY29weU9yV3JpdGVUb0ZpbGUiLCJyYXciLCJwcm9jZXNzIiwic3Rkb3V0Iiwid3JpdGUiLCJsaW5lQ291bnQiLCJjaGFyQ291bnQiLCJ0cnVuY2F0ZUxpbmUiLCJtYXhMZW4iLCJmaXJzdExpbmUiLCJzcGxpdCIsInJlc3VsdCIsIndpZHRoIiwidGFyZ2V0V2lkdGgiLCJjaGFyIiwiY2hhcldpZHRoIiwiUGlja2VyUHJvcHMiLCJmdWxsVGV4dCIsImNvZGVCbG9ja3MiLCJtZXNzYWdlQWdlIiwib25Eb25lIiwib3B0aW9ucyIsImRpc3BsYXkiLCJQaWNrZXJTZWxlY3Rpb24iLCJDb3B5UGlja2VyIiwidDAiLCIkIiwiX2MiLCJmb2N1c2VkUmVmIiwidDEiLCJ0MiIsImxhYmVsIiwidmFsdWUiLCJjb25zdCIsImRlc2NyaXB0aW9uIiwidDMiLCJ0NCIsIlN5bWJvbCIsImZvciIsIm1hcCIsIl90ZW1wIiwiZ2V0U2VsZWN0aW9uQ29udGVudCIsInNlbGVjdGVkIiwiYmxvY2tfMCIsImJsb2NrIiwiYmxvY2tJbmRleCIsInQ1IiwiaGFuZGxlU2VsZWN0Iiwic2VsZWN0ZWRfMCIsImNvcHlGdWxsUmVzcG9uc2UiLCJfdGVtcDIiLCJibG9ja19jb3VudCIsImFsd2F5cyIsIm1lc3NhZ2VfYWdlIiwic2VsZWN0ZWRfYmxvY2siLCJyZXN1bHRfMCIsInQ2IiwiaGFuZGxlV3JpdGUiLCJzZWxlY3RlZF8xIiwiY29udGVudF8wIiwid3JpdGVfc2hvcnRjdXQiLCJ0NyIsImUiLCJFcnJvciIsImhhbmRsZUtleURvd24iLCJlXzAiLCJrZXkiLCJwcmV2ZW50RGVmYXVsdCIsImN1cnJlbnQiLCJ0OCIsInQ5Iiwic2VsZWN0ZWRfMiIsInQxMCIsInQxMSIsInQxMiIsInQxMyIsImMiLCJpbmRleCIsImJsb2NrTGluZXMiLCJ1bmRlZmluZWQiLCJmaWx0ZXIiLCJCb29sZWFuIiwiY2FsbCIsImNvbnRleHQiLCJhcmdzIiwiYWdlIiwiYXJnIiwidHJpbSIsIm4iLCJOdW1iZXIiLCJpc0ludGVnZXIiLCJjb25maWciXSwic291cmNlcyI6WyJjb3B5LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBta2Rpciwgd3JpdGVGaWxlIH0gZnJvbSAnZnMvcHJvbWlzZXMnXG5pbXBvcnQgeyBtYXJrZWQsIHR5cGUgVG9rZW5zIH0gZnJvbSAnbWFya2VkJ1xuaW1wb3J0IHsgdG1wZGlyIH0gZnJvbSAnb3MnXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCdcbmltcG9ydCBSZWFjdCwgeyB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgQ29tbWFuZFJlc3VsdERpc3BsYXkgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB0eXBlIHsgT3B0aW9uV2l0aERlc2NyaXB0aW9uIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9DdXN0b21TZWxlY3Qvc2VsZWN0LmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9DdXN0b21TZWxlY3Qvc2VsZWN0LmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgUGFuZSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvZGVzaWduLXN5c3RlbS9QYW5lLmpzJ1xuaW1wb3J0IHR5cGUgeyBLZXlib2FyZEV2ZW50IH0gZnJvbSAnLi4vLi4vaW5rL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB7IHN0cmluZ1dpZHRoIH0gZnJvbSAnLi4vLi4vaW5rL3N0cmluZ1dpZHRoLmpzJ1xuaW1wb3J0IHsgc2V0Q2xpcGJvYXJkIH0gZnJvbSAnLi4vLi4vaW5rL3Rlcm1pby9vc2MuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBsb2dFdmVudCB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ2FsbCB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgdHlwZSB7IEFzc2lzdGFudE1lc3NhZ2UsIE1lc3NhZ2UgfSBmcm9tICcuLi8uLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYmFsQ29uZmlnLCBzYXZlR2xvYmFsQ29uZmlnIH0gZnJvbSAnLi4vLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgZXh0cmFjdFRleHRDb250ZW50LCBzdHJpcFByb21wdFhNTFRhZ3MgfSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB7IGNvdW50Q2hhckluU3RyaW5nIH0gZnJvbSAnLi4vLi4vdXRpbHMvc3RyaW5nVXRpbHMuanMnXG5cbmNvbnN0IENPUFlfRElSID0gam9pbih0bXBkaXIoKSwgJ2NsYXVkZScpXG5jb25zdCBSRVNQT05TRV9GSUxFTkFNRSA9ICdyZXNwb25zZS5tZCdcbmNvbnN0IE1BWF9MT09LQkFDSyA9IDIwXG5cbnR5cGUgQ29kZUJsb2NrID0ge1xuICBjb2RlOiBzdHJpbmdcbiAgbGFuZzogc3RyaW5nIHwgdW5kZWZpbmVkXG59XG5cbmZ1bmN0aW9uIGV4dHJhY3RDb2RlQmxvY2tzKG1hcmtkb3duOiBzdHJpbmcpOiBDb2RlQmxvY2tbXSB7XG4gIGNvbnN0IHRva2VucyA9IG1hcmtlZC5sZXhlcihzdHJpcFByb21wdFhNTFRhZ3MobWFya2Rvd24pKVxuICBjb25zdCBibG9ja3M6IENvZGVCbG9ja1tdID0gW11cbiAgZm9yIChjb25zdCB0b2tlbiBvZiB0b2tlbnMpIHtcbiAgICBpZiAodG9rZW4udHlwZSA9PT0gJ2NvZGUnKSB7XG4gICAgICBjb25zdCBjb2RlVG9rZW4gPSB0b2tlbiBhcyBUb2tlbnMuQ29kZVxuICAgICAgYmxvY2tzLnB1c2goeyBjb2RlOiBjb2RlVG9rZW4udGV4dCwgbGFuZzogY29kZVRva2VuLmxhbmcgfSlcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGJsb2Nrc1xufVxuXG4vKipcbiAqIFdhbGsgbWVzc2FnZXMgbmV3ZXN0LWZpcnN0LCByZXR1cm5pbmcgdGV4dCBmcm9tIGFzc2lzdGFudCBtZXNzYWdlcyB0aGF0XG4gKiBhY3R1YWxseSBzYWlkIHNvbWV0aGluZyAoc2tpcHMgdG9vbC11c2Utb25seSB0dXJucyBhbmQgQVBJIGVycm9ycykuXG4gKiBJbmRleCAwID0gbGF0ZXN0LCAxID0gc2Vjb25kLXRvLWxhdGVzdCwgZXRjLiBDYXBzIGF0IE1BWF9MT09LQkFDSy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbGxlY3RSZWNlbnRBc3Npc3RhbnRUZXh0cyhtZXNzYWdlczogTWVzc2FnZVtdKTogc3RyaW5nW10ge1xuICBjb25zdCB0ZXh0czogc3RyaW5nW10gPSBbXVxuICBmb3IgKFxuICAgIGxldCBpID0gbWVzc2FnZXMubGVuZ3RoIC0gMTtcbiAgICBpID49IDAgJiYgdGV4dHMubGVuZ3RoIDwgTUFYX0xPT0tCQUNLO1xuICAgIGktLVxuICApIHtcbiAgICBjb25zdCBtc2cgPSBtZXNzYWdlc1tpXVxuICAgIGlmIChtc2c/LnR5cGUgIT09ICdhc3Npc3RhbnQnIHx8IG1zZy5pc0FwaUVycm9yTWVzc2FnZSkgY29udGludWVcbiAgICBjb25zdCBjb250ZW50ID0gKG1zZyBhcyBBc3Npc3RhbnRNZXNzYWdlKS5tZXNzYWdlLmNvbnRlbnRcbiAgICBpZiAoIUFycmF5LmlzQXJyYXkoY29udGVudCkpIGNvbnRpbnVlXG4gICAgY29uc3QgdGV4dCA9IGV4dHJhY3RUZXh0Q29udGVudChjb250ZW50LCAnXFxuXFxuJylcbiAgICBpZiAodGV4dCkgdGV4dHMucHVzaCh0ZXh0KVxuICB9XG4gIHJldHVybiB0ZXh0c1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZmlsZUV4dGVuc2lvbihsYW5nOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBzdHJpbmcge1xuICBpZiAobGFuZykge1xuICAgIC8vIFNhbml0aXplIHRvIHByZXZlbnQgcGF0aCB0cmF2ZXJzYWwgKGUuZy4gYGBgLi4vLi4vZXRjL3Bhc3N3ZClcbiAgICAvLyBMYW5ndWFnZSBpZGVudGlmaWVycyBhcmUgYWxwaGFudW1lcmljOiBweXRob24sIHRzeCwganNvbmMsIGV0Yy5cbiAgICBjb25zdCBzYW5pdGl6ZWQgPSBsYW5nLnJlcGxhY2UoL1teYS16QS1aMC05XS9nLCAnJylcbiAgICBpZiAoc2FuaXRpemVkICYmIHNhbml0aXplZCAhPT0gJ3BsYWludGV4dCcpIHtcbiAgICAgIHJldHVybiBgLiR7c2FuaXRpemVkfWBcbiAgICB9XG4gIH1cbiAgcmV0dXJuICcudHh0J1xufVxuXG5hc3luYyBmdW5jdGlvbiB3cml0ZVRvRmlsZSh0ZXh0OiBzdHJpbmcsIGZpbGVuYW1lOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBmaWxlUGF0aCA9IGpvaW4oQ09QWV9ESVIsIGZpbGVuYW1lKVxuICBhd2FpdCBta2RpcihDT1BZX0RJUiwgeyByZWN1cnNpdmU6IHRydWUgfSlcbiAgYXdhaXQgd3JpdGVGaWxlKGZpbGVQYXRoLCB0ZXh0LCAndXRmLTgnKVxuICByZXR1cm4gZmlsZVBhdGhcbn1cblxuYXN5bmMgZnVuY3Rpb24gY29weU9yV3JpdGVUb0ZpbGUoXG4gIHRleHQ6IHN0cmluZyxcbiAgZmlsZW5hbWU6IHN0cmluZyxcbik6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHJhdyA9IGF3YWl0IHNldENsaXBib2FyZCh0ZXh0KVxuICBpZiAocmF3KSBwcm9jZXNzLnN0ZG91dC53cml0ZShyYXcpXG4gIGNvbnN0IGxpbmVDb3VudCA9IGNvdW50Q2hhckluU3RyaW5nKHRleHQsICdcXG4nKSArIDFcbiAgY29uc3QgY2hhckNvdW50ID0gdGV4dC5sZW5ndGhcbiAgLy8gQWxzbyB3cml0ZSB0byBhIHRlbXAgZmlsZSDigJQgY2xpcGJvYXJkIHBhdGhzIGFyZSBiZXN0LWVmZm9ydCAoT1NDIDUyIG5lZWRzXG4gIC8vIHRlcm1pbmFsIHN1cHBvcnQpLCBzbyB0aGUgZmlsZSBwcm92aWRlcyBhIHJlbGlhYmxlIGZhbGxiYWNrLlxuICB0cnkge1xuICAgIGNvbnN0IGZpbGVQYXRoID0gYXdhaXQgd3JpdGVUb0ZpbGUodGV4dCwgZmlsZW5hbWUpXG4gICAgcmV0dXJuIGBDb3BpZWQgdG8gY2xpcGJvYXJkICgke2NoYXJDb3VudH0gY2hhcmFjdGVycywgJHtsaW5lQ291bnR9IGxpbmVzKVxcbkFsc28gd3JpdHRlbiB0byAke2ZpbGVQYXRofWBcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIGBDb3BpZWQgdG8gY2xpcGJvYXJkICgke2NoYXJDb3VudH0gY2hhcmFjdGVycywgJHtsaW5lQ291bnR9IGxpbmVzKWBcbiAgfVxufVxuXG5mdW5jdGlvbiB0cnVuY2F0ZUxpbmUodGV4dDogc3RyaW5nLCBtYXhMZW46IG51bWJlcik6IHN0cmluZyB7XG4gIGNvbnN0IGZpcnN0TGluZSA9IHRleHQuc3BsaXQoJ1xcbicpWzBdID8/ICcnXG4gIGlmIChzdHJpbmdXaWR0aChmaXJzdExpbmUpIDw9IG1heExlbikge1xuICAgIHJldHVybiBmaXJzdExpbmVcbiAgfVxuICBsZXQgcmVzdWx0ID0gJydcbiAgbGV0IHdpZHRoID0gMFxuICBjb25zdCB0YXJnZXRXaWR0aCA9IG1heExlbiAtIDFcbiAgZm9yIChjb25zdCBjaGFyIG9mIGZpcnN0TGluZSkge1xuICAgIGNvbnN0IGNoYXJXaWR0aCA9IHN0cmluZ1dpZHRoKGNoYXIpXG4gICAgaWYgKHdpZHRoICsgY2hhcldpZHRoID4gdGFyZ2V0V2lkdGgpIGJyZWFrXG4gICAgcmVzdWx0ICs9IGNoYXJcbiAgICB3aWR0aCArPSBjaGFyV2lkdGhcbiAgfVxuICByZXR1cm4gcmVzdWx0ICsgJ1xcdTIwMjYnXG59XG5cbnR5cGUgUGlja2VyUHJvcHMgPSB7XG4gIGZ1bGxUZXh0OiBzdHJpbmdcbiAgY29kZUJsb2NrczogQ29kZUJsb2NrW11cbiAgbWVzc2FnZUFnZTogbnVtYmVyXG4gIG9uRG9uZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkXG59XG5cbnR5cGUgUGlja2VyU2VsZWN0aW9uID0gbnVtYmVyIHwgJ2Z1bGwnIHwgJ2Fsd2F5cydcblxuZnVuY3Rpb24gQ29weVBpY2tlcih7XG4gIGZ1bGxUZXh0LFxuICBjb2RlQmxvY2tzLFxuICBtZXNzYWdlQWdlLFxuICBvbkRvbmUsXG59OiBQaWNrZXJQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGZvY3VzZWRSZWYgPSB1c2VSZWY8UGlja2VyU2VsZWN0aW9uPignZnVsbCcpXG5cbiAgY29uc3Qgb3B0aW9uczogT3B0aW9uV2l0aERlc2NyaXB0aW9uPFBpY2tlclNlbGVjdGlvbj5bXSA9IFtcbiAgICB7XG4gICAgICBsYWJlbDogJ0Z1bGwgcmVzcG9uc2UnLFxuICAgICAgdmFsdWU6ICdmdWxsJyBhcyBjb25zdCxcbiAgICAgIGRlc2NyaXB0aW9uOiBgJHtmdWxsVGV4dC5sZW5ndGh9IGNoYXJzLCAke2NvdW50Q2hhckluU3RyaW5nKGZ1bGxUZXh0LCAnXFxuJykgKyAxfSBsaW5lc2AsXG4gICAgfSxcbiAgICAuLi5jb2RlQmxvY2tzLm1hcCgoYmxvY2ssIGluZGV4KSA9PiB7XG4gICAgICBjb25zdCBibG9ja0xpbmVzID0gY291bnRDaGFySW5TdHJpbmcoYmxvY2suY29kZSwgJ1xcbicpICsgMVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbGFiZWw6IHRydW5jYXRlTGluZShibG9jay5jb2RlLCA2MCksXG4gICAgICAgIHZhbHVlOiBpbmRleCxcbiAgICAgICAgZGVzY3JpcHRpb246XG4gICAgICAgICAgW2Jsb2NrLmxhbmcsIGJsb2NrTGluZXMgPiAxID8gYCR7YmxvY2tMaW5lc30gbGluZXNgIDogdW5kZWZpbmVkXVxuICAgICAgICAgICAgLmZpbHRlcihCb29sZWFuKVxuICAgICAgICAgICAgLmpvaW4oJywgJykgfHwgdW5kZWZpbmVkLFxuICAgICAgfVxuICAgIH0pLFxuICAgIHtcbiAgICAgIGxhYmVsOiAnQWx3YXlzIGNvcHkgZnVsbCByZXNwb25zZScsXG4gICAgICB2YWx1ZTogJ2Fsd2F5cycgYXMgY29uc3QsXG4gICAgICBkZXNjcmlwdGlvbjogJ1NraXAgdGhpcyBwaWNrZXIgaW4gdGhlIGZ1dHVyZSAocmV2ZXJ0IHZpYSAvY29uZmlnKScsXG4gICAgfSxcbiAgXVxuXG4gIGZ1bmN0aW9uIGdldFNlbGVjdGlvbkNvbnRlbnQoc2VsZWN0ZWQ6IFBpY2tlclNlbGVjdGlvbik6IHtcbiAgICB0ZXh0OiBzdHJpbmdcbiAgICBmaWxlbmFtZTogc3RyaW5nXG4gICAgYmxvY2tJbmRleD86IG51bWJlclxuICB9IHtcbiAgICBpZiAoc2VsZWN0ZWQgPT09ICdmdWxsJyB8fCBzZWxlY3RlZCA9PT0gJ2Fsd2F5cycpIHtcbiAgICAgIHJldHVybiB7IHRleHQ6IGZ1bGxUZXh0LCBmaWxlbmFtZTogUkVTUE9OU0VfRklMRU5BTUUgfVxuICAgIH1cbiAgICBjb25zdCBibG9jayA9IGNvZGVCbG9ja3Nbc2VsZWN0ZWRdIVxuICAgIHJldHVybiB7XG4gICAgICB0ZXh0OiBibG9jay5jb2RlLFxuICAgICAgZmlsZW5hbWU6IGBjb3B5JHtmaWxlRXh0ZW5zaW9uKGJsb2NrLmxhbmcpfWAsXG4gICAgICBibG9ja0luZGV4OiBzZWxlY3RlZCxcbiAgICB9XG4gIH1cblxuICBhc3luYyBmdW5jdGlvbiBoYW5kbGVTZWxlY3Qoc2VsZWN0ZWQ6IFBpY2tlclNlbGVjdGlvbik6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IGNvbnRlbnQgPSBnZXRTZWxlY3Rpb25Db250ZW50KHNlbGVjdGVkKVxuICAgIGlmIChzZWxlY3RlZCA9PT0gJ2Fsd2F5cycpIHtcbiAgICAgIGlmICghZ2V0R2xvYmFsQ29uZmlnKCkuY29weUZ1bGxSZXNwb25zZSkge1xuICAgICAgICBzYXZlR2xvYmFsQ29uZmlnKGMgPT4gKHsgLi4uYywgY29weUZ1bGxSZXNwb25zZTogdHJ1ZSB9KSlcbiAgICAgIH1cbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9jb3B5Jywge1xuICAgICAgICBibG9ja19jb3VudDogY29kZUJsb2Nrcy5sZW5ndGgsXG4gICAgICAgIGFsd2F5czogdHJ1ZSxcbiAgICAgICAgbWVzc2FnZV9hZ2U6IG1lc3NhZ2VBZ2UsXG4gICAgICB9KVxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgY29weU9yV3JpdGVUb0ZpbGUoY29udGVudC50ZXh0LCBjb250ZW50LmZpbGVuYW1lKVxuICAgICAgb25Eb25lKFxuICAgICAgICBgJHtyZXN1bHR9XFxuUHJlZmVyZW5jZSBzYXZlZC4gVXNlIC9jb25maWcgdG8gY2hhbmdlIGNvcHlGdWxsUmVzcG9uc2VgLFxuICAgICAgKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGxvZ0V2ZW50KCd0ZW5ndV9jb3B5Jywge1xuICAgICAgc2VsZWN0ZWRfYmxvY2s6IGNvbnRlbnQuYmxvY2tJbmRleCxcbiAgICAgIGJsb2NrX2NvdW50OiBjb2RlQmxvY2tzLmxlbmd0aCxcbiAgICAgIG1lc3NhZ2VfYWdlOiBtZXNzYWdlQWdlLFxuICAgIH0pXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgY29weU9yV3JpdGVUb0ZpbGUoY29udGVudC50ZXh0LCBjb250ZW50LmZpbGVuYW1lKVxuICAgIG9uRG9uZShyZXN1bHQpXG4gIH1cblxuICBhc3luYyBmdW5jdGlvbiBoYW5kbGVXcml0ZShzZWxlY3RlZDogUGlja2VyU2VsZWN0aW9uKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgY29udGVudCA9IGdldFNlbGVjdGlvbkNvbnRlbnQoc2VsZWN0ZWQpXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X2NvcHknLCB7XG4gICAgICBzZWxlY3RlZF9ibG9jazogY29udGVudC5ibG9ja0luZGV4LFxuICAgICAgYmxvY2tfY291bnQ6IGNvZGVCbG9ja3MubGVuZ3RoLFxuICAgICAgbWVzc2FnZV9hZ2U6IG1lc3NhZ2VBZ2UsXG4gICAgICB3cml0ZV9zaG9ydGN1dDogdHJ1ZSxcbiAgICB9KVxuICAgIHRyeSB7XG4gICAgICBjb25zdCBmaWxlUGF0aCA9IGF3YWl0IHdyaXRlVG9GaWxlKGNvbnRlbnQudGV4dCwgY29udGVudC5maWxlbmFtZSlcbiAgICAgIG9uRG9uZShgV3JpdHRlbiB0byAke2ZpbGVQYXRofWApXG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgb25Eb25lKGBGYWlsZWQgdG8gd3JpdGUgZmlsZTogJHtlIGluc3RhbmNlb2YgRXJyb3IgPyBlLm1lc3NhZ2UgOiBlfWApXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gaGFuZGxlS2V5RG93bihlOiBLZXlib2FyZEV2ZW50KTogdm9pZCB7XG4gICAgaWYgKGUua2V5ID09PSAndycpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgdm9pZCBoYW5kbGVXcml0ZShmb2N1c2VkUmVmLmN1cnJlbnQpXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8UGFuZT5cbiAgICAgIDxCb3hcbiAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgIGdhcD17MX1cbiAgICAgICAgdGFiSW5kZXg9ezB9XG4gICAgICAgIGF1dG9Gb2N1c1xuICAgICAgICBvbktleURvd249e2hhbmRsZUtleURvd259XG4gICAgICA+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlNlbGVjdCBjb250ZW50IHRvIGNvcHk6PC9UZXh0PlxuICAgICAgICA8U2VsZWN0PFBpY2tlclNlbGVjdGlvbj5cbiAgICAgICAgICBvcHRpb25zPXtvcHRpb25zfVxuICAgICAgICAgIGhpZGVJbmRleGVzPXtmYWxzZX1cbiAgICAgICAgICBvbkZvY3VzPXt2YWx1ZSA9PiB7XG4gICAgICAgICAgICBmb2N1c2VkUmVmLmN1cnJlbnQgPSB2YWx1ZVxuICAgICAgICAgIH19XG4gICAgICAgICAgb25DaGFuZ2U9e3NlbGVjdGVkID0+IHtcbiAgICAgICAgICAgIHZvaWQgaGFuZGxlU2VsZWN0KHNlbGVjdGVkKVxuICAgICAgICAgIH19XG4gICAgICAgICAgb25DYW5jZWw9eygpID0+IHtcbiAgICAgICAgICAgIG9uRG9uZSgnQ29weSBjYW5jZWxsZWQnLCB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0pXG4gICAgICAgICAgfX1cbiAgICAgICAgLz5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cImVudGVyXCIgYWN0aW9uPVwiY29weVwiIC8+XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJ3XCIgYWN0aW9uPVwid3JpdGUgdG8gZmlsZVwiIC8+XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJlc2NcIiBhY3Rpb249XCJjYW5jZWxcIiAvPlxuICAgICAgICAgIDwvQnlsaW5lPlxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L1BhbmU+XG4gIClcbn1cblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0LCBhcmdzKSA9PiB7XG4gIGNvbnN0IHRleHRzID0gY29sbGVjdFJlY2VudEFzc2lzdGFudFRleHRzKGNvbnRleHQubWVzc2FnZXMpXG5cbiAgaWYgKHRleHRzLmxlbmd0aCA9PT0gMCkge1xuICAgIG9uRG9uZSgnTm8gYXNzaXN0YW50IG1lc3NhZ2UgdG8gY29weScpXG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIC8vIC9jb3B5IE4gcmVhY2hlcyBiYWNrIE4tMSBtZXNzYWdlcyAoMSA9IGxhdGVzdCwgMiA9IHNlY29uZC10by1sYXRlc3QsIC4uLilcbiAgbGV0IGFnZSA9IDBcbiAgY29uc3QgYXJnID0gYXJncz8udHJpbSgpXG4gIGlmIChhcmcpIHtcbiAgICBjb25zdCBuID0gTnVtYmVyKGFyZylcbiAgICBpZiAoIU51bWJlci5pc0ludGVnZXIobikgfHwgbiA8IDEpIHtcbiAgICAgIG9uRG9uZShgVXNhZ2U6IC9jb3B5IFtOXSB3aGVyZSBOIGlzIDEgKGxhdGVzdCksIDIsIDMsIFxcdTIwMjYgR290OiAke2FyZ31gKVxuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG4gICAgaWYgKG4gPiB0ZXh0cy5sZW5ndGgpIHtcbiAgICAgIG9uRG9uZShcbiAgICAgICAgYE9ubHkgJHt0ZXh0cy5sZW5ndGh9IGFzc2lzdGFudCAke3RleHRzLmxlbmd0aCA9PT0gMSA/ICdtZXNzYWdlJyA6ICdtZXNzYWdlcyd9IGF2YWlsYWJsZSB0byBjb3B5YCxcbiAgICAgIClcbiAgICAgIHJldHVybiBudWxsXG4gICAgfVxuICAgIGFnZSA9IG4gLSAxXG4gIH1cblxuICBjb25zdCB0ZXh0ID0gdGV4dHNbYWdlXSFcbiAgY29uc3QgY29kZUJsb2NrcyA9IGV4dHJhY3RDb2RlQmxvY2tzKHRleHQpXG4gIGNvbnN0IGNvbmZpZyA9IGdldEdsb2JhbENvbmZpZygpXG5cbiAgaWYgKGNvZGVCbG9ja3MubGVuZ3RoID09PSAwIHx8IGNvbmZpZy5jb3B5RnVsbFJlc3BvbnNlKSB7XG4gICAgbG9nRXZlbnQoJ3Rlbmd1X2NvcHknLCB7XG4gICAgICBhbHdheXM6IGNvbmZpZy5jb3B5RnVsbFJlc3BvbnNlLFxuICAgICAgYmxvY2tfY291bnQ6IGNvZGVCbG9ja3MubGVuZ3RoLFxuICAgICAgbWVzc2FnZV9hZ2U6IGFnZSxcbiAgICB9KVxuICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNvcHlPcldyaXRlVG9GaWxlKHRleHQsIFJFU1BPTlNFX0ZJTEVOQU1FKVxuICAgIG9uRG9uZShyZXN1bHQpXG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPENvcHlQaWNrZXJcbiAgICAgIGZ1bGxUZXh0PXt0ZXh0fVxuICAgICAgY29kZUJsb2Nrcz17Y29kZUJsb2Nrc31cbiAgICAgIG1lc3NhZ2VBZ2U9e2FnZX1cbiAgICAgIG9uRG9uZT17b25Eb25lfVxuICAgIC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLEtBQUssRUFBRUMsU0FBUyxRQUFRLGFBQWE7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 \ No newline at end of file diff --git a/commands/copy/index.ts b/commands/copy/index.ts new file mode 100644 index 0000000..092c70e --- /dev/null +++ b/commands/copy/index.ts @@ -0,0 +1,15 @@ +/** + * Copy command - minimal metadata only. + * Implementation is lazy-loaded from copy.tsx to reduce startup time. + */ +import type { Command } from '../../commands.js' + +const copy = { + type: 'local-jsx', + name: 'copy', + description: + "Copy Claude's last response to clipboard (or /copy N for the Nth-latest)", + load: () => import('./copy.js'), +} satisfies Command + +export default copy diff --git a/commands/cost/cost.ts b/commands/cost/cost.ts new file mode 100644 index 0000000..c9fb0cb --- /dev/null +++ b/commands/cost/cost.ts @@ -0,0 +1,24 @@ +import { formatTotalCost } from '../../cost-tracker.js' +import { currentLimits } from '../../services/claudeAiLimits.js' +import type { LocalCommandCall } from '../../types/command.js' +import { isClaudeAISubscriber } from '../../utils/auth.js' + +export const call: LocalCommandCall = async () => { + if (isClaudeAISubscriber()) { + let value: string + + if (currentLimits.isUsingOverage) { + value = + 'You are currently using your overages to power your Claude Code usage. We will automatically switch you back to your subscription rate limits when they reset' + } else { + value = + 'You are currently using your subscription to power your Claude Code usage' + } + + if (process.env.USER_TYPE === 'ant') { + value += `\n\n[ANT-ONLY] Showing cost anyway:\n ${formatTotalCost()}` + } + return { type: 'text', value } + } + return { type: 'text', value: formatTotalCost() } +} diff --git a/commands/cost/index.ts b/commands/cost/index.ts new file mode 100644 index 0000000..d1c2d23 --- /dev/null +++ b/commands/cost/index.ts @@ -0,0 +1,23 @@ +/** + * Cost command - minimal metadata only. + * Implementation is lazy-loaded from cost.ts to reduce startup time. + */ +import type { Command } from '../../commands.js' +import { isClaudeAISubscriber } from '../../utils/auth.js' + +const cost = { + type: 'local', + name: 'cost', + description: 'Show the total cost and duration of the current session', + get isHidden() { + // Keep visible for Ants even if they're subscribers (they see cost breakdowns) + if (process.env.USER_TYPE === 'ant') { + return false + } + return isClaudeAISubscriber() + }, + supportsNonInteractive: true, + load: () => import('./cost.js'), +} satisfies Command + +export default cost diff --git a/commands/createMovedToPluginCommand.ts b/commands/createMovedToPluginCommand.ts new file mode 100644 index 0000000..08dee29 --- /dev/null +++ b/commands/createMovedToPluginCommand.ts @@ -0,0 +1,65 @@ +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.js' +import type { Command } from '../commands.js' +import type { ToolUseContext } from '../Tool.js' + +type Options = { + name: string + description: string + progressMessage: string + pluginName: string + pluginCommand: string + /** + * The prompt to use while the marketplace is private. + * External users will get this prompt. Once the marketplace is public, + * this parameter and the fallback logic can be removed. + */ + getPromptWhileMarketplaceIsPrivate: ( + args: string, + context: ToolUseContext, + ) => Promise +} + +export function createMovedToPluginCommand({ + name, + description, + progressMessage, + pluginName, + pluginCommand, + getPromptWhileMarketplaceIsPrivate, +}: Options): Command { + return { + type: 'prompt', + name, + description, + progressMessage, + contentLength: 0, // Dynamic content + userFacingName() { + return name + }, + source: 'builtin', + async getPromptForCommand( + args: string, + context: ToolUseContext, + ): Promise { + if (process.env.USER_TYPE === 'ant') { + return [ + { + type: 'text', + text: `This command has been moved to a plugin. Tell the user: + +1. To install the plugin, run: + claude plugin install ${pluginName}@claude-code-marketplace + +2. After installation, use /${pluginName}:${pluginCommand} to run this command + +3. For more information, see: https://github.com/anthropics/claude-code-marketplace/blob/main/${pluginName}/README.md + +Do not attempt to run the command. Simply inform the user about the plugin installation.`, + }, + ] + } + + return getPromptWhileMarketplaceIsPrivate(args, context) + }, + } +} diff --git a/commands/ctx_viz/index.js b/commands/ctx_viz/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/ctx_viz/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/debug-tool-call/index.js b/commands/debug-tool-call/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/debug-tool-call/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/desktop/desktop.tsx b/commands/desktop/desktop.tsx new file mode 100644 index 0000000..a449c90 --- /dev/null +++ b/commands/desktop/desktop.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import type { CommandResultDisplay } from '../../commands.js'; +import { DesktopHandoff } from '../../components/DesktopHandoff.js'; +export async function call(onDone: (result?: string, options?: { + display?: CommandResultDisplay; +}) => void): Promise { + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiRGVza3RvcEhhbmRvZmYiLCJjYWxsIiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJQcm9taXNlIiwiUmVhY3ROb2RlIl0sInNvdXJjZXMiOlsiZGVza3RvcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgRGVza3RvcEhhbmRvZmYgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0Rlc2t0b3BIYW5kb2ZmLmpzJ1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWQsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICByZXR1cm4gPERlc2t0b3BIYW5kb2ZmIG9uRG9uZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixjQUFjQyxvQkFBb0IsUUFBUSxtQkFBbUI7QUFDN0QsU0FBU0MsY0FBYyxRQUFRLG9DQUFvQztBQUVuRSxPQUFPLGVBQWVDLElBQUlBLENBQ3hCQyxNQUFNLEVBQUUsQ0FDTkMsTUFBZSxDQUFSLEVBQUUsTUFBTSxFQUNmQyxPQUE0QyxDQUFwQyxFQUFFO0VBQUVDLE9BQU8sQ0FBQyxFQUFFTixvQkFBb0I7QUFBQyxDQUFDLEVBQzVDLEdBQUcsSUFBSSxDQUNWLEVBQUVPLE9BQU8sQ0FBQ1IsS0FBSyxDQUFDUyxTQUFTLENBQUMsQ0FBQztFQUMxQixPQUFPLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDTCxNQUFNLENBQUMsR0FBRztBQUMzQyIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/commands/desktop/index.ts b/commands/desktop/index.ts new file mode 100644 index 0000000..d03c3ae --- /dev/null +++ b/commands/desktop/index.ts @@ -0,0 +1,26 @@ +import type { Command } from '../../commands.js' + +function isSupportedPlatform(): boolean { + if (process.platform === 'darwin') { + return true + } + if (process.platform === 'win32' && process.arch === 'x64') { + return true + } + return false +} + +const desktop = { + type: 'local-jsx', + name: 'desktop', + aliases: ['app'], + description: 'Continue the current session in Claude Desktop', + availability: ['claude-ai'], + isEnabled: isSupportedPlatform, + get isHidden() { + return !isSupportedPlatform() + }, + load: () => import('./desktop.js'), +} satisfies Command + +export default desktop diff --git a/commands/diff/diff.tsx b/commands/diff/diff.tsx new file mode 100644 index 0000000..f31b086 --- /dev/null +++ b/commands/diff/diff.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; +import type { LocalJSXCommandCall } from '../../types/command.js'; +export const call: LocalJSXCommandCall = async (onDone, context) => { + const { + DiffDialog + } = await import('../../components/diff/DiffDialog.js'); + return ; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwiY29udGV4dCIsIkRpZmZEaWFsb2ciLCJtZXNzYWdlcyJdLCJzb3VyY2VzIjpbImRpZmYudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0KSA9PiB7XG4gIGNvbnN0IHsgRGlmZkRpYWxvZyB9ID0gYXdhaXQgaW1wb3J0KCcuLi8uLi9jb21wb25lbnRzL2RpZmYvRGlmZkRpYWxvZy5qcycpXG4gIHJldHVybiA8RGlmZkRpYWxvZyBtZXNzYWdlcz17Y29udGV4dC5tZXNzYWdlc30gb25Eb25lPXtvbkRvbmV9IC8+XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFBQyxDQUFPQyxNQUFNLEVBQUVDLE9BQU8sS0FBSztFQUNsRSxNQUFNO0lBQUVDO0VBQVcsQ0FBQyxHQUFHLE1BQU0sTUFBTSxDQUFDLHFDQUFxQyxDQUFDO0VBQzFFLE9BQU8sQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUNELE9BQU8sQ0FBQ0UsUUFBUSxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUNILE1BQU0sQ0FBQyxHQUFHO0FBQ25FLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/commands/diff/index.ts b/commands/diff/index.ts new file mode 100644 index 0000000..a15b819 --- /dev/null +++ b/commands/diff/index.ts @@ -0,0 +1,8 @@ +import type { Command } from '../../commands.js' + +export default { + type: 'local-jsx', + name: 'diff', + description: 'View uncommitted changes and per-turn diffs', + load: () => import('./diff.js'), +} satisfies Command diff --git a/commands/doctor/doctor.tsx b/commands/doctor/doctor.tsx new file mode 100644 index 0000000..447cd40 --- /dev/null +++ b/commands/doctor/doctor.tsx @@ -0,0 +1,7 @@ +import React from 'react'; +import { Doctor } from '../../screens/Doctor.js'; +import type { LocalJSXCommandCall } from '../../types/command.js'; +export const call: LocalJSXCommandCall = (onDone, _context, _args) => { + return Promise.resolve(); +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkRvY3RvciIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwiX2NvbnRleHQiLCJfYXJncyIsIlByb21pc2UiLCJyZXNvbHZlIl0sInNvdXJjZXMiOlsiZG9jdG9yLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBEb2N0b3IgfSBmcm9tICcuLi8uLi9zY3JlZW5zL0RvY3Rvci5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ2FsbCB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBjb25zdCBjYWxsOiBMb2NhbEpTWENvbW1hbmRDYWxsID0gKG9uRG9uZSwgX2NvbnRleHQsIF9hcmdzKSA9PiB7XG4gIHJldHVybiBQcm9taXNlLnJlc29sdmUoPERvY3RvciBvbkRvbmU9e29uRG9uZX0gLz4pXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLE1BQU0sUUFBUSx5QkFBeUI7QUFDaEQsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBR0MsQ0FBQ0MsTUFBTSxFQUFFQyxRQUFRLEVBQUVDLEtBQUssS0FBSztFQUNwRSxPQUFPQyxPQUFPLENBQUNDLE9BQU8sQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQ0osTUFBTSxDQUFDLEdBQUcsQ0FBQztBQUNwRCxDQUFDIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/doctor/index.ts b/commands/doctor/index.ts new file mode 100644 index 0000000..6a0b089 --- /dev/null +++ b/commands/doctor/index.ts @@ -0,0 +1,12 @@ +import type { Command } from '../../commands.js' +import { isEnvTruthy } from '../../utils/envUtils.js' + +const doctor: Command = { + name: 'doctor', + description: 'Diagnose and verify your Claude Code installation and settings', + isEnabled: () => !isEnvTruthy(process.env.DISABLE_DOCTOR_COMMAND), + type: 'local-jsx', + load: () => import('./doctor.js'), +} + +export default doctor diff --git a/commands/effort/effort.tsx b/commands/effort/effort.tsx new file mode 100644 index 0000000..41dd0d8 --- /dev/null +++ b/commands/effort/effort.tsx @@ -0,0 +1,183 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useMainLoopModel } from '../../hooks/useMainLoopModel.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../services/analytics/index.js'; +import { useAppState, useSetAppState } from '../../state/AppState.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import { type EffortValue, getDisplayedEffortLevel, getEffortEnvOverride, getEffortValueDescription, isEffortLevel, toPersistableEffort } from '../../utils/effort.js'; +import { updateSettingsForSource } from '../../utils/settings/settings.js'; +const COMMON_HELP_ARGS = ['help', '-h', '--help']; +type EffortCommandResult = { + message: string; + effortUpdate?: { + value: EffortValue | undefined; + }; +}; +function setEffortValue(effortValue: EffortValue): EffortCommandResult { + const persistable = toPersistableEffort(effortValue); + if (persistable !== undefined) { + const result = updateSettingsForSource('userSettings', { + effortLevel: persistable + }); + if (result.error) { + return { + message: `Failed to set effort level: ${result.error.message}` + }; + } + } + logEvent('tengu_effort_command', { + effort: effortValue as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + + // Env var wins at resolveAppliedEffort time. Only flag it when it actually + // conflicts — if env matches what the user just asked for, the outcome is + // the same, so "Set effort to X" is true and the note is noise. + const envOverride = getEffortEnvOverride(); + if (envOverride !== undefined && envOverride !== effortValue) { + const envRaw = process.env.CLAUDE_CODE_EFFORT_LEVEL; + if (persistable === undefined) { + return { + message: `Not applied: CLAUDE_CODE_EFFORT_LEVEL=${envRaw} overrides effort this session, and ${effortValue} is session-only (nothing saved)`, + effortUpdate: { + value: effortValue + } + }; + } + return { + message: `CLAUDE_CODE_EFFORT_LEVEL=${envRaw} overrides this session — clear it and ${effortValue} takes over`, + effortUpdate: { + value: effortValue + } + }; + } + const description = getEffortValueDescription(effortValue); + const suffix = persistable !== undefined ? '' : ' (this session only)'; + return { + message: `Set effort level to ${effortValue}${suffix}: ${description}`, + effortUpdate: { + value: effortValue + } + }; +} +export function showCurrentEffort(appStateEffort: EffortValue | undefined, model: string): EffortCommandResult { + const envOverride = getEffortEnvOverride(); + const effectiveValue = envOverride === null ? undefined : envOverride ?? appStateEffort; + if (effectiveValue === undefined) { + const level = getDisplayedEffortLevel(model, appStateEffort); + return { + message: `Effort level: auto (currently ${level})` + }; + } + const description = getEffortValueDescription(effectiveValue); + return { + message: `Current effort level: ${effectiveValue} (${description})` + }; +} +function unsetEffortLevel(): EffortCommandResult { + const result = updateSettingsForSource('userSettings', { + effortLevel: undefined + }); + if (result.error) { + return { + message: `Failed to set effort level: ${result.error.message}` + }; + } + logEvent('tengu_effort_command', { + effort: 'auto' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + // env=auto/unset (null) matches what /effort auto asks for, so only warn + // when env is pinning a specific level that will keep overriding. + const envOverride = getEffortEnvOverride(); + if (envOverride !== undefined && envOverride !== null) { + const envRaw = process.env.CLAUDE_CODE_EFFORT_LEVEL; + return { + message: `Cleared effort from settings, but CLAUDE_CODE_EFFORT_LEVEL=${envRaw} still controls this session`, + effortUpdate: { + value: undefined + } + }; + } + return { + message: 'Effort level set to auto', + effortUpdate: { + value: undefined + } + }; +} +export function executeEffort(args: string): EffortCommandResult { + const normalized = args.toLowerCase(); + if (normalized === 'auto' || normalized === 'unset') { + return unsetEffortLevel(); + } + if (!isEffortLevel(normalized)) { + return { + message: `Invalid argument: ${args}. Valid options are: low, medium, high, max, auto` + }; + } + return setEffortValue(normalized); +} +function ShowCurrentEffort(t0) { + const { + onDone + } = t0; + const effortValue = useAppState(_temp); + const model = useMainLoopModel(); + const { + message + } = showCurrentEffort(effortValue, model); + onDone(message); + return null; +} +function _temp(s) { + return s.effortValue; +} +function ApplyEffortAndClose(t0) { + const $ = _c(6); + const { + result, + onDone + } = t0; + const setAppState = useSetAppState(); + const { + effortUpdate, + message + } = result; + let t1; + let t2; + if ($[0] !== effortUpdate || $[1] !== message || $[2] !== onDone || $[3] !== setAppState) { + t1 = () => { + if (effortUpdate) { + setAppState(prev => ({ + ...prev, + effortValue: effortUpdate.value + })); + } + onDone(message); + }; + t2 = [setAppState, effortUpdate, message, onDone]; + $[0] = effortUpdate; + $[1] = message; + $[2] = onDone; + $[3] = setAppState; + $[4] = t1; + $[5] = t2; + } else { + t1 = $[4]; + t2 = $[5]; + } + React.useEffect(t1, t2); + return null; +} +export async function call(onDone: LocalJSXCommandOnDone, _context: unknown, args?: string): Promise { + args = args?.trim() || ''; + if (COMMON_HELP_ARGS.includes(args)) { + onDone('Usage: /effort [low|medium|high|max|auto]\n\nEffort levels:\n- low: Quick, straightforward implementation\n- medium: Balanced approach with standard testing\n- high: Comprehensive implementation with extensive testing\n- max: Maximum capability with deepest reasoning (Opus 4.6 only)\n- auto: Use the default effort level for your model'); + return; + } + if (!args || args === 'current' || args === 'status') { + return ; + } + const result = executeEffort(args); + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZU1haW5Mb29wTW9kZWwiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJ1c2VBcHBTdGF0ZSIsInVzZVNldEFwcFN0YXRlIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiRWZmb3J0VmFsdWUiLCJnZXREaXNwbGF5ZWRFZmZvcnRMZXZlbCIsImdldEVmZm9ydEVudk92ZXJyaWRlIiwiZ2V0RWZmb3J0VmFsdWVEZXNjcmlwdGlvbiIsImlzRWZmb3J0TGV2ZWwiLCJ0b1BlcnNpc3RhYmxlRWZmb3J0IiwidXBkYXRlU2V0dGluZ3NGb3JTb3VyY2UiLCJDT01NT05fSEVMUF9BUkdTIiwiRWZmb3J0Q29tbWFuZFJlc3VsdCIsIm1lc3NhZ2UiLCJlZmZvcnRVcGRhdGUiLCJ2YWx1ZSIsInNldEVmZm9ydFZhbHVlIiwiZWZmb3J0VmFsdWUiLCJwZXJzaXN0YWJsZSIsInVuZGVmaW5lZCIsInJlc3VsdCIsImVmZm9ydExldmVsIiwiZXJyb3IiLCJlZmZvcnQiLCJlbnZPdmVycmlkZSIsImVudlJhdyIsInByb2Nlc3MiLCJlbnYiLCJDTEFVREVfQ09ERV9FRkZPUlRfTEVWRUwiLCJkZXNjcmlwdGlvbiIsInN1ZmZpeCIsInNob3dDdXJyZW50RWZmb3J0IiwiYXBwU3RhdGVFZmZvcnQiLCJtb2RlbCIsImVmZmVjdGl2ZVZhbHVlIiwibGV2ZWwiLCJ1bnNldEVmZm9ydExldmVsIiwiZXhlY3V0ZUVmZm9ydCIsImFyZ3MiLCJub3JtYWxpemVkIiwidG9Mb3dlckNhc2UiLCJTaG93Q3VycmVudEVmZm9ydCIsInQwIiwib25Eb25lIiwiX3RlbXAiLCJzIiwiQXBwbHlFZmZvcnRBbmRDbG9zZSIsIiQiLCJfYyIsInNldEFwcFN0YXRlIiwidDEiLCJ0MiIsInByZXYiLCJ1c2VFZmZlY3QiLCJjYWxsIiwiX2NvbnRleHQiLCJQcm9taXNlIiwiUmVhY3ROb2RlIiwidHJpbSIsImluY2x1ZGVzIl0sInNvdXJjZXMiOlsiZWZmb3J0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZU1haW5Mb29wTW9kZWwgfSBmcm9tICcuLi8uLi9ob29rcy91c2VNYWluTG9vcE1vZGVsLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnLi4vLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUsIHVzZVNldEFwcFN0YXRlIH0gZnJvbSAnLi4vLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQge1xuICB0eXBlIEVmZm9ydFZhbHVlLFxuICBnZXREaXNwbGF5ZWRFZmZvcnRMZXZlbCxcbiAgZ2V0RWZmb3J0RW52T3ZlcnJpZGUsXG4gIGdldEVmZm9ydFZhbHVlRGVzY3JpcHRpb24sXG4gIGlzRWZmb3J0TGV2ZWwsXG4gIHRvUGVyc2lzdGFibGVFZmZvcnQsXG59IGZyb20gJy4uLy4uL3V0aWxzL2VmZm9ydC5qcydcbmltcG9ydCB7IHVwZGF0ZVNldHRpbmdzRm9yU291cmNlIH0gZnJvbSAnLi4vLi4vdXRpbHMvc2V0dGluZ3Mvc2V0dGluZ3MuanMnXG5cbmNvbnN0IENPTU1PTl9IRUxQX0FSR1MgPSBbJ2hlbHAnLCAnLWgnLCAnLS1oZWxwJ11cblxudHlwZSBFZmZvcnRDb21tYW5kUmVzdWx0ID0ge1xuICBtZXNzYWdlOiBzdHJpbmdcbiAgZWZmb3J0VXBkYXRlPzogeyB2YWx1ZTogRWZmb3J0VmFsdWUgfCB1bmRlZmluZWQgfVxufVxuXG5mdW5jdGlvbiBzZXRFZmZvcnRWYWx1ZShlZmZvcnRWYWx1ZTogRWZmb3J0VmFsdWUpOiBFZmZvcnRDb21tYW5kUmVzdWx0IHtcbiAgY29uc3QgcGVyc2lzdGFibGUgPSB0b1BlcnNpc3RhYmxlRWZmb3J0KGVmZm9ydFZhbHVlKVxuICBpZiAocGVyc2lzdGFibGUgIT09IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IHJlc3VsdCA9IHVwZGF0ZVNldHRpbmdzRm9yU291cmNlKCd1c2VyU2V0dGluZ3MnLCB7XG4gICAgICBlZmZvcnRMZXZlbDogcGVyc2lzdGFibGUsXG4gICAgfSlcbiAgICBpZiAocmVzdWx0LmVycm9yKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBtZXNzYWdlOiBgRmFpbGVkIHRvIHNldCBlZmZvcnQgbGV2ZWw6ICR7cmVzdWx0LmVycm9yLm1lc3NhZ2V9YCxcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgbG9nRXZlbnQoJ3Rlbmd1X2VmZm9ydF9jb21tYW5kJywge1xuICAgIGVmZm9ydDpcbiAgICAgIGVmZm9ydFZhbHVlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIH0pXG5cbiAgLy8gRW52IHZhciB3aW5zIGF0IHJlc29sdmVBcHBsaWVkRWZmb3J0IHRpbWUuIE9ubHkgZmxhZyBpdCB3aGVuIGl0IGFjdHVhbGx5XG4gIC8vIGNvbmZsaWN0cyDigJQgaWYgZW52IG1hdGNoZXMgd2hhdCB0aGUgdXNlciBqdXN0IGFza2VkIGZvciwgdGhlIG91dGNvbWUgaXNcbiAgLy8gdGhlIHNhbWUsIHNvIFwiU2V0IGVmZm9ydCB0byBYXCIgaXMgdHJ1ZSBhbmQgdGhlIG5vdGUgaXMgbm9pc2UuXG4gIGNvbnN0IGVudk92ZXJyaWRlID0gZ2V0RWZmb3J0RW52T3ZlcnJpZGUoKVxuICBpZiAoZW52T3ZlcnJpZGUgIT09IHVuZGVmaW5lZCAmJiBlbnZPdmVycmlkZSAhPT0gZWZmb3J0VmFsdWUpIHtcbiAgICBjb25zdCBlbnZSYXcgPSBwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9FRkZPUlRfTEVWRUxcbiAgICBpZiAocGVyc2lzdGFibGUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbWVzc2FnZTogYE5vdCBhcHBsaWVkOiBDTEFVREVfQ09ERV9FRkZPUlRfTEVWRUw9JHtlbnZSYXd9IG92ZXJyaWRlcyBlZmZvcnQgdGhpcyBzZXNzaW9uLCBhbmQgJHtlZmZvcnRWYWx1ZX0gaXMgc2Vzc2lvbi1vbmx5IChub3RoaW5nIHNhdmVkKWAsXG4gICAgICAgIGVmZm9ydFVwZGF0ZTogeyB2YWx1ZTogZWZmb3J0VmFsdWUgfSxcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIG1lc3NhZ2U6IGBDTEFVREVfQ09ERV9FRkZPUlRfTEVWRUw9JHtlbnZSYXd9IG92ZXJyaWRlcyB0aGlzIHNlc3Npb24g4oCUIGNsZWFyIGl0IGFuZCAke2VmZm9ydFZhbHVlfSB0YWtlcyBvdmVyYCxcbiAgICAgIGVmZm9ydFVwZGF0ZTogeyB2YWx1ZTogZWZmb3J0VmFsdWUgfSxcbiAgICB9XG4gIH1cblxuICBjb25zdCBkZXNjcmlwdGlvbiA9IGdldEVmZm9ydFZhbHVlRGVzY3JpcHRpb24oZWZmb3J0VmFsdWUpXG4gIGNvbnN0IHN1ZmZpeCA9IHBlcnNpc3RhYmxlICE9PSB1bmRlZmluZWQgPyAnJyA6ICcgKHRoaXMgc2Vzc2lvbiBvbmx5KSdcbiAgcmV0dXJuIHtcbiAgICBtZXNzYWdlOiBgU2V0IGVmZm9ydCBsZXZlbCB0byAke2VmZm9ydFZhbHVlfSR7c3VmZml4fTogJHtkZXNjcmlwdGlvbn1gLFxuICAgIGVmZm9ydFVwZGF0ZTogeyB2YWx1ZTogZWZmb3J0VmFsdWUgfSxcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2hvd0N1cnJlbnRFZmZvcnQoXG4gIGFwcFN0YXRlRWZmb3J0OiBFZmZvcnRWYWx1ZSB8IHVuZGVmaW5lZCxcbiAgbW9kZWw6IHN0cmluZyxcbik6IEVmZm9ydENvbW1hbmRSZXN1bHQge1xuICBjb25zdCBlbnZPdmVycmlkZSA9IGdldEVmZm9ydEVudk92ZXJyaWRlKClcbiAgY29uc3QgZWZmZWN0aXZlVmFsdWUgPVxuICAgIGVudk92ZXJyaWRlID09PSBudWxsID8gdW5kZWZpbmVkIDogKGVudk92ZXJyaWRlID8/IGFwcFN0YXRlRWZmb3J0KVxuICBpZiAoZWZmZWN0aXZlVmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IGxldmVsID0gZ2V0RGlzcGxheWVkRWZmb3J0TGV2ZWwobW9kZWwsIGFwcFN0YXRlRWZmb3J0KVxuICAgIHJldHVybiB7IG1lc3NhZ2U6IGBFZmZvcnQgbGV2ZWw6IGF1dG8gKGN1cnJlbnRseSAke2xldmVsfSlgIH1cbiAgfVxuICBjb25zdCBkZXNjcmlwdGlvbiA9IGdldEVmZm9ydFZhbHVlRGVzY3JpcHRpb24oZWZmZWN0aXZlVmFsdWUpXG4gIHJldHVybiB7XG4gICAgbWVzc2FnZTogYEN1cnJlbnQgZWZmb3J0IGxldmVsOiAke2VmZmVjdGl2ZVZhbHVlfSAoJHtkZXNjcmlwdGlvbn0pYCxcbiAgfVxufVxuXG5mdW5jdGlvbiB1bnNldEVmZm9ydExldmVsKCk6IEVmZm9ydENvbW1hbmRSZXN1bHQge1xuICBjb25zdCByZXN1bHQgPSB1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSgndXNlclNldHRpbmdzJywge1xuICAgIGVmZm9ydExldmVsOiB1bmRlZmluZWQsXG4gIH0pXG4gIGlmIChyZXN1bHQuZXJyb3IpIHtcbiAgICByZXR1cm4ge1xuICAgICAgbWVzc2FnZTogYEZhaWxlZCB0byBzZXQgZWZmb3J0IGxldmVsOiAke3Jlc3VsdC5lcnJvci5tZXNzYWdlfWAsXG4gICAgfVxuICB9XG4gIGxvZ0V2ZW50KCd0ZW5ndV9lZmZvcnRfY29tbWFuZCcsIHtcbiAgICBlZmZvcnQ6XG4gICAgICAnYXV0bycgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgfSlcbiAgLy8gZW52PWF1dG8vdW5zZXQgKG51bGwpIG1hdGNoZXMgd2hhdCAvZWZmb3J0IGF1dG8gYXNrcyBmb3IsIHNvIG9ubHkgd2FyblxuICAvLyB3aGVuIGVudiBpcyBwaW5uaW5nIGEgc3BlY2lmaWMgbGV2ZWwgdGhhdCB3aWxsIGtlZXAgb3ZlcnJpZGluZy5cbiAgY29uc3QgZW52T3ZlcnJpZGUgPSBnZXRFZmZvcnRFbnZPdmVycmlkZSgpXG4gIGlmIChlbnZPdmVycmlkZSAhPT0gdW5kZWZpbmVkICYmIGVudk92ZXJyaWRlICE9PSBudWxsKSB7XG4gICAgY29uc3QgZW52UmF3ID0gcHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfRUZGT1JUX0xFVkVMXG4gICAgcmV0dXJuIHtcbiAgICAgIG1lc3NhZ2U6IGBDbGVhcmVkIGVmZm9ydCBmcm9tIHNldHRpbmdzLCBidXQgQ0xBVURFX0NPREVfRUZGT1JUX0xFVkVMPSR7ZW52UmF3fSBzdGlsbCBjb250cm9scyB0aGlzIHNlc3Npb25gLFxuICAgICAgZWZmb3J0VXBkYXRlOiB7IHZhbHVlOiB1bmRlZmluZWQgfSxcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHtcbiAgICBtZXNzYWdlOiAnRWZmb3J0IGxldmVsIHNldCB0byBhdXRvJyxcbiAgICBlZmZvcnRVcGRhdGU6IHsgdmFsdWU6IHVuZGVmaW5lZCB9LFxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBleGVjdXRlRWZmb3J0KGFyZ3M6IHN0cmluZyk6IEVmZm9ydENvbW1hbmRSZXN1bHQge1xuICBjb25zdCBub3JtYWxpemVkID0gYXJncy50b0xvd2VyQ2FzZSgpXG4gIGlmIChub3JtYWxpemVkID09PSAnYXV0bycgfHwgbm9ybWFsaXplZCA9PT0gJ3Vuc2V0Jykge1xuICAgIHJldHVybiB1bnNldEVmZm9ydExldmVsKClcbiAgfVxuXG4gIGlmICghaXNFZmZvcnRMZXZlbChub3JtYWxpemVkKSkge1xuICAgIHJldHVybiB7XG4gICAgICBtZXNzYWdlOiBgSW52YWxpZCBhcmd1bWVudDogJHthcmdzfS4gVmFsaWQgb3B0aW9ucyBhcmU6IGxvdywgbWVkaXVtLCBoaWdoLCBtYXgsIGF1dG9gLFxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBzZXRFZmZvcnRWYWx1ZShub3JtYWxpemVkKVxufVxuXG5mdW5jdGlvbiBTaG93Q3VycmVudEVmZm9ydCh7XG4gIG9uRG9uZSxcbn06IHtcbiAgb25Eb25lOiAocmVzdWx0OiBzdHJpbmcpID0+IHZvaWRcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBlZmZvcnRWYWx1ZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5lZmZvcnRWYWx1ZSlcbiAgY29uc3QgbW9kZWwgPSB1c2VNYWluTG9vcE1vZGVsKClcbiAgY29uc3QgeyBtZXNzYWdlIH0gPSBzaG93Q3VycmVudEVmZm9ydChlZmZvcnRWYWx1ZSwgbW9kZWwpXG4gIG9uRG9uZShtZXNzYWdlKVxuICByZXR1cm4gbnVsbFxufVxuXG5mdW5jdGlvbiBBcHBseUVmZm9ydEFuZENsb3NlKHtcbiAgcmVzdWx0LFxuICBvbkRvbmUsXG59OiB7XG4gIHJlc3VsdDogRWZmb3J0Q29tbWFuZFJlc3VsdFxuICBvbkRvbmU6IChyZXN1bHQ6IHN0cmluZykgPT4gdm9pZFxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHNldEFwcFN0YXRlID0gdXNlU2V0QXBwU3RhdGUoKVxuICBjb25zdCB7IGVmZm9ydFVwZGF0ZSwgbWVzc2FnZSB9ID0gcmVzdWx0XG4gIFJlYWN0LnVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGVmZm9ydFVwZGF0ZSkge1xuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiAoe1xuICAgICAgICAuLi5wcmV2LFxuICAgICAgICBlZmZvcnRWYWx1ZTogZWZmb3J0VXBkYXRlLnZhbHVlLFxuICAgICAgfSkpXG4gICAgfVxuICAgIG9uRG9uZShtZXNzYWdlKVxuICB9LCBbc2V0QXBwU3RhdGUsIGVmZm9ydFVwZGF0ZSwgbWVzc2FnZSwgb25Eb25lXSlcbiAgcmV0dXJuIG51bGxcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lLFxuICBfY29udGV4dDogdW5rbm93bixcbiAgYXJncz86IHN0cmluZyxcbik6IFByb21pc2U8UmVhY3QuUmVhY3ROb2RlPiB7XG4gIGFyZ3MgPSBhcmdzPy50cmltKCkgfHwgJydcblxuICBpZiAoQ09NTU9OX0hFTFBfQVJHUy5pbmNsdWRlcyhhcmdzKSkge1xuICAgIG9uRG9uZShcbiAgICAgICdVc2FnZTogL2VmZm9ydCBbbG93fG1lZGl1bXxoaWdofG1heHxhdXRvXVxcblxcbkVmZm9ydCBsZXZlbHM6XFxuLSBsb3c6IFF1aWNrLCBzdHJhaWdodGZvcndhcmQgaW1wbGVtZW50YXRpb25cXG4tIG1lZGl1bTogQmFsYW5jZWQgYXBwcm9hY2ggd2l0aCBzdGFuZGFyZCB0ZXN0aW5nXFxuLSBoaWdoOiBDb21wcmVoZW5zaXZlIGltcGxlbWVudGF0aW9uIHdpdGggZXh0ZW5zaXZlIHRlc3RpbmdcXG4tIG1heDogTWF4aW11bSBjYXBhYmlsaXR5IHdpdGggZGVlcGVzdCByZWFzb25pbmcgKE9wdXMgNC42IG9ubHkpXFxuLSBhdXRvOiBVc2UgdGhlIGRlZmF1bHQgZWZmb3J0IGxldmVsIGZvciB5b3VyIG1vZGVsJyxcbiAgICApXG4gICAgcmV0dXJuXG4gIH1cblxuICBpZiAoIWFyZ3MgfHwgYXJncyA9PT0gJ2N1cnJlbnQnIHx8IGFyZ3MgPT09ICdzdGF0dXMnKSB7XG4gICAgcmV0dXJuIDxTaG93Q3VycmVudEVmZm9ydCBvbkRvbmU9e29uRG9uZX0gLz5cbiAgfVxuXG4gIGNvbnN0IHJlc3VsdCA9IGV4ZWN1dGVFZmZvcnQoYXJncylcbiAgcmV0dXJuIDxBcHBseUVmZm9ydEFuZENsb3NlIHJlc3VsdD17cmVzdWx0fSBvbkRvbmU9e29uRG9uZX0gLz5cbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsZ0JBQWdCLFFBQVEsaUNBQWlDO0FBQ2xFLFNBQ0UsS0FBS0MsMERBQTBELEVBQy9EQyxRQUFRLFFBQ0gsbUNBQW1DO0FBQzFDLFNBQVNDLFdBQVcs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 \ No newline at end of file diff --git a/commands/effort/index.ts b/commands/effort/index.ts new file mode 100644 index 0000000..66cd511 --- /dev/null +++ b/commands/effort/index.ts @@ -0,0 +1,13 @@ +import type { Command } from '../../commands.js' +import { shouldInferenceConfigCommandBeImmediate } from '../../utils/immediateCommand.js' + +export default { + type: 'local-jsx', + name: 'effort', + description: 'Set effort level for model usage', + argumentHint: '[low|medium|high|max|auto]', + get immediate() { + return shouldInferenceConfigCommandBeImmediate() + }, + load: () => import('./effort.js'), +} satisfies Command diff --git a/commands/env/index.js b/commands/env/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/env/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/exit/exit.tsx b/commands/exit/exit.tsx new file mode 100644 index 0000000..0f6f49e --- /dev/null +++ b/commands/exit/exit.tsx @@ -0,0 +1,33 @@ +import { feature } from 'bun:bundle'; +import { spawnSync } from 'child_process'; +import sample from 'lodash-es/sample.js'; +import * as React from 'react'; +import { ExitFlow } from '../../components/ExitFlow.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import { isBgSession } from '../../utils/concurrentSessions.js'; +import { gracefulShutdown } from '../../utils/gracefulShutdown.js'; +import { getCurrentWorktreeSession } from '../../utils/worktree.js'; +const GOODBYE_MESSAGES = ['Goodbye!', 'See ya!', 'Bye!', 'Catch you later!']; +function getRandomGoodbyeMessage(): string { + return sample(GOODBYE_MESSAGES) ?? 'Goodbye!'; +} +export async function call(onDone: LocalJSXCommandOnDone): Promise { + // Inside a `claude --bg` tmux session: detach instead of kill. The REPL + // keeps running; `claude attach` can reconnect. Covers /exit, /quit, + // ctrl+c, ctrl+d — all funnel through here via REPL's handleExit. + if (feature('BG_SESSIONS') && isBgSession()) { + onDone(); + spawnSync('tmux', ['detach-client'], { + stdio: 'ignore' + }); + return null; + } + const showWorktree = getCurrentWorktreeSession() !== null; + if (showWorktree) { + return onDone()} />; + } + onDone(getRandomGoodbyeMessage()); + await gracefulShutdown(0, 'prompt_input_exit'); + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwic3Bhd25TeW5jIiwic2FtcGxlIiwiUmVhY3QiLCJFeGl0RmxvdyIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImlzQmdTZXNzaW9uIiwiZ3JhY2VmdWxTaHV0ZG93biIsImdldEN1cnJlbnRXb3JrdHJlZVNlc3Npb24iLCJHT09EQllFX01FU1NBR0VTIiwiZ2V0UmFuZG9tR29vZGJ5ZU1lc3NhZ2UiLCJjYWxsIiwib25Eb25lIiwiUHJvbWlzZSIsIlJlYWN0Tm9kZSIsInN0ZGlvIiwic2hvd1dvcmt0cmVlIl0sInNvdXJjZXMiOlsiZXhpdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgeyBzcGF3blN5bmMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJ1xuaW1wb3J0IHNhbXBsZSBmcm9tICdsb2Rhc2gtZXMvc2FtcGxlLmpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBFeGl0RmxvdyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvRXhpdEZsb3cuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgeyBpc0JnU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbmN1cnJlbnRTZXNzaW9ucy5qcydcbmltcG9ydCB7IGdyYWNlZnVsU2h1dGRvd24gfSBmcm9tICcuLi8uLi91dGlscy9ncmFjZWZ1bFNodXRkb3duLmpzJ1xuaW1wb3J0IHsgZ2V0Q3VycmVudFdvcmt0cmVlU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL3dvcmt0cmVlLmpzJ1xuXG5jb25zdCBHT09EQllFX01FU1NBR0VTID0gWydHb29kYnllIScsICdTZWUgeWEhJywgJ0J5ZSEnLCAnQ2F0Y2ggeW91IGxhdGVyISddXG5cbmZ1bmN0aW9uIGdldFJhbmRvbUdvb2RieWVNZXNzYWdlKCk6IHN0cmluZyB7XG4gIHJldHVybiBzYW1wbGUoR09PREJZRV9NRVNTQUdFUykgPz8gJ0dvb2RieWUhJ1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICAvLyBJbnNpZGUgYSBgY2xhdWRlIC0tYmdgIHRtdXggc2Vzc2lvbjogZGV0YWNoIGluc3RlYWQgb2Yga2lsbC4gVGhlIFJFUExcbiAgLy8ga2VlcHMgcnVubmluZzsgYGNsYXVkZSBhdHRhY2hgIGNhbiByZWNvbm5lY3QuIENvdmVycyAvZXhpdCwgL3F1aXQsXG4gIC8vIGN0cmwrYywgY3RybCtkIOKAlCBhbGwgZnVubmVsIHRocm91Z2ggaGVyZSB2aWEgUkVQTCdzIGhhbmRsZUV4aXQuXG4gIGlmIChmZWF0dXJlKCdCR19TRVNTSU9OUycpICYmIGlzQmdTZXNzaW9uKCkpIHtcbiAgICBvbkRvbmUoKVxuICAgIHNwYXduU3luYygndG11eCcsIFsnZGV0YWNoLWNsaWVudCddLCB7IHN0ZGlvOiAnaWdub3JlJyB9KVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBzaG93V29ya3RyZWUgPSBnZXRDdXJyZW50V29ya3RyZWVTZXNzaW9uKCkgIT09IG51bGxcblxuICBpZiAoc2hvd1dvcmt0cmVlKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxFeGl0Rmxvd1xuICAgICAgICBzaG93V29ya3RyZWU9e3Nob3dXb3JrdHJlZX1cbiAgICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBvbkRvbmUoKX1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgb25Eb25lKGdldFJhbmRvbUdvb2RieWVNZXNzYWdlKCkpXG4gIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMCwgJ3Byb21wdF9pbnB1dF9leGl0JylcbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsU0FBU0MsU0FBUyxRQUFRLGVBQWU7QUFDekMsT0FBT0MsTUFBTSxNQUFNLHFCQUFxQjtBQUN4QyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFFBQVEsUUFBUSw4QkFBOEI7QUFDdkQsY0FBY0MscUJBQXFCLFFBQVEsd0JBQXdCO0FBQ25FLFNBQVNDLFdBQVcsUUFBUSxtQ0FBbUM7QUFDL0QsU0FBU0MsZ0JBQWdCLFFBQVEsaUNBQWlDO0FBQ2xFLFNBQVNDLHlCQUF5QixRQUFRLHlCQUF5QjtBQUVuRSxNQUFNQyxnQkFBZ0IsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsTUFBTSxFQUFFLGtCQUFrQixDQUFDO0FBRTVFLFNBQVNDLHVCQUF1QkEsQ0FBQSxDQUFFLEVBQUUsTUFBTSxDQUFDO0VBQ3pDLE9BQU9SLE1BQU0sQ0FBQ08sZ0JBQWdCLENBQUMsSUFBSSxVQUFVO0FBQy9DO0FBRUEsT0FBTyxlQUFlRSxJQUFJQSxDQUN4QkMsTUFBTSxFQUFFUCxxQkFBcUIsQ0FDOUIsRUFBRVEsT0FBTyxDQUFDVixLQUFLLENBQUNXLFNBQVMsQ0FBQyxDQUFDO0VBQzFCO0VBQ0E7RUFDQTtFQUNBLElBQUlkLE9BQU8sQ0FBQyxhQUFhLENBQUMsSUFBSU0sV0FBVyxDQUFDLENBQUMsRUFBRTtJQUMzQ00sTUFBTSxDQUFDLENBQUM7SUFDUlgsU0FBUyxDQUFDLE1BQU0sRUFBRSxDQUFDLGVBQWUsQ0FBQyxFQUFFO01BQUVjLEtBQUssRUFBRTtJQUFTLENBQUMsQ0FBQztJQUN6RCxPQUFPLElBQUk7RUFDYjtFQUVBLE1BQU1DLFlBQVksR0FBR1IseUJBQXlCLENBQUMsQ0FBQyxLQUFLLElBQUk7RUFFekQsSUFBSVEsWUFBWSxFQUFFO0lBQ2hCLE9BQ0UsQ0FBQyxRQUFRLENBQ1AsWUFBWSxDQUFDLENBQUNBLFlBQVksQ0FBQyxDQUMzQixNQUFNLENBQUMsQ0FBQ0osTUFBTSxDQUFDLENBQ2YsUUFBUSxDQUFDLENBQUMsTUFBTUEsTUFBTSxDQUFDLENBQUMsQ0FBQyxHQUN6QjtFQUVOO0VBRUFBLE1BQU0sQ0FBQ0YsdUJBQXVCLENBQUMsQ0FBQyxDQUFDO0VBQ2pDLE1BQU1ILGdCQUFnQixDQUFDLENBQUMsRUFBRSxtQkFBbUIsQ0FBQztFQUM5QyxPQUFPLElBQUk7QUFDYiIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/commands/exit/index.ts b/commands/exit/index.ts new file mode 100644 index 0000000..f32499e --- /dev/null +++ b/commands/exit/index.ts @@ -0,0 +1,12 @@ +import type { Command } from '../../commands.js' + +const exit = { + type: 'local-jsx', + name: 'exit', + aliases: ['quit'], + description: 'Exit the REPL', + immediate: true, + load: () => import('./exit.js'), +} satisfies Command + +export default exit diff --git a/commands/export/export.tsx b/commands/export/export.tsx new file mode 100644 index 0000000..c47f5cf --- /dev/null +++ b/commands/export/export.tsx @@ -0,0 +1,91 @@ +import { join } from 'path'; +import React from 'react'; +import { ExportDialog } from '../../components/ExportDialog.js'; +import type { ToolUseContext } from '../../Tool.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import type { Message } from '../../types/message.js'; +import { getCwd } from '../../utils/cwd.js'; +import { renderMessagesToPlainText } from '../../utils/exportRenderer.js'; +import { writeFileSync_DEPRECATED } from '../../utils/slowOperations.js'; +function formatTimestamp(date: Date): string { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + const hours = String(date.getHours()).padStart(2, '0'); + const minutes = String(date.getMinutes()).padStart(2, '0'); + const seconds = String(date.getSeconds()).padStart(2, '0'); + return `${year}-${month}-${day}-${hours}${minutes}${seconds}`; +} +export function extractFirstPrompt(messages: Message[]): string { + const firstUserMessage = messages.find(msg => msg.type === 'user'); + if (!firstUserMessage || firstUserMessage.type !== 'user') { + return ''; + } + const content = firstUserMessage.message?.content; + let result = ''; + if (typeof content === 'string') { + result = content.trim(); + } else if (Array.isArray(content)) { + const textContent = content.find(item => item.type === 'text'); + if (textContent && 'text' in textContent) { + result = textContent.text.trim(); + } + } + + // Take first line only and limit length + result = result.split('\n')[0] || ''; + if (result.length > 50) { + result = result.substring(0, 49) + '…'; + } + return result; +} +export function sanitizeFilename(text: string): string { + // Replace special characters with hyphens + return text.toLowerCase().replace(/[^a-z0-9\s-]/g, '') // Remove special chars + .replace(/\s+/g, '-') // Replace spaces with hyphens + .replace(/-+/g, '-') // Replace multiple hyphens with single + .replace(/^-|-$/g, ''); // Remove leading/trailing hyphens +} +async function exportWithReactRenderer(context: ToolUseContext): Promise { + const tools = context.options.tools || []; + return renderMessagesToPlainText(context.messages, tools); +} +export async function call(onDone: LocalJSXCommandOnDone, context: ToolUseContext, args: string): Promise { + // Render the conversation content + const content = await exportWithReactRenderer(context); + + // If args are provided, write directly to file and skip dialog + const filename = args.trim(); + if (filename) { + const finalFilename = filename.endsWith('.txt') ? filename : filename.replace(/\.[^.]+$/, '') + '.txt'; + const filepath = join(getCwd(), finalFilename); + try { + writeFileSync_DEPRECATED(filepath, content, { + encoding: 'utf-8', + flush: true + }); + onDone(`Conversation exported to: ${filepath}`); + return null; + } catch (error) { + onDone(`Failed to export conversation: ${error instanceof Error ? error.message : 'Unknown error'}`); + return null; + } + } + + // Generate default filename from first prompt or timestamp + const firstPrompt = extractFirstPrompt(context.messages); + const timestamp = formatTimestamp(new Date()); + let defaultFilename: string; + if (firstPrompt) { + const sanitized = sanitizeFilename(firstPrompt); + defaultFilename = sanitized ? `${timestamp}-${sanitized}.txt` : `conversation-${timestamp}.txt`; + } else { + defaultFilename = `conversation-${timestamp}.txt`; + } + + // Return the dialog component when no args provided + return { + onDone(result.message); + }} />; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJqb2luIiwiUmVhY3QiLCJFeHBvcnREaWFsb2ciLCJUb29sVXNlQ29udGV4dCIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsIk1lc3NhZ2UiLCJnZXRDd2QiLCJyZW5kZXJNZXNzYWdlc1RvUGxhaW5UZXh0Iiwid3JpdGVGaWxlU3luY19ERVBSRUNBVEVEIiwiZm9ybWF0VGltZXN0YW1wIiwiZGF0ZSIsIkRhdGUiLCJ5ZWFyIiwiZ2V0RnVsbFllYXIiLCJtb250aCIsIlN0cmluZyIsImdldE1vbnRoIiwicGFkU3RhcnQiLCJkYXkiLCJnZXREYXRlIiwiaG91cnMiLCJnZXRIb3VycyIsIm1pbnV0ZXMiLCJnZXRNaW51dGVzIiwic2Vjb25kcyIsImdldFNlY29uZHMiLCJleHRyYWN0Rmlyc3RQcm9tcHQiLCJtZXNzYWdlcyIsImZpcnN0VXNlck1lc3NhZ2UiLCJmaW5kIiwibXNnIiwidHlwZSIsImNvbnRlbnQiLCJtZXNzYWdlIiwicmVzdWx0IiwidHJpbSIsIkFycmF5IiwiaXNBcnJheSIsInRleHRDb250ZW50IiwiaXRlbSIsInRleHQiLCJzcGxpdCIsImxlbmd0aCIsInN1YnN0cmluZyIsInNhbml0aXplRmlsZW5hbWUiLCJ0b0xvd2VyQ2FzZSIsInJlcGxhY2UiLCJleHBvcnRXaXRoUmVhY3RSZW5kZXJlciIsImNvbnRleHQiLCJQcm9taXNlIiwidG9vbHMiLCJvcHRpb25zIiwiY2FsbCIsIm9uRG9uZSIsImFyZ3MiLCJSZWFjdE5vZGUiLCJmaWxlbmFtZSIsImZpbmFsRmlsZW5hbWUiLCJlbmRzV2l0aCIsImZpbGVwYXRoIiwiZW5jb2RpbmciLCJmbHVzaCIsImVycm9yIiwiRXJyb3IiLCJmaXJzdFByb21wdCIsInRpbWVzdGFtcCIsImRlZmF1bHRGaWxlbmFtZSIsInNhbml0aXplZCJdLCJzb3VyY2VzIjpbImV4cG9ydC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgam9pbiB9IGZyb20gJ3BhdGgnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBFeHBvcnREaWFsb2cgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0V4cG9ydERpYWxvZy5qcydcbmltcG9ydCB0eXBlIHsgVG9vbFVzZUNvbnRleHQgfSBmcm9tICcuLi8uLi9Ub29sLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRPbkRvbmUgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuaW1wb3J0IHR5cGUgeyBNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IGdldEN3ZCB9IGZyb20gJy4uLy4uL3V0aWxzL2N3ZC5qcydcbmltcG9ydCB7IHJlbmRlck1lc3NhZ2VzVG9QbGFpblRleHQgfSBmcm9tICcuLi8uLi91dGlscy9leHBvcnRSZW5kZXJlci5qcydcbmltcG9ydCB7IHdyaXRlRmlsZVN5bmNfREVQUkVDQVRFRCB9IGZyb20gJy4uLy4uL3V0aWxzL3Nsb3dPcGVyYXRpb25zLmpzJ1xuXG5mdW5jdGlvbiBmb3JtYXRUaW1lc3RhbXAoZGF0ZTogRGF0ZSk6IHN0cmluZyB7XG4gIGNvbnN0IHllYXIgPSBkYXRlLmdldEZ1bGxZZWFyKClcbiAgY29uc3QgbW9udGggPSBTdHJpbmcoZGF0ZS5nZXRNb250aCgpICsgMSkucGFkU3RhcnQoMiwgJzAnKVxuICBjb25zdCBkYXkgPSBTdHJpbmcoZGF0ZS5nZXREYXRlKCkpLnBhZFN0YXJ0KDIsICcwJylcbiAgY29uc3QgaG91cnMgPSBTdHJpbmcoZGF0ZS5nZXRIb3VycygpKS5wYWRTdGFydCgyLCAnMCcpXG4gIGNvbnN0IG1pbnV0ZXMgPSBTdHJpbmcoZGF0ZS5nZXRNaW51dGVzKCkpLnBhZFN0YXJ0KDIsICcwJylcbiAgY29uc3Qgc2Vjb25kcyA9IFN0cmluZyhkYXRlLmdldFNlY29uZHMoKSkucGFkU3RhcnQoMiwgJzAnKVxuICByZXR1cm4gYCR7eWVhcn0tJHttb250aH0tJHtkYXl9LSR7aG91cnN9JHttaW51dGVzfSR7c2Vjb25kc31gXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0Rmlyc3RQcm9tcHQobWVzc2FnZXM6IE1lc3NhZ2VbXSk6IHN0cmluZyB7XG4gIGNvbnN0IGZpcnN0VXNlck1lc3NhZ2UgPSBtZXNzYWdlcy5maW5kKG1zZyA9PiBtc2cudHlwZSA9PT0gJ3VzZXInKVxuXG4gIGlmICghZmlyc3RVc2VyTWVzc2FnZSB8fCBmaXJzdFVzZXJNZXNzYWdlLnR5cGUgIT09ICd1c2VyJykge1xuICAgIHJldHVybiAnJ1xuICB9XG5cbiAgY29uc3QgY29udGVudCA9IGZpcnN0VXNlck1lc3NhZ2UubWVzc2FnZT8uY29udGVudFxuICBsZXQgcmVzdWx0ID0gJydcblxuICBpZiAodHlwZW9mIGNvbnRlbnQgPT09ICdzdHJpbmcnKSB7XG4gICAgcmVzdWx0ID0gY29udGVudC50cmltKClcbiAgfSBlbHNlIGlmIChBcnJheS5pc0FycmF5KGNvbnRlbnQpKSB7XG4gICAgY29uc3QgdGV4dENvbnRlbnQgPSBjb250ZW50LmZpbmQoaXRlbSA9PiBpdGVtLnR5cGUgPT09ICd0ZXh0JylcbiAgICBpZiAodGV4dENvbnRlbnQgJiYgJ3RleHQnIGluIHRleHRDb250ZW50KSB7XG4gICAgICByZXN1bHQgPSB0ZXh0Q29udGVudC50ZXh0LnRyaW0oKVxuICAgIH1cbiAgfVxuXG4gIC8vIFRha2UgZmlyc3QgbGluZSBvbmx5IGFuZCBsaW1pdCBsZW5ndGhcbiAgcmVzdWx0ID0gcmVzdWx0LnNwbGl0KCdcXG4nKVswXSB8fCAnJ1xuICBpZiAocmVzdWx0Lmxlbmd0aCA+IDUwKSB7XG4gICAgcmVzdWx0ID0gcmVzdWx0LnN1YnN0cmluZygwLCA0OSkgKyAn4oCmJ1xuICB9XG5cbiAgcmV0dXJuIHJlc3VsdFxufVxuXG5leHBvcnQgZnVuY3Rpb24gc2FuaXRpemVGaWxlbmFtZSh0ZXh0OiBzdHJpbmcpOiBzdHJpbmcge1xuICAvLyBSZXBsYWNlIHNwZWNpYWwgY2hhcmFjdGVycyB3aXRoIGh5cGhlbnNcbiAgcmV0dXJuIHRleHRcbiAgICAudG9Mb3dlckNhc2UoKVxuICAgIC5yZXBsYWNlKC9bXmEtejAtOVxccy1dL2csICcnKSAvLyBSZW1vdmUgc3BlY2lhbCBjaGFyc1xuICAgIC5yZXBsYWNlKC9cXHMrL2csICctJykgLy8gUmVwbGFjZSBzcGFjZXMgd2l0aCBoeXBoZW5zXG4gICAgLnJlcGxhY2UoLy0rL2csICctJykgLy8gUmVwbGFjZSBtdWx0aXBsZSBoeXBoZW5zIHdpdGggc2luZ2xlXG4gICAgLnJlcGxhY2UoL14tfC0kL2csICcnKSAvLyBSZW1vdmUgbGVhZGluZy90cmFpbGluZyBoeXBoZW5zXG59XG5cbmFzeW5jIGZ1bmN0aW9uIGV4cG9ydFdpdGhSZWFjdFJlbmRlcmVyKFxuICBjb250ZXh0OiBUb29sVXNlQ29udGV4dCxcbik6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHRvb2xzID0gY29udGV4dC5vcHRpb25zLnRvb2xzIHx8IFtdXG4gIHJldHVybiByZW5kZXJNZXNzYWdlc1RvUGxhaW5UZXh0KGNvbnRleHQubWVzc2FnZXMsIHRvb2xzKVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4gIGNvbnRleHQ6IFRvb2xVc2VDb250ZXh0LFxuICBhcmdzOiBzdHJpbmcsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICAvLyBSZW5kZXIgdGhlIGNvbnZlcnNhdGlvbiBjb250ZW50XG4gIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBleHBvcnRXaXRoUmVhY3RSZW5kZXJlcihjb250ZXh0KVxuXG4gIC8vIElmIGFyZ3MgYXJlIHByb3ZpZGVkLCB3cml0ZSBkaXJlY3RseSB0byBmaWxlIGFuZCBza2lwIGRpYWxvZ1xuICBjb25zdCBmaWxlbmFtZSA9IGFyZ3MudHJpbSgpXG4gIGlmIChmaWxlbmFtZSkge1xuICAgIGNvbnN0IGZpbmFsRmlsZW5hbWUgPSBmaWxlbmFtZS5lbmRzV2l0aCgnLnR4dCcpXG4gICAgICA/IGZpbGVuYW1lXG4gICAgICA6IGZpbGVuYW1lLnJlcGxhY2UoL1xcLlteLl0rJC8sICcnKSArICcudHh0J1xuICAgIGNvbnN0IGZpbGVwYXRoID0gam9pbihnZXRDd2QoKSwgZmluYWxGaWxlbmFtZSlcblxuICAgIHRyeSB7XG4gICAgICB3cml0ZUZpbGVTeW5jX0RFUFJFQ0FURUQoZmlsZXBhdGgsIGNvbnRlbnQsIHtcbiAgICAgICAgZW5jb2Rpbmc6ICd1dGYtOCcsXG4gICAgICAgIGZsdXNoOiB0cnVlLFxuICAgICAgfSlcbiAgICAgIG9uRG9uZShgQ29udmVyc2F0aW9uIGV4cG9ydGVkIHRvOiAke2ZpbGVwYXRofWApXG4gICAgICByZXR1cm4gbnVsbFxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBvbkRvbmUoXG4gICAgICAgIGBGYWlsZWQgdG8gZXhwb3J0IGNvbnZlcnNhdGlvbjogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6ICdVbmtub3duIGVycm9yJ31gLFxuICAgICAgKVxuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG4gIH1cblxuICAvLyBHZW5lcmF0ZSBkZWZhdWx0IGZpbGVuYW1lIGZyb20gZmlyc3QgcHJvbXB0IG9yIHRpbWVzdGFtcFxuICBjb25zdCBmaXJzdFByb21wdCA9IGV4dHJhY3RGaXJzdFByb21wdChjb250ZXh0Lm1lc3NhZ2VzKVxuICBjb25zdCB0aW1lc3RhbXAgPSBmb3JtYXRUaW1lc3RhbXAobmV3IERhdGUoKSlcblxuICBsZXQgZGVmYXVsdEZpbGVuYW1lOiBzdHJpbmdcbiAgaWYgKGZpcnN0UHJvbXB0KSB7XG4gICAgY29uc3Qgc2FuaXRpemVkID0gc2FuaXRpemVGaWxlbmFtZShmaXJzdFByb21wdClcbiAgICBkZWZhdWx0RmlsZW5hbWUgPSBzYW5pdGl6ZWRcbiAgICAgID8gYCR7dGltZXN0YW1wfS0ke3Nhbml0aXplZH0udHh0YFxuICAgICAgOiBgY29udmVyc2F0aW9uLSR7dGltZXN0YW1wfS50eHRgXG4gIH0gZWxzZSB7XG4gICAgZGVmYXVsdEZpbGVuYW1lID0gYGNvbnZlcnNhdGlvbi0ke3RpbWVzdGFtcH0udHh0YFxuICB9XG5cbiAgLy8gUmV0dXJuIHRoZSBkaWFsb2cgY29tcG9uZW50IHdoZW4gbm8gYXJncyBwcm92aWRlZFxuICByZXR1cm4gKFxuICAgIDxFeHBvcnREaWFsb2dcbiAgICAgIGNvbnRlbnQ9e2NvbnRlbnR9XG4gICAgICBkZWZhdWx0RmlsZW5hbWU9e2RlZmF1bHRGaWxlbmFtZX1cbiAgICAgIG9uRG9uZT17cmVzdWx0ID0+IHtcbiAgICAgICAgb25Eb25lKHJlc3VsdC5tZXNzYWdlKVxuICAgICAgfX1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLElBQUksUUFBUSxNQUFNO0FBQzNCLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLFlBQVksUUFBUSxrQ0FBa0M7QUFDL0QsY0FBY0MsY0FBYyxRQUFRLGVBQWU7QUFDbkQsY0FBY0MscUJBQXFCLFFBQVEsd0JBQXdCO0FBQ25FLGNBQWNDLE9BQU8sUUFBUSx3QkFBd0I7QUFDckQsU0FBU0MsTUFBTSxRQUFRLG9CQUFvQjtBQUMzQyxTQUFTQyx5QkFBeUIsUUFBUSwrQkFBK0I7QUFDekUsU0FBU0Msd0JBQXdCLFFBQVEsK0JBQStCO0FBRXhFLFNBQVNDLGVBQWVBLENBQUNDLElBQUksRUFBRUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxDQUFDO0VBQzNDLE1BQU1DLElBQUksR0FBR0YsSUFBSSxDQUFDRyxXQUFXLENBQUMsQ0FBQztFQUMvQixNQUFNQyxLQUFLLEdBQUdDLE1BQU0sQ0FBQ0wsSUFBSSxDQUFDTSxRQUFRLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDQyxRQUFRLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQztFQUMxRCxNQUFNQyxHQUFHLEdBQUdILE1BQU0sQ0FBQ0wsSUFBSSxDQUFDUyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUNGLFFBQVEsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDO0VBQ25ELE1BQU1HLEtBQUssR0FBR0wsTUFBTSxD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 \ No newline at end of file diff --git a/commands/export/index.ts b/commands/export/index.ts new file mode 100644 index 0000000..a3d8bb2 --- /dev/null +++ b/commands/export/index.ts @@ -0,0 +1,11 @@ +import type { Command } from '../../commands.js' + +const exportCommand = { + type: 'local-jsx', + name: 'export', + description: 'Export the current conversation to a file or clipboard', + argumentHint: '[filename]', + load: () => import('./export.js'), +} satisfies Command + +export default exportCommand diff --git a/commands/extra-usage/extra-usage-core.ts b/commands/extra-usage/extra-usage-core.ts new file mode 100644 index 0000000..4a8c03b --- /dev/null +++ b/commands/extra-usage/extra-usage-core.ts @@ -0,0 +1,118 @@ +import { + checkAdminRequestEligibility, + createAdminRequest, + getMyAdminRequests, +} from '../../services/api/adminRequests.js' +import { invalidateOverageCreditGrantCache } from '../../services/api/overageCreditGrant.js' +import { type ExtraUsage, fetchUtilization } from '../../services/api/usage.js' +import { getSubscriptionType } from '../../utils/auth.js' +import { hasClaudeAiBillingAccess } from '../../utils/billing.js' +import { openBrowser } from '../../utils/browser.js' +import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' +import { logError } from '../../utils/log.js' + +type ExtraUsageResult = + | { type: 'message'; value: string } + | { type: 'browser-opened'; url: string; opened: boolean } + +export async function runExtraUsage(): Promise { + if (!getGlobalConfig().hasVisitedExtraUsage) { + saveGlobalConfig(prev => ({ ...prev, hasVisitedExtraUsage: true })) + } + // Invalidate only the current org's entry so a follow-up read refetches + // the granted state. Separate from the visited flag since users may run + // /extra-usage more than once while iterating on the claim flow. + invalidateOverageCreditGrantCache() + + const subscriptionType = getSubscriptionType() + const isTeamOrEnterprise = + subscriptionType === 'team' || subscriptionType === 'enterprise' + const hasBillingAccess = hasClaudeAiBillingAccess() + + if (!hasBillingAccess && isTeamOrEnterprise) { + // Mirror apps/claude-ai useHasUnlimitedOverage(): if overage is enabled + // with no monthly cap, there is nothing to request. On fetch error, fall + // through and let the user ask (matching web's "err toward show" behavior). + let extraUsage: ExtraUsage | null | undefined + try { + const utilization = await fetchUtilization() + extraUsage = utilization?.extra_usage + } catch (error) { + logError(error as Error) + } + + if (extraUsage?.is_enabled && extraUsage.monthly_limit === null) { + return { + type: 'message', + value: + 'Your organization already has unlimited extra usage. No request needed.', + } + } + + try { + const eligibility = await checkAdminRequestEligibility('limit_increase') + if (eligibility?.is_allowed === false) { + return { + type: 'message', + value: 'Please contact your admin to manage extra usage settings.', + } + } + } catch (error) { + logError(error as Error) + // If eligibility check fails, continue — the create endpoint will enforce if necessary + } + + try { + const pendingOrDismissedRequests = await getMyAdminRequests( + 'limit_increase', + ['pending', 'dismissed'], + ) + if (pendingOrDismissedRequests && pendingOrDismissedRequests.length > 0) { + return { + type: 'message', + value: + 'You have already submitted a request for extra usage to your admin.', + } + } + } catch (error) { + logError(error as Error) + // Fall through to creating a new request below + } + + try { + await createAdminRequest({ + request_type: 'limit_increase', + details: null, + }) + return { + type: 'message', + value: extraUsage?.is_enabled + ? 'Request sent to your admin to increase extra usage.' + : 'Request sent to your admin to enable extra usage.', + } + } catch (error) { + logError(error as Error) + // Fall through to generic message below + } + + return { + type: 'message', + value: 'Please contact your admin to manage extra usage settings.', + } + } + + const url = isTeamOrEnterprise + ? 'https://claude.ai/admin-settings/usage' + : 'https://claude.ai/settings/usage' + + try { + const opened = await openBrowser(url) + return { type: 'browser-opened', url, opened } + } catch (error) { + logError(error as Error) + return { + type: 'message', + value: `Failed to open browser. Please visit ${url} to manage extra usage.`, + } + } +} diff --git a/commands/extra-usage/extra-usage-noninteractive.ts b/commands/extra-usage/extra-usage-noninteractive.ts new file mode 100644 index 0000000..b4eabe8 --- /dev/null +++ b/commands/extra-usage/extra-usage-noninteractive.ts @@ -0,0 +1,16 @@ +import { runExtraUsage } from './extra-usage-core.js' + +export async function call(): Promise<{ type: 'text'; value: string }> { + const result = await runExtraUsage() + + if (result.type === 'message') { + return { type: 'text', value: result.value } + } + + return { + type: 'text', + value: result.opened + ? `Browser opened to manage extra usage. If it didn't open, visit: ${result.url}` + : `Please visit ${result.url} to manage extra usage.`, + } +} diff --git a/commands/extra-usage/extra-usage.tsx b/commands/extra-usage/extra-usage.tsx new file mode 100644 index 0000000..ca27f39 --- /dev/null +++ b/commands/extra-usage/extra-usage.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import type { LocalJSXCommandContext } from '../../commands.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import { Login } from '../login/login.js'; +import { runExtraUsage } from './extra-usage-core.js'; +export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext): Promise { + const result = await runExtraUsage(); + if (result.type === 'message') { + onDone(result.value); + return null; + } + return { + context.onChangeAPIKey(); + onDone(success ? 'Login successful' : 'Login interrupted'); + }} />; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJMb2NhbEpTWENvbW1hbmRPbkRvbmUiLCJMb2dpbiIsInJ1bkV4dHJhVXNhZ2UiLCJjYWxsIiwib25Eb25lIiwiY29udGV4dCIsIlByb21pc2UiLCJSZWFjdE5vZGUiLCJyZXN1bHQiLCJ0eXBlIiwidmFsdWUiLCJzdWNjZXNzIiwib25DaGFuZ2VBUElLZXkiXSwic291cmNlcyI6WyJleHRyYS11c2FnZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDb250ZXh0IH0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQgeyBMb2dpbiB9IGZyb20gJy4uL2xvZ2luL2xvZ2luLmpzJ1xuaW1wb3J0IHsgcnVuRXh0cmFVc2FnZSB9IGZyb20gJy4vZXh0cmEtdXNhZ2UtY29yZS5qcydcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lLFxuICBjb250ZXh0OiBMb2NhbEpTWENvbW1hbmRDb250ZXh0LFxuKTogUHJvbWlzZTxSZWFjdC5SZWFjdE5vZGUgfCBudWxsPiB7XG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJ1bkV4dHJhVXNhZ2UoKVxuXG4gIGlmIChyZXN1bHQudHlwZSA9PT0gJ21lc3NhZ2UnKSB7XG4gICAgb25Eb25lKHJlc3VsdC52YWx1ZSlcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8TG9naW5cbiAgICAgIHN0YXJ0aW5nTWVzc2FnZT17XG4gICAgICAgICdTdGFydGluZyBuZXcgbG9naW4gZm9sbG93aW5nIC9leHRyYS11c2FnZS4gRXhpdCB3aXRoIEN0cmwtQyB0byB1c2UgZXhpc3RpbmcgYWNjb3VudC4nXG4gICAgICB9XG4gICAgICBvbkRvbmU9e3N1Y2Nlc3MgPT4ge1xuICAgICAgICBjb250ZXh0Lm9uQ2hhbmdlQVBJS2V5KClcbiAgICAgICAgb25Eb25lKHN1Y2Nlc3MgPyAnTG9naW4gc3VjY2Vzc2Z1bCcgOiAnTG9naW4gaW50ZXJydXB0ZWQnKVxuICAgICAgfX1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLGNBQWNDLHNCQUFzQixRQUFRLG1CQUFtQjtBQUMvRCxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFDbkUsU0FBU0MsS0FBSyxRQUFRLG1CQUFtQjtBQUN6QyxTQUFTQyxhQUFhLFFBQVEsdUJBQXVCO0FBRXJELE9BQU8sZUFBZUMsSUFBSUEsQ0FDeEJDLE1BQU0sRUFBRUoscUJBQXFCLEVBQzdCSyxPQUFPLEVBQUVOLHNCQUFzQixDQUNoQyxFQUFFTyxPQUFPLENBQUNSLEtBQUssQ0FBQ1MsU0FBUyxHQUFHLElBQUksQ0FBQyxDQUFDO0VBQ2pDLE1BQU1DLE1BQU0sR0FBRyxNQUFNTixhQUFhLENBQUMsQ0FBQztFQUVwQyxJQUFJTSxNQUFNLENBQUNDLElBQUksS0FBSyxTQUFTLEVBQUU7SUFDN0JMLE1BQU0sQ0FBQ0ksTUFBTSxDQUFDRSxLQUFLLENBQUM7SUFDcEIsT0FBTyxJQUFJO0VBQ2I7RUFFQSxPQUNFLENBQUMsS0FBSyxDQUNKLGVBQWUsQ0FBQyxDQUNkLHNGQUNGLENBQUMsQ0FDRCxNQUFNLENBQUMsQ0FBQ0MsT0FBTyxJQUFJO0lBQ2pCTixPQUFPLENBQUNPLGNBQWMsQ0FBQyxDQUFDO0lBQ3hCUixNQUFNLENBQUNPLE9BQU8sR0FBRyxrQkFBa0IsR0FBRyxtQkFBbUIsQ0FBQztFQUM1RCxDQUFDLENBQUMsR0FDRjtBQUVOIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/extra-usage/index.ts b/commands/extra-usage/index.ts new file mode 100644 index 0000000..cea0ba4 --- /dev/null +++ b/commands/extra-usage/index.ts @@ -0,0 +1,31 @@ +import { getIsNonInteractiveSession } from '../../bootstrap/state.js' +import type { Command } from '../../commands.js' +import { isOverageProvisioningAllowed } from '../../utils/auth.js' +import { isEnvTruthy } from '../../utils/envUtils.js' + +function isExtraUsageAllowed(): boolean { + if (isEnvTruthy(process.env.DISABLE_EXTRA_USAGE_COMMAND)) { + return false + } + return isOverageProvisioningAllowed() +} + +export const extraUsage = { + type: 'local-jsx', + name: 'extra-usage', + description: 'Configure extra usage to keep working when limits are hit', + isEnabled: () => isExtraUsageAllowed() && !getIsNonInteractiveSession(), + load: () => import('./extra-usage.js'), +} satisfies Command + +export const extraUsageNonInteractive = { + type: 'local', + name: 'extra-usage', + supportsNonInteractive: true, + description: 'Configure extra usage to keep working when limits are hit', + isEnabled: () => isExtraUsageAllowed() && getIsNonInteractiveSession(), + get isHidden() { + return !getIsNonInteractiveSession() + }, + load: () => import('./extra-usage-noninteractive.js'), +} satisfies Command diff --git a/commands/fast/fast.tsx b/commands/fast/fast.tsx new file mode 100644 index 0000000..398c3c3 --- /dev/null +++ b/commands/fast/fast.tsx @@ -0,0 +1,269 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useState } from 'react'; +import type { CommandResultDisplay, LocalJSXCommandContext } from '../../commands.js'; +import { Dialog } from '../../components/design-system/Dialog.js'; +import { FastIcon, getFastIconString } from '../../components/FastIcon.js'; +import { Box, Link, Text } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../services/analytics/index.js'; +import { type AppState, useAppState, useSetAppState } from '../../state/AppState.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import { clearFastModeCooldown, FAST_MODE_MODEL_DISPLAY, getFastModeModel, getFastModeRuntimeState, getFastModeUnavailableReason, isFastModeEnabled, isFastModeSupportedByModel, prefetchFastModeStatus } from '../../utils/fastMode.js'; +import { formatDuration } from '../../utils/format.js'; +import { formatModelPricing, getOpus46CostTier } from '../../utils/modelCost.js'; +import { updateSettingsForSource } from '../../utils/settings/settings.js'; +function applyFastMode(enable: boolean, setAppState: (f: (prev: AppState) => AppState) => void): void { + clearFastModeCooldown(); + updateSettingsForSource('userSettings', { + fastMode: enable ? true : undefined + }); + if (enable) { + setAppState(prev => { + // Only switch model if current model doesn't support fast mode + const needsModelSwitch = !isFastModeSupportedByModel(prev.mainLoopModel); + return { + ...prev, + ...(needsModelSwitch ? { + mainLoopModel: getFastModeModel(), + mainLoopModelForSession: null + } : {}), + fastMode: true + }; + }); + } else { + setAppState(prev => ({ + ...prev, + fastMode: false + })); + } +} +export function FastModePicker(t0) { + const $ = _c(30); + const { + onDone, + unavailableReason + } = t0; + const model = useAppState(_temp); + const initialFastMode = useAppState(_temp2); + const setAppState = useSetAppState(); + const [enableFastMode, setEnableFastMode] = useState(initialFastMode ?? false); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = getFastModeRuntimeState(); + $[0] = t1; + } else { + t1 = $[0]; + } + const runtimeState = t1; + const isCooldown = runtimeState.status === "cooldown"; + const isUnavailable = unavailableReason !== null; + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = formatModelPricing(getOpus46CostTier(true)); + $[1] = t2; + } else { + t2 = $[1]; + } + const pricing = t2; + let t3; + if ($[2] !== enableFastMode || $[3] !== isUnavailable || $[4] !== model || $[5] !== onDone || $[6] !== setAppState) { + t3 = function handleConfirm() { + if (isUnavailable) { + return; + } + applyFastMode(enableFastMode, setAppState); + logEvent("tengu_fast_mode_toggled", { + enabled: enableFastMode, + source: "picker" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + if (enableFastMode) { + const fastIcon = getFastIconString(enableFastMode); + const modelUpdated = !isFastModeSupportedByModel(model) ? ` · model set to ${FAST_MODE_MODEL_DISPLAY}` : ""; + onDone(`${fastIcon} Fast mode ON${modelUpdated} · ${pricing}`); + } else { + setAppState(_temp3); + onDone("Fast mode OFF"); + } + }; + $[2] = enableFastMode; + $[3] = isUnavailable; + $[4] = model; + $[5] = onDone; + $[6] = setAppState; + $[7] = t3; + } else { + t3 = $[7]; + } + const handleConfirm = t3; + let t4; + if ($[8] !== initialFastMode || $[9] !== isUnavailable || $[10] !== onDone || $[11] !== setAppState) { + t4 = function handleCancel() { + if (isUnavailable) { + if (initialFastMode) { + applyFastMode(false, setAppState); + } + onDone("Fast mode OFF", { + display: "system" + }); + return; + } + const message = initialFastMode ? `${getFastIconString()} Kept Fast mode ON` : "Kept Fast mode OFF"; + onDone(message, { + display: "system" + }); + }; + $[8] = initialFastMode; + $[9] = isUnavailable; + $[10] = onDone; + $[11] = setAppState; + $[12] = t4; + } else { + t4 = $[12]; + } + const handleCancel = t4; + let t5; + if ($[13] !== isUnavailable) { + t5 = function handleToggle() { + if (isUnavailable) { + return; + } + setEnableFastMode(_temp4); + }; + $[13] = isUnavailable; + $[14] = t5; + } else { + t5 = $[14]; + } + const handleToggle = t5; + let t6; + if ($[15] !== handleConfirm || $[16] !== handleToggle) { + t6 = { + "confirm:yes": handleConfirm, + "confirm:nextField": handleToggle, + "confirm:next": handleToggle, + "confirm:previous": handleToggle, + "confirm:cycleMode": handleToggle, + "confirm:toggle": handleToggle + }; + $[15] = handleConfirm; + $[16] = handleToggle; + $[17] = t6; + } else { + t6 = $[17]; + } + let t7; + if ($[18] === Symbol.for("react.memo_cache_sentinel")) { + t7 = { + context: "Confirmation" + }; + $[18] = t7; + } else { + t7 = $[18]; + } + useKeybindings(t6, t7); + let t8; + if ($[19] === Symbol.for("react.memo_cache_sentinel")) { + t8 = Fast mode (research preview); + $[19] = t8; + } else { + t8 = $[19]; + } + const title = t8; + let t9; + if ($[20] !== isUnavailable) { + t9 = exitState => exitState.pending ? Press {exitState.keyName} again to exit : isUnavailable ? Esc to cancel : Tab to toggle · Enter to confirm · Esc to cancel; + $[20] = isUnavailable; + $[21] = t9; + } else { + t9 = $[21]; + } + let t10; + if ($[22] !== enableFastMode || $[23] !== unavailableReason) { + t10 = unavailableReason ? {unavailableReason} : <>Fast mode{enableFastMode ? "ON " : "OFF"}{pricing}{isCooldown && runtimeState.status === "cooldown" && {runtimeState.reason === "overloaded" ? "Fast mode overloaded and is temporarily unavailable" : "You've hit your fast limit"}{" \xB7 resets in "}{formatDuration(runtimeState.resetAt - Date.now(), { + hideTrailingZeros: true + })}}; + $[22] = enableFastMode; + $[23] = unavailableReason; + $[24] = t10; + } else { + t10 = $[24]; + } + let t11; + if ($[25] === Symbol.for("react.memo_cache_sentinel")) { + t11 = Learn more:{" "}https://code.claude.com/docs/en/fast-mode; + $[25] = t11; + } else { + t11 = $[25]; + } + let t12; + if ($[26] !== handleCancel || $[27] !== t10 || $[28] !== t9) { + t12 = {t10}{t11}; + $[26] = handleCancel; + $[27] = t10; + $[28] = t9; + $[29] = t12; + } else { + t12 = $[29]; + } + return t12; +} +function _temp4(prev_0) { + return !prev_0; +} +function _temp3(prev) { + return { + ...prev, + fastMode: false + }; +} +function _temp2(s_0) { + return s_0.fastMode; +} +function _temp(s) { + return s.mainLoopModel; +} +async function handleFastModeShortcut(enable: boolean, getAppState: () => AppState, setAppState: (f: (prev: AppState) => AppState) => void): Promise { + const unavailableReason = getFastModeUnavailableReason(); + if (unavailableReason) { + return `Fast mode unavailable: ${unavailableReason}`; + } + const { + mainLoopModel + } = getAppState(); + applyFastMode(enable, setAppState); + logEvent('tengu_fast_mode_toggled', { + enabled: enable, + source: 'shortcut' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + if (enable) { + const fastIcon = getFastIconString(true); + const modelUpdated = !isFastModeSupportedByModel(mainLoopModel) ? ` · model set to ${FAST_MODE_MODEL_DISPLAY}` : ''; + const pricing = formatModelPricing(getOpus46CostTier(true)); + return `${fastIcon} Fast mode ON${modelUpdated} · ${pricing}`; + } else { + return `Fast mode OFF`; + } +} +export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext, args?: string): Promise { + if (!isFastModeEnabled()) { + return null; + } + + // Fetch org fast mode status before showing the picker. We must know + // whether the org has disabled fast mode before allowing any toggle. + // If a startup prefetch is already in flight, this awaits it. + await prefetchFastModeStatus(); + const arg = args?.trim().toLowerCase(); + if (arg === 'on' || arg === 'off') { + const result = await handleFastModeShortcut(arg === 'on', context.getAppState, context.setAppState); + onDone(result); + return null; + } + const unavailableReason = getFastModeUnavailableReason(); + logEvent('tengu_fast_mode_picker_shown', { + unavailable_reason: (unavailableReason ?? '') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZVN0YXRlIiwiQ29tbWFuZFJlc3VsdERpc3BsYXkiLCJMb2NhbEpTWENvbW1hbmRDb250ZXh0IiwiRGlhbG9nIiwiRmFzdEljb24iLCJnZXRGYXN0SWNvblN0cmluZyIsIkJveCIsIkxpbmsiLCJUZXh0IiwidXNlS2V5YmluZGluZ3MiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJBcHBTdGF0ZSIsInVzZUFwcFN0YXRlIiwidXNlU2V0QXBwU3RhdGUiLCJMb2NhbEpTWENvbW1hbmRPbkRvbmUiLCJjbGVhckZhc3RNb2RlQ29vbGRvd24iLCJGQVNUX01PREVfTU9ERUxfRElTUExBWSIsImdldEZhc3RNb2RlTW9kZWwiLCJnZXRGYXN0TW9kZVJ1bnRpbWVTdGF0ZSIsImdldEZhc3RNb2RlVW5hdmFpbGFibGVSZWFzb24iLCJpc0Zhc3RNb2RlRW5hYmxlZCIsImlzRmFzdE1vZGVTdXBwb3J0ZWRCeU1vZGVsIiwicHJlZmV0Y2hGYXN0TW9kZVN0YXR1cyIsImZvcm1hdER1cmF0aW9uIiwiZm9ybWF0TW9kZWxQcmljaW5nIiwiZ2V0T3B1czQ2Q29zdFRpZXIiLCJ1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSIsImFwcGx5RmFzdE1vZGUiLCJlbmFibGUiLCJzZXRBcHBTdGF0ZSIsImYiLCJwcmV2IiwiZmFzdE1vZGUiLCJ1bmRlZmluZWQiLCJuZWVkc01vZGVsU3dpdGNoIiwibWFpbkxvb3BNb2RlbCIsIm1haW5Mb29wTW9kZWxGb3JTZXNzaW9uIiwiRmFzdE1vZGVQaWNrZXIiLCJ0MCIsIiQiLCJfYyIsIm9uRG9uZSIsInVuYXZhaWxhYmxlUmVhc29uIiwibW9kZWwiLCJfdGVtcCIsImluaXRpYWxGYXN0TW9kZSIsIl90ZW1wMiIsImVuYWJsZUZhc3RNb2RlIiwic2V0RW5hYmxlRmFzdE1vZGUiLCJ0MSIsIlN5bWJvbCIsImZvciIsInJ1bnRpbWVTdGF0ZSIsImlzQ29vbGRvd24iLCJzdGF0dXMiLCJpc1VuYXZhaWxhYmxlIiwidDIiLCJwcmljaW5nIiwidDMiLCJoYW5kbGVDb25maXJtIiwiZW5hYmxlZCIsInNvdXJjZSIsImZhc3RJY29uIiwibW9kZWxVcGRhdGVkIiwiX3RlbXAzIiwidDQiLCJoYW5kbGVDYW5jZWwiLCJkaXNwbGF5IiwibWVzc2FnZSIsInQ1IiwiaGFuZGxlVG9nZ2xlIiwiX3RlbXA0IiwidDYiLCJ0NyIsImNvbnRleHQiLCJ0OCIsInRpdGxlIiwidDkiLCJleGl0U3RhdGUiLCJwZW5kaW5nIiwia2V5TmFtZSIsInQxMCIsInJlYXNvbiIsInJlc2V0QXQiLCJEYXRlIiwibm93IiwiaGlkZVRyYWlsaW5nWmVyb3MiLCJ0MTEiLCJ0MTIiLCJwcmV2XzAiLCJzXzAiLCJzIiwiaGFuZGxlRmFzdE1vZGVTaG9ydGN1dCIsImdldEFwcFN0YXRlIiwiUHJvbWlzZSIsImNhbGwiLCJhcmdzIiwiUmVhY3ROb2RlIiwiYXJnIiwidHJpbSIsInRvTG93ZXJDYXNlIiwicmVzdWx0IiwidW5hdmFpbGFibGVfcmVhc29uIl0sInNvdXJjZXMiOlsiZmFzdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUge1xuICBDb21tYW5kUmVzdWx0RGlzcGxheSxcbiAgTG9jYWxKU1hDb21tYW5kQ29udGV4dCxcbn0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgRmFzdEljb24sIGdldEZhc3RJY29uU3RyaW5nIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9GYXN0SWNvbi5qcydcbmltcG9ydCB7IEJveCwgTGluaywgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmdzIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7XG4gIHR5cGUgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgbG9nRXZlbnQsXG59IGZyb20gJy4uLy4uL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7XG4gIHR5cGUgQXBwU3RhdGUsXG4gIHVzZUFwcFN0YXRlLFxuICB1c2VTZXRBcHBTdGF0ZSxcbn0gZnJvbSAnLi4vLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5pbXBvcnQge1xuICBjbGVhckZhc3RNb2RlQ29vbGRvd24sXG4gIEZBU1RfTU9ERV9NT0RFTF9ESVNQTEFZLFxuICBnZXRGYXN0TW9kZU1vZGVsLFxuICBnZXRGYXN0TW9kZVJ1bnRpbWVTdGF0ZSxcbiAgZ2V0RmFzdE1vZGVVbmF2YWlsYWJsZVJlYXNvbixcbiAgaXNGYXN0TW9kZUVuYWJsZWQsXG4gIGlzRmFzdE1vZGVTdXBwb3J0ZWRCeU1vZGVsLFxuICBwcmVmZXRjaEZhc3RNb2RlU3RhdHVzLFxufSBmcm9tICcuLi8uLi91dGlscy9mYXN0TW9kZS5qcydcbmltcG9ydCB7IGZvcm1hdER1cmF0aW9uIH0gZnJvbSAnLi4vLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgZm9ybWF0TW9kZWxQcmljaW5nLCBnZXRPcHVzNDZDb3N0VGllciB9IGZyb20gJy4uLy4uL3V0aWxzL21vZGVsQ29zdC5qcydcbmltcG9ydCB7IHVwZGF0ZVNldHRpbmdzRm9yU291cmNlIH0gZnJvbSAnLi4vLi4vdXRpbHMvc2V0dGluZ3Mvc2V0dGluZ3MuanMnXG5cbmZ1bmN0aW9uIGFwcGx5RmFzdE1vZGUoXG4gIGVuYWJsZTogYm9vbGVhbixcbiAgc2V0QXBwU3RhdGU6IChmOiAocHJldjogQXBwU3RhdGUpID0+IEFwcFN0YXRlKSA9PiB2b2lkLFxuKTogdm9pZCB7XG4gIGNsZWFyRmFzdE1vZGVDb29sZG93bigpXG4gIHVwZGF0ZVNldHRpbmdzRm9yU291cmNlKCd1c2VyU2V0dGluZ3MnLCB7XG4gICAgZmFzdE1vZGU6IGVuYWJsZSA/IHRydWUgOiB1bmRlZmluZWQsXG4gIH0pXG4gIGlmIChlbmFibGUpIHtcbiAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgIC8vIE9ubHkgc3dpdGNoIG1vZGVsIGlmIGN1cnJlbnQgbW9kZWwgZG9lc24ndCBzdXBwb3J0IGZhc3QgbW9kZVxuICAgICAgY29uc3QgbmVlZHNNb2RlbFN3aXRjaCA9ICFpc0Zhc3RNb2RlU3VwcG9ydGVkQnlNb2RlbChwcmV2Lm1haW5Mb29wTW9kZWwpXG4gICAgICByZXR1cm4ge1xuICAgICAgICAuLi5wcmV2LFxuICAgICAgICAuLi4obmVlZHNNb2RlbFN3aXRjaFxuICAgICAgICAgID8geyBtYWluTG9vcE1vZGVsOiBnZXRGYXN0TW9kZU1vZGVsKCksIG1haW5Mb29wTW9kZWxGb3JTZXNzaW9uOiBudWxsIH1cbiAgICAgICAgICA6IHt9KSxcbiAgICAgICAgZmFzdE1vZGU6IHRydWUsXG4gICAgICB9XG4gICAgfSlcbiAgfSBlbHNlIHtcbiAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7IC4uLnByZXYsIGZhc3RNb2RlOiBmYWxzZSB9KSlcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gRmFzdE1vZGVQaWNrZXIoe1xuICBvbkRvbmUsXG4gIHVuYXZhaWxhYmxlUmVhc29uLFxufToge1xuICBvbkRvbmU6IChcbiAgICByZXN1bHQ/OiBzdHJpbmcsXG4gICAgb3B0aW9ucz86IHsgZGlzcGxheT86IENvbW1hbmRSZXN1bHREaXNwbGF5IH0sXG4gICkgPT4gdm9pZFxuICB1bmF2YWlsYWJsZVJlYXNvbjogc3RyaW5nIHwgbnVsbFxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IG1vZGVsID0gdXNlQXBwU3RhdGUocyA9PiBzLm1haW5Mb29wTW9kZWwpXG4gIGNvbnN0IGluaXRpYWxGYXN0TW9kZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5mYXN0TW9kZSlcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IFtlbmFibGVGYXN0TW9kZSwgc2V0RW5hYmxlRmFzdE1vZGVdID0gdXNlU3RhdGUoaW5pdGlhbEZhc3RNb2RlID8/IGZhbHNlKVxuICBjb25zdCBydW50aW1lU3RhdGUgPSBnZXRGYXN0TW9kZVJ1bnRpbWVTdGF0ZSgpXG4gIGNvbnN0IGlzQ29vbGRvd24gPSBydW50aW1lU3RhdGUuc3RhdHVzID09PSAnY29vbGRvd24nXG4gIGNvbnN0IGlzVW5hdmFpbGFibGUgPSB1bmF2YWlsYWJsZVJlYXNvbiAhPT0gbnVsbFxuICBjb25zdCBwcmljaW5nID0gZm9ybWF0TW9kZWxQcmljaW5nKGdldE9wdXM0NkNvc3RUaWVyKHRydWUpKVxuXG4gIGZ1bmN0aW9uIGhhbmRsZUNvbmZpcm0oKTogdm9pZCB7XG4gICAgaWYgKGlzVW5hdmFpbGFibGUpIHJldHVyblxuICAgIGFwcGx5RmFzdE1vZGUoZW5hYmxlRmFzdE1vZGUsIHNldEFwcFN0YXRlKVxuICAgIGxvZ0V2ZW50KCd0ZW5ndV9mYXN0X21vZGVfdG9nZ2xlZCcsIHtcbiAgICAgIGVuYWJsZWQ6IGVuYWJsZUZhc3RNb2RlLFxuICAgICAgc291cmNlOlxuICAgICAgICAncGlja2VyJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgIH0pXG4gICAgaWYgKGVuYWJsZUZhc3RNb2RlKSB7XG4gICAgICBjb25zdCBmYXN0SWNvbiA9IGdldEZhc3RJY29uU3RyaW5nKGVuYWJsZUZhc3RNb2RlKVxuICAgICAgY29uc3QgbW9kZWxVcGRhdGVkID0gIWlzRmFzdE1vZGVTdXBwb3J0ZWRCeU1vZGVsKG1vZGVsKVxuICAgICAgICA/IGAgwrcgbW9kZWwgc2V0IHRvICR7RkFTVF9NT0RFX01PREVMX0RJU1BMQVl9YFxuICAgICAgICA6ICcnXG4gICAgICBvbkRvbmUoYCR7ZmFzdEljb259IEZhc3QgbW9kZSBPTiR7bW9kZWxVcGRhdGVkfSDCtyAke3ByaWNpbmd9YClcbiAgICB9IGVsc2Uge1xuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiAoeyAuLi5wcmV2LCBmYXN0TW9kZTogZmFsc2UgfSkpXG4gICAgICBvbkRvbmUoYEZhc3QgbW9kZSBPRkZgKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGhhbmRsZUNhbmNlbCgpOiB2b2lkIHtcbiAgICBpZiAoaXNVbmF2YWlsYWJsZSkge1xuICAgICAgLy8gRW5zdXJlIGZhc3QgbW9kZSBpcyBvZmYgaWYgdGhlIG9yZyBoYXMgZGlzYWJsZWQgaXRcbiAgICAgIGlmIChpbml0aWFsRmFzdE1vZGUpIHtcbiAgICAgICAgYXBwbHlGYXN0TW9kZShmYWxzZSwgc2V0QXBwU3RhdGUpXG4gICAgICB9XG4gICAgICBvbkRvbmUoJ0Zhc3QgbW9kZSBPRkYnLCB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0pXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgY29uc3QgbWVzc2FnZSA9IGluaXRpYWxGYXN0TW9kZVxuICAgICAgPyBgJHtnZXRGYXN0SWNvblN0cmluZygpfSBLZXB0IEZhc3QgbW9kZSBPTmBcbiAgICAgIDogYEtlcHQgRmFzdCBtb2RlIE9GRmBcbiAgICBvbkRvbmUobWVzc2FnZSwgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICB9XG5cbiAgZnVuY3Rpb24gaGFuZGxlVG9nZ2xlKCk6IHZvaWQge1xuICAgIGlmIChpc1VuYXZhaWxhYmxlKSByZXR1cm5cbiAgICBzZXRFbmFibGVGYXN0TW9kZShwcmV2ID0+ICFwcmV2KVxuICB9XG5cbiAgdXNlS2V5YmluZGluZ3MoXG4gICAge1xuICAgICAgJ2NvbmZpcm06eWVzJzogaGFuZGxlQ29uZmlybSxcbiAgICAgICdjb25maXJtOm5leHRGaWVsZCc6IGhhbmRsZVRvZ2dsZSxcbiAgICAgICdjb25maXJtOm5leHQnOiBoYW5kbGVUb2dnbGUsXG4gICAgICAnY29uZmlybTpwcmV2aW91cyc6IGhhbmRsZVRvZ2dsZSxcbiAgICAgICdjb25maXJtOmN5Y2xlTW9kZSc6IGhhbmRsZVRvZ2dsZSxcbiAgICAgICdjb25maXJtOnRvZ2dsZSc6IGhhbmRsZVRvZ2dsZSxcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicgfSxcbiAgKVxuXG4gIGNvbnN0IHRpdGxlID0gKFxuICAgIDxUZXh0PlxuICAgICAgPEZhc3RJY29uIGNvb2xkb3duPXtpc0Nvb2xkb3dufSAvPiBGYXN0IG1vZGUgKHJlc2VhcmNoIHByZXZpZXcpXG4gICAgPC9UZXh0PlxuICApXG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT17dGl0bGV9XG4gICAgICBzdWJ0aXRsZT17YEhpZ2gtc3BlZWQgbW9kZSBmb3IgJHtGQVNUX01PREVfTU9ERUxfRElTUExBWX0uIEJpbGxlZCBhcyBleHRyYSB1c2FnZSBhdCBhIHByZW1pdW0gcmF0ZS4gU2VwYXJhdGUgcmF0ZSBsaW1pdHMgYXBwbHkuYH1cbiAgICAgIG9uQ2FuY2VsPXtoYW5kbGVDYW5jZWx9XG4gICAgICBjb2xvcj1cImZhc3RNb2RlXCJcbiAgICAgIGlucHV0R3VpZGU9e2V4aXRTdGF0ZSA9PlxuICAgICAgICBleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICA8VGV4dD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8L1RleHQ+XG4gICAgICAgICkgOiBpc1VuYXZhaWxhYmxlID8gKFxuICAgICAgICAgIDxUZXh0PkVzYyB0byBjYW5jZWw8L1RleHQ+XG4gICAgICAgICkgOiAoXG4gICAgICAgICAgPFRleHQ+VGFiIHRvIHRvZ2dsZSDCtyBFbnRlciB0byBjb25maXJtIMK3IEVzYyB0byBjYW5jZWw8L1RleHQ+XG4gICAgICAgIClcbiAgICAgIH1cbiAgICA+XG4gICAgICB7dW5hdmFpbGFibGVSZWFzb24gPyAoXG4gICAgICAgIDxCb3ggbWFyZ2luTGVmdD17Mn0+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPnt1bmF2YWlsYWJsZVJlYXNvbn08L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKSA6IChcbiAgICAgICAgPD5cbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezB9IG1hcmdpbkxlZnQ9ezJ9PlxuICAgICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXsyfT5cbiAgICAgICAgICAgICAgPFRleHQgYm9sZD5GYXN0IG1vZGU8L1RleHQ+XG4gICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgY29sb3I9e2VuYWJsZUZhc3RNb2RlID8gJ2Zhc3RNb2RlJyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICAgICAgICBib2xkPXtlbmFibGVGYXN0TW9kZX1cbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIHtlbmFibGVGYXN0TW9kZSA/ICdPTiAnIDogJ09GRid9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+e3ByaWNpbmd9PC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgICB7aXNDb29sZG93biAmJiBydW50aW1lU3RhdGUuc3RhdHVzID09PSAnY29vbGRvd24nICYmIChcbiAgICAgICAgICAgIDxCb3ggbWFyZ2luTGVmdD17Mn0+XG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPlxuICAgICAgICAgICAgICAgIHtydW50aW1lU3RhdGUucmVhc29uID09PSAnb3ZlcmxvYWRlZCdcbiAgICAgICAgICAgICAgICAgID8gJ0Zhc3QgbW9kZSBvdmVybG9hZGVkIGFuZCBpcyB0ZW1wb3JhcmlseSB1bmF2YWlsYWJsZSdcbiAgICAgICAgICAgICAgICAgIDogXCJZb3UndmUgaGl0IHlvdXIgZmFzdCBsaW1pdFwifVxuICAgICAgICAgICAgICAgIHsnIMK3IHJlc2V0cyBpbiAnfVxuICAgICAgICAgICAgICAgIHtmb3JtYXREdXJhdGlvbihydW50aW1lU3RhdGUucmVzZXRBdCAtIERhdGUubm93KCksIHtcbiAgICAgICAgICAgICAgICAgIGhpZGVUcmFpbGluZ1plcm9zOiB0cnVlLFxuICAgICAgICAgICAgICAgIH0pfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICApfVxuICAgICAgICA8Lz5cbiAgICAgICl9XG4gICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgTGVhcm4gbW9yZTp7JyAnfVxuICAgICAgICA8TGluayB1cmw9XCJodHRwczovL2NvZGUuY2xhdWRlLmNvbS9kb2NzL2VuL2Zhc3QtbW9kZVwiPlxuICAgICAgICAgIGh0dHBzOi8vY29kZS5jbGF1ZGUuY29tL2RvY3MvZW4vZmFzdC1tb2RlXG4gICAgICAgIDwvTGluaz5cbiAgICAgIDwvVGV4dD5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuXG5hc3luYyBmdW5jdGlvbiBoYW5kbGVGYXN0TW9kZVNob3J0Y3V0KFxuICBlbmFibGU6IGJvb2xlYW4sXG4gIGdldEFwcFN0YXRlOiAoKSA9PiBBcHBTdGF0ZSxcbiAgc2V0QXBwU3RhdGU6IChmOiAocHJldjogQXBwU3RhdGUpID0+IEFwcFN0YXRlKSA9PiB2b2lkLFxuKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgdW5hdmFpbGFibGVSZWFzb24gPSBnZXRGYXN0TW9kZVVuYXZhaWxhYmxlUmVhc29uKClcbiAgaWYgKHVuYXZhaWxhYmxlUmVhc29uKSB7XG4gICAgcmV0dXJuIGBGYXN0IG1vZGUgdW5hdmFpbGFibGU6ICR7dW5hdmFpbGFibGVSZWFzb259YFxuICB9XG5cbiAgY29uc3QgeyBtYWluTG9vcE1vZGVsIH0gPSBnZXRBcHBTdGF0ZSgpXG4gIGFwcGx5RmFzdE1vZGUoZW5hYmxlLCBzZXRBcHBTdGF0ZSlcbiAgbG9nRXZlbnQoJ3Rlbmd1X2Zhc3RfbW9kZV90b2dnbGVkJywge1xuICAgIGVuYWJsZWQ6IGVuYWJsZSxcbiAgICBzb3VyY2U6XG4gICAgICAnc2hvcnRjdXQnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIH0pXG5cbiAgaWYgKGVuYWJsZSkge1xuICAgIGNvbnN0IGZhc3RJY29uID0gZ2V0RmFzdEljb25TdHJpbmcodHJ1ZSlcbiAgICBjb25zdCBtb2RlbFVwZGF0ZWQgPSAhaXNGYXN0TW9kZVN1cHBvcnRlZEJ5TW9kZWwobWFpbkxvb3BNb2RlbClcbiAgICAgID8gYCDCtyBtb2RlbCBzZXQgdG8gJHtGQVNUX01PREVfTU9ERUxfRElTUExBWX1gXG4gICAgICA6ICcnXG4gICAgY29uc3QgcHJpY2luZyA9IGZvcm1hdE1vZGVsUHJpY2luZyhnZXRPcHVzNDZDb3N0VGllcih0cnVlKSlcbiAgICByZXR1cm4gYCR7ZmFzdEljb259IEZhc3QgbW9kZSBPTiR7bW9kZWxVcGRhdGVkfSDCtyAke3ByaWNpbmd9YFxuICB9IGVsc2Uge1xuICAgIHJldHVybiBgRmFzdCBtb2RlIE9GRmBcbiAgfVxufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY2FsbChcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4gIGNvbnRleHQ6IExvY2FsSlNYQ29tbWFuZENvbnRleHQsXG4gIGFyZ3M/OiBzdHJpbmcsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZSB8IG51bGw+IHtcbiAgaWYgKCFpc0Zhc3RNb2RlRW5hYmxlZCgpKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIC8vIEZldGNoIG9yZyBmYXN0IG1vZGUgc3RhdHVzIGJlZm9yZSBzaG93aW5nIHRoZSBwaWNrZXIuIFdlIG11c3Qga25vd1xuICAvLyB3aGV0aGVyIHRoZSBvcmcgaGFzIGRpc2FibGVkIGZhc3QgbW9kZSBiZWZvcmUgYWxsb3dpbmcgYW55IHRvZ2dsZS5cbiAgLy8gSWYgYSBzdGFydHVwIHByZWZldGNoIGlzIGFscmVhZHkgaW4gZmxpZ2h0LCB0aGlzIGF3YWl0cyBpdC5cbiAgYXdhaXQgcHJlZmV0Y2hGYXN0TW9kZVN0YXR1cygpXG5cbiAgY29uc3QgYXJnID0gYXJncz8udHJpbSgpLnRvTG93ZXJDYXNlKClcbiAgaWYgKGFyZyA9PT0gJ29uJyB8fCBhcmcgPT09ICdvZmYnKSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgaGFuZGxlRmFzdE1vZGVTaG9ydGN1dChcbiAgICAgIGFyZyA9PT0gJ29uJyxcbiAgICAgIGNvbnRleHQuZ2V0QXBwU3RhdGUsXG4gICAgICBjb250ZXh0LnNldEFwcFN0YXRlLFxuICAgIClcbiAgICBvbkRvbmUocmVzdWx0KVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCB1bmF2YWlsYWJsZVJlYXNvbiA9IGdldEZhc3RNb2RlVW5hdmFpbGFibGVSZWFzb24oKVxuICBsb2dFdmVudCgndGVuZ3VfZmFzdF9tb2RlX3BpY2tlcl9zaG93bicsIHtcbiAgICB1bmF2YWlsYWJsZV9yZWFzb246ICh1bmF2YWlsYWJsZVJlYXNvbiA/P1xuICAgICAgJycpIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIH0pXG4gIHJldHVybiAoXG4gICAgPEZhc3RNb2RlUGlja2VyIG9uRG9uZT17b25Eb25lfSB1bmF2YWlsYWJsZVJlYXNvbj17dW5hdmFpbGFibGVSZWFzb259IC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsUUFBUSxRQUFRLE9BQU87QUFDaEMsY0FDRUMsb0JBQW9CLEVBQ3BCQyxzQkFBc0IsUUFDakIsbUJBQW1CO0FBQzFCLFNBQVNDLE1BQU0sUUFBUSwwQ0FBMEM7QUFDakUsU0FBU0MsUUFBUSxFQUFFQyxpQkFBaUIsUUFBUSw4QkFBOEI7QUFDMUUsU0FBU0Ms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QUltQixNQUFNLEVBQUU7SUFDVixNQUFNa0MsUUFBUSxHQUFHMUQsaUJBQWlCLENBQUMsSUFBSSxDQUFDO0lBQ3hDLE1BQU0yRCxZQUFZLEdBQUcsQ0FBQzFDLDBCQUEwQixDQUFDYyxhQUFhLENBQUMsR0FDM0QsbUJBQW1CbkIsdUJBQXVCLEVBQUUsR0FDNUMsRUFBRTtJQUNOLE1BQU15QyxPQUFPLEdBQUdqQyxrQkFBa0IsQ0FBQ0MsaUJBQWlCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDM0QsT0FBTyxHQUFHcUMsUUFBUSxnQkFBZ0JDLFlBQVksTUFBTU4sT0FBTyxFQUFFO0VBQy9ELENBQUMsTUFBTTtJQUNMLE9BQU8sZUFBZTtFQUN4QjtBQUNGO0FBRUEsT0FBTyxlQUFlc0MsSUFBSUEsQ0FDeEJ0RCxNQUFNLEVBQUUzQixxQkFBcUIsRUFDN0I0RCxPQUFPLEVBQUV6RSxzQkFBc0IsRUFDL0IrRixJQUFhLENBQVIsRUFBRSxNQUFNLENBQ2QsRUFBRUYsT0FBTyxDQUFDaEcsS0FBSyxDQUFDbUcsU0FBUyxHQUFHLElBQUksQ0FBQyxDQUFDO0VBQ2pDLElBQUksQ0FBQzdFLGlCQUFpQixDQUFDLENBQUMsRUFBRTtJQUN4QixPQUFPLElBQUk7RUFDYjs7RUFFQTtFQUNBO0VBQ0E7RUFDQSxNQUFNRSxzQkFBc0IsQ0FBQyxDQUFDO0VBRTlCLE1BQU00RSxHQUFHLEdBQUdGLElBQUksRUFBRUcsSUFBSSxDQUFDLENBQUMsQ0FBQ0MsV0FBVyxDQUFDLENBQUM7RUFDdEMsSUFBSUYsR0FBRyxLQUFLLElBQUksSUFBSUEsR0FBRyxLQUFLLEtBQUssRUFBRTtJQUNqQyxNQUFNRyxNQUFNLEdBQUcsTUFBTVQsc0JBQXNCLENBQ3pDTSxHQUFHLEtBQUssSUFBSSxFQUNaeEIsT0FBTyxDQUFDbUIsV0FBVyxFQUNuQm5CLE9BQU8sQ0FBQzdDLFdBQ1YsQ0FBQztJQUNEWSxNQUFNLENBQUM0RCxNQUFNLENBQUM7SUFDZCxPQUFPLElBQUk7RUFDYjtFQUVBLE1BQU0zRCxpQkFBaUIsR0FBR3ZCLDRCQUE0QixDQUFDLENBQUM7RUFDeERULFFBQVEsQ0FBQyw4QkFBOEIsRUFBRTtJQUN2QzRGLGtCQUFrQixFQUFFLENBQUM1RCxpQkFBaUIsSUFDcEMsRUFBRSxLQUFLakM7RUFDWCxDQUFDLENBQUM7RUFDRixPQUNFLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDZ0MsTUFBTSxDQUFDLENBQUMsaUJBQWlCLENBQUMsQ0FBQ0MsaUJBQWlCLENBQUMsR0FBRztBQUU1RSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/commands/fast/index.ts b/commands/fast/index.ts new file mode 100644 index 0000000..88ed550 --- /dev/null +++ b/commands/fast/index.ts @@ -0,0 +1,26 @@ +import type { Command } from '../../commands.js' +import { + FAST_MODE_MODEL_DISPLAY, + isFastModeEnabled, +} from '../../utils/fastMode.js' +import { shouldInferenceConfigCommandBeImmediate } from '../../utils/immediateCommand.js' + +const fast = { + type: 'local-jsx', + name: 'fast', + get description() { + return `Toggle fast mode (${FAST_MODE_MODEL_DISPLAY} only)` + }, + availability: ['claude-ai', 'console'], + isEnabled: () => isFastModeEnabled(), + get isHidden() { + return !isFastModeEnabled() + }, + argumentHint: '[on|off]', + get immediate() { + return shouldInferenceConfigCommandBeImmediate() + }, + load: () => import('./fast.js'), +} satisfies Command + +export default fast diff --git a/commands/feedback/feedback.tsx b/commands/feedback/feedback.tsx new file mode 100644 index 0000000..43828b0 --- /dev/null +++ b/commands/feedback/feedback.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import type { CommandResultDisplay, LocalJSXCommandContext } from '../../commands.js'; +import { Feedback } from '../../components/Feedback.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import type { Message } from '../../types/message.js'; + +// Shared function to render the Feedback component +export function renderFeedbackComponent(onDone: (result?: string, options?: { + display?: CommandResultDisplay; +}) => void, abortSignal: AbortSignal, messages: Message[], initialDescription: string = '', backgroundTasks: { + [taskId: string]: { + type: string; + identity?: { + agentId: string; + }; + messages?: Message[]; + }; +} = {}): React.ReactNode { + return ; +} +export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext, args?: string): Promise { + const initialDescription = args || ''; + return renderFeedbackComponent(onDone, context.abortController.signal, context.messages, initialDescription); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiTG9jYWxKU1hDb21tYW5kQ29udGV4dCIsIkZlZWRiYWNrIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiTWVzc2FnZSIsInJlbmRlckZlZWRiYWNrQ29tcG9uZW50Iiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJhYm9ydFNpZ25hbCIsIkFib3J0U2lnbmFsIiwibWVzc2FnZXMiLCJpbml0aWFsRGVzY3JpcHRpb24iLCJiYWNrZ3JvdW5kVGFza3MiLCJ0YXNrSWQiLCJ0eXBlIiwiaWRlbnRpdHkiLCJhZ2VudElkIiwiUmVhY3ROb2RlIiwiY2FsbCIsImNvbnRleHQiLCJhcmdzIiwiUHJvbWlzZSIsImFib3J0Q29udHJvbGxlciIsInNpZ25hbCJdLCJzb3VyY2VzIjpbImZlZWRiYWNrLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHtcbiAgQ29tbWFuZFJlc3VsdERpc3BsYXksXG4gIExvY2FsSlNYQ29tbWFuZENvbnRleHQsXG59IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgRmVlZGJhY2sgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0ZlZWRiYWNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRPbkRvbmUgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuaW1wb3J0IHR5cGUgeyBNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcblxuLy8gU2hhcmVkIGZ1bmN0aW9uIHRvIHJlbmRlciB0aGUgRmVlZGJhY2sgY29tcG9uZW50XG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyRmVlZGJhY2tDb21wb25lbnQoXG4gIG9uRG9uZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkLFxuICBhYm9ydFNpZ25hbDogQWJvcnRTaWduYWwsXG4gIG1lc3NhZ2VzOiBNZXNzYWdlW10sXG4gIGluaXRpYWxEZXNjcmlwdGlvbjogc3RyaW5nID0gJycsXG4gIGJhY2tncm91bmRUYXNrczoge1xuICAgIFt0YXNrSWQ6IHN0cmluZ106IHtcbiAgICAgIHR5cGU6IHN0cmluZ1xuICAgICAgaWRlbnRpdHk/OiB7IGFnZW50SWQ6IHN0cmluZyB9XG4gICAgICBtZXNzYWdlcz86IE1lc3NhZ2VbXVxuICAgIH1cbiAgfSA9IHt9LFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8RmVlZGJhY2tcbiAgICAgIGFib3J0U2lnbmFsPXthYm9ydFNpZ25hbH1cbiAgICAgIG1lc3NhZ2VzPXttZXNzYWdlc31cbiAgICAgIGluaXRpYWxEZXNjcmlwdGlvbj17aW5pdGlhbERlc2NyaXB0aW9ufVxuICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICBiYWNrZ3JvdW5kVGFza3M9e2JhY2tncm91bmRUYXNrc31cbiAgICAvPlxuICApXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogTG9jYWxKU1hDb21tYW5kQ29udGV4dCxcbiAgYXJncz86IHN0cmluZyxcbik6IFByb21pc2U8UmVhY3QuUmVhY3ROb2RlPiB7XG4gIGNvbnN0IGluaXRpYWxEZXNjcmlwdGlvbiA9IGFyZ3MgfHwgJydcbiAgcmV0dXJuIHJlbmRlckZlZWRiYWNrQ29tcG9uZW50KFxuICAgIG9uRG9uZSxcbiAgICBjb250ZXh0LmFib3J0Q29udHJvbGxlci5zaWduYWwsXG4gICAgY29udGV4dC5tZXNzYWdlcyxcbiAgICBpbml0aWFsRGVzY3JpcHRpb24sXG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixjQUNFQyxvQkFBb0IsRUFDcEJDLHNCQUFzQixRQUNqQixtQkFBbUI7QUFDMUIsU0FBU0MsUUFBUSxRQUFRLDhCQUE4QjtBQUN2RCxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFDbkUsY0FBY0MsT0FBTyxRQUFRLHdCQUF3Qjs7QUFFckQ7QUFDQSxPQUFPLFNBQVNDLHVCQUF1QkEsQ0FDckNDLE1BQU0sRUFBRSxDQUNOQyxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQ2ZDLE9BQTRDLENBQXBDLEVBQUU7RUFBRUMsT0FBTyxDQUFDLEVBQUVULG9CQUFvQjtBQUFDLENBQUMsRUFDNUMsR0FBRyxJQUFJLEVBQ1RVLFdBQVcsRUFBRUMsV0FBVyxFQUN4QkMsUUFBUSxFQUFFUixPQUFPLEVBQUUsRUFDbkJTLGtCQUFrQixFQUFFLE1BQU0sR0FBRyxFQUFFLEVBQy9CQyxlQUFlLEVBQUU7RUFDZixDQUFDQyxNQUFNLEVBQUUsTUFBTSxDQUFDLEVBQUU7SUFDaEJDLElBQUksRUFBRSxNQUFNO0lBQ1pDLFFBQVEsQ0FBQyxFQUFFO01BQUVDLE9BQU8sRUFBRSxNQUFNO0lBQUMsQ0FBQztJQUM5Qk4sUUFBUSxDQUFDLEVBQUVSLE9BQU8sRUFBRTtFQUN0QixDQUFDO0FBQ0gsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUNQLEVBQUVMLEtBQUssQ0FBQ29CLFNBQVMsQ0FBQztFQUNqQixPQUNFLENBQUMsUUFBUSxDQUNQLFdBQVcsQ0FBQyxDQUFDVCxXQUFXLENBQUMsQ0FDekIsUUFBUSxDQUFDLENBQUNFLFFBQVEsQ0FBQyxDQUNuQixrQkFBa0IsQ0FBQyxDQUFDQyxrQkFBa0IsQ0FBQyxDQUN2QyxNQUFNLENBQUMsQ0FBQ1AsTUFBTSxDQUFDLENBQ2YsZUFBZSxDQUFDLENBQUNRLGVBQWUsQ0FBQyxHQUNqQztBQUVOO0FBRUEsT0FBTyxlQUFlTSxJQUFJQSxDQUN4QmQsTUFBTSxFQUFFSCxxQkFBcUIsRUFDN0JrQixPQUFPLEVBQUVwQixzQkFBc0IsRUFDL0JxQixJQUFhLENBQVIsRUFBRSxNQUFNLENBQ2QsRUFBRUMsT0FBTyxDQUFDeEIsS0FBSyxDQUFDb0IsU0FBUyxDQUFDLENBQUM7RUFDMUIsTUFBTU4sa0JBQWtCLEdBQUdTLElBQUksSUFBSSxFQUFFO0VBQ3JDLE9BQU9qQix1QkFBdUIsQ0FDNUJDLE1BQU0sRUFDTmUsT0FBTyxDQUFDRyxlQUFlLENBQUNDLE1BQU0sRUFDOUJKLE9BQU8sQ0FBQ1QsUUFBUSxFQUNoQkMsa0JBQ0YsQ0FBQztBQUNIIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/feedback/index.ts b/commands/feedback/index.ts new file mode 100644 index 0000000..ec092c8 --- /dev/null +++ b/commands/feedback/index.ts @@ -0,0 +1,26 @@ +import type { Command } from '../../commands.js' +import { isPolicyAllowed } from '../../services/policyLimits/index.js' +import { isEnvTruthy } from '../../utils/envUtils.js' +import { isEssentialTrafficOnly } from '../../utils/privacyLevel.js' + +const feedback = { + aliases: ['bug'], + type: 'local-jsx', + name: 'feedback', + description: `Submit feedback about Claude Code`, + argumentHint: '[report]', + isEnabled: () => + !( + isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) || + isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) || + isEnvTruthy(process.env.CLAUDE_CODE_USE_FOUNDRY) || + isEnvTruthy(process.env.DISABLE_FEEDBACK_COMMAND) || + isEnvTruthy(process.env.DISABLE_BUG_COMMAND) || + isEssentialTrafficOnly() || + process.env.USER_TYPE === 'ant' || + !isPolicyAllowed('allow_product_feedback') + ), + load: () => import('./feedback.js'), +} satisfies Command + +export default feedback diff --git a/commands/files/files.ts b/commands/files/files.ts new file mode 100644 index 0000000..6da238b --- /dev/null +++ b/commands/files/files.ts @@ -0,0 +1,19 @@ +import { relative } from 'path' +import type { ToolUseContext } from '../../Tool.js' +import type { LocalCommandResult } from '../../types/command.js' +import { getCwd } from '../../utils/cwd.js' +import { cacheKeys } from '../../utils/fileStateCache.js' + +export async function call( + _args: string, + context: ToolUseContext, +): Promise { + const files = context.readFileState ? cacheKeys(context.readFileState) : [] + + if (files.length === 0) { + return { type: 'text' as const, value: 'No files in context' } + } + + const fileList = files.map(file => relative(getCwd(), file)).join('\n') + return { type: 'text' as const, value: `Files in context:\n${fileList}` } +} diff --git a/commands/files/index.ts b/commands/files/index.ts new file mode 100644 index 0000000..984b2d3 --- /dev/null +++ b/commands/files/index.ts @@ -0,0 +1,12 @@ +import type { Command } from '../../commands.js' + +const files = { + type: 'local', + name: 'files', + description: 'List all files currently in context', + isEnabled: () => process.env.USER_TYPE === 'ant', + supportsNonInteractive: true, + load: () => import('./files.js'), +} satisfies Command + +export default files diff --git a/commands/good-claude/index.js b/commands/good-claude/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/good-claude/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/heapdump/heapdump.ts b/commands/heapdump/heapdump.ts new file mode 100644 index 0000000..75dd90e --- /dev/null +++ b/commands/heapdump/heapdump.ts @@ -0,0 +1,17 @@ +import { performHeapDump } from '../../utils/heapDumpService.js' + +export async function call(): Promise<{ type: 'text'; value: string }> { + const result = await performHeapDump() + + if (!result.success) { + return { + type: 'text', + value: `Failed to create heap dump: ${result.error}`, + } + } + + return { + type: 'text', + value: `${result.heapPath}\n${result.diagPath}`, + } +} diff --git a/commands/heapdump/index.ts b/commands/heapdump/index.ts new file mode 100644 index 0000000..11628ae --- /dev/null +++ b/commands/heapdump/index.ts @@ -0,0 +1,12 @@ +import type { Command } from '../../commands.js' + +const heapDump = { + type: 'local', + name: 'heapdump', + description: 'Dump the JS heap to ~/Desktop', + isHidden: true, + supportsNonInteractive: true, + load: () => import('./heapdump.js'), +} satisfies Command + +export default heapDump diff --git a/commands/help/help.tsx b/commands/help/help.tsx new file mode 100644 index 0000000..2d86e71 --- /dev/null +++ b/commands/help/help.tsx @@ -0,0 +1,11 @@ +import * as React from 'react'; +import { HelpV2 } from '../../components/HelpV2/HelpV2.js'; +import type { LocalJSXCommandCall } from '../../types/command.js'; +export const call: LocalJSXCommandCall = async (onDone, { + options: { + commands + } +}) => { + return ; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkhlbHBWMiIsIkxvY2FsSlNYQ29tbWFuZENhbGwiLCJjYWxsIiwib25Eb25lIiwib3B0aW9ucyIsImNvbW1hbmRzIl0sInNvdXJjZXMiOlsiaGVscC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBIZWxwVjIgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0hlbHBWMi9IZWxwVjIuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZENhbGwgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuXG5leHBvcnQgY29uc3QgY2FsbDogTG9jYWxKU1hDb21tYW5kQ2FsbCA9IGFzeW5jIChcbiAgb25Eb25lLFxuICB7IG9wdGlvbnM6IHsgY29tbWFuZHMgfSB9LFxuKSA9PiB7XG4gIHJldHVybiA8SGVscFYyIGNvbW1hbmRzPXtjb21tYW5kc30gb25DbG9zZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLE1BQU0sUUFBUSxtQ0FBbUM7QUFDMUQsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFBQyxDQUN2Q0MsTUFBTSxFQUNOO0VBQUVDLE9BQU8sRUFBRTtJQUFFQztFQUFTO0FBQUUsQ0FBQyxLQUN0QjtFQUNILE9BQU8sQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUNBLFFBQVEsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDRixNQUFNLENBQUMsR0FBRztBQUN4RCxDQUFDIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/help/index.ts b/commands/help/index.ts new file mode 100644 index 0000000..31f465d --- /dev/null +++ b/commands/help/index.ts @@ -0,0 +1,10 @@ +import type { Command } from '../../commands.js' + +const help = { + type: 'local-jsx', + name: 'help', + description: 'Show help and available commands', + load: () => import('./help.js'), +} satisfies Command + +export default help diff --git a/commands/hooks/hooks.tsx b/commands/hooks/hooks.tsx new file mode 100644 index 0000000..c399454 --- /dev/null +++ b/commands/hooks/hooks.tsx @@ -0,0 +1,13 @@ +import * as React from 'react'; +import { HooksConfigMenu } from '../../components/hooks/HooksConfigMenu.js'; +import { logEvent } from '../../services/analytics/index.js'; +import { getTools } from '../../tools.js'; +import type { LocalJSXCommandCall } from '../../types/command.js'; +export const call: LocalJSXCommandCall = async (onDone, context) => { + logEvent('tengu_hooks_command', {}); + const appState = context.getAppState(); + const permissionContext = appState.toolPermissionContext; + const toolNames = getTools(permissionContext).map(tool => tool.name); + return ; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkhvb2tzQ29uZmlnTWVudSIsImxvZ0V2ZW50IiwiZ2V0VG9vbHMiLCJMb2NhbEpTWENvbW1hbmRDYWxsIiwiY2FsbCIsIm9uRG9uZSIsImNvbnRleHQiLCJhcHBTdGF0ZSIsImdldEFwcFN0YXRlIiwicGVybWlzc2lvbkNvbnRleHQiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJ0b29sTmFtZXMiLCJtYXAiLCJ0b29sIiwibmFtZSJdLCJzb3VyY2VzIjpbImhvb2tzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEhvb2tzQ29uZmlnTWVudSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvaG9va3MvSG9va3NDb25maWdNZW51LmpzJ1xuaW1wb3J0IHsgbG9nRXZlbnQgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hbmFseXRpY3MvaW5kZXguanMnXG5pbXBvcnQgeyBnZXRUb29scyB9IGZyb20gJy4uLy4uL3Rvb2xzLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0KSA9PiB7XG4gIGxvZ0V2ZW50KCd0ZW5ndV9ob29rc19jb21tYW5kJywge30pXG4gIGNvbnN0IGFwcFN0YXRlID0gY29udGV4dC5nZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IHBlcm1pc3Npb25Db250ZXh0ID0gYXBwU3RhdGUudG9vbFBlcm1pc3Npb25Db250ZXh0XG4gIGNvbnN0IHRvb2xOYW1lcyA9IGdldFRvb2xzKHBlcm1pc3Npb25Db250ZXh0KS5tYXAodG9vbCA9PiB0b29sLm5hbWUpXG4gIHJldHVybiA8SG9va3NDb25maWdNZW51IHRvb2xOYW1lcz17dG9vbE5hbWVzfSBvbkV4aXQ9e29uRG9uZX0gLz5cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxlQUFlLFFBQVEsMkNBQTJDO0FBQzNFLFNBQVNDLFFBQVEsUUFBUSxtQ0FBbUM7QUFDNUQsU0FBU0MsUUFBUSxRQUFRLGdCQUFnQjtBQUN6QyxjQUFjQyxtQkFBbUIsUUFBUSx3QkFBd0I7QUFFakUsT0FBTyxNQUFNQyxJQUFJLEVBQUVELG1CQUFtQixHQUFHLE1BQUFDLENBQU9DLE1BQU0sRUFBRUMsT0FBTyxLQUFLO0VBQ2xFTCxRQUFRLENBQUMscUJBQXFCLEVBQUUsQ0FBQyxDQUFDLENBQUM7RUFDbkMsTUFBTU0sUUFBUSxHQUFHRCxPQUFPLENBQUNFLFdBQVcsQ0FBQyxDQUFDO0VBQ3RDLE1BQU1DLGlCQUFpQixHQUFHRixRQUFRLENBQUNHLHFCQUFxQjtFQUN4RCxNQUFNQyxTQUFTLEdBQUdULFFBQVEsQ0FBQ08saUJBQWlCLENBQUMsQ0FBQ0csR0FBRyxDQUFDQyxJQUFJLElBQUlBLElBQUksQ0FBQ0MsSUFBSSxDQUFDO0VBQ3BFLE9BQU8sQ0FBQyxlQUFlLENBQUMsU0FBUyxDQUFDLENBQUNILFNBQVMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDTixNQUFNLENBQUMsR0FBRztBQUNsRSxDQUFDIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/hooks/index.ts b/commands/hooks/index.ts new file mode 100644 index 0000000..4567dbf --- /dev/null +++ b/commands/hooks/index.ts @@ -0,0 +1,11 @@ +import type { Command } from '../../commands.js' + +const hooks = { + type: 'local-jsx', + name: 'hooks', + description: 'View hook configurations for tool events', + immediate: true, + load: () => import('./hooks.js'), +} satisfies Command + +export default hooks diff --git a/commands/ide/ide.tsx b/commands/ide/ide.tsx new file mode 100644 index 0000000..0a41b97 --- /dev/null +++ b/commands/ide/ide.tsx @@ -0,0 +1,646 @@ +import { c as _c } from "react/compiler-runtime"; +import chalk from 'chalk'; +import * as path from 'path'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { logEvent } from 'src/services/analytics/index.js'; +import type { CommandResultDisplay, LocalJSXCommandContext } from '../../commands.js'; +import { Select } from '../../components/CustomSelect/index.js'; +import { Dialog } from '../../components/design-system/Dialog.js'; +import { IdeAutoConnectDialog, IdeDisableAutoConnectDialog, shouldShowAutoConnectDialog, shouldShowDisableAutoConnectDialog } from '../../components/IdeAutoConnectDialog.js'; +import { Box, Text } from '../../ink.js'; +import { clearServerCache } from '../../services/mcp/client.js'; +import type { ScopedMcpServerConfig } from '../../services/mcp/types.js'; +import { useAppState, useSetAppState } from '../../state/AppState.js'; +import { getCwd } from '../../utils/cwd.js'; +import { execFileNoThrow } from '../../utils/execFileNoThrow.js'; +import { type DetectedIDEInfo, detectIDEs, detectRunningIDEs, type IdeType, isJetBrainsIde, isSupportedJetBrainsTerminal, isSupportedTerminal, toIDEDisplayName } from '../../utils/ide.js'; +import { getCurrentWorktreeSession } from '../../utils/worktree.js'; +type IDEScreenProps = { + availableIDEs: DetectedIDEInfo[]; + unavailableIDEs: DetectedIDEInfo[]; + selectedIDE?: DetectedIDEInfo | null; + onClose: () => void; + onSelect: (ide?: DetectedIDEInfo) => void; +}; +function IDEScreen(t0) { + const $ = _c(39); + const { + availableIDEs, + unavailableIDEs, + selectedIDE, + onClose, + onSelect + } = t0; + let t1; + if ($[0] !== selectedIDE?.port) { + t1 = selectedIDE?.port?.toString() ?? "None"; + $[0] = selectedIDE?.port; + $[1] = t1; + } else { + t1 = $[1]; + } + const [selectedValue, setSelectedValue] = useState(t1); + const [showAutoConnectDialog, setShowAutoConnectDialog] = useState(false); + const [showDisableAutoConnectDialog, setShowDisableAutoConnectDialog] = useState(false); + let t2; + if ($[2] !== availableIDEs || $[3] !== onSelect) { + t2 = value => { + if (value !== "None" && shouldShowAutoConnectDialog()) { + setShowAutoConnectDialog(true); + } else { + if (value === "None" && shouldShowDisableAutoConnectDialog()) { + setShowDisableAutoConnectDialog(true); + } else { + onSelect(availableIDEs.find(ide => ide.port === parseInt(value))); + } + } + }; + $[2] = availableIDEs; + $[3] = onSelect; + $[4] = t2; + } else { + t2 = $[4]; + } + const handleSelectIDE = t2; + let t3; + if ($[5] !== availableIDEs) { + t3 = availableIDEs.reduce(_temp, {}); + $[5] = availableIDEs; + $[6] = t3; + } else { + t3 = $[6]; + } + const ideCounts = t3; + let t4; + if ($[7] !== availableIDEs || $[8] !== ideCounts) { + let t5; + if ($[10] !== ideCounts) { + t5 = ide_1 => { + const hasMultipleInstances = (ideCounts[ide_1.name] || 0) > 1; + const showWorkspace = hasMultipleInstances && ide_1.workspaceFolders.length > 0; + return { + label: ide_1.name, + value: ide_1.port.toString(), + description: showWorkspace ? formatWorkspaceFolders(ide_1.workspaceFolders) : undefined + }; + }; + $[10] = ideCounts; + $[11] = t5; + } else { + t5 = $[11]; + } + t4 = availableIDEs.map(t5).concat([{ + label: "None", + value: "None", + description: undefined + }]); + $[7] = availableIDEs; + $[8] = ideCounts; + $[9] = t4; + } else { + t4 = $[9]; + } + const options = t4; + if (showAutoConnectDialog) { + let t5; + if ($[12] !== handleSelectIDE || $[13] !== selectedValue) { + t5 = handleSelectIDE(selectedValue)} />; + $[12] = handleSelectIDE; + $[13] = selectedValue; + $[14] = t5; + } else { + t5 = $[14]; + } + return t5; + } + if (showDisableAutoConnectDialog) { + let t5; + if ($[15] !== onSelect) { + t5 = { + onSelect(undefined); + }} />; + $[15] = onSelect; + $[16] = t5; + } else { + t5 = $[16]; + } + return t5; + } + let t5; + if ($[17] !== availableIDEs.length) { + t5 = availableIDEs.length === 0 && {isSupportedJetBrainsTerminal() ? "No available IDEs detected. Please install the plugin and restart your IDE:\nhttps://docs.claude.com/s/claude-code-jetbrains" : "No available IDEs detected. Make sure your IDE has the Claude Code extension or plugin installed and is running."}; + $[17] = availableIDEs.length; + $[18] = t5; + } else { + t5 = $[18]; + } + let t6; + if ($[19] !== availableIDEs.length || $[20] !== handleSelectIDE || $[21] !== options || $[22] !== selectedValue) { + t6 = availableIDEs.length !== 0 && ; + $[11] = options; + $[12] = selectedValue; + $[13] = t5; + $[14] = t6; + } else { + t6 = $[14]; + } + let t7; + if ($[15] !== handleCancel || $[16] !== t6) { + t7 = {t6}; + $[15] = handleCancel; + $[16] = t6; + $[17] = t7; + } else { + t7 = $[17]; + } + return t7; +} +function _temp4(ide_0) { + return { + label: ide_0.name, + value: ide_0.port.toString() + }; +} +function RunningIDESelector(t0) { + const $ = _c(15); + const { + runningIDEs, + onSelectIDE, + onDone + } = t0; + const [selectedValue, setSelectedValue] = useState(runningIDEs[0] ?? ""); + let t1; + if ($[0] !== onSelectIDE) { + t1 = value => { + onSelectIDE(value as IdeType); + }; + $[0] = onSelectIDE; + $[1] = t1; + } else { + t1 = $[1]; + } + const handleSelectIDE = t1; + let t2; + if ($[2] !== runningIDEs) { + t2 = runningIDEs.map(_temp5); + $[2] = runningIDEs; + $[3] = t2; + } else { + t2 = $[3]; + } + const options = t2; + let t3; + if ($[4] !== onDone) { + t3 = function handleCancel() { + onDone("IDE selection cancelled", { + display: "system" + }); + }; + $[4] = onDone; + $[5] = t3; + } else { + t3 = $[5]; + } + const handleCancel = t3; + let t4; + if ($[6] !== handleSelectIDE) { + t4 = value_0 => { + setSelectedValue(value_0); + handleSelectIDE(value_0); + }; + $[6] = handleSelectIDE; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] !== options || $[9] !== selectedValue || $[10] !== t4) { + t5 = + +
${escapeHtml(add.why)}
+ + `, + ) + .join('')} + + ` + : '' + } + ${ + suggestions.features_to_try && suggestions.features_to_try.length > 0 + ? ` +

Just copy this into Claude Code and it'll set it up for you.

+
+ ${suggestions.features_to_try + .map( + feat => ` +
+
${escapeHtml(feat.feature || '')}
+
${escapeHtml(feat.one_liner || '')}
+
Why for you: ${escapeHtml(feat.why_for_you || '')}
+ ${ + feat.example_code + ? ` +
+
+
+ ${escapeHtml(feat.example_code)} + +
+
+
+ ` + : '' + } +
+ `, + ) + .join('')} +
+ ` + : '' + } + ${ + suggestions.usage_patterns && suggestions.usage_patterns.length > 0 + ? ` +

New Ways to Use Claude Code

+

Just copy this into Claude Code and it'll walk you through it.

+
+ ${suggestions.usage_patterns + .map( + pat => ` +
+
${escapeHtml(pat.title || '')}
+
${escapeHtml(pat.suggestion || '')}
+ ${pat.detail ? `
${escapeHtml(pat.detail)}
` : ''} + ${ + pat.copyable_prompt + ? ` +
+
Paste into Claude Code:
+
+ ${escapeHtml(pat.copyable_prompt)} + +
+
+ ` + : '' + } +
+ `, + ) + .join('')} +
+ ` + : '' + } + ` + : '' + + // Build On the Horizon section + const horizonData = insights.on_the_horizon + const horizonHtml = + horizonData?.opportunities && horizonData.opportunities.length > 0 + ? ` +

On the Horizon

+ ${horizonData.intro ? `

${escapeHtml(horizonData.intro)}

` : ''} +
+ ${horizonData.opportunities + .map( + opp => ` +
+
${escapeHtml(opp.title || '')}
+
${escapeHtml(opp.whats_possible || '')}
+ ${opp.how_to_try ? `
Getting started: ${escapeHtml(opp.how_to_try)}
` : ''} + ${opp.copyable_prompt ? `
Paste into Claude Code:
${escapeHtml(opp.copyable_prompt)}
` : ''} +
+ `, + ) + .join('')} +
+ ` + : '' + + // Build Team Feedback section (collapsible, ant-only) + const ccImprovements = + process.env.USER_TYPE === 'ant' + ? insights.cc_team_improvements?.improvements || [] + : [] + const modelImprovements = + process.env.USER_TYPE === 'ant' + ? insights.model_behavior_improvements?.improvements || [] + : [] + const teamFeedbackHtml = + ccImprovements.length > 0 || modelImprovements.length > 0 + ? ` + + + ${ + ccImprovements.length > 0 + ? ` +
+
+ +

Product Improvements for CC Team

+
+
+
+ ${ccImprovements + .map( + imp => ` + + `, + ) + .join('')} +
+
+
+ ` + : '' + } + ${ + modelImprovements.length > 0 + ? ` +
+
+ +

Model Behavior Improvements

+
+
+
+ ${modelImprovements + .map( + imp => ` + + `, + ) + .join('')} +
+
+
+ ` + : '' + } + ` + : '' + + // Build Fun Ending section + const funEnding = insights.fun_ending + const funEndingHtml = funEnding?.headline + ? ` +
+
"${escapeHtml(funEnding.headline)}"
+ ${funEnding.detail ? `
${escapeHtml(funEnding.detail)}
` : ''} +
+ ` + : '' + + const css = ` + * { box-sizing: border-box; margin: 0; padding: 0; } + body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; background: #f8fafc; color: #334155; line-height: 1.65; padding: 48px 24px; } + .container { max-width: 800px; margin: 0 auto; } + h1 { font-size: 32px; font-weight: 700; color: #0f172a; margin-bottom: 8px; } + h2 { font-size: 20px; font-weight: 600; color: #0f172a; margin-top: 48px; margin-bottom: 16px; } + .subtitle { color: #64748b; font-size: 15px; margin-bottom: 32px; } + .nav-toc { display: flex; flex-wrap: wrap; gap: 8px; margin: 24px 0 32px 0; padding: 16px; background: white; border-radius: 8px; border: 1px solid #e2e8f0; } + .nav-toc a { font-size: 12px; color: #64748b; text-decoration: none; padding: 6px 12px; border-radius: 6px; background: #f1f5f9; transition: all 0.15s; } + .nav-toc a:hover { background: #e2e8f0; color: #334155; } + .stats-row { display: flex; gap: 24px; margin-bottom: 40px; padding: 20px 0; border-top: 1px solid #e2e8f0; border-bottom: 1px solid #e2e8f0; flex-wrap: wrap; } + .stat { text-align: center; } + .stat-value { font-size: 24px; font-weight: 700; color: #0f172a; } + .stat-label { font-size: 11px; color: #64748b; text-transform: uppercase; } + .at-a-glance { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border: 1px solid #f59e0b; border-radius: 12px; padding: 20px 24px; margin-bottom: 32px; } + .glance-title { font-size: 16px; font-weight: 700; color: #92400e; margin-bottom: 16px; } + .glance-sections { display: flex; flex-direction: column; gap: 12px; } + .glance-section { font-size: 14px; color: #78350f; line-height: 1.6; } + .glance-section strong { color: #92400e; } + .see-more { color: #b45309; text-decoration: none; font-size: 13px; white-space: nowrap; } + .see-more:hover { text-decoration: underline; } + .project-areas { display: flex; flex-direction: column; gap: 12px; margin-bottom: 32px; } + .project-area { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; } + .area-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } + .area-name { font-weight: 600; font-size: 15px; color: #0f172a; } + .area-count { font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px; } + .area-desc { font-size: 14px; color: #475569; line-height: 1.5; } + .narrative { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 20px; margin-bottom: 24px; } + .narrative p { margin-bottom: 12px; font-size: 14px; color: #475569; line-height: 1.7; } + .key-insight { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 12px 16px; margin-top: 12px; font-size: 14px; color: #166534; } + .section-intro { font-size: 14px; color: #64748b; margin-bottom: 16px; } + .big-wins { display: flex; flex-direction: column; gap: 12px; margin-bottom: 24px; } + .big-win { background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 16px; } + .big-win-title { font-weight: 600; font-size: 15px; color: #166534; margin-bottom: 8px; } + .big-win-desc { font-size: 14px; color: #15803d; line-height: 1.5; } + .friction-categories { display: flex; flex-direction: column; gap: 16px; margin-bottom: 24px; } + .friction-category { background: #fef2f2; border: 1px solid #fca5a5; border-radius: 8px; padding: 16px; } + .friction-title { font-weight: 600; font-size: 15px; color: #991b1b; margin-bottom: 6px; } + .friction-desc { font-size: 13px; color: #7f1d1d; margin-bottom: 10px; } + .friction-examples { margin: 0 0 0 20px; font-size: 13px; color: #334155; } + .friction-examples li { margin-bottom: 4px; } + .claude-md-section { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; padding: 16px; margin-bottom: 20px; } + .claude-md-section h3 { font-size: 14px; font-weight: 600; color: #1e40af; margin: 0 0 12px 0; } + .claude-md-actions { margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #dbeafe; } + .copy-all-btn { background: #2563eb; color: white; border: none; border-radius: 4px; padding: 6px 12px; font-size: 12px; cursor: pointer; font-weight: 500; transition: all 0.2s; } + .copy-all-btn:hover { background: #1d4ed8; } + .copy-all-btn.copied { background: #16a34a; } + .claude-md-item { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px; padding: 10px 0; border-bottom: 1px solid #dbeafe; } + .claude-md-item:last-child { border-bottom: none; } + .cmd-checkbox { margin-top: 2px; } + .cmd-code { background: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; color: #1e40af; border: 1px solid #bfdbfe; font-family: monospace; display: block; white-space: pre-wrap; word-break: break-word; flex: 1; } + .cmd-why { font-size: 12px; color: #64748b; width: 100%; padding-left: 24px; margin-top: 4px; } + .features-section, .patterns-section { display: flex; flex-direction: column; gap: 12px; margin: 16px 0; } + .feature-card { background: #f0fdf4; border: 1px solid #86efac; border-radius: 8px; padding: 16px; } + .pattern-card { background: #f0f9ff; border: 1px solid #7dd3fc; border-radius: 8px; padding: 16px; } + .feature-title, .pattern-title { font-weight: 600; font-size: 15px; color: #0f172a; margin-bottom: 6px; } + .feature-oneliner { font-size: 14px; color: #475569; margin-bottom: 8px; } + .pattern-summary { font-size: 14px; color: #475569; margin-bottom: 8px; } + .feature-why, .pattern-detail { font-size: 13px; color: #334155; line-height: 1.5; } + .feature-examples { margin-top: 12px; } + .feature-example { padding: 8px 0; border-top: 1px solid #d1fae5; } + .feature-example:first-child { border-top: none; } + .example-desc { font-size: 13px; color: #334155; margin-bottom: 6px; } + .example-code-row { display: flex; align-items: flex-start; gap: 8px; } + .example-code { flex: 1; background: #f1f5f9; padding: 8px 12px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #334155; overflow-x: auto; white-space: pre-wrap; } + .copyable-prompt-section { margin-top: 12px; padding-top: 12px; border-top: 1px solid #e2e8f0; } + .copyable-prompt-row { display: flex; align-items: flex-start; gap: 8px; } + .copyable-prompt { flex: 1; background: #f8fafc; padding: 10px 12px; border-radius: 4px; font-family: monospace; font-size: 12px; color: #334155; border: 1px solid #e2e8f0; white-space: pre-wrap; line-height: 1.5; } + .feature-code { background: #f8fafc; padding: 12px; border-radius: 6px; margin-top: 12px; border: 1px solid #e2e8f0; display: flex; align-items: flex-start; gap: 8px; } + .feature-code code { flex: 1; font-family: monospace; font-size: 12px; color: #334155; white-space: pre-wrap; } + .pattern-prompt { background: #f8fafc; padding: 12px; border-radius: 6px; margin-top: 12px; border: 1px solid #e2e8f0; } + .pattern-prompt code { font-family: monospace; font-size: 12px; color: #334155; display: block; white-space: pre-wrap; margin-bottom: 8px; } + .prompt-label { font-size: 11px; font-weight: 600; text-transform: uppercase; color: #64748b; margin-bottom: 6px; } + .copy-btn { background: #e2e8f0; border: none; border-radius: 4px; padding: 4px 8px; font-size: 11px; cursor: pointer; color: #475569; flex-shrink: 0; } + .copy-btn:hover { background: #cbd5e1; } + .charts-row { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin: 24px 0; } + .chart-card { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; } + .chart-title { font-size: 12px; font-weight: 600; color: #64748b; text-transform: uppercase; margin-bottom: 12px; } + .bar-row { display: flex; align-items: center; margin-bottom: 6px; } + .bar-label { width: 100px; font-size: 11px; color: #475569; flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + .bar-track { flex: 1; height: 6px; background: #f1f5f9; border-radius: 3px; margin: 0 8px; } + .bar-fill { height: 100%; border-radius: 3px; } + .bar-value { width: 28px; font-size: 11px; font-weight: 500; color: #64748b; text-align: right; } + .empty { color: #94a3b8; font-size: 13px; } + .horizon-section { display: flex; flex-direction: column; gap: 16px; } + .horizon-card { background: linear-gradient(135deg, #faf5ff 0%, #f5f3ff 100%); border: 1px solid #c4b5fd; border-radius: 8px; padding: 16px; } + .horizon-title { font-weight: 600; font-size: 15px; color: #5b21b6; margin-bottom: 8px; } + .horizon-possible { font-size: 14px; color: #334155; margin-bottom: 10px; line-height: 1.5; } + .horizon-tip { font-size: 13px; color: #6b21a8; background: rgba(255,255,255,0.6); padding: 8px 12px; border-radius: 4px; } + .feedback-header { margin-top: 48px; color: #64748b; font-size: 16px; } + .feedback-intro { font-size: 13px; color: #94a3b8; margin-bottom: 16px; } + .feedback-section { margin-top: 16px; } + .feedback-section h3 { font-size: 14px; font-weight: 600; color: #475569; margin-bottom: 12px; } + .feedback-card { background: white; border: 1px solid #e2e8f0; border-radius: 8px; padding: 16px; margin-bottom: 12px; } + .feedback-card.team-card { background: #eff6ff; border-color: #bfdbfe; } + .feedback-card.model-card { background: #faf5ff; border-color: #e9d5ff; } + .feedback-title { font-weight: 600; font-size: 14px; color: #0f172a; margin-bottom: 6px; } + .feedback-detail { font-size: 13px; color: #475569; line-height: 1.5; } + .feedback-evidence { font-size: 12px; color: #64748b; margin-top: 8px; } + .fun-ending { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); border: 1px solid #fbbf24; border-radius: 12px; padding: 24px; margin-top: 40px; text-align: center; } + .fun-headline { font-size: 18px; font-weight: 600; color: #78350f; margin-bottom: 8px; } + .fun-detail { font-size: 14px; color: #92400e; } + .collapsible-section { margin-top: 16px; } + .collapsible-header { display: flex; align-items: center; gap: 8px; cursor: pointer; padding: 12px 0; border-bottom: 1px solid #e2e8f0; } + .collapsible-header h3 { margin: 0; font-size: 14px; font-weight: 600; color: #475569; } + .collapsible-arrow { font-size: 12px; color: #94a3b8; transition: transform 0.2s; } + .collapsible-content { display: none; padding-top: 16px; } + .collapsible-content.open { display: block; } + .collapsible-header.open .collapsible-arrow { transform: rotate(90deg); } + @media (max-width: 640px) { .charts-row { grid-template-columns: 1fr; } .stats-row { justify-content: center; } } + ` + + const hourCountsJson = getHourCountsJson(data.message_hours) + + const js = ` + function toggleCollapsible(header) { + header.classList.toggle('open'); + const content = header.nextElementSibling; + content.classList.toggle('open'); + } + function copyText(btn) { + const code = btn.previousElementSibling; + navigator.clipboard.writeText(code.textContent).then(() => { + btn.textContent = 'Copied!'; + setTimeout(() => { btn.textContent = 'Copy'; }, 2000); + }); + } + function copyCmdItem(idx) { + const checkbox = document.getElementById('cmd-' + idx); + if (checkbox) { + const text = checkbox.dataset.text; + navigator.clipboard.writeText(text).then(() => { + const btn = checkbox.nextElementSibling.querySelector('.copy-btn'); + if (btn) { btn.textContent = 'Copied!'; setTimeout(() => { btn.textContent = 'Copy'; }, 2000); } + }); + } + } + function copyAllCheckedClaudeMd() { + const checkboxes = document.querySelectorAll('.cmd-checkbox:checked'); + const texts = []; + checkboxes.forEach(cb => { + if (cb.dataset.text) { texts.push(cb.dataset.text); } + }); + const combined = texts.join('\\n'); + const btn = document.querySelector('.copy-all-btn'); + if (btn) { + navigator.clipboard.writeText(combined).then(() => { + btn.textContent = 'Copied ' + texts.length + ' items!'; + btn.classList.add('copied'); + setTimeout(() => { btn.textContent = 'Copy All Checked'; btn.classList.remove('copied'); }, 2000); + }); + } + } + // Timezone selector for time of day chart (data is from our own analytics, not user input) + const rawHourCounts = ${hourCountsJson}; + function updateHourHistogram(offsetFromPT) { + const periods = [ + { label: "Morning (6-12)", range: [6,7,8,9,10,11] }, + { label: "Afternoon (12-18)", range: [12,13,14,15,16,17] }, + { label: "Evening (18-24)", range: [18,19,20,21,22,23] }, + { label: "Night (0-6)", range: [0,1,2,3,4,5] } + ]; + const adjustedCounts = {}; + for (const [hour, count] of Object.entries(rawHourCounts)) { + const newHour = (parseInt(hour) + offsetFromPT + 24) % 24; + adjustedCounts[newHour] = (adjustedCounts[newHour] || 0) + count; + } + const periodCounts = periods.map(p => ({ + label: p.label, + count: p.range.reduce((sum, h) => sum + (adjustedCounts[h] || 0), 0) + })); + const maxCount = Math.max(...periodCounts.map(p => p.count)) || 1; + const container = document.getElementById('hour-histogram'); + container.textContent = ''; + periodCounts.forEach(p => { + const row = document.createElement('div'); + row.className = 'bar-row'; + const label = document.createElement('div'); + label.className = 'bar-label'; + label.textContent = p.label; + const track = document.createElement('div'); + track.className = 'bar-track'; + const fill = document.createElement('div'); + fill.className = 'bar-fill'; + fill.style.width = (p.count / maxCount) * 100 + '%'; + fill.style.background = '#8b5cf6'; + track.appendChild(fill); + const value = document.createElement('div'); + value.className = 'bar-value'; + value.textContent = p.count; + row.appendChild(label); + row.appendChild(track); + row.appendChild(value); + container.appendChild(row); + }); + } + document.getElementById('timezone-select').addEventListener('change', function() { + const customInput = document.getElementById('custom-offset'); + if (this.value === 'custom') { + customInput.style.display = 'inline-block'; + customInput.focus(); + } else { + customInput.style.display = 'none'; + updateHourHistogram(parseInt(this.value)); + } + }); + document.getElementById('custom-offset').addEventListener('change', function() { + const offset = parseInt(this.value) + 8; + updateHourHistogram(offset); + }); + ` + + return ` + + + + Claude Code Insights + + + + +
+

Claude Code Insights

+

${data.total_messages.toLocaleString()} messages across ${data.total_sessions} sessions${data.total_sessions_scanned && data.total_sessions_scanned > data.total_sessions ? ` (${data.total_sessions_scanned.toLocaleString()} total)` : ''} | ${data.date_range.start} to ${data.date_range.end}

+ + ${atAGlanceHtml} + + + +
+
${data.total_messages.toLocaleString()}
Messages
+
+${data.total_lines_added.toLocaleString()}/-${data.total_lines_removed.toLocaleString()}
Lines
+
${data.total_files_modified}
Files
+
${data.days_active}
Days
+
${data.messages_per_day}
Msgs/Day
+
+ + ${projectAreasHtml} + +
+
+
What You Wanted
+ ${generateBarChart(data.goal_categories, '#2563eb')} +
+
+
Top Tools Used
+ ${generateBarChart(data.tool_counts, '#0891b2')} +
+
+ +
+
+
Languages
+ ${generateBarChart(data.languages, '#10b981')} +
+
+
Session Types
+ ${generateBarChart(data.session_types || {}, '#8b5cf6')} +
+
+ + ${interactionHtml} + + +
+
User Response Time Distribution
+ ${generateResponseTimeHistogram(data.user_response_times)} +
+ Median: ${data.median_response_time.toFixed(1)}s • Average: ${data.avg_response_time.toFixed(1)}s +
+
+ + +
+
Multi-Clauding (Parallel Sessions)
+ ${ + data.multi_clauding.overlap_events === 0 + ? ` +

+ No parallel session usage detected. You typically work with one Claude Code session at a time. +

+ ` + : ` +
+
+
${data.multi_clauding.overlap_events}
+
Overlap Events
+
+
+
${data.multi_clauding.sessions_involved}
+
Sessions Involved
+
+
+
${data.total_messages > 0 ? Math.round((100 * data.multi_clauding.user_messages_during) / data.total_messages) : 0}%
+
Of Messages
+
+
+

+ You run multiple Claude Code sessions simultaneously. Multi-clauding is detected when sessions + overlap in time, suggesting parallel workflows. +

+ ` + } +
+ + +
+
+
+ User Messages by Time of Day + + +
+ ${generateTimeOfDayChart(data.message_hours)} +
+
+
Tool Errors Encountered
+ ${Object.keys(data.tool_error_categories).length > 0 ? generateBarChart(data.tool_error_categories, '#dc2626') : '

No tool errors

'} +
+
+ + ${whatWorksHtml} + +
+
+
What Helped Most (Claude's Capabilities)
+ ${generateBarChart(data.success, '#16a34a')} +
+
+
Outcomes
+ ${generateBarChart(data.outcomes, '#8b5cf6', 6, OUTCOME_ORDER)} +
+
+ + ${frictionHtml} + +
+
+
Primary Friction Types
+ ${generateBarChart(data.friction, '#dc2626')} +
+
+
Inferred Satisfaction (model-estimated)
+ ${generateBarChart(data.satisfaction, '#eab308', 6, SATISFACTION_ORDER)} +
+
+ + ${suggestionsHtml} + + ${horizonHtml} + + ${funEndingHtml} + + ${teamFeedbackHtml} +
+ + +` +} + +// ============================================================================ +// Export Types & Functions +// ============================================================================ + +/** + * Structured export format for claudescope consumption + */ +export type InsightsExport = { + metadata: { + username: string + generated_at: string + claude_code_version: string + date_range: { start: string; end: string } + session_count: number + remote_hosts_collected?: string[] + } + aggregated_data: AggregatedData + insights: InsightResults + facets_summary?: { + total: number + goal_categories: Record + outcomes: Record + satisfaction: Record + friction: Record + } +} + +/** + * Build export data from already-computed values. + * Used by background upload to S3. + */ +export function buildExportData( + data: AggregatedData, + insights: InsightResults, + facets: Map, + remoteStats?: { hosts: RemoteHostInfo[]; totalCopied: number }, +): InsightsExport { + const version = typeof MACRO !== 'undefined' ? MACRO.VERSION : 'unknown' + + const remote_hosts_collected = remoteStats?.hosts + .filter(h => h.sessionCount > 0) + .map(h => h.name) + + const facets_summary = { + total: facets.size, + goal_categories: {} as Record, + outcomes: {} as Record, + satisfaction: {} as Record, + friction: {} as Record, + } + for (const f of facets.values()) { + for (const [cat, count] of safeEntries(f.goal_categories)) { + if (count > 0) { + facets_summary.goal_categories[cat] = + (facets_summary.goal_categories[cat] || 0) + count + } + } + facets_summary.outcomes[f.outcome] = + (facets_summary.outcomes[f.outcome] || 0) + 1 + for (const [level, count] of safeEntries(f.user_satisfaction_counts)) { + if (count > 0) { + facets_summary.satisfaction[level] = + (facets_summary.satisfaction[level] || 0) + count + } + } + for (const [type, count] of safeEntries(f.friction_counts)) { + if (count > 0) { + facets_summary.friction[type] = + (facets_summary.friction[type] || 0) + count + } + } + } + + return { + metadata: { + username: process.env.SAFEUSER || process.env.USER || 'unknown', + generated_at: new Date().toISOString(), + claude_code_version: version, + date_range: data.date_range, + session_count: data.total_sessions, + ...(remote_hosts_collected && + remote_hosts_collected.length > 0 && { + remote_hosts_collected, + }), + }, + aggregated_data: data, + insights, + facets_summary, + } +} + +// ============================================================================ +// Lite Session Scanning +// ============================================================================ + +type LiteSessionInfo = { + sessionId: string + path: string + mtime: number + size: number +} + +/** + * Scans all project directories using filesystem metadata only (no JSONL parsing). + * Returns a list of session file info sorted by mtime descending. + * Yields to the event loop between project directories to keep the UI responsive. + */ +async function scanAllSessions(): Promise { + const projectsDir = getProjectsDir() + + let dirents: Awaited> + try { + dirents = await readdir(projectsDir, { withFileTypes: true }) + } catch { + return [] + } + + const projectDirs = dirents + .filter(dirent => dirent.isDirectory()) + .map(dirent => join(projectsDir, dirent.name)) + + const allSessions: LiteSessionInfo[] = [] + + for (let i = 0; i < projectDirs.length; i++) { + const sessionFiles = await getSessionFilesWithMtime(projectDirs[i]!) + for (const [sessionId, fileInfo] of sessionFiles) { + allSessions.push({ + sessionId, + path: fileInfo.path, + mtime: fileInfo.mtime, + size: fileInfo.size, + }) + } + // Yield to event loop every 10 project directories + if (i % 10 === 9) { + await new Promise(resolve => setImmediate(resolve)) + } + } + + // Sort by mtime descending (most recent first) + allSessions.sort((a, b) => b.mtime - a.mtime) + return allSessions +} + +// ============================================================================ +// Main Function +// ============================================================================ + +export async function generateUsageReport(options?: { + collectRemote?: boolean +}): Promise<{ + insights: InsightResults + htmlPath: string + data: AggregatedData + remoteStats?: { hosts: RemoteHostInfo[]; totalCopied: number } + facets: Map +}> { + let remoteStats: { hosts: RemoteHostInfo[]; totalCopied: number } | undefined + + // Optionally collect data from remote hosts first (ant-only) + if (process.env.USER_TYPE === 'ant' && options?.collectRemote) { + const destDir = join(getClaudeConfigHomeDir(), 'projects') + const { hosts, totalCopied } = await collectAllRemoteHostData(destDir) + remoteStats = { hosts, totalCopied } + } + + // Phase 1: Lite scan — filesystem metadata only (no JSONL parsing) + const allScannedSessions = await scanAllSessions() + const totalSessionsScanned = allScannedSessions.length + + // Phase 2: Load SessionMeta — use cache where available, parse only uncached + // Read cached metas in parallel batches to avoid blocking the event loop + const META_BATCH_SIZE = 50 + const MAX_SESSIONS_TO_LOAD = 200 + let allMetas: SessionMeta[] = [] + const uncachedSessions: LiteSessionInfo[] = [] + + for (let i = 0; i < allScannedSessions.length; i += META_BATCH_SIZE) { + const batch = allScannedSessions.slice(i, i + META_BATCH_SIZE) + const results = await Promise.all( + batch.map(async sessionInfo => ({ + sessionInfo, + cached: await loadCachedSessionMeta(sessionInfo.sessionId), + })), + ) + for (const { sessionInfo, cached } of results) { + if (cached) { + allMetas.push(cached) + } else if (uncachedSessions.length < MAX_SESSIONS_TO_LOAD) { + uncachedSessions.push(sessionInfo) + } + } + } + + // Load full message data only for uncached sessions and compute SessionMeta + const logsForFacets = new Map() + + // Filter out /insights meta-sessions (facet extraction API calls get logged as sessions) + const isMetaSession = (log: LogOption): boolean => { + for (const msg of log.messages.slice(0, 5)) { + if (msg.type === 'user' && msg.message) { + const content = msg.message.content + if (typeof content === 'string') { + if ( + content.includes('RESPOND WITH ONLY A VALID JSON OBJECT') || + content.includes('record_facets') + ) { + return true + } + } + } + } + return false + } + + // Load uncached sessions in batches to yield to event loop between batches + const LOAD_BATCH_SIZE = 10 + for (let i = 0; i < uncachedSessions.length; i += LOAD_BATCH_SIZE) { + const batch = uncachedSessions.slice(i, i + LOAD_BATCH_SIZE) + const batchResults = await Promise.all( + batch.map(async sessionInfo => { + try { + return await loadAllLogsFromSessionFile(sessionInfo.path) + } catch { + return [] + } + }), + ) + // Collect metas synchronously, then save them in parallel (independent writes) + const metasToSave: SessionMeta[] = [] + for (const logs of batchResults) { + for (const log of logs) { + if (isMetaSession(log) || !hasValidDates(log)) continue + const meta = logToSessionMeta(log) + allMetas.push(meta) + metasToSave.push(meta) + // Keep the log around for potential facet extraction + logsForFacets.set(meta.session_id, log) + } + } + await Promise.all(metasToSave.map(meta => saveSessionMeta(meta))) + } + + // Deduplicate session branches (keep the one with most user messages per session_id) + // This prevents inflated totals when a session has multiple conversation branches + const bestBySession = new Map() + for (const meta of allMetas) { + const existing = bestBySession.get(meta.session_id) + if ( + !existing || + meta.user_message_count > existing.user_message_count || + (meta.user_message_count === existing.user_message_count && + meta.duration_minutes > existing.duration_minutes) + ) { + bestBySession.set(meta.session_id, meta) + } + } + // Replace allMetas with deduplicated list and remove unused logs from logsForFacets + const keptSessionIds = new Set(bestBySession.keys()) + allMetas = [...bestBySession.values()] + for (const sessionId of logsForFacets.keys()) { + if (!keptSessionIds.has(sessionId)) { + logsForFacets.delete(sessionId) + } + } + + // Sort all metas by start_time descending (most recent first) + allMetas.sort((a, b) => b.start_time.localeCompare(a.start_time)) + + // Pre-filter obviously minimal sessions to save API calls + // (matching Python's substantive filtering concept) + const isSubstantiveSession = (meta: SessionMeta): boolean => { + // Skip sessions with very few user messages + if (meta.user_message_count < 2) return false + // Skip very short sessions (< 1 minute) + if (meta.duration_minutes < 1) return false + return true + } + + const substantiveMetas = allMetas.filter(isSubstantiveSession) + + // Phase 3: Facet extraction — only for sessions without cached facets + const facets = new Map() + const toExtract: Array<{ log: LogOption; sessionId: string }> = [] + const MAX_FACET_EXTRACTIONS = 50 + + // Load cached facets for all substantive sessions in parallel + const cachedFacetResults = await Promise.all( + substantiveMetas.map(async meta => ({ + sessionId: meta.session_id, + cached: await loadCachedFacets(meta.session_id), + })), + ) + for (const { sessionId, cached } of cachedFacetResults) { + if (cached) { + facets.set(sessionId, cached) + } else { + const log = logsForFacets.get(sessionId) + if (log && toExtract.length < MAX_FACET_EXTRACTIONS) { + toExtract.push({ log, sessionId }) + } + } + } + + // Extract facets for sessions that need them (50 concurrent) + const CONCURRENCY = 50 + for (let i = 0; i < toExtract.length; i += CONCURRENCY) { + const batch = toExtract.slice(i, i + CONCURRENCY) + const results = await Promise.all( + batch.map(async ({ log, sessionId }) => { + const newFacets = await extractFacetsFromAPI(log, sessionId) + return { sessionId, newFacets } + }), + ) + // Collect facets synchronously, save in parallel (independent writes) + const facetsToSave: SessionFacets[] = [] + for (const { sessionId, newFacets } of results) { + if (newFacets) { + facets.set(sessionId, newFacets) + facetsToSave.push(newFacets) + } + } + await Promise.all(facetsToSave.map(f => saveFacets(f))) + } + + // Filter out warmup/minimal sessions (matching Python's is_minimal) + // A session is minimal if warmup_minimal is the ONLY goal category + const isMinimalSession = (sessionId: string): boolean => { + const sessionFacets = facets.get(sessionId) + if (!sessionFacets) return false + const cats = sessionFacets.goal_categories + const catKeys = safeKeys(cats).filter(k => (cats[k] ?? 0) > 0) + return catKeys.length === 1 && catKeys[0] === 'warmup_minimal' + } + + const substantiveSessions = substantiveMetas.filter( + s => !isMinimalSession(s.session_id), + ) + + const substantiveFacets = new Map() + for (const [sessionId, f] of facets) { + if (!isMinimalSession(sessionId)) { + substantiveFacets.set(sessionId, f) + } + } + + const aggregated = aggregateData(substantiveSessions, substantiveFacets) + aggregated.total_sessions_scanned = totalSessionsScanned + + // Generate parallel insights from Claude (6 sections) + const insights = await generateParallelInsights(aggregated, facets) + + // Generate HTML report + const htmlReport = generateHtmlReport(aggregated, insights) + + // Save reports + try { + await mkdir(getDataDir(), { recursive: true }) + } catch { + // Directory may already exist + } + + const htmlPath = join(getDataDir(), 'report.html') + await writeFile(htmlPath, htmlReport, { + encoding: 'utf-8', + mode: 0o600, + }) + + return { + insights, + htmlPath, + data: aggregated, + remoteStats, + facets: substantiveFacets, + } +} + +function safeEntries( + obj: Record | undefined | null, +): [string, V][] { + return obj ? Object.entries(obj) : [] +} + +function safeKeys(obj: Record | undefined | null): string[] { + return obj ? Object.keys(obj) : [] +} + +// ============================================================================ +// Command Definition +// ============================================================================ + +const usageReport: Command = { + type: 'prompt', + name: 'insights', + description: 'Generate a report analyzing your Claude Code sessions', + contentLength: 0, // Dynamic content + progressMessage: 'analyzing your sessions', + source: 'builtin', + async getPromptForCommand(args) { + let collectRemote = false + let remoteHosts: string[] = [] + let hasRemoteHosts = false + + if (process.env.USER_TYPE === 'ant') { + // Parse --homespaces flag + collectRemote = args?.includes('--homespaces') ?? false + + // Check for available remote hosts + remoteHosts = await getRunningRemoteHosts() + hasRemoteHosts = remoteHosts.length > 0 + + // Show collection message if collecting + if (collectRemote && hasRemoteHosts) { + // biome-ignore lint/suspicious/noConsole: intentional + console.error( + `Collecting sessions from ${remoteHosts.length} homespace(s): ${remoteHosts.join(', ')}...`, + ) + } + } + + const { insights, htmlPath, data, remoteStats } = await generateUsageReport( + { collectRemote }, + ) + + let reportUrl = `file://${htmlPath}` + let uploadHint = '' + + if (process.env.USER_TYPE === 'ant') { + // Try to upload to S3 + const timestamp = new Date() + .toISOString() + .replace(/[-:]/g, '') + .replace('T', '_') + .slice(0, 15) + const username = process.env.SAFEUSER || process.env.USER || 'unknown' + const filename = `${username}_insights_${timestamp}.html` + const s3Path = `s3://anthropic-serve/atamkin/cc-user-reports/${filename}` + const s3Url = `https://s3-frontend.infra.ant.dev/anthropic-serve/atamkin/cc-user-reports/${filename}` + + reportUrl = s3Url + try { + execFileSync('ff', ['cp', htmlPath, s3Path], { + timeout: 60000, + stdio: 'pipe', // Suppress output + }) + } catch { + // Upload failed - fall back to local file and show upload command + reportUrl = `file://${htmlPath}` + uploadHint = `\nAutomatic upload failed. Are you on the boron namespace? Try \`use-bo\` and ensure you've run \`sso\`. +To share, run: ff cp ${htmlPath} ${s3Path} +Then access at: ${s3Url}` + } + } + + // Build header with stats + const sessionLabel = + data.total_sessions_scanned && + data.total_sessions_scanned > data.total_sessions + ? `${data.total_sessions_scanned.toLocaleString()} sessions total · ${data.total_sessions} analyzed` + : `${data.total_sessions} sessions` + const stats = [ + sessionLabel, + `${data.total_messages.toLocaleString()} messages`, + `${Math.round(data.total_duration_hours)}h`, + `${data.git_commits} commits`, + ].join(' · ') + + // Build remote host info (ant-only) + let remoteInfo = '' + if (process.env.USER_TYPE === 'ant') { + if (remoteStats && remoteStats.totalCopied > 0) { + const hsNames = remoteStats.hosts + .filter(h => h.sessionCount > 0) + .map(h => h.name) + .join(', ') + remoteInfo = `\n_Collected ${remoteStats.totalCopied} new sessions from: ${hsNames}_\n` + } else if (!collectRemote && hasRemoteHosts) { + // Suggest using --homespaces if they have remote hosts but didn't use the flag + remoteInfo = `\n_Tip: Run \`/insights --homespaces\` to include sessions from your ${remoteHosts.length} running homespace(s)_\n` + } + } + + // Build markdown summary from insights + const atAGlance = insights.at_a_glance + const summaryText = atAGlance + ? `## At a Glance + +${atAGlance.whats_working ? `**What's working:** ${atAGlance.whats_working} See _Impressive Things You Did_.` : ''} + +${atAGlance.whats_hindering ? `**What's hindering you:** ${atAGlance.whats_hindering} See _Where Things Go Wrong_.` : ''} + +${atAGlance.quick_wins ? `**Quick wins to try:** ${atAGlance.quick_wins} See _Features to Try_.` : ''} + +${atAGlance.ambitious_workflows ? `**Ambitious workflows:** ${atAGlance.ambitious_workflows} See _On the Horizon_.` : ''}` + : '_No insights generated_' + + const header = `# Claude Code Insights + +${stats} +${data.date_range.start} to ${data.date_range.end} +${remoteInfo} +` + + const userSummary = `${header}${summaryText} + +Your full shareable insights report is ready: ${reportUrl}${uploadHint}` + + // Return prompt for Claude to respond to + return [ + { + type: 'text', + text: `The user just ran /insights to generate a usage report analyzing their Claude Code sessions. + +Here is the full insights data: +${jsonStringify(insights, null, 2)} + +Report URL: ${reportUrl} +HTML file: ${htmlPath} +Facets directory: ${getFacetsDir()} + +Here is what the user sees: +${userSummary} + +Now output the following message exactly: + + +Your shareable insights report is ready: +${reportUrl}${uploadHint} + +Want to dig into any section or try one of the suggestions? +`, + }, + ] + }, +} + +function isValidSessionFacets(obj: unknown): obj is SessionFacets { + if (!obj || typeof obj !== 'object') return false + const o = obj as Record + return ( + typeof o.underlying_goal === 'string' && + typeof o.outcome === 'string' && + typeof o.brief_summary === 'string' && + o.goal_categories !== null && + typeof o.goal_categories === 'object' && + o.user_satisfaction_counts !== null && + typeof o.user_satisfaction_counts === 'object' && + o.friction_counts !== null && + typeof o.friction_counts === 'object' + ) +} + +export default usageReport diff --git a/commands/install-github-app/ApiKeyStep.tsx b/commands/install-github-app/ApiKeyStep.tsx new file mode 100644 index 0000000..2dcb312 --- /dev/null +++ b/commands/install-github-app/ApiKeyStep.tsx @@ -0,0 +1,231 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useState } from 'react'; +import TextInput from '../../components/TextInput.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { Box, color, Text, useTheme } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +interface ApiKeyStepProps { + existingApiKey: string | null; + useExistingKey: boolean; + apiKeyOrOAuthToken: string; + onApiKeyChange: (value: string) => void; + onToggleUseExistingKey: (useExisting: boolean) => void; + onSubmit: () => void; + onCreateOAuthToken?: () => void; + selectedOption?: 'existing' | 'new' | 'oauth'; + onSelectOption?: (option: 'existing' | 'new' | 'oauth') => void; +} +export function ApiKeyStep(t0) { + const $ = _c(55); + const { + existingApiKey, + apiKeyOrOAuthToken, + onApiKeyChange, + onSubmit, + onToggleUseExistingKey, + onCreateOAuthToken, + selectedOption: t1, + onSelectOption + } = t0; + const selectedOption = t1 === undefined ? existingApiKey ? "existing" : onCreateOAuthToken ? "oauth" : "new" : t1; + const [cursorOffset, setCursorOffset] = useState(0); + const terminalSize = useTerminalSize(); + const [theme] = useTheme(); + let t2; + if ($[0] !== existingApiKey || $[1] !== onCreateOAuthToken || $[2] !== onSelectOption || $[3] !== onToggleUseExistingKey || $[4] !== selectedOption) { + t2 = () => { + if (selectedOption === "new" && onCreateOAuthToken) { + onSelectOption?.("oauth"); + } else { + if (selectedOption === "oauth" && existingApiKey) { + onSelectOption?.("existing"); + onToggleUseExistingKey(true); + } + } + }; + $[0] = existingApiKey; + $[1] = onCreateOAuthToken; + $[2] = onSelectOption; + $[3] = onToggleUseExistingKey; + $[4] = selectedOption; + $[5] = t2; + } else { + t2 = $[5]; + } + const handlePrevious = t2; + let t3; + if ($[6] !== onCreateOAuthToken || $[7] !== onSelectOption || $[8] !== onToggleUseExistingKey || $[9] !== selectedOption) { + t3 = () => { + if (selectedOption === "existing") { + onSelectOption?.(onCreateOAuthToken ? "oauth" : "new"); + onToggleUseExistingKey(false); + } else { + if (selectedOption === "oauth") { + onSelectOption?.("new"); + } + } + }; + $[6] = onCreateOAuthToken; + $[7] = onSelectOption; + $[8] = onToggleUseExistingKey; + $[9] = selectedOption; + $[10] = t3; + } else { + t3 = $[10]; + } + const handleNext = t3; + let t4; + if ($[11] !== onCreateOAuthToken || $[12] !== onSubmit || $[13] !== selectedOption) { + t4 = () => { + if (selectedOption === "oauth" && onCreateOAuthToken) { + onCreateOAuthToken(); + } else { + onSubmit(); + } + }; + $[11] = onCreateOAuthToken; + $[12] = onSubmit; + $[13] = selectedOption; + $[14] = t4; + } else { + t4 = $[14]; + } + const handleConfirm = t4; + const isTextInputVisible = selectedOption === "new"; + let t5; + if ($[15] !== handleConfirm || $[16] !== handleNext || $[17] !== handlePrevious) { + t5 = { + "confirm:previous": handlePrevious, + "confirm:next": handleNext, + "confirm:yes": handleConfirm + }; + $[15] = handleConfirm; + $[16] = handleNext; + $[17] = handlePrevious; + $[18] = t5; + } else { + t5 = $[18]; + } + const t6 = !isTextInputVisible; + let t7; + if ($[19] !== t6) { + t7 = { + context: "Confirmation", + isActive: t6 + }; + $[19] = t6; + $[20] = t7; + } else { + t7 = $[20]; + } + useKeybindings(t5, t7); + let t8; + if ($[21] !== handleNext || $[22] !== handlePrevious) { + t8 = { + "confirm:previous": handlePrevious, + "confirm:next": handleNext + }; + $[21] = handleNext; + $[22] = handlePrevious; + $[23] = t8; + } else { + t8 = $[23]; + } + let t9; + if ($[24] !== isTextInputVisible) { + t9 = { + context: "Confirmation", + isActive: isTextInputVisible + }; + $[24] = isTextInputVisible; + $[25] = t9; + } else { + t9 = $[25]; + } + useKeybindings(t8, t9); + let t10; + if ($[26] === Symbol.for("react.memo_cache_sentinel")) { + t10 = Install GitHub AppChoose API key; + $[26] = t10; + } else { + t10 = $[26]; + } + let t11; + if ($[27] !== existingApiKey || $[28] !== selectedOption || $[29] !== theme) { + t11 = existingApiKey && {selectedOption === "existing" ? color("success", theme)("> ") : " "}Use your existing Claude Code API key; + $[27] = existingApiKey; + $[28] = selectedOption; + $[29] = theme; + $[30] = t11; + } else { + t11 = $[30]; + } + let t12; + if ($[31] !== onCreateOAuthToken || $[32] !== selectedOption || $[33] !== theme) { + t12 = onCreateOAuthToken && {selectedOption === "oauth" ? color("success", theme)("> ") : " "}Create a long-lived token with your Claude subscription; + $[31] = onCreateOAuthToken; + $[32] = selectedOption; + $[33] = theme; + $[34] = t12; + } else { + t12 = $[34]; + } + let t13; + if ($[35] !== selectedOption || $[36] !== theme) { + t13 = selectedOption === "new" ? color("success", theme)("> ") : " "; + $[35] = selectedOption; + $[36] = theme; + $[37] = t13; + } else { + t13 = $[37]; + } + let t14; + if ($[38] !== t13) { + t14 = {t13}Enter a new API key; + $[38] = t13; + $[39] = t14; + } else { + t14 = $[39]; + } + let t15; + if ($[40] !== apiKeyOrOAuthToken || $[41] !== cursorOffset || $[42] !== onApiKeyChange || $[43] !== onSubmit || $[44] !== selectedOption || $[45] !== terminalSize) { + t15 = selectedOption === "new" && ; + $[40] = apiKeyOrOAuthToken; + $[41] = cursorOffset; + $[42] = onApiKeyChange; + $[43] = onSubmit; + $[44] = selectedOption; + $[45] = terminalSize; + $[46] = t15; + } else { + t15 = $[46]; + } + let t16; + if ($[47] !== t11 || $[48] !== t12 || $[49] !== t14 || $[50] !== t15) { + t16 = {t10}{t11}{t12}{t14}{t15}; + $[47] = t11; + $[48] = t12; + $[49] = t14; + $[50] = t15; + $[51] = t16; + } else { + t16 = $[51]; + } + let t17; + if ($[52] === Symbol.for("react.memo_cache_sentinel")) { + t17 = ↑/↓ to select · Enter to continue; + $[52] = t17; + } else { + t17 = $[52]; + } + let t18; + if ($[53] !== t16) { + t18 = <>{t16}{t17}; + $[53] = t16; + $[54] = t18; + } else { + t18 = $[54]; + } + return t18; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlU3RhdGUiLCJUZXh0SW5wdXQiLCJ1c2VUZXJtaW5hbFNpemUiLCJCb3giLCJjb2xvciIsIlRleHQiLCJ1c2VUaGVtZSIsInVzZUtleWJpbmRpbmdzIiwiQXBpS2V5U3RlcFByb3BzIiwiZXhpc3RpbmdBcGlLZXkiLCJ1c2VFeGlzdGluZ0tleSIsImFwaUtleU9yT0F1dGhUb2tlbiIsIm9uQXBpS2V5Q2hhbmdlIiwidmFsdWUiLCJvblRvZ2dsZVVzZUV4aXN0aW5nS2V5IiwidXNlRXhpc3RpbmciLCJvblN1Ym1pdCIsIm9uQ3JlYXRlT0F1dGhUb2tlbiIsInNlbGVjdGVkT3B0aW9uIiwib25TZWxlY3RPcHRpb24iLCJvcHRpb24iLCJBcGlLZXlTdGVwIiwidDAiLCIkIiwiX2MiLCJ0MSIsInVuZGVmaW5lZCIsImN1cnNvck9mZnNldCIsInNldEN1cnNvck9mZnNldCIsInRlcm1pbmFsU2l6ZSIsInRoZW1lIiwidDIiLCJoYW5kbGVQcmV2aW91cyIsInQzIiwiaGFuZGxlTmV4dCIsInQ0IiwiaGFuZGxlQ29uZmlybSIsImlzVGV4dElucHV0VmlzaWJsZSIsInQ1IiwidDYiLCJ0NyIsImNvbnRleHQiLCJpc0FjdGl2ZSIsInQ4IiwidDkiLCJ0MTAiLCJTeW1ib2wiLCJmb3IiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJjb2x1bW5zIiwidDE2IiwidDE3IiwidDE4Il0sInNvdXJjZXMiOlsiQXBpS2V5U3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHVzZUNhbGxiYWNrLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IFRleHRJbnB1dCBmcm9tICcuLi8uLi9jb21wb25lbnRzL1RleHRJbnB1dC5qcydcbmltcG9ydCB7IHVzZVRlcm1pbmFsU2l6ZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZVRlcm1pbmFsU2l6ZS5qcydcbmltcG9ydCB7IEJveCwgY29sb3IsIFRleHQsIHVzZVRoZW1lIH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZ3MgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuXG5pbnRlcmZhY2UgQXBpS2V5U3RlcFByb3BzIHtcbiAgZXhpc3RpbmdBcGlLZXk6IHN0cmluZyB8IG51bGxcbiAgdXNlRXhpc3RpbmdLZXk6IGJvb2xlYW5cbiAgYXBpS2V5T3JPQXV0aFRva2VuOiBzdHJpbmdcbiAgb25BcGlLZXlDaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG4gIG9uVG9nZ2xlVXNlRXhpc3RpbmdLZXk6ICh1c2VFeGlzdGluZzogYm9vbGVhbikgPT4gdm9pZFxuICBvblN1Ym1pdDogKCkgPT4gdm9pZFxuICBvbkNyZWF0ZU9BdXRoVG9rZW4/OiAoKSA9PiB2b2lkXG4gIHNlbGVjdGVkT3B0aW9uPzogJ2V4aXN0aW5nJyB8ICduZXcnIHwgJ29hdXRoJ1xuICBvblNlbGVjdE9wdGlvbj86IChvcHRpb246ICdleGlzdGluZycgfCAnbmV3JyB8ICdvYXV0aCcpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIEFwaUtleVN0ZXAoe1xuICBleGlzdGluZ0FwaUtleSxcbiAgYXBpS2V5T3JPQXV0aFRva2VuLFxuICBvbkFwaUtleUNoYW5nZSxcbiAgb25TdWJtaXQsXG4gIG9uVG9nZ2xlVXNlRXhpc3RpbmdLZXksXG4gIG9uQ3JlYXRlT0F1dGhUb2tlbixcbiAgc2VsZWN0ZWRPcHRpb24gPSBleGlzdGluZ0FwaUtleVxuICAgID8gJ2V4aXN0aW5nJ1xuICAgIDogb25DcmVhdGVPQXV0aFRva2VuXG4gICAgICA/ICdvYXV0aCdcbiAgICAgIDogJ25ldycsXG4gIG9uU2VsZWN0T3B0aW9uLFxufTogQXBpS2V5U3RlcFByb3BzKSB7XG4gIGNvbnN0IFtjdXJzb3JPZmZzZXQsIHNldEN1cnNvck9mZnNldF0gPSB1c2VTdGF0ZSgwKVxuICBjb25zdCB0ZXJtaW5hbFNpemUgPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCBbdGhlbWVdID0gdXNlVGhlbWUoKVxuXG4gIGNvbnN0IGhhbmRsZVByZXZpb3VzID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChzZWxlY3RlZE9wdGlvbiA9PT0gJ25ldycgJiYgb25DcmVhdGVPQXV0aFRva2VuKSB7XG4gICAgICAvLyBGcm9tICduZXcnIGdvIHVwIHRvICdvYXV0aCdcbiAgICAgIG9uU2VsZWN0T3B0aW9uPy4oJ29hdXRoJylcbiAgICB9IGVsc2UgaWYgKHNlbGVjdGVkT3B0aW9uID09PSAnb2F1dGgnICYmIGV4aXN0aW5nQXBpS2V5KSB7XG4gICAgICAvLyBGcm9tICdvYXV0aCcgZ28gdXAgdG8gJ2V4aXN0aW5nJyAob25seSBpZiBpdCBleGlzdHMpXG4gICAgICBvblNlbGVjdE9wdGlvbj8uKCdleGlzdGluZycpXG4gICAgICBvblRvZ2dsZVVzZUV4aXN0aW5nS2V5KHRydWUpXG4gICAgfVxuICB9LCBbXG4gICAgc2VsZWN0ZWRPcHRpb24sXG4gICAgb25DcmVhdGVPQXV0aFRva2VuLFxuICAgIGV4aXN0aW5nQXBpS2V5LFxuICAgIG9uU2VsZWN0T3B0aW9uLFxuICAgIG9uVG9nZ2xlVXNlRXhpc3RpbmdLZXksXG4gIF0pXG5cbiAgY29uc3QgaGFuZGxlTmV4dCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoc2VsZWN0ZWRPcHRpb24gPT09ICdleGlzdGluZycpIHtcbiAgICAgIC8vIEZyb20gJ2V4aXN0aW5nJyBnbyBkb3duIHRvICdvYXV0aCcgKGlmIGF2YWlsYWJsZSkgb3IgJ25ldydcbiAgICAgIG9uU2VsZWN0T3B0aW9uPy4ob25DcmVhdGVPQXV0aFRva2VuID8gJ29hdXRoJyA6ICduZXcnKVxuICAgICAgb25Ub2dnbGVVc2VFeGlzdGluZ0tleShmYWxzZSlcbiAgICB9IGVsc2UgaWYgKHNlbGVjdGVkT3B0aW9uID09PSAnb2F1dGgnKSB7XG4gICAgICAvLyBGcm9tICdvYXV0aCcgZ28gZG93biB0byAnbmV3J1xuICAgICAgb25TZWxlY3RPcHRpb24/LignbmV3JylcbiAgICB9XG4gIH0sIFtcbiAgICBzZWxlY3RlZE9wdGlvbixcbiAgICBvbkNyZWF0ZU9BdXRoVG9rZW4sXG4gICAgb25TZWxlY3RPcHRpb24sXG4gICAgb25Ub2dnbGVVc2VFeGlzdGluZ0tleSxcbiAgXSlcblxuICBjb25zdCBoYW5kbGVDb25maXJtID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChzZWxlY3RlZE9wdGlvbiA9PT0gJ29hdXRoJyAmJiBvbkNyZWF0ZU9BdXRoVG9rZW4pIHtcbiAgICAgIG9uQ3JlYXRlT0F1dGhUb2tlbigpXG4gICAgfSBlbHNlIHtcbiAgICAgIG9uU3VibWl0KClcbiAgICB9XG4gIH0sIFtzZWxlY3RlZE9wdGlvbiwgb25DcmVhdGVPQXV0aFRva2VuLCBvblN1Ym1pdF0pXG5cbiAgLy8gV2hlbiB0aGUgdGV4dCBpbnB1dCBpcyB2aXNpYmxlLCBvbWl0IGNvbmZpcm06eWVzIHNvIGJhcmUgJ3knIHBhc3Nlc1xuICAvLyB0aHJvdWdoIHRvIHRoZSBpbnB1dCBpbnN0ZWFkIG9mIHN1Ym1pdHRpbmcuIFRleHRJbnB1dCdzIG9uU3VibWl0IGhhbmRsZXNcbiAgLy8gRW50ZXIuIEtlZXAgdGhlIENvbmZpcm1hdGlvbiBjb250ZXh0IChub3QgU2V0dGluZ3MpIHRvIGF2b2lkIGovayBiaW5kaW5ncy5cbiAgY29uc3QgaXNUZXh0SW5wdXRWaXNpYmxlID0gc2VsZWN0ZWRPcHRpb24gPT09ICduZXcnXG4gIHVzZUtleWJpbmRpbmdzKFxuICAgIHtcbiAgICAgICdjb25maXJtOnByZXZpb3VzJzogaGFuZGxlUHJldmlvdXMsXG4gICAgICAnY29uZmlybTpuZXh0JzogaGFuZGxlTmV4dCxcbiAgICAgICdjb25maXJtOnllcyc6IGhhbmRsZUNvbmZpcm0sXG4gICAgfSxcbiAgICB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nLCBpc0FjdGl2ZTogIWlzVGV4dElucHV0VmlzaWJsZSB9LFxuICApXG4gIHVzZUtleWJpbmRpbmdzKFxuICAgIHtcbiAgICAgICdjb25maXJtOnByZXZpb3VzJzogaGFuZGxlUHJldmlvdXMsXG4gICAgICAnY29uZmlybTpuZXh0JzogaGFuZGxlTmV4dCxcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsIGlzQWN0aXZlOiBpc1RleHRJbnB1dFZpc2libGUgfSxcbiAgKVxuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGJvcmRlclN0eWxlPVwicm91bmRcIiBwYWRkaW5nWD17MX0+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgPFRleHQgYm9sZD5JbnN0YWxsIEdpdEh1YiBBcHA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+Q2hvb3NlIEFQSSBrZXk8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7ZXhpc3RpbmdBcGlLZXkgJiYgKFxuICAgICAgICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICB7c2VsZWN0ZWRPcHRpb24gPT09ICdleGlzdGluZydcbiAgICAgICAgICAgICAgICA/IGNvbG9yKCdzdWNjZXNzJywgdGhlbWUpKCc+ICcpXG4gICAgICAgICAgICAgICAgOiAnICAnfVxuICAgICAgICAgICAgICBVc2UgeW91ciBleGlzdGluZyBDbGF1ZGUgQ29kZSBBUEkga2V5XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICAgIHtvbkNyZWF0ZU9BdXRoVG9rZW4gJiYgKFxuICAgICAgICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICB7c2VsZWN0ZWRPcHRpb24gPT09ICdvYXV0aCdcbiAgICAgICAgICAgICAgICA/IGNvbG9yKCdzdWNjZXNzJywgdGhlbWUpKCc+ICcpXG4gICAgICAgICAgICAgICAgOiAnICAnfVxuICAgICAgICAgICAgICBDcmVhdGUgYSBsb25nLWxpdmVkIHRva2VuIHdpdGggeW91ciBDbGF1ZGUgc3Vic2NyaXB0aW9uXG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIHtzZWxlY3RlZE9wdGlvbiA9PT0gJ25ldycgPyBjb2xvcignc3VjY2VzcycsIHRoZW1lKSgnPiAnKSA6ICcgICd9XG4gICAgICAgICAgICBFbnRlciBhIG5ldyBBUEkga2V5XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAge3NlbGVjdGVkT3B0aW9uID09PSAnbmV3JyAmJiAoXG4gICAgICAgICAgPFRleHRJbnB1dFxuICAgICAgICAgICAgdmFsdWU9e2FwaUtleU9yT0F1dGhUb2tlbn1cbiAgICAgICAgICAgIG9uQ2hhbmdlPXtvbkFwaUtleUNoYW5nZX1cbiAgICAgICAgICAgIG9uU3VibWl0PXtvblN1Ym1pdH1cbiAgICAgICAgICAgIG9uUGFzdGU9e29uQXBpS2V5Q2hhbmdlfVxuICAgICAgICAgICAgZm9jdXM9e3RydWV9XG4gICAgICAgICAgICBwbGFjZWhvbGRlcj1cInNrLWFudOKApiAoQ3JlYXRlIGEgbmV3IGtleSBhdCBodHRwczovL3BsYXRmb3JtLmNsYXVkZS5jb20vc2V0dGluZ3Mva2V5cylcIlxuICAgICAgICAgICAgbWFzaz1cIipcIlxuICAgICAgICAgICAgY29sdW1ucz17dGVybWluYWxTaXplLmNvbHVtbnN9XG4gICAgICAgICAgICBjdXJzb3JPZmZzZXQ9e2N1cnNvck9mZnNldH1cbiAgICAgICAgICAgIG9uQ2hhbmdlQ3Vyc29yT2Zmc2V0PXtzZXRDdXJzb3JPZmZzZXR9XG4gICAgICAgICAgICBzaG93Q3Vyc29yPXt0cnVlfVxuICAgICAgICAgIC8+XG4gICAgICAgICl9XG4gICAgICA8L0JveD5cbiAgICAgIDxCb3ggbWFyZ2luTGVmdD17M30+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPuKGkS/ihpMgdG8gc2VsZWN0IMK3IEVudGVyIHRvIGNvbnRpbnVlPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgPC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFBSUMsV0FBVyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUNwRCxPQUFPQyxTQUFTLE1BQU0sK0JBQStCO0FBQ3JELFNBQVNDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDaEUsU0FBU0MsR0FBRyxFQUFFQyxLQUFLLEVBQUVDLElBQUksRUFBRUMsUUFBUSxRQUFRLGNBQWM7QUFDekQsU0FBU0MsY0FBYyxRQUFRLG9DQUFvQztBQUVuRSxVQUFVQyxlQUFlLENBQUM7RUFDeEJDLGNBQWMsRUFBRSxNQUFNLEdBQUcsSUFBSTtFQUM3QkMsY0FBYyxFQUFFLE9BQU87RUFDdkJDLGtCQUFrQixFQUFFLE1BQU07RUFDMUJDLGNBQWMsRUFBRSxDQUFDQyxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSTtFQUN2Q0Msc0JBQXNCLEVBQUUsQ0FBQ0MsV0FBVyxFQUFFLE9BQU8sRUFBRSxHQUFHLElBQUk7RUFDdERDLFFBQVEsRUFBRSxHQUFHLEdBQUcsSUFBSTtFQUNwQkMsa0JBQWtCLENBQUMsRUFBRSxHQUFHLEdBQUcsSUFBSTtFQUMvQkMsY0FBYyxDQUFDLEVBQUUsVUFBVSxHQUFHLEtBQUssR0FBRyxPQUFPO0VBQzdDQyxjQUFjLENBQUMsRUFBRSxDQUFDQyxNQUFNLEVBQUUsVUFBVSxHQUFHLEtBQUssR0FBRyxPQUFPLEVBQUUsR0FBRyxJQUFJO0FBQ2pFO0FBRUEsT0FBTyxTQUFBQyxXQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQW9CO0lBQUFmLGNBQUE7SUFBQUUsa0JBQUE7SUFBQUMsY0FBQTtJQUFBSSxRQUFBO0lBQUFGLHNCQUFB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 \ No newline at end of file diff --git a/commands/install-github-app/CheckExistingSecretStep.tsx b/commands/install-github-app/CheckExistingSecretStep.tsx new file mode 100644 index 0000000..ff2bf45 --- /dev/null +++ b/commands/install-github-app/CheckExistingSecretStep.tsx @@ -0,0 +1,190 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useState } from 'react'; +import TextInput from '../../components/TextInput.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { Box, color, Text, useTheme } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +interface CheckExistingSecretStepProps { + useExistingSecret: boolean; + secretName: string; + onToggleUseExistingSecret: (useExisting: boolean) => void; + onSecretNameChange: (value: string) => void; + onSubmit: () => void; +} +export function CheckExistingSecretStep(t0) { + const $ = _c(42); + const { + useExistingSecret, + secretName, + onToggleUseExistingSecret, + onSecretNameChange, + onSubmit + } = t0; + const [cursorOffset, setCursorOffset] = useState(0); + const terminalSize = useTerminalSize(); + const [theme] = useTheme(); + let t1; + if ($[0] !== onToggleUseExistingSecret) { + t1 = () => onToggleUseExistingSecret(true); + $[0] = onToggleUseExistingSecret; + $[1] = t1; + } else { + t1 = $[1]; + } + const handlePrevious = t1; + let t2; + if ($[2] !== onToggleUseExistingSecret) { + t2 = () => onToggleUseExistingSecret(false); + $[2] = onToggleUseExistingSecret; + $[3] = t2; + } else { + t2 = $[3]; + } + const handleNext = t2; + let t3; + if ($[4] !== handleNext || $[5] !== handlePrevious || $[6] !== onSubmit) { + t3 = { + "confirm:previous": handlePrevious, + "confirm:next": handleNext, + "confirm:yes": onSubmit + }; + $[4] = handleNext; + $[5] = handlePrevious; + $[6] = onSubmit; + $[7] = t3; + } else { + t3 = $[7]; + } + let t4; + if ($[8] !== useExistingSecret) { + t4 = { + context: "Confirmation", + isActive: useExistingSecret + }; + $[8] = useExistingSecret; + $[9] = t4; + } else { + t4 = $[9]; + } + useKeybindings(t3, t4); + let t5; + if ($[10] !== handleNext || $[11] !== handlePrevious) { + t5 = { + "confirm:previous": handlePrevious, + "confirm:next": handleNext + }; + $[10] = handleNext; + $[11] = handlePrevious; + $[12] = t5; + } else { + t5 = $[12]; + } + const t6 = !useExistingSecret; + let t7; + if ($[13] !== t6) { + t7 = { + context: "Confirmation", + isActive: t6 + }; + $[13] = t6; + $[14] = t7; + } else { + t7 = $[14]; + } + useKeybindings(t5, t7); + let t8; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t8 = Install GitHub AppSetup API key secret; + $[15] = t8; + } else { + t8 = $[15]; + } + let t9; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t9 = ANTHROPIC_API_KEY already exists in repository secrets!; + $[16] = t9; + } else { + t9 = $[16]; + } + let t10; + if ($[17] === Symbol.for("react.memo_cache_sentinel")) { + t10 = Would you like to:; + $[17] = t10; + } else { + t10 = $[17]; + } + let t11; + if ($[18] !== theme || $[19] !== useExistingSecret) { + t11 = useExistingSecret ? color("success", theme)("> ") : " "; + $[18] = theme; + $[19] = useExistingSecret; + $[20] = t11; + } else { + t11 = $[20]; + } + let t12; + if ($[21] !== t11) { + t12 = {t11}Use the existing API key; + $[21] = t11; + $[22] = t12; + } else { + t12 = $[22]; + } + let t13; + if ($[23] !== theme || $[24] !== useExistingSecret) { + t13 = !useExistingSecret ? color("success", theme)("> ") : " "; + $[23] = theme; + $[24] = useExistingSecret; + $[25] = t13; + } else { + t13 = $[25]; + } + let t14; + if ($[26] !== t13) { + t14 = {t13}Create a new secret with a different name; + $[26] = t13; + $[27] = t14; + } else { + t14 = $[27]; + } + let t15; + if ($[28] !== cursorOffset || $[29] !== onSecretNameChange || $[30] !== onSubmit || $[31] !== secretName || $[32] !== terminalSize || $[33] !== useExistingSecret) { + t15 = !useExistingSecret && <>Enter new secret name (alphanumeric with underscores):; + $[28] = cursorOffset; + $[29] = onSecretNameChange; + $[30] = onSubmit; + $[31] = secretName; + $[32] = terminalSize; + $[33] = useExistingSecret; + $[34] = t15; + } else { + t15 = $[34]; + } + let t16; + if ($[35] !== t12 || $[36] !== t14 || $[37] !== t15) { + t16 = {t8}{t9}{t10}{t12}{t14}{t15}; + $[35] = t12; + $[36] = t14; + $[37] = t15; + $[38] = t16; + } else { + t16 = $[38]; + } + let t17; + if ($[39] === Symbol.for("react.memo_cache_sentinel")) { + t17 = ↑/↓ to select · Enter to continue; + $[39] = t17; + } else { + t17 = $[39]; + } + let t18; + if ($[40] !== t16) { + t18 = <>{t16}{t17}; + $[40] = t16; + $[41] = t18; + } else { + t18 = $[41]; + } + return t18; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlU3RhdGUiLCJUZXh0SW5wdXQiLCJ1c2VUZXJtaW5hbFNpemUiLCJCb3giLCJjb2xvciIsIlRleHQiLCJ1c2VUaGVtZSIsInVzZUtleWJpbmRpbmdzIiwiQ2hlY2tFeGlzdGluZ1NlY3JldFN0ZXBQcm9wcyIsInVzZUV4aXN0aW5nU2VjcmV0Iiwic2VjcmV0TmFtZSIsIm9uVG9nZ2xlVXNlRXhpc3RpbmdTZWNyZXQiLCJ1c2VFeGlzdGluZyIsIm9uU2VjcmV0TmFtZUNoYW5nZSIsInZhbHVlIiwib25TdWJtaXQiLCJDaGVja0V4aXN0aW5nU2VjcmV0U3RlcCIsInQwIiwiJCIsIl9jIiwiY3Vyc29yT2Zmc2V0Iiwic2V0Q3Vyc29yT2Zmc2V0IiwidGVybWluYWxTaXplIiwidGhlbWUiLCJ0MSIsImhhbmRsZVByZXZpb3VzIiwidDIiLCJoYW5kbGVOZXh0IiwidDMiLCJ0NCIsImNvbnRleHQiLCJpc0FjdGl2ZSIsInQ1IiwidDYiLCJ0NyIsInQ4IiwiU3ltYm9sIiwiZm9yIiwidDkiLCJ0MTAiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJjb2x1bW5zIiwidDE2IiwidDE3IiwidDE4Il0sInNvdXJjZXMiOlsiQ2hlY2tFeGlzdGluZ1NlY3JldFN0ZXAudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VDYWxsYmFjaywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCBUZXh0SW5wdXQgZnJvbSAnLi4vLi4vY29tcG9uZW50cy9UZXh0SW5wdXQuanMnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyBCb3gsIGNvbG9yLCBUZXh0LCB1c2VUaGVtZSB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmdzIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcblxuaW50ZXJmYWNlIENoZWNrRXhpc3RpbmdTZWNyZXRTdGVwUHJvcHMge1xuICB1c2VFeGlzdGluZ1NlY3JldDogYm9vbGVhblxuICBzZWNyZXROYW1lOiBzdHJpbmdcbiAgb25Ub2dnbGVVc2VFeGlzdGluZ1NlY3JldDogKHVzZUV4aXN0aW5nOiBib29sZWFuKSA9PiB2b2lkXG4gIG9uU2VjcmV0TmFtZUNoYW5nZTogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWRcbiAgb25TdWJtaXQ6ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIENoZWNrRXhpc3RpbmdTZWNyZXRTdGVwKHtcbiAgdXNlRXhpc3RpbmdTZWNyZXQsXG4gIHNlY3JldE5hbWUsXG4gIG9uVG9nZ2xlVXNlRXhpc3RpbmdTZWNyZXQsXG4gIG9uU2VjcmV0TmFtZUNoYW5nZSxcbiAgb25TdWJtaXQsXG59OiBDaGVja0V4aXN0aW5nU2VjcmV0U3RlcFByb3BzKSB7XG4gIGNvbnN0IFtjdXJzb3JPZmZzZXQsIHNldEN1cnNvck9mZnNldF0gPSB1c2VTdGF0ZSgwKVxuICBjb25zdCB0ZXJtaW5hbFNpemUgPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCBbdGhlbWVdID0gdXNlVGhlbWUoKVxuXG4gIC8vIFdoZW4gdGhlIHRleHQgaW5wdXQgaXMgdmlzaWJsZSwgb21pdCBjb25maXJtOnllcyBzbyBiYXJlICd5JyBwYXNzZXNcbiAgLy8gdGhyb3VnaCB0byB0aGUgaW5wdXQgaW5zdGVhZCBvZiBzdWJtaXR0aW5nLiBUZXh0SW5wdXQncyBvblN1Ym1pdCBoYW5kbGVzXG4gIC8vIEVudGVyLiBLZWVwIHRoZSBDb25maXJtYXRpb24gY29udGV4dCAobm90IFNldHRpbmdzKSB0byBhdm9pZCBqL2sgYmluZGluZ3MuXG4gIGNvbnN0IGhhbmRsZVByZXZpb3VzID0gdXNlQ2FsbGJhY2soXG4gICAgKCkgPT4gb25Ub2dnbGVVc2VFeGlzdGluZ1NlY3JldCh0cnVlKSxcbiAgICBbb25Ub2dnbGVVc2VFeGlzdGluZ1NlY3JldF0sXG4gIClcbiAgY29uc3QgaGFuZGxlTmV4dCA9IHVzZUNhbGxiYWNrKFxuICAgICgpID0+IG9uVG9nZ2xlVXNlRXhpc3RpbmdTZWNyZXQoZmFsc2UpLFxuICAgIFtvblRvZ2dsZVVzZUV4aXN0aW5nU2VjcmV0XSxcbiAgKVxuICB1c2VLZXliaW5kaW5ncyhcbiAgICB7XG4gICAgICAnY29uZmlybTpwcmV2aW91cyc6IGhhbmRsZVByZXZpb3VzLFxuICAgICAgJ2NvbmZpcm06bmV4dCc6IGhhbmRsZU5leHQsXG4gICAgICAnY29uZmlybTp5ZXMnOiBvblN1Ym1pdCxcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsIGlzQWN0aXZlOiB1c2VFeGlzdGluZ1NlY3JldCB9LFxuICApXG4gIHVzZUtleWJpbmRpbmdzKFxuICAgIHtcbiAgICAgICdjb25maXJtOnByZXZpb3VzJzogaGFuZGxlUHJldmlvdXMsXG4gICAgICAnY29uZmlybTpuZXh0JzogaGFuZGxlTmV4dCxcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsIGlzQWN0aXZlOiAhdXNlRXhpc3RpbmdTZWNyZXQgfSxcbiAgKVxuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGJvcmRlclN0eWxlPVwicm91bmRcIiBwYWRkaW5nWD17MX0+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgPFRleHQgYm9sZD5JbnN0YWxsIEdpdEh1YiBBcHA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+U2V0dXAgQVBJIGtleSBzZWNyZXQ8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJ3YXJuaW5nXCI+XG4gICAgICAgICAgICBBTlRIUk9QSUNfQVBJX0tFWSBhbHJlYWR5IGV4aXN0cyBpbiByZXBvc2l0b3J5IHNlY3JldHMhXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgIDxUZXh0PldvdWxkIHlvdSBsaWtlIHRvOjwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIHt1c2VFeGlzdGluZ1NlY3JldCA/IGNvbG9yKCdzdWNjZXNzJywgdGhlbWUpKCc+ICcpIDogJyAgJ31cbiAgICAgICAgICAgIFVzZSB0aGUgZXhpc3RpbmcgQVBJIGtleVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIHshdXNlRXhpc3RpbmdTZWNyZXQgPyBjb2xvcignc3VjY2VzcycsIHRoZW1lKSgnPiAnKSA6ICcgICd9XG4gICAgICAgICAgICBDcmVhdGUgYSBuZXcgc2VjcmV0IHdpdGggYSBkaWZmZXJlbnQgbmFtZVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHshdXNlRXhpc3RpbmdTZWNyZXQgJiYgKFxuICAgICAgICAgIDw+XG4gICAgICAgICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAgIEVudGVyIG5ldyBzZWNyZXQgbmFtZSAoYWxwaGFudW1lcmljIHdpdGggdW5kZXJzY29yZXMpOlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgIDxUZXh0SW5wdXRcbiAgICAgICAgICAgICAgdmFsdWU9e3NlY3JldE5hbWV9XG4gICAgICAgICAgICAgIG9uQ2hhbmdlPXtvblNlY3JldE5hbWVDaGFuZ2V9XG4gICAgICAgICAgICAgIG9uU3VibWl0PXtvblN1Ym1pdH1cbiAgICAgICAgICAgICAgZm9jdXM9e3RydWV9XG4gICAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiZS5nLiwgQ0xBVURFX0FQSV9LRVlcIlxuICAgICAgICAgICAgICBjb2x1bW5zPXt0ZXJtaW5hbFNpemUuY29sdW1uc31cbiAgICAgICAgICAgICAgY3Vyc29yT2Zmc2V0PXtjdXJzb3JPZmZzZXR9XG4gICAgICAgICAgICAgIG9uQ2hhbmdlQ3Vyc29yT2Zmc2V0PXtzZXRDdXJzb3JPZmZzZXR9XG4gICAgICAgICAgICAgIHNob3dDdXJzb3I9e3RydWV9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgIDwvPlxuICAgICAgICApfVxuICAgICAgPC9Cb3g+XG4gICAgICA8Qm94IG1hcmdpbkxlZnQ9ezN9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj7ihpEv4oaTIHRvIHNlbGVjdCDCtyBFbnRlciB0byBjb250aW51ZTwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUlDLFdBQVcsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDcEQsT0FBT0MsU0FBUyxNQUFNLCtCQUErQjtBQUNyRCxTQUFTQyxlQUFlLFFBQVEsZ0NBQWdDO0FBQ2hFLFNBQVNDLEdBQUcsRUFBRUMsS0FBSyxFQUFFQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxjQUFjO0FBQ3pELFNBQVNDLGNBQWMsUUFBUSxvQ0FBb0M7QUFFbkUsVUFBVUMsNEJBQTRCLENBQUM7RUFDckNDLGlCQUFpQixFQUFFLE9BQU87RUFDMUJDLFVBQVUsRUFBRSxNQUFNO0VBQ2xCQyx5QkFBeUIsRUFBRSxDQUFDQyxXQUFXLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSTtFQUN6REMsa0JBQWtCLEVBQUUsQ0FBQ0MsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUk7RUFDM0NDLFFBQVEsRUFBRSxHQUFHLEdBQUcsSUFBSTtBQUN0QjtB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 \ No newline at end of file diff --git a/commands/install-github-app/CheckGitHubStep.tsx b/commands/install-github-app/CheckGitHubStep.tsx new file mode 100644 index 0000000..5bf1d8f --- /dev/null +++ b/commands/install-github-app/CheckGitHubStep.tsx @@ -0,0 +1,15 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Text } from '../../ink.js'; +export function CheckGitHubStep() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = Checking GitHub CLI installation…; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJDaGVja0dpdEh1YlN0ZXAiLCIkIiwiX2MiLCJ0MCIsIlN5bWJvbCIsImZvciJdLCJzb3VyY2VzIjpbIkNoZWNrR2l0SHViU3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIENoZWNrR2l0SHViU3RlcCgpIHtcbiAgcmV0dXJuIDxUZXh0PkNoZWNraW5nIEdpdEh1YiBDTEkgaW5zdGFsbGF0aW9u4oCmPC9UZXh0PlxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsSUFBSSxRQUFRLGNBQWM7QUFFbkMsT0FBTyxTQUFBQyxnQkFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFHLE1BQUEsQ0FBQUMsR0FBQTtJQUNFRixFQUFBLElBQUMsSUFBSSxDQUFDLGlDQUFpQyxFQUF0QyxJQUFJLENBQXlDO0lBQUFGLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsT0FBOUNFLEVBQThDO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/commands/install-github-app/ChooseRepoStep.tsx b/commands/install-github-app/ChooseRepoStep.tsx new file mode 100644 index 0000000..04d1a6b --- /dev/null +++ b/commands/install-github-app/ChooseRepoStep.tsx @@ -0,0 +1,211 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useState } from 'react'; +import TextInput from '../../components/TextInput.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +interface ChooseRepoStepProps { + currentRepo: string | null; + useCurrentRepo: boolean; + repoUrl: string; + onRepoUrlChange: (value: string) => void; + onToggleUseCurrentRepo: (useCurrentRepo: boolean) => void; + onSubmit: () => void; +} +export function ChooseRepoStep(t0) { + const $ = _c(49); + const { + currentRepo, + useCurrentRepo, + repoUrl, + onRepoUrlChange, + onSubmit, + onToggleUseCurrentRepo + } = t0; + const [cursorOffset, setCursorOffset] = useState(0); + const [showEmptyError, setShowEmptyError] = useState(false); + const terminalSize = useTerminalSize(); + const textInputColumns = terminalSize.columns; + let t1; + if ($[0] !== currentRepo || $[1] !== onSubmit || $[2] !== repoUrl || $[3] !== useCurrentRepo) { + t1 = () => { + const repoName = useCurrentRepo ? currentRepo : repoUrl; + if (!repoName?.trim()) { + setShowEmptyError(true); + return; + } + onSubmit(); + }; + $[0] = currentRepo; + $[1] = onSubmit; + $[2] = repoUrl; + $[3] = useCurrentRepo; + $[4] = t1; + } else { + t1 = $[4]; + } + const handleSubmit = t1; + const isTextInputVisible = !useCurrentRepo || !currentRepo; + let t2; + if ($[5] !== onToggleUseCurrentRepo) { + t2 = () => { + onToggleUseCurrentRepo(true); + setShowEmptyError(false); + }; + $[5] = onToggleUseCurrentRepo; + $[6] = t2; + } else { + t2 = $[6]; + } + const handlePrevious = t2; + let t3; + if ($[7] !== onToggleUseCurrentRepo) { + t3 = () => { + onToggleUseCurrentRepo(false); + setShowEmptyError(false); + }; + $[7] = onToggleUseCurrentRepo; + $[8] = t3; + } else { + t3 = $[8]; + } + const handleNext = t3; + let t4; + if ($[9] !== handleNext || $[10] !== handlePrevious || $[11] !== handleSubmit) { + t4 = { + "confirm:previous": handlePrevious, + "confirm:next": handleNext, + "confirm:yes": handleSubmit + }; + $[9] = handleNext; + $[10] = handlePrevious; + $[11] = handleSubmit; + $[12] = t4; + } else { + t4 = $[12]; + } + const t5 = !isTextInputVisible; + let t6; + if ($[13] !== t5) { + t6 = { + context: "Confirmation", + isActive: t5 + }; + $[13] = t5; + $[14] = t6; + } else { + t6 = $[14]; + } + useKeybindings(t4, t6); + let t7; + if ($[15] !== handleNext || $[16] !== handlePrevious) { + t7 = { + "confirm:previous": handlePrevious, + "confirm:next": handleNext + }; + $[15] = handleNext; + $[16] = handlePrevious; + $[17] = t7; + } else { + t7 = $[17]; + } + let t8; + if ($[18] !== isTextInputVisible) { + t8 = { + context: "Confirmation", + isActive: isTextInputVisible + }; + $[18] = isTextInputVisible; + $[19] = t8; + } else { + t8 = $[19]; + } + useKeybindings(t7, t8); + let t9; + if ($[20] === Symbol.for("react.memo_cache_sentinel")) { + t9 = Install GitHub AppSelect GitHub repository; + $[20] = t9; + } else { + t9 = $[20]; + } + let t10; + if ($[21] !== currentRepo || $[22] !== useCurrentRepo) { + t10 = currentRepo && {useCurrentRepo ? "> " : " "}Use current repository: {currentRepo}; + $[21] = currentRepo; + $[22] = useCurrentRepo; + $[23] = t10; + } else { + t10 = $[23]; + } + const t11 = !useCurrentRepo || !currentRepo; + const t12 = !useCurrentRepo || !currentRepo ? "permission" : undefined; + const t13 = !useCurrentRepo || !currentRepo ? "> " : " "; + const t14 = currentRepo ? "Enter a different repository" : "Enter repository"; + let t15; + if ($[24] !== t11 || $[25] !== t12 || $[26] !== t13 || $[27] !== t14) { + t15 = {t13}{t14}; + $[24] = t11; + $[25] = t12; + $[26] = t13; + $[27] = t14; + $[28] = t15; + } else { + t15 = $[28]; + } + let t16; + if ($[29] !== currentRepo || $[30] !== cursorOffset || $[31] !== handleSubmit || $[32] !== onRepoUrlChange || $[33] !== repoUrl || $[34] !== textInputColumns || $[35] !== useCurrentRepo) { + t16 = (!useCurrentRepo || !currentRepo) && { + onRepoUrlChange(value); + setShowEmptyError(false); + }} onSubmit={handleSubmit} focus={true} placeholder={"Enter a repo as owner/repo or https://github.com/owner/repo\u2026"} columns={textInputColumns} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} showCursor={true} />; + $[29] = currentRepo; + $[30] = cursorOffset; + $[31] = handleSubmit; + $[32] = onRepoUrlChange; + $[33] = repoUrl; + $[34] = textInputColumns; + $[35] = useCurrentRepo; + $[36] = t16; + } else { + t16 = $[36]; + } + let t17; + if ($[37] !== t10 || $[38] !== t15 || $[39] !== t16) { + t17 = {t9}{t10}{t15}{t16}; + $[37] = t10; + $[38] = t15; + $[39] = t16; + $[40] = t17; + } else { + t17 = $[40]; + } + let t18; + if ($[41] !== showEmptyError) { + t18 = showEmptyError && Please enter a repository name to continue; + $[41] = showEmptyError; + $[42] = t18; + } else { + t18 = $[42]; + } + const t19 = currentRepo ? "\u2191/\u2193 to select \xB7 " : ""; + let t20; + if ($[43] !== t19) { + t20 = {t19}Enter to continue; + $[43] = t19; + $[44] = t20; + } else { + t20 = $[44]; + } + let t21; + if ($[45] !== t17 || $[46] !== t18 || $[47] !== t20) { + t21 = <>{t17}{t18}{t20}; + $[45] = t17; + $[46] = t18; + $[47] = t20; + $[48] = t21; + } else { + t21 = $[48]; + } + return t21; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlU3RhdGUiLCJUZXh0SW5wdXQiLCJ1c2VUZXJtaW5hbFNpemUiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZ3MiLCJDaG9vc2VSZXBvU3RlcFByb3BzIiwiY3VycmVudFJlcG8iLCJ1c2VDdXJyZW50UmVwbyIsInJlcG9VcmwiLCJvblJlcG9VcmxDaGFuZ2UiLCJ2YWx1ZSIsIm9uVG9nZ2xlVXNlQ3VycmVudFJlcG8iLCJvblN1Ym1pdCIsIkNob29zZVJlcG9TdGVwIiwidDAiLCIkIiwiX2MiLCJjdXJzb3JPZmZzZXQiLCJzZXRDdXJzb3JPZmZzZXQiLCJzaG93RW1wdHlFcnJvciIsInNldFNob3dFbXB0eUVycm9yIiwidGVybWluYWxTaXplIiwidGV4dElucHV0Q29sdW1ucyIsImNvbHVtbnMiLCJ0MSIsInJlcG9OYW1lIiwidHJpbSIsImhhbmRsZVN1Ym1pdCIsImlzVGV4dElucHV0VmlzaWJsZSIsInQyIiwiaGFuZGxlUHJldmlvdXMiLCJ0MyIsImhhbmRsZU5leHQiLCJ0NCIsInQ1IiwidDYiLCJjb250ZXh0IiwiaXNBY3RpdmUiLCJ0NyIsInQ4IiwidDkiLCJTeW1ib2wiLCJmb3IiLCJ0MTAiLCJ1bmRlZmluZWQiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJ0MTYiLCJ0MTciLCJ0MTgiLCJ0MTkiLCJ0MjAiLCJ0MjEiXSwic291cmNlcyI6WyJDaG9vc2VSZXBvU3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHVzZUNhbGxiYWNrLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IFRleHRJbnB1dCBmcm9tICcuLi8uLi9jb21wb25lbnRzL1RleHRJbnB1dC5qcydcbmltcG9ydCB7IHVzZVRlcm1pbmFsU2l6ZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZVRlcm1pbmFsU2l6ZS5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmdzIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcblxuaW50ZXJmYWNlIENob29zZVJlcG9TdGVwUHJvcHMge1xuICBjdXJyZW50UmVwbzogc3RyaW5nIHwgbnVsbFxuICB1c2VDdXJyZW50UmVwbzogYm9vbGVhblxuICByZXBvVXJsOiBzdHJpbmdcbiAgb25SZXBvVXJsQ2hhbmdlOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZFxuICBvblRvZ2dsZVVzZUN1cnJlbnRSZXBvOiAodXNlQ3VycmVudFJlcG86IGJvb2xlYW4pID0+IHZvaWRcbiAgb25TdWJtaXQ6ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIENob29zZVJlcG9TdGVwKHtcbiAgY3VycmVudFJlcG8sXG4gIHVzZUN1cnJlbnRSZXBvLFxuICByZXBvVXJsLFxuICBvblJlcG9VcmxDaGFuZ2UsXG4gIG9uU3VibWl0LFxuICBvblRvZ2dsZVVzZUN1cnJlbnRSZXBvLFxufTogQ2hvb3NlUmVwb1N0ZXBQcm9wcykge1xuICBjb25zdCBbY3Vyc29yT2Zmc2V0LCBzZXRDdXJzb3JPZmZzZXRdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgW3Nob3dFbXB0eUVycm9yLCBzZXRTaG93RW1wdHlFcnJvcl0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgdGVybWluYWxTaXplID0gdXNlVGVybWluYWxTaXplKClcbiAgY29uc3QgdGV4dElucHV0Q29sdW1ucyA9IHRlcm1pbmFsU2l6ZS5jb2x1bW5zXG5cbiAgY29uc3QgaGFuZGxlU3VibWl0ID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGNvbnN0IHJlcG9OYW1lID0gdXNlQ3VycmVudFJlcG8gPyBjdXJyZW50UmVwbyA6IHJlcG9VcmxcbiAgICBpZiAoIXJlcG9OYW1lPy50cmltKCkpIHtcbiAgICAgIHNldFNob3dFbXB0eUVycm9yKHRydWUpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgb25TdWJtaXQoKVxuICB9LCBbdXNlQ3VycmVudFJlcG8sIGN1cnJlbnRSZXBvLCByZXBvVXJsLCBvblN1Ym1pdF0pXG5cbiAgLy8gV2hlbiB0aGUgdGV4dCBpbnB1dCBpcyB2aXNpYmxlLCBvbWl0IGNvbmZpcm06eWVzIHNvIGJhcmUgJ3knIHBhc3Nlc1xuICAvLyB0aHJvdWdoIHRvIHRoZSBpbnB1dCBpbnN0ZWFkIG9mIHN1Ym1pdHRpbmcuIFRleHRJbnB1dCdzIG9uU3VibWl0IGhhbmRsZXNcbiAgLy8gRW50ZXIuIEtlZXAgdGhlIENvbmZpcm1hdGlvbiBjb250ZXh0IChub3QgU2V0dGluZ3MpIHRvIGF2b2lkIGovayBiaW5kaW5ncy5cbiAgY29uc3QgaXNUZXh0SW5wdXRWaXNpYmxlID0gIXVzZUN1cnJlbnRSZXBvIHx8ICFjdXJyZW50UmVwb1xuICBjb25zdCBoYW5kbGVQcmV2aW91cyA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBvblRvZ2dsZVVzZUN1cnJlbnRSZXBvKHRydWUpXG4gICAgc2V0U2hvd0VtcHR5RXJyb3IoZmFsc2UpXG4gIH0sIFtvblRvZ2dsZVVzZUN1cnJlbnRSZXBvXSlcbiAgY29uc3QgaGFuZGxlTmV4dCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBvblRvZ2dsZVVzZUN1cnJlbnRSZXBvKGZhbHNlKVxuICAgIHNldFNob3dFbXB0eUVycm9yKGZhbHNlKVxuICB9LCBbb25Ub2dnbGVVc2VDdXJyZW50UmVwb10pXG5cbiAgdXNlS2V5YmluZGluZ3MoXG4gICAge1xuICAgICAgJ2NvbmZpcm06cHJldmlvdXMnOiBoYW5kbGVQcmV2aW91cyxcbiAgICAgICdjb25maXJtOm5leHQnOiBoYW5kbGVOZXh0LFxuICAgICAgJ2NvbmZpcm06eWVzJzogaGFuZGxlU3VibWl0LFxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJywgaXNBY3RpdmU6ICFpc1RleHRJbnB1dFZpc2libGUgfSxcbiAgKVxuICB1c2VLZXliaW5kaW5ncyhcbiAgICB7XG4gICAgICAnY29uZmlybTpwcmV2aW91cyc6IGhhbmRsZVByZXZpb3VzLFxuICAgICAgJ2NvbmZpcm06bmV4dCc6IGhhbmRsZU5leHQsXG4gICAgfSxcbiAgICB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nLCBpc0FjdGl2ZTogaXNUZXh0SW5wdXRWaXNpYmxlIH0sXG4gIClcblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBib3JkZXJTdHlsZT1cInJvdW5kXCIgcGFkZGluZ1g9ezF9PlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgIDxUZXh0IGJvbGQ+SW5zdGFsbCBHaXRIdWIgQXBwPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlNlbGVjdCBHaXRIdWIgcmVwb3NpdG9yeTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHtjdXJyZW50UmVwbyAmJiAoXG4gICAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgYm9sZD17dXNlQ3VycmVudFJlcG99XG4gICAgICAgICAgICAgIGNvbG9yPXt1c2VDdXJyZW50UmVwbyA/ICdwZXJtaXNzaW9uJyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAge3VzZUN1cnJlbnRSZXBvID8gJz4gJyA6ICcgICd9XG4gICAgICAgICAgICAgIFVzZSBjdXJyZW50IHJlcG9zaXRvcnk6IHtjdXJyZW50UmVwb31cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICBib2xkPXshdXNlQ3VycmVudFJlcG8gfHwgIWN1cnJlbnRSZXBvfVxuICAgICAgICAgICAgY29sb3I9eyF1c2VDdXJyZW50UmVwbyB8fCAhY3VycmVudFJlcG8gPyAncGVybWlzc2lvbicgOiB1bmRlZmluZWR9XG4gICAgICAgICAgPlxuICAgICAgICAgICAgeyF1c2VDdXJyZW50UmVwbyB8fCAhY3VycmVudFJlcG8gPyAnPiAnIDogJyAgJ31cbiAgICAgICAgICAgIHtjdXJyZW50UmVwbyA/ICdFbnRlciBhIGRpZmZlcmVudCByZXBvc2l0b3J5JyA6ICdFbnRlciByZXBvc2l0b3J5J31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7KCF1c2VDdXJyZW50UmVwbyB8fCAhY3VycmVudFJlcG8pICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpbkxlZnQ9ezJ9IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgICA8VGV4dElucHV0XG4gICAgICAgICAgICAgIHZhbHVlPXtyZXBvVXJsfVxuICAgICAgICAgICAgICBvbkNoYW5nZT17dmFsdWUgPT4ge1xuICAgICAgICAgICAgICAgIG9uUmVwb1VybENoYW5nZSh2YWx1ZSlcbiAgICAgICAgICAgICAgICBzZXRTaG93RW1wdHlFcnJvcihmYWxzZSlcbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgICAgb25TdWJtaXQ9e2hhbmRsZVN1Ym1pdH1cbiAgICAgICAgICAgICAgZm9jdXM9e3RydWV9XG4gICAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiRW50ZXIgYSByZXBvIGFzIG93bmVyL3JlcG8gb3IgaHR0cHM6Ly9naXRodWIuY29tL293bmVyL3JlcG/igKZcIlxuICAgICAgICAgICAgICBjb2x1bW5zPXt0ZXh0SW5wdXRDb2x1bW5zfVxuICAgICAgICAgICAgICBjdXJzb3JPZmZzZXQ9e2N1cnNvck9mZnNldH1cbiAgICAgICAgICAgICAgb25DaGFuZ2VDdXJzb3JPZmZzZXQ9e3NldEN1cnNvck9mZnNldH1cbiAgICAgICAgICAgICAgc2hvd0N1cnNvcj17dHJ1ZX1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICA8L0JveD5cbiAgICAgIHtzaG93RW1wdHlFcnJvciAmJiAoXG4gICAgICAgIDxCb3ggbWFyZ2luTGVmdD17M30gbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+UGxlYXNlIGVudGVyIGEgcmVwb3NpdG9yeSBuYW1lIHRvIGNvbnRpbnVlPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgICA8Qm94IG1hcmdpbkxlZnQ9ezN9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICB7Y3VycmVudFJlcG8gPyAn4oaRL+KGkyB0byBzZWxlY3QgwrcgJyA6ICcnfUVudGVyIHRvIGNvbnRpbnVlXG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUlDLFdBQVcsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDcEQsT0FBT0MsU0FBUyxNQUFNLCtCQUErQjtBQUNyRCxTQUFTQyxlQUFlLFFBQVEsZ0NBQWdDO0FBQ2hFLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsU0FBU0MsY0FBYyxRQUFRLG9DQUFvQztBQUVuRSxVQUFVQyxtQkFBbUIsQ0FBQztFQUM1QkMsV0FBVyxFQUFFLE1BQU0sR0FBRyxJQUFJO0VBQzFCQyxjQUFjLEVBQUUsT0FBTztFQUN2QkMsT0FBTyxFQUFFLE1BQU07RUFDZkMsZUFBZSxFQUFFLENBQUNDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQ3hDQyxzQkFBc0IsRUFBRSxDQUFDSixjQUFjLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSTtFQUN6REssUUFBUSxFQUFFLEdBQUcsR0FBRyxJQUFJO0FBQ3RCO0FBRUEsT0FBTyxTQUFBQyxlQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXdCO0lBQUFWLFdBQUE7SUFBQUMsY0FBQTtJQUFBQyxPQUFBO0lBQUFD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 \ No newline at end of file diff --git a/commands/install-github-app/CreatingStep.tsx b/commands/install-github-app/CreatingStep.tsx new file mode 100644 index 0000000..ef59787 --- /dev/null +++ b/commands/install-github-app/CreatingStep.tsx @@ -0,0 +1,65 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, Text } from '../../ink.js'; +import type { Workflow } from './types.js'; +interface CreatingStepProps { + currentWorkflowInstallStep: number; + secretExists: boolean; + useExistingSecret: boolean; + secretName: string; + skipWorkflow?: boolean; + selectedWorkflows: Workflow[]; +} +export function CreatingStep(t0) { + const $ = _c(10); + const { + currentWorkflowInstallStep, + secretExists, + useExistingSecret, + secretName, + skipWorkflow: t1, + selectedWorkflows + } = t0; + const skipWorkflow = t1 === undefined ? false : t1; + let t2; + if ($[0] !== secretExists || $[1] !== secretName || $[2] !== selectedWorkflows || $[3] !== skipWorkflow || $[4] !== useExistingSecret) { + t2 = skipWorkflow ? ["Getting repository information", secretExists && useExistingSecret ? "Using existing API key secret" : `Setting up ${secretName} secret`] : ["Getting repository information", "Creating branch", selectedWorkflows.length > 1 ? "Creating workflow files" : "Creating workflow file", secretExists && useExistingSecret ? "Using existing API key secret" : `Setting up ${secretName} secret`, "Opening pull request page"]; + $[0] = secretExists; + $[1] = secretName; + $[2] = selectedWorkflows; + $[3] = skipWorkflow; + $[4] = useExistingSecret; + $[5] = t2; + } else { + t2 = $[5]; + } + const progressSteps = t2; + let t3; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t3 = Install GitHub AppCreate GitHub Actions workflow; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] !== currentWorkflowInstallStep || $[8] !== progressSteps) { + t4 = <>{t3}{progressSteps.map((stepText, index) => { + let status = "pending"; + if (index < currentWorkflowInstallStep) { + status = "completed"; + } else { + if (index === currentWorkflowInstallStep) { + status = "in-progress"; + } + } + return {status === "completed" ? "\u2713 " : ""}{stepText}{status === "in-progress" ? "\u2026" : ""}; + })}; + $[7] = currentWorkflowInstallStep; + $[8] = progressSteps; + $[9] = t4; + } else { + t4 = $[9]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJXb3JrZmxvdyIsIkNyZWF0aW5nU3RlcFByb3BzIiwiY3VycmVudFdvcmtmbG93SW5zdGFsbFN0ZXAiLCJzZWNyZXRFeGlzdHMiLCJ1c2VFeGlzdGluZ1NlY3JldCIsInNlY3JldE5hbWUiLCJza2lwV29ya2Zsb3ciLCJzZWxlY3RlZFdvcmtmbG93cyIsIkNyZWF0aW5nU3RlcCIsInQwIiwiJCIsIl9jIiwidDEiLCJ1bmRlZmluZWQiLCJ0MiIsImxlbmd0aCIsInByb2dyZXNzU3RlcHMiLCJ0MyIsIlN5bWJvbCIsImZvciIsInQ0IiwibWFwIiwic3RlcFRleHQiLCJpbmRleCIsInN0YXR1cyJdLCJzb3VyY2VzIjpbIkNyZWF0aW5nU3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBXb3JrZmxvdyB9IGZyb20gJy4vdHlwZXMuanMnXG5cbmludGVyZmFjZSBDcmVhdGluZ1N0ZXBQcm9wcyB7XG4gIGN1cnJlbnRXb3JrZmxvd0luc3RhbGxTdGVwOiBudW1iZXJcbiAgc2VjcmV0RXhpc3RzOiBib29sZWFuXG4gIHVzZUV4aXN0aW5nU2VjcmV0OiBib29sZWFuXG4gIHNlY3JldE5hbWU6IHN0cmluZ1xuICBza2lwV29ya2Zsb3c/OiBib29sZWFuXG4gIHNlbGVjdGVkV29ya2Zsb3dzOiBXb3JrZmxvd1tdXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBDcmVhdGluZ1N0ZXAoe1xuICBjdXJyZW50V29ya2Zsb3dJbnN0YWxsU3RlcCxcbiAgc2VjcmV0RXhpc3RzLFxuICB1c2VFeGlzdGluZ1NlY3JldCxcbiAgc2VjcmV0TmFtZSxcbiAgc2tpcFdvcmtmbG93ID0gZmFsc2UsXG4gIHNlbGVjdGVkV29ya2Zsb3dzLFxufTogQ3JlYXRpbmdTdGVwUHJvcHMpIHtcbiAgY29uc3QgcHJvZ3Jlc3NTdGVwcyA9IHNraXBXb3JrZmxvd1xuICAgID8gW1xuICAgICAgICAnR2V0dGluZyByZXBvc2l0b3J5IGluZm9ybWF0aW9uJyxcbiAgICAgICAgc2VjcmV0RXhpc3RzICYmIHVzZUV4aXN0aW5nU2VjcmV0XG4gICAgICAgICAgPyAnVXNpbmcgZXhpc3RpbmcgQVBJIGtleSBzZWNyZXQnXG4gICAgICAgICAgOiBgU2V0dGluZyB1cCAke3NlY3JldE5hbWV9IHNlY3JldGAsXG4gICAgICBdXG4gICAgOiBbXG4gICAgICAgICdHZXR0aW5nIHJlcG9zaXRvcnkgaW5mb3JtYXRpb24nLFxuICAgICAgICAnQ3JlYXRpbmcgYnJhbmNoJyxcbiAgICAgICAgc2VsZWN0ZWRXb3JrZmxvd3MubGVuZ3RoID4gMVxuICAgICAgICAgID8gJ0NyZWF0aW5nIHdvcmtmbG93IGZpbGVzJ1xuICAgICAgICAgIDogJ0NyZWF0aW5nIHdvcmtmbG93IGZpbGUnLFxuICAgICAgICBzZWNyZXRFeGlzdHMgJiYgdXNlRXhpc3RpbmdTZWNyZXRcbiAgICAgICAgICA/ICdVc2luZyBleGlzdGluZyBBUEkga2V5IHNlY3JldCdcbiAgICAgICAgICA6IGBTZXR0aW5nIHVwICR7c2VjcmV0TmFtZX0gc2VjcmV0YCxcbiAgICAgICAgJ09wZW5pbmcgcHVsbCByZXF1ZXN0IHBhZ2UnLFxuICAgICAgXVxuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGJvcmRlclN0eWxlPVwicm91bmRcIiBwYWRkaW5nWD17MX0+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgPFRleHQgYm9sZD5JbnN0YWxsIEdpdEh1YiBBcHA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+Q3JlYXRlIEdpdEh1YiBBY3Rpb25zIHdvcmtmbG93PC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAge3Byb2dyZXNzU3RlcHMubWFwKChzdGVwVGV4dCwgaW5kZXgpID0+IHtcbiAgICAgICAgICBsZXQgc3RhdHVzOiAnY29tcGxldGVkJyB8ICdpbi1wcm9ncmVzcycgfCAncGVuZGluZycgPSAncGVuZGluZydcblxuICAgICAgICAgIGlmIChpbmRleCA8IGN1cnJlbnRXb3JrZmxvd0luc3RhbGxTdGVwKSB7XG4gICAgICAgICAgICBzdGF0dXMgPSAnY29tcGxldGVkJ1xuICAgICAgICAgIH0gZWxzZSBpZiAoaW5kZXggPT09IGN1cnJlbnRXb3JrZmxvd0luc3RhbGxTdGVwKSB7XG4gICAgICAgICAgICBzdGF0dXMgPSAnaW4tcHJvZ3Jlc3MnXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIDxCb3gga2V5PXtpbmRleH0+XG4gICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgY29sb3I9e1xuICAgICAgICAgICAgICAgICAgc3RhdHVzID09PSAnY29tcGxldGVkJ1xuICAgICAgICAgICAgICAgICAgICA/ICdzdWNjZXNzJ1xuICAgICAgICAgICAgICAgICAgICA6IHN0YXR1cyA9PT0gJ2luLXByb2dyZXNzJ1xuICAgICAgICAgICAgICAgICAgICAgID8gJ3dhcm5pbmcnXG4gICAgICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICB7c3RhdHVzID09PSAnY29tcGxldGVkJyA/ICfinJMgJyA6ICcnfVxuICAgICAgICAgICAgICAgIHtzdGVwVGV4dH1cbiAgICAgICAgICAgICAgICB7c3RhdHVzID09PSAnaW4tcHJvZ3Jlc3MnID8gJ+KApicgOiAnJ31cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKVxuICAgICAgICB9KX1cbiAgICAgIDwvQm94PlxuICAgIDwvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFP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 \ No newline at end of file diff --git a/commands/install-github-app/ErrorStep.tsx b/commands/install-github-app/ErrorStep.tsx new file mode 100644 index 0000000..6fad7af --- /dev/null +++ b/commands/install-github-app/ErrorStep.tsx @@ -0,0 +1,85 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js'; +import { Box, Text } from '../../ink.js'; +interface ErrorStepProps { + error: string | undefined; + errorReason?: string; + errorInstructions?: string[]; +} +export function ErrorStep(t0) { + const $ = _c(15); + const { + error, + errorReason, + errorInstructions + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Install GitHub App; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== error) { + t2 = Error: {error}; + $[1] = error; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] !== errorReason) { + t3 = errorReason && Reason: {errorReason}; + $[3] = errorReason; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== errorInstructions) { + t4 = errorInstructions && errorInstructions.length > 0 && How to fix:{errorInstructions.map(_temp)}; + $[5] = errorInstructions; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t5 = For manual setup instructions, see:{" "}{GITHUB_ACTION_SETUP_DOCS_URL}; + $[7] = t5; + } else { + t5 = $[7]; + } + let t6; + if ($[8] !== t2 || $[9] !== t3 || $[10] !== t4) { + t6 = {t1}{t2}{t3}{t4}{t5}; + $[8] = t2; + $[9] = t3; + $[10] = t4; + $[11] = t6; + } else { + t6 = $[11]; + } + let t7; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t7 = Press any key to exit; + $[12] = t7; + } else { + t7 = $[12]; + } + let t8; + if ($[13] !== t6) { + t8 = <>{t6}{t7}; + $[13] = t6; + $[14] = t8; + } else { + t8 = $[14]; + } + return t8; +} +function _temp(instruction, index) { + return {instruction}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkdJVEhVQl9BQ1RJT05fU0VUVVBfRE9DU19VUkwiLCJCb3giLCJUZXh0IiwiRXJyb3JTdGVwUHJvcHMiLCJlcnJvciIsImVycm9yUmVhc29uIiwiZXJyb3JJbnN0cnVjdGlvbnMiLCJFcnJvclN0ZXAiLCJ0MCIsIiQiLCJfYyIsInQxIiwiU3ltYm9sIiwiZm9yIiwidDIiLCJ0MyIsInQ0IiwibGVuZ3RoIiwibWFwIiwiX3RlbXAiLCJ0NSIsInQ2IiwidDciLCJ0OCIsImluc3RydWN0aW9uIiwiaW5kZXgiXSwic291cmNlcyI6WyJFcnJvclN0ZXAudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEdJVEhVQl9BQ1RJT05fU0VUVVBfRE9DU19VUkwgfSBmcm9tICcuLi8uLi9jb25zdGFudHMvZ2l0aHViLWFwcC5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcblxuaW50ZXJmYWNlIEVycm9yU3RlcFByb3BzIHtcbiAgZXJyb3I6IHN0cmluZyB8IHVuZGVmaW5lZFxuICBlcnJvclJlYXNvbj86IHN0cmluZ1xuICBlcnJvckluc3RydWN0aW9ucz86IHN0cmluZ1tdXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBFcnJvclN0ZXAoe1xuICBlcnJvcixcbiAgZXJyb3JSZWFzb24sXG4gIGVycm9ySW5zdHJ1Y3Rpb25zLFxufTogRXJyb3JTdGVwUHJvcHMpIHtcbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgYm9yZGVyU3R5bGU9XCJyb3VuZFwiIHBhZGRpbmdYPXsxfT5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICA8VGV4dCBib2xkPkluc3RhbGwgR2l0SHViIEFwcDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5FcnJvcjoge2Vycm9yfTwvVGV4dD5cbiAgICAgICAge2Vycm9yUmVhc29uICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5SZWFzb246IHtlcnJvclJlYXNvbn08L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICAgIHtlcnJvckluc3RydWN0aW9ucyAmJiBlcnJvckluc3RydWN0aW9ucy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+SG93IHRvIGZpeDo8L1RleHQ+XG4gICAgICAgICAgICB7ZXJyb3JJbnN0cnVjdGlvbnMubWFwKChpbnN0cnVjdGlvbiwgaW5kZXgpID0+IChcbiAgICAgICAgICAgICAgPEJveCBrZXk9e2luZGV4fSBtYXJnaW5MZWZ0PXsyfT5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj7igKIgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0PntpbnN0cnVjdGlvbn08L1RleHQ+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKSl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIEZvciBtYW51YWwgc2V0dXAgaW5zdHJ1Y3Rpb25zLCBzZWU6eycgJ31cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+e0dJVEhVQl9BQ1RJT05fU0VUVVBfRE9DU19VUkx9PC9UZXh0PlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cbiAgICAgIDxCb3ggbWFyZ2luTGVmdD17M30+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlByZXNzIGFueSBrZXkgdG8gZXhpdDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyw0QkFBNEIsUUFBUSwrQkFBK0I7QUFDNUUsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUV4QyxVQUFVQyxjQUFjLENBQUM7RUFDdkJDLEtBQUssRUFBRSxNQUFNLEdBQUcsU0FBUztFQUN6QkMsV0FBVyxDQUFDLEVBQUUsTUFBTTtFQUNwQkMsaUJBQWlCLENBQUMsRUFBRSxNQUFNLEVBQUU7QUFDOUI7QUFFQSxPQUFPLFNBQUFDLFVBQUFDLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBbUI7SUFBQU4sS0FBQTtJQUFBQyxXQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFJVDtFQUFBLElBQUFHLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFHLE1BQUEsQ0FBQUMsR0FBQTtJQUlURixFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQWUsWUFBQyxDQUFELEdBQUMsQ0FDekMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFKLEtBQUcsQ0FBQyxDQUFDLGtCQUFrQixFQUE1QixJQUFJLENBQ1AsRUFGQyxHQUFHLENBRUU7SUFBQUYsQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFBQSxJQUFBSyxFQUFBO0VBQUEsSUFBQUwsQ0FBQSxRQUFBTCxLQUFBO0lBQ05VLEVBQUEsSUFBQyxJQUFJLENBQU8sS0FBTyxDQUFQLE9BQU8sQ0FBQyxPQUFRVixNQUFJLENBQUUsRUFBakMsSUFBSSxDQUFvQztJQUFBSyxDQUFBLE1BQUFMLEtBQUE7SUFBQUssQ0FBQSxNQUFBSyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBTCxDQUFBO0VBQUE7RUFBQSxJQUFBTSxFQUFBO0VBQUEsSUFBQU4sQ0FBQSxRQUFBSixXQUFBO0lBQ3hDVSxFQUFBLEdBQUFWLFdBSUEsSUFIQyxDQUFDLEdBQUcsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUNmLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxRQUFTQSxZQUFVLENBQUUsRUFBbkMsSUFBSSxDQUNQLEVBRkMsR0FBRyxDQUdMO0lBQUFJLENBQUEsTUFBQUosV0FBQTtJQUFBSSxDQUFBLE1BQUFNLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFOLENBQUE7RUFBQTtFQUFBLElBQUFPLEVBQUE7RUFBQSxJQUFBUCxDQUFBLFFBQUFILGlCQUFBO0lBQ0FVLEVBQUEsR0FBQVYsaUJBQWlELElBQTVCQSxpQkFBaUIsQ0FBQVcsTUFBTyxHQUFHLENBVWhELElBVEMsQ0FBQyxHQUFHLENBQWUsYUFBUSxDQUFSLFFBQVEsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUN0QyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsV0FBVyxFQUF6QixJQUFJLENBQ0osQ0FBQVgsaUJBQWlCLENBQUFZLEdBQUksQ0FBQ0MsS0FLdEIsRUFDSCxFQVJDLEdBQUcsQ0FTTDtJQUFBVixDQUFBLE1BQUFILGlCQUFBO0lBQUFHLENBQUEsTUFBQU8sRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVAsQ0FBQTtFQUFBO0VBQUEsSUFBQVcsRUFBQTtFQUFBLElBQUFYLENBQUEsUUFBQUcsTUFBQSxDQUFBQyxHQUFBO0lBQ0RPLEVBQUEsSUFBQyxHQUFHLENBQVksU0FBQyxDQUFELEdBQUMsQ0FDZixDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsbUNBQ3VCLElBQUUsQ0FDdEMsQ0FBQyxJQUFJLENBQU8sS0FBUSxDQUFSLFFBQVEsQ0FBRXBCLDZCQUEyQixDQUFFLEVBQWxELElBQUksQ0FDUCxFQUhDLElBQUksQ0FJUCxFQUxDLEdBQUcsQ0FLRTtJQUFBUyxDQUFBLE1BQUFXLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFYLENBQUE7RUFBQTtFQUFBLElBQUFZLEVBQUE7RUFBQSxJQUFBWixDQUFBLFFBQUFLLEVBQUEsSUFBQUwsQ0FBQSxRQUFBTSxFQUFBLElBQUFOLENBQUEsU0FBQU8sRUFBQTtJQTFCUkssRUFBQSxJQUFDLEdBQUcsQ0FBZSxhQUFRLENBQVIsUUFBUSxDQUFhLFdBQU8sQ0FBUCxPQUFPLENBQVcsUUFBQyxDQUFELEdBQUMsQ0FDekQsQ0FBQVYsRUFFSyxDQUNMLENBQUFHLEVBQXdDLENBQ3ZDLENBQUFDLEVBSUQsQ0FDQyxDQUFBQyxFQVVELENBQ0EsQ0FBQUksRUFLSyxDQUNQLEVBM0JDLEdBQUcsQ0EyQkU7SUFBQVgsQ0FBQSxNQUFBSyxFQUFBO0lBQUFMLENBQUEsTUFBQU0sRUFBQTtJQUFBTixDQUFBLE9BQUFPLEVBQUE7SUFBQVAsQ0FBQSxPQUFBWSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBWixDQUFBO0VBQUE7RUFBQSxJQUFBYSxFQUFBO0VBQUEsSUFBQWIsQ0FBQSxTQUFBRyxNQUFBLENBQUFDLEdBQUE7SUFDTlMsRUFBQSxJQUFDLEdBQUcsQ0FBYSxVQUFDLENBQUQsR0FBQyxDQUNoQixDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMscUJBQXFCLEVBQW5DLElBQUksQ0FDUCxFQUZDLEdBQUcsQ0FFRTtJQUFBYixDQUFBLE9BQUFhLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFiLENBQUE7RUFBQTtFQUFBLElBQUFjLEVBQUE7RUFBQSxJQUFBZCxDQUFBLFNBQUFZLEVBQUE7SUEvQlJFLEVBQUEsS0FDRSxDQUFBRixFQTJCSyxDQUNMLENBQUFDLEVBRUssQ0FBQyxHQUNMO0lBQUFiLENBQUEsT0FBQVksRUFBQTtJQUFBWixDQUFBLE9BQUFjLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFkLENBQUE7RUFBQTtFQUFBLE9BaENIYyxFQWdDRztBQUFBO0FBdENBLFNBQUFKLE1BQUFLLFdBQUEsRUFBQUMsS0FBQTtFQUFBLE9BcUJPLENBQUMsR0FBRyxDQUFNQSxHQUFLLENBQUxBLE1BQUksQ0FBQyxDQUFjLFVBQUMsQ0FBRCxHQUFDLENBQzVCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxFQUFFLEVBQWhCLElBQUksQ0FDTCxDQUFDLElBQUksQ0FBRUQsWUFBVSxDQUFFLEVBQWxCLElBQUksQ0FDUCxFQUhDLEdBQUcsQ0FHRTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/install-github-app/ExistingWorkflowStep.tsx b/commands/install-github-app/ExistingWorkflowStep.tsx new file mode 100644 index 0000000..3efff6f --- /dev/null +++ b/commands/install-github-app/ExistingWorkflowStep.tsx @@ -0,0 +1,103 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Select } from 'src/components/CustomSelect/index.js'; +import { Box, Text } from '../../ink.js'; +interface ExistingWorkflowStepProps { + repoName: string; + onSelectAction: (action: 'update' | 'skip' | 'exit') => void; +} +export function ExistingWorkflowStep(t0) { + const $ = _c(16); + const { + repoName, + onSelectAction + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = [{ + label: "Update workflow file with latest version", + value: "update" + }, { + label: "Skip workflow update (configure secrets only)", + value: "skip" + }, { + label: "Exit without making changes", + value: "exit" + }]; + $[0] = t1; + } else { + t1 = $[0]; + } + const options = t1; + let t2; + if ($[1] !== onSelectAction) { + t2 = value => { + onSelectAction(value as 'update' | 'skip' | 'exit'); + }; + $[1] = onSelectAction; + $[2] = t2; + } else { + t2 = $[2]; + } + const handleSelect = t2; + let t3; + if ($[3] !== onSelectAction) { + t3 = () => { + onSelectAction("exit"); + }; + $[3] = onSelectAction; + $[4] = t3; + } else { + t3 = $[4]; + } + const handleCancel = t3; + let t4; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t4 = Existing Workflow Found; + $[5] = t4; + } else { + t4 = $[5]; + } + let t5; + if ($[6] !== repoName) { + t5 = {t4}Repository: {repoName}; + $[6] = repoName; + $[7] = t5; + } else { + t5 = $[7]; + } + let t6; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t6 = A Claude workflow file already exists at{" "}.github/workflows/claude.ymlWhat would you like to do?; + $[8] = t6; + } else { + t6 = $[8]; + } + let t7; + if ($[9] !== handleCancel || $[10] !== handleSelect) { + t7 = ; + $[19] = handleSelect; + $[20] = options; + $[21] = t6; + } else { + t6 = $[21]; + } + let t7; + if ($[22] !== handleCancel || $[23] !== t6) { + t7 = {t6}; + $[22] = handleCancel; + $[23] = t6; + $[24] = t7; + } else { + t7 = $[24]; + } + return t7; +} +export async function call(onDone: LocalJSXCommandOnDone, context: ToolUseContext & LocalJSXCommandContext): Promise { + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZU1lbW8iLCJ1c2VTdGF0ZSIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiTG9jYWxKU1hDb21tYW5kQ29udGV4dCIsIk9wdGlvbldpdGhEZXNjcmlwdGlvbiIsIlNlbGVjdCIsIkRpYWxvZyIsImdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFIiwibG9nRXZlbnQiLCJ1c2VDbGF1ZGVBaUxpbWl0cyIsIlRvb2xVc2VDb250ZXh0IiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiZ2V0T2F1dGhBY2NvdW50SW5mbyIsImdldFJhdGVMaW1pdFRpZXIiLCJnZXRTdWJzY3JpcHRpb25UeXBlIiwiaGFzQ2xhdWRlQWlCaWxsaW5nQWNjZXNzIiwiY2FsbCIsImV4dHJhVXNhZ2VDYWxsIiwiZXh0cmFVc2FnZSIsInVwZ3JhZGUiLCJ1cGdyYWRlQ2FsbCIsIlJhdGVMaW1pdE9wdGlvbnNNZW51T3B0aW9uVHlwZSIsIlJhdGVMaW1pdE9wdGlvbnNNZW51UHJvcHMiLCJvbkRvbmUiLCJyZXN1bHQiLCJvcHRpb25zIiwiZGlzcGxheSIsImNvbnRleHQiLCJSYXRlTGltaXRPcHRpb25zTWVudSIsInQwIiwiJCIsIl9jIiwic3ViQ29tbWFuZEpTWCIsInNldFN1YkNvbW1hbmRKU1giLCJjbGF1ZGVBaUxpbWl0cyIsInQxIiwiU3ltYm9sIiwiZm9yIiwic3Vic2NyaXB0aW9uVHlwZSIsInQyIiwicmF0ZUxpbWl0VGllciIsImhhc0V4dHJhVXNhZ2VFbmFibGVkIiwiaXNNYXgiLCJpc01heDIweCIsImlzVGVhbU9yRW50ZXJwcmlzZSIsImJ1eUZpcnN0IiwidDMiLCJiYjAiLCJhY3Rpb25PcHRpb25zIiwib3ZlcmFnZURpc2FibGVkUmVhc29uIiwib3ZlcmFnZVN0YXR1cyIsImlzRW5hYmxlZCIsImhhc0JpbGxpbmdBY2Nlc3MiLCJuZWVkc1RvUmVxdWVzdEZyb21BZG1pbiIsImlzT3JnU3BlbmRDYXBEZXBsZXRlZCIsImlzT3ZlcmFnZVN0YXRlIiwibGFiZWwiLCJ0NCIsInZhbHVlIiwicHVzaCIsImNhbmNlbE9wdGlvbiIsInQ1IiwiaGFuZGxlQ2FuY2VsIiwidW5kZWZpbmVkIiwiaGFuZGxlU2VsZWN0IiwidGhlbiIsImpzeCIsImpzeF8wIiwidDYiLCJsZW5ndGgiLCJ0NyIsIlByb21pc2UiLCJSZWFjdE5vZGUiXSwic291cmNlcyI6WyJyYXRlLWxpbWl0LW9wdGlvbnMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VNZW1vLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUge1xuICBDb21tYW5kUmVzdWx0RGlzcGxheSxcbiAgTG9jYWxKU1hDb21tYW5kQ29udGV4dCxcbn0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQge1xuICB0eXBlIE9wdGlvbldpdGhEZXNjcmlwdGlvbixcbiAgU2VsZWN0LFxufSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0N1c3RvbVNlbGVjdC9zZWxlY3QuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hbmFseXRpY3MvZ3Jvd3RoYm9vay5qcydcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnLi4vLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgdXNlQ2xhdWRlQWlMaW1pdHMgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9jbGF1ZGVBaUxpbWl0c0hvb2suanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xVc2VDb250ZXh0IH0gZnJvbSAnLi4vLi4vVG9vbC5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kT25Eb25lIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcbmltcG9ydCB7XG4gIGdldE9hdXRoQWNjb3VudEluZm8sXG4gIGdldFJhdGVMaW1pdFRpZXIsXG4gIGdldFN1YnNjcmlwdGlvblR5cGUsXG59IGZyb20gJy4uLy4uL3V0aWxzL2F1dGguanMnXG5pbXBvcnQgeyBoYXNDbGF1ZGVBaUJpbGxpbmdBY2Nlc3MgfSBmcm9tICcuLi8uLi91dGlscy9iaWxsaW5nLmpzJ1xuaW1wb3J0IHsgY2FsbCBhcyBleHRyYVVzYWdlQ2FsbCB9IGZyb20gJy4uL2V4dHJhLXVzYWdlL2V4dHJhLXVzYWdlLmpzJ1xuaW1wb3J0IHsgZXh0cmFVc2FnZSB9IGZyb20gJy4uL2V4dHJhLXVzYWdlL2luZGV4LmpzJ1xuaW1wb3J0IHVwZ3JhZGUgZnJvbSAnLi4vdXBncmFkZS9pbmRleC5qcydcbmltcG9ydCB7IGNhbGwgYXMgdXBncmFkZUNhbGwgfSBmcm9tICcuLi91cGdyYWRlL3VwZ3JhZGUuanMnXG5cbnR5cGUgUmF0ZUxpbWl0T3B0aW9uc01lbnVPcHRpb25UeXBlID0gJ3VwZ3JhZGUnIHwgJ2V4dHJhLXVzYWdlJyB8ICdjYW5jZWwnXG5cbnR5cGUgUmF0ZUxpbWl0T3B0aW9uc01lbnVQcm9wcyA9IHtcbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OlxuICAgICAgfCB7XG4gICAgICAgICAgZGlzcGxheT86IENvbW1hbmRSZXN1bHREaXNwbGF5IHwgdW5kZWZpbmVkXG4gICAgICAgIH1cbiAgICAgIHwgdW5kZWZpbmVkLFxuICApID0+IHZvaWRcbiAgY29udGV4dDogVG9vbFVzZUNvbnRleHQgJiBMb2NhbEpTWENvbW1hbmRDb250ZXh0XG59XG5cbmZ1bmN0aW9uIFJhdGVMaW1pdE9wdGlvbnNNZW51KHtcbiAgb25Eb25lLFxuICBjb250ZXh0LFxufTogUmF0ZUxpbWl0T3B0aW9uc01lbnVQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtzdWJDb21tYW5kSlNYLCBzZXRTdWJDb21tYW5kSlNYXSA9IHVzZVN0YXRlPFJlYWN0LlJlYWN0Tm9kZT4obnVsbClcbiAgY29uc3QgY2xhdWRlQWlMaW1pdHMgPSB1c2VDbGF1ZGVBaUxpbWl0cygpXG4gIGNvbnN0IHN1YnNjcmlwdGlvblR5cGUgPSBnZXRTdWJzY3JpcHRpb25UeXBlKClcbiAgY29uc3QgcmF0ZUxpbWl0VGllciA9IGdldFJhdGVMaW1pdFRpZXIoKVxuICBjb25zdCBoYXNFeHRyYVVzYWdlRW5hYmxlZCA9XG4gICAgZ2V0T2F1dGhBY2NvdW50SW5mbygpPy5oYXNFeHRyYVVzYWdlRW5hYmxlZCA9PT0gdHJ1ZVxuICBjb25zdCBpc01heCA9IHN1YnNjcmlwdGlvblR5cGUgPT09ICdtYXgnXG4gIGNvbnN0IGlzTWF4MjB4ID0gaXNNYXggJiYgcmF0ZUxpbWl0VGllciA9PT0gJ2RlZmF1bHRfY2xhdWRlX21heF8yMHgnXG4gIGNvbnN0IGlzVGVhbU9yRW50ZXJwcmlzZSA9XG4gICAgc3Vic2NyaXB0aW9uVHlwZSA9PT0gJ3RlYW0nIHx8IHN1YnNjcmlwdGlvblR5cGUgPT09ICdlbnRlcnByaXNlJ1xuICBjb25zdCBidXlGaXJzdCA9IGdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFKFxuICAgICd0ZW5ndV9qYWRlX2FudmlsXzQnLFxuICAgIGZhbHNlLFxuICApXG5cbiAgY29uc3Qgb3B0aW9ucyA9IHVzZU1lbW88XG4gICAgT3B0aW9uV2l0aERlc2NyaXB0aW9uPFJhdGVMaW1pdE9wdGlvbnNNZW51T3B0aW9uVHlwZT5bXVxuICA+KCgpID0+IHtcbiAgICBjb25zdCBhY3Rpb25PcHRpb25zOiBPcHRpb25XaXRoRGVzY3JpcHRpb248UmF0ZUxpbWl0T3B0aW9uc01lbnVPcHRpb25UeXBlPltdID1cbiAgICAgIFtdXG5cbiAgICBpZiAoZXh0cmFVc2FnZS5pc0VuYWJsZWQoKSkge1xuICAgICAgY29uc3QgaGFzQmlsbGluZ0FjY2VzcyA9IGhhc0NsYXVkZUFpQmlsbGluZ0FjY2VzcygpXG4gICAgICBjb25zdCBuZWVkc1RvUmVxdWVzdEZyb21BZG1pbiA9IGlzVGVhbU9yRW50ZXJwcmlzZSAmJiAhaGFzQmlsbGluZ0FjY2Vzc1xuICAgICAgLy8gT3JnIHNwZW5kIGNhcCBkZXBsZXRlZCAtIG5vbi1hZG1pbnMgY2FuJ3QgcmVxdWVzdCBtb3JlIHNpbmNlIHRoZXJlJ3Mgbm90aGluZyB0byBhbGxvY2F0ZVxuICAgICAgLy8gLSBvdXRfb2ZfY3JlZGl0czogd2FsbGV0IGVtcHR5XG4gICAgICAvLyAtIG9yZ19sZXZlbF9kaXNhYmxlZF91bnRpbDogb3JnIHNwZW5kIGNhcCBoaXQgZm9yIHRoZSBtb250aFxuICAgICAgLy8gLSBvcmdfc2VydmljZV96ZXJvX2NyZWRpdF9saW1pdDogb3JnIHNlcnZpY2UgaGFzIHplcm8gY3JlZGl0IGxpbWl0XG4gICAgICBjb25zdCBpc09yZ1NwZW5kQ2FwRGVwbGV0ZWQgPVxuICAgICAgICBjbGF1ZGVBaUxpbWl0cy5vdmVyYWdlRGlzYWJsZWRSZWFzb24gPT09ICdvdXRfb2ZfY3JlZGl0cycgfHxcbiAgICAgICAgY2xhdWRlQWlMaW1pdHMub3ZlcmFnZURpc2FibGVkUmVhc29uID09PSAnb3JnX2xldmVsX2Rpc2FibGVkX3VudGlsJyB8fFxuICAgICAgICBjbGF1ZGVBaUxpbWl0cy5vdmVyYWdlRGlzYWJsZWRSZWFzb24gPT09ICdvcmdfc2VydmljZV96ZXJvX2NyZWRpdF9saW1pdCdcblxuICAgICAgLy8gSGlkZSBmb3Igbm9uLWFkbWluIFRlYW0vRW50ZXJwcmlzZSB1c2VycyB3aGVuIG9yZyBzcGVuZCBjYXAgaXMgZGVwbGV0ZWRcbiAgICAgIGlmIChuZWVkc1RvUmVxdWVzdEZyb21BZG1pbiAmJiBpc09yZ1NwZW5kQ2FwRGVwbGV0ZWQpIHtcbiAgICAgICAgLy8gRG9uJ3Qgc2hvdyBleHRyYS11c2FnZSBvcHRpb25cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IGlzT3ZlcmFnZVN0YXRlID1cbiAgICAgICAgICBjbGF1ZGVBaUxpbWl0cy5vdmVyYWdlU3RhdHVzID09PSAncmVqZWN0ZWQnIHx8XG4gICAgICAgICAgY2xhdWRlQWlMaW1pdHMub3ZlcmFnZVN0YXR1cyA9PT0gJ2FsbG93ZWRfd2FybmluZydcblxuICAgICAgICBsZXQgbGFiZWw6IHN0cmluZ1xuICAgICAgICBpZiAobmVlZHNUb1JlcXVlc3RGcm9tQWRtaW4pIHtcbiAgICAgICAgICBsYWJlbCA9IGlzT3ZlcmFnZVN0YXRlID8gJ1JlcXVlc3QgbW9yZScgOiAnUmVxdWVzdCBleHRyYSB1c2FnZSdcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBsYWJlbCA9IGhhc0V4dHJhVXNhZ2VFbmFibGVkXG4gICAgICAgICAgICA/ICdBZGQgZnVuZHMgdG8gY29udGludWUgd2l0aCBleHRyYSB1c2FnZSdcbiAgICAgICAgICAgIDogJ1N3aXRjaCB0byBleHRyYSB1c2FnZSdcbiAgICAgICAgfVxuXG4gICAgICAgIGFjdGlvbk9wdGlvbnMucHVzaCh7XG4gICAgICAgICAgbGFiZWwsXG4gICAgICAgICAgdmFsdWU6ICdleHRyYS11c2FnZScsXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFpc01heDIweCAmJiAhaXNUZWFtT3JFbnRlcnByaXNlICYmIHVwZ3JhZGUuaXNFbmFibGVkKCkpIHtcbiAgICAgIGFjdGlvbk9wdGlvbnMucHVzaCh7XG4gICAgICAgIGxhYmVsOiAnVXBncmFkZSB5b3VyIHBsYW4nLFxuICAgICAgICB2YWx1ZTogJ3VwZ3JhZGUnLFxuICAgICAgfSlcbiAgICB9XG5cbiAgICBjb25zdCBjYW5jZWxPcHRpb246IE9wdGlvbldpdGhEZXNjcmlwdGlvbjxSYXRlTGltaXRPcHRpb25zTWVudU9wdGlvblR5cGU+ID1cbiAgICAgIHtcbiAgICAgICAgbGFiZWw6ICdTdG9wIGFuZCB3YWl0IGZvciBsaW1pdCB0byByZXNldCcsXG4gICAgICAgIHZhbHVlOiAnY2FuY2VsJyxcbiAgICAgIH1cblxuICAgIGlmIChidXlGaXJzdCkge1xuICAgICAgcmV0dXJuIFsuLi5hY3Rpb25PcHRpb25zLCBjYW5jZWxPcHRpb25dXG4gICAgfVxuICAgIHJldHVybiBbY2FuY2VsT3B0aW9uLCAuLi5hY3Rpb25PcHRpb25zXVxuICB9LCBbXG4gICAgYnV5Rmlyc3QsXG4gICAgaXNNYXgyMHgsXG4gICAgaXNUZWFtT3JFbnRlcnByaXNlLFxuICAgIGhhc0V4dHJhVXNhZ2VFbmFibGVkLFxuICAgIGNsYXVkZUFpTGltaXRzLm92ZXJhZ2VTdGF0dXMsXG4gICAgY2xhdWRlQWlMaW1pdHMub3ZlcmFnZURpc2FibGVkUmVhc29uLFxuICBdKVxuXG4gIGZ1bmN0aW9uIGhhbmRsZUNhbmNlbCgpOiB2b2lkIHtcbiAgICBsb2dFdmVudCgndGVuZ3VfcmF0ZV9saW1pdF9vcHRpb25zX21lbnVfY2FuY2VsJywge30pXG4gICAgb25Eb25lKHVuZGVmaW5lZCwgeyBkaXNwbGF5OiAnc2tpcCcgfSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGhhbmRsZVNlbGVjdCh2YWx1ZTogUmF0ZUxpbWl0T3B0aW9uc01lbnVPcHRpb25UeXBlKTogdm9pZCB7XG4gICAgaWYgKHZhbHVlID09PSAndXBncmFkZScpIHtcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9yYXRlX2xpbWl0X29wdGlvbnNfbWVudV9zZWxlY3RfdXBncmFkZScsIHt9KVxuICAgICAgdm9pZCB1cGdyYWRlQ2FsbChvbkRvbmUsIGNvbnRleHQpLnRoZW4oanN4ID0+IHtcbiAgICAgICAgaWYgKGpzeCkge1xuICAgICAgICAgIHNldFN1YkNvbW1hbmRKU1goanN4KVxuICAgICAgICB9XG4gICAgICB9KVxuICAgIH0gZWxzZSBpZiAodmFsdWUgPT09ICdleHRyYS11c2FnZScpIHtcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9yYXRlX2xpbWl0X29wdGlvbnNfbWVudV9zZWxlY3RfZXh0cmFfdXNhZ2UnLCB7fSlcbiAgICAgIHZvaWQgZXh0cmFVc2FnZUNhbGwob25Eb25lLCBjb250ZXh0KS50aGVuKGpzeCA9PiB7XG4gICAgICAgIGlmIChqc3gpIHtcbiAgICAgICAgICBzZXRTdWJDb21tYW5kSlNYKGpzeClcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9IGVsc2UgaWYgKHZhbHVlID09PSAnY2FuY2VsJykge1xuICAgICAgaGFuZGxlQ2FuY2VsKClcbiAgICB9XG4gIH1cblxuICBpZiAoc3ViQ29tbWFuZEpTWCkge1xuICAgIHJldHVybiBzdWJDb21tYW5kSlNYXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxEaWFsb2dcbiAgICAgIHRpdGxlPVwiV2hhdCBkbyB5b3Ugd2FudCB0byBkbz9cIlxuICAgICAgb25DYW5jZWw9e2hhbmRsZUNhbmNlbH1cbiAgICAgIGNvbG9yPVwic3VnZ2VzdGlvblwiXG4gICAgPlxuICAgICAgPFNlbGVjdDxSYXRlTGltaXRPcHRpb25zTWVudU9wdGlvblR5cGU+XG4gICAgICAgIG9wdGlvbnM9e29wdGlvbnN9XG4gICAgICAgIG9uQ2hhbmdlPXtoYW5kbGVTZWxlY3R9XG4gICAgICAgIHZpc2libGVPcHRpb25Db3VudD17b3B0aW9ucy5sZW5ndGh9XG4gICAgICAvPlxuICAgIDwvRGlhbG9nPlxuICApXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogVG9vbFVzZUNvbnRleHQgJiBMb2NhbEpTWENvbW1hbmRDb250ZXh0LFxuKTogUHJvbWlzZTxSZWFjdC5SZWFjdE5vZGU+IHtcbiAgcmV0dXJuIDxSYXRlTGltaXRPcHRpb25zTWVudSBvbkRvbmU9e29uRG9uZX0gY29udGV4dD17Y29udGV4dH0gLz5cbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFBSUMsT0FBTyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUNoRCxjQUNFQyxvQkFBb0IsRUFDcEJDLHNCQUFzQixRQUNqQixtQkFBbUI7QUFDMUIsU0FDRSxLQUFLQyxxQkFBcUIsRUFDMUJDLE1BQU0sUUFDRCx5Q0FBeUM7QUFDaEQsU0FBU0MsTUFBTSxRQUFRLDBDQUEwQztBQUNqRSxTQUFTQyxtQ0FBbUMsUUFBUSx3Q0FBd0M7QUFDNUYsU0FBU0MsUUFBUSxR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 \ No newline at end of file diff --git a/commands/release-notes/index.ts b/commands/release-notes/index.ts new file mode 100644 index 0000000..75413de --- /dev/null +++ b/commands/release-notes/index.ts @@ -0,0 +1,11 @@ +import type { Command } from '../../commands.js' + +const releaseNotes: Command = { + description: 'View release notes', + name: 'release-notes', + type: 'local', + supportsNonInteractive: true, + load: () => import('./release-notes.js'), +} + +export default releaseNotes diff --git a/commands/release-notes/release-notes.ts b/commands/release-notes/release-notes.ts new file mode 100644 index 0000000..dfd7aec --- /dev/null +++ b/commands/release-notes/release-notes.ts @@ -0,0 +1,50 @@ +import type { LocalCommandResult } from '../../types/command.js' +import { + CHANGELOG_URL, + fetchAndStoreChangelog, + getAllReleaseNotes, + getStoredChangelog, +} from '../../utils/releaseNotes.js' + +function formatReleaseNotes(notes: Array<[string, string[]]>): string { + return notes + .map(([version, notes]) => { + const header = `Version ${version}:` + const bulletPoints = notes.map(note => `· ${note}`).join('\n') + return `${header}\n${bulletPoints}` + }) + .join('\n\n') +} + +export async function call(): Promise { + // Try to fetch the latest changelog with a 500ms timeout + let freshNotes: Array<[string, string[]]> = [] + + try { + const timeoutPromise = new Promise((_, reject) => { + setTimeout(rej => rej(new Error('Timeout')), 500, reject) + }) + + await Promise.race([fetchAndStoreChangelog(), timeoutPromise]) + freshNotes = getAllReleaseNotes(await getStoredChangelog()) + } catch { + // Either fetch failed or timed out - just use cached notes + } + + // If we have fresh notes from the quick fetch, use those + if (freshNotes.length > 0) { + return { type: 'text', value: formatReleaseNotes(freshNotes) } + } + + // Otherwise check cached notes + const cachedNotes = getAllReleaseNotes(await getStoredChangelog()) + if (cachedNotes.length > 0) { + return { type: 'text', value: formatReleaseNotes(cachedNotes) } + } + + // Nothing available, show link + return { + type: 'text', + value: `See the full changelog at: ${CHANGELOG_URL}`, + } +} diff --git a/commands/reload-plugins/index.ts b/commands/reload-plugins/index.ts new file mode 100644 index 0000000..5d7a163 --- /dev/null +++ b/commands/reload-plugins/index.ts @@ -0,0 +1,18 @@ +/** + * /reload-plugins — Layer-3 refresh. Applies pending plugin changes to the + * running session. Implementation lazy-loaded. + */ +import type { Command } from '../../commands.js' + +const reloadPlugins = { + type: 'local', + name: 'reload-plugins', + description: 'Activate pending plugin changes in the current session', + // SDK callers use query.reloadPlugins() (control request) instead of + // sending this as a text prompt — that returns structured data + // (commands, agents, plugins, mcpServers) for UI updates. + supportsNonInteractive: false, + load: () => import('./reload-plugins.js'), +} satisfies Command + +export default reloadPlugins diff --git a/commands/reload-plugins/reload-plugins.ts b/commands/reload-plugins/reload-plugins.ts new file mode 100644 index 0000000..0789be4 --- /dev/null +++ b/commands/reload-plugins/reload-plugins.ts @@ -0,0 +1,61 @@ +import { feature } from 'bun:bundle' +import { getIsRemoteMode } from '../../bootstrap/state.js' +import { redownloadUserSettings } from '../../services/settingsSync/index.js' +import type { LocalCommandCall } from '../../types/command.js' +import { isEnvTruthy } from '../../utils/envUtils.js' +import { refreshActivePlugins } from '../../utils/plugins/refresh.js' +import { settingsChangeDetector } from '../../utils/settings/changeDetector.js' +import { plural } from '../../utils/stringUtils.js' + +export const call: LocalCommandCall = async (_args, context) => { + // CCR: re-pull user settings before the cache sweep so enabledPlugins / + // extraKnownMarketplaces pushed from the user's local CLI (settingsSync) + // take effect. Non-CCR headless (e.g. vscode SDK subprocess) shares disk + // with whoever writes settings — the file watcher delivers changes, no + // re-pull needed there. + // + // Managed settings intentionally NOT re-fetched: it already polls hourly + // (POLLING_INTERVAL_MS), and policy enforcement is eventually-consistent + // by design (stale-cache fallback on fetch failure). Interactive + // /reload-plugins has never re-fetched it either. + // + // No retries: user-initiated command, one attempt + fail-open. The user + // can re-run /reload-plugins to retry. Startup path keeps its retries. + if ( + feature('DOWNLOAD_USER_SETTINGS') && + (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) || getIsRemoteMode()) + ) { + const applied = await redownloadUserSettings() + // applyRemoteEntriesToLocal uses markInternalWrite to suppress the + // file watcher (correct for startup, nothing listening yet); fire + // notifyChange here so mid-session applySettingsChange runs. + if (applied) { + settingsChangeDetector.notifyChange('userSettings') + } + } + + const r = await refreshActivePlugins(context.setAppState) + + const parts = [ + n(r.enabled_count, 'plugin'), + n(r.command_count, 'skill'), + n(r.agent_count, 'agent'), + n(r.hook_count, 'hook'), + // "plugin MCP/LSP" disambiguates from user-config/built-in servers, + // which /reload-plugins doesn't touch. Commands/hooks are plugin-only; + // agent_count is total agents (incl. built-ins). (gh-31321) + n(r.mcp_count, 'plugin MCP server'), + n(r.lsp_count, 'plugin LSP server'), + ] + let msg = `Reloaded: ${parts.join(' · ')}` + + if (r.error_count > 0) { + msg += `\n${n(r.error_count, 'error')} during load. Run /doctor for details.` + } + + return { type: 'text', value: msg } +} + +function n(count: number, noun: string): string { + return `${count} ${plural(count, noun)}` +} diff --git a/commands/remote-env/index.ts b/commands/remote-env/index.ts new file mode 100644 index 0000000..090cc60 --- /dev/null +++ b/commands/remote-env/index.ts @@ -0,0 +1,15 @@ +import type { Command } from '../../commands.js' +import { isPolicyAllowed } from '../../services/policyLimits/index.js' +import { isClaudeAISubscriber } from '../../utils/auth.js' + +export default { + type: 'local-jsx', + name: 'remote-env', + description: 'Configure the default remote environment for teleport sessions', + isEnabled: () => + isClaudeAISubscriber() && isPolicyAllowed('allow_remote_sessions'), + get isHidden() { + return !isClaudeAISubscriber() || !isPolicyAllowed('allow_remote_sessions') + }, + load: () => import('./remote-env.js'), +} satisfies Command diff --git a/commands/remote-env/remote-env.tsx b/commands/remote-env/remote-env.tsx new file mode 100644 index 0000000..dce659a --- /dev/null +++ b/commands/remote-env/remote-env.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; +import { RemoteEnvironmentDialog } from '../../components/RemoteEnvironmentDialog.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +export async function call(onDone: LocalJSXCommandOnDone): Promise { + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlbW90ZUVudmlyb25tZW50RGlhbG9nIiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiY2FsbCIsIm9uRG9uZSIsIlByb21pc2UiLCJSZWFjdE5vZGUiXSwic291cmNlcyI6WyJyZW1vdGUtZW52LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFJlbW90ZUVudmlyb25tZW50RGlhbG9nIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9SZW1vdGVFbnZpcm9ubWVudERpYWxvZy5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kT25Eb25lIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lLFxuKTogUHJvbWlzZTxSZWFjdC5SZWFjdE5vZGU+IHtcbiAgcmV0dXJuIDxSZW1vdGVFbnZpcm9ubWVudERpYWxvZyBvbkRvbmU9e29uRG9uZX0gLz5cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyx1QkFBdUIsUUFBUSw2Q0FBNkM7QUFDckYsY0FBY0MscUJBQXFCLFFBQVEsd0JBQXdCO0FBRW5FLE9BQU8sZUFBZUMsSUFBSUEsQ0FDeEJDLE1BQU0sRUFBRUYscUJBQXFCLENBQzlCLEVBQUVHLE9BQU8sQ0FBQ0wsS0FBSyxDQUFDTSxTQUFTLENBQUMsQ0FBQztFQUMxQixPQUFPLENBQUMsdUJBQXVCLENBQUMsTUFBTSxDQUFDLENBQUNGLE1BQU0sQ0FBQyxHQUFHO0FBQ3BEIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/remote-setup/api.ts b/commands/remote-setup/api.ts new file mode 100644 index 0000000..d08659c --- /dev/null +++ b/commands/remote-setup/api.ts @@ -0,0 +1,182 @@ +import axios from 'axios' +import { getOauthConfig } from '../../constants/oauth.js' +import { logForDebugging } from '../../utils/debug.js' +import { getOAuthHeaders, prepareApiRequest } from '../../utils/teleport/api.js' +import { fetchEnvironments } from '../../utils/teleport/environments.js' + +const CCR_BYOC_BETA_HEADER = 'ccr-byoc-2025-07-29' + +/** + * Wraps a raw GitHub token so that its string representation is redacted. + * `String(token)`, template literals, `JSON.stringify(token)`, and any + * attached error messages will show `[REDACTED:gh-token]` instead of the + * token value. Call `.reveal()` only at the single point where the raw + * value is placed into an HTTP body. + */ +export class RedactedGithubToken { + readonly #value: string + constructor(raw: string) { + this.#value = raw + } + reveal(): string { + return this.#value + } + toString(): string { + return '[REDACTED:gh-token]' + } + toJSON(): string { + return '[REDACTED:gh-token]' + } + [Symbol.for('nodejs.util.inspect.custom')](): string { + return '[REDACTED:gh-token]' + } +} + +export type ImportTokenResult = { + github_username: string +} + +export type ImportTokenError = + | { kind: 'not_signed_in' } + | { kind: 'invalid_token' } + | { kind: 'server'; status: number } + | { kind: 'network' } + +/** + * POSTs a GitHub token to the CCR backend, which validates it against + * GitHub's /user endpoint and stores it Fernet-encrypted in sync_user_tokens. + * The stored token satisfies the same read paths as an OAuth token, so + * clone/push in claude.ai/code works immediately after this succeeds. + */ +export async function importGithubToken( + token: RedactedGithubToken, +): Promise< + | { ok: true; result: ImportTokenResult } + | { ok: false; error: ImportTokenError } +> { + let accessToken: string, orgUUID: string + try { + ;({ accessToken, orgUUID } = await prepareApiRequest()) + } catch { + return { ok: false, error: { kind: 'not_signed_in' } } + } + + const url = `${getOauthConfig().BASE_API_URL}/v1/code/github/import-token` + const headers = { + ...getOAuthHeaders(accessToken), + 'anthropic-beta': CCR_BYOC_BETA_HEADER, + 'x-organization-uuid': orgUUID, + } + + try { + const response = await axios.post( + url, + { token: token.reveal() }, + { headers, timeout: 15000, validateStatus: () => true }, + ) + if (response.status === 200) { + return { ok: true, result: response.data } + } + if (response.status === 400) { + return { ok: false, error: { kind: 'invalid_token' } } + } + if (response.status === 401) { + return { ok: false, error: { kind: 'not_signed_in' } } + } + logForDebugging(`import-token returned ${response.status}`, { + level: 'error', + }) + return { ok: false, error: { kind: 'server', status: response.status } } + } catch (err) { + if (axios.isAxiosError(err)) { + // err.config.data would contain the POST body with the raw token. + // Do not include it in any log. The error code alone is enough. + logForDebugging(`import-token network error: ${err.code ?? 'unknown'}`, { + level: 'error', + }) + } + return { ok: false, error: { kind: 'network' } } + } +} + +async function hasExistingEnvironment(): Promise { + try { + const envs = await fetchEnvironments() + return envs.length > 0 + } catch { + return false + } +} + +/** + * Best-effort default environment creation. Mirrors the web onboarding's + * DEFAULT_CLOUD_ENVIRONMENT_REQUEST so a first-time user lands on the + * composer instead of env-setup. Checks for existing environments first + * so re-running /web-setup doesn't pile up duplicates. Failures are + * non-fatal — the token import already succeeded, and the web state + * machine falls back to env-setup on next load. + */ +export async function createDefaultEnvironment(): Promise { + let accessToken: string, orgUUID: string + try { + ;({ accessToken, orgUUID } = await prepareApiRequest()) + } catch { + return false + } + + if (await hasExistingEnvironment()) { + return true + } + + // The /private/organizations/{org}/ path rejects CLI OAuth tokens (wrong + // auth dep). The public path uses build_flexible_auth — same path + // fetchEnvironments() uses. Org is passed via x-organization-uuid header. + const url = `${getOauthConfig().BASE_API_URL}/v1/environment_providers/cloud/create` + const headers = { + ...getOAuthHeaders(accessToken), + 'x-organization-uuid': orgUUID, + } + + try { + const response = await axios.post( + url, + { + name: 'Default', + kind: 'anthropic_cloud', + description: 'Default - trusted network access', + config: { + environment_type: 'anthropic', + cwd: '/home/user', + init_script: null, + environment: {}, + languages: [ + { name: 'python', version: '3.11' }, + { name: 'node', version: '20' }, + ], + network_config: { + allowed_hosts: [], + allow_default_hosts: true, + }, + }, + }, + { headers, timeout: 15000, validateStatus: () => true }, + ) + return response.status >= 200 && response.status < 300 + } catch { + return false + } +} + +/** Returns true when the user has valid Claude OAuth credentials. */ +export async function isSignedIn(): Promise { + try { + await prepareApiRequest() + return true + } catch { + return false + } +} + +export function getCodeWebUrl(): string { + return `${getOauthConfig().CLAUDE_AI_ORIGIN}/code` +} diff --git a/commands/remote-setup/index.ts b/commands/remote-setup/index.ts new file mode 100644 index 0000000..7b291df --- /dev/null +++ b/commands/remote-setup/index.ts @@ -0,0 +1,20 @@ +import type { Command } from '../../commands.js' +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' +import { isPolicyAllowed } from '../../services/policyLimits/index.js' + +const web = { + type: 'local-jsx', + name: 'web-setup', + description: + 'Setup Claude Code on the web (requires connecting your GitHub account)', + availability: ['claude-ai'], + isEnabled: () => + getFeatureValue_CACHED_MAY_BE_STALE('tengu_cobalt_lantern', false) && + isPolicyAllowed('allow_remote_sessions'), + get isHidden() { + return !isPolicyAllowed('allow_remote_sessions') + }, + load: () => import('./remote-setup.js'), +} satisfies Command + +export default web diff --git a/commands/remote-setup/remote-setup.tsx b/commands/remote-setup/remote-setup.tsx new file mode 100644 index 0000000..0092879 --- /dev/null +++ b/commands/remote-setup/remote-setup.tsx @@ -0,0 +1,187 @@ +import { execa } from 'execa'; +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { Select } from '../../components/CustomSelect/index.js'; +import { Dialog } from '../../components/design-system/Dialog.js'; +import { LoadingState } from '../../components/design-system/LoadingState.js'; +import { Box, Text } from '../../ink.js'; +import { logEvent, type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS as SafeString } from '../../services/analytics/index.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import { openBrowser } from '../../utils/browser.js'; +import { getGhAuthStatus } from '../../utils/github/ghAuthStatus.js'; +import { createDefaultEnvironment, getCodeWebUrl, type ImportTokenError, importGithubToken, isSignedIn, RedactedGithubToken } from './api.js'; +type CheckResult = { + status: 'not_signed_in'; +} | { + status: 'has_gh_token'; + token: RedactedGithubToken; +} | { + status: 'gh_not_installed'; +} | { + status: 'gh_not_authenticated'; +}; +async function checkLoginState(): Promise { + if (!(await isSignedIn())) { + return { + status: 'not_signed_in' + }; + } + const ghStatus = await getGhAuthStatus(); + if (ghStatus === 'not_installed') { + return { + status: 'gh_not_installed' + }; + } + if (ghStatus === 'not_authenticated') { + return { + status: 'gh_not_authenticated' + }; + } + + // ghStatus === 'authenticated'. getGhAuthStatus spawns with stdout:'ignore' + // (telemetry-safe); spawn once more with stdout:'pipe' to read the token. + const { + stdout + } = await execa('gh', ['auth', 'token'], { + stdout: 'pipe', + stderr: 'ignore', + timeout: 5000, + reject: false + }); + const trimmed = stdout.trim(); + if (!trimmed) { + return { + status: 'gh_not_authenticated' + }; + } + return { + status: 'has_gh_token', + token: new RedactedGithubToken(trimmed) + }; +} +function errorMessage(err: ImportTokenError, codeUrl: string): string { + switch (err.kind) { + case 'not_signed_in': + return `Login failed. Please visit ${codeUrl} and login using the GitHub App`; + case 'invalid_token': + return 'GitHub rejected that token. Run `gh auth login` and try again.'; + case 'server': + return `Server error (${err.status}). Try again in a moment.`; + case 'network': + return "Couldn't reach the server. Check your connection."; + } +} +type Step = { + name: 'checking'; +} | { + name: 'confirm'; + token: RedactedGithubToken; +} | { + name: 'uploading'; +}; +function Web({ + onDone +}: { + onDone: LocalJSXCommandOnDone; +}) { + const [step, setStep] = useState({ + name: 'checking' + }); + useEffect(() => { + logEvent('tengu_remote_setup_started', {}); + void checkLoginState().then(async result => { + switch (result.status) { + case 'not_signed_in': + logEvent('tengu_remote_setup_result', { + result: 'not_signed_in' as SafeString + }); + onDone('Not signed in to Claude. Run /login first.'); + return; + case 'gh_not_installed': + case 'gh_not_authenticated': + { + const url = `${getCodeWebUrl()}/onboarding?step=alt-auth`; + await openBrowser(url); + logEvent('tengu_remote_setup_result', { + result: result.status as SafeString + }); + onDone(result.status === 'gh_not_installed' ? `GitHub CLI not found. Install it via https://cli.github.com/, then run \`gh auth login\`, or connect GitHub on the web: ${url}` : `GitHub CLI not authenticated. Run \`gh auth login\` and try again, or connect GitHub on the web: ${url}`); + return; + } + case 'has_gh_token': + setStep({ + name: 'confirm', + token: result.token + }); + } + }); + // onDone is stable across renders; intentionally not in deps. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const handleCancel = () => { + logEvent('tengu_remote_setup_result', { + result: 'cancelled' as SafeString + }); + onDone(); + }; + const handleConfirm = async (token: RedactedGithubToken) => { + setStep({ + name: 'uploading' + }); + const result = await importGithubToken(token); + if (!result.ok) { + logEvent('tengu_remote_setup_result', { + result: 'import_failed' as SafeString, + error_kind: result.error.kind as SafeString + }); + onDone(errorMessage(result.error, getCodeWebUrl())); + return; + } + + // Token import succeeded. Environment creation is best-effort — if it + // fails, the web state machine routes to env-setup on landing, which is + // one extra click but still better than the OAuth dance. + await createDefaultEnvironment(); + const url = getCodeWebUrl(); + await openBrowser(url); + logEvent('tengu_remote_setup_result', { + result: 'success' as SafeString + }); + onDone(`Connected as ${result.result.github_username}. Opened ${url}`); + }; + if (step.name === 'checking') { + return ; + } + if (step.name === 'uploading') { + return ; + } + const token = step.token; + return + + + Claude on the web requires connecting to your GitHub account to clone + and push code on your behalf. + + + Your local credentials are used to authenticate with GitHub + + + }; + $[8] = handleCancel; + $[9] = handleSelect; + $[10] = isLaunching; + $[11] = t6; + } else { + t6 = $[11]; + } + let t7; + if ($[12] !== handleCancel || $[13] !== t6) { + t7 = {t6}; + $[12] = handleCancel; + $[13] = t6; + $[14] = t7; + } else { + t7 = $[14]; + } + return t7; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlUmVmIiwidXNlU3RhdGUiLCJTZWxlY3QiLCJEaWFsb2ciLCJCb3giLCJUZXh0IiwiUHJvcHMiLCJvblByb2NlZWQiLCJzaWduYWwiLCJBYm9ydFNpZ25hbCIsIlByb21pc2UiLCJvbkNhbmNlbCIsIlVsdHJhcmV2aWV3T3ZlcmFnZURpYWxvZyIsInQwIiwiJCIsIl9jIiwiaXNMYXVuY2hpbmciLCJzZXRJc0xhdW5jaGluZyIsInQxIiwiU3ltYm9sIiwiZm9yIiwiQWJvcnRDb250cm9sbGVyIiwiYWJvcnRDb250cm9sbGVyUmVmIiwidDIiLCJ2YWx1ZSIsImN1cnJlbnQiLCJjYXRjaCIsImhhbmRsZVNlbGVjdCIsInQzIiwiYWJvcnQiLCJoYW5kbGVDYW5jZWwiLCJ0NCIsImxhYmVsIiwib3B0aW9ucyIsInQ1IiwidDYiLCJ0NyJdLCJzb3VyY2VzIjpbIlVsdHJhcmV2aWV3T3ZlcmFnZURpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHVzZUNhbGxiYWNrLCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL0N1c3RvbVNlbGVjdC9zZWxlY3QuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBvblByb2NlZWQ6IChzaWduYWw6IEFib3J0U2lnbmFsKSA9PiBQcm9taXNlPHZvaWQ+XG4gIG9uQ2FuY2VsOiAoKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBVbHRyYXJldmlld092ZXJhZ2VEaWFsb2coe1xuICBvblByb2NlZWQsXG4gIG9uQ2FuY2VsLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbaXNMYXVuY2hpbmcsIHNldElzTGF1bmNoaW5nXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBhYm9ydENvbnRyb2xsZXJSZWYgPSB1c2VSZWYobmV3IEFib3J0Q29udHJvbGxlcigpKVxuXG4gIGNvbnN0IGhhbmRsZVNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgICh2YWx1ZTogc3RyaW5nKSA9PiB7XG4gICAgICBpZiAodmFsdWUgPT09ICdwcm9jZWVkJykge1xuICAgICAgICBzZXRJc0xhdW5jaGluZyh0cnVlKVxuICAgICAgICAvLyBJZiBvblByb2NlZWQgcmVqZWN0cyAoZS5nLiBsYXVuY2hSZW1vdGVSZXZpZXcgdGhyb3dzKSwgb25Eb25lIGlzXG4gICAgICAgIC8vIG5ldmVyIGNhbGxlZCBhbmQgdGhlIGRpYWxvZyBzdGF5cyBtb3VudGVkIOKAlCByZXN0b3JlIHRoZSBTZWxlY3Qgc29cbiAgICAgICAgLy8gdGhlIHVzZXIgY2FuIHJldHJ5IG9yIGNhbmNlbCBpbnN0ZWFkIG9mIHN0YXJpbmcgYXQgXCJMYXVuY2hpbmfigKZcIi5cbiAgICAgICAgdm9pZCBvblByb2NlZWQoYWJvcnRDb250cm9sbGVyUmVmLmN1cnJlbnQuc2lnbmFsKS5jYXRjaCgoKSA9PlxuICAgICAgICAgIHNldElzTGF1bmNoaW5nKGZhbHNlKSxcbiAgICAgICAgKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb25DYW5jZWwoKVxuICAgICAgfVxuICAgIH0sXG4gICAgW29uUHJvY2VlZCwgb25DYW5jZWxdLFxuICApXG5cbiAgLy8gRXNjYXBlIGR1cmluZyBsYXVuY2ggYWJvcnRzIHRoZSBpbi1mbGlnaHQgb25Qcm9jZWVkIHZpYSBzaWduYWwgc28gdGhlXG4gIC8vIGNhbGxlciBjYW4gc2tpcCBzaWRlIGVmZmVjdHMgKGNvbmZpcm1PdmVyYWdlLCBvbkRvbmUpIOKAlCBvdGhlcndpc2UgYVxuICAvLyBmaXJlLWFuZC1mb3JnZXQgbGF1bmNoIHdvdWxkIGtlZXAgcnVubmluZyBhbmQgYmlsbCBkZXNwaXRlIFwiY2FuY2VsbGVkXCIuXG4gIGNvbnN0IGhhbmRsZUNhbmNlbCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBhYm9ydENvbnRyb2xsZXJSZWYuY3VycmVudC5hYm9ydCgpXG4gICAgb25DYW5jZWwoKVxuICB9LCBbb25DYW5jZWxdKVxuXG4gIGNvbnN0IG9wdGlvbnMgPSBbXG4gICAgeyBsYWJlbDogJ1Byb2NlZWQgd2l0aCBFeHRyYSBVc2FnZSBiaWxsaW5nJywgdmFsdWU6ICdwcm9jZWVkJyB9LFxuICAgIHsgbGFiZWw6ICdDYW5jZWwnLCB2YWx1ZTogJ2NhbmNlbCcgfSxcbiAgXVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJVbHRyYXJldmlldyBiaWxsaW5nXCJcbiAgICAgIG9uQ2FuY2VsPXtoYW5kbGVDYW5jZWx9XG4gICAgICBjb2xvcj1cImJhY2tncm91bmRcIlxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIFlvdXIgZnJlZSB1bHRyYXJldmlld3MgZm9yIHRoaXMgb3JnYW5pemF0aW9uIGFyZSB1c2VkLiBGdXJ0aGVyIHJldmlld3NcbiAgICAgICAgICBiaWxsIGFzIEV4dHJhIFVzYWdlIChwYXktcGVyLXVzZSkuXG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAge2lzTGF1bmNoaW5nID8gKFxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiPkxhdW5jaGluZ+KApjwvVGV4dD5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8U2VsZWN0XG4gICAgICAgICAgICBvcHRpb25zPXtvcHRpb25zfVxuICAgICAgICAgICAgb25DaGFuZ2U9e2hhbmRsZVNlbGVjdH1cbiAgICAgICAgICAgIG9uQ2FuY2VsPXtoYW5kbGVDYW5jZWx9XG4gICAgICAgICAgLz5cbiAgICAgICAgKX1cbiAgICAgIDwvQm94PlxuICAgIDwvRGlhbG9nPlxuICAp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 \ No newline at end of file diff --git a/commands/review/reviewRemote.ts b/commands/review/reviewRemote.ts new file mode 100644 index 0000000..0b80e33 --- /dev/null +++ b/commands/review/reviewRemote.ts @@ -0,0 +1,316 @@ +/** + * Teleported /ultrareview execution. Creates a CCR session with the current repo, + * sends the review prompt as the initial message, and registers a + * RemoteAgentTask so the polling loop pipes results back into the local + * session via task-notification. Mirrors the /ultraplan → CCR flow. + * + * TODO(#22051): pass useBundleMode once landed so local-only / uncommitted + * repo state is captured. The GitHub-clone path (current) only works for + * pushed branches on repos with the Claude GitHub app installed. + */ + +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.js' +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../../services/analytics/index.js' +import { fetchUltrareviewQuota } from '../../services/api/ultrareviewQuota.js' +import { fetchUtilization } from '../../services/api/usage.js' +import type { ToolUseContext } from '../../Tool.js' +import { + checkRemoteAgentEligibility, + formatPreconditionError, + getRemoteTaskSessionUrl, + registerRemoteAgentTask, +} from '../../tasks/RemoteAgentTask/RemoteAgentTask.js' +import { isEnterpriseSubscriber, isTeamSubscriber } from '../../utils/auth.js' +import { detectCurrentRepositoryWithHost } from '../../utils/detectRepository.js' +import { execFileNoThrow } from '../../utils/execFileNoThrow.js' +import { getDefaultBranch, gitExe } from '../../utils/git.js' +import { teleportToRemote } from '../../utils/teleport.js' + +// One-time session flag: once the user confirms overage billing via the +// dialog, all subsequent /ultrareview invocations in this session proceed +// without re-prompting. +let sessionOverageConfirmed = false + +export function confirmOverage(): void { + sessionOverageConfirmed = true +} + +export type OverageGate = + | { kind: 'proceed'; billingNote: string } + | { kind: 'not-enabled' } + | { kind: 'low-balance'; available: number } + | { kind: 'needs-confirm' } + +/** + * Determine whether the user can launch an ultrareview and under what + * billing terms. Fetches quota and utilization in parallel. + */ +export async function checkOverageGate(): Promise { + // Team and Enterprise plans include ultrareview — no free-review quota + // or Extra Usage dialog. The quota endpoint is scoped to consumer plans + // (pro/max); hitting it on team/ent would surface a confusing dialog. + if (isTeamSubscriber() || isEnterpriseSubscriber()) { + return { kind: 'proceed', billingNote: '' } + } + + const [quota, utilization] = await Promise.all([ + fetchUltrareviewQuota(), + fetchUtilization().catch(() => null), + ]) + + // No quota info (non-subscriber or endpoint down) — let it through, + // server-side billing will handle it. + if (!quota) { + return { kind: 'proceed', billingNote: '' } + } + + if (quota.reviews_remaining > 0) { + return { + kind: 'proceed', + billingNote: ` This is free ultrareview ${quota.reviews_used + 1} of ${quota.reviews_limit}.`, + } + } + + // Utilization fetch failed (transient network error, timeout, etc.) — + // let it through, same rationale as the quota fallback above. + if (!utilization) { + return { kind: 'proceed', billingNote: '' } + } + + // Free reviews exhausted — check Extra Usage setup. + const extraUsage = utilization.extra_usage + if (!extraUsage?.is_enabled) { + logEvent('tengu_review_overage_not_enabled', {}) + return { kind: 'not-enabled' } + } + + // Check available balance (null monthly_limit = unlimited). + const monthlyLimit = extraUsage.monthly_limit + const usedCredits = extraUsage.used_credits ?? 0 + const available = + monthlyLimit === null || monthlyLimit === undefined + ? Infinity + : monthlyLimit - usedCredits + + if (available < 10) { + logEvent('tengu_review_overage_low_balance', { available }) + return { kind: 'low-balance', available } + } + + if (!sessionOverageConfirmed) { + logEvent('tengu_review_overage_dialog_shown', {}) + return { kind: 'needs-confirm' } + } + + return { + kind: 'proceed', + billingNote: ' This review bills as Extra Usage.', + } +} + +/** + * Launch a teleported review session. Returns ContentBlockParam[] describing + * the launch outcome for injection into the local conversation (model is then + * queried with this content, so it can narrate the launch to the user). + * + * Returns ContentBlockParam[] with user-facing error messages on recoverable + * failures (missing merge-base, empty diff, bundle too large), or null on + * other failures so the caller falls through to the local-review prompt. + * Reason is captured in analytics. + * + * Caller must run checkOverageGate() BEFORE calling this function + * (ultrareviewCommand.tsx handles the dialog). + */ +export async function launchRemoteReview( + args: string, + context: ToolUseContext, + billingNote?: string, +): Promise { + const eligibility = await checkRemoteAgentEligibility() + // Synthetic DEFAULT_CODE_REVIEW_ENVIRONMENT_ID works without per-org CCR + // setup, so no_remote_environment isn't a blocker. Server-side quota + // consume at session creation routes billing: first N zero-rate, then + // anthropic:cccr org-service-key (overage-only). + if (!eligibility.eligible) { + const blockers = eligibility.errors.filter( + e => e.type !== 'no_remote_environment', + ) + if (blockers.length > 0) { + logEvent('tengu_review_remote_precondition_failed', { + precondition_errors: blockers + .map(e => e.type) + .join( + ',', + ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + const reasons = blockers.map(formatPreconditionError).join('\n') + return [ + { + type: 'text', + text: `Ultrareview cannot launch:\n${reasons}`, + }, + ] + } + } + + const resolvedBillingNote = billingNote ?? '' + + const prNumber = args.trim() + const isPrNumber = /^\d+$/.test(prNumber) + // Synthetic code_review env. Go taggedid.FromUUID(TagEnvironment, + // UUID{...,0x02}) encodes with version prefix '01' — NOT Python's + // legacy tagged_id() format. Verified in prod. + const CODE_REVIEW_ENV_ID = 'env_011111111111111111111113' + // Lite-review bypasses bughunter.go entirely, so it doesn't see the + // webhook's bug_hunter_config (different GB project). These env vars are + // the only tuning surface — without them, run_hunt.sh's bash defaults + // apply (60min, 120s agent timeout), and 120s kills verifiers mid-run + // which causes infinite respawn. + // + // total_wallclock must stay below RemoteAgentTask's 30min poll timeout + // with headroom for finalization (~3min synthesis). Per-field guards + // match autoDream.ts — GB cache can return stale wrong-type values. + const raw = getFeatureValue_CACHED_MAY_BE_STALE | null>('tengu_review_bughunter_config', null) + const posInt = (v: unknown, fallback: number, max?: number): number => { + if (typeof v !== 'number' || !Number.isFinite(v)) return fallback + const n = Math.floor(v) + if (n <= 0) return fallback + return max !== undefined && n > max ? fallback : n + } + // Upper bounds: 27min on wallclock leaves ~3min for finalization under + // RemoteAgentTask's 30min poll timeout. If GB is set above that, the + // hang we're fixing comes back — fall to the safe default instead. + const commonEnvVars = { + BUGHUNTER_DRY_RUN: '1', + BUGHUNTER_FLEET_SIZE: String(posInt(raw?.fleet_size, 5, 20)), + BUGHUNTER_MAX_DURATION: String(posInt(raw?.max_duration_minutes, 10, 25)), + BUGHUNTER_AGENT_TIMEOUT: String( + posInt(raw?.agent_timeout_seconds, 600, 1800), + ), + BUGHUNTER_TOTAL_WALLCLOCK: String( + posInt(raw?.total_wallclock_minutes, 22, 27), + ), + ...(process.env.BUGHUNTER_DEV_BUNDLE_B64 && { + BUGHUNTER_DEV_BUNDLE_B64: process.env.BUGHUNTER_DEV_BUNDLE_B64, + }), + } + + let session + let command + let target + if (isPrNumber) { + // PR mode: refs/pull/N/head via github.com. Orchestrator --pr N. + const repo = await detectCurrentRepositoryWithHost() + if (!repo || repo.host !== 'github.com') { + logEvent('tengu_review_remote_precondition_failed', {}) + return null + } + session = await teleportToRemote({ + initialMessage: null, + description: `ultrareview: ${repo.owner}/${repo.name}#${prNumber}`, + signal: context.abortController.signal, + branchName: `refs/pull/${prNumber}/head`, + environmentId: CODE_REVIEW_ENV_ID, + environmentVariables: { + BUGHUNTER_PR_NUMBER: prNumber, + BUGHUNTER_REPOSITORY: `${repo.owner}/${repo.name}`, + ...commonEnvVars, + }, + }) + command = `/ultrareview ${prNumber}` + target = `${repo.owner}/${repo.name}#${prNumber}` + } else { + // Branch mode: bundle the working tree, orchestrator diffs against + // the fork point. No PR, no existing comments, no dedup. + const baseBranch = (await getDefaultBranch()) || 'main' + // Env-manager's `git remote remove origin` after bundle-clone + // deletes refs/remotes/origin/* — the base branch name won't resolve + // in the container. Pass the merge-base SHA instead: it's reachable + // from HEAD's history so `git diff ` works without a named ref. + const { stdout: mbOut, code: mbCode } = await execFileNoThrow( + gitExe(), + ['merge-base', baseBranch, 'HEAD'], + { preserveOutputOnError: false }, + ) + const mergeBaseSha = mbOut.trim() + if (mbCode !== 0 || !mergeBaseSha) { + logEvent('tengu_review_remote_precondition_failed', {}) + return [ + { + type: 'text', + text: `Could not find merge-base with ${baseBranch}. Make sure you're in a git repo with a ${baseBranch} branch.`, + }, + ] + } + + // Bail early on empty diffs instead of launching a container that + // will just echo "no changes". + const { stdout: diffStat, code: diffCode } = await execFileNoThrow( + gitExe(), + ['diff', '--shortstat', mergeBaseSha], + { preserveOutputOnError: false }, + ) + if (diffCode === 0 && !diffStat.trim()) { + logEvent('tengu_review_remote_precondition_failed', {}) + return [ + { + type: 'text', + text: `No changes against the ${baseBranch} fork point. Make some commits or stage files first.`, + }, + ] + } + + session = await teleportToRemote({ + initialMessage: null, + description: `ultrareview: ${baseBranch}`, + signal: context.abortController.signal, + useBundle: true, + environmentId: CODE_REVIEW_ENV_ID, + environmentVariables: { + BUGHUNTER_BASE_BRANCH: mergeBaseSha, + ...commonEnvVars, + }, + }) + if (!session) { + logEvent('tengu_review_remote_teleport_failed', {}) + return [ + { + type: 'text', + text: 'Repo is too large. Push a PR and use `/ultrareview ` instead.', + }, + ] + } + command = '/ultrareview' + target = baseBranch + } + + if (!session) { + logEvent('tengu_review_remote_teleport_failed', {}) + return null + } + registerRemoteAgentTask({ + remoteTaskType: 'ultrareview', + session, + command, + context, + isRemoteReview: true, + }) + logEvent('tengu_review_remote_launched', {}) + const sessionUrl = getRemoteTaskSessionUrl(session.id) + // Concise — the tool-output block is visible to the user, so the model + // shouldn't echo the same info. Just enough for Claude to acknowledge the + // launch without restating the target/URL (both already printed above). + return [ + { + type: 'text', + text: `Ultrareview launched for ${target} (~10–20 min, runs in the cloud). Track: ${sessionUrl}${resolvedBillingNote} Findings arrive via task-notification. Briefly acknowledge the launch to the user without repeating the target or URL — both are already visible in the tool output above.`, + }, + ] +} diff --git a/commands/review/ultrareviewCommand.tsx b/commands/review/ultrareviewCommand.tsx new file mode 100644 index 0000000..3af5f5c --- /dev/null +++ b/commands/review/ultrareviewCommand.tsx @@ -0,0 +1,58 @@ +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.js'; +import React from 'react'; +import type { LocalJSXCommandCall, LocalJSXCommandOnDone } from '../../types/command.js'; +import { checkOverageGate, confirmOverage, launchRemoteReview } from './reviewRemote.js'; +import { UltrareviewOverageDialog } from './UltrareviewOverageDialog.js'; +function contentBlocksToString(blocks: ContentBlockParam[]): string { + return blocks.map(b => b.type === 'text' ? b.text : '').filter(Boolean).join('\n'); +} +async function launchAndDone(args: string, context: Parameters[1], onDone: LocalJSXCommandOnDone, billingNote: string, signal?: AbortSignal): Promise { + const result = await launchRemoteReview(args, context, billingNote); + // User hit Escape during the ~5s launch — the dialog already showed + // "cancelled" and unmounted, so skip onDone (would write to a dead + // transcript slot) and let the caller skip confirmOverage. + if (signal?.aborted) return; + if (result) { + onDone(contentBlocksToString(result), { + shouldQuery: true + }); + } else { + // Precondition failures now return specific ContentBlockParam[] above. + // null only reaches here on teleport failure (PR mode) or non-github + // repo — both are CCR/repo connectivity issues. + onDone('Ultrareview failed to launch the remote session. Check that this is a GitHub repo and try again.', { + display: 'system' + }); + } +} +export const call: LocalJSXCommandCall = async (onDone, context, args) => { + const gate = await checkOverageGate(); + if (gate.kind === 'not-enabled') { + onDone('Free ultrareviews used. Enable Extra Usage at https://claude.ai/settings/billing to continue.', { + display: 'system' + }); + return null; + } + if (gate.kind === 'low-balance') { + onDone(`Balance too low to launch ultrareview ($${gate.available.toFixed(2)} available, $10 minimum). Top up at https://claude.ai/settings/billing`, { + display: 'system' + }); + return null; + } + if (gate.kind === 'needs-confirm') { + return { + await launchAndDone(args, context, onDone, ' This review bills as Extra Usage.', signal); + // Only persist the confirmation flag after a non-aborted launch — + // otherwise Escape-during-launch would leave the flag set and + // skip this dialog on the next attempt. + if (!signal.aborted) confirmOverage(); + }} onCancel={() => onDone('Ultrareview cancelled.', { + display: 'system' + })} />; + } + + // gate.kind === 'proceed' + await launchAndDone(args, context, onDone, gate.billingNote); + return null; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJDb250ZW50QmxvY2tQYXJhbSIsIlJlYWN0IiwiTG9jYWxKU1hDb21tYW5kQ2FsbCIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImNoZWNrT3ZlcmFnZUdhdGUiLCJjb25maXJtT3ZlcmFnZSIsImxhdW5jaFJlbW90ZVJldmlldyIsIlVsdHJhcmV2aWV3T3ZlcmFnZURpYWxvZyIsImNvbnRlbnRCbG9ja3NUb1N0cmluZyIsImJsb2NrcyIsIm1hcCIsImIiLCJ0eXBlIiwidGV4dCIsImZpbHRlciIsIkJvb2xlYW4iLCJqb2luIiwibGF1bmNoQW5kRG9uZSIsImFyZ3MiLCJjb250ZXh0IiwiUGFyYW1ldGVycyIsIm9uRG9uZSIsImJpbGxpbmdOb3RlIiwic2lnbmFsIiwiQWJvcnRTaWduYWwiLCJQcm9taXNlIiwicmVzdWx0IiwiYWJvcnRlZCIsInNob3VsZFF1ZXJ5IiwiZGlzcGxheSIsImNhbGwiLCJnYXRlIiwia2luZCIsImF2YWlsYWJsZSIsInRvRml4ZWQiXSwic291cmNlcyI6WyJ1bHRyYXJldmlld0NvbW1hbmQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgQ29udGVudEJsb2NrUGFyYW0gfSBmcm9tICdAYW50aHJvcGljLWFpL3Nkay9yZXNvdXJjZXMvbWVzc2FnZXMuanMnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7XG4gIExvY2FsSlNYQ29tbWFuZENhbGwsXG4gIExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbn0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcbmltcG9ydCB7XG4gIGNoZWNrT3ZlcmFnZUdhdGUsXG4gIGNvbmZpcm1PdmVyYWdlLFxuICBsYXVuY2hSZW1vdGVSZXZpZXcsXG59IGZyb20gJy4vcmV2aWV3UmVtb3RlLmpzJ1xuaW1wb3J0IHsgVWx0cmFyZXZpZXdPdmVyYWdlRGlhbG9nIH0gZnJvbSAnLi9VbHRyYXJldmlld092ZXJhZ2VEaWFsb2cuanMnXG5cbmZ1bmN0aW9uIGNvbnRlbnRCbG9ja3NUb1N0cmluZyhibG9ja3M6IENvbnRlbnRCbG9ja1BhcmFtW10pOiBzdHJpbmcge1xuICByZXR1cm4gYmxvY2tzXG4gICAgLm1hcChiID0+IChiLnR5cGUgPT09ICd0ZXh0JyA/IGIudGV4dCA6ICcnKSlcbiAgICAuZmlsdGVyKEJvb2xlYW4pXG4gICAgLmpvaW4oJ1xcbicpXG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxhdW5jaEFuZERvbmUoXG4gIGFyZ3M6IHN0cmluZyxcbiAgY29udGV4dDogUGFyYW1ldGVyczxMb2NhbEpTWENvbW1hbmRDYWxsPlsxXSxcbiAgb25Eb25lOiBMb2NhbEpTWENvbW1hbmRPbkRvbmUsXG4gIGJpbGxpbmdOb3RlOiBzdHJpbmcsXG4gIHNpZ25hbD86IEFib3J0U2lnbmFsLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGxhdW5jaFJlbW90ZVJldmlldyhhcmdzLCBjb250ZXh0LCBiaWxsaW5nTm90ZSlcbiAgLy8gVXNlciBoaXQgRXNjYXBlIGR1cmluZyB0aGUgfjVzIGxhdW5jaCDigJQgdGhlIGRpYWxvZyBhbHJlYWR5IHNob3dlZFxuICAvLyBcImNhbmNlbGxlZFwiIGFuZCB1bm1vdW50ZWQsIHNvIHNraXAgb25Eb25lICh3b3VsZCB3cml0ZSB0byBhIGRlYWRcbiAgLy8gdHJhbnNjcmlwdCBzbG90KSBhbmQgbGV0IHRoZSBjYWxsZXIgc2tpcCBjb25maXJtT3ZlcmFnZS5cbiAgaWYgKHNpZ25hbD8uYWJvcnRlZCkgcmV0dXJuXG4gIGlmIChyZXN1bHQpIHtcbiAgICBvbkRvbmUoY29udGVudEJsb2Nrc1RvU3RyaW5nKHJlc3VsdCksIHsgc2hvdWxkUXVlcnk6IHRydWUgfSlcbiAgfSBlbHNlIHtcbiAgICAvLyBQcmVjb25kaXRpb24gZmFpbHVyZXMgbm93IHJldHVybiBzcGVjaWZpYyBDb250ZW50QmxvY2tQYXJhbVtdIGFib3ZlLlxuICAgIC8vIG51bGwgb25seSByZWFjaGVzIGhlcmUgb24gdGVsZXBvcnQgZmFpbHVyZSAoUFIgbW9kZSkgb3Igbm9uLWdpdGh1YlxuICAgIC8vIHJlcG8g4oCUIGJvdGggYXJlIENDUi9yZXBvIGNvbm5lY3Rpdml0eSBpc3N1ZXMuXG4gICAgb25Eb25lKFxuICAgICAgJ1VsdHJhcmV2aWV3IGZhaWxlZCB0byBsYXVuY2ggdGhlIHJlbW90ZSBzZXNzaW9uLiBDaGVjayB0aGF0IHRoaXMgaXMgYSBHaXRIdWIgcmVwbyBhbmQgdHJ5IGFnYWluLicsXG4gICAgICB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0sXG4gICAgKVxuICB9XG59XG5cbmV4cG9ydCBjb25zdCBjYWxsOiBMb2NhbEpTWENvbW1hbmRDYWxsID0gYXN5bmMgKG9uRG9uZSwgY29udGV4dCwgYXJncykgPT4ge1xuICBjb25zdCBnYXRlID0gYXdhaXQgY2hlY2tPdmVyYWdlR2F0ZSgpXG5cbiAgaWYgKGdhdGUua2luZCA9PT0gJ25vdC1lbmFibGVkJykge1xuICAgIG9uRG9uZShcbiAgICAgICdGcmVlIHVsdHJhcmV2aWV3cyB1c2VkLiBFbmFibGUgRXh0cmEgVXNhZ2UgYXQgaHR0cHM6Ly9jbGF1ZGUuYWkvc2V0dGluZ3MvYmlsbGluZyB0byBjb250aW51ZS4nLFxuICAgICAgeyBkaXNwbGF5OiAnc3lzdGVtJyB9LFxuICAgIClcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKGdhdGUua2luZCA9PT0gJ2xvdy1iYWxhbmNlJykge1xuICAgIG9uRG9uZShcbiAgICAgIGBCYWxhbmNlIHRvbyBsb3cgdG8gbGF1bmNoIHVsdHJhcmV2aWV3ICgkJHtnYXRlLmF2YWlsYWJsZS50b0ZpeGVkKDIpfSBhdmFpbGFibGUsICQxMCBtaW5pbXVtKS4gVG9wIHVwIGF0IGh0dHBzOi8vY2xhdWRlLmFpL3NldHRpbmdzL2JpbGxpbmdgLFxuICAgICAgeyBkaXNwbGF5OiAnc3lzdGVtJyB9LFxuICAgIClcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKGdhdGUua2luZCA9PT0gJ25lZWRzLWNvbmZpcm0nKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxVbHRyYXJldmlld092ZXJhZ2VEaWFsb2dcbiAgICAgICAgb25Qcm9jZWVkPXthc3luYyBzaWduYWwgPT4ge1xuICAgICAgICAgIGF3YWl0IGxhdW5jaEFuZERvbmUoXG4gICAgICAgICAgICBhcmdzLFxuICAgICAgICAgICAgY29udGV4dCxcbiAgICAgICAgICAgIG9uRG9uZSxcbiAgICAgICAgICAgICcgVGhpcyByZXZpZXcgYmlsbHMgYXMgRXh0cmEgVXNhZ2UuJyxcbiAgICAgICAgICAgIHNpZ25hbCxcbiAgICAgICAgICApXG4gICAgICAgICAgLy8gT25seSBwZXJzaXN0IHRoZSBjb25maXJtYXRpb24gZmxhZyBhZnRlciBhIG5vbi1hYm9ydGVkIGxhdW5jaCDigJRcbiAgICAgICAgICAvLyBvdGhlcndpc2UgRXNjYXBlLWR1cmluZy1sYXVuY2ggd291bGQgbGVhdmUgdGhlIGZsYWcgc2V0IGFuZFxuICAgICAgICAgIC8vIHNraXAgdGhpcyBkaWFsb2cgb24gdGhlIG5leHQgYXR0ZW1wdC5cbiAgICAgICAgICBpZiAoIXNpZ25hbC5hYm9ydGVkKSBjb25maXJtT3ZlcmFnZSgpXG4gICAgICAgIH19XG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBvbkRvbmUoJ1VsdHJhcmV2aWV3IGNhbmNlbGxlZC4nLCB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0pfVxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICAvLyBnYXRlLmtpbmQgPT09ICdwcm9jZWVkJ1xuICBhd2FpdCBsYXVuY2hBbmREb25lKGFyZ3MsIGNvbnRleHQsIG9uRG9uZSwgZ2F0ZS5iaWxsaW5nTm90ZSlcbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsY0FBY0EsaUJBQWlCLFFBQVEseUNBQXlDO0FBQ2hGLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLGNBQ0VDLG1CQUFtQixFQUNuQkMscUJBQXFCLFFBQ2hCLHdCQUF3QjtBQUMvQixTQUNFQyxnQkFBZ0IsRUFDaEJDLGNBQWMsRUFDZEMsa0JB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 \ No newline at end of file diff --git a/commands/review/ultrareviewEnabled.ts b/commands/review/ultrareviewEnabled.ts new file mode 100644 index 0000000..d10e5f5 --- /dev/null +++ b/commands/review/ultrareviewEnabled.ts @@ -0,0 +1,14 @@ +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js' + +/** + * Runtime gate for /ultrareview. GB config's `enabled` field controls + * visibility — isEnabled() on the command filters it from getCommands() + * when false, so ungated users don't see the command at all. + */ +export function isUltrareviewEnabled(): boolean { + const cfg = getFeatureValue_CACHED_MAY_BE_STALE | null>('tengu_review_bughunter_config', null) + return cfg?.enabled === true +} diff --git a/commands/rewind/index.ts b/commands/rewind/index.ts new file mode 100644 index 0000000..cfce193 --- /dev/null +++ b/commands/rewind/index.ts @@ -0,0 +1,13 @@ +import type { Command } from '../../commands.js' + +const rewind = { + description: `Restore the code and/or conversation to a previous point`, + name: 'rewind', + aliases: ['checkpoint'], + argumentHint: '', + type: 'local', + supportsNonInteractive: false, + load: () => import('./rewind.js'), +} satisfies Command + +export default rewind diff --git a/commands/rewind/rewind.ts b/commands/rewind/rewind.ts new file mode 100644 index 0000000..4b48a99 --- /dev/null +++ b/commands/rewind/rewind.ts @@ -0,0 +1,13 @@ +import type { LocalCommandResult } from '../../commands.js' +import type { ToolUseContext } from '../../Tool.js' + +export async function call( + _args: string, + context: ToolUseContext, +): Promise { + if (context.openMessageSelector) { + context.openMessageSelector() + } + // Return a skip message to not append any messages. + return { type: 'skip' } +} diff --git a/commands/sandbox-toggle/index.ts b/commands/sandbox-toggle/index.ts new file mode 100644 index 0000000..f467394 --- /dev/null +++ b/commands/sandbox-toggle/index.ts @@ -0,0 +1,50 @@ +import figures from 'figures' +import type { Command } from '../../commands.js' +import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js' + +const command = { + name: 'sandbox', + get description() { + const currentlyEnabled = SandboxManager.isSandboxingEnabled() + const autoAllow = SandboxManager.isAutoAllowBashIfSandboxedEnabled() + const allowUnsandboxed = SandboxManager.areUnsandboxedCommandsAllowed() + const isLocked = SandboxManager.areSandboxSettingsLockedByPolicy() + const hasDeps = SandboxManager.checkDependencies().errors.length === 0 + + // Show warning icon if dependencies missing, otherwise enabled/disabled status + let icon: string + if (!hasDeps) { + icon = figures.warning + } else { + icon = currentlyEnabled ? figures.tick : figures.circle + } + + let statusText = 'sandbox disabled' + if (currentlyEnabled) { + statusText = autoAllow + ? 'sandbox enabled (auto-allow)' + : 'sandbox enabled' + + // Add unsandboxed fallback status + statusText += allowUnsandboxed ? ', fallback allowed' : '' + } + + if (isLocked) { + statusText += ' (managed)' + } + + return `${icon} ${statusText} (⏎ to configure)` + }, + argumentHint: 'exclude "command pattern"', + get isHidden() { + return ( + !SandboxManager.isSupportedPlatform() || + !SandboxManager.isPlatformInEnabledList() + ) + }, + immediate: true, + type: 'local-jsx', + load: () => import('./sandbox-toggle.js'), +} satisfies Command + +export default command diff --git a/commands/sandbox-toggle/sandbox-toggle.tsx b/commands/sandbox-toggle/sandbox-toggle.tsx new file mode 100644 index 0000000..f56503c --- /dev/null +++ b/commands/sandbox-toggle/sandbox-toggle.tsx @@ -0,0 +1,83 @@ +import { relative } from 'path'; +import React from 'react'; +import { getCwdState } from '../../bootstrap/state.js'; +import { SandboxSettings } from '../../components/sandbox/SandboxSettings.js'; +import { color } from '../../ink.js'; +import { getPlatform } from '../../utils/platform.js'; +import { addToExcludedCommands, SandboxManager } from '../../utils/sandbox/sandbox-adapter.js'; +import { getSettings_DEPRECATED, getSettingsFilePathForSource } from '../../utils/settings/settings.js'; +import type { ThemeName } from '../../utils/theme.js'; +export async function call(onDone: (result?: string) => void, _context: unknown, args?: string): Promise { + const settings = getSettings_DEPRECATED(); + const themeName: ThemeName = settings.theme as ThemeName || 'light'; + const platform = getPlatform(); + if (!SandboxManager.isSupportedPlatform()) { + // WSL1 users will see this since isSupportedPlatform returns false for WSL1 + const errorMessage = platform === 'wsl' ? 'Error: Sandboxing requires WSL2. WSL1 is not supported.' : 'Error: Sandboxing is currently only supported on macOS, Linux, and WSL2.'; + const message = color('error', themeName)(errorMessage); + onDone(message); + return null; + } + + // Check dependencies - get structured result with errors/warnings + const depCheck = SandboxManager.checkDependencies(); + + // Check if platform is in enabledPlatforms list (undocumented enterprise setting) + if (!SandboxManager.isPlatformInEnabledList()) { + const message = color('error', themeName)(`Error: Sandboxing is disabled for this platform (${platform}) via the enabledPlatforms setting.`); + onDone(message); + return null; + } + + // Check if sandbox settings are locked by higher-priority settings + if (SandboxManager.areSandboxSettingsLockedByPolicy()) { + const message = color('error', themeName)('Error: Sandbox settings are overridden by a higher-priority configuration and cannot be changed locally.'); + onDone(message); + return null; + } + + // Parse the arguments + const trimmedArgs = args?.trim() || ''; + + // If no args, show the interactive menu + if (!trimmedArgs) { + return ; + } + + // Handle subcommands + if (trimmedArgs) { + const parts = trimmedArgs.split(' '); + const subcommand = parts[0]; + if (subcommand === 'exclude') { + // Handle exclude subcommand + const commandPattern = trimmedArgs.slice('exclude '.length).trim(); + if (!commandPattern) { + const message = color('error', themeName)('Error: Please provide a command pattern to exclude (e.g., /sandbox exclude "npm run test:*")'); + onDone(message); + return null; + } + + // Remove quotes if present + const cleanPattern = commandPattern.replace(/^["']|["']$/g, ''); + + // Add to excludedCommands + addToExcludedCommands(cleanPattern); + + // Get the local settings path and make it relative to cwd + const localSettingsPath = getSettingsFilePathForSource('localSettings'); + const relativePath = localSettingsPath ? relative(getCwdState(), localSettingsPath) : '.claude/settings.local.json'; + const message = color('success', themeName)(`Added "${cleanPattern}" to excluded commands in ${relativePath}`); + onDone(message); + return null; + } else { + // Unknown subcommand + const message = color('error', themeName)(`Error: Unknown subcommand "${subcommand}". Available subcommand: exclude`); + onDone(message); + return null; + } + } + + // Should never reach here since we handle all cases above + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyZWxhdGl2ZSIsIlJlYWN0IiwiZ2V0Q3dkU3RhdGUiLCJTYW5kYm94U2V0dGluZ3MiLCJjb2xvciIsImdldFBsYXRmb3JtIiwiYWRkVG9FeGNsdWRlZENvbW1hbmRzIiwiU2FuZGJveE1hbmFnZXIiLCJnZXRTZXR0aW5nc19ERVBSRUNBVEVEIiwiZ2V0U2V0dGluZ3NGaWxlUGF0aEZvclNvdXJjZSIsIlRoZW1lTmFtZSIsImNhbGwiLCJvbkRvbmUiLCJyZXN1bHQiLCJfY29udGV4dCIsImFyZ3MiLCJQcm9taXNlIiwiUmVhY3ROb2RlIiwic2V0dGluZ3MiLCJ0aGVtZU5hbWUiLCJ0aGVtZSIsInBsYXRmb3JtIiwiaXNTdXBwb3J0ZWRQbGF0Zm9ybSIsImVycm9yTWVzc2FnZSIsIm1lc3NhZ2UiLCJkZXBDaGVjayIsImNoZWNrRGVwZW5kZW5jaWVzIiwiaXNQbGF0Zm9ybUluRW5hYmxlZExpc3QiLCJhcmVTYW5kYm94U2V0dGluZ3NMb2NrZWRCeVBvbGljeSIsInRyaW1tZWRBcmdzIiwidHJpbSIsInBhcnRzIiwic3BsaXQiLCJzdWJjb21tYW5kIiwiY29tbWFuZFBhdHRlcm4iLCJzbGljZSIsImxlbmd0aCIsImNsZWFuUGF0dGVybiIsInJlcGxhY2UiLCJsb2NhbFNldHRpbmdzUGF0aCIsInJlbGF0aXZlUGF0aCJdLCJzb3VyY2VzIjpbInNhbmRib3gtdG9nZ2xlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyByZWxhdGl2ZSB9IGZyb20gJ3BhdGgnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBnZXRDd2RTdGF0ZSB9IGZyb20gJy4uLy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7IFNhbmRib3hTZXR0aW5ncyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvc2FuZGJveC9TYW5kYm94U2V0dGluZ3MuanMnXG5pbXBvcnQgeyBjb2xvciB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGdldFBsYXRmb3JtIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGxhdGZvcm0uanMnXG5pbXBvcnQge1xuICBhZGRUb0V4Y2x1ZGVkQ29tbWFuZHMsXG4gIFNhbmRib3hNYW5hZ2VyLFxufSBmcm9tICcuLi8uLi91dGlscy9zYW5kYm94L3NhbmRib3gtYWRhcHRlci5qcydcbmltcG9ydCB7XG4gIGdldFNldHRpbmdzX0RFUFJFQ0FURUQsXG4gIGdldFNldHRpbmdzRmlsZVBhdGhGb3JTb3VyY2UsXG59IGZyb20gJy4uLy4uL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZU5hbWUgfSBmcm9tICcuLi8uLi91dGlscy90aGVtZS5qcydcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogKHJlc3VsdD86IHN0cmluZykgPT4gdm9pZCxcbiAgX2NvbnRleHQ6IHVua25vd24sXG4gIGFyZ3M/OiBzdHJpbmcsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZSB8IG51bGw+IHtcbiAgY29uc3Qgc2V0dGluZ3MgPSBnZXRTZXR0aW5nc19ERVBSRUNBVEVEKClcbiAgY29uc3QgdGhlbWVOYW1lOiBUaGVtZU5hbWUgPSAoc2V0dGluZ3MudGhlbWUgYXMgVGhlbWVOYW1lKSB8fCAnbGlnaHQnXG5cbiAgY29uc3QgcGxhdGZvcm0gPSBnZXRQbGF0Zm9ybSgpXG5cbiAgaWYgKCFTYW5kYm94TWFuYWdlci5pc1N1cHBvcnRlZFBsYXRmb3JtKCkpIHtcbiAgICAvLyBXU0wxIHVzZXJzIHdpbGwgc2VlIHRoaXMgc2luY2UgaXNTdXBwb3J0ZWRQbGF0Zm9ybSByZXR1cm5zIGZhbHNlIGZvciBXU0wxXG4gICAgY29uc3QgZXJyb3JNZXNzYWdlID1cbiAgICAgIHBsYXRmb3JtID09PSAnd3NsJ1xuICAgICAgICA/ICdFcnJvcjogU2FuZGJveGluZyByZXF1aXJlcyBXU0wyLiBXU0wxIGlzIG5vdCBzdXBwb3J0ZWQuJ1xuICAgICAgICA6ICdFcnJvcjogU2FuZGJveGluZyBpcyBjdXJyZW50bHkgb25seSBzdXBwb3J0ZWQgb24gbWFjT1MsIExpbnV4LCBhbmQgV1NMMi4nXG4gICAgY29uc3QgbWVzc2FnZSA9IGNvbG9yKCdlcnJvcicsIHRoZW1lTmFtZSkoZXJyb3JNZXNzYWdlKVxuICAgIG9uRG9uZShtZXNzYWdlKVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBDaGVjayBkZXBlbmRlbmNpZXMgLSBnZXQgc3RydWN0dXJlZCByZXN1bHQgd2l0aCBlcnJvcnMvd2FybmluZ3NcbiAgY29uc3QgZGVwQ2hlY2sgPSBTYW5kYm94TWFuYWdlci5jaGVja0RlcGVuZGVuY2llcygpXG5cbiAgLy8gQ2hlY2sgaWYgcGxhdGZvcm0gaXMgaW4gZW5hYmxlZFBsYXRmb3JtcyBsaXN0ICh1bmRvY3VtZW50ZWQgZW50ZXJwcmlzZSBzZXR0aW5nKVxuICBpZiAoIVNhbmRib3hNYW5hZ2VyLmlzUGxhdGZvcm1JbkVuYWJsZWRMaXN0KCkpIHtcbiAgICBjb25zdCBtZXNzYWdlID0gY29sb3IoXG4gICAgICAnZXJyb3InLFxuICAgICAgdGhlbWVOYW1lLFxuICAgICkoXG4gICAgICBgRXJyb3I6IFNhbmRib3hpbmcgaXMgZGlzYWJsZWQgZm9yIHRoaXMgcGxhdGZvcm0gKCR7cGxhdGZvcm19KSB2aWEgdGhlIGVuYWJsZWRQbGF0Zm9ybXMgc2V0dGluZy5gLFxuICAgIClcbiAgICBvbkRvbmUobWVzc2FnZSlcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gQ2hlY2sgaWYgc2FuZGJveCBzZXR0aW5ncyBhcmUgbG9ja2VkIGJ5IGhpZ2hlci1wcmlvcml0eSBzZXR0aW5nc1xuICBpZiAoU2FuZGJveE1hbmFnZXIuYXJlU2FuZGJveFNldHRpbmdzTG9ja2VkQnlQb2xpY3koKSkge1xuICAgIGNvbnN0IG1lc3NhZ2UgPSBjb2xvcihcbiAgICAgICdlcnJvcicsXG4gICAgICB0aGVtZU5hbWUsXG4gICAgKShcbiAgICAgICdFcnJvcjogU2FuZGJveCBzZXR0aW5ncyBhcmUgb3ZlcnJpZGRlbiBieSBhIGhpZ2hlci1wcmlvcml0eSBjb25maWd1cmF0aW9uIGFuZCBjYW5ub3QgYmUgY2hhbmdlZCBsb2NhbGx5LicsXG4gICAgKVxuICAgIG9uRG9uZShtZXNzYWdlKVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBQYXJzZSB0aGUgYXJndW1lbnRzXG4gIGNvbnN0IHRyaW1tZWRBcmdzID0gYXJncz8udHJpbSgpIHx8ICcnXG5cbiAgLy8gSWYgbm8gYXJncywgc2hvdyB0aGUgaW50ZXJhY3RpdmUgbWVudVxuICBpZiAoIXRyaW1tZWRBcmdzKSB7XG4gICAgcmV0dXJuIDxTYW5kYm94U2V0dGluZ3Mgb25Db21wbGV0ZT17b25Eb25lfSBkZXBDaGVjaz17ZGVwQ2hlY2t9IC8+XG4gIH1cblxuICAvLyBIYW5kbGUgc3ViY29tbWFuZHNcbiAgaWYgKHRyaW1tZWRBcmdzKSB7XG4gICAgY29uc3QgcGFydHMgPSB0cmltbWVkQXJncy5zcGxpdCgnICcpXG4gICAgY29uc3Qgc3ViY29tbWFuZCA9IHBhcnRzWzBdXG5cbiAgICBpZiAoc3ViY29tbWFuZCA9PT0gJ2V4Y2x1ZGUnKSB7XG4gICAgICAvLyBIYW5kbGUgZXhjbHVkZSBzdWJjb21tYW5kXG4gICAgICBjb25zdCBjb21tYW5kUGF0dGVybiA9IHRyaW1tZWRBcmdzLnNsaWNlKCdleGNsdWRlICcubGVuZ3RoKS50cmltKClcblxuICAgICAgaWYgKCFjb21tYW5kUGF0dGVybikge1xuICAgICAgICBjb25zdCBtZXNzYWdlID0gY29sb3IoXG4gICAgICAgICAgJ2Vycm9yJyxcbiAgICAgICAgICB0aGVtZU5hbWUsXG4gICAgICAgICkoXG4gICAgICAgICAgJ0Vycm9yOiBQbGVhc2UgcHJvdmlkZSBhIGNvbW1hbmQgcGF0dGVybiB0byBleGNsdWRlIChlLmcuLCAvc2FuZGJveCBleGNsdWRlIFwibnBtIHJ1biB0ZXN0OipcIiknLFxuICAgICAgICApXG4gICAgICAgIG9uRG9uZShtZXNzYWdlKVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgfVxuXG4gICAgICAvLyBSZW1vdmUgcXVvdGVzIGlmIHByZXNlbnRcbiAgICAgIGNvbnN0IGNsZWFuUGF0dGVybiA9IGNvbW1hbmRQYXR0ZXJuLnJlcGxhY2UoL15bXCInXXxbXCInXSQvZywgJycpXG5cbiAgICAgIC8vIEFkZCB0byBleGNsdWRlZENvbW1hbmRzXG4gICAgICBhZGRUb0V4Y2x1ZGVkQ29tbWFuZHMoY2xlYW5QYXR0ZXJuKVxuXG4gICAgICAvLyBHZXQgdGhlIGxvY2FsIHNldHRpbmdzIHBhdGggYW5kIG1ha2UgaXQgcmVsYXRpdmUgdG8gY3dkXG4gICAgICBjb25zdCBsb2NhbFNldHRpbmdzUGF0aCA9IGdldFNldHRpbmdzRmlsZVBhdGhGb3JTb3VyY2UoJ2xvY2FsU2V0dGluZ3MnKVxuICAgICAgY29uc3QgcmVsYXRpdmVQYXRoID0gbG9jYWxTZXR0aW5nc1BhdGhcbiAgICAgICAgPyByZWxhdGl2ZShnZXRDd2RTdGF0ZSgpLCBsb2NhbFNldHRpbmdzUGF0aClcbiAgICAgICAgOiAnLmNsYXVkZS9zZXR0aW5ncy5sb2NhbC5qc29uJ1xuXG4gICAgICBjb25zdCBtZXNzYWdlID0gY29sb3IoXG4gICAgICAgICdzdWNjZXNzJyxcbiAgICAgICAgdGhlbWVOYW1lLFxuICAgICAgKShgQWRkZWQgXCIke2NsZWFuUGF0dGVybn1cIiB0byBleGNsdWRlZCBjb21tYW5kcyBpbiAke3JlbGF0aXZlUGF0aH1gKVxuXG4gICAgICBvbkRvbmUobWVzc2FnZSlcbiAgICAgIHJldHVybiBudWxsXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFVua25vd24gc3ViY29tbWFuZFxuICAgICAgY29uc3QgbWVzc2FnZSA9IGNvbG9yKFxuICAgICAgICAnZXJyb3InLFxuICAgICAgICB0aGVtZU5hbWUsXG4gICAgICApKFxuICAgICAgICBgRXJyb3I6IFVua25vd24gc3ViY29tbWFuZCBcIiR7c3ViY29tbWFuZH1cIi4gQXZhaWxhYmxlIHN1YmNvbW1hbmQ6IGV4Y2x1ZGVgLFxuICAgICAgKVxuICAgICAgb25Eb25lKG1lc3NhZ2UpXG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cbiAgfVxuXG4gIC8vIFNob3VsZCBuZXZlciByZWFjaCBoZXJlIHNpbmNlIHdlIGhhbmRsZSBhbGwgY2FzZXMgYWJvdmVcbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsUUFBUSxRQUFRLE1BQU07QUFDL0IsT0FBT0MsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsV0FBVyxRQUFRLDBCQUEwQjtBQUN0RCxTQUFTQyxlQUFlLFFBQVEsNkNBQTZDO0FBQzdFLFNBQVNDLEtBQUssUUFBUSxjQUFjO0FBQ3BDLFNBQVNDLFdBQVcsUUFBUSx5QkFBeUI7QUFDckQsU0FDRUMscUJBQXFCLEVBQ3JCQyxjQUFjLFFBQ1Qsd0NBQXdDO0FBQy9DLFNBQ0VDLHNCQUFzQixFQUN0QkMsNEJBQTRCLFFBQ3ZCLGtDQUFrQztBQUN6QyxjQUFjQyxTQUFTLFFBQVEsc0JBQXNCO0FBRXJELE9BQU8sZUFBZUMsSUFBSUEsQ0FDeEJDLE1BQU0sRUFBRSxDQUFDQyxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJLEVBQ2pDQyxRQUFRLEVBQUUsT0FBTyxFQUNqQkMsSUFBYSxDQUFSLEVBQUUsTUFBTSxDQUNkLEVBQUVDLE9BQU8sQ0FBQ2YsS0FBSyxDQUFDZ0IsU0FBUyxHQUFHLElBQUksQ0FBQyxDQUFDO0VBQ2pDLE1BQU1DLFFBQVEsR0FBR1Ysc0JBQXNCLENBQUMsQ0FBQztFQUN6QyxNQUFNVyxTQUFTLEVBQUVULFNBQVMsR0FBSVEsUUFBUSxDQUFDRSxLQUFLLElBQUlWLFNBQVMsSUFBSyxPQUFPO0VBRXJFLE1BQU1XLFFBQVEsR0FBR2hCLFdBQVcsQ0FBQyxDQUFDO0VBRTlCLElBQUksQ0FBQ0UsY0FBYyxDQUFDZSxtQkFBbUIsQ0FBQyxDQUFDLEVBQUU7SUFDekM7SUFDQSxNQUFNQyxZQUFZLEdBQ2hCRixRQUFRLEtBQUssS0FBSyxHQUNkLHlEQUF5RCxHQUN6RCwwRUFBMEU7SUFDaEYsTUFBTUcsT0FBTyxHQUFHcEIs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 \ No newline at end of file diff --git a/commands/security-review.ts b/commands/security-review.ts new file mode 100644 index 0000000..03f7057 --- /dev/null +++ b/commands/security-review.ts @@ -0,0 +1,243 @@ +import { parseFrontmatter } from '../utils/frontmatterParser.js' +import { parseSlashCommandToolsFromFrontmatter } from '../utils/markdownConfigLoader.js' +import { executeShellCommandsInPrompt } from '../utils/promptShellExecution.js' +import { createMovedToPluginCommand } from './createMovedToPluginCommand.js' + +const SECURITY_REVIEW_MARKDOWN = `--- +allowed-tools: Bash(git diff:*), Bash(git status:*), Bash(git log:*), Bash(git show:*), Bash(git remote show:*), Read, Glob, Grep, LS, Task +description: Complete a security review of the pending changes on the current branch +--- + +You are a senior security engineer conducting a focused security review of the changes on this branch. + +GIT STATUS: + +\`\`\` +!\`git status\` +\`\`\` + +FILES MODIFIED: + +\`\`\` +!\`git diff --name-only origin/HEAD...\` +\`\`\` + +COMMITS: + +\`\`\` +!\`git log --no-decorate origin/HEAD...\` +\`\`\` + +DIFF CONTENT: + +\`\`\` +!\`git diff origin/HEAD...\` +\`\`\` + +Review the complete diff above. This contains all code changes in the PR. + + +OBJECTIVE: +Perform a security-focused code review to identify HIGH-CONFIDENCE security vulnerabilities that could have real exploitation potential. This is not a general code review - focus ONLY on security implications newly added by this PR. Do not comment on existing security concerns. + +CRITICAL INSTRUCTIONS: +1. MINIMIZE FALSE POSITIVES: Only flag issues where you're >80% confident of actual exploitability +2. AVOID NOISE: Skip theoretical issues, style concerns, or low-impact findings +3. FOCUS ON IMPACT: Prioritize vulnerabilities that could lead to unauthorized access, data breaches, or system compromise +4. EXCLUSIONS: Do NOT report the following issue types: + - Denial of Service (DOS) vulnerabilities, even if they allow service disruption + - Secrets or sensitive data stored on disk (these are handled by other processes) + - Rate limiting or resource exhaustion issues + +SECURITY CATEGORIES TO EXAMINE: + +**Input Validation Vulnerabilities:** +- SQL injection via unsanitized user input +- Command injection in system calls or subprocesses +- XXE injection in XML parsing +- Template injection in templating engines +- NoSQL injection in database queries +- Path traversal in file operations + +**Authentication & Authorization Issues:** +- Authentication bypass logic +- Privilege escalation paths +- Session management flaws +- JWT token vulnerabilities +- Authorization logic bypasses + +**Crypto & Secrets Management:** +- Hardcoded API keys, passwords, or tokens +- Weak cryptographic algorithms or implementations +- Improper key storage or management +- Cryptographic randomness issues +- Certificate validation bypasses + +**Injection & Code Execution:** +- Remote code execution via deseralization +- Pickle injection in Python +- YAML deserialization vulnerabilities +- Eval injection in dynamic code execution +- XSS vulnerabilities in web applications (reflected, stored, DOM-based) + +**Data Exposure:** +- Sensitive data logging or storage +- PII handling violations +- API endpoint data leakage +- Debug information exposure + +Additional notes: +- Even if something is only exploitable from the local network, it can still be a HIGH severity issue + +ANALYSIS METHODOLOGY: + +Phase 1 - Repository Context Research (Use file search tools): +- Identify existing security frameworks and libraries in use +- Look for established secure coding patterns in the codebase +- Examine existing sanitization and validation patterns +- Understand the project's security model and threat model + +Phase 2 - Comparative Analysis: +- Compare new code changes against existing security patterns +- Identify deviations from established secure practices +- Look for inconsistent security implementations +- Flag code that introduces new attack surfaces + +Phase 3 - Vulnerability Assessment: +- Examine each modified file for security implications +- Trace data flow from user inputs to sensitive operations +- Look for privilege boundaries being crossed unsafely +- Identify injection points and unsafe deserialization + +REQUIRED OUTPUT FORMAT: + +You MUST output your findings in markdown. The markdown output should contain the file, line number, severity, category (e.g. \`sql_injection\` or \`xss\`), description, exploit scenario, and fix recommendation. + +For example: + +# Vuln 1: XSS: \`foo.py:42\` + +* Severity: High +* Description: User input from \`username\` parameter is directly interpolated into HTML without escaping, allowing reflected XSS attacks +* Exploit Scenario: Attacker crafts URL like /bar?q= to execute JavaScript in victim's browser, enabling session hijacking or data theft +* Recommendation: Use Flask's escape() function or Jinja2 templates with auto-escaping enabled for all user inputs rendered in HTML + +SEVERITY GUIDELINES: +- **HIGH**: Directly exploitable vulnerabilities leading to RCE, data breach, or authentication bypass +- **MEDIUM**: Vulnerabilities requiring specific conditions but with significant impact +- **LOW**: Defense-in-depth issues or lower-impact vulnerabilities + +CONFIDENCE SCORING: +- 0.9-1.0: Certain exploit path identified, tested if possible +- 0.8-0.9: Clear vulnerability pattern with known exploitation methods +- 0.7-0.8: Suspicious pattern requiring specific conditions to exploit +- Below 0.7: Don't report (too speculative) + +FINAL REMINDER: +Focus on HIGH and MEDIUM findings only. Better to miss some theoretical issues than flood the report with false positives. Each finding should be something a security engineer would confidently raise in a PR review. + +FALSE POSITIVE FILTERING: + +> You do not need to run commands to reproduce the vulnerability, just read the code to determine if it is a real vulnerability. Do not use the bash tool or write to any files. +> +> HARD EXCLUSIONS - Automatically exclude findings matching these patterns: +> 1. Denial of Service (DOS) vulnerabilities or resource exhaustion attacks. +> 2. Secrets or credentials stored on disk if they are otherwise secured. +> 3. Rate limiting concerns or service overload scenarios. +> 4. Memory consumption or CPU exhaustion issues. +> 5. Lack of input validation on non-security-critical fields without proven security impact. +> 6. Input sanitization concerns for GitHub Action workflows unless they are clearly triggerable via untrusted input. +> 7. A lack of hardening measures. Code is not expected to implement all security best practices, only flag concrete vulnerabilities. +> 8. Race conditions or timing attacks that are theoretical rather than practical issues. Only report a race condition if it is concretely problematic. +> 9. Vulnerabilities related to outdated third-party libraries. These are managed separately and should not be reported here. +> 10. Memory safety issues such as buffer overflows or use-after-free-vulnerabilities are impossible in rust. Do not report memory safety issues in rust or any other memory safe languages. +> 11. Files that are only unit tests or only used as part of running tests. +> 12. Log spoofing concerns. Outputting un-sanitized user input to logs is not a vulnerability. +> 13. SSRF vulnerabilities that only control the path. SSRF is only a concern if it can control the host or protocol. +> 14. Including user-controlled content in AI system prompts is not a vulnerability. +> 15. Regex injection. Injecting untrusted content into a regex is not a vulnerability. +> 16. Regex DOS concerns. +> 16. Insecure documentation. Do not report any findings in documentation files such as markdown files. +> 17. A lack of audit logs is not a vulnerability. +> +> PRECEDENTS - +> 1. Logging high value secrets in plaintext is a vulnerability. Logging URLs is assumed to be safe. +> 2. UUIDs can be assumed to be unguessable and do not need to be validated. +> 3. Environment variables and CLI flags are trusted values. Attackers are generally not able to modify them in a secure environment. Any attack that relies on controlling an environment variable is invalid. +> 4. Resource management issues such as memory or file descriptor leaks are not valid. +> 5. Subtle or low impact web vulnerabilities such as tabnabbing, XS-Leaks, prototype pollution, and open redirects should not be reported unless they are extremely high confidence. +> 6. React and Angular are generally secure against XSS. These frameworks do not need to sanitize or escape user input unless it is using dangerouslySetInnerHTML, bypassSecurityTrustHtml, or similar methods. Do not report XSS vulnerabilities in React or Angular components or tsx files unless they are using unsafe methods. +> 7. Most vulnerabilities in github action workflows are not exploitable in practice. Before validating a github action workflow vulnerability ensure it is concrete and has a very specific attack path. +> 8. A lack of permission checking or authentication in client-side JS/TS code is not a vulnerability. Client-side code is not trusted and does not need to implement these checks, they are handled on the server-side. The same applies to all flows that send untrusted data to the backend, the backend is responsible for validating and sanitizing all inputs. +> 9. Only include MEDIUM findings if they are obvious and concrete issues. +> 10. Most vulnerabilities in ipython notebooks (*.ipynb files) are not exploitable in practice. Before validating a notebook vulnerability ensure it is concrete and has a very specific attack path where untrusted input can trigger the vulnerability. +> 11. Logging non-PII data is not a vulnerability even if the data may be sensitive. Only report logging vulnerabilities if they expose sensitive information such as secrets, passwords, or personally identifiable information (PII). +> 12. Command injection vulnerabilities in shell scripts are generally not exploitable in practice since shell scripts generally do not run with untrusted user input. Only report command injection vulnerabilities in shell scripts if they are concrete and have a very specific attack path for untrusted input. +> +> SIGNAL QUALITY CRITERIA - For remaining findings, assess: +> 1. Is there a concrete, exploitable vulnerability with a clear attack path? +> 2. Does this represent a real security risk vs theoretical best practice? +> 3. Are there specific code locations and reproduction steps? +> 4. Would this finding be actionable for a security team? +> +> For each finding, assign a confidence score from 1-10: +> - 1-3: Low confidence, likely false positive or noise +> - 4-6: Medium confidence, needs investigation +> - 7-10: High confidence, likely true vulnerability + +START ANALYSIS: + +Begin your analysis now. Do this in 3 steps: + +1. Use a sub-task to identify vulnerabilities. Use the repository exploration tools to understand the codebase context, then analyze the PR changes for security implications. In the prompt for this sub-task, include all of the above. +2. Then for each vulnerability identified by the above sub-task, create a new sub-task to filter out false-positives. Launch these sub-tasks as parallel sub-tasks. In the prompt for these sub-tasks, include everything in the "FALSE POSITIVE FILTERING" instructions. +3. Filter out any vulnerabilities where the sub-task reported a confidence less than 8. + +Your final reply must contain the markdown report and nothing else.` + +export default createMovedToPluginCommand({ + name: 'security-review', + description: + 'Complete a security review of the pending changes on the current branch', + progressMessage: 'analyzing code changes for security risks', + pluginName: 'security-review', + pluginCommand: 'security-review', + async getPromptWhileMarketplaceIsPrivate(_args, context) { + // Parse frontmatter from the markdown + const parsed = parseFrontmatter(SECURITY_REVIEW_MARKDOWN) + + // Parse allowed tools from frontmatter + const allowedTools = parseSlashCommandToolsFromFrontmatter( + parsed.frontmatter['allowed-tools'], + ) + + // Execute bash commands in the prompt + const processedContent = await executeShellCommandsInPrompt( + parsed.content, + { + ...context, + getAppState() { + const appState = context.getAppState() + return { + ...appState, + toolPermissionContext: { + ...appState.toolPermissionContext, + alwaysAllowRules: { + ...appState.toolPermissionContext.alwaysAllowRules, + command: allowedTools, + }, + }, + } + }, + }, + 'security-review', + ) + + return [ + { + type: 'text', + text: processedContent, + }, + ] + }, +}) diff --git a/commands/session/index.ts b/commands/session/index.ts new file mode 100644 index 0000000..c661878 --- /dev/null +++ b/commands/session/index.ts @@ -0,0 +1,16 @@ +import { getIsRemoteMode } from '../../bootstrap/state.js' +import type { Command } from '../../commands.js' + +const session = { + type: 'local-jsx', + name: 'session', + aliases: ['remote'], + description: 'Show remote session URL and QR code', + isEnabled: () => getIsRemoteMode(), + get isHidden() { + return !getIsRemoteMode() + }, + load: () => import('./session.js'), +} satisfies Command + +export default session diff --git a/commands/session/session.tsx b/commands/session/session.tsx new file mode 100644 index 0000000..f4f6083 --- /dev/null +++ b/commands/session/session.tsx @@ -0,0 +1,140 @@ +import { c as _c } from "react/compiler-runtime"; +import { toString as qrToString } from 'qrcode'; +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { Pane } from '../../components/design-system/Pane.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { useAppState } from '../../state/AppState.js'; +import type { LocalJSXCommandCall } from '../../types/command.js'; +import { logForDebugging } from '../../utils/debug.js'; +type Props = { + onDone: () => void; +}; +function SessionInfo(t0) { + const $ = _c(19); + const { + onDone + } = t0; + const remoteSessionUrl = useAppState(_temp); + const [qrCode, setQrCode] = useState(""); + let t1; + let t2; + if ($[0] !== remoteSessionUrl) { + t1 = () => { + if (!remoteSessionUrl) { + return; + } + const url = remoteSessionUrl; + const generateQRCode = async function generateQRCode() { + const qr = await qrToString(url, { + type: "utf8", + errorCorrectionLevel: "L" + }); + setQrCode(qr); + }; + generateQRCode().catch(_temp2); + }; + t2 = [remoteSessionUrl]; + $[0] = remoteSessionUrl; + $[1] = t1; + $[2] = t2; + } else { + t1 = $[1]; + t2 = $[2]; + } + useEffect(t1, t2); + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = { + context: "Confirmation" + }; + $[3] = t3; + } else { + t3 = $[3]; + } + useKeybinding("confirm:no", onDone, t3); + if (!remoteSessionUrl) { + let t4; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t4 = Not in remote mode. Start with `claude --remote` to use this command.(press esc to close); + $[4] = t4; + } else { + t4 = $[4]; + } + return t4; + } + let T0; + let t4; + let t5; + if ($[5] !== qrCode) { + const lines = qrCode.split("\n").filter(_temp3); + const isLoading = lines.length === 0; + T0 = Pane; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t4 = Remote session; + $[9] = t4; + } else { + t4 = $[9]; + } + t5 = isLoading ? Generating QR code… : lines.map(_temp4); + $[5] = qrCode; + $[6] = T0; + $[7] = t4; + $[8] = t5; + } else { + T0 = $[6]; + t4 = $[7]; + t5 = $[8]; + } + let t6; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t6 = Open in browser: ; + $[10] = t6; + } else { + t6 = $[10]; + } + let t7; + if ($[11] !== remoteSessionUrl) { + t7 = {t6}{remoteSessionUrl}; + $[11] = remoteSessionUrl; + $[12] = t7; + } else { + t7 = $[12]; + } + let t8; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t8 = (press esc to close); + $[13] = t8; + } else { + t8 = $[13]; + } + let t9; + if ($[14] !== T0 || $[15] !== t4 || $[16] !== t5 || $[17] !== t7) { + t9 = {t4}{t5}{t7}{t8}; + $[14] = T0; + $[15] = t4; + $[16] = t5; + $[17] = t7; + $[18] = t9; + } else { + t9 = $[18]; + } + return t9; +} +function _temp4(line_0, i) { + return {line_0}; +} +function _temp3(line) { + return line.length > 0; +} +function _temp2(e) { + logForDebugging("QR code generation failed", e); +} +function _temp(s) { + return s.remoteSessionUrl; +} +export const call: LocalJSXCommandCall = async onDone => { + return ; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ0b1N0cmluZyIsInFyVG9TdHJpbmciLCJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiUGFuZSIsIkJveCIsIlRleHQiLCJ1c2VLZXliaW5kaW5nIiwidXNlQXBwU3RhdGUiLCJMb2NhbEpTWENvbW1hbmRDYWxsIiwibG9nRm9yRGVidWdnaW5nIiwiUHJvcHMiLCJvbkRvbmUiLCJTZXNzaW9uSW5mbyIsInQwIiwiJCIsIl9jIiwicmVtb3RlU2Vzc2lvblVybCIsIl90ZW1wIiwicXJDb2RlIiwic2V0UXJDb2RlIiwidDEiLCJ0MiIsInVybCIsImdlbmVyYXRlUVJDb2RlIiwicXIiLCJ0eXBlIiwiZXJyb3JDb3JyZWN0aW9uTGV2ZWwiLCJjYXRjaCIsIl90ZW1wMiIsInQzIiwiU3ltYm9sIiwiZm9yIiwiY29udGV4dCIsInQ0IiwiVDAiLCJ0NSIsImxpbmVzIiwic3BsaXQiLCJmaWx0ZXIiLCJfdGVtcDMiLCJpc0xvYWRpbmciLCJsZW5ndGgiLCJtYXAiLCJfdGVtcDQiLCJ0NiIsInQ3IiwidDgiLCJ0OSIsImxpbmVfMCIsImkiLCJsaW5lIiwiZSIsInMiLCJjYWxsIl0sInNvdXJjZXMiOlsic2Vzc2lvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgdG9TdHJpbmcgYXMgcXJUb1N0cmluZyB9IGZyb20gJ3FyY29kZSdcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgUGFuZSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvZGVzaWduLXN5c3RlbS9QYW5lLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSB9IGZyb20gJy4uLy4uL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uLy4uL3V0aWxzL2RlYnVnLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBvbkRvbmU6ICgpID0+IHZvaWRcbn1cblxuZnVuY3Rpb24gU2Vzc2lvbkluZm8oeyBvbkRvbmUgfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCByZW1vdGVTZXNzaW9uVXJsID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlbW90ZVNlc3Npb25VcmwpXG4gIGNvbnN0IFtxckNvZGUsIHNldFFyQ29kZV0gPSB1c2VTdGF0ZTxzdHJpbmc+KCcnKVxuXG4gIC8vIEdlbmVyYXRlIFFSIGNvZGUgd2hlbiBVUkwgaXMgYXZhaWxhYmxlXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKCFyZW1vdGVTZXNzaW9uVXJsKSByZXR1cm5cblxuICAgIGNvbnN0IHVybCA9IHJlbW90ZVNlc3Npb25VcmxcbiAgICBhc3luYyBmdW5jdGlvbiBnZW5lcmF0ZVFSQ29kZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgIGNvbnN0IHFyID0gYXdhaXQgcXJUb1N0cmluZyh1cmwsIHtcbiAgICAgICAgdHlwZTogJ3V0ZjgnLFxuICAgICAgICBlcnJvckNvcnJlY3Rpb25MZXZlbDogJ0wnLFxuICAgICAgfSlcbiAgICAgIHNldFFyQ29kZShxcilcbiAgICB9XG4gICAgLy8gSW50ZW50aW9uYWxseSBzaWxlbnQgZmFpbCAtIFVSTCBpcyBzdGlsbCBzaG93biBzbyBRUiBpcyBub24tY3JpdGljYWxcbiAgICBnZW5lcmF0ZVFSQ29kZSgpLmNhdGNoKGUgPT4ge1xuICAgICAgbG9nRm9yRGVidWdnaW5nKCdRUiBjb2RlIGdlbmVyYXRpb24gZmFpbGVkJywgZSlcbiAgICB9KVxuICB9LCBbcmVtb3RlU2Vzc2lvblVybF0pXG5cbiAgLy8gSGFuZGxlIEVTQyB0byBkaXNtaXNzXG4gIHVzZUtleWJpbmRpbmcoJ2NvbmZpcm06bm8nLCBvbkRvbmUsIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicgfSlcblxuICAvLyBOb3QgaW4gcmVtb3RlIG1vZGVcbiAgaWYgKCFyZW1vdGVTZXNzaW9uVXJsKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxQYW5lPlxuICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj5cbiAgICAgICAgICBOb3QgaW4gcmVtb3RlIG1vZGUuIFN0YXJ0IHdpdGggYGNsYXVkZSAtLXJlbW90ZWAgdG8gdXNlIHRoaXMgY29tbWFuZC5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj4ocHJlc3MgZXNjIHRvIGNsb3NlKTwvVGV4dD5cbiAgICAgIDwvUGFuZT5cbiAgICApXG4gIH1cblxuICBjb25zdCBsaW5lcyA9IHFyQ29kZS5zcGxpdCgnXFxuJykuZmlsdGVyKGxpbmUgPT4gbGluZS5sZW5ndGggPiAwKVxuICBjb25zdCBpc0xvYWRpbmcgPSBsaW5lcy5sZW5ndGggPT09IDBcblxuICByZXR1cm4gKFxuICAgIDxQYW5lPlxuICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICA8VGV4dCBib2xkPlJlbW90ZSBzZXNzaW9uPC9UZXh0PlxuICAgICAgPC9Cb3g+XG5cbiAgICAgIHsvKiBRUiBDb2RlIC0gc2lsZW50bHkgZmFpbHMgaWYgZ2VuZXJhdGlvbiBlcnJvcnMsIFVSTCBpcyBzdGlsbCBzaG93biAqL31cbiAgICAgIHtpc0xvYWRpbmcgPyAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPkdlbmVyYXRpbmcgUVIgY29kZeKApjwvVGV4dD5cbiAgICAgICkgOiAoXG4gICAgICAgIGxpbmVzLm1hcCgobGluZSwgaSkgPT4gPFRleHQga2V5PXtpfT57bGluZX08L1RleHQ+KVxuICAgICAgKX1cblxuICAgICAgey8qIFVSTCAqL31cbiAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+T3BlbiBpbiBicm93c2VyOiA8L1RleHQ+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiaWRlXCI+e3JlbW90ZVNlc3Npb25Vcmx9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG5cbiAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+KHByZXNzIGVzYyB0byBjbG9zZSk8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L1BhbmU+XG4gIClcbn1cblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyBvbkRvbmUgPT4ge1xuICByZXR1cm4gPFNlc3Npb25JbmZvIG9uRG9uZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsUUFBUSxJQUFJQyxVQUFVLFFBQVEsUUFBUTtBQUMvQyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFNBQVMsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDM0MsU0FBU0MsSUFBSSxRQUFRLHdDQUF3QztBQUM3RCxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQVNDLGFBQWEsUUFBUSxvQ0FBb0M7QUFDbEUsU0FBU0MsV0FBVyxRQUFRLHlCQUF5QjtBQUNyRCxjQUFjQyxtQkFBbUIsUUFBUSx3QkFBd0I7QUFDakUsU0FBU0MsZUFBZSxRQUFRLHNCQUFzQjtBQUV0RCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsTUFBTSxFQUFFLEdBQUcsR0FBRyxJQUFJO0FBQ3BCLENBQUM7QUFFRCxTQUFBQyxZQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXFCO0lBQUFKO0VBQUEsSUFBQUUsRUFBaUI7RUFDcEMsTUFBQUcsZ0JBQUEsR0FBeUJULFdBQVcsQ0FBQ1UsS0FBdUIsQ0FBQztFQUM3RCxPQUFBQyxNQUFBLEVBQUFDLFNBQUEsSUFBNEJqQixRQUFRLENBQVMsRUFBRSxDQUFDO0VBQUEsSUFB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 \ No newline at end of file diff --git a/commands/share/index.js b/commands/share/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/share/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/skills/index.ts b/commands/skills/index.ts new file mode 100644 index 0000000..90e1d7f --- /dev/null +++ b/commands/skills/index.ts @@ -0,0 +1,10 @@ +import type { Command } from '../../commands.js' + +const skills = { + type: 'local-jsx', + name: 'skills', + description: 'List available skills', + load: () => import('./skills.js'), +} satisfies Command + +export default skills diff --git a/commands/skills/skills.tsx b/commands/skills/skills.tsx new file mode 100644 index 0000000..c9bf0e6 --- /dev/null +++ b/commands/skills/skills.tsx @@ -0,0 +1,8 @@ +import * as React from 'react'; +import type { LocalJSXCommandContext } from '../../commands.js'; +import { SkillsMenu } from '../../components/skills/SkillsMenu.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext): Promise { + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJTa2lsbHNNZW51IiwiTG9jYWxKU1hDb21tYW5kT25Eb25lIiwiY2FsbCIsIm9uRG9uZSIsImNvbnRleHQiLCJQcm9taXNlIiwiUmVhY3ROb2RlIiwib3B0aW9ucyIsImNvbW1hbmRzIl0sInNvdXJjZXMiOlsic2tpbGxzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kQ29udGV4dCB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgU2tpbGxzTWVudSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvc2tpbGxzL1NraWxsc01lbnUuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZE9uRG9uZSB9IGZyb20gJy4uLy4uL3R5cGVzL2NvbW1hbmQuanMnXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogTG9jYWxKU1hDb21tYW5kQ29udGV4dCxcbik6IFByb21pc2U8UmVhY3QuUmVhY3ROb2RlPiB7XG4gIHJldHVybiA8U2tpbGxzTWVudSBvbkV4aXQ9e29uRG9uZX0gY29tbWFuZHM9e2NvbnRleHQub3B0aW9ucy5jb21tYW5kc30gLz5cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixjQUFjQyxzQkFBc0IsUUFBUSxtQkFBbUI7QUFDL0QsU0FBU0MsVUFBVSxRQUFRLHVDQUF1QztBQUNsRSxjQUFjQyxxQkFBcUIsUUFBUSx3QkFBd0I7QUFFbkUsT0FBTyxlQUFlQyxJQUFJQSxDQUN4QkMsTUFBTSxFQUFFRixxQkFBcUIsRUFDN0JHLE9BQU8sRUFBRUwsc0JBQXNCLENBQ2hDLEVBQUVNLE9BQU8sQ0FBQ1AsS0FBSyxDQUFDUSxTQUFTLENBQUMsQ0FBQztFQUMxQixPQUFPLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxDQUFDSCxNQUFNLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQ0MsT0FBTyxDQUFDRyxPQUFPLENBQUNDLFFBQVEsQ0FBQyxHQUFHO0FBQzNFIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/stats/index.ts b/commands/stats/index.ts new file mode 100644 index 0000000..c9680d6 --- /dev/null +++ b/commands/stats/index.ts @@ -0,0 +1,10 @@ +import type { Command } from '../../commands.js' + +const stats = { + type: 'local-jsx', + name: 'stats', + description: 'Show your Claude Code usage statistics and activity', + load: () => import('./stats.js'), +} satisfies Command + +export default stats diff --git a/commands/stats/stats.tsx b/commands/stats/stats.tsx new file mode 100644 index 0000000..0e83433 --- /dev/null +++ b/commands/stats/stats.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; +import { Stats } from '../../components/Stats.js'; +import type { LocalJSXCommandCall } from '../../types/command.js'; +export const call: LocalJSXCommandCall = async onDone => { + return ; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlN0YXRzIiwiTG9jYWxKU1hDb21tYW5kQ2FsbCIsImNhbGwiLCJvbkRvbmUiXSwic291cmNlcyI6WyJzdGF0cy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBTdGF0cyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvU3RhdHMuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZENhbGwgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuXG5leHBvcnQgY29uc3QgY2FsbDogTG9jYWxKU1hDb21tYW5kQ2FsbCA9IGFzeW5jIG9uRG9uZSA9PiB7XG4gIHJldHVybiA8U3RhdHMgb25DbG9zZT17b25Eb25lfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEtBQUssUUFBUSwyQkFBMkI7QUFDakQsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFNRSxNQUFNLElBQUk7RUFDdkQsT0FBTyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQ0EsTUFBTSxDQUFDLEdBQUc7QUFDbkMsQ0FBQyIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/commands/status/index.ts b/commands/status/index.ts new file mode 100644 index 0000000..768b358 --- /dev/null +++ b/commands/status/index.ts @@ -0,0 +1,12 @@ +import type { Command } from '../../commands.js' + +const status = { + type: 'local-jsx', + name: 'status', + description: + 'Show Claude Code status including version, model, account, API connectivity, and tool statuses', + immediate: true, + load: () => import('./status.js'), +} satisfies Command + +export default status diff --git a/commands/status/status.tsx b/commands/status/status.tsx new file mode 100644 index 0000000..7d98ad1 --- /dev/null +++ b/commands/status/status.tsx @@ -0,0 +1,8 @@ +import * as React from 'react'; +import type { LocalJSXCommandContext } from '../../commands.js'; +import { Settings } from '../../components/Settings/Settings.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext): Promise { + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJTZXR0aW5ncyIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImNhbGwiLCJvbkRvbmUiLCJjb250ZXh0IiwiUHJvbWlzZSIsIlJlYWN0Tm9kZSJdLCJzb3VyY2VzIjpbInN0YXR1cy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZENvbnRleHQgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IFNldHRpbmdzIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9TZXR0aW5ncy9TZXR0aW5ncy5qcydcbmltcG9ydCB0eXBlIHsgTG9jYWxKU1hDb21tYW5kT25Eb25lIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogTG9jYWxKU1hDb21tYW5kT25Eb25lLFxuICBjb250ZXh0OiBMb2NhbEpTWENvbW1hbmRDb250ZXh0LFxuKTogUHJvbWlzZTxSZWFjdC5SZWFjdE5vZGU+IHtcbiAgcmV0dXJuIDxTZXR0aW5ncyBvbkNsb3NlPXtvbkRvbmV9IGNvbnRleHQ9e2NvbnRleHR9IGRlZmF1bHRUYWI9XCJTdGF0dXNcIiAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLGNBQWNDLHNCQUFzQixRQUFRLG1CQUFtQjtBQUMvRCxTQUFTQyxRQUFRLFFBQVEsdUNBQXVDO0FBQ2hFLGNBQWNDLHFCQUFxQixRQUFRLHdCQUF3QjtBQUVuRSxPQUFPLGVBQWVDLElBQUlBLENBQ3hCQyxNQUFNLEVBQUVGLHFCQUFxQixFQUM3QkcsT0FBTyxFQUFFTCxzQkFBc0IsQ0FDaEMsRUFBRU0sT0FBTyxDQUFDUCxLQUFLLENBQUNRLFNBQVMsQ0FBQyxDQUFDO0VBQzFCLE9BQU8sQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUNILE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDQyxPQUFPLENBQUMsQ0FBQyxVQUFVLENBQUMsUUFBUSxHQUFHO0FBQzVFIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/statusline.tsx b/commands/statusline.tsx new file mode 100644 index 0000000..02c7f0b --- /dev/null +++ b/commands/statusline.tsx @@ -0,0 +1,24 @@ +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'; +import type { Command } from '../commands.js'; +import { AGENT_TOOL_NAME } from '../tools/AgentTool/constants.js'; +const statusline = { + type: 'prompt', + description: "Set up Claude Code's status line UI", + contentLength: 0, + // Dynamic content + aliases: [], + name: 'statusline', + progressMessage: 'setting up statusLine', + allowedTools: [AGENT_TOOL_NAME, 'Read(~/**)', 'Edit(~/.claude/settings.json)'], + source: 'builtin', + disableNonInteractive: true, + async getPromptForCommand(args): Promise { + const prompt = args.trim() || 'Configure my statusLine from my shell PS1 configuration'; + return [{ + type: 'text', + text: `Create an ${AGENT_TOOL_NAME} with subagent_type "statusline-setup" and the prompt "${prompt}"` + }]; + } +} satisfies Command; +export default statusline; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJDb250ZW50QmxvY2tQYXJhbSIsIkNvbW1hbmQiLCJBR0VOVF9UT09MX05BTUUiLCJzdGF0dXNsaW5lIiwidHlwZSIsImRlc2NyaXB0aW9uIiwiY29udGVudExlbmd0aCIsImFsaWFzZXMiLCJuYW1lIiwicHJvZ3Jlc3NNZXNzYWdlIiwiYWxsb3dlZFRvb2xzIiwic291cmNlIiwiZGlzYWJsZU5vbkludGVyYWN0aXZlIiwiZ2V0UHJvbXB0Rm9yQ29tbWFuZCIsImFyZ3MiLCJQcm9taXNlIiwicHJvbXB0IiwidHJpbSIsInRleHQiXSwic291cmNlcyI6WyJzdGF0dXNsaW5lLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IENvbnRlbnRCbG9ja1BhcmFtIH0gZnJvbSAnQGFudGhyb3BpYy1haS9zZGsvcmVzb3VyY2VzL2luZGV4Lm1qcydcbmltcG9ydCB0eXBlIHsgQ29tbWFuZCB9IGZyb20gJy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgQUdFTlRfVE9PTF9OQU1FIH0gZnJvbSAnLi4vdG9vbHMvQWdlbnRUb29sL2NvbnN0YW50cy5qcydcblxuY29uc3Qgc3RhdHVzbGluZSA9IHtcbiAgdHlwZTogJ3Byb21wdCcsXG4gIGRlc2NyaXB0aW9uOiBcIlNldCB1cCBDbGF1ZGUgQ29kZSdzIHN0YXR1cyBsaW5lIFVJXCIsXG4gIGNvbnRlbnRMZW5ndGg6IDAsIC8vIER5bmFtaWMgY29udGVudFxuICBhbGlhc2VzOiBbXSxcbiAgbmFtZTogJ3N0YXR1c2xpbmUnLFxuICBwcm9ncmVzc01lc3NhZ2U6ICdzZXR0aW5nIHVwIHN0YXR1c0xpbmUnLFxuICBhbGxvd2VkVG9vbHM6IFtcbiAgICBBR0VOVF9UT09MX05BTUUsXG4gICAgJ1JlYWQofi8qKiknLFxuICAgICdFZGl0KH4vLmNsYXVkZS9zZXR0aW5ncy5qc29uKScsXG4gIF0sXG4gIHNvdXJjZTogJ2J1aWx0aW4nLFxuICBkaXNhYmxlTm9uSW50ZXJhY3RpdmU6IHRydWUsXG4gIGFzeW5jIGdldFByb21wdEZvckNvbW1hbmQoYXJncyk6IFByb21pc2U8Q29udGVudEJsb2NrUGFyYW1bXT4ge1xuICAgIGNvbnN0IHByb21wdCA9XG4gICAgICBhcmdzLnRyaW0oKSB8fCAnQ29uZmlndXJlIG15IHN0YXR1c0xpbmUgZnJvbSBteSBzaGVsbCBQUzEgY29uZmlndXJhdGlvbidcbiAgICByZXR1cm4gW1xuICAgICAge1xuICAgICAgICB0eXBlOiAndGV4dCcsXG4gICAgICAgIHRleHQ6IGBDcmVhdGUgYW4gJHtBR0VOVF9UT09MX05BTUV9IHdpdGggc3ViYWdlbnRfdHlwZSBcInN0YXR1c2xpbmUtc2V0dXBcIiBhbmQgdGhlIHByb21wdCBcIiR7cHJvbXB0fVwiYCxcbiAgICAgIH0sXG4gICAgXVxuICB9LFxufSBzYXRpc2ZpZXMgQ29tbWFuZFxuXG5leHBvcnQgZGVmYXVsdCBzdGF0dXNsaW5lXG4iXSwibWFwcGluZ3MiOiJBQUFBLGNBQWNBLGlCQUFpQixRQUFRLHVDQUF1QztBQUM5RSxjQUFjQyxPQUFPLFFBQVEsZ0JBQWdCO0FBQzdDLFNBQVNDLGVBQWUsUUFBUSxpQ0FBaUM7QUFFakUsTUFBTUMsVUFBVSxHQUFHO0VBQ2pCQyxJQUFJLEVBQUUsUUFBUTtFQUNkQyxXQUFXLEVBQUUscUNBQXFDO0VBQ2xEQyxhQUFhLEVBQUUsQ0FBQztFQUFFO0VBQ2xCQyxPQUFPLEVBQUUsRUFBRTtFQUNYQyxJQUFJLEVBQUUsWUFBWTtFQUNsQkMsZUFBZSxFQUFFLHVCQUF1QjtFQUN4Q0MsWUFBWSxFQUFFLENBQ1pSLGVBQWUsRUFDZixZQUFZLEVBQ1osK0JBQStCLENBQ2hDO0VBQ0RTLE1BQU0sRUFBRSxTQUFTO0VBQ2pCQyxxQkFBcUIsRUFBRSxJQUFJO0VBQzNCLE1BQU1DLG1CQUFtQkEsQ0FBQ0MsSUFBSSxDQUFDLEVBQUVDLE9BQU8sQ0FBQ2YsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDO0lBQzVELE1BQU1nQixNQUFNLEdBQ1ZGLElBQUksQ0FBQ0csSUFBSSxDQUFDLENBQUMsSUFBSSx5REFBeUQ7SUFDMUUsT0FBTyxDQUNMO01BQ0ViLElBQUksRUFBRSxNQUFNO01BQ1pjLElBQUksRUFBRSxhQUFhaEIsZUFBZSwwREFBMERjLE1BQU07SUFDcEcsQ0FBQyxDQUNGO0VBQ0g7QUFDRixDQUFDLFdBQVdmLE9BQU87QUFFbkIsZUFBZUUsVUFBVSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/commands/stickers/index.ts b/commands/stickers/index.ts new file mode 100644 index 0000000..ebca453 --- /dev/null +++ b/commands/stickers/index.ts @@ -0,0 +1,11 @@ +import type { Command } from '../../commands.js' + +const stickers = { + type: 'local', + name: 'stickers', + description: 'Order Claude Code stickers', + supportsNonInteractive: false, + load: () => import('./stickers.js'), +} satisfies Command + +export default stickers diff --git a/commands/stickers/stickers.ts b/commands/stickers/stickers.ts new file mode 100644 index 0000000..ede5193 --- /dev/null +++ b/commands/stickers/stickers.ts @@ -0,0 +1,16 @@ +import type { LocalCommandResult } from '../../types/command.js' +import { openBrowser } from '../../utils/browser.js' + +export async function call(): Promise { + const url = 'https://www.stickermule.com/claudecode' + const success = await openBrowser(url) + + if (success) { + return { type: 'text', value: 'Opening sticker page in browser…' } + } else { + return { + type: 'text', + value: `Failed to open browser. Visit: ${url}`, + } + } +} diff --git a/commands/summary/index.js b/commands/summary/index.js new file mode 100644 index 0000000..e1a619d --- /dev/null +++ b/commands/summary/index.js @@ -0,0 +1 @@ +export default { isEnabled: () => false, isHidden: true, name: 'stub' }; diff --git a/commands/tag/index.ts b/commands/tag/index.ts new file mode 100644 index 0000000..8d0bd65 --- /dev/null +++ b/commands/tag/index.ts @@ -0,0 +1,12 @@ +import type { Command } from '../../commands.js' + +const tag = { + type: 'local-jsx', + name: 'tag', + description: 'Toggle a searchable tag on the current session', + isEnabled: () => process.env.USER_TYPE === 'ant', + argumentHint: '', + load: () => import('./tag.js'), +} satisfies Command + +export default tag diff --git a/commands/tag/tag.tsx b/commands/tag/tag.tsx new file mode 100644 index 0000000..3809a99 --- /dev/null +++ b/commands/tag/tag.tsx @@ -0,0 +1,215 @@ +import { c as _c } from "react/compiler-runtime"; +import chalk from 'chalk'; +import type { UUID } from 'crypto'; +import * as React from 'react'; +import { getSessionId } from '../../bootstrap/state.js'; +import type { CommandResultDisplay } from '../../commands.js'; +import { Select } from '../../components/CustomSelect/select.js'; +import { Dialog } from '../../components/design-system/Dialog.js'; +import { COMMON_HELP_ARGS, COMMON_INFO_ARGS } from '../../constants/xml.js'; +import { Box, Text } from '../../ink.js'; +import { logEvent } from '../../services/analytics/index.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import { recursivelySanitizeUnicode } from '../../utils/sanitization.js'; +import { getCurrentSessionTag, getTranscriptPath, saveTag } from '../../utils/sessionStorage.js'; +function ConfirmRemoveTag(t0) { + const $ = _c(11); + const { + tagName, + onConfirm, + onCancel + } = t0; + const t1 = `Current tag: #${tagName}`; + let t2; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t2 = This will remove the tag from the current session.; + $[0] = t2; + } else { + t2 = $[0]; + } + let t3; + if ($[1] !== onCancel || $[2] !== onConfirm) { + t3 = value => value === "yes" ? onConfirm() : onCancel(); + $[1] = onCancel; + $[2] = onConfirm; + $[3] = t3; + } else { + t3 = $[3]; + } + let t4; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t4 = [{ + label: "Yes, remove tag", + value: "yes" + }, { + label: "No, keep tag", + value: "no" + }]; + $[4] = t4; + } else { + t4 = $[4]; + } + let t5; + if ($[5] !== t3) { + t5 = {t2}; + $[10] = handleSelect; + $[11] = options; + $[12] = t5; + } else { + t5 = $[12]; + } + let t6; + if ($[13] !== t4 || $[14] !== t5) { + t6 = {t4}{t5}; + $[13] = t4; + $[14] = t5; + $[15] = t6; + } else { + t6 = $[15]; + } + let t7; + if ($[16] !== handleCancel || $[17] !== t6) { + t7 = {t6}; + $[16] = handleCancel; + $[17] = t6; + $[18] = t7; + } else { + t7 = $[18]; + } + return t7; +} +const EDIT_PROMPT = 'Use the Skill tool to invoke the "thinkback" skill with mode=edit to modify my existing Claude Code year in review animation. Ask me what I want to change. When the animation is ready, tell the user to run /think-back again to play it.'; +const FIX_PROMPT = 'Use the Skill tool to invoke the "thinkback" skill with mode=fix to fix validation or rendering errors in my existing Claude Code year in review animation. Run the validator, identify errors, and fix them. When the animation is ready, tell the user to run /think-back again to play it.'; +const REGENERATE_PROMPT = 'Use the Skill tool to invoke the "thinkback" skill with mode=regenerate to create a completely new Claude Code year in review animation from scratch. Delete the existing animation and start fresh. When the animation is ready, tell the user to run /think-back again to play it.'; +function ThinkbackFlow(t0) { + const $ = _c(27); + const { + onDone + } = t0; + const [installComplete, setInstallComplete] = useState(false); + const [installError, setInstallError] = useState(null); + const [skillDir, setSkillDir] = useState(null); + const [hasGenerated, setHasGenerated] = useState(null); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = function handleReady() { + setInstallComplete(true); + }; + $[0] = t1; + } else { + t1 = $[0]; + } + const handleReady = t1; + let t2; + if ($[1] !== onDone) { + t2 = message => { + setInstallError(message); + onDone(`Error with thinkback: ${message}. Try running /plugin to manually install the think-back plugin.`, { + display: "system" + }); + }; + $[1] = onDone; + $[2] = t2; + } else { + t2 = $[2]; + } + const handleError = t2; + let t3; + let t4; + if ($[3] !== handleError || $[4] !== installComplete || $[5] !== installError || $[6] !== skillDir) { + t3 = () => { + if (installComplete && !skillDir && !installError) { + getThinkbackSkillDir().then(dir => { + if (dir) { + logForDebugging(`Thinkback skill directory: ${dir}`); + setSkillDir(dir); + } else { + handleError("Could not find thinkback skill directory"); + } + }); + } + }; + t4 = [installComplete, skillDir, installError, handleError]; + $[3] = handleError; + $[4] = installComplete; + $[5] = installError; + $[6] = skillDir; + $[7] = t3; + $[8] = t4; + } else { + t3 = $[7]; + t4 = $[8]; + } + useEffect(t3, t4); + let t5; + let t6; + if ($[9] !== skillDir) { + t5 = () => { + if (!skillDir) { + return; + } + const dataPath = join(skillDir, "year_in_review.js"); + pathExists(dataPath).then(exists => { + logForDebugging(`Checking for ${dataPath}: ${exists ? "found" : "not found"}`); + setHasGenerated(exists); + }); + }; + t6 = [skillDir]; + $[9] = skillDir; + $[10] = t5; + $[11] = t6; + } else { + t5 = $[10]; + t6 = $[11]; + } + useEffect(t5, t6); + let t7; + if ($[12] !== onDone) { + t7 = function handleAction(action) { + const prompts = { + edit: EDIT_PROMPT, + fix: FIX_PROMPT, + regenerate: REGENERATE_PROMPT + }; + onDone(prompts[action], { + display: "user", + shouldQuery: true + }); + }; + $[12] = onDone; + $[13] = t7; + } else { + t7 = $[13]; + } + const handleAction = t7; + if (installError) { + let t8; + if ($[14] !== installError) { + t8 = Error: {installError}; + $[14] = installError; + $[15] = t8; + } else { + t8 = $[15]; + } + let t9; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t9 = Try running /plugin to manually install the think-back plugin.; + $[16] = t9; + } else { + t9 = $[16]; + } + let t10; + if ($[17] !== t8) { + t10 = {t8}{t9}; + $[17] = t8; + $[18] = t10; + } else { + t10 = $[18]; + } + return t10; + } + if (!installComplete) { + let t8; + if ($[19] !== handleError) { + t8 = ; + $[19] = handleError; + $[20] = t8; + } else { + t8 = $[20]; + } + return t8; + } + if (!skillDir || hasGenerated === null) { + let t8; + if ($[21] === Symbol.for("react.memo_cache_sentinel")) { + t8 = Loading thinkback skill…; + $[21] = t8; + } else { + t8 = $[21]; + } + return t8; + } + let t8; + if ($[22] !== handleAction || $[23] !== hasGenerated || $[24] !== onDone || $[25] !== skillDir) { + t8 = ; + $[22] = handleAction; + $[23] = hasGenerated; + $[24] = onDone; + $[25] = skillDir; + $[26] = t8; + } else { + t8 = $[26]; + } + return t8; +} +export async function call(onDone: (result?: string, options?: { + display?: CommandResultDisplay; + shouldQuery?: boolean; +}) => void): Promise { + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJleGVjYSIsInJlYWRGaWxlIiwiam9pbiIsIlJlYWN0IiwidXNlQ2FsbGJhY2siLCJ1c2VFZmZlY3QiLCJ1c2VTdGF0ZSIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiU2VsZWN0IiwiRGlhbG9nIiwiU3Bpbm5lciIsImluc3RhbmNlcyIsIkJveCIsIlRleHQiLCJlbmFibGVQbHVnaW5PcCIsImxvZ0ZvckRlYnVnZ2luZyIsImlzRU5PRU5UIiwidG9FcnJvciIsImV4ZWNGaWxlTm9UaHJvdyIsInBhdGhFeGlzdHMiLCJsb2dFcnJvciIsImdldFBsYXRmb3JtIiwiY2xlYXJBbGxDYWNoZXMiLCJpc1BsdWdpbkluc3RhbGxlZCIsImFkZE1hcmtldHBsYWNlU291cmNlIiwiY2xlYXJNYXJrZXRwbGFjZXNDYWNoZSIsImxvYWRLbm93bk1hcmtldHBsYWNlc0NvbmZpZyIsInJlZnJlc2hNYXJrZXRwbGFjZSIsIk9GRklDSUFMX01BUktFVFBMQUNFX05BTUUiLCJsb2FkQWxsUGx1Z2lucyIsImluc3RhbGxTZWxlY3RlZFBsdWdpbnMiLCJJTlRFUk5BTF9NQVJLRVRQTEFDRV9OQU1FIiwiSU5URVJOQUxfTUFSS0VUUExBQ0VfUkVQTyIsIk9GRklDSUFMX01BUktFVFBMQUNFX1JFUE8iLCJnZXRNYXJrZXRwbGFjZU5hbWUiLCJnZXRNYXJrZXRwbGFjZVJlcG8iLCJnZXRQbHVnaW5JZCIsIlNLSUxMX05BTUUiLCJnZXRUaGlua2JhY2tTa2lsbERpciIsIlByb21pc2UiLCJlbmFibGVkIiwidGhpbmtiYWNrUGx1Z2luIiwiZmluZCIsInAiLCJuYW1lIiwic291cmNlIiwiaW5jbHVkZXMiLCJza2lsbERpciIsInBhdGgiLCJwbGF5QW5pbWF0aW9uIiwic3VjY2VzcyIsIm1lc3NhZ2UiLCJkYXRhUGF0aCIsInBsYXllclBhdGgiLCJlIiwiaW5rSW5zdGFuY2UiLCJnZXQiLCJwcm9jZXNzIiwic3Rkb3V0IiwiZW50ZXJBbHRlcm5hdGVTY3JlZW4iLCJzdGRpbyIsImN3ZCIsInJlamVjdCIsImV4aXRBbHRlcm5hdGVTY3JlZW4iLCJodG1sUGF0aCIsInBsYXRmb3JtIiwib3BlbkNtZCIsIkluc3RhbGxTdGF0ZSIsInBoYXNlIiwiVGhpbmtiYWNrSW5zdGFsbGVyIiwib25SZWFkeSIsIm9uRXJyb3IiLCJSZWFjdE5vZGUiLCJzdGF0ZSIsInNldFN0YXRlIiwicHJvZ3Jlc3NNZXNzYWdlIiwic2V0UHJvZ3Jlc3NNZXNzYWdlIiwiY2hlY2tBbmRJbnN0YWxsIiwia25vd25NYXJrZXRwbGFjZXMiLCJtYXJrZXRwbGFjZU5hbWUiLCJtYXJrZXRwbGFjZVJlcG8iLCJwbHVnaW5JZCIsIm1hcmtldHBsYWNlSW5zdGFsbGVkIiwicGx1Z2luQWxyZWFkeUluc3RhbGxlZCIsInJlcG8iLCJyZXN1bHQiLCJmYWlsZWQiLCJsZW5ndGgiLCJlcnJvck1zZyIsIm1hcCIsImYiLCJlcnJvciIsIkVycm9yIiwiZGlzYWJsZWQiLCJpc0Rpc2FibGVkIiwic29tZSIsImVuYWJsZVJlc3VsdCIsImVyciIsInN0YXR1c01lc3NhZ2UiLCJNZW51QWN0aW9uIiwiR2VuZXJhdGl2ZUFjdGlvbiIsIkV4Y2x1ZGUiLCJUaGlua2JhY2tNZW51IiwidDAiLCIkIiwiX2MiLCJvbkRvbmUiLCJvbkFjdGlvbiIsImhhc0dlbmVyYXRlZCIsImhhc1NlbGVjdGVkIiwic2V0SGFzU2VsZWN0ZWQiLCJ0MSIsImxhYmVsIiwidmFsdWUiLCJjb25zdCIsImRlc2NyaXB0aW9uIiwib3B0aW9ucyIsInQyIiwiaGFuZGxlU2VsZWN0IiwidGhlbiIsInVuZGVmaW5lZCIsImRpc3BsYXkiLCJ0MyIsImhhbmRsZUNhbmNlbCIsInQ0IiwidDUiLCJ0NiIsInQ3IiwiRURJVF9QUk9NUFQiLCJGSVhfUFJPTVBUIiwiUkVHRU5FUkFURV9QUk9NUFQiLCJUaGlua2JhY2tGbG93IiwiaW5zdGFsbENvbXBsZXRlIiwic2V0SW5zdGFsbENvbXBsZXRlIiwiaW5zdGFsbEVycm9yIiwic2V0SW5zdGFsbEVycm9yIiwic2V0U2tpbGxEaXIiLCJzZXRIYXNHZW5lcmF0ZWQiLCJTeW1ib2wiLCJmb3IiLCJoYW5kbGVSZWFkeSIsImhhbmRsZUVycm9yIiwiZGlyIiwiZXhpc3RzIiwiaGFuZGxlQWN0aW9uIiwiYWN0aW9uIiwicHJvbXB0cyIsImVkaXQiLCJmaXgiLCJyZWdlbmVyYXRlIiwic2hvdWxkUXVlcnkiLCJ0OCIsInQ5IiwidDEwIiwiY2FsbCJdLCJzb3VyY2VzIjpbInRoaW5rYmFjay50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZXhlY2EgfSBmcm9tICdleGVjYSdcbmltcG9ydCB7IHJlYWRGaWxlIH0gZnJvbSAnZnMvcHJvbWlzZXMnXG5pbXBvcnQgeyBqb2luIH0gZnJvbSAncGF0aCdcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgQ29tbWFuZFJlc3VsdERpc3BsYXkgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5pbXBvcnQgeyBTcGlubmVyIH0gZnJvbSAnLi4vLi4vY29tcG9uZW50cy9TcGlubmVyLmpzJ1xuaW1wb3J0IGluc3RhbmNlcyBmcm9tICcuLi8uLi9pbmsvaW5zdGFuY2VzLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgZW5hYmxlUGx1Z2luT3AgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9wbHVnaW5zL3BsdWdpbk9wZXJhdGlvbnMuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi8uLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IGlzRU5PRU5ULCB0b0Vycm9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvZXJyb3JzLmpzJ1xuaW1wb3J0IHsgZXhlY0ZpbGVOb1Rocm93IH0gZnJvbSAnLi4vLi4vdXRpbHMvZXhlY0ZpbGVOb1Rocm93LmpzJ1xuaW1wb3J0IHsgcGF0aEV4aXN0cyB9IGZyb20gJy4uLy4uL3V0aWxzL2ZpbGUuanMnXG5pbXBvcnQgeyBsb2dFcnJvciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7IGdldFBsYXRmb3JtIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGxhdGZvcm0uanMnXG5pbXBvcnQgeyBjbGVhckFsbENhY2hlcyB9IGZyb20gJy4uLy4uL3V0aWxzL3BsdWdpbnMvY2FjaGVVdGlscy5qcydcbmltcG9ydCB7IGlzUGx1Z2luSW5zdGFsbGVkIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGx1Z2lucy9pbnN0YWxsZWRQbHVnaW5zTWFuYWdlci5qcydcbmltcG9ydCB7XG4gIGFkZE1hcmtldHBsYWNlU291cmNlLFxuICBjbGVhck1hcmtldHBsYWNlc0NhY2hlLFxuICBsb2FkS25vd25NYXJrZXRwbGFjZXNDb25maWcsXG4gIHJlZnJlc2hNYXJrZXRwbGFjZSxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvcGx1Z2lucy9tYXJrZXRwbGFjZU1hbmFnZXIuanMnXG5pbXBvcnQgeyBPRkZJQ0lBTF9NQVJLRVRQTEFDRV9OQU1FIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGx1Z2lucy9vZmZpY2lhbE1hcmtldHBsYWNlLmpzJ1xuaW1wb3J0IHsgbG9hZEFsbFBsdWdpbnMgfSBmcm9tICcuLi8uLi91dGlscy9wbHVnaW5zL3BsdWdpbkxvYWRlci5qcydcbmltcG9ydCB7IGluc3RhbGxTZWxlY3RlZFBsdWdpbnMgfSBmcm9tICcuLi8uLi91dGlscy9wbHVnaW5zL3BsdWdpblN0YXJ0dXBDaGVjay5qcydcblxuLy8gTWFya2V0cGxhY2UgYW5kIHBsdWdpbiBpZGVudGlmaWVycyAtIHZhcmllcyBieSB1c2VyIHR5cGVcbmNvbnN0IElOVEVSTkFMX01BUktFVFBMQUNFX05BTUUgPSAnY2xhdWRlLWNvZGUtbWFya2V0cGxhY2UnXG5jb25zdCBJTlRFUk5BTF9NQVJLRVRQTEFDRV9SRVBPID0gJ2FudGhyb3BpY3MvY2xhdWRlLWNvZGUtbWFya2V0cGxhY2UnXG5jb25zdCBPRkZJQ0lBTF9NQVJLRVRQTEFDRV9SRVBPID0gJ2FudGhyb3BpY3MvY2xhdWRlLXBsdWdpbnMtb2ZmaWNpYWwnXG5cbmZ1bmN0aW9uIGdldE1hcmtldHBsYWNlTmFtZSgpOiBzdHJpbmcge1xuICByZXR1cm4gXCJleHRlcm5hbFwiID09PSAnYW50J1xuICAgID8gSU5URVJOQUxfTUFSS0VUUExBQ0VfTkFNRVxuICAgIDogT0ZGSUNJQUxfTUFSS0VUUExBQ0VfTkFNRVxufVxuXG5mdW5jdGlvbiBnZXRNYXJrZXRwbGFjZVJlcG8oKTogc3RyaW5nIHtcbiAgcmV0dXJuIFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCdcbiAgICA/IElOVEVSTkFMX01BUktFVFBMQUNFX1JFUE9cbiAgICA6IE9GRklDSUFMX01BUktFVFBMQUNFX1JFUE9cbn1cblxuZnVuY3Rpb24gZ2V0UGx1Z2luSWQoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGB0aGlua2JhY2tAJHtnZXRNYXJrZXRwbGFjZU5hbWUoKX1gXG59XG5cbmNvbnN0IFNLSUxMX05BTUUgPSAndGhpbmtiYWNrJ1xuXG4vKipcbiAqIEdldCB0aGUgdGhpbmtiYWNrIHNraWxsIGRpcmVjdG9yeSBmcm9tIHRoZSBpbnN0YWxsZWQgcGx1Z2luJ3MgY2FjaGUgcGF0aFxuICovXG5hc3luYyBmdW5jdGlvbiBnZXRUaGlua2JhY2tTa2lsbERpcigpOiBQcm9taXNlPHN0cmluZyB8IG51bGw+IHtcbiAgY29uc3QgeyBlbmFibGVkIH0gPSBhd2FpdCBsb2FkQWxsUGx1Z2lucygpXG4gIGNvbnN0IHRoaW5rYmFja1BsdWdpbiA9IGVuYWJsZWQuZmluZChcbiAgICBwID0+XG4gICAgICBwLm5hbWUgPT09ICd0aGlua2JhY2snIHx8IChwLnNvdXJjZSAmJiBwLnNvdXJjZS5pbmNsdWRlcyhnZXRQbHVnaW5JZCgpKSksXG4gIClcblxuICBpZiAoIXRoaW5rYmFja1BsdWdpbikge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBza2lsbERpciA9IGpvaW4odGhpbmtiYWNrUGx1Z2luLnBhdGgsICdza2lsbHMnLCBTS0lMTF9OQU1FKVxuICBpZiAoYXdhaXQgcGF0aEV4aXN0cyhza2lsbERpcikpIHtcbiAgICByZXR1cm4gc2tpbGxEaXJcbiAgfVxuXG4gIHJldHVybiBudWxsXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBwbGF5QW5pbWF0aW9uKHNraWxsRGlyOiBzdHJpbmcpOiBQcm9taXNlPHtcbiAgc3VjY2VzczogYm9vbGVhblxuICBtZXNzYWdlOiBzdHJpbmdcbn0+IHtcbiAgY29uc3QgZGF0YVBhdGggPSBqb2luKHNraWxsRGlyLCAneWVhcl9pbl9yZXZpZXcuanMnKVxuICBjb25zdCBwbGF5ZXJQYXRoID0gam9pbihza2lsbERpciwgJ3BsYXllci5qcycpXG5cbiAgLy8gQm90aCBmaWxlcyBhcmUgcHJlcmVxdWlzaXRlcyBmb3IgdGhlIG5vZGUgc3VicHJvY2Vzcy4gUmVhZCB0aGVtIGhlcmVcbiAgLy8gKG5vdCBhdCBjYWxsIHNpdGVzKSBzbyBhbGwgY2FsbGVycyBnZXQgY29uc2lzdGVudCBlcnJvciBtZXNzYWdpbmcuIFRoZVxuICAvLyBzdWJwcm9jZXNzIHJ1bnMgd2l0aCByZWplY3Q6IGZhbHNlLCBzbyBhIG1pc3NpbmcgZmlsZSB3b3VsZCBvdGhlcndpc2VcbiAgLy8gc2lsZW50bHkgcmV0dXJuIHN1Y2Nlc3MuIFVzaW5nIHJlYWRGaWxlIChub3QgYWNjZXNzKSBwZXIgQ0xBVURFLm1kLlxuICAvL1xuICAvLyBOb24tRU5PRU5UIGVycm9ycyAoRUFDQ0VTIGV0YykgYXJlIGxvZ2dlZCBhbmQgcmV0dXJuZWQgYXMgZmFpbHVyZXMgcmF0aGVyXG4gIC8vIHRoYW4gdGhyb3duIOKAlCB0aGUgb2xkIHBhdGhFeGlzdHMtYmFzZWQgY29kZSBuZXZlciB0aHJldywgYW5kIG9uZSBjYWxsZXJcbiAgLy8gKGhhbmRsZVNlbGVjdCkgdXNlcyBgdm9pZCBwbGF5QW5pbWF0aW9uKCkudGhlbiguLi4pYCB3aXRob3V0IGEgLmNhdGNoKCkuXG4gIHRyeSB7XG4gICAgYXdhaXQgcmVhZEZpbGUoZGF0YVBhdGgpXG4gIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICBpZiAoaXNFTk9FTlQoZSkpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICBtZXNzYWdlOiAnTm8gYW5pbWF0aW9uIGZvdW5kLiBSdW4gL3RoaW5rLWJhY2sgZmlyc3QgdG8gZ2VuZXJhdGUgb25lLicsXG4gICAgICB9XG4gICAgfVxuICAgIGxvZ0Vycm9yKGUpXG4gICAgcmV0dXJuIHtcbiAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgbWVzc2FnZTogYENvdWxkIG5vdCBhY2Nlc3MgYW5pbWF0aW9uIGRhdGE6ICR7dG9FcnJvcihlKS5tZXNzYWdlfWAsXG4gICAgfVxuICB9XG5cbiAgdHJ5IHtcbiAgICBhd2FpdCByZWFkRmlsZShwbGF5ZXJQYXRoKVxuICB9IGNhdGNoIChlOiB1bmtub3duKSB7XG4gICAgaWYgKGlzRU5PRU5UKGUpKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgbWVzc2FnZTpcbiAgICAgICAgICAnUGxheWVyIHNjcmlwdCBub3QgZm91bmQuIFRoZSBwbGF5ZXIuanMgZmlsZSBpcyBtaXNzaW5nIGZyb20gdGhlIHRoaW5rYmFjayBza2lsbC4nLFxuICAgICAgfVxuICAgIH1cbiAgICBsb2dFcnJvcihlKVxuICAgIHJldHVybiB7XG4gICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgIG1lc3NhZ2U6IGBDb3VsZCBub3QgYWNjZXNzIHBsYXllciBzY3JpcHQ6ICR7dG9FcnJvcihlKS5tZXNzYWdlfWAsXG4gICAgfVxuICB9XG5cbiAgLy8gR2V0IGluayBpbnN0YW5jZSBmb3IgdGVybWluYWwgdGFrZW92ZXJcbiAgY29uc3QgaW5rSW5zdGFuY2UgPSBpbnN0YW5jZXMuZ2V0KHByb2Nlc3Muc3Rkb3V0KVxuICBpZiAoIWlua0luc3RhbmNlKSB7XG4gICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UsIG1lc3NhZ2U6ICdGYWlsZWQgdG8gYWNjZXNzIHRlcm1pbmFsIGluc3RhbmNlJyB9XG4gIH1cblxuICBpbmtJbnN0YW5jZS5lbnRlckFsdGVybmF0ZVNjcmVlbigpXG4gIHRyeSB7XG4gICAgYXdhaXQgZXhlY2EoJ25vZGUnLCBbcGxheWVyUGF0aF0sIHtcbiAgICAgIHN0ZGlvOiAnaW5oZXJpdCcsXG4gICAgICBjd2Q6IHNraWxsRGlyLFxuICAgICAgcmVqZWN0OiBmYWxzZSxcbiAgICB9KVxuICB9IGNhdGNoIHtcbiAgICAvLyBBbmltYXRpb24gbWF5IGhhdmUgYmVlbiBpbnRlcnJ1cHRlZCAoZS5nLiwgQ3RybCtDKVxuICB9IGZpbmFsbHkge1xuICAgIGlua0luc3RhbmNlLmV4aXRBbHRlcm5hdGVTY3JlZW4oKVxuICB9XG5cbiAgLy8gT3BlbiB0aGUgSFRNTCBmaWxlIGluIGJyb3dzZXIgZm9yIHZpZGVvIGRvd25sb2FkXG4gIGNvbnN0IGh0bWxQYXRoID0gam9pbihza2lsbERpciwgJ3llYXJfaW5fcmV2aWV3Lmh0bWwnKVxuICBpZiAoYXdhaXQgcGF0aEV4aXN0cyhodG1sUGF0aCkpIHtcbiAgICBjb25zdCBwbGF0Zm9ybSA9IGdldFBsYXRmb3JtKClcbiAgICBjb25zdCBvcGVuQ21kID1cbiAgICAgIHBsYXRmb3JtID09PSAnbWFjb3MnXG4gICAgICAgID8gJ29wZW4nXG4gICAgICAgIDogcGxhdGZvcm0gPT09ICd3aW5kb3dzJ1xuICAgICAgICAgID8gJ3N0YXJ0J1xuICAgICAgICAgIDogJ3hkZy1vcGVuJ1xuICAgIHZvaWQgZXhlY0ZpbGVOb1Rocm93KG9wZW5DbWQsIFtodG1sUGF0aF0pXG4gIH1cblxuICByZXR1cm4geyBzdWNjZXNzOiB0cnVlLCBtZXNzYWdlOiAnWWVhciBpbiByZXZpZXcgYW5pbWF0aW9uIGNvbXBsZXRlIScgfVxufVxuXG50eXBlIEluc3RhbGxTdGF0ZSA9XG4gIHwgeyBwaGFzZTogJ2NoZWNraW5nJyB9XG4gIHwgeyBwaGFzZTogJ2luc3RhbGxpbmctbWFya2V0cGxhY2UnIH1cbiAgfCB7IHBoYXNlOiAnaW5zdGFsbGluZy1wbHVnaW4nIH1cbiAgfCB7IHBoYXNlOiAnZW5hYmxpbmctcGx1Z2luJyB9XG4gIHwgeyBwaGFzZTogJ3JlYWR5JyB9XG4gIHwgeyBwaGFzZTogJ2Vycm9yJzsgbWVzc2FnZTogc3RyaW5nIH1cblxuZnVuY3Rpb24gVGhpbmtiYWNrSW5zdGFsbGVyKHtcbiAgb25SZWFkeSxcbiAgb25FcnJvcixcbn06IHtcbiAgb25SZWFkeTogKCkgPT4gdm9pZFxuICBvbkVycm9yOiAobWVzc2FnZTogc3RyaW5nKSA9PiB2b2lkXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3N0YXRlLCBzZXRTdGF0ZV0gPSB1c2VTdGF0ZTxJbnN0YWxsU3RhdGU+KHsgcGhhc2U6ICdjaGVja2luZycgfSlcbiAgY29uc3QgW3Byb2dyZXNzTWVzc2FnZSwgc2V0UHJvZ3Jlc3NNZXNzYWdlXSA9IHVzZVN0YXRlKCcnKVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgYXN5bmMgZnVuY3Rpb24gY2hlY2tBbmRJbnN0YWxsKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gQ2hlY2sgaWYgbWFya2V0cGxhY2UgaXMgaW5zdGFsbGVkXG4gICAgICAgIGNvbnN0IGtub3duTWFya2V0cGxhY2VzID0gYXdhaXQgbG9hZEtub3duTWFya2V0cGxhY2VzQ29uZmlnKClcbiAgICAgICAgY29uc3QgbWFya2V0cGxhY2VOYW1lID0gZ2V0TWFya2V0cGxhY2VOYW1lKClcbiAgICAgICAgY29uc3QgbWFya2V0cGxhY2VSZXBvID0gZ2V0TWFya2V0cGxhY2VSZXBvKClcbiAgICAgICAgY29uc3QgcGx1Z2luSWQgPSBnZXRQbHVnaW5JZCgpXG4gICAgICAgIGNvbnN0IG1hcmtldHBsYWNlSW5zdGFsbGVkID0gbWFya2V0cGxhY2VOYW1lIGluIGtub3duTWFya2V0cGxhY2VzXG5cbiAgICAgICAgLy8gQ2hlY2sgaWYgcGx1Z2luIGlzIGFscmVhZHkgaW5zdGFsbGVkIGZpcnN0XG4gICAgICAgIGNvbnN0IHBsdWdpbkFscmVhZHlJbnN0YWxsZWQgPSBpc1BsdWdpbkluc3RhbGxlZChwbHVnaW5JZClcblxuICAgICAgICBpZiAoIW1hcmtldHBsYWNlSW5zdGFsbGVkKSB7XG4gICAgICAgICAgLy8gSW5zdGFsbCB0aGUgbWFya2V0cGxhY2VcbiAgICAgICAgICBzZXRTdGF0ZSh7IHBoYXNlOiAnaW5zdGFsbGluZy1tYXJrZXRwbGFjZScgfSlcbiAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoYEluc3RhbGxpbmcgbWFya2V0cGxhY2UgJHttYXJrZXRwbGFjZVJlcG99YClcblxuICAgICAgICAgIGF3YWl0IGFkZE1hcmtldHBsYWNlU291cmNlKFxuICAgICAgICAgICAgeyBzb3VyY2U6ICdnaXRodWInLCByZXBvOiBtYXJrZXRwbGFjZVJlcG8gfSxcbiAgICAgICAgICAgIG1lc3NhZ2UgPT4ge1xuICAgICAgICAgICAgICBzZXRQcm9ncmVzc01lc3NhZ2UobWVzc2FnZSlcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgKVxuICAgICAgICAgIGNsZWFyQWxsQ2FjaGVzKClcbiAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoYE1hcmtldHBsYWNlICR7bWFya2V0cGxhY2VOYW1lfSBpbnN0YWxsZWRgKVxuICAgICAgICB9IGVsc2UgaWYgKCFwbHVnaW5BbHJlYWR5SW5zdGFsbGVkKSB7XG4gICAgICAgICAgLy8gTWFya2V0cGxhY2UgaW5zdGFsbGVkIGJ1dCBwbHVnaW4gbm90IGluc3RhbGxlZCAtIHJlZnJlc2ggdG8gZ2V0IGxhdGVzdCBwbHVnaW5zXG4gICAgICAgICAgLy8gT25seSByZWZyZXNoIHdoZW4gbmVlZGVkIHRvIGF2b2lkIHBvdGVudGlhbGx5IGRlc3RydWN0aXZlIGdpdCBvcGVyYXRpb25zXG4gICAgICAgICAgc2V0U3RhdGUoeyBwaGFzZTogJ2luc3RhbGxpbmctbWFya2V0cGxhY2UnIH0pXG4gICAgICAgICAgc2V0UHJvZ3Jlc3NNZXNzYWdlKCdVcGRhdGluZyBtYXJrZXRwbGFjZeKApicpXG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKGBSZWZyZXNoaW5nIG1hcmtldHBsYWNlICR7bWFya2V0cGxhY2VOYW1lfWApXG5cbiAgICAgICAgICBhd2FpdCByZWZyZXNoTWFya2V0cGxhY2UobWFya2V0cGxhY2VOYW1lLCBtZXNzYWdlID0+IHtcbiAgICAgICAgICAgIHNldFByb2dyZXNzTWVzc2FnZShtZXNzYWdlKVxuICAgICAgICAgIH0pXG4gICAgICAgICAgY2xlYXJNYXJrZXRwbGFjZXNDYWNoZSgpXG4gICAgICAgICAgY2xlYXJBbGxDYWNoZXMoKVxuICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgTWFya2V0cGxhY2UgJHttYXJrZXRwbGFjZU5hbWV9IHJlZnJlc2hlZGApXG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIXBsdWdpbkFscmVhZHlJbnN0YWxsZWQpIHtcbiAgICAgICAgICAvLyBJbnN0YWxsIHRoZSBwbHVnaW5cbiAgICAgICAgICBzZXRTdGF0ZSh7IHBoYXNlOiAnaW5zdGFsbGluZy1wbHVnaW4nIH0pXG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKGBJbnN0YWxsaW5nIHBsdWdpbiAke3BsdWdpbklkfWApXG5cbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBpbnN0YWxsU2VsZWN0ZWRQbHVnaW5zKFtwbHVnaW5JZF0pXG5cbiAgICAgICAgICBpZiAocmVzdWx0LmZhaWxlZC5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBlcnJvck1zZyA9IHJlc3VsdC5mYWlsZWRcbiAgICAgICAgICAgICAgLm1hcChmID0+IGAke2YubmFtZX06ICR7Zi5lcnJvcn1gKVxuICAgICAgICAgICAgICAuam9pbignLCAnKVxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBGYWlsZWQgdG8gaW5zdGFsbCBwbHVnaW46ICR7ZXJyb3JNc2d9YClcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjbGVhckFsbENhY2hlcygpXG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKGBQbHVnaW4gJHtwbHVnaW5JZH0gaW5zdGFsbGVkYClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBQbHVnaW4gaXMgaW5zdGFsbGVkLCBjaGVjayBpZiBpdCdzIGVuYWJsZWRcbiAgICAgICAgICBjb25zdCB7IGRpc2FibGVkIH0gPSBhd2FpdCBsb2FkQWxsUGx1Z2lucygpXG4gICAgICAgICAgY29uc3QgaXNEaXNhYmxlZCA9IGRpc2FibGVkLnNvbWUoXG4gICAgICAgICAgICBwID0+IHAubmFtZSA9PT0gJ3RoaW5rYmFjaycgfHwgcC5zb3VyY2U/LmluY2x1ZGVzKHBsdWdpbklkKSxcbiAgICAgICAgICApXG5cbiAgICAgICAgICBpZiAoaXNEaXNhYmxlZCkge1xuICAgICAgICAgICAgLy8gRW5hYmxlIHRoZSBwbHVnaW5cbiAgICAgICAgICAgIHNldFN0YXRlKHsgcGhhc2U6ICdlbmFibGluZy1wbHVnaW4nIH0pXG4gICAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoYEVuYWJsaW5nIHBsdWdpbiAke3BsdWdpbklkfWApXG5cbiAgICAgICAgICAgIGNvbnN0IGVuYWJsZVJlc3VsdCA9IGF3YWl0IGVuYWJsZVBsdWdpbk9wKHBsdWdpbklkKVxuICAgICAgICAgICAgaWYgKCFlbmFibGVSZXN1bHQuc3VjY2Vzcykge1xuICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAgICAgYEZhaWxlZCB0byBlbmFibGUgcGx1Z2luOiAke2VuYWJsZVJlc3VsdC5tZXNzYWdlfWAsXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY2xlYXJBbGxDYWNoZXMoKVxuICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKGBQbHVnaW4gJHtwbHVnaW5JZH0gZW5hYmxlZGApXG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgc2V0U3RhdGUoeyBwaGFzZTogJ3JlYWR5JyB9KVxuICAgICAgICBvblJlYWR5KClcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGNvbnN0IGVyciA9IHRvRXJyb3IoZXJyb3IpXG4gICAgICAgIGxvZ0Vycm9yKGVycilcbiAgICAgICAgc2V0U3RhdGUoeyBwaGFzZTogJ2Vycm9yJywgbWVzc2FnZTogZXJyLm1lc3NhZ2UgfSlcbiAgICAgICAgb25FcnJvcihlcnIubWVzc2FnZSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICB2b2lkIGNoZWNrQW5kSW5zdGFsbCgpXG4gIH0sIFtvblJlYWR5LCBvbkVycm9yXSlcblxuICBpZiAoc3RhdGUucGhhc2UgPT09ICdlcnJvcicpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5FcnJvcjoge3N0YXRlLm1lc3NhZ2V9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgaWYgKHN0YXRlLnBoYXNlID09PSAncmVhZHknKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IHN0YXR1c01lc3NhZ2UgPVxuICAgIHN0YXRlLnBoYXNlID09PSAnY2hlY2tpbmcnXG4gICAgICA/ICdDaGVja2luZyB0aGlua2JhY2sgaW5zdGFsbGF0aW9u4oCmJ1xuICAgICAgOiBzdGF0ZS5waGFzZSA9PT0gJ2luc3RhbGxpbmctbWFya2V0cGxhY2UnXG4gICAgICAgID8gJ0luc3RhbGxpbmcgbWFya2V0cGxhY2XigKYnXG4gICAgICAgIDogc3RhdGUucGhhc2UgPT09ICdlbmFibGluZy1wbHVnaW4nXG4gICAgICAgICAgPyAnRW5hYmxpbmcgdGhpbmtiYWNrIHBsdWdpbuKApidcbiAgICAgICAgICA6ICdJbnN0YWxsaW5nIHRoaW5rYmFjayBwbHVnaW7igKYnXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgIDxCb3g+XG4gICAgICAgIDxTcGlubmVyIC8+XG4gICAgICAgIDxUZXh0Pntwcm9ncmVzc01lc3NhZ2UgfHwgc3RhdHVzTWVzc2FnZX08L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L0JveD5cbiAgKVxufVxuXG50eXBlIE1lbnVBY3Rpb24gPSAncGxheScgfCAnZWRpdCcgfCAnZml4JyB8ICdyZWdlbmVyYXRlJ1xudHlwZSBHZW5lcmF0aXZlQWN0aW9uID0gRXhjbHVkZTxNZW51QWN0aW9uLCAncGxheSc+XG5cbmZ1bmN0aW9uIFRoaW5rYmFja01lbnUoe1xuICBvbkRvbmUsXG4gIG9uQWN0aW9uLFxuICBza2lsbERpcixcbiAgaGFzR2VuZXJhdGVkLFxufToge1xuICBvbkRvbmU6IChcbiAgICByZXN1bHQ/OiBzdHJpbmcsXG4gICAgb3B0aW9ucz86IHsgZGlzcGxheT86IENvbW1hbmRSZXN1bHREaXNwbGF5OyBzaG91bGRRdWVyeT86IGJvb2xlYW4gfSxcbiAgKSA9PiB2b2lkXG4gIG9uQWN0aW9uOiAoYWN0aW9uOiBHZW5lcmF0aXZlQWN0aW9uKSA9PiB2b2lkXG4gIHNraWxsRGlyOiBzdHJpbmdcbiAgaGFzR2VuZXJhdGVkOiBib29sZWFuXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW2hhc1NlbGVjdGVkLCBzZXRIYXNTZWxlY3RlZF0gPSB1c2VTdGF0ZShmYWxzZSlcblxuICBjb25zdCBvcHRpb25zID0gaGFzR2VuZXJhdGVkXG4gICAgPyBbXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1BsYXkgYW5pbWF0aW9uJyxcbiAgICAgICAgICB2YWx1ZTogJ3BsYXknIGFzIGNvbnN0LFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnV2F0Y2ggeW91ciB5ZWFyIGluIHJldmlldycsXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ0VkaXQgY29udGVudCcsXG4gICAgICAgICAgdmFsdWU6ICdlZGl0JyBhcyBjb25zdCxcbiAgICAgICAgICBkZXNjcmlwdGlvbjogJ01vZGlmeSB0aGUgYW5pbWF0aW9uJyxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnRml4IGVycm9ycycsXG4gICAgICAgICAgdmFsdWU6ICdmaXgnIGFzIGNvbnN0LFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnRml4IHZhbGlkYXRpb24gb3IgcmVuZGVyaW5nIGlzc3VlcycsXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ1JlZ2VuZXJhdGUnLFxuICAgICAgICAgIHZhbHVlOiAncmVnZW5lcmF0ZScgYXMgY29uc3QsXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdDcmVhdGUgYSBuZXcgYW5pbWF0aW9uIGZyb20gc2NyYXRjaCcsXG4gICAgICAgIH0sXG4gICAgICBdXG4gICAgOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogXCJMZXQncyBnbyFcIixcbiAgICAgICAgICB2YWx1ZTogJ3JlZ2VuZXJhdGUnIGFzIGNvbnN0LFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnR2VuZXJhdGUgeW91ciBwZXJzb25hbGl6ZWQgYW5pbWF0aW9uJyxcbiAgICAgICAgfSxcbiAgICAgIF1cblxuICBmdW5jdGlvbiBoYW5kbGVTZWxlY3QodmFsdWU6IE1lbnVBY3Rpb24pOiB2b2lkIHtcbiAgICBzZXRIYXNTZWxlY3RlZCh0cnVlKVxuICAgIGlmICh2YWx1ZSA9PT0gJ3BsYXknKSB7XG4gICAgICAvLyBQbGF5IHJ1bnMgdGhlIHRlcm1pbmFsLXRha2VvdmVyIGFuaW1hdGlvbiwgdGhlbiBzaWduYWwgZG9uZSB3aXRoIHNraXBcbiAgICAgIHZvaWQgcGxheUFuaW1hdGlvbihza2lsbERpcikudGhlbigoKSA9PiB7XG4gICAgICAgIG9uRG9uZSh1bmRlZmluZWQsIHsgZGlzcGxheTogJ3NraXAnIH0pXG4gICAgICB9KVxuICAgIH0gZWxzZSB7XG4gICAgICBvbkFjdGlvbih2YWx1ZSlcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBoYW5kbGVDYW5jZWwoKTogdm9pZCB7XG4gICAgb25Eb25lKHVuZGVmaW5lZCwgeyBkaXNwbGF5OiAnc2tpcCcgfSlcbiAgfVxuXG4gIGlmIChoYXNTZWxlY3RlZCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxEaWFsb2dcbiAgICAgIHRpdGxlPVwiVGhpbmsgQmFjayBvbiAyMDI1IHdpdGggQ2xhdWRlIENvZGVcIlxuICAgICAgc3VidGl0bGU9XCJHZW5lcmF0ZSB5b3VyIDIwMjUgQ2xhdWRlIENvZGUgVGhpbmsgQmFjayAodGFrZXMgYSBmZXcgbWludXRlcyB0byBydW4pXCJcbiAgICAgIG9uQ2FuY2VsPXtoYW5kbGVDYW5jZWx9XG4gICAgICBjb2xvcj1cImNsYXVkZVwiXG4gICAgPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgey8qIERlc2NyaXB0aW9uIGZvciBmaXJzdC10aW1lIHVzZXJzICovfVxuICAgICAgICB7IWhhc0dlbmVyYXRlZCAmJiAoXG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICA8VGV4dD5SZWxpdmUgeW91ciB5ZWFyIG9mIGNvZGluZyB3aXRoIENsYXVkZS48L1RleHQ+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIFwiV2UnbGwgY3JlYXRlIGEgcGVyc29uYWxpemVkIEFTQ0lJIGFuaW1hdGlvbiBjZWxlYnJhdGluZyB5b3VyIGpvdXJuZXkuXCJcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApfVxuXG4gICAgICAgIHsvKiBNZW51ICovfVxuICAgICAgICA8U2VsZWN0XG4gICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICBvbkNoYW5nZT17aGFuZGxlU2VsZWN0fVxuICAgICAgICAgIHZpc2libGVPcHRpb25Db3VudD17NX1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIDwvRGlhbG9nPlxuICApXG59XG5cbmNvbnN0IEVESVRfUFJPTVBUID1cbiAgJ1VzZSB0aGUgU2tpbGwgdG9vbCB0byBpbnZva2UgdGhlIFwidGhpbmtiYWNrXCIgc2tpbGwgd2l0aCBtb2RlPWVkaXQgdG8gbW9kaWZ5IG15IGV4aXN0aW5nIENsYXVkZSBDb2RlIHllYXIgaW4gcmV2aWV3IGFuaW1hdGlvbi4gQXNrIG1lIHdoYXQgSSB3YW50IHRvIGNoYW5nZS4gV2hlbiB0aGUgYW5pbWF0aW9uIGlzIHJlYWR5LCB0ZWxsIHRoZSB1c2VyIHRvIHJ1biAvdGhpbmstYmFjayBhZ2FpbiB0byBwbGF5IGl0LidcblxuY29uc3QgRklYX1BST01QVCA9XG4gICdVc2UgdGhlIFNraWxsIHRvb2wgdG8gaW52b2tlIHRoZSBcInRoaW5rYmFja1wiIHNraWxsIHdpdGggbW9kZT1maXggdG8gZml4IHZhbGlkYXRpb24gb3IgcmVuZGVyaW5nIGVycm9ycyBpbiBteSBleGlzdGluZyBDbGF1ZGUgQ29kZSB5ZWFyIGluIHJldmlldyBhbmltYXRpb24uIFJ1biB0aGUgdmFsaWRhdG9yLCBpZGVudGlmeSBlcnJvcnMsIGFuZCBmaXggdGhlbS4gV2hlbiB0aGUgYW5pbWF0aW9uIGlzIHJlYWR5LCB0ZWxsIHRoZSB1c2VyIHRvIHJ1biAvdGhpbmstYmFjayBhZ2FpbiB0byBwbGF5IGl0LidcblxuY29uc3QgUkVHRU5FUkFURV9QUk9NUFQgPVxuICAnVXNlIHRoZSBTa2lsbCB0b29sIHRvIGludm9rZSB0aGUgXCJ0aGlua2JhY2tcIiBza2lsbCB3aXRoIG1vZGU9cmVnZW5lcmF0ZSB0byBjcmVhdGUgYSBjb21wbGV0ZWx5IG5ldyBDbGF1ZGUgQ29kZSB5ZWFyIGluIHJldmlldyBhbmltYXRpb24gZnJvbSBzY3JhdGNoLiBEZWxldGUgdGhlIGV4aXN0aW5nIGFuaW1hdGlvbiBhbmQgc3RhcnQgZnJlc2guIFdoZW4gdGhlIGFuaW1hdGlvbiBpcyByZWFkeSwgdGVsbCB0aGUgdXNlciB0byBydW4gL3RoaW5rLWJhY2sgYWdhaW4gdG8gcGxheSBpdC4nXG5cbmZ1bmN0aW9uIFRoaW5rYmFja0Zsb3coe1xuICBvbkRvbmUsXG59OiB7XG4gIG9uRG9uZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXk7IHNob3VsZFF1ZXJ5PzogYm9vbGVhbiB9LFxuICApID0+IHZvaWRcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbaW5zdGFsbENvbXBsZXRlLCBzZXRJbnN0YWxsQ29tcGxldGVdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtpbnN0YWxsRXJyb3IsIHNldEluc3RhbGxFcnJvcl0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICBjb25zdCBbc2tpbGxEaXIsIHNldFNraWxsRGlyXSA9IHVzZVN0YXRlPHN0cmluZyB8IG51bGw+KG51bGwpXG4gIGNvbnN0IFtoYXNHZW5lcmF0ZWQsIHNldEhhc0dlbmVyYXRlZF0gPSB1c2VTdGF0ZTxib29sZWFuIHwgbnVsbD4obnVsbClcblxuICBmdW5jdGlvbiBoYW5kbGVSZWFkeSgpOiB2b2lkIHtcbiAgICBzZXRJbnN0YWxsQ29tcGxldGUodHJ1ZSlcbiAgfVxuXG4gIGNvbnN0IGhhbmRsZUVycm9yID0gdXNlQ2FsbGJhY2soXG4gICAgKG1lc3NhZ2U6IHN0cmluZyk6IHZvaWQgPT4ge1xuICAgICAgc2V0SW5zdGFsbEVycm9yKG1lc3NhZ2UpXG4gICAgICAvLyBDYWxsIG9uRG9uZSB3aXRoIHRoZSBlcnJvciBtZXNzYWdlIHNvIHRoZSBtb2RlbCBjYW4gY29udGludWVcbiAgICAgIG9uRG9uZShcbiAgICAgICAgYEVycm9yIHdpdGggdGhpbmtiYWNrOiAke21lc3NhZ2V9LiBUcnkgcnVubmluZyAvcGx1Z2luIHRvIG1hbnVhbGx5IGluc3RhbGwgdGhlIHRoaW5rLWJhY2sgcGx1Z2luLmAsXG4gICAgICAgIHsgZGlzcGxheTogJ3N5c3RlbScgfSxcbiAgICAgIClcbiAgICB9LFxuICAgIFtvbkRvbmVdLFxuICApXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoaW5zdGFsbENvbXBsZXRlICYmICFza2lsbERpciAmJiAhaW5zdGFsbEVycm9yKSB7XG4gICAgICAvLyBHZXQgdGhlIHNraWxsIGRpcmVjdG9yeSBhZnRlciBpbnN0YWxsYXRpb25cbiAgICAgIHZvaWQgZ2V0VGhpbmtiYWNrU2tpbGxEaXIoKS50aGVuKGRpciA9PiB7XG4gICAgICAgIGlmIChkaXIpIHtcbiAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoYFRoaW5rYmFjayBza2lsbCBkaXJlY3Rvcnk6ICR7ZGlyfWApXG4gICAgICAgICAgc2V0U2tpbGxEaXIoZGlyKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGhhbmRsZUVycm9yKCdDb3VsZCBub3QgZmluZCB0aGlua2JhY2sgc2tpbGwgZGlyZWN0b3J5JylcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9XG4gIH0sIFtpbnN0YWxsQ29tcGxldGUsIHNraWxsRGlyLCBpbnN0YWxsRXJyb3IsIGhhbmRsZUVycm9yXSlcblxuICAvLyBDaGVjayBmb3IgZ2VuZXJhdGVkIGZpbGUgb25jZSB3ZSBoYXZlIHNraWxsRGlyXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKCFza2lsbERpcikge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3QgZGF0YVBhdGggPSBqb2luKHNraWxsRGlyLCAneWVhcl9pbl9yZXZpZXcuanMnKVxuICAgIHZvaWQgcGF0aEV4aXN0cyhkYXRhUGF0aCkudGhlbihleGlzdHMgPT4ge1xuICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICBgQ2hlY2tpbmcgZm9yICR7ZGF0YVBhdGh9OiAke2V4aXN0cyA/ICdmb3VuZCcgOiAnbm90IGZvdW5kJ31gLFxuICAgICAgKVxuICAgICAgc2V0SGFzR2VuZXJhdGVkKGV4aXN0cylcbiAgICB9KVxuICB9LCBbc2tpbGxEaXJdKVxuXG4gIGZ1bmN0aW9uIGhhbmRsZUFjdGlvbihhY3Rpb246IEdlbmVyYXRpdmVBY3Rpb24pOiB2b2lkIHtcbiAgICAvLyBTZW5kIHByb21wdCB0byBtb2RlbCBiYXNlZCBvbiBhY3Rpb25cbiAgICBjb25zdCBwcm9tcHRzOiBSZWNvcmQ8R2VuZXJhdGl2ZUFjdGlvbiwgc3RyaW5nPiA9IHtcbiAgICAgIGVkaXQ6IEVESVRfUFJPTVBULFxuICAgICAgZml4OiBGSVhfUFJPTVBULFxuICAgICAgcmVnZW5lcmF0ZTogUkVHRU5FUkFURV9QUk9NUFQsXG4gICAgfVxuICAgIG9uRG9uZShwcm9tcHRzW2FjdGlvbl0sIHsgZGlzcGxheTogJ3VzZXInLCBzaG91bGRRdWVyeTogdHJ1ZSB9KVxuICB9XG5cbiAgaWYgKGluc3RhbGxFcnJvcikge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPkVycm9yOiB7aW5zdGFsbEVycm9yfTwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgVHJ5IHJ1bm5pbmcgL3BsdWdpbiB0byBtYW51YWxseSBpbnN0YWxsIHRoZSB0aGluay1iYWNrIHBsdWdpbi5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgaWYgKCFpbnN0YWxsQ29tcGxldGUpIHtcbiAgICByZXR1cm4gPFRoaW5rYmFja0luc3RhbGxlciBvblJlYWR5PXtoYW5kbGVSZWFkeX0gb25FcnJvcj17aGFuZGxlRXJyb3J9IC8+XG4gIH1cblxuICBpZiAoIXNraWxsRGlyIHx8IGhhc0dlbmVyYXRlZCA9PT0gbnVsbCkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94PlxuICAgICAgICA8U3Bpbm5lciAvPlxuICAgICAgICA8VGV4dD5Mb2FkaW5nIHRoaW5rYmFjayBza2lsbOKApjwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPFRoaW5rYmFja01lbnVcbiAgICAgIG9uRG9uZT17b25Eb25lfVxuICAgICAgb25BY3Rpb249e2hhbmRsZUFjdGlvbn1cbiAgICAgIHNraWxsRGlyPXtza2lsbERpcn1cbiAgICAgIGhhc0dlbmVyYXRlZD17aGFzR2VuZXJhdGVkfVxuICAgIC8+XG4gIClcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNhbGwoXG4gIG9uRG9uZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXk7IHNob3VsZFF1ZXJ5PzogYm9vbGVhbiB9LFxuICApID0+IHZvaWQsXG4pOiBQcm9taXNlPFJlYWN0LlJlYWN0Tm9kZT4ge1xuICByZXR1cm4gPFRoaW5rYmFja0Zsb3cgb25Eb25lPXtvbkRvbmV9IC8+XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxLQUFLLFFBQVEsT0FBTztBQUM3QixTQUFTQyxRQUFRLFFBQVEsYUFBYTtBQUN0QyxTQUFTQyxJQUFJLFFBQVEsTUFBTTtBQUMzQixPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUN4RCxjQUFjQyxvQkFBb0IsUUFBUSxtQkFBbUI7QUFDN0QsU0FBU0MsTUFBTSxRQUFRLHlDQUF5QztBQUNoRSxTQUFTQyxNQUFNLFFBQVEsMENBQTBDO0FBQ2pFLFNBQVNDLE9BQU8sUUFBUSw2QkFBNkI7QUFDckQsT0FBT0MsU0FBUyxNQUFNLHdCQUF3QjtBQUM5QyxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFj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 \ No newline at end of file diff --git a/commands/ultraplan.tsx b/commands/ultraplan.tsx new file mode 100644 index 0000000..17710eb --- /dev/null +++ b/commands/ultraplan.tsx @@ -0,0 +1,471 @@ +import { readFileSync } from 'fs'; +import { REMOTE_CONTROL_DISCONNECTED_MSG } from '../bridge/types.js'; +import type { Command } from '../commands.js'; +import { DIAMOND_OPEN } from '../constants/figures.js'; +import { getRemoteSessionUrl } from '../constants/product.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../services/analytics/index.js'; +import type { AppState } from '../state/AppStateStore.js'; +import { checkRemoteAgentEligibility, formatPreconditionError, RemoteAgentTask, type RemoteAgentTaskState, registerRemoteAgentTask } from '../tasks/RemoteAgentTask/RemoteAgentTask.js'; +import type { LocalJSXCommandCall } from '../types/command.js'; +import { logForDebugging } from '../utils/debug.js'; +import { errorMessage } from '../utils/errors.js'; +import { logError } from '../utils/log.js'; +import { enqueuePendingNotification } from '../utils/messageQueueManager.js'; +import { ALL_MODEL_CONFIGS } from '../utils/model/configs.js'; +import { updateTaskState } from '../utils/task/framework.js'; +import { archiveRemoteSession, teleportToRemote } from '../utils/teleport.js'; +import { pollForApprovedExitPlanMode, UltraplanPollError } from '../utils/ultraplan/ccrSession.js'; + +// TODO(prod-hardening): OAuth token may go stale over the 30min poll; +// consider refresh. + +// Multi-agent exploration is slow; 30min timeout. +const ULTRAPLAN_TIMEOUT_MS = 30 * 60 * 1000; +export const CCR_TERMS_URL = 'https://code.claude.com/docs/en/claude-code-on-the-web'; + +// CCR runs against the first-party API — use the canonical ID, not the +// provider-specific string getModelStrings() would return (which may be a +// Bedrock ARN or Vertex ID on the local CLI). Read at call time, not module +// load: the GrowthBook cache is empty at import and `/config` Gates can flip +// it between invocations. +function getUltraplanModel(): string { + return getFeatureValue_CACHED_MAY_BE_STALE('tengu_ultraplan_model', ALL_MODEL_CONFIGS.opus46.firstParty); +} + +// prompt.txt is wrapped in so the CCR browser hides +// scaffolding (CLI_BLOCK_TAGS dropped by stripSystemNotifications) +// while the model still sees full text. +// Phrasing deliberately avoids the feature name because +// the remote CCR CLI runs keyword detection on raw input before +// any tag stripping, and a bare "ultraplan" in the prompt would self-trigger as +// /ultraplan, which is filtered out of headless mode as "Unknown skill" +// +// Bundler inlines .txt as a string; the test runner wraps it as {default}. +/* eslint-disable @typescript-eslint/no-require-imports */ +const _rawPrompt = require('../utils/ultraplan/prompt.txt'); +/* eslint-enable @typescript-eslint/no-require-imports */ +const DEFAULT_INSTRUCTIONS: string = (typeof _rawPrompt === 'string' ? _rawPrompt : _rawPrompt.default).trimEnd(); + +// Dev-only prompt override resolved eagerly at module load. +// Gated to ant builds (USER_TYPE is a build-time define, +// so the override path is DCE'd from external builds). +// Shell-set env only, so top-level process.env read is fine +// — settings.env never injects this. +/* eslint-disable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs -- ant-only dev override; eager top-level read is the point (crash at startup, not silently inside the slash-command try/catch) */ +const ULTRAPLAN_INSTRUCTIONS: string = "external" === 'ant' && process.env.ULTRAPLAN_PROMPT_FILE ? readFileSync(process.env.ULTRAPLAN_PROMPT_FILE, 'utf8').trimEnd() : DEFAULT_INSTRUCTIONS; +/* eslint-enable custom-rules/no-process-env-top-level, custom-rules/no-sync-fs */ + +/** + * Assemble the initial CCR user message. seedPlan and blurb stay outside the + * system-reminder so the browser renders them; scaffolding is hidden. + */ +export function buildUltraplanPrompt(blurb: string, seedPlan?: string): string { + const parts: string[] = []; + if (seedPlan) { + parts.push('Here is a draft plan to refine:', '', seedPlan, ''); + } + parts.push(ULTRAPLAN_INSTRUCTIONS); + if (blurb) { + parts.push('', blurb); + } + return parts.join('\n'); +} +function startDetachedPoll(taskId: string, sessionId: string, url: string, getAppState: () => AppState, setAppState: (f: (prev: AppState) => AppState) => void): void { + const started = Date.now(); + let failed = false; + void (async () => { + try { + const { + plan, + rejectCount, + executionTarget + } = await pollForApprovedExitPlanMode(sessionId, ULTRAPLAN_TIMEOUT_MS, phase => { + if (phase === 'needs_input') logEvent('tengu_ultraplan_awaiting_input', {}); + updateTaskState(taskId, setAppState, t => { + if (t.status !== 'running') return t; + const next = phase === 'running' ? undefined : phase; + return t.ultraplanPhase === next ? t : { + ...t, + ultraplanPhase: next + }; + }); + }, () => getAppState().tasks?.[taskId]?.status !== 'running'); + logEvent('tengu_ultraplan_approved', { + duration_ms: Date.now() - started, + plan_length: plan.length, + reject_count: rejectCount, + execution_target: executionTarget as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + if (executionTarget === 'remote') { + // User chose "execute in CCR" in the browser PlanModal — the remote + // session is now coding. Skip archive (ARCHIVE has no running-check, + // would kill mid-execution) and skip the choice dialog (already chose). + // Guard on task status so a poll that resolves after stopUltraplan + // doesn't notify for a killed session. + const task = getAppState().tasks?.[taskId]; + if (task?.status !== 'running') return; + updateTaskState(taskId, setAppState, t => t.status !== 'running' ? t : { + ...t, + status: 'completed', + endTime: Date.now() + }); + setAppState(prev => prev.ultraplanSessionUrl === url ? { + ...prev, + ultraplanSessionUrl: undefined + } : prev); + enqueuePendingNotification({ + value: [`Ultraplan approved — executing in Claude Code on the web. Follow along at: ${url}`, '', 'Results will land as a pull request when the remote session finishes. There is nothing to do here.'].join('\n'), + mode: 'task-notification' + }); + } else { + // Teleport: set pendingChoice so REPL mounts UltraplanChoiceDialog. + // The dialog owns archive + URL clear on choice. Guard on task status + // so a poll that resolves after stopUltraplan doesn't resurrect the + // dialog for a killed session. + setAppState(prev => { + const task = prev.tasks?.[taskId]; + if (!task || task.status !== 'running') return prev; + return { + ...prev, + ultraplanPendingChoice: { + plan, + sessionId, + taskId + } + }; + }); + } + } catch (e) { + // If the task was stopped (stopUltraplan sets status=killed), the poll + // erroring is expected — skip the failure notification and cleanup + // (kill() already archived; stopUltraplan cleared the URL). + const task = getAppState().tasks?.[taskId]; + if (task?.status !== 'running') return; + failed = true; + logEvent('tengu_ultraplan_failed', { + duration_ms: Date.now() - started, + reason: (e instanceof UltraplanPollError ? e.reason : 'network_or_unknown') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + reject_count: e instanceof UltraplanPollError ? e.rejectCount : undefined + }); + enqueuePendingNotification({ + value: `Ultraplan failed: ${errorMessage(e)}\n\nSession: ${url}`, + mode: 'task-notification' + }); + // Error path owns cleanup; teleport path defers to the dialog; remote + // path handled its own cleanup above. + void archiveRemoteSession(sessionId).catch(e => logForDebugging(`ultraplan archive failed: ${String(e)}`)); + setAppState(prev => + // Compare against this poll's URL so a newer relaunched session's + // URL isn't cleared by a stale poll erroring out. + prev.ultraplanSessionUrl === url ? { + ...prev, + ultraplanSessionUrl: undefined + } : prev); + } finally { + // Remote path already set status=completed above; teleport path + // leaves status=running so the pill shows the ultraplanPhase state + // until UltraplanChoiceDialog completes the task after the user's + // choice. Setting completed here would filter the task out of + // isBackgroundTask before the pill can render the phase state. + // Failure path has no dialog, so it owns the status transition here. + if (failed) { + updateTaskState(taskId, setAppState, t => t.status !== 'running' ? t : { + ...t, + status: 'failed', + endTime: Date.now() + }); + } + } + })(); +} + +// Renders immediately so the terminal doesn't appear hung during the +// multi-second teleportToRemote round-trip. +function buildLaunchMessage(disconnectedBridge?: boolean): string { + const prefix = disconnectedBridge ? `${REMOTE_CONTROL_DISCONNECTED_MSG} ` : ''; + return `${DIAMOND_OPEN} ultraplan\n${prefix}Starting Claude Code on the web…`; +} +function buildSessionReadyMessage(url: string): string { + return `${DIAMOND_OPEN} ultraplan · Monitor progress in Claude Code on the web ${url}\nYou can continue working — when the ${DIAMOND_OPEN} fills, press ↓ to view results`; +} +function buildAlreadyActiveMessage(url: string | undefined): string { + return url ? `ultraplan: already polling. Open ${url} to check status, or wait for the plan to land here.` : 'ultraplan: already launching. Please wait for the session to start.'; +} + +/** + * Stop a running ultraplan: archive the remote session (halts it but keeps the + * URL viewable), kill the local task entry (clears the pill), and clear + * ultraplanSessionUrl (re-arms the keyword trigger). startDetachedPoll's + * shouldStop callback sees the killed status on its next tick and throws; + * the catch block early-returns when status !== 'running'. + */ +export async function stopUltraplan(taskId: string, sessionId: string, setAppState: (f: (prev: AppState) => AppState) => void): Promise { + // RemoteAgentTask.kill archives the session (with .catch) — no separate + // archive call needed here. + await RemoteAgentTask.kill(taskId, setAppState); + setAppState(prev => prev.ultraplanSessionUrl || prev.ultraplanPendingChoice || prev.ultraplanLaunching ? { + ...prev, + ultraplanSessionUrl: undefined, + ultraplanPendingChoice: undefined, + ultraplanLaunching: undefined + } : prev); + const url = getRemoteSessionUrl(sessionId, process.env.SESSION_INGRESS_URL); + enqueuePendingNotification({ + value: `Ultraplan stopped.\n\nSession: ${url}`, + mode: 'task-notification' + }); + enqueuePendingNotification({ + value: 'The user stopped the ultraplan session above. Do not respond to the stop notification — wait for their next message.', + mode: 'task-notification', + isMeta: true + }); +} + +/** + * Shared entry for the slash command, keyword trigger, and the plan-approval + * dialog's "Ultraplan" button. When seedPlan is present (dialog path), it is + * prepended as a draft to refine; blurb may be empty in that case. + * + * Resolves immediately with the user-facing message. Eligibility check, + * session creation, and task registration run detached and failures surface via + * enqueuePendingNotification. + */ +export async function launchUltraplan(opts: { + blurb: string; + seedPlan?: string; + getAppState: () => AppState; + setAppState: (f: (prev: AppState) => AppState) => void; + signal: AbortSignal; + /** True if the caller disconnected Remote Control before launching. */ + disconnectedBridge?: boolean; + /** + * Called once teleportToRemote resolves with a session URL. Callers that + * have setMessages (REPL) append this as a second transcript message so the + * URL is visible without opening the ↓ detail view. Callers without + * transcript access (ExitPlanModePermissionRequest) omit this — the pill + * still shows live status. + */ + onSessionReady?: (msg: string) => void; +}): Promise { + const { + blurb, + seedPlan, + getAppState, + setAppState, + signal, + disconnectedBridge, + onSessionReady + } = opts; + const { + ultraplanSessionUrl: active, + ultraplanLaunching + } = getAppState(); + if (active || ultraplanLaunching) { + logEvent('tengu_ultraplan_create_failed', { + reason: (active ? 'already_polling' : 'already_launching') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + return buildAlreadyActiveMessage(active); + } + if (!blurb && !seedPlan) { + // No event — bare /ultraplan is a usage query, not an attempt. + return [ + // Rendered via ; raw is tokenized as HTML + // and dropped. Backslash-escape the brackets. + 'Usage: /ultraplan \\, or include "ultraplan" anywhere', 'in your prompt', '', 'Advanced multi-agent plan mode with our most powerful model', '(Opus). Runs in Claude Code on the web. When the plan is ready,', 'you can execute it in the web session or send it back here.', 'Terminal stays free while the remote plans.', 'Requires /login.', '', `Terms: ${CCR_TERMS_URL}`].join('\n'); + } + + // Set synchronously before the detached flow to prevent duplicate launches + // during the teleportToRemote window. + setAppState(prev => prev.ultraplanLaunching ? prev : { + ...prev, + ultraplanLaunching: true + }); + void launchDetached({ + blurb, + seedPlan, + getAppState, + setAppState, + signal, + onSessionReady + }); + return buildLaunchMessage(disconnectedBridge); +} +async function launchDetached(opts: { + blurb: string; + seedPlan?: string; + getAppState: () => AppState; + setAppState: (f: (prev: AppState) => AppState) => void; + signal: AbortSignal; + onSessionReady?: (msg: string) => void; +}): Promise { + const { + blurb, + seedPlan, + getAppState, + setAppState, + signal, + onSessionReady + } = opts; + // Hoisted so the catch block can archive the remote session if an error + // occurs after teleportToRemote succeeds (avoids 30min orphan). + let sessionId: string | undefined; + try { + const model = getUltraplanModel(); + const eligibility = await checkRemoteAgentEligibility(); + if (!eligibility.eligible) { + logEvent('tengu_ultraplan_create_failed', { + reason: 'precondition' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + precondition_errors: eligibility.errors.map(e => e.type).join(',') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + const reasons = eligibility.errors.map(formatPreconditionError).join('\n'); + enqueuePendingNotification({ + value: `ultraplan: cannot launch remote session —\n${reasons}`, + mode: 'task-notification' + }); + return; + } + const prompt = buildUltraplanPrompt(blurb, seedPlan); + let bundleFailMsg: string | undefined; + const session = await teleportToRemote({ + initialMessage: prompt, + description: blurb || 'Refine local plan', + model, + permissionMode: 'plan', + ultraplan: true, + signal, + useDefaultEnvironment: true, + onBundleFail: msg => { + bundleFailMsg = msg; + } + }); + if (!session) { + logEvent('tengu_ultraplan_create_failed', { + reason: (bundleFailMsg ? 'bundle_fail' : 'teleport_null') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + enqueuePendingNotification({ + value: `ultraplan: session creation failed${bundleFailMsg ? ` — ${bundleFailMsg}` : ''}. See --debug for details.`, + mode: 'task-notification' + }); + return; + } + sessionId = session.id; + const url = getRemoteSessionUrl(session.id, process.env.SESSION_INGRESS_URL); + setAppState(prev => ({ + ...prev, + ultraplanSessionUrl: url, + ultraplanLaunching: undefined + })); + onSessionReady?.(buildSessionReadyMessage(url)); + logEvent('tengu_ultraplan_launched', { + has_seed_plan: Boolean(seedPlan), + model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + // TODO(#23985): replace registerRemoteAgentTask + startDetachedPoll with + // ExitPlanModeScanner inside startRemoteSessionPolling. + const { + taskId + } = registerRemoteAgentTask({ + remoteTaskType: 'ultraplan', + session: { + id: session.id, + title: blurb || 'Ultraplan' + }, + command: blurb, + context: { + abortController: new AbortController(), + getAppState, + setAppState + }, + isUltraplan: true + }); + startDetachedPoll(taskId, session.id, url, getAppState, setAppState); + } catch (e) { + logError(e); + logEvent('tengu_ultraplan_create_failed', { + reason: 'unexpected_error' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + enqueuePendingNotification({ + value: `ultraplan: unexpected error — ${errorMessage(e)}`, + mode: 'task-notification' + }); + if (sessionId) { + // Error after teleport succeeded — archive so the remote doesn't sit + // running for 30min with nobody polling it. + void archiveRemoteSession(sessionId).catch(err => logForDebugging('ultraplan: failed to archive orphaned session', err)); + // ultraplanSessionUrl may have been set before the throw; clear it so + // the "already polling" guard doesn't block future launches. + setAppState(prev => prev.ultraplanSessionUrl ? { + ...prev, + ultraplanSessionUrl: undefined + } : prev); + } + } finally { + // No-op on success: the url-setting setAppState already cleared this. + setAppState(prev => prev.ultraplanLaunching ? { + ...prev, + ultraplanLaunching: undefined + } : prev); + } +} +const call: LocalJSXCommandCall = async (onDone, context, args) => { + const blurb = args.trim(); + + // Bare /ultraplan (no args, no seed plan) just shows usage — no dialog. + if (!blurb) { + const msg = await launchUltraplan({ + blurb, + getAppState: context.getAppState, + setAppState: context.setAppState, + signal: context.abortController.signal + }); + onDone(msg, { + display: 'system' + }); + return null; + } + + // Guard matches launchUltraplan's own check — showing the dialog when a + // session is already active or launching would waste the user's click and set + // hasSeenUltraplanTerms before the launch fails. + const { + ultraplanSessionUrl: active, + ultraplanLaunching + } = context.getAppState(); + if (active || ultraplanLaunching) { + logEvent('tengu_ultraplan_create_failed', { + reason: (active ? 'already_polling' : 'already_launching') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + onDone(buildAlreadyActiveMessage(active), { + display: 'system' + }); + return null; + } + + // Mount the pre-launch dialog via focusedInputDialog (bottom region, like + // permission dialogs) rather than returning JSX (transcript area, anchors + // at top of scrollback). REPL.tsx handles launch/clear/cancel on choice. + context.setAppState(prev => ({ + ...prev, + ultraplanLaunchPending: { + blurb + } + })); + // 'skip' suppresses the (no content) echo — the dialog's choice handler + // adds the real /ultraplan echo + launch confirmation. + onDone(undefined, { + display: 'skip' + }); + return null; +}; +export default { + type: 'local-jsx', + name: 'ultraplan', + description: `~10–30 min · Claude Code on the web drafts an advanced plan you can edit and approve. See ${CCR_TERMS_URL}`, + argumentHint: '', + isEnabled: () => "external" === 'ant', + load: () => Promise.resolve({ + call + }) +} satisfies Command; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyZWFkRmlsZVN5bmMiLCJSRU1PVEVfQ09OVFJPTF9ESVNDT05ORUNURURfTVNHIiwiQ29tbWFuZCIsIkRJQU1PTkRfT1BFTiIsImdldFJlbW90ZVNlc3Npb25VcmwiLCJnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsIkFwcFN0YXRlIiwiY2hlY2tSZW1vdGVBZ2VudEVsaWdpYmlsaXR5IiwiZm9ybWF0UHJlY29uZGl0aW9uRXJyb3IiLCJSZW1vdGVBZ2VudFRhc2siLCJSZW1vdGVBZ2VudFRhc2tTdGF0ZSIsInJlZ2lzdGVyUmVtb3RlQWdlbnRUYXNrIiwiTG9jYWxKU1hDb21tYW5kQ2FsbCIsImxvZ0ZvckRlYnVnZ2luZyIsImVycm9yTWVzc2FnZSIsImxvZ0Vycm9yIiwiZW5xdWV1ZVBlbmRpbmdOb3RpZmljYXRpb24iLCJBTExfTU9ERUxfQ09ORklHUyIsInVwZGF0ZVRhc2tTdGF0ZSIsImFyY2hpdmVSZW1vdGVTZXNzaW9uIiwidGVsZXBvcnRUb1JlbW90ZSIsInBvbGxGb3JBcHByb3ZlZEV4aXRQbGFuTW9kZSIsIlVsdHJhcGxhblBvbGxFcnJvciIsIlVMVFJBUExBTl9USU1FT1VUX01TIiwiQ0NSX1RFUk1TX1VSTCIsImdldFVsdHJhcGxhbk1vZGVsIiwib3B1czQ2IiwiZmlyc3RQYXJ0eSIsIl9yYXdQcm9tcHQiLCJyZXF1aXJlIiwiREVGQVVMVF9JTlNUUlVDVElPTlMiLCJkZWZhdWx0IiwidHJpbUVuZCIsIlVMVFJBUExBTl9JTlNUUlVDVElPTlMiLCJwcm9jZXNzIiwiZW52IiwiVUxUUkFQTEFOX1BST01QVF9GSUxFIiwiYnVpbGRVbHRyYXBsYW5Qcm9tcHQiLCJibHVyYiIsInNlZWRQbGFuIiwicGFydHMiLCJwdXNoIiwiam9pbiIsInN0YXJ0RGV0YWNoZWRQb2xsIiwidGFza0lkIiwic2Vzc2lvbklkIiwidXJsIiwiZ2V0QXBwU3RhdGUiLCJzZXRBcHBTdGF0ZSIsImYiLCJwcmV2Iiwic3RhcnRlZCIsIkRhdGUiLCJub3ciLCJmYWlsZWQiLCJwbGFuIiwicmVqZWN0Q291bnQiLCJleGVjdXRpb25UYXJnZXQiLCJwaGFzZSIsInQiLCJzdGF0dXMiLCJuZXh0IiwidW5kZWZpbmVkIiwidWx0cmFwbGFuUGhhc2UiLCJ0YXNrcyIsImR1cmF0aW9uX21zIiwicGxhbl9sZW5ndGgiLCJsZW5ndGgiLCJyZWplY3RfY291bnQiLCJleGVjdXRpb25fdGFyZ2V0IiwidGFzayIsImVuZFRpbWUiLCJ1bHRyYXBsYW5TZXNzaW9uVXJsIiwidmFsdWUiLCJtb2RlIiwidWx0cmFwbGFuUGVuZGluZ0Nob2ljZSIsImUiLCJyZWFzb24iLCJjYXRjaCIsIlN0cmluZyIsImJ1aWxkTGF1bmNoTWVzc2FnZSIsImRpc2Nvbm5lY3RlZEJyaWRnZSIsInByZWZpeCIsImJ1aWxkU2Vzc2lvblJlYWR5TWVzc2FnZSIsImJ1aWxkQWxyZWFkeUFjdGl2ZU1lc3NhZ2UiLCJzdG9wVWx0cmFwbGFuIiwiUHJvbWlzZSIsImtpbGwiLCJ1bHRyYXBsYW5MYXVuY2hpbmciLCJTRVNTSU9OX0lOR1JFU1NfVVJMIiwiaXNNZXRhIiwibGF1bmNoVWx0cmFwbGFuIiwib3B0cyIsInNpZ25hbCIsIkFib3J0U2lnbmFsIiwib25TZXNzaW9uUmVhZHkiLCJtc2ciLCJhY3RpdmUiLCJsYXVuY2hEZXRhY2hlZCIsIm1vZGVsIiwiZWxpZ2liaWxpdHkiLCJlbGlnaWJsZSIsInByZWNvbmRpdGlvbl9lcnJvcnMiLCJlcnJvcnMiLCJtYXAiLCJ0eXBlIiwicmVhc29ucyIsInByb21wdCIsImJ1bmRsZUZhaWxNc2ciLCJzZXNzaW9uIiwiaW5pdGlhbE1lc3NhZ2UiLCJkZXNjcmlwdGlvbiIsInBlcm1pc3Npb25Nb2RlIiwidWx0cmFwbGFuIiwidXNlRGVmYXVsdEVudmlyb25tZW50Iiwib25CdW5kbGVGYWlsIiwiaWQiLCJoYXNfc2VlZF9wbGFuIiwiQm9vbGVhbiIsInJlbW90ZVRhc2tUeXBlIiwidGl0bGUiLCJjb21tYW5kIiwiY29udGV4dCIsImFib3J0Q29udHJvbGxlciIsIkFib3J0Q29udHJvbGxlciIsImlzVWx0cmFwbGFuIiwiZXJyIiwiY2FsbCIsIm9uRG9uZSIsImFyZ3MiLCJ0cmltIiwiZGlzcGxheSIsInVsdHJhcGxhbkxhdW5jaFBlbmRpbmciLCJuYW1lIiwiYXJndW1lbnRIaW50IiwiaXNFbmFibGVkIiwibG9hZCIsInJlc29sdmUiXSwic291cmNlcyI6WyJ1bHRyYXBsYW4udHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHJlYWRGaWxlU3luYyB9IGZyb20gJ2ZzJ1xuaW1wb3J0IHsgUkVNT1RFX0NPTlRST0xfRElTQ09OTkVDVEVEX01TRyB9IGZyb20gJy4uL2JyaWRnZS90eXBlcy5qcydcbmltcG9ydCB0eXBlIHsgQ29tbWFuZCB9IGZyb20gJy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgRElBTU9ORF9PUEVOIH0gZnJvbSAnLi4vY29uc3RhbnRzL2ZpZ3VyZXMuanMnXG5pbXBvcnQgeyBnZXRSZW1vdGVTZXNzaW9uVXJsIH0gZnJvbSAnLi4vY29uc3RhbnRzL3Byb2R1Y3QuanMnXG5pbXBvcnQgeyBnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSB9IGZyb20gJy4uL3NlcnZpY2VzL2FuYWx5dGljcy9ncm93dGhib29rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHR5cGUgeyBBcHBTdGF0ZSB9IGZyb20gJy4uL3N0YXRlL0FwcFN0YXRlU3RvcmUuanMnXG5pbXBvcnQge1xuICBjaGVja1JlbW90ZUFnZW50RWxpZ2liaWxpdHksXG4gIGZvcm1hdFByZWNvbmRpdGlvbkVycm9yLFxuICBSZW1vdGVBZ2VudFRhc2ssXG4gIHR5cGUgUmVtb3RlQWdlbnRUYXNrU3RhdGUsXG4gIHJlZ2lzdGVyUmVtb3RlQWdlbnRUYXNrLFxufSBmcm9tICcuLi90YXNrcy9SZW1vdGVBZ2VudFRhc2svUmVtb3RlQWdlbnRUYXNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vdHlwZXMvY29tbWFuZC5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHsgZXJyb3JNZXNzYWdlIH0gZnJvbSAnLi4vdXRpbHMvZXJyb3JzLmpzJ1xuaW1wb3J0IHsgbG9nRXJyb3IgfSBmcm9tICcuLi91dGlscy9sb2cuanMnXG5pbXBvcnQgeyBlbnF1ZXVlUGVuZGluZ05vdGlmaWNhdGlvbiB9IGZyb20gJy4uL3V0aWxzL21lc3NhZ2VRdWV1ZU1hbmFnZXIuanMnXG5pbXBvcnQgeyBBTExfTU9ERUxfQ09ORklHUyB9IGZyb20gJy4uL3V0aWxzL21vZGVsL2NvbmZpZ3MuanMnXG5pbXBvcnQgeyB1cGRhdGVUYXNrU3RhdGUgfSBmcm9tICcuLi91dGlscy90YXNrL2ZyYW1ld29yay5qcydcbmltcG9ydCB7IGFyY2hpdmVSZW1vdGVTZXNzaW9uLCB0ZWxlcG9ydFRvUmVtb3RlIH0gZnJvbSAnLi4vdXRpbHMvdGVsZXBvcnQuanMnXG5pbXBvcnQge1xuICBwb2xsRm9yQXBwcm92ZWRFeGl0UGxhbk1vZGUsXG4gIFVsdHJhcGxhblBvbGxFcnJvcixcbn0gZnJvbSAnLi4vdXRpbHMvdWx0cmFwbGFuL2NjclNlc3Npb24uanMnXG5cbi8vIFRPRE8ocHJvZC1oYXJkZW5pbmcpOiBPQXV0aCB0b2tlbiBtYXkgZ28gc3RhbGUgb3ZlciB0aGUgMzBtaW4gcG9sbDtcbi8vIGNvbnNpZGVyIHJlZnJlc2guXG5cbi8vIE11bHRpLWFnZW50IGV4cGxvcmF0aW9uIGlzIHNsb3c7IDMwbWluIHRpbWVvdXQuXG5jb25zdCBVTFRSQVBMQU5fVElNRU9VVF9NUyA9IDMwICogNjAgKiAxMDAwXG5cbmV4cG9ydCBjb25zdCBDQ1JfVEVSTVNfVVJMID1cbiAgJ2h0dHBzOi8vY29kZS5jbGF1ZGUuY29tL2RvY3MvZW4vY2xhdWRlLWNvZGUtb24tdGhlLXdlYidcblxuLy8gQ0NSIHJ1bnMgYWdhaW5zdCB0aGUgZmlyc3QtcGFydHkgQVBJIOKAlCB1c2UgdGhlIGNhbm9uaWNhbCBJRCwgbm90IHRoZVxuLy8gcHJvdmlkZXItc3BlY2lmaWMgc3RyaW5nIGdldE1vZGVsU3RyaW5ncygpIHdvdWxkIHJldHVybiAod2hpY2ggbWF5IGJlIGFcbi8vIEJlZHJvY2sgQVJOIG9yIFZlcnRleCBJRCBvbiB0aGUgbG9jYWwgQ0xJKS4gUmVhZCBhdCBjYWxsIHRpbWUsIG5vdCBtb2R1bGVcbi8vIGxvYWQ6IHRoZSBHcm93dGhCb29rIGNhY2hlIGlzIGVtcHR5IGF0IGltcG9ydCBhbmQgYC9jb25maWdgIEdhdGVzIGNhbiBmbGlwXG4vLyBpdCBiZXR3ZWVuIGludm9jYXRpb25zLlxuZnVuY3Rpb24gZ2V0VWx0cmFwbGFuTW9kZWwoKTogc3RyaW5nIHtcbiAgcmV0dXJuIGdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFKFxuICAgICd0ZW5ndV91bHRyYXBsYW5fbW9kZWwnLFxuICAgIEFMTF9NT0RFTF9DT05GSUdTLm9wdXM0Ni5maXJzdFBhcnR5LFxuICApXG59XG5cbi8vIHByb21wdC50eHQgaXMgd3JhcHBlZCBpbiA8c3lzdGVtLXJlbWluZGVyPiBzbyB0aGUgQ0NSIGJyb3dzZXIgaGlkZXNcbi8vIHNjYWZmb2xkaW5nIChDTElfQkxPQ0tfVEFHUyBkcm9wcGVkIGJ5IHN0cmlwU3lzdGVtTm90aWZpY2F0aW9ucylcbi8vIHdoaWxlIHRoZSBtb2RlbCBzdGlsbCBzZWVzIGZ1bGwgdGV4dC5cbi8vIFBocmFzaW5nIGRlbGliZXJhdGVseSBhdm9pZHMgdGhlIGZlYXR1cmUgbmFtZSBiZWNhdXNlXG4vLyB0aGUgcmVtb3RlIENDUiBDTEkgcnVucyBrZXl3b3JkIGRldGVjdGlvbiBvbiByYXcgaW5wdXQgYmVmb3JlXG4vLyBhbnkgdGFnIHN0cmlwcGluZywgYW5kIGEgYmFyZSBcInVsdHJhcGxhblwiIGluIHRoZSBwcm9tcHQgd291bGQgc2VsZi10cmlnZ2VyIGFzXG4vLyAvdWx0cmFwbGFuLCB3aGljaCBpcyBmaWx0ZXJlZCBvdXQgb2YgaGVhZGxlc3MgbW9kZSBhcyBcIlVua25vd24gc2tpbGxcIlxuLy9cbi8vIEJ1bmRsZXIgaW5saW5lcyAudHh0IGFzIGEgc3RyaW5nOyB0aGUgdGVzdCBydW5uZXIgd3JhcHMgaXQgYXMge2RlZmF1bHR9LlxuLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuY29uc3QgX3Jhd1Byb21wdCA9IHJlcXVpcmUoJy4uL3V0aWxzL3VsdHJhcGxhbi9wcm9tcHQudHh0Jylcbi8qIGVzbGludC1lbmFibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuY29uc3QgREVGQVVMVF9JTlNUUlVDVElPTlM6IHN0cmluZyA9IChcbiAgdHlwZW9mIF9yYXdQcm9tcHQgPT09ICdzdHJpbmcnID8gX3Jhd1Byb21wdCA6IF9yYXdQcm9tcHQuZGVmYXVsdFxuKS50cmltRW5kKClcblxuLy8gRGV2LW9ubHkgcHJvbXB0IG92ZXJyaWRlIHJlc29sdmVkIGVhZ2VybHkgYXQgbW9kdWxlIGxvYWQuXG4vLyBHYXRlZCB0byBhbnQgYnVpbGRzIChVU0VSX1RZUEUgaXMgYSBidWlsZC10aW1lIGRlZmluZSxcbi8vIHNvIHRoZSBvdmVycmlkZSBwYXRoIGlzIERDRSdkIGZyb20gZXh0ZXJuYWwgYnVpbGRzKS5cbi8vIFNoZWxsLXNldCBlbnYgb25seSwgc28gdG9wLWxldmVsIHByb2Nlc3MuZW52IHJlYWQgaXMgZmluZVxuLy8g4oCUIHNldHRpbmdzLmVudiBuZXZlciBpbmplY3RzIHRoaXMuXG4vKiBlc2xpbnQtZGlzYWJsZSBjdXN0b20tcnVsZXMvbm8tcHJvY2Vzcy1lbnYtdG9wLWxldmVsLCBjdXN0b20tcnVsZXMvbm8tc3luYy1mcyAtLSBhbnQtb25seSBkZXYgb3ZlcnJpZGU7IGVhZ2VyIHRvcC1sZXZlbCByZWFkIGlzIHRoZSBwb2ludCAoY3Jhc2ggYXQgc3RhcnR1cCwgbm90IHNpbGVudGx5IGluc2lkZSB0aGUgc2xhc2gtY29tbWFuZCB0cnkvY2F0Y2gpICovXG5jb25zdCBVTFRSQVBMQU5fSU5TVFJVQ1RJT05TOiBzdHJpbmcgPVxuICBcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIHByb2Nlc3MuZW52LlVMVFJBUExBTl9QUk9NUFRfRklMRVxuICAgID8gcmVhZEZpbGVTeW5jKHByb2Nlc3MuZW52LlVMVFJBUExBTl9QUk9NUFRfRklMRSwgJ3V0ZjgnKS50cmltRW5kKClcbiAgICA6IERFRkFVTFRfSU5TVFJVQ1RJT05TXG4vKiBlc2xpbnQtZW5hYmxlIGN1c3RvbS1ydWxlcy9uby1wcm9jZXNzLWVudi10b3AtbGV2ZWwsIGN1c3RvbS1ydWxlcy9uby1zeW5jLWZzICovXG5cbi8qKlxuICogQXNzZW1ibGUgdGhlIGluaXRpYWwgQ0NSIHVzZXIgbWVzc2FnZS4gc2VlZFBsYW4gYW5kIGJsdXJiIHN0YXkgb3V0c2lkZSB0aGVcbiAqIHN5c3RlbS1yZW1pbmRlciBzbyB0aGUgYnJvd3NlciByZW5kZXJzIHRoZW07IHNjYWZmb2xkaW5nIGlzIGhpZGRlbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkVWx0cmFwbGFuUHJvbXB0KGJsdXJiOiBzdHJpbmcsIHNlZWRQbGFuPzogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgcGFydHM6IHN0cmluZ1tdID0gW11cbiAgaWYgKHNlZWRQbGFuKSB7XG4gICAgcGFydHMucHVzaCgnSGVyZSBpcyBhIGRyYWZ0IHBsYW4gdG8gcmVmaW5lOicsICcnLCBzZWVkUGxhbiwgJycpXG4gIH1cbiAgcGFydHMucHVzaChVTFRSQVBMQU5fSU5TVFJVQ1RJT05TKVxuICBpZiAoYmx1cmIpIHtcbiAgICBwYXJ0cy5wdXNoKCcnLCBibHVyYilcbiAgfVxuICByZXR1cm4gcGFydHMuam9pbignXFxuJylcbn1cblxuZnVuY3Rpb24gc3RhcnREZXRhY2hlZFBvbGwoXG4gIHRhc2tJZDogc3RyaW5nLFxuICBzZXNzaW9uSWQ6IHN0cmluZyxcbiAgdXJsOiBzdHJpbmcsXG4gIGdldEFwcFN0YXRlOiAoKSA9PiBBcHBTdGF0ZSxcbiAgc2V0QXBwU3RhdGU6IChmOiAocHJldjogQXBwU3RhdGUpID0+IEFwcFN0YXRlKSA9PiB2b2lkLFxuKTogdm9pZCB7XG4gIGNvbnN0IHN0YXJ0ZWQgPSBEYXRlLm5vdygpXG4gIGxldCBmYWlsZWQgPSBmYWxzZVxuICB2b2lkIChhc3luYyAoKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHsgcGxhbiwgcmVqZWN0Q291bnQsIGV4ZWN1dGlvblRhcmdldCB9ID1cbiAgICAgICAgYXdhaXQgcG9sbEZvckFwcHJvdmVkRXhpdFBsYW5Nb2RlKFxuICAgICAgICAgIHNlc3Npb25JZCxcbiAgICAgICAgICBVTFRSQVBMQU5fVElNRU9VVF9NUyxcbiAgICAgICAgICBwaGFzZSA9PiB7XG4gICAgICAgICAgICBpZiAocGhhc2UgPT09ICduZWVkc19pbnB1dCcpXG4gICAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV91bHRyYXBsYW5fYXdhaXRpbmdfaW5wdXQnLCB7fSlcbiAgICAgICAgICAgIHVwZGF0ZVRhc2tTdGF0ZTxSZW1vdGVBZ2VudFRhc2tTdGF0ZT4odGFza0lkLCBzZXRBcHBTdGF0ZSwgdCA9PiB7XG4gICAgICAgICAgICAgIGlmICh0LnN0YXR1cyAhPT0gJ3J1bm5pbmcnKSByZXR1cm4gdFxuICAgICAgICAgICAgICBjb25zdCBuZXh0ID0gcGhhc2UgPT09ICdydW5uaW5nJyA/IHVuZGVmaW5lZCA6IHBoYXNlXG4gICAgICAgICAgICAgIHJldHVybiB0LnVsdHJhcGxhblBoYXNlID09PSBuZXh0XG4gICAgICAgICAgICAgICAgPyB0XG4gICAgICAgICAgICAgICAgOiB7IC4uLnQsIHVsdHJhcGxhblBoYXNlOiBuZXh0IH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgfSxcbiAgICAgICAgICAoKSA9PiBnZXRBcHBTdGF0ZSgpLnRhc2tzPy5bdGFza0lkXT8uc3RhdHVzICE9PSAncnVubmluZycsXG4gICAgICAgIClcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV91bHRyYXBsYW5fYXBwcm92ZWQnLCB7XG4gICAgICAgIGR1cmF0aW9uX21zOiBEYXRlLm5vdygpIC0gc3RhcnRlZCxcbiAgICAgICAgcGxhbl9sZW5ndGg6IHBsYW4ubGVuZ3RoLFxuICAgICAgICByZWplY3RfY291bnQ6IHJlamVjdENvdW50LFxuICAgICAgICBleGVjdXRpb25fdGFyZ2V0OlxuICAgICAgICAgIGV4ZWN1dGlvblRhcmdldCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcbiAgICAgIGlmIChleGVjdXRpb25UYXJnZXQgPT09ICdyZW1vdGUnKSB7XG4gICAgICAgIC8vIFVzZXIgY2hvc2UgXCJleGVjdXRlIGluIENDUlwiIGluIHRoZSBicm93c2VyIFBsYW5Nb2RhbCDigJQgdGhlIHJlbW90ZVxuICAgICAgICAvLyBzZXNzaW9uIGlzIG5vdyBjb2RpbmcuIFNraXAgYXJjaGl2ZSAoQVJDSElWRSBoYXMgbm8gcnVubmluZy1jaGVjayxcbiAgICAgICAgLy8gd291bGQga2lsbCBtaWQtZXhlY3V0aW9uKSBhbmQgc2tpcCB0aGUgY2hvaWNlIGRpYWxvZyAoYWxyZWFkeSBjaG9zZSkuXG4gICAgICAgIC8vIEd1YXJkIG9uIHRhc2sgc3RhdHVzIHNvIGEgcG9sbCB0aGF0IHJlc29sdmVzIGFmdGVyIHN0b3BVbHRyYXBsYW5cbiAgICAgICAgLy8gZG9lc24ndCBub3RpZnkgZm9yIGEga2lsbGVkIHNlc3Npb24uXG4gICAgICAgIGNvbnN0IHRhc2sgPSBnZXRBcHBTdGF0ZSgpLnRhc2tzPy5bdGFza0lkXVxuICAgICAgICBpZiAodGFzaz8uc3RhdHVzICE9PSAncnVubmluZycpIHJldHVyblxuICAgICAgICB1cGRhdGVUYXNrU3RhdGU8UmVtb3RlQWdlbnRUYXNrU3RhdGU+KHRhc2tJZCwgc2V0QXBwU3RhdGUsIHQgPT5cbiAgICAgICAgICB0LnN0YXR1cyAhPT0gJ3J1bm5pbmcnXG4gICAgICAgICAgICA/IHRcbiAgICAgICAgICAgIDogeyAuLi50LCBzdGF0dXM6ICdjb21wbGV0ZWQnLCBlbmRUaW1lOiBEYXRlLm5vdygpIH0sXG4gICAgICAgIClcbiAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PlxuICAgICAgICAgIHByZXYudWx0cmFwbGFuU2Vzc2lvblVybCA9PT0gdXJsXG4gICAgICAgICAgICA/IHsgLi4ucHJldiwgdWx0cmFwbGFuU2Vzc2lvblVybDogdW5kZWZpbmVkIH1cbiAgICAgICAgICAgIDogcHJldixcbiAgICAgICAgKVxuICAgICAgICBlbnF1ZXVlUGVuZGluZ05vdGlmaWNhdGlvbih7XG4gICAgICAgICAgdmFsdWU6IFtcbiAgICAgICAgICAgIGBVbHRyYXBsYW4gYXBwcm92ZWQg4oCUIGV4ZWN1dGluZyBpbiBDbGF1ZGUgQ29kZSBvbiB0aGUgd2ViLiBGb2xsb3cgYWxvbmcgYXQ6ICR7dXJsfWAsXG4gICAgICAgICAgICAnJyxcbiAgICAgICAgICAgICdSZXN1bHRzIHdpbGwgbGFuZCBhcyBhIHB1bGwgcmVxdWVzdCB3aGVuIHRoZSByZW1vdGUgc2Vzc2lvbiBmaW5pc2hlcy4gVGhlcmUgaXMgbm90aGluZyB0byBkbyBoZXJlLicsXG4gICAgICAgICAgXS5qb2luKCdcXG4nKSxcbiAgICAgICAgICBtb2RlOiAndGFzay1ub3RpZmljYXRpb24nLFxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gVGVsZXBvcnQ6IHNldCBwZW5kaW5nQ2hvaWNlIHNvIFJFUEwgbW91bnRzIFVsdHJhcGxhbkNob2ljZURpYWxvZy5cbiAgICAgICAgLy8gVGhlIGRpYWxvZyBvd25zIGFyY2hpdmUgKyBVUkwgY2xlYXIgb24gY2hvaWNlLiBHdWFyZCBvbiB0YXNrIHN0YXR1c1xuICAgICAgICAvLyBzbyBhIHBvbGwgdGhhdCByZXNvbHZlcyBhZnRlciBzdG9wVWx0cmFwbGFuIGRvZXNuJ3QgcmVzdXJyZWN0IHRoZVxuICAgICAgICAvLyBkaWFsb2cgZm9yIGEga2lsbGVkIHNlc3Npb24uXG4gICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgIGNvbnN0IHRhc2sgPSBwcmV2LnRhc2tzPy5bdGFza0lkXVxuICAgICAgICAgIGlmICghdGFzayB8fCB0YXNrLnN0YXR1cyAhPT0gJ3J1bm5pbmcnKSByZXR1cm4gcHJldlxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgdWx0cmFwbGFuUGVuZGluZ0Nob2ljZTogeyBwbGFuLCBzZXNzaW9uSWQsIHRhc2tJZCB9LFxuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICAvLyBJZiB0aGUgdGFzayB3YXMgc3RvcHBlZCAoc3RvcFVsdHJhcGxhbiBzZXRzIHN0YXR1cz1raWxsZWQpLCB0aGUgcG9sbFxuICAgICAgLy8gZXJyb3JpbmcgaXMgZXhwZWN0ZWQg4oCUIHNraXAgdGhlIGZhaWx1cmUgbm90aWZpY2F0aW9uIGFuZCBjbGVhbnVwXG4gICAgICAvLyAoa2lsbCgpIGFscmVhZHkgYXJjaGl2ZWQ7IHN0b3BVbHRyYXBsYW4gY2xlYXJlZCB0aGUgVVJMKS5cbiAgICAgIGNvbnN0IHRhc2sgPSBnZXRBcHBTdGF0ZSgpLnRhc2tzPy5bdGFza0lkXVxuICAgICAgaWYgKHRhc2s/LnN0YXR1cyAhPT0gJ3J1bm5pbmcnKSByZXR1cm5cbiAgICAgIGZhaWxlZCA9IHRydWVcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV91bHRyYXBsYW5fZmFpbGVkJywge1xuICAgICAgICBkdXJhdGlvbl9tczogRGF0ZS5ub3coKSAtIHN0YXJ0ZWQsXG4gICAgICAgIHJlYXNvbjogKGUgaW5zdGFuY2VvZiBVbHRyYXBsYW5Qb2xsRXJyb3JcbiAgICAgICAgICA/IGUucmVhc29uXG4gICAgICAgICAgOiAnbmV0d29ya19vcl91bmtub3duJykgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgcmVqZWN0X2NvdW50OlxuICAgICAgICAgIGUgaW5zdGFuY2VvZiBVbHRyYXBsYW5Qb2xsRXJyb3IgPyBlLnJlamVjdENvdW50IDogdW5kZWZpbmVkLFxuICAgICAgfSlcbiAgICAgIGVucXVldWVQZW5kaW5nTm90aWZpY2F0aW9uKHtcbiAgICAgICAgdmFsdWU6IGBVbHRyYXBsYW4gZmFpbGVkOiAke2Vycm9yTWVzc2FnZShlKX1cXG5cXG5TZXNzaW9uOiAke3VybH1gLFxuICAgICAgICBtb2RlOiAndGFzay1ub3RpZmljYXRpb24nLFxuICAgICAgfSlcbiAgICAgIC8vIEVycm9yIHBhdGggb3ducyBjbGVhbnVwOyB0ZWxlcG9ydCBwYXRoIGRlZmVycyB0byB0aGUgZGlhbG9nOyByZW1vdGVcbiAgICAgIC8vIHBhdGggaGFuZGxlZCBpdHMgb3duIGNsZWFudXAgYWJvdmUuXG4gICAgICB2b2lkIGFyY2hpdmVSZW1vdGVTZXNzaW9uKHNlc3Npb25JZCkuY2F0Y2goZSA9PlxuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoYHVsdHJhcGxhbiBhcmNoaXZlIGZhaWxlZDogJHtTdHJpbmcoZSl9YCksXG4gICAgICApXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+XG4gICAgICAgIC8vIENvbXBhcmUgYWdhaW5zdCB0aGlzIHBvbGwncyBVUkwgc28gYSBuZXdlciByZWxhdW5jaGVkIHNlc3Npb24nc1xuICAgICAgICAvLyBVUkwgaXNuJ3QgY2xlYXJlZCBieSBhIHN0YWxlIHBvbGwgZXJyb3Jpbmcgb3V0LlxuICAgICAgICBwcmV2LnVsdHJhcGxhblNlc3Npb25VcmwgPT09IHVybFxuICAgICAgICAgID8geyAuLi5wcmV2LCB1bHRyYXBsYW5TZXNzaW9uVXJsOiB1bmRlZmluZWQgfVxuICAgICAgICAgIDogcHJldixcbiAgICAgIClcbiAgICB9IGZpbmFsbHkge1xuICAgICAgLy8gUmVtb3RlIHBhdGggYWxyZWFkeSBzZXQgc3RhdHVzPWNvbXBsZXRlZCBhYm92ZTsgdGVsZXBvcnQgcGF0aFxuICAgICAgLy8gbGVhdmVzIHN0YXR1cz1ydW5uaW5nIHNvIHRoZSBwaWxsIHNob3dzIHRoZSB1bHRyYXBsYW5QaGFzZSBzdGF0ZVxuICAgICAgLy8gdW50aWwgVWx0cmFwbGFuQ2hvaWNlRGlhbG9nIGNvbXBsZXRlcyB0aGUgdGFzayBhZnRlciB0aGUgdXNlcidzXG4gICAgICAvLyBjaG9pY2UuIFNldHRpbmcgY29tcGxldGVkIGhlcmUgd291bGQgZmlsdGVyIHRoZSB0YXNrIG91dCBvZlxuICAgICAgLy8gaXNCYWNrZ3JvdW5kVGFzayBiZWZvcmUgdGhlIHBpbGwgY2FuIHJlbmRlciB0aGUgcGhhc2Ugc3RhdGUuXG4gICAgICAvLyBGYWlsdXJlIHBhdGggaGFzIG5vIGRpYWxvZywgc28gaXQgb3ducyB0aGUgc3RhdHVzIHRyYW5zaXRpb24gaGVyZS5cbiAgICAgIGlmIChmYWlsZWQpIHtcbiAgICAgICAgdXBkYXRlVGFza1N0YXRlPFJlbW90ZUFnZW50VGFza1N0YXRlPih0YXNrSWQsIHNldEFwcFN0YXRlLCB0ID0+XG4gICAgICAgICAgdC5zdGF0dXMgIT09ICdydW5uaW5nJ1xuICAgICAgICAgICAgPyB0XG4gICAgICAgICAgICA6IHsgLi4udCwgc3RhdHVzOiAnZmFpbGVkJywgZW5kVGltZTogRGF0ZS5ub3coKSB9LFxuICAgICAgICApXG4gICAgICB9XG4gICAgfVxuICB9KSgpXG59XG5cbi8vIFJlbmRlcnMgaW1tZWRpYXRlbHkgc28gdGhlIHRlcm1pbmFsIGRvZXNuJ3QgYXBwZWFyIGh1bmcgZHVyaW5nIHRoZVxuLy8gbXVsdGktc2Vjb25kIHRlbGVwb3J0VG9SZW1vdGUgcm91bmQtdHJpcC5cbmZ1bmN0aW9uIGJ1aWxkTGF1bmNoTWVzc2FnZShkaXNjb25uZWN0ZWRCcmlkZ2U/OiBib29sZWFuKTogc3RyaW5nIHtcbiAgY29uc3QgcHJlZml4ID0gZGlzY29ubmVjdGVkQnJpZGdlID8gYCR7UkVNT1RFX0NPTlRST0xfRElTQ09OTkVDVEVEX01TR30gYCA6ICcnXG4gIHJldHVybiBgJHtESUFNT05EX09QRU59IHVsdHJhcGxhblxcbiR7cHJlZml4fVN0YXJ0aW5nIENsYXVkZSBDb2RlIG9uIHRoZSB3ZWLigKZgXG59XG5cbmZ1bmN0aW9uIGJ1aWxkU2Vzc2lvblJlYWR5TWVzc2FnZSh1cmw6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBgJHtESUFNT05EX09QRU59IHVsdHJhcGxhbiDCtyBNb25pdG9yIHByb2dyZXNzIGluIENsYXVkZSBDb2RlIG9uIHRoZSB3ZWIgJHt1cmx9XFxuWW91IGNhbiBjb250aW51ZSB3b3JraW5nIOKAlCB3aGVuIHRoZSAke0RJQU1PTkRfT1BFTn0gZmlsbHMsIHByZXNzIOKGkyB0byB2aWV3IHJlc3VsdHNgXG59XG5cbmZ1bmN0aW9uIGJ1aWxkQWxyZWFkeUFjdGl2ZU1lc3NhZ2UodXJsOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBzdHJpbmcge1xuICByZXR1cm4gdXJsXG4gICAgPyBgdWx0cmFwbGFuOiBhbHJlYWR5IHBvbGxpbmcuIE9wZW4gJHt1cmx9IHRvIGNoZWNrIHN0YXR1cywgb3Igd2FpdCBmb3IgdGhlIHBsYW4gdG8gbGFuZCBoZXJlLmBcbiAgICA6ICd1bHRyYXBsYW46IGFscmVhZHkgbGF1bmNoaW5nLiBQbGVhc2Ugd2FpdCBmb3IgdGhlIHNlc3Npb24gdG8gc3RhcnQuJ1xufVxuXG4vKipcbiAqIFN0b3AgYSBydW5uaW5nIHVsdHJhcGxhbjogYXJjaGl2ZSB0aGUgcmVtb3RlIHNlc3Npb24gKGhhbHRzIGl0IGJ1dCBrZWVwcyB0aGVcbiAqIFVSTCB2aWV3YWJsZSksIGtpbGwgdGhlIGxvY2FsIHRhc2sgZW50cnkgKGNsZWFycyB0aGUgcGlsbCksIGFuZCBjbGVhclxuICogdWx0cmFwbGFuU2Vzc2lvblVybCAocmUtYXJtcyB0aGUga2V5d29yZCB0cmlnZ2VyKS4gc3RhcnREZXRhY2hlZFBvbGwnc1xuICogc2hvdWxkU3RvcCBjYWxsYmFjayBzZWVzIHRoZSBraWxsZWQgc3RhdHVzIG9uIGl0cyBuZXh0IHRpY2sgYW5kIHRocm93cztcbiAqIHRoZSBjYXRjaCBibG9jayBlYXJseS1yZXR1cm5zIHdoZW4gc3RhdHVzICE9PSAncnVubmluZycuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdG9wVWx0cmFwbGFuKFxuICB0YXNrSWQ6IHN0cmluZyxcbiAgc2Vzc2lvbklkOiBzdHJpbmcsXG4gIHNldEFwcFN0YXRlOiAoZjogKHByZXY6IEFwcFN0YXRlKSA9PiBBcHBTdGF0ZSkgPT4gdm9pZCxcbik6IFByb21pc2U8dm9pZD4ge1xuICAvLyBSZW1vdGVBZ2VudFRhc2sua2lsbCBhcmNoaXZlcyB0aGUgc2Vzc2lvbiAod2l0aCAuY2F0Y2gpIOKAlCBubyBzZXBhcmF0ZVxuICAvLyBhcmNoaXZlIGNhbGwgbmVlZGVkIGhlcmUuXG4gIGF3YWl0IFJlbW90ZUFnZW50VGFzay5raWxsKHRhc2tJZCwgc2V0QXBwU3RhdGUpXG4gIHNldEFwcFN0YXRlKHByZXYgPT5cbiAgICBwcmV2LnVsdHJhcGxhblNlc3Npb25VcmwgfHxcbiAgICBwcmV2LnVsdHJhcGxhblBlbmRpbmdDaG9pY2UgfHxcbiAgICBwcmV2LnVsdHJhcGxhbkxhdW5jaGluZ1xuICAgICAgPyB7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICB1bHRyYXBsYW5TZXNzaW9uVXJsOiB1bmRlZmluZWQsXG4gICAgICAgICAgdWx0cmFwbGFuUGVuZGluZ0Nob2ljZTogdW5kZWZpbmVkLFxuICAgICAgICAgIHVsdHJhcGxhbkxhdW5jaGluZzogdW5kZWZpbmVkLFxuICAgICAgICB9XG4gICAgICA6IHByZXYsXG4gIClcbiAgY29uc3QgdXJsID0gZ2V0UmVtb3RlU2Vzc2lvblVybChzZXNzaW9uSWQsIHByb2Nlc3MuZW52LlNFU1NJT05fSU5HUkVTU19VUkwpXG4gIGVucXVldWVQZW5kaW5nTm90aWZpY2F0aW9uKHtcbiAgICB2YWx1ZTogYFVsdHJhcGxhbiBzdG9wcGVkLlxcblxcblNlc3Npb246ICR7dXJsfWAsXG4gICAgbW9kZTogJ3Rhc2stbm90aWZpY2F0aW9uJyxcbiAgfSlcbiAgZW5xdWV1ZVBlbmRpbmdOb3RpZmljYXRpb24oe1xuICAgIHZhbHVlOlxuICAgICAgJ1RoZSB1c2VyIHN0b3BwZWQgdGhlIHVsdHJhcGxhbiBzZXNzaW9uIGFib3ZlLiBEbyBub3QgcmVzcG9uZCB0byB0aGUgc3RvcCBub3RpZmljYXRpb24g4oCUIHdhaXQgZm9yIHRoZWlyIG5leHQgbWVzc2FnZS4nLFxuICAgIG1vZGU6ICd0YXNrLW5vdGlmaWNhdGlvbicsXG4gICAgaXNNZXRhOiB0cnVlLFxuICB9KVxufVxuXG4vKipcbiAqIFNoYXJlZCBlbnRyeSBmb3IgdGhlIHNsYXNoIGNvbW1hbmQsIGtleXdvcmQgdHJpZ2dlciwgYW5kIHRoZSBwbGFuLWFwcHJvdmFsXG4gKiBkaWFsb2cncyBcIlVsdHJhcGxhblwiIGJ1dHRvbi4gV2hlbiBzZWVkUGxhbiBpcyBwcmVzZW50IChkaWFsb2cgcGF0aCksIGl0IGlzXG4gKiBwcmVwZW5kZWQgYXMgYSBkcmFmdCB0byByZWZpbmU7IGJsdXJiIG1heSBiZSBlbXB0eSBpbiB0aGF0IGNhc2UuXG4gKlxuICogUmVzb2x2ZXMgaW1tZWRpYXRlbHkgd2l0aCB0aGUgdXNlci1mYWNpbmcgbWVzc2FnZS4gRWxpZ2liaWxpdHkgY2hlY2ssXG4gKiBzZXNzaW9uIGNyZWF0aW9uLCBhbmQgdGFzayByZWdpc3RyYXRpb24gcnVuIGRldGFjaGVkIGFuZCBmYWlsdXJlcyBzdXJmYWNlIHZpYVxuICogZW5xdWV1ZVBlbmRpbmdOb3RpZmljYXRpb24uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsYXVuY2hVbHRyYXBsYW4ob3B0czoge1xuICBibHVyYjogc3RyaW5nXG4gIHNlZWRQbGFuPzogc3RyaW5nXG4gIGdldEFwcFN0YXRlOiAoKSA9PiBBcHBTdGF0ZVxuICBzZXRBcHBTdGF0ZTogKGY6IChwcmV2OiBBcHBTdGF0ZSkgPT4gQXBwU3RhdGUpID0+IHZvaWRcbiAgc2lnbmFsOiBBYm9ydFNpZ25hbFxuICAvKiogVHJ1ZSBpZiB0aGUgY2FsbGVyIGRpc2Nvbm5lY3RlZCBSZW1vdGUgQ29udHJvbCBiZWZvcmUgbGF1bmNoaW5nLiAqL1xuICBkaXNjb25uZWN0ZWRCcmlkZ2U/OiBib29sZWFuXG4gIC8qKlxuICAgKiBDYWxsZWQgb25jZSB0ZWxlcG9ydFRvUmVtb3RlIHJlc29sdmVzIHdpdGggYSBzZXNzaW9uIFVSTC4gQ2FsbGVycyB0aGF0XG4gICAqIGhhdmUgc2V0TWVzc2FnZXMgKFJFUEwpIGFwcGVuZCB0aGlzIGFzIGEgc2Vjb25kIHRyYW5zY3JpcHQgbWVzc2FnZSBzbyB0aGVcbiAgICogVVJMIGlzIHZpc2libGUgd2l0aG91dCBvcGVuaW5nIHRoZSDihpMgZGV0YWlsIHZpZXcuIENhbGxlcnMgd2l0aG91dFxuICAgKiB0cmFuc2NyaXB0IGFjY2VzcyAoRXhpdFBsYW5Nb2RlUGVybWlzc2lvblJlcXVlc3QpIG9taXQgdGhpcyDigJQgdGhlIHBpbGxcbiAgICogc3RpbGwgc2hvd3MgbGl2ZSBzdGF0dXMuXG4gICAqL1xuICBvblNlc3Npb25SZWFkeT86IChtc2c6IHN0cmluZykgPT4gdm9pZFxufSk6IFByb21pc2U8c3RyaW5nPiB7XG4gIGNvbnN0IHtcbiAgICBibHVyYixcbiAgICBzZWVkUGxhbixcbiAgICBnZXRBcHBTdGF0ZSxcbiAgICBzZXRBcHBTdGF0ZSxcbiAgICBzaWduYWwsXG4gICAgZGlzY29ubmVjdGVkQnJpZGdlLFxuICAgIG9uU2Vzc2lvblJlYWR5LFxuICB9ID0gb3B0c1xuXG4gIGNvbnN0IHsgdWx0cmFwbGFuU2Vzc2lvblVybDogYWN0aXZlLCB1bHRyYXBsYW5MYXVuY2hpbmcgfSA9IGdldEFwcFN0YXRlKClcbiAgaWYgKGFjdGl2ZSB8fCB1bHRyYXBsYW5MYXVuY2hpbmcpIHtcbiAgICBsb2dFdmVudCgndGVuZ3VfdWx0cmFwbGFuX2NyZWF0ZV9mYWlsZWQnLCB7XG4gICAgICByZWFzb246IChhY3RpdmVcbiAgICAgICAgPyAnYWxyZWFkeV9wb2xsaW5nJ1xuICAgICAgICA6ICdhbHJlYWR5X2xhdW5jaGluZycpIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgfSlcbiAgICByZXR1cm4gYnVpbGRBbHJlYWR5QWN0aXZlTWVzc2FnZShhY3RpdmUpXG4gIH1cblxuICBpZiAoIWJsdXJiICYmICFzZWVkUGxhbikge1xuICAgIC8vIE5vIGV2ZW50IOKAlCBiYXJlIC91bHRyYXBsYW4gaXMgYSB1c2FnZSBxdWVyeSwgbm90IGFuIGF0dGVtcHQuXG4gICAgcmV0dXJuIFtcbiAgICAgIC8vIFJlbmRlcmVkIHZpYSA8TWFya2Rvd24+OyByYXcgPG1lc3NhZ2U+IGlzIHRva2VuaXplZCBhcyBIVE1MXG4gICAgICAvLyBhbmQgZHJvcHBlZC4gQmFja3NsYXNoLWVzY2FwZSB0aGUgYnJhY2tldHMuXG4gICAgICAnVXNhZ2U6IC91bHRyYXBsYW4gXFxcXDxwcm9tcHRcXFxcPiwgb3IgaW5jbHVkZSBcInVsdHJhcGxhblwiIGFueXdoZXJlJyxcbiAgICAgICdpbiB5b3VyIHByb21wdCcsXG4gICAgICAnJyxcbiAgICAgICdBZHZhbmNlZCBtdWx0aS1hZ2VudCBwbGFuIG1vZGUgd2l0aCBvdXIgbW9zdCBwb3dlcmZ1bCBtb2RlbCcsXG4gICAgICAnKE9wdXMpLiBSdW5zIGluIENsYXVkZSBDb2RlIG9uIHRoZSB3ZWIuIFdoZW4gdGhlIHBsYW4gaXMgcmVhZHksJyxcbiAgICAgICd5b3UgY2FuIGV4ZWN1dGUgaXQgaW4gdGhlIHdlYiBzZXNzaW9uIG9yIHNlbmQgaXQgYmFjayBoZXJlLicsXG4gICAgICAnVGVybWluYWwgc3RheXMgZnJlZSB3aGlsZSB0aGUgcmVtb3RlIHBsYW5zLicsXG4gICAgICAnUmVxdWlyZXMgL2xvZ2luLicsXG4gICAgICAnJyxcbiAgICAgIGBUZXJtczogJHtDQ1JfVEVSTVNfVVJMfWAsXG4gICAgXS5qb2luKCdcXG4nKVxuICB9XG5cbiAgLy8gU2V0IHN5bmNocm9ub3VzbHkgYmVmb3JlIHRoZSBkZXRhY2hlZCBmbG93IHRvIHByZXZlbnQgZHVwbGljYXRlIGxhdW5jaGVzXG4gIC8vIGR1cmluZyB0aGUgdGVsZXBvcnRUb1JlbW90ZSB3aW5kb3cuXG4gIHNldEFwcFN0YXRlKHByZXYgPT5cbiAgICBwcmV2LnVsdHJhcGxhbkxhdW5jaGluZyA/IHByZXYgOiB7IC4uLnByZXYsIHVsdHJhcGxhbkxhdW5jaGluZzogdHJ1ZSB9LFxuICApXG4gIHZvaWQgbGF1bmNoRGV0YWNoZWQoe1xuICAgIGJsdXJiLFxuICAgIHNlZWRQbGFuLFxuICAgIGdldEFwcFN0YXRlLFxuICAgIHNldEFwcFN0YXRlLFxuICAgIHNpZ25hbCxcbiAgICBvblNlc3Npb25SZWFkeSxcbiAgfSlcbiAgcmV0dXJuIGJ1aWxkTGF1bmNoTWVzc2FnZShkaXNjb25uZWN0ZWRCcmlkZ2UpXG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxhdW5jaERldGFjaGVkKG9wdHM6IHtcbiAgYmx1cmI6IHN0cmluZ1xuICBzZWVkUGxhbj86IHN0cmluZ1xuICBnZXRBcHBTdGF0ZTogKCkgPT4gQXBwU3RhdGVcbiAgc2V0QXBwU3RhdGU6IChmOiAocHJldjogQXBwU3RhdGUpID0+IEFwcFN0YXRlKSA9PiB2b2lkXG4gIHNpZ25hbDogQWJvcnRTaWduYWxcbiAgb25TZXNzaW9uUmVhZHk/OiAobXNnOiBzdHJpbmcpID0+IHZvaWRcbn0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3QgeyBibHVyYiwgc2VlZFBsYW4sIGdldEFwcFN0YXRlLCBzZXRBcHBTdGF0ZSwgc2lnbmFsLCBvblNlc3Npb25SZWFkeSB9ID1cbiAgICBvcHRzXG4gIC8vIEhvaXN0ZWQgc28gdGhlIGNhdGNoIGJsb2NrIGNhbiBhcmNoaXZlIHRoZSByZW1vdGUgc2Vzc2lvbiBpZiBhbiBlcnJvclxuICAvLyBvY2N1cnMgYWZ0ZXIgdGVsZXBvcnRUb1JlbW90ZSBzdWNjZWVkcyAoYXZvaWRzIDMwbWluIG9ycGhhbikuXG4gIGxldCBzZXNzaW9uSWQ6IHN0cmluZyB8IHVuZGVmaW5lZFxuICB0cnkge1xuICAgIGNvbnN0IG1vZGVsID0gZ2V0VWx0cmFwbGFuTW9kZWwoKVxuXG4gICAgY29uc3QgZWxpZ2liaWxpdHkgPSBhd2FpdCBjaGVja1JlbW90ZUFnZW50RWxpZ2liaWxpdHkoKVxuICAgIGlmICghZWxpZ2liaWxpdHkuZWxpZ2libGUpIHtcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV91bHRyYXBsYW5fY3JlYXRlX2ZhaWxlZCcsIHtcbiAgICAgICAgcmVhc29uOlxuICAgICAgICAgICdwcmVjb25kaXRpb24nIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHByZWNvbmRpdGlvbl9lcnJvcnM6IGVsaWdpYmlsaXR5LmVycm9yc1xuICAgICAgICAgIC5tYXAoZSA9PiBlLnR5cGUpXG4gICAgICAgICAgLmpvaW4oXG4gICAgICAgICAgICAnLCcsXG4gICAgICAgICAgKSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcbiAgICAgIGNvbnN0IHJlYXNvbnMgPSBlbGlnaWJpbGl0eS5lcnJvcnMubWFwKGZvcm1hdFByZWNvbmRpdGlvbkVycm9yKS5qb2luKCdcXG4nKVxuICAgICAgZW5xdWV1ZVBlbmRpbmdOb3RpZmljYXRpb24oe1xuICAgICAgICB2YWx1ZTogYHVsdHJhcGxhbjogY2Fubm90IGxhdW5jaCByZW1vdGUgc2Vzc2lvbiDigJRcXG4ke3JlYXNvbnN9YCxcbiAgICAgICAgbW9kZTogJ3Rhc2stbm90aWZpY2F0aW9uJyxcbiAgICAgIH0pXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjb25zdCBwcm9tcHQgPSBidWlsZFVsdHJhcGxhblByb21wdChibHVyYiwgc2VlZFBsYW4pXG4gICAgbGV0IGJ1bmRsZUZhaWxNc2c6IHN0cmluZyB8IHVuZGVmaW5lZFxuICAgIGNvbnN0IHNlc3Npb24gPSBhd2FpdCB0ZWxlcG9ydFRvUmVtb3RlKHtcbiAgICAgIGluaXRpYWxNZXNzYWdlOiBwcm9tcHQsXG4gICAgICBkZXNjcmlwdGlvbjogYmx1cmIgfHwgJ1JlZmluZSBsb2NhbCBwbGFuJyxcbiAgICAgIG1vZGVsLFxuICAgICAgcGVybWlzc2lvbk1vZGU6ICdwbGFuJyxcbiAgICAgIHVsdHJhcGxhbjogdHJ1ZSxcbiAgICAgIHNpZ25hbCxcbiAgICAgIHVzZURlZmF1bHRFbnZpcm9ubWVudDogdHJ1ZSxcbiAgICAgIG9uQnVuZGxlRmFpbDogbXNnID0+IHtcbiAgICAgICAgYnVuZGxlRmFpbE1zZyA9IG1zZ1xuICAgICAgfSxcbiAgICB9KVxuICAgIGlmICghc2Vzc2lvbikge1xuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3VsdHJhcGxhbl9jcmVhdGVfZmFpbGVkJywge1xuICAgICAgICByZWFzb246IChidW5kbGVGYWlsTXNnXG4gICAgICAgICAgPyAnYnVuZGxlX2ZhaWwnXG4gICAgICAgICAgOiAndGVsZXBvcnRfbnVsbCcpIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KVxuICAgICAgZW5xdWV1ZVBlbmRpbmdOb3RpZmljYXRpb24oe1xuICAgICAgICB2YWx1ZTogYHVsdHJhcGxhbjogc2Vzc2lvbiBjcmVhdGlvbiBmYWlsZWQke2J1bmRsZUZhaWxNc2cgPyBgIOKAlCAke2J1bmRsZUZhaWxNc2d9YCA6ICcnfS4gU2VlIC0tZGVidWcgZm9yIGRldGFpbHMuYCxcbiAgICAgICAgbW9kZTogJ3Rhc2stbm90aWZpY2F0aW9uJyxcbiAgICAgIH0pXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgc2Vzc2lvbklkID0gc2Vzc2lvbi5pZFxuXG4gICAgY29uc3QgdXJsID0gZ2V0UmVtb3RlU2Vzc2lvblVybChzZXNzaW9uLmlkLCBwcm9jZXNzLmVudi5TRVNTSU9OX0lOR1JFU1NfVVJMKVxuICAgIHNldEFwcFN0YXRlKHByZXYgPT4gKHtcbiAgICAgIC4uLnByZXYsXG4gICAgICB1bHRyYXBsYW5TZXNzaW9uVXJsOiB1cmwsXG4gICAgICB1bHRyYXBsYW5MYXVuY2hpbmc6IHVuZGVmaW5lZCxcbiAgICB9KSlcbiAgICBvblNlc3Npb25SZWFkeT8uKGJ1aWxkU2Vzc2lvblJlYWR5TWVzc2FnZSh1cmwpKVxuICAgIGxvZ0V2ZW50KCd0ZW5ndV91bHRyYXBsYW5fbGF1bmNoZWQnLCB7XG4gICAgICBoYXNfc2VlZF9wbGFuOiBCb29sZWFuKHNlZWRQbGFuKSxcbiAgICAgIG1vZGVsOlxuICAgICAgICBtb2RlbCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgIH0pXG4gICAgLy8gVE9ETygjMjM5ODUpOiByZXBsYWNlIHJlZ2lzdGVyUmVtb3RlQWdlbnRUYXNrICsgc3RhcnREZXRhY2hlZFBvbGwgd2l0aFxuICAgIC8vIEV4aXRQbGFuTW9kZVNjYW5uZXIgaW5zaWRlIHN0YXJ0UmVtb3RlU2Vzc2lvblBvbGxpbmcuXG4gICAgY29uc3QgeyB0YXNrSWQgfSA9IHJlZ2lzdGVyUmVtb3RlQWdlbnRUYXNrKHtcbiAgICAgIHJlbW90ZVRhc2tUeXBlOiAndWx0cmFwbGFuJyxcbiAgICAgIHNlc3Npb246IHsgaWQ6IHNlc3Npb24uaWQsIHRpdGxlOiBibHVyYiB8fCAnVWx0cmFwbGFuJyB9LFxuICAgICAgY29tbWFuZDogYmx1cmIsXG4gICAgICBjb250ZXh0OiB7XG4gICAgICAgIGFib3J0Q29udHJvbGxlcjogbmV3IEFib3J0Q29udHJvbGxlcigpLFxuICAgICAgICBnZXRBcHBTdGF0ZSxcbiAgICAgICAgc2V0QXBwU3RhdGUsXG4gICAgICB9LFxuICAgICAgaXNVbHRyYXBsYW46IHRydWUsXG4gICAgfSlcbiAgICBzdGFydERldGFjaGVkUG9sbCh0YXNrSWQsIHNlc3Npb24uaWQsIHVybCwgZ2V0QXBwU3RhdGUsIHNldEFwcFN0YXRlKVxuICB9IGNhdGNoIChlKSB7XG4gICAgbG9nRXJyb3IoZSlcbiAgICBsb2dFdmVudCgndGVuZ3VfdWx0cmFwbGFuX2NyZWF0ZV9mYWlsZWQnLCB7XG4gICAgICByZWFzb246XG4gICAgICAgICd1bmV4cGVjdGVkX2Vycm9yJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgIH0pXG4gICAgZW5xdWV1ZVBlbmRpbmdOb3RpZmljYXRpb24oe1xuICAgICAgdmFsdWU6IGB1bHRyYXBsYW46IHVuZXhwZWN0ZWQgZXJyb3Ig4oCUICR7ZXJyb3JNZXNzYWdlKGUpfWAsXG4gICAgICBtb2RlOiAndGFzay1ub3RpZmljYXRpb24nLFxuICAgIH0pXG4gICAgaWYgKHNlc3Npb25JZCkge1xuICAgICAgLy8gRXJyb3IgYWZ0ZXIgdGVsZXBvcnQgc3VjY2VlZGVkIOKAlCBhcmNoaXZlIHNvIHRoZSByZW1vdGUgZG9lc24ndCBzaXRcbiAgICAgIC8vIHJ1bm5pbmcgZm9yIDMwbWluIHdpdGggbm9ib2R5IHBvbGxpbmcgaXQuXG4gICAgICB2b2lkIGFyY2hpdmVSZW1vdGVTZXNzaW9uKHNlc3Npb25JZCkuY2F0Y2goZXJyID0+XG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZygndWx0cmFwbGFuOiBmYWlsZWQgdG8gYXJjaGl2ZSBvcnBoYW5lZCBzZXNzaW9uJywgZXJyKSxcbiAgICAgIClcbiAgICAgIC8vIHVsdHJhcGxhblNlc3Npb25VcmwgbWF5IGhhdmUgYmVlbiBzZXQgYmVmb3JlIHRoZSB0aHJvdzsgY2xlYXIgaXQgc29cbiAgICAgIC8vIHRoZSBcImFscmVhZHkgcG9sbGluZ1wiIGd1YXJkIGRvZXNuJ3QgYmxvY2sgZnV0dXJlIGxhdW5jaGVzLlxuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PlxuICAgICAgICBwcmV2LnVsdHJhcGxhblNlc3Npb25VcmxcbiAgICAgICAgICA/IHsgLi4ucHJldiwgdWx0cmFwbGFuU2Vzc2lvblVybDogdW5kZWZpbmVkIH1cbiAgICAgICAgICA6IHByZXYsXG4gICAgICApXG4gICAgfVxuICB9IGZpbmFsbHkge1xuICAgIC8vIE5vLW9wIG9uIHN1Y2Nlc3M6IHRoZSB1cmwtc2V0dGluZyBzZXRBcHBTdGF0ZSBhbHJlYWR5IGNsZWFyZWQgdGhpcy5cbiAgICBzZXRBcHBTdGF0ZShwcmV2ID0+XG4gICAgICBwcmV2LnVsdHJhcGxhbkxhdW5jaGluZ1xuICAgICAgICA/IHsgLi4ucHJldiwgdWx0cmFwbGFuTGF1bmNoaW5nOiB1bmRlZmluZWQgfVxuICAgICAgICA6IHByZXYsXG4gICAgKVxuICB9XG59XG5cbmNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0LCBhcmdzKSA9PiB7XG4gIGNvbnN0IGJsdXJiID0gYXJncy50cmltKClcblxuICAvLyBCYXJlIC91bHRyYXBsYW4gKG5vIGFyZ3MsIG5vIHNlZWQgcGxhbikganVzdCBzaG93cyB1c2FnZSDigJQgbm8gZGlhbG9nLlxuICBpZiAoIWJsdXJiKSB7XG4gICAgY29uc3QgbXNnID0gYXdhaXQgbGF1bmNoVWx0cmFwbGFuKHtcbiAgICAgIGJsdXJiLFxuICAgICAgZ2V0QXBwU3RhdGU6IGNvbnRleHQuZ2V0QXBwU3RhdGUsXG4gICAgICBzZXRBcHBTdGF0ZTogY29udGV4dC5zZXRBcHBTdGF0ZSxcbiAgICAgIHNpZ25hbDogY29udGV4dC5hYm9ydENvbnRyb2xsZXIuc2lnbmFsLFxuICAgIH0pXG4gICAgb25Eb25lKG1zZywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBHdWFyZCBtYXRjaGVzIGxhdW5jaFVsdHJhcGxhbidzIG93biBjaGVjayDigJQgc2hvd2luZyB0aGUgZGlhbG9nIHdoZW4gYVxuICAvLyBzZXNzaW9uIGlzIGFscmVhZHkgYWN0aXZlIG9yIGxhdW5jaGluZyB3b3VsZCB3YXN0ZSB0aGUgdXNlcidzIGNsaWNrIGFuZCBzZXRcbiAgLy8gaGFzU2VlblVsdHJhcGxhblRlcm1zIGJlZm9yZSB0aGUgbGF1bmNoIGZhaWxzLlxuICBjb25zdCB7IHVsdHJhcGxhblNlc3Npb25Vcmw6IGFjdGl2ZSwgdWx0cmFwbGFuTGF1bmNoaW5nIH0gPVxuICAgIGNvbnRleHQuZ2V0QXBwU3RhdGUoKVxuICBpZiAoYWN0aXZlIHx8IHVsdHJhcGxhbkxhdW5jaGluZykge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV91bHRyYXBsYW5fY3JlYXRlX2ZhaWxlZCcsIHtcbiAgICAgIHJlYXNvbjogKGFjdGl2ZVxuICAgICAgICA/ICdhbHJlYWR5X3BvbGxpbmcnXG4gICAgICAgIDogJ2FscmVhZHlfbGF1bmNoaW5nJykgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICB9KVxuICAgIG9uRG9uZShidWlsZEFscmVhZHlBY3RpdmVNZXNzYWdlKGFjdGl2ZSksIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gTW91bnQgdGhlIHByZS1sYXVuY2ggZGlhbG9nIHZpYSBmb2N1c2VkSW5wdXREaWFsb2cgKGJvdHRvbSByZWdpb24sIGxpa2VcbiAgLy8gcGVybWlzc2lvbiBkaWFsb2dzKSByYXRoZXIgdGhhbiByZXR1cm5pbmcgSlNYICh0cmFuc2NyaXB0IGFyZWEsIGFuY2hvcnNcbiAgLy8gYXQgdG9wIG9mIHNjcm9sbGJhY2spLiBSRVBMLnRzeCBoYW5kbGVzIGxhdW5jaC9jbGVhci9jYW5jZWwgb24gY2hvaWNlLlxuICBjb250ZXh0LnNldEFwcFN0YXRlKHByZXYgPT4gKHsgLi4ucHJldiwgdWx0cmFwbGFuTGF1bmNoUGVuZGluZzogeyBibHVyYiB9IH0pKVxuICAvLyAnc2tpcCcgc3VwcHJlc3NlcyB0aGUgKG5vIGNvbnRlbnQpIGVjaG8g4oCUIHRoZSBkaWFsb2cncyBjaG9pY2UgaGFuZGxlclxuICAvLyBhZGRzIHRoZSByZWFsIC91bHRyYXBsYW4gZWNobyArIGxhdW5jaCBjb25maXJtYXRpb24uXG4gIG9uRG9uZSh1bmRlZmluZWQsIHsgZGlzcGxheTogJ3NraXAnIH0pXG4gIHJldHVybiBudWxsXG59XG5cbmV4cG9ydCBkZWZhdWx0IHtcbiAgdHlwZTogJ2xvY2FsLWpzeCcsXG4gIG5hbWU6ICd1bHRyYXBsYW4nLFxuICBkZXNjcmlwdGlvbjogYH4xMOKAkzMwIG1pbiDCtyBDbGF1ZGUgQ29kZSBvbiB0aGUgd2ViIGRyYWZ0cyBhbiBhZHZhbmNlZCBwbGFuIHlvdSBjYW4gZWRpdCBhbmQgYXBwcm92ZS4gU2VlICR7Q0NSX1RFUk1TX1VSTH1gLFxuICBhcmd1bWVudEhpbnQ6ICc8cHJvbXB0PicsXG4gIGlzRW5hYmxlZDogKCkgPT4gXCJleHRlcm5hbFwiID09PSAnYW50JyxcbiAgbG9hZDogKCkgPT4gUHJvbWlzZS5yZXNvbHZlKHsgY2FsbCB9KSxcbn0gc2F0aXNmaWVzIENvbW1hbmRcbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsWUFBWSxRQUFRLElBQUk7QUFDakMsU0FBU0MsK0JBQStCLFFBQVEsb0JBQW9CO0FBQ3BFLGNBQWNDLE9BQU8sUUFBUSxnQkFBZ0I7QUFDN0MsU0FBU0MsWUFBWSxRQUFRLHlCQUF5QjtBQUN0RCxTQUFTQyxtQkFBbUIsUUFBUSx5QkFBeUI7QUFDN0QsU0FBU0MsbUNBQW1DLFFBQVEscUNBQXFDO0FBQ3pGLFNBQ0UsS0FBS0MsMERBQTBELEVBQy9EQyxRQUFRLFFBQ0gsZ0NBQWdDO0FBQ3ZDLGNBQWNDLFFBQVEsUUFBUSwyQkFBMkI7QUFDekQsU0FDRUMsMkJBQTJCLEVBQzNCQyx1QkFBdUIsRUFDdkJDLGVBQWUsRUFDZixLQUFLQyxvQkFBb0IsRUFDekJDLHVCQUF1QixRQUNsQiw2Q0FBNkM7QUFDcEQsY0FBY0MsbUJBQW1CLFFBQVEscUJBQXFCO0FBQzlELFNBQVNDLGVBQWUsUUFB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 \ No newline at end of file diff --git a/commands/upgrade/index.ts b/commands/upgrade/index.ts new file mode 100644 index 0000000..63dc5ff --- /dev/null +++ b/commands/upgrade/index.ts @@ -0,0 +1,16 @@ +import type { Command } from '../../commands.js' +import { getSubscriptionType } from '../../utils/auth.js' +import { isEnvTruthy } from '../../utils/envUtils.js' + +const upgrade = { + type: 'local-jsx', + name: 'upgrade', + description: 'Upgrade to Max for higher rate limits and more Opus', + availability: ['claude-ai'], + isEnabled: () => + !isEnvTruthy(process.env.DISABLE_UPGRADE_COMMAND) && + getSubscriptionType() !== 'enterprise', + load: () => import('./upgrade.js'), +} satisfies Command + +export default upgrade diff --git a/commands/upgrade/upgrade.tsx b/commands/upgrade/upgrade.tsx new file mode 100644 index 0000000..1daf73d --- /dev/null +++ b/commands/upgrade/upgrade.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import type { LocalJSXCommandContext } from '../../commands.js'; +import { getOauthProfileFromOauthToken } from '../../services/oauth/getOauthProfile.js'; +import type { LocalJSXCommandOnDone } from '../../types/command.js'; +import { getClaudeAIOAuthTokens, isClaudeAISubscriber } from '../../utils/auth.js'; +import { openBrowser } from '../../utils/browser.js'; +import { logError } from '../../utils/log.js'; +import { Login } from '../login/login.js'; +export async function call(onDone: LocalJSXCommandOnDone, context: LocalJSXCommandContext): Promise { + try { + // Check if user is already on the highest Max plan (20x) + if (isClaudeAISubscriber()) { + const tokens = getClaudeAIOAuthTokens(); + let isMax20x = false; + if (tokens?.subscriptionType && tokens?.rateLimitTier) { + isMax20x = tokens.subscriptionType === 'max' && tokens.rateLimitTier === 'default_claude_max_20x'; + } else if (tokens?.accessToken) { + const profile = await getOauthProfileFromOauthToken(tokens.accessToken); + isMax20x = profile?.organization?.organization_type === 'claude_max' && profile?.organization?.rate_limit_tier === 'default_claude_max_20x'; + } + if (isMax20x) { + setTimeout(onDone, 0, 'You are already on the highest Max subscription plan. For additional usage, run /login to switch to an API usage-billed account.'); + return null; + } + } + const url = 'https://claude.ai/upgrade/max'; + await openBrowser(url); + return { + context.onChangeAPIKey(); + onDone(success ? 'Login successful' : 'Login interrupted'); + }} />; + } catch (error) { + logError(error as Error); + setTimeout(onDone, 0, 'Failed to open browser. Please visit https://claude.ai/upgrade/max to upgrade.'); + } + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJnZXRPYXV0aFByb2ZpbGVGcm9tT2F1dGhUb2tlbiIsIkxvY2FsSlNYQ29tbWFuZE9uRG9uZSIsImdldENsYXVkZUFJT0F1dGhUb2tlbnMiLCJpc0NsYXVkZUFJU3Vic2NyaWJlciIsIm9wZW5Ccm93c2VyIiwibG9nRXJyb3IiLCJMb2dpbiIsImNhbGwiLCJvbkRvbmUiLCJjb250ZXh0IiwiUHJvbWlzZSIsIlJlYWN0Tm9kZSIsInRva2VucyIsImlzTWF4MjB4Iiwic3Vic2NyaXB0aW9uVHlwZSIsInJhdGVMaW1pdFRpZXIiLCJhY2Nlc3NUb2tlbiIsInByb2ZpbGUiLCJvcmdhbml6YXRpb24iLCJvcmdhbml6YXRpb25fdHlwZSIsInJhdGVfbGltaXRfdGllciIsInNldFRpbWVvdXQiLCJ1cmwiLCJzdWNjZXNzIiwib25DaGFuZ2VBUElLZXkiLCJlcnJvciIsIkVycm9yIl0sInNvdXJjZXMiOlsidXBncmFkZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IExvY2FsSlNYQ29tbWFuZENvbnRleHQgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IGdldE9hdXRoUHJvZmlsZUZyb21PYXV0aFRva2VuIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvb2F1dGgvZ2V0T2F1dGhQcm9maWxlLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRPbkRvbmUgfSBmcm9tICcuLi8uLi90eXBlcy9jb21tYW5kLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0Q2xhdWRlQUlPQXV0aFRva2VucyxcbiAgaXNDbGF1ZGVBSVN1YnNjcmliZXIsXG59IGZyb20gJy4uLy4uL3V0aWxzL2F1dGguanMnXG5pbXBvcnQgeyBvcGVuQnJvd3NlciB9IGZyb20gJy4uLy4uL3V0aWxzL2Jyb3dzZXIuanMnXG5pbXBvcnQgeyBsb2dFcnJvciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7IExvZ2luIH0gZnJvbSAnLi4vbG9naW4vbG9naW4uanMnXG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjYWxsKFxuICBvbkRvbmU6IExvY2FsSlNYQ29tbWFuZE9uRG9uZSxcbiAgY29udGV4dDogTG9jYWxKU1hDb21tYW5kQ29udGV4dCxcbik6IFByb21pc2U8UmVhY3QuUmVhY3ROb2RlIHwgbnVsbD4ge1xuICB0cnkge1xuICAgIC8vIENoZWNrIGlmIHVzZXIgaXMgYWxyZWFkeSBvbiB0aGUgaGlnaGVzdCBNYXggcGxhbiAoMjB4KVxuICAgIGlmIChpc0NsYXVkZUFJU3Vic2NyaWJlcigpKSB7XG4gICAgICBjb25zdCB0b2tlbnMgPSBnZXRDbGF1ZGVBSU9BdXRoVG9rZW5zKClcbiAgICAgIGxldCBpc01heDIweCA9IGZhbHNlXG5cbiAgICAgIGlmICh0b2tlbnM/LnN1YnNjcmlwdGlvblR5cGUgJiYgdG9rZW5zPy5yYXRlTGltaXRUaWVyKSB7XG4gICAgICAgIGlzTWF4MjB4ID1cbiAgICAgICAgICB0b2tlbnMuc3Vic2NyaXB0aW9uVHlwZSA9PT0gJ21heCcgJiZcbiAgICAgICAgICB0b2tlbnMucmF0ZUxpbWl0VGllciA9PT0gJ2RlZmF1bHRfY2xhdWRlX21heF8yMHgnXG4gICAgICB9IGVsc2UgaWYgKHRva2Vucz8uYWNjZXNzVG9rZW4pIHtcbiAgICAgICAgY29uc3QgcHJvZmlsZSA9IGF3YWl0IGdldE9hdXRoUHJvZmlsZUZyb21PYXV0aFRva2VuKHRva2Vucy5hY2Nlc3NUb2tlbilcbiAgICAgICAgaXNNYXgyMHggPVxuICAgICAgICAgIHByb2ZpbGU/Lm9yZ2FuaXphdGlvbj8ub3JnYW5pemF0aW9uX3R5cGUgPT09ICdjbGF1ZGVfbWF4JyAmJlxuICAgICAgICAgIHByb2ZpbGU/Lm9yZ2FuaXphdGlvbj8ucmF0ZV9saW1pdF90aWVyID09PSAnZGVmYXVsdF9jbGF1ZGVfbWF4XzIweCdcbiAgICAgIH1cblxuICAgICAgaWYgKGlzTWF4MjB4KSB7XG4gICAgICAgIHNldFRpbWVvdXQoXG4gICAgICAgICAgb25Eb25lLFxuICAgICAgICAgIDAsXG4gICAgICAgICAgJ1lvdSBhcmUgYWxyZWFkeSBvbiB0aGUgaGlnaGVzdCBNYXggc3Vic2NyaXB0aW9uIHBsYW4uIEZvciBhZGRpdGlvbmFsIHVzYWdlLCBydW4gL2xvZ2luIHRvIHN3aXRjaCB0byBhbiBBUEkgdXNhZ2UtYmlsbGVkIGFjY291bnQuJyxcbiAgICAgICAgKVxuICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHVybCA9ICdodHRwczovL2NsYXVkZS5haS91cGdyYWRlL21heCdcbiAgICBhd2FpdCBvcGVuQnJvd3Nlcih1cmwpXG5cbiAgICByZXR1cm4gKFxuICAgICAgPExvZ2luXG4gICAgICAgIHN0YXJ0aW5nTWVzc2FnZT17XG4gICAgICAgICAgJ1N0YXJ0aW5nIG5ldyBsb2dpbiBmb2xsb3dpbmcgL3VwZ3JhZGUuIEV4aXQgd2l0aCBDdHJsLUMgdG8gdXNlIGV4aXN0aW5nIGFjY291bnQuJ1xuICAgICAgICB9XG4gICAgICAgIG9uRG9uZT17c3VjY2VzcyA9PiB7XG4gICAgICAgICAgY29udGV4dC5vbkNoYW5nZUFQSUtleSgpXG4gICAgICAgICAgb25Eb25lKHN1Y2Nlc3MgPyAnTG9naW4gc3VjY2Vzc2Z1bCcgOiAnTG9naW4gaW50ZXJydXB0ZWQnKVxuICAgICAgICB9fVxuICAgICAgLz5cbiAgICApXG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nRXJyb3IoZXJyb3IgYXMgRXJyb3IpXG4gICAgc2V0VGltZW91dChcbiAgICAgIG9uRG9uZSxcbiAgICAgIDAsXG4gICAgICAnRmFpbGVkIHRvIG9wZW4gYnJvd3Nlci4gUGxlYXNlIHZpc2l0IGh0dHBzOi8vY2xhdWRlLmFpL3VwZ3JhZGUvbWF4IHRvIHVwZ3JhZGUuJyxcbiAgICApXG4gIH1cbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixjQUFjQyxzQkFBc0IsUUFBUSxtQkFBbUI7QUFDL0QsU0FBU0MsNkJBQTZCLFFBQVEseUNBQXlDO0FBQ3ZGLGNBQWNDLHFCQUFxQixRQUFRLHdCQUF3QjtBQUNuRSxTQUNFQyxzQkFBc0IsRUFDdEJDLG9CQUFvQixRQUNmLHFCQUFxQjtBQUM1QixTQUFTQyxXQUFXLFFBQVEsd0JBQXdCO0FBQ3BELFNBQVNDLFFBQVEsUUFBUSxvQkFBb0I7QUFDN0MsU0FBU0MsS0FBSyxRQUFRLG1CQUFtQjtBQUV6QyxPQUFPLGVBQWVDLElBQUlBLENBQ3hCQyxNQUFNLEVBQUVQLHFCQUFxQixFQUM3QlEsT0FBTyxFQUFFVixzQkFBc0IsQ0FDaEMsRUFBRVcsT0FBTyxDQUFDWixLQUFLLENBQUNhLFNBQVMsR0FBRyxJQUFJLENBQUMsQ0FBQztFQUNqQyxJQUFJO0lBQ0Y7SUFDQSxJQUFJUixvQkFBb0IsQ0FBQyxDQUFDLEVBQUU7TUFDMUIsTUFBTVMsTUFBTSxHQUFHVixzQkFBc0IsQ0FBQyxDQUFDO01BQ3ZDLElBQUlXLFFBQVEsR0FBRyxLQUFLO01BRXBCLElBQUlELE1BQU0sRUFBRUUsZ0JBQWdCLElBQUlGLE1BQU0sRUFBRUcsYUFBYSxFQUFFO1FBQ3JERixRQUFRLEdBQ05ELE1BQU0sQ0FBQ0UsZ0JBQWdCLEtBQUssS0FBSyxJQUNqQ0YsTUFBTSxDQUFDRyxhQUFhLEtBQUssd0JBQXdCO01BQ3JELENBQUMsTUFBTSxJQUFJSCxNQUFNLEVBQUVJLFdBQVcsRUFBRTtRQUM5QixNQUFNQyxPQUFPLEdBQUcsTUFBTWpCLDZCQUE2QixDQUFDWSxNQUFNLENBQUNJLFdBQVcsQ0FBQztRQUN2RUgsUUFBUSxHQUNOSSxPQUFPLEVBQUVDLFlBQVksRUFBRUMsaUJBQWlCLEtBQUssWUFBWSxJQUN6REYsT0FBTyxFQUFFQyxZQUFZLEVBQUVFLGVBQWUsS0FBSyx3QkFBd0I7TUFDdkU7TUFFQSxJQUFJUCxRQUFRLEVBQUU7UUFDWlEsVUFBVSxDQUNSYixNQUFNLEVBQ04sQ0FBQyxFQUNELGtJQUNGLENBQUM7UUFDRCxPQUFPLElBQUk7TUFDYjtJQUNGO0lBRUEsTUFBTWMsR0FBRyxHQUFHLCtCQUErQjtJQUMzQyxNQUFNbEIsV0FBVyxDQUFDa0IsR0FBRyxDQUFDO0lBRXRCLE9BQ0UsQ0FBQyxLQUFLLENBQ0osZUFBZSxDQUFDLENBQ2Qsa0ZBQ0YsQ0FBQyxDQUNELE1BQU0sQ0FBQyxDQUFDQyxPQUFPLElBQUk7TUFDakJkLE9BQU8sQ0FBQ2UsY0FBYyxDQUFDLENBQUM7TUFDeEJoQixNQUFNLENBQUNlLE9BQU8sR0FBRyxrQkFBa0IsR0FBRyxtQkFBbUIsQ0FBQztJQUM1RCxDQUFDLENBQUMsR0FDRjtFQUVOLENBQUMsQ0FBQyxPQUFPRSxLQUFLLEVBQUU7SUFDZHBCLFFBQVEsQ0FBQ29CLEtBQUssSUFBSUMsS0FBSyxDQUFDO0lBQ3hCTCxVQUFVLENBQ1JiLE1BQU0sRUFDTixDQUFDLEVBQ0QsZ0ZBQ0YsQ0FBQztFQUNIO0VBQ0EsT0FBTyxJQUFJO0FBQ2IiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/commands/usage/index.ts b/commands/usage/index.ts new file mode 100644 index 0000000..c387104 --- /dev/null +++ b/commands/usage/index.ts @@ -0,0 +1,9 @@ +import type { Command } from '../../commands.js' + +export default { + type: 'local-jsx', + name: 'usage', + description: 'Show plan usage limits', + availability: ['claude-ai'], + load: () => import('./usage.js'), +} satisfies Command diff --git a/commands/usage/usage.tsx b/commands/usage/usage.tsx new file mode 100644 index 0000000..b7deb40 --- /dev/null +++ b/commands/usage/usage.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; +import { Settings } from '../../components/Settings/Settings.js'; +import type { LocalJSXCommandCall } from '../../types/command.js'; +export const call: LocalJSXCommandCall = async (onDone, context) => { + return ; +}; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlNldHRpbmdzIiwiTG9jYWxKU1hDb21tYW5kQ2FsbCIsImNhbGwiLCJvbkRvbmUiLCJjb250ZXh0Il0sInNvdXJjZXMiOlsidXNhZ2UudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgU2V0dGluZ3MgfSBmcm9tICcuLi8uLi9jb21wb25lbnRzL1NldHRpbmdzL1NldHRpbmdzLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEpTWENvbW1hbmRDYWxsIH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcblxuZXhwb3J0IGNvbnN0IGNhbGw6IExvY2FsSlNYQ29tbWFuZENhbGwgPSBhc3luYyAob25Eb25lLCBjb250ZXh0KSA9PiB7XG4gIHJldHVybiA8U2V0dGluZ3Mgb25DbG9zZT17b25Eb25lfSBjb250ZXh0PXtjb250ZXh0fSBkZWZhdWx0VGFiPVwiVXNhZ2VcIiAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFFBQVEsUUFBUSx1Q0FBdUM7QUFDaEUsY0FBY0MsbUJBQW1CLFFBQVEsd0JBQXdCO0FBRWpFLE9BQU8sTUFBTUMsSUFBSSxFQUFFRCxtQkFBbUIsR0FBRyxNQUFBQyxDQUFPQyxNQUFNLEVBQUVDLE9BQU8sS0FBSztFQUNsRSxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDRCxNQUFNLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQ0MsT0FBTyxDQUFDLENBQUMsVUFBVSxDQUFDLE9BQU8sR0FBRztBQUMzRSxDQUFDIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/commands/version.ts b/commands/version.ts new file mode 100644 index 0000000..09f0a44 --- /dev/null +++ b/commands/version.ts @@ -0,0 +1,22 @@ +import type { Command, LocalCommandCall } from '../types/command.js' + +const call: LocalCommandCall = async () => { + return { + type: 'text', + value: MACRO.BUILD_TIME + ? `${MACRO.VERSION} (built ${MACRO.BUILD_TIME})` + : MACRO.VERSION, + } +} + +const version = { + type: 'local', + name: 'version', + description: + 'Print the version this session is running (not what autoupdate downloaded)', + isEnabled: () => process.env.USER_TYPE === 'ant', + supportsNonInteractive: true, + load: () => Promise.resolve({ call }), +} satisfies Command + +export default version diff --git a/commands/vim/index.ts b/commands/vim/index.ts new file mode 100644 index 0000000..f7f2592 --- /dev/null +++ b/commands/vim/index.ts @@ -0,0 +1,11 @@ +import type { Command } from '../../commands.js' + +const command = { + name: 'vim', + description: 'Toggle between Vim and Normal editing modes', + supportsNonInteractive: false, + type: 'local', + load: () => import('./vim.js'), +} satisfies Command + +export default command diff --git a/commands/vim/vim.ts b/commands/vim/vim.ts new file mode 100644 index 0000000..de5fd99 --- /dev/null +++ b/commands/vim/vim.ts @@ -0,0 +1,38 @@ +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../../services/analytics/index.js' +import type { LocalCommandCall } from '../../types/command.js' +import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' + +export const call: LocalCommandCall = async () => { + const config = getGlobalConfig() + let currentMode = config.editorMode || 'normal' + + // Handle backward compatibility - treat 'emacs' as 'normal' + if (currentMode === 'emacs') { + currentMode = 'normal' + } + + const newMode = currentMode === 'normal' ? 'vim' : 'normal' + + saveGlobalConfig(current => ({ + ...current, + editorMode: newMode, + })) + + logEvent('tengu_editor_mode_changed', { + mode: newMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + source: + 'command' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + return { + type: 'text', + value: `Editor mode set to ${newMode}. ${ + newMode === 'vim' + ? 'Use Escape key to toggle between INSERT and NORMAL modes.' + : 'Using standard (readline) keyboard bindings.' + }`, + } +} diff --git a/commands/voice/index.ts b/commands/voice/index.ts new file mode 100644 index 0000000..61540d3 --- /dev/null +++ b/commands/voice/index.ts @@ -0,0 +1,20 @@ +import type { Command } from '../../commands.js' +import { + isVoiceGrowthBookEnabled, + isVoiceModeEnabled, +} from '../../voice/voiceModeEnabled.js' + +const voice = { + type: 'local', + name: 'voice', + description: 'Toggle voice mode', + availability: ['claude-ai'], + isEnabled: () => isVoiceGrowthBookEnabled(), + get isHidden() { + return !isVoiceModeEnabled() + }, + supportsNonInteractive: false, + load: () => import('./voice.js'), +} satisfies Command + +export default voice diff --git a/commands/voice/voice.ts b/commands/voice/voice.ts new file mode 100644 index 0000000..f369891 --- /dev/null +++ b/commands/voice/voice.ts @@ -0,0 +1,150 @@ +import { normalizeLanguageForSTT } from '../../hooks/useVoice.js' +import { getShortcutDisplay } from '../../keybindings/shortcutFormat.js' +import { logEvent } from '../../services/analytics/index.js' +import type { LocalCommandCall } from '../../types/command.js' +import { isAnthropicAuthEnabled } from '../../utils/auth.js' +import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js' +import { settingsChangeDetector } from '../../utils/settings/changeDetector.js' +import { + getInitialSettings, + updateSettingsForSource, +} from '../../utils/settings/settings.js' +import { isVoiceModeEnabled } from '../../voice/voiceModeEnabled.js' + +const LANG_HINT_MAX_SHOWS = 2 + +export const call: LocalCommandCall = async () => { + // Check auth and kill-switch before allowing voice mode + if (!isVoiceModeEnabled()) { + // Differentiate: OAuth-less users get an auth hint, everyone else + // gets nothing (command shouldn't be reachable when the kill-switch is on). + if (!isAnthropicAuthEnabled()) { + return { + type: 'text' as const, + value: + 'Voice mode requires a Claude.ai account. Please run /login to sign in.', + } + } + return { + type: 'text' as const, + value: 'Voice mode is not available.', + } + } + + const currentSettings = getInitialSettings() + const isCurrentlyEnabled = currentSettings.voiceEnabled === true + + // Toggle OFF — no checks needed + if (isCurrentlyEnabled) { + const result = updateSettingsForSource('userSettings', { + voiceEnabled: false, + }) + if (result.error) { + return { + type: 'text' as const, + value: + 'Failed to update settings. Check your settings file for syntax errors.', + } + } + settingsChangeDetector.notifyChange('userSettings') + logEvent('tengu_voice_toggled', { enabled: false }) + return { + type: 'text' as const, + value: 'Voice mode disabled.', + } + } + + // Toggle ON — run pre-flight checks first + const { isVoiceStreamAvailable } = await import( + '../../services/voiceStreamSTT.js' + ) + const { checkRecordingAvailability } = await import('../../services/voice.js') + + // Check recording availability (microphone access) + const recording = await checkRecordingAvailability() + if (!recording.available) { + return { + type: 'text' as const, + value: + recording.reason ?? 'Voice mode is not available in this environment.', + } + } + + // Check for API key + if (!isVoiceStreamAvailable()) { + return { + type: 'text' as const, + value: + 'Voice mode requires a Claude.ai account. Please run /login to sign in.', + } + } + + // Check for recording tools + const { checkVoiceDependencies, requestMicrophonePermission } = await import( + '../../services/voice.js' + ) + const deps = await checkVoiceDependencies() + if (!deps.available) { + const hint = deps.installCommand + ? `\nInstall audio recording tools? Run: ${deps.installCommand}` + : '\nInstall SoX manually for audio recording.' + return { + type: 'text' as const, + value: `No audio recording tool found.${hint}`, + } + } + + // Probe mic access so the OS permission dialog fires now rather than + // on the user's first hold-to-talk activation. + if (!(await requestMicrophonePermission())) { + let guidance: string + if (process.platform === 'win32') { + guidance = 'Settings \u2192 Privacy \u2192 Microphone' + } else if (process.platform === 'linux') { + guidance = "your system's audio settings" + } else { + guidance = 'System Settings \u2192 Privacy & Security \u2192 Microphone' + } + return { + type: 'text' as const, + value: `Microphone access is denied. To enable it, go to ${guidance}, then run /voice again.`, + } + } + + // All checks passed — enable voice + const result = updateSettingsForSource('userSettings', { voiceEnabled: true }) + if (result.error) { + return { + type: 'text' as const, + value: + 'Failed to update settings. Check your settings file for syntax errors.', + } + } + settingsChangeDetector.notifyChange('userSettings') + logEvent('tengu_voice_toggled', { enabled: true }) + const key = getShortcutDisplay('voice:pushToTalk', 'Chat', 'Space') + const stt = normalizeLanguageForSTT(currentSettings.language) + const cfg = getGlobalConfig() + // Reset the hint counter whenever the resolved STT language changes + // (including first-ever enable, where lastLanguage is undefined). + const langChanged = cfg.voiceLangHintLastLanguage !== stt.code + const priorCount = langChanged ? 0 : (cfg.voiceLangHintShownCount ?? 0) + const showHint = !stt.fellBackFrom && priorCount < LANG_HINT_MAX_SHOWS + let langNote = '' + if (stt.fellBackFrom) { + langNote = ` Note: "${stt.fellBackFrom}" is not a supported dictation language; using English. Change it via /config.` + } else if (showHint) { + langNote = ` Dictation language: ${stt.code} (/config to change).` + } + if (langChanged || showHint) { + saveGlobalConfig(prev => ({ + ...prev, + voiceLangHintShownCount: priorCount + (showHint ? 1 : 0), + voiceLangHintLastLanguage: stt.code, + })) + } + return { + type: 'text' as const, + value: `Voice mode enabled. Hold ${key} to record.${langNote}`, + } +} diff --git a/components/AgentProgressLine.tsx b/components/AgentProgressLine.tsx new file mode 100644 index 0000000..49fa502 --- /dev/null +++ b/components/AgentProgressLine.tsx @@ -0,0 +1,136 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Box, Text } from '../ink.js'; +import { formatNumber } from '../utils/format.js'; +import type { Theme } from '../utils/theme.js'; +type Props = { + agentType: string; + description?: string; + name?: string; + descriptionColor?: keyof Theme; + taskDescription?: string; + toolUseCount: number; + tokens: number | null; + color?: keyof Theme; + isLast: boolean; + isResolved: boolean; + isError: boolean; + isAsync?: boolean; + shouldAnimate: boolean; + lastToolInfo?: string | null; + hideType?: boolean; +}; +export function AgentProgressLine(t0) { + const $ = _c(32); + const { + agentType, + description, + name, + descriptionColor, + taskDescription, + toolUseCount, + tokens, + color, + isLast, + isResolved, + isAsync: t1, + lastToolInfo, + hideType: t2 + } = t0; + const isAsync = t1 === undefined ? false : t1; + const hideType = t2 === undefined ? false : t2; + const treeChar = isLast ? "\u2514\u2500" : "\u251C\u2500"; + const isBackgrounded = isAsync && isResolved; + let t3; + if ($[0] !== isBackgrounded || $[1] !== isResolved || $[2] !== lastToolInfo || $[3] !== taskDescription) { + t3 = () => { + if (!isResolved) { + return lastToolInfo || "Initializing\u2026"; + } + if (isBackgrounded) { + return taskDescription ?? "Running in the background"; + } + return "Done"; + }; + $[0] = isBackgrounded; + $[1] = isResolved; + $[2] = lastToolInfo; + $[3] = taskDescription; + $[4] = t3; + } else { + t3 = $[4]; + } + const getStatusText = t3; + let t4; + if ($[5] !== treeChar) { + t4 = {treeChar} ; + $[5] = treeChar; + $[6] = t4; + } else { + t4 = $[6]; + } + const t5 = !isResolved; + let t6; + if ($[7] !== agentType || $[8] !== color || $[9] !== description || $[10] !== descriptionColor || $[11] !== hideType || $[12] !== name) { + t6 = hideType ? <>{name ?? description ?? agentType}{name && description && : {description}} : <>{agentType}{description && <>{" ("}{description}{")"}}; + $[7] = agentType; + $[8] = color; + $[9] = description; + $[10] = descriptionColor; + $[11] = hideType; + $[12] = name; + $[13] = t6; + } else { + t6 = $[13]; + } + let t7; + if ($[14] !== isBackgrounded || $[15] !== tokens || $[16] !== toolUseCount) { + t7 = !isBackgrounded && <>{" \xB7 "}{toolUseCount} tool {toolUseCount === 1 ? "use" : "uses"}{tokens !== null && <> · {formatNumber(tokens)} tokens}; + $[14] = isBackgrounded; + $[15] = tokens; + $[16] = toolUseCount; + $[17] = t7; + } else { + t7 = $[17]; + } + let t8; + if ($[18] !== t5 || $[19] !== t6 || $[20] !== t7) { + t8 = {t6}{t7}; + $[18] = t5; + $[19] = t6; + $[20] = t7; + $[21] = t8; + } else { + t8 = $[21]; + } + let t9; + if ($[22] !== t4 || $[23] !== t8) { + t9 = {t4}{t8}; + $[22] = t4; + $[23] = t8; + $[24] = t9; + } else { + t9 = $[24]; + } + let t10; + if ($[25] !== getStatusText || $[26] !== isBackgrounded || $[27] !== isLast) { + t10 = !isBackgrounded && {isLast ? " \u23BF " : "\u2502 \u23BF "}{getStatusText()}; + $[25] = getStatusText; + $[26] = isBackgrounded; + $[27] = isLast; + $[28] = t10; + } else { + t10 = $[28]; + } + let t11; + if ($[29] !== t10 || $[30] !== t9) { + t11 = {t9}{t10}; + $[29] = t10; + $[30] = t9; + $[31] = t11; + } else { + t11 = $[31]; + } + return t11; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJmb3JtYXROdW1iZXIiLCJUaGVtZSIsIlByb3BzIiwiYWdlbnRUeXBlIiwiZGVzY3JpcHRpb24iLCJuYW1lIiwiZGVzY3JpcHRpb25Db2xvciIsInRhc2tEZXNjcmlwdGlvbiIsInRvb2xVc2VDb3VudCIsInRva2VucyIsImNvbG9yIiwiaXNMYXN0IiwiaXNSZXNvbHZlZCIsImlzRXJyb3IiLCJpc0FzeW5jIiwic2hvdWxkQW5pbWF0ZSIsImxhc3RUb29sSW5mbyIsImhpZGVUeXBlIiwiQWdlbnRQcm9ncmVzc0xpbmUiLCJ0MCIsIiQiLCJfYyIsInQxIiwidDIiLCJ1bmRlZmluZWQiLCJ0cmVlQ2hhciIsImlzQmFja2dyb3VuZGVkIiwidDMiLCJnZXRTdGF0dXNUZXh0IiwidDQiLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5IiwidDEwIiwidDExIl0sInNvdXJjZXMiOlsiQWdlbnRQcm9ncmVzc0xpbmUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgZm9ybWF0TnVtYmVyIH0gZnJvbSAnLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJy4uL3V0aWxzL3RoZW1lLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBhZ2VudFR5cGU6IHN0cmluZ1xuICBkZXNjcmlwdGlvbj86IHN0cmluZ1xuICBuYW1lPzogc3RyaW5nXG4gIGRlc2NyaXB0aW9uQ29sb3I/OiBrZXlvZiBUaGVtZVxuICB0YXNrRGVzY3JpcHRpb24/OiBzdHJpbmdcbiAgdG9vbFVzZUNvdW50OiBudW1iZXJcbiAgdG9rZW5zOiBudW1iZXIgfCBudWxsXG4gIGNvbG9yPzoga2V5b2YgVGhlbWVcbiAgaXNMYXN0OiBib29sZWFuXG4gIGlzUmVzb2x2ZWQ6IGJvb2xlYW5cbiAgaXNFcnJvcjogYm9vbGVhblxuICBpc0FzeW5jPzogYm9vbGVhblxuICBzaG91bGRBbmltYXRlOiBib29sZWFuXG4gIGxhc3RUb29sSW5mbz86IHN0cmluZyB8IG51bGxcbiAgaGlkZVR5cGU/OiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBBZ2VudFByb2dyZXNzTGluZSh7XG4gIGFnZW50VHlwZSxcbiAgZGVzY3JpcHRpb24sXG4gIG5hbWUsXG4gIGRlc2NyaXB0aW9uQ29sb3IsXG4gIHRhc2tEZXNjcmlwdGlvbixcbiAgdG9vbFVzZUNvdW50LFxuICB0b2tlbnMsXG4gIGNvbG9yLFxuICBpc0xhc3QsXG4gIGlzUmVzb2x2ZWQsXG4gIGlzRXJyb3I6IF9pc0Vycm9yLFxuICBpc0FzeW5jID0gZmFsc2UsXG4gIHNob3VsZEFuaW1hdGU6IF9zaG91bGRBbmltYXRlLFxuICBsYXN0VG9vbEluZm8sXG4gIGhpZGVUeXBlID0gZmFsc2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHRyZWVDaGFyID0gaXNMYXN0ID8gJ+KUlOKUgCcgOiAn4pSc4pSAJ1xuICBjb25zdCBpc0JhY2tncm91bmRlZCA9IGlzQXN5bmMgJiYgaXNSZXNvbHZlZFxuXG4gIC8vIERldGVybWluZSB0aGUgc3RhdHVzIHRleHRcbiAgY29uc3QgZ2V0U3RhdHVzVGV4dCA9ICgpOiBzdHJpbmcgPT4ge1xuICAgIGlmICghaXNSZXNvbHZlZCkge1xuICAgICAgcmV0dXJuIGxhc3RUb29sSW5mbyB8fCAnSW5pdGlhbGl6aW5n4oCmJ1xuICAgIH1cbiAgICBpZiAoaXNCYWNrZ3JvdW5kZWQpIHtcbiAgICAgIHJldHVybiB0YXNrRGVzY3JpcHRpb24gPz8gJ1J1bm5pbmcgaW4gdGhlIGJhY2tncm91bmQnXG4gICAgfVxuICAgIHJldHVybiAnRG9uZSdcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8Qm94IHBhZGRpbmdMZWZ0PXszfT5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+e3RyZWVDaGFyfSA8L1RleHQ+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPXshaXNSZXNvbHZlZH0+XG4gICAgICAgICAge2hpZGVUeXBlID8gKFxuICAgICAgICAgICAgPD5cbiAgICAgICAgICAgICAgPFRleHQgYm9sZD57bmFtZSA/PyBkZXNjcmlwdGlvbiA/PyBhZ2VudFR5cGV9PC9UZXh0PlxuICAgICAgICAgICAgICB7bmFtZSAmJiBkZXNjcmlwdGlvbiAmJiA8VGV4dCBkaW1Db2xvcj46IHtkZXNjcmlwdGlvbn08L1RleHQ+fVxuICAgICAgICAgICAgPC8+XG4gICAgICAgICAgKSA6IChcbiAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgYm9sZFxuICAgICAgICAgICAgICAgIGJhY2tncm91bmRDb2xvcj17Y29sb3J9XG4gICAgICAgICAgICAgICAgY29sb3I9e2NvbG9yID8gJ2ludmVyc2VUZXh0JyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIHthZ2VudFR5cGV9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAge2Rlc2NyaXB0aW9uICYmIChcbiAgICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgICAgeycgKCd9XG4gICAgICAgICAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3I9e2Rlc2NyaXB0aW9uQ29sb3J9XG4gICAgICAgICAgICAgICAgICAgIGNvbG9yPXtkZXNjcmlwdGlvbkNvbG9yID8gJ2ludmVyc2VUZXh0JyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAge2Rlc2NyaXB0aW9ufVxuICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgeycpJ31cbiAgICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIDwvPlxuICAgICAgICAgICl9XG4gICAgICAgICAgeyFpc0JhY2tncm91bmRlZCAmJiAoXG4gICAgICAgICAgICA8PlxuICAgICAgICAgICAgICB7JyDCtyAnfVxuICAgICAgICAgICAgICB7dG9vbFVzZUNvdW50fSB0b29sIHt0b29sVXNlQ291bnQgPT09IDEgPyAndXNlJyA6ICd1c2VzJ31cbiAgICAgICAgICAgICAge3Rva2VucyAhPT0gbnVsbCAmJiA8PiDCtyB7Zm9ybWF0TnVtYmVyKHRva2Vucyl9IHRva2VuczwvPn1cbiAgICAgICAgICAgIDwvPlxuICAgICAgICAgICl9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgICAgeyFpc0JhY2tncm91bmRlZCAmJiAoXG4gICAgICAgIDxCb3ggcGFkZGluZ0xlZnQ9ezN9IGZsZXhEaXJlY3Rpb249XCJyb3dcIj5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57aXNMYXN0ID8gJyAgIOKOvyAgJyA6ICfilIIgIOKOvyAgJ308L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+e2dldFN0YXR1c1RleHQoKX08L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLFNBQVNDLFlBQVksUUFBUSxvQkFBb0I7QUFDakQsY0FBY0MsS0FBSyxRQUFRLG1CQUFtQjtBQUU5QyxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsU0FBUyxFQUFFLE1BQU07RUFDakJDLFdBQVcsQ0FBQyxFQUFFLE1BQU07RUFDcEJDLElBQUksQ0FBQyxFQUFFLE1BQU07RUFDYkMsZ0JBQWdCLENBQUMsRUFBRSxNQUFNTCxLQUFLO0VBQzlCTSxlQUFlLENBQUMsRUFBRSxNQUFNO0VBQ3hCQyxZQUFZLEVBQUUsTUFBTTtFQUNwQkMsTUFBTSxF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 \ No newline at end of file diff --git a/components/App.tsx b/components/App.tsx new file mode 100644 index 0000000..69ca968 --- /dev/null +++ b/components/App.tsx @@ -0,0 +1,56 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { FpsMetricsProvider } from '../context/fpsMetrics.js'; +import { StatsProvider, type StatsStore } from '../context/stats.js'; +import { type AppState, AppStateProvider } from '../state/AppState.js'; +import { onChangeAppState } from '../state/onChangeAppState.js'; +import type { FpsMetrics } from '../utils/fpsTracker.js'; +type Props = { + getFpsMetrics: () => FpsMetrics | undefined; + stats?: StatsStore; + initialState: AppState; + children: React.ReactNode; +}; + +/** + * Top-level wrapper for interactive sessions. + * Provides FPS metrics, stats context, and app state to the component tree. + */ +export function App(t0) { + const $ = _c(9); + const { + getFpsMetrics, + stats, + initialState, + children + } = t0; + let t1; + if ($[0] !== children || $[1] !== initialState) { + t1 = {children}; + $[0] = children; + $[1] = initialState; + $[2] = t1; + } else { + t1 = $[2]; + } + let t2; + if ($[3] !== stats || $[4] !== t1) { + t2 = {t1}; + $[3] = stats; + $[4] = t1; + $[5] = t2; + } else { + t2 = $[5]; + } + let t3; + if ($[6] !== getFpsMetrics || $[7] !== t2) { + t3 = {t2}; + $[6] = getFpsMetrics; + $[7] = t2; + $[8] = t3; + } else { + t3 = $[8]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkZwc01ldHJpY3NQcm92aWRlciIsIlN0YXRzUHJvdmlkZXIiLCJTdGF0c1N0b3JlIiwiQXBwU3RhdGUiLCJBcHBTdGF0ZVByb3ZpZGVyIiwib25DaGFuZ2VBcHBTdGF0ZSIsIkZwc01ldHJpY3MiLCJQcm9wcyIsImdldEZwc01ldHJpY3MiLCJzdGF0cyIsImluaXRpYWxTdGF0ZSIsImNoaWxkcmVuIiwiUmVhY3ROb2RlIiwiQXBwIiwidDAiLCIkIiwiX2MiLCJ0MSIsInQyIiwidDMiXSwic291cmNlcyI6WyJBcHAudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEZwc01ldHJpY3NQcm92aWRlciB9IGZyb20gJy4uL2NvbnRleHQvZnBzTWV0cmljcy5qcydcbmltcG9ydCB7IFN0YXRzUHJvdmlkZXIsIHR5cGUgU3RhdHNTdG9yZSB9IGZyb20gJy4uL2NvbnRleHQvc3RhdHMuanMnXG5pbXBvcnQgeyB0eXBlIEFwcFN0YXRlLCBBcHBTdGF0ZVByb3ZpZGVyIH0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgeyBvbkNoYW5nZUFwcFN0YXRlIH0gZnJvbSAnLi4vc3RhdGUvb25DaGFuZ2VBcHBTdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgRnBzTWV0cmljcyB9IGZyb20gJy4uL3V0aWxzL2Zwc1RyYWNrZXIuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGdldEZwc01ldHJpY3M6ICgpID0+IEZwc01ldHJpY3MgfCB1bmRlZmluZWRcbiAgc3RhdHM/OiBTdGF0c1N0b3JlXG4gIGluaXRpYWxTdGF0ZTogQXBwU3RhdGVcbiAgY2hpbGRyZW46IFJlYWN0LlJlYWN0Tm9kZVxufVxuXG4vKipcbiAqIFRvcC1sZXZlbCB3cmFwcGVyIGZvciBpbnRlcmFjdGl2ZSBzZXNzaW9ucy5cbiAqIFByb3ZpZGVzIEZQUyBtZXRyaWNzLCBzdGF0cyBjb250ZXh0LCBhbmQgYXBwIHN0YXRlIHRvIHRoZSBjb21wb25lbnQgdHJlZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEFwcCh7XG4gIGdldEZwc01ldHJpY3MsXG4gIHN0YXRzLFxuICBpbml0aWFsU3RhdGUsXG4gIGNoaWxkcmVuLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDxGcHNNZXRyaWNzUHJvdmlkZXIgZ2V0RnBzTWV0cmljcz17Z2V0RnBzTWV0cmljc30+XG4gICAgICA8U3RhdHNQcm92aWRlciBzdG9yZT17c3RhdHN9PlxuICAgICAgICA8QXBwU3RhdGVQcm92aWRlclxuICAgICAgICAgIGluaXRpYWxTdGF0ZT17aW5pdGlhbFN0YXRlfVxuICAgICAgICAgIG9uQ2hhbmdlQXBwU3RhdGU9e29uQ2hhbmdlQXBwU3RhdGV9XG4gICAgICAgID5cbiAgICAgICAgICB7Y2hpbGRyZW59XG4gICAgICAgIDwvQXBwU3RhdGVQcm92aWRlcj5cbiAgICAgIDwvU3RhdHNQcm92aWRlcj5cbiAgICA8L0Zwc01ldHJpY3NQcm92aWRlcj5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0Msa0JBQWtCLFFBQVEsMEJBQTBCO0FBQzdELFNBQVNDLGFBQWEsRUFBRSxLQUFLQyxVQUFVLFFBQVEscUJBQXFCO0FBQ3BFLFNBQVMsS0FBS0MsUUFBUSxFQUFFQyxnQkFBZ0IsUUFBUSxzQkFBc0I7QUFDdEUsU0FBU0MsZ0JBQWdCLFFBQVEsOEJBQThCO0FBQy9ELGNBQWNDLFVBQVUsUUFBUSx3QkFBd0I7QUFFeEQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLGFBQWEsRUFBRSxHQUFHLEdBQUdGLFVBQVUsR0FBRyxTQUFTO0VBQzNDRyxLQUFLLENBQUMsRUFBRVAsVUFBVTtFQUNsQlEsWUFBWSxFQUFFUCxRQUFRO0VBQ3RCUSxRQUFRLEVBQUVaLEtBQUssQ0FBQ2EsU0FBUztBQUMzQixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTyxTQUFBQyxJQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWE7SUFBQVIsYUFBQTtJQUFBQyxLQUFBO0lBQUFDLFlBQUE7SUFBQUM7RUFBQSxJQUFBRyxFQUtaO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQUosUUFBQSxJQUFBSSxDQUFBLFFBQUFMLFlBQUE7SUFJQU8sRUFBQSxJQUFDLGdCQUFnQixDQUNEUCxZQUFZLENBQVpBLGFBQVcsQ0FBQyxDQUNSTCxnQkFBZ0IsQ0FBaEJBLGlCQUFlLENBQUMsQ0FFakNNLFNBQU8sQ0FDVixFQUxDLGdCQUFnQixDQUtFO0lBQUFJLENBQUEsTUFBQUosUUFBQTtJQUFBSSxDQUFBLE1BQUFMLFlBQUE7SUFBQUssQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFBQSxJQUFBRyxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBTixLQUFBLElBQUFNLENBQUEsUUFBQUUsRUFBQTtJQU5yQkMsRUFBQSxJQUFDLGFBQWEsQ0FBUVQsS0FBSyxDQUFMQSxNQUFJLENBQUMsQ0FDekIsQ0FBQVEsRUFLa0IsQ0FDcEIsRUFQQyxhQUFhLENBT0U7SUFBQUYsQ0FBQSxNQUFBTixLQUFBO0lBQUFNLENBQUEsTUFBQUUsRUFBQTtJQUFBRixDQUFBLE1BQUFHLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFILENBQUE7RUFBQTtFQUFBLElBQUFJLEVBQUE7RUFBQSxJQUFBSixDQUFBLFFBQUFQLGFBQUEsSUFBQU8sQ0FBQSxRQUFBRyxFQUFBO0lBUmxCQyxFQUFBLElBQUMsa0JBQWtCLENBQWdCWCxhQUFhLENBQWJBLGNBQVksQ0FBQyxDQUM5QyxDQUFBVSxFQU9lLENBQ2pCLEVBVEMsa0JBQWtCLENBU0U7SUFBQUgsQ0FBQSxNQUFBUCxhQUFBO0lBQUFPLENBQUEsTUFBQUcsRUFBQTtJQUFBSCxDQUFBLE1BQUFJLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFKLENBQUE7RUFBQTtFQUFBLE9BVHJCSSxFQVNxQjtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/ApproveApiKey.tsx b/components/ApproveApiKey.tsx new file mode 100644 index 0000000..be54c0e --- /dev/null +++ b/components/ApproveApiKey.tsx @@ -0,0 +1,123 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Text } from '../ink.js'; +import { saveGlobalConfig } from '../utils/config.js'; +import { Select } from './CustomSelect/index.js'; +import { Dialog } from './design-system/Dialog.js'; +type Props = { + customApiKeyTruncated: string; + onDone(approved: boolean): void; +}; +export function ApproveApiKey(t0) { + const $ = _c(17); + const { + customApiKeyTruncated, + onDone + } = t0; + let t1; + if ($[0] !== customApiKeyTruncated || $[1] !== onDone) { + t1 = function onChange(value) { + bb2: switch (value) { + case "yes": + { + saveGlobalConfig(current_0 => ({ + ...current_0, + customApiKeyResponses: { + ...current_0.customApiKeyResponses, + approved: [...(current_0.customApiKeyResponses?.approved ?? []), customApiKeyTruncated] + } + })); + onDone(true); + break bb2; + } + case "no": + { + saveGlobalConfig(current => ({ + ...current, + customApiKeyResponses: { + ...current.customApiKeyResponses, + rejected: [...(current.customApiKeyResponses?.rejected ?? []), customApiKeyTruncated] + } + })); + onDone(false); + } + } + }; + $[0] = customApiKeyTruncated; + $[1] = onDone; + $[2] = t1; + } else { + t1 = $[2]; + } + const onChange = t1; + let t2; + if ($[3] !== onChange) { + t2 = () => onChange("no"); + $[3] = onChange; + $[4] = t2; + } else { + t2 = $[4]; + } + let t3; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t3 = ANTHROPIC_API_KEY; + $[5] = t3; + } else { + t3 = $[5]; + } + let t4; + if ($[6] !== customApiKeyTruncated) { + t4 = {t3}: sk-ant-...{customApiKeyTruncated}; + $[6] = customApiKeyTruncated; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t5 = Do you want to use this API key?; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t6 = { + label: "Yes", + value: "yes" + }; + $[9] = t6; + } else { + t6 = $[9]; + } + let t7; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t7 = [t6, { + label: No (recommended), + value: "no" + }]; + $[10] = t7; + } else { + t7 = $[10]; + } + let t8; + if ($[11] !== onChange) { + t8 = ; + $[11] = onDecline; + $[12] = t7; + $[13] = t8; + $[14] = t9; + } else { + t9 = $[14]; + } + let t10; + if ($[15] !== onDecline || $[16] !== t9) { + t10 = {t3}{t9}; + $[15] = onDecline; + $[16] = t9; + $[17] = t10; + } else { + t10 = $[17]; + } + return t10; +} +function _temp() { + logEvent("tengu_auto_mode_opt_in_dialog_shown", {}); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImxvZ0V2ZW50IiwiQm94IiwiTGluayIsIlRleHQiLCJ1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSIsIlNlbGVjdCIsIkRpYWxvZyIsIkFVVE9fTU9ERV9ERVNDUklQVElPTiIsIlByb3BzIiwib25BY2NlcHQiLCJvbkRlY2xpbmUiLCJkZWNsaW5lRXhpdHMiLCJBdXRvTW9kZU9wdEluRGlhbG9nIiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsInVzZUVmZmVjdCIsIl90ZW1wIiwidDIiLCJvbkNoYW5nZSIsInZhbHVlIiwiYmIzIiwic2tpcEF1dG9QZXJtaXNzaW9uUHJvbXB0IiwicGVybWlzc2lvbnMiLCJkZWZhdWx0TW9kZSIsInQzIiwidDQiLCJsYWJlbCIsImNvbnN0IiwidDUiLCJ0NiIsInQ3IiwidDgiLCJ2YWx1ZV8wIiwidDkiLCJ0MTAiXSwic291cmNlcyI6WyJBdXRvTW9kZU9wdEluRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBsb2dFdmVudCB9IGZyb20gJ3NyYy9zZXJ2aWNlcy9hbmFseXRpY3MvaW5kZXguanMnXG5pbXBvcnQgeyBCb3gsIExpbmssIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyB1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSB9IGZyb20gJy4uL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi9DdXN0b21TZWxlY3QvaW5kZXguanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuXG4vLyBOT1RFOiBUaGlzIGNvcHkgaXMgbGVnYWxseSByZXZpZXdlZCDigJQgZG8gbm90IG1vZGlmeSB3aXRob3V0IExlZ2FsIHRlYW0gYXBwcm92YWwuXG5leHBvcnQgY29uc3QgQVVUT19NT0RFX0RFU0NSSVBUSU9OID1cbiAgXCJBdXRvIG1vZGUgbGV0cyBDbGF1ZGUgaGFuZGxlIHBlcm1pc3Npb24gcHJvbXB0cyBhdXRvbWF0aWNhbGx5IOKAlCBDbGF1ZGUgY2hlY2tzIGVhY2ggdG9vbCBjYWxsIGZvciByaXNreSBhY3Rpb25zIGFuZCBwcm9tcHQgaW5qZWN0aW9uIGJlZm9yZSBleGVjdXRpbmcuIEFjdGlvbnMgQ2xhdWRlIGlkZW50aWZpZXMgYXMgc2FmZSBhcmUgZXhlY3V0ZWQsIHdoaWxlIGFjdGlvbnMgQ2xhdWRlIGlkZW50aWZpZXMgYXMgcmlza3kgYXJlIGJsb2NrZWQgYW5kIENsYXVkZSBtYXkgdHJ5IGEgZGlmZmVyZW50IGFwcHJvYWNoLiBJZGVhbCBmb3IgbG9uZy1ydW5uaW5nIHRhc2tzLiBTZXNzaW9ucyBhcmUgc2xpZ2h0bHkgbW9yZSBleHBlbnNpdmUuIENsYXVkZSBjYW4gbWFrZSBtaXN0YWtlcyB0aGF0IGFsbG93IGhhcm1mdWwgY29tbWFuZHMgdG8gcnVuLCBpdCdzIHJlY29tbWVuZGVkIHRvIG9ubHkgdXNlIGluIGlzb2xhdGVkIGVudmlyb25tZW50cy4gU2hpZnQrVGFiIHRvIGNoYW5nZSBtb2RlLlwiXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG9uQWNjZXB0KCk6IHZvaWRcbiAgb25EZWNsaW5lKCk6IHZvaWRcbiAgLy8gU3RhcnR1cCBnYXRlOiBkZWNsaW5lIGV4aXRzIHRoZSBwcm9jZXNzLCBzbyByZWxhYmVsIGFjY29yZGluZ2x5LlxuICBkZWNsaW5lRXhpdHM/OiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBBdXRvTW9kZU9wdEluRGlhbG9nKHtcbiAgb25BY2NlcHQsXG4gIG9uRGVjbGluZSxcbiAgZGVjbGluZUV4aXRzLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBSZWFjdC51c2VFZmZlY3QoKCkgPT4ge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV9hdXRvX21vZGVfb3B0X2luX2RpYWxvZ19zaG93bicsIHt9KVxuICB9LCBbXSlcblxuICBmdW5jdGlvbiBvbkNoYW5nZSh2YWx1ZTogJ2FjY2VwdCcgfCAnYWNjZXB0LWRlZmF1bHQnIHwgJ2RlY2xpbmUnKSB7XG4gICAgc3dpdGNoICh2YWx1ZSkge1xuICAgICAgY2FzZSAnYWNjZXB0Jzoge1xuICAgICAgICBsb2dFdmVudCgndGVuZ3VfYXV0b19tb2RlX29wdF9pbl9kaWFsb2dfYWNjZXB0Jywge30pXG4gICAgICAgIHVwZGF0ZVNldHRpbmdzRm9yU291cmNlKCd1c2VyU2V0dGluZ3MnLCB7XG4gICAgICAgICAgc2tpcEF1dG9QZXJtaXNzaW9uUHJvbXB0OiB0cnVlLFxuICAgICAgICB9KVxuICAgICAgICBvbkFjY2VwdCgpXG4gICAgICAgIGJyZWFrXG4gICAgICB9XG4gICAgICBjYXNlICdhY2NlcHQtZGVmYXVsdCc6IHtcbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2F1dG9fbW9kZV9vcHRfaW5fZGlhbG9nX2FjY2VwdF9kZWZhdWx0Jywge30pXG4gICAgICAgIHVwZGF0ZVNldHRpbmdzRm9yU291cmNlKCd1c2VyU2V0dGluZ3MnLCB7XG4gICAgICAgICAgc2tpcEF1dG9QZXJtaXNzaW9uUHJvbXB0OiB0cnVlLFxuICAgICAgICAgIHBlcm1pc3Npb25zOiB7IGRlZmF1bHRNb2RlOiAnYXV0bycgfSxcbiAgICAgICAgfSlcbiAgICAgICAgb25BY2NlcHQoKVxuICAgICAgICBicmVha1xuICAgICAgfVxuICAgICAgY2FzZSAnZGVjbGluZSc6IHtcbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2F1dG9fbW9kZV9vcHRfaW5fZGlhbG9nX2RlY2xpbmUnLCB7fSlcbiAgICAgICAgb25EZWNsaW5lKClcbiAgICAgICAgYnJlYWtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxEaWFsb2cgdGl0bGU9XCJFbmFibGUgYXV0byBtb2RlP1wiIGNvbG9yPVwid2FybmluZ1wiIG9uQ2FuY2VsPXtvbkRlY2xpbmV9PlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgPFRleHQ+e0FVVE9fTU9ERV9ERVNDUklQVElPTn08L1RleHQ+XG5cbiAgICAgICAgPExpbmsgdXJsPVwiaHR0cHM6Ly9jb2RlLmNsYXVkZS5jb20vZG9jcy9lbi9zZWN1cml0eVwiIC8+XG4gICAgICA8L0JveD5cblxuICAgICAgPFNlbGVjdFxuICAgICAgICBvcHRpb25zPXtbXG4gICAgICAgICAgLi4uKFwiZXh0ZXJuYWxcIiAhPT0gJ2FudCdcbiAgICAgICAgICAgID8gW1xuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIGxhYmVsOiAnWWVzLCBhbmQgbWFrZSBpdCBteSBkZWZhdWx0IG1vZGUnLFxuICAgICAgICAgICAgICAgICAgdmFsdWU6ICdhY2NlcHQtZGVmYXVsdCcgYXMgY29uc3QsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgOiBbXSksXG4gICAgICAgICAgeyBsYWJlbDogJ1llcywgZW5hYmxlIGF1dG8gbW9kZScsIHZhbHVlOiAnYWNjZXB0JyBhcyBjb25zdCB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiBkZWNsaW5lRXhpdHMgPyAnTm8sIGV4aXQnIDogJ05vLCBnbyBiYWNrJyxcbiAgICAgICAgICAgIHZhbHVlOiAnZGVjbGluZScgYXMgY29uc3QsXG4gICAgICAgICAgfSxcbiAgICAgICAgXX1cbiAgICAgICAgb25DaGFuZ2U9e3ZhbHVlID0+XG4gICAgICAgICAgb25DaGFuZ2UodmFsdWUgYXMgJ2FjY2VwdCcgfCAnYWNjZXB0LWRlZmF1bHQnIHwgJ2RlY2xpbmUnKVxuICAgICAgICB9XG4gICAgICAgIG9uQ2FuY2VsPXtvbkRlY2xpbmV9XG4gICAgICAvPlxuICAgIDwvRGlhbG9nPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyxRQUFRLFFBQVEsaUNBQWlDO0FBQzFELFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxFQUFFQyxJQUFJ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 \ No newline at end of file diff --git a/components/AutoUpdater.tsx b/components/AutoUpdater.tsx new file mode 100644 index 0000000..144e2c8 --- /dev/null +++ b/components/AutoUpdater.tsx @@ -0,0 +1,198 @@ +import * as React from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { useInterval } from 'usehooks-ts'; +import { useUpdateNotification } from '../hooks/useUpdateNotification.js'; +import { Box, Text } from '../ink.js'; +import { type AutoUpdaterResult, getLatestVersion, getMaxVersion, type InstallStatus, installGlobalPackage, shouldSkipVersion } from '../utils/autoUpdater.js'; +import { getGlobalConfig, isAutoUpdaterDisabled } from '../utils/config.js'; +import { logForDebugging } from '../utils/debug.js'; +import { getCurrentInstallationType } from '../utils/doctorDiagnostic.js'; +import { installOrUpdateClaudePackage, localInstallationExists } from '../utils/localInstaller.js'; +import { removeInstalledSymlink } from '../utils/nativeInstaller/index.js'; +import { gt, gte } from '../utils/semver.js'; +import { getInitialSettings } from '../utils/settings/settings.js'; +type Props = { + isUpdating: boolean; + onChangeIsUpdating: (isUpdating: boolean) => void; + onAutoUpdaterResult: (autoUpdaterResult: AutoUpdaterResult) => void; + autoUpdaterResult: AutoUpdaterResult | null; + showSuccessMessage: boolean; + verbose: boolean; +}; +export function AutoUpdater({ + isUpdating, + onChangeIsUpdating, + onAutoUpdaterResult, + autoUpdaterResult, + showSuccessMessage, + verbose +}: Props): React.ReactNode { + const [versions, setVersions] = useState<{ + global?: string | null; + latest?: string | null; + }>({}); + const [hasLocalInstall, setHasLocalInstall] = useState(false); + const updateSemver = useUpdateNotification(autoUpdaterResult?.version); + useEffect(() => { + void localInstallationExists().then(setHasLocalInstall); + }, []); + + // Track latest isUpdating value in a ref so the memoized checkForUpdates + // callback always sees the current value. Without this, the 30-minute + // interval fires with a stale closure where isUpdating is false, allowing + // a concurrent installGlobalPackage() to run while one is already in + // progress. + const isUpdatingRef = useRef(isUpdating); + isUpdatingRef.current = isUpdating; + const checkForUpdates = React.useCallback(async () => { + if (isUpdatingRef.current) { + return; + } + if ("production" === 'test' || "production" === 'development') { + logForDebugging('AutoUpdater: Skipping update check in test/dev environment'); + return; + } + const currentVersion = MACRO.VERSION; + const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest'; + let latestVersion = await getLatestVersion(channel); + const isDisabled = isAutoUpdaterDisabled(); + + // Check if max version is set (server-side kill switch for auto-updates) + const maxVersion = await getMaxVersion(); + if (maxVersion && latestVersion && gt(latestVersion, maxVersion)) { + logForDebugging(`AutoUpdater: maxVersion ${maxVersion} is set, capping update from ${latestVersion} to ${maxVersion}`); + if (gte(currentVersion, maxVersion)) { + logForDebugging(`AutoUpdater: current version ${currentVersion} is already at or above maxVersion ${maxVersion}, skipping update`); + setVersions({ + global: currentVersion, + latest: latestVersion + }); + return; + } + latestVersion = maxVersion; + } + setVersions({ + global: currentVersion, + latest: latestVersion + }); + + // Check if update needed and perform update + if (!isDisabled && currentVersion && latestVersion && !gte(currentVersion, latestVersion) && !shouldSkipVersion(latestVersion)) { + const startTime = Date.now(); + onChangeIsUpdating(true); + + // Remove native installer symlink since we're using JS-based updates + // But only if user hasn't migrated to native installation + const config = getGlobalConfig(); + if (config.installMethod !== 'native') { + await removeInstalledSymlink(); + } + + // Detect actual running installation type + const installationType = await getCurrentInstallationType(); + logForDebugging(`AutoUpdater: Detected installation type: ${installationType}`); + + // Skip update for development builds + if (installationType === 'development') { + logForDebugging('AutoUpdater: Cannot auto-update development build'); + onChangeIsUpdating(false); + return; + } + + // Choose the appropriate update method based on what's actually running + let installStatus: InstallStatus; + let updateMethod: 'local' | 'global'; + if (installationType === 'npm-local') { + // Use local update for local installations + logForDebugging('AutoUpdater: Using local update method'); + updateMethod = 'local'; + installStatus = await installOrUpdateClaudePackage(channel); + } else if (installationType === 'npm-global') { + // Use global update for global installations + logForDebugging('AutoUpdater: Using global update method'); + updateMethod = 'global'; + installStatus = await installGlobalPackage(); + } else if (installationType === 'native') { + // This shouldn't happen - native should use NativeAutoUpdater + logForDebugging('AutoUpdater: Unexpected native installation in non-native updater'); + onChangeIsUpdating(false); + return; + } else { + // Fallback to config-based detection for unknown types + logForDebugging(`AutoUpdater: Unknown installation type, falling back to config`); + const isMigrated = config.installMethod === 'local'; + updateMethod = isMigrated ? 'local' : 'global'; + if (isMigrated) { + installStatus = await installOrUpdateClaudePackage(channel); + } else { + installStatus = await installGlobalPackage(); + } + } + onChangeIsUpdating(false); + if (installStatus === 'success') { + logEvent('tengu_auto_updater_success', { + fromVersion: currentVersion as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + toVersion: latestVersion as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + durationMs: Date.now() - startTime, + wasMigrated: updateMethod === 'local', + installationType: installationType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } else { + logEvent('tengu_auto_updater_fail', { + fromVersion: currentVersion as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + attemptedVersion: latestVersion as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + status: installStatus as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + durationMs: Date.now() - startTime, + wasMigrated: updateMethod === 'local', + installationType: installationType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + onAutoUpdaterResult({ + version: latestVersion, + status: installStatus + }); + } + // isUpdating intentionally omitted from deps; we read isUpdatingRef + // instead so the guard is always current without changing callback + // identity (which would re-trigger the initial-check useEffect below). + // eslint-disable-next-line react-hooks/exhaustive-deps + // biome-ignore lint/correctness/useExhaustiveDependencies: isUpdating read via ref + }, [onAutoUpdaterResult]); + + // Initial check + useEffect(() => { + void checkForUpdates(); + }, [checkForUpdates]); + + // Check every 30 minutes + useInterval(checkForUpdates, 30 * 60 * 1000); + if (!autoUpdaterResult?.version && (!versions.global || !versions.latest)) { + return null; + } + if (!autoUpdaterResult?.version && !isUpdating) { + return null; + } + return + {verbose && + globalVersion: {versions.global} · latestVersion:{' '} + {versions.latest} + } + {isUpdating ? <> + + + Auto-updating… + + + : autoUpdaterResult?.status === 'success' && showSuccessMessage && updateSemver && + ✓ Update installed · Restart to apply + } + {(autoUpdaterResult?.status === 'install_failed' || autoUpdaterResult?.status === 'no_permissions') && + ✗ Auto-update failed · Try claude doctor or{' '} + + {hasLocalInstall ? `cd ~/.claude/local && npm update ${MACRO.PACKAGE_URL}` : `npm i -g ${MACRO.PACKAGE_URL}`} + + } + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZVN0YXRlIiwiQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyIsImxvZ0V2ZW50IiwidXNlSW50ZXJ2YWwiLCJ1c2VVcGRhdGVOb3RpZmljYXRpb24iLCJCb3giLCJUZXh0IiwiQXV0b1VwZGF0ZXJSZXN1bHQiLCJnZXRMYXRlc3RWZXJzaW9uIiwiZ2V0TWF4VmVyc2lvbiIsIkluc3RhbGxTdGF0dXMiLCJpbnN0YWxsR2xvYmFsUGFja2FnZSIsInNob3VsZFNraXBWZXJzaW9uIiwiZ2V0R2xvYmFsQ29uZmlnIiwiaXNBdXRvVXBkYXRlckRpc2FibGVkIiwibG9nRm9yRGVidWdnaW5nIiwiZ2V0Q3VycmVudEluc3RhbGxhdGlvblR5cGUiLCJpbnN0YWxsT3JVcGRhdGVDbGF1ZGVQYWNrYWdlIiwibG9jYWxJbnN0YWxsYXRpb25FeGlzdHMiLCJyZW1vdmVJbnN0YWxsZWRTeW1saW5rIiwiZ3QiLCJndGUiLCJnZXRJbml0aWFsU2V0dGluZ3MiLCJQcm9wcyIsImlzVXBkYXRpbmciLCJvbkNoYW5nZUlzVXBkYXRpbmciLCJvbkF1dG9VcGRhdGVyUmVzdWx0IiwiYXV0b1VwZGF0ZXJSZXN1bHQiLCJzaG93U3VjY2Vzc01lc3NhZ2UiLCJ2ZXJib3NlIiwiQXV0b1VwZGF0ZXIiLCJSZWFjdE5vZGUiLCJ2ZXJzaW9ucyIsInNldFZlcnNpb25zIiwiZ2xvYmFsIiwibGF0ZXN0IiwiaGFzTG9jYWxJbnN0YWxsIiwic2V0SGFzTG9jYWxJbnN0YWxsIiwidXBkYXRlU2VtdmVyIiwidmVyc2lvbiIsInRoZW4iLCJpc1VwZGF0aW5nUmVmIiwiY3VycmVudCIsImNoZWNrRm9yVXBkYXRlcyIsInVzZUNhbGxiYWNrIiwiY3VycmVudFZlcnNpb24iLCJNQUNSTyIsIlZFUlNJT04iLCJjaGFubmVsIiwiYXV0b1VwZGF0ZXNDaGFubmVsIiwibGF0ZXN0VmVyc2lvbiIsImlzRGlzYWJsZWQiLCJtYXhWZXJzaW9uIiwic3RhcnRUaW1lIiwiRGF0ZSIsIm5vdyIsImNvbmZpZyIsImluc3RhbGxNZXRob2QiLCJpbnN0YWxsYXRpb25UeXBlIiwiaW5zdGFsbFN0YXR1cyIsInVwZGF0ZU1ldGhvZCIsImlzTWlncmF0ZWQiLCJmcm9tVmVyc2lvbiIsInRvVmVyc2lvbiIsImR1cmF0aW9uTXMiLCJ3YXNNaWdyYXRlZCIsImF0dGVtcHRlZFZlcnNpb24iLCJzdGF0dXMiLCJQQUNLQUdFX1VSTCJdLCJzb3VyY2VzIjpbIkF1dG9VcGRhdGVyLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IHVzZUludGVydmFsIH0gZnJvbSAndXNlaG9va3MtdHMnXG5pbXBvcnQgeyB1c2VVcGRhdGVOb3RpZmljYXRpb24gfSBmcm9tICcuLi9ob29rcy91c2VVcGRhdGVOb3RpZmljYXRpb24uanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQge1xuICB0eXBlIEF1dG9VcGRhdGVyUmVzdWx0LFxuICBnZXRMYXRlc3RWZXJzaW9uLFxuICBnZXRNYXhWZXJzaW9uLFxuICB0eXBlIEluc3RhbGxTdGF0dXMsXG4gIGluc3RhbGxHbG9iYWxQYWNrYWdlLFxuICBzaG91bGRTa2lwVmVyc2lvbixcbn0gZnJvbSAnLi4vdXRpbHMvYXV0b1VwZGF0ZXIuanMnXG5pbXBvcnQgeyBnZXRHbG9iYWxDb25maWcsIGlzQXV0b1VwZGF0ZXJEaXNhYmxlZCB9IGZyb20gJy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHsgZ2V0Q3VycmVudEluc3RhbGxhdGlvblR5cGUgfSBmcm9tICcuLi91dGlscy9kb2N0b3JEaWFnbm9zdGljLmpzJ1xuaW1wb3J0IHtcbiAgaW5zdGFsbE9yVXBkYXRlQ2xhdWRlUGFja2FnZSxcbiAgbG9jYWxJbnN0YWxsYXRpb25FeGlzdHMsXG59IGZyb20gJy4uL3V0aWxzL2xvY2FsSW5zdGFsbGVyLmpzJ1xuaW1wb3J0IHsgcmVtb3ZlSW5zdGFsbGVkU3ltbGluayB9IGZyb20gJy4uL3V0aWxzL25hdGl2ZUluc3RhbGxlci9pbmRleC5qcydcbmltcG9ydCB7IGd0LCBndGUgfSBmcm9tICcuLi91dGlscy9zZW12ZXIuanMnXG5pbXBvcnQgeyBnZXRJbml0aWFsU2V0dGluZ3MgfSBmcm9tICcuLi91dGlscy9zZXR0aW5ncy9zZXR0aW5ncy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgaXNVcGRhdGluZzogYm9vbGVhblxuICBvbkNoYW5nZUlzVXBkYXRpbmc6IChpc1VwZGF0aW5nOiBib29sZWFuKSA9PiB2b2lkXG4gIG9uQXV0b1VwZGF0ZXJSZXN1bHQ6IChhdXRvVXBkYXRlclJlc3VsdDogQXV0b1VwZGF0ZXJSZXN1bHQpID0+IHZvaWRcbiAgYXV0b1VwZGF0ZXJSZXN1bHQ6IEF1dG9VcGRhdGVyUmVzdWx0IHwgbnVsbFxuICBzaG93U3VjY2Vzc01lc3NhZ2U6IGJvb2xlYW5cbiAgdmVyYm9zZTogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gQXV0b1VwZGF0ZXIoe1xuICBpc1VwZGF0aW5nLFxuICBvbkNoYW5nZUlzVXBkYXRpbmcsXG4gIG9uQXV0b1VwZGF0ZXJSZXN1bHQsXG4gIGF1dG9VcGRhdGVyUmVzdWx0LFxuICBzaG93U3VjY2Vzc01lc3NhZ2UsXG4gIHZlcmJvc2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt2ZXJzaW9ucywgc2V0VmVyc2lvbnNdID0gdXNlU3RhdGU8e1xuICAgIGdsb2JhbD86IHN0cmluZyB8IG51bGxcbiAgICBsYXRlc3Q/OiBzdHJpbmcgfCBudWxsXG4gIH0+KHt9KVxuICBjb25zdCBbaGFzTG9jYWxJbnN0YWxsLCBzZXRIYXNMb2NhbEluc3RhbGxdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IHVwZGF0ZVNlbXZlciA9IHVzZVVwZGF0ZU5vdGlmaWNhdGlvbihhdXRvVXBkYXRlclJlc3VsdD8udmVyc2lvbilcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIHZvaWQgbG9jYWxJbnN0YWxsYXRpb25FeGlzdHMoKS50aGVuKHNldEhhc0xvY2FsSW5zdGFsbClcbiAgfSwgW10pXG5cbiAgLy8gVHJhY2sgbGF0ZXN0IGlzVXBkYXRpbmcgdmFsdWUgaW4gYSByZWYgc28gdGhlIG1lbW9pemVkIGNoZWNrRm9yVXBkYXRlc1xuICAvLyBjYWxsYmFjayBhbHdheXMgc2VlcyB0aGUgY3VycmVudCB2YWx1ZS4gV2l0aG91dCB0aGlzLCB0aGUgMzAtbWludXRlXG4gIC8vIGludGVydmFsIGZpcmVzIHdpdGggYSBzdGFsZSBjbG9zdXJlIHdoZXJlIGlzVXBkYXRpbmcgaXMgZmFsc2UsIGFsbG93aW5nXG4gIC8vIGEgY29uY3VycmVudCBpbnN0YWxsR2xvYmFsUGFja2FnZSgpIHRvIHJ1biB3aGlsZSBvbmUgaXMgYWxyZWFkeSBpblxuICAvLyBwcm9ncmVzcy5cbiAgY29uc3QgaXNVcGRhdGluZ1JlZiA9IHVzZVJlZihpc1VwZGF0aW5nKVxuICBpc1VwZGF0aW5nUmVmLmN1cnJlbnQgPSBpc1VwZGF0aW5nXG5cbiAgY29uc3QgY2hlY2tGb3JVcGRhdGVzID0gUmVhY3QudXNlQ2FsbGJhY2soYXN5bmMgKCkgPT4ge1xuICAgIGlmIChpc1VwZGF0aW5nUmVmLmN1cnJlbnQpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChcbiAgICAgIFwicHJvZHVjdGlvblwiID09PSAndGVzdCcgfHxcbiAgICAgIFwicHJvZHVjdGlvblwiID09PSAnZGV2ZWxvcG1lbnQnXG4gICAgKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICdBdXRvVXBkYXRlcjogU2tpcHBpbmcgdXBkYXRlIGNoZWNrIGluIHRlc3QvZGV2IGVudmlyb25tZW50JyxcbiAgICAgIClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IGN1cnJlbnRWZXJzaW9uID0gTUFDUk8uVkVSU0lPTlxuICAgIGNvbnN0IGNoYW5uZWwgPSBnZXRJbml0aWFsU2V0dGluZ3MoKT8uYXV0b1VwZGF0ZXNDaGFubmVsID8/ICdsYXRlc3QnXG4gICAgbGV0IGxhdGVzdFZlcnNpb24gPSBhd2FpdCBnZXRMYXRlc3RWZXJzaW9uKGNoYW5uZWwpXG4gICAgY29uc3QgaXNEaXNhYmxlZCA9IGlzQXV0b1VwZGF0ZXJEaXNhYmxlZCgpXG5cbiAgICAvLyBDaGVjayBpZiBtYXggdmVyc2lvbiBpcyBzZXQgKHNlcnZlci1zaWRlIGtpbGwgc3dpdGNoIGZvciBhdXRvLXVwZGF0ZXMpXG4gICAgY29uc3QgbWF4VmVyc2lvbiA9IGF3YWl0IGdldE1heFZlcnNpb24oKVxuICAgIGlmIChtYXhWZXJzaW9uICYmIGxhdGVzdFZlcnNpb24gJiYgZ3QobGF0ZXN0VmVyc2lvbiwgbWF4VmVyc2lvbikpIHtcbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgYEF1dG9VcGRhdGVyOiBtYXhWZXJzaW9uICR7bWF4VmVyc2lvbn0gaXMgc2V0LCBjYXBwaW5nIHVwZGF0ZSBmcm9tICR7bGF0ZXN0VmVyc2lvbn0gdG8gJHttYXhWZXJzaW9ufWAsXG4gICAgICApXG4gICAgICBpZiAoZ3RlKGN1cnJlbnRWZXJzaW9uLCBtYXhWZXJzaW9uKSkge1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgYEF1dG9VcGRhdGVyOiBjdXJyZW50IHZlcnNpb24gJHtjdXJyZW50VmVyc2lvbn0gaXMgYWxyZWFkeSBhdCBvciBhYm92ZSBtYXhWZXJzaW9uICR7bWF4VmVyc2lvbn0sIHNraXBwaW5nIHVwZGF0ZWAsXG4gICAgICAgIClcbiAgICAgICAgc2V0VmVyc2lvbnMoeyBnbG9iYWw6IGN1cnJlbnRWZXJzaW9uLCBsYXRlc3Q6IGxhdGVzdFZlcnNpb24gfSlcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBsYXRlc3RWZXJzaW9uID0gbWF4VmVyc2lvblxuICAgIH1cblxuICAgIHNldFZlcnNpb25zKHsgZ2xvYmFsOiBjdXJyZW50VmVyc2lvbiwgbGF0ZXN0OiBsYXRlc3RWZXJzaW9uIH0pXG5cbiAgICAvLyBDaGVjayBpZiB1cGRhdGUgbmVlZGVkIGFuZCBwZXJmb3JtIHVwZGF0ZVxuICAgIGlmIChcbiAgICAgICFpc0Rpc2FibGVkICYmXG4gICAgICBjdXJyZW50VmVyc2lvbiAmJlxuICAgICAgbGF0ZXN0VmVyc2lvbiAmJlxuICAgICAgIWd0ZShjdXJyZW50VmVyc2lvbiwgbGF0ZXN0VmVyc2lvbikgJiZcbiAgICAgICFzaG91bGRTa2lwVmVyc2lvbihsYXRlc3RWZXJzaW9uKVxuICAgICkge1xuICAgICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKVxuICAgICAgb25DaGFuZ2VJc1VwZGF0aW5nKHRydWUpXG5cbiAgICAgIC8vIFJlbW92ZSBuYXRpdmUgaW5zdGFsbGVyIHN5bWxpbmsgc2luY2Ugd2UncmUgdXNpbmcgSlMtYmFzZWQgdXBkYXRlc1xuICAgICAgLy8gQnV0IG9ubHkgaWYgdXNlciBoYXNuJ3QgbWlncmF0ZWQgdG8gbmF0aXZlIGluc3RhbGxhdGlvblxuICAgICAgY29uc3QgY29uZmlnID0gZ2V0R2xvYmFsQ29uZmlnKClcbiAgICAgIGlmIChjb25maWcuaW5zdGFsbE1ldGhvZCAhPT0gJ25hdGl2ZScpIHtcbiAgICAgICAgYXdhaXQgcmVtb3ZlSW5zdGFsbGVkU3ltbGluaygpXG4gICAgICB9XG5cbiAgICAgIC8vIERldGVjdCBhY3R1YWwgcnVubmluZyBpbnN0YWxsYXRpb24gdHlwZVxuICAgICAgY29uc3QgaW5zdGFsbGF0aW9uVHlwZSA9IGF3YWl0IGdldEN1cnJlbnRJbnN0YWxsYXRpb25UeXBlKClcbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgYEF1dG9VcGRhdGVyOiBEZXRlY3RlZCBpbnN0YWxsYXRpb24gdHlwZTogJHtpbnN0YWxsYXRpb25UeXBlfWAsXG4gICAgICApXG5cbiAgICAgIC8vIFNraXAgdXBkYXRlIGZvciBkZXZlbG9wbWVudCBidWlsZHNcbiAgICAgIGlmIChpbnN0YWxsYXRpb25UeXBlID09PSAnZGV2ZWxvcG1lbnQnKSB7XG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZygnQXV0b1VwZGF0ZXI6IENhbm5vdCBhdXRvLXVwZGF0ZSBkZXZlbG9wbWVudCBidWlsZCcpXG4gICAgICAgIG9uQ2hhbmdlSXNVcGRhdGluZyhmYWxzZSlcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIENob29zZSB0aGUgYXBwcm9wcmlhdGUgdXBkYXRlIG1ldGhvZCBiYXNlZCBvbiB3aGF0J3MgYWN0dWFsbHkgcnVubmluZ1xuICAgICAgbGV0IGluc3RhbGxTdGF0dXM6IEluc3RhbGxTdGF0dXNcbiAgICAgIGxldCB1cGRhdGVNZXRob2Q6ICdsb2NhbCcgfCAnZ2xvYmFsJ1xuXG4gICAgICBpZiAoaW5zdGFsbGF0aW9uVHlwZSA9PT0gJ25wbS1sb2NhbCcpIHtcbiAgICAgICAgLy8gVXNlIGxvY2FsIHVwZGF0ZSBmb3IgbG9jYWwgaW5zdGFsbGF0aW9uc1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoJ0F1dG9VcGRhdGVyOiBVc2luZyBsb2NhbCB1cGRhdGUgbWV0aG9kJylcbiAgICAgICAgdXBkYXRlTWV0aG9kID0gJ2xvY2FsJ1xuICAgICAgICBpbnN0YWxsU3RhdHVzID0gYXdhaXQgaW5zdGFsbE9yVXBkYXRlQ2xhdWRlUGFja2FnZShjaGFubmVsKVxuICAgICAgfSBlbHNlIGlmIChpbnN0YWxsYXRpb25UeXBlID09PSAnbnBtLWdsb2JhbCcpIHtcbiAgICAgICAgLy8gVXNlIGdsb2JhbCB1cGRhdGUgZm9yIGdsb2JhbCBpbnN0YWxsYXRpb25zXG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZygnQXV0b1VwZGF0ZXI6IFVzaW5nIGdsb2JhbCB1cGRhdGUgbWV0aG9kJylcbiAgICAgICAgdXBkYXRlTWV0aG9kID0gJ2dsb2JhbCdcbiAgICAgICAgaW5zdGFsbFN0YXR1cyA9IGF3YWl0IGluc3RhbGxHbG9iYWxQYWNrYWdlKClcbiAgICAgIH0gZWxzZSBpZiAoaW5zdGFsbGF0aW9uVHlwZSA9PT0gJ25hdGl2ZScpIHtcbiAgICAgICAgLy8gVGhpcyBzaG91bGRuJ3QgaGFwcGVuIC0gbmF0aXZlIHNob3VsZCB1c2UgTmF0aXZlQXV0b1VwZGF0ZXJcbiAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICdBdXRvVXBkYXRlcjogVW5leHBlY3RlZCBuYXRpdmUgaW5zdGFsbGF0aW9uIGluIG5vbi1uYXRpdmUgdXBkYXRlcicsXG4gICAgICAgIClcbiAgICAgICAgb25DaGFuZ2VJc1VwZGF0aW5nKGZhbHNlKVxuICAgICAgICByZXR1cm5cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEZhbGxiYWNrIHRvIGNvbmZpZy1iYXNlZCBkZXRlY3Rpb24gZm9yIHVua25vd24gdHlwZXNcbiAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgIGBBdXRvVXBkYXRlcjogVW5rbm93biBpbnN0YWxsYXRpb24gdHlwZSwgZmFsbGluZyBiYWNrIHRvIGNvbmZpZ2AsXG4gICAgICAgIClcbiAgICAgICAgY29uc3QgaXNNaWdyYXRlZCA9IGNvbmZpZy5pbnN0YWxsTWV0aG9kID09PSAnbG9jYWwnXG4gICAgICAgIHVwZGF0ZU1ldGhvZCA9IGlzTWlncmF0ZWQgPyAnbG9jYWwnIDogJ2dsb2JhbCdcblxuICAgICAgICBpZiAoaXNNaWdyYXRlZCkge1xuICAgICAgICAgIGluc3RhbGxTdGF0dXMgPSBhd2FpdCBpbnN0YWxsT3JVcGRhdGVDbGF1ZGVQYWNrYWdlKGNoYW5uZWwpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgaW5zdGFsbFN0YXR1cyA9IGF3YWl0IGluc3RhbGxHbG9iYWxQYWNrYWdlKClcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBvbkNoYW5nZUlzVXBkYXRpbmcoZmFsc2UpXG5cbiAgICAgIGlmIChpbnN0YWxsU3RhdHVzID09PSAnc3VjY2VzcycpIHtcbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2F1dG9fdXBkYXRlcl9zdWNjZXNzJywge1xuICAgICAgICAgIGZyb21WZXJzaW9uOlxuICAgICAgICAgICAgY3VycmVudFZlcnNpb24gYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICB0b1ZlcnNpb246XG4gICAgICAgICAgICBsYXRlc3RWZXJzaW9uIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgZHVyYXRpb25NczogRGF0ZS5ub3coKSAtIHN0YXJ0VGltZSxcbiAgICAgICAgICB3YXNNaWdyYXRlZDogdXBkYXRlTWV0aG9kID09PSAnbG9jYWwnLFxuICAgICAgICAgIGluc3RhbGxhdGlvblR5cGU6XG4gICAgICAgICAgICBpbnN0YWxsYXRpb25UeXBlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIH0pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsb2dFdmVudCgndGVuZ3VfYXV0b191cGRhdGVyX2ZhaWwnLCB7XG4gICAgICAgICAgZnJvbVZlcnNpb246XG4gICAgICAgICAgICBjdXJyZW50VmVyc2lvbiBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIGF0dGVtcHRlZFZlcnNpb246XG4gICAgICAgICAgICBsYXRlc3RWZXJzaW9uIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgc3RhdHVzOlxuICAgICAgICAgICAgaW5zdGFsbFN0YXR1cyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIGR1cmF0aW9uTXM6IERhdGUubm93KCkgLSBzdGFydFRpbWUsXG4gICAgICAgICAgd2FzTWlncmF0ZWQ6IHVwZGF0ZU1ldGhvZCA9PT0gJ2xvY2FsJyxcbiAgICAgICAgICBpbnN0YWxsYXRpb25UeXBlOlxuICAgICAgICAgICAgaW5zdGFsbGF0aW9uVHlwZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICBvbkF1dG9VcGRhdGVyUmVzdWx0KHtcbiAgICAgICAgdmVyc2lvbjogbGF0ZXN0VmVyc2lvbixcbiAgICAgICAgc3RhdHVzOiBpbnN0YWxsU3RhdHVzLFxuICAgICAgfSlcbiAgICB9XG4gICAgLy8gaXNVcGRhdGluZyBpbnRlbnRpb25hbGx5IG9taXR0ZWQgZnJvbSBkZXBzOyB3ZSByZWFkIGlzVXBkYXRpbmdSZWZcbiAgICAvLyBpbnN0ZWFkIHNvIHRoZSBndWFyZCBpcyBhbHdheXMgY3VycmVudCB3aXRob3V0IGNoYW5naW5nIGNhbGxiYWNrXG4gICAgLy8gaWRlbnRpdHkgKHdoaWNoIHdvdWxkIHJlLXRyaWdnZXIgdGhlIGluaXRpYWwtY2hlY2sgdXNlRWZmZWN0IGJlbG93KS5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcmVhY3QtaG9va3MvZXhoYXVzdGl2ZS1kZXBzXG4gICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlRXhoYXVzdGl2ZURlcGVuZGVuY2llczogaXNVcGRhdGluZyByZWFkIHZpYSByZWZcbiAgfSwgW29uQXV0b1VwZGF0ZXJSZXN1bHRdKVxuXG4gIC8vIEluaXRpYWwgY2hlY2tcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICB2b2lkIGNoZWNrRm9yVXBkYXRlcygpXG4gIH0sIFtjaGVja0ZvclVwZGF0ZXNdKVxuXG4gIC8vIENoZWNrIGV2ZXJ5IDMwIG1pbnV0ZXNcbiAgdXNlSW50ZXJ2YWwoY2hlY2tGb3JVcGRhdGVzLCAzMCAqIDYwICogMTAwMClcblxuICBpZiAoIWF1dG9VcGRhdGVyUmVzdWx0Py52ZXJzaW9uICYmICghdmVyc2lvbnMuZ2xvYmFsIHx8ICF2ZXJzaW9ucy5sYXRlc3QpKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGlmICghYXV0b1VwZGF0ZXJSZXN1bHQ/LnZlcnNpb24gJiYgIWlzVXBkYXRpbmcpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9PlxuICAgICAge3ZlcmJvc2UgJiYgKFxuICAgICAgICA8VGV4dCBkaW1Db2xvciB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICBnbG9iYWxWZXJzaW9uOiB7dmVyc2lvbnMuZ2xvYmFsfSAmbWlkZG90OyBsYXRlc3RWZXJzaW9uOnsnICd9XG4gICAgICAgICAge3ZlcnNpb25zLmxhdGVzdH1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKX1cbiAgICAgIHtpc1VwZGF0aW5nID8gKFxuICAgICAgICA8PlxuICAgICAgICAgIDxCb3g+XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cInRleHRcIiBkaW1Db2xvciB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICAgICAgQXV0by11cGRhdGluZ+KAplxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8Lz5cbiAgICAgICkgOiAoXG4gICAgICAgIGF1dG9VcGRhdGVyUmVzdWx0Py5zdGF0dXMgPT09ICdzdWNjZXNzJyAmJlxuICAgICAgICBzaG93U3VjY2Vzc01lc3NhZ2UgJiZcbiAgICAgICAgdXBkYXRlU2VtdmVyICYmIChcbiAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIiB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICAgIOKckyBVcGRhdGUgaW5zdGFsbGVkIMK3IFJlc3RhcnQgdG8gYXBwbHlcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIClcbiAgICAgICl9XG4gICAgICB7KGF1dG9VcGRhdGVyUmVzdWx0Py5zdGF0dXMgPT09ICdpbnN0YWxsX2ZhaWxlZCcgfHxcbiAgICAgICAgYXV0b1VwZGF0ZXJSZXN1bHQ/LnN0YXR1cyA9PT0gJ25vX3Blcm1pc3Npb25zJykgJiYgKFxuICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCIgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAg4pyXIEF1dG8tdXBkYXRlIGZhaWxlZCAmbWlkZG90OyBUcnkgPFRleHQgYm9sZD5jbGF1ZGUgZG9jdG9yPC9UZXh0PiBvcnsnICd9XG4gICAgICAgICAgPFRleHQgYm9sZD5cbiAgICAgICAgICAgIHtoYXNMb2NhbEluc3RhbGxcbiAgICAgICAgICAgICAgPyBgY2Qgfi8uY2xhdWRlL2xvY2FsICYmIG5wbSB1cGRhdGUgJHtNQUNSTy5QQUNLQUdFX1VSTH1gXG4gICAgICAgICAgICAgIDogYG5wbSBpIC1nICR7TUFDUk8uUEFDS0FHRV9VUkx9YH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxTQUFTLEVBQUVDLE1BQU0sRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDbkQsU0FDRSxLQUFLQywwREFBMEQsRUFDL0RDLFFBQVEsUUFDSCxpQ0FBaUM7QUFDeEMsU0FBU0MsV0FBVyxRQUFRLGFBQWE7QUFDekMsU0FBU0MscUJB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QUFDLEVBQUU7SUFDekUsT0FBTyxJQUFJO0VBQ2I7RUFFQSxJQUFJLENBQUNSLGlCQUFpQixFQUFFWSxPQUFPLElBQUksQ0FBQ2YsVUFBVSxFQUFFO0lBQzlDLE9BQU8sSUFBSTtFQUNiO0VBRUEsT0FDRSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNwQyxNQUFNLENBQUNLLE9BQU8sSUFDTixDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFVBQVU7QUFDdEMseUJBQXlCLENBQUNHLFFBQVEsQ0FBQ0UsTUFBTSxDQUFDLHdCQUF3QixDQUFDLEdBQUc7QUFDdEUsVUFBVSxDQUFDRixRQUFRLENBQUNHLE1BQU07QUFDMUIsUUFBUSxFQUFFLElBQUksQ0FDUDtBQUNQLE1BQU0sQ0FBQ1gsVUFBVSxHQUNUO0FBQ1IsVUFBVSxDQUFDLEdBQUc7QUFDZCxZQUFZLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxVQUFVO0FBQ3ZEO0FBQ0EsWUFBWSxFQUFFLElBQUk7QUFDbEIsVUFBVSxFQUFFLEdBQUc7QUFDZixRQUFRLEdBQUcsR0FFSEcsaUJBQWlCLEVBQUV3QyxNQUFNLEtBQUssU0FBUyxJQUN2Q3ZDLGtCQUFrQixJQUNsQlUsWUFBWSxJQUNWLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFVBQVU7QUFDL0M7QUFDQSxVQUFVLEVBQUUsSUFBSSxDQUVUO0FBQ1AsTUFBTSxDQUFDLENBQUNYLGlCQUFpQixFQUFFd0MsTUFBTSxLQUFLLGdCQUFnQixJQUM5Q3hDLGlCQUFpQixFQUFFd0MsTUFBTSxLQUFLLGdCQUFnQixLQUM5QyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVO0FBQzNDLDRDQUE0QyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRztBQUNsRixVQUFVLENBQUMsSUFBSSxDQUFDLElBQUk7QUFDcEIsWUFBWSxDQUFDL0IsZUFBZSxHQUNaLG9DQUFvQ1UsS0FBSyxDQUFDc0IsV0FBVyxFQUFFLEdBQ3ZELFlBQVl0QixLQUFLLENBQUNzQixXQUFXLEVBQUU7QUFDL0MsVUFBVSxFQUFFLElBQUk7QUFDaEIsUUFBUSxFQUFFLElBQUksQ0FDUDtBQUNQLElBQUksRUFBRSxHQUFHLENBQUM7QUFFViIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/AutoUpdaterWrapper.tsx b/components/AutoUpdaterWrapper.tsx new file mode 100644 index 0000000..d812ec3 --- /dev/null +++ b/components/AutoUpdaterWrapper.tsx @@ -0,0 +1,91 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import type { AutoUpdaterResult } from '../utils/autoUpdater.js'; +import { isAutoUpdaterDisabled } from '../utils/config.js'; +import { logForDebugging } from '../utils/debug.js'; +import { getCurrentInstallationType } from '../utils/doctorDiagnostic.js'; +import { AutoUpdater } from './AutoUpdater.js'; +import { NativeAutoUpdater } from './NativeAutoUpdater.js'; +import { PackageManagerAutoUpdater } from './PackageManagerAutoUpdater.js'; +type Props = { + isUpdating: boolean; + onChangeIsUpdating: (isUpdating: boolean) => void; + onAutoUpdaterResult: (autoUpdaterResult: AutoUpdaterResult) => void; + autoUpdaterResult: AutoUpdaterResult | null; + showSuccessMessage: boolean; + verbose: boolean; +}; +export function AutoUpdaterWrapper(t0) { + const $ = _c(17); + const { + isUpdating, + onChangeIsUpdating, + onAutoUpdaterResult, + autoUpdaterResult, + showSuccessMessage, + verbose + } = t0; + const [useNativeInstaller, setUseNativeInstaller] = React.useState(null); + const [isPackageManager, setIsPackageManager] = React.useState(null); + let t1; + let t2; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + const checkInstallation = async function checkInstallation() { + if (feature("SKIP_DETECTION_WHEN_AUTOUPDATES_DISABLED") && isAutoUpdaterDisabled()) { + logForDebugging("AutoUpdaterWrapper: Skipping detection, auto-updates disabled"); + return; + } + const installationType = await getCurrentInstallationType(); + logForDebugging(`AutoUpdaterWrapper: Installation type: ${installationType}`); + setUseNativeInstaller(installationType === "native"); + setIsPackageManager(installationType === "package-manager"); + }; + checkInstallation(); + }; + t2 = []; + $[0] = t1; + $[1] = t2; + } else { + t1 = $[0]; + t2 = $[1]; + } + React.useEffect(t1, t2); + if (useNativeInstaller === null || isPackageManager === null) { + return null; + } + if (isPackageManager) { + let t3; + if ($[2] !== autoUpdaterResult || $[3] !== isUpdating || $[4] !== onAutoUpdaterResult || $[5] !== onChangeIsUpdating || $[6] !== showSuccessMessage || $[7] !== verbose) { + t3 = ; + $[2] = autoUpdaterResult; + $[3] = isUpdating; + $[4] = onAutoUpdaterResult; + $[5] = onChangeIsUpdating; + $[6] = showSuccessMessage; + $[7] = verbose; + $[8] = t3; + } else { + t3 = $[8]; + } + return t3; + } + const Updater = useNativeInstaller ? NativeAutoUpdater : AutoUpdater; + let t3; + if ($[9] !== Updater || $[10] !== autoUpdaterResult || $[11] !== isUpdating || $[12] !== onAutoUpdaterResult || $[13] !== onChangeIsUpdating || $[14] !== showSuccessMessage || $[15] !== verbose) { + t3 = ; + $[9] = Updater; + $[10] = autoUpdaterResult; + $[11] = isUpdating; + $[12] = onAutoUpdaterResult; + $[13] = onChangeIsUpdating; + $[14] = showSuccessMessage; + $[15] = verbose; + $[16] = t3; + } else { + t3 = $[16]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJBdXRvVXBkYXRlclJlc3VsdCIsImlzQXV0b1VwZGF0ZXJEaXNhYmxlZCIsImxvZ0ZvckRlYnVnZ2luZyIsImdldEN1cnJlbnRJbnN0YWxsYXRpb25UeXBlIiwiQXV0b1VwZGF0ZXIiLCJOYXRpdmVBdXRvVXBkYXRlciIsIlBhY2thZ2VNYW5hZ2VyQXV0b1VwZGF0ZXIiLCJQcm9wcyIsImlzVXBkYXRpbmciLCJvbkNoYW5nZUlzVXBkYXRpbmciLCJvbkF1dG9VcGRhdGVyUmVzdWx0IiwiYXV0b1VwZGF0ZXJSZXN1bHQiLCJzaG93U3VjY2Vzc01lc3NhZ2UiLCJ2ZXJib3NlIiwiQXV0b1VwZGF0ZXJXcmFwcGVyIiwidDAiLCIkIiwiX2MiLCJ1c2VOYXRpdmVJbnN0YWxsZXIiLCJzZXRVc2VOYXRpdmVJbnN0YWxsZXIiLCJ1c2VTdGF0ZSIsImlzUGFja2FnZU1hbmFnZXIiLCJzZXRJc1BhY2thZ2VNYW5hZ2VyIiwidDEiLCJ0MiIsIlN5bWJvbCIsImZvciIsImNoZWNrSW5zdGFsbGF0aW9uIiwiaW5zdGFsbGF0aW9uVHlwZSIsInVzZUVmZmVjdCIsInQzIiwiVXBkYXRlciJdLCJzb3VyY2VzIjpbIkF1dG9VcGRhdGVyV3JhcHBlci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgQXV0b1VwZGF0ZXJSZXN1bHQgfSBmcm9tICcuLi91dGlscy9hdXRvVXBkYXRlci5qcydcbmltcG9ydCB7IGlzQXV0b1VwZGF0ZXJEaXNhYmxlZCB9IGZyb20gJy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHsgZ2V0Q3VycmVudEluc3RhbGxhdGlvblR5cGUgfSBmcm9tICcuLi91dGlscy9kb2N0b3JEaWFnbm9zdGljLmpzJ1xuaW1wb3J0IHsgQXV0b1VwZGF0ZXIgfSBmcm9tICcuL0F1dG9VcGRhdGVyLmpzJ1xuaW1wb3J0IHsgTmF0aXZlQXV0b1VwZGF0ZXIgfSBmcm9tICcuL05hdGl2ZUF1dG9VcGRhdGVyLmpzJ1xuaW1wb3J0IHsgUGFja2FnZU1hbmFnZXJBdXRvVXBkYXRlciB9IGZyb20gJy4vUGFja2FnZU1hbmFnZXJBdXRvVXBkYXRlci5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgaXNVcGRhdGluZzogYm9vbGVhblxuICBvbkNoYW5nZUlzVXBkYXRpbmc6IChpc1VwZGF0aW5nOiBib29sZWFuKSA9PiB2b2lkXG4gIG9uQXV0b1VwZGF0ZXJSZXN1bHQ6IChhdXRvVXBkYXRlclJlc3VsdDogQXV0b1VwZGF0ZXJSZXN1bHQpID0+IHZvaWRcbiAgYXV0b1VwZGF0ZXJSZXN1bHQ6IEF1dG9VcGRhdGVyUmVzdWx0IHwgbnVsbFxuICBzaG93U3VjY2Vzc01lc3NhZ2U6IGJvb2xlYW5cbiAgdmVyYm9zZTogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gQXV0b1VwZGF0ZXJXcmFwcGVyKHtcbiAgaXNVcGRhdGluZyxcbiAgb25DaGFuZ2VJc1VwZGF0aW5nLFxuICBvbkF1dG9VcGRhdGVyUmVzdWx0LFxuICBhdXRvVXBkYXRlclJlc3VsdCxcbiAgc2hvd1N1Y2Nlc3NNZXNzYWdlLFxuICB2ZXJib3NlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbdXNlTmF0aXZlSW5zdGFsbGVyLCBzZXRVc2VOYXRpdmVJbnN0YWxsZXJdID0gUmVhY3QudXNlU3RhdGU8XG4gICAgYm9vbGVhbiB8IG51bGxcbiAgPihudWxsKVxuICBjb25zdCBbaXNQYWNrYWdlTWFuYWdlciwgc2V0SXNQYWNrYWdlTWFuYWdlcl0gPSBSZWFjdC51c2VTdGF0ZTxcbiAgICBib29sZWFuIHwgbnVsbFxuICA+KG51bGwpXG5cbiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHtcbiAgICBhc3luYyBmdW5jdGlvbiBjaGVja0luc3RhbGxhdGlvbigpIHtcbiAgICAgIC8vIFNraXAgaW5zdGFsbGF0aW9uIHR5cGUgZGV0ZWN0aW9uIGlmIGF1dG8tdXBkYXRlcyBhcmUgZGlzYWJsZWQgKGFudC1vbmx5KVxuICAgICAgLy8gVGhpcyBhdm9pZHMgcG90ZW50aWFsbHkgc2xvdyBwYWNrYWdlIG1hbmFnZXIgZGV0ZWN0aW9uIChzcGF3blN5bmMgY2FsbHMpXG4gICAgICBpZiAoXG4gICAgICAgIGZlYXR1cmUoJ1NLSVBfREVURUNUSU9OX1dIRU5fQVVUT1VQREFURVNfRElTQUJMRUQnKSAmJlxuICAgICAgICBpc0F1dG9VcGRhdGVyRGlzYWJsZWQoKVxuICAgICAgKSB7XG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAnQXV0b1VwZGF0ZXJXcmFwcGVyOiBTa2lwcGluZyBkZXRlY3Rpb24sIGF1dG8tdXBkYXRlcyBkaXNhYmxlZCcsXG4gICAgICAgIClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGluc3RhbGxhdGlvblR5cGUgPSBhd2FpdCBnZXRDdXJyZW50SW5zdGFsbGF0aW9uVHlwZSgpXG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgIGBBdXRvVXBkYXRlcldyYXBwZXI6IEluc3RhbGxhdGlvbiB0eXBlOiAke2luc3RhbGxhdGlvblR5cGV9YCxcbiAgICAgIClcbiAgICAgIHNldFVzZU5hdGl2ZUluc3RhbGxlcihpbnN0YWxsYXRpb25UeXBlID09PSAnbmF0aXZlJylcbiAgICAgIHNldElzUGFja2FnZU1hbmFnZXIoaW5zdGFsbGF0aW9uVHlwZSA9PT0gJ3BhY2thZ2UtbWFuYWdlcicpXG4gICAgfVxuXG4gICAgdm9pZCBjaGVja0luc3RhbGxhdGlvbigpXG4gIH0sIFtdKVxuXG4gIC8vIERvbid0IHJlbmRlciB1bnRpbCB3ZSBrbm93IHRoZSBpbnN0YWxsYXRpb24gdHlwZVxuICBpZiAodXNlTmF0aXZlSW5zdGFsbGVyID09PSBudWxsIHx8IGlzUGFja2FnZU1hbmFnZXIgPT09IG51bGwpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKGlzUGFja2FnZU1hbmFnZXIpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPFBhY2thZ2VNYW5hZ2VyQXV0b1VwZGF0ZXJcbiAgICAgICAgdmVyYm9zZT17dmVyYm9zZX1cbiAgICAgICAgb25BdXRvVXBkYXRlclJlc3VsdD17b25BdXRvVXBkYXRlclJlc3VsdH1cbiAgICAgICAgYXV0b1VwZGF0ZXJSZXN1bHQ9e2F1dG9VcGRhdGVyUmVzdWx0fVxuICAgICAgICBpc1VwZGF0aW5nPXtpc1VwZGF0aW5nfVxuICAgICAgICBvbkNoYW5nZUlzVXBkYXRpbmc9e29uQ2hhbmdlSXNVcGRhdGluZ31cbiAgICAgICAgc2hvd1N1Y2Nlc3NNZXNzYWdlPXtzaG93U3VjY2Vzc01lc3NhZ2V9XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IFVwZGF0ZXIgPSB1c2VOYXRpdmVJbnN0YWxsZXIgPyBOYXRpdmVBdXRvVXBkYXRlciA6IEF1dG9VcGRhdGVyXG5cbiAgcmV0dXJuIChcbiAgICA8VXBkYXRlclxuICAgICAgdmVyYm9zZT17dmVyYm9zZX1cbiAgICAgIG9uQXV0b1VwZGF0ZXJSZXN1bHQ9e29uQXV0b1VwZGF0ZXJSZXN1bHR9XG4gICAgICBhdXRvVXBkYXRlclJlc3VsdD17YXV0b1VwZGF0ZXJSZXN1bHR9XG4gICAgICBpc1VwZGF0aW5nPXtpc1VwZGF0aW5nfVxuICAgICAgb25DaGFuZ2VJc1VwZGF0aW5nPXtvbkNoYW5nZUlzVXBkYXRpbmd9XG4gICAgICBzaG93U3VjY2Vzc01lc3NhZ2U9e3Nob3dTdWNjZXNzTWVzc2FnZX1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxPQUFPLFFBQVEsWUFBWTtBQUNwQyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLGNBQWNDLGlCQUFpQixRQUFRLHlCQUF5QjtBQUNoRSxTQUFTQyxxQkFBcUIsUUFBUSxvQkFBb0I7QUFDMUQs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 \ No newline at end of file diff --git a/components/AwsAuthStatusBox.tsx b/components/AwsAuthStatusBox.tsx new file mode 100644 index 0000000..9dd5849 --- /dev/null +++ b/components/AwsAuthStatusBox.tsx @@ -0,0 +1,82 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useEffect, useState } from 'react'; +import { Box, Link, Text } from '../ink.js'; +import { type AwsAuthStatus, AwsAuthStatusManager } from '../utils/awsAuthStatusManager.js'; +const URL_RE = /https?:\/\/\S+/; +export function AwsAuthStatusBox() { + const $ = _c(11); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = AwsAuthStatusManager.getInstance().getStatus(); + $[0] = t0; + } else { + t0 = $[0]; + } + const [status, setStatus] = useState(t0); + let t1; + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + const unsubscribe = AwsAuthStatusManager.getInstance().subscribe(setStatus); + return unsubscribe; + }; + t2 = []; + $[1] = t1; + $[2] = t2; + } else { + t1 = $[1]; + t2 = $[2]; + } + useEffect(t1, t2); + if (!status.isAuthenticating && !status.error && status.output.length === 0) { + return null; + } + if (!status.isAuthenticating && !status.error) { + return null; + } + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = Cloud Authentication; + $[3] = t3; + } else { + t3 = $[3]; + } + let t4; + if ($[4] !== status.output) { + t4 = status.output.length > 0 && {status.output.slice(-5).map(_temp)}; + $[4] = status.output; + $[5] = t4; + } else { + t4 = $[5]; + } + let t5; + if ($[6] !== status.error) { + t5 = status.error && {status.error}; + $[6] = status.error; + $[7] = t5; + } else { + t5 = $[7]; + } + let t6; + if ($[8] !== t4 || $[9] !== t5) { + t6 = {t3}{t4}{t5}; + $[8] = t4; + $[9] = t5; + $[10] = t6; + } else { + t6 = $[10]; + } + return t6; +} +function _temp(line, index) { + const m = line.match(URL_RE); + if (!m) { + return {line}; + } + const url = m[0]; + const start = m.index ?? 0; + const before = line.slice(0, start); + const after = line.slice(start + url.length); + return {before}{url}{after}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiQm94IiwiTGluayIsIlRleHQiLCJBd3NBdXRoU3RhdHVzIiwiQXdzQXV0aFN0YXR1c01hbmFnZXIiLCJVUkxfUkUiLCJBd3NBdXRoU3RhdHVzQm94IiwiJCIsIl9jIiwidDAiLCJTeW1ib2wiLCJmb3IiLCJnZXRJbnN0YW5jZSIsImdldFN0YXR1cyIsInN0YXR1cyIsInNldFN0YXR1cyIsInQxIiwidDIiLCJ1bnN1YnNjcmliZSIsInN1YnNjcmliZSIsImlzQXV0aGVudGljYXRpbmciLCJlcnJvciIsIm91dHB1dCIsImxlbmd0aCIsInQzIiwidDQiLCJzbGljZSIsIm1hcCIsIl90ZW1wIiwidDUiLCJ0NiIsImxpbmUiLCJpbmRleCIsIm0iLCJtYXRjaCIsInVybCIsInN0YXJ0IiwiYmVmb3JlIiwiYWZ0ZXIiXSwic291cmNlcyI6WyJBd3NBdXRoU3RhdHVzQm94LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBMaW5rLCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBd3NBdXRoU3RhdHVzLFxuICBBd3NBdXRoU3RhdHVzTWFuYWdlcixcbn0gZnJvbSAnLi4vdXRpbHMvYXdzQXV0aFN0YXR1c01hbmFnZXIuanMnXG5cbmNvbnN0IFVSTF9SRSA9IC9odHRwcz86XFwvXFwvXFxTKy9cblxuZXhwb3J0IGZ1bmN0aW9uIEF3c0F1dGhTdGF0dXNCb3goKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3N0YXR1cywgc2V0U3RhdHVzXSA9IHVzZVN0YXRlPEF3c0F1dGhTdGF0dXM+KFxuICAgIEF3c0F1dGhTdGF0dXNNYW5hZ2VyLmdldEluc3RhbmNlKCkuZ2V0U3RhdHVzKCksXG4gIClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIC8vIFN1YnNjcmliZSB0byBzdGF0dXMgdXBkYXRlc1xuICAgIGNvbnN0IHVuc3Vic2NyaWJlID0gQXdzQXV0aFN0YXR1c01hbmFnZXIuZ2V0SW5zdGFuY2UoKS5zdWJzY3JpYmUoc2V0U3RhdHVzKVxuICAgIHJldHVybiB1bnN1YnNjcmliZVxuICB9LCBbXSlcblxuICAvLyBEb24ndCBzaG93IGFueXRoaW5nIGlmIG5vdCBhdXRoZW50aWNhdGluZyBhbmQgbm8gZXJyb3JcbiAgaWYgKCFzdGF0dXMuaXNBdXRoZW50aWNhdGluZyAmJiAhc3RhdHVzLmVycm9yICYmIHN0YXR1cy5vdXRwdXQubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIC8vIERvbid0IHNob3cgaWYgYXV0aGVudGljYXRpb24gc3VjY2VlZGVkIChubyBlcnJvciBhbmQgbm90IGF1dGhlbnRpY2F0aW5nKVxuICBpZiAoIXN0YXR1cy5pc0F1dGhlbnRpY2F0aW5nICYmICFzdGF0dXMuZXJyb3IpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgIGJvcmRlclN0eWxlPVwicm91bmRcIlxuICAgICAgYm9yZGVyQ29sb3I9XCJwZXJtaXNzaW9uXCJcbiAgICAgIHBhZGRpbmdYPXsxfVxuICAgICAgbWFyZ2luWT17MX1cbiAgICA+XG4gICAgICA8VGV4dCBib2xkIGNvbG9yPVwicGVybWlzc2lvblwiPlxuICAgICAgICBDbG91ZCBBdXRoZW50aWNhdGlvblxuICAgICAgPC9UZXh0PlxuXG4gICAgICB7c3RhdHVzLm91dHB1dC5sZW5ndGggPiAwICYmIChcbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICB7c3RhdHVzLm91dHB1dC5zbGljZSgtNSkubWFwKChsaW5lLCBpbmRleCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgbSA9IGxpbmUubWF0Y2goVVJMX1JFKVxuICAgICAgICAgICAgaWYgKCFtKSB7XG4gICAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgPFRleHQga2V5PXtpbmRleH0gZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICB7bGluZX1cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHVybCA9IG1bMF1cbiAgICAgICAgICAgIGNvbnN0IHN0YXJ0ID0gbS5pbmRleCA/PyAwXG4gICAgICAgICAgICBjb25zdCBiZWZvcmUgPSBsaW5lLnNsaWNlKDAsIHN0YXJ0KVxuICAgICAgICAgICAgY29uc3QgYWZ0ZXIgPSBsaW5lLnNsaWNlKHN0YXJ0ICsgdXJsLmxlbmd0aClcbiAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgIDxUZXh0IGtleT17aW5kZXh9IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgIHtiZWZvcmV9XG4gICAgICAgICAgICAgICAgPExpbmsgdXJsPXt1cmx9Pnt1cmx9PC9MaW5rPlxuICAgICAgICAgICAgICAgIHthZnRlcn1cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgKVxuICAgICAgICAgIH0pfVxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG5cbiAgICAgIHtzdGF0dXMuZXJyb3IgJiYgKFxuICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPntzdGF0dXMuZXJyb3J9PC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFBSUMsU0FBUyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUNsRCxTQUFTQyxHQUFHLEVBQUVDLElBQUksRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDM0MsU0FDRSxLQUFLQyxhQUFhLEVBQ2xCQyxvQkFBb0Is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 \ No newline at end of file diff --git a/components/BaseTextInput.tsx b/components/BaseTextInput.tsx new file mode 100644 index 0000000..8edc605 --- /dev/null +++ b/components/BaseTextInput.tsx @@ -0,0 +1,136 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { renderPlaceholder } from '../hooks/renderPlaceholder.js'; +import { usePasteHandler } from '../hooks/usePasteHandler.js'; +import { useDeclaredCursor } from '../ink/hooks/use-declared-cursor.js'; +import { Ansi, Box, Text, useInput } from '../ink.js'; +import type { BaseInputState, BaseTextInputProps } from '../types/textInputTypes.js'; +import type { TextHighlight } from '../utils/textHighlighting.js'; +import { HighlightedInput } from './PromptInput/ShimmeredInput.js'; +type BaseTextInputComponentProps = BaseTextInputProps & { + inputState: BaseInputState; + children?: React.ReactNode; + terminalFocus: boolean; + highlights?: TextHighlight[]; + invert?: (text: string) => string; + hidePlaceholderText?: boolean; +}; + +/** + * A base component for text inputs that handles rendering and basic input + */ +export function BaseTextInput(t0) { + const $ = _c(14); + const { + inputState, + children, + terminalFocus, + invert, + hidePlaceholderText, + ...props + } = t0; + const { + onInput, + renderedValue, + cursorLine, + cursorColumn + } = inputState; + const t1 = Boolean(props.focus && props.showCursor && terminalFocus); + let t2; + if ($[0] !== cursorColumn || $[1] !== cursorLine || $[2] !== t1) { + t2 = { + line: cursorLine, + column: cursorColumn, + active: t1 + }; + $[0] = cursorColumn; + $[1] = cursorLine; + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + const cursorRef = useDeclaredCursor(t2); + const { + wrappedOnInput, + isPasting: t3 + } = usePasteHandler({ + onPaste: props.onPaste, + onInput: (input, key) => { + if (isPasting && key.return) { + return; + } + onInput(input, key); + }, + onImagePaste: props.onImagePaste + }); + const isPasting = t3; + const { + onIsPastingChange + } = props; + React.useEffect(() => { + if (onIsPastingChange) { + onIsPastingChange(isPasting); + } + }, [isPasting, onIsPastingChange]); + const { + showPlaceholder, + renderedPlaceholder + } = renderPlaceholder({ + placeholder: props.placeholder, + value: props.value, + showCursor: props.showCursor, + focus: props.focus, + terminalFocus, + invert, + hidePlaceholderText + }); + useInput(wrappedOnInput, { + isActive: props.focus + }); + const commandWithoutArgs = props.value && props.value.trim().indexOf(" ") === -1 || props.value && props.value.endsWith(" "); + const showArgumentHint = Boolean(props.argumentHint && props.value && commandWithoutArgs && props.value.startsWith("/")); + const cursorFiltered = props.showCursor && props.highlights ? props.highlights.filter(h => h.dimColor || props.cursorOffset < h.start || props.cursorOffset >= h.end) : props.highlights; + const { + viewportCharOffset, + viewportCharEnd + } = inputState; + const filteredHighlights = cursorFiltered && viewportCharOffset > 0 ? cursorFiltered.filter(h_0 => h_0.end > viewportCharOffset && h_0.start < viewportCharEnd).map(h_1 => ({ + ...h_1, + start: Math.max(0, h_1.start - viewportCharOffset), + end: h_1.end - viewportCharOffset + })) : cursorFiltered; + const hasHighlights = filteredHighlights && filteredHighlights.length > 0; + if (hasHighlights) { + return {showArgumentHint && {props.value?.endsWith(" ") ? "" : " "}{props.argumentHint}}{children}; + } + const T0 = Box; + const T1 = Text; + const t4 = "truncate-end"; + const t5 = showPlaceholder && props.placeholderElement ? props.placeholderElement : showPlaceholder && renderedPlaceholder ? {renderedPlaceholder} : {renderedValue}; + const t6 = showArgumentHint && {props.value?.endsWith(" ") ? "" : " "}{props.argumentHint}; + let t7; + if ($[4] !== T1 || $[5] !== children || $[6] !== props || $[7] !== t5 || $[8] !== t6) { + t7 = {t5}{t6}{children}; + $[4] = T1; + $[5] = children; + $[6] = props; + $[7] = t5; + $[8] = t6; + $[9] = t7; + } else { + t7 = $[9]; + } + let t8; + if ($[10] !== T0 || $[11] !== cursorRef || $[12] !== t7) { + t8 = {t7}; + $[10] = T0; + $[11] = cursorRef; + $[12] = t7; + $[13] = t8; + } else { + t8 = $[13]; + } + return t8; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInJlbmRlclBsYWNlaG9sZGVyIiwidXNlUGFzdGVIYW5kbGVyIiwidXNlRGVjbGFyZWRDdXJzb3IiLCJBbnNpIiwiQm94IiwiVGV4dCIsInVzZUlucHV0IiwiQmFzZUlucHV0U3RhdGUiLCJCYXNlVGV4dElucHV0UHJvcHMiLCJUZXh0SGlnaGxpZ2h0IiwiSGlnaGxpZ2h0ZWRJbnB1dCIsIkJhc2VUZXh0SW5wdXRDb21wb25lbnRQcm9wcyIsImlucHV0U3RhdGUiLCJjaGlsZHJlbiIsIlJlYWN0Tm9kZSIsInRlcm1pbmFsRm9jdXMiLCJoaWdobGlnaHRzIiwiaW52ZXJ0IiwidGV4dCIsImhpZGVQbGFjZWhvbGRlclRleHQiLCJCYXNlVGV4dElucHV0IiwidDAiLCIkIiwiX2MiLCJwcm9wcyIsIm9uSW5wdXQiLCJyZW5kZXJlZFZhbHVlIiwiY3Vyc29yTGluZSIsImN1cnNvckNvbHVtbiIsInQxIiwiQm9vbGVhbiIsImZvY3VzIiwic2hvd0N1cnNvciIsInQyIiwibGluZSIsImNvbHVtbiIsImFjdGl2ZSIsImN1cnNvclJlZiIsIndyYXBwZWRPbklucHV0IiwiaXNQYXN0aW5nIiwidDMiLCJvblBhc3RlIiwiaW5wdXQiLCJrZXkiLCJyZXR1cm4iLCJvbkltYWdlUGFzdGUiLCJvbklzUGFzdGluZ0NoYW5nZSIsInVzZUVmZmVjdCIsInNob3dQbGFjZWhvbGRlciIsInJlbmRlcmVkUGxhY2Vob2xkZXIiLCJwbGFjZWhvbGRlciIsInZhbHVlIiwiaXNBY3RpdmUiLCJjb21tYW5kV2l0aG91dEFyZ3MiLCJ0cmltIiwiaW5kZXhPZiIsImVuZHNXaXRoIiwic2hvd0FyZ3VtZW50SGludCIsImFyZ3VtZW50SGludCIsInN0YXJ0c1dpdGgiLCJjdXJzb3JGaWx0ZXJlZCIsImZpbHRlciIsImgiLCJkaW1Db2xvciIsImN1cnNvck9mZnNldCIsInN0YXJ0IiwiZW5kIiwidmlld3BvcnRDaGFyT2Zmc2V0Iiwidmlld3BvcnRDaGFyRW5kIiwiZmlsdGVyZWRIaWdobGlnaHRzIiwiaF8wIiwibWFwIiwiaF8xIiwiTWF0aCIsIm1heCIsImhhc0hpZ2hsaWdodHMiLCJsZW5ndGgiLCJUMCIsIlQxIiwidDQiLCJ0NSIsInBsYWNlaG9sZGVyRWxlbWVudCIsInQ2IiwidDciLCJ0OCJdLCJzb3VyY2VzIjpbIkJhc2VUZXh0SW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHJlbmRlclBsYWNlaG9sZGVyIH0gZnJvbSAnLi4vaG9va3MvcmVuZGVyUGxhY2Vob2xkZXIuanMnXG5pbXBvcnQgeyB1c2VQYXN0ZUhhbmRsZXIgfSBmcm9tICcuLi9ob29rcy91c2VQYXN0ZUhhbmRsZXIuanMnXG5pbXBvcnQgeyB1c2VEZWNsYXJlZEN1cnNvciB9IGZyb20gJy4uL2luay9ob29rcy91c2UtZGVjbGFyZWQtY3Vyc29yLmpzJ1xuaW1wb3J0IHsgQW5zaSwgQm94LCBUZXh0LCB1c2VJbnB1dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHtcbiAgQmFzZUlucHV0U3RhdGUsXG4gIEJhc2VUZXh0SW5wdXRQcm9wcyxcbn0gZnJvbSAnLi4vdHlwZXMvdGV4dElucHV0VHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IFRleHRIaWdobGlnaHQgfSBmcm9tICcuLi91dGlscy90ZXh0SGlnaGxpZ2h0aW5nLmpzJ1xuaW1wb3J0IHsgSGlnaGxpZ2h0ZWRJbnB1dCB9IGZyb20gJy4vUHJvbXB0SW5wdXQvU2hpbW1lcmVkSW5wdXQuanMnXG5cbnR5cGUgQmFzZVRleHRJbnB1dENvbXBvbmVudFByb3BzID0gQmFzZVRleHRJbnB1dFByb3BzICYge1xuICBpbnB1dFN0YXRlOiBCYXNlSW5wdXRTdGF0ZVxuICBjaGlsZHJlbj86IFJlYWN0LlJlYWN0Tm9kZVxuICB0ZXJtaW5hbEZvY3VzOiBib29sZWFuXG4gIGhpZ2hsaWdodHM/OiBUZXh0SGlnaGxpZ2h0W11cbiAgaW52ZXJ0PzogKHRleHQ6IHN0cmluZykgPT4gc3RyaW5nXG4gIGhpZGVQbGFjZWhvbGRlclRleHQ/OiBib29sZWFuXG59XG5cbi8qKlxuICogQSBiYXNlIGNvbXBvbmVudCBmb3IgdGV4dCBpbnB1dHMgdGhhdCBoYW5kbGVzIHJlbmRlcmluZyBhbmQgYmFzaWMgaW5wdXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEJhc2VUZXh0SW5wdXQoe1xuICBpbnB1dFN0YXRlLFxuICBjaGlsZHJlbixcbiAgdGVybWluYWxGb2N1cyxcbiAgaW52ZXJ0LFxuICBoaWRlUGxhY2Vob2xkZXJUZXh0LFxuICAuLi5wcm9wc1xufTogQmFzZVRleHRJbnB1dENvbXBvbmVudFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBvbklucHV0LCByZW5kZXJlZFZhbHVlLCBjdXJzb3JMaW5lLCBjdXJzb3JDb2x1bW4gfSA9IGlucHV0U3RhdGVcblxuICAvLyBQYXJrIHRoZSBuYXRpdmUgdGVybWluYWwgY3Vyc29yIGF0IHRoZSBpbnB1dCBjYXJldC4gVGVybWluYWwgZW11bGF0b3JzXG4gIC8vIHBvc2l0aW9uIElNRSBwcmVlZGl0IHRleHQgYXQgdGhlIHBoeXNpY2FsIGN1cnNvciwgYW5kIHNjcmVlbiByZWFkZXJzIC9cbiAgLy8gc2NyZWVuIG1hZ25pZmllcnMgdHJhY2sgaXQg4oCUIHNvIHBhcmtpbmcgaGVyZSBtYWtlcyBDSksgaW5wdXQgYXBwZWFyXG4gIC8vIGlubGluZSBhbmQgbGV0cyBhY2Nlc3NpYmlsaXR5IHRvb2xzIGZvbGxvdyB0aGUgaW5wdXQuIFRoZSBCb3ggcmVmIGJlbG93XG4gIC8vIGlzIHRoZSB5b2dhIGxheW91dCBvcmlnaW47IChjdXJzb3JMaW5lLCBjdXJzb3JDb2x1bW4pIGlzIHJlbGF0aXZlIHRvIGl0LlxuICAvLyBPbmx5IGFjdGl2ZSB3aGVuIHRoZSBpbnB1dCBpcyBmb2N1c2VkLCBzaG93aW5nIGl0cyBjdXJzb3IsIGFuZCB0aGVcbiAgLy8gdGVybWluYWwgaXRzZWxmIGhhcyBmb2N1cy5cbiAgY29uc3QgY3Vyc29yUmVmID0gdXNlRGVjbGFyZWRDdXJzb3Ioe1xuICAgIGxpbmU6IGN1cnNvckxpbmUsXG4gICAgY29sdW1uOiBjdXJzb3JDb2x1bW4sXG4gICAgYWN0aXZlOiBCb29sZWFuKHByb3BzLmZvY3VzICYmIHByb3BzLnNob3dDdXJzb3IgJiYgdGVybWluYWxGb2N1cyksXG4gIH0pXG5cbiAgY29uc3QgeyB3cmFwcGVkT25JbnB1dCwgaXNQYXN0aW5nIH0gPSB1c2VQYXN0ZUhhbmRsZXIoe1xuICAgIG9uUGFzdGU6IHByb3BzLm9uUGFzdGUsXG4gICAgb25JbnB1dDogKGlucHV0LCBrZXkpID0+IHtcbiAgICAgIC8vIFByZXZlbnQgRW50ZXIga2V5IGZyb20gdHJpZ2dlcmluZyBzdWJtaXNzaW9uIGR1cmluZyBwYXN0ZVxuICAgICAgaWYgKGlzUGFzdGluZyAmJiBrZXkucmV0dXJuKSB7XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuICAgICAgb25JbnB1dChpbnB1dCwga2V5KVxuICAgIH0sXG4gICAgb25JbWFnZVBhc3RlOiBwcm9wcy5vbkltYWdlUGFzdGUsXG4gIH0pXG5cbiAgLy8gTm90aWZ5IHBhcmVudCB3aGVuIHBhc3RlIHN0YXRlIGNoYW5nZXNcbiAgY29uc3QgeyBvbklzUGFzdGluZ0NoYW5nZSB9ID0gcHJvcHNcbiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAob25Jc1Bhc3RpbmdDaGFuZ2UpIHtcbiAgICAgIG9uSXNQYXN0aW5nQ2hhbmdlKGlzUGFzdGluZylcbiAgICB9XG4gIH0sIFtpc1Bhc3RpbmcsIG9uSXNQYXN0aW5nQ2hhbmdlXSlcblxuICBjb25zdCB7IHNob3dQbGFjZWhvbGRlciwgcmVuZGVyZWRQbGFjZWhvbGRlciB9ID0gcmVuZGVyUGxhY2Vob2xkZXIoe1xuICAgIHBsYWNlaG9sZGVyOiBwcm9wcy5wbGFjZWhvbGRlcixcbiAgICB2YWx1ZTogcHJvcHMudmFsdWUsXG4gICAgc2hvd0N1cnNvcjogcHJvcHMuc2hvd0N1cnNvcixcbiAgICBmb2N1czogcHJvcHMuZm9jdXMsXG4gICAgdGVybWluYWxGb2N1cyxcbiAgICBpbnZlcnQsXG4gICAgaGlkZVBsYWNlaG9sZGVyVGV4dCxcbiAgfSlcblxuICB1c2VJbnB1dCh3cmFwcGVkT25JbnB1dCwgeyBpc0FjdGl2ZTogcHJvcHMuZm9jdXMgfSlcblxuICAvLyBTaG93IGFyZ3VtZW50IGhpbnQgb25seSB3aGVuIHdlIGhhdmUgYSB2YWx1ZSBhbmQgdGhlIGhpbnQgaXMgcHJvdmlkZWRcbiAgLy8gT25seSBzaG93IHRoZSBhcmd1bWVudCBoaW50IHdoZW46XG4gIC8vIDEuIFdlIGhhdmUgYSBoaW50IHRvIHNob3dcbiAgLy8gMi4gV2UgaGF2ZSBhIGNvbW1hbmQgdHlwZWQgKHZhbHVlIGlzIG5vdCBlbXB0eSlcbiAgLy8gMy4gVGhlIGNvbW1hbmQgZG9lc24ndCBoYXZlIGFyZ3VtZW50cyB5ZXQgKG5vIHRleHQgYWZ0ZXIgdGhlIHNwYWNlKVxuICAvLyA0LiBXZSdyZSBhY3R1YWxseSB0eXBpbmcgYSBjb21tYW5kICh0aGUgdmFsdWUgc3RhcnRzIHdpdGggLylcbiAgY29uc3QgY29tbWFuZFdpdGhvdXRBcmdzID1cbiAgICAocHJvcHMudmFsdWUgJiYgcHJvcHMudmFsdWUudHJpbSgpLmluZGV4T2YoJyAnKSA9PT0gLTEpIHx8XG4gICAgKHByb3BzLnZhbHVlICYmIHByb3BzLnZhbHVlLmVuZHNXaXRoKCcgJykpXG5cbiAgY29uc3Qgc2hvd0FyZ3VtZW50SGludCA9IEJvb2xlYW4oXG4gICAgcHJvcHMuYXJndW1lbnRIaW50ICYmXG4gICAgICBwcm9wcy52YWx1ZSAmJlxuICAgICAgY29tbWFuZFdpdGhvdXRBcmdzICYmXG4gICAgICBwcm9wcy52YWx1ZS5zdGFydHNXaXRoKCcvJyksXG4gIClcblxuICAvLyBGaWx0ZXIgb3V0IGhpZ2hsaWdodHMgdGhhdCBjb250YWluIHRoZSBjdXJzb3IgcG9zaXRpb25cbiAgY29uc3QgY3Vyc29yRmlsdGVyZWQgPVxuICAgIHByb3BzLnNob3dDdXJzb3IgJiYgcHJvcHMuaGlnaGxpZ2h0c1xuICAgICAgPyBwcm9wcy5oaWdobGlnaHRzLmZpbHRlcihcbiAgICAgICAgICBoID0+XG4gICAgICAgICAgICBoLmRpbUNvbG9yIHx8XG4gICAgICAgICAgICBwcm9wcy5jdXJzb3JPZmZzZXQgPCBoLnN0YXJ0IHx8XG4gICAgICAgICAgICBwcm9wcy5jdXJzb3JPZmZzZXQgPj0gaC5lbmQsXG4gICAgICAgIClcbiAgICAgIDogcHJvcHMuaGlnaGxpZ2h0c1xuXG4gIC8vIEFkanVzdCBoaWdobGlnaHRzIGZvciB2aWV3cG9ydCB3aW5kb3dpbmc6IGhpZ2hsaWdodCBwb3NpdGlvbnMgcmVmZXJlbmNlIHRoZVxuICAvLyBmdWxsIGlucHV0IHRleHQsIGJ1dCByZW5kZXJlZFZhbHVlIG9ubHkgY29udGFpbnMgdGhlIHdpbmRvd2VkIHN1YnNldC5cbiAgY29uc3QgeyB2aWV3cG9ydENoYXJPZmZzZXQsIHZpZXdwb3J0Q2hhckVuZCB9ID0gaW5wdXRTdGF0ZVxuICBjb25zdCBmaWx0ZXJlZEhpZ2hsaWdodHMgPVxuICAgIGN1cnNvckZpbHRlcmVkICYmIHZpZXdwb3J0Q2hhck9mZnNldCA+IDBcbiAgICAgID8gY3Vyc29yRmlsdGVyZWRcbiAgICAgICAgICAuZmlsdGVyKGggPT4gaC5lbmQgPiB2aWV3cG9ydENoYXJPZmZzZXQgJiYgaC5zdGFydCA8IHZpZXdwb3J0Q2hhckVuZClcbiAgICAgICAgICAubWFwKGggPT4gKHtcbiAgICAgICAgICAgIC4uLmgsXG4gICAgICAgICAgICBzdGFydDogTWF0aC5tYXgoMCwgaC5zdGFydCAtIHZpZXdwb3J0Q2hhck9mZnNldCksXG4gICAgICAgICAgICBlbmQ6IGguZW5kIC0gdmlld3BvcnRDaGFyT2Zmc2V0LFxuICAgICAgICAgIH0pKVxuICAgICAgOiBjdXJzb3JGaWx0ZXJlZFxuXG4gIGNvbnN0IGhhc0hpZ2hsaWdodHMgPSBmaWx0ZXJlZEhpZ2hsaWdodHMgJiYgZmlsdGVyZWRIaWdobGlnaHRzLmxlbmd0aCA+IDBcblxuICBpZiAoaGFzSGlnaGxpZ2h0cykge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IHJlZj17Y3Vyc29yUmVmfT5cbiAgICAgICAgPEhpZ2hsaWdodGVkSW5wdXRcbiAgICAgICAgICB0ZXh0PXtyZW5kZXJlZFZhbHVlfVxuICAgICAgICAgIGhpZ2hsaWdodHM9e2ZpbHRlcmVkSGlnaGxpZ2h0c31cbiAgICAgICAgLz5cbiAgICAgICAge3Nob3dBcmd1bWVudEhpbnQgJiYgKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAge3Byb3BzLnZhbHVlPy5lbmRzV2l0aCgnICcpID8gJycgOiAnICd9XG4gICAgICAgICAgICB7cHJvcHMuYXJndW1lbnRIaW50fVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKX1cbiAgICAgICAge2NoaWxkcmVufVxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IHJlZj17Y3Vyc29yUmVmfT5cbiAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZS1lbmRcIiBkaW1Db2xvcj17cHJvcHMuZGltQ29sb3J9PlxuICAgICAgICB7c2hvd1BsYWNlaG9sZGVyICYmIHByb3BzLnBsYWNlaG9sZGVyRWxlbWVudCA/IChcbiAgICAgICAgICBwcm9wcy5wbGFjZWhvbGRlckVsZW1lbnRcbiAgICAgICAgKSA6IHNob3dQbGFjZWhvbGRlciAmJiByZW5kZXJlZFBsYWNlaG9sZGVyID8gKFxuICAgICAgICAgIDxBbnNpPntyZW5kZXJlZFBsYWNlaG9sZGVyfTwvQW5zaT5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8QW5zaT57cmVuZGVyZWRWYWx1ZX08L0Fuc2k+XG4gICAgICAgICl9XG4gICAgICAgIHtzaG93QXJndW1lbnRIaW50ICYmIChcbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHtwcm9wcy52YWx1ZT8uZW5kc1dpdGgoJyAnKSA/ICcnIDogJyAnfVxuICAgICAgICAgICAge3Byb3BzLmFyZ3VtZW50SGludH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIHtjaGlsZHJlbn1cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsaUJBQWlCLFFBQVEsK0JBQStCO0FBQ2pFLFNBQVNDLGVBQWUsUUFBUSw2QkFBNkI7QUFDN0QsU0FBU0MsaUJBQWlCLFFBQVEscUNBQXFDO0FBQ3ZFLFNBQVNDLElBQUksRUFBRUMsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxXQUFXO0FBQ3JELGNBQ0VDLGNBQWMsRUFDZEMsa0JBQWtCLFFBQ2IsNEJBQTRCO0FBQ25DLGNBQWNDLGFBQWEsUUFBUSw4QkFBOEI7QUFDakUsU0FBU0MsZ0JBQWdCLFFBQVEsaUNBQWlDO0FBRWxFLEtBQUtDLDJCQUEyQixHQUFHSCxrQkFBa0IsR0FBRztFQUN0REksVUFBVSxFQUFFTCxjQUFjO0VBQzFCTSxRQUFRLENBQUMsRUFBRWQsS0FBSyxDQUFDZSxTQUFTO0VBQzFCQyxhQUFhLEVBQUUsT0FBTztFQUN0QkMsVUFBVSxDQUFDLEVBQUVQLGFBQWEsRUFBRTtFQUM1QlEsTUFBTSxDQUFDLEVBQUUsQ0FBQ0MsSUFBSSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU07RUFDakNDLG1CQUFtQixDQUFDLEVBQUUsT0FBTztBQUMvQixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBLE9BQU8sU0FBQUMsY0FBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUF1QjtJQUFBWCxVQUFBO0lBQUFDLFFBQUE7SUFBQUUsYUFBQTtJQUFBRSxNQUFBO0lBQUFFLG1CQUFBO0lBQUEsR0FBQUs7RUFBQSxJQUFBSCxFQU9BO0VBQzVCO0lBQUFJLE9BQUE7SUFBQUMsYUFBQTtJQUFBQyxVQUFBO0lBQUFDO0VBQUEsSUFBNkRoQixVQUFVO0VBWTdELE1BQUFpQixFQUFBLEdBQUFDLE9BQU8sQ0FBQ04sS0FBSyxDQUFBTyxLQUEwQixJQUFoQlAs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 \ No newline at end of file diff --git a/components/BashModeProgress.tsx b/components/BashModeProgress.tsx new file mode 100644 index 0000000..dbbce04 --- /dev/null +++ b/components/BashModeProgress.tsx @@ -0,0 +1,56 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box } from '../ink.js'; +import { BashTool } from '../tools/BashTool/BashTool.js'; +import type { ShellProgress } from '../types/tools.js'; +import { UserBashInputMessage } from './messages/UserBashInputMessage.js'; +import { ShellProgressMessage } from './shell/ShellProgressMessage.js'; +type Props = { + input: string; + progress: ShellProgress | null; + verbose: boolean; +}; +export function BashModeProgress(t0) { + const $ = _c(8); + const { + input, + progress, + verbose + } = t0; + const t1 = `${input}`; + let t2; + if ($[0] !== t1) { + t2 = ; + $[0] = t1; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== progress || $[3] !== verbose) { + t3 = progress ? : BashTool.renderToolUseProgressMessage?.([], { + verbose, + tools: [], + terminalSize: undefined + }); + $[2] = progress; + $[3] = verbose; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== t2 || $[6] !== t3) { + t4 = {t2}{t3}; + $[5] = t2; + $[6] = t3; + $[7] = t4; + } else { + t4 = $[7]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIkJhc2hUb29sIiwiU2hlbGxQcm9ncmVzcyIsIlVzZXJCYXNoSW5wdXRNZXNzYWdlIiwiU2hlbGxQcm9ncmVzc01lc3NhZ2UiLCJQcm9wcyIsImlucHV0IiwicHJvZ3Jlc3MiLCJ2ZXJib3NlIiwiQmFzaE1vZGVQcm9ncmVzcyIsInQwIiwiJCIsIl9jIiwidDEiLCJ0MiIsInRleHQiLCJ0eXBlIiwidDMiLCJmdWxsT3V0cHV0Iiwib3V0cHV0IiwiZWxhcHNlZFRpbWVTZWNvbmRzIiwidG90YWxMaW5lcyIsInJlbmRlclRvb2xVc2VQcm9ncmVzc01lc3NhZ2UiLCJ0b29scyIsInRlcm1pbmFsU2l6ZSIsInVuZGVmaW5lZCIsInQ0Il0sInNvdXJjZXMiOlsiQmFzaE1vZGVQcm9ncmVzcy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgQmFzaFRvb2wgfSBmcm9tICcuLi90b29scy9CYXNoVG9vbC9CYXNoVG9vbC5qcydcbmltcG9ydCB0eXBlIHsgU2hlbGxQcm9ncmVzcyB9IGZyb20gJy4uL3R5cGVzL3Rvb2xzLmpzJ1xuaW1wb3J0IHsgVXNlckJhc2hJbnB1dE1lc3NhZ2UgfSBmcm9tICcuL21lc3NhZ2VzL1VzZXJCYXNoSW5wdXRNZXNzYWdlLmpzJ1xuaW1wb3J0IHsgU2hlbGxQcm9ncmVzc01lc3NhZ2UgfSBmcm9tICcuL3NoZWxsL1NoZWxsUHJvZ3Jlc3NNZXNzYWdlLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBpbnB1dDogc3RyaW5nXG4gIHByb2dyZXNzOiBTaGVsbFByb2dyZXNzIHwgbnVsbFxuICB2ZXJib3NlOiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBCYXNoTW9kZVByb2dyZXNzKHtcbiAgaW5wdXQsXG4gIHByb2dyZXNzLFxuICB2ZXJib3NlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpblRvcD17MX0+XG4gICAgICA8VXNlckJhc2hJbnB1dE1lc3NhZ2VcbiAgICAgICAgYWRkTWFyZ2luPXtmYWxzZX1cbiAgICAgICAgcGFyYW09e3sgdGV4dDogYDxiYXNoLWlucHV0PiR7aW5wdXR9PC9iYXNoLWlucHV0PmAsIHR5cGU6ICd0ZXh0JyB9fVxuICAgICAgLz5cbiAgICAgIHtwcm9ncmVzcyA/IChcbiAgICAgICAgPFNoZWxsUHJvZ3Jlc3NNZXNzYWdlXG4gICAgICAgICAgZnVsbE91dHB1dD17cHJvZ3Jlc3MuZnVsbE91dHB1dH1cbiAgICAgICAgICBvdXRwdXQ9e3Byb2dyZXNzLm91dHB1dH1cbiAgICAgICAgICBlbGFwc2VkVGltZVNlY29uZHM9e3Byb2dyZXNzLmVsYXBzZWRUaW1lU2Vjb25kc31cbiAgICAgICAgICB0b3RhbExpbmVzPXtwcm9ncmVzcy50b3RhbExpbmVzfVxuICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgIC8+XG4gICAgICApIDogKFxuICAgICAgICBCYXNoVG9vbC5yZW5kZXJUb29sVXNlUHJvZ3Jlc3NNZXNzYWdlPy4oW10sIHtcbiAgICAgICAgICB2ZXJib3NlLFxuICAgICAgICAgIHRvb2xzOiBbXSxcbiAgICAgICAgICB0ZXJtaW5hbFNpemU6IHVuZGVmaW5lZCxcbiAgICAgICAgfSlcbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLEdBQUcsUUFBUSxXQUFXO0FBQy9CLFNBQVNDLFFBQVEsUUFBUSwrQkFBK0I7QUFDeEQsY0FBY0MsYUFBYSxRQUFRLG1CQUFtQjtBQUN0RCxTQUFTQyxvQkFBb0IsUUFBUSxvQ0FBb0M7QUFDekUsU0FBU0Msb0JBQW9CLFFBQVEsaUNBQWlDO0FBRXRFLEtBQUtDLEtBQUssR0FBRztFQUNYQyxLQUFLLEVBQUUsTUFBTTtFQUNiQyxRQUFRLEVBQUVMLGFBQWEsR0FBRyxJQUFJO0VBQzlCTSxPQUFPLEVBQUUsT0FBTztBQUNsQixDQUFDO0FBRUQsT0FBTyxTQUFBQyxpQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUEwQjtJQUFBTixLQUFBO0lBQUFDLFFBQUE7SUFBQUM7RUFBQSxJQUFBRSxFQUl6QjtFQUtlLE1BQUFHLEVBQUEsa0JBQWVQLEtBQUssZUFBZTtFQUFBLElBQUFRLEVBQUE7RUFBQSxJQUFBSCxDQUFBLFFBQUFFLEVBQUE7SUFGcERDLEVBQUEsSUFBQyxvQkFBb0IsQ0FDUixTQUFLLENBQUwsTUFBSSxDQUFDLENBQ1QsS0FBMkQsQ0FBM0Q7TUFBQUMsSUFBQSxFQUFRRixFQUFtQztNQUFBRyxJQUFBLEVBQVE7SUFBTyxFQUFDLEdBQ2xFO0lBQUFMLENBQUEsTUFBQUUsRUFBQTtJQUFBRixDQUFBLE1BQUFHLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFILENBQUE7RUFBQTtFQUFBLElBQUFNLEVBQUE7RUFBQSxJQUFBTixDQUFBLFFBQUFKLFFBQUEsSUFBQUksQ0FBQSxRQUFBSCxPQUFBO0lBQ0RTLEVBQUEsR0FBQVYsUUFBUSxHQUNQLENBQUMsb0JBQW9CLENBQ1AsVUFBbUIsQ0FBbkIsQ0FBQUEsUUFBUSxDQUFBVyxVQUFVLENBQUMsQ0FDdkIsTUFBZSxDQUFmLENBQUFYLFFBQVEsQ0FBQVksTUFBTSxDQUFDLENBQ0gsa0JBQTJCLENBQTNCLENBQUFaLFFBQVEsQ0FBQWEsa0JBQWtCLENBQUMsQ0FDbkMsVUFBbUIsQ0FBbkIsQ0FBQWIsUUFBUSxDQUFBYyxVQUFVLENBQUMsQ0FDdEJiLE9BQU8sQ0FBUEEsUUFBTSxDQUFDLEdBUW5CLEdBTENQLFFBQVEsQ0FBQXFCLDRCQUlOLEdBSnNDLEVBQUUsRUFBRTtNQUFBZCxPQUFBO01BQUFlLEtBQUEsRUFFbkMsRUFBRTtNQUFBQyxZQUFBLEVBQ0tDO0lBQ2hCLENBQ0YsQ0FBQztJQUFBZCxDQUFBLE1BQUFKLFFBQUE7SUFBQUksQ0FBQSxNQUFBSCxPQUFBO0lBQUFHLENBQUEsTUFBQU0sRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQU4sQ0FBQTtFQUFBO0VBQUEsSUFBQWUsRUFBQTtFQUFBLElBQUFmLENBQUEsUUFBQUcsRUFBQSxJQUFBSCxDQUFBLFFBQUFNLEVBQUE7SUFuQkhTLEVBQUEsSUFBQyxHQUFHLENBQWUsYUFBUSxDQUFSLFFBQVEsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUN0QyxDQUFBWixFQUdDLENBQ0EsQ0FBQUcsRUFjRCxDQUNGLEVBcEJDLEdBQUcsQ0FvQkU7SUFBQU4sQ0FBQSxNQUFBRyxFQUFBO0lBQUFILENBQUEsTUFBQU0sRUFBQTtJQUFBTixDQUFBLE1BQUFlLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFmLENBQUE7RUFBQTtFQUFBLE9BcEJOZSxFQW9CTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/BridgeDialog.tsx b/components/BridgeDialog.tsx new file mode 100644 index 0000000..4f25836 --- /dev/null +++ b/components/BridgeDialog.tsx @@ -0,0 +1,401 @@ +import { c as _c } from "react/compiler-runtime"; +import { basename } from 'path'; +import { toString as qrToString } from 'qrcode'; +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { getOriginalCwd } from '../bootstrap/state.js'; +import { buildActiveFooterText, buildIdleFooterText, FAILED_FOOTER_TEXT, getBridgeStatus } from '../bridge/bridgeStatusUtil.js'; +import { BRIDGE_FAILED_INDICATOR, BRIDGE_READY_INDICATOR } from '../constants/figures.js'; +import { useRegisterOverlay } from '../context/overlayContext.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw 'd' key for disconnect, not a configurable keybinding action +import { Box, Text, useInput } from '../ink.js'; +import { useKeybindings } from '../keybindings/useKeybinding.js'; +import { useAppState, useSetAppState } from '../state/AppState.js'; +import { saveGlobalConfig } from '../utils/config.js'; +import { getBranch } from '../utils/git.js'; +import { Dialog } from './design-system/Dialog.js'; +type Props = { + onDone: () => void; +}; +export function BridgeDialog(t0) { + const $ = _c(87); + const { + onDone + } = t0; + useRegisterOverlay("bridge-dialog"); + const connected = useAppState(_temp); + const sessionActive = useAppState(_temp2); + const reconnecting = useAppState(_temp3); + const connectUrl = useAppState(_temp4); + const sessionUrl = useAppState(_temp5); + const error = useAppState(_temp6); + const explicit = useAppState(_temp7); + const environmentId = useAppState(_temp8); + const sessionId = useAppState(_temp9); + const verbose = useAppState(_temp0); + const setAppState = useSetAppState(); + const [showQR, setShowQR] = useState(false); + const [qrText, setQrText] = useState(""); + const [branchName, setBranchName] = useState(""); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = basename(getOriginalCwd()); + $[0] = t1; + } else { + t1 = $[0]; + } + const repoName = t1; + let t2; + let t3; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => { + getBranch().then(setBranchName).catch(_temp1); + }; + t3 = []; + $[1] = t2; + $[2] = t3; + } else { + t2 = $[1]; + t3 = $[2]; + } + useEffect(t2, t3); + const displayUrl = sessionActive ? sessionUrl : connectUrl; + let t4; + let t5; + if ($[3] !== displayUrl || $[4] !== showQR) { + t4 = () => { + if (!showQR || !displayUrl) { + setQrText(""); + return; + } + qrToString(displayUrl, { + type: "utf8", + errorCorrectionLevel: "L", + small: true + }).then(setQrText).catch(() => setQrText("")); + }; + t5 = [showQR, displayUrl]; + $[3] = displayUrl; + $[4] = showQR; + $[5] = t4; + $[6] = t5; + } else { + t4 = $[5]; + t5 = $[6]; + } + useEffect(t4, t5); + let t6; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t6 = () => { + setShowQR(_temp10); + }; + $[7] = t6; + } else { + t6 = $[7]; + } + let t7; + if ($[8] !== onDone) { + t7 = { + "confirm:yes": onDone, + "confirm:toggle": t6 + }; + $[8] = onDone; + $[9] = t7; + } else { + t7 = $[9]; + } + let t8; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t8 = { + context: "Confirmation" + }; + $[10] = t8; + } else { + t8 = $[10]; + } + useKeybindings(t7, t8); + let t9; + if ($[11] !== explicit || $[12] !== onDone || $[13] !== setAppState) { + t9 = input => { + if (input === "d") { + if (explicit) { + saveGlobalConfig(_temp11); + } + setAppState(_temp12); + onDone(); + } + }; + $[11] = explicit; + $[12] = onDone; + $[13] = setAppState; + $[14] = t9; + } else { + t9 = $[14]; + } + useInput(t9); + let t10; + if ($[15] !== connected || $[16] !== error || $[17] !== reconnecting || $[18] !== sessionActive) { + t10 = getBridgeStatus({ + error, + connected, + sessionActive, + reconnecting + }); + $[15] = connected; + $[16] = error; + $[17] = reconnecting; + $[18] = sessionActive; + $[19] = t10; + } else { + t10 = $[19]; + } + const { + label: statusLabel, + color: statusColor + } = t10; + const indicator = error ? BRIDGE_FAILED_INDICATOR : BRIDGE_READY_INDICATOR; + let T0; + let T1; + let footerText; + let t11; + let t12; + let t13; + let t14; + let t15; + let t16; + let t17; + if ($[20] !== branchName || $[21] !== displayUrl || $[22] !== environmentId || $[23] !== error || $[24] !== indicator || $[25] !== onDone || $[26] !== qrText || $[27] !== sessionActive || $[28] !== sessionId || $[29] !== showQR || $[30] !== statusColor || $[31] !== statusLabel || $[32] !== verbose) { + const qrLines = qrText ? qrText.split("\n").filter(_temp13) : []; + let contextParts; + if ($[43] !== branchName) { + contextParts = []; + if (repoName) { + contextParts.push(repoName); + } + if (branchName) { + contextParts.push(branchName); + } + $[43] = branchName; + $[44] = contextParts; + } else { + contextParts = $[44]; + } + const contextSuffix = contextParts.length > 0 ? " \xB7 " + contextParts.join(" \xB7 ") : ""; + let t18; + if ($[45] !== displayUrl || $[46] !== error || $[47] !== sessionActive) { + t18 = error ? FAILED_FOOTER_TEXT : displayUrl ? sessionActive ? buildActiveFooterText(displayUrl) : buildIdleFooterText(displayUrl) : undefined; + $[45] = displayUrl; + $[46] = error; + $[47] = sessionActive; + $[48] = t18; + } else { + t18 = $[48]; + } + footerText = t18; + T1 = Dialog; + t15 = "Remote Control"; + t16 = onDone; + t17 = true; + T0 = Box; + t11 = "column"; + t12 = 1; + let t19; + if ($[49] !== indicator || $[50] !== statusColor || $[51] !== statusLabel) { + t19 = {indicator} {statusLabel}; + $[49] = indicator; + $[50] = statusColor; + $[51] = statusLabel; + $[52] = t19; + } else { + t19 = $[52]; + } + let t20; + if ($[53] !== contextSuffix) { + t20 = {contextSuffix}; + $[53] = contextSuffix; + $[54] = t20; + } else { + t20 = $[54]; + } + let t21; + if ($[55] !== t19 || $[56] !== t20) { + t21 = {t19}{t20}; + $[55] = t19; + $[56] = t20; + $[57] = t21; + } else { + t21 = $[57]; + } + let t22; + if ($[58] !== error) { + t22 = error && {error}; + $[58] = error; + $[59] = t22; + } else { + t22 = $[59]; + } + let t23; + if ($[60] !== environmentId || $[61] !== verbose) { + t23 = verbose && environmentId && Environment: {environmentId}; + $[60] = environmentId; + $[61] = verbose; + $[62] = t23; + } else { + t23 = $[62]; + } + let t24; + if ($[63] !== sessionId || $[64] !== verbose) { + t24 = verbose && sessionId && Session: {sessionId}; + $[63] = sessionId; + $[64] = verbose; + $[65] = t24; + } else { + t24 = $[65]; + } + if ($[66] !== t21 || $[67] !== t22 || $[68] !== t23 || $[69] !== t24) { + t13 = {t21}{t22}{t23}{t24}; + $[66] = t21; + $[67] = t22; + $[68] = t23; + $[69] = t24; + $[70] = t13; + } else { + t13 = $[70]; + } + t14 = showQR && qrLines.length > 0 && {qrLines.map(_temp14)}; + $[20] = branchName; + $[21] = displayUrl; + $[22] = environmentId; + $[23] = error; + $[24] = indicator; + $[25] = onDone; + $[26] = qrText; + $[27] = sessionActive; + $[28] = sessionId; + $[29] = showQR; + $[30] = statusColor; + $[31] = statusLabel; + $[32] = verbose; + $[33] = T0; + $[34] = T1; + $[35] = footerText; + $[36] = t11; + $[37] = t12; + $[38] = t13; + $[39] = t14; + $[40] = t15; + $[41] = t16; + $[42] = t17; + } else { + T0 = $[33]; + T1 = $[34]; + footerText = $[35]; + t11 = $[36]; + t12 = $[37]; + t13 = $[38]; + t14 = $[39]; + t15 = $[40]; + t16 = $[41]; + t17 = $[42]; + } + let t18; + if ($[71] !== footerText) { + t18 = footerText && {footerText}; + $[71] = footerText; + $[72] = t18; + } else { + t18 = $[72]; + } + let t19; + if ($[73] === Symbol.for("react.memo_cache_sentinel")) { + t19 = d to disconnect · space for QR code · Enter/Esc to close; + $[73] = t19; + } else { + t19 = $[73]; + } + let t20; + if ($[74] !== T0 || $[75] !== t11 || $[76] !== t12 || $[77] !== t13 || $[78] !== t14 || $[79] !== t18) { + t20 = {t13}{t14}{t18}{t19}; + $[74] = T0; + $[75] = t11; + $[76] = t12; + $[77] = t13; + $[78] = t14; + $[79] = t18; + $[80] = t20; + } else { + t20 = $[80]; + } + let t21; + if ($[81] !== T1 || $[82] !== t15 || $[83] !== t16 || $[84] !== t17 || $[85] !== t20) { + t21 = {t20}; + $[81] = T1; + $[82] = t15; + $[83] = t16; + $[84] = t17; + $[85] = t20; + $[86] = t21; + } else { + t21 = $[86]; + } + return t21; +} +function _temp14(line, i) { + return {line}; +} +function _temp13(l) { + return l.length > 0; +} +function _temp12(prev_0) { + if (!prev_0.replBridgeEnabled) { + return prev_0; + } + return { + ...prev_0, + replBridgeEnabled: false + }; +} +function _temp11(current) { + if (current.remoteControlAtStartup === false) { + return current; + } + return { + ...current, + remoteControlAtStartup: false + }; +} +function _temp10(prev) { + return !prev; +} +function _temp1() {} +function _temp0(s_8) { + return s_8.verbose; +} +function _temp9(s_7) { + return s_7.replBridgeSessionId; +} +function _temp8(s_6) { + return s_6.replBridgeEnvironmentId; +} +function _temp7(s_5) { + return s_5.replBridgeExplicit; +} +function _temp6(s_4) { + return s_4.replBridgeError; +} +function _temp5(s_3) { + return s_3.replBridgeSessionUrl; +} +function _temp4(s_2) { + return s_2.replBridgeConnectUrl; +} +function _temp3(s_1) { + return s_1.replBridgeReconnecting; +} +function _temp2(s_0) { + return s_0.replBridgeSessionActive; +} +function _temp(s) { + return s.replBridgeConnected; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJiYXNlbmFtZSIsInRvU3RyaW5nIiwicXJUb1N0cmluZyIsIlJlYWN0IiwidXNlRWZmZWN0IiwidXNlU3RhdGUiLCJnZXRPcmlnaW5hbEN3ZCIsImJ1aWxkQWN0aXZlRm9vdGVyVGV4dCIsImJ1aWxkSWRsZUZvb3RlclRleHQiLCJGQUlMRURfRk9PVEVSX1RFWFQiLCJnZXRCcmlkZ2VTdGF0dXMiLCJCUklER0VfRkFJTEVEX0lORElDQVRPUiIsIkJSSURHRV9SRUFEWV9JTkRJQ0FUT1IiLCJ1c2VSZWdpc3Rlck92ZXJsYXkiLCJCb3giLCJUZXh0IiwidXNlSW5wdXQiLCJ1c2VLZXliaW5kaW5ncyIsInVzZUFwcFN0YXRlIiwidXNlU2V0QXBwU3RhdGUiLCJzYXZlR2xvYmFsQ29uZmlnIiwiZ2V0QnJhbmNoIiwiRGlhbG9nIiwiUHJvcHMiLCJvbkRvbmUiLCJCcmlkZ2VEaWFsb2ciLCJ0MCIsIiQiLCJfYyIsImNvbm5lY3RlZCIsIl90ZW1wIiwic2Vzc2lvbkFjdGl2ZSIsIl90ZW1wMiIsInJlY29ubmVjdGluZyIsIl90ZW1wMyIsImNvbm5lY3RVcmwiLCJfdGVtcDQiLCJzZXNzaW9uVXJsIiwiX3RlbXA1IiwiZXJyb3IiLCJfdGVtcDYiLCJleHBsaWNpdCIsIl90ZW1wNyIsImVudmlyb25tZW50SWQiLCJfdGVtcDgiLCJzZXNzaW9uSWQiLCJfdGVtcDkiLCJ2ZXJib3NlIiwiX3RlbXAwIiwic2V0QXBwU3RhdGUiLCJzaG93UVIiLCJzZXRTaG93UVIiLCJxclRleHQiLCJzZXRRclRleHQiLCJicmFuY2hOYW1lIiwic2V0QnJhbmNoTmFtZSIsInQxIiwiU3ltYm9sIiwiZm9yIiwicmVwb05hbWUiLCJ0MiIsInQzIiwidGhlbiIsImNhdGNoIiwiX3RlbXAxIiwiZGlzcGxheVVybCIsInQ0IiwidDUiLCJ0eXBlIiwiZXJyb3JDb3JyZWN0aW9uTGV2ZWwiLCJzbWFsbCIsInQ2IiwiX3RlbXAxMCIsInQ3IiwidDgiLCJjb250ZXh0IiwidDkiLCJpbnB1dCIsIl90ZW1wMTEiLCJfdGVtcDEyIiwidDEwIiwibGFiZWwiLCJzdGF0dXNMYWJlbCIsImNvbG9yIiwic3RhdHVzQ29sb3IiLCJpbmRpY2F0b3IiLCJUMCIsIlQxIiwiZm9vdGVyVGV4dCIsInQxMSIsInQxMiIsInQxMyIsInQxNCIsInQxNSIsInQxNiIsInQxNyIsInFyTGluZXMiLCJzcGxpdCIsImZpbHRlciIsIl90ZW1wMTMiLCJjb250ZXh0UGFydHMiLCJwdXNoIiwiY29udGV4dFN1ZmZpeCIsImxlbmd0aCIsImpvaW4iLCJ0MTgiLCJ1bmRlZmluZWQiLCJ0MTkiLCJ0MjAiLCJ0MjEiLCJ0MjIiLCJ0MjMiLCJ0MjQiLCJtYXAiLCJfdGVtcDE0IiwibGluZSIsImkiLCJsIiwicHJldl8wIiwicHJldiIsInJlcGxCcmlkZ2VFbmFibGVkIiwiY3VycmVudCIsInJlbW90ZUNvbnRyb2xBdFN0YXJ0dXAiLCJzXzgiLCJzIiwic183IiwicmVwbEJyaWRnZVNlc3Npb25JZCIsInNfNiIsInJlcGxCcmlkZ2VFbnZpcm9ubWVudElkIiwic181IiwicmVwbEJyaWRnZUV4cGxpY2l0Iiwic180IiwicmVwbEJyaWRnZUVycm9yIiwic18zIiwicmVwbEJyaWRnZVNlc3Npb25VcmwiLCJzXzIiLCJyZXBsQnJpZGdlQ29ubmVjdFVybCIsInNfMSIsInJlcGxCcmlkZ2VSZWNvbm5lY3RpbmciLCJzXzAiLCJyZXBsQnJpZGdlU2Vzc2lvbkFjdGl2ZSIsInJlcGxCcmlkZ2VDb25uZWN0ZWQiXSwic291cmNlcyI6WyJCcmlkZ2VEaWFsb2cudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGJhc2VuYW1lIH0gZnJvbSAncGF0aCdcbmltcG9ydCB7IHRvU3RyaW5nIGFzIHFyVG9TdHJpbmcgfSBmcm9tICdxcmNvZGUnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUVmZmVjdCwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGdldE9yaWdpbmFsQ3dkIH0gZnJvbSAnLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHtcbiAgYnVpbGRBY3RpdmVGb290ZXJUZXh0LFxuICBidWlsZElkbGVGb290ZXJUZXh0LFxuICBGQUlMRURfRk9PVEVSX1RFWFQsXG4gIGdldEJyaWRnZVN0YXR1cyxcbn0gZnJvbSAnLi4vYnJpZGdlL2JyaWRnZVN0YXR1c1V0aWwuanMnXG5pbXBvcnQge1xuICBCUklER0VfRkFJTEVEX0lORElDQVRPUixcbiAgQlJJREdFX1JFQURZX0lORElDQVRPUixcbn0gZnJvbSAnLi4vY29uc3RhbnRzL2ZpZ3VyZXMuanMnXG5pbXBvcnQgeyB1c2VSZWdpc3Rlck92ZXJsYXkgfSBmcm9tICcuLi9jb250ZXh0L292ZXJsYXlDb250ZXh0LmpzJ1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9wcmVmZXItdXNlLWtleWJpbmRpbmdzIC0tIHJhdyAnZCcga2V5IGZvciBkaXNjb25uZWN0LCBub3QgYSBjb25maWd1cmFibGUga2V5YmluZGluZyBhY3Rpb25cbmltcG9ydCB7IEJveCwgVGV4dCwgdXNlSW5wdXQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5ncyB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSwgdXNlU2V0QXBwU3RhdGUgfSBmcm9tICcuLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IHNhdmVHbG9iYWxDb25maWcgfSBmcm9tICcuLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgeyBnZXRCcmFuY2ggfSBmcm9tICcuLi91dGlscy9naXQuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBvbkRvbmU6ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIEJyaWRnZURpYWxvZyh7IG9uRG9uZSB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHVzZVJlZ2lzdGVyT3ZlcmxheSgnYnJpZGdlLWRpYWxvZycpXG5cbiAgY29uc3QgY29ubmVjdGVkID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VDb25uZWN0ZWQpXG4gIGNvbnN0IHNlc3Npb25BY3RpdmUgPSB1c2VBcHBTdGF0ZShzID0+IHMucmVwbEJyaWRnZVNlc3Npb25BY3RpdmUpXG4gIGNvbnN0IHJlY29ubmVjdGluZyA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlUmVjb25uZWN0aW5nKVxuICBjb25zdCBjb25uZWN0VXJsID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VDb25uZWN0VXJsKVxuICBjb25zdCBzZXNzaW9uVXJsID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VTZXNzaW9uVXJsKVxuICBjb25zdCBlcnJvciA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlRXJyb3IpXG4gIGNvbnN0IGV4cGxpY2l0ID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VFeHBsaWNpdClcbiAgY29uc3QgZW52aXJvbm1lbnRJZCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlRW52aXJvbm1lbnRJZClcbiAgY29uc3Qgc2Vzc2lvbklkID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VTZXNzaW9uSWQpXG4gIGNvbnN0IHZlcmJvc2UgPSB1c2VBcHBTdGF0ZShzID0+IHMudmVyYm9zZSlcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG5cbiAgY29uc3QgW3Nob3dRUiwgc2V0U2hvd1FSXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbcXJUZXh0LCBzZXRRclRleHRdID0gdXNlU3RhdGUoJycpXG4gIGNvbnN0IFticmFuY2hOYW1lLCBzZXRCcmFuY2hOYW1lXSA9IHVzZVN0YXRlKCcnKVxuXG4gIGNvbnN0IHJlcG9OYW1lID0gYmFzZW5hbWUoZ2V0T3JpZ2luYWxDd2QoKSlcblxuICAvLyBGZXRjaCBicmFuY2ggbmFtZSBvbiBtb3VudFxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGdldEJyYW5jaCgpXG4gICAgICAudGhlbihzZXRCcmFuY2hOYW1lKVxuICAgICAgLmNhdGNoKCgpID0+IHt9KVxuICB9LCBbXSlcblxuICAvLyBUaGUgVVJMIHRvIGRpc3BsYXkvUVI6IHNlc3Npb24gVVJMIHdoZW4gY29ubmVjdGVkLCBjb25uZWN0IFVSTCB3aGVuIHJlYWR5XG4gIGNvbnN0IGRpc3BsYXlVcmwgPSBzZXNzaW9uQWN0aXZlID8gc2Vzc2lvblVybCA6IGNvbm5lY3RVcmxcblxuICAvLyBHZW5lcmF0ZSBRUiBjb2RlIHdoZW4gVVJMIGNoYW5nZXMgb3IgUVIgaXMgdG9nZ2xlZCBvblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghc2hvd1FSIHx8ICFkaXNwbGF5VXJsKSB7XG4gICAgICBzZXRRclRleHQoJycpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgcXJUb1N0cmluZyhkaXNwbGF5VXJsLCB7XG4gICAgICB0eXBlOiAndXRmOCcsXG4gICAgICBlcnJvckNvcnJlY3Rpb25MZXZlbDogJ0wnLFxuICAgICAgc21hbGw6IHRydWUsXG4gICAgfSlcbiAgICAgIC50aGVuKHNldFFyVGV4dClcbiAgICAgIC5jYXRjaCgoKSA9PiBzZXRRclRleHQoJycpKVxuICB9LCBbc2hvd1FSLCBkaXNwbGF5VXJsXSlcblxuICB1c2VLZXliaW5kaW5ncyhcbiAgICB7XG4gICAgICAnY29uZmlybTp5ZXMnOiBvbkRvbmUsXG4gICAgICAnY29uZmlybTp0b2dnbGUnOiAoKSA9PiB7XG4gICAgICAgIHNldFNob3dRUihwcmV2ID0+ICFwcmV2KVxuICAgICAgfSxcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicgfSxcbiAgKVxuXG4gIHVzZUlucHV0KGlucHV0ID0+IHtcbiAgICBpZiAoaW5wdXQgPT09ICdkJykge1xuICAgICAgLy8gUGVyc2lzdCBvcHQtb3V0IG9ubHkgZm9yIENMSS1mbGFnL2NvbW1hbmQtYWN0aXZhdGVkIGJyaWRnZS5cbiAgICAgIC8vIENvbmZpZy1kcml2ZW4gYW5kIEdCLWF1dG8tY29ubmVjdCB1c2VycyBnZXQgc2Vzc2lvbi1vbmx5IGRpc2Nvbm5lY3RcbiAgICAgIC8vIOKAlCB3cml0aW5nIGZhbHNlIHdvdWxkIHNpbGVudGx5IHVuZG8gYSBTZXR0aW5ncyBjaG9pY2Ugb3Igb3B0IGFcbiAgICAgIC8vIEdCLXJvbGxvdXQgdXNlciBvdXQgcGVybWFuZW50bHkuXG4gICAgICBpZiAoZXhwbGljaXQpIHtcbiAgICAgICAgc2F2ZUdsb2JhbENvbmZpZyhjdXJyZW50ID0+IHtcbiAgICAgICAgICBpZiAoY3VycmVudC5yZW1vdGVDb250cm9sQXRTdGFydHVwID09PSBmYWxzZSkgcmV0dXJuIGN1cnJlbnRcbiAgICAgICAgICByZXR1cm4geyAuLi5jdXJyZW50LCByZW1vdGVDb250cm9sQXRTdGFydHVwOiBmYWxzZSB9XG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgaWYgKCFwcmV2LnJlcGxCcmlkZ2VFbmFibGVkKSByZXR1cm4gcHJldlxuICAgICAgICByZXR1cm4geyAuLi5wcmV2LCByZXBsQnJpZGdlRW5hYmxlZDogZmFsc2UgfVxuICAgICAgfSlcbiAgICAgIG9uRG9uZSgpXG4gICAgfVxuICB9KVxuXG4gIGNvbnN0IHsgbGFiZWw6IHN0YXR1c0xhYmVsLCBjb2xvcjogc3RhdHVzQ29sb3IgfSA9IGdldEJyaWRnZVN0YXR1cyh7XG4gICAgZXJyb3IsXG4gICAgY29ubmVjdGVkLFxuICAgIHNlc3Npb25BY3RpdmUsXG4gICAgcmVjb25uZWN0aW5nLFxuICB9KVxuICBjb25zdCBpbmRpY2F0b3IgPSBlcnJvciA/IEJSSURHRV9GQUlMRURfSU5ESUNBVE9SIDogQlJJREdFX1JFQURZX0lORElDQVRPUlxuICBjb25zdCBxckxpbmVzID0gcXJUZXh0ID8gcXJUZXh0LnNwbGl0KCdcXG4nKS5maWx0ZXIobCA9PiBsLmxlbmd0aCA+IDApIDogW11cblxuICAvLyBCdWlsZCBzdWZmaXggd2l0aCByZXBvIGFuZCBicmFuY2ggKG1hdGNoZXMgc3RhbmRhbG9uZSBicmlkZ2UgZm9ybWF0KVxuICBjb25zdCBjb250ZXh0UGFydHM6IHN0cmluZ1tdID0gW11cbiAgaWYgKHJlcG9OYW1lKSBjb250ZXh0UGFydHMucHVzaChyZXBvTmFtZSlcbiAgaWYgKGJyYW5jaE5hbWUpIGNvbnRleHRQYXJ0cy5wdXNoKGJyYW5jaE5hbWUpXG4gIGNvbnN0IGNvbnRleHRTdWZmaXggPVxuICAgIGNvbnRleHRQYXJ0cy5sZW5ndGggPiAwID8gJyBcXHUwMGI3ICcgKyBjb250ZXh0UGFydHMuam9pbignIFxcdTAwYjcgJykgOiAnJ1xuXG4gIC8vIEZvb3RlciB0ZXh0IG1hdGNoZXMgc3RhbmRhbG9uZSBicmlkZ2VcbiAgY29uc3QgZm9vdGVyVGV4dCA9IGVycm9yXG4gICAgPyBGQUlMRURfRk9PVEVSX1RFWFRcbiAgICA6IGRpc3BsYXlVcmxcbiAgICAgID8gc2Vzc2lvbkFjdGl2ZVxuICAgICAgICA/IGJ1aWxkQWN0aXZlRm9vdGVyVGV4dChkaXNwbGF5VXJsKVxuICAgICAgICA6IGJ1aWxkSWRsZUZvb3RlclRleHQoZGlzcGxheVVybClcbiAgICAgIDogdW5kZWZpbmVkXG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nIHRpdGxlPVwiUmVtb3RlIENvbnRyb2xcIiBvbkNhbmNlbD17b25Eb25lfSBoaWRlSW5wdXRHdWlkZT5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9e3N0YXR1c0NvbG9yfT5cbiAgICAgICAgICAgICAge2luZGljYXRvcn0ge3N0YXR1c0xhYmVsfVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+e2NvbnRleHRTdWZmaXh9PC9UZXh0PlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICB7ZXJyb3IgJiYgPFRleHQgY29sb3I9XCJlcnJvclwiPntlcnJvcn08L1RleHQ+fVxuICAgICAgICAgIHt2ZXJib3NlICYmIGVudmlyb25tZW50SWQgJiYgKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+RW52aXJvbm1lbnQ6IHtlbnZpcm9ubWVudElkfTwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICAgIHt2ZXJib3NlICYmIHNlc3Npb25JZCAmJiA8VGV4dCBkaW1Db2xvcj5TZXNzaW9uOiB7c2Vzc2lvbklkfTwvVGV4dD59XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7c2hvd1FSICYmIHFyTGluZXMubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICB7cXJMaW5lcy5tYXAoKGxpbmUsIGkpID0+IChcbiAgICAgICAgICAgICAgPFRleHQga2V5PXtpfT57bGluZX08L1RleHQ+XG4gICAgICAgICAgICApKX1cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgICAge2Zvb3RlclRleHQgJiYgPFRleHQgZGltQ29sb3I+e2Zvb3RlclRleHR9PC9UZXh0Pn1cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgZCB0byBkaXNjb25uZWN0IMK3IHNwYWNlIGZvciBRUiBjb2RlIMK3IEVudGVyL0VzYyB0byBjbG9zZVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsUUFBUSxRQUFRLE1BQU07QUFDL0IsU0FBU0MsUUFBUSxJQUFJQyxVQUFVLFFBQVEsUUFBUTtBQUMvQyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFNBQVMsRUFBRUMsUUFBUSxRQUFRLE9B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 \ No newline at end of file diff --git a/components/BypassPermissionsModeDialog.tsx b/components/BypassPermissionsModeDialog.tsx new file mode 100644 index 0000000..ed09416 --- /dev/null +++ b/components/BypassPermissionsModeDialog.tsx @@ -0,0 +1,87 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback } from 'react'; +import { logEvent } from 'src/services/analytics/index.js'; +import { Box, Link, Newline, Text } from '../ink.js'; +import { gracefulShutdownSync } from '../utils/gracefulShutdown.js'; +import { updateSettingsForSource } from '../utils/settings/settings.js'; +import { Select } from './CustomSelect/index.js'; +import { Dialog } from './design-system/Dialog.js'; +type Props = { + onAccept(): void; +}; +export function BypassPermissionsModeDialog(t0) { + const $ = _c(7); + const { + onAccept + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = []; + $[0] = t1; + } else { + t1 = $[0]; + } + React.useEffect(_temp, t1); + let t2; + if ($[1] !== onAccept) { + t2 = function onChange(value) { + bb3: switch (value) { + case "accept": + { + logEvent("tengu_bypass_permissions_mode_dialog_accept", {}); + updateSettingsForSource("userSettings", { + skipDangerousModePermissionPrompt: true + }); + onAccept(); + break bb3; + } + case "decline": + { + gracefulShutdownSync(1); + } + } + }; + $[1] = onAccept; + $[2] = t2; + } else { + t2 = $[2]; + } + const onChange = t2; + const handleEscape = _temp2; + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = In Bypass Permissions mode, Claude Code will not ask for your approval before running potentially dangerous commands.This mode should only be used in a sandboxed container/VM that has restricted internet access and can easily be restored if damaged.By proceeding, you accept all responsibility for actions taken while running in Bypass Permissions mode.; + $[3] = t3; + } else { + t3 = $[3]; + } + let t4; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t4 = [{ + label: "No, exit", + value: "decline" + }, { + label: "Yes, I accept", + value: "accept" + }]; + $[4] = t4; + } else { + t4 = $[4]; + } + let t5; + if ($[5] !== onChange) { + t5 = {t3}; + $[10] = handleSelect; + $[11] = t7; + $[12] = t8; + } else { + t8 = $[12]; + } + let t9; + if ($[13] !== handleCancel || $[14] !== t3 || $[15] !== t8) { + t9 = {t3}{t4}{t8}; + $[13] = handleCancel; + $[14] = t3; + $[15] = t8; + $[16] = t9; + } else { + t9 = $[16]; + } + return t9; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJTZWxlY3QiLCJEaWFsb2ciLCJDaGFubmVsRG93bmdyYWRlQ2hvaWNlIiwiUHJvcHMiLCJjdXJyZW50VmVyc2lvbiIsIm9uQ2hvaWNlIiwiY2hvaWNlIiwiQ2hhbm5lbERvd25ncmFkZURpYWxvZyIsInQwIiwiJCIsIl9jIiwidDEiLCJoYW5kbGVTZWxlY3QiLCJ2YWx1ZSIsInQyIiwiaGFuZGxlQ2FuY2VsIiwidDMiLCJ0NCIsIlN5bWJvbCIsImZvciIsInQ1IiwibGFiZWwiLCJ0NiIsInQ3IiwidDgiLCJ0OSJdLCJzb3VyY2VzIjpbIkNoYW5uZWxEb3duZ3JhZGVEaWFsb2cudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuL0N1c3RvbVNlbGVjdC9pbmRleC5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5cbmV4cG9ydCB0eXBlIENoYW5uZWxEb3duZ3JhZGVDaG9pY2UgPSAnZG93bmdyYWRlJyB8ICdzdGF5JyB8ICdjYW5jZWwnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGN1cnJlbnRWZXJzaW9uOiBzdHJpbmdcbiAgb25DaG9pY2U6IChjaG9pY2U6IENoYW5uZWxEb3duZ3JhZGVDaG9pY2UpID0+IHZvaWRcbn1cblxuLyoqXG4gKiBEaWFsb2cgc2hvd24gd2hlbiBzd2l0Y2hpbmcgZnJvbSBsYXRlc3QgdG8gc3RhYmxlIGNoYW5uZWwuXG4gKiBBbGxvd3MgdXNlciB0byBjaG9vc2Ugd2hldGhlciB0byBkb3duZ3JhZGUgb3Igc3RheSBvbiBjdXJyZW50IHZlcnNpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBDaGFubmVsRG93bmdyYWRlRGlhbG9nKHtcbiAgY3VycmVudFZlcnNpb24sXG4gIG9uQ2hvaWNlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBmdW5jdGlvbiBoYW5kbGVTZWxlY3QodmFsdWU6IENoYW5uZWxEb3duZ3JhZGVDaG9pY2UpOiB2b2lkIHtcbiAgICBvbkNob2ljZSh2YWx1ZSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGhhbmRsZUNhbmNlbCgpOiB2b2lkIHtcbiAgICBvbkNob2ljZSgnY2FuY2VsJylcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJTd2l0Y2ggdG8gU3RhYmxlIENoYW5uZWxcIlxuICAgICAgb25DYW5jZWw9e2hhbmRsZUNhbmNlbH1cbiAgICAgIGNvbG9yPVwicGVybWlzc2lvblwiXG4gICAgICBoaWRlQm9yZGVyXG4gICAgICBoaWRlSW5wdXRHdWlkZVxuICAgID5cbiAgICAgIDxUZXh0PlxuICAgICAgICBUaGUgc3RhYmxlIGNoYW5uZWwgbWF5IGhhdmUgYW4gb2xkZXIgdmVyc2lvbiB0aGFuIHdoYXQgeW91JmFwb3M7cmVcbiAgICAgICAgY3VycmVudGx5IHJ1bm5pbmcgKHtjdXJyZW50VmVyc2lvbn0pLlxuICAgICAgPC9UZXh0PlxuICAgICAgPFRleHQgZGltQ29sb3I+SG93IHdvdWxkIHlvdSBsaWtlIHRvIGhhbmRsZSB0aGlzPzwvVGV4dD5cbiAgICAgIDxTZWxlY3RcbiAgICAgICAgb3B0aW9ucz17W1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAnQWxsb3cgcG9zc2libGUgZG93bmdyYWRlIHRvIHN0YWJsZSB2ZXJzaW9uJyxcbiAgICAgICAgICAgIHZhbHVlOiAnZG93bmdyYWRlJyBhcyBDaGFubmVsRG93bmdyYWRlQ2hvaWNlLFxuICAgICAgICAgIH0sXG4gICAgICAgICAge1xuICAgICAgICAgICAgbGFiZWw6IGBTdGF5IG9uIGN1cnJlbnQgdmVyc2lvbiAoJHtjdXJyZW50VmVyc2lvbn0pIHVudGlsIHN0YWJsZSBjYXRjaGVzIHVwYCxcbiAgICAgICAgICAgIHZhbHVlOiAnc3RheScgYXMgQ2hhbm5lbERvd25ncmFkZUNob2ljZSxcbiAgICAgICAgICB9LFxuICAgICAgICBdfVxuICAgICAgICBvbkNoYW5nZT17aGFuZGxlU2VsZWN0fVxuICAgICAgLz5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsSUFBSSxRQUFRLFdBQVc7QUFDaEMsU0FBU0MsTUFBTSxRQUFRLHlCQUF5QjtBQUNoRCxTQUFTQyxNQUFNLFFBQVEsMkJBQTJCO0FBRWxELE9BQU8sS0FBS0Msc0JBQXNCLEdBQUcsV0FBVyxHQUFHLE1BQU0sR0FBRyxRQUFRO0FBRXBFLEtBQUtDLEtBQUssR0FBRztFQUNYQyxjQUFjLEVBQUUsTUFBTTtFQUN0QkMsUUFBUSxFQUFFLENBQUNDLE1BQU0sRUFBRUosc0JBQXNCLEVBQUUsR0FBRyxJQUFJO0FBQ3BELENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFLLHVCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWdDO0lBQUFOLGNBQUE7SUFBQUM7RUFBQSxJQUFBRyxFQUcvQjtFQUFBLElBQUFHLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFKLFFBQUE7SUFDTk0sRUFBQSxZQUFBQyxhQUFBQyxLQUFBO01BQ0VSLFFBQVEsQ0FBQ1EsS0FBSyxDQUFDO0lBQUEsQ0FDaEI7SUFBQUosQ0FBQSxNQUFBSixRQUFBO0lBQUFJLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBRkQsTUFBQUcsWUFBQSxHQUFBRCxFQUVDO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFMLENBQUEsUUFBQUosUUFBQTtJQUVEUyxFQUFBLFlBQUFDLGFBQUE7TUFDRVYsUUFBUSxDQUFDLFFBQVEsQ0FBQztJQUFBLENBQ25CO0lBQUFJLENBQUEsTUFBQUosUUFBQTtJQUFBSSxDQUFBLE1BQUFLLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFMLENBQUE7RUFB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 \ No newline at end of file diff --git a/components/ClaudeCodeHint/PluginHintMenu.tsx b/components/ClaudeCodeHint/PluginHintMenu.tsx new file mode 100644 index 0000000..8ebd16e --- /dev/null +++ b/components/ClaudeCodeHint/PluginHintMenu.tsx @@ -0,0 +1,78 @@ +import * as React from 'react'; +import { Box, Text } from '../../ink.js'; +import { Select } from '../CustomSelect/select.js'; +import { PermissionDialog } from '../permissions/PermissionDialog.js'; +type Props = { + pluginName: string; + pluginDescription?: string; + marketplaceName: string; + sourceCommand: string; + onResponse: (response: 'yes' | 'no' | 'disable') => void; +}; +const AUTO_DISMISS_MS = 30_000; +export function PluginHintMenu({ + pluginName, + pluginDescription, + marketplaceName, + sourceCommand, + onResponse +}: Props): React.ReactNode { + const onResponseRef = React.useRef(onResponse); + onResponseRef.current = onResponse; + React.useEffect(() => { + const timeoutId = setTimeout(ref => ref.current('no'), AUTO_DISMISS_MS, onResponseRef); + return () => clearTimeout(timeoutId); + }, []); + function onSelect(value: string): void { + switch (value) { + case 'yes': + onResponse('yes'); + break; + case 'disable': + onResponse('disable'); + break; + default: + onResponse('no'); + } + } + const options = [{ + label: + Yes, install {pluginName} + , + value: 'yes' + }, { + label: 'No', + value: 'no' + }, { + label: "No, and don't show plugin installation hints again", + value: 'disable' + }]; + return + + + + The {sourceCommand} command suggests installing a + plugin. + + + + Plugin: + {pluginName} + + + Marketplace: + {marketplaceName} + + {pluginDescription && + {pluginDescription} + } + + Would you like to install it? + + + handleSelection(value_0 as 'yes' | 'no')} />; + $[10] = handleSelection; + $[11] = t10; + } else { + t10 = $[11]; + } + let t11; + if ($[12] !== handleEscape || $[13] !== t10 || $[14] !== t4 || $[15] !== t5 || $[16] !== t7) { + t11 = {t6}{t7}{t8}{t10}; + $[12] = handleEscape; + $[13] = t10; + $[14] = t4; + $[15] = t5; + $[16] = t7; + $[17] = t11; + } else { + t11 = $[17]; + } + return t11; +} +function _temp4(include, i) { + return {" "}{include.path}; +} +function _temp3(current_0) { + return { + ...current_0, + hasClaudeMdExternalIncludesApproved: true, + hasClaudeMdExternalIncludesWarningShown: true + }; +} +function _temp2(current) { + return { + ...current, + hasClaudeMdExternalIncludesApproved: false, + hasClaudeMdExternalIncludesWarningShown: true + }; +} +function _temp() { + logEvent("tengu_claude_md_includes_dialog_shown", {}); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwibG9nRXZlbnQiLCJCb3giLCJMaW5rIiwiVGV4dCIsIkV4dGVybmFsQ2xhdWRlTWRJbmNsdWRlIiwic2F2ZUN1cnJlbnRQcm9qZWN0Q29uZmlnIiwiU2VsZWN0IiwiRGlhbG9nIiwiUHJvcHMiLCJvbkRvbmUiLCJpc1N0YW5kYWxvbmVEaWFsb2ciLCJleHRlcm5hbEluY2x1ZGVzIiwiQ2xhdWRlTWRFeHRlcm5hbEluY2x1ZGVzRGlhbG9nIiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsInVzZUVmZmVjdCIsIl90ZW1wIiwidDIiLCJ2YWx1ZSIsIl90ZW1wMiIsIl90ZW1wMyIsImhhbmRsZVNlbGVjdGlvbiIsInQzIiwiaGFuZGxlRXNjYXBlIiwidDQiLCJ0NSIsInQ2IiwidDciLCJsZW5ndGgiLCJtYXAiLCJfdGVtcDQiLCJ0OCIsInQ5IiwibGFiZWwiLCJ0MTAiLCJ2YWx1ZV8wIiwidDExIiwiaW5jbHVkZSIsImkiLCJwYXRoIiwiY3VycmVudF8wIiwiY3VycmVudCIsImhhc0NsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc0FwcHJvdmVkIiwiaGFzQ2xhdWRlTWRFeHRlcm5hbEluY2x1ZGVzV2FybmluZ1Nob3duIl0sInNvdXJjZXMiOlsiQ2xhdWRlTWRFeHRlcm5hbEluY2x1ZGVzRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlQ2FsbGJhY2sgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IEJveCwgTGluaywgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgRXh0ZXJuYWxDbGF1ZGVNZEluY2x1ZGUgfSBmcm9tICcuLi91dGlscy9jbGF1ZGVtZC5qcydcbmltcG9ydCB7IHNhdmVDdXJyZW50UHJvamVjdENvbmZpZyB9IGZyb20gJy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L2luZGV4LmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgb25Eb25lKCk6IHZvaWRcbiAgaXNTdGFuZGFsb25lRGlhbG9nPzogYm9vbGVhblxuICBleHRlcm5hbEluY2x1ZGVzPzogRXh0ZXJuYWxDbGF1ZGVNZEluY2x1ZGVbXVxufVxuXG5leHBvcnQgZnVuY3Rpb24gQ2xhdWRlTWRFeHRlcm5hbEluY2x1ZGVzRGlhbG9nKHtcbiAgb25Eb25lLFxuICBpc1N0YW5kYWxvbmVEaWFsb2csXG4gIGV4dGVybmFsSW5jbHVkZXMsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIFJlYWN0LnVzZUVmZmVjdCgoKSA9PiB7XG4gICAgLy8gTG9nIHdoZW4gZGlhbG9nIGlzIHNob3duXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X2NsYXVkZV9tZF9pbmNsdWRlc19kaWFsb2dfc2hvd24nLCB7fSlcbiAgfSwgW10pXG5cbiAgY29uc3QgaGFuZGxlU2VsZWN0aW9uID0gdXNlQ2FsbGJhY2soXG4gICAgKHZhbHVlOiAneWVzJyB8ICdubycpID0+IHtcbiAgICAgIGlmICh2YWx1ZSA9PT0gJ25vJykge1xuICAgICAgICBsb2dFdmVudCgndGVuZ3VfY2xhdWRlX21kX2V4dGVybmFsX2luY2x1ZGVzX2RpYWxvZ19kZWNsaW5lZCcsIHt9KVxuICAgICAgICAvLyBNYXJrIHRoYXQgd2UndmUgc2hvd24gdGhlIGRpYWxvZyBidXQgaXQgd2FzIGRlY2xpbmVkXG4gICAgICAgIHNhdmVDdXJyZW50UHJvamVjdENvbmZpZyhjdXJyZW50ID0+ICh7XG4gICAgICAgICAgLi4uY3VycmVudCxcbiAgICAgICAgICBoYXNDbGF1ZGVNZEV4dGVybmFsSW5jbHVkZXNBcHByb3ZlZDogZmFsc2UsXG4gICAgICAgICAgaGFzQ2xhdWRlTWRFeHRlcm5hbEluY2x1ZGVzV2FybmluZ1Nob3duOiB0cnVlLFxuICAgICAgICB9KSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9jbGF1ZGVfbWRfZXh0ZXJuYWxfaW5jbHVkZXNfZGlhbG9nX2FjY2VwdGVkJywge30pXG4gICAgICAgIHNhdmVDdXJyZW50UHJvamVjdENvbmZpZyhjdXJyZW50ID0+ICh7XG4gICAgICAgICAgLi4uY3VycmVudCxcbiAgICAgICAgICBoYXNDbGF1ZGVNZEV4dGVybmFsSW5jbHVkZXNBcHByb3ZlZDogdHJ1ZSxcbiAgICAgICAgICBoYXNDbGF1ZGVNZEV4dGVybmFsSW5jbHVkZXNXYXJuaW5nU2hvd246IHRydWUsXG4gICAgICAgIH0pKVxuICAgICAgfVxuXG4gICAgICBvbkRvbmUoKVxuICAgIH0sXG4gICAgW29uRG9uZV0sXG4gIClcblxuICBjb25zdCBoYW5kbGVFc2NhcGUgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgaGFuZGxlU2VsZWN0aW9uKCdubycpXG4gIH0sIFtoYW5kbGVTZWxlY3Rpb25dKVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJBbGxvdyBleHRlcm5hbCBDTEFVREUubWQgZmlsZSBpbXBvcnRzP1wiXG4gICAgICBjb2xvcj1cIndhcm5pbmdcIlxuICAgICAgb25DYW5jZWw9e2hhbmRsZUVzY2FwZX1cbiAgICAgIGhpZGVCb3JkZXI9eyFpc1N0YW5kYWxvbmVEaWFsb2d9XG4gICAgICBoaWRlSW5wdXRHdWlkZT17IWlzU3RhbmRhbG9uZURpYWxvZ31cbiAgICA+XG4gICAgICA8VGV4dD5cbiAgICAgICAgVGhpcyBwcm9qZWN0JmFwb3M7cyBDTEFVREUubWQgaW1wb3J0cyBmaWxlcyBvdXRzaWRlIHRoZSBjdXJyZW50IHdvcmtpbmdcbiAgICAgICAgZGlyZWN0b3J5LiBOZXZlciBhbGxvdyB0aGlzIGZvciB0aGlyZC1wYXJ0eSByZXBvc2l0b3JpZXMuXG4gICAgICA8L1RleHQ+XG5cbiAgICAgIHtleHRlcm5hbEluY2x1ZGVzICYmIGV4dGVybmFsSW5jbHVkZXMubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPkV4dGVybmFsIGltcG9ydHM6PC9UZXh0PlxuICAgICAgICAgIHtleHRlcm5hbEluY2x1ZGVzLm1hcCgoaW5jbHVkZSwgaSkgPT4gKFxuICAgICAgICAgICAgPFRleHQga2V5PXtpfSBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgeycgICd9XG4gICAgICAgICAgICAgIHtpbmNsdWRlLnBhdGh9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgKSl9XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cblxuICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgIEltcG9ydGFudDogT25seSB1c2UgQ2xhdWRlIENvZGUgd2l0aCBmaWxlcyB5b3UgdHJ1c3QuIEFjY2Vzc2luZ1xuICAgICAgICB1bnRydXN0ZWQgZmlsZXMgbWF5IHBvc2Ugc2VjdXJpdHkgcmlza3N7JyAnfVxuICAgICAgICA8TGluayB1cmw9XCJodHRwczovL2NvZGUuY2xhdWRlLmNvbS9kb2NzL2VuL3NlY3VyaXR5XCIgLz57JyAnfVxuICAgICAgPC9UZXh0PlxuXG4gICAgICA8U2VsZWN0XG4gICAgICAgIG9wdGlvbnM9e1tcbiAgICAgICAgICB7IGxhYmVsOiAnWWVzLCBhbGxvdyBleHRlcm5hbCBpbXBvcnRzJywgdmFsdWU6ICd5ZXMnIH0sXG4gICAgICAgICAgeyBsYWJlbDogJ05vLCBkaXNhYmxlIGV4dGVybmFsIGltcG9ydHMnLCB2YWx1ZTogJ25vJyB9LFxuICAgICAgICBdfVxuICAgICAgICBvbkNoYW5nZT17dmFsdWUgPT4gaGFuZGxlU2VsZWN0aW9uKHZhbHVlIGFzICd5ZXMnIHwgJ25vJyl9XG4gICAgICAvPlxuICAgIDwvRGlhbG9nPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFL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 \ No newline at end of file diff --git a/components/ClickableImageRef.tsx b/components/ClickableImageRef.tsx new file mode 100644 index 0000000..ff48a72 --- /dev/null +++ b/components/ClickableImageRef.tsx @@ -0,0 +1,73 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { pathToFileURL } from 'url'; +import Link from '../ink/components/Link.js'; +import { supportsHyperlinks } from '../ink/supports-hyperlinks.js'; +import { Text } from '../ink.js'; +import { getStoredImagePath } from '../utils/imageStore.js'; +import type { Theme } from '../utils/theme.js'; +type Props = { + imageId: number; + backgroundColor?: keyof Theme; + isSelected?: boolean; +}; + +/** + * Renders an image reference like [Image #1] as a clickable link. + * When clicked, opens the stored image file in the default viewer. + * + * Falls back to styled text if: + * - Terminal doesn't support hyperlinks + * - Image file is not found in the store + */ +export function ClickableImageRef(t0) { + const $ = _c(13); + const { + imageId, + backgroundColor, + isSelected: t1 + } = t0; + const isSelected = t1 === undefined ? false : t1; + const imagePath = getStoredImagePath(imageId); + const displayText = `[Image #${imageId}]`; + if (imagePath && supportsHyperlinks()) { + const fileUrl = pathToFileURL(imagePath).href; + let t2; + let t3; + if ($[0] !== backgroundColor || $[1] !== displayText || $[2] !== isSelected) { + t2 = {displayText}; + t3 = {displayText}; + $[0] = backgroundColor; + $[1] = displayText; + $[2] = isSelected; + $[3] = t2; + $[4] = t3; + } else { + t2 = $[3]; + t3 = $[4]; + } + let t4; + if ($[5] !== fileUrl || $[6] !== t2 || $[7] !== t3) { + t4 = {t3}; + $[5] = fileUrl; + $[6] = t2; + $[7] = t3; + $[8] = t4; + } else { + t4 = $[8]; + } + return t4; + } + let t2; + if ($[9] !== backgroundColor || $[10] !== displayText || $[11] !== isSelected) { + t2 = {displayText}; + $[9] = backgroundColor; + $[10] = displayText; + $[11] = isSelected; + $[12] = t2; + } else { + t2 = $[12]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInBhdGhUb0ZpbGVVUkwiLCJMaW5rIiwic3VwcG9ydHNIeXBlcmxpbmtzIiwiVGV4dCIsImdldFN0b3JlZEltYWdlUGF0aCIsIlRoZW1lIiwiUHJvcHMiLCJpbWFnZUlkIiwiYmFja2dyb3VuZENvbG9yIiwiaXNTZWxlY3RlZCIsIkNsaWNrYWJsZUltYWdlUmVmIiwidDAiLCIkIiwiX2MiLCJ0MSIsInVuZGVmaW5lZCIsImltYWdlUGF0aCIsImRpc3BsYXlUZXh0IiwiZmlsZVVybCIsImhyZWYiLCJ0MiIsInQzIiwidDQiXSwic291cmNlcyI6WyJDbGlja2FibGVJbWFnZVJlZi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBwYXRoVG9GaWxlVVJMIH0gZnJvbSAndXJsJ1xuaW1wb3J0IExpbmsgZnJvbSAnLi4vaW5rL2NvbXBvbmVudHMvTGluay5qcydcbmltcG9ydCB7IHN1cHBvcnRzSHlwZXJsaW5rcyB9IGZyb20gJy4uL2luay9zdXBwb3J0cy1oeXBlcmxpbmtzLmpzJ1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGdldFN0b3JlZEltYWdlUGF0aCB9IGZyb20gJy4uL3V0aWxzL2ltYWdlU3RvcmUuanMnXG5pbXBvcnQgdHlwZSB7IFRoZW1lIH0gZnJvbSAnLi4vdXRpbHMvdGhlbWUuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGltYWdlSWQ6IG51bWJlclxuICBiYWNrZ3JvdW5kQ29sb3I/OiBrZXlvZiBUaGVtZVxuICBpc1NlbGVjdGVkPzogYm9vbGVhblxufVxuXG4vKipcbiAqIFJlbmRlcnMgYW4gaW1hZ2UgcmVmZXJlbmNlIGxpa2UgW0ltYWdlICMxXSBhcyBhIGNsaWNrYWJsZSBsaW5rLlxuICogV2hlbiBjbGlja2VkLCBvcGVucyB0aGUgc3RvcmVkIGltYWdlIGZpbGUgaW4gdGhlIGRlZmF1bHQgdmlld2VyLlxuICpcbiAqIEZhbGxzIGJhY2sgdG8gc3R5bGVkIHRleHQgaWY6XG4gKiAtIFRlcm1pbmFsIGRvZXNuJ3Qgc3VwcG9ydCBoeXBlcmxpbmtzXG4gKiAtIEltYWdlIGZpbGUgaXMgbm90IGZvdW5kIGluIHRoZSBzdG9yZVxuICovXG5leHBvcnQgZnVuY3Rpb24gQ2xpY2thYmxlSW1hZ2VSZWYoe1xuICBpbWFnZUlkLFxuICBiYWNrZ3JvdW5kQ29sb3IsXG4gIGlzU2VsZWN0ZWQgPSBmYWxzZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgaW1hZ2VQYXRoID0gZ2V0U3RvcmVkSW1hZ2VQYXRoKGltYWdlSWQpXG4gIGNvbnN0IGRpc3BsYXlUZXh0ID0gYFtJbWFnZSAjJHtpbWFnZUlkfV1gXG5cbiAgLy8gSWYgd2UgaGF2ZSBhIHN0b3JlZCBpbWFnZSBhbmQgdGVybWluYWwgc3VwcG9ydHMgaHlwZXJsaW5rcywgbWFrZSBpdCBjbGlja2FibGVcbiAgaWYgKGltYWdlUGF0aCAmJiBzdXBwb3J0c0h5cGVybGlua3MoKSkge1xuICAgIGNvbnN0IGZpbGVVcmwgPSBwYXRoVG9GaWxlVVJMKGltYWdlUGF0aCkuaHJlZlxuXG4gICAgcmV0dXJuIChcbiAgICAgIDxMaW5rXG4gICAgICAgIHVybD17ZmlsZVVybH1cbiAgICAgICAgZmFsbGJhY2s9e1xuICAgICAgICAgIDxUZXh0IGJhY2tncm91bmRDb2xvcj17YmFja2dyb3VuZENvbG9yfSBpbnZlcnNlPXtpc1NlbGVjdGVkfT5cbiAgICAgICAgICAgIHtkaXNwbGF5VGV4dH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIH1cbiAgICAgID5cbiAgICAgICAgPFRleHRcbiAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3I9e2JhY2tncm91bmRDb2xvcn1cbiAgICAgICAgICBpbnZlcnNlPXtpc1NlbGVjdGVkfVxuICAgICAgICAgIGJvbGQ9e2lzU2VsZWN0ZWR9XG4gICAgICAgID5cbiAgICAgICAgICB7ZGlzcGxheVRleHR9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvTGluaz5cbiAgICApXG4gIH1cblxuICAvLyBGYWxsYmFjazogc3R5bGVkIGJ1dCBub3QgY2xpY2thYmxlXG4gIHJldHVybiAoXG4gICAgPFRleHQgYmFja2dyb3VuZENvbG9yPXtiYWNrZ3JvdW5kQ29sb3J9IGludmVyc2U9e2lzU2VsZWN0ZWR9PlxuICAgICAge2Rpc3BsYXlUZXh0fVxuICAgIDwvVGV4dD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxhQUFhLFFBQVEsS0FBSztBQUNuQyxPQUFPQyxJQUFJLE1BQU0sMkJBQTJCO0FBQzVDLFNBQVNDLGtCQUFrQixRQUFRLCtCQUErQjtBQUNsRSxTQUFTQyxJQUFJLFFBQVEsV0FBVztBQUNoQyxTQUFTQyxrQkFBa0IsUUFBUSx3QkFBd0I7QUFDM0QsY0FBY0MsS0FBSyxRQUFRLG1CQUFtQjtBQUU5QyxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsT0FBTyxFQUFFLE1BQU07RUFDZkMsZUFBZSxDQUFDLEVBQUUsTUFBTUgsS0FBSztFQUM3QkksVUFBVSxDQUFDLEVBQUUsT0FBTztBQUN0QixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFDLGtCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQTJCO0lBQUFOLE9BQUE7SUFBQUMsZUFBQTtJQUFBQyxVQUFBLEVBQUFLO0VBQUEsSUFBQUgsRUFJMUI7RUFETixNQUFBRixVQUFBLEdBQUFLLEVBQWtCLEtBQWxCQyxTQUFrQixHQUFsQixLQUFrQixHQUFsQkQsRUFBa0I7RUFFbEIsTUFBQUUsU0FBQSxHQUFrQlosa0JBQWtCLENBQUNHLE9BQU8sQ0FBQztFQUM3QyxNQUFBVSxXQUFBLEdBQW9CLFdBQVdWLE9BQU8sR0FBRztFQUd6QyxJQUFJUyxTQUFpQyxJQUFwQmQsa0JBQWtCLENBQUMsQ0FBQztJQUNuQyxNQUFBZ0IsT0FBQSxHQUFnQmxCLGFBQWEsQ0FBQ2dCLFNBQVMsQ0FBQyxDQUFBRyxJQUFLO0lBQUEsSUFBQUMsRUFBQTtJQUFBLElBQUFDLEVBQUE7SUFBQSxJQUFBVCxDQUFBLFFBQUFKLGVBQUEsSUFBQUksQ0FBQSxRQUFBSyxXQUFBLElBQUFMLENBQUEsUUFBQUgsVUFBQTtNQU12Q1csRUFBQSxJQUFDLElBQUksQ0FBa0JaLGVBQWUsQ0FBZkEsZ0JBQWMsQ0FBQyxDQUFXQyxPQUFVLENBQVZBLFdBQVMsQ0FBQyxDQUN4RFEsWUFBVSxDQUNiLEVBRkMsSUFBSSxDQUVFO01BR1RJLEVBQUEsSUFBQyxJQUFJLENBQ2NiLGVBQWUsQ0FBZkEsZ0JBQWMsQ0FBQyxDQUN2QkMsT0FBVSxDQUFWQSxXQUFTLENBQUMsQ0FDYkEsSUFBVSxDQUFWQSxXQUFTLENBQUMsQ0FFZlEsWUFBVSxDQUNiLEVBTkMsSUFBSSxDQU1FO01BQUFMLENBQUEsTUFBQUosZUFBQTtNQUFBSSxDQUFBLE1BQUFLLFdBQUE7TUFBQUwsQ0FBQSxNQUFBSCxVQUFBO01BQUFHLENBQUEsTUFBQVEsRUFBQTtNQUFBUixDQUFBLE1BQUFTLEVBQUE7SUFBQTtNQUFBRCxFQUFBLEdBQUFSLENBQUE7TUFBQVMsRUFBQSxHQUFBVCxDQUFBO0lBQUE7SUFBQSxJQUFBVSxFQUFBO0lBQUEsSUFBQVYsQ0FBQSxRQUFBTSxPQUFBLElBQUFOLENBQUEsUUFBQVEsRUFBQSxJQUFBUixDQUFBLFFBQUFTLEVBQUE7TUFkVEMsRUFBQSxJQUFDLElBQUksQ0FDRUosR0FBTyxDQUFQQSxRQUFNLENBQUMsQ0FFVixRQUVPLENBRlAsQ0FBQUUsRUFFTSxDQUFDLENBR1QsQ0FBQUMsRUFNTSxDQUNSLEVBZkMsSUFBSSxDQWVFO01BQUFULENBQUEsTUFBQU0sT0FBQTtNQUFBTixDQUFBLE1BQUFRLEVBQUE7TUFBQVIsQ0FBQSxNQUFBUyxFQUFBO01BQUFULENBQUEsTUFBQVUsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQVYsQ0FBQTtJQUFBO0lBQUEsT0FmUFUsRUFlTztFQUFBO0VBRVYsSUFBQUYsRUFBQTtFQUFBLElBQUFSLENBQUEsUUFBQUosZUFBQSxJQUFBSSxDQUFBLFNBQUFLLFdBQUEsSUFBQUwsQ0FBQSxTQUFBSCxVQUFBO0lBSUNXLEVBQUEsSUFBQyxJQUFJLENBQWtCWixlQUFlLENBQWZBLGdCQUFjLENBQUMsQ0FBV0MsT0FBVSxDQUFWQSxXQUFTLENBQUMsQ0FDeERRLFlBQVUsQ0FDYixFQUZDLElBQUksQ0FFRTtJQUFBTCxDQUFBLE1BQUFKLGVBQUE7SUFBQUksQ0FBQSxPQUFBSyxXQUFBO0lBQUFMLENBQUEsT0FBQUgsVUFBQTtJQUFBRyxDQUFBLE9BQUFRLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFSLENBQUE7RUFBQTtFQUFBLE9BRlBRLEVBRU87QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/CompactSummary.tsx b/components/CompactSummary.tsx new file mode 100644 index 0000000..72e1b18 --- /dev/null +++ b/components/CompactSummary.tsx @@ -0,0 +1,118 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { BLACK_CIRCLE } from '../constants/figures.js'; +import { Box, Text } from '../ink.js'; +import type { Screen } from '../screens/REPL.js'; +import type { NormalizedUserMessage } from '../types/message.js'; +import { getUserMessageText } from '../utils/messages.js'; +import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js'; +import { MessageResponse } from './MessageResponse.js'; +type Props = { + message: NormalizedUserMessage; + screen: Screen; +}; +export function CompactSummary(t0) { + const $ = _c(24); + const { + message, + screen + } = t0; + const isTranscriptMode = screen === "transcript"; + let t1; + if ($[0] !== message) { + t1 = getUserMessageText(message) || ""; + $[0] = message; + $[1] = t1; + } else { + t1 = $[1]; + } + const textContent = t1; + const metadata = message.summarizeMetadata; + if (metadata) { + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = {BLACK_CIRCLE}; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = Summarized conversation; + $[3] = t3; + } else { + t3 = $[3]; + } + let t4; + if ($[4] !== isTranscriptMode || $[5] !== metadata) { + t4 = !isTranscriptMode && Summarized {metadata.messagesSummarized} messages{" "}{metadata.direction === "up_to" ? "up to this point" : "from this point"}{metadata.userContext && Context: {"\u201C"}{metadata.userContext}{"\u201D"}}; + $[4] = isTranscriptMode; + $[5] = metadata; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] !== isTranscriptMode || $[8] !== textContent) { + t5 = isTranscriptMode && {textContent}; + $[7] = isTranscriptMode; + $[8] = textContent; + $[9] = t5; + } else { + t5 = $[9]; + } + let t6; + if ($[10] !== t4 || $[11] !== t5) { + t6 = {t2}{t3}{t4}{t5}; + $[10] = t4; + $[11] = t5; + $[12] = t6; + } else { + t6 = $[12]; + } + return t6; + } + let t2; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t2 = {BLACK_CIRCLE}; + $[13] = t2; + } else { + t2 = $[13]; + } + let t3; + if ($[14] !== isTranscriptMode) { + t3 = !isTranscriptMode && {" "}; + $[14] = isTranscriptMode; + $[15] = t3; + } else { + t3 = $[15]; + } + let t4; + if ($[16] !== t3) { + t4 = {t2}Compact summary{t3}; + $[16] = t3; + $[17] = t4; + } else { + t4 = $[17]; + } + let t5; + if ($[18] !== isTranscriptMode || $[19] !== textContent) { + t5 = isTranscriptMode && {textContent}; + $[18] = isTranscriptMode; + $[19] = textContent; + $[20] = t5; + } else { + t5 = $[20]; + } + let t6; + if ($[21] !== t4 || $[22] !== t5) { + t6 = {t4}{t5}; + $[21] = t4; + $[22] = t5; + $[23] = t6; + } else { + t6 = $[23]; + } + return t6; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJMQUNLX0NJUkNMRSIsIkJveCIsIlRleHQiLCJTY3JlZW4iLCJOb3JtYWxpemVkVXNlck1lc3NhZ2UiLCJnZXRVc2VyTWVzc2FnZVRleHQiLCJDb25maWd1cmFibGVTaG9ydGN1dEhpbnQiLCJNZXNzYWdlUmVzcG9uc2UiLCJQcm9wcyIsIm1lc3NhZ2UiLCJzY3JlZW4iLCJDb21wYWN0U3VtbWFyeSIsInQwIiwiJCIsIl9jIiwiaXNUcmFuc2NyaXB0TW9kZSIsInQxIiwidGV4dENvbnRlbnQiLCJtZXRhZGF0YSIsInN1bW1hcml6ZU1ldGFkYXRhIiwidDIiLCJTeW1ib2wiLCJmb3IiLCJ0MyIsInQ0IiwibWVzc2FnZXNTdW1tYXJpemVkIiwiZGlyZWN0aW9uIiwidXNlckNvbnRleHQiLCJ0NSIsInQ2Il0sInNvdXJjZXMiOlsiQ29tcGFjdFN1bW1hcnkudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQkxBQ0tfQ0lSQ0xFIH0gZnJvbSAnLi4vY29uc3RhbnRzL2ZpZ3VyZXMuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IFNjcmVlbiB9IGZyb20gJy4uL3NjcmVlbnMvUkVQTC5qcydcbmltcG9ydCB0eXBlIHsgTm9ybWFsaXplZFVzZXJNZXNzYWdlIH0gZnJvbSAnLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IGdldFVzZXJNZXNzYWdlVGV4dCB9IGZyb20gJy4uL3V0aWxzL21lc3NhZ2VzLmpzJ1xuaW1wb3J0IHsgQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBNZXNzYWdlUmVzcG9uc2UgfSBmcm9tICcuL01lc3NhZ2VSZXNwb25zZS5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgbWVzc2FnZTogTm9ybWFsaXplZFVzZXJNZXNzYWdlXG4gIHNjcmVlbjogU2NyZWVuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBDb21wYWN0U3VtbWFyeSh7IG1lc3NhZ2UsIHNjcmVlbiB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGlzVHJhbnNjcmlwdE1vZGUgPSBzY3JlZW4gPT09ICd0cmFuc2NyaXB0J1xuICBjb25zdCB0ZXh0Q29udGVudCA9IGdldFVzZXJNZXNzYWdlVGV4dChtZXNzYWdlKSB8fCAnJ1xuICBjb25zdCBtZXRhZGF0YSA9IG1lc3NhZ2Uuc3VtbWFyaXplTWV0YWRhdGFcblxuICAvLyBcIlN1bW1hcml6ZSBmcm9tIGhlcmVcIiB3aXRoIG1ldGFkYXRhXG4gIGlmIChtZXRhZGF0YSkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIj5cbiAgICAgICAgICA8Qm94IG1pbldpZHRoPXsyfT5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwidGV4dFwiPntCTEFDS19DSVJDTEV9PC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPFRleHQgYm9sZD5TdW1tYXJpemVkIGNvbnZlcnNhdGlvbjwvVGV4dD5cbiAgICAgICAgICAgIHshaXNUcmFuc2NyaXB0TW9kZSAmJiAoXG4gICAgICAgICAgICAgIDxNZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICAgICAgU3VtbWFyaXplZCB7bWV0YWRhdGEubWVzc2FnZXNTdW1tYXJpemVkfSBtZXNzYWdlc3snICd9XG4gICAgICAgICAgICAgICAgICAgIHttZXRhZGF0YS5kaXJlY3Rpb24gPT09ICd1cF90bydcbiAgICAgICAgICAgICAgICAgICAgICA/ICd1cCB0byB0aGlzIHBvaW50J1xuICAgICAgICAgICAgICAgICAgICAgIDogJ2Zyb20gdGhpcyBwb2ludCd9XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICB7bWV0YWRhdGEudXNlckNvbnRleHQgJiYgKFxuICAgICAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICAgICAgICBDb250ZXh0OiB7J1xcdTIwMWMnfVxuICAgICAgICAgICAgICAgICAgICAgIHttZXRhZGF0YS51c2VyQ29udGV4dH1cbiAgICAgICAgICAgICAgICAgICAgICB7J1xcdTIwMWQnfVxuICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgICAgICAgICBhY3Rpb249XCJhcHA6dG9nZ2xlVHJhbnNjcmlwdFwiXG4gICAgICAgICAgICAgICAgICAgICAgY29udGV4dD1cIkdsb2JhbFwiXG4gICAgICAgICAgICAgICAgICAgICAgZmFsbGJhY2s9XCJjdHJsK29cIlxuICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwiZXhwYW5kIGhpc3RvcnlcIlxuICAgICAgICAgICAgICAgICAgICAgIHBhcmVuc1xuICAgICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgICB7aXNUcmFuc2NyaXB0TW9kZSAmJiAoXG4gICAgICAgICAgICAgIDxNZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgICAgICAgICAgPFRleHQ+e3RleHRDb250ZW50fTwvVGV4dD5cbiAgICAgICAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIC8vIERlZmF1bHQgY29tcGFjdCBzdW1tYXJ5IChhdXRvLWNvbXBhY3QpXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgICA8Qm94IG1pbldpZHRoPXsyfT5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cInRleHRcIj57QkxBQ0tfQ0lSQ0xFfTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0IGJvbGQ+XG4gICAgICAgICAgICBDb21wYWN0IHN1bW1hcnlcbiAgICAgICAgICAgIHshaXNUcmFuc2NyaXB0TW9kZSAmJiAoXG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgIHsnICd9XG4gICAgICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICAgICAgYWN0aW9uPVwiYXBwOnRvZ2dsZVRyYW5zY3JpcHRcIlxuICAgICAgICAgICAgICAgICAgY29udGV4dD1cIkdsb2JhbFwiXG4gICAgICAgICAgICAgICAgICBmYWxsYmFjaz1cImN0cmwrb1wiXG4gICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbj1cImV4cGFuZFwiXG4gICAgICAgICAgICAgICAgICBwYXJlbnNcbiAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cbiAgICAgIHtpc1RyYW5zY3JpcHRNb2RlICYmIChcbiAgICAgICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgICA8VGV4dD57dGV4dENvbnRlbnR9PC9UZXh0PlxuICAgICAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsWUFBWSxRQUFRLHlCQUF5QjtBQUN0RCxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLGNBQWNDLE1BQU0sUUFBUSxvQkFBb0I7QUFDaEQsY0FBY0MscUJBQXFCLFFBQVEscUJBQXFCO0FBQ2hFLFNBQVNDLGtCQUFrQixRQUFRLHNCQUFzQjtBQUN6RCxTQUFTQyx3QkFBd0IsUUFBUSwrQkFBK0I7QUFDeEUsU0FBU0MsZUFBZSxRQUFRLHNCQUFzQjtBQUV0RCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsT0FBTyxFQUFFTCxxQkFBcUI7RUFDOUJNLE1BQU0sRUFBRVAsTUFBTTtBQUNoQixDQUFDO0FBRUQsT0FBTyxTQUFBUSxlQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXdCO0lBQUFMLE9BQUE7SUFBQUM7RUFBQSxJQUFBRSxFQUEwQjtFQUN2RCxNQUFBRyxnQkFBQSxHQUF5QkwsTUFBTSxLQUFLLFlBQVk7RUFBQSxJQUFBTSxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBSixPQUFBO0lBQzVCTyxFQUFBLEdBQUFYLGtCQUFrQixDQUFDSSxPQUFhLENBQUMsSUFBakMsRUFBaUM7SUFBQUksQ0FBQSxNQUFBSixPQUFBO0lBQUFJLENBQUEsTUFBQUcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBQXJELE1BQUFJLFdBQUEsR0FBb0JELEVBQWlDO0VBQ3JELE1BQUFFLFFBQUEsR0FBaUJULE9BQU8sQ0FBQVUsaUJBQWtCO0VBRzFDLElB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 \ No newline at end of file diff --git a/components/ConfigurableShortcutHint.tsx b/components/ConfigurableShortcutHint.tsx new file mode 100644 index 0000000..e783da5 --- /dev/null +++ b/components/ConfigurableShortcutHint.tsx @@ -0,0 +1,57 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import type { KeybindingAction, KeybindingContextName } from '../keybindings/types.js'; +import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +type Props = { + /** The keybinding action (e.g., 'app:toggleTranscript') */ + action: KeybindingAction; + /** The keybinding context (e.g., 'Global') */ + context: KeybindingContextName; + /** Default shortcut if keybinding not configured */ + fallback: string; + /** The action description text (e.g., 'expand') */ + description: string; + /** Whether to wrap in parentheses */ + parens?: boolean; + /** Whether to show in bold */ + bold?: boolean; +}; + +/** + * KeyboardShortcutHint that displays the user-configured shortcut. + * Falls back to default if keybinding context is not available. + * + * @example + * + */ +export function ConfigurableShortcutHint(t0) { + const $ = _c(5); + const { + action, + context, + fallback, + description, + parens, + bold + } = t0; + const shortcut = useShortcutDisplay(action, context, fallback); + let t1; + if ($[0] !== bold || $[1] !== description || $[2] !== parens || $[3] !== shortcut) { + t1 = ; + $[0] = bold; + $[1] = description; + $[2] = parens; + $[3] = shortcut; + $[4] = t1; + } else { + t1 = $[4]; + } + return t1; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIktleWJpbmRpbmdBY3Rpb24iLCJLZXliaW5kaW5nQ29udGV4dE5hbWUiLCJ1c2VTaG9ydGN1dERpc3BsYXkiLCJLZXlib2FyZFNob3J0Y3V0SGludCIsIlByb3BzIiwiYWN0aW9uIiwiY29udGV4dCIsImZhbGxiYWNrIiwiZGVzY3JpcHRpb24iLCJwYXJlbnMiLCJib2xkIiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwidDAiLCIkIiwiX2MiLCJzaG9ydGN1dCIsInQxIl0sInNvdXJjZXMiOlsiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHtcbiAgS2V5YmluZGluZ0FjdGlvbixcbiAgS2V5YmluZGluZ0NvbnRleHROYW1lLFxufSBmcm9tICcuLi9rZXliaW5kaW5ncy90eXBlcy5qcydcbmltcG9ydCB7IHVzZVNob3J0Y3V0RGlzcGxheSB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZVNob3J0Y3V0RGlzcGxheS5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICAvKiogVGhlIGtleWJpbmRpbmcgYWN0aW9uIChlLmcuLCAnYXBwOnRvZ2dsZVRyYW5zY3JpcHQnKSAqL1xuICBhY3Rpb246IEtleWJpbmRpbmdBY3Rpb25cbiAgLyoqIFRoZSBrZXliaW5kaW5nIGNvbnRleHQgKGUuZy4sICdHbG9iYWwnKSAqL1xuICBjb250ZXh0OiBLZXliaW5kaW5nQ29udGV4dE5hbWVcbiAgLyoqIERlZmF1bHQgc2hvcnRjdXQgaWYga2V5YmluZGluZyBub3QgY29uZmlndXJlZCAqL1xuICBmYWxsYmFjazogc3RyaW5nXG4gIC8qKiBUaGUgYWN0aW9uIGRlc2NyaXB0aW9uIHRleHQgKGUuZy4sICdleHBhbmQnKSAqL1xuICBkZXNjcmlwdGlvbjogc3RyaW5nXG4gIC8qKiBXaGV0aGVyIHRvIHdyYXAgaW4gcGFyZW50aGVzZXMgKi9cbiAgcGFyZW5zPzogYm9vbGVhblxuICAvKiogV2hldGhlciB0byBzaG93IGluIGJvbGQgKi9cbiAgYm9sZD86IGJvb2xlYW5cbn1cblxuLyoqXG4gKiBLZXlib2FyZFNob3J0Y3V0SGludCB0aGF0IGRpc3BsYXlzIHRoZSB1c2VyLWNvbmZpZ3VyZWQgc2hvcnRjdXQuXG4gKiBGYWxscyBiYWNrIHRvIGRlZmF1bHQgaWYga2V5YmluZGluZyBjb250ZXh0IGlzIG5vdCBhdmFpbGFibGUuXG4gKlxuICogQGV4YW1wbGVcbiAqIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAqICAgYWN0aW9uPVwiYXBwOnRvZ2dsZVRyYW5zY3JpcHRcIlxuICogICBjb250ZXh0PVwiR2xvYmFsXCJcbiAqICAgZmFsbGJhY2s9XCJjdHJsK29cIlxuICogICBkZXNjcmlwdGlvbj1cImV4cGFuZFwiXG4gKiAvPlxuICovXG5leHBvcnQgZnVuY3Rpb24gQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50KHtcbiAgYWN0aW9uLFxuICBjb250ZXh0LFxuICBmYWxsYmFjayxcbiAgZGVzY3JpcHRpb24sXG4gIHBhcmVucyxcbiAgYm9sZCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3Qgc2hvcnRjdXQgPSB1c2VTaG9ydGN1dERpc3BsYXkoYWN0aW9uLCBjb250ZXh0LCBmYWxsYmFjaylcbiAgcmV0dXJuIChcbiAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnRcbiAgICAgIHNob3J0Y3V0PXtzaG9ydGN1dH1cbiAgICAgIGFjdGlvbj17ZGVzY3JpcHRpb259XG4gICAgICBwYXJlbnM9e3BhcmVuc31cbiAgICAgIGJvbGQ9e2JvbGR9XG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixjQUNFQyxnQkFBZ0IsRUFDaEJDLHFCQUFxQixRQUNoQix5QkFBeUI7QUFDaEMsU0FBU0Msa0JBQWtCLFFBQVEsc0NBQXNDO0FBQ3pFLFNBQVNDLG9CQUFvQixRQUFRLHlDQUF5QztBQUU5RSxLQUFLQyxLQUFLLEdBQUc7RUFDWDtFQUNBQyxNQUFNLEVBQUVMLGdCQUFnQjtFQUN4QjtFQUNBTSxPQUFPLEVBQUVMLHFCQUFxQjtFQUM5QjtFQUNBTSxRQUFRLEVBQUUsTUFBTTtFQUNoQjtFQUNBQyxXQUFXLEVBQUUsTUFBTTtFQUNuQjtFQUNBQyxNQUFNLENBQUMsRUFBRSxPQUFPO0VBQ2hCO0VBQ0FDLElBQUksQ0FBQyxFQUFFLE9BQU87QUFDaEIsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFDLHlCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWtDO0lBQUFULE1BQUE7SUFBQUMsT0FBQTtJQUFBQyxRQUFBO0lBQUFDLFdBQUE7SUFBQUMsTUFBQTtJQUFBQztFQUFBLElBQUFFLEVBT2pDO0VBQ04sTUFBQUcsUUFBQSxHQUFpQmIsa0JBQWtCLENBQUNHLE1BQU0sRUFBRUMsT0FBTyxFQUFFQyxRQUFRLENBQUM7RUFBQSxJQUFBUyxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBSCxJQUFBLElBQUFHLENBQUEsUUFBQUwsV0FBQSxJQUFBSyxDQUFBLFFBQUFKLE1BQUEsSUFBQUksQ0FBQSxRQUFBRSxRQUFBO0lBRTVEQyxFQUFBLElBQUMsb0JBQW9CLENBQ1RELFFBQVEsQ0FBUkEsU0FBTyxDQUFDLENBQ1ZQLE1BQVcsQ0FBWEEsWUFBVSxDQUFDLENBQ1hDLE1BQU0sQ0FBTkEsT0FBSyxDQUFDLENBQ1JDLElBQUksQ0FBSkEsS0FBRyxDQUFDLEdBQ1Y7SUFBQUcsQ0FBQSxNQUFBSCxJQUFBO0lBQUFHLENBQUEsTUFBQUwsV0FBQTtJQUFBSyxDQUFBLE1BQUFKLE1BQUE7SUFBQUksQ0FBQSxNQUFBRSxRQUFBO0lBQUFGLENBQUEsTUFBQUcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBQUEsT0FMRkcsRUFLRTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/ConsoleOAuthFlow.tsx b/components/ConsoleOAuthFlow.tsx new file mode 100644 index 0000000..717697f --- /dev/null +++ b/components/ConsoleOAuthFlow.tsx @@ -0,0 +1,631 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { installOAuthTokens } from '../cli/handlers/auth.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { setClipboard } from '../ink/termio/osc.js'; +import { useTerminalNotification } from '../ink/useTerminalNotification.js'; +import { Box, Link, Text } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { getSSLErrorHint } from '../services/api/errorUtils.js'; +import { sendNotification } from '../services/notifier.js'; +import { OAuthService } from '../services/oauth/index.js'; +import { getOauthAccountInfo, validateForceLoginOrg } from '../utils/auth.js'; +import { logError } from '../utils/log.js'; +import { getSettings_DEPRECATED } from '../utils/settings/settings.js'; +import { Select } from './CustomSelect/select.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +import { Spinner } from './Spinner.js'; +import TextInput from './TextInput.js'; +type Props = { + onDone(): void; + startingMessage?: string; + mode?: 'login' | 'setup-token'; + forceLoginMethod?: 'claudeai' | 'console'; +}; +type OAuthStatus = { + state: 'idle'; +} // Initial state, waiting to select login method +| { + state: 'platform_setup'; +} // Show platform setup info (Bedrock/Vertex/Foundry) +| { + state: 'ready_to_start'; +} // Flow started, waiting for browser to open +| { + state: 'waiting_for_login'; + url: string; +} // Browser opened, waiting for user to login +| { + state: 'creating_api_key'; +} // Got access token, creating API key +| { + state: 'about_to_retry'; + nextState: OAuthStatus; +} | { + state: 'success'; + token?: string; +} | { + state: 'error'; + message: string; + toRetry?: OAuthStatus; +}; +const PASTE_HERE_MSG = 'Paste code here if prompted > '; +export function ConsoleOAuthFlow({ + onDone, + startingMessage, + mode = 'login', + forceLoginMethod: forceLoginMethodProp +}: Props): React.ReactNode { + const settings = getSettings_DEPRECATED() || {}; + const forceLoginMethod = forceLoginMethodProp ?? settings.forceLoginMethod; + const orgUUID = settings.forceLoginOrgUUID; + const forcedMethodMessage = forceLoginMethod === 'claudeai' ? 'Login method pre-selected: Subscription Plan (Claude Pro/Max)' : forceLoginMethod === 'console' ? 'Login method pre-selected: API Usage Billing (Anthropic Console)' : null; + const terminal = useTerminalNotification(); + const [oauthStatus, setOAuthStatus] = useState(() => { + if (mode === 'setup-token') { + return { + state: 'ready_to_start' + }; + } + if (forceLoginMethod === 'claudeai' || forceLoginMethod === 'console') { + return { + state: 'ready_to_start' + }; + } + return { + state: 'idle' + }; + }); + const [pastedCode, setPastedCode] = useState(''); + const [cursorOffset, setCursorOffset] = useState(0); + const [oauthService] = useState(() => new OAuthService()); + const [loginWithClaudeAi, setLoginWithClaudeAi] = useState(() => { + // Use Claude AI auth for setup-token mode to support user:inference scope + return mode === 'setup-token' || forceLoginMethod === 'claudeai'; + }); + // After a few seconds we suggest the user to copy/paste url if the + // browser did not open automatically. In this flow we expect the user to + // copy the code from the browser and paste it in the terminal + const [showPastePrompt, setShowPastePrompt] = useState(false); + const [urlCopied, setUrlCopied] = useState(false); + const textInputColumns = useTerminalSize().columns - PASTE_HERE_MSG.length - 1; + + // Log forced login method on mount + useEffect(() => { + if (forceLoginMethod === 'claudeai') { + logEvent('tengu_oauth_claudeai_forced', {}); + } else if (forceLoginMethod === 'console') { + logEvent('tengu_oauth_console_forced', {}); + } + }, [forceLoginMethod]); + + // Retry logic + useEffect(() => { + if (oauthStatus.state === 'about_to_retry') { + const timer = setTimeout(setOAuthStatus, 1000, oauthStatus.nextState); + return () => clearTimeout(timer); + } + }, [oauthStatus]); + + // Handle Enter to continue on success state + useKeybinding('confirm:yes', () => { + logEvent('tengu_oauth_success', { + loginWithClaudeAi + }); + onDone(); + }, { + context: 'Confirmation', + isActive: oauthStatus.state === 'success' && mode !== 'setup-token' + }); + + // Handle Enter to continue from platform setup + useKeybinding('confirm:yes', () => { + setOAuthStatus({ + state: 'idle' + }); + }, { + context: 'Confirmation', + isActive: oauthStatus.state === 'platform_setup' + }); + + // Handle Enter to retry on error state + useKeybinding('confirm:yes', () => { + if (oauthStatus.state === 'error' && oauthStatus.toRetry) { + setPastedCode(''); + setOAuthStatus({ + state: 'about_to_retry', + nextState: oauthStatus.toRetry + }); + } + }, { + context: 'Confirmation', + isActive: oauthStatus.state === 'error' && !!oauthStatus.toRetry + }); + useEffect(() => { + if (pastedCode === 'c' && oauthStatus.state === 'waiting_for_login' && showPastePrompt && !urlCopied) { + void setClipboard(oauthStatus.url).then(raw => { + if (raw) process.stdout.write(raw); + setUrlCopied(true); + setTimeout(setUrlCopied, 2000, false); + }); + setPastedCode(''); + } + }, [pastedCode, oauthStatus, showPastePrompt, urlCopied]); + async function handleSubmitCode(value: string, url: string) { + try { + // Expecting format "authorizationCode#state" from the authorization callback URL + const [authorizationCode, state] = value.split('#'); + if (!authorizationCode || !state) { + setOAuthStatus({ + state: 'error', + message: 'Invalid code. Please make sure the full code was copied', + toRetry: { + state: 'waiting_for_login', + url + } + }); + return; + } + + // Track which path the user is taking (manual code entry) + logEvent('tengu_oauth_manual_entry', {}); + oauthService.handleManualAuthCodeInput({ + authorizationCode, + state + }); + } catch (err: unknown) { + logError(err); + setOAuthStatus({ + state: 'error', + message: (err as Error).message, + toRetry: { + state: 'waiting_for_login', + url + } + }); + } + } + const startOAuth = useCallback(async () => { + try { + logEvent('tengu_oauth_flow_start', { + loginWithClaudeAi + }); + const result = await oauthService.startOAuthFlow(async url_0 => { + setOAuthStatus({ + state: 'waiting_for_login', + url: url_0 + }); + setTimeout(setShowPastePrompt, 3000, true); + }, { + loginWithClaudeAi, + inferenceOnly: mode === 'setup-token', + expiresIn: mode === 'setup-token' ? 365 * 24 * 60 * 60 : undefined, + // 1 year for setup-token + orgUUID + }).catch(err_1 => { + const isTokenExchangeError = err_1.message.includes('Token exchange failed'); + // Enterprise TLS proxies (Zscaler et al.) intercept the token + // exchange POST and cause cryptic SSL errors. Surface an + // actionable hint so the user isn't stuck in a login loop. + const sslHint_0 = getSSLErrorHint(err_1); + setOAuthStatus({ + state: 'error', + message: sslHint_0 ?? (isTokenExchangeError ? 'Failed to exchange authorization code for access token. Please try again.' : err_1.message), + toRetry: mode === 'setup-token' ? { + state: 'ready_to_start' + } : { + state: 'idle' + } + }); + logEvent('tengu_oauth_token_exchange_error', { + error: err_1.message, + ssl_error: sslHint_0 !== null + }); + throw err_1; + }); + if (mode === 'setup-token') { + // For setup-token mode, return the OAuth access token directly (it can be used as an API key) + // Don't save to keychain - the token is displayed for manual use with CLAUDE_CODE_OAUTH_TOKEN + setOAuthStatus({ + state: 'success', + token: result.accessToken + }); + } else { + await installOAuthTokens(result); + const orgResult = await validateForceLoginOrg(); + if (!orgResult.valid) { + throw new Error(orgResult.message); + } + setOAuthStatus({ + state: 'success' + }); + void sendNotification({ + message: 'Claude Code login successful', + notificationType: 'auth_success' + }, terminal); + } + } catch (err_0) { + const errorMessage = (err_0 as Error).message; + const sslHint = getSSLErrorHint(err_0); + setOAuthStatus({ + state: 'error', + message: sslHint ?? errorMessage, + toRetry: { + state: mode === 'setup-token' ? 'ready_to_start' : 'idle' + } + }); + logEvent('tengu_oauth_error', { + error: errorMessage as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + ssl_error: sslHint !== null + }); + } + }, [oauthService, setShowPastePrompt, loginWithClaudeAi, mode, orgUUID]); + const pendingOAuthStartRef = useRef(false); + useEffect(() => { + if (oauthStatus.state === 'ready_to_start' && !pendingOAuthStartRef.current) { + pendingOAuthStartRef.current = true; + process.nextTick((startOAuth_0: () => Promise, pendingOAuthStartRef_0: React.MutableRefObject) => { + void startOAuth_0(); + pendingOAuthStartRef_0.current = false; + }, startOAuth, pendingOAuthStartRef); + } + }, [oauthStatus.state, startOAuth]); + + // Auto-exit for setup-token mode + useEffect(() => { + if (mode === 'setup-token' && oauthStatus.state === 'success') { + // Delay to ensure static content is fully rendered before exiting + const timer_0 = setTimeout((loginWithClaudeAi_0, onDone_0) => { + logEvent('tengu_oauth_success', { + loginWithClaudeAi: loginWithClaudeAi_0 + }); + // Don't clear terminal so the token remains visible + onDone_0(); + }, 500, loginWithClaudeAi, onDone); + return () => clearTimeout(timer_0); + } + }, [mode, oauthStatus, loginWithClaudeAi, onDone]); + + // Cleanup OAuth service when component unmounts + useEffect(() => { + return () => { + oauthService.cleanup(); + }; + }, [oauthService]); + return + {oauthStatus.state === 'waiting_for_login' && showPastePrompt && + + + Browser didn't open? Use the url below to sign in{' '} + + {urlCopied ? (Copied!) : + + } + + + {oauthStatus.url} + + } + {mode === 'setup-token' && oauthStatus.state === 'success' && oauthStatus.token && + + ✓ Long-lived authentication token created successfully! + + + Your OAuth token (valid for 1 year): + {oauthStatus.token} + + Store this token securely. You won't be able to see it + again. + + + Use this token by setting: export + CLAUDE_CODE_OAUTH_TOKEN=<token> + + + } + + + + ; +} +type OAuthStatusMessageProps = { + oauthStatus: OAuthStatus; + mode: 'login' | 'setup-token'; + startingMessage: string | undefined; + forcedMethodMessage: string | null; + showPastePrompt: boolean; + pastedCode: string; + setPastedCode: (value: string) => void; + cursorOffset: number; + setCursorOffset: (offset: number) => void; + textInputColumns: number; + handleSubmitCode: (value: string, url: string) => void; + setOAuthStatus: (status: OAuthStatus) => void; + setLoginWithClaudeAi: (value: boolean) => void; +}; +function OAuthStatusMessage(t0) { + const $ = _c(51); + const { + oauthStatus, + mode, + startingMessage, + forcedMethodMessage, + showPastePrompt, + pastedCode, + setPastedCode, + cursorOffset, + setCursorOffset, + textInputColumns, + handleSubmitCode, + setOAuthStatus, + setLoginWithClaudeAi + } = t0; + switch (oauthStatus.state) { + case "idle": + { + const t1 = startingMessage ? startingMessage : "Claude Code can be used with your Claude subscription or billed based on API usage through your Console account."; + let t2; + if ($[0] !== t1) { + t2 = {t1}; + $[0] = t1; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t3 = Select login method:; + $[2] = t3; + } else { + t3 = $[2]; + } + let t4; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t4 = { + label: Claude account with subscription ·{" "}Pro, Max, Team, or Enterprise{false && {"\n"}[ANT-ONLY]{" "}Please use this option unless you need to login to a special org for accessing sensitive data (e.g. customer data, HIPI data) with the Console option}{"\n"}, + value: "claudeai" + }; + $[3] = t4; + } else { + t4 = $[3]; + } + let t5; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + label: Anthropic Console account ·{" "}API usage billing{"\n"}, + value: "console" + }; + $[4] = t5; + } else { + t5 = $[4]; + } + let t6; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t6 = [t4, t5, { + label: 3rd-party platform ·{" "}Amazon Bedrock, Microsoft Foundry, or Vertex AI{"\n"}, + value: "platform" + }]; + $[5] = t6; + } else { + t6 = $[5]; + } + let t7; + if ($[6] !== setLoginWithClaudeAi || $[7] !== setOAuthStatus) { + t7 = ; + $[2] = onDone; + $[3] = t3; + } else { + t3 = $[3]; + } + let t4; + if ($[4] !== onDone || $[5] !== t3) { + t4 = {t1}{t3}; + $[4] = onDone; + $[5] = t3; + $[6] = t4; + } else { + t4 = $[6]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIkxpbmsiLCJUZXh0IiwiU2VsZWN0IiwiRGlhbG9nIiwiUHJvcHMiLCJvbkRvbmUiLCJDb3N0VGhyZXNob2xkRGlhbG9nIiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsInQyIiwidmFsdWUiLCJsYWJlbCIsInQzIiwidDQiXSwic291cmNlcyI6WyJDb3N0VGhyZXNob2xkRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIExpbmssIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuL0N1c3RvbVNlbGVjdC9pbmRleC5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG9uRG9uZTogKCkgPT4gdm9pZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gQ29zdFRocmVzaG9sZERpYWxvZyh7IG9uRG9uZSB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJZb3UndmUgc3BlbnQgJDUgb24gdGhlIEFudGhyb3BpYyBBUEkgdGhpcyBzZXNzaW9uLlwiXG4gICAgICBvbkNhbmNlbD17b25Eb25lfVxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8VGV4dD5MZWFybiBtb3JlIGFib3V0IGhvdyB0byBtb25pdG9yIHlvdXIgc3BlbmRpbmc6PC9UZXh0PlxuICAgICAgICA8TGluayB1cmw9XCJodHRwczovL2NvZGUuY2xhdWRlLmNvbS9kb2NzL2VuL2Nvc3RzXCIgLz5cbiAgICAgIDwvQm94PlxuICAgICAgPFNlbGVjdFxuICAgICAgICBvcHRpb25zPXtbXG4gICAgICAgICAge1xuICAgICAgICAgICAgdmFsdWU6ICdvaycsXG4gICAgICAgICAgICBsYWJlbDogJ0dvdCBpdCwgdGhhbmtzIScsXG4gICAgICAgICAgfSxcbiAgICAgICAgXX1cbiAgICAgICAgb25DaGFuZ2U9e29uRG9uZX1cbiAgICAgIC8+XG4gICAgPC9EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUMzQyxTQUFTQyxNQUFNLFFBQVEseUJBQXlCO0FBQ2hELFNBQVNDLE1BQU0sUUFBUSwyQkFBMkI7QUFFbEQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLE1BQU0sRUFBRSxHQUFHLEdBQUcsSUFBSTtBQUNwQixDQUFDO0FBRUQsT0FBTyxTQUFBQyxvQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUE2QjtJQUFBSjtFQUFBLElBQUFFLEVBQWlCO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQUcsTUFBQSxDQUFBQyxHQUFBO0lBTS9DRixFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQ3pCLENBQUMsSUFBSSxDQUFDLDhDQUE4QyxFQUFuRCxJQUFJLENBQ0wsQ0FBQyxJQUFJLENBQUssR0FBdUMsQ0FBdkMsdUNBQXVDLEdBQ25ELEVBSEMsR0FBRyxDQUdFO0lBQUFGLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsSUFBQUssRUFBQTtFQUFBLElBQUFMLENBQUEsUUFBQUcsTUFBQSxDQUFBQyxHQUFBO0lBRUtDLEVBQUEsSUFDUDtNQUFBQyxLQUFBLEVBQ1MsSUFBSTtNQUFBQyxLQUFBLEVBQ0o7SUFDVCxDQUFDLENBQ0Y7SUFBQVAsQ0FBQSxNQUFBSyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBTCxDQUFBO0VBQUE7RUFBQSxJQUFBUSxFQUFBO0VBQUEsSUFBQVIsQ0FBQSxRQUFBSCxNQUFBO0lBTkhXLEVBQUEsSUFBQyxNQUFNLENBQ0ksT0FLUixDQUxRLENBQUFILEVBS1QsQ0FBQyxDQUNTUixRQUFNLENBQU5BLE9BQUssQ0FBQyxHQUNoQjtJQUFBRyxDQUFBLE1BQUFILE1BQUE7SUFBQUcsQ0FBQSxNQUFBUSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBUixDQUFBO0VBQUE7RUFBQSxJQUFBUyxFQUFBO0VBQUEsSUFBQVQsQ0FBQSxRQUFBSCxNQUFBLElBQUFHLENBQUEsUUFBQVEsRUFBQTtJQWhCSkMsRUFBQSxJQUFDLE1BQU0sQ0FDQyxLQUFvRCxDQUFwRCxvREFBb0QsQ0FDaERaLFFBQU0sQ0FBTkEsT0FBSyxDQUFDLENBRWhCLENBQUFLLEVBR0ssQ0FDTCxDQUFBTSxFQVFDLENBQ0gsRUFqQkMsTUFBTSxDQWlCRTtJQUFBUixDQUFBLE1BQUFILE1BQUE7SUFBQUcsQ0FBQSxNQUFBUSxFQUFBO0lBQUFSLENBQUEsTUFBQVMsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVQsQ0FBQTtFQUFBO0VBQUEsT0FqQlRTLEVBaUJTO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/CtrlOToExpand.tsx b/components/CtrlOToExpand.tsx new file mode 100644 index 0000000..3fe799b --- /dev/null +++ b/components/CtrlOToExpand.tsx @@ -0,0 +1,51 @@ +import { c as _c } from "react/compiler-runtime"; +import chalk from 'chalk'; +import React, { useContext } from 'react'; +import { Text } from '../ink.js'; +import { getShortcutDisplay } from '../keybindings/shortcutFormat.js'; +import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +import { InVirtualListContext } from './messageActions.js'; + +// Context to track if we're inside a sub agent +// Similar to MessageResponseContext, this helps us avoid showing +// too many "(ctrl+o to expand)" hints in sub agent output +const SubAgentContext = React.createContext(false); +export function SubAgentProvider(t0) { + const $ = _c(2); + const { + children + } = t0; + let t1; + if ($[0] !== children) { + t1 = {children}; + $[0] = children; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} +export function CtrlOToExpand() { + const $ = _c(2); + const isInSubAgent = useContext(SubAgentContext); + const inVirtualList = useContext(InVirtualListContext); + const expandShortcut = useShortcutDisplay("app:toggleTranscript", "Global", "ctrl+o"); + if (isInSubAgent || inVirtualList) { + return null; + } + let t0; + if ($[0] !== expandShortcut) { + t0 = ; + $[0] = expandShortcut; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} +export function ctrlOToExpand(): string { + const shortcut = getShortcutDisplay('app:toggleTranscript', 'Global', 'ctrl+o'); + return chalk.dim(`(${shortcut} to expand)`); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjaGFsayIsIlJlYWN0IiwidXNlQ29udGV4dCIsIlRleHQiLCJnZXRTaG9ydGN1dERpc3BsYXkiLCJ1c2VTaG9ydGN1dERpc3BsYXkiLCJLZXlib2FyZFNob3J0Y3V0SGludCIsIkluVmlydHVhbExpc3RDb250ZXh0IiwiU3ViQWdlbnRDb250ZXh0IiwiY3JlYXRlQ29udGV4dCIsIlN1YkFnZW50UHJvdmlkZXIiLCJ0MCIsIiQiLCJfYyIsImNoaWxkcmVuIiwidDEiLCJDdHJsT1RvRXhwYW5kIiwiaXNJblN1YkFnZW50IiwiaW5WaXJ0dWFsTGlzdCIsImV4cGFuZFNob3J0Y3V0IiwiY3RybE9Ub0V4cGFuZCIsInNob3J0Y3V0IiwiZGltIl0sInNvdXJjZXMiOlsiQ3RybE9Ub0V4cGFuZC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJ1xuaW1wb3J0IFJlYWN0LCB7IHVzZUNvbnRleHQgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBnZXRTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi9rZXliaW5kaW5ncy9zaG9ydGN1dEZvcm1hdC5qcydcbmltcG9ydCB7IHVzZVNob3J0Y3V0RGlzcGxheSB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZVNob3J0Y3V0RGlzcGxheS5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgSW5WaXJ0dWFsTGlzdENvbnRleHQgfSBmcm9tICcuL21lc3NhZ2VBY3Rpb25zLmpzJ1xuXG4vLyBDb250ZXh0IHRvIHRyYWNrIGlmIHdlJ3JlIGluc2lkZSBhIHN1YiBhZ2VudFxuLy8gU2ltaWxhciB0byBNZXNzYWdlUmVzcG9uc2VDb250ZXh0LCB0aGlzIGhlbHBzIHVzIGF2b2lkIHNob3dpbmdcbi8vIHRvbyBtYW55IFwiKGN0cmwrbyB0byBleHBhbmQpXCIgaGludHMgaW4gc3ViIGFnZW50IG91dHB1dFxuY29uc3QgU3ViQWdlbnRDb250ZXh0ID0gUmVhY3QuY3JlYXRlQ29udGV4dChmYWxzZSlcblxuZXhwb3J0IGZ1bmN0aW9uIFN1YkFnZW50UHJvdmlkZXIoe1xuICBjaGlsZHJlbixcbn06IHtcbiAgY2hpbGRyZW46IFJlYWN0LlJlYWN0Tm9kZVxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPFN1YkFnZW50Q29udGV4dC5Qcm92aWRlciB2YWx1ZT17dHJ1ZX0+e2NoaWxkcmVufTwvU3ViQWdlbnRDb250ZXh0LlByb3ZpZGVyPlxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBDdHJsT1RvRXhwYW5kKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGlzSW5TdWJBZ2VudCA9IHVzZUNvbnRleHQoU3ViQWdlbnRDb250ZXh0KVxuICBjb25zdCBpblZpcnR1YWxMaXN0ID0gdXNlQ29udGV4dChJblZpcnR1YWxMaXN0Q29udGV4dClcbiAgY29uc3QgZXhwYW5kU2hvcnRjdXQgPSB1c2VTaG9ydGN1dERpc3BsYXkoXG4gICAgJ2FwcDp0b2dnbGVUcmFuc2NyaXB0JyxcbiAgICAnR2xvYmFsJyxcbiAgICAnY3RybCtvJyxcbiAgKVxuICBpZiAoaXNJblN1YkFnZW50IHx8IGluVmlydHVhbExpc3QpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG4gIHJldHVybiAoXG4gICAgPFRleHQgZGltQ29sb3I+XG4gICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9e2V4cGFuZFNob3J0Y3V0fSBhY3Rpb249XCJleHBhbmRcIiBwYXJlbnMgLz5cbiAgICA8L1RleHQ+XG4gIClcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGN0cmxPVG9FeHBhbmQoKTogc3RyaW5nIHtcbiAgY29uc3Qgc2hvcnRjdXQgPSBnZXRTaG9ydGN1dERpc3BsYXkoXG4gICAgJ2FwcDp0b2dnbGVUcmFuc2NyaXB0JyxcbiAgICAnR2xvYmFsJyxcbiAgICAnY3RybCtvJyxcbiAgKVxuICByZXR1cm4gY2hhbGsuZGltKGAoJHtzaG9ydGN1dH0gdG8gZXhwYW5kKWApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixPQUFPQyxLQUFLLElBQUlDLFVBQVUsUUFBUSxPQUFPO0FBQ3pDLFNBQVNDLElBQUksUUFBUSxXQUFXO0FBQ2hDLFNBQVNDLGtCQUFrQixRQUFRLGtDQUFrQztBQUNyRSxTQUFTQyxrQkFBa0IsUUFBUSxzQ0FBc0M7QUFDekUsU0FBU0Msb0JBQW9CLFFBQVEseUNBQXlDO0FBQzlFLFNBQVNDLG9CQUFvQixRQUFRLHFCQUFxQjs7QUFFMUQ7QUFDQTtBQUNBO0FBQ0EsTUFBTUMsZUFBZSxHQUFHUCxLQUFLLENBQUNRLGFBQWEsQ0FBQyxLQUFLLENBQUM7QUFFbEQsT0FBTyxTQUFBQyxpQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUEwQjtJQUFBQztFQUFBLElBQUFILEVBSWhDO0VBQUEsSUFBQUksRUFBQTtFQUFBLElBQUFILENBQUEsUUFBQUUsUUFBQTtJQUVHQyxFQUFBLDZCQUFpQyxLQUFJLENBQUosS0FBRyxDQUFDLENBQUdELFNBQU8sQ0FBRSwyQkFBMkI7SUFBQUYsQ0FBQSxNQUFBRSxRQUFBO0lBQUFGLENBQUEsTUFBQUcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBQUEsT0FBNUVHLEVBQTRFO0FBQUE7QUFJaEYsT0FBTyxTQUFBQyxjQUFBO0VBQUEsTUFBQUosQ0FBQSxHQUFBQyxFQUFBO0VBQ0wsTUFBQUksWUFBQSxHQUFxQmYsVUFBVSxDQUFDTSxlQUFlLENBQUM7RUFDaEQsTUFBQVUsYUFBQSxHQUFzQmhCLFVBQVUsQ0FBQ0ssb0JBQW9CLENBQUM7RUFDdEQsTUFBQVksY0FBQSxHQUF1QmQsa0JBQWtCLENBQ3ZDLHNCQUFzQixFQUN0QixRQUFRLEVBQ1IsUUFDRixDQUFDO0VBQ0QsSUFBSVksWUFBNkIsSUFBN0JDLGFBQTZCO0lBQUEsT0FDeEIsSUFBSTtFQUFBO0VBQ1osSUFBQVAsRUFBQTtFQUFBLElBQUFDLENBQUEsUUFBQU8sY0FBQTtJQUVDUixFQUFBLElBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FDWixDQUFDLG9CQUFvQixDQUFXUSxRQUFjLENBQWRBLGVBQWEsQ0FBQyxDQUFTLE1BQVEsQ0FBUixRQUFRLENBQUMsTUFBTSxDQUFOLEtBQUssQ0FBQyxHQUN4RSxFQUZDLElBQUksQ0FFRTtJQUFBUCxDQUFBLE1BQUFPLGNBQUE7SUFBQVAsQ0FBQSxNQUFBRCxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBQyxDQUFBO0VBQUE7RUFBQSxPQUZQRCxFQUVPO0FBQUE7QUFJWCxPQUFPLFNBQVNTLGFBQWFBLENBQUEsQ0FBRSxFQUFFLE1BQU0sQ0FBQztFQUN0QyxNQUFNQyxRQUFRLEdBQUdqQixrQkFBa0IsQ0FDakMsc0JBQXNCLEVBQ3RCLFFBQVEsRUFDUixRQUNGLENBQUM7RUFDRCxPQUFPSixLQUFLLENBQUNzQixHQUFHLENBQUMsSUFBSUQsUUFBUSxhQUFhLENBQUM7QUFDN0MiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/CustomSelect/SelectMulti.tsx b/components/CustomSelect/SelectMulti.tsx new file mode 100644 index 0000000..e757ec3 --- /dev/null +++ b/components/CustomSelect/SelectMulti.tsx @@ -0,0 +1,213 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import React from 'react'; +import { Box, Text } from '../../ink.js'; +import type { PastedContent } from '../../utils/config.js'; +import type { ImageDimensions } from '../../utils/imageResizer.js'; +import type { OptionWithDescription } from './select.js'; +import { SelectInputOption } from './select-input-option.js'; +import { SelectOption } from './select-option.js'; +import { useMultiSelectState } from './use-multi-select-state.js'; +export type SelectMultiProps = { + readonly isDisabled?: boolean; + readonly visibleOptionCount?: number; + readonly options: OptionWithDescription[]; + readonly defaultValue?: T[]; + readonly onCancel: () => void; + readonly onChange?: (values: T[]) => void; + readonly onFocus?: (value: T) => void; + readonly focusValue?: T; + /** + * Text for the submit button. When provided, a submit button is shown and + * Enter toggles selection (submit only fires when the button is focused). + * When omitted, Enter submits directly and Space toggles selection. + */ + readonly submitButtonText?: string; + /** + * Callback when user submits. Receives the currently selected values. + */ + readonly onSubmit?: (values: T[]) => void; + /** + * When true, hides the numeric indexes next to each option. + */ + readonly hideIndexes?: boolean; + /** + * Callback when user presses down from the last item (submit button). + * If provided, navigation will not wrap to the first item. + */ + readonly onDownFromLastItem?: () => void; + /** + * Callback when user presses up from the first item. + * If provided, navigation will not wrap to the last item. + */ + readonly onUpFromFirstItem?: () => void; + /** + * Focus the last option initially instead of the first. + */ + readonly initialFocusLast?: boolean; + /** + * Callback to open external editor for editing input option values. + * When provided, ctrl+g will trigger this callback in input options + * with the current value and a setter function to update the internal state. + */ + readonly onOpenEditor?: (currentValue: string, setValue: (value: string) => void) => void; + readonly onImagePaste?: (base64Image: string, mediaType?: string, filename?: string, dimensions?: ImageDimensions, sourcePath?: string) => void; + readonly pastedContents?: Record; + readonly onRemoveImage?: (id: number) => void; +}; +export function SelectMulti(t0) { + const $ = _c(44); + const { + isDisabled: t1, + visibleOptionCount: t2, + options, + defaultValue: t3, + onCancel, + onChange, + onFocus, + focusValue, + submitButtonText, + onSubmit, + onDownFromLastItem, + onUpFromFirstItem, + initialFocusLast, + onOpenEditor, + hideIndexes: t4, + onImagePaste, + pastedContents, + onRemoveImage + } = t0; + const isDisabled = t1 === undefined ? false : t1; + const visibleOptionCount = t2 === undefined ? 5 : t2; + let t5; + if ($[0] !== t3) { + t5 = t3 === undefined ? [] : t3; + $[0] = t3; + $[1] = t5; + } else { + t5 = $[1]; + } + const defaultValue = t5; + const hideIndexes = t4 === undefined ? false : t4; + let t6; + if ($[2] !== defaultValue || $[3] !== focusValue || $[4] !== hideIndexes || $[5] !== initialFocusLast || $[6] !== isDisabled || $[7] !== onCancel || $[8] !== onChange || $[9] !== onDownFromLastItem || $[10] !== onFocus || $[11] !== onSubmit || $[12] !== onUpFromFirstItem || $[13] !== options || $[14] !== submitButtonText || $[15] !== visibleOptionCount) { + t6 = { + isDisabled, + visibleOptionCount, + options, + defaultValue, + onChange, + onCancel, + onFocus, + focusValue, + submitButtonText, + onSubmit, + onDownFromLastItem, + onUpFromFirstItem, + initialFocusLast, + hideIndexes + }; + $[2] = defaultValue; + $[3] = focusValue; + $[4] = hideIndexes; + $[5] = initialFocusLast; + $[6] = isDisabled; + $[7] = onCancel; + $[8] = onChange; + $[9] = onDownFromLastItem; + $[10] = onFocus; + $[11] = onSubmit; + $[12] = onUpFromFirstItem; + $[13] = options; + $[14] = submitButtonText; + $[15] = visibleOptionCount; + $[16] = t6; + } else { + t6 = $[16]; + } + const state = useMultiSelectState(t6); + let T0; + let T1; + let t7; + let t8; + let t9; + if ($[17] !== hideIndexes || $[18] !== isDisabled || $[19] !== onCancel || $[20] !== onImagePaste || $[21] !== onOpenEditor || $[22] !== onRemoveImage || $[23] !== options.length || $[24] !== pastedContents || $[25] !== state) { + const maxIndexWidth = options.length.toString().length; + T1 = Box; + t9 = "column"; + T0 = Box; + t7 = "column"; + t8 = state.visibleOptions.map((option, index) => { + const isOptionFocused = !isDisabled && state.focusedValue === option.value && !state.isSubmitFocused; + const isSelected = state.selectedValues.includes(option.value); + const isFirstVisibleOption = option.index === state.visibleFromIndex; + const isLastVisibleOption = option.index === state.visibleToIndex - 1; + const areMoreOptionsBelow = state.visibleToIndex < options.length; + const areMoreOptionsAbove = state.visibleFromIndex > 0; + const i = state.visibleFromIndex + index + 1; + if (option.type === "input") { + const inputValue = state.inputValues.get(option.value) || ""; + return { + state.updateInputValue(option.value, value); + }} onSubmit={_temp} onExit={() => { + onCancel(); + }} layout="compact" onOpenEditor={onOpenEditor} onImagePaste={onImagePaste} pastedContents={pastedContents} onRemoveImage={onRemoveImage}>[{isSelected ? figures.tick : " "}]{" "}; + } + return {!hideIndexes && {`${i}.`.padEnd(maxIndexWidth)}}[{isSelected ? figures.tick : " "}]{option.label}; + }); + $[17] = hideIndexes; + $[18] = isDisabled; + $[19] = onCancel; + $[20] = onImagePaste; + $[21] = onOpenEditor; + $[22] = onRemoveImage; + $[23] = options.length; + $[24] = pastedContents; + $[25] = state; + $[26] = T0; + $[27] = T1; + $[28] = t7; + $[29] = t8; + $[30] = t9; + } else { + T0 = $[26]; + T1 = $[27]; + t7 = $[28]; + t8 = $[29]; + t9 = $[30]; + } + let t10; + if ($[31] !== T0 || $[32] !== t7 || $[33] !== t8) { + t10 = {t8}; + $[31] = T0; + $[32] = t7; + $[33] = t8; + $[34] = t10; + } else { + t10 = $[34]; + } + let t11; + if ($[35] !== onSubmit || $[36] !== state.isSubmitFocused || $[37] !== submitButtonText) { + t11 = submitButtonText && onSubmit && {state.isSubmitFocused ? {figures.pointer} : }{submitButtonText}; + $[35] = onSubmit; + $[36] = state.isSubmitFocused; + $[37] = submitButtonText; + $[38] = t11; + } else { + t11 = $[38]; + } + let t12; + if ($[39] !== T1 || $[40] !== t10 || $[41] !== t11 || $[42] !== t9) { + t12 = {t10}{t11}; + $[39] = T1; + $[40] = t10; + $[41] = t11; + $[42] = t9; + $[43] = t12; + } else { + t12 = $[43]; + } + return t12; +} +function _temp() {} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwiUGFzdGVkQ29udGVudCIsIkltYWdlRGltZW5zaW9ucyIsIk9wdGlvbldpdGhEZXNjcmlwdGlvbiIsIlNlbGVjdElucHV0T3B0aW9uIiwiU2VsZWN0T3B0aW9uIiwidXNlTXVsdGlTZWxlY3RTdGF0ZSIsIlNlbGVjdE11bHRpUHJvcHMiLCJpc0Rpc2FibGVkIiwidmlzaWJsZU9wdGlvbkNvdW50Iiwib3B0aW9ucyIsIlQiLCJkZWZhdWx0VmFsdWUiLCJvbkNhbmNlbCIsIm9uQ2hhbmdlIiwidmFsdWVzIiwib25Gb2N1cyIsInZhbHVlIiwiZm9jdXNWYWx1ZSIsInN1Ym1pdEJ1dHRvblRleHQiLCJvblN1Ym1pdCIsImhpZGVJbmRleGVzIiwib25Eb3duRnJvbUxhc3RJdGVtIiwib25VcEZyb21GaXJzdEl0ZW0iLCJpbml0aWFsRm9jdXNMYXN0Iiwib25PcGVuRWRpdG9yIiwiY3VycmVudFZhbHVlIiwic2V0VmFsdWUiLCJvbkltYWdlUGFzdGUiLCJiYXNlNjRJbWFnZSIsIm1lZGlhVHlwZSIsImZpbGVuYW1lIiwiZGltZW5zaW9ucyIsInNvdXJjZVBhdGgiLCJwYXN0ZWRDb250ZW50cyIsIlJlY29yZCIsIm9uUmVtb3ZlSW1hZ2UiLCJpZCIsIlNlbGVjdE11bHRpIiwidDAiLCIkIiwiX2MiLCJ0MSIsInQyIiwidDMiLCJ0NCIsInVuZGVmaW5lZCIsInQ1IiwidDYiLCJzdGF0ZSIsIlQwIiwiVDEiLCJ0NyIsInQ4IiwidDkiLCJsZW5ndGgiLCJtYXhJbmRleFdpZHRoIiwidG9TdHJpbmciLCJ2aXNpYmxlT3B0aW9ucyIsIm1hcCIsIm9wdGlvbiIsImluZGV4IiwiaXNPcHRpb25Gb2N1c2VkIiwiZm9jdXNlZFZhbHVlIiwiaXNTdWJtaXRGb2N1c2VkIiwiaXNTZWxlY3RlZCIsInNlbGVjdGVkVmFsdWVzIiwiaW5jbHVkZXMiLCJpc0ZpcnN0VmlzaWJsZU9wdGlvbiIsInZpc2libGVGcm9tSW5kZXgiLCJpc0xhc3RWaXNpYmxlT3B0aW9uIiwidmlzaWJsZVRvSW5kZXgiLCJhcmVNb3JlT3B0aW9uc0JlbG93IiwiYXJlTW9yZU9wdGlvbnNBYm92ZSIsImkiLCJ0eXBlIiwiaW5wdXRWYWx1ZSIsImlucHV0VmFsdWVzIiwiZ2V0IiwiU3RyaW5nIiwidXBkYXRlSW5wdXRWYWx1ZSIsIl90ZW1wIiwidGljayIsImRlc2NyaXB0aW9uIiwicGFkRW5kIiwibGFiZWwiLCJ0MTAiLCJ0MTEiLCJwb2ludGVyIiwidDEyIl0sInNvdXJjZXMiOlsiU2VsZWN0TXVsdGkudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IFBhc3RlZENvbnRlbnQgfSBmcm9tICcuLi8uLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgdHlwZSB7IEltYWdlRGltZW5zaW9ucyB9IGZyb20gJy4uLy4uL3V0aWxzL2ltYWdlUmVzaXplci5qcydcbmltcG9ydCB0eXBlIHsgT3B0aW9uV2l0aERlc2NyaXB0aW9uIH0gZnJvbSAnLi9zZWxlY3QuanMnXG5pbXBvcnQgeyBTZWxlY3RJbnB1dE9wdGlvbiB9IGZyb20gJy4vc2VsZWN0LWlucHV0LW9wdGlvbi5qcydcbmltcG9ydCB7IFNlbGVjdE9wdGlvbiB9IGZyb20gJy4vc2VsZWN0LW9wdGlvbi5qcydcbmltcG9ydCB7IHVzZU11bHRpU2VsZWN0U3RhdGUgfSBmcm9tICcuL3VzZS1tdWx0aS1zZWxlY3Qtc3RhdGUuanMnXG5cbmV4cG9ydCB0eXBlIFNlbGVjdE11bHRpUHJvcHM8VD4gPSB7XG4gIHJlYWRvbmx5IGlzRGlzYWJsZWQ/OiBib29sZWFuXG4gIHJlYWRvbmx5IHZpc2libGVPcHRpb25Db3VudD86IG51bWJlclxuICByZWFkb25seSBvcHRpb25zOiBPcHRpb25XaXRoRGVzY3JpcHRpb248VD5bXVxuICByZWFkb25seSBkZWZhdWx0VmFsdWU/OiBUW11cbiAgcmVhZG9ubHkgb25DYW5jZWw6ICgpID0+IHZvaWRcbiAgcmVhZG9ubHkgb25DaGFuZ2U/OiAodmFsdWVzOiBUW10pID0+IHZvaWRcbiAgcmVhZG9ubHkgb25Gb2N1cz86ICh2YWx1ZTogVCkgPT4gdm9pZFxuICByZWFkb25seSBmb2N1c1ZhbHVlPzogVFxuICAvKipcbiAgICogVGV4dCBmb3IgdGhlIHN1Ym1pdCBidXR0b24uIFdoZW4gcHJvdmlkZWQsIGEgc3VibWl0IGJ1dHRvbiBpcyBzaG93biBhbmRcbiAgICogRW50ZXIgdG9nZ2xlcyBzZWxlY3Rpb24gKHN1Ym1pdCBvbmx5IGZpcmVzIHdoZW4gdGhlIGJ1dHRvbiBpcyBmb2N1c2VkKS5cbiAgICogV2hlbiBvbWl0dGVkLCBFbnRlciBzdWJtaXRzIGRpcmVjdGx5IGFuZCBTcGFjZSB0b2dnbGVzIHNlbGVjdGlvbi5cbiAgICovXG4gIHJlYWRvbmx5IHN1Ym1pdEJ1dHRvblRleHQ/OiBzdHJpbmdcbiAgLyoqXG4gICAqIENhbGxiYWNrIHdoZW4gdXNlciBzdWJtaXRzLiBSZWNlaXZlcyB0aGUgY3VycmVudGx5IHNlbGVjdGVkIHZhbHVlcy5cbiAgICovXG4gIHJlYWRvbmx5IG9uU3VibWl0PzogKHZhbHVlczogVFtdKSA9PiB2b2lkXG4gIC8qKlxuICAgKiBXaGVuIHRydWUsIGhpZGVzIHRoZSBudW1lcmljIGluZGV4ZXMgbmV4dCB0byBlYWNoIG9wdGlvbi5cbiAgICovXG4gIHJlYWRvbmx5IGhpZGVJbmRleGVzPzogYm9vbGVhblxuICAvKipcbiAgICogQ2FsbGJhY2sgd2hlbiB1c2VyIHByZXNzZXMgZG93biBmcm9tIHRoZSBsYXN0IGl0ZW0gKHN1Ym1pdCBidXR0b24pLlxuICAgKiBJZiBwcm92aWRlZCwgbmF2aWdhdGlvbiB3aWxsIG5vdCB3cmFwIHRvIHRoZSBmaXJzdCBpdGVtLlxuICAgKi9cbiAgcmVhZG9ubHkgb25Eb3duRnJvbUxhc3RJdGVtPzogKCkgPT4gdm9pZFxuICAvKipcbiAgICogQ2FsbGJhY2sgd2hlbiB1c2VyIHByZXNzZXMgdXAgZnJvbSB0aGUgZmlyc3QgaXRlbS5cbiAgICogSWYgcHJvdmlkZWQsIG5hdmlnYXRpb24gd2lsbCBub3Qgd3JhcCB0byB0aGUgbGFzdCBpdGVtLlxuICAgKi9cbiAgcmVhZG9ubHkgb25VcEZyb21GaXJzdEl0ZW0/OiAoKSA9PiB2b2lkXG4gIC8qKlxuICAgKiBGb2N1cyB0aGUgbGFzdCBvcHRpb24gaW5pdGlhbGx5IGluc3RlYWQgb2YgdGhlIGZpcnN0LlxuICAgKi9cbiAgcmVhZG9ubHkgaW5pdGlhbEZvY3VzTGFzdD86IGJvb2xlYW5cbiAgLyoqXG4gICAqIENhbGxiYWNrIHRvIG9wZW4gZXh0ZXJuYWwgZWRpdG9yIGZvciBlZGl0aW5nIGlucHV0IG9wdGlvbiB2YWx1ZXMuXG4gICAqIFdoZW4gcHJvdmlkZWQsIGN0cmwrZyB3aWxsIHRyaWdnZXIgdGhpcyBjYWxsYmFjayBpbiBpbnB1dCBvcHRpb25zXG4gICAqIHdpdGggdGhlIGN1cnJlbnQgdmFsdWUgYW5kIGEgc2V0dGVyIGZ1bmN0aW9uIHRvIHVwZGF0ZSB0aGUgaW50ZXJuYWwgc3RhdGUuXG4gICAqL1xuICByZWFkb25seSBvbk9wZW5FZGl0b3I/OiAoXG4gICAgY3VycmVudFZhbHVlOiBzdHJpbmcsXG4gICAgc2V0VmFsdWU6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkLFxuICApID0+IHZvaWRcbiAgcmVhZG9ubHkgb25JbWFnZVBhc3RlPzogKFxuICAgIGJhc2U2NEltYWdlOiBzdHJpbmcsXG4gICAgbWVkaWFUeXBlPzogc3RyaW5nLFxuICAgIGZpbGVuYW1lPzogc3RyaW5nLFxuICAgIGRpbWVuc2lvbnM/OiBJbWFnZURpbWVuc2lvbnMsXG4gICAgc291cmNlUGF0aD86IHN0cmluZyxcbiAgKSA9PiB2b2lkXG4gIHJlYWRvbmx5IHBhc3RlZENvbnRlbnRzPzogUmVjb3JkPG51bWJlciwgUGFzdGVkQ29udGVudD5cbiAgcmVhZG9ubHkgb25SZW1vdmVJbWFnZT86IChpZDogbnVtYmVyKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTZWxlY3RNdWx0aTxUPih7XG4gIGlzRGlzYWJsZWQgPSBmYWxzZSxcbiAgdmlzaWJsZU9wdGlvbkNvdW50ID0gNSxcbiAgb3B0aW9ucyxcbiAgZGVmYXVsdFZhbHVlID0gW10sXG4gIG9uQ2FuY2VsLFxuICBvbkNoYW5nZSxcbiAgb25Gb2N1cyxcbiAgZm9jdXNWYWx1ZSxcbiAgc3VibWl0QnV0dG9uVGV4dCxcbiAgb25TdWJtaXQsXG4gIG9uRG93bkZyb21MYXN0SXRlbSxcbiAgb25VcEZyb21GaXJzdEl0ZW0sXG4gIGluaXRpYWxGb2N1c0xhc3QsXG4gIG9uT3BlbkVkaXRvcixcbiAgaGlkZUluZGV4ZXMgPSBmYWxzZSxcbiAgb25JbWFnZVBhc3RlLFxuICBwYXN0ZWRDb250ZW50cyxcbiAgb25SZW1vdmVJbWFnZSxcbn06IFNlbGVjdE11bHRpUHJvcHM8VD4pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBzdGF0ZSA9IHVzZU11bHRpU2VsZWN0U3RhdGU8VD4oe1xuICAgIGlzRGlzYWJsZWQsXG4gICAgdmlzaWJsZU9wdGlvbkNvdW50LFxuICAgIG9wdGlvbnMsXG4gICAgZGVmYXVsdFZhbHVlLFxuICAgIG9uQ2hhbmdlLFxuICAgIG9uQ2FuY2VsLFxuICAgIG9uRm9jdXMsXG4gICAgZm9jdXNWYWx1ZSxcbiAgICBzdWJtaXRCdXR0b25UZXh0LFxuICAgIG9uU3VibWl0LFxuICAgIG9uRG93bkZyb21MYXN0SXRlbSxcbiAgICBvblVwRnJvbUZpcnN0SXRlbSxcbiAgICBpbml0aWFsRm9jdXNMYXN0LFxuICAgIGhpZGVJbmRleGVzLFxuICB9KVxuXG4gIGNvbnN0IG1heEluZGV4V2lkdGggPSBvcHRpb25zLmxlbmd0aC50b1N0cmluZygpLmxlbmd0aFxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAge3N0YXRlLnZpc2libGVPcHRpb25zLm1hcCgob3B0aW9uLCBpbmRleCkgPT4ge1xuICAgICAgICAgIGNvbnN0IGlzT3B0aW9uRm9jdXNlZCA9XG4gICAgICAgICAgICAhaXNEaXNhYmxlZCAmJlxuICAgICAgICAgICAgc3RhdGUuZm9jdXNlZFZhbHVlID09PSBvcHRpb24udmFsdWUgJiZcbiAgICAgICAgICAgICFzdGF0ZS5pc1N1Ym1pdEZvY3VzZWRcbiAgICAgICAgICBjb25zdCBpc1NlbGVjdGVkID0gc3RhdGUuc2VsZWN0ZWRWYWx1ZXMuaW5jbHVkZXMob3B0aW9uLnZhbHVlKVxuXG4gICAgICAgICAgY29uc3QgaXNGaXJzdFZpc2libGVPcHRpb24gPSBvcHRpb24uaW5kZXggPT09IHN0YXRlLnZpc2libGVGcm9tSW5kZXhcbiAgICAgICAgICBjb25zdCBpc0xhc3RWaXNpYmxlT3B0aW9uID0gb3B0aW9uLmluZGV4ID09PSBzdGF0ZS52aXNpYmxlVG9JbmRleCAtIDFcbiAgICAgICAgICBjb25zdCBhcmVNb3JlT3B0aW9uc0JlbG93ID0gc3RhdGUudmlzaWJsZVRvSW5kZXggPCBvcHRpb25zLmxlbmd0aFxuICAgICAgICAgIGNvbnN0IGFyZU1vcmVPcHRpb25zQWJvdmUgPSBzdGF0ZS52aXNpYmxlRnJvbUluZGV4ID4gMFxuXG4gICAgICAgICAgY29uc3QgaSA9IHN0YXRlLnZpc2libGVGcm9tSW5kZXggKyBpbmRleCArIDFcblxuICAgICAgICAgIGlmIChvcHRpb24udHlwZSA9PT0gJ2lucHV0Jykge1xuICAgICAgICAgICAgY29uc3QgaW5wdXRWYWx1ZSA9IHN0YXRlLmlucHV0VmFsdWVzLmdldChvcHRpb24udmFsdWUpIHx8ICcnXG5cbiAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgIDxCb3gga2V5PXtTdHJpbmcob3B0aW9uLnZhbHVlKX0gZ2FwPXsxfT5cbiAgICAgICAgICAgICAgICA8U2VsZWN0SW5wdXRPcHRpb25cbiAgICAgICAgICAgICAgICAgIG9wdGlvbj17b3B0aW9ufVxuICAgICAgICAgICAgICAgICAgaXNGb2N1c2VkPXtpc09wdGlvbkZvY3VzZWR9XG4gICAgICAgICAgICAgICAgICBpc1NlbGVjdGVkPXtcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UgLyogV2Ugc2hvdyBzZWxlY3Rpb24gc3RhdGUgZGlmZmVyZW50bHkgZm9yIG11bHRpLXNlbGVjdCAqL1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgc2hvdWxkU2hvd0Rvd25BcnJvdz17XG4gICAgICAgICAgICAgICAgICAgIGFyZU1vcmVPcHRpb25zQmVsb3cgJiYgaXNMYXN0VmlzaWJsZU9wdGlvblxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgc2hvdWxkU2hvd1VwQXJyb3c9e1xuICAgICAgICAgICAgICAgICAgICBhcmVNb3JlT3B0aW9uc0Fib3ZlICYmIGlzRmlyc3RWaXNpYmxlT3B0aW9uXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBtYXhJbmRleFdpZHRoPXttYXhJbmRleFdpZHRofVxuICAgICAgICAgICAgICAgICAgaW5kZXg9e2l9XG4gICAgICAgICAgICAgICAgICBpbnB1dFZhbHVlPXtpbnB1dFZhbHVlfVxuICAgICAgICAgICAgICAgICAgb25JbnB1dENoYW5nZT17dmFsdWUgPT4ge1xuICAgICAgICAgICAgICAgICAgICBzdGF0ZS51cGRhdGVJbnB1dFZhbHVlKG9wdGlvbi52YWx1ZSwgdmFsdWUpXG4gICAgICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgICAgICAgb25TdWJtaXQ9eygpID0+IHt9fSAvKiBXZSBoYW5kbGUgc3VibWl0IGhpZ2hlciB1cCAqL1xuICAgICAgICAgICAgICAgICAgb25FeGl0PXsoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIG9uQ2FuY2VsKClcbiAgICAgICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAgICAgICBsYXlvdXQ9XCJjb21wYWN0XCJcbiAgICAgICAgICAgICAgICAgIG9uT3BlbkVkaXRvcj17b25PcGVuRWRpdG9yfVxuICAgICAgICAgICAgICAgICAgb25JbWFnZVBhc3RlPXtvbkltYWdlUGFzdGV9XG4gICAgICAgICAgICAgICAgICBwYXN0ZWRDb250ZW50cz17cGFzdGVkQ29udGVudHN9XG4gICAgICAgICAgICAgICAgICBvblJlbW92ZUltYWdlPXtvblJlbW92ZUltYWdlfVxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPXtpc1NlbGVjdGVkID8gJ3N1Y2Nlc3MnIDogdW5kZWZpbmVkfT5cbiAgICAgICAgICAgICAgICAgICAgW3tpc1NlbGVjdGVkID8gZmlndXJlcy50aWNrIDogJyAnfV17JyAnfVxuICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDwvU2VsZWN0SW5wdXRPcHRpb24+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8Qm94IGtleT17U3RyaW5nKG9wdGlvbi52YWx1ZSl9IGdhcD17MX0+XG4gICAgICAgICAgICAgIDxTZWxlY3RPcHRpb25cbiAgICAgICAgICAgICAgICBpc0ZvY3VzZWQ9e2lzT3B0aW9uRm9jdXNlZH1cbiAgICAgICAgICAgICAgICBpc1NlbGVjdGVkPXtcbiAgICAgICAgICAgICAgICAgIGZhbHNlIC8qIFdlIHNob3cgc2VsZWN0aW9uIHN0YXRlIGRpZmZlcmVudGx5IGZvciBtdWx0aS1zZWxlY3QgKi9cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgc2hvdWxkU2hvd0Rvd25BcnJvdz17YXJlTW9yZU9wdGlvbnNCZWxvdyAmJiBpc0xhc3RWaXNpYmxlT3B0aW9ufVxuICAgICAgICAgICAgICAgIHNob3VsZFNob3dVcEFycm93PXthcmVNb3JlT3B0aW9uc0Fib3ZlICYmIGlzRmlyc3RWaXNpYmxlT3B0aW9ufVxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uPXtvcHRpb24uZGVzY3JpcHRpb259XG4gICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICB7IWhpZGVJbmRleGVzICYmIChcbiAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntgJHtpfS5gLnBhZEVuZChtYXhJbmRleFdpZHRoKX08L1RleHQ+XG4gICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17aXNTZWxlY3RlZCA/ICdzdWNjZXNzJyA6IHVuZGVmaW5lZH0+XG4gICAgICAgICAgICAgICAgICBbe2lzU2VsZWN0ZWQgPyBmaWd1cmVzLnRpY2sgOiAnICd9XVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17aXNPcHRpb25Gb2N1c2VkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfT5cbiAgICAgICAgICAgICAgICAgIHtvcHRpb24ubGFiZWx9XG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICA8L1NlbGVjdE9wdGlvbj5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgIClcbiAgICAgICAgfSl9XG4gICAgICA8L0JveD5cbiAgICAgIHtzdWJtaXRCdXR0b25UZXh0ICYmIG9uU3VibWl0ICYmIChcbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezB9IGdhcD17MX0+XG4gICAgICAgICAge3N0YXRlLmlzU3VibWl0Rm9jdXNlZCA/IChcbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPntmaWd1cmVzLnBvaW50ZXJ9PC9UZXh0PlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICA8VGV4dD4gPC9UZXh0PlxuICAgICAgICAgICl9XG4gICAgICAgICAgPEJveCBtYXJnaW5MZWZ0PXszfT5cbiAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgIGNvbG9yPXtzdGF0ZS5pc1N1Ym1pdEZvY3VzZWQgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICAgIGJvbGQ9e3RydWV9XG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgIHtzdWJtaXRCdXR0b25UZXh0fVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLE9BQU8sTUFBTSxTQUFTO0FBQzdCLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsY0FBY0MsYUFBYSxRQUFRLHVCQUF1QjtBQUMxRCxjQUFjQyxlQUFlLFFBQVEsNkJBQTZCO0FBQ2xFLGNBQWNDLHFCQUFxQixRQUFRLGFBQWE7QUFDeEQsU0FBU0MsaUJBQWlCLFFBQVEsMEJBQTBCO0FBQzVELFNBQVNDLFlBQVksUUFBUSxvQkFBb0I7QUFDakQsU0FBU0MsbUJBQW1CLFFBQVEsNkJBQTZCO0FBRWpFLE9BQU8sS0FBS0MsZ0JBQWdCLENBQUMsQ0FBQyxDQUFDLEdBQUc7RUFDaEMsU0FBU0MsVUFBVSxDQUFDLEVBQUUsT0FBTztFQUM3QixTQUFTQyxrQkFBa0IsQ0FBQyxFQUFFLE1BQU07RUFDcEMsU0FBU0MsT0FBTyxFQUFFUCxxQkFBcUIsQ0FBQ1EsQ0FBQyxDQUFDLEVBQUU7RUFDNUMsU0FBU0MsWUFBWSxDQUFDLEVBQUVELENBQUMsRUFBRTtFQUMzQixTQUFTRSxRQUFRLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDN0IsU0FBU0MsUUFBUSxDQUFDLEVBQUUsQ0FBQ0MsTUFBTSxFQUFFSixDQUFDLEVBQUUsRUFBRSxHQUFHLElBQUk7RUFDekMsU0FBU0ssT0FBTyxDQUFDLEVBQUUsQ0FBQ0MsS0FBSyxFQUFFTixDQUFDLEVBQUUsR0FBRyxJQUFJO0VBQ3JDLFNBQVNPLFVBQVUsQ0FBQyxFQUFFUCxDQUFDO0VBQ3ZCO0FBQ0Y7QUFDQTtB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 \ No newline at end of file diff --git a/components/CustomSelect/index.ts b/components/CustomSelect/index.ts new file mode 100644 index 0000000..fee30a5 --- /dev/null +++ b/components/CustomSelect/index.ts @@ -0,0 +1,3 @@ +export * from './SelectMulti.js' +export type { OptionWithDescription } from './select.js' +export * from './select.js' diff --git a/components/CustomSelect/option-map.ts b/components/CustomSelect/option-map.ts new file mode 100644 index 0000000..ef51c5b --- /dev/null +++ b/components/CustomSelect/option-map.ts @@ -0,0 +1,50 @@ +import type { ReactNode } from 'react' +import type { OptionWithDescription } from './select.js' + +type OptionMapItem = { + label: ReactNode + value: T + description?: string + previous: OptionMapItem | undefined + next: OptionMapItem | undefined + index: number +} + +export default class OptionMap extends Map> { + readonly first: OptionMapItem | undefined + readonly last: OptionMapItem | undefined + + constructor(options: OptionWithDescription[]) { + const items: Array<[T, OptionMapItem]> = [] + let firstItem: OptionMapItem | undefined + let lastItem: OptionMapItem | undefined + let previous: OptionMapItem | undefined + let index = 0 + + for (const option of options) { + const item = { + label: option.label, + value: option.value, + description: option.description, + previous, + next: undefined, + index, + } + + if (previous) { + previous.next = item + } + + firstItem ||= item + lastItem = item + + items.push([option.value, item]) + index++ + previous = item + } + + super(items) + this.first = firstItem + this.last = lastItem + } +} diff --git a/components/CustomSelect/select-input-option.tsx b/components/CustomSelect/select-input-option.tsx new file mode 100644 index 0000000..51e60ce --- /dev/null +++ b/components/CustomSelect/select-input-option.tsx @@ -0,0 +1,488 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode, useEffect, useRef, useState } from 'react'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- UP arrow exit not in Attachments bindings +import { Box, Text, useInput } from '../../ink.js'; +import { useKeybinding, useKeybindings } from '../../keybindings/useKeybinding.js'; +import type { PastedContent } from '../../utils/config.js'; +import { getImageFromClipboard } from '../../utils/imagePaste.js'; +import type { ImageDimensions } from '../../utils/imageResizer.js'; +import { ClickableImageRef } from '../ClickableImageRef.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { Byline } from '../design-system/Byline.js'; +import TextInput from '../TextInput.js'; +import type { OptionWithDescription } from './select.js'; +import { SelectOption } from './select-option.js'; +type Props = { + option: Extract, { + type: 'input'; + }>; + isFocused: boolean; + isSelected: boolean; + shouldShowDownArrow: boolean; + shouldShowUpArrow: boolean; + maxIndexWidth: number; + index: number; + inputValue: string; + onInputChange: (value: string) => void; + onSubmit: (value: string) => void; + onExit?: () => void; + layout: 'compact' | 'expanded'; + children?: ReactNode; + /** + * When true, shows the label before the input field. + * When false (default), uses the label as the placeholder. + */ + showLabel?: boolean; + /** + * Callback to open external editor for editing the input value. + * When provided, ctrl+g will trigger this callback with the current value + * and a setter function to update the internal state. + */ + onOpenEditor?: (currentValue: string, setValue: (value: string) => void) => void; + /** + * When true, automatically reset cursor to end of line when: + * - Option becomes focused + * - Input value changes + * This prevents cursor position bugs when the input value updates asynchronously. + */ + resetCursorOnUpdate?: boolean; + /** + * Optional callback when an image is pasted into the input. + */ + onImagePaste?: (base64Image: string, mediaType?: string, filename?: string, dimensions?: ImageDimensions, sourcePath?: string) => void; + /** + * Pasted content to display inline above the input when focused. + */ + pastedContents?: Record; + /** + * Callback to remove a pasted image by its ID. + */ + onRemoveImage?: (id: number) => void; + /** + * Whether image selection mode is active. + */ + imagesSelected?: boolean; + /** + * Currently selected image index within the image attachments array. + */ + selectedImageIndex?: number; + /** + * Callback to set image selection mode on/off. + */ + onImagesSelectedChange?: (selected: boolean) => void; + /** + * Callback to change the selected image index. + */ + onSelectedImageIndexChange?: (index: number) => void; +}; +export function SelectInputOption(t0) { + const $ = _c(100); + const { + option, + isFocused, + isSelected, + shouldShowDownArrow, + shouldShowUpArrow, + maxIndexWidth, + index, + inputValue, + onInputChange, + onSubmit, + onExit, + layout, + children, + showLabel: t1, + onOpenEditor, + resetCursorOnUpdate: t2, + onImagePaste, + pastedContents, + onRemoveImage, + imagesSelected, + selectedImageIndex: t3, + onImagesSelectedChange, + onSelectedImageIndexChange + } = t0; + const showLabelProp = t1 === undefined ? false : t1; + const resetCursorOnUpdate = t2 === undefined ? false : t2; + const selectedImageIndex = t3 === undefined ? 0 : t3; + let t4; + if ($[0] !== pastedContents) { + t4 = pastedContents ? Object.values(pastedContents).filter(_temp) : []; + $[0] = pastedContents; + $[1] = t4; + } else { + t4 = $[1]; + } + const imageAttachments = t4; + const showLabel = showLabelProp || option.showLabelWithValue === true; + const [cursorOffset, setCursorOffset] = useState(inputValue.length); + const isUserEditing = useRef(false); + let t5; + if ($[2] !== inputValue.length || $[3] !== isFocused || $[4] !== resetCursorOnUpdate) { + t5 = () => { + if (resetCursorOnUpdate && isFocused) { + if (isUserEditing.current) { + isUserEditing.current = false; + } else { + setCursorOffset(inputValue.length); + } + } + }; + $[2] = inputValue.length; + $[3] = isFocused; + $[4] = resetCursorOnUpdate; + $[5] = t5; + } else { + t5 = $[5]; + } + let t6; + if ($[6] !== inputValue || $[7] !== isFocused || $[8] !== resetCursorOnUpdate) { + t6 = [resetCursorOnUpdate, isFocused, inputValue]; + $[6] = inputValue; + $[7] = isFocused; + $[8] = resetCursorOnUpdate; + $[9] = t6; + } else { + t6 = $[9]; + } + useEffect(t5, t6); + let t7; + if ($[10] !== inputValue || $[11] !== onInputChange || $[12] !== onOpenEditor) { + t7 = () => { + onOpenEditor?.(inputValue, onInputChange); + }; + $[10] = inputValue; + $[11] = onInputChange; + $[12] = onOpenEditor; + $[13] = t7; + } else { + t7 = $[13]; + } + const t8 = isFocused && !!onOpenEditor; + let t9; + if ($[14] !== t8) { + t9 = { + context: "Chat", + isActive: t8 + }; + $[14] = t8; + $[15] = t9; + } else { + t9 = $[15]; + } + useKeybinding("chat:externalEditor", t7, t9); + let t10; + if ($[16] !== onImagePaste) { + t10 = () => { + if (!onImagePaste) { + return; + } + getImageFromClipboard().then(imageData => { + if (imageData) { + onImagePaste(imageData.base64, imageData.mediaType, undefined, imageData.dimensions); + } + }); + }; + $[16] = onImagePaste; + $[17] = t10; + } else { + t10 = $[17]; + } + const t11 = isFocused && !!onImagePaste; + let t12; + if ($[18] !== t11) { + t12 = { + context: "Chat", + isActive: t11 + }; + $[18] = t11; + $[19] = t12; + } else { + t12 = $[19]; + } + useKeybinding("chat:imagePaste", t10, t12); + let t13; + if ($[20] !== imageAttachments || $[21] !== onRemoveImage) { + t13 = () => { + if (imageAttachments.length > 0 && onRemoveImage) { + onRemoveImage(imageAttachments.at(-1).id); + } + }; + $[20] = imageAttachments; + $[21] = onRemoveImage; + $[22] = t13; + } else { + t13 = $[22]; + } + const t14 = isFocused && !imagesSelected && inputValue === "" && imageAttachments.length > 0 && !!onRemoveImage; + let t15; + if ($[23] !== t14) { + t15 = { + context: "Attachments", + isActive: t14 + }; + $[23] = t14; + $[24] = t15; + } else { + t15 = $[24]; + } + useKeybinding("attachments:remove", t13, t15); + let t16; + let t17; + if ($[25] !== imageAttachments.length || $[26] !== onSelectedImageIndexChange || $[27] !== selectedImageIndex) { + t16 = () => { + if (imageAttachments.length > 1) { + onSelectedImageIndexChange?.((selectedImageIndex + 1) % imageAttachments.length); + } + }; + t17 = () => { + if (imageAttachments.length > 1) { + onSelectedImageIndexChange?.((selectedImageIndex - 1 + imageAttachments.length) % imageAttachments.length); + } + }; + $[25] = imageAttachments.length; + $[26] = onSelectedImageIndexChange; + $[27] = selectedImageIndex; + $[28] = t16; + $[29] = t17; + } else { + t16 = $[28]; + t17 = $[29]; + } + let t18; + if ($[30] !== imageAttachments || $[31] !== onImagesSelectedChange || $[32] !== onRemoveImage || $[33] !== onSelectedImageIndexChange || $[34] !== selectedImageIndex) { + t18 = () => { + const img = imageAttachments[selectedImageIndex]; + if (img && onRemoveImage) { + onRemoveImage(img.id); + if (imageAttachments.length <= 1) { + onImagesSelectedChange?.(false); + } else { + onSelectedImageIndexChange?.(Math.min(selectedImageIndex, imageAttachments.length - 2)); + } + } + }; + $[30] = imageAttachments; + $[31] = onImagesSelectedChange; + $[32] = onRemoveImage; + $[33] = onSelectedImageIndexChange; + $[34] = selectedImageIndex; + $[35] = t18; + } else { + t18 = $[35]; + } + let t19; + if ($[36] !== onImagesSelectedChange) { + t19 = () => { + onImagesSelectedChange?.(false); + }; + $[36] = onImagesSelectedChange; + $[37] = t19; + } else { + t19 = $[37]; + } + let t20; + if ($[38] !== t16 || $[39] !== t17 || $[40] !== t18 || $[41] !== t19) { + t20 = { + "attachments:next": t16, + "attachments:previous": t17, + "attachments:remove": t18, + "attachments:exit": t19 + }; + $[38] = t16; + $[39] = t17; + $[40] = t18; + $[41] = t19; + $[42] = t20; + } else { + t20 = $[42]; + } + const t21 = isFocused && !!imagesSelected; + let t22; + if ($[43] !== t21) { + t22 = { + context: "Attachments", + isActive: t21 + }; + $[43] = t21; + $[44] = t22; + } else { + t22 = $[44]; + } + useKeybindings(t20, t22); + let t23; + if ($[45] !== onImagesSelectedChange) { + t23 = (_input, key) => { + if (key.upArrow) { + onImagesSelectedChange?.(false); + } + }; + $[45] = onImagesSelectedChange; + $[46] = t23; + } else { + t23 = $[46]; + } + const t24 = isFocused && !!imagesSelected; + let t25; + if ($[47] !== t24) { + t25 = { + isActive: t24 + }; + $[47] = t24; + $[48] = t25; + } else { + t25 = $[48]; + } + useInput(t23, t25); + let t26; + let t27; + if ($[49] !== imagesSelected || $[50] !== isFocused || $[51] !== onImagesSelectedChange) { + t26 = () => { + if (!isFocused && imagesSelected) { + onImagesSelectedChange?.(false); + } + }; + t27 = [isFocused, imagesSelected, onImagesSelectedChange]; + $[49] = imagesSelected; + $[50] = isFocused; + $[51] = onImagesSelectedChange; + $[52] = t26; + $[53] = t27; + } else { + t26 = $[52]; + t27 = $[53]; + } + useEffect(t26, t27); + const descriptionPaddingLeft = layout === "expanded" ? maxIndexWidth + 3 : maxIndexWidth + 4; + const t28 = layout === "compact" ? 0 : undefined; + const t29 = `${index}.`; + let t30; + if ($[54] !== maxIndexWidth || $[55] !== t29) { + t30 = t29.padEnd(maxIndexWidth + 2); + $[54] = maxIndexWidth; + $[55] = t29; + $[56] = t30; + } else { + t30 = $[56]; + } + let t31; + if ($[57] !== t30) { + t31 = {t30}; + $[57] = t30; + $[58] = t31; + } else { + t31 = $[58]; + } + let t32; + if ($[59] !== cursorOffset || $[60] !== imagesSelected || $[61] !== inputValue || $[62] !== isFocused || $[63] !== onExit || $[64] !== onImagePaste || $[65] !== onInputChange || $[66] !== onSubmit || $[67] !== option || $[68] !== showLabel) { + t32 = showLabel ? <>{option.label}{isFocused ? <>{option.labelValueSeparator ?? ", "} { + isUserEditing.current = true; + onInputChange(value); + option.onChange(value); + }} onSubmit={onSubmit} onExit={onExit} placeholder={option.placeholder} focus={!imagesSelected} showCursor={true} multiline={true} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} columns={80} onImagePaste={onImagePaste} onPaste={pastedText => { + isUserEditing.current = true; + const before = inputValue.slice(0, cursorOffset); + const after = inputValue.slice(cursorOffset); + const newValue = before + pastedText + after; + onInputChange(newValue); + option.onChange(newValue); + setCursorOffset(before.length + pastedText.length); + }} /> : inputValue && {option.labelValueSeparator ?? ", "}{inputValue}} : isFocused ? { + isUserEditing.current = true; + onInputChange(value_0); + option.onChange(value_0); + }} onSubmit={onSubmit} onExit={onExit} placeholder={option.placeholder || (typeof option.label === "string" ? option.label : undefined)} focus={!imagesSelected} showCursor={true} multiline={true} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} columns={80} onImagePaste={onImagePaste} onPaste={pastedText_0 => { + isUserEditing.current = true; + const before_0 = inputValue.slice(0, cursorOffset); + const after_0 = inputValue.slice(cursorOffset); + const newValue_0 = before_0 + pastedText_0 + after_0; + onInputChange(newValue_0); + option.onChange(newValue_0); + setCursorOffset(before_0.length + pastedText_0.length); + }} /> : {inputValue || option.placeholder || option.label}; + $[59] = cursorOffset; + $[60] = imagesSelected; + $[61] = inputValue; + $[62] = isFocused; + $[63] = onExit; + $[64] = onImagePaste; + $[65] = onInputChange; + $[66] = onSubmit; + $[67] = option; + $[68] = showLabel; + $[69] = t32; + } else { + t32 = $[69]; + } + let t33; + if ($[70] !== children || $[71] !== t28 || $[72] !== t31 || $[73] !== t32) { + t33 = {t31}{children}{t32}; + $[70] = children; + $[71] = t28; + $[72] = t31; + $[73] = t32; + $[74] = t33; + } else { + t33 = $[74]; + } + let t34; + if ($[75] !== isFocused || $[76] !== isSelected || $[77] !== shouldShowDownArrow || $[78] !== shouldShowUpArrow || $[79] !== t33) { + t34 = {t33}; + $[75] = isFocused; + $[76] = isSelected; + $[77] = shouldShowDownArrow; + $[78] = shouldShowUpArrow; + $[79] = t33; + $[80] = t34; + } else { + t34 = $[80]; + } + let t35; + if ($[81] !== descriptionPaddingLeft || $[82] !== isFocused || $[83] !== isSelected || $[84] !== option.description || $[85] !== option.dimDescription) { + t35 = option.description && {option.description}; + $[81] = descriptionPaddingLeft; + $[82] = isFocused; + $[83] = isSelected; + $[84] = option.description; + $[85] = option.dimDescription; + $[86] = t35; + } else { + t35 = $[86]; + } + let t36; + if ($[87] !== descriptionPaddingLeft || $[88] !== imageAttachments || $[89] !== imagesSelected || $[90] !== isFocused || $[91] !== selectedImageIndex) { + t36 = imageAttachments.length > 0 && {imageAttachments.map((img_0, idx) => )}{imagesSelected ? {imageAttachments.length > 1 && <>} : isFocused ? "(\u2193 to select)" : null}; + $[87] = descriptionPaddingLeft; + $[88] = imageAttachments; + $[89] = imagesSelected; + $[90] = isFocused; + $[91] = selectedImageIndex; + $[92] = t36; + } else { + t36 = $[92]; + } + let t37; + if ($[93] !== layout) { + t37 = layout === "expanded" && ; + $[93] = layout; + $[94] = t37; + } else { + t37 = $[94]; + } + let t38; + if ($[95] !== t34 || $[96] !== t35 || $[97] !== t36 || $[98] !== t37) { + t38 = {t34}{t35}{t36}{t37}; + $[95] = t34; + $[96] = t35; + $[97] = t36; + $[98] = t37; + $[99] = t38; + } else { + t38 = $[99]; + } + return t38; +} +function _temp(c) { + return c.type === "image"; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZVN0YXRlIiwiQm94IiwiVGV4dCIsInVzZUlucHV0IiwidXNlS2V5YmluZGluZyIsInVzZUtleWJpbmRpbmdzIiwiUGFzdGVkQ29udGVudCIsImdldEltYWdlRnJvbUNsaXBib2FyZCIsIkltYWdlRGltZW5zaW9ucyIsIkNsaWNrYWJsZUltYWdlUmVmIiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiQnlsaW5lIiwiVGV4dElucHV0IiwiT3B0aW9uV2l0aERlc2NyaXB0aW9uIiwiU2VsZWN0T3B0aW9uIiwiUHJvcHMiLCJvcHRpb24iLCJFeHRyYWN0IiwiVCIsInR5cGUiLCJpc0ZvY3VzZWQiLCJpc1NlbGVjdGVkIiwic2hvdWxkU2hvd0Rvd25BcnJvdyIsInNob3VsZFNob3dVcEFycm93IiwibWF4SW5kZXhXaWR0aCIsImluZGV4IiwiaW5wdXRWYWx1ZSIsIm9uSW5wdXRDaGFuZ2UiLCJ2YWx1ZSIsIm9uU3VibWl0Iiwib25FeGl0IiwibGF5b3V0IiwiY2hpbGRyZW4iLCJzaG93TGFiZWwiLCJvbk9wZW5FZGl0b3IiLCJjdXJyZW50VmFsdWUiLCJzZXRWYWx1ZSIsInJlc2V0Q3Vyc29yT25VcGRhdGUiLCJvbkltYWdlUGFzdGUiLCJiYXNlNjRJbWFnZSIsIm1lZGlhVHlwZSIsImZpbGVuYW1lIiwiZGltZW5zaW9ucyIsInNvdXJjZVBhdGgiLCJwYXN0ZWRDb250ZW50cyIsIlJlY29yZCIsIm9uUmVtb3ZlSW1hZ2UiLCJpZCIsImltYWdlc1NlbGVjdGVkIiwic2VsZWN0ZWRJbWFnZUluZGV4Iiwib25JbWFnZXNTZWxlY3RlZENoYW5nZSIsInNlbGVjdGVkIiwib25TZWxlY3RlZEltYWdlSW5kZXhDaGFuZ2UiLCJTZWxlY3RJbnB1dE9wdGlvbiIsInQwIiwiJCIsIl9jIiwidDEiLCJ0MiIsInQzIiwic2hvd0xhYmVsUHJvcCIsInVuZGVmaW5lZCIsInQ0IiwiT2JqZWN0IiwidmFsdWVzIiwiZmlsdGVyIiwiX3RlbXAiLCJpbWFnZUF0dGFjaG1lbnRzIiwic2hvd0xhYmVsV2l0aFZhbHVlIiwiY3Vyc29yT2Zmc2V0Iiwic2V0Q3Vyc29yT2Zmc2V0IiwibGVuZ3RoIiwiaXNVc2VyRWRpdGluZyIsInQ1IiwiY3VycmVudCIsInQ2IiwidDciLCJ0OCIsInQ5IiwiY29udGV4dCIsImlzQWN0aXZlIiwidDEwIiwidGhlbiIsImltYWdlRGF0YSIsImJhc2U2NCIsInQxMSIsInQxMiIsInQxMyIsImF0IiwidDE0IiwidDE1IiwidDE2IiwidDE3IiwidDE4IiwiaW1nIiwiTWF0aCIsIm1pbiIsInQxOSIsInQyMCIsInQyMSIsInQyMiIsInQyMyIsIl9pbnB1dCIsImtleSIsInVwQXJyb3ciLCJ0MjQiLCJ0MjUiLCJ0MjYiLCJ0MjciLCJkZXNjcmlwdGlvblBhZGRpbmdMZWZ0IiwidDI4IiwidDI5IiwidDMwIiwicGFkRW5kIiwidDMxIiwidDMyIiwibGFiZWwiLCJsYWJlbFZhbHVlU2VwYXJhdG9yIiwib25DaGFuZ2UiLCJwbGFjZWhvbGRlciIsInBhc3RlZFRleHQiLCJiZWZvcmUiLCJzbGljZSIsImFmdGVyIiwibmV3VmFsdWUiLCJ2YWx1ZV8wIiwicGFzdGVkVGV4dF8wIiwiYmVmb3JlXzAiLCJhZnRlcl8wIiwibmV3VmFsdWVfMCIsInQzMyIsInQzNCIsInQzNSIsImRlc2NyaXB0aW9uIiwiZGltRGVzY3JpcHRpb24iLCJ0MzYiLCJtYXAiLCJpbWdfMCIsImlkeCIsInQzNyIsInQzOCIsImMiXSwic291cmNlcyI6WyJzZWxlY3QtaW5wdXQtb3B0aW9uLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdHlwZSBSZWFjdE5vZGUsIHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9wcmVmZXItdXNlLWtleWJpbmRpbmdzIC0tIFVQIGFycm93IGV4aXQgbm90IGluIEF0dGFjaG1lbnRzIGJpbmRpbmdzXG5pbXBvcnQgeyBCb3gsIFRleHQsIHVzZUlucHV0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHtcbiAgdXNlS2V5YmluZGluZyxcbiAgdXNlS2V5YmluZGluZ3MsXG59IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgdHlwZSB7IFBhc3RlZENvbnRlbnQgfSBmcm9tICcuLi8uLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgeyBnZXRJbWFnZUZyb21DbGlwYm9hcmQgfSBmcm9tICcuLi8uLi91dGlscy9pbWFnZVBhc3RlLmpzJ1xuaW1wb3J0IHR5cGUgeyBJbWFnZURpbWVuc2lvbnMgfSBmcm9tICcuLi8uLi91dGlscy9pbWFnZVJlc2l6ZXIuanMnXG5pbXBvcnQgeyBDbGlja2FibGVJbWFnZVJlZiB9IGZyb20gJy4uL0NsaWNrYWJsZUltYWdlUmVmLmpzJ1xuaW1wb3J0IHsgQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgVGV4dElucHV0IGZyb20gJy4uL1RleHRJbnB1dC5qcydcbmltcG9ydCB0eXBlIHsgT3B0aW9uV2l0aERlc2NyaXB0aW9uIH0gZnJvbSAnLi9zZWxlY3QuanMnXG5pbXBvcnQgeyBTZWxlY3RPcHRpb24gfSBmcm9tICcuL3NlbGVjdC1vcHRpb24uanMnXG5cbnR5cGUgUHJvcHM8VD4gPSB7XG4gIG9wdGlvbjogRXh0cmFjdDxPcHRpb25XaXRoRGVzY3JpcHRpb248VD4sIHsgdHlwZTogJ2lucHV0JyB9PlxuICBpc0ZvY3VzZWQ6IGJvb2xlYW5cbiAgaXNTZWxlY3RlZDogYm9vbGVhblxuICBzaG91bGRTaG93RG93bkFycm93OiBib29sZWFuXG4gIHNob3VsZFNob3dVcEFycm93OiBib29sZWFuXG4gIG1heEluZGV4V2lkdGg6IG51bWJlclxuICBpbmRleDogbnVtYmVyXG4gIGlucHV0VmFsdWU6IHN0cmluZ1xuICBvbklucHV0Q2hhbmdlOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZFxuICBvblN1Ym1pdDogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWRcbiAgb25FeGl0PzogKCkgPT4gdm9pZFxuICBsYXlvdXQ6ICdjb21wYWN0JyB8ICdleHBhbmRlZCdcbiAgY2hpbGRyZW4/OiBSZWFjdE5vZGVcbiAgLyoqXG4gICAqIFdoZW4gdHJ1ZSwgc2hvd3MgdGhlIGxhYmVsIGJlZm9yZSB0aGUgaW5wdXQgZmllbGQuXG4gICAqIFdoZW4gZmFsc2UgKGRlZmF1bHQpLCB1c2VzIHRoZSBsYWJlbCBhcyB0aGUgcGxhY2Vob2xkZXIuXG4gICAqL1xuICBzaG93TGFiZWw/OiBib29sZWFuXG4gIC8qKlxuICAgKiBDYWxsYmFjayB0byBvcGVuIGV4dGVybmFsIGVkaXRvciBmb3IgZWRpdGluZyB0aGUgaW5wdXQgdmFsdWUuXG4gICAqIFdoZW4gcHJvdmlkZWQsIGN0cmwrZyB3aWxsIHRyaWdnZXIgdGhpcyBjYWxsYmFjayB3aXRoIHRoZSBjdXJyZW50IHZhbHVlXG4gICAqIGFuZCBhIHNldHRlciBmdW5jdGlvbiB0byB1cGRhdGUgdGhlIGludGVybmFsIHN0YXRlLlxuICAgKi9cbiAgb25PcGVuRWRpdG9yPzogKFxuICAgIGN1cnJlbnRWYWx1ZTogc3RyaW5nLFxuICAgIHNldFZhbHVlOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZCxcbiAgKSA9PiB2b2lkXG4gIC8qKlxuICAgKiBXaGVuIHRydWUsIGF1dG9tYXRpY2FsbHkgcmVzZXQgY3Vyc29yIHRvIGVuZCBvZiBsaW5lIHdoZW46XG4gICAqIC0gT3B0aW9uIGJlY29tZXMgZm9jdXNlZFxuICAgKiAtIElucHV0IHZhbHVlIGNoYW5nZXNcbiAgICogVGhpcyBwcmV2ZW50cyBjdXJzb3IgcG9zaXRpb24gYnVncyB3aGVuIHRoZSBpbnB1dCB2YWx1ZSB1cGRhdGVzIGFzeW5jaHJvbm91c2x5LlxuICAgKi9cbiAgcmVzZXRDdXJzb3JPblVwZGF0ZT86IGJvb2xlYW5cbiAgLyoqXG4gICAqIE9wdGlvbmFsIGNhbGxiYWNrIHdoZW4gYW4gaW1hZ2UgaXMgcGFzdGVkIGludG8gdGhlIGlucHV0LlxuICAgKi9cbiAgb25JbWFnZVBhc3RlPzogKFxuICAgIGJhc2U2NEltYWdlOiBzdHJpbmcsXG4gICAgbWVkaWFUeXBlPzogc3RyaW5nLFxuICAgIGZpbGVuYW1lPzogc3RyaW5nLFxuICAgIGRpbWVuc2lvbnM/OiBJbWFnZURpbWVuc2lvbnMsXG4gICAgc291cmNlUGF0aD86IHN0cmluZyxcbiAgKSA9PiB2b2lkXG4gIC8qKlxuICAgKiBQYXN0ZWQgY29udGVudCB0byBkaXNwbGF5IGlubGluZSBhYm92ZSB0aGUgaW5wdXQgd2hlbiBmb2N1c2VkLlxuICAgKi9cbiAgcGFzdGVkQ29udGVudHM/OiBSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50PlxuICAvKipcbiAgICogQ2FsbGJhY2sgdG8gcmVtb3ZlIGEgcGFzdGVkIGltYWdlIGJ5IGl0cyBJRC5cbiAgICovXG4gIG9uUmVtb3ZlSW1hZ2U/OiAoaWQ6IG51bWJlcikgPT4gdm9pZFxuICAvKipcbiAgICogV2hldGhlciBpbWFnZSBzZWxlY3Rpb24gbW9kZSBpcyBhY3RpdmUuXG4gICAqL1xuICBpbWFnZXNTZWxlY3RlZD86IGJvb2xlYW5cbiAgLyoqXG4gICAqIEN1cnJlbnRseSBzZWxlY3RlZCBpbWFnZSBpbmRleCB3aXRoaW4gdGhlIGltYWdlIGF0dGFjaG1lbnRzIGFycmF5LlxuICAgKi9cbiAgc2VsZWN0ZWRJbWFnZUluZGV4PzogbnVtYmVyXG4gIC8qKlxuICAgKiBDYWxsYmFjayB0byBzZXQgaW1hZ2Ugc2VsZWN0aW9uIG1vZGUgb24vb2ZmLlxuICAgKi9cbiAgb25JbWFnZXNTZWxlY3RlZENoYW5nZT86IChzZWxlY3RlZDogYm9vbGVhbikgPT4gdm9pZFxuICAvKipcbiAgICogQ2FsbGJhY2sgdG8gY2hhbmdlIHRoZSBzZWxlY3RlZCBpbWFnZSBpbmRleC5cbiAgICovXG4gIG9uU2VsZWN0ZWRJbWFnZUluZGV4Q2hhbmdlPzogKGluZGV4OiBudW1iZXIpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFNlbGVjdElucHV0T3B0aW9uPFQ+KHtcbiAgb3B0aW9uLFxuICBpc0ZvY3VzZWQsXG4gIGlzU2VsZWN0ZWQsXG4gIHNob3VsZFNob3dEb3duQXJyb3csXG4gIHNob3VsZFNob3dVcEFycm93LFxuICBtYXhJbmRleFdpZHRoLFxuICBpbmRleCxcbiAgaW5wdXRWYWx1ZSxcbiAgb25JbnB1dENoYW5nZSxcbiAgb25TdWJtaXQsXG4gIG9uRXhpdCxcbiAgbGF5b3V0LFxuICBjaGlsZHJlbixcbiAgc2hvd0xhYmVsOiBzaG93TGFiZWxQcm9wID0gZmFsc2UsXG4gIG9uT3BlbkVkaXRvcixcbiAgcmVzZXRDdXJzb3JPblVwZGF0ZSA9IGZhbHNlLFxuICBvbkltYWdlUGFzdGUsXG4gIHBhc3RlZENvbnRlbnRzLFxuICBvblJlbW92ZUltYWdlLFxuICBpbWFnZXNTZWxlY3RlZCxcbiAgc2VsZWN0ZWRJbWFnZUluZGV4ID0gMCxcbiAgb25JbWFnZXNTZWxlY3RlZENoYW5nZSxcbiAgb25TZWxlY3RlZEltYWdlSW5kZXhDaGFuZ2UsXG59OiBQcm9wczxUPik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGltYWdlQXR0YWNobWVudHMgPSBwYXN0ZWRDb250ZW50c1xuICAgID8gT2JqZWN0LnZhbHVlcyhwYXN0ZWRDb250ZW50cykuZmlsdGVyKGMgPT4gYy50eXBlID09PSAnaW1hZ2UnKVxuICAgIDogW11cblxuICAvLyBBbGxvdyBpbmRpdmlkdWFsIG9wdGlvbnMgdG8gZm9yY2Ugc2hvd2luZyB0aGUgbGFiZWwgdmlhIHNob3dMYWJlbFdpdGhWYWx1ZVxuICBjb25zdCBzaG93TGFiZWwgPSBzaG93TGFiZWxQcm9wIHx8IG9wdGlvbi5zaG93TGFiZWxXaXRoVmFsdWUgPT09IHRydWVcbiAgY29uc3QgW2N1cnNvck9mZnNldCwgc2V0Q3Vyc29yT2Zmc2V0XSA9IHVzZVN0YXRlKGlucHV0VmFsdWUubGVuZ3RoKVxuXG4gIC8vIFRyYWNrIHdoZXRoZXIgdGhlIGxhdGVzdCBpbnB1dFZhbHVlIGNoYW5nZSB3YXMgZnJvbSB1c2VyIHR5cGluZy9wYXN0aW5nLFxuICAvLyBzbyB3ZSBjYW4gc2tpcCByZXNldHRpbmcgY3Vyc29yIHRvIGVuZCBvbiB1c2VyLWluaXRpYXRlZCBjaGFuZ2VzLlxuICBjb25zdCBpc1VzZXJFZGl0aW5nID0gdXNlUmVmKGZhbHNlKVxuXG4gIC8vIFJlc2V0IGN1cnNvciB0byBlbmQgb2YgbGluZSB3aGVuOlxuICAvLyAxLiBPcHRpb24gYmVjb21lcyBmb2N1c2VkICh1c2VyIG5hdmlnYXRlcyB0byBpdClcbiAgLy8gMi4gSW5wdXQgdmFsdWUgY2hhbmdlcyBleHRlcm5hbGx5IChlLmcuLCBhc3luYyBjbGFzc2lmaWVyIGRlc2NyaXB0aW9uIHVwZGF0ZXMpXG4gIC8vIFNraXAgcmVzZXQgd2hlbiB0aGUgY2hhbmdlIHdhcyBmcm9tIHVzZXIgdHlwaW5nICh3aGljaCBzZXRzIGlzVXNlckVkaXRpbmcgcmVmKVxuICAvLyBPbmx5IGVuYWJsZWQgd2hlbiByZXNldEN1cnNvck9uVXBkYXRlIHByb3AgaXMgdHJ1ZVxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChyZXNldEN1cnNvck9uVXBkYXRlICYmIGlzRm9jdXNlZCkge1xuICAgICAgaWYgKGlzVXNlckVkaXRpbmcuY3VycmVudCkge1xuICAgICAgICBpc1VzZXJFZGl0aW5nLmN1cnJlbnQgPSBmYWxzZVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KGlucHV0VmFsdWUubGVuZ3RoKVxuICAgICAgfVxuICAgIH1cbiAgfSwgW3Jlc2V0Q3Vyc29yT25VcGRhdGUsIGlzRm9jdXNlZCwgaW5wdXRWYWx1ZV0pXG5cbiAgLy8gY3RybCtnIHRvIG9wZW4gZXh0ZXJuYWwgZWRpdG9yIChyZXVzZXMgY2hhdDpleHRlcm5hbEVkaXRvciBrZXliaW5kaW5nKVxuICB1c2VLZXliaW5kaW5nKFxuICAgICdjaGF0OmV4dGVybmFsRWRpdG9yJyxcbiAgICAoKSA9PiB7XG4gICAgICBvbk9wZW5FZGl0b3I/LihpbnB1dFZhbHVlLCBvbklucHV0Q2hhbmdlKVxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnQ2hhdCcsIGlzQWN0aXZlOiBpc0ZvY3VzZWQgJiYgISFvbk9wZW5FZGl0b3IgfSxcbiAgKVxuXG4gIC8vIGN0cmwrdiB0byBwYXN0ZSBpbWFnZSBmcm9tIGNsaXBib2FyZCAoc2FtZSBhcyBQcm9tcHRJbnB1dClcbiAgdXNlS2V5YmluZGluZyhcbiAgICAnY2hhdDppbWFnZVBhc3RlJyxcbiAgICAoKSA9PiB7XG4gICAgICBpZiAoIW9uSW1hZ2VQYXN0ZSkgcmV0dXJuXG4gICAgICB2b2lkIGdldEltYWdlRnJvbUNsaXBib2FyZCgpLnRoZW4oaW1hZ2VEYXRhID0+IHtcbiAgICAgICAgaWYgKGltYWdlRGF0YSkge1xuICAgICAgICAgIG9uSW1hZ2VQYXN0ZShcbiAgICAgICAgICAgIGltYWdlRGF0YS5iYXNlNjQsXG4gICAgICAgICAgICBpbWFnZURhdGEubWVkaWFUeXBlLFxuICAgICAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgICAgICAgaW1hZ2VEYXRhLmRpbWVuc2lvbnMsXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICB9KVxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnQ2hhdCcsIGlzQWN0aXZlOiBpc0ZvY3VzZWQgJiYgISFvbkltYWdlUGFzdGUgfSxcbiAgKVxuXG4gIC8vIEJhY2tzcGFjZSB3aXRoIGVtcHR5IGlucHV0IHJlbW92ZXMgdGhlIGxhc3QgcGFzdGVkIGltYWdlIChub24taW1hZ2Utc2VsZWN0aW9uIG1vZGUpXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2F0dGFjaG1lbnRzOnJlbW92ZScsXG4gICAgKCkgPT4ge1xuICAgICAgaWYgKGltYWdlQXR0YWNobWVudHMubGVuZ3RoID4gMCAmJiBvblJlbW92ZUltYWdlKSB7XG4gICAgICAgIG9uUmVtb3ZlSW1hZ2UoaW1hZ2VBdHRhY2htZW50cy5hdCgtMSkhLmlkKVxuICAgICAgfVxuICAgIH0sXG4gICAge1xuICAgICAgY29udGV4dDogJ0F0dGFjaG1lbnRzJyxcbiAgICAgIGlzQWN0aXZlOlxuICAgICAgICBpc0ZvY3VzZWQgJiZcbiAgICAgICAgIWltYWdlc1NlbGVjdGVkICYmXG4gICAgICAgIGlucHV0VmFsdWUgPT09ICcnICYmXG4gICAgICAgIGltYWdlQXR0YWNobWVudHMubGVuZ3RoID4gMCAmJlxuICAgICAgICAhIW9uUmVtb3ZlSW1hZ2UsXG4gICAgfSxcbiAgKVxuXG4gIC8vIEltYWdlIHNlbGVjdGlvbiBtb2RlIGtleWJpbmRpbmdzIOKAlCByZXVzZXMgZXhpc3RpbmcgQXR0YWNobWVudHMgYWN0aW9uc1xuICB1c2VLZXliaW5kaW5ncyhcbiAgICB7XG4gICAgICAnYXR0YWNobWVudHM6bmV4dCc6ICgpID0+IHtcbiAgICAgICAgaWYgKGltYWdlQXR0YWNobWVudHMubGVuZ3RoID4gMSkge1xuICAgICAgICAgIG9uU2VsZWN0ZWRJbWFnZUluZGV4Q2hhbmdlPy4oXG4gICAgICAgICAgICAoc2VsZWN0ZWRJbWFnZUluZGV4ICsgMSkgJSBpbWFnZUF0dGFjaG1lbnRzLmxlbmd0aCxcbiAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICAnYXR0YWNobWVudHM6cHJldmlvdXMnOiAoKSA9PiB7XG4gICAgICAgIGlmIChpbWFnZUF0dGFjaG1lbnRzLmxlbmd0aCA+IDEpIHtcbiAgICAgICAgICBvblNlbGVjdGVkSW1hZ2VJbmRleENoYW5nZT8uKFxuICAgICAgICAgICAgKHNlbGVjdGVkSW1hZ2VJbmRleCAtIDEgKyBpbWFnZUF0dGFjaG1lbnRzLmxlbmd0aCkgJVxuICAgICAgICAgICAgICBpbWFnZUF0dGFjaG1lbnRzLmxlbmd0aCxcbiAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICAnYXR0YWNobWVudHM6cmVtb3ZlJzogKCkgPT4ge1xuICAgICAgICBjb25zdCBpbWcgPSBpbWFnZUF0dGFjaG1lbnRzW3NlbGVjdGVkSW1hZ2VJbmRleF1cbiAgICAgICAgaWYgKGltZyAmJiBvblJlbW92ZUltYWdlKSB7XG4gICAgICAgICAgb25SZW1vdmVJbWFnZShpbWcuaWQpXG4gICAgICAgICAgLy8gSWYgbm8gaW1hZ2VzIGxlZnQgYWZ0ZXIgcmVtb3ZhbCwgZXhpdCBpbWFnZSBzZWxlY3Rpb25cbiAgICAgICAgICBpZiAoaW1hZ2VBdHRhY2htZW50cy5sZW5ndGggPD0gMSkge1xuICAgICAgICAgICAgb25JbWFnZXNTZWxlY3RlZENoYW5nZT8uKGZhbHNlKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBBZGp1c3QgaW5kZXggaWYgd2UgZGVsZXRlZCB0aGUgbGFzdCBpbWFnZVxuICAgICAgICAgICAgb25TZWxlY3RlZEltYWdlSW5kZXhDaGFuZ2U/LihcbiAgICAgICAgICAgICAgTWF0aC5taW4oc2VsZWN0ZWRJbWFnZUluZGV4LCBpbWFnZUF0dGFjaG1lbnRzLmxlbmd0aCAtIDIpLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgICdhdHRhY2htZW50czpleGl0JzogKCkgPT4ge1xuICAgICAgICBvbkltYWdlc1NlbGVjdGVkQ2hhbmdlPy4oZmFsc2UpXG4gICAgICB9LFxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnQXR0YWNobWVudHMnLCBpc0FjdGl2ZTogaXNGb2N1c2VkICYmICEhaW1hZ2VzU2VsZWN0ZWQgfSxcbiAgKVxuXG4gIC8vIFVQIGFycm93IGV4aXRzIGltYWdlIHNlbGVjdGlvbiBtb2RlIChVUCBpc24ndCBib3VuZCB0byBhdHRhY2htZW50czpleGl0KVxuICB1c2VJbnB1dChcbiAgICAoX2lucHV0LCBrZXkpID0+IHtcbiAgICAgIGlmIChrZXkudXBBcnJvdykge1xuICAgICAgICBvbkltYWdlc1NlbGVjdGVkQ2hhbmdlPy4oZmFsc2UpXG4gICAgICB9XG4gICAgfSxcbiAgICB7IGlzQWN0aXZlOiBpc0ZvY3VzZWQgJiYgISFpbWFnZXNTZWxlY3RlZCB9LFxuICApXG5cbiAgLy8gRXhpdCBpbWFnZSBtb2RlIHdoZW4gb3B0aW9uIGxvc2VzIGZvY3VzXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKCFpc0ZvY3VzZWQgJiYgaW1hZ2VzU2VsZWN0ZWQpIHtcbiAgICAgIG9uSW1hZ2VzU2VsZWN0ZWRDaGFuZ2U/LihmYWxzZSlcbiAgICB9XG4gIH0sIFtpc0ZvY3VzZWQsIGltYWdlc1NlbGVjdGVkLCBvbkltYWdlc1NlbGVjdGVkQ2hhbmdlXSlcblxuICBjb25zdCBkZXNjcmlwdGlvblBhZGRpbmdMZWZ0ID1cbiAgICBsYXlvdXQgPT09ICdleHBhbmRlZCcgPyBtYXhJbmRleFdpZHRoICsgMyA6IG1heEluZGV4V2lkdGggKyA0XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBmbGV4U2hyaW5rPXswfT5cbiAgICAgIDxTZWxlY3RPcHRpb25cbiAgICAgICAgaXNGb2N1c2VkPXtpc0ZvY3VzZWR9XG4gICAgICAgIGlzU2VsZWN0ZWQ9e2lzU2VsZWN0ZWR9XG4gICAgICAgIHNob3VsZFNob3dEb3duQXJyb3c9e3Nob3VsZFNob3dEb3duQXJyb3d9XG4gICAgICAgIHNob3VsZFNob3dVcEFycm93PXtzaG91bGRTaG93VXBBcnJvd31cbiAgICAgICAgZGVjbGFyZUN1cnNvcj17ZmFsc2V9XG4gICAgICA+XG4gICAgICAgIDxCb3hcbiAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwicm93XCJcbiAgICAgICAgICBmbGV4U2hyaW5rPXtsYXlvdXQgPT09ICdjb21wYWN0JyA/IDAgOiB1bmRlZmluZWR9XG4gICAgICAgID5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57YCR7aW5kZXh9LmAucGFkRW5kKG1heEluZGV4V2lkdGggKyAyKX08L1RleHQ+XG4gICAgICAgICAge2NoaWxkcmVufVxuICAgICAgICAgIHtzaG93TGFiZWwgPyAoXG4gICAgICAgICAgICA8PlxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17aXNGb2N1c2VkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfT5cbiAgICAgICAgICAgICAgICB7b3B0aW9uLmxhYmVsfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIHtpc0ZvY3VzZWQgPyAoXG4gICAgICAgICAgICAgICAgPD5cbiAgICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPlxuICAgICAgICAgICAgICAgICAgICB7b3B0aW9uLmxhYmVsVmFsdWVTZXBhcmF0b3IgPz8gJywgJ31cbiAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgIDxUZXh0SW5wdXRcbiAgICAgICAgICAgICAgICAgICAgdmFsdWU9e2lucHV0VmFsdWV9XG4gICAgICAgICAgICAgICAgICAgIG9uQ2hhbmdlPXt2YWx1ZSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgaXNVc2VyRWRpdGluZy5jdXJyZW50ID0gdHJ1ZVxuICAgICAgICAgICAgICAgICAgICAgIG9uSW5wdXRDaGFuZ2UodmFsdWUpXG4gICAgICAgICAgICAgICAgICAgICAgb3B0aW9uLm9uQ2hhbmdlKHZhbHVlKVxuICAgICAgICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgICAgICAgICBvblN1Ym1pdD17b25TdWJtaXR9XG4gICAgICAgICAgICAgICAgICAgIG9uRXhpdD17b25FeGl0fVxuICAgICAgICAgICAgICAgICAgICBwbGFjZWhvbGRlcj17b3B0aW9uLnBsYWNlaG9sZGVyfVxuICAgICAgICAgICAgICAgICAgICBmb2N1cz17IWltYWdlc1NlbGVjdGVkfVxuICAgICAgICAgICAgICAgICAgICBzaG93Q3Vyc29yPXt0cnVlfVxuICAgICAgICAgICAgICAgICAgICBtdWx0aWxpbmU9e3RydWV9XG4gICAgICAgICAgICAgICAgICAgIGN1cnNvck9mZnNldD17Y3Vyc29yT2Zmc2V0fVxuICAgICAgICAgICAgICAgICAgICBvbkNoYW5nZUN1cnNvck9mZnNldD17c2V0Q3Vyc29yT2Zmc2V0fVxuICAgICAgICAgICAgICAgICAgICBjb2x1bW5zPXs4MH1cbiAgICAgICAgICAgICAgICAgICAgb25JbWFnZVBhc3RlPXtvbkltYWdlUGFzdGV9XG4gICAgICAgICAgICAgICAgICAgIG9uUGFzdGU9eyhwYXN0ZWRUZXh0OiBzdHJpbmcpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICBpc1VzZXJFZGl0aW5nLmN1cnJlbnQgPSB0cnVlXG4gICAgICAgICAgICAgICAgICAgICAgY29uc3QgYmVmb3JlID0gaW5wdXRWYWx1ZS5zbGljZSgwLCBjdXJzb3JPZmZzZXQpXG4gICAgICAgICAgICAgICAgICAgICAgY29uc3QgYWZ0ZXIgPSBpbnB1dFZhbHVlLnNsaWNlKGN1cnNvck9mZnNldClcbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCBuZXdWYWx1ZSA9IGJlZm9yZSArIHBhc3RlZFRleHQgKyBhZnRlclxuICAgICAgICAgICAgICAgICAgICAgIG9uSW5wdXRDaGFuZ2UobmV3VmFsdWUpXG4gICAgICAgICAgICAgICAgICAgICAgb3B0aW9uLm9uQ2hhbmdlKG5ld1ZhbHVlKVxuICAgICAgICAgICAgICAgICAgICAgIHNldEN1cnNvck9mZnNldChiZWZvcmUubGVuZ3RoICsgcGFzdGVkVGV4dC5sZW5ndGgpXG4gICAgICAgICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgIDwvPlxuICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgIGlucHV0VmFsdWUgJiYgKFxuICAgICAgICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgICAgICAgIHtvcHRpb24ubGFiZWxWYWx1ZVNlcGFyYXRvciA/PyAnLCAnfVxuICAgICAgICAgICAgICAgICAgICB7aW5wdXRWYWx1ZX1cbiAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICl9XG4gICAgICAgICAgICA8Lz5cbiAgICAgICAgICApIDogaXNGb2N1c2VkID8gKFxuICAgICAgICAgICAgPFRleHRJbnB1dFxuICAgICAgICAgICAgICB2YWx1ZT17aW5wdXRWYWx1ZX1cbiAgICAgICAgICAgICAgb25DaGFuZ2U9e3ZhbHVlID0+IHtcbiAgICAgICAgICAgICAgICBpc1VzZXJFZGl0aW5nLmN1cnJlbnQgPSB0cnVlXG4gICAgICAgICAgICAgICAgb25JbnB1dENoYW5nZSh2YWx1ZSlcbiAgICAgICAgICAgICAgICBvcHRpb24ub25DaGFuZ2UodmFsdWUpXG4gICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAgIG9uU3VibWl0PXtvblN1Ym1pdH1cbiAgICAgICAgICAgICAgb25FeGl0PXtvbkV4aXR9XG4gICAgICAgICAgICAgIHBsYWNlaG9sZGVyPXtcbiAgICAgICAgICAgICAgICBvcHRpb24ucGxhY2Vob2xkZXIgfHxcbiAgICAgICAgICAgICAgICAodHlwZW9mIG9wdGlvbi5sYWJlbCA9PT0gJ3N0cmluZycgPyBvcHRpb24ubGFiZWwgOiB1bmRlZmluZWQpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgZm9jdXM9eyFpbWFnZXNTZWxlY3RlZH1cbiAgICAgICAgICAgICAgc2hvd0N1cnNvcj17dHJ1ZX1cbiAgICAgICAgICAgICAgbXVsdGlsaW5lPXt0cnVlfVxuICAgICAgICAgICAgICBjdXJzb3JPZmZzZXQ9e2N1cnNvck9mZnNldH1cbiAgICAgICAgICAgICAgb25DaGFuZ2VDdXJzb3JPZmZzZXQ9e3NldEN1cnNvck9mZnNldH1cbiAgICAgICAgICAgICAgY29sdW1ucz17ODB9XG4gICAgICAgICAgICAgIG9uSW1hZ2VQYXN0ZT17b25JbWFnZVBhc3RlfVxuICAgICAgICAgICAgICBvblBhc3RlPXsocGFzdGVkVGV4dDogc3RyaW5nKSA9PiB7XG4gICAgICAgICAgICAgICAgaXNVc2VyRWRpdGluZy5jdXJyZW50ID0gdHJ1ZVxuICAgICAgICAgICAgICAgIGNvbnN0IGJlZm9yZSA9IGlucHV0VmFsdWUuc2xpY2UoMCwgY3Vyc29yT2Zmc2V0KVxuICAgICAgICAgICAgICAgIGNvbnN0IGFmdGVyID0gaW5wdXRWYWx1ZS5zbGljZShjdXJzb3JPZmZzZXQpXG4gICAgICAgICAgICAgICAgY29uc3QgbmV3VmFsdWUgPSBiZWZvcmUgKyBwYXN0ZWRUZXh0ICsgYWZ0ZXJcbiAgICAgICAgICAgICAgICBvbklucHV0Q2hhbmdlKG5ld1ZhbHVlKVxuICAgICAgICAgICAgICAgIG9wdGlvbi5vbkNoYW5nZShuZXdWYWx1ZSlcbiAgICAgICAgICAgICAgICBzZXRDdXJzb3JPZmZzZXQoYmVmb3JlLmxlbmd0aCArIHBhc3RlZFRleHQubGVuZ3RoKVxuICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgLz5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPFRleHQgY29sb3I9e2lucHV0VmFsdWUgPyB1bmRlZmluZWQgOiAnaW5hY3RpdmUnfT5cbiAgICAgICAgICAgICAge2lucHV0VmFsdWUgfHwgb3B0aW9uLnBsYWNlaG9sZGVyIHx8IG9wdGlvbi5sYWJlbH1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgIDwvU2VsZWN0T3B0aW9uPlxuICAgICAge29wdGlvbi5kZXNjcmlwdGlvbiAmJiAoXG4gICAgICAgIDxCb3ggcGFkZGluZ0xlZnQ9e2Rlc2NyaXB0aW9uUGFkZGluZ0xlZnR9PlxuICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICBkaW1Db2xvcj17b3B0aW9uLmRpbURlc2NyaXB0aW9uICE9PSBmYWxzZX1cbiAgICAgICAgICAgIGNvbG9yPXtcbiAgICAgICAgICAgICAgaXNTZWxlY3RlZCA/ICdzdWNjZXNzJyA6IGlzRm9jdXNlZCA/ICdzdWdnZXN0aW9uJyA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgfVxuICAgICAgICAgID5cbiAgICAgICAgICAgIHtvcHRpb24uZGVzY3JpcHRpb259XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgICB7aW1hZ2VBdHRhY2htZW50cy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXsxfSBwYWRkaW5nTGVmdD17ZGVzY3JpcHRpb25QYWRkaW5nTGVmdH0+XG4gICAgICAgICAge2ltYWdlQXR0YWNobWVudHMubWFwKChpbWcsIGlkeCkgPT4gKFxuICAgICAgICAgICAgPENsaWNrYWJsZUltYWdlUmVmXG4gICAgICAgICAgICAgIGtleT17aW1nLmlkfVxuICAgICAgICAgICAgICBpbWFnZUlkPXtpbWcuaWR9XG4gICAgICAgICAgICAgIGlzU2VsZWN0ZWQ9eyEhaW1hZ2VzU2VsZWN0ZWQgJiYgaWR4ID09PSBzZWxlY3RlZEltYWdlSW5kZXh9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgICkpfVxuICAgICAgICAgIDxCb3ggZmxleEdyb3c9ezF9IGp1c3RpZnlDb250ZW50PVwiZmxleC1zdGFydFwiIGZsZXhEaXJlY3Rpb249XCJyb3dcIj5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICB7aW1hZ2VzU2VsZWN0ZWQgPyAoXG4gICAgICAgICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgICAgICAgIHtpbWFnZUF0dGFjaG1lbnRzLmxlbmd0aCA+IDEgJiYgKFxuICAgICAgICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgICAgICAgICAgIGFjdGlvbj1cImF0dGFjaG1lbnRzOm5leHRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgY29udGV4dD1cIkF0dGFjaG1lbnRzXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIGZhbGxiYWNrPVwi4oaSXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwibmV4dFwiXG4gICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgICAgICAgICAgICBhY3Rpb249XCJhdHRhY2htZW50czpwcmV2aW91c1wiXG4gICAgICAgICAgICAgICAgICAgICAgICBjb250ZXh0PVwiQXR0YWNobWVudHNcIlxuICAgICAgICAgICAgICAgICAgICAgICAgZmFsbGJhY2s9XCLihpBcIlxuICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJwcmV2XCJcbiAgICAgICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgICAgICAgIGFjdGlvbj1cImF0dGFjaG1lbnRzOnJlbW92ZVwiXG4gICAgICAgICAgICAgICAgICAgIGNvbnRleHQ9XCJBdHRhY2htZW50c1wiXG4gICAgICAgICAgICAgICAgICAgIGZhbGxiYWNrPVwiYmFja3NwYWNlXCJcbiAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJyZW1vdmVcIlxuICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgICAgICAgYWN0aW9uPVwiYXR0YWNobWVudHM6ZXhpdFwiXG4gICAgICAgICAgICAgICAgICAgIGNvbnRleHQ9XCJBdHRhY2htZW50c1wiXG4gICAgICAgICAgICAgICAgICAgIGZhbGxiYWNrPVwiZXNjXCJcbiAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgICAgICAgKSA6IGlzRm9jdXNlZCA/IChcbiAgICAgICAgICAgICAgICAnKOKGkyB0byBzZWxlY3QpJ1xuICAgICAgICAgICAgICApIDogbnVsbH1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuICAgICAge2xheW91dCA9PT0gJ2V4cGFuZGVkJyAmJiA8VGV4dD4gPC9UZXh0Pn1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJLEtBQUtDLFNBQVMsRUFBRUMsU0FBUyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQzFFO0FBQ0EsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxjQUFjO0FBQ2xELFNBQ0VDLGFBQWEsRUFDYkMsY0FBYyxRQUNULG9DQUFvQztBQUMzQyxjQUFjQyxhQUFhLFFBQVEsdUJBQXVCO0FBQzFELFNBQVNDLHFCQUFxQixRQUFRLDJCQUEyQjtBQUNqRSxjQUFjQyxlQUFlLFFBQVEsNkJBQTZCO0FBQ2xFLFNBQVNDLGlCQUFpQixRQUFRLHlCQUF5QjtBQUMzRCxTQUFTQyx3QkFBd0IsUUFBUSxnQ0FBZ0M7QUFDekUsU0FBU0MsTUFBTSxRQUFRLDRCQUE0QjtBQUNuRCxPQUFPQyxTQUFTLE1BQU0saUJBQWlCO0FBQ3ZDLGNBQWNDLHFCQUFxQixRQUFRLGFBQWE7QUFDeEQsU0FBU0MsWUFBWSxRQUFRLG9CQUFvQjtBQUVqRCxLQUFLQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUc7RUFDZEMsTUFBTSxFQUFFQyxPQUFPLENBQUNKLHFCQUFxQixDQUFDSyxDQUFDLENBQUMsRUFBRTtJQUFFQyxJQUFJLEVBQUUsT0FBTztFQUFDLENBQUMsQ0FBQztFQUM1REMsU0FBUyxFQUFFLE9BQU87RUFDbEJDLFVBQVUsRUFBRSxPQUFPO0VBQ25CQyxtQkFBbUIsRUFBRSxPQUFPO0VBQzVCQyxp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 \ No newline at end of file diff --git a/components/CustomSelect/select-option.tsx b/components/CustomSelect/select-option.tsx new file mode 100644 index 0000000..e3a98d6 --- /dev/null +++ b/components/CustomSelect/select-option.tsx @@ -0,0 +1,68 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode } from 'react'; +import { ListItem } from '../design-system/ListItem.js'; +export type SelectOptionProps = { + /** + * Determines if option is focused. + */ + readonly isFocused: boolean; + + /** + * Determines if option is selected. + */ + readonly isSelected: boolean; + + /** + * Option label. + */ + readonly children: ReactNode; + + /** + * Optional description to display below the label. + */ + readonly description?: string; + + /** + * Determines if the down arrow should be shown. + */ + readonly shouldShowDownArrow?: boolean; + + /** + * Determines if the up arrow should be shown. + */ + readonly shouldShowUpArrow?: boolean; + + /** + * Whether ListItem should declare the terminal cursor position. + * Set false when a child declares its own cursor (e.g. BaseTextInput). + */ + readonly declareCursor?: boolean; +}; +export function SelectOption(t0) { + const $ = _c(8); + const { + isFocused, + isSelected, + children, + description, + shouldShowDownArrow, + shouldShowUpArrow, + declareCursor + } = t0; + let t1; + if ($[0] !== children || $[1] !== declareCursor || $[2] !== description || $[3] !== isFocused || $[4] !== isSelected || $[5] !== shouldShowDownArrow || $[6] !== shouldShowUpArrow) { + t1 = {children}; + $[0] = children; + $[1] = declareCursor; + $[2] = description; + $[3] = isFocused; + $[4] = isSelected; + $[5] = shouldShowDownArrow; + $[6] = shouldShowUpArrow; + $[7] = t1; + } else { + t1 = $[7]; + } + return t1; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsIkxpc3RJdGVtIiwiU2VsZWN0T3B0aW9uUHJvcHMiLCJpc0ZvY3VzZWQiLCJpc1NlbGVjdGVkIiwiY2hpbGRyZW4iLCJkZXNjcmlwdGlvbiIsInNob3VsZFNob3dEb3duQXJyb3ciLCJzaG91bGRTaG93VXBBcnJvdyIsImRlY2xhcmVDdXJzb3IiLCJTZWxlY3RPcHRpb24iLCJ0MCIsIiQiLCJfYyIsInQxIl0sInNvdXJjZXMiOlsic2VsZWN0LW9wdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHR5cGUgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBMaXN0SXRlbSB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vTGlzdEl0ZW0uanMnXG5cbmV4cG9ydCB0eXBlIFNlbGVjdE9wdGlvblByb3BzID0ge1xuICAvKipcbiAgICogRGV0ZXJtaW5lcyBpZiBvcHRpb24gaXMgZm9jdXNlZC5cbiAgICovXG4gIHJlYWRvbmx5IGlzRm9jdXNlZDogYm9vbGVhblxuXG4gIC8qKlxuICAgKiBEZXRlcm1pbmVzIGlmIG9wdGlvbiBpcyBzZWxlY3RlZC5cbiAgICovXG4gIHJlYWRvbmx5IGlzU2VsZWN0ZWQ6IGJvb2xlYW5cblxuICAvKipcbiAgICogT3B0aW9uIGxhYmVsLlxuICAgKi9cbiAgcmVhZG9ubHkgY2hpbGRyZW46IFJlYWN0Tm9kZVxuXG4gIC8qKlxuICAgKiBPcHRpb25hbCBkZXNjcmlwdGlvbiB0byBkaXNwbGF5IGJlbG93IHRoZSBsYWJlbC5cbiAgICovXG4gIHJlYWRvbmx5IGRlc2NyaXB0aW9uPzogc3RyaW5nXG5cbiAgLyoqXG4gICAqIERldGVybWluZXMgaWYgdGhlIGRvd24gYXJyb3cgc2hvdWxkIGJlIHNob3duLlxuICAgKi9cbiAgcmVhZG9ubHkgc2hvdWxkU2hvd0Rvd25BcnJvdz86IGJvb2xlYW5cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyBpZiB0aGUgdXAgYXJyb3cgc2hvdWxkIGJlIHNob3duLlxuICAgKi9cbiAgcmVhZG9ubHkgc2hvdWxkU2hvd1VwQXJyb3c/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIFdoZXRoZXIgTGlzdEl0ZW0gc2hvdWxkIGRlY2xhcmUgdGhlIHRlcm1pbmFsIGN1cnNvciBwb3NpdGlvbi5cbiAgICogU2V0IGZhbHNlIHdoZW4gYSBjaGlsZCBkZWNsYXJlcyBpdHMgb3duIGN1cnNvciAoZS5nLiBCYXNlVGV4dElucHV0KS5cbiAgICovXG4gIHJlYWRvbmx5IGRlY2xhcmVDdXJzb3I/OiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTZWxlY3RPcHRpb24oe1xuICBpc0ZvY3VzZWQsXG4gIGlzU2VsZWN0ZWQsXG4gIGNoaWxkcmVuLFxuICBkZXNjcmlwdGlvbixcbiAgc2hvdWxkU2hvd0Rvd25BcnJvdyxcbiAgc2hvdWxkU2hvd1VwQXJyb3csXG4gIGRlY2xhcmVDdXJzb3IsXG59OiBTZWxlY3RPcHRpb25Qcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPExpc3RJdGVtXG4gICAgICBpc0ZvY3VzZWQ9e2lzRm9jdXNlZH1cbiAgICAgIGlzU2VsZWN0ZWQ9e2lzU2VsZWN0ZWR9XG4gICAgICBkZXNjcmlwdGlvbj17ZGVzY3JpcHRpb259XG4gICAgICBzaG93U2Nyb2xsRG93bj17c2hvdWxkU2hvd0Rvd25BcnJvd31cbiAgICAgIHNob3dTY3JvbGxVcD17c2hvdWxkU2hvd1VwQXJyb3d9XG4gICAgICBzdHlsZWQ9e2ZhbHNlfVxuICAgICAgZGVjbGFyZUN1cnNvcj17ZGVjbGFyZUN1cnNvcn1cbiAgICA+XG4gICAgICB7Y2hpbGRyZW59XG4gICAgPC9MaXN0SXRlbT5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJLEtBQUtDLFNBQVMsUUFBUSxPQUFPO0FBQzdDLFNBQVNDLFFBQVEsUUFBUSw4QkFBOEI7QUFFdkQsT0FBTyxLQUFLQyxpQkFBaUIsR0FBRztFQUM5QjtBQUNGO0FBQ0E7RUFDRSxTQUFTQyxTQUFTLEVBQUUsT0FBTzs7RUFFM0I7QUFDRjtBQUNBO0VBQ0UsU0FBU0MsVUFBVSxFQUFFLE9BQU87O0VBRTVCO0FBQ0Y7QUFDQTtFQUNFLFNBQVNDLFFBQVEsRUFBRUwsU0FBUzs7RUFFNUI7QUFDRjtBQUNBO0VBQ0UsU0FBU00sV0FBVyxDQUFDLEVBQUUsTUFBTTs7RUFFN0I7QUFDRjtBQUNBO0VBQ0UsU0FBU0MsbUJBQW1CLENBQUMsRUFBRSxPQUFPOztFQUV0QztBQUNGO0FBQ0E7RUFDRSxTQUFTQyxpQkFBaUIsQ0FBQyxFQUFFLE9BQU87O0VBRXBDO0FBQ0Y7QUFDQTtBQUNBO0VBQ0UsU0FBU0MsYUFBYSxDQUFDLEVBQUUsT0FBTztBQUNsQyxDQUFDO0FBRUQsT0FBTyxTQUFBQyxhQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXNCO0lBQUFWLFNBQUE7SUFBQUMsVUFBQTtJQUFBQyxRQUFBO0lBQUFDLFdBQUE7SUFBQUMsbUJBQUE7SUFBQUMsaUJBQUE7SUFBQUM7RUFBQSxJQUFBRSxFQVFUO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQVAsUUFBQSxJQUFBTyxDQUFBLFFBQUFILGFBQUEsSUFBQUcsQ0FBQSxRQUFBTixXQUFBLElBQUFNLENBQUEsUUFBQVQsU0FBQSxJQUFBUyxDQUFBLFFBQUFSLFVBQUEsSUFBQVEsQ0FBQSxRQUFBTCxtQkFBQSxJQUFBSyxDQUFBLFFBQUFKLGlCQUFBO0lBRWhCTSxFQUFBLElBQUMsUUFBUSxDQUNJWCxTQUFTLENBQVRBLFVBQVEsQ0FBQyxDQUNSQyxVQUFVLENBQVZBLFdBQVMsQ0FBQyxDQUNURSxXQUFXLENBQVhBLFlBQVUsQ0FBQyxDQUNSQyxjQUFtQixDQUFuQkEsb0JBQWtCLENBQUMsQ0FDckJDLFlBQWlCLENBQWpCQSxrQkFBZ0IsQ0FBQyxDQUN2QixNQUFLLENBQUwsTUFBSSxDQUFDLENBQ0VDLGFBQWEsQ0FBYkEsY0FBWSxDQUFDLENBRTNCSixTQUFPLENBQ1YsRUFWQyxRQUFRLENBVUU7SUFBQU8sQ0FBQSxNQUFBUCxRQUFBO0lBQUFPLENBQUEsTUFBQUgsYUFBQTtJQUFBRyxDQUFBLE1BQUFOLFdBQUE7SUFBQU0sQ0FBQSxNQUFBVCxTQUFBO0lBQUFTLENBQUEsTUFBQVIsVUFBQTtJQUFBUSxDQUFBLE1BQUFMLG1CQUFBO0lBQUFLLENBQUEsTUFBQUosaUJBQUE7SUFBQUksQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFBQSxPQVZYRSxFQVVXO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/CustomSelect/select.tsx b/components/CustomSelect/select.tsx new file mode 100644 index 0000000..134de48 --- /dev/null +++ b/components/CustomSelect/select.tsx @@ -0,0 +1,690 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import React, { type ReactNode, useEffect, useRef, useState } from 'react'; +import { useDeclaredCursor } from '../../ink/hooks/use-declared-cursor.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Ansi, Box, Text } from '../../ink.js'; +import { count } from '../../utils/array.js'; +import type { PastedContent } from '../../utils/config.js'; +import type { ImageDimensions } from '../../utils/imageResizer.js'; +import { SelectInputOption } from './select-input-option.js'; +import { SelectOption } from './select-option.js'; +import { useSelectInput } from './use-select-input.js'; +import { useSelectState } from './use-select-state.js'; + +// Extract text content from ReactNode for width calculation +function getTextContent(node: ReactNode): string { + if (typeof node === 'string') return node; + if (typeof node === 'number') return String(node); + if (!node) return ''; + if (Array.isArray(node)) return node.map(getTextContent).join(''); + if (React.isValidElement<{ + children?: ReactNode; + }>(node)) { + return getTextContent(node.props.children); + } + return ''; +} +type BaseOption = { + description?: string; + dimDescription?: boolean; + label: ReactNode; + value: T; + disabled?: boolean; +}; +export type OptionWithDescription = (BaseOption & { + type?: 'text'; +}) | (BaseOption & { + type: 'input'; + onChange: (value: string) => void; + placeholder?: string; + initialValue?: string; + /** + * Controls behavior when submitting with empty input: + * - true: calls onChange (treats empty as valid submission) + * - false (default): calls onCancel (treats empty as cancellation) + * + * Also affects initial Enter press: when true, submits immediately; + * when false, enters input mode first so user can type. + */ + allowEmptySubmitToCancel?: boolean; + /** + * When true, always shows the label alongside the input value, regardless of + * the global inlineDescriptions/showLabel setting. Use this when the label + * provides important context that should always be visible (e.g., "Yes, and allow..."). + */ + showLabelWithValue?: boolean; + /** + * Custom separator between label and value when showLabel is true. + * Defaults to ", ". Use ": " for labels that read better with a colon. + */ + labelValueSeparator?: string; + /** + * When true, automatically reset cursor to end of line when: + * - Option becomes focused + * - Input value changes + * This prevents cursor position bugs when the input value updates asynchronously. + */ + resetCursorOnUpdate?: boolean; +}); +export type SelectProps = { + /** + * When disabled, user input is ignored. + * + * @default false + */ + readonly isDisabled?: boolean; + + /** + * When true, prevents selection on Enter but allows scrolling. + * + * @default false + */ + readonly disableSelection?: boolean; + + /** + * When true, hides the numeric indexes next to each option. + * + * @default false + */ + readonly hideIndexes?: boolean; + + /** + * Number of visible options. + * + * @default 5 + */ + readonly visibleOptionCount?: number; + + /** + * Highlight text in option labels. + */ + readonly highlightText?: string; + + /** + * Options. + */ + readonly options: OptionWithDescription[]; + + /** + * Default value. + */ + readonly defaultValue?: T; + + /** + * Callback when cancel is pressed. + */ + readonly onCancel?: () => void; + + /** + * Callback when selected option changes. + */ + readonly onChange?: (value: T) => void; + + /** + * Callback when focused option changes. + * Note: This is for one-way notification only. Avoid combining with focusValue + * for bidirectional sync, as this can cause feedback loops. + */ + readonly onFocus?: (value: T) => void; + + /** + * Initial value to focus. This is used to set focus when the component mounts. + */ + readonly defaultFocusValue?: T; + + /** + * Layout of the options. + * - `compact` (default) tries to use one line per option + * - `expanded` uses multiple lines and an empty line between options + * - `compact-vertical` uses compact index formatting with descriptions below labels + */ + readonly layout?: 'compact' | 'expanded' | 'compact-vertical'; + + /** + * When true, descriptions are rendered inline after the label instead of + * in a separate column. Use this for short descriptions like hints. + * + * @default false + */ + readonly inlineDescriptions?: boolean; + + /** + * Callback when user presses up from the first item. + * If provided, navigation will not wrap to the last item. + */ + readonly onUpFromFirstItem?: () => void; + + /** + * Callback when user presses down from the last item. + * If provided, navigation will not wrap to the first item. + */ + readonly onDownFromLastItem?: () => void; + + /** + * Callback when input mode should be toggled for an option. + * Called when Tab is pressed (to enter or exit input mode). + */ + readonly onInputModeToggle?: (value: T) => void; + + /** + * Callback to open external editor for editing input option values. + * When provided, ctrl+g will trigger this callback in input options + * with the current value and a setter function to update the internal state. + */ + readonly onOpenEditor?: (currentValue: string, setValue: (value: string) => void) => void; + + /** + * Optional callback when an image is pasted into an input option. + */ + readonly onImagePaste?: (base64Image: string, mediaType?: string, filename?: string, dimensions?: ImageDimensions, sourcePath?: string) => void; + + /** + * Pasted content to display inline in input options. + */ + readonly pastedContents?: Record; + + /** + * Callback to remove a pasted image by its ID. + */ + readonly onRemoveImage?: (id: number) => void; +}; +export function Select(t0) { + const $ = _c(72); + const { + isDisabled: t1, + hideIndexes: t2, + visibleOptionCount: t3, + highlightText, + options, + defaultValue, + onCancel, + onChange, + onFocus, + defaultFocusValue, + layout: t4, + disableSelection: t5, + inlineDescriptions: t6, + onUpFromFirstItem, + onDownFromLastItem, + onInputModeToggle, + onOpenEditor, + onImagePaste, + pastedContents, + onRemoveImage + } = t0; + const isDisabled = t1 === undefined ? false : t1; + const hideIndexes = t2 === undefined ? false : t2; + const visibleOptionCount = t3 === undefined ? 5 : t3; + const layout = t4 === undefined ? "compact" : t4; + const disableSelection = t5 === undefined ? false : t5; + const inlineDescriptions = t6 === undefined ? false : t6; + const [imagesSelected, setImagesSelected] = useState(false); + const [selectedImageIndex, setSelectedImageIndex] = useState(0); + let t7; + if ($[0] !== options) { + t7 = () => { + const initialMap = new Map(); + options.forEach(option => { + if (option.type === "input" && option.initialValue) { + initialMap.set(option.value, option.initialValue); + } + }); + return initialMap; + }; + $[0] = options; + $[1] = t7; + } else { + t7 = $[1]; + } + const [inputValues, setInputValues] = useState(t7); + let t8; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t8 = new Map(); + $[2] = t8; + } else { + t8 = $[2]; + } + const lastInitialValues = useRef(t8); + let t10; + let t9; + if ($[3] !== inputValues || $[4] !== options) { + t9 = () => { + for (const option_0 of options) { + if (option_0.type === "input" && option_0.initialValue !== undefined) { + const lastInitial = lastInitialValues.current.get(option_0.value) ?? ""; + const currentValue = inputValues.get(option_0.value) ?? ""; + const newInitial = option_0.initialValue; + if (newInitial !== lastInitial && currentValue === lastInitial) { + setInputValues(prev => { + const next = new Map(prev); + next.set(option_0.value, newInitial); + return next; + }); + } + lastInitialValues.current.set(option_0.value, newInitial); + } + } + }; + t10 = [options, inputValues]; + $[3] = inputValues; + $[4] = options; + $[5] = t10; + $[6] = t9; + } else { + t10 = $[5]; + t9 = $[6]; + } + useEffect(t9, t10); + let t11; + if ($[7] !== defaultFocusValue || $[8] !== defaultValue || $[9] !== onCancel || $[10] !== onChange || $[11] !== onFocus || $[12] !== options || $[13] !== visibleOptionCount) { + t11 = { + visibleOptionCount, + options, + defaultValue, + onChange, + onCancel, + onFocus, + focusValue: defaultFocusValue + }; + $[7] = defaultFocusValue; + $[8] = defaultValue; + $[9] = onCancel; + $[10] = onChange; + $[11] = onFocus; + $[12] = options; + $[13] = visibleOptionCount; + $[14] = t11; + } else { + t11 = $[14]; + } + const state = useSelectState(t11); + const t12 = disableSelection || (hideIndexes ? "numeric" : false); + let t13; + if ($[15] !== pastedContents) { + t13 = () => { + if (pastedContents && Object.values(pastedContents).some(_temp)) { + const imageCount = count(Object.values(pastedContents), _temp2); + setImagesSelected(true); + setSelectedImageIndex(imageCount - 1); + return true; + } + return false; + }; + $[15] = pastedContents; + $[16] = t13; + } else { + t13 = $[16]; + } + let t14; + if ($[17] !== imagesSelected || $[18] !== inputValues || $[19] !== isDisabled || $[20] !== onDownFromLastItem || $[21] !== onInputModeToggle || $[22] !== onUpFromFirstItem || $[23] !== options || $[24] !== state || $[25] !== t12 || $[26] !== t13) { + t14 = { + isDisabled, + disableSelection: t12, + state, + options, + isMultiSelect: false, + onUpFromFirstItem, + onDownFromLastItem, + onInputModeToggle, + inputValues, + imagesSelected, + onEnterImageSelection: t13 + }; + $[17] = imagesSelected; + $[18] = inputValues; + $[19] = isDisabled; + $[20] = onDownFromLastItem; + $[21] = onInputModeToggle; + $[22] = onUpFromFirstItem; + $[23] = options; + $[24] = state; + $[25] = t12; + $[26] = t13; + $[27] = t14; + } else { + t14 = $[27]; + } + useSelectInput(t14); + let T0; + let t15; + let t16; + let t17; + if ($[28] !== hideIndexes || $[29] !== highlightText || $[30] !== imagesSelected || $[31] !== inlineDescriptions || $[32] !== inputValues || $[33] !== isDisabled || $[34] !== layout || $[35] !== onCancel || $[36] !== onChange || $[37] !== onImagePaste || $[38] !== onOpenEditor || $[39] !== onRemoveImage || $[40] !== options.length || $[41] !== pastedContents || $[42] !== selectedImageIndex || $[43] !== state.focusedValue || $[44] !== state.options || $[45] !== state.value || $[46] !== state.visibleFromIndex || $[47] !== state.visibleOptions || $[48] !== state.visibleToIndex) { + t17 = Symbol.for("react.early_return_sentinel"); + bb0: { + const styles = { + container: _temp3, + highlightedText: _temp4 + }; + if (layout === "expanded") { + let t18; + if ($[53] !== state.options.length) { + t18 = state.options.length.toString(); + $[53] = state.options.length; + $[54] = t18; + } else { + t18 = $[54]; + } + const maxIndexWidth = t18.length; + t17 = {state.visibleOptions.map((option_1, index) => { + const isFirstVisibleOption = option_1.index === state.visibleFromIndex; + const isLastVisibleOption = option_1.index === state.visibleToIndex - 1; + const areMoreOptionsBelow = state.visibleToIndex < options.length; + const areMoreOptionsAbove = state.visibleFromIndex > 0; + const i = state.visibleFromIndex + index + 1; + const isFocused = !isDisabled && state.focusedValue === option_1.value; + const isSelected = state.value === option_1.value; + if (option_1.type === "input") { + const inputValue = inputValues.has(option_1.value) ? inputValues.get(option_1.value) : option_1.initialValue || ""; + return { + setInputValues(prev_0 => { + const next_0 = new Map(prev_0); + next_0.set(option_1.value, value); + return next_0; + }); + }} onSubmit={value_0 => { + const hasImageAttachments = pastedContents && Object.values(pastedContents).some(_temp5); + if (value_0.trim() || hasImageAttachments || option_1.allowEmptySubmitToCancel) { + onChange?.(option_1.value); + } else { + onCancel?.(); + } + }} onExit={onCancel} layout="expanded" showLabel={inlineDescriptions} onOpenEditor={onOpenEditor} resetCursorOnUpdate={option_1.resetCursorOnUpdate} onImagePaste={onImagePaste} pastedContents={pastedContents} onRemoveImage={onRemoveImage} imagesSelected={imagesSelected} selectedImageIndex={selectedImageIndex} onImagesSelectedChange={setImagesSelected} onSelectedImageIndexChange={setSelectedImageIndex} />; + } + let label = option_1.label; + if (typeof option_1.label === "string" && highlightText && option_1.label.includes(highlightText)) { + const labelText = option_1.label; + const index_0 = labelText.indexOf(highlightText); + label = <>{labelText.slice(0, index_0)}{highlightText}{labelText.slice(index_0 + highlightText.length)}; + } + const isOptionDisabled = option_1.disabled === true; + const optionColor = isOptionDisabled ? undefined : isSelected ? "success" : isFocused ? "suggestion" : undefined; + return {label}{option_1.description && {option_1.description}} ; + })}; + break bb0; + } + if (layout === "compact-vertical") { + let t18; + if ($[55] !== hideIndexes || $[56] !== state.options) { + t18 = hideIndexes ? 0 : state.options.length.toString().length; + $[55] = hideIndexes; + $[56] = state.options; + $[57] = t18; + } else { + t18 = $[57]; + } + const maxIndexWidth_0 = t18; + t17 = {state.visibleOptions.map((option_2, index_1) => { + const isFirstVisibleOption_0 = option_2.index === state.visibleFromIndex; + const isLastVisibleOption_0 = option_2.index === state.visibleToIndex - 1; + const areMoreOptionsBelow_0 = state.visibleToIndex < options.length; + const areMoreOptionsAbove_0 = state.visibleFromIndex > 0; + const i_0 = state.visibleFromIndex + index_1 + 1; + const isFocused_0 = !isDisabled && state.focusedValue === option_2.value; + const isSelected_0 = state.value === option_2.value; + if (option_2.type === "input") { + const inputValue_0 = inputValues.has(option_2.value) ? inputValues.get(option_2.value) : option_2.initialValue || ""; + return { + setInputValues(prev_1 => { + const next_1 = new Map(prev_1); + next_1.set(option_2.value, value_1); + return next_1; + }); + }} onSubmit={value_2 => { + const hasImageAttachments_0 = pastedContents && Object.values(pastedContents).some(_temp6); + if (value_2.trim() || hasImageAttachments_0 || option_2.allowEmptySubmitToCancel) { + onChange?.(option_2.value); + } else { + onCancel?.(); + } + }} onExit={onCancel} layout="compact" showLabel={inlineDescriptions} onOpenEditor={onOpenEditor} resetCursorOnUpdate={option_2.resetCursorOnUpdate} onImagePaste={onImagePaste} pastedContents={pastedContents} onRemoveImage={onRemoveImage} imagesSelected={imagesSelected} selectedImageIndex={selectedImageIndex} onImagesSelectedChange={setImagesSelected} onSelectedImageIndexChange={setSelectedImageIndex} />; + } + let label_0 = option_2.label; + if (typeof option_2.label === "string" && highlightText && option_2.label.includes(highlightText)) { + const labelText_0 = option_2.label; + const index_2 = labelText_0.indexOf(highlightText); + label_0 = <>{labelText_0.slice(0, index_2)}{highlightText}{labelText_0.slice(index_2 + highlightText.length)}; + } + const isOptionDisabled_0 = option_2.disabled === true; + return <>{!hideIndexes && {`${i_0}.`.padEnd(maxIndexWidth_0 + 1)}}{label_0}{option_2.description && {option_2.description}}; + })}; + break bb0; + } + let t18; + if ($[58] !== hideIndexes || $[59] !== state.options) { + t18 = hideIndexes ? 0 : state.options.length.toString().length; + $[58] = hideIndexes; + $[59] = state.options; + $[60] = t18; + } else { + t18 = $[60]; + } + const maxIndexWidth_1 = t18; + const hasInputOptions = state.visibleOptions.some(_temp7); + const hasDescriptions = !inlineDescriptions && !hasInputOptions && state.visibleOptions.some(_temp8); + const optionData = state.visibleOptions.map((option_3, index_3) => { + const isFirstVisibleOption_1 = option_3.index === state.visibleFromIndex; + const isLastVisibleOption_1 = option_3.index === state.visibleToIndex - 1; + const areMoreOptionsBelow_1 = state.visibleToIndex < options.length; + const areMoreOptionsAbove_1 = state.visibleFromIndex > 0; + const i_1 = state.visibleFromIndex + index_3 + 1; + const isFocused_1 = !isDisabled && state.focusedValue === option_3.value; + const isSelected_1 = state.value === option_3.value; + const isOptionDisabled_1 = option_3.disabled === true; + let label_1 = option_3.label; + if (typeof option_3.label === "string" && highlightText && option_3.label.includes(highlightText)) { + const labelText_1 = option_3.label; + const idx = labelText_1.indexOf(highlightText); + label_1 = <>{labelText_1.slice(0, idx)}{highlightText}{labelText_1.slice(idx + highlightText.length)}; + } + return { + option: option_3, + index: i_1, + label: label_1, + isFocused: isFocused_1, + isSelected: isSelected_1, + isOptionDisabled: isOptionDisabled_1, + shouldShowDownArrow: areMoreOptionsBelow_1 && isLastVisibleOption_1, + shouldShowUpArrow: areMoreOptionsAbove_1 && isFirstVisibleOption_1 + }; + }); + if (hasDescriptions) { + let t19; + if ($[61] !== hideIndexes || $[62] !== maxIndexWidth_1) { + t19 = data => { + if (data.option.type === "input") { + return 0; + } + const labelText_2 = getTextContent(data.option.label); + const indexWidth = hideIndexes ? 0 : maxIndexWidth_1 + 2; + const checkmarkWidth = data.isSelected ? 2 : 0; + return 2 + indexWidth + stringWidth(labelText_2) + checkmarkWidth; + }; + $[61] = hideIndexes; + $[62] = maxIndexWidth_1; + $[63] = t19; + } else { + t19 = $[63]; + } + const maxLabelWidth = Math.max(...optionData.map(t19)); + let t20; + if ($[64] !== hideIndexes || $[65] !== maxIndexWidth_1 || $[66] !== maxLabelWidth) { + t20 = data_0 => { + if (data_0.option.type === "input") { + return null; + } + const labelText_3 = getTextContent(data_0.option.label); + const indexWidth_0 = hideIndexes ? 0 : maxIndexWidth_1 + 2; + const checkmarkWidth_0 = data_0.isSelected ? 2 : 0; + const currentLabelWidth = 2 + indexWidth_0 + stringWidth(labelText_3) + checkmarkWidth_0; + const padding = maxLabelWidth - currentLabelWidth; + return {data_0.isFocused ? {figures.pointer} : data_0.shouldShowDownArrow ? {figures.arrowDown} : data_0.shouldShowUpArrow ? {figures.arrowUp} : } {!hideIndexes && {`${data_0.index}.`.padEnd(maxIndexWidth_1 + 2)}}{data_0.label}{data_0.isSelected && {figures.tick}}{padding > 0 && {" ".repeat(padding)}}{data_0.option.description || " "}; + }; + $[64] = hideIndexes; + $[65] = maxIndexWidth_1; + $[66] = maxLabelWidth; + $[67] = t20; + } else { + t20 = $[67]; + } + t17 = {optionData.map(t20)}; + break bb0; + } + T0 = Box; + t15 = styles.container(); + t16 = state.visibleOptions.map((option_4, index_4) => { + if (option_4.type === "input") { + const inputValue_1 = inputValues.has(option_4.value) ? inputValues.get(option_4.value) : option_4.initialValue || ""; + const isFirstVisibleOption_2 = option_4.index === state.visibleFromIndex; + const isLastVisibleOption_2 = option_4.index === state.visibleToIndex - 1; + const areMoreOptionsBelow_2 = state.visibleToIndex < options.length; + const areMoreOptionsAbove_2 = state.visibleFromIndex > 0; + const i_2 = state.visibleFromIndex + index_4 + 1; + const isFocused_2 = !isDisabled && state.focusedValue === option_4.value; + const isSelected_2 = state.value === option_4.value; + return { + setInputValues(prev_2 => { + const next_2 = new Map(prev_2); + next_2.set(option_4.value, value_3); + return next_2; + }); + }} onSubmit={value_4 => { + const hasImageAttachments_1 = pastedContents && Object.values(pastedContents).some(_temp9); + if (value_4.trim() || hasImageAttachments_1 || option_4.allowEmptySubmitToCancel) { + onChange?.(option_4.value); + } else { + onCancel?.(); + } + }} onExit={onCancel} layout="compact" showLabel={inlineDescriptions} onOpenEditor={onOpenEditor} resetCursorOnUpdate={option_4.resetCursorOnUpdate} onImagePaste={onImagePaste} pastedContents={pastedContents} onRemoveImage={onRemoveImage} imagesSelected={imagesSelected} selectedImageIndex={selectedImageIndex} onImagesSelectedChange={setImagesSelected} onSelectedImageIndexChange={setSelectedImageIndex} />; + } + let label_2 = option_4.label; + if (typeof option_4.label === "string" && highlightText && option_4.label.includes(highlightText)) { + const labelText_4 = option_4.label; + const index_5 = labelText_4.indexOf(highlightText); + label_2 = <>{labelText_4.slice(0, index_5)}{highlightText}{labelText_4.slice(index_5 + highlightText.length)}; + } + const isFirstVisibleOption_3 = option_4.index === state.visibleFromIndex; + const isLastVisibleOption_3 = option_4.index === state.visibleToIndex - 1; + const areMoreOptionsBelow_3 = state.visibleToIndex < options.length; + const areMoreOptionsAbove_3 = state.visibleFromIndex > 0; + const i_3 = state.visibleFromIndex + index_4 + 1; + const isFocused_3 = !isDisabled && state.focusedValue === option_4.value; + const isSelected_3 = state.value === option_4.value; + const isOptionDisabled_2 = option_4.disabled === true; + return {!hideIndexes && {`${i_3}.`.padEnd(maxIndexWidth_1 + 2)}}{label_2}{inlineDescriptions && option_4.description && {" "}{option_4.description}}{!inlineDescriptions && option_4.description && {option_4.description}}; + }); + } + $[28] = hideIndexes; + $[29] = highlightText; + $[30] = imagesSelected; + $[31] = inlineDescriptions; + $[32] = inputValues; + $[33] = isDisabled; + $[34] = layout; + $[35] = onCancel; + $[36] = onChange; + $[37] = onImagePaste; + $[38] = onOpenEditor; + $[39] = onRemoveImage; + $[40] = options.length; + $[41] = pastedContents; + $[42] = selectedImageIndex; + $[43] = state.focusedValue; + $[44] = state.options; + $[45] = state.value; + $[46] = state.visibleFromIndex; + $[47] = state.visibleOptions; + $[48] = state.visibleToIndex; + $[49] = T0; + $[50] = t15; + $[51] = t16; + $[52] = t17; + } else { + T0 = $[49]; + t15 = $[50]; + t16 = $[51]; + t17 = $[52]; + } + if (t17 !== Symbol.for("react.early_return_sentinel")) { + return t17; + } + let t18; + if ($[68] !== T0 || $[69] !== t15 || $[70] !== t16) { + t18 = {t16}; + $[68] = T0; + $[69] = t15; + $[70] = t16; + $[71] = t18; + } else { + t18 = $[71]; + } + return t18; +} + +// Row container for the two-column (label + description) layout. Unlike +// the other Select layouts, this one doesn't render through SelectOption → +// ListItem, so it declares the native cursor directly. Parks the cursor +// on the pointer indicator so screen readers / magnifiers track focus. +function _temp9(c_3) { + return c_3.type === "image"; +} +function _temp8(opt_0) { + return opt_0.description; +} +function _temp7(opt) { + return opt.type === "input"; +} +function _temp6(c_2) { + return c_2.type === "image"; +} +function _temp5(c_1) { + return c_1.type === "image"; +} +function _temp4() { + return { + bold: true + }; +} +function _temp3() { + return { + flexDirection: "column" as const + }; +} +function _temp2(c) { + return c.type === "image"; +} +function _temp(c_0) { + return c_0.type === "image"; +} +function TwoColumnRow(t0) { + const $ = _c(5); + const { + isFocused, + children + } = t0; + let t1; + if ($[0] !== isFocused) { + t1 = { + line: 0, + column: 0, + active: isFocused + }; + $[0] = isFocused; + $[1] = t1; + } else { + t1 = $[1]; + } + const cursorRef = useDeclaredCursor(t1); + let t2; + if ($[2] !== children || $[3] !== cursorRef) { + t2 = {children}; + $[2] = children; + $[3] = cursorRef; + $[4] = t2; + } else { + t2 = $[4]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJSZWFjdE5vZGUiLCJ1c2VFZmZlY3QiLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsInVzZURlY2xhcmVkQ3Vyc29yIiwic3RyaW5nV2lkdGgiLCJBbnNpIiwiQm94IiwiVGV4dCIsImNvdW50IiwiUGFzdGVkQ29udGVudCIsIkltYWdlRGltZW5zaW9ucyIsIlNlbGVjdElucHV0T3B0aW9uIiwiU2VsZWN0T3B0aW9uIiwidXNlU2VsZWN0SW5wdXQiLCJ1c2VTZWxlY3RTdGF0ZSIsImdldFRleHRDb250ZW50Iiwibm9kZSIsIlN0cmluZyIsIkFycmF5IiwiaXNBcnJheSIsIm1hcCIsImpvaW4iLCJpc1ZhbGlkRWxlbWVudCIsImNoaWxkcmVuIiwicHJvcHMiLCJCYXNlT3B0aW9uIiwiZGVzY3JpcHRpb24iLCJkaW1EZXNjcmlwdGlvbiIsImxhYmVsIiwidmFsdWUiLCJUIiwiZGlzYWJsZWQiLCJPcHRpb25XaXRoRGVzY3JpcHRpb24iLCJ0eXBlIiwib25DaGFuZ2UiLCJwbGFjZWhvbGRlciIsImluaXRpYWxWYWx1ZSIsImFsbG93RW1wdHlTdWJtaXRUb0NhbmNlbCIsInNob3dMYWJlbFdpdGhWYWx1ZSIsImxhYmVsVmFsdWVTZXBhcmF0b3IiLCJyZXNldEN1cnNvck9uVXBkYXRlIiwiU2VsZWN0UHJvcHMiLCJpc0Rpc2FibGVkIiwiZGlzYWJsZVNlbGVjdGlvbiIsImhpZGVJbmRleGVzIiwidmlzaWJsZU9wdGlvbkNvdW50IiwiaGlnaGxpZ2h0VGV4dCIsIm9wdGlvbnMiLCJkZWZhdWx0VmFsdWUiLCJvbkNhbmNlbCIsIm9uRm9jdXMiLCJkZWZhdWx0Rm9jdXNWYWx1ZSIsImxheW91dCIsImlubGluZURlc2NyaXB0aW9ucyIsIm9uVXBGcm9tRmlyc3RJdGVtIiwib25Eb3duRnJvbUxhc3RJdGVtIiwib25JbnB1dE1vZGVUb2dnbGUiLCJvbk9wZW5FZGl0b3IiLCJjdXJyZW50VmFsdWUiLCJzZXRWYWx1ZSIsIm9uSW1hZ2VQYXN0ZSIsImJhc2U2NEltYWdlIiwibWVkaWFUeXBlIiwiZmlsZW5hbWUiLCJkaW1lbnNpb25zIiwic291cmNlUGF0aCIsInBhc3RlZENvbnRlbnRzIiwiUmVjb3JkIiwib25SZW1vdmVJbWFnZSIsImlkIiwiU2VsZWN0IiwidDAiLCIkIiwiX2MiLCJ0MSIsInQyIiwidDMiLCJ0NCIsInQ1IiwidDYiLCJ1bmRlZmluZWQiLCJpbWFnZXNTZWxlY3RlZCIsInNldEltYWdlc1NlbGVjdGVkIiwic2VsZWN0ZWRJbWFnZUluZGV4Iiwic2V0U2VsZWN0ZWRJbWFnZUluZGV4IiwidDciLCJpbml0aWFsTWFwIiwiTWFwIiwiZm9yRWFjaCIsIm9wdGlvbiIsInNldCIsImlucHV0VmFsdWVzIiwic2V0SW5wdXRWYWx1ZXMiLCJ0OCIsIlN5bWJvbCIsImZvciIsImxhc3RJbml0aWFsVmFsdWVzIiwidDEwIiwidDkiLCJvcHRpb25fMCIsImxhc3RJbml0aWFsIiwiY3VycmVudCIsImdldCIsIm5ld0luaXRpYWwiLCJwcmV2IiwibmV4dCIsInQxMSIsImZvY3VzVmFsdWUiLCJzdGF0ZSIsInQxMiIsInQxMyIsIk9iamVjdCIsInZhbHVlcyIsInNvbWUiLCJfdGVtcCIsImltYWdlQ291bnQiLCJfdGVtcDIiLCJ0MTQiLCJpc011bHRpU2VsZWN0Iiwib25FbnRlckltYWdlU2VsZWN0aW9uIiwiVDAiLCJ0MTUiLCJ0MTYiLCJ0MTciLCJsZW5ndGgiLCJmb2N1c2VkVmFsdWUiLCJ2aXNpYmxlRnJvbUluZGV4IiwidmlzaWJsZU9wdGlvbnMiLCJ2aXNpYmxlVG9JbmRleCIsImJiMCIsInN0eWxlcyIsImNvbnRhaW5lciIsIl90ZW1wMyIsImhpZ2hsaWdodGVkVGV4dCIsIl90ZW1wNCIsInQxOCIsInRvU3RyaW5nIiwibWF4SW5kZXhXaWR0aCIsIm9wdGlvbl8xIiwiaW5kZXgiLCJpc0ZpcnN0VmlzaWJsZU9wdGlvbiIsImlzTGFzdFZpc2libGVPcHRpb24iLCJhcmVNb3JlT3B0aW9uc0JlbG93IiwiYXJlTW9yZU9wdGlvbnNBYm92ZSIsImkiLCJpc0ZvY3VzZWQiLCJpc1NlbGVjdGVkIiwiaW5wdXRWYWx1ZSIsImhhcyIsInByZXZfMCIsIm5leHRfMCIsInZhbHVlXzAiLCJoYXNJbWFnZUF0dGFjaG1lbnRzIiwiX3RlbXA1IiwidHJpbSIsImluY2x1ZGVzIiwibGFiZWxUZXh0IiwiaW5kZXhfMCIsImluZGV4T2YiLCJzbGljZSIsImlzT3B0aW9uRGlzYWJsZWQiLCJvcHRpb25Db2xvciIsIm1heEluZGV4V2lkdGhfMCIsIm9wdGlvbl8yIiwiaW5kZXhfMSIsImlzRmlyc3RWaXNpYmxlT3B0aW9uXzAiLCJpc0xhc3RWaXNpYmxlT3B0aW9uXzAiLCJhcmVNb3JlT3B0aW9uc0JlbG93XzAiLCJhcmVNb3JlT3B0aW9uc0Fib3ZlXzAiLCJpXzAiLCJpc0ZvY3VzZWRfMCIsImlzU2VsZWN0ZWRfMCIsImlucHV0VmFsdWVfMCIsInZhbHVlXzEiLCJwcmV2XzEiLCJuZXh0XzEiLCJ2YWx1ZV8yIiwiaGFzSW1hZ2VBdHRhY2htZW50c18wIiwiX3RlbXA2IiwibGFiZWxfMCIsImxhYmVsVGV4dF8wIiwiaW5kZXhfMiIsImlzT3B0aW9uRGlzYWJsZWRfMCIsInBhZEVuZCIsIm1heEluZGV4V2lkdGhfMSIsImhhc0lucHV0T3B0aW9ucyIsIl90ZW1wNyIsImhhc0Rlc2NyaXB0aW9ucyIsIl90ZW1wOCIsIm9wdGlvbkRhdGEiLCJvcHRpb25fMyIsImluZGV4XzMiLCJpc0ZpcnN0VmlzaWJsZU9wdGlvbl8xIiwiaXNMYXN0VmlzaWJsZU9wdGlvbl8xIiwiYXJlTW9yZU9wdGlvbnNCZWxvd18xIiwiYXJlTW9yZU9wdGlvbnNBYm92ZV8xIiwiaV8xIiwiaXNGb2N1c2VkXzEiLCJpc1NlbGVjdGVkXzEiLCJpc09wdGlvbkRpc2FibGVkXzEiLCJsYWJlbF8xIiwibGFiZWxUZXh0XzEiLCJpZHgiLCJzaG91bGRTaG93RG93bkFycm93Iiwic2hvdWxkU2hvd1VwQXJyb3ciLCJ0MTkiLCJkYXRhIiwibGFiZWxUZXh0XzIiLCJpbmRleFdpZHRoIiwiY2hlY2ttYXJrV2lkdGgiLCJtYXhMYWJlbFdpZHRoIiwiTWF0aCIsIm1heCIsInQyMCIsImRhdGFfMCIsImxhYmVsVGV4dF8zIiwiaW5kZXhXaWR0aF8wIiwiY2hlY2ttYXJrV2lkdGhfMCIsImN1cnJlbnRMYWJlbFdpZHRoIiwicGFkZGluZyIsInBvaW50ZXIiLCJhcnJvd0Rvd24iLCJhcnJvd1VwIiwidGljayIsInJlcGVhdCIsIm9wdGlvbl80IiwiaW5kZXhfNCIsImlucHV0VmFsdWVfMSIsImlzRmlyc3RWaXNpYmxlT3B0aW9uXzIiLCJpc0xhc3RWaXNpYmxlT3B0aW9uXzIiLCJhcmVNb3JlT3B0aW9uc0JlbG93XzIiLCJhcmVNb3JlT3B0aW9uc0Fib3ZlXzIiLCJpXzIiLCJpc0ZvY3VzZWRfMiIsImlzU2VsZWN0ZWRfMiIsInZhbHVlXzMiLCJwcmV2XzIiLCJuZXh0XzIiLCJ2YWx1ZV80IiwiaGFzSW1hZ2VBdHRhY2htZW50c18xIiwiX3RlbXA5IiwibGFiZWxfMiIsImxhYmVsVGV4dF80IiwiaW5kZXhfNSIsImlzRmlyc3RWaXNpYmxlT3B0aW9uXzMiLCJpc0xhc3RWaXNpYmxlT3B0aW9uXzMiLCJhcmVNb3JlT3B0aW9uc0JlbG93XzMiLCJhcmVNb3JlT3B0aW9uc0Fib3ZlXzMiLCJpXzMiLCJpc0ZvY3VzZWRfMyIsImlzU2VsZWN0ZWRfMyIsImlzT3B0aW9uRGlzYWJsZWRfMiIsImNfMyIsImMiLCJvcHRfMCIsIm9wdCIsImNfMiIsImNfMSIsImJvbGQiLCJmbGV4RGlyZWN0aW9uIiwiY29uc3QiLCJjXzAiLCJUd29Db2x1bW5Sb3ciLCJsaW5lIiwiY29sdW1uIiwiYWN0aXZlIiwiY3Vyc29yUmVmIl0sInNvdXJjZXMiOlsic2VsZWN0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0IFJlYWN0LCB7IHR5cGUgUmVhY3ROb2RlLCB1c2VFZmZlY3QsIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZURlY2xhcmVkQ3Vyc29yIH0gZnJvbSAnLi4vLi4vaW5rL2hvb2tzL3VzZS1kZWNsYXJlZC1jdXJzb3IuanMnXG5pbXBvcnQgeyBzdHJpbmdXaWR0aCB9IGZyb20gJy4uLy4uL2luay9zdHJpbmdXaWR0aC5qcydcbmltcG9ydCB7IEFuc2ksIEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGNvdW50IH0gZnJvbSAnLi4vLi4vdXRpbHMvYXJyYXkuanMnXG5pbXBvcnQgdHlwZSB7IFBhc3RlZENvbnRlbnQgfSBmcm9tICcuLi8uLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgdHlwZSB7IEltYWdlRGltZW5zaW9ucyB9IGZyb20gJy4uLy4uL3V0aWxzL2ltYWdlUmVzaXplci5qcydcbmltcG9ydCB7IFNlbGVjdElucHV0T3B0aW9uIH0gZnJvbSAnLi9zZWxlY3QtaW5wdXQtb3B0aW9uLmpzJ1xuaW1wb3J0IHsgU2VsZWN0T3B0aW9uIH0gZnJvbSAnLi9zZWxlY3Qtb3B0aW9uLmpzJ1xuaW1wb3J0IHsgdXNlU2VsZWN0SW5wdXQgfSBmcm9tICcuL3VzZS1zZWxlY3QtaW5wdXQuanMnXG5pbXBvcnQgeyB1c2VTZWxlY3RTdGF0ZSB9IGZyb20gJy4vdXNlLXNlbGVjdC1zdGF0ZS5qcydcblxuLy8gRXh0cmFjdCB0ZXh0IGNvbnRlbnQgZnJvbSBSZWFjdE5vZGUgZm9yIHdpZHRoIGNhbGN1bGF0aW9uXG5mdW5jdGlvbiBnZXRUZXh0Q29udGVudChub2RlOiBSZWFjdE5vZGUpOiBzdHJpbmcge1xuICBpZiAodHlwZW9mIG5vZGUgPT09ICdzdHJpbmcnKSByZXR1cm4gbm9kZVxuICBpZiAodHlwZW9mIG5vZGUgPT09ICdudW1iZXInKSByZXR1cm4gU3RyaW5nKG5vZGUpXG4gIGlmICghbm9kZSkgcmV0dXJuICcnXG4gIGlmIChBcnJheS5pc0FycmF5KG5vZGUpKSByZXR1cm4gbm9kZS5tYXAoZ2V0VGV4dENvbnRlbnQpLmpvaW4oJycpXG4gIGlmIChSZWFjdC5pc1ZhbGlkRWxlbWVudDx7IGNoaWxkcmVuPzogUmVhY3ROb2RlIH0+KG5vZGUpKSB7XG4gICAgcmV0dXJuIGdldFRleHRDb250ZW50KG5vZGUucHJvcHMuY2hpbGRyZW4pXG4gIH1cbiAgcmV0dXJuICcnXG59XG5cbnR5cGUgQmFzZU9wdGlvbjxUPiA9IHtcbiAgZGVzY3JpcHRpb24/OiBzdHJpbmdcbiAgZGltRGVzY3JpcHRpb24/OiBib29sZWFuXG4gIGxhYmVsOiBSZWFjdE5vZGVcbiAgdmFsdWU6IFRcbiAgZGlzYWJsZWQ/OiBib29sZWFuXG59XG5cbmV4cG9ydCB0eXBlIE9wdGlvbldpdGhEZXNjcmlwdGlvbjxUID0gc3RyaW5nPiA9XG4gIHwgKEJhc2VPcHRpb248VD4gJiB7XG4gICAgICB0eXBlPzogJ3RleHQnXG4gICAgfSlcbiAgfCAoQmFzZU9wdGlvbjxUPiAmIHtcbiAgICAgIHR5cGU6ICdpbnB1dCdcbiAgICAgIG9uQ2hhbmdlOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZFxuICAgICAgcGxhY2Vob2xkZXI/OiBzdHJpbmdcbiAgICAgIGluaXRpYWxWYWx1ZT86IHN0cmluZ1xuICAgICAgLyoqXG4gICAgICAgKiBDb250cm9scyBiZWhhdmlvciB3aGVuIHN1Ym1pdHRpbmcgd2l0aCBlbXB0eSBpbnB1dDpcbiAgICAgICAqIC0gdHJ1ZTogY2FsbHMgb25DaGFuZ2UgKHRyZWF0cyBlbXB0eSBhcyB2YWxpZCBzdWJtaXNzaW9uKVxuICAgICAgICogLSBmYWxzZSAoZGVmYXVsdCk6IGNhbGxzIG9uQ2FuY2VsICh0cmVhdHMgZW1wdHkgYXMgY2FuY2VsbGF0aW9uKVxuICAgICAgICpcbiAgICAgICAqIEFsc28gYWZmZWN0cyBpbml0aWFsIEVudGVyIHByZXNzOiB3aGVuIHRydWUsIHN1Ym1pdHMgaW1tZWRpYXRlbHk7XG4gICAgICAgKiB3aGVuIGZhbHNlLCBlbnRlcnMgaW5wdXQgbW9kZSBmaXJzdCBzbyB1c2VyIGNhbiB0eXBlLlxuICAgICAgICovXG4gICAgICBhbGxvd0VtcHR5U3VibWl0VG9DYW5jZWw/OiBib29sZWFuXG4gICAgICAvKipcbiAgICAgICAqIFdoZW4gdHJ1ZSwgYWx3YXlzIHNob3dzIHRoZSBsYWJlbCBhbG9uZ3NpZGUgdGhlIGlucHV0IHZhbHVlLCByZWdhcmRsZXNzIG9mXG4gICAgICAgKiB0aGUgZ2xvYmFsIGlubGluZURlc2NyaXB0aW9ucy9zaG93TGFiZWwgc2V0dGluZy4gVXNlIHRoaXMgd2hlbiB0aGUgbGFiZWxcbiAgICAgICAqIHByb3ZpZGVzIGltcG9ydGFudCBjb250ZXh0IHRoYXQgc2hvdWxkIGFsd2F5cyBiZSB2aXNpYmxlIChlLmcuLCBcIlllcywgYW5kIGFsbG93Li4uXCIpLlxuICAgICAgICovXG4gICAgICBzaG93TGFiZWxXaXRoVmFsdWU/OiBib29sZWFuXG4gICAgICAvKipcbiAgICAgICAqIEN1c3RvbSBzZXBhcmF0b3IgYmV0d2VlbiBsYWJlbCBhbmQgdmFsdWUgd2hlbiBzaG93TGFiZWwgaXMgdHJ1ZS5cbiAgICAgICAqIERlZmF1bHRzIHRvIFwiLCBcIi4gVXNlIFwiOiBcIiBmb3IgbGFiZWxzIHRoYXQgcmVhZCBiZXR0ZXIgd2l0aCBhIGNvbG9uLlxuICAgICAgICovXG4gICAgICBsYWJlbFZhbHVlU2VwYXJhdG9yPzogc3RyaW5nXG4gICAgICAvKipcbiAgICAgICAqIFdoZW4gdHJ1ZSwgYXV0b21hdGljYWxseSByZXNldCBjdXJzb3IgdG8gZW5kIG9mIGxpbmUgd2hlbjpcbiAgICAgICAqIC0gT3B0aW9uIGJlY29tZXMgZm9jdXNlZFxuICAgICAgICogLSBJbnB1dCB2YWx1ZSBjaGFuZ2VzXG4gICAgICAgKiBUaGlzIHByZXZlbnRzIGN1cnNvciBwb3NpdGlvbiBidWdzIHdoZW4gdGhlIGlucHV0IHZhbHVlIHVwZGF0ZXMgYXN5bmNocm9ub3VzbHkuXG4gICAgICAgKi9cbiAgICAgIHJlc2V0Q3Vyc29yT25VcGRhdGU/OiBib29sZWFuXG4gICAgfSlcblxuZXhwb3J0IHR5cGUgU2VsZWN0UHJvcHM8VD4gPSB7XG4gIC8qKlxuICAgKiBXaGVuIGRpc2FibGVkLCB1c2VyIGlucHV0IGlzIGlnbm9yZWQuXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBpc0Rpc2FibGVkPzogYm9vbGVhblxuXG4gIC8qKlxuICAgKiBXaGVuIHRydWUsIHByZXZlbnRzIHNlbGVjdGlvbiBvbiBFbnRlciBidXQgYWxsb3dzIHNjcm9sbGluZy5cbiAgICpcbiAgICogQGRlZmF1bHQgZmFsc2VcbiAgICovXG4gIHJlYWRvbmx5IGRpc2FibGVTZWxlY3Rpb24/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIFdoZW4gdHJ1ZSwgaGlkZXMgdGhlIG51bWVyaWMgaW5kZXhlcyBuZXh0IHRvIGVhY2ggb3B0aW9uLlxuICAgKlxuICAgKiBAZGVmYXVsdCBmYWxzZVxuICAgKi9cbiAgcmVhZG9ubHkgaGlkZUluZGV4ZXM/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIE51bWJlciBvZiB2aXNpYmxlIG9wdGlvbnMuXG4gICAqXG4gICAqIEBkZWZhdWx0IDVcbiAgICovXG4gIHJlYWRvbmx5IHZpc2libGVPcHRpb25Db3VudD86IG51bWJlclxuXG4gIC8qKlxuICAgKiBIaWdobGlnaHQgdGV4dCBpbiBvcHRpb24gbGFiZWxzLlxuICAgKi9cbiAgcmVhZG9ubHkgaGlnaGxpZ2h0VGV4dD86IHN0cmluZ1xuXG4gIC8qKlxuICAgKiBPcHRpb25zLlxuICAgKi9cbiAgcmVhZG9ubHkgb3B0aW9uczogT3B0aW9uV2l0aERlc2NyaXB0aW9uPFQ+W11cblxuICAvKipcbiAgICogRGVmYXVsdCB2YWx1ZS5cbiAgICovXG4gIHJlYWRvbmx5IGRlZmF1bHRWYWx1ZT86IFRcblxuICAvKipcbiAgICogQ2FsbGJhY2sgd2hlbiBjYW5jZWwgaXMgcHJlc3NlZC5cbiAgICovXG4gIHJlYWRvbmx5IG9uQ2FuY2VsPzogKCkgPT4gdm9pZFxuXG4gIC8qKlxuICAgKiBDYWxsYmFjayB3aGVuIHNlbGVjdGVkIG9wdGlvbiBjaGFuZ2VzLlxuICAgKi9cbiAgcmVhZG9ubHkgb25DaGFuZ2U/OiAodmFsdWU6IFQpID0+IHZvaWRcblxuICAvKipcbiAgICogQ2FsbGJhY2sgd2hlbiBmb2N1c2VkIG9wdGlvbiBjaGFuZ2VzLlxuICAgKiBOb3RlOiBUaGlzIGlzIGZvciBvbmUtd2F5IG5vdGlmaWNhdGlvbiBvbmx5LiBBdm9pZCBjb21iaW5pbmcgd2l0aCBmb2N1c1ZhbHVlXG4gICAqIGZvciBiaWRpcmVjdGlvbmFsIHN5bmMsIGFzIHRoaXMgY2FuIGNhdXNlIGZlZWRiYWNrIGxvb3BzLlxuICAgKi9cbiAgcmVhZG9ubHkgb25Gb2N1cz86ICh2YWx1ZTogVCkgPT4gdm9pZFxuXG4gIC8qKlxuICAgKiBJbml0aWFsIHZhbHVlIHRvIGZvY3VzLiBUaGlzIGlzIHVzZWQgdG8gc2V0IGZvY3VzIHdoZW4gdGhlIGNvbXBvbmVudCBtb3VudHMuXG4gICAqL1xuICByZWFkb25seSBkZWZhdWx0Rm9jdXNWYWx1ZT86IFRcblxuICAvKipcbiAgICogTGF5b3V0IG9mIHRoZSBvcHRpb25zLlxuICAgKiAtIGBjb21wYWN0YCAoZGVmYXVsdCkgdHJpZXMgdG8gdXNlIG9uZSBsaW5lIHBlciBvcHRpb25cbiAgICogLSBgZXhwYW5kZWRgIHVzZXMgbXVsdGlwbGUgbGluZXMgYW5kIGFuIGVtcHR5IGxpbmUgYmV0d2VlbiBvcHRpb25zXG4gICAqIC0gYGNvbXBhY3QtdmVydGljYWxgIHVzZXMgY29tcGFjdCBpbmRleCBmb3JtYXR0aW5nIHdpdGggZGVzY3JpcHRpb25zIGJlbG93IGxhYmVsc1xuICAgKi9cbiAgcmVhZG9ubHkgbGF5b3V0PzogJ2NvbXBhY3QnIHwgJ2V4cGFuZGVkJyB8ICdjb21wYWN0LXZlcnRpY2FsJ1xuXG4gIC8qKlxuICAgKiBXaGVuIHRydWUsIGRlc2NyaXB0aW9ucyBhcmUgcmVuZGVyZWQgaW5saW5lIGFmdGVyIHRoZSBsYWJlbCBpbnN0ZWFkIG9mXG4gICAqIGluIGEgc2VwYXJhdGUgY29sdW1uLiBVc2UgdGhpcyBmb3Igc2hvcnQgZGVzY3JpcHRpb25zIGxpa2UgaGludHMuXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICByZWFkb25seSBpbmxpbmVEZXNjcmlwdGlvbnM/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIENhbGxiYWNrIHdoZW4gdXNlciBwcmVzc2VzIHVwIGZyb20gdGhlIGZpcnN0IGl0ZW0uXG4gICAqIElmIHByb3ZpZGVkLCBuYXZpZ2F0aW9uIHdpbGwgbm90IHdyYXAgdG8gdGhlIGxhc3QgaXRlbS5cbiAgICovXG4gIHJlYWRvbmx5IG9uVXBGcm9tRmlyc3RJdGVtPzogKCkgPT4gdm9pZFxuXG4gIC8qKlxuICAgKiBDYWxsYmFjayB3aGVuIHVzZXIgcHJlc3NlcyBkb3duIGZyb20gdGhlIGxhc3QgaXRlbS5cbiAgICogSWYgcHJvdmlkZWQsIG5hdmlnYXRpb24gd2lsbCBub3Qgd3JhcCB0byB0aGUgZmlyc3QgaXRlbS5cbiAgICovXG4gIHJlYWRvbmx5IG9uRG93bkZyb21MYXN0SXRlbT86ICgpID0+IHZvaWRcblxuICAvKipcbiAgICogQ2FsbGJhY2sgd2hlbiBpbnB1dCBtb2RlIHNob3VsZCBiZSB0b2dnbGVkIGZvciBhbiBvcHRpb24uXG4gICAqIENhbGxlZCB3aGVuIFRhYiBpcyBwcmVzc2VkICh0byBlbnRlciBvciBleGl0IGlucHV0IG1vZGUpLlxuICAgKi9cbiAgcmVhZG9ubHkgb25JbnB1dE1vZGVUb2dnbGU/OiAodmFsdWU6IFQpID0+IHZvaWRcblxuICAvKipcbiAgICogQ2FsbGJhY2sgdG8gb3BlbiBleHRlcm5hbCBlZGl0b3IgZm9yIGVkaXRpbmcgaW5wdXQgb3B0aW9uIHZhbHVlcy5cbiAgICogV2hlbiBwcm92aWRlZCwgY3RybCtnIHdpbGwgdHJpZ2dlciB0aGlzIGNhbGxiYWNrIGluIGlucHV0IG9wdGlvbnNcbiAgICogd2l0aCB0aGUgY3VycmVudCB2YWx1ZSBhbmQgYSBzZXR0ZXIgZnVuY3Rpb24gdG8gdXBkYXRlIHRoZSBpbnRlcm5hbCBzdGF0ZS5cbiAgICovXG4gIHJlYWRvbmx5IG9uT3BlbkVkaXRvcj86IChcbiAgICBjdXJyZW50VmFsdWU6IHN0cmluZyxcbiAgICBzZXRWYWx1ZTogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWQsXG4gICkgPT4gdm9pZFxuXG4gIC8qKlxuICAgKiBPcHRpb25hbCBjYWxsYmFjayB3aGVuIGFuIGltYWdlIGlzIHBhc3RlZCBpbnRvIGFuIGlucHV0IG9wdGlvbi5cbiAgICovXG4gIHJlYWRvbmx5IG9uSW1hZ2VQYXN0ZT86IChcbiAgICBiYXNlNjRJbWFnZTogc3RyaW5nLFxuICAgIG1lZGlhVHlwZT86IHN0cmluZyxcbiAgICBmaWxlbmFtZT86IHN0cmluZyxcbiAgICBkaW1lbnNpb25zPzogSW1hZ2VEaW1lbnNpb25zLFxuICAgIHNvdXJjZVBhdGg/OiBzdHJpbmcsXG4gICkgPT4gdm9pZFxuXG4gIC8qKlxuICAgKiBQYXN0ZWQgY29udGVudCB0byBkaXNwbGF5IGlubGluZSBpbiBpbnB1dCBvcHRpb25zLlxuICAgKi9cbiAgcmVhZG9ubHkgcGFzdGVkQ29udGVudHM/OiBSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50PlxuXG4gIC8qKlxuICAgKiBDYWxsYmFjayB0byByZW1vdmUgYSBwYXN0ZWQgaW1hZ2UgYnkgaXRzIElELlxuICAgKi9cbiAgcmVhZG9ubHkgb25SZW1vdmVJbWFnZT86IChpZDogbnVtYmVyKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTZWxlY3Q8VD4oe1xuICBpc0Rpc2FibGVkID0gZmFsc2UsXG4gIGhpZGVJbmRleGVzID0gZmFsc2UsXG4gIHZpc2libGVPcHRpb25Db3VudCA9IDUsXG4gIGhpZ2hsaWdodFRleHQsXG4gIG9wdGlvbnMsXG4gIGRlZmF1bHRWYWx1ZSxcbiAgb25DYW5jZWwsXG4gIG9uQ2hhbmdlLFxuICBvbkZvY3VzLFxuICBkZWZhdWx0Rm9jdXNWYWx1ZSxcbiAgbGF5b3V0ID0gJ2NvbXBhY3QnLFxuICBkaXNhYmxlU2VsZWN0aW9uID0gZmFsc2UsXG4gIGlubGluZURlc2NyaXB0aW9ucyA9IGZhbHNlLFxuICBvblVwRnJvbUZpcnN0SXRlbSxcbiAgb25Eb3duRnJvbUxhc3RJdGVtLFxuICBvbklucHV0TW9kZVRvZ2dsZSxcbiAgb25PcGVuRWRpdG9yLFxuICBvbkltYWdlUGFzdGUsXG4gIHBhc3RlZENvbnRlbnRzLFxuICBvblJlbW92ZUltYWdlLFxufTogU2VsZWN0UHJvcHM8VD4pOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBJbWFnZSBzZWxlY3Rpb24gbW9kZSBzdGF0ZVxuICBjb25zdCBbaW1hZ2VzU2VsZWN0ZWQsIHNldEltYWdlc1NlbGVjdGVkXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbc2VsZWN0ZWRJbWFnZUluZGV4LCBzZXRTZWxlY3RlZEltYWdlSW5kZXhdID0gdXNlU3RhdGUoMClcblxuICAvLyBTdGF0ZSBmb3IgaW5wdXQgdHlwZSBvcHRpb25zXG4gIGNvbnN0IFtpbnB1dFZhbHVlcywgc2V0SW5wdXRWYWx1ZXNdID0gdXNlU3RhdGU8TWFwPFQsIHN0cmluZz4+KCgpID0+IHtcbiAgICBjb25zdCBpbml0aWFsTWFwID0gbmV3IE1hcDxULCBzdHJpbmc+KClcbiAgICBvcHRpb25zLmZvckVhY2gob3B0aW9uID0+IHtcbiAgICAgIGlmIChvcHRpb24udHlwZSA9PT0gJ2lucHV0JyAmJiBvcHRpb24uaW5pdGlhbFZhbHVlKSB7XG4gICAgICAgIGluaXRpYWxNYXAuc2V0KG9wdGlvbi52YWx1ZSwgb3B0aW9uLmluaXRpYWxWYWx1ZSlcbiAgICAgIH1cbiAgICB9KVxuICAgIHJldHVybiBpbml0aWFsTWFwXG4gIH0pXG5cbiAgLy8gVHJhY2sgdGhlIGxhc3QgaW5pdGlhbFZhbHVlIHdlIHN5bmNlZCwgc28gd2UgY2FuIGRldGVjdCB1c2VyIGVkaXRzXG4gIGNvbnN0IGxhc3RJbml0aWFsVmFsdWVzID0gdXNlUmVmPE1hcDxULCBzdHJpbmc+PihuZXcgTWFwKCkpXG5cbiAgLy8gU3luYyBpbml0aWFsVmFsdWUgY2hhbmdlcyB0byBpbnB1dFZhbHVlcyBzdGF0ZSwgYnV0IG9ubHkgaWYgdXNlciBoYXNuJ3QgZWRpdGVkXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgZm9yIChjb25zdCBvcHRpb24gb2Ygb3B0aW9ucykge1xuICAgICAgaWYgKG9wdGlvbi50eXBlID09PSAnaW5wdXQnICYmIG9wdGlvbi5pbml0aWFsVmFsdWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjb25zdCBsYXN0SW5pdGlhbCA9IGxhc3RJbml0aWFsVmFsdWVzLmN1cnJlbnQuZ2V0KG9wdGlvbi52YWx1ZSkgPz8gJydcbiAgICAgICAgY29uc3QgY3VycmVudFZhbHVlID0gaW5wdXRWYWx1ZXMuZ2V0KG9wdGlvbi52YWx1ZSkgPz8gJydcbiAgICAgICAgY29uc3QgbmV3SW5pdGlhbCA9IG9wdGlvbi5pbml0aWFsVmFsdWVcblxuICAgICAgICAvLyBPbmx5IHVwZGF0ZSBpZjpcbiAgICAgICAgLy8gMS4gVGhlIGluaXRpYWxWYWx1ZSBoYXMgY2hhbmdlZFxuICAgICAgICAvLyAyLiBUaGUgdXNlciBoYXNuJ3QgZWRpdGVkIChjdXJyZW50IHZhbHVlIHN0aWxsIG1hdGNoZXMgdGhlIGxhc3QgaW5pdGlhbFZhbHVlIHdlIHNldClcbiAgICAgICAgaWYgKG5ld0luaXRpYWwgIT09IGxhc3RJbml0aWFsICYmIGN1cnJlbnRWYWx1ZSA9PT0gbGFzdEluaXRpYWwpIHtcbiAgICAgICAgICBzZXRJbnB1dFZhbHVlcyhwcmV2ID0+IHtcbiAgICAgICAgICAgIGNvbnN0IG5leHQgPSBuZXcgTWFwKHByZXYpXG4gICAgICAgICAgICBuZXh0LnNldChvcHRpb24udmFsdWUsIG5ld0luaXRpYWwpXG4gICAgICAgICAgICByZXR1cm4gbmV4dFxuICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICAvLyBBbHdheXMgdHJhY2sgdGhlIGxhdGVzdCBpbml0aWFsVmFsdWVcbiAgICAgICAgbGFzdEluaXRpYWxWYWx1ZXMuY3VycmVudC5zZXQob3B0aW9uLnZhbHVlLCBuZXdJbml0aWFsKVxuICAgICAgfVxuICAgIH1cbiAgfSwgW29wdGlvbnMsIGlucHV0VmFsdWVzXSlcblxuICBjb25zdCBzdGF0ZSA9IHVzZVNlbGVjdFN0YXRlKHtcbiAgICB2aXNpYmxlT3B0aW9uQ291bnQsXG4gICAgb3B0aW9ucyxcbiAgICBkZWZhdWx0VmFsdWUsXG4gICAgb25DaGFuZ2UsXG4gICAgb25DYW5jZWwsXG4gICAgb25Gb2N1cyxcbiAgICBmb2N1c1ZhbHVlOiBkZWZhdWx0Rm9jdXNWYWx1ZSxcbiAgfSlcblxuICB1c2VTZWxlY3RJbnB1dCh7XG4gICAgaXNEaXNhYmxlZCxcbiAgICBkaXNhYmxlU2VsZWN0aW9uOiBkaXNhYmxlU2VsZWN0aW9uIHx8IChoaWRlSW5kZXhlcyA/ICdudW1lcmljJyA6IGZhbHNlKSxcbiAgICBzdGF0ZSxcbiAgICBvcHRpb25zLFxuICAgIGlzTXVsdGlTZWxlY3Q6IGZhbHNlLCAvLyBTZWxlY3QgaXMgYWx3YXlzIHNpbmdsZS1jaG9pY2VcbiAgICBvblVwRnJvbUZpcnN0SXRlbSxcbiAgICBvbkRvd25Gcm9tTGFzdEl0ZW0sXG4gICAgb25JbnB1dE1vZGVUb2dnbGUsXG4gICAgaW5wdXRWYWx1ZXMsXG4gICAgaW1hZ2VzU2VsZWN0ZWQsXG4gICAgb25FbnRlckltYWdlU2VsZWN0aW9uOiAoKSA9PiB7XG4gICAgICBpZiAoXG4gICAgICAgIHBhc3RlZENvbnRlbnRzICYmXG4gICAgICAgIE9iamVjdC52YWx1ZXMocGFzdGVkQ29udGVudHMpLnNvbWUoYyA9PiBjLnR5cGUgPT09ICdpbWFnZScpXG4gICAgICApIHtcbiAgICAgICAgY29uc3QgaW1hZ2VDb3VudCA9IGNvdW50KFxuICAgICAgICAgIE9iamVjdC52YWx1ZXMocGFzdGVkQ29udGVudHMpLFxuICAgICAgICAgIGMgPT4gYy50eXBlID09PSAnaW1hZ2UnLFxuICAgICAgICApXG4gICAgICAgIHNldEltYWdlc1NlbGVjdGVkKHRydWUpXG4gICAgICAgIHNldFNlbGVjdGVkSW1hZ2VJbmRleChpbWFnZUNvdW50IC0gMSlcbiAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH0sXG4gIH0pXG5cbiAgY29uc3Qgc3R5bGVzID0ge1xuICAgIGNvbnRhaW5lcjogKCkgPT4gKHsgZmxleERpcmVjdGlvbjogJ2NvbHVtbicgYXMgY29uc3QgfSksXG4gICAgaGlnaGxpZ2h0ZWRUZXh0OiAoKSA9PiAoeyBib2xkOiB0cnVlIH0pLFxuICB9XG5cbiAgaWYgKGxheW91dCA9PT0gJ2V4cGFuZGVkJykge1xuICAgIGNvbnN0IG1heEluZGV4V2lkdGggPSBzdGF0ZS5vcHRpb25zLmxlbmd0aC50b1N0cmluZygpLmxlbmd0aFxuXG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggey4uLnN0eWxlcy5jb250YWluZXIoKX0+XG4gICAgICAgIHtzdGF0ZS52aXNpYmxlT3B0aW9ucy5tYXAoKG9wdGlvbiwgaW5kZXgpID0+IHtcbiAgICAgICAgICBjb25zdCBpc0ZpcnN0VmlzaWJsZU9wdGlvbiA9IG9wdGlvbi5pbmRleCA9PT0gc3RhdGUudmlzaWJsZUZyb21JbmRleFxuICAgICAgICAgIGNvbnN0IGlzTGFzdFZpc2libGVPcHRpb24gPSBvcHRpb24uaW5kZXggPT09IHN0YXRlLnZpc2libGVUb0luZGV4IC0gMVxuICAgICAgICAgIGNvbnN0IGFyZU1vcmVPcHRpb25zQmVsb3cgPSBzdGF0ZS52aXNpYmxlVG9JbmRleCA8IG9wdGlvbnMubGVuZ3RoXG4gICAgICAgICAgY29uc3QgYXJlTW9yZU9wdGlvbnNBYm92ZSA9IHN0YXRlLnZpc2libGVGcm9tSW5kZXggPiAwXG5cbiAgICAgICAgICBjb25zdCBpID0gc3RhdGUudmlzaWJsZUZyb21JbmRleCArIGluZGV4ICsgMVxuXG4gICAgICAgICAgY29uc3QgaXNGb2N1c2VkID0gIWlzRGlzYWJsZWQgJiYgc3RhdGUuZm9jdXNlZFZhbHVlID09PSBvcHRpb24udmFsdWVcbiAgICAgICAgICBjb25zdCBpc1NlbGVjdGVkID0gc3RhdGUudmFsdWUgPT09IG9wdGlvbi52YWx1ZVxuXG4gICAgICAgICAgLy8gSGFuZGxlIGlucHV0IHR5cGUgb3B0aW9uc1xuICAgICAgICAgIGlmIChvcHRpb24udHlwZSA9PT0gJ2lucHV0Jykge1xuICAgICAgICAgICAgY29uc3QgaW5wdXRWYWx1ZSA9IGlucHV0VmFsdWVzLmhhcyhvcHRpb24udmFsdWUpXG4gICAgICAgICAgICAgID8gaW5wdXRWYWx1ZXMuZ2V0KG9wdGlvbi52YWx1ZSkhXG4gICAgICAgICAgICAgIDogb3B0aW9uLmluaXRpYWxWYWx1ZSB8fCAnJ1xuXG4gICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICA8U2VsZWN0SW5wdXRPcHRpb25cbiAgICAgICAgICAgICAgICBrZXk9e1N0cmluZyhvcHRpb24udmFsdWUpfVxuICAgICAgICAgICAgICAgIG9wdGlvbj17b3B0aW9ufVxuICAgICAgICAgICAgICAgIGlzRm9jdXNlZD17aXNGb2N1c2VkfVxuICAgICAgICAgICAgICAgIGlzU2VsZWN0ZWQ9e2lzU2VsZWN0ZWR9XG4gICAgICAgICAgICAgICAgc2hvdWxkU2hvd0Rvd25BcnJvdz17YXJlTW9yZU9wdGlvbnNCZWxvdyAmJiBpc0xhc3RWaXNpYmxlT3B0aW9ufVxuICAgICAgICAgICAgICAgIHNob3VsZFNob3dVcEFycm93PXthcmVNb3JlT3B0aW9uc0Fib3ZlICYmIGlzRmlyc3RWaXNpYmxlT3B0aW9ufVxuICAgICAgICAgICAgICAgIG1heEluZGV4V2lkdGg9e21heEluZGV4V2lkdGh9XG4gICAgICAgICAgICAgICAgaW5kZXg9e2l9XG4gICAgICAgICAgICAgICAgaW5wdXRWYWx1ZT17aW5wdXRWYWx1ZX1cbiAgICAgICAgICAgICAgICBvbklucHV0Q2hhbmdlPXt2YWx1ZSA9PiB7XG4gICAgICAgICAgICAgICAgICBzZXRJbnB1dFZhbHVlcyhwcmV2ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgbmV4dCA9IG5ldyBNYXAocHJldilcbiAgICAgICAgICAgICAgICAgICAgbmV4dC5zZXQob3B0aW9uLnZhbHVlLCB2YWx1ZSlcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5leHRcbiAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgICAgICBvblN1Ym1pdD17KHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGhhc0ltYWdlQXR0YWNobWVudHMgPVxuICAgICAgICAgICAgICAgICAgICBwYXN0ZWRDb250ZW50cyAmJlxuICAgICAgICAgICAgICAgICAgICBPYmplY3QudmFsdWVzKHBhc3RlZENvbnRlbnRzKS5zb21lKGMgPT4gYy50eXBlID09PSAnaW1hZ2UnKVxuICAgICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgICB2YWx1ZS50cmltKCkgfHxcbiAgICAgICAgICAgICAgICAgICAgaGFzSW1hZ2VBdHRhY2htZW50cyB8fFxuICAgICAgICAgICAgICAgICAgICBvcHRpb24uYWxsb3dFbXB0eVN1Ym1pdFRvQ2FuY2VsXG4gICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgb25DaGFuZ2U/LihvcHRpb24udmFsdWUpXG4gICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBvbkNhbmNlbD8uKClcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgICAgIG9uRXhpdD17b25DYW5jZWx9XG4gICAgICAgICAgICAgICAgbGF5b3V0PVwiZXhwYW5kZWRcIlxuICAgICAgICAgICAgICAgIHNob3dMYWJlbD17aW5saW5lRGVzY3JpcHRpb25zfVxuICAgICAgICAgICAgICAgIG9uT3BlbkVkaXRvcj17b25PcGVuRWRpdG9yfVxuICAgICAgICAgICAgICAgIHJlc2V0Q3Vyc29yT25VcGRhdGU9e29wdGlvbi5yZXNldEN1cnNvck9uVXBkYXRlfVxuICAgICAgICAgICAgICAgIG9uSW1hZ2VQYXN0ZT17b25JbWFnZVBhc3RlfVxuICAgICAgICAgICAgICAgIHBhc3RlZENvbnRlbnRzPXtwYXN0ZWRDb250ZW50c31cbiAgICAgICAgICAgICAgICBvblJlbW92ZUltYWdlPXtvblJlbW92ZUltYWdlfVxuICAgICAgICAgICAgICAgIGltYWdlc1NlbGVjdGVkPXtpbWFnZXNTZWxlY3RlZH1cbiAgICAgICAgICAgICAgICBzZWxlY3RlZEltYWdlSW5kZXg9e3NlbGVjdGVkSW1hZ2VJbmRleH1cbiAgICAgICAgICAgICAgICBvbkltYWdlc1NlbGVjdGVkQ2hhbmdlPXtzZXRJbWFnZXNTZWxlY3RlZH1cbiAgICAgICAgICAgICAgICBvblNlbGVjdGVkSW1hZ2VJbmRleENoYW5nZT17c2V0U2VsZWN0ZWRJbWFnZUluZGV4fVxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEhhbmRsZSB0ZXh0IHR5cGUgb3B0aW9uc1xuICAgICAgICAgIGxldCBsYWJlbDogUmVhY3ROb2RlID0gb3B0aW9uLmxhYmVsXG5cbiAgICAgICAgICAvLyBPbmx5IGFwcGx5IGhpZ2hsaWdodCB3aGVuIGxhYmVsIGlzIGEgc3RyaW5nXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgdHlwZW9mIG9wdGlvbi5sYWJlbCA9PT0gJ3N0cmluZycgJiZcbiAgICAgICAgICAgIGhpZ2hsaWdodFRleHQgJiZcbiAgICAgICAgICAgIG9wdGlvbi5sYWJlbC5pbmNsdWRlcyhoaWdobGlnaHRUZXh0KVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgY29uc3QgbGFiZWxUZXh0ID0gb3B0aW9uLmxhYmVsXG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IGxhYmVsVGV4dC5pbmRleE9mKGhpZ2hsaWdodFRleHQpXG5cbiAgICAgICAgICAgIGxhYmVsID0gKFxuICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgIHtsYWJlbFRleHQuc2xpY2UoMCwgaW5kZXgpfVxuICAgICAgICAgICAgICAgIDxUZXh0IHsuLi5zdHlsZXMuaGlnaGxpZ2h0ZWRUZXh0KCl9PntoaWdobGlnaHRUZXh0fTwvVGV4dD5cbiAgICAgICAgICAgICAgICB7bGFiZWxUZXh0LnNsaWNlKGluZGV4ICsgaGlnaGxpZ2h0VGV4dC5sZW5ndGgpfVxuICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBpc09wdGlvbkRpc2FibGVkID0gb3B0aW9uLmRpc2FibGVkID09PSB0cnVlXG4gICAgICAgICAgY29uc3Qgb3B0aW9uQ29sb3IgPSBpc09wdGlvbkRpc2FibGVkXG4gICAgICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICAgICAgOiBpc1NlbGVjdGVkXG4gICAgICAgICAgICAgID8gJ3N1Y2Nlc3MnXG4gICAgICAgICAgICAgIDogaXNGb2N1c2VkXG4gICAgICAgICAgICAgICAgPyAnc3VnZ2VzdGlvbidcbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuXG4gICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIDxCb3hcbiAgICAgICAgICAgICAga2V5PXtTdHJpbmcob3B0aW9uLnZhbHVlKX1cbiAgICAgICAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgICAgICAgIGZsZXhTaHJpbms9ezB9XG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgIDxTZWxlY3RPcHRpb25cbiAgICAgICAgICAgICAgICBpc0ZvY3VzZWQ9e2lzRm9jdXNlZH1cbiAgICAgICAgICAgICAgICBpc1NlbGVjdGVkPXtpc1NlbGVjdGVkfVxuICAgICAgICAgICAgICAgIHNob3VsZFNob3dEb3duQXJyb3c9e2FyZU1vcmVPcHRpb25zQmVsb3cgJiYgaXNMYXN0VmlzaWJsZU9wdGlvbn1cbiAgICAgICAgICAgICAgICBzaG91bGRTaG93VXBBcnJvdz17YXJlTW9yZU9wdGlvbnNBYm92ZSAmJiBpc0ZpcnN0VmlzaWJsZU9wdGlvbn1cbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPXtpc09wdGlvbkRpc2FibGVkfSBjb2xvcj17b3B0aW9uQ29sb3J9PlxuICAgICAgICAgICAgICAgICAge2xhYmVsfVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9TZWxlY3RPcHRpb24+XG4gICAgICAgICAgICAgIHtvcHRpb24uZGVzY3JpcHRpb24gJiYgKFxuICAgICAgICAgICAgICAgIDxCb3ggcGFkZGluZ0xlZnQ9ezJ9PlxuICAgICAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICAgICAgZGltQ29sb3I9e1xuICAgICAgICAgICAgICAgICAgICAgIGlzT3B0aW9uRGlzYWJsZWQgfHwgb3B0aW9uLmRpbURlc2NyaXB0aW9uICE9PSBmYWxzZVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbG9yPXtvcHRpb25Db2xvcn1cbiAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgPEFuc2k+e29wdGlvbi5kZXNjcmlwdGlvbn08L0Fuc2k+XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgIDxUZXh0PiA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICApXG4gICAgICAgIH0pfVxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgaWYgKGxheW91dCA9PT0gJ2NvbXBhY3QtdmVydGljYWwnKSB7XG4gICAgY29uc3QgbWF4SW5kZXhXaWR0aCA9IGhpZGVJbmRleGVzXG4gICAgICA/IDBcbiAgICAgIDogc3RhdGUub3B0aW9ucy5sZW5ndGgudG9TdHJpbmcoKS5sZW5ndGhcblxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IHsuLi5zdHlsZXMuY29udGFpbmVyKCl9PlxuICAgICAgICB7c3RhdGUudmlzaWJsZU9wdGlvbnMubWFwKChvcHRpb24sIGluZGV4KSA9PiB7XG4gICAgICAgICAgY29uc3QgaXNGaXJzdFZpc2libGVPcHRpb24gPSBvcHRpb24uaW5kZXggPT09IHN0YXRlLnZpc2libGVGcm9tSW5kZXhcbiAgICAgICAgICBjb25zdCBpc0xhc3RWaXNpYmxlT3B0aW9uID0gb3B0aW9uLmluZGV4ID09PSBzdGF0ZS52aXNpYmxlVG9JbmRleCAtIDFcbiAgICAgICAgICBjb25zdCBhcmVNb3JlT3B0aW9uc0JlbG93ID0gc3RhdGUudmlzaWJsZVRvSW5kZXggPCBvcHRpb25zLmxlbmd0aFxuICAgICAgICAgIGNvbnN0IGFyZU1vcmVPcHRpb25zQWJvdmUgPSBzdGF0ZS52aXNpYmxlRnJvbUluZGV4ID4gMFxuXG4gICAgICAgICAgY29uc3QgaSA9IHN0YXRlLnZpc2libGVGcm9tSW5kZXggKyBpbmRleCArIDFcblxuICAgICAgICAgIGNvbnN0IGlzRm9jdXNlZCA9ICFpc0Rpc2FibGVkICYmIHN0YXRlLmZvY3VzZWRWYWx1ZSA9PT0gb3B0aW9uLnZhbHVlXG4gICAgICAgICAgY29uc3QgaXNTZWxlY3RlZCA9IHN0YXRlLnZhbHVlID09PSBvcHRpb24udmFsdWVcblxuICAgICAgICAgIC8vIEhhbmRsZSBpbnB1dCB0eXBlIG9wdGlvbnNcbiAgICAgICAgICBpZiAob3B0aW9uLnR5cGUgPT09ICdpbnB1dCcpIHtcbiAgICAgICAgICAgIGNvbnN0IGlucHV0VmFsdWUgPSBpbnB1dFZhbHVlcy5oYXMob3B0aW9uLnZhbHVlKVxuICAgICAgICAgICAgICA/IGlucHV0VmFsdWVzLmdldChvcHRpb24udmFsdWUpIVxuICAgICAgICAgICAgICA6IG9wdGlvbi5pbml0aWFsVmFsdWUgfHwgJydcblxuICAgICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICAgPFNlbGVjdElucHV0T3B0aW9uXG4gICAgICAgICAgICAgICAga2V5PXtTdHJpbmcob3B0aW9uLnZhbHVlKX1cbiAgICAgICAgICAgICAgICBvcHRpb249e29wdGlvbn1cbiAgICAgICAgICAgICAgICBpc0ZvY3VzZWQ9e2lzRm9jdXNlZH1cbiAgICAgICAgICAgICAgICBpc1NlbGVjdGVkPXtpc1NlbGVjdGVkfVxuICAgICAgICAgICAgICAgIHNob3VsZFNob3dEb3duQXJyb3c9e2FyZU1vcmVPcHRpb25zQmVsb3cgJiYgaXNMYXN0VmlzaWJsZU9wdGlvbn1cbiAgICAgICAgICAgICAgICBzaG91bGRTaG93VXBBcnJvdz17YXJlTW9yZU9wdGlvbnNBYm92ZSAmJiBpc0ZpcnN0VmlzaWJsZU9wdGlvbn1cbiAgICAgICAgICAgICAgICBtYXhJbmRleFdpZHRoPXttYXhJbmRleFdpZHRofVxuICAgICAgICAgICAgICAgIGluZGV4PXtpfVxuICAgICAgICAgICAgICAgIGlucHV0VmFsdWU9e2lucHV0VmFsdWV9XG4gICAgICAgICAgICAgICAgb25JbnB1dENoYW5nZT17dmFsdWUgPT4ge1xuICAgICAgICAgICAgICAgICAgc2V0SW5wdXRWYWx1ZXMocHJldiA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IG5leHQgPSBuZXcgTWFwKHByZXYpXG4gICAgICAgICAgICAgICAgICAgIG5leHQuc2V0KG9wdGlvbi52YWx1ZSwgdmFsdWUpXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBuZXh0XG4gICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAgICAgb25TdWJtaXQ9eyh2YWx1ZTogc3RyaW5nKSA9PiB7XG4gICAgICAgICAgICAgICAgICBjb25zdCBoYXNJbWFnZUF0dGFjaG1lbnRzID1cbiAgICAgICAgICAgICAgICAgICAgcGFzdGVkQ29udGVudHMgJiZcbiAgICAgICAgICAgICAgICAgICAgT2JqZWN0LnZhbHVlcyhwYXN0ZWRDb250ZW50cykuc29tZShjID0+IGMudHlwZSA9PT0gJ2ltYWdlJylcbiAgICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgdmFsdWUudHJpbSgpIHx8XG4gICAgICAgICAgICAgICAgICAgIGhhc0ltYWdlQXR0YWNobWVudHMgfHxcbiAgICAgICAgICAgICAgICAgICAgb3B0aW9uLmFsbG93RW1wdHlTdWJtaXRUb0NhbmNlbFxuICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgIG9uQ2hhbmdlPy4ob3B0aW9uLnZhbHVlKVxuICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgb25DYW5jZWw/LigpXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgICAgICBvbkV4aXQ9e29uQ2FuY2VsfVxuICAgICAgICAgICAgICAgIGxheW91dD1cImNvbXBhY3RcIlxuICAgICAgICAgICAgICAgIHNob3dMYWJlbD17aW5saW5lRGVzY3JpcHRpb25zfVxuICAgICAgICAgICAgICAgIG9uT3BlbkVkaXRvcj17b25PcGVuRWRpdG9yfVxuICAgICAgICAgICAgICAgIHJlc2V0Q3Vyc29yT25VcGRhdGU9e29wdGlvbi5yZXNldEN1cnNvck9uVXBkYXRlfVxuICAgICAgICAgICAgICAgIG9uSW1hZ2VQYXN0ZT17b25JbWFnZVBhc3RlfVxuICAgICAgICAgICAgICAgIHBhc3RlZENvbnRlbnRzPXtwYXN0ZWRDb250ZW50c31cbiAgICAgICAgICAgICAgICBvblJlbW92ZUltYWdlPXtvblJlbW92ZUltYWdlfVxuICAgICAgICAgICAgICAgIGltYWdlc1NlbGVjdGVkPXtpbWFnZXNTZWxlY3RlZH1cbiAgICAgICAgICAgICAgICBzZWxlY3RlZEltYWdlSW5kZXg9e3NlbGVjdGVkSW1hZ2VJbmRleH1cbiAgICAgICAgICAgICAgICBvbkltYWdlc1NlbGVjdGVkQ2hhbmdlPXtzZXRJbWFnZXNTZWxlY3RlZH1cbiAgICAgICAgICAgICAgICBvblNlbGVjdGVkSW1hZ2VJbmRleENoYW5nZT17c2V0U2VsZWN0ZWRJbWFnZUluZGV4fVxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIEhhbmRsZSB0ZXh0IHR5cGUgb3B0aW9uc1xuICAgICAgICAgIGxldCBsYWJlbDogUmVhY3ROb2RlID0gb3B0aW9uLmxhYmVsXG5cbiAgICAgICAgICAvLyBPbmx5IGFwcGx5IGhpZ2hsaWdodCB3aGVuIGxhYmVsIGlzIGEgc3RyaW5nXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgdHlwZW9mIG9wdGlvbi5sYWJlbCA9PT0gJ3N0cmluZycgJiZcbiAgICAgICAgICAgIGhpZ2hsaWdodFRleHQgJiZcbiAgICAgICAgICAgIG9wdGlvbi5sYWJlbC5pbmNsdWRlcyhoaWdobGlnaHRUZXh0KVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgY29uc3QgbGFiZWxUZXh0ID0gb3B0aW9uLmxhYmVsXG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IGxhYmVsVGV4dC5pbmRleE9mKGhpZ2hsaWdodFRleHQpXG5cbiAgICAgICAgICAgIGxhYmVsID0gKFxuICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgIHtsYWJlbFRleHQuc2xpY2UoMCwgaW5kZXgpfVxuICAgICAgICAgICAgICAgIDxUZXh0IHsuLi5zdHlsZXMuaGlnaGxpZ2h0ZWRUZXh0KCl9PntoaWdobGlnaHRUZXh0fTwvVGV4dD5cbiAgICAgICAgICAgICAgICB7bGFiZWxUZXh0LnNsaWNlKGluZGV4ICsgaGlnaGxpZ2h0VGV4dC5sZW5ndGgpfVxuICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBpc09wdGlvbkRpc2FibGVkID0gb3B0aW9uLmRpc2FibGVkID09PSB0cnVlXG5cbiAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgPEJveFxuICAgICAgICAgICAgICBrZXk9e1N0cmluZyhvcHRpb24udmFsdWUpfVxuICAgICAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICAgICAgZmxleFNocmluaz17MH1cbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgPFNlbGVjdE9wdGlvblxuICAgICAgICAgICAgICAgIGlzRm9jdXNlZD17aXNGb2N1c2VkfVxuICAgICAgICAgICAgICAgIGlzU2VsZWN0ZWQ9e2lzU2VsZWN0ZWR9XG4gICAgICAgICAgICAgICAgc2hvdWxkU2hvd0Rvd25BcnJvdz17YXJlTW9yZU9wdGlvbnNCZWxvdyAmJiBpc0xhc3RWaXNpYmxlT3B0aW9ufVxuICAgICAgICAgICAgICAgIHNob3VsZFNob3dVcEFycm93PXthcmVNb3JlT3B0aW9uc0Fib3ZlICYmIGlzRmlyc3RWaXNpYmxlT3B0aW9ufVxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPD5cbiAgICAgICAgICAgICAgICAgIHshaGlkZUluZGV4ZXMgJiYgKFxuICAgICAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57YCR7aX0uYC5wYWRFbmQobWF4SW5kZXhXaWR0aCArIDEpfTwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgICAgICAgICBkaW1Db2xvcj17aXNPcHRpb25EaXNhYmxlZH1cbiAgICAgICAgICAgICAgICAgICAgY29sb3I9e1xuICAgICAgICAgICAgICAgICAgICAgIGlzT3B0aW9uRGlzYWJsZWRcbiAgICAgICAgICAgICAgICAgICAgICAgID8gdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgICAgICAgICA6IGlzU2VsZWN0ZWRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgPyAnc3VjY2VzcydcbiAgICAgICAgICAgICAgICAgICAgICAgICAgOiBpc0ZvY3VzZWRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA/ICdzdWdnZXN0aW9uJ1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAge2xhYmVsfVxuICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDwvPlxuICAgICAgICAgICAgICA8L1NlbGVjdE9wdGlvbj5cbiAgICAgICAgICAgICAge29wdGlvbi5kZXNjcmlwdGlvbiAmJiAoXG4gICAgICAgICAgICAgICAgPEJveCBwYWRkaW5nTGVmdD17aGlkZUluZGV4ZXMgPyA0IDogbWF4SW5kZXhXaWR0aCArIDR9PlxuICAgICAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICAgICAgZGltQ29sb3I9e1xuICAgICAgICAgICAgICAgICAgICAgIGlzT3B0aW9uRGlzYWJsZWQgfHwgb3B0aW9uLmRpbURlc2NyaXB0aW9uICE9PSBmYWxzZVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGNvbG9yPXtcbiAgICAgICAgICAgICAgICAgICAgICBpc09wdGlvbkRpc2FibGVkXG4gICAgICAgICAgICAgICAgICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgICAgICAgOiBpc1NlbGVjdGVkXG4gICAgICAgICAgICAgICAgICAgICAgICAgID8gJ3N1Y2Nlc3MnXG4gICAgICAgICAgICAgICAgICAgICAgICAgIDogaXNGb2N1c2VkXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPyAnc3VnZ2VzdGlvbidcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgIDxBbnNpPntvcHRpb24uZGVzY3JpcHRpb259PC9BbnNpPlxuICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKVxuICAgICAgICB9KX1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IG1heEluZGV4V2lkdGggPSBoaWRlSW5kZXhlcyA/IDAgOiBzdGF0ZS5vcHRpb25zLmxlbmd0aC50b1N0cmluZygpLmxlbmd0aFxuXG4gIC8vIENoZWNrIGlmIGFueSB2aXNpYmxlIG9wdGlvbnMgaGF2ZSBkZXNjcmlwdGlvbnMgKGZvciB0d28tY29sdW1uIGxheW91dClcbiAgLy8gQWxzbyBjaGVjayB0aGF0IHRoZXJlIGFyZSBOTyBpbnB1dCBvcHRpb25zLCBzaW5jZSB0aGV5J3JlIG5vdCBzdXBwb3J0ZWQgaW4gdHdvLWNvbHVtbiBsYXlvdXRcbiAgLy8gU2tpcCB0d28tY29sdW1uIGxheW91dCB3aGVuIGlubGluZURlc2NyaXB0aW9ucyBpcyBlbmFibGVkXG4gIGNvbnN0IGhhc0lucHV0T3B0aW9ucyA9IHN0YXRlLnZpc2libGVPcHRpb25zLnNvbWUob3B0ID0+IG9wdC50eXBlID09PSAnaW5wdXQnKVxuICBjb25zdCBoYXNEZXNjcmlwdGlvbnMgPVxuICAgICFpbmxpbmVEZXNjcmlwdGlvbnMgJiZcbiAgICAhaGFzSW5wdXRPcHRpb25zICYmXG4gICAgc3RhdGUudmlzaWJsZU9wdGlvbnMuc29tZShvcHQgPT4gb3B0LmRlc2NyaXB0aW9uKVxuXG4gIC8vIFByZS1jb21wdXRlIG9wdGlvbiBkYXRhIGZvciB0d28tY29sdW1uIGxheW91dFxuICBjb25zdCBvcHRpb25EYXRhID0gc3RhdGUudmlzaWJsZU9wdGlvbnMubWFwKChvcHRpb24sIGluZGV4KSA9PiB7XG4gICAgY29uc3QgaXNGaXJzdFZpc2libGVPcHRpb24gPSBvcHRpb24uaW5kZXggPT09IHN0YXRlLnZpc2libGVGcm9tSW5kZXhcbiAgICBjb25zdCBpc0xhc3RWaXNpYmxlT3B0aW9uID0gb3B0aW9uLmluZGV4ID09PSBzdGF0ZS52aXNpYmxlVG9JbmRleCAtIDFcbiAgICBjb25zdCBhcmVNb3JlT3B0aW9uc0JlbG93ID0gc3RhdGUudmlzaWJsZVRvSW5kZXggPCBvcHRpb25zLmxlbmd0aFxuICAgIGNvbnN0IGFyZU1vcmVPcHRpb25zQWJvdmUgPSBzdGF0ZS52aXNpYmxlRnJvbUluZGV4ID4gMFxuICAgIGNvbnN0IGkgPSBzdGF0ZS52aXNpYmxlRnJvbUluZGV4ICsgaW5kZXggKyAxXG4gICAgY29uc3QgaXNGb2N1c2VkID0gIWlzRGlzYWJsZWQgJiYgc3RhdGUuZm9jdXNlZFZhbHVlID09PSBvcHRpb24udmFsdWVcbiAgICBjb25zdCBpc1NlbGVjdGVkID0gc3RhdGUudmFsdWUgPT09IG9wdGlvbi52YWx1ZVxuICAgIGNvbnN0IGlzT3B0aW9uRGlzYWJsZWQgPSBvcHRpb24uZGlzYWJsZWQgPT09IHRydWVcblxuICAgIGxldCBsYWJlbDogUmVhY3ROb2RlID0gb3B0aW9uLmxhYmVsXG4gICAgaWYgKFxuICAgICAgdHlwZW9mIG9wdGlvbi5sYWJlbCA9PT0gJ3N0cmluZycgJiZcbiAgICAgIGhpZ2hsaWdodFRleHQgJiZcbiAgICAgIG9wdGlvbi5sYWJlbC5pbmNsdWRlcyhoaWdobGlnaHRUZXh0KVxuICAgICkge1xuICAgICAgY29uc3QgbGFiZWxUZXh0ID0gb3B0aW9uLmxhYmVsXG4gICAgICBjb25zdCBpZHggPSBsYWJlbFRleHQuaW5kZXhPZihoaWdobGlnaHRUZXh0KVxuICAgICAgbGFiZWwgPSAoXG4gICAgICAgIDw+XG4gICAgICAgICAge2xhYmVsVGV4dC5zbGljZSgwLCBpZHgpfVxuICAgICAgICAgIDxUZXh0IHsuLi5zdHlsZXMuaGlnaGxpZ2h0ZWRUZXh0KCl9PntoaWdobGlnaHRUZXh0fTwvVGV4dD5cbiAgICAgICAgICB7bGFiZWxUZXh0LnNsaWNlKGlkeCArIGhpZ2hsaWdodFRleHQubGVuZ3RoKX1cbiAgICAgICAgPC8+XG4gICAgICApXG4gICAgfVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIG9wdGlvbixcbiAgICAgIGluZGV4OiBpLFxuICAgICAgbGFiZWwsXG4gICAgICBpc0ZvY3VzZWQsXG4gICAgICBpc1NlbGVjdGVkLFxuICAgICAgaXNPcHRpb25EaXNhYmxlZCxcbiAgICAgIHNob3VsZFNob3dEb3duQXJyb3c6IGFyZU1vcmVPcHRpb25zQmVsb3cgJiYgaXNMYXN0VmlzaWJsZU9wdGlvbixcbiAgICAgIHNob3VsZFNob3dVcEFycm93OiBhcmVNb3JlT3B0aW9uc0Fib3ZlICYmIGlzRmlyc3RWaXNpYmxlT3B0aW9uLFxuICAgIH1cbiAgfSlcblxuICAvLyBDYWxjdWxhdGUgbWF4IGxhYmVsIHdpZHRoIGZvciBhbGlnbm1lbnQgd2hlbiBkZXNjcmlwdGlvbnMgZXhpc3RcbiAgaWYgKGhhc0Rlc2NyaXB0aW9ucykge1xuICAgIGNvbnN0IG1heExhYmVsV2lkdGggPSBNYXRoLm1heChcbiAgICAgIC4uLm9wdGlvbkRhdGEubWFwKGRhdGEgPT4ge1xuICAgICAgICBpZiAoZGF0YS5vcHRpb24udHlwZSA9PT0gJ2lucHV0JykgcmV0dXJuIDBcbiAgICAgICAgY29uc3QgbGFiZWxUZXh0ID0gZ2V0VGV4dENvbnRlbnQoZGF0YS5vcHRpb24ubGFiZWwpXG4gICAgICAgIC8vIFdpZHRoOiBpbmRpY2F0b3IgKDEpICsgc3BhY2UgKDEpICsgaW5kZXggKyBsYWJlbCArIHNwYWNlICsgY2hlY2ttYXJrICgxKVxuICAgICAgICBjb25zdCBpbmRleFdpZHRoID0gaGlkZUluZGV4ZXMgPyAwIDogbWF4SW5kZXhXaWR0aCArIDJcbiAgICAgICAgY29uc3QgY2hlY2ttYXJrV2lkdGggPSBkYXRhLmlzU2VsZWN0ZWQgPyAyIDogMFxuICAgICAgICByZXR1cm4gMiArIGluZGV4V2lkdGggKyBzdHJpbmdXaWR0aChsYWJlbFRleHQpICsgY2hlY2ttYXJrV2lkdGhcbiAgICAgIH0pLFxuICAgIClcblxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IHsuLi5zdHlsZXMuY29udGFpbmVyKCl9PlxuICAgICAgICB7b3B0aW9uRGF0YS5tYXAoZGF0YSA9PiB7XG4gICAgICAgICAgaWYgKGRhdGEub3B0aW9uLnR5cGUgPT09ICdpbnB1dCcpIHtcbiAgICAgICAgICAgIC8vIElucHV0IG9wdGlvbnMgbm90IHN1cHBvcnRlZCBpbiB0d28tY29sdW1uIGxheW91dFxuICAgICAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgbGFiZWxUZXh0ID0gZ2V0VGV4dENvbnRlbnQoZGF0YS5vcHRpb24ubGFiZWwpXG4gICAgICAgICAgY29uc3QgaW5kZXhXaWR0aCA9IGhpZGVJbmRleGVzID8gMCA6IG1heEluZGV4V2lkdGggKyAyXG4gICAgICAgICAgY29uc3QgY2hlY2ttYXJrV2lkdGggPSBkYXRhLmlzU2VsZWN0ZWQgPyAyIDogMFxuICAgICAgICAgIGNvbnN0IGN1cnJlbnRMYWJlbFdpZHRoID1cbiAgICAgICAgICAgIDIgKyBpbmRleFdpZHRoICsgc3RyaW5nV2lkdGgobGFiZWxUZXh0KSArIGNoZWNrbWFya1dpZHRoXG4gICAgICAgICAgY29uc3QgcGFkZGluZyA9IG1heExhYmVsV2lkdGggLSBjdXJyZW50TGFiZWxXaWR0aFxuXG4gICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIDxUd29Db2x1bW5Sb3dcbiAgICAgICAgICAgICAga2V5PXtTdHJpbmcoZGF0YS5vcHRpb24udmFsdWUpfVxuICAgICAgICAgICAgICBpc0ZvY3VzZWQ9e2RhdGEuaXNGb2N1c2VkfVxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICB7LyogTGFiZWwgcGFydCAtIG5vIGdhcCwgaGFuZGxlIHNwYWNpbmcgZXhwbGljaXRseSAqL31cbiAgICAgICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZmxleFNocmluaz17MH0+XG4gICAgICAgICAgICAgICAge2RhdGEuaXNGb2N1c2VkID8gKFxuICAgICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWdnZXN0aW9uXCI+e2ZpZ3VyZXMucG9pbnRlcn08L1RleHQ+XG4gICAgICAgICAgICAgICAgKSA6IGRhdGEuc2hvdWxkU2hvd0Rvd25BcnJvdyA/IChcbiAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntmaWd1cmVzLmFycm93RG93bn08L1RleHQ+XG4gICAgICAgICAgICAgICAgKSA6IGRhdGEuc2hvdWxkU2hvd1VwQXJyb3cgPyAoXG4gICAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57ZmlndXJlcy5hcnJvd1VwfTwvVGV4dD5cbiAgICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgICAgPFRleHQ+IDwvVGV4dD5cbiAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgIDxUZXh0PiA8L1RleHQ+XG4gICAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICAgIGRpbUNvbG9yPXtkYXRhLmlzT3B0aW9uRGlzYWJsZWR9XG4gICAgICAgICAgICAgICAgICBjb2xvcj17XG4gICAgICAgICAgICAgICAgICAgIGRhdGEuaXNPcHRpb25EaXNhYmxlZFxuICAgICAgICAgICAgICAgICAgICAgID8gdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgICAgICAgOiBkYXRhLmlzU2VsZWN0ZWRcbiAgICAgICAgICAgICAgICAgICAgICAgID8gJ3N1Y2Nlc3MnXG4gICAgICAgICAgICAgICAgICAgICAgICA6IGRhdGEuaXNGb2N1c2VkXG4gICAgICAgICAgICAgICAgICAgICAgICAgID8gJ3N1Z2dlc3Rpb24nXG4gICAgICAgICAgICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgeyFoaWRlSW5kZXhlcyAmJiAoXG4gICAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgICAgIHtgJHtkYXRhLmluZGV4fS5gLnBhZEVuZChtYXhJbmRleFdpZHRoICsgMil9XG4gICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgICAgICB7ZGF0YS5sYWJlbH1cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAge2RhdGEuaXNTZWxlY3RlZCAmJiAoXG4gICAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj4ge2ZpZ3VyZXMudGlja308L1RleHQ+XG4gICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICB7LyogUGFkZGluZyB0byBhbGlnbiBkZXNjcmlwdGlvbnMgKi99XG4gICAgICAgICAgICAgICAge3BhZGRpbmcgPiAwICYmIDxUZXh0PnsnICcucmVwZWF0KHBhZGRpbmcpfTwvVGV4dD59XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICB7LyogRGVzY3JpcHRpb24gcGFydCAqL31cbiAgICAgICAgICAgICAgPEJveCBmbGV4R3Jvdz17MX0gbWFyZ2luTGVmdD17Mn0+XG4gICAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICAgIHdyYXA9XCJ3cmFwXCJcbiAgICAgICAgICAgICAgICAgIGRpbUNvbG9yPXtcbiAgICAgICAgICAgICAgICAgICAgZGF0YS5pc09wdGlvbkRpc2FibGVkIHx8XG4gICAgICAgICAgICAgICAgICAgIGRhdGEub3B0aW9uLmRpbURlc2NyaXB0aW9uICE9PSBmYWxzZVxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgY29sb3I9e1xuICAgICAgICAgICAgICAgICAgICBkYXRhLmlzT3B0aW9uRGlzYWJsZWRcbiAgICAgICAgICAgICAgICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgICAgIDogZGF0YS5pc1NlbGVjdGVkXG4gICAgICAgICAgICAgICAgICAgICAgICA/ICdzdWNjZXNzJ1xuICAgICAgICAgICAgICAgICAgICAgICAgOiBkYXRhLmlzRm9jdXNlZFxuICAgICAgICAgICAgICAgICAgICAgICAgICA/ICdzdWdnZXN0aW9uJ1xuICAgICAgICAgICAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgIDxBbnNpPntkYXRhLm9wdGlvbi5kZXNjcmlwdGlvbiB8fCAnICd9PC9BbnNpPlxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICA8L1R3b0NvbHVtblJvdz5cbiAgICAgICAgICApXG4gICAgICAgIH0pfVxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IHsuLi5zdHlsZXMuY29udGFpbmVyKCl9PlxuICAgICAge3N0YXRlLnZpc2libGVPcHRpb25zLm1hcCgob3B0aW9uLCBpbmRleCkgPT4ge1xuICAgICAgICAvLyBIYW5kbGUgaW5wdXQgdHlwZSBvcHRpb25zXG4gICAgICAgIGlmIChvcHRpb24udHlwZSA9PT0gJ2lucHV0Jykge1xuICAgICAgICAgIGNvbnN0IGlucHV0VmFsdWUgPSBpbnB1dFZhbHVlcy5oYXMob3B0aW9uLnZhbHVlKVxuICAgICAgICAgICAgPyBpbnB1dFZhbHVlcy5nZXQob3B0aW9uLnZhbHVlKSFcbiAgICAgICAgICAgIDogb3B0aW9uLmluaXRpYWxWYWx1ZSB8fCAnJ1xuXG4gICAgICAgICAgY29uc3QgaXNGaXJzdFZpc2libGVPcHRpb24gPSBvcHRpb24uaW5kZXggPT09IHN0YXRlLnZpc2libGVGcm9tSW5kZXhcbiAgICAgICAgICBjb25zdCBpc0xhc3RWaXNpYmxlT3B0aW9uID0gb3B0aW9uLmluZGV4ID09PSBzdGF0ZS52aXNpYmxlVG9JbmRleCAtIDFcbiAgICAgICAgICBjb25zdCBhcmVNb3JlT3B0aW9uc0JlbG93ID0gc3RhdGUudmlzaWJsZVRvSW5kZXggPCBvcHRpb25zLmxlbmd0aFxuICAgICAgICAgIGNvbnN0IGFyZU1vcmVPcHRpb25zQWJvdmUgPSBzdGF0ZS52aXNpYmxlRnJvbUluZGV4ID4gMFxuXG4gICAgICAgICAgY29uc3QgaSA9IHN0YXRlLnZpc2libGVGcm9tSW5kZXggKyBpbmRleCArIDFcblxuICAgICAgICAgIGNvbnN0IGlzRm9jdXNlZCA9ICFpc0Rpc2FibGVkICYmIHN0YXRlLmZvY3VzZWRWYWx1ZSA9PT0gb3B0aW9uLnZhbHVlXG4gICAgICAgICAgY29uc3QgaXNTZWxlY3RlZCA9IHN0YXRlLnZhbHVlID09PSBvcHRpb24udmFsdWVcblxuICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8U2VsZWN0SW5wdXRPcHRpb25cbiAgICAgICAgICAgICAga2V5PXtTdHJpbmcob3B0aW9uLnZhbHVlKX1cbiAgICAgICAgICAgICAgb3B0aW9uPXtvcHRpb259XG4gICAgICAgICAgICAgIGlzRm9jdXNlZD17aXNGb2N1c2VkfVxuICAgICAgICAgICAgICBpc1NlbGVjdGVkPXtpc1NlbGVjdGVkfVxuICAgICAgICAgICAgICBzaG91bGRTaG93RG93bkFycm93PXthcmVNb3JlT3B0aW9uc0JlbG93ICYmIGlzTGFzdFZpc2libGVPcHRpb259XG4gICAgICAgICAgICAgIHNob3VsZFNob3dVcEFycm93PXthcmVNb3JlT3B0aW9uc0Fib3ZlICYmIGlzRmlyc3RWaXNpYmxlT3B0aW9ufVxuICAgICAgICAgICAgICBtYXhJbmRleFdpZHRoPXttYXhJbmRleFdpZHRofVxuICAgICAgICAgICAgICBpbmRleD17aX1cbiAgICAgICAgICAgICAgaW5wdXRWYWx1ZT17aW5wdXRWYWx1ZX1cbiAgICAgICAgICAgICAgb25JbnB1dENoYW5nZT17dmFsdWUgPT4ge1xuICAgICAgICAgICAgICAgIHNldElucHV0VmFsdWVzKHByZXYgPT4ge1xuICAgICAgICAgICAgICAgICAgY29uc3QgbmV4dCA9IG5ldyBNYXAocHJldilcbiAgICAgICAgICAgICAgICAgIG5leHQuc2V0KG9wdGlvbi52YWx1ZSwgdmFsdWUpXG4gICAgICAgICAgICAgICAgICByZXR1cm4gbmV4dFxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAgIG9uU3VibWl0PXsodmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGhhc0ltYWdlQXR0YWNobWVudHMgPVxuICAgICAgICAgICAgICAgICAgcGFzdGVkQ29udGVudHMgJiZcbiAgICAgICAgICAgICAgICAgIE9iamVjdC52YWx1ZXMocGFzdGVkQ29udGVudHMpLnNvbWUoYyA9PiBjLnR5cGUgPT09ICdpbWFnZScpXG4gICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgdmFsdWUudHJpbSgpIHx8XG4gICAgICAgICAgICAgICAgICBoYXNJbWFnZUF0dGFjaG1lbnRzIHx8XG4gICAgICAgICAgICAgICAgICBvcHRpb24uYWxsb3dFbXB0eVN1Ym1pdFRvQ2FuY2VsXG4gICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICBvbkNoYW5nZT8uKG9wdGlvbi52YWx1ZSlcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgb25DYW5jZWw/LigpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgICBvbkV4aXQ9e29uQ2FuY2VsfVxuICAgICAgICAgICAgICBsYXlvdXQ9XCJjb21wYWN0XCJcbiAgICAgICAgICAgICAgc2hvd0xhYmVsPXtpbmxpbmVEZXNjcmlwdGlvbnN9XG4gICAgICAgICAgICAgIG9uT3BlbkVkaXRvcj17b25PcGVuRWRpdG9yfVxuICAgICAgICAgICAgICByZXNldEN1cnNvck9uVXBkYXRlPXtvcHRpb24ucmVzZXRDdXJzb3JPblVwZGF0ZX1cbiAgICAgICAgICAgICAgb25JbWFnZVBhc3RlPXtvbkltYWdlUGFzdGV9XG4gICAgICAgICAgICAgIHBhc3RlZENvbnRlbnRzPXtwYXN0ZWRDb250ZW50c31cbiAgICAgICAgICAgICAgb25SZW1vdmVJbWFnZT17b25SZW1vdmVJbWFnZX1cbiAgICAgICAgICAgICAgaW1hZ2VzU2VsZWN0ZWQ9e2ltYWdlc1NlbGVjdGVkfVxuICAgICAgICAgICAgICBzZWxlY3RlZEltYWdlSW5kZXg9e3NlbGVjdGVkSW1hZ2VJbmRleH1cbiAgICAgICAgICAgICAgb25JbWFnZXNTZWxlY3RlZENoYW5nZT17c2V0SW1hZ2VzU2VsZWN0ZWR9XG4gICAgICAgICAgICAgIG9uU2VsZWN0ZWRJbWFnZUluZGV4Q2hhbmdlPXtzZXRTZWxlY3RlZEltYWdlSW5kZXh9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgIClcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEhhbmRsZSB0ZXh0IHR5cGUgb3B0aW9uc1xuICAgICAgICBsZXQgbGFiZWw6IFJlYWN0Tm9kZSA9IG9wdGlvbi5sYWJlbFxuXG4gICAgICAgIC8vIE9ubHkgYXBwbHkgaGlnaGxpZ2h0IHdoZW4gbGFiZWwgaXMgYSBzdHJpbmdcbiAgICAgICAgaWYgKFxuICAgICAgICAgIHR5cGVvZiBvcHRpb24ubGFiZWwgPT09ICdzdHJpbmcnICYmXG4gICAgICAgICAgaGlnaGxpZ2h0VGV4dCAmJlxuICAgICAgICAgIG9wdGlvbi5sYWJlbC5pbmNsdWRlcyhoaWdobGlnaHRUZXh0KVxuICAgICAgICApIHtcbiAgICAgICAgICBjb25zdCBsYWJlbFRleHQgPSBvcHRpb24ubGFiZWxcbiAgICAgICAgICBjb25zdCBpbmRleCA9IGxhYmVsVGV4dC5pbmRleE9mKGhpZ2hsaWdodFRleHQpXG5cbiAgICAgICAgICBsYWJlbCA9IChcbiAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgIHtsYWJlbFRleHQuc2xpY2UoMCwgaW5kZXgpfVxuICAgICAgICAgICAgICA8VGV4dCB7Li4uc3R5bGVzLmhpZ2hsaWdodGVkVGV4dCgpfT57aGlnaGxpZ2h0VGV4dH08L1RleHQ+XG4gICAgICAgICAgICAgIHtsYWJlbFRleHQuc2xpY2UoaW5kZXggKyBoaWdobGlnaHRUZXh0Lmxlbmd0aCl9XG4gICAgICAgICAgICA8Lz5cbiAgICAgICAgICApXG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBpc0ZpcnN0VmlzaWJsZU9wdGlvbiA9IG9wdGlvbi5pbmRleCA9PT0gc3RhdGUudmlzaWJsZUZyb21JbmRleFxuICAgICAgICBjb25zdCBpc0xhc3RWaXNpYmxlT3B0aW9uID0gb3B0aW9uLmluZGV4ID09PSBzdGF0ZS52aXNpYmxlVG9JbmRleCAtIDFcbiAgICAgICAgY29uc3QgYXJlTW9yZU9wdGlvbnNCZWxvdyA9IHN0YXRlLnZpc2libGVUb0luZGV4IDwgb3B0aW9ucy5sZW5ndGhcbiAgICAgICAgY29uc3QgYXJlTW9yZU9wdGlvbnNBYm92ZSA9IHN0YXRlLnZpc2libGVGcm9tSW5kZXggPiAwXG5cbiAgICAgICAgY29uc3QgaSA9IHN0YXRlLnZpc2libGVGcm9tSW5kZXggKyBpbmRleCArIDFcblxuICAgICAgICBjb25zdCBpc0ZvY3VzZWQgPSAhaXNEaXNhYmxlZCAmJiBzdGF0ZS5mb2N1c2VkVmFsdWUgPT09IG9wdGlvbi52YWx1ZVxuICAgICAgICBjb25zdCBpc1NlbGVjdGVkID0gc3RhdGUudmFsdWUgPT09IG9wdGlvbi52YWx1ZVxuICAgICAgICBjb25zdCBpc09wdGlvbkRpc2FibGVkID0gb3B0aW9uLmRpc2FibGVkID09PSB0cnVlXG5cbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICA8U2VsZWN0T3B0aW9uXG4gICAgICAgICAgICBrZXk9e1N0cmluZyhvcHRpb24udmFsdWUpfVxuICAgICAgICAgICAgaXNGb2N1c2VkPXtpc0ZvY3VzZWR9XG4gICAgICAgICAgICBpc1NlbGVjdGVkPXtpc1NlbGVjdGVkfVxuICAgICAgICAgICAgc2hvdWxkU2hvd0Rvd25BcnJvdz17YXJlTW9yZU9wdGlvbnNCZWxvdyAmJiBpc0xhc3RWaXNpYmxlT3B0aW9ufVxuICAgICAgICAgICAgc2hvdWxkU2hvd1VwQXJyb3c9e2FyZU1vcmVPcHRpb25zQWJvdmUgJiYgaXNGaXJzdFZpc2libGVPcHRpb259XG4gICAgICAgICAgPlxuICAgICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZmxleFNocmluaz17MH0+XG4gICAgICAgICAgICAgIHshaGlkZUluZGV4ZXMgJiYgKFxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntgJHtpfS5gLnBhZEVuZChtYXhJbmRleFdpZHRoICsgMil9PC9UZXh0PlxuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgICAgIGRpbUNvbG9yPXtpc09wdGlvbkRpc2FibGVkfVxuICAgICAgICAgICAgICAgIGNvbG9yPXtcbiAgICAgICAgICAgICAgICAgIGlzT3B0aW9uRGlzYWJsZWRcbiAgICAgICAgICAgICAgICAgICAgPyB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICAgICAgOiBpc1NlbGVjdGVkXG4gICAgICAgICAgICAgICAgICAgICAgPyAnc3VjY2VzcydcbiAgICAgICAgICAgICAgICAgICAgICA6IGlzRm9jdXNlZFxuICAgICAgICAgICAgICAgICAgICAgICAgPyAnc3VnZ2VzdGlvbidcbiAgICAgICAgICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAge2xhYmVsfVxuICAgICAgICAgICAgICAgIHtpbmxpbmVEZXNjcmlwdGlvbnMgJiYgb3B0aW9uLmRlc2NyaXB0aW9uICYmIChcbiAgICAgICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgICAgIGRpbUNvbG9yPXtcbiAgICAgICAgICAgICAgICAgICAgICBpc09wdGlvbkRpc2FibGVkIHx8IG9wdGlvbi5kaW1EZXNjcmlwdGlvbiAhPT0gZmFsc2VcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICB7JyAnfVxuICAgICAgICAgICAgICAgICAgICB7b3B0aW9uLmRlc2NyaXB0aW9ufVxuICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgeyFpbmxpbmVEZXNjcmlwdGlvbnMgJiYgb3B0aW9uLmRlc2NyaXB0aW9uICYmIChcbiAgICAgICAgICAgICAgPEJveCBmbGV4U2hyaW5rPXs5OX0gbWFyZ2luTGVmdD17Mn0+XG4gICAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICAgIHdyYXA9XCJ3cmFwLXRyaW1cIlxuICAgICAgICAgICAgICAgICAgZGltQ29sb3I9e2lzT3B0aW9uRGlzYWJsZWQgfHwgb3B0aW9uLmRpbURlc2NyaXB0aW9uICE9PSBmYWxzZX1cbiAgICAgICAgICAgICAgICAgIGNvbG9yPXtcbiAgICAgICAgICAgICAgICAgICAgaXNPcHRpb25EaXNhYmxlZFxuICAgICAgICAgICAgICAgICAgICAgID8gdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgICAgICAgOiBpc1NlbGVjdGVkXG4gICAgICAgICAgICAgICAgICAgICAgICA/ICdzdWNjZXNzJ1xuICAgICAgICAgICAgICAgICAgICAgICAgOiBpc0ZvY3VzZWRcbiAgICAgICAgICAgICAgICAgICAgICAgICAgPyAnc3VnZ2VzdGlvbidcbiAgICAgICAgICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICA8QW5zaT57b3B0aW9uLmRlc2NyaXB0aW9ufTwvQW5zaT5cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L1NlbGVjdE9wdGlvbj5cbiAgICAgICAgKVxuICAgICAgfSl9XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuLy8gUm93IGNvbnRhaW5lciBmb3IgdGhlIHR3by1jb2x1bW4gKGxhYmVsICsgZGVzY3JpcHRpb24pIGxheW91dC4gVW5saWtlXG4vLyB0aGUgb3RoZXIgU2VsZWN0IGxheW91dHMsIHRoaXMgb25lIGRvZXNuJ3QgcmVuZGVyIHRocm91Z2ggU2VsZWN0T3B0aW9uIOKGklxuLy8gTGlzdEl0ZW0sIHNvIGl0IGRlY2xhcmVzIHRoZSBuYXRpdmUgY3Vyc29yIGRpcmVjdGx5LiBQYXJrcyB0aGUgY3Vyc29yXG4vLyBvbiB0aGUgcG9pbnRlciBpbmRpY2F0b3Igc28gc2NyZWVuIHJlYWRlcnMgLyBtYWduaWZpZXJzIHRyYWNrIGZvY3VzLlxuZnVuY3Rpb24gVHdvQ29sdW1uUm93KHtcbiAgaXNGb2N1c2VkLFxuICBjaGlsZHJlbixcbn06IHtcbiAgaXNGb2N1c2VkOiBib29sZWFuXG4gIGNoaWxkcmVuOiBSZWFjdE5vZGVcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBjdXJzb3JSZWYgPSB1c2VEZWNsYXJlZEN1cnNvcih7XG4gICAgbGluZTogMCxcbiAgICBjb2x1bW46IDAsXG4gICAgYWN0aXZlOiBpc0ZvY3VzZWQsXG4gIH0pXG4gIHJldHVybiAoXG4gICAgPEJveCByZWY9e2N1cnNvclJlZn0gZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAge2NoaWxkcmVufVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPQyxLQUFLLElBQUksS0FBS0MsU0FBUyxFQUFFQyxTQUFTLEVBQUVDLE1BQU0sRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDMUUsU0FBU0MsaUJBQWlCLFFBQVEsd0NBQXdDO0FBQzFFLFNBQVNDLFdBQVcsUUFBUSwwQkFBMEI7QUFDdEQsU0FBU0MsSUFBSSxFQUFFQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQzlDLFNBQVNDLEtBQUssUUFBUSxzQkFBc0I7QUFDNUMsY0FBY0MsYUFBYSxRQUFRLHVCQUF1QjtBQUMxRCxjQUFjQyxlQUFlLFFBQVEsNkJBQTZCO0FBQ2xFLFNBQVNDLGlCQUFpQixRQUFRLDBCQUEwQjtBQUM1RCxTQUFTQyxZQUFZLFFBQVEsb0JBQW9CO0FBQ2pELFNBQVND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 \ No newline at end of file diff --git a/components/CustomSelect/use-multi-select-state.ts b/components/CustomSelect/use-multi-select-state.ts new file mode 100644 index 0000000..bf2bd8b --- /dev/null +++ b/components/CustomSelect/use-multi-select-state.ts @@ -0,0 +1,414 @@ +import { useCallback, useState } from 'react' +import { isDeepStrictEqual } from 'util' +import { useRegisterOverlay } from '../../context/overlayContext.js' +import type { InputEvent } from '../../ink/events/input-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw space/arrow multiselect input +import { useInput } from '../../ink.js' +import { + normalizeFullWidthDigits, + normalizeFullWidthSpace, +} from '../../utils/stringUtils.js' +import type { OptionWithDescription } from './select.js' +import { useSelectNavigation } from './use-select-navigation.js' + +export type UseMultiSelectStateProps = { + /** + * When disabled, user input is ignored. + * + * @default false + */ + isDisabled?: boolean + + /** + * Number of items to display. + * + * @default 5 + */ + visibleOptionCount?: number + + /** + * Options. + */ + options: OptionWithDescription[] + + /** + * Initially selected values. + */ + defaultValue?: T[] + + /** + * Callback when selection changes. + */ + onChange?: (values: T[]) => void + + /** + * Callback for canceling the select. + */ + onCancel: () => void + + /** + * Callback for focusing an option. + */ + onFocus?: (value: T) => void + + /** + * Value to focus + */ + focusValue?: T + + /** + * Text for the submit button. When provided, a submit button is shown and + * Enter toggles selection (submit only fires when the button is focused). + * When omitted, Enter submits directly and Space toggles selection. + */ + submitButtonText?: string + + /** + * Callback when user submits. Receives the currently selected values. + */ + onSubmit?: (values: T[]) => void + + /** + * Callback when user presses down from the last item (submit button). + * If provided, navigation will not wrap to the first item. + */ + onDownFromLastItem?: () => void + + /** + * Callback when user presses up from the first item. + * If provided, navigation will not wrap to the last item. + */ + onUpFromFirstItem?: () => void + + /** + * Focus the last option initially instead of the first. + */ + initialFocusLast?: boolean + + /** + * When true, numeric keys (1-9) do not toggle options by index. + * Mirrors the rendering layer's hideIndexes: if index labels aren't shown, + * pressing a number shouldn't silently toggle an invisible mapping. + */ + hideIndexes?: boolean +} + +export type MultiSelectState = { + /** + * Value of the currently focused option. + */ + focusedValue: T | undefined + + /** + * Index of the first visible option. + */ + visibleFromIndex: number + + /** + * Index of the last visible option. + */ + visibleToIndex: number + + /** + * All options. + */ + options: OptionWithDescription[] + + /** + * Visible options. + */ + visibleOptions: Array & { index: number }> + + /** + * Whether the focused option is an input type. + */ + isInInput: boolean + + /** + * Currently selected values. + */ + selectedValues: T[] + + /** + * Current input field values. + */ + inputValues: Map + + /** + * Whether the submit button is focused. + */ + isSubmitFocused: boolean + + /** + * Update an input field value. + */ + updateInputValue: (value: T, inputValue: string) => void + + /** + * Callback for canceling the select. + */ + onCancel: () => void +} + +export function useMultiSelectState({ + isDisabled = false, + visibleOptionCount = 5, + options, + defaultValue = [], + onChange, + onCancel, + onFocus, + focusValue, + submitButtonText, + onSubmit, + onDownFromLastItem, + onUpFromFirstItem, + initialFocusLast, + hideIndexes = false, +}: UseMultiSelectStateProps): MultiSelectState { + const [selectedValues, setSelectedValues] = useState(defaultValue) + const [isSubmitFocused, setIsSubmitFocused] = useState(false) + + // Reset selectedValues when options change (e.g. async-loaded data changes + // defaultValue after mount). Mirrors the reset pattern in use-select-navigation.ts + // and the deleted ui/useMultiSelectState.ts — without this, MCPServerDesktopImportDialog + // keeps colliding servers checked after getAllMcpConfigs() resolves. + const [lastOptions, setLastOptions] = useState(options) + if (options !== lastOptions && !isDeepStrictEqual(options, lastOptions)) { + setSelectedValues(defaultValue) + setLastOptions(options) + } + + // State for input type options + const [inputValues, setInputValues] = useState>(() => { + const initialMap = new Map() + options.forEach(option => { + if (option.type === 'input' && option.initialValue) { + initialMap.set(option.value, option.initialValue) + } + }) + return initialMap + }) + + const updateSelectedValues = useCallback( + (values: T[] | ((prev: T[]) => T[])) => { + const newValues = + typeof values === 'function' ? values(selectedValues) : values + setSelectedValues(newValues) + onChange?.(newValues) + }, + [selectedValues, onChange], + ) + + const navigation = useSelectNavigation({ + visibleOptionCount, + options, + initialFocusValue: initialFocusLast + ? options[options.length - 1]?.value + : undefined, + onFocus, + focusValue, + }) + + // Automatically register as an overlay. + // This ensures CancelRequestHandler won't intercept Escape when the multi-select is active. + useRegisterOverlay('multi-select') + + const updateInputValue = useCallback( + (value: T, inputValue: string) => { + setInputValues(prev => { + const next = new Map(prev) + next.set(value, inputValue) + return next + }) + + // Find the option and call its onChange + const option = options.find(opt => opt.value === value) + if (option && option.type === 'input') { + option.onChange(inputValue) + } + + // Update selected values to include/exclude based on input + updateSelectedValues(prev => { + if (inputValue) { + if (!prev.includes(value)) { + return [...prev, value] + } + return prev + } else { + return prev.filter(v => v !== value) + } + }) + }, + [options, updateSelectedValues], + ) + + // Handle all keyboard input + useInput( + (input, key, event: InputEvent) => { + const normalizedInput = normalizeFullWidthDigits(input) + const focusedOption = options.find( + opt => opt.value === navigation.focusedValue, + ) + const isInInput = focusedOption?.type === 'input' + + // When in input field, only allow navigation keys + if (isInInput) { + const isAllowedKey = + key.upArrow || + key.downArrow || + key.escape || + key.tab || + key.return || + (key.ctrl && (input === 'n' || input === 'p' || key.return)) + if (!isAllowedKey) return + } + + const lastOptionValue = options[options.length - 1]?.value + + // Handle Tab to move forward + if (key.tab && !key.shift) { + if ( + submitButtonText && + onSubmit && + navigation.focusedValue === lastOptionValue && + !isSubmitFocused + ) { + setIsSubmitFocused(true) + } else if (!isSubmitFocused) { + navigation.focusNextOption() + } + return + } + + // Handle Shift+Tab to move backward + if (key.tab && key.shift) { + if (submitButtonText && onSubmit && isSubmitFocused) { + setIsSubmitFocused(false) + navigation.focusOption(lastOptionValue) + } else { + navigation.focusPreviousOption() + } + return + } + + // Handle arrow down / Ctrl+N / j + if ( + key.downArrow || + (key.ctrl && input === 'n') || + (!key.ctrl && !key.shift && input === 'j') + ) { + if (isSubmitFocused && onDownFromLastItem) { + onDownFromLastItem() + } else if ( + submitButtonText && + onSubmit && + navigation.focusedValue === lastOptionValue && + !isSubmitFocused + ) { + setIsSubmitFocused(true) + } else if ( + !submitButtonText && + onDownFromLastItem && + navigation.focusedValue === lastOptionValue + ) { + // No submit button — exit from the last option + onDownFromLastItem() + } else if (!isSubmitFocused) { + navigation.focusNextOption() + } + return + } + + // Handle arrow up / Ctrl+P / k + if ( + key.upArrow || + (key.ctrl && input === 'p') || + (!key.ctrl && !key.shift && input === 'k') + ) { + if (submitButtonText && onSubmit && isSubmitFocused) { + setIsSubmitFocused(false) + navigation.focusOption(lastOptionValue) + } else if ( + onUpFromFirstItem && + navigation.focusedValue === options[0]?.value + ) { + onUpFromFirstItem() + } else { + navigation.focusPreviousOption() + } + return + } + + // Handle page navigation + if (key.pageDown) { + navigation.focusNextPage() + return + } + + if (key.pageUp) { + navigation.focusPreviousPage() + return + } + + // Handle Enter or Space for selection/submit + if (key.return || normalizeFullWidthSpace(input) === ' ') { + // Ctrl+Enter from input field submits + if (key.ctrl && key.return && isInInput && onSubmit) { + onSubmit(selectedValues) + return + } + + // Enter on submit button submits + if (isSubmitFocused && onSubmit) { + onSubmit(selectedValues) + return + } + + // No submit button: Enter submits directly, Space still toggles + if (key.return && !submitButtonText && onSubmit) { + onSubmit(selectedValues) + return + } + + // Enter or Space toggles selection (including for input fields) + if (navigation.focusedValue !== undefined) { + const newValues = selectedValues.includes(navigation.focusedValue) + ? selectedValues.filter(v => v !== navigation.focusedValue) + : [...selectedValues, navigation.focusedValue] + updateSelectedValues(newValues) + } + return + } + + // Handle numeric keys (1-9) for direct selection + if (!hideIndexes && /^[0-9]+$/.test(normalizedInput)) { + const index = parseInt(normalizedInput) - 1 + if (index >= 0 && index < options.length) { + const value = options[index]!.value + const newValues = selectedValues.includes(value) + ? selectedValues.filter(v => v !== value) + : [...selectedValues, value] + updateSelectedValues(newValues) + } + return + } + + // Handle Escape + if (key.escape) { + onCancel() + event.stopImmediatePropagation() + } + }, + { isActive: !isDisabled }, + ) + + return { + ...navigation, + selectedValues, + inputValues, + isSubmitFocused, + updateInputValue, + onCancel, + } +} diff --git a/components/CustomSelect/use-select-input.ts b/components/CustomSelect/use-select-input.ts new file mode 100644 index 0000000..dcafeb4 --- /dev/null +++ b/components/CustomSelect/use-select-input.ts @@ -0,0 +1,287 @@ +import { useMemo } from 'react' +import { useRegisterOverlay } from '../../context/overlayContext.js' +import type { InputEvent } from '../../ink/events/input-event.js' +import { useInput } from '../../ink.js' +import { useKeybindings } from '../../keybindings/useKeybinding.js' +import { + normalizeFullWidthDigits, + normalizeFullWidthSpace, +} from '../../utils/stringUtils.js' +import type { OptionWithDescription } from './select.js' +import type { SelectState } from './use-select-state.js' + +export type UseSelectProps = { + /** + * When disabled, user input is ignored. + * + * @default false + */ + isDisabled?: boolean + + /** + * When true, prevents selection on Enter or number keys, but allows + * scrolling. + * When 'numeric', prevents selection on number keys, but allows Enter (and + * scrolling). + * + * @default false + */ + readonly disableSelection?: boolean | 'numeric' + + /** + * Select state. + */ + state: SelectState + + /** + * Options. + */ + options: OptionWithDescription[] + + /** + * Whether this is a multi-select component. + * + * @default false + */ + isMultiSelect?: boolean + + /** + * Callback when user presses up from the first item. + * If provided, navigation will not wrap to the last item. + */ + onUpFromFirstItem?: () => void + + /** + * Callback when user presses down from the last item. + * If provided, navigation will not wrap to the first item. + */ + onDownFromLastItem?: () => void + + /** + * Callback when input mode should be toggled for an option. + * Called when Tab is pressed (to enter or exit input mode). + */ + onInputModeToggle?: (value: T) => void + + /** + * Current input values for input-type options. + * Used to determine if number key should submit an empty input option. + */ + inputValues?: Map + + /** + * Whether image selection mode is active on the focused input option. + * When true, arrow key navigation in useInput is suppressed so that + * Attachments keybindings can handle image navigation instead. + */ + imagesSelected?: boolean + + /** + * Callback to attempt entering image selection mode on DOWN arrow. + * Returns true if image selection was entered (images exist), false otherwise. + */ + onEnterImageSelection?: () => boolean +} + +export const useSelectInput = ({ + isDisabled = false, + disableSelection = false, + state, + options, + isMultiSelect = false, + onUpFromFirstItem, + onDownFromLastItem, + onInputModeToggle, + inputValues, + imagesSelected = false, + onEnterImageSelection, +}: UseSelectProps) => { + // Automatically register as an overlay when onCancel is provided. + // This ensures CancelRequestHandler won't intercept Escape when the select is active. + useRegisterOverlay('select', !!state.onCancel) + + // Determine if the focused option is an input type + const isInInput = useMemo(() => { + const focusedOption = options.find(opt => opt.value === state.focusedValue) + return focusedOption?.type === 'input' + }, [options, state.focusedValue]) + + // Core navigation via keybindings (up/down/enter/escape) + // When in input mode, exclude navigation/accept keybindings so that + // j/k/enter pass through to the TextInput instead of being intercepted. + const keybindingHandlers = useMemo(() => { + const handlers: Record void> = {} + + if (!isInInput) { + handlers['select:next'] = () => { + if (onDownFromLastItem) { + const lastOption = options[options.length - 1] + if (lastOption && state.focusedValue === lastOption.value) { + onDownFromLastItem() + return + } + } + state.focusNextOption() + } + handlers['select:previous'] = () => { + if (onUpFromFirstItem && state.visibleFromIndex === 0) { + const firstOption = options[0] + if (firstOption && state.focusedValue === firstOption.value) { + onUpFromFirstItem() + return + } + } + state.focusPreviousOption() + } + handlers['select:accept'] = () => { + if (disableSelection === true) return + if (state.focusedValue === undefined) return + + const focusedOption = options.find( + opt => opt.value === state.focusedValue, + ) + if (focusedOption?.disabled === true) return + + state.selectFocusedOption?.() + state.onChange?.(state.focusedValue) + } + } + + if (state.onCancel) { + handlers['select:cancel'] = () => { + state.onCancel!() + } + } + + return handlers + }, [ + options, + state, + onDownFromLastItem, + onUpFromFirstItem, + isInInput, + disableSelection, + ]) + + useKeybindings(keybindingHandlers, { + context: 'Select', + isActive: !isDisabled, + }) + + // Remaining keys that stay as useInput: number keys, pageUp/pageDown, tab, space, + // and arrow key navigation when in input mode + useInput( + (input, key, event: InputEvent) => { + const normalizedInput = normalizeFullWidthDigits(input) + const focusedOption = options.find( + opt => opt.value === state.focusedValue, + ) + const currentIsInInput = focusedOption?.type === 'input' + + // Handle Tab key for input mode toggling + if (key.tab && onInputModeToggle && state.focusedValue !== undefined) { + onInputModeToggle(state.focusedValue) + return + } + + if (currentIsInInput) { + // When in image selection mode, suppress all input handling so + // Attachments keybindings can handle navigation/deletion instead + if (imagesSelected) return + + // DOWN arrow enters image selection mode if images exist + if (key.downArrow && onEnterImageSelection?.()) { + event.stopImmediatePropagation() + return + } + + // Arrow keys still navigate the select even while in input mode + if (key.downArrow || (key.ctrl && input === 'n')) { + if (onDownFromLastItem) { + const lastOption = options[options.length - 1] + if (lastOption && state.focusedValue === lastOption.value) { + onDownFromLastItem() + event.stopImmediatePropagation() + return + } + } + state.focusNextOption() + event.stopImmediatePropagation() + return + } + if (key.upArrow || (key.ctrl && input === 'p')) { + if (onUpFromFirstItem && state.visibleFromIndex === 0) { + const firstOption = options[0] + if (firstOption && state.focusedValue === firstOption.value) { + onUpFromFirstItem() + event.stopImmediatePropagation() + return + } + } + state.focusPreviousOption() + event.stopImmediatePropagation() + return + } + + // All other keys (including digits) pass through to TextInput. + // Digits should type literally into the input rather than select + // options — the user has focused a text field and expects typing + // to insert characters, not jump to a different option. + return + } + + if (key.pageDown) { + state.focusNextPage() + } + + if (key.pageUp) { + state.focusPreviousPage() + } + + if (disableSelection !== true) { + // Space for multi-select toggle + if ( + isMultiSelect && + normalizeFullWidthSpace(input) === ' ' && + state.focusedValue !== undefined + ) { + const isFocusedOptionDisabled = focusedOption?.disabled === true + if (!isFocusedOptionDisabled) { + state.selectFocusedOption?.() + state.onChange?.(state.focusedValue) + } + } + + if ( + disableSelection !== 'numeric' && + /^[0-9]+$/.test(normalizedInput) + ) { + const index = parseInt(normalizedInput) - 1 + if (index >= 0 && index < state.options.length) { + const selectedOption = state.options[index]! + if (selectedOption.disabled === true) { + return + } + if (selectedOption.type === 'input') { + const currentValue = inputValues?.get(selectedOption.value) ?? '' + if (currentValue.trim()) { + // Pre-filled input: auto-submit (user can Tab to edit instead) + state.onChange?.(selectedOption.value) + return + } + if (selectedOption.allowEmptySubmitToCancel) { + state.onChange?.(selectedOption.value) + return + } + state.focusOption(selectedOption.value) + return + } + state.onChange?.(selectedOption.value) + return + } + } + } + }, + { isActive: !isDisabled }, + ) +} diff --git a/components/CustomSelect/use-select-navigation.ts b/components/CustomSelect/use-select-navigation.ts new file mode 100644 index 0000000..7ecb4e7 --- /dev/null +++ b/components/CustomSelect/use-select-navigation.ts @@ -0,0 +1,653 @@ +import { + useCallback, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from 'react' +import { isDeepStrictEqual } from 'util' +import OptionMap from './option-map.js' +import type { OptionWithDescription } from './select.js' + +type State = { + /** + * Map where key is option's value and value is option's index. + */ + optionMap: OptionMap + + /** + * Number of visible options. + */ + visibleOptionCount: number + + /** + * Value of the currently focused option. + */ + focusedValue: T | undefined + + /** + * Index of the first visible option. + */ + visibleFromIndex: number + + /** + * Index of the last visible option. + */ + visibleToIndex: number +} + +type Action = + | FocusNextOptionAction + | FocusPreviousOptionAction + | FocusNextPageAction + | FocusPreviousPageAction + | SetFocusAction + | ResetAction + +type SetFocusAction = { + type: 'set-focus' + value: T +} + +type FocusNextOptionAction = { + type: 'focus-next-option' +} + +type FocusPreviousOptionAction = { + type: 'focus-previous-option' +} + +type FocusNextPageAction = { + type: 'focus-next-page' +} + +type FocusPreviousPageAction = { + type: 'focus-previous-page' +} + +type ResetAction = { + type: 'reset' + state: State +} + +const reducer = (state: State, action: Action): State => { + switch (action.type) { + case 'focus-next-option': { + if (state.focusedValue === undefined) { + return state + } + + const item = state.optionMap.get(state.focusedValue) + + if (!item) { + return state + } + + // Wrap to first item if at the end + const next = item.next || state.optionMap.first + + if (!next) { + return state + } + + // When wrapping to first, reset viewport to start + if (!item.next && next === state.optionMap.first) { + return { + ...state, + focusedValue: next.value, + visibleFromIndex: 0, + visibleToIndex: state.visibleOptionCount, + } + } + + const needsToScroll = next.index >= state.visibleToIndex + + if (!needsToScroll) { + return { + ...state, + focusedValue: next.value, + } + } + + const nextVisibleToIndex = Math.min( + state.optionMap.size, + state.visibleToIndex + 1, + ) + + const nextVisibleFromIndex = nextVisibleToIndex - state.visibleOptionCount + + return { + ...state, + focusedValue: next.value, + visibleFromIndex: nextVisibleFromIndex, + visibleToIndex: nextVisibleToIndex, + } + } + + case 'focus-previous-option': { + if (state.focusedValue === undefined) { + return state + } + + const item = state.optionMap.get(state.focusedValue) + + if (!item) { + return state + } + + // Wrap to last item if at the beginning + const previous = item.previous || state.optionMap.last + + if (!previous) { + return state + } + + // When wrapping to last, reset viewport to end + if (!item.previous && previous === state.optionMap.last) { + const nextVisibleToIndex = state.optionMap.size + const nextVisibleFromIndex = Math.max( + 0, + nextVisibleToIndex - state.visibleOptionCount, + ) + return { + ...state, + focusedValue: previous.value, + visibleFromIndex: nextVisibleFromIndex, + visibleToIndex: nextVisibleToIndex, + } + } + + const needsToScroll = previous.index <= state.visibleFromIndex + + if (!needsToScroll) { + return { + ...state, + focusedValue: previous.value, + } + } + + const nextVisibleFromIndex = Math.max(0, state.visibleFromIndex - 1) + + const nextVisibleToIndex = nextVisibleFromIndex + state.visibleOptionCount + + return { + ...state, + focusedValue: previous.value, + visibleFromIndex: nextVisibleFromIndex, + visibleToIndex: nextVisibleToIndex, + } + } + + case 'focus-next-page': { + if (state.focusedValue === undefined) { + return state + } + + const item = state.optionMap.get(state.focusedValue) + + if (!item) { + return state + } + + // Move by a full page (visibleOptionCount items) + const targetIndex = Math.min( + state.optionMap.size - 1, + item.index + state.visibleOptionCount, + ) + + // Find the item at the target index + let targetItem = state.optionMap.first + while (targetItem && targetItem.index < targetIndex) { + if (targetItem.next) { + targetItem = targetItem.next + } else { + break + } + } + + if (!targetItem) { + return state + } + + // Update the visible range to include the new focused item + const nextVisibleToIndex = Math.min( + state.optionMap.size, + targetItem.index + 1, + ) + const nextVisibleFromIndex = Math.max( + 0, + nextVisibleToIndex - state.visibleOptionCount, + ) + + return { + ...state, + focusedValue: targetItem.value, + visibleFromIndex: nextVisibleFromIndex, + visibleToIndex: nextVisibleToIndex, + } + } + + case 'focus-previous-page': { + if (state.focusedValue === undefined) { + return state + } + + const item = state.optionMap.get(state.focusedValue) + + if (!item) { + return state + } + + // Move by a full page (visibleOptionCount items) + const targetIndex = Math.max(0, item.index - state.visibleOptionCount) + + // Find the item at the target index + let targetItem = state.optionMap.first + while (targetItem && targetItem.index < targetIndex) { + if (targetItem.next) { + targetItem = targetItem.next + } else { + break + } + } + + if (!targetItem) { + return state + } + + // Update the visible range to include the new focused item + const nextVisibleFromIndex = Math.max(0, targetItem.index) + const nextVisibleToIndex = Math.min( + state.optionMap.size, + nextVisibleFromIndex + state.visibleOptionCount, + ) + + return { + ...state, + focusedValue: targetItem.value, + visibleFromIndex: nextVisibleFromIndex, + visibleToIndex: nextVisibleToIndex, + } + } + + case 'reset': { + return action.state + } + + case 'set-focus': { + // Early return if already focused on this value + if (state.focusedValue === action.value) { + return state + } + + const item = state.optionMap.get(action.value) + if (!item) { + return state + } + + // Check if the item is already in view + if ( + item.index >= state.visibleFromIndex && + item.index < state.visibleToIndex + ) { + // Already visible, just update focus + return { + ...state, + focusedValue: action.value, + } + } + + // Need to scroll to make the item visible + // Scroll as little as possible - put item at edge of viewport + let nextVisibleFromIndex: number + let nextVisibleToIndex: number + + if (item.index < state.visibleFromIndex) { + // Item is above viewport - scroll up to put it at the top + nextVisibleFromIndex = item.index + nextVisibleToIndex = Math.min( + state.optionMap.size, + nextVisibleFromIndex + state.visibleOptionCount, + ) + } else { + // Item is below viewport - scroll down to put it at the bottom + nextVisibleToIndex = Math.min(state.optionMap.size, item.index + 1) + nextVisibleFromIndex = Math.max( + 0, + nextVisibleToIndex - state.visibleOptionCount, + ) + } + + return { + ...state, + focusedValue: action.value, + visibleFromIndex: nextVisibleFromIndex, + visibleToIndex: nextVisibleToIndex, + } + } + } +} + +export type UseSelectNavigationProps = { + /** + * Number of items to display. + * + * @default 5 + */ + visibleOptionCount?: number + + /** + * Options. + */ + options: OptionWithDescription[] + + /** + * Initially focused option's value. + */ + initialFocusValue?: T + + /** + * Callback for focusing an option. + */ + onFocus?: (value: T) => void + + /** + * Value to focus + */ + focusValue?: T +} + +export type SelectNavigation = { + /** + * Value of the currently focused option. + */ + focusedValue: T | undefined + + /** + * 1-based index of the focused option in the full list. + * Returns 0 if no option is focused. + */ + focusedIndex: number + + /** + * Index of the first visible option. + */ + visibleFromIndex: number + + /** + * Index of the last visible option. + */ + visibleToIndex: number + + /** + * All options. + */ + options: OptionWithDescription[] + + /** + * Visible options. + */ + visibleOptions: Array & { index: number }> + + /** + * Whether the focused option is an input type. + */ + isInInput: boolean + + /** + * Focus next option and scroll the list down, if needed. + */ + focusNextOption: () => void + + /** + * Focus previous option and scroll the list up, if needed. + */ + focusPreviousOption: () => void + + /** + * Focus next page and scroll the list down by a page. + */ + focusNextPage: () => void + + /** + * Focus previous page and scroll the list up by a page. + */ + focusPreviousPage: () => void + + /** + * Focus a specific option by value. + */ + focusOption: (value: T | undefined) => void +} + +const createDefaultState = ({ + visibleOptionCount: customVisibleOptionCount, + options, + initialFocusValue, + currentViewport, +}: Pick, 'visibleOptionCount' | 'options'> & { + initialFocusValue?: T + currentViewport?: { visibleFromIndex: number; visibleToIndex: number } +}): State => { + const visibleOptionCount = + typeof customVisibleOptionCount === 'number' + ? Math.min(customVisibleOptionCount, options.length) + : options.length + + const optionMap = new OptionMap(options) + const focusedItem = + initialFocusValue !== undefined && optionMap.get(initialFocusValue) + const focusedValue = focusedItem ? initialFocusValue : optionMap.first?.value + + let visibleFromIndex = 0 + let visibleToIndex = visibleOptionCount + + // When there's a valid focused item, adjust viewport to show it + if (focusedItem) { + const focusedIndex = focusedItem.index + + if (currentViewport) { + // If focused item is already in the current viewport range, try to preserve it + if ( + focusedIndex >= currentViewport.visibleFromIndex && + focusedIndex < currentViewport.visibleToIndex + ) { + // Keep the same viewport if it's valid + visibleFromIndex = currentViewport.visibleFromIndex + visibleToIndex = Math.min( + optionMap.size, + currentViewport.visibleToIndex, + ) + } else { + // Need to adjust viewport to show focused item + // Use minimal scrolling - put item at edge of viewport + if (focusedIndex < currentViewport.visibleFromIndex) { + // Item is above current viewport - scroll up to put it at the top + visibleFromIndex = focusedIndex + visibleToIndex = Math.min( + optionMap.size, + visibleFromIndex + visibleOptionCount, + ) + } else { + // Item is below current viewport - scroll down to put it at the bottom + visibleToIndex = Math.min(optionMap.size, focusedIndex + 1) + visibleFromIndex = Math.max(0, visibleToIndex - visibleOptionCount) + } + } + } else if (focusedIndex >= visibleOptionCount) { + // No current viewport but focused item is outside default viewport + // Scroll to show the focused item at the bottom of the viewport + visibleToIndex = Math.min(optionMap.size, focusedIndex + 1) + visibleFromIndex = Math.max(0, visibleToIndex - visibleOptionCount) + } + + // Ensure viewport bounds are valid + visibleFromIndex = Math.max( + 0, + Math.min(visibleFromIndex, optionMap.size - 1), + ) + visibleToIndex = Math.min( + optionMap.size, + Math.max(visibleOptionCount, visibleToIndex), + ) + } + + return { + optionMap, + visibleOptionCount, + focusedValue, + visibleFromIndex, + visibleToIndex, + } +} + +export function useSelectNavigation({ + visibleOptionCount = 5, + options, + initialFocusValue, + onFocus, + focusValue, +}: UseSelectNavigationProps): SelectNavigation { + const [state, dispatch] = useReducer( + reducer, + { + visibleOptionCount, + options, + initialFocusValue: focusValue || initialFocusValue, + } as Parameters>[0], + createDefaultState, + ) + + // Store onFocus in a ref to avoid re-running useEffect when callback changes + const onFocusRef = useRef(onFocus) + onFocusRef.current = onFocus + + const [lastOptions, setLastOptions] = useState(options) + + if (options !== lastOptions && !isDeepStrictEqual(options, lastOptions)) { + dispatch({ + type: 'reset', + state: createDefaultState({ + visibleOptionCount, + options, + initialFocusValue: + focusValue ?? state.focusedValue ?? initialFocusValue, + currentViewport: { + visibleFromIndex: state.visibleFromIndex, + visibleToIndex: state.visibleToIndex, + }, + }), + }) + + setLastOptions(options) + } + + const focusNextOption = useCallback(() => { + dispatch({ + type: 'focus-next-option', + }) + }, []) + + const focusPreviousOption = useCallback(() => { + dispatch({ + type: 'focus-previous-option', + }) + }, []) + + const focusNextPage = useCallback(() => { + dispatch({ + type: 'focus-next-page', + }) + }, []) + + const focusPreviousPage = useCallback(() => { + dispatch({ + type: 'focus-previous-page', + }) + }, []) + + const focusOption = useCallback((value: T | undefined) => { + if (value !== undefined) { + dispatch({ + type: 'set-focus', + value, + }) + } + }, []) + + const visibleOptions = useMemo(() => { + return options + .map((option, index) => ({ + ...option, + index, + })) + .slice(state.visibleFromIndex, state.visibleToIndex) + }, [options, state.visibleFromIndex, state.visibleToIndex]) + + // Validate that focusedValue exists in current options. + // This handles the case where options change during render but the reset + // action hasn't been processed yet - without this, the cursor would disappear + // because focusedValue points to an option that no longer exists. + const validatedFocusedValue = useMemo(() => { + if (state.focusedValue === undefined) { + return undefined + } + const exists = options.some(opt => opt.value === state.focusedValue) + if (exists) { + return state.focusedValue + } + // Fall back to first option if focused value doesn't exist + return options[0]?.value + }, [state.focusedValue, options]) + + const isInInput = useMemo(() => { + const focusedOption = options.find( + opt => opt.value === validatedFocusedValue, + ) + return focusedOption?.type === 'input' + }, [validatedFocusedValue, options]) + + // Call onFocus with the validated value (what's actually displayed), + // not the internal state value which may be stale if options changed. + // Use ref to avoid re-running when callback reference changes. + useEffect(() => { + if (validatedFocusedValue !== undefined) { + onFocusRef.current?.(validatedFocusedValue) + } + }, [validatedFocusedValue]) + + // Allow parent to programmatically set focus via focusValue prop + useEffect(() => { + if (focusValue !== undefined) { + dispatch({ + type: 'set-focus', + value: focusValue, + }) + } + }, [focusValue]) + + // Compute 1-based focused index for scroll position display + const focusedIndex = useMemo(() => { + if (validatedFocusedValue === undefined) { + return 0 + } + const index = options.findIndex(opt => opt.value === validatedFocusedValue) + return index >= 0 ? index + 1 : 0 + }, [validatedFocusedValue, options]) + + return { + focusedValue: validatedFocusedValue, + focusedIndex, + visibleFromIndex: state.visibleFromIndex, + visibleToIndex: state.visibleToIndex, + visibleOptions, + isInInput: isInInput ?? false, + focusNextOption, + focusPreviousOption, + focusNextPage, + focusPreviousPage, + focusOption, + options, + } +} diff --git a/components/CustomSelect/use-select-state.ts b/components/CustomSelect/use-select-state.ts new file mode 100644 index 0000000..3951d95 --- /dev/null +++ b/components/CustomSelect/use-select-state.ts @@ -0,0 +1,157 @@ +import { useCallback, useState } from 'react' +import type { OptionWithDescription } from './select.js' +import { useSelectNavigation } from './use-select-navigation.js' + +export type UseSelectStateProps = { + /** + * Number of items to display. + * + * @default 5 + */ + visibleOptionCount?: number + + /** + * Options. + */ + options: OptionWithDescription[] + + /** + * Initially selected option's value. + */ + defaultValue?: T + + /** + * Callback for selecting an option. + */ + onChange?: (value: T) => void + + /** + * Callback for canceling the select. + */ + onCancel?: () => void + + /** + * Callback for focusing an option. + */ + onFocus?: (value: T) => void + + /** + * Value to focus + */ + focusValue?: T +} + +export type SelectState = { + /** + * Value of the currently focused option. + */ + focusedValue: T | undefined + + /** + * 1-based index of the focused option in the full list. + * Returns 0 if no option is focused. + */ + focusedIndex: number + + /** + * Index of the first visible option. + */ + visibleFromIndex: number + + /** + * Index of the last visible option. + */ + visibleToIndex: number + + /** + * Value of the selected option. + */ + value: T | undefined + + /** + * All options. + */ + options: OptionWithDescription[] + + /** + * Visible options. + */ + visibleOptions: Array & { index: number }> + + /** + * Whether the focused option is an input type. + */ + isInInput: boolean + + /** + * Focus next option and scroll the list down, if needed. + */ + focusNextOption: () => void + + /** + * Focus previous option and scroll the list up, if needed. + */ + focusPreviousOption: () => void + + /** + * Focus next page and scroll the list down by a page. + */ + focusNextPage: () => void + + /** + * Focus previous page and scroll the list up by a page. + */ + focusPreviousPage: () => void + + /** + * Focus a specific option by value. + */ + focusOption: (value: T | undefined) => void + + /** + * Select currently focused option. + */ + selectFocusedOption: () => void + + /** + * Callback for selecting an option. + */ + onChange?: (value: T) => void + + /** + * Callback for canceling the select. + */ + onCancel?: () => void +} + +export function useSelectState({ + visibleOptionCount = 5, + options, + defaultValue, + onChange, + onCancel, + onFocus, + focusValue, +}: UseSelectStateProps): SelectState { + const [value, setValue] = useState(defaultValue) + + const navigation = useSelectNavigation({ + visibleOptionCount, + options, + initialFocusValue: undefined, + onFocus, + focusValue, + }) + + const selectFocusedOption = useCallback(() => { + setValue(navigation.focusedValue) + }, [navigation.focusedValue]) + + return { + ...navigation, + value, + selectFocusedOption, + onChange, + onCancel, + } +} diff --git a/components/DesktopHandoff.tsx b/components/DesktopHandoff.tsx new file mode 100644 index 0000000..7e70733 --- /dev/null +++ b/components/DesktopHandoff.tsx @@ -0,0 +1,193 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useEffect, useState } from 'react'; +import type { CommandResultDisplay } from '../commands.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw input for "any key" dismiss and y/n prompt +import { Box, Text, useInput } from '../ink.js'; +import { openBrowser } from '../utils/browser.js'; +import { getDesktopInstallStatus, openCurrentSessionInDesktop } from '../utils/desktopDeepLink.js'; +import { errorMessage } from '../utils/errors.js'; +import { gracefulShutdown } from '../utils/gracefulShutdown.js'; +import { flushSessionStorage } from '../utils/sessionStorage.js'; +import { LoadingState } from './design-system/LoadingState.js'; +const DESKTOP_DOCS_URL = 'https://clau.de/desktop'; +export function getDownloadUrl(): string { + switch (process.platform) { + case 'win32': + return 'https://claude.ai/api/desktop/win32/x64/exe/latest/redirect'; + default: + return 'https://claude.ai/api/desktop/darwin/universal/dmg/latest/redirect'; + } +} +type DesktopHandoffState = 'checking' | 'prompt-download' | 'flushing' | 'opening' | 'success' | 'error'; +type Props = { + onDone: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; +}; +export function DesktopHandoff(t0) { + const $ = _c(20); + const { + onDone + } = t0; + const [state, setState] = useState("checking"); + const [error, setError] = useState(null); + const [downloadMessage, setDownloadMessage] = useState(""); + let t1; + if ($[0] !== error || $[1] !== onDone || $[2] !== state) { + t1 = input => { + if (state === "error") { + onDone(error ?? "Unknown error", { + display: "system" + }); + return; + } + if (state === "prompt-download") { + if (input === "y" || input === "Y") { + openBrowser(getDownloadUrl()).catch(_temp); + onDone(`Starting download. Re-run /desktop once you\u2019ve installed the app.\nLearn more at ${DESKTOP_DOCS_URL}`, { + display: "system" + }); + } else { + if (input === "n" || input === "N") { + onDone(`The desktop app is required for /desktop. Learn more at ${DESKTOP_DOCS_URL}`, { + display: "system" + }); + } + } + } + }; + $[0] = error; + $[1] = onDone; + $[2] = state; + $[3] = t1; + } else { + t1 = $[3]; + } + useInput(t1); + let t2; + let t3; + if ($[4] !== onDone) { + t2 = () => { + const performHandoff = async function performHandoff() { + setState("checking"); + const installStatus = await getDesktopInstallStatus(); + if (installStatus.status === "not-installed") { + setDownloadMessage("Claude Desktop is not installed."); + setState("prompt-download"); + return; + } + if (installStatus.status === "version-too-old") { + setDownloadMessage(`Claude Desktop needs to be updated (found v${installStatus.version}, need v1.1.2396+).`); + setState("prompt-download"); + return; + } + setState("flushing"); + await flushSessionStorage(); + setState("opening"); + const result = await openCurrentSessionInDesktop(); + if (!result.success) { + setError(result.error ?? "Failed to open Claude Desktop"); + setState("error"); + return; + } + setState("success"); + setTimeout(_temp2, 500, onDone); + }; + performHandoff().catch(err => { + setError(errorMessage(err)); + setState("error"); + }); + }; + t3 = [onDone]; + $[4] = onDone; + $[5] = t2; + $[6] = t3; + } else { + t2 = $[5]; + t3 = $[6]; + } + useEffect(t2, t3); + if (state === "error") { + let t4; + if ($[7] !== error) { + t4 = Error: {error}; + $[7] = error; + $[8] = t4; + } else { + t4 = $[8]; + } + let t5; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t5 = Press any key to continue…; + $[9] = t5; + } else { + t5 = $[9]; + } + let t6; + if ($[10] !== t4) { + t6 = {t4}{t5}; + $[10] = t4; + $[11] = t6; + } else { + t6 = $[11]; + } + return t6; + } + if (state === "prompt-download") { + let t4; + if ($[12] !== downloadMessage) { + t4 = {downloadMessage}; + $[12] = downloadMessage; + $[13] = t4; + } else { + t4 = $[13]; + } + let t5; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t5 = Download now? (y/n); + $[14] = t5; + } else { + t5 = $[14]; + } + let t6; + if ($[15] !== t4) { + t6 = {t4}{t5}; + $[15] = t4; + $[16] = t6; + } else { + t6 = $[16]; + } + return t6; + } + let t4; + if ($[17] === Symbol.for("react.memo_cache_sentinel")) { + t4 = { + checking: "Checking for Claude Desktop\u2026", + flushing: "Saving session\u2026", + opening: "Opening Claude Desktop\u2026", + success: "Opening in Claude Desktop\u2026" + }; + $[17] = t4; + } else { + t4 = $[17]; + } + const messages = t4; + const t5 = messages[state]; + let t6; + if ($[18] !== t5) { + t6 = ; + $[18] = t5; + $[19] = t6; + } else { + t6 = $[19]; + } + return t6; +} +async function _temp2(onDone_0) { + onDone_0("Session transferred to Claude Desktop", { + display: "system" + }); + await gracefulShutdown(0, "other"); +} +function _temp() {} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiQ29tbWFuZFJlc3VsdERpc3BsYXkiLCJCb3giLCJUZXh0IiwidXNlSW5wdXQiLCJvcGVuQnJvd3NlciIsImdldERlc2t0b3BJbnN0YWxsU3RhdHVzIiwib3BlbkN1cnJlbnRTZXNzaW9uSW5EZXNrdG9wIiwiZXJyb3JNZXNzYWdlIiwiZ3JhY2VmdWxTaHV0ZG93biIsImZsdXNoU2Vzc2lvblN0b3JhZ2UiLCJMb2FkaW5nU3RhdGUiLCJERVNLVE9QX0RPQ1NfVVJMIiwiZ2V0RG93bmxvYWRVcmwiLCJwcm9jZXNzIiwicGxhdGZvcm0iLCJEZXNrdG9wSGFuZG9mZlN0YXRlIiwiUHJvcHMiLCJvbkRvbmUiLCJyZXN1bHQiLCJvcHRpb25zIiwiZGlzcGxheSIsIkRlc2t0b3BIYW5kb2ZmIiwidDAiLCIkIiwiX2MiLCJzdGF0ZSIsInNldFN0YXRlIiwiZXJyb3IiLCJzZXRFcnJvciIsImRvd25sb2FkTWVzc2FnZSIsInNldERvd25sb2FkTWVzc2FnZSIsInQxIiwiaW5wdXQiLCJjYXRjaCIsIl90ZW1wIiwidDIiLCJ0MyIsInBlcmZvcm1IYW5kb2ZmIiwiaW5zdGFsbFN0YXR1cyIsInN0YXR1cyIsInZlcnNpb24iLCJzdWNjZXNzIiwic2V0VGltZW91dCIsIl90ZW1wMiIsImVyciIsInQ0IiwidDUiLCJTeW1ib2wiLCJmb3IiLCJ0NiIsImNoZWNraW5nIiwiZmx1c2hpbmciLCJvcGVuaW5nIiwibWVzc2FnZXMiLCJvbkRvbmVfMCJdLCJzb3VyY2VzIjpbIkRlc2t0b3BIYW5kb2ZmLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uL2NvbW1hbmRzLmpzJ1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9wcmVmZXItdXNlLWtleWJpbmRpbmdzIC0tIHJhdyBpbnB1dCBmb3IgXCJhbnkga2V5XCIgZGlzbWlzcyBhbmQgeS9uIHByb21wdFxuaW1wb3J0IHsgQm94LCBUZXh0LCB1c2VJbnB1dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IG9wZW5Ccm93c2VyIH0gZnJvbSAnLi4vdXRpbHMvYnJvd3Nlci5qcydcbmltcG9ydCB7XG4gIGdldERlc2t0b3BJbnN0YWxsU3RhdHVzLFxuICBvcGVuQ3VycmVudFNlc3Npb25JbkRlc2t0b3AsXG59IGZyb20gJy4uL3V0aWxzL2Rlc2t0b3BEZWVwTGluay5qcydcbmltcG9ydCB7IGVycm9yTWVzc2FnZSB9IGZyb20gJy4uL3V0aWxzL2Vycm9ycy5qcydcbmltcG9ydCB7IGdyYWNlZnVsU2h1dGRvd24gfSBmcm9tICcuLi91dGlscy9ncmFjZWZ1bFNodXRkb3duLmpzJ1xuaW1wb3J0IHsgZmx1c2hTZXNzaW9uU3RvcmFnZSB9IGZyb20gJy4uL3V0aWxzL3Nlc3Npb25TdG9yYWdlLmpzJ1xuaW1wb3J0IHsgTG9hZGluZ1N0YXRlIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0xvYWRpbmdTdGF0ZS5qcydcblxuY29uc3QgREVTS1RPUF9ET0NTX1VSTCA9ICdodHRwczovL2NsYXUuZGUvZGVza3RvcCdcblxuZXhwb3J0IGZ1bmN0aW9uIGdldERvd25sb2FkVXJsKCk6IHN0cmluZyB7XG4gIHN3aXRjaCAocHJvY2Vzcy5wbGF0Zm9ybSkge1xuICAgIGNhc2UgJ3dpbjMyJzpcbiAgICAgIHJldHVybiAnaHR0cHM6Ly9jbGF1ZGUuYWkvYXBpL2Rlc2t0b3Avd2luMzIveDY0L2V4ZS9sYXRlc3QvcmVkaXJlY3QnXG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiAnaHR0cHM6Ly9jbGF1ZGUuYWkvYXBpL2Rlc2t0b3AvZGFyd2luL3VuaXZlcnNhbC9kbWcvbGF0ZXN0L3JlZGlyZWN0J1xuICB9XG59XG5cbnR5cGUgRGVza3RvcEhhbmRvZmZTdGF0ZSA9XG4gIHwgJ2NoZWNraW5nJ1xuICB8ICdwcm9tcHQtZG93bmxvYWQnXG4gIHwgJ2ZsdXNoaW5nJ1xuICB8ICdvcGVuaW5nJ1xuICB8ICdzdWNjZXNzJ1xuICB8ICdlcnJvcidcblxudHlwZSBQcm9wcyA9IHtcbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIERlc2t0b3BIYW5kb2ZmKHsgb25Eb25lIH06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3N0YXRlLCBzZXRTdGF0ZV0gPSB1c2VTdGF0ZTxEZXNrdG9wSGFuZG9mZlN0YXRlPignY2hlY2tpbmcnKVxuICBjb25zdCBbZXJyb3IsIHNldEVycm9yXSA9IHVzZVN0YXRlPHN0cmluZyB8IG51bGw+KG51bGwpXG4gIGNvbnN0IFtkb3dubG9hZE1lc3NhZ2UsIHNldERvd25sb2FkTWVzc2FnZV0gPSB1c2VTdGF0ZTxzdHJpbmc+KCcnKVxuXG4gIC8vIEhhbmRsZSBrZXlib2FyZCBpbnB1dCBmb3IgZXJyb3IgYW5kIHByb21wdC1kb3dubG9hZCBzdGF0ZXNcbiAgdXNlSW5wdXQoaW5wdXQgPT4ge1xuICAgIGlmIChzdGF0ZSA9PT0gJ2Vycm9yJykge1xuICAgICAgb25Eb25lKGVycm9yID8/ICdVbmtub3duIGVycm9yJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGlmIChzdGF0ZSA9PT0gJ3Byb21wdC1kb3dubG9hZCcpIHtcbiAgICAgIGlmIChpbnB1dCA9PT0gJ3knIHx8IGlucHV0ID09PSAnWScpIHtcbiAgICAgICAgb3BlbkJyb3dzZXIoZ2V0RG93bmxvYWRVcmwoKSkuY2F0Y2goKCkgPT4ge30pXG4gICAgICAgIG9uRG9uZShcbiAgICAgICAgICBgU3RhcnRpbmcgZG93bmxvYWQuIFJlLXJ1biAvZGVza3RvcCBvbmNlIHlvdVxcdTIwMTl2ZSBpbnN0YWxsZWQgdGhlIGFwcC5cXG5MZWFybiBtb3JlIGF0ICR7REVTS1RPUF9ET0NTX1VSTH1gLFxuICAgICAgICAgIHsgZGlzcGxheTogJ3N5c3RlbScgfSxcbiAgICAgICAgKVxuICAgICAgfSBlbHNlIGlmIChpbnB1dCA9PT0gJ24nIHx8IGlucHV0ID09PSAnTicpIHtcbiAgICAgICAgb25Eb25lKFxuICAgICAgICAgIGBUaGUgZGVza3RvcCBhcHAgaXMgcmVxdWlyZWQgZm9yIC9kZXNrdG9wLiBMZWFybiBtb3JlIGF0ICR7REVTS1RPUF9ET0NTX1VSTH1gLFxuICAgICAgICAgIHsgZGlzcGxheTogJ3N5c3RlbScgfSxcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH1cbiAgfSlcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGFzeW5jIGZ1bmN0aW9uIHBlcmZvcm1IYW5kb2ZmKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgLy8gQ2hlY2sgRGVza3RvcCBpbnN0YWxsIHN0YXR1c1xuICAgICAgc2V0U3RhdGUoJ2NoZWNraW5nJylcbiAgICAgIGNvbnN0IGluc3RhbGxTdGF0dXMgPSBhd2FpdCBnZXREZXNrdG9wSW5zdGFsbFN0YXR1cygpXG5cbiAgICAgIGlmIChpbnN0YWxsU3RhdHVzLnN0YXR1cyA9PT0gJ25vdC1pbnN0YWxsZWQnKSB7XG4gICAgICAgIHNldERvd25sb2FkTWVzc2FnZSgnQ2xhdWRlIERlc2t0b3AgaXMgbm90IGluc3RhbGxlZC4nKVxuICAgICAgICBzZXRTdGF0ZSgncHJvbXB0LWRvd25sb2FkJylcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGlmIChpbnN0YWxsU3RhdHVzLnN0YXR1cyA9PT0gJ3ZlcnNpb24tdG9vLW9sZCcpIHtcbiAgICAgICAgc2V0RG93bmxvYWRNZXNzYWdlKFxuICAgICAgICAgIGBDbGF1ZGUgRGVza3RvcCBuZWVkcyB0byBiZSB1cGRhdGVkIChmb3VuZCB2JHtpbnN0YWxsU3RhdHVzLnZlcnNpb259LCBuZWVkIHYxLjEuMjM5NispLmAsXG4gICAgICAgIClcbiAgICAgICAgc2V0U3RhdGUoJ3Byb21wdC1kb3dubG9hZCcpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBGbHVzaCBzZXNzaW9uIHN0b3JhZ2UgdG8gZW5zdXJlIHRyYW5zY3JpcHQgaXMgZnVsbHkgd3JpdHRlblxuICAgICAgc2V0U3RhdGUoJ2ZsdXNoaW5nJylcbiAgICAgIGF3YWl0IGZsdXNoU2Vzc2lvblN0b3JhZ2UoKVxuXG4gICAgICAvLyBPcGVuIHRoZSBkZWVwIGxpbmsgKHVzZXMgY2xhdWRlLWRldjovLyBpbiBkZXYgbW9kZSlcbiAgICAgIHNldFN0YXRlKCdvcGVuaW5nJylcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IG9wZW5DdXJyZW50U2Vzc2lvbkluRGVza3RvcCgpXG5cbiAgICAgIGlmICghcmVzdWx0LnN1Y2Nlc3MpIHtcbiAgICAgICAgc2V0RXJyb3IocmVzdWx0LmVycm9yID8/ICdGYWlsZWQgdG8gb3BlbiBDbGF1ZGUgRGVza3RvcCcpXG4gICAgICAgIHNldFN0YXRlKCdlcnJvcicpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBTdWNjZXNzIC0gZXhpdCB0aGUgQ0xJXG4gICAgICBzZXRTdGF0ZSgnc3VjY2VzcycpXG5cbiAgICAgIC8vIEdpdmUgdGhlIHVzZXIgYSBtb21lbnQgdG8gc2VlIHRoZSBzdWNjZXNzIG1lc3NhZ2VcbiAgICAgIHNldFRpbWVvdXQoXG4gICAgICAgIGFzeW5jIChvbkRvbmU6IFByb3BzWydvbkRvbmUnXSkgPT4ge1xuICAgICAgICAgIG9uRG9uZSgnU2Vzc2lvbiB0cmFuc2ZlcnJlZCB0byBDbGF1ZGUgRGVza3RvcCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgICAgICAgICBhd2FpdCBncmFjZWZ1bFNodXRkb3duKDAsICdvdGhlcicpXG4gICAgICAgIH0sXG4gICAgICAgIDUwMCxcbiAgICAgICAgb25Eb25lLFxuICAgICAgKVxuICAgIH1cblxuICAgIHBlcmZvcm1IYW5kb2ZmKCkuY2F0Y2goZXJyID0+IHtcbiAgICAgIHNldEVycm9yKGVycm9yTWVzc2FnZShlcnIpKVxuICAgICAgc2V0U3RhdGUoJ2Vycm9yJylcbiAgICB9KVxuICB9LCBbb25Eb25lXSlcblxuICBpZiAoc3RhdGUgPT09ICdlcnJvcicpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgcGFkZGluZ1g9ezJ9PlxuICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+RXJyb3I6IHtlcnJvcn08L1RleHQ+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlByZXNzIGFueSBrZXkgdG8gY29udGludWXigKY8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBpZiAoc3RhdGUgPT09ICdwcm9tcHQtZG93bmxvYWQnKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdYPXsyfT5cbiAgICAgICAgPFRleHQ+e2Rvd25sb2FkTWVzc2FnZX08L1RleHQ+XG4gICAgICAgIDxUZXh0PkRvd25sb2FkIG5vdz8gKHkvbik8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBjb25zdCBtZXNzYWdlczogUmVjb3JkPFxuICAgIEV4Y2x1ZGU8RGVza3RvcEhhbmRvZmZTdGF0ZSwgJ2Vycm9yJyB8ICdwcm9tcHQtZG93bmxvYWQnPixcbiAgICBzdHJpbmdcbiAgPiA9IHtcbiAgICBjaGVja2luZzogJ0NoZWNraW5nIGZvciBDbGF1ZGUgRGVza3RvcOKApicsXG4gICAgZmx1c2hpbmc6ICdTYXZpbmcgc2Vzc2lvbuKApicsXG4gICAgb3BlbmluZzogJ09wZW5pbmcgQ2xhdWRlIERlc2t0b3DigKYnLFxuICAgIHN1Y2Nlc3M6ICdPcGVuaW5nIGluIENsYXVkZSBEZXNrdG9w4oCmJyxcbiAgfVxuXG4gIHJldHVybiA8TG9hZGluZ1N0YXRlIG1lc3NhZ2U9e21lc3NhZ2VzW3N0YXRl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 \ No newline at end of file diff --git a/components/DesktopUpsell/DesktopUpsellStartup.tsx b/components/DesktopUpsell/DesktopUpsellStartup.tsx new file mode 100644 index 0000000..e919039 --- /dev/null +++ b/components/DesktopUpsell/DesktopUpsellStartup.tsx @@ -0,0 +1,171 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { Box, Text } from '../../ink.js'; +import { getDynamicConfig_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'; +import { logEvent } from '../../services/analytics/index.js'; +import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'; +import { Select } from '../CustomSelect/select.js'; +import { DesktopHandoff } from '../DesktopHandoff.js'; +import { PermissionDialog } from '../permissions/PermissionDialog.js'; +type DesktopUpsellConfig = { + enable_shortcut_tip: boolean; + enable_startup_dialog: boolean; +}; +const DESKTOP_UPSELL_DEFAULT: DesktopUpsellConfig = { + enable_shortcut_tip: false, + enable_startup_dialog: false +}; +export function getDesktopUpsellConfig(): DesktopUpsellConfig { + return getDynamicConfig_CACHED_MAY_BE_STALE('tengu_desktop_upsell', DESKTOP_UPSELL_DEFAULT); +} +function isSupportedPlatform(): boolean { + return process.platform === 'darwin' || process.platform === 'win32' && process.arch === 'x64'; +} +export function shouldShowDesktopUpsellStartup(): boolean { + if (!isSupportedPlatform()) return false; + if (!getDesktopUpsellConfig().enable_startup_dialog) return false; + const config = getGlobalConfig(); + if (config.desktopUpsellDismissed) return false; + if ((config.desktopUpsellSeenCount ?? 0) >= 3) return false; + return true; +} +type DesktopUpsellSelection = 'try' | 'not-now' | 'never'; +type Props = { + onDone: () => void; +}; +export function DesktopUpsellStartup(t0) { + const $ = _c(14); + const { + onDone + } = t0; + const [showHandoff, setShowHandoff] = useState(false); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = []; + $[0] = t1; + } else { + t1 = $[0]; + } + useEffect(_temp, t1); + if (showHandoff) { + let t2; + if ($[1] !== onDone) { + t2 = onDone()} />; + $[1] = onDone; + $[2] = t2; + } else { + t2 = $[2]; + } + return t2; + } + let t2; + if ($[3] !== onDone) { + t2 = function handleSelect(value) { + switch (value) { + case "try": + { + setShowHandoff(true); + return; + } + case "never": + { + saveGlobalConfig(_temp2); + onDone(); + return; + } + case "not-now": + { + onDone(); + return; + } + } + }; + $[3] = onDone; + $[4] = t2; + } else { + t2 = $[4]; + } + const handleSelect = t2; + let t3; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t3 = { + label: "Open in Claude Code Desktop", + value: "try" as const + }; + $[5] = t3; + } else { + t3 = $[5]; + } + let t4; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t4 = { + label: "Not now", + value: "not-now" as const + }; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t5 = [t3, t4, { + label: "Don't ask again", + value: "never" as const + }]; + $[7] = t5; + } else { + t5 = $[7]; + } + const options = t5; + let t6; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t6 = Same Claude Code with visual diffs, live app preview, parallel sessions, and more.; + $[8] = t6; + } else { + t6 = $[8]; + } + let t7; + if ($[9] !== handleSelect) { + t7 = () => handleSelect("not-now"); + $[9] = handleSelect; + $[10] = t7; + } else { + t7 = $[10]; + } + let t8; + if ($[11] !== handleSelect || $[12] !== t7) { + t8 = {t6} onChange(value_0 as 'accept' | 'exit')} />; + $[9] = onChange; + $[10] = t7; + } else { + t7 = $[10]; + } + let t8; + if ($[11] !== t5 || $[12] !== t7) { + t8 = {t5}{t7}; + $[11] = t5; + $[12] = t7; + $[13] = t8; + } else { + t8 = $[13]; + } + return t8; +} +function _temp2(c) { + return c.kind === "plugin" ? `plugin:${c.name}@${c.marketplace}` : `server:${c.name}`; +} +function _temp() { + gracefulShutdownSync(0); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwiQ2hhbm5lbEVudHJ5IiwiQm94IiwiVGV4dCIsImdyYWNlZnVsU2h1dGRvd25TeW5jIiwiU2VsZWN0IiwiRGlhbG9nIiwiUHJvcHMiLCJjaGFubmVscyIsIm9uQWNjZXB0IiwiRGV2Q2hhbm5lbHNEaWFsb2ciLCJ0MCIsIiQiLCJfYyIsInQxIiwib25DaGFuZ2UiLCJ2YWx1ZSIsImJiMiIsImhhbmRsZUVzY2FwZSIsIl90ZW1wIiwidDIiLCJ0MyIsIlN5bWJvbCIsImZvciIsInQ0IiwibWFwIiwiX3RlbXAyIiwiam9pbiIsInQ1IiwidDYiLCJsYWJlbCIsInQ3IiwidmFsdWVfMCIsInQ4IiwiYyIsImtpbmQiLCJuYW1lIiwibWFya2V0cGxhY2UiXSwic291cmNlcyI6WyJEZXZDaGFubmVsc0RpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHVzZUNhbGxiYWNrIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IENoYW5uZWxFbnRyeSB9IGZyb20gJy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGdyYWNlZnVsU2h1dGRvd25TeW5jIH0gZnJvbSAnLi4vdXRpbHMvZ3JhY2VmdWxTaHV0ZG93bi5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L2luZGV4LmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgY2hhbm5lbHM6IENoYW5uZWxFbnRyeVtdXG4gIG9uQWNjZXB0KCk6IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIERldkNoYW5uZWxzRGlhbG9nKHtcbiAgY2hhbm5lbHMsXG4gIG9uQWNjZXB0LFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBmdW5jdGlvbiBvbkNoYW5nZSh2YWx1ZTogJ2FjY2VwdCcgfCAnZXhpdCcpIHtcbiAgICBzd2l0Y2ggKHZhbHVlKSB7XG4gICAgICBjYXNlICdhY2NlcHQnOlxuICAgICAgICBvbkFjY2VwdCgpXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdleGl0JzpcbiAgICAgICAgZ3JhY2VmdWxTaHV0ZG93blN5bmMoMSlcbiAgICAgICAgYnJlYWtcbiAgICB9XG4gIH1cblxuICBjb25zdCBoYW5kbGVFc2NhcGUgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgZ3JhY2VmdWxTaHV0ZG93blN5bmMoMClcbiAgfSwgW10pXG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT1cIldBUk5JTkc6IExvYWRpbmcgZGV2ZWxvcG1lbnQgY2hhbm5lbHNcIlxuICAgICAgY29sb3I9XCJlcnJvclwiXG4gICAgICBvbkNhbmNlbD17aGFuZGxlRXNjYXBlfVxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIC0tZGFuZ2Vyb3VzbHktbG9hZC1kZXZlbG9wbWVudC1jaGFubmVscyBpcyBmb3IgbG9jYWwgY2hhbm5lbFxuICAgICAgICAgIGRldmVsb3BtZW50IG9ubHkuIERvIG5vdCB1c2UgdGhpcyBvcHRpb24gdG8gcnVuIGNoYW5uZWxzIHlvdSBoYXZlXG4gICAgICAgICAgZG93bmxvYWRlZCBvZmYgdGhlIGludGVybmV0LlxuICAgICAgICA8L1RleHQ+XG4gICAgICAgIDxUZXh0PlBsZWFzZSB1c2UgLS1jaGFubmVscyB0byBydW4gYSBsaXN0IG9mIGFwcHJvdmVkIGNoYW5uZWxzLjwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgQ2hhbm5lbHM6eycgJ31cbiAgICAgICAgICB7Y2hhbm5lbHNcbiAgICAgICAgICAgIC5tYXAoYyA9PlxuICAgICAgICAgICAgICBjLmtpbmQgPT09ICdwbHVnaW4nXG4gICAgICAgICAgICAgICAgPyBgcGx1Z2luOiR7Yy5uYW1lfUAke2MubWFya2V0cGxhY2V9YFxuICAgICAgICAgICAgICAgIDogYHNlcnZlcjoke2MubmFtZX1gLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgLmpvaW4oJywgJyl9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuXG4gICAgICA8U2VsZWN0XG4gICAgICAgIG9wdGlvbnM9e1tcbiAgICAgICAgICB7IGxhYmVsOiAnSSBhbSB1c2luZyB0aGlzIGZvciBsb2NhbCBkZXZlbG9wbWVudCcsIHZhbHVlOiAnYWNjZXB0JyB9LFxuICAgICAgICAgIHsgbGFiZWw6ICdFeGl0JywgdmFsdWU6ICdleGl0JyB9LFxuICAgICAgICBdfVxuICAgICAgICBvbkNoYW5nZT17dmFsdWUgPT4gb25DaGFuZ2UodmFsdWUgYXMgJ2FjY2VwdCcgfCAnZXhpdCcpfVxuICAgICAgLz5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJQyxXQUFXLFFBQVEsT0FBTztBQUMxQyxjQUFjQyxZQUFZLFFBQVEsdUJBQXVCO0FBQ3pELFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDckMsU0FBU0Msb0JBQW9CLFFBQVEsOEJBQThCO0FBQ25FLFNBQVNDLE1BQU0sUUFBUSx5QkFBeUI7QUFDaEQsU0FBU0MsTUFBTSxRQUFRLDJCQUEyQjtBQUVsRCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsUUFBUSxFQUFFUCxZQUFZLEVBQUU7RUFDeEJRLFFBQVEsRUFBRSxFQUFFLElBQUk7QUFDbEIsQ0FBQztBQUVELE9BQU8sU0FBQUMsa0JBQUFDLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBMkI7SUFBQUwsUUFBQTtJQUFBQztFQUFBLElBQUFFLEVBRzFCO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQUgsUUFBQTtJQUNOSyxFQUFBLFlBQUFDLFNBQUFDLEtBQUE7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 \ No newline at end of file diff --git a/components/DiagnosticsDisplay.tsx b/components/DiagnosticsDisplay.tsx new file mode 100644 index 0000000..6eb3ad8 --- /dev/null +++ b/components/DiagnosticsDisplay.tsx @@ -0,0 +1,95 @@ +import { c as _c } from "react/compiler-runtime"; +import { relative } from 'path'; +import React from 'react'; +import { Box, Text } from '../ink.js'; +import { DiagnosticTrackingService } from '../services/diagnosticTracking.js'; +import type { Attachment } from '../utils/attachments.js'; +import { getCwd } from '../utils/cwd.js'; +import { CtrlOToExpand } from './CtrlOToExpand.js'; +import { MessageResponse } from './MessageResponse.js'; +type DiagnosticsAttachment = Extract; +type DiagnosticsDisplayProps = { + attachment: DiagnosticsAttachment; + verbose: boolean; +}; +export function DiagnosticsDisplay(t0) { + const $ = _c(14); + const { + attachment, + verbose + } = t0; + if (attachment.files.length === 0) { + return null; + } + let t1; + if ($[0] !== attachment.files) { + t1 = attachment.files.reduce(_temp, 0); + $[0] = attachment.files; + $[1] = t1; + } else { + t1 = $[1]; + } + const totalIssues = t1; + const fileCount = attachment.files.length; + if (verbose) { + let t2; + if ($[2] !== attachment.files) { + t2 = attachment.files.map(_temp3); + $[2] = attachment.files; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== t2) { + t3 = {t2}; + $[4] = t2; + $[5] = t3; + } else { + t3 = $[5]; + } + return t3; + } else { + let t2; + if ($[6] !== totalIssues) { + t2 = {totalIssues}; + $[6] = totalIssues; + $[7] = t2; + } else { + t2 = $[7]; + } + const t3 = totalIssues === 1 ? "issue" : "issues"; + const t4 = fileCount === 1 ? "file" : "files"; + let t5; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t5 = ; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] !== fileCount || $[10] !== t2 || $[11] !== t3 || $[12] !== t4) { + t6 = Found {t2} new diagnostic{" "}{t3} in {fileCount}{" "}{t4} {t5}; + $[9] = fileCount; + $[10] = t2; + $[11] = t3; + $[12] = t4; + $[13] = t6; + } else { + t6 = $[13]; + } + return t6; + } +} +function _temp3(file_0, fileIndex) { + return {relative(getCwd(), file_0.uri.replace("file://", "").replace("_claude_fs_right:", ""))}{" "}{file_0.uri.startsWith("file://") ? "(file://)" : file_0.uri.startsWith("_claude_fs_right:") ? "(claude_fs_right)" : `(${file_0.uri.split(":")[0]})`}:{file_0.diagnostics.map(_temp2)}; +} +function _temp2(diagnostic, diagIndex) { + return {" "}{DiagnosticTrackingService.getSeveritySymbol(diagnostic.severity)}{" [Line "}{diagnostic.range.start.line + 1}:{diagnostic.range.start.character + 1}{"] "}{diagnostic.message}{diagnostic.code ? ` [${diagnostic.code}]` : ""}{diagnostic.source ? ` (${diagnostic.source})` : ""}; +} +function _temp(sum, file) { + return sum + file.diagnostics.length; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyZWxhdGl2ZSIsIlJlYWN0IiwiQm94IiwiVGV4dCIsIkRpYWdub3N0aWNUcmFja2luZ1NlcnZpY2UiLCJBdHRhY2htZW50IiwiZ2V0Q3dkIiwiQ3RybE9Ub0V4cGFuZCIsIk1lc3NhZ2VSZXNwb25zZSIsIkRpYWdub3N0aWNzQXR0YWNobWVudCIsIkV4dHJhY3QiLCJ0eXBlIiwiRGlhZ25vc3RpY3NEaXNwbGF5UHJvcHMiLCJhdHRhY2htZW50IiwidmVyYm9zZSIsIkRpYWdub3N0aWNzRGlzcGxheSIsInQwIiwiJCIsIl9jIiwiZmlsZXMiLCJsZW5ndGgiLCJ0MSIsInJlZHVjZSIsIl90ZW1wIiwidG90YWxJc3N1ZXMiLCJmaWxlQ291bnQiLCJ0MiIsIm1hcCIsIl90ZW1wMyIsInQzIiwidDQiLCJ0NSIsIlN5bWJvbCIsImZvciIsInQ2IiwiZmlsZV8wIiwiZmlsZUluZGV4IiwiZmlsZSIsInVyaSIsInJlcGxhY2UiLCJzdGFydHNXaXRoIiwic3BsaXQiLCJkaWFnbm9zdGljcyIsIl90ZW1wMiIsImRpYWdub3N0aWMiLCJkaWFnSW5kZXgiLCJnZXRTZXZlcml0eVN5bWJvbCIsInNldmVyaXR5IiwicmFuZ2UiLCJzdGFydCIsImxpbmUiLCJjaGFyYWN0ZXIiLCJtZXNzYWdlIiwiY29kZSIsInNvdXJjZSIsInN1bSJdLCJzb3VyY2VzIjpbIkRpYWdub3N0aWNzRGlzcGxheS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcmVsYXRpdmUgfSBmcm9tICdwYXRoJ1xuaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgRGlhZ25vc3RpY1RyYWNraW5nU2VydmljZSB9IGZyb20gJy4uL3NlcnZpY2VzL2RpYWdub3N0aWNUcmFja2luZy5qcydcbmltcG9ydCB0eXBlIHsgQXR0YWNobWVudCB9IGZyb20gJy4uL3V0aWxzL2F0dGFjaG1lbnRzLmpzJ1xuaW1wb3J0IHsgZ2V0Q3dkIH0gZnJvbSAnLi4vdXRpbHMvY3dkLmpzJ1xuaW1wb3J0IHsgQ3RybE9Ub0V4cGFuZCB9IGZyb20gJy4vQ3RybE9Ub0V4cGFuZC5qcydcbmltcG9ydCB7IE1lc3NhZ2VSZXNwb25zZSB9IGZyb20gJy4vTWVzc2FnZVJlc3BvbnNlLmpzJ1xuXG50eXBlIERpYWdub3N0aWNzQXR0YWNobWVudCA9IEV4dHJhY3Q8QXR0YWNobWVudCwgeyB0eXBlOiAnZGlhZ25vc3RpY3MnIH0+XG5cbnR5cGUgRGlhZ25vc3RpY3NEaXNwbGF5UHJvcHMgPSB7XG4gIGF0dGFjaG1lbnQ6IERpYWdub3N0aWNzQXR0YWNobWVudFxuICB2ZXJib3NlOiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBEaWFnbm9zdGljc0Rpc3BsYXkoe1xuICBhdHRhY2htZW50LFxuICB2ZXJib3NlLFxufTogRGlhZ25vc3RpY3NEaXNwbGF5UHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBPbmx5IHNob3cgaWYgdGhlcmUgYXJlIGRpYWdub3N0aWNzIHRvIHJlcG9ydFxuICBpZiAoYXR0YWNobWVudC5maWxlcy5sZW5ndGggPT09IDApIHJldHVybiBudWxsXG5cbiAgLy8gQ291bnQgdG90YWwgaXNzdWVzXG4gIGNvbnN0IHRvdGFsSXNzdWVzID0gYXR0YWNobWVudC5maWxlcy5yZWR1Y2UoXG4gICAgKHN1bSwgZmlsZSkgPT4gc3VtICsgZmlsZS5kaWFnbm9zdGljcy5sZW5ndGgsXG4gICAgMCxcbiAgKVxuXG4gIGNvbnN0IGZpbGVDb3VudCA9IGF0dGFjaG1lbnQuZmlsZXMubGVuZ3RoXG5cbiAgaWYgKHZlcmJvc2UpIHtcbiAgICAvLyBTaG93IGFsbCBkaWFnbm9zdGljcyBpbiB2ZXJib3NlIG1vZGUgKGN0cmwrbylcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIHthdHRhY2htZW50LmZpbGVzLm1hcCgoZmlsZSwgZmlsZUluZGV4KSA9PiAoXG4gICAgICAgICAgPFJlYWN0LkZyYWdtZW50IGtleT17ZmlsZUluZGV4fT5cbiAgICAgICAgICAgIDxNZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIHdyYXA9XCJ3cmFwXCI+XG4gICAgICAgICAgICAgICAgPFRleHQgYm9sZD5cbiAgICAgICAgICAgICAgICAgIHtyZWxhdGl2ZShcbiAgICAgICAgICAgICAgICAgICAgZ2V0Q3dkKCksXG4gICAgICAgICAgICAgICAgICAgIGZpbGUudXJpXG4gICAgICAgICAgICAgICAgICAgICAgLnJlcGxhY2UoJ2ZpbGU6Ly8nLCAnJylcbiAgICAgICAgICAgICAgICAgICAgICAucmVwbGFjZSgnX2NsYXVkZV9mc19yaWdodDonLCAnJyksXG4gICAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgIDwvVGV4dD57JyAnfVxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAge2ZpbGUudXJpLnN0YXJ0c1dpdGgoJ2ZpbGU6Ly8nKVxuICAgICAgICAgICAgICAgICAgICA/ICcoZmlsZTovLyknXG4gICAgICAgICAgICAgICAgICAgIDogZmlsZS51cmkuc3RhcnRzV2l0aCgnX2NsYXVkZV9mc19yaWdodDonKVxuICAgICAgICAgICAgICAgICAgICAgID8gJyhjbGF1ZGVfZnNfcmlnaHQpJ1xuICAgICAgICAgICAgICAgICAgICAgIDogYCgke2ZpbGUudXJpLnNwbGl0KCc6JylbMF19KWB9XG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDpcbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgICAgICB7ZmlsZS5kaWFnbm9zdGljcy5tYXAoKGRpYWdub3N0aWMsIGRpYWdJbmRleCkgPT4gKFxuICAgICAgICAgICAgICA8TWVzc2FnZVJlc3BvbnNlIGtleT17ZGlhZ0luZGV4fT5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvciB3cmFwPVwid3JhcFwiPlxuICAgICAgICAgICAgICAgICAgeycgICd9XG4gICAgICAgICAgICAgICAgICB7RGlhZ25vc3RpY1RyYWNraW5nU2VydmljZS5nZXRTZXZlcml0eVN5bWJvbChcbiAgICAgICAgICAgICAgICAgICAgZGlhZ25vc3RpYy5zZXZlcml0eSxcbiAgICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgICAgICB7JyBbTGluZSAnfVxuICAgICAgICAgICAgICAgICAge2RpYWdub3N0aWMucmFuZ2Uuc3RhcnQubGluZSArIDF9OlxuICAgICAgICAgICAgICAgICAge2RpYWdub3N0aWMucmFuZ2Uuc3RhcnQuY2hhcmFjdGVyICsgMX1cbiAgICAgICAgICAgICAgICAgIHsnXSAnfVxuICAgICAgICAgICAgICAgICAge2RpYWdub3N0aWMubWVzc2FnZX1cbiAgICAgICAgICAgICAgICAgIHtkaWFnbm9zdGljLmNvZGUgPyBgIFske2RpYWdub3N0aWMuY29kZX1dYCA6ICcnfVxuICAgICAgICAgICAgICAgICAge2RpYWdub3N0aWMuc291cmNlID8gYCAoJHtkaWFnbm9zdGljLnNvdXJjZX0pYCA6ICcnfVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgICAgICApKX1cbiAgICAgICAgICA8L1JlYWN0LkZyYWdtZW50PlxuICAgICAgICApKX1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfSBlbHNlIHtcbiAgICAvLyBTaG93IHN1bW1hcnkgaW4gbm9ybWFsIG1vZGVcbiAgICByZXR1cm4gKFxuICAgICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgPFRleHQgZGltQ29sb3Igd3JhcD1cIndyYXBcIj5cbiAgICAgICAgICBGb3VuZCA8VGV4dCBib2xkPnt0b3RhbElzc3Vlc308L1RleHQ+IG5ldyBkaWFnbm9zdGljeycgJ31cbiAgICAgICAgICB7dG90YWxJc3N1ZXMgPT09IDEgPyAnaXNzdWUnIDogJ2lzc3Vlcyd9IGluIHtmaWxlQ291bnR9eycgJ31cbiAgICAgICAgICB7ZmlsZUNvdW50ID09PSAxID8gJ2ZpbGUnIDogJ2ZpbGVzJ30gPEN0cmxPVG9FeHBhbmQgLz5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgKVxuICB9XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxRQUFRLFFBQVEsTUFBTTtBQUMvQixPQUFPQyxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLFNBQVNDLHlCQUF5QixRQUFRLG1DQUFtQztBQUM3RSxjQUFjQyxVQUFVLFFBQVEseUJBQXlCO0FBQ3pELFNBQVNDLE1BQU0sUUFBUSxpQkFBaUI7QUFDeEMsU0FBU0MsYUFBYSxRQUFRLG9CQUFvQjtBQUNsRCxTQUFTQyxlQUFlLFFBQVEsc0JBQXNCO0FBRXRELEtBQUtDLHFCQUFxQixHQUFHQyxPQUFPLENBQUNMLFVBQVUsRUFBRTtFQUFFTSxJQUFJLEVBQUUsYUFBYTtBQUFDLENBQUMsQ0FBQztBQUV6RSxLQUFLQyx1QkFBdUIsR0FBRztFQUM3QkMsVUFBVSxFQUFFSixxQkFBcUI7RUFDakNLLE9BQU8sRUFBRSxPQUFPO0FBQ2xCLENBQUM7QUFFRCxPQUFPLFNBQUFDLG1CQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQTRCO0lBQUFMLFVBQUE7SUFBQUM7RUFBQSxJQUFBRSxFQUdUO0VBRXhCLElBQUlILFVBQVUsQ0FBQU0sS0FBTSxDQUFBQyxNQUFPLEtBQUssQ0FBQztJQUFBLE9BQVMsSUFBSTtFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFKLENBQUEsUUFBQUosVUFBQSxDQUFBTSxLQUFBO0lBRzFCRSxFQUFBLEdBQUFSLFVBQVUsQ0FBQU0sS0FBTSxDQUFBRyxNQUFPLENBQ3pDQyxLQUE0QyxFQUM1QyxDQUNGLENBQUM7SUFBQU4sQ0FBQSxNQUFBSixVQUFBLENB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QUNSK0IsTUFBSSxDQUFBQyxHQUFJLENBQUFDLE9BQ0UsQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDLENBQUFBLE9BQ2YsQ0FBQyxtQkFBbUIsRUFBRSxFQUFFLENBQ3BDLEVBQ0YsRUFQQyxJQUFJLENBT0csSUFBRSxDQUNWLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FDWCxDQUFBRixNQUFJLENBQUFDLEdBQUksQ0FBQUUsVUFBVyxDQUFDLFNBSWEsQ0FBQyxHQUpsQyxXQUlrQyxHQUYvQkgsTUFBSSxDQUFBQyxHQUFJLENBQUFFLFVBQVcsQ0FBQyxtQkFFVSxDQUFDLEdBRi9CLG1CQUUrQixHQUYvQixJQUVNSCxNQUFJLENBQUFDLEdBQUksQ0FBQUcsS0FBTSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEdBQUUsQ0FDcEMsRUFOQyxJQUFJLENBTUUsQ0FFVCxFQWpCQyxJQUFJLENBa0JQLEVBbkJDLGVBQWUsQ0FvQmYsQ0FBQUosTUFBSSxDQUFBSyxXQUFZLENBQUFmLEdBQUksQ0FBQ2dCLE1BZ0JyQixFQUNILGlCQUFpQjtBQUFBO0FBMURwQixTQUFBQSxPQUFBQyxVQUFBLEVBQUFDLFNBQUE7RUFBQSxPQTBDTyxDQUFDLGVBQWUsQ0FBTUEsR0FBUyxDQUFUQSxVQUFRLENBQUMsQ0FDN0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFNLElBQU0sQ0FBTixNQUFNLENBQ3ZCLEtBQUcsQ0FDSCxDQUFBekMseUJBQXlCLENBQUEwQyxpQkFBa0IsQ0FDMUNGLFVBQVUsQ0FBQUcsUUFDWixFQUNDLFVBQVEsQ0FDUixDQUFBSCxVQUFVLENBQUFJLEtBQU0sQ0FBQUMsS0FBTSxDQUFBQyxJQUFLLEdBQUcsRUFBRSxDQUNoQyxDQUFBTixVQUFVLENBQUFJLEtBQU0sQ0FBQUMsS0FBTSxDQUFBRSxTQUFVLEdBQUcsRUFDbkMsS0FBRyxDQUNILENBQUFQLFVBQVUsQ0FBQVEsT0FBTyxDQUNqQixDQUFBUixVQUFVLENBQUFTLElBQW9DLEdBQTlDLEtBQXVCVCxVQUFVLENBQUFTLElBQUssR0FBUSxHQUE5QyxFQUE2QyxDQUM3QyxDQUFBVCxVQUFVLENBQUFVLE1BQXdDLEdBQWxELEtBQXlCVixVQUFVLENBQUFVLE1BQU8sR0FBUSxHQUFsRCxFQUFpRCxDQUNwRCxFQVpDLElBQUksQ0FhUCxFQWRDLGVBQWUsQ0FjRTtBQUFBO0FBeER6QixTQUFBL0IsTUFBQWdDLEdBQUEsRUFBQWxCLElBQUE7RUFBQSxPQVNZa0IsR0FBRyxHQUFHbEIsSUFBSSxDQUFBSyxXQUFZLENBQUF0QixNQUFPO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/EffortCallout.tsx b/components/EffortCallout.tsx new file mode 100644 index 0000000..68e311f --- /dev/null +++ b/components/EffortCallout.tsx @@ -0,0 +1,265 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useEffect, useRef } from 'react'; +import { Box, Text } from '../ink.js'; +import { isMaxSubscriber, isProSubscriber, isTeamSubscriber } from '../utils/auth.js'; +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js'; +import type { EffortLevel } from '../utils/effort.js'; +import { convertEffortValueToLevel, getDefaultEffortForModel, getOpusDefaultEffortConfig, toPersistableEffort } from '../utils/effort.js'; +import { parseUserSpecifiedModel } from '../utils/model/model.js'; +import { updateSettingsForSource } from '../utils/settings/settings.js'; +import type { OptionWithDescription } from './CustomSelect/select.js'; +import { Select } from './CustomSelect/select.js'; +import { effortLevelToSymbol } from './EffortIndicator.js'; +import { PermissionDialog } from './permissions/PermissionDialog.js'; +type EffortCalloutSelection = EffortLevel | undefined | 'dismiss'; +type Props = { + model: string; + onDone: (selection: EffortCalloutSelection) => void; +}; +const AUTO_DISMISS_MS = 30_000; +export function EffortCallout(t0) { + const $ = _c(18); + const { + model, + onDone + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = getOpusDefaultEffortConfig(); + $[0] = t1; + } else { + t1 = $[0]; + } + const defaultEffortConfig = t1; + const onDoneRef = useRef(onDone); + let t2; + if ($[1] !== onDone) { + t2 = () => { + onDoneRef.current = onDone; + }; + $[1] = onDone; + $[2] = t2; + } else { + t2 = $[2]; + } + useEffect(t2); + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = () => { + onDoneRef.current("dismiss"); + }; + $[3] = t3; + } else { + t3 = $[3]; + } + const handleCancel = t3; + let t4; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t4 = []; + $[4] = t4; + } else { + t4 = $[4]; + } + useEffect(_temp, t4); + let t5; + let t6; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t5 = () => { + const timeoutId = setTimeout(handleCancel, AUTO_DISMISS_MS); + return () => clearTimeout(timeoutId); + }; + t6 = [handleCancel]; + $[5] = t5; + $[6] = t6; + } else { + t5 = $[5]; + t6 = $[6]; + } + useEffect(t5, t6); + let t7; + if ($[7] !== model) { + const defaultEffort = getDefaultEffortForModel(model); + t7 = defaultEffort ? convertEffortValueToLevel(defaultEffort) : "high"; + $[7] = model; + $[8] = t7; + } else { + t7 = $[8]; + } + const defaultLevel = t7; + let t8; + if ($[9] !== defaultLevel) { + t8 = value => { + const effortLevel = value === defaultLevel ? undefined : value; + updateSettingsForSource("userSettings", { + effortLevel: toPersistableEffort(effortLevel) + }); + onDoneRef.current(value); + }; + $[9] = defaultLevel; + $[10] = t8; + } else { + t8 = $[10]; + } + const handleSelect = t8; + let t9; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t9 = [{ + label: , + value: "medium" + }, { + label: , + value: "high" + }, { + label: , + value: "low" + }]; + $[11] = t9; + } else { + t9 = $[11]; + } + const options = t9; + let t10; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t10 = {defaultEffortConfig.dialogDescription}; + $[12] = t10; + } else { + t10 = $[12]; + } + let t11; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t11 = ; + $[13] = t11; + } else { + t11 = $[13]; + } + let t12; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t12 = ; + $[14] = t12; + } else { + t12 = $[14]; + } + let t13; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t13 = {t11} low {"\xB7"}{" "}{t12} medium {"\xB7"}{" "} high; + $[15] = t13; + } else { + t13 = $[15]; + } + let t14; + if ($[16] !== handleSelect) { + t14 = {t10}{t13} : + Enter filename: + + > + + + } + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJqb2luIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZVN0YXRlIiwiRXhpdFN0YXRlIiwidXNlVGVybWluYWxTaXplIiwic2V0Q2xpcGJvYXJkIiwiQm94IiwiVGV4dCIsInVzZUtleWJpbmRpbmciLCJnZXRDd2QiLCJ3cml0ZUZpbGVTeW5jX0RFUFJFQ0FURUQiLCJDb25maWd1cmFibGVTaG9ydGN1dEhpbnQiLCJTZWxlY3QiLCJCeWxpbmUiLCJEaWFsb2ciLCJLZXlib2FyZFNob3J0Y3V0SGludCIsIlRleHRJbnB1dCIsIkV4cG9ydERpYWxvZ1Byb3BzIiwiY29udGVudCIsImRlZmF1bHRGaWxlbmFtZSIsIm9uRG9uZSIsInJlc3VsdCIsInN1Y2Nlc3MiLCJtZXNzYWdlIiwiRXhwb3J0T3B0aW9uIiwiRXhwb3J0RGlhbG9nIiwiUmVhY3ROb2RlIiwic2V0U2VsZWN0ZWRPcHRpb24iLCJmaWxlbmFtZSIsInNldEZpbGVuYW1lIiwiY3Vyc29yT2Zmc2V0Iiwic2V0Q3Vyc29yT2Zmc2V0IiwibGVuZ3RoIiwic2hvd0ZpbGVuYW1lSW5wdXQiLCJzZXRTaG93RmlsZW5hbWVJbnB1dCIsImNvbHVtbnMiLCJoYW5kbGVHb0JhY2siLCJoYW5kbGVTZWxlY3RPcHRpb24iLCJ2YWx1ZSIsIlByb21pc2UiLCJyYXciLCJwcm9jZXNzIiwic3Rkb3V0Iiwid3JpdGUiLCJoYW5kbGVGaWxlbmFtZVN1Ym1pdCIsImZpbmFsRmlsZW5hbWUiLCJlbmRzV2l0aCIsInJlcGxhY2UiLCJmaWxlcGF0aCIsImVuY29kaW5nIiwiZmx1c2giLCJlcnJvciIsIkVycm9yIiwiaGFuZGxlQ2FuY2VsIiwib3B0aW9ucyIsImxhYmVsIiwiZGVzY3JpcHRpb24iLCJyZW5kZXJJbnB1dEd1aWRlIiwiZXhpdFN0YXRlIiwicGVuZGluZyIsImtleU5hbWUiLCJjb250ZXh0IiwiaXNBY3RpdmUiXSwic291cmNlcyI6WyJFeHBvcnREaWFsb2cudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGpvaW4gfSBmcm9tICdwYXRoJ1xuaW1wb3J0IFJlYWN0LCB7IHVzZUNhbGxiYWNrLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBFeGl0U3RhdGUgfSBmcm9tICcuLi9ob29rcy91c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MuanMnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyBzZXRDbGlwYm9hcmQgfSBmcm9tICcuLi9pbmsvdGVybWlvL29zYy5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgZ2V0Q3dkIH0gZnJvbSAnLi4vdXRpbHMvY3dkLmpzJ1xuaW1wb3J0IHsgd3JpdGVGaWxlU3luY19ERVBSRUNBVEVEIH0gZnJvbSAnLi4vdXRpbHMvc2xvd09wZXJhdGlvbnMuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuL0NvbmZpZ3VyYWJsZVNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgVGV4dElucHV0IGZyb20gJy4vVGV4dElucHV0LmpzJ1xuXG50eXBlIEV4cG9ydERpYWxvZ1Byb3BzID0ge1xuICBjb250ZW50OiBzdHJpbmdcbiAgZGVmYXVsdEZpbGVuYW1lOiBzdHJpbmdcbiAgb25Eb25lOiAocmVzdWx0OiB7IHN1Y2Nlc3M6IGJvb2xlYW47IG1lc3NhZ2U6IHN0cmluZyB9KSA9PiB2b2lkXG59XG5cbnR5cGUgRXhwb3J0T3B0aW9uID0gJ2NsaXBib2FyZCcgfCAnZmlsZSdcblxuZXhwb3J0IGZ1bmN0aW9uIEV4cG9ydERpYWxvZyh7XG4gIGNvbnRlbnQsXG4gIGRlZmF1bHRGaWxlbmFtZSxcbiAgb25Eb25lLFxufTogRXhwb3J0RGlhbG9nUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbLCBzZXRTZWxlY3RlZE9wdGlvbl0gPSB1c2VTdGF0ZTxFeHBvcnRPcHRpb24gfCBudWxsPihudWxsKVxuICBjb25zdCBbZmlsZW5hbWUsIHNldEZpbGVuYW1lXSA9IHVzZVN0YXRlPHN0cmluZz4oZGVmYXVsdEZpbGVuYW1lKVxuICBjb25zdCBbY3Vyc29yT2Zmc2V0LCBzZXRDdXJzb3JPZmZzZXRdID0gdXNlU3RhdGU8bnVtYmVyPihcbiAgICBkZWZhdWx0RmlsZW5hbWUubGVuZ3RoLFxuICApXG4gIGNvbnN0IFtzaG93RmlsZW5hbWVJbnB1dCwgc2V0U2hvd0ZpbGVuYW1lSW5wdXRdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IHsgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcblxuICAvLyBIYW5kbGUgZ29pbmcgYmFjayBmcm9tIGZpbGVuYW1lIGlucHV0IHRvIG9wdGlvbiBzZWxlY3Rpb25cbiAgY29uc3QgaGFuZGxlR29CYWNrID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHNldFNob3dGaWxlbmFtZUlucHV0KGZhbHNlKVxuICAgIHNldFNlbGVjdGVkT3B0aW9uKG51bGwpXG4gIH0sIFtdKVxuXG4gIGNvbnN0IGhhbmRsZVNlbGVjdE9wdGlvbiA9IGFzeW5jICh2YWx1ZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgaWYgKHZhbHVlID09PSAnY2xpcGJvYXJkJykge1xuICAgICAgLy8gQ29weSB0byBjbGlwYm9hcmQgaW1tZWRpYXRlbHlcbiAgICAgIGNvbnN0IHJhdyA9IGF3YWl0IHNldENsaXBib2FyZChjb250ZW50KVxuICAgICAgaWYgKHJhdykgcHJvY2Vzcy5zdGRvdXQud3JpdGUocmF3KVxuICAgICAgb25Eb25lKHsgc3VjY2VzczogdHJ1ZSwgbWVzc2FnZTogJ0NvbnZlcnNhdGlvbiBjb3BpZWQgdG8gY2xpcGJvYXJkJyB9KVxuICAgIH0gZWxzZSBpZiAodmFsdWUgPT09ICdmaWxlJykge1xuICAgICAgc2V0U2VsZWN0ZWRPcHRpb24oJ2ZpbGUnKVxuICAgICAgc2V0U2hvd0ZpbGVuYW1lSW5wdXQodHJ1ZSlcbiAgICB9XG4gIH1cblxuICBjb25zdCBoYW5kbGVGaWxlbmFtZVN1Ym1pdCA9ICgpID0+IHtcbiAgICBjb25zdCBmaW5hbEZpbGVuYW1lID0gZmlsZW5hbWUuZW5kc1dpdGgoJy50eHQnKVxuICAgICAgPyBmaWxlbmFtZVxuICAgICAgOiBmaWxlbmFtZS5yZXBsYWNlKC9cXC5bXi5dKyQvLCAnJykgKyAnLnR4dCdcbiAgICBjb25zdCBmaWxlcGF0aCA9IGpvaW4oZ2V0Q3dkKCksIGZpbmFsRmlsZW5hbWUpXG5cbiAgICB0cnkge1xuICAgICAgd3JpdGVGaWxlU3luY19ERVBSRUNBVEVEKGZpbGVwYXRoLCBjb250ZW50LCB7XG4gICAgICAgIGVuY29kaW5nOiAndXRmLTgnLFxuICAgICAgICBmbHVzaDogdHJ1ZSxcbiAgICAgIH0pXG4gICAgICBvbkRvbmUoe1xuICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICBtZXNzYWdlOiBgQ29udmVyc2F0aW9uIGV4cG9ydGVkIHRvOiAke2ZpbGVwYXRofWAsXG4gICAgICB9KVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBvbkRvbmUoe1xuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgbWVzc2FnZTogYEZhaWxlZCB0byBleHBvcnQgY29udmVyc2F0aW9uOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogJ1Vua25vd24gZXJyb3InfWAsXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIC8vIERpYWxvZyBjYWxscyBvbkNhbmNlbCB3aGVuIEVzY2FwZSBpcyBwcmVzc2VkLiBJZiB3ZSBhcmUgaW4gdGhlIGZpbGVuYW1lXG4gIC8vIGlucHV0IHN1Yi1zY3JlZW4sIGdvIGJhY2sgdG8gdGhlIG9wdGlvbiBsaXN0IGluc3RlYWQgb2YgY2xvc2luZyBlbnRpcmVseS5cbiAgY29uc3QgaGFuZGxlQ2FuY2VsID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChzaG93RmlsZW5hbWVJbnB1dCkge1xuICAgICAgaGFuZGxlR29CYWNrKClcbiAgICB9IGVsc2Uge1xuICAgICAgb25Eb25lKHsgc3VjY2VzczogZmFsc2UsIG1lc3NhZ2U6ICdFeHBvcnQgY2FuY2VsbGVkJyB9KVxuICAgIH1cbiAgfSwgW3Nob3dGaWxlbmFtZUlucHV0LCBoYW5kbGVHb0JhY2ssIG9uRG9uZV0pXG5cbiAgY29uc3Qgb3B0aW9ucyA9IFtcbiAgICB7XG4gICAgICBsYWJlbDogJ0NvcHkgdG8gY2xpcGJvYXJkJyxcbiAgICAgIHZhbHVlOiAnY2xpcGJvYXJkJyxcbiAgICAgIGRlc2NyaXB0aW9uOiAnQ29weSB0aGUgY29udmVyc2F0aW9uIHRvIHlvdXIgc3lzdGVtIGNsaXBib2FyZCcsXG4gICAgfSxcbiAgICB7XG4gICAgICBsYWJlbDogJ1NhdmUgdG8gZmlsZScsXG4gICAgICB2YWx1ZTogJ2ZpbGUnLFxuICAgICAgZGVzY3JpcHRpb246ICdTYXZlIHRoZSBjb252ZXJzYXRpb24gdG8gYSBmaWxlIGluIHRoZSBjdXJyZW50IGRpcmVjdG9yeScsXG4gICAgfSxcbiAgXVxuXG4gIC8vIEN1c3RvbSBpbnB1dCBndWlkZSB0aGF0IGNoYW5nZXMgYmFzZWQgb24gZGlhbG9nIHN0YXRlXG4gIGZ1bmN0aW9uIHJlbmRlcklucHV0R3VpZGUoZXhpdFN0YXRlOiBFeGl0U3RhdGUpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAgIGlmIChzaG93RmlsZW5hbWVJbnB1dCkge1xuICAgICAgcmV0dXJuIChcbiAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cInNhdmVcIiAvPlxuICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgICAgICBmYWxsYmFjaz1cIkVzY1wiXG4gICAgICAgICAgICBkZXNjcmlwdGlvbj1cImdvIGJhY2tcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQnlsaW5lPlxuICAgICAgKVxuICAgIH1cblxuICAgIGlmIChleGl0U3RhdGUucGVuZGluZykge1xuICAgICAgcmV0dXJuIDxUZXh0PlByZXNzIHtleGl0U3RhdGUua2V5TmFtZX0gYWdhaW4gdG8gZXhpdDwvVGV4dD5cbiAgICB9XG5cbiAgICByZXR1cm4gKFxuICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICAvLyBVc2UgU2V0dGluZ3MgY29udGV4dCBzbyAnbicga2V5IGRvZXNuJ3QgY2FuY2VsIChhbGxvd3MgdHlwaW5nICduJyBpbiBmaWxlbmFtZSBpbnB1dClcbiAgdXNlS2V5YmluZGluZygnY29uZmlybTpubycsIGhhbmRsZUNhbmNlbCwge1xuICAgIGNvbnRleHQ6ICdTZXR0aW5ncycsXG4gICAgaXNBY3RpdmU6IHNob3dGaWxlbmFtZUlucHV0LFxuICB9KVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJFeHBvcnQgQ29udmVyc2F0aW9uXCJcbiAgICAgIHN1YnRpdGxlPVwiU2VsZWN0IGV4cG9ydCBtZXRob2Q6XCJcbiAgICAgIGNvbG9yPVwicGVybWlzc2lvblwiXG4gICAgICBvbkNhbmNlbD17aGFuZGxlQ2FuY2VsfVxuICAgICAgaW5wdXRHdWlkZT17cmVuZGVySW5wdXRHdWlkZX1cbiAgICAgIGlzQ2FuY2VsQWN0aXZlPXshc2hvd0ZpbGVuYW1lSW5wdXR9XG4gICAgPlxuICAgICAgeyFzaG93RmlsZW5hbWVJbnB1dCA/IChcbiAgICAgICAgPFNlbGVjdFxuICAgICAgICAgIG9wdGlvbnM9e29wdGlvbnN9XG4gICAgICAgICAgb25DaGFuZ2U9e2hhbmRsZVNlbGVjdE9wdGlvbn1cbiAgICAgICAgICBvbkNhbmNlbD17aGFuZGxlQ2FuY2VsfVxuICAgICAgICAvPlxuICAgICAgKSA6IChcbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPFRleHQ+RW50ZXIgZmlsZW5hbWU6PC9UZXh0PlxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiIGdhcD17MX0gbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICAgIDxUZXh0PiZndDs8L1RleHQ+XG4gICAgICAgICAgICA8VGV4dElucHV0XG4gICAgICAgICAgICAgIHZhbHVlPXtmaWxlbmFtZX1cbiAgICAgICAgICAgICAgb25DaGFuZ2U9e3NldEZpbGVuYW1lfVxuICAgICAgICAgICAgICBvblN1Ym1pdD17aGFuZGxlRmlsZW5hbWVTdWJtaXR9XG4gICAgICAgICAgICAgIGZvY3VzPXt0cnVlfVxuICAgICAgICAgICAgICBzaG93Q3Vyc29yPXt0cnVlfVxuICAgICAgICAgICAgICBjb2x1bW5zPXtjb2x1bW5zfVxuICAgICAgICAgICAgICBjdXJzb3JPZmZzZXQ9e2N1cnNvck9mZnNldH1cbiAgICAgICAgICAgICAgb25DaGFuZ2VDdXJzb3JPZmZzZXQ9e3NldEN1cnNvck9mZnNldH1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxJQUFJLFFBQVEsTUFBTTtBQUMzQixPQUFPQyxLQUFLLElBQUlDLFdBQVcsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDcEQsY0FBY0MsU0FBUyxRQUFRLDRDQUE0QztBQUMzRSxTQUFTQyxlQUFlLFFBQVEsNkJBQTZCO0FBQzdELFNBQVNDLFlBQVksUUFBUSxzQkFBc0I7QUFDbkQsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUNyQyxTQUFTQyxhQUFhLFFBQVEsaUNBQWlDO0FBQy9ELFNBQVNDLE1BQU0sUUFBUSxpQkFBaUI7QUFDeEMsU0FBU0Msd0JBQXdCLFFBQVEsNEJBQTRCO0FBQ3JFLFNBQVNDLHdCQUF3QixRQUFRLCtCQUErQjtBQUN4RSxTQUFTQyxNQUFNLFFBQVEsMEJBQTBCO0FBQ2pELFNBQVNDLE1BQU0sUUFBUSwyQkFBMkI7QUFDbEQsU0FBU0MsTUFBTSxRQUFRLDJCQUEyQjtBQUNsRCxTQUFTQyxvQkFBb0IsUUFBUSx5Q0FBeUM7QUFDOUUsT0FBT0MsU0FBUyxNQUFNLGdCQUFnQjtBQUV0QyxLQUFLQyxpQkFBaUIsR0FBRztFQUN2QkMsT0FBTyxFQUFFLE1BQU07RUFDZkMsZUFBZSxFQUFFLE1BQU07RUFDdkJDLE1BQU0sRUFBRSxDQUFDQyxNQUFNLEVBQUU7SUFBRUMsT0FBTyxFQUFFLE9BQU87SUFBRUMsT0FBTyxFQUFFLE1BQU07RUFBQyxDQUFDLEVBQUUsR0FBRyxJQUFJO0FBQ2pFLENBQUM7QUFFRCxLQUFLQyxZQUFZLEdBQUcsV0FBVyxHQUFHLE1BQU07QUFFeEMsT0FBTyxTQUFTQyxZQUFZQSxDQUFDO0VBQzNCUCxPQUFPO0VBQ1BDLGVBQWU7RUFDZkM7QUFDaUIsQ0FBbEIsRUFBRUgsaUJBQWlCLENBQUMsRUFBRWpCLEtBQUssQ0FBQzBCLFNBQVMsQ0FBQztFQUNyQyxNQUFNLEdB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 \ No newline at end of file diff --git a/components/FallbackToolUseErrorMessage.tsx b/components/FallbackToolUseErrorMessage.tsx new file mode 100644 index 0000000..12d3b39 --- /dev/null +++ b/components/FallbackToolUseErrorMessage.tsx @@ -0,0 +1,116 @@ +import { c as _c } from "react/compiler-runtime"; +import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/messages/messages.mjs'; +import * as React from 'react'; +import { stripUnderlineAnsi } from 'src/components/shell/OutputLine.js'; +import { extractTag } from 'src/utils/messages.js'; +import { removeSandboxViolationTags } from 'src/utils/sandbox/sandbox-ui-utils.js'; +import { Box, Text } from '../ink.js'; +import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js'; +import { countCharInString } from '../utils/stringUtils.js'; +import { MessageResponse } from './MessageResponse.js'; +const MAX_RENDERED_LINES = 10; +type Props = { + result: ToolResultBlockParam['content']; + verbose: boolean; +}; +export function FallbackToolUseErrorMessage(t0) { + const $ = _c(25); + const { + result, + verbose + } = t0; + const transcriptShortcut = useShortcutDisplay("app:toggleTranscript", "Global", "ctrl+o"); + let T0; + let T1; + let T2; + let plusLines; + let t1; + let t2; + let t3; + if ($[0] !== result || $[1] !== verbose) { + let error; + if (typeof result !== "string") { + error = "Tool execution failed"; + } else { + const extractedError = extractTag(result, "tool_use_error") ?? result; + const withoutSandboxViolations = removeSandboxViolationTags(extractedError); + const withoutErrorTags = withoutSandboxViolations.replace(/<\/?error>/g, ""); + const trimmed = withoutErrorTags.trim(); + if (!verbose && trimmed.includes("InputValidationError: ")) { + error = "Invalid tool parameters"; + } else { + if (trimmed.startsWith("Error: ") || trimmed.startsWith("Cancelled: ")) { + error = trimmed; + } else { + error = `Error: ${trimmed}`; + } + } + } + plusLines = countCharInString(error, "\n") + 1 - MAX_RENDERED_LINES; + T2 = MessageResponse; + T1 = Box; + t3 = "column"; + T0 = Text; + t1 = "error"; + t2 = stripUnderlineAnsi(verbose ? error : error.split("\n").slice(0, MAX_RENDERED_LINES).join("\n")); + $[0] = result; + $[1] = verbose; + $[2] = T0; + $[3] = T1; + $[4] = T2; + $[5] = plusLines; + $[6] = t1; + $[7] = t2; + $[8] = t3; + } else { + T0 = $[2]; + T1 = $[3]; + T2 = $[4]; + plusLines = $[5]; + t1 = $[6]; + t2 = $[7]; + t3 = $[8]; + } + let t4; + if ($[9] !== T0 || $[10] !== t1 || $[11] !== t2) { + t4 = {t2}; + $[9] = T0; + $[10] = t1; + $[11] = t2; + $[12] = t4; + } else { + t4 = $[12]; + } + let t5; + if ($[13] !== plusLines || $[14] !== transcriptShortcut || $[15] !== verbose) { + t5 = !verbose && plusLines > 0 && … +{plusLines} {plusLines === 1 ? "line" : "lines"} ({transcriptShortcut} to see all); + $[13] = plusLines; + $[14] = transcriptShortcut; + $[15] = verbose; + $[16] = t5; + } else { + t5 = $[16]; + } + let t6; + if ($[17] !== T1 || $[18] !== t3 || $[19] !== t4 || $[20] !== t5) { + t6 = {t4}{t5}; + $[17] = T1; + $[18] = t3; + $[19] = t4; + $[20] = t5; + $[21] = t6; + } else { + t6 = $[21]; + } + let t7; + if ($[22] !== T2 || $[23] !== t6) { + t7 = {t6}; + $[22] = T2; + $[23] = t6; + $[24] = t7; + } else { + t7 = $[24]; + } + return t7; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJUb29sUmVzdWx0QmxvY2tQYXJhbSIsIlJlYWN0Iiwic3RyaXBVbmRlcmxpbmVBbnNpIiwiZXh0cmFjdFRhZyIsInJlbW92ZVNhbmRib3hWaW9sYXRpb25UYWdzIiwiQm94IiwiVGV4dCIsInVzZVNob3J0Y3V0RGlzcGxheSIsImNvdW50Q2hhckluU3RyaW5nIiwiTWVzc2FnZVJlc3BvbnNlIiwiTUFYX1JFTkRFUkVEX0xJTkVTIiwiUHJvcHMiLCJyZXN1bHQiLCJ2ZXJib3NlIiwiRmFsbGJhY2tUb29sVXNlRXJyb3JNZXNzYWdlIiwidDAiLCIkIiwiX2MiLCJ0cmFuc2NyaXB0U2hvcnRjdXQiLCJUMCIsIlQxIiwiVDIiLCJwbHVzTGluZXMiLCJ0MSIsInQyIiwidDMiLCJlcnJvciIsImV4dHJhY3RlZEVycm9yIiwid2l0aG91dFNhbmRib3hWaW9sYXRpb25zIiwid2l0aG91dEVycm9yVGFncyIsInJlcGxhY2UiLCJ0cmltbWVkIiwidHJpbSIsImluY2x1ZGVzIiwic3RhcnRzV2l0aCIsInNwbGl0Iiwic2xpY2UiLCJqb2luIiwidDQiLCJ0NSIsInQ2IiwidDciXSwic291cmNlcyI6WyJGYWxsYmFja1Rvb2xVc2VFcnJvck1lc3NhZ2UudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgVG9vbFJlc3VsdEJsb2NrUGFyYW0gfSBmcm9tICdAYW50aHJvcGljLWFpL3Nkay9yZXNvdXJjZXMvbWVzc2FnZXMvbWVzc2FnZXMubWpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBzdHJpcFVuZGVybGluZUFuc2kgfSBmcm9tICdzcmMvY29tcG9uZW50cy9zaGVsbC9PdXRwdXRMaW5lLmpzJ1xuaW1wb3J0IHsgZXh0cmFjdFRhZyB9IGZyb20gJ3NyYy91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB7IHJlbW92ZVNhbmRib3hWaW9sYXRpb25UYWdzIH0gZnJvbSAnc3JjL3V0aWxzL3NhbmRib3gvc2FuZGJveC11aS11dGlscy5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IHVzZVNob3J0Y3V0RGlzcGxheSB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZVNob3J0Y3V0RGlzcGxheS5qcydcbmltcG9ydCB7IGNvdW50Q2hhckluU3RyaW5nIH0gZnJvbSAnLi4vdXRpbHMvc3RyaW5nVXRpbHMuanMnXG5pbXBvcnQgeyBNZXNzYWdlUmVzcG9uc2UgfSBmcm9tICcuL01lc3NhZ2VSZXNwb25zZS5qcydcblxuY29uc3QgTUFYX1JFTkRFUkVEX0xJTkVTID0gMTBcblxudHlwZSBQcm9wcyA9IHtcbiAgcmVzdWx0OiBUb29sUmVzdWx0QmxvY2tQYXJhbVsnY29udGVudCddXG4gIHZlcmJvc2U6IGJvb2xlYW5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIEZhbGxiYWNrVG9vbFVzZUVycm9yTWVzc2FnZSh7XG4gIHJlc3VsdCxcbiAgdmVyYm9zZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgdHJhbnNjcmlwdFNob3J0Y3V0ID0gdXNlU2hvcnRjdXREaXNwbGF5KFxuICAgICdhcHA6dG9nZ2xlVHJhbnNjcmlwdCcsXG4gICAgJ0dsb2JhbCcsXG4gICAgJ2N0cmwrbycsXG4gIClcbiAgbGV0IGVycm9yOiBzdHJpbmdcblxuICBpZiAodHlwZW9mIHJlc3VsdCAhPT0gJ3N0cmluZycpIHtcbiAgICBlcnJvciA9ICdUb29sIGV4ZWN1dGlvbiBmYWlsZWQnXG4gIH0gZWxzZSB7XG4gICAgY29uc3QgZXh0cmFjdGVkRXJyb3IgPSBleHRyYWN0VGFnKHJlc3VsdCwgJ3Rvb2xfdXNlX2Vycm9yJykgPz8gcmVzdWx0XG4gICAgLy8gUmVtb3ZlIHNhbmRib3hfdmlvbGF0aW9ucyB0YWdzIGZyb20gZXJyb3IgZGlzcGxheSAoQ2xhdWRlIHN0aWxsIHNlZXMgdGhlbSBpbiB0aGUgdG9vbCByZXN1bHQpXG4gICAgY29uc3Qgd2l0aG91dFNhbmRib3hWaW9sYXRpb25zID0gcmVtb3ZlU2FuZGJveFZpb2xhdGlvblRhZ3MoZXh0cmFjdGVkRXJyb3IpXG4gICAgLy8gU3RyaXAgPGVycm9yPiB0YWdzIGJ1dCBrZWVwIHRoZWlyIGNvbnRlbnQgKHRhZ3MgYXJlIGZvciB0aGUgbW9kZWwsIG5vdCB0aGUgVUkpXG4gICAgY29uc3Qgd2l0aG91dEVycm9yVGFncyA9IHdpdGhvdXRTYW5kYm94VmlvbGF0aW9ucy5yZXBsYWNlKC88XFwvP2Vycm9yPi9nLCAnJylcbiAgICBjb25zdCB0cmltbWVkID0gd2l0aG91dEVycm9yVGFncy50cmltKClcbiAgICBpZiAoIXZlcmJvc2UgJiYgdHJpbW1lZC5pbmNsdWRlcygnSW5wdXRWYWxpZGF0aW9uRXJyb3I6ICcpKSB7XG4gICAgICBlcnJvciA9ICdJbnZhbGlkIHRvb2wgcGFyYW1ldGVycydcbiAgICB9IGVsc2UgaWYgKFxuICAgICAgdHJpbW1lZC5zdGFydHNXaXRoKCdFcnJvcjogJykgfHxcbiAgICAgIHRyaW1tZWQuc3RhcnRzV2l0aCgnQ2FuY2VsbGVkOiAnKVxuICAgICkge1xuICAgICAgZXJyb3IgPSB0cmltbWVkXG4gICAgfSBlbHNlIHtcbiAgICAgIGVycm9yID0gYEVycm9yOiAke3RyaW1tZWR9YFxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHBsdXNMaW5lcyA9IGNvdW50Q2hhckluU3RyaW5nKGVycm9yLCAnXFxuJykgKyAxIC0gTUFYX1JFTkRFUkVEX0xJTkVTXG5cbiAgcmV0dXJuIChcbiAgICA8TWVzc2FnZVJlc3BvbnNlPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5cbiAgICAgICAgICB7c3RyaXBVbmRlcmxpbmVBbnNpKFxuICAgICAgICAgICAgdmVyYm9zZVxuICAgICAgICAgICAgICA/IGVycm9yXG4gICAgICAgICAgICAgIDogZXJyb3Iuc3BsaXQoJ1xcbicpLnNsaWNlKDAsIE1BWF9SRU5ERVJFRF9MSU5FUykuam9pbignXFxuJyksXG4gICAgICAgICAgKX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICB7IXZlcmJvc2UgJiYgcGx1c0xpbmVzID4gMCAmJiAoXG4gICAgICAgICAgLy8gVGhlIGNhcmVmdWwgPFRleHQ+IGxheW91dCBpcyBhIHdvcmthcm91bmQgZm9yIHRoZSBkaW0tYm9sZFxuICAgICAgICAgIC8vIHJlbmRlcmluZyBidWdcbiAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIOKApiAre3BsdXNMaW5lc30ge3BsdXNMaW5lcyA9PT0gMSA/ICdsaW5lJyA6ICdsaW5lcyd9IChcbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGJvbGQ+XG4gICAgICAgICAgICAgIHt0cmFuc2NyaXB0U2hvcnRjdXR9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8VGV4dD4gPC9UZXh0PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+dG8gc2VlIGFsbCk8L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICA8L0JveD5cbiAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsY0FBY0Esb0JBQW9CLFFBQVEsbURBQW1EO0FBQzdGLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0Msa0JBQWtCLFFBQVEsb0NBQW9DO0FBQ3ZFLFNBQVNDLFVBQVUsUUFBUSx1QkFBdUI7QUFDbEQsU0FBU0MsMEJBQTBCLFFBQVEsdUNBQXVDO0FBQ2xGLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDckMsU0FBU0Msa0JBQWtCLFFBQVEsc0NBQXNDO0FBQ3pFLFNBQVNDLGlCQUFpQixRQUFRLHlCQUF5QjtBQUMzRCxTQUFTQyxlQUFlLFFBQVEsc0JBQXNCO0FBRXRELE1BQU1DLGtCQUFrQixHQUFHLEVBQUU7QUFFN0IsS0FBS0MsS0FBSyxHQUFHO0VB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 \ No newline at end of file diff --git a/components/FallbackToolUseRejectedMessage.tsx b/components/FallbackToolUseRejectedMessage.tsx new file mode 100644 index 0000000..3e0d2ca --- /dev/null +++ b/components/FallbackToolUseRejectedMessage.tsx @@ -0,0 +1,16 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { InterruptedByUser } from './InterruptedByUser.js'; +import { MessageResponse } from './MessageResponse.js'; +export function FallbackToolUseRejectedMessage() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = ; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkludGVycnVwdGVkQnlVc2VyIiwiTWVzc2FnZVJlc3BvbnNlIiwiRmFsbGJhY2tUb29sVXNlUmVqZWN0ZWRNZXNzYWdlIiwiJCIsIl9jIiwidDAiLCJTeW1ib2wiLCJmb3IiXSwic291cmNlcyI6WyJGYWxsYmFja1Rvb2xVc2VSZWplY3RlZE1lc3NhZ2UudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgSW50ZXJydXB0ZWRCeVVzZXIgfSBmcm9tICcuL0ludGVycnVwdGVkQnlVc2VyLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZVJlc3BvbnNlIH0gZnJvbSAnLi9NZXNzYWdlUmVzcG9uc2UuanMnXG5cbmV4cG9ydCBmdW5jdGlvbiBGYWxsYmFja1Rvb2xVc2VSZWplY3RlZE1lc3NhZ2UoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8TWVzc2FnZVJlc3BvbnNlIGhlaWdodD17MX0+XG4gICAgICA8SW50ZXJydXB0ZWRCeVVzZXIgLz5cbiAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxpQkFBaUIsUUFBUSx3QkFBd0I7QUFDMUQsU0FBU0MsZUFBZSxRQUFRLHNCQUFzQjtBQUV0RCxPQUFPLFNBQUFDLCtCQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQUcsTUFBQSxDQUFBQyxHQUFBO0lBRUhGLEVBQUEsSUFBQyxlQUFlLENBQVMsTUFBQyxDQUFELEdBQUMsQ0FDeEIsQ0FBQyxpQkFBaUIsR0FDcEIsRUFGQyxlQUFlLENBRUU7SUFBQUYsQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFBQSxPQUZsQkUsRUFFa0I7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/FastIcon.tsx b/components/FastIcon.tsx new file mode 100644 index 0000000..d229a86 --- /dev/null +++ b/components/FastIcon.tsx @@ -0,0 +1,46 @@ +import { c as _c } from "react/compiler-runtime"; +import chalk from 'chalk'; +import * as React from 'react'; +import { LIGHTNING_BOLT } from '../constants/figures.js'; +import { Text } from '../ink.js'; +import { getGlobalConfig } from '../utils/config.js'; +import { resolveThemeSetting } from '../utils/systemTheme.js'; +import { color } from './design-system/color.js'; +type Props = { + cooldown?: boolean; +}; +export function FastIcon(t0) { + const $ = _c(2); + const { + cooldown + } = t0; + if (cooldown) { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {LIGHTNING_BOLT}; + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; + } + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {LIGHTNING_BOLT}; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} +export function getFastIconString(applyColor = true, cooldown = false): string { + if (!applyColor) { + return LIGHTNING_BOLT; + } + const themeName = resolveThemeSetting(getGlobalConfig().theme); + if (cooldown) { + return chalk.dim(color('promptBorder', themeName)(LIGHTNING_BOLT)); + } + return color('fastMode', themeName)(LIGHTNING_BOLT); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjaGFsayIsIlJlYWN0IiwiTElHSFROSU5HX0JPTFQiLCJUZXh0IiwiZ2V0R2xvYmFsQ29uZmlnIiwicmVzb2x2ZVRoZW1lU2V0dGluZyIsImNvbG9yIiwiUHJvcHMiLCJjb29sZG93biIsIkZhc3RJY29uIiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsImdldEZhc3RJY29uU3RyaW5nIiwiYXBwbHlDb2xvciIsInRoZW1lTmFtZSIsInRoZW1lIiwiZGltIl0sInNvdXJjZXMiOlsiRmFzdEljb24udHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjaGFsayBmcm9tICdjaGFsaydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgTElHSFROSU5HX0JPTFQgfSBmcm9tICcuLi9jb25zdGFudHMvZmlndXJlcy5qcydcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBnZXRHbG9iYWxDb25maWcgfSBmcm9tICcuLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgeyByZXNvbHZlVGhlbWVTZXR0aW5nIH0gZnJvbSAnLi4vdXRpbHMvc3lzdGVtVGhlbWUuanMnXG5pbXBvcnQgeyBjb2xvciB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9jb2xvci5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgY29vbGRvd24/OiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBGYXN0SWNvbih7IGNvb2xkb3duIH06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKGNvb2xkb3duKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxUZXh0IGNvbG9yPVwicHJvbXB0Qm9yZGVyXCIgZGltQ29sb3I+XG4gICAgICAgIHtMSUdIVE5JTkdfQk9MVH1cbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH1cbiAgcmV0dXJuIDxUZXh0IGNvbG9yPVwiZmFzdE1vZGVcIj57TElHSFROSU5HX0JPTFR9PC9UZXh0PlxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RmFzdEljb25TdHJpbmcoYXBwbHlDb2xvciA9IHRydWUsIGNvb2xkb3duID0gZmFsc2UpOiBzdHJpbmcge1xuICBpZiAoIWFwcGx5Q29sb3IpIHtcbiAgICByZXR1cm4gTElHSFROSU5HX0JPTFRcbiAgfVxuICBjb25zdCB0aGVtZU5hbWUgPSByZXNvbHZlVGhlbWVTZXR0aW5nKGdldEdsb2JhbENvbmZpZygpLnRoZW1lKVxuICBpZiAoY29vbGRvd24pIHtcbiAgICByZXR1cm4gY2hhbGsuZGltKGNvbG9yKCdwcm9tcHRCb3JkZXInLCB0aGVtZU5hbWUpKExJR0hUTklOR19CT0xUKSlcbiAgfVxuICByZXR1cm4gY29sb3IoJ2Zhc3RNb2RlJywgdGhlbWVOYW1lKShMSUdIVE5JTkdfQk9MVClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsY0FBYyxRQUFRLHlCQUF5QjtBQUN4RCxTQUFTQyxJQUFJLFFBQVEsV0FBVztBQUNoQyxTQUFTQyxlQUFlLFFBQVEsb0JBQW9CO0FBQ3BELFNBQVNDLG1CQUFtQixRQUFRLHlCQUF5QjtBQUM3RCxTQUFTQyxLQUFLLFFBQVEsMEJBQTBCO0FBRWhELEtBQUtDLEtBQUssR0FBRztFQUNYQyxRQUFRLENBQUMsRUFBRSxPQUFPO0FBQ3BCLENBQUM7QUFFRCxPQUFPLFNBQUFDLFNBQUFDLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBa0I7SUFBQUo7RUFBQSxJQUFBRSxFQUFtQjtFQUMxQyxJQUFJRixRQUFRO0lBQUEsSUFBQUssRUFBQTtJQUFBLElBQUFGLENBQUEsUUFBQUcsTUFBQSxDQUFBQyxHQUFBO01BRVJGLEVBQUEsSUFBQyxJQUFJLENBQU8sS0FBYyxDQUFkLGNBQWMsQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQ2hDWCxlQUFhLENBQ2hCLEVBRkMsSUFBSSxDQUVFO01BQUFTLENBQUEsTUFBQUUsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQUYsQ0FBQTtJQUFBO0lBQUEsT0FGUEUsRUFFTztFQUFBO0VBRVYsSUFBQUEsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQUcsTUFBQSxDQUFBQyxHQUFBO0lBQ01GLEVBQUEsSUFBQyxJQUFJLENBQU8sS0FBVSxDQUFWLFVBQVUsQ0FBRVgsZUFBYSxDQUFFLEVBQXRDLElBQUksQ0FBeUM7SUFBQVMsQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFBQSxPQUE5Q0UsRUFBOEM7QUFBQTtBQUd2RCxPQUFPLFNBQVNHLGlCQUFpQkEsQ0FBQ0MsVUFBVSxHQUFHLElBQUksRUFBRVQsUUFBUSxHQUFHLEtBQUssQ0FBQyxFQUFFLE1BQU0sQ0FBQztFQUM3RSxJQUFJLENBQUNTLFVBQVUsRUFBRTtJQUNmLE9BQU9mLGNBQWM7RUFDdkI7RUFDQSxNQUFNZ0IsU0FBUyxHQUFHYixtQkFBbUIsQ0FBQ0QsZUFBZSxDQUFDLENBQUMsQ0FBQ2UsS0FBSyxDQUFDO0VBQzlELElBQUlYLFFBQVEsRUFBRTtJQUNaLE9BQU9SLEtBQUssQ0FBQ29CLEdBQUcsQ0FBQ2QsS0FBSyxDQUFDLGNBQWMsRUFBRVksU0FBUyxDQUFDLENBQUNoQixjQUFjLENBQUMsQ0FBQztFQUNwRTtFQUNBLE9BQU9JLEtBQUssQ0FBQyxVQUFVLEVBQUVZLFNBQVMsQ0FBQyxDQUFDaEIsY0FBYyxDQUFDO0FBQ3JEIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/Feedback.tsx b/components/Feedback.tsx new file mode 100644 index 0000000..8f2fbb1 --- /dev/null +++ b/components/Feedback.tsx @@ -0,0 +1,592 @@ +import axios from 'axios'; +import { readFile, stat } from 'fs/promises'; +import * as React from 'react'; +import { useCallback, useEffect, useState } from 'react'; +import { getLastAPIRequest } from 'src/bootstrap/state.js'; +import { logEventTo1P } from 'src/services/analytics/firstPartyEventLogger.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { getLastAssistantMessage, normalizeMessagesForAPI } from 'src/utils/messages.js'; +import type { CommandResultDisplay } from '../commands.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { Box, Text, useInput } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { queryHaiku } from '../services/api/claude.js'; +import { startsWithApiErrorPrefix } from '../services/api/errors.js'; +import type { Message } from '../types/message.js'; +import { checkAndRefreshOAuthTokenIfNeeded } from '../utils/auth.js'; +import { openBrowser } from '../utils/browser.js'; +import { logForDebugging } from '../utils/debug.js'; +import { env } from '../utils/env.js'; +import { type GitRepoState, getGitState, getIsGit } from '../utils/git.js'; +import { getAuthHeaders, getUserAgent } from '../utils/http.js'; +import { getInMemoryErrors, logError } from '../utils/log.js'; +import { isEssentialTrafficOnly } from '../utils/privacyLevel.js'; +import { extractTeammateTranscriptsFromTasks, getTranscriptPath, loadAllSubagentTranscriptsFromDisk, MAX_TRANSCRIPT_READ_BYTES } from '../utils/sessionStorage.js'; +import { jsonStringify } from '../utils/slowOperations.js'; +import { asSystemPrompt } from '../utils/systemPromptType.js'; +import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js'; +import { Byline } from './design-system/Byline.js'; +import { Dialog } from './design-system/Dialog.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +import TextInput from './TextInput.js'; + +// This value was determined experimentally by testing the URL length limit +const GITHUB_URL_LIMIT = 7250; +const GITHUB_ISSUES_REPO_URL = "external" === 'ant' ? 'https://github.com/anthropics/claude-cli-internal/issues' : 'https://github.com/anthropics/claude-code/issues'; +type Props = { + abortSignal: AbortSignal; + messages: Message[]; + initialDescription?: string; + onDone(result: string, options?: { + display?: CommandResultDisplay; + }): void; + backgroundTasks?: { + [taskId: string]: { + type: string; + identity?: { + agentId: string; + }; + messages?: Message[]; + }; + }; +}; +type Step = 'userInput' | 'consent' | 'submitting' | 'done'; +type FeedbackData = { + // latestAssistantMessageId is the message ID from the latest main model call + latestAssistantMessageId: string | null; + message_count: number; + datetime: string; + description: string; + platform: string; + gitRepo: boolean; + version: string | null; + transcript: Message[]; + subagentTranscripts?: { + [agentId: string]: Message[]; + }; + rawTranscriptJsonl?: string; +}; + +// Utility function to redact sensitive information from strings +export function redactSensitiveInfo(text: string): string { + let redacted = text; + + // Anthropic API keys (sk-ant...) with or without quotes + // First handle the case with quotes + redacted = redacted.replace(/"(sk-ant[^\s"']{24,})"/g, '"[REDACTED_API_KEY]"'); + // Then handle the cases without quotes - more general pattern + redacted = redacted.replace( + // eslint-disable-next-line custom-rules/no-lookbehind-regex -- .replace(re, string) on /bug path: no-match returns same string (Object.is) + /(? { + // Sanitize error logs to remove any API keys + return getInMemoryErrors().map(errorInfo => { + // Create a copy of the error info to avoid modifying the original + const errorCopy = { + ...errorInfo + } as { + error?: string; + timestamp?: string; + }; + + // Sanitize error if present and is a string + if (errorCopy && typeof errorCopy.error === 'string') { + errorCopy.error = redactSensitiveInfo(errorCopy.error); + } + return errorCopy; + }); +} +async function loadRawTranscriptJsonl(): Promise { + try { + const transcriptPath = getTranscriptPath(); + const { + size + } = await stat(transcriptPath); + if (size > MAX_TRANSCRIPT_READ_BYTES) { + logForDebugging(`Skipping raw transcript read: file too large (${size} bytes)`, { + level: 'warn' + }); + return null; + } + return await readFile(transcriptPath, 'utf-8'); + } catch { + return null; + } +} +export function Feedback({ + abortSignal, + messages, + initialDescription, + onDone, + backgroundTasks = {} +}: Props): React.ReactNode { + const [step, setStep] = useState('userInput'); + const [cursorOffset, setCursorOffset] = useState(0); + const [description, setDescription] = useState(initialDescription ?? ''); + const [feedbackId, setFeedbackId] = useState(null); + const [error, setError] = useState(null); + const [envInfo, setEnvInfo] = useState<{ + isGit: boolean; + gitState: GitRepoState | null; + }>({ + isGit: false, + gitState: null + }); + const [title, setTitle] = useState(null); + const textInputColumns = useTerminalSize().columns - 4; + useEffect(() => { + async function loadEnvInfo() { + const isGit = await getIsGit(); + let gitState: GitRepoState | null = null; + if (isGit) { + gitState = await getGitState(); + } + setEnvInfo({ + isGit, + gitState + }); + } + void loadEnvInfo(); + }, []); + const submitReport = useCallback(async () => { + setStep('submitting'); + setError(null); + setFeedbackId(null); + + // Get sanitized errors for the report + const sanitizedErrors = getSanitizedErrorLogs(); + + // Extract last assistant message ID from messages array + const lastAssistantMessage = getLastAssistantMessage(messages); + const lastAssistantMessageId = lastAssistantMessage?.requestId ?? null; + const [diskTranscripts, rawTranscriptJsonl] = await Promise.all([loadAllSubagentTranscriptsFromDisk(), loadRawTranscriptJsonl()]); + const teammateTranscripts = extractTeammateTranscriptsFromTasks(backgroundTasks); + const subagentTranscripts = { + ...diskTranscripts, + ...teammateTranscripts + }; + const reportData = { + latestAssistantMessageId: lastAssistantMessageId, + message_count: messages.length, + datetime: new Date().toISOString(), + description, + platform: env.platform, + gitRepo: envInfo.isGit, + terminal: env.terminal, + version: MACRO.VERSION, + transcript: normalizeMessagesForAPI(messages), + errors: sanitizedErrors, + lastApiRequest: getLastAPIRequest(), + ...(Object.keys(subagentTranscripts).length > 0 && { + subagentTranscripts + }), + ...(rawTranscriptJsonl && { + rawTranscriptJsonl + }) + }; + const [result, t] = await Promise.all([submitFeedback(reportData, abortSignal), generateTitle(description, abortSignal)]); + setTitle(t); + if (result.success) { + if (result.feedbackId) { + setFeedbackId(result.feedbackId); + logEvent('tengu_bug_report_submitted', { + feedback_id: result.feedbackId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + last_assistant_message_id: lastAssistantMessageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + // 1P-only: freeform text approved for BQ. Join on feedback_id. + logEventTo1P('tengu_bug_report_description', { + feedback_id: result.feedbackId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + description: redactSensitiveInfo(description) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + setStep('done'); + } else { + if (result.isZdrOrg) { + setError('Feedback collection is not available for organizations with custom data retention policies.'); + } else { + setError('Could not submit feedback. Please try again later.'); + } + // Stay on userInput step so user can retry with their content preserved + setStep('userInput'); + } + }, [description, envInfo.isGit, messages]); + + // Handle cancel - this will be called by Dialog's automatic Esc handling + const handleCancel = useCallback(() => { + // Don't cancel when done - let other keys close the dialog + if (step === 'done') { + if (error) { + onDone('Error submitting feedback / bug report', { + display: 'system' + }); + } else { + onDone('Feedback / bug report submitted', { + display: 'system' + }); + } + return; + } + onDone('Feedback / bug report cancelled', { + display: 'system' + }); + }, [step, error, onDone]); + + // During text input, use Settings context where only Escape (not 'n') triggers confirm:no. + // This allows typing 'n' in the text field while still supporting Escape to cancel. + useKeybinding('confirm:no', handleCancel, { + context: 'Settings', + isActive: step === 'userInput' + }); + useInput((input, key) => { + // Allow any key press to close the dialog when done or when there's an error + if (step === 'done') { + if (key.return && title) { + // Open GitHub issue URL when Enter is pressed + const issueUrl = createGitHubIssueUrl(feedbackId ?? '', title, description, getSanitizedErrorLogs()); + void openBrowser(issueUrl); + } + if (error) { + onDone('Error submitting feedback / bug report', { + display: 'system' + }); + } else { + onDone('Feedback / bug report submitted', { + display: 'system' + }); + } + return; + } + + // When in userInput step with error, allow user to edit and retry + // (don't close on any keypress - they can still press Esc to cancel) + if (error && step !== 'userInput') { + onDone('Error submitting feedback / bug report', { + display: 'system' + }); + return; + } + if (step === 'consent' && (key.return || input === ' ')) { + void submitReport(); + } + }); + return exitState.pending ? Press {exitState.keyName} again to exit : step === 'userInput' ? + + + : step === 'consent' ? + + + : null}> + {step === 'userInput' && + Describe the issue below: + { + setDescription(value); + // Clear error when user starts editing to allow retry + if (error) { + setError(null); + } + }} columns={textInputColumns} onSubmit={() => setStep('consent')} onExitMessage={() => onDone('Feedback cancelled', { + display: 'system' + })} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} showCursor /> + {error && + {error} + + Edit and press Enter to retry, or Esc to cancel + + } + } + + {step === 'consent' && + This report will include: + + + - Your feedback / bug description:{' '} + {description} + + + - Environment info:{' '} + + {env.platform}, {env.terminal}, v{MACRO.VERSION} + + + {envInfo.gitState && + - Git repo metadata:{' '} + + {envInfo.gitState.branchName} + {envInfo.gitState.commitHash ? `, ${envInfo.gitState.commitHash.slice(0, 7)}` : ''} + {envInfo.gitState.remoteUrl ? ` @ ${envInfo.gitState.remoteUrl}` : ''} + {!envInfo.gitState.isHeadOnRemote && ', not synced'} + {!envInfo.gitState.isClean && ', has local changes'} + + } + - Current session transcript + + + + We will use your feedback to debug related issues or to improve{' '} + Claude Code's functionality (eg. to reduce the risk of bugs + occurring in the future). + + + + + Press Enter to confirm and submit. + + + } + + {step === 'submitting' && + Submitting report… + } + + {step === 'done' && + {error ? {error} : Thank you for your report!} + {feedbackId && Feedback ID: {feedbackId}} + + Press + Enter + + to open your browser and draft a GitHub issue, or any other key to + close. + + + } + ; +} +export function createGitHubIssueUrl(feedbackId: string, title: string, description: string, errors: Array<{ + error?: string; + timestamp?: string; +}>): string { + const sanitizedTitle = redactSensitiveInfo(title); + const sanitizedDescription = redactSensitiveInfo(description); + const bodyPrefix = `**Bug Description**\n${sanitizedDescription}\n\n` + `**Environment Info**\n` + `- Platform: ${env.platform}\n` + `- Terminal: ${env.terminal}\n` + `- Version: ${MACRO.VERSION || 'unknown'}\n` + `- Feedback ID: ${feedbackId}\n` + `\n**Errors**\n\`\`\`json\n`; + const errorSuffix = `\n\`\`\`\n`; + const errorsJson = jsonStringify(errors); + const baseUrl = `${GITHUB_ISSUES_REPO_URL}/new?title=${encodeURIComponent(sanitizedTitle)}&labels=user-reported,bug&body=`; + const truncationNote = `\n**Note:** Content was truncated.\n`; + const encodedPrefix = encodeURIComponent(bodyPrefix); + const encodedSuffix = encodeURIComponent(errorSuffix); + const encodedNote = encodeURIComponent(truncationNote); + const encodedErrors = encodeURIComponent(errorsJson); + + // Calculate space available for errors + const spaceForErrors = GITHUB_URL_LIMIT - baseUrl.length - encodedPrefix.length - encodedSuffix.length - encodedNote.length; + + // If description alone exceeds limit, truncate everything + if (spaceForErrors <= 0) { + const ellipsis = encodeURIComponent('…'); + const buffer = 50; // Extra safety margin + const maxEncodedLength = GITHUB_URL_LIMIT - baseUrl.length - ellipsis.length - encodedNote.length - buffer; + const fullBody = bodyPrefix + errorsJson + errorSuffix; + let encodedFullBody = encodeURIComponent(fullBody); + if (encodedFullBody.length > maxEncodedLength) { + encodedFullBody = encodedFullBody.slice(0, maxEncodedLength); + // Don't cut in middle of %XX sequence + const lastPercent = encodedFullBody.lastIndexOf('%'); + if (lastPercent >= encodedFullBody.length - 2) { + encodedFullBody = encodedFullBody.slice(0, lastPercent); + } + } + return baseUrl + encodedFullBody + ellipsis + encodedNote; + } + + // If errors fit, no truncation needed + if (encodedErrors.length <= spaceForErrors) { + return baseUrl + encodedPrefix + encodedErrors + encodedSuffix; + } + + // Truncate errors to fit (prioritize keeping description) + // Slice encoded errors directly, then trim to avoid cutting %XX sequences + const ellipsis = encodeURIComponent('…'); + const buffer = 50; // Extra safety margin + let truncatedEncodedErrors = encodedErrors.slice(0, spaceForErrors - ellipsis.length - buffer); + // If we cut in middle of %XX, back up to before the % + const lastPercent = truncatedEncodedErrors.lastIndexOf('%'); + if (lastPercent >= truncatedEncodedErrors.length - 2) { + truncatedEncodedErrors = truncatedEncodedErrors.slice(0, lastPercent); + } + return baseUrl + encodedPrefix + truncatedEncodedErrors + ellipsis + encodedSuffix + encodedNote; +} +async function generateTitle(description: string, abortSignal: AbortSignal): Promise { + try { + const response = await queryHaiku({ + systemPrompt: asSystemPrompt(['Generate a concise, technical issue title (max 80 chars) for a public GitHub issue based on this bug report for Claude Code.', 'Claude Code is an agentic coding CLI based on the Anthropic API.', 'The title should:', '- Include the type of issue [Bug] or [Feature Request] as the first thing in the title', '- Be concise, specific and descriptive of the actual problem', '- Use technical terminology appropriate for a software issue', '- For error messages, extract the key error (e.g., "Missing Tool Result Block" rather than the full message)', '- Be direct and clear for developers to understand the problem', '- If you cannot determine a clear issue, use "Bug Report: [brief description]"', '- Any LLM API errors are from the Anthropic API, not from any other model provider', 'Your response will be directly used as the title of the Github issue, and as such should not contain any other commentary or explaination', 'Examples of good titles include: "[Bug] Auto-Compact triggers to soon", "[Bug] Anthropic API Error: Missing Tool Result Block", "[Bug] Error: Invalid Model Name for Opus"']), + userPrompt: description, + signal: abortSignal, + options: { + hasAppendSystemPrompt: false, + toolChoice: undefined, + isNonInteractiveSession: false, + agents: [], + querySource: 'feedback', + mcpTools: [] + } + }); + const title = response.message.content[0]?.type === 'text' ? response.message.content[0].text : 'Bug Report'; + + // Check if the title contains an API error message + if (startsWithApiErrorPrefix(title)) { + return createFallbackTitle(description); + } + return title; + } catch (error) { + // If there's any error in title generation, use a fallback title + logError(error); + return createFallbackTitle(description); + } +} +function createFallbackTitle(description: string): string { + // Create a safe fallback title based on the bug description + + // Try to extract a meaningful title from the first line + const firstLine = description.split('\n')[0] || ''; + + // If the first line is very short, use it directly + if (firstLine.length <= 60 && firstLine.length > 5) { + return firstLine; + } + + // For longer descriptions, create a truncated version + // Truncate at word boundaries when possible + let truncated = firstLine.slice(0, 60); + if (firstLine.length > 60) { + // Find the last space before the 60 char limit + const lastSpace = truncated.lastIndexOf(' '); + if (lastSpace > 30) { + // Only trim at word if we're not cutting too much + truncated = truncated.slice(0, lastSpace); + } + truncated += '...'; + } + return truncated.length < 10 ? 'Bug Report' : truncated; +} + +// Helper function to sanitize and log errors without exposing API keys +function sanitizeAndLogError(err: unknown): void { + if (err instanceof Error) { + // Create a copy with potentially sensitive info redacted + const safeError = new Error(redactSensitiveInfo(err.message)); + + // Also redact the stack trace if present + if (err.stack) { + safeError.stack = redactSensitiveInfo(err.stack); + } + logError(safeError); + } else { + // For non-Error objects, convert to string and redact sensitive info + const errorString = redactSensitiveInfo(String(err)); + logError(new Error(errorString)); + } +} +async function submitFeedback(data: FeedbackData, signal?: AbortSignal): Promise<{ + success: boolean; + feedbackId?: string; + isZdrOrg?: boolean; +}> { + if (isEssentialTrafficOnly()) { + return { + success: false + }; + } + try { + // Ensure OAuth token is fresh before getting auth headers + // This prevents 401 errors from stale cached tokens + await checkAndRefreshOAuthTokenIfNeeded(); + const authResult = getAuthHeaders(); + if (authResult.error) { + return { + success: false + }; + } + const headers: Record = { + 'Content-Type': 'application/json', + 'User-Agent': getUserAgent(), + ...authResult.headers + }; + const response = await axios.post('https://api.anthropic.com/api/claude_cli_feedback', { + content: jsonStringify(data) + }, { + headers, + timeout: 30000, + // 30 second timeout to prevent hanging + signal + }); + if (response.status === 200) { + const result = response.data; + if (result?.feedback_id) { + return { + success: true, + feedbackId: result.feedback_id + }; + } + sanitizeAndLogError(new Error('Failed to submit feedback: request did not return feedback_id')); + return { + success: false + }; + } + sanitizeAndLogError(new Error('Failed to submit feedback:' + response.status)); + return { + success: false + }; + } catch (err) { + // Handle cancellation/abort - don't log as error + if (axios.isCancel(err)) { + return { + success: false + }; + } + if (axios.isAxiosError(err) && err.response?.status === 403) { + const errorData = err.response.data; + if (errorData?.error?.type === 'permission_error' && errorData?.error?.message?.includes('Custom data retention settings')) { + sanitizeAndLogError(new Error('Cannot submit feedback because custom data retention settings are enabled')); + return { + success: false, + isZdrOrg: true + }; + } + } + // Use our safe error logging function to avoid leaking API keys + sanitizeAndLogError(err); + return { + success: false + }; + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJheGlvcyIsInJlYWRGaWxlIiwic3RhdCIsIlJlYWN0IiwidXNlQ2FsbGJhY2siLCJ1c2VFZmZlY3QiLCJ1c2VTdGF0ZSIsImdldExhc3RBUElSZXF1ZXN0IiwibG9nRXZlbnRUbzFQIiwiQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyIsImxvZ0V2ZW50IiwiZ2V0TGFzdEFzc2lzdGFudE1lc3NhZ2UiLCJub3JtYWxpemVNZXNzYWdlc0ZvckFQSSIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwidXNlVGVybWluYWxTaXplIiwiQm94IiwiVGV4dCIsInVzZUlucHV0IiwidXNlS2V5YmluZGluZyIsInF1ZXJ5SGFpa3UiLCJzdGFydHNXaXRoQXBpRXJyb3JQcmVmaXgiLCJNZXNzYWdlIiwiY2hlY2tBbmRSZWZyZXNoT0F1dGhUb2tlbklmTmVlZGVkIiwib3BlbkJyb3dzZXIiLCJsb2dGb3JEZWJ1Z2dpbmciLCJlbnYiLCJHaXRSZXBvU3RhdGUiLCJnZXRHaXRTdGF0ZSIsImdldElzR2l0IiwiZ2V0QXV0aEhlYWRlcnMiLCJnZXRVc2VyQWdlbnQiLCJnZXRJbk1lbW9yeUVycm9ycyIsImxvZ0Vycm9yIiwiaXNFc3NlbnRpYWxUcmFmZmljT25seSIsImV4dHJhY3RUZWFtbWF0ZVRyYW5zY3JpcHRzRnJvbVRhc2tzIiwiZ2V0VHJhbnNjcmlwdFBhdGgiLCJsb2FkQWxsU3ViYWdlbnRUcmFuc2NyaXB0c0Zyb21EaXNrIiwiTUFYX1RSQU5TQ1JJUFRfUkVBRF9CWVRFUyIsImpzb25TdHJpbmdpZnkiLCJhc1N5c3RlbVByb21wdCIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIkJ5bGluZSIsIkRpYWxvZyIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwiVGV4dElucHV0IiwiR0lUSFVCX1VSTF9MSU1JVCIsIkdJVEhVQl9JU1NVRVNfUkVQT19VUkwiLCJQcm9wcyIsImFib3J0U2lnbmFsIiwiQWJvcnRTaWduYWwiLCJtZXNzYWdlcyIsImluaXRpYWxEZXNjcmlwdGlvbiIsIm9uRG9uZSIsInJlc3VsdCIsIm9wdGlvbnMiLCJkaXNwbGF5IiwiYmFja2dyb3VuZFRhc2tzIiwidGFza0lkIiwidHlwZSIsImlkZW50aXR5IiwiYWdlbnRJZCIsIlN0ZXAiLCJGZWVkYmFja0RhdGEiLCJsYXRlc3RBc3Npc3RhbnRNZXNzYWdlSWQiLCJtZXNzYWdlX2NvdW50IiwiZGF0ZXRpbWUiLCJkZXNjcmlwdGlvbiIsInBsYXRmb3JtIiwiZ2l0UmVwbyIsInZlcnNpb24iLCJ0cmFuc2NyaXB0Iiwic3ViYWdlbnRUcmFuc2NyaXB0cyIsInJhd1RyYW5zY3JpcHRKc29ubCIsInJlZGFjdFNlbnNpdGl2ZUluZm8iLCJ0ZXh0IiwicmVkYWN0ZWQiLCJyZXBsYWNlIiwiZ2V0U2FuaXRpemVkRXJyb3JMb2dzIiwiQXJyYXkiLCJlcnJvciIsInRpbWVzdGFtcCIsIm1hcCIsImVycm9ySW5mbyIsImVycm9yQ29weSIsImxvYWRSYXdUcmFuc2NyaXB0SnNvbmwiLCJQcm9taXNlIiwidHJhbnNjcmlwdFBhdGgiLCJzaXplIiwibGV2ZWwiLCJGZWVkYmFjayIsIlJlYWN0Tm9kZSIsInN0ZXAiLCJzZXRTdGVwIiwiY3Vyc29yT2Zmc2V0Iiwic2V0Q3Vyc29yT2Zmc2V0Iiwic2V0RGVzY3JpcHRpb24iLCJmZWVkYmFja0lkIiwic2V0RmVlZGJhY2tJZCIsInNldEVycm9yIiwiZW52SW5mbyIsInNldEVudkluZm8iLCJpc0dpdCIsImdpdFN0YXRlIiwidGl0bGUiLCJzZXRUaXRsZSIsInRleHRJbnB1dENvbHVtbnMiLCJjb2x1bW5zIiwibG9hZEVudkluZm8iLCJzdWJtaXRSZXBvcnQiLCJzYW5pdGl6ZWRFcnJvcnMiLCJsYXN0QXNzaXN0YW50TWVzc2FnZSIsImxhc3RBc3Npc3RhbnRNZXNzYWdlSWQiLCJyZXF1ZXN0SWQiLCJkaXNrVHJhbnNjcmlwdHMiLCJhbGwiLCJ0ZWFtbWF0ZVRyYW5zY3JpcHRzIiwicmVwb3J0RGF0YSIsImxlbmd0aCIsIkRhdGUiLCJ0b0lTT1N0cmluZyIsInRlcm1pbmFsIiwiTUFDUk8iLCJWRVJTSU9OIiwiZXJyb3JzIiwibGFzdEFwaVJlcXVlc3QiLCJPYmplY3QiLCJrZXlzIiwidCIsInN1Ym1pdEZlZWRiYWNrIiwiZ2VuZXJhdGVUaXRsZSIsInN1Y2Nlc3MiLCJmZWVkYmFja19pZCIsImxhc3RfYXNzaXN0YW50X21lc3NhZ2VfaWQiLCJpc1pkck9yZyIsImhhbmRsZUNhbmNlbCIsImNvbnRleHQiLCJpc0FjdGl2ZSIsImlucHV0Iiwia2V5IiwicmV0dXJuIiwiaXNzdWVVcmwiLCJjcmVhdGVHaXRIdWJJc3N1ZVVybCIsImV4aXRTdGF0ZSIsInBlbmRpbmciLCJrZXlOYW1lIiwidmFsdWUiLCJicmFuY2hOYW1lIiwiY29tbWl0SGFzaCIsInNsaWNlIiwicmVtb3RlVXJsIiwiaXNIZWFkT25SZW1vdGUiLCJpc0NsZWFuIiwic2FuaXRpemVkVGl0bGUiLCJzYW5pdGl6ZWREZXNjcmlwdGlvbiIsImJvZHlQcmVmaXgiLCJlcnJvclN1ZmZpeCIsImVycm9yc0pzb24iLCJiYXNlVXJsIiwiZW5jb2RlVVJJQ29tcG9uZW50IiwidHJ1bmNhdGlvbk5vdGUiLCJlbmNvZGVkUHJlZml4IiwiZW5jb2RlZFN1ZmZpeCIsImVuY29kZWROb3RlIiwiZW5jb2RlZEVycm9ycyIsInNwYWNlRm9yRXJyb3JzIiwiZWxsaXBzaXMiLCJidWZmZXIiLCJtYXhFbmNvZGVkTGVuZ3RoIiwiZnVsbEJvZHkiLCJlbmNvZGVkRnVsbEJvZHkiLCJsYXN0UGVyY2VudCIsImxhc3RJbmRleE9mIiwidHJ1bmNhdGVkRW5jb2RlZEVycm9ycyIsInJlc3BvbnNlIiwic3lzdGVtUHJvbXB0IiwidXNlclByb21wdCIsInNpZ25hbCIsImhhc0FwcGVuZFN5c3RlbVByb21wdCIsInRvb2xDaG9pY2UiLCJ1bmRlZmluZWQiLCJpc05vbkludGVyYWN0aXZlU2Vzc2lvbiIsImFnZW50cyIsInF1ZXJ5U291cmNlIiwibWNwVG9vbHMiLCJtZXNzYWdlIiwiY29udGVudCIsImNyZWF0ZUZhbGxiYWNrVGl0bGUiLCJmaXJzdExpbmUiLCJzcGxpdCIsInRydW5jYXRlZCIsImxhc3RTcGFjZSIsInNhbml0aXplQW5kTG9nRXJyb3IiLCJlcnIiLCJFcnJvciIsInNhZmVFcnJvciIsInN0YWNrIiwiZXJyb3JTdHJpbmciLCJTdHJpbmciLCJkYXRhIiwiYXV0aFJlc3VsdCIsImhlYWRlcnMiLCJSZWNvcmQiLCJwb3N0IiwidGltZW91dCIsInN0YXR1cyIsImlzQ2FuY2VsIiwiaXNBeGlvc0Vycm9yIiwiZXJyb3JEYXRhIiwiaW5jbHVkZXMiXSwic291cmNlcyI6WyJGZWVkYmFjay50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGF4aW9zIGZyb20gJ2F4aW9zJ1xuaW1wb3J0IHsgcmVhZEZpbGUsIHN0YXQgfSBmcm9tICdmcy9wcm9taXNlcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGdldExhc3RBUElSZXF1ZXN0IH0gZnJvbSAnc3JjL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7IGxvZ0V2ZW50VG8xUCB9IGZyb20gJ3NyYy9zZXJ2aWNlcy9hbmFseXRpY3MvZmlyc3RQYXJ0eUV2ZW50TG9nZ2VyLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7XG4gIGdldExhc3RBc3Npc3RhbnRNZXNzYWdlLFxuICBub3JtYWxpemVNZXNzYWdlc0ZvckFQSSxcbn0gZnJvbSAnc3JjL3V0aWxzL21lc3NhZ2VzLmpzJ1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0LCB1c2VJbnB1dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgcXVlcnlIYWlrdSB9IGZyb20gJy4uL3NlcnZpY2VzL2FwaS9jbGF1ZGUuanMnXG5pbXBvcnQgeyBzdGFydHNXaXRoQXBpRXJyb3JQcmVmaXggfSBmcm9tICcuLi9zZXJ2aWNlcy9hcGkvZXJyb3JzLmpzJ1xuaW1wb3J0IHR5cGUgeyBNZXNzYWdlIH0gZnJvbSAnLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IGNoZWNrQW5kUmVmcmVzaE9BdXRoVG9rZW5JZk5lZWRlZCB9IGZyb20gJy4uL3V0aWxzL2F1dGguanMnXG5pbXBvcnQgeyBvcGVuQnJvd3NlciB9IGZyb20gJy4uL3V0aWxzL2Jyb3dzZXIuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IGVudiB9IGZyb20gJy4uL3V0aWxzL2Vudi5qcydcbmltcG9ydCB7IHR5cGUgR2l0UmVwb1N0YXRlLCBnZXRHaXRTdGF0ZSwgZ2V0SXNHaXQgfSBmcm9tICcuLi91dGlscy9naXQuanMnXG5pbXBvcnQgeyBnZXRBdXRoSGVhZGVycywgZ2V0VXNlckFnZW50IH0gZnJvbSAnLi4vdXRpbHMvaHR0cC5qcydcbmltcG9ydCB7IGdldEluTWVtb3J5RXJyb3JzLCBsb2dFcnJvciB9IGZyb20gJy4uL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7IGlzRXNzZW50aWFsVHJhZmZpY09ubHkgfSBmcm9tICcuLi91dGlscy9wcml2YWN5TGV2ZWwuanMnXG5pbXBvcnQge1xuICBleHRyYWN0VGVhbW1hdGVUcmFuc2NyaXB0c0Zyb21UYXNrcyxcbiAgZ2V0VHJhbnNjcmlwdFBhdGgsXG4gIGxvYWRBbGxTdWJhZ2VudFRyYW5zY3JpcHRzRnJvbURpc2ssXG4gIE1BWF9UUkFOU0NSSVBUX1JFQURfQllURVMsXG59IGZyb20gJy4uL3V0aWxzL3Nlc3Npb25TdG9yYWdlLmpzJ1xuaW1wb3J0IHsganNvblN0cmluZ2lmeSB9IGZyb20gJy4uL3V0aWxzL3Nsb3dPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHsgYXNTeXN0ZW1Qcm9tcHQgfSBmcm9tICcuLi91dGlscy9zeXN0ZW1Qcm9tcHRUeXBlLmpzJ1xuaW1wb3J0IHsgQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vQnlsaW5lLmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IFRleHRJbnB1dCBmcm9tICcuL1RleHRJbnB1dC5qcydcblxuLy8gVGhpcyB2YWx1ZSB3YXMgZGV0ZXJtaW5lZCBleHBlcmltZW50YWxseSBieSB0ZXN0aW5nIHRoZSBVUkwgbGVuZ3RoIGxpbWl0XG5jb25zdCBHSVRIVUJfVVJMX0xJTUlUID0gNzI1MFxuY29uc3QgR0lUSFVCX0lTU1VFU19SRVBPX1VSTCA9XG4gIFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCdcbiAgICA/ICdodHRwczovL2dpdGh1Yi5jb20vYW50aHJvcGljcy9jbGF1ZGUtY2xpLWludGVybmFsL2lzc3VlcydcbiAgICA6ICdodHRwczovL2dpdGh1Yi5jb20vYW50aHJvcGljcy9jbGF1ZGUtY29kZS9pc3N1ZXMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGFib3J0U2lnbmFsOiBBYm9ydFNpZ25hbFxuICBtZXNzYWdlczogTWVzc2FnZVtdXG4gIGluaXRpYWxEZXNjcmlwdGlvbj86IHN0cmluZ1xuICBvbkRvbmUocmVzdWx0OiBzdHJpbmcsIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9KTogdm9pZFxuICBiYWNrZ3JvdW5kVGFza3M/OiB7XG4gICAgW3Rhc2tJZDogc3RyaW5nXToge1xuICAgICAgdHlwZTogc3RyaW5nXG4gICAgICBpZGVudGl0eT86IHsgYWdlbnRJZDogc3RyaW5nIH1cbiAgICAgIG1lc3NhZ2VzPzogTWVzc2FnZVtdXG4gICAgfVxuICB9XG59XG5cbnR5cGUgU3RlcCA9ICd1c2VySW5wdXQnIHwgJ2NvbnNlbnQnIHwgJ3N1Ym1pdHRpbmcnIHwgJ2RvbmUnXG5cbnR5cGUgRmVlZGJhY2tEYXRhID0ge1xuICAvLyBsYXRlc3RBc3Npc3RhbnRNZXNzYWdlSWQgaXMgdGhlIG1lc3NhZ2UgSUQgZnJvbSB0aGUgbGF0ZXN0IG1haW4gbW9kZWwgY2FsbFxuICBsYXRlc3RBc3Npc3RhbnRNZXNzYWdlSWQ6IHN0cmluZyB8IG51bGxcbiAgbWVzc2FnZV9jb3VudDogbnVtYmVyXG4gIGRhdGV0aW1lOiBzdHJpbmdcbiAgZGVzY3JpcHRpb246IHN0cmluZ1xuICBwbGF0Zm9ybTogc3RyaW5nXG4gIGdpdFJlcG86IGJvb2xlYW5cbiAgdmVyc2lvbjogc3RyaW5nIHwgbnVsbFxuICB0cmFuc2NyaXB0OiBNZXNzYWdlW11cbiAgc3ViYWdlbnRUcmFuc2NyaXB0cz86IHsgW2FnZW50SWQ6IHN0cmluZ106IE1lc3NhZ2VbXSB9XG4gIHJhd1RyYW5zY3JpcHRKc29ubD86IHN0cmluZ1xufVxuXG4vLyBVdGlsaXR5IGZ1bmN0aW9uIHRvIHJlZGFjdCBzZW5zaXRpdmUgaW5mb3JtYXRpb24gZnJvbSBzdHJpbmdzXG5leHBvcnQgZnVuY3Rpb24gcmVkYWN0U2Vuc2l0aXZlSW5mbyh0ZXh0OiBzdHJpbmcpOiBzdHJpbmcge1xuICBsZXQgcmVkYWN0ZWQgPSB0ZXh0XG5cbiAgLy8gQW50aHJvcGljIEFQSSBrZXlzIChzay1hbnQuLi4pIHdpdGggb3Igd2l0aG91dCBxdW90ZXNcbiAgLy8gRmlyc3QgaGFuZGxlIHRoZSBjYXNlIHdpdGggcXVvdGVzXG4gIHJlZGFjdGVkID0gcmVkYWN0ZWQucmVwbGFjZSgvXCIoc2stYW50W15cXHNcIiddezI0LH0pXCIvZywgJ1wiW1JFREFDVEVEX0FQSV9LRVldXCInKVxuICAvLyBUaGVuIGhhbmRsZSB0aGUgY2FzZXMgd2l0aG91dCBxdW90ZXMgLSBtb3JlIGdlbmVyYWwgcGF0dGVyblxuICByZWRhY3RlZCA9IHJlZGFjdGVkLnJlcGxhY2UoXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9uby1sb29rYmVoaW5kLXJlZ2V4IC0tIC5yZXBsYWNlKHJlLCBzdHJpbmcpIG9uIC9idWcgcGF0aDogbm8tbWF0Y2ggcmV0dXJucyBzYW1lIHN0cmluZyAoT2JqZWN0LmlzKVxuICAgIC8oPzwhW0EtWmEtejAtOVwiJ10pKHNrLWFudC0/W0EtWmEtejAtOV8tXXsxMCx9KSg/IVtBLVphLXowLTlcIiddKS9nLFxuICAgICdbUkVEQUNURURfQVBJX0tFWV0nLFxuICApXG5cbiAgLy8gQVdTIGtleXMgLSBBV1NYWFhYIGZvcm1hdCAtIGFkZCB0aGUgcGF0dGVybiB3ZSBuZWVkIGZvciB0aGUgdGVzdFxuICByZWRhY3RlZCA9IHJlZGFjdGVkLnJlcGxhY2UoXG4gICAgL0FXUyBrZXk6IFwiKEFXU1tBLVowLTldezIwLH0pXCIvZyxcbiAgICAnQVdTIGtleTogXCJbUkVEQUNURURfQVdTX0tFWV1cIicsXG4gIClcblxuICAvLyBBV1MgQUtJQVhYWCBrZXlzXG4gIHJlZGFjdGVkID0gcmVkYWN0ZWQucmVwbGFjZSgvKEFLSUFbQS1aMC05XXsxNn0pL2csICdbUkVEQUNURURfQVdTX0tFWV0nKVxuXG4gIC8vIEdvb2dsZSBDbG91ZCBrZXlzXG4gIHJlZGFjdGVkID0gcmVkYWN0ZWQucmVwbGFjZShcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL25vLWxvb2tiZWhpbmQtcmVnZXggLS0gc2FtZSBhcyBhYm92ZVxuICAgIC8oPzwhW0EtWmEtejAtOV0pKEFJemFbQS1aYS16MC05Xy1dezM1fSkoPyFbQS1aYS16MC05XSkvZyxcbiAgICAnW1JFREFDVEVEX0dDUF9LRVldJyxcbiAgKVxuXG4gIC8vIFZlcnRleCBBSSBzZXJ2aWNlIGFjY291bnQga2V5c1xuICByZWRhY3RlZCA9IHJlZGFjdGVkLnJlcGxhY2UoXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9uby1sb29rYmVoaW5kLXJlZ2V4IC0tIHNhbWUgYXMgYWJvdmVcbiAgICAvKD88IVtBLVphLXowLTldKShbYS16MC05LV0rQFthLXowLTktXStcXC5pYW1cXC5nc2VydmljZWFjY291bnRcXC5jb20pKD8hW0EtWmEtejAtOV0pL2csXG4gICAgJ1tSRURBQ1RFRF9HQ1BfU0VSVklDRV9BQ0NPVU5UXScsXG4gIClcblxuICAvLyBHZW5lcmljIEFQSSBrZXlzIGluIGhlYWRlcnNcbiAgcmVkYWN0ZWQgPSByZWRhY3RlZC5yZXBsYWNlKFxuICAgIC8oW1wiJ10/eC1hcGkta2V5W1wiJ10/XFxzKls6PV1cXHMqW1wiJ10/KVteXCInLFxccyl9XFxdXSsvZ2ksXG4gICAgJyQxW1JFREFDVEVEX0FQSV9LRVldJyxcbiAgKVxuXG4gIC8vIEF1dGhvcml6YXRpb24gaGVhZGVycyBhbmQgQmVhcmVyIHRva2Vuc1xuICByZWRhY3RlZCA9IHJlZGFjdGVkLnJlcGxhY2UoXG4gICAgLyhbXCInXT9hdXRob3JpemF0aW9uW1wiJ10/XFxzKls6PV1cXHMqW1wiJ10/KGJlYXJlclxccyspPylbXlwiJyxcXHMpfVxcXV0rL2dpLFxuICAgICckMVtSRURBQ1RFRF9UT0tFTl0nLFxuICApXG5cbiAgLy8gQVdTIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICByZWRhY3RlZCA9IHJlZGFjdGVkLnJlcGxhY2UoXG4gICAgLyhBV1NbXy1dW0EtWmEtejAtOV9dK1xccypbPTpdXFxzKilbXCInXT9bXlwiJyxcXHMpfVxcXV0rW1wiJ10/L2dpLFxuICAgICckMVtSRURBQ1RFRF9BV1NfVkFMVUVdJyxcbiAgKVxuXG4gIC8vIEdDUCBlbnZpcm9ubWVudCB2YXJpYWJsZXNcbiAgcmVkYWN0ZWQgPSByZWRhY3RlZC5yZXBsYWNlKFxuICAgIC8oR09PR0xFW18tXVtBLVphLXowLTlfXStcXHMqWz06XVxccyopW1wiJ10/W15cIicsXFxzKX1cXF1dK1tcIiddPy9naSxcbiAgICAnJDFbUkVEQUNURURfR0NQX1ZBTFVFXScsXG4gIClcblxuICAvLyBFbnZpcm9ubWVudCB2YXJpYWJsZXMgd2l0aCBrZXlzXG4gIHJlZGFjdGVkID0gcmVkYWN0ZWQucmVwbGFjZShcbiAgICAvKChBUElbLV9dP0tFWXxUT0tFTnxTRUNSRVR8UEFTU1dPUkQpXFxzKls9Ol1cXHMqKVtcIiddP1teXCInLFxccyl9XFxdXStbXCInXT8vZ2ksXG4gICAgJyQxW1JFREFDVEVEXScsXG4gIClcblxuICByZXR1cm4gcmVkYWN0ZWRcbn1cblxuLy8gR2V0IHNhbml0aXplZCBlcnJvciBsb2dzIHdpdGggc2Vuc2l0aXZlIGluZm9ybWF0aW9uIHJlZGFjdGVkXG5mdW5jdGlvbiBnZXRTYW5pdGl6ZWRFcnJvckxvZ3MoKTogQXJyYXk8e1xuICBlcnJvcj86IHN0cmluZ1xuICB0aW1lc3RhbXA/OiBzdHJpbmdcbn0+IHtcbiAgLy8gU2FuaXRpemUgZXJyb3IgbG9ncyB0byByZW1vdmUgYW55IEFQSSBrZXlzXG4gIHJldHVybiBnZXRJbk1lbW9yeUVycm9ycygpLm1hcChlcnJvckluZm8gPT4ge1xuICAgIC8vIENyZWF0ZSBhIGNvcHkgb2YgdGhlIGVycm9yIGluZm8gdG8gYXZvaWQgbW9kaWZ5aW5nIHRoZSBvcmlnaW5hbFxuICAgIGNvbnN0IGVycm9yQ29weSA9IHsgLi4uZXJyb3JJbmZvIH0gYXMgeyBlcnJvcj86IHN0cmluZzsgdGltZXN0YW1wPzogc3RyaW5nIH1cblxuICAgIC8vIFNhbml0aXplIGVycm9yIGlmIHByZXNlbnQgYW5kIGlzIGEgc3RyaW5nXG4gICAgaWYgKGVycm9yQ29weSAmJiB0eXBlb2YgZXJyb3JDb3B5LmVycm9yID09PSAnc3RyaW5nJykge1xuICAgICAgZXJyb3JDb3B5LmVycm9yID0gcmVkYWN0U2Vuc2l0aXZlSW5mbyhlcnJvckNvcHkuZXJyb3IpXG4gICAgfVxuXG4gICAgcmV0dXJuIGVycm9yQ29weVxuICB9KVxufVxuXG5hc3luYyBmdW5jdGlvbiBsb2FkUmF3VHJhbnNjcmlwdEpzb25sKCk6IFByb21pc2U8c3RyaW5nIHwgbnVsbD4ge1xuICB0cnkge1xuICAgIGNvbnN0IHRyYW5zY3JpcHRQYXRoID0gZ2V0VHJhbnNjcmlwdFBhdGgoKVxuICAgIGNvbnN0IHsgc2l6ZSB9ID0gYXdhaXQgc3RhdCh0cmFuc2NyaXB0UGF0aClcbiAgICBpZiAoc2l6ZSA+IE1BWF9UUkFOU0NSSVBUX1JFQURfQllURVMpIHtcbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgYFNraXBwaW5nIHJhdyB0cmFuc2NyaXB0IHJlYWQ6IGZpbGUgdG9vIGxhcmdlICgke3NpemV9IGJ5dGVzKWAsXG4gICAgICAgIHsgbGV2ZWw6ICd3YXJuJyB9LFxuICAgICAgKVxuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG4gICAgcmV0dXJuIGF3YWl0IHJlYWRGaWxlKHRyYW5zY3JpcHRQYXRoLCAndXRmLTgnKVxuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBGZWVkYmFjayh7XG4gIGFib3J0U2lnbmFsLFxuICBtZXNzYWdlcyxcbiAgaW5pdGlhbERlc2NyaXB0aW9uLFxuICBvbkRvbmUsXG4gIGJhY2tncm91bmRUYXNrcyA9IHt9LFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbc3RlcCwgc2V0U3RlcF0gPSB1c2VTdGF0ZTxTdGVwPigndXNlcklucHV0JylcbiAgY29uc3QgW2N1cnNvck9mZnNldCwgc2V0Q3Vyc29yT2Zmc2V0XSA9IHVzZVN0YXRlKDApXG4gIGNvbnN0IFtkZXNjcmlwdGlvbiwgc2V0RGVzY3JpcHRpb25dID0gdXNlU3RhdGUoaW5pdGlhbERlc2NyaXB0aW9uID8/ICcnKVxuICBjb25zdCBbZmVlZGJhY2tJZCwgc2V0RmVlZGJhY2tJZF0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICBjb25zdCBbZXJyb3IsIHNldEVycm9yXSA9IHVzZVN0YXRlPHN0cmluZyB8IG51bGw+KG51bGwpXG4gIGNvbnN0IFtlbnZJbmZvLCBzZXRFbnZJbmZvXSA9IHVzZVN0YXRlPHtcbiAgICBpc0dpdDogYm9vbGVhblxuICAgIGdpdFN0YXRlOiBHaXRSZXBvU3RhdGUgfCBudWxsXG4gIH0+KHsgaXNHaXQ6IGZhbHNlLCBnaXRTdGF0ZTogbnVsbCB9KVxuICBjb25zdCBbdGl0bGUsIHNldFRpdGxlXSA9IHVzZVN0YXRlPHN0cmluZyB8IG51bGw+KG51bGwpXG4gIGNvbnN0IHRleHRJbnB1dENvbHVtbnMgPSB1c2VUZXJtaW5hbFNpemUoKS5jb2x1bW5zIC0gNFxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgYXN5bmMgZnVuY3Rpb24gbG9hZEVudkluZm8oKSB7XG4gICAgICBjb25zdCBpc0dpdCA9IGF3YWl0IGdldElzR2l0KClcbiAgICAgIGxldCBnaXRTdGF0ZTogR2l0UmVwb1N0YXRlIHwgbnVsbCA9IG51bGxcbiAgICAgIGlmIChpc0dpdCkge1xuICAgICAgICBnaXRTdGF0ZSA9IGF3YWl0IGdldEdpdFN0YXRlKClcbiAgICAgIH1cbiAgICAgIHNldEVudkluZm8oeyBpc0dpdCwgZ2l0U3RhdGUgfSlcbiAgICB9XG4gICAgdm9pZCBsb2FkRW52SW5mbygpXG4gIH0sIFtdKVxuXG4gIGNvbnN0IHN1Ym1pdFJlcG9ydCA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBzZXRTdGVwKCdzdWJtaXR0aW5nJylcbiAgICBzZXRFcnJvcihudWxsKVxuICAgIHNldEZlZWRiYWNrSWQobnVsbClcblxuICAgIC8vIEdldCBzYW5pdGl6ZWQgZXJyb3JzIGZvciB0aGUgcmVwb3J0XG4gICAgY29uc3Qgc2FuaXRpemVkRXJyb3JzID0gZ2V0U2FuaXRpemVkRXJyb3JMb2dzKClcblxuICAgIC8vIEV4dHJhY3QgbGFzdCBhc3Npc3RhbnQgbWVzc2FnZSBJRCBmcm9tIG1lc3NhZ2VzIGFycmF5XG4gICAgY29uc3QgbGFzdEFzc2lzdGFudE1lc3NhZ2UgPSBnZXRMYXN0QXNzaXN0YW50TWVzc2FnZShtZXNzYWdlcylcbiAgICBjb25zdCBsYXN0QXNzaXN0YW50TWVzc2FnZUlkID0gbGFzdEFzc2lzdGFudE1lc3NhZ2U/LnJlcXVlc3RJZCA/PyBudWxsXG5cbiAgICBjb25zdCBbZGlza1RyYW5zY3JpcHRzLCByYXdUcmFuc2NyaXB0SnNvbmxdID0gYXdhaXQgUHJvbWlzZS5hbGwoW1xuICAgICAgbG9hZEFsbFN1YmFnZW50VHJhbnNjcmlwdHNGcm9tRGlzaygpLFxuICAgICAgbG9hZFJhd1RyYW5zY3JpcHRKc29ubCgpLFxuICAgIF0pXG4gICAgY29uc3QgdGVhbW1hdGVUcmFuc2NyaXB0cyA9XG4gICAgICBleHRyYWN0VGVhbW1hdGVUcmFuc2NyaXB0c0Zyb21UYXNrcyhiYWNrZ3JvdW5kVGFza3MpXG4gICAgY29uc3Qgc3ViYWdlbnRUcmFuc2NyaXB0cyA9IHsgLi4uZGlza1RyYW5zY3JpcHRzLCAuLi50ZWFtbWF0ZVRyYW5zY3JpcHRzIH1cblxuICAgIGNvbnN0IHJlcG9ydERhdGEgPSB7XG4gICAgICBsYXRlc3RBc3Npc3RhbnRNZXNzYWdlSWQ6IGxhc3RBc3Npc3RhbnRNZXNzYWdlSWQsXG4gICAgICBtZXNzYWdlX2NvdW50OiBtZXNzYWdlcy5sZW5ndGgsXG4gICAgICBkYXRldGltZTogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgZGVzY3JpcHRpb24sXG4gICAgICBwbGF0Zm9ybTogZW52LnBsYXRmb3JtLFxuICAgICAgZ2l0UmVwbzogZW52SW5mby5pc0dpdCxcbiAgICAgIHRlcm1pbmFsOiBlbnYudGVybWluYWwsXG4gICAgICB2ZXJzaW9uOiBNQUNSTy5WRVJTSU9OLFxuICAgICAgdHJhbnNjcmlwdDogbm9ybWFsaXplTWVzc2FnZXNGb3JBUEkobWVzc2FnZXMpLFxuICAgICAgZXJyb3JzOiBzYW5pdGl6ZWRFcnJvcnMsXG4gICAgICBsYXN0QXBpUmVxdWVzdDogZ2V0TGFzdEFQSVJlcXVlc3QoKSxcbiAgICAgIC4uLihPYmplY3Qua2V5cyhzdWJhZ2VudFRyYW5zY3JpcHRzKS5sZW5ndGggPiAwICYmIHtcbiAgICAgICAgc3ViYWdlbnRUcmFuc2NyaXB0cyxcbiAgICAgIH0pLFxuICAgICAgLi4uKHJhd1RyYW5zY3JpcHRKc29ubCAmJiB7IHJhd1RyYW5zY3JpcHRKc29ubCB9KSxcbiAgICB9XG5cbiAgICBjb25zdCBbcmVzdWx0LCB0XSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgIHN1Ym1pdEZlZWRiYWNrKHJlcG9ydERhdGEsIGFib3J0U2lnbmFsKSxcbiAgICAgIGdlbmVyYXRlVGl0bGUoZGVzY3JpcHRpb24sIGFib3J0U2lnbmFsKSxcbiAgICBdKVxuXG4gICAgc2V0VGl0bGUodClcblxuICAgIGlmIChyZXN1bHQuc3VjY2Vzcykge1xuICAgICAgaWYgKHJlc3VsdC5mZWVkYmFja0lkKSB7XG4gICAgICAgIHNldEZlZWRiYWNrSWQocmVzdWx0LmZlZWRiYWNrSWQpXG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9idWdfcmVwb3J0X3N1Ym1pdHRlZCcsIHtcbiAgICAgICAgICBmZWVkYmFja19pZDpcbiAgICAgICAgICAgIHJlc3VsdC5mZWVkYmFja0lkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgbGFzdF9hc3Npc3RhbnRfbWVzc2FnZV9pZDpcbiAgICAgICAgICAgIGxhc3RBc3Npc3RhbnRNZXNzYWdlSWQgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgfSlcbiAgICAgICAgLy8gMVAtb25seTogZnJlZWZvcm0gdGV4dCBhcHByb3ZlZCBmb3IgQlEuIEpvaW4gb24gZmVlZGJhY2tfaWQuXG4gICAgICAgIGxvZ0V2ZW50VG8xUCgndGVuZ3VfYnVnX3JlcG9ydF9kZXNjcmlwdGlvbicsIHtcbiAgICAgICAgICBmZWVkYmFja19pZDpcbiAgICAgICAgICAgIHJlc3VsdC5mZWVkYmFja0lkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgZGVzY3JpcHRpb246IHJlZGFjdFNlbnNpdGl2ZUluZm8oXG4gICAgICAgICAgICBkZXNjcmlwdGlvbixcbiAgICAgICAgICApIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICBzZXRTdGVwKCdkb25lJylcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKHJlc3VsdC5pc1pkck9yZykge1xuICAgICAgICBzZXRFcnJvcihcbiAgICAgICAgICAnRmVlZGJhY2sgY29sbGVjdGlvbiBpcyBub3QgYXZhaWxhYmxlIGZvciBvcmdhbml6YXRpb25zIHdpdGggY3VzdG9tIGRhdGEgcmV0ZW50aW9uIHBvbGljaWVzLicsXG4gICAgICAgIClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNldEVycm9yKCdDb3VsZCBub3Qgc3VibWl0IGZlZWRiYWNrLiBQbGVhc2UgdHJ5IGFnYWluIGxhdGVyLicpXG4gICAgICB9XG4gICAgICAvLyBTdGF5IG9uIHVzZXJJbnB1dCBzdGVwIHNvIHVzZXIgY2FuIHJldHJ5IHdpdGggdGhlaXIgY29udGVudCBwcmVzZXJ2ZWRcbiAgICAgIHNldFN0ZXAoJ3VzZXJJbnB1dCcpXG4gICAgfVxuICB9LCBbZGVzY3JpcHRpb24sIGVudkluZm8uaXNHaXQsIG1lc3NhZ2VzXSlcblxuICAvLyBIYW5kbGUgY2FuY2VsIC0gdGhpcyB3aWxsIGJlIGNhbGxlZCBieSBEaWFsb2cncyBhdXRvbWF0aWMgRXNjIGhhbmRsaW5nXG4gIGNvbnN0IGhhbmRsZUNhbmNlbCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICAvLyBEb24ndCBjYW5jZWwgd2hlbiBkb25lIC0gbGV0IG90aGVyIGtleXMgY2xvc2UgdGhlIGRpYWxvZ1xuICAgIGlmIChzdGVwID09PSAnZG9uZScpIHtcbiAgICAgIGlmIChlcnJvcikge1xuICAgICAgICBvbkRvbmUoJ0Vycm9yIHN1Ym1pdHRpbmcgZmVlZGJhY2sgLyBidWcgcmVwb3J0Jywge1xuICAgICAgICAgIGRpc3BsYXk6ICdzeXN0ZW0nLFxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb25Eb25lKCdGZWVkYmFjayAvIGJ1ZyByZXBvcnQgc3VibWl0dGVkJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgICAgfVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIG9uRG9uZSgnRmVlZGJhY2sgLyBidWcgcmVwb3J0IGNhbmNlbGxlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgfSwgW3N0ZXAsIGVycm9yLCBvbkRvbmVdKVxuXG4gIC8vIER1cmluZyB0ZXh0IGlucHV0LCB1c2UgU2V0dGluZ3MgY29udGV4dCB3aGVyZSBvbmx5IEVzY2FwZSAobm90ICduJykgdHJpZ2dlcnMgY29uZmlybTpuby5cbiAgLy8gVGhpcyBhbGxvd3MgdHlwaW5nICduJyBpbiB0aGUgdGV4dCBmaWVsZCB3aGlsZSBzdGlsbCBzdXBwb3J0aW5nIEVzY2FwZSB0byBjYW5jZWwuXG4gIHVzZUtleWJpbmRpbmcoJ2NvbmZpcm06bm8nLCBoYW5kbGVDYW5jZWwsIHtcbiAgICBjb250ZXh0OiAnU2V0dGluZ3MnLFxuICAgIGlzQWN0aXZlOiBzdGVwID09PSAndXNlcklucHV0JyxcbiAgfSlcblxuICB1c2VJbnB1dCgoaW5wdXQsIGtleSkgPT4ge1xuICAgIC8vIEFsbG93IGFueSBrZXkgcHJlc3MgdG8gY2xvc2UgdGhlIGRpYWxvZyB3aGVuIGRvbmUgb3Igd2hlbiB0aGVyZSdzIGFuIGVycm9yXG4gICAgaWYgKHN0ZXAgPT09ICdkb25lJykge1xuICAgICAgaWYgKGtleS5yZXR1cm4gJiYgdGl0bGUpIHtcbiAgICAgICAgLy8gT3BlbiBHaXRIdWIgaXNzdWUgVVJMIHdoZW4gRW50ZXIgaXMgcHJlc3NlZFxuICAgICAgICBjb25zdCBpc3N1ZVVybCA9IGNyZWF0ZUdpdEh1Yklzc3VlVXJsKFxuICAgICAgICAgIGZlZWRiYWNrSWQgPz8gJycsXG4gICAgICAgICAgdGl0bGUsXG4gICAgICAgICAgZGVzY3JpcHRpb24sXG4gICAgICAgICAgZ2V0U2FuaXRpemVkRXJyb3JMb2dzKCksXG4gICAgICAgIClcbiAgICAgICAgdm9pZCBvcGVuQnJvd3Nlcihpc3N1ZVVybClcbiAgICAgIH1cbiAgICAgIGlmIChlcnJvcikge1xuICAgICAgICBvbkRvbmUoJ0Vycm9yIHN1Ym1pdHRpbmcgZmVlZGJhY2sgLyBidWcgcmVwb3J0Jywge1xuICAgICAgICAgIGRpc3BsYXk6ICdzeXN0ZW0nLFxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb25Eb25lKCdGZWVkYmFjayAvIGJ1ZyByZXBvcnQgc3VibWl0dGVkJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgICAgfVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gV2hlbiBpbiB1c2VySW5wdXQgc3RlcCB3aXRoIGVycm9yLCBhbGxvdyB1c2VyIHRvIGVkaXQgYW5kIHJldHJ5XG4gICAgLy8gKGRvbid0IGNsb3NlIG9uIGFueSBrZXlwcmVzcyAtIHRoZXkgY2FuIHN0aWxsIHByZXNzIEVzYyB0byBjYW5jZWwpXG4gICAgaWYgKGVycm9yICYmIHN0ZXAgIT09ICd1c2VySW5wdXQnKSB7XG4gICAgICBvbkRvbmUoJ0Vycm9yIHN1Ym1pdHRpbmcgZmVlZGJhY2sgLyBidWcgcmVwb3J0Jywge1xuICAgICAgICBkaXNwbGF5OiAnc3lzdGVtJyxcbiAgICAgIH0pXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoc3RlcCA9PT0gJ2NvbnNlbnQnICYmIChrZXkucmV0dXJuIHx8IGlucHV0ID09PSAnICcpKSB7XG4gICAgICB2b2lkIHN1Ym1pdFJlcG9ydCgpXG4gICAgfVxuICB9KVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJTdWJtaXQgRmVlZGJhY2sgLyBCdWcgUmVwb3J0XCJcbiAgICAgIG9uQ2FuY2VsPXtoYW5kbGVDYW5jZWx9XG4gICAgICBpc0NhbmNlbEFjdGl2ZT17c3RlcCAhPT0gJ3VzZXJJbnB1dCd9XG4gICAgICBpbnB1dEd1aWRlPXtleGl0U3RhdGUgPT5cbiAgICAgICAgZXhpdFN0YXRlLnBlbmRpbmcgPyAoXG4gICAgICAgICAgPFRleHQ+UHJlc3Mge2V4aXRTdGF0ZS5rZXlOYW1lfSBhZ2FpbiB0byBleGl0PC9UZXh0PlxuICAgICAgICApIDogc3RlcCA9PT0gJ3VzZXJJbnB1dCcgPyAoXG4gICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVudGVyXCIgYWN0aW9uPVwiY29udGludWVcIiAvPlxuICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgKSA6IHN0ZXAgPT09ICdjb25zZW50JyA/IChcbiAgICAgICAgICA8QnlsaW5lPlxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiRW50ZXJcIiBhY3Rpb249XCJzdWJtaXRcIiAvPlxuICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgKSA6IG51bGxcbiAgICAgIH1cbiAgICA+XG4gICAgICB7c3RlcCA9PT0gJ3VzZXJJbnB1dCcgJiYgKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICAgIDxUZXh0PkRlc2NyaWJlIHRoZSBpc3N1ZSBiZWxvdzo8L1RleHQ+XG4gICAgICAgICAgPFRleHRJbnB1dFxuICAgICAgICAgICAgdmFsdWU9e2Rlc2NyaXB0aW9ufVxuICAgICAgICAgICAgb25DaGFuZ2U9e3ZhbHVlID0+IHtcbiAgICAgICAgICAgICAgc2V0RGVzY3JpcHRpb24odmFsdWUpXG4gICAgICAgICAgICAgIC8vIENsZWFyIGVycm9yIHdoZW4gdXNlciBzdGFydHMgZWRpdGluZyB0byBhbGxvdyByZXRyeVxuICAgICAgICAgICAgICBpZiAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICBzZXRFcnJvcihudWxsKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9fVxuICAgICAgICAgICAgY29sdW1ucz17dGV4dElucHV0Q29sdW1uc31cbiAgICAgICAgICAgIG9uU3VibWl0PXsoKSA9PiBzZXRTdGVwKCdjb25zZW50Jyl9XG4gICAgICAgICAgICBvbkV4aXRNZXNzYWdlPXsoKSA9PlxuICAgICAgICAgICAgICBvbkRvbmUoJ0ZlZWRiYWNrIGNhbmNlbGxlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGN1cnNvck9mZnNldD17Y3Vyc29yT2Zmc2V0fVxuICAgICAgICAgICAgb25DaGFuZ2VDdXJzb3JPZmZzZXQ9e3NldEN1cnNvck9mZnNldH1cbiAgICAgICAgICAgIHNob3dDdXJzb3JcbiAgICAgICAgICAvPlxuICAgICAgICAgIHtlcnJvciAmJiAoXG4gICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+e2Vycm9yfTwvVGV4dD5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgRWRpdCBhbmQgcHJlc3MgRW50ZXIgdG8gcmV0cnksIG9yIEVzYyB0byBjYW5jZWxcbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuXG4gICAgICB7c3RlcCA9PT0gJ2NvbnNlbnQnICYmIChcbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPFRleHQ+VGhpcyByZXBvcnQgd2lsbCBpbmNsdWRlOjwvVGV4dD5cbiAgICAgICAgICA8Qm94IG1hcmdpbkxlZnQ9ezJ9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAtIFlvdXIgZmVlZGJhY2sgLyBidWcgZGVzY3JpcHRpb246eycgJ31cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+e2Rlc2NyaXB0aW9ufTwvVGV4dD5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAtIEVudmlyb25tZW50IGluZm86eycgJ31cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAge2Vudi5wbGF0Zm9ybX0sIHtlbnYudGVybWluYWx9LCB2e01BQ1JPLlZFUlNJT059XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIHtlbnZJbmZvLmdpdFN0YXRlICYmIChcbiAgICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgICAgLSBHaXQgcmVwbyBtZXRhZGF0YTp7JyAnfVxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAge2VudkluZm8uZ2l0U3RhdGUuYnJhbmNoTmFtZX1cbiAgICAgICAgICAgICAgICAgIHtlbnZJbmZvLmdpdFN0YXRlLmNvbW1pdEhhc2hcbiAgICAgICAgICAgICAgICAgICAgPyBgLCAke2VudkluZm8uZ2l0U3RhdGUuY29tbWl0SGFzaC5zbGljZSgwLCA3KX1gXG4gICAgICAgICAgICAgICAgICAgIDogJyd9XG4gICAgICAgICAgICAgICAgICB7ZW52SW5mby5naXRTdGF0ZS5yZW1vdGVVcmxcbiAgICAgICAgICAgICAgICAgICAgPyBgIEAgJHtlbnZJbmZvLmdpdFN0YXRlLnJlbW90ZVVybH1gXG4gICAgICAgICAgICAgICAgICAgIDogJyd9XG4gICAgICAgICAgICAgICAgICB7IWVudkluZm8uZ2l0U3RhdGUuaXNIZWFkT25SZW1vdGUgJiYgJywgbm90IHN5bmNlZCd9XG4gICAgICAgICAgICAgICAgICB7IWVudkluZm8uZ2l0U3RhdGUuaXNDbGVhbiAmJiAnLCBoYXMgbG9jYWwgY2hhbmdlcyd9XG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICApfVxuICAgICAgICAgICAgPFRleHQ+LSBDdXJyZW50IHNlc3Npb24gdHJhbnNjcmlwdDwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCB3cmFwPVwid3JhcFwiIGRpbUNvbG9yPlxuICAgICAgICAgICAgICBXZSB3aWxsIHVzZSB5b3VyIGZlZWRiYWNrIHRvIGRlYnVnIHJlbGF0ZWQgaXNzdWVzIG9yIHRvIGltcHJvdmV7JyAnfVxuICAgICAgICAgICAgICBDbGF1ZGUgQ29kZSZhcG9zO3MgZnVuY3Rpb25hbGl0eSAoZWcuIHRvIHJlZHVjZSB0aGUgcmlzayBvZiBidWdzXG4gICAgICAgICAgICAgIG9jY3VycmluZyBpbiB0aGUgZnV0dXJlKS5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgICAgUHJlc3MgPFRleHQgYm9sZD5FbnRlcjwvVGV4dD4gdG8gY29uZmlybSBhbmQgc3VibWl0LlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG5cbiAgICAgIHtzdGVwID09PSAnc3VibWl0dGluZycgJiYgKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9PlxuICAgICAgICAgIDxUZXh0PlN1Ym1pdHRpbmcgcmVwb3J04oCmPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG5cbiAgICAgIHtzdGVwID09PSAnZG9uZScgJiYgKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICB7ZXJyb3IgPyAoXG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+e2Vycm9yfTwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+VGhhbmsgeW91IGZvciB5b3VyIHJlcG9ydCE8L1RleHQ+XG4gICAgICAgICAgKX1cbiAgICAgICAgICB7ZmVlZGJhY2tJZCAmJiA8VGV4dCBkaW1Db2xvcj5GZWVkYmFjayBJRDoge2ZlZWRiYWNrSWR9PC9UZXh0Pn1cbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dD5QcmVzcyA8L1RleHQ+XG4gICAgICAgICAgICA8VGV4dCBib2xkPkVudGVyIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICB0byBvcGVuIHlvdXIgYnJvd3NlciBhbmQgZHJhZnQgYSBHaXRIdWIgaXNzdWUsIG9yIGFueSBvdGhlciBrZXkgdG9cbiAgICAgICAgICAgICAgY2xvc2UuXG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlR2l0SHViSXNzdWVVcmwoXG4gIGZlZWRiYWNrSWQ6IHN0cmluZyxcbiAgdGl0bGU6IHN0cmluZyxcbiAgZGVzY3JpcHRpb246IHN0cmluZyxcbiAgZXJyb3JzOiBBcnJheTx7XG4gICAgZXJyb3I/OiBzdHJpbmdcbiAgICB0aW1lc3RhbXA/OiBzdHJpbmdcbiAgfT4sXG4pOiBzdHJpbmcge1xuICBjb25zdCBzYW5pdGl6ZWRUaXRsZSA9IHJlZGFjdFNlbnNpdGl2ZUluZm8odGl0bGUpXG4gIGNvbnN0IHNhbml0aXplZERlc2NyaXB0aW9uID0gcmVkYWN0U2Vuc2l0aXZlSW5mbyhkZXNjcmlwdGlvbilcblxuICBjb25zdCBib2R5UHJlZml4ID1cbiAgICBgKipCdWcgRGVzY3JpcHRpb24qKlxcbiR7c2FuaXRpemVkRGVzY3JpcHRpb259XFxuXFxuYCArXG4gICAgYCoqRW52aXJvbm1lbnQgSW5mbyoqXFxuYCArXG4gICAgYC0gUGxhdGZvcm06ICR7ZW52LnBsYXRmb3JtfVxcbmAgK1xuICAgIGAtIFRlcm1pbmFsOiAke2Vudi50ZXJtaW5hbH1cXG5gICtcbiAgICBgLSBWZXJzaW9uOiAke01BQ1JPLlZFUlNJT04gfHwgJ3Vua25vd24nfVxcbmAgK1xuICAgIGAtIEZlZWRiYWNrIElEOiAke2ZlZWRiYWNrSWR9XFxuYCArXG4gICAgYFxcbioqRXJyb3JzKipcXG5cXGBcXGBcXGBqc29uXFxuYFxuICBjb25zdCBlcnJvclN1ZmZpeCA9IGBcXG5cXGBcXGBcXGBcXG5gXG4gIGNvbnN0IGVycm9yc0pzb24gPSBqc29uU3RyaW5naWZ5KGVycm9ycylcblxuICBjb25zdCBiYXNlVXJsID0gYCR7R0lUSFVCX0lTU1VFU19SRVBPX1VSTH0vbmV3P3RpdGxlPSR7ZW5jb2RlVVJJQ29tcG9uZW50KHNhbml0aXplZFRpdGxlKX0mbGFiZWxzPXVzZXItcmVwb3J0ZWQsYnVnJmJvZHk9YFxuICBjb25zdCB0cnVuY2F0aW9uTm90ZSA9IGBcXG4qKk5vdGU6KiogQ29udGVudCB3YXMgdHJ1bmNhdGVkLlxcbmBcblxuICBjb25zdCBlbmNvZGVkUHJlZml4ID0gZW5jb2RlVVJJQ29tcG9uZW50KGJvZHlQcmVmaXgpXG4gIGNvbnN0IGVuY29kZWRTdWZmaXggPSBlbmNvZGVVUklDb21wb25lbnQoZXJyb3JTdWZmaXgpXG4gIGNvbnN0IGVuY29kZWROb3RlID0gZW5jb2RlVVJJQ29tcG9uZW50KHRydW5jYXRpb25Ob3RlKVxuICBjb25zdCBlbmNvZGVkRXJyb3JzID0gZW5jb2RlVVJJQ29tcG9uZW50KGVycm9yc0pzb24pXG5cbiAgLy8gQ2FsY3VsYXRlIHNwYWNlIGF2YWlsYWJsZSBmb3IgZXJyb3JzXG4gIGNvbnN0IHNwYWNlRm9yRXJyb3JzID1cbiAgICBHSVRIVUJfVVJMX0xJTUlUIC1cbiAgICBiYXNlVXJsLmxlbmd0aCAtXG4gICAgZW5jb2RlZFByZWZpeC5sZW5ndGggLVxuICAgIGVuY29kZWRTdWZmaXgubGVuZ3RoIC1cbiAgICBlbmNvZGVkTm90ZS5sZW5ndGhcblxuICAvLyBJZiBkZXNjcmlwdGlvbiBhbG9uZSBleGNlZWRzIGxpbWl0LCB0cnVuY2F0ZSBldmVyeXRoaW5nXG4gIGlmIChzcGFjZUZvckVycm9ycyA8PSAwKSB7XG4gICAgY29uc3QgZWxsaXBzaXMgPSBlbmNvZGVVUklDb21wb25lbnQoJ+KApicpXG4gICAgY29uc3QgYnVmZmVyID0gNTAgLy8gRXh0cmEgc2FmZXR5IG1hcmdpblxuICAgIGNvbnN0IG1heEVuY29kZWRMZW5ndGggPVxuICAgICAgR0lUSFVCX1VSTF9MSU1JVCAtXG4gICAgICBiYXNlVXJsLmxlbmd0aCAtXG4gICAgICBlbGxpcHNpcy5sZW5ndGggLVxuICAgICAgZW5jb2RlZE5vdGUubGVuZ3RoIC1cbiAgICAgIGJ1ZmZlclxuICAgIGNvbnN0IGZ1bGxCb2R5ID0gYm9keVByZWZpeCArIGVycm9yc0pzb24gKyBlcnJvclN1ZmZpeFxuICAgIGxldCBlbmNvZGVkRnVsbEJvZHkgPSBlbmNvZGVVUklDb21wb25lbnQoZnVsbEJvZHkpXG5cbiAgICBpZiAoZW5jb2RlZEZ1bGxCb2R5Lmxlbmd0aCA+IG1heEVuY29kZWRMZW5ndGgpIHtcbiAgICAgIGVuY29kZWRGdWxsQm9keSA9IGVuY29kZWRGdWxsQm9keS5zbGljZSgwLCBtYXhFbmNvZGVkTGVuZ3RoKVxuICAgICAgLy8gRG9uJ3QgY3V0IGluIG1pZGRsZSBvZiAlWFggc2VxdWVuY2VcbiAgICAgIGNvbnN0IGxhc3RQZXJjZW50ID0gZW5jb2RlZEZ1bGxCb2R5Lmxhc3RJbmRleE9mKCclJylcbiAgICAgIGlmIChsYXN0UGVyY2VudCA+PSBlbmNvZGVkRnVsbEJvZHkubGVuZ3RoIC0gMikge1xuICAgICAgICBlbmNvZGVkRnVsbEJvZHkgPSBlbmNvZGVkRnVsbEJvZHkuc2xpY2UoMCwgbGFzdFBlcmNlbnQpXG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGJhc2VVcmwgKyBlbmNvZGVkRnVsbEJvZHkgKyBlbGxpcHNpcyArIGVuY29kZWROb3RlXG4gIH1cblxuICAvLyBJZiBlcnJvcnMgZml0LCBubyB0cnVuY2F0aW9uIG5lZWRlZFxuICBpZiAoZW5jb2RlZEVycm9ycy5sZW5ndGggPD0gc3BhY2VGb3JFcnJvcnMpIHtcbiAgICByZXR1cm4gYmFzZVVybCArIGVuY29kZWRQcmVmaXggKyBlbmNvZGVkRXJyb3JzICsgZW5jb2RlZFN1ZmZpeFxuICB9XG5cbiAgLy8gVHJ1bmNhdGUgZXJyb3JzIHRvIGZpdCAocHJpb3JpdGl6ZSBrZWVwaW5nIGRlc2NyaXB0aW9uKVxuICAvLyBTbGljZSBlbmNvZGVkIGVycm9ycyBkaXJlY3RseSwgdGhlbiB0cmltIHRvIGF2b2lkIGN1dHRpbmcgJVhYIHNlcXVlbmNlc1xuICBjb25zdCBlbGxpcHNpcyA9IGVuY29kZVVSSUNvbXBvbmVudCgn4oCmJylcbiAgY29uc3QgYnVmZmVyID0gNTAgLy8gRXh0cmEgc2FmZXR5IG1hcmdpblxuICBsZXQgdHJ1bmNhdGVkRW5jb2RlZEVycm9ycyA9IGVuY29kZWRFcnJvcnMuc2xpY2UoXG4gICAgMCxcbiAgICBzcGFjZUZvckVycm9ycyAtIGVsbGlwc2lzLmxlbmd0aCAtIGJ1ZmZlcixcbiAgKVxuICAvLyBJZiB3ZSBjdXQgaW4gbWlkZGxlIG9mICVYWCwgYmFjayB1cCB0byBiZWZvcmUgdGhlICVcbiAgY29uc3QgbGFzdFBlcmNlbnQgPSB0cnVuY2F0ZWRFbmNvZGVkRXJyb3JzLmxhc3RJbmRleE9mKCclJylcbiAgaWYgKGxhc3RQZXJjZW50ID49IHRydW5jYXRlZEVuY29kZWRFcnJvcnMubGVuZ3RoIC0gMikge1xuICAgIHRydW5jYXRlZEVuY29kZWRFcnJvcnMgPSB0cnVuY2F0ZWRFbmNvZGVkRXJyb3JzLnNsaWNlKDAsIGxhc3RQZXJjZW50KVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICBiYXNlVXJsICtcbiAgICBlbmNvZGVkUHJlZml4ICtcbiAgICB0cnVuY2F0ZWRFbmNvZGVkRXJyb3JzICtcbiAgICBlbGxpcHNpcyArXG4gICAgZW5jb2RlZFN1ZmZpeCArXG4gICAgZW5jb2RlZE5vdGVcbiAgKVxufVxuXG5hc3luYyBmdW5jdGlvbiBnZW5lcmF0ZVRpdGxlKFxuICBkZXNjcmlwdGlvbjogc3RyaW5nLFxuICBhYm9ydFNpZ25hbDogQWJvcnRTaWduYWwsXG4pOiBQcm9taXNlPHN0cmluZz4ge1xuICB0cnkge1xuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgcXVlcnlIYWlrdSh7XG4gICAgICBzeXN0ZW1Qcm9tcHQ6IGFzU3lzdGVtUHJvbXB0KFtcbiAgICAgICAgJ0dlbmVyYXRlIGEgY29uY2lzZSwgdGVjaG5pY2FsIGlzc3VlIHRpdGxlIChtYXggODAgY2hhcnMpIGZvciBhIHB1YmxpYyBHaXRIdWIgaXNzdWUgYmFzZWQgb24gdGhpcyBidWcgcmVwb3J0IGZvciBDbGF1ZGUgQ29kZS4nLFxuICAgICAgICAnQ2xhdWRlIENvZGUgaXMgYW4gYWdlbnRpYyBjb2RpbmcgQ0xJIGJhc2VkIG9uIHRoZSBBbnRocm9waWMgQVBJLicsXG4gICAgICAgICdUaGUgdGl0bGUgc2hvdWxkOicsXG4gICAgICAgICctIEluY2x1ZGUgdGhlIHR5cGUgb2YgaXNzdWUgW0J1Z10gb3IgW0ZlYXR1cmUgUmVxdWVzdF0gYXMgdGhlIGZpcnN0IHRoaW5nIGluIHRoZSB0aXRsZScsXG4gICAgICAgICctIEJlIGNvbmNpc2UsIHNwZWNpZmljIGFuZCBkZXNjcmlwdGl2ZSBvZiB0aGUgYWN0dWFsIHByb2JsZW0nLFxuICAgICAgICAnLSBVc2UgdGVjaG5pY2FsIHRlcm1pbm9sb2d5IGFwcHJvcHJpYXRlIGZvciBhIHNvZnR3YXJlIGlzc3VlJyxcbiAgICAgICAgJy0gRm9yIGVycm9yIG1lc3NhZ2VzLCBleHRyYWN0IHRoZSBrZXkgZXJyb3IgKGUuZy4sIFwiTWlzc2luZyBUb29sIFJlc3VsdCBCbG9ja1wiIHJhdGhlciB0aGFuIHRoZSBmdWxsIG1lc3NhZ2UpJyxcbiAgICAgICAgJy0gQmUgZGlyZWN0IGFuZCBjbGVhciBmb3IgZGV2ZWxvcGVycyB0byB1bmRlcnN0YW5kIHRoZSBwcm9ibGVtJyxcbiAgICAgICAgJy0gSWYgeW91IGNhbm5vdCBkZXRlcm1pbmUgYSBjbGVhciBpc3N1ZSwgdXNlIFwiQnVnIFJlcG9ydDogW2JyaWVmIGRlc2NyaXB0aW9uXVwiJyxcbiAgICAgICAgJy0gQW55IExMTSBBUEkgZXJyb3JzIGFyZSBmcm9tIHRoZSBBbnRocm9waWMgQVBJLCBub3QgZnJvbSBhbnkgb3RoZXIgbW9kZWwgcHJvdmlkZXInLFxuICAgICAgICAnWW91ciByZXNwb25zZSB3aWxsIGJlIGRpcmVjdGx5IHVzZWQgYXMgdGhlIHRpdGxlIG9mIHRoZSBHaXRodWIgaXNzdWUsIGFuZCBhcyBzdWNoIHNob3VsZCBub3QgY29udGFpbiBhbnkgb3RoZXIgY29tbWVudGFyeSBvciBleHBsYWluYXRpb24nLFxuICAgICAgICAnRXhhbXBsZXMgb2YgZ29vZCB0aXRsZXMgaW5jbHVkZTogXCJbQnVnXSBBdXRvLUNvbXBhY3QgdHJpZ2dlcnMgdG8gc29vblwiLCBcIltCdWddIEFudGhyb3BpYyBBUEkgRXJyb3I6IE1pc3NpbmcgVG9vbCBSZXN1bHQgQmxvY2tcIiwgXCJbQnVnXSBFcnJvcjogSW52YWxpZCBNb2RlbCBOYW1lIGZvciBPcHVzXCInLFxuICAgICAgXSksXG4gICAgICB1c2VyUHJvbXB0OiBkZXNjcmlwdGlvbixcbiAgICAgIHNpZ25hbDogYWJvcnRTaWduYWwsXG4gICAgICBvcHRpb25zOiB7XG4gICAgICAgIGhhc0FwcGVuZFN5c3RlbVByb21wdDogZmFsc2UsXG4gICAgICAgIHRvb2xDaG9pY2U6IHVuZGVmaW5lZCxcbiAgICAgICAgaXNOb25JbnRlcmFjdGl2ZVNlc3Npb246IGZhbHNlLFxuICAgICAgICBhZ2VudHM6IFtdLFxuICAgICAgICBxdWVyeVNvdXJjZTogJ2ZlZWRiYWNrJyxcbiAgICAgICAgbWNwVG9vbHM6IFtdLFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgY29uc3QgdGl0bGUgPVxuICAgICAgcmVzcG9uc2UubWVzc2FnZS5jb250ZW50WzBdPy50eXBlID09PSAndGV4dCdcbiAgICAgICAgPyByZXNwb25zZS5tZXNzYWdlLmNvbnRlbnRbMF0udGV4dFxuICAgICAgICA6ICdCdWcgUmVwb3J0J1xuXG4gICAgLy8gQ2hlY2sgaWYgdGhlIHRpdGxlIGNvbnRhaW5zIGFuIEFQSSBlcnJvciBtZXNzYWdlXG4gICAgaWYgKHN0YXJ0c1dpdGhBcGlFcnJvclByZWZpeCh0aXRsZSkpIHtcbiAgICAgIHJldHVybiBjcmVhdGVGYWxsYmFja1RpdGxlKGRlc2NyaXB0aW9uKVxuICAgIH1cblxuICAgIHJldHVybiB0aXRsZVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIC8vIElmIHRoZXJlJ3MgYW55IGVycm9yIGluIHRpdGxlIGdlbmVyYXRpb24sIHVzZSBhIGZhbGxiYWNrIHRpdGxlXG4gICAgbG9nRXJyb3IoZXJyb3IpXG4gICAgcmV0dXJuIGNyZWF0ZUZhbGxiYWNrVGl0bGUoZGVzY3JpcHRpb24pXG4gIH1cbn1cblxuZnVuY3Rpb24gY3JlYXRlRmFsbGJhY2tUaXRsZShkZXNjcmlwdGlvbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgLy8gQ3JlYXRlIGEgc2FmZSBmYWxsYmFjayB0aXRsZSBiYXNlZCBvbiB0aGUgYnVnIGRlc2NyaXB0aW9uXG5cbiAgLy8gVHJ5IHRvIGV4dHJhY3QgYSBtZWFuaW5nZnVsIHRpdGxlIGZyb20gdGhlIGZpcnN0IGxpbmVcbiAgY29uc3QgZmlyc3RMaW5lID0gZGVzY3JpcHRpb24uc3BsaXQoJ1xcbicpWzBdIHx8ICcnXG5cbiAgLy8gSWYgdGhlIGZpcnN0IGxpbmUgaXMgdmVyeSBzaG9ydCwgdXNlIGl0IGRpcmVjdGx5XG4gIGlmIChmaXJzdExpbmUubGVuZ3RoIDw9IDYwICYmIGZpcnN0TGluZS5sZW5ndGggPiA1KSB7XG4gICAgcmV0dXJuIGZpcnN0TGluZVxuICB9XG5cbiAgLy8gRm9yIGxvbmdlciBkZXNjcmlwdGlvbnMsIGNyZWF0ZSBhIHRydW5jYXRlZCB2ZXJzaW9uXG4gIC8vIFRydW5jYXRlIGF0IHdvcmQgYm91bmRhcmllcyB3aGVuIHBvc3NpYmxlXG4gIGxldCB0cnVuY2F0ZWQgPSBmaXJzdExpbmUuc2xpY2UoMCwgNjApXG4gIGlmIChmaXJzdExpbmUubGVuZ3RoID4gNjApIHtcbiAgICAvLyBGaW5kIHRoZSBsYXN0IHNwYWNlIGJlZm9yZSB0aGUgNjAgY2hhciBsaW1pdFxuICAgIGNvbnN0IGxhc3RTcGFjZSA9IHRydW5jYXRlZC5sYXN0SW5kZXhPZignICcpXG4gICAgaWYgKGxhc3RTcGFjZSA+IDMwKSB7XG4gICAgICAvLyBPbmx5IHRyaW0gYXQgd29yZCBpZiB3ZSdyZSBub3QgY3V0dGluZyB0b28gbXVjaFxuICAgICAgdHJ1bmNhdGVkID0gdHJ1bmNhdGVkLnNsaWNlKDAsIGxhc3RTcGFjZSlcbiAgICB9XG4gICAgdHJ1bmNhdGVkICs9ICcuLi4nXG4gIH1cblxuICByZXR1cm4gdHJ1bmNhdGVkLmxlbmd0aCA8IDEwID8gJ0J1ZyBSZXBvcnQnIDogdHJ1bmNhdGVkXG59XG5cbi8vIEhlbHBlciBmdW5jdGlvbiB0byBzYW5pdGl6ZSBhbmQgbG9nIGVycm9ycyB3aXRob3V0IGV4cG9zaW5nIEFQSSBrZXlzXG5mdW5jdGlvbiBzYW5pdGl6ZUFuZExvZ0Vycm9yKGVycjogdW5rbm93bik6IHZvaWQge1xuICBpZiAoZXJyIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAvLyBDcmVhdGUgYSBjb3B5IHdpdGggcG90ZW50aWFsbHkgc2Vuc2l0aXZlIGluZm8gcmVkYWN0ZWRcbiAgICBjb25zdCBzYWZlRXJyb3IgPSBuZXcgRXJyb3IocmVkYWN0U2Vuc2l0aXZlSW5mbyhlcnIubWVzc2FnZSkpXG5cbiAgICAvLyBBbHNvIHJlZGFjdCB0aGUgc3RhY2sgdHJhY2UgaWYgcHJlc2VudFxuICAgIGlmIChlcnIuc3RhY2spIHtcbiAgICAgIHNhZmVFcnJvci5zdGFjayA9IHJlZGFjdFNlbnNpdGl2ZUluZm8oZXJyLnN0YWNrKVxuICAgIH1cblxuICAgIGxvZ0Vycm9yKHNhZmVFcnJvcilcbiAgfSBlbHNlIHtcbiAgICAvLyBGb3Igbm9uLUVycm9yIG9iamVjdHMsIGNvbnZlcnQgdG8gc3RyaW5nIGFuZCByZWRhY3Qgc2Vuc2l0aXZlIGluZm9cbiAgICBjb25zdCBlcnJvclN0cmluZyA9IHJlZGFjdFNlbnNpdGl2ZUluZm8oU3RyaW5nKGVycikpXG4gICAgbG9nRXJyb3IobmV3IEVycm9yKGVycm9yU3RyaW5nKSlcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBzdWJtaXRGZWVkYmFjayhcbiAgZGF0YTogRmVlZGJhY2tEYXRhLFxuICBzaWduYWw/OiBBYm9ydFNpZ25hbCxcbik6IFByb21pc2U8eyBzdWNjZXNzOiBib29sZWFuOyBmZWVkYmFja0lkPzogc3RyaW5nOyBpc1pkck9yZz86IGJvb2xlYW4gfT4ge1xuICBpZiAoaXNFc3NlbnRpYWxUcmFmZmljT25seSgpKSB7XG4gICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UgfVxuICB9XG5cbiAgdHJ5IHtcbiAgICAvLyBFbnN1cmUgT0F1dGggdG9rZW4gaXMgZnJlc2ggYmVmb3JlIGdldHRpbmcgYXV0aCBoZWFkZXJzXG4gICAgLy8gVGhpcyBwcmV2ZW50cyA0MDEgZXJyb3JzIGZyb20gc3RhbGUgY2FjaGVkIHRva2Vuc1xuICAgIGF3YWl0IGNoZWNrQW5kUmVmcmVzaE9BdXRoVG9rZW5JZk5lZWRlZCgpXG5cbiAgICBjb25zdCBhdXRoUmVzdWx0ID0gZ2V0QXV0aEhlYWRlcnMoKVxuICAgIGlmIChhdXRoUmVzdWx0LmVycm9yKSB7XG4gICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSB9XG4gICAgfVxuXG4gICAgY29uc3QgaGVhZGVyczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXG4gICAgICAnVXNlci1BZ2VudCc6IGdldFVzZXJBZ2VudCgpLFxuICAgICAgLi4uYXV0aFJlc3VsdC5oZWFkZXJzLFxuICAgIH1cblxuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgYXhpb3MucG9zdChcbiAgICAgICdodHRwczovL2FwaS5hbnRocm9waWMuY29tL2FwaS9jbGF1ZGVfY2xpX2ZlZWRiYWNrJyxcbiAgICAgIHtcbiAgICAgICAgY29udGVudDoganNvblN0cmluZ2lmeShkYXRhKSxcbiAgICAgIH0sXG4gICAgICB7XG4gICAgICAgIGhlYWRlcnMsXG4gICAgICAgIHRpbWVvdXQ6IDMwMDAwLCAvLyAzMCBzZWNvbmQgdGltZW91dCB0byBwcmV2ZW50IGhhbmdpbmdcbiAgICAgICAgc2lnbmFsLFxuICAgICAgfSxcbiAgICApXG5cbiAgICBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSAyMDApIHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IHJlc3BvbnNlLmRhdGFcbiAgICAgIGlmIChyZXN1bHQ/LmZlZWRiYWNrX2lkKSB7XG4gICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IHRydWUsIGZlZWRiYWNrSWQ6IHJlc3VsdC5mZWVkYmFja19pZCB9XG4gICAgICB9XG4gICAgICBzYW5pdGl6ZUFuZExvZ0Vycm9yKFxuICAgICAgICBuZXcgRXJyb3IoXG4gICAgICAgICAgJ0ZhaWxlZCB0byBzdWJtaXQgZmVlZGJhY2s6IHJlcXVlc3QgZGlkIG5vdCByZXR1cm4gZmVlZGJhY2tfaWQnLFxuICAgICAgICApLFxuICAgICAgKVxuICAgICAgcmV0dXJuIHsgc3VjY2VzczogZmFsc2UgfVxuICAgIH1cblxuICAgIHNhbml0aXplQW5kTG9nRXJyb3IoXG4gICAgICBuZXcgRXJyb3IoJ0ZhaWxlZCB0byBzdWJtaXQgZmVlZGJhY2s6JyArIHJlc3BvbnNlLnN0YXR1cyksXG4gICAgKVxuICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlIH1cbiAgfSBjYXRjaCAoZXJyKSB7XG4gICAgLy8gSGFuZGxlIGNhbmNlbGxhdGlvbi9hYm9ydCAtIGRvbid0IGxvZyBhcyBlcnJvclxuICAgIGlmIChheGlvcy5pc0NhbmNlbChlcnIpKSB7XG4gICAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSB9XG4gICAgfVxuXG4gICAgaWYgKGF4aW9zLmlzQXhpb3NFcnJvcihlcnIpICYmIGVyci5yZXNwb25zZT8uc3RhdHVzID09PSA0MDMpIHtcbiAgICAgIGNvbnN0IGVycm9yRGF0YSA9IGVyci5yZXNwb25zZS5kYXRhXG4gICAgICBpZiAoXG4gICAgICAgIGVycm9yRGF0YT8uZXJyb3I/LnR5cGUgPT09ICdwZXJtaXNzaW9uX2Vycm9yJyAmJlxuICAgICAgICBlcnJvckRhdGE/LmVycm9yPy5tZXNzYWdlPy5pbmNsdWRlcygnQ3VzdG9tIGRhdGEgcmV0ZW50aW9uIHNldHRpbmdzJylcbiAgICAgICkge1xuICAgICAgICBzYW5pdGl6ZUFuZExvZ0Vycm9yKFxuICAgICAgICAgIG5ldyBFcnJvcihcbiAgICAgICAgICAgICdDYW5ub3Qgc3VibWl0IGZlZWRiYWNrIGJlY2F1c2UgY3VzdG9tIGRhdGEgcmV0ZW50aW9uIHNldHRpbmdzIGFyZSBlbmFibGVkJyxcbiAgICAgICAgICApLFxuICAgICAgICApXG4gICAgICAgIHJldHVybiB7IHN1Y2Nlc3M6IGZhbHNlLCBpc1pkck9yZzogdHJ1ZSB9XG4gICAgICB9XG4gICAgfVxuICAgIC8vIFVzZSBvdXIgc2FmZSBlcnJvciBsb2dnaW5nIGZ1bmN0aW9uIHRvIGF2b2lkIGxlYWtpbmcgQVBJIGtleXNcbiAgICBzYW5pdGl6ZUFuZExvZ0Vycm9yKGVycilcbiAgICByZXR1cm4geyBzdWNjZXNzOiBmYWxzZSB9XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsUUFBUSxFQUFFQyxJQUFJLFFBQVEsYUFBYTtBQUM1QyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUN4RCxTQUFTQyxpQkFBaUIsUUFBUSx3QkFBd0I7QUFDMUQsU0FBU0MsWUFBWSxRQUFRLGlEQUFpRDtBQUM5RSxTQUNFLEtBQUtDLDBEQUEwRCxFQUMvREMsUUFBUSxRQUNILGlDQUFpQztBQUN4QyxTQUNFQyx1QkFBdUIsRUFDdkJDLHVCQUF1QixRQUNsQix1QkFBdUI7QUFDOUIsY0FBY0Msb0JBQW9CLFFBQVEsZ0JBQWdCO0FBQzFELFNBQVNDLGVBQWUsUUFBUSw2QkFBNkI7QUFDN0QsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxXQUFXO0FBQy9DLFNBQVNDLGFBQWEsUUFBUSxp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 \ No newline at end of file diff --git a/components/FeedbackSurvey/FeedbackSurvey.tsx b/components/FeedbackSurvey/FeedbackSurvey.tsx new file mode 100644 index 0000000..dc3e712 --- /dev/null +++ b/components/FeedbackSurvey/FeedbackSurvey.tsx @@ -0,0 +1,174 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { Box, Text } from '../../ink.js'; +import { FeedbackSurveyView, isValidResponseInput } from './FeedbackSurveyView.js'; +import type { TranscriptShareResponse } from './TranscriptSharePrompt.js'; +import { TranscriptSharePrompt } from './TranscriptSharePrompt.js'; +import { useDebouncedDigitInput } from './useDebouncedDigitInput.js'; +import type { FeedbackSurveyResponse } from './utils.js'; +type Props = { + state: 'closed' | 'open' | 'thanks' | 'transcript_prompt' | 'submitting' | 'submitted'; + lastResponse: FeedbackSurveyResponse | null; + handleSelect: (selected: FeedbackSurveyResponse) => void; + handleTranscriptSelect?: (selected: TranscriptShareResponse) => void; + inputValue: string; + setInputValue: (value: string) => void; + onRequestFeedback?: () => void; + message?: string; +}; +export function FeedbackSurvey(t0) { + const $ = _c(16); + const { + state, + lastResponse, + handleSelect, + handleTranscriptSelect, + inputValue, + setInputValue, + onRequestFeedback, + message + } = t0; + if (state === "closed") { + return null; + } + if (state === "thanks") { + let t1; + if ($[0] !== inputValue || $[1] !== lastResponse || $[2] !== onRequestFeedback || $[3] !== setInputValue) { + t1 = ; + $[0] = inputValue; + $[1] = lastResponse; + $[2] = onRequestFeedback; + $[3] = setInputValue; + $[4] = t1; + } else { + t1 = $[4]; + } + return t1; + } + if (state === "submitted") { + let t1; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {"\u2713"} Thanks for sharing your transcript!; + $[5] = t1; + } else { + t1 = $[5]; + } + return t1; + } + if (state === "submitting") { + let t1; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Sharing transcript{"\u2026"}; + $[6] = t1; + } else { + t1 = $[6]; + } + return t1; + } + if (state === "transcript_prompt") { + if (!handleTranscriptSelect) { + return null; + } + if (inputValue && !["1", "2", "3"].includes(inputValue)) { + return null; + } + let t1; + if ($[7] !== handleTranscriptSelect || $[8] !== inputValue || $[9] !== setInputValue) { + t1 = ; + $[7] = handleTranscriptSelect; + $[8] = inputValue; + $[9] = setInputValue; + $[10] = t1; + } else { + t1 = $[10]; + } + return t1; + } + if (inputValue && !isValidResponseInput(inputValue)) { + return null; + } + let t1; + if ($[11] !== handleSelect || $[12] !== inputValue || $[13] !== message || $[14] !== setInputValue) { + t1 = ; + $[11] = handleSelect; + $[12] = inputValue; + $[13] = message; + $[14] = setInputValue; + $[15] = t1; + } else { + t1 = $[15]; + } + return t1; +} +type ThanksProps = { + lastResponse: FeedbackSurveyResponse | null; + inputValue: string; + setInputValue: (value: string) => void; + onRequestFeedback?: () => void; +}; +const isFollowUpDigit = (char: string): char is '1' => char === '1'; +function FeedbackSurveyThanks(t0) { + const $ = _c(12); + const { + lastResponse, + inputValue, + setInputValue, + onRequestFeedback + } = t0; + const showFollowUp = onRequestFeedback && lastResponse === "good"; + const t1 = Boolean(showFollowUp); + let t2; + if ($[0] !== lastResponse || $[1] !== onRequestFeedback) { + t2 = () => { + logEvent("tengu_feedback_survey_event", { + event_type: "followup_accepted" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + response: lastResponse as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + onRequestFeedback?.(); + }; + $[0] = lastResponse; + $[1] = onRequestFeedback; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] !== inputValue || $[4] !== setInputValue || $[5] !== t1 || $[6] !== t2) { + t3 = { + inputValue, + setInputValue, + isValidDigit: isFollowUpDigit, + enabled: t1, + once: true, + onDigit: t2 + }; + $[3] = inputValue; + $[4] = setInputValue; + $[5] = t1; + $[6] = t2; + $[7] = t3; + } else { + t3 = $[7]; + } + useDebouncedDigitInput(t3); + const feedbackCommand = false ? "/issue" : "/feedback"; + let t4; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t4 = Thanks for the feedback!; + $[8] = t4; + } else { + t4 = $[8]; + } + let t5; + if ($[9] !== lastResponse || $[10] !== showFollowUp) { + t5 = {t4}{showFollowUp ? (Optional) Press [1] to tell us what went well {" \xB7 "}{feedbackCommand} : lastResponse === "bad" ? Use /issue to report model behavior issues. : Use {feedbackCommand} to share detailed feedback anytime.}; + $[9] = lastResponse; + $[10] = showFollowUp; + $[11] = t5; + } else { + t5 = $[11]; + } + return t5; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsIkJveCIsIlRleHQiLCJGZWVkYmFja1N1cnZleVZpZXciLCJpc1ZhbGlkUmVzcG9uc2VJbnB1dCIsIlRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlIiwiVHJhbnNjcmlwdFNoYXJlUHJvbXB0IiwidXNlRGVib3VuY2VkRGlnaXRJbnB1dCIsIkZlZWRiYWNrU3VydmV5UmVzcG9uc2UiLCJQcm9wcyIsInN0YXRlIiwibGFzdFJlc3BvbnNlIiwiaGFuZGxlU2VsZWN0Iiwic2VsZWN0ZWQiLCJoYW5kbGVUcmFuc2NyaXB0U2VsZWN0IiwiaW5wdXRWYWx1ZSIsInNldElucHV0VmFsdWUiLCJ2YWx1ZSIsIm9uUmVxdWVzdEZlZWRiYWNrIiwibWVzc2FnZSIsIkZlZWRiYWNrU3VydmV5IiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsImluY2x1ZGVzIiwiVGhhbmtzUHJvcHMiLCJpc0ZvbGxvd1VwRGlnaXQiLCJjaGFyIiwiRmVlZGJhY2tTdXJ2ZXlUaGFua3MiLCJzaG93Rm9sbG93VXAiLCJCb29sZWFuIiwidDIiLCJldmVudF90eXBlIiwicmVzcG9uc2UiLCJ0MyIsImlzVmFsaWREaWdpdCIsImVuYWJsZWQiLCJvbmNlIiwib25EaWdpdCIsImZlZWRiYWNrQ29tbWFuZCIsInQ0IiwidDUiXSwic291cmNlcyI6WyJGZWVkYmFja1N1cnZleS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7XG4gIEZlZWRiYWNrU3VydmV5VmlldyxcbiAgaXNWYWxpZFJlc3BvbnNlSW5wdXQsXG59IGZyb20gJy4vRmVlZGJhY2tTdXJ2ZXlWaWV3LmpzJ1xuaW1wb3J0IHR5cGUgeyBUcmFuc2NyaXB0U2hhcmVSZXNwb25zZSB9IGZyb20gJy4vVHJhbnNjcmlwdFNoYXJlUHJvbXB0LmpzJ1xuaW1wb3J0IHsgVHJhbnNjcmlwdFNoYXJlUHJvbXB0IH0gZnJvbSAnLi9UcmFuc2NyaXB0U2hhcmVQcm9tcHQuanMnXG5pbXBvcnQgeyB1c2VEZWJvdW5jZWREaWdpdElucHV0IH0gZnJvbSAnLi91c2VEZWJvdW5jZWREaWdpdElucHV0LmpzJ1xuaW1wb3J0IHR5cGUgeyBGZWVkYmFja1N1cnZleVJlc3BvbnNlIH0gZnJvbSAnLi91dGlscy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgc3RhdGU6XG4gICAgfCAnY2xvc2VkJ1xuICAgIHwgJ29wZW4nXG4gICAgfCAndGhhbmtzJ1xuICAgIHwgJ3RyYW5zY3JpcHRfcHJvbXB0J1xuICAgIHwgJ3N1Ym1pdHRpbmcnXG4gICAgfCAnc3VibWl0dGVkJ1xuICBsYXN0UmVzcG9uc2U6IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UgfCBudWxsXG4gIGhhbmRsZVNlbGVjdDogKHNlbGVjdGVkOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlKSA9PiB2b2lkXG4gIGhhbmRsZVRyYW5zY3JpcHRTZWxlY3Q/OiAoc2VsZWN0ZWQ6IFRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlKSA9PiB2b2lkXG4gIGlucHV0VmFsdWU6IHN0cmluZ1xuICBzZXRJbnB1dFZhbHVlOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZFxuICBvblJlcXVlc3RGZWVkYmFjaz86ICgpID0+IHZvaWRcbiAgbWVzc2FnZT86IHN0cmluZ1xufVxuXG5leHBvcnQgZnVuY3Rpb24gRmVlZGJhY2tTdXJ2ZXkoe1xuICBzdGF0ZSxcbiAgbGFzdFJlc3BvbnNlLFxuICBoYW5kbGVTZWxlY3QsXG4gIGhhbmRsZVRyYW5zY3JpcHRTZWxlY3QsXG4gIGlucHV0VmFsdWUsXG4gIHNldElucHV0VmFsdWUsXG4gIG9uUmVxdWVzdEZlZWRiYWNrLFxuICBtZXNzYWdlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoc3RhdGUgPT09ICdjbG9zZWQnKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGlmIChzdGF0ZSA9PT0gJ3RoYW5rcycpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEZlZWRiYWNrU3VydmV5VGhhbmtzXG4gICAgICAgIGxhc3RSZXNwb25zZT17bGFzdFJlc3BvbnNlfVxuICAgICAgICBpbnB1dFZhbHVlPXtpbnB1dFZhbHVlfVxuICAgICAgICBzZXRJbnB1dFZhbHVlPXtzZXRJbnB1dFZhbHVlfVxuICAgICAgICBvblJlcXVlc3RGZWVkYmFjaz17b25SZXF1ZXN0RmVlZGJhY2t9XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIGlmIChzdGF0ZSA9PT0gJ3N1Ym1pdHRlZCcpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj5cbiAgICAgICAgICB7J1xcdTI3MTMnfSBUaGFua3MgZm9yIHNoYXJpbmcgeW91ciB0cmFuc2NyaXB0IVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBpZiAoc3RhdGUgPT09ICdzdWJtaXR0aW5nJykge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlNoYXJpbmcgdHJhbnNjcmlwdHsnXFx1MjAyNid9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgaWYgKHN0YXRlID09PSAndHJhbnNjcmlwdF9wcm9tcHQnKSB7XG4gICAgaWYgKCFoYW5kbGVUcmFuc2NyaXB0U2VsZWN0KSB7XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cbiAgICAvLyBIaWRlIHByb21wdCBpZiB1c2VyIGlzIHR5cGluZyBub24tcmVzcG9uc2UgY2hhcmFjdGVyc1xuICAgIGlmIChpbnB1dFZhbHVlICYmICFbJzEnLCAnMicsICczJ10uaW5jbHVkZXMoaW5wdXRWYWx1ZSkpIHtcbiAgICAgIHJldHVybiBudWxsXG4gICAgfVxuICAgIHJldHVybiAoXG4gICAgICA8VHJhbnNjcmlwdFNoYXJlUHJvbXB0XG4gICAgICAgIG9uU2VsZWN0PXtoYW5kbGVUcmFuc2NyaXB0U2VsZWN0fVxuICAgICAgICBpbnB1dFZhbHVlPXtpbnB1dFZhbHVlfVxuICAgICAgICBzZXRJbnB1dFZhbHVlPXtzZXRJbnB1dFZhbHVlfVxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICAvLyBzdGF0ZSA9PT0gJ29wZW4nXG4gIC8vIEhpZGUgdGhlIHN1cnZleSBpZiB0aGUgdXNlciBpcyB0eXBpbmcgYW55dGhpbmcgb3RoZXIgdGhhbiBhIHN1cnZleSByZXNwb25zZS5cbiAgLy8gVGhpcyBwcmV2ZW50cyB0aGUgc3VydmV5IGZyb20gc2hvd2luZyB1cCB3aGVuIHRoZSB1c2VyIGlzIHR5cGluZyBhIG1lc3NhZ2UsXG4gIC8vIHdoaWNoIGNhbiByZXN1bHQgaW4gYWNjaWRlbnRhbCBzdXJ2ZXkgc3VibWlzc2lvbnMgKGUuZy4gXCJzM2NtZFwiKS5cbiAgaWYgKGlucHV0VmFsdWUgJiYgIWlzVmFsaWRSZXNwb25zZUlucHV0KGlucHV0VmFsdWUpKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEZlZWRiYWNrU3VydmV5Vmlld1xuICAgICAgb25TZWxlY3Q9e2hhbmRsZVNlbGVjdH1cbiAgICAgIGlucHV0VmFsdWU9e2lucHV0VmFsdWV9XG4gICAgICBzZXRJbnB1dFZhbHVlPXtzZXRJbnB1dFZhbHVlfVxuICAgICAgbWVzc2FnZT17bWVzc2FnZX1cbiAgICAvPlxuICApXG59XG5cbnR5cGUgVGhhbmtzUHJvcHMgPSB7XG4gIGxhc3RSZXNwb25zZTogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSB8IG51bGxcbiAgaW5wdXRWYWx1ZTogc3RyaW5nXG4gIHNldElucHV0VmFsdWU6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG4gIG9uUmVxdWVzdEZlZWRiYWNrPzogKCkgPT4gdm9pZFxufVxuXG5jb25zdCBpc0ZvbGxvd1VwRGlnaXQgPSAoY2hhcjogc3RyaW5nKTogY2hhciBpcyAnMScgPT4gY2hhciA9PT0gJzEnXG5cbmZ1bmN0aW9uIEZlZWRiYWNrU3VydmV5VGhhbmtzKHtcbiAgbGFzdFJlc3BvbnNlLFxuICBpbnB1dFZhbHVlLFxuICBzZXRJbnB1dFZhbHVlLFxuICBvblJlcXVlc3RGZWVkYmFjayxcbn06IFRoYW5rc1Byb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3Qgc2hvd0ZvbGxvd1VwID0gb25SZXF1ZXN0RmVlZGJhY2sgJiYgbGFzdFJlc3BvbnNlID09PSAnZ29vZCdcblxuICAvLyBMaXN0ZW4gZm9yIFwiMVwiIGtleXByZXNzIHRvIGxhdW5jaCAvZmVlZGJhY2tcbiAgdXNlRGVib3VuY2VkRGlnaXRJbnB1dCh7XG4gICAgaW5wdXRWYWx1ZSxcbiAgICBzZXRJbnB1dFZhbHVlLFxuICAgIGlzVmFsaWREaWdpdDogaXNGb2xsb3dVcERpZ2l0LFxuICAgIGVuYWJsZWQ6IEJvb2xlYW4oc2hvd0ZvbGxvd1VwKSxcbiAgICBvbmNlOiB0cnVlLFxuICAgIG9uRGlnaXQ6ICgpID0+IHtcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9mZWVkYmFja19zdXJ2ZXlfZXZlbnQnLCB7XG4gICAgICAgIGV2ZW50X3R5cGU6XG4gICAgICAgICAgJ2ZvbGxvd3VwX2FjY2VwdGVkJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICByZXNwb25zZTpcbiAgICAgICAgICBsYXN0UmVzcG9uc2UgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgIH0pXG4gICAgICBvblJlcXVlc3RGZWVkYmFjaz8uKClcbiAgICB9LFxuICB9KVxuXG4gIGNvbnN0IGZlZWRiYWNrQ29tbWFuZCA9XG4gICAgXCJleHRlcm5hbFwiID09PSAnYW50JyA/ICcvaXNzdWUnIDogJy9mZWVkYmFjaydcblxuICByZXR1cm4gKFxuICAgIDxCb3ggbWFyZ2luVG9wPXsxfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj5UaGFua3MgZm9yIHRoZSBmZWVkYmFjayE8L1RleHQ+XG4gICAgICB7c2hvd0ZvbGxvd1VwID8gKFxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAoT3B0aW9uYWwpIFByZXNzIFs8VGV4dCBjb2xvcj1cImFuc2k6Y3lhblwiPjE8L1RleHQ+XSB0byB0ZWxsIHVzIHdoYXRcbiAgICAgICAgICB3ZW50IHdlbGwgeycgXFx1MDBiNyAnfVxuICAgICAgICAgIHtmZWVkYmFja0NvbW1hbmR9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICkgOiBsYXN0UmVzcG9uc2UgPT09ICdiYWQnID8gKFxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5Vc2UgL2lzc3VlIHRvIHJlcG9ydCBtb2RlbCBiZWhhdmlvciBpc3N1ZXMuPC9UZXh0PlxuICAgICAgKSA6IChcbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgVXNlIHtmZWVkYmFja0NvbW1hbmR9IHRvIHNoYXJlIGRldGFpbGVkIGZlZWRiYWNrIGFueXRpbWUuXG4gICAgICAgIDwvVGV4dD5cbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQ0UsS0FBS0MsMERBQTBELEVBQy9EQyxRQUFRLFFBQ0gsaUNBQWlDO0FBQ3hDLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsU0FDRUMsa0JB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 \ No newline at end of file diff --git a/components/FeedbackSurvey/FeedbackSurveyView.tsx b/components/FeedbackSurvey/FeedbackSurveyView.tsx new file mode 100644 index 0000000..a2f3757 --- /dev/null +++ b/components/FeedbackSurvey/FeedbackSurveyView.tsx @@ -0,0 +1,108 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, Text } from '../../ink.js'; +import { useDebouncedDigitInput } from './useDebouncedDigitInput.js'; +import type { FeedbackSurveyResponse } from './utils.js'; +type Props = { + onSelect: (option: FeedbackSurveyResponse) => void; + inputValue: string; + setInputValue: (value: string) => void; + message?: string; +}; +const RESPONSE_INPUTS = ['0', '1', '2', '3'] as const; +type ResponseInput = (typeof RESPONSE_INPUTS)[number]; +const inputToResponse: Record = { + '0': 'dismissed', + '1': 'bad', + '2': 'fine', + '3': 'good' +} as const; +export const isValidResponseInput = (input: string): input is ResponseInput => (RESPONSE_INPUTS as readonly string[]).includes(input); +const DEFAULT_MESSAGE = 'How is Claude doing this session? (optional)'; +export function FeedbackSurveyView(t0) { + const $ = _c(15); + const { + onSelect, + inputValue, + setInputValue, + message: t1 + } = t0; + const message = t1 === undefined ? DEFAULT_MESSAGE : t1; + let t2; + if ($[0] !== onSelect) { + t2 = digit => onSelect(inputToResponse[digit]); + $[0] = onSelect; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== inputValue || $[3] !== setInputValue || $[4] !== t2) { + t3 = { + inputValue, + setInputValue, + isValidDigit: isValidResponseInput, + onDigit: t2 + }; + $[2] = inputValue; + $[3] = setInputValue; + $[4] = t2; + $[5] = t3; + } else { + t3 = $[5]; + } + useDebouncedDigitInput(t3); + let t4; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t4 = ; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] !== message) { + t5 = {t4}{message}; + $[7] = message; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t6 = 1: Bad; + $[9] = t6; + } else { + t6 = $[9]; + } + let t7; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t7 = 2: Fine; + $[10] = t7; + } else { + t7 = $[10]; + } + let t8; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t8 = 3: Good; + $[11] = t8; + } else { + t8 = $[11]; + } + let t9; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t9 = {t6}{t7}{t8}0: Dismiss; + $[12] = t9; + } else { + t9 = $[12]; + } + let t10; + if ($[13] !== t5) { + t10 = {t5}{t9}; + $[13] = t5; + $[14] = t10; + } else { + t10 = $[14]; + } + return t10; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJ1c2VEZWJvdW5jZWREaWdpdElucHV0IiwiRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSIsIlByb3BzIiwib25TZWxlY3QiLCJvcHRpb24iLCJpbnB1dFZhbHVlIiwic2V0SW5wdXRWYWx1ZSIsInZhbHVlIiwibWVzc2FnZSIsIlJFU1BPTlNFX0lOUFVUUyIsImNvbnN0IiwiUmVzcG9uc2VJbnB1dCIsImlucHV0VG9SZXNwb25zZSIsIlJlY29yZCIsImlzVmFsaWRSZXNwb25zZUlucHV0IiwiaW5wdXQiLCJpbmNsdWRlcyIsIkRFRkFVTFRfTUVTU0FHRSIsIkZlZWRiYWNrU3VydmV5VmlldyIsInQwIiwiJCIsIl9jIiwidDEiLCJ1bmRlZmluZWQiLCJ0MiIsImRpZ2l0IiwidDMiLCJpc1ZhbGlkRGlnaXQiLCJvbkRpZ2l0IiwidDQiLCJTeW1ib2wiLCJmb3IiLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5IiwidDEwIl0sInNvdXJjZXMiOlsiRmVlZGJhY2tTdXJ2ZXlWaWV3LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VEZWJvdW5jZWREaWdpdElucHV0IH0gZnJvbSAnLi91c2VEZWJvdW5jZWREaWdpdElucHV0LmpzJ1xuaW1wb3J0IHR5cGUgeyBGZWVkYmFja1N1cnZleVJlc3BvbnNlIH0gZnJvbSAnLi91dGlscy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgb25TZWxlY3Q6IChvcHRpb246IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UpID0+IHZvaWRcbiAgaW5wdXRWYWx1ZTogc3RyaW5nXG4gIHNldElucHV0VmFsdWU6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG4gIG1lc3NhZ2U/OiBzdHJpbmdcbn1cblxuY29uc3QgUkVTUE9OU0VfSU5QVVRTID0gWycwJywgJzEnLCAnMicsICczJ10gYXMgY29uc3RcbnR5cGUgUmVzcG9uc2VJbnB1dCA9ICh0eXBlb2YgUkVTUE9OU0VfSU5QVVRTKVtudW1iZXJdXG5cbmNvbnN0IGlucHV0VG9SZXNwb25zZTogUmVjb3JkPFJlc3BvbnNlSW5wdXQsIEZlZWRiYWNrU3VydmV5UmVzcG9uc2U+ID0ge1xuICAnMCc6ICdkaXNtaXNzZWQnLFxuICAnMSc6ICdiYWQnLFxuICAnMic6ICdmaW5lJyxcbiAgJzMnOiAnZ29vZCcsXG59IGFzIGNvbnN0XG5cbmV4cG9ydCBjb25zdCBpc1ZhbGlkUmVzcG9uc2VJbnB1dCA9IChpbnB1dDogc3RyaW5nKTogaW5wdXQgaXMgUmVzcG9uc2VJbnB1dCA9PlxuICAoUkVTUE9OU0VfSU5QVVRTIGFzIHJlYWRvbmx5IHN0cmluZ1tdKS5pbmNsdWRlcyhpbnB1dClcblxuY29uc3QgREVGQVVMVF9NRVNTQUdFID0gJ0hvdyBpcyBDbGF1ZGUgZG9pbmcgdGhpcyBzZXNzaW9uPyAob3B0aW9uYWwpJ1xuXG5leHBvcnQgZnVuY3Rpb24gRmVlZGJhY2tTdXJ2ZXlWaWV3KHtcbiAgb25TZWxlY3QsXG4gIGlucHV0VmFsdWUsXG4gIHNldElucHV0VmFsdWUsXG4gIG1lc3NhZ2UgPSBERUZBVUxUX01FU1NBR0UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHVzZURlYm91bmNlZERpZ2l0SW5wdXQoe1xuICAgIGlucHV0VmFsdWUsXG4gICAgc2V0SW5wdXRWYWx1ZSxcbiAgICBpc1ZhbGlkRGlnaXQ6IGlzVmFsaWRSZXNwb25zZUlucHV0LFxuICAgIG9uRGlnaXQ6IGRpZ2l0ID0+IG9uU2VsZWN0KGlucHV0VG9SZXNwb25zZVtkaWdpdF0pLFxuICB9KVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgIDxCb3g+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiYW5zaTpjeWFuXCI+4pePIDwvVGV4dD5cbiAgICAgICAgPFRleHQgYm9sZD57bWVzc2FnZX08L1RleHQ+XG4gICAgICA8L0JveD5cblxuICAgICAgPEJveCBtYXJnaW5MZWZ0PXsyfT5cbiAgICAgICAgPEJveCB3aWR0aD17MTB9PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJhbnNpOmN5YW5cIj4xPC9UZXh0PjogQmFkXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCB3aWR0aD17MTB9PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJhbnNpOmN5YW5cIj4yPC9UZXh0PjogRmluZVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggd2lkdGg9ezEwfT5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiYW5zaTpjeWFuXCI+MzwvVGV4dD46IEdvb2RcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJhbnNpOmN5YW5cIj4wPC9UZXh0PjogRGlzbWlzc1xuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxzQkFBc0IsUUFBUSw2QkFBNkI7QUFDcEUsY0FBY0Msc0JBQXNCLFFBQVEsWUFBWTtBQUV4RCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsUUFBUSxFQUFFLENBQUNDLE1BQU0sRUFBRUgsc0JBQXNCLEVBQUUsR0FBRyxJQUFJO0VBQ2xESSxVQUFVLEVBQUUsTUFBTTtFQUNsQkMsYUFBYSxFQUFFLENBQUNDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FB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 \ No newline at end of file diff --git a/components/FeedbackSurvey/TranscriptSharePrompt.tsx b/components/FeedbackSurvey/TranscriptSharePrompt.tsx new file mode 100644 index 0000000..b9556c8 --- /dev/null +++ b/components/FeedbackSurvey/TranscriptSharePrompt.tsx @@ -0,0 +1,88 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { BLACK_CIRCLE } from '../../constants/figures.js'; +import { Box, Text } from '../../ink.js'; +import { useDebouncedDigitInput } from './useDebouncedDigitInput.js'; +export type TranscriptShareResponse = 'yes' | 'no' | 'dont_ask_again'; +type Props = { + onSelect: (option: TranscriptShareResponse) => void; + inputValue: string; + setInputValue: (value: string) => void; +}; +const RESPONSE_INPUTS = ['1', '2', '3'] as const; +type ResponseInput = (typeof RESPONSE_INPUTS)[number]; +const inputToResponse: Record = { + '1': 'yes', + '2': 'no', + '3': 'dont_ask_again' +} as const; +const isValidResponseInput = (input: string): input is ResponseInput => (RESPONSE_INPUTS as readonly string[]).includes(input); +export function TranscriptSharePrompt(t0) { + const $ = _c(11); + const { + onSelect, + inputValue, + setInputValue + } = t0; + let t1; + if ($[0] !== onSelect) { + t1 = digit => onSelect(inputToResponse[digit]); + $[0] = onSelect; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== inputValue || $[3] !== setInputValue || $[4] !== t1) { + t2 = { + inputValue, + setInputValue, + isValidDigit: isValidResponseInput, + onDigit: t1 + }; + $[2] = inputValue; + $[3] = setInputValue; + $[4] = t1; + $[5] = t2; + } else { + t2 = $[5]; + } + useDebouncedDigitInput(t2); + let t3; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t3 = {BLACK_CIRCLE} Can Anthropic look at your session transcript to help us improve Claude Code?; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t4 = Learn more: https://code.claude.com/docs/en/data-usage#session-quality-surveys; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t5 = 1: Yes; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t6 = 2: No; + $[9] = t6; + } else { + t6 = $[9]; + } + let t7; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t7 = {t3}{t4}{t5}{t6}3: Don't ask again; + $[10] = t7; + } else { + t7 = $[10]; + } + return t7; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJMQUNLX0NJUkNMRSIsIkJveCIsIlRleHQiLCJ1c2VEZWJvdW5jZWREaWdpdElucHV0IiwiVHJhbnNjcmlwdFNoYXJlUmVzcG9uc2UiLCJQcm9wcyIsIm9uU2VsZWN0Iiwib3B0aW9uIiwiaW5wdXRWYWx1ZSIsInNldElucHV0VmFsdWUiLCJ2YWx1ZSIsIlJFU1BPTlNFX0lOUFVUUyIsImNvbnN0IiwiUmVzcG9uc2VJbnB1dCIsImlucHV0VG9SZXNwb25zZSIsIlJlY29yZCIsImlzVmFsaWRSZXNwb25zZUlucHV0IiwiaW5wdXQiLCJpbmNsdWRlcyIsIlRyYW5zY3JpcHRTaGFyZVByb21wdCIsInQwIiwiJCIsIl9jIiwidDEiLCJkaWdpdCIsInQyIiwiaXNWYWxpZERpZ2l0Iiwib25EaWdpdCIsInQzIiwiU3ltYm9sIiwiZm9yIiwidDQiLCJ0NSIsInQ2IiwidDciXSwic291cmNlcyI6WyJUcmFuc2NyaXB0U2hhcmVQcm9tcHQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJMQUNLX0NJUkNMRSB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy9maWd1cmVzLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlRGVib3VuY2VkRGlnaXRJbnB1dCB9IGZyb20gJy4vdXNlRGVib3VuY2VkRGlnaXRJbnB1dC5qcydcblxuZXhwb3J0IHR5cGUgVHJhbnNjcmlwdFNoYXJlUmVzcG9uc2UgPSAneWVzJyB8ICdubycgfCAnZG9udF9hc2tfYWdhaW4nXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG9uU2VsZWN0OiAob3B0aW9uOiBUcmFuc2NyaXB0U2hhcmVSZXNwb25zZSkgPT4gdm9pZFxuICBpbnB1dFZhbHVlOiBzdHJpbmdcbiAgc2V0SW5wdXRWYWx1ZTogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWRcbn1cblxuY29uc3QgUkVTUE9OU0VfSU5QVVRTID0gWycxJywgJzInLCAnMyddIGFzIGNvbnN0XG50eXBlIFJlc3BvbnNlSW5wdXQgPSAodHlwZW9mIFJFU1BPTlNFX0lOUFVUUylbbnVtYmVyXVxuXG5jb25zdCBpbnB1dFRvUmVzcG9uc2U6IFJlY29yZDxSZXNwb25zZUlucHV0LCBUcmFuc2NyaXB0U2hhcmVSZXNwb25zZT4gPSB7XG4gICcxJzogJ3llcycsXG4gICcyJzogJ25vJyxcbiAgJzMnOiAnZG9udF9hc2tfYWdhaW4nLFxufSBhcyBjb25zdFxuXG5jb25zdCBpc1ZhbGlkUmVzcG9uc2VJbnB1dCA9IChpbnB1dDogc3RyaW5nKTogaW5wdXQgaXMgUmVzcG9uc2VJbnB1dCA9PlxuICAoUkVTUE9OU0VfSU5QVVRTIGFzIHJlYWRvbmx5IHN0cmluZ1tdKS5pbmNsdWRlcyhpbnB1dClcblxuZXhwb3J0IGZ1bmN0aW9uIFRyYW5zY3JpcHRTaGFyZVByb21wdCh7XG4gIG9uU2VsZWN0LFxuICBpbnB1dFZhbHVlLFxuICBzZXRJbnB1dFZhbHVlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICB1c2VEZWJvdW5jZWREaWdpdElucHV0KHtcbiAgICBpbnB1dFZhbHVlLFxuICAgIHNldElucHV0VmFsdWUsXG4gICAgaXNWYWxpZERpZ2l0OiBpc1ZhbGlkUmVzcG9uc2VJbnB1dCxcbiAgICBvbkRpZ2l0OiBkaWdpdCA9PiBvblNlbGVjdChpbnB1dFRvUmVzcG9uc2VbZGlnaXRdKSxcbiAgfSlcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpblRvcD17MX0+XG4gICAgICA8Qm94PlxuICAgICAgICA8VGV4dCBjb2xvcj1cImFuc2k6Y3lhblwiPntCTEFDS19DSVJDTEV9IDwvVGV4dD5cbiAgICAgICAgPFRleHQgYm9sZD5cbiAgICAgICAgICBDYW4gQW50aHJvcGljIGxvb2sgYXQgeW91ciBzZXNzaW9uIHRyYW5zY3JpcHQgdG8gaGVscCB1cyBpbXByb3ZlXG4gICAgICAgICAgQ2xhdWRlIENvZGU/XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuXG4gICAgICA8Qm94IG1hcmdpbkxlZnQ9ezJ9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICBMZWFybiBtb3JlOlxuICAgICAgICAgIGh0dHBzOi8vY29kZS5jbGF1ZGUuY29tL2RvY3MvZW4vZGF0YS11c2FnZSNzZXNzaW9uLXF1YWxpdHktc3VydmV5c1xuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cblxuICAgICAgPEJveCBtYXJnaW5MZWZ0PXsyfT5cbiAgICAgICAgPEJveCB3aWR0aD17MTB9PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJhbnNpOmN5YW5cIj4xPC9UZXh0PjogWWVzXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCB3aWR0aD17MTB9PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJhbnNpOmN5YW5cIj4yPC9UZXh0PjogTm9cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJhbnNpOmN5YW5cIj4zPC9UZXh0PjogRG9uJmFwb3M7dCBhc2sgYWdhaW5cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLFlBQVksUUFBUSw0QkFBNEI7QUFDekQsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxzQkFBc0IsUUFBUSw2QkFBNkI7QUFFcEUsT0FBTyxLQUFLQyx1QkFBdUIsR0FBRyxLQUFLLEdBQUcsSUFBSSxHQUFHLGdCQUFnQjtBQUVyRSxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsUUFBUSxFQUFFLENBQUNDLE1BQU0sRUFBRUgsdUJBQXVCLEVBQUUsR0FBRyxJQUFJO0VBQ25ESSxVQUFVLEVBQUUsTUFBTTtFQUNsQkMsYUFBYSxFQUFFLENBQUNDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0FBQ3hDLENBQUM7QUFFRCxNQUFNQyxlQUFlLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQyxJQUFJQyxLQUFLO0FBQ2hELEtBQUtDLGFBQWEsR0FBRyxDQUFDLE9BQU9GLGVBQWUsQ0FBQyxDQUFDLE1BQU0sQ0FBQztBQUVyRCxNQUFNRyxlQUFlLEVBQUVDLE1BQU0sQ0FBQ0YsYUFBYSxFQUFFVCx1QkFBdUIsQ0FBQyxHQUFHO0VBQ3RFLEdBQUcsRUFBRSxLQUFLO0VBQ1YsR0FBRyxFQUFFLElBQUk7RUFDVCxHQUFHLEVBQUU7QUFDUCxDQUFDLElBQUlRLEtBQUs7QUFFVixNQUFNSSxvQkFBb0IsR0FBR0EsQ0FBQ0MsS0FBSyxFQUFFLE1BQU0sQ0FBQyxFQUFFQSxLQUFLLElBQUlKLGFBQWEsSUFDbEUsQ0FBQ0YsZUFBZSxJQUFJLFNBQVMsTUFBTSxFQUFFLEVBQUVPLFFBQVEsQ0FBQ0QsS0FBSyxDQUFDO0FBRXhELE9BQU8sU0FBQUUsc0JBQUFDLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBK0I7SUFBQWhCLFFBQUE7SUFBQUUsVUFBQTtJQUFBQztFQUFBLElBQUFXLEVBSTlCO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQWYsUUFBQTtJQUtLaUIsRUFBQSxHQUFBQyxLQUFBLElBQVNsQixRQUFRLENBQUNRLGVBQWUsQ0FBQ1UsS0FBSyxDQUFDLENBQUM7SUFBQUgsQ0FBQSxNQUFBZixRQUFBO0lBQUFlLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsSUFBQUksRUFBQTtFQUFBLElBQUFKLENBQUEsUUFBQWIsVUFBQSxJQUFBYSxDQUFBLFFBQUFaLGFBQUEsSUFBQVksQ0FBQSxRQUFBRSxFQUFBO0lBSjdCRSxFQUFBO01BQUFqQixVQUFBO01BQUFDLGFBQUE7TUFBQWlCLFlBQUEsRUFHUFYsb0JBQW9CO01BQUFXLE9BQUEsRUFDekJKO0lBQ1gsQ0FBQztJQUFBRixDQUFBLE1BQUFiLFVBQUE7SUFBQWEsQ0FBQSxNQUFBWixhQUFBO0lBQUFZLENBQUEsTUFBQUUsRUFBQTtJQUFBRixDQUFBLE1BQUFJLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFKLENBQUE7RUFBQTtFQUxEbEIsc0JBQXNCLENBQUNzQixFQUt0QixDQUFDO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFQLENBQUEsUUFBQVEsTUFBQSxDQUFBQyxHQUFBO0lBSUVGLEVBQUEsSUFBQyxHQUFHLENBQ0YsQ0FBQyxJQUFJLENBQU8sS0FBVyxDQUFYLFdBQVcsQ0FBRTVCLGFBQVcsQ0FBRSxDQUFDLEVBQXRDLElBQUksQ0FDTCxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUosS0FBRyxDQUFDLENBQUMsNkVBR1gsRUFIQyxJQUFJLENBSVAsRUFOQyxHQUFHLENBTUU7SUFBQXFCLENBQUEsTUFBQU8sRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVAsQ0FBQTtFQUFBO0VBQUEsSUFBQVUsRUFBQTtFQUFBLElBQUFWLENBQUEsUUFBQVEsTUFBQSxDQUFBQyxHQUFBO0lBRU5DLEVBQUEsSUFBQyxHQUFHLENBQWEsVUFBQyxDQUFELEdBQUMsQ0FDaEIsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLDhFQUdmLEVBSEMsSUFBSSxDQUlQLEVBTEMsR0FBRyxDQUtFO0lBQUFWLENBQUEsTUFBQVUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVYsQ0FBQTtFQUFBO0VBQUEsSUFBQVcsRUFBQTtFQUFBLElBQUFYLENBQUEsUUFBQVEsTUFBQSxDQUFBQyxHQUFBO0lBR0pFLEVBQUEsSUFBQyxHQUFHLENBQVEsS0FBRSxDQUFGLEdBQUMsQ0FBQyxDQUNaLENBQUMsSUFBSSxDQUNILENBQUMsSUFBSSxDQUFPLEtBQVcsQ0FBWCxXQUFXLENBQUMsQ0FBQyxFQUF4QixJQUFJLENBQTJCLEtBQ2xDLEVBRkMsSUFBSSxDQUdQLEVBSkMsR0FBRyxDQUlFO0lBQUFYLENBQUEsTUFBQVcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVgsQ0FBQTtFQUFBO0VBQUEsSUFBQVksRUFBQTtFQUFBLElBQUFaLENBQUEsUUFBQVEsTUFBQSxDQUFBQyxHQUFBO0lBQ05HLEVBQUEsSUFBQyxHQUFHLENBQVEsS0FBRSxDQUFGLEdBQUMsQ0FBQyxDQUNaLENBQUMsSUFBSSxDQUNILENBQUMsSUFBSSxDQUFPLEtBQVcsQ0FBWCxXQUFXLENBQUMsQ0FBQyxFQUF4QixJQUFJLENBQTJCLElBQ2xDLEVBRkMsSUFBSSxDQUdQLEVBSkMsR0FBRyxDQUlFO0lBQUFaLENBQUEsTUFBQVksRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVosQ0FBQTtFQUFBO0VBQUEsSUFBQWEsRUFBQTtFQUFBLElBQUFiLENBQUEsU0FBQVEsTUFBQSxDQUFBQyxHQUFBO0lBMUJWSSxFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQVksU0FBQyxDQUFELEdBQUMsQ0FDdEMsQ0FBQU4sRUFNSyxDQUVMLENBQUFHLEVBS0ssQ0FFTCxDQUFDLEdBQUcsQ0FBYSxVQUFDLENBQUQsR0FBQyxDQUNoQixDQUFBQyxFQUlLLENBQ0wsQ0FBQUMsRUFJSyxDQUNMLENBQUMsR0FBRyxDQUNGLENBQUMsSUFBSSxDQUNILENBQUMsSUFBSSxDQUFPLEtBQVcsQ0FBWCxXQUFXLENBQUMsQ0FBQyxFQUF4QixJQUFJLENBQTJCLGlCQUNsQyxFQUZDLElBQUksQ0FHUCxFQUpDLEdBQUcsQ0FLTixFQWhCQyxHQUFHLENBaUJOLEVBakNDLEdBQUcsQ0FpQ0U7SUFBQVosQ0FBQSxPQUFBYSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBYixDQUFBO0VBQUE7RUFBQSxPQWpDTmEsRUFpQ007QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/FeedbackSurvey/submitTranscriptShare.ts b/components/FeedbackSurvey/submitTranscriptShare.ts new file mode 100644 index 0000000..52e1425 --- /dev/null +++ b/components/FeedbackSurvey/submitTranscriptShare.ts @@ -0,0 +1,112 @@ +import axios from 'axios' +import { readFile, stat } from 'fs/promises' +import type { Message } from '../../types/message.js' +import { checkAndRefreshOAuthTokenIfNeeded } from '../../utils/auth.js' +import { logForDebugging } from '../../utils/debug.js' +import { errorMessage } from '../../utils/errors.js' +import { getAuthHeaders, getUserAgent } from '../../utils/http.js' +import { normalizeMessagesForAPI } from '../../utils/messages.js' +import { + extractAgentIdsFromMessages, + getTranscriptPath, + loadSubagentTranscripts, + MAX_TRANSCRIPT_READ_BYTES, +} from '../../utils/sessionStorage.js' +import { jsonStringify } from '../../utils/slowOperations.js' +import { redactSensitiveInfo } from '../Feedback.js' + +type TranscriptShareResult = { + success: boolean + transcriptId?: string +} + +export type TranscriptShareTrigger = + | 'bad_feedback_survey' + | 'good_feedback_survey' + | 'frustration' + | 'memory_survey' + +export async function submitTranscriptShare( + messages: Message[], + trigger: TranscriptShareTrigger, + appearanceId: string, +): Promise { + try { + logForDebugging('Collecting transcript for sharing', { level: 'info' }) + + const transcript = normalizeMessagesForAPI(messages) + + // Collect subagent transcripts + const agentIds = extractAgentIdsFromMessages(messages) + const subagentTranscripts = await loadSubagentTranscripts(agentIds) + + // Read raw JSONL transcript (with size guard to prevent OOM) + let rawTranscriptJsonl: string | undefined + try { + const transcriptPath = getTranscriptPath() + const { size } = await stat(transcriptPath) + if (size <= MAX_TRANSCRIPT_READ_BYTES) { + rawTranscriptJsonl = await readFile(transcriptPath, 'utf-8') + } else { + logForDebugging( + `Skipping raw transcript read: file too large (${size} bytes)`, + { level: 'warn' }, + ) + } + } catch { + // File may not exist + } + + const data = { + trigger, + version: MACRO.VERSION, + platform: process.platform, + transcript, + subagentTranscripts: + Object.keys(subagentTranscripts).length > 0 + ? subagentTranscripts + : undefined, + rawTranscriptJsonl, + } + + const content = redactSensitiveInfo(jsonStringify(data)) + + await checkAndRefreshOAuthTokenIfNeeded() + + const authResult = getAuthHeaders() + if (authResult.error) { + return { success: false } + } + + const headers: Record = { + 'Content-Type': 'application/json', + 'User-Agent': getUserAgent(), + ...authResult.headers, + } + + const response = await axios.post( + 'https://api.anthropic.com/api/claude_code_shared_session_transcripts', + { content, appearance_id: appearanceId }, + { + headers, + timeout: 30000, + }, + ) + + if (response.status === 200 || response.status === 201) { + const result = response.data + logForDebugging('Transcript shared successfully', { level: 'info' }) + return { + success: true, + transcriptId: result?.transcript_id, + } + } + + return { success: false } + } catch (err) { + logForDebugging(errorMessage(err), { + level: 'error', + }) + return { success: false } + } +} diff --git a/components/FeedbackSurvey/useDebouncedDigitInput.ts b/components/FeedbackSurvey/useDebouncedDigitInput.ts new file mode 100644 index 0000000..072eaeb --- /dev/null +++ b/components/FeedbackSurvey/useDebouncedDigitInput.ts @@ -0,0 +1,82 @@ +import { useEffect, useRef } from 'react' +import { normalizeFullWidthDigits } from '../../utils/stringUtils.js' + +// Delay before accepting a digit as a response, to prevent accidental +// submissions when users start messages with numbers (e.g., numbered lists). +// Short enough to feel instant for intentional presses, long enough to +// cancel when the user types more characters. +const DEFAULT_DEBOUNCE_MS = 400 + +/** + * Detects when the user types a single valid digit into the prompt input, + * debounces to avoid accidental submissions (e.g., "1. First item"), + * trims the digit from the input, and fires a callback. + * + * Used by survey components that accept numeric responses typed directly + * into the main prompt input. + */ +export function useDebouncedDigitInput({ + inputValue, + setInputValue, + isValidDigit, + onDigit, + enabled = true, + once = false, + debounceMs = DEFAULT_DEBOUNCE_MS, +}: { + inputValue: string + setInputValue: (value: string) => void + isValidDigit: (char: string) => char is T + onDigit: (digit: T) => void + enabled?: boolean + once?: boolean + debounceMs?: number +}): void { + const initialInputValue = useRef(inputValue) + const hasTriggeredRef = useRef(false) + const debounceRef = useRef | null>(null) + + // Latest-ref pattern so callers can pass inline callbacks without causing + // the effect to re-run (which would reset the debounce timer every render). + const callbacksRef = useRef({ setInputValue, isValidDigit, onDigit }) + callbacksRef.current = { setInputValue, isValidDigit, onDigit } + + useEffect(() => { + if (!enabled || (once && hasTriggeredRef.current)) { + return + } + + if (debounceRef.current !== null) { + clearTimeout(debounceRef.current) + debounceRef.current = null + } + + if (inputValue !== initialInputValue.current) { + const lastChar = normalizeFullWidthDigits(inputValue.slice(-1)) + if (callbacksRef.current.isValidDigit(lastChar)) { + const trimmed = inputValue.slice(0, -1) + debounceRef.current = setTimeout( + (debounceRef, hasTriggeredRef, callbacksRef, trimmed, lastChar) => { + debounceRef.current = null + hasTriggeredRef.current = true + callbacksRef.current.setInputValue(trimmed) + callbacksRef.current.onDigit(lastChar) + }, + debounceMs, + debounceRef, + hasTriggeredRef, + callbacksRef, + trimmed, + lastChar, + ) + } + } + + return () => { + if (debounceRef.current !== null) { + clearTimeout(debounceRef.current) + debounceRef.current = null + } + } + }, [inputValue, enabled, once, debounceMs]) +} diff --git a/components/FeedbackSurvey/useFeedbackSurvey.tsx b/components/FeedbackSurvey/useFeedbackSurvey.tsx new file mode 100644 index 0000000..20bab3d --- /dev/null +++ b/components/FeedbackSurvey/useFeedbackSurvey.tsx @@ -0,0 +1,296 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useDynamicConfig } from 'src/hooks/useDynamicConfig.js'; +import { isFeedbackSurveyDisabled } from 'src/services/analytics/config.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { isPolicyAllowed } from '../../services/policyLimits/index.js'; +import type { Message } from '../../types/message.js'; +import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'; +import { isEnvTruthy } from '../../utils/envUtils.js'; +import { getLastAssistantMessage } from '../../utils/messages.js'; +import { getMainLoopModel } from '../../utils/model/model.js'; +import { getInitialSettings } from '../../utils/settings/settings.js'; +import { logOTelEvent } from '../../utils/telemetry/events.js'; +import { submitTranscriptShare, type TranscriptShareTrigger } from './submitTranscriptShare.js'; +import type { TranscriptShareResponse } from './TranscriptSharePrompt.js'; +import { useSurveyState } from './useSurveyState.js'; +import type { FeedbackSurveyResponse, FeedbackSurveyType } from './utils.js'; +type FeedbackSurveyConfig = { + minTimeBeforeFeedbackMs: number; + minTimeBetweenFeedbackMs: number; + minTimeBetweenGlobalFeedbackMs: number; + minUserTurnsBeforeFeedback: number; + minUserTurnsBetweenFeedback: number; + hideThanksAfterMs: number; + onForModels: string[]; + probability: number; +}; +type TranscriptAskConfig = { + probability: number; +}; +const DEFAULT_FEEDBACK_SURVEY_CONFIG: FeedbackSurveyConfig = { + minTimeBeforeFeedbackMs: 600000, + minTimeBetweenFeedbackMs: 3600000, + minTimeBetweenGlobalFeedbackMs: 100000000, + minUserTurnsBeforeFeedback: 5, + minUserTurnsBetweenFeedback: 10, + hideThanksAfterMs: 3000, + onForModels: ['*'], + probability: 0.005 +}; +const DEFAULT_TRANSCRIPT_ASK_CONFIG: TranscriptAskConfig = { + probability: 0 +}; +export function useFeedbackSurvey(messages: Message[], isLoading: boolean, submitCount: number, surveyType: FeedbackSurveyType = 'session', hasActivePrompt: boolean = false): { + state: 'closed' | 'open' | 'thanks' | 'transcript_prompt' | 'submitting' | 'submitted'; + lastResponse: FeedbackSurveyResponse | null; + handleSelect: (selected: FeedbackSurveyResponse) => boolean; + handleTranscriptSelect: (selected: TranscriptShareResponse) => void; +} { + const lastAssistantMessageIdRef = useRef('unknown'); + lastAssistantMessageIdRef.current = getLastAssistantMessage(messages)?.message?.id || 'unknown'; + const [feedbackSurvey, setFeedbackSurvey] = useState<{ + timeLastShown: number | null; + submitCountAtLastAppearance: number | null; + }>(() => ({ + timeLastShown: null, + submitCountAtLastAppearance: null + })); + const config = useDynamicConfig('tengu_feedback_survey_config', DEFAULT_FEEDBACK_SURVEY_CONFIG); + const badTranscriptAskConfig = useDynamicConfig('tengu_bad_survey_transcript_ask_config', DEFAULT_TRANSCRIPT_ASK_CONFIG); + const goodTranscriptAskConfig = useDynamicConfig('tengu_good_survey_transcript_ask_config', DEFAULT_TRANSCRIPT_ASK_CONFIG); + const settingsRate = getInitialSettings().feedbackSurveyRate; + const sessionStartTime = useRef(Date.now()); + const submitCountAtSessionStart = useRef(submitCount); + const submitCountRef = useRef(submitCount); + submitCountRef.current = submitCount; + const messagesRef = useRef(messages); + messagesRef.current = messages; + // Probability gate: roll once when eligibility conditions are met, not on every + // useMemo re-evaluation. Without this, each dependency change (submitCount, + // isLoading toggle, etc.) re-rolls Math.random(), making the survey almost + // certain to appear after enough renders. + const probabilityPassedRef = useRef(false); + const lastEligibleSubmitCountRef = useRef(null); + const updateLastShownTime = useCallback((timestamp: number, submitCountValue: number) => { + setFeedbackSurvey(prev => { + if (prev.timeLastShown === timestamp && prev.submitCountAtLastAppearance === submitCountValue) { + return prev; + } + return { + timeLastShown: timestamp, + submitCountAtLastAppearance: submitCountValue + }; + }); + // Persist cross-session pacing state (previously done by onChangeAppState observer) + if (getGlobalConfig().feedbackSurveyState?.lastShownTime !== timestamp) { + saveGlobalConfig(current => ({ + ...current, + feedbackSurveyState: { + lastShownTime: timestamp + } + })); + } + }, []); + const onOpen = useCallback((appearanceId: string) => { + updateLastShownTime(Date.now(), submitCountRef.current); + logEvent('tengu_feedback_survey_event', { + event_type: 'appeared' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + last_assistant_message_id: lastAssistantMessageIdRef.current as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + survey_type: surveyType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + void logOTelEvent('feedback_survey', { + event_type: 'appeared', + appearance_id: appearanceId, + survey_type: surveyType + }); + }, [updateLastShownTime, surveyType]); + const onSelect = useCallback((appearanceId_0: string, selected: FeedbackSurveyResponse) => { + updateLastShownTime(Date.now(), submitCountRef.current); + logEvent('tengu_feedback_survey_event', { + event_type: 'responded' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_0 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + response: selected as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + last_assistant_message_id: lastAssistantMessageIdRef.current as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + survey_type: surveyType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + void logOTelEvent('feedback_survey', { + event_type: 'responded', + appearance_id: appearanceId_0, + response: selected, + survey_type: surveyType + }); + }, [updateLastShownTime, surveyType]); + const shouldShowTranscriptPrompt = useCallback((selected_0: FeedbackSurveyResponse) => { + // Only bad and good ratings trigger the transcript ask + if (selected_0 !== 'bad' && selected_0 !== 'good') { + return false; + } + + // Don't show if user previously chose "Don't ask again" + if (getGlobalConfig().transcriptShareDismissed) { + return false; + } + + // Don't show if product feedback is blocked by org policy (ZDR) + if (!isPolicyAllowed('allow_product_feedback')) { + return false; + } + + // Probability gate from GrowthBook config (separate per rating) + const probability = selected_0 === 'bad' ? badTranscriptAskConfig.probability : goodTranscriptAskConfig.probability; + return Math.random() <= probability; + }, [badTranscriptAskConfig.probability, goodTranscriptAskConfig.probability]); + const onTranscriptPromptShown = useCallback((appearanceId_1: string, surveyResponse: FeedbackSurveyResponse) => { + const trigger: TranscriptShareTrigger = surveyResponse === 'good' ? 'good_feedback_survey' : 'bad_feedback_survey'; + logEvent('tengu_feedback_survey_event', { + event_type: 'transcript_prompt_appeared' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_1 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + last_assistant_message_id: lastAssistantMessageIdRef.current as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + survey_type: surveyType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + trigger: trigger as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + void logOTelEvent('feedback_survey', { + event_type: 'transcript_prompt_appeared', + appearance_id: appearanceId_1, + survey_type: surveyType + }); + }, [surveyType]); + const onTranscriptSelect = useCallback(async (appearanceId_2: string, selected_1: TranscriptShareResponse, surveyResponse_0: FeedbackSurveyResponse | null): Promise => { + const trigger_0: TranscriptShareTrigger = surveyResponse_0 === 'good' ? 'good_feedback_survey' : 'bad_feedback_survey'; + logEvent('tengu_feedback_survey_event', { + event_type: `transcript_share_${selected_1}` as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_2 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + last_assistant_message_id: lastAssistantMessageIdRef.current as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + survey_type: surveyType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + trigger: trigger_0 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + if (selected_1 === 'dont_ask_again') { + saveGlobalConfig(current_0 => ({ + ...current_0, + transcriptShareDismissed: true + })); + } + if (selected_1 === 'yes') { + const result = await submitTranscriptShare(messagesRef.current, trigger_0, appearanceId_2); + logEvent('tengu_feedback_survey_event', { + event_type: (result.success ? 'transcript_share_submitted' : 'transcript_share_failed') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_2 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + trigger: trigger_0 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + return result.success; + } + return false; + }, [surveyType]); + const { + state, + lastResponse, + open, + handleSelect, + handleTranscriptSelect + } = useSurveyState({ + hideThanksAfterMs: config.hideThanksAfterMs, + onOpen, + onSelect, + shouldShowTranscriptPrompt, + onTranscriptPromptShown, + onTranscriptSelect + }); + const currentModel = getMainLoopModel(); + const isModelAllowed = useMemo(() => { + if (config.onForModels.length === 0) { + return false; + } + if (config.onForModels.includes('*')) { + return true; + } + return config.onForModels.includes(currentModel); + }, [config.onForModels, currentModel]); + const shouldOpen = useMemo(() => { + if (state !== 'closed') { + return false; + } + if (isLoading) { + return false; + } + + // Don't show survey when permission or ask question prompts are visible + if (hasActivePrompt) { + return false; + } + + // Force display for testing + if (process.env.CLAUDE_FORCE_DISPLAY_SURVEY && !feedbackSurvey.timeLastShown) { + return true; + } + if (!isModelAllowed) { + return false; + } + if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY)) { + return false; + } + if (isFeedbackSurveyDisabled()) { + return false; + } + + // Check if product feedback is allowed by org policy + if (!isPolicyAllowed('allow_product_feedback')) { + return false; + } + + // Check session-local pacing + if (feedbackSurvey.timeLastShown) { + // Check time elapsed since last appearance in this session + const timeSinceLastShown = Date.now() - feedbackSurvey.timeLastShown; + if (timeSinceLastShown < config.minTimeBetweenFeedbackMs) { + return false; + } + // Check user turn requirement for subsequent appearances + if (feedbackSurvey.submitCountAtLastAppearance !== null && submitCount < feedbackSurvey.submitCountAtLastAppearance + config.minUserTurnsBetweenFeedback) { + return false; + } + } else { + // First appearance in this session + const timeSinceSessionStart = Date.now() - sessionStartTime.current; + if (timeSinceSessionStart < config.minTimeBeforeFeedbackMs) { + return false; + } + if (submitCount < submitCountAtSessionStart.current + config.minUserTurnsBeforeFeedback) { + return false; + } + } + + // Probability check: roll once per eligibility window to avoid re-rolling + // on every useMemo re-evaluation (which would make triggering near-certain). + if (lastEligibleSubmitCountRef.current !== submitCount) { + lastEligibleSubmitCountRef.current = submitCount; + probabilityPassedRef.current = Math.random() <= (settingsRate ?? config.probability); + } + if (!probabilityPassedRef.current) { + return false; + } + + // Check global pacing (across all sessions) + // Leave this till last because it reads from the filesystem which is expensive. + const globalFeedbackState = getGlobalConfig().feedbackSurveyState; + if (globalFeedbackState?.lastShownTime) { + const timeSinceGlobalLastShown = Date.now() - globalFeedbackState.lastShownTime; + if (timeSinceGlobalLastShown < config.minTimeBetweenGlobalFeedbackMs) { + return false; + } + } + return true; + }, [state, isLoading, hasActivePrompt, isModelAllowed, feedbackSurvey.timeLastShown, feedbackSurvey.submitCountAtLastAppearance, submitCount, config.minTimeBetweenFeedbackMs, config.minTimeBetweenGlobalFeedbackMs, config.minUserTurnsBetweenFeedback, config.minTimeBeforeFeedbackMs, config.minUserTurnsBeforeFeedback, config.probability, settingsRate]); + useEffect(() => { + if (shouldOpen) { + open(); + } + }, [shouldOpen, open]); + return { + state, + lastResponse, + handleSelect, + handleTranscriptSelect + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1c2VDYWxsYmFjayIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsInVzZUR5bmFtaWNDb25maWciLCJpc0ZlZWRiYWNrU3VydmV5RGlzYWJsZWQiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJpc1BvbGljeUFsbG93ZWQiLCJNZXNzYWdlIiwiZ2V0R2xvYmFsQ29uZmlnIiwic2F2ZUdsb2JhbENvbmZpZyIsImlzRW52VHJ1dGh5IiwiZ2V0TGFzdEFzc2lzdGFudE1lc3NhZ2UiLCJnZXRNYWluTG9vcE1vZGVsIiwiZ2V0SW5pdGlhbFNldHRpbmdzIiwibG9nT1RlbEV2ZW50Iiwic3VibWl0VHJhbnNjcmlwdFNoYXJlIiwiVHJhbnNjcmlwdFNoYXJlVHJpZ2dlciIsIlRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlIiwidXNlU3VydmV5U3RhdGUiLCJGZWVkYmFja1N1cnZleVJlc3BvbnNlIiwiRmVlZGJhY2tTdXJ2ZXlUeXBlIiwiRmVlZGJhY2tTdXJ2ZXlDb25maWciLCJtaW5UaW1lQmVmb3JlRmVlZGJhY2tNcyIsIm1pblRpbWVCZXR3ZWVuRmVlZGJhY2tNcyIsIm1pblRpbWVCZXR3ZWVuR2xvYmFsRmVlZGJhY2tNcyIsIm1pblVzZXJUdXJuc0JlZm9yZUZlZWRiYWNrIiwibWluVXNlclR1cm5zQmV0d2VlbkZlZWRiYWNrIiwiaGlkZVRoYW5rc0FmdGVyTXMiLCJvbkZvck1vZGVscyIsInByb2JhYmlsaXR5IiwiVHJhbnNjcmlwdEFza0NvbmZpZyIsIkRFRkFVTFRfRkVFREJBQ0tfU1VSVkVZX0NPTkZJRyIsIkRFRkFVTFRfVFJBTlNDUklQVF9BU0tfQ09ORklHIiwidXNlRmVlZGJhY2tTdXJ2ZXkiLCJtZXNzYWdlcyIsImlzTG9hZGluZyIsInN1Ym1pdENvdW50Iiwic3VydmV5VHlwZSIsImhhc0FjdGl2ZVByb21wdCIsInN0YXRlIiwibGFzdFJlc3BvbnNlIiwiaGFuZGxlU2VsZWN0Iiwic2VsZWN0ZWQiLCJoYW5kbGVUcmFuc2NyaXB0U2VsZWN0IiwibGFzdEFzc2lzdGFudE1lc3NhZ2VJZFJlZiIsImN1cnJlbnQiLCJtZXNzYWdlIiwiaWQiLCJmZWVkYmFja1N1cnZleSIsInNldEZlZWRiYWNrU3VydmV5IiwidGltZUxhc3RTaG93biIsInN1Ym1pdENvdW50QXRMYXN0QXBwZWFyYW5jZSIsImNvbmZpZyIsImJhZFRyYW5zY3JpcHRBc2tDb25maWciLCJnb29kVHJhbnNjcmlwdEFza0NvbmZpZyIsInNldHRpbmdzUmF0ZSIsImZlZWRiYWNrU3VydmV5UmF0ZSIsInNlc3Npb25TdGFydFRpbWUiLCJEYXRlIiwibm93Iiwic3VibWl0Q291bnRBdFNlc3Npb25TdGFydCIsInN1Ym1pdENvdW50UmVmIiwibWVzc2FnZXNSZWYiLCJwcm9iYWJpbGl0eVBhc3NlZFJlZiIsImxhc3RFbGlnaWJsZVN1Ym1pdENvdW50UmVmIiwidXBkYXRlTGFzdFNob3duVGltZSIsInRpbWVzdGFtcCIsInN1Ym1pdENvdW50VmFsdWUiLCJwcmV2IiwiZmVlZGJhY2tTdXJ2ZXlTdGF0ZSIsImxhc3RTaG93blRpbWUiLCJvbk9wZW4iLCJhcHBlYXJhbmNlSWQiLCJldmVudF90eXBlIiwiYXBwZWFyYW5jZV9pZCIsImxhc3RfYXNzaXN0YW50X21lc3NhZ2VfaWQiLCJzdXJ2ZXlfdHlwZSIsIm9uU2VsZWN0IiwicmVzcG9uc2UiLCJzaG91bGRTaG93VHJhbnNjcmlwdFByb21wdCIsInRyYW5zY3JpcHRTaGFyZURpc21pc3NlZCIsIk1hdGgiLCJyYW5kb20iLCJvblRyYW5zY3JpcHRQcm9tcHRTaG93biIsInN1cnZleVJlc3BvbnNlIiwidHJpZ2dlciIsIm9uVHJhbnNjcmlwdFNlbGVjdCIsIlByb21pc2UiLCJyZXN1bHQiLCJzdWNjZXNzIiwib3BlbiIsImN1cnJlbnRNb2RlbCIsImlzTW9kZWxBbGxvd2VkIiwibGVuZ3RoIiwiaW5jbHVkZXMiLCJzaG91bGRPcGVuIiwicHJvY2VzcyIsImVudiIsIkNMQVVERV9GT1JDRV9ESVNQTEFZX1NVUlZFWSIsIkNMQVVERV9DT0RFX0RJU0FCTEVfRkVFREJBQ0tfU1VSVkVZIiwidGltZVNpbmNlTGFzdFNob3duIiwidGltZVNpbmNlU2Vzc2lvblN0YXJ0IiwiZ2xvYmFsRmVlZGJhY2tTdGF0ZSIsInRpbWVTaW5jZUdsb2JhbExhc3RTaG93biJdLCJzb3VyY2VzIjpbInVzZUZlZWRiYWNrU3VydmV5LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB1c2VDYWxsYmFjaywgdXNlRWZmZWN0LCB1c2VNZW1vLCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VEeW5hbWljQ29uZmlnIH0gZnJvbSAnc3JjL2hvb2tzL3VzZUR5bmFtaWNDb25maWcuanMnXG5pbXBvcnQgeyBpc0ZlZWRiYWNrU3VydmV5RGlzYWJsZWQgfSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2NvbmZpZy5qcydcbmltcG9ydCB7XG4gIHR5cGUgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgbG9nRXZlbnQsXG59IGZyb20gJ3NyYy9zZXJ2aWNlcy9hbmFseXRpY3MvaW5kZXguanMnXG5pbXBvcnQgeyBpc1BvbGljeUFsbG93ZWQgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9wb2xpY3lMaW1pdHMvaW5kZXguanMnXG5pbXBvcnQgdHlwZSB7IE1lc3NhZ2UgfSBmcm9tICcuLi8uLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYmFsQ29uZmlnLCBzYXZlR2xvYmFsQ29uZmlnIH0gZnJvbSAnLi4vLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgaXNFbnZUcnV0aHkgfSBmcm9tICcuLi8uLi91dGlscy9lbnZVdGlscy5qcydcbmltcG9ydCB7IGdldExhc3RBc3Npc3RhbnRNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdXRpbHMvbWVzc2FnZXMuanMnXG5pbXBvcnQgeyBnZXRNYWluTG9vcE1vZGVsIH0gZnJvbSAnLi4vLi4vdXRpbHMvbW9kZWwvbW9kZWwuanMnXG5pbXBvcnQgeyBnZXRJbml0aWFsU2V0dGluZ3MgfSBmcm9tICcuLi8uLi91dGlscy9zZXR0aW5ncy9zZXR0aW5ncy5qcydcbmltcG9ydCB7IGxvZ09UZWxFdmVudCB9IGZyb20gJy4uLy4uL3V0aWxzL3RlbGVtZXRyeS9ldmVudHMuanMnXG5pbXBvcnQge1xuICBzdWJtaXRUcmFuc2NyaXB0U2hhcmUsXG4gIHR5cGUgVHJhbnNjcmlwdFNoYXJlVHJpZ2dlcixcbn0gZnJvbSAnLi9zdWJtaXRUcmFuc2NyaXB0U2hhcmUuanMnXG5pbXBvcnQgdHlwZSB7IFRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlIH0gZnJvbSAnLi9UcmFuc2NyaXB0U2hhcmVQcm9tcHQuanMnXG5pbXBvcnQgeyB1c2VTdXJ2ZXlTdGF0ZSB9IGZyb20gJy4vdXNlU3VydmV5U3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UsIEZlZWRiYWNrU3VydmV5VHlwZSB9IGZyb20gJy4vdXRpbHMuanMnXG5cbnR5cGUgRmVlZGJhY2tTdXJ2ZXlDb25maWcgPSB7XG4gIG1pblRpbWVCZWZvcmVGZWVkYmFja01zOiBudW1iZXJcbiAgbWluVGltZUJldHdlZW5GZWVkYmFja01zOiBudW1iZXJcbiAgbWluVGltZUJldHdlZW5HbG9iYWxGZWVkYmFja01zOiBudW1iZXJcbiAgbWluVXNlclR1cm5zQmVmb3JlRmVlZGJhY2s6IG51bWJlclxuICBtaW5Vc2VyVHVybnNCZXR3ZWVuRmVlZGJhY2s6IG51bWJlclxuICBoaWRlVGhhbmtzQWZ0ZXJNczogbnVtYmVyXG4gIG9uRm9yTW9kZWxzOiBzdHJpbmdbXVxuICBwcm9iYWJpbGl0eTogbnVtYmVyXG59XG5cbnR5cGUgVHJhbnNjcmlwdEFza0NvbmZpZyA9IHtcbiAgcHJvYmFiaWxpdHk6IG51bWJlclxufVxuXG5jb25zdCBERUZBVUxUX0ZFRURCQUNLX1NVUlZFWV9DT05GSUc6IEZlZWRiYWNrU3VydmV5Q29uZmlnID0ge1xuICBtaW5UaW1lQmVmb3JlRmVlZGJhY2tNczogNjAwMDAwLFxuICBtaW5UaW1lQmV0d2VlbkZlZWRiYWNrTXM6IDM2MDAwMDAsXG4gIG1pblRpbWVCZXR3ZWVuR2xvYmFsRmVlZGJhY2tNczogMTAwMDAwMDAwLFxuICBtaW5Vc2VyVHVybnNCZWZvcmVGZWVkYmFjazogNSxcbiAgbWluVXNlclR1cm5zQmV0d2VlbkZlZWRiYWNrOiAxMCxcbiAgaGlkZVRoYW5rc0FmdGVyTXM6IDMwMDAsXG4gIG9uRm9yTW9kZWxzOiBbJyonXSxcbiAgcHJvYmFiaWxpdHk6IDAuMDA1LFxufVxuXG5jb25zdCBERUZBVUxUX1RSQU5TQ1JJUFRfQVNLX0NPTkZJRzogVHJhbnNjcmlwdEFza0NvbmZpZyA9IHtcbiAgcHJvYmFiaWxpdHk6IDAsXG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VGZWVkYmFja1N1cnZleShcbiAgbWVzc2FnZXM6IE1lc3NhZ2VbXSxcbiAgaXNMb2FkaW5nOiBib29sZWFuLFxuICBzdWJtaXRDb3VudDogbnVtYmVyLFxuICBzdXJ2ZXlUeXBlOiBGZWVkYmFja1N1cnZleVR5cGUgPSAnc2Vzc2lvbicsXG4gIGhhc0FjdGl2ZVByb21wdDogYm9vbGVhbiA9IGZhbHNlLFxuKToge1xuICBzdGF0ZTpcbiAgICB8ICdjbG9zZWQnXG4gICAgfCAnb3BlbidcbiAgICB8ICd0aGFua3MnXG4gICAgfCAndHJhbnNjcmlwdF9wcm9tcHQnXG4gICAgfCAnc3VibWl0dGluZydcbiAgICB8ICdzdWJtaXR0ZWQnXG4gIGxhc3RSZXNwb25zZTogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSB8IG51bGxcbiAgaGFuZGxlU2VsZWN0OiAoc2VsZWN0ZWQ6IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UpID0+IGJvb2xlYW5cbiAgaGFuZGxlVHJhbnNjcmlwdFNlbGVjdDogKHNlbGVjdGVkOiBUcmFuc2NyaXB0U2hhcmVSZXNwb25zZSkgPT4gdm9pZFxufSB7XG4gIGNvbnN0IGxhc3RBc3Npc3RhbnRNZXNzYWdlSWRSZWYgPSB1c2VSZWYoJ3Vua25vd24nKVxuICBsYXN0QXNzaXN0YW50TWVzc2FnZUlkUmVmLmN1cnJlbnQgPVxuICAgIGdldExhc3RBc3Npc3RhbnRNZXNzYWdlKG1lc3NhZ2VzKT8ubWVzc2FnZT8uaWQgfHwgJ3Vua25vd24nXG4gIGNvbnN0IFtmZWVkYmFja1N1cnZleSwgc2V0RmVlZGJhY2tTdXJ2ZXldID0gdXNlU3RhdGU8e1xuICAgIHRpbWVMYXN0U2hvd246IG51bWJlciB8IG51bGxcbiAgICBzdWJtaXRDb3VudEF0TGFzdEFwcGVhcmFuY2U6IG51bWJlciB8IG51bGxcbiAgfT4oKCkgPT4gKHsgdGltZUxhc3RTaG93bjogbnVsbCwgc3VibWl0Q291bnRBdExhc3RBcHBlYXJhbmNlOiBudWxsIH0pKVxuICBjb25zdCBjb25maWcgPSB1c2VEeW5hbWljQ29uZmlnPEZlZWRiYWNrU3VydmV5Q29uZmlnPihcbiAgICAndGVuZ3VfZmVlZGJhY2tfc3VydmV5X2NvbmZpZycsXG4gICAgREVGQVVMVF9GRUVEQkFDS19TVVJWRVlfQ09ORklHLFxuICApXG4gIGNvbnN0IGJhZFRyYW5zY3JpcHRBc2tDb25maWcgPSB1c2VEeW5hbWljQ29uZmlnPFRyYW5zY3JpcHRBc2tDb25maWc+KFxuICAgICd0ZW5ndV9iYWRfc3VydmV5X3RyYW5zY3JpcHRfYXNrX2NvbmZpZycsXG4gICAgREVGQVVMVF9UUkFOU0NSSVBUX0FTS19DT05GSUcsXG4gIClcbiAgY29uc3QgZ29vZFRyYW5zY3JpcHRBc2tDb25maWcgPSB1c2VEeW5hbWljQ29uZmlnPFRyYW5zY3JpcHRBc2tDb25maWc+KFxuICAgICd0ZW5ndV9nb29kX3N1cnZleV90cmFuc2NyaXB0X2Fza19jb25maWcnLFxuICAgIERFRkFVTFRfVFJBTlNDUklQVF9BU0tfQ09ORklHLFxuICApXG4gIGNvbnN0IHNldHRpbmdzUmF0ZSA9IGdldEluaXRpYWxTZXR0aW5ncygpLmZlZWRiYWNrU3VydmV5UmF0ZVxuICBjb25zdCBzZXNzaW9uU3RhcnRUaW1lID0gdXNlUmVmKERhdGUubm93KCkpXG4gIGNvbnN0IHN1Ym1pdENvdW50QXRTZXNzaW9uU3RhcnQgPSB1c2VSZWYoc3VibWl0Q291bnQpXG4gIGNvbnN0IHN1Ym1pdENvdW50UmVmID0gdXNlUmVmKHN1Ym1pdENvdW50KVxuICBzdWJtaXRDb3VudFJlZi5jdXJyZW50ID0gc3VibWl0Q291bnRcbiAgY29uc3QgbWVzc2FnZXNSZWYgPSB1c2VSZWYobWVzc2FnZXMpXG4gIG1lc3NhZ2VzUmVmLmN1cnJlbnQgPSBtZXNzYWdlc1xuICAvLyBQcm9iYWJpbGl0eSBnYXRlOiByb2xsIG9uY2Ugd2hlbiBlbGlnaWJpbGl0eSBjb25kaXRpb25zIGFyZSBtZXQsIG5vdCBvbiBldmVyeVxuICAvLyB1c2VNZW1vIHJlLWV2YWx1YXRpb24uIFdpdGhvdXQgdGhpcywgZWFjaCBkZXBlbmRlbmN5IGNoYW5nZSAoc3VibWl0Q291bnQsXG4gIC8vIGlzTG9hZGluZyB0b2dnbGUsIGV0Yy4pIHJlLXJvbGxzIE1hdGgucmFuZG9tKCksIG1ha2luZyB0aGUgc3VydmV5IGFsbW9zdFxuICAvLyBjZXJ0YWluIHRvIGFwcGVhciBhZnRlciBlbm91Z2ggcmVuZGVycy5cbiAgY29uc3QgcHJvYmFiaWxpdHlQYXNzZWRSZWYgPSB1c2VSZWYoZmFsc2UpXG4gIGNvbnN0IGxhc3RFbGlnaWJsZVN1Ym1pdENvdW50UmVmID0gdXNlUmVmPG51bWJlciB8IG51bGw+KG51bGwpXG5cbiAgY29uc3QgdXBkYXRlTGFzdFNob3duVGltZSA9IHVzZUNhbGxiYWNrKFxuICAgICh0aW1lc3RhbXA6IG51bWJlciwgc3VibWl0Q291bnRWYWx1ZTogbnVtYmVyKSA9PiB7XG4gICAgICBzZXRGZWVkYmFja1N1cnZleShwcmV2ID0+IHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIHByZXYudGltZUxhc3RTaG93biA9PT0gdGltZXN0YW1wICYmXG4gICAgICAgICAgcHJldi5zdWJtaXRDb3VudEF0TGFzdEFwcGVhcmFuY2UgPT09IHN1Ym1pdENvdW50VmFsdWVcbiAgICAgICAgKSB7XG4gICAgICAgICAgcmV0dXJuIHByZXZcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHRpbWVMYXN0U2hvd246IHRpbWVzdGFtcCxcbiAgICAgICAgICBzdWJtaXRDb3VudEF0TGFzdEFwcGVhcmFuY2U6IHN1Ym1pdENvdW50VmFsdWUsXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAvLyBQZXJzaXN0IGNyb3NzLXNlc3Npb24gcGFjaW5nIHN0YXRlIChwcmV2aW91c2x5IGRvbmUgYnkgb25DaGFuZ2VBcHBTdGF0ZSBvYnNlcnZlcilcbiAgICAgIGlmIChnZXRHbG9iYWxDb25maWcoKS5mZWVkYmFja1N1cnZleVN0YXRlPy5sYXN0U2hvd25UaW1lICE9PSB0aW1lc3RhbXApIHtcbiAgICAgICAgc2F2ZUdsb2JhbENvbmZpZyhjdXJyZW50ID0+ICh7XG4gICAgICAgICAgLi4uY3VycmVudCxcbiAgICAgICAgICBmZWVkYmFja1N1cnZleVN0YXRlOiB7XG4gICAgICAgICAgICBsYXN0U2hvd25UaW1lOiB0aW1lc3RhbXAsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSkpXG4gICAgICB9XG4gICAgfSxcbiAgICBbXSxcbiAgKVxuXG4gIGNvbnN0IG9uT3BlbiA9IHVzZUNhbGxiYWNrKFxuICAgIChhcHBlYXJhbmNlSWQ6IHN0cmluZykgPT4ge1xuICAgICAgdXBkYXRlTGFzdFNob3duVGltZShEYXRlLm5vdygpLCBzdWJtaXRDb3VudFJlZi5jdXJyZW50KVxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2ZlZWRiYWNrX3N1cnZleV9ldmVudCcsIHtcbiAgICAgICAgZXZlbnRfdHlwZTpcbiAgICAgICAgICAnYXBwZWFyZWQnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGFwcGVhcmFuY2VfaWQ6XG4gICAgICAgICAgYXBwZWFyYW5jZUlkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGxhc3RfYXNzaXN0YW50X21lc3NhZ2VfaWQ6XG4gICAgICAgICAgbGFzdEFzc2lzdGFudE1lc3NhZ2VJZFJlZi5jdXJyZW50IGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHN1cnZleV90eXBlOlxuICAgICAgICAgIHN1cnZleVR5cGUgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgIH0pXG4gICAgICB2b2lkIGxvZ09UZWxFdmVudCgnZmVlZGJhY2tfc3VydmV5Jywge1xuICAgICAgICBldmVudF90eXBlOiAnYXBwZWFyZWQnLFxuICAgICAgICBhcHBlYXJhbmNlX2lkOiBhcHBlYXJhbmNlSWQsXG4gICAgICAgIHN1cnZleV90eXBlOiBzdXJ2ZXlUeXBlLFxuICAgICAgfSlcbiAgICB9LFxuICAgIFt1cGRhdGVMYXN0U2hvd25UaW1lLCBzdXJ2ZXlUeXBlXSxcbiAgKVxuXG4gIGNvbnN0IG9uU2VsZWN0ID0gdXNlQ2FsbGJhY2soXG4gICAgKGFwcGVhcmFuY2VJZDogc3RyaW5nLCBzZWxlY3RlZDogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSkgPT4ge1xuICAgICAgdXBkYXRlTGFzdFNob3duVGltZShEYXRlLm5vdygpLCBzdWJtaXRDb3VudFJlZi5jdXJyZW50KVxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2ZlZWRiYWNrX3N1cnZleV9ldmVudCcsIHtcbiAgICAgICAgZXZlbnRfdHlwZTpcbiAgICAgICAgICAncmVzcG9uZGVkJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBhcHBlYXJhbmNlX2lkOlxuICAgICAgICAgIGFwcGVhcmFuY2VJZCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICByZXNwb25zZTpcbiAgICAgICAgICBzZWxlY3RlZCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBsYXN0X2Fzc2lzdGFudF9tZXNzYWdlX2lkOlxuICAgICAgICAgIGxhc3RBc3Npc3RhbnRNZXNzYWdlSWRSZWYuY3VycmVudCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBzdXJ2ZXlfdHlwZTpcbiAgICAgICAgICBzdXJ2ZXlUeXBlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KVxuICAgICAgdm9pZCBsb2dPVGVsRXZlbnQoJ2ZlZWRiYWNrX3N1cnZleScsIHtcbiAgICAgICAgZXZlbnRfdHlwZTogJ3Jlc3BvbmRlZCcsXG4gICAgICAgIGFwcGVhcmFuY2VfaWQ6IGFwcGVhcmFuY2VJZCxcbiAgICAgICAgcmVzcG9uc2U6IHNlbGVjdGVkLFxuICAgICAgICBzdXJ2ZXlfdHlwZTogc3VydmV5VHlwZSxcbiAgICAgIH0pXG4gICAgfSxcbiAgICBbdXBkYXRlTGFzdFNob3duVGltZSwgc3VydmV5VHlwZV0sXG4gIClcblxuICBjb25zdCBzaG91bGRTaG93VHJhbnNjcmlwdFByb21wdCA9IHVzZUNhbGxiYWNrKFxuICAgIChzZWxlY3RlZDogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSkgPT4ge1xuICAgICAgLy8gT25seSBiYWQgYW5kIGdvb2QgcmF0aW5ncyB0cmlnZ2VyIHRoZSB0cmFuc2NyaXB0IGFza1xuICAgICAgaWYgKHNlbGVjdGVkICE9PSAnYmFkJyAmJiBzZWxlY3RlZCAhPT0gJ2dvb2QnKSB7XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgfVxuXG4gICAgICAvLyBEb24ndCBzaG93IGlmIHVzZXIgcHJldmlvdXNseSBjaG9zZSBcIkRvbid0IGFzayBhZ2FpblwiXG4gICAgICBpZiAoZ2V0R2xvYmFsQ29uZmlnKCkudHJhbnNjcmlwdFNoYXJlRGlzbWlzc2VkKSB7XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgfVxuXG4gICAgICAvLyBEb24ndCBzaG93IGlmIHByb2R1Y3QgZmVlZGJhY2sgaXMgYmxvY2tlZCBieSBvcmcgcG9saWN5IChaRFIpXG4gICAgICBpZiAoIWlzUG9saWN5QWxsb3dlZCgnYWxsb3dfcHJvZHVjdF9mZWVkYmFjaycpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgfVxuXG4gICAgICAvLyBQcm9iYWJpbGl0eSBnYXRlIGZyb20gR3Jvd3RoQm9vayBjb25maWcgKHNlcGFyYXRlIHBlciByYXRpbmcpXG4gICAgICBjb25zdCBwcm9iYWJpbGl0eSA9XG4gICAgICAgIHNlbGVjdGVkID09PSAnYmFkJ1xuICAgICAgICAgID8gYmFkVHJhbnNjcmlwdEFza0NvbmZpZy5wcm9iYWJpbGl0eVxuICAgICAgICAgIDogZ29vZFRyYW5zY3JpcHRBc2tDb25maWcucHJvYmFiaWxpdHlcbiAgICAgIHJldHVybiBNYXRoLnJhbmRvbSgpIDw9IHByb2JhYmlsaXR5XG4gICAgfSxcbiAgICBbYmFkVHJhbnNjcmlwdEFza0NvbmZpZy5wcm9iYWJpbGl0eSwgZ29vZFRyYW5zY3JpcHRBc2tDb25maWcucHJvYmFiaWxpdHldLFxuICApXG5cbiAgY29uc3Qgb25UcmFuc2NyaXB0UHJvbXB0U2hvd24gPSB1c2VDYWxsYmFjayhcbiAgICAoYXBwZWFyYW5jZUlkOiBzdHJpbmcsIHN1cnZleVJlc3BvbnNlOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlKSA9PiB7XG4gICAgICBjb25zdCB0cmlnZ2VyOiBUcmFuc2NyaXB0U2hhcmVUcmlnZ2VyID1cbiAgICAgICAgc3VydmV5UmVzcG9uc2UgPT09ICdnb29kJ1xuICAgICAgICAgID8gJ2dvb2RfZmVlZGJhY2tfc3VydmV5J1xuICAgICAgICAgIDogJ2JhZF9mZWVkYmFja19zdXJ2ZXknXG4gICAgICBsb2dFdmVudCgndGVuZ3VfZmVlZGJhY2tfc3VydmV5X2V2ZW50Jywge1xuICAgICAgICBldmVudF90eXBlOlxuICAgICAgICAgICd0cmFuc2NyaXB0X3Byb21wdF9hcHBlYXJlZCcgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgYXBwZWFyYW5jZV9pZDpcbiAgICAgICAgICBhcHBlYXJhbmNlSWQgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgbGFzdF9hc3Npc3RhbnRfbWVzc2FnZV9pZDpcbiAgICAgICAgICBsYXN0QXNzaXN0YW50TWVzc2FnZUlkUmVmLmN1cnJlbnQgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgc3VydmV5X3R5cGU6XG4gICAgICAgICAgc3VydmV5VHlwZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICB0cmlnZ2VyOlxuICAgICAgICAgIHRyaWdnZXIgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgIH0pXG4gICAgICB2b2lkIGxvZ09UZWxFdmVudCgnZmVlZGJhY2tfc3VydmV5Jywge1xuICAgICAgICBldmVudF90eXBlOiAndHJhbnNjcmlwdF9wcm9tcHRfYXBwZWFyZWQnLFxuICAgICAgICBhcHBlYXJhbmNlX2lkOiBhcHBlYXJhbmNlSWQsXG4gICAgICAgIHN1cnZleV90eXBlOiBzdXJ2ZXlUeXBlLFxuICAgICAgfSlcbiAgICB9LFxuICAgIFtzdXJ2ZXlUeXBlXSxcbiAgKVxuXG4gIGNvbnN0IG9uVHJhbnNjcmlwdFNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jIChcbiAgICAgIGFwcGVhcmFuY2VJZDogc3RyaW5nLFxuICAgICAgc2VsZWN0ZWQ6IFRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlLFxuICAgICAgc3VydmV5UmVzcG9uc2U6IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UgfCBudWxsLFxuICAgICk6IFByb21pc2U8Ym9vbGVhbj4gPT4ge1xuICAgICAgY29uc3QgdHJpZ2dlcjogVHJhbnNjcmlwdFNoYXJlVHJpZ2dlciA9XG4gICAgICAgIHN1cnZleVJlc3BvbnNlID09PSAnZ29vZCdcbiAgICAgICAgICA/ICdnb29kX2ZlZWRiYWNrX3N1cnZleSdcbiAgICAgICAgICA6ICdiYWRfZmVlZGJhY2tfc3VydmV5J1xuXG4gICAgICBsb2dFdmVudCgndGVuZ3VfZmVlZGJhY2tfc3VydmV5X2V2ZW50Jywge1xuICAgICAgICBldmVudF90eXBlOlxuICAgICAgICAgIGB0cmFuc2NyaXB0X3NoYXJlXyR7c2VsZWN0ZWR9YCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBhcHBlYXJhbmNlX2lkOlxuICAgICAgICAgIGFwcGVhcmFuY2VJZCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBsYXN0X2Fzc2lzdGFudF9tZXNzYWdlX2lkOlxuICAgICAgICAgIGxhc3RBc3Npc3RhbnRNZXNzYWdlSWRSZWYuY3VycmVudCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBzdXJ2ZXlfdHlwZTpcbiAgICAgICAgICBzdXJ2ZXlUeXBlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHRyaWdnZXI6XG4gICAgICAgICAgdHJpZ2dlciBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcblxuICAgICAgaWYgKHNlbGVjdGVkID09PSAnZG9udF9hc2tfYWdhaW4nKSB7XG4gICAgICAgIHNhdmVHbG9iYWxDb25maWcoY3VycmVudCA9PiAoe1xuICAgICAgICAgIC4uLmN1cnJlbnQsXG4gICAgICAgICAgdHJhbnNjcmlwdFNoYXJlRGlzbWlzc2VkOiB0cnVlLFxuICAgICAgICB9KSlcbiAgICAgIH1cblxuICAgICAgaWYgKHNlbGVjdGVkID09PSAneWVzJykge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzdWJtaXRUcmFuc2NyaXB0U2hhcmUoXG4gICAgICAgICAgbWVzc2FnZXNSZWYuY3VycmVudCxcbiAgICAgICAgICB0cmlnZ2VyLFxuICAgICAgICAgIGFwcGVhcmFuY2VJZCxcbiAgICAgICAgKVxuICAgICAgICBsb2dFdmVudCgndGVuZ3VfZmVlZGJhY2tfc3VydmV5X2V2ZW50Jywge1xuICAgICAgICAgIGV2ZW50X3R5cGU6IChyZXN1bHQuc3VjY2Vzc1xuICAgICAgICAgICAgPyAndHJhbnNjcmlwdF9zaGFyZV9zdWJtaXR0ZWQnXG4gICAgICAgICAgICA6ICd0cmFuc2NyaXB0X3NoYXJlX2ZhaWxlZCcpIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgYXBwZWFyYW5jZV9pZDpcbiAgICAgICAgICAgIGFwcGVhcmFuY2VJZCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIHRyaWdnZXI6XG4gICAgICAgICAgICB0cmlnZ2VyIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVybiByZXN1bHQuc3VjY2Vzc1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9LFxuICAgIFtzdXJ2ZXlUeXBlXSxcbiAgKVxuXG4gIGNvbnN0IHsgc3RhdGUsIGxhc3RSZXNwb25zZSwgb3BlbiwgaGFuZGxlU2VsZWN0LCBoYW5kbGVUcmFuc2NyaXB0U2VsZWN0IH0gPVxuICAgIHVzZVN1cnZleVN0YXRlKHtcbiAgICAgIGhpZGVUaGFua3NBZnRlck1zOiBjb25maWcuaGlkZVRoYW5rc0FmdGVyTXMsXG4gICAgICBvbk9wZW4sXG4gICAgICBvblNlbGVjdCxcbiAgICAgIHNob3VsZFNob3dUcmFuc2NyaXB0UHJvbXB0LFxuICAgICAgb25UcmFuc2NyaXB0UHJvbXB0U2hvd24sXG4gICAgICBvblRyYW5zY3JpcHRTZWxlY3QsXG4gICAgfSlcblxuICBjb25zdCBjdXJyZW50TW9kZWwgPSBnZXRNYWluTG9vcE1vZGVsKClcbiAgY29uc3QgaXNNb2RlbEFsbG93ZWQgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBpZiAoY29uZmlnLm9uRm9yTW9kZWxzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuICAgIGlmIChjb25maWcub25Gb3JNb2RlbHMuaW5jbHVkZXMoJyonKSkge1xuICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG4gICAgcmV0dXJuIGNvbmZpZy5vbkZvck1vZGVscy5pbmNsdWRlcyhjdXJyZW50TW9kZWwpXG4gIH0sIFtjb25maWcub25Gb3JNb2RlbHMsIGN1cnJlbnRNb2RlbF0pXG5cbiAgY29uc3Qgc2hvdWxkT3BlbiA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmIChzdGF0ZSAhPT0gJ2Nsb3NlZCcpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGlmIChpc0xvYWRpbmcpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIC8vIERvbid0IHNob3cgc3VydmV5IHdoZW4gcGVybWlzc2lvbiBvciBhc2sgcXVlc3Rpb24gcHJvbXB0cyBhcmUgdmlzaWJsZVxuICAgIGlmIChoYXNBY3RpdmVQcm9tcHQpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIC8vIEZvcmNlIGRpc3BsYXkgZm9yIHRlc3RpbmdcbiAgICBpZiAoXG4gICAgICBwcm9jZXNzLmVudi5DTEFVREVfRk9SQ0VfRElTUExBWV9TVVJWRVkgJiZcbiAgICAgICFmZWVkYmFja1N1cnZleS50aW1lTGFzdFNob3duXG4gICAgKSB7XG4gICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cblxuICAgIGlmICghaXNNb2RlbEFsbG93ZWQpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGlmIChpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9ESVNBQkxFX0ZFRURCQUNLX1NVUlZFWSkpIHtcbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cblxuICAgIGlmIChpc0ZlZWRiYWNrU3VydmV5RGlzYWJsZWQoKSkge1xuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgaWYgcHJvZHVjdCBmZWVkYmFjayBpcyBhbGxvd2VkIGJ5IG9yZyBwb2xpY3lcbiAgICBpZiAoIWlzUG9saWN5QWxsb3dlZCgnYWxsb3dfcHJvZHVjdF9mZWVkYmFjaycpKSB7XG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICAvLyBDaGVjayBzZXNzaW9uLWxvY2FsIHBhY2luZ1xuICAgIGlmIChmZWVkYmFja1N1cnZleS50aW1lTGFzdFNob3duKSB7XG4gICAgICAvLyBDaGVjayB0aW1lIGVsYXBzZWQgc2luY2UgbGFzdCBhcHBlYXJhbmNlIGluIHRoaXMgc2Vzc2lvblxuICAgICAgY29uc3QgdGltZVNpbmNlTGFzdFNob3duID0gRGF0ZS5ub3coKSAtIGZlZWRiYWNrU3VydmV5LnRpbWVMYXN0U2hvd25cbiAgICAgIGlmICh0aW1lU2luY2VMYXN0U2hvd24gPCBjb25maWcubWluVGltZUJldHdlZW5GZWVkYmFja01zKSB7XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgfVxuICAgICAgLy8gQ2hlY2sgdXNlciB0dXJuIHJlcXVpcmVtZW50IGZvciBzdWJzZXF1ZW50IGFwcGVhcmFuY2VzXG4gICAgICBpZiAoXG4gICAgICAgIGZlZWRiYWNrU3VydmV5LnN1Ym1pdENvdW50QXRMYXN0QXBwZWFyYW5jZSAhPT0gbnVsbCAmJlxuICAgICAgICBzdWJtaXRDb3VudCA8XG4gICAgICAgICAgZmVlZGJhY2tTdXJ2ZXkuc3VibWl0Q291bnRBdExhc3RBcHBlYXJhbmNlICtcbiAgICAgICAgICAgIGNvbmZpZy5taW5Vc2VyVHVybnNCZXR3ZWVuRmVlZGJhY2tcbiAgICAgICkge1xuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gRmlyc3QgYXBwZWFyYW5jZSBpbiB0aGlzIHNlc3Npb25cbiAgICAgIGNvbnN0IHRpbWVTaW5jZVNlc3Npb25TdGFydCA9IERhdGUubm93KCkgLSBzZXNzaW9uU3RhcnRUaW1lLmN1cnJlbnRcbiAgICAgIGlmICh0aW1lU2luY2VTZXNzaW9uU3RhcnQgPCBjb25maWcubWluVGltZUJlZm9yZUZlZWRiYWNrTXMpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgICBpZiAoXG4gICAgICAgIHN1Ym1pdENvdW50IDxcbiAgICAgICAgc3VibWl0Q291bnRBdFNlc3Npb25TdGFydC5jdXJyZW50ICsgY29uZmlnLm1pblVzZXJUdXJuc0JlZm9yZUZlZWRiYWNrXG4gICAgICApIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gUHJvYmFiaWxpdHkgY2hlY2s6IHJvbGwgb25jZSBwZXIgZWxpZ2liaWxpdHkgd2luZG93IHRvIGF2b2lkIHJlLXJvbGxpbmdcbiAgICAvLyBvbiBldmVyeSB1c2VNZW1vIHJlLWV2YWx1YXRpb24gKHdoaWNoIHdvdWxkIG1ha2UgdHJpZ2dlcmluZyBuZWFyLWNlcnRhaW4pLlxuICAgIGlmIChsYXN0RWxpZ2libGVTdWJtaXRDb3VudFJlZi5jdXJyZW50ICE9PSBzdWJtaXRDb3VudCkge1xuICAgICAgbGFzdEVsaWdpYmxlU3VibWl0Q291bnRSZWYuY3VycmVudCA9IHN1Ym1pdENvdW50XG4gICAgICBwcm9iYWJpbGl0eVBhc3NlZFJlZi5jdXJyZW50ID1cbiAgICAgICAgTWF0aC5yYW5kb20oKSA8PSAoc2V0dGluZ3NSYXRlID8/IGNvbmZpZy5wcm9iYWJpbGl0eSlcbiAgICB9XG4gICAgaWYgKCFwcm9iYWJpbGl0eVBhc3NlZFJlZi5jdXJyZW50KSB7XG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICAvLyBDaGVjayBnbG9iYWwgcGFjaW5nIChhY3Jvc3MgYWxsIHNlc3Npb25zKVxuICAgIC8vIExlYXZlIHRoaXMgdGlsbCBsYXN0IGJlY2F1c2UgaXQgcmVhZHMgZnJvbSB0aGUgZmlsZXN5c3RlbSB3aGljaCBpcyBleHBlbnNpdmUuXG4gICAgY29uc3QgZ2xvYmFsRmVlZGJhY2tTdGF0ZSA9IGdldEdsb2JhbENvbmZpZygpLmZlZWRiYWNrU3VydmV5U3RhdGVcbiAgICBpZiAoZ2xvYmFsRmVlZGJhY2tTdGF0ZT8ubGFzdFNob3duVGltZSkge1xuICAgICAgY29uc3QgdGltZVNpbmNlR2xvYmFsTGFzdFNob3duID1cbiAgICAgICAgRGF0ZS5ub3coKSAtIGdsb2JhbEZlZWRiYWNrU3RhdGUubGFzdFNob3duVGltZVxuICAgICAgaWYgKHRpbWVTaW5jZUdsb2JhbExhc3RTaG93biA8IGNvbmZpZy5taW5UaW1lQmV0d2Vlbkdsb2JhbEZlZWRiYWNrTXMpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHRydWVcbiAgfSwgW1xuICAgIHN0YXRlLFxuICAgIGlzTG9hZGluZyxcbiAgICBoYXNBY3RpdmVQcm9tcHQsXG4gICAgaXNNb2RlbEFsbG93ZWQsXG4gICAgZmVlZGJhY2tTdXJ2ZXkudGltZUxhc3RTaG93bixcbiAgICBmZWVkYmFja1N1cnZleS5zdWJtaXRDb3VudEF0TGFzdEFwcGVhcmFuY2UsXG4gICAgc3VibWl0Q291bnQsXG4gICAgY29uZmlnLm1pblRpbWVCZXR3ZWVuRmVlZGJhY2tNcyxcbiAgICBjb25maWcubWluVGltZUJldHdlZW5HbG9iYWxGZWVkYmFja01zLFxuICAgIGNvbmZpZy5taW5Vc2VyVHVybnNCZXR3ZWVuRmVlZGJhY2ssXG4gICAgY29uZmlnLm1pblRpbWVCZWZvcmVGZWVkYmFja01zLFxuICAgIGNvbmZpZy5taW5Vc2VyVHVybnNCZWZvcmVGZWVkYmFjayxcbiAgICBjb25maWcucHJvYmFiaWxpdHksXG4gICAgc2V0dGluZ3NSYXRlLFxuICBdKVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKHNob3VsZE9wZW4pIHtcbiAgICAgIG9wZW4oKVxuICAgIH1cbiAgfSwgW3Nob3VsZE9wZW4sIG9wZW5dKVxuXG4gIHJldHVybiB7IHN0YXRlLCBsYXN0UmVzcG9uc2UsIGhhbmRsZVNlbGVjdCwgaGFuZGxlVHJhbnNjcmlwdFNlbGVjdCB9XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLE1BQU0sRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDekUsU0FBU0MsZ0JBQWdCLFFBQVEsK0JBQStCO0FBQ2hFLFNBQVNDLHdCQUF3QixRQUFRLGtDQUFrQztBQUMzRSxTQUNFLEtBQUtDLDBEQUEwRCxFQUMvREMsUUFBUSxRQUNILGlDQUFpQztBQUN4QyxTQUFTQyxlQUFlLFFBQVEsc0NBQXNDO0FBQ3RFLGNBQWNDLE9BQU8sUUFBUSx3QkFBd0I7QUFDckQsU0FBU0MsZUFBZSxFQUFFQyxnQkFBZ0IsUUFBUSx1QkFBdUI7QUFDekUsU0FBU0MsV0FBVyxRQUFR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 \ No newline at end of file diff --git a/components/FeedbackSurvey/useMemorySurvey.tsx b/components/FeedbackSurvey/useMemorySurvey.tsx new file mode 100644 index 0000000..e7d9b18 --- /dev/null +++ b/components/FeedbackSurvey/useMemorySurvey.tsx @@ -0,0 +1,213 @@ +import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { isFeedbackSurveyDisabled } from 'src/services/analytics/config.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { isAutoMemoryEnabled } from '../../memdir/paths.js'; +import { isPolicyAllowed } from '../../services/policyLimits/index.js'; +import { FILE_READ_TOOL_NAME } from '../../tools/FileReadTool/prompt.js'; +import type { Message } from '../../types/message.js'; +import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'; +import { isEnvTruthy } from '../../utils/envUtils.js'; +import { isAutoManagedMemoryFile } from '../../utils/memoryFileDetection.js'; +import { extractTextContent, getLastAssistantMessage } from '../../utils/messages.js'; +import { logOTelEvent } from '../../utils/telemetry/events.js'; +import { submitTranscriptShare } from './submitTranscriptShare.js'; +import type { TranscriptShareResponse } from './TranscriptSharePrompt.js'; +import { useSurveyState } from './useSurveyState.js'; +import type { FeedbackSurveyResponse } from './utils.js'; +const HIDE_THANKS_AFTER_MS = 3000; +const MEMORY_SURVEY_GATE = 'tengu_dunwich_bell'; +const MEMORY_SURVEY_EVENT = 'tengu_memory_survey_event'; +const SURVEY_PROBABILITY = 0.2; +const TRANSCRIPT_SHARE_TRIGGER = 'memory_survey'; +const MEMORY_WORD_RE = /\bmemor(?:y|ies)\b/i; +function hasMemoryFileRead(messages: Message[]): boolean { + for (const message of messages) { + if (message.type !== 'assistant') { + continue; + } + const content = message.message.content; + if (!Array.isArray(content)) { + continue; + } + for (const block of content) { + if (block.type !== 'tool_use' || block.name !== FILE_READ_TOOL_NAME) { + continue; + } + const input = block.input as { + file_path?: unknown; + }; + if (typeof input.file_path === 'string' && isAutoManagedMemoryFile(input.file_path)) { + return true; + } + } + } + return false; +} +export function useMemorySurvey(messages: Message[], isLoading: boolean, hasActivePrompt = false, { + enabled = true +}: { + enabled?: boolean; +} = {}): { + state: 'closed' | 'open' | 'thanks' | 'transcript_prompt' | 'submitting' | 'submitted'; + lastResponse: FeedbackSurveyResponse | null; + handleSelect: (selected: FeedbackSurveyResponse) => void; + handleTranscriptSelect: (selected: TranscriptShareResponse) => void; +} { + // Track assistant message UUIDs that were already evaluated so we don't + // re-roll probability on re-renders or re-scan messages for the same turn. + const seenAssistantUuids = useRef>(new Set()); + // Once a memory file read is observed it stays true for the session — + // skip the O(n) scan on subsequent turns. + const memoryReadSeen = useRef(false); + const messagesRef = useRef(messages); + messagesRef.current = messages; + const onOpen = useCallback((appearanceId: string) => { + logEvent(MEMORY_SURVEY_EVENT, { + event_type: 'appeared' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + void logOTelEvent('feedback_survey', { + event_type: 'appeared', + appearance_id: appearanceId, + survey_type: 'memory' + }); + }, []); + const onSelect = useCallback((appearanceId_0: string, selected: FeedbackSurveyResponse) => { + logEvent(MEMORY_SURVEY_EVENT, { + event_type: 'responded' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_0 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + response: selected as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + void logOTelEvent('feedback_survey', { + event_type: 'responded', + appearance_id: appearanceId_0, + response: selected, + survey_type: 'memory' + }); + }, []); + const shouldShowTranscriptPrompt = useCallback((selected_0: FeedbackSurveyResponse) => { + if ("external" !== 'ant') { + return false; + } + if (selected_0 !== 'bad' && selected_0 !== 'good') { + return false; + } + if (getGlobalConfig().transcriptShareDismissed) { + return false; + } + if (!isPolicyAllowed('allow_product_feedback')) { + return false; + } + return true; + }, []); + const onTranscriptPromptShown = useCallback((appearanceId_1: string) => { + logEvent(MEMORY_SURVEY_EVENT, { + event_type: 'transcript_prompt_appeared' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_1 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + trigger: TRANSCRIPT_SHARE_TRIGGER as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + void logOTelEvent('feedback_survey', { + event_type: 'transcript_prompt_appeared', + appearance_id: appearanceId_1, + survey_type: 'memory' + }); + }, []); + const onTranscriptSelect = useCallback(async (appearanceId_2: string, selected_1: TranscriptShareResponse): Promise => { + logEvent(MEMORY_SURVEY_EVENT, { + event_type: `transcript_share_${selected_1}` as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_2 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + trigger: TRANSCRIPT_SHARE_TRIGGER as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + if (selected_1 === 'dont_ask_again') { + saveGlobalConfig(current => ({ + ...current, + transcriptShareDismissed: true + })); + } + if (selected_1 === 'yes') { + const result = await submitTranscriptShare(messagesRef.current, TRANSCRIPT_SHARE_TRIGGER, appearanceId_2); + logEvent(MEMORY_SURVEY_EVENT, { + event_type: (result.success ? 'transcript_share_submitted' : 'transcript_share_failed') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_2 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + trigger: TRANSCRIPT_SHARE_TRIGGER as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + return result.success; + } + return false; + }, []); + const { + state, + lastResponse, + open, + handleSelect, + handleTranscriptSelect + } = useSurveyState({ + hideThanksAfterMs: HIDE_THANKS_AFTER_MS, + onOpen, + onSelect, + shouldShowTranscriptPrompt, + onTranscriptPromptShown, + onTranscriptSelect + }); + const lastAssistant = useMemo(() => getLastAssistantMessage(messages), [messages]); + useEffect(() => { + if (!enabled) return; + + // /clear resets messages but REPL stays mounted — reset refs so a memory + // read from the previous conversation doesn't leak into the new one. + if (messages.length === 0) { + memoryReadSeen.current = false; + seenAssistantUuids.current.clear(); + return; + } + if (state !== 'closed' || isLoading || hasActivePrompt) { + return; + } + + // 3P default: survey off (no GrowthBook on Bedrock/Vertex/Foundry). + if (!getFeatureValue_CACHED_MAY_BE_STALE(MEMORY_SURVEY_GATE, false)) { + return; + } + if (!isAutoMemoryEnabled()) { + return; + } + if (isFeedbackSurveyDisabled()) { + return; + } + if (!isPolicyAllowed('allow_product_feedback')) { + return; + } + if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY)) { + return; + } + if (!lastAssistant || seenAssistantUuids.current.has(lastAssistant.uuid)) { + return; + } + const text = extractTextContent(lastAssistant.message.content, ' '); + if (!MEMORY_WORD_RE.test(text)) { + return; + } + + // Mark as evaluated before the memory-read scan so a turn that mentions + // "memory" but has no memory read doesn't trigger repeated O(n) scans + // on subsequent renders with the same last assistant message. + seenAssistantUuids.current.add(lastAssistant.uuid); + if (!memoryReadSeen.current) { + memoryReadSeen.current = hasMemoryFileRead(messages); + } + if (!memoryReadSeen.current) { + return; + } + if (Math.random() < SURVEY_PROBABILITY) { + open(); + } + }, [enabled, state, isLoading, hasActivePrompt, lastAssistant, messages, open]); + return { + state, + lastResponse, + handleSelect, + handleTranscriptSelect + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1c2VDYWxsYmFjayIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJ1c2VSZWYiLCJpc0ZlZWRiYWNrU3VydmV5RGlzYWJsZWQiLCJnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsImlzQXV0b01lbW9yeUVuYWJsZWQiLCJpc1BvbGljeUFsbG93ZWQiLCJGSUxFX1JFQURfVE9PTF9OQU1FIiwiTWVzc2FnZSIsImdldEdsb2JhbENvbmZpZyIsInNhdmVHbG9iYWxDb25maWciLCJpc0VudlRydXRoeSIsImlzQXV0b01hbmFnZWRNZW1vcnlGaWxlIiwiZXh0cmFjdFRleHRDb250ZW50IiwiZ2V0TGFzdEFzc2lzdGFudE1lc3NhZ2UiLCJsb2dPVGVsRXZlbnQiLCJzdWJtaXRUcmFuc2NyaXB0U2hhcmUiLCJUcmFuc2NyaXB0U2hhcmVSZXNwb25zZSIsInVzZVN1cnZleVN0YXRlIiwiRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSIsIkhJREVfVEhBTktTX0FGVEVSX01TIiwiTUVNT1JZX1NVUlZFWV9HQVRFIiwiTUVNT1JZX1NVUlZFWV9FVkVOVCIsIlNVUlZFWV9QUk9CQUJJTElUWSIsIlRSQU5TQ1JJUFRfU0hBUkVfVFJJR0dFUiIsIk1FTU9SWV9XT1JEX1JFIiwiaGFzTWVtb3J5RmlsZVJlYWQiLCJtZXNzYWdlcyIsIm1lc3NhZ2UiLCJ0eXBlIiwiY29udGVudCIsIkFycmF5IiwiaXNBcnJheSIsImJsb2NrIiwibmFtZSIsImlucHV0IiwiZmlsZV9wYXRoIiwidXNlTWVtb3J5U3VydmV5IiwiaXNMb2FkaW5nIiwiaGFzQWN0aXZlUHJvbXB0IiwiZW5hYmxlZCIsInN0YXRlIiwibGFzdFJlc3BvbnNlIiwiaGFuZGxlU2VsZWN0Iiwic2VsZWN0ZWQiLCJoYW5kbGVUcmFuc2NyaXB0U2VsZWN0Iiwic2VlbkFzc2lzdGFudFV1aWRzIiwiU2V0IiwibWVtb3J5UmVhZFNlZW4iLCJtZXNzYWdlc1JlZiIsImN1cnJlbnQiLCJvbk9wZW4iLCJhcHBlYXJhbmNlSWQiLCJldmVudF90eXBlIiwiYXBwZWFyYW5jZV9pZCIsInN1cnZleV90eXBlIiwib25TZWxlY3QiLCJyZXNwb25zZSIsInNob3VsZFNob3dUcmFuc2NyaXB0UHJvbXB0IiwidHJhbnNjcmlwdFNoYXJlRGlzbWlzc2VkIiwib25UcmFuc2NyaXB0UHJvbXB0U2hvd24iLCJ0cmlnZ2VyIiwib25UcmFuc2NyaXB0U2VsZWN0IiwiUHJvbWlzZSIsInJlc3VsdCIsInN1Y2Nlc3MiLCJvcGVuIiwiaGlkZVRoYW5rc0FmdGVyTXMiLCJsYXN0QXNzaXN0YW50IiwibGVuZ3RoIiwiY2xlYXIiLCJwcm9jZXNzIiwiZW52IiwiQ0xBVURFX0NPREVfRElTQUJMRV9GRUVEQkFDS19TVVJWRVkiLCJoYXMiLCJ1dWlkIiwidGV4dCIsInRlc3QiLCJhZGQiLCJNYXRoIiwicmFuZG9tIl0sInNvdXJjZXMiOlsidXNlTWVtb3J5U3VydmV5LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyB1c2VDYWxsYmFjaywgdXNlRWZmZWN0LCB1c2VNZW1vLCB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGlzRmVlZGJhY2tTdXJ2ZXlEaXNhYmxlZCB9IGZyb20gJ3NyYy9zZXJ2aWNlcy9hbmFseXRpY3MvY29uZmlnLmpzJ1xuaW1wb3J0IHsgZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUgfSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2dyb3d0aGJvb2suanMnXG5pbXBvcnQge1xuICB0eXBlIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIGxvZ0V2ZW50LFxufSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgaXNBdXRvTWVtb3J5RW5hYmxlZCB9IGZyb20gJy4uLy4uL21lbWRpci9wYXRocy5qcydcbmltcG9ydCB7IGlzUG9saWN5QWxsb3dlZCB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL3BvbGljeUxpbWl0cy9pbmRleC5qcydcbmltcG9ydCB7IEZJTEVfUkVBRF9UT09MX05BTUUgfSBmcm9tICcuLi8uLi90b29scy9GaWxlUmVhZFRvb2wvcHJvbXB0LmpzJ1xuaW1wb3J0IHR5cGUgeyBNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IGdldEdsb2JhbENvbmZpZywgc2F2ZUdsb2JhbENvbmZpZyB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGlzRW52VHJ1dGh5IH0gZnJvbSAnLi4vLi4vdXRpbHMvZW52VXRpbHMuanMnXG5pbXBvcnQgeyBpc0F1dG9NYW5hZ2VkTWVtb3J5RmlsZSB9IGZyb20gJy4uLy4uL3V0aWxzL21lbW9yeUZpbGVEZXRlY3Rpb24uanMnXG5pbXBvcnQge1xuICBleHRyYWN0VGV4dENvbnRlbnQsXG4gIGdldExhc3RBc3Npc3RhbnRNZXNzYWdlLFxufSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB7IGxvZ09UZWxFdmVudCB9IGZyb20gJy4uLy4uL3V0aWxzL3RlbGVtZXRyeS9ldmVudHMuanMnXG5pbXBvcnQgeyBzdWJtaXRUcmFuc2NyaXB0U2hhcmUgfSBmcm9tICcuL3N1Ym1pdFRyYW5zY3JpcHRTaGFyZS5qcydcbmltcG9ydCB0eXBlIHsgVHJhbnNjcmlwdFNoYXJlUmVzcG9uc2UgfSBmcm9tICcuL1RyYW5zY3JpcHRTaGFyZVByb21wdC5qcydcbmltcG9ydCB7IHVzZVN1cnZleVN0YXRlIH0gZnJvbSAnLi91c2VTdXJ2ZXlTdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSB9IGZyb20gJy4vdXRpbHMuanMnXG5cbmNvbnN0IEhJREVfVEhBTktTX0FGVEVSX01TID0gMzAwMFxuY29uc3QgTUVNT1JZX1NVUlZFWV9HQVRFID0gJ3Rlbmd1X2R1bndpY2hfYmVsbCdcbmNvbnN0IE1FTU9SWV9TVVJWRVlfRVZFTlQgPSAndGVuZ3VfbWVtb3J5X3N1cnZleV9ldmVudCdcbmNvbnN0IFNVUlZFWV9QUk9CQUJJTElUWSA9IDAuMlxuY29uc3QgVFJBTlNDUklQVF9TSEFSRV9UUklHR0VSID0gJ21lbW9yeV9zdXJ2ZXknXG5cbmNvbnN0IE1FTU9SWV9XT1JEX1JFID0gL1xcYm1lbW9yKD86eXxpZXMpXFxiL2lcblxuZnVuY3Rpb24gaGFzTWVtb3J5RmlsZVJlYWQobWVzc2FnZXM6IE1lc3NhZ2VbXSk6IGJvb2xlYW4ge1xuICBmb3IgKGNvbnN0IG1lc3NhZ2Ugb2YgbWVzc2FnZXMpIHtcbiAgICBpZiAobWVzc2FnZS50eXBlICE9PSAnYXNzaXN0YW50Jykge1xuICAgICAgY29udGludWVcbiAgICB9XG4gICAgY29uc3QgY29udGVudCA9IG1lc3NhZ2UubWVzc2FnZS5jb250ZW50XG4gICAgaWYgKCFBcnJheS5pc0FycmF5KGNvbnRlbnQpKSB7XG4gICAgICBjb250aW51ZVxuICAgIH1cbiAgICBmb3IgKGNvbnN0IGJsb2NrIG9mIGNvbnRlbnQpIHtcbiAgICAgIGlmIChibG9jay50eXBlICE9PSAndG9vbF91c2UnIHx8IGJsb2NrLm5hbWUgIT09IEZJTEVfUkVBRF9UT09MX05BTUUpIHtcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cbiAgICAgIGNvbnN0IGlucHV0ID0gYmxvY2suaW5wdXQgYXMgeyBmaWxlX3BhdGg/OiB1bmtub3duIH1cbiAgICAgIGlmIChcbiAgICAgICAgdHlwZW9mIGlucHV0LmZpbGVfcGF0aCA9PT0gJ3N0cmluZycgJiZcbiAgICAgICAgaXNBdXRvTWFuYWdlZE1lbW9yeUZpbGUoaW5wdXQuZmlsZV9wYXRoKVxuICAgICAgKSB7XG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBmYWxzZVxufVxuXG5leHBvcnQgZnVuY3Rpb24gdXNlTWVtb3J5U3VydmV5KFxuICBtZXNzYWdlczogTWVzc2FnZVtdLFxuICBpc0xvYWRpbmc6IGJvb2xlYW4sXG4gIGhhc0FjdGl2ZVByb21wdCA9IGZhbHNlLFxuICB7IGVuYWJsZWQgPSB0cnVlIH06IHsgZW5hYmxlZD86IGJvb2xlYW4gfSA9IHt9LFxuKToge1xuICBzdGF0ZTpcbiAgICB8ICdjbG9zZWQnXG4gICAgfCAnb3BlbidcbiAgICB8ICd0aGFua3MnXG4gICAgfCAndHJhbnNjcmlwdF9wcm9tcHQnXG4gICAgfCAnc3VibWl0dGluZydcbiAgICB8ICdzdWJtaXR0ZWQnXG4gIGxhc3RSZXNwb25zZTogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSB8IG51bGxcbiAgaGFuZGxlU2VsZWN0OiAoc2VsZWN0ZWQ6IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UpID0+IHZvaWRcbiAgaGFuZGxlVHJhbnNjcmlwdFNlbGVjdDogKHNlbGVjdGVkOiBUcmFuc2NyaXB0U2hhcmVSZXNwb25zZSkgPT4gdm9pZFxufSB7XG4gIC8vIFRyYWNrIGFzc2lzdGFudCBtZXNzYWdlIFVVSURzIHRoYXQgd2VyZSBhbHJlYWR5IGV2YWx1YXRlZCBzbyB3ZSBkb24ndFxuICAvLyByZS1yb2xsIHByb2JhYmlsaXR5IG9uIHJlLXJlbmRlcnMgb3IgcmUtc2NhbiBtZXNzYWdlcyBmb3IgdGhlIHNhbWUgdHVybi5cbiAgY29uc3Qgc2VlbkFzc2lzdGFudFV1aWRzID0gdXNlUmVmPFNldDxzdHJpbmc+PihuZXcgU2V0KCkpXG4gIC8vIE9uY2UgYSBtZW1vcnkgZmlsZSByZWFkIGlzIG9ic2VydmVkIGl0IHN0YXlzIHRydWUgZm9yIHRoZSBzZXNzaW9uIOKAlFxuICAvLyBza2lwIHRoZSBPKG4pIHNjYW4gb24gc3Vic2VxdWVudCB0dXJucy5cbiAgY29uc3QgbWVtb3J5UmVhZFNlZW4gPSB1c2VSZWYoZmFsc2UpXG4gIGNvbnN0IG1lc3NhZ2VzUmVmID0gdXNlUmVmKG1lc3NhZ2VzKVxuICBtZXNzYWdlc1JlZi5jdXJyZW50ID0gbWVzc2FnZXNcblxuICBjb25zdCBvbk9wZW4gPSB1c2VDYWxsYmFjaygoYXBwZWFyYW5jZUlkOiBzdHJpbmcpID0+IHtcbiAgICBsb2dFdmVudChNRU1PUllfU1VSVkVZX0VWRU5ULCB7XG4gICAgICBldmVudF90eXBlOlxuICAgICAgICAnYXBwZWFyZWQnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICBhcHBlYXJhbmNlX2lkOlxuICAgICAgICBhcHBlYXJhbmNlSWQgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICB9KVxuICAgIHZvaWQgbG9nT1RlbEV2ZW50KCdmZWVkYmFja19zdXJ2ZXknLCB7XG4gICAgICBldmVudF90eXBlOiAnYXBwZWFyZWQnLFxuICAgICAgYXBwZWFyYW5jZV9pZDogYXBwZWFyYW5jZUlkLFxuICAgICAgc3VydmV5X3R5cGU6ICdtZW1vcnknLFxuICAgIH0pXG4gIH0sIFtdKVxuXG4gIGNvbnN0IG9uU2VsZWN0ID0gdXNlQ2FsbGJhY2soXG4gICAgKGFwcGVhcmFuY2VJZDogc3RyaW5nLCBzZWxlY3RlZDogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSkgPT4ge1xuICAgICAgbG9nRXZlbnQoTUVNT1JZX1NVUlZFWV9FVkVOVCwge1xuICAgICAgICBldmVudF90eXBlOlxuICAgICAgICAgICdyZXNwb25kZWQnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGFwcGVhcmFuY2VfaWQ6XG4gICAgICAgICAgYXBwZWFyYW5jZUlkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHJlc3BvbnNlOlxuICAgICAgICAgIHNlbGVjdGVkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KVxuICAgICAgdm9pZCBsb2dPVGVsRXZlbnQoJ2ZlZWRiYWNrX3N1cnZleScsIHtcbiAgICAgICAgZXZlbnRfdHlwZTogJ3Jlc3BvbmRlZCcsXG4gICAgICAgIGFwcGVhcmFuY2VfaWQ6IGFwcGVhcmFuY2VJZCxcbiAgICAgICAgcmVzcG9uc2U6IHNlbGVjdGVkLFxuICAgICAgICBzdXJ2ZXlfdHlwZTogJ21lbW9yeScsXG4gICAgICB9KVxuICAgIH0sXG4gICAgW10sXG4gIClcblxuICBjb25zdCBzaG91bGRTaG93VHJhbnNjcmlwdFByb21wdCA9IHVzZUNhbGxiYWNrKFxuICAgIChzZWxlY3RlZDogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSkgPT4ge1xuICAgICAgaWYgKFwiZXh0ZXJuYWxcIiAhPT0gJ2FudCcpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgICBpZiAoc2VsZWN0ZWQgIT09ICdiYWQnICYmIHNlbGVjdGVkICE9PSAnZ29vZCcpIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgICBpZiAoZ2V0R2xvYmFsQ29uZmlnKCkudHJhbnNjcmlwdFNoYXJlRGlzbWlzc2VkKSB7XG4gICAgICAgIHJldHVybiBmYWxzZVxuICAgICAgfVxuICAgICAgaWYgKCFpc1BvbGljeUFsbG93ZWQoJ2FsbG93X3Byb2R1Y3RfZmVlZGJhY2snKSkge1xuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgIH1cbiAgICAgIHJldHVybiB0cnVlXG4gICAgfSxcbiAgICBbXSxcbiAgKVxuXG4gIGNvbnN0IG9uVHJhbnNjcmlwdFByb21wdFNob3duID0gdXNlQ2FsbGJhY2soKGFwcGVhcmFuY2VJZDogc3RyaW5nKSA9PiB7XG4gICAgbG9nRXZlbnQoTUVNT1JZX1NVUlZFWV9FVkVOVCwge1xuICAgICAgZXZlbnRfdHlwZTpcbiAgICAgICAgJ3RyYW5zY3JpcHRfcHJvbXB0X2FwcGVhcmVkJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgYXBwZWFyYW5jZV9pZDpcbiAgICAgICAgYXBwZWFyYW5jZUlkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB0cmlnZ2VyOlxuICAgICAgICBUUkFOU0NSSVBUX1NIQVJFX1RSSUdHRVIgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICB9KVxuICAgIHZvaWQgbG9nT1RlbEV2ZW50KCdmZWVkYmFja19zdXJ2ZXknLCB7XG4gICAgICBldmVudF90eXBlOiAndHJhbnNjcmlwdF9wcm9tcHRfYXBwZWFyZWQnLFxuICAgICAgYXBwZWFyYW5jZV9pZDogYXBwZWFyYW5jZUlkLFxuICAgICAgc3VydmV5X3R5cGU6ICdtZW1vcnknLFxuICAgIH0pXG4gIH0sIFtdKVxuXG4gIGNvbnN0IG9uVHJhbnNjcmlwdFNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jIChcbiAgICAgIGFwcGVhcmFuY2VJZDogc3RyaW5nLFxuICAgICAgc2VsZWN0ZWQ6IFRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlLFxuICAgICk6IFByb21pc2U8Ym9vbGVhbj4gPT4ge1xuICAgICAgbG9nRXZlbnQoTUVNT1JZX1NVUlZFWV9FVkVOVCwge1xuICAgICAgICBldmVudF90eXBlOlxuICAgICAgICAgIGB0cmFuc2NyaXB0X3NoYXJlXyR7c2VsZWN0ZWR9YCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBhcHBlYXJhbmNlX2lkOlxuICAgICAgICAgIGFwcGVhcmFuY2VJZCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICB0cmlnZ2VyOlxuICAgICAgICAgIFRSQU5TQ1JJUFRfU0hBUkVfVFJJR0dFUiBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcblxuICAgICAgaWYgKHNlbGVjdGVkID09PSAnZG9udF9hc2tfYWdhaW4nKSB7XG4gICAgICAgIHNhdmVHbG9iYWxDb25maWcoY3VycmVudCA9PiAoe1xuICAgICAgICAgIC4uLmN1cnJlbnQsXG4gICAgICAgICAgdHJhbnNjcmlwdFNoYXJlRGlzbWlzc2VkOiB0cnVlLFxuICAgICAgICB9KSlcbiAgICAgIH1cblxuICAgICAgaWYgKHNlbGVjdGVkID09PSAneWVzJykge1xuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzdWJtaXRUcmFuc2NyaXB0U2hhcmUoXG4gICAgICAgICAgbWVzc2FnZXNSZWYuY3VycmVudCxcbiAgICAgICAgICBUUkFOU0NSSVBUX1NIQVJFX1RSSUdHRVIsXG4gICAgICAgICAgYXBwZWFyYW5jZUlkLFxuICAgICAgICApXG4gICAgICAgIGxvZ0V2ZW50KE1FTU9SWV9TVVJWRVlfRVZFTlQsIHtcbiAgICAgICAgICBldmVudF90eXBlOiAocmVzdWx0LnN1Y2Nlc3NcbiAgICAgICAgICAgID8gJ3RyYW5zY3JpcHRfc2hhcmVfc3VibWl0dGVkJ1xuICAgICAgICAgICAgOiAndHJhbnNjcmlwdF9zaGFyZV9mYWlsZWQnKSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIGFwcGVhcmFuY2VfaWQ6XG4gICAgICAgICAgICBhcHBlYXJhbmNlSWQgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICB0cmlnZ2VyOlxuICAgICAgICAgICAgVFJBTlNDUklQVF9TSEFSRV9UUklHR0VSIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVybiByZXN1bHQuc3VjY2Vzc1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9LFxuICAgIFtdLFxuICApXG5cbiAgY29uc3QgeyBzdGF0ZSwgbGFzdFJlc3BvbnNlLCBvcGVuLCBoYW5kbGVTZWxlY3QsIGhhbmRsZVRyYW5zY3JpcHRTZWxlY3QgfSA9XG4gICAgdXNlU3VydmV5U3RhdGUoe1xuICAgICAgaGlkZVRoYW5rc0FmdGVyTXM6IEhJREVfVEhBTktTX0FGVEVSX01TLFxuICAgICAgb25PcGVuLFxuICAgICAgb25TZWxlY3QsXG4gICAgICBzaG91bGRTaG93VHJhbnNjcmlwdFByb21wdCxcbiAgICAgIG9uVHJhbnNjcmlwdFByb21wdFNob3duLFxuICAgICAgb25UcmFuc2NyaXB0U2VsZWN0LFxuICAgIH0pXG5cbiAgY29uc3QgbGFzdEFzc2lzdGFudCA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZ2V0TGFzdEFzc2lzdGFudE1lc3NhZ2UobWVzc2FnZXMpLFxuICAgIFttZXNzYWdlc10sXG4gIClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghZW5hYmxlZCkgcmV0dXJuXG5cbiAgICAvLyAvY2xlYXIgcmVzZXRzIG1lc3NhZ2VzIGJ1dCBSRVBMIHN0YXlzIG1vdW50ZWQg4oCUIHJlc2V0IHJlZnMgc28gYSBtZW1vcnlcbiAgICAvLyByZWFkIGZyb20gdGhlIHByZXZpb3VzIGNvbnZlcnNhdGlvbiBkb2Vzbid0IGxlYWsgaW50byB0aGUgbmV3IG9uZS5cbiAgICBpZiAobWVzc2FnZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICBtZW1vcnlSZWFkU2Vlbi5jdXJyZW50ID0gZmFsc2VcbiAgICAgIHNlZW5Bc3Npc3RhbnRVdWlkcy5jdXJyZW50LmNsZWFyKClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChzdGF0ZSAhPT0gJ2Nsb3NlZCcgfHwgaXNMb2FkaW5nIHx8IGhhc0FjdGl2ZVByb21wdCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gM1AgZGVmYXVsdDogc3VydmV5IG9mZiAobm8gR3Jvd3RoQm9vayBvbiBCZWRyb2NrL1ZlcnRleC9Gb3VuZHJ5KS5cbiAgICBpZiAoIWdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFKE1FTU9SWV9TVVJWRVlfR0FURSwgZmFsc2UpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoIWlzQXV0b01lbW9yeUVuYWJsZWQoKSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgaWYgKGlzRmVlZGJhY2tTdXJ2ZXlEaXNhYmxlZCgpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoIWlzUG9saWN5QWxsb3dlZCgnYWxsb3dfcHJvZHVjdF9mZWVkYmFjaycpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfRElTQUJMRV9GRUVEQkFDS19TVVJWRVkpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoIWxhc3RBc3Npc3RhbnQgfHwgc2VlbkFzc2lzdGFudFV1aWRzLmN1cnJlbnQuaGFzKGxhc3RBc3Npc3RhbnQudXVpZCkpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IHRleHQgPSBleHRyYWN0VGV4dENvbnRlbnQobGFzdEFzc2lzdGFudC5tZXNzYWdlLmNvbnRlbnQsICcgJylcbiAgICBpZiAoIU1FTU9SWV9XT1JEX1JFLnRlc3QodGV4dCkpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIE1hcmsgYXMgZXZhbHVhdGVkIGJlZm9yZSB0aGUgbWVtb3J5LXJlYWQgc2NhbiBzbyBhIHR1cm4gdGhhdCBtZW50aW9uc1xuICAgIC8vIFwibWVtb3J5XCIgYnV0IGhhcyBubyBtZW1vcnkgcmVhZCBkb2Vzbid0IHRyaWdnZXIgcmVwZWF0ZWQgTyhuKSBzY2Fuc1xuICAgIC8vIG9uIHN1YnNlcXVlbnQgcmVuZGVycyB3aXRoIHRoZSBzYW1lIGxhc3QgYXNzaXN0YW50IG1lc3NhZ2UuXG4gICAgc2VlbkFzc2lzdGFudFV1aWRzLmN1cnJlbnQuYWRkKGxhc3RBc3Npc3RhbnQudXVpZClcblxuICAgIGlmICghbWVtb3J5UmVhZFNlZW4uY3VycmVudCkge1xuICAgICAgbWVtb3J5UmVhZFNlZW4uY3VycmVudCA9IGhhc01lbW9yeUZpbGVSZWFkKG1lc3NhZ2VzKVxuICAgIH1cbiAgICBpZiAoIW1lbW9yeVJlYWRTZWVuLmN1cnJlbnQpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChNYXRoLnJhbmRvbSgpIDwgU1VSVkVZX1BST0JBQklMSVRZKSB7XG4gICAgICBvcGVuKClcbiAgICB9XG4gIH0sIFtcbiAgICBlbmFibGVkLFxuICAgIHN0YXRlLFxuICAgIGlzTG9hZGluZyxcbiAgICBoYXNBY3RpdmVQcm9tcHQsXG4gICAgbGFzdEFzc2lzdGFudCxcbiAgICBtZXNzYWdlcyxcbiAgICBvcGVuLFxuICBdKVxuXG4gIHJldHVybiB7IHN0YXRlLCBsYXN0UmVzcG9uc2UsIGhhbmRsZVNlbGVjdCwgaGFuZGxlVHJhbnNjcmlwdFNlbGVjdCB9XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLE1BQU0sUUFBUSxPQUFPO0FBQy9ELFNBQVNDLHdCQUF3QixRQUFRLGtDQUFrQztBQUMzRSxTQUFTQyxtQ0FBbUMsUUFBUSxzQ0FBc0M7QUFDMUYsU0FDRSxLQUFLQywwREFBMEQsRUFDL0RDLFFBQVEsUUFDSCxpQ0FBaUM7QUFDeEMsU0FBU0MsbUJBQW1CLFFBQVEsdUJBQXVCO0FBQzNELFNBQVNDLGVBQWUsUUFBUSxzQ0FBc0M7QUFDdEUsU0FBU0MsbUJBQW1CLFFBQVEsb0NBQW9DO0FBQ3hFLGNBQWNDLE9BQU8sUUFBUSx3QkFBd0I7QUFDckQs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O0lBQ0Y7SUFFQSxJQUFJN0IsS0FBSyxLQUFLLFFBQVEsSUFBSUgsU0FBUyxJQUFJQyxlQUFlLEVBQUU7TUFDdEQ7SUFDRjs7SUFFQTtJQUNBLElBQUksQ0FBQ3JDLG1DQUFtQyxDQUFDbUIsa0JBQWtCLEVBQUUsS0FBSyxDQUFDLEVBQUU7TUFDbkU7SUFDRjtJQUVBLElBQUksQ0FBQ2hCLG1CQUFtQixDQUFDLENBQUMsRUFBRTtNQUMxQjtJQUNGO0lBRUEsSUFBSUosd0JBQXdCLENBQUMsQ0FBQyxFQUFFO01BQzlCO0lBQ0Y7SUFFQSxJQUFJLENBQUNLLGVBQWUsQ0FBQyx3QkFBd0IsQ0FBQyxFQUFFO01BQzlDO0lBQ0Y7SUFFQSxJQUFJSyxXQUFXLENBQUM0RCxPQUFPLENBQUNDLEdBQUcsQ0FBQ0MsbUNBQW1DLENBQUMsRUFBRTtNQUNoRTtJQUNGO0lBRUEsSUFBSSxDQUFDTCxhQUFhLElBQUl0QixrQkFBa0IsQ0FBQ0ksT0FBTyxDQUFDd0IsR0FBRyxDQUFDTixhQUFhLENBQUNPLElBQUksQ0FBQyxFQUFFO01BQ3hFO0lBQ0Y7SUFFQSxNQUFNQyxJQUFJLEdBQUcvRCxrQkFBa0IsQ0FBQ3VELGFBQWEsQ0FBQ3hDLE9BQU8sQ0FBQ0UsT0FBTyxFQUFFLEdBQUcsQ0FBQztJQUNuRSxJQUFJLENBQUNMLGNBQWMsQ0FBQ29ELElBQUksQ0FBQ0QsSUFBSSxDQUFDLEVBQUU7TUFDOUI7SUFDRjs7SUFFQTtJQUNBO0lBQ0E7SUFDQTlCLGtCQUFrQixDQUFDSSxPQUFPLENBQUM0QixHQUFHLENBQUNWLGFBQWEsQ0FBQ08sSUFBSSxDQUFDO0lBRWxELElBQUksQ0FBQzNCLGNBQWMsQ0FBQ0UsT0FBTyxFQUFFO01BQzNCRixjQUFjLENBQUNFLE9BQU8sR0FBR3hCLGlCQUFpQixDQUFDQyxRQUFRLENBQUM7SUFDdEQ7SUFDQSxJQUFJLENBQUNxQixjQUFjLENBQUNFLE9BQU8sRUFBRTtNQUMzQjtJQUNGO0lBRUEsSUFBSTZCLElBQUksQ0FBQ0MsTUFBTSxDQUFDLENBQUMsR0FBR3pELGtCQUFrQixFQUFFO01BQ3RDMkMsSUFBSSxDQUFDLENBQUM7SUFDUjtFQUNGLENBQUMsRUFBRSxDQUNEMUIsT0FBTyxFQUNQQyxLQUFLLEVBQ0xILFNBQVMsRUFDVEMsZUFBZSxFQUNmNkIsYUFBYSxFQUNiekMsUUFBUSxFQUNSdUMsSUFBSSxDQUNMLENBQUM7RUFFRixPQUFPO0lBQUV6QixLQUFLO0lBQUVDLFlBQVk7SUFBRUMsWUFBWTtJQUFFRTtFQUF1QixDQUFDO0FBQ3RFIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/FeedbackSurvey/usePostCompactSurvey.tsx b/components/FeedbackSurvey/usePostCompactSurvey.tsx new file mode 100644 index 0000000..b33281a --- /dev/null +++ b/components/FeedbackSurvey/usePostCompactSurvey.tsx @@ -0,0 +1,206 @@ +import { c as _c } from "react/compiler-runtime"; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { isFeedbackSurveyDisabled } from 'src/services/analytics/config.js'; +import { checkStatsigFeatureGate_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { shouldUseSessionMemoryCompaction } from '../../services/compact/sessionMemoryCompact.js'; +import type { Message } from '../../types/message.js'; +import { isEnvTruthy } from '../../utils/envUtils.js'; +import { isCompactBoundaryMessage } from '../../utils/messages.js'; +import { logOTelEvent } from '../../utils/telemetry/events.js'; +import { useSurveyState } from './useSurveyState.js'; +import type { FeedbackSurveyResponse } from './utils.js'; +const HIDE_THANKS_AFTER_MS = 3000; +const POST_COMPACT_SURVEY_GATE = 'tengu_post_compact_survey'; +const SURVEY_PROBABILITY = 0.2; // Show survey 20% of the time after compaction + +function hasMessageAfterBoundary(messages: Message[], boundaryUuid: string): boolean { + const boundaryIndex = messages.findIndex(msg => msg.uuid === boundaryUuid); + if (boundaryIndex === -1) { + return false; + } + + // Check if there's a user or assistant message after the boundary + for (let i = boundaryIndex + 1; i < messages.length; i++) { + const msg = messages[i]; + if (msg && (msg.type === 'user' || msg.type === 'assistant')) { + return true; + } + } + return false; +} +export function usePostCompactSurvey(messages, isLoading, t0, t1) { + const $ = _c(23); + const hasActivePrompt = t0 === undefined ? false : t0; + let t2; + if ($[0] !== t1) { + t2 = t1 === undefined ? {} : t1; + $[0] = t1; + $[1] = t2; + } else { + t2 = $[1]; + } + const { + enabled: t3 + } = t2; + const enabled = t3 === undefined ? true : t3; + const [gateEnabled, setGateEnabled] = useState(null); + let t4; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t4 = new Set(); + $[2] = t4; + } else { + t4 = $[2]; + } + const seenCompactBoundaries = useRef(t4); + const pendingCompactBoundaryUuid = useRef(null); + const onOpen = _temp; + const onSelect = _temp2; + let t5; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + hideThanksAfterMs: HIDE_THANKS_AFTER_MS, + onOpen, + onSelect + }; + $[3] = t5; + } else { + t5 = $[3]; + } + const { + state, + lastResponse, + open, + handleSelect + } = useSurveyState(t5); + let t6; + let t7; + if ($[4] !== enabled) { + t6 = () => { + if (!enabled) { + return; + } + setGateEnabled(checkStatsigFeatureGate_CACHED_MAY_BE_STALE(POST_COMPACT_SURVEY_GATE)); + }; + t7 = [enabled]; + $[4] = enabled; + $[5] = t6; + $[6] = t7; + } else { + t6 = $[5]; + t7 = $[6]; + } + useEffect(t6, t7); + let t8; + if ($[7] !== messages) { + t8 = new Set(messages.filter(_temp3).map(_temp4)); + $[7] = messages; + $[8] = t8; + } else { + t8 = $[8]; + } + const currentCompactBoundaries = t8; + let t10; + let t9; + if ($[9] !== currentCompactBoundaries || $[10] !== enabled || $[11] !== gateEnabled || $[12] !== hasActivePrompt || $[13] !== isLoading || $[14] !== messages || $[15] !== open || $[16] !== state) { + t9 = () => { + if (!enabled) { + return; + } + if (state !== "closed" || isLoading) { + return; + } + if (hasActivePrompt) { + return; + } + if (gateEnabled !== true) { + return; + } + if (isFeedbackSurveyDisabled()) { + return; + } + if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_FEEDBACK_SURVEY)) { + return; + } + if (pendingCompactBoundaryUuid.current !== null) { + if (hasMessageAfterBoundary(messages, pendingCompactBoundaryUuid.current)) { + pendingCompactBoundaryUuid.current = null; + if (Math.random() < SURVEY_PROBABILITY) { + open(); + } + return; + } + } + const newBoundaries = Array.from(currentCompactBoundaries).filter(uuid => !seenCompactBoundaries.current.has(uuid)); + if (newBoundaries.length > 0) { + seenCompactBoundaries.current = new Set(currentCompactBoundaries); + pendingCompactBoundaryUuid.current = newBoundaries[newBoundaries.length - 1]; + } + }; + t10 = [enabled, currentCompactBoundaries, state, isLoading, hasActivePrompt, gateEnabled, messages, open]; + $[9] = currentCompactBoundaries; + $[10] = enabled; + $[11] = gateEnabled; + $[12] = hasActivePrompt; + $[13] = isLoading; + $[14] = messages; + $[15] = open; + $[16] = state; + $[17] = t10; + $[18] = t9; + } else { + t10 = $[17]; + t9 = $[18]; + } + useEffect(t9, t10); + let t11; + if ($[19] !== handleSelect || $[20] !== lastResponse || $[21] !== state) { + t11 = { + state, + lastResponse, + handleSelect + }; + $[19] = handleSelect; + $[20] = lastResponse; + $[21] = state; + $[22] = t11; + } else { + t11 = $[22]; + } + return t11; +} +function _temp4(msg_0) { + return msg_0.uuid; +} +function _temp3(msg) { + return isCompactBoundaryMessage(msg); +} +function _temp2(appearanceId_0, selected) { + const smCompactionEnabled_0 = shouldUseSessionMemoryCompaction(); + logEvent("tengu_post_compact_survey_event", { + event_type: "responded" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId_0 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + response: selected as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + session_memory_compaction_enabled: smCompactionEnabled_0 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + logOTelEvent("feedback_survey", { + event_type: "responded", + appearance_id: appearanceId_0, + response: selected, + survey_type: "post_compact" + }); +} +function _temp(appearanceId) { + const smCompactionEnabled = shouldUseSessionMemoryCompaction(); + logEvent("tengu_post_compact_survey_event", { + event_type: "appeared" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + appearance_id: appearanceId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + session_memory_compaction_enabled: smCompactionEnabled as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + logOTelEvent("feedback_survey", { + event_type: "appeared", + appearance_id: appearanceId, + survey_type: "post_compact" + }); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1c2VDYWxsYmFjayIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsImlzRmVlZGJhY2tTdXJ2ZXlEaXNhYmxlZCIsImNoZWNrU3RhdHNpZ0ZlYXR1cmVHYXRlX0NBQ0hFRF9NQVlfQkVfU1RBTEUiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJzaG91bGRVc2VTZXNzaW9uTWVtb3J5Q29tcGFjdGlvbiIsIk1lc3NhZ2UiLCJpc0VudlRydXRoeSIsImlzQ29tcGFjdEJvdW5kYXJ5TWVzc2FnZSIsImxvZ09UZWxFdmVudCIsInVzZVN1cnZleVN0YXRlIiwiRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSIsIkhJREVfVEhBTktTX0FGVEVSX01TIiwiUE9TVF9DT01QQUNUX1NVUlZFWV9HQVRFIiwiU1VSVkVZX1BST0JBQklMSVRZIiwiaGFzTWVzc2FnZUFmdGVyQm91bmRhcnkiLCJtZXNzYWdlcyIsImJvdW5kYXJ5VXVpZCIsImJvdW5kYXJ5SW5kZXgiLCJmaW5kSW5kZXgiLCJtc2ciLCJ1dWlkIiwiaSIsImxlbmd0aCIsInR5cGUiLCJ1c2VQb3N0Q29tcGFjdFN1cnZleSIsImlzTG9hZGluZyIsInQwIiwidDEiLCIkIiwiX2MiLCJoYXNBY3RpdmVQcm9tcHQiLCJ1bmRlZmluZWQiLCJ0MiIsImVuYWJsZWQiLCJ0MyIsImdhdGVFbmFibGVkIiwic2V0R2F0ZUVuYWJsZWQiLCJ0NCIsIlN5bWJvbCIsImZvciIsIlNldCIsInNlZW5Db21wYWN0Qm91bmRhcmllcyIsInBlbmRpbmdDb21wYWN0Qm91bmRhcnlVdWlkIiwib25PcGVuIiwiX3RlbXAiLCJvblNlbGVjdCIsIl90ZW1wMiIsInQ1IiwiaGlkZVRoYW5rc0FmdGVyTXMiLCJzdGF0ZSIsImxhc3RSZXNwb25zZSIsIm9wZW4iLCJoYW5kbGVTZWxlY3QiLCJ0NiIsInQ3IiwidDgiLCJmaWx0ZXIiLCJfdGVtcDMiLCJtYXAiLCJfdGVtcDQiLCJjdXJyZW50Q29tcGFjdEJvdW5kYXJpZXMiLCJ0MTAiLCJ0OSIsInByb2Nlc3MiLCJlbnYiLCJDTEFVREVfQ09ERV9ESVNBQkxFX0ZFRURCQUNLX1NVUlZFWSIsImN1cnJlbnQiLCJNYXRoIiwicmFuZG9tIiwibmV3Qm91bmRhcmllcyIsIkFycmF5IiwiZnJvbSIsImhhcyIsInQxMSIsIm1zZ18wIiwiYXBwZWFyYW5jZUlkXzAiLCJzZWxlY3RlZCIsInNtQ29tcGFjdGlvbkVuYWJsZWRfMCIsImV2ZW50X3R5cGUiLCJhcHBlYXJhbmNlX2lkIiwiYXBwZWFyYW5jZUlkIiwicmVzcG9uc2UiLCJzZXNzaW9uX21lbW9yeV9jb21wYWN0aW9uX2VuYWJsZWQiLCJzbUNvbXBhY3Rpb25FbmFibGVkIiwic3VydmV5X3R5cGUiXSwic291cmNlcyI6WyJ1c2VQb3N0Q29tcGFjdFN1cnZleS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlTWVtbywgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgaXNGZWVkYmFja1N1cnZleURpc2FibGVkIH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9jb25maWcuanMnXG5pbXBvcnQgeyBjaGVja1N0YXRzaWdGZWF0dXJlR2F0ZV9DQUNIRURfTUFZX0JFX1NUQUxFIH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9ncm93dGhib29rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IHNob3VsZFVzZVNlc3Npb25NZW1vcnlDb21wYWN0aW9uIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvY29tcGFjdC9zZXNzaW9uTWVtb3J5Q29tcGFjdC5qcydcbmltcG9ydCB0eXBlIHsgTWVzc2FnZSB9IGZyb20gJy4uLy4uL3R5cGVzL21lc3NhZ2UuanMnXG5pbXBvcnQgeyBpc0VudlRydXRoeSB9IGZyb20gJy4uLy4uL3V0aWxzL2VudlV0aWxzLmpzJ1xuaW1wb3J0IHsgaXNDb21wYWN0Qm91bmRhcnlNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdXRpbHMvbWVzc2FnZXMuanMnXG5pbXBvcnQgeyBsb2dPVGVsRXZlbnQgfSBmcm9tICcuLi8uLi91dGlscy90ZWxlbWV0cnkvZXZlbnRzLmpzJ1xuaW1wb3J0IHsgdXNlU3VydmV5U3RhdGUgfSBmcm9tICcuL3VzZVN1cnZleVN0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBGZWVkYmFja1N1cnZleVJlc3BvbnNlIH0gZnJvbSAnLi91dGlscy5qcydcblxuY29uc3QgSElERV9USEFOS1NfQUZURVJfTVMgPSAzMDAwXG5jb25zdCBQT1NUX0NPTVBBQ1RfU1VSVkVZX0dBVEUgPSAndGVuZ3VfcG9zdF9jb21wYWN0X3N1cnZleSdcbmNvbnN0IFNVUlZFWV9QUk9CQUJJTElUWSA9IDAuMiAvLyBTaG93IHN1cnZleSAyMCUgb2YgdGhlIHRpbWUgYWZ0ZXIgY29tcGFjdGlvblxuXG5mdW5jdGlvbiBoYXNNZXNzYWdlQWZ0ZXJCb3VuZGFyeShcbiAgbWVzc2FnZXM6IE1lc3NhZ2VbXSxcbiAgYm91bmRhcnlVdWlkOiBzdHJpbmcsXG4pOiBib29sZWFuIHtcbiAgY29uc3QgYm91bmRhcnlJbmRleCA9IG1lc3NhZ2VzLmZpbmRJbmRleChtc2cgPT4gbXNnLnV1aWQgPT09IGJvdW5kYXJ5VXVpZClcbiAgaWYgKGJvdW5kYXJ5SW5kZXggPT09IC0xKSB7XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cblxuICAvLyBDaGVjayBpZiB0aGVyZSdzIGEgdXNlciBvciBhc3Npc3RhbnQgbWVzc2FnZSBhZnRlciB0aGUgYm91bmRhcnlcbiAgZm9yIChsZXQgaSA9IGJvdW5kYXJ5SW5kZXggKyAxOyBpIDwgbWVzc2FnZXMubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBtc2cgPSBtZXNzYWdlc1tpXVxuICAgIGlmIChtc2cgJiYgKG1zZy50eXBlID09PSAndXNlcicgfHwgbXNnLnR5cGUgPT09ICdhc3Npc3RhbnQnKSkge1xuICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGZhbHNlXG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VQb3N0Q29tcGFjdFN1cnZleShcbiAgbWVzc2FnZXM6IE1lc3NhZ2VbXSxcbiAgaXNMb2FkaW5nOiBib29sZWFuLFxuICBoYXNBY3RpdmVQcm9tcHQgPSBmYWxzZSxcbiAgeyBlbmFibGVkID0gdHJ1ZSB9OiB7IGVuYWJsZWQ/OiBib29sZWFuIH0gPSB7fSxcbik6IHtcbiAgc3RhdGU6XG4gICAgfCAnY2xvc2VkJ1xuICAgIHwgJ29wZW4nXG4gICAgfCAndGhhbmtzJ1xuICAgIHwgJ3RyYW5zY3JpcHRfcHJvbXB0J1xuICAgIHwgJ3N1Ym1pdHRpbmcnXG4gICAgfCAnc3VibWl0dGVkJ1xuICBsYXN0UmVzcG9uc2U6IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UgfCBudWxsXG4gIGhhbmRsZVNlbGVjdDogKHNlbGVjdGVkOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlKSA9PiB2b2lkXG59IHtcbiAgY29uc3QgW2dhdGVFbmFibGVkLCBzZXRHYXRlRW5hYmxlZF0gPSB1c2VTdGF0ZTxib29sZWFuIHwgbnVsbD4obnVsbClcbiAgY29uc3Qgc2VlbkNvbXBhY3RCb3VuZGFyaWVzID0gdXNlUmVmPFNldDxzdHJpbmc+PihuZXcgU2V0KCkpXG4gIC8vIFRyYWNrIHRoZSBjb21wYWN0IGJvdW5kYXJ5IHdlJ3JlIHdhaXRpbmcgb24gKHRvIHNob3cgc3VydmV5IGFmdGVyIG5leHQgbWVzc2FnZSlcbiAgY29uc3QgcGVuZGluZ0NvbXBhY3RCb3VuZGFyeVV1aWQgPSB1c2VSZWY8c3RyaW5nIHwgbnVsbD4obnVsbClcblxuICBjb25zdCBvbk9wZW4gPSB1c2VDYWxsYmFjaygoYXBwZWFyYW5jZUlkOiBzdHJpbmcpID0+IHtcbiAgICBjb25zdCBzbUNvbXBhY3Rpb25FbmFibGVkID0gc2hvdWxkVXNlU2Vzc2lvbk1lbW9yeUNvbXBhY3Rpb24oKVxuICAgIGxvZ0V2ZW50KCd0ZW5ndV9wb3N0X2NvbXBhY3Rfc3VydmV5X2V2ZW50Jywge1xuICAgICAgZXZlbnRfdHlwZTpcbiAgICAgICAgJ2FwcGVhcmVkJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgYXBwZWFyYW5jZV9pZDpcbiAgICAgICAgYXBwZWFyYW5jZUlkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICBzZXNzaW9uX21lbW9yeV9jb21wYWN0aW9uX2VuYWJsZWQ6XG4gICAgICAgIHNtQ29tcGFjdGlvbkVuYWJsZWQgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICB9KVxuICAgIHZvaWQgbG9nT1RlbEV2ZW50KCdmZWVkYmFja19zdXJ2ZXknLCB7XG4gICAgICBldmVudF90eXBlOiAnYXBwZWFyZWQnLFxuICAgICAgYXBwZWFyYW5jZV9pZDogYXBwZWFyYW5jZUlkLFxuICAgICAgc3VydmV5X3R5cGU6ICdwb3N0X2NvbXBhY3QnLFxuICAgIH0pXG4gIH0sIFtdKVxuXG4gIGNvbnN0IG9uU2VsZWN0ID0gdXNlQ2FsbGJhY2soXG4gICAgKGFwcGVhcmFuY2VJZDogc3RyaW5nLCBzZWxlY3RlZDogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSkgPT4ge1xuICAgICAgY29uc3Qgc21Db21wYWN0aW9uRW5hYmxlZCA9IHNob3VsZFVzZVNlc3Npb25NZW1vcnlDb21wYWN0aW9uKClcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9wb3N0X2NvbXBhY3Rfc3VydmV5X2V2ZW50Jywge1xuICAgICAgICBldmVudF90eXBlOlxuICAgICAgICAgICdyZXNwb25kZWQnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGFwcGVhcmFuY2VfaWQ6XG4gICAgICAgICAgYXBwZWFyYW5jZUlkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHJlc3BvbnNlOlxuICAgICAgICAgIHNlbGVjdGVkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHNlc3Npb25fbWVtb3J5X2NvbXBhY3Rpb25fZW5hYmxlZDpcbiAgICAgICAgICBzbUNvbXBhY3Rpb25FbmFibGVkIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KVxuICAgICAgdm9pZCBsb2dPVGVsRXZlbnQoJ2ZlZWRiYWNrX3N1cnZleScsIHtcbiAgICAgICAgZXZlbnRfdHlwZTogJ3Jlc3BvbmRlZCcsXG4gICAgICAgIGFwcGVhcmFuY2VfaWQ6IGFwcGVhcmFuY2VJZCxcbiAgICAgICAgcmVzcG9uc2U6IHNlbGVjdGVkLFxuICAgICAgICBzdXJ2ZXlfdHlwZTogJ3Bvc3RfY29tcGFjdCcsXG4gICAgICB9KVxuICAgIH0sXG4gICAgW10sXG4gIClcblxuICBjb25zdCB7IHN0YXRlLCBsYXN0UmVzcG9uc2UsIG9wZW4sIGhhbmRsZVNlbGVjdCB9ID0gdXNlU3VydmV5U3RhdGUoe1xuICAgIGhpZGVUaGFua3NBZnRlck1zOiBISURFX1RIQU5LU19BRlRFUl9NUyxcbiAgICBvbk9wZW4sXG4gICAgb25TZWxlY3QsXG4gIH0pXG5cbiAgLy8gQ2hlY2sgdGhlIGZlYXR1cmUgZ2F0ZSBvbiBtb3VudFxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghZW5hYmxlZCkgcmV0dXJuXG4gICAgc2V0R2F0ZUVuYWJsZWQoXG4gICAgICBjaGVja1N0YXRzaWdGZWF0dXJlR2F0ZV9DQUNIRURfTUFZX0JFX1NUQUxFKFBPU1RfQ09NUEFDVF9TVVJWRVlfR0FURSksXG4gICAgKVxuICB9LCBbZW5hYmxlZF0pXG5cbiAgLy8gRmluZCBjb21wYWN0IGJvdW5kYXJ5IG1lc3NhZ2VzXG4gIGNvbnN0IGN1cnJlbnRDb21wYWN0Qm91bmRhcmllcyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIG5ldyBTZXQoXG4gICAgICAgIG1lc3NhZ2VzXG4gICAgICAgICAgLmZpbHRlcihtc2cgPT4gaXNDb21wYWN0Qm91bmRhcnlNZXNzYWdlKG1zZykpXG4gICAgICAgICAgLm1hcChtc2cgPT4gbXNnLnV1aWQpLFxuICAgICAgKSxcbiAgICBbbWVzc2FnZXNdLFxuICApXG5cbiAgLy8gRGV0ZWN0IG5ldyBjb21wYWN0IGJvdW5kYXJpZXMgYW5kIGRlZmVyIHNob3dpbmcgc3VydmV5IHVudGlsIG5leHQgbWVzc2FnZVxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghZW5hYmxlZCkgcmV0dXJuXG5cbiAgICAvLyBEb24ndCBwcm9jZXNzIGlmIGFscmVhZHkgc2hvd2luZ1xuICAgIGlmIChzdGF0ZSAhPT0gJ2Nsb3NlZCcgfHwgaXNMb2FkaW5nKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBEb24ndCBzaG93IHN1cnZleSB3aGVuIHBlcm1pc3Npb24gb3IgYXNrIHF1ZXN0aW9uIHByb21wdHMgYXJlIHZpc2libGVcbiAgICBpZiAoaGFzQWN0aXZlUHJvbXB0KSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBDaGVjayBpZiB0aGUgZ2F0ZSBpcyBlbmFibGVkXG4gICAgaWYgKGdhdGVFbmFibGVkICE9PSB0cnVlKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoaXNGZWVkYmFja1N1cnZleURpc2FibGVkKCkpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIENoZWNrIGlmIHN1cnZleSBpcyBleHBsaWNpdGx5IGRpc2FibGVkXG4gICAgaWYgKGlzRW52VHJ1dGh5KHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0RJU0FCTEVfRkVFREJBQ0tfU1VSVkVZKSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gRmlyc3QsIGNoZWNrIGlmIHdlIGhhdmUgYSBwZW5kaW5nIGNvbXBhY3QgYW5kIGEgbmV3IG1lc3NhZ2UgaGFzIGFycml2ZWRcbiAgICBpZiAocGVuZGluZ0NvbXBhY3RCb3VuZGFyeVV1aWQuY3VycmVudCAhPT0gbnVsbCkge1xuICAgICAgaWYgKFxuICAgICAgICBoYXNNZXNzYWdlQWZ0ZXJCb3VuZGFyeShtZXNzYWdlcywgcGVuZGluZ0NvbXBhY3RCb3VuZGFyeVV1aWQuY3VycmVudClcbiAgICAgICkge1xuICAgICAgICAvLyBBIG5ldyBtZXNzYWdlIGFycml2ZWQgYWZ0ZXIgdGhlIGNvbXBhY3QgLSBkZWNpZGUgd2hldGhlciB0byBzaG93IHN1cnZleVxuICAgICAgICBwZW5kaW5nQ29tcGFjdEJvdW5kYXJ5VXVpZC5jdXJyZW50ID0gbnVsbFxuXG4gICAgICAgIC8vIE9ubHkgc2hvdyBzdXJ2ZXkgMjAlIG9mIHRoZSB0aW1lXG4gICAgICAgIGlmIChNYXRoLnJhbmRvbSgpIDwgU1VSVkVZX1BST0JBQklMSVRZKSB7XG4gICAgICAgICAgb3BlbigpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRmluZCBuZXcgY29tcGFjdCBib3VuZGFyaWVzIHRoYXQgd2UgaGF2ZW4ndCBzZWVuIHlldFxuICAgIGNvbnN0IG5ld0JvdW5kYXJpZXMgPSBBcnJheS5mcm9tKGN1cnJlbnRDb21wYWN0Qm91bmRhcmllcykuZmlsdGVyKFxuICAgICAgdXVpZCA9PiAhc2VlbkNvbXBhY3RCb3VuZGFyaWVzLmN1cnJlbnQuaGFzKHV1aWQpLFxuICAgIClcblxuICAgIGlmIChuZXdCb3VuZGFyaWVzLmxlbmd0aCA+IDApIHtcbiAgICAgIC8vIE1hcmsgdGhlc2UgYm91bmRhcmllcyBhcyBzZWVuXG4gICAgICBzZWVuQ29tcGFjdEJvdW5kYXJpZXMuY3VycmVudCA9IG5ldyBTZXQoY3VycmVudENvbXBhY3RCb3VuZGFyaWVzKVxuXG4gICAgICAvLyBEb24ndCBzaG93IHN1cnZleSBpbW1lZGlhdGVseSAtIHdhaXQgZm9yIG5leHQgbWVzc2FnZVxuICAgICAgLy8gU3RvcmUgdGhlIG1vc3QgcmVjZW50IG5ldyBib3VuZGFyeSBVVUlEXG4gICAgICBwZW5kaW5nQ29tcGFjdEJvdW5kYXJ5VXVpZC5jdXJyZW50ID1cbiAgICAgICAgbmV3Qm91bmRhcmllc1tuZXdCb3VuZGFyaWVzLmxlbmd0aCAtIDFdIVxuICAgIH1cbiAgfSwgW1xuICAgIGVuYWJsZWQsXG4gICAgY3VycmVudENvbXBhY3RCb3VuZGFyaWVzLFxuICAgIHN0YXRlLFxuICAgIGlzTG9hZGluZyxcbiAgICBoYXNBY3RpdmVQcm9tcHQsXG4gICAgZ2F0ZUVuYWJsZWQsXG4gICAgbWVzc2FnZXMsXG4gICAgb3BlbixcbiAgXSlcblxuICByZXR1cm4geyBzdGF0ZSwgbGFzdFJlc3BvbnNlLCBoYW5kbGVTZWxlY3QgfVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsV0FBVyxFQUFFQyxTQUFTLEVBQUVDLE9BQU8sRUFBRUMsTUFBTSxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUN6RSxTQUFTQyx3QkFBd0IsUUFBUSxrQ0FBa0M7QUFDM0UsU0FBU0MsMkNBQTJDLFFBQVEsc0NBQXNDO0FBQ2xHLFNBQ0UsS0FBS0MsMERBQTBELEVBQy9EQyxRQUFRLFFBQ0gsaUNBQWlDO0FBQ3hDLFNBQVNDLGdDQUFnQyxRQUFRLGdEQUFnRDtBQUNqRyxjQUFjQyxPQUFPLFFBQVEsd0JBQXdCO0FBQ3JELFNBQVNDLFdBQVcsUUFBUSx5QkFBeUI7QUFDckQsU0FBU0Msd0JBQXdCLFFBQVEseUJBQXlCO0FBQ2xFLFNBQVNDLFlBQVksUUFBUSxpQ0FBaUM7QUFDOUQsU0FB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 \ No newline at end of file diff --git a/components/FeedbackSurvey/useSurveyState.tsx b/components/FeedbackSurvey/useSurveyState.tsx new file mode 100644 index 0000000..a2758ed --- /dev/null +++ b/components/FeedbackSurvey/useSurveyState.tsx @@ -0,0 +1,100 @@ +import { randomUUID } from 'crypto'; +import { useCallback, useRef, useState } from 'react'; +import type { TranscriptShareResponse } from './TranscriptSharePrompt.js'; +import type { FeedbackSurveyResponse } from './utils.js'; +type SurveyState = 'closed' | 'open' | 'thanks' | 'transcript_prompt' | 'submitting' | 'submitted'; +type UseSurveyStateOptions = { + hideThanksAfterMs: number; + onOpen: (appearanceId: string) => void | Promise; + onSelect: (appearanceId: string, selected: FeedbackSurveyResponse) => void | Promise; + shouldShowTranscriptPrompt?: (selected: FeedbackSurveyResponse) => boolean; + onTranscriptPromptShown?: (appearanceId: string, surveyResponse: FeedbackSurveyResponse) => void; + onTranscriptSelect?: (appearanceId: string, selected: TranscriptShareResponse, surveyResponse: FeedbackSurveyResponse | null) => boolean | Promise; +}; +export function useSurveyState({ + hideThanksAfterMs, + onOpen, + onSelect, + shouldShowTranscriptPrompt, + onTranscriptPromptShown, + onTranscriptSelect +}: UseSurveyStateOptions): { + state: SurveyState; + lastResponse: FeedbackSurveyResponse | null; + open: () => void; + handleSelect: (selected: FeedbackSurveyResponse) => boolean; + handleTranscriptSelect: (selected: TranscriptShareResponse) => void; +} { + const [state, setState] = useState('closed'); + const [lastResponse, setLastResponse] = useState(null); + const appearanceId = useRef(randomUUID()); + const lastResponseRef = useRef(null); + const showThanksThenClose = useCallback(() => { + setState('thanks'); + setTimeout((setState_0, setLastResponse_0) => { + setState_0('closed'); + setLastResponse_0(null); + }, hideThanksAfterMs, setState, setLastResponse); + }, [hideThanksAfterMs]); + const showSubmittedThenClose = useCallback(() => { + setState('submitted'); + setTimeout(setState, hideThanksAfterMs, 'closed'); + }, [hideThanksAfterMs]); + const open = useCallback(() => { + if (state !== 'closed') { + return; + } + setState('open'); + appearanceId.current = randomUUID(); + void onOpen(appearanceId.current); + }, [state, onOpen]); + const handleSelect = useCallback((selected: FeedbackSurveyResponse): boolean => { + setLastResponse(selected); + lastResponseRef.current = selected; + // Always fire the survey response event first + void onSelect(appearanceId.current, selected); + if (selected === 'dismissed') { + setState('closed'); + setLastResponse(null); + } else if (shouldShowTranscriptPrompt?.(selected)) { + setState('transcript_prompt'); + onTranscriptPromptShown?.(appearanceId.current, selected); + return true; + } else { + showThanksThenClose(); + } + return false; + }, [showThanksThenClose, onSelect, shouldShowTranscriptPrompt, onTranscriptPromptShown]); + const handleTranscriptSelect = useCallback((selected_0: TranscriptShareResponse) => { + switch (selected_0) { + case 'yes': + setState('submitting'); + void (async () => { + try { + const success = await onTranscriptSelect?.(appearanceId.current, selected_0, lastResponseRef.current); + if (success) { + showSubmittedThenClose(); + } else { + showThanksThenClose(); + } + } catch { + showThanksThenClose(); + } + })(); + break; + case 'no': + case 'dont_ask_again': + void onTranscriptSelect?.(appearanceId.current, selected_0, lastResponseRef.current); + showThanksThenClose(); + break; + } + }, [showThanksThenClose, showSubmittedThenClose, onTranscriptSelect]); + return { + state, + lastResponse, + open, + handleSelect, + handleTranscriptSelect + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyYW5kb21VVUlEIiwidXNlQ2FsbGJhY2siLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsIlRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlIiwiRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSIsIlN1cnZleVN0YXRlIiwiVXNlU3VydmV5U3RhdGVPcHRpb25zIiwiaGlkZVRoYW5rc0FmdGVyTXMiLCJvbk9wZW4iLCJhcHBlYXJhbmNlSWQiLCJQcm9taXNlIiwib25TZWxlY3QiLCJzZWxlY3RlZCIsInNob3VsZFNob3dUcmFuc2NyaXB0UHJvbXB0Iiwib25UcmFuc2NyaXB0UHJvbXB0U2hvd24iLCJzdXJ2ZXlSZXNwb25zZSIsIm9uVHJhbnNjcmlwdFNlbGVjdCIsInVzZVN1cnZleVN0YXRlIiwic3RhdGUiLCJsYXN0UmVzcG9uc2UiLCJvcGVuIiwiaGFuZGxlU2VsZWN0IiwiaGFuZGxlVHJhbnNjcmlwdFNlbGVjdCIsInNldFN0YXRlIiwic2V0TGFzdFJlc3BvbnNlIiwibGFzdFJlc3BvbnNlUmVmIiwic2hvd1RoYW5rc1RoZW5DbG9zZSIsInNldFRpbWVvdXQiLCJzaG93U3VibWl0dGVkVGhlbkNsb3NlIiwiY3VycmVudCIsInN1Y2Nlc3MiXSwic291cmNlcyI6WyJ1c2VTdXJ2ZXlTdGF0ZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcmFuZG9tVVVJRCB9IGZyb20gJ2NyeXB0bydcbmltcG9ydCB7IHVzZUNhbGxiYWNrLCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IFRyYW5zY3JpcHRTaGFyZVJlc3BvbnNlIH0gZnJvbSAnLi9UcmFuc2NyaXB0U2hhcmVQcm9tcHQuanMnXG5pbXBvcnQgdHlwZSB7IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UgfSBmcm9tICcuL3V0aWxzLmpzJ1xuXG50eXBlIFN1cnZleVN0YXRlID1cbiAgfCAnY2xvc2VkJ1xuICB8ICdvcGVuJ1xuICB8ICd0aGFua3MnXG4gIHwgJ3RyYW5zY3JpcHRfcHJvbXB0J1xuICB8ICdzdWJtaXR0aW5nJ1xuICB8ICdzdWJtaXR0ZWQnXG5cbnR5cGUgVXNlU3VydmV5U3RhdGVPcHRpb25zID0ge1xuICBoaWRlVGhhbmtzQWZ0ZXJNczogbnVtYmVyXG4gIG9uT3BlbjogKGFwcGVhcmFuY2VJZDogc3RyaW5nKSA9PiB2b2lkIHwgUHJvbWlzZTx2b2lkPlxuICBvblNlbGVjdDogKFxuICAgIGFwcGVhcmFuY2VJZDogc3RyaW5nLFxuICAgIHNlbGVjdGVkOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlLFxuICApID0+IHZvaWQgfCBQcm9taXNlPHZvaWQ+XG4gIHNob3VsZFNob3dUcmFuc2NyaXB0UHJvbXB0PzogKHNlbGVjdGVkOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlKSA9PiBib29sZWFuXG4gIG9uVHJhbnNjcmlwdFByb21wdFNob3duPzogKFxuICAgIGFwcGVhcmFuY2VJZDogc3RyaW5nLFxuICAgIHN1cnZleVJlc3BvbnNlOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlLFxuICApID0+IHZvaWRcbiAgb25UcmFuc2NyaXB0U2VsZWN0PzogKFxuICAgIGFwcGVhcmFuY2VJZDogc3RyaW5nLFxuICAgIHNlbGVjdGVkOiBUcmFuc2NyaXB0U2hhcmVSZXNwb25zZSxcbiAgICBzdXJ2ZXlSZXNwb25zZTogRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSB8IG51bGwsXG4gICkgPT4gYm9vbGVhbiB8IFByb21pc2U8Ym9vbGVhbj5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVzZVN1cnZleVN0YXRlKHtcbiAgaGlkZVRoYW5rc0FmdGVyTXMsXG4gIG9uT3BlbixcbiAgb25TZWxlY3QsXG4gIHNob3VsZFNob3dUcmFuc2NyaXB0UHJvbXB0LFxuICBvblRyYW5zY3JpcHRQcm9tcHRTaG93bixcbiAgb25UcmFuc2NyaXB0U2VsZWN0LFxufTogVXNlU3VydmV5U3RhdGVPcHRpb25zKToge1xuICBzdGF0ZTogU3VydmV5U3RhdGVcbiAgbGFzdFJlc3BvbnNlOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlIHwgbnVsbFxuICBvcGVuOiAoKSA9PiB2b2lkXG4gIGhhbmRsZVNlbGVjdDogKHNlbGVjdGVkOiBGZWVkYmFja1N1cnZleVJlc3BvbnNlKSA9PiBib29sZWFuXG4gIGhhbmRsZVRyYW5zY3JpcHRTZWxlY3Q6IChzZWxlY3RlZDogVHJhbnNjcmlwdFNoYXJlUmVzcG9uc2UpID0+IHZvaWRcbn0ge1xuICBjb25zdCBbc3RhdGUsIHNldFN0YXRlXSA9IHVzZVN0YXRlPFN1cnZleVN0YXRlPignY2xvc2VkJylcbiAgY29uc3QgW2xhc3RSZXNwb25zZSwgc2V0TGFzdFJlc3BvbnNlXSA9XG4gICAgdXNlU3RhdGU8RmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSB8IG51bGw+KG51bGwpXG4gIGNvbnN0IGFwcGVhcmFuY2VJZCA9IHVzZVJlZihyYW5kb21VVUlEKCkpXG4gIGNvbnN0IGxhc3RSZXNwb25zZVJlZiA9IHVzZVJlZjxGZWVkYmFja1N1cnZleVJlc3BvbnNlIHwgbnVsbD4obnVsbClcblxuICBjb25zdCBzaG93VGhhbmtzVGhlbkNsb3NlID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHNldFN0YXRlKCd0aGFua3MnKVxuICAgIHNldFRpbWVvdXQoXG4gICAgICAoc2V0U3RhdGUsIHNldExhc3RSZXNwb25zZSkgPT4ge1xuICAgICAgICBzZXRTdGF0ZSgnY2xvc2VkJylcbiAgICAgICAgc2V0TGFzdFJlc3BvbnNlKG51bGwpXG4gICAgICB9LFxuICAgICAgaGlkZVRoYW5rc0FmdGVyTXMsXG4gICAgICBzZXRTdGF0ZSxcbiAgICAgIHNldExhc3RSZXNwb25zZSxcbiAgICApXG4gIH0sIFtoaWRlVGhhbmtzQWZ0ZXJNc10pXG5cbiAgY29uc3Qgc2hvd1N1Ym1pdHRlZFRoZW5DbG9zZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTdGF0ZSgnc3VibWl0dGVkJylcbiAgICBzZXRUaW1lb3V0KHNldFN0YXRlLCBoaWRlVGhhbmtzQWZ0ZXJNcywgJ2Nsb3NlZCcpXG4gIH0sIFtoaWRlVGhhbmtzQWZ0ZXJNc10pXG5cbiAgY29uc3Qgb3BlbiA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoc3RhdGUgIT09ICdjbG9zZWQnKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgc2V0U3RhdGUoJ29wZW4nKVxuICAgIGFwcGVhcmFuY2VJZC5jdXJyZW50ID0gcmFuZG9tVVVJRCgpXG4gICAgdm9pZCBvbk9wZW4oYXBwZWFyYW5jZUlkLmN1cnJlbnQpXG4gIH0sIFtzdGF0ZSwgb25PcGVuXSlcblxuICBjb25zdCBoYW5kbGVTZWxlY3QgPSB1c2VDYWxsYmFjayhcbiAgICAoc2VsZWN0ZWQ6IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UpOiBib29sZWFuID0+IHtcbiAgICAgIHNldExhc3RSZXNwb25zZShzZWxlY3RlZClcbiAgICAgIGxhc3RSZXNwb25zZVJlZi5jdXJyZW50ID0gc2VsZWN0ZWRcbiAgICAgIC8vIEFsd2F5cyBmaXJlIHRoZSBzdXJ2ZXkgcmVzcG9uc2UgZXZlbnQgZmlyc3RcbiAgICAgIHZvaWQgb25TZWxlY3QoYXBwZWFyYW5jZUlkLmN1cnJlbnQsIHNlbGVjdGVkKVxuXG4gICAgICBpZiAoc2VsZWN0ZWQgPT09ICdkaXNtaXNzZWQnKSB7XG4gICAgICAgIHNldFN0YXRlKCdjbG9zZWQnKVxuICAgICAgICBzZXRMYXN0UmVzcG9uc2UobnVsbClcbiAgICAgIH0gZWxzZSBpZiAoc2hvdWxkU2hvd1RyYW5zY3JpcHRQcm9tcHQ/LihzZWxlY3RlZCkpIHtcbiAgICAgICAgc2V0U3RhdGUoJ3RyYW5zY3JpcHRfcHJvbXB0JylcbiAgICAgICAgb25UcmFuc2NyaXB0UHJvbXB0U2hvd24/LihhcHBlYXJhbmNlSWQuY3VycmVudCwgc2VsZWN0ZWQpXG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzaG93VGhhbmtzVGhlbkNsb3NlKClcbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH0sXG4gICAgW1xuICAgICAgc2hvd1RoYW5rc1RoZW5DbG9zZSxcbiAgICAgIG9uU2VsZWN0LFxuICAgICAgc2hvdWxkU2hvd1RyYW5zY3JpcHRQcm9tcHQsXG4gICAgICBvblRyYW5zY3JpcHRQcm9tcHRTaG93bixcbiAgICBdLFxuICApXG5cbiAgY29uc3QgaGFuZGxlVHJhbnNjcmlwdFNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgIChzZWxlY3RlZDogVHJhbnNjcmlwdFNoYXJlUmVzcG9uc2UpID0+IHtcbiAgICAgIHN3aXRjaCAoc2VsZWN0ZWQpIHtcbiAgICAgICAgY2FzZSAneWVzJzpcbiAgICAgICAgICBzZXRTdGF0ZSgnc3VibWl0dGluZycpXG4gICAgICAgICAgdm9pZCAoYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgY29uc3Qgc3VjY2VzcyA9IGF3YWl0IG9uVHJhbnNjcmlwdFNlbGVjdD8uKFxuICAgICAgICAgICAgICAgIGFwcGVhcmFuY2VJZC5jdXJyZW50LFxuICAgICAgICAgICAgICAgIHNlbGVjdGVkLFxuICAgICAgICAgICAgICAgIGxhc3RSZXNwb25zZVJlZi5jdXJyZW50LFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIGlmIChzdWNjZXNzKSB7XG4gICAgICAgICAgICAgICAgc2hvd1N1Ym1pdHRlZFRoZW5DbG9zZSgpXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc2hvd1RoYW5rc1RoZW5DbG9zZSgpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgICBzaG93VGhhbmtzVGhlbkNsb3NlKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9KSgpXG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgY2FzZSAnbm8nOlxuICAgICAgICBjYXNlICdkb250X2Fza19hZ2Fpbic6XG4gICAgICAgICAgdm9pZCBvblRyYW5zY3JpcHRTZWxlY3Q/LihcbiAgICAgICAgICAgIGFwcGVhcmFuY2VJZC5jdXJyZW50LFxuICAgICAgICAgICAgc2VsZWN0ZWQsXG4gICAgICAgICAgICBsYXN0UmVzcG9uc2VSZWYuY3VycmVudCxcbiAgICAgICAgICApXG4gICAgICAgICAgc2hvd1RoYW5rc1RoZW5DbG9zZSgpXG4gICAgICAgICAgYnJlYWtcbiAgICAgIH1cbiAgICB9LFxuICAgIFtzaG93VGhhbmtzVGhlbkNsb3NlLCBzaG93U3VibWl0dGVkVGhlbkNsb3NlLCBvblRyYW5zY3JpcHRTZWxlY3RdLFxuICApXG5cbiAgcmV0dXJuIHsgc3RhdGUsIGxhc3RSZXNwb25zZSwgb3BlbiwgaGFuZGxlU2VsZWN0LCBoYW5kbGVUcmFuc2NyaXB0U2VsZWN0IH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsVUFBVSxRQUFRLFFBQVE7QUFDbkMsU0FBU0MsV0FBVyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3JELGNBQWNDLHVCQUF1QixRQUFRLDRCQUE0QjtBQUN6RSxjQUFjQyxzQkFBc0IsUUFBUSxZQUFZO0FBRXhELEtBQUtDLFdBQVcsR0FDWixRQUFRLEdBQ1IsTUFBTSxHQUNOLFFBQVEsR0FDUixtQkFBbUIsR0FDbkIsWUFBWSxHQUNaLFdBQVc7QUFFZixLQUFLQyxxQkFBcUIsR0FBRztFQUMzQkMsaUJBQWlCLEVBQUUsTUFBTTtFQUN6QkMsTUFBTSxFQUFFLENBQUNDLFlBQVksRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJLEdBQUdDLE9BQU8sQ0FBQyxJQUFJLENBQUM7RUFDdERDLFFBQVEsRUFBRSxDQUNSRixZQUFZLEVBQUUsTUFBTSxFQUNwQkcsUUFBUSxFQUFFUixzQkFBc0IsRUFDaEMsR0FBRyxJQUFJLEdBQUdNLE9BQU8sQ0FBQyxJQUFJLENBQUM7RUFDekJHLDBCQUEwQixDQUFDLEVBQUUsQ0FBQ0QsUUFBUSxFQUFFUixzQkFBc0IsRUFBRSxHQUFHLE9BQU87RUFDMUVVLHVCQUF1QixDQUFDLEVBQUUsQ0FDeEJMLFlBQVksRUFBRSxNQUFNLEVBQ3BCTSxjQUFjLEVBQUVYLHNCQUFzQixFQUN0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 \ No newline at end of file diff --git a/components/FileEditToolDiff.tsx b/components/FileEditToolDiff.tsx new file mode 100644 index 0000000..6b4896d --- /dev/null +++ b/components/FileEditToolDiff.tsx @@ -0,0 +1,181 @@ +import { c as _c } from "react/compiler-runtime"; +import type { StructuredPatchHunk } from 'diff'; +import * as React from 'react'; +import { Suspense, use, useState } from 'react'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { Box, Text } from '../ink.js'; +import type { FileEdit } from '../tools/FileEditTool/types.js'; +import { findActualString, preserveQuoteStyle } from '../tools/FileEditTool/utils.js'; +import { adjustHunkLineNumbers, CONTEXT_LINES, getPatchForDisplay } from '../utils/diff.js'; +import { logError } from '../utils/log.js'; +import { CHUNK_SIZE, openForScan, readCapped, scanForContext } from '../utils/readEditContext.js'; +import { firstLineOf } from '../utils/stringUtils.js'; +import { StructuredDiffList } from './StructuredDiffList.js'; +type Props = { + file_path: string; + edits: FileEdit[]; +}; +type DiffData = { + patch: StructuredPatchHunk[]; + firstLine: string | null; + fileContent: string | undefined; +}; +export function FileEditToolDiff(props) { + const $ = _c(7); + let t0; + if ($[0] !== props.edits || $[1] !== props.file_path) { + t0 = () => loadDiffData(props.file_path, props.edits); + $[0] = props.edits; + $[1] = props.file_path; + $[2] = t0; + } else { + t0 = $[2]; + } + const [dataPromise] = useState(t0); + let t1; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[3] = t1; + } else { + t1 = $[3]; + } + let t2; + if ($[4] !== dataPromise || $[5] !== props.file_path) { + t2 = ; + $[4] = dataPromise; + $[5] = props.file_path; + $[6] = t2; + } else { + t2 = $[6]; + } + return t2; +} +function DiffBody(t0) { + const $ = _c(6); + const { + promise, + file_path + } = t0; + const { + patch, + firstLine, + fileContent + } = use(promise); + const { + columns + } = useTerminalSize(); + let t1; + if ($[0] !== columns || $[1] !== fileContent || $[2] !== file_path || $[3] !== firstLine || $[4] !== patch) { + t1 = ; + $[0] = columns; + $[1] = fileContent; + $[2] = file_path; + $[3] = firstLine; + $[4] = patch; + $[5] = t1; + } else { + t1 = $[5]; + } + return t1; +} +function DiffFrame(t0) { + const $ = _c(5); + const { + children, + placeholder + } = t0; + let t1; + if ($[0] !== children || $[1] !== placeholder) { + t1 = placeholder ? : children; + $[0] = children; + $[1] = placeholder; + $[2] = t1; + } else { + t1 = $[2]; + } + let t2; + if ($[3] !== t1) { + t2 = {t1}; + $[3] = t1; + $[4] = t2; + } else { + t2 = $[4]; + } + return t2; +} +async function loadDiffData(file_path: string, edits: FileEdit[]): Promise { + const valid = edits.filter(e => e.old_string != null && e.new_string != null); + const single = valid.length === 1 ? valid[0]! : undefined; + + // SedEditPermissionRequest passes the entire file as old_string. Scanning for + // a needle ≥ CHUNK_SIZE allocates O(needle) for the overlap buffer — skip the + // file read entirely and diff the inputs we already have. + if (single && single.old_string.length >= CHUNK_SIZE) { + return diffToolInputsOnly(file_path, [single]); + } + try { + const handle = await openForScan(file_path); + if (handle === null) return diffToolInputsOnly(file_path, valid); + try { + // Multi-edit and empty old_string genuinely need full-file for sequential + // replacements — structuredPatch needs before/after strings. replace_all + // routes through the chunked path below (shows first-occurrence window; + // matches within the slice still replace via edit.replace_all). + if (!single || single.old_string === '') { + const file = await readCapped(handle); + if (file === null) return diffToolInputsOnly(file_path, valid); + const normalized = valid.map(e => normalizeEdit(file, e)); + return { + patch: getPatchForDisplay({ + filePath: file_path, + fileContents: file, + edits: normalized + }), + firstLine: firstLineOf(file), + fileContent: file + }; + } + const ctx = await scanForContext(handle, single.old_string, CONTEXT_LINES); + if (ctx.truncated || ctx.content === '') { + return diffToolInputsOnly(file_path, [single]); + } + const normalized = normalizeEdit(ctx.content, single); + const hunks = getPatchForDisplay({ + filePath: file_path, + fileContents: ctx.content, + edits: [normalized] + }); + return { + patch: adjustHunkLineNumbers(hunks, ctx.lineOffset - 1), + firstLine: ctx.lineOffset === 1 ? firstLineOf(ctx.content) : null, + fileContent: ctx.content + }; + } finally { + await handle.close(); + } + } catch (e) { + logError(e as Error); + return diffToolInputsOnly(file_path, valid); + } +} +function diffToolInputsOnly(filePath: string, edits: FileEdit[]): DiffData { + return { + patch: edits.flatMap(e => getPatchForDisplay({ + filePath, + fileContents: e.old_string, + edits: [e] + })), + firstLine: null, + fileContent: undefined + }; +} +function normalizeEdit(fileContent: string, edit: FileEdit): FileEdit { + const actualOld = findActualString(fileContent, edit.old_string) || edit.old_string; + const actualNew = preserveQuoteStyle(edit.old_string, actualOld, edit.new_string); + return { + ...edit, + old_string: actualOld, + new_string: actualNew + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTdHJ1Y3R1cmVkUGF0Y2hIdW5rIiwiUmVhY3QiLCJTdXNwZW5zZSIsInVzZSIsInVzZVN0YXRlIiwidXNlVGVybWluYWxTaXplIiwiQm94IiwiVGV4dCIsIkZpbGVFZGl0IiwiZmluZEFjdHVhbFN0cmluZyIsInByZXNlcnZlUXVvdGVTdHlsZSIsImFkanVzdEh1bmtMaW5lTnVtYmVycyIsIkNPTlRFWFRfTElORVMiLCJnZXRQYXRjaEZvckRpc3BsYXkiLCJsb2dFcnJvciIsIkNIVU5LX1NJWkUiLCJvcGVuRm9yU2NhbiIsInJlYWRDYXBwZWQiLCJzY2FuRm9yQ29udGV4dCIsImZpcnN0TGluZU9mIiwiU3RydWN0dXJlZERpZmZMaXN0IiwiUHJvcHMiLCJmaWxlX3BhdGgiLCJlZGl0cyIsIkRpZmZEYXRhIiwicGF0Y2giLCJmaXJzdExpbmUiLCJmaWxlQ29udGVudCIsIkZpbGVFZGl0VG9vbERpZmYiLCJwcm9wcyIsIiQiLCJfYyIsInQwIiwibG9hZERpZmZEYXRhIiwiZGF0YVByb21pc2UiLCJ0MSIsIlN5bWJvbCIsImZvciIsInQyIiwiRGlmZkJvZHkiLCJwcm9taXNlIiwiY29sdW1ucyIsIkRpZmZGcmFtZSIsImNoaWxkcmVuIiwicGxhY2Vob2xkZXIiLCJQcm9taXNlIiwidmFsaWQiLCJmaWx0ZXIiLCJlIiwib2xkX3N0cmluZyIsIm5ld19zdHJpbmciLCJzaW5nbGUiLCJsZW5ndGgiLCJ1bmRlZmluZWQiLCJkaWZmVG9vbElucHV0c09ubHkiLCJoYW5kbGUiLCJmaWxlIiwibm9ybWFsaXplZCIsIm1hcCIsIm5vcm1hbGl6ZUVkaXQiLCJmaWxlUGF0aCIsImZpbGVDb250ZW50cyIsImN0eCIsInRydW5jYXRlZCIsImNvbnRlbnQiLCJodW5rcyIsImxpbmVPZmZzZXQiLCJjbG9zZSIsIkVycm9yIiwiZmxhdE1hcCIsImVkaXQiLCJhY3R1YWxPbGQiLCJhY3R1YWxOZXciXSwic291cmNlcyI6WyJGaWxlRWRpdFRvb2xEaWZmLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IFN0cnVjdHVyZWRQYXRjaEh1bmsgfSBmcm9tICdkaWZmJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBTdXNwZW5zZSwgdXNlLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBGaWxlRWRpdCB9IGZyb20gJy4uL3Rvb2xzL0ZpbGVFZGl0VG9vbC90eXBlcy5qcydcbmltcG9ydCB7XG4gIGZpbmRBY3R1YWxTdHJpbmcsXG4gIHByZXNlcnZlUXVvdGVTdHlsZSxcbn0gZnJvbSAnLi4vdG9vbHMvRmlsZUVkaXRUb29sL3V0aWxzLmpzJ1xuaW1wb3J0IHtcbiAgYWRqdXN0SHVua0xpbmVOdW1iZXJzLFxuICBDT05URVhUX0xJTkVTLFxuICBnZXRQYXRjaEZvckRpc3BsYXksXG59IGZyb20gJy4uL3V0aWxzL2RpZmYuanMnXG5pbXBvcnQgeyBsb2dFcnJvciB9IGZyb20gJy4uL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7XG4gIENIVU5LX1NJWkUsXG4gIG9wZW5Gb3JTY2FuLFxuICByZWFkQ2FwcGVkLFxuICBzY2FuRm9yQ29udGV4dCxcbn0gZnJvbSAnLi4vdXRpbHMvcmVhZEVkaXRDb250ZXh0LmpzJ1xuaW1wb3J0IHsgZmlyc3RMaW5lT2YgfSBmcm9tICcuLi91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB7IFN0cnVjdHVyZWREaWZmTGlzdCB9IGZyb20gJy4vU3RydWN0dXJlZERpZmZMaXN0LmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBmaWxlX3BhdGg6IHN0cmluZ1xuICBlZGl0czogRmlsZUVkaXRbXVxufVxuXG50eXBlIERpZmZEYXRhID0ge1xuICBwYXRjaDogU3RydWN0dXJlZFBhdGNoSHVua1tdXG4gIGZpcnN0TGluZTogc3RyaW5nIHwgbnVsbFxuICBmaWxlQ29udGVudDogc3RyaW5nIHwgdW5kZWZpbmVkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBGaWxlRWRpdFRvb2xEaWZmKHByb3BzOiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIFNuYXBzaG90IG9uIG1vdW50IOKAlCB0aGUgZGlmZiBtdXN0IHN0YXkgY29uc2lzdGVudCBldmVuIGlmIHRoZSBmaWxlIGNoYW5nZXNcbiAgLy8gd2hpbGUgdGhlIGRpYWxvZyBpcyBvcGVuLiB1c2VNZW1vIG9uIHByb3BzLmVkaXRzIHdvdWxkIHJlLXJlYWQgdGhlIGZpbGUgb25cbiAgLy8gZXZlcnkgcmVuZGVyIGJlY2F1c2UgY2FsbGVycyBwYXNzIGZyZXNoIGFycmF5IGxpdGVyYWxzLlxuICBjb25zdCBbZGF0YVByb21pc2VdID0gdXNlU3RhdGUoKCkgPT5cbiAgICBsb2FkRGlmZkRhdGEocHJvcHMuZmlsZV9wYXRoLCBwcm9wcy5lZGl0cyksXG4gIClcbiAgcmV0dXJuIChcbiAgICA8U3VzcGVuc2UgZmFsbGJhY2s9ezxEaWZmRnJhbWUgcGxhY2Vob2xkZXIgLz59PlxuICAgICAgPERpZmZCb2R5IHByb21pc2U9e2RhdGFQcm9taXNlfSBmaWxlX3BhdGg9e3Byb3BzLmZpbGVfcGF0aH0gLz5cbiAgICA8L1N1c3BlbnNlPlxuICApXG59XG5cbmZ1bmN0aW9uIERpZmZCb2R5KHtcbiAgcHJvbWlzZSxcbiAgZmlsZV9wYXRoLFxufToge1xuICBwcm9taXNlOiBQcm9taXNlPERpZmZEYXRhPlxuICBmaWxlX3BhdGg6IHN0cmluZ1xufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgcGF0Y2gsIGZpcnN0TGluZSwgZmlsZUNvbnRlbnQgfSA9IHVzZShwcm9taXNlKVxuICBjb25zdCB7IGNvbHVtbnMgfSA9IHVzZVRlcm1pbmFsU2l6ZSgpXG4gIHJldHVybiAoXG4gICAgPERpZmZGcmFtZT5cbiAgICAgIDxTdHJ1Y3R1cmVkRGlmZkxpc3RcbiAgICAgICAgaHVua3M9e3BhdGNofVxuICAgICAgICBkaW09e2ZhbHNlfVxuICAgICAgICB3aWR0aD17Y29sdW1uc31cbiAgICAgICAgZmlsZVBhdGg9e2ZpbGVfcGF0aH1cbiAgICAgICAgZmlyc3RMaW5lPXtmaXJzdExpbmV9XG4gICAgICAgIGZpbGVDb250ZW50PXtmaWxlQ29udGVudH1cbiAgICAgIC8+XG4gICAgPC9EaWZmRnJhbWU+XG4gIClcbn1cblxuZnVuY3Rpb24gRGlmZkZyYW1lKHtcbiAgY2hpbGRyZW4sXG4gIHBsYWNlaG9sZGVyLFxufToge1xuICBjaGlsZHJlbj86IFJlYWN0LlJlYWN0Tm9kZVxuICBwbGFjZWhvbGRlcj86IGJvb2xlYW5cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPEJveFxuICAgICAgICBib3JkZXJDb2xvcj1cInN1YnRsZVwiXG4gICAgICAgIGJvcmRlclN0eWxlPVwiZGFzaGVkXCJcbiAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgIGJvcmRlckxlZnQ9e2ZhbHNlfVxuICAgICAgICBib3JkZXJSaWdodD17ZmFsc2V9XG4gICAgICA+XG4gICAgICAgIHtwbGFjZWhvbGRlciA/IDxUZXh0IGRpbUNvbG9yPuKApjwvVGV4dD4gOiBjaGlsZHJlbn1cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG5cbmFzeW5jIGZ1bmN0aW9uIGxvYWREaWZmRGF0YShcbiAgZmlsZV9wYXRoOiBzdHJpbmcsXG4gIGVkaXRzOiBGaWxlRWRpdFtdLFxuKTogUHJvbWlzZTxEaWZmRGF0YT4ge1xuICBjb25zdCB2YWxpZCA9IGVkaXRzLmZpbHRlcihlID0+IGUub2xkX3N0cmluZyAhPSBudWxsICYmIGUubmV3X3N0cmluZyAhPSBudWxsKVxuICBjb25zdCBzaW5nbGUgPSB2YWxpZC5sZW5ndGggPT09IDEgPyB2YWxpZFswXSEgOiB1bmRlZmluZWRcblxuICAvLyBTZWRFZGl0UGVybWlzc2lvblJlcXVlc3QgcGFzc2VzIHRoZSBlbnRpcmUgZmlsZSBhcyBvbGRfc3RyaW5nLiBTY2FubmluZyBmb3JcbiAgLy8gYSBuZWVkbGUg4omlIENIVU5LX1NJWkUgYWxsb2NhdGVzIE8obmVlZGxlKSBmb3IgdGhlIG92ZXJsYXAgYnVmZmVyIOKAlCBza2lwIHRoZVxuICAvLyBmaWxlIHJlYWQgZW50aXJlbHkgYW5kIGRpZmYgdGhlIGlucHV0cyB3ZSBhbHJlYWR5IGhhdmUuXG4gIGlmIChzaW5nbGUgJiYgc2luZ2xlLm9sZF9zdHJpbmcubGVuZ3RoID49IENIVU5LX1NJWkUpIHtcbiAgICByZXR1cm4gZGlmZlRvb2xJbnB1dHNPbmx5KGZpbGVfcGF0aCwgW3NpbmdsZV0pXG4gIH1cblxuICB0cnkge1xuICAgIGNvbnN0IGhhbmRsZSA9IGF3YWl0IG9wZW5Gb3JTY2FuKGZpbGVfcGF0aClcbiAgICBpZiAoaGFuZGxlID09PSBudWxsKSByZXR1cm4gZGlmZlRvb2xJbnB1dHNPbmx5KGZpbGVfcGF0aCwgdmFsaWQpXG4gICAgdHJ5IHtcbiAgICAgIC8vIE11bHRpLWVkaXQgYW5kIGVtcHR5IG9sZF9zdHJpbmcgZ2VudWluZWx5IG5lZWQgZnVsbC1maWxlIGZvciBzZXF1ZW50aWFsXG4gICAgICAvLyByZXBsYWNlbWVudHMg4oCUIHN0cnVjdHVyZWRQYXRjaCBuZWVkcyBiZWZvcmUvYWZ0ZXIgc3RyaW5ncy4gcmVwbGFjZV9hbGxcbiAgICAgIC8vIHJvdXRlcyB0aHJvdWdoIHRoZSBjaHVua2VkIHBhdGggYmVsb3cgKHNob3dzIGZpcnN0LW9jY3VycmVuY2Ugd2luZG93O1xuICAgICAgLy8gbWF0Y2hlcyB3aXRoaW4gdGhlIHNsaWNlIHN0aWxsIHJlcGxhY2UgdmlhIGVkaXQucmVwbGFjZV9hbGwpLlxuICAgICAgaWYgKCFzaW5nbGUgfHwgc2luZ2xlLm9sZF9zdHJpbmcgPT09ICcnKSB7XG4gICAgICAgIGNvbnN0IGZpbGUgPSBhd2FpdCByZWFkQ2FwcGVkKGhhbmRsZSlcbiAgICAgICAgaWYgKGZpbGUgPT09IG51bGwpIHJldHVybiBkaWZmVG9vbElucHV0c09ubHkoZmlsZV9wYXRoLCB2YWxpZClcbiAgICAgICAgY29uc3Qgbm9ybWFsaXplZCA9IHZhbGlkLm1hcChlID0+IG5vcm1hbGl6ZUVkaXQoZmlsZSwgZSkpXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgcGF0Y2g6IGdldFBhdGNoRm9yRGlzcGxheSh7XG4gICAgICAgICAgICBmaWxlUGF0aDogZmlsZV9wYXRoLFxuICAgICAgICAgICAgZmlsZUNvbnRlbnRzOiBmaWxlLFxuICAgICAgICAgICAgZWRpdHM6IG5vcm1hbGl6ZWQsXG4gICAgICAgICAgfSksXG4gICAgICAgICAgZmlyc3RMaW5lOiBmaXJzdExpbmVPZihmaWxlKSxcbiAgICAgICAgICBmaWxlQ29udGVudDogZmlsZSxcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjb25zdCBjdHggPSBhd2FpdCBzY2FuRm9yQ29udGV4dChoYW5kbGUsIHNpbmdsZS5vbGRfc3RyaW5nLCBDT05URVhUX0xJTkVTKVxuICAgICAgaWYgKGN0eC50cnVuY2F0ZWQgfHwgY3R4LmNvbnRlbnQgPT09ICcnKSB7XG4gICAgICAgIHJldHVybiBkaWZmVG9vbElucHV0c09ubHkoZmlsZV9wYXRoLCBbc2luZ2xlXSlcbiAgICAgIH1cbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPSBub3JtYWxpemVFZGl0KGN0eC5jb250ZW50LCBzaW5nbGUpXG4gICAgICBjb25zdCBodW5rcyA9IGdldFBhdGNoRm9yRGlzcGxheSh7XG4gICAgICAgIGZpbGVQYXRoOiBmaWxlX3BhdGgsXG4gICAgICAgIGZpbGVDb250ZW50czogY3R4LmNvbnRlbnQsXG4gICAgICAgIGVkaXRzOiBbbm9ybWFsaXplZF0sXG4gICAgICB9KVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcGF0Y2g6IGFkanVzdEh1bmtMaW5lTnVtYmVycyhodW5rcywgY3R4LmxpbmVPZmZzZXQgLSAxKSxcbiAgICAgICAgZmlyc3RMaW5lOiBjdHgubGluZU9mZnNldCA9PT0gMSA/IGZpcnN0TGluZU9mKGN0eC5jb250ZW50KSA6IG51bGwsXG4gICAgICAgIGZpbGVDb250ZW50OiBjdHguY29udGVudCxcbiAgICAgIH1cbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgaGFuZGxlLmNsb3NlKClcbiAgICB9XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBsb2dFcnJvcihlIGFzIEVycm9yKVxuICAgIHJldHVybiBkaWZmVG9vbElucHV0c09ubHkoZmlsZV9wYXRoLCB2YWxpZClcbiAgfVxufVxuXG5mdW5jdGlvbiBkaWZmVG9vbElucHV0c09ubHkoZmlsZVBhdGg6IHN0cmluZywgZWRpdHM6IEZpbGVFZGl0W10pOiBEaWZmRGF0YSB7XG4gIHJldHVybiB7XG4gICAgcGF0Y2g6IGVkaXRzLmZsYXRNYXAoZSA9PlxuICAgICAgZ2V0UGF0Y2hGb3JEaXNwbGF5KHtcbiAgICAgICAgZmlsZVBhdGgsXG4gICAgICAgIGZpbGVDb250ZW50czogZS5vbGRfc3RyaW5nLFxuICAgICAgICBlZGl0czogW2VdLFxuICAgICAgfSksXG4gICAgKSxcbiAgICBmaXJzdExpbmU6IG51bGwsXG4gICAgZmlsZUNvbnRlbnQ6IHVuZGVmaW5lZCxcbiAgfVxufVxuXG5mdW5jdGlvbiBub3JtYWxpemVFZGl0KGZpbGVDb250ZW50OiBzdHJpbmcsIGVkaXQ6IEZpbGVFZGl0KTogRmlsZUVkaXQge1xuICBjb25zdCBhY3R1YWxPbGQgPVxuICAgIGZpbmRBY3R1YWxTdHJpbmcoZmlsZUNvbnRlbnQsIGVkaXQub2xkX3N0cmluZykgfHwgZWRpdC5vbGRfc3RyaW5nXG4gIGNvbnN0IGFjdHVhbE5ldyA9IHByZXNlcnZlUXVvdGVTdHlsZShcbiAgICBlZGl0Lm9sZF9zdHJpbmcsXG4gICAgYWN0dWFsT2xkLFxuICAgIGVkaXQubmV3X3N0cmluZyxcbiAgKVxuICByZXR1cm4geyAuLi5lZGl0LCBvbGRfc3RyaW5nOiBhY3R1YWxPbGQsIG5ld19zdHJpbmc6IGFjdHVhbE5l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 \ No newline at end of file diff --git a/components/FileEditToolUpdatedMessage.tsx b/components/FileEditToolUpdatedMessage.tsx new file mode 100644 index 0000000..909889a --- /dev/null +++ b/components/FileEditToolUpdatedMessage.tsx @@ -0,0 +1,124 @@ +import { c as _c } from "react/compiler-runtime"; +import type { StructuredPatchHunk } from 'diff'; +import * as React from 'react'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { Box, Text } from '../ink.js'; +import { count } from '../utils/array.js'; +import { MessageResponse } from './MessageResponse.js'; +import { StructuredDiffList } from './StructuredDiffList.js'; +type Props = { + filePath: string; + structuredPatch: StructuredPatchHunk[]; + firstLine: string | null; + fileContent?: string; + style?: 'condensed'; + verbose: boolean; + previewHint?: string; +}; +export function FileEditToolUpdatedMessage(t0) { + const $ = _c(22); + const { + filePath, + structuredPatch, + firstLine, + fileContent, + style, + verbose, + previewHint + } = t0; + const { + columns + } = useTerminalSize(); + const numAdditions = structuredPatch.reduce(_temp2, 0); + const numRemovals = structuredPatch.reduce(_temp4, 0); + let t1; + if ($[0] !== numAdditions) { + t1 = numAdditions > 0 ? <>Added {numAdditions}{" "}{numAdditions > 1 ? "lines" : "line"} : null; + $[0] = numAdditions; + $[1] = t1; + } else { + t1 = $[1]; + } + const t2 = numAdditions > 0 && numRemovals > 0 ? ", " : null; + let t3; + if ($[2] !== numAdditions || $[3] !== numRemovals) { + t3 = numRemovals > 0 ? <>{numAdditions === 0 ? "R" : "r"}emoved {numRemovals}{" "}{numRemovals > 1 ? "lines" : "line"} : null; + $[2] = numAdditions; + $[3] = numRemovals; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== t1 || $[6] !== t2 || $[7] !== t3) { + t4 = {t1}{t2}{t3}; + $[5] = t1; + $[6] = t2; + $[7] = t3; + $[8] = t4; + } else { + t4 = $[8]; + } + const text = t4; + if (previewHint) { + if (style !== "condensed" && !verbose) { + let t5; + if ($[9] !== previewHint) { + t5 = {previewHint}; + $[9] = previewHint; + $[10] = t5; + } else { + t5 = $[10]; + } + return t5; + } + } else { + if (style === "condensed" && !verbose) { + return text; + } + } + let t5; + if ($[11] !== text) { + t5 = {text}; + $[11] = text; + $[12] = t5; + } else { + t5 = $[12]; + } + const t6 = columns - 12; + let t7; + if ($[13] !== fileContent || $[14] !== filePath || $[15] !== firstLine || $[16] !== structuredPatch || $[17] !== t6) { + t7 = ; + $[13] = fileContent; + $[14] = filePath; + $[15] = firstLine; + $[16] = structuredPatch; + $[17] = t6; + $[18] = t7; + } else { + t7 = $[18]; + } + let t8; + if ($[19] !== t5 || $[20] !== t7) { + t8 = {t5}{t7}; + $[19] = t5; + $[20] = t7; + $[21] = t8; + } else { + t8 = $[21]; + } + return t8; +} +function _temp4(acc_0, hunk_0) { + return acc_0 + count(hunk_0.lines, _temp3); +} +function _temp3(__0) { + return __0.startsWith("-"); +} +function _temp2(acc, hunk) { + return acc + count(hunk.lines, _temp); +} +function _temp(_) { + return _.startsWith("+"); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTdHJ1Y3R1cmVkUGF0Y2hIdW5rIiwiUmVhY3QiLCJ1c2VUZXJtaW5hbFNpemUiLCJCb3giLCJUZXh0IiwiY291bnQiLCJNZXNzYWdlUmVzcG9uc2UiLCJTdHJ1Y3R1cmVkRGlmZkxpc3QiLCJQcm9wcyIsImZpbGVQYXRoIiwic3RydWN0dXJlZFBhdGNoIiwiZmlyc3RMaW5lIiwiZmlsZUNvbnRlbnQiLCJzdHlsZSIsInZlcmJvc2UiLCJwcmV2aWV3SGludCIsIkZpbGVFZGl0VG9vbFVwZGF0ZWRNZXNzYWdlIiwidDAiLCIkIiwiX2MiLCJjb2x1bW5zIiwibnVtQWRkaXRpb25zIiwicmVkdWNlIiwiX3RlbXAyIiwibnVtUmVtb3ZhbHMiLCJfdGVtcDQiLCJ0MSIsInQyIiwidDMiLCJ0NCIsInRleHQiLCJ0NSIsInQ2IiwidDciLCJ0OCIsImFjY18wIiwiaHVua18wIiwiYWNjIiwiaHVuayIsImxpbmVzIiwiX3RlbXAzIiwiX18wIiwiXyIsInN0YXJ0c1dpdGgiLCJfdGVtcCJdLCJzb3VyY2VzIjpbIkZpbGVFZGl0VG9vbFVwZGF0ZWRNZXNzYWdlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IFN0cnVjdHVyZWRQYXRjaEh1bmsgfSBmcm9tICdkaWZmJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBjb3VudCB9IGZyb20gJy4uL3V0aWxzL2FycmF5LmpzJ1xuaW1wb3J0IHsgTWVzc2FnZVJlc3BvbnNlIH0gZnJvbSAnLi9NZXNzYWdlUmVzcG9uc2UuanMnXG5pbXBvcnQgeyBTdHJ1Y3R1cmVkRGlmZkxpc3QgfSBmcm9tICcuL1N0cnVjdHVyZWREaWZmTGlzdC5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgZmlsZVBhdGg6IHN0cmluZ1xuICBzdHJ1Y3R1cmVkUGF0Y2g6IFN0cnVjdHVyZWRQYXRjaEh1bmtbXVxuICBmaXJzdExpbmU6IHN0cmluZyB8IG51bGxcbiAgZmlsZUNvbnRlbnQ/OiBzdHJpbmdcbiAgc3R5bGU/OiAnY29uZGVuc2VkJ1xuICB2ZXJib3NlOiBib29sZWFuXG4gIHByZXZpZXdIaW50Pzogc3RyaW5nXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBGaWxlRWRpdFRvb2xVcGRhdGVkTWVzc2FnZSh7XG4gIGZpbGVQYXRoLFxuICBzdHJ1Y3R1cmVkUGF0Y2gsXG4gIGZpcnN0TGluZSxcbiAgZmlsZUNvbnRlbnQsXG4gIHN0eWxlLFxuICB2ZXJib3NlLFxuICBwcmV2aWV3SGludCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBjb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCBudW1BZGRpdGlvbnMgPSBzdHJ1Y3R1cmVkUGF0Y2gucmVkdWNlKFxuICAgIChhY2MsIGh1bmspID0+IGFjYyArIGNvdW50KGh1bmsubGluZXMsIF8gPT4gXy5zdGFydHNXaXRoKCcrJykpLFxuICAgIDAsXG4gIClcbiAgY29uc3QgbnVtUmVtb3ZhbHMgPSBzdHJ1Y3R1cmVkUGF0Y2gucmVkdWNlKFxuICAgIChhY2MsIGh1bmspID0+IGFjYyArIGNvdW50KGh1bmsubGluZXMsIF8gPT4gXy5zdGFydHNXaXRoKCctJykpLFxuICAgIDAsXG4gIClcblxuICBjb25zdCB0ZXh0ID0gKFxuICAgIDxUZXh0PlxuICAgICAge251bUFkZGl0aW9ucyA+IDAgPyAoXG4gICAgICAgIDw+XG4gICAgICAgICAgQWRkZWQgPFRleHQgYm9sZD57bnVtQWRkaXRpb25zfTwvVGV4dD57JyAnfVxuICAgICAgICAgIHtudW1BZGRpdGlvbnMgPiAxID8gJ2xpbmVzJyA6ICdsaW5lJ31cbiAgICAgICAgPC8+XG4gICAgICApIDogbnVsbH1cbiAgICAgIHtudW1BZGRpdGlvbnMgPiAwICYmIG51bVJlbW92YWxzID4gMCA/ICcsICcgOiBudWxsfVxuICAgICAge251bVJlbW92YWxzID4gMCA/IChcbiAgICAgICAgPD5cbiAgICAgICAgICB7bnVtQWRkaXRpb25zID09PSAwID8gJ1InIDogJ3InfWVtb3ZlZCA8VGV4dCBib2xkPntudW1SZW1vdmFsc308L1RleHQ+eycgJ31cbiAgICAgICAgICB7bnVtUmVtb3ZhbHMgPiAxID8gJ2xpbmVzJyA6ICdsaW5lJ31cbiAgICAgICAgPC8+XG4gICAgICApIDogbnVsbH1cbiAgICA8L1RleHQ+XG4gIClcblxuICAvLyBQbGFuIGZpbGVzOiBpbnZlcnQgY29uZGVuc2VkIGJlaGF2aW9yXG4gIC8vIC0gUmVndWxhciBtb2RlOiBqdXN0IHNob3cgdGhlIGhpbnQgKHVzZXIgY2FuIHR5cGUgL3BsYW4gdG8gc2VlIGZ1bGwgY29udGVudClcbiAgLy8gLSBDb25kZW5zZWQgbW9kZSAoc3ViYWdlbnQgdmlldyk6IHNob3cgdGhlIGRpZmZcbiAgaWYgKHByZXZpZXdIaW50KSB7XG4gICAgaWYgKHN0eWxlICE9PSAnY29uZGVuc2VkJyAmJiAhdmVyYm9zZSkge1xuICAgICAgcmV0dXJuIChcbiAgICAgICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57cHJldmlld0hpbnR9PC9UZXh0PlxuICAgICAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgICAgIClcbiAgICB9XG4gIH0gZWxzZSBpZiAoc3R5bGUgPT09ICdjb25kZW5zZWQnICYmICF2ZXJib3NlKSB7XG4gICAgcmV0dXJuIHRleHRcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8VGV4dD57dGV4dH08L1RleHQ+XG4gICAgICAgIDxTdHJ1Y3R1cmVkRGlmZkxpc3RcbiAgICAgICAgICBodW5rcz17c3RydWN0dXJlZFBhdGNofVxuICAgICAgICAgIGRpbT17ZmFsc2V9XG4gICAgICAgICAgd2lkdGg9e2NvbHVtbnMgLSAxMn1cbiAgICAgICAgICBmaWxlUGF0aD17ZmlsZVBhdGh9XG4gICAgICAgICAgZmlyc3RMaW5lPXtmaXJzdExpbmV9XG4gICAgICAgICAgZmlsZUNvbnRlbnQ9e2ZpbGVDb250ZW50fVxuICAgICAgICAvPlxuICAgICAgPC9Cb3g+XG4gICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLGNBQWNBLG1CQUFtQixRQUFRLE1BQU07QUFDL0MsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxlQUFlLFFBQVEsNkJBQTZCO0FBQzdELFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDckMsU0FBU0MsS0FBSyxRQUFRLG1CQUFtQjtBQUN6QyxTQUFTQyxlQUFlLFFBQVEsc0JBQXNCO0FBQ3RELFNBQVNDLGtCQUFrQixRQUFRLHlCQUF5QjtBQUU1RCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsUUFBUSxFQUFFLE1BQU07RUFDaEJDLGVBQWUsRUFBRVYsbUJBQW1CLEVBQUU7RUFDdENXLFNBQVMsRUFBRSxNQUFNLEdBQUcsSUFBSTtFQUN4QkMsV0FBVyxDQUFDLEVBQUUsTUFBTTtFQUNwQkMsS0FBSyxDQUFDLEVBQUUsV0FBVztFQUNuQkMsT0FBTyxFQUFFLE9BQU87RUFDaEJDLFdBQVcsQ0FBQyxFQUFFLE1BQU07QUFDdEIsQ0FBQztBQUVELE9BQU8sU0FBQUMsMkJBQUFDLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBb0M7SUFBQVYsUUFBQTtJQUFBQyxlQUFBO0lBQUFDLFNBQUE7SUFBQUMsV0FBQTtJQUFBQyxLQUFBO0lBQUFDLE9BQUE7SUFBQUM7RUFBQSxJQUFBRSxFQVFuQztFQUNOO0lBQUFHO0VBQUEsSUFBb0JsQixlQUFlLENBQUMsQ0FBQztFQUNyQyxNQUFBbUIsWUFBQSxHQUFxQlgsZUFBZSxDQUFBWSxNQUFPLENBQ3pDQyxNQUE4RCxFQUM5RCxDQUNGLENBQUM7RUFDRCxNQUFBQyxX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QTtFQUFBLElBQUFmLENBQUEsU0FBQU4sV0FBQSxJQUFBTSxDQUFBLFNBQUFULFFBQUEsSUFBQVMsQ0FBQSxTQUFBUCxTQUFBLElBQUFPLENBQUEsU0FBQVIsZUFBQSxJQUFBUSxDQUFBLFNBQUFjLEVBQUE7SUFIckJDLEVBQUEsSUFBQyxrQkFBa0IsQ0FDVnZCLEtBQWUsQ0FBZkEsZ0JBQWMsQ0FBQyxDQUNqQixHQUFLLENBQUwsTUFBSSxDQUFDLENBQ0gsS0FBWSxDQUFaLENBQUFzQixFQUFXLENBQUMsQ0FDVHZCLFFBQVEsQ0FBUkEsU0FBTyxDQUFDLENBQ1BFLFNBQVMsQ0FBVEEsVUFBUSxDQUFDLENBQ1BDLFdBQVcsQ0FBWEEsWUFBVSxDQUFDLEdBQ3hCO0lBQUFNLENBQUEsT0FBQU4sV0FBQTtJQUFBTSxDQUFBLE9BQUFULFFBQUE7SUFBQVMsQ0FBQSxPQUFBUCxTQUFBO0lBQUFPLENBQUEsT0FBQVIsZUFBQTtJQUFBUSxDQUFBLE9BQUFjLEVBQUE7SUFBQWQsQ0FBQSxPQUFBZSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBZixDQUFBO0VBQUE7RUFBQSxJQUFBZ0IsRUFBQTtFQUFBLElBQUFoQixDQUFBLFNBQUFhLEVBQUEsSUFBQWIsQ0FBQSxTQUFBZSxFQUFBO0lBVk5DLEVBQUEsSUFBQyxlQUFlLENBQ2QsQ0FBQyxHQUFHLENBQWUsYUFBUSxDQUFSLFFBQVEsQ0FDekIsQ0FBQUgsRUFBa0IsQ0FDbEIsQ0FBQUUsRUFPQyxDQUNILEVBVkMsR0FBRyxDQVdOLEVBWkMsZUFBZSxDQVlFO0lBQUFmLENBQUEsT0FBQWEsRUFBQTtJQUFBYixDQUFBLE9BQUFlLEVBQUE7SUFBQWYsQ0FBQSxPQUFBZ0IsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQWhCLENBQUE7RUFBQTtFQUFBLE9BWmxCZ0IsRUFZa0I7QUFBQTtBQWpFZixTQUFBVCxPQUFBVSxLQUFBLEVBQUFDLE1BQUE7RUFBQSxPQWVZQyxLQUFHLEdBQUdoQyxLQUFLLENBQUNpQyxNQUFJLENBQUFDLEtBQU0sRUFBRUMsTUFBc0IsQ0FBQztBQUFBO0FBZjNELFNBQUFBLE9BQUFDLEdBQUE7RUFBQSxPQWV5Q0MsR0FBQyxDQUFBQyxVQUFXLENBQUMsR0FBRyxDQUFDO0FBQUE7QUFmMUQsU0FBQXBCLE9BQUFjLEdBQUEsRUFBQUMsSUFBQTtFQUFBLE9BV1lELEdBQUcsR0FBR2hDLEtBQUssQ0FBQ2lDLElBQUksQ0FBQUMsS0FBTSxFQUFFSyxLQUFzQixDQUFDO0FBQUE7QUFYM0QsU0FBQUEsTUFBQUYsQ0FBQTtFQUFBLE9BV3lDQSxDQUFDLENBQUFDLFVBQVcsQ0FBQyxHQUFHLENBQUM7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/FileEditToolUseRejectedMessage.tsx b/components/FileEditToolUseRejectedMessage.tsx new file mode 100644 index 0000000..23bfd12 --- /dev/null +++ b/components/FileEditToolUseRejectedMessage.tsx @@ -0,0 +1,170 @@ +import { c as _c } from "react/compiler-runtime"; +import type { StructuredPatchHunk } from 'diff'; +import { relative } from 'path'; +import * as React from 'react'; +import { useTerminalSize } from 'src/hooks/useTerminalSize.js'; +import { getCwd } from 'src/utils/cwd.js'; +import { Box, Text } from '../ink.js'; +import { HighlightedCode } from './HighlightedCode.js'; +import { MessageResponse } from './MessageResponse.js'; +import { StructuredDiffList } from './StructuredDiffList.js'; +const MAX_LINES_TO_RENDER = 10; +type Props = { + file_path: string; + operation: 'write' | 'update'; + // For updates - show diff + patch?: StructuredPatchHunk[]; + firstLine: string | null; + fileContent?: string; + // For new file creation - show content preview + content?: string; + style?: 'condensed'; + verbose: boolean; +}; +export function FileEditToolUseRejectedMessage(t0) { + const $ = _c(38); + const { + file_path, + operation, + patch, + firstLine, + fileContent, + content, + style, + verbose + } = t0; + const { + columns + } = useTerminalSize(); + let t1; + if ($[0] !== operation) { + t1 = User rejected {operation} to ; + $[0] = operation; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== file_path || $[3] !== verbose) { + t2 = verbose ? file_path : relative(getCwd(), file_path); + $[2] = file_path; + $[3] = verbose; + $[4] = t2; + } else { + t2 = $[4]; + } + let t3; + if ($[5] !== t2) { + t3 = {t2}; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] !== t1 || $[8] !== t3) { + t4 = {t1}{t3}; + $[7] = t1; + $[8] = t3; + $[9] = t4; + } else { + t4 = $[9]; + } + const text = t4; + if (style === "condensed" && !verbose) { + let t5; + if ($[10] !== text) { + t5 = {text}; + $[10] = text; + $[11] = t5; + } else { + t5 = $[11]; + } + return t5; + } + if (operation === "write" && content !== undefined) { + let plusLines; + let t5; + if ($[12] !== content || $[13] !== verbose) { + const lines = content.split("\n"); + const numLines = lines.length; + plusLines = numLines - MAX_LINES_TO_RENDER; + t5 = verbose ? content : lines.slice(0, MAX_LINES_TO_RENDER).join("\n"); + $[12] = content; + $[13] = verbose; + $[14] = plusLines; + $[15] = t5; + } else { + plusLines = $[14]; + t5 = $[15]; + } + const truncatedContent = t5; + const t6 = truncatedContent || "(No content)"; + const t7 = columns - 12; + let t8; + if ($[16] !== file_path || $[17] !== t6 || $[18] !== t7) { + t8 = ; + $[16] = file_path; + $[17] = t6; + $[18] = t7; + $[19] = t8; + } else { + t8 = $[19]; + } + let t9; + if ($[20] !== plusLines || $[21] !== verbose) { + t9 = !verbose && plusLines > 0 && … +{plusLines} lines; + $[20] = plusLines; + $[21] = verbose; + $[22] = t9; + } else { + t9 = $[22]; + } + let t10; + if ($[23] !== t8 || $[24] !== t9 || $[25] !== text) { + t10 = {text}{t8}{t9}; + $[23] = t8; + $[24] = t9; + $[25] = text; + $[26] = t10; + } else { + t10 = $[26]; + } + return t10; + } + if (!patch || patch.length === 0) { + let t5; + if ($[27] !== text) { + t5 = {text}; + $[27] = text; + $[28] = t5; + } else { + t5 = $[28]; + } + return t5; + } + const t5 = columns - 12; + let t6; + if ($[29] !== fileContent || $[30] !== file_path || $[31] !== firstLine || $[32] !== patch || $[33] !== t5) { + t6 = ; + $[29] = fileContent; + $[30] = file_path; + $[31] = firstLine; + $[32] = patch; + $[33] = t5; + $[34] = t6; + } else { + t6 = $[34]; + } + let t7; + if ($[35] !== t6 || $[36] !== text) { + t7 = {text}{t6}; + $[35] = t6; + $[36] = text; + $[37] = t7; + } else { + t7 = $[37]; + } + return t7; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTdHJ1Y3R1cmVkUGF0Y2hIdW5rIiwicmVsYXRpdmUiLCJSZWFjdCIsInVzZVRlcm1pbmFsU2l6ZSIsImdldEN3ZCIsIkJveCIsIlRleHQiLCJIaWdobGlnaHRlZENvZGUiLCJNZXNzYWdlUmVzcG9uc2UiLCJTdHJ1Y3R1cmVkRGlmZkxpc3QiLCJNQVhfTElORVNfVE9fUkVOREVSIiwiUHJvcHMiLCJmaWxlX3BhdGgiLCJvcGVyYXRpb24iLCJwYXRjaCIsImZpcnN0TGluZSIsImZpbGVDb250ZW50IiwiY29udGVudCIsInN0eWxlIiwidmVyYm9zZSIsIkZpbGVFZGl0VG9vbFVzZVJlamVjdGVkTWVzc2FnZSIsInQwIiwiJCIsIl9jIiwiY29sdW1ucyIsInQxIiwidDIiLCJ0MyIsInQ0IiwidGV4dCIsInQ1IiwidW5kZWZpbmVkIiwicGx1c0xpbmVzIiwibGluZXMiLCJzcGxpdCIsIm51bUxpbmVzIiwibGVuZ3RoIiwic2xpY2UiLCJqb2luIiwidHJ1bmNhdGVkQ29udGVudCIsInQ2IiwidDciLCJ0OCIsInQ5IiwidDEwIl0sInNvdXJjZXMiOlsiRmlsZUVkaXRUb29sVXNlUmVqZWN0ZWRNZXNzYWdlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IFN0cnVjdHVyZWRQYXRjaEh1bmsgfSBmcm9tICdkaWZmJ1xuaW1wb3J0IHsgcmVsYXRpdmUgfSBmcm9tICdwYXRoJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICdzcmMvaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgZ2V0Q3dkIH0gZnJvbSAnc3JjL3V0aWxzL2N3ZC5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IEhpZ2hsaWdodGVkQ29kZSB9IGZyb20gJy4vSGlnaGxpZ2h0ZWRDb2RlLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZVJlc3BvbnNlIH0gZnJvbSAnLi9NZXNzYWdlUmVzcG9uc2UuanMnXG5pbXBvcnQgeyBTdHJ1Y3R1cmVkRGlmZkxpc3QgfSBmcm9tICcuL1N0cnVjdHVyZWREaWZmTGlzdC5qcydcblxuY29uc3QgTUFYX0xJTkVTX1RPX1JFTkRFUiA9IDEwXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGZpbGVfcGF0aDogc3RyaW5nXG4gIG9wZXJhdGlvbjogJ3dyaXRlJyB8ICd1cGRhdGUnXG4gIC8vIEZvciB1cGRhdGVzIC0gc2hvdyBkaWZmXG4gIHBhdGNoPzogU3RydWN0dXJlZFBhdGNoSHVua1tdXG4gIGZpcnN0TGluZTogc3RyaW5nIHwgbnVsbFxuICBmaWxlQ29udGVudD86IHN0cmluZ1xuICAvLyBGb3IgbmV3IGZpbGUgY3JlYXRpb24gLSBzaG93IGNvbnRlbnQgcHJldmlld1xuICBjb250ZW50Pzogc3RyaW5nXG4gIHN0eWxlPzogJ2NvbmRlbnNlZCdcbiAgdmVyYm9zZTogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gRmlsZUVkaXRUb29sVXNlUmVqZWN0ZWRNZXNzYWdlKHtcbiAgZmlsZV9wYXRoLFxuICBvcGVyYXRpb24sXG4gIHBhdGNoLFxuICBmaXJzdExpbmUsXG4gIGZpbGVDb250ZW50LFxuICBjb250ZW50LFxuICBzdHlsZSxcbiAgdmVyYm9zZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBjb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCB0ZXh0ID0gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgPFRleHQgY29sb3I9XCJzdWJ0bGVcIj5Vc2VyIHJlamVjdGVkIHtvcGVyYXRpb259IHRvIDwvVGV4dD5cbiAgICAgIDxUZXh0IGJvbGQgY29sb3I9XCJzdWJ0bGVcIj5cbiAgICAgICAge3ZlcmJvc2UgPyBmaWxlX3BhdGggOiByZWxhdGl2ZShnZXRDd2QoKSwgZmlsZV9wYXRoKX1cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxuXG4gIC8vIEZvciBjb25kZW5zZWQgc3R5bGUsIGp1c3Qgc2hvdyB0aGUgdGV4dFxuICBpZiAoc3R5bGUgPT09ICdjb25kZW5zZWQnICYmICF2ZXJib3NlKSB7XG4gICAgcmV0dXJuIDxNZXNzYWdlUmVzcG9uc2U+e3RleHR9PC9NZXNzYWdlUmVzcG9uc2U+XG4gIH1cblxuICAvLyBGb3IgbmV3IGZpbGUgY3JlYXRpb24sIHNob3cgY29udGVudCBwcmV2aWV3IChkaW1tZWQpXG4gIGlmIChvcGVyYXRpb24gPT09ICd3cml0ZScgJiYgY29udGVudCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgY29uc3QgbGluZXMgPSBjb250ZW50LnNwbGl0KCdcXG4nKVxuICAgIGNvbnN0IG51bUxpbmVzID0gbGluZXMubGVuZ3RoXG4gICAgY29uc3QgcGx1c0xpbmVzID0gbnVtTGluZXMgLSBNQVhfTElORVNfVE9fUkVOREVSXG4gICAgY29uc3QgdHJ1bmNhdGVkQ29udGVudCA9IHZlcmJvc2VcbiAgICAgID8gY29udGVudFxuICAgICAgOiBsaW5lcy5zbGljZSgwLCBNQVhfTElORVNfVE9fUkVOREVSKS5qb2luKCdcXG4nKVxuXG4gICAgcmV0dXJuIChcbiAgICAgIDxNZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIHt0ZXh0fVxuICAgICAgICAgIDxIaWdobGlnaHRlZENvZGVcbiAgICAgICAgICAgIGNvZGU9e3RydW5jYXRlZENvbnRlbnQgfHwgJyhObyBjb250ZW50KSd9XG4gICAgICAgICAgICBmaWxlUGF0aD17ZmlsZV9wYXRofVxuICAgICAgICAgICAgd2lkdGg9e2NvbHVtbnMgLSAxMn1cbiAgICAgICAgICAgIGRpbVxuICAgICAgICAgIC8+XG4gICAgICAgICAgeyF2ZXJib3NlICYmIHBsdXNMaW5lcyA+IDAgJiYgKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+4oCmICt7cGx1c0xpbmVzfSBsaW5lczwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgIDwvTWVzc2FnZVJlc3BvbnNlPlxuICAgIClcbiAgfVxuXG4gIC8vIEZvciB1cGRhdGVzLCBzaG93IGRpZmZcbiAgaWYgKCFwYXRjaCB8fCBwYXRjaC5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gPE1lc3NhZ2VSZXNwb25zZT57dGV4dH08L01lc3NhZ2VSZXNwb25zZT5cbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICB7dGV4dH1cbiAgICAgICAgPFN0cnVjdHVyZWREaWZmTGlzdFxuICAgICAgICAgIGh1bmtzPXtwYXRjaH1cbiAgICAgICAgICBkaW1cbiAgICAgICAgICB3aWR0aD17Y29sdW1ucyAtIDEyfVxuICAgICAgICAgIGZpbGVQYXRoPXtmaWxlX3BhdGh9XG4gICAgICAgICAgZmlyc3RMaW5lPXtmaXJzdExpbmV9XG4gICAgICAgICAgZmlsZUNvbnRlbnQ9e2ZpbGVDb250ZW50fVxuICAgICAgICAvPlxuICAgICAgPC9Cb3g+XG4gICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLGNBQWNBLG1CQUFtQixRQUFRLE1BQU07QUFDL0MsU0FBU0MsUUFBUSxRQUFRLE1BQU07QUFDL0IsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxlQUFlLFFBQVEsOEJBQThCO0FBQzlELFNBQVNDLE1BQU0sUUFBUSxrQkFBa0I7QUFDekMsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUNyQyxTQUFTQyxlQUFlLFFBQVEsc0JBQXNCO0FBQ3RELFNBQVNDLGVBQWUsUUFBUSxzQkFBc0I7QUFDdEQsU0FBU0Msa0JBQWtCLFFBQVEseUJBQXlCO0FBRTVELE1BQU1DLG1CQUFtQixHQUFHLEVBQUU7QUFFOUIsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLFNBQVMsRUFBRSxNQUFN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 \ No newline at end of file diff --git a/components/FilePathLink.tsx b/components/FilePathLink.tsx new file mode 100644 index 0000000..5b2917a --- /dev/null +++ b/components/FilePathLink.tsx @@ -0,0 +1,43 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { pathToFileURL } from 'url'; +import Link from '../ink/components/Link.js'; +type Props = { + /** The absolute file path */ + filePath: string; + /** Optional display text (defaults to filePath) */ + children?: React.ReactNode; +}; + +/** + * Renders a file path as an OSC 8 hyperlink. + * This helps terminals like iTerm correctly identify file paths + * even when they appear inside parentheses or other text. + */ +export function FilePathLink(t0) { + const $ = _c(5); + const { + filePath, + children + } = t0; + let t1; + if ($[0] !== filePath) { + t1 = pathToFileURL(filePath); + $[0] = filePath; + $[1] = t1; + } else { + t1 = $[1]; + } + const t2 = children ?? filePath; + let t3; + if ($[2] !== t1.href || $[3] !== t2) { + t3 = {t2}; + $[2] = t1.href; + $[3] = t2; + $[4] = t3; + } else { + t3 = $[4]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInBhdGhUb0ZpbGVVUkwiLCJMaW5rIiwiUHJvcHMiLCJmaWxlUGF0aCIsImNoaWxkcmVuIiwiUmVhY3ROb2RlIiwiRmlsZVBhdGhMaW5rIiwidDAiLCIkIiwiX2MiLCJ0MSIsInQyIiwidDMiLCJocmVmIl0sInNvdXJjZXMiOlsiRmlsZVBhdGhMaW5rLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBwYXRoVG9GaWxlVVJMIH0gZnJvbSAndXJsJ1xuaW1wb3J0IExpbmsgZnJvbSAnLi4vaW5rL2NvbXBvbmVudHMvTGluay5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgLyoqIFRoZSBhYnNvbHV0ZSBmaWxlIHBhdGggKi9cbiAgZmlsZVBhdGg6IHN0cmluZ1xuICAvKiogT3B0aW9uYWwgZGlzcGxheSB0ZXh0IChkZWZhdWx0cyB0byBmaWxlUGF0aCkgKi9cbiAgY2hpbGRyZW4/OiBSZWFjdC5SZWFjdE5vZGVcbn1cblxuLyoqXG4gKiBSZW5kZXJzIGEgZmlsZSBwYXRoIGFzIGFuIE9TQyA4IGh5cGVybGluay5cbiAqIFRoaXMgaGVscHMgdGVybWluYWxzIGxpa2UgaVRlcm0gY29ycmVjdGx5IGlkZW50aWZ5IGZpbGUgcGF0aHNcbiAqIGV2ZW4gd2hlbiB0aGV5IGFwcGVhciBpbnNpZGUgcGFyZW50aGVzZXMgb3Igb3RoZXIgdGV4dC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEZpbGVQYXRoTGluayh7IGZpbGVQYXRoLCBjaGlsZHJlbiB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiA8TGluayB1cmw9e3BhdGhUb0ZpbGVVUkwoZmlsZVBhdGgpLmhyZWZ9PntjaGlsZHJlbiA/PyBmaWxlUGF0aH08L0xpbms+XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyxhQUFhLFFBQVEsS0FBSztBQUNuQyxPQUFPQyxJQUFJLE1BQU0sMkJBQTJCO0FBRTVDLEtBQUtDLEtBQUssR0FBRztFQUNYO0VBQ0FDLFFBQVEsRUFBRSxNQUFNO0VBQ2hCO0VBQ0FDLFFBQVEsQ0FBQyxFQUFFTCxLQUFLLENBQUNNLFNBQVM7QUFDNUIsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTyxTQUFBQyxhQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXNCO0lBQUFOLFFBQUE7SUFBQUM7RUFBQSxJQUFBRyxFQUE2QjtFQUFBLElBQUFHLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFMLFFBQUE7SUFDdENPLEVBQUEsR0FBQVYsYUFBYSxDQUFDRyxRQUFRLENBQUM7SUFBQUssQ0FBQSxNQUFBTCxRQUFBO0lBQUFLLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQVEsTUFBQUcsRUFBQSxHQUFBUCxRQUFvQixJQUFwQkQsUUFBb0I7RUFBQSxJQUFBUyxFQUFBO0VBQUEsSUFBQUosQ0FBQSxRQUFBRSxFQUFBLENBQUFHLElBQUEsSUFBQUwsQ0FBQSxRQUFBRyxFQUFBO0lBQTlEQyxFQUFBLElBQUMsSUFBSSxDQUFNLEdBQTRCLENBQTVCLENBQUFGLEVBQXVCLENBQUFHLElBQUksQ0FBQyxDQUFHLENBQUFGLEVBQW1CLENBQUUsRUFBOUQsSUFBSSxDQUFpRTtJQUFBSCxDQUFBLE1BQUFFLEVBQUEsQ0FBQUcsSUFBQTtJQUFBTCxDQUFBLE1BQUFHLEVBQUE7SUFBQUgsQ0FBQSxNQUFBSSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSixDQUFBO0VBQUE7RUFBQSxPQUF0RUksRUFBc0U7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/FullscreenLayout.tsx b/components/FullscreenLayout.tsx new file mode 100644 index 0000000..2475ec6 --- /dev/null +++ b/components/FullscreenLayout.tsx @@ -0,0 +1,637 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import React, { createContext, type ReactNode, type RefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'; +import { fileURLToPath } from 'url'; +import { ModalContext } from '../context/modalContext.js'; +import { PromptOverlayProvider, usePromptOverlay, usePromptOverlayDialog } from '../context/promptOverlayContext.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import ScrollBox, { type ScrollBoxHandle } from '../ink/components/ScrollBox.js'; +import instances from '../ink/instances.js'; +import { Box, Text } from '../ink.js'; +import type { Message } from '../types/message.js'; +import { openBrowser, openPath } from '../utils/browser.js'; +import { isFullscreenEnvEnabled } from '../utils/fullscreen.js'; +import { plural } from '../utils/stringUtils.js'; +import { isNullRenderingAttachment } from './messages/nullRenderingAttachments.js'; +import PromptInputFooterSuggestions from './PromptInput/PromptInputFooterSuggestions.js'; +import type { StickyPrompt } from './VirtualMessageList.js'; + +/** Rows of transcript context kept visible above the modal pane's ▔ divider. */ +const MODAL_TRANSCRIPT_PEEK = 2; + +/** Context for scroll-derived chrome (sticky header, pill). StickyTracker + * in VirtualMessageList writes via this instead of threading a callback + * up through Messages → REPL → FullscreenLayout. The setter is stable so + * consuming this context never causes re-renders. */ +export const ScrollChromeContext = createContext<{ + setStickyPrompt: (p: StickyPrompt | null) => void; +}>({ + setStickyPrompt: () => {} +}); +type Props = { + /** Content that scrolls (messages, tool output) */ + scrollable: ReactNode; + /** Content pinned to the bottom (spinner, prompt, permissions) */ + bottom: ReactNode; + /** Content rendered inside the ScrollBox after messages — user can scroll + * up to see context while it's showing (used by PermissionRequest). */ + overlay?: ReactNode; + /** Absolute-positioned content anchored at the bottom-right of the + * ScrollBox area, floating over scrollback. Rendered inside the flexGrow + * region (not the bottom slot) so the overflowY:hidden cap doesn't clip + * it. Fullscreen only — used for the companion speech bubble. */ + bottomFloat?: ReactNode; + /** Slash-command dialog content. Rendered in an absolute-positioned + * bottom-anchored pane (▔ divider, paddingX=2) that paints over the + * ScrollBox AND bottom slot. Provides ModalContext so Pane/Dialog inside + * skip their own frame. Fullscreen only; inline after overlay otherwise. */ + modal?: ReactNode; + /** Ref passed via ModalContext so Tabs (or any scroll-owning descendant) + * can attach it to their own ScrollBox for tall content. */ + modalScrollRef?: React.RefObject; + /** Ref to the scroll box for keyboard scrolling. RefObject (not Ref) so + * pillVisible's useSyncExternalStore can subscribe to scroll changes. */ + scrollRef?: RefObject; + /** Y-position (scrollHeight at snapshot) of the unseen-divider. Pill + * shows while viewport bottom hasn't reached this. Ref so REPL doesn't + * re-render on the one-shot snapshot write. */ + dividerYRef?: RefObject; + /** Force-hide the pill (e.g. viewing a sub-agent task). */ + hidePill?: boolean; + /** Force-hide the sticky prompt header (e.g. viewing a teammate task). */ + hideSticky?: boolean; + /** Count for the pill text. 0 → "Jump to bottom", >0 → "N new messages". */ + newMessageCount?: number; + /** Called when the user clicks the "N new" pill. */ + onPillClick?: () => void; +}; + +/** + * Tracks the in-transcript "N new messages" divider position while the + * user is scrolled up. Snapshots message count AND scrollHeight the first + * time sticky breaks. scrollHeight ≈ the y-position of the divider in the + * scroll content (it renders right after the last message that existed at + * snapshot time). + * + * `pillVisible` lives in FullscreenLayout (not here) — it subscribes + * directly to ScrollBox via useSyncExternalStore with a boolean snapshot + * against `dividerYRef`, so per-frame scroll never re-renders REPL. + * `dividerIndex` stays here because REPL needs it for computeUnseenDivider + * → Messages' divider line; it changes only ~twice/scroll-session + * (first scroll-away + repin), acceptable REPL re-render cost. + * + * `onScrollAway` must be called by every scroll-away action with the + * handle; `onRepin` by submit/scroll-to-bottom. + */ +export function useUnseenDivider(messageCount: number): { + /** Index into messages[] where the divider line renders. Cleared on + * sticky-resume (scroll back to bottom) so the "N new" line doesn't + * linger once everything is visible. */ + dividerIndex: number | null; + /** scrollHeight snapshot at first scroll-away — the divider's y-position. + * FullscreenLayout subscribes to ScrollBox and compares viewport bottom + * against this for pillVisible. Ref so writes don't re-render REPL. */ + dividerYRef: RefObject; + onScrollAway: (handle: ScrollBoxHandle) => void; + onRepin: () => void; + /** Scroll the handle so the divider line is at the top of the viewport. */ + jumpToNew: (handle: ScrollBoxHandle | null) => void; + /** Shift dividerIndex and dividerYRef when messages are prepended + * (infinite scroll-back). indexDelta = number of messages prepended; + * heightDelta = content height growth in rows. */ + shiftDivider: (indexDelta: number, heightDelta: number) => void; +} { + const [dividerIndex, setDividerIndex] = useState(null); + // Ref holds the current count for onScrollAway to snapshot. Written in + // the render body (not useEffect) so wheel events arriving between a + // message-append render and its effect flush don't capture a stale + // count (off-by-one in the baseline). React Compiler bails out here — + // acceptable for a hook instantiated once in REPL. + const countRef = useRef(messageCount); + countRef.current = messageCount; + // scrollHeight snapshot — the divider's y in content coords. Ref-only: + // read synchronously in onScrollAway (setState is batched, can't + // read-then-write in the same callback) AND by FullscreenLayout's + // pillVisible subscription. null = pinned to bottom. + const dividerYRef = useRef(null); + const onRepin = useCallback(() => { + // Don't clear dividerYRef here — a trackpad momentum wheel event + // racing in the same stdin batch would see null and re-snapshot, + // overriding the setDividerIndex(null) below. The useEffect below + // clears the ref after React commits the null dividerIndex, so the + // ref stays non-null until the state settles. + setDividerIndex(null); + }, []); + const onScrollAway = useCallback((handle: ScrollBoxHandle) => { + // Nothing below the viewport → nothing to jump to. Covers both: + // • empty/short session: scrollUp calls scrollTo(0) which breaks sticky + // even at scrollTop=0 (wheel-up on fresh session showed the pill) + // • click-to-select at bottom: useDragToScroll.check() calls + // scrollTo(current) to break sticky so streaming content doesn't shift + // under the selection, then onScroll(false, …) — but scrollTop is still + // at max (Sarah Deaton, #claude-code-feedback 2026-03-15) + // pendingDelta: scrollBy accumulates without updating scrollTop. Without + // it, wheeling up from max would see scrollTop==max and suppress the pill. + const max = Math.max(0, handle.getScrollHeight() - handle.getViewportHeight()); + if (handle.getScrollTop() + handle.getPendingDelta() >= max) return; + // Snapshot only on the FIRST scroll-away. onScrollAway fires on EVERY + // scroll action (not just the initial break from sticky) — this guard + // preserves the original baseline so the count doesn't reset on the + // second PageUp. Subsequent calls are ref-only no-ops (no REPL re-render). + if (dividerYRef.current === null) { + dividerYRef.current = handle.getScrollHeight(); + // New scroll-away session → move the divider here (replaces old one) + setDividerIndex(countRef.current); + } + }, []); + const jumpToNew = useCallback((handle_0: ScrollBoxHandle | null) => { + if (!handle_0) return; + // scrollToBottom (not scrollTo(dividerY)): sets stickyScroll=true so + // useVirtualScroll mounts the tail and render-node-to-output pins + // scrollTop=maxScroll. scrollTo sets stickyScroll=false → the clamp + // (still at top-range bounds before React re-renders) pins scrollTop + // back, stopping short. The divider stays rendered (dividerIndex + // unchanged) so users see where new messages started; the clear on + // next submit/explicit scroll-to-bottom handles cleanup. + handle_0.scrollToBottom(); + }, []); + + // Sync dividerYRef with dividerIndex. When onRepin fires (submit, + // scroll-to-bottom), it sets dividerIndex=null but leaves the ref + // non-null — a wheel event racing in the same stdin batch would + // otherwise see null and re-snapshot. Deferring the ref clear to + // useEffect guarantees the ref stays non-null until React has committed + // the null dividerIndex, blocking the if-null guard in onScrollAway. + // + // Also handles /clear, rewind, teammate-view swap — if the count drops + // below the divider index, the divider would point at nothing. + useEffect(() => { + if (dividerIndex === null) { + dividerYRef.current = null; + } else if (messageCount < dividerIndex) { + dividerYRef.current = null; + setDividerIndex(null); + } + }, [messageCount, dividerIndex]); + const shiftDivider = useCallback((indexDelta: number, heightDelta: number) => { + setDividerIndex(idx => idx === null ? null : idx + indexDelta); + if (dividerYRef.current !== null) { + dividerYRef.current += heightDelta; + } + }, []); + return { + dividerIndex, + dividerYRef, + onScrollAway, + onRepin, + jumpToNew, + shiftDivider + }; +} + +/** + * Counts assistant turns in messages[dividerIndex..end). A "turn" is what + * users think of as "a new message from Claude" — not raw assistant entries + * (one turn yields multiple entries: tool_use blocks + text blocks). We count + * non-assistant→assistant transitions, but only for entries that actually + * carry text — tool-use-only entries are skipped (like progress messages) + * so "⏺ Searched for 13 patterns, read 6 files" doesn't tick the pill. + */ +export function countUnseenAssistantTurns(messages: readonly Message[], dividerIndex: number): number { + let count = 0; + let prevWasAssistant = false; + for (let i = dividerIndex; i < messages.length; i++) { + const m = messages[i]!; + if (m.type === 'progress') continue; + // Tool-use-only assistant entries aren't "new messages" to the user — + // skip them the same way we skip progress. prevWasAssistant is NOT + // updated, so a text block immediately following still counts as the + // same turn (tool_use + text from one API response = 1). + if (m.type === 'assistant' && !assistantHasVisibleText(m)) continue; + const isAssistant = m.type === 'assistant'; + if (isAssistant && !prevWasAssistant) count++; + prevWasAssistant = isAssistant; + } + return count; +} +function assistantHasVisibleText(m: Message): boolean { + if (m.type !== 'assistant') return false; + for (const b of m.message.content) { + if (b.type === 'text' && b.text.trim() !== '') return true; + } + return false; +} +export type UnseenDivider = { + firstUnseenUuid: Message['uuid']; + count: number; +}; + +/** + * Builds the unseenDivider object REPL passes to Messages + the pill. + * Returns undefined only when no content has arrived past the divider + * yet (messages[dividerIndex] doesn't exist). Once ANY message arrives + * — including tool_use-only assistant entries and tool_result user entries + * that countUnseenAssistantTurns skips — count floors at 1 so the pill + * flips from "Jump to bottom" to "1 new message". Without the floor, + * the pill stays "Jump to bottom" through an entire tool-call sequence + * until Claude's text response lands. + */ +export function computeUnseenDivider(messages: readonly Message[], dividerIndex: number | null): UnseenDivider | undefined { + if (dividerIndex === null) return undefined; + // Skip progress and null-rendering attachments when picking the divider + // anchor — Messages.tsx filters these out of renderableMessages before the + // dividerBeforeIndex search, so their UUID wouldn't be found (CC-724). + // Hook attachments use randomUUID() so nothing shares their 24-char prefix. + let anchorIdx = dividerIndex; + while (anchorIdx < messages.length && (messages[anchorIdx]?.type === 'progress' || isNullRenderingAttachment(messages[anchorIdx]!))) { + anchorIdx++; + } + const uuid = messages[anchorIdx]?.uuid; + if (!uuid) return undefined; + const count = countUnseenAssistantTurns(messages, dividerIndex); + return { + firstUnseenUuid: uuid, + count: Math.max(1, count) + }; +} + +/** + * Layout wrapper for the REPL. In fullscreen mode, puts scrollable + * content in a sticky-scroll box and pins bottom content via flexbox. + * Outside fullscreen mode, renders content sequentially so the existing + * main-screen scrollback rendering works unchanged. + * + * Fullscreen mode defaults on for ants (CLAUDE_CODE_NO_FLICKER=0 to opt out) + * and off for external users (CLAUDE_CODE_NO_FLICKER=1 to opt in). + * The wrapper + * (alt buffer + mouse tracking + height constraint) lives at REPL's root + * so nothing can accidentally render outside it. + */ +export function FullscreenLayout(t0) { + const $ = _c(47); + const { + scrollable, + bottom, + overlay, + bottomFloat, + modal, + modalScrollRef, + scrollRef, + dividerYRef, + hidePill: t1, + hideSticky: t2, + newMessageCount: t3, + onPillClick + } = t0; + const hidePill = t1 === undefined ? false : t1; + const hideSticky = t2 === undefined ? false : t2; + const newMessageCount = t3 === undefined ? 0 : t3; + const { + rows: terminalRows, + columns + } = useTerminalSize(); + const [stickyPrompt, setStickyPrompt] = useState(null); + let t4; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t4 = { + setStickyPrompt + }; + $[0] = t4; + } else { + t4 = $[0]; + } + const chromeCtx = t4; + let t5; + if ($[1] !== scrollRef) { + t5 = listener => scrollRef?.current?.subscribe(listener) ?? _temp; + $[1] = scrollRef; + $[2] = t5; + } else { + t5 = $[2]; + } + const subscribe = t5; + let t6; + if ($[3] !== dividerYRef || $[4] !== scrollRef) { + t6 = () => { + const s = scrollRef?.current; + const dividerY = dividerYRef?.current; + if (!s || dividerY == null) { + return false; + } + return s.getScrollTop() + s.getPendingDelta() + s.getViewportHeight() < dividerY; + }; + $[3] = dividerYRef; + $[4] = scrollRef; + $[5] = t6; + } else { + t6 = $[5]; + } + const pillVisible = useSyncExternalStore(subscribe, t6); + let t7; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t7 = []; + $[6] = t7; + } else { + t7 = $[6]; + } + useLayoutEffect(_temp3, t7); + if (isFullscreenEnvEnabled()) { + const sticky = hideSticky ? null : stickyPrompt; + const headerPrompt = sticky != null && sticky !== "clicked" && overlay == null ? sticky : null; + const padCollapsed = sticky != null && overlay == null; + let t8; + if ($[7] !== headerPrompt) { + t8 = headerPrompt && ; + $[7] = headerPrompt; + $[8] = t8; + } else { + t8 = $[8]; + } + const t9 = padCollapsed ? 0 : 1; + let t10; + if ($[9] !== scrollable) { + t10 = {scrollable}; + $[9] = scrollable; + $[10] = t10; + } else { + t10 = $[10]; + } + let t11; + if ($[11] !== overlay || $[12] !== scrollRef || $[13] !== t10 || $[14] !== t9) { + t11 = {t10}{overlay}; + $[11] = overlay; + $[12] = scrollRef; + $[13] = t10; + $[14] = t9; + $[15] = t11; + } else { + t11 = $[15]; + } + let t12; + if ($[16] !== hidePill || $[17] !== newMessageCount || $[18] !== onPillClick || $[19] !== overlay || $[20] !== pillVisible) { + t12 = !hidePill && pillVisible && overlay == null && ; + $[16] = hidePill; + $[17] = newMessageCount; + $[18] = onPillClick; + $[19] = overlay; + $[20] = pillVisible; + $[21] = t12; + } else { + t12 = $[21]; + } + let t13; + if ($[22] !== bottomFloat) { + t13 = bottomFloat != null && {bottomFloat}; + $[22] = bottomFloat; + $[23] = t13; + } else { + t13 = $[23]; + } + let t14; + if ($[24] !== t11 || $[25] !== t12 || $[26] !== t13 || $[27] !== t8) { + t14 = {t8}{t11}{t12}{t13}; + $[24] = t11; + $[25] = t12; + $[26] = t13; + $[27] = t8; + $[28] = t14; + } else { + t14 = $[28]; + } + let t15; + let t16; + if ($[29] === Symbol.for("react.memo_cache_sentinel")) { + t15 = ; + t16 = ; + $[29] = t15; + $[30] = t16; + } else { + t15 = $[29]; + t16 = $[30]; + } + let t17; + if ($[31] !== bottom) { + t17 = {t15}{t16}{bottom}; + $[31] = bottom; + $[32] = t17; + } else { + t17 = $[32]; + } + let t18; + if ($[33] !== columns || $[34] !== modal || $[35] !== modalScrollRef || $[36] !== terminalRows) { + t18 = modal != null && {"\u2594".repeat(columns)}{modal}; + $[33] = columns; + $[34] = modal; + $[35] = modalScrollRef; + $[36] = terminalRows; + $[37] = t18; + } else { + t18 = $[37]; + } + let t19; + if ($[38] !== t14 || $[39] !== t17 || $[40] !== t18) { + t19 = {t14}{t17}{t18}; + $[38] = t14; + $[39] = t17; + $[40] = t18; + $[41] = t19; + } else { + t19 = $[41]; + } + return t19; + } + let t8; + if ($[42] !== bottom || $[43] !== modal || $[44] !== overlay || $[45] !== scrollable) { + t8 = <>{scrollable}{bottom}{overlay}{modal}; + $[42] = bottom; + $[43] = modal; + $[44] = overlay; + $[45] = scrollable; + $[46] = t8; + } else { + t8 = $[46]; + } + return t8; +} + +// Slack-style pill. Absolute overlay at bottom={0} of the scrollwrap — floats +// over the ScrollBox's last content row, only obscuring the centered pill +// text (the rest of the row shows ScrollBox content). Scroll-smear from +// DECSTBM shifting the pill's pixels is repaired at the Ink layer +// (absoluteRectsPrev third-pass in render-node-to-output.ts, #23939). Shows +// "Jump to bottom" when count is 0 (scrolled away but no new messages yet — +// the dead zone where users previously thought chat stalled). +function _temp3() { + if (!isFullscreenEnvEnabled()) { + return; + } + const ink = instances.get(process.stdout); + if (!ink) { + return; + } + ink.onHyperlinkClick = _temp2; + return () => { + ink.onHyperlinkClick = undefined; + }; +} +function _temp2(url) { + if (url.startsWith("file:")) { + try { + openPath(fileURLToPath(url)); + } catch {} + } else { + openBrowser(url); + } +} +function _temp() {} +function NewMessagesPill(t0) { + const $ = _c(10); + const { + count, + onClick + } = t0; + const [hover, setHover] = useState(false); + let t1; + let t2; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => setHover(true); + t2 = () => setHover(false); + $[0] = t1; + $[1] = t2; + } else { + t1 = $[0]; + t2 = $[1]; + } + const t3 = hover ? "userMessageBackgroundHover" : "userMessageBackground"; + let t4; + if ($[2] !== count) { + t4 = count > 0 ? `${count} new ${plural(count, "message")}` : "Jump to bottom"; + $[2] = count; + $[3] = t4; + } else { + t4 = $[3]; + } + let t5; + if ($[4] !== t3 || $[5] !== t4) { + t5 = {" "}{t4}{" "}{figures.arrowDown}{" "}; + $[4] = t3; + $[5] = t4; + $[6] = t5; + } else { + t5 = $[6]; + } + let t6; + if ($[7] !== onClick || $[8] !== t5) { + t6 = {t5}; + $[7] = onClick; + $[8] = t5; + $[9] = t6; + } else { + t6 = $[9]; + } + return t6; +} + +// Context breadcrumb: when scrolled up into history, pin the current +// conversation turn's prompt above the viewport so you know what Claude was +// responding to. Normal-flow sibling BEFORE the ScrollBox (mirrors the pill +// below it) — shrinks the ScrollBox by exactly 1 row via flex, stays outside +// the DECSTBM scroll region. Click jumps back to the prompt. +// +// Height is FIXED at 1 row (truncate-end for long prompts). A variable-height +// header (1 when short, 2 when wrapped) shifts the ScrollBox by 1 row every +// time the sticky prompt switches during scroll — content jumps on screen +// even with scrollTop unchanged (the DECSTBM region top shifts with the +// ScrollBox, and the diff engine sees "everything moved"). Fixed height +// keeps the ScrollBox anchored; only the header TEXT changes, not its box. +function StickyPromptHeader(t0) { + const $ = _c(8); + const { + text, + onClick + } = t0; + const [hover, setHover] = useState(false); + const t1 = hover ? "userMessageBackgroundHover" : "userMessageBackground"; + let t2; + let t3; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => setHover(true); + t3 = () => setHover(false); + $[0] = t2; + $[1] = t3; + } else { + t2 = $[0]; + t3 = $[1]; + } + let t4; + if ($[2] !== text) { + t4 = {figures.pointer} {text}; + $[2] = text; + $[3] = t4; + } else { + t4 = $[3]; + } + let t5; + if ($[4] !== onClick || $[5] !== t1 || $[6] !== t4) { + t5 = {t4}; + $[4] = onClick; + $[5] = t1; + $[6] = t4; + $[7] = t5; + } else { + t5 = $[7]; + } + return t5; +} + +// Slash-command suggestion overlay — see promptOverlayContext.tsx for why +// it's portaled. Scroll-smear from floating over the DECSTBM region is +// repaired at the Ink layer (absoluteRectsPrev in render-node-to-output.ts). +// The renderer clamps negative y to 0 for absolute elements (see +// render-node-to-output.ts), so the top rows (best matches) stay visible +// even when the overlay extends above the viewport. We omit minHeight and +// flex-end here: they would create empty padding rows that shift visible +// items down into the prompt area when the list has fewer items than max. +function SuggestionsOverlay() { + const $ = _c(4); + const data = usePromptOverlay(); + if (!data || data.suggestions.length === 0) { + return null; + } + let t0; + if ($[0] !== data.maxColumnWidth || $[1] !== data.selectedSuggestion || $[2] !== data.suggestions) { + t0 = ; + $[0] = data.maxColumnWidth; + $[1] = data.selectedSuggestion; + $[2] = data.suggestions; + $[3] = t0; + } else { + t0 = $[3]; + } + return t0; +} + +// Dialog portaled from PromptInput (AutoModeOptInDialog) — same clip-escape +// pattern as SuggestionsOverlay. Renders later in tree order so it paints +// over suggestions if both are ever up (they shouldn't be). +function DialogOverlay() { + const $ = _c(2); + const node = usePromptOverlayDialog(); + if (!node) { + return null; + } + let t0; + if ($[0] !== node) { + t0 = {node}; + $[0] = node; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJjcmVhdGVDb250ZXh0IiwiUmVhY3ROb2RlIiwiUmVmT2JqZWN0IiwidXNlQ2FsbGJhY2siLCJ1c2VFZmZlY3QiLCJ1c2VMYXlvdXRFZmZlY3QiLCJ1c2VNZW1vIiwidXNlUmVmIiwidXNlU3RhdGUiLCJ1c2VTeW5jRXh0ZXJuYWxTdG9yZSIsImZpbGVVUkxUb1BhdGgiLCJNb2RhbENvbnRleHQiLCJQcm9tcHRPdmVybGF5UHJvdmlkZXIiLCJ1c2VQcm9tcHRPdmVybGF5IiwidXNlUHJvbXB0T3ZlcmxheURpYWxvZyIsInVzZVRlcm1pbmFsU2l6ZSIsIlNjcm9sbEJveCIsIlNjcm9sbEJveEhhbmRsZSIsImluc3RhbmNlcyIsIkJveCIsIlRleHQiLCJNZXNzYWdlIiwib3BlbkJyb3dzZXIiLCJvcGVuUGF0aCIsImlzRnVsbHNjcmVlbkVudkVuYWJsZWQiLCJwbHVyYWwiLCJpc051bGxSZW5kZXJpbmdBdHRhY2htZW50IiwiUHJvbXB0SW5wdXRGb290ZXJTdWdnZXN0aW9ucyIsIlN0aWNreVByb21wdCIsIk1PREFMX1RSQU5TQ1JJUFRfUEVFSyIsIlNjcm9sbENocm9tZUNvbnRleHQiLCJzZXRTdGlja3lQcm9tcHQiLCJwIiwiUHJvcHMiLCJzY3JvbGxhYmxlIiwiYm90dG9tIiwib3ZlcmxheSIsImJvdHRvbUZsb2F0IiwibW9kYWwiLCJtb2RhbFNjcm9sbFJlZiIsInNjcm9sbFJlZiIsImRpdmlkZXJZUmVmIiwiaGlkZVBpbGwiLCJoaWRlU3RpY2t5IiwibmV3TWVzc2FnZUNvdW50Iiwib25QaWxsQ2xpY2siLCJ1c2VVbnNlZW5EaXZpZGVyIiwibWVzc2FnZUNvdW50IiwiZGl2aWRlckluZGV4Iiwib25TY3JvbGxBd2F5IiwiaGFuZGxlIiwib25SZXBpbiIsImp1bXBUb05ldyIsInNoaWZ0RGl2aWRlciIsImluZGV4RGVsdGEiLCJoZWlnaHREZWx0YSIsInNldERpdmlkZXJJbmRleCIsImNvdW50UmVmIiwiY3VycmVudCIsIm1heCIsIk1hdGgiLCJnZXRTY3JvbGxIZWlnaHQiLCJnZXRWaWV3cG9ydEhlaWdodCIsImdldFNjcm9sbFRvcCIsImdldFBlbmRpbmdEZWx0YSIsInNjcm9sbFRvQm90dG9tIiwiaWR4IiwiY291bnRVbnNlZW5Bc3Npc3RhbnRUdXJucyIsIm1lc3NhZ2VzIiwiY291bnQiLCJwcmV2V2FzQXNzaXN0YW50IiwiaSIsImxlbmd0aCIsIm0iLCJ0eXBlIiwiYXNzaXN0YW50SGFzVmlzaWJsZVRleHQiLCJpc0Fzc2lzdGFudCIsImIiLCJtZXNzYWdlIiwiY29udGVudCIsInRleHQiLCJ0cmltIiwiVW5zZWVuRGl2aWRlciIsImZpcnN0VW5zZWVuVXVpZCIsImNvbXB1dGVVbnNlZW5EaXZpZGVyIiwidW5kZWZpbmVkIiwiYW5jaG9ySWR4IiwidXVpZCIsIkZ1bGxzY3JlZW5MYXlvdXQiLCJ0MCIsIiQiLCJfYyIsInQxIiwidDIiLCJ0MyIsInJvd3MiLCJ0ZXJtaW5hbFJvd3MiLCJjb2x1bW5zIiwic3RpY2t5UHJvbXB0IiwidDQiLCJTeW1ib2wiLCJmb3IiLCJjaHJvbWVDdHgiLCJ0NSIsImxpc3RlbmVyIiwic3Vic2NyaWJlIiwiX3RlbXAiLCJ0NiIsInMiLCJkaXZpZGVyWSIsInBpbGxWaXNpYmxlIiwidDciLCJfdGVtcDMiLCJzdGlja3kiLCJoZWFkZXJQcm9tcHQiLCJwYWRDb2xsYXBzZWQiLCJ0OCIsInNjcm9sbFRvIiwidDkiLCJ0MTAiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJ0MTYiLCJ0MTciLCJ0MTgiLCJyZXBlYXQiLCJ0MTkiLCJpbmsiLCJnZXQiLCJwcm9jZXNzIiwic3Rkb3V0Iiwib25IeXBlcmxpbmtDbGljayIsIl90ZW1wMiIsInVybCIsInN0YXJ0c1dpdGgiLCJOZXdNZXNzYWdlc1BpbGwiLCJvbkNsaWNrIiwiaG92ZXIiLCJzZXRIb3ZlciIsImFycm93RG93biIsIlN0aWNreVByb21wdEhlYWRlciIsInBvaW50ZXIiLCJTdWdnZXN0aW9uc092ZXJsYXkiLCJkYXRhIiwic3VnZ2VzdGlvbnMiLCJtYXhDb2x1bW5XaWR0aCIsInNlbGVjdGVkU3VnZ2VzdGlvbiIsIkRpYWxvZ092ZXJsYXkiLCJub2RlIl0sInNvdXJjZXMiOlsiRnVsbHNjcmVlbkxheW91dC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCBSZWFjdCwge1xuICBjcmVhdGVDb250ZXh0LFxuICB0eXBlIFJlYWN0Tm9kZSxcbiAgdHlwZSBSZWZPYmplY3QsXG4gIHVzZUNhbGxiYWNrLFxuICB1c2VFZmZlY3QsXG4gIHVzZUxheW91dEVmZmVjdCxcbiAgdXNlTWVtbyxcbiAgdXNlUmVmLFxuICB1c2VTdGF0ZSxcbiAgdXNlU3luY0V4dGVybmFsU3RvcmUsXG59IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gJ3VybCdcbmltcG9ydCB7IE1vZGFsQ29udGV4dCB9IGZyb20gJy4uL2NvbnRleHQvbW9kYWxDb250ZXh0LmpzJ1xuaW1wb3J0IHtcbiAgUHJvbXB0T3ZlcmxheVByb3ZpZGVyLFxuICB1c2VQcm9tcHRPdmVybGF5LFxuICB1c2VQcm9tcHRPdmVybGF5RGlhbG9nLFxufSBmcm9tICcuLi9jb250ZXh0L3Byb21wdE92ZXJsYXlDb250ZXh0LmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IFNjcm9sbEJveCwgeyB0eXBlIFNjcm9sbEJveEhhbmRsZSB9IGZyb20gJy4uL2luay9jb21wb25lbnRzL1Njcm9sbEJveC5qcydcbmltcG9ydCBpbnN0YW5jZXMgZnJvbSAnLi4vaW5rL2luc3RhbmNlcy5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgTWVzc2FnZSB9IGZyb20gJy4uL3R5cGVzL21lc3NhZ2UuanMnXG5pbXBvcnQgeyBvcGVuQnJvd3Nlciwgb3BlblBhdGggfSBmcm9tICcuLi91dGlscy9icm93c2VyLmpzJ1xuaW1wb3J0IHsgaXNGdWxsc2NyZWVuRW52RW5hYmxlZCB9IGZyb20gJy4uL3V0aWxzL2Z1bGxzY3JlZW4uanMnXG5pbXBvcnQgeyBwbHVyYWwgfSBmcm9tICcuLi91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB7IGlzTnVsbFJlbmRlcmluZ0F0dGFjaG1lbnQgfSBmcm9tICcuL21lc3NhZ2VzL251bGxSZW5kZXJpbmdBdHRhY2htZW50cy5qcydcbmltcG9ydCBQcm9tcHRJbnB1dEZvb3RlclN1Z2dlc3Rpb25zIGZyb20gJy4vUHJvbXB0SW5wdXQvUHJvbXB0SW5wdXRGb290ZXJTdWdnZXN0aW9ucy5qcydcbmltcG9ydCB0eXBlIHsgU3RpY2t5UHJvbXB0IH0gZnJvbSAnLi9WaXJ0dWFsTWVzc2FnZUxpc3QuanMnXG5cbi8qKiBSb3dzIG9mIHRyYW5zY3JpcHQgY29udGV4dCBrZXB0IHZpc2libGUgYWJvdmUgdGhlIG1vZGFsIHBhbmUncyDilpQgZGl2aWRlci4gKi9cbmNvbnN0IE1PREFMX1RSQU5TQ1JJUFRfUEVFSyA9IDJcblxuLyoqIENvbnRleHQgZm9yIHNjcm9sbC1kZXJpdmVkIGNocm9tZSAoc3RpY2t5IGhlYWRlciwgcGlsbCkuIFN0aWNreVRyYWNrZXJcbiAqICBpbiBWaXJ0dWFsTWVzc2FnZUxpc3Qgd3JpdGVzIHZpYSB0aGlzIGluc3RlYWQgb2YgdGhyZWFkaW5nIGEgY2FsbGJhY2tcbiAqICB1cCB0aHJvdWdoIE1lc3NhZ2VzIOKGkiBSRVBMIOKGkiBGdWxsc2NyZWVuTGF5b3V0LiBUaGUgc2V0dGVyIGlzIHN0YWJsZSBzb1xuICogIGNvbnN1bWluZyB0aGlzIGNvbnRleHQgbmV2ZXIgY2F1c2VzIHJlLXJlbmRlcnMuICovXG5leHBvcnQgY29uc3QgU2Nyb2xsQ2hyb21lQ29udGV4dCA9IGNyZWF0ZUNvbnRleHQ8e1xuICBzZXRTdGlja3lQcm9tcHQ6IChwOiBTdGlja3lQcm9tcHQgfCBudWxsKSA9PiB2b2lkXG59Pih7IHNldFN0aWNreVByb21wdDogKCkgPT4ge30gfSlcblxudHlwZSBQcm9wcyA9IHtcbiAgLyoqIENvbnRlbnQgdGhhdCBzY3JvbGxzIChtZXNzYWdlcywgdG9vbCBvdXRwdXQpICovXG4gIHNjcm9sbGFibGU6IFJlYWN0Tm9kZVxuICAvKiogQ29udGVudCBwaW5uZWQgdG8gdGhlIGJvdHRvbSAoc3Bpbm5lciwgcHJvbXB0LCBwZXJtaXNzaW9ucykgKi9cbiAgYm90dG9tOiBSZWFjdE5vZGVcbiAgLyoqIENvbnRlbnQgcmVuZGVyZWQgaW5zaWRlIHRoZSBTY3JvbGxCb3ggYWZ0ZXIgbWVzc2FnZXMg4oCUIHVzZXIgY2FuIHNjcm9sbFxuICAgKiAgdXAgdG8gc2VlIGNvbnRleHQgd2hpbGUgaXQncyBzaG93aW5nICh1c2VkIGJ5IFBlcm1pc3Npb25SZXF1ZXN0KS4gKi9cbiAgb3ZlcmxheT86IFJlYWN0Tm9kZVxuICAvKiogQWJzb2x1dGUtcG9zaXRpb25lZCBjb250ZW50IGFuY2hvcmVkIGF0IHRoZSBib3R0b20tcmlnaHQgb2YgdGhlXG4gICAqICBTY3JvbGxCb3ggYXJlYSwgZmxvYXRpbmcgb3ZlciBzY3JvbGxiYWNrLiBSZW5kZXJlZCBpbnNpZGUgdGhlIGZsZXhHcm93XG4gICAqICByZWdpb24gKG5vdCB0aGUgYm90dG9tIHNsb3QpIHNvIHRoZSBvdmVyZmxvd1k6aGlkZGVuIGNhcCBkb2Vzbid0IGNsaXBcbiAgICogIGl0LiBGdWxsc2NyZWVuIG9ubHkg4oCUIHVzZWQgZm9yIHRoZSBjb21wYW5pb24gc3BlZWNoIGJ1YmJsZS4gKi9cbiAgYm90dG9tRmxvYXQ/OiBSZWFjdE5vZGVcbiAgLyoqIFNsYXNoLWNvbW1hbmQgZGlhbG9nIGNvbnRlbnQuIFJlbmRlcmVkIGluIGFuIGFic29sdXRlLXBvc2l0aW9uZWRcbiAgICogIGJvdHRvbS1hbmNob3JlZCBwYW5lICjilpQgZGl2aWRlciwgcGFkZGluZ1g9MikgdGhhdCBwYWludHMgb3ZlciB0aGVcbiAgICogIFNjcm9sbEJveCBBTkQgYm90dG9tIHNsb3QuIFByb3ZpZGVzIE1vZGFsQ29udGV4dCBzbyBQYW5lL0RpYWxvZyBpbnNpZGVcbiAgICogIHNraXAgdGhlaXIgb3duIGZyYW1lLiBGdWxsc2NyZWVuIG9ubHk7IGlubGluZSBhZnRlciBvdmVybGF5IG90aGVyd2lzZS4gKi9cbiAgbW9kYWw/OiBSZWFjdE5vZGVcbiAgLyoqIFJlZiBwYXNzZWQgdmlhIE1vZGFsQ29udGV4dCBzbyBUYWJzIChvciBhbnkgc2Nyb2xsLW93bmluZyBkZXNjZW5kYW50KVxuICAgKiAgY2FuIGF0dGFjaCBpdCB0byB0aGVpciBvd24gU2Nyb2xsQm94IGZvciB0YWxsIGNvbnRlbnQuICovXG4gIG1vZGFsU2Nyb2xsUmVmPzogUmVhY3QuUmVmT2JqZWN0PFNjcm9sbEJveEhhbmRsZSB8IG51bGw+XG4gIC8qKiBSZWYgdG8gdGhlIHNjcm9sbCBib3ggZm9yIGtleWJvYXJkIHNjcm9sbGluZy4gUmVmT2JqZWN0IChub3QgUmVmKSBzb1xuICAgKiAgcGlsbFZpc2libGUncyB1c2VTeW5jRXh0ZXJuYWxTdG9yZSBjYW4gc3Vic2NyaWJlIHRvIHNjcm9sbCBjaGFuZ2VzLiAqL1xuICBzY3JvbGxSZWY/OiBSZWZPYmplY3Q8U2Nyb2xsQm94SGFuZGxlIHwgbnVsbD5cbiAgLyoqIFktcG9zaXRpb24gKHNjcm9sbEhlaWdodCBhdCBzbmFwc2hvdCkgb2YgdGhlIHVuc2Vlbi1kaXZpZGVyLiBQaWxsXG4gICAqICBzaG93cyB3aGlsZSB2aWV3cG9ydCBib3R0b20gaGFzbid0IHJlYWNoZWQgdGhpcy4gUmVmIHNvIFJFUEwgZG9lc24ndFxuICAgKiAgcmUtcmVuZGVyIG9uIHRoZSBvbmUtc2hvdCBzbmFwc2hvdCB3cml0ZS4gKi9cbiAgZGl2aWRlcllSZWY/OiBSZWZPYmplY3Q8bnVtYmVyIHwgbnVsbD5cbiAgLyoqIEZvcmNlLWhpZGUgdGhlIHBpbGwgKGUuZy4gdmlld2luZyBhIHN1Yi1hZ2VudCB0YXNrKS4gKi9cbiAgaGlkZVBpbGw/OiBib29sZWFuXG4gIC8qKiBGb3JjZS1oaWRlIHRoZSBzdGlja3kgcHJvbXB0IGhlYWRlciAoZS5nLiB2aWV3aW5nIGEgdGVhbW1hdGUgdGFzaykuICovXG4gIGhpZGVTdGlja3k/OiBib29sZWFuXG4gIC8qKiBDb3VudCBmb3IgdGhlIHBpbGwgdGV4dC4gMCDihpIgXCJKdW1wIHRvIGJvdHRvbVwiLCA+MCDihpIgXCJOIG5ldyBtZXNzYWdlc1wiLiAqL1xuICBuZXdNZXNzYWdlQ291bnQ/OiBudW1iZXJcbiAgLyoqIENhbGxlZCB3aGVuIHRoZSB1c2VyIGNsaWNrcyB0aGUgXCJOIG5ld1wiIHBpbGwuICovXG4gIG9uUGlsbENsaWNrPzogKCkgPT4gdm9pZFxufVxuXG4vKipcbiAqIFRyYWNrcyB0aGUgaW4tdHJhbnNjcmlwdCBcIk4gbmV3IG1lc3NhZ2VzXCIgZGl2aWRlciBwb3NpdGlvbiB3aGlsZSB0aGVcbiAqIHVzZXIgaXMgc2Nyb2xsZWQgdXAuIFNuYXBzaG90cyBtZXNzYWdlIGNvdW50IEFORCBzY3JvbGxIZWlnaHQgdGhlIGZpcnN0XG4gKiB0aW1lIHN0aWNreSBicmVha3MuIHNjcm9sbEhlaWdodCDiiYggdGhlIHktcG9zaXRpb24gb2YgdGhlIGRpdmlkZXIgaW4gdGhlXG4gKiBzY3JvbGwgY29udGVudCAoaXQgcmVuZGVycyByaWdodCBhZnRlciB0aGUgbGFzdCBtZXNzYWdlIHRoYXQgZXhpc3RlZCBhdFxuICogc25hcHNob3QgdGltZSkuXG4gKlxuICogYHBpbGxWaXNpYmxlYCBsaXZlcyBpbiBGdWxsc2NyZWVuTGF5b3V0IChub3QgaGVyZSkg4oCUIGl0IHN1YnNjcmliZXNcbiAqIGRpcmVjdGx5IHRvIFNjcm9sbEJveCB2aWEgdXNlU3luY0V4dGVybmFsU3RvcmUgd2l0aCBhIGJvb2xlYW4gc25hcHNob3RcbiAqIGFnYWluc3QgYGRpdmlkZXJZUmVmYCwgc28gcGVyLWZyYW1lIHNjcm9sbCBuZXZlciByZS1yZW5kZXJzIFJFUEwuXG4gKiBgZGl2aWRlckluZGV4YCBzdGF5cyBoZXJlIGJlY2F1c2UgUkVQTCBuZWVkcyBpdCBmb3IgY29tcHV0ZVVuc2VlbkRpdmlkZXJcbiAqIOKGkiBNZXNzYWdlcycgZGl2aWRlciBsaW5lOyBpdCBjaGFuZ2VzIG9ubHkgfnR3aWNlL3Njcm9sbC1zZXNzaW9uXG4gKiAoZmlyc3Qgc2Nyb2xsLWF3YXkgKyByZXBpbiksIGFjY2VwdGFibGUgUkVQTCByZS1yZW5kZXIgY29zdC5cbiAqXG4gKiBgb25TY3JvbGxBd2F5YCBtdXN0IGJlIGNhbGxlZCBieSBldmVyeSBzY3JvbGwtYXdheSBhY3Rpb24gd2l0aCB0aGVcbiAqIGhhbmRsZTsgYG9uUmVwaW5gIGJ5IHN1Ym1pdC9zY3JvbGwtdG8tYm90dG9tLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlVW5zZWVuRGl2aWRlcihtZXNzYWdlQ291bnQ6IG51bWJlcik6IHtcbiAgLyoqIEluZGV4IGludG8gbWVzc2FnZXNbXSB3aGVyZSB0aGUgZGl2aWRlciBsaW5lIHJlbmRlcnMuIENsZWFyZWQgb25cbiAgICogIHN0aWNreS1yZXN1bWUgKHNjcm9sbCBiYWNrIHRvIGJvdHRvbSkgc28gdGhlIFwiTiBuZXdcIiBsaW5lIGRvZXNuJ3RcbiAgICogIGxpbmdlciBvbmNlIGV2ZXJ5dGhpbmcgaXMgdmlzaWJsZS4gKi9cbiAgZGl2aWRlckluZGV4OiBudW1iZXIgfCBudWxsXG4gIC8qKiBzY3JvbGxIZWlnaHQgc25hcHNob3QgYXQgZmlyc3Qgc2Nyb2xsLWF3YXkg4oCUIHRoZSBkaXZpZGVyJ3MgeS1wb3NpdGlvbi5cbiAgICogIEZ1bGxzY3JlZW5MYXlvdXQgc3Vic2NyaWJlcyB0byBTY3JvbGxCb3ggYW5kIGNvbXBhcmVzIHZpZXdwb3J0IGJvdHRvbVxuICAgKiAgYWdhaW5zdCB0aGlzIGZvciBwaWxsVmlzaWJsZS4gUmVmIHNvIHdyaXRlcyBkb24ndCByZS1yZW5kZXIgUkVQTC4gKi9cbiAgZGl2aWRlcllSZWY6IFJlZk9iamVjdDxudW1iZXIgfCBudWxsPlxuICBvblNjcm9sbEF3YXk6IChoYW5kbGU6IFNjcm9sbEJveEhhbmRsZSkgPT4gdm9pZFxuICBvblJlcGluOiAoKSA9PiB2b2lkXG4gIC8qKiBTY3JvbGwgdGhlIGhhbmRsZSBzbyB0aGUgZGl2aWRlciBsaW5lIGlzIGF0IHRoZSB0b3Agb2YgdGhlIHZpZXdwb3J0LiAqL1xuICBqdW1wVG9OZXc6IChoYW5kbGU6IFNjcm9sbEJveEhhbmRsZSB8IG51bGwpID0+IHZvaWRcbiAgLyoqIFNoaWZ0IGRpdmlkZXJJbmRleCBhbmQgZGl2aWRlcllSZWYgd2hlbiBtZXNzYWdlcyBhcmUgcHJlcGVuZGVkXG4gICAqICAoaW5maW5pdGUgc2Nyb2xsLWJhY2spLiBpbmRleERlbHRhID0gbnVtYmVyIG9mIG1lc3NhZ2VzIHByZXBlbmRlZDtcbiAgICogIGhlaWdodERlbHRhID0gY29udGVudCBoZWlnaHQgZ3Jvd3RoIGluIHJvd3MuICovXG4gIHNoaWZ0RGl2aWRlcjogKGluZGV4RGVsdGE6IG51bWJlciwgaGVpZ2h0RGVsdGE6IG51bWJlcikgPT4gdm9pZFxufSB7XG4gIGNvbnN0IFtkaXZpZGVySW5kZXgsIHNldERpdmlkZXJJbmRleF0gPSB1c2VTdGF0ZTxudW1iZXIgfCBudWxsPihudWxsKVxuICAvLyBSZWYgaG9sZHMgdGhlIGN1cnJlbnQgY291bnQgZm9yIG9uU2Nyb2xsQXdheSB0byBzbmFwc2hvdC4gV3JpdHRlbiBpblxuICAvLyB0aGUgcmVuZGVyIGJvZHkgKG5vdCB1c2VFZmZlY3QpIHNvIHdoZWVsIGV2ZW50cyBhcnJpdmluZyBiZXR3ZWVuIGFcbiAgLy8gbWVzc2FnZS1hcHBlbmQgcmVuZGVyIGFuZCBpdHMgZWZmZWN0IGZsdXNoIGRvbid0IGNhcHR1cmUgYSBzdGFsZVxuICAvLyBjb3VudCAob2ZmLWJ5LW9uZSBpbiB0aGUgYmFzZWxpbmUpLiBSZWFjdCBDb21waWxlciBiYWlscyBvdXQgaGVyZSDigJRcbiAgLy8gYWNjZXB0YWJsZSBmb3IgYSBob29rIGluc3RhbnRpYXRlZCBvbmNlIGluIFJFUEwuXG4gIGNvbnN0IGNvdW50UmVmID0gdXNlUmVmKG1lc3NhZ2VDb3VudClcbiAgY291bnRSZWYuY3VycmVudCA9IG1lc3NhZ2VDb3VudFxuICAvLyBzY3JvbGxIZWlnaHQgc25hcHNob3Qg4oCUIHRoZSBkaXZpZGVyJ3MgeSBpbiBjb250ZW50IGNvb3Jkcy4gUmVmLW9ubHk6XG4gIC8vIHJlYWQgc3luY2hyb25vdXNseSBpbiBvblNjcm9sbEF3YXkgKHNldFN0YXRlIGlzIGJhdGNoZWQsIGNhbid0XG4gIC8vIHJlYWQtdGhlbi13cml0ZSBpbiB0aGUgc2FtZSBjYWxsYmFjaykgQU5EIGJ5IEZ1bGxzY3JlZW5MYXlvdXQnc1xuICAvLyBwaWxsVmlzaWJsZSBzdWJzY3JpcHRpb24uIG51bGwgPSBwaW5uZWQgdG8gYm90dG9tLlxuICBjb25zdCBkaXZpZGVyWVJlZiA9IHVzZVJlZjxudW1iZXIgfCBudWxsPihudWxsKVxuXG4gIGNvbnN0IG9uUmVwaW4gPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgLy8gRG9uJ3QgY2xlYXIgZGl2aWRlcllSZWYgaGVyZSDigJQgYSB0cmFja3BhZCBtb21lbnR1bSB3aGVlbCBldmVudFxuICAgIC8vIHJhY2luZyBpbiB0aGUgc2FtZSBzdGRpbiBiYXRjaCB3b3VsZCBzZWUgbnVsbCBhbmQgcmUtc25hcHNob3QsXG4gICAgLy8gb3ZlcnJpZGluZyB0aGUgc2V0RGl2aWRlckluZGV4KG51bGwpIGJlbG93LiBUaGUgdXNlRWZmZWN0IGJlbG93XG4gICAgLy8gY2xlYXJzIHRoZSByZWYgYWZ0ZXIgUmVhY3QgY29tbWl0cyB0aGUgbnVsbCBkaXZpZGVySW5kZXgsIHNvIHRoZVxuICAgIC8vIHJlZiBzdGF5cyBub24tbnVsbCB1bnRpbCB0aGUgc3RhdGUgc2V0dGxlcy5cbiAgICBzZXREaXZpZGVySW5kZXgobnVsbClcbiAgfSwgW10pXG5cbiAgY29uc3Qgb25TY3JvbGxBd2F5ID0gdXNlQ2FsbGJhY2soKGhhbmRsZTogU2Nyb2xsQm94SGFuZGxlKSA9PiB7XG4gICAgLy8gTm90aGluZyBiZWxvdyB0aGUgdmlld3BvcnQg4oaSIG5vdGhpbmcgdG8ganVtcCB0by4gQ292ZXJzIGJvdGg6XG4gICAgLy8g4oCiIGVtcHR5L3Nob3J0IHNlc3Npb246IHNjcm9sbFVwIGNhbGxzIHNjcm9sbFRvKDApIHdoaWNoIGJyZWFrcyBzdGlja3lcbiAgICAvLyAgIGV2ZW4gYXQgc2Nyb2xsVG9wPTAgKHdoZWVsLXVwIG9uIGZyZXNoIHNlc3Npb24gc2hvd2VkIHRoZSBwaWxsKVxuICAgIC8vIOKAoiBjbGljay10by1zZWxlY3QgYXQgYm90dG9tOiB1c2VEcmFnVG9TY3JvbGwuY2hlY2soKSBjYWxsc1xuICAgIC8vICAgc2Nyb2xsVG8oY3VycmVudCkgdG8gYnJlYWsgc3RpY2t5IHNvIHN0cmVhbWluZyBjb250ZW50IGRvZXNuJ3Qgc2hpZnRcbiAgICAvLyAgIHVuZGVyIHRoZSBzZWxlY3Rpb24sIHRoZW4gb25TY3JvbGwoZmFsc2UsIOKApikg4oCUIGJ1dCBzY3JvbGxUb3AgaXMgc3RpbGxcbiAgICAvLyAgIGF0IG1heCAoU2FyYWggRGVhdG9uLCAjY2xhdWRlLWNvZGUtZmVlZGJhY2sgMjAyNi0wMy0xNSlcbiAgICAvLyBwZW5kaW5nRGVsdGE6IHNjcm9sbEJ5IGFjY3VtdWxhdGVzIHdpdGhvdXQgdXBkYXRpbmcgc2Nyb2xsVG9wLiBXaXRob3V0XG4gICAgLy8gaXQsIHdoZWVsaW5nIHVwIGZyb20gbWF4IHdvdWxkIHNlZSBzY3JvbGxUb3A9PW1heCBhbmQgc3VwcHJlc3MgdGhlIHBpbGwuXG4gICAgY29uc3QgbWF4ID0gTWF0aC5tYXgoXG4gICAgICAwLFxuICAgICAgaGFuZGxlLmdldFNjcm9sbEhlaWdodCgpIC0gaGFuZGxlLmdldFZpZXdwb3J0SGVpZ2h0KCksXG4gICAgKVxuICAgIGlmIChoYW5kbGUuZ2V0U2Nyb2xsVG9wKCkgKyBoYW5kbGUuZ2V0UGVuZGluZ0RlbHRhKCkgPj0gbWF4KSByZXR1cm5cbiAgICAvLyBTbmFwc2hvdCBvbmx5IG9uIHRoZSBGSVJTVCBzY3JvbGwtYXdheS4gb25TY3JvbGxBd2F5IGZpcmVzIG9uIEVWRVJZXG4gICAgLy8gc2Nyb2xsIGFjdGlvbiAobm90IGp1c3QgdGhlIGluaXRpYWwgYnJlYWsgZnJvbSBzdGlja3kpIOKAlCB0aGlzIGd1YXJkXG4gICAgLy8gcHJlc2VydmVzIHRoZSBvcmlnaW5hbCBiYXNlbGluZSBzbyB0aGUgY291bnQgZG9lc24ndCByZXNldCBvbiB0aGVcbiAgICAvLyBzZWNvbmQgUGFnZVVwLiBTdWJzZXF1ZW50IGNhbGxzIGFyZSByZWYtb25seSBuby1vcHMgKG5vIFJFUEwgcmUtcmVuZGVyKS5cbiAgICBpZiAoZGl2aWRlcllSZWYuY3VycmVudCA9PT0gbnVsbCkge1xuICAgICAgZGl2aWRlcllSZWYuY3VycmVudCA9IGhhbmRsZS5nZXRTY3JvbGxIZWlnaHQoKVxuICAgICAgLy8gTmV3IHNjcm9sbC1hd2F5IHNlc3Npb24g4oaSIG1vdmUgdGhlIGRpdmlkZXIgaGVyZSAocmVwbGFjZXMgb2xkIG9uZSlcbiAgICAgIHNldERpdmlkZXJJbmRleChjb3VudFJlZi5jdXJyZW50KVxuICAgIH1cbiAgfSwgW10pXG5cbiAgY29uc3QganVtcFRvTmV3ID0gdXNlQ2FsbGJhY2soKGhhbmRsZTogU2Nyb2xsQm94SGFuZGxlIHwgbnVsbCkgPT4ge1xuICAgIGlmICghaGFuZGxlKSByZXR1cm5cbiAgICAvLyBzY3JvbGxUb0JvdHRvbSAobm90IHNjcm9sbFRvKGRpdmlkZXJZKSk6IHNldHMgc3RpY2t5U2Nyb2xsPXRydWUgc29cbiAgICAvLyB1c2VWaXJ0dWFsU2Nyb2xsIG1vdW50cyB0aGUgdGFpbCBhbmQgcmVuZGVyLW5vZGUtdG8tb3V0cHV0IHBpbnNcbiAgICAvLyBzY3JvbGxUb3A9bWF4U2Nyb2xsLiBzY3JvbGxUbyBzZXRzIHN0aWNreVNjcm9sbD1mYWxzZSDihpIgdGhlIGNsYW1wXG4gICAgLy8gKHN0aWxsIGF0IHRvcC1yYW5nZSBib3VuZHMgYmVmb3JlIFJlYWN0IHJlLXJlbmRlcnMpIHBpbnMgc2Nyb2xsVG9wXG4gICAgLy8gYmFjaywgc3RvcHBpbmcgc2hvcnQuIFRoZSBkaXZpZGVyIHN0YXlzIHJlbmRlcmVkIChkaXZpZGVySW5kZXhcbiAgICAvLyB1bmNoYW5nZWQpIHNvIHVzZXJzIHNlZSB3aGVyZSBuZXcgbWVzc2FnZXMgc3RhcnRlZDsgdGhlIGNsZWFyIG9uXG4gICAgLy8gbmV4dCBzdWJtaXQvZXhwbGljaXQgc2Nyb2xsLXRvLWJvdHRvbSBoYW5kbGVzIGNsZWFudXAuXG4gICAgaGFuZGxlLnNjcm9sbFRvQm90dG9tKClcbiAgfSwgW10pXG5cbiAgLy8gU3luYyBkaXZpZGVyWVJlZiB3aXRoIGRpdmlkZXJJbmRleC4gV2hlbiBvblJlcGluIGZpcmVzIChzdWJtaXQsXG4gIC8vIHNjcm9sbC10by1ib3R0b20pLCBpdCBzZXRzIGRpdmlkZXJJbmRleD1udWxsIGJ1dCBsZWF2ZXMgdGhlIHJlZlxuICAvLyBub24tbnVsbCDigJQgYSB3aGVlbCBldmVudCByYWNpbmcgaW4gdGhlIHNhbWUgc3RkaW4gYmF0Y2ggd291bGRcbiAgLy8gb3RoZXJ3aXNlIHNlZSBudWxsIGFuZCByZS1zbmFwc2hvdC4gRGVmZXJyaW5nIHRoZSByZWYgY2xlYXIgdG9cbiAgLy8gdXNlRWZmZWN0IGd1YXJhbnRlZXMgdGhlIHJlZiBzdGF5cyBub24tbnVsbCB1bnRpbCBSZWFjdCBoYXMgY29tbWl0dGVkXG4gIC8vIHRoZSBudWxsIGRpdmlkZXJJbmRleCwgYmxvY2tpbmcgdGhlIGlmLW51bGwgZ3VhcmQgaW4gb25TY3JvbGxBd2F5LlxuICAvL1xuICAvLyBBbHNvIGhhbmRsZXMgL2NsZWFyLCByZXdpbmQsIHRlYW1tYXRlLXZpZXcgc3dhcCDigJQgaWYgdGhlIGNvdW50IGRyb3BzXG4gIC8vIGJlbG93IHRoZSBkaXZpZGVyIGluZGV4LCB0aGUgZGl2aWRlciB3b3VsZCBwb2ludCBhdCBub3RoaW5nLlxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChkaXZpZGVySW5kZXggPT09IG51bGwpIHtcbiAgICAgIGRpdmlkZXJZUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgfSBlbHNlIGlmIChtZXNzYWdlQ291bnQgPCBkaXZpZGVySW5kZXgpIHtcbiAgICAgIGRpdmlkZXJZUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICBzZXREaXZpZGVySW5kZXgobnVsbClcbiAgICB9XG4gIH0sIFttZXNzYWdlQ291bnQsIGRpdmlkZXJJbmRleF0pXG5cbiAgY29uc3Qgc2hpZnREaXZpZGVyID0gdXNlQ2FsbGJhY2soXG4gICAgKGluZGV4RGVsdGE6IG51bWJlciwgaGVpZ2h0RGVsdGE6IG51bWJlcikgPT4ge1xuICAgICAgc2V0RGl2aWRlckluZGV4KGlkeCA9PiAoaWR4ID09PSBudWxsID8gbnVsbCA6IGlkeCArIGluZGV4RGVsdGEpKVxuICAgICAgaWYgKGRpdmlkZXJZUmVmLmN1cnJlbnQgIT09IG51bGwpIHtcbiAgICAgICAgZGl2aWRlcllSZWYuY3VycmVudCArPSBoZWlnaHREZWx0YVxuICAgICAgfVxuICAgIH0sXG4gICAgW10sXG4gIClcblxuICByZXR1cm4ge1xuICAgIGRpdmlkZXJJbmRleCxcbiAgICBkaXZpZGVyWVJlZixcbiAgICBvblNjcm9sbEF3YXksXG4gICAgb25SZXBpbixcbiAgICBqdW1wVG9OZXcsXG4gICAgc2hpZnREaXZpZGVyLFxuICB9XG59XG5cbi8qKlxuICogQ291bnRzIGFzc2lzdGFudCB0dXJucyBpbiBtZXNzYWdlc1tkaXZpZGVySW5kZXguLmVuZCkuIEEgXCJ0dXJuXCIgaXMgd2hhdFxuICogdXNlcnMgdGhpbmsgb2YgYXMgXCJhIG5ldyBtZXNzYWdlIGZyb20gQ2xhdWRlXCIg4oCUIG5vdCByYXcgYXNzaXN0YW50IGVudHJpZXNcbiAqIChvbmUgdHVybiB5aWVsZHMgbXVsdGlwbGUgZW50cmllczogdG9vbF91c2UgYmxvY2tzICsgdGV4dCBibG9ja3MpLiBXZSBjb3VudFxuICogbm9uLWFzc2lzdGFudOKGkmFzc2lzdGFudCB0cmFuc2l0aW9ucywgYnV0IG9ubHkgZm9yIGVudHJpZXMgdGhhdCBhY3R1YWxseVxuICogY2FycnkgdGV4dCDigJQgdG9vbC11c2Utb25seSBlbnRyaWVzIGFyZSBza2lwcGVkIChsaWtlIHByb2dyZXNzIG1lc3NhZ2VzKVxuICogc28gXCLij7ogU2VhcmNoZWQgZm9yIDEzIHBhdHRlcm5zLCByZWFkIDYgZmlsZXNcIiBkb2Vzbid0IHRpY2sgdGhlIHBpbGwuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb3VudFVuc2VlbkFzc2lzdGFudFR1cm5zKFxuICBtZXNzYWdlczogcmVhZG9ubHkgTWVzc2FnZVtdLFxuICBkaXZpZGVySW5kZXg6IG51bWJlcixcbik6IG51bWJlciB7XG4gIGxldCBjb3VudCA9IDBcbiAgbGV0IHByZXZXYXNBc3Npc3RhbnQgPSBmYWxzZVxuICBmb3IgKGxldCBpID0gZGl2aWRlckluZGV4OyBpIDwgbWVzc2FnZXMubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBtID0gbWVzc2FnZXNbaV0hXG4gICAgaWYgKG0udHlwZSA9PT0gJ3Byb2dyZXNzJykgY29udGludWVcbiAgICAvLyBUb29sLXVzZS1vbmx5IGFzc2lzdGFudCBlbnRyaWVzIGFyZW4ndCBcIm5ldyBtZXNzYWdlc1wiIHRvIHRoZSB1c2VyIOKAlFxuICAgIC8vIHNraXAgdGhlbSB0aGUgc2FtZSB3YXkgd2Ugc2tpcCBwcm9ncmVzcy4gcHJldldhc0Fzc2lzdGFudCBpcyBOT1RcbiAgICAvLyB1cGRhdGVkLCBzbyBhIHRleHQgYmxvY2sgaW1tZWRpYXRlbHkgZm9sbG93aW5nIHN0aWxsIGNvdW50cyBhcyB0aGVcbiAgICAvLyBzYW1lIHR1cm4gKHRvb2xfdXNlICsgdGV4dCBmcm9tIG9uZSBBUEkgcmVzcG9uc2UgPSAxKS5cbiAgICBpZiAobS50eXBlID09PSAnYXNzaXN0YW50JyAmJiAhYXNzaXN0YW50SGFzVmlzaWJsZVRleHQobSkpIGNvbnRpbnVlXG4gICAgY29uc3QgaXNBc3Npc3RhbnQgPSBtLnR5cGUgPT09ICdhc3Npc3RhbnQnXG4gICAgaWYgKGlzQXNzaXN0YW50ICYmICFwcmV2V2FzQXNzaXN0YW50KSBjb3VudCsrXG4gICAgcHJldldhc0Fzc2lzdGFudCA9IGlzQXNzaXN0YW50XG4gIH1cbiAgcmV0dXJuIGNvdW50XG59XG5cbmZ1bmN0aW9uIGFzc2lzdGFudEhhc1Zpc2libGVUZXh0KG06IE1lc3NhZ2UpOiBib29sZWFuIHtcbiAgaWYgKG0udHlwZSAhPT0gJ2Fzc2lzdGFudCcpIHJldHVybiBmYWxzZVxuICBmb3IgKGNvbnN0IGIgb2YgbS5tZXNzYWdlLmNvbnRlbnQpIHtcbiAgICBpZiAoYi50eXBlID09PSAndGV4dCcgJiYgYi50ZXh0LnRyaW0oKSAhPT0gJycpIHJldHVybiB0cnVlXG4gIH1cbiAgcmV0dXJuIGZhbHNlXG59XG5cbmV4cG9ydCB0eXBlIFVuc2VlbkRpdmlkZXIgPSB7IGZpcnN0VW5zZWVuVXVpZDogTWVzc2FnZVsndXVpZCddOyBjb3VudDogbnVtYmVyIH1cblxuLyoqXG4gKiBCdWlsZHMgdGhlIHVuc2VlbkRpdmlkZXIgb2JqZWN0IFJFUEwgcGFzc2VzIHRvIE1lc3NhZ2VzICsgdGhlIHBpbGwuXG4gKiBSZXR1cm5zIHVuZGVmaW5lZCBvbmx5IHdoZW4gbm8gY29udGVudCBoYXMgYXJyaXZlZCBwYXN0IHRoZSBkaXZpZGVyXG4gKiB5ZXQgKG1lc3NhZ2VzW2RpdmlkZXJJbmRleF0gZG9lc24ndCBleGlzdCkuIE9uY2UgQU5ZIG1lc3NhZ2UgYXJyaXZlc1xuICog4oCUIGluY2x1ZGluZyB0b29sX3VzZS1vbmx5IGFzc2lzdGFudCBlbnRyaWVzIGFuZCB0b29sX3Jlc3VsdCB1c2VyIGVudHJpZXNcbiAqIHRoYXQgY291bnRVbnNlZW5Bc3Npc3RhbnRUdXJucyBza2lwcyDigJQgY291bnQgZmxvb3JzIGF0IDEgc28gdGhlIHBpbGxcbiAqIGZsaXBzIGZyb20gXCJKdW1wIHRvIGJvdHRvbVwiIHRvIFwiMSBuZXcgbWVzc2FnZVwiLiBXaXRob3V0IHRoZSBmbG9vcixcbiAqIHRoZSBwaWxsIHN0YXlzIFwiSnVtcCB0byBib3R0b21cIiB0aHJvdWdoIGFuIGVudGlyZSB0b29sLWNhbGwgc2VxdWVuY2VcbiAqIHVudGlsIENsYXVkZSdzIHRleHQgcmVzcG9uc2UgbGFuZHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb21wdXRlVW5zZWVuRGl2aWRlcihcbiAgbWVzc2FnZXM6IHJlYWRvbmx5IE1lc3NhZ2VbXSxcbiAgZGl2aWRlckluZGV4OiBudW1iZXIgfCBudWxsLFxuKTogVW5zZWVuRGl2aWRlciB8IHVuZGVmaW5lZCB7XG4gIGlmIChkaXZpZGVySW5kZXggPT09IG51bGwpIHJldHVybiB1bmRlZmluZWRcbiAgLy8gU2tpcCBwcm9ncmVzcyBhbmQgbnVsbC1yZW5kZXJpbmcgYXR0YWNobWVudHMgd2hlbiBwaWNraW5nIHRoZSBkaXZpZGVyXG4gIC8vIGFuY2hvciDigJQgTWVzc2FnZXMudHN4IGZpbHRlcnMgdGhlc2Ugb3V0IG9mIHJlbmRlcmFibGVNZXNzYWdlcyBiZWZvcmUgdGhlXG4gIC8vIGRpdmlkZXJCZWZvcmVJbmRleCBzZWFyY2gsIHNvIHRoZWlyIFVVSUQgd291bGRuJ3QgYmUgZm91bmQgKENDLTcyNCkuXG4gIC8vIEhvb2sgYXR0YWNobWVudHMgdXNlIHJhbmRvbVVVSUQoKSBzbyBub3RoaW5nIHNoYXJlcyB0aGVpciAyNC1jaGFyIHByZWZpeC5cbiAgbGV0IGFuY2hvcklkeCA9IGRpdmlkZXJJbmRleFxuICB3aGlsZSAoXG4gICAgYW5jaG9ySWR4IDwgbWVzc2FnZXMubGVuZ3RoICYmXG4gICAgKG1lc3NhZ2VzW2FuY2hvcklkeF0/LnR5cGUgPT09ICdwcm9ncmVzcycgfHxcbiAgICAgIGlzTnVsbFJlbmRlcmluZ0F0dGFjaG1lbnQobWVzc2FnZXNbYW5jaG9ySWR4XSEpKVxuICApIHtcbiAgICBhbmNob3JJZHgrK1xuICB9XG4gIGNvbnN0IHV1aWQgPSBtZXNzYWdlc1thbmNob3JJZHhdPy51dWlkXG4gIGlmICghdXVpZCkgcmV0dXJuIHVuZGVmaW5lZFxuICBjb25zdCBjb3VudCA9IGNvdW50VW5zZWVuQXNzaXN0YW50VHVybnMobWVzc2FnZXMsIGRpdmlkZXJJbmRleClcbiAgcmV0dXJuIHsgZmlyc3RVbnNlZW5VdWlkOiB1dWlkLCBjb3VudDogTWF0aC5tYXgoMSwgY291bnQpIH1cbn1cblxuLyoqXG4gKiBMYXlvdXQgd3JhcHBlciBmb3IgdGhlIFJFUEwuIEluIGZ1bGxzY3JlZW4gbW9kZSwgcHV0cyBzY3JvbGxhYmxlXG4gKiBjb250ZW50IGluIGEgc3RpY2t5LXNjcm9sbCBib3ggYW5kIHBpbnMgYm90dG9tIGNvbnRlbnQgdmlhIGZsZXhib3guXG4gKiBPdXRzaWRlIGZ1bGxzY3JlZW4gbW9kZSwgcmVuZGVycyBjb250ZW50IHNlcXVlbnRpYWxseSBzbyB0aGUgZXhpc3RpbmdcbiAqIG1haW4tc2NyZWVuIHNjcm9sbGJhY2sgcmVuZGVyaW5nIHdvcmtzIHVuY2hhbmdlZC5cbiAqXG4gKiBGdWxsc2NyZWVuIG1vZGUgZGVmYXVsdHMgb24gZm9yIGFudHMgKENMQVVERV9DT0RFX05PX0ZMSUNLRVI9MCB0byBvcHQgb3V0KVxuICogYW5kIG9mZiBmb3IgZXh0ZXJuYWwgdXNlcnMgKENMQVVERV9DT0RFX05PX0ZMSUNLRVI9MSB0byBvcHQgaW4pLlxuICogVGhlIDxBbHRlcm5hdGVTY3JlZW4+IHdyYXBwZXJcbiAqIChhbHQgYnVmZmVyICsgbW91c2UgdHJhY2tpbmcgKyBoZWlnaHQgY29uc3RyYWludCkgbGl2ZXMgYXQgUkVQTCdzIHJvb3RcbiAqIHNvIG5vdGhpbmcgY2FuIGFjY2lkZW50YWxseSByZW5kZXIgb3V0c2lkZSBpdC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEZ1bGxzY3JlZW5MYXlvdXQoe1xuICBzY3JvbGxhYmxlLFxuICBib3R0b20sXG4gIG92ZXJsYXksXG4gIGJvdHRvbUZsb2F0LFxuICBtb2RhbCxcbiAgbW9kYWxTY3JvbGxSZWYsXG4gIHNjcm9sbFJlZixcbiAgZGl2aWRlcllSZWYsXG4gIGhpZGVQaWxsID0gZmFsc2UsXG4gIGhpZGVTdGlja3kgPSBmYWxzZSxcbiAgbmV3TWVzc2FnZUNvdW50ID0gMCxcbiAgb25QaWxsQ2xpY2ssXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgcm93czogdGVybWluYWxSb3dzLCBjb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICAvLyBTY3JvbGwtZGVyaXZlZCBjaHJvbWUgc3RhdGUgbGl2ZXMgSEVSRSwgbm90IGluIFJFUEwuIFN0aWNreVRyYWNrZXJcbiAgLy8gd3JpdGVzIHZpYSBTY3JvbGxDaHJvbWVDb250ZXh0OyBwaWxsVmlzaWJsZSBzdWJzY3JpYmVzIGRpcmVjdGx5IHRvXG4gIC8vIFNjcm9sbEJveC4gQm90aCBjaGFuZ2UgcmFyZWx5IChwaWxsIGZsaXBzIG9uY2UgcGVyIHRocmVzaG9sZCBjcm9zc2luZyxcbiAgLy8gc3RpY2t5IGNoYW5nZXMgfjUtMjDDly90cmFuc2NyaXB0KSDigJQgcmUtcmVuZGVyaW5nIEZ1bGxzY3JlZW5MYXlvdXQgb25cbiAgLy8gdGhvc2UgaXMgZmluZTsgcmUtcmVuZGVyaW5nIHRoZSA2OTY2LWxpbmUgUkVQTCArIGl0cyAyMisgdXNlQXBwU3RhdGVcbiAgLy8gc2VsZWN0b3JzIHBlci1zY3JvbGwtZnJhbWUgd2FzIG5vdC5cbiAgY29uc3QgW3N0aWNreVByb21wdCwgc2V0U3RpY2t5UHJvbXB0XSA9IHVzZVN0YXRlPFN0aWNreVByb21wdCB8IG51bGw+KG51bGwpXG4gIGNvbnN0IGNocm9tZUN0eCA9IHVzZU1lbW8oKCkgPT4gKHsgc2V0U3RpY2t5UHJvbXB0IH0pLCBbXSlcbiAgLy8gQm9vbGVhbi1xdWFudGl6ZWQgc2Nyb2xsIHN1YnNjcmlwdGlvbi4gU25hcHNob3QgaXMgXCJpcyB2aWV3cG9ydCBib3R0b21cbiAgLy8gYWJvdmUgdGhlIGRpdmlkZXIgeT9cIiDigJQgT2JqZWN0LmlzIG9uIGEgYm9vbGVhbiDihpIgRnVsbHNjcmVlbkxheW91dCBvbmx5XG4gIC8vIHJlLXJlbmRlcnMgd2hlbiB0aGUgcGlsbCBzaG91bGQgYWN0dWFsbHkgZmxpcCwgbm90IHBlci1mcmFtZS5cbiAgY29uc3Qgc3Vic2NyaWJlID0gdXNlQ2FsbGJhY2soXG4gICAgKGxpc3RlbmVyOiAoKSA9PiB2b2lkKSA9PlxuICAgICAgc2Nyb2xsUmVmPy5jdXJyZW50Py5zdWJzY3JpYmUobGlzdGVuZXIpID8/ICgoKSA9PiB7fSksXG4gICAgW3Njcm9sbFJlZl0sXG4gIClcbiAgY29uc3QgcGlsbFZpc2libGUgPSB1c2VTeW5jRXh0ZXJuYWxTdG9yZShzdWJzY3JpYmUsICgpID0+IHtcbiAgICBjb25zdCBzID0gc2Nyb2xsUmVmPy5jdXJyZW50XG4gICAgY29uc3QgZGl2aWRlclkgPSBkaXZpZGVyWVJlZj8uY3VycmVudFxuICAgIGlmICghcyB8fCBkaXZpZGVyWSA9PSBudWxsKSByZXR1cm4gZmFsc2VcbiAgICByZXR1cm4gKFxuICAgICAgcy5nZXRTY3JvbGxUb3AoKSArIHMuZ2V0UGVuZGluZ0RlbHRhKCkgKyBzLmdldFZpZXdwb3J0SGVpZ2h0KCkgPCBkaXZpZGVyWVxuICAgIClcbiAgfSlcbiAgLy8gV2lyZSB1cCBoeXBlcmxpbmsgY2xpY2sgaGFuZGxpbmcg4oCUIGluIGZ1bGxzY3JlZW4gbW9kZSwgbW91c2UgdHJhY2tpbmdcbiAgLy8gaW50ZXJjZXB0cyBjbGlja3MgYmVmb3JlIHRoZSB0ZXJtaW5hbCBjYW4gb3BlbiBPU0MgOCBsaW5rcyBuYXRpdmVseS5cbiAgdXNlTGF5b3V0RWZmZWN0KCgpID0+IHtcbiAgICBpZiAoIWlzRnVsbHNjcmVlbkVudkVuYWJsZWQoKSkgcmV0dXJuXG4gICAgY29uc3QgaW5rID0gaW5zdGFuY2VzLmdldChwcm9jZXNzLnN0ZG91dClcbiAgICBpZiAoIWluaykgcmV0dXJuXG4gICAgaW5rLm9uSHlwZXJsaW5rQ2xpY2sgPSB1cmwgPT4ge1xuICAgICAgLy8gTW9zdCBPU0MgOCBsaW5rcyBlbWl0dGVkIGJ5IENsYXVkZSBDb2RlIGFyZSBmaWxlOi8vIFVSTHMgZnJvbVxuICAgICAgLy8gRmlsZVBhdGhMaW5rIChGaWxlRWRpdC9GaWxlV3JpdGUvRmlsZVJlYWQgdG9vbCBvdXRwdXQpLiBvcGVuQnJvd3NlclxuICAgICAgLy8gcmVqZWN0cyBub24taHR0cChzKSBwcm90b2NvbHMg4oCUIHJvdXRlIGZpbGU6IHRvIG9wZW5QYXRoIGluc3RlYWQuXG4gICAgICBpZiAodXJsLnN0YXJ0c1dpdGgoJ2ZpbGU6JykpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICB2b2lkIG9wZW5QYXRoKGZpbGVVUkxUb1BhdGgodXJsKSlcbiAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgLy8gTWFsZm9ybWVkIGZpbGU6IFVSTHMgKGUuZy4gZmlsZTovL2hvc3QvcGF0aCBmcm9tIHBsYWluLXRleHRcbiAgICAgICAgICAvLyBkZXRlY3Rpb24pIGNhdXNlIGZpbGVVUkxUb1BhdGggdG8gdGhyb3cg4oCUIGlnbm9yZSBzaWxlbnRseS5cbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdm9pZCBvcGVuQnJvd3Nlcih1cmwpXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBpbmsub25IeXBlcmxpbmtDbGljayA9IHVuZGVmaW5lZFxuICAgIH1cbiAgfSwgW10pXG5cbiAgaWYgKGlzRnVsbHNjcmVlbkVudkVuYWJsZWQoKSkge1xuICAgIC8vIE92ZXJsYXkgcmVuZGVycyBCRUxPVyBtZXNzYWdlcyBpbnNpZGUgdGhlIHNhbWUgU2Nyb2xsQm94IOKAlCB1c2VyIGNhblxuICAgIC8vIHNjcm9sbCB1cCB0byBzZWUgcHJpb3IgY29udGV4dCB3aGlsZSBhIHBlcm1pc3Npb24gZGlhbG9nIGlzIHNob3dpbmcuXG4gICAgLy8gVGhlIFNjcm9sbEJveCBuZXZlciB1bm1vdW50cyBhY3Jvc3Mgb3ZlcmxheSB0cmFuc2l0aW9ucywgc28gc2Nyb2xsXG4gICAgLy8gcG9zaXRpb24gaXMgcHJlc2VydmVkIHdpdGhvdXQgc2F2ZS9yZXN0b3JlLiBzdGlja3lTY3JvbGwgYXV0by1zY3JvbGxzXG4gICAgLy8gdG8gdGhlIGFwcGVuZGVkIG92ZXJsYXkgd2hlbiBpdCBtb3VudHMgKGlmIHVzZXIgd2FzIGFscmVhZHkgYXRcbiAgICAvLyBib3R0b20pOyBSRVBMIHJlLXBpbnMgb24gdGhlIG92ZXJsYXkgYXBwZWFyL2Rpc21pc3MgdHJhbnNpdGlvbiBmb3JcbiAgICAvLyB0aGUgY2FzZSB3aGVyZSBzdGlja3kgd2FzIGJyb2tlbi4gVGFsbCBkaWFsb2dzIChGaWxlRWRpdCBkaWZmcykgc3RpbGxcbiAgICAvLyBnZXQgUGdVcC9QZ0RuL3doZWVsIOKAlCBzYW1lIHNjcm9sbFJlZiBkcml2ZXMgdGhlIHNhbWUgU2Nyb2xsQm94LlxuICAgIC8vIFRocmVlIHN0aWNreSBzdGF0ZXM6IG51bGwgKGF0IGJvdHRvbSksIHt0ZXh0LHNjcm9sbFRvfSAoc2Nyb2xsZWQgdXAsXG4gICAgLy8gaGVhZGVyIHNob3dzKSwgJ2NsaWNrZWQnIChqdXN0IGNsaWNrZWQgaGVhZGVyIOKAlCBoaWRlIGl0IHNvIHRoZVxuICAgIC8vIGNvbnRlbnQg4p2vIHRha2VzIHJvdyAwKS4gcGFkQ29sbGFwc2VkIGNvdmVycyB0aGUgbGF0dGVyIHR3bzogb25jZVxuICAgIC8vIHNjcm9sbGVkIGF3YXkgZnJvbSBib3R0b20sIHBhZGRpbmcgZHJvcHMgdG8gMCBhbmQgc3RheXMgdGhlcmUgdW50aWxcbiAgICAvLyByZXBpbi4gaGVhZGVyVmlzaWJsZSBpcyBvbmx5IHRoZSBtaWRkbGUgc3RhdGUuIEFmdGVyIGNsaWNrOlxuICAgIC8vIHNjcm9sbEJveF95PTAgKGhlYWRlciBnb25lKSArIHBhZGRpbmc9MCDihpIgdmlld3BvcnRUb3A9MCDihpIg4p2vIGF0XG4gICAgLy8gcm93IDAuIE9uIG5leHQgc2Nyb2xsIHRoZSBvbkNoYW5nZSBmaXJlcyB3aXRoIGEgZnJlc2gge3RleHR9IGFuZFxuICAgIC8vIGhlYWRlciBjb21lcyBiYWNrICh2aWV3cG9ydFRvcCAw4oaSMSwgYSBzaW5nbGUgMS1yb3cgc2hpZnQg4oCUXG4gICAgLy8gYWNjZXB0YWJsZSBzaW5jZSB1c2VyIGV4cGxpY2l0bHkgc2Nyb2xsZWQpLlxuICAgIGNvbnN0IHN0aWNreSA9IGhpZGVTdGlja3kgPyBudWxsIDogc3RpY2t5UHJvbXB0XG4gICAgY29uc3QgaGVhZGVyUHJvbXB0ID1cbiAgICAgIHN0aWNreSAhPSBudWxsICYmIHN0aWNreSAhPT0gJ2NsaWNrZWQnICYmIG92ZXJsYXkgPT0gbnVsbCA/IHN0aWNreSA6IG51bGxcbiAgICBjb25zdCBwYWRDb2xsYXBzZWQgPSBzdGlja3kgIT0gbnVsbCAmJiBvdmVybGF5ID09IG51bGxcbiAgICByZXR1cm4gKFxuICAgICAgPFByb21wdE92ZXJsYXlQcm92aWRlcj5cbiAgICAgICAgPEJveCBmbGV4R3Jvdz17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG92ZXJmbG93PVwiaGlkZGVuXCI+XG4gICAgICAgICAge2hlYWRlclByb21wdCAmJiAoXG4gICAgICAgICAgICA8U3RpY2t5UHJvbXB0SGVhZGVyXG4gICAgICAgICAgICAgIHRleHQ9e2hlYWRlclByb21wdC50ZXh0fVxuICAgICAgICAgICAgICBvbkNsaWNrPXtoZWFkZXJQcm9tcHQuc2Nyb2xsVG99XG4gICAgICAgICAgICAvPlxuICAgICAgICAgICl9XG4gICAgICAgICAgPFNjcm9sbEJveFxuICAgICAgICAgICAgcmVmPXtzY3JvbGxSZWZ9XG4gICAgICAgICAgICBmbGV4R3Jvdz17MX1cbiAgICAgICAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgICAgICAgcGFkZGluZ1RvcD17cGFkQ29sbGFwc2VkID8gMCA6IDF9XG4gICAgICAgICAgICBzdGlja3lTY3JvbGxcbiAgICAgICAgICA+XG4gICAgICAgICAgICA8U2Nyb2xsQ2hyb21lQ29udGV4dCB2YWx1ZT17Y2hyb21lQ3R4fT5cbiAgICAgICAgICAgICAge3Njcm9sbGFibGV9XG4gICAgICAgICAgICA8L1Njcm9sbENocm9tZUNvbnRleHQ+XG4gICAgICAgICAgICB7b3ZlcmxheX1cbiAgICAgICAgICA8L1Njcm9sbEJveD5cbiAgICAgICAgICB7IWhpZGVQaWxsICYmIHBpbGxWaXNpYmxlICYmIG92ZXJsYXkgPT0gbnVsbCAmJiAoXG4gICAgICAgICAgICA8TmV3TWVzc2FnZXNQaWxsIGNvdW50PXtuZXdNZXNzYWdlQ291bnR9IG9uQ2xpY2s9e29uUGlsbENsaWNrfSAvPlxuICAgICAgICAgICl9XG4gICAgICAgICAge2JvdHRvbUZsb2F0ICE9IG51bGwgJiYgKFxuICAgICAgICAgICAgPEJveCBwb3NpdGlvbj1cImFic29sdXRlXCIgYm90dG9tPXswfSByaWdodD17MH0gb3BhcXVlPlxuICAgICAgICAgICAgICB7Ym90dG9tRmxvYXR9XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZmxleFNocmluaz17MH0gd2lkdGg9XCIxMDAlXCIgbWF4SGVpZ2h0PVwiNTAlXCI+XG4gICAgICAgICAgPFN1Z2dlc3Rpb25zT3ZlcmxheSAvPlxuICAgICAgICAgIDxEaWFsb2dPdmVybGF5IC8+XG4gICAgICAgICAgPEJveFxuICAgICAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgICAgICB3aWR0aD1cIjEwMCVcIlxuICAgICAgICAgICAgZmxleEdyb3c9ezF9XG4gICAgICAgICAgICBvdmVyZmxvd1k9XCJoaWRkZW5cIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIHtib3R0b219XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7bW9kYWwgIT0gbnVsbCAmJiAoXG4gICAgICAgICAgPE1vZGFsQ29udGV4dFxuICAgICAgICAgICAgdmFsdWU9e3tcbiAgICAgICAgICAgICAgcm93czogdGVybWluYWxSb3dzIC0gTU9EQUxfVFJBTlNDUklQVF9QRUVLIC0gMSxcbiAgICAgICAgICAgICAgY29sdW1uczogY29sdW1ucyAtIDQsXG4gICAgICAgICAgICAgIHNjcm9sbFJlZjogbW9kYWxTY3JvbGxSZWYgPz8gbnVsbCxcbiAgICAgICAgICAgIH19XG4gICAgICAgICAgPlxuICAgICAgICAgICAgey8qIEJvdHRvbS1hbmNob3JlZCwgZ3Jvd3MgdXB3YXJkIHRvIGZpdCBjb250ZW50LiBtYXhIZWlnaHQga2VlcHMgYVxuICAgICAgICAgICAgICAgIGZldyByb3dzIG9mIHRyYW5zY3JpcHQgcGVlayBhYm92ZSB0aGUg4paUIGRpdmlkZXIuIFNob3J0IG1vZGFsc1xuICAgICAgICAgICAgICAgICgvbW9kZWwpIHNpdCBzbWFsbCBhdCB0aGUgYm90dG9tIHdpdGggbG90cyBvZiB0cmFuc2NyaXB0IGFib3ZlO1xuICAgICAgICAgICAgICAgIHRhbGwgbW9kYWxzICgvYnVkZHkgQ2FyZCkgZ3JvdyBhcyBuZWVkZWQsIGNsaXBwZWQgYnkgb3ZlcmZsb3cuXG4gICAgICAgICAgICAgICAgUHJldmlvdXNseSBmaXhlZC1oZWlnaHQgKHRvcCtib3R0b20gYW5jaG9yZWQpIOKAlCBhbnkgZml4ZWQgY2FwXG4gICAgICAgICAgICAgICAgZWl0aGVyIGNsaXBwZWQgdGFsbCBjb250ZW50IG9yIGxlZnQgc2hvcnQgY29udGVudCBmbG9hdGluZyBpblxuICAgICAgICAgICAgICAgIGEgbW9zdGx5LWVtcHR5IHBhbmUuXG5cbiAgICAgICAgICAgICAgICBmbGV4U2hyaW5rPTAgb24gdGhlIGlubmVyIEJveCBpcyBsb2FkLWJlYXJpbmc6IHdpdGggU2hyaW5rPTEsXG4gICAgICAgICAgICAgICAgeW9nYSBzcXVlZXplcyBkZWVwIGNoaWxkcmVuIHRvIGg9MCB3aGVuIGNvbnRlbnQgPiBtYXhIZWlnaHQsXG4gICAgICAgICAgICAgICAgYW5kIHNpYmxpbmcgVGV4dHMgbGFuZCBvbiB0aGUgc2FtZSByb3cg4oaSIGdob3N0IG92ZXJsYXBcbiAgICAgICAgICAgICAgICAoXCI1IHNlcnZlcnNQIHNlcnZlcnNcIikuIENsaXBwaW5nIGF0IHRoZSBvdXRlciBCb3gncyBtYXhIZWlnaHRcbiAgICAgICAgICAgICAgICBrZWVwcyBjaGlsZHJlbiBhdCBuYXR1cmFsIHNpemUuXG5cbiAgICAgICAgICAgICAgICBEaXZpZGVyIHdyYXBwZWQgaW4gZmxleFNocmluaz0wOiB3aGVuIHRoZSBpbm5lciBib3ggb3ZlcmZsb3dzXG4gICAgICAgICAgICAgICAgKHRhbGwgL2NvbmZpZyBvcHRpb24gbGlzdCksIHlvZ2Egc2hyaW5rcyB0aGUgZGl2aWRlciBUZXh0IHRvXG4gICAgICAgICAgICAgICAgaD0wIHRvIGFic29yYiB0aGUgZGVmaWNpdCDigJQgaXQncyB0aGUgb25seSBzaHJpbmthYmxlIHNpYmxpbmcuXG4gICAgICAgICAgICAgICAgVGhlIHdyYXBwZXIga2VlcHMgaXQgYXQgMSByb3c7IG92ZXJmbG93IHBhc3QgbWF4SGVpZ2h0IGlzXG4gICAgICAgICAgICAgICAgY2xpcHBlZCBhdCB0aGUgYm90dG9tIGJ5IG92ZXJmbG93PWhpZGRlbiBpbnN0ZWFkLiAqL31cbiAgICAgICAgICAgIDxCb3hcbiAgICAgICAgICAgICAgcG9zaXRpb249XCJhYnNvbHV0ZVwiXG4gICAgICAgICAgICAgIGJvdHRvbT17MH1cbiAgICAgICAgICAgICAgbGVmdD17MH1cbiAgICAgICAgICAgICAgcmlnaHQ9ezB9XG4gICAgICAgICAgICAgIG1heEhlaWdodD17dGVybWluYWxSb3dzIC0gTU9EQUxfVFJBTlNDUklQVF9QRUVLfVxuICAgICAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICAgICAgb3ZlcmZsb3c9XCJoaWRkZW5cIlxuICAgICAgICAgICAgICBvcGFxdWVcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgPEJveCBmbGV4U2hyaW5rPXswfT5cbiAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cInBlcm1pc3Npb25cIj57J+KWlCcucmVwZWF0KGNvbHVtbnMpfTwvVGV4dD5cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgIDxCb3hcbiAgICAgICAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICAgICAgICBwYWRkaW5nWD17Mn1cbiAgICAgICAgICAgICAgICBmbGV4U2hyaW5rPXswfVxuICAgICAgICAgICAgICAgIG92ZXJmbG93PVwiaGlkZGVuXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIHttb2RhbH1cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8L01vZGFsQ29udGV4dD5cbiAgICAgICAgKX1cbiAgICAgIDwvUHJvbXB0T3ZlcmxheVByb3ZpZGVyPlxuICAgIClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIHtzY3JvbGxhYmxlfVxuICAgICAge2JvdHRvbX1cbiAgICAgIHtvdmVybGF5fVxuICAgICAge21vZGFsfVxuICAgIDwvPlxuICApXG59XG5cbi8vIFNsYWNrLXN0eWxlIHBpbGwuIEFic29sdXRlIG92ZXJsYXkgYXQgYm90dG9tPXswfSBvZiB0aGUgc2Nyb2xsd3JhcCDigJQgZmxvYXRzXG4vLyBvdmVyIHRoZSBTY3JvbGxCb3gncyBsYXN0IGNvbnRlbnQgcm93LCBvbmx5IG9ic2N1cmluZyB0aGUgY2VudGVyZWQgcGlsbFxuLy8gdGV4dCAodGhlIHJlc3Qgb2YgdGhlIHJvdyBzaG93cyBTY3JvbGxCb3ggY29udGVudCkuIFNjcm9sbC1zbWVhciBmcm9tXG4vLyBERUNTVEJNIHNoaWZ0aW5nIHRoZSBwaWxsJ3MgcGl4ZWxzIGlzIHJlcGFpcmVkIGF0IHRoZSBJbmsgbGF5ZXJcbi8vIChhYnNvbHV0ZVJlY3RzUHJldiB0aGlyZC1wYXNzIGluIHJlbmRlci1ub2RlLXRvLW91dHB1dC50cywgIzIzOTM5KS4gU2hvd3Ncbi8vIFwiSnVtcCB0byBib3R0b21cIiB3aGVuIGNvdW50IGlzIDAgKHNjcm9sbGVkIGF3YXkgYnV0IG5vIG5ldyBtZXNzYWdlcyB5ZXQg4oCUXG4vLyB0aGUgZGVhZCB6b25lIHdoZXJlIHVzZXJzIHByZXZpb3VzbHkgdGhvdWdodCBjaGF0IHN0YWxsZWQpLlxuZnVuY3Rpb24gTmV3TWVzc2FnZXNQaWxsKHtcbiAgY291bnQsXG4gIG9uQ2xpY2ssXG59OiB7XG4gIGNvdW50OiBudW1iZXJcbiAgb25DbGljaz86ICgpID0+IHZvaWRcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbaG92ZXIsIHNldEhvdmVyXSA9IHVzZVN0YXRlKGZhbHNlKVxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIHBvc2l0aW9uPVwiYWJzb2x1dGVcIlxuICAgICAgYm90dG9tPXswfVxuICAgICAgbGVmdD17MH1cbiAgICAgIHJpZ2h0PXswfVxuICAgICAganVzdGlmeUNvbnRlbnQ9XCJjZW50ZXJcIlxuICAgID5cbiAgICAgIDxCb3hcbiAgICAgICAgb25DbGljaz17b25DbGlja31cbiAgICAgICAgb25Nb3VzZUVudGVyPXsoKSA9PiBzZXRIb3Zlcih0cnVlKX1cbiAgICAgICAgb25Nb3VzZUxlYXZlPXsoKSA9PiBzZXRIb3ZlcihmYWxzZSl9XG4gICAgICA+XG4gICAgICAgIDxUZXh0XG4gICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtcbiAgICAgICAgICAgIGhvdmVyID8gJ3VzZXJNZXNzYWdlQmFja2dyb3VuZEhvdmVyJyA6ICd1c2VyTWVzc2FnZUJhY2tncm91bmQnXG4gICAgICAgICAgfVxuICAgICAgICAgIGRpbUNvbG9yXG4gICAgICAgID5cbiAgICAgICAgICB7JyAnfVxuICAgICAgICAgIHtjb3VudCA+IDBcbiAgICAgICAgICAgID8gYCR7Y291bnR9IG5ldyAke3BsdXJhbChjb3VudCwgJ21lc3NhZ2UnKX1gXG4gICAgICAgICAgICA6ICdKdW1wIHRvIGJvdHRvbSd9eycgJ31cbiAgICAgICAgICB7ZmlndXJlcy5hcnJvd0Rvd259eycgJ31cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuLy8gQ29udGV4dCBicmVhZGNydW1iOiB3aGVuIHNjcm9sbGVkIHVwIGludG8gaGlzdG9yeSwgcGluIHRoZSBjdXJyZW50XG4vLyBjb252ZXJzYXRpb24gdHVybidzIHByb21wdCBhYm92ZSB0aGUgdmlld3BvcnQgc28geW91IGtub3cgd2hhdCBDbGF1ZGUgd2FzXG4vLyByZXNwb25kaW5nIHRvLiBOb3JtYWwtZmxvdyBzaWJsaW5nIEJFRk9SRSB0aGUgU2Nyb2xsQm94IChtaXJyb3JzIHRoZSBwaWxsXG4vLyBiZWxvdyBpdCkg4oCUIHNocmlua3MgdGhlIFNjcm9sbEJveCBieSBleGFjdGx5IDEgcm93IHZpYSBmbGV4LCBzdGF5cyBvdXRzaWRlXG4vLyB0aGUgREVDU1RCTSBzY3JvbGwgcmVnaW9uLiBDbGljayBqdW1wcyBiYWNrIHRvIHRoZSBwcm9tcHQuXG4vL1xuLy8gSGVpZ2h0IGlzIEZJWEVEIGF0IDEgcm93ICh0cnVuY2F0ZS1lbmQgZm9yIGxvbmcgcHJvbXB0cykuIEEgdmFyaWFibGUtaGVpZ2h0XG4vLyBoZWFkZXIgKDEgd2hlbiBzaG9ydCwgMiB3aGVuIHdyYXBwZWQpIHNoaWZ0cyB0aGUgU2Nyb2xsQm94IGJ5IDEgcm93IGV2ZXJ5XG4vLyB0aW1lIHRoZSBzdGlja3kgcHJvbXB0IHN3aXRjaGVzIGR1cmluZyBzY3JvbGwg4oCUIGNvbnRlbnQganVtcHMgb24gc2NyZWVuXG4vLyBldmVuIHdpdGggc2Nyb2xsVG9wIHVuY2hhbmdlZCAodGhlIERFQ1NUQk0gcmVnaW9uIHRvcCBzaGlmdHMgd2l0aCB0aGVcbi8vIFNjcm9sbEJveCwgYW5kIHRoZSBkaWZmIGVuZ2luZSBzZWVzIFwiZXZlcnl0aGluZyBtb3ZlZFwiKS4gRml4ZWQgaGVpZ2h0XG4vLyBrZWVwcyB0aGUgU2Nyb2xsQm94IGFuY2hvcmVkOyBvbmx5IHRoZSBoZWFkZXIgVEVYVCBjaGFuZ2VzLCBub3QgaXRzIGJveC5cbmZ1bmN0aW9uIFN0aWNreVByb21wdEhlYWRlcih7XG4gIHRleHQsXG4gIG9uQ2xpY2ssXG59OiB7XG4gIHRleHQ6IHN0cmluZ1xuICBvbkNsaWNrOiAoKSA9PiB2b2lkXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW2hvdmVyLCBzZXRIb3Zlcl0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBmbGV4U2hyaW5rPXswfVxuICAgICAgd2lkdGg9XCIxMDAlXCJcbiAgICAgIGhlaWdodD17MX1cbiAgICAgIHBhZGRpbmdSaWdodD17MX1cbiAgICAgIGJhY2tncm91bmRDb2xvcj17XG4gICAgICAgIGhvdmVyID8gJ3VzZXJNZXNzYWdlQmFja2dyb3VuZEhvdmVyJyA6ICd1c2VyTWVzc2FnZUJhY2tncm91bmQnXG4gICAgICB9XG4gICAgICBvbkNsaWNrPXtvbkNsaWNrfVxuICAgICAgb25Nb3VzZUVudGVyPXsoKSA9PiBzZXRIb3Zlcih0cnVlKX1cbiAgICAgIG9uTW91c2VMZWF2ZT17KCkgPT4gc2V0SG92ZXIoZmFsc2UpfVxuICAgID5cbiAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCIgd3JhcD1cInRydW5jYXRlLWVuZFwiPlxuICAgICAgICB7ZmlndXJlcy5wb2ludGVyfSB7dGV4dH1cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuXG4vLyBTbGFzaC1jb21tYW5kIHN1Z2dlc3Rpb24gb3ZlcmxheSDigJQgc2VlIHByb21wdE92ZXJsYXlDb250ZXh0LnRzeCBmb3Igd2h5XG4vLyBpdCdzIHBvcnRhbGVkLiBTY3JvbGwtc21lYXIgZnJvbSBmbG9hdGluZyBvdmVyIHRoZSBERUNTVEJNIHJlZ2lvbiBpc1xuLy8gcmVwYWlyZWQgYXQgdGhlIEluayBsYXllciAoYWJzb2x1dGVSZWN0c1ByZXYgaW4gcmVuZGVyLW5vZGUtdG8tb3V0cHV0LnRzKS5cbi8vIFRoZSByZW5kZXJlciBjbGFtcHMgbmVnYXRpdmUgeSB0byAwIGZvciBhYnNvbHV0ZSBlbGVtZW50cyAoc2VlXG4vLyByZW5kZXItbm9kZS10by1vdXRwdXQudHMpLCBzbyB0aGUgdG9wIHJvd3MgKGJlc3QgbWF0Y2hlcykgc3RheSB2aXNpYmxlXG4vLyBldmVuIHdoZW4gdGhlIG92ZXJsYXkgZXh0ZW5kcyBhYm92ZSB0aGUgdmlld3BvcnQuIFdlIG9taXQgbWluSGVpZ2h0IGFuZFxuLy8gZmxleC1lbmQgaGVyZTogdGhleSB3b3VsZCBjcmVhdGUgZW1wdHkgcGFkZGluZyByb3dzIHRoYXQgc2hpZnQgdmlzaWJsZVxuLy8gaXRlbXMgZG93biBpbnRvIHRoZSBwcm9tcHQgYXJlYSB3aGVuIHRoZSBsaXN0IGhhcyBmZXdlciBpdGVtcyB0aGFuIG1heC5cbmZ1bmN0aW9uIFN1Z2dlc3Rpb25zT3ZlcmxheSgpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBkYXRhID0gdXNlUHJvbXB0T3ZlcmxheSgpXG4gIGlmICghZGF0YSB8fCBkYXRhLnN1Z2dlc3Rpb25zLmxlbmd0aCA9PT0gMCkgcmV0dXJuIG51bGxcbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBwb3NpdGlvbj1cImFic29sdXRlXCJcbiAgICAgIGJvdHRvbT1cIjEwMCVcIlxuICAgICAgbGVmdD17MH1cbiAgICAgIHJpZ2h0PXswfVxuICAgICAgcGFkZGluZ1g9ezJ9XG4gICAgICBwYWRkaW5nVG9wPXsxfVxuICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICBvcGFxdWVcbiAgICA+XG4gICAgICA8UHJvbXB0SW5wdXRGb290ZXJTdWdnZXN0aW9uc1xuICAgICAgICBzdWdnZXN0aW9ucz17ZGF0YS5zdWdnZXN0aW9uc31cbiAgICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uPXtkYXRhLnNlbGVjdGVkU3VnZ2VzdGlvbn1cbiAgICAgICAgbWF4Q29sdW1uV2lkdGg9e2RhdGEubWF4Q29sdW1uV2lkdGh9XG4gICAgICAgIG92ZXJsYXlcbiAgICAgIC8+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuLy8gRGlhbG9nIHBvcnRhbGVkIGZyb20gUHJvbXB0SW5wdXQgKEF1dG9Nb2RlT3B0SW5EaWFsb2cpIOKAlCBzYW1lIGNsaXAtZXNjYXBlXG4vLyBwYXR0ZXJuIGFzIFN1Z2dlc3Rpb25zT3ZlcmxheS4gUmVuZGVycyBsYXRlciBpbiB0cmVlIG9yZGVyIHNvIGl0IHBhaW50c1xuLy8gb3ZlciBzdWdnZXN0aW9ucyBpZiBib3RoIGFyZSBldmVyIHVwICh0aGV5IHNob3VsZG4ndCBiZSkuXG5mdW5jdGlvbiBEaWFsb2dPdmVybGF5KCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IG5vZGUgPSB1c2VQcm9tcHRPdmVybGF5RGlhbG9nKClcbiAgaWYgKCFub2RlKSByZXR1cm4gbnVsbFxuICByZXR1cm4gKFxuICAgIDxCb3ggcG9zaXRpb249XCJhYnNvbHV0ZVwiIGJvdHRvbT1cIjEwMCVcIiBsZWZ0PXswfSByaWdodD17MH0gb3BhcXVlPlxuICAgICAge25vZGV9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLE9BQU8sTUFBTSxTQUFTO0FBQzdCLE9BQU9DLEtBQUssSUFDVkMsYUFBYSxFQUNiLEtBQUtDLFNBQVMsRUFDZCxLQUFLQyxTQUFTLEVBQ2RDLFdBQVcsRUFDWEMsU0FBUyxFQUNUQyxlQUFlLEVBQ2ZDLE9BQU8sRUFDUEMsTUFBTSxFQUNOQyxRQUFRLEVBQ1JDLG9CQUFvQixRQUNmLE9BQU87QUFDZCxTQUFTQyxhQUFhLFFBQVEsS0FBSztBQUNuQyxTQUFTQyxZQUFZLFFBQVEsNEJBQTRCO0FBQ3pELFNBQ0VDLHFCQUFxQixFQUNyQkMsZ0JBQWdCLEVBQ2hCQyxzQkFBc0IsUUFDakIsb0NBQW9DO0FBQzNDLFNBQVNDLGVBQWUsUUFBUSw2QkFBNkI7QUFDN0QsT0FBT0MsU0FBUyxJQUFJLEtBQUtDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDaEYsT0FBT0MsU0FBUyxNQUFNLHFCQUFxQjtBQUMzQyxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLGNBQWNDLE9BQU8sUUFBUSxxQkFBcUI7QUFDbEQsU0FBU0MsV0FBVyxFQUFFQyxRQUFRLFFBQVEscUJBQXFCO0FBQzNELFNBQVNDLHNCQUFzQixRQUFRLHdCQUF3QjtBQUMvRCxTQUFTQyxNQUFNLFFBQVEseUJBQXlCO0FBQ2hELFNBQVNDLHlCQUF5QixRQUFRLHdDQUF3QztBQUNsRixPQUFPQyw0QkFBNEIsTUFBTSwrQ0FBK0M7QUFDeEYsY0FBY0MsWUFBWSxRQUFRLHlCQUF5Qjs7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 \ No newline at end of file diff --git a/components/GlobalSearchDialog.tsx b/components/GlobalSearchDialog.tsx new file mode 100644 index 0000000..b4551e2 --- /dev/null +++ b/components/GlobalSearchDialog.tsx @@ -0,0 +1,343 @@ +import { c as _c } from "react/compiler-runtime"; +import { resolve as resolvePath } from 'path'; +import * as React from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { useRegisterOverlay } from '../context/overlayContext.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { Text } from '../ink.js'; +import { logEvent } from '../services/analytics/index.js'; +import { getCwd } from '../utils/cwd.js'; +import { openFileInExternalEditor } from '../utils/editor.js'; +import { truncatePathMiddle, truncateToWidth } from '../utils/format.js'; +import { highlightMatch } from '../utils/highlightMatch.js'; +import { relativePath } from '../utils/permissions/filesystem.js'; +import { readFileInRange } from '../utils/readFileInRange.js'; +import { ripGrepStream } from '../utils/ripgrep.js'; +import { FuzzyPicker } from './design-system/FuzzyPicker.js'; +import { LoadingState } from './design-system/LoadingState.js'; +type Props = { + onDone: () => void; + onInsert: (text: string) => void; +}; +type Match = { + file: string; + line: number; + text: string; +}; +const VISIBLE_RESULTS = 12; +const DEBOUNCE_MS = 100; +const PREVIEW_CONTEXT_LINES = 4; +// rg -m is per-file; we also cap the parsed array to keep memory bounded. +const MAX_MATCHES_PER_FILE = 10; +const MAX_TOTAL_MATCHES = 500; + +/** + * Global Search dialog (ctrl+shift+f / cmd+shift+f). + * Debounced ripgrep search across the workspace. + */ +export function GlobalSearchDialog(t0) { + const $ = _c(40); + const { + onDone, + onInsert + } = t0; + useRegisterOverlay("global-search"); + const { + columns, + rows + } = useTerminalSize(); + const previewOnRight = columns >= 140; + const visibleResults = Math.min(VISIBLE_RESULTS, Math.max(4, rows - 14)); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = []; + $[0] = t1; + } else { + t1 = $[0]; + } + const [matches, setMatches] = useState(t1); + const [truncated, setTruncated] = useState(false); + const [isSearching, setIsSearching] = useState(false); + const [query, setQuery] = useState(""); + const [focused, setFocused] = useState(undefined); + const [preview, setPreview] = useState(null); + const abortRef = useRef(null); + const timeoutRef = useRef(null); + let t2; + let t3; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + abortRef.current?.abort(); + }; + t3 = []; + $[1] = t2; + $[2] = t3; + } else { + t2 = $[1]; + t3 = $[2]; + } + useEffect(t2, t3); + let t4; + let t5; + if ($[3] !== focused) { + t4 = () => { + if (!focused) { + setPreview(null); + return; + } + const controller = new AbortController(); + const absolute = resolvePath(getCwd(), focused.file); + const start = Math.max(0, focused.line - PREVIEW_CONTEXT_LINES - 1); + readFileInRange(absolute, start, PREVIEW_CONTEXT_LINES * 2 + 1, undefined, controller.signal).then(r => { + if (controller.signal.aborted) { + return; + } + setPreview({ + file: focused.file, + line: focused.line, + content: r.content + }); + }).catch(() => { + if (controller.signal.aborted) { + return; + } + setPreview({ + file: focused.file, + line: focused.line, + content: "(preview unavailable)" + }); + }); + return () => controller.abort(); + }; + t5 = [focused]; + $[3] = focused; + $[4] = t4; + $[5] = t5; + } else { + t4 = $[4]; + t5 = $[5]; + } + useEffect(t4, t5); + let t6; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t6 = q => { + setQuery(q); + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + abortRef.current?.abort(); + if (!q.trim()) { + setMatches(_temp); + setIsSearching(false); + setTruncated(false); + return; + } + const controller_0 = new AbortController(); + abortRef.current = controller_0; + setIsSearching(true); + setTruncated(false); + const queryLower = q.toLowerCase(); + setMatches(m_0 => { + const filtered = m_0.filter(match => match.text.toLowerCase().includes(queryLower)); + return filtered.length === m_0.length ? m_0 : filtered; + }); + timeoutRef.current = setTimeout(_temp4, DEBOUNCE_MS, q, controller_0, setMatches, setTruncated, setIsSearching); + }; + $[6] = t6; + } else { + t6 = $[6]; + } + const handleQueryChange = t6; + const listWidth = previewOnRight ? Math.floor((columns - 10) * 0.5) : columns - 8; + const maxPathWidth = Math.max(20, Math.floor(listWidth * 0.4)); + const maxTextWidth = Math.max(20, listWidth - maxPathWidth - 4); + const previewWidth = previewOnRight ? Math.max(40, columns - listWidth - 14) : columns - 6; + let t7; + if ($[7] !== matches.length || $[8] !== onDone) { + t7 = m_3 => { + const opened = openFileInExternalEditor(resolvePath(getCwd(), m_3.file), m_3.line); + logEvent("tengu_global_search_select", { + result_count: matches.length, + opened_editor: opened + }); + onDone(); + }; + $[7] = matches.length; + $[8] = onDone; + $[9] = t7; + } else { + t7 = $[9]; + } + const handleOpen = t7; + let t8; + if ($[10] !== matches.length || $[11] !== onDone || $[12] !== onInsert) { + t8 = (m_4, mention) => { + onInsert(mention ? `@${m_4.file}#L${m_4.line} ` : `${m_4.file}:${m_4.line} `); + logEvent("tengu_global_search_insert", { + result_count: matches.length, + mention + }); + onDone(); + }; + $[10] = matches.length; + $[11] = onDone; + $[12] = onInsert; + $[13] = t8; + } else { + t8 = $[13]; + } + const handleInsert = t8; + const matchLabel = matches.length > 0 ? `${matches.length}${truncated ? "+" : ""} matches${isSearching ? "\u2026" : ""}` : " "; + const t9 = previewOnRight ? "right" : "bottom"; + let t10; + if ($[14] !== handleInsert) { + t10 = { + action: "mention", + handler: m_5 => handleInsert(m_5, true) + }; + $[14] = handleInsert; + $[15] = t10; + } else { + t10 = $[15]; + } + let t11; + if ($[16] !== handleInsert) { + t11 = { + action: "insert path", + handler: m_6 => handleInsert(m_6, false) + }; + $[16] = handleInsert; + $[17] = t11; + } else { + t11 = $[17]; + } + let t12; + if ($[18] !== isSearching) { + t12 = q_0 => isSearching ? "Searching\u2026" : q_0 ? "No matches" : "Type to search\u2026"; + $[18] = isSearching; + $[19] = t12; + } else { + t12 = $[19]; + } + let t13; + if ($[20] !== maxPathWidth || $[21] !== maxTextWidth || $[22] !== query) { + t13 = (m_7, isFocused) => {truncatePathMiddle(m_7.file, maxPathWidth)}:{m_7.line}{" "}{highlightMatch(truncateToWidth(m_7.text.trimStart(), maxTextWidth), query)}; + $[20] = maxPathWidth; + $[21] = maxTextWidth; + $[22] = query; + $[23] = t13; + } else { + t13 = $[23]; + } + let t14; + if ($[24] !== preview || $[25] !== previewWidth || $[26] !== query) { + t14 = m_8 => preview?.file === m_8.file && preview.line === m_8.line ? <>{truncatePathMiddle(m_8.file, previewWidth)}:{m_8.line}{preview.content.split("\n").map((line_0, i) => {highlightMatch(truncateToWidth(line_0, previewWidth), query)})} : ; + $[24] = preview; + $[25] = previewWidth; + $[26] = query; + $[27] = t14; + } else { + t14 = $[27]; + } + let t15; + if ($[28] !== handleOpen || $[29] !== matchLabel || $[30] !== matches || $[31] !== onDone || $[32] !== t10 || $[33] !== t11 || $[34] !== t12 || $[35] !== t13 || $[36] !== t14 || $[37] !== t9 || $[38] !== visibleResults) { + t15 = ; + $[28] = handleOpen; + $[29] = matchLabel; + $[30] = matches; + $[31] = onDone; + $[32] = t10; + $[33] = t11; + $[34] = t12; + $[35] = t13; + $[36] = t14; + $[37] = t9; + $[38] = visibleResults; + $[39] = t15; + } else { + t15 = $[39]; + } + return t15; +} +function _temp4(query_0, controller_1, setMatches_0, setTruncated_0, setIsSearching_0) { + const cwd = getCwd(); + let collected = 0; + ripGrepStream(["-n", "--no-heading", "-i", "-m", String(MAX_MATCHES_PER_FILE), "-F", "-e", query_0], cwd, controller_1.signal, lines => { + if (controller_1.signal.aborted) { + return; + } + const parsed = []; + for (const line of lines) { + const m_1 = parseRipgrepLine(line); + if (!m_1) { + continue; + } + const rel = relativePath(cwd, m_1.file); + parsed.push({ + ...m_1, + file: rel.startsWith("..") ? m_1.file : rel + }); + } + if (!parsed.length) { + return; + } + collected = collected + parsed.length; + collected; + setMatches_0(prev => { + const seen = new Set(prev.map(matchKey)); + const fresh = parsed.filter(p => !seen.has(matchKey(p))); + if (!fresh.length) { + return prev; + } + const next = prev.concat(fresh); + return next.length > MAX_TOTAL_MATCHES ? next.slice(0, MAX_TOTAL_MATCHES) : next; + }); + if (collected >= MAX_TOTAL_MATCHES) { + controller_1.abort(); + setTruncated_0(true); + setIsSearching_0(false); + } + }).catch(_temp2).finally(() => { + if (controller_1.signal.aborted) { + return; + } + if (collected === 0) { + setMatches_0(_temp3); + } + setIsSearching_0(false); + }); +} +function _temp3(m_2) { + return m_2.length ? [] : m_2; +} +function _temp2() {} +function _temp(m) { + return m.length ? [] : m; +} +function matchKey(m: Match): string { + return `${m.file}:${m.line}`; +} + +/** + * Parse a ripgrep -n --no-heading output line: "path:line:text". + * Windows paths may contain a drive letter ("C:\..."), so a simple split on + * the first colon would mangle the path — use a regex that captures up to + * the first :: instead. + * @internal exported for testing + */ +export function parseRipgrepLine(line: string): Match | null { + const m = /^(.*?):(\d+):(.*)$/.exec(line); + if (!m) return null; + const [, file, lineStr, text] = m; + const lineNum = Number(lineStr); + if (!file || !Number.isFinite(lineNum)) return null; + return { + file, + line: lineNum, + text: text ?? '' + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyZXNvbHZlIiwicmVzb2x2ZVBhdGgiLCJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZVN0YXRlIiwidXNlUmVnaXN0ZXJPdmVybGF5IiwidXNlVGVybWluYWxTaXplIiwiVGV4dCIsImxvZ0V2ZW50IiwiZ2V0Q3dkIiwib3BlbkZpbGVJbkV4dGVybmFsRWRpdG9yIiwidHJ1bmNhdGVQYXRoTWlkZGxlIiwidHJ1bmNhdGVUb1dpZHRoIiwiaGlnaGxpZ2h0TWF0Y2giLCJyZWxhdGl2ZVBhdGgiLCJyZWFkRmlsZUluUmFuZ2UiLCJyaXBHcmVwU3RyZWFtIiwiRnV6enlQaWNrZXIiLCJMb2FkaW5nU3RhdGUiLCJQcm9wcyIsIm9uRG9uZSIsIm9uSW5zZXJ0IiwidGV4dCIsIk1hdGNoIiwiZmlsZSIsImxpbmUiLCJWSVNJQkxFX1JFU1VMVFMiLCJERUJPVU5DRV9NUyIsIlBSRVZJRVdfQ09OVEVYVF9MSU5FUyIsIk1BWF9NQVRDSEVTX1BFUl9GSUxFIiwiTUFYX1RPVEFMX01BVENIRVMiLCJHbG9iYWxTZWFyY2hEaWFsb2ciLCJ0MCIsIiQiLCJfYyIsImNvbHVtbnMiLCJyb3dzIiwicHJldmlld09uUmlnaHQiLCJ2aXNpYmxlUmVzdWx0cyIsIk1hdGgiLCJtaW4iLCJtYXgiLCJ0MSIsIlN5bWJvbCIsImZvciIsIm1hdGNoZXMiLCJzZXRNYXRjaGVzIiwidHJ1bmNhdGVkIiwic2V0VHJ1bmNhdGVkIiwiaXNTZWFyY2hpbmciLCJzZXRJc1NlYXJjaGluZyIsInF1ZXJ5Iiwic2V0UXVlcnkiLCJmb2N1c2VkIiwic2V0Rm9jdXNlZCIsInVuZGVmaW5lZCIsInByZXZpZXciLCJzZXRQcmV2aWV3IiwiYWJvcnRSZWYiLCJ0aW1lb3V0UmVmIiwidDIiLCJ0MyIsImN1cnJlbnQiLCJjbGVhclRpbWVvdXQiLCJhYm9ydCIsInQ0IiwidDUiLCJjb250cm9sbGVyIiwiQWJvcnRDb250cm9sbGVyIiwiYWJzb2x1dGUiLCJzdGFydCIsInNpZ25hbCIsInRoZW4iLCJyIiwiYWJvcnRlZCIsImNvbnRlbnQiLCJjYXRjaCIsInQ2IiwicSIsInRyaW0iLCJfdGVtcCIsImNvbnRyb2xsZXJfMCIsInF1ZXJ5TG93ZXIiLCJ0b0xvd2VyQ2FzZSIsIm1fMCIsImZpbHRlcmVkIiwibSIsImZpbHRlciIsIm1hdGNoIiwiaW5jbHVkZXMiLCJsZW5ndGgiLCJzZXRUaW1lb3V0IiwiX3RlbXA0IiwiaGFuZGxlUXVlcnlDaGFuZ2UiLCJsaXN0V2lkdGgiLCJmbG9vciIsIm1heFBhdGhXaWR0aCIsIm1heFRleHRXaWR0aCIsInByZXZpZXdXaWR0aCIsInQ3IiwibV8zIiwib3BlbmVkIiwicmVzdWx0X2NvdW50Iiwib3BlbmVkX2VkaXRvciIsImhhbmRsZU9wZW4iLCJ0OCIsIm1fNCIsIm1lbnRpb24iLCJoYW5kbGVJbnNlcnQiLCJtYXRjaExhYmVsIiwidDkiLCJ0MTAiLCJhY3Rpb24iLCJoYW5kbGVyIiwibV81IiwidDExIiwibV82IiwidDEyIiwicV8wIiwidDEzIiwibV83IiwiaXNGb2N1c2VkIiwidHJpbVN0YXJ0IiwidDE0IiwibV84Iiwic3BsaXQiLCJtYXAiLCJsaW5lXzAiLCJpIiwidDE1IiwibWF0Y2hLZXkiLCJxdWVyeV8wIiwiY29udHJvbGxlcl8xIiwic2V0TWF0Y2hlc18wIiwic2V0VHJ1bmNhdGVkXzAiLCJzZXRJc1NlYXJjaGluZ18wIiwiY3dkIiwiY29sbGVjdGVkIiwiU3RyaW5nIiwibGluZXMiLCJwYXJzZWQiLCJtXzEiLCJwYXJzZVJpcGdyZXBMaW5lIiwicmVsIiwicHVzaCIsInN0YXJ0c1dpdGgiLCJwcmV2Iiwic2VlbiIsIlNldCIsImZyZXNoIiwicCIsImhhcyIsIm5leHQiLCJjb25jYXQiLCJzbGljZSIsIl90ZW1wMiIsImZpbmFsbHkiLCJfdGVtcDMiLCJtXzIiLCJleGVjIiwibGluZVN0ciIsImxpbmVOdW0iLCJOdW1iZXIiLCJpc0Zpbml0ZSJdLCJzb3VyY2VzIjpbIkdsb2JhbFNlYXJjaERpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgcmVzb2x2ZSBhcyByZXNvbHZlUGF0aCB9IGZyb20gJ3BhdGgnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlUmVnaXN0ZXJPdmVybGF5IH0gZnJvbSAnLi4vY29udGV4dC9vdmVybGF5Q29udGV4dC5qcydcbmltcG9ydCB7IHVzZVRlcm1pbmFsU2l6ZSB9IGZyb20gJy4uL2hvb2tzL3VzZVRlcm1pbmFsU2l6ZS5qcydcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBsb2dFdmVudCB9IGZyb20gJy4uL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IGdldEN3ZCB9IGZyb20gJy4uL3V0aWxzL2N3ZC5qcydcbmltcG9ydCB7IG9wZW5GaWxlSW5FeHRlcm5hbEVkaXRvciB9IGZyb20gJy4uL3V0aWxzL2VkaXRvci5qcydcbmltcG9ydCB7IHRydW5jYXRlUGF0aE1pZGRsZSwgdHJ1bmNhdGVUb1dpZHRoIH0gZnJvbSAnLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgaGlnaGxpZ2h0TWF0Y2ggfSBmcm9tICcuLi91dGlscy9oaWdobGlnaHRNYXRjaC5qcydcbmltcG9ydCB7IHJlbGF0aXZlUGF0aCB9IGZyb20gJy4uL3V0aWxzL3Blcm1pc3Npb25zL2ZpbGVzeXN0ZW0uanMnXG5pbXBvcnQgeyByZWFkRmlsZUluUmFuZ2UgfSBmcm9tICcuLi91dGlscy9yZWFkRmlsZUluUmFuZ2UuanMnXG5pbXBvcnQgeyByaXBHcmVwU3RyZWFtIH0gZnJvbSAnLi4vdXRpbHMvcmlwZ3JlcC5qcydcbmltcG9ydCB7IEZ1enp5UGlja2VyIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0Z1enp5UGlja2VyLmpzJ1xuaW1wb3J0IHsgTG9hZGluZ1N0YXRlIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0xvYWRpbmdTdGF0ZS5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgb25Eb25lOiAoKSA9PiB2b2lkXG4gIG9uSW5zZXJ0OiAodGV4dDogc3RyaW5nKSA9PiB2b2lkXG59XG5cbnR5cGUgTWF0Y2ggPSB7XG4gIGZpbGU6IHN0cmluZ1xuICBsaW5lOiBudW1iZXJcbiAgdGV4dDogc3RyaW5nXG59XG5cbmNvbnN0IFZJU0lCTEVfUkVTVUxUUyA9IDEyXG5jb25zdCBERUJPVU5DRV9NUyA9IDEwMFxuY29uc3QgUFJFVklFV19DT05URVhUX0xJTkVTID0gNFxuLy8gcmcgLW0gaXMgcGVyLWZpbGU7IHdlIGFsc28gY2FwIHRoZSBwYXJzZWQgYXJyYXkgdG8ga2VlcCBtZW1vcnkgYm91bmRlZC5cbmNvbnN0IE1BWF9NQVRDSEVTX1BFUl9GSUxFID0gMTBcbmNvbnN0IE1BWF9UT1RBTF9NQVRDSEVTID0gNTAwXG5cbi8qKlxuICogR2xvYmFsIFNlYXJjaCBkaWFsb2cgKGN0cmwrc2hpZnQrZiAvIGNtZCtzaGlmdCtmKS5cbiAqIERlYm91bmNlZCByaXBncmVwIHNlYXJjaCBhY3Jvc3MgdGhlIHdvcmtzcGFjZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEdsb2JhbFNlYXJjaERpYWxvZyh7XG4gIG9uRG9uZSxcbiAgb25JbnNlcnQsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHVzZVJlZ2lzdGVyT3ZlcmxheSgnZ2xvYmFsLXNlYXJjaCcpXG4gIGNvbnN0IHsgY29sdW1ucywgcm93cyB9ID0gdXNlVGVybWluYWxTaXplKClcbiAgY29uc3QgcHJldmlld09uUmlnaHQgPSBjb2x1bW5zID49IDE0MFxuICAvLyBDaHJvbWUgKHRpdGxlICsgc2VhcmNoICsgbWF0Y2hMYWJlbCArIGhpbnRzICsgcGFuZSBib3JkZXIgKyBnYXBzKSBlYXRzXG4gIC8vIH4xNCByb3dzLiBTaHJpbmsgdGhlIGxpc3Qgb24gc2hvcnQgdGVybWluYWxzIHNvIHRoZSBkaWFsb2cgZG9lc24ndCBjbGlwLlxuICBjb25zdCB2aXNpYmxlUmVzdWx0cyA9IE1hdGgubWluKFZJU0lCTEVfUkVTVUxUUywgTWF0aC5tYXgoNCwgcm93cyAtIDE0KSlcblxuICBjb25zdCBbbWF0Y2hlcywgc2V0TWF0Y2hlc10gPSB1c2VTdGF0ZTxNYXRjaFtdPihbXSlcbiAgY29uc3QgW3RydW5jYXRlZCwgc2V0VHJ1bmNhdGVkXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbaXNTZWFyY2hpbmcsIHNldElzU2VhcmNoaW5nXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbcXVlcnksIHNldFF1ZXJ5XSA9IHVzZVN0YXRlKCcnKVxuICBjb25zdCBbZm9jdXNlZCwgc2V0Rm9jdXNlZF0gPSB1c2VTdGF0ZTxNYXRjaCB8IHVuZGVmaW5lZD4odW5kZWZpbmVkKVxuICBjb25zdCBbcHJldmlldywgc2V0UHJldmlld10gPSB1c2VTdGF0ZTx7XG4gICAgZmlsZTogc3RyaW5nXG4gICAgbGluZTogbnVtYmVyXG4gICAgY29udGVudDogc3RyaW5nXG4gIH0gfCBudWxsPihudWxsKVxuICBjb25zdCBhYm9ydFJlZiA9IHVzZVJlZjxBYm9ydENvbnRyb2xsZXIgfCBudWxsPihudWxsKVxuICBjb25zdCB0aW1lb3V0UmVmID0gdXNlUmVmPFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgbnVsbD4obnVsbClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBpZiAodGltZW91dFJlZi5jdXJyZW50KSBjbGVhclRpbWVvdXQodGltZW91dFJlZi5jdXJyZW50KVxuICAgICAgYWJvcnRSZWYuY3VycmVudD8uYWJvcnQoKVxuICAgIH1cbiAgfSwgW10pXG5cbiAgLy8gTG9hZCBjb250ZXh0IGxpbmVzIGFyb3VuZCB0aGUgZm9jdXNlZCBtYXRjaC4gQWJvcnRDb250cm9sbGVyIHByZXZlbnRzXG4gIC8vIGhvbGRpbmcg4oaTIGZyb20gcGlsaW5nIHVwIHJlYWRzLlxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghZm9jdXNlZCkge1xuICAgICAgc2V0UHJldmlldyhudWxsKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGNvbnN0IGNvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKClcbiAgICBjb25zdCBhYnNvbHV0ZSA9IHJlc29sdmVQYXRoKGdldEN3ZCgpLCBmb2N1c2VkLmZpbGUpXG4gICAgY29uc3Qgc3RhcnQgPSBNYXRoLm1heCgwLCBmb2N1c2VkLmxpbmUgLSBQUkVWSUVXX0NPTlRFWFRfTElORVMgLSAxKVxuICAgIHZvaWQgcmVhZEZpbGVJblJhbmdlKFxuICAgICAgYWJzb2x1dGUsXG4gICAgICBzdGFydCxcbiAgICAgIFBSRVZJRVdfQ09OVEVYVF9MSU5FUyAqIDIgKyAxLFxuICAgICAgdW5kZWZpbmVkLFxuICAgICAgY29udHJvbGxlci5zaWduYWwsXG4gICAgKVxuICAgICAgLnRoZW4ociA9PiB7XG4gICAgICAgIGlmIChjb250cm9sbGVyLnNpZ25hbC5hYm9ydGVkKSByZXR1cm5cbiAgICAgICAgc2V0UHJldmlldyh7XG4gICAgICAgICAgZmlsZTogZm9jdXNlZC5maWxlLFxuICAgICAgICAgIGxpbmU6IGZvY3VzZWQubGluZSxcbiAgICAgICAgICBjb250ZW50OiByLmNvbnRlbnQsXG4gICAgICAgIH0pXG4gICAgICB9KVxuICAgICAgLmNhdGNoKCgpID0+IHtcbiAgICAgICAgaWYgKGNvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHJldHVyblxuICAgICAgICBzZXRQcmV2aWV3KHtcbiAgICAgICAgICBmaWxlOiBmb2N1c2VkLmZpbGUsXG4gICAgICAgICAgbGluZTogZm9jdXNlZC5saW5lLFxuICAgICAgICAgIGNvbnRlbnQ6ICcocHJldmlldyB1bmF2YWlsYWJsZSknLFxuICAgICAgICB9KVxuICAgICAgfSlcbiAgICByZXR1cm4gKCkgPT4gY29udHJvbGxlci5hYm9ydCgpXG4gIH0sIFtmb2N1c2VkXSlcblxuICBjb25zdCBoYW5kbGVRdWVyeUNoYW5nZSA9IChxOiBzdHJpbmcpID0+IHtcbiAgICBzZXRRdWVyeShxKVxuICAgIGlmICh0aW1lb3V0UmVmLmN1cnJlbnQpIGNsZWFyVGltZW91dCh0aW1lb3V0UmVmLmN1cnJlbnQpXG4gICAgYWJvcnRSZWYuY3VycmVudD8uYWJvcnQoKVxuXG4gICAgaWYgKCFxLnRyaW0oKSkge1xuICAgICAgc2V0TWF0Y2hlcyhtID0+IChtLmxlbmd0aCA/IFtdIDogbSkpXG4gICAgICBzZXRJc1NlYXJjaGluZyhmYWxzZSlcbiAgICAgIHNldFRydW5jYXRlZChmYWxzZSlcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpXG4gICAgYWJvcnRSZWYuY3VycmVudCA9IGNvbnRyb2xsZXJcbiAgICBzZXRJc1NlYXJjaGluZyh0cnVlKVxuICAgIHNldFRydW5jYXRlZChmYWxzZSlcbiAgICAvLyBDbGllbnQtZmlsdGVyIGV4aXN0aW5nIHJlc3VsdHMgd2hpbGUgcmcgd2Fsa3Mg4oCUIGtlZXBzIHNvbWV0aGluZyBvblxuICAgIC8vIHNjcmVlbiBpbnN0ZWFkIG9mIGZsYXNoaW5nIGJsYW5rLiByZyByZXN1bHRzIGFyZSBtZXJnZWQgaW4gKGRlZHVwZWQgYnlcbiAgICAvLyBmaWxlOmxpbmUpIHJhdGhlciB0aGFuIHJlcGxhY2VkLCBzbyB0aGUgY291bnQgaXMgbW9ub3RvbmljIHdpdGhpbiBhXG4gICAgLy8gcXVlcnk6IGl0IG9ubHkgZ3Jvd3MgYXMgcmcgc3RyZWFtcywgbmV2ZXIgZGlwcyB0byB0aGUgZmlyc3QgY2h1bmsnc1xuICAgIC8vIHNpemUuIE5hcnJvd2luZyAobmV3IHF1ZXJ5IGV4dGVuZHMgb2xkKTogZmlsdGVyIGlzIGV4YWN0IOKAlCBhbnkgbGluZVxuICAgIC8vIHRoYXQgbWF0Y2hlZCB0aGUgb2xkIC1GIC1pIGxpdGVyYWwgY29udGFpbnMgdGhlIG5ldyBvbmUgaWZmIGl0cyB0ZXh0XG4gICAgLy8gaW5jbHVkZXMgdGhlIG5ldyBxdWVyeSBsb3dlcmVkLiBOb24tbmFycm93aW5nIChicm9hZGVuaW5nL2RpZmZlcmVudCk6XG4gICAgLy8gZmlsdGVyIGlzIGJlc3QtZWZmb3J0IOKAlCBtYXkgYnJpZWZseSBzaG93IGEgc3Vic2V0IHVudGlsIHJnIGZpbGxzIGluXG4gICAgLy8gdGhlIHJlc3QuXG4gICAgY29uc3QgcXVlcnlMb3dlciA9IHEudG9Mb3dlckNhc2UoKVxuICAgIHNldE1hdGNoZXMobSA9PiB7XG4gICAgICBjb25zdCBmaWx0ZXJlZCA9IG0uZmlsdGVyKG1hdGNoID0+XG4gICAgICAgIG1hdGNoLnRleHQudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhxdWVyeUxvd2VyKSxcbiAgICAgIClcbiAgICAgIHJldHVybiBmaWx0ZXJlZC5sZW5ndGggPT09IG0ubGVuZ3RoID8gbSA6IGZpbHRlcmVkXG4gICAgfSlcblxuICAgIHRpbWVvdXRSZWYuY3VycmVudCA9IHNldFRpbWVvdXQoXG4gICAgICAocXVlcnksIGNvbnRyb2xsZXIsIHNldE1hdGNoZXMsIHNldFRydW5jYXRlZCwgc2V0SXNTZWFyY2hpbmcpID0+IHtcbiAgICAgICAgLy8gcmlwZ3JlcCBvdXRwdXRzIGFic29sdXRlIHBhdGhzIHdoZW4gZ2l2ZW4gYW4gYWJzb2x1dGUgdGFyZ2V0LCBzb1xuICAgICAgICAvLyByZWxhdGl2aXplIGFnYWluc3QgY3dkIHRvIHByZXNlcnZlIGRpcmVjdG9yeSBjb250ZXh0IGluIHRoZSB0cnVuY2F0ZWRcbiAgICAgICAgLy8gZGlzcGxheSAob3RoZXJ3aXNlIHRoZSBjd2QgcHJlZml4IGVhdHMgdGhlIHdpZHRoIGJ1ZGdldCkuXG4gICAgICAgIC8vIHJlbGF0aXZlUGF0aCgpIHJldHVybnMgUE9TSVgtbm9ybWFsaXplZCBvdXRwdXQgc28gdHJ1bmNhdGVQYXRoTWlkZGxlXG4gICAgICAgIC8vICh3aGljaCB1c2VzIGxhc3RJbmRleE9mKCcvJykpIHdvcmtzIG9uIFdpbmRvd3MgdG9vLlxuICAgICAgICBjb25zdCBjd2QgPSBnZXRDd2QoKVxuICAgICAgICBsZXQgY29sbGVjdGVkID0gMFxuICAgICAgICB2b2lkIHJpcEdyZXBTdHJlYW0oXG4gICAgICAgICAgLy8gLWUgZGlzYW1iaWd1YXRlcyBwYXR0ZXJuIGZyb20gb3B0aW9ucyB3aGVuIHRoZSBxdWVyeSBzdGFydHMgd2l0aCAnLSdcbiAgICAgICAgICAvLyAoZS5nLiBzZWFyY2hpbmcgZm9yIFwiLS12ZXJib3NlXCIgb3IgXCItcmZcIikuIFNlZSBHcmVwVG9vbC50cyBmb3IgdGhlXG4gICAgICAgICAgLy8gc2FtZSBwcmVjYXV0aW9uLlxuICAgICAgICAgIFtcbiAgICAgICAgICAgICctbicsXG4gICAgICAgICAgICAnLS1uby1oZWFkaW5nJyxcbiAgICAgICAgICAgICctaScsXG4gICAgICAgICAgICAnLW0nLFxuICAgICAgICAgICAgU3RyaW5nKE1BWF9NQVRDSEVTX1BFUl9GSUxFKSxcbiAgICAgICAgICAgICctRicsXG4gICAgICAgICAgICAnLWUnLFxuICAgICAgICAgICAgcXVlcnksXG4gICAgICAgICAgXSxcbiAgICAgICAgICBjd2QsXG4gICAgICAgICAgY29udHJvbGxlci5zaWduYWwsXG4gICAgICAgICAgbGluZXMgPT4ge1xuICAgICAgICAgICAgaWYgKGNvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHJldHVyblxuICAgICAgICAgICAgY29uc3QgcGFyc2VkOiBNYXRjaFtdID0gW11cbiAgICAgICAgICAgIGZvciAoY29uc3QgbGluZSBvZiBsaW5lcykge1xuICAgICAgICAgICAgICBjb25zdCBtID0gcGFyc2VSaXBncmVwTGluZShsaW5lKVxuICAgICAgICAgICAgICBpZiAoIW0pIGNvbnRpbnVlXG4gICAgICAgICAgICAgIGNvbnN0IHJlbCA9IHJlbGF0aXZlUGF0aChjd2QsIG0uZmlsZSlcbiAgICAgICAgICAgICAgcGFyc2VkLnB1c2goeyAuLi5tLCBmaWxlOiByZWwuc3RhcnRzV2l0aCgnLi4nKSA/IG0uZmlsZSA6IHJlbCB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKCFwYXJzZWQubGVuZ3RoKSByZXR1cm5cbiAgICAgICAgICAgIGNvbGxlY3RlZCArPSBwYXJzZWQubGVuZ3RoXG4gICAgICAgICAgICBzZXRNYXRjaGVzKHByZXYgPT4ge1xuICAgICAgICAgICAgICAvLyBBcHBlbmQrZGVkdXBlIGluc3RlYWQgb2YgcmVwbGFjZTogcHJldiBtYXkgaG9sZCBjbGllbnQtXG4gICAgICAgICAgICAgIC8vIGZpbHRlcmVkIHJlc3VsdHMgdGhhdCBhcmUgdmFsaWQgbWF0Y2hlcyBmb3IgdGhpcyBxdWVyeS5cbiAgICAgICAgICAgICAgLy8gUmVwbGFjaW5nIHdvdWxkIGRyb3AgdGhlIGNvdW50IHRvIHRoaXMgY2h1bmsncyBzaXplIHRoZW5cbiAgICAgICAgICAgICAgLy8gZ3JvdyBpdCBiYWNrIOKAlCB2aXNpYmxlIGFzIGEgZmxpY2tlci5cbiAgICAgICAgICAgICAgY29uc3Qgc2VlbiA9IG5ldyBTZXQocHJldi5tYXAobWF0Y2hLZXkpKVxuICAgICAgICAgICAgICBjb25zdCBmcmVzaCA9IHBhcnNlZC5maWx0ZXIocCA9PiAhc2Vlbi5oYXMobWF0Y2hLZXkocCkpKVxuICAgICAgICAgICAgICBpZiAoIWZyZXNoLmxlbmd0aCkgcmV0dXJuIHByZXZcbiAgICAgICAgICAgICAgY29uc3QgbmV4dCA9IHByZXYuY29uY2F0KGZyZXNoKVxuICAgICAgICAgICAgICByZXR1cm4gbmV4dC5sZW5ndGggPiBNQVhfVE9UQUxfTUFUQ0hFU1xuICAgICAgICAgICAgICAgID8gbmV4dC5zbGljZSgwLCBNQVhfVE9UQUxfTUFUQ0hFUylcbiAgICAgICAgICAgICAgICA6IG5leHRcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICBpZiAoY29sbGVjdGVkID49IE1BWF9UT1RBTF9NQVRDSEVTKSB7XG4gICAgICAgICAgICAgIGNvbnRyb2xsZXIuYWJvcnQoKVxuICAgICAgICAgICAgICBzZXRUcnVuY2F0ZWQodHJ1ZSlcbiAgICAgICAgICAgICAgc2V0SXNTZWFyY2hpbmcoZmFsc2UpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSxcbiAgICAgICAgKVxuICAgICAgICAgIC5jYXRjaCgoKSA9PiB7fSlcbiAgICAgICAgICAvLyBTdHJlYW0gY2xvc2VkIHdpdGggemVybyBjaHVua3Mg4oCUIGNsZWFyIHN0YWxlIHJlc3VsdHMgc29cbiAgICAgICAgICAvLyBcIk5vIG1hdGNoZXNcIiByZW5kZXJzIGluc3RlYWQgb2YgdGhlIHByZXZpb3VzIHF1ZXJ5J3MgbGlzdC5cbiAgICAgICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgICAgICBpZiAoY29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkgcmV0dXJuXG4gICAgICAgICAgICBpZiAoY29sbGVjdGVkID09PSAwKSBzZXRNYXRjaGVzKG0gPT4gKG0ubGVuZ3RoID8gW10gOiBtKSlcbiAgICAgICAgICAgIHNldElzU2VhcmNoaW5nKGZhbHNlKVxuICAgICAgICAgIH0pXG4gICAgICB9LFxuICAgICAgREVCT1VOQ0VfTVMsXG4gICAgICBxLFxuICAgICAgY29udHJvbGxlcixcbiAgICAgIHNldE1hdGNoZXMsXG4gICAgICBzZXRUcnVuY2F0ZWQsXG4gICAgICBzZXRJc1NlYXJjaGluZyxcbiAgICApXG4gIH1cblxuICBjb25zdCBsaXN0V2lkdGggPSBwcmV2aWV3T25SaWdodFxuICAgID8gTWF0aC5mbG9vcigoY29sdW1ucyAtIDEwKSAqIDAuNSlcbiAgICA6IGNvbHVtbnMgLSA4XG4gIGNvbnN0IG1heFBhdGhXaWR0aCA9IE1hdGgubWF4KDIwLCBNYXRoLmZsb29yKGxpc3RXaWR0aCAqIDAuNCkpXG4gIGNvbnN0IG1heFRleHRXaWR0aCA9IE1hdGgubWF4KDIwLCBsaXN0V2lkdGggLSBtYXhQYXRoV2lkdGggLSA0KVxuICBjb25zdCBwcmV2aWV3V2lkdGggPSBwcmV2aWV3T25SaWdodFxuICAgID8gTWF0aC5tYXgoNDAsIGNvbHVtbnMgLSBsaXN0V2lkdGggLSAxNClcbiAgICA6IGNvbHVtbnMgLSA2XG5cbiAgY29uc3QgaGFuZGxlT3BlbiA9IChtOiBNYXRjaCkgPT4ge1xuICAgIGNvbnN0IG9wZW5lZCA9IG9wZW5GaWxlSW5FeHRlcm5hbEVkaXRvcihcbiAgICAgIHJlc29sdmVQYXRoKGdldEN3ZCgpLCBtLmZpbGUpLFxuICAgICAgbS5saW5lLFxuICAgIClcbiAgICBsb2dFdmVudCgndGVuZ3VfZ2xvYmFsX3NlYXJjaF9zZWxlY3QnLCB7XG4gICAgICByZXN1bHRfY291bnQ6IG1hdGNoZXMubGVuZ3RoLFxuICAgICAgb3BlbmVkX2VkaXRvcjogb3BlbmVkLFxuICAgIH0pXG4gICAgb25Eb25lKClcbiAgfVxuXG4gIGNvbnN0IGhhbmRsZUluc2VydCA9IChtOiBNYXRjaCwgbWVudGlvbjogYm9vbGVhbikgPT4ge1xuICAgIG9uSW5zZXJ0KG1lbnRpb24gPyBgQCR7bS5maWxlfSNMJHttLmxpbmV9IGAgOiBgJHttLmZpbGV9OiR7bS5saW5lfSBgKVxuICAgIGxvZ0V2ZW50KCd0ZW5ndV9nbG9iYWxfc2VhcmNoX2luc2VydCcsIHtcbiAgICAgIHJlc3VsdF9jb3VudDogbWF0Y2hlcy5sZW5ndGgsXG4gICAgICBtZW50aW9uLFxuICAgIH0pXG4gICAgb25Eb25lKClcbiAgfVxuXG4gIC8vIEFsd2F5cyBwYXNzIGEgbm9uLWVtcHR5IHN0cmluZyBzbyB0aGUgbGluZSBpcyByZXNlcnZlZCDigJQgcHJldmVudHMgdGhlXG4gIC8vIHNlYXJjaEJveCBmcm9tIGJvdW5jaW5nIHdoZW4gdGhlIGNvdW50IGFwcGVhcnMvZGlzYXBwZWFycy5cbiAgY29uc3QgbWF0Y2hMYWJlbCA9XG4gICAgbWF0Y2hlcy5sZW5ndGggPiAwXG4gICAgICA/IGAke21hdGNoZXMubGVuZ3RofSR7dHJ1bmNhdGVkID8gJysnIDogJyd9IG1hdGNoZXMke2lzU2VhcmNoaW5nID8gJ+KApicgOiAnJ31gXG4gICAgICA6ICcgJ1xuXG4gIHJldHVybiAoXG4gICAgPEZ1enp5UGlja2VyXG4gICAgICB0aXRsZT1cIkdsb2JhbCBTZWFyY2hcIlxuICAgICAgcGxhY2Vob2xkZXI9XCJUeXBlIHRvIHNlYXJjaOKAplwiXG4gICAgICBpdGVtcz17bWF0Y2hlc31cbiAgICAgIGdldEtleT17bWF0Y2hLZXl9XG4gICAgICB2aXNpYmxlQ291bnQ9e3Zpc2libGVSZXN1bHRzfVxuICAgICAgZGlyZWN0aW9uPVwidXBcIlxuICAgICAgcHJldmlld1Bvc2l0aW9uPXtwcmV2aWV3T25SaWdodCA/ICdyaWdodCcgOiAnYm90dG9tJ31cbiAgICAgIG9uUXVlcnlDaGFuZ2U9e2hhbmRsZVF1ZXJ5Q2hhbmdlfVxuICAgICAgb25Gb2N1cz17c2V0Rm9jdXNlZH1cbiAgICAgIG9uU2VsZWN0PXtoYW5kbGVPcGVufVxuICAgICAgb25UYWI9e3sgYWN0aW9uOiAnbWVudGlvbicsIGhhbmRsZXI6IG0gPT4gaGFuZGxlSW5zZXJ0KG0sIHRydWUpIH19XG4gICAgICBvblNoaWZ0VGFiPXt7XG4gICAgICAgIGFjdGlvbjogJ2luc2VydCBwYXRoJyxcbiAgICAgICAgaGFuZGxlcjogbSA9PiBoYW5kbGVJbnNlcnQobSwgZmFsc2UpLFxuICAgICAgfX1cbiAgICAgIG9uQ2FuY2VsPXtvbkRvbmV9XG4gICAgICBlbXB0eU1lc3NhZ2U9e3EgPT5cbiAgICAgICAgaXNTZWFyY2hpbmcgPyAnU2VhcmNoaW5n4oCmJyA6IHEgPyAnTm8gbWF0Y2hlcycgOiAnVHlwZSB0byBzZWFyY2jigKYnXG4gICAgICB9XG4gICAgICBtYXRjaExhYmVsPXttYXRjaExhYmVsfVxuICAgICAgc2VsZWN0QWN0aW9uPVwib3BlbiBpbiBlZGl0b3JcIlxuICAgICAgcmVuZGVySXRlbT17KG0sIGlzRm9jdXNlZCkgPT4gKFxuICAgICAgICA8VGV4dCBjb2xvcj17aXNGb2N1c2VkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHt0cnVuY2F0ZVBhdGhNaWRkbGUobS5maWxlLCBtYXhQYXRoV2lkdGgpfTp7bS5saW5lfVxuICAgICAgICAgIDwvVGV4dD57JyAnfVxuICAgICAgICAgIHtoaWdobGlnaHRNYXRjaChcbiAgICAgICAgICAgIHRydW5jYXRlVG9XaWR0aChtLnRleHQudHJpbVN0YXJ0KCksIG1heFRleHRXaWR0aCksXG4gICAgICAgICAgICBxdWVyeSxcbiAgICAgICAgICApfVxuICAgICAgICA8L1RleHQ+XG4gICAgICApfVxuICAgICAgcmVuZGVyUHJldmlldz17bSA9PlxuICAgICAgICBwcmV2aWV3Py5maWxlID09PSBtLmZpbGUgJiYgcHJldmlldy5saW5lID09PSBtLmxpbmUgPyAoXG4gICAgICAgICAgPD5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICB7dHJ1bmNhdGVQYXRoTWlkZGxlKG0uZmlsZSwgcHJldmlld1dpZHRoKX06e20ubGluZX1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIHtwcmV2aWV3LmNvbnRlbnQuc3BsaXQoJ1xcbicpLm1hcCgobGluZSwgaSkgPT4gKFxuICAgICAgICAgICAgICA8VGV4dCBrZXk9e2l9PlxuICAgICAgICAgICAgICAgIHtoaWdobGlnaHRNYXRjaCh0cnVuY2F0ZVRvV2lkdGgobGluZSwgcHJldmlld1dpZHRoKSwgcXVlcnkpfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICApKX1cbiAgICAgICAgICA8Lz5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8TG9hZGluZ1N0YXRlIG1lc3NhZ2U9XCJMb2FkaW5n4oCmXCIgZGltQ29sb3IgLz5cbiAgICAgICAgKVxuICAgICAgfVxuICAgIC8+XG4gIClcbn1cblxuZnVuY3Rpb24gbWF0Y2hLZXkobTogTWF0Y2gpOiBzdHJpbmcge1xuICByZXR1cm4gYCR7bS5maWxlfToke20ubGluZX1gXG59XG5cbi8qKlxuICogUGFyc2UgYSByaXBncmVwIC1uIC0tbm8taGVhZGluZyBvdXRwdXQgbGluZTogXCJwYXRoOmxpbmU6dGV4dFwiLlxuICogV2luZG93cyBwYXRocyBtYXkgY29udGFpbiBhIGRyaXZlIGxldHRlciAoXCJDOlxcLi4uXCIpLCBzbyBhIHNpbXBsZSBzcGxpdCBvblxuICogdGhlIGZpcnN0IGNvbG9uIHdvdWxkIG1hbmdsZSB0aGUgcGF0aCDigJQgdXNlIGEgcmVnZXggdGhhdCBjYXB0dXJlcyB1cCB0b1xuICogdGhlIGZpcnN0IDo8ZGlnaXRzPjogaW5zdGVhZC5cbiAqIEBpbnRlcm5hbCBleHBvcnRlZCBmb3IgdGVzdGluZ1xuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VSaXBncmVwTGluZShsaW5lOiBzdHJpbmcpOiBNYXRjaCB8IG51bGwge1xuICBjb25zdCBtID0gL14oLio/KTooXFxkKyk6KC4qKSQvLmV4ZWMobGluZSlcbiAgaWYgKCFtKSByZXR1cm4gbnVsbFxuICBjb25zdCBbLCBmaWxlLCBsaW5lU3RyLCB0ZXh0XSA9IG1cbiAgY29uc3QgbGluZU51bSA9IE51bWJlcihsaW5lU3RyKVxuICBpZiAoIWZpbGUgfHwgIU51bWJlci5pc0Zpbml0ZShsaW5lTnVtKSkgcmV0dXJuIG51bGxcbiAgcmV0dXJuIHsgZmlsZSwgbGluZTogbGluZU51bSwgdGV4dDogdGV4dCA/PyAnJyB9XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxPQUFPLElBQUlDLFdBQVcsUUFBUSxNQUFNO0FBQzdDLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsU0FBUyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ25ELFNBQVNDLGtCQUFrQixRQUFRLDhCQUE4QjtBQUNqRSxTQUFTQyxlQUFlLFFBQVEsNkJBQTZCO0FBQzdELFNBQVNDLElBQUksUUFBUSxXQUFXO0FBQ2hDLFNBQVNDLFFBQVEsUUFBUSxnQ0FBZ0M7QUFDekQsU0FBU0MsTUFBTSxRQUFRLGlCQUFpQjtBQUN4QyxTQUFTQyx3QkFBd0IsUUFBUSxvQkFBb0I7QUFDN0QsU0FBU0Msa0JBQWtCLEVBQUVDLGVBQWUsUUFBUSxvQkFBb0I7QUFDeEUsU0FBU0MsY0FBYyxRQUFRLDRCQUE0QjtBQUMzRCxTQUFTQyxZQUFZLFFBQVEsb0NBQW9D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 \ No newline at end of file diff --git a/components/HelpV2/Commands.tsx b/components/HelpV2/Commands.tsx new file mode 100644 index 0000000..525ef1b --- /dev/null +++ b/components/HelpV2/Commands.tsx @@ -0,0 +1,82 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useMemo } from 'react'; +import { type Command, formatDescriptionWithSource } from '../../commands.js'; +import { Box, Text } from '../../ink.js'; +import { truncate } from '../../utils/format.js'; +import { Select } from '../CustomSelect/select.js'; +import { useTabHeaderFocus } from '../design-system/Tabs.js'; +type Props = { + commands: Command[]; + maxHeight: number; + columns: number; + title: string; + onCancel: () => void; + emptyMessage?: string; +}; +export function Commands(t0) { + const $ = _c(14); + const { + commands, + maxHeight, + columns, + title, + onCancel, + emptyMessage + } = t0; + const { + headerFocused, + focusHeader + } = useTabHeaderFocus(); + const maxWidth = Math.max(1, columns - 10); + const visibleCount = Math.max(1, Math.floor((maxHeight - 10) / 2)); + let t1; + if ($[0] !== commands || $[1] !== maxWidth) { + const seen = new Set(); + let t2; + if ($[3] !== maxWidth) { + t2 = cmd_0 => ({ + label: `/${cmd_0.name}`, + value: cmd_0.name, + description: truncate(formatDescriptionWithSource(cmd_0), maxWidth, true) + }); + $[3] = maxWidth; + $[4] = t2; + } else { + t2 = $[4]; + } + t1 = commands.filter(cmd => { + if (seen.has(cmd.name)) { + return false; + } + seen.add(cmd.name); + return true; + }).sort(_temp).map(t2); + $[0] = commands; + $[1] = maxWidth; + $[2] = t1; + } else { + t1 = $[2]; + } + const options = t1; + let t2; + if ($[5] !== commands.length || $[6] !== emptyMessage || $[7] !== focusHeader || $[8] !== headerFocused || $[9] !== onCancel || $[10] !== options || $[11] !== title || $[12] !== visibleCount) { + t2 = {commands.length === 0 && emptyMessage ? {emptyMessage} : <>{title}; + $[3] = handleSelect; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t4 = You can also configure this in /config or with the --ide flag; + $[5] = t4; + } else { + t4 = $[5]; + } + let t5; + if ($[6] !== onComplete || $[7] !== t3) { + t5 = {t3}{t4}; + $[6] = onComplete; + $[7] = t3; + $[8] = t5; + } else { + t5 = $[8]; + } + return t5; +} +export function shouldShowAutoConnectDialog(): boolean { + const config = getGlobalConfig(); + return !isSupportedTerminal() && config.autoConnectIde !== true && config.hasIdeAutoConnectDialogBeenShown !== true; +} +type IdeDisableAutoConnectDialogProps = { + onComplete: (disableAutoConnect: boolean) => void; +}; +export function IdeDisableAutoConnectDialog(t0) { + const $ = _c(10); + const { + onComplete + } = t0; + let t1; + if ($[0] !== onComplete) { + t1 = value => { + const disableAutoConnect = value === "yes"; + if (disableAutoConnect) { + saveGlobalConfig(_temp); + } + onComplete(disableAutoConnect); + }; + $[0] = onComplete; + $[1] = t1; + } else { + t1 = $[1]; + } + const handleSelect = t1; + let t2; + if ($[2] !== onComplete) { + t2 = () => { + onComplete(false); + }; + $[2] = onComplete; + $[3] = t2; + } else { + t2 = $[3]; + } + const handleCancel = t2; + let t3; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t3 = [{ + label: "No", + value: "no" + }, { + label: "Yes", + value: "yes" + }]; + $[4] = t3; + } else { + t3 = $[4]; + } + const options = t3; + let t4; + if ($[5] !== handleSelect) { + t4 = onDone(value)} />; + $[10] = onDone; + $[11] = t9; + } else { + t9 = $[11]; + } + let t10; + if ($[12] !== t3 || $[13] !== t4 || $[14] !== t9) { + t10 = {t5}{t9}; + $[12] = t3; + $[13] = t4; + $[14] = t9; + $[15] = t10; + } else { + t10 = $[15]; + } + return t10; +} +function formatIdleDuration(minutes: number): string { + if (minutes < 1) { + return '< 1m'; + } + if (minutes < 60) { + return `${Math.floor(minutes)}m`; + } + const hours = Math.floor(minutes / 60); + const remainingMinutes = Math.floor(minutes % 60); + if (remainingMinutes === 0) { + return `${hours}h`; + } + return `${hours}h ${remainingMinutes}m`; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJmb3JtYXRUb2tlbnMiLCJTZWxlY3QiLCJEaWFsb2ciLCJJZGxlUmV0dXJuQWN0aW9uIiwiUHJvcHMiLCJpZGxlTWludXRlcyIsInRvdGFsSW5wdXRUb2tlbnMiLCJvbkRvbmUiLCJhY3Rpb24iLCJJZGxlUmV0dXJuRGlhbG9nIiwidDAiLCIkIiwiX2MiLCJ0MSIsImZvcm1hdElkbGVEdXJhdGlvbiIsImZvcm1hdHRlZElkbGUiLCJ0MiIsImZvcm1hdHRlZFRva2VucyIsInQzIiwidDQiLCJ0NSIsIlN5bWJvbCIsImZvciIsInQ2IiwidmFsdWUiLCJjb25zdCIsImxhYmVsIiwidDciLCJ0OCIsInQ5IiwidDEwIiwibWludXRlcyIsIk1hdGgiLCJmbG9vciIsImhvdXJzIiwicmVtYWluaW5nTWludXRlcyJdLCJzb3VyY2VzIjpbIklkbGVSZXR1cm5EaWFsb2cudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGZvcm1hdFRva2VucyB9IGZyb20gJy4uL3V0aWxzL2Zvcm1hdC5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L2luZGV4LmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcblxudHlwZSBJZGxlUmV0dXJuQWN0aW9uID0gJ2NvbnRpbnVlJyB8ICdjbGVhcicgfCAnZGlzbWlzcycgfCAnbmV2ZXInXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGlkbGVNaW51dGVzOiBudW1iZXJcbiAgdG90YWxJbnB1dFRva2VuczogbnVtYmVyXG4gIG9uRG9uZTogKGFjdGlvbjogSWRsZVJldHVybkFjdGlvbikgPT4gdm9pZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gSWRsZVJldHVybkRpYWxvZyh7XG4gIGlkbGVNaW51dGVzLFxuICB0b3RhbElucHV0VG9rZW5zLFxuICBvbkRvbmUsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGZvcm1hdHRlZElkbGUgPSBmb3JtYXRJZGxlRHVyYXRpb24oaWRsZU1pbnV0ZXMpXG4gIGNvbnN0IGZvcm1hdHRlZFRva2VucyA9IGZvcm1hdFRva2Vucyh0b3RhbElucHV0VG9rZW5zKVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9e2BZb3UndmUgYmVlbiBhd2F5ICR7Zm9ybWF0dGVkSWRsZX0gYW5kIHRoaXMgY29udmVyc2F0aW9uIGlzICR7Zm9ybWF0dGVkVG9rZW5zfSB0b2tlbnMuYH1cbiAgICAgIG9uQ2FuY2VsPXsoKSA9PiBvbkRvbmUoJ2Rpc21pc3MnKX1cbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgSWYgdGhpcyBpcyBhIG5ldyB0YXNrLCBjbGVhcmluZyBjb250ZXh0IHdpbGwgc2F2ZSB1c2FnZSBhbmQgYmUgZmFzdGVyLlxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICAgIDxTZWxlY3RcbiAgICAgICAgb3B0aW9ucz17W1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIHZhbHVlOiAnY29udGludWUnIGFzIGNvbnN0LFxuICAgICAgICAgICAgbGFiZWw6ICdDb250aW51ZSB0aGlzIGNvbnZlcnNhdGlvbicsXG4gICAgICAgICAgfSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICB2YWx1ZTogJ2NsZWFyJyBhcyBjb25zdCxcbiAgICAgICAgICAgIGxhYmVsOiAnU2VuZCBtZXNzYWdlIGFzIGEgbmV3IGNvbnZlcnNhdGlvbicsXG4gICAgICAgICAgfSxcbiAgICAgICAgICB7XG4gICAgICAgICAgICB2YWx1ZTogJ25ldmVyJyBhcyBjb25zdCxcbiAgICAgICAgICAgIGxhYmVsOiBcIkRvbid0IGFzayBtZSBhZ2FpblwiLFxuICAgICAgICAgIH0sXG4gICAgICAgIF19XG4gICAgICAgIG9uQ2hhbmdlPXsodmFsdWU6IElkbGVSZXR1cm5BY3Rpb24pID0+IG9uRG9uZSh2YWx1ZSl9XG4gICAgICAvPlxuICAgIDwvRGlhbG9nPlxuICApXG59XG5cbmZ1bmN0aW9uIGZvcm1hdElkbGVEdXJhdGlvbihtaW51dGVzOiBudW1iZXIpOiBzdHJpbmcge1xuICBpZiAobWludXRlcyA8IDEpIHtcbiAgICByZXR1cm4gJzwgMW0nXG4gIH1cbiAgaWYgKG1pbnV0ZXMgPCA2MCkge1xuICAgIHJldHVybiBgJHtNYXRoLmZsb29yKG1pbnV0ZXMpfW1gXG4gIH1cbiAgY29uc3QgaG91cnMgPSBNYXRoLmZsb29yKG1pbnV0ZXMgLyA2MClcbiAgY29uc3QgcmVtYWluaW5nTWludXRlcyA9IE1hdGguZmxvb3IobWludXRlcyAlIDYwKVxuICBpZiAocmVtYWluaW5nTWludXRlcyA9PT0gMCkge1xuICAgIHJldHVybiBgJHtob3Vyc31oYFxuICB9XG4gIHJldHVybiBgJHtob3Vyc31oICR7cmVtYWluaW5nTWludXRlc31tYFxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUNyQyxTQUFTQyxZQUFZLFFBQVEsb0JBQW9CO0FBQ2pELFNBQVNDLE1BQU0sUUFBUSx5QkFBeUI7QUFDaEQsU0FBU0MsTUFBTSxRQUFRLDJCQUEyQjtBQUVsRCxLQUFLQyxnQkFBZ0IsR0FBRyxVQUFVLEdBQUcsT0FBTyxHQUFHLFNBQVMsR0FBRyxPQUFPO0FBRWxFLEtBQUtDLEtBQUssR0FBRztFQUNYQyxXQUFXLEVBQUUsTUFBTTtFQUNuQkMsZ0JBQWdCLEVBQUUsTUFBTTtFQUN4QkMsTUFBTSxFQUFFLENBQUNDLE1BQU0sRUFBRUwsZ0JBQWdCLEVBQUUsR0FBRyxJQUFJO0FBQzVDLENBQUM7QUFFRCxPQUFPLFNBQUFNLGlCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxF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 \ No newline at end of file diff --git a/components/InterruptedByUser.tsx b/components/InterruptedByUser.tsx new file mode 100644 index 0000000..979adf5 --- /dev/null +++ b/components/InterruptedByUser.tsx @@ -0,0 +1,15 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Text } from '../ink.js'; +export function InterruptedByUser() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = <>Interrupted {false ? · [ANT-ONLY] /issue to report a model issue : · What should Claude do instead?}; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJJbnRlcnJ1cHRlZEJ5VXNlciIsIiQiLCJfYyIsInQwIiwiU3ltYm9sIiwiZm9yIl0sInNvdXJjZXMiOlsiSW50ZXJydXB0ZWRCeVVzZXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uL2luay5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIEludGVycnVwdGVkQnlVc2VyKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxUZXh0IGRpbUNvbG9yPkludGVycnVwdGVkIDwvVGV4dD5cbiAgICAgIHtcImV4dGVybmFsXCIgPT09ICdhbnQnID8gKFxuICAgICAgICA8VGV4dCBkaW1Db2xvcj7CtyBbQU5ULU9OTFldIC9pc3N1ZSB0byByZXBvcnQgYSBtb2RlbCBpc3N1ZTwvVGV4dD5cbiAgICAgICkgOiAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPsK3IFdoYXQgc2hvdWxkIENsYXVkZSBkbyBpbnN0ZWFkPzwvVGV4dD5cbiAgICAgICl9XG4gICAgPC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsSUFBSSxRQUFRLFdBQVc7QUFFaEMsT0FBTyxTQUFBQyxrQkFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFHLE1BQUEsQ0FBQUMsR0FBQTtJQUVIRixFQUFBLEtBQ0UsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLFlBQVksRUFBMUIsSUFBSSxDQUNKLE1BQW9CLEdBQ25CLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQywyQ0FBMkMsRUFBekQsSUFBSSxDQUdOLEdBREMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLGdDQUFnQyxFQUE5QyxJQUFJLENBQ1AsQ0FBQyxHQUNBO0lBQUFGLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsT0FQSEUsRUFPRztBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/InvalidConfigDialog.tsx b/components/InvalidConfigDialog.tsx new file mode 100644 index 0000000..7805b13 --- /dev/null +++ b/components/InvalidConfigDialog.tsx @@ -0,0 +1,156 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, render, Text } from '../ink.js'; +import { KeybindingSetup } from '../keybindings/KeybindingProviderSetup.js'; +import { AppStateProvider } from '../state/AppState.js'; +import type { ConfigParseError } from '../utils/errors.js'; +import { getBaseRenderOptions } from '../utils/renderOptions.js'; +import { jsonStringify, writeFileSync_DEPRECATED } from '../utils/slowOperations.js'; +import type { ThemeName } from '../utils/theme.js'; +import { Select } from './CustomSelect/index.js'; +import { Dialog } from './design-system/Dialog.js'; +interface InvalidConfigHandlerProps { + error: ConfigParseError; +} +interface InvalidConfigDialogProps { + filePath: string; + errorDescription: string; + onExit: () => void; + onReset: () => void; +} + +/** + * Dialog shown when the Claude config file contains invalid JSON + */ +function InvalidConfigDialog(t0) { + const $ = _c(19); + const { + filePath, + errorDescription, + onExit, + onReset + } = t0; + let t1; + if ($[0] !== onExit || $[1] !== onReset) { + t1 = value => { + if (value === "exit") { + onExit(); + } else { + onReset(); + } + }; + $[0] = onExit; + $[1] = onReset; + $[2] = t1; + } else { + t1 = $[2]; + } + const handleSelect = t1; + let t2; + if ($[3] !== filePath) { + t2 = The configuration file at {filePath} contains invalid JSON.; + $[3] = filePath; + $[4] = t2; + } else { + t2 = $[4]; + } + let t3; + if ($[5] !== errorDescription) { + t3 = {errorDescription}; + $[5] = errorDescription; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] !== t2 || $[8] !== t3) { + t4 = {t2}{t3}; + $[7] = t2; + $[8] = t3; + $[9] = t4; + } else { + t4 = $[9]; + } + let t5; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t5 = Choose an option:; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t6 = [{ + label: "Exit and fix manually", + value: "exit" + }, { + label: "Reset with default configuration", + value: "reset" + }]; + $[11] = t6; + } else { + t6 = $[11]; + } + let t7; + if ($[12] !== handleSelect || $[13] !== onExit) { + t7 = {t5}; + $[7] = handleSelect; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] !== onExit || $[10] !== t2 || $[11] !== t5) { + t6 = {t2}{t3}{t5}; + $[9] = onExit; + $[10] = t2; + $[11] = t5; + $[12] = t6; + } else { + t6 = $[12]; + } + return t6; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJWYWxpZGF0aW9uRXJyb3IiLCJTZWxlY3QiLCJEaWFsb2ciLCJWYWxpZGF0aW9uRXJyb3JzTGlzdCIsIlByb3BzIiwic2V0dGluZ3NFcnJvcnMiLCJvbkNvbnRpbnVlIiwib25FeGl0IiwiSW52YWxpZFNldHRpbmdzRGlhbG9nIiwidDAiLCIkIiwiX2MiLCJ0MSIsImhhbmRsZVNlbGVjdCIsInZhbHVlIiwidDIiLCJ0MyIsIlN5bWJvbCIsImZvciIsInQ0IiwibGFiZWwiLCJ0NSIsInQ2Il0sInNvdXJjZXMiOlsiSW52YWxpZFNldHRpbmdzRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBWYWxpZGF0aW9uRXJyb3IgfSBmcm9tICcuLi91dGlscy9zZXR0aW5ncy92YWxpZGF0aW9uLmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi9DdXN0b21TZWxlY3QvaW5kZXguanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgVmFsaWRhdGlvbkVycm9yc0xpc3QgfSBmcm9tICcuL1ZhbGlkYXRpb25FcnJvcnNMaXN0LmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBzZXR0aW5nc0Vycm9yczogVmFsaWRhdGlvbkVycm9yW11cbiAgb25Db250aW51ZTogKCkgPT4gdm9pZFxuICBvbkV4aXQ6ICgpID0+IHZvaWRcbn1cblxuLyoqXG4gKiBEaWFsb2cgc2hvd24gd2hlbiBzZXR0aW5ncyBmaWxlcyBoYXZlIHZhbGlkYXRpb24gZXJyb3JzLlxuICogVXNlciBtdXN0IGNob29zZSB0byBjb250aW51ZSAoc2tpcHBpbmcgaW52YWxpZCBmaWxlcykgb3IgZXhpdCB0byBmaXggdGhlbS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIEludmFsaWRTZXR0aW5nc0RpYWxvZyh7XG4gIHNldHRpbmdzRXJyb3JzLFxuICBvbkNvbnRpbnVlLFxuICBvbkV4aXQsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGZ1bmN0aW9uIGhhbmRsZVNlbGVjdCh2YWx1ZTogc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKHZhbHVlID09PSAnZXhpdCcpIHtcbiAgICAgIG9uRXhpdCgpXG4gICAgfSBlbHNlIHtcbiAgICAgIG9uQ29udGludWUoKVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZyB0aXRsZT1cIlNldHRpbmdzIEVycm9yXCIgb25DYW5jZWw9e29uRXhpdH0gY29sb3I9XCJ3YXJuaW5nXCI+XG4gICAgICA8VmFsaWRhdGlvbkVycm9yc0xpc3QgZXJyb3JzPXtzZXR0aW5nc0Vycm9yc30gLz5cbiAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICBGaWxlcyB3aXRoIGVycm9ycyBhcmUgc2tpcHBlZCBlbnRpcmVseSwgbm90IGp1c3QgdGhlIGludmFsaWQgc2V0dGluZ3MuXG4gICAgICA8L1RleHQ+XG4gICAgICA8U2VsZWN0XG4gICAgICAgIG9wdGlvbnM9e1tcbiAgICAgICAgICB7IGxhYmVsOiAnRXhpdCBhbmQgZml4IG1hbnVhbGx5JywgdmFsdWU6ICdleGl0JyB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAnQ29udGludWUgd2l0aG91dCB0aGVzZSBzZXR0aW5ncycsXG4gICAgICAgICAgICB2YWx1ZTogJ2NvbnRpbnVlJyxcbiAgICAgICAgICB9LFxuICAgICAgICBdfVxuICAgICAgICBvbkNoYW5nZT17aGFuZGxlU2VsZWN0fVxuICAgICAgLz5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsSUFBSSxRQUFRLFdBQVc7QUFDaEMsY0FBY0MsZUFBZSxRQUFRLGlDQUFpQztBQUN0RSxTQUFTQyxNQUFNLFFB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 \ No newline at end of file diff --git a/components/KeybindingWarnings.tsx b/components/KeybindingWarnings.tsx new file mode 100644 index 0000000..c728685 --- /dev/null +++ b/components/KeybindingWarnings.tsx @@ -0,0 +1,55 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, Text } from '../ink.js'; +import { getCachedKeybindingWarnings, getKeybindingsPath, isKeybindingCustomizationEnabled } from '../keybindings/loadUserBindings.js'; + +/** + * Displays keybinding validation warnings in the UI. + * Similar to McpParsingWarnings, this provides persistent visibility + * of configuration issues. + * + * Only shown when keybinding customization is enabled (ant users + feature gate). + */ +export function KeybindingWarnings() { + const $ = _c(2); + if (!isKeybindingCustomizationEnabled()) { + return null; + } + let t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Symbol.for("react.early_return_sentinel"); + bb0: { + const warnings = getCachedKeybindingWarnings(); + if (warnings.length === 0) { + t1 = null; + break bb0; + } + const errors = warnings.filter(_temp); + const warns = warnings.filter(_temp2); + t0 = 0 ? "error" : "warning"}>Keybinding Configuration IssuesLocation: {getKeybindingsPath()}{errors.map(_temp3)}{warns.map(_temp4)}; + } + $[0] = t0; + $[1] = t1; + } else { + t0 = $[0]; + t1 = $[1]; + } + if (t1 !== Symbol.for("react.early_return_sentinel")) { + return t1; + } + return t0; +} +function _temp4(warning, i_0) { + return [Warning] {warning.message}{warning.suggestion && → {warning.suggestion}}; +} +function _temp3(error, i) { + return [Error] {error.message}{error.suggestion && → {error.suggestion}}; +} +function _temp2(w_0) { + return w_0.severity === "warning"; +} +function _temp(w) { + return w.severity === "error"; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJnZXRDYWNoZWRLZXliaW5kaW5nV2FybmluZ3MiLCJnZXRLZXliaW5kaW5nc1BhdGgiLCJpc0tleWJpbmRpbmdDdXN0b21pemF0aW9uRW5hYmxlZCIsIktleWJpbmRpbmdXYXJuaW5ncyIsIiQiLCJfYyIsInQwIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJiYjAiLCJ3YXJuaW5ncyIsImxlbmd0aCIsImVycm9ycyIsImZpbHRlciIsIl90ZW1wIiwid2FybnMiLCJfdGVtcDIiLCJtYXAiLCJfdGVtcDMiLCJfdGVtcDQiLCJ3YXJuaW5nIiwiaV8wIiwiaSIsIm1lc3NhZ2UiLCJzdWdnZXN0aW9uIiwiZXJyb3IiLCJ3XzAiLCJ3Iiwic2V2ZXJpdHkiXSwic291cmNlcyI6WyJLZXliaW5kaW5nV2FybmluZ3MudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7XG4gIGdldENhY2hlZEtleWJpbmRpbmdXYXJuaW5ncyxcbiAgZ2V0S2V5YmluZGluZ3NQYXRoLFxuICBpc0tleWJpbmRpbmdDdXN0b21pemF0aW9uRW5hYmxlZCxcbn0gZnJvbSAnLi4va2V5YmluZGluZ3MvbG9hZFVzZXJCaW5kaW5ncy5qcydcblxuLyoqXG4gKiBEaXNwbGF5cyBrZXliaW5kaW5nIHZhbGlkYXRpb24gd2FybmluZ3MgaW4gdGhlIFVJLlxuICogU2ltaWxhciB0byBNY3BQYXJzaW5nV2FybmluZ3MsIHRoaXMgcHJvdmlkZXMgcGVyc2lzdGVudCB2aXNpYmlsaXR5XG4gKiBvZiBjb25maWd1cmF0aW9uIGlzc3Vlcy5cbiAqXG4gKiBPbmx5IHNob3duIHdoZW4ga2V5YmluZGluZyBjdXN0b21pemF0aW9uIGlzIGVuYWJsZWQgKGFudCB1c2VycyArIGZlYXR1cmUgZ2F0ZSkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBLZXliaW5kaW5nV2FybmluZ3MoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgLy8gT25seSBzaG93IHdhcm5pbmdzIHdoZW4ga2V5YmluZGluZyBjdXN0b21pemF0aW9uIGlzIGVuYWJsZWRcbiAgaWYgKCFpc0tleWJpbmRpbmdDdXN0b21pemF0aW9uRW5hYmxlZCgpKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IHdhcm5pbmdzID0gZ2V0Q2FjaGVkS2V5YmluZGluZ1dhcm5pbmdzKClcblxuICBpZiAod2FybmluZ3MubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IGVycm9ycyA9IHdhcm5pbmdzLmZpbHRlcih3ID0+IHcuc2V2ZXJpdHkgPT09ICdlcnJvcicpXG4gIGNvbnN0IHdhcm5zID0gd2FybmluZ3MuZmlsdGVyKHcgPT4gdy5zZXZlcml0eSA9PT0gJ3dhcm5pbmcnKVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfSBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgPFRleHQgYm9sZCBjb2xvcj17ZXJyb3JzLmxlbmd0aCA+IDAgPyAnZXJyb3InIDogJ3dhcm5pbmcnfT5cbiAgICAgICAgS2V5YmluZGluZyBDb25maWd1cmF0aW9uIElzc3Vlc1xuICAgICAgPC9UZXh0PlxuICAgICAgPEJveD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+TG9jYXRpb246IDwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+e2dldEtleWJpbmRpbmdzUGF0aCgpfTwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgICAgPEJveCBtYXJnaW5MZWZ0PXsxfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgICAge2Vycm9ycy5tYXAoKGVycm9yLCBpKSA9PiAoXG4gICAgICAgICAgPEJveCBrZXk9e2BlcnJvci0ke2l9YH0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPEJveD5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+4pSUIDwvVGV4dD5cbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPltFcnJvcl08L1RleHQ+XG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiB7ZXJyb3IubWVzc2FnZX08L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgIHtlcnJvci5zdWdnZXN0aW9uICYmIChcbiAgICAgICAgICAgICAgPEJveCBtYXJnaW5MZWZ0PXszfT5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj7ihpIge2Vycm9yLnN1Z2dlc3Rpb259PC9UZXh0PlxuICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICkpfVxuICAgICAgICB7d2FybnMubWFwKCh3YXJuaW5nLCBpKSA9PiAoXG4gICAgICAgICAgPEJveCBrZXk9e2B3YXJuaW5nLSR7aX1gfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj7ilJQgPC9UZXh0PlxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj5bV2FybmluZ108L1RleHQ+XG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiB7d2FybmluZy5tZXNzYWdlfTwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAge3dhcm5pbmcuc3VnZ2VzdGlvbiAmJiAoXG4gICAgICAgICAgICAgIDxCb3ggbWFyZ2luTGVmdD17M30+XG4gICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+4oaSIHt3YXJuaW5nLnN1Z2dlc3Rpb259PC9UZXh0PlxuICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICkpfVxuICAgICAgPC9Cb3g+XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDckMsU0FDRUMsMkJBQTJCLEVBQzNCQyxrQkFBa0IsRUFDbEJDLGdDQUFnQyxRQUMzQixvQ0FBb0M7O0FBRTNDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTyxTQUFBQyxtQkFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUVMLElBQUksQ0FBQ0gsZ0NBQWdDLENBQUMsQ0FBQztJQUFBLE9BQzlCLElBQUk7RUFBQTtFQUNaLElBQUFJLEVBQUE7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBSSxNQUFBLENBQUFDLEdBQUE7SUFLUUYsRUFBQSxHQUFBQyxNQUFJLENBQUFDLEdBQUEsQ0FBSiw2QkFBRyxDQUFDO0lBQUFDLEdBQUE7TUFIYixNQUFBQyxRQUFBLEdBQWlCWCwyQkFBMkIsQ0FBQyxDQUFDO01BRTlDLElBQUlXLFFBQVEsQ0FBQUMsTUFBTyxLQUFLLENBQUM7UUFDaEJMLEVBQUEsT0FBSTtRQUFKLE1BQUFHLEdBQUE7TUFBSTtNQUdiLE1BQUFHLE1BQUEsR0FBZUYsUUFBUSxDQUFBRyxNQUFPLENBQUNDLEtBQTJCLENBQUM7TUFDM0QsTUFBQUMsS0FBQSxHQUFjTCxRQUFRLENBQUFHLE1BQU8sQ0FBQ0csTUFBNkIsQ0FBQztNQUcxRFgsRUFBQSxJQUFDLEdBQUcsQ0FBZSxhQUFRLENBQVIsUUFBUSxDQUFZLFNBQUMsQ0FBRCxHQUFDLENBQWdCLFlBQUMsQ0FBRCxHQUFDLENBQ3ZELENBQUMsSUFBSSxDQUFDLElBQUksQ0FBSixLQUFHLENBQUMsQ0FBUSxLQUF1QyxDQUF2QyxDQUFBTyxNQUFNLENBQUFELE1BQU8sR0FBRyxDQUF1QixHQUF2QyxPQUF1QyxHQUF2QyxTQUFzQyxDQUFDLENBQUUsK0JBRTNELEVBRkMsSUFBSSxDQUdMLENBQUMsR0FBRyxDQUNGLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxVQUFVLEVBQXhCLElBQUksQ0FDTCxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUUsQ0FBQVgsa0JBQWtCLENBQUMsRUFBRSxFQUFwQyxJQUFJLENBQ1AsRUFIQyxHQUFHLENBSUosQ0FBQyxHQUFHLENBQWEsVUFBQyxDQUFELEdBQUMsQ0FBZ0IsYUFBUSxDQUFSLFFBQVEsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUNwRCxDQUFBWSxNQUFNLENBQUFLLEdBQUksQ0FBQ0MsTUFhWCxFQUNBLENBQUFILEtBQUssQ0FBQUUsR0FBSSxDQUFDRSxNQWFWLEVBQ0gsRUE3QkMsR0FBRyxDQThCTixFQXRDQyxHQUFHLENBc0NFO0lBQUE7SUFBQWhCLENBQUEsTUFBQUUsRUFBQTtJQUFBRixDQUFBLE1BQUFHLEVBQUE7RUFBQTtJQUFBRCxFQUFBLEdBQUFGLENBQUE7SUFBQUcsRUFBQSxHQUFBSCxDQUFBO0VBQUE7RUFBQSxJQUFBRyxFQUFBLEtBQUFDLE1BQUEsQ0FBQUMsR0FBQTtJQUFBLE9BQUFGLEVBQUE7RUFBQTtFQUFBLE9BdENORCxFQXNDTTtBQUFBO0FBdERILFNBQUFjLE9BQUFDLE9BQUEsRUFBQUMsR0FBQTtFQUFBLE9Bd0NHLENBQUMsR0FBRyxDQUFNLEdBQWMsQ0FBZCxZQUFXQyxHQUFDLEVBQUMsQ0FBQyxDQUFnQixhQUFRLENBQVIsUUFBUSxDQUM5QyxDQUFDLEdBQUcsQ0FDRixDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsRUFBRSxFQUFoQixJQUFJLENBQ0wsQ0FBQyxJQUFJLENBQU8sS0FBUyxDQUFULFNBQVMsQ0FBQyxTQUFTLEVBQTlCLElBQUksQ0FDTCxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsQ0FBRSxDQUFBRixPQUFPLENBQUFHLE9BQU8sQ0FBRSxFQUFoQyxJQUFJLENBQ1AsRUFKQyxHQUFHLENBS0gsQ0FBQUgsT0FBTyxDQUFBSSxVQUlQLElBSEMsQ0FBQyxHQUFHLENBQWEsVUFBQyxDQUFELEdBQUMsQ0FDaEIsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLEVBQUcsQ0FBQUosT0FBTyxDQUFBSSxVQUFVLENBQUUsRUFBcEMsSUFBSSxDQUNQLEVBRkMsR0FBRyxDQUdOLENBQ0YsRUFYQyxHQUFHLENBV0U7QUFBQTtBQW5EVCxTQUFBTixPQUFBTyxLQUFBLEVBQUFILENBQUE7RUFBQSxPQTBCRyxDQUFDLEdBQUcsQ0FBTSxHQUFZLENBQVosVUFBU0EsQ0FBQyxFQUFDLENBQUMsQ0FBZ0IsYUFBUSxDQUFSLFFBQVEsQ0FDNUMsQ0FBQyxHQUFHLENBQ0YsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLEVBQUUsRUFBaEIsSUFBSSxDQUNMLENBQUMsSUFBSSxDQUFPLEtBQU8sQ0FBUCxPQUFPLENBQUMsT0FBTyxFQUExQixJQUFJLENBQ0wsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLENBQUUsQ0FBQUcsS0FBSyxDQUFBRixPQUFPLENBQUUsRUFBOUIsSUFBSSxDQUNQLEVBSkMsR0FBRyxDQUtILENBQUFFLEtBQUssQ0FBQUQsVUFJTCxJQUhDLENBQUMsR0FBRyxDQUFhLFVBQUMsQ0FBRCxHQUFDLENBQ2hCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxFQUFHLENBQUFDLEtBQUssQ0FBQUQsVUFBVSxDQUFFLEVBQWxDLElBQUksQ0FDUCxFQUZDLEdBQUcsQ0FHTixDQUNGLEVBWEMsR0FBRyxDQVdFO0FBQUE7QUFyQ1QsU0FBQVIsT0FBQVUsR0FBQTtFQUFBLE9BYThCQyxHQUFDLENBQUFDLFFBQVMsS0FBSyxTQUFTO0FBQUE7QUFidEQsU0FBQWQsTUFBQWEsQ0FBQTtFQUFBLE9BWStCQSxDQUFDLENBQUFDLFFBQVMsS0FBSyxPQUFPO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/LanguagePicker.tsx b/components/LanguagePicker.tsx new file mode 100644 index 0000000..96fd2fc --- /dev/null +++ b/components/LanguagePicker.tsx @@ -0,0 +1,86 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import React, { useState } from 'react'; +import { Box, Text } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import TextInput from './TextInput.js'; +type Props = { + initialLanguage: string | undefined; + onComplete: (language: string | undefined) => void; + onCancel: () => void; +}; +export function LanguagePicker(t0) { + const $ = _c(13); + const { + initialLanguage, + onComplete, + onCancel + } = t0; + const [language, setLanguage] = useState(initialLanguage); + const [cursorOffset, setCursorOffset] = useState((initialLanguage ?? "").length); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + context: "Settings" + }; + $[0] = t1; + } else { + t1 = $[0]; + } + useKeybinding("confirm:no", onCancel, t1); + let t2; + if ($[1] !== language || $[2] !== onComplete) { + t2 = function handleSubmit() { + const trimmed = language?.trim(); + onComplete(trimmed || undefined); + }; + $[1] = language; + $[2] = onComplete; + $[3] = t2; + } else { + t2 = $[3]; + } + const handleSubmit = t2; + let t3; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t3 = Enter your preferred response and voice language:; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t4 = {figures.pointer}; + $[5] = t4; + } else { + t4 = $[5]; + } + const t5 = language ?? ""; + let t6; + if ($[6] !== cursorOffset || $[7] !== handleSubmit || $[8] !== t5) { + t6 = {t4}; + $[6] = cursorOffset; + $[7] = handleSubmit; + $[8] = t5; + $[9] = t6; + } else { + t6 = $[9]; + } + let t7; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t7 = Leave empty for default (English); + $[10] = t7; + } else { + t7 = $[10]; + } + let t8; + if ($[11] !== t6) { + t8 = {t3}{t6}{t7}; + $[11] = t6; + $[12] = t8; + } else { + t8 = $[12]; + } + return t8; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VTdGF0ZSIsIkJveCIsIlRleHQiLCJ1c2VLZXliaW5kaW5nIiwiVGV4dElucHV0IiwiUHJvcHMiLCJpbml0aWFsTGFuZ3VhZ2UiLCJvbkNvbXBsZXRlIiwibGFuZ3VhZ2UiLCJvbkNhbmNlbCIsIkxhbmd1YWdlUGlja2VyIiwidDAiLCIkIiwiX2MiLCJzZXRMYW5ndWFnZSIsImN1cnNvck9mZnNldCIsInNldEN1cnNvck9mZnNldCIsImxlbmd0aCIsInQxIiwiU3ltYm9sIiwiZm9yIiwiY29udGV4dCIsInQyIiwiaGFuZGxlU3VibWl0IiwidHJpbW1lZCIsInRyaW0iLCJ1bmRlZmluZWQiLCJ0MyIsInQ0IiwicG9pbnRlciIsInQ1IiwidDYiLCJlbGxpcHNpcyIsInQ3IiwidDgiXSwic291cmNlcyI6WyJMYW5ndWFnZVBpY2tlci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCBSZWFjdCwgeyB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgVGV4dElucHV0IGZyb20gJy4vVGV4dElucHV0LmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBpbml0aWFsTGFuZ3VhZ2U6IHN0cmluZyB8IHVuZGVmaW5lZFxuICBvbkNvbXBsZXRlOiAobGFuZ3VhZ2U6IHN0cmluZyB8IHVuZGVmaW5lZCkgPT4gdm9pZFxuICBvbkNhbmNlbDogKCkgPT4gdm9pZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gTGFuZ3VhZ2VQaWNrZXIoe1xuICBpbml0aWFsTGFuZ3VhZ2UsXG4gIG9uQ29tcGxldGUsXG4gIG9uQ2FuY2VsLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbbGFuZ3VhZ2UsIHNldExhbmd1YWdlXSA9IHVzZVN0YXRlKGluaXRpYWxMYW5ndWFnZSlcbiAgY29uc3QgW2N1cnNvck9mZnNldCwgc2V0Q3Vyc29yT2Zmc2V0XSA9IHVzZVN0YXRlKFxuICAgIChpbml0aWFsTGFuZ3VhZ2UgPz8gJycpLmxlbmd0aCxcbiAgKVxuXG4gIC8vIFVzZSBjb25maWd1cmFibGUga2V5YmluZGluZyBmb3IgRVNDIHRvIGNhbmNlbFxuICAvLyBVc2UgU2V0dGluZ3MgY29udGV4dCBzbyAnbicga2V5IGRvZXNuJ3QgdHJpZ2dlciBjYW5jZWwgKGFsbG93cyB0eXBpbmcgJ24nIGluIGlucHV0KVxuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOm5vJywgb25DYW5jZWwsIHsgY29udGV4dDogJ1NldHRpbmdzJyB9KVxuXG4gIGZ1bmN0aW9uIGhhbmRsZVN1Ym1pdCgpOiB2b2lkIHtcbiAgICBjb25zdCB0cmltbWVkID0gbGFuZ3VhZ2U/LnRyaW0oKVxuICAgIG9uQ29tcGxldGUodHJpbW1lZCB8fCB1bmRlZmluZWQpXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICA8VGV4dD5FbnRlciB5b3VyIHByZWZlcnJlZCByZXNwb25zZSBhbmQgdm9pY2UgbGFuZ3VhZ2U6PC9UZXh0PlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXsxfT5cbiAgICAgICAgPFRleHQ+e2ZpZ3VyZXMucG9pbnRlcn08L1RleHQ+XG4gICAgICAgIDxUZXh0SW5wdXRcbiAgICAgICAgICB2YWx1ZT17bGFuZ3VhZ2UgPz8gJyd9XG4gICAgICAgICAgb25DaGFuZ2U9e3NldExhbmd1YWdlfVxuICAgICAgICAgIG9uU3VibWl0PXtoYW5kbGVTdWJtaXR9XG4gICAgICAgICAgZm9jdXM9e3RydWV9XG4gICAgICAgICAgc2hvd0N1cnNvcj17dHJ1ZX1cbiAgICAgICAgICBwbGFjZWhvbGRlcj17YGUuZy4sIEphcGFuZXNlLCDml6XmnKzoqp4sIEVzcGHDsW9sJHtmaWd1cmVzLmVsbGlwc2lzfWB9XG4gICAgICAgICAgY29sdW1ucz17NjB9XG4gICAgICAgICAgY3Vyc29yT2Zmc2V0PXtjdXJzb3JPZmZzZXR9XG4gICAgICAgICAgb25DaGFuZ2VDdXJzb3JPZmZzZXQ9e3NldEN1cnNvck9mZnNldH1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgICAgPFRleHQgZGltQ29sb3I+TGVhdmUgZW1wdHkgZm9yIGRlZmF1bHQgKEVuZ2xpc2gpPC9UZXh0PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPQyxLQUFLLElBQUlDLFFBQVEsUUFBUSxPQUFPO0FBQ3ZDLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDckMsU0FBU0MsYUFBYSxRQUFRLGlDQUFpQztBQUMvRCxPQUFPQyxTQUFTLE1BQU0sZ0JBQWdCO0FBRXRDLEtBQUtDLEtBQUssR0FBRztFQUNYQyxlQUFlLEVBQUUsTUFBTSxHQUFHLFNBQVM7RUFDbkNDLFVBQVUsRUFBRSxDQUFDQyxRQUFRLEVBQUUsTUFBTSxHQUFHLFNBQVMsRUFBRSxHQUFHLElBQUk7RUFDbERDLFFBQVEsRUFBRSxHQUFHLEdBQUcsSUFBSTtBQUN0QixDQUFDO0FBRUQsT0FBTyxTQUFBQyxlQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXdCO0lBQUFQLGVBQUE7SUFBQUMsVUFBQTtJQUFBRTtFQUFBLElBQUFFLEVBSXZCO0VBQ04sT0FBQUgsUUFBQSxFQUFBTSxXQUFBLElBQWdDZCxRQUFRLENBQUNNLGVBQWUsQ0FBQztFQUN6RCxPQUFBUyxZQUFBLEVBQUFDLGVBQUEsSUFBd0NoQixRQUFRLENBQzlDLENBQUNNLGVBQXFCLElBQXJCLEVBQXFCLEVBQUFXLE1BQ3hCLENBQUM7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQU4sQ0FBQSxRQUFBTyxNQUFBLENBQUFDLEdB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 \ No newline at end of file diff --git a/components/LogSelector.tsx b/components/LogSelector.tsx new file mode 100644 index 0000000..8b4ba2d --- /dev/null +++ b/components/LogSelector.tsx @@ -0,0 +1,1575 @@ +import { c as _c } from "react/compiler-runtime"; +import chalk from 'chalk'; +import figures from 'figures'; +import Fuse from 'fuse.js'; +import React from 'react'; +import { getOriginalCwd, getSessionId } from '../bootstrap/state.js'; +import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { useSearchInput } from '../hooks/useSearchInput.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { applyColor } from '../ink/colorize.js'; +import type { Color } from '../ink/styles.js'; +import { Box, Text, useInput, useTerminalFocus, useTheme } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { logEvent } from '../services/analytics/index.js'; +import type { LogOption, SerializedMessage } from '../types/logs.js'; +import { formatLogMetadata, truncateToWidth } from '../utils/format.js'; +import { getWorktreePaths } from '../utils/getWorktreePaths.js'; +import { getBranch } from '../utils/git.js'; +import { getLogDisplayTitle } from '../utils/log.js'; +import { getFirstMeaningfulUserMessageTextContent, getSessionIdFromLog, isCustomTitleEnabled, saveCustomTitle } from '../utils/sessionStorage.js'; +import { getTheme } from '../utils/theme.js'; +import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js'; +import { Select } from './CustomSelect/select.js'; +import { Byline } from './design-system/Byline.js'; +import { Divider } from './design-system/Divider.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +import { SearchBox } from './SearchBox.js'; +import { SessionPreview } from './SessionPreview.js'; +import { Spinner } from './Spinner.js'; +import { TagTabs } from './TagTabs.js'; +import TextInput from './TextInput.js'; +import { type TreeNode, TreeSelect } from './ui/TreeSelect.js'; +type AgenticSearchState = { + status: 'idle'; +} | { + status: 'searching'; +} | { + status: 'results'; + results: LogOption[]; + query: string; +} | { + status: 'error'; + message: string; +}; +export type LogSelectorProps = { + logs: LogOption[]; + maxHeight?: number; + forceWidth?: number; + onCancel?: () => void; + onSelect: (log: LogOption) => void; + onLogsChanged?: () => void; + onLoadMore?: (count: number) => void; + initialSearchQuery?: string; + showAllProjects?: boolean; + onToggleAllProjects?: () => void; + onAgenticSearch?: (query: string, logs: LogOption[], signal?: AbortSignal) => Promise; +}; +type LogTreeNode = TreeNode<{ + log: LogOption; + indexInFiltered: number; +}>; +function normalizeAndTruncateToWidth(text: string, maxWidth: number): string { + const normalized = text.replace(/\s+/g, ' ').trim(); + return truncateToWidth(normalized, maxWidth); +} + +// Width of prefixes that TreeSelect will add +const PARENT_PREFIX_WIDTH = 2; // '▼ ' or '▶ ' +const CHILD_PREFIX_WIDTH = 4; // ' ▸ ' + +// Deep search constants +const DEEP_SEARCH_MAX_MESSAGES = 2000; +const DEEP_SEARCH_CROP_SIZE = 1000; +const DEEP_SEARCH_MAX_TEXT_LENGTH = 50000; // Cap searchable text per session +const FUSE_THRESHOLD = 0.3; +const DATE_TIE_THRESHOLD_MS = 60 * 1000; // 1 minute - use relevance as tie-breaker within this window +const SNIPPET_CONTEXT_CHARS = 50; // Characters to show before/after match + +type Snippet = { + before: string; + match: string; + after: string; +}; +function formatSnippet({ + before, + match, + after +}: Snippet, highlightColor: (text: string) => string): string { + return chalk.dim(before) + highlightColor(match) + chalk.dim(after); +} +function extractSnippet(text: string, query: string, contextChars: number): Snippet | null { + // Find exact query occurrence (case-insensitive). + // Note: Fuse does fuzzy matching, so this may miss some fuzzy matches. + // This is acceptable for now - in the future we could use Fuse's includeMatches + // option and work with the match indices directly. + const matchIndex = text.toLowerCase().indexOf(query.toLowerCase()); + if (matchIndex === -1) return null; + const matchEnd = matchIndex + query.length; + const snippetStart = Math.max(0, matchIndex - contextChars); + const snippetEnd = Math.min(text.length, matchEnd + contextChars); + const beforeRaw = text.slice(snippetStart, matchIndex); + const matchText = text.slice(matchIndex, matchEnd); + const afterRaw = text.slice(matchEnd, snippetEnd); + return { + before: (snippetStart > 0 ? '…' : '') + beforeRaw.replace(/\s+/g, ' ').trimStart(), + match: matchText.trim(), + after: afterRaw.replace(/\s+/g, ' ').trimEnd() + (snippetEnd < text.length ? '…' : '') + }; +} +function buildLogLabel(log: LogOption, maxLabelWidth: number, options?: { + isGroupHeader?: boolean; + isChild?: boolean; + forkCount?: number; +}): string { + const { + isGroupHeader = false, + isChild = false, + forkCount = 0 + } = options || {}; + + // TreeSelect will add the prefix, so we just need to account for its width + const prefixWidth = isGroupHeader && forkCount > 0 ? PARENT_PREFIX_WIDTH : isChild ? CHILD_PREFIX_WIDTH : 0; + const sessionCountSuffix = isGroupHeader && forkCount > 0 ? ` (+${forkCount} other ${forkCount === 1 ? 'session' : 'sessions'})` : ''; + const sidechainSuffix = log.isSidechain ? ' (sidechain)' : ''; + const maxSummaryWidth = maxLabelWidth - prefixWidth - sidechainSuffix.length - sessionCountSuffix.length; + const truncatedSummary = normalizeAndTruncateToWidth(getLogDisplayTitle(log), maxSummaryWidth); + return `${truncatedSummary}${sidechainSuffix}${sessionCountSuffix}`; +} +function buildLogMetadata(log: LogOption, options?: { + isChild?: boolean; + showProjectPath?: boolean; +}): string { + const { + isChild = false, + showProjectPath = false + } = options || {}; + // Match the child prefix width for proper alignment + const childPadding = isChild ? ' ' : ''; // 4 spaces to match ' ▸ ' + const baseMetadata = formatLogMetadata(log); + const projectSuffix = showProjectPath && log.projectPath ? ` · ${log.projectPath}` : ''; + return childPadding + baseMetadata + projectSuffix; +} +export function LogSelector(t0) { + const $ = _c(247); + const { + logs, + maxHeight: t1, + forceWidth, + onCancel, + onSelect, + onLogsChanged, + onLoadMore, + initialSearchQuery, + showAllProjects: t2, + onToggleAllProjects, + onAgenticSearch + } = t0; + const maxHeight = t1 === undefined ? Infinity : t1; + const showAllProjects = t2 === undefined ? false : t2; + const terminalSize = useTerminalSize(); + const columns = forceWidth === undefined ? terminalSize.columns : forceWidth; + const exitState = useExitOnCtrlCDWithKeybindings(onCancel); + const isTerminalFocused = useTerminalFocus(); + let t3; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t3 = isCustomTitleEnabled(); + $[0] = t3; + } else { + t3 = $[0]; + } + const isResumeWithRenameEnabled = t3; + const isDeepSearchEnabled = false; + const [themeName] = useTheme(); + let t4; + if ($[1] !== themeName) { + t4 = getTheme(themeName); + $[1] = themeName; + $[2] = t4; + } else { + t4 = $[2]; + } + const theme = t4; + let t5; + if ($[3] !== theme.warning) { + t5 = text => applyColor(text, theme.warning as Color); + $[3] = theme.warning; + $[4] = t5; + } else { + t5 = $[4]; + } + const highlightColor = t5; + const isAgenticSearchEnabled = false; + const [currentBranch, setCurrentBranch] = React.useState(null); + const [branchFilterEnabled, setBranchFilterEnabled] = React.useState(false); + const [showAllWorktrees, setShowAllWorktrees] = React.useState(false); + const [hasMultipleWorktrees, setHasMultipleWorktrees] = React.useState(false); + let t6; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t6 = getOriginalCwd(); + $[5] = t6; + } else { + t6 = $[5]; + } + const currentCwd = t6; + const [renameValue, setRenameValue] = React.useState(""); + const [renameCursorOffset, setRenameCursorOffset] = React.useState(0); + let t7; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t7 = new Set(); + $[6] = t7; + } else { + t7 = $[6]; + } + const [expandedGroupSessionIds, setExpandedGroupSessionIds] = React.useState(t7); + const [focusedNode, setFocusedNode] = React.useState(null); + const [focusedIndex, setFocusedIndex] = React.useState(1); + const [viewMode, setViewMode] = React.useState("list"); + const [previewLog, setPreviewLog] = React.useState(null); + const prevFocusedIdRef = React.useRef(null); + const [selectedTagIndex, setSelectedTagIndex] = React.useState(0); + let t8; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t8 = { + status: "idle" + }; + $[7] = t8; + } else { + t8 = $[7]; + } + const [agenticSearchState, setAgenticSearchState] = React.useState(t8); + const [isAgenticSearchOptionFocused, setIsAgenticSearchOptionFocused] = React.useState(false); + const agenticSearchAbortRef = React.useRef(null); + const t9 = viewMode === "search" && agenticSearchState.status !== "searching"; + let t10; + let t11; + let t12; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t10 = () => { + setViewMode("list"); + logEvent("tengu_session_search_toggled", { + enabled: false + }); + }; + t11 = () => { + setViewMode("list"); + logEvent("tengu_session_search_toggled", { + enabled: false + }); + }; + t12 = ["n"]; + $[8] = t10; + $[9] = t11; + $[10] = t12; + } else { + t10 = $[8]; + t11 = $[9]; + t12 = $[10]; + } + const t13 = initialSearchQuery || ""; + let t14; + if ($[11] !== t13 || $[12] !== t9) { + t14 = { + isActive: t9, + onExit: t10, + onExitUp: t11, + passthroughCtrlKeys: t12, + initialQuery: t13 + }; + $[11] = t13; + $[12] = t9; + $[13] = t14; + } else { + t14 = $[13]; + } + const { + query: searchQuery, + setQuery: setSearchQuery, + cursorOffset: searchCursorOffset + } = useSearchInput(t14); + const deferredSearchQuery = React.useDeferredValue(searchQuery); + const [debouncedDeepSearchQuery, setDebouncedDeepSearchQuery] = React.useState(""); + let t15; + let t16; + if ($[14] !== deferredSearchQuery) { + t15 = () => { + if (!deferredSearchQuery) { + setDebouncedDeepSearchQuery(""); + return; + } + const timeoutId = setTimeout(setDebouncedDeepSearchQuery, 300, deferredSearchQuery); + return () => clearTimeout(timeoutId); + }; + t16 = [deferredSearchQuery]; + $[14] = deferredSearchQuery; + $[15] = t15; + $[16] = t16; + } else { + t15 = $[15]; + t16 = $[16]; + } + React.useEffect(t15, t16); + const [deepSearchResults, setDeepSearchResults] = React.useState(null); + const [isSearching, setIsSearching] = React.useState(false); + let t17; + let t18; + if ($[17] === Symbol.for("react.memo_cache_sentinel")) { + t17 = () => { + getBranch().then(branch => setCurrentBranch(branch)); + getWorktreePaths(currentCwd).then(paths => { + setHasMultipleWorktrees(paths.length > 1); + }); + }; + t18 = [currentCwd]; + $[17] = t17; + $[18] = t18; + } else { + t17 = $[17]; + t18 = $[18]; + } + React.useEffect(t17, t18); + const searchableTextByLog = new Map(logs.map(_temp)); + let t19; + t19 = null; + let t20; + if ($[19] !== logs) { + t20 = getUniqueTags(logs); + $[19] = logs; + $[20] = t20; + } else { + t20 = $[20]; + } + const uniqueTags = t20; + const hasTags = uniqueTags.length > 0; + let t21; + if ($[21] !== hasTags || $[22] !== uniqueTags) { + t21 = hasTags ? ["All", ...uniqueTags] : []; + $[21] = hasTags; + $[22] = uniqueTags; + $[23] = t21; + } else { + t21 = $[23]; + } + const tagTabs = t21; + const effectiveTagIndex = tagTabs.length > 0 && selectedTagIndex < tagTabs.length ? selectedTagIndex : 0; + const selectedTab = tagTabs[effectiveTagIndex]; + const tagFilter = selectedTab === "All" ? undefined : selectedTab; + const tagTabsLines = hasTags ? 1 : 0; + let filtered = logs; + if (isResumeWithRenameEnabled) { + let t22; + if ($[24] !== logs) { + t22 = logs.filter(_temp2); + $[24] = logs; + $[25] = t22; + } else { + t22 = $[25]; + } + filtered = t22; + } + if (tagFilter !== undefined) { + let t22; + if ($[26] !== filtered || $[27] !== tagFilter) { + let t23; + if ($[29] !== tagFilter) { + t23 = log_2 => log_2.tag === tagFilter; + $[29] = tagFilter; + $[30] = t23; + } else { + t23 = $[30]; + } + t22 = filtered.filter(t23); + $[26] = filtered; + $[27] = tagFilter; + $[28] = t22; + } else { + t22 = $[28]; + } + filtered = t22; + } + if (branchFilterEnabled && currentBranch) { + let t22; + if ($[31] !== currentBranch || $[32] !== filtered) { + let t23; + if ($[34] !== currentBranch) { + t23 = log_3 => log_3.gitBranch === currentBranch; + $[34] = currentBranch; + $[35] = t23; + } else { + t23 = $[35]; + } + t22 = filtered.filter(t23); + $[31] = currentBranch; + $[32] = filtered; + $[33] = t22; + } else { + t22 = $[33]; + } + filtered = t22; + } + if (hasMultipleWorktrees && !showAllWorktrees) { + let t22; + if ($[36] !== filtered) { + let t23; + if ($[38] === Symbol.for("react.memo_cache_sentinel")) { + t23 = log_4 => log_4.projectPath === currentCwd; + $[38] = t23; + } else { + t23 = $[38]; + } + t22 = filtered.filter(t23); + $[36] = filtered; + $[37] = t22; + } else { + t22 = $[37]; + } + filtered = t22; + } + const baseFilteredLogs = filtered; + let t22; + bb0: { + if (!searchQuery) { + t22 = baseFilteredLogs; + break bb0; + } + let t23; + if ($[39] !== baseFilteredLogs || $[40] !== searchQuery) { + const query = searchQuery.toLowerCase(); + t23 = baseFilteredLogs.filter(log_5 => { + const displayedTitle = getLogDisplayTitle(log_5).toLowerCase(); + const branch_0 = (log_5.gitBranch || "").toLowerCase(); + const tag = (log_5.tag || "").toLowerCase(); + const prInfo = log_5.prNumber ? `pr #${log_5.prNumber} ${log_5.prRepository || ""}`.toLowerCase() : ""; + return displayedTitle.includes(query) || branch_0.includes(query) || tag.includes(query) || prInfo.includes(query); + }); + $[39] = baseFilteredLogs; + $[40] = searchQuery; + $[41] = t23; + } else { + t23 = $[41]; + } + t22 = t23; + } + const titleFilteredLogs = t22; + let t23; + let t24; + if ($[42] !== debouncedDeepSearchQuery || $[43] !== deferredSearchQuery) { + t23 = () => { + if (false && deferredSearchQuery && deferredSearchQuery !== debouncedDeepSearchQuery) { + setIsSearching(true); + } + }; + t24 = [deferredSearchQuery, debouncedDeepSearchQuery, false]; + $[42] = debouncedDeepSearchQuery; + $[43] = deferredSearchQuery; + $[44] = t23; + $[45] = t24; + } else { + t23 = $[44]; + t24 = $[45]; + } + React.useEffect(t23, t24); + let t25; + let t26; + if ($[46] !== debouncedDeepSearchQuery) { + t25 = () => { + if (true || !debouncedDeepSearchQuery || true) { + setDeepSearchResults(null); + setIsSearching(false); + return; + } + const timeoutId_0 = setTimeout(_temp5, 0, null, debouncedDeepSearchQuery, setDeepSearchResults, setIsSearching); + return () => { + clearTimeout(timeoutId_0); + }; + }; + t26 = [debouncedDeepSearchQuery, null, false]; + $[46] = debouncedDeepSearchQuery; + $[47] = t25; + $[48] = t26; + } else { + t25 = $[47]; + t26 = $[48]; + } + React.useEffect(t25, t26); + let filtered_0; + let snippetMap; + if ($[49] !== debouncedDeepSearchQuery || $[50] !== deepSearchResults || $[51] !== titleFilteredLogs) { + snippetMap = new Map(); + filtered_0 = titleFilteredLogs; + if (deepSearchResults && debouncedDeepSearchQuery && deepSearchResults.query === debouncedDeepSearchQuery) { + for (const result of deepSearchResults.results) { + if (result.searchableText) { + const snippet = extractSnippet(result.searchableText, debouncedDeepSearchQuery, SNIPPET_CONTEXT_CHARS); + if (snippet) { + snippetMap.set(result.log, snippet); + } + } + } + let t27; + if ($[54] !== filtered_0) { + t27 = new Set(filtered_0.map(_temp6)); + $[54] = filtered_0; + $[55] = t27; + } else { + t27 = $[55]; + } + const titleMatchIds = t27; + let t28; + if ($[56] !== deepSearchResults.results || $[57] !== filtered_0 || $[58] !== titleMatchIds) { + let t29; + if ($[60] !== titleMatchIds) { + t29 = log_7 => !titleMatchIds.has(log_7.messages[0]?.uuid); + $[60] = titleMatchIds; + $[61] = t29; + } else { + t29 = $[61]; + } + const transcriptOnlyMatches = deepSearchResults.results.map(_temp7).filter(t29); + t28 = [...filtered_0, ...transcriptOnlyMatches]; + $[56] = deepSearchResults.results; + $[57] = filtered_0; + $[58] = titleMatchIds; + $[59] = t28; + } else { + t28 = $[59]; + } + filtered_0 = t28; + } + $[49] = debouncedDeepSearchQuery; + $[50] = deepSearchResults; + $[51] = titleFilteredLogs; + $[52] = filtered_0; + $[53] = snippetMap; + } else { + filtered_0 = $[52]; + snippetMap = $[53]; + } + let t27; + if ($[62] !== filtered_0 || $[63] !== snippetMap) { + t27 = { + filteredLogs: filtered_0, + snippets: snippetMap + }; + $[62] = filtered_0; + $[63] = snippetMap; + $[64] = t27; + } else { + t27 = $[64]; + } + const { + filteredLogs, + snippets + } = t27; + let t28; + bb1: { + if (agenticSearchState.status === "results" && agenticSearchState.results.length > 0) { + t28 = agenticSearchState.results; + break bb1; + } + t28 = filteredLogs; + } + const displayedLogs = t28; + const maxLabelWidth = Math.max(30, columns - 4); + let t29; + bb2: { + if (!isResumeWithRenameEnabled) { + let t30; + if ($[65] === Symbol.for("react.memo_cache_sentinel")) { + t30 = []; + $[65] = t30; + } else { + t30 = $[65]; + } + t29 = t30; + break bb2; + } + let t30; + if ($[66] !== displayedLogs || $[67] !== highlightColor || $[68] !== maxLabelWidth || $[69] !== showAllProjects || $[70] !== snippets) { + const sessionGroups = groupLogsBySessionId(displayedLogs); + t30 = Array.from(sessionGroups.entries()).map(t31 => { + const [sessionId, groupLogs] = t31; + const latestLog = groupLogs[0]; + const indexInFiltered = displayedLogs.indexOf(latestLog); + const snippet_0 = snippets.get(latestLog); + const snippetStr = snippet_0 ? formatSnippet(snippet_0, highlightColor) : null; + if (groupLogs.length === 1) { + const metadata = buildLogMetadata(latestLog, { + showProjectPath: showAllProjects + }); + return { + id: `log:${sessionId}:0`, + value: { + log: latestLog, + indexInFiltered + }, + label: buildLogLabel(latestLog, maxLabelWidth), + description: snippetStr ? `${metadata}\n ${snippetStr}` : metadata, + dimDescription: true + }; + } + const forkCount = groupLogs.length - 1; + const children = groupLogs.slice(1).map((log_8, index) => { + const childIndexInFiltered = displayedLogs.indexOf(log_8); + const childSnippet = snippets.get(log_8); + const childSnippetStr = childSnippet ? formatSnippet(childSnippet, highlightColor) : null; + const childMetadata = buildLogMetadata(log_8, { + isChild: true, + showProjectPath: showAllProjects + }); + return { + id: `log:${sessionId}:${index + 1}`, + value: { + log: log_8, + indexInFiltered: childIndexInFiltered + }, + label: buildLogLabel(log_8, maxLabelWidth, { + isChild: true + }), + description: childSnippetStr ? `${childMetadata}\n ${childSnippetStr}` : childMetadata, + dimDescription: true + }; + }); + const parentMetadata = buildLogMetadata(latestLog, { + showProjectPath: showAllProjects + }); + return { + id: `group:${sessionId}`, + value: { + log: latestLog, + indexInFiltered + }, + label: buildLogLabel(latestLog, maxLabelWidth, { + isGroupHeader: true, + forkCount + }), + description: snippetStr ? `${parentMetadata}\n ${snippetStr}` : parentMetadata, + dimDescription: true, + children + }; + }); + $[66] = displayedLogs; + $[67] = highlightColor; + $[68] = maxLabelWidth; + $[69] = showAllProjects; + $[70] = snippets; + $[71] = t30; + } else { + t30 = $[71]; + } + t29 = t30; + } + const treeNodes = t29; + let t30; + bb3: { + if (isResumeWithRenameEnabled) { + let t31; + if ($[72] === Symbol.for("react.memo_cache_sentinel")) { + t31 = []; + $[72] = t31; + } else { + t31 = $[72]; + } + t30 = t31; + break bb3; + } + let t31; + if ($[73] !== displayedLogs || $[74] !== highlightColor || $[75] !== maxLabelWidth || $[76] !== showAllProjects || $[77] !== snippets) { + let t32; + if ($[79] !== highlightColor || $[80] !== maxLabelWidth || $[81] !== showAllProjects || $[82] !== snippets) { + t32 = (log_9, index_0) => { + const rawSummary = getLogDisplayTitle(log_9); + const summaryWithSidechain = rawSummary + (log_9.isSidechain ? " (sidechain)" : ""); + const summary = normalizeAndTruncateToWidth(summaryWithSidechain, maxLabelWidth); + const baseDescription = formatLogMetadata(log_9); + const projectSuffix = showAllProjects && log_9.projectPath ? ` · ${log_9.projectPath}` : ""; + const snippet_1 = snippets.get(log_9); + const snippetStr_0 = snippet_1 ? formatSnippet(snippet_1, highlightColor) : null; + return { + label: summary, + description: snippetStr_0 ? `${baseDescription}${projectSuffix}\n ${snippetStr_0}` : baseDescription + projectSuffix, + dimDescription: true, + value: index_0.toString() + }; + }; + $[79] = highlightColor; + $[80] = maxLabelWidth; + $[81] = showAllProjects; + $[82] = snippets; + $[83] = t32; + } else { + t32 = $[83]; + } + t31 = displayedLogs.map(t32); + $[73] = displayedLogs; + $[74] = highlightColor; + $[75] = maxLabelWidth; + $[76] = showAllProjects; + $[77] = snippets; + $[78] = t31; + } else { + t31 = $[78]; + } + t30 = t31; + } + const flatOptions = t30; + const focusedLog = focusedNode?.value.log ?? null; + let t31; + if ($[84] !== displayedLogs || $[85] !== expandedGroupSessionIds || $[86] !== focusedLog) { + t31 = () => { + if (!isResumeWithRenameEnabled || !focusedLog) { + return ""; + } + const sessionId_0 = getSessionIdFromLog(focusedLog); + if (!sessionId_0) { + return ""; + } + const sessionLogs = displayedLogs.filter(log_10 => getSessionIdFromLog(log_10) === sessionId_0); + const hasMultipleLogs = sessionLogs.length > 1; + if (!hasMultipleLogs) { + return ""; + } + const isExpanded = expandedGroupSessionIds.has(sessionId_0); + const isChildNode = sessionLogs.indexOf(focusedLog) > 0; + if (isChildNode) { + return "\u2190 to collapse"; + } + return isExpanded ? "\u2190 to collapse" : "\u2192 to expand"; + }; + $[84] = displayedLogs; + $[85] = expandedGroupSessionIds; + $[86] = focusedLog; + $[87] = t31; + } else { + t31 = $[87]; + } + const getExpandCollapseHint = t31; + let t32; + if ($[88] !== focusedLog || $[89] !== onLogsChanged || $[90] !== renameValue) { + t32 = async () => { + const sessionId_1 = focusedLog ? getSessionIdFromLog(focusedLog) : undefined; + if (!focusedLog || !sessionId_1) { + setViewMode("list"); + setRenameValue(""); + return; + } + if (renameValue.trim()) { + await saveCustomTitle(sessionId_1, renameValue.trim(), focusedLog.fullPath); + if (isResumeWithRenameEnabled && onLogsChanged) { + onLogsChanged(); + } + } + setViewMode("list"); + setRenameValue(""); + }; + $[88] = focusedLog; + $[89] = onLogsChanged; + $[90] = renameValue; + $[91] = t32; + } else { + t32 = $[91]; + } + const handleRenameSubmit = t32; + let t33; + if ($[92] === Symbol.for("react.memo_cache_sentinel")) { + t33 = () => { + setViewMode("list"); + logEvent("tengu_session_search_toggled", { + enabled: false + }); + }; + $[92] = t33; + } else { + t33 = $[92]; + } + const exitSearchMode = t33; + let t34; + if ($[93] === Symbol.for("react.memo_cache_sentinel")) { + t34 = () => { + setViewMode("search"); + logEvent("tengu_session_search_toggled", { + enabled: true + }); + }; + $[93] = t34; + } else { + t34 = $[93]; + } + const enterSearchMode = t34; + let t35; + if ($[94] !== logs || $[95] !== onAgenticSearch || $[96] !== searchQuery) { + t35 = async () => { + if (!searchQuery.trim() || !onAgenticSearch || true) { + return; + } + agenticSearchAbortRef.current?.abort(); + const abortController = new AbortController(); + agenticSearchAbortRef.current = abortController; + setAgenticSearchState({ + status: "searching" + }); + logEvent("tengu_agentic_search_started", { + query_length: searchQuery.length + }); + ; + try { + const results_0 = await onAgenticSearch(searchQuery, logs, abortController.signal); + if (abortController.signal.aborted) { + return; + } + setAgenticSearchState({ + status: "results", + results: results_0, + query: searchQuery + }); + logEvent("tengu_agentic_search_completed", { + query_length: searchQuery.length, + results_count: results_0.length + }); + } catch (t36) { + const error = t36; + if (abortController.signal.aborted) { + return; + } + setAgenticSearchState({ + status: "error", + message: error instanceof Error ? error.message : "Search failed" + }); + logEvent("tengu_agentic_search_error", { + query_length: searchQuery.length + }); + } + }; + $[94] = logs; + $[95] = onAgenticSearch; + $[96] = searchQuery; + $[97] = t35; + } else { + t35 = $[97]; + } + const handleAgenticSearch = t35; + let t36; + if ($[98] !== agenticSearchState.query || $[99] !== agenticSearchState.status || $[100] !== searchQuery) { + t36 = () => { + if (agenticSearchState.status !== "idle" && agenticSearchState.status !== "searching") { + if (agenticSearchState.status === "results" && agenticSearchState.query !== searchQuery || agenticSearchState.status === "error") { + setAgenticSearchState({ + status: "idle" + }); + } + } + }; + $[98] = agenticSearchState.query; + $[99] = agenticSearchState.status; + $[100] = searchQuery; + $[101] = t36; + } else { + t36 = $[101]; + } + let t37; + if ($[102] !== agenticSearchState || $[103] !== searchQuery) { + t37 = [searchQuery, agenticSearchState]; + $[102] = agenticSearchState; + $[103] = searchQuery; + $[104] = t37; + } else { + t37 = $[104]; + } + React.useEffect(t36, t37); + let t38; + let t39; + if ($[105] === Symbol.for("react.memo_cache_sentinel")) { + t38 = () => () => { + agenticSearchAbortRef.current?.abort(); + }; + t39 = []; + $[105] = t38; + $[106] = t39; + } else { + t38 = $[105]; + t39 = $[106]; + } + React.useEffect(t38, t39); + const prevAgenticStatusRef = React.useRef(agenticSearchState.status); + let t40; + if ($[107] !== agenticSearchState.status || $[108] !== displayedLogs[0] || $[109] !== displayedLogs.length || $[110] !== treeNodes) { + t40 = () => { + const prevStatus = prevAgenticStatusRef.current; + prevAgenticStatusRef.current = agenticSearchState.status; + if (prevStatus === "searching" && agenticSearchState.status === "results") { + if (isResumeWithRenameEnabled && treeNodes.length > 0) { + setFocusedNode(treeNodes[0]); + } else { + if (!isResumeWithRenameEnabled && displayedLogs.length > 0) { + const firstLog = displayedLogs[0]; + setFocusedNode({ + id: "0", + value: { + log: firstLog, + indexInFiltered: 0 + }, + label: "" + }); + } + } + } + }; + $[107] = agenticSearchState.status; + $[108] = displayedLogs[0]; + $[109] = displayedLogs.length; + $[110] = treeNodes; + $[111] = t40; + } else { + t40 = $[111]; + } + let t41; + if ($[112] !== agenticSearchState.status || $[113] !== displayedLogs || $[114] !== treeNodes) { + t41 = [agenticSearchState.status, isResumeWithRenameEnabled, treeNodes, displayedLogs]; + $[112] = agenticSearchState.status; + $[113] = displayedLogs; + $[114] = treeNodes; + $[115] = t41; + } else { + t41 = $[115]; + } + React.useEffect(t40, t41); + let t42; + if ($[116] !== displayedLogs) { + t42 = value => { + const index_1 = parseInt(value, 10); + const log_11 = displayedLogs[index_1]; + if (!log_11 || prevFocusedIdRef.current === index_1.toString()) { + return; + } + prevFocusedIdRef.current = index_1.toString(); + setFocusedNode({ + id: index_1.toString(), + value: { + log: log_11, + indexInFiltered: index_1 + }, + label: "" + }); + setFocusedIndex(index_1 + 1); + }; + $[116] = displayedLogs; + $[117] = t42; + } else { + t42 = $[117]; + } + const handleFlatOptionsSelectFocus = t42; + let t43; + if ($[118] !== displayedLogs) { + t43 = node => { + setFocusedNode(node); + const index_2 = displayedLogs.findIndex(log_12 => getSessionIdFromLog(log_12) === getSessionIdFromLog(node.value.log)); + if (index_2 >= 0) { + setFocusedIndex(index_2 + 1); + } + }; + $[118] = displayedLogs; + $[119] = t43; + } else { + t43 = $[119]; + } + const handleTreeSelectFocus = t43; + let t44; + if ($[120] === Symbol.for("react.memo_cache_sentinel")) { + t44 = () => { + agenticSearchAbortRef.current?.abort(); + setAgenticSearchState({ + status: "idle" + }); + logEvent("tengu_agentic_search_cancelled", {}); + }; + $[120] = t44; + } else { + t44 = $[120]; + } + const t45 = viewMode !== "preview" && agenticSearchState.status === "searching"; + let t46; + if ($[121] !== t45) { + t46 = { + context: "Confirmation", + isActive: t45 + }; + $[121] = t45; + $[122] = t46; + } else { + t46 = $[122]; + } + useKeybinding("confirm:no", t44, t46); + let t47; + if ($[123] === Symbol.for("react.memo_cache_sentinel")) { + t47 = () => { + setViewMode("list"); + setRenameValue(""); + }; + $[123] = t47; + } else { + t47 = $[123]; + } + const t48 = viewMode === "rename" && agenticSearchState.status !== "searching"; + let t49; + if ($[124] !== t48) { + t49 = { + context: "Settings", + isActive: t48 + }; + $[124] = t48; + $[125] = t49; + } else { + t49 = $[125]; + } + useKeybinding("confirm:no", t47, t49); + let t50; + if ($[126] !== onCancel || $[127] !== setSearchQuery) { + t50 = () => { + setSearchQuery(""); + setIsAgenticSearchOptionFocused(false); + onCancel?.(); + }; + $[126] = onCancel; + $[127] = setSearchQuery; + $[128] = t50; + } else { + t50 = $[128]; + } + const t51 = viewMode !== "preview" && viewMode !== "rename" && viewMode !== "search" && isAgenticSearchOptionFocused && agenticSearchState.status !== "searching"; + let t52; + if ($[129] !== t51) { + t52 = { + context: "Confirmation", + isActive: t51 + }; + $[129] = t51; + $[130] = t52; + } else { + t52 = $[130]; + } + useKeybinding("confirm:no", t50, t52); + let t53; + if ($[131] !== agenticSearchState.status || $[132] !== branchFilterEnabled || $[133] !== focusedLog || $[134] !== handleAgenticSearch || $[135] !== hasMultipleWorktrees || $[136] !== hasTags || $[137] !== isAgenticSearchOptionFocused || $[138] !== onAgenticSearch || $[139] !== onToggleAllProjects || $[140] !== searchQuery || $[141] !== setSearchQuery || $[142] !== showAllProjects || $[143] !== showAllWorktrees || $[144] !== tagTabs || $[145] !== uniqueTags || $[146] !== viewMode) { + t53 = (input, key) => { + if (viewMode === "preview") { + return; + } + if (agenticSearchState.status === "searching") { + return; + } + if (viewMode === "rename") {} else { + if (viewMode === "search") { + if (input.toLowerCase() === "n" && key.ctrl) { + exitSearchMode(); + } else { + if (key.return || key.downArrow) { + if (searchQuery.trim() && onAgenticSearch && false && agenticSearchState.status !== "results") { + setIsAgenticSearchOptionFocused(true); + } + } + } + } else { + if (isAgenticSearchOptionFocused) { + if (key.return) { + handleAgenticSearch(); + setIsAgenticSearchOptionFocused(false); + return; + } else { + if (key.downArrow) { + setIsAgenticSearchOptionFocused(false); + return; + } else { + if (key.upArrow) { + setViewMode("search"); + setIsAgenticSearchOptionFocused(false); + return; + } + } + } + } + if (hasTags && key.tab) { + const offset = key.shift ? -1 : 1; + setSelectedTagIndex(prev => { + const current = prev < tagTabs.length ? prev : 0; + const newIndex = (current + tagTabs.length + offset) % tagTabs.length; + const newTab = tagTabs[newIndex]; + logEvent("tengu_session_tag_filter_changed", { + is_all: newTab === "All", + tag_count: uniqueTags.length + }); + return newIndex; + }); + return; + } + const keyIsNotCtrlOrMeta = !key.ctrl && !key.meta; + const lowerInput = input.toLowerCase(); + if (lowerInput === "a" && key.ctrl && onToggleAllProjects) { + onToggleAllProjects(); + logEvent("tengu_session_all_projects_toggled", { + enabled: !showAllProjects + }); + } else { + if (lowerInput === "b" && key.ctrl) { + const newEnabled = !branchFilterEnabled; + setBranchFilterEnabled(newEnabled); + logEvent("tengu_session_branch_filter_toggled", { + enabled: newEnabled + }); + } else { + if (lowerInput === "w" && key.ctrl && hasMultipleWorktrees) { + const newValue = !showAllWorktrees; + setShowAllWorktrees(newValue); + logEvent("tengu_session_worktree_filter_toggled", { + enabled: newValue + }); + } else { + if (lowerInput === "/" && keyIsNotCtrlOrMeta) { + setViewMode("search"); + logEvent("tengu_session_search_toggled", { + enabled: true + }); + } else { + if (lowerInput === "r" && key.ctrl && focusedLog) { + setViewMode("rename"); + setRenameValue(""); + logEvent("tengu_session_rename_started", {}); + } else { + if (lowerInput === "v" && key.ctrl && focusedLog) { + setPreviewLog(focusedLog); + setViewMode("preview"); + logEvent("tengu_session_preview_opened", { + messageCount: focusedLog.messageCount + }); + } else { + if (focusedLog && keyIsNotCtrlOrMeta && input.length > 0 && !/^\s+$/.test(input)) { + setViewMode("search"); + setSearchQuery(input); + logEvent("tengu_session_search_toggled", { + enabled: true + }); + } + } + } + } + } + } + } + } + } + }; + $[131] = agenticSearchState.status; + $[132] = branchFilterEnabled; + $[133] = focusedLog; + $[134] = handleAgenticSearch; + $[135] = hasMultipleWorktrees; + $[136] = hasTags; + $[137] = isAgenticSearchOptionFocused; + $[138] = onAgenticSearch; + $[139] = onToggleAllProjects; + $[140] = searchQuery; + $[141] = setSearchQuery; + $[142] = showAllProjects; + $[143] = showAllWorktrees; + $[144] = tagTabs; + $[145] = uniqueTags; + $[146] = viewMode; + $[147] = t53; + } else { + t53 = $[147]; + } + let t54; + if ($[148] === Symbol.for("react.memo_cache_sentinel")) { + t54 = { + isActive: true + }; + $[148] = t54; + } else { + t54 = $[148]; + } + useInput(t53, t54); + let filterIndicators; + if ($[149] !== branchFilterEnabled || $[150] !== currentBranch || $[151] !== hasMultipleWorktrees || $[152] !== showAllWorktrees) { + filterIndicators = []; + if (branchFilterEnabled && currentBranch) { + filterIndicators.push(currentBranch); + } + if (hasMultipleWorktrees && !showAllWorktrees) { + filterIndicators.push("current worktree"); + } + $[149] = branchFilterEnabled; + $[150] = currentBranch; + $[151] = hasMultipleWorktrees; + $[152] = showAllWorktrees; + $[153] = filterIndicators; + } else { + filterIndicators = $[153]; + } + const showAdditionalFilterLine = filterIndicators.length > 0 && viewMode !== "search"; + const headerLines = 8 + (showAdditionalFilterLine ? 1 : 0) + tagTabsLines; + const visibleCount = Math.max(1, Math.floor((maxHeight - headerLines - 2) / 3)); + let t55; + let t56; + if ($[154] !== displayedLogs.length || $[155] !== focusedIndex || $[156] !== onLoadMore || $[157] !== visibleCount) { + t55 = () => { + if (!onLoadMore) { + return; + } + const buffer = visibleCount * 2; + if (focusedIndex + buffer >= displayedLogs.length) { + onLoadMore(visibleCount * 3); + } + }; + t56 = [focusedIndex, visibleCount, displayedLogs.length, onLoadMore]; + $[154] = displayedLogs.length; + $[155] = focusedIndex; + $[156] = onLoadMore; + $[157] = visibleCount; + $[158] = t55; + $[159] = t56; + } else { + t55 = $[158]; + t56 = $[159]; + } + React.useEffect(t55, t56); + if (logs.length === 0) { + return null; + } + if (viewMode === "preview" && previewLog && isResumeWithRenameEnabled) { + let t57; + if ($[160] === Symbol.for("react.memo_cache_sentinel")) { + t57 = () => { + setViewMode("list"); + setPreviewLog(null); + }; + $[160] = t57; + } else { + t57 = $[160]; + } + let t58; + if ($[161] !== onSelect || $[162] !== previewLog) { + t58 = ; + $[161] = onSelect; + $[162] = previewLog; + $[163] = t58; + } else { + t58 = $[163]; + } + return t58; + } + const t57 = maxHeight - 1; + let t58; + if ($[164] === Symbol.for("react.memo_cache_sentinel")) { + t58 = ; + $[164] = t58; + } else { + t58 = $[164]; + } + let t59; + if ($[165] === Symbol.for("react.memo_cache_sentinel")) { + t59 = ; + $[165] = t59; + } else { + t59 = $[165]; + } + let t60; + if ($[166] !== columns || $[167] !== displayedLogs.length || $[168] !== effectiveTagIndex || $[169] !== focusedIndex || $[170] !== hasTags || $[171] !== showAllProjects || $[172] !== tagTabs || $[173] !== viewMode || $[174] !== visibleCount) { + t60 = hasTags ? : Resume Session{viewMode === "list" && displayedLogs.length > visibleCount && {" "}({focusedIndex} of {displayedLogs.length})}; + $[166] = columns; + $[167] = displayedLogs.length; + $[168] = effectiveTagIndex; + $[169] = focusedIndex; + $[170] = hasTags; + $[171] = showAllProjects; + $[172] = tagTabs; + $[173] = viewMode; + $[174] = visibleCount; + $[175] = t60; + } else { + t60 = $[175]; + } + const t61 = viewMode === "search"; + let t62; + if ($[176] !== isTerminalFocused || $[177] !== searchCursorOffset || $[178] !== searchQuery || $[179] !== t61) { + t62 = ; + $[176] = isTerminalFocused; + $[177] = searchCursorOffset; + $[178] = searchQuery; + $[179] = t61; + $[180] = t62; + } else { + t62 = $[180]; + } + let t63; + if ($[181] !== filterIndicators || $[182] !== viewMode) { + t63 = filterIndicators.length > 0 && viewMode !== "search" && {filterIndicators}; + $[181] = filterIndicators; + $[182] = viewMode; + $[183] = t63; + } else { + t63 = $[183]; + } + let t64; + if ($[184] === Symbol.for("react.memo_cache_sentinel")) { + t64 = ; + $[184] = t64; + } else { + t64 = $[184]; + } + let t65; + if ($[185] !== agenticSearchState.status) { + t65 = agenticSearchState.status === "searching" && Searching…; + $[185] = agenticSearchState.status; + $[186] = t65; + } else { + t65 = $[186]; + } + let t66; + if ($[187] !== agenticSearchState.results || $[188] !== agenticSearchState.status) { + t66 = agenticSearchState.status === "results" && agenticSearchState.results.length > 0 && Claude found these results:; + $[187] = agenticSearchState.results; + $[188] = agenticSearchState.status; + $[189] = t66; + } else { + t66 = $[189]; + } + let t67; + if ($[190] !== agenticSearchState.results || $[191] !== agenticSearchState.status || $[192] !== filteredLogs) { + t67 = agenticSearchState.status === "results" && agenticSearchState.results.length === 0 && filteredLogs.length === 0 && No matching sessions found.; + $[190] = agenticSearchState.results; + $[191] = agenticSearchState.status; + $[192] = filteredLogs; + $[193] = t67; + } else { + t67 = $[193]; + } + let t68; + if ($[194] !== agenticSearchState.status || $[195] !== filteredLogs) { + t68 = agenticSearchState.status === "error" && filteredLogs.length === 0 && No matching sessions found.; + $[194] = agenticSearchState.status; + $[195] = filteredLogs; + $[196] = t68; + } else { + t68 = $[196]; + } + let t69; + if ($[197] !== agenticSearchState.status || $[198] !== isAgenticSearchOptionFocused || $[199] !== onAgenticSearch || $[200] !== searchQuery) { + t69 = Boolean(searchQuery.trim()) && onAgenticSearch && false && agenticSearchState.status !== "searching" && agenticSearchState.status !== "results" && agenticSearchState.status !== "error" && {isAgenticSearchOptionFocused ? figures.pointer : " "}Search deeply using Claude →; + $[197] = agenticSearchState.status; + $[198] = isAgenticSearchOptionFocused; + $[199] = onAgenticSearch; + $[200] = searchQuery; + $[201] = t69; + } else { + t69 = $[201]; + } + let t70; + if ($[202] !== agenticSearchState.status || $[203] !== branchFilterEnabled || $[204] !== columns || $[205] !== displayedLogs || $[206] !== expandedGroupSessionIds || $[207] !== flatOptions || $[208] !== focusedLog || $[209] !== focusedNode?.id || $[210] !== handleFlatOptionsSelectFocus || $[211] !== handleRenameSubmit || $[212] !== handleTreeSelectFocus || $[213] !== isAgenticSearchOptionFocused || $[214] !== onCancel || $[215] !== onSelect || $[216] !== renameCursorOffset || $[217] !== renameValue || $[218] !== treeNodes || $[219] !== viewMode || $[220] !== visibleCount) { + t70 = agenticSearchState.status === "searching" ? null : viewMode === "rename" && focusedLog ? Rename session: : isResumeWithRenameEnabled ? { + onSelect(node_0.value.log); + }} onFocus={handleTreeSelectFocus} onCancel={onCancel} focusNodeId={focusedNode?.id} visibleOptionCount={visibleCount} layout="expanded" isDisabled={viewMode === "search" || isAgenticSearchOptionFocused} hideIndexes={false} isNodeExpanded={nodeId => { + if (viewMode === "search" || branchFilterEnabled) { + return true; + } + const sessionId_2 = typeof nodeId === "string" && nodeId.startsWith("group:") ? nodeId.substring(6) : null; + return sessionId_2 ? expandedGroupSessionIds.has(sessionId_2) : false; + }} onExpand={nodeId_0 => { + const sessionId_3 = typeof nodeId_0 === "string" && nodeId_0.startsWith("group:") ? nodeId_0.substring(6) : null; + if (sessionId_3) { + setExpandedGroupSessionIds(prev_0 => new Set(prev_0).add(sessionId_3)); + logEvent("tengu_session_group_expanded", {}); + } + }} onCollapse={nodeId_1 => { + const sessionId_4 = typeof nodeId_1 === "string" && nodeId_1.startsWith("group:") ? nodeId_1.substring(6) : null; + if (sessionId_4) { + setExpandedGroupSessionIds(prev_1 => { + const newSet = new Set(prev_1); + newSet.delete(sessionId_4); + return newSet; + }); + } + }} onUpFromFirstItem={enterSearchMode} /> : onResponse('no')} /> + + + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJTZWxlY3QiLCJQZXJtaXNzaW9uRGlhbG9nIiwiUHJvcHMiLCJwbHVnaW5OYW1lIiwicGx1Z2luRGVzY3JpcHRpb24iLCJmaWxlRXh0ZW5zaW9uIiwib25SZXNwb25zZSIsInJlc3BvbnNlIiwiQVVUT19ESVNNSVNTX01TIiwiTHNwUmVjb21tZW5kYXRpb25NZW51IiwiUmVhY3ROb2RlIiwib25SZXNwb25zZVJlZiIsInVzZVJlZiIsImN1cnJlbnQiLCJ1c2VFZmZlY3QiLCJ0aW1lb3V0SWQiLCJzZXRUaW1lb3V0IiwicmVmIiwiY2xlYXJUaW1lb3V0Iiwib25TZWxlY3QiLCJ2YWx1ZSIsIm9wdGlvbnMiLCJsYWJlbCJdLCJzb3VyY2VzIjpbIkxzcFJlY29tbWVuZGF0aW9uTWVudS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi9DdXN0b21TZWxlY3Qvc2VsZWN0LmpzJ1xuaW1wb3J0IHsgUGVybWlzc2lvbkRpYWxvZyB9IGZyb20gJy4uL3Blcm1pc3Npb25zL1Blcm1pc3Npb25EaWFsb2cuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHBsdWdpbk5hbWU6IHN0cmluZ1xuICBwbHVnaW5EZXNjcmlwdGlvbj86IHN0cmluZ1xuICBmaWxlRXh0ZW5zaW9uOiBzdHJpbmdcbiAgb25SZXNwb25zZTogKHJlc3BvbnNlOiAneWVzJyB8ICdubycgfCAnbmV2ZXInIHwgJ2Rpc2FibGUnKSA9PiB2b2lkXG59XG5cbmNvbnN0IEFVVE9fRElTTUlTU19NUyA9IDMwXzAwMFxuXG5leHBvcnQgZnVuY3Rpb24gTHNwUmVjb21tZW5kYXRpb25NZW51KHtcbiAgcGx1Z2luTmFtZSxcbiAgcGx1Z2luRGVzY3JpcHRpb24sXG4gIGZpbGVFeHRlbnNpb24sXG4gIG9uUmVzcG9uc2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIFVzZSByZWYgdG8gYXZvaWQgdGltZXIgcmVzZXQgd2hlbiBvblJlc3BvbnNlIGNoYW5nZXNcbiAgY29uc3Qgb25SZXNwb25zZVJlZiA9IFJlYWN0LnVzZVJlZihvblJlc3BvbnNlKVxuICBvblJlc3BvbnNlUmVmLmN1cnJlbnQgPSBvblJlc3BvbnNlXG5cbiAgLy8gMzAtc2Vjb25kIGF1dG8tZGlzbWlzcyB0aW1lciAtIGNvdW50cyBhcyBpZ25vcmVkIChubylcbiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHtcbiAgICBjb25zdCB0aW1lb3V0SWQgPSBzZXRUaW1lb3V0KFxuICAgICAgcmVmID0+IHJlZi5jdXJyZW50KCdubycpLFxuICAgICAgQVVUT19ESVNNSVNTX01TLFxuICAgICAgb25SZXNwb25zZVJlZixcbiAgICApXG4gICAgcmV0dXJuICgpID0+IGNsZWFyVGltZW91dCh0aW1lb3V0SWQpXG4gIH0sIFtdKVxuXG4gIGZ1bmN0aW9uIG9uU2VsZWN0KHZhbHVlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBzd2l0Y2ggKHZhbHVlKSB7XG4gICAgICBjYXNlICd5ZXMnOlxuICAgICAgICBvblJlc3BvbnNlKCd5ZXMnKVxuICAgICAgICBicmVha1xuICAgICAgY2FzZSAnbm8nOlxuICAgICAgICBvblJlc3BvbnNlKCdubycpXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICduZXZlcic6XG4gICAgICAgIG9uUmVzcG9uc2UoJ25ldmVyJylcbiAgICAgICAgYnJlYWtcbiAgICAgIGNhc2UgJ2Rpc2FibGUnOlxuICAgICAgICBvblJlc3BvbnNlKCdkaXNhYmxlJylcbiAgICAgICAgYnJlYWtcbiAgICB9XG4gIH1cblxuICBjb25zdCBvcHRpb25zID0gW1xuICAgIHtcbiAgICAgIGxhYmVsOiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIFllcywgaW5zdGFsbCA8VGV4dCBib2xkPntwbHVnaW5OYW1lfTwvVGV4dD5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKSxcbiAgICAgIHZhbHVlOiAneWVzJyxcbiAgICB9LFxuICAgIHtcbiAgICAgIGxhYmVsOiAnTm8sIG5vdCBub3cnLFxuICAgICAgdmFsdWU6ICdubycsXG4gICAgfSxcbiAgICB7XG4gICAgICBsYWJlbDogKFxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICBOZXZlciBmb3IgPFRleHQgYm9sZD57cGx1Z2luTmFtZX08L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICksXG4gICAgICB2YWx1ZTogJ25ldmVyJyxcbiAgICB9LFxuICAgIHtcbiAgICAgIGxhYmVsOiAnRGlzYWJsZSBhbGwgTFNQIHJlY29tbWVuZGF0aW9ucycsXG4gICAgICB2YWx1ZTogJ2Rpc2FibGUnLFxuICAgIH0sXG4gIF1cblxuICByZXR1cm4gKFxuICAgIDxQZXJtaXNzaW9uRGlhbG9nIHRpdGxlPVwiTFNQIFBsdWdpbiBSZWNvbW1lbmRhdGlvblwiPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgcGFkZGluZ1g9ezJ9IHBhZGRpbmdZPXsxfT5cbiAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgTFNQIHByb3ZpZGVzIGNvZGUgaW50ZWxsaWdlbmNlIGxpa2UgZ28tdG8tZGVmaW5pdGlvbiBhbmQgZXJyb3JcbiAgICAgICAgICAgIGNoZWNraW5nXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5QbHVnaW46PC9UZXh0PlxuICAgICAgICAgIDxUZXh0PiB7cGx1Z2luTmFtZX08L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7cGx1Z2luRGVzY3JpcHRpb24gJiYgKFxuICAgICAgICAgIDxCb3g+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57cGx1Z2luRGVzY3JpcHRpb259PC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApfVxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlRyaWdnZXJlZCBieTo8L1RleHQ+XG4gICAgICAgICAgPFRleHQ+IHtmaWxlRXh0ZW5zaW9ufSBmaWxlczwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dD5Xb3VsZCB5b3UgbGlrZSB0byBpbnN0YWxsIHRoaXMgTFNQIHBsdWdpbj88L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxTZWxlY3RcbiAgICAgICAgICAgIG9wdGlvbnM9e29wdGlvbnN9XG4gICAgICAgICAgICBvbkNoYW5nZT17b25TZWxlY3R9XG4gICAgICAgICAgICBvbkNhbmNlbD17KCkgPT4gb25SZXNwb25zZSgnbm8nKX1cbiAgICAgICAgICAvPlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuICAgIDwvUGVybWlzc2lvbkRpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsU0FBU0MsTUFBTSxRQUFRLDJCQUEyQjtBQUNsRCxTQUFTQyxnQkFBZ0IsUUFBUSxvQ0FBb0M7QUFFckUsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLFVBQVUsRUFBRSxNQUFNO0VBQ2xCQyxpQkFBaUIsQ0FBQyxFQUFFLE1BQU07RUFDMUJDLGFBQWEsRUFBRSxNQUFNO0VBQ3JCQyxVQUFVLEVBQUUsQ0FBQ0MsUUFBUSxFQUFFLEtBQUssR0FBRyxJQUFJLEdBQUcsT0FBTyxHQUFHLFNBQVMsRUFBRSxHQUFHLElBQUk7QUFDcEUsQ0FBQztBQUVELE1BQU1DLGVBQWUsR0FBRyxNQUFNO0FBRTlCLE9BQU8sU0FBU0MscUJBQXFCQSxDQUFDO0VBQ3BDTixVQUFVO0VBQ1ZD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 \ No newline at end of file diff --git a/components/MCPServerApprovalDialog.tsx b/components/MCPServerApprovalDialog.tsx new file mode 100644 index 0000000..f75abcc --- /dev/null +++ b/components/MCPServerApprovalDialog.tsx @@ -0,0 +1,115 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { getSettings_DEPRECATED, updateSettingsForSource } from '../utils/settings/settings.js'; +import { Select } from './CustomSelect/index.js'; +import { Dialog } from './design-system/Dialog.js'; +import { MCPServerDialogCopy } from './MCPServerDialogCopy.js'; +type Props = { + serverName: string; + onDone(): void; +}; +export function MCPServerApprovalDialog(t0) { + const $ = _c(13); + const { + serverName, + onDone + } = t0; + let t1; + if ($[0] !== onDone || $[1] !== serverName) { + t1 = function onChange(value) { + logEvent("tengu_mcp_dialog_choice", { + choice: value as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + bb2: switch (value) { + case "yes": + case "yes_all": + { + const currentSettings_0 = getSettings_DEPRECATED() || {}; + const enabledServers = currentSettings_0.enabledMcpjsonServers || []; + if (!enabledServers.includes(serverName)) { + updateSettingsForSource("localSettings", { + enabledMcpjsonServers: [...enabledServers, serverName] + }); + } + if (value === "yes_all") { + updateSettingsForSource("localSettings", { + enableAllProjectMcpServers: true + }); + } + onDone(); + break bb2; + } + case "no": + { + const currentSettings = getSettings_DEPRECATED() || {}; + const disabledServers = currentSettings.disabledMcpjsonServers || []; + if (!disabledServers.includes(serverName)) { + updateSettingsForSource("localSettings", { + disabledMcpjsonServers: [...disabledServers, serverName] + }); + } + onDone(); + } + } + }; + $[0] = onDone; + $[1] = serverName; + $[2] = t1; + } else { + t1 = $[2]; + } + const onChange = t1; + const t2 = `New MCP server found in .mcp.json: ${serverName}`; + let t3; + if ($[3] !== onChange) { + t3 = () => onChange("no"); + $[3] = onChange; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t4 = ; + $[5] = t4; + } else { + t4 = $[5]; + } + let t5; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t5 = [{ + label: "Use this and all future MCP servers in this project", + value: "yes_all" + }, { + label: "Use this MCP server", + value: "yes" + }, { + label: "Continue without using this MCP server", + value: "no" + }]; + $[6] = t5; + } else { + t5 = $[6]; + } + let t6; + if ($[7] !== onChange) { + t6 = onChange(value_0 as 'accept' | 'exit')} onCancel={() => onChange("exit")} />; + $[12] = onChange; + $[13] = t16; + } else { + t16 = $[13]; + } + let t17; + if ($[14] !== exitState.keyName || $[15] !== exitState.pending) { + t17 = {exitState.pending ? <>Press {exitState.keyName} again to exit : <>Enter to confirm · Esc to exit}; + $[14] = exitState.keyName; + $[15] = exitState.pending; + $[16] = t17; + } else { + t17 = $[16]; + } + let t18; + if ($[17] !== T1 || $[18] !== t13 || $[19] !== t16 || $[20] !== t17 || $[21] !== t9) { + t18 = {t9}{t13}{t14}{t16}{t17}; + $[17] = T1; + $[18] = t13; + $[19] = t16; + $[20] = t17; + $[21] = t9; + $[22] = t18; + } else { + t18 = $[22]; + } + let t19; + if ($[23] !== T0 || $[24] !== t18) { + t19 = {t18}; + $[23] = T0; + $[24] = t18; + $[25] = t19; + } else { + t19 = $[25]; + } + return t19; +} +function _temp(item, index) { + return · {item}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncyIsIkJveCIsIlRleHQiLCJ1c2VLZXliaW5kaW5nIiwiU2V0dGluZ3NKc29uIiwiU2VsZWN0IiwiUGVybWlzc2lvbkRpYWxvZyIsImV4dHJhY3REYW5nZXJvdXNTZXR0aW5ncyIsImZvcm1hdERhbmdlcm91c1NldHRpbmdzTGlzdCIsIlByb3BzIiwic2V0dGluZ3MiLCJvbkFjY2VwdCIsIm9uUmVqZWN0IiwiTWFuYWdlZFNldHRpbmdzU2VjdXJpdHlEaWFsb2ciLCJ0MCIsIiQiLCJfYyIsImRhbmdlcm91cyIsInNldHRpbmdzTGlzdCIsImV4aXRTdGF0ZSIsInQxIiwiU3ltYm9sIiwiZm9yIiwiY29udGV4dCIsInQyIiwib25DaGFuZ2UiLCJ2YWx1ZSIsIlQwIiwidDMiLCJ0NCIsInQ1IiwiVDEiLCJ0NiIsInQ3IiwidDgiLCJ0OSIsIlQyIiwidDEwIiwidDExIiwidDEyIiwibWFwIiwiX3RlbXAiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJsYWJlbCIsInQxNiIsInZhbHVlXzAiLCJ0MTciLCJrZXlOYW1lIiwicGVuZGluZyIsInQxOCIsInQxOSIsIml0ZW0iLCJpbmRleCJdLCJzb3VyY2VzIjpbIk1hbmFnZWRTZXR0aW5nc1NlY3VyaXR5RGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MgfSBmcm9tICcuLi8uLi9ob29rcy91c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB0eXBlIHsgU2V0dGluZ3NKc29uIH0gZnJvbSAnLi4vLi4vdXRpbHMvc2V0dGluZ3MvdHlwZXMuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi9DdXN0b21TZWxlY3QvaW5kZXguanMnXG5pbXBvcnQgeyBQZXJtaXNzaW9uRGlhbG9nIH0gZnJvbSAnLi4vcGVybWlzc2lvbnMvUGVybWlzc2lvbkRpYWxvZy5qcydcbmltcG9ydCB7XG4gIGV4dHJhY3REYW5nZXJvdXNTZXR0aW5ncyxcbiAgZm9ybWF0RGFuZ2Vyb3VzU2V0dGluZ3NMaXN0LFxufSBmcm9tICcuL3V0aWxzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBzZXR0aW5nczogU2V0dGluZ3NKc29uXG4gIG9uQWNjZXB0OiAoKSA9PiB2b2lkXG4gIG9uUmVqZWN0OiAoKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBNYW5hZ2VkU2V0dGluZ3NTZWN1cml0eURpYWxvZyh7XG4gIHNldHRpbmdzLFxuICBvbkFjY2VwdCxcbiAgb25SZWplY3QsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGRhbmdlcm91cyA9IGV4dHJhY3REYW5nZXJvdXNTZXR0aW5ncyhzZXR0aW5ncylcbiAgY29uc3Qgc2V0dGluZ3NMaXN0ID0gZm9ybWF0RGFuZ2Vyb3VzU2V0dGluZ3NMaXN0KGRhbmdlcm91cylcblxuICBjb25zdCBleGl0U3RhdGUgPSB1c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MoKVxuXG4gIHVzZUtleWJpbmRpbmcoJ2NvbmZpcm06bm8nLCBvblJlamVjdCwgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJyB9KVxuXG4gIGZ1bmN0aW9uIG9uQ2hhbmdlKHZhbHVlOiAnYWNjZXB0JyB8ICdleGl0Jyk6IHZvaWQge1xuICAgIGlmICh2YWx1ZSA9PT0gJ2V4aXQnKSB7XG4gICAgICBvblJlamVjdCgpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgb25BY2NlcHQoKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8UGVybWlzc2lvbkRpYWxvZ1xuICAgICAgY29sb3I9XCJ3YXJuaW5nXCJcbiAgICAgIHRpdGxlQ29sb3I9XCJ3YXJuaW5nXCJcbiAgICAgIHRpdGxlPVwiTWFuYWdlZCBzZXR0aW5ncyByZXF1aXJlIGFwcHJvdmFsXCJcbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9IHBhZGRpbmdUb3A9ezF9PlxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICBZb3VyIG9yZ2FuaXphdGlvbiBoYXMgY29uZmlndXJlZCBtYW5hZ2VkIHNldHRpbmdzIHRoYXQgY291bGQgYWxsb3dcbiAgICAgICAgICBleGVjdXRpb24gb2YgYXJiaXRyYXJ5IGNvZGUgb3IgaW50ZXJjZXB0aW9uIG9mIHlvdXIgcHJvbXB0cyBhbmRcbiAgICAgICAgICByZXNwb25zZXMuXG4gICAgICAgIDwvVGV4dD5cblxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5TZXR0aW5ncyByZXF1aXJpbmcgYXBwcm92YWw6PC9UZXh0PlxuICAgICAgICAgIHtzZXR0aW5nc0xpc3QubWFwKChpdGVtLCBpbmRleCkgPT4gKFxuICAgICAgICAgICAgPEJveCBrZXk9e2luZGV4fSBwYWRkaW5nTGVmdD17Mn0+XG4gICAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPsK3IDwvVGV4dD5cbiAgICAgICAgICAgICAgICA8VGV4dD57aXRlbX08L1RleHQ+XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICkpfVxuICAgICAgICA8L0JveD5cblxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICBPbmx5IGFjY2VwdCBpZiB5b3UgdHJ1c3QgeW91ciBvcmdhbml6YXRpb24mYXBvcztzIElUIGFkbWluaXN0cmF0aW9uXG4gICAgICAgICAgYW5kIGV4cGVjdCB0aGVzZSBzZXR0aW5ncyB0byBiZSBjb25maWd1cmVkLlxuICAgICAgICA8L1RleHQ+XG5cbiAgICAgICAgPFNlbGVjdFxuICAgICAgICAgIG9wdGlvbnM9e1tcbiAgICAgICAgICAgIHsgbGFiZWw6ICdZZXMsIEkgdHJ1c3QgdGhlc2Ugc2V0dGluZ3MnLCB2YWx1ZTogJ2FjY2VwdCcgfSxcbiAgICAgICAgICAgIHsgbGFiZWw6ICdObywgZXhpdCBDbGF1ZGUgQ29kZScsIHZhbHVlOiAnZXhpdCcgfSxcbiAgICAgICAgICBdfVxuICAgICAgICAgIG9uQ2hhbmdlPXt2YWx1ZSA9PiBvbkNoYW5nZSh2YWx1ZSBhcyAnYWNjZXB0JyB8ICdleGl0Jyl9XG4gICAgICAgICAgb25DYW5jZWw9eygpID0+IG9uQ2hhbmdlKCdleGl0Jyl9XG4gICAgICAgIC8+XG5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAge2V4aXRTdGF0ZS5wZW5kaW5nID8gKFxuICAgICAgICAgICAgPD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8Lz5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPD5FbnRlciB0byBjb25maXJtIMK3IEVzYyB0byBleGl0PC8+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgPC9QZXJtaXNzaW9uRGlhbG9nPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyw4QkFBOEIsUUFBUSwrQ0FBK0M7QUFDOUYsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxhQUFhLFFBQVEsb0NBQW9DO0FBQ2xFLGNBQWNDLFlBQVksUUFBUSwrQkFBK0I7QUFDakUsU0FBU0MsTUFBTSxRQUFRLDBCQUEwQjtBQUNqRCxTQUFTQyxnQkFBZ0IsUUFBUSxvQ0FBb0M7QUFDckUsU0FDRUMsd0JBQXdCLEVBQ3hCQywyQkFBMkIsUUFDdEIsWUFBWTtBQUVuQixLQUFLQyxLQUFLLEdBQUc7RUFDWEMsUUFBUSxFQUFFTixZQUFZO0VB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 \ No newline at end of file diff --git a/components/ManagedSettingsSecurityDialog/utils.ts b/components/ManagedSettingsSecurityDialog/utils.ts new file mode 100644 index 0000000..0f4cccb --- /dev/null +++ b/components/ManagedSettingsSecurityDialog/utils.ts @@ -0,0 +1,144 @@ +import { + DANGEROUS_SHELL_SETTINGS, + SAFE_ENV_VARS, +} from '../../utils/managedEnvConstants.js' +import type { SettingsJson } from '../../utils/settings/types.js' +import { jsonStringify } from '../../utils/slowOperations.js' + +type DangerousShellSetting = (typeof DANGEROUS_SHELL_SETTINGS)[number] + +export type DangerousSettings = { + shellSettings: Partial> + envVars: Record + hasHooks: boolean + hooks?: unknown +} + +/** + * Extract dangerous settings from a settings object. + * + * Dangerous env vars are determined by checking against SAFE_ENV_VARS - + * any env var NOT in SAFE_ENV_VARS is considered dangerous. + * See managedEnv.ts for the authoritative list and threat categories. + */ +export function extractDangerousSettings( + settings: SettingsJson | null | undefined, +): DangerousSettings { + if (!settings) { + return { + shellSettings: {}, + envVars: {}, + hasHooks: false, + } + } + + // Extract dangerous shell settings + const shellSettings: Partial> = {} + for (const key of DANGEROUS_SHELL_SETTINGS) { + const value = settings[key] + if (typeof value === 'string' && value.length > 0) { + shellSettings[key] = value + } + } + + // Extract dangerous env vars - any var NOT in SAFE_ENV_VARS is dangerous + const envVars: Record = {} + if (settings.env && typeof settings.env === 'object') { + for (const [key, value] of Object.entries(settings.env)) { + if (typeof value === 'string' && value.length > 0) { + // Check if this env var is NOT in the safe list + if (!SAFE_ENV_VARS.has(key.toUpperCase())) { + envVars[key] = value + } + } + } + } + + // Check for hooks + const hasHooks = + settings.hooks !== undefined && + settings.hooks !== null && + typeof settings.hooks === 'object' && + Object.keys(settings.hooks).length > 0 + + return { + shellSettings, + envVars, + hasHooks, + hooks: hasHooks ? settings.hooks : undefined, + } +} + +/** + * Check if settings contain any dangerous settings + */ +export function hasDangerousSettings(dangerous: DangerousSettings): boolean { + return ( + Object.keys(dangerous.shellSettings).length > 0 || + Object.keys(dangerous.envVars).length > 0 || + dangerous.hasHooks + ) +} + +/** + * Compare two sets of dangerous settings to see if the new settings + * have changed or added dangerous settings compared to the old settings + */ +export function hasDangerousSettingsChanged( + oldSettings: SettingsJson | null | undefined, + newSettings: SettingsJson | null | undefined, +): boolean { + const oldDangerous = extractDangerousSettings(oldSettings) + const newDangerous = extractDangerousSettings(newSettings) + + // If new settings don't have any dangerous settings, no prompt needed + if (!hasDangerousSettings(newDangerous)) { + return false + } + + // If old settings didn't have dangerous settings but new does, prompt needed + if (!hasDangerousSettings(oldDangerous)) { + return true + } + + // Compare the dangerous settings - any change triggers a prompt + const oldJson = jsonStringify({ + shellSettings: oldDangerous.shellSettings, + envVars: oldDangerous.envVars, + hooks: oldDangerous.hooks, + }) + const newJson = jsonStringify({ + shellSettings: newDangerous.shellSettings, + envVars: newDangerous.envVars, + hooks: newDangerous.hooks, + }) + + return oldJson !== newJson +} + +/** + * Format dangerous settings as a human-readable list for the UI + * Only returns setting names, not values + */ +export function formatDangerousSettingsList( + dangerous: DangerousSettings, +): string[] { + const items: string[] = [] + + // Shell settings (names only) + for (const key of Object.keys(dangerous.shellSettings)) { + items.push(key) + } + + // Env vars (names only) + for (const key of Object.keys(dangerous.envVars)) { + items.push(key) + } + + // Hooks + if (dangerous.hasHooks) { + items.push('hooks') + } + + return items +} diff --git a/components/Markdown.tsx b/components/Markdown.tsx new file mode 100644 index 0000000..e82f4c7 --- /dev/null +++ b/components/Markdown.tsx @@ -0,0 +1,236 @@ +import { c as _c } from "react/compiler-runtime"; +import { marked, type Token, type Tokens } from 'marked'; +import React, { Suspense, use, useMemo, useRef } from 'react'; +import { useSettings } from '../hooks/useSettings.js'; +import { Ansi, Box, useTheme } from '../ink.js'; +import { type CliHighlight, getCliHighlightPromise } from '../utils/cliHighlight.js'; +import { hashContent } from '../utils/hash.js'; +import { configureMarked, formatToken } from '../utils/markdown.js'; +import { stripPromptXMLTags } from '../utils/messages.js'; +import { MarkdownTable } from './MarkdownTable.js'; +type Props = { + children: string; + /** When true, render all text content as dim */ + dimColor?: boolean; +}; + +// Module-level token cache — marked.lexer is the hot cost on virtual-scroll +// remounts (~3ms per message). useMemo doesn't survive unmount→remount, so +// scrolling back to a previously-visible message re-parses. Messages are +// immutable in history; same content → same tokens. Keyed by hash to avoid +// retaining full content strings (turn50→turn99 RSS regression, #24180). +const TOKEN_CACHE_MAX = 500; +const tokenCache = new Map(); + +// Characters that indicate markdown syntax. If none are present, skip the +// ~3ms marked.lexer call entirely — render as a single paragraph. Covers +// the majority of short assistant responses and user prompts that are +// plain sentences. Checked via indexOf (not regex) for speed. +// Single regex: matches any MD marker or ordered-list start (N. at line start). +// One pass instead of 10× includes scans. +const MD_SYNTAX_RE = /[#*`|[>\-_~]|\n\n|^\d+\. |\n\d+\. /; +function hasMarkdownSyntax(s: string): boolean { + // Sample first 500 chars — if markdown exists it's usually early (headers, + // code fence, list). Long tool outputs are mostly plain text tails. + return MD_SYNTAX_RE.test(s.length > 500 ? s.slice(0, 500) : s); +} +function cachedLexer(content: string): Token[] { + // Fast path: plain text with no markdown syntax → single paragraph token. + // Skips marked.lexer's full GFM parse (~3ms on long content). Not cached — + // reconstruction is a single object allocation, and caching would retain + // 4× content in raw/text fields plus the hash key for zero benefit. + if (!hasMarkdownSyntax(content)) { + return [{ + type: 'paragraph', + raw: content, + text: content, + tokens: [{ + type: 'text', + raw: content, + text: content + }] + } as Token]; + } + const key = hashContent(content); + const hit = tokenCache.get(key); + if (hit) { + // Promote to MRU — without this the eviction is FIFO (scrolling back to + // an early message evicts the very item you're looking at). + tokenCache.delete(key); + tokenCache.set(key, hit); + return hit; + } + const tokens = marked.lexer(content); + if (tokenCache.size >= TOKEN_CACHE_MAX) { + // LRU-ish: drop oldest. Map preserves insertion order. + const first = tokenCache.keys().next().value; + if (first !== undefined) tokenCache.delete(first); + } + tokenCache.set(key, tokens); + return tokens; +} + +/** + * Renders markdown content using a hybrid approach: + * - Tables are rendered as React components with proper flexbox layout + * - Other content is rendered as ANSI strings via formatToken + */ +export function Markdown(props) { + const $ = _c(4); + const settings = useSettings(); + if (settings.syntaxHighlightingDisabled) { + let t0; + if ($[0] !== props) { + t0 = ; + $[0] = props; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; + } + let t0; + if ($[2] !== props) { + t0 = }>; + $[2] = props; + $[3] = t0; + } else { + t0 = $[3]; + } + return t0; +} +function MarkdownWithHighlight(props) { + const $ = _c(4); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = getCliHighlightPromise(); + $[0] = t0; + } else { + t0 = $[0]; + } + const highlight = use(t0); + let t1; + if ($[1] !== highlight || $[2] !== props) { + t1 = ; + $[1] = highlight; + $[2] = props; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; +} +function MarkdownBody(t0) { + const $ = _c(7); + const { + children, + dimColor, + highlight + } = t0; + const [theme] = useTheme(); + configureMarked(); + let elements; + if ($[0] !== children || $[1] !== dimColor || $[2] !== highlight || $[3] !== theme) { + const tokens = cachedLexer(stripPromptXMLTags(children)); + elements = []; + let nonTableContent = ""; + const flushNonTableContent = function flushNonTableContent() { + if (nonTableContent) { + elements.push({nonTableContent.trim()}); + nonTableContent = ""; + } + }; + for (const token of tokens) { + if (token.type === "table") { + flushNonTableContent(); + elements.push(); + } else { + nonTableContent = nonTableContent + formatToken(token, theme, 0, null, null, highlight); + nonTableContent; + } + } + flushNonTableContent(); + $[0] = children; + $[1] = dimColor; + $[2] = highlight; + $[3] = theme; + $[4] = elements; + } else { + elements = $[4]; + } + const elements_0 = elements; + let t1; + if ($[5] !== elements_0) { + t1 = {elements_0}; + $[5] = elements_0; + $[6] = t1; + } else { + t1 = $[6]; + } + return t1; +} +type StreamingProps = { + children: string; +}; + +/** + * Renders markdown during streaming by splitting at the last top-level block + * boundary: everything before is stable (memoized, never re-parsed), only the + * final block is re-parsed per delta. marked.lexer() correctly handles + * unclosed code fences as a single token, so block boundaries are always safe. + * + * The stable boundary only advances (monotonic), so ref mutation during render + * is idempotent and safe under StrictMode double-rendering. Component unmounts + * between turns (streamingText → null), resetting the ref. + */ +export function StreamingMarkdown({ + children +}: StreamingProps): React.ReactNode { + // React Compiler: this component reads and writes stablePrefixRef.current + // during render by design. The boundary only advances (monotonic), so + // the ref mutation is idempotent under StrictMode double-render — but the + // compiler can't prove that, and memoizing around the ref reads would + // break the algorithm (stale boundary). Opt out. + 'use no memo'; + + configureMarked(); + + // Strip before boundary tracking so it matches 's stripping + // (line 29). When a closing tag arrives, stripped(N+1) is not a prefix + // of stripped(N), but the startsWith reset below handles that with a + // one-time re-lex on the smaller stripped string. + const stripped = stripPromptXMLTags(children); + const stablePrefixRef = useRef(''); + + // Reset if text was replaced (defensive; normally unmount handles this) + if (!stripped.startsWith(stablePrefixRef.current)) { + stablePrefixRef.current = ''; + } + + // Lex only from current boundary — O(unstable length), not O(full text) + const boundary = stablePrefixRef.current.length; + const tokens = marked.lexer(stripped.substring(boundary)); + + // Last non-space token is the growing block; everything before is final + let lastContentIdx = tokens.length - 1; + while (lastContentIdx >= 0 && tokens[lastContentIdx]!.type === 'space') { + lastContentIdx--; + } + let advance = 0; + for (let i = 0; i < lastContentIdx; i++) { + advance += tokens[i]!.raw.length; + } + if (advance > 0) { + stablePrefixRef.current = stripped.substring(0, boundary + advance); + } + const stablePrefix = stablePrefixRef.current; + const unstableSuffix = stripped.substring(stablePrefix.length); + + // stablePrefix is memoized inside via useMemo([children, ...]) + // so it never re-parses as the unstable suffix grows + return + {stablePrefix && {stablePrefix}} + {unstableSuffix && {unstableSuffix}} + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJtYXJrZWQiLCJUb2tlbiIsIlRva2VucyIsIlJlYWN0IiwiU3VzcGVuc2UiLCJ1c2UiLCJ1c2VNZW1vIiwidXNlUmVmIiwidXNlU2V0dGluZ3MiLCJBbnNpIiwiQm94IiwidXNlVGhlbWUiLCJDbGlIaWdobGlnaHQiLCJnZXRDbGlIaWdobGlnaHRQcm9taXNlIiwiaGFzaENvbnRlbnQiLCJjb25maWd1cmVNYXJrZWQiLCJmb3JtYXRUb2tlbiIsInN0cmlwUHJvbXB0WE1MVGFncyIsIk1hcmtkb3duVGFibGUiLCJQcm9wcyIsImNoaWxkcmVuIiwiZGltQ29sb3IiLCJUT0tFTl9DQUNIRV9NQVgiLCJ0b2tlbkNhY2hlIiwiTWFwIiwiTURfU1lOVEFYX1JFIiwiaGFzTWFya2Rvd25TeW50YXgiLCJzIiwidGVzdCIsImxlbmd0aCIsInNsaWNlIiwiY2FjaGVkTGV4ZXIiLCJjb250ZW50IiwidHlwZSIsInJhdyIsInRleHQiLCJ0b2tlbnMiLCJrZXkiLCJoaXQiLCJnZXQiLCJkZWxldGUiLCJzZXQiLCJsZXhlciIsInNpemUiLCJmaXJzdCIsImtleXMiLCJuZXh0IiwidmFsdWUiLCJ1bmRlZmluZWQiLCJNYXJrZG93biIsInByb3BzIiwiJCIsIl9jIiwic2V0dGluZ3MiLCJzeW50YXhIaWdobGlnaHRpbmdEaXNhYmxlZCIsInQwIiwiTWFya2Rvd25XaXRoSGlnaGxpZ2h0IiwiU3ltYm9sIiwiZm9yIiwiaGlnaGxpZ2h0IiwidDEiLCJNYXJrZG93bkJvZHkiLCJ0aGVtZSIsImVsZW1lbnRzIiwibm9uVGFibGVDb250ZW50IiwiZmx1c2hOb25UYWJsZUNvbnRlbnQiLCJwdXNoIiwidHJpbSIsInRva2VuIiwiVGFibGUiLCJlbGVtZW50c18wIiwiU3RyZWFtaW5nUHJvcHMiLCJTdHJlYW1pbmdNYXJrZG93biIsIlJlYWN0Tm9kZSIsInN0cmlwcGVkIiwic3RhYmxlUHJlZml4UmVmIiwic3RhcnRzV2l0aCIsImN1cnJlbnQiLCJib3VuZGFyeSIsInN1YnN0cmluZyIsImxhc3RDb250ZW50SWR4IiwiYWR2YW5jZSIsImkiLCJzdGFibGVQcmVmaXgiLCJ1bnN0YWJsZVN1ZmZpeCJdLCJzb3VyY2VzIjpbIk1hcmtkb3duLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBtYXJrZWQsIHR5cGUgVG9rZW4sIHR5cGUgVG9rZW5zIH0gZnJvbSAnbWFya2VkJ1xuaW1wb3J0IFJlYWN0LCB7IFN1c3BlbnNlLCB1c2UsIHVzZU1lbW8sIHVzZVJlZiB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlU2V0dGluZ3MgfSBmcm9tICcuLi9ob29rcy91c2VTZXR0aW5ncy5qcydcbmltcG9ydCB7IEFuc2ksIEJveCwgdXNlVGhlbWUgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQge1xuICB0eXBlIENsaUhpZ2hsaWdodCxcbiAgZ2V0Q2xpSGlnaGxpZ2h0UHJvbWlzZSxcbn0gZnJvbSAnLi4vdXRpbHMvY2xpSGlnaGxpZ2h0LmpzJ1xuaW1wb3J0IHsgaGFzaENvbnRlbnQgfSBmcm9tICcuLi91dGlscy9oYXNoLmpzJ1xuaW1wb3J0IHsgY29uZmlndXJlTWFya2VkLCBmb3JtYXRUb2tlbiB9IGZyb20gJy4uL3V0aWxzL21hcmtkb3duLmpzJ1xuaW1wb3J0IHsgc3RyaXBQcm9tcHRYTUxUYWdzIH0gZnJvbSAnLi4vdXRpbHMvbWVzc2FnZXMuanMnXG5pbXBvcnQgeyBNYXJrZG93blRhYmxlIH0gZnJvbSAnLi9NYXJrZG93blRhYmxlLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBjaGlsZHJlbjogc3RyaW5nXG4gIC8qKiBXaGVuIHRydWUsIHJlbmRlciBhbGwgdGV4dCBjb250ZW50IGFzIGRpbSAqL1xuICBkaW1Db2xvcj86IGJvb2xlYW5cbn1cblxuLy8gTW9kdWxlLWxldmVsIHRva2VuIGNhY2hlIOKAlCBtYXJrZWQubGV4ZXIgaXMgdGhlIGhvdCBjb3N0IG9uIHZpcnR1YWwtc2Nyb2xsXG4vLyByZW1vdW50cyAofjNtcyBwZXIgbWVzc2FnZSkuIHVzZU1lbW8gZG9lc24ndCBzdXJ2aXZlIHVubW91bnTihpJyZW1vdW50LCBzb1xuLy8gc2Nyb2xsaW5nIGJhY2sgdG8gYSBwcmV2aW91c2x5LXZpc2libGUgbWVzc2FnZSByZS1wYXJzZXMuIE1lc3NhZ2VzIGFyZVxuLy8gaW1tdXRhYmxlIGluIGhpc3Rvcnk7IHNhbWUgY29udGVudCDihpIgc2FtZSB0b2tlbnMuIEtleWVkIGJ5IGhhc2ggdG8gYXZvaWRcbi8vIHJldGFpbmluZyBmdWxsIGNvbnRlbnQgc3RyaW5ncyAodHVybjUw4oaSdHVybjk5IFJTUyByZWdyZXNzaW9uLCAjMjQxODApLlxuY29uc3QgVE9LRU5fQ0FDSEVfTUFYID0gNTAwXG5jb25zdCB0b2tlbkNhY2hlID0gbmV3IE1hcDxzdHJpbmcsIFRva2VuW10+KClcblxuLy8gQ2hhcmFjdGVycyB0aGF0IGluZGljYXRlIG1hcmtkb3duIHN5bnRheC4gSWYgbm9uZSBhcmUgcHJlc2VudCwgc2tpcCB0aGVcbi8vIH4zbXMgbWFya2VkLmxleGVyIGNhbGwgZW50aXJlbHkg4oCUIHJlbmRlciBhcyBhIHNpbmdsZSBwYXJhZ3JhcGguIENvdmVyc1xuLy8gdGhlIG1ham9yaXR5IG9mIHNob3J0IGFzc2lzdGFudCByZXNwb25zZXMgYW5kIHVzZXIgcHJvbXB0cyB0aGF0IGFyZVxuLy8gcGxhaW4gc2VudGVuY2VzLiBDaGVja2VkIHZpYSBpbmRleE9mIChub3QgcmVnZXgpIGZvciBzcGVlZC5cbi8vIFNpbmdsZSByZWdleDogbWF0Y2hlcyBhbnkgTUQgbWFya2VyIG9yIG9yZGVyZWQtbGlzdCBzdGFydCAoTi4gYXQgbGluZSBzdGFydCkuXG4vLyBPbmUgcGFzcyBpbnN0ZWFkIG9mIDEww5cgaW5jbHVkZXMgc2NhbnMuXG5jb25zdCBNRF9TWU5UQVhfUkUgPSAvWyMqYHxbPlxcLV9+XXxcXG5cXG58XlxcZCtcXC4gfFxcblxcZCtcXC4gL1xuZnVuY3Rpb24gaGFzTWFya2Rvd25TeW50YXgoczogc3RyaW5nKTogYm9vbGVhbiB7XG4gIC8vIFNhbXBsZSBmaXJzdCA1MDAgY2hhcnMg4oCUIGlmIG1hcmtkb3duIGV4aXN0cyBpdCdzIHVzdWFsbHkgZWFybHkgKGhlYWRlcnMsXG4gIC8vIGNvZGUgZmVuY2UsIGxpc3QpLiBMb25nIHRvb2wgb3V0cHV0cyBhcmUgbW9zdGx5IHBsYWluIHRleHQgdGFpbHMuXG4gIHJldHVybiBNRF9TWU5UQVhfUkUudGVzdChzLmxlbmd0aCA+IDUwMCA/IHMuc2xpY2UoMCwgNTAwKSA6IHMpXG59XG5cbmZ1bmN0aW9uIGNhY2hlZExleGVyKGNvbnRlbnQ6IHN0cmluZyk6IFRva2VuW10ge1xuICAvLyBGYXN0IHBhdGg6IHBsYWluIHRleHQgd2l0aCBubyBtYXJrZG93biBzeW50YXgg4oaSIHNpbmdsZSBwYXJhZ3JhcGggdG9rZW4uXG4gIC8vIFNraXBzIG1hcmtlZC5sZXhlcidzIGZ1bGwgR0ZNIHBhcnNlICh+M21zIG9uIGxvbmcgY29udGVudCkuIE5vdCBjYWNoZWQg4oCUXG4gIC8vIHJlY29uc3RydWN0aW9uIGlzIGEgc2luZ2xlIG9iamVjdCBhbGxvY2F0aW9uLCBhbmQgY2FjaGluZyB3b3VsZCByZXRhaW5cbiAgLy8gNMOXIGNvbnRlbnQgaW4gcmF3L3RleHQgZmllbGRzIHBsdXMgdGhlIGhhc2gga2V5IGZvciB6ZXJvIGJlbmVmaXQuXG4gIGlmICghaGFzTWFya2Rvd25TeW50YXgoY29udGVudCkpIHtcbiAgICByZXR1cm4gW1xuICAgICAge1xuICAgICAgICB0eXBlOiAncGFyYWdyYXBoJyxcbiAgICAgICAgcmF3OiBjb250ZW50LFxuICAgICAgICB0ZXh0OiBjb250ZW50LFxuICAgICAgICB0b2tlbnM6IFt7IHR5cGU6ICd0ZXh0JywgcmF3OiBjb250ZW50LCB0ZXh0OiBjb250ZW50IH1dLFxuICAgICAgfSBhcyBUb2tlbixcbiAgICBdXG4gIH1cbiAgY29uc3Qga2V5ID0gaGFzaENvbnRlbnQoY29udGVudClcbiAgY29uc3QgaGl0ID0gdG9rZW5DYWNoZS5nZXQoa2V5KVxuICBpZiAoaGl0KSB7XG4gICAgLy8gUHJvbW90ZSB0byBNUlUg4oCUIHdpdGhvdXQgdGhpcyB0aGUgZXZpY3Rpb24gaXMgRklGTyAoc2Nyb2xsaW5nIGJhY2sgdG9cbiAgICAvLyBhbiBlYXJseSBtZXNzYWdlIGV2aWN0cyB0aGUgdmVyeSBpdGVtIHlvdSdyZSBsb29raW5nIGF0KS5cbiAgICB0b2tlbkNhY2hlLmRlbGV0ZShrZXkpXG4gICAgdG9rZW5DYWNoZS5zZXQoa2V5LCBoaXQpXG4gICAgcmV0dXJuIGhpdFxuICB9XG4gIGNvbnN0IHRva2VucyA9IG1hcmtlZC5sZXhlcihjb250ZW50KVxuICBpZiAodG9rZW5DYWNoZS5zaXplID49IFRPS0VOX0NBQ0hFX01BWCkge1xuICAgIC8vIExSVS1pc2g6IGRyb3Agb2xkZXN0LiBNYXAgcHJlc2VydmVzIGluc2VydGlvbiBvcmRlci5cbiAgICBjb25zdCBmaXJzdCA9IHRva2VuQ2FjaGUua2V5cygpLm5leHQoKS52YWx1ZVxuICAgIGlmIChmaXJzdCAhPT0gdW5kZWZpbmVkKSB0b2tlbkNhY2hlLmRlbGV0ZShmaXJzdClcbiAgfVxuICB0b2tlbkNhY2hlLnNldChrZXksIHRva2VucylcbiAgcmV0dXJuIHRva2Vuc1xufVxuXG4vKipcbiAqIFJlbmRlcnMgbWFya2Rvd24gY29udGVudCB1c2luZyBhIGh5YnJpZCBhcHByb2FjaDpcbiAqIC0gVGFibGVzIGFyZSByZW5kZXJlZCBhcyBSZWFjdCBjb21wb25lbnRzIHdpdGggcHJvcGVyIGZsZXhib3ggbGF5b3V0XG4gKiAtIE90aGVyIGNvbnRlbnQgaXMgcmVuZGVyZWQgYXMgQU5TSSBzdHJpbmdzIHZpYSBmb3JtYXRUb2tlblxuICovXG5leHBvcnQgZnVuY3Rpb24gTWFya2Rvd24ocHJvcHM6IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3Qgc2V0dGluZ3MgPSB1c2VTZXR0aW5ncygpXG4gIGlmIChzZXR0aW5ncy5zeW50YXhIaWdobGlnaHRpbmdEaXNhYmxlZCkge1xuICAgIHJldHVybiA8TWFya2Rvd25Cb2R5IHsuLi5wcm9wc30gaGlnaGxpZ2h0PXtudWxsfSAvPlxuICB9XG4gIC8vIFN1c3BlbnNlIGZhbGxiYWNrIHJlbmRlcnMgd2l0aCBoaWdobGlnaHQ9bnVsbCDigJQgcGxhaW4gbWFya2Rvd24gc2hvd3NcbiAgLy8gZm9yIH41MG1zIG9uIGZpcnN0IGV2ZXIgcmVuZGVyIHdoaWxlIGNsaS1oaWdobGlnaHQgbG9hZHMuXG4gIHJldHVybiAoXG4gICAgPFN1c3BlbnNlIGZhbGxiYWNrPXs8TWFya2Rvd25Cb2R5IHsuLi5wcm9wc30gaGlnaGxpZ2h0PXtudWxsfSAvPn0+XG4gICAgICA8TWFya2Rvd25XaXRoSGlnaGxpZ2h0IHsuLi5wcm9wc30gLz5cbiAgICA8L1N1c3BlbnNlPlxuICApXG59XG5cbmZ1bmN0aW9uIE1hcmtkb3duV2l0aEhpZ2hsaWdodChwcm9wczogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBoaWdobGlnaHQgPSB1c2UoZ2V0Q2xpSGlnaGxpZ2h0UHJvbWlzZSgpKVxuICByZXR1cm4gPE1hcmtkb3duQm9keSB7Li4ucHJvcHN9IGhpZ2hsaWdodD17aGlnaGxpZ2h0fSAvPlxufVxuXG5mdW5jdGlvbiBNYXJrZG93bkJvZHkoe1xuICBjaGlsZHJlbixcbiAgZGltQ29sb3IsXG4gIGhpZ2hsaWdodCxcbn06IFByb3BzICYgeyBoaWdobGlnaHQ6IENsaUhpZ2hsaWdodCB8IG51bGwgfSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZV0gPSB1c2VUaGVtZSgpXG4gIGNvbmZpZ3VyZU1hcmtlZCgpXG5cbiAgY29uc3QgZWxlbWVudHMgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBjb25zdCB0b2tlbnMgPSBjYWNoZWRMZXhlcihzdHJpcFByb21wdFhNTFRhZ3MoY2hpbGRyZW4pKVxuICAgIGNvbnN0IGVsZW1lbnRzOiBSZWFjdC5SZWFjdE5vZGVbXSA9IFtdXG4gICAgbGV0IG5vblRhYmxlQ29udGVudCA9ICcnXG5cbiAgICBmdW5jdGlvbiBmbHVzaE5vblRhYmxlQ29udGVudCgpOiB2b2lkIHtcbiAgICAgIGlmIChub25UYWJsZUNvbnRlbnQpIHtcbiAgICAgICAgZWxlbWVudHMucHVzaChcbiAgICAgICAgICA8QW5zaSBrZXk9e2VsZW1lbnRzLmxlbmd0aH0gZGltQ29sb3I9e2RpbUNvbG9yfT5cbiAgICAgICAgICAgIHtub25UYWJsZUNvbnRlbnQudHJpbSgpfVxuICAgICAgICAgIDwvQW5zaT4sXG4gICAgICAgIClcbiAgICAgICAgbm9uVGFibGVDb250ZW50ID0gJydcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IHRva2VuIG9mIHRva2Vucykge1xuICAgICAgaWYgKHRva2VuLnR5cGUgPT09ICd0YWJsZScpIHtcbiAgICAgICAgZmx1c2hOb25UYWJsZUNvbnRlbnQoKVxuICAgICAgICBlbGVtZW50cy5wdXNoKFxuICAgICAgICAgIDxNYXJrZG93blRhYmxlXG4gICAgICAgICAgICBrZXk9e2VsZW1lbnRzLmxlbmd0aH1cbiAgICAgICAgICAgIHRva2VuPXt0b2tlbiBhcyBUb2tlbnMuVGFibGV9XG4gICAgICAgICAgICBoaWdobGlnaHQ9e2hpZ2hsaWdodH1cbiAgICAgICAgICAvPixcbiAgICAgICAgKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbm9uVGFibGVDb250ZW50ICs9IGZvcm1hdFRva2VuKHRva2VuLCB0aGVtZSwgMCwgbnVsbCwgbnVsbCwgaGlnaGxpZ2h0KVxuICAgICAgfVxuICAgIH1cblxuICAgIGZsdXNoTm9uVGFibGVDb250ZW50KClcbiAgICByZXR1cm4gZWxlbWVudHNcbiAgfSwgW2NoaWxkcmVuLCBkaW1Db2xvciwgaGlnaGxpZ2h0LCB0aGVtZV0pXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAge2VsZW1lbnRzfVxuICAgIDwvQm94PlxuICApXG59XG5cbnR5cGUgU3RyZWFtaW5nUHJvcHMgPSB7XG4gIGNoaWxkcmVuOiBzdHJpbmdcbn1cblxuLyoqXG4gKiBSZW5kZXJzIG1hcmtkb3duIGR1cmluZyBzdHJlYW1pbmcgYnkgc3BsaXR0aW5nIGF0IHRoZSBsYXN0IHRvcC1sZXZlbCBibG9ja1xuICogYm91bmRhcnk6IGV2ZXJ5dGhpbmcgYmVmb3JlIGlzIHN0YWJsZSAobWVtb2l6ZWQsIG5ldmVyIHJlLXBhcnNlZCksIG9ubHkgdGhlXG4gKiBmaW5hbCBibG9jayBpcyByZS1wYXJzZWQgcGVyIGRlbHRhLiBtYXJrZWQubGV4ZXIoKSBjb3JyZWN0bHkgaGFuZGxlc1xuICogdW5jbG9zZWQgY29kZSBmZW5jZXMgYXMgYSBzaW5nbGUgdG9rZW4sIHNvIGJsb2NrIGJvdW5kYXJpZXMgYXJlIGFsd2F5cyBzYWZlLlxuICpcbiAqIFRoZSBzdGFibGUgYm91bmRhcnkgb25seSBhZHZhbmNlcyAobW9ub3RvbmljKSwgc28gcmVmIG11dGF0aW9uIGR1cmluZyByZW5kZXJcbiAqIGlzIGlkZW1wb3RlbnQgYW5kIHNhZmUgdW5kZXIgU3RyaWN0TW9kZSBkb3VibGUtcmVuZGVyaW5nLiBDb21wb25lbnQgdW5tb3VudHNcbiAqIGJldHdlZW4gdHVybnMgKHN0cmVhbWluZ1RleHQg4oaSIG51bGwpLCByZXNldHRpbmcgdGhlIHJlZi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFN0cmVhbWluZ01hcmtkb3duKHtcbiAgY2hpbGRyZW4sXG59OiBTdHJlYW1pbmdQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIFJlYWN0IENvbXBpbGVyOiB0aGlzIGNvbXBvbmVudCByZWFkcyBhbmQgd3JpdGVzIHN0YWJsZVByZWZpeFJlZi5jdXJyZW50XG4gIC8vIGR1cmluZyByZW5kZXIgYnkgZGVzaWduLiBUaGUgYm91bmRhcnkgb25seSBhZHZhbmNlcyAobW9ub3RvbmljKSwgc29cbiAgLy8gdGhlIHJlZiBtdXRhdGlvbiBpcyBpZGVtcG90ZW50IHVuZGVyIFN0cmljdE1vZGUgZG91YmxlLXJlbmRlciDigJQgYnV0IHRoZVxuICAvLyBjb21waWxlciBjYW4ndCBwcm92ZSB0aGF0LCBhbmQgbWVtb2l6aW5nIGFyb3VuZCB0aGUgcmVmIHJlYWRzIHdvdWxkXG4gIC8vIGJyZWFrIHRoZSBhbGdvcml0aG0gKHN0YWxlIGJvdW5kYXJ5KS4gT3B0IG91dC5cbiAgJ3VzZSBubyBtZW1vJ1xuICBjb25maWd1cmVNYXJrZWQoKVxuXG4gIC8vIFN0cmlwIGJlZm9yZSBib3VuZGFyeSB0cmFja2luZyBzbyBpdCBtYXRjaGVzIDxNYXJrZG93bj4ncyBzdHJpcHBpbmdcbiAgLy8gKGxpbmUgMjkpLiBXaGVuIGEgY2xvc2luZyB0YWcgYXJyaXZlcywgc3RyaXBwZWQoTisxKSBpcyBub3QgYSBwcmVmaXhcbiAgLy8gb2Ygc3RyaXBwZWQoTiksIGJ1dCB0aGUgc3RhcnRzV2l0aCByZXNldCBiZWxvdyBoYW5kbGVzIHRoYXQgd2l0aCBhXG4gIC8vIG9uZS10aW1lIHJlLWxleCBvbiB0aGUgc21hbGxlciBzdHJpcHBlZCBzdHJpbmcuXG4gIGNvbnN0IHN0cmlwcGVkID0gc3RyaXBQcm9tcHRYTUxUYWdzKGNoaWxkcmVuKVxuXG4gIGNvbnN0IHN0YWJsZVByZWZpeFJlZiA9IHVzZVJlZignJylcblxuICAvLyBSZXNldCBpZiB0ZXh0IHdhcyByZXBsYWNlZCAoZGVmZW5zaXZlOyBub3JtYWxseSB1bm1vdW50IGhhbmRsZXMgdGhpcylcbiAgaWYgKCFzdHJpcHBlZC5zdGFydHNXaXRoKHN0YWJsZVByZWZpeFJlZi5jdXJyZW50KSkge1xuICAgIHN0YWJsZVByZWZpeFJlZi5jdXJyZW50ID0gJydcbiAgfVxuXG4gIC8vIExleCBvbmx5IGZyb20gY3VycmVudCBib3VuZGFyeSDigJQgTyh1bnN0YWJsZSBsZW5ndGgpLCBub3QgTyhmdWxsIHRleHQpXG4gIGNvbnN0IGJvdW5kYXJ5ID0gc3RhYmxlUHJlZml4UmVmLmN1cnJlbnQubGVuZ3RoXG4gIGNvbnN0IHRva2VucyA9IG1hcmtlZC5sZXhlcihzdHJpcHBlZC5zdWJzdHJpbmcoYm91bmRhcnkpKVxuXG4gIC8vIExhc3Qgbm9uLXNwYWNlIHRva2VuIGlzIHRoZSBncm93aW5nIGJsb2NrOyBldmVyeXRoaW5nIGJlZm9yZSBpcyBmaW5hbFxuICBsZXQgbGFzdENvbnRlbnRJZHggPSB0b2tlbnMubGVuZ3RoIC0gMVxuICB3aGlsZSAobGFzdENvbnRlbnRJZHggPj0gMCAmJiB0b2tlbnNbbGFzdENvbnRlbnRJZHhdIS50eXBlID09PSAnc3BhY2UnKSB7XG4gICAgbGFzdENvbnRlbnRJZHgtLVxuICB9XG4gIGxldCBhZHZhbmNlID0gMFxuICBmb3IgKGxldCBpID0gMDsgaSA8IGxhc3RDb250ZW50SWR4OyBpKyspIHtcbiAgICBhZHZhbmNlICs9IHRva2Vuc1tpXSEucmF3Lmxlbmd0aFxuICB9XG4gIGlmIChhZHZhbmNlID4gMCkge1xuICAgIHN0YWJsZVByZWZpeFJlZi5jdXJyZW50ID0gc3RyaXBwZWQuc3Vic3RyaW5nKDAsIGJvdW5kYXJ5ICsgYWR2YW5jZSlcbiAgfVxuXG4gIGNvbnN0IHN0YWJsZVByZWZpeCA9IHN0YWJsZVByZWZpeFJlZi5jdXJyZW50XG4gIGNvbnN0IHVuc3RhYmxlU3VmZml4ID0gc3RyaXBwZWQuc3Vic3RyaW5nKHN0YWJsZVByZWZpeC5sZW5ndGgpXG5cbiAgLy8gc3RhYmxlUHJlZml4IGlzIG1lbW9pemVkIGluc2lkZSA8TWFya2Rvd24+IHZpYSB1c2VNZW1vKFtjaGlsZHJlbiwgLi4uXSlcbiAgLy8gc28gaXQgbmV2ZXIgcmUtcGFyc2VzIGFzIHRoZSB1bnN0YWJsZSBzdWZmaXggZ3Jvd3NcbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAge3N0YWJsZVByZWZpeCAmJiA8TWFya2Rvd24+e3N0YWJsZVByZWZpeH08L01hcmtkb3duPn1cbiAgICAgIHt1bnN0YWJsZVN1ZmZpeCAmJiA8TWFya2Rvd24+e3Vuc3RhYmxlU3VmZml4fTwvTWFya2Rvd24+fVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxNQUFNLEVBQUUsS0FBS0MsS0FBSyxFQUFFLEtBQUtDLE1BQU0sUUFBUSxR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 \ No newline at end of file diff --git a/components/MarkdownTable.tsx b/components/MarkdownTable.tsx new file mode 100644 index 0000000..d81d16e --- /dev/null +++ b/components/MarkdownTable.tsx @@ -0,0 +1,322 @@ +import type { Token, Tokens } from 'marked'; +import React from 'react'; +import stripAnsi from 'strip-ansi'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { stringWidth } from '../ink/stringWidth.js'; +import { wrapAnsi } from '../ink/wrapAnsi.js'; +import { Ansi, useTheme } from '../ink.js'; +import type { CliHighlight } from '../utils/cliHighlight.js'; +import { formatToken, padAligned } from '../utils/markdown.js'; + +/** Accounts for parent indentation (e.g. message dot prefix) and terminal + * resize races. Without enough margin the table overflows its layout box + * and Ink's clip truncates differently on alternating frames, causing an + * infinite flicker loop in scrollback. */ +const SAFETY_MARGIN = 4; + +/** Minimum column width to prevent degenerate layouts */ +const MIN_COLUMN_WIDTH = 3; + +/** + * Maximum number of lines per row before switching to vertical format. + * When wrapping would make rows taller than this, vertical (key-value) + * format provides better readability. + */ +const MAX_ROW_LINES = 4; + +/** ANSI escape codes for text formatting */ +const ANSI_BOLD_START = '\x1b[1m'; +const ANSI_BOLD_END = '\x1b[22m'; +type Props = { + token: Tokens.Table; + highlight: CliHighlight | null; + /** Override terminal width (useful for testing) */ + forceWidth?: number; +}; + +/** + * Wrap text to fit within a given width, returning array of lines. + * ANSI-aware: preserves styling across line breaks. + * + * @param hard - If true, break words that exceed width (needed when columns + * are narrower than the longest word). Default false. + */ +function wrapText(text: string, width: number, options?: { + hard?: boolean; +}): string[] { + if (width <= 0) return [text]; + // Strip trailing whitespace/newlines before wrapping. + // formatToken() adds EOL to paragraphs and other token types, + // which would otherwise create extra blank lines in table cells. + const trimmedText = text.trimEnd(); + const wrapped = wrapAnsi(trimmedText, width, { + hard: options?.hard ?? false, + trim: false, + wordWrap: true + }); + // Filter out empty lines that result from trailing newlines or + // multiple consecutive newlines in the source content. + const lines = wrapped.split('\n').filter(line => line.length > 0); + // Ensure we always return at least one line (empty string for empty cells) + return lines.length > 0 ? lines : ['']; +} + +/** + * Renders a markdown table using Ink's Box layout. + * Handles terminal width by: + * 1. Calculating minimum column widths based on longest word + * 2. Distributing available space proportionally + * 3. Wrapping text within cells (no truncation) + * 4. Properly aligning multi-line rows with borders + */ +export function MarkdownTable({ + token, + highlight, + forceWidth +}: Props): React.ReactNode { + const [theme] = useTheme(); + const { + columns: actualTerminalWidth + } = useTerminalSize(); + const terminalWidth = forceWidth ?? actualTerminalWidth; + + // Format cell content to ANSI string + function formatCell(tokens: Token[] | undefined): string { + return tokens?.map(_ => formatToken(_, theme, 0, null, null, highlight)).join('') ?? ''; + } + + // Get plain text (stripped of ANSI codes) + function getPlainText(tokens_0: Token[] | undefined): string { + return stripAnsi(formatCell(tokens_0)); + } + + // Get the longest word width in a cell (minimum width to avoid breaking words) + function getMinWidth(tokens_1: Token[] | undefined): number { + const text = getPlainText(tokens_1); + const words = text.split(/\s+/).filter(w => w.length > 0); + if (words.length === 0) return MIN_COLUMN_WIDTH; + return Math.max(...words.map(w_0 => stringWidth(w_0)), MIN_COLUMN_WIDTH); + } + + // Get ideal width (full content without wrapping) + function getIdealWidth(tokens_2: Token[] | undefined): number { + return Math.max(stringWidth(getPlainText(tokens_2)), MIN_COLUMN_WIDTH); + } + + // Calculate column widths + // Step 1: Get minimum (longest word) and ideal (full content) widths + const minWidths = token.header.map((header, colIndex) => { + let maxMinWidth = getMinWidth(header.tokens); + for (const row of token.rows) { + maxMinWidth = Math.max(maxMinWidth, getMinWidth(row[colIndex]?.tokens)); + } + return maxMinWidth; + }); + const idealWidths = token.header.map((header_0, colIndex_0) => { + let maxIdeal = getIdealWidth(header_0.tokens); + for (const row_0 of token.rows) { + maxIdeal = Math.max(maxIdeal, getIdealWidth(row_0[colIndex_0]?.tokens)); + } + return maxIdeal; + }); + + // Step 2: Calculate available space + // Border overhead: │ content │ content │ = 1 + (width + 3) per column + const numCols = token.header.length; + const borderOverhead = 1 + numCols * 3; // │ + (2 padding + 1 border) per col + // Account for SAFETY_MARGIN to avoid triggering the fallback safety check + const availableWidth = Math.max(terminalWidth - borderOverhead - SAFETY_MARGIN, numCols * MIN_COLUMN_WIDTH); + + // Step 3: Calculate column widths that fit available space + const totalMin = minWidths.reduce((sum, w_1) => sum + w_1, 0); + const totalIdeal = idealWidths.reduce((sum_0, w_2) => sum_0 + w_2, 0); + + // Track whether columns are narrower than longest words (needs hard wrap) + let needsHardWrap = false; + let columnWidths: number[]; + if (totalIdeal <= availableWidth) { + // Everything fits - use ideal widths + columnWidths = idealWidths; + } else if (totalMin <= availableWidth) { + // Need to shrink - give each column its min, distribute remaining space + const extraSpace = availableWidth - totalMin; + const overflows = idealWidths.map((ideal, i) => ideal - minWidths[i]!); + const totalOverflow = overflows.reduce((sum_1, o) => sum_1 + o, 0); + columnWidths = minWidths.map((min, i_0) => { + if (totalOverflow === 0) return min; + const extra = Math.floor(overflows[i_0]! / totalOverflow * extraSpace); + return min + extra; + }); + } else { + // Table wider than terminal at minimum widths + // Shrink columns proportionally to fit, allowing word breaks + needsHardWrap = true; + const scaleFactor = availableWidth / totalMin; + columnWidths = minWidths.map(w_3 => Math.max(Math.floor(w_3 * scaleFactor), MIN_COLUMN_WIDTH)); + } + + // Step 4: Calculate max row lines to determine if vertical format is needed + function calculateMaxRowLines(): number { + let maxLines = 1; + // Check header + for (let i_1 = 0; i_1 < token.header.length; i_1++) { + const content = formatCell(token.header[i_1]!.tokens); + const wrapped = wrapText(content, columnWidths[i_1]!, { + hard: needsHardWrap + }); + maxLines = Math.max(maxLines, wrapped.length); + } + // Check rows + for (const row_1 of token.rows) { + for (let i_2 = 0; i_2 < row_1.length; i_2++) { + const content_0 = formatCell(row_1[i_2]?.tokens); + const wrapped_0 = wrapText(content_0, columnWidths[i_2]!, { + hard: needsHardWrap + }); + maxLines = Math.max(maxLines, wrapped_0.length); + } + } + return maxLines; + } + + // Use vertical format if wrapping would make rows too tall + const maxRowLines = calculateMaxRowLines(); + const useVerticalFormat = maxRowLines > MAX_ROW_LINES; + + // Render a single row with potential multi-line cells + // Returns an array of strings, one per line of the row + function renderRowLines(cells: Array<{ + tokens?: Token[]; + }>, isHeader: boolean): string[] { + // Get wrapped lines for each cell (preserving ANSI formatting) + const cellLines = cells.map((cell, colIndex_1) => { + const formattedText = formatCell(cell.tokens); + const width = columnWidths[colIndex_1]!; + return wrapText(formattedText, width, { + hard: needsHardWrap + }); + }); + + // Find max number of lines in this row + const maxLines_0 = Math.max(...cellLines.map(lines => lines.length), 1); + + // Calculate vertical offset for each cell (to center vertically) + const verticalOffsets = cellLines.map(lines_0 => Math.floor((maxLines_0 - lines_0.length) / 2)); + + // Build each line of the row as a single string + const result: string[] = []; + for (let lineIdx = 0; lineIdx < maxLines_0; lineIdx++) { + let line = '│'; + for (let colIndex_2 = 0; colIndex_2 < cells.length; colIndex_2++) { + const lines_1 = cellLines[colIndex_2]!; + const offset = verticalOffsets[colIndex_2]!; + const contentLineIdx = lineIdx - offset; + const lineText = contentLineIdx >= 0 && contentLineIdx < lines_1.length ? lines_1[contentLineIdx]! : ''; + const width_0 = columnWidths[colIndex_2]!; + // Headers always centered; data uses table alignment + const align = isHeader ? 'center' : token.align?.[colIndex_2] ?? 'left'; + line += ' ' + padAligned(lineText, stringWidth(lineText), width_0, align) + ' │'; + } + result.push(line); + } + return result; + } + + // Render horizontal border as a single string + function renderBorderLine(type: 'top' | 'middle' | 'bottom'): string { + const [left, mid, cross, right] = { + top: ['┌', '─', '┬', '┐'], + middle: ['├', '─', '┼', '┤'], + bottom: ['└', '─', '┴', '┘'] + }[type] as [string, string, string, string]; + let line_0 = left; + columnWidths.forEach((width_1, colIndex_3) => { + line_0 += mid.repeat(width_1 + 2); + line_0 += colIndex_3 < columnWidths.length - 1 ? cross : right; + }); + return line_0; + } + + // Render vertical format (key-value pairs) for extra-narrow terminals + function renderVerticalFormat(): string { + const lines_2: string[] = []; + const headers = token.header.map(h => getPlainText(h.tokens)); + const separatorWidth = Math.min(terminalWidth - 1, 40); + const separator = '─'.repeat(separatorWidth); + // Small indent for wrapped lines (just 2 spaces) + const wrapIndent = ' '; + token.rows.forEach((row_2, rowIndex) => { + if (rowIndex > 0) { + lines_2.push(separator); + } + row_2.forEach((cell_0, colIndex_4) => { + const label = headers[colIndex_4] || `Column ${colIndex_4 + 1}`; + // Clean value: trim, remove extra internal whitespace/newlines + const rawValue = formatCell(cell_0.tokens).trimEnd(); + const value = rawValue.replace(/\n+/g, ' ').replace(/\s+/g, ' ').trim(); + + // Wrap value to fit terminal, accounting for label on first line + const firstLineWidth = terminalWidth - stringWidth(label) - 3; + const subsequentLineWidth = terminalWidth - wrapIndent.length - 1; + + // Two-pass wrap: first line is narrower (label takes space), + // continuation lines get the full width minus indent. + const firstPassLines = wrapText(value, Math.max(firstLineWidth, 10)); + const firstLine = firstPassLines[0] || ''; + let wrappedValue: string[]; + if (firstPassLines.length <= 1 || subsequentLineWidth <= firstLineWidth) { + wrappedValue = firstPassLines; + } else { + // Re-join remaining text and re-wrap to the wider continuation width + const remainingText = firstPassLines.slice(1).map(l => l.trim()).join(' '); + const rewrapped = wrapText(remainingText, subsequentLineWidth); + wrappedValue = [firstLine, ...rewrapped]; + } + + // First line: bold label + value + lines_2.push(`${ANSI_BOLD_START}${label}:${ANSI_BOLD_END} ${wrappedValue[0] || ''}`); + + // Subsequent lines with small indent (skip empty lines) + for (let i_3 = 1; i_3 < wrappedValue.length; i_3++) { + const line_1 = wrappedValue[i_3]!; + if (!line_1.trim()) continue; + lines_2.push(`${wrapIndent}${line_1}`); + } + }); + }); + return lines_2.join('\n'); + } + + // Choose format based on available width + if (useVerticalFormat) { + return {renderVerticalFormat()}; + } + + // Build the complete horizontal table as an array of strings + const tableLines: string[] = []; + tableLines.push(renderBorderLine('top')); + tableLines.push(...renderRowLines(token.header, true)); + tableLines.push(renderBorderLine('middle')); + token.rows.forEach((row_3, rowIndex_0) => { + tableLines.push(...renderRowLines(row_3, false)); + if (rowIndex_0 < token.rows.length - 1) { + tableLines.push(renderBorderLine('middle')); + } + }); + tableLines.push(renderBorderLine('bottom')); + + // Safety check: verify no line exceeds terminal width. + // This catches edge cases during terminal resize where calculations + // were based on a different width than the current render target. + const maxLineWidth = Math.max(...tableLines.map(line_2 => stringWidth(stripAnsi(line_2)))); + + // If we're within SAFETY_MARGIN characters of the edge, use vertical format + // to account for terminal resize race conditions. + if (maxLineWidth > terminalWidth - SAFETY_MARGIN) { + return {renderVerticalFormat()}; + } + + // Render as a single Ansi block to prevent Ink from wrapping mid-row + return {tableLines.join('\n')}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJUb2tlbiIsIlRva2VucyIsIlJlYWN0Iiwic3RyaXBBbnNpIiwidXNlVGVybWluYWxTaXplIiwic3RyaW5nV2lkdGgiLCJ3cmFwQW5zaSIsIkFuc2kiLCJ1c2VUaGVtZSIsIkNsaUhpZ2hsaWdodCIsImZvcm1hdFRva2VuIiwicGFkQWxpZ25lZCIsIlNBRkVUWV9NQVJHSU4iLCJNSU5fQ09MVU1OX1dJRFRIIiwiTUFYX1JPV19MSU5FUyIsIkFOU0lfQk9MRF9TVEFSVCIsIkFOU0lfQk9MRF9FTkQiLCJQcm9wcyIsInRva2VuIiwiVGFibGUiLCJoaWdobGlnaHQiLCJmb3JjZVdpZHRoIiwid3JhcFRleHQiLCJ0ZXh0Iiwid2lkdGgiLCJvcHRpb25zIiwiaGFyZCIsInRyaW1tZWRUZXh0IiwidHJpbUVuZCIsIndyYXBwZWQiLCJ0cmltIiwid29yZFdyYXAiLCJsaW5lcyIsInNwbGl0IiwiZmlsdGVyIiwibGluZSIsImxlbmd0aCIsIk1hcmtkb3duVGFibGUiLCJSZWFjdE5vZGUiLCJ0aGVtZSIsImNvbHVtbnMiLCJhY3R1YWxUZXJtaW5hbFdpZHRoIiwidGVybWluYWxXaWR0aCIsImZvcm1hdENlbGwiLCJ0b2tlbnMiLCJtYXAiLCJfIiwiam9pbiIsImdldFBsYWluVGV4dCIsImdldE1pbldpZHRoIiwid29yZHMiLCJ3IiwiTWF0aCIsIm1heCIsImdldElkZWFsV2lkdGgiLCJtaW5XaWR0aHMiLCJoZWFkZXIiLCJjb2xJbmRleCIsIm1heE1pbldpZHRoIiwicm93Iiwicm93cyIsImlkZWFsV2lkdGhzIiwibWF4SWRlYWwiLCJudW1Db2xzIiwiYm9yZGVyT3ZlcmhlYWQiLCJhdmFpbGFibGVXaWR0aCIsInRvdGFsTWluIiwicmVkdWNlIiwic3VtIiwidG90YWxJZGVhbCIsIm5lZWRzSGFyZFdyYXAiLCJjb2x1bW5XaWR0aHMiLCJleHRyYVNwYWNlIiwib3ZlcmZsb3dzIiwiaWRlYWwiLCJpIiwidG90YWxPdmVyZmxvdyIsIm8iLCJtaW4iLCJleHRyYSIsImZsb29yIiwic2NhbGVGYWN0b3IiLCJjYWxjdWxhdGVNYXhSb3dMaW5lcyIsIm1heExpbmVzIiwiY29udGVudCIsIm1heFJvd0xpbmVzIiwidXNlVmVydGljYWxGb3JtYXQiLCJyZW5kZXJSb3dMaW5lcyIsImNlbGxzIiwiQXJyYXkiLCJpc0hlYWRlciIsImNlbGxMaW5lcyIsImNlbGwiLCJmb3JtYXR0ZWRUZXh0IiwidmVydGljYWxPZmZzZXRzIiwicmVzdWx0IiwibGluZUlkeCIsIm9mZnNldCIsImNvbnRlbnRMaW5lSWR4IiwibGluZVRleHQiLCJhbGlnbiIsInB1c2giLCJyZW5kZXJCb3JkZXJMaW5lIiwidHlwZSIsImxlZnQiLCJtaWQiLCJjcm9zcyIsInJpZ2h0IiwidG9wIiwibWlkZGxlIiwiYm90dG9tIiwiZm9yRWFjaCIsInJlcGVhdCIsInJlbmRlclZlcnRpY2FsRm9ybWF0IiwiaGVhZGVycyIsImgiLCJzZXBhcmF0b3JXaWR0aCIsInNlcGFyYXRvciIsIndyYXBJbmRlbnQiLCJyb3dJbmRleCIsImxhYmVsIiwicmF3VmFsdWUiLCJ2YWx1ZSIsInJlcGxhY2UiLCJmaXJzdExpbmVXaWR0aCIsInN1YnNlcXVlbnRMaW5lV2lkdGgiLCJmaXJzdFBhc3NMaW5lcyIsImZpcnN0TGluZSIsIndyYXBwZWRWYWx1ZSIsInJlbWFpbmluZ1RleHQiLCJzbGljZSIsImwiLCJyZXdyYXBwZWQiLCJ0YWJsZUxpbmVzIiwibWF4TGluZVdpZHRoIl0sInNvdXJjZXMiOlsiTWFya2Rvd25UYWJsZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBUb2tlbiwgVG9rZW5zIH0gZnJvbSAnbWFya2VkJ1xuaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHN0cmlwQW5zaSBmcm9tICdzdHJpcC1hbnNpJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyB3cmFwQW5zaSB9IGZyb20gJy4uL2luay93cmFwQW5zaS5qcydcbmltcG9ydCB7IEFuc2ksIHVzZVRoZW1lIH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBDbGlIaWdobGlnaHQgfSBmcm9tICcuLi91dGlscy9jbGlIaWdobGlnaHQuanMnXG5pbXBvcnQgeyBmb3JtYXRUb2tlbiwgcGFkQWxpZ25lZCB9IGZyb20gJy4uL3V0aWxzL21hcmtkb3duLmpzJ1xuXG4vKiogQWNjb3VudHMgZm9yIHBhcmVudCBpbmRlbnRhdGlvbiAoZS5nLiBtZXNzYWdlIGRvdCBwcmVmaXgpIGFuZCB0ZXJtaW5hbFxuICogIHJlc2l6ZSByYWNlcy4gV2l0aG91dCBlbm91Z2ggbWFyZ2luIHRoZSB0YWJsZSBvdmVyZmxvd3MgaXRzIGxheW91dCBib3hcbiAqICBhbmQgSW5rJ3MgY2xpcCB0cnVuY2F0ZXMgZGlmZmVyZW50bHkgb24gYWx0ZXJuYXRpbmcgZnJhbWVzLCBjYXVzaW5nIGFuXG4gKiAgaW5maW5pdGUgZmxpY2tlciBsb29wIGluIHNjcm9sbGJhY2suICovXG5jb25zdCBTQUZFVFlfTUFSR0lOID0gNFxuXG4vKiogTWluaW11bSBjb2x1bW4gd2lkdGggdG8gcHJldmVudCBkZWdlbmVyYXRlIGxheW91dHMgKi9cbmNvbnN0IE1JTl9DT0xVTU5fV0lEVEggPSAzXG5cbi8qKlxuICogTWF4aW11bSBudW1iZXIgb2YgbGluZXMgcGVyIHJvdyBiZWZvcmUgc3dpdGNoaW5nIHRvIHZlcnRpY2FsIGZvcm1hdC5cbiAqIFdoZW4gd3JhcHBpbmcgd291bGQgbWFrZSByb3dzIHRhbGxlciB0aGFuIHRoaXMsIHZlcnRpY2FsIChrZXktdmFsdWUpXG4gKiBmb3JtYXQgcHJvdmlkZXMgYmV0dGVyIHJlYWRhYmlsaXR5LlxuICovXG5jb25zdCBNQVhfUk9XX0xJTkVTID0gNFxuXG4vKiogQU5TSSBlc2NhcGUgY29kZXMgZm9yIHRleHQgZm9ybWF0dGluZyAqL1xuY29uc3QgQU5TSV9CT0xEX1NUQVJUID0gJ1xceDFiWzFtJ1xuY29uc3QgQU5TSV9CT0xEX0VORCA9ICdcXHgxYlsyMm0nXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHRva2VuOiBUb2tlbnMuVGFibGVcbiAgaGlnaGxpZ2h0OiBDbGlIaWdobGlnaHQgfCBudWxsXG4gIC8qKiBPdmVycmlkZSB0ZXJtaW5hbCB3aWR0aCAodXNlZnVsIGZvciB0ZXN0aW5nKSAqL1xuICBmb3JjZVdpZHRoPzogbnVtYmVyXG59XG5cbi8qKlxuICogV3JhcCB0ZXh0IHRvIGZpdCB3aXRoaW4gYSBnaXZlbiB3aWR0aCwgcmV0dXJuaW5nIGFycmF5IG9mIGxpbmVzLlxuICogQU5TSS1hd2FyZTogcHJlc2VydmVzIHN0eWxpbmcgYWNyb3NzIGxpbmUgYnJlYWtzLlxuICpcbiAqIEBwYXJhbSBoYXJkIC0gSWYgdHJ1ZSwgYnJlYWsgd29yZHMgdGhhdCBleGNlZWQgd2lkdGggKG5lZWRlZCB3aGVuIGNvbHVtbnNcbiAqICAgICAgICAgICAgICAgYXJlIG5hcnJvd2VyIHRoYW4gdGhlIGxvbmdlc3Qgd29yZCkuIERlZmF1bHQgZmFsc2UuXG4gKi9cbmZ1bmN0aW9uIHdyYXBUZXh0KFxuICB0ZXh0OiBzdHJpbmcsXG4gIHdpZHRoOiBudW1iZXIsXG4gIG9wdGlvbnM/OiB7IGhhcmQ/OiBib29sZWFuIH0sXG4pOiBzdHJpbmdbXSB7XG4gIGlmICh3aWR0aCA8PSAwKSByZXR1cm4gW3RleHRdXG4gIC8vIFN0cmlwIHRyYWlsaW5nIHdoaXRlc3BhY2UvbmV3bGluZXMgYmVmb3JlIHdyYXBwaW5nLlxuICAvLyBmb3JtYXRUb2tlbigpIGFkZHMgRU9MIHRvIHBhcmFncmFwaHMgYW5kIG90aGVyIHRva2VuIHR5cGVzLFxuICAvLyB3aGljaCB3b3VsZCBvdGhlcndpc2UgY3JlYXRlIGV4dHJhIGJsYW5rIGxpbmVzIGluIHRhYmxlIGNlbGxzLlxuICBjb25zdCB0cmltbWVkVGV4dCA9IHRleHQudHJpbUVuZCgpXG4gIGNvbnN0IHdyYXBwZWQgPSB3cmFwQW5zaSh0cmltbWVkVGV4dCwgd2lkdGgsIHtcbiAgICBoYXJkOiBvcHRpb25zPy5oYXJkID8/IGZhbHNlLFxuICAgIHRyaW06IGZhbHNlLFxuICAgIHdvcmRXcmFwOiB0cnVlLFxuICB9KVxuICAvLyBGaWx0ZXIgb3V0IGVtcHR5IGxpbmVzIHRoYXQgcmVzdWx0IGZyb20gdHJhaWxpbmcgbmV3bGluZXMgb3JcbiAgLy8gbXVsdGlwbGUgY29uc2VjdXRpdmUgbmV3bGluZXMgaW4gdGhlIHNvdXJjZSBjb250ZW50LlxuICBjb25zdCBsaW5lcyA9IHdyYXBwZWQuc3BsaXQoJ1xcbicpLmZpbHRlcihsaW5lID0+IGxpbmUubGVuZ3RoID4gMClcbiAgLy8gRW5zdXJlIHdlIGFsd2F5cyByZXR1cm4gYXQgbGVhc3Qgb25lIGxpbmUgKGVtcHR5IHN0cmluZyBmb3IgZW1wdHkgY2VsbHMpXG4gIHJldHVybiBsaW5lcy5sZW5ndGggPiAwID8gbGluZXMgOiBbJyddXG59XG5cbi8qKlxuICogUmVuZGVycyBhIG1hcmtkb3duIHRhYmxlIHVzaW5nIEluaydzIEJveCBsYXlvdXQuXG4gKiBIYW5kbGVzIHRlcm1pbmFsIHdpZHRoIGJ5OlxuICogMS4gQ2FsY3VsYXRpbmcgbWluaW11bSBjb2x1bW4gd2lkdGhzIGJhc2VkIG9uIGxvbmdlc3Qgd29yZFxuICogMi4gRGlzdHJpYnV0aW5nIGF2YWlsYWJsZSBzcGFjZSBwcm9wb3J0aW9uYWxseVxuICogMy4gV3JhcHBpbmcgdGV4dCB3aXRoaW4gY2VsbHMgKG5vIHRydW5jYXRpb24pXG4gKiA0LiBQcm9wZXJseSBhbGlnbmluZyBtdWx0aS1saW5lIHJvd3Mgd2l0aCBib3JkZXJzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBNYXJrZG93blRhYmxlKHtcbiAgdG9rZW4sXG4gIGhpZ2hsaWdodCxcbiAgZm9yY2VXaWR0aCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3RoZW1lXSA9IHVzZVRoZW1lKClcbiAgY29uc3QgeyBjb2x1bW5zOiBhY3R1YWxUZXJtaW5hbFdpZHRoIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCB0ZXJtaW5hbFdpZHRoID0gZm9yY2VXaWR0aCA/PyBhY3R1YWxUZXJtaW5hbFdpZHRoXG5cbiAgLy8gRm9ybWF0IGNlbGwgY29udGVudCB0byBBTlNJIHN0cmluZ1xuICBmdW5jdGlvbiBmb3JtYXRDZWxsKHRva2VuczogVG9rZW5bXSB8IHVuZGVmaW5lZCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIChcbiAgICAgIHRva2Vuc1xuICAgICAgICA/Lm1hcChfID0+IGZvcm1hdFRva2VuKF8sIHRoZW1lLCAwLCBudWxsLCBudWxsLCBoaWdobGlnaHQpKVxuICAgICAgICAuam9pbignJykgPz8gJydcbiAgICApXG4gIH1cblxuICAvLyBHZXQgcGxhaW4gdGV4dCAoc3RyaXBwZWQgb2YgQU5TSSBjb2RlcylcbiAgZnVuY3Rpb24gZ2V0UGxhaW5UZXh0KHRva2VuczogVG9rZW5bXSB8IHVuZGVmaW5lZCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHN0cmlwQW5zaShmb3JtYXRDZWxsKHRva2VucykpXG4gIH1cblxuICAvLyBHZXQgdGhlIGxvbmdlc3Qgd29yZCB3aWR0aCBpbiBhIGNlbGwgKG1pbmltdW0gd2lkdGggdG8gYXZvaWQgYnJlYWtpbmcgd29yZHMpXG4gIGZ1bmN0aW9uIGdldE1pbldpZHRoKHRva2VuczogVG9rZW5bXSB8IHVuZGVmaW5lZCk6IG51bWJlciB7XG4gICAgY29uc3QgdGV4dCA9IGdldFBsYWluVGV4dCh0b2tlbnMpXG4gICAgY29uc3Qgd29yZHMgPSB0ZXh0LnNwbGl0KC9cXHMrLykuZmlsdGVyKHcgPT4gdy5sZW5ndGggPiAwKVxuICAgIGlmICh3b3Jkcy5sZW5ndGggPT09IDApIHJldHVybiBNSU5fQ09MVU1OX1dJRFRIXG4gICAgcmV0dXJuIE1hdGgubWF4KC4uLndvcmRzLm1hcCh3ID0+IHN0cmluZ1dpZHRoKHcpKSwgTUlOX0NPTFVNTl9XSURUSClcbiAgfVxuXG4gIC8vIEdldCBpZGVhbCB3aWR0aCAoZnVsbCBjb250ZW50IHdpdGhvdXQgd3JhcHBpbmcpXG4gIGZ1bmN0aW9uIGdldElkZWFsV2lkdGgodG9rZW5zOiBUb2tlbltdIHwgdW5kZWZpbmVkKTogbnVtYmVyIHtcbiAgICByZXR1cm4gTWF0aC5tYXgoc3RyaW5nV2lkdGgoZ2V0UGxhaW5UZXh0KHRva2VucykpLCBNSU5fQ09MVU1OX1dJRFRIKVxuICB9XG5cbiAgLy8gQ2FsY3VsYXRlIGNvbHVtbiB3aWR0aHNcbiAgLy8gU3RlcCAxOiBHZXQgbWluaW11bSAobG9uZ2VzdCB3b3JkKSBhbmQgaWRlYWwgKGZ1bGwgY29udGVudCkgd2lkdGhzXG4gIGNvbnN0IG1pbldpZHRocyA9IHRva2VuLmhlYWRlci5tYXAoKGhlYWRlciwgY29sSW5kZXgpID0+IHtcbiAgICBsZXQgbWF4TWluV2lkdGggPSBnZXRNaW5XaWR0aChoZWFkZXIudG9rZW5zKVxuICAgIGZvciAoY29uc3Qgcm93IG9mIHRva2VuLnJvd3MpIHtcbiAgICAgIG1heE1pbldpZHRoID0gTWF0aC5tYXgobWF4TWluV2lkdGgsIGdldE1pbldpZHRoKHJvd1tjb2xJbmRleF0/LnRva2VucykpXG4gICAgfVxuICAgIHJldHVybiBtYXhNaW5XaWR0aFxuICB9KVxuXG4gIGNvbnN0IGlkZWFsV2lkdGhzID0gdG9rZW4uaGVhZGVyLm1hcCgoaGVhZGVyLCBjb2xJbmRleCkgPT4ge1xuICAgIGxldCBtYXhJZGVhbCA9IGdldElkZWFsV2lkdGgoaGVhZGVyLnRva2VucylcbiAgICBmb3IgKGNvbnN0IHJvdyBvZiB0b2tlbi5yb3dzKSB7XG4gICAgICBtYXhJZGVhbCA9IE1hdGgubWF4KG1heElkZWFsLCBnZXRJZGVhbFdpZHRoKHJvd1tjb2xJbmRleF0/LnRva2VucykpXG4gICAgfVxuICAgIHJldHVybiBtYXhJZGVhbFxuICB9KVxuXG4gIC8vIFN0ZXAgMjogQ2FsY3VsYXRlIGF2YWlsYWJsZSBzcGFjZVxuICAvLyBCb3JkZXIgb3ZlcmhlYWQ6IOKUgiBjb250ZW50IOKUgiBjb250ZW50IOKUgiA9IDEgKyAod2lkdGggKyAzKSBwZXIgY29sdW1uXG4gIGNvbnN0IG51bUNvbHMgPSB0b2tlbi5oZWFkZXIubGVuZ3RoXG4gIGNvbnN0IGJvcmRlck92ZXJoZWFkID0gMSArIG51bUNvbHMgKiAzIC8vIOKUgiArICgyIHBhZGRpbmcgKyAxIGJvcmRlcikgcGVyIGNvbFxuICAvLyBBY2NvdW50IGZvciBTQUZFVFlfTUFSR0lOIHRvIGF2b2lkIHRyaWdnZXJpbmcgdGhlIGZhbGxiYWNrIHNhZmV0eSBjaGVja1xuICBjb25zdCBhdmFpbGFibGVXaWR0aCA9IE1hdGgubWF4KFxuICAgIHRlcm1pbmFsV2lkdGggLSBib3JkZXJPdmVyaGVhZCAtIFNBRkVUWV9NQVJHSU4sXG4gICAgbnVtQ29scyAqIE1JTl9DT0xVTU5fV0lEVEgsXG4gIClcblxuICAvLyBTdGVwIDM6IENhbGN1bGF0ZSBjb2x1bW4gd2lkdGhzIHRoYXQgZml0IGF2YWlsYWJsZSBzcGFjZVxuICBjb25zdCB0b3RhbE1pbiA9IG1pbldpZHRocy5yZWR1Y2UoKHN1bSwgdykgPT4gc3VtICsgdywgMClcbiAgY29uc3QgdG90YWxJZGVhbCA9IGlkZWFsV2lkdGhzLnJlZHVjZSgoc3VtLCB3KSA9PiBzdW0gKyB3LCAwKVxuXG4gIC8vIFRyYWNrIHdoZXRoZXIgY29sdW1ucyBhcmUgbmFycm93ZXIgdGhhbiBsb25nZXN0IHdvcmRzIChuZWVkcyBoYXJkIHdyYXApXG4gIGxldCBuZWVkc0hhcmRXcmFwID0gZmFsc2VcblxuICBsZXQgY29sdW1uV2lkdGhzOiBudW1iZXJbXVxuICBpZiAodG90YWxJZGVhbCA8PSBhdmFpbGFibGVXaWR0aCkge1xuICAgIC8vIEV2ZXJ5dGhpbmcgZml0cyAtIHVzZSBpZGVhbCB3aWR0aHNcbiAgICBjb2x1bW5XaWR0aHMgPSBpZGVhbFdpZHRoc1xuICB9IGVsc2UgaWYgKHRvdGFsTWluIDw9IGF2YWlsYWJsZVdpZHRoKSB7XG4gICAgLy8gTmVlZCB0byBzaHJpbmsgLSBnaXZlIGVhY2ggY29sdW1uIGl0cyBtaW4sIGRpc3RyaWJ1dGUgcmVtYWluaW5nIHNwYWNlXG4gICAgY29uc3QgZXh0cmFTcGFjZSA9IGF2YWlsYWJsZVdpZHRoIC0gdG90YWxNaW5cbiAgICBjb25zdCBvdmVyZmxvd3MgPSBpZGVhbFdpZHRocy5tYXAoKGlkZWFsLCBpKSA9PiBpZGVhbCAtIG1pbldpZHRoc1tpXSEpXG4gICAgY29uc3QgdG90YWxPdmVyZmxvdyA9IG92ZXJmbG93cy5yZWR1Y2UoKHN1bSwgbykgPT4gc3VtICsgbywgMClcblxuICAgIGNvbHVtbldpZHRocyA9IG1pbldpZHRocy5tYXAoKG1pbiwgaSkgPT4ge1xuICAgICAgaWYgKHRvdGFsT3ZlcmZsb3cgPT09IDApIHJldHVybiBtaW5cbiAgICAgIGNvbnN0IGV4dHJhID0gTWF0aC5mbG9vcigob3ZlcmZsb3dzW2ldISAvIHRvdGFsT3ZlcmZsb3cpICogZXh0cmFTcGFjZSlcbiAgICAgIHJldHVybiBtaW4gKyBleHRyYVxuICAgIH0pXG4gIH0gZWxzZSB7XG4gICAgLy8gVGFibGUgd2lkZXIgdGhhbiB0ZXJtaW5hbCBhdCBtaW5pbXVtIHdpZHRoc1xuICAgIC8vIFNocmluayBjb2x1bW5zIHByb3BvcnRpb25hbGx5IHRvIGZpdCwgYWxsb3dpbmcgd29yZCBicmVha3NcbiAgICBuZWVkc0hhcmRXcmFwID0gdHJ1ZVxuICAgIGNvbnN0IHNjYWxlRmFjdG9yID0gYXZhaWxhYmxlV2lkdGggLyB0b3RhbE1pblxuICAgIGNvbHVtbldpZHRocyA9IG1pbldpZHRocy5tYXAodyA9PlxuICAgICAgTWF0aC5tYXgoTWF0aC5mbG9vcih3ICogc2NhbGVGYWN0b3IpLCBNSU5fQ09MVU1OX1dJRFRIKSxcbiAgICApXG4gIH1cblxuICAvLyBTdGVwIDQ6IENhbGN1bGF0ZSBtYXggcm93IGxpbmVzIHRvIGRldGVybWluZSBpZiB2ZXJ0aWNhbCBmb3JtYXQgaXMgbmVlZGVkXG4gIGZ1bmN0aW9uIGNhbGN1bGF0ZU1heFJvd0xpbmVzKCk6IG51bWJlciB7XG4gICAgbGV0IG1heExpbmVzID0gMVxuICAgIC8vIENoZWNrIGhlYWRlclxuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdG9rZW4uaGVhZGVyLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBjb250ZW50ID0gZm9ybWF0Q2VsbCh0b2tlbi5oZWFkZXJbaV0hLnRva2VucylcbiAgICAgIGNvbnN0IHdyYXBwZWQgPSB3cmFwVGV4dChjb250ZW50LCBjb2x1bW5XaWR0aHNbaV0hLCB7XG4gICAgICAgIGhhcmQ6IG5lZWRzSGFyZFdyYXAsXG4gICAgICB9KVxuICAgICAgbWF4TGluZXMgPSBNYXRoLm1heChtYXhMaW5lcywgd3JhcHBlZC5sZW5ndGgpXG4gICAgfVxuICAgIC8vIENoZWNrIHJvd3NcbiAgICBmb3IgKGNvbnN0IHJvdyBvZiB0b2tlbi5yb3dzKSB7XG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJvdy5sZW5ndGg7IGkrKykge1xuICAgICAgICBjb25zdCBjb250ZW50ID0gZm9ybWF0Q2VsbChyb3dbaV0/LnRva2VucylcbiAgICAgICAgY29uc3Qgd3JhcHBlZCA9IHdyYXBUZXh0KGNvbnRlbnQsIGNvbHVtbldpZHRoc1tpXSEsIHtcbiAgICAgICAgICBoYXJkOiBuZWVkc0hhcmRXcmFwLFxuICAgICAgICB9KVxuICAgICAgICBtYXhMaW5lcyA9IE1hdGgubWF4KG1heExpbmVzLCB3cmFwcGVkLmxlbmd0aClcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIG1heExpbmVzXG4gIH1cblxuICAvLyBVc2UgdmVydGljYWwgZm9ybWF0IGlmIHdyYXBwaW5nIHdvdWxkIG1ha2Ugcm93cyB0b28gdGFsbFxuICBjb25zdCBtYXhSb3dMaW5lcyA9IGNhbGN1bGF0ZU1heFJvd0xpbmVzKClcbiAgY29uc3QgdXNlVmVydGljYWxGb3JtYXQgPSBtYXhSb3dMaW5lcyA+IE1BWF9ST1dfTElORVNcblxuICAvLyBSZW5kZXIgYSBzaW5nbGUgcm93IHdpdGggcG90ZW50aWFsIG11bHRpLWxpbmUgY2VsbHNcbiAgLy8gUmV0dXJucyBhbiBhcnJheSBvZiBzdHJpbmdzLCBvbmUgcGVyIGxpbmUgb2YgdGhlIHJvd1xuICBmdW5jdGlvbiByZW5kZXJSb3dMaW5lcyhcbiAgICBjZWxsczogQXJyYXk8eyB0b2tlbnM/OiBUb2tlbltdIH0+LFxuICAgIGlzSGVhZGVyOiBib29sZWFuLFxuICApOiBzdHJpbmdbXSB7XG4gICAgLy8gR2V0IHdyYXBwZWQgbGluZXMgZm9yIGVhY2ggY2VsbCAocHJlc2VydmluZyBBTlNJIGZvcm1hdHRpbmcpXG4gICAgY29uc3QgY2VsbExpbmVzID0gY2VsbHMubWFwKChjZWxsLCBjb2xJbmRleCkgPT4ge1xuICAgICAgY29uc3QgZm9ybWF0dGVkVGV4dCA9IGZvcm1hdENlbGwoY2VsbC50b2tlbnMpXG4gICAgICBjb25zdCB3aWR0aCA9IGNvbHVtbldpZHRoc1tjb2xJbmRleF0hXG4gICAgICByZXR1cm4gd3JhcFRleHQoZm9ybWF0dGVkVGV4dCwgd2lkdGgsIHsgaGFyZDogbmVlZHNIYXJkV3JhcCB9KVxuICAgIH0pXG5cbiAgICAvLyBGaW5kIG1heCBudW1iZXIgb2YgbGluZXMgaW4gdGhpcyByb3dcbiAgICBjb25zdCBtYXhMaW5lcyA9IE1hdGgubWF4KC4uLmNlbGxMaW5lcy5tYXAobGluZXMgPT4gbGluZXMubGVuZ3RoKSwgMSlcblxuICAgIC8vIENhbGN1bGF0ZSB2ZXJ0aWNhbCBvZmZzZXQgZm9yIGVhY2ggY2VsbCAodG8gY2VudGVyIHZlcnRpY2FsbHkpXG4gICAgY29uc3QgdmVydGljYWxPZmZzZXRzID0gY2VsbExpbmVzLm1hcChsaW5lcyA9PlxuICAgICAgTWF0aC5mbG9vcigobWF4TGluZXMgLSBsaW5lcy5sZW5ndGgpIC8gMiksXG4gICAgKVxuXG4gICAgLy8gQnVpbGQgZWFjaCBsaW5lIG9mIHRoZSByb3cgYXMgYSBzaW5nbGUgc3RyaW5nXG4gICAgY29uc3QgcmVzdWx0OiBzdHJpbmdbXSA9IFtdXG4gICAgZm9yIChsZXQgbGluZUlkeCA9IDA7IGxpbmVJZHggPCBtYXhMaW5lczsgbGluZUlkeCsrKSB7XG4gICAgICBsZXQgbGluZSA9ICfilIInXG4gICAgICBmb3IgKGxldCBjb2xJbmRleCA9IDA7IGNvbEluZGV4IDwgY2VsbHMubGVuZ3RoOyBjb2xJbmRleCsrKSB7XG4gICAgICAgIGNvbnN0IGxpbmVzID0gY2VsbExpbmVzW2NvbEluZGV4XSFcbiAgICAgICAgY29uc3Qgb2Zmc2V0ID0gdmVydGljYWxPZmZzZXRzW2NvbEluZGV4XSFcbiAgICAgICAgY29uc3QgY29udGVudExpbmVJZHggPSBsaW5lSWR4IC0gb2Zmc2V0XG4gICAgICAgIGNvbnN0IGxpbmVUZXh0ID1cbiAgICAgICAgICBjb250ZW50TGluZUlkeCA+PSAwICYmIGNvbnRlbnRMaW5lSWR4IDwgbGluZXMubGVuZ3RoXG4gICAgICAgICAgICA/IGxpbmVzW2NvbnRlbnRMaW5lSWR4XSFcbiAgICAgICAgICAgIDogJydcbiAgICAgICAgY29uc3Qgd2lkdGggPSBjb2x1bW5XaWR0aHNbY29sSW5kZXhdIVxuICAgICAgICAvLyBIZWFkZXJzIGFsd2F5cyBjZW50ZXJlZDsgZGF0YSB1c2VzIHRhYmxlIGFsaWdubWVudFxuICAgICAgICBjb25zdCBhbGlnbiA9IGlzSGVhZGVyID8gJ2NlbnRlcicgOiAodG9rZW4uYWxpZ24/Lltjb2xJbmRleF0gPz8gJ2xlZnQnKVxuXG4gICAgICAgIGxpbmUgKz1cbiAgICAgICAgICAnICcgKyBwYWRBbGlnbmVkKGxpbmVUZXh0LCBzdHJpbmdXaWR0aChsaW5lVGV4dCksIHdpZHRoLCBhbGlnbikgKyAnIOKUgidcbiAgICAgIH1cbiAgICAgIHJlc3VsdC5wdXNoKGxpbmUpXG4gICAgfVxuXG4gICAgcmV0dXJuIHJlc3VsdFxuICB9XG5cbiAgLy8gUmVuZGVyIGhvcml6b250YWwgYm9yZGVyIGFzIGEgc2luZ2xlIHN0cmluZ1xuICBmdW5jdGlvbiByZW5kZXJCb3JkZXJMaW5lKHR5cGU6ICd0b3AnIHwgJ21pZGRsZScgfCAnYm90dG9tJyk6IHN0cmluZyB7XG4gICAgY29uc3QgW2xlZnQsIG1pZCwgY3Jvc3MsIHJpZ2h0XSA9IHtcbiAgICAgIHRvcDogWyfilIwnLCAn4pSAJywgJ+KUrCcsICfilJAnXSxcbiAgICAgIG1pZGRsZTogWyfilJwnLCAn4pSAJywgJ+KUvCcsICfilKQnXSxcbiAgICAgIGJvdHRvbTogWyfilJQnLCAn4pSAJywgJ+KUtCcsICfilJgnXSxcbiAgICB9W3R5cGVdIGFzIFtzdHJpbmcsIHN0cmluZywgc3RyaW5nLCBzdHJpbmddXG5cbiAgICBsZXQgbGluZSA9IGxlZnRcbiAgICBjb2x1bW5XaWR0aHMuZm9yRWFjaCgod2lkdGgsIGNvbEluZGV4KSA9PiB7XG4gICAgICBsaW5lICs9IG1pZC5yZXBlYXQod2lkdGggKyAyKVxuICAgICAgbGluZSArPSBjb2xJbmRleCA8IGNvbHVtbldpZHRocy5sZW5ndGggLSAxID8gY3Jvc3MgOiByaWdodFxuICAgIH0pXG4gICAgcmV0dXJuIGxpbmVcbiAgfVxuXG4gIC8vIFJlbmRlciB2ZXJ0aWNhbCBmb3JtYXQgKGtleS12YWx1ZSBwYWlycykgZm9yIGV4dHJhLW5hcnJvdyB0ZXJtaW5hbHNcbiAgZnVuY3Rpb24gcmVuZGVyVmVydGljYWxGb3JtYXQoKTogc3RyaW5nIHtcbiAgICBjb25zdCBsaW5lczogc3RyaW5nW10gPSBbXVxuICAgIGNvbnN0IGhlYWRlcnMgPSB0b2tlbi5oZWFkZXIubWFwKGggPT4gZ2V0UGxhaW5UZXh0KGgudG9rZW5zKSlcbiAgICBjb25zdCBzZXBhcmF0b3JXaWR0aCA9IE1hdGgubWluKHRlcm1pbmFsV2lkdGggLSAxLCA0MClcbiAgICBjb25zdCBzZXBhcmF0b3IgPSAn4pSAJy5yZXBlYXQoc2VwYXJhdG9yV2lkdGgpXG4gICAgLy8gU21hbGwgaW5kZW50IGZvciB3cmFwcGVkIGxpbmVzIChqdXN0IDIgc3BhY2VzKVxuICAgIGNvbnN0IHdyYXBJbmRlbnQgPSAnICAnXG5cbiAgICB0b2tlbi5yb3dzLmZvckVhY2goKHJvdywgcm93SW5kZXgpID0+IHtcbiAgICAgIGlmIChyb3dJbmRleCA+IDApIHtcbiAgICAgICAgbGluZXMucHVzaChzZXBhcmF0b3IpXG4gICAgICB9XG5cbiAgICAgIHJvdy5mb3JFYWNoKChjZWxsLCBjb2xJbmRleCkgPT4ge1xuICAgICAgICBjb25zdCBsYWJlbCA9IGhlYWRlcnNbY29sSW5kZXhdIHx8IGBDb2x1bW4gJHtjb2xJbmRleCArIDF9YFxuICAgICAgICAvLyBDbGVhbiB2YWx1ZTogdHJpbSwgcmVtb3ZlIGV4dHJhIGludGVybmFsIHdoaXRlc3BhY2UvbmV3bGluZXNcbiAgICAgICAgY29uc3QgcmF3VmFsdWUgPSBmb3JtYXRDZWxsKGNlbGwudG9rZW5zKS50cmltRW5kKClcbiAgICAgICAgY29uc3QgdmFsdWUgPSByYXdWYWx1ZS5yZXBsYWNlKC9cXG4rL2csICcgJykucmVwbGFjZSgvXFxzKy9nLCAnICcpLnRyaW0oKVxuXG4gICAgICAgIC8vIFdyYXAgdmFsdWUgdG8gZml0IHRlcm1pbmFsLCBhY2NvdW50aW5nIGZvciBsYWJlbCBvbiBmaXJzdCBsaW5lXG4gICAgICAgIGNvbnN0IGZpcnN0TGluZVdpZHRoID0gdGVybWluYWxXaWR0aCAtIHN0cmluZ1dpZHRoKGxhYmVsKSAtIDNcbiAgICAgICAgY29uc3Qgc3Vic2VxdWVudExpbmVXaWR0aCA9IHRlcm1pbmFsV2lkdGggLSB3cmFwSW5kZW50Lmxlbmd0aCAtIDFcblxuICAgICAgICAvLyBUd28tcGFzcyB3cmFwOiBmaXJzdCBsaW5lIGlzIG5hcnJvd2VyIChsYWJlbCB0YWtlcyBzcGFjZSksXG4gICAgICAgIC8vIGNvbnRpbnVhdGlvbiBsaW5lcyBnZXQgdGhlIGZ1bGwgd2lkdGggbWludXMgaW5kZW50LlxuICAgICAgICBjb25zdCBmaXJzdFBhc3NMaW5lcyA9IHdyYXBUZXh0KHZhbHVlLCBNYXRoLm1heChmaXJzdExpbmVXaWR0aCwgMTApKVxuICAgICAgICBjb25zdCBmaXJzdExpbmUgPSBmaXJzdFBhc3NMaW5lc1swXSB8fCAnJ1xuXG4gICAgICAgIGxldCB3cmFwcGVkVmFsdWU6IHN0cmluZ1tdXG4gICAgICAgIGlmIChcbiAgICAgICAgICBmaXJzdFBhc3NMaW5lcy5sZW5ndGggPD0gMSB8fFxuICAgICAgICAgIHN1YnNlcXVlbnRMaW5lV2lkdGggPD0gZmlyc3RMaW5lV2lkdGhcbiAgICAgICAgKSB7XG4gICAgICAgICAgd3JhcHBlZFZhbHVlID0gZmlyc3RQYXNzTGluZXNcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBSZS1qb2luIHJlbWFpbmluZyB0ZXh0IGFuZCByZS13cmFwIHRvIHRoZSB3aWRlciBjb250aW51YXRpb24gd2lkdGhcbiAgICAgICAgICBjb25zdCByZW1haW5pbmdUZXh0ID0gZmlyc3RQYXNzTGluZXNcbiAgICAgICAgICAgIC5zbGljZSgxKVxuICAgICAgICAgICAgLm1hcChsID0+IGwudHJpbSgpKVxuICAgICAgICAgICAgLmpvaW4oJyAnKVxuICAgICAgICAgIGNvbnN0IHJld3JhcHBlZCA9IHdyYXBUZXh0KHJlbWFpbmluZ1RleHQsIHN1YnNlcXVlbnRMaW5lV2lkdGgpXG4gICAgICAgICAgd3JhcHBlZFZhbHVlID0gW2ZpcnN0TGluZSwgLi4ucmV3cmFwcGVkXVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gRmlyc3QgbGluZTogYm9sZCBsYWJlbCArIHZhbHVlXG4gICAgICAgIGxpbmVzLnB1c2goXG4gICAgICAgICAgYCR7QU5TSV9CT0xEX1NUQVJUfSR7bGFiZWx9OiR7QU5TSV9CT0xEX0VORH0gJHt3cmFwcGVkVmFsdWVbMF0gfHwgJyd9YCxcbiAgICAgICAgKVxuXG4gICAgICAgIC8vIFN1YnNlcXVlbnQgbGluZXMgd2l0aCBzbWFsbCBpbmRlbnQgKHNraXAgZW1wdHkgbGluZXMpXG4gICAgICAgIGZvciAobGV0IGkgPSAxOyBpIDwgd3JhcHBlZFZhbHVlLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgY29uc3QgbGluZSA9IHdyYXBwZWRWYWx1ZVtpXSFcbiAgICAgICAgICBpZiAoIWxpbmUudHJpbSgpKSBjb250aW51ZVxuICAgICAgICAgIGxpbmVzLnB1c2goYCR7d3JhcEluZGVudH0ke2xpbmV9YClcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9KVxuXG4gICAgcmV0dXJuIGxpbmVzLmpvaW4oJ1xcbicpXG4gIH1cblxuICAvLyBDaG9vc2UgZm9ybWF0IGJhc2VkIG9uIGF2YWlsYWJsZSB3aWR0aFxuICBpZiAodXNlVmVydGljYWxGb3JtYXQpIHtcbiAgICByZXR1cm4gPEFuc2k+e3JlbmRlclZlcnRpY2FsRm9ybWF0KCl9PC9BbnNpPlxuICB9XG5cbiAgLy8gQnVpbGQgdGhlIGNvbXBsZXRlIGhvcml6b250YWwgdGFibGUgYXMgYW4gYXJyYXkgb2Ygc3RyaW5nc1xuICBjb25zdCB0YWJsZUxpbmVzOiBzdHJpbmdbXSA9IFtdXG4gIHRhYmxlTGluZXMucHVzaChyZW5kZXJCb3JkZXJMaW5lKCd0b3AnKSlcbiAgdGFibGVMaW5lcy5wdXNoKC4uLnJlbmRlclJvd0xpbmVzKHRva2VuLmhlYWRlciwgdHJ1ZSkpXG4gIHRhYmxlTGluZXMucHVzaChyZW5kZXJCb3JkZXJMaW5lKCdtaWRkbGUnKSlcbiAgdG9rZW4ucm93cy5mb3JFYWNoKChyb3csIHJvd0luZGV4KSA9PiB7XG4gICAgdGFibGVMaW5lcy5wdXNoKC4uLnJlbmRlclJvd0xpbmVzKHJvdywgZmFsc2UpKVxuICAgIGlmIChyb3dJbmRleCA8IHRva2VuLnJvd3MubGVuZ3RoIC0gMSkge1xuICAgICAgdGFibGVMaW5lcy5wdXNoKHJlbmRlckJvcmRlckxpbmUoJ21pZGRsZScpKVxuICAgIH1cbiAgfSlcbiAgdGFibGVMaW5lcy5wdXNoKHJlbmRlckJvcmRlckxpbmUoJ2JvdHRvbScpKVxuXG4gIC8vIFNhZmV0eSBjaGVjazogdmVyaWZ5IG5vIGxpbmUgZXhjZWVkcyB0ZXJtaW5hbCB3aWR0aC5cbiAgLy8gVGhpcyBjYXRjaGVzIGVkZ2UgY2FzZXMgZHVyaW5nIHRlcm1pbmFsIHJlc2l6ZSB3aGVyZSBjYWxjdWxhdGlvbnNcbiAgLy8gd2VyZSBiYXNlZCBvbiBhIGRpZmZlcmVudCB3aWR0aCB0aGFuIHRoZSBjdXJyZW50IHJlbmRlciB0YXJnZXQuXG4gIGNvbnN0IG1heExpbmVXaWR0aCA9IE1hdGgubWF4KFxuICAgIC4uLnRhYmxlTGluZXMubWFwKGxpbmUgPT4gc3RyaW5nV2lkdGgoc3RyaXBBbnNpKGxpbmUpKSksXG4gIClcblxuICAvLyBJZiB3ZSdyZSB3aXRoaW4gU0FGRVRZX01BUkdJTiBjaGFyYWN0ZXJzIG9mIHRoZSBlZGdlLCB1c2UgdmVydGljYWwgZm9ybWF0XG4gIC8vIHRvIGFjY291bnQgZm9yIHRlcm1pbmFsIHJlc2l6ZSByYWNlIGNvbmRpdGlvbnMuXG4gIGlmIChtYXhMaW5lV2lkdGggPiB0ZXJtaW5hbFdpZHRoIC0gU0FGRVRZX01BUkdJTikge1xuICAgIHJldHVybiA8QW5zaT57cmVuZGVyVmVydGljYWxGb3JtYXQoKX08L0Fuc2k+XG4gIH1cblxuICAvLyBSZW5kZXIgYXMgYSBzaW5nbGUgQW5zaSBibG9jayB0byBwcmV2ZW50IEluayBmcm9tIHdyYXBwaW5nIG1pZC1yb3dcbiAgcmV0dXJuIDxBbnNpPnt0YWJsZUxpbmVzLmpvaW4oJ1xcbicpfTwvQW5zaT5cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsY0FBY0EsS0FBSyxFQUFFQyxNQUFNLFFBQVEsUUFBUTtBQUMzQyxPQUFPQyxLQUFLLE1BQU0sT0FBTztBQUN6QixPQUFPQyxTQUFTLE1BQU0sWUFBWTtBQUNsQyxTQUFTQyxlQUFlLFFBQVEsNkJBQTZCO0FBQzdELFNBQVNDLFdBQVcsUUFBUSx1QkFBdUI7QUFDbkQsU0FBU0MsUUFBUSxRQUFRLG9CQUFvQjtBQUM3QyxTQUFTQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxXQUFXO0FBQzFDLGNBQWNDLFlBQVksUUFBUSwwQkFBMEI7QUFDNUQsU0FBU0MsV0FBVyxFQUFFQyxVQUFVLFFBQVEsc0JBQXNCOztBQUU5RDtBQUNBO0FBQ0E7QUFDQTtBQUNBLE1BQU1DLGFBQWEsR0FBRyxDQUFDOztBQUV2QjtBQUNBLE1BQU1DLGdCQUFnQixHQUFHLENBQUM7O0FBRTFCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNQyxh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 \ No newline at end of file diff --git a/components/MemoryUsageIndicator.tsx b/components/MemoryUsageIndicator.tsx new file mode 100644 index 0000000..aabace3 --- /dev/null +++ b/components/MemoryUsageIndicator.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { useMemoryUsage } from '../hooks/useMemoryUsage.js'; +import { Box, Text } from '../ink.js'; +import { formatFileSize } from '../utils/format.js'; +export function MemoryUsageIndicator(): React.ReactNode { + // Ant-only: the /heapdump link is an internal debugging aid. Gating before + // the hook means the 10s polling interval is never set up in external builds. + // USER_TYPE is a build-time constant, so the hook call below is either always + // reached or dead-code-eliminated — never conditional at runtime. + if ("external" !== 'ant') { + return null; + } + + // eslint-disable-next-line react-hooks/rules-of-hooks + // biome-ignore lint/correctness/useHookAtTopLevel: USER_TYPE is a build-time constant + const memoryUsage = useMemoryUsage(); + if (!memoryUsage) { + return null; + } + const { + heapUsed, + status + } = memoryUsage; + + // Only show indicator when memory usage is high or critical + if (status === 'normal') { + return null; + } + const formattedSize = formatFileSize(heapUsed); + const color = status === 'critical' ? 'error' : 'warning'; + return + + High memory usage ({formattedSize}) · /heapdump + + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZU1lbW9yeVVzYWdlIiwiQm94IiwiVGV4dCIsImZvcm1hdEZpbGVTaXplIiwiTWVtb3J5VXNhZ2VJbmRpY2F0b3IiLCJSZWFjdE5vZGUiLCJtZW1vcnlVc2FnZSIsImhlYXBVc2VkIiwic3RhdHVzIiwiZm9ybWF0dGVkU2l6ZSIsImNvbG9yIl0sInNvdXJjZXMiOlsiTWVtb3J5VXNhZ2VJbmRpY2F0b3IudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTWVtb3J5VXNhZ2UgfSBmcm9tICcuLi9ob29rcy91c2VNZW1vcnlVc2FnZS5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGZvcm1hdEZpbGVTaXplIH0gZnJvbSAnLi4vdXRpbHMvZm9ybWF0LmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gTWVtb3J5VXNhZ2VJbmRpY2F0b3IoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgLy8gQW50LW9ubHk6IHRoZSAvaGVhcGR1bXAgbGluayBpcyBhbiBpbnRlcm5hbCBkZWJ1Z2dpbmcgYWlkLiBHYXRpbmcgYmVmb3JlXG4gIC8vIHRoZSBob29rIG1lYW5zIHRoZSAxMHMgcG9sbGluZyBpbnRlcnZhbCBpcyBuZXZlciBzZXQgdXAgaW4gZXh0ZXJuYWwgYnVpbGRzLlxuICAvLyBVU0VSX1RZUEUgaXMgYSBidWlsZC10aW1lIGNvbnN0YW50LCBzbyB0aGUgaG9vayBjYWxsIGJlbG93IGlzIGVpdGhlciBhbHdheXNcbiAgLy8gcmVhY2hlZCBvciBkZWFkLWNvZGUtZWxpbWluYXRlZCDigJQgbmV2ZXIgY29uZGl0aW9uYWwgYXQgcnVudGltZS5cbiAgaWYgKFwiZXh0ZXJuYWxcIiAhPT0gJ2FudCcpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL3J1bGVzLW9mLWhvb2tzXG4gIC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBVU0VSX1RZUEUgaXMgYSBidWlsZC10aW1lIGNvbnN0YW50XG4gIGNvbnN0IG1lbW9yeVVzYWdlID0gdXNlTWVtb3J5VXNhZ2UoKVxuXG4gIGlmICghbWVtb3J5VXNhZ2UpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgY29uc3QgeyBoZWFwVXNlZCwgc3RhdHVzIH0gPSBtZW1vcnlVc2FnZVxuXG4gIC8vIE9ubHkgc2hvdyBpbmRpY2F0b3Igd2hlbiBtZW1vcnkgdXNhZ2UgaXMgaGlnaCBvciBjcml0aWNhbFxuICBpZiAoc3RhdHVzID09PSAnbm9ybWFsJykge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBmb3JtYXR0ZWRTaXplID0gZm9ybWF0RmlsZVNpemUoaGVhcFVzZWQpXG4gIGNvbnN0IGNvbG9yID0gc3RhdHVzID09PSAnY3JpdGljYWwnID8gJ2Vycm9yJyA6ICd3YXJuaW5nJ1xuXG4gIHJldHVybiAoXG4gICAgPEJveD5cbiAgICAgIDxUZXh0IGNvbG9yPXtjb2xvcn0gd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgIEhpZ2ggbWVtb3J5IHVzYWdlICh7Zm9ybWF0dGVkU2l6ZX0pIMK3IC9oZWFwZHVtcFxuICAgICAgPC9UZXh0PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsY0FBYyxRQUFRLDRCQUE0QjtBQUMzRCxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLFNBQVNDLGNBQWMsUUFBUSxvQkFBb0I7QUFFbkQsT0FBTyxTQUFTQyxvQkFBb0JBLENBQUEsQ0FBRSxFQUFFTCxLQUFLLENBQUNNLFNBQVMsQ0FBQztFQUN0RDtFQUNBO0VBQ0E7RUFDQTtFQUNBLElBQUksVUFBVSxLQUFLLEtBQUssRUFBRTtJQUN4QixPQUFPLElBQUk7RUFDYjs7RUFFQTtFQUNBO0VBQ0EsTUFBTUMsV0FBVyxHQUFHTixjQUFjLENBQUMsQ0FBQztFQUVwQyxJQUFJLENBQUNNLFdBQVcsRUFBRTtJQUNoQixPQUFPLElBQUk7RUFDYjtFQUVBLE1BQU07SUFBRUMsUUFBUTtJQUFFQztFQUFPLENBQUMsR0FBR0YsV0FBVzs7RUFFeEM7RUFDQSxJQUFJRSxNQUFNLEtBQUssUUFBUSxFQUFFO0lBQ3ZCLE9BQU8sSUFBSTtFQUNiO0VBRUEsTUFBTUMsYUFBYSxHQUFHTixjQUFjLENBQUNJLFFBQVEsQ0FBQztFQUM5QyxNQUFNRyxLQUFLLEdBQUdGLE1BQU0sS0FBSyxVQUFVLEdBQUcsT0FBTyxHQUFHLFNBQVM7RUFFekQsT0FDRSxDQUFDLEdBQUc7QUFDUixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDRSxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVTtBQUN6QywyQkFBMkIsQ0FBQ0QsYUFBYSxDQUFDO0FBQzFDLE1BQU0sRUFBRSxJQUFJO0FBQ1osSUFBSSxFQUFFLEdBQUcsQ0FBQztBQUVWIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/Message.tsx b/components/Message.tsx new file mode 100644 index 0000000..ca2ef76 --- /dev/null +++ b/components/Message.tsx @@ -0,0 +1,627 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import type { BetaContentBlock } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'; +import type { ImageBlockParam, TextBlockParam, ThinkingBlockParam, ToolResultBlockParam, ToolUseBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'; +import * as React from 'react'; +import type { Command } from '../commands.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { Box } from '../ink.js'; +import type { Tools } from '../Tool.js'; +import { type ConnectorTextBlock, isConnectorTextBlock } from '../types/connectorText.js'; +import type { AssistantMessage, AttachmentMessage as AttachmentMessageType, CollapsedReadSearchGroup as CollapsedReadSearchGroupType, GroupedToolUseMessage as GroupedToolUseMessageType, NormalizedUserMessage, ProgressMessage, SystemMessage } from '../types/message.js'; +import { type AdvisorBlock, isAdvisorBlock } from '../utils/advisor.js'; +import { isFullscreenEnvEnabled } from '../utils/fullscreen.js'; +import { logError } from '../utils/log.js'; +import type { buildMessageLookups } from '../utils/messages.js'; +import { CompactSummary } from './CompactSummary.js'; +import { AdvisorMessage } from './messages/AdvisorMessage.js'; +import { AssistantRedactedThinkingMessage } from './messages/AssistantRedactedThinkingMessage.js'; +import { AssistantTextMessage } from './messages/AssistantTextMessage.js'; +import { AssistantThinkingMessage } from './messages/AssistantThinkingMessage.js'; +import { AssistantToolUseMessage } from './messages/AssistantToolUseMessage.js'; +import { AttachmentMessage } from './messages/AttachmentMessage.js'; +import { CollapsedReadSearchContent } from './messages/CollapsedReadSearchContent.js'; +import { CompactBoundaryMessage } from './messages/CompactBoundaryMessage.js'; +import { GroupedToolUseContent } from './messages/GroupedToolUseContent.js'; +import { SystemTextMessage } from './messages/SystemTextMessage.js'; +import { UserImageMessage } from './messages/UserImageMessage.js'; +import { UserTextMessage } from './messages/UserTextMessage.js'; +import { UserToolResultMessage } from './messages/UserToolResultMessage/UserToolResultMessage.js'; +import { OffscreenFreeze } from './OffscreenFreeze.js'; +import { ExpandShellOutputProvider } from './shell/ExpandShellOutputContext.js'; +export type Props = { + message: NormalizedUserMessage | AssistantMessage | AttachmentMessageType | SystemMessage | GroupedToolUseMessageType | CollapsedReadSearchGroupType; + lookups: ReturnType; + // TODO: Find a way to remove this, and leave spacing to the consumer + /** Absolute width for the container Box. When provided, eliminates a wrapper Box in the caller. */ + containerWidth?: number; + addMargin: boolean; + tools: Tools; + commands: Command[]; + verbose: boolean; + inProgressToolUseIDs: Set; + progressMessagesForMessage: ProgressMessage[]; + shouldAnimate: boolean; + shouldShowDot: boolean; + style?: 'condensed'; + width?: number | string; + isTranscriptMode: boolean; + isStatic: boolean; + onOpenRateLimitOptions?: () => void; + isActiveCollapsedGroup?: boolean; + isUserContinuation?: boolean; + /** ID of the last thinking block (uuid:index) to show, used for hiding past thinking in transcript mode */ + lastThinkingBlockId?: string | null; + /** UUID of the latest user bash output message (for auto-expanding) */ + latestBashOutputUUID?: string | null; +}; +function MessageImpl(t0) { + const $ = _c(94); + const { + message, + lookups, + containerWidth, + addMargin, + tools, + commands, + verbose, + inProgressToolUseIDs, + progressMessagesForMessage, + shouldAnimate, + shouldShowDot, + style, + width, + isTranscriptMode, + onOpenRateLimitOptions, + isActiveCollapsedGroup, + isUserContinuation: t1, + lastThinkingBlockId, + latestBashOutputUUID + } = t0; + const isUserContinuation = t1 === undefined ? false : t1; + switch (message.type) { + case "attachment": + { + let t2; + if ($[0] !== addMargin || $[1] !== isTranscriptMode || $[2] !== message.attachment || $[3] !== verbose) { + t2 = ; + $[0] = addMargin; + $[1] = isTranscriptMode; + $[2] = message.attachment; + $[3] = verbose; + $[4] = t2; + } else { + t2 = $[4]; + } + return t2; + } + case "assistant": + { + const t2 = containerWidth ?? "100%"; + let t3; + if ($[5] !== addMargin || $[6] !== commands || $[7] !== inProgressToolUseIDs || $[8] !== isTranscriptMode || $[9] !== lastThinkingBlockId || $[10] !== lookups || $[11] !== message.advisorModel || $[12] !== message.message.content || $[13] !== message.uuid || $[14] !== onOpenRateLimitOptions || $[15] !== progressMessagesForMessage || $[16] !== shouldAnimate || $[17] !== shouldShowDot || $[18] !== tools || $[19] !== verbose || $[20] !== width) { + let t4; + if ($[22] !== addMargin || $[23] !== commands || $[24] !== inProgressToolUseIDs || $[25] !== isTranscriptMode || $[26] !== lastThinkingBlockId || $[27] !== lookups || $[28] !== message.advisorModel || $[29] !== message.uuid || $[30] !== onOpenRateLimitOptions || $[31] !== progressMessagesForMessage || $[32] !== shouldAnimate || $[33] !== shouldShowDot || $[34] !== tools || $[35] !== verbose || $[36] !== width) { + t4 = (_, index_0) => ; + $[22] = addMargin; + $[23] = commands; + $[24] = inProgressToolUseIDs; + $[25] = isTranscriptMode; + $[26] = lastThinkingBlockId; + $[27] = lookups; + $[28] = message.advisorModel; + $[29] = message.uuid; + $[30] = onOpenRateLimitOptions; + $[31] = progressMessagesForMessage; + $[32] = shouldAnimate; + $[33] = shouldShowDot; + $[34] = tools; + $[35] = verbose; + $[36] = width; + $[37] = t4; + } else { + t4 = $[37]; + } + t3 = message.message.content.map(t4); + $[5] = addMargin; + $[6] = commands; + $[7] = inProgressToolUseIDs; + $[8] = isTranscriptMode; + $[9] = lastThinkingBlockId; + $[10] = lookups; + $[11] = message.advisorModel; + $[12] = message.message.content; + $[13] = message.uuid; + $[14] = onOpenRateLimitOptions; + $[15] = progressMessagesForMessage; + $[16] = shouldAnimate; + $[17] = shouldShowDot; + $[18] = tools; + $[19] = verbose; + $[20] = width; + $[21] = t3; + } else { + t3 = $[21]; + } + let t4; + if ($[38] !== t2 || $[39] !== t3) { + t4 = {t3}; + $[38] = t2; + $[39] = t3; + $[40] = t4; + } else { + t4 = $[40]; + } + return t4; + } + case "user": + { + if (message.isCompactSummary) { + const t2 = isTranscriptMode ? "transcript" : "prompt"; + let t3; + if ($[41] !== message || $[42] !== t2) { + t3 = ; + $[41] = message; + $[42] = t2; + $[43] = t3; + } else { + t3 = $[43]; + } + return t3; + } + let imageIndices; + if ($[44] !== message.imagePasteIds || $[45] !== message.message.content) { + imageIndices = []; + let imagePosition = 0; + for (const param of message.message.content) { + if (param.type === "image") { + const id = message.imagePasteIds?.[imagePosition]; + imagePosition++; + imageIndices.push(id ?? imagePosition); + } else { + imageIndices.push(imagePosition); + } + } + $[44] = message.imagePasteIds; + $[45] = message.message.content; + $[46] = imageIndices; + } else { + imageIndices = $[46]; + } + const isLatestBashOutput = latestBashOutputUUID === message.uuid; + const t2 = containerWidth ?? "100%"; + let t3; + if ($[47] !== addMargin || $[48] !== imageIndices || $[49] !== isTranscriptMode || $[50] !== isUserContinuation || $[51] !== lookups || $[52] !== message || $[53] !== progressMessagesForMessage || $[54] !== style || $[55] !== tools || $[56] !== verbose) { + t3 = message.message.content.map((param_0, index) => ); + $[47] = addMargin; + $[48] = imageIndices; + $[49] = isTranscriptMode; + $[50] = isUserContinuation; + $[51] = lookups; + $[52] = message; + $[53] = progressMessagesForMessage; + $[54] = style; + $[55] = tools; + $[56] = verbose; + $[57] = t3; + } else { + t3 = $[57]; + } + let t4; + if ($[58] !== t2 || $[59] !== t3) { + t4 = {t3}; + $[58] = t2; + $[59] = t3; + $[60] = t4; + } else { + t4 = $[60]; + } + const content = t4; + let t5; + if ($[61] !== content || $[62] !== isLatestBashOutput) { + t5 = isLatestBashOutput ? {content} : content; + $[61] = content; + $[62] = isLatestBashOutput; + $[63] = t5; + } else { + t5 = $[63]; + } + return t5; + } + case "system": + { + if (message.subtype === "compact_boundary") { + if (isFullscreenEnvEnabled()) { + return null; + } + let t2; + if ($[64] === Symbol.for("react.memo_cache_sentinel")) { + t2 = ; + $[64] = t2; + } else { + t2 = $[64]; + } + return t2; + } + if (message.subtype === "microcompact_boundary") { + return null; + } + if (feature("HISTORY_SNIP")) { + const { + isSnipBoundaryMessage + } = require("../services/compact/snipProjection.js") as typeof import('../services/compact/snipProjection.js'); + const { + isSnipMarkerMessage + } = require("../services/compact/snipCompact.js") as typeof import('../services/compact/snipCompact.js'); + if (isSnipBoundaryMessage(message)) { + let t2; + if ($[65] === Symbol.for("react.memo_cache_sentinel")) { + t2 = require("./messages/SnipBoundaryMessage.js"); + $[65] = t2; + } else { + t2 = $[65]; + } + const { + SnipBoundaryMessage + } = t2 as typeof import('./messages/SnipBoundaryMessage.js'); + let t3; + if ($[66] !== message) { + t3 = ; + $[66] = message; + $[67] = t3; + } else { + t3 = $[67]; + } + return t3; + } + if (isSnipMarkerMessage(message)) { + return null; + } + } + if (message.subtype === "local_command") { + let t2; + if ($[68] !== message.content) { + t2 = { + type: "text", + text: message.content + }; + $[68] = message.content; + $[69] = t2; + } else { + t2 = $[69]; + } + let t3; + if ($[70] !== addMargin || $[71] !== isTranscriptMode || $[72] !== t2 || $[73] !== verbose) { + t3 = ; + $[70] = addMargin; + $[71] = isTranscriptMode; + $[72] = t2; + $[73] = verbose; + $[74] = t3; + } else { + t3 = $[74]; + } + return t3; + } + let t2; + if ($[75] !== addMargin || $[76] !== isTranscriptMode || $[77] !== message || $[78] !== verbose) { + t2 = ; + $[75] = addMargin; + $[76] = isTranscriptMode; + $[77] = message; + $[78] = verbose; + $[79] = t2; + } else { + t2 = $[79]; + } + return t2; + } + case "grouped_tool_use": + { + let t2; + if ($[80] !== inProgressToolUseIDs || $[81] !== lookups || $[82] !== message || $[83] !== shouldAnimate || $[84] !== tools) { + t2 = ; + $[80] = inProgressToolUseIDs; + $[81] = lookups; + $[82] = message; + $[83] = shouldAnimate; + $[84] = tools; + $[85] = t2; + } else { + t2 = $[85]; + } + return t2; + } + case "collapsed_read_search": + { + const t2 = verbose || isTranscriptMode; + let t3; + if ($[86] !== inProgressToolUseIDs || $[87] !== isActiveCollapsedGroup || $[88] !== lookups || $[89] !== message || $[90] !== shouldAnimate || $[91] !== t2 || $[92] !== tools) { + t3 = ; + $[86] = inProgressToolUseIDs; + $[87] = isActiveCollapsedGroup; + $[88] = lookups; + $[89] = message; + $[90] = shouldAnimate; + $[91] = t2; + $[92] = tools; + $[93] = t3; + } else { + t3 = $[93]; + } + return t3; + } + } +} +function UserMessage(t0) { + const $ = _c(20); + const { + message, + addMargin, + tools, + progressMessagesForMessage, + param, + style, + verbose, + imageIndex, + isUserContinuation, + lookups, + isTranscriptMode + } = t0; + const { + columns + } = useTerminalSize(); + switch (param.type) { + case "text": + { + let t1; + if ($[0] !== addMargin || $[1] !== isTranscriptMode || $[2] !== message.planContent || $[3] !== message.timestamp || $[4] !== param || $[5] !== verbose) { + t1 = ; + $[0] = addMargin; + $[1] = isTranscriptMode; + $[2] = message.planContent; + $[3] = message.timestamp; + $[4] = param; + $[5] = verbose; + $[6] = t1; + } else { + t1 = $[6]; + } + return t1; + } + case "image": + { + const t1 = addMargin && !isUserContinuation; + let t2; + if ($[7] !== imageIndex || $[8] !== t1) { + t2 = ; + $[7] = imageIndex; + $[8] = t1; + $[9] = t2; + } else { + t2 = $[9]; + } + return t2; + } + case "tool_result": + { + const t1 = columns - 5; + let t2; + if ($[10] !== isTranscriptMode || $[11] !== lookups || $[12] !== message || $[13] !== param || $[14] !== progressMessagesForMessage || $[15] !== style || $[16] !== t1 || $[17] !== tools || $[18] !== verbose) { + t2 = ; + $[10] = isTranscriptMode; + $[11] = lookups; + $[12] = message; + $[13] = param; + $[14] = progressMessagesForMessage; + $[15] = style; + $[16] = t1; + $[17] = tools; + $[18] = verbose; + $[19] = t2; + } else { + t2 = $[19]; + } + return t2; + } + default: + { + return; + } + } +} +function AssistantMessageBlock(t0) { + const $ = _c(45); + const { + param, + addMargin, + tools, + commands, + verbose, + inProgressToolUseIDs, + progressMessagesForMessage, + shouldAnimate, + shouldShowDot, + width, + inProgressToolCallCount, + isTranscriptMode, + lookups, + onOpenRateLimitOptions, + thinkingBlockId, + lastThinkingBlockId, + advisorModel + } = t0; + if (feature("CONNECTOR_TEXT")) { + if (isConnectorTextBlock(param)) { + let t1; + if ($[0] !== param.connector_text) { + t1 = { + type: "text", + text: param.connector_text + }; + $[0] = param.connector_text; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== addMargin || $[3] !== onOpenRateLimitOptions || $[4] !== shouldShowDot || $[5] !== t1 || $[6] !== verbose || $[7] !== width) { + t2 = ; + $[2] = addMargin; + $[3] = onOpenRateLimitOptions; + $[4] = shouldShowDot; + $[5] = t1; + $[6] = verbose; + $[7] = width; + $[8] = t2; + } else { + t2 = $[8]; + } + return t2; + } + } + switch (param.type) { + case "tool_use": + { + let t1; + if ($[9] !== addMargin || $[10] !== commands || $[11] !== inProgressToolCallCount || $[12] !== inProgressToolUseIDs || $[13] !== isTranscriptMode || $[14] !== lookups || $[15] !== param || $[16] !== progressMessagesForMessage || $[17] !== shouldAnimate || $[18] !== shouldShowDot || $[19] !== tools || $[20] !== verbose) { + t1 = ; + $[9] = addMargin; + $[10] = commands; + $[11] = inProgressToolCallCount; + $[12] = inProgressToolUseIDs; + $[13] = isTranscriptMode; + $[14] = lookups; + $[15] = param; + $[16] = progressMessagesForMessage; + $[17] = shouldAnimate; + $[18] = shouldShowDot; + $[19] = tools; + $[20] = verbose; + $[21] = t1; + } else { + t1 = $[21]; + } + return t1; + } + case "text": + { + let t1; + if ($[22] !== addMargin || $[23] !== onOpenRateLimitOptions || $[24] !== param || $[25] !== shouldShowDot || $[26] !== verbose || $[27] !== width) { + t1 = ; + $[22] = addMargin; + $[23] = onOpenRateLimitOptions; + $[24] = param; + $[25] = shouldShowDot; + $[26] = verbose; + $[27] = width; + $[28] = t1; + } else { + t1 = $[28]; + } + return t1; + } + case "redacted_thinking": + { + if (!isTranscriptMode && !verbose) { + return null; + } + let t1; + if ($[29] !== addMargin) { + t1 = ; + $[29] = addMargin; + $[30] = t1; + } else { + t1 = $[30]; + } + return t1; + } + case "thinking": + { + if (!isTranscriptMode && !verbose) { + return null; + } + const isLastThinking = !lastThinkingBlockId || thinkingBlockId === lastThinkingBlockId; + const t1 = isTranscriptMode && !isLastThinking; + let t2; + if ($[31] !== addMargin || $[32] !== isTranscriptMode || $[33] !== param || $[34] !== t1 || $[35] !== verbose) { + t2 = ; + $[31] = addMargin; + $[32] = isTranscriptMode; + $[33] = param; + $[34] = t1; + $[35] = verbose; + $[36] = t2; + } else { + t2 = $[36]; + } + return t2; + } + case "server_tool_use": + case "advisor_tool_result": + { + if (isAdvisorBlock(param)) { + const t1 = verbose || isTranscriptMode; + let t2; + if ($[37] !== addMargin || $[38] !== advisorModel || $[39] !== lookups.erroredToolUseIDs || $[40] !== lookups.resolvedToolUseIDs || $[41] !== param || $[42] !== shouldAnimate || $[43] !== t1) { + t2 = ; + $[37] = addMargin; + $[38] = advisorModel; + $[39] = lookups.erroredToolUseIDs; + $[40] = lookups.resolvedToolUseIDs; + $[41] = param; + $[42] = shouldAnimate; + $[43] = t1; + $[44] = t2; + } else { + t2 = $[44]; + } + return t2; + } + logError(new Error(`Unable to render server tool block: ${param.type}`)); + return null; + } + default: + { + logError(new Error(`Unable to render message type: ${param.type}`)); + return null; + } + } +} +export function hasThinkingContent(m: { + type: string; + message?: { + content: Array<{ + type: string; + }>; + }; +}): boolean { + if (m.type !== 'assistant' || !m.message) return false; + return m.message.content.some(b => b.type === 'thinking' || b.type === 'redacted_thinking'); +} + +/** Exported for testing */ +export function areMessagePropsEqual(prev: Props, next: Props): boolean { + if (prev.message.uuid !== next.message.uuid) return false; + // Only re-render on lastThinkingBlockId change if this message actually + // has thinking content — otherwise every message in scrollback re-renders + // whenever streaming thinking starts/stops (CC-941). + if (prev.lastThinkingBlockId !== next.lastThinkingBlockId && hasThinkingContent(next.message)) { + return false; + } + // Verbose toggle changes thinking block visibility/expansion + if (prev.verbose !== next.verbose) return false; + // Only re-render if this message's "is latest bash output" status changed, + // not when the global latestBashOutputUUID changes to a different message + const prevIsLatest = prev.latestBashOutputUUID === prev.message.uuid; + const nextIsLatest = next.latestBashOutputUUID === next.message.uuid; + if (prevIsLatest !== nextIsLatest) return false; + if (prev.isTranscriptMode !== next.isTranscriptMode) return false; + // containerWidth is an absolute number in the no-metadata path (wrapper + // Box is skipped). Static messages must re-render on terminal resize. + if (prev.containerWidth !== next.containerWidth) return false; + if (prev.isStatic && next.isStatic) return true; + return false; +} +export const Message = React.memo(MessageImpl, areMessagePropsEqual); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiQmV0YUNvbnRlbnRCbG9jayIsIkltYWdlQmxvY2tQYXJhbSIsIlRleHRCbG9ja1BhcmFtIiwiVGhpbmtpbmdCbG9ja1BhcmFtIiwiVG9vbFJlc3VsdEJsb2NrUGFyYW0iLCJUb29sVXNlQmxvY2tQYXJhbSIsIlJlYWN0IiwiQ29tbWFuZCIsInVzZVRlcm1pbmFsU2l6ZSIsIkJveCIsIlRvb2xzIiwiQ29ubmVjdG9yVGV4dEJsb2NrIiwiaXNDb25uZWN0b3JUZXh0QmxvY2siLCJBc3Npc3RhbnRNZXNzYWdlIiwiQXR0YWNobWVudE1lc3NhZ2UiLCJBdHRhY2htZW50TWVzc2FnZVR5cGUiLCJDb2xsYXBzZWRSZWFkU2VhcmNoR3JvdXAiLCJDb2xsYXBzZWRSZWFkU2VhcmNoR3JvdXBUeXBlIiwiR3JvdXBlZFRvb2xVc2VNZXNzYWdlIiwiR3JvdXBlZFRvb2xVc2VNZXNzYWdlVHlwZSIsIk5vcm1hbGl6ZWRVc2VyTWVzc2FnZSIsIlByb2dyZXNzTWVzc2FnZSIsIlN5c3RlbU1lc3NhZ2UiLCJBZHZpc29yQmxvY2siLCJpc0Fkdmlzb3JCbG9jayIsImlzRnVsbHNjcmVlbkVudkVuYWJsZWQiLCJsb2dFcnJvciIsImJ1aWxkTWVzc2FnZUxvb2t1cHMiLCJDb21wYWN0U3VtbWFyeSIsIkFkdmlzb3JNZXNzYWdlIiwiQXNzaXN0YW50UmVkYWN0ZWRUaGlua2luZ01lc3NhZ2UiLCJBc3Npc3RhbnRUZXh0TWVzc2FnZSIsIkFzc2lzdGFudFRoaW5raW5nTWVzc2FnZSIsIkFzc2lzdGFudFRvb2xVc2VNZXNzYWdlIiwiQ29sbGFwc2VkUmVhZFNlYXJjaENvbnRlbnQiLCJDb21wYWN0Qm91bmRhcnlNZXNzYWdlIiwiR3JvdXBlZFRvb2xVc2VDb250ZW50IiwiU3lzdGVtVGV4dE1lc3NhZ2UiLCJVc2VySW1hZ2VNZXNzYWdlIiwiVXNlclRleHRNZXNzYWdlIiwiVXNlclRvb2xSZXN1bHRNZXNzYWdlIiwiT2Zmc2NyZWVuRnJlZXplIiwiRXhwYW5kU2hlbGxPdXRwdXRQcm92aWRlciIsIlByb3BzIiwibWVzc2FnZSIsImxvb2t1cHMiLCJSZXR1cm5UeXBlIiwiY29udGFpbmVyV2lkdGgiLCJhZGRNYXJnaW4iLCJ0b29scyIsImNvbW1hbmRzIiwidmVyYm9zZSIsImluUHJvZ3Jlc3NUb29sVXNlSURzIiwiU2V0IiwicHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2UiLCJzaG91bGRBbmltYXRlIiwic2hvdWxkU2hvd0RvdCIsInN0eWxlIiwid2lkdGgiLCJpc1RyYW5zY3JpcHRNb2RlIiwiaXNTdGF0aWMiLCJvbk9wZW5SYXRlTGltaXRPcHRpb25zIiwiaXNBY3RpdmVDb2xsYXBzZWRHcm91cCIsImlzVXNlckNvbnRpbnVhdGlvbiIsImxhc3RUaGlua2luZ0Jsb2NrSWQiLCJsYXRlc3RCYXNoT3V0cHV0VVVJRCIsIk1lc3NhZ2VJbXBsIiwidDAiLCIkIiwiX2MiLCJ0MSIsInVuZGVmaW5lZCIsInR5cGUiLCJ0MiIsImF0dGFjaG1lbnQiLCJ0MyIsImFkdmlzb3JNb2RlbCIsImNvbnRlbnQiLCJ1dWlkIiwidDQiLCJfIiwiaW5kZXhfMCIsImluZGV4Iiwic2l6ZSIsIm1hcCIsImlzQ29tcGFjdFN1bW1hcnkiLCJpbWFnZUluZGljZXMiLCJpbWFnZVBhc3RlSWRzIiwiaW1hZ2VQb3NpdGlvbiIsInBhcmFtIiwiaWQiLCJwdXNoIiwiaXNMYXRlc3RCYXNoT3V0cHV0IiwicGFyYW1fMCIsInQ1Iiwic3VidHlwZSIsIlN5bWJvbCIsImZvciIsImlzU25pcEJvdW5kYXJ5TWVzc2FnZSIsInJlcXVpcmUiLCJpc1NuaXBNYXJrZXJNZXNzYWdlIiwiU25pcEJvdW5kYXJ5TWVzc2FnZSIsInRleHQiLCJVc2VyTWVzc2FnZSIsImltYWdlSW5kZXgiLCJjb2x1bW5zIiwicGxhbkNvbnRlbnQiLCJ0aW1lc3RhbXAiLCJBc3Npc3RhbnRNZXNzYWdlQmxvY2siLCJpblByb2dyZXNzVG9vbENhbGxDb3VudCIsInRoaW5raW5nQmxvY2tJZCIsImNvbm5lY3Rvcl90ZXh0IiwiaXNMYXN0VGhpbmtpbmciLCJlcnJvcmVkVG9vbFVzZUlEcyIsInJlc29sdmVkVG9vbFVzZUlEcyIsIkVycm9yIiwiaGFzVGhpbmtpbmdDb250ZW50IiwibSIsIkFycmF5Iiwic29tZSIsImIiLCJhcmVNZXNzYWdlUHJvcHNFcXVhbCIsInByZXYiLCJuZXh0IiwicHJldklzTGF0ZXN0IiwibmV4dElzTGF0ZXN0IiwiTWVzc2FnZSIsIm1lbW8iXSwic291cmNlcyI6WyJNZXNzYWdlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCB0eXBlIHsgQmV0YUNvbnRlbnRCbG9jayB9IGZyb20gJ0BhbnRocm9waWMtYWkvc2RrL3Jlc291cmNlcy9iZXRhL21lc3NhZ2VzL21lc3NhZ2VzLm1qcydcbmltcG9ydCB0eXBlIHtcbiAgSW1hZ2VCbG9ja1BhcmFtLFxuICBUZXh0QmxvY2tQYXJhbSxcbiAgVGhpbmtpbmdCbG9ja1BhcmFtLFxuICBUb29sUmVzdWx0QmxvY2tQYXJhbSxcbiAgVG9vbFVzZUJsb2NrUGFyYW0sXG59IGZyb20gJ0BhbnRocm9waWMtYWkvc2RrL3Jlc291cmNlcy9pbmRleC5tanMnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgQ29tbWFuZCB9IGZyb20gJy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgQm94IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBUb29scyB9IGZyb20gJy4uL1Rvb2wuanMnXG5pbXBvcnQge1xuICB0eXBlIENvbm5lY3RvclRleHRCbG9jayxcbiAgaXNDb25uZWN0b3JUZXh0QmxvY2ssXG59IGZyb20gJy4uL3R5cGVzL2Nvbm5lY3RvclRleHQuanMnXG5pbXBvcnQgdHlwZSB7XG4gIEFzc2lzdGFudE1lc3NhZ2UsXG4gIEF0dGFjaG1lbnRNZXNzYWdlIGFzIEF0dGFjaG1lbnRNZXNzYWdlVHlwZSxcbiAgQ29sbGFwc2VkUmVhZFNlYXJjaEdyb3VwIGFzIENvbGxhcHNlZFJlYWRTZWFyY2hHcm91cFR5cGUsXG4gIEdyb3VwZWRUb29sVXNlTWVzc2FnZSBhcyBHcm91cGVkVG9vbFVzZU1lc3NhZ2VUeXBlLFxuICBOb3JtYWxpemVkVXNlck1lc3NhZ2UsXG4gIFByb2dyZXNzTWVzc2FnZSxcbiAgU3lzdGVtTWVzc2FnZSxcbn0gZnJvbSAnLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IHR5cGUgQWR2aXNvckJsb2NrLCBpc0Fkdmlzb3JCbG9jayB9IGZyb20gJy4uL3V0aWxzL2Fkdmlzb3IuanMnXG5pbXBvcnQgeyBpc0Z1bGxzY3JlZW5FbnZFbmFibGVkIH0gZnJvbSAnLi4vdXRpbHMvZnVsbHNjcmVlbi5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnLi4vdXRpbHMvbG9nLmpzJ1xuaW1wb3J0IHR5cGUgeyBidWlsZE1lc3NhZ2VMb29rdXBzIH0gZnJvbSAnLi4vdXRpbHMvbWVzc2FnZXMuanMnXG5pbXBvcnQgeyBDb21wYWN0U3VtbWFyeSB9IGZyb20gJy4vQ29tcGFjdFN1bW1hcnkuanMnXG5pbXBvcnQgeyBBZHZpc29yTWVzc2FnZSB9IGZyb20gJy4vbWVzc2FnZXMvQWR2aXNvck1lc3NhZ2UuanMnXG5pbXBvcnQgeyBBc3Npc3RhbnRSZWRhY3RlZFRoaW5raW5nTWVzc2FnZSB9IGZyb20gJy4vbWVzc2FnZXMvQXNzaXN0YW50UmVkYWN0ZWRUaGlua2luZ01lc3NhZ2UuanMnXG5pbXBvcnQgeyBBc3Npc3RhbnRUZXh0TWVzc2FnZSB9IGZyb20gJy4vbWVzc2FnZXMvQXNzaXN0YW50VGV4dE1lc3NhZ2UuanMnXG5pbXBvcnQgeyBBc3Npc3RhbnRUaGlua2luZ01lc3NhZ2UgfSBmcm9tICcuL21lc3NhZ2VzL0Fzc2lzdGFudFRoaW5raW5nTWVzc2FnZS5qcydcbmltcG9ydCB7IEFzc2lzdGFudFRvb2xVc2VNZXNzYWdlIH0gZnJvbSAnLi9tZXNzYWdlcy9Bc3Npc3RhbnRUb29sVXNlTWVzc2FnZS5qcydcbmltcG9ydCB7IEF0dGFjaG1lbnRNZXNzYWdlIH0gZnJvbSAnLi9tZXNzYWdlcy9BdHRhY2htZW50TWVzc2FnZS5qcydcbmltcG9ydCB7IENvbGxhcHNlZFJlYWRTZWFyY2hDb250ZW50IH0gZnJvbSAnLi9tZXNzYWdlcy9Db2xsYXBzZWRSZWFkU2VhcmNoQ29udGVudC5qcydcbmltcG9ydCB7IENvbXBhY3RCb3VuZGFyeU1lc3NhZ2UgfSBmcm9tICcuL21lc3NhZ2VzL0NvbXBhY3RCb3VuZGFyeU1lc3NhZ2UuanMnXG5pbXBvcnQgeyBHcm91cGVkVG9vbFVzZUNvbnRlbnQgfSBmcm9tICcuL21lc3NhZ2VzL0dyb3VwZWRUb29sVXNlQ29udGVudC5qcydcbmltcG9ydCB7IFN5c3RlbVRleHRNZXNzYWdlIH0gZnJvbSAnLi9tZXNzYWdlcy9TeXN0ZW1UZXh0TWVzc2FnZS5qcydcbmltcG9ydCB7IFVzZXJJbWFnZU1lc3NhZ2UgfSBmcm9tICcuL21lc3NhZ2VzL1VzZXJJbWFnZU1lc3NhZ2UuanMnXG5pbXBvcnQgeyBVc2VyVGV4dE1lc3NhZ2UgfSBmcm9tICcuL21lc3NhZ2VzL1VzZXJUZXh0TWVzc2FnZS5qcydcbmltcG9ydCB7IFVzZXJUb29sUmVzdWx0TWVzc2FnZSB9IGZyb20gJy4vbWVzc2FnZXMvVXNlclRvb2xSZXN1bHRNZXNzYWdlL1VzZXJUb29sUmVzdWx0TWVzc2FnZS5qcydcbmltcG9ydCB7IE9mZnNjcmVlbkZyZWV6ZSB9IGZyb20gJy4vT2Zmc2NyZWVuRnJlZXplLmpzJ1xuaW1wb3J0IHsgRXhwYW5kU2hlbGxPdXRwdXRQcm92aWRlciB9IGZyb20gJy4vc2hlbGwvRXhwYW5kU2hlbGxPdXRwdXRDb250ZXh0LmpzJ1xuXG5leHBvcnQgdHlwZSBQcm9wcyA9IHtcbiAgbWVzc2FnZTpcbiAgICB8IE5vcm1hbGl6ZWRVc2VyTWVzc2FnZVxuICAgIHwgQXNzaXN0YW50TWVzc2FnZVxuICAgIHwgQXR0YWNobWVudE1lc3NhZ2VUeXBlXG4gICAgfCBTeXN0ZW1NZXNzYWdlXG4gICAgfCBHcm91cGVkVG9vbFVzZU1lc3NhZ2VUeXBlXG4gICAgfCBDb2xsYXBzZWRSZWFkU2VhcmNoR3JvdXBUeXBlXG4gIGxvb2t1cHM6IFJldHVyblR5cGU8dHlwZW9mIGJ1aWxkTWVzc2FnZUxvb2t1cHM+XG4gIC8vIFRPRE86IEZpbmQgYSB3YXkgdG8gcmVtb3ZlIHRoaXMsIGFuZCBsZWF2ZSBzcGFjaW5nIHRvIHRoZSBjb25zdW1lclxuICAvKiogQWJzb2x1dGUgd2lkdGggZm9yIHRoZSBjb250YWluZXIgQm94LiBXaGVuIHByb3ZpZGVkLCBlbGltaW5hdGVzIGEgd3JhcHBlciBCb3ggaW4gdGhlIGNhbGxlci4gKi9cbiAgY29udGFpbmVyV2lkdGg/OiBudW1iZXJcbiAgYWRkTWFyZ2luOiBib29sZWFuXG4gIHRvb2xzOiBUb29sc1xuICBjb21tYW5kczogQ29tbWFuZFtdXG4gIHZlcmJvc2U6IGJvb2xlYW5cbiAgaW5Qcm9ncmVzc1Rvb2xVc2VJRHM6IFNldDxzdHJpbmc+XG4gIHByb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlOiBQcm9ncmVzc01lc3NhZ2VbXVxuICBzaG91bGRBbmltYXRlOiBib29sZWFuXG4gIHNob3VsZFNob3dEb3Q6IGJvb2xlYW5cbiAgc3R5bGU/OiAnY29uZGVuc2VkJ1xuICB3aWR0aD86IG51bWJlciB8IHN0cmluZ1xuICBpc1RyYW5zY3JpcHRNb2RlOiBib29sZWFuXG4gIGlzU3RhdGljOiBib29sZWFuXG4gIG9uT3BlblJhdGVMaW1pdE9wdGlvbnM/OiAoKSA9PiB2b2lkXG4gIGlzQWN0aXZlQ29sbGFwc2VkR3JvdXA/OiBib29sZWFuXG4gIGlzVXNlckNvbnRpbnVhdGlvbj86IGJvb2xlYW5cbiAgLyoqIElEIG9mIHRoZSBsYXN0IHRoaW5raW5nIGJsb2NrICh1dWlkOmluZGV4KSB0byBzaG93LCB1c2VkIGZvciBoaWRpbmcgcGFzdCB0aGlua2luZyBpbiB0cmFuc2NyaXB0IG1vZGUgKi9cbiAgbGFzdFRoaW5raW5nQmxvY2tJZD86IHN0cmluZyB8IG51bGxcbiAgLyoqIFVVSUQgb2YgdGhlIGxhdGVzdCB1c2VyIGJhc2ggb3V0cHV0IG1lc3NhZ2UgKGZvciBhdXRvLWV4cGFuZGluZykgKi9cbiAgbGF0ZXN0QmFzaE91dHB1dFVVSUQ/OiBzdHJpbmcgfCBudWxsXG59XG5cbmZ1bmN0aW9uIE1lc3NhZ2VJbXBsKHtcbiAgbWVzc2FnZSxcbiAgbG9va3VwcyxcbiAgY29udGFpbmVyV2lkdGgsXG4gIGFkZE1hcmdpbixcbiAgdG9vbHMsXG4gIGNvbW1hbmRzLFxuICB2ZXJib3NlLFxuICBpblByb2dyZXNzVG9vbFVzZUlEcyxcbiAgcHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2UsXG4gIHNob3VsZEFuaW1hdGUsXG4gIHNob3VsZFNob3dEb3QsXG4gIHN0eWxlLFxuICB3aWR0aCxcbiAgaXNUcmFuc2NyaXB0TW9kZSxcbiAgb25PcGVuUmF0ZUxpbWl0T3B0aW9ucyxcbiAgaXNBY3RpdmVDb2xsYXBzZWRHcm91cCxcbiAgaXNVc2VyQ29udGludWF0aW9uID0gZmFsc2UsXG4gIGxhc3RUaGlua2luZ0Jsb2NrSWQsXG4gIGxhdGVzdEJhc2hPdXRwdXRVVUlELFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBzd2l0Y2ggKG1lc3NhZ2UudHlwZSkge1xuICAgIGNhc2UgJ2F0dGFjaG1lbnQnOlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPEF0dGFjaG1lbnRNZXNzYWdlXG4gICAgICAgICAgYWRkTWFyZ2luPXthZGRNYXJnaW59XG4gICAgICAgICAgYXR0YWNobWVudD17bWVzc2FnZS5hdHRhY2htZW50fVxuICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgICAgaXNUcmFuc2NyaXB0TW9kZT17aXNUcmFuc2NyaXB0TW9kZX1cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICBjYXNlICdhc3Npc3RhbnQnOlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9e2NvbnRhaW5lcldpZHRoID8/ICcxMDAlJ30+XG4gICAgICAgICAge21lc3NhZ2UubWVzc2FnZS5jb250ZW50Lm1hcCgoXywgaW5kZXgpID0+IChcbiAgICAgICAgICAgIDxBc3Npc3RhbnRNZXNzYWdlQmxvY2tcbiAgICAgICAgICAgICAga2V5PXtpbmRleH1cbiAgICAgICAgICAgICAgcGFyYW09e199XG4gICAgICAgICAgICAgIGFkZE1hcmdpbj17YWRkTWFyZ2lufVxuICAgICAgICAgICAgICB0b29scz17dG9vbHN9XG4gICAgICAgICAgICAgIGNvbW1hbmRzPXtjb21tYW5kc31cbiAgICAgICAgICAgICAgdmVyYm9zZT17dmVyYm9zZX1cbiAgICAgICAgICAgICAgaW5Qcm9ncmVzc1Rvb2xVc2VJRHM9e2luUHJvZ3Jlc3NUb29sVXNlSURzfVxuICAgICAgICAgICAgICBwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZT17cHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2V9XG4gICAgICAgICAgICAgIHNob3VsZEFuaW1hdGU9e3Nob3VsZEFuaW1hdGV9XG4gICAgICAgICAgICAgIHNob3VsZFNob3dEb3Q9e3Nob3VsZFNob3dEb3R9XG4gICAgICAgICAgICAgIHdpZHRoPXt3aWR0aH1cbiAgICAgICAgICAgICAgaW5Qcm9ncmVzc1Rvb2xDYWxsQ291bnQ9e2luUHJvZ3Jlc3NUb29sVXNlSURzLnNpemV9XG4gICAgICAgICAgICAgIGlzVHJhbnNjcmlwdE1vZGU9e2lzVHJhbnNjcmlwdE1vZGV9XG4gICAgICAgICAgICAgIGxvb2t1cHM9e2xvb2t1cHN9XG4gICAgICAgICAgICAgIG9uT3BlblJhdGVMaW1pdE9wdGlvbnM9e29uT3BlblJhdGVMaW1pdE9wdGlvbnN9XG4gICAgICAgICAgICAgIHRoaW5raW5nQmxvY2tJZD17YCR7bWVzc2FnZS51dWlkfToke2luZGV4fWB9XG4gICAgICAgICAgICAgIGxhc3RUaGlua2luZ0Jsb2NrSWQ9e2xhc3RUaGlua2luZ0Jsb2NrSWR9XG4gICAgICAgICAgICAgIGFkdmlzb3JNb2RlbD17bWVzc2FnZS5hZHZpc29yTW9kZWx9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgICkpfVxuICAgICAgICA8L0JveD5cbiAgICAgIClcbiAgICBjYXNlICd1c2VyJzoge1xuICAgICAgaWYgKG1lc3NhZ2UuaXNDb21wYWN0U3VtbWFyeSkge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIDxDb21wYWN0U3VtbWFyeVxuICAgICAgICAgICAgbWVzc2FnZT17bWVzc2FnZX1cbiAgICAgICAgICAgIHNjcmVlbj17aXNUcmFuc2NyaXB0TW9kZSA/ICd0cmFuc2NyaXB0JyA6ICdwcm9tcHQnfVxuICAgICAgICAgIC8+XG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIC8vIFByZWNvbXB1dGUgdGhlIGltYWdlSW5kZXggcHJvcCBmb3IgZWFjaCBjb250ZW50IGJsb2NrLiBUaGUgcHJldmlvdXNcbiAgICAgIC8vIHZlcnNpb24gaW5jcmVtZW50ZWQgYSBjb3VudGVyIGluc2lkZSB0aGUgLm1hcCgpIGNhbGxiYWNrLCB3aGljaFxuICAgICAgLy8gUmVhY3QgQ29tcGlsZXIgYmFpbHMgb24gKFwiVXBkYXRlRXhwcmVzc2lvbiB0byB2YXJpYWJsZXMgY2FwdHVyZWRcbiAgICAgIC8vIHdpdGhpbiBsYW1iZGFzXCIpLiBBIHBsYWluIGZvciBsb29wIGtlZXBzIHRoZSBtdXRhdGlvbiBvdXQgb2YgYVxuICAgICAgLy8gY2xvc3VyZSBzbyB0aGUgY29tcGlsZXIgY2FuIG1lbW9pemUgTWVzc2FnZUltcGwuXG4gICAgICBjb25zdCBpbWFnZUluZGljZXM6IG51bWJlcltdID0gW11cbiAgICAgIGxldCBpbWFnZVBvc2l0aW9uID0gMFxuICAgICAgZm9yIChjb25zdCBwYXJhbSBvZiBtZXNzYWdlLm1lc3NhZ2UuY29udGVudCkge1xuICAgICAgICBpZiAocGFyYW0udHlwZSA9PT0gJ2ltYWdlJykge1xuICAgICAgICAgIGNvbnN0IGlkID0gbWVzc2FnZS5pbWFnZVBhc3RlSWRzPy5baW1hZ2VQb3NpdGlvbl1cbiAgICAgICAgICBpbWFnZVBvc2l0aW9uKytcbiAgICAgICAgICBpbWFnZUluZGljZXMucHVzaChpZCA/PyBpbWFnZVBvc2l0aW9uKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGltYWdlSW5kaWNlcy5wdXNoKGltYWdlUG9zaXRpb24pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIENoZWNrIGlmIHRoaXMgbWVzc2FnZSBpcyB0aGUgbGF0ZXN0IGJhc2ggb3V0cHV0IC0gaWYgc28sIHdyYXAgY29udGVudFxuICAgICAgLy8gd2l0aCBwcm92aWRlciBzbyBPdXRwdXRMaW5lIGNhbiBzaG93IGZ1bGwgb3V0cHV0IHZpYSBjb250ZXh0XG4gICAgICBjb25zdCBpc0xhdGVzdEJhc2hPdXRwdXQgPSBsYXRlc3RCYXNoT3V0cHV0VVVJRCA9PT0gbWVzc2FnZS51dWlkXG4gICAgICBjb25zdCBjb250ZW50ID0gKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiB3aWR0aD17Y29udGFpbmVyV2lkdGggPz8gJzEwMCUnfT5cbiAgICAgICAgICB7bWVzc2FnZS5tZXNzYWdlLmNvbnRlbnQubWFwKChwYXJhbSwgaW5kZXgpID0+IChcbiAgICAgICAgICAgIDxVc2VyTWVzc2FnZVxuICAgICAgICAgICAgICBrZXk9e2luZGV4fVxuICAgICAgICAgICAgICBtZXNzYWdlPXttZXNzYWdlfVxuICAgICAgICAgICAgICBhZGRNYXJnaW49e2FkZE1hcmdpbn1cbiAgICAgICAgICAgICAgdG9vbHM9e3Rvb2xzfVxuICAgICAgICAgICAgICBwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZT17cHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2V9XG4gICAgICAgICAgICAgIHBhcmFtPXtwYXJhbX1cbiAgICAgICAgICAgICAgc3R5bGU9e3N0eWxlfVxuICAgICAgICAgICAgICB2ZXJib3NlPXt2ZXJib3NlfVxuICAgICAgICAgICAgICBpbWFnZUluZGV4PXtpbWFnZUluZGljZXNbaW5kZXhdIX1cbiAgICAgICAgICAgICAgaXNVc2VyQ29udGludWF0aW9uPXtpc1VzZXJDb250aW51YXRpb259XG4gICAgICAgICAgICAgIGxvb2t1cHM9e2xvb2t1cHN9XG4gICAgICAgICAgICAgIGlzVHJhbnNjcmlwdE1vZGU9e2lzVHJhbnNjcmlwdE1vZGV9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgICkpfVxuICAgICAgICA8L0JveD5cbiAgICAgIClcbiAgICAgIHJldHVybiBpc0xhdGVzdEJhc2hPdXRwdXQgPyAoXG4gICAgICAgIDxFeHBhbmRTaGVsbE91dHB1dFByb3ZpZGVyPntjb250ZW50fTwvRXhwYW5kU2hlbGxPdXRwdXRQcm92aWRlcj5cbiAgICAgICkgOiAoXG4gICAgICAgIGNvbnRlbnRcbiAgICAgIClcbiAgICB9XG4gICAgY2FzZSAnc3lzdGVtJzpcbiAgICAgIGlmIChtZXNzYWdlLnN1YnR5cGUgPT09ICdjb21wYWN0X2JvdW5kYXJ5Jykge1xuICAgICAgICAvLyBGdWxsc2NyZWVuIGtlZXBzIHByZS1jb21wYWN0IG1lc3NhZ2VzIGluIHRoZSBTY3JvbGxCb3ggKFJFUEwudHN4XG4gICAgICAgIC8vIGFwcGVuZHMgaW5zdGVhZCBvZiByZXNldHRpbmcsIE1lc3NhZ2VzLnRzeCBza2lwcyB0aGUgYm91bmRhcnlcbiAgICAgICAgLy8gZmlsdGVyKSDigJQgc2Nyb2xsIHVwIGZvciBoaXN0b3J5LCBubyBuZWVkIGZvciB0aGUgY3RybCtvIGhpbnQuXG4gICAgICAgIGlmIChpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkpIHtcbiAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICB9XG4gICAgICAgIHJldHVybiA8Q29tcGFjdEJvdW5kYXJ5TWVzc2FnZSAvPlxuICAgICAgfVxuICAgICAgaWYgKG1lc3NhZ2Uuc3VidHlwZSA9PT0gJ21pY3JvY29tcGFjdF9ib3VuZGFyeScpIHtcbiAgICAgICAgLy8gTG9nZ2VkIGF0IGNyZWF0aW9uIHRpbWUgaW4gY3JlYXRlTWljcm9jb21wYWN0Qm91bmRhcnlNZXNzYWdlXG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG4gICAgICBpZiAoZmVhdHVyZSgnSElTVE9SWV9TTklQJykpIHtcbiAgICAgICAgLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICAgICAgICBjb25zdCB7IGlzU25pcEJvdW5kYXJ5TWVzc2FnZSB9ID1cbiAgICAgICAgICByZXF1aXJlKCcuLi9zZXJ2aWNlcy9jb21wYWN0L3NuaXBQcm9qZWN0aW9uLmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi4vc2VydmljZXMvY29tcGFjdC9zbmlwUHJvamVjdGlvbi5qcycpXG4gICAgICAgIGNvbnN0IHsgaXNTbmlwTWFya2VyTWVzc2FnZSB9ID1cbiAgICAgICAgICByZXF1aXJlKCcuLi9zZXJ2aWNlcy9jb21wYWN0L3NuaXBDb21wYWN0LmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi4vc2VydmljZXMvY29tcGFjdC9zbmlwQ29tcGFjdC5qcycpXG4gICAgICAgIC8qIGVzbGludC1lbmFibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICAgICAgICBpZiAoaXNTbmlwQm91bmRhcnlNZXNzYWdlKG1lc3NhZ2UpKSB7XG4gICAgICAgICAgLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICAgICAgICAgIGNvbnN0IHsgU25pcEJvdW5kYXJ5TWVzc2FnZSB9ID1cbiAgICAgICAgICAgIHJlcXVpcmUoJy4vbWVzc2FnZXMvU25pcEJvdW5kYXJ5TWVzc2FnZS5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vbWVzc2FnZXMvU25pcEJvdW5kYXJ5TWVzc2FnZS5qcycpXG4gICAgICAgICAgLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG4gICAgICAgICAgcmV0dXJuIDxTbmlwQm91bmRhcnlNZXNzYWdlIG1lc3NhZ2U9e21lc3NhZ2V9IC8+XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGlzU25pcE1hcmtlck1lc3NhZ2UobWVzc2FnZSkpIHtcbiAgICAgICAgICAvLyBJbnRlcm5hbCByZWdpc3RyYXRpb24gbWFya2VyIOKAlCBub3QgdXNlci1mYWNpbmcuIFRoZSBib3VuZGFyeVxuICAgICAgICAgIC8vIG1lc3NhZ2UgKGFib3ZlKSBpcyB3aGF0IHNob3dzIHdoZW4gc25pcHMgYWN0dWFsbHkgZXhlY3V0ZS5cbiAgICAgICAgICByZXR1cm4gbnVsbFxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAobWVzc2FnZS5zdWJ0eXBlID09PSAnbG9jYWxfY29tbWFuZCcpIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICA8VXNlclRleHRNZXNzYWdlXG4gICAgICAgICAgICBhZGRNYXJnaW49e2FkZE1hcmdpbn1cbiAgICAgICAgICAgIHBhcmFtPXt7IHR5cGU6ICd0ZXh0JywgdGV4dDogbWVzc2FnZS5jb250ZW50IH19XG4gICAgICAgICAgICB2ZXJib3NlPXt2ZXJib3NlfVxuICAgICAgICAgICAgaXNUcmFuc2NyaXB0TW9kZT17aXNUcmFuc2NyaXB0TW9kZX1cbiAgICAgICAgICAvPlxuICAgICAgICApXG4gICAgICB9XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8U3lzdGVtVGV4dE1lc3NhZ2VcbiAgICAgICAgICBtZXNzYWdlPXttZXNzYWdlfVxuICAgICAgICAgIGFkZE1hcmdpbj17YWRkTWFyZ2lufVxuICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgICAgaXNUcmFuc2NyaXB0TW9kZT17aXNUcmFuc2NyaXB0TW9kZX1cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICBjYXNlICdncm91cGVkX3Rvb2xfdXNlJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxHcm91cGVkVG9vbFVzZUNvbnRlbnRcbiAgICAgICAgICBtZXNzYWdlPXttZXNzYWdlfVxuICAgICAgICAgIHRvb2xzPXt0b29sc31cbiAgICAgICAgICBsb29rdXBzPXtsb29rdXBzfVxuICAgICAgICAgIGluUHJvZ3Jlc3NUb29sVXNlSURzPXtpblByb2dyZXNzVG9vbFVzZUlEc31cbiAgICAgICAgICBzaG91bGRBbmltYXRlPXtzaG91bGRBbmltYXRlfVxuICAgICAgICAvPlxuICAgICAgKVxuICAgIGNhc2UgJ2NvbGxhcHNlZF9yZWFkX3NlYXJjaCc6XG4gICAgICAvLyBPZmZzY3JlZW5GcmVlemU6IHRoZSB2ZXJiIGZsaXBzIFwiUmVhZGluZ+KAplwi4oaSXCJSZWFkXCIgd2hlbiB0b29scyBjb21wbGV0ZS5cbiAgICAgIC8vIElmIHRoZSBncm91cCBoYXMgc2Nyb2xsZWQgaW50byBzY3JvbGxiYWNrIGJ5IHRoZW4sIHRoZSB1cGRhdGUgdHJpZ2dlcnNcbiAgICAgIC8vIGEgZnVsbCB0ZXJtaW5hbCByZXNldCAoQ0MtMTE1NSkuIFRoaXMgY29tcG9uZW50IGlzIG5ldmVyIG1hcmtlZCBzdGF0aWNcbiAgICAgIC8vIGluIHByb21wdCBtb2RlIChzaG91bGRSZW5kZXJTdGF0aWNhbGx5IHJldHVybnMgZmFsc2UgdG8gYWxsb3cgbGl2ZVxuICAgICAgLy8gdXBkYXRlcyBiZXR3ZWVuIEFQSSB0dXJucyksIHNvIHRoZSBtZW1vIGNhbid0IGhlbHAuIEZyZWV6ZSB3aGVuXG4gICAgICAvLyBvZmZzY3JlZW4g4oCUIHNjcm9sbGJhY2sgc2hvd3Mgd2hhdGV2ZXIgc3RhdGUgd2FzIHZpc2libGUgd2hlbiBpdCBsZWZ0LlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPE9mZnNjcmVlbkZyZWV6ZT5cbiAgICAgICAgICA8Q29sbGFwc2VkUmVhZFNlYXJjaENvbnRlbnRcbiAgICAgICAgICAgIG1lc3NhZ2U9e21lc3NhZ2V9XG4gICAgICAgICAgICBpblByb2dyZXNzVG9vbFVzZUlEcz17aW5Qcm9ncmVzc1Rvb2xVc2VJRHN9XG4gICAgICAgICAgICBzaG91bGRBbmltYXRlPXtzaG91bGRBbmltYXRlfVxuICAgICAgICAgICAgLy8gY3RybCtvIHRyYW5zY3JpcHQgbW9kZSBzaG91bGQgZXhwYW5kIHRoZSBncm91cCB0aGUgc2FtZSB3YXlcbiAgICAgICAgICAgIC8vIC0tdmVyYm9zZSBkb2VzLCBzbyByZWNhbGxlZCBtZW1vcmllcyArIHRvb2wgZGV0YWlscyBhcmUgdmlzaWJsZS5cbiAgICAgICAgICAgIC8vIEF0dGFjaG1lbnRNZXNzYWdlLnRzeCdzIHN0YW5kYWxvbmUgcmVsZXZhbnRfbWVtb3JpZXMgYnJhbmNoXG4gICAgICAgICAgICAvLyBhbHJlYWR5IGNoZWNrcyAodmVyYm9zZSB8fCBpc1RyYW5zY3JpcHRNb2RlKTsgdGhpcyBhbGlnbnMgdGhlXG4gICAgICAgICAgICAvLyBjb2xsYXBzZWQtZ3JvdXAgcGF0aCB0byBtYXRjaC5cbiAgICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2UgfHwgaXNUcmFuc2NyaXB0TW9kZX1cbiAgICAgICAgICAgIHRvb2xzPXt0b29sc31cbiAgICAgICAgICAgIGxvb2t1cHM9e2xvb2t1cHN9XG4gICAgICAgICAgICBpc0FjdGl2ZUdyb3VwPXtpc0FjdGl2ZUNvbGxhcHNlZEdyb3VwfVxuICAgICAgICAgIC8+XG4gICAgICAgIDwvT2Zmc2NyZWVuRnJlZXplPlxuICAgICAgKVxuICB9XG59XG5cbmZ1bmN0aW9uIFVzZXJNZXNzYWdlKHtcbiAgbWVzc2FnZSxcbiAgYWRkTWFyZ2luLFxuICB0b29scyxcbiAgcHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2UsXG4gIHBhcmFtLFxuICBzdHlsZSxcbiAgdmVyYm9zZSxcbiAgaW1hZ2VJbmRleCxcbiAgaXNVc2VyQ29udGludWF0aW9uLFxuICBsb29rdXBzLFxuICBpc1RyYW5zY3JpcHRNb2RlLFxufToge1xuICBtZXNzYWdlOiBOb3JtYWxpemVkVXNlck1lc3NhZ2VcbiAgYWRkTWFyZ2luOiBib29sZWFuXG4gIHRvb2xzOiBUb29sc1xuICBwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZTogUHJvZ3Jlc3NNZXNzYWdlW11cbiAgcGFyYW06XG4gICAgfCBUZXh0QmxvY2tQYXJhbVxuICAgIHwgSW1hZ2VCbG9ja1BhcmFtXG4gICAgfCBUb29sVXNlQmxvY2tQYXJhbVxuICAgIHwgVG9vbFJlc3VsdEJsb2NrUGFyYW1cbiAgc3R5bGU/OiAnY29uZGVuc2VkJ1xuICB2ZXJib3NlOiBib29sZWFuXG4gIGltYWdlSW5kZXg/OiBudW1iZXJcbiAgaXNVc2VyQ29udGludWF0aW9uOiBib29sZWFuXG4gIGxvb2t1cHM6IFJldHVyblR5cGU8dHlwZW9mIGJ1aWxkTWVzc2FnZUxvb2t1cHM+XG4gIGlzVHJhbnNjcmlwdE1vZGU6IGJvb2xlYW5cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB7IGNvbHVtbnMgfSA9IHVzZVRlcm1pbmFsU2l6ZSgpXG4gIHN3aXRjaCAocGFyYW0udHlwZSkge1xuICAgIGNhc2UgJ3RleHQnOlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPFVzZXJUZXh0TWVzc2FnZVxuICAgICAgICAgIGFkZE1hcmdpbj17YWRkTWFyZ2lufVxuICAgICAgICAgIHBhcmFtPXtwYXJhbX1cbiAgICAgICAgICB2ZXJib3NlPXt2ZXJib3NlfVxuICAgICAgICAgIHBsYW5Db250ZW50PXttZXNzYWdlLnBsYW5Db250ZW50fVxuICAgICAgICAgIGlzVHJhbnNjcmlwdE1vZGU9e2lzVHJhbnNjcmlwdE1vZGV9XG4gICAgICAgICAgdGltZXN0YW1wPXttZXNzYWdlLnRpbWVzdGFtcH1cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICBjYXNlICdpbWFnZSc6XG4gICAgICAvLyBJZiBwcmV2aW91cyBtZXNzYWdlIGlzIHVzZXIgKHRleHQgb3IgaW1hZ2UpLCB0aGlzIGlzIGEgY29udGludWF0aW9uIC0gdXNlIGNvbm5lY3RvclxuICAgICAgLy8gT3RoZXJ3aXNlIHRoaXMgaW1hZ2Ugc3RhcnRzIGEgbmV3IHVzZXIgdHVybiAtIHVzZSBtYXJnaW5cbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxVc2VySW1hZ2VNZXNzYWdlXG4gICAgICAgICAgaW1hZ2VJZD17aW1hZ2VJbmRleH1cbiAgICAgICAgICBhZGRNYXJnaW49e2FkZE1hcmdpbiAmJiAhaXNVc2VyQ29udGludWF0aW9ufVxuICAgICAgICAvPlxuICAgICAgKVxuICAgIGNhc2UgJ3Rvb2xfcmVzdWx0JzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxVc2VyVG9vbFJlc3VsdE1lc3NhZ2VcbiAgICAgICAgICBwYXJhbT17cGFyYW19XG4gICAgICAgICAgbWVzc2FnZT17bWVzc2FnZX1cbiAgICAgICAgICBsb29rdXBzPXtsb29rdXBzfVxuICAgICAgICAgIHByb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlPXtwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZX1cbiAgICAgICAgICBzdHlsZT17c3R5bGV9XG4gICAgICAgICAgdG9vbHM9e3Rvb2xzfVxuICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgICAgd2lkdGg9e2NvbHVtbnMgLSA1fVxuICAgICAgICAgIGlzVHJhbnNjcmlwdE1vZGU9e2lzVHJhbnNjcmlwdE1vZGV9XG4gICAgICAgIC8+XG4gICAgICApXG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxufVxuXG5mdW5jdGlvbiBBc3Npc3RhbnRNZXNzYWdlQmxvY2soe1xuICBwYXJhbSxcbiAgYWRkTWFyZ2luLFxuICB0b29scyxcbiAgY29tbWFuZHMsXG4gIHZlcmJvc2UsXG4gIGluUHJvZ3Jlc3NUb29sVXNlSURzLFxuICBwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZSxcbiAgc2hvdWxkQW5pbWF0ZSxcbiAgc2hvdWxkU2hvd0RvdCxcbiAgd2lkdGgsXG4gIGluUHJvZ3Jlc3NUb29sQ2FsbENvdW50LFxuICBpc1RyYW5zY3JpcHRNb2RlLFxuICBsb29rdXBzLFxuICBvbk9wZW5SYXRlTGltaXRPcHRpb25zLFxuICB0aGlua2luZ0Jsb2NrSWQsXG4gIGxhc3RUaGlua2luZ0Jsb2NrSWQsXG4gIGFkdmlzb3JNb2RlbCxcbn06IHtcbiAgcGFyYW06XG4gICAgfCBCZXRhQ29udGVudEJsb2NrXG4gICAgfCBDb25uZWN0b3JUZXh0QmxvY2tcbiAgICB8IEFkdmlzb3JCbG9ja1xuICAgIHwgVGV4dEJsb2NrUGFyYW1cbiAgICB8IEltYWdlQmxvY2tQYXJhbVxuICAgIHwgVGhpbmtpbmdCbG9ja1BhcmFtXG4gICAgfCBUb29sVXNlQmxvY2tQYXJhbVxuICAgIHwgVG9vbFJlc3VsdEJsb2NrUGFyYW1cbiAgYWRkTWFyZ2luOiBib29sZWFuXG4gIHRvb2xzOiBUb29sc1xuICBjb21tYW5kczogQ29tbWFuZFtdXG4gIHZlcmJvc2U6IGJvb2xlYW5cbiAgaW5Qcm9ncmVzc1Rvb2xVc2VJRHM6IFNldDxzdHJpbmc+XG4gIHByb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlOiBQcm9ncmVzc01lc3NhZ2VbXVxuICBzaG91bGRBbmltYXRlOiBib29sZWFuXG4gIHNob3VsZFNob3dEb3Q6IGJvb2xlYW5cbiAgd2lkdGg/OiBudW1iZXIgfCBzdHJpbmdcbiAgaW5Qcm9ncmVzc1Rvb2xDYWxsQ291bnQ/OiBudW1iZXJcbiAgaXNUcmFuc2NyaXB0TW9kZTogYm9vbGVhblxuICBsb29rdXBzOiBSZXR1cm5UeXBlPHR5cGVvZiBidWlsZE1lc3NhZ2VMb29rdXBzPlxuICBvbk9wZW5SYXRlTGltaXRPcHRpb25zPzogKCkgPT4gdm9pZFxuICAvKiogSUQgb2YgdGhpcyBjb250ZW50IGJsb2NrJ3MgbWVzc2FnZTppbmRleCBmb3IgdGhpbmtpbmcgYmxvY2sgY29tcGFyaXNvbiAqL1xuICB0aGlua2luZ0Jsb2NrSWQ6IHN0cmluZ1xuICAvKiogSUQgb2YgdGhlIGxhc3QgdGhpbmtpbmcgYmxvY2sgdG8gc2hvdywgbnVsbCBtZWFucyBzaG93IGFsbCAqL1xuICBsYXN0VGhpbmtpbmdCbG9ja0lkPzogc3RyaW5nIHwgbnVsbFxuICBhZHZpc29yTW9kZWw/OiBzdHJpbmdcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoZmVhdHVyZSgnQ09OTkVDVE9SX1RFWFQnKSkge1xuICAgIGlmIChpc0Nvbm5lY3RvclRleHRCbG9jayhwYXJhbSkpIHtcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxBc3Npc3RhbnRUZXh0TWVzc2FnZVxuICAgICAgICAgIHBhcmFtPXt7IHR5cGU6ICd0ZXh0JywgdGV4dDogcGFyYW0uY29ubmVjdG9yX3RleHQgfX1cbiAgICAgICAgICBhZGRNYXJnaW49e2FkZE1hcmdpbn1cbiAgICAgICAgICBzaG91bGRTaG93RG90PXtzaG91bGRTaG93RG90fVxuICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgICAgd2lkdGg9e3dpZHRofVxuICAgICAgICAgIG9uT3BlblJhdGVMaW1pdE9wdGlvbnM9e29uT3BlblJhdGVMaW1pdE9wdGlvbnN9XG4gICAgICAgIC8+XG4gICAgICApXG4gICAgfVxuICB9XG4gIHN3aXRjaCAocGFyYW0udHlwZSkge1xuICAgIGNhc2UgJ3Rvb2xfdXNlJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxBc3Npc3RhbnRUb29sVXNlTWVzc2FnZVxuICAgICAgICAgIHBhcmFtPXtwYXJhbX1cbiAgICAgICAgICBhZGRNYXJnaW49e2FkZE1hcmdpbn1cbiAgICAgICAgICB0b29scz17dG9vbHN9XG4gICAgICAgICAgY29tbWFuZHM9e2NvbW1hbmRzfVxuICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgICAgaW5Qcm9ncmVzc1Rvb2xVc2VJRHM9e2luUHJvZ3Jlc3NUb29sVXNlSURzfVxuICAgICAgICAgIHByb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlPXtwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZX1cbiAgICAgICAgICBzaG91bGRBbmltYXRlPXtzaG91bGRBbmltYXRlfVxuICAgICAgICAgIHNob3VsZFNob3dEb3Q9e3Nob3VsZFNob3dEb3R9XG4gICAgICAgICAgaW5Qcm9ncmVzc1Rvb2xDYWxsQ291bnQ9e2luUHJvZ3Jlc3NUb29sQ2FsbENvdW50fVxuICAgICAgICAgIGxvb2t1cHM9e2xvb2t1cHN9XG4gICAgICAgICAgaXNUcmFuc2NyaXB0TW9kZT17aXNUcmFuc2NyaXB0TW9kZX1cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICBjYXNlICd0ZXh0JzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxBc3Npc3RhbnRUZXh0TWVzc2FnZVxuICAgICAgICAgIHBhcmFtPXtwYXJhbX1cbiAgICAgICAgICBhZGRNYXJnaW49e2FkZE1hcmdpbn1cbiAgICAgICAgICBzaG91bGRTaG93RG90PXtzaG91bGRTaG93RG90fVxuICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgICAgd2lkdGg9e3dpZHRofVxuICAgICAgICAgIG9uT3BlblJhdGVMaW1pdE9wdGlvbnM9e29uT3BlblJhdGVMaW1pdE9wdGlvbnN9XG4gICAgICAgIC8+XG4gICAgICApXG4gICAgY2FzZSAncmVkYWN0ZWRfdGhpbmtpbmcnOlxuICAgICAgaWYgKCFpc1RyYW5zY3JpcHRNb2RlICYmICF2ZXJib3NlKSB7XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG4gICAgICByZXR1cm4gPEFzc2lzdGFudFJlZGFjdGVkVGhpbmtpbmdNZXNzYWdlIGFkZE1hcmdpbj17YWRkTWFyZ2lufSAvPlxuICAgIGNhc2UgJ3RoaW5raW5nJzoge1xuICAgICAgaWYgKCFpc1RyYW5zY3JpcHRNb2RlICYmICF2ZXJib3NlKSB7XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG4gICAgICAvLyBJbiB0cmFuc2NyaXB0IG1vZGUgd2l0aCBoaWRlUGFzdFRoaW5raW5nLCBvbmx5IHNob3cgdGhlIGxhc3QgdGhpbmtpbmcgYmxvY2tcbiAgICAgIGNvbnN0IGlzTGFzdFRoaW5raW5nID1cbiAgICAgICAgIWxhc3RUaGlua2luZ0Jsb2NrSWQgfHwgdGhpbmtpbmdCbG9ja0lkID09PSBsYXN0VGhpbmtpbmdCbG9ja0lkXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8QXNzaXN0YW50VGhpbmtpbmdNZXNzYWdlXG4gICAgICAgICAgYWRkTWFyZ2luPXthZGRNYXJnaW59XG4gICAgICAgICAgcGFyYW09e3BhcmFtfVxuICAgICAgICAgIGlzVHJhbnNjcmlwdE1vZGU9e2lzVHJhbnNjcmlwdE1vZGV9XG4gICAgICAgICAgdmVyYm9zZT17dmVyYm9zZX1cbiAgICAgICAgICBoaWRlSW5UcmFuc2NyaXB0PXtpc1RyYW5zY3JpcHRNb2RlICYmICFpc0xhc3RUaGlua2luZ31cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICB9XG4gICAgY2FzZSAnc2VydmVyX3Rvb2xfdXNlJzpcbiAgICBjYXNlICdhZHZpc29yX3Rvb2xfcmVzdWx0JzpcbiAgICAgIGlmIChpc0Fkdmlzb3JCbG9jayhwYXJhbSkpIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICA8QWR2aXNvck1lc3NhZ2VcbiAgICAgICAgICAgIGJsb2NrPXtwYXJhbX1cbiAgICAgICAgICAgIGFkZE1hcmdpbj17YWRkTWFyZ2lufVxuICAgICAgICAgICAgcmVzb2x2ZWRUb29sVXNlSURzPXtsb29rdXBzLnJlc29sdmVkVG9vbFVzZUlEc31cbiAgICAgICAgICAgIGVycm9yZWRUb29sVXNlSURzPXtsb29rdXBzLmVycm9yZWRUb29sVXNlSURzfVxuICAgICAgICAgICAgc2hvdWxkQW5pbWF0ZT17c2hvdWxkQW5pbWF0ZX1cbiAgICAgICAgICAgIHZlcmJvc2U9e3ZlcmJvc2UgfHwgaXNUcmFuc2NyaXB0TW9kZX1cbiAgICAgICAgICAgIGFkdmlzb3JNb2RlbD17YWR2aXNvck1vZGVsfVxuICAgICAgICAgIC8+XG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGxvZ0Vycm9yKG5ldyBFcnJvcihgVW5hYmxlIHRvIHJlbmRlciBzZXJ2ZXIgdG9vbCBibG9jazogJHtwYXJhbS50eXBlfWApKVxuICAgICAgcmV0dXJuIG51bGxcbiAgICBkZWZhdWx0OlxuICAgICAgbG9nRXJyb3IobmV3IEVycm9yKGBVbmFibGUgdG8gcmVuZGVyIG1lc3NhZ2UgdHlwZTogJHtwYXJhbS50eXBlfWApKVxuICAgICAgcmV0dXJuIG51bGxcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gaGFzVGhpbmtpbmdDb250ZW50KG06IHtcbiAgdHlwZTogc3RyaW5nXG4gIG1lc3NhZ2U/OiB7IGNvbnRlbnQ6IEFycmF5PHsgdHlwZTogc3RyaW5nIH0+IH1cbn0pOiBib29sZWFuIHtcbiAgaWYgKG0udHlwZSAhPT0gJ2Fzc2lzdGFudCcgfHwgIW0ubWVzc2FnZSkgcmV0dXJuIGZhbHNlXG4gIHJldHVybiBtLm1lc3NhZ2UuY29udGVudC5zb21lKFxuICAgIGIgPT4gYi50eXBlID09PSAndGhpbmtpbmcnIHx8IGIudHlwZSA9PT0gJ3JlZGFjdGVkX3RoaW5raW5nJyxcbiAgKVxufVxuXG4vKiogRXhwb3J0ZWQgZm9yIHRlc3RpbmcgKi9cbmV4cG9ydCBmdW5jdGlvbiBhcmVNZXNzYWdlUHJvcHNFcXVhbChwcmV2OiBQcm9wcywgbmV4dDogUHJvcHMpOiBib29sZWFuIHtcbiAgaWYgKHByZXYubWVzc2FnZS51dWlkICE9PSBuZXh0Lm1lc3NhZ2UudXVpZCkgcmV0dXJuIGZhbHNlXG4gIC8vIE9ubHkgcmUtcmVuZGVyIG9uIGxhc3RUaGlua2luZ0Jsb2NrSWQgY2hhbmdlIGlmIHRoaXMgbWVzc2FnZSBhY3R1YWxseVxuICAvLyBoYXMgdGhpbmtpbmcgY29udGVudCDigJQgb3RoZXJ3aXNlIGV2ZXJ5IG1lc3NhZ2UgaW4gc2Nyb2xsYmFjayByZS1yZW5kZXJzXG4gIC8vIHdoZW5ldmVyIHN0cmVhbWluZyB0aGlua2luZyBzdGFydHMvc3RvcHMgKENDLTk0MSkuXG4gIGlmIChcbiAgICBwcmV2Lmxhc3RUaGlua2luZ0Jsb2NrSWQgIT09IG5leHQubGFzdFRoaW5raW5nQmxvY2tJZCAmJlxuICAgIGhhc1RoaW5raW5nQ29udGVudChuZXh0Lm1lc3NhZ2UpXG4gICkge1xuICAgIHJldHVybiBmYWxzZVxuICB9XG4gIC8vIFZlcmJvc2UgdG9nZ2xlIGNoYW5nZXMgdGhpbmtpbmcgYmxvY2sgdmlzaWJpbGl0eS9leHBhbnNpb25cbiAgaWYgKHByZXYudmVyYm9zZSAhPT0gbmV4dC52ZXJib3NlKSByZXR1cm4gZmFsc2VcbiAgLy8gT25seSByZS1yZW5kZXIgaWYgdGhpcyBtZXNzYWdlJ3MgXCJpcyBsYXRlc3QgYmFzaCBvdXRwdXRcIiBzdGF0dXMgY2hhbmdlZCxcbiAgLy8gbm90IHdoZW4gdGhlIGdsb2JhbCBsYXRlc3RCYXNoT3V0cHV0VVVJRCBjaGFuZ2VzIHRvIGEgZGlmZmVyZW50IG1lc3NhZ2VcbiAgY29uc3QgcHJldklzTGF0ZXN0ID0gcHJldi5sYXRlc3RCYXNoT3V0cHV0VVVJRCA9PT0gcHJldi5tZXNzYWdlLnV1aWRcbiAgY29uc3QgbmV4dElzTGF0ZXN0ID0gbmV4dC5sYXRlc3RCYXNoT3V0cHV0VVVJRCA9PT0gbmV4dC5tZXNzYWdlLnV1aWRcbiAgaWYgKHByZXZJc0xhdGVzdCAhPT0gbmV4dElzTGF0ZXN0KSByZXR1cm4gZmFsc2VcbiAgaWYgKHByZXYuaXNUcmFuc2NyaXB0TW9kZSAhPT0gbmV4dC5pc1RyYW5zY3JpcHRNb2RlKSByZXR1cm4gZmFsc2VcbiAgLy8gY29udGFpbmVyV2lkdGggaXMgYW4gYWJzb2x1dGUgbnVtYmVyIGluIHRoZSBuby1tZXRhZGF0YSBwYXRoICh3cmFwcGVyXG4gIC8vIEJveCBpcyBza2lwcGVkKS4gU3RhdGljIG1lc3NhZ2VzIG11c3QgcmUtcmVuZGVyIG9uIHRlcm1pbmFsIHJlc2l6ZS5cbiAgaWYgKHByZXYuY29udGFpbmVyV2lkdGggIT09IG5leHQuY29udGFpbmVyV2lkdGgpIHJldHVybiBmYWxzZVxuICBpZiAocHJldi5pc1N0YXRpYyAmJiBuZXh0LmlzU3RhdGljKSByZXR1cm4gdHJ1ZVxuICByZXR1cm4gZmFsc2Vcbn1cblxuZXhwb3J0IGNvbnN0IE1lc3NhZ2UgPSBSZWFjdC5tZW1vKE1lc3NhZ2VJbXBsLCBhcmVNZXNzYWdlUHJvcHNFcXVhbClcbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNB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 \ No newline at end of file diff --git a/components/MessageModel.tsx b/components/MessageModel.tsx new file mode 100644 index 0000000..796bf27 --- /dev/null +++ b/components/MessageModel.tsx @@ -0,0 +1,43 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { stringWidth } from '../ink/stringWidth.js'; +import { Box, Text } from '../ink.js'; +import type { NormalizedMessage } from '../types/message.js'; +type Props = { + message: NormalizedMessage; + isTranscriptMode: boolean; +}; +export function MessageModel(t0) { + const $ = _c(5); + const { + message, + isTranscriptMode + } = t0; + const shouldShowModel = isTranscriptMode && message.type === "assistant" && message.message.model && message.message.content.some(_temp); + if (!shouldShowModel) { + return null; + } + const t1 = stringWidth(message.message.model) + 8; + let t2; + if ($[0] !== message.message.model) { + t2 = {message.message.model}; + $[0] = message.message.model; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== t1 || $[3] !== t2) { + t3 = {t2}; + $[2] = t1; + $[3] = t2; + $[4] = t3; + } else { + t3 = $[4]; + } + return t3; +} +function _temp(c) { + return c.type === "text"; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInN0cmluZ1dpZHRoIiwiQm94IiwiVGV4dCIsIk5vcm1hbGl6ZWRNZXNzYWdlIiwiUHJvcHMiLCJtZXNzYWdlIiwiaXNUcmFuc2NyaXB0TW9kZSIsIk1lc3NhZ2VNb2RlbCIsInQwIiwiJCIsIl9jIiwic2hvdWxkU2hvd01vZGVsIiwidHlwZSIsIm1vZGVsIiwiY29udGVudCIsInNvbWUiLCJfdGVtcCIsInQxIiwidDIiLCJ0MyIsImMiXSwic291cmNlcyI6WyJNZXNzYWdlTW9kZWwudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHN0cmluZ1dpZHRoIH0gZnJvbSAnLi4vaW5rL3N0cmluZ1dpZHRoLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBOb3JtYWxpemVkTWVzc2FnZSB9IGZyb20gJy4uL3R5cGVzL21lc3NhZ2UuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG1lc3NhZ2U6IE5vcm1hbGl6ZWRNZXNzYWdlXG4gIGlzVHJhbnNjcmlwdE1vZGU6IGJvb2xlYW5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIE1lc3NhZ2VNb2RlbCh7XG4gIG1lc3NhZ2UsXG4gIGlzVHJhbnNjcmlwdE1vZGUsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHNob3VsZFNob3dNb2RlbCA9XG4gICAgaXNUcmFuc2NyaXB0TW9kZSAmJlxuICAgIG1lc3NhZ2UudHlwZSA9PT0gJ2Fzc2lzdGFudCcgJiZcbiAgICBtZXNzYWdlLm1lc3NhZ2UubW9kZWwgJiZcbiAgICBtZXNzYWdlLm1lc3NhZ2UuY29udGVudC5zb21lKGMgPT4gYy50eXBlID09PSAndGV4dCcpXG5cbiAgaWYgKCFzaG91bGRTaG93TW9kZWwpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IG1pbldpZHRoPXtzdHJpbmdXaWR0aChtZXNzYWdlLm1lc3NhZ2UubW9kZWwpICsgOH0+XG4gICAgICA8VGV4dCBkaW1Db2xvcj57bWVzc2FnZS5tZXNzYWdlLm1vZGVsfTwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsV0FBVyxRQUFRLHVCQUF1QjtBQUNuRCxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLGNBQWNDLGlCQUFpQixRQUFRLHFCQUFxQjtBQUU1RCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsT0FBTyxFQUFFRixpQkFBaUI7RUFDMUJHLGdCQUFnQixFQUFFLE9BQU87QUFDM0IsQ0FBQztBQUVELE9BQU8sU0FBQUMsYUFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFzQjtJQUFBTCxPQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFHckI7RUFDTixNQUFBRyxlQUFBLEdBQ0VMLGdCQUM0QixJQUE1QkQsT0FBTyxDQUFBTyxJQUFLLEtBQUssV0FDSSxJQUFyQlAsT0FBTyxDQUFBQSxPQUFRLENBQUFRLEtBQ3FDLElBQXBEUixPQUFPLENBQUFBLE9BQVEsQ0FBQVMsT0FBUSxDQUFBQyxJQUFLLENBQUNDLEtBQXNCLENBQUM7RUFFdEQsSUFBSSxDQUFDTCxlQUFlO0lBQUEsT0FDWCxJQUFJO0VBQUE7RUFJSSxNQUFBTSxFQUFBLEdBQUFqQixXQUFXLENBQUNLLE9BQU8sQ0FBQUEsT0FBUSxDQUFBUSxLQUFNLENBQUMsR0FBRyxDQUFDO0VBQUEsSUFBQUssRUFBQTtFQUFBLElBQUFULENBQUEsUUFBQUosT0FBQSxDQUFBQSxPQUFBLENBQUFRLEtBQUE7SUFDbkRLLEVBQUEsSUFBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFFLENBQUFiLE9BQU8sQ0FBQUEsT0FBUSxDQUFBUSxLQUFLLENBQUUsRUFBckMsSUFBSSxDQUF3QztJQUFBSixDQUFBLE1BQUFKLE9BQUEsQ0FBQUEsT0FBQSxDQUFBUSxLQUFBO0lBQUFKLENBQUEsTUFBQVMsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVQsQ0FBQTtFQUFBO0VBQUEsSUFBQVUsRUFBQTtFQUFBLElBQUFWLENBQUEsUUFBQVEsRUFBQSxJQUFBUixDQUFBLFFBQUFTLEVBQUE7SUFEL0NDLEVBQUEsSUFBQyxHQUFHLENBQVcsUUFBc0MsQ0FBdEMsQ0FBQUYsRUFBcUMsQ0FBQyxDQUNuRCxDQUFBQyxFQUE0QyxDQUM5QyxFQUZDLEdBQUcsQ0FFRTtJQUFBVCxDQUFBLE1BQUFRLEVBQUE7SUFBQVIsQ0FBQSxNQUFBUyxFQUFBO0lBQUFULENBQUEsTUFBQVUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVYsQ0FBQTtFQUFBO0VBQUEsT0FGTlUsRUFFTTtBQUFBO0FBakJILFNBQUFILE1BQUFJLENBQUE7RUFBQSxPQVErQkEsQ0FBQyxDQUFBUixJQUFLLEtBQUssTUFBTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/MessageResponse.tsx b/components/MessageResponse.tsx new file mode 100644 index 0000000..71af216 --- /dev/null +++ b/components/MessageResponse.tsx @@ -0,0 +1,78 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useContext } from 'react'; +import { Box, NoSelect, Text } from '../ink.js'; +import { Ratchet } from './design-system/Ratchet.js'; +type Props = { + children: React.ReactNode; + height?: number; +}; +export function MessageResponse(t0) { + const $ = _c(8); + const { + children, + height + } = t0; + const isMessageResponse = useContext(MessageResponseContext); + if (isMessageResponse) { + return children; + } + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {" "}⎿  ; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== children) { + t2 = {children}; + $[1] = children; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] !== height || $[4] !== t2) { + t3 = {t1}{t2}; + $[3] = height; + $[4] = t2; + $[5] = t3; + } else { + t3 = $[5]; + } + const content = t3; + if (height !== undefined) { + return content; + } + let t4; + if ($[6] !== content) { + t4 = {content}; + $[6] = content; + $[7] = t4; + } else { + t4 = $[7]; + } + return t4; +} + +// This is a context that is used to determine if the message response +// is rendered as a descendant of another MessageResponse. We use it +// to avoid rendering nested ⎿ characters. +const MessageResponseContext = React.createContext(false); +function MessageResponseProvider(t0) { + const $ = _c(2); + const { + children + } = t0; + let t1; + if ($[0] !== children) { + t1 = {children}; + $[0] = children; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNvbnRleHQiLCJCb3giLCJOb1NlbGVjdCIsIlRleHQiLCJSYXRjaGV0IiwiUHJvcHMiLCJjaGlsZHJlbiIsIlJlYWN0Tm9kZSIsImhlaWdodCIsIk1lc3NhZ2VSZXNwb25zZSIsInQwIiwiJCIsIl9jIiwiaXNNZXNzYWdlUmVzcG9uc2UiLCJNZXNzYWdlUmVzcG9uc2VDb250ZXh0IiwidDEiLCJTeW1ib2wiLCJmb3IiLCJ0MiIsInQzIiwiY29udGVudCIsInVuZGVmaW5lZCIsInQ0IiwiY3JlYXRlQ29udGV4dCIsIk1lc3NhZ2VSZXNwb25zZVByb3ZpZGVyIl0sInNvdXJjZXMiOlsiTWVzc2FnZVJlc3BvbnNlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUNvbnRleHQgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgTm9TZWxlY3QsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBSYXRjaGV0IH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL1JhdGNoZXQuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGNoaWxkcmVuOiBSZWFjdC5SZWFjdE5vZGVcbiAgaGVpZ2h0PzogbnVtYmVyXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBNZXNzYWdlUmVzcG9uc2UoeyBjaGlsZHJlbiwgaGVpZ2h0IH06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgaXNNZXNzYWdlUmVzcG9uc2UgPSB1c2VDb250ZXh0KE1lc3NhZ2VSZXNwb25zZUNvbnRleHQpXG4gIGlmIChpc01lc3NhZ2VSZXNwb25zZSkge1xuICAgIHJldHVybiBjaGlsZHJlblxuICB9XG4gIGNvbnN0IGNvbnRlbnQgPSAoXG4gICAgPE1lc3NhZ2VSZXNwb25zZVByb3ZpZGVyPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgaGVpZ2h0PXtoZWlnaHR9IG92ZXJmbG93WT1cImhpZGRlblwiPlxuICAgICAgICA8Tm9TZWxlY3QgZnJvbUxlZnRFZGdlIGZsZXhTaHJpbms9ezB9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPnsnICAnfeKOvyAmbmJzcDs8L1RleHQ+XG4gICAgICAgIDwvTm9TZWxlY3Q+XG4gICAgICAgIDxCb3ggZmxleFNocmluaz17MX0gZmxleEdyb3c9ezF9PlxuICAgICAgICAgIHtjaGlsZHJlbn1cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cbiAgICA8L01lc3NhZ2VSZXNwb25zZVByb3ZpZGVyPlxuICApXG4gIGlmIChoZWlnaHQgIT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybiBjb250ZW50XG4gIH1cbiAgcmV0dXJuIDxSYXRjaGV0IGxvY2s9XCJvZmZzY3JlZW5cIj57Y29udGVudH08L1JhdGNoZXQ+XG59XG5cbi8vIFRoaXMgaXMgYSBjb250ZXh0IHRoYXQgaXMgdXNlZCB0byBkZXRlcm1pbmUgaWYgdGhlIG1lc3NhZ2UgcmVzcG9uc2Vcbi8vIGlzIHJlbmRlcmVkIGFzIGEgZGVzY2VuZGFudCBvZiBhbm90aGVyIE1lc3NhZ2VSZXNwb25zZS4gV2UgdXNlIGl0XG4vLyB0byBhdm9pZCByZW5kZXJpbmcgbmVzdGVkIOKOvyBjaGFyYWN0ZXJzLlxuY29uc3QgTWVzc2FnZVJlc3BvbnNlQ29udGV4dCA9IFJlYWN0LmNyZWF0ZUNvbnRleHQoZmFsc2UpXG5cbmZ1bmN0aW9uIE1lc3NhZ2VSZXNwb25zZVByb3ZpZGVyKHtcbiAgY2hpbGRyZW4sXG59OiB7XG4gIGNoaWxkcmVuOiBSZWFjdC5SZWFjdE5vZGVcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDxNZXNzYWdlUmVzcG9uc2VDb250ZXh0LlByb3ZpZGVyIHZhbHVlPXt0cnVlfT5cbiAgICAgIHtjaGlsZHJlbn1cbiAgICA8L01lc3NhZ2VSZXNwb25zZUNvbnRleHQuUHJvdmlkZXI+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0Es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 \ No newline at end of file diff --git a/components/MessageRow.tsx b/components/MessageRow.tsx new file mode 100644 index 0000000..dce0eda --- /dev/null +++ b/components/MessageRow.tsx @@ -0,0 +1,383 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import type { Command } from '../commands.js'; +import { Box } from '../ink.js'; +import type { Screen } from '../screens/REPL.js'; +import type { Tools } from '../Tool.js'; +import type { RenderableMessage } from '../types/message.js'; +import { getDisplayMessageFromCollapsed, getToolSearchOrReadInfo, getToolUseIdsFromCollapsedGroup, hasAnyToolInProgress } from '../utils/collapseReadSearch.js'; +import { type buildMessageLookups, EMPTY_STRING_SET, getProgressMessagesFromLookup, getSiblingToolUseIDsFromLookup, getToolUseID } from '../utils/messages.js'; +import { hasThinkingContent, Message } from './Message.js'; +import { MessageModel } from './MessageModel.js'; +import { shouldRenderStatically } from './Messages.js'; +import { MessageTimestamp } from './MessageTimestamp.js'; +import { OffscreenFreeze } from './OffscreenFreeze.js'; +export type Props = { + message: RenderableMessage; + /** Whether the previous message in renderableMessages is also a user message. */ + isUserContinuation: boolean; + /** + * Whether there is non-skippable content after this message in renderableMessages. + * Only needs to be accurate for `collapsed_read_search` messages — used to decide + * if the collapsed group spinner should stay active. Pass `false` otherwise. + */ + hasContentAfter: boolean; + tools: Tools; + commands: Command[]; + verbose: boolean; + inProgressToolUseIDs: Set; + streamingToolUseIDs: Set; + screen: Screen; + canAnimate: boolean; + onOpenRateLimitOptions?: () => void; + lastThinkingBlockId: string | null; + latestBashOutputUUID: string | null; + columns: number; + isLoading: boolean; + lookups: ReturnType; +}; + +/** + * Scans forward from `index+1` to check if any "real" content follows. Used to + * decide whether a collapsed read/search group should stay in its active + * (grey dot, present-tense "Reading…") state while the query is still loading. + * + * Exported so Messages.tsx can compute this once per message and pass the + * result as a boolean prop — avoids passing the full `renderableMessages` array + * to each MessageRow (which React Compiler would pin in the fiber's memoCache, + * accumulating every historical version of the array ≈ 1-2MB over a 7-turn session). + */ +export function hasContentAfterIndex(messages: RenderableMessage[], index: number, tools: Tools, streamingToolUseIDs: Set): boolean { + for (let i = index + 1; i < messages.length; i++) { + const msg = messages[i]; + if (msg?.type === 'assistant') { + const content = msg.message.content[0]; + if (content?.type === 'thinking' || content?.type === 'redacted_thinking') { + continue; + } + if (content?.type === 'tool_use') { + if (getToolSearchOrReadInfo(content.name, content.input, tools).isCollapsible) { + continue; + } + // Non-collapsible tool uses appear in syntheticStreamingToolUseMessages + // before their ID is added to inProgressToolUseIDs. Skip while streaming + // to avoid briefly finalizing the read group. + if (streamingToolUseIDs.has(content.id)) { + continue; + } + } + return true; + } + if (msg?.type === 'system' || msg?.type === 'attachment') { + continue; + } + // Tool results arrive while the collapsed group is still being built + if (msg?.type === 'user') { + const content = msg.message.content[0]; + if (content?.type === 'tool_result') { + continue; + } + } + // Collapsible grouped_tool_use messages arrive transiently before being + // merged into the current collapsed group on the next render cycle + if (msg?.type === 'grouped_tool_use') { + const firstInput = msg.messages[0]?.message.content[0]?.input; + if (getToolSearchOrReadInfo(msg.toolName, firstInput, tools).isCollapsible) { + continue; + } + } + return true; + } + return false; +} +function MessageRowImpl(t0) { + const $ = _c(64); + const { + message: msg, + isUserContinuation, + hasContentAfter, + tools, + commands, + verbose, + inProgressToolUseIDs, + streamingToolUseIDs, + screen, + canAnimate, + onOpenRateLimitOptions, + lastThinkingBlockId, + latestBashOutputUUID, + columns, + isLoading, + lookups + } = t0; + const isTranscriptMode = screen === "transcript"; + const isGrouped = msg.type === "grouped_tool_use"; + const isCollapsed = msg.type === "collapsed_read_search"; + let t1; + if ($[0] !== hasContentAfter || $[1] !== inProgressToolUseIDs || $[2] !== isCollapsed || $[3] !== isLoading || $[4] !== msg) { + t1 = isCollapsed && (hasAnyToolInProgress(msg, inProgressToolUseIDs) || isLoading && !hasContentAfter); + $[0] = hasContentAfter; + $[1] = inProgressToolUseIDs; + $[2] = isCollapsed; + $[3] = isLoading; + $[4] = msg; + $[5] = t1; + } else { + t1 = $[5]; + } + const isActiveCollapsedGroup = t1; + let t2; + if ($[6] !== isCollapsed || $[7] !== isGrouped || $[8] !== msg) { + t2 = isGrouped ? msg.displayMessage : isCollapsed ? getDisplayMessageFromCollapsed(msg) : msg; + $[6] = isCollapsed; + $[7] = isGrouped; + $[8] = msg; + $[9] = t2; + } else { + t2 = $[9]; + } + const displayMsg = t2; + let t3; + if ($[10] !== isCollapsed || $[11] !== isGrouped || $[12] !== lookups || $[13] !== msg) { + t3 = isGrouped || isCollapsed ? [] : getProgressMessagesFromLookup(msg, lookups); + $[10] = isCollapsed; + $[11] = isGrouped; + $[12] = lookups; + $[13] = msg; + $[14] = t3; + } else { + t3 = $[14]; + } + const progressMessagesForMessage = t3; + let t4; + if ($[15] !== inProgressToolUseIDs || $[16] !== isCollapsed || $[17] !== isGrouped || $[18] !== lookups || $[19] !== msg || $[20] !== screen || $[21] !== streamingToolUseIDs) { + const siblingToolUseIDs = isGrouped || isCollapsed ? EMPTY_STRING_SET : getSiblingToolUseIDsFromLookup(msg, lookups); + t4 = shouldRenderStatically(msg, streamingToolUseIDs, inProgressToolUseIDs, siblingToolUseIDs, screen, lookups); + $[15] = inProgressToolUseIDs; + $[16] = isCollapsed; + $[17] = isGrouped; + $[18] = lookups; + $[19] = msg; + $[20] = screen; + $[21] = streamingToolUseIDs; + $[22] = t4; + } else { + t4 = $[22]; + } + const isStatic = t4; + let shouldAnimate = false; + if (canAnimate) { + if (isGrouped) { + let t5; + if ($[23] !== inProgressToolUseIDs || $[24] !== msg.messages) { + let t6; + if ($[26] !== inProgressToolUseIDs) { + t6 = m => { + const content = m.message.content[0]; + return content?.type === "tool_use" && inProgressToolUseIDs.has(content.id); + }; + $[26] = inProgressToolUseIDs; + $[27] = t6; + } else { + t6 = $[27]; + } + t5 = msg.messages.some(t6); + $[23] = inProgressToolUseIDs; + $[24] = msg.messages; + $[25] = t5; + } else { + t5 = $[25]; + } + shouldAnimate = t5; + } else { + if (isCollapsed) { + let t5; + if ($[28] !== inProgressToolUseIDs || $[29] !== msg) { + t5 = hasAnyToolInProgress(msg, inProgressToolUseIDs); + $[28] = inProgressToolUseIDs; + $[29] = msg; + $[30] = t5; + } else { + t5 = $[30]; + } + shouldAnimate = t5; + } else { + let t5; + if ($[31] !== inProgressToolUseIDs || $[32] !== msg) { + const toolUseID = getToolUseID(msg); + t5 = !toolUseID || inProgressToolUseIDs.has(toolUseID); + $[31] = inProgressToolUseIDs; + $[32] = msg; + $[33] = t5; + } else { + t5 = $[33]; + } + shouldAnimate = t5; + } + } + } + let t5; + if ($[34] !== displayMsg || $[35] !== isTranscriptMode) { + t5 = isTranscriptMode && displayMsg.type === "assistant" && displayMsg.message.content.some(_temp) && (displayMsg.timestamp || displayMsg.message.model); + $[34] = displayMsg; + $[35] = isTranscriptMode; + $[36] = t5; + } else { + t5 = $[36]; + } + const hasMetadata = t5; + const t6 = !hasMetadata; + const t7 = hasMetadata ? undefined : columns; + let t8; + if ($[37] !== commands || $[38] !== inProgressToolUseIDs || $[39] !== isActiveCollapsedGroup || $[40] !== isStatic || $[41] !== isTranscriptMode || $[42] !== isUserContinuation || $[43] !== lastThinkingBlockId || $[44] !== latestBashOutputUUID || $[45] !== lookups || $[46] !== msg || $[47] !== onOpenRateLimitOptions || $[48] !== progressMessagesForMessage || $[49] !== shouldAnimate || $[50] !== t6 || $[51] !== t7 || $[52] !== tools || $[53] !== verbose) { + t8 = ; + $[37] = commands; + $[38] = inProgressToolUseIDs; + $[39] = isActiveCollapsedGroup; + $[40] = isStatic; + $[41] = isTranscriptMode; + $[42] = isUserContinuation; + $[43] = lastThinkingBlockId; + $[44] = latestBashOutputUUID; + $[45] = lookups; + $[46] = msg; + $[47] = onOpenRateLimitOptions; + $[48] = progressMessagesForMessage; + $[49] = shouldAnimate; + $[50] = t6; + $[51] = t7; + $[52] = tools; + $[53] = verbose; + $[54] = t8; + } else { + t8 = $[54]; + } + const messageEl = t8; + if (!hasMetadata) { + let t9; + if ($[55] !== messageEl) { + t9 = {messageEl}; + $[55] = messageEl; + $[56] = t9; + } else { + t9 = $[56]; + } + return t9; + } + let t9; + if ($[57] !== displayMsg || $[58] !== isTranscriptMode) { + t9 = ; + $[57] = displayMsg; + $[58] = isTranscriptMode; + $[59] = t9; + } else { + t9 = $[59]; + } + let t10; + if ($[60] !== columns || $[61] !== messageEl || $[62] !== t9) { + t10 = {t9}{messageEl}; + $[60] = columns; + $[61] = messageEl; + $[62] = t9; + $[63] = t10; + } else { + t10 = $[63]; + } + return t10; +} + +/** + * Checks if a message is "streaming" - i.e., its content may still be changing. + * Exported for testing. + */ +function _temp(c) { + return c.type === "text"; +} +export function isMessageStreaming(msg: RenderableMessage, streamingToolUseIDs: Set): boolean { + if (msg.type === 'grouped_tool_use') { + return msg.messages.some(m => { + const content = m.message.content[0]; + return content?.type === 'tool_use' && streamingToolUseIDs.has(content.id); + }); + } + if (msg.type === 'collapsed_read_search') { + const toolIds = getToolUseIdsFromCollapsedGroup(msg); + return toolIds.some(id => streamingToolUseIDs.has(id)); + } + const toolUseID = getToolUseID(msg); + return !!toolUseID && streamingToolUseIDs.has(toolUseID); +} + +/** + * Checks if all tools in a message are resolved. + * Exported for testing. + */ +export function allToolsResolved(msg: RenderableMessage, resolvedToolUseIDs: Set): boolean { + if (msg.type === 'grouped_tool_use') { + return msg.messages.every(m => { + const content = m.message.content[0]; + return content?.type === 'tool_use' && resolvedToolUseIDs.has(content.id); + }); + } + if (msg.type === 'collapsed_read_search') { + const toolIds = getToolUseIdsFromCollapsedGroup(msg); + return toolIds.every(id => resolvedToolUseIDs.has(id)); + } + if (msg.type === 'assistant') { + const block = msg.message.content[0]; + if (block?.type === 'server_tool_use') { + return resolvedToolUseIDs.has(block.id); + } + } + const toolUseID = getToolUseID(msg); + return !toolUseID || resolvedToolUseIDs.has(toolUseID); +} + +/** + * Conservative memo comparator that only bails out when we're CERTAIN + * the message won't change. Fails safe by re-rendering when uncertain. + * + * Exported for testing. + */ +export function areMessageRowPropsEqual(prev: Props, next: Props): boolean { + // Different message reference = content may have changed, must re-render + if (prev.message !== next.message) return false; + + // Screen mode change = re-render + if (prev.screen !== next.screen) return false; + + // Verbose toggle changes thinking block visibility + if (prev.verbose !== next.verbose) return false; + + // collapsed_read_search is never static in prompt mode (matches shouldRenderStatically) + if (prev.message.type === 'collapsed_read_search' && next.screen !== 'transcript') { + return false; + } + + // Width change affects Box layout + if (prev.columns !== next.columns) return false; + + // latestBashOutputUUID affects rendering (full vs truncated output) + const prevIsLatestBash = prev.latestBashOutputUUID === prev.message.uuid; + const nextIsLatestBash = next.latestBashOutputUUID === next.message.uuid; + if (prevIsLatestBash !== nextIsLatestBash) return false; + + // lastThinkingBlockId affects thinking block visibility — but only for + // messages that HAVE thinking content. Checking unconditionally busts the + // memo for every scrollback message whenever thinking starts/stops (CC-941). + if (prev.lastThinkingBlockId !== next.lastThinkingBlockId && hasThinkingContent(next.message)) { + return false; + } + + // Check if this message is still "in flight" + const isStreaming = isMessageStreaming(prev.message, prev.streamingToolUseIDs); + const isResolved = allToolsResolved(prev.message, prev.lookups.resolvedToolUseIDs); + + // Only bail out for truly static messages + if (isStreaming || !isResolved) return false; + + // Static message - safe to skip re-render + return true; +} +export const MessageRow = React.memo(MessageRowImpl, areMessageRowPropsEqual); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkNvbW1hbmQiLCJCb3giLCJTY3JlZW4iLCJUb29scyIsIlJlbmRlcmFibGVNZXNzYWdlIiwiZ2V0RGlzcGxheU1lc3NhZ2VGcm9tQ29sbGFwc2VkIiwiZ2V0VG9vbFNlYXJjaE9yUmVhZEluZm8iLCJnZXRUb29sVXNlSWRzRnJvbUNvbGxhcHNlZEdyb3VwIiwiaGFzQW55VG9vbEluUHJvZ3Jlc3MiLCJidWlsZE1lc3NhZ2VMb29rdXBzIiwiRU1QVFlfU1RSSU5HX1NFVCIsImdldFByb2dyZXNzTWVzc2FnZXNGcm9tTG9va3VwIiwiZ2V0U2libGluZ1Rvb2xVc2VJRHNGcm9tTG9va3VwIiwiZ2V0VG9vbFVzZUlEIiwiaGFzVGhpbmtpbmdDb250ZW50IiwiTWVzc2FnZSIsIk1lc3NhZ2VNb2RlbCIsInNob3VsZFJlbmRlclN0YXRpY2FsbHkiLCJNZXNzYWdlVGltZXN0YW1wIiwiT2Zmc2NyZWVuRnJlZXplIiwiUHJvcHMiLCJtZXNzYWdlIiwiaXNVc2VyQ29udGludWF0aW9uIiwiaGFzQ29udGVudEFmdGVyIiwidG9vbHMiLCJjb21tYW5kcyIsInZlcmJvc2UiLCJpblByb2dyZXNzVG9vbFVzZUlEcyIsIlNldCIsInN0cmVhbWluZ1Rvb2xVc2VJRHMiLCJzY3JlZW4iLCJjYW5BbmltYXRlIiwib25PcGVuUmF0ZUxpbWl0T3B0aW9ucyIsImxhc3RUaGlua2luZ0Jsb2NrSWQiLCJsYXRlc3RCYXNoT3V0cHV0VVVJRCIsImNvbHVtbnMiLCJpc0xvYWRpbmciLCJsb29rdXBzIiwiUmV0dXJuVHlwZSIsImhhc0NvbnRlbnRBZnRlckluZGV4IiwibWVzc2FnZXMiLCJpbmRleCIsImkiLCJsZW5ndGgiLCJtc2ciLCJ0eXBlIiwiY29udGVudCIsIm5hbWUiLCJpbnB1dCIsImlzQ29sbGFwc2libGUiLCJoYXMiLCJpZCIsImZpcnN0SW5wdXQiLCJ0b29sTmFtZSIsIk1lc3NhZ2VSb3dJbXBsIiwidDAiLCIkIiwiX2MiLCJpc1RyYW5zY3JpcHRNb2RlIiwiaXNHcm91cGVkIiwiaXNDb2xsYXBzZWQiLCJ0MSIsImlzQWN0aXZlQ29sbGFwc2VkR3JvdXAiLCJ0MiIsImRpc3BsYXlNZXNzYWdlIiwiZGlzcGxheU1zZyIsInQzIiwicHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2UiLCJ0NCIsInNpYmxpbmdUb29sVXNlSURzIiwiaXNTdGF0aWMiLCJzaG91bGRBbmltYXRlIiwidDUiLCJ0NiIsIm0iLCJzb21lIiwidG9vbFVzZUlEIiwiX3RlbXAiLCJ0aW1lc3RhbXAiLCJtb2RlbCIsImhhc01ldGFkYXRhIiwidDciLCJ1bmRlZmluZWQiLCJ0OCIsIm1lc3NhZ2VFbCIsInQ5IiwidDEwIiwiYyIsImlzTWVzc2FnZVN0cmVhbWluZyIsInRvb2xJZHMiLCJhbGxUb29sc1Jlc29sdmVkIiwicmVzb2x2ZWRUb29sVXNlSURzIiwiZXZlcnkiLCJibG9jayIsImFyZU1lc3NhZ2VSb3dQcm9wc0VxdWFsIiwicHJldiIsIm5leHQiLCJwcmV2SXNMYXRlc3RCYXNoIiwidXVpZCIsIm5leHRJc0xhdGVzdEJhc2giLCJpc1N0cmVhbWluZyIsImlzUmVzb2x2ZWQiLCJNZXNzYWdlUm93IiwibWVtbyJdLCJzb3VyY2VzIjpbIk1lc3NhZ2VSb3cudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kIH0gZnJvbSAnLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyBCb3ggfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IFNjcmVlbiB9IGZyb20gJy4uL3NjcmVlbnMvUkVQTC5qcydcbmltcG9ydCB0eXBlIHsgVG9vbHMgfSBmcm9tICcuLi9Ub29sLmpzJ1xuaW1wb3J0IHR5cGUgeyBSZW5kZXJhYmxlTWVzc2FnZSB9IGZyb20gJy4uL3R5cGVzL21lc3NhZ2UuanMnXG5pbXBvcnQge1xuICBnZXREaXNwbGF5TWVzc2FnZUZyb21Db2xsYXBzZWQsXG4gIGdldFRvb2xTZWFyY2hPclJlYWRJbmZvLFxuICBnZXRUb29sVXNlSWRzRnJvbUNvbGxhcHNlZEdyb3VwLFxuICBoYXNBbnlUb29sSW5Qcm9ncmVzcyxcbn0gZnJvbSAnLi4vdXRpbHMvY29sbGFwc2VSZWFkU2VhcmNoLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBidWlsZE1lc3NhZ2VMb29rdXBzLFxuICBFTVBUWV9TVFJJTkdfU0VULFxuICBnZXRQcm9ncmVzc01lc3NhZ2VzRnJvbUxvb2t1cCxcbiAgZ2V0U2libGluZ1Rvb2xVc2VJRHNGcm9tTG9va3VwLFxuICBnZXRUb29sVXNlSUQsXG59IGZyb20gJy4uL3V0aWxzL21lc3NhZ2VzLmpzJ1xuaW1wb3J0IHsgaGFzVGhpbmtpbmdDb250ZW50LCBNZXNzYWdlIH0gZnJvbSAnLi9NZXNzYWdlLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZU1vZGVsIH0gZnJvbSAnLi9NZXNzYWdlTW9kZWwuanMnXG5pbXBvcnQgeyBzaG91bGRSZW5kZXJTdGF0aWNhbGx5IH0gZnJvbSAnLi9NZXNzYWdlcy5qcydcbmltcG9ydCB7IE1lc3NhZ2VUaW1lc3RhbXAgfSBmcm9tICcuL01lc3NhZ2VUaW1lc3RhbXAuanMnXG5pbXBvcnQgeyBPZmZzY3JlZW5GcmVlemUgfSBmcm9tICcuL09mZnNjcmVlbkZyZWV6ZS5qcydcblxuZXhwb3J0IHR5cGUgUHJvcHMgPSB7XG4gIG1lc3NhZ2U6IFJlbmRlcmFibGVNZXNzYWdlXG4gIC8qKiBXaGV0aGVyIHRoZSBwcmV2aW91cyBtZXNzYWdlIGluIHJlbmRlcmFibGVNZXNzYWdlcyBpcyBhbHNvIGEgdXNlciBtZXNzYWdlLiAqL1xuICBpc1VzZXJDb250aW51YXRpb246IGJvb2xlYW5cbiAgLyoqXG4gICAqIFdoZXRoZXIgdGhlcmUgaXMgbm9uLXNraXBwYWJsZSBjb250ZW50IGFmdGVyIHRoaXMgbWVzc2FnZSBpbiByZW5kZXJhYmxlTWVzc2FnZXMuXG4gICAqIE9ubHkgbmVlZHMgdG8gYmUgYWNjdXJhdGUgZm9yIGBjb2xsYXBzZWRfcmVhZF9zZWFyY2hgIG1lc3NhZ2VzIOKAlCB1c2VkIHRvIGRlY2lkZVxuICAgKiBpZiB0aGUgY29sbGFwc2VkIGdyb3VwIHNwaW5uZXIgc2hvdWxkIHN0YXkgYWN0aXZlLiBQYXNzIGBmYWxzZWAgb3RoZXJ3aXNlLlxuICAgKi9cbiAgaGFzQ29udGVudEFmdGVyOiBib29sZWFuXG4gIHRvb2xzOiBUb29sc1xuICBjb21tYW5kczogQ29tbWFuZFtdXG4gIHZlcmJvc2U6IGJvb2xlYW5cbiAgaW5Qcm9ncmVzc1Rvb2xVc2VJRHM6IFNldDxzdHJpbmc+XG4gIHN0cmVhbWluZ1Rvb2xVc2VJRHM6IFNldDxzdHJpbmc+XG4gIHNjcmVlbjogU2NyZWVuXG4gIGNhbkFuaW1hdGU6IGJvb2xlYW5cbiAgb25PcGVuUmF0ZUxpbWl0T3B0aW9ucz86ICgpID0+IHZvaWRcbiAgbGFzdFRoaW5raW5nQmxvY2tJZDogc3RyaW5nIHwgbnVsbFxuICBsYXRlc3RCYXNoT3V0cHV0VVVJRDogc3RyaW5nIHwgbnVsbFxuICBjb2x1bW5zOiBudW1iZXJcbiAgaXNMb2FkaW5nOiBib29sZWFuXG4gIGxvb2t1cHM6IFJldHVyblR5cGU8dHlwZW9mIGJ1aWxkTWVzc2FnZUxvb2t1cHM+XG59XG5cbi8qKlxuICogU2NhbnMgZm9yd2FyZCBmcm9tIGBpbmRleCsxYCB0byBjaGVjayBpZiBhbnkgXCJyZWFsXCIgY29udGVudCBmb2xsb3dzLiBVc2VkIHRvXG4gKiBkZWNpZGUgd2hldGhlciBhIGNvbGxhcHNlZCByZWFkL3NlYXJjaCBncm91cCBzaG91bGQgc3RheSBpbiBpdHMgYWN0aXZlXG4gKiAoZ3JleSBkb3QsIHByZXNlbnQtdGVuc2UgXCJSZWFkaW5n4oCmXCIpIHN0YXRlIHdoaWxlIHRoZSBxdWVyeSBpcyBzdGlsbCBsb2FkaW5nLlxuICpcbiAqIEV4cG9ydGVkIHNvIE1lc3NhZ2VzLnRzeCBjYW4gY29tcHV0ZSB0aGlzIG9uY2UgcGVyIG1lc3NhZ2UgYW5kIHBhc3MgdGhlXG4gKiByZXN1bHQgYXMgYSBib29sZWFuIHByb3Ag4oCUIGF2b2lkcyBwYXNzaW5nIHRoZSBmdWxsIGByZW5kZXJhYmxlTWVzc2FnZXNgIGFycmF5XG4gKiB0byBlYWNoIE1lc3NhZ2VSb3cgKHdoaWNoIFJlYWN0IENvbXBpbGVyIHdvdWxkIHBpbiBpbiB0aGUgZmliZXIncyBtZW1vQ2FjaGUsXG4gKiBhY2N1bXVsYXRpbmcgZXZlcnkgaGlzdG9yaWNhbCB2ZXJzaW9uIG9mIHRoZSBhcnJheSDiiYggMS0yTUIgb3ZlciBhIDctdHVybiBzZXNzaW9uKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGhhc0NvbnRlbnRBZnRlckluZGV4KFxuICBtZXNzYWdlczogUmVuZGVyYWJsZU1lc3NhZ2VbXSxcbiAgaW5kZXg6IG51bWJlcixcbiAgdG9vbHM6IFRvb2xzLFxuICBzdHJlYW1pbmdUb29sVXNlSURzOiBTZXQ8c3RyaW5nPixcbik6IGJvb2xlYW4ge1xuICBmb3IgKGxldCBpID0gaW5kZXggKyAxOyBpIDwgbWVzc2FnZXMubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBtc2cgPSBtZXNzYWdlc1tpXVxuICAgIGlmIChtc2c/LnR5cGUgPT09ICdhc3Npc3RhbnQnKSB7XG4gICAgICBjb25zdCBjb250ZW50ID0gbXNnLm1lc3NhZ2UuY29udGVudFswXVxuICAgICAgaWYgKFxuICAgICAgICBjb250ZW50Py50eXBlID09PSAndGhpbmtpbmcnIHx8XG4gICAgICAgIGNvbnRlbnQ/LnR5cGUgPT09ICdyZWRhY3RlZF90aGlua2luZydcbiAgICAgICkge1xuICAgICAgICBjb250aW51ZVxuICAgICAgfVxuICAgICAgaWYgKGNvbnRlbnQ/LnR5cGUgPT09ICd0b29sX3VzZScpIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGdldFRvb2xTZWFyY2hPclJlYWRJbmZvKGNvbnRlbnQubmFtZSwgY29udGVudC5pbnB1dCwgdG9vbHMpXG4gICAgICAgICAgICAuaXNDb2xsYXBzaWJsZVxuICAgICAgICApIHtcbiAgICAgICAgICBjb250aW51ZVxuICAgICAgICB9XG4gICAgICAgIC8vIE5vbi1jb2xsYXBzaWJsZSB0b29sIHVzZXMgYXBwZWFyIGluIHN5bnRoZXRpY1N0cmVhbWluZ1Rvb2xVc2VNZXNzYWdlc1xuICAgICAgICAvLyBiZWZvcmUgdGhlaXIgSUQgaXMgYWRkZWQgdG8gaW5Qcm9ncmVzc1Rvb2xVc2VJRHMuIFNraXAgd2hpbGUgc3RyZWFtaW5nXG4gICAgICAgIC8vIHRvIGF2b2lkIGJyaWVmbHkgZmluYWxpemluZyB0aGUgcmVhZCBncm91cC5cbiAgICAgICAgaWYgKHN0cmVhbWluZ1Rvb2xVc2VJRHMuaGFzKGNvbnRlbnQuaWQpKSB7XG4gICAgICAgICAgY29udGludWVcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG4gICAgaWYgKG1zZz8udHlwZSA9PT0gJ3N5c3RlbScgfHwgbXNnPy50eXBlID09PSAnYXR0YWNobWVudCcpIHtcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuICAgIC8vIFRvb2wgcmVzdWx0cyBhcnJpdmUgd2hpbGUgdGhlIGNvbGxhcHNlZCBncm91cCBpcyBzdGlsbCBiZWluZyBidWlsdFxuICAgIGlmIChtc2c/LnR5cGUgPT09ICd1c2VyJykge1xuICAgICAgY29uc3QgY29udGVudCA9IG1zZy5tZXNzYWdlLmNvbnRlbnRbMF1cbiAgICAgIGlmIChjb250ZW50Py50eXBlID09PSAndG9vbF9yZXN1bHQnKSB7XG4gICAgICAgIGNvbnRpbnVlXG4gICAgICB9XG4gICAgfVxuICAgIC8vIENvbGxhcHNpYmxlIGdyb3VwZWRfdG9vbF91c2UgbWVzc2FnZXMgYXJyaXZlIHRyYW5zaWVudGx5IGJlZm9yZSBiZWluZ1xuICAgIC8vIG1lcmdlZCBpbnRvIHRoZSBjdXJyZW50IGNvbGxhcHNlZCBncm91cCBvbiB0aGUgbmV4dCByZW5kZXIgY3ljbGVcbiAgICBpZiAobXNnPy50eXBlID09PSAnZ3JvdXBlZF90b29sX3VzZScpIHtcbiAgICAgIGNvbnN0IGZpcnN0SW5wdXQgPSBtc2cubWVzc2FnZXNbMF0/Lm1lc3NhZ2UuY29udGVudFswXT8uaW5wdXRcbiAgICAgIGlmIChcbiAgICAgICAgZ2V0VG9vbFNlYXJjaE9yUmVhZEluZm8obXNnLnRvb2xOYW1lLCBmaXJzdElucHV0LCB0b29scykuaXNDb2xsYXBzaWJsZVxuICAgICAgKSB7XG4gICAgICAgIGNvbnRpbnVlXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0cnVlXG4gIH1cbiAgcmV0dXJuIGZhbHNlXG59XG5cbmZ1bmN0aW9uIE1lc3NhZ2VSb3dJbXBsKHtcbiAgbWVzc2FnZTogbXNnLFxuICBpc1VzZXJDb250aW51YXRpb24sXG4gIGhhc0NvbnRlbnRBZnRlcixcbiAgdG9vbHMsXG4gIGNvbW1hbmRzLFxuICB2ZXJib3NlLFxuICBpblByb2dyZXNzVG9vbFVzZUlEcyxcbiAgc3RyZWFtaW5nVG9vbFVzZUlEcyxcbiAgc2NyZWVuLFxuICBjYW5BbmltYXRlLFxuICBvbk9wZW5SYXRlTGltaXRPcHRpb25zLFxuICBsYXN0VGhpbmtpbmdCbG9ja0lkLFxuICBsYXRlc3RCYXNoT3V0cHV0VVVJRCxcbiAgY29sdW1ucyxcbiAgaXNMb2FkaW5nLFxuICBsb29rdXBzLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBpc1RyYW5zY3JpcHRNb2RlID0gc2NyZWVuID09PSAndHJhbnNjcmlwdCdcbiAgY29uc3QgaXNHcm91cGVkID0gbXNnLnR5cGUgPT09ICdncm91cGVkX3Rvb2xfdXNlJ1xuICBjb25zdCBpc0NvbGxhcHNlZCA9IG1zZy50eXBlID09PSAnY29sbGFwc2VkX3JlYWRfc2VhcmNoJ1xuXG4gIC8vIEEgY29sbGFwc2VkIGdyb3VwIGlzIFwiYWN0aXZlXCIgKGdyZXkgZG90LCBwcmVzZW50IHRlbnNlIFwiUmVhZGluZ+KAplwiKSB3aGVuIGl0cyB0b29sc1xuICAvLyBhcmUgc3RpbGwgZXhlY3V0aW5nIE9SIHdoZW4gdGhlIG92ZXJhbGwgcXVlcnkgaXMgc3RpbGwgcnVubmluZyB3aXRoIG5vdGhpbmcgYWZ0ZXIgaXQuXG4gIC8vIGhhc0FueVRvb2xJblByb2dyZXNzIHRha2VzIHByaW9yaXR5OiBpZiB0b29scyBhcmUgcnVubmluZywgYWx3YXlzIHNob3cgYWN0aXZlIHJlZ2FyZGxlc3NcbiAgLy8gb2Ygd2hhdCBlbHNlIGlzIGluIHRoZSBtZXNzYWdlIGxpc3QgKGF2b2lkcyBmYWxzZSBmaW5hbGl6YXRpb24gZHVyaW5nIHBhcmFsbGVsIGV4ZWN1dGlvbikuXG4gIGNvbnN0IGlzQWN0aXZlQ29sbGFwc2VkR3JvdXAgPVxuICAgIGlzQ29sbGFwc2VkICYmXG4gICAgKGhhc0FueVRvb2xJblByb2dyZXNzKG1zZywgaW5Qcm9ncmVzc1Rvb2xVc2VJRHMpIHx8XG4gICAgICAoaXNMb2FkaW5nICYmICFoYXNDb250ZW50QWZ0ZXIpKVxuXG4gIGNvbnN0IGRpc3BsYXlNc2cgPSBpc0dyb3VwZWRcbiAgICA/IG1zZy5kaXNwbGF5TWVzc2FnZVxuICAgIDogaXNDb2xsYXBzZWRcbiAgICAgID8gZ2V0RGlzcGxheU1lc3NhZ2VGcm9tQ29sbGFwc2VkKG1zZylcbiAgICAgIDogbXNnXG5cbiAgY29uc3QgcHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2UgPVxuICAgIGlzR3JvdXBlZCB8fCBpc0NvbGxhcHNlZCA/IFtdIDogZ2V0UHJvZ3Jlc3NNZXNzYWdlc0Zyb21Mb29rdXAobXNnLCBsb29rdXBzKVxuXG4gIGNvbnN0IHNpYmxpbmdUb29sVXNlSURzID1cbiAgICBpc0dyb3VwZWQgfHwgaXNDb2xsYXBzZWRcbiAgICAgID8gRU1QVFlfU1RSSU5HX1NFVFxuICAgICAgOiBnZXRTaWJsaW5nVG9vbFVzZUlEc0Zyb21Mb29rdXAobXNnLCBsb29rdXBzKVxuXG4gIGNvbnN0IGlzU3RhdGljID0gc2hvdWxkUmVuZGVyU3RhdGljYWxseShcbiAgICBtc2csXG4gICAgc3RyZWFtaW5nVG9vbFVzZUlEcyxcbiAgICBpblByb2dyZXNzVG9vbFVzZUlEcyxcbiAgICBzaWJsaW5nVG9vbFVzZUlEcyxcbiAgICBzY3JlZW4sXG4gICAgbG9va3VwcyxcbiAgKVxuXG4gIGxldCBzaG91bGRBbmltYXRlID0gZmFsc2VcbiAgaWYgKGNhbkFuaW1hdGUpIHtcbiAgICBpZiAoaXNHcm91cGVkKSB7XG4gICAgICBzaG91bGRBbmltYXRlID0gbXNnLm1lc3NhZ2VzLnNvbWUobSA9PiB7XG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBtLm1lc3NhZ2UuY29udGVudFswXVxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIGNvbnRlbnQ/LnR5cGUgPT09ICd0b29sX3VzZScgJiYgaW5Qcm9ncmVzc1Rvb2xVc2VJRHMuaGFzKGNvbnRlbnQuaWQpXG4gICAgICAgIClcbiAgICAgIH0pXG4gICAgfSBlbHNlIGlmIChpc0NvbGxhcHNlZCkge1xuICAgICAgc2hvdWxkQW5pbWF0ZSA9IGhhc0FueVRvb2xJblByb2dyZXNzKG1zZywgaW5Qcm9ncmVzc1Rvb2xVc2VJRHMpXG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IHRvb2xVc2VJRCA9IGdldFRvb2xVc2VJRChtc2cpXG4gICAgICBzaG91bGRBbmltYXRlID0gIXRvb2xVc2VJRCB8fCBpblByb2dyZXNzVG9vbFVzZUlEcy5oYXModG9vbFVzZUlEKVxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IGhhc01ldGFkYXRhID1cbiAgICBpc1RyYW5zY3JpcHRNb2RlICYmXG4gICAgZGlzcGxheU1zZy50eXBlID09PSAnYXNzaXN0YW50JyAmJlxuICAgIGRpc3BsYXlNc2cubWVzc2FnZS5jb250ZW50LnNvbWUoYyA9PiBjLnR5cGUgPT09ICd0ZXh0JykgJiZcbiAgICAoZGlzcGxheU1zZy50aW1lc3RhbXAgfHwgZGlzcGxheU1zZy5tZXNzYWdlLm1vZGVsKVxuXG4gIGNvbnN0IG1lc3NhZ2VFbCA9IChcbiAgICA8TWVzc2FnZVxuICAgICAgbWVzc2FnZT17bXNnfVxuICAgICAgbG9va3Vwcz17bG9va3Vwc31cbiAgICAgIGFkZE1hcmdpbj17IWhhc01ldGFkYXRhfVxuICAgICAgY29udGFpbmVyV2lkdGg9e2hhc01ldGFkYXRhID8gdW5kZWZpbmVkIDogY29sdW1uc31cbiAgICAgIHRvb2xzPXt0b29sc31cbiAgICAgIGNvbW1hbmRzPXtjb21tYW5kc31cbiAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICBpblByb2dyZXNzVG9vbFVzZUlEcz17aW5Qcm9ncmVzc1Rvb2xVc2VJRHN9XG4gICAgICBwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZT17cHJvZ3Jlc3NNZXNzYWdlc0Zvck1lc3NhZ2V9XG4gICAgICBzaG91bGRBbmltYXRlPXtzaG91bGRBbmltYXRlfVxuICAgICAgc2hvdWxkU2hvd0RvdD17dHJ1ZX1cbiAgICAgIGlzVHJhbnNjcmlwdE1vZGU9e2lzVHJhbnNjcmlwdE1vZGV9XG4gICAgICBpc1N0YXRpYz17aXNTdGF0aWN9XG4gICAgICBvbk9wZW5SYXRlTGltaXRPcHRpb25zPXtvbk9wZW5SYXRlTGltaXRPcHRpb25zfVxuICAgICAgaXNBY3RpdmVDb2xsYXBzZWRHcm91cD17aXNBY3RpdmVDb2xsYXBzZWRHcm91cH1cbiAgICAgIGlzVXNlckNvbnRpbnVhdGlvbj17aXNVc2VyQ29udGludWF0aW9ufVxuICAgICAgbGFzdFRoaW5raW5nQmxvY2tJZD17bGFzdFRoaW5raW5nQmxvY2tJZH1cbiAgICAgIGxhdGVzdEJhc2hPdXRwdXRVVUlEPXtsYXRlc3RCYXNoT3V0cHV0VVVJRH1cbiAgICAvPlxuICApXG4gIC8vIE9mZnNjcmVlbkZyZWV6ZTogdGhlIG91dGVyIFJlYWN0Lm1lbW8gYWxyZWFkeSBiYWlscyBmb3Igc3RhdGljIG1lc3NhZ2VzLFxuICAvLyBzbyB0aGlzIG9ubHkgd3JhcHMgcm93cyB0aGF0IERPIHJlLXJlbmRlciDigJQgaW4tcHJvZ3Jlc3MgdG9vbHMsIGNvbGxhcHNlZFxuICAvLyByZWFkL3NlYXJjaCBzcGlubmVycywgYmFzaCBlbGFwc2VkIHRpbWVycy4gV2hlbiB0aG9zZSByb3dzIGhhdmUgc2Nyb2xsZWRcbiAgLy8gaW50byB0ZXJtaW5hbCBzY3JvbGxiYWNrIChub24tZnVsbHNjcmVlbiBleHRlcm5hbCBidWlsZHMpLCBhbnkgY29udGVudFxuICAvLyBjaGFuZ2UgZm9yY2VzIGxvZy11cGRhdGUudHMgaW50byBhIGZ1bGwgdGVybWluYWwgcmVzZXQgcGVyIHRpY2suIEZyZWV6aW5nXG4gIC8vIHJldHVybnMgdGhlIGNhY2hlZCBlbGVtZW50IHJlZiBzbyBSZWFjdCBiYWlscyBhbmQgcHJvZHVjZXMgemVybyBkaWZmLlxuICBpZiAoIWhhc01ldGFkYXRhKSB7XG4gICAgcmV0dXJuIDxPZmZzY3JlZW5GcmVlemU+e21lc3NhZ2VFbH08L09mZnNjcmVlbkZyZWV6ZT5cbiAgfVxuICAvLyBNYXJnaW4gb24gY2hpbGRyZW4sIG5vdCBoZXJlIOKAlCBlbHNlIG51bGwgaXRlbXMgKGhvb2tfc3VjY2VzcyBldGMuKSBnZXQgcGhhbnRvbSAxLXJvdyBzcGFjaW5nLlxuICByZXR1cm4gKFxuICAgIDxPZmZzY3JlZW5GcmVlemU+XG4gICAgICA8Qm94IHdpZHRoPXtjb2x1bW5zfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIDxCb3hcbiAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwicm93XCJcbiAgICAgICAgICBqdXN0aWZ5Q29udGVudD1cImZsZXgtZW5kXCJcbiAgICAgICAgICBnYXA9ezF9XG4gICAgICAgICAgbWFyZ2luVG9wPXsxfVxuICAgICAgICA+XG4gICAgICAgICAgPE1lc3NhZ2VUaW1lc3RhbXBcbiAgICAgICAgICAgIG1lc3NhZ2U9e2Rpc3BsYXlNc2d9XG4gICAgICAgICAgICBpc1RyYW5zY3JpcHRNb2RlPXtpc1RyYW5zY3JpcHRNb2RlfVxuICAgICAgICAgIC8+XG4gICAgICAgICAgPE1lc3NhZ2VNb2RlbFxuICAgICAgICAgICAgbWVzc2FnZT17ZGlzcGxheU1zZ31cbiAgICAgICAgICAgIGlzVHJhbnNjcmlwdE1vZGU9e2lzVHJhbnNjcmlwdE1vZGV9XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHttZXNzYWdlRWx9XG4gICAgICA8L0JveD5cbiAgICA8L09mZnNjcmVlbkZyZWV6ZT5cbiAgKVxufVxuXG4vKipcbiAqIENoZWNrcyBpZiBhIG1lc3NhZ2UgaXMgXCJzdHJlYW1pbmdcIiAtIGkuZS4sIGl0cyBjb250ZW50IG1heSBzdGlsbCBiZSBjaGFuZ2luZy5cbiAqIEV4cG9ydGVkIGZvciB0ZXN0aW5nLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNNZXNzYWdlU3RyZWFtaW5nKFxuICBtc2c6IFJlbmRlcmFibGVNZXNzYWdlLFxuICBzdHJlYW1pbmdUb29sVXNlSURzOiBTZXQ8c3RyaW5nPixcbik6IGJvb2xlYW4ge1xuICBpZiAobXNnLnR5cGUgPT09ICdncm91cGVkX3Rvb2xfdXNlJykge1xuICAgIHJldHVybiBtc2cubWVzc2FnZXMuc29tZShtID0+IHtcbiAgICAgIGNvbnN0IGNvbnRlbnQgPSBtLm1lc3NhZ2UuY29udGVudFswXVxuICAgICAgcmV0dXJuIGNvbnRlbnQ/LnR5cGUgPT09ICd0b29sX3VzZScgJiYgc3RyZWFtaW5nVG9vbFVzZUlEcy5oYXMoY29udGVudC5pZClcbiAgICB9KVxuICB9XG4gIGlmIChtc2cudHlwZSA9PT0gJ2NvbGxhcHNlZF9yZWFkX3NlYXJjaCcpIHtcbiAgICBjb25zdCB0b29sSWRzID0gZ2V0VG9vbFVzZUlkc0Zyb21Db2xsYXBzZWRHcm91cChtc2cpXG4gICAgcmV0dXJuIHRvb2xJZHMuc29tZShpZCA9PiBzdHJlYW1pbmdUb29sVXNlSURzLmhhcyhpZCkpXG4gIH1cbiAgY29uc3QgdG9vbFVzZUlEID0gZ2V0VG9vbFVzZUlEKG1zZylcbiAgcmV0dXJuICEhdG9vbFVzZUlEICYmIHN0cmVhbWluZ1Rvb2xVc2VJRHMuaGFzKHRvb2xVc2VJRClcbn1cblxuLyoqXG4gKiBDaGVja3MgaWYgYWxsIHRvb2xzIGluIGEgbWVzc2FnZSBhcmUgcmVzb2x2ZWQuXG4gKiBFeHBvcnRlZCBmb3IgdGVzdGluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFsbFRvb2xzUmVzb2x2ZWQoXG4gIG1zZzogUmVuZGVyYWJsZU1lc3NhZ2UsXG4gIHJlc29sdmVkVG9vbFVzZUlEczogU2V0PHN0cmluZz4sXG4pOiBib29sZWFuIHtcbiAgaWYgKG1zZy50eXBlID09PSAnZ3JvdXBlZF90b29sX3VzZScpIHtcbiAgICByZXR1cm4gbXNnLm1lc3NhZ2VzLmV2ZXJ5KG0gPT4ge1xuICAgICAgY29uc3QgY29udGVudCA9IG0ubWVzc2FnZS5jb250ZW50WzBdXG4gICAgICByZXR1cm4gY29udGVudD8udHlwZSA9PT0gJ3Rvb2xfdXNlJyAmJiByZXNvbHZlZFRvb2xVc2VJRHMuaGFzKGNvbnRlbnQuaWQpXG4gICAgfSlcbiAgfVxuICBpZiAobXNnLnR5cGUgPT09ICdjb2xsYXBzZWRfcmVhZF9zZWFyY2gnKSB7XG4gICAgY29uc3QgdG9vbElkcyA9IGdldFRvb2xVc2VJZHNGcm9tQ29sbGFwc2VkR3JvdXAobXNnKVxuICAgIHJldHVybiB0b29sSWRzLmV2ZXJ5KGlkID0+IHJlc29sdmVkVG9vbFVzZUlEcy5oYXMoaWQpKVxuICB9XG4gIGlmIChtc2cudHlwZSA9PT0gJ2Fzc2lzdGFudCcpIHtcbiAgICBjb25zdCBibG9jayA9IG1zZy5tZXNzYWdlLmNvbnRlbnRbMF1cbiAgICBpZiAoYmxvY2s/LnR5cGUgPT09ICdzZXJ2ZXJfdG9vbF91c2UnKSB7XG4gICAgICByZXR1cm4gcmVzb2x2ZWRUb29sVXNlSURzLmhhcyhibG9jay5pZClcbiAgICB9XG4gIH1cbiAgY29uc3QgdG9vbFVzZUlEID0gZ2V0VG9vbFVzZUlEKG1zZylcbiAgcmV0dXJuICF0b29sVXNlSUQgfHwgcmVzb2x2ZWRUb29sVXNlSURzLmhhcyh0b29sVXNlSUQpXG59XG5cbi8qKlxuICogQ29uc2VydmF0aXZlIG1lbW8gY29tcGFyYXRvciB0aGF0IG9ubHkgYmFpbHMgb3V0IHdoZW4gd2UncmUgQ0VSVEFJTlxuICogdGhlIG1lc3NhZ2Ugd29uJ3QgY2hhbmdlLiBGYWlscyBzYWZlIGJ5IHJlLXJlbmRlcmluZyB3aGVuIHVuY2VydGFpbi5cbiAqXG4gKiBFeHBvcnRlZCBmb3IgdGVzdGluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFyZU1lc3NhZ2VSb3dQcm9wc0VxdWFsKHByZXY6IFByb3BzLCBuZXh0OiBQcm9wcyk6IGJvb2xlYW4ge1xuICAvLyBEaWZmZXJlbnQgbWVzc2FnZSByZWZlcmVuY2UgPSBjb250ZW50IG1heSBoYXZlIGNoYW5nZWQsIG11c3QgcmUtcmVuZGVyXG4gIGlmIChwcmV2Lm1lc3NhZ2UgIT09IG5leHQubWVzc2FnZSkgcmV0dXJuIGZhbHNlXG5cbiAgLy8gU2NyZWVuIG1vZGUgY2hhbmdlID0gcmUtcmVuZGVyXG4gIGlmIChwcmV2LnNjcmVlbiAhPT0gbmV4dC5zY3JlZW4pIHJldHVybiBmYWxzZVxuXG4gIC8vIFZlcmJvc2UgdG9nZ2xlIGNoYW5nZXMgdGhpbmtpbmcgYmxvY2sgdmlzaWJpbGl0eVxuICBpZiAocHJldi52ZXJib3NlICE9PSBuZXh0LnZlcmJvc2UpIHJldHVybiBmYWxzZVxuXG4gIC8vIGNvbGxhcHNlZF9yZWFkX3NlYXJjaCBpcyBuZXZlciBzdGF0aWMgaW4gcHJvbXB0IG1vZGUgKG1hdGNoZXMgc2hvdWxkUmVuZGVyU3RhdGljYWxseSlcbiAgaWYgKFxuICAgIHByZXYubWVzc2FnZS50eXBlID09PSAnY29sbGFwc2VkX3JlYWRfc2VhcmNoJyAmJlxuICAgIG5leHQuc2NyZWVuICE9PSAndHJhbnNjcmlwdCdcbiAgKSB7XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cblxuICAvLyBXaWR0aCBjaGFuZ2UgYWZmZWN0cyBCb3ggbGF5b3V0XG4gIGlmIChwcmV2LmNvbHVtbnMgIT09IG5leHQuY29sdW1ucykgcmV0dXJuIGZhbHNlXG5cbiAgLy8gbGF0ZXN0QmFzaE91dHB1dFVVSUQgYWZmZWN0cyByZW5kZXJpbmcgKGZ1bGwgdnMgdHJ1bmNhdGVkIG91dHB1dClcbiAgY29uc3QgcHJldklzTGF0ZXN0QmFzaCA9IHByZXYubGF0ZXN0QmFzaE91dHB1dFVVSUQgPT09IHByZXYubWVzc2FnZS51dWlkXG4gIGNvbnN0IG5leHRJc0xhdGVzdEJhc2ggPSBuZXh0LmxhdGVzdEJhc2hPdXRwdXRVVUlEID09PSBuZXh0Lm1lc3NhZ2UudXVpZFxuICBpZiAocHJldklzTGF0ZXN0QmFzaCAhPT0gbmV4dElzTGF0ZXN0QmFzaCkgcmV0dXJuIGZhbHNlXG5cbiAgLy8gbGFzdFRoaW5raW5nQmxvY2tJZCBhZmZlY3RzIHRoaW5raW5nIGJsb2NrIHZpc2liaWxpdHkg4oCUIGJ1dCBvbmx5IGZvclxuICAvLyBtZXNzYWdlcyB0aGF0IEhBVkUgdGhpbmtpbmcgY29udGVudC4gQ2hlY2tpbmcgdW5jb25kaXRpb25hbGx5IGJ1c3RzIHRoZVxuICAvLyBtZW1vIGZvciBldmVyeSBzY3JvbGxiYWNrIG1lc3NhZ2Ugd2hlbmV2ZXIgdGhpbmtpbmcgc3RhcnRzL3N0b3BzIChDQy05NDEpLlxuICBpZiAoXG4gICAgcHJldi5sYXN0VGhpbmtpbmdCbG9ja0lkICE9PSBuZXh0Lmxhc3RUaGlua2luZ0Jsb2NrSWQgJiZcbiAgICBoYXNUaGlua2luZ0NvbnRlbnQobmV4dC5tZXNzYWdlKVxuICApIHtcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIC8vIENoZWNrIGlmIHRoaXMgbWVzc2FnZSBpcyBzdGlsbCBcImluIGZsaWdodFwiXG4gIGNvbnN0IGlzU3RyZWFtaW5nID0gaXNNZXNzYWdlU3RyZWFtaW5nKHByZXYubWVzc2FnZSwgcHJldi5zdHJlYW1pbmdUb29sVXNlSURzKVxuICBjb25zdCBpc1Jlc29sdmVkID0gYWxsVG9vbHNSZXNvbHZlZChcbiAgICBwcmV2Lm1lc3NhZ2UsXG4gICAgcHJldi5sb29rdXBzLnJlc29sdmVkVG9vbFVzZUlEcyxcbiAgKVxuXG4gIC8vIE9ubHkgYmFpbCBvdXQgZm9yIHRydWx5IHN0YXRpYyBtZXNzYWdlc1xuICBpZiAoaXNTdHJlYW1pbmcgfHwgIWlzUmVzb2x2ZWQpIHJldHVybiBmYWxzZVxuXG4gIC8vIFN0YXRpYyBtZXNzYWdlIC0gc2FmZSB0byBza2lwIHJlLXJlbmRlclxuICByZXR1cm4gdHJ1ZVxufVxuXG5leHBvcnQgY29uc3QgTWVzc2FnZVJvdyA9IFJlYWN0Lm1l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 \ No newline at end of file diff --git a/components/MessageSelector.tsx b/components/MessageSelector.tsx new file mode 100644 index 0000000..7df5b56 --- /dev/null +++ b/components/MessageSelector.tsx @@ -0,0 +1,831 @@ +import { c as _c } from "react/compiler-runtime"; +import type { ContentBlockParam, TextBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'; +import { randomUUID, type UUID } from 'crypto'; +import figures from 'figures'; +import * as React from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { useAppState } from 'src/state/AppState.js'; +import { type DiffStats, fileHistoryCanRestore, fileHistoryEnabled, fileHistoryGetDiffStats } from 'src/utils/fileHistory.js'; +import { logError } from 'src/utils/log.js'; +import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { Box, Text } from '../ink.js'; +import { useKeybinding, useKeybindings } from '../keybindings/useKeybinding.js'; +import type { Message, PartialCompactDirection, UserMessage } from '../types/message.js'; +import { stripDisplayTags } from '../utils/displayTags.js'; +import { createUserMessage, extractTag, isEmptyMessageText, isSyntheticMessage, isToolUseResultMessage } from '../utils/messages.js'; +import { type OptionWithDescription, Select } from './CustomSelect/select.js'; +import { Spinner } from './Spinner.js'; +function isTextBlock(block: ContentBlockParam): block is TextBlockParam { + return block.type === 'text'; +} +import * as path from 'path'; +import { useTerminalSize } from 'src/hooks/useTerminalSize.js'; +import type { FileEditOutput } from 'src/tools/FileEditTool/types.js'; +import type { Output as FileWriteToolOutput } from 'src/tools/FileWriteTool/FileWriteTool.js'; +import { BASH_STDERR_TAG, BASH_STDOUT_TAG, COMMAND_MESSAGE_TAG, LOCAL_COMMAND_STDERR_TAG, LOCAL_COMMAND_STDOUT_TAG, TASK_NOTIFICATION_TAG, TEAMMATE_MESSAGE_TAG, TICK_TAG } from '../constants/xml.js'; +import { count } from '../utils/array.js'; +import { formatRelativeTimeAgo, truncate } from '../utils/format.js'; +import type { Theme } from '../utils/theme.js'; +import { Divider } from './design-system/Divider.js'; +type RestoreOption = 'both' | 'conversation' | 'code' | 'summarize' | 'summarize_up_to' | 'nevermind'; +function isSummarizeOption(option: RestoreOption | null): option is 'summarize' | 'summarize_up_to' { + return option === 'summarize' || option === 'summarize_up_to'; +} +type Props = { + messages: Message[]; + onPreRestore: () => void; + onRestoreMessage: (message: UserMessage) => Promise; + onRestoreCode: (message: UserMessage) => Promise; + onSummarize: (message: UserMessage, feedback?: string, direction?: PartialCompactDirection) => Promise; + onClose: () => void; + /** Skip pick-list, land on confirm. Caller ran skip-check first. Esc closes fully (no back-to-list). */ + preselectedMessage?: UserMessage; +}; +const MAX_VISIBLE_MESSAGES = 7; +export function MessageSelector({ + messages, + onPreRestore, + onRestoreMessage, + onRestoreCode, + onSummarize, + onClose, + preselectedMessage +}: Props): React.ReactNode { + const fileHistory = useAppState(s => s.fileHistory); + const [error, setError] = useState(undefined); + const isFileHistoryEnabled = fileHistoryEnabled(); + + // Add current prompt as a virtual message + const currentUUID = useMemo(randomUUID, []); + const messageOptions = useMemo(() => [...messages.filter(selectableUserMessagesFilter), { + ...createUserMessage({ + content: '' + }), + uuid: currentUUID + } as UserMessage], [messages, currentUUID]); + const [selectedIndex, setSelectedIndex] = useState(messageOptions.length - 1); + + // Orient the selected message as the middle of the visible options + const firstVisibleIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_MESSAGES / 2), messageOptions.length - MAX_VISIBLE_MESSAGES)); + const hasMessagesToSelect = messageOptions.length > 1; + const [messageToRestore, setMessageToRestore] = useState(preselectedMessage); + const [diffStatsForRestore, setDiffStatsForRestore] = useState(undefined); + useEffect(() => { + if (!preselectedMessage || !isFileHistoryEnabled) return; + let cancelled = false; + void fileHistoryGetDiffStats(fileHistory, preselectedMessage.uuid).then(stats => { + if (!cancelled) setDiffStatsForRestore(stats); + }); + return () => { + cancelled = true; + }; + }, [preselectedMessage, isFileHistoryEnabled, fileHistory]); + const [isRestoring, setIsRestoring] = useState(false); + const [restoringOption, setRestoringOption] = useState(null); + const [selectedRestoreOption, setSelectedRestoreOption] = useState('both'); + // Per-option feedback state; Select's internal inputValues Map persists + // per-option text independently, so sharing one variable would desync. + const [summarizeFromFeedback, setSummarizeFromFeedback] = useState(''); + const [summarizeUpToFeedback, setSummarizeUpToFeedback] = useState(''); + + // Generate options with summarize as input type for inline context + function getRestoreOptions(canRestoreCode: boolean): OptionWithDescription[] { + const baseOptions: OptionWithDescription[] = canRestoreCode ? [{ + value: 'both', + label: 'Restore code and conversation' + }, { + value: 'conversation', + label: 'Restore conversation' + }, { + value: 'code', + label: 'Restore code' + }] : [{ + value: 'conversation', + label: 'Restore conversation' + }]; + const summarizeInputProps = { + type: 'input' as const, + placeholder: 'add context (optional)', + initialValue: '', + allowEmptySubmitToCancel: true, + showLabelWithValue: true, + labelValueSeparator: ': ' + }; + baseOptions.push({ + value: 'summarize', + label: 'Summarize from here', + ...summarizeInputProps, + onChange: setSummarizeFromFeedback + }); + if ("external" === 'ant') { + baseOptions.push({ + value: 'summarize_up_to', + label: 'Summarize up to here', + ...summarizeInputProps, + onChange: setSummarizeUpToFeedback + }); + } + baseOptions.push({ + value: 'nevermind', + label: 'Never mind' + }); + return baseOptions; + } + + // Log when selector is opened + useEffect(() => { + logEvent('tengu_message_selector_opened', {}); + }, []); + + // Helper to restore conversation without confirmation + async function restoreConversationDirectly(message: UserMessage) { + onPreRestore(); + setIsRestoring(true); + try { + await onRestoreMessage(message); + setIsRestoring(false); + onClose(); + } catch (error_0) { + logError(error_0 as Error); + setIsRestoring(false); + setError(`Failed to restore the conversation:\n${error_0}`); + } + } + async function handleSelect(message_0: UserMessage) { + const index = messages.indexOf(message_0); + const indexFromEnd = messages.length - 1 - index; + logEvent('tengu_message_selector_selected', { + index_from_end: indexFromEnd, + message_type: message_0.type as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + is_current_prompt: false + }); + + // Do nothing if the message is not found + if (!messages.includes(message_0)) { + onClose(); + return; + } + if (!isFileHistoryEnabled) { + await restoreConversationDirectly(message_0); + return; + } + const diffStats = await fileHistoryGetDiffStats(fileHistory, message_0.uuid); + setMessageToRestore(message_0); + setDiffStatsForRestore(diffStats); + } + async function onSelectRestoreOption(option: RestoreOption) { + logEvent('tengu_message_selector_restore_option_selected', { + option: option as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + if (!messageToRestore) { + setError('Message not found.'); + return; + } + if (option === 'nevermind') { + if (preselectedMessage) onClose();else setMessageToRestore(undefined); + return; + } + if (isSummarizeOption(option)) { + onPreRestore(); + setIsRestoring(true); + setRestoringOption(option); + setError(undefined); + try { + const direction = option === 'summarize_up_to' ? 'up_to' : 'from'; + const feedback = (direction === 'up_to' ? summarizeUpToFeedback : summarizeFromFeedback).trim() || undefined; + await onSummarize(messageToRestore, feedback, direction); + setIsRestoring(false); + setRestoringOption(null); + setMessageToRestore(undefined); + onClose(); + } catch (error_1) { + logError(error_1 as Error); + setIsRestoring(false); + setRestoringOption(null); + setMessageToRestore(undefined); + setError(`Failed to summarize:\n${error_1}`); + } + return; + } + onPreRestore(); + setIsRestoring(true); + setError(undefined); + let codeError: Error | null = null; + let conversationError: Error | null = null; + if (option === 'code' || option === 'both') { + try { + await onRestoreCode(messageToRestore); + } catch (error_2) { + codeError = error_2 as Error; + logError(codeError); + } + } + if (option === 'conversation' || option === 'both') { + try { + await onRestoreMessage(messageToRestore); + } catch (error_3) { + conversationError = error_3 as Error; + logError(conversationError); + } + } + setIsRestoring(false); + setMessageToRestore(undefined); + + // Handle errors + if (conversationError && codeError) { + setError(`Failed to restore the conversation and code:\n${conversationError}\n${codeError}`); + } else if (conversationError) { + setError(`Failed to restore the conversation:\n${conversationError}`); + } else if (codeError) { + setError(`Failed to restore the code:\n${codeError}`); + } else { + // Success - close the selector + onClose(); + } + } + const exitState = useExitOnCtrlCDWithKeybindings(); + const handleEscape = useCallback(() => { + if (messageToRestore && !preselectedMessage) { + // Go back to message list instead of closing entirely + setMessageToRestore(undefined); + return; + } + logEvent('tengu_message_selector_cancelled', {}); + onClose(); + }, [onClose, messageToRestore, preselectedMessage]); + const moveUp = useCallback(() => setSelectedIndex(prev => Math.max(0, prev - 1)), []); + const moveDown = useCallback(() => setSelectedIndex(prev_0 => Math.min(messageOptions.length - 1, prev_0 + 1)), [messageOptions.length]); + const jumpToTop = useCallback(() => setSelectedIndex(0), []); + const jumpToBottom = useCallback(() => setSelectedIndex(messageOptions.length - 1), [messageOptions.length]); + const handleSelectCurrent = useCallback(() => { + const selected = messageOptions[selectedIndex]; + if (selected) { + void handleSelect(selected); + } + }, [messageOptions, selectedIndex, handleSelect]); + + // Escape to close - uses Confirmation context where escape is bound + useKeybinding('confirm:no', handleEscape, { + context: 'Confirmation', + isActive: !messageToRestore + }); + + // Message selector navigation keybindings + useKeybindings({ + 'messageSelector:up': moveUp, + 'messageSelector:down': moveDown, + 'messageSelector:top': jumpToTop, + 'messageSelector:bottom': jumpToBottom, + 'messageSelector:select': handleSelectCurrent + }, { + context: 'MessageSelector', + isActive: !isRestoring && !error && !messageToRestore && hasMessagesToSelect + }); + const [fileHistoryMetadata, setFileHistoryMetadata] = useState>({}); + useEffect(() => { + async function loadFileHistoryMetadata() { + if (!isFileHistoryEnabled) { + return; + } + // Load file snapshot metadata + void Promise.all(messageOptions.map(async (userMessage, itemIndex) => { + if (userMessage.uuid !== currentUUID) { + const canRestore = fileHistoryCanRestore(fileHistory, userMessage.uuid); + const nextUserMessage = messageOptions.at(itemIndex + 1); + const diffStats_0 = canRestore ? computeDiffStatsBetweenMessages(messages, userMessage.uuid, nextUserMessage?.uuid !== currentUUID ? nextUserMessage?.uuid : undefined) : undefined; + if (diffStats_0 !== undefined) { + setFileHistoryMetadata(prev_1 => ({ + ...prev_1, + [itemIndex]: diffStats_0 + })); + } else { + setFileHistoryMetadata(prev_2 => ({ + ...prev_2, + [itemIndex]: undefined + })); + } + } + })); + } + void loadFileHistoryMetadata(); + }, [messageOptions, messages, currentUUID, fileHistory, isFileHistoryEnabled]); + const canRestoreCode_0 = isFileHistoryEnabled && diffStatsForRestore?.filesChanged && diffStatsForRestore.filesChanged.length > 0; + const showPickList = !error && !messageToRestore && !preselectedMessage && hasMessagesToSelect; + return + + + + Rewind + + + {error && <> + Error: {error} + } + {!hasMessagesToSelect && <> + Nothing to rewind to yet. + } + {!error && messageToRestore && hasMessagesToSelect && <> + + Confirm you want to restore{' '} + {!diffStatsForRestore && 'the conversation '}to the point before + you sent this message: + + + + + ({formatRelativeTimeAgo(new Date(messageToRestore.timestamp))}) + + + + {isRestoring && isSummarizeOption(restoringOption) ? + + Summarizing… + : ; + $[49] = handleFocus; + $[50] = handleSelect; + $[51] = initialFocusValue; + $[52] = initialValue; + $[53] = selectOptions; + $[54] = t20; + $[55] = visibleCount; + $[56] = t21; + } else { + t21 = $[56]; + } + let t22; + if ($[57] !== hiddenCount) { + t22 = hiddenCount > 0 && and {hiddenCount} more…; + $[57] = hiddenCount; + $[58] = t22; + } else { + t22 = $[58]; + } + let t23; + if ($[59] !== t21 || $[60] !== t22) { + t23 = {t21}{t22}; + $[59] = t21; + $[60] = t22; + $[61] = t23; + } else { + t23 = $[61]; + } + let t24; + if ($[62] !== displayEffort || $[63] !== focusedDefaultEffort || $[64] !== focusedModelName || $[65] !== focusedSupportsEffort) { + t24 = {focusedSupportsEffort ? {" "}{capitalize(displayEffort)} effort{displayEffort === focusedDefaultEffort ? " (default)" : ""}{" "}← → to adjust : Effort not supported{focusedModelName ? ` for ${focusedModelName}` : ""}}; + $[62] = displayEffort; + $[63] = focusedDefaultEffort; + $[64] = focusedModelName; + $[65] = focusedSupportsEffort; + $[66] = t24; + } else { + t24 = $[66]; + } + let t25; + if ($[67] !== showFastModeNotice) { + t25 = isFastModeEnabled() ? showFastModeNotice ? Fast mode is ON and available with{" "}{FAST_MODE_MODEL_DISPLAY} only (/fast). Switching to other models turn off fast mode. : isFastModeAvailable() && !isFastModeCooldown() ? Use /fast to turn on Fast mode ({FAST_MODE_MODEL_DISPLAY} only). : null : null; + $[67] = showFastModeNotice; + $[68] = t25; + } else { + t25 = $[68]; + } + let t26; + if ($[69] !== t19 || $[70] !== t23 || $[71] !== t24 || $[72] !== t25) { + t26 = {t19}{t23}{t24}{t25}; + $[69] = t19; + $[70] = t23; + $[71] = t24; + $[72] = t25; + $[73] = t26; + } else { + t26 = $[73]; + } + let t27; + if ($[74] !== exitState || $[75] !== isStandaloneCommand) { + t27 = isStandaloneCommand && {exitState.pending ? <>Press {exitState.keyName} again to exit : }; + $[74] = exitState; + $[75] = isStandaloneCommand; + $[76] = t27; + } else { + t27 = $[76]; + } + let t28; + if ($[77] !== t26 || $[78] !== t27) { + t28 = {t26}{t27}; + $[77] = t26; + $[78] = t27; + $[79] = t28; + } else { + t28 = $[79]; + } + const content = t28; + if (!isStandaloneCommand) { + return content; + } + let t29; + if ($[80] !== content) { + t29 = {content}; + $[80] = content; + $[81] = t29; + } else { + t29 = $[81]; + } + return t29; +} +function _temp4() {} +function _temp3(opt_0) { + return { + ...opt_0, + value: opt_0.value === null ? NO_PREFERENCE : opt_0.value + }; +} +function _temp2(s_0) { + return s_0.effortValue; +} +function _temp(s) { + return isFastModeEnabled() ? s.fastMode : false; +} +function resolveOptionModel(value?: string): string | undefined { + if (!value) return undefined; + return value === NO_PREFERENCE ? getDefaultMainLoopModel() : parseUserSpecifiedModel(value); +} +function EffortLevelIndicator(t0) { + const $ = _c(5); + const { + effort + } = t0; + const t1 = effort ? "claude" : "subtle"; + const t2 = effort ?? "low"; + let t3; + if ($[0] !== t2) { + t3 = effortLevelToSymbol(t2); + $[0] = t2; + $[1] = t3; + } else { + t3 = $[1]; + } + let t4; + if ($[2] !== t1 || $[3] !== t3) { + t4 = {t3}; + $[2] = t1; + $[3] = t3; + $[4] = t4; + } else { + t4 = $[4]; + } + return t4; +} +function cycleEffortLevel(current: EffortLevel, direction: 'left' | 'right', includeMax: boolean): EffortLevel { + const levels: EffortLevel[] = includeMax ? ['low', 'medium', 'high', 'max'] : ['low', 'medium', 'high']; + // If the current level isn't in the cycle (e.g. 'max' after switching to a + // non-Opus model), clamp to 'high'. + const idx = levels.indexOf(current); + const currentIndex = idx !== -1 ? idx : levels.indexOf('high'); + if (direction === 'right') { + return levels[(currentIndex + 1) % levels.length]!; + } else { + return levels[(currentIndex - 1 + levels.length) % levels.length]!; + } +} +function getDefaultEffortLevelForOption(value?: string): EffortLevel { + const resolved = resolveOptionModel(value) ?? getDefaultMainLoopModel(); + const defaultValue = getDefaultEffortForModel(resolved); + return defaultValue !== undefined ? convertEffortValueToLevel(defaultValue) : 'high'; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjYXBpdGFsaXplIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZU1lbW8iLCJ1c2VTdGF0ZSIsInVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncyIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsIkZBU1RfTU9ERV9NT0RFTF9ESVNQTEFZIiwiaXNGYXN0TW9kZUF2YWlsYWJsZSIsImlzRmFzdE1vZGVDb29sZG93biIsImlzRmFzdE1vZGVFbmFibGVkIiwiQm94IiwiVGV4dCIsInVzZUtleWJpbmRpbmdzIiwidXNlQXBwU3RhdGUiLCJ1c2VTZXRBcHBTdGF0ZSIsImNvbnZlcnRFZmZvcnRWYWx1ZVRvTGV2ZWwiLCJFZmZvcnRMZXZlbCIsImdldERlZmF1bHRFZmZvcnRGb3JNb2RlbCIsIm1vZGVsU3VwcG9ydHNFZmZvcnQiLCJtb2RlbFN1cHBvcnRzTWF4RWZmb3J0IiwicmVzb2x2ZVBpY2tlckVmZm9ydFBlcnNpc3RlbmNlIiwidG9QZXJzaXN0YWJsZUVmZm9ydCIsImdldERlZmF1bHRNYWluTG9vcE1vZGVsIiwiTW9kZWxTZXR0aW5nIiwibW9kZWxEaXNwbGF5U3RyaW5nIiwicGFyc2VVc2VyU3BlY2lmaWVkTW9kZWwiLCJnZXRNb2RlbE9wdGlvbnMiLCJnZXRTZXR0aW5nc0ZvclNvdXJjZSIsInVwZGF0ZVNldHRpbmdzRm9yU291cmNlIiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiU2VsZWN0IiwiQnlsaW5lIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJQYW5lIiwiZWZmb3J0TGV2ZWxUb1N5bWJvbCIsIlByb3BzIiwiaW5pdGlhbCIsInNlc3Npb25Nb2RlbCIsIm9uU2VsZWN0IiwibW9kZWwiLCJlZmZvcnQiLCJvbkNhbmNlbCIsImlzU3RhbmRhbG9uZUNvbW1hbmQiLCJzaG93RmFzdE1vZGVOb3RpY2UiLCJoZWFkZXJUZXh0Iiwic2tpcFNldHRpbmdzV3JpdGUiLCJOT19QUkVGRVJFTkNFIiwiTW9kZWxQaWNrZXIiLCJ0MCIsIiQiLCJfYyIsInNldEFwcFN0YXRlIiwiZXhpdFN0YXRlIiwiaW5pdGlhbFZhbHVlIiwiZm9jdXNlZFZhbHVlIiwic2V0Rm9jdXNlZFZhbHVlIiwiaXNGYXN0TW9kZSIsIl90ZW1wIiwiaGFzVG9nZ2xlZEVmZm9ydCIsInNldEhhc1RvZ2dsZWRFZmZvcnQiLCJlZmZvcnRWYWx1ZSIsIl90ZW1wMiIsInQxIiwidW5kZWZpbmVkIiwic2V0RWZmb3J0IiwidDIiLCJ0MyIsIm1vZGVsT3B0aW9ucyIsInQ0IiwiYmIwIiwic29tZSIsIm9wdCIsInZhbHVlIiwidDUiLCJ0NiIsImxhYmVsIiwiZGVzY3JpcHRpb24iLCJ0NyIsIm9wdGlvbnNXaXRoSW5pdGlhbCIsIm1hcCIsIl90ZW1wMyIsInNlbGVjdE9wdGlvbnMiLCJfIiwiaW5pdGlhbEZvY3VzVmFsdWUiLCJ2aXNpYmxlQ291bnQiLCJNYXRoIiwibWluIiwibGVuZ3RoIiwiaGlkZGVuQ291bnQiLCJtYXgiLCJmaW5kIiwib3B0XzEiLCJmb2N1c2VkTW9kZWxOYW1lIiwiZm9jdXNlZFN1cHBvcnRzRWZmb3J0IiwidDgiLCJmb2N1c2VkTW9kZWwiLCJyZXNvbHZlT3B0aW9uTW9kZWwiLCJmb2N1c2VkU3VwcG9ydHNNYXgiLCJ0OSIsImdldERlZmF1bHRFZmZvcnRMZXZlbEZvck9wdGlvbiIsImZvY3VzZWREZWZhdWx0RWZmb3J0IiwiZGlzcGxheUVmZm9ydCIsInQxMCIsImhhbmRsZUZvY3VzIiwidDExIiwiZGlyZWN0aW9uIiwicHJldiIsImN5Y2xlRWZmb3J0TGV2ZWwiLCJoYW5kbGVDeWNsZUVmZm9ydCIsInQxMiIsIm1vZGVsUGlja2VyOmRlY3JlYXNlRWZmb3J0IiwibW9kZWxQaWNrZXI6aW5jcmVhc2VFZmZvcnQiLCJ0MTMiLCJTeW1ib2wiLCJmb3IiLCJjb250ZXh0IiwidDE0IiwiaGFuZGxlU2VsZWN0IiwidmFsdWVfMCIsImVmZm9ydExldmVsIiwicGVyc2lzdGFibGUiLCJwcmV2XzAiLCJzZWxlY3RlZE1vZGVsIiwic2VsZWN0ZWRFZmZvcnQiLCJ0MTUiLCJ0MTYiLCJ0MTciLCJ0MTgiLCJ0MTkiLCJ0MjAiLCJfdGVtcDQiLCJ0MjEiLCJ0MjIiLCJ0MjMiLCJ0MjQiLCJ0MjUiLCJ0MjYiLCJ0MjciLCJwZW5kaW5nIiwia2V5TmFtZSIsInQyOCIsImNvbnRlbnQiLCJ0MjkiLCJvcHRfMCIsInNfMCIsInMiLCJmYXN0TW9kZSIsIkVmZm9ydExldmVsSW5kaWNhdG9yIiwiY3VycmVudCIsImluY2x1ZGVNYXgiLCJsZXZlbHMiLCJpZHgiLCJpbmRleE9mIiwiY3VycmVudEluZGV4IiwicmVzb2x2ZWQiLCJkZWZhdWx0VmFsdWUiXSwic291cmNlcyI6WyJNb2RlbFBpY2tlci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNhcGl0YWxpemUgZnJvbSAnbG9kYXNoLWVzL2NhcGl0YWxpemUuanMnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUNhbGxiYWNrLCB1c2VNZW1vLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlRXhpdE9uQ3RybENEV2l0aEtleWJpbmRpbmdzIH0gZnJvbSAnc3JjL2hvb2tzL3VzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncy5qcydcbmltcG9ydCB7XG4gIHR5cGUgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgbG9nRXZlbnQsXG59IGZyb20gJ3NyYy9zZXJ2aWNlcy9hbmFseXRpY3MvaW5kZXguanMnXG5pbXBvcnQge1xuICBGQVNUX01PREVfTU9ERUxfRElTUExBWSxcbiAgaXNGYXN0TW9kZUF2YWlsYWJsZSxcbiAgaXNGYXN0TW9kZUNvb2xkb3duLFxuICBpc0Zhc3RNb2RlRW5hYmxlZCxcbn0gZnJvbSAnc3JjL3V0aWxzL2Zhc3RNb2RlLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZ3MgfSBmcm9tICcuLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUsIHVzZVNldEFwcFN0YXRlIH0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQge1xuICBjb252ZXJ0RWZmb3J0VmFsdWVUb0xldmVsLFxuICB0eXBlIEVmZm9ydExldmVsLFxuICBnZXREZWZhdWx0RWZmb3J0Rm9yTW9kZWwsXG4gIG1vZGVsU3VwcG9ydHNFZmZvcnQsXG4gIG1vZGVsU3VwcG9ydHNNYXhFZmZvcnQsXG4gIHJlc29sdmVQaWNrZXJFZmZvcnRQZXJzaXN0ZW5jZSxcbiAgdG9QZXJzaXN0YWJsZUVmZm9ydCxcbn0gZnJvbSAnLi4vdXRpbHMvZWZmb3J0LmpzJ1xuaW1wb3J0IHtcbiAgZ2V0RGVmYXVsdE1haW5Mb29wTW9kZWwsXG4gIHR5cGUgTW9kZWxTZXR0aW5nLFxuICBtb2RlbERpc3BsYXlTdHJpbmcsXG4gIHBhcnNlVXNlclNwZWNpZmllZE1vZGVsLFxufSBmcm9tICcuLi91dGlscy9tb2RlbC9tb2RlbC5qcydcbmltcG9ydCB7IGdldE1vZGVsT3B0aW9ucyB9IGZyb20gJy4uL3V0aWxzL21vZGVsL21vZGVsT3B0aW9ucy5qcydcbmltcG9ydCB7XG4gIGdldFNldHRpbmdzRm9yU291cmNlLFxuICB1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSxcbn0gZnJvbSAnLi4vdXRpbHMvc2V0dGluZ3Mvc2V0dGluZ3MuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuL0NvbmZpZ3VyYWJsZVNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L2luZGV4LmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgUGFuZSB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9QYW5lLmpzJ1xuaW1wb3J0IHsgZWZmb3J0TGV2ZWxUb1N5bWJvbCB9IGZyb20gJy4vRWZmb3J0SW5kaWNhdG9yLmpzJ1xuXG5leHBvcnQgdHlwZSBQcm9wcyA9IHtcbiAgaW5pdGlhbDogc3RyaW5nIHwgbnVsbFxuICBzZXNzaW9uTW9kZWw/OiBNb2RlbFNldHRpbmdcbiAgb25TZWxlY3Q6IChtb2RlbDogc3RyaW5nIHwgbnVsbCwgZWZmb3J0OiBFZmZvcnRMZXZlbCB8IHVuZGVmaW5lZCkgPT4gdm9pZFxuICBvbkNhbmNlbD86ICgpID0+IHZvaWRcbiAgaXNTdGFuZGFsb25lQ29tbWFuZD86IGJvb2xlYW5cbiAgc2hvd0Zhc3RNb2RlTm90aWNlPzogYm9vbGVhblxuICAvKiogT3ZlcnJpZGVzIHRoZSBkaW0gaGVhZGVyIGxpbmUgYmVsb3cgXCJTZWxlY3QgbW9kZWxcIi4gKi9cbiAgaGVhZGVyVGV4dD86IHN0cmluZ1xuICAvKipcbiAgICogV2hlbiB0cnVlLCBza2lwIHdyaXRpbmcgZWZmb3J0TGV2ZWwgdG8gdXNlclNldHRpbmdzIG9uIHNlbGVjdGlvbi5cbiAgICogVXNlZCBieSB0aGUgYXNzaXN0YW50IGluc3RhbGxlciB3aXphcmQgd2hlcmUgdGhlIG1vZGVsIGNob2ljZSBpc1xuICAgKiBwcm9qZWN0LXNjb3BlZCAod3JpdHRlbiB0byB0aGUgYXNzaXN0YW50J3MgLmNsYXVkZS9zZXR0aW5ncy5qc29uIHZpYVxuICAgKiBpbnN0YWxsLnRzKSBhbmQgc2hvdWxkIG5vdCBsZWFrIHRvIHRoZSB1c2VyJ3MgZ2xvYmFsIH4vLmNsYXVkZS9zZXR0aW5ncy5cbiAgICovXG4gIHNraXBTZXR0aW5nc1dyaXRlPzogYm9vbGVhblxufVxuXG5jb25zdCBOT19QUkVGRVJFTkNFID0gJ19fTk9fUFJFRkVSRU5DRV9fJ1xuXG5leHBvcnQgZnVuY3Rpb24gTW9kZWxQaWNrZXIoe1xuICBpbml0aWFsLFxuICBzZXNzaW9uTW9kZWwsXG4gIG9uU2VsZWN0LFxuICBvbkNhbmNlbCxcbiAgaXNTdGFuZGFsb25lQ29tbWFuZCxcbiAgc2hvd0Zhc3RNb2RlTm90aWNlLFxuICBoZWFkZXJUZXh0LFxuICBza2lwU2V0dGluZ3NXcml0ZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IGV4aXRTdGF0ZSA9IHVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncygpXG4gIGNvbnN0IG1heFZpc2libGUgPSAxMFxuXG4gIGNvbnN0IGluaXRpYWxWYWx1ZSA9IGluaXRpYWwgPT09IG51bGwgPyBOT19QUkVGRVJFTkNFIDogaW5pdGlhbFxuICBjb25zdCBbZm9jdXNlZFZhbHVlLCBzZXRGb2N1c2VkVmFsdWVdID0gdXNlU3RhdGU8c3RyaW5nIHwgdW5kZWZpbmVkPihcbiAgICBpbml0aWFsVmFsdWUsXG4gIClcblxuICBjb25zdCBpc0Zhc3RNb2RlID0gdXNlQXBwU3RhdGUocyA9PlxuICAgIGlzRmFzdE1vZGVFbmFibGVkKCkgPyBzLmZhc3RNb2RlIDogZmFsc2UsXG4gIClcblxuICBjb25zdCBbaGFzVG9nZ2xlZEVmZm9ydCwgc2V0SGFzVG9nZ2xlZEVmZm9ydF0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgZWZmb3J0VmFsdWUgPSB1c2VBcHBTdGF0ZShzID0+IHMuZWZmb3J0VmFsdWUpXG4gIGNvbnN0IFtlZmZvcnQsIHNldEVmZm9ydF0gPSB1c2VTdGF0ZTxFZmZvcnRMZXZlbCB8IHVuZGVmaW5lZD4oXG4gICAgZWZmb3J0VmFsdWUgIT09IHVuZGVmaW5lZFxuICAgICAgPyBjb252ZXJ0RWZmb3J0VmFsdWVUb0xldmVsKGVmZm9ydFZhbHVlKVxuICAgICAgOiB1bmRlZmluZWQsXG4gIClcblxuICAvLyBNZW1vaXplIGFsbCBkZXJpdmVkIHZhbHVlcyB0byBwcmV2ZW50IHJlLXJlbmRlcnNcbiAgY29uc3QgbW9kZWxPcHRpb25zID0gdXNlTWVtbyhcbiAgICAoKSA9PiBnZXRNb2RlbE9wdGlvbnMoaXNGYXN0TW9kZSA/PyBmYWxzZSksXG4gICAgW2lzRmFzdE1vZGVdLFxuICApXG5cbiAgLy8gRW5zdXJlIHRoZSBpbml0aWFsIHZhbHVlIGlzIGluIHRoZSBvcHRpb25zIGxpc3RcbiAgLy8gVGhpcyBoYW5kbGVzIGVkZ2UgY2FzZXMgd2hlcmUgdGhlIHVzZXIncyBjdXJyZW50IG1vZGVsIChlLmcuLCAnaGFpa3UnIGZvciAzUCB1c2VycylcbiAgLy8gaXMgbm90IGluIHRoZSBiYXNlIG9wdGlvbnMgYnV0IHNob3VsZCBzdGlsbCBiZSBzZWxlY3RhYmxlIGFuZCBzaG93biBhcyBzZWxlY3RlZFxuICBjb25zdCBvcHRpb25zV2l0aEluaXRpYWwgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBpZiAoaW5pdGlhbCAhPT0gbnVsbCAmJiAhbW9kZWxPcHRpb25zLnNvbWUob3B0ID0+IG9wdC52YWx1ZSA9PT0gaW5pdGlhbCkpIHtcbiAgICAgIHJldHVybiBbXG4gICAgICAgIC4uLm1vZGVsT3B0aW9ucyxcbiAgICAgICAge1xuICAgICAgICAgIHZhbHVlOiBpbml0aWFsLFxuICAgICAgICAgIGxhYmVsOiBtb2RlbERpc3BsYXlTdHJpbmcoaW5pdGlhbCksXG4gICAgICAgICAgZGVzY3JpcHRpb246ICdDdXJyZW50IG1vZGVsJyxcbiAgICAgICAgfSxcbiAgICAgIF1cbiAgICB9XG4gICAgcmV0dXJuIG1vZGVsT3B0aW9uc1xuICB9LCBbbW9kZWxPcHRpb25zLCBpbml0aWFsXSlcblxuICBjb25zdCBzZWxlY3RPcHRpb25zID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgb3B0aW9uc1dpdGhJbml0aWFsLm1hcChvcHQgPT4gKHtcbiAgICAgICAgLi4ub3B0LFxuICAgICAgICB2YWx1ZTogb3B0LnZhbHVlID09PSBudWxsID8gTk9fUFJFRkVSRU5DRSA6IG9wdC52YWx1ZSxcbiAgICAgIH0pKSxcbiAgICBbb3B0aW9uc1dpdGhJbml0aWFsXSxcbiAgKVxuICBjb25zdCBpbml0aWFsRm9jdXNWYWx1ZSA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIHNlbGVjdE9wdGlvbnMuc29tZShfID0+IF8udmFsdWUgPT09IGluaXRpYWxWYWx1ZSlcbiAgICAgICAgPyBpbml0aWFsVmFsdWVcbiAgICAgICAgOiAoc2VsZWN0T3B0aW9uc1swXT8udmFsdWUgPz8gdW5kZWZpbmVkKSxcbiAgICBbc2VsZWN0T3B0aW9ucywgaW5pdGlhbFZhbHVlXSxcbiAgKVxuICBjb25zdCB2aXNpYmxlQ291bnQgPSBNYXRoLm1pbihtYXhWaXNpYmxlLCBzZWxlY3RPcHRpb25zLmxlbmd0aClcbiAgY29uc3QgaGlkZGVuQ291bnQgPSBNYXRoLm1heCgwLCBzZWxlY3RPcHRpb25zLmxlbmd0aCAtIHZpc2libGVDb3VudClcblxuICBjb25zdCBmb2N1c2VkTW9kZWxOYW1lID0gc2VsZWN0T3B0aW9ucy5maW5kKFxuICAgIG9wdCA9PiBvcHQudmFsdWUgPT09IGZvY3VzZWRWYWx1ZSxcbiAgKT8ubGFiZWxcbiAgY29uc3QgZm9jdXNlZE1vZGVsID0gcmVzb2x2ZU9wdGlvbk1vZGVsKGZvY3VzZWRWYWx1ZSlcbiAgY29uc3QgZm9jdXNlZFN1cHBvcnRzRWZmb3J0ID0gZm9jdXNlZE1vZGVsXG4gICAgPyBtb2RlbFN1cHBvcnRzRWZmb3J0KGZvY3VzZWRNb2RlbClcbiAgICA6IGZhbHNlXG4gIGNvbnN0IGZvY3VzZWRTdXBwb3J0c01heCA9IGZvY3VzZWRNb2RlbFxuICAgID8gbW9kZWxTdXBwb3J0c01heEVmZm9ydChmb2N1c2VkTW9kZWwpXG4gICAgOiBmYWxzZVxuICBjb25zdCBmb2N1c2VkRGVmYXVsdEVmZm9ydCA9IGdldERlZmF1bHRFZmZvcnRMZXZlbEZvck9wdGlvbihmb2N1c2VkVmFsdWUpXG4gIC8vIENsYW1wIGRpc3BsYXkgd2hlbiAnbWF4JyBpcyBzZWxlY3RlZCBidXQgdGhlIGZvY3VzZWQgbW9kZWwgZG9lc24ndCBzdXBwb3J0IGl0LlxuICAvLyByZXNvbHZlQXBwbGllZEVmZm9ydCgpIGRvZXMgdGhlIHNhbWUgZG93bmdyYWRlIGF0IEFQSS1zZW5kIHRpbWUuXG4gIGNvbnN0IGRpc3BsYXlFZmZvcnQgPVxuICAgIGVmZm9ydCA9PT0gJ21heCcgJiYgIWZvY3VzZWRTdXBwb3J0c01heCA/ICdoaWdoJyA6IGVmZm9ydFxuXG4gIGNvbnN0IGhhbmRsZUZvY3VzID0gdXNlQ2FsbGJhY2soXG4gICAgKHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICAgIHNldEZvY3VzZWRWYWx1ZSh2YWx1ZSlcbiAgICAgIGlmICghaGFzVG9nZ2xlZEVmZm9ydCAmJiBlZmZvcnRWYWx1ZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHNldEVmZm9ydChnZXREZWZhdWx0RWZmb3J0TGV2ZWxGb3JPcHRpb24odmFsdWUpKVxuICAgICAgfVxuICAgIH0sXG4gICAgW2hhc1RvZ2dsZWRFZmZvcnQsIGVmZm9ydFZhbHVlXSxcbiAgKVxuXG4gIC8vIEVmZm9ydCBsZXZlbCBjeWNsaW5nIGtleWJpbmRpbmdzXG4gIGNvbnN0IGhhbmRsZUN5Y2xlRWZmb3J0ID0gdXNlQ2FsbGJhY2soXG4gICAgKGRpcmVjdGlvbjogJ2xlZnQnIHwgJ3JpZ2h0JykgPT4ge1xuICAgICAgaWYgKCFmb2N1c2VkU3VwcG9ydHNFZmZvcnQpIHJldHVyblxuICAgICAgc2V0RWZmb3J0KHByZXYgPT5cbiAgICAgICAgY3ljbGVFZmZvcnRMZXZlbChcbiAgICAgICAgICBwcmV2ID8/IGZvY3VzZWREZWZhdWx0RWZmb3J0LFxuICAgICAgICAgIGRpcmVjdGlvbixcbiAgICAgICAgICBmb2N1c2VkU3VwcG9ydHNNYXgsXG4gICAgICAgICksXG4gICAgICApXG4gICAgICBzZXRIYXNUb2dnbGVkRWZmb3J0KHRydWUpXG4gICAgfSxcbiAgICBbZm9jdXNlZFN1cHBvcnRzRWZmb3J0LCBmb2N1c2VkU3VwcG9ydHNNYXgsIGZvY3VzZWREZWZhdWx0RWZmb3J0XSxcbiAgKVxuXG4gIHVzZUtleWJpbmRpbmdzKFxuICAgIHtcbiAgICAgICdtb2RlbFBpY2tlcjpkZWNyZWFzZUVmZm9ydCc6ICgpID0+IGhhbmRsZUN5Y2xlRWZmb3J0KCdsZWZ0JyksXG4gICAgICAnbW9kZWxQaWNrZXI6aW5jcmVhc2VFZmZvcnQnOiAoKSA9PiBoYW5kbGVDeWNsZUVmZm9ydCgncmlnaHQnKSxcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ01vZGVsUGlja2VyJyB9LFxuICApXG5cbiAgZnVuY3Rpb24gaGFuZGxlU2VsZWN0KHZhbHVlOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBsb2dFdmVudCgndGVuZ3VfbW9kZWxfY29tbWFuZF9tZW51X2VmZm9ydCcsIHtcbiAgICAgIGVmZm9ydDpcbiAgICAgICAgZWZmb3J0IGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgfSlcbiAgICBpZiAoIXNraXBTZXR0aW5nc1dyaXRlKSB7XG4gICAgICAvLyBQcmlvciBjb21lcyBmcm9tIHVzZXJTZXR0aW5ncyBvbiBkaXNrIOKAlCBOT1QgbWVyZ2VkIHNldHRpbmdzICh3aGljaFxuICAgICAgLy8gaW5jbHVkZXMgcHJvamVjdC9wb2xpY3kgbGF5ZXJzIHRoYXQgbXVzdCBub3QgbGVhayBpbnRvIHRoZSB1c2VyJ3NcbiAgICAgIC8vIGdsb2JhbCB+Ly5jbGF1ZGUvc2V0dGluZ3MuanNvbiksIGFuZCBOT1QgQXBwU3RhdGUuZWZmb3J0VmFsdWUgKHdoaWNoXG4gICAgICAvLyBpbmNsdWRlcyBzZXNzaW9uLWVwaGVtZXJhbCBzb3VyY2VzIGxpa2UgLS1lZmZvcnQgQ0xJIGZsYWcpLlxuICAgICAgLy8gU2VlIHJlc29sdmVQaWNrZXJFZmZvcnRQZXJzaXN0ZW5jZSBKU0RvYy5cbiAgICAgIGNvbnN0IGVmZm9ydExldmVsID0gcmVzb2x2ZVBpY2tlckVmZm9ydFBlcnNpc3RlbmNlKFxuICAgICAgICBlZmZvcnQsXG4gICAgICAgIGdldERlZmF1bHRFZmZvcnRMZXZlbEZvck9wdGlvbih2YWx1ZSksXG4gICAgICAgIGdldFNldHRpbmdzRm9yU291cmNlKCd1c2VyU2V0dGluZ3MnKT8uZWZmb3J0TGV2ZWwsXG4gICAgICAgIGhhc1RvZ2dsZWRFZmZvcnQsXG4gICAgICApXG4gICAgICBjb25zdCBwZXJzaXN0YWJsZSA9IHRvUGVyc2lzdGFibGVFZmZvcnQoZWZmb3J0TGV2ZWwpXG4gICAgICBpZiAocGVyc2lzdGFibGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICB1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSgndXNlclNldHRpbmdzJywgeyBlZmZvcnRMZXZlbDogcGVyc2lzdGFibGUgfSlcbiAgICAgIH1cbiAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4gKHsgLi4ucHJldiwgZWZmb3J0VmFsdWU6IGVmZm9ydExldmVsIH0pKVxuICAgIH1cblxuICAgIGNvbnN0IHNlbGVjdGVkTW9kZWwgPSByZXNvbHZlT3B0aW9uTW9kZWwodmFsdWUpXG4gICAgY29uc3Qgc2VsZWN0ZWRFZmZvcnQgPVxuICAgICAgaGFzVG9nZ2xlZEVmZm9ydCAmJiBzZWxlY3RlZE1vZGVsICYmIG1vZGVsU3VwcG9ydHNFZmZvcnQoc2VsZWN0ZWRNb2RlbClcbiAgICAgICAgPyBlZmZvcnRcbiAgICAgICAgOiB1bmRlZmluZWRcbiAgICBpZiAodmFsdWUgPT09IE5PX1BSRUZFUkVOQ0UpIHtcbiAgICAgIG9uU2VsZWN0KG51bGwsIHNlbGVjdGVkRWZmb3J0KVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIG9uU2VsZWN0KHZhbHVlLCBzZWxlY3RlZEVmZm9ydClcbiAgfVxuXG4gIGNvbnN0IGNvbnRlbnQgPSAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cInJlbWVtYmVyXCIgYm9sZD5cbiAgICAgICAgICAgIFNlbGVjdCBtb2RlbFxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHtoZWFkZXJUZXh0ID8/XG4gICAgICAgICAgICAgICdTd2l0Y2ggYmV0d2VlbiBDbGF1ZGUgbW9kZWxzLiBBcHBsaWVzIHRvIHRoaXMgc2Vzc2lvbiBhbmQgZnV0dXJlIENsYXVkZSBDb2RlIHNlc3Npb25zLiBGb3Igb3RoZXIvcHJldmlvdXMgbW9kZWwgbmFtZXMsIHNwZWNpZnkgd2l0aCAtLW1vZGVsLid9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIHtzZXNzaW9uTW9kZWwgJiYgKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIEN1cnJlbnRseSB1c2luZyB7bW9kZWxEaXNwbGF5U3RyaW5nKHNlc3Npb25Nb2RlbCl9IGZvciB0aGlzXG4gICAgICAgICAgICAgIHNlc3Npb24gKHNldCBieSBwbGFuIG1vZGUpLiBTZWxlY3RpbmcgYSBtb2RlbCB3aWxsIHVuZG8gdGhpcy5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cblxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU9e2luaXRpYWxWYWx1ZX1cbiAgICAgICAgICAgICAgZGVmYXVsdEZvY3VzVmFsdWU9e2luaXRpYWxGb2N1c1ZhbHVlfVxuICAgICAgICAgICAgICBvcHRpb25zPXtzZWxlY3RPcHRpb25zfVxuICAgICAgICAgICAgICBvbkNoYW5nZT17aGFuZGxlU2VsZWN0fVxuICAgICAgICAgICAgICBvbkZvY3VzPXtoYW5kbGVGb2N1c31cbiAgICAgICAgICAgICAgb25DYW5jZWw9e29uQ2FuY2VsID8/ICgoKSA9PiB7fSl9XG4gICAgICAgICAgICAgIHZpc2libGVPcHRpb25Db3VudD17dmlzaWJsZUNvdW50fVxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgICB7aGlkZGVuQ291bnQgPiAwICYmIChcbiAgICAgICAgICAgIDxCb3ggcGFkZGluZ0xlZnQ9ezN9PlxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5hbmQge2hpZGRlbkNvdW50fSBtb3Jl4oCmPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICB7Zm9jdXNlZFN1cHBvcnRzRWZmb3J0ID8gKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIDxFZmZvcnRMZXZlbEluZGljYXRvciBlZmZvcnQ9e2Rpc3BsYXlFZmZvcnR9IC8+eycgJ31cbiAgICAgICAgICAgICAge2NhcGl0YWxpemUoZGlzcGxheUVmZm9ydCl9IGVmZm9ydFxuICAgICAgICAgICAgICB7ZGlzcGxheUVmZm9ydCA9PT0gZm9jdXNlZERlZmF1bHRFZmZvcnQgPyBgIChkZWZhdWx0KWAgOiBgYH17JyAnfVxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1YnRsZVwiPuKGkCDihpIgdG8gYWRqdXN0PC9UZXh0PlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1YnRsZVwiPlxuICAgICAgICAgICAgICA8RWZmb3J0TGV2ZWxJbmRpY2F0b3IgZWZmb3J0PXt1bmRlZmluZWR9IC8+IEVmZm9ydCBub3Qgc3VwcG9ydGVkXG4gICAgICAgICAgICAgIHtmb2N1c2VkTW9kZWxOYW1lID8gYCBmb3IgJHtmb2N1c2VkTW9kZWxOYW1lfWAgOiAnJ31cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cblxuICAgICAgICB7aXNGYXN0TW9kZUVuYWJsZWQoKSA/IChcbiAgICAgICAgICBzaG93RmFzdE1vZGVOb3RpY2UgPyAoXG4gICAgICAgICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgIEZhc3QgbW9kZSBpcyA8VGV4dCBib2xkPk9OPC9UZXh0PiBhbmQgYXZhaWxhYmxlIHdpdGh7JyAnfVxuICAgICAgICAgICAgICAgIHtGQVNUX01PREVfTU9ERUxfRElTUExBWX0gb25seSAoL2Zhc3QpLiBTd2l0Y2hpbmcgdG8gb3RoZXJcbiAgICAgICAgICAgICAgICBtb2RlbHMgdHVybiBvZmYgZmFzdCBtb2RlLlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICApIDogaXNGYXN0TW9kZUF2YWlsYWJsZSgpICYmICFpc0Zhc3RNb2RlQ29vbGRvd24oKSA/IChcbiAgICAgICAgICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgVXNlIDxUZXh0IGJvbGQ+L2Zhc3Q8L1RleHQ+IHRvIHR1cm4gb24gRmFzdCBtb2RlIChcbiAgICAgICAgICAgICAgICB7RkFTVF9NT0RFX01PREVMX0RJU1BMQVl9IG9ubHkpLlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICApIDogbnVsbFxuICAgICAgICApIDogbnVsbH1cbiAgICAgIDwvQm94PlxuXG4gICAgICB7aXNTdGFuZGFsb25lQ29tbWFuZCAmJiAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yIGl0YWxpYz5cbiAgICAgICAgICB7ZXhpdFN0YXRlLnBlbmRpbmcgPyAoXG4gICAgICAgICAgICA8PlByZXNzIHtleGl0U3RhdGUua2V5TmFtZX0gYWdhaW4gdG8gZXhpdDwvPlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICA8QnlsaW5lPlxuICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cImNvbmZpcm1cIiAvPlxuICAgICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgICAgYWN0aW9uPVwic2VsZWN0OmNhbmNlbFwiXG4gICAgICAgICAgICAgICAgY29udGV4dD1cIlNlbGVjdFwiXG4gICAgICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwiZXhpdFwiXG4gICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgICApfVxuICAgICAgICA8L1RleHQ+XG4gICAgICApfVxuICAgIDwvQm94PlxuICApXG5cbiAgaWYgKCFpc1N0YW5kYWxvbmVDb21tYW5kKSB7XG4gICAgcmV0dXJuIGNvbnRlbnRcbiAgfVxuXG4gIHJldHVybiA8UGFuZSBjb2xvcj1cInBlcm1pc3Npb25cIj57Y29udGVudH08L1BhbmU+XG59XG5cbmZ1bmN0aW9uIHJlc29sdmVPcHRpb25Nb2RlbCh2YWx1ZT86IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIGlmICghdmFsdWUpIHJldHVybiB1bmRlZmluZWRcbiAgcmV0dXJuIHZhbHVlID09PSBOT19QUkVGRVJFTkNFXG4gICAgPyBnZXREZWZhdWx0TWFpbkxvb3BNb2RlbCgpXG4gICAgOiBwYXJzZVVzZXJTcGVjaWZpZWRNb2RlbCh2YWx1ZSlcbn1cblxuZnVuY3Rpb24gRWZmb3J0TGV2ZWxJbmRpY2F0b3Ioe1xuICBlZmZvcnQsXG59OiB7XG4gIGVmZm9ydD86IEVmZm9ydExldmVsXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8VGV4dCBjb2xvcj17ZWZmb3J0ID8gJ2NsYXVkZScgOiAnc3VidGxlJ30+XG4gICAgICB7ZWZmb3J0TGV2ZWxUb1N5bWJvbChlZmZvcnQgPz8gJ2xvdycpfVxuICAgIDwvVGV4dD5cbiAgKVxufVxuXG5mdW5jdGlvbiBjeWNsZUVmZm9ydExldmVsKFxuICBjdXJyZW50OiBFZmZvcnRMZXZlbCxcbiAgZGlyZWN0aW9uOiAnbGVmdCcgfCAncmlnaHQnLFxuICBpbmNsdWRlTWF4OiBib29sZWFuLFxuKTogRWZmb3J0TGV2ZWwge1xuICBjb25zdCBsZXZlbHM6IEVmZm9ydExldmVsW10gPSBpbmNsdWRlTWF4XG4gICAgPyBbJ2xvdycsICdtZWRpdW0nLCAnaGlnaCcsICdtYXgnXVxuICAgIDogWydsb3cnLCAnbWVkaXVtJywgJ2hpZ2gnXVxuICAvLyBJZiB0aGUgY3VycmVudCBsZXZlbCBpc24ndCBpbiB0aGUgY3ljbGUgKGUuZy4gJ21heCcgYWZ0ZXIgc3dpdGNoaW5nIHRvIGFcbiAgLy8gbm9uLU9wdXMgbW9kZWwpLCBjbGFtcCB0byAnaGlnaCcuXG4gIGNvbnN0IGlkeCA9IGxldmVscy5pbmRleE9mKGN1cnJlbnQpXG4gIGNvbnN0IGN1cnJlbnRJbmRleCA9IGlkeCAhPT0gLTEgPyBpZHggOiBsZXZlbHMuaW5kZXhPZignaGlnaCcpXG4gIGlmIChkaXJlY3Rpb24gPT09ICdyaWdodCcpIHtcbiAgICByZXR1cm4gbGV2ZWxzWyhjdXJyZW50SW5kZXggKyAxKSAlIGxldmVscy5sZW5ndGhdIVxuICB9IGVsc2Uge1xuICAgIHJldHVybiBsZXZlbHNbKGN1cnJlbnRJbmRleCAtIDEgKyBsZXZlbHMubGVuZ3RoKSAlIGxldmVscy5sZW5ndGhdIVxuICB9XG59XG5cbmZ1bmN0aW9uIGdldERlZmF1bHRFZmZvcnRMZXZlbEZvck9wdGlvbih2YWx1ZT86IHN0cmluZyk6IEVmZm9ydExldmVsIHtcbiAgY29uc3QgcmVzb2x2ZWQgPSByZXNvbHZlT3B0aW9uTW9kZWwodmFsdWUpID8/IGdldERlZmF1bHRNYWluTG9vcE1vZGVsKClcbiAgY29uc3QgZGVmYXVsdFZhbHVlID0gZ2V0RGVmYXVsdEVmZm9ydEZvck1vZGVsKHJlc29sdmVkKVxuICByZXR1cm4gZGVmYXVsdFZhbHVlICE9PSB1bmRlZmluZWRcbiAgICA/IGNvbnZlcnRFZmZvcnRWYWx1ZVRvTGV2ZWwoZGVmYXVsdFZhbHVlKVxuICAgIDogJ2hpZ2gnXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxVQUFVLE1BQU0seUJBQXlCO0FBQ2hELE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsV0FBVyxFQUFFQyxPQUFPLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3RELFNBQVNDLDhCQUE4QixRQUFRLDZDQUE2QztBQUM1RixTQUNFLEtBQUtDLDBEQUEwRCxFQUMvREMsUUFBUSxRQUNILGlDQUFpQztBQUN4QyxTQUNFQyx1QkFBdUIsRUFDdkJDLG1CQUFtQixFQUNuQkMsa0JBQWtCLEVBQ2xCQyxpQkFBaUIsUUFDWix1QkFBdUI7QUFDOUIsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUNyQyxTQUFTQyxjQUFjLFFBQVEsaUNBQWlDO0FBQ2hFLFNBQVNDLFdBQVcsRUFBRUMsY0FBYyxRQUFRLHNCQUFzQjtBQUNsRSxTQUNFQyx5QkFBeUIsRUFDekIsS0FBS0MsV0FBVyxFQUNoQkMsd0JBQXdCLEVBQ3hCQyxtQkFBbUIsRUFDbkJDLHNCQUFzQixFQUN0QkMsOEJBQThCLEVBQzlCQyxtQkFBbUIsUUFDZCxvQkFBb0I7QUFDM0IsU0FDRUMsdUJBQXVCLEVBQ3ZCLEtBQUtDLFlBQVksRUFDakJDLGtCQUFrQixFQUNsQkMsdUJBQXVCLFFBQ2xCLHlCQUF5QjtBQUNoQyxTQUFTQyxlQUFlLFFBQVEsZ0NBQWdDO0FBQ2hFLFNBQ0VDLG9CQUFvQixFQUNwQkMsdUJBQXVCLFFBQ2xCLCtCQUErQjtBQUN0QyxTQUFTQyx3QkFBd0IsUUFBUSwrQkFBK0I7QUFDeEUsU0FBU0MsTUFBTSxRQUFRLHlCQUF5QjtBQUNoRCxTQUFTQyxNQUFNLFFBQVEs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 \ No newline at end of file diff --git a/components/NativeAutoUpdater.tsx b/components/NativeAutoUpdater.tsx new file mode 100644 index 0000000..a71244d --- /dev/null +++ b/components/NativeAutoUpdater.tsx @@ -0,0 +1,193 @@ +import * as React from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { logEvent } from 'src/services/analytics/index.js'; +import { logForDebugging } from 'src/utils/debug.js'; +import { logError } from 'src/utils/log.js'; +import { useInterval } from 'usehooks-ts'; +import { useUpdateNotification } from '../hooks/useUpdateNotification.js'; +import { Box, Text } from '../ink.js'; +import type { AutoUpdaterResult } from '../utils/autoUpdater.js'; +import { getMaxVersion, getMaxVersionMessage } from '../utils/autoUpdater.js'; +import { isAutoUpdaterDisabled } from '../utils/config.js'; +import { installLatest } from '../utils/nativeInstaller/index.js'; +import { gt } from '../utils/semver.js'; +import { getInitialSettings } from '../utils/settings/settings.js'; + +/** + * Categorize error messages for analytics + */ +function getErrorType(errorMessage: string): string { + if (errorMessage.includes('timeout')) { + return 'timeout'; + } + if (errorMessage.includes('Checksum mismatch')) { + return 'checksum_mismatch'; + } + if (errorMessage.includes('ENOENT') || errorMessage.includes('not found')) { + return 'not_found'; + } + if (errorMessage.includes('EACCES') || errorMessage.includes('permission')) { + return 'permission_denied'; + } + if (errorMessage.includes('ENOSPC')) { + return 'disk_full'; + } + if (errorMessage.includes('npm')) { + return 'npm_error'; + } + if (errorMessage.includes('network') || errorMessage.includes('ECONNREFUSED') || errorMessage.includes('ENOTFOUND')) { + return 'network_error'; + } + return 'unknown'; +} +type Props = { + isUpdating: boolean; + onChangeIsUpdating: (isUpdating: boolean) => void; + onAutoUpdaterResult: (autoUpdaterResult: AutoUpdaterResult) => void; + autoUpdaterResult: AutoUpdaterResult | null; + showSuccessMessage: boolean; + verbose: boolean; +}; +export function NativeAutoUpdater({ + isUpdating, + onChangeIsUpdating, + onAutoUpdaterResult, + autoUpdaterResult, + showSuccessMessage, + verbose +}: Props): React.ReactNode { + const [versions, setVersions] = useState<{ + current?: string | null; + latest?: string | null; + }>({}); + const [maxVersionIssue, setMaxVersionIssue] = useState(null); + const updateSemver = useUpdateNotification(autoUpdaterResult?.version); + const channel = getInitialSettings()?.autoUpdatesChannel ?? 'latest'; + + // Track latest isUpdating value in a ref so the memoized checkForUpdates + // callback always sees the current value without changing callback identity + // (which would re-trigger the initial-check useEffect below and cause + // repeated downloads on remount — the upstream trigger for #22413). + const isUpdatingRef = useRef(isUpdating); + isUpdatingRef.current = isUpdating; + const checkForUpdates = React.useCallback(async () => { + if (isUpdatingRef.current) { + return; + } + if ("production" === 'test' || "production" === 'development') { + logForDebugging('NativeAutoUpdater: Skipping update check in test/dev environment'); + return; + } + if (isAutoUpdaterDisabled()) { + return; + } + onChangeIsUpdating(true); + const startTime = Date.now(); + + // Log the start of an auto-update check for funnel analysis + logEvent('tengu_native_auto_updater_start', {}); + try { + // Check if current version is above the max allowed version + const maxVersion = await getMaxVersion(); + if (maxVersion && gt(MACRO.VERSION, maxVersion)) { + const msg = await getMaxVersionMessage(); + setMaxVersionIssue(msg ?? 'affects your version'); + } + const result = await installLatest(channel); + const currentVersion = MACRO.VERSION; + const latencyMs = Date.now() - startTime; + + // Handle lock contention gracefully - just return without treating as error + if (result.lockFailed) { + logEvent('tengu_native_auto_updater_lock_contention', { + latency_ms: latencyMs + }); + return; // Silently skip this update check, will try again later + } + + // Update versions for display + setVersions({ + current: currentVersion, + latest: result.latestVersion + }); + if (result.wasUpdated) { + logEvent('tengu_native_auto_updater_success', { + latency_ms: latencyMs + }); + onAutoUpdaterResult({ + version: result.latestVersion, + status: 'success' + }); + } else { + // Already up to date + logEvent('tengu_native_auto_updater_up_to_date', { + latency_ms: latencyMs + }); + } + } catch (error) { + const latencyMs = Date.now() - startTime; + const errorMessage = error instanceof Error ? error.message : String(error); + logError(error); + const errorType = getErrorType(errorMessage); + logEvent('tengu_native_auto_updater_fail', { + latency_ms: latencyMs, + error_timeout: errorType === 'timeout', + error_checksum: errorType === 'checksum_mismatch', + error_not_found: errorType === 'not_found', + error_permission: errorType === 'permission_denied', + error_disk_full: errorType === 'disk_full', + error_npm: errorType === 'npm_error', + error_network: errorType === 'network_error' + }); + onAutoUpdaterResult({ + version: null, + status: 'install_failed' + }); + } finally { + onChangeIsUpdating(false); + } + // isUpdating intentionally omitted from deps; we read isUpdatingRef + // instead so the guard is always current without changing callback + // identity (which would re-trigger the initial-check useEffect below). + // eslint-disable-next-line react-hooks/exhaustive-deps + // biome-ignore lint/correctness/useExhaustiveDependencies: isUpdating read via ref + }, [onAutoUpdaterResult, channel]); + + // Initial check + useEffect(() => { + void checkForUpdates(); + }, [checkForUpdates]); + + // Check every 30 minutes + useInterval(checkForUpdates, 30 * 60 * 1000); + const hasUpdateResult = !!autoUpdaterResult?.version; + const hasVersionInfo = !!versions.current && !!versions.latest; + // Show the component when: + // - warning banner needed (above max version), or + // - there's an update result to display (success/error), or + // - actively checking and we have version info to show + const shouldRender = !!maxVersionIssue || hasUpdateResult || isUpdating && hasVersionInfo; + if (!shouldRender) { + return null; + } + return + {verbose && + current: {versions.current} · {channel}: {versions.latest} + } + {isUpdating ? + + Checking for updates + + : autoUpdaterResult?.status === 'success' && showSuccessMessage && updateSemver && + ✓ Update installed · Restart to update + } + {autoUpdaterResult?.status === 'install_failed' && + ✗ Auto-update failed · Try /status + } + {maxVersionIssue && "external" === 'ant' && + ⚠ Known issue: {maxVersionIssue} · Run{' '} + claude rollback --safe to downgrade + } + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZVN0YXRlIiwibG9nRXZlbnQiLCJsb2dGb3JEZWJ1Z2dpbmciLCJsb2dFcnJvciIsInVzZUludGVydmFsIiwidXNlVXBkYXRlTm90aWZpY2F0aW9uIiwiQm94IiwiVGV4dCIsIkF1dG9VcGRhdGVyUmVzdWx0IiwiZ2V0TWF4VmVyc2lvbiIsImdldE1heFZlcnNpb25NZXNzYWdlIiwiaXNBdXRvVXBkYXRlckRpc2FibGVkIiwiaW5zdGFsbExhdGVzdCIsImd0IiwiZ2V0SW5pdGlhbFNldHRpbmdzIiwiZ2V0RXJyb3JUeXBlIiwiZXJyb3JNZXNzYWdlIiwiaW5jbHVkZXMiLCJQcm9wcyIsImlzVXBkYXRpbmciLCJvbkNoYW5nZUlzVXBkYXRpbmciLCJvbkF1dG9VcGRhdGVyUmVzdWx0IiwiYXV0b1VwZGF0ZXJSZXN1bHQiLCJzaG93U3VjY2Vzc01lc3NhZ2UiLCJ2ZXJib3NlIiwiTmF0aXZlQXV0b1VwZGF0ZXIiLCJSZWFjdE5vZGUiLCJ2ZXJzaW9ucyIsInNldFZlcnNpb25zIiwiY3VycmVudCIsImxhdGVzdCIsIm1heFZlcnNpb25Jc3N1ZSIsInNldE1heFZlcnNpb25Jc3N1ZSIsInVwZGF0ZVNlbXZlciIsInZlcnNpb24iLCJjaGFubmVsIiwiYXV0b1VwZGF0ZXNDaGFubmVsIiwiaXNVcGRhdGluZ1JlZiIsImNoZWNrRm9yVXBkYXRlcyIsInVzZUNhbGxiYWNrIiwic3RhcnRUaW1lIiwiRGF0ZSIsIm5vdyIsIm1heFZlcnNpb24iLCJNQUNSTyIsIlZFUlNJT04iLCJtc2ciLCJyZXN1bHQiLCJjdXJyZW50VmVyc2lvbiIsImxhdGVuY3lNcyIsImxvY2tGYWlsZWQiLCJsYXRlbmN5X21zIiwibGF0ZXN0VmVyc2lvbiIsIndhc1VwZGF0ZWQiLCJzdGF0dXMiLCJlcnJvciIsIkVycm9yIiwibWVzc2FnZSIsIlN0cmluZyIsImVycm9yVHlwZSIsImVycm9yX3RpbWVvdXQiLCJlcnJvcl9jaGVja3N1bSIsImVycm9yX25vdF9mb3VuZCIsImVycm9yX3Blcm1pc3Npb24iLCJlcnJvcl9kaXNrX2Z1bGwiLCJlcnJvcl9ucG0iLCJlcnJvcl9uZXR3b3JrIiwiaGFzVXBkYXRlUmVzdWx0IiwiaGFzVmVyc2lvbkluZm8iLCJzaG91bGRSZW5kZXIiXSwic291cmNlcyI6WyJOYXRpdmVBdXRvVXBkYXRlci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJ3NyYy91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnc3JjL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7IHVzZUludGVydmFsIH0gZnJvbSAndXNlaG9va3MtdHMnXG5pbXBvcnQgeyB1c2VVcGRhdGVOb3RpZmljYXRpb24gfSBmcm9tICcuLi9ob29rcy91c2VVcGRhdGVOb3RpZmljYXRpb24uanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IEF1dG9VcGRhdGVyUmVzdWx0IH0gZnJvbSAnLi4vdXRpbHMvYXV0b1VwZGF0ZXIuanMnXG5pbXBvcnQgeyBnZXRNYXhWZXJzaW9uLCBnZXRNYXhWZXJzaW9uTWVzc2FnZSB9IGZyb20gJy4uL3V0aWxzL2F1dG9VcGRhdGVyLmpzJ1xuaW1wb3J0IHsgaXNBdXRvVXBkYXRlckRpc2FibGVkIH0gZnJvbSAnLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgaW5zdGFsbExhdGVzdCB9IGZyb20gJy4uL3V0aWxzL25hdGl2ZUluc3RhbGxlci9pbmRleC5qcydcbmltcG9ydCB7IGd0IH0gZnJvbSAnLi4vdXRpbHMvc2VtdmVyLmpzJ1xuaW1wb3J0IHsgZ2V0SW5pdGlhbFNldHRpbmdzIH0gZnJvbSAnLi4vdXRpbHMvc2V0dGluZ3Mvc2V0dGluZ3MuanMnXG5cbi8qKlxuICogQ2F0ZWdvcml6ZSBlcnJvciBtZXNzYWdlcyBmb3IgYW5hbHl0aWNzXG4gKi9cbmZ1bmN0aW9uIGdldEVycm9yVHlwZShlcnJvck1lc3NhZ2U6IHN0cmluZyk6IHN0cmluZyB7XG4gIGlmIChlcnJvck1lc3NhZ2UuaW5jbHVkZXMoJ3RpbWVvdXQnKSkge1xuICAgIHJldHVybiAndGltZW91dCdcbiAgfVxuICBpZiAoZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdDaGVja3N1bSBtaXNtYXRjaCcpKSB7XG4gICAgcmV0dXJuICdjaGVja3N1bV9taXNtYXRjaCdcbiAgfVxuICBpZiAoZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdFTk9FTlQnKSB8fCBlcnJvck1lc3NhZ2UuaW5jbHVkZXMoJ25vdCBmb3VuZCcpKSB7XG4gICAgcmV0dXJuICdub3RfZm91bmQnXG4gIH1cbiAgaWYgKGVycm9yTWVzc2FnZS5pbmNsdWRlcygnRUFDQ0VTJykgfHwgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdwZXJtaXNzaW9uJykpIHtcbiAgICByZXR1cm4gJ3Blcm1pc3Npb25fZGVuaWVkJ1xuICB9XG4gIGlmIChlcnJvck1lc3NhZ2UuaW5jbHVkZXMoJ0VOT1NQQycpKSB7XG4gICAgcmV0dXJuICdkaXNrX2Z1bGwnXG4gIH1cbiAgaWYgKGVycm9yTWVzc2FnZS5pbmNsdWRlcygnbnBtJykpIHtcbiAgICByZXR1cm4gJ25wbV9lcnJvcidcbiAgfVxuICBpZiAoXG4gICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCduZXR3b3JrJykgfHxcbiAgICBlcnJvck1lc3NhZ2UuaW5jbHVkZXMoJ0VDT05OUkVGVVNFRCcpIHx8XG4gICAgZXJyb3JNZXNzYWdlLmluY2x1ZGVzKCdFTk9URk9VTkQnKVxuICApIHtcbiAgICByZXR1cm4gJ25ldHdvcmtfZXJyb3InXG4gIH1cbiAgcmV0dXJuICd1bmtub3duJ1xufVxuXG50eXBlIFByb3BzID0ge1xuICBpc1VwZGF0aW5nOiBib29sZWFuXG4gIG9uQ2hhbmdlSXNVcGRhdGluZzogKGlzVXBkYXRpbmc6IGJvb2xlYW4pID0+IHZvaWRcbiAgb25BdXRvVXBkYXRlclJlc3VsdDogKGF1dG9VcGRhdGVyUmVzdWx0OiBBdXRvVXBkYXRlclJlc3VsdCkgPT4gdm9pZFxuICBhdXRvVXBkYXRlclJlc3VsdDogQXV0b1VwZGF0ZXJSZXN1bHQgfCBudWxsXG4gIHNob3dTdWNjZXNzTWVzc2FnZTogYm9vbGVhblxuICB2ZXJib3NlOiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBOYXRpdmVBdXRvVXBkYXRlcih7XG4gIGlzVXBkYXRpbmcsXG4gIG9uQ2hhbmdlSXNVcGRhdGluZyxcbiAgb25BdXRvVXBkYXRlclJlc3VsdCxcbiAgYXV0b1VwZGF0ZXJSZXN1bHQsXG4gIHNob3dTdWNjZXNzTWVzc2FnZSxcbiAgdmVyYm9zZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3ZlcnNpb25zLCBzZXRWZXJzaW9uc10gPSB1c2VTdGF0ZTx7XG4gICAgY3VycmVudD86IHN0cmluZyB8IG51bGxcbiAgICBsYXRlc3Q/OiBzdHJpbmcgfCBudWxsXG4gIH0+KHt9KVxuICBjb25zdCBbbWF4VmVyc2lvbklzc3VlLCBzZXRNYXhWZXJzaW9uSXNzdWVdID0gdXNlU3RhdGU8c3RyaW5nIHwgbnVsbD4obnVsbClcbiAgY29uc3QgdXBkYXRlU2VtdmVyID0gdXNlVXBkYXRlTm90aWZpY2F0aW9uKGF1dG9VcGRhdGVyUmVzdWx0Py52ZXJzaW9uKVxuICBjb25zdCBjaGFubmVsID0gZ2V0SW5pdGlhbFNldHRpbmdzKCk/LmF1dG9VcGRhdGVzQ2hhbm5lbCA/PyAnbGF0ZXN0J1xuXG4gIC8vIFRyYWNrIGxhdGVzdCBpc1VwZGF0aW5nIHZhbHVlIGluIGEgcmVmIHNvIHRoZSBtZW1vaXplZCBjaGVja0ZvclVwZGF0ZXNcbiAgLy8gY2FsbGJhY2sgYWx3YXlzIHNlZXMgdGhlIGN1cnJlbnQgdmFsdWUgd2l0aG91dCBjaGFuZ2luZyBjYWxsYmFjayBpZGVudGl0eVxuICAvLyAod2hpY2ggd291bGQgcmUtdHJpZ2dlciB0aGUgaW5pdGlhbC1jaGVjayB1c2VFZmZlY3QgYmVsb3cgYW5kIGNhdXNlXG4gIC8vIHJlcGVhdGVkIGRvd25sb2FkcyBvbiByZW1vdW50IOKAlCB0aGUgdXBzdHJlYW0gdHJpZ2dlciBmb3IgIzIyNDEzKS5cbiAgY29uc3QgaXNVcGRhdGluZ1JlZiA9IHVzZVJlZihpc1VwZGF0aW5nKVxuICBpc1VwZGF0aW5nUmVmLmN1cnJlbnQgPSBpc1VwZGF0aW5nXG5cbiAgY29uc3QgY2hlY2tGb3JVcGRhdGVzID0gUmVhY3QudXNlQ2FsbGJhY2soYXN5bmMgKCkgPT4ge1xuICAgIGlmIChpc1VwZGF0aW5nUmVmLmN1cnJlbnQpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChcbiAgICAgIFwicHJvZHVjdGlvblwiID09PSAndGVzdCcgfHxcbiAgICAgIFwicHJvZHVjdGlvblwiID09PSAnZGV2ZWxvcG1lbnQnXG4gICAgKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICdOYXRpdmVBdXRvVXBkYXRlcjogU2tpcHBpbmcgdXBkYXRlIGNoZWNrIGluIHRlc3QvZGV2IGVudmlyb25tZW50JyxcbiAgICAgIClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChpc0F1dG9VcGRhdGVyRGlzYWJsZWQoKSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgb25DaGFuZ2VJc1VwZGF0aW5nKHRydWUpXG4gICAgY29uc3Qgc3RhcnRUaW1lID0gRGF0ZS5ub3coKVxuXG4gICAgLy8gTG9nIHRoZSBzdGFydCBvZiBhbiBhdXRvLXVwZGF0ZSBjaGVjayBmb3IgZnVubmVsIGFuYWx5c2lzXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X25hdGl2ZV9hdXRvX3VwZGF0ZXJfc3RhcnQnLCB7fSlcblxuICAgIHRyeSB7XG4gICAgICAvLyBDaGVjayBpZiBjdXJyZW50IHZlcnNpb24gaXMgYWJvdmUgdGhlIG1heCBhbGxvd2VkIHZlcnNpb25cbiAgICAgIGNvbnN0IG1heFZlcnNpb24gPSBhd2FpdCBnZXRNYXhWZXJzaW9uKClcbiAgICAgIGlmIChtYXhWZXJzaW9uICYmIGd0KE1BQ1JPLlZFUlNJT04sIG1heFZlcnNpb24pKSB7XG4gICAgICAgIGNvbnN0IG1zZyA9IGF3YWl0IGdldE1heFZlcnNpb25NZXNzYWdlKClcbiAgICAgICAgc2V0TWF4VmVyc2lvbklzc3VlKG1zZyA/PyAnYWZmZWN0cyB5b3VyIHZlcnNpb24nKVxuICAgICAgfVxuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBpbnN0YWxsTGF0ZXN0KGNoYW5uZWwpXG4gICAgICBjb25zdCBjdXJyZW50VmVyc2lvbiA9IE1BQ1JPLlZFUlNJT05cbiAgICAgIGNvbnN0IGxhdGVuY3lNcyA9IERhdGUubm93KCkgLSBzdGFydFRpbWVcblxuICAgICAgLy8gSGFuZGxlIGxvY2sgY29udGVudGlvbiBncmFjZWZ1bGx5IC0ganVzdCByZXR1cm4gd2l0aG91dCB0cmVhdGluZyBhcyBlcnJvclxuICAgICAgaWYgKHJlc3VsdC5sb2NrRmFpbGVkKSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9uYXRpdmVfYXV0b191cGRhdGVyX2xvY2tfY29udGVudGlvbicsIHtcbiAgICAgICAgICBsYXRlbmN5X21zOiBsYXRlbmN5TXMsXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVybiAvLyBTaWxlbnRseSBza2lwIHRoaXMgdXBkYXRlIGNoZWNrLCB3aWxsIHRyeSBhZ2FpbiBsYXRlclxuICAgICAgfVxuXG4gICAgICAvLyBVcGRhdGUgdmVyc2lvbnMgZm9yIGRpc3BsYXlcbiAgICAgIHNldFZlcnNpb25zKHsgY3VycmVudDogY3VycmVudFZlcnNpb24sIGxhdGVzdDogcmVzdWx0LmxhdGVzdFZlcnNpb24gfSlcblxuICAgICAgaWYgKHJlc3VsdC53YXNVcGRhdGVkKSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9uYXRpdmVfYXV0b191cGRhdGVyX3N1Y2Nlc3MnLCB7XG4gICAgICAgICAgbGF0ZW5jeV9tczogbGF0ZW5jeU1zLFxuICAgICAgICB9KVxuXG4gICAgICAgIG9uQXV0b1VwZGF0ZXJSZXN1bHQoe1xuICAgICAgICAgIHZlcnNpb246IHJlc3VsdC5sYXRlc3RWZXJzaW9uLFxuICAgICAgICAgIHN0YXR1czogJ3N1Y2Nlc3MnLFxuICAgICAgICB9KVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gQWxyZWFkeSB1cCB0byBkYXRlXG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9uYXRpdmVfYXV0b191cGRhdGVyX3VwX3RvX2RhdGUnLCB7XG4gICAgICAgICAgbGF0ZW5jeV9tczogbGF0ZW5jeU1zLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBjb25zdCBsYXRlbmN5TXMgPSBEYXRlLm5vdygpIC0gc3RhcnRUaW1lXG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPVxuICAgICAgICBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcilcbiAgICAgIGxvZ0Vycm9yKGVycm9yKVxuXG4gICAgICBjb25zdCBlcnJvclR5cGUgPSBnZXRFcnJvclR5cGUoZXJyb3JNZXNzYWdlKVxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X25hdGl2ZV9hdXRvX3VwZGF0ZXJfZmFpbCcsIHtcbiAgICAgICAgbGF0ZW5jeV9tczogbGF0ZW5jeU1zLFxuICAgICAgICBlcnJvcl90aW1lb3V0OiBlcnJvclR5cGUgPT09ICd0aW1lb3V0JyxcbiAgICAgICAgZXJyb3JfY2hlY2tzdW06IGVycm9yVHlwZSA9PT0gJ2NoZWNrc3VtX21pc21hdGNoJyxcbiAgICAgICAgZXJyb3Jfbm90X2ZvdW5kOiBlcnJvclR5cGUgPT09ICdub3RfZm91bmQnLFxuICAgICAgICBlcnJvcl9wZXJtaXNzaW9uOiBlcnJvclR5cGUgPT09ICdwZXJtaXNzaW9uX2RlbmllZCcsXG4gICAgICAgIGVycm9yX2Rpc2tfZnVsbDogZXJyb3JUeXBlID09PSAnZGlza19mdWxsJyxcbiAgICAgICAgZXJyb3JfbnBtOiBlcnJvclR5cGUgPT09ICducG1fZXJyb3InLFxuICAgICAgICBlcnJvcl9uZXR3b3JrOiBlcnJvclR5cGUgPT09ICduZXR3b3JrX2Vycm9yJyxcbiAgICAgIH0pXG5cbiAgICAgIG9uQXV0b1VwZGF0ZXJSZXN1bHQoe1xuICAgICAgICB2ZXJzaW9uOiBudWxsLFxuICAgICAgICBzdGF0dXM6ICdpbnN0YWxsX2ZhaWxlZCcsXG4gICAgICB9KVxuICAgIH0gZmluYWxseSB7XG4gICAgICBvbkNoYW5nZUlzVXBkYXRpbmcoZmFsc2UpXG4gICAgfVxuICAgIC8vIGlzVXBkYXRpbmcgaW50ZW50aW9uYWxseSBvbWl0dGVkIGZyb20gZGVwczsgd2UgcmVhZCBpc1VwZGF0aW5nUmVmXG4gICAgLy8gaW5zdGVhZCBzbyB0aGUgZ3VhcmQgaXMgYWx3YXlzIGN1cnJlbnQgd2l0aG91dCBjaGFuZ2luZyBjYWxsYmFja1xuICAgIC8vIGlkZW50aXR5ICh3aGljaCB3b3VsZCByZS10cmlnZ2VyIHRoZSBpbml0aWFsLWNoZWNrIHVzZUVmZmVjdCBiZWxvdykuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwc1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUV4aGF1c3RpdmVEZXBlbmRlbmNpZXM6IGlzVXBkYXRpbmcgcmVhZCB2aWEgcmVmXG4gIH0sIFtvbkF1dG9VcGRhdGVyUmVzdWx0LCBjaGFubmVsXSlcblxuICAvLyBJbml0aWFsIGNoZWNrXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgdm9pZCBjaGVja0ZvclVwZGF0ZXMoKVxuICB9LCBbY2hlY2tGb3JVcGRhdGVzXSlcblxuICAvLyBDaGVjayBldmVyeSAzMCBtaW51dGVzXG4gIHVzZUludGVydmFsKGNoZWNrRm9yVXBkYXRlcywgMzAgKiA2MCAqIDEwMDApXG5cbiAgY29uc3QgaGFzVXBkYXRlUmVzdWx0ID0gISFhdXRvVXBkYXRlclJlc3VsdD8udmVyc2lvblxuICBjb25zdCBoYXNWZXJzaW9uSW5mbyA9ICEhdmVyc2lvbnMuY3VycmVudCAmJiAhIXZlcnNpb25zLmxhdGVzdFxuICAvLyBTaG93IHRoZSBjb21wb25lbnQgd2hlbjpcbiAgLy8gLSB3YXJuaW5nIGJhbm5lciBuZWVkZWQgKGFib3ZlIG1heCB2ZXJzaW9uKSwgb3JcbiAgLy8gLSB0aGVyZSdzIGFuIHVwZGF0ZSByZXN1bHQgdG8gZGlzcGxheSAoc3VjY2Vzcy9lcnJvciksIG9yXG4gIC8vIC0gYWN0aXZlbHkgY2hlY2tpbmcgYW5kIHdlIGhhdmUgdmVyc2lvbiBpbmZvIHRvIHNob3dcbiAgY29uc3Qgc2hvdWxkUmVuZGVyID1cbiAgICAhIW1heFZlcnNpb25Jc3N1ZSB8fCBoYXNVcGRhdGVSZXN1bHQgfHwgKGlzVXBkYXRpbmcgJiYgaGFzVmVyc2lvbkluZm8pXG5cbiAgaWYgKCFzaG91bGRSZW5kZXIpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9PlxuICAgICAge3ZlcmJvc2UgJiYgKFxuICAgICAgICA8VGV4dCBkaW1Db2xvciB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICBjdXJyZW50OiB7dmVyc2lvbnMuY3VycmVudH0gJm1pZGRvdDsge2NoYW5uZWx9OiB7dmVyc2lvbnMubGF0ZXN0fVxuICAgICAgICA8L1RleHQ+XG4gICAgICApfVxuICAgICAge2lzVXBkYXRpbmcgPyAoXG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3Igd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICBDaGVja2luZyBmb3IgdXBkYXRlc1xuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApIDogKFxuICAgICAgICBhdXRvVXBkYXRlclJlc3VsdD8uc3RhdHVzID09PSAnc3VjY2VzcycgJiZcbiAgICAgICAgc2hvd1N1Y2Nlc3NNZXNzYWdlICYmXG4gICAgICAgIHVwZGF0ZVNlbXZlciAmJiAoXG4gICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCIgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICDinJMgVXBkYXRlIGluc3RhbGxlZCDCtyBSZXN0YXJ0IHRvIHVwZGF0ZVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKVxuICAgICAgKX1cbiAgICAgIHthdXRvVXBkYXRlclJlc3VsdD8uc3RhdHVzID09PSAnaW5zdGFsbF9mYWlsZWQnICYmIChcbiAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiIHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgIOKclyBBdXRvLXVwZGF0ZSBmYWlsZWQgJm1pZGRvdDsgVHJ5IDxUZXh0IGJvbGQ+L3N0YXR1czwvVGV4dD5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKX1cbiAgICAgIHttYXhWZXJzaW9uSXNzdWUgJiYgXCJleHRlcm5hbFwiID09PSAnYW50JyAmJiAoXG4gICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPlxuICAgICAgICAgIOKaoCBLbm93biBpc3N1ZToge21heFZlcnNpb25Jc3N1ZX0gJm1pZGRvdDsgUnVueycgJ31cbiAgICAgICAgICA8VGV4dCBib2xkPmNsYXVkZSByb2xsYmFjayAtLXNhZmU8L1RleHQ+IHRvIGRvd25ncmFkZVxuICAgICAgICA8L1RleHQ+XG4gICAgICApfVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsU0FBUyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ25ELFNBQVNDLFFBQVEsUUFBUSxpQ0FBaUM7QUFDMUQsU0FBU0MsZUFBZSxRQUFRLG9CQUFvQjtBQUNwRCxTQUFTQyxRQUFRLFFB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 \ No newline at end of file diff --git a/components/NotebookEditToolUseRejectedMessage.tsx b/components/NotebookEditToolUseRejectedMessage.tsx new file mode 100644 index 0000000..8fa355f --- /dev/null +++ b/components/NotebookEditToolUseRejectedMessage.tsx @@ -0,0 +1,92 @@ +import { c as _c } from "react/compiler-runtime"; +import { relative } from 'path'; +import * as React from 'react'; +import { getCwd } from 'src/utils/cwd.js'; +import { Box, Text } from '../ink.js'; +import { HighlightedCode } from './HighlightedCode.js'; +import { MessageResponse } from './MessageResponse.js'; +type Props = { + notebook_path: string; + cell_id: string | undefined; + new_source: string; + cell_type?: 'code' | 'markdown'; + edit_mode?: 'replace' | 'insert' | 'delete'; + verbose: boolean; +}; +export function NotebookEditToolUseRejectedMessage(t0) { + const $ = _c(20); + const { + notebook_path, + cell_id, + new_source, + cell_type, + edit_mode: t1, + verbose + } = t0; + const edit_mode = t1 === undefined ? "replace" : t1; + const operation = edit_mode === "delete" ? "delete" : `${edit_mode} cell in`; + let t2; + if ($[0] !== operation) { + t2 = User rejected {operation} ; + $[0] = operation; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== notebook_path || $[3] !== verbose) { + t3 = verbose ? notebook_path : relative(getCwd(), notebook_path); + $[2] = notebook_path; + $[3] = verbose; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== t3) { + t4 = {t3}; + $[5] = t3; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] !== cell_id) { + t5 = at cell {cell_id}; + $[7] = cell_id; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] !== t2 || $[10] !== t4 || $[11] !== t5) { + t6 = {t2}{t4}{t5}; + $[9] = t2; + $[10] = t4; + $[11] = t5; + $[12] = t6; + } else { + t6 = $[12]; + } + let t7; + if ($[13] !== cell_type || $[14] !== edit_mode || $[15] !== new_source) { + t7 = edit_mode !== "delete" && ; + $[13] = cell_type; + $[14] = edit_mode; + $[15] = new_source; + $[16] = t7; + } else { + t7 = $[16]; + } + let t8; + if ($[17] !== t6 || $[18] !== t7) { + t8 = {t6}{t7}; + $[17] = t6; + $[18] = t7; + $[19] = t8; + } else { + t8 = $[19]; + } + return t8; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyZWxhdGl2ZSIsIlJlYWN0IiwiZ2V0Q3dkIiwiQm94IiwiVGV4dCIsIkhpZ2hsaWdodGVkQ29kZSIsIk1lc3NhZ2VSZXNwb25zZSIsIlByb3BzIiwibm90ZWJvb2tfcGF0aCIsImNlbGxfaWQiLCJuZXdfc291cmNlIiwiY2VsbF90eXBlIiwiZWRpdF9tb2RlIiwidmVyYm9zZSIsIk5vdGVib29rRWRpdFRvb2xVc2VSZWplY3RlZE1lc3NhZ2UiLCJ0MCIsIiQiLCJfYyIsInQxIiwidW5kZWZpbmVkIiwib3BlcmF0aW9uIiwidDIiLCJ0MyIsInQ0IiwidDUiLCJ0NiIsInQ3IiwidDgiXSwic291cmNlcyI6WyJOb3RlYm9va0VkaXRUb29sVXNlUmVqZWN0ZWRNZXNzYWdlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyByZWxhdGl2ZSB9IGZyb20gJ3BhdGgnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGdldEN3ZCB9IGZyb20gJ3NyYy91dGlscy9jd2QuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBIaWdobGlnaHRlZENvZGUgfSBmcm9tICcuL0hpZ2hsaWdodGVkQ29kZS5qcydcbmltcG9ydCB7IE1lc3NhZ2VSZXNwb25zZSB9IGZyb20gJy4vTWVzc2FnZVJlc3BvbnNlLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBub3RlYm9va19wYXRoOiBzdHJpbmdcbiAgY2VsbF9pZDogc3RyaW5nIHwgdW5kZWZpbmVkXG4gIG5ld19zb3VyY2U6IHN0cmluZ1xuICBjZWxsX3R5cGU/OiAnY29kZScgfCAnbWFya2Rvd24nXG4gIGVkaXRfbW9kZT86ICdyZXBsYWNlJyB8ICdpbnNlcnQnIHwgJ2RlbGV0ZSdcbiAgdmVyYm9zZTogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gTm90ZWJvb2tFZGl0VG9vbFVzZVJlamVjdGVkTWVzc2FnZSh7XG4gIG5vdGVib29rX3BhdGgsXG4gIGNlbGxfaWQsXG4gIG5ld19zb3VyY2UsXG4gIGNlbGxfdHlwZSxcbiAgZWRpdF9tb2RlID0gJ3JlcGxhY2UnLFxuICB2ZXJib3NlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBvcGVyYXRpb24gPSBlZGl0X21vZGUgPT09ICdkZWxldGUnID8gJ2RlbGV0ZScgOiBgJHtlZGl0X21vZGV9IGNlbGwgaW5gXG5cbiAgcmV0dXJuIChcbiAgICA8TWVzc2FnZVJlc3BvbnNlPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCI+VXNlciByZWplY3RlZCB7b3BlcmF0aW9ufSA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgYm9sZCBjb2xvcj1cInN1YnRsZVwiPlxuICAgICAgICAgICAge3ZlcmJvc2UgPyBub3RlYm9va19wYXRoIDogcmVsYXRpdmUoZ2V0Q3dkKCksIG5vdGVib29rX3BhdGgpfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1YnRsZVwiPiBhdCBjZWxsIHtjZWxsX2lkfTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHtlZGl0X21vZGUgIT09ICdkZWxldGUnICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPEhpZ2hsaWdodGVkQ29kZVxuICAgICAgICAgICAgICBjb2RlPXtuZXdfc291cmNlfVxuICAgICAgICAgICAgICBmaWxlUGF0aD17Y2VsbF90eXBlID09PSAnbWFya2Rvd24nID8gJ2ZpbGUubWQnIDogJ2ZpbGUucHknfVxuICAgICAgICAgICAgICBkaW1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICA8L0JveD5cbiAgICA8L01l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 \ No newline at end of file diff --git a/components/OffscreenFreeze.tsx b/components/OffscreenFreeze.tsx new file mode 100644 index 0000000..de283f0 --- /dev/null +++ b/components/OffscreenFreeze.tsx @@ -0,0 +1,44 @@ +import React, { useContext, useRef } from 'react'; +import { useTerminalViewport } from '../ink/hooks/use-terminal-viewport.js'; +import { Box } from '../ink.js'; +import { InVirtualListContext } from './messageActions.js'; +type Props = { + children: React.ReactNode; +}; + +/** + * Freezes children when they scroll above the terminal viewport (into scrollback). + * + * Any content change above the viewport forces log-update.ts into a full terminal + * reset (it cannot partially update rows that have scrolled out). For content that + * updates on a timer — spinners, elapsed counters — this produces a reset per tick. + * + * When offscreen, returns the same ReactElement reference that was cached during + * the last visible render. React's reconciler bails on identical element refs, so + * the subtree never re-renders, producing zero diff. + * + * The cache is one slot deep: the first re-render after scrolling back into view + * picks up the live children. Content still updates normally while visible. + */ +export function OffscreenFreeze({ + children +}: Props): React.ReactNode { + // React Compiler: reading cached.current in the return is the entire + // freeze mechanism — memoizing this component would defeat it. Opt out. + 'use no memo'; + + const inVirtualList = useContext(InVirtualListContext); + const [ref, { + isVisible + }] = useTerminalViewport(); + const cached = useRef(children); + // Virtual list has no terminal scrollback — the ScrollBox clips inside the + // viewport, so there's nothing to freeze. Freezing there also blocks + // click-to-expand since useTerminalViewport's visibility calc can disagree + // with the ScrollBox's virtual scroll position. + if (isVisible || inVirtualList) { + cached.current = children; + } + return {cached.current}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNvbnRleHQiLCJ1c2VSZWYiLCJ1c2VUZXJtaW5hbFZpZXdwb3J0IiwiQm94IiwiSW5WaXJ0dWFsTGlzdENvbnRleHQiLCJQcm9wcyIsImNoaWxkcmVuIiwiUmVhY3ROb2RlIiwiT2Zmc2NyZWVuRnJlZXplIiwiaW5WaXJ0dWFsTGlzdCIsInJlZiIsImlzVmlzaWJsZSIsImNhY2hlZCIsImN1cnJlbnQiXSwic291cmNlcyI6WyJPZmZzY3JlZW5GcmVlemUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VDb250ZXh0LCB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZVRlcm1pbmFsVmlld3BvcnQgfSBmcm9tICcuLi9pbmsvaG9va3MvdXNlLXRlcm1pbmFsLXZpZXdwb3J0LmpzJ1xuaW1wb3J0IHsgQm94IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgSW5WaXJ0dWFsTGlzdENvbnRleHQgfSBmcm9tICcuL21lc3NhZ2VBY3Rpb25zLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBjaGlsZHJlbjogUmVhY3QuUmVhY3ROb2RlXG59XG5cbi8qKlxuICogRnJlZXplcyBjaGlsZHJlbiB3aGVuIHRoZXkgc2Nyb2xsIGFib3ZlIHRoZSB0ZXJtaW5hbCB2aWV3cG9ydCAoaW50byBzY3JvbGxiYWNrKS5cbiAqXG4gKiBBbnkgY29udGVudCBjaGFuZ2UgYWJvdmUgdGhlIHZpZXdwb3J0IGZvcmNlcyBsb2ctdXBkYXRlLnRzIGludG8gYSBmdWxsIHRlcm1pbmFsXG4gKiByZXNldCAoaXQgY2Fubm90IHBhcnRpYWxseSB1cGRhdGUgcm93cyB0aGF0IGhhdmUgc2Nyb2xsZWQgb3V0KS4gRm9yIGNvbnRlbnQgdGhhdFxuICogdXBkYXRlcyBvbiBhIHRpbWVyIOKAlCBzcGlubmVycywgZWxhcHNlZCBjb3VudGVycyDigJQgdGhpcyBwcm9kdWNlcyBhIHJlc2V0IHBlciB0aWNrLlxuICpcbiAqIFdoZW4gb2Zmc2NyZWVuLCByZXR1cm5zIHRoZSBzYW1lIFJlYWN0RWxlbWVudCByZWZlcmVuY2UgdGhhdCB3YXMgY2FjaGVkIGR1cmluZ1xuICogdGhlIGxhc3QgdmlzaWJsZSByZW5kZXIuIFJlYWN0J3MgcmVjb25jaWxlciBiYWlscyBvbiBpZGVudGljYWwgZWxlbWVudCByZWZzLCBzb1xuICogdGhlIHN1YnRyZWUgbmV2ZXIgcmUtcmVuZGVycywgcHJvZHVjaW5nIHplcm8gZGlmZi5cbiAqXG4gKiBUaGUgY2FjaGUgaXMgb25lIHNsb3QgZGVlcDogdGhlIGZpcnN0IHJlLXJlbmRlciBhZnRlciBzY3JvbGxpbmcgYmFjayBpbnRvIHZpZXdcbiAqIHBpY2tzIHVwIHRoZSBsaXZlIGNoaWxkcmVuLiBDb250ZW50IHN0aWxsIHVwZGF0ZXMgbm9ybWFsbHkgd2hpbGUgdmlzaWJsZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIE9mZnNjcmVlbkZyZWV6ZSh7IGNoaWxkcmVuIH06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgLy8gUmVhY3QgQ29tcGlsZXI6IHJlYWRpbmcgY2FjaGVkLmN1cnJlbnQgaW4gdGhlIHJldHVybiBpcyB0aGUgZW50aXJlXG4gIC8vIGZyZWV6ZSBtZWNoYW5pc20g4oCUIG1lbW9pemluZyB0aGlzIGNvbXBvbmVudCB3b3VsZCBkZWZlYXQgaXQuIE9wdCBvdXQuXG4gICd1c2Ugbm8gbWVtbydcbiAgY29uc3QgaW5WaXJ0dWFsTGlzdCA9IHVzZUNvbnRleHQoSW5WaXJ0dWFsTGlzdENvbnRleHQpXG4gIGNvbnN0IFtyZWYsIHsgaXNWaXNpYmxlIH1dID0gdXNlVGVybWluYWxWaWV3cG9ydCgpXG4gIGNvbnN0IGNhY2hlZCA9IHVzZVJlZihjaGlsZHJlbilcbiAgLy8gVmlydHVhbCBsaXN0IGhhcyBubyB0ZXJtaW5hbCBzY3JvbGxiYWNrIOKAlCB0aGUgU2Nyb2xsQm94IGNsaXBzIGluc2lkZSB0aGVcbiAgLy8gdmlld3BvcnQsIHNvIHRoZXJlJ3Mgbm90aGluZyB0byBmcmVlemUuIEZyZWV6aW5nIHRoZXJlIGFsc28gYmxvY2tzXG4gIC8vIGNsaWNrLXRvLWV4cGFuZCBzaW5jZSB1c2VUZXJtaW5hbFZpZXdwb3J0J3MgdmlzaWJpbGl0eSBjYWxjIGNhbiBkaXNhZ3JlZVxuICAvLyB3aXRoIHRoZSBTY3JvbGxCb3gncyB2aXJ0dWFsIHNjcm9sbCBwb3NpdGlvbi5cbiAgaWYgKGlzVmlzaWJsZSB8fCBpblZpcnR1YWxMaXN0KSB7XG4gICAgY2FjaGVkLmN1cnJlbnQgPSBjaGlsZHJlblxuICB9XG4gIHJldHVybiA8Qm94IHJlZj17cmVmfT57Y2FjaGVkLmN1cnJlbnR9PC9Cb3g+XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssSUFBSUMsVUFBVSxFQUFFQyxNQUFNLFFBQVEsT0FBTztBQUNqRCxTQUFTQyxtQkFBbUIsUUFBUSx1Q0FBdUM7QUFDM0UsU0FBU0MsR0FBRyxRQUFRLFdBQVc7QUFDL0IsU0FBU0Msb0JBQW9CLFFBQVEscUJBQXFCO0FBRTFELEtBQUtDLEtBQUssR0FBRztFQUNYQyxRQUFRLEVBQUVQLEtBQUssQ0FBQ1EsU0FBUztBQUMzQixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQVNDLGVBQWVBLENBQUM7RUFBRUY7QUFBZ0IsQ0FBTixFQUFFRCxLQUFLLENBQUMsRUFBRU4sS0FBSyxDQUFDUSxTQUFTLENBQUM7RUFDcEU7RUFDQTtFQUNBLGFBQWE7O0VBQ2IsTUFBTUUsYUFBYSxHQUFHVCxVQUFVLENBQUNJLG9CQUFvQixDQUFDO0VBQ3RELE1BQU0sQ0FBQ00sR0FBRyxFQUFFO0lBQUVDO0VBQVUsQ0FBQyxDQUFDLEdBQUdULG1CQUFtQixDQUFDLENBQUM7RUFDbEQsTUFBTVUsTUFBTSxHQUFHWCxNQUFNLENBQUNLLFFBQVEsQ0FBQztFQUMvQjtFQUNBO0VBQ0E7RUFDQTtFQUNBLElBQUlLLFNBQVMsSUFBSUYsYUFBYSxFQUFFO0lBQzlCRyxNQUFNLENBQUNDLE9BQU8sR0FBR1AsUUFBUTtFQUMzQjtFQUNBLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUNJLEdBQUcsQ0FBQyxDQUFDLENBQUNFLE1BQU0sQ0FBQ0MsT0FBTyxDQUFDLEVBQUUsR0FBRyxDQUFDO0FBQzlDIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/Onboarding.tsx b/components/Onboarding.tsx new file mode 100644 index 0000000..d4b6266 --- /dev/null +++ b/components/Onboarding.tsx @@ -0,0 +1,244 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { setupTerminal, shouldOfferTerminalSetup } from '../commands/terminalSetup/terminalSetup.js'; +import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { Box, Link, Newline, Text, useTheme } from '../ink.js'; +import { useKeybindings } from '../keybindings/useKeybinding.js'; +import { isAnthropicAuthEnabled } from '../utils/auth.js'; +import { normalizeApiKeyForConfig } from '../utils/authPortable.js'; +import { getCustomApiKeyStatus } from '../utils/config.js'; +import { env } from '../utils/env.js'; +import { isRunningOnHomespace } from '../utils/envUtils.js'; +import { PreflightStep } from '../utils/preflightChecks.js'; +import type { ThemeSetting } from '../utils/theme.js'; +import { ApproveApiKey } from './ApproveApiKey.js'; +import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js'; +import { Select } from './CustomSelect/select.js'; +import { WelcomeV2 } from './LogoV2/WelcomeV2.js'; +import { PressEnterToContinue } from './PressEnterToContinue.js'; +import { ThemePicker } from './ThemePicker.js'; +import { OrderedList } from './ui/OrderedList.js'; +type StepId = 'preflight' | 'theme' | 'oauth' | 'api-key' | 'security' | 'terminal-setup'; +interface OnboardingStep { + id: StepId; + component: React.ReactNode; +} +type Props = { + onDone(): void; +}; +export function Onboarding({ + onDone +}: Props): React.ReactNode { + const [currentStepIndex, setCurrentStepIndex] = useState(0); + const [skipOAuth, setSkipOAuth] = useState(false); + const [oauthEnabled] = useState(() => isAnthropicAuthEnabled()); + const [theme, setTheme] = useTheme(); + useEffect(() => { + logEvent('tengu_began_setup', { + oauthEnabled + }); + }, [oauthEnabled]); + function goToNextStep() { + if (currentStepIndex < steps.length - 1) { + const nextIndex = currentStepIndex + 1; + setCurrentStepIndex(nextIndex); + logEvent('tengu_onboarding_step', { + oauthEnabled, + stepId: steps[nextIndex]?.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } else { + onDone(); + } + } + function handleThemeSelection(newTheme: ThemeSetting) { + setTheme(newTheme); + goToNextStep(); + } + const exitState = useExitOnCtrlCDWithKeybindings(); + + // Define all onboarding steps + const themeStep = + + ; + const securityStep = + Security notes: + + {/** + * OrderedList misnumbers items when rendering conditionally, + * so put all items in the if/else + */} + + + Claude can make mistakes + + You should always review Claude's responses, especially when + + running code. + + + + + + Due to prompt injection risks, only use it with code you trust + + + For more details see: + + + + + + + + ; + const preflightStep = ; + // Create the steps array - determine which steps to include based on reAuth and oauthEnabled + const apiKeyNeedingApproval = useMemo(() => { + // Add API key step if needed + // On homespace, ANTHROPIC_API_KEY is preserved in process.env for child + // processes but ignored by Claude Code itself (see auth.ts). + if (!process.env.ANTHROPIC_API_KEY || isRunningOnHomespace()) { + return ''; + } + const customApiKeyTruncated = normalizeApiKeyForConfig(process.env.ANTHROPIC_API_KEY); + if (getCustomApiKeyStatus(customApiKeyTruncated) === 'new') { + return customApiKeyTruncated; + } + }, []); + function handleApiKeyDone(approved: boolean) { + if (approved) { + setSkipOAuth(true); + } + goToNextStep(); + } + const steps: OnboardingStep[] = []; + if (oauthEnabled) { + steps.push({ + id: 'preflight', + component: preflightStep + }); + } + steps.push({ + id: 'theme', + component: themeStep + }); + if (apiKeyNeedingApproval) { + steps.push({ + id: 'api-key', + component: + }); + } + if (oauthEnabled) { + steps.push({ + id: 'oauth', + component: + + + }); + } + steps.push({ + id: 'security', + component: securityStep + }); + if (shouldOfferTerminalSetup()) { + steps.push({ + id: 'terminal-setup', + component: + Use Claude Code's terminal setup? + + + For the optimal coding experience, enable the recommended settings + + for your terminal:{' '} + {env.terminal === 'Apple_Terminal' ? 'Option+Enter for newlines and visual bell' : 'Shift+Enter for newlines'} + + }; + $[6] = handleStyleSelect; + $[7] = initialStyle; + $[8] = isLoading; + $[9] = styleOptions; + $[10] = t8; + } else { + t8 = $[10]; + } + let t9; + if ($[11] !== onCancel || $[12] !== t5 || $[13] !== t6 || $[14] !== t8) { + t9 = {t8}; + $[11] = onCancel; + $[12] = t5; + $[13] = t6; + $[14] = t8; + $[15] = t9; + } else { + t9 = $[15]; + } + return t9; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlRWZmZWN0IiwidXNlU3RhdGUiLCJnZXRBbGxPdXRwdXRTdHlsZXMiLCJPVVRQVVRfU1RZTEVfQ09ORklHIiwiT3V0cHV0U3R5bGVDb25maWciLCJCb3giLCJUZXh0IiwiT3V0cHV0U3R5bGUiLCJnZXRDd2QiLCJPcHRpb25XaXRoRGVzY3JpcHRpb24iLCJTZWxlY3QiLCJEaWFsb2ciLCJERUZBVUxUX09VVFBVVF9TVFlMRV9MQUJFTCIsIkRFRkFVTFRfT1VUUFVUX1NUWUxFX0RFU0NSSVBUSU9OIiwibWFwQ29uZmlnc1RvT3B0aW9ucyIsInN0eWxlcyIsInN0eWxlTmFtZSIsIk9iamVjdCIsImVudHJpZXMiLCJtYXAiLCJzdHlsZSIsImNvbmZpZyIsImxhYmVsIiwibmFtZSIsInZhbHVlIiwiZGVzY3JpcHRpb24iLCJPdXRwdXRTdHlsZVBpY2tlclByb3BzIiwiaW5pdGlhbFN0eWxlIiwib25Db21wbGV0ZSIsIm9uQ2FuY2VsIiwiaXNTdGFuZGFsb25lQ29tbWFuZCIsIk91dHB1dFN0eWxlUGlja2VyIiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsInN0eWxlT3B0aW9ucyIsInNldFN0eWxlT3B0aW9ucyIsImlzTG9hZGluZyIsInNldElzTG9hZGluZyIsInQyIiwidDMiLCJ0aGVuIiwiYWxsU3R5bGVzIiwib3B0aW9ucyIsImNhdGNoIiwiYnVpbHRJbk9wdGlvbnMiLCJ0NCIsIm91dHB1dFN0eWxlIiwiaGFuZGxlU3R5bGVTZWxlY3QiLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5Il0sInNvdXJjZXMiOlsiT3V0cHV0U3R5bGVQaWNrZXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7XG4gIGdldEFsbE91dHB1dFN0eWxlcyxcbiAgT1VUUFVUX1NUWUxFX0NPTkZJRyxcbiAgdHlwZSBPdXRwdXRTdHlsZUNvbmZpZyxcbn0gZnJvbSAnLi4vY29uc3RhbnRzL291dHB1dFN0eWxlcy5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgT3V0cHV0U3R5bGUgfSBmcm9tICcuLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgeyBnZXRDd2QgfSBmcm9tICcuLi91dGlscy9jd2QuanMnXG5pbXBvcnQgdHlwZSB7IE9wdGlvbldpdGhEZXNjcmlwdGlvbiB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5cbmNvbnN0IERFRkFVTFRfT1VUUFVUX1NUWUxFX0xBQkVMID0gJ0RlZmF1bHQnXG5jb25zdCBERUZBVUxUX09VVFBVVF9TVFlMRV9ERVNDUklQVElPTiA9XG4gICdDbGF1ZGUgY29tcGxldGVzIGNvZGluZyB0YXNrcyBlZmZpY2llbnRseSBhbmQgcHJvdmlkZXMgY29uY2lzZSByZXNwb25zZXMnXG5cbmZ1bmN0aW9uIG1hcENvbmZpZ3NUb09wdGlvbnMoc3R5bGVzOiB7XG4gIFtzdHlsZU5hbWU6IHN0cmluZ106IE91dHB1dFN0eWxlQ29uZmlnIHwgbnVsbFxufSk6IE9wdGlvbldpdGhEZXNjcmlwdGlvbltdIHtcbiAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKHN0eWxlcykubWFwKChbc3R5bGUsIGNvbmZpZ10pID0+ICh7XG4gICAgbGFiZWw6IGNvbmZpZz8ubmFtZSA/PyBERUZBVUxUX09VVFBVVF9TVFlMRV9MQUJFTCxcbiAgICB2YWx1ZTogc3R5bGUsXG4gICAgZGVzY3JpcHRpb246IGNvbmZpZz8uZGVzY3JpcHRpb24gPz8gREVGQVVMVF9PVVRQVVRfU1RZTEVfREVTQ1JJUFRJT04sXG4gIH0pKVxufVxuXG5leHBvcnQgdHlwZSBPdXRwdXRTdHlsZVBpY2tlclByb3BzID0ge1xuICBpbml0aWFsU3R5bGU6IE91dHB1dFN0eWxlXG4gIG9uQ29tcGxldGU6IChzdHlsZTogT3V0cHV0U3R5bGUpID0+IHZvaWRcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbiAgaXNTdGFuZGFsb25lQ29tbWFuZD86IGJvb2xlYW5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIE91dHB1dFN0eWxlUGlja2VyKHtcbiAgaW5pdGlhbFN0eWxlLFxuICBvbkNvbXBsZXRlLFxuICBvbkNhbmNlbCxcbiAgaXNTdGFuZGFsb25lQ29tbWFuZCxcbn06IE91dHB1dFN0eWxlUGlja2VyUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbc3R5bGVPcHRpb25zLCBzZXRTdHlsZU9wdGlvbnNdID0gdXNlU3RhdGU8T3B0aW9uV2l0aERlc2NyaXB0aW9uW10+KFtdKVxuICBjb25zdCBbaXNMb2FkaW5nLCBzZXRJc0xvYWRpbmddID0gdXNlU3RhdGUodHJ1ZSlcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIC8vIExvYWQgYWxsIG91dHB1dCBzdHlsZXMgaW5jbHVkaW5nIGN1c3RvbSBvbmVzXG4gICAgZ2V0QWxsT3V0cHV0U3R5bGVzKGdldEN3ZCgpKVxuICAgICAgLnRoZW4oYWxsU3R5bGVzID0+IHtcbiAgICAgICAgY29uc3Qgb3B0aW9ucyA9IG1hcENvbmZpZ3NUb09wdGlvbnMoYWxsU3R5bGVzKVxuICAgICAgICBzZXRTdHlsZU9wdGlvbnMob3B0aW9ucylcbiAgICAgICAgc2V0SXNMb2FkaW5nKGZhbHNlKVxuICAgICAgfSlcbiAgICAgIC5jYXRjaCgoKSA9PiB7XG4gICAgICAgIC8vIE9uIGVycm9yLCBmYWxsIGJhY2sgdG8gYnVpbHQtaW4gc3R5bGVzIG9ubHlcbiAgICAgICAgY29uc3QgYnVpbHRJbk9wdGlvbnMgPSBtYXBDb25maWdzVG9PcHRpb25zKE9VVFBVVF9TVFlMRV9DT05GSUcpXG4gICAgICAgIHNldFN0eWxlT3B0aW9ucyhidWlsdEluT3B0aW9ucylcbiAgICAgICAgc2V0SXNMb2FkaW5nKGZhbHNlKVxuICAgICAgfSlcbiAgfSwgW10pXG5cbiAgY29uc3QgaGFuZGxlU3R5bGVTZWxlY3QgPSB1c2VDYWxsYmFjayhcbiAgICAoc3R5bGU6IHN0cmluZykgPT4ge1xuICAgICAgY29uc3Qgb3V0cHV0U3R5bGUgPSBzdHlsZSBhcyBPdXRwdXRTdHlsZVxuICAgICAgb25Db21wbGV0ZShvdXRwdXRTdHlsZSlcbiAgICB9LFxuICAgIFtvbkNvbXBsZXRlXSxcbiAgKVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJQcmVmZXJyZWQgb3V0cHV0IHN0eWxlXCJcbiAgICAgIG9uQ2FuY2VsPXtvbkNhbmNlbH1cbiAgICAgIGhpZGVJbnB1dEd1aWRlPXshaXNTdGFuZGFsb25lQ29tbWFuZH1cbiAgICAgIGhpZGVCb3JkZXI9eyFpc1N0YW5kYWxvbmVDb21tYW5kfVxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIFRoaXMgY2hhbmdlcyBob3cgQ2xhdWRlIENvZGUgY29tbXVuaWNhdGVzIHdpdGggeW91XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAge2lzTG9hZGluZyA/IChcbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5Mb2FkaW5nIG91dHB1dCBzdHlsZXPigKY8L1RleHQ+XG4gICAgICAgICkgOiAoXG4gICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgb3B0aW9ucz17c3R5bGVPcHRpb25zfVxuICAgICAgICAgICAgb25DaGFuZ2U9e2hhbmRsZVN0eWxlU2VsZWN0fVxuICAgICAgICAgICAgdmlzaWJsZU9wdGlvbkNvdW50PXsxMH1cbiAgICAgICAgICAgIGRlZmF1bHRWYWx1ZT17aW5pdGlhbFN0eWxlfVxuICAgICAgICAgIC8+XG4gICAgICAgICl9XG4gICAgICA8L0JveD5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxXQUFXLEVBQUVDLFNBQVMsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDeEQsU0FDRUMsa0JBQWtCLEVBQ2xCQyxtQkFBbUIsRUFDbkIsS0FBS0MsaUJBQWlCLFFBQ2pCLDhCQUE4QjtBQUNyQyxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLGNBQWNDLFdBQVcsUUFBUSxvQkFBb0I7QUFDckQsU0FBU0MsTUFBTSxRQUFRLGlCQUFpQjtBQUN4QyxjQUFjQyxxQkFBcUIsUUFBUSwwQkFBMEI7QUFDckUsU0FBU0MsTUFBTSxRQUFRLDBCQUEwQjtBQUNqRCxTQUFTQyxNQUFNLFFBQVEsMkJBQTJCO0FBRWxELE1BQU1DLDBCQUEwQixHQUFHLFNBQVM7QUFDNUMsTUFBTUMsZ0NBQWdDLEdBQ3BDLDBFQUEwRTtBQUU1RSxTQUFTQyxtQkFBbUJBLENBQUNDLE1BQU0sRUFBRTtFQUNuQyxDQUFDQyxTQUFTLEVBQUUsTUFBTSxDQUFDLEVBQUVaLGlCQUFpQixHQUFHLElBQUk7QUFDL0MsQ0FBQyxDQUFDLEVBQUVLLHFCQUFxQixFQUFFLENBQUM7RUFDMUIsT0FBT1EsTUFBTSxDQUFDQyxPQUFPLENBQUNILE1BQU0sQ0FBQyxDQUFDSSxHQUFHLENBQUMsQ0FBQyxDQUFDQyxLQUFLLEVBQUVDLE1BQU0sQ0FBQyxNQUFNO0lBQ3REQyxLQUFLLEVBQUVELE1BQU0sRUFBRUUsSUFBSSxJQUFJWCwwQkFBMEI7SUFDakRZLEtBQUssRUFBRUosS0FBSztJQUNaSyxXQUFXLEVBQUVKLE1BQU0sRUFBRUksV0FBVyxJQUFJWjtFQUN0QyxDQUFDLENBQUMsQ0FBQztBQUNMO0FBRUEsT0FBTyxLQUFLYSxzQkFBc0IsR0FBRztFQUNuQ0MsWUFBWSxFQUFFcEIsV0FBVztFQUN6QnFCLFVBQVUsRUFBRSxDQUFDUixLQUFLLEVBQUViLFdBQVcsRUFBRSxHQUFHLElBQUk7RUFDeENzQixRQUFRLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDcEJDLG1CQUFtQixDQUFDLEVBQUUsT0FBTztBQUMvQixDQUFDO0FBRUQsT0FBTyxTQUFBQyxrQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUEyQjtJQUFBUCxZQUFBO0lBQUFDLFVBQUE7SUFBQUMsUUFBQTtJQUFBQztFQUFBLElBQUFFLEVBS1Q7RUFBQSxJQUFBRyxFQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBRyxNQUFBLENBQUFDLEdBQUE7SUFDbURGLEVBQUEsS0FBRTtJQUFBRixDQUFBLE1BQUFFLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFGLENBQUE7RUFBQTtFQUE1RSxPQUFBSyxZQUFBLEVBQUFDLGVBQUEsSUFBd0N0QyxRQUFRLENBQTBCa0MsRUFBRSxDQUFDO0VBQzdFLE9BQUFLLFNBQUEsRUFBQUMsWUFBQSxJQUFrQ3hDLFFBQVEsQ0FBQyxJQUFJLENBQUM7RUFBQSxJQUFBeUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBVixDQUFBLFFBQUFHLE1BQUEsQ0FBQUMsR0FBQTtJQUV0Q0ssRUFBQSxHQUFBQSxDQUFBO01BRVJ4QyxrQkFBa0IsQ0FBQ00sTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFBb0MsSUFDckIsQ0FBQ0MsU0FBQTtRQUNKLE1BQUFDLE9BQUEsR0FBZ0JoQyxtQkFBbUIsQ0FBQytCLFNBQVMsQ0FBQztRQUM5Q04sZUFBZSxDQUFDTyxPQUFPLENBQUM7UUFDeEJMLFlBQVksQ0FBQyxLQUFLLENBQUM7TUFBQSxDQUNwQixDQUFDLENBQUFNLEtBQ0ksQ0FBQztRQUVMLE1BQUFDLGNBQUEsR0FBdUJsQyxtQkFBbUIsQ0FBQ1gsbUJBQW1CLENBQUM7UUFDL0RvQyxlQUFlLENBQUNTLGNBQWMsQ0FBQztRQUMvQlAsWUFBWSxDQUFDLEtBQUssQ0FBQztNQUFBLENBQ3BCLENBQUM7SUFBQSxDQUNMO0lBQUVFLEVBQUEsS0FBRTtJQUFBVixDQUFBLE1BQUFTLEVBQUE7SUFBQVQsQ0FBQSxNQUFBVSxFQUFBO0VBQUE7SUFBQUQsRUFBQSxHQUFBVCxDQUFBO0lBQUFVLEVBQUEsR0FBQVYsQ0FBQTtFQUFBO0VBZExqQyxTQUFTLENBQUMwQyxFQWNULEVBQUVDLEVBQUUsQ0FBQztFQUFBLElBQUFNLEVBQUE7RUFBQSxJQUFBaEIsQ0FBQSxRQUFBTCxVQUFBO0lBR0pxQixFQUFBLEdBQUE3QixLQUFBO01BQ0UsTUFBQThCLFdBQUEsR0FBb0I5QixLQUFLLElBQUliLFdBQVc7TUFDeENxQixVQUFVLENBQUNzQixXQUFXLENBQUM7SUFBQSxDQUN4QjtJQUFBakIsQ0FBQSxNQUFBTCxVQUFBO0lBQUFLLENBQUEsTUFBQWdCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFoQixDQUFBO0VBQUE7RUFKSCxNQUFBa0IsaUJBQUEsR0FBMEJGLEVBTXpCO0VBTW1CLE1BQUFHLEVBQUEsSUFBQ3RCLG1CQUFtQjtFQUN4QixNQUFBdUIsRUFBQSxJQUFDdkIsbUJBQW1CO0VBQUEsSUFBQXdCLEVBQUE7RUFBQSxJQUFBckIsQ0FBQSxRQUFBRyxNQUFBLENBQUFDLEdBQUE7SUFHOUJpQixFQUFBLElBQUMsR0FBRyxDQUFZLFNBQUMsQ0FBRCxHQUFDLENBQ2YsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLGtEQUVmLEVBRkMsSUFBSSxDQUdQLEVBSkMsR0FBRyxDQUlFO0lBQUFyQixDQUFBLE1BQUFxQixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBckIsQ0FBQTtFQUFBO0VBQUEsSUFBQXNCLEVBQUE7RUFBQSxJQUFBdEIsQ0FBQSxRQUFBa0IsaUJBQUEsSUFBQWxCLENBQUEsUUFBQU4sWUFBQSxJQUFBTSxDQUFBLFFBQUFPLFNBQUEsSUFBQVAsQ0FBQSxRQUFBSyxZQUFBO0lBTFJpQixFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQU0sR0FBQyxDQUFELEdBQUMsQ0FDaEMsQ0FBQUQsRUFJSyxDQUNKLENBQUFkLFNBQVMsR0FDUixDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsc0JBQXNCLEVBQXBDLElBQUksQ0FRTixHQU5DLENBQUMsTUFBTSxDQUNJRixPQUFZLENBQVpBLGFBQVcsQ0FBQyxDQUNYYSxRQUFpQixDQUFqQkEsa0JBQWdCLENBQUMsQ0FDUCxrQkFBRSxDQUFGLEdBQUMsQ0FBQyxDQUNSeEIsWUFBWSxDQUFaQSxhQUFXLENBQUMsR0FFOUIsQ0FDRixFQWhCQyxHQUFHLENBZ0JFO0lBQUFNLENBQUEsTUFBQWtCLGlCQUFBO0lBQUFsQixDQUFBLE1BQUFOLFlBQUE7SUFBQU0sQ0FBQSxNQUFBTyxTQUFBO0lBQUFQLENBQUEsTUFBQUssWUFBQTtJQUFBTCxDQUFBLE9BQUFzQixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBdEIsQ0FBQTtFQUFBO0VBQUEsSUFBQXVCLEVBQUE7RUFBQSxJQUFBdkIsQ0FBQSxTQUFBSixRQUFBLElBQUFJLENBQUEsU0FBQW1CLEVBQUEsSUFBQW5CLENBQUEsU0FBQW9CLEVBQUEsSUFBQXBCLENBQUEsU0FBQXNCLEVBQUE7SUF0QlJDLEVBQUEsSUFBQyxNQUFNLENBQ0MsS0FBd0IsQ0FBeEIsd0JBQXdCLENBQ3BCM0IsUUFBUSxDQUFSQSxTQUFPLENBQUMsQ0FDRixjQUFvQixDQUFwQixDQUFBdUIsRUFBbUIsQ0FBQyxDQUN4QixVQUFvQixDQUFwQixDQUFBQyxFQUFtQixDQUFDLENBRWhDLENBQUFFLEVBZ0JLLENBQ1AsRUF2QkMsTUFBTSxDQXVCRTtJQUFBdEIsQ0FBQSxPQUFBSixRQUFBO0lBQUFJLENBQUEsT0FBQW1CLEVBQUE7SUFBQW5CLENBQUEsT0FBQW9CLEVBQUE7SUFBQXBCLENBQUEsT0FBQXNCLEVBQUE7SUFBQXRCLENBQUEsT0FBQXVCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUF2QixDQUFBO0VBQUE7RUFBQSxPQXZCVHVCLEVBdUJTO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/PackageManagerAutoUpdater.tsx b/components/PackageManagerAutoUpdater.tsx new file mode 100644 index 0000000..a8681ab --- /dev/null +++ b/components/PackageManagerAutoUpdater.tsx @@ -0,0 +1,104 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useState } from 'react'; +import { useInterval } from 'usehooks-ts'; +import { Text } from '../ink.js'; +import { type AutoUpdaterResult, getLatestVersionFromGcs, getMaxVersion, shouldSkipVersion } from '../utils/autoUpdater.js'; +import { isAutoUpdaterDisabled } from '../utils/config.js'; +import { logForDebugging } from '../utils/debug.js'; +import { getPackageManager, type PackageManager } from '../utils/nativeInstaller/packageManagers.js'; +import { gt, gte } from '../utils/semver.js'; +import { getInitialSettings } from '../utils/settings/settings.js'; +type Props = { + isUpdating: boolean; + onChangeIsUpdating: (isUpdating: boolean) => void; + onAutoUpdaterResult: (autoUpdaterResult: AutoUpdaterResult) => void; + autoUpdaterResult: AutoUpdaterResult | null; + showSuccessMessage: boolean; + verbose: boolean; +}; +export function PackageManagerAutoUpdater(t0) { + const $ = _c(10); + const { + verbose + } = t0; + const [updateAvailable, setUpdateAvailable] = useState(false); + const [packageManager, setPackageManager] = useState("unknown"); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = async () => { + false || false; + if (isAutoUpdaterDisabled()) { + return; + } + const [channel, pm] = await Promise.all([Promise.resolve(getInitialSettings()?.autoUpdatesChannel ?? "latest"), getPackageManager()]); + setPackageManager(pm); + let latest = await getLatestVersionFromGcs(channel); + const maxVersion = await getMaxVersion(); + if (maxVersion && latest && gt(latest, maxVersion)) { + logForDebugging(`PackageManagerAutoUpdater: maxVersion ${maxVersion} is set, capping update from ${latest} to ${maxVersion}`); + if (gte(MACRO.VERSION, maxVersion)) { + logForDebugging(`PackageManagerAutoUpdater: current version ${MACRO.VERSION} is already at or above maxVersion ${maxVersion}, skipping update`); + setUpdateAvailable(false); + return; + } + latest = maxVersion; + } + const hasUpdate = latest && !gte(MACRO.VERSION, latest) && !shouldSkipVersion(latest); + setUpdateAvailable(!!hasUpdate); + if (hasUpdate) { + logForDebugging(`PackageManagerAutoUpdater: Update available ${MACRO.VERSION} -> ${latest}`); + } + }; + $[0] = t1; + } else { + t1 = $[0]; + } + const checkForUpdates = t1; + let t2; + let t3; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => { + checkForUpdates(); + }; + t3 = [checkForUpdates]; + $[1] = t2; + $[2] = t3; + } else { + t2 = $[1]; + t3 = $[2]; + } + React.useEffect(t2, t3); + useInterval(checkForUpdates, 1800000); + if (!updateAvailable) { + return null; + } + const updateCommand = packageManager === "homebrew" ? "brew upgrade claude-code" : packageManager === "winget" ? "winget upgrade Anthropic.ClaudeCode" : packageManager === "apk" ? "apk upgrade claude-code" : "your package manager update command"; + let t4; + if ($[3] !== verbose) { + t4 = verbose && currentVersion: {MACRO.VERSION}; + $[3] = verbose; + $[4] = t4; + } else { + t4 = $[4]; + } + let t5; + if ($[5] !== updateCommand) { + t5 = Update available! Run: {updateCommand}; + $[5] = updateCommand; + $[6] = t5; + } else { + t5 = $[6]; + } + let t6; + if ($[7] !== t4 || $[8] !== t5) { + t6 = <>{t4}{t5}; + $[7] = t4; + $[8] = t5; + $[9] = t6; + } else { + t6 = $[9]; + } + return t6; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZVN0YXRlIiwidXNlSW50ZXJ2YWwiLCJUZXh0IiwiQXV0b1VwZGF0ZXJSZXN1bHQiLCJnZXRMYXRlc3RWZXJzaW9uRnJvbUdjcyIsImdldE1heFZlcnNpb24iLCJzaG91bGRTa2lwVmVyc2lvbiIsImlzQXV0b1VwZGF0ZXJEaXNhYmxlZCIsImxvZ0ZvckRlYnVnZ2luZyIsImdldFBhY2thZ2VNYW5hZ2VyIiwiUGFja2FnZU1hbmFnZXIiLCJndCIsImd0ZSIsImdldEluaXRpYWxTZXR0aW5ncyIsIlByb3BzIiwiaXNVcGRhdGluZyIsIm9uQ2hhbmdlSXNVcGRhdGluZyIsIm9uQXV0b1VwZGF0ZXJSZXN1bHQiLCJhdXRvVXBkYXRlclJlc3VsdCIsInNob3dTdWNjZXNzTWVzc2FnZSIsInZlcmJvc2UiLCJQYWNrYWdlTWFuYWdlckF1dG9VcGRhdGVyIiwidDAiLCIkIiwiX2MiLCJ1cGRhdGVBdmFpbGFibGUiLCJzZXRVcGRhdGVBdmFpbGFibGUiLCJwYWNrYWdlTWFuYWdlciIsInNldFBhY2thZ2VNYW5hZ2VyIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJjaGFubmVsIiwicG0iLCJQcm9taXNlIiwiYWxsIiwicmVzb2x2ZSIsImF1dG9VcGRhdGVzQ2hhbm5lbCIsImxhdGVzdCIsIm1heFZlcnNpb24iLCJNQUNSTyIsIlZFUlNJT04iLCJoYXNVcGRhdGUiLCJjaGVja0ZvclVwZGF0ZXMiLCJ0MiIsInQzIiwidXNlRWZmZWN0IiwidXBkYXRlQ29tbWFuZCIsInQ0IiwidDUiLCJ0NiJdLCJzb3VyY2VzIjpbIlBhY2thZ2VNYW5hZ2VyQXV0b1VwZGF0ZXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUludGVydmFsIH0gZnJvbSAndXNlaG9va3MtdHMnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBdXRvVXBkYXRlclJlc3VsdCxcbiAgZ2V0TGF0ZXN0VmVyc2lvbkZyb21HY3MsXG4gIGdldE1heFZlcnNpb24sXG4gIHNob3VsZFNraXBWZXJzaW9uLFxufSBmcm9tICcuLi91dGlscy9hdXRvVXBkYXRlci5qcydcbmltcG9ydCB7IGlzQXV0b1VwZGF0ZXJEaXNhYmxlZCB9IGZyb20gJy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0UGFja2FnZU1hbmFnZXIsXG4gIHR5cGUgUGFja2FnZU1hbmFnZXIsXG59IGZyb20gJy4uL3V0aWxzL25hdGl2ZUluc3RhbGxlci9wYWNrYWdlTWFuYWdlcnMuanMnXG5pbXBvcnQgeyBndCwgZ3RlIH0gZnJvbSAnLi4vdXRpbHMvc2VtdmVyLmpzJ1xuaW1wb3J0IHsgZ2V0SW5pdGlhbFNldHRpbmdzIH0gZnJvbSAnLi4vdXRpbHMvc2V0dGluZ3Mvc2V0dGluZ3MuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGlzVXBkYXRpbmc6IGJvb2xlYW5cbiAgb25DaGFuZ2VJc1VwZGF0aW5nOiAoaXNVcGRhdGluZzogYm9vbGVhbikgPT4gdm9pZFxuICBvbkF1dG9VcGRhdGVyUmVzdWx0OiAoYXV0b1VwZGF0ZXJSZXN1bHQ6IEF1dG9VcGRhdGVyUmVzdWx0KSA9PiB2b2lkXG4gIGF1dG9VcGRhdGVyUmVzdWx0OiBBdXRvVXBkYXRlclJlc3VsdCB8IG51bGxcbiAgc2hvd1N1Y2Nlc3NNZXNzYWdlOiBib29sZWFuXG4gIHZlcmJvc2U6IGJvb2xlYW5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFBhY2thZ2VNYW5hZ2VyQXV0b1VwZGF0ZXIoeyB2ZXJib3NlIH06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3VwZGF0ZUF2YWlsYWJsZSwgc2V0VXBkYXRlQXZhaWxhYmxlXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbcGFja2FnZU1hbmFnZXIsIHNldFBhY2thZ2VNYW5hZ2VyXSA9XG4gICAgdXNlU3RhdGU8UGFja2FnZU1hbmFnZXI+KCd1bmtub3duJylcblxuICBjb25zdCBjaGVja0ZvclVwZGF0ZXMgPSBSZWFjdC51c2VDYWxsYmFjayhhc3luYyAoKSA9PiB7XG4gICAgaWYgKFxuICAgICAgXCJwcm9kdWN0aW9uXCIgPT09ICd0ZXN0JyB8fFxuICAgICAgXCJwcm9kdWN0aW9uXCIgPT09ICdkZXZlbG9wbWVudCdcbiAgICApIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChpc0F1dG9VcGRhdGVyRGlzYWJsZWQoKSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3QgW2NoYW5uZWwsIHBtXSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgIFByb21pc2UucmVzb2x2ZShnZXRJbml0aWFsU2V0dGluZ3MoKT8uYXV0b1VwZGF0ZXNDaGFubmVsID8/ICdsYXRlc3QnKSxcbiAgICAgIGdldFBhY2thZ2VNYW5hZ2VyKCksXG4gICAgXSlcbiAgICBzZXRQYWNrYWdlTWFuYWdlcihwbSlcblxuICAgIGxldCBsYXRlc3QgPSBhd2FpdCBnZXRMYXRlc3RWZXJzaW9uRnJvbUdjcyhjaGFubmVsKVxuXG4gICAgLy8gQ2hlY2sgaWYgbWF4IHZlcnNpb24gaXMgc2V0IChzZXJ2ZXItc2lkZSBraWxsIHN3aXRjaCBmb3IgYXV0by11cGRhdGVzKVxuICAgIGNvbnN0IG1heFZlcnNpb24gPSBhd2FpdCBnZXRNYXhWZXJzaW9uKClcblxuICAgIGlmIChtYXhWZXJzaW9uICYmIGxhdGVzdCAmJiBndChsYXRlc3QsIG1heFZlcnNpb24pKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgIGBQYWNrYWdlTWFuYWdlckF1dG9VcGRhdGVyOiBtYXhWZXJzaW9uICR7bWF4VmVyc2lvbn0gaXMgc2V0LCBjYXBwaW5nIHVwZGF0ZSBmcm9tICR7bGF0ZXN0fSB0byAke21heFZlcnNpb259YCxcbiAgICAgIClcbiAgICAgIGlmIChndGUoTUFDUk8uVkVSU0lPTiwgbWF4VmVyc2lvbikpIHtcbiAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgIGBQYWNrYWdlTWFuYWdlckF1dG9VcGRhdGVyOiBjdXJyZW50IHZlcnNpb24gJHtNQUNSTy5WRVJTSU9OfSBpcyBhbHJlYWR5IGF0IG9yIGFib3ZlIG1heFZlcnNpb24gJHttYXhWZXJzaW9ufSwgc2tpcHBpbmcgdXBkYXRlYCxcbiAgICAgICAgKVxuICAgICAgICBzZXRVcGRhdGVBdmFpbGFibGUoZmFsc2UpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuICAgICAgbGF0ZXN0ID0gbWF4VmVyc2lvblxuICAgIH1cblxuICAgIGNvbnN0IGhhc1VwZGF0ZSA9XG4gICAgICBsYXRlc3QgJiYgIWd0ZShNQUNSTy5WRVJTSU9OLCBsYXRlc3QpICYmICFzaG91bGRTa2lwVmVyc2lvbihsYXRlc3QpXG5cbiAgICBzZXRVcGRhdGVBdmFpbGFibGUoISFoYXNVcGRhdGUpXG5cbiAgICBpZiAoaGFzVXBkYXRlKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgIGBQYWNrYWdlTWFuYWdlckF1dG9VcGRhdGVyOiBVcGRhdGUgYXZhaWxhYmxlICR7TUFDUk8uVkVSU0lPTn0gLT4gJHtsYXRlc3R9YCxcbiAgICAgIClcbiAgICB9XG4gIH0sIFtdKVxuXG4gIC8vIEluaXRpYWwgY2hlY2tcbiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHtcbiAgICB2b2lkIGNoZWNrRm9yVXBkYXRlcygpXG4gIH0sIFtjaGVja0ZvclVwZGF0ZXNdKVxuXG4gIC8vIENoZWNrIGV2ZXJ5IDMwIG1pbnV0ZXNcbiAgdXNlSW50ZXJ2YWwoY2hlY2tGb3JVcGRhdGVzLCAzMCAqIDYwICogMTAwMClcblxuICBpZiAoIXVwZGF0ZUF2YWlsYWJsZSkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBwYWNtYW4sIGRlYiwgYW5kIHJwbSBkb24ndCBnZXQgc3BlY2lmaWMgY29tbWFuZHMgYmVjYXVzZSB0aGV5IGVhY2ggaGF2ZVxuICAvLyBtdWx0aXBsZSBmcm9udGVuZHMgKHBhY21hbjogeWF5L3BhcnUvbWFrZXBrZywgZGViOiBhcHQvYXB0LWdldC9hcHRpdHVkZS9uYWxhLFxuICAvLyBycG06IGRuZi95dW0venlwcGVyKVxuICBjb25zdCB1cGRhdGVDb21tYW5kID1cbiAgICBwYWNrYWdlTWFuYWdlciA9PT0gJ2hvbWVicmV3J1xuICAgICAgPyAnYnJldyB1cGdyYWRlIGNsYXVkZS1jb2RlJ1xuICAgICAgOiBwYWNrYWdlTWFuYWdlciA9PT0gJ3dpbmdldCdcbiAgICAgICAgPyAnd2luZ2V0IHVwZ3JhZGUgQW50aHJvcGljLkNsYXVkZUNvZGUnXG4gICAgICAgIDogcGFja2FnZU1hbmFnZXIgPT09ICdhcGsnXG4gICAgICAgICAgPyAnYXBrIHVwZ3JhZGUgY2xhdWRlLWNvZGUnXG4gICAgICAgICAgOiAneW91ciBwYWNrYWdlIG1hbmFnZXIgdXBkYXRlIGNvbW1hbmQnXG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAge3ZlcmJvc2UgJiYgKFxuICAgICAgICA8VGV4dCBkaW1Db2xvciB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICBjdXJyZW50VmVyc2lvbjoge01BQ1JPLlZFUlNJT059XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICl9XG4gICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIiB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgVXBkYXRlIGF2YWlsYWJsZSEgUnVuOiA8VGV4dCBib2xkPnt1cGRhdGVDb21tYW5kfTwvVGV4dD5cbiAgICAgIDwvVGV4dD5cbiAgICA8Lz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxRQUFRLFFBQVEsT0FBTztBQUNoQyxTQUFTQyxXQUFXLFFBQVEsYUFBYTtBQUN6QyxTQUFTQyxJQUFJLFFBQVEsV0FBVztBQUNoQyxTQUNFLEtBQUtDLGlC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 \ No newline at end of file diff --git a/components/Passes/Passes.tsx b/components/Passes/Passes.tsx new file mode 100644 index 0000000..fe47f9d --- /dev/null +++ b/components/Passes/Passes.tsx @@ -0,0 +1,184 @@ +import * as React from 'react'; +import { useCallback, useEffect, useState } from 'react'; +import type { CommandResultDisplay } from '../../commands.js'; +import { TEARDROP_ASTERISK } from '../../constants/figures.js'; +import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { setClipboard } from '../../ink/termio/osc.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- enter to copy link +import { Box, Link, Text, useInput } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { logEvent } from '../../services/analytics/index.js'; +import { fetchReferralRedemptions, formatCreditAmount, getCachedOrFetchPassesEligibility } from '../../services/api/referral.js'; +import type { ReferralRedemptionsResponse, ReferrerRewardInfo } from '../../services/oauth/types.js'; +import { count } from '../../utils/array.js'; +import { logError } from '../../utils/log.js'; +import { Pane } from '../design-system/Pane.js'; +type PassStatus = { + passNumber: number; + isAvailable: boolean; +}; +type Props = { + onDone: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; +}; +export function Passes({ + onDone +}: Props): React.ReactNode { + const [loading, setLoading] = useState(true); + const [passStatuses, setPassStatuses] = useState([]); + const [isAvailable, setIsAvailable] = useState(false); + const [referralLink, setReferralLink] = useState(null); + const [referrerReward, setReferrerReward] = useState(undefined); + const exitState = useExitOnCtrlCDWithKeybindings(() => onDone('Guest passes dialog dismissed', { + display: 'system' + })); + const handleCancel = useCallback(() => { + onDone('Guest passes dialog dismissed', { + display: 'system' + }); + }, [onDone]); + useKeybinding('confirm:no', handleCancel, { + context: 'Confirmation' + }); + useInput((_input, key) => { + if (key.return && referralLink) { + void setClipboard(referralLink).then(raw => { + if (raw) process.stdout.write(raw); + logEvent('tengu_guest_passes_link_copied', {}); + onDone(`Referral link copied to clipboard!`); + }); + } + }); + useEffect(() => { + async function loadPassesData() { + try { + // Check eligibility first (uses cache if available) + const eligibilityData = await getCachedOrFetchPassesEligibility(); + if (!eligibilityData || !eligibilityData.eligible) { + setIsAvailable(false); + setLoading(false); + return; + } + setIsAvailable(true); + + // Store the referral link if available + if (eligibilityData.referral_code_details?.referral_link) { + setReferralLink(eligibilityData.referral_code_details.referral_link); + } + + // Store referrer reward info for v1 campaign messaging + setReferrerReward(eligibilityData.referrer_reward); + + // Use the campaign returned from eligibility for redemptions + const campaign = eligibilityData.referral_code_details?.campaign ?? 'claude_code_guest_pass'; + + // Fetch redemptions data + let redemptionsData: ReferralRedemptionsResponse; + try { + redemptionsData = await fetchReferralRedemptions(campaign); + } catch (err_0) { + logError(err_0 as Error); + setIsAvailable(false); + setLoading(false); + return; + } + + // Build pass statuses array + const redemptions = redemptionsData.redemptions || []; + const maxRedemptions = redemptionsData.limit || 3; + const statuses: PassStatus[] = []; + for (let i = 0; i < maxRedemptions; i++) { + const redemption = redemptions[i]; + statuses.push({ + passNumber: i + 1, + isAvailable: !redemption + }); + } + setPassStatuses(statuses); + setLoading(false); + } catch (err) { + // For any error, just show passes as not available + logError(err as Error); + setIsAvailable(false); + setLoading(false); + } + } + void loadPassesData(); + }, []); + if (loading) { + return + + Loading guest pass information… + + {exitState.pending ? <>Press {exitState.keyName} again to exit : <>Esc to cancel} + + + ; + } + if (!isAvailable) { + return + + Guest passes are not currently available. + + {exitState.pending ? <>Press {exitState.keyName} again to exit : <>Esc to cancel} + + + ; + } + const availableCount = count(passStatuses, p => p.isAvailable); + + // Sort passes: available first, then redeemed + const sortedPasses = [...passStatuses].sort((a, b) => +b.isAvailable - +a.isAvailable); + + // ASCII art for tickets + const renderTicket = (pass: PassStatus) => { + const isRedeemed = !pass.isAvailable; + if (isRedeemed) { + // Grayed out redeemed ticket with slashes + return + {'┌─────────╱'} + {` ) CC ${TEARDROP_ASTERISK} ┊╱`} + {'└───────╱'} + ; + } + return + {'┌──────────┐'} + + {' ) CC '} + {TEARDROP_ASTERISK} + {' ┊ ( '} + + {'└──────────┘'} + ; + }; + return + + Guest passes · {availableCount} left + + + {sortedPasses.slice(0, 3).map(pass_0 => renderTicket(pass_0))} + + + {referralLink && + {referralLink} + } + + + + {referrerReward ? `Share a free week of Claude Code with friends. If they love it and subscribe, you'll get ${formatCreditAmount(referrerReward)} of extra usage to keep building. ` : 'Share a free week of Claude Code with friends. '} + + Terms apply. + + + + + + + {exitState.pending ? <>Press {exitState.keyName} again to exit : <>Enter to copy link · Esc to cancel} + + + + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlRWZmZWN0IiwidXNlU3RhdGUiLCJDb21tYW5kUmVzdWx0RGlzcGxheSIsIlRFQVJEUk9QX0FTVEVSSVNLIiwidXNlRXhpdE9uQ3RybENEV2l0aEtleWJpbmRpbmdzIiwic2V0Q2xpcGJvYXJkIiwiQm94IiwiTGluayIsIlRleHQiLCJ1c2VJbnB1dCIsInVzZUtleWJpbmRpbmciLCJsb2dFdmVudCIsImZldGNoUmVmZXJyYWxSZWRlbXB0aW9ucyIsImZvcm1hdENyZWRpdEFtb3VudCIsImdldENhY2hlZE9yRmV0Y2hQYXNzZXNFbGlnaWJpbGl0eSIsIlJlZmVycmFsUmVkZW1wdGlvbnNSZXNwb25zZSIsIlJlZmVycmVyUmV3YXJkSW5mbyIsImNvdW50IiwibG9nRXJyb3IiLCJQYW5lIiwiUGFzc1N0YXR1cyIsInBhc3NOdW1iZXIiLCJpc0F2YWlsYWJsZSIsIlByb3BzIiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJQYXNzZXMiLCJSZWFjdE5vZGUiLCJsb2FkaW5nIiwic2V0TG9hZGluZyIsInBhc3NTdGF0dXNlcyIsInNldFBhc3NTdGF0dXNlcyIsInNldElzQXZhaWxhYmxlIiwicmVmZXJyYWxMaW5rIiwic2V0UmVmZXJyYWxMaW5rIiwicmVmZXJyZXJSZXdhcmQiLCJzZXRSZWZlcnJlclJld2FyZCIsInVuZGVmaW5lZCIsImV4aXRTdGF0ZSIsImhhbmRsZUNhbmNlbCIsImNvbnRleHQiLCJfaW5wdXQiLCJrZXkiLCJyZXR1cm4iLCJ0aGVuIiwicmF3IiwicHJvY2VzcyIsInN0ZG91dCIsIndyaXRlIiwibG9hZFBhc3Nlc0RhdGEiLCJlbGlnaWJpbGl0eURhdGEiLCJlbGlnaWJsZSIsInJlZmVycmFsX2NvZGVfZGV0YWlscyIsInJlZmVycmFsX2xpbmsiLCJyZWZlcnJlcl9yZXdhcmQiLCJjYW1wYWlnbiIsInJlZGVtcHRpb25zRGF0YSIsImVyciIsIkVycm9yIiwicmVkZW1wdGlvbnMiLCJtYXhSZWRlbXB0aW9ucyIsImxpbWl0Iiwic3RhdHVzZXMiLCJpIiwicmVkZW1wdGlvbiIsInB1c2giLCJwZW5kaW5nIiwia2V5TmFtZSIsImF2YWlsYWJsZUNvdW50IiwicCIsInNvcnRlZFBhc3NlcyIsInNvcnQiLCJhIiwiYiIsInJlbmRlclRpY2tldCIsInBhc3MiLCJpc1JlZGVlbWVkIiwic2xpY2UiLCJtYXAiXSwic291cmNlcyI6WyJQYXNzZXMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgQ29tbWFuZFJlc3VsdERpc3BsYXkgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IFRFQVJEUk9QX0FTVEVSSVNLIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL2ZpZ3VyZXMuanMnXG5pbXBvcnQgeyB1c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MgfSBmcm9tICcuLi8uLi9ob29rcy91c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MuanMnXG5pbXBvcnQgeyBzZXRDbGlwYm9hcmQgfSBmcm9tICcuLi8uLi9pbmsvdGVybWlvL29zYy5qcydcbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvcHJlZmVyLXVzZS1rZXliaW5kaW5ncyAtLSBlbnRlciB0byBjb3B5IGxpbmtcbmltcG9ydCB7IEJveCwgTGluaywgVGV4dCwgdXNlSW5wdXQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnLi4vLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHtcbiAgZmV0Y2hSZWZlcnJhbFJlZGVtcHRpb25zLFxuICBmb3JtYXRDcmVkaXRBbW91bnQsXG4gIGdldENhY2hlZE9yRmV0Y2hQYXNzZXNFbGlnaWJpbGl0eSxcbn0gZnJvbSAnLi4vLi4vc2VydmljZXMvYXBpL3JlZmVycmFsLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBSZWZlcnJhbFJlZGVtcHRpb25zUmVzcG9uc2UsXG4gIFJlZmVycmVyUmV3YXJkSW5mbyxcbn0gZnJvbSAnLi4vLi4vc2VydmljZXMvb2F1dGgvdHlwZXMuanMnXG5pbXBvcnQgeyBjb3VudCB9IGZyb20gJy4uLy4uL3V0aWxzL2FycmF5LmpzJ1xuaW1wb3J0IHsgbG9nRXJyb3IgfSBmcm9tICcuLi8uLi91dGlscy9sb2cuanMnXG5pbXBvcnQgeyBQYW5lIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9QYW5lLmpzJ1xuXG50eXBlIFBhc3NTdGF0dXMgPSB7XG4gIHBhc3NOdW1iZXI6IG51bWJlclxuICBpc0F2YWlsYWJsZTogYm9vbGVhblxufVxuXG50eXBlIFByb3BzID0ge1xuICBvbkRvbmU6IChcbiAgICByZXN1bHQ/OiBzdHJpbmcsXG4gICAgb3B0aW9ucz86IHsgZGlzcGxheT86IENvbW1hbmRSZXN1bHREaXNwbGF5IH0sXG4gICkgPT4gdm9pZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gUGFzc2VzKHsgb25Eb25lIH06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW2xvYWRpbmcsIHNldExvYWRpbmddID0gdXNlU3RhdGUodHJ1ZSlcbiAgY29uc3QgW3Bhc3NTdGF0dXNlcywgc2V0UGFzc1N0YXR1c2VzXSA9IHVzZVN0YXRlPFBhc3NTdGF0dXNbXT4oW10pXG4gIGNvbnN0IFtpc0F2YWlsYWJsZSwgc2V0SXNBdmFpbGFibGVdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtyZWZlcnJhbExpbmssIHNldFJlZmVycmFsTGlua10gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICBjb25zdCBbcmVmZXJyZXJSZXdhcmQsIHNldFJlZmVycmVyUmV3YXJkXSA9IHVzZVN0YXRlPFxuICAgIFJlZmVycmVyUmV3YXJkSW5mbyB8IG51bGwgfCB1bmRlZmluZWRcbiAgPih1bmRlZmluZWQpXG5cbiAgY29uc3QgZXhpdFN0YXRlID0gdXNlRXhpdE9uQ3RybENEV2l0aEtleWJpbmRpbmdzKCgpID0+XG4gICAgb25Eb25lKCdHdWVzdCBwYXNzZXMgZGlhbG9nIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSksXG4gIClcblxuICBjb25zdCBoYW5kbGVDYW5jZWwgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgb25Eb25lKCdHdWVzdCBwYXNzZXMgZGlhbG9nIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgfSwgW29uRG9uZV0pXG5cbiAgdXNlS2V5YmluZGluZygnY29uZmlybTpubycsIGhhbmRsZUNhbmNlbCwgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJyB9KVxuXG4gIHVzZUlucHV0KChfaW5wdXQsIGtleSkgPT4ge1xuICAgIGlmIChrZXkucmV0dXJuICYmIHJlZmVycmFsTGluaykge1xuICAgICAgdm9pZCBzZXRDbGlwYm9hcmQocmVmZXJyYWxMaW5rKS50aGVuKHJhdyA9PiB7XG4gICAgICAgIGlmIChyYXcpIHByb2Nlc3Muc3Rkb3V0LndyaXRlKHJhdylcbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2d1ZXN0X3Bhc3Nlc19saW5rX2NvcGllZCcsIHt9KVxuICAgICAgICBvbkRvbmUoYFJlZmVycmFsIGxpbmsgY29waWVkIHRvIGNsaXBib2FyZCFgKVxuICAgICAgfSlcbiAgICB9XG4gIH0pXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBhc3luYyBmdW5jdGlvbiBsb2FkUGFzc2VzRGF0YSgpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIENoZWNrIGVsaWdpYmlsaXR5IGZpcnN0ICh1c2VzIGNhY2hlIGlmIGF2YWlsYWJsZSlcbiAgICAgICAgY29uc3QgZWxpZ2liaWxpdHlEYXRhID0gYXdhaXQgZ2V0Q2FjaGVkT3JGZXRjaFBhc3Nlc0VsaWdpYmlsaXR5KClcblxuICAgICAgICBpZiAoIWVsaWdpYmlsaXR5RGF0YSB8fCAhZWxpZ2liaWxpdHlEYXRhLmVsaWdpYmxlKSB7XG4gICAgICAgICAgc2V0SXNBdmFpbGFibGUoZmFsc2UpXG4gICAgICAgICAgc2V0TG9hZGluZyhmYWxzZSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIHNldElzQXZhaWxhYmxlKHRydWUpXG5cbiAgICAgICAgLy8gU3RvcmUgdGhlIHJlZmVycmFsIGxpbmsgaWYgYXZhaWxhYmxlXG4gICAgICAgIGlmIChlbGlnaWJpbGl0eURhdGEucmVmZXJyYWxfY29kZV9kZXRhaWxzPy5yZWZlcnJhbF9saW5rKSB7XG4gICAgICAgICAgc2V0UmVmZXJyYWxMaW5rKGVsaWdpYmlsaXR5RGF0YS5yZWZlcnJhbF9jb2RlX2RldGFpbHMucmVmZXJyYWxfbGluaylcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFN0b3JlIHJlZmVycmVyIHJld2FyZCBpbmZvIGZvciB2MSBjYW1wYWlnbiBtZXNzYWdpbmdcbiAgICAgICAgc2V0UmVmZXJyZXJSZXdhcmQoZWxpZ2liaWxpdHlEYXRhLnJlZmVycmVyX3Jld2FyZClcblxuICAgICAgICAvLyBVc2UgdGhlIGNhbXBhaWduIHJldHVybmVkIGZyb20gZWxpZ2liaWxpdHkgZm9yIHJlZGVtcHRpb25zXG4gICAgICAgIGNvbnN0IGNhbXBhaWduID1cbiAgICAgICAgICBlbGlnaWJpbGl0eURhdGEucmVmZXJyYWxfY29kZV9kZXRhaWxzPy5jYW1wYWlnbiA/P1xuICAgICAgICAgICdjbGF1ZGVfY29kZV9ndWVzdF9wYXNzJ1xuXG4gICAgICAgIC8vIEZldGNoIHJlZGVtcHRpb25zIGRhdGFcbiAgICAgICAgbGV0IHJlZGVtcHRpb25zRGF0YTogUmVmZXJyYWxSZWRlbXB0aW9uc1Jlc3BvbnNlXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgcmVkZW1wdGlvbnNEYXRhID0gYXdhaXQgZmV0Y2hSZWZlcnJhbFJlZGVtcHRpb25zKGNhbXBhaWduKVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICBsb2dFcnJvcihlcnIgYXMgRXJyb3IpXG4gICAgICAgICAgc2V0SXNBdmFpbGFibGUoZmFsc2UpXG4gICAgICAgICAgc2V0TG9hZGluZyhmYWxzZSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEJ1aWxkIHBhc3Mgc3RhdHVzZXMgYXJyYXlcbiAgICAgICAgY29uc3QgcmVkZW1wdGlvbnMgPSByZWRlbXB0aW9uc0RhdGEucmVkZW1wdGlvbnMgfHwgW11cbiAgICAgICAgY29uc3QgbWF4UmVkZW1wdGlvbnMgPSByZWRlbXB0aW9uc0RhdGEubGltaXQgfHwgM1xuICAgICAgICBjb25zdCBzdGF0dXNlczogUGFzc1N0YXR1c1tdID0gW11cblxuICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IG1heFJlZGVtcHRpb25zOyBpKyspIHtcbiAgICAgICAgICBjb25zdCByZWRlbXB0aW9uID0gcmVkZW1wdGlvbnNbaV1cbiAgICAgICAgICBzdGF0dXNlcy5wdXNoKHtcbiAgICAgICAgICAgIHBhc3NOdW1iZXI6IGkgKyAxLFxuICAgICAgICAgICAgaXNBdmFpbGFibGU6ICFyZWRlbXB0aW9uLFxuICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICBzZXRQYXNzU3RhdHVzZXMoc3RhdHVzZXMpXG4gICAgICAgIHNldExvYWRpbmcoZmFsc2UpXG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgLy8gRm9yIGFueSBlcnJvciwganVzdCBzaG93IHBhc3NlcyBhcyBub3QgYXZhaWxhYmxlXG4gICAgICAgIGxvZ0Vycm9yKGVyciBhcyBFcnJvcilcbiAgICAgICAgc2V0SXNBdmFpbGFibGUoZmFsc2UpXG4gICAgICAgIHNldExvYWRpbmcoZmFsc2UpXG4gICAgICB9XG4gICAgfVxuXG4gICAgdm9pZCBsb2FkUGFzc2VzRGF0YSgpXG4gIH0sIFtdKVxuXG4gIGlmIChsb2FkaW5nKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxQYW5lPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPkxvYWRpbmcgZ3Vlc3QgcGFzcyBpbmZvcm1hdGlvbuKApjwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAgICB7ZXhpdFN0YXRlLnBlbmRpbmcgPyAoXG4gICAgICAgICAgICAgIDw+UHJlc3Mge2V4aXRTdGF0ZS5rZXlOYW1lfSBhZ2FpbiB0byBleGl0PC8+XG4gICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICA8PkVzYyB0byBjYW5jZWw8Lz5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvUGFuZT5cbiAgICApXG4gIH1cblxuICBpZiAoIWlzQXZhaWxhYmxlKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxQYW5lPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICAgIDxUZXh0Pkd1ZXN0IHBhc3NlcyBhcmUgbm90IGN1cnJlbnRseSBhdmFpbGFibGUuPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGl0YWxpYz5cbiAgICAgICAgICAgIHtleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICAgICAgPD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8Lz5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgIDw+RXNjIHRvIGNhbmNlbDwvPlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9QYW5lPlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IGF2YWlsYWJsZUNvdW50ID0gY291bnQocGFzc1N0YXR1c2VzLCBwID0+IHAuaXNBdmFpbGFibGUpXG5cbiAgLy8gU29ydCBwYXNzZXM6IGF2YWlsYWJsZSBmaXJzdCwgdGhlbiByZWRlZW1lZFxuICBjb25zdCBzb3J0ZWRQYXNzZXMgPSBbLi4ucGFzc1N0YXR1c2VzXS5zb3J0KFxuICAgIChhLCBiKSA9PiArYi5pc0F2YWlsYWJsZSAtICthLmlzQXZhaWxhYmxlLFxuICApXG5cbiAgLy8gQVNDSUkgYXJ0IGZvciB0aWNrZXRzXG4gIGNvbnN0IHJlbmRlclRpY2tldCA9IChwYXNzOiBQYXNzU3RhdHVzKSA9PiB7XG4gICAgY29uc3QgaXNSZWRlZW1lZCA9ICFwYXNzLmlzQXZhaWxhYmxlXG5cbiAgICBpZiAoaXNSZWRlZW1lZCkge1xuICAgICAgLy8gR3JheWVkIG91dCByZWRlZW1lZCB0aWNrZXQgd2l0aCBzbGFzaGVzXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8Qm94IGtleT17cGFzcy5wYXNzTnVtYmVyfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luUmlnaHQ9ezF9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPnsn4pSM4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pWxJ308L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+e2AgKSBDQyAke1RFQVJEUk9QX0FTVEVSSVNLfSDilIrilbFgfTwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57J+KUlOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKVsSd9PC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIClcbiAgICB9XG5cbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBrZXk9e3Bhc3MucGFzc051bWJlcn0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpblJpZ2h0PXsxfT5cbiAgICAgICAgPFRleHQ+eyfilIzilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilJAnfTwvVGV4dD5cbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgeycgKSBDQyAnfVxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+e1RFQVJEUk9QX0FTVEVSSVNLfTwvVGV4dD5cbiAgICAgICAgICB7JyDilIogKCAnfVxuICAgICAgICA8L1RleHQ+XG4gICAgICAgIDxUZXh0Pnsn4pSU4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSYJ308L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxQYW5lPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgPFRleHQgY29sb3I9XCJwZXJtaXNzaW9uXCI+R3Vlc3QgcGFzc2VzIMK3IHthdmFpbGFibGVDb3VudH0gbGVmdDwvVGV4dD5cblxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBtYXJnaW5MZWZ0PXsyfT5cbiAgICAgICAgICB7c29ydGVkUGFzc2VzLnNsaWNlKDAsIDMpLm1hcChwYXNzID0+IHJlbmRlclRpY2tldChwYXNzKSl9XG4gICAgICAgIDwvQm94PlxuXG4gICAgICAgIHtyZWZlcnJhbExpbmsgJiYgKFxuICAgICAgICAgIDxCb3ggbWFyZ2luTGVmdD17Mn0+XG4gICAgICAgICAgICA8VGV4dD57cmVmZXJyYWxMaW5rfTwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cblxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5MZWZ0PXsyfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHtyZWZlcnJlclJld2FyZFxuICAgICAgICAgICAgICA/IGBTaGFyZSBhIGZyZWUgd2VlayBvZiBDbGF1ZGUgQ29kZSB3aXRoIGZyaWVuZHMuIElmIHRoZXkgbG92ZSBpdCBhbmQgc3Vic2NyaWJlLCB5b3UnbGwgZ2V0ICR7Zm9ybWF0Q3JlZGl0QW1vdW50KHJlZmVycmVyUmV3YXJkKX0gb2YgZXh0cmEgdXNhZ2UgdG8ga2VlcCBidWlsZGluZy4gYFxuICAgICAgICAgICAgICA6ICdTaGFyZSBhIGZyZWUgd2VlayBvZiBDbGF1ZGUgQ29kZSB3aXRoIGZyaWVuZHMuICd9XG4gICAgICAgICAgICA8TGlua1xuICAgICAgICAgICAgICB1cmw9e1xuICAgICAgICAgICAgICAgIHJlZmVycmVyUmV3YXJkXG4gICAgICAgICAgICAgICAgICA/ICdodHRwczovL3N1cHBvcnQuY2xhdWRlLmNvbS9lbi9hcnRpY2xlcy8xMzQ1NjcwMi1jbGF1ZGUtY29kZS1ndWVzdC1wYXNzZXMnXG4gICAgICAgICAgICAgICAgICA6ICdodHRwczovL3N1cHBvcnQuY2xhdWRlLmNvbS9lbi9hcnRpY2xlcy8xMjg3NTA2MS1jbGF1ZGUtY29kZS1ndWVzdC1wYXNzZXMnXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgVGVybXMgYXBwbHkuXG4gICAgICAgICAgICA8L0xpbms+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cblxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGl0YWxpYz5cbiAgICAgICAgICAgIHtleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICAgICAgPD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8Lz5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgIDw+RW50ZXIgdG8gY29weSBsaW5rIMK3IEVzYyB0byBjYW5jZWw8Lz5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuICAgIDwvUGFuZT5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUN4RCxjQUFjQyxvQkFBb0IsUUFBUSxtQkFBbUI7QUFDN0Qs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 \ No newline at end of file diff --git a/components/PrBadge.tsx b/components/PrBadge.tsx new file mode 100644 index 0000000..2b99f31 --- /dev/null +++ b/components/PrBadge.tsx @@ -0,0 +1,97 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Link, Text } from '../ink.js'; +import type { PrReviewState } from '../utils/ghPrStatus.js'; +type Props = { + number: number; + url: string; + reviewState?: PrReviewState; + bold?: boolean; +}; +export function PrBadge(t0) { + const $ = _c(21); + const { + number, + url, + reviewState, + bold + } = t0; + let t1; + if ($[0] !== reviewState) { + t1 = getPrStatusColor(reviewState); + $[0] = reviewState; + $[1] = t1; + } else { + t1 = $[1]; + } + const statusColor = t1; + const t2 = !statusColor && !bold; + let t3; + if ($[2] !== bold || $[3] !== number || $[4] !== statusColor || $[5] !== t2) { + t3 = #{number}; + $[2] = bold; + $[3] = number; + $[4] = statusColor; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + const label = t3; + const t4 = !bold; + let t5; + if ($[7] !== t4) { + t5 = PR; + $[7] = t4; + $[8] = t5; + } else { + t5 = $[8]; + } + const t6 = !statusColor && !bold; + let t7; + if ($[9] !== bold || $[10] !== number || $[11] !== statusColor || $[12] !== t6) { + t7 = #{number}; + $[9] = bold; + $[10] = number; + $[11] = statusColor; + $[12] = t6; + $[13] = t7; + } else { + t7 = $[13]; + } + let t8; + if ($[14] !== label || $[15] !== t7 || $[16] !== url) { + t8 = {t7}; + $[14] = label; + $[15] = t7; + $[16] = url; + $[17] = t8; + } else { + t8 = $[17]; + } + let t9; + if ($[18] !== t5 || $[19] !== t8) { + t9 = {t5}{" "}{t8}; + $[18] = t5; + $[19] = t8; + $[20] = t9; + } else { + t9 = $[20]; + } + return t9; +} +function getPrStatusColor(state?: PrReviewState): 'success' | 'error' | 'warning' | 'merged' | undefined { + switch (state) { + case 'approved': + return 'success'; + case 'changes_requested': + return 'error'; + case 'pending': + return 'warning'; + case 'merged': + return 'merged'; + default: + return undefined; + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxpbmsiLCJUZXh0IiwiUHJSZXZpZXdTdGF0ZSIsIlByb3BzIiwibnVtYmVyIiwidXJsIiwicmV2aWV3U3RhdGUiLCJib2xkIiwiUHJCYWRnZSIsInQwIiwiJCIsIl9jIiwidDEiLCJnZXRQclN0YXR1c0NvbG9yIiwic3RhdHVzQ29sb3IiLCJ0MiIsInQzIiwibGFiZWwiLCJ0NCIsInQ1IiwidDYiLCJ0NyIsInQ4IiwidDkiLCJzdGF0ZSIsInVuZGVmaW5lZCJdLCJzb3VyY2VzIjpbIlByQmFkZ2UudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IExpbmssIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IFByUmV2aWV3U3RhdGUgfSBmcm9tICcuLi91dGlscy9naFByU3RhdHVzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBudW1iZXI6IG51bWJlclxuICB1cmw6IHN0cmluZ1xuICByZXZpZXdTdGF0ZT86IFByUmV2aWV3U3RhdGVcbiAgYm9sZD86IGJvb2xlYW5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFByQmFkZ2Uoe1xuICBudW1iZXIsXG4gIHVybCxcbiAgcmV2aWV3U3RhdGUsXG4gIGJvbGQsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHN0YXR1c0NvbG9yID0gZ2V0UHJTdGF0dXNDb2xvcihyZXZpZXdTdGF0ZSlcbiAgY29uc3QgbGFiZWwgPSAoXG4gICAgPFRleHQgY29sb3I9e3N0YXR1c0NvbG9yfSBkaW1Db2xvcj17IXN0YXR1c0NvbG9yICYmICFib2xkfSBib2xkPXtib2xkfT5cbiAgICAgICN7bnVtYmVyfVxuICAgIDwvVGV4dD5cbiAgKVxuICByZXR1cm4gKFxuICAgIDxUZXh0PlxuICAgICAgPFRleHQgZGltQ29sb3I9eyFib2xkfT5QUjwvVGV4dD57JyAnfVxuICAgICAgPExpbmsgdXJsPXt1cmx9IGZhbGxiYWNrPXtsYWJlbH0+XG4gICAgICAgIDxUZXh0XG4gICAgICAgICAgY29sb3I9e3N0YXR1c0NvbG9yfVxuICAgICAgICAgIGRpbUNvbG9yPXshc3RhdHVzQ29sb3IgJiYgIWJvbGR9XG4gICAgICAgICAgdW5kZXJsaW5lXG4gICAgICAgICAgYm9sZD17Ym9sZH1cbiAgICAgICAgPlxuICAgICAgICAgICN7bnVtYmVyfVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0xpbms+XG4gICAgPC9UZXh0PlxuICApXG59XG5cbmZ1bmN0aW9uIGdldFByU3RhdHVzQ29sb3IoXG4gIHN0YXRlPzogUHJSZXZpZXdTdGF0ZSxcbik6ICdzdWNjZXNzJyB8ICdlcnJvcicgfCAnd2FybmluZycgfCAnbWVyZ2VkJyB8IHVuZGVmaW5lZCB7XG4gIHN3aXRjaCAoc3RhdGUpIHtcbiAgICBjYXNlICdhcHByb3ZlZCc6XG4gICAgICByZXR1cm4gJ3N1Y2Nlc3MnXG4gICAgY2FzZSAnY2hhbmdlc19yZXF1ZXN0ZWQnOlxuICAgICAgcmV0dXJuICdlcnJvcidcbiAgICBjYXNlICdwZW5kaW5nJzpcbiAgICAgIHJldHVybiAnd2FybmluZydcbiAgICBjYXNlICdtZXJnZWQnOlxuICAgICAgcmV0dXJuICdtZXJnZWQnXG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsSUFBSSxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUN0QyxjQUFjQyxhQUFhLFFBQVEsd0JBQXdCO0FBRTNELEtBQUtDLEtBQUssR0FBRztFQUNYQyxNQUFNLEVBQUUsTUFBTTtFQUNkQyxHQUFHLEVBQUUsTUFBTTtFQUNYQyxXQUFXLENBQUMsRUFBRUosYUFBYTtFQUMzQkssSUFBSSxDQUFDLEVBQUUsT0FBTztBQUNoQixDQUFDO0FBRUQsT0FBTyxTQUFBQyxRQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWlCO0lBQUFQ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 \ No newline at end of file diff --git a/components/PressEnterToContinue.tsx b/components/PressEnterToContinue.tsx new file mode 100644 index 0000000..6df0b2e --- /dev/null +++ b/components/PressEnterToContinue.tsx @@ -0,0 +1,15 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Text } from '../ink.js'; +export function PressEnterToContinue() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = Press Enter to continue…; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJQcmVzc0VudGVyVG9Db250aW51ZSIsIiQiLCJfYyIsInQwIiwiU3ltYm9sIiwiZm9yIl0sInNvdXJjZXMiOlsiUHJlc3NFbnRlclRvQ29udGludWUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uL2luay5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIFByZXNzRW50ZXJUb0NvbnRpbnVlKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPFRleHQgY29sb3I9XCJwZXJtaXNzaW9uXCI+XG4gICAgICBQcmVzcyA8VGV4dCBib2xkPkVudGVyPC9UZXh0PiB0byBjb250aW51ZeKAplxuICAgIDwvVGV4dD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxJQUFJLFFBQVEsV0FBVztBQUVoQyxPQUFPLFNBQUFDLHFCQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQUcsTUFBQSxDQUFBQyxHQUFBO0lBRUhGLEVBQUEsSUFBQyxJQUFJLENBQU8sS0FBWSxDQUFaLFlBQVksQ0FBQyxNQUNqQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUosS0FBRyxDQUFDLENBQUMsS0FBSyxFQUFmLElBQUksQ0FBa0IsYUFDL0IsRUFGQyxJQUFJLENBRUU7SUFBQUYsQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFBQSxPQUZQRSxFQUVPO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/PromptInput/HistorySearchInput.tsx b/components/PromptInput/HistorySearchInput.tsx new file mode 100644 index 0000000..97c6910 --- /dev/null +++ b/components/PromptInput/HistorySearchInput.tsx @@ -0,0 +1,51 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, Text } from '../../ink.js'; +import TextInput from '../TextInput.js'; +type Props = { + value: string; + onChange: (value: string) => void; + historyFailedMatch: boolean; +}; +function HistorySearchInput(t0) { + const $ = _c(9); + const { + value, + onChange, + historyFailedMatch + } = t0; + const t1 = historyFailedMatch ? "no matching prompt:" : "search prompts:"; + let t2; + if ($[0] !== t1) { + t2 = {t1}; + $[0] = t1; + $[1] = t2; + } else { + t2 = $[1]; + } + const t3 = stringWidth(value) + 1; + let t4; + if ($[2] !== onChange || $[3] !== t3 || $[4] !== value) { + t4 = ; + $[2] = onChange; + $[3] = t3; + $[4] = value; + $[5] = t4; + } else { + t4 = $[5]; + } + let t5; + if ($[6] !== t2 || $[7] !== t4) { + t5 = {t2}{t4}; + $[6] = t2; + $[7] = t4; + $[8] = t5; + } else { + t5 = $[8]; + } + return t5; +} +function _temp() {} +export default HistorySearchInput; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInN0cmluZ1dpZHRoIiwiQm94IiwiVGV4dCIsIlRleHRJbnB1dCIsIlByb3BzIiwidmFsdWUiLCJvbkNoYW5nZSIsImhpc3RvcnlGYWlsZWRNYXRjaCIsIkhpc3RvcnlTZWFyY2hJbnB1dCIsInQwIiwiJCIsIl9jIiwidDEiLCJ0MiIsInQzIiwidDQiLCJsZW5ndGgiLCJfdGVtcCIsInQ1Il0sInNvdXJjZXMiOlsiSGlzdG9yeVNlYXJjaElucHV0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHN0cmluZ1dpZHRoIH0gZnJvbSAnLi4vLi4vaW5rL3N0cmluZ1dpZHRoLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IFRleHRJbnB1dCBmcm9tICcuLi9UZXh0SW5wdXQuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHZhbHVlOiBzdHJpbmdcbiAgb25DaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG4gIGhpc3RvcnlGYWlsZWRNYXRjaDogYm9vbGVhblxufVxuXG5mdW5jdGlvbiBIaXN0b3J5U2VhcmNoSW5wdXQoe1xuICB2YWx1ZSxcbiAgb25DaGFuZ2UsXG4gIGhpc3RvcnlGYWlsZWRNYXRjaCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8Qm94IGdhcD17MX0+XG4gICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAge2hpc3RvcnlGYWlsZWRNYXRjaCA/ICdubyBtYXRjaGluZyBwcm9tcHQ6JyA6ICdzZWFyY2ggcHJvbXB0czonfVxuICAgICAgPC9UZXh0PlxuICAgICAgPFRleHRJbnB1dFxuICAgICAgICB2YWx1ZT17dmFsdWV9XG4gICAgICAgIG9uQ2hhbmdlPXtvbkNoYW5nZX1cbiAgICAgICAgLy8gRm9yY2UgY3Vyc29yIHRvIGVuZCBvZiBzZWFyY2ggaW5wdXQgc2luY2UgbmF2aWdhdGlvbiBzaG91bGQgY2FuY2VsIHNlYXJjaFxuICAgICAgICBjdXJzb3JPZmZzZXQ9e3ZhbHVlLmxlbmd0aH1cbiAgICAgICAgb25DaGFuZ2VDdXJzb3JPZmZzZXQ9eygpID0+IHt9fVxuICAgICAgICBjb2x1bW5zPXtzdHJpbmdXaWR0aCh2YWx1ZSkgKyAxfVxuICAgICAgICBmb2N1cz17dHJ1ZX1cbiAgICAgICAgc2hvd0N1cnNvcj17dHJ1ZX1cbiAgICAgICAgbXVsdGlsaW5lPXtmYWxzZX1cbiAgICAgICAgZGltQ29sb3I9e3RydWV9XG4gICAgICAvPlxuICAgIDwvQm94PlxuICApXG59XG5cbmV4cG9ydCBkZWZhdWx0IEhpc3RvcnlTZWFyY2hJbnB1dFxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxXQUFXLFFBQVEsMEJBQTBCO0FBQ3RELFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsT0FBT0MsU0FBUyxNQUFNLGlCQUFpQjtBQUV2QyxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsS0FBSyxFQUFFLE1BQU07RUFDYkMsUUFBUSxFQUFFLENBQUNELEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQ2pDRSxrQkFBa0IsRUFBRSxPQUFPO0FBQzdCLENBQUM7QUFFRCxTQUFBQyxtQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUE0QjtJQUFBTixLQUFBO0lBQUFDLFFBQUE7SUFBQUM7RUFBQSxJQUFBRSxFQUlwQjtFQUlDLE1BQUFHLEVBQUEsR0FBQUwsa0JBQWtCLEdBQWxCLHFCQUE4RCxHQUE5RCxpQkFBOEQ7RUFBQSxJQUFBTSxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBRSxFQUFBO0lBRGpFQyxFQUFBLElBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FDWCxDQUFBRCxFQUE2RCxDQUNoRSxFQUZDLElBQUksQ0FFRTtJQUFBRixDQUFBLE1BQUFFLEVBQUE7SUFBQUYsQ0FBQSxNQUFBRyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSCxDQUFBO0VBQUE7RUFPSSxNQUFBSSxFQUFBLEdBQUFkLFdBQVcsQ0FBQ0ssS0FBSyxDQUFDLEdBQUcsQ0FBQztFQUFBLElBQUFVLEVBQUE7RUFBQSxJQUFBTCxDQUFBLFFBQUFKLFFBQUEsSUFBQUksQ0FBQSxRQUFBSSxFQUFBLElBQUFKLENBQUEsUUFBQUwsS0FBQTtJQU5qQ1UsRUFBQSxJQUFDLFNBQVMsQ0FDRFYsS0FBSyxDQUFMQSxNQUFJLENBQUMsQ0FDRkMsUUFBUSxDQUFSQSxTQUFPLENBQUMsQ0FFSixZQUFZLENBQVosQ0FBQUQsS0FBSyxDQUFBVyxNQUFNLENBQUMsQ0FDSixvQkFBUSxDQUFSLENBQUFDLEtBQU8sQ0FBQyxDQUNyQixPQUFzQixDQUF0QixDQUFBSCxFQUFxQixDQUFDLENBQ3hCLEtBQUksQ0FBSixLQUFHLENBQUMsQ0FDQyxVQUFJLENBQUosS0FBRyxDQUFDLENBQ0wsU0FBSyxDQUFMLE1BQUksQ0FBQyxDQUNOLFFBQUksQ0FBSixLQUFHLENBQUMsR0FDZDtJQUFBSixDQUFBLE1BQUFKLFFBQUE7SUFBQUksQ0FBQSxNQUFBSSxFQUFBO0lBQUFKLENBQUEsTUFBQUwsS0FBQTtJQUFBSyxDQUFBLE1BQUFLLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFMLENBQUE7RUFBQTtFQUFBLElBQUFRLEVBQUE7RUFBQSxJQUFBUixDQUFBLFFBQUFHLEVBQUEsSUFBQUgsQ0FBQSxRQUFBSyxFQUFBO0lBZkpHLEVBQUEsSUFBQyxHQUFHLENBQU0sR0FBQyxDQUFELEdBQUMsQ0FDVCxDQUFBTCxFQUVNLENBQ04sQ0FBQUUsRUFXQyxDQUNILEVBaEJDLEdBQUcsQ0FnQkU7SUFBQUwsQ0FBQSxNQUFBRyxFQUFBO0lBQUFILENBQUEsTUFBQUssRUFBQTtJQUFBTCxDQUFBLE1BQUFRLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFSLENBQUE7RUFBQTtFQUFBLE9BaEJOUSxFQWdCTTtBQUFBO0FBdEJWLFNBQUFELE1BQUE7QUEwQkEsZUFBZVQsa0JBQWtCIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/PromptInput/IssueFlagBanner.tsx b/components/PromptInput/IssueFlagBanner.tsx new file mode 100644 index 0000000..3889967 --- /dev/null +++ b/components/PromptInput/IssueFlagBanner.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; +import { FLAG_ICON } from '../../constants/figures.js'; +import { Box, Text } from '../../ink.js'; + +/** + * ANT-ONLY: Banner shown in the transcript that prompts users to report + * issues via /issue. Appears when friction is detected in the conversation. + */ +export function IssueFlagBanner() { + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkZMQUdfSUNPTiIsIkJveCIsIlRleHQiLCJJc3N1ZUZsYWdCYW5uZXIiXSwic291cmNlcyI6WyJJc3N1ZUZsYWdCYW5uZXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgRkxBR19JQ09OIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL2ZpZ3VyZXMuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5cbi8qKlxuICogQU5ULU9OTFk6IEJhbm5lciBzaG93biBpbiB0aGUgdHJhbnNjcmlwdCB0aGF0IHByb21wdHMgdXNlcnMgdG8gcmVwb3J0XG4gKiBpc3N1ZXMgdmlhIC9pc3N1ZS4gQXBwZWFycyB3aGVuIGZyaWN0aW9uIGlzIGRldGVjdGVkIGluIHRoZSBjb252ZXJzYXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBJc3N1ZUZsYWdCYW5uZXIoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKFwiZXh0ZXJuYWxcIiAhPT0gJ2FudCcpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBtYXJnaW5Ub3A9ezF9IHdpZHRoPVwiMTAwJVwiPlxuICAgICAgPEJveCBtaW5XaWR0aD17Mn0+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPntGTEFHX0lDT059PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgICA8VGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+W0FOVC1PTkxZXSA8L1RleHQ+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiIGJvbGQ+XG4gICAgICAgICAgU29tZXRoaW5nIG9mZiB3aXRoIENsYXVkZT9cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj4gL2lzc3VlIHRvIHJlcG9ydCBpdDwvVGV4dD5cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFNBQVMsUUFBUSw0QkFBNEI7QUFDdEQsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYzs7QUFFeEM7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFDLGdCQUFBO0VBQUEsT0FFSSxJQUFJO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/PromptInput/Notifications.tsx b/components/PromptInput/Notifications.tsx new file mode 100644 index 0000000..9b263cf --- /dev/null +++ b/components/PromptInput/Notifications.tsx @@ -0,0 +1,332 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import { type ReactNode, useEffect, useMemo, useState } from 'react'; +import { type Notification, useNotifications } from 'src/context/notifications.js'; +import { logEvent } from 'src/services/analytics/index.js'; +import { useAppState } from 'src/state/AppState.js'; +import { useVoiceState } from '../../context/voice.js'; +import type { VerificationStatus } from '../../hooks/useApiKeyVerification.js'; +import { useIdeConnectionStatus } from '../../hooks/useIdeConnectionStatus.js'; +import type { IDESelection } from '../../hooks/useIdeSelection.js'; +import { useMainLoopModel } from '../../hooks/useMainLoopModel.js'; +import { useVoiceEnabled } from '../../hooks/useVoiceEnabled.js'; +import { Box, Text } from '../../ink.js'; +import { useClaudeAiLimits } from '../../services/claudeAiLimitsHook.js'; +import { calculateTokenWarningState } from '../../services/compact/autoCompact.js'; +import type { MCPServerConnection } from '../../services/mcp/types.js'; +import type { Message } from '../../types/message.js'; +import { getApiKeyHelperElapsedMs, getConfiguredApiKeyHelper, getSubscriptionType } from '../../utils/auth.js'; +import type { AutoUpdaterResult } from '../../utils/autoUpdater.js'; +import { getExternalEditor } from '../../utils/editor.js'; +import { isEnvTruthy } from '../../utils/envUtils.js'; +import { formatDuration } from '../../utils/format.js'; +import { setEnvHookNotifier } from '../../utils/hooks/fileChangedWatcher.js'; +import { toIDEDisplayName } from '../../utils/ide.js'; +import { getMessagesAfterCompactBoundary } from '../../utils/messages.js'; +import { tokenCountFromLastAPIResponse } from '../../utils/tokens.js'; +import { AutoUpdaterWrapper } from '../AutoUpdaterWrapper.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { IdeStatusIndicator } from '../IdeStatusIndicator.js'; +import { MemoryUsageIndicator } from '../MemoryUsageIndicator.js'; +import { SentryErrorBoundary } from '../SentryErrorBoundary.js'; +import { TokenWarning } from '../TokenWarning.js'; +import { SandboxPromptFooterHint } from './SandboxPromptFooterHint.js'; + +/* eslint-disable @typescript-eslint/no-require-imports */ +const VoiceIndicator: typeof import('./VoiceIndicator.js').VoiceIndicator = feature('VOICE_MODE') ? require('./VoiceIndicator.js').VoiceIndicator : () => null; +/* eslint-enable @typescript-eslint/no-require-imports */ + +export const FOOTER_TEMPORARY_STATUS_TIMEOUT = 5000; +type Props = { + apiKeyStatus: VerificationStatus; + autoUpdaterResult: AutoUpdaterResult | null; + isAutoUpdating: boolean; + debug: boolean; + verbose: boolean; + messages: Message[]; + onAutoUpdaterResult: (result: AutoUpdaterResult) => void; + onChangeIsUpdating: (isUpdating: boolean) => void; + ideSelection: IDESelection | undefined; + mcpClients?: MCPServerConnection[]; + isInputWrapped?: boolean; + isNarrow?: boolean; +}; +export function Notifications(t0) { + const $ = _c(34); + const { + apiKeyStatus, + autoUpdaterResult, + debug, + isAutoUpdating, + verbose, + messages, + onAutoUpdaterResult, + onChangeIsUpdating, + ideSelection, + mcpClients, + isInputWrapped: t1, + isNarrow: t2 + } = t0; + const isInputWrapped = t1 === undefined ? false : t1; + const isNarrow = t2 === undefined ? false : t2; + let t3; + if ($[0] !== messages) { + const messagesForTokenCount = getMessagesAfterCompactBoundary(messages); + t3 = tokenCountFromLastAPIResponse(messagesForTokenCount); + $[0] = messages; + $[1] = t3; + } else { + t3 = $[1]; + } + const tokenUsage = t3; + const mainLoopModel = useMainLoopModel(); + let t4; + if ($[2] !== mainLoopModel || $[3] !== tokenUsage) { + t4 = calculateTokenWarningState(tokenUsage, mainLoopModel); + $[2] = mainLoopModel; + $[3] = tokenUsage; + $[4] = t4; + } else { + t4 = $[4]; + } + const isShowingCompactMessage = t4.isAboveWarningThreshold; + const { + status: ideStatus + } = useIdeConnectionStatus(mcpClients); + const notifications = useAppState(_temp); + const { + addNotification, + removeNotification + } = useNotifications(); + const claudeAiLimits = useClaudeAiLimits(); + let t5; + let t6; + if ($[5] !== addNotification) { + t5 = () => { + setEnvHookNotifier((text, isError) => { + addNotification({ + key: "env-hook", + text, + color: isError ? "error" : undefined, + priority: isError ? "medium" : "low", + timeoutMs: isError ? 8000 : 5000 + }); + }); + return _temp2; + }; + t6 = [addNotification]; + $[5] = addNotification; + $[6] = t5; + $[7] = t6; + } else { + t5 = $[6]; + t6 = $[7]; + } + useEffect(t5, t6); + const shouldShowIdeSelection = ideStatus === "connected" && (ideSelection?.filePath || ideSelection?.text && ideSelection.lineCount > 0); + const shouldShowAutoUpdater = !shouldShowIdeSelection || isAutoUpdating || autoUpdaterResult?.status !== "success"; + const isInOverageMode = claudeAiLimits.isUsingOverage; + let t7; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t7 = getSubscriptionType(); + $[8] = t7; + } else { + t7 = $[8]; + } + const subscriptionType = t7; + const isTeamOrEnterprise = subscriptionType === "team" || subscriptionType === "enterprise"; + let t8; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t8 = getExternalEditor(); + $[9] = t8; + } else { + t8 = $[9]; + } + const editor = t8; + const shouldShowExternalEditorHint = isInputWrapped && !isShowingCompactMessage && apiKeyStatus !== "invalid" && apiKeyStatus !== "missing" && editor !== undefined; + let t10; + let t9; + if ($[10] !== addNotification || $[11] !== removeNotification || $[12] !== shouldShowExternalEditorHint) { + t9 = () => { + if (shouldShowExternalEditorHint && editor) { + logEvent("tengu_external_editor_hint_shown", {}); + addNotification({ + key: "external-editor-hint", + jsx: , + priority: "immediate", + timeoutMs: 5000 + }); + } else { + removeNotification("external-editor-hint"); + } + }; + t10 = [shouldShowExternalEditorHint, editor, addNotification, removeNotification]; + $[10] = addNotification; + $[11] = removeNotification; + $[12] = shouldShowExternalEditorHint; + $[13] = t10; + $[14] = t9; + } else { + t10 = $[13]; + t9 = $[14]; + } + useEffect(t9, t10); + const t11 = isNarrow ? "flex-start" : "flex-end"; + const t12 = isInOverageMode ?? false; + let t13; + if ($[15] !== apiKeyStatus || $[16] !== autoUpdaterResult || $[17] !== debug || $[18] !== ideSelection || $[19] !== isAutoUpdating || $[20] !== isShowingCompactMessage || $[21] !== mainLoopModel || $[22] !== mcpClients || $[23] !== notifications || $[24] !== onAutoUpdaterResult || $[25] !== onChangeIsUpdating || $[26] !== shouldShowAutoUpdater || $[27] !== t12 || $[28] !== tokenUsage || $[29] !== verbose) { + t13 = ; + $[15] = apiKeyStatus; + $[16] = autoUpdaterResult; + $[17] = debug; + $[18] = ideSelection; + $[19] = isAutoUpdating; + $[20] = isShowingCompactMessage; + $[21] = mainLoopModel; + $[22] = mcpClients; + $[23] = notifications; + $[24] = onAutoUpdaterResult; + $[25] = onChangeIsUpdating; + $[26] = shouldShowAutoUpdater; + $[27] = t12; + $[28] = tokenUsage; + $[29] = verbose; + $[30] = t13; + } else { + t13 = $[30]; + } + let t14; + if ($[31] !== t11 || $[32] !== t13) { + t14 = {t13}; + $[31] = t11; + $[32] = t13; + $[33] = t14; + } else { + t14 = $[33]; + } + return t14; +} +function _temp2() { + return setEnvHookNotifier(null); +} +function _temp(s) { + return s.notifications; +} +function NotificationContent({ + ideSelection, + mcpClients, + notifications, + isInOverageMode, + isTeamOrEnterprise, + apiKeyStatus, + debug, + verbose, + tokenUsage, + mainLoopModel, + shouldShowAutoUpdater, + autoUpdaterResult, + isAutoUpdating, + isShowingCompactMessage, + onAutoUpdaterResult, + onChangeIsUpdating +}: { + ideSelection: IDESelection | undefined; + mcpClients?: MCPServerConnection[]; + notifications: { + current: Notification | null; + queue: Notification[]; + }; + isInOverageMode: boolean; + isTeamOrEnterprise: boolean; + apiKeyStatus: VerificationStatus; + debug: boolean; + verbose: boolean; + tokenUsage: number; + mainLoopModel: string; + shouldShowAutoUpdater: boolean; + autoUpdaterResult: AutoUpdaterResult | null; + isAutoUpdating: boolean; + isShowingCompactMessage: boolean; + onAutoUpdaterResult: (result: AutoUpdaterResult) => void; + onChangeIsUpdating: (isUpdating: boolean) => void; +}): ReactNode { + // Poll apiKeyHelper inflight state to show slow-helper notice. + // Gated on configuration — most users never set apiKeyHelper, so the + // effect is a no-op for them (no interval allocated). + const [apiKeyHelperSlow, setApiKeyHelperSlow] = useState(null); + useEffect(() => { + if (!getConfiguredApiKeyHelper()) return; + const interval = setInterval((setSlow: React.Dispatch>) => { + const ms = getApiKeyHelperElapsedMs(); + const next = ms >= 10_000 ? formatDuration(ms) : null; + setSlow(prev => next === prev ? prev : next); + }, 1000, setApiKeyHelperSlow); + return () => clearInterval(interval); + }, []); + + // Voice state (VOICE_MODE builds only, runtime-gated by GrowthBook) + const voiceState = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useVoiceState(s => s.voiceState) : 'idle' as const; + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const voiceEnabled = feature('VOICE_MODE') ? useVoiceEnabled() : false; + const voiceError = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useVoiceState(s_0 => s_0.voiceError) : null; + const isBriefOnly = feature('KAIROS') || feature('KAIROS_BRIEF') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s_1 => s_1.isBriefOnly) : false; + + // When voice is actively recording or processing, replace all + // notifications with just the voice indicator. + if (feature('VOICE_MODE') && voiceEnabled && (voiceState === 'recording' || voiceState === 'processing')) { + return ; + } + return <> + + {notifications.current && ('jsx' in notifications.current ? + {notifications.current.jsx} + : + {notifications.current.text} + )} + {isInOverageMode && !isTeamOrEnterprise && + + Now using extra usage + + } + {apiKeyHelperSlow && + + apiKeyHelper is taking a while{' '} + + + ({apiKeyHelperSlow}) + + } + {(apiKeyStatus === 'invalid' || apiKeyStatus === 'missing') && + + {isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) ? 'Authentication error · Try again' : 'Not logged in · Run /login'} + + } + {debug && + + Debug mode + + } + {apiKeyStatus !== 'invalid' && apiKeyStatus !== 'missing' && verbose && + + {tokenUsage} tokens + + } + {!isBriefOnly && } + {shouldShowAutoUpdater && } + {feature('VOICE_MODE') ? voiceEnabled && voiceError && + + {voiceError} + + : null} + + + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJSZWFjdE5vZGUiLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwidXNlU3RhdGUiLCJOb3RpZmljYXRpb24iLCJ1c2VOb3RpZmljYXRpb25zIiwibG9nRXZlbnQiLCJ1c2VBcHBTdGF0ZSIsInVzZVZvaWNlU3RhdGUiLCJWZXJpZmljYXRpb25TdGF0dXMiLCJ1c2VJZGVDb25uZWN0aW9uU3RhdHVzIiwiSURFU2VsZWN0aW9uIiwidXNlTWFpbkxvb3BNb2RlbCIsInVzZVZvaWNlRW5hYmxlZCIsIkJveCIsIlRleHQiLCJ1c2VDbGF1ZGVBaUxpbWl0cyIsImNhbGN1bGF0ZVRva2VuV2FybmluZ1N0YXRlIiwiTUNQU2VydmVyQ29ubmVjdGlvbiIsIk1lc3NhZ2UiLCJnZXRBcGlLZXlIZWxwZXJFbGFwc2VkTXMiLCJnZXRDb25maWd1cmVkQXBpS2V5SGVscGVyIiwiZ2V0U3Vic2NyaXB0aW9uVHlwZSIsIkF1dG9VcGRhdGVyUmVzdWx0IiwiZ2V0RXh0ZXJuYWxFZGl0b3IiLCJpc0VudlRydXRoeSIsImZvcm1hdER1cmF0aW9uIiwic2V0RW52SG9va05vdGlmaWVyIiwidG9JREVEaXNwbGF5TmFtZSIsImdldE1lc3NhZ2VzQWZ0ZXJDb21wYWN0Qm91bmRhcnkiLCJ0b2tlbkNvdW50RnJvbUxhc3RBUElSZXNwb25zZSIsIkF1dG9VcGRhdGVyV3JhcHBlciIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIklkZVN0YXR1c0luZGljYXRvciIsIk1lbW9yeVVzYWdlSW5kaWNhdG9yIiwiU2VudHJ5RXJyb3JCb3VuZGFyeSIsIlRva2VuV2FybmluZyIsIlNhbmRib3hQcm9tcHRGb290ZXJIaW50IiwiVm9pY2VJbmRpY2F0b3IiLCJyZXF1aXJlIiwiRk9PVEVSX1RFTVBPUkFSWV9TVEFUVVNfVElNRU9VVCIsIlByb3BzIiwiYXBpS2V5U3RhdHVzIiwiYXV0b1VwZGF0ZXJSZXN1bHQiLCJpc0F1dG9VcGRhdGluZyIsImRlYnVnIiwidmVyYm9zZSIsIm1lc3NhZ2VzIiwib25BdXRvVXBkYXRlclJlc3VsdCIsInJlc3VsdCIsIm9uQ2hhbmdlSXNVcGRhdGluZyIsImlzVXBkYXRpbmciLCJpZGVTZWxlY3Rpb24iLCJtY3BDbGllbnRzIiwiaXNJbnB1dFdyYXBwZWQiLCJpc05hcnJvdyIsIk5vdGlmaWNhdGlvbnMiLCJ0MCIsIiQiLCJfYyIsInQxIiwidDIiLCJ1bmRlZmluZWQiLCJ0MyIsIm1lc3NhZ2VzRm9yVG9rZW5Db3VudCIsInRva2VuVXNhZ2UiLCJtYWluTG9vcE1vZGVsIiwidDQiLCJpc1Nob3dpbmdDb21wYWN0TWVzc2FnZSIsImlzQWJvdmVXYXJuaW5nVGhyZXNob2xkIiwic3RhdHVzIiwiaWRlU3RhdHVzIiwibm90aWZpY2F0aW9ucyIsIl90ZW1wIiwiYWRkTm90aWZpY2F0aW9uIiwicmVtb3ZlTm90aWZpY2F0aW9uIiwiY2xhdWRlQWlMaW1pdHMiLCJ0NSIsInQ2IiwidGV4dCIsImlzRXJyb3IiLCJrZXkiLCJjb2xvciIsInByaW9yaXR5IiwidGltZW91dE1zIiwiX3RlbXAyIiwic2hvdWxkU2hvd0lkZVNlbGVjdGlvbiIsImZpbGVQYXRoIiwibGluZUNvdW50Iiwic2hvdWxkU2hvd0F1dG9VcGRhdGVyIiwiaXNJbk92ZXJhZ2VNb2RlIiwiaXNVc2luZ092ZXJhZ2UiLCJ0NyIsIlN5bWJvbCIsImZvciIsInN1YnNjcmlwdGlvblR5cGUiLCJpc1RlYW1PckVudGVycHJpc2UiLCJ0OCIsImVkaXRvciIsInNob3VsZFNob3dFeHRlcm5hbEVkaXRvckhpbnQiLCJ0MTAiLCJ0OSIsImpzeCIsInQxMSIsInQxMiIsInQxMyIsInQxNCIsInMiLCJOb3RpZmljYXRpb25Db250ZW50IiwiY3VycmVudCIsInF1ZXVlIiwiYXBpS2V5SGVscGVyU2xvdyIsInNldEFwaUtleUhlbHBlclNsb3ciLCJpbnRlcnZhbCIsInNldEludGVydmFsIiwic2V0U2xvdyIsIkRpc3BhdGNoIiwiU2V0U3RhdGVBY3Rpb24iLCJtcyIsIm5leHQiLCJwcmV2IiwiY2xlYXJJbnRlcnZhbCIsInZvaWNlU3RhdGUiLCJjb25zdCIsInZvaWNlRW5hYmxlZCIsInZvaWNlRXJyb3IiLCJpc0JyaWVmT25seSIsInByb2Nlc3MiLCJlbnYiLCJDTEFVREVfQ09ERV9SRU1PVEUiXSwic291cmNlcyI6WyJOb3RpZmljYXRpb25zLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdHlwZSBSZWFjdE5vZGUsIHVzZUVmZmVjdCwgdXNlTWVtbywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7XG4gIHR5cGUgTm90aWZpY2F0aW9uLFxuICB1c2VOb3RpZmljYXRpb25zLFxufSBmcm9tICdzcmMvY29udGV4dC9ub3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IHsgbG9nRXZlbnQgfSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUgfSBmcm9tICdzcmMvc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgeyB1c2VWb2ljZVN0YXRlIH0gZnJvbSAnLi4vLi4vY29udGV4dC92b2ljZS5qcydcbmltcG9ydCB0eXBlIHsgVmVyaWZpY2F0aW9uU3RhdHVzIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlQXBpS2V5VmVyaWZpY2F0aW9uLmpzJ1xuaW1wb3J0IHsgdXNlSWRlQ29ubmVjdGlvblN0YXR1cyB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUlkZUNvbm5lY3Rpb25TdGF0dXMuanMnXG5pbXBvcnQgdHlwZSB7IElERVNlbGVjdGlvbiB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUlkZVNlbGVjdGlvbi5qcydcbmltcG9ydCB7IHVzZU1haW5Mb29wTW9kZWwgfSBmcm9tICcuLi8uLi9ob29rcy91c2VNYWluTG9vcE1vZGVsLmpzJ1xuaW1wb3J0IHsgdXNlVm9pY2VFbmFibGVkIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlVm9pY2VFbmFibGVkLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlQ2xhdWRlQWlMaW1pdHMgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9jbGF1ZGVBaUxpbWl0c0hvb2suanMnXG5pbXBvcnQgeyBjYWxjdWxhdGVUb2tlbldhcm5pbmdTdGF0ZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2NvbXBhY3QvYXV0b0NvbXBhY3QuanMnXG5pbXBvcnQgdHlwZSB7IE1DUFNlcnZlckNvbm5lY3Rpb24gfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvdHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IE1lc3NhZ2UgfSBmcm9tICcuLi8uLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0QXBpS2V5SGVscGVyRWxhcHNlZE1zLFxuICBnZXRDb25maWd1cmVkQXBpS2V5SGVscGVyLFxuICBnZXRTdWJzY3JpcHRpb25UeXBlLFxufSBmcm9tICcuLi8uLi91dGlscy9hdXRoLmpzJ1xuaW1wb3J0IHR5cGUgeyBBdXRvVXBkYXRlclJlc3VsdCB9IGZyb20gJy4uLy4uL3V0aWxzL2F1dG9VcGRhdGVyLmpzJ1xuaW1wb3J0IHsgZ2V0RXh0ZXJuYWxFZGl0b3IgfSBmcm9tICcuLi8uLi91dGlscy9lZGl0b3IuanMnXG5pbXBvcnQgeyBpc0VudlRydXRoeSB9IGZyb20gJy4uLy4uL3V0aWxzL2VudlV0aWxzLmpzJ1xuaW1wb3J0IHsgZm9ybWF0RHVyYXRpb24gfSBmcm9tICcuLi8uLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQgeyBzZXRFbnZIb29rTm90aWZpZXIgfSBmcm9tICcuLi8uLi91dGlscy9ob29rcy9maWxlQ2hhbmdlZFdhdGNoZXIuanMnXG5pbXBvcnQgeyB0b0lERURpc3BsYXlOYW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMvaWRlLmpzJ1xuaW1wb3J0IHsgZ2V0TWVzc2FnZXNBZnRlckNvbXBhY3RCb3VuZGFyeSB9IGZyb20gJy4uLy4uL3V0aWxzL21lc3NhZ2VzLmpzJ1xuaW1wb3J0IHsgdG9rZW5Db3VudEZyb21MYXN0QVBJUmVzcG9uc2UgfSBmcm9tICcuLi8uLi91dGlscy90b2tlbnMuanMnXG5pbXBvcnQgeyBBdXRvVXBkYXRlcldyYXBwZXIgfSBmcm9tICcuLi9BdXRvVXBkYXRlcldyYXBwZXIuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBJZGVTdGF0dXNJbmRpY2F0b3IgfSBmcm9tICcuLi9JZGVTdGF0dXNJbmRpY2F0b3IuanMnXG5pbXBvcnQgeyBNZW1vcnlVc2FnZUluZGljYXRvciB9IGZyb20gJy4uL01lbW9yeVVzYWdlSW5kaWNhdG9yLmpzJ1xuaW1wb3J0IHsgU2VudHJ5RXJyb3JCb3VuZGFyeSB9IGZyb20gJy4uL1NlbnRyeUVycm9yQm91bmRhcnkuanMnXG5pbXBvcnQgeyBUb2tlbldhcm5pbmcgfSBmcm9tICcuLi9Ub2tlbldhcm5pbmcuanMnXG5pbXBvcnQgeyBTYW5kYm94UHJvbXB0Rm9vdGVySGludCB9IGZyb20gJy4vU2FuZGJveFByb21wdEZvb3RlckhpbnQuanMnXG5cbi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbmNvbnN0IFZvaWNlSW5kaWNhdG9yOiB0eXBlb2YgaW1wb3J0KCcuL1ZvaWNlSW5kaWNhdG9yLmpzJykuVm9pY2VJbmRpY2F0b3IgPVxuICBmZWF0dXJlKCdWT0lDRV9NT0RFJylcbiAgICA/IHJlcXVpcmUoJy4vVm9pY2VJbmRpY2F0b3IuanMnKS5Wb2ljZUluZGljYXRvclxuICAgIDogKCkgPT4gbnVsbFxuLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG5cbmV4cG9ydCBjb25zdCBGT09URVJfVEVNUE9SQVJZX1NUQVRVU19USU1FT1VUID0gNTAwMFxuXG50eXBlIFByb3BzID0ge1xuICBhcGlLZXlTdGF0dXM6IFZlcmlmaWNhdGlvblN0YXR1c1xuICBhdXRvVXBkYXRlclJlc3VsdDogQXV0b1VwZGF0ZXJSZXN1bHQgfCBudWxsXG4gIGlzQXV0b1VwZGF0aW5nOiBib29sZWFuXG4gIGRlYnVnOiBib29sZWFuXG4gIHZlcmJvc2U6IGJvb2xlYW5cbiAgbWVzc2FnZXM6IE1lc3NhZ2VbXVxuICBvbkF1dG9VcGRhdGVyUmVzdWx0OiAocmVzdWx0OiBBdXRvVXBkYXRlclJlc3VsdCkgPT4gdm9pZFxuICBvbkNoYW5nZUlzVXBkYXRpbmc6IChpc1VwZGF0aW5nOiBib29sZWFuKSA9PiB2b2lkXG4gIGlkZVNlbGVjdGlvbjogSURFU2VsZWN0aW9uIHwgdW5kZWZpbmVkXG4gIG1jcENsaWVudHM/OiBNQ1BTZXJ2ZXJDb25uZWN0aW9uW11cbiAgaXNJbnB1dFdyYXBwZWQ/OiBib29sZWFuXG4gIGlzTmFycm93PzogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gTm90aWZpY2F0aW9ucyh7XG4gIGFwaUtleVN0YXR1cyxcbiAgYXV0b1VwZGF0ZXJSZXN1bHQsXG4gIGRlYnVnLFxuICBpc0F1dG9VcGRhdGluZyxcbiAgdmVyYm9zZSxcbiAgbWVzc2FnZXMsXG4gIG9uQXV0b1VwZGF0ZXJSZXN1bHQsXG4gIG9uQ2hhbmdlSXNVcGRhdGluZyxcbiAgaWRlU2VsZWN0aW9uLFxuICBtY3BDbGllbnRzLFxuICBpc0lucHV0V3JhcHBlZCA9IGZhbHNlLFxuICBpc05hcnJvdyA9IGZhbHNlLFxufTogUHJvcHMpOiBSZWFjdE5vZGUge1xuICBjb25zdCB0b2tlblVzYWdlID0gdXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3QgbWVzc2FnZXNGb3JUb2tlbkNvdW50ID0gZ2V0TWVzc2FnZXNBZnRlckNvbXBhY3RCb3VuZGFyeShtZXNzYWdlcylcbiAgICByZXR1cm4gdG9rZW5Db3VudEZyb21MYXN0QVBJUmVzcG9uc2UobWVzc2FnZXNGb3JUb2tlbkNvdW50KVxuICB9LCBbbWVzc2FnZXNdKVxuXG4gIC8vIEFwcFN0YXRlLXNvdXJjZWQgbW9kZWwg4oCUIHNhbWUgc291cmNlIGFzIEFQSSByZXF1ZXN0cy4gZ2V0TWFpbkxvb3BNb2RlbCgpXG4gIC8vIHJlLXJlYWRzIHNldHRpbmdzLmpzb24gb24gZXZlcnkgY2FsbCwgc28gYW5vdGhlciBzZXNzaW9uJ3MgL21vZGVsIHdyaXRlXG4gIC8vIHdvdWxkIGxlYWsgaW50byB0aGlzIHNlc3Npb24ncyBkaXNwbGF5IChhbnRocm9waWNzL2NsYXVkZS1jb2RlIzM3NTk2KS5cbiAgY29uc3QgbWFpbkxvb3BNb2RlbCA9IHVzZU1haW5Mb29wTW9kZWwoKVxuICBjb25zdCBpc1Nob3dpbmdDb21wYWN0TWVzc2FnZSA9IGNhbGN1bGF0ZVRva2VuV2FybmluZ1N0YXRlKFxuICAgIHRva2VuVXNhZ2UsXG4gICAgbWFpbkxvb3BNb2RlbCxcbiAgKS5pc0Fib3ZlV2FybmluZ1RocmVzaG9sZFxuICBjb25zdCB7IHN0YXR1czogaWRlU3RhdHVzIH0gPSB1c2VJZGVDb25uZWN0aW9uU3RhdHVzKG1jcENsaWVudHMpXG4gIGNvbnN0IG5vdGlmaWNhdGlvbnMgPSB1c2VBcHBTdGF0ZShzID0+IHMubm90aWZpY2F0aW9ucylcbiAgY29uc3QgeyBhZGROb3RpZmljYXRpb24sIHJlbW92ZU5vdGlmaWNhdGlvbiB9ID0gdXNlTm90aWZpY2F0aW9ucygpXG4gIGNvbnN0IGNsYXVkZUFpTGltaXRzID0gdXNlQ2xhdWRlQWlMaW1pdHMoKVxuXG4gIC8vIFJlZ2lzdGVyIGVudiBob29rIG5vdGlmaWVyIGZvciBDd2RDaGFuZ2VkL0ZpbGVDaGFuZ2VkIGZlZWRiYWNrXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgc2V0RW52SG9va05vdGlmaWVyKCh0ZXh0LCBpc0Vycm9yKSA9PiB7XG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICdlbnYtaG9vaycsXG4gICAgICAgIHRleHQsXG4gICAgICAgIGNvbG9yOiBpc0Vycm9yID8gJ2Vycm9yJyA6IHVuZGVmaW5lZCxcbiAgICAgICAgcHJpb3JpdHk6IGlzRXJyb3IgPyAnbWVkaXVtJyA6ICdsb3cnLFxuICAgICAgICB0aW1lb3V0TXM6IGlzRXJyb3IgPyA4MDAwIDogNTAwMCxcbiAgICAgIH0pXG4gICAgfSlcbiAgICByZXR1cm4gKCkgPT4gc2V0RW52SG9va05vdGlmaWVyKG51bGwpXG4gIH0sIFthZGROb3RpZmljYXRpb25dKVxuXG4gIC8vIENoZWNrIGlmIHdlIHNob3VsZCBzaG93IHRoZSBJREUgc2VsZWN0aW9uIGluZGljYXRvclxuICBjb25zdCBzaG91bGRTaG93SWRlU2VsZWN0aW9uID1cbiAgICBpZGVTdGF0dXMgPT09ICdjb25uZWN0ZWQnICYmXG4gICAgKGlkZVNlbGVjdGlvbj8uZmlsZVBhdGggfHxcbiAgICAgIChpZGVTZWxlY3Rpb24/LnRleHQgJiYgaWRlU2VsZWN0aW9uLmxpbmVDb3VudCA+IDApKVxuXG4gIC8vIEhpZGUgdXBkYXRlIGluc3RhbGxlZCBtZXNzYWdlIHdoZW4gc2hvd2luZyBJREUgc2VsZWN0aW9uXG4gIGNvbnN0IHNob3VsZFNob3dBdXRvVXBkYXRlciA9XG4gICAgIXNob3VsZFNob3dJZGVTZWxlY3Rpb24gfHxcbiAgICBpc0F1dG9VcGRhdGluZyB8fFxuICAgIGF1dG9VcGRhdGVyUmVzdWx0Py5zdGF0dXMgIT09ICdzdWNjZXNzJ1xuXG4gIC8vIENoZWNrIGlmIHdlJ3JlIGluIG92ZXJhZ2UgbW9kZSBmb3IgVUkgaW5kaWNhdG9yc1xuICBjb25zdCBpc0luT3ZlcmFnZU1vZGUgPSBjbGF1ZGVBaUxpbWl0cy5pc1VzaW5nT3ZlcmFnZVxuICBjb25zdCBzdWJzY3JpcHRpb25UeXBlID0gZ2V0U3Vic2NyaXB0aW9uVHlwZSgpXG4gIGNvbnN0IGlzVGVhbU9yRW50ZXJwcmlzZSA9XG4gICAgc3Vic2NyaXB0aW9uVHlwZSA9PT0gJ3RlYW0nIHx8IHN1YnNjcmlwdGlvblR5cGUgPT09ICdlbnRlcnByaXNlJ1xuXG4gIC8vIENoZWNrIGlmIHRoZSBleHRlcm5hbCBlZGl0b3IgaGludCBzaG91bGQgYmUgc2hvd25cbiAgY29uc3QgZWRpdG9yID0gZ2V0RXh0ZXJuYWxFZGl0b3IoKVxuICBjb25zdCBzaG91bGRTaG93RXh0ZXJuYWxFZGl0b3JIaW50ID1cbiAgICBpc0lucHV0V3JhcHBlZCAmJlxuICAgICFpc1Nob3dpbmdDb21wYWN0TWVzc2FnZSAmJlxuICAgIGFwaUtleVN0YXR1cyAhPT0gJ2ludmFsaWQnICYmXG4gICAgYXBpS2V5U3RhdHVzICE9PSAnbWlzc2luZycgJiZcbiAgICBlZGl0b3IgIT09IHVuZGVmaW5lZFxuXG4gIC8vIFNob3cgZXh0ZXJuYWwgZWRpdG9yIGhpbnQgYXMgbm90aWZpY2F0aW9uIHdoZW4gaW5wdXQgaXMgd3JhcHBlZFxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChzaG91bGRTaG93RXh0ZXJuYWxFZGl0b3JIaW50ICYmIGVkaXRvcikge1xuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2V4dGVybmFsX2VkaXRvcl9oaW50X3Nob3duJywge30pXG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICdleHRlcm5hbC1lZGl0b3ItaGludCcsXG4gICAgICAgIGpzeDogKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICBhY3Rpb249XCJjaGF0OmV4dGVybmFsRWRpdG9yXCJcbiAgICAgICAgICAgICAgY29udGV4dD1cIkNoYXRcIlxuICAgICAgICAgICAgICBmYWxsYmFjaz1cImN0cmwrZ1wiXG4gICAgICAgICAgICAgIGRlc2NyaXB0aW9uPXtgZWRpdCBpbiAke3RvSURFRGlzcGxheU5hbWUoZWRpdG9yKX1gfVxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICksXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgdGltZW91dE1zOiA1MDAwLFxuICAgICAgfSlcbiAgICB9IGVsc2Uge1xuICAgICAgcmVtb3ZlTm90aWZpY2F0aW9uKCdleHRlcm5hbC1lZGl0b3ItaGludCcpXG4gICAgfVxuICB9LCBbXG4gICAgc2hvdWxkU2hvd0V4dGVybmFsRWRpdG9ySGludCxcbiAgICBlZGl0b3IsXG4gICAgYWRkTm90aWZpY2F0aW9uLFxuICAgIHJlbW92ZU5vdGlmaWNhdGlvbixcbiAgXSlcblxuICByZXR1cm4gKFxuICAgIDxTZW50cnlFcnJvckJvdW5kYXJ5PlxuICAgICAgPEJveFxuICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgYWxpZ25JdGVtcz17aXNOYXJyb3cgPyAnZmxleC1zdGFydCcgOiAnZmxleC1lbmQnfVxuICAgICAgICBmbGV4U2hyaW5rPXswfVxuICAgICAgICBvdmVyZmxvd1g9XCJoaWRkZW5cIlxuICAgICAgPlxuICAgICAgICA8Tm90aWZpY2F0aW9uQ29udGVudFxuICAgICAgICAgIGlkZVNlbGVjdGlvbj17aWRlU2VsZWN0aW9ufVxuICAgICAgICAgIG1jcENsaWVudHM9e21jcENsaWVudHN9XG4gICAgICAgICAgbm90aWZpY2F0aW9ucz17bm90aWZpY2F0aW9uc31cbiAgICAgICAgICBpc0luT3ZlcmFnZU1vZGU9e2lzSW5PdmVyYWdlTW9kZSA/PyBmYWxzZX1cbiAgICAgICAgICBpc1RlYW1PckVudGVycHJpc2U9e2lzVGVhbU9yRW50ZXJwcmlzZX1cbiAgICAgICAgICBhcGlLZXlTdGF0dXM9e2FwaUtleVN0YXR1c31cbiAgICAgICAgICBkZWJ1Zz17ZGVidWd9XG4gICAgICAgICAgdmVyYm9zZT17dmVyYm9zZX1cbiAgICAgICAgICB0b2tlblVzYWdlPXt0b2tlblVzYWdlfVxuICAgICAgICAgIG1haW5Mb29wTW9kZWw9e21haW5Mb29wTW9kZWx9XG4gICAgICAgICAgc2hvdWxkU2hvd0F1dG9VcGRhdGVyPXtzaG91bGRTaG93QXV0b1VwZGF0ZXJ9XG4gICAgICAgICAgYXV0b1VwZGF0ZXJSZXN1bHQ9e2F1dG9VcGRhdGVyUmVzdWx0fVxuICAgICAgICAgIGlzQXV0b1VwZGF0aW5nPXtpc0F1dG9VcGRhdGluZ31cbiAgICAgICAgICBpc1Nob3dpbmdDb21wYWN0TWVzc2FnZT17aXNTaG93aW5nQ29tcGFjdE1lc3NhZ2V9XG4gICAgICAgICAgb25BdXRvVXBkYXRlclJlc3VsdD17b25BdXRvVXBkYXRlclJlc3VsdH1cbiAgICAgICAgICBvbkNoYW5nZUlzVXBkYXRpbmc9e29uQ2hhbmdlSXNVcGRhdGluZ31cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIDwvU2VudHJ5RXJyb3JCb3VuZGFyeT5cbiAgKVxufVxuXG5mdW5jdGlvbiBOb3RpZmljYXRpb25Db250ZW50KHtcbiAgaWRlU2VsZWN0aW9uLFxuICBtY3BDbGllbnRzLFxuICBub3RpZmljYXRpb25zLFxuICBpc0luT3ZlcmFnZU1vZGUsXG4gIGlzVGVhbU9yRW50ZXJwcmlzZSxcbiAgYXBpS2V5U3RhdHVzLFxuICBkZWJ1ZyxcbiAgdmVyYm9zZSxcbiAgdG9rZW5Vc2FnZSxcbiAgbWFpbkxvb3BNb2RlbCxcbiAgc2hvdWxkU2hvd0F1dG9VcGRhdGVyLFxuICBhdXRvVXBkYXRlclJlc3VsdCxcbiAgaXNBdXRvVXBkYXRpbmcsXG4gIGlzU2hvd2luZ0NvbXBhY3RNZXNzYWdlLFxuICBvbkF1dG9VcGRhdGVyUmVzdWx0LFxuICBvbkNoYW5nZUlzVXBkYXRpbmcsXG59OiB7XG4gIGlkZVNlbGVjdGlvbjogSURFU2VsZWN0aW9uIHwgdW5kZWZpbmVkXG4gIG1jcENsaWVudHM/OiBNQ1BTZXJ2ZXJDb25uZWN0aW9uW11cbiAgbm90aWZpY2F0aW9uczoge1xuICAgIGN1cnJlbnQ6IE5vdGlmaWNhdGlvbiB8IG51bGxcbiAgICBxdWV1ZTogTm90aWZpY2F0aW9uW11cbiAgfVxuICBpc0luT3ZlcmFnZU1vZGU6IGJvb2xlYW5cbiAgaXNUZWFtT3JFbnRlcnByaXNlOiBib29sZWFuXG4gIGFwaUtleVN0YXR1czogVmVyaWZpY2F0aW9uU3RhdHVzXG4gIGRlYnVnOiBib29sZWFuXG4gIHZlcmJvc2U6IGJvb2xlYW5cbiAgdG9rZW5Vc2FnZTogbnVtYmVyXG4gIG1haW5Mb29wTW9kZWw6IHN0cmluZ1xuICBzaG91bGRTaG93QXV0b1VwZGF0ZXI6IGJvb2xlYW5cbiAgYXV0b1VwZGF0ZXJSZXN1bHQ6IEF1dG9VcGRhdGVyUmVzdWx0IHwgbnVsbFxuICBpc0F1dG9VcGRhdGluZzogYm9vbGVhblxuICBpc1Nob3dpbmdDb21wYWN0TWVzc2FnZTogYm9vbGVhblxuICBvbkF1dG9VcGRhdGVyUmVzdWx0OiAocmVzdWx0OiBBdXRvVXBkYXRlclJlc3VsdCkgPT4gdm9pZFxuICBvbkNoYW5nZUlzVXBkYXRpbmc6IChpc1VwZGF0aW5nOiBib29sZWFuKSA9PiB2b2lkXG59KTogUmVhY3ROb2RlIHtcbiAgLy8gUG9sbCBhcGlLZXlIZWxwZXIgaW5mbGlnaHQgc3RhdGUgdG8gc2hvdyBzbG93LWhlbHBlciBub3RpY2UuXG4gIC8vIEdhdGVkIG9uIGNvbmZpZ3VyYXRpb24g4oCUIG1vc3QgdXNlcnMgbmV2ZXIgc2V0IGFwaUtleUhlbHBlciwgc28gdGhlXG4gIC8vIGVmZmVjdCBpcyBhIG5vLW9wIGZvciB0aGVtIChubyBpbnRlcnZhbCBhbGxvY2F0ZWQpLlxuICBjb25zdCBbYXBpS2V5SGVscGVyU2xvdywgc2V0QXBpS2V5SGVscGVyU2xvd10gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghZ2V0Q29uZmlndXJlZEFwaUtleUhlbHBlcigpKSByZXR1cm5cbiAgICBjb25zdCBpbnRlcnZhbCA9IHNldEludGVydmFsKFxuICAgICAgKHNldFNsb3c6IFJlYWN0LkRpc3BhdGNoPFJlYWN0LlNldFN0YXRlQWN0aW9uPHN0cmluZyB8IG51bGw+PikgPT4ge1xuICAgICAgICBjb25zdCBtcyA9IGdldEFwaUtleUhlbHBlckVsYXBzZWRNcygpXG4gICAgICAgIGNvbnN0IG5leHQgPSBtcyA+PSAxMF8wMDAgPyBmb3JtYXREdXJhdGlvbihtcykgOiBudWxsXG4gICAgICAgIHNldFNsb3cocHJldiA9PiAobmV4dCA9PT0gcHJldiA/IHByZXYgOiBuZXh0KSlcbiAgICAgIH0sXG4gICAgICAxMDAwLFxuICAgICAgc2V0QXBpS2V5SGVscGVyU2xvdyxcbiAgICApXG4gICAgcmV0dXJuICgpID0+IGNsZWFySW50ZXJ2YWwoaW50ZXJ2YWwpXG4gIH0sIFtdKVxuXG4gIC8vIFZvaWNlIHN0YXRlIChWT0lDRV9NT0RFIGJ1aWxkcyBvbmx5LCBydW50aW1lLWdhdGVkIGJ5IEdyb3d0aEJvb2spXG4gIGNvbnN0IHZvaWNlU3RhdGUgPSBmZWF0dXJlKCdWT0lDRV9NT0RFJylcbiAgICA/IC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgICAgIHVzZVZvaWNlU3RhdGUocyA9PiBzLnZvaWNlU3RhdGUpXG4gICAgOiAoJ2lkbGUnIGFzIGNvbnN0KVxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gIGNvbnN0IHZvaWNlRW5hYmxlZCA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKSA/IHVzZVZvaWNlRW5hYmxlZCgpIDogZmFsc2VcbiAgY29uc3Qgdm9pY2VFcnJvciA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKVxuICAgID8gLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICAgICAgdXNlVm9pY2VTdGF0ZShzID0+IHMudm9pY2VFcnJvcilcbiAgICA6IG51bGxcbiAgY29uc3QgaXNCcmllZk9ubHkgPVxuICAgIGZlYXR1cmUoJ0tBSVJPUycpIHx8IGZlYXR1cmUoJ0tBSVJPU19CUklFRicpXG4gICAgICA/IC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgICAgICAgdXNlQXBwU3RhdGUocyA9PiBzLmlzQnJpZWZPbmx5KVxuICAgICAgOiBmYWxzZVxuXG4gIC8vIFdoZW4gdm9pY2UgaXMgYWN0aXZlbHkgcmVjb3JkaW5nIG9yIHByb2Nlc3NpbmcsIHJlcGxhY2UgYWxsXG4gIC8vIG5vdGlmaWNhdGlvbnMgd2l0aCBqdXN0IHRoZSB2b2ljZSBpbmRpY2F0b3IuXG4gIGlmIChcbiAgICBmZWF0dXJlKCdWT0lDRV9NT0RFJykgJiZcbiAgICB2b2ljZUVuYWJsZWQgJiZcbiAgICAodm9pY2VTdGF0ZSA9PT0gJ3JlY29yZGluZycgfHwgdm9pY2VTdGF0ZSA9PT0gJ3Byb2Nlc3NpbmcnKVxuICApIHtcbiAgICByZXR1cm4gPFZvaWNlSW5kaWNhdG9yIHZvaWNlU3RhdGU9e3ZvaWNlU3RhdGV9IC8+XG4gIH1cblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8SWRlU3RhdHVzSW5kaWNhdG9yIGlkZVNlbGVjdGlvbj17aWRlU2VsZWN0aW9ufSBtY3BDbGllbnRzPXttY3BDbGllbnRzfSAvPlxuICAgICAge25vdGlmaWNhdGlvbnMuY3VycmVudCAmJlxuICAgICAgICAoJ2pzeCcgaW4gbm90aWZpY2F0aW9ucy5jdXJyZW50ID8gKFxuICAgICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiIGtleT17bm90aWZpY2F0aW9ucy5jdXJyZW50LmtleX0+XG4gICAgICAgICAgICB7bm90aWZpY2F0aW9ucy5jdXJyZW50LmpzeH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICkgOiAoXG4gICAgICAgICAgPFRleHRcbiAgICAgICAgICAgIGNvbG9yPXtub3RpZmljYXRpb25zLmN1cnJlbnQuY29sb3J9XG4gICAgICAgICAgICBkaW1Db2xvcj17IW5vdGlmaWNhdGlvbnMuY3VycmVudC5jb2xvcn1cbiAgICAgICAgICAgIHdyYXA9XCJ0cnVuY2F0ZVwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAge25vdGlmaWNhdGlvbnMuY3VycmVudC50ZXh0fVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKSl9XG4gICAgICB7aXNJbk92ZXJhZ2VNb2RlICYmICFpc1RlYW1PckVudGVycHJpc2UgJiYgKFxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAgTm93IHVzaW5nIGV4dHJhIHVzYWdlXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgICB7YXBpS2V5SGVscGVyU2xvdyAmJiAoXG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJ3YXJuaW5nXCIgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICBhcGlLZXlIZWxwZXIgaXMgdGFraW5nIGEgd2hpbGV7JyAnfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvciB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICAgICh7YXBpS2V5SGVscGVyU2xvd30pXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgICB7KGFwaUtleVN0YXR1cyA9PT0gJ2ludmFsaWQnIHx8IGFwaUtleVN0YXR1cyA9PT0gJ21pc3NpbmcnKSAmJiAoXG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiIHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAge2lzRW52VHJ1dGh5KHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX1JFTU9URSlcbiAgICAgICAgICAgICAgPyAnQXV0aGVudGljYXRpb24gZXJyb3IgwrcgVHJ5IGFnYWluJ1xuICAgICAgICAgICAgICA6ICdOb3QgbG9nZ2VkIGluIMK3IFJ1biAvbG9naW4nfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuICAgICAge2RlYnVnICYmIChcbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIiB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICAgIERlYnVnIG1vZGVcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICAgIHthcGlLZXlTdGF0dXMgIT09ICdpbnZhbGlkJyAmJiBhcGlLZXlTdGF0dXMgIT09ICdtaXNzaW5nJyAmJiB2ZXJib3NlICYmIChcbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvciB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICAgIHt0b2tlblVzYWdlfSB0b2tlbnNcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICAgIHshaXNCcmllZk9ubHkgJiYgKFxuICAgICAgICA8VG9rZW5XYXJuaW5nIHRva2VuVXNhZ2U9e3Rva2VuVXNhZ2V9IG1vZGVsPXttYWluTG9vcE1vZGVsfSAvPlxuICAgICAgKX1cbiAgICAgIHtzaG91bGRTaG93QXV0b1VwZGF0ZXIgJiYgKFxuICAgICAgICA8QXV0b1VwZGF0ZXJXcmFwcGVyXG4gICAgICAgICAgdmVyYm9zZT17dmVyYm9zZX1cbiAgICAgICAgICBvbkF1dG9VcGRhdGVyUmVzdWx0PXtvbkF1dG9VcGRhdGVyUmVzdWx0fVxuICAgICAgICAgIGF1dG9VcGRhdGVyUmVzdWx0PXthdXRvVXBkYXRlclJlc3VsdH1cbiAgICAgICAgICBpc1VwZGF0aW5nPXtpc0F1dG9VcGRhdGluZ31cbiAgICAgICAgICBvbkNoYW5nZUlzVXBkYXRpbmc9e29uQ2hhbmdlSXNVcGRhdGluZ31cbiAgICAgICAgICBzaG93U3VjY2Vzc01lc3NhZ2U9eyFpc1Nob3dpbmdDb21wYWN0TWVzc2FnZX1cbiAgICAgICAgLz5cbiAgICAgICl9XG4gICAgICB7ZmVhdHVyZSgnVk9JQ0VfTU9ERScpXG4gICAgICAgID8gdm9pY2VFbmFibGVkICYmXG4gICAgICAgICAgdm9pY2VFcnJvciAmJiAoXG4gICAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCIgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICAgICAge3ZvaWNlRXJyb3J9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgIClcbiAgICAgICAgOiBudWxsfVxuICAgICAgPE1lbW9yeVVzYWdlSW5kaWNhdG9yIC8+XG4gICAgICA8U2FuZGJveFByb21wdEZvb3RlckhpbnQgLz5cbiAgICA8Lz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTLEtBQUtDLFNBQVMsRUFBRUMsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3BFLFNBQ0UsS0FBS0MsWUFBWSxFQUNqQkMsZ0JBQWdCLFFBQ1gsOEJBQThCO0FBQ3JDLFNBQVNDLFFBQVEsUUFBUSxpQ0FBaUM7QUFDMUQsU0FBU0MsV0FBVyxRQUFRLHVCQUF1QjtBQUNuRCxTQUFTQyxhQUFhLFFBQVEsd0JBQXdCO0FBQ3RELGNBQWNDLGtCQUFrQixRQUFRLHNDQUFzQztBQUM5RSxTQUFTQyxzQkFBc0IsUUFBUSx1Q0FBdUM7QUFDOUUsY0FBY0MsWUFBWSxRQUFRLGdDQUFnQztBQUNsRSxTQUFTQyxnQkFBZ0IsUUFBUSxpQ0FBaUM7QUFDbEUsU0FBU0MsZUFBZSxRQUFRLGdDQUFnQztBQUNoRSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQVNDLGlCQUFpQixRQUFRLHNDQUFzQztBQUN4RSxTQUFTQywwQkFBMEIsUUFBUSx1Q0FBdUM7QUFDbEYsY0FBY0MsbUJBQW1CLFFBQVEsNkJBQTZCO0FBQ3RFLGNBQWNDLE9BQU8sUUFBUSx3QkFBd0I7QUFDckQsU0FDRUMsd0JBQXdCLEVBQ3hCQyx5QkFBeUIsRUFDekJDLG1CQUFtQixRQUNkLHFCQUFxQjtBQUM1QixjQUFjQyxpQkFBaUIsUUFBUSw0QkFBNEI7QUFDbkUsU0FBU0MsaUJBQWlCLFFBQVEsdUJBQXVCO0FBQ3pELFNBQVNDLFdBQVcsUUFBUSx5QkFBeUI7QUFDckQsU0FBU0MsY0FBYyxRQUFRLHVCQUF1QjtBQUN0RCxTQUFTQyxrQkFBa0IsUUFBUSx5Q0FBeUM7QUFDNUUsU0FBU0MsZ0JBQWdCLFFBQVEsb0JBQW9CO0FBQ3JELFNBQVNDLCtCQUErQixRQUFRLHlCQUF5QjtBQUN6RSxTQUFTQyw2QkFBNkIsUUFBUSx1QkFBdUI7QUFDckUsU0FBU0Msa0JBQWtCLFFBQVEsMEJBQTBCO0FBQzdELFNBQVNDLHdCQUF3QixRQUFRLGdDQUFnQztBQUN6RSxTQUFTQyxrQkFBa0IsUUFBUSwwQkFBMEI7QUFDN0QsU0FBU0Msb0JBQW9CLFFBQVEsNEJBQTRCO0FBQ2pFLFNBQVNDLG1CQUFtQixRQUFRLDJCQUEyQjtBQUMvRCxTQUFTQyxZQUFZLFFBQVEsb0JBQW9CO0FBQ2pELFNBQVNDLHVCQUF1QixRQUFRLDhCQUE4Qjs7QUFFdEU7QUFDQSxNQUFNQyxjQUFjLEVBQUUsT0FBTyxPQUFPLHFCQUFxQixFQUFFQSxjQUFjLEdBQ3ZFeEMsT0FBTyxDQUFDLFlBQVksQ0FBQyxHQUNqQnlDLE9BQU8sQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDRCxjQUFjLEdBQzdDLE1BQU0sSUFBSTtBQUNoQjs7QUFFQSxPQUFPLE1BQU1FLCtCQUErQixHQUFHLElBQUk7QUFFbkQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLFlBQVksRUFBRWpDLGtCQUFrQjtFQUNoQ2tDLGlCQUFpQixFQUFFcEIsaUJBQWlCLEdBQUcsSUFBSTtFQUMzQ3FCLGNBQWMsRUFBRSxPQUFPO0VBQ3ZCQyxLQUFLLEVBQUUsT0FBTztFQUNkQyxPQUFPLEVBQUUsT0FBTztFQUNoQkMsUUFBUSxFQUFFNUIsT0FBTyxFQUFFO0VBQ25CNkIsbUJBQW1CLEVBQUUsQ0FBQ0MsTUFBTSxFQUFFMUIsaUJBQWlCLEVBQUUsR0FBRyxJQUFJO0VBQ3hEMkIsa0JBQWtCLEVBQUUsQ0FBQ0MsVUFBVSxFQUFFLE9BQU8sRUFBRSxHQUFHLElBQUk7RUFDakRDLFlBQVksRUFBRXpDLFlBQVksR0FBRyxTQUFTO0VBQ3RDMEMsVUFBVSxDQUFDLEVBQUVuQyxtQkFBbUIsRUFBRTtFQUNsQ29DLGNBQWMsQ0FBQyxFQUFFLE9BQU87RUFDeEJDLFFBQVEsQ0FBQyxFQUFFLE9BQU87QUFDcEIsQ0FBQztBQUVELE9BQU8sU0FBQUMsY0FBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUF1QjtJQUFBakIsWUFBQTtJQUFBQyxpQkFBQTtJQUFBRSxLQUFBO0lBQUFELGNBQUE7SUFBQUUsT0FBQTtJQUFBQyxRQUFBO0lBQUFDLG1CQUFBO0lBQUFFLGtCQUFBO0lBQUFFLFlBQUE7SUFBQUMsVUFBQTtJQUFBQyxjQUFBLEVBQUFNLEVBQUE7SUFBQUwsUUFBQSxFQUFBTTtFQUFBLElBQUFKLEVBYXRCO0VBRk4sTUFBQUgsY0FBQSxHQUFBTSxFQUFzQixLQUF0QkUsU0FBc0IsR0FBdEIsS0FBc0IsR0FBdEJGLEVBQXNCO0VBQ3RCLE1BQUFMLFFBQUEsR0FBQU0sRUFBZ0IsS0FBaEJDLFNBQWdCLEdBQWhCLEtBQWdCLEdBQWhCRCxFQUFnQjtFQUFBLElBQUFFLEVBQUE7RUFBQSxJQUFBTCxDQUFBLFFBQUFYLFFBQUE7SUFHZCxNQUFBaUIscUJBQUEsR0FBOEJuQywrQkFBK0IsQ0FBQ2tCLFFBQVEsQ0FBQztJQUNoRWdCLEVBQUEsR0FBQWpDLDZCQUE2QixDQUFDa0MscUJBQXFCLENBQUM7SUFBQU4sQ0FBQSxNQUFBWCxRQUFBO0lBQUFXLENBQUEsTUFBQUssRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUwsQ0FBQTtFQUFBO0VBRjdELE1BQUFPLFVBQUEsR0FFRUYsRUFBMkQ7RUFNN0QsTUFBQUcsYUFBQSxHQUFzQnRELGdCQUFnQixDQUFDLENBQUM7RUFBQSxJQUFBdUQsRUFBQTtFQUFBLElBQUFULENBQUEsUUFBQVEsYUFBQSxJQUFBUixDQUFBLFFBQUFPLFVBQUE7SUFDUkUsRUFBQSxHQUFBbEQsMEJBQTBCLENBQ3hEZ0QsVUFBVSxFQUNWQyxhQUNGLENBQUM7SUFBQVIsQ0FBQSxNQUFBUSxhQUFBO0lBQUFSLENBQUEsTUFBQU8sVUFBQTtJQUFBUCxDQUFBLE1BQUFTLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFULENBQUE7RUFBQTtFQUhELE1BQUFVLHVCQUFBLEdBQWdDRCxFQUcvQixDQUFBRSx1QkFBd0I7RUFDekI7SUFBQUMsTUFBQSxFQUFBQztFQUFBLElBQThCN0Qsc0JBQXNCLENBQUMyQyxVQUFVLENBQUM7RUFDaEUsTUFBQW1CLGFBQUEsR0FBc0JqRSxXQUFXLENBQUNrRSxLQUFvQixDQUFDO0VBQ3ZEO0lBQUFDLGVBQUE7SUFBQUM7RUFBQSxJQUFnRHRFLGdCQUFnQixDQUFDLENBQUM7RUFDbEUsTUFBQXVFLGNBQUEsR0FBdUI1RCxpQkFBaUIsQ0FBQyxDQUFDO0VBQUEsSUFBQTZELEVBQUE7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQXBCLENBQUEsUUFBQWdCLGVBQUE7SUFHaENHLEVBQUEsR0FBQUEsQ0FBQTtNQUNSbEQsa0JBQWtCLENBQUMsQ0FBQW9ELElBQUEsRUFBQUMsT0FBQTtRQUNqQk4sZUFBZSxDQUFDO1VBQUFPLEdBQUEsRUFDVCxVQUFVO1VBQUFGLElBQUE7VUFBQUcsS0FBQSxFQUVSRixPQUFPLEdBQVAsT0FBNkIsR0FBN0JsQixTQUE2QjtVQUFBcUIsUUFBQSxFQUMxQkgsT0FBTyxHQUFQLFFBQTBCLEdBQTFCLEtBQTBCO1VBQUFJLFNBQUEsRUFDekJKLE9BQU8sR0FBUCxJQUFxQixHQUFyQjtRQUNiLENBQUMsQ0FBQztNQUFBLENBQ0gsQ0FBQztNQUFBLE9BQ0tLLE1BQThCO0lBQUEsQ0FDdEM7SUFBRVAsRUFBQSxJQUFDSixlQUFlLENBQUM7SUFBQWhCLENBQUEsTUFBQWdCLGVBQUE7SUFBQWhCLENBQUEsTUFBQW1CLEVBQUE7SUFBQW5CLENBQUEsTUFBQW9CLEVBQUE7RUFBQTtJQUFBRCxFQUFBLEdBQUFuQixDQUFBO0lBQUFvQixFQUFBLEdBQUFwQixDQUFBO0VBQUE7RUFYcEJ6RCxTQUFTLENBQUM0RSxFQVdULEVBQUVDLEVBQWlCLENBQUM7RUFHckIsTUFBQVEsc0JBQUEsR0FDRWYsU0FBUyxLQUFLLFdBRXVDLEtBRHBEbkIsWUFBWSxFQUFBbUMsUUFDdUMsSUFBakRuQyxZQUFZLEVBQUEyQixJQUFvQyxJQUExQjNCLFlBQVksQ0FBQW9DLFNBQVUsR0FBRyxDQUFHO0VBR3ZELE1BQUFDLHFCQUFBLEdBQ0UsQ0FBQ0gsc0JBQ2EsSUFEZDFDLGNBRXVDLElBQXZDRCxpQkFBaUIsRUFBQTJCLE1BQVEsS0FBSyxTQUFTO0VBR3pDLE1BQUFvQixlQUFBLEdBQXdCZCxjQUFjLENBQUFlLGNBQWU7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQWxDLENBQUEsUUFBQW1DLE1BQUEsQ0FBQUMsR0FBQTtJQUM1QkYsRUFBQSxHQUFBdEUsbUJBQW1CLENBQUMsQ0FBQztJQUFBb0MsQ0FBQSxNQUFBa0MsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQWxDLENBQUE7RUFBQTtFQUE5QyxNQUFBcUMsZ0JBQUEsR0FBeUJILEVBQXFCO0VBQzlDLE1BQUFJLGtCQUFBLEdBQ0VELGdCQUFnQixLQUFLLE1BQTJDLElBQWpDQSxnQkFBZ0IsS0FBSyxZQUFZO0VBQUEsSUFBQUUsRUFBQTtFQUFBLElBQUF2QyxDQUFBLFFBQUFtQyxNQUFBLENBQUFDLEdBQUE7SUFHbkRHLEVBQUEsR0FBQXpFLGlCQUFpQixDQUFDLENBQUM7SUFBQWtDLENBQUEsTUFBQXVDLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUF2QyxDQUFBO0VBQUE7RUFBbEMsTUFBQXdDLE1BQUEsR0FBZUQsRUFBbUI7RUFDbEMsTUFBQUUsNEJBQUEsR0FDRTdDLGNBQ3dCLElBRHhCLENBQ0NjLHVCQUN5QixJQUExQjFCLFlBQVksS0FBSyxTQUNTLElBQTFCQSxZQUFZLEtBQUssU0FDRyxJQUFwQndELE1BQU0sS0FBS3BDLFNBQVM7RUFBQSxJQUFBc0MsR0FBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBM0MsQ0FBQSxTQUFBZ0IsZUFBQSxJQUFBaEIsQ0FBQSxTQUFBaUIsa0JBQUEsSUFBQWpCLENBQUEsU0FBQXlDLDRCQUFBO0lBR1pFLEVBQUEsR0FBQUEsQ0FBQTtNQUNSLElBQUlGLDRCQUFzQyxJQUF0Q0QsTUFBc0M7UUFDeEM1RixRQUFRLENBQUMsa0NBQWtDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDaERvRSxlQUFlLENBQUM7VUFBQU8sR0FBQSxFQUNULHNCQUFzQjtVQUFBcUIsR0FBQSxFQUV6QixDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQ1osQ0FBQyx3QkFBd0IsQ0FDaEIsTUFBcUIsQ0FBckIscUJBQXFCLENBQ3BCLE9BQU0sQ0FBTixNQUFNLENBQ0wsUUFBUSxDQUFSLFFBQVEsQ0FDSixXQUFxQyxDQUFyQyxZQUFXMUUsZ0JBQWdCLENBQUNzRSxNQUFNLENBQUMsRUFBQyxDQUFDLEdBRXRELEVBUEMsSUFBSSxDQU9FO1VBQUFmLFFBQUEsRUFFQyxXQUFXO1VBQUFDLFNBQUEsRUFDVjtRQUNiLENBQUMsQ0FBQztNQUFBO1FBRUZULGtCQUFrQixDQUFDLHNCQUFzQixDQUFDO01BQUE7SUFDM0MsQ0FDRjtJQUFFeUIsR0FBQSxJQUNERCw0QkFBNEIsRUFDNUJELE1BQU0sRUFDTnhCLGVBQWUsRUFDZkMsa0JBQWtCLENBQ25CO0lBQUFqQixDQUFBLE9BQUFnQixlQUFBO0lBQUFoQixDQUFBLE9BQUFpQixrQkFBQTtJQUFBakIsQ0FBQSxPQUFBeUMsNEJBQUE7SUFBQXpDLENBQUEsT0FBQTBDLEdBQUE7SUFBQTFDLENBQUEsT0FBQTJDLEVBQUE7RUFBQTtJQUFBRCxHQUFBLEdBQUExQyxDQUFBO0lBQUEyQyxFQUFBLEdBQUEzQyxDQUFBO0VBQUE7RUExQkR6RCxTQUFTLENBQUNvRyxFQXFCVCxFQUFFRCxHQUtGLENBQUM7RUFNZ0IsTUFBQUcsR0FBQSxHQUFBaEQsUUFBUSxHQUFSLFlBQW9DLEdBQXBDLFVBQW9DO0VBUTdCLE1BQUFpRCxHQUFBLEdBQUFkLGVBQXdCLElBQXhCLEtBQXdCO0VBQUEsSUFBQWUsR0FBQTtFQUFBLElBQUEvQyxDQUFBLFNBQUFoQixZQUFBLElBQUFnQixDQUFBLFNBQUFmLGlCQUFBLElBQUFlLENBQUEsU0FBQWIsS0FBQSxJQUFBYSxDQUFBLFNBQUFOLFlBQUEsSUFBQU0sQ0FBQSxTQUFBZCxjQUFBLElBQUFjLENBQUEsU0FBQVUsdUJBQUEsSUFBQVYsQ0FBQSxTQUFBUSxhQUFBLElBQUFSLENBQUEsU0FBQUwsVUFBQSxJQUFBSyxDQUFBLFNBQUFjLGFBQUEsSUFBQWQsQ0FBQSxTQUFBVixtQkFBQSxJQUFBVSxDQUFBLFNBQUFSLGtCQUFBLElBQUFRLENBQUEsU0FBQStCLHFCQUFBLElBQUEvQixDQUFBLFNBQUE4QyxHQUFBLElBQUE5QyxDQUFBLFNBQUFPLFVBQUEsSUFBQVAsQ0FBQSxTQUFBWixPQUFBO0lBSjNDMkQsR0FBQSxJQUFDLG1CQUFtQixDQUNKckQsWUFBWSxDQUFaQSxhQUFXLENBQUMsQ0FDZEMsVUFBVSxDQUFWQSxXQUFTLENBQUMsQ0FDUG1CLGFBQWEsQ0FBYkEsY0FBWSxDQUFDLENBQ1gsZUFBd0IsQ0FBeEIsQ0FBQWdDLEdBQXVCLENBQUMsQ0FDckJSLGtCQUFrQixDQUFsQkEsbUJBQWlCLENBQUMsQ0FDeEJ0RCxZQUFZLENBQVpBLGFBQVcsQ0FBQyxDQUNuQkcsS0FBSyxDQUFMQSxNQUFJLENBQUMsQ0FDSEMsT0FBTyxDQUFQQSxRQUFNLENBQUMsQ0FDSm1CLFVBQVUsQ0FBVkEsV0FBUyxDQUFDLENBQ1BDLGFBQWEsQ0FBYkEsY0FBWSxDQUFDLENBQ0x1QixxQkFBcUIsQ0FBckJBLHNCQUFvQixDQUFDLENBQ3pCOUMsaUJBQWlCLENBQWpCQSxrQkFBZ0IsQ0FBQyxDQUNwQkMsY0FBYyxDQUFkQSxlQUFhLENBQUMsQ0FDTHdCLHVCQUF1QixDQUF2QkEsd0JBQXNCLENBQUMsQ0FDM0JwQixtQkFBbUIsQ0FBbkJBLG9CQUFrQixDQUFDLENBQ3BCRSxrQkFBa0IsQ0FBbEJBLG1CQUFpQixDQUFDLEdBQ3RDO0lBQUFRLENBQUEsT0FBQWhCLFlBQUE7SUFBQWdCLENBQUEsT0FBQWYsaUJBQUE7SUFBQWUsQ0FBQSxPQUFBYixLQUFBO0lBQUFhLENBQUEsT0FBQU4sWUFBQTtJQUFBTSxDQUFBLE9BQUFkLGNBQUE7SUFBQWMsQ0FBQSxPQUFBVSx1QkFBQTtJQUFBVixDQUFBLE9BQUFRLGFBQUE7SUFBQVIsQ0FBQSxPQUFBTCxVQUFBO0lBQUFLLENBQUEsT0FBQWMsYUFBQTtJQUFBZCxDQUFBLE9BQUFWLG1CQUFBO0lBQUFVLENBQUEsT0FBQVIsa0JBQUE7SUFBQVEsQ0FBQSxPQUFBK0IscUJBQUE7SUFBQS9CLENBQUEsT0FBQThDLEdBQUE7SUFBQTlDLENBQUEsT0FBQU8sVUFBQTtJQUFBUCxDQUFBLE9BQUFaLE9BQUE7SUFBQVksQ0FBQSxPQUFBK0MsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQS9DLENBQUE7RUFBQTtFQUFBLElBQUFnRCxHQUFBO0VBQUEsSUFBQWhELENBQUEsU0FBQTZDLEdBQUEsSUFBQTdDLENBQUEsU0FBQStDLEdBQUE7SUF4Qk5DLEdBQUEsSUFBQyxtQkFBbUIsQ0FDbEIsQ0FBQyxHQUFHLENBQ1ksYUFBUSxDQUFSLFFBQVEsQ0FDVixVQUFvQyxDQUFwQyxDQUFBSCxHQUFtQyxDQUFDLENBQ3BDLFVBQUMsQ0FBRCxHQUFDLENBQ0gsU0FBUSxDQUFSLFFBQVEsQ0FFbEIsQ0FBQUUsR0FpQkMsQ0FDSCxFQXhCQyxHQUFHLENBeUJOLEVBMUJDLG1CQUFtQixDQTBCRTtJQUFBL0MsQ0FBQSxPQUFBNkMsR0FBQTtJQUFBN0MsQ0FBQSxPQUFBK0MsR0FBQTtJQUFBL0MsQ0FBQSxPQUFBZ0QsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQWhELENBQUE7RUFBQTtFQUFBLE9BMUJ0QmdELEdBMEJzQjtBQUFBO0FBakluQixTQUFBckIsT0FBQTtFQUFBLE9BMkNVMUQsa0JBQWtCLENBQUMsSUFBSSxDQUFDO0FBQUE7QUEzQ2xDLFNBQUE4QyxNQUFBa0MsQ0FBQTtFQUFBLE9BNEJrQ0EsQ0FBQyxDQUFBbkMsYUFBYztBQUFBO0FBeUd4RCxTQUFTb0MsbUJBQW1CQSxDQUFDO0VBQzNCeEQsWUFBWTtFQUNaQyxVQUFVO0VBQ1ZtQixhQUFhO0VBQ2JrQixlQUFlO0VBQ2ZNLGtCQUFrQjtFQUNsQnRELFlBQVk7RUFDWkcsS0FBSztFQUNMQyxPQUFPO0VBQ1BtQixVQUFVO0VBQ1ZDLGFBQWE7RUFDYnVCLHFCQUFxQjtFQUNyQjlDLGlCQUFpQjtFQUNqQkMsY0FBYztFQUNkd0IsdUJBQXVCO0VBQ3ZCcEIsbUJBQW1CO0VBQ25CRTtBQXFCRixDQXBCQyxFQUFFO0VBQ0RFLFlBQVksRUFBRXpDLFlBQVksR0FBRyxTQUFTO0VBQ3RDMEMsVUFBVSxDQUFDLEVBQUVuQyxtQkFBbUIsRUFBRTtFQUNsQ3NELGFBQWEsRUFBRTtJQUNicUMsT0FBTyxFQUFFekcsWUFBWSxHQUFHLElBQUk7SUFDNUIwRyxLQUFLLEVBQUUxRyxZQUFZLEVBQUU7RUFDdkIsQ0FBQztFQUNEc0YsZUFBZSxFQUFFLE9BQU87RUFDeEJNLGtCQUFrQixFQUFFLE9BQU87RUFDM0J0RCxZQUFZLEVBQUVqQyxrQkFBa0I7RUFDaENvQyxLQUFLLEVBQUUsT0FBTztFQUNkQyxPQUFPLEVBQUUsT0FBTztFQUNoQm1CLFVBQVUsRUFBRSxNQUFNO0VBQ2xCQyxhQUFhLEVBQUUsTUFBTTtFQUNyQnVCLHFCQUFxQixFQUFFLE9BQU87RUFDOUI5QyxpQkFBaUIsRUFBRXBCLGlCQUFpQixHQUFHLElBQUk7RUFDM0NxQixjQUFjLEVBQUUsT0FBTztFQUN2QndCLHVCQUF1QixFQUFFLE9BQU87RUFDaENwQixtQkFBbUIsRUFBRSxDQUFDQyxNQUFNLEVBQUUxQixpQkFBaUIsRUFBRSxHQUFHLElBQUk7RUFDeEQyQixrQkFBa0IsRUFBRSxDQUFDQyxVQUFVLEVBQUUsT0FBTyxFQUFFLEdBQUcsSUFBSTtBQUNuRCxDQUFDLENBQUMsRUFBRW5ELFNBQVMsQ0FBQztFQUNaO0VBQ0E7RUFDQTtFQUNBLE1BQU0sQ0FBQytHLGdCQUFnQixFQUFFQyxtQkFBbUIsQ0FBQyxHQUFHN0csUUFBUSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUM7RUFDN0VGLFNBQVMsQ0FBQyxNQUFNO0lBQ2QsSUFBSSxDQUFDb0IseUJBQXlCLENBQUMsQ0FBQyxFQUFFO0lBQ2xDLE1BQU00RixRQUFRLEdBQUdDLFdBQVcsQ0FDMUIsQ0FBQ0MsT0FBTyxFQUFFcEgsS0FBSyxDQUFDcUgsUUFBUSxDQUFDckgsS0FBSyxDQUFDc0gsY0FBYyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsQ0FBQyxLQUFLO01BQ2hFLE1BQU1DLEVBQUUsR0FBR2xHLHdCQUF3QixDQUFDLENBQUM7TUFDckMsTUFBTW1HLElBQUksR0FBR0QsRUFBRSxJQUFJLE1BQU0sR0FBRzVGLGNBQWMsQ0FBQzRGLEVBQUUsQ0FBQyxHQUFHLElBQUk7TUFDckRILE9BQU8sQ0FBQ0ssSUFBSSxJQUFLRCxJQUFJLEtBQUtDLElBQUksR0FBR0EsSUFBSSxHQUFHRCxJQUFLLENBQUM7SUFDaEQsQ0FBQyxFQUNELElBQUksRUFDSlAsbUJBQ0YsQ0FBQztJQUNELE9BQU8sTUFBTVMsYUFBYSxDQUFDUixRQUFRLENBQUM7RUFDdEMsQ0FBQyxFQUFFLEVBQUUsQ0FBQzs7RUFFTjtFQUNBLE1BQU1TLFVBQVUsR0FBRzVILE9BQU8sQ0FBQyxZQUFZLENBQUM7RUFDcEM7RUFDQVUsYUFBYSxDQUFDbUcsQ0FBQyxJQUFJQSxDQUFDLENBQUNlLFVBQVUsQ0FBQyxHQUMvQixNQUFNLElBQUlDLEtBQU07RUFDckI7RUFDQSxNQUFNQyxZQUFZLEdBQUc5SCxPQUFPLENBQUMsWUFBWSxDQUFDLEdBQUdlLGVBQWUsQ0FBQyxDQUFDLEdBQUcsS0FBSztFQUN0RSxNQUFNZ0gsVUFBVSxHQUFHL0gsT0FBTyxDQUFDLFlBQVksQ0FBQztFQUNwQztFQUNBVSxhQUFhLENBQUNtRyxHQUFDLElBQUlBLEdBQUMsQ0FBQ2tCLFVBQVUsQ0FBQyxHQUNoQyxJQUFJO0VBQ1IsTUFBTUMsV0FBVyxHQUNmaEksT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJQSxPQUFPLENBQUMsY0FBYyxDQUFDO0VBQ3hDO0VBQ0FTLFdBQVcsQ0FBQ29HLEdBQUMsSUFBSUEsR0FBQyxDQUFDbUIsV0FBVyxDQUFDLEdBQy9CLEtBQUs7O0VBRVg7RUFDQTtFQUNBLElBQ0VoSSxPQUFPLENBQUMsWUFBWSxDQUFDLElBQ3JCOEgsWUFBWSxLQUNYRixVQUFVLEtBQUssV0FBVyxJQUFJQSxVQUFVLEtBQUssWUFBWSxDQUFDLEVBQzNEO0lBQ0EsT0FBTyxDQUFDLGNBQWMsQ0FBQyxVQUFVLENBQUMsQ0FBQ0EsVUFBVSxDQUFDLEdBQUc7RUFDbkQ7RUFFQSxPQUNFO0FBQ0osTUFBTSxDQUFDLGtCQUFrQixDQUFDLFlBQVksQ0FBQyxDQUFDdEUsWUFBWSxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUNDLFVBQVUsQ0FBQztBQUM3RSxNQUFNLENBQUNtQixhQUFhLENBQUNxQyxPQUFPLEtBQ25CLEtBQUssSUFBSXJDLGFBQWEsQ0FBQ3FDLE9BQU8sR0FDN0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQ3JDLGFBQWEsQ0FBQ3FDLE9BQU8sQ0FBQzVCLEdBQUcsQ0FBQztBQUMvRCxZQUFZLENBQUNULGFBQWEsQ0FBQ3FDLE9BQU8sQ0FBQ1AsR0FBRztBQUN0QyxVQUFVLEVBQUUsSUFBSSxDQUFDLEdBRVAsQ0FBQyxJQUFJLENBQ0gsS0FBSyxDQUFDLENBQUM5QixhQUFhLENBQUNxQyxPQUFPLENBQUMzQixLQUFLLENBQUMsQ0FDbkMsUUFBUSxDQUFDLENBQUMsQ0FBQ1YsYUFBYSxDQUFDcUMsT0FBTyxDQUFDM0IsS0FBSyxDQUFDLENBQ3ZDLElBQUksQ0FBQyxVQUFVO0FBRTNCLFlBQVksQ0FBQ1YsYUFBYSxDQUFDcUMsT0FBTyxDQUFDOUIsSUFBSTtBQUN2QyxVQUFVLEVBQUUsSUFBSSxDQUNQLENBQUM7QUFDVixNQUFNLENBQUNXLGVBQWUsSUFBSSxDQUFDTSxrQkFBa0IsSUFDckMsQ0FBQyxHQUFHO0FBQ1osVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFVBQVU7QUFDeEM7QUFDQSxVQUFVLEVBQUUsSUFBSTtBQUNoQixRQUFRLEVBQUUsR0FBRyxDQUNOO0FBQ1AsTUFBTSxDQUFDZSxnQkFBZ0IsSUFDZixDQUFDLEdBQUc7QUFDWixVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLFVBQVU7QUFDL0MsMENBQTBDLENBQUMsR0FBRztBQUM5QyxVQUFVLEVBQUUsSUFBSTtBQUNoQixVQUFVLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsVUFBVTtBQUN4QyxhQUFhLENBQUNBLGdCQUFnQixDQUFDO0FBQy9CLFVBQVUsRUFBRSxJQUFJO0FBQ2hCLFFBQVEsRUFBRSxHQUFHLENBQ047QUFDUCxNQUFNLENBQUMsQ0FBQ3JFLFlBQVksS0FBSyxTQUFTLElBQUlBLFlBQVksS0FBSyxTQUFTLEtBQ3hELENBQUMsR0FBRztBQUNaLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVTtBQUM3QyxZQUFZLENBQUNqQixXQUFXLENBQUNzRyxPQUFPLENBQUNDLEdBQUcsQ0FBQ0Msa0JBQWtCLENBQUMsR0FDeEMsa0NBQWtDLEdBQ2xDLDRCQUE0QjtBQUM1QyxVQUFVLEVBQUUsSUFBSTtBQUNoQixRQUFRLEVBQUUsR0FBRyxDQUNOO0FBQ1AsTUFBTSxDQUFDcEYsS0FBSyxJQUNKLENBQUMsR0FBRztBQUNaLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsVUFBVTtBQUMvQztBQUNBLFVBQVUsRUFBRSxJQUFJO0FBQ2hCLFFBQVEsRUFBRSxHQUFHLENBQ047QUFDUCxNQUFNLENBQUNILFlBQVksS0FBSyxTQUFTLElBQUlBLFlBQVksS0FBSyxTQUFTLElBQUlJLE9BQU8sSUFDbEUsQ0FBQyxHQUFHO0FBQ1osVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLFVBQVU7QUFDeEMsWUFBWSxDQUFDbUIsVUFBVSxDQUFDO0FBQ3hCLFVBQVUsRUFBRSxJQUFJO0FBQ2hCLFFBQVEsRUFBRSxHQUFHLENBQ047QUFDUCxNQUFNLENBQUMsQ0FBQzZELFdBQVcsSUFDWCxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQzdELFVBQVUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDQyxhQUFhLENBQUMsR0FDNUQ7QUFDUCxNQUFNLENBQUN1QixxQkFBcUIsSUFDcEIsQ0FBQyxrQkFBa0IsQ0FDakIsT0FBTyxDQUFDLENBQUMzQyxPQUFPLENBQUMsQ0FDakIsbUJBQW1CLENBQUMsQ0FBQ0UsbUJBQW1CLENBQUMsQ0FDekMsaUJBQWlCLENBQUMsQ0FBQ0wsaUJBQWlCLENBQUMsQ0FDckMsVUFBVSxDQUFDLENBQUNDLGNBQWMsQ0FBQyxDQUMzQixrQkFBa0IsQ0FBQyxDQUFDTSxrQkFBa0IsQ0FBQyxDQUN2QyxrQkFBa0IsQ0FBQyxDQUFDLENBQUNrQix1QkFBdUIsQ0FBQyxHQUVoRDtBQUNQLE1BQU0sQ0FBQ3RFLE9BQU8sQ0FBQyxZQUFZLENBQUMsR0FDbEI4SCxZQUFZLElBQ1pDLFVBQVUsSUFDUixDQUFDLEdBQUc7QUFDaEIsY0FBYyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxVQUFVO0FBQ2pELGdCQUFnQixDQUFDQSxVQUFVO0FBQzNCLGNBQWMsRUFBRSxJQUFJO0FBQ3BCLFlBQVksRUFBRSxHQUFHLENBQ04sR0FDRCxJQUFJO0FBQ2QsTUFBTSxDQUFDLG9CQUFvQjtBQUMzQixNQUFNLENBQUMsdUJBQXVCO0FBQzlCLElBQUksR0FBRztBQUVQIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/PromptInput/PromptInput.tsx b/components/PromptInput/PromptInput.tsx new file mode 100644 index 0000000..128e73c --- /dev/null +++ b/components/PromptInput/PromptInput.tsx @@ -0,0 +1,2339 @@ +import { feature } from 'bun:bundle'; +import chalk from 'chalk'; +import * as path from 'path'; +import * as React from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'; +import { useNotifications } from 'src/context/notifications.js'; +import { useCommandQueue } from 'src/hooks/useCommandQueue.js'; +import { type IDEAtMentioned, useIdeAtMentioned } from 'src/hooks/useIdeAtMentioned.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { type AppState, useAppState, useAppStateStore, useSetAppState } from 'src/state/AppState.js'; +import type { FooterItem } from 'src/state/AppStateStore.js'; +import { getCwd } from 'src/utils/cwd.js'; +import { isQueuedCommandEditable, popAllEditable } from 'src/utils/messageQueueManager.js'; +import stripAnsi from 'strip-ansi'; +import { companionReservedColumns } from '../../buddy/CompanionSprite.js'; +import { findBuddyTriggerPositions, useBuddyNotification } from '../../buddy/useBuddyNotification.js'; +import { FastModePicker } from '../../commands/fast/fast.js'; +import { isUltrareviewEnabled } from '../../commands/review/ultrareviewEnabled.js'; +import { getNativeCSIuTerminalDisplayName } from '../../commands/terminalSetup/terminalSetup.js'; +import { type Command, hasCommand } from '../../commands.js'; +import { useIsModalOverlayActive } from '../../context/overlayContext.js'; +import { useSetPromptOverlayDialog } from '../../context/promptOverlayContext.js'; +import { formatImageRef, formatPastedTextRef, getPastedTextRefNumLines, parseReferences } from '../../history.js'; +import type { VerificationStatus } from '../../hooks/useApiKeyVerification.js'; +import { type HistoryMode, useArrowKeyHistory } from '../../hooks/useArrowKeyHistory.js'; +import { useDoublePress } from '../../hooks/useDoublePress.js'; +import { useHistorySearch } from '../../hooks/useHistorySearch.js'; +import type { IDESelection } from '../../hooks/useIdeSelection.js'; +import { useInputBuffer } from '../../hooks/useInputBuffer.js'; +import { useMainLoopModel } from '../../hooks/useMainLoopModel.js'; +import { usePromptSuggestion } from '../../hooks/usePromptSuggestion.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { useTypeahead } from '../../hooks/useTypeahead.js'; +import type { BorderTextOptions } from '../../ink/render-border.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, type ClickEvent, type Key, Text, useInput } from '../../ink.js'; +import { useOptionalKeybindingContext } from '../../keybindings/KeybindingContext.js'; +import { getShortcutDisplay } from '../../keybindings/shortcutFormat.js'; +import { useKeybinding, useKeybindings } from '../../keybindings/useKeybinding.js'; +import type { MCPServerConnection } from '../../services/mcp/types.js'; +import { abortPromptSuggestion, logSuggestionSuppressed } from '../../services/PromptSuggestion/promptSuggestion.js'; +import { type ActiveSpeculationState, abortSpeculation } from '../../services/PromptSuggestion/speculation.js'; +import { getActiveAgentForInput, getViewedTeammateTask } from '../../state/selectors.js'; +import { enterTeammateView, exitTeammateView, stopOrDismissAgent } from '../../state/teammateViewHelpers.js'; +import type { ToolPermissionContext } from '../../Tool.js'; +import { getRunningTeammatesSorted } from '../../tasks/InProcessTeammateTask/InProcessTeammateTask.js'; +import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js'; +import { isPanelAgentTask, type LocalAgentTaskState } from '../../tasks/LocalAgentTask/LocalAgentTask.js'; +import { isBackgroundTask } from '../../tasks/types.js'; +import { AGENT_COLOR_TO_THEME_COLOR, AGENT_COLORS, type AgentColorName } from '../../tools/AgentTool/agentColorManager.js'; +import type { AgentDefinition } from '../../tools/AgentTool/loadAgentsDir.js'; +import type { Message } from '../../types/message.js'; +import type { PermissionMode } from '../../types/permissions.js'; +import type { BaseTextInputProps, PromptInputMode, VimMode } from '../../types/textInputTypes.js'; +import { isAgentSwarmsEnabled } from '../../utils/agentSwarmsEnabled.js'; +import { count } from '../../utils/array.js'; +import type { AutoUpdaterResult } from '../../utils/autoUpdater.js'; +import { Cursor } from '../../utils/Cursor.js'; +import { getGlobalConfig, type PastedContent, saveGlobalConfig } from '../../utils/config.js'; +import { logForDebugging } from '../../utils/debug.js'; +import { parseDirectMemberMessage, sendDirectMemberMessage } from '../../utils/directMemberMessage.js'; +import type { EffortLevel } from '../../utils/effort.js'; +import { env } from '../../utils/env.js'; +import { errorMessage } from '../../utils/errors.js'; +import { isBilledAsExtraUsage } from '../../utils/extraUsage.js'; +import { getFastModeUnavailableReason, isFastModeAvailable, isFastModeCooldown, isFastModeEnabled, isFastModeSupportedByModel } from '../../utils/fastMode.js'; +import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js'; +import type { PromptInputHelpers } from '../../utils/handlePromptSubmit.js'; +import { getImageFromClipboard, PASTE_THRESHOLD } from '../../utils/imagePaste.js'; +import type { ImageDimensions } from '../../utils/imageResizer.js'; +import { cacheImagePath, storeImage } from '../../utils/imageStore.js'; +import { isMacosOptionChar, MACOS_OPTION_SPECIAL_CHARS } from '../../utils/keyboardShortcuts.js'; +import { logError } from '../../utils/log.js'; +import { isOpus1mMergeEnabled, modelDisplayString } from '../../utils/model/model.js'; +import { setAutoModeActive } from '../../utils/permissions/autoModeState.js'; +import { cyclePermissionMode, getNextPermissionMode } from '../../utils/permissions/getNextPermissionMode.js'; +import { transitionPermissionMode } from '../../utils/permissions/permissionSetup.js'; +import { getPlatform } from '../../utils/platform.js'; +import type { ProcessUserInputContext } from '../../utils/processUserInput/processUserInput.js'; +import { editPromptInEditor } from '../../utils/promptEditor.js'; +import { hasAutoModeOptIn } from '../../utils/settings/settings.js'; +import { findBtwTriggerPositions } from '../../utils/sideQuestion.js'; +import { findSlashCommandPositions } from '../../utils/suggestions/commandSuggestions.js'; +import { findSlackChannelPositions, getKnownChannelsVersion, hasSlackMcpServer, subscribeKnownChannels } from '../../utils/suggestions/slackChannelSuggestions.js'; +import { isInProcessEnabled } from '../../utils/swarm/backends/registry.js'; +import { syncTeammateMode } from '../../utils/swarm/teamHelpers.js'; +import type { TeamSummary } from '../../utils/teamDiscovery.js'; +import { getTeammateColor } from '../../utils/teammate.js'; +import { isInProcessTeammate } from '../../utils/teammateContext.js'; +import { writeToMailbox } from '../../utils/teammateMailbox.js'; +import type { TextHighlight } from '../../utils/textHighlighting.js'; +import type { Theme } from '../../utils/theme.js'; +import { findThinkingTriggerPositions, getRainbowColor, isUltrathinkEnabled } from '../../utils/thinking.js'; +import { findTokenBudgetPositions } from '../../utils/tokenBudget.js'; +import { findUltraplanTriggerPositions, findUltrareviewTriggerPositions } from '../../utils/ultraplan/keyword.js'; +import { AutoModeOptInDialog } from '../AutoModeOptInDialog.js'; +import { BridgeDialog } from '../BridgeDialog.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { getVisibleAgentTasks, useCoordinatorTaskCount } from '../CoordinatorAgentStatus.js'; +import { getEffortNotificationText } from '../EffortIndicator.js'; +import { getFastIconString } from '../FastIcon.js'; +import { GlobalSearchDialog } from '../GlobalSearchDialog.js'; +import { HistorySearchDialog } from '../HistorySearchDialog.js'; +import { ModelPicker } from '../ModelPicker.js'; +import { QuickOpenDialog } from '../QuickOpenDialog.js'; +import TextInput from '../TextInput.js'; +import { ThinkingToggle } from '../ThinkingToggle.js'; +import { BackgroundTasksDialog } from '../tasks/BackgroundTasksDialog.js'; +import { shouldHideTasksFooter } from '../tasks/taskStatusUtils.js'; +import { TeamsDialog } from '../teams/TeamsDialog.js'; +import VimTextInput from '../VimTextInput.js'; +import { getModeFromInput, getValueFromInput } from './inputModes.js'; +import { FOOTER_TEMPORARY_STATUS_TIMEOUT, Notifications } from './Notifications.js'; +import PromptInputFooter from './PromptInputFooter.js'; +import type { SuggestionItem } from './PromptInputFooterSuggestions.js'; +import { PromptInputModeIndicator } from './PromptInputModeIndicator.js'; +import { PromptInputQueuedCommands } from './PromptInputQueuedCommands.js'; +import { PromptInputStashNotice } from './PromptInputStashNotice.js'; +import { useMaybeTruncateInput } from './useMaybeTruncateInput.js'; +import { usePromptInputPlaceholder } from './usePromptInputPlaceholder.js'; +import { useShowFastIconHint } from './useShowFastIconHint.js'; +import { useSwarmBanner } from './useSwarmBanner.js'; +import { isNonSpacePrintable, isVimModeEnabled } from './utils.js'; +type Props = { + debug: boolean; + ideSelection: IDESelection | undefined; + toolPermissionContext: ToolPermissionContext; + setToolPermissionContext: (ctx: ToolPermissionContext) => void; + apiKeyStatus: VerificationStatus; + commands: Command[]; + agents: AgentDefinition[]; + isLoading: boolean; + verbose: boolean; + messages: Message[]; + onAutoUpdaterResult: (result: AutoUpdaterResult) => void; + autoUpdaterResult: AutoUpdaterResult | null; + input: string; + onInputChange: (value: string) => void; + mode: PromptInputMode; + onModeChange: (mode: PromptInputMode) => void; + stashedPrompt: { + text: string; + cursorOffset: number; + pastedContents: Record; + } | undefined; + setStashedPrompt: (value: { + text: string; + cursorOffset: number; + pastedContents: Record; + } | undefined) => void; + submitCount: number; + onShowMessageSelector: () => void; + /** Fullscreen message actions: shift+↑ enters cursor. */ + onMessageActionsEnter?: () => void; + mcpClients: MCPServerConnection[]; + pastedContents: Record; + setPastedContents: React.Dispatch>>; + vimMode: VimMode; + setVimMode: (mode: VimMode) => void; + showBashesDialog: string | boolean; + setShowBashesDialog: (show: string | boolean) => void; + onExit: () => void; + getToolUseContext: (messages: Message[], newMessages: Message[], abortController: AbortController, mainLoopModel: string) => ProcessUserInputContext; + onSubmit: (input: string, helpers: PromptInputHelpers, speculationAccept?: { + state: ActiveSpeculationState; + speculationSessionTimeSavedMs: number; + setAppState: (f: (prev: AppState) => AppState) => void; + }, options?: { + fromKeybinding?: boolean; + }) => Promise; + onAgentSubmit?: (input: string, task: InProcessTeammateTaskState | LocalAgentTaskState, helpers: PromptInputHelpers) => Promise; + isSearchingHistory: boolean; + setIsSearchingHistory: (isSearching: boolean) => void; + onDismissSideQuestion?: () => void; + isSideQuestionVisible?: boolean; + helpOpen: boolean; + setHelpOpen: React.Dispatch>; + hasSuppressedDialogs?: boolean; + isLocalJSXCommandActive?: boolean; + insertTextRef?: React.MutableRefObject<{ + insert: (text: string) => void; + setInputWithCursor: (value: string, cursor: number) => void; + cursorOffset: number; + } | null>; + voiceInterimRange?: { + start: number; + end: number; + } | null; +}; + +// Bottom slot has maxHeight="50%"; reserve lines for footer, border, status. +const PROMPT_FOOTER_LINES = 5; +const MIN_INPUT_VIEWPORT_LINES = 3; +function PromptInput({ + debug, + ideSelection, + toolPermissionContext, + setToolPermissionContext, + apiKeyStatus, + commands, + agents, + isLoading, + verbose, + messages, + onAutoUpdaterResult, + autoUpdaterResult, + input, + onInputChange, + mode, + onModeChange, + stashedPrompt, + setStashedPrompt, + submitCount, + onShowMessageSelector, + onMessageActionsEnter, + mcpClients, + pastedContents, + setPastedContents, + vimMode, + setVimMode, + showBashesDialog, + setShowBashesDialog, + onExit, + getToolUseContext, + onSubmit: onSubmitProp, + onAgentSubmit, + isSearchingHistory, + setIsSearchingHistory, + onDismissSideQuestion, + isSideQuestionVisible, + helpOpen, + setHelpOpen, + hasSuppressedDialogs, + isLocalJSXCommandActive = false, + insertTextRef, + voiceInterimRange +}: Props): React.ReactNode { + const mainLoopModel = useMainLoopModel(); + // A local-jsx command (e.g., /mcp while agent is running) renders a full- + // screen dialog on top of PromptInput via the immediate-command path with + // shouldHidePromptInput: false. Those dialogs don't register in the overlay + // system, so treat them as a modal overlay here to stop navigation keys from + // leaking into TextInput/footer handlers and stacking a second dialog. + const isModalOverlayActive = useIsModalOverlayActive() || isLocalJSXCommandActive; + const [isAutoUpdating, setIsAutoUpdating] = useState(false); + const [exitMessage, setExitMessage] = useState<{ + show: boolean; + key?: string; + }>({ + show: false + }); + const [cursorOffset, setCursorOffset] = useState(input.length); + // Track the last input value set via internal handlers so we can detect + // external input changes (e.g. speech-to-text injection) and move cursor to end. + const lastInternalInputRef = React.useRef(input); + if (input !== lastInternalInputRef.current) { + // Input changed externally (not through any internal handler) — move cursor to end + setCursorOffset(input.length); + lastInternalInputRef.current = input; + } + // Wrap onInputChange to track internal changes before they trigger re-render + const trackAndSetInput = React.useCallback((value: string) => { + lastInternalInputRef.current = value; + onInputChange(value); + }, [onInputChange]); + // Expose an insertText function so callers (e.g. STT) can splice text at the + // current cursor position instead of replacing the entire input. + if (insertTextRef) { + insertTextRef.current = { + cursorOffset, + insert: (text: string) => { + const needsSpace = cursorOffset === input.length && input.length > 0 && !/\s$/.test(input); + const insertText = needsSpace ? ' ' + text : text; + const newValue = input.slice(0, cursorOffset) + insertText + input.slice(cursorOffset); + lastInternalInputRef.current = newValue; + onInputChange(newValue); + setCursorOffset(cursorOffset + insertText.length); + }, + setInputWithCursor: (value: string, cursor: number) => { + lastInternalInputRef.current = value; + onInputChange(value); + setCursorOffset(cursor); + } + }; + } + const store = useAppStateStore(); + const setAppState = useSetAppState(); + const tasks = useAppState(s => s.tasks); + const replBridgeConnected = useAppState(s => s.replBridgeConnected); + const replBridgeExplicit = useAppState(s => s.replBridgeExplicit); + const replBridgeReconnecting = useAppState(s => s.replBridgeReconnecting); + // Must match BridgeStatusIndicator's render condition (PromptInputFooter.tsx) — + // the pill returns null for implicit-and-not-reconnecting, so nav must too, + // otherwise bridge becomes an invisible selection stop. + const bridgeFooterVisible = replBridgeConnected && (replBridgeExplicit || replBridgeReconnecting); + // Tmux pill (ant-only) — visible when there's an active tungsten session + const hasTungstenSession = useAppState(s => "external" === 'ant' && s.tungstenActiveSession !== undefined); + const tmuxFooterVisible = "external" === 'ant' && hasTungstenSession; + // WebBrowser pill — visible when a browser is open + const bagelFooterVisible = useAppState(s => false); + const teamContext = useAppState(s => s.teamContext); + const queuedCommands = useCommandQueue(); + const promptSuggestionState = useAppState(s => s.promptSuggestion); + const speculation = useAppState(s => s.speculation); + const speculationSessionTimeSavedMs = useAppState(s => s.speculationSessionTimeSavedMs); + const viewingAgentTaskId = useAppState(s => s.viewingAgentTaskId); + const viewSelectionMode = useAppState(s => s.viewSelectionMode); + const showSpinnerTree = useAppState(s => s.expandedView) === 'teammates'; + const { + companion: _companion, + companionMuted + } = feature('BUDDY') ? getGlobalConfig() : { + companion: undefined, + companionMuted: undefined + }; + const companionFooterVisible = !!_companion && !companionMuted; + // Brief mode: BriefSpinner/BriefIdleStatus own the 2-row footprint above + // the input. Dropping marginTop here lets the spinner sit flush against + // the input bar. viewingAgentTaskId mirrors the gate on both (Spinner.tsx, + // REPL.tsx) — teammate view falls back to SpinnerWithVerbInner which has + // its own marginTop, so the gap stays even without ours. + const briefOwnsGap = feature('KAIROS') || feature('KAIROS_BRIEF') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s => s.isBriefOnly) && !viewingAgentTaskId : false; + const mainLoopModel_ = useAppState(s => s.mainLoopModel); + const mainLoopModelForSession = useAppState(s => s.mainLoopModelForSession); + const thinkingEnabled = useAppState(s => s.thinkingEnabled); + const isFastMode = useAppState(s => isFastModeEnabled() ? s.fastMode : false); + const effortValue = useAppState(s => s.effortValue); + const viewedTeammate = getViewedTeammateTask(store.getState()); + const viewingAgentName = viewedTeammate?.identity.agentName; + // identity.color is typed as `string | undefined` (not AgentColorName) because + // teammate identity comes from file-based config. Validate before casting to + // ensure we only use valid color names (falls back to cyan if invalid). + const viewingAgentColor = viewedTeammate?.identity.color && AGENT_COLORS.includes(viewedTeammate.identity.color as AgentColorName) ? viewedTeammate.identity.color as AgentColorName : undefined; + // In-process teammates sorted alphabetically for footer team selector + const inProcessTeammates = useMemo(() => getRunningTeammatesSorted(tasks), [tasks]); + + // Team mode: all background tasks are in-process teammates + const isTeammateMode = inProcessTeammates.length > 0 || viewedTeammate !== undefined; + + // When viewing a teammate, show their permission mode in the footer instead of the leader's + const effectiveToolPermissionContext = useMemo((): ToolPermissionContext => { + if (viewedTeammate) { + return { + ...toolPermissionContext, + mode: viewedTeammate.permissionMode + }; + } + return toolPermissionContext; + }, [viewedTeammate, toolPermissionContext]); + const { + historyQuery, + setHistoryQuery, + historyMatch, + historyFailedMatch + } = useHistorySearch(entry => { + setPastedContents(entry.pastedContents); + void onSubmit(entry.display); + }, input, trackAndSetInput, setCursorOffset, cursorOffset, onModeChange, mode, isSearchingHistory, setIsSearchingHistory, setPastedContents, pastedContents); + // Counter for paste IDs (shared between images and text). + // Compute initial value once from existing messages (for --continue/--resume). + // useRef(fn()) evaluates fn() on every render and discards the result after + // mount — getInitialPasteId walks all messages + regex-scans text blocks, + // so guard with a lazy-init pattern to run it exactly once. + const nextPasteIdRef = useRef(-1); + if (nextPasteIdRef.current === -1) { + nextPasteIdRef.current = getInitialPasteId(messages); + } + // Armed by onImagePaste; if the very next keystroke is a non-space + // printable, inputFilter prepends a space before it. Any other input + // (arrow, escape, backspace, paste, space) disarms without inserting. + const pendingSpaceAfterPillRef = useRef(false); + const [showTeamsDialog, setShowTeamsDialog] = useState(false); + const [showBridgeDialog, setShowBridgeDialog] = useState(false); + const [teammateFooterIndex, setTeammateFooterIndex] = useState(0); + // -1 sentinel: tasks pill is selected but no specific agent row is selected yet. + // First ↓ selects the pill, second ↓ moves to row 0. Prevents double-select + // of pill + row when both bg tasks (pill) and forked agents (rows) are visible. + const coordinatorTaskIndex = useAppState(s => s.coordinatorTaskIndex); + const setCoordinatorTaskIndex = useCallback((v: number | ((prev: number) => number)) => setAppState(prev => { + const next = typeof v === 'function' ? v(prev.coordinatorTaskIndex) : v; + if (next === prev.coordinatorTaskIndex) return prev; + return { + ...prev, + coordinatorTaskIndex: next + }; + }), [setAppState]); + const coordinatorTaskCount = useCoordinatorTaskCount(); + // The pill (BackgroundTaskStatus) only renders when non-local_agent bg tasks + // exist. When only local_agent tasks are running (coordinator/fork mode), the + // pill is absent, so the -1 sentinel would leave nothing visually selected. + // In that case, skip -1 and treat 0 as the minimum selectable index. + const hasBgTaskPill = useMemo(() => Object.values(tasks).some(t => isBackgroundTask(t) && !("external" === 'ant' && isPanelAgentTask(t))), [tasks]); + const minCoordinatorIndex = hasBgTaskPill ? -1 : 0; + // Clamp index when tasks complete and the list shrinks beneath the cursor + useEffect(() => { + if (coordinatorTaskIndex >= coordinatorTaskCount) { + setCoordinatorTaskIndex(Math.max(minCoordinatorIndex, coordinatorTaskCount - 1)); + } else if (coordinatorTaskIndex < minCoordinatorIndex) { + setCoordinatorTaskIndex(minCoordinatorIndex); + } + }, [coordinatorTaskCount, coordinatorTaskIndex, minCoordinatorIndex]); + const [isPasting, setIsPasting] = useState(false); + const [isExternalEditorActive, setIsExternalEditorActive] = useState(false); + const [showModelPicker, setShowModelPicker] = useState(false); + const [showQuickOpen, setShowQuickOpen] = useState(false); + const [showGlobalSearch, setShowGlobalSearch] = useState(false); + const [showHistoryPicker, setShowHistoryPicker] = useState(false); + const [showFastModePicker, setShowFastModePicker] = useState(false); + const [showThinkingToggle, setShowThinkingToggle] = useState(false); + const [showAutoModeOptIn, setShowAutoModeOptIn] = useState(false); + const [previousModeBeforeAuto, setPreviousModeBeforeAuto] = useState(null); + const autoModeOptInTimeoutRef = useRef(null); + + // Check if cursor is on the first line of input + const isCursorOnFirstLine = useMemo(() => { + const firstNewlineIndex = input.indexOf('\n'); + if (firstNewlineIndex === -1) { + return true; // No newlines, cursor is always on first line + } + return cursorOffset <= firstNewlineIndex; + }, [input, cursorOffset]); + const isCursorOnLastLine = useMemo(() => { + const lastNewlineIndex = input.lastIndexOf('\n'); + if (lastNewlineIndex === -1) { + return true; // No newlines, cursor is always on last line + } + return cursorOffset > lastNewlineIndex; + }, [input, cursorOffset]); + + // Derive team info from teamContext (no filesystem I/O needed) + // A session can only lead one team at a time + const cachedTeams: TeamSummary[] = useMemo(() => { + if (!isAgentSwarmsEnabled()) return []; + // In-process mode uses Shift+Down/Up navigation instead of footer menu + if (isInProcessEnabled()) return []; + if (!teamContext) { + return []; + } + const teammateCount = count(Object.values(teamContext.teammates), t => t.name !== 'team-lead'); + return [{ + name: teamContext.teamName, + memberCount: teammateCount, + runningCount: 0, + idleCount: 0 + }]; + }, [teamContext]); + + // ─── Footer pill navigation ───────────────────────────────────────────── + // Which pills render below the input box. Order here IS the nav order + // (down/right = forward, up/left = back). Selection lives in AppState so + // pills rendered outside PromptInput (CompanionSprite) can read focus. + const runningTaskCount = useMemo(() => count(Object.values(tasks), t => t.status === 'running'), [tasks]); + // Panel shows retained-completed agents too (getVisibleAgentTasks), so the + // pill must stay navigable whenever the panel has rows — not just when + // something is running. + const tasksFooterVisible = (runningTaskCount > 0 || "external" === 'ant' && coordinatorTaskCount > 0) && !shouldHideTasksFooter(tasks, showSpinnerTree); + const teamsFooterVisible = cachedTeams.length > 0; + const footerItems = useMemo(() => [tasksFooterVisible && 'tasks', tmuxFooterVisible && 'tmux', bagelFooterVisible && 'bagel', teamsFooterVisible && 'teams', bridgeFooterVisible && 'bridge', companionFooterVisible && 'companion'].filter(Boolean) as FooterItem[], [tasksFooterVisible, tmuxFooterVisible, bagelFooterVisible, teamsFooterVisible, bridgeFooterVisible, companionFooterVisible]); + + // Effective selection: null if the selected pill stopped rendering (bridge + // disconnected, task finished). The derivation makes the UI correct + // immediately; the useEffect below clears the raw state so it doesn't + // resurrect when the same pill reappears (new task starts → focus stolen). + const rawFooterSelection = useAppState(s => s.footerSelection); + const footerItemSelected = rawFooterSelection && footerItems.includes(rawFooterSelection) ? rawFooterSelection : null; + useEffect(() => { + if (rawFooterSelection && !footerItemSelected) { + setAppState(prev => prev.footerSelection === null ? prev : { + ...prev, + footerSelection: null + }); + } + }, [rawFooterSelection, footerItemSelected, setAppState]); + const tasksSelected = footerItemSelected === 'tasks'; + const tmuxSelected = footerItemSelected === 'tmux'; + const bagelSelected = footerItemSelected === 'bagel'; + const teamsSelected = footerItemSelected === 'teams'; + const bridgeSelected = footerItemSelected === 'bridge'; + function selectFooterItem(item: FooterItem | null): void { + setAppState(prev => prev.footerSelection === item ? prev : { + ...prev, + footerSelection: item + }); + if (item === 'tasks') { + setTeammateFooterIndex(0); + setCoordinatorTaskIndex(minCoordinatorIndex); + } + } + + // delta: +1 = down/right, -1 = up/left. Returns true if nav happened + // (including deselecting at the start), false if at a boundary. + function navigateFooter(delta: 1 | -1, exitAtStart = false): boolean { + const idx = footerItemSelected ? footerItems.indexOf(footerItemSelected) : -1; + const next = footerItems[idx + delta]; + if (next) { + selectFooterItem(next); + return true; + } + if (delta < 0 && exitAtStart) { + selectFooterItem(null); + return true; + } + return false; + } + + // Prompt suggestion hook - reads suggestions generated by forked agent in query loop + const { + suggestion: promptSuggestion, + markAccepted, + logOutcomeAtSubmission, + markShown + } = usePromptSuggestion({ + inputValue: input, + isAssistantResponding: isLoading + }); + const displayedValue = useMemo(() => isSearchingHistory && historyMatch ? getValueFromInput(typeof historyMatch === 'string' ? historyMatch : historyMatch.display) : input, [isSearchingHistory, historyMatch, input]); + const thinkTriggers = useMemo(() => findThinkingTriggerPositions(displayedValue), [displayedValue]); + const ultraplanSessionUrl = useAppState(s => s.ultraplanSessionUrl); + const ultraplanLaunching = useAppState(s => s.ultraplanLaunching); + const ultraplanTriggers = useMemo(() => feature('ULTRAPLAN') && !ultraplanSessionUrl && !ultraplanLaunching ? findUltraplanTriggerPositions(displayedValue) : [], [displayedValue, ultraplanSessionUrl, ultraplanLaunching]); + const ultrareviewTriggers = useMemo(() => isUltrareviewEnabled() ? findUltrareviewTriggerPositions(displayedValue) : [], [displayedValue]); + const btwTriggers = useMemo(() => findBtwTriggerPositions(displayedValue), [displayedValue]); + const buddyTriggers = useMemo(() => findBuddyTriggerPositions(displayedValue), [displayedValue]); + const slashCommandTriggers = useMemo(() => { + const positions = findSlashCommandPositions(displayedValue); + // Only highlight valid commands + return positions.filter(pos => { + const commandName = displayedValue.slice(pos.start + 1, pos.end); // +1 to skip "/" + return hasCommand(commandName, commands); + }); + }, [displayedValue, commands]); + const tokenBudgetTriggers = useMemo(() => feature('TOKEN_BUDGET') ? findTokenBudgetPositions(displayedValue) : [], [displayedValue]); + const knownChannelsVersion = useSyncExternalStore(subscribeKnownChannels, getKnownChannelsVersion); + const slackChannelTriggers = useMemo(() => hasSlackMcpServer(store.getState().mcp.clients) ? findSlackChannelPositions(displayedValue) : [], + // eslint-disable-next-line react-hooks/exhaustive-deps -- store is a stable ref + [displayedValue, knownChannelsVersion]); + + // Find @name mentions and highlight with team member's color + const memberMentionHighlights = useMemo((): Array<{ + start: number; + end: number; + themeColor: keyof Theme; + }> => { + if (!isAgentSwarmsEnabled()) return []; + if (!teamContext?.teammates) return []; + const highlights: Array<{ + start: number; + end: number; + themeColor: keyof Theme; + }> = []; + const members = teamContext.teammates; + if (!members) return highlights; + + // Find all @name patterns in the input + const regex = /(^|\s)@([\w-]+)/g; + const memberValues = Object.values(members); + let match; + while ((match = regex.exec(displayedValue)) !== null) { + const leadingSpace = match[1] ?? ''; + const nameStart = match.index + leadingSpace.length; + const fullMatch = match[0].trimStart(); + const name = match[2]; + + // Check if this name matches a team member + const member = memberValues.find(t => t.name === name); + if (member?.color) { + const themeColor = AGENT_COLOR_TO_THEME_COLOR[member.color as AgentColorName]; + if (themeColor) { + highlights.push({ + start: nameStart, + end: nameStart + fullMatch.length, + themeColor + }); + } + } + } + return highlights; + }, [displayedValue, teamContext]); + const imageRefPositions = useMemo(() => parseReferences(displayedValue).filter(r => r.match.startsWith('[Image')).map(r => ({ + start: r.index, + end: r.index + r.match.length + })), [displayedValue]); + + // chip.start is the "selected" state: the inverted chip IS the cursor. + // chip.end stays a normal position so you can park the cursor right after + // `]` like any other character. + const cursorAtImageChip = imageRefPositions.some(r => r.start === cursorOffset); + + // up/down movement or a fullscreen click can land the cursor strictly + // inside a chip; snap to the nearer boundary so it's never editable + // char-by-char. + useEffect(() => { + const inside = imageRefPositions.find(r => cursorOffset > r.start && cursorOffset < r.end); + if (inside) { + const mid = (inside.start + inside.end) / 2; + setCursorOffset(cursorOffset < mid ? inside.start : inside.end); + } + }, [cursorOffset, imageRefPositions, setCursorOffset]); + const combinedHighlights = useMemo((): TextHighlight[] => { + const highlights: TextHighlight[] = []; + + // Invert the [Image #N] chip when the cursor is at chip.start (the + // "selected" state) so backspace-to-delete is visually obvious. + for (const ref of imageRefPositions) { + if (cursorOffset === ref.start) { + highlights.push({ + start: ref.start, + end: ref.end, + color: undefined, + inverse: true, + priority: 8 + }); + } + } + if (isSearchingHistory && historyMatch && !historyFailedMatch) { + highlights.push({ + start: cursorOffset, + end: cursorOffset + historyQuery.length, + color: 'warning', + priority: 20 + }); + } + + // Add "btw" highlighting (solid yellow) + for (const trigger of btwTriggers) { + highlights.push({ + start: trigger.start, + end: trigger.end, + color: 'warning', + priority: 15 + }); + } + + // Add /command highlighting (blue) + for (const trigger of slashCommandTriggers) { + highlights.push({ + start: trigger.start, + end: trigger.end, + color: 'suggestion', + priority: 5 + }); + } + + // Add token budget highlighting (blue) + for (const trigger of tokenBudgetTriggers) { + highlights.push({ + start: trigger.start, + end: trigger.end, + color: 'suggestion', + priority: 5 + }); + } + for (const trigger of slackChannelTriggers) { + highlights.push({ + start: trigger.start, + end: trigger.end, + color: 'suggestion', + priority: 5 + }); + } + + // Add @name highlighting with team member's color + for (const mention of memberMentionHighlights) { + highlights.push({ + start: mention.start, + end: mention.end, + color: mention.themeColor, + priority: 5 + }); + } + + // Dim interim voice dictation text + if (voiceInterimRange) { + highlights.push({ + start: voiceInterimRange.start, + end: voiceInterimRange.end, + color: undefined, + dimColor: true, + priority: 1 + }); + } + + // Rainbow highlighting for ultrathink keyword (per-character cycling colors) + if (isUltrathinkEnabled()) { + for (const trigger of thinkTriggers) { + for (let i = trigger.start; i < trigger.end; i++) { + highlights.push({ + start: i, + end: i + 1, + color: getRainbowColor(i - trigger.start), + shimmerColor: getRainbowColor(i - trigger.start, true), + priority: 10 + }); + } + } + } + + // Same rainbow treatment for the ultraplan keyword + if (feature('ULTRAPLAN')) { + for (const trigger of ultraplanTriggers) { + for (let i = trigger.start; i < trigger.end; i++) { + highlights.push({ + start: i, + end: i + 1, + color: getRainbowColor(i - trigger.start), + shimmerColor: getRainbowColor(i - trigger.start, true), + priority: 10 + }); + } + } + } + + // Same rainbow treatment for the ultrareview keyword + for (const trigger of ultrareviewTriggers) { + for (let i = trigger.start; i < trigger.end; i++) { + highlights.push({ + start: i, + end: i + 1, + color: getRainbowColor(i - trigger.start), + shimmerColor: getRainbowColor(i - trigger.start, true), + priority: 10 + }); + } + } + + // Rainbow for /buddy + for (const trigger of buddyTriggers) { + for (let i = trigger.start; i < trigger.end; i++) { + highlights.push({ + start: i, + end: i + 1, + color: getRainbowColor(i - trigger.start), + shimmerColor: getRainbowColor(i - trigger.start, true), + priority: 10 + }); + } + } + return highlights; + }, [isSearchingHistory, historyQuery, historyMatch, historyFailedMatch, cursorOffset, btwTriggers, imageRefPositions, memberMentionHighlights, slashCommandTriggers, tokenBudgetTriggers, slackChannelTriggers, displayedValue, voiceInterimRange, thinkTriggers, ultraplanTriggers, ultrareviewTriggers, buddyTriggers]); + const { + addNotification, + removeNotification + } = useNotifications(); + + // Show ultrathink notification + useEffect(() => { + if (thinkTriggers.length && isUltrathinkEnabled()) { + addNotification({ + key: 'ultrathink-active', + text: 'Effort set to high for this turn', + priority: 'immediate', + timeoutMs: 5000 + }); + } else { + removeNotification('ultrathink-active'); + } + }, [addNotification, removeNotification, thinkTriggers.length]); + useEffect(() => { + if (feature('ULTRAPLAN') && ultraplanTriggers.length) { + addNotification({ + key: 'ultraplan-active', + text: 'This prompt will launch an ultraplan session in Claude Code on the web', + priority: 'immediate', + timeoutMs: 5000 + }); + } else { + removeNotification('ultraplan-active'); + } + }, [addNotification, removeNotification, ultraplanTriggers.length]); + useEffect(() => { + if (isUltrareviewEnabled() && ultrareviewTriggers.length) { + addNotification({ + key: 'ultrareview-active', + text: 'Run /ultrareview after Claude finishes to review these changes in the cloud', + priority: 'immediate', + timeoutMs: 5000 + }); + } + }, [addNotification, ultrareviewTriggers.length]); + + // Track input length for stash hint + const prevInputLengthRef = useRef(input.length); + const peakInputLengthRef = useRef(input.length); + + // Dismiss stash hint when user makes any input change + const dismissStashHint = useCallback(() => { + removeNotification('stash-hint'); + }, [removeNotification]); + + // Show stash hint when user gradually clears substantial input + useEffect(() => { + const prevLength = prevInputLengthRef.current; + const peakLength = peakInputLengthRef.current; + const currentLength = input.length; + prevInputLengthRef.current = currentLength; + + // Update peak when input grows + if (currentLength > peakLength) { + peakInputLengthRef.current = currentLength; + return; + } + + // Reset state when input is empty + if (currentLength === 0) { + peakInputLengthRef.current = 0; + return; + } + + // Detect gradual clear: peak was high, current is low, but this wasn't a single big jump + // (rapid clears like esc-esc go from 20+ to 0 in one step) + const clearedSubstantialInput = peakLength >= 20 && currentLength <= 5; + const wasRapidClear = prevLength >= 20 && currentLength <= 5; + if (clearedSubstantialInput && !wasRapidClear) { + const config = getGlobalConfig(); + if (!config.hasUsedStash) { + addNotification({ + key: 'stash-hint', + jsx: + Tip:{' '} + + , + priority: 'immediate', + timeoutMs: FOOTER_TEMPORARY_STATUS_TIMEOUT + }); + } + peakInputLengthRef.current = currentLength; + } + }, [input.length, addNotification]); + + // Initialize input buffer for undo functionality + const { + pushToBuffer, + undo, + canUndo, + clearBuffer + } = useInputBuffer({ + maxBufferSize: 50, + debounceMs: 1000 + }); + useMaybeTruncateInput({ + input, + pastedContents, + onInputChange: trackAndSetInput, + setCursorOffset, + setPastedContents + }); + const defaultPlaceholder = usePromptInputPlaceholder({ + input, + submitCount, + viewingAgentName + }); + const onChange = useCallback((value: string) => { + if (value === '?') { + logEvent('tengu_help_toggled', {}); + setHelpOpen(v => !v); + return; + } + setHelpOpen(false); + + // Dismiss stash hint when user makes any input change + dismissStashHint(); + + // Cancel any pending prompt suggestion and speculation when user types + abortPromptSuggestion(); + abortSpeculation(setAppState); + + // Check if this is a single character insertion at the start + const isSingleCharInsertion = value.length === input.length + 1; + const insertedAtStart = cursorOffset === 0; + const mode = getModeFromInput(value); + if (insertedAtStart && mode !== 'prompt') { + if (isSingleCharInsertion) { + onModeChange(mode); + return; + } + // Multi-char insertion into empty input (e.g. tab-accepting "! gcloud auth login") + if (input.length === 0) { + onModeChange(mode); + const valueWithoutMode = getValueFromInput(value).replaceAll('\t', ' '); + pushToBuffer(input, cursorOffset, pastedContents); + trackAndSetInput(valueWithoutMode); + setCursorOffset(valueWithoutMode.length); + return; + } + } + const processedValue = value.replaceAll('\t', ' '); + + // Push current state to buffer before making changes + if (input !== processedValue) { + pushToBuffer(input, cursorOffset, pastedContents); + } + + // Deselect footer items when user types + setAppState(prev => prev.footerSelection === null ? prev : { + ...prev, + footerSelection: null + }); + trackAndSetInput(processedValue); + }, [trackAndSetInput, onModeChange, input, cursorOffset, pushToBuffer, pastedContents, dismissStashHint, setAppState]); + const { + resetHistory, + onHistoryUp, + onHistoryDown, + dismissSearchHint, + historyIndex + } = useArrowKeyHistory((value: string, historyMode: HistoryMode, pastedContents: Record) => { + onChange(value); + onModeChange(historyMode); + setPastedContents(pastedContents); + }, input, pastedContents, setCursorOffset, mode); + + // Dismiss search hint when user starts searching + useEffect(() => { + if (isSearchingHistory) { + dismissSearchHint(); + } + }, [isSearchingHistory, dismissSearchHint]); + + // Only use history navigation when there are 0 or 1 slash command suggestions. + // Footer nav is NOT here — when a pill is selected, TextInput focus=false so + // these never fire. The Footer keybinding context handles ↑/↓ instead. + function handleHistoryUp() { + if (suggestions.length > 1) { + return; + } + + // Only navigate history when cursor is on the first line. + // In multiline inputs, up arrow should move the cursor (handled by TextInput) + // and only trigger history when at the top of the input. + if (!isCursorOnFirstLine) { + return; + } + + // If there's an editable queued command, move it to the input for editing when UP is pressed + const hasEditableCommand = queuedCommands.some(isQueuedCommandEditable); + if (hasEditableCommand) { + void popAllCommandsFromQueue(); + return; + } + onHistoryUp(); + } + function handleHistoryDown() { + if (suggestions.length > 1) { + return; + } + + // Only navigate history/footer when cursor is on the last line. + // In multiline inputs, down arrow should move the cursor (handled by TextInput) + // and only trigger navigation when at the bottom of the input. + if (!isCursorOnLastLine) { + return; + } + + // At bottom of history → enter footer at first visible pill + if (onHistoryDown() && footerItems.length > 0) { + const first = footerItems[0]!; + selectFooterItem(first); + if (first === 'tasks' && !getGlobalConfig().hasSeenTasksHint) { + saveGlobalConfig(c => c.hasSeenTasksHint ? c : { + ...c, + hasSeenTasksHint: true + }); + } + } + } + + // Create a suggestions state directly - we'll sync it with useTypeahead later + const [suggestionsState, setSuggestionsStateRaw] = useState<{ + suggestions: SuggestionItem[]; + selectedSuggestion: number; + commandArgumentHint?: string; + }>({ + suggestions: [], + selectedSuggestion: -1, + commandArgumentHint: undefined + }); + + // Setter for suggestions state + const setSuggestionsState = useCallback((updater: typeof suggestionsState | ((prev: typeof suggestionsState) => typeof suggestionsState)) => { + setSuggestionsStateRaw(prev => typeof updater === 'function' ? updater(prev) : updater); + }, []); + const onSubmit = useCallback(async (inputParam: string, isSubmittingSlashCommand = false) => { + inputParam = inputParam.trimEnd(); + + // Don't submit if a footer indicator is being opened. Read fresh from + // store — footer:openSelected calls selectFooterItem(null) then onSubmit + // in the same tick, and the closure value hasn't updated yet. Apply the + // same "still visible?" derivation as footerItemSelected so a stale + // selection (pill disappeared) doesn't swallow Enter. + const state = store.getState(); + if (state.footerSelection && footerItems.includes(state.footerSelection)) { + return; + } + + // Enter in selection modes confirms selection (useBackgroundTaskNavigation). + // BaseTextInput's useInput registers before that hook (child effects fire first), + // so without this guard Enter would double-fire and auto-submit the suggestion. + if (state.viewSelectionMode === 'selecting-agent') { + return; + } + + // Check for images early - we need this for suggestion logic below + const hasImages = Object.values(pastedContents).some(c => c.type === 'image'); + + // If input is empty OR matches the suggestion, submit it + // But if there are images attached, don't auto-accept the suggestion - + // the user wants to submit just the image(s). + // Only in leader view — promptSuggestion is leader-context, not teammate. + const suggestionText = promptSuggestionState.text; + const inputMatchesSuggestion = inputParam.trim() === '' || inputParam === suggestionText; + if (inputMatchesSuggestion && suggestionText && !hasImages && !state.viewingAgentTaskId) { + // If speculation is active, inject messages immediately as they stream + if (speculation.status === 'active') { + markAccepted(); + // skipReset: resetSuggestion would abort the speculation before we accept it + logOutcomeAtSubmission(suggestionText, { + skipReset: true + }); + void onSubmitProp(suggestionText, { + setCursorOffset, + clearBuffer, + resetHistory + }, { + state: speculation, + speculationSessionTimeSavedMs: speculationSessionTimeSavedMs, + setAppState + }); + return; // Skip normal query - speculation handled it + } + + // Regular suggestion acceptance (requires shownAt > 0) + if (promptSuggestionState.shownAt > 0) { + markAccepted(); + inputParam = suggestionText; + } + } + + // Handle @name direct message + if (isAgentSwarmsEnabled()) { + const directMessage = parseDirectMemberMessage(inputParam); + if (directMessage) { + const result = await sendDirectMemberMessage(directMessage.recipientName, directMessage.message, teamContext, writeToMailbox); + if (result.success) { + addNotification({ + key: 'direct-message-sent', + text: `Sent to @${result.recipientName}`, + priority: 'immediate', + timeoutMs: 3000 + }); + trackAndSetInput(''); + setCursorOffset(0); + clearBuffer(); + resetHistory(); + return; + } else if (result.error === 'no_team_context') { + // No team context - fall through to normal prompt submission + } else { + // Unknown recipient - fall through to normal prompt submission + // This allows e.g. "@utils explain this code" to be sent as a prompt + } + } + } + + // Allow submission if there are images attached, even without text + if (inputParam.trim() === '' && !hasImages) { + return; + } + + // PromptInput UX: Check if suggestions dropdown is showing + // For directory suggestions, allow submission (Tab is used for completion) + const hasDirectorySuggestions = suggestionsState.suggestions.length > 0 && suggestionsState.suggestions.every(s => s.description === 'directory'); + if (suggestionsState.suggestions.length > 0 && !isSubmittingSlashCommand && !hasDirectorySuggestions) { + logForDebugging(`[onSubmit] early return: suggestions showing (count=${suggestionsState.suggestions.length})`); + return; // Don't submit, user needs to clear suggestions first + } + + // Log suggestion outcome if one exists + if (promptSuggestionState.text && promptSuggestionState.shownAt > 0) { + logOutcomeAtSubmission(inputParam); + } + + // Clear stash hint notification on submit + removeNotification('stash-hint'); + + // Route input to viewed agent (in-process teammate or named local_agent). + const activeAgent = getActiveAgentForInput(store.getState()); + if (activeAgent.type !== 'leader' && onAgentSubmit) { + logEvent('tengu_transcript_input_to_teammate', {}); + await onAgentSubmit(inputParam, activeAgent.task, { + setCursorOffset, + clearBuffer, + resetHistory + }); + return; + } + + // Normal leader submission + await onSubmitProp(inputParam, { + setCursorOffset, + clearBuffer, + resetHistory + }); + }, [promptSuggestionState, speculation, speculationSessionTimeSavedMs, teamContext, store, footerItems, suggestionsState.suggestions, onSubmitProp, onAgentSubmit, clearBuffer, resetHistory, logOutcomeAtSubmission, setAppState, markAccepted, pastedContents, removeNotification]); + const { + suggestions, + selectedSuggestion, + commandArgumentHint, + inlineGhostText, + maxColumnWidth + } = useTypeahead({ + commands, + onInputChange: trackAndSetInput, + onSubmit, + setCursorOffset, + input, + cursorOffset, + mode, + agents, + setSuggestionsState, + suggestionsState, + suppressSuggestions: isSearchingHistory || historyIndex > 0, + markAccepted, + onModeChange + }); + + // Track if prompt suggestion should be shown (computed later with terminal width). + // Hidden in teammate view — suggestion is leader-context only. + const showPromptSuggestion = mode === 'prompt' && suggestions.length === 0 && promptSuggestion && !viewingAgentTaskId; + if (showPromptSuggestion) { + markShown(); + } + + // If suggestion was generated but can't be shown due to timing, log suppression. + // Exclude teammate view: markShown() is gated above, so shownAt stays 0 there — + // but that's not a timing failure, the suggestion is valid when returning to leader. + if (promptSuggestionState.text && !promptSuggestion && promptSuggestionState.shownAt === 0 && !viewingAgentTaskId) { + logSuggestionSuppressed('timing', promptSuggestionState.text); + setAppState(prev => ({ + ...prev, + promptSuggestion: { + text: null, + promptId: null, + shownAt: 0, + acceptedAt: 0, + generationRequestId: null + } + })); + } + function onImagePaste(image: string, mediaType?: string, filename?: string, dimensions?: ImageDimensions, sourcePath?: string) { + logEvent('tengu_paste_image', {}); + onModeChange('prompt'); + const pasteId = nextPasteIdRef.current++; + const newContent: PastedContent = { + id: pasteId, + type: 'image', + content: image, + mediaType: mediaType || 'image/png', + // default to PNG if not provided + filename: filename || 'Pasted image', + dimensions, + sourcePath + }; + + // Cache path immediately (fast) so links work on render + cacheImagePath(newContent); + + // Store image to disk in background + void storeImage(newContent); + + // Update UI + setPastedContents(prev => ({ + ...prev, + [pasteId]: newContent + })); + // Multi-image paste calls onImagePaste in a loop. If the ref is already + // armed, the previous pill's lazy space fires now (before this pill) + // rather than being lost. + const prefix = pendingSpaceAfterPillRef.current ? ' ' : ''; + insertTextAtCursor(prefix + formatImageRef(pasteId)); + pendingSpaceAfterPillRef.current = true; + } + + // Prune images whose [Image #N] placeholder is no longer in the input text. + // Covers pill backspace, Ctrl+U, char-by-char deletion — any edit that drops + // the ref. onImagePaste batches setPastedContents + insertTextAtCursor in the + // same event, so this effect sees the placeholder already present. + useEffect(() => { + const referencedIds = new Set(parseReferences(input).map(r => r.id)); + setPastedContents(prev => { + const orphaned = Object.values(prev).filter(c => c.type === 'image' && !referencedIds.has(c.id)); + if (orphaned.length === 0) return prev; + const next = { + ...prev + }; + for (const img of orphaned) delete next[img.id]; + return next; + }); + }, [input, setPastedContents]); + function onTextPaste(rawText: string) { + pendingSpaceAfterPillRef.current = false; + // Clean up pasted text - strip ANSI escape codes and normalize line endings and tabs + let text = stripAnsi(rawText).replace(/\r/g, '\n').replaceAll('\t', ' '); + + // Match typed/auto-suggest: `!cmd` pasted into empty input enters bash mode. + if (input.length === 0) { + const pastedMode = getModeFromInput(text); + if (pastedMode !== 'prompt') { + onModeChange(pastedMode); + text = getValueFromInput(text); + } + } + const numLines = getPastedTextRefNumLines(text); + // Limit the number of lines to show in the input + // If the overall layout is too high then Ink will repaint + // the entire terminal. + // The actual required height is dependent on the content, this + // is just an estimate. + const maxLines = Math.min(rows - 10, 2); + + // Use special handling for long pasted text (>PASTE_THRESHOLD chars) + // or if it exceeds the number of lines we want to show + if (text.length > PASTE_THRESHOLD || numLines > maxLines) { + const pasteId = nextPasteIdRef.current++; + const newContent: PastedContent = { + id: pasteId, + type: 'text', + content: text + }; + setPastedContents(prev => ({ + ...prev, + [pasteId]: newContent + })); + insertTextAtCursor(formatPastedTextRef(pasteId, numLines)); + } else { + // For shorter pastes, just insert the text normally + insertTextAtCursor(text); + } + } + const lazySpaceInputFilter = useCallback((input: string, key: Key): string => { + if (!pendingSpaceAfterPillRef.current) return input; + pendingSpaceAfterPillRef.current = false; + if (isNonSpacePrintable(input, key)) return ' ' + input; + return input; + }, []); + function insertTextAtCursor(text: string) { + // Push current state to buffer before inserting + pushToBuffer(input, cursorOffset, pastedContents); + const newInput = input.slice(0, cursorOffset) + text + input.slice(cursorOffset); + trackAndSetInput(newInput); + setCursorOffset(cursorOffset + text.length); + } + const doublePressEscFromEmpty = useDoublePress(() => {}, () => onShowMessageSelector()); + + // Function to get the queued command for editing. Returns true if commands were popped. + const popAllCommandsFromQueue = useCallback((): boolean => { + const result = popAllEditable(input, cursorOffset); + if (!result) { + return false; + } + trackAndSetInput(result.text); + onModeChange('prompt'); // Always prompt mode for queued commands + setCursorOffset(result.cursorOffset); + + // Restore images from queued commands to pastedContents + if (result.images.length > 0) { + setPastedContents(prev => { + const newContents = { + ...prev + }; + for (const image of result.images) { + newContents[image.id] = image; + } + return newContents; + }); + } + return true; + }, [trackAndSetInput, onModeChange, input, cursorOffset, setPastedContents]); + + // Insert the at-mentioned reference (the file and, optionally, a line range) when + // we receive an at-mentioned notification the IDE. + const onIdeAtMentioned = function (atMentioned: IDEAtMentioned) { + logEvent('tengu_ext_at_mentioned', {}); + let atMentionedText: string; + const relativePath = path.relative(getCwd(), atMentioned.filePath); + if (atMentioned.lineStart && atMentioned.lineEnd) { + atMentionedText = atMentioned.lineStart === atMentioned.lineEnd ? `@${relativePath}#L${atMentioned.lineStart} ` : `@${relativePath}#L${atMentioned.lineStart}-${atMentioned.lineEnd} `; + } else { + atMentionedText = `@${relativePath} `; + } + const cursorChar = input[cursorOffset - 1] ?? ' '; + if (!/\s/.test(cursorChar)) { + atMentionedText = ` ${atMentionedText}`; + } + insertTextAtCursor(atMentionedText); + }; + useIdeAtMentioned(mcpClients, onIdeAtMentioned); + + // Handler for chat:undo - undo last edit + const handleUndo = useCallback(() => { + if (canUndo) { + const previousState = undo(); + if (previousState) { + trackAndSetInput(previousState.text); + setCursorOffset(previousState.cursorOffset); + setPastedContents(previousState.pastedContents); + } + } + }, [canUndo, undo, trackAndSetInput, setPastedContents]); + + // Handler for chat:newline - insert a newline at the cursor position + const handleNewline = useCallback(() => { + pushToBuffer(input, cursorOffset, pastedContents); + const newInput = input.slice(0, cursorOffset) + '\n' + input.slice(cursorOffset); + trackAndSetInput(newInput); + setCursorOffset(cursorOffset + 1); + }, [input, cursorOffset, trackAndSetInput, setCursorOffset, pushToBuffer, pastedContents]); + + // Handler for chat:externalEditor - edit in $EDITOR + const handleExternalEditor = useCallback(async () => { + logEvent('tengu_external_editor_used', {}); + setIsExternalEditorActive(true); + try { + // Pass pastedContents to expand collapsed text references + const result = await editPromptInEditor(input, pastedContents); + if (result.error) { + addNotification({ + key: 'external-editor-error', + text: result.error, + color: 'warning', + priority: 'high' + }); + } + if (result.content !== null && result.content !== input) { + // Push current state to buffer before making changes + pushToBuffer(input, cursorOffset, pastedContents); + trackAndSetInput(result.content); + setCursorOffset(result.content.length); + } + } catch (err) { + if (err instanceof Error) { + logError(err); + } + addNotification({ + key: 'external-editor-error', + text: `External editor failed: ${errorMessage(err)}`, + color: 'warning', + priority: 'high' + }); + } finally { + setIsExternalEditorActive(false); + } + }, [input, cursorOffset, pastedContents, pushToBuffer, trackAndSetInput, addNotification]); + + // Handler for chat:stash - stash/unstash prompt + const handleStash = useCallback(() => { + if (input.trim() === '' && stashedPrompt !== undefined) { + // Pop stash when input is empty + trackAndSetInput(stashedPrompt.text); + setCursorOffset(stashedPrompt.cursorOffset); + setPastedContents(stashedPrompt.pastedContents); + setStashedPrompt(undefined); + } else if (input.trim() !== '') { + // Push to stash (save text, cursor position, and pasted contents) + setStashedPrompt({ + text: input, + cursorOffset, + pastedContents + }); + trackAndSetInput(''); + setCursorOffset(0); + setPastedContents({}); + // Track usage for /discover and stop showing hint + saveGlobalConfig(c => { + if (c.hasUsedStash) return c; + return { + ...c, + hasUsedStash: true + }; + }); + } + }, [input, cursorOffset, stashedPrompt, trackAndSetInput, setStashedPrompt, pastedContents, setPastedContents]); + + // Handler for chat:modelPicker - toggle model picker + const handleModelPicker = useCallback(() => { + setShowModelPicker(prev => !prev); + if (helpOpen) { + setHelpOpen(false); + } + }, [helpOpen]); + + // Handler for chat:fastMode - toggle fast mode picker + const handleFastModePicker = useCallback(() => { + setShowFastModePicker(prev => !prev); + if (helpOpen) { + setHelpOpen(false); + } + }, [helpOpen]); + + // Handler for chat:thinkingToggle - toggle thinking mode + const handleThinkingToggle = useCallback(() => { + setShowThinkingToggle(prev => !prev); + if (helpOpen) { + setHelpOpen(false); + } + }, [helpOpen]); + + // Handler for chat:cycleMode - cycle through permission modes + const handleCycleMode = useCallback(() => { + // When viewing a teammate, cycle their mode instead of the leader's + if (isAgentSwarmsEnabled() && viewedTeammate && viewingAgentTaskId) { + const teammateContext: ToolPermissionContext = { + ...toolPermissionContext, + mode: viewedTeammate.permissionMode + }; + // Pass undefined for teamContext (unused but kept for API compatibility) + const nextMode = getNextPermissionMode(teammateContext, undefined); + logEvent('tengu_mode_cycle', { + to: nextMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + const teammateTaskId = viewingAgentTaskId; + setAppState(prev => { + const task = prev.tasks[teammateTaskId]; + if (!task || task.type !== 'in_process_teammate') { + return prev; + } + if (task.permissionMode === nextMode) { + return prev; + } + return { + ...prev, + tasks: { + ...prev.tasks, + [teammateTaskId]: { + ...task, + permissionMode: nextMode + } + } + }; + }); + if (helpOpen) { + setHelpOpen(false); + } + return; + } + + // Compute the next mode without triggering side effects first + logForDebugging(`[auto-mode] handleCycleMode: currentMode=${toolPermissionContext.mode} isAutoModeAvailable=${toolPermissionContext.isAutoModeAvailable} showAutoModeOptIn=${showAutoModeOptIn} timeoutPending=${!!autoModeOptInTimeoutRef.current}`); + const nextMode = getNextPermissionMode(toolPermissionContext, teamContext); + + // Check if user is entering auto mode for the first time. Gated on the + // persistent settings flag (hasAutoModeOptIn) rather than the broader + // hasAutoModeOptInAnySource so that --enable-auto-mode users still see + // the warning dialog once — the CLI flag should grant carousel access, + // not bypass the safety text. + let isEnteringAutoModeFirstTime = false; + if (feature('TRANSCRIPT_CLASSIFIER')) { + isEnteringAutoModeFirstTime = nextMode === 'auto' && toolPermissionContext.mode !== 'auto' && !hasAutoModeOptIn() && !viewingAgentTaskId; // Only show for primary agent, not subagents + } + if (feature('TRANSCRIPT_CLASSIFIER')) { + if (isEnteringAutoModeFirstTime) { + // Store previous mode so we can revert if user declines + setPreviousModeBeforeAuto(toolPermissionContext.mode); + + // Only update the UI mode label — do NOT call transitionPermissionMode + // or cyclePermissionMode yet; we haven't confirmed with the user. + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...prev.toolPermissionContext, + mode: 'auto' + } + })); + setToolPermissionContext({ + ...toolPermissionContext, + mode: 'auto' + }); + + // Show opt-in dialog after 400ms debounce + if (autoModeOptInTimeoutRef.current) { + clearTimeout(autoModeOptInTimeoutRef.current); + } + autoModeOptInTimeoutRef.current = setTimeout((setShowAutoModeOptIn, autoModeOptInTimeoutRef) => { + setShowAutoModeOptIn(true); + autoModeOptInTimeoutRef.current = null; + }, 400, setShowAutoModeOptIn, autoModeOptInTimeoutRef); + if (helpOpen) { + setHelpOpen(false); + } + return; + } + } + + // Dismiss auto mode opt-in dialog if showing or pending (user is cycling away). + // Do NOT revert to previousModeBeforeAuto here — shift+tab means "advance the + // carousel", not "decline". Reverting causes a ping-pong loop: auto reverts to + // the prior mode, whose next mode is auto again, forever. + // The dialog's own decline button (handleAutoModeOptInDecline) handles revert. + if (feature('TRANSCRIPT_CLASSIFIER')) { + if (showAutoModeOptIn || autoModeOptInTimeoutRef.current) { + if (showAutoModeOptIn) { + logEvent('tengu_auto_mode_opt_in_dialog_decline', {}); + } + setShowAutoModeOptIn(false); + if (autoModeOptInTimeoutRef.current) { + clearTimeout(autoModeOptInTimeoutRef.current); + autoModeOptInTimeoutRef.current = null; + } + setPreviousModeBeforeAuto(null); + // Fall through — mode is 'auto', cyclePermissionMode below goes to 'default'. + } + } + + // Now that we know this is NOT the first-time auto mode path, + // call cyclePermissionMode to apply side effects (e.g. strip + // dangerous permissions, activate classifier) + const { + context: preparedContext + } = cyclePermissionMode(toolPermissionContext, teamContext); + logEvent('tengu_mode_cycle', { + to: nextMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + + // Track when user enters plan mode + if (nextMode === 'plan') { + saveGlobalConfig(current => ({ + ...current, + lastPlanModeUse: Date.now() + })); + } + + // Set the mode via setAppState directly because setToolPermissionContext + // intentionally preserves the existing mode (to prevent coordinator mode + // corruption from workers). Then call setToolPermissionContext to trigger + // recheck of queued permission prompts. + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...preparedContext, + mode: nextMode + } + })); + setToolPermissionContext({ + ...preparedContext, + mode: nextMode + }); + + // If this is a teammate, update config.json so team lead sees the change + syncTeammateMode(nextMode, teamContext?.teamName); + + // Close help tips if they're open when mode is cycled + if (helpOpen) { + setHelpOpen(false); + } + }, [toolPermissionContext, teamContext, viewingAgentTaskId, viewedTeammate, setAppState, setToolPermissionContext, helpOpen, showAutoModeOptIn]); + + // Handler for auto mode opt-in dialog acceptance + const handleAutoModeOptInAccept = useCallback(() => { + if (feature('TRANSCRIPT_CLASSIFIER')) { + setShowAutoModeOptIn(false); + setPreviousModeBeforeAuto(null); + + // Now that the user accepted, apply the full transition: activate the + // auto mode backend (classifier, beta headers) and strip dangerous + // permissions (e.g. Bash(*) always-allow rules). + const strippedContext = transitionPermissionMode(previousModeBeforeAuto ?? toolPermissionContext.mode, 'auto', toolPermissionContext); + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...strippedContext, + mode: 'auto' + } + })); + setToolPermissionContext({ + ...strippedContext, + mode: 'auto' + }); + + // Close help tips if they're open when auto mode is enabled + if (helpOpen) { + setHelpOpen(false); + } + } + }, [helpOpen, setHelpOpen, previousModeBeforeAuto, toolPermissionContext, setAppState, setToolPermissionContext]); + + // Handler for auto mode opt-in dialog decline + const handleAutoModeOptInDecline = useCallback(() => { + if (feature('TRANSCRIPT_CLASSIFIER')) { + logForDebugging(`[auto-mode] handleAutoModeOptInDecline: reverting to ${previousModeBeforeAuto}, setting isAutoModeAvailable=false`); + setShowAutoModeOptIn(false); + if (autoModeOptInTimeoutRef.current) { + clearTimeout(autoModeOptInTimeoutRef.current); + autoModeOptInTimeoutRef.current = null; + } + + // Revert to previous mode and remove auto from the carousel + // for the rest of this session + if (previousModeBeforeAuto) { + setAutoModeActive(false); + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...prev.toolPermissionContext, + mode: previousModeBeforeAuto, + isAutoModeAvailable: false + } + })); + setToolPermissionContext({ + ...toolPermissionContext, + mode: previousModeBeforeAuto, + isAutoModeAvailable: false + }); + setPreviousModeBeforeAuto(null); + } + } + }, [previousModeBeforeAuto, toolPermissionContext, setAppState, setToolPermissionContext]); + + // Handler for chat:imagePaste - paste image from clipboard + const handleImagePaste = useCallback(() => { + void getImageFromClipboard().then(imageData => { + if (imageData) { + onImagePaste(imageData.base64, imageData.mediaType); + } else { + const shortcutDisplay = getShortcutDisplay('chat:imagePaste', 'Chat', 'ctrl+v'); + const message = env.isSSH() ? "No image found in clipboard. You're SSH'd; try scp?" : `No image found in clipboard. Use ${shortcutDisplay} to paste images.`; + addNotification({ + key: 'no-image-in-clipboard', + text: message, + priority: 'immediate', + timeoutMs: 1000 + }); + } + }); + }, [addNotification, onImagePaste]); + + // Register chat:submit handler directly in the handler registry (not via + // useKeybindings) so that only the ChordInterceptor can invoke it for chord + // completions (e.g., "ctrl+e s"). The default Enter binding for submit is + // handled by TextInput directly (via onSubmit prop) and useTypeahead (for + // autocomplete acceptance). Using useKeybindings would cause + // stopImmediatePropagation on Enter, blocking autocomplete from seeing the key. + const keybindingContext = useOptionalKeybindingContext(); + useEffect(() => { + if (!keybindingContext || isModalOverlayActive) return; + return keybindingContext.registerHandler({ + action: 'chat:submit', + context: 'Chat', + handler: () => { + void onSubmit(input); + } + }); + }, [keybindingContext, isModalOverlayActive, onSubmit, input]); + + // Chat context keybindings for editing shortcuts + // Note: history:previous/history:next are NOT handled here. They are passed as + // onHistoryUp/onHistoryDown props to TextInput, so that useTextInput's + // upOrHistoryUp/downOrHistoryDown can try cursor movement first and only + // fall through to history when the cursor can't move further. + const chatHandlers = useMemo(() => ({ + 'chat:undo': handleUndo, + 'chat:newline': handleNewline, + 'chat:externalEditor': handleExternalEditor, + 'chat:stash': handleStash, + 'chat:modelPicker': handleModelPicker, + 'chat:thinkingToggle': handleThinkingToggle, + 'chat:cycleMode': handleCycleMode, + 'chat:imagePaste': handleImagePaste + }), [handleUndo, handleNewline, handleExternalEditor, handleStash, handleModelPicker, handleThinkingToggle, handleCycleMode, handleImagePaste]); + useKeybindings(chatHandlers, { + context: 'Chat', + isActive: !isModalOverlayActive + }); + + // Shift+↑ enters message-actions cursor. Separate isActive so ctrl+r search + // doesn't leave stale isSearchingHistory on cursor-exit remount. + useKeybinding('chat:messageActions', () => onMessageActionsEnter?.(), { + context: 'Chat', + isActive: !isModalOverlayActive && !isSearchingHistory + }); + + // Fast mode keybinding is only active when fast mode is enabled and available + useKeybinding('chat:fastMode', handleFastModePicker, { + context: 'Chat', + isActive: !isModalOverlayActive && isFastModeEnabled() && isFastModeAvailable() + }); + + // Handle help:dismiss keybinding (ESC closes help menu) + // This is registered separately from Chat context so it has priority over + // CancelRequestHandler when help menu is open + useKeybinding('help:dismiss', () => { + setHelpOpen(false); + }, { + context: 'Help', + isActive: helpOpen + }); + + // Quick Open / Global Search. Hook calls are unconditional (Rules of Hooks); + // the handler body is feature()-gated so the setState calls and component + // references get tree-shaken in external builds. + const quickSearchActive = feature('QUICK_SEARCH') ? !isModalOverlayActive : false; + useKeybinding('app:quickOpen', () => { + if (feature('QUICK_SEARCH')) { + setShowQuickOpen(true); + setHelpOpen(false); + } + }, { + context: 'Global', + isActive: quickSearchActive + }); + useKeybinding('app:globalSearch', () => { + if (feature('QUICK_SEARCH')) { + setShowGlobalSearch(true); + setHelpOpen(false); + } + }, { + context: 'Global', + isActive: quickSearchActive + }); + useKeybinding('history:search', () => { + if (feature('HISTORY_PICKER')) { + setShowHistoryPicker(true); + setHelpOpen(false); + } + }, { + context: 'Global', + isActive: feature('HISTORY_PICKER') ? !isModalOverlayActive : false + }); + + // Handle Ctrl+C to abort speculation when idle (not loading) + // CancelRequestHandler only handles Ctrl+C during active tasks + useKeybinding('app:interrupt', () => { + abortSpeculation(setAppState); + }, { + context: 'Global', + isActive: !isLoading && speculation.status === 'active' + }); + + // Footer indicator navigation keybindings. ↑/↓ live here (not in + // handleHistoryUp/Down) because TextInput focus=false when a pill is + // selected — its useInput is inactive, so this is the only path. + useKeybindings({ + 'footer:up': () => { + // ↑ scrolls within the coordinator task list before leaving the pill + if (tasksSelected && "external" === 'ant' && coordinatorTaskCount > 0 && coordinatorTaskIndex > minCoordinatorIndex) { + setCoordinatorTaskIndex(prev => prev - 1); + return; + } + navigateFooter(-1, true); + }, + 'footer:down': () => { + // ↓ scrolls within the coordinator task list, never leaves the pill + if (tasksSelected && "external" === 'ant' && coordinatorTaskCount > 0) { + if (coordinatorTaskIndex < coordinatorTaskCount - 1) { + setCoordinatorTaskIndex(prev => prev + 1); + } + return; + } + if (tasksSelected && !isTeammateMode) { + setShowBashesDialog(true); + selectFooterItem(null); + return; + } + navigateFooter(1); + }, + 'footer:next': () => { + // Teammate mode: ←/→ cycles within the team member list + if (tasksSelected && isTeammateMode) { + const totalAgents = 1 + inProcessTeammates.length; + setTeammateFooterIndex(prev => (prev + 1) % totalAgents); + return; + } + navigateFooter(1); + }, + 'footer:previous': () => { + if (tasksSelected && isTeammateMode) { + const totalAgents = 1 + inProcessTeammates.length; + setTeammateFooterIndex(prev => (prev - 1 + totalAgents) % totalAgents); + return; + } + navigateFooter(-1); + }, + 'footer:openSelected': () => { + if (viewSelectionMode === 'selecting-agent') { + return; + } + switch (footerItemSelected) { + case 'companion': + if (feature('BUDDY')) { + selectFooterItem(null); + void onSubmit('/buddy'); + } + break; + case 'tasks': + if (isTeammateMode) { + // Enter switches to the selected agent's view + if (teammateFooterIndex === 0) { + exitTeammateView(setAppState); + } else { + const teammate = inProcessTeammates[teammateFooterIndex - 1]; + if (teammate) enterTeammateView(teammate.id, setAppState); + } + } else if (coordinatorTaskIndex === 0 && coordinatorTaskCount > 0) { + exitTeammateView(setAppState); + } else { + const selectedTaskId = getVisibleAgentTasks(tasks)[coordinatorTaskIndex - 1]?.id; + if (selectedTaskId) { + enterTeammateView(selectedTaskId, setAppState); + } else { + setShowBashesDialog(true); + selectFooterItem(null); + } + } + break; + case 'tmux': + if ("external" === 'ant') { + setAppState(prev => prev.tungstenPanelAutoHidden ? { + ...prev, + tungstenPanelAutoHidden: false + } : { + ...prev, + tungstenPanelVisible: !(prev.tungstenPanelVisible ?? true) + }); + } + break; + case 'bagel': + break; + case 'teams': + setShowTeamsDialog(true); + selectFooterItem(null); + break; + case 'bridge': + setShowBridgeDialog(true); + selectFooterItem(null); + break; + } + }, + 'footer:clearSelection': () => { + selectFooterItem(null); + }, + 'footer:close': () => { + if (tasksSelected && coordinatorTaskIndex >= 1) { + const task = getVisibleAgentTasks(tasks)[coordinatorTaskIndex - 1]; + if (!task) return false; + // When the selected row IS the viewed agent, 'x' types into the + // steering input. Any other row — dismiss it. + if (viewSelectionMode === 'viewing-agent' && task.id === viewingAgentTaskId) { + onChange(input.slice(0, cursorOffset) + 'x' + input.slice(cursorOffset)); + setCursorOffset(cursorOffset + 1); + return; + } + stopOrDismissAgent(task.id, setAppState); + if (task.status !== 'running') { + setCoordinatorTaskIndex(i => Math.max(minCoordinatorIndex, i - 1)); + } + return; + } + // Not handled — let 'x' fall through to type-to-exit + return false; + } + }, { + context: 'Footer', + isActive: !!footerItemSelected && !isModalOverlayActive + }); + useInput((char, key) => { + // Skip all input handling when a full-screen dialog is open. These dialogs + // render via early return, but hooks run unconditionally — so without this + // guard, Escape inside a dialog leaks to the double-press message-selector. + if (showTeamsDialog || showQuickOpen || showGlobalSearch || showHistoryPicker) { + return; + } + + // Detect failed Alt shortcuts on macOS (Option key produces special characters) + if (getPlatform() === 'macos' && isMacosOptionChar(char)) { + const shortcut = MACOS_OPTION_SPECIAL_CHARS[char]; + const terminalName = getNativeCSIuTerminalDisplayName(); + const jsx = terminalName ? + To enable {shortcut}, set Option as Meta in{' '} + {terminalName} preferences (⌘,) + : To enable {shortcut}, run /terminal-setup; + addNotification({ + key: 'option-meta-hint', + jsx, + priority: 'immediate', + timeoutMs: 5000 + }); + // Don't return - let the character be typed so user sees the issue + } + + // Footer navigation is handled via useKeybindings above (Footer context) + + // NOTE: ctrl+_, ctrl+g, ctrl+s are handled via Chat context keybindings above + + // Type-to-exit footer: printable chars while a pill is selected refocus + // the input and type the char. Nav keys are captured by useKeybindings + // above, so anything reaching here is genuinely not a footer action. + // onChange clears footerSelection, so no explicit deselect. + if (footerItemSelected && char && !key.ctrl && !key.meta && !key.escape && !key.return) { + onChange(input.slice(0, cursorOffset) + char + input.slice(cursorOffset)); + setCursorOffset(cursorOffset + char.length); + return; + } + + // Exit special modes when backspace/escape/delete/ctrl+u is pressed at cursor position 0 + if (cursorOffset === 0 && (key.escape || key.backspace || key.delete || key.ctrl && char === 'u')) { + onModeChange('prompt'); + setHelpOpen(false); + } + + // Exit help mode when backspace is pressed and input is empty + if (helpOpen && input === '' && (key.backspace || key.delete)) { + setHelpOpen(false); + } + + // esc is a little overloaded: + // - when we're loading a response, it's used to cancel the request + // - otherwise, it's used to show the message selector + // - when double pressed, it's used to clear the input + // - when input is empty, pop from command queue + + // Handle ESC key press + if (key.escape) { + // Abort active speculation + if (speculation.status === 'active') { + abortSpeculation(setAppState); + return; + } + + // Dismiss side question response if visible + if (isSideQuestionVisible && onDismissSideQuestion) { + onDismissSideQuestion(); + return; + } + + // Close help menu if open + if (helpOpen) { + setHelpOpen(false); + return; + } + + // Footer selection clearing is now handled via Footer context keybindings + // (footer:clearSelection action bound to escape) + // If a footer item is selected, let the Footer keybinding handle it + if (footerItemSelected) { + return; + } + + // If there's an editable queued command, move it to the input for editing when ESC is pressed + const hasEditableCommand = queuedCommands.some(isQueuedCommandEditable); + if (hasEditableCommand) { + void popAllCommandsFromQueue(); + return; + } + if (messages.length > 0 && !input && !isLoading) { + doublePressEscFromEmpty(); + } + } + if (key.return && helpOpen) { + setHelpOpen(false); + } + }); + const swarmBanner = useSwarmBanner(); + const fastModeCooldown = isFastModeEnabled() ? isFastModeCooldown() : false; + const showFastIcon = isFastModeEnabled() ? isFastMode && (isFastModeAvailable() || fastModeCooldown) : false; + const showFastIconHint = useShowFastIconHint(showFastIcon ?? false); + + // Show effort notification on startup and when effort changes. + // Suppressed in brief/assistant mode — the value reflects the local + // client's effort, not the connected agent's. + const effortNotificationText = briefOwnsGap ? undefined : getEffortNotificationText(effortValue, mainLoopModel); + useEffect(() => { + if (!effortNotificationText) { + removeNotification('effort-level'); + return; + } + addNotification({ + key: 'effort-level', + text: effortNotificationText, + priority: 'high', + timeoutMs: 12_000 + }); + }, [effortNotificationText, addNotification, removeNotification]); + useBuddyNotification(); + const companionSpeaking = feature('BUDDY') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s => s.companionReaction !== undefined) : false; + const { + columns, + rows + } = useTerminalSize(); + const textInputColumns = columns - 3 - companionReservedColumns(columns, companionSpeaking); + + // POC: click-to-position-cursor. Mouse tracking is only enabled inside + // , so this is dormant in the normal main-screen REPL. + // localCol/localRow are relative to the onClick Box's top-left; the Box + // tightly wraps the text input so they map directly to (column, line) + // in the Cursor wrap model. MeasuredText.getOffsetFromPosition handles + // wide chars, wrapped lines, and clamps past-end clicks to line end. + const maxVisibleLines = isFullscreenEnvEnabled() ? Math.max(MIN_INPUT_VIEWPORT_LINES, Math.floor(rows / 2) - PROMPT_FOOTER_LINES) : undefined; + const handleInputClick = useCallback((e: ClickEvent) => { + // During history search the displayed text is historyMatch, not + // input, and showCursor is false anyway — skip rather than + // compute an offset against the wrong string. + if (!input || isSearchingHistory) return; + const c = Cursor.fromText(input, textInputColumns, cursorOffset); + const viewportStart = c.getViewportStartLine(maxVisibleLines); + const offset = c.measuredText.getOffsetFromPosition({ + line: e.localRow + viewportStart, + column: e.localCol + }); + setCursorOffset(offset); + }, [input, textInputColumns, isSearchingHistory, cursorOffset, maxVisibleLines]); + const handleOpenTasksDialog = useCallback((taskId?: string) => setShowBashesDialog(taskId ?? true), [setShowBashesDialog]); + const placeholder = showPromptSuggestion && promptSuggestion ? promptSuggestion : defaultPlaceholder; + + // Calculate if input has multiple lines + const isInputWrapped = useMemo(() => input.includes('\n'), [input]); + + // Memoized callbacks for model picker to prevent re-renders when unrelated + // state (like notifications) changes. This prevents the inline model picker + // from visually "jumping" when notifications arrive. + const handleModelSelect = useCallback((model: string | null, _effort: EffortLevel | undefined) => { + let wasFastModeDisabled = false; + setAppState(prev => { + wasFastModeDisabled = isFastModeEnabled() && !isFastModeSupportedByModel(model) && !!prev.fastMode; + return { + ...prev, + mainLoopModel: model, + mainLoopModelForSession: null, + // Turn off fast mode if switching to a model that doesn't support it + ...(wasFastModeDisabled && { + fastMode: false + }) + }; + }); + setShowModelPicker(false); + const effectiveFastMode = (isFastMode ?? false) && !wasFastModeDisabled; + let message = `Model set to ${modelDisplayString(model)}`; + if (isBilledAsExtraUsage(model, effectiveFastMode, isOpus1mMergeEnabled())) { + message += ' · Billed as extra usage'; + } + if (wasFastModeDisabled) { + message += ' · Fast mode OFF'; + } + addNotification({ + key: 'model-switched', + jsx: {message}, + priority: 'immediate', + timeoutMs: 3000 + }); + logEvent('tengu_model_picker_hotkey', { + model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + }, [setAppState, addNotification, isFastMode]); + const handleModelCancel = useCallback(() => { + setShowModelPicker(false); + }, []); + + // Memoize the model picker element to prevent unnecessary re-renders + // when AppState changes for unrelated reasons (e.g., notifications arriving) + const modelPickerElement = useMemo(() => { + if (!showModelPicker) return null; + return + + ; + }, [showModelPicker, mainLoopModel_, mainLoopModelForSession, handleModelSelect, handleModelCancel]); + const handleFastModeSelect = useCallback((result?: string) => { + setShowFastModePicker(false); + if (result) { + addNotification({ + key: 'fast-mode-toggled', + jsx: {result}, + priority: 'immediate', + timeoutMs: 3000 + }); + } + }, [addNotification]); + + // Memoize the fast mode picker element + const fastModePickerElement = useMemo(() => { + if (!showFastModePicker) return null; + return + + ; + }, [showFastModePicker, handleFastModeSelect]); + + // Memoized callbacks for thinking toggle + const handleThinkingSelect = useCallback((enabled: boolean) => { + setAppState(prev => ({ + ...prev, + thinkingEnabled: enabled + })); + setShowThinkingToggle(false); + logEvent('tengu_thinking_toggled_hotkey', { + enabled + }); + addNotification({ + key: 'thinking-toggled-hotkey', + jsx: + Thinking {enabled ? 'on' : 'off'} + , + priority: 'immediate', + timeoutMs: 3000 + }); + }, [setAppState, addNotification]); + const handleThinkingCancel = useCallback(() => { + setShowThinkingToggle(false); + }, []); + + // Memoize the thinking toggle element + const thinkingToggleElement = useMemo(() => { + if (!showThinkingToggle) return null; + return + m.type === 'assistant')} /> + ; + }, [showThinkingToggle, thinkingEnabled, handleThinkingSelect, handleThinkingCancel, messages.length]); + + // Portal dialog to DialogOverlay in fullscreen so it escapes the bottom + // slot's overflowY:hidden clip (same pattern as SuggestionsOverlay). + // Must be called before early returns below to satisfy rules-of-hooks. + // Memoized so the portal useEffect doesn't churn on every PromptInput render. + const autoModeOptInDialog = useMemo(() => feature('TRANSCRIPT_CLASSIFIER') && showAutoModeOptIn ? : null, [showAutoModeOptIn, handleAutoModeOptInAccept, handleAutoModeOptInDecline]); + useSetPromptOverlayDialog(isFullscreenEnvEnabled() ? autoModeOptInDialog : null); + if (showBashesDialog) { + return setShowBashesDialog(false)} toolUseContext={getToolUseContext(messages, [], new AbortController(), mainLoopModel)} initialDetailTaskId={typeof showBashesDialog === 'string' ? showBashesDialog : undefined} />; + } + if (isAgentSwarmsEnabled() && showTeamsDialog) { + return { + setShowTeamsDialog(false); + }} />; + } + if (feature('QUICK_SEARCH')) { + const insertWithSpacing = (text: string) => { + const cursorChar = input[cursorOffset - 1] ?? ' '; + insertTextAtCursor(/\s/.test(cursorChar) ? text : ` ${text}`); + }; + if (showQuickOpen) { + return setShowQuickOpen(false)} onInsert={insertWithSpacing} />; + } + if (showGlobalSearch) { + return setShowGlobalSearch(false)} onInsert={insertWithSpacing} />; + } + } + if (feature('HISTORY_PICKER') && showHistoryPicker) { + return { + const entryMode = getModeFromInput(entry.display); + const value = getValueFromInput(entry.display); + onModeChange(entryMode); + trackAndSetInput(value); + setPastedContents(entry.pastedContents); + setCursorOffset(value.length); + setShowHistoryPicker(false); + }} onCancel={() => setShowHistoryPicker(false)} />; + } + + // Show loop mode menu when requested (ant-only, eliminated from external builds) + if (modelPickerElement) { + return modelPickerElement; + } + if (fastModePickerElement) { + return fastModePickerElement; + } + if (thinkingToggleElement) { + return thinkingToggleElement; + } + if (showBridgeDialog) { + return { + setShowBridgeDialog(false); + selectFooterItem(null); + }} />; + } + const baseProps: BaseTextInputProps = { + multiline: true, + onSubmit, + onChange, + value: historyMatch ? getValueFromInput(typeof historyMatch === 'string' ? historyMatch : historyMatch.display) : input, + // History navigation is handled via TextInput props (onHistoryUp/onHistoryDown), + // NOT via useKeybindings. This allows useTextInput's upOrHistoryUp/downOrHistoryDown + // to try cursor movement first and only fall through to history navigation when the + // cursor can't move further (important for wrapped text and multi-line input). + onHistoryUp: handleHistoryUp, + onHistoryDown: handleHistoryDown, + onHistoryReset: resetHistory, + placeholder, + onExit, + onExitMessage: (show, key) => setExitMessage({ + show, + key + }), + onImagePaste, + columns: textInputColumns, + maxVisibleLines, + disableCursorMovementForUpDownKeys: suggestions.length > 0 || !!footerItemSelected, + disableEscapeDoublePress: suggestions.length > 0, + cursorOffset, + onChangeCursorOffset: setCursorOffset, + onPaste: onTextPaste, + onIsPastingChange: setIsPasting, + focus: !isSearchingHistory && !isModalOverlayActive && !footerItemSelected, + showCursor: !footerItemSelected && !isSearchingHistory && !cursorAtImageChip, + argumentHint: commandArgumentHint, + onUndo: canUndo ? () => { + const previousState = undo(); + if (previousState) { + trackAndSetInput(previousState.text); + setCursorOffset(previousState.cursorOffset); + setPastedContents(previousState.pastedContents); + } + } : undefined, + highlights: combinedHighlights, + inlineGhostText, + inputFilter: lazySpaceInputFilter + }; + const getBorderColor = (): keyof Theme => { + const modeColors: Record = { + bash: 'bashBorder' + }; + + // Mode colors take priority, then teammate color, then default + if (modeColors[mode]) { + return modeColors[mode]; + } + + // In-process teammates run headless - don't apply teammate colors to leader UI + if (isInProcessTeammate()) { + return 'promptBorder'; + } + + // Check for teammate color from environment + const teammateColorName = getTeammateColor(); + if (teammateColorName && AGENT_COLORS.includes(teammateColorName as AgentColorName)) { + return AGENT_COLOR_TO_THEME_COLOR[teammateColorName as AgentColorName]; + } + return 'promptBorder'; + }; + if (isExternalEditorActive) { + return + + Save and close editor to continue... + + ; + } + const textInputElement = isVimModeEnabled() ? : ; + return + {!isFullscreenEnvEnabled() && } + {hasSuppressedDialogs && + Waiting for permission… + } + + {swarmBanner ? <> + + {swarmBanner.text ? <> + {'─'.repeat(Math.max(0, columns - stringWidth(swarmBanner.text) - 4))} + + {' '} + {swarmBanner.text}{' '} + + {'──'} + : '─'.repeat(columns)} + + + + + {textInputElement} + + + {'─'.repeat(columns)} + : + + + {textInputElement} + + } + 0} isLoading={isLoading} tasksSelected={tasksSelected} teamsSelected={teamsSelected} bridgeSelected={bridgeSelected} tmuxSelected={tmuxSelected} teammateFooterIndex={teammateFooterIndex} ideSelection={ideSelection} mcpClients={mcpClients} isPasting={isPasting} isInputWrapped={isInputWrapped} messages={messages} isSearching={isSearchingHistory} historyQuery={historyQuery} setHistoryQuery={setHistoryQuery} historyFailedMatch={historyFailedMatch} onOpenTasksDialog={isFullscreenEnvEnabled() ? handleOpenTasksDialog : undefined} /> + {isFullscreenEnvEnabled() ? null : autoModeOptInDialog} + {isFullscreenEnvEnabled() ? + // position=absolute takes zero layout height so the spinner + // doesn't shift when a notification appears/disappears. Yoga + // anchors absolute children at the parent's content-box origin; + // marginTop=-1 pulls it into the marginTop=1 gap row above the + // prompt border. In brief mode there is no such gap (briefOwnsGap + // strips our marginTop) and BriefSpinner sits flush against the + // border — marginTop=-2 skips over the spinner content into + // BriefSpinner's own marginTop=1 blank row. height=1 + + // overflow=hidden clips multi-line notifications to a single row. + // flex-end anchors the bottom line so the visible row is always + // the most recent. Suppressed while the slash overlay or + // auto-mode opt-in dialog is up by height=0 (NOT unmount) — this + // Box renders later in tree order so it would paint over their + // bottom row. Keeping Notifications mounted prevents AutoUpdater's + // initial-check effect from re-firing on every slash-completion + // toggle (PR#22413). + + + : null} + ; +} + +/** + * Compute the initial paste ID by finding the max ID used in existing messages. + * This handles --continue/--resume scenarios where we need to avoid ID collisions. + */ +function getInitialPasteId(messages: Message[]): number { + let maxId = 0; + for (const message of messages) { + if (message.type === 'user') { + // Check image paste IDs + if (message.imagePasteIds) { + for (const id of message.imagePasteIds) { + if (id > maxId) maxId = id; + } + } + // Check text paste references in message content + if (Array.isArray(message.message.content)) { + for (const block of message.message.content) { + if (block.type === 'text') { + const refs = parseReferences(block.text); + for (const ref of refs) { + if (ref.id > maxId) maxId = ref.id; + } + } + } + } + } + } + return maxId + 1; +} +function buildBorderText(showFastIcon: boolean, showFastIconHint: boolean, fastModeCooldown: boolean): BorderTextOptions | undefined { + if (!showFastIcon) return undefined; + const fastSeg = showFastIconHint ? `${getFastIconString(true, fastModeCooldown)} ${chalk.dim('/fast')}` : getFastIconString(true, fastModeCooldown); + return { + content: ` ${fastSeg} `, + position: 'top', + align: 'end', + offset: 0 + }; +} +export default React.memo(PromptInput); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiY2hhbGsiLCJwYXRoIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsInVzZVN5bmNFeHRlcm5hbFN0b3JlIiwidXNlTm90aWZpY2F0aW9ucyIsInVzZUNvbW1hbmRRdWV1ZSIsIklERUF0TWVudGlvbmVkIiwidXNlSWRlQXRNZW50aW9uZWQiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJBcHBTdGF0ZSIsInVzZUFwcFN0YXRlIiwidXNlQXBwU3RhdGVTdG9yZSIsInVzZVNldEFwcFN0YXRlIiwiRm9vdGVySXRlbSIsImdldEN3ZCIsImlzUXVldWVkQ29tbWFuZEVkaXRhYmxlIiwicG9wQWxsRWRpdGFibGUiLCJzdHJpcEFuc2kiLCJjb21wYW5pb25SZXNlcnZlZENvbHVtbnMiLCJmaW5kQnVkZHlUcmlnZ2VyUG9zaXRpb25zIiwidXNlQnVkZHlOb3RpZmljYXRpb24iLCJGYXN0TW9kZVBpY2tlciIsImlzVWx0cmFyZXZpZXdFbmFibGVkIiwiZ2V0TmF0aXZlQ1NJdVRlcm1pbmFsRGlzcGxheU5hbWUiLCJDb21tYW5kIiwiaGFzQ29tbWFuZCIsInVzZUlzTW9kYWxPdmVybGF5QWN0aXZlIiwidXNlU2V0UHJvbXB0T3ZlcmxheURpYWxvZyIsImZvcm1hdEltYWdlUmVmIiwiZm9ybWF0UGFzdGVkVGV4dFJlZiIsImdldFBhc3RlZFRleHRSZWZOdW1MaW5lcyIsInBhcnNlUmVmZXJlbmNlcyIsIlZlcmlmaWNhdGlvblN0YXR1cyIsIkhpc3RvcnlNb2RlIiwidXNlQXJyb3dLZXlIaXN0b3J5IiwidXNlRG91YmxlUHJlc3MiLCJ1c2VIaXN0b3J5U2VhcmNoIiwiSURFU2VsZWN0aW9uIiwidXNlSW5wdXRCdWZmZXIiLCJ1c2VNYWluTG9vcE1vZGVsIiwidXNlUHJvbXB0U3VnZ2VzdGlvbiIsInVzZVRlcm1pbmFsU2l6ZSIsInVzZVR5cGVhaGVhZCIsIkJvcmRlclRleHRPcHRpb25zIiwic3RyaW5nV2lkdGgiLCJCb3giLCJDbGlja0V2ZW50IiwiS2V5IiwiVGV4dCIsInVzZUlucHV0IiwidXNlT3B0aW9uYWxLZXliaW5kaW5nQ29udGV4dCIsImdldFNob3J0Y3V0RGlzcGxheSIsInVzZUtleWJpbmRpbmciLCJ1c2VLZXliaW5kaW5ncyIsIk1DUFNlcnZlckNvbm5lY3Rpb24iLCJhYm9ydFByb21wdFN1Z2dlc3Rpb24iLCJsb2dTdWdnZXN0aW9uU3VwcHJlc3NlZCIsIkFjdGl2ZVNwZWN1bGF0aW9uU3RhdGUiLCJhYm9ydFNwZWN1bGF0aW9uIiwiZ2V0QWN0aXZlQWdlbnRGb3JJbnB1dCIsImdldFZpZXdlZFRlYW1tYXRlVGFzayIsImVudGVyVGVhbW1hdGVWaWV3IiwiZXhpdFRlYW1tYXRlVmlldyIsInN0b3BPckRpc21pc3NBZ2VudCIsIlRvb2xQZXJtaXNzaW9uQ29udGV4dCIsImdldFJ1bm5pbmdUZWFtbWF0ZXNTb3J0ZWQiLCJJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZSIsImlzUGFuZWxBZ2VudFRhc2siLCJMb2NhbEFnZW50VGFza1N0YXRlIiwiaXNCYWNrZ3JvdW5kVGFzayIsIkFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SIiwiQUdFTlRfQ09MT1JTIiwiQWdlbnRDb2xvck5hbWUiLCJBZ2VudERlZmluaXRpb24iLCJNZXNzYWdlIiwiUGVybWlzc2lvbk1vZGUiLCJCYXNlVGV4dElucHV0UHJvcHMiLCJQcm9tcHRJbnB1dE1vZGUiLCJWaW1Nb2RlIiwiaXNBZ2VudFN3YXJtc0VuYWJsZWQiLCJjb3VudCIsIkF1dG9VcGRhdGVyUmVzdWx0IiwiQ3Vyc29yIiwiZ2V0R2xvYmFsQ29uZmlnIiwiUGFzdGVkQ29udGVudCIsInNhdmVHbG9iYWxDb25maWciLCJsb2dGb3JEZWJ1Z2dpbmciLCJwYXJzZURpcmVjdE1lbWJlck1lc3NhZ2UiLCJzZW5kRGlyZWN0TWVtYmVyTWVzc2FnZSIsIkVmZm9ydExldmVsIiwiZW52IiwiZXJyb3JNZXNzYWdlIiwiaXNCaWxsZWRBc0V4dHJhVXNhZ2UiLCJnZXRGYXN0TW9kZVVuYXZhaWxhYmxlUmVhc29uIiwiaXNGYXN0TW9kZUF2YWlsYWJsZSIsImlzRmFzdE1vZGVDb29sZG93biIsImlzRmFzdE1vZGVFbmFibGVkIiwiaXNGYXN0TW9kZVN1cHBvcnRlZEJ5TW9kZWwiLCJpc0Z1bGxzY3JlZW5FbnZFbmFibGVkIiwiUHJvbXB0SW5wdXRIZWxwZXJzIiwiZ2V0SW1hZ2VGcm9tQ2xpcGJvYXJkIiwiUEFTVEVfVEhSRVNIT0xEIiwiSW1hZ2VEaW1lbnNpb25zIiwiY2FjaGVJbWFnZVBhdGgiLCJzdG9yZUltYWdlIiwiaXNNYWNvc09wdGlvbkNoYXIiLCJNQUNPU19PUFRJT05fU1BFQ0lBTF9DSEFSUyIsImxvZ0Vycm9yIiwiaXNPcHVzMW1NZXJnZUVuYWJsZWQiLCJtb2RlbERpc3BsYXlTdHJpbmciLCJzZXRBdXRvTW9kZUFjdGl2ZSIsImN5Y2xlUGVybWlzc2lvbk1vZGUiLCJnZXROZXh0UGVybWlzc2lvbk1vZGUiLCJ0cmFuc2l0aW9uUGVybWlzc2lvbk1vZGUiLCJnZXRQbGF0Zm9ybSIsIlByb2Nlc3NVc2VySW5wdXRDb250ZXh0IiwiZWRpdFByb21wdEluRWRpdG9yIiwiaGFzQXV0b01vZGVPcHRJbiIsImZpbmRCdHdUcmlnZ2VyUG9zaXRpb25zIiwiZmluZFNsYXNoQ29tbWFuZFBvc2l0aW9ucyIsImZpbmRTbGFja0NoYW5uZWxQb3NpdGlvbnMiLCJnZXRLbm93bkNoYW5uZWxzVmVyc2lvbiIsImhhc1NsYWNrTWNwU2VydmVyIiwic3Vic2NyaWJlS25vd25DaGFubmVscyIsImlzSW5Qcm9jZXNzRW5hYmxlZCIsInN5bmNUZWFtbWF0ZU1vZGUiLCJUZWFtU3VtbWFyeSIsImdldFRlYW1tYXRlQ29sb3IiLCJpc0luUHJvY2Vzc1RlYW1tYXRlIiwid3JpdGVUb01haWxib3giLCJUZXh0SGlnaGxpZ2h0IiwiVGhlbWUiLCJmaW5kVGhpbmtpbmdUcmlnZ2VyUG9zaXRpb25zIiwiZ2V0UmFpbmJvd0NvbG9yIiwiaXNVbHRyYXRoaW5rRW5hYmxlZCIsImZpbmRUb2tlbkJ1ZGdldFBvc2l0aW9ucyIsImZpbmRVbHRyYXBsYW5UcmlnZ2VyUG9zaXRpb25zIiwiZmluZFVsdHJhcmV2aWV3VHJpZ2dlclBvc2l0aW9ucyIsIkF1dG9Nb2RlT3B0SW5EaWFsb2ciLCJCcmlkZ2VEaWFsb2ciLCJDb25maWd1cmFibGVTaG9ydGN1dEhpbnQiLCJnZXRWaXNpYmxlQWdlbnRUYXNrcyIsInVzZUNvb3JkaW5hdG9yVGFza0NvdW50IiwiZ2V0RWZmb3J0Tm90aWZpY2F0aW9uVGV4dCIsImdldEZhc3RJY29uU3RyaW5nIiwiR2xvYmFsU2VhcmNoRGlhbG9nIiwiSGlzdG9yeVNlYXJjaERpYWxvZyIsIk1vZGVsUGlja2VyIiwiUXVpY2tPcGVuRGlhbG9nIiwiVGV4dElucHV0IiwiVGhpbmtpbmdUb2dnbGUiLCJCYWNrZ3JvdW5kVGFza3NEaWFsb2ciLCJzaG91bGRIaWRlVGFza3NGb290ZXIiLCJUZWFtc0RpYWxvZyIsIlZpbVRleHRJbnB1dCIsImdldE1vZGVGcm9tSW5wdXQiLCJnZXRWYWx1ZUZyb21JbnB1dCIsIkZPT1RFUl9URU1QT1JBUllfU1RBVFVTX1RJTUVPVVQiLCJOb3RpZmljYXRpb25zIiwiUHJvbXB0SW5wdXRGb290ZXIiLCJTdWdnZXN0aW9uSXRlbSIsIlByb21wdElucHV0TW9kZUluZGljYXRvciIsIlByb21wdElucHV0UXVldWVkQ29tbWFuZHMiLCJQcm9tcHRJbnB1dFN0YXNoTm90aWNlIiwidXNlTWF5YmVUcnVuY2F0ZUlucHV0IiwidXNlUHJvbXB0SW5wdXRQbGFjZWhvbGRlciIsInVzZVNob3dGYXN0SWNvbkhpbnQiLCJ1c2VTd2FybUJhbm5lciIsImlzTm9uU3BhY2VQcmludGFibGUiLCJpc1ZpbU1vZGVFbmFibGVkIiwiUHJvcHMiLCJkZWJ1ZyIsImlkZVNlbGVjdGlvbiIsInRvb2xQZXJtaXNzaW9uQ29udGV4dCIsInNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCIsImN0eCIsImFwaUtleVN0YXR1cyIsImNvbW1hbmRzIiwiYWdlbnRzIiwiaXNMb2FkaW5nIiwidmVyYm9zZSIsIm1lc3NhZ2VzIiwib25BdXRvVXBkYXRlclJlc3VsdCIsInJlc3VsdCIsImF1dG9VcGRhdGVyUmVzdWx0IiwiaW5wdXQiLCJvbklucHV0Q2hhbmdlIiwidmFsdWUiLCJtb2RlIiwib25Nb2RlQ2hhbmdlIiwic3Rhc2hlZFByb21wdCIsInRleHQiLCJjdXJzb3JPZmZzZXQiLCJwYXN0ZWRDb250ZW50cyIsIlJlY29yZCIsInNldFN0YXNoZWRQcm9tcHQiLCJzdWJtaXRDb3VudCIsIm9uU2hvd01lc3NhZ2VTZWxlY3RvciIsIm9uTWVzc2FnZUFjdGlvbnNFbnRlciIsIm1jcENsaWVudHMiLCJzZXRQYXN0ZWRDb250ZW50cyIsIkRpc3BhdGNoIiwiU2V0U3RhdGVBY3Rpb24iLCJ2aW1Nb2RlIiwic2V0VmltTW9kZSIsInNob3dCYXNoZXNEaWFsb2ciLCJzZXRTaG93QmFzaGVzRGlhbG9nIiwic2hvdyIsIm9uRXhpdCIsImdldFRvb2xVc2VDb250ZXh0IiwibmV3TWVzc2FnZXMiLCJhYm9ydENvbnRyb2xsZXIiLCJBYm9ydENvbnRyb2xsZXIiLCJtYWluTG9vcE1vZGVsIiwib25TdWJtaXQiLCJoZWxwZXJzIiwic3BlY3VsYXRpb25BY2NlcHQiLCJzdGF0ZSIsInNwZWN1bGF0aW9uU2Vzc2lvblRpbWVTYXZlZE1zIiwic2V0QXBwU3RhdGUiLCJmIiwicHJldiIsIm9wdGlvbnMiLCJmcm9tS2V5YmluZGluZyIsIlByb21pc2UiLCJvbkFnZW50U3VibWl0IiwidGFzayIsImlzU2VhcmNoaW5nSGlzdG9yeSIsInNldElzU2VhcmNoaW5nSGlzdG9yeSIsImlzU2VhcmNoaW5nIiwib25EaXNtaXNzU2lkZVF1ZXN0aW9uIiwiaXNTaWRlUXVlc3Rpb25WaXNpYmxlIiwiaGVscE9wZW4iLCJzZXRIZWxwT3BlbiIsImhhc1N1cHByZXNzZWREaWFsb2dzIiwiaXNMb2NhbEpTWENvbW1hbmRBY3RpdmUiLCJpbnNlcnRUZXh0UmVmIiwiTXV0YWJsZVJlZk9iamVjdCIsImluc2VydCIsInNldElucHV0V2l0aEN1cnNvciIsImN1cnNvciIsInZvaWNlSW50ZXJpbVJhbmdlIiwic3RhcnQiLCJlbmQiLCJQUk9NUFRfRk9PVEVSX0xJTkVTIiwiTUlOX0lOUFVUX1ZJRVdQT1JUX0xJTkVTIiwiUHJvbXB0SW5wdXQiLCJvblN1Ym1pdFByb3AiLCJSZWFjdE5vZGUiLCJpc01vZGFsT3ZlcmxheUFjdGl2ZSIsImlzQXV0b1VwZGF0aW5nIiwic2V0SXNBdXRvVXBkYXRpbmciLCJleGl0TWVzc2FnZSIsInNldEV4aXRNZXNzYWdlIiwia2V5Iiwic2V0Q3Vyc29yT2Zmc2V0IiwibGVuZ3RoIiwibGFzdEludGVybmFsSW5wdXRSZWYiLCJjdXJyZW50IiwidHJhY2tBbmRTZXRJbnB1dCIsIm5lZWRzU3BhY2UiLCJ0ZXN0IiwiaW5zZXJ0VGV4dCIsIm5ld1ZhbHVlIiwic2xpY2UiLCJzdG9yZSIsInRhc2tzIiwicyIsInJlcGxCcmlkZ2VDb25uZWN0ZWQiLCJyZXBsQnJpZGdlRXhwbGljaXQiLCJyZXBsQnJpZGdlUmVjb25uZWN0aW5nIiwiYnJpZGdlRm9vdGVyVmlzaWJsZSIsImhhc1R1bmdzdGVuU2Vzc2lvbiIsInR1bmdzdGVuQWN0aXZlU2Vzc2lvbiIsInVuZGVmaW5lZCIsInRtdXhGb290ZXJWaXNpYmxlIiwiYmFnZWxGb290ZXJWaXNpYmxlIiwidGVhbUNvbnRleHQiLCJxdWV1ZWRDb21tYW5kcyIsInByb21wdFN1Z2dlc3Rpb25TdGF0ZSIsInByb21wdFN1Z2dlc3Rpb24iLCJzcGVjdWxhdGlvbiIsInZpZXdpbmdBZ2VudFRhc2tJZCIsInZpZXdTZWxlY3Rpb25Nb2RlIiwic2hvd1NwaW5uZXJUcmVlIiwiZXhwYW5kZWRWaWV3IiwiY29tcGFuaW9uIiwiX2NvbXBhbmlvbiIsImNvbXBhbmlvbk11dGVkIiwiY29tcGFuaW9uRm9vdGVyVmlzaWJsZSIsImJyaWVmT3duc0dhcCIsImlzQnJpZWZPbmx5IiwibWFpbkxvb3BNb2RlbF8iLCJtYWluTG9vcE1vZGVsRm9yU2Vzc2lvbiIsInRoaW5raW5nRW5hYmxlZCIsImlzRmFzdE1vZGUiLCJmYXN0TW9kZSIsImVmZm9ydFZhbHVlIiwidmlld2VkVGVhbW1hdGUiLCJnZXRTdGF0ZSIsInZpZXdpbmdBZ2VudE5hbWUiLCJpZGVudGl0eSIsImFnZW50TmFtZSIsInZpZXdpbmdBZ2VudENvbG9yIiwiY29sb3IiLCJpbmNsdWRlcyIsImluUHJvY2Vzc1RlYW1tYXRlcyIsImlzVGVhbW1hdGVNb2RlIiwiZWZmZWN0aXZlVG9vbFBlcm1pc3Npb25Db250ZXh0IiwicGVybWlzc2lvbk1vZGUiLCJoaXN0b3J5UXVlcnkiLCJzZXRIaXN0b3J5UXVlcnkiLCJoaXN0b3J5TWF0Y2giLCJoaXN0b3J5RmFpbGVkTWF0Y2giLCJlbnRyeSIsImRpc3BsYXkiLCJuZXh0UGFzdGVJZFJlZiIsImdldEluaXRpYWxQYXN0ZUlkIiwicGVuZGluZ1NwYWNlQWZ0ZXJQaWxsUmVmIiwic2hvd1RlYW1zRGlhbG9nIiwic2V0U2hvd1RlYW1zRGlhbG9nIiwic2hvd0JyaWRnZURpYWxvZyIsInNldFNob3dCcmlkZ2VEaWFsb2ciLCJ0ZWFtbWF0ZUZvb3RlckluZGV4Iiwic2V0VGVhbW1hdGVGb290ZXJJbmRleCIsImNvb3JkaW5hdG9yVGFza0luZGV4Iiwic2V0Q29vcmRpbmF0b3JUYXNrSW5kZXgiLCJ2IiwibmV4dCIsImNvb3JkaW5hdG9yVGFza0NvdW50IiwiaGFzQmdUYXNrUGlsbCIsIk9iamVjdCIsInZhbHVlcyIsInNvbWUiLCJ0IiwibWluQ29vcmRpbmF0b3JJbmRleCIsIk1hdGgiLCJtYXgiLCJpc1Bhc3RpbmciLCJzZXRJc1Bhc3RpbmciLCJpc0V4dGVybmFsRWRpdG9yQWN0aXZlIiwic2V0SXNFeHRlcm5hbEVkaXRvckFjdGl2ZSIsInNob3dNb2RlbFBpY2tlciIsInNldFNob3dNb2RlbFBpY2tlciIsInNob3dRdWlja09wZW4iLCJzZXRTaG93UXVpY2tPcGVuIiwic2hvd0dsb2JhbFNlYXJjaCIsInNldFNob3dHbG9iYWxTZWFyY2giLCJzaG93SGlzdG9yeVBpY2tlciIsInNldFNob3dIaXN0b3J5UGlja2VyIiwic2hvd0Zhc3RNb2RlUGlja2VyIiwic2V0U2hvd0Zhc3RNb2RlUGlja2VyIiwic2hvd1RoaW5raW5nVG9nZ2xlIiwic2V0U2hvd1RoaW5raW5nVG9nZ2xlIiwic2hvd0F1dG9Nb2RlT3B0SW4iLCJzZXRTaG93QXV0b01vZGVPcHRJbiIsInByZXZpb3VzTW9kZUJlZm9yZUF1dG8iLCJzZXRQcmV2aW91c01vZGVCZWZvcmVBdXRvIiwiYXV0b01vZGVPcHRJblRpbWVvdXRSZWYiLCJOb2RlSlMiLCJUaW1lb3V0IiwiaXNDdXJzb3JPbkZpcnN0TGluZSIsImZpcnN0TmV3bGluZUluZGV4IiwiaW5kZXhPZiIsImlzQ3Vyc29yT25MYXN0TGluZSIsImxhc3ROZXdsaW5lSW5kZXgiLCJsYXN0SW5kZXhPZiIsImNhY2hlZFRlYW1zIiwidGVhbW1hdGVDb3VudCIsInRlYW1tYXRlcyIsIm5hbWUiLCJ0ZWFtTmFtZSIsIm1lbWJlckNvdW50IiwicnVubmluZ0NvdW50IiwiaWRsZUNvdW50IiwicnVubmluZ1Rhc2tDb3VudCIsInN0YXR1cyIsInRhc2tzRm9vdGVyVmlzaWJsZSIsInRlYW1zRm9vdGVyVmlzaWJsZSIsImZvb3Rlckl0ZW1zIiwiZmlsdGVyIiwiQm9vbGVhbiIsInJhd0Zvb3RlclNlbGVjdGlvbiIsImZvb3RlclNlbGVjdGlvbiIsImZvb3Rlckl0ZW1TZWxlY3RlZCIsInRhc2tzU2VsZWN0ZWQiLCJ0bXV4U2VsZWN0ZWQiLCJiYWdlbFNlbGVjdGVkIiwidGVhbXNTZWxlY3RlZCIsImJyaWRnZVNlbGVjdGVkIiwic2VsZWN0Rm9vdGVySXRlbSIsIml0ZW0iLCJuYXZpZ2F0ZUZvb3RlciIsImRlbHRhIiwiZXhpdEF0U3RhcnQiLCJpZHgiLCJzdWdnZXN0aW9uIiwibWFya0FjY2VwdGVkIiwibG9nT3V0Y29tZUF0U3VibWlzc2lvbiIsIm1hcmtTaG93biIsImlucHV0VmFsdWUiLCJpc0Fzc2lzdGFudFJlc3BvbmRpbmciLCJkaXNwbGF5ZWRWYWx1ZSIsInRoaW5rVHJpZ2dlcnMiLCJ1bHRyYXBsYW5TZXNzaW9uVXJsIiwidWx0cmFwbGFuTGF1bmNoaW5nIiwidWx0cmFwbGFuVHJpZ2dlcnMiLCJ1bHRyYXJldmlld1RyaWdnZXJzIiwiYnR3VHJpZ2dlcnMiLCJidWRkeVRyaWdnZXJzIiwic2xhc2hDb21tYW5kVHJpZ2dlcnMiLCJwb3NpdGlvbnMiLCJwb3MiLCJjb21tYW5kTmFtZSIsInRva2VuQnVkZ2V0VHJpZ2dlcnMiLCJrbm93bkNoYW5uZWxzVmVyc2lvbiIsInNsYWNrQ2hhbm5lbFRyaWdnZXJzIiwibWNwIiwiY2xpZW50cyIsIm1lbWJlck1lbnRpb25IaWdobGlnaHRzIiwiQXJyYXkiLCJ0aGVtZUNvbG9yIiwiaGlnaGxpZ2h0cyIsIm1lbWJlcnMiLCJyZWdleCIsIm1lbWJlclZhbHVlcyIsIm1hdGNoIiwiZXhlYyIsImxlYWRpbmdTcGFjZSIsIm5hbWVTdGFydCIsImluZGV4IiwiZnVsbE1hdGNoIiwidHJpbVN0YXJ0IiwibWVtYmVyIiwiZmluZCIsInB1c2giLCJpbWFnZVJlZlBvc2l0aW9ucyIsInIiLCJzdGFydHNXaXRoIiwibWFwIiwiY3Vyc29yQXRJbWFnZUNoaXAiLCJpbnNpZGUiLCJtaWQiLCJjb21iaW5lZEhpZ2hsaWdodHMiLCJyZWYiLCJpbnZlcnNlIiwicHJpb3JpdHkiLCJ0cmlnZ2VyIiwibWVudGlvbiIsImRpbUNvbG9yIiwiaSIsInNoaW1tZXJDb2xvciIsImFkZE5vdGlmaWNhdGlvbiIsInJlbW92ZU5vdGlmaWNhdGlvbiIsInRpbWVvdXRNcyIsInByZXZJbnB1dExlbmd0aFJlZiIsInBlYWtJbnB1dExlbmd0aFJlZiIsImRpc21pc3NTdGFzaEhpbnQiLCJwcmV2TGVuZ3RoIiwicGVha0xlbmd0aCIsImN1cnJlbnRMZW5ndGgiLCJjbGVhcmVkU3Vic3RhbnRpYWxJbnB1dCIsIndhc1JhcGlkQ2xlYXIiLCJjb25maWciLCJoYXNVc2VkU3Rhc2giLCJqc3giLCJwdXNoVG9CdWZmZXIiLCJ1bmRvIiwiY2FuVW5kbyIsImNsZWFyQnVmZmVyIiwibWF4QnVmZmVyU2l6ZSIsImRlYm91bmNlTXMiLCJkZWZhdWx0UGxhY2Vob2xkZXIiLCJvbkNoYW5nZSIsImlzU2luZ2xlQ2hhckluc2VydGlvbiIsImluc2VydGVkQXRTdGFydCIsInZhbHVlV2l0aG91dE1vZGUiLCJyZXBsYWNlQWxsIiwicHJvY2Vzc2VkVmFsdWUiLCJyZXNldEhpc3RvcnkiLCJvbkhpc3RvcnlVcCIsIm9uSGlzdG9yeURvd24iLCJkaXNtaXNzU2VhcmNoSGludCIsImhpc3RvcnlJbmRleCIsImhpc3RvcnlNb2RlIiwiaGFuZGxlSGlzdG9yeVVwIiwic3VnZ2VzdGlvbnMiLCJoYXNFZGl0YWJsZUNvbW1hbmQiLCJwb3BBbGxDb21tYW5kc0Zyb21RdWV1ZSIsImhhbmRsZUhpc3RvcnlEb3duIiwiZmlyc3QiLCJoYXNTZWVuVGFza3NIaW50IiwiYyIsInN1Z2dlc3Rpb25zU3RhdGUiLCJzZXRTdWdnZXN0aW9uc1N0YXRlUmF3Iiwic2VsZWN0ZWRTdWdnZXN0aW9uIiwiY29tbWFuZEFyZ3VtZW50SGludCIsInNldFN1Z2dlc3Rpb25zU3RhdGUiLCJ1cGRhdGVyIiwiaW5wdXRQYXJhbSIsImlzU3VibWl0dGluZ1NsYXNoQ29tbWFuZCIsInRyaW1FbmQiLCJoYXNJbWFnZXMiLCJ0eXBlIiwic3VnZ2VzdGlvblRleHQiLCJpbnB1dE1hdGNoZXNTdWdnZXN0aW9uIiwidHJpbSIsInNraXBSZXNldCIsInNob3duQXQiLCJkaXJlY3RNZXNzYWdlIiwicmVjaXBpZW50TmFtZSIsIm1lc3NhZ2UiLCJzdWNjZXNzIiwiZXJyb3IiLCJoYXNEaXJlY3RvcnlTdWdnZXN0aW9ucyIsImV2ZXJ5IiwiZGVzY3JpcHRpb24iLCJhY3RpdmVBZ2VudCIsImlubGluZUdob3N0VGV4dCIsIm1heENvbHVtbldpZHRoIiwic3VwcHJlc3NTdWdnZXN0aW9ucyIsInNob3dQcm9tcHRTdWdnZXN0aW9uIiwicHJvbXB0SWQiLCJhY2NlcHRlZEF0IiwiZ2VuZXJhdGlvblJlcXVlc3RJZCIsIm9uSW1hZ2VQYXN0ZSIsImltYWdlIiwibWVkaWFUeXBlIiwiZmlsZW5hbWUiLCJkaW1lbnNpb25zIiwic291cmNlUGF0aCIsInBhc3RlSWQiLCJuZXdDb250ZW50IiwiaWQiLCJjb250ZW50IiwicHJlZml4IiwiaW5zZXJ0VGV4dEF0Q3Vyc29yIiwicmVmZXJlbmNlZElkcyIsIlNldCIsIm9ycGhhbmVkIiwiaGFzIiwiaW1nIiwib25UZXh0UGFzdGUiLCJyYXdUZXh0IiwicmVwbGFjZSIsInBhc3RlZE1vZGUiLCJudW1MaW5lcyIsIm1heExpbmVzIiwibWluIiwicm93cyIsImxhenlTcGFjZUlucHV0RmlsdGVyIiwibmV3SW5wdXQiLCJkb3VibGVQcmVzc0VzY0Zyb21FbXB0eSIsImltYWdlcyIsIm5ld0NvbnRlbnRzIiwib25JZGVBdE1lbnRpb25lZCIsImF0TWVudGlvbmVkIiwiYXRNZW50aW9uZWRUZXh0IiwicmVsYXRpdmVQYXRoIiwicmVsYXRpdmUiLCJmaWxlUGF0aCIsImxpbmVTdGFydCIsImxpbmVFbmQiLCJjdXJzb3JDaGFyIiwiaGFuZGxlVW5kbyIsInByZXZpb3VzU3RhdGUiLCJoYW5kbGVOZXdsaW5lIiwiaGFuZGxlRXh0ZXJuYWxFZGl0b3IiLCJlcnIiLCJFcnJvciIsImhhbmRsZVN0YXNoIiwiaGFuZGxlTW9kZWxQaWNrZXIiLCJoYW5kbGVGYXN0TW9kZVBpY2tlciIsImhhbmRsZVRoaW5raW5nVG9nZ2xlIiwiaGFuZGxlQ3ljbGVNb2RlIiwidGVhbW1hdGVDb250ZXh0IiwibmV4dE1vZGUiLCJ0byIsInRlYW1tYXRlVGFza0lkIiwiaXNBdXRvTW9kZUF2YWlsYWJsZSIsImlzRW50ZXJpbmdBdXRvTW9kZUZpcnN0VGltZSIsImNsZWFyVGltZW91dCIsInNldFRpbWVvdXQiLCJjb250ZXh0IiwicHJlcGFyZWRDb250ZXh0IiwibGFzdFBsYW5Nb2RlVXNlIiwiRGF0ZSIsIm5vdyIsImhhbmRsZUF1dG9Nb2RlT3B0SW5BY2NlcHQiLCJzdHJpcHBlZENvbnRleHQiLCJoYW5kbGVBdXRvTW9kZU9wdEluRGVjbGluZSIsImhhbmRsZUltYWdlUGFzdGUiLCJ0aGVuIiwiaW1hZ2VEYXRhIiwiYmFzZTY0Iiwic2hvcnRjdXREaXNwbGF5IiwiaXNTU0giLCJrZXliaW5kaW5nQ29udGV4dCIsInJlZ2lzdGVySGFuZGxlciIsImFjdGlvbiIsImhhbmRsZXIiLCJjaGF0SGFuZGxlcnMiLCJpc0FjdGl2ZSIsInF1aWNrU2VhcmNoQWN0aXZlIiwiZm9vdGVyOnVwIiwiZm9vdGVyOmRvd24iLCJmb290ZXI6bmV4dCIsInRvdGFsQWdlbnRzIiwiZm9vdGVyOnByZXZpb3VzIiwiZm9vdGVyOm9wZW5TZWxlY3RlZCIsInRlYW1tYXRlIiwic2VsZWN0ZWRUYXNrSWQiLCJ0dW5nc3RlblBhbmVsQXV0b0hpZGRlbiIsInR1bmdzdGVuUGFuZWxWaXNpYmxlIiwiZm9vdGVyOmNsZWFyU2VsZWN0aW9uIiwiZm9vdGVyOmNsb3NlIiwiY2hhciIsInNob3J0Y3V0IiwidGVybWluYWxOYW1lIiwiY3RybCIsIm1ldGEiLCJlc2NhcGUiLCJyZXR1cm4iLCJiYWNrc3BhY2UiLCJkZWxldGUiLCJzd2FybUJhbm5lciIsImZhc3RNb2RlQ29vbGRvd24iLCJzaG93RmFzdEljb24iLCJzaG93RmFzdEljb25IaW50IiwiZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCIsImNvbXBhbmlvblNwZWFraW5nIiwiY29tcGFuaW9uUmVhY3Rpb24iLCJjb2x1bW5zIiwidGV4dElucHV0Q29sdW1ucyIsIm1heFZpc2libGVMaW5lcyIsImZsb29yIiwiaGFuZGxlSW5wdXRDbGljayIsImUiLCJmcm9tVGV4dCIsInZpZXdwb3J0U3RhcnQiLCJnZXRWaWV3cG9ydFN0YXJ0TGluZSIsIm9mZnNldCIsIm1lYXN1cmVkVGV4dCIsImdldE9mZnNldEZyb21Qb3NpdGlvbiIsImxpbmUiLCJsb2NhbFJvdyIsImNvbHVtbiIsImxvY2FsQ29sIiwiaGFuZGxlT3BlblRhc2tzRGlhbG9nIiwidGFza0lkIiwicGxhY2Vob2xkZXIiLCJpc0lucHV0V3JhcHBlZCIsImhhbmRsZU1vZGVsU2VsZWN0IiwibW9kZWwiLCJfZWZmb3J0Iiwid2FzRmFzdE1vZGVEaXNhYmxlZCIsImVmZmVjdGl2ZUZhc3RNb2RlIiwiaGFuZGxlTW9kZWxDYW5jZWwiLCJtb2RlbFBpY2tlckVsZW1lbnQiLCJoYW5kbGVGYXN0TW9kZVNlbGVjdCIsImZhc3RNb2RlUGlja2VyRWxlbWVudCIsImhhbmRsZVRoaW5raW5nU2VsZWN0IiwiZW5hYmxlZCIsImhhbmRsZVRoaW5raW5nQ2FuY2VsIiwidGhpbmtpbmdUb2dnbGVFbGVtZW50IiwibSIsImF1dG9Nb2RlT3B0SW5EaWFsb2ciLCJpbnNlcnRXaXRoU3BhY2luZyIsImVudHJ5TW9kZSIsImJhc2VQcm9wcyIsIm11bHRpbGluZSIsIm9uSGlzdG9yeVJlc2V0Iiwib25FeGl0TWVzc2FnZSIsImRpc2FibGVDdXJzb3JNb3ZlbWVudEZvclVwRG93bktleXMiLCJkaXNhYmxlRXNjYXBlRG91YmxlUHJlc3MiLCJvbkNoYW5nZUN1cnNvck9mZnNldCIsIm9uUGFzdGUiLCJvbklzUGFzdGluZ0NoYW5nZSIsImZvY3VzIiwic2hvd0N1cnNvciIsImFyZ3VtZW50SGludCIsIm9uVW5kbyIsImlucHV0RmlsdGVyIiwiZ2V0Qm9yZGVyQ29sb3IiLCJtb2RlQ29sb3JzIiwiYmFzaCIsInRlYW1tYXRlQ29sb3JOYW1lIiwidGV4dElucHV0RWxlbWVudCIsImJnQ29sb3IiLCJyZXBlYXQiLCJidWlsZEJvcmRlclRleHQiLCJtYXhJZCIsImltYWdlUGFzdGVJZHMiLCJpc0FycmF5IiwiYmxvY2siLCJyZWZzIiwiZmFzdFNlZyIsImRpbSIsInBvc2l0aW9uIiwiYWxpZ24iLCJtZW1vIl0sInNvdXJjZXMiOlsiUHJvbXB0SW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJ1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICB1c2VDYWxsYmFjayxcbiAgdXNlRWZmZWN0LFxuICB1c2VNZW1vLFxuICB1c2VSZWYsXG4gIHVzZVN0YXRlLFxuICB1c2VTeW5jRXh0ZXJuYWxTdG9yZSxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnc3JjL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB7IHVzZUNvbW1hbmRRdWV1ZSB9IGZyb20gJ3NyYy9ob29rcy91c2VDb21tYW5kUXVldWUuanMnXG5pbXBvcnQge1xuICB0eXBlIElERUF0TWVudGlvbmVkLFxuICB1c2VJZGVBdE1lbnRpb25lZCxcbn0gZnJvbSAnc3JjL2hvb2tzL3VzZUlkZUF0TWVudGlvbmVkLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7XG4gIHR5cGUgQXBwU3RhdGUsXG4gIHVzZUFwcFN0YXRlLFxuICB1c2VBcHBTdGF0ZVN0b3JlLFxuICB1c2VTZXRBcHBTdGF0ZSxcbn0gZnJvbSAnc3JjL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBGb290ZXJJdGVtIH0gZnJvbSAnc3JjL3N0YXRlL0FwcFN0YXRlU3RvcmUuanMnXG5pbXBvcnQgeyBnZXRDd2QgfSBmcm9tICdzcmMvdXRpbHMvY3dkLmpzJ1xuaW1wb3J0IHtcbiAgaXNRdWV1ZWRDb21tYW5kRWRpdGFibGUsXG4gIHBvcEFsbEVkaXRhYmxlLFxufSBmcm9tICdzcmMvdXRpbHMvbWVzc2FnZVF1ZXVlTWFuYWdlci5qcydcbmltcG9ydCBzdHJpcEFuc2kgZnJvbSAnc3RyaXAtYW5zaSdcbmltcG9ydCB7IGNvbXBhbmlvblJlc2VydmVkQ29sdW1ucyB9IGZyb20gJy4uLy4uL2J1ZGR5L0NvbXBhbmlvblNwcml0ZS5qcydcbmltcG9ydCB7XG4gIGZpbmRCdWRkeVRyaWdnZXJQb3NpdGlvbnMsXG4gIHVzZUJ1ZGR5Tm90aWZpY2F0aW9uLFxufSBmcm9tICcuLi8uLi9idWRkeS91c2VCdWRkeU5vdGlmaWNhdGlvbi5qcydcbmltcG9ydCB7IEZhc3RNb2RlUGlja2VyIH0gZnJvbSAnLi4vLi4vY29tbWFuZHMvZmFzdC9mYXN0LmpzJ1xuaW1wb3J0IHsgaXNVbHRyYXJldmlld0VuYWJsZWQgfSBmcm9tICcuLi8uLi9jb21tYW5kcy9yZXZpZXcvdWx0cmFyZXZpZXdFbmFibGVkLmpzJ1xuaW1wb3J0IHsgZ2V0TmF0aXZlQ1NJdVRlcm1pbmFsRGlzcGxheU5hbWUgfSBmcm9tICcuLi8uLi9jb21tYW5kcy90ZXJtaW5hbFNldHVwL3Rlcm1pbmFsU2V0dXAuanMnXG5pbXBvcnQgeyB0eXBlIENvbW1hbmQsIGhhc0NvbW1hbmQgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IHVzZUlzTW9kYWxPdmVybGF5QWN0aXZlIH0gZnJvbSAnLi4vLi4vY29udGV4dC9vdmVybGF5Q29udGV4dC5qcydcbmltcG9ydCB7IHVzZVNldFByb21wdE92ZXJsYXlEaWFsb2cgfSBmcm9tICcuLi8uLi9jb250ZXh0L3Byb21wdE92ZXJsYXlDb250ZXh0LmpzJ1xuaW1wb3J0IHtcbiAgZm9ybWF0SW1hZ2VSZWYsXG4gIGZvcm1hdFBhc3RlZFRleHRSZWYsXG4gIGdldFBhc3RlZFRleHRSZWZOdW1MaW5lcyxcbiAgcGFyc2VSZWZlcmVuY2VzLFxufSBmcm9tICcuLi8uLi9oaXN0b3J5LmpzJ1xuaW1wb3J0IHR5cGUgeyBWZXJpZmljYXRpb25TdGF0dXMgfSBmcm9tICcuLi8uLi9ob29rcy91c2VBcGlLZXlWZXJpZmljYXRpb24uanMnXG5pbXBvcnQge1xuICB0eXBlIEhpc3RvcnlNb2RlLFxuICB1c2VBcnJvd0tleUhpc3RvcnksXG59IGZyb20gJy4uLy4uL2hvb2tzL3VzZUFycm93S2V5SGlzdG9yeS5qcydcbmltcG9ydCB7IHVzZURvdWJsZVByZXNzIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlRG91YmxlUHJlc3MuanMnXG5pbXBvcnQgeyB1c2VIaXN0b3J5U2VhcmNoIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlSGlzdG9yeVNlYXJjaC5qcydcbmltcG9ydCB0eXBlIHsgSURFU2VsZWN0aW9uIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlSWRlU2VsZWN0aW9uLmpzJ1xuaW1wb3J0IHsgdXNlSW5wdXRCdWZmZXIgfSBmcm9tICcuLi8uLi9ob29rcy91c2VJbnB1dEJ1ZmZlci5qcydcbmltcG9ydCB7IHVzZU1haW5Mb29wTW9kZWwgfSBmcm9tICcuLi8uLi9ob29rcy91c2VNYWluTG9vcE1vZGVsLmpzJ1xuaW1wb3J0IHsgdXNlUHJvbXB0U3VnZ2VzdGlvbiB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZVByb21wdFN1Z2dlc3Rpb24uanMnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyB1c2VUeXBlYWhlYWQgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUeXBlYWhlYWQuanMnXG5pbXBvcnQgdHlwZSB7IEJvcmRlclRleHRPcHRpb25zIH0gZnJvbSAnLi4vLi4vaW5rL3JlbmRlci1ib3JkZXIuanMnXG5pbXBvcnQgeyBzdHJpbmdXaWR0aCB9IGZyb20gJy4uLy4uL2luay9zdHJpbmdXaWR0aC5qcydcbmltcG9ydCB7IEJveCwgdHlwZSBDbGlja0V2ZW50LCB0eXBlIEtleSwgVGV4dCwgdXNlSW5wdXQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0IH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvS2V5YmluZGluZ0NvbnRleHQuanMnXG5pbXBvcnQgeyBnZXRTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy9zaG9ydGN1dEZvcm1hdC5qcydcbmltcG9ydCB7XG4gIHVzZUtleWJpbmRpbmcsXG4gIHVzZUtleWJpbmRpbmdzLFxufSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHR5cGUgeyBNQ1BTZXJ2ZXJDb25uZWN0aW9uIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvbWNwL3R5cGVzLmpzJ1xuaW1wb3J0IHtcbiAgYWJvcnRQcm9tcHRTdWdnZXN0aW9uLFxuICBsb2dTdWdnZXN0aW9uU3VwcHJlc3NlZCxcbn0gZnJvbSAnLi4vLi4vc2VydmljZXMvUHJvbXB0U3VnZ2VzdGlvbi9wcm9tcHRTdWdnZXN0aW9uLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBY3RpdmVTcGVjdWxhdGlvblN0YXRlLFxuICBhYm9ydFNwZWN1bGF0aW9uLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9Qcm9tcHRTdWdnZXN0aW9uL3NwZWN1bGF0aW9uLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0QWN0aXZlQWdlbnRGb3JJbnB1dCxcbiAgZ2V0Vmlld2VkVGVhbW1hdGVUYXNrLFxufSBmcm9tICcuLi8uLi9zdGF0ZS9zZWxlY3RvcnMuanMnXG5pbXBvcnQge1xuICBlbnRlclRlYW1tYXRlVmlldyxcbiAgZXhpdFRlYW1tYXRlVmlldyxcbiAgc3RvcE9yRGlzbWlzc0FnZW50LFxufSBmcm9tICcuLi8uLi9zdGF0ZS90ZWFtbWF0ZVZpZXdIZWxwZXJzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUb29sUGVybWlzc2lvbkNvbnRleHQgfSBmcm9tICcuLi8uLi9Ub29sLmpzJ1xuaW1wb3J0IHsgZ2V0UnVubmluZ1RlYW1tYXRlc1NvcnRlZCB9IGZyb20gJy4uLy4uL3Rhc2tzL0luUHJvY2Vzc1RlYW1tYXRlVGFzay9JblByb2Nlc3NUZWFtbWF0ZVRhc2suanMnXG5pbXBvcnQgdHlwZSB7IEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlIH0gZnJvbSAnLi4vLi4vdGFza3MvSW5Qcm9jZXNzVGVhbW1hdGVUYXNrL3R5cGVzLmpzJ1xuaW1wb3J0IHtcbiAgaXNQYW5lbEFnZW50VGFzayxcbiAgdHlwZSBMb2NhbEFnZW50VGFza1N0YXRlLFxufSBmcm9tICcuLi8uLi90YXNrcy9Mb2NhbEFnZW50VGFzay9Mb2NhbEFnZW50VGFzay5qcydcbmltcG9ydCB7IGlzQmFja2dyb3VuZFRhc2sgfSBmcm9tICcuLi8uLi90YXNrcy90eXBlcy5qcydcbmltcG9ydCB7XG4gIEFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SLFxuICBBR0VOVF9DT0xPUlMsXG4gIHR5cGUgQWdlbnRDb2xvck5hbWUsXG59IGZyb20gJy4uLy4uL3Rvb2xzL0FnZW50VG9vbC9hZ2VudENvbG9yTWFuYWdlci5qcydcbmltcG9ydCB0eXBlIHsgQWdlbnREZWZpbml0aW9uIH0gZnJvbSAnLi4vLi4vdG9vbHMvQWdlbnRUb29sL2xvYWRBZ2VudHNEaXIuanMnXG5pbXBvcnQgdHlwZSB7IE1lc3NhZ2UgfSBmcm9tICcuLi8uLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHR5cGUgeyBQZXJtaXNzaW9uTW9kZSB9IGZyb20gJy4uLy4uL3R5cGVzL3Blcm1pc3Npb25zLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBCYXNlVGV4dElucHV0UHJvcHMsXG4gIFByb21wdElucHV0TW9kZSxcbiAgVmltTW9kZSxcbn0gZnJvbSAnLi4vLi4vdHlwZXMvdGV4dElucHV0VHlwZXMuanMnXG5pbXBvcnQgeyBpc0FnZW50U3dhcm1zRW5hYmxlZCB9IGZyb20gJy4uLy4uL3V0aWxzL2FnZW50U3dhcm1zRW5hYmxlZC5qcydcbmltcG9ydCB7IGNvdW50IH0gZnJvbSAnLi4vLi4vdXRpbHMvYXJyYXkuanMnXG5pbXBvcnQgdHlwZSB7IEF1dG9VcGRhdGVyUmVzdWx0IH0gZnJvbSAnLi4vLi4vdXRpbHMvYXV0b1VwZGF0ZXIuanMnXG5pbXBvcnQgeyBDdXJzb3IgfSBmcm9tICcuLi8uLi91dGlscy9DdXJzb3IuanMnXG5pbXBvcnQge1xuICBnZXRHbG9iYWxDb25maWcsXG4gIHR5cGUgUGFzdGVkQ29udGVudCxcbiAgc2F2ZUdsb2JhbENvbmZpZyxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgbG9nRm9yRGVidWdnaW5nIH0gZnJvbSAnLi4vLi4vdXRpbHMvZGVidWcuanMnXG5pbXBvcnQge1xuICBwYXJzZURpcmVjdE1lbWJlck1lc3NhZ2UsXG4gIHNlbmREaXJlY3RNZW1iZXJNZXNzYWdlLFxufSBmcm9tICcuLi8uLi91dGlscy9kaXJlY3RNZW1iZXJNZXNzYWdlLmpzJ1xuaW1wb3J0IHR5cGUgeyBFZmZvcnRMZXZlbCB9IGZyb20gJy4uLy4uL3V0aWxzL2VmZm9ydC5qcydcbmltcG9ydCB7IGVudiB9IGZyb20gJy4uLy4uL3V0aWxzL2Vudi5qcydcbmltcG9ydCB7IGVycm9yTWVzc2FnZSB9IGZyb20gJy4uLy4uL3V0aWxzL2Vycm9ycy5qcydcbmltcG9ydCB7IGlzQmlsbGVkQXNFeHRyYVVzYWdlIH0gZnJvbSAnLi4vLi4vdXRpbHMvZXh0cmFVc2FnZS5qcydcbmltcG9ydCB7XG4gIGdldEZhc3RNb2RlVW5hdmFpbGFibGVSZWFzb24sXG4gIGlzRmFzdE1vZGVBdmFpbGFibGUsXG4gIGlzRmFzdE1vZGVDb29sZG93bixcbiAgaXNGYXN0TW9kZUVuYWJsZWQsXG4gIGlzRmFzdE1vZGVTdXBwb3J0ZWRCeU1vZGVsLFxufSBmcm9tICcuLi8uLi91dGlscy9mYXN0TW9kZS5qcydcbmltcG9ydCB7IGlzRnVsbHNjcmVlbkVudkVuYWJsZWQgfSBmcm9tICcuLi8uLi91dGlscy9mdWxsc2NyZWVuLmpzJ1xuaW1wb3J0IHR5cGUgeyBQcm9tcHRJbnB1dEhlbHBlcnMgfSBmcm9tICcuLi8uLi91dGlscy9oYW5kbGVQcm9tcHRTdWJtaXQuanMnXG5pbXBvcnQge1xuICBnZXRJbWFnZUZyb21DbGlwYm9hcmQsXG4gIFBBU1RFX1RIUkVTSE9MRCxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvaW1hZ2VQYXN0ZS5qcydcbmltcG9ydCB0eXBlIHsgSW1hZ2VEaW1lbnNpb25zIH0gZnJvbSAnLi4vLi4vdXRpbHMvaW1hZ2VSZXNpemVyLmpzJ1xuaW1wb3J0IHsgY2FjaGVJbWFnZVBhdGgsIHN0b3JlSW1hZ2UgfSBmcm9tICcuLi8uLi91dGlscy9pbWFnZVN0b3JlLmpzJ1xuaW1wb3J0IHtcbiAgaXNNYWNvc09wdGlvbkNoYXIsXG4gIE1BQ09TX09QVElPTl9TUEVDSUFMX0NIQVJTLFxufSBmcm9tICcuLi8uLi91dGlscy9rZXlib2FyZFNob3J0Y3V0cy5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nLmpzJ1xuaW1wb3J0IHtcbiAgaXNPcHVzMW1NZXJnZUVuYWJsZWQsXG4gIG1vZGVsRGlzcGxheVN0cmluZyxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvbW9kZWwvbW9kZWwuanMnXG5pbXBvcnQgeyBzZXRBdXRvTW9kZUFjdGl2ZSB9IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL2F1dG9Nb2RlU3RhdGUuanMnXG5pbXBvcnQge1xuICBjeWNsZVBlcm1pc3Npb25Nb2RlLFxuICBnZXROZXh0UGVybWlzc2lvbk1vZGUsXG59IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL2dldE5leHRQZXJtaXNzaW9uTW9kZS5qcydcbmltcG9ydCB7IHRyYW5zaXRpb25QZXJtaXNzaW9uTW9kZSB9IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL3Blcm1pc3Npb25TZXR1cC5qcydcbmltcG9ydCB7IGdldFBsYXRmb3JtIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGxhdGZvcm0uanMnXG5pbXBvcnQgdHlwZSB7IFByb2Nlc3NVc2VySW5wdXRDb250ZXh0IH0gZnJvbSAnLi4vLi4vdXRpbHMvcHJvY2Vzc1VzZXJJbnB1dC9wcm9jZXNzVXNlcklucHV0LmpzJ1xuaW1wb3J0IHsgZWRpdFByb21wdEluRWRpdG9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvcHJvbXB0RWRpdG9yLmpzJ1xuaW1wb3J0IHsgaGFzQXV0b01vZGVPcHRJbiB9IGZyb20gJy4uLy4uL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuaW1wb3J0IHsgZmluZEJ0d1RyaWdnZXJQb3NpdGlvbnMgfSBmcm9tICcuLi8uLi91dGlscy9zaWRlUXVlc3Rpb24uanMnXG5pbXBvcnQgeyBmaW5kU2xhc2hDb21tYW5kUG9zaXRpb25zIH0gZnJvbSAnLi4vLi4vdXRpbHMvc3VnZ2VzdGlvbnMvY29tbWFuZFN1Z2dlc3Rpb25zLmpzJ1xuaW1wb3J0IHtcbiAgZmluZFNsYWNrQ2hhbm5lbFBvc2l0aW9ucyxcbiAgZ2V0S25vd25DaGFubmVsc1ZlcnNpb24sXG4gIGhhc1NsYWNrTWNwU2VydmVyLFxuICBzdWJzY3JpYmVLbm93bkNoYW5uZWxzLFxufSBmcm9tICcuLi8uLi91dGlscy9zdWdnZXN0aW9ucy9zbGFja0NoYW5uZWxTdWdnZXN0aW9ucy5qcydcbmltcG9ydCB7IGlzSW5Qcm9jZXNzRW5hYmxlZCB9IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL2JhY2tlbmRzL3JlZ2lzdHJ5LmpzJ1xuaW1wb3J0IHsgc3luY1RlYW1tYXRlTW9kZSB9IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL3RlYW1IZWxwZXJzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUZWFtU3VtbWFyeSB9IGZyb20gJy4uLy4uL3V0aWxzL3RlYW1EaXNjb3ZlcnkuanMnXG5pbXBvcnQgeyBnZXRUZWFtbWF0ZUNvbG9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGVhbW1hdGUuanMnXG5pbXBvcnQgeyBpc0luUHJvY2Vzc1RlYW1tYXRlIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGVhbW1hdGVDb250ZXh0LmpzJ1xuaW1wb3J0IHsgd3JpdGVUb01haWxib3ggfSBmcm9tICcuLi8uLi91dGlscy90ZWFtbWF0ZU1haWxib3guanMnXG5pbXBvcnQgdHlwZSB7IFRleHRIaWdobGlnaHQgfSBmcm9tICcuLi8uLi91dGlscy90ZXh0SGlnaGxpZ2h0aW5nLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJy4uLy4uL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHtcbiAgZmluZFRoaW5raW5nVHJpZ2dlclBvc2l0aW9ucyxcbiAgZ2V0UmFpbmJvd0NvbG9yLFxuICBpc1VsdHJhdGhpbmtFbmFibGVkLFxufSBmcm9tICcuLi8uLi91dGlscy90aGlua2luZy5qcydcbmltcG9ydCB7IGZpbmRUb2tlbkJ1ZGdldFBvc2l0aW9ucyB9IGZyb20gJy4uLy4uL3V0aWxzL3Rva2VuQnVkZ2V0LmpzJ1xuaW1wb3J0IHtcbiAgZmluZFVsdHJhcGxhblRyaWdnZXJQb3NpdGlvbnMsXG4gIGZpbmRVbHRyYXJldmlld1RyaWdnZXJQb3NpdGlvbnMsXG59IGZyb20gJy4uLy4uL3V0aWxzL3VsdHJhcGxhbi9rZXl3b3JkLmpzJ1xuaW1wb3J0IHsgQXV0b01vZGVPcHRJbkRpYWxvZyB9IGZyb20gJy4uL0F1dG9Nb2RlT3B0SW5EaWFsb2cuanMnXG5pbXBvcnQgeyBCcmlkZ2VEaWFsb2cgfSBmcm9tICcuLi9CcmlkZ2VEaWFsb2cuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQge1xuICBnZXRWaXNpYmxlQWdlbnRUYXNrcyxcbiAgdXNlQ29vcmRpbmF0b3JUYXNrQ291bnQsXG59IGZyb20gJy4uL0Nvb3JkaW5hdG9yQWdlbnRTdGF0dXMuanMnXG5pbXBvcnQgeyBnZXRFZmZvcnROb3RpZmljYXRpb25UZXh0IH0gZnJvbSAnLi4vRWZmb3J0SW5kaWNhdG9yLmpzJ1xuaW1wb3J0IHsgZ2V0RmFzdEljb25TdHJpbmcgfSBmcm9tICcuLi9GYXN0SWNvbi5qcydcbmltcG9ydCB7IEdsb2JhbFNlYXJjaERpYWxvZyB9IGZyb20gJy4uL0dsb2JhbFNlYXJjaERpYWxvZy5qcydcbmltcG9ydCB7IEhpc3RvcnlTZWFyY2hEaWFsb2cgfSBmcm9tICcuLi9IaXN0b3J5U2VhcmNoRGlhbG9nLmpzJ1xuaW1wb3J0IHsgTW9kZWxQaWNrZXIgfSBmcm9tICcuLi9Nb2RlbFBpY2tlci5qcydcbmltcG9ydCB7IFF1aWNrT3BlbkRpYWxvZyB9IGZyb20gJy4uL1F1aWNrT3BlbkRpYWxvZy5qcydcbmltcG9ydCBUZXh0SW5wdXQgZnJvbSAnLi4vVGV4dElucHV0LmpzJ1xuaW1wb3J0IHsgVGhpbmtpbmdUb2dnbGUgfSBmcm9tICcuLi9UaGlua2luZ1RvZ2dsZS5qcydcbmltcG9ydCB7IEJhY2tncm91bmRUYXNrc0RpYWxvZyB9IGZyb20gJy4uL3Rhc2tzL0JhY2tncm91bmRUYXNrc0RpYWxvZy5qcydcbmltcG9ydCB7IHNob3VsZEhpZGVUYXNrc0Zvb3RlciB9IGZyb20gJy4uL3Rhc2tzL3Rhc2tTdGF0dXNVdGlscy5qcydcbmltcG9ydCB7IFRlYW1zRGlhbG9nIH0gZnJvbSAnLi4vdGVhbXMvVGVhbXNEaWFsb2cuanMnXG5pbXBvcnQgVmltVGV4dElucHV0IGZyb20gJy4uL1ZpbVRleHRJbnB1dC5qcydcbmltcG9ydCB7IGdldE1vZGVGcm9tSW5wdXQsIGdldFZhbHVlRnJvbUlucHV0IH0gZnJvbSAnLi9pbnB1dE1vZGVzLmpzJ1xuaW1wb3J0IHtcbiAgRk9PVEVSX1RFTVBPUkFSWV9TVEFUVVNfVElNRU9VVCxcbiAgTm90aWZpY2F0aW9ucyxcbn0gZnJvbSAnLi9Ob3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IFByb21wdElucHV0Rm9vdGVyIGZyb20gJy4vUHJvbXB0SW5wdXRGb290ZXIuanMnXG5pbXBvcnQgdHlwZSB7IFN1Z2dlc3Rpb25JdGVtIH0gZnJvbSAnLi9Qcm9tcHRJbnB1dEZvb3RlclN1Z2dlc3Rpb25zLmpzJ1xuaW1wb3J0IHsgUHJvbXB0SW5wdXRNb2RlSW5kaWNhdG9yIH0gZnJvbSAnLi9Qcm9tcHRJbnB1dE1vZGVJbmRpY2F0b3IuanMnXG5pbXBvcnQgeyBQcm9tcHRJbnB1dFF1ZXVlZENvbW1hbmRzIH0gZnJvbSAnLi9Qcm9tcHRJbnB1dFF1ZXVlZENvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgUHJvbXB0SW5wdXRTdGFzaE5vdGljZSB9IGZyb20gJy4vUHJvbXB0SW5wdXRTdGFzaE5vdGljZS5qcydcbmltcG9ydCB7IHVzZU1heWJlVHJ1bmNhdGVJbnB1dCB9IGZyb20gJy4vdXNlTWF5YmVUcnVuY2F0ZUlucHV0LmpzJ1xuaW1wb3J0IHsgdXNlUHJvbXB0SW5wdXRQbGFjZWhvbGRlciB9IGZyb20gJy4vdXNlUHJvbXB0SW5wdXRQbGFjZWhvbGRlci5qcydcbmltcG9ydCB7IHVzZVNob3dGYXN0SWNvbkhpbnQgfSBmcm9tICcuL3VzZVNob3dGYXN0SWNvbkhpbnQuanMnXG5pbXBvcnQgeyB1c2VTd2FybUJhbm5lciB9IGZyb20gJy4vdXNlU3dhcm1CYW5uZXIuanMnXG5pbXBvcnQgeyBpc05vblNwYWNlUHJpbnRhYmxlLCBpc1ZpbU1vZGVFbmFibGVkIH0gZnJvbSAnLi91dGlscy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgZGVidWc6IGJvb2xlYW5cbiAgaWRlU2VsZWN0aW9uOiBJREVTZWxlY3Rpb24gfCB1bmRlZmluZWRcbiAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHRcbiAgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0OiAoY3R4OiBUb29sUGVybWlzc2lvbkNvbnRleHQpID0+IHZvaWRcbiAgYXBpS2V5U3RhdHVzOiBWZXJpZmljYXRpb25TdGF0dXNcbiAgY29tbWFuZHM6IENvbW1hbmRbXVxuICBhZ2VudHM6IEFnZW50RGVmaW5pdGlvbltdXG4gIGlzTG9hZGluZzogYm9vbGVhblxuICB2ZXJib3NlOiBib29sZWFuXG4gIG1lc3NhZ2VzOiBNZXNzYWdlW11cbiAgb25BdXRvVXBkYXRlclJlc3VsdDogKHJlc3VsdDogQXV0b1VwZGF0ZXJSZXN1bHQpID0+IHZvaWRcbiAgYXV0b1VwZGF0ZXJSZXN1bHQ6IEF1dG9VcGRhdGVyUmVzdWx0IHwgbnVsbFxuICBpbnB1dDogc3RyaW5nXG4gIG9uSW5wdXRDaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG4gIG1vZGU6IFByb21wdElucHV0TW9kZVxuICBvbk1vZGVDaGFuZ2U6IChtb2RlOiBQcm9tcHRJbnB1dE1vZGUpID0+IHZvaWRcbiAgc3Rhc2hlZFByb21wdDpcbiAgICB8IHtcbiAgICAgICAgdGV4dDogc3RyaW5nXG4gICAgICAgIGN1cnNvck9mZnNldDogbnVtYmVyXG4gICAgICAgIHBhc3RlZENvbnRlbnRzOiBSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50PlxuICAgICAgfVxuICAgIHwgdW5kZWZpbmVkXG4gIHNldFN0YXNoZWRQcm9tcHQ6IChcbiAgICB2YWx1ZTpcbiAgICAgIHwge1xuICAgICAgICAgIHRleHQ6IHN0cmluZ1xuICAgICAgICAgIGN1cnNvck9mZnNldDogbnVtYmVyXG4gICAgICAgICAgcGFzdGVkQ29udGVudHM6IFJlY29yZDxudW1iZXIsIFBhc3RlZENvbnRlbnQ+XG4gICAgICAgIH1cbiAgICAgIHwgdW5kZWZpbmVkLFxuICApID0+IHZvaWRcbiAgc3VibWl0Q291bnQ6IG51bWJlclxuICBvblNob3dNZXNzYWdlU2VsZWN0b3I6ICgpID0+IHZvaWRcbiAgLyoqIEZ1bGxzY3JlZW4gbWVzc2FnZSBhY3Rpb25zOiBzaGlmdCvihpEgZW50ZXJzIGN1cnNvci4gKi9cbiAgb25NZXNzYWdlQWN0aW9uc0VudGVyPzogKCkgPT4gdm9pZFxuICBtY3BDbGllbnRzOiBNQ1BTZXJ2ZXJDb25uZWN0aW9uW11cbiAgcGFzdGVkQ29udGVudHM6IFJlY29yZDxudW1iZXIsIFBhc3RlZENvbnRlbnQ+XG4gIHNldFBhc3RlZENvbnRlbnRzOiBSZWFjdC5EaXNwYXRjaDxcbiAgICBSZWFjdC5TZXRTdGF0ZUFjdGlvbjxSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50Pj5cbiAgPlxuICB2aW1Nb2RlOiBWaW1Nb2RlXG4gIHNldFZpbU1vZGU6IChtb2RlOiBWaW1Nb2RlKSA9PiB2b2lkXG4gIHNob3dCYXNoZXNEaWFsb2c6IHN0cmluZyB8IGJvb2xlYW5cbiAgc2V0U2hvd0Jhc2hlc0RpYWxvZzogKHNob3c6IHN0cmluZyB8IGJvb2xlYW4pID0+IHZvaWRcbiAgb25FeGl0OiAoKSA9PiB2b2lkXG4gIGdldFRvb2xVc2VDb250ZXh0OiAoXG4gICAgbWVzc2FnZXM6IE1lc3NhZ2VbXSxcbiAgICBuZXdNZXNzYWdlczogTWVzc2FnZVtdLFxuICAgIGFib3J0Q29udHJvbGxlcjogQWJvcnRDb250cm9sbGVyLFxuICAgIG1haW5Mb29wTW9kZWw6IHN0cmluZyxcbiAgKSA9PiBQcm9jZXNzVXNlcklucHV0Q29udGV4dFxuICBvblN1Ym1pdDogKFxuICAgIGlucHV0OiBzdHJpbmcsXG4gICAgaGVscGVyczogUHJvbXB0SW5wdXRIZWxwZXJzLFxuICAgIHNwZWN1bGF0aW9uQWNjZXB0Pzoge1xuICAgICAgc3RhdGU6IEFjdGl2ZVNwZWN1bGF0aW9uU3RhdGVcbiAgICAgIHNwZWN1bGF0aW9uU2Vzc2lvblRpbWVTYXZlZE1zOiBudW1iZXJcbiAgICAgIHNldEFwcFN0YXRlOiAoZjogKHByZXY6IEFwcFN0YXRlKSA9PiBBcHBTdGF0ZSkgPT4gdm9pZFxuICAgIH0sXG4gICAgb3B0aW9ucz86IHsgZnJvbUtleWJpbmRpbmc/OiBib29sZWFuIH0sXG4gICkgPT4gUHJvbWlzZTx2b2lkPlxuICBvbkFnZW50U3VibWl0PzogKFxuICAgIGlucHV0OiBzdHJpbmcsXG4gICAgdGFzazogSW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGUgfCBMb2NhbEFnZW50VGFza1N0YXRlLFxuICAgIGhlbHBlcnM6IFByb21wdElucHV0SGVscGVycyxcbiAgKSA9PiBQcm9taXNlPHZvaWQ+XG4gIGlzU2VhcmNoaW5nSGlzdG9yeTogYm9vbGVhblxuICBzZXRJc1NlYXJjaGluZ0hpc3Rvcnk6IChpc1NlYXJjaGluZzogYm9vbGVhbikgPT4gdm9pZFxuICBvbkRpc21pc3NTaWRlUXVlc3Rpb24/OiAoKSA9PiB2b2lkXG4gIGlzU2lkZVF1ZXN0aW9uVmlzaWJsZT86IGJvb2xlYW5cbiAgaGVscE9wZW46IGJvb2xlYW5cbiAgc2V0SGVscE9wZW46IFJlYWN0LkRpc3BhdGNoPFJlYWN0LlNldFN0YXRlQWN0aW9uPGJvb2xlYW4+PlxuICBoYXNTdXBwcmVzc2VkRGlhbG9ncz86IGJvb2xlYW5cbiAgaXNMb2NhbEpTWENvbW1hbmRBY3RpdmU/OiBib29sZWFuXG4gIGluc2VydFRleHRSZWY/OiBSZWFjdC5NdXRhYmxlUmVmT2JqZWN0PHtcbiAgICBpbnNlcnQ6ICh0ZXh0OiBzdHJpbmcpID0+IHZvaWRcbiAgICBzZXRJbnB1dFdpdGhDdXJzb3I6ICh2YWx1ZTogc3RyaW5nLCBjdXJzb3I6IG51bWJlcikgPT4gdm9pZFxuICAgIGN1cnNvck9mZnNldDogbnVtYmVyXG4gIH0gfCBudWxsPlxuICB2b2ljZUludGVyaW1SYW5nZT86IHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfSB8IG51bGxcbn1cblxuLy8gQm90dG9tIHNsb3QgaGFzIG1heEhlaWdodD1cIjUwJVwiOyByZXNlcnZlIGxpbmVzIGZvciBmb290ZXIsIGJvcmRlciwgc3RhdHVzLlxuY29uc3QgUFJPTVBUX0ZPT1RFUl9MSU5FUyA9IDVcbmNvbnN0IE1JTl9JTlBVVF9WSUVXUE9SVF9MSU5FUyA9IDNcblxuZnVuY3Rpb24gUHJvbXB0SW5wdXQoe1xuICBkZWJ1ZyxcbiAgaWRlU2VsZWN0aW9uLFxuICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgYXBpS2V5U3RhdHVzLFxuICBjb21tYW5kcyxcbiAgYWdlbnRzLFxuICBpc0xvYWRpbmcsXG4gIHZlcmJvc2UsXG4gIG1lc3NhZ2VzLFxuICBvbkF1dG9VcGRhdGVyUmVzdWx0LFxuICBhdXRvVXBkYXRlclJlc3VsdCxcbiAgaW5wdXQsXG4gIG9uSW5wdXRDaGFuZ2UsXG4gIG1vZGUsXG4gIG9uTW9kZUNoYW5nZSxcbiAgc3Rhc2hlZFByb21wdCxcbiAgc2V0U3Rhc2hlZFByb21wdCxcbiAgc3VibWl0Q291bnQsXG4gIG9uU2hvd01lc3NhZ2VTZWxlY3RvcixcbiAgb25NZXNzYWdlQWN0aW9uc0VudGVyLFxuICBtY3BDbGllbnRzLFxuICBwYXN0ZWRDb250ZW50cyxcbiAgc2V0UGFzdGVkQ29udGVudHMsXG4gIHZpbU1vZGUsXG4gIHNldFZpbU1vZGUsXG4gIHNob3dCYXNoZXNEaWFsb2csXG4gIHNldFNob3dCYXNoZXNEaWFsb2csXG4gIG9uRXhpdCxcbiAgZ2V0VG9vbFVzZUNvbnRleHQsXG4gIG9uU3VibWl0OiBvblN1Ym1pdFByb3AsXG4gIG9uQWdlbnRTdWJtaXQsXG4gIGlzU2VhcmNoaW5nSGlzdG9yeSxcbiAgc2V0SXNTZWFyY2hpbmdIaXN0b3J5LFxuICBvbkRpc21pc3NTaWRlUXVlc3Rpb24sXG4gIGlzU2lkZVF1ZXN0aW9uVmlzaWJsZSxcbiAgaGVscE9wZW4sXG4gIHNldEhlbHBPcGVuLFxuICBoYXNTdXBwcmVzc2VkRGlhbG9ncyxcbiAgaXNMb2NhbEpTWENvbW1hbmRBY3RpdmUgPSBmYWxzZSxcbiAgaW5zZXJ0VGV4dFJlZixcbiAgdm9pY2VJbnRlcmltUmFuZ2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IG1haW5Mb29wTW9kZWwgPSB1c2VNYWluTG9vcE1vZGVsKClcbiAgLy8gQSBsb2NhbC1qc3ggY29tbWFuZCAoZS5nLiwgL21jcCB3aGlsZSBhZ2VudCBpcyBydW5uaW5nKSByZW5kZXJzIGEgZnVsbC1cbiAgLy8gc2NyZWVuIGRpYWxvZyBvbiB0b3Agb2YgUHJvbXB0SW5wdXQgdmlhIHRoZSBpbW1lZGlhdGUtY29tbWFuZCBwYXRoIHdpdGhcbiAgLy8gc2hvdWxkSGlkZVByb21wdElucHV0OiBmYWxzZS4gVGhvc2UgZGlhbG9ncyBkb24ndCByZWdpc3RlciBpbiB0aGUgb3ZlcmxheVxuICAvLyBzeXN0ZW0sIHNvIHRyZWF0IHRoZW0gYXMgYSBtb2RhbCBvdmVybGF5IGhlcmUgdG8gc3RvcCBuYXZpZ2F0aW9uIGtleXMgZnJvbVxuICAvLyBsZWFraW5nIGludG8gVGV4dElucHV0L2Zvb3RlciBoYW5kbGVycyBhbmQgc3RhY2tpbmcgYSBzZWNvbmQgZGlhbG9nLlxuICBjb25zdCBpc01vZGFsT3ZlcmxheUFjdGl2ZSA9XG4gICAgdXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUoKSB8fCBpc0xvY2FsSlNYQ29tbWFuZEFjdGl2ZVxuICBjb25zdCBbaXNBdXRvVXBkYXRpbmcsIHNldElzQXV0b1VwZGF0aW5nXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbZXhpdE1lc3NhZ2UsIHNldEV4aXRNZXNzYWdlXSA9IHVzZVN0YXRlPHtcbiAgICBzaG93OiBib29sZWFuXG4gICAga2V5Pzogc3RyaW5nXG4gIH0+KHsgc2hvdzogZmFsc2UgfSlcbiAgY29uc3QgW2N1cnNvck9mZnNldCwgc2V0Q3Vyc29yT2Zmc2V0XSA9IHVzZVN0YXRlPG51bWJlcj4oaW5wdXQubGVuZ3RoKVxuICAvLyBUcmFjayB0aGUgbGFzdCBpbnB1dCB2YWx1ZSBzZXQgdmlhIGludGVybmFsIGhhbmRsZXJzIHNvIHdlIGNhbiBkZXRlY3RcbiAgLy8gZXh0ZXJuYWwgaW5wdXQgY2hhbmdlcyAoZS5nLiBzcGVlY2gtdG8tdGV4dCBpbmplY3Rpb24pIGFuZCBtb3ZlIGN1cnNvciB0byBlbmQuXG4gIGNvbnN0IGxhc3RJbnRlcm5hbElucHV0UmVmID0gUmVhY3QudXNlUmVmKGlucHV0KVxuICBpZiAoaW5wdXQgIT09IGxhc3RJbnRlcm5hbElucHV0UmVmLmN1cnJlbnQpIHtcbiAgICAvLyBJbnB1dCBjaGFuZ2VkIGV4dGVybmFsbHkgKG5vdCB0aHJvdWdoIGFueSBpbnRlcm5hbCBoYW5kbGVyKSDigJQgbW92ZSBjdXJzb3IgdG8gZW5kXG4gICAgc2V0Q3Vyc29yT2Zmc2V0KGlucHV0Lmxlbmd0aClcbiAgICBsYXN0SW50ZXJuYWxJbnB1dFJlZi5jdXJyZW50ID0gaW5wdXRcbiAgfVxuICAvLyBXcmFwIG9uSW5wdXRDaGFuZ2UgdG8gdHJhY2sgaW50ZXJuYWwgY2hhbmdlcyBiZWZvcmUgdGhleSB0cmlnZ2VyIHJlLXJlbmRlclxuICBjb25zdCB0cmFja0FuZFNldElucHV0ID0gUmVhY3QudXNlQ2FsbGJhY2soXG4gICAgKHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICAgIGxhc3RJbnRlcm5hbElucHV0UmVmLmN1cnJlbnQgPSB2YWx1ZVxuICAgICAgb25JbnB1dENoYW5nZSh2YWx1ZSlcbiAgICB9LFxuICAgIFtvbklucHV0Q2hhbmdlXSxcbiAgKVxuICAvLyBFeHBvc2UgYW4gaW5zZXJ0VGV4dCBmdW5jdGlvbiBzbyBjYWxsZXJzIChlLmcuIFNUVCkgY2FuIHNwbGljZSB0ZXh0IGF0IHRoZVxuICAvLyBjdXJyZW50IGN1cnNvciBwb3NpdGlvbiBpbnN0ZWFkIG9mIHJlcGxhY2luZyB0aGUgZW50aXJlIGlucHV0LlxuICBpZiAoaW5zZXJ0VGV4dFJlZikge1xuICAgIGluc2VydFRleHRSZWYuY3VycmVudCA9IHtcbiAgICAgIGN1cnNvck9mZnNldCxcbiAgICAgIGluc2VydDogKHRleHQ6IHN0cmluZykgPT4ge1xuICAgICAgICBjb25zdCBuZWVkc1NwYWNlID1cbiAgICAgICAgICBjdXJzb3JPZmZzZXQgPT09IGlucHV0Lmxlbmd0aCAmJlxuICAgICAgICAgIGlucHV0Lmxlbmd0aCA+IDAgJiZcbiAgICAgICAgICAhL1xccyQvLnRlc3QoaW5wdXQpXG4gICAgICAgIGNvbnN0IGluc2VydFRleHQgPSBuZWVkc1NwYWNlID8gJyAnICsgdGV4dCA6IHRleHRcbiAgICAgICAgY29uc3QgbmV3VmFsdWUgPVxuICAgICAgICAgIGlucHV0LnNsaWNlKDAsIGN1cnNvck9mZnNldCkgKyBpbnNlcnRUZXh0ICsgaW5wdXQuc2xpY2UoY3Vyc29yT2Zmc2V0KVxuICAgICAgICBsYXN0SW50ZXJuYWxJbnB1dFJlZi5jdXJyZW50ID0gbmV3VmFsdWVcbiAgICAgICAgb25JbnB1dENoYW5nZShuZXdWYWx1ZSlcbiAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KGN1cnNvck9mZnNldCArIGluc2VydFRleHQubGVuZ3RoKVxuICAgICAgfSxcbiAgICAgIHNldElucHV0V2l0aEN1cnNvcjogKHZhbHVlOiBzdHJpbmcsIGN1cnNvcjogbnVtYmVyKSA9PiB7XG4gICAgICAgIGxhc3RJbnRlcm5hbElucHV0UmVmLmN1cnJlbnQgPSB2YWx1ZVxuICAgICAgICBvbklucHV0Q2hhbmdlKHZhbHVlKVxuICAgICAgICBzZXRDdXJzb3JPZmZzZXQoY3Vyc29yKVxuICAgICAgfSxcbiAgICB9XG4gIH1cbiAgY29uc3Qgc3RvcmUgPSB1c2VBcHBTdGF0ZVN0b3JlKClcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IHRhc2tzID0gdXNlQXBwU3RhdGUocyA9PiBzLnRhc2tzKVxuICBjb25zdCByZXBsQnJpZGdlQ29ubmVjdGVkID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VDb25uZWN0ZWQpXG4gIGNvbnN0IHJlcGxCcmlkZ2VFeHBsaWNpdCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlRXhwbGljaXQpXG4gIGNvbnN0IHJlcGxCcmlkZ2VSZWNvbm5lY3RpbmcgPSB1c2VBcHBTdGF0ZShzID0+IHMucmVwbEJyaWRnZVJlY29ubmVjdGluZylcbiAgLy8gTXVzdCBtYXRjaCBCcmlkZ2VTdGF0dXNJbmRpY2F0b3IncyByZW5kZXIgY29uZGl0aW9uIChQcm9tcHRJbnB1dEZvb3Rlci50c3gpIOKAlFxuICAvLyB0aGUgcGlsbCByZXR1cm5zIG51bGwgZm9yIGltcGxpY2l0LWFuZC1ub3QtcmVjb25uZWN0aW5nLCBzbyBuYXYgbXVzdCB0b28sXG4gIC8vIG90aGVyd2lzZSBicmlkZ2UgYmVjb21lcyBhbiBpbnZpc2libGUgc2VsZWN0aW9uIHN0b3AuXG4gIGNvbnN0IGJyaWRnZUZvb3RlclZpc2libGUgPVxuICAgIHJlcGxCcmlkZ2VDb25uZWN0ZWQgJiYgKHJlcGxCcmlkZ2VFeHBsaWNpdCB8fCByZXBsQnJpZGdlUmVjb25uZWN0aW5nKVxuICAvLyBUbXV4IHBpbGwgKGFudC1vbmx5KSDigJQgdmlzaWJsZSB3aGVuIHRoZXJlJ3MgYW4gYWN0aXZlIHR1bmdzdGVuIHNlc3Npb25cbiAgY29uc3QgaGFzVHVuZ3N0ZW5TZXNzaW9uID0gdXNlQXBwU3RhdGUoXG4gICAgcyA9PlxuICAgICAgXCJleHRlcm5hbFwiID09PSAnYW50JyAmJiBzLnR1bmdzdGVuQWN0aXZlU2Vzc2lvbiAhPT0gdW5kZWZpbmVkLFxuICApXG4gIGNvbnN0IHRtdXhGb290ZXJWaXNpYmxlID1cbiAgICBcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIGhhc1R1bmdzdGVuU2Vzc2lvblxuICAvLyBXZWJCcm93c2VyIHBpbGwg4oCUIHZpc2libGUgd2hlbiBhIGJyb3dzZXIgaXMgb3BlblxuICBjb25zdCBiYWdlbEZvb3RlclZpc2libGUgPSB1c2VBcHBTdGF0ZShzID0+XG4gICAgICAgIGZhbHNlLFxuICApXG4gIGNvbnN0IHRlYW1Db250ZXh0ID0gdXNlQXBwU3RhdGUocyA9PiBzLnRlYW1Db250ZXh0KVxuICBjb25zdCBxdWV1ZWRDb21tYW5kcyA9IHVzZUNvbW1hbmRRdWV1ZSgpXG4gIGNvbnN0IHByb21wdFN1Z2dlc3Rpb25TdGF0ZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5wcm9tcHRTdWdnZXN0aW9uKVxuICBjb25zdCBzcGVjdWxhdGlvbiA9IHVzZUFwcFN0YXRlKHMgPT4gcy5zcGVjdWxhdGlvbilcbiAgY29uc3Qgc3BlY3VsYXRpb25TZXNzaW9uVGltZVNhdmVkTXMgPSB1c2VBcHBTdGF0ZShcbiAgICBzID0+IHMuc3BlY3VsYXRpb25TZXNzaW9uVGltZVNhdmVkTXMsXG4gIClcbiAgY29uc3Qgdmlld2luZ0FnZW50VGFza0lkID0gdXNlQXBwU3RhdGUocyA9PiBzLnZpZXdpbmdBZ2VudFRhc2tJZClcbiAgY29uc3Qgdmlld1NlbGVjdGlvbk1vZGUgPSB1c2VBcHBTdGF0ZShzID0+IHMudmlld1NlbGVjdGlvbk1vZGUpXG4gIGNvbnN0IHNob3dTcGlubmVyVHJlZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5leHBhbmRlZFZpZXcpID09PSAndGVhbW1hdGVzJ1xuICBjb25zdCB7IGNvbXBhbmlvbjogX2NvbXBhbmlvbiwgY29tcGFuaW9uTXV0ZWQgfSA9IGZlYXR1cmUoJ0JVRERZJylcbiAgICA/IGdldEdsb2JhbENvbmZpZygpXG4gICAgOiB7IGNvbXBhbmlvbjogdW5kZWZpbmVkLCBjb21wYW5pb25NdXRlZDogdW5kZWZpbmVkIH1cbiAgY29uc3QgY29tcGFuaW9uRm9vdGVyVmlzaWJsZSA9ICEhX2NvbXBhbmlvbiAmJiAhY29tcGFuaW9uTXV0ZWRcbiAgLy8gQnJpZWYgbW9kZTogQnJpZWZTcGlubmVyL0JyaWVmSWRsZVN0YXR1cyBvd24gdGhlIDItcm93IGZvb3RwcmludCBhYm92ZVxuICAvLyB0aGUgaW5wdXQuIERyb3BwaW5nIG1hcmdpblRvcCBoZXJlIGxldHMgdGhlIHNwaW5uZXIgc2l0IGZsdXNoIGFnYWluc3RcbiAgLy8gdGhlIGlucHV0IGJhci4gdmlld2luZ0FnZW50VGFza0lkIG1pcnJvcnMgdGhlIGdhdGUgb24gYm90aCAoU3Bpbm5lci50c3gsXG4gIC8vIFJFUEwudHN4KSDigJQgdGVhbW1hdGUgdmlldyBmYWxscyBiYWNrIHRvIFNwaW5uZXJXaXRoVmVyYklubmVyIHdoaWNoIGhhc1xuICAvLyBpdHMgb3duIG1hcmdpblRvcCwgc28gdGhlIGdhcCBzdGF5cyBldmVuIHdpdGhvdXQgb3Vycy5cbiAgY29uc3QgYnJpZWZPd25zR2FwID1cbiAgICBmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKVxuICAgICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICAgIHVzZUFwcFN0YXRlKHMgPT4gcy5pc0JyaWVmT25seSkgJiYgIXZpZXdpbmdBZ2VudFRhc2tJZFxuICAgICAgOiBmYWxzZVxuICBjb25zdCBtYWluTG9vcE1vZGVsXyA9IHVzZUFwcFN0YXRlKHMgPT4gcy5tYWluTG9vcE1vZGVsKVxuICBjb25zdCBtYWluTG9vcE1vZGVsRm9yU2Vzc2lvbiA9IHVzZUFwcFN0YXRlKHMgPT4gcy5tYWluTG9vcE1vZGVsRm9yU2Vzc2lvbilcbiAgY29uc3QgdGhpbmtpbmdFbmFibGVkID0gdXNlQXBwU3RhdGUocyA9PiBzLnRoaW5raW5nRW5hYmxlZClcbiAgY29uc3QgaXNGYXN0TW9kZSA9IHVzZUFwcFN0YXRlKHMgPT5cbiAgICBpc0Zhc3RNb2RlRW5hYmxlZCgpID8gcy5mYXN0TW9kZSA6IGZhbHNlLFxuICApXG4gIGNvbnN0IGVmZm9ydFZhbHVlID0gdXNlQXBwU3RhdGUocyA9PiBzLmVmZm9ydFZhbHVlKVxuICBjb25zdCB2aWV3ZWRUZWFtbWF0ZSA9IGdldFZpZXdlZFRlYW1tYXRlVGFzayhzdG9yZS5nZXRTdGF0ZSgpKVxuICBjb25zdCB2aWV3aW5nQWdlbnROYW1lID0gdmlld2VkVGVhbW1hdGU/LmlkZW50aXR5LmFnZW50TmFtZVxuICAvLyBpZGVudGl0eS5jb2xvciBpcyB0eXBlZCBhcyBgc3RyaW5nIHwgdW5kZWZpbmVkYCAobm90IEFnZW50Q29sb3JOYW1lKSBiZWNhdXNlXG4gIC8vIHRlYW1tYXRlIGlkZW50aXR5IGNvbWVzIGZyb20gZmlsZS1iYXNlZCBjb25maWcuIFZhbGlkYXRlIGJlZm9yZSBjYXN0aW5nIHRvXG4gIC8vIGVuc3VyZSB3ZSBvbmx5IHVzZSB2YWxpZCBjb2xvciBuYW1lcyAoZmFsbHMgYmFjayB0byBjeWFuIGlmIGludmFsaWQpLlxuICBjb25zdCB2aWV3aW5nQWdlbnRDb2xvciA9XG4gICAgdmlld2VkVGVhbW1hdGU/LmlkZW50aXR5LmNvbG9yICYmXG4gICAgQUdFTlRfQ09MT1JTLmluY2x1ZGVzKHZpZXdlZFRlYW1tYXRlLmlkZW50aXR5LmNvbG9yIGFzIEFnZW50Q29sb3JOYW1lKVxuICAgICAgPyAodmlld2VkVGVhbW1hdGUuaWRlbnRpdHkuY29sb3IgYXMgQWdlbnRDb2xvck5hbWUpXG4gICAgICA6IHVuZGVmaW5lZFxuICAvLyBJbi1wcm9jZXNzIHRlYW1tYXRlcyBzb3J0ZWQgYWxwaGFiZXRpY2FsbHkgZm9yIGZvb3RlciB0ZWFtIHNlbGVjdG9yXG4gIGNvbnN0IGluUHJvY2Vzc1RlYW1tYXRlcyA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZ2V0UnVubmluZ1RlYW1tYXRlc1NvcnRlZCh0YXNrcyksXG4gICAgW3Rhc2tzXSxcbiAgKVxuXG4gIC8vIFRlYW0gbW9kZTogYWxsIGJhY2tncm91bmQgdGFza3MgYXJlIGluLXByb2Nlc3MgdGVhbW1hdGVzXG4gIGNvbnN0IGlzVGVhbW1hdGVNb2RlID1cbiAgICBpblByb2Nlc3NUZWFtbWF0ZXMubGVuZ3RoID4gMCB8fCB2aWV3ZWRUZWFtbWF0ZSAhPT0gdW5kZWZpbmVkXG5cbiAgLy8gV2hlbiB2aWV3aW5nIGEgdGVhbW1hdGUsIHNob3cgdGhlaXIgcGVybWlzc2lvbiBtb2RlIGluIHRoZSBmb290ZXIgaW5zdGVhZCBvZiB0aGUgbGVhZGVyJ3NcbiAgY29uc3QgZWZmZWN0aXZlVG9vbFBlcm1pc3Npb25Db250ZXh0ID0gdXNlTWVtbygoKTogVG9vbFBlcm1pc3Npb25Db250ZXh0ID0+IHtcbiAgICBpZiAodmlld2VkVGVhbW1hdGUpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgbW9kZTogdmlld2VkVGVhbW1hdGUucGVybWlzc2lvbk1vZGUsXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0b29sUGVybWlzc2lvbkNvbnRleHRcbiAgfSwgW3ZpZXdlZFRlYW1tYXRlLCB0b29sUGVybWlzc2lvbkNvbnRleHRdKVxuICBjb25zdCB7IGhpc3RvcnlRdWVyeSwgc2V0SGlzdG9yeVF1ZXJ5LCBoaXN0b3J5TWF0Y2gsIGhpc3RvcnlGYWlsZWRNYXRjaCB9ID1cbiAgICB1c2VIaXN0b3J5U2VhcmNoKFxuICAgICAgZW50cnkgPT4ge1xuICAgICAgICBzZXRQYXN0ZWRDb250ZW50cyhlbnRyeS5wYXN0ZWRDb250ZW50cylcbiAgICAgICAgdm9pZCBvblN1Ym1pdChlbnRyeS5kaXNwbGF5KVxuICAgICAgfSxcbiAgICAgIGlucHV0LFxuICAgICAgdHJhY2tBbmRTZXRJbnB1dCxcbiAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgIGN1cnNvck9mZnNldCxcbiAgICAgIG9uTW9kZUNoYW5nZSxcbiAgICAgIG1vZGUsXG4gICAgICBpc1NlYXJjaGluZ0hpc3RvcnksXG4gICAgICBzZXRJc1NlYXJjaGluZ0hpc3RvcnksXG4gICAgICBzZXRQYXN0ZWRDb250ZW50cyxcbiAgICAgIHBhc3RlZENvbnRlbnRzLFxuICAgIClcbiAgLy8gQ291bnRlciBmb3IgcGFzdGUgSURzIChzaGFyZWQgYmV0d2VlbiBpbWFnZXMgYW5kIHRleHQpLlxuICAvLyBDb21wdXRlIGluaXRpYWwgdmFsdWUgb25jZSBmcm9tIGV4aXN0aW5nIG1lc3NhZ2VzIChmb3IgLS1jb250aW51ZS8tLXJlc3VtZSkuXG4gIC8vIHVzZVJlZihmbigpKSBldmFsdWF0ZXMgZm4oKSBvbiBldmVyeSByZW5kZXIgYW5kIGRpc2NhcmRzIHRoZSByZXN1bHQgYWZ0ZXJcbiAgLy8gbW91bnQg4oCUIGdldEluaXRpYWxQYXN0ZUlkIHdhbGtzIGFsbCBtZXNzYWdlcyArIHJlZ2V4LXNjYW5zIHRleHQgYmxvY2tzLFxuICAvLyBzbyBndWFyZCB3aXRoIGEgbGF6eS1pbml0IHBhdHRlcm4gdG8gcnVuIGl0IGV4YWN0bHkgb25jZS5cbiAgY29uc3QgbmV4dFBhc3RlSWRSZWYgPSB1c2VSZWYoLTEpXG4gIGlmIChuZXh0UGFzdGVJZFJlZi5jdXJyZW50ID09PSAtMSkge1xuICAgIG5leHRQYXN0ZUlkUmVmLmN1cnJlbnQgPSBnZXRJbml0aWFsUGFzdGVJZChtZXNzYWdlcylcbiAgfVxuICAvLyBBcm1lZCBieSBvbkltYWdlUGFzdGU7IGlmIHRoZSB2ZXJ5IG5leHQga2V5c3Ryb2tlIGlzIGEgbm9uLXNwYWNlXG4gIC8vIHByaW50YWJsZSwgaW5wdXRGaWx0ZXIgcHJlcGVuZHMgYSBzcGFjZSBiZWZvcmUgaXQuIEFueSBvdGhlciBpbnB1dFxuICAvLyAoYXJyb3csIGVzY2FwZSwgYmFja3NwYWNlLCBwYXN0ZSwgc3BhY2UpIGRpc2FybXMgd2l0aG91dCBpbnNlcnRpbmcuXG4gIGNvbnN0IHBlbmRpbmdTcGFjZUFmdGVyUGlsbFJlZiA9IHVzZVJlZihmYWxzZSlcblxuICBjb25zdCBbc2hvd1RlYW1zRGlhbG9nLCBzZXRTaG93VGVhbXNEaWFsb2ddID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtzaG93QnJpZGdlRGlhbG9nLCBzZXRTaG93QnJpZGdlRGlhbG9nXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbdGVhbW1hdGVGb290ZXJJbmRleCwgc2V0VGVhbW1hdGVGb290ZXJJbmRleF0gPSB1c2VTdGF0ZSgwKVxuICAvLyAtMSBzZW50aW5lbDogdGFza3MgcGlsbCBpcyBzZWxlY3RlZCBidXQgbm8gc3BlY2lmaWMgYWdlbnQgcm93IGlzIHNlbGVjdGVkIHlldC5cbiAgLy8gRmlyc3Qg4oaTIHNlbGVjdHMgdGhlIHBpbGwsIHNlY29uZCDihpMgbW92ZXMgdG8gcm93IDAuIFByZXZlbnRzIGRvdWJsZS1zZWxlY3RcbiAgLy8gb2YgcGlsbCArIHJvdyB3aGVuIGJvdGggYmcgdGFza3MgKHBpbGwpIGFuZCBmb3JrZWQgYWdlbnRzIChyb3dzKSBhcmUgdmlzaWJsZS5cbiAgY29uc3QgY29vcmRpbmF0b3JUYXNrSW5kZXggPSB1c2VBcHBTdGF0ZShzID0+IHMuY29vcmRpbmF0b3JUYXNrSW5kZXgpXG4gIGNvbnN0IHNldENvb3JkaW5hdG9yVGFza0luZGV4ID0gdXNlQ2FsbGJhY2soXG4gICAgKHY6IG51bWJlciB8ICgocHJldjogbnVtYmVyKSA9PiBudW1iZXIpKSA9PlxuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICAgIGNvbnN0IG5leHQgPSB0eXBlb2YgdiA9PT0gJ2Z1bmN0aW9uJyA/IHYocHJldi5jb29yZGluYXRvclRhc2tJbmRleCkgOiB2XG4gICAgICAgIGlmIChuZXh0ID09PSBwcmV2LmNvb3JkaW5hdG9yVGFza0luZGV4KSByZXR1cm4gcHJldlxuICAgICAgICByZXR1cm4geyAuLi5wcmV2LCBjb29yZGluYXRvclRhc2tJbmRleDogbmV4dCB9XG4gICAgICB9KSxcbiAgICBbc2V0QXBwU3RhdGVdLFxuICApXG4gIGNvbnN0IGNvb3JkaW5hdG9yVGFza0NvdW50ID0gdXNlQ29vcmRpbmF0b3JUYXNrQ291bnQoKVxuICAvLyBUaGUgcGlsbCAoQmFja2dyb3VuZFRhc2tTdGF0dXMpIG9ubHkgcmVuZGVycyB3aGVuIG5vbi1sb2NhbF9hZ2VudCBiZyB0YXNrc1xuICAvLyBleGlzdC4gV2hlbiBvbmx5IGxvY2FsX2FnZW50IHRhc2tzIGFyZSBydW5uaW5nIChjb29yZGluYXRvci9mb3JrIG1vZGUpLCB0aGVcbiAgLy8gcGlsbCBpcyBhYnNlbnQsIHNvIHRoZSAtMSBzZW50aW5lbCB3b3VsZCBsZWF2ZSBub3RoaW5nIHZpc3VhbGx5IHNlbGVjdGVkLlxuICAvLyBJbiB0aGF0IGNhc2UsIHNraXAgLTEgYW5kIHRyZWF0IDAgYXMgdGhlIG1pbmltdW0gc2VsZWN0YWJsZSBpbmRleC5cbiAgY29uc3QgaGFzQmdUYXNrUGlsbCA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIE9iamVjdC52YWx1ZXModGFza3MpLnNvbWUoXG4gICAgICAgIHQgPT5cbiAgICAgICAgICBpc0JhY2tncm91bmRUYXNrKHQpICYmXG4gICAgICAgICAgIShcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIGlzUGFuZWxBZ2VudFRhc2sodCkpLFxuICAgICAgKSxcbiAgICBbdGFza3NdLFxuICApXG4gIGNvbnN0IG1pbkNvb3JkaW5hdG9ySW5kZXggPSBoYXNCZ1Rhc2tQaWxsID8gLTEgOiAwXG4gIC8vIENsYW1wIGluZGV4IHdoZW4gdGFza3MgY29tcGxldGUgYW5kIHRoZSBsaXN0IHNocmlua3MgYmVuZWF0aCB0aGUgY3Vyc29yXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGNvb3JkaW5hdG9yVGFza0luZGV4ID49IGNvb3JkaW5hdG9yVGFza0NvdW50KSB7XG4gICAgICBzZXRDb29yZGluYXRvclRhc2tJbmRleChcbiAgICAgICAgTWF0aC5tYXgobWluQ29vcmRpbmF0b3JJbmRleCwgY29vcmRpbmF0b3JUYXNrQ291bnQgLSAxKSxcbiAgICAgIClcbiAgICB9IGVsc2UgaWYgKGNvb3JkaW5hdG9yVGFza0luZGV4IDwgbWluQ29vcmRpbmF0b3JJbmRleCkge1xuICAgICAgc2V0Q29vcmRpbmF0b3JUYXNrSW5kZXgobWluQ29vcmRpbmF0b3JJbmRleClcbiAgICB9XG4gIH0sIFtjb29yZGluYXRvclRhc2tDb3VudCwgY29vcmRpbmF0b3JUYXNrSW5kZXgsIG1pbkNvb3JkaW5hdG9ySW5kZXhdKVxuICBjb25zdCBbaXNQYXN0aW5nLCBzZXRJc1Bhc3RpbmddID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtpc0V4dGVybmFsRWRpdG9yQWN0aXZlLCBzZXRJc0V4dGVybmFsRWRpdG9yQWN0aXZlXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbc2hvd01vZGVsUGlja2VyLCBzZXRTaG93TW9kZWxQaWNrZXJdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtzaG93UXVpY2tPcGVuLCBzZXRTaG93UXVpY2tPcGVuXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbc2hvd0dsb2JhbFNlYXJjaCwgc2V0U2hvd0dsb2JhbFNlYXJjaF0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW3Nob3dIaXN0b3J5UGlja2VyLCBzZXRTaG93SGlzdG9yeVBpY2tlcl0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW3Nob3dGYXN0TW9kZVBpY2tlciwgc2V0U2hvd0Zhc3RNb2RlUGlja2VyXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbc2hvd1RoaW5raW5nVG9nZ2xlLCBzZXRTaG93VGhpbmtpbmdUb2dnbGVdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtzaG93QXV0b01vZGVPcHRJbiwgc2V0U2hvd0F1dG9Nb2RlT3B0SW5dID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtwcmV2aW91c01vZGVCZWZvcmVBdXRvLCBzZXRQcmV2aW91c01vZGVCZWZvcmVBdXRvXSA9XG4gICAgdXNlU3RhdGU8UGVybWlzc2lvbk1vZGUgfCBudWxsPihudWxsKVxuICBjb25zdCBhdXRvTW9kZU9wdEluVGltZW91dFJlZiA9IHVzZVJlZjxOb2RlSlMuVGltZW91dCB8IG51bGw+KG51bGwpXG5cbiAgLy8gQ2hlY2sgaWYgY3Vyc29yIGlzIG9uIHRoZSBmaXJzdCBsaW5lIG9mIGlucHV0XG4gIGNvbnN0IGlzQ3Vyc29yT25GaXJzdExpbmUgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBjb25zdCBmaXJzdE5ld2xpbmVJbmRleCA9IGlucHV0LmluZGV4T2YoJ1xcbicpXG4gICAgaWYgKGZpcnN0TmV3bGluZUluZGV4ID09PSAtMSkge1xuICAgICAgcmV0dXJuIHRydWUgLy8gTm8gbmV3bGluZXMsIGN1cnNvciBpcyBhbHdheXMgb24gZmlyc3QgbGluZVxuICAgIH1cbiAgICByZXR1cm4gY3Vyc29yT2Zmc2V0IDw9IGZpcnN0TmV3bGluZUluZGV4XG4gIH0sIFtpbnB1dCwgY3Vyc29yT2Zmc2V0XSlcblxuICBjb25zdCBpc0N1cnNvck9uTGFzdExpbmUgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBjb25zdCBsYXN0TmV3bGluZUluZGV4ID0gaW5wdXQubGFzdEluZGV4T2YoJ1xcbicpXG4gICAgaWYgKGxhc3ROZXdsaW5lSW5kZXggPT09IC0xKSB7XG4gICAgICByZXR1cm4gdHJ1ZSAvLyBObyBuZXdsaW5lcywgY3Vyc29yIGlzIGFsd2F5cyBvbiBsYXN0IGxpbmVcbiAgICB9XG4gICAgcmV0dXJuIGN1cnNvck9mZnNldCA+IGxhc3ROZXdsaW5lSW5kZXhcbiAgfSwgW2lucHV0LCBjdXJzb3JPZmZzZXRdKVxuXG4gIC8vIERlcml2ZSB0ZWFtIGluZm8gZnJvbSB0ZWFtQ29udGV4dCAobm8gZmlsZXN5c3RlbSBJL08gbmVlZGVkKVxuICAvLyBBIHNlc3Npb24gY2FuIG9ubHkgbGVhZCBvbmUgdGVhbSBhdCBhIHRpbWVcbiAgY29uc3QgY2FjaGVkVGVhbXM6IFRlYW1TdW1tYXJ5W10gPSB1c2VNZW1vKCgpID0+IHtcbiAgICBpZiAoIWlzQWdlbnRTd2FybXNFbmFibGVkKCkpIHJldHVybiBbXVxuICAgIC8vIEluLXByb2Nlc3MgbW9kZSB1c2VzIFNoaWZ0K0Rvd24vVXAgbmF2aWdhdGlvbiBpbnN0ZWFkIG9mIGZvb3RlciBtZW51XG4gICAgaWYgKGlzSW5Qcm9jZXNzRW5hYmxlZCgpKSByZXR1cm4gW11cbiAgICBpZiAoIXRlYW1Db250ZXh0KSB7XG4gICAgICByZXR1cm4gW11cbiAgICB9XG4gICAgY29uc3QgdGVhbW1hdGVDb3VudCA9IGNvdW50KFxuICAgICAgT2JqZWN0LnZhbHVlcyh0ZWFtQ29udGV4dC50ZWFtbWF0ZXMpLFxuICAgICAgdCA9PiB0Lm5hbWUgIT09ICd0ZWFtLWxlYWQnLFxuICAgIClcbiAgICByZXR1cm4gW1xuICAgICAge1xuICAgICAgICBuYW1lOiB0ZWFtQ29udGV4dC50ZWFtTmFtZSxcbiAgICAgICAgbWVtYmVyQ291bnQ6IHRlYW1tYXRlQ291bnQsXG4gICAgICAgIHJ1bm5pbmdDb3VudDogMCxcbiAgICAgICAgaWRsZUNvdW50OiAwLFxuICAgICAgfSxcbiAgICBdXG4gIH0sIFt0ZWFtQ29udGV4dF0pXG5cbiAgLy8g4pSA4pSA4pSAIEZvb3RlciBwaWxsIG5hdmlnYXRpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vIFdoaWNoIHBpbGxzIHJlbmRlciBiZWxvdyB0aGUgaW5wdXQgYm94LiBPcmRlciBoZXJlIElTIHRoZSBuYXYgb3JkZXJcbiAgLy8gKGRvd24vcmlnaHQgPSBmb3J3YXJkLCB1cC9sZWZ0ID0gYmFjaykuIFNlbGVjdGlvbiBsaXZlcyBpbiBBcHBTdGF0ZSBzb1xuICAvLyBwaWxscyByZW5kZXJlZCBvdXRzaWRlIFByb21wdElucHV0IChDb21wYW5pb25TcHJpdGUpIGNhbiByZWFkIGZvY3VzLlxuICBjb25zdCBydW5uaW5nVGFza0NvdW50ID0gdXNlTWVtbyhcbiAgICAoKSA9PiBjb3VudChPYmplY3QudmFsdWVzKHRhc2tzKSwgdCA9PiB0LnN0YXR1cyA9PT0gJ3J1bm5pbmcnKSxcbiAgICBbdGFza3NdLFxuICApXG4gIC8vIFBhbmVsIHNob3dzIHJldGFpbmVkLWNvbXBsZXRlZCBhZ2VudHMgdG9vIChnZXRWaXNpYmxlQWdlbnRUYXNrcyksIHNvIHRoZVxuICAvLyBwaWxsIG11c3Qgc3RheSBuYXZpZ2FibGUgd2hlbmV2ZXIgdGhlIHBhbmVsIGhhcyByb3dzIOKAlCBub3QganVzdCB3aGVuXG4gIC8vIHNvbWV0aGluZyBpcyBydW5uaW5nLlxuICBjb25zdCB0YXNrc0Zvb3RlclZpc2libGUgPVxuICAgIChydW5uaW5nVGFza0NvdW50ID4gMCB8fFxuICAgICAgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiYgY29vcmRpbmF0b3JUYXNrQ291bnQgPiAwKSkgJiZcbiAgICAhc2hvdWxkSGlkZVRhc2tzRm9vdGVyKHRhc2tzLCBzaG93U3Bpbm5lclRyZWUpXG4gIGNvbnN0IHRlYW1zRm9vdGVyVmlzaWJsZSA9IGNhY2hlZFRlYW1zLmxlbmd0aCA+IDBcblxuICBjb25zdCBmb290ZXJJdGVtcyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIFtcbiAgICAgICAgdGFza3NGb290ZXJWaXNpYmxlICYmICd0YXNrcycsXG4gICAgICAgIHRtdXhGb290ZXJWaXNpYmxlICYmICd0bXV4JyxcbiAgICAgICAgYmFnZWxGb290ZXJWaXNpYmxlICYmICdiYWdlbCcsXG4gICAgICAgIHRlYW1zRm9vdGVyVmlzaWJsZSAmJiAndGVhbXMnLFxuICAgICAgICBicmlkZ2VGb290ZXJWaXNpYmxlICYmICdicmlkZ2UnLFxuICAgICAgICBjb21wYW5pb25Gb290ZXJWaXNpYmxlICYmICdjb21wYW5pb24nLFxuICAgICAgXS5maWx0ZXIoQm9vbGVhbikgYXMgRm9vdGVySXRlbVtdLFxuICAgIFtcbiAgICAgIHRhc2tzRm9vdGVyVmlzaWJsZSxcbiAgICAgIHRtdXhGb290ZXJWaXNpYmxlLFxuICAgICAgYmFnZWxGb290ZXJWaXNpYmxlLFxuICAgICAgdGVhbXNGb290ZXJWaXNpYmxlLFxuICAgICAgYnJpZGdlRm9vdGVyVmlzaWJsZSxcbiAgICAgIGNvbXBhbmlvbkZvb3RlclZpc2libGUsXG4gICAgXSxcbiAgKVxuXG4gIC8vIEVmZmVjdGl2ZSBzZWxlY3Rpb246IG51bGwgaWYgdGhlIHNlbGVjdGVkIHBpbGwgc3RvcHBlZCByZW5kZXJpbmcgKGJyaWRnZVxuICAvLyBkaXNjb25uZWN0ZWQsIHRhc2sgZmluaXNoZWQpLiBUaGUgZGVyaXZhdGlvbiBtYWtlcyB0aGUgVUkgY29ycmVjdFxuICAvLyBpbW1lZGlhdGVseTsgdGhlIHVzZUVmZmVjdCBiZWxvdyBjbGVhcnMgdGhlIHJhdyBzdGF0ZSBzbyBpdCBkb2Vzbid0XG4gIC8vIHJlc3VycmVjdCB3aGVuIHRoZSBzYW1lIHBpbGwgcmVhcHBlYXJzIChuZXcgdGFzayBzdGFydHMg4oaSIGZvY3VzIHN0b2xlbikuXG4gIGNvbnN0IHJhd0Zvb3RlclNlbGVjdGlvbiA9IHVzZUFwcFN0YXRlKHMgPT4gcy5mb290ZXJTZWxlY3Rpb24pXG4gIGNvbnN0IGZvb3Rlckl0ZW1TZWxlY3RlZCA9XG4gICAgcmF3Rm9vdGVyU2VsZWN0aW9uICYmIGZvb3Rlckl0ZW1zLmluY2x1ZGVzKHJhd0Zvb3RlclNlbGVjdGlvbilcbiAgICAgID8gcmF3Rm9vdGVyU2VsZWN0aW9uXG4gICAgICA6IG51bGxcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChyYXdGb290ZXJTZWxlY3Rpb24gJiYgIWZvb3Rlckl0ZW1TZWxlY3RlZCkge1xuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PlxuICAgICAgICBwcmV2LmZvb3RlclNlbGVjdGlvbiA9PT0gbnVsbFxuICAgICAgICAgID8gcHJldlxuICAgICAgICAgIDogeyAuLi5wcmV2LCBmb290ZXJTZWxlY3Rpb246IG51bGwgfSxcbiAgICAgIClcbiAgICB9XG4gIH0sIFtyYXdGb290ZXJTZWxlY3Rpb24sIGZvb3Rlckl0ZW1TZWxlY3RlZCwgc2V0QXBwU3RhdGVdKVxuXG4gIGNvbnN0IHRhc2tzU2VsZWN0ZWQgPSBmb290ZXJJdGVtU2VsZWN0ZWQgPT09ICd0YXNrcydcbiAgY29uc3QgdG11eFNlbGVjdGVkID0gZm9vdGVySXRlbVNlbGVjdGVkID09PSAndG11eCdcbiAgY29uc3QgYmFnZWxTZWxlY3RlZCA9IGZvb3Rlckl0ZW1TZWxlY3RlZCA9PT0gJ2JhZ2VsJ1xuICBjb25zdCB0ZWFtc1NlbGVjdGVkID0gZm9vdGVySXRlbVNlbGVjdGVkID09PSAndGVhbXMnXG4gIGNvbnN0IGJyaWRnZVNlbGVjdGVkID0gZm9vdGVySXRlbVNlbGVjdGVkID09PSAnYnJpZGdlJ1xuXG4gIGZ1bmN0aW9uIHNlbGVjdEZvb3Rlckl0ZW0oaXRlbTogRm9vdGVySXRlbSB8IG51bGwpOiB2b2lkIHtcbiAgICBzZXRBcHBTdGF0ZShwcmV2ID0+XG4gICAgICBwcmV2LmZvb3RlclNlbGVjdGlvbiA9PT0gaXRlbSA/IHByZXYgOiB7IC4uLnByZXYsIGZvb3RlclNlbGVjdGlvbjogaXRlbSB9LFxuICAgIClcbiAgICBpZiAoaXRlbSA9PT0gJ3Rhc2tzJykge1xuICAgICAgc2V0VGVhbW1hdGVGb290ZXJJbmRleCgwKVxuICAgICAgc2V0Q29vcmRpbmF0b3JUYXNrSW5kZXgobWluQ29vcmRpbmF0b3JJbmRleClcbiAgICB9XG4gIH1cblxuICAvLyBkZWx0YTogKzEgPSBkb3duL3JpZ2h0LCAtMSA9IHVwL2xlZnQuIFJldHVybnMgdHJ1ZSBpZiBuYXYgaGFwcGVuZWRcbiAgLy8gKGluY2x1ZGluZyBkZXNlbGVjdGluZyBhdCB0aGUgc3RhcnQpLCBmYWxzZSBpZiBhdCBhIGJvdW5kYXJ5LlxuICBmdW5jdGlvbiBuYXZpZ2F0ZUZvb3RlcihkZWx0YTogMSB8IC0xLCBleGl0QXRTdGFydCA9IGZhbHNlKTogYm9vbGVhbiB7XG4gICAgY29uc3QgaWR4ID0gZm9vdGVySXRlbVNlbGVjdGVkXG4gICAgICA/IGZvb3Rlckl0ZW1zLmluZGV4T2YoZm9vdGVySXRlbVNlbGVjdGVkKVxuICAgICAgOiAtMVxuICAgIGNvbnN0IG5leHQgPSBmb290ZXJJdGVtc1tpZHggKyBkZWx0YV1cbiAgICBpZiAobmV4dCkge1xuICAgICAgc2VsZWN0Rm9vdGVySXRlbShuZXh0KVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG4gICAgaWYgKGRlbHRhIDwgMCAmJiBleGl0QXRTdGFydCkge1xuICAgICAgc2VsZWN0Rm9vdGVySXRlbShudWxsKVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cblxuICAvLyBQcm9tcHQgc3VnZ2VzdGlvbiBob29rIC0gcmVhZHMgc3VnZ2VzdGlvbnMgZ2VuZXJhdGVkIGJ5IGZvcmtlZCBhZ2VudCBpbiBxdWVyeSBsb29wXG4gIGNvbnN0IHtcbiAgICBzdWdnZXN0aW9uOiBwcm9tcHRTdWdnZXN0aW9uLFxuICAgIG1hcmtBY2NlcHRlZCxcbiAgICBsb2dPdXRjb21lQXRTdWJtaXNzaW9uLFxuICAgIG1hcmtTaG93bixcbiAgfSA9IHVzZVByb21wdFN1Z2dlc3Rpb24oe1xuICAgIGlucHV0VmFsdWU6IGlucHV0LFxuICAgIGlzQXNzaXN0YW50UmVzcG9uZGluZzogaXNMb2FkaW5nLFxuICB9KVxuXG4gIGNvbnN0IGRpc3BsYXllZFZhbHVlID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgaXNTZWFyY2hpbmdIaXN0b3J5ICYmIGhpc3RvcnlNYXRjaFxuICAgICAgICA/IGdldFZhbHVlRnJvbUlucHV0KFxuICAgICAgICAgICAgdHlwZW9mIGhpc3RvcnlNYXRjaCA9PT0gJ3N0cmluZydcbiAgICAgICAgICAgICAgPyBoaXN0b3J5TWF0Y2hcbiAgICAgICAgICAgICAgOiBoaXN0b3J5TWF0Y2guZGlzcGxheSxcbiAgICAgICAgICApXG4gICAgICAgIDogaW5wdXQsXG4gICAgW2lzU2VhcmNoaW5nSGlzdG9yeSwgaGlzdG9yeU1hdGNoLCBpbnB1dF0sXG4gIClcblxuICBjb25zdCB0aGlua1RyaWdnZXJzID0gdXNlTWVtbyhcbiAgICAoKSA9PiBmaW5kVGhpbmtpbmdUcmlnZ2VyUG9zaXRpb25zKGRpc3BsYXllZFZhbHVlKSxcbiAgICBbZGlzcGxheWVkVmFsdWVdLFxuICApXG5cbiAgY29uc3QgdWx0cmFwbGFuU2Vzc2lvblVybCA9IHVzZUFwcFN0YXRlKHMgPT4gcy51bHRyYXBsYW5TZXNzaW9uVXJsKVxuICBjb25zdCB1bHRyYXBsYW5MYXVuY2hpbmcgPSB1c2VBcHBTdGF0ZShzID0+IHMudWx0cmFwbGFuTGF1bmNoaW5nKVxuICBjb25zdCB1bHRyYXBsYW5UcmlnZ2VycyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIGZlYXR1cmUoJ1VMVFJBUExBTicpICYmICF1bHRyYXBsYW5TZXNzaW9uVXJsICYmICF1bHRyYXBsYW5MYXVuY2hpbmdcbiAgICAgICAgPyBmaW5kVWx0cmFwbGFuVHJpZ2dlclBvc2l0aW9ucyhkaXNwbGF5ZWRWYWx1ZSlcbiAgICAgICAgOiBbXSxcbiAgICBbZGlzcGxheWVkVmFsdWUsIHVsdHJhcGxhblNlc3Npb25VcmwsIHVsdHJhcGxhbkxhdW5jaGluZ10sXG4gIClcblxuICBjb25zdCB1bHRyYXJldmlld1RyaWdnZXJzID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgaXNVbHRyYXJldmlld0VuYWJsZWQoKVxuICAgICAgICA/IGZpbmRVbHRyYXJldmlld1RyaWdnZXJQb3NpdGlvbnMoZGlzcGxheWVkVmFsdWUpXG4gICAgICAgIDogW10sXG4gICAgW2Rpc3BsYXllZFZhbHVlXSxcbiAgKVxuXG4gIGNvbnN0IGJ0d1RyaWdnZXJzID0gdXNlTWVtbyhcbiAgICAoKSA9PiBmaW5kQnR3VHJpZ2dlclBvc2l0aW9ucyhkaXNwbGF5ZWRWYWx1ZSksXG4gICAgW2Rpc3BsYXllZFZhbHVlXSxcbiAgKVxuXG4gIGNvbnN0IGJ1ZGR5VHJpZ2dlcnMgPSB1c2VNZW1vKFxuICAgICgpID0+IGZpbmRCdWRkeVRyaWdnZXJQb3NpdGlvbnMoZGlzcGxheWVkVmFsdWUpLFxuICAgIFtkaXNwbGF5ZWRWYWx1ZV0sXG4gIClcblxuICBjb25zdCBzbGFzaENvbW1hbmRUcmlnZ2VycyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGNvbnN0IHBvc2l0aW9ucyA9IGZpbmRTbGFzaENvbW1hbmRQb3NpdGlvbnMoZGlzcGxheWVkVmFsdWUpXG4gICAgLy8gT25seSBoaWdobGlnaHQgdmFsaWQgY29tbWFuZHNcbiAgICByZXR1cm4gcG9zaXRpb25zLmZpbHRlcihwb3MgPT4ge1xuICAgICAgY29uc3QgY29tbWFuZE5hbWUgPSBkaXNwbGF5ZWRWYWx1ZS5zbGljZShwb3Muc3RhcnQgKyAxLCBwb3MuZW5kKSAvLyArMSB0byBza2lwIFwiL1wiXG4gICAgICByZXR1cm4gaGFzQ29tbWFuZChjb21tYW5kTmFtZSwgY29tbWFuZHMpXG4gICAgfSlcbiAgfSwgW2Rpc3BsYXllZFZhbHVlLCBjb21tYW5kc10pXG5cbiAgY29uc3QgdG9rZW5CdWRnZXRUcmlnZ2VycyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIGZlYXR1cmUoJ1RPS0VOX0JVREdFVCcpID8gZmluZFRva2VuQnVkZ2V0UG9zaXRpb25zKGRpc3BsYXllZFZhbHVlKSA6IFtdLFxuICAgIFtkaXNwbGF5ZWRWYWx1ZV0sXG4gIClcblxuICBjb25zdCBrbm93bkNoYW5uZWxzVmVyc2lvbiA9IHVzZVN5bmNFeHRlcm5hbFN0b3JlKFxuICAgIHN1YnNjcmliZUtub3duQ2hhbm5lbHMsXG4gICAgZ2V0S25vd25DaGFubmVsc1ZlcnNpb24sXG4gIClcbiAgY29uc3Qgc2xhY2tDaGFubmVsVHJpZ2dlcnMgPSB1c2VNZW1vKFxuICAgICgpID0+XG4gICAgICBoYXNTbGFja01jcFNlcnZlcihzdG9yZS5nZXRTdGF0ZSgpLm1jcC5jbGllbnRzKVxuICAgICAgICA/IGZpbmRTbGFja0NoYW5uZWxQb3NpdGlvbnMoZGlzcGxheWVkVmFsdWUpXG4gICAgICAgIDogW10sXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwcyAtLSBzdG9yZSBpcyBhIHN0YWJsZSByZWZcbiAgICBbZGlzcGxheWVkVmFsdWUsIGtub3duQ2hhbm5lbHNWZXJzaW9uXSxcbiAgKVxuXG4gIC8vIEZpbmQgQG5hbWUgbWVudGlvbnMgYW5kIGhpZ2hsaWdodCB3aXRoIHRlYW0gbWVtYmVyJ3MgY29sb3JcbiAgY29uc3QgbWVtYmVyTWVudGlvbkhpZ2hsaWdodHMgPSB1c2VNZW1vKCgpOiBBcnJheTx7XG4gICAgc3RhcnQ6IG51bWJlclxuICAgIGVuZDogbnVtYmVyXG4gICAgdGhlbWVDb2xvcjoga2V5b2YgVGhlbWVcbiAgfT4gPT4ge1xuICAgIGlmICghaXNBZ2VudFN3YXJtc0VuYWJsZWQoKSkgcmV0dXJuIFtdXG4gICAgaWYgKCF0ZWFtQ29udGV4dD8udGVhbW1hdGVzKSByZXR1cm4gW11cblxuICAgIGNvbnN0IGhpZ2hsaWdodHM6IEFycmF5PHtcbiAgICAgIHN0YXJ0OiBudW1iZXJcbiAgICAgIGVuZDogbnVtYmVyXG4gICAgICB0aGVtZUNvbG9yOiBrZXlvZiBUaGVtZVxuICAgIH0+ID0gW11cbiAgICBjb25zdCBtZW1iZXJzID0gdGVhbUNvbnRleHQudGVhbW1hdGVzXG4gICAgaWYgKCFtZW1iZXJzKSByZXR1cm4gaGlnaGxpZ2h0c1xuXG4gICAgLy8gRmluZCBhbGwgQG5hbWUgcGF0dGVybnMgaW4gdGhlIGlucHV0XG4gICAgY29uc3QgcmVnZXggPSAvKF58XFxzKUAoW1xcdy1dKykvZ1xuICAgIGNvbnN0IG1lbWJlclZhbHVlcyA9IE9iamVjdC52YWx1ZXMobWVtYmVycylcbiAgICBsZXQgbWF0Y2hcbiAgICB3aGlsZSAoKG1hdGNoID0gcmVnZXguZXhlYyhkaXNwbGF5ZWRWYWx1ZSkpICE9PSBudWxsKSB7XG4gICAgICBjb25zdCBsZWFkaW5nU3BhY2UgPSBtYXRjaFsxXSA/PyAnJ1xuICAgICAgY29uc3QgbmFtZVN0YXJ0ID0gbWF0Y2guaW5kZXggKyBsZWFkaW5nU3BhY2UubGVuZ3RoXG4gICAgICBjb25zdCBmdWxsTWF0Y2ggPSBtYXRjaFswXS50cmltU3RhcnQoKVxuICAgICAgY29uc3QgbmFtZSA9IG1hdGNoWzJdXG5cbiAgICAgIC8vIENoZWNrIGlmIHRoaXMgbmFtZSBtYXRjaGVzIGEgdGVhbSBtZW1iZXJcbiAgICAgIGNvbnN0IG1lbWJlciA9IG1lbWJlclZhbHVlcy5maW5kKHQgPT4gdC5uYW1lID09PSBuYW1lKVxuICAgICAgaWYgKG1lbWJlcj8uY29sb3IpIHtcbiAgICAgICAgY29uc3QgdGhlbWVDb2xvciA9XG4gICAgICAgICAgQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1JbbWVtYmVyLmNvbG9yIGFzIEFnZW50Q29sb3JOYW1lXVxuICAgICAgICBpZiAodGhlbWVDb2xvcikge1xuICAgICAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgICAgICBzdGFydDogbmFtZVN0YXJ0LFxuICAgICAgICAgICAgZW5kOiBuYW1lU3RhcnQgKyBmdWxsTWF0Y2gubGVuZ3RoLFxuICAgICAgICAgICAgdGhlbWVDb2xvcixcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBoaWdobGlnaHRzXG4gIH0sIFtkaXNwbGF5ZWRWYWx1ZSwgdGVhbUNvbnRleHRdKVxuXG4gIGNvbnN0IGltYWdlUmVmUG9zaXRpb25zID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgcGFyc2VSZWZlcmVuY2VzKGRpc3BsYXllZFZhbHVlKVxuICAgICAgICAuZmlsdGVyKHIgPT4gci5tYXRjaC5zdGFydHNXaXRoKCdbSW1hZ2UnKSlcbiAgICAgICAgLm1hcChyID0+ICh7IHN0YXJ0OiByLmluZGV4LCBlbmQ6IHIuaW5kZXggKyByLm1hdGNoLmxlbmd0aCB9KSksXG4gICAgW2Rpc3BsYXllZFZhbHVlXSxcbiAgKVxuXG4gIC8vIGNoaXAuc3RhcnQgaXMgdGhlIFwic2VsZWN0ZWRcIiBzdGF0ZTogdGhlIGludmVydGVkIGNoaXAgSVMgdGhlIGN1cnNvci5cbiAgLy8gY2hpcC5lbmQgc3RheXMgYSBub3JtYWwgcG9zaXRpb24gc28geW91IGNhbiBwYXJrIHRoZSBjdXJzb3IgcmlnaHQgYWZ0ZXJcbiAgLy8gYF1gIGxpa2UgYW55IG90aGVyIGNoYXJhY3Rlci5cbiAgY29uc3QgY3Vyc29yQXRJbWFnZUNoaXAgPSBpbWFnZVJlZlBvc2l0aW9ucy5zb21lKFxuICAgIHIgPT4gci5zdGFydCA9PT0gY3Vyc29yT2Zmc2V0LFxuICApXG5cbiAgLy8gdXAvZG93biBtb3ZlbWVudCBvciBhIGZ1bGxzY3JlZW4gY2xpY2sgY2FuIGxhbmQgdGhlIGN1cnNvciBzdHJpY3RseVxuICAvLyBpbnNpZGUgYSBjaGlwOyBzbmFwIHRvIHRoZSBuZWFyZXIgYm91bmRhcnkgc28gaXQncyBuZXZlciBlZGl0YWJsZVxuICAvLyBjaGFyLWJ5LWNoYXIuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc3QgaW5zaWRlID0gaW1hZ2VSZWZQb3NpdGlvbnMuZmluZChcbiAgICAgIHIgPT4gY3Vyc29yT2Zmc2V0ID4gci5zdGFydCAmJiBjdXJzb3JPZmZzZXQgPCByLmVuZCxcbiAgICApXG4gICAgaWYgKGluc2lkZSkge1xuICAgICAgY29uc3QgbWlkID0gKGluc2lkZS5zdGFydCArIGluc2lkZS5lbmQpIC8gMlxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KGN1cnNvck9mZnNldCA8IG1pZCA/IGluc2lkZS5zdGFydCA6IGluc2lkZS5lbmQpXG4gICAgfVxuICB9LCBbY3Vyc29yT2Zmc2V0LCBpbWFnZVJlZlBvc2l0aW9ucywgc2V0Q3Vyc29yT2Zmc2V0XSlcblxuICBjb25zdCBjb21iaW5lZEhpZ2hsaWdodHMgPSB1c2VNZW1vKCgpOiBUZXh0SGlnaGxpZ2h0W10gPT4ge1xuICAgIGNvbnN0IGhpZ2hsaWdodHM6IFRleHRIaWdobGlnaHRbXSA9IFtdXG5cbiAgICAvLyBJbnZlcnQgdGhlIFtJbWFnZSAjTl0gY2hpcCB3aGVuIHRoZSBjdXJzb3IgaXMgYXQgY2hpcC5zdGFydCAodGhlXG4gICAgLy8gXCJzZWxlY3RlZFwiIHN0YXRlKSBzbyBiYWNrc3BhY2UtdG8tZGVsZXRlIGlzIHZpc3VhbGx5IG9idmlvdXMuXG4gICAgZm9yIChjb25zdCByZWYgb2YgaW1hZ2VSZWZQb3NpdGlvbnMpIHtcbiAgICAgIGlmIChjdXJzb3JPZmZzZXQgPT09IHJlZi5zdGFydCkge1xuICAgICAgICBoaWdobGlnaHRzLnB1c2goe1xuICAgICAgICAgIHN0YXJ0OiByZWYuc3RhcnQsXG4gICAgICAgICAgZW5kOiByZWYuZW5kLFxuICAgICAgICAgIGNvbG9yOiB1bmRlZmluZWQsXG4gICAgICAgICAgaW52ZXJzZTogdHJ1ZSxcbiAgICAgICAgICBwcmlvcml0eTogOCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaXNTZWFyY2hpbmdIaXN0b3J5ICYmIGhpc3RvcnlNYXRjaCAmJiAhaGlzdG9yeUZhaWxlZE1hdGNoKSB7XG4gICAgICBoaWdobGlnaHRzLnB1c2goe1xuICAgICAgICBzdGFydDogY3Vyc29yT2Zmc2V0LFxuICAgICAgICBlbmQ6IGN1cnNvck9mZnNldCArIGhpc3RvcnlRdWVyeS5sZW5ndGgsXG4gICAgICAgIGNvbG9yOiAnd2FybmluZycsXG4gICAgICAgIHByaW9yaXR5OiAyMCxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gQWRkIFwiYnR3XCIgaGlnaGxpZ2h0aW5nIChzb2xpZCB5ZWxsb3cpXG4gICAgZm9yIChjb25zdCB0cmlnZ2VyIG9mIGJ0d1RyaWdnZXJzKSB7XG4gICAgICBoaWdobGlnaHRzLnB1c2goe1xuICAgICAgICBzdGFydDogdHJpZ2dlci5zdGFydCxcbiAgICAgICAgZW5kOiB0cmlnZ2VyLmVuZCxcbiAgICAgICAgY29sb3I6ICd3YXJuaW5nJyxcbiAgICAgICAgcHJpb3JpdHk6IDE1LFxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBBZGQgL2NvbW1hbmQgaGlnaGxpZ2h0aW5nIChibHVlKVxuICAgIGZvciAoY29uc3QgdHJpZ2dlciBvZiBzbGFzaENvbW1hbmRUcmlnZ2Vycykge1xuICAgICAgaGlnaGxpZ2h0cy5wdXNoKHtcbiAgICAgICAgc3RhcnQ6IHRyaWdnZXIuc3RhcnQsXG4gICAgICAgIGVuZDogdHJpZ2dlci5lbmQsXG4gICAgICAgIGNvbG9yOiAnc3VnZ2VzdGlvbicsXG4gICAgICAgIHByaW9yaXR5OiA1LFxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBBZGQgdG9rZW4gYnVkZ2V0IGhpZ2hsaWdodGluZyAoYmx1ZSlcbiAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2YgdG9rZW5CdWRnZXRUcmlnZ2Vycykge1xuICAgICAgaGlnaGxpZ2h0cy5wdXNoKHtcbiAgICAgICAgc3RhcnQ6IHRyaWdnZXIuc3RhcnQsXG4gICAgICAgIGVuZDogdHJpZ2dlci5lbmQsXG4gICAgICAgIGNvbG9yOiAnc3VnZ2VzdGlvbicsXG4gICAgICAgIHByaW9yaXR5OiA1LFxuICAgICAgfSlcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2Ygc2xhY2tDaGFubmVsVHJpZ2dlcnMpIHtcbiAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgIHN0YXJ0OiB0cmlnZ2VyLnN0YXJ0LFxuICAgICAgICBlbmQ6IHRyaWdnZXIuZW5kLFxuICAgICAgICBjb2xvcjogJ3N1Z2dlc3Rpb24nLFxuICAgICAgICBwcmlvcml0eTogNSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gQWRkIEBuYW1lIGhpZ2hsaWdodGluZyB3aXRoIHRlYW0gbWVtYmVyJ3MgY29sb3JcbiAgICBmb3IgKGNvbnN0IG1lbnRpb24gb2YgbWVtYmVyTWVudGlvbkhpZ2hsaWdodHMpIHtcbiAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgIHN0YXJ0OiBtZW50aW9uLnN0YXJ0LFxuICAgICAgICBlbmQ6IG1lbnRpb24uZW5kLFxuICAgICAgICBjb2xvcjogbWVudGlvbi50aGVtZUNvbG9yLFxuICAgICAgICBwcmlvcml0eTogNSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gRGltIGludGVyaW0gdm9pY2UgZGljdGF0aW9uIHRleHRcbiAgICBpZiAodm9pY2VJbnRlcmltUmFuZ2UpIHtcbiAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgIHN0YXJ0OiB2b2ljZUludGVyaW1SYW5nZS5zdGFydCxcbiAgICAgICAgZW5kOiB2b2ljZUludGVyaW1SYW5nZS5lbmQsXG4gICAgICAgIGNvbG9yOiB1bmRlZmluZWQsXG4gICAgICAgIGRpbUNvbG9yOiB0cnVlLFxuICAgICAgICBwcmlvcml0eTogMSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gUmFpbmJvdyBoaWdobGlnaHRpbmcgZm9yIHVsdHJhdGhpbmsga2V5d29yZCAocGVyLWNoYXJhY3RlciBjeWNsaW5nIGNvbG9ycylcbiAgICBpZiAoaXNVbHRyYXRoaW5rRW5hYmxlZCgpKSB7XG4gICAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2YgdGhpbmtUcmlnZ2Vycykge1xuICAgICAgICBmb3IgKGxldCBpID0gdHJpZ2dlci5zdGFydDsgaSA8IHRyaWdnZXIuZW5kOyBpKyspIHtcbiAgICAgICAgICBoaWdobGlnaHRzLnB1c2goe1xuICAgICAgICAgICAgc3RhcnQ6IGksXG4gICAgICAgICAgICBlbmQ6IGkgKyAxLFxuICAgICAgICAgICAgY29sb3I6IGdldFJhaW5ib3dDb2xvcihpIC0gdHJpZ2dlci5zdGFydCksXG4gICAgICAgICAgICBzaGltbWVyQ29sb3I6IGdldFJhaW5ib3dDb2xvcihpIC0gdHJpZ2dlci5zdGFydCwgdHJ1ZSksXG4gICAgICAgICAgICBwcmlvcml0eTogMTAsXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFNhbWUgcmFpbmJvdyB0cmVhdG1lbnQgZm9yIHRoZSB1bHRyYXBsYW4ga2V5d29yZFxuICAgIGlmIChmZWF0dXJlKCdVTFRSQVBMQU4nKSkge1xuICAgICAgZm9yIChjb25zdCB0cmlnZ2VyIG9mIHVsdHJhcGxhblRyaWdnZXJzKSB7XG4gICAgICAgIGZvciAobGV0IGkgPSB0cmlnZ2VyLnN0YXJ0OyBpIDwgdHJpZ2dlci5lbmQ7IGkrKykge1xuICAgICAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgICAgICBzdGFydDogaSxcbiAgICAgICAgICAgIGVuZDogaSArIDEsXG4gICAgICAgICAgICBjb2xvcjogZ2V0UmFpbmJvd0NvbG9yKGkgLSB0cmlnZ2VyLnN0YXJ0KSxcbiAgICAgICAgICAgIHNoaW1tZXJDb2xvcjogZ2V0UmFpbmJvd0NvbG9yKGkgLSB0cmlnZ2VyLnN0YXJ0LCB0cnVlKSxcbiAgICAgICAgICAgIHByaW9yaXR5OiAxMCxcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gU2FtZSByYWluYm93IHRyZWF0bWVudCBmb3IgdGhlIHVsdHJhcmV2aWV3IGtleXdvcmRcbiAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2YgdWx0cmFyZXZpZXdUcmlnZ2Vycykge1xuICAgICAgZm9yIChsZXQgaSA9IHRyaWdnZXIuc3RhcnQ7IGkgPCB0cmlnZ2VyLmVuZDsgaSsrKSB7XG4gICAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgICAgc3RhcnQ6IGksXG4gICAgICAgICAgZW5kOiBpICsgMSxcbiAgICAgICAgICBjb2xvcjogZ2V0UmFpbmJvd0NvbG9yKGkgLSB0cmlnZ2VyLnN0YXJ0KSxcbiAgICAgICAgICBzaGltbWVyQ29sb3I6IGdldFJhaW5ib3dDb2xvcihpIC0gdHJpZ2dlci5zdGFydCwgdHJ1ZSksXG4gICAgICAgICAgcHJpb3JpdHk6IDEwLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFJhaW5ib3cgZm9yIC9idWRkeVxuICAgIGZvciAoY29uc3QgdHJpZ2dlciBvZiBidWRkeVRyaWdnZXJzKSB7XG4gICAgICBmb3IgKGxldCBpID0gdHJpZ2dlci5zdGFydDsgaSA8IHRyaWdnZXIuZW5kOyBpKyspIHtcbiAgICAgICAgaGlnaGxpZ2h0cy5wdXNoKHtcbiAgICAgICAgICBzdGFydDogaSxcbiAgICAgICAgICBlbmQ6IGkgKyAxLFxuICAgICAgICAgIGNvbG9yOiBnZXRSYWluYm93Q29sb3IoaSAtIHRyaWdnZXIuc3RhcnQpLFxuICAgICAgICAgIHNoaW1tZXJDb2xvcjogZ2V0UmFpbmJvd0NvbG9yKGkgLSB0cmlnZ2VyLnN0YXJ0LCB0cnVlKSxcbiAgICAgICAgICBwcmlvcml0eTogMTAsXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGhpZ2hsaWdodHNcbiAgfSwgW1xuICAgIGlzU2VhcmNoaW5nSGlzdG9yeSxcbiAgICBoaXN0b3J5UXVlcnksXG4gICAgaGlzdG9yeU1hdGNoLFxuICAgIGhpc3RvcnlGYWlsZWRNYXRjaCxcbiAgICBjdXJzb3JPZmZzZXQsXG4gICAgYnR3VHJpZ2dlcnMsXG4gICAgaW1hZ2VSZWZQb3NpdGlvbnMsXG4gICAgbWVtYmVyTWVudGlvbkhpZ2hsaWdodHMsXG4gICAgc2xhc2hDb21tYW5kVHJpZ2dlcnMsXG4gICAgdG9rZW5CdWRnZXRUcmlnZ2VycyxcbiAgICBzbGFja0NoYW5uZWxUcmlnZ2VycyxcbiAgICBkaXNwbGF5ZWRWYWx1ZSxcbiAgICB2b2ljZUludGVyaW1SYW5nZSxcbiAgICB0aGlua1RyaWdnZXJzLFxuICAgIHVsdHJhcGxhblRyaWdnZXJzLFxuICAgIHVsdHJhcmV2aWV3VHJpZ2dlcnMsXG4gICAgYnVkZHlUcmlnZ2VycyxcbiAgXSlcblxuICBjb25zdCB7IGFkZE5vdGlmaWNhdGlvbiwgcmVtb3ZlTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcblxuICAvLyBTaG93IHVsdHJhdGhpbmsgbm90aWZpY2F0aW9uXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKHRoaW5rVHJpZ2dlcnMubGVuZ3RoICYmIGlzVWx0cmF0aGlua0VuYWJsZWQoKSkge1xuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAndWx0cmF0aGluay1hY3RpdmUnLFxuICAgICAgICB0ZXh0OiAnRWZmb3J0IHNldCB0byBoaWdoIGZvciB0aGlzIHR1cm4nLFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogNTAwMCxcbiAgICAgIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHJlbW92ZU5vdGlmaWNhdGlvbigndWx0cmF0aGluay1hY3RpdmUnKVxuICAgIH1cbiAgfSwgW2FkZE5vdGlmaWNhdGlvbiwgcmVtb3ZlTm90aWZpY2F0aW9uLCB0aGlua1RyaWdnZXJzLmxlbmd0aF0pXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZmVhdHVyZSgnVUxUUkFQTEFOJykgJiYgdWx0cmFwbGFuVHJpZ2dlcnMubGVuZ3RoKSB7XG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICd1bHRyYXBsYW4tYWN0aXZlJyxcbiAgICAgICAgdGV4dDogJ1RoaXMgcHJvbXB0IHdpbGwgbGF1bmNoIGFuIHVsdHJhcGxhbiBzZXNzaW9uIGluIENsYXVkZSBDb2RlIG9uIHRoZSB3ZWInLFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogNTAwMCxcbiAgICAgIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHJlbW92ZU5vdGlmaWNhdGlvbigndWx0cmFwbGFuLWFjdGl2ZScpXG4gICAgfVxuICB9LCBbYWRkTm90aWZpY2F0aW9uLCByZW1vdmVOb3RpZmljYXRpb24sIHVsdHJhcGxhblRyaWdnZXJzLmxlbmd0aF0pXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoaXNVbHRyYXJldmlld0VuYWJsZWQoKSAmJiB1bHRyYXJldmlld1RyaWdnZXJzLmxlbmd0aCkge1xuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAndWx0cmFyZXZpZXctYWN0aXZlJyxcbiAgICAgICAgdGV4dDogJ1J1biAvdWx0cmFyZXZpZXcgYWZ0ZXIgQ2xhdWRlIGZpbmlzaGVzIHRvIHJldmlldyB0aGVzZSBjaGFuZ2VzIGluIHRoZSBjbG91ZCcsXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgdGltZW91dE1zOiA1MDAwLFxuICAgICAgfSlcbiAgICB9XG4gIH0sIFthZGROb3RpZmljYXRpb24sIHVsdHJhcmV2aWV3VHJpZ2dlcnMubGVuZ3RoXSlcblxuICAvLyBUcmFjayBpbnB1dCBsZW5ndGggZm9yIHN0YXNoIGhpbnRcbiAgY29uc3QgcHJldklucHV0TGVuZ3RoUmVmID0gdXNlUmVmKGlucHV0Lmxlbmd0aClcbiAgY29uc3QgcGVha0lucHV0TGVuZ3RoUmVmID0gdXNlUmVmKGlucHV0Lmxlbmd0aClcblxuICAvLyBEaXNtaXNzIHN0YXNoIGhpbnQgd2hlbiB1c2VyIG1ha2VzIGFueSBpbnB1dCBjaGFuZ2VcbiAgY29uc3QgZGlzbWlzc1N0YXNoSGludCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICByZW1vdmVOb3RpZmljYXRpb24oJ3N0YXNoLWhpbnQnKVxuICB9LCBbcmVtb3ZlTm90aWZpY2F0aW9uXSlcblxuICAvLyBTaG93IHN0YXNoIGhpbnQgd2hlbiB1c2VyIGdyYWR1YWxseSBjbGVhcnMgc3Vic3RhbnRpYWwgaW5wdXRcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBjb25zdCBwcmV2TGVuZ3RoID0gcHJldklucHV0TGVuZ3RoUmVmLmN1cnJlbnRcbiAgICBjb25zdCBwZWFrTGVuZ3RoID0gcGVha0lucHV0TGVuZ3RoUmVmLmN1cnJlbnRcbiAgICBjb25zdCBjdXJyZW50TGVuZ3RoID0gaW5wdXQubGVuZ3RoXG4gICAgcHJldklucHV0TGVuZ3RoUmVmLmN1cnJlbnQgPSBjdXJyZW50TGVuZ3RoXG5cbiAgICAvLyBVcGRhdGUgcGVhayB3aGVuIGlucHV0IGdyb3dzXG4gICAgaWYgKGN1cnJlbnRMZW5ndGggPiBwZWFrTGVuZ3RoKSB7XG4gICAgICBwZWFrSW5wdXRMZW5ndGhSZWYuY3VycmVudCA9IGN1cnJlbnRMZW5ndGhcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIFJlc2V0IHN0YXRlIHdoZW4gaW5wdXQgaXMgZW1wdHlcbiAgICBpZiAoY3VycmVudExlbmd0aCA9PT0gMCkge1xuICAgICAgcGVha0lucHV0TGVuZ3RoUmVmLmN1cnJlbnQgPSAwXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBEZXRlY3QgZ3JhZHVhbCBjbGVhcjogcGVhayB3YXMgaGlnaCwgY3VycmVudCBpcyBsb3csIGJ1dCB0aGlzIHdhc24ndCBhIHNpbmdsZSBiaWcganVtcFxuICAgIC8vIChyYXBpZCBjbGVhcnMgbGlrZSBlc2MtZXNjIGdvIGZyb20gMjArIHRvIDAgaW4gb25lIHN0ZXApXG4gICAgY29uc3QgY2xlYXJlZFN1YnN0YW50aWFsSW5wdXQgPSBwZWFrTGVuZ3RoID49IDIwICYmIGN1cnJlbnRMZW5ndGggPD0gNVxuICAgIGNvbnN0IHdhc1JhcGlkQ2xlYXIgPSBwcmV2TGVuZ3RoID49IDIwICYmIGN1cnJlbnRMZW5ndGggPD0gNVxuXG4gICAgaWYgKGNsZWFyZWRTdWJzdGFudGlhbElucHV0ICYmICF3YXNSYXBpZENsZWFyKSB7XG4gICAgICBjb25zdCBjb25maWcgPSBnZXRHbG9iYWxDb25maWcoKVxuICAgICAgaWYgKCFjb25maWcuaGFzVXNlZFN0YXNoKSB7XG4gICAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgICAga2V5OiAnc3Rhc2gtaGludCcsXG4gICAgICAgICAganN4OiAoXG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgVGlwOnsnICd9XG4gICAgICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgICBhY3Rpb249XCJjaGF0OnN0YXNoXCJcbiAgICAgICAgICAgICAgICBjb250ZXh0PVwiQ2hhdFwiXG4gICAgICAgICAgICAgICAgZmFsbGJhY2s9XCJjdHJsK3NcIlxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwic3Rhc2hcIlxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICksXG4gICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgIHRpbWVvdXRNczogRk9PVEVSX1RFTVBPUkFSWV9TVEFUVVNfVElNRU9VVCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIHBlYWtJbnB1dExlbmd0aFJlZi5jdXJyZW50ID0gY3VycmVudExlbmd0aFxuICAgIH1cbiAgfSwgW2lucHV0Lmxlbmd0aCwgYWRkTm90aWZpY2F0aW9uXSlcblxuICAvLyBJbml0aWFsaXplIGlucHV0IGJ1ZmZlciBmb3IgdW5kbyBmdW5jdGlvbmFsaXR5XG4gIGNvbnN0IHsgcHVzaFRvQnVmZmVyLCB1bmRvLCBjYW5VbmRvLCBjbGVhckJ1ZmZlciB9ID0gdXNlSW5wdXRCdWZmZXIoe1xuICAgIG1heEJ1ZmZlclNpemU6IDUwLFxuICAgIGRlYm91bmNlTXM6IDEwMDAsXG4gIH0pXG5cbiAgdXNlTWF5YmVUcnVuY2F0ZUlucHV0KHtcbiAgICBpbnB1dCxcbiAgICBwYXN0ZWRDb250ZW50cyxcbiAgICBvbklucHV0Q2hhbmdlOiB0cmFja0FuZFNldElucHV0LFxuICAgIHNldEN1cnNvck9mZnNldCxcbiAgICBzZXRQYXN0ZWRDb250ZW50cyxcbiAgfSlcblxuICBjb25zdCBkZWZhdWx0UGxhY2Vob2xkZXIgPSB1c2VQcm9tcHRJbnB1dFBsYWNlaG9sZGVyKHtcbiAgICBpbnB1dCxcbiAgICBzdWJtaXRDb3VudCxcbiAgICB2aWV3aW5nQWdlbnROYW1lLFxuICB9KVxuXG4gIGNvbnN0IG9uQ2hhbmdlID0gdXNlQ2FsbGJhY2soXG4gICAgKHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICAgIGlmICh2YWx1ZSA9PT0gJz8nKSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9oZWxwX3RvZ2dsZWQnLCB7fSlcbiAgICAgICAgc2V0SGVscE9wZW4odiA9PiAhdilcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBzZXRIZWxwT3BlbihmYWxzZSlcblxuICAgICAgLy8gRGlzbWlzcyBzdGFzaCBoaW50IHdoZW4gdXNlciBtYWtlcyBhbnkgaW5wdXQgY2hhbmdlXG4gICAgICBkaXNtaXNzU3Rhc2hIaW50KClcblxuICAgICAgLy8gQ2FuY2VsIGFueSBwZW5kaW5nIHByb21wdCBzdWdnZXN0aW9uIGFuZCBzcGVjdWxhdGlvbiB3aGVuIHVzZXIgdHlwZXNcbiAgICAgIGFib3J0UHJvbXB0U3VnZ2VzdGlvbigpXG4gICAgICBhYm9ydFNwZWN1bGF0aW9uKHNldEFwcFN0YXRlKVxuXG4gICAgICAvLyBDaGVjayBpZiB0aGlzIGlzIGEgc2luZ2xlIGNoYXJhY3RlciBpbnNlcnRpb24gYXQgdGhlIHN0YXJ0XG4gICAgICBjb25zdCBpc1NpbmdsZUNoYXJJbnNlcnRpb24gPSB2YWx1ZS5sZW5ndGggPT09IGlucHV0Lmxlbmd0aCArIDFcbiAgICAgIGNvbnN0IGluc2VydGVkQXRTdGFydCA9IGN1cnNvck9mZnNldCA9PT0gMFxuICAgICAgY29uc3QgbW9kZSA9IGdldE1vZGVGcm9tSW5wdXQodmFsdWUpXG5cbiAgICAgIGlmIChpbnNlcnRlZEF0U3RhcnQgJiYgbW9kZSAhPT0gJ3Byb21wdCcpIHtcbiAgICAgICAgaWYgKGlzU2luZ2xlQ2hhckluc2VydGlvbikge1xuICAgICAgICAgIG9uTW9kZUNoYW5nZShtb2RlKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIE11bHRpLWNoYXIgaW5zZXJ0aW9uIGludG8gZW1wdHkgaW5wdXQgKGUuZy4gdGFiLWFjY2VwdGluZyBcIiEgZ2Nsb3VkIGF1dGggbG9naW5cIilcbiAgICAgICAgaWYgKGlucHV0Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIG9uTW9kZUNoYW5nZShtb2RlKVxuICAgICAgICAgIGNvbnN0IHZhbHVlV2l0aG91dE1vZGUgPSBnZXRWYWx1ZUZyb21JbnB1dCh2YWx1ZSkucmVwbGFjZUFsbChcbiAgICAgICAgICAgICdcXHQnLFxuICAgICAgICAgICAgJyAgICAnLFxuICAgICAgICAgIClcbiAgICAgICAgICBwdXNoVG9CdWZmZXIoaW5wdXQsIGN1cnNvck9mZnNldCwgcGFzdGVkQ29udGVudHMpXG4gICAgICAgICAgdHJhY2tBbmRTZXRJbnB1dCh2YWx1ZVdpdGhvdXRNb2RlKVxuICAgICAgICAgIHNldEN1cnNvck9mZnNldCh2YWx1ZVdpdGhvdXRNb2RlLmxlbmd0aClcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjb25zdCBwcm9jZXNzZWRWYWx1ZSA9IHZhbHVlLnJlcGxhY2VBbGwoJ1xcdCcsICcgICAgJylcblxuICAgICAgLy8gUHVzaCBjdXJyZW50IHN0YXRlIHRvIGJ1ZmZlciBiZWZvcmUgbWFraW5nIGNoYW5nZXNcbiAgICAgIGlmIChpbnB1dCAhPT0gcHJvY2Vzc2VkVmFsdWUpIHtcbiAgICAgICAgcHVzaFRvQnVmZmVyKGlucHV0LCBjdXJzb3JPZmZzZXQsIHBhc3RlZENvbnRlbnRzKVxuICAgICAgfVxuXG4gICAgICAvLyBEZXNlbGVjdCBmb290ZXIgaXRlbXMgd2hlbiB1c2VyIHR5cGVzXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+XG4gICAgICAgIHByZXYuZm9vdGVyU2VsZWN0aW9uID09PSBudWxsXG4gICAgICAgICAgPyBwcmV2XG4gICAgICAgICAgOiB7IC4uLnByZXYsIGZvb3RlclNlbGVjdGlvbjogbnVsbCB9LFxuICAgICAgKVxuXG4gICAgICB0cmFja0FuZFNldElucHV0KHByb2Nlc3NlZFZhbHVlKVxuICAgIH0sXG4gICAgW1xuICAgICAgdHJhY2tBbmRTZXRJbnB1dCxcbiAgICAgIG9uTW9kZUNoYW5nZSxcbiAgICAgIGlucHV0LFxuICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgcHVzaFRvQnVmZmVyLFxuICAgICAgcGFzdGVkQ29udGVudHMsXG4gICAgICBkaXNtaXNzU3Rhc2hIaW50LFxuICAgICAgc2V0QXBwU3RhdGUsXG4gICAgXSxcbiAgKVxuXG4gIGNvbnN0IHtcbiAgICByZXNldEhpc3RvcnksXG4gICAgb25IaXN0b3J5VXAsXG4gICAgb25IaXN0b3J5RG93bixcbiAgICBkaXNtaXNzU2VhcmNoSGludCxcbiAgICBoaXN0b3J5SW5kZXgsXG4gIH0gPSB1c2VBcnJvd0tleUhpc3RvcnkoXG4gICAgKFxuICAgICAgdmFsdWU6IHN0cmluZyxcbiAgICAgIGhpc3RvcnlNb2RlOiBIaXN0b3J5TW9kZSxcbiAgICAgIHBhc3RlZENvbnRlbnRzOiBSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50PixcbiAgICApID0+IHtcbiAgICAgIG9uQ2hhbmdlKHZhbHVlKVxuICAgICAgb25Nb2RlQ2hhbmdlKGhpc3RvcnlNb2RlKVxuICAgICAgc2V0UGFzdGVkQ29udGVudHMocGFzdGVkQ29udGVudHMpXG4gICAgfSxcbiAgICBpbnB1dCxcbiAgICBwYXN0ZWRDb250ZW50cyxcbiAgICBzZXRDdXJzb3JPZmZzZXQsXG4gICAgbW9kZSxcbiAgKVxuXG4gIC8vIERpc21pc3Mgc2VhcmNoIGhpbnQgd2hlbiB1c2VyIHN0YXJ0cyBzZWFyY2hpbmdcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoaXNTZWFyY2hpbmdIaXN0b3J5KSB7XG4gICAgICBkaXNtaXNzU2VhcmNoSGludCgpXG4gICAgfVxuICB9LCBbaXNTZWFyY2hpbmdIaXN0b3J5LCBkaXNtaXNzU2VhcmNoSGludF0pXG5cbiAgLy8gT25seSB1c2UgaGlzdG9yeSBuYXZpZ2F0aW9uIHdoZW4gdGhlcmUgYXJlIDAgb3IgMSBzbGFzaCBjb21tYW5kIHN1Z2dlc3Rpb25zLlxuICAvLyBGb290ZXIgbmF2IGlzIE5PVCBoZXJlIOKAlCB3aGVuIGEgcGlsbCBpcyBzZWxlY3RlZCwgVGV4dElucHV0IGZvY3VzPWZhbHNlIHNvXG4gIC8vIHRoZXNlIG5ldmVyIGZpcmUuIFRoZSBGb290ZXIga2V5YmluZGluZyBjb250ZXh0IGhhbmRsZXMg4oaRL+KGkyBpbnN0ZWFkLlxuICBmdW5jdGlvbiBoYW5kbGVIaXN0b3J5VXAoKSB7XG4gICAgaWYgKHN1Z2dlc3Rpb25zLmxlbmd0aCA+IDEpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIE9ubHkgbmF2aWdhdGUgaGlzdG9yeSB3aGVuIGN1cnNvciBpcyBvbiB0aGUgZmlyc3QgbGluZS5cbiAgICAvLyBJbiBtdWx0aWxpbmUgaW5wdXRzLCB1cCBhcnJvdyBzaG91bGQgbW92ZSB0aGUgY3Vyc29yIChoYW5kbGVkIGJ5IFRleHRJbnB1dClcbiAgICAvLyBhbmQgb25seSB0cmlnZ2VyIGhpc3Rvcnkgd2hlbiBhdCB0aGUgdG9wIG9mIHRoZSBpbnB1dC5cbiAgICBpZiAoIWlzQ3Vyc29yT25GaXJzdExpbmUpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIElmIHRoZXJlJ3MgYW4gZWRpdGFibGUgcXVldWVkIGNvbW1hbmQsIG1vdmUgaXQgdG8gdGhlIGlucHV0IGZvciBlZGl0aW5nIHdoZW4gVVAgaXMgcHJlc3NlZFxuICAgIGNvbnN0IGhhc0VkaXRhYmxlQ29tbWFuZCA9IHF1ZXVlZENvbW1hbmRzLnNvbWUoaXNRdWV1ZWRDb21tYW5kRWRpdGFibGUpXG4gICAgaWYgKGhhc0VkaXRhYmxlQ29tbWFuZCkge1xuICAgICAgdm9pZCBwb3BBbGxDb21tYW5kc0Zyb21RdWV1ZSgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBvbkhpc3RvcnlVcCgpXG4gIH1cblxuICBmdW5jdGlvbiBoYW5kbGVIaXN0b3J5RG93bigpIHtcbiAgICBpZiAoc3VnZ2VzdGlvbnMubGVuZ3RoID4gMSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gT25seSBuYXZpZ2F0ZSBoaXN0b3J5L2Zvb3RlciB3aGVuIGN1cnNvciBpcyBvbiB0aGUgbGFzdCBsaW5lLlxuICAgIC8vIEluIG11bHRpbGluZSBpbnB1dHMsIGRvd24gYXJyb3cgc2hvdWxkIG1vdmUgdGhlIGN1cnNvciAoaGFuZGxlZCBieSBUZXh0SW5wdXQpXG4gICAgLy8gYW5kIG9ubHkgdHJpZ2dlciBuYXZpZ2F0aW9uIHdoZW4gYXQgdGhlIGJvdHRvbSBvZiB0aGUgaW5wdXQuXG4gICAgaWYgKCFpc0N1cnNvck9uTGFzdExpbmUpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIEF0IGJvdHRvbSBvZiBoaXN0b3J5IOKGkiBlbnRlciBmb290ZXIgYXQgZmlyc3QgdmlzaWJsZSBwaWxsXG4gICAgaWYgKG9uSGlzdG9yeURvd24oKSAmJiBmb290ZXJJdGVtcy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBmaXJzdCA9IGZvb3Rlckl0ZW1zWzBdIVxuICAgICAgc2VsZWN0Rm9vdGVySXRlbShmaXJzdClcbiAgICAgIGlmIChmaXJzdCA9PT0gJ3Rhc2tzJyAmJiAhZ2V0R2xvYmFsQ29uZmlnKCkuaGFzU2VlblRhc2tzSGludCkge1xuICAgICAgICBzYXZlR2xvYmFsQ29uZmlnKGMgPT5cbiAgICAgICAgICBjLmhhc1NlZW5UYXNrc0hpbnQgPyBjIDogeyAuLi5jLCBoYXNTZWVuVGFza3NIaW50OiB0cnVlIH0sXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBDcmVhdGUgYSBzdWdnZXN0aW9ucyBzdGF0ZSBkaXJlY3RseSAtIHdlJ2xsIHN5bmMgaXQgd2l0aCB1c2VUeXBlYWhlYWQgbGF0ZXJcbiAgY29uc3QgW3N1Z2dlc3Rpb25zU3RhdGUsIHNldFN1Z2dlc3Rpb25zU3RhdGVSYXddID0gdXNlU3RhdGU8e1xuICAgIHN1Z2dlc3Rpb25zOiBTdWdnZXN0aW9uSXRlbVtdXG4gICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBudW1iZXJcbiAgICBjb21tYW5kQXJndW1lbnRIaW50Pzogc3RyaW5nXG4gIH0+KHtcbiAgICBzdWdnZXN0aW9uczogW10sXG4gICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiAtMSxcbiAgICBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQsXG4gIH0pXG5cbiAgLy8gU2V0dGVyIGZvciBzdWdnZXN0aW9ucyBzdGF0ZVxuICBjb25zdCBzZXRTdWdnZXN0aW9uc1N0YXRlID0gdXNlQ2FsbGJhY2soXG4gICAgKFxuICAgICAgdXBkYXRlcjpcbiAgICAgICAgfCB0eXBlb2Ygc3VnZ2VzdGlvbnNTdGF0ZVxuICAgICAgICB8ICgocHJldjogdHlwZW9mIHN1Z2dlc3Rpb25zU3RhdGUpID0+IHR5cGVvZiBzdWdnZXN0aW9uc1N0YXRlKSxcbiAgICApID0+IHtcbiAgICAgIHNldFN1Z2dlc3Rpb25zU3RhdGVSYXcocHJldiA9PlxuICAgICAgICB0eXBlb2YgdXBkYXRlciA9PT0gJ2Z1bmN0aW9uJyA/IHVwZGF0ZXIocHJldikgOiB1cGRhdGVyLFxuICAgICAgKVxuICAgIH0sXG4gICAgW10sXG4gIClcblxuICBjb25zdCBvblN1Ym1pdCA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jIChpbnB1dFBhcmFtOiBzdHJpbmcsIGlzU3VibWl0dGluZ1NsYXNoQ29tbWFuZCA9IGZhbHNlKSA9PiB7XG4gICAgICBpbnB1dFBhcmFtID0gaW5wdXRQYXJhbS50cmltRW5kKClcblxuICAgICAgLy8gRG9uJ3Qgc3VibWl0IGlmIGEgZm9vdGVyIGluZGljYXRvciBpcyBiZWluZyBvcGVuZWQuIFJlYWQgZnJlc2ggZnJvbVxuICAgICAgLy8gc3RvcmUg4oCUIGZvb3RlcjpvcGVuU2VsZWN0ZWQgY2FsbHMgc2VsZWN0Rm9vdGVySXRlbShudWxsKSB0aGVuIG9uU3VibWl0XG4gICAgICAvLyBpbiB0aGUgc2FtZSB0aWNrLCBhbmQgdGhlIGNsb3N1cmUgdmFsdWUgaGFzbid0IHVwZGF0ZWQgeWV0LiBBcHBseSB0aGVcbiAgICAgIC8vIHNhbWUgXCJzdGlsbCB2aXNpYmxlP1wiIGRlcml2YXRpb24gYXMgZm9vdGVySXRlbVNlbGVjdGVkIHNvIGEgc3RhbGVcbiAgICAgIC8vIHNlbGVjdGlvbiAocGlsbCBkaXNhcHBlYXJlZCkgZG9lc24ndCBzd2FsbG93IEVudGVyLlxuICAgICAgY29uc3Qgc3RhdGUgPSBzdG9yZS5nZXRTdGF0ZSgpXG4gICAgICBpZiAoXG4gICAgICAgIHN0YXRlLmZvb3RlclNlbGVjdGlvbiAmJlxuICAgICAgICBmb290ZXJJdGVtcy5pbmNsdWRlcyhzdGF0ZS5mb290ZXJTZWxlY3Rpb24pXG4gICAgICApIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIEVudGVyIGluIHNlbGVjdGlvbiBtb2RlcyBjb25maXJtcyBzZWxlY3Rpb24gKHVzZUJhY2tncm91bmRUYXNrTmF2aWdhdGlvbikuXG4gICAgICAvLyBCYXNlVGV4dElucHV0J3MgdXNlSW5wdXQgcmVnaXN0ZXJzIGJlZm9yZSB0aGF0IGhvb2sgKGNoaWxkIGVmZmVjdHMgZmlyZSBmaXJzdCksXG4gICAgICAvLyBzbyB3aXRob3V0IHRoaXMgZ3VhcmQgRW50ZXIgd291bGQgZG91YmxlLWZpcmUgYW5kIGF1dG8tc3VibWl0IHRoZSBzdWdnZXN0aW9uLlxuICAgICAgaWYgKHN0YXRlLnZpZXdTZWxlY3Rpb25Nb2RlID09PSAnc2VsZWN0aW5nLWFnZW50Jykge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgZm9yIGltYWdlcyBlYXJseSAtIHdlIG5lZWQgdGhpcyBmb3Igc3VnZ2VzdGlvbiBsb2dpYyBiZWxvd1xuICAgICAgY29uc3QgaGFzSW1hZ2VzID0gT2JqZWN0LnZhbHVlcyhwYXN0ZWRDb250ZW50cykuc29tZShcbiAgICAgICAgYyA9PiBjLnR5cGUgPT09ICdpbWFnZScsXG4gICAgICApXG5cbiAgICAgIC8vIElmIGlucHV0IGlzIGVtcHR5IE9SIG1hdGNoZXMgdGhlIHN1Z2dlc3Rpb24sIHN1Ym1pdCBpdFxuICAgICAgLy8gQnV0IGlmIHRoZXJlIGFyZSBpbWFnZXMgYXR0YWNoZWQsIGRvbid0IGF1dG8tYWNjZXB0IHRoZSBzdWdnZXN0aW9uIC1cbiAgICAgIC8vIHRoZSB1c2VyIHdhbnRzIHRvIHN1Ym1pdCBqdXN0IHRoZSBpbWFnZShzKS5cbiAgICAgIC8vIE9ubHkgaW4gbGVhZGVyIHZpZXcg4oCUIHByb21wdFN1Z2dlc3Rpb24gaXMgbGVhZGVyLWNvbnRleHQsIG5vdCB0ZWFtbWF0ZS5cbiAgICAgIGNvbnN0IHN1Z2dlc3Rpb25UZXh0ID0gcHJvbXB0U3VnZ2VzdGlvblN0YXRlLnRleHRcbiAgICAgIGNvbnN0IGlucHV0TWF0Y2hlc1N1Z2dlc3Rpb24gPVxuICAgICAgICBpbnB1dFBhcmFtLnRyaW0oKSA9PT0gJycgfHwgaW5wdXRQYXJhbSA9PT0gc3VnZ2VzdGlvblRleHRcbiAgICAgIGlmIChcbiAgICAgICAgaW5wdXRNYXRjaGVzU3VnZ2VzdGlvbiAmJlxuICAgICAgICBzdWdnZXN0aW9uVGV4dCAmJlxuICAgICAgICAhaGFzSW1hZ2VzICYmXG4gICAgICAgICFzdGF0ZS52aWV3aW5nQWdlbnRUYXNrSWRcbiAgICAgICkge1xuICAgICAgICAvLyBJZiBzcGVjdWxhdGlvbiBpcyBhY3RpdmUsIGluamVjdCBtZXNzYWdlcyBpbW1lZGlhdGVseSBhcyB0aGV5IHN0cmVhbVxuICAgICAgICBpZiAoc3BlY3VsYXRpb24uc3RhdHVzID09PSAnYWN0aXZlJykge1xuICAgICAgICAgIG1hcmtBY2NlcHRlZCgpXG4gICAgICAgICAgLy8gc2tpcFJlc2V0OiByZXNldFN1Z2dlc3Rpb24gd291bGQgYWJvcnQgdGhlIHNwZWN1bGF0aW9uIGJlZm9yZSB3ZSBhY2NlcHQgaXRcbiAgICAgICAgICBsb2dPdXRjb21lQXRTdWJtaXNzaW9uKHN1Z2dlc3Rpb25UZXh0LCB7IHNraXBSZXNldDogdHJ1ZSB9KVxuXG4gICAgICAgICAgdm9pZCBvblN1Ym1pdFByb3AoXG4gICAgICAgICAgICBzdWdnZXN0aW9uVGV4dCxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0LFxuICAgICAgICAgICAgICBjbGVhckJ1ZmZlcixcbiAgICAgICAgICAgICAgcmVzZXRIaXN0b3J5LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgc3RhdGU6IHNwZWN1bGF0aW9uLFxuICAgICAgICAgICAgICBzcGVjdWxhdGlvblNlc3Npb25UaW1lU2F2ZWRNczogc3BlY3VsYXRpb25TZXNzaW9uVGltZVNhdmVkTXMsXG4gICAgICAgICAgICAgIHNldEFwcFN0YXRlLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICApXG4gICAgICAgICAgcmV0dXJuIC8vIFNraXAgbm9ybWFsIHF1ZXJ5IC0gc3BlY3VsYXRpb24gaGFuZGxlZCBpdFxuICAgICAgICB9XG5cbiAgICAgICAgLy8gUmVndWxhciBzdWdnZXN0aW9uIGFjY2VwdGFuY2UgKHJlcXVpcmVzIHNob3duQXQgPiAwKVxuICAgICAgICBpZiAocHJvbXB0U3VnZ2VzdGlvblN0YXRlLnNob3duQXQgPiAwKSB7XG4gICAgICAgICAgbWFya0FjY2VwdGVkKClcbiAgICAgICAgICBpbnB1dFBhcmFtID0gc3VnZ2VzdGlvblRleHRcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBIYW5kbGUgQG5hbWUgZGlyZWN0IG1lc3NhZ2VcbiAgICAgIGlmIChpc0FnZW50U3dhcm1zRW5hYmxlZCgpKSB7XG4gICAgICAgIGNvbnN0IGRpcmVjdE1lc3NhZ2UgPSBwYXJzZURpcmVjdE1lbWJlck1lc3NhZ2UoaW5wdXRQYXJhbSlcbiAgICAgICAgaWYgKGRpcmVjdE1lc3NhZ2UpIHtcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzZW5kRGlyZWN0TWVtYmVyTWVzc2FnZShcbiAgICAgICAgICAgIGRpcmVjdE1lc3NhZ2UucmVjaXBpZW50TmFtZSxcbiAgICAgICAgICAgIGRpcmVjdE1lc3NhZ2UubWVzc2FnZSxcbiAgICAgICAgICAgIHRlYW1Db250ZXh0LFxuICAgICAgICAgICAgd3JpdGVUb01haWxib3gsXG4gICAgICAgICAgKVxuXG4gICAgICAgICAgaWYgKHJlc3VsdC5zdWNjZXNzKSB7XG4gICAgICAgICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICAgICAgICBrZXk6ICdkaXJlY3QtbWVzc2FnZS1zZW50JyxcbiAgICAgICAgICAgICAgdGV4dDogYFNlbnQgdG8gQCR7cmVzdWx0LnJlY2lwaWVudE5hbWV9YCxcbiAgICAgICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgICAgICB0aW1lb3V0TXM6IDMwMDAsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgdHJhY2tBbmRTZXRJbnB1dCgnJylcbiAgICAgICAgICAgIHNldEN1cnNvck9mZnNldCgwKVxuICAgICAgICAgICAgY2xlYXJCdWZmZXIoKVxuICAgICAgICAgICAgcmVzZXRIaXN0b3J5KClcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH0gZWxzZSBpZiAocmVzdWx0LmVycm9yID09PSAnbm9fdGVhbV9jb250ZXh0Jykge1xuICAgICAgICAgICAgLy8gTm8gdGVhbSBjb250ZXh0IC0gZmFsbCB0aHJvdWdoIHRvIG5vcm1hbCBwcm9tcHQgc3VibWlzc2lvblxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBVbmtub3duIHJlY2lwaWVudCAtIGZhbGwgdGhyb3VnaCB0byBub3JtYWwgcHJvbXB0IHN1Ym1pc3Npb25cbiAgICAgICAgICAgIC8vIFRoaXMgYWxsb3dzIGUuZy4gXCJAdXRpbHMgZXhwbGFpbiB0aGlzIGNvZGVcIiB0byBiZSBzZW50IGFzIGEgcHJvbXB0XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEFsbG93IHN1Ym1pc3Npb24gaWYgdGhlcmUgYXJlIGltYWdlcyBhdHRhY2hlZCwgZXZlbiB3aXRob3V0IHRleHRcbiAgICAgIGlmIChpbnB1dFBhcmFtLnRyaW0oKSA9PT0gJycgJiYgIWhhc0ltYWdlcykge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gUHJvbXB0SW5wdXQgVVg6IENoZWNrIGlmIHN1Z2dlc3Rpb25zIGRyb3Bkb3duIGlzIHNob3dpbmdcbiAgICAgIC8vIEZvciBkaXJlY3Rvcnkgc3VnZ2VzdGlvbnMsIGFsbG93IHN1Ym1pc3Npb24gKFRhYiBpcyB1c2VkIGZvciBjb21wbGV0aW9uKVxuICAgICAgY29uc3QgaGFzRGlyZWN0b3J5U3VnZ2VzdGlvbnMgPVxuICAgICAgICBzdWdnZXN0aW9uc1N0YXRlLnN1Z2dlc3Rpb25zLmxlbmd0aCA+IDAgJiZcbiAgICAgICAgc3VnZ2VzdGlvbnNTdGF0ZS5zdWdnZXN0aW9ucy5ldmVyeShzID0+IHMuZGVzY3JpcHRpb24gPT09ICdkaXJlY3RvcnknKVxuXG4gICAgICBpZiAoXG4gICAgICAgIHN1Z2dlc3Rpb25zU3RhdGUuc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCAmJlxuICAgICAgICAhaXNTdWJtaXR0aW5nU2xhc2hDb21tYW5kICYmXG4gICAgICAgICFoYXNEaXJlY3RvcnlTdWdnZXN0aW9uc1xuICAgICAgKSB7XG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICBgW29uU3VibWl0XSBlYXJseSByZXR1cm46IHN1Z2dlc3Rpb25zIHNob3dpbmcgKGNvdW50PSR7c3VnZ2VzdGlvbnNTdGF0ZS5zdWdnZXN0aW9ucy5sZW5ndGh9KWAsXG4gICAgICAgIClcbiAgICAgICAgcmV0dXJuIC8vIERvbid0IHN1Ym1pdCwgdXNlciBuZWVkcyB0byBjbGVhciBzdWdnZXN0aW9ucyBmaXJzdFxuICAgICAgfVxuXG4gICAgICAvLyBMb2cgc3VnZ2VzdGlvbiBvdXRjb21lIGlmIG9uZSBleGlzdHNcbiAgICAgIGlmIChwcm9tcHRTdWdnZXN0aW9uU3RhdGUudGV4dCAmJiBwcm9tcHRTdWdnZXN0aW9uU3RhdGUuc2hvd25BdCA+IDApIHtcbiAgICAgICAgbG9nT3V0Y29tZUF0U3VibWlzc2lvbihpbnB1dFBhcmFtKVxuICAgICAgfVxuXG4gICAgICAvLyBDbGVhciBzdGFzaCBoaW50IG5vdGlmaWNhdGlvbiBvbiBzdWJtaXRcbiAgICAgIHJlbW92ZU5vdGlmaWNhdGlvbignc3Rhc2gtaGludCcpXG5cbiAgICAgIC8vIFJvdXRlIGlucHV0IHRvIHZpZXdlZCBhZ2VudCAoaW4tcHJvY2VzcyB0ZWFtbWF0ZSBvciBuYW1lZCBsb2NhbF9hZ2VudCkuXG4gICAgICBjb25zdCBhY3RpdmVBZ2VudCA9IGdldEFjdGl2ZUFnZW50Rm9ySW5wdXQoc3RvcmUuZ2V0U3RhdGUoKSlcbiAgICAgIGlmIChhY3RpdmVBZ2VudC50eXBlICE9PSAnbGVhZGVyJyAmJiBvbkFnZW50U3VibWl0KSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV90cmFuc2NyaXB0X2lucHV0X3RvX3RlYW1tYXRlJywge30pXG4gICAgICAgIGF3YWl0IG9uQWdlbnRTdWJtaXQoaW5wdXRQYXJhbSwgYWN0aXZlQWdlbnQudGFzaywge1xuICAgICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgICBjbGVhckJ1ZmZlcixcbiAgICAgICAgICByZXNldEhpc3RvcnksXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBOb3JtYWwgbGVhZGVyIHN1Ym1pc3Npb25cbiAgICAgIGF3YWl0IG9uU3VibWl0UHJvcChpbnB1dFBhcmFtLCB7XG4gICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgY2xlYXJCdWZmZXIsXG4gICAgICAgIHJlc2V0SGlzdG9yeSxcbiAgICAgIH0pXG4gICAgfSxcbiAgICBbXG4gICAgICBwcm9tcHRTdWdnZXN0aW9uU3RhdGUsXG4gICAgICBzcGVjdWxhdGlvbixcbiAgICAgIHNwZWN1bGF0aW9uU2Vzc2lvblRpbWVTYXZlZE1zLFxuICAgICAgdGVhbUNvbnRleHQsXG4gICAgICBzdG9yZSxcbiAgICAgIGZvb3Rlckl0ZW1zLFxuICAgICAgc3VnZ2VzdGlvbnNTdGF0ZS5zdWdnZXN0aW9ucyxcbiAgICAgIG9uU3VibWl0UHJvcCxcbiAgICAgIG9uQWdlbnRTdWJtaXQsXG4gICAgICBjbGVhckJ1ZmZlcixcbiAgICAgIHJlc2V0SGlzdG9yeSxcbiAgICAgIGxvZ091dGNvbWVBdFN1Ym1pc3Npb24sXG4gICAgICBzZXRBcHBTdGF0ZSxcbiAgICAgIG1hcmtBY2NlcHRlZCxcbiAgICAgIHBhc3RlZENvbnRlbnRzLFxuICAgICAgcmVtb3ZlTm90aWZpY2F0aW9uLFxuICAgIF0sXG4gIClcblxuICBjb25zdCB7XG4gICAgc3VnZ2VzdGlvbnMsXG4gICAgc2VsZWN0ZWRTdWdnZXN0aW9uLFxuICAgIGNvbW1hbmRBcmd1bWVudEhpbnQsXG4gICAgaW5saW5lR2hvc3RUZXh0LFxuICAgIG1heENvbHVtbldpZHRoLFxuICB9ID0gdXNlVHlwZWFoZWFkKHtcbiAgICBjb21tYW5kcyxcbiAgICBvbklucHV0Q2hhbmdlOiB0cmFja0FuZFNldElucHV0LFxuICAgIG9uU3VibWl0LFxuICAgIHNldEN1cnNvck9mZnNldCxcbiAgICBpbnB1dCxcbiAgICBjdXJzb3JPZmZzZXQsXG4gICAgbW9kZSxcbiAgICBhZ2VudHMsXG4gICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZSxcbiAgICBzdWdnZXN0aW9uc1N0YXRlLFxuICAgIHN1cHByZXNzU3VnZ2VzdGlvbnM6IGlzU2VhcmNoaW5nSGlzdG9yeSB8fCBoaXN0b3J5SW5kZXggPiAwLFxuICAgIG1hcmtBY2NlcHRlZCxcbiAgICBvbk1vZGVDaGFuZ2UsXG4gIH0pXG5cbiAgLy8gVHJhY2sgaWYgcHJvbXB0IHN1Z2dlc3Rpb24gc2hvdWxkIGJlIHNob3duIChjb21wdXRlZCBsYXRlciB3aXRoIHRlcm1pbmFsIHdpZHRoKS5cbiAgLy8gSGlkZGVuIGluIHRlYW1tYXRlIHZpZXcg4oCUIHN1Z2dlc3Rpb24gaXMgbGVhZGVyLWNvbnRleHQgb25seS5cbiAgY29uc3Qgc2hvd1Byb21wdFN1Z2dlc3Rpb24gPVxuICAgIG1vZGUgPT09ICdwcm9tcHQnICYmXG4gICAgc3VnZ2VzdGlvbnMubGVuZ3RoID09PSAwICYmXG4gICAgcHJvbXB0U3VnZ2VzdGlvbiAmJlxuICAgICF2aWV3aW5nQWdlbnRUYXNrSWRcbiAgaWYgKHNob3dQcm9tcHRTdWdnZXN0aW9uKSB7XG4gICAgbWFya1Nob3duKClcbiAgfVxuXG4gIC8vIElmIHN1Z2dlc3Rpb24gd2FzIGdlbmVyYXRlZCBidXQgY2FuJ3QgYmUgc2hvd24gZHVlIHRvIHRpbWluZywgbG9nIHN1cHByZXNzaW9uLlxuICAvLyBFeGNsdWRlIHRlYW1tYXRlIHZpZXc6IG1hcmtTaG93bigpIGlzIGdhdGVkIGFib3ZlLCBzbyBzaG93bkF0IHN0YXlzIDAgdGhlcmUg4oCUXG4gIC8vIGJ1dCB0aGF0J3Mgbm90IGEgdGltaW5nIGZhaWx1cmUsIHRoZSBzdWdnZXN0aW9uIGlzIHZhbGlkIHdoZW4gcmV0dXJuaW5nIHRvIGxlYWRlci5cbiAgaWYgKFxuICAgIHByb21wdFN1Z2dlc3Rpb25TdGF0ZS50ZXh0ICYmXG4gICAgIXByb21wdFN1Z2dlc3Rpb24gJiZcbiAgICBwcm9tcHRTdWdnZXN0aW9uU3RhdGUuc2hvd25BdCA9PT0gMCAmJlxuICAgICF2aWV3aW5nQWdlbnRUYXNrSWRcbiAgKSB7XG4gICAgbG9nU3VnZ2VzdGlvblN1cHByZXNzZWQoJ3RpbWluZycsIHByb21wdFN1Z2dlc3Rpb25TdGF0ZS50ZXh0KVxuICAgIHNldEFwcFN0YXRlKHByZXYgPT4gKHtcbiAgICAgIC4uLnByZXYsXG4gICAgICBwcm9tcHRTdWdnZXN0aW9uOiB7XG4gICAgICAgIHRleHQ6IG51bGwsXG4gICAgICAgIHByb21wdElkOiBudWxsLFxuICAgICAgICBzaG93bkF0OiAwLFxuICAgICAgICBhY2NlcHRlZEF0OiAwLFxuICAgICAgICBnZW5lcmF0aW9uUmVxdWVzdElkOiBudWxsLFxuICAgICAgfSxcbiAgICB9KSlcbiAgfVxuXG4gIGZ1bmN0aW9uIG9uSW1hZ2VQYXN0ZShcbiAgICBpbWFnZTogc3RyaW5nLFxuICAgIG1lZGlhVHlwZT86IHN0cmluZyxcbiAgICBmaWxlbmFtZT86IHN0cmluZyxcbiAgICBkaW1lbnNpb25zPzogSW1hZ2VEaW1lbnNpb25zLFxuICAgIHNvdXJjZVBhdGg/OiBzdHJpbmcsXG4gICkge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV9wYXN0ZV9pbWFnZScsIHt9KVxuICAgIG9uTW9kZUNoYW5nZSgncHJvbXB0JylcblxuICAgIGNvbnN0IHBhc3RlSWQgPSBuZXh0UGFzdGVJZFJlZi5jdXJyZW50KytcblxuICAgIGNvbnN0IG5ld0NvbnRlbnQ6IFBhc3RlZENvbnRlbnQgPSB7XG4gICAgICBpZDogcGFzdGVJZCxcbiAgICAgIHR5cGU6ICdpbWFnZScsXG4gICAgICBjb250ZW50OiBpbWFnZSxcbiAgICAgIG1lZGlhVHlwZTogbWVkaWFUeXBlIHx8ICdpbWFnZS9wbmcnLCAvLyBkZWZhdWx0IHRvIFBORyBpZiBub3QgcHJvdmlkZWRcbiAgICAgIGZpbGVuYW1lOiBmaWxlbmFtZSB8fCAnUGFzdGVkIGltYWdlJyxcbiAgICAgIGRpbWVuc2lvbnMsXG4gICAgICBzb3VyY2VQYXRoLFxuICAgIH1cblxuICAgIC8vIENhY2hlIHBhdGggaW1tZWRpYXRlbHkgKGZhc3QpIHNvIGxpbmtzIHdvcmsgb24gcmVuZGVyXG4gICAgY2FjaGVJbWFnZVBhdGgobmV3Q29udGVudClcblxuICAgIC8vIFN0b3JlIGltYWdlIHRvIGRpc2sgaW4gYmFja2dyb3VuZFxuICAgIHZvaWQgc3RvcmVJbWFnZShuZXdDb250ZW50KVxuXG4gICAgLy8gVXBkYXRlIFVJXG4gICAgc2V0UGFzdGVkQ29udGVudHMocHJldiA9PiAoeyAuLi5wcmV2LCBbcGFzdGVJZF06IG5ld0NvbnRlbnQgfSkpXG4gICAgLy8gTXVsdGktaW1hZ2UgcGFzdGUgY2FsbHMgb25JbWFnZVBhc3RlIGluIGEgbG9vcC4gSWYgdGhlIHJlZiBpcyBhbHJlYWR5XG4gICAgLy8gYXJtZWQsIHRoZSBwcmV2aW91cyBwaWxsJ3MgbGF6eSBzcGFjZSBmaXJlcyBub3cgKGJlZm9yZSB0aGlzIHBpbGwpXG4gICAgLy8gcmF0aGVyIHRoYW4gYmVpbmcgbG9zdC5cbiAgICBjb25zdCBwcmVmaXggPSBwZW5kaW5nU3BhY2VBZnRlclBpbGxSZWYuY3VycmVudCA/ICcgJyA6ICcnXG4gICAgaW5zZXJ0VGV4dEF0Q3Vyc29yKHByZWZpeCArIGZvcm1hdEltYWdlUmVmKHBhc3RlSWQpKVxuICAgIHBlbmRpbmdTcGFjZUFmdGVyUGlsbFJlZi5jdXJyZW50ID0gdHJ1ZVxuICB9XG5cbiAgLy8gUHJ1bmUgaW1hZ2VzIHdob3NlIFtJbWFnZSAjTl0gcGxhY2Vob2xkZXIgaXMgbm8gbG9uZ2VyIGluIHRoZSBpbnB1dCB0ZXh0LlxuICAvLyBDb3ZlcnMgcGlsbCBiYWNrc3BhY2UsIEN0cmwrVSwgY2hhci1ieS1jaGFyIGRlbGV0aW9uIOKAlCBhbnkgZWRpdCB0aGF0IGRyb3BzXG4gIC8vIHRoZSByZWYuIG9uSW1hZ2VQYXN0ZSBiYXRjaGVzIHNldFBhc3RlZENvbnRlbnRzICsgaW5zZXJ0VGV4dEF0Q3Vyc29yIGluIHRoZVxuICAvLyBzYW1lIGV2ZW50LCBzbyB0aGlzIGVmZmVjdCBzZWVzIHRoZSBwbGFjZWhvbGRlciBhbHJlYWR5IHByZXNlbnQuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc3QgcmVmZXJlbmNlZElkcyA9IG5ldyBTZXQocGFyc2VSZWZlcmVuY2VzKGlucHV0KS5tYXAociA9PiByLmlkKSlcbiAgICBzZXRQYXN0ZWRDb250ZW50cyhwcmV2ID0+IHtcbiAgICAgIGNvbnN0IG9ycGhhbmVkID0gT2JqZWN0LnZhbHVlcyhwcmV2KS5maWx0ZXIoXG4gICAgICAgIGMgPT4gYy50eXBlID09PSAnaW1hZ2UnICYmICFyZWZlcmVuY2VkSWRzLmhhcyhjLmlkKSxcbiAgICAgIClcbiAgICAgIGlmIChvcnBoYW5lZC5sZW5ndGggPT09IDApIHJldHVybiBwcmV2XG4gICAgICBjb25zdCBuZXh0ID0geyAuLi5wcmV2IH1cbiAgICAgIGZvciAoY29uc3QgaW1nIG9mIG9ycGhhbmVkKSBkZWxldGUgbmV4dFtpbWcuaWRdXG4gICAgICByZXR1cm4gbmV4dFxuICAgIH0pXG4gIH0sIFtpbnB1dCwgc2V0UGFzdGVkQ29udGVudHNdKVxuXG4gIGZ1bmN0aW9uIG9uVGV4dFBhc3RlKHJhd1RleHQ6IHN0cmluZykge1xuICAgIHBlbmRpbmdTcGFjZUFmdGVyUGlsbFJlZi5jdXJyZW50ID0gZmFsc2VcbiAgICAvLyBDbGVhbiB1cCBwYXN0ZWQgdGV4dCAtIHN0cmlwIEFOU0kgZXNjYXBlIGNvZGVzIGFuZCBub3JtYWxpemUgbGluZSBlbmRpbmdzIGFuZCB0YWJzXG4gICAgbGV0IHRleHQgPSBzdHJpcEFuc2kocmF3VGV4dCkucmVwbGFjZSgvXFxyL2csICdcXG4nKS5yZXBsYWNlQWxsKCdcXHQnLCAnICAgICcpXG5cbiAgICAvLyBNYXRjaCB0eXBlZC9hdXRvLXN1Z2dlc3Q6IGAhY21kYCBwYXN0ZWQgaW50byBlbXB0eSBpbnB1dCBlbnRlcnMgYmFzaCBtb2RlLlxuICAgIGlmIChpbnB1dC5sZW5ndGggPT09IDApIHtcbiAgICAgIGNvbnN0IHBhc3RlZE1vZGUgPSBnZXRNb2RlRnJvbUlucHV0KHRleHQpXG4gICAgICBpZiAocGFzdGVkTW9kZSAhPT0gJ3Byb21wdCcpIHtcbiAgICAgICAgb25Nb2RlQ2hhbmdlKHBhc3RlZE1vZGUpXG4gICAgICAgIHRleHQgPSBnZXRWYWx1ZUZyb21JbnB1dCh0ZXh0KVxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IG51bUxpbmVzID0gZ2V0UGFzdGVkVGV4dFJlZk51bUxpbmVzKHRleHQpXG4gICAgLy8gTGltaXQgdGhlIG51bWJlciBvZiBsaW5lcyB0byBzaG93IGluIHRoZSBpbnB1dFxuICAgIC8vIElmIHRoZSBvdmVyYWxsIGxheW91dCBpcyB0b28gaGlnaCB0aGVuIEluayB3aWxsIHJlcGFpbnRcbiAgICAvLyB0aGUgZW50aXJlIHRlcm1pbmFsLlxuICAgIC8vIFRoZSBhY3R1YWwgcmVxdWlyZWQgaGVpZ2h0IGlzIGRlcGVuZGVudCBvbiB0aGUgY29udGVudCwgdGhpc1xuICAgIC8vIGlzIGp1c3QgYW4gZXN0aW1hdGUuXG4gICAgY29uc3QgbWF4TGluZXMgPSBNYXRoLm1pbihyb3dzIC0gMTAsIDIpXG5cbiAgICAvLyBVc2Ugc3BlY2lhbCBoYW5kbGluZyBmb3IgbG9uZyBwYXN0ZWQgdGV4dCAoPlBBU1RFX1RIUkVTSE9MRCBjaGFycylcbiAgICAvLyBvciBpZiBpdCBleGNlZWRzIHRoZSBudW1iZXIgb2YgbGluZXMgd2Ugd2FudCB0byBzaG93XG4gICAgaWYgKHRleHQubGVuZ3RoID4gUEFTVEVfVEhSRVNIT0xEIHx8IG51bUxpbmVzID4gbWF4TGluZXMpIHtcbiAgICAgIGNvbnN0IHBhc3RlSWQgPSBuZXh0UGFzdGVJZFJlZi5jdXJyZW50KytcblxuICAgICAgY29uc3QgbmV3Q29udGVudDogUGFzdGVkQ29udGVudCA9IHtcbiAgICAgICAgaWQ6IHBhc3RlSWQsXG4gICAgICAgIHR5cGU6ICd0ZXh0JyxcbiAgICAgICAgY29udGVudDogdGV4dCxcbiAgICAgIH1cblxuICAgICAgc2V0UGFzdGVkQ29udGVudHMocHJldiA9PiAoeyAuLi5wcmV2LCBbcGFzdGVJZF06IG5ld0NvbnRlbnQgfSkpXG5cbiAgICAgIGluc2VydFRleHRBdEN1cnNvcihmb3JtYXRQYXN0ZWRUZXh0UmVmKHBhc3RlSWQsIG51bUxpbmVzKSlcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gRm9yIHNob3J0ZXIgcGFzdGVzLCBqdXN0IGluc2VydCB0aGUgdGV4dCBub3JtYWxseVxuICAgICAgaW5zZXJ0VGV4dEF0Q3Vyc29yKHRleHQpXG4gICAgfVxuICB9XG5cbiAgY29uc3QgbGF6eVNwYWNlSW5wdXRGaWx0ZXIgPSB1c2VDYWxsYmFjayhcbiAgICAoaW5wdXQ6IHN0cmluZywga2V5OiBLZXkpOiBzdHJpbmcgPT4ge1xuICAgICAgaWYgKCFwZW5kaW5nU3BhY2VBZnRlclBpbGxSZWYuY3VycmVudCkgcmV0dXJuIGlucHV0XG4gICAgICBwZW5kaW5nU3BhY2VBZnRlclBpbGxSZWYuY3VycmVudCA9IGZhbHNlXG4gICAgICBpZiAoaXNOb25TcGFjZVByaW50YWJsZShpbnB1dCwga2V5KSkgcmV0dXJuICcgJyArIGlucHV0XG4gICAgICByZXR1cm4gaW5wdXRcbiAgICB9LFxuICAgIFtdLFxuICApXG5cbiAgZnVuY3Rpb24gaW5zZXJ0VGV4dEF0Q3Vyc29yKHRleHQ6IHN0cmluZykge1xuICAgIC8vIFB1c2ggY3VycmVudCBzdGF0ZSB0byBidWZmZXIgYmVmb3JlIGluc2VydGluZ1xuICAgIHB1c2hUb0J1ZmZlcihpbnB1dCwgY3Vyc29yT2Zmc2V0LCBwYXN0ZWRDb250ZW50cylcblxuICAgIGNvbnN0IG5ld0lucHV0ID1cbiAgICAgIGlucHV0LnNsaWNlKDAsIGN1cnNvck9mZnNldCkgKyB0ZXh0ICsgaW5wdXQuc2xpY2UoY3Vyc29yT2Zmc2V0KVxuICAgIHRyYWNrQW5kU2V0SW5wdXQobmV3SW5wdXQpXG4gICAgc2V0Q3Vyc29yT2Zmc2V0KGN1cnNvck9mZnNldCArIHRleHQubGVuZ3RoKVxuICB9XG5cbiAgY29uc3QgZG91YmxlUHJlc3NFc2NGcm9tRW1wdHkgPSB1c2VEb3VibGVQcmVzcyhcbiAgICAoKSA9PiB7fSxcbiAgICAoKSA9PiBvblNob3dNZXNzYWdlU2VsZWN0b3IoKSxcbiAgKVxuXG4gIC8vIEZ1bmN0aW9uIHRvIGdldCB0aGUgcXVldWVkIGNvbW1hbmQgZm9yIGVkaXRpbmcuIFJldHVybnMgdHJ1ZSBpZiBjb21tYW5kcyB3ZXJlIHBvcHBlZC5cbiAgY29uc3QgcG9wQWxsQ29tbWFuZHNGcm9tUXVldWUgPSB1c2VDYWxsYmFjaygoKTogYm9vbGVhbiA9PiB7XG4gICAgY29uc3QgcmVzdWx0ID0gcG9wQWxsRWRpdGFibGUoaW5wdXQsIGN1cnNvck9mZnNldClcbiAgICBpZiAoIXJlc3VsdCkge1xuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgdHJhY2tBbmRTZXRJbnB1dChyZXN1bHQudGV4dClcbiAgICBvbk1vZGVDaGFuZ2UoJ3Byb21wdCcpIC8vIEFsd2F5cyBwcm9tcHQgbW9kZSBmb3IgcXVldWVkIGNvbW1hbmRzXG4gICAgc2V0Q3Vyc29yT2Zmc2V0KHJlc3VsdC5jdXJzb3JPZmZzZXQpXG5cbiAgICAvLyBSZXN0b3JlIGltYWdlcyBmcm9tIHF1ZXVlZCBjb21tYW5kcyB0byBwYXN0ZWRDb250ZW50c1xuICAgIGlmIChyZXN1bHQuaW1hZ2VzLmxlbmd0aCA+IDApIHtcbiAgICAgIHNldFBhc3RlZENvbnRlbnRzKHByZXYgPT4ge1xuICAgICAgICBjb25zdCBuZXdDb250ZW50cyA9IHsgLi4ucHJldiB9XG4gICAgICAgIGZvciAoY29uc3QgaW1hZ2Ugb2YgcmVzdWx0LmltYWdlcykge1xuICAgICAgICAgIG5ld0NvbnRlbnRzW2ltYWdlLmlkXSA9IGltYWdlXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ld0NvbnRlbnRzXG4gICAgICB9KVxuICAgIH1cblxuICAgIHJldHVybiB0cnVlXG4gIH0sIFt0cmFja0FuZFNldElucHV0LCBvbk1vZGVDaGFuZ2UsIGlucHV0LCBjdXJzb3JPZmZzZXQsIHNldFBhc3RlZENvbnRlbnRzXSlcblxuICAvLyBJbnNlcnQgdGhlIGF0LW1lbnRpb25lZCByZWZlcmVuY2UgKHRoZSBmaWxlIGFuZCwgb3B0aW9uYWxseSwgYSBsaW5lIHJhbmdlKSB3aGVuXG4gIC8vIHdlIHJlY2VpdmUgYW4gYXQtbWVudGlvbmVkIG5vdGlmaWNhdGlvbiB0aGUgSURFLlxuICBjb25zdCBvbklkZUF0TWVudGlvbmVkID0gZnVuY3Rpb24gKGF0TWVudGlvbmVkOiBJREVBdE1lbnRpb25lZCkge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV9leHRfYXRfbWVudGlvbmVkJywge30pXG4gICAgbGV0IGF0TWVudGlvbmVkVGV4dDogc3RyaW5nXG4gICAgY29uc3QgcmVsYXRpdmVQYXRoID0gcGF0aC5yZWxhdGl2ZShnZXRDd2QoKSwgYXRNZW50aW9uZWQuZmlsZVBhdGgpXG4gICAgaWYgKGF0TWVudGlvbmVkLmxpbmVTdGFydCAmJiBhdE1lbnRpb25lZC5saW5lRW5kKSB7XG4gICAgICBhdE1lbnRpb25lZFRleHQgPVxuICAgICAgICBhdE1lbnRpb25lZC5saW5lU3RhcnQgPT09IGF0TWVudGlvbmVkLmxpbmVFbmRcbiAgICAgICAgICA/IGBAJHtyZWxhdGl2ZVBhdGh9I0wke2F0TWVudGlvbmVkLmxpbmVTdGFydH0gYFxuICAgICAgICAgIDogYEAke3JlbGF0aXZlUGF0aH0jTCR7YXRNZW50aW9uZWQubGluZVN0YXJ0fS0ke2F0TWVudGlvbmVkLmxpbmVFbmR9IGBcbiAgICB9IGVsc2Uge1xuICAgICAgYXRNZW50aW9uZWRUZXh0ID0gYEAke3JlbGF0aXZlUGF0aH0gYFxuICAgIH1cbiAgICBjb25zdCBjdXJzb3JDaGFyID0gaW5wdXRbY3Vyc29yT2Zmc2V0IC0gMV0gPz8gJyAnXG4gICAgaWYgKCEvXFxzLy50ZXN0KGN1cnNvckNoYXIpKSB7XG4gICAgICBhdE1lbnRpb25lZFRleHQgPSBgICR7YXRNZW50aW9uZWRUZXh0fWBcbiAgICB9XG4gICAgaW5zZXJ0VGV4dEF0Q3Vyc29yKGF0TWVudGlvbmVkVGV4dClcbiAgfVxuICB1c2VJZGVBdE1lbnRpb25lZChtY3BDbGllbnRzLCBvbklkZUF0TWVudGlvbmVkKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6dW5kbyAtIHVuZG8gbGFzdCBlZGl0XG4gIGNvbnN0IGhhbmRsZVVuZG8gPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgaWYgKGNhblVuZG8pIHtcbiAgICAgIGNvbnN0IHByZXZpb3VzU3RhdGUgPSB1bmRvKClcbiAgICAgIGlmIChwcmV2aW91c1N0YXRlKSB7XG4gICAgICAgIHRyYWNrQW5kU2V0SW5wdXQocHJldmlvdXNTdGF0ZS50ZXh0KVxuICAgICAgICBzZXRDdXJzb3JPZmZzZXQocHJldmlvdXNTdGF0ZS5jdXJzb3JPZmZzZXQpXG4gICAgICAgIHNldFBhc3RlZENvbnRlbnRzKHByZXZpb3VzU3RhdGUucGFzdGVkQ29udGVudHMpXG4gICAgICB9XG4gICAgfVxuICB9LCBbY2FuVW5kbywgdW5kbywgdHJhY2tBbmRTZXRJbnB1dCwgc2V0UGFzdGVkQ29udGVudHNdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6bmV3bGluZSAtIGluc2VydCBhIG5ld2xpbmUgYXQgdGhlIGN1cnNvciBwb3NpdGlvblxuICBjb25zdCBoYW5kbGVOZXdsaW5lID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHB1c2hUb0J1ZmZlcihpbnB1dCwgY3Vyc29yT2Zmc2V0LCBwYXN0ZWRDb250ZW50cylcbiAgICBjb25zdCBuZXdJbnB1dCA9XG4gICAgICBpbnB1dC5zbGljZSgwLCBjdXJzb3JPZmZzZXQpICsgJ1xcbicgKyBpbnB1dC5zbGljZShjdXJzb3JPZmZzZXQpXG4gICAgdHJhY2tBbmRTZXRJbnB1dChuZXdJbnB1dClcbiAgICBzZXRDdXJzb3JPZmZzZXQoY3Vyc29yT2Zmc2V0ICsgMSlcbiAgfSwgW1xuICAgIGlucHV0LFxuICAgIGN1cnNvck9mZnNldCxcbiAgICB0cmFja0FuZFNldElucHV0LFxuICAgIHNldEN1cnNvck9mZnNldCxcbiAgICBwdXNoVG9CdWZmZXIsXG4gICAgcGFzdGVkQ29udGVudHMsXG4gIF0pXG5cbiAgLy8gSGFuZGxlciBmb3IgY2hhdDpleHRlcm5hbEVkaXRvciAtIGVkaXQgaW4gJEVESVRPUlxuICBjb25zdCBoYW5kbGVFeHRlcm5hbEVkaXRvciA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBsb2dFdmVudCgndGVuZ3VfZXh0ZXJuYWxfZWRpdG9yX3VzZWQnLCB7fSlcbiAgICBzZXRJc0V4dGVybmFsRWRpdG9yQWN0aXZlKHRydWUpXG5cbiAgICB0cnkge1xuICAgICAgLy8gUGFzcyBwYXN0ZWRDb250ZW50cyB0byBleHBhbmQgY29sbGFwc2VkIHRleHQgcmVmZXJlbmNlc1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZWRpdFByb21wdEluRWRpdG9yKGlucHV0LCBwYXN0ZWRDb250ZW50cylcblxuICAgICAgaWYgKHJlc3VsdC5lcnJvcikge1xuICAgICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICAgIGtleTogJ2V4dGVybmFsLWVkaXRvci1lcnJvcicsXG4gICAgICAgICAgdGV4dDogcmVzdWx0LmVycm9yLFxuICAgICAgICAgIGNvbG9yOiAnd2FybmluZycsXG4gICAgICAgICAgcHJpb3JpdHk6ICdoaWdoJyxcbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgaWYgKHJlc3VsdC5jb250ZW50ICE9PSBudWxsICYmIHJlc3VsdC5jb250ZW50ICE9PSBpbnB1dCkge1xuICAgICAgICAvLyBQdXNoIGN1cnJlbnQgc3RhdGUgdG8gYnVmZmVyIGJlZm9yZSBtYWtpbmcgY2hhbmdlc1xuICAgICAgICBwdXNoVG9CdWZmZXIoaW5wdXQsIGN1cnNvck9mZnNldCwgcGFzdGVkQ29udGVudHMpXG5cbiAgICAgICAgdHJhY2tBbmRTZXRJbnB1dChyZXN1bHQuY29udGVudClcbiAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KHJlc3VsdC5jb250ZW50Lmxlbmd0aClcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgICBsb2dFcnJvcihlcnIpXG4gICAgICB9XG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICdleHRlcm5hbC1lZGl0b3ItZXJyb3InLFxuICAgICAgICB0ZXh0OiBgRXh0ZXJuYWwgZWRpdG9yIGZhaWxlZDogJHtlcnJvck1lc3NhZ2UoZXJyKX1gLFxuICAgICAgICBjb2xvcjogJ3dhcm5pbmcnLFxuICAgICAgICBwcmlvcml0eTogJ2hpZ2gnLFxuICAgICAgfSlcbiAgICB9IGZpbmFsbHkge1xuICAgICAgc2V0SXNFeHRlcm5hbEVkaXRvckFjdGl2ZShmYWxzZSlcbiAgICB9XG4gIH0sIFtcbiAgICBpbnB1dCxcbiAgICBjdXJzb3JPZmZzZXQsXG4gICAgcGFzdGVkQ29udGVudHMsXG4gICAgcHVzaFRvQnVmZmVyLFxuICAgIHRyYWNrQW5kU2V0SW5wdXQsXG4gICAgYWRkTm90aWZpY2F0aW9uLFxuICBdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6c3Rhc2ggLSBzdGFzaC91bnN0YXNoIHByb21wdFxuICBjb25zdCBoYW5kbGVTdGFzaCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoaW5wdXQudHJpbSgpID09PSAnJyAmJiBzdGFzaGVkUHJvbXB0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIFBvcCBzdGFzaCB3aGVuIGlucHV0IGlzIGVtcHR5XG4gICAgICB0cmFja0FuZFNldElucHV0KHN0YXNoZWRQcm9tcHQudGV4dClcbiAgICAgIHNldEN1cnNvck9mZnNldChzdGFzaGVkUHJvbXB0LmN1cnNvck9mZnNldClcbiAgICAgIHNldFBhc3RlZENvbnRlbnRzKHN0YXNoZWRQcm9tcHQucGFzdGVkQ29udGVudHMpXG4gICAgICBzZXRTdGFzaGVkUHJvbXB0KHVuZGVmaW5lZClcbiAgICB9IGVsc2UgaWYgKGlucHV0LnRyaW0oKSAhPT0gJycpIHtcbiAgICAgIC8vIFB1c2ggdG8gc3Rhc2ggKHNhdmUgdGV4dCwgY3Vyc29yIHBvc2l0aW9uLCBhbmQgcGFzdGVkIGNvbnRlbnRzKVxuICAgICAgc2V0U3Rhc2hlZFByb21wdCh7IHRleHQ6IGlucHV0LCBjdXJzb3JPZmZzZXQsIHBhc3RlZENvbnRlbnRzIH0pXG4gICAgICB0cmFja0FuZFNldElucHV0KCcnKVxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KDApXG4gICAgICBzZXRQYXN0ZWRDb250ZW50cyh7fSlcbiAgICAgIC8vIFRyYWNrIHVzYWdlIGZvciAvZGlzY292ZXIgYW5kIHN0b3Agc2hvd2luZyBoaW50XG4gICAgICBzYXZlR2xvYmFsQ29uZmlnKGMgPT4ge1xuICAgICAgICBpZiAoYy5oYXNVc2VkU3Rhc2gpIHJldHVybiBjXG4gICAgICAgIHJldHVybiB7IC4uLmMsIGhhc1VzZWRTdGFzaDogdHJ1ZSB9XG4gICAgICB9KVxuICAgIH1cbiAgfSwgW1xuICAgIGlucHV0LFxuICAgIGN1cnNvck9mZnNldCxcbiAgICBzdGFzaGVkUHJvbXB0LFxuICAgIHRyYWNrQW5kU2V0SW5wdXQsXG4gICAgc2V0U3Rhc2hlZFByb21wdCxcbiAgICBwYXN0ZWRDb250ZW50cyxcbiAgICBzZXRQYXN0ZWRDb250ZW50cyxcbiAgXSlcblxuICAvLyBIYW5kbGVyIGZvciBjaGF0Om1vZGVsUGlja2VyIC0gdG9nZ2xlIG1vZGVsIHBpY2tlclxuICBjb25zdCBoYW5kbGVNb2RlbFBpY2tlciA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTaG93TW9kZWxQaWNrZXIocHJldiA9PiAhcHJldilcbiAgICBpZiAoaGVscE9wZW4pIHtcbiAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgIH1cbiAgfSwgW2hlbHBPcGVuXSlcblxuICAvLyBIYW5kbGVyIGZvciBjaGF0OmZhc3RNb2RlIC0gdG9nZ2xlIGZhc3QgbW9kZSBwaWNrZXJcbiAgY29uc3QgaGFuZGxlRmFzdE1vZGVQaWNrZXIgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgc2V0U2hvd0Zhc3RNb2RlUGlja2VyKHByZXYgPT4gIXByZXYpXG4gICAgaWYgKGhlbHBPcGVuKSB7XG4gICAgICBzZXRIZWxwT3BlbihmYWxzZSlcbiAgICB9XG4gIH0sIFtoZWxwT3Blbl0pXG5cbiAgLy8gSGFuZGxlciBmb3IgY2hhdDp0aGlua2luZ1RvZ2dsZSAtIHRvZ2dsZSB0aGlua2luZyBtb2RlXG4gIGNvbnN0IGhhbmRsZVRoaW5raW5nVG9nZ2xlID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHNldFNob3dUaGlua2luZ1RvZ2dsZShwcmV2ID0+ICFwcmV2KVxuICAgIGlmIChoZWxwT3Blbikge1xuICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgfVxuICB9LCBbaGVscE9wZW5dKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6Y3ljbGVNb2RlIC0gY3ljbGUgdGhyb3VnaCBwZXJtaXNzaW9uIG1vZGVzXG4gIGNvbnN0IGhhbmRsZUN5Y2xlTW9kZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICAvLyBXaGVuIHZpZXdpbmcgYSB0ZWFtbWF0ZSwgY3ljbGUgdGhlaXIgbW9kZSBpbnN0ZWFkIG9mIHRoZSBsZWFkZXInc1xuICAgIGlmIChpc0FnZW50U3dhcm1zRW5hYmxlZCgpICYmIHZpZXdlZFRlYW1tYXRlICYmIHZpZXdpbmdBZ2VudFRhc2tJZCkge1xuICAgICAgY29uc3QgdGVhbW1hdGVDb250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHQgPSB7XG4gICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgbW9kZTogdmlld2VkVGVhbW1hdGUucGVybWlzc2lvbk1vZGUsXG4gICAgICB9XG4gICAgICAvLyBQYXNzIHVuZGVmaW5lZCBmb3IgdGVhbUNvbnRleHQgKHVudXNlZCBidXQga2VwdCBmb3IgQVBJIGNvbXBhdGliaWxpdHkpXG4gICAgICBjb25zdCBuZXh0TW9kZSA9IGdldE5leHRQZXJtaXNzaW9uTW9kZSh0ZWFtbWF0ZUNvbnRleHQsIHVuZGVmaW5lZClcblxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X21vZGVfY3ljbGUnLCB7XG4gICAgICAgIHRvOiBuZXh0TW9kZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcblxuICAgICAgY29uc3QgdGVhbW1hdGVUYXNrSWQgPSB2aWV3aW5nQWdlbnRUYXNrSWRcbiAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICBjb25zdCB0YXNrID0gcHJldi50YXNrc1t0ZWFtbWF0ZVRhc2tJZF1cbiAgICAgICAgaWYgKCF0YXNrIHx8IHRhc2sudHlwZSAhPT0gJ2luX3Byb2Nlc3NfdGVhbW1hdGUnKSB7XG4gICAgICAgICAgcmV0dXJuIHByZXZcbiAgICAgICAgfVxuICAgICAgICBpZiAodGFzay5wZXJtaXNzaW9uTW9kZSA9PT0gbmV4dE1vZGUpIHtcbiAgICAgICAgICByZXR1cm4gcHJldlxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICB0YXNrczoge1xuICAgICAgICAgICAgLi4ucHJldi50YXNrcyxcbiAgICAgICAgICAgIFt0ZWFtbWF0ZVRhc2tJZF06IHtcbiAgICAgICAgICAgICAgLi4udGFzayxcbiAgICAgICAgICAgICAgcGVybWlzc2lvbk1vZGU6IG5leHRNb2RlLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICBpZiAoaGVscE9wZW4pIHtcbiAgICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBDb21wdXRlIHRoZSBuZXh0IG1vZGUgd2l0aG91dCB0cmlnZ2VyaW5nIHNpZGUgZWZmZWN0cyBmaXJzdFxuICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgIGBbYXV0by1tb2RlXSBoYW5kbGVDeWNsZU1vZGU6IGN1cnJlbnRNb2RlPSR7dG9vbFBlcm1pc3Npb25Db250ZXh0Lm1vZGV9IGlzQXV0b01vZGVBdmFpbGFibGU9JHt0b29sUGVybWlzc2lvbkNvbnRleHQuaXNBdXRvTW9kZUF2YWlsYWJsZX0gc2hvd0F1dG9Nb2RlT3B0SW49JHtzaG93QXV0b01vZGVPcHRJbn0gdGltZW91dFBlbmRpbmc9JHshIWF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnR9YCxcbiAgICApXG4gICAgY29uc3QgbmV4dE1vZGUgPSBnZXROZXh0UGVybWlzc2lvbk1vZGUodG9vbFBlcm1pc3Npb25Db250ZXh0LCB0ZWFtQ29udGV4dClcblxuICAgIC8vIENoZWNrIGlmIHVzZXIgaXMgZW50ZXJpbmcgYXV0byBtb2RlIGZvciB0aGUgZmlyc3QgdGltZS4gR2F0ZWQgb24gdGhlXG4gICAgLy8gcGVyc2lzdGVudCBzZXR0aW5ncyBmbGFnIChoYXNBdXRvTW9kZU9wdEluKSByYXRoZXIgdGhhbiB0aGUgYnJvYWRlclxuICAgIC8vIGhhc0F1dG9Nb2RlT3B0SW5BbnlTb3VyY2Ugc28gdGhhdCAtLWVuYWJsZS1hdXRvLW1vZGUgdXNlcnMgc3RpbGwgc2VlXG4gICAgLy8gdGhlIHdhcm5pbmcgZGlhbG9nIG9uY2Ug4oCUIHRoZSBDTEkgZmxhZyBzaG91bGQgZ3JhbnQgY2Fyb3VzZWwgYWNjZXNzLFxuICAgIC8vIG5vdCBieXBhc3MgdGhlIHNhZmV0eSB0ZXh0LlxuICAgIGxldCBpc0VudGVyaW5nQXV0b01vZGVGaXJzdFRpbWUgPSBmYWxzZVxuICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgICAgaXNFbnRlcmluZ0F1dG9Nb2RlRmlyc3RUaW1lID1cbiAgICAgICAgbmV4dE1vZGUgPT09ICdhdXRvJyAmJlxuICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSAhPT0gJ2F1dG8nICYmXG4gICAgICAgICFoYXNBdXRvTW9kZU9wdEluKCkgJiZcbiAgICAgICAgIXZpZXdpbmdBZ2VudFRhc2tJZCAvLyBPbmx5IHNob3cgZm9yIHByaW1hcnkgYWdlbnQsIG5vdCBzdWJhZ2VudHNcbiAgICB9XG5cbiAgICBpZiAoZmVhdHVyZSgnVFJBTlNDUklQVF9DTEFTU0lGSUVSJykpIHtcbiAgICAgIGlmIChpc0VudGVyaW5nQXV0b01vZGVGaXJzdFRpbWUpIHtcbiAgICAgICAgLy8gU3RvcmUgcHJldmlvdXMgbW9kZSBzbyB3ZSBjYW4gcmV2ZXJ0IGlmIHVzZXIgZGVjbGluZXNcbiAgICAgICAgc2V0UHJldmlvdXNNb2RlQmVmb3JlQXV0byh0b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSlcblxuICAgICAgICAvLyBPbmx5IHVwZGF0ZSB0aGUgVUkgbW9kZSBsYWJlbCDigJQgZG8gTk9UIGNhbGwgdHJhbnNpdGlvblBlcm1pc3Npb25Nb2RlXG4gICAgICAgIC8vIG9yIGN5Y2xlUGVybWlzc2lvbk1vZGUgeWV0OyB3ZSBoYXZlbid0IGNvbmZpcm1lZCB3aXRoIHRoZSB1c2VyLlxuICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQ6IHtcbiAgICAgICAgICAgIC4uLnByZXYudG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICAgICAgICAgICAgbW9kZTogJ2F1dG8nLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pKVxuICAgICAgICBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQoe1xuICAgICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgICBtb2RlOiAnYXV0bycsXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gU2hvdyBvcHQtaW4gZGlhbG9nIGFmdGVyIDQwMG1zIGRlYm91bmNlXG4gICAgICAgIGlmIChhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50KSB7XG4gICAgICAgICAgY2xlYXJUaW1lb3V0KGF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnQpXG4gICAgICAgIH1cbiAgICAgICAgYXV0b01vZGVPcHRJblRpbWVvdXRSZWYuY3VycmVudCA9IHNldFRpbWVvdXQoXG4gICAgICAgICAgKHNldFNob3dBdXRvTW9kZU9wdEluLCBhdXRvTW9kZU9wdEluVGltZW91dFJlZikgPT4ge1xuICAgICAgICAgICAgc2V0U2hvd0F1dG9Nb2RlT3B0SW4odHJ1ZSlcbiAgICAgICAgICAgIGF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICAgICAgfSxcbiAgICAgICAgICA0MDAsXG4gICAgICAgICAgc2V0U2hvd0F1dG9Nb2RlT3B0SW4sXG4gICAgICAgICAgYXV0b01vZGVPcHRJblRpbWVvdXRSZWYsXG4gICAgICAgIClcblxuICAgICAgICBpZiAoaGVscE9wZW4pIHtcbiAgICAgICAgICBzZXRIZWxwT3BlbihmYWxzZSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBEaXNtaXNzIGF1dG8gbW9kZSBvcHQtaW4gZGlhbG9nIGlmIHNob3dpbmcgb3IgcGVuZGluZyAodXNlciBpcyBjeWNsaW5nIGF3YXkpLlxuICAgIC8vIERvIE5PVCByZXZlcnQgdG8gcHJldmlvdXNNb2RlQmVmb3JlQXV0byBoZXJlIOKAlCBzaGlmdCt0YWIgbWVhbnMgXCJhZHZhbmNlIHRoZVxuICAgIC8vIGNhcm91c2VsXCIsIG5vdCBcImRlY2xpbmVcIi4gUmV2ZXJ0aW5nIGNhdXNlcyBhIHBpbmctcG9uZyBsb29wOiBhdXRvIHJldmVydHMgdG9cbiAgICAvLyB0aGUgcHJpb3IgbW9kZSwgd2hvc2UgbmV4dCBtb2RlIGlzIGF1dG8gYWdhaW4sIGZvcmV2ZXIuXG4gICAgLy8gVGhlIGRpYWxvZydzIG93biBkZWNsaW5lIGJ1dHRvbiAoaGFuZGxlQXV0b01vZGVPcHRJbkRlY2xpbmUpIGhhbmRsZXMgcmV2ZXJ0LlxuICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgICAgaWYgKHNob3dBdXRvTW9kZU9wdEluIHx8IGF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnQpIHtcbiAgICAgICAgaWYgKHNob3dBdXRvTW9kZU9wdEluKSB7XG4gICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2F1dG9fbW9kZV9vcHRfaW5fZGlhbG9nX2RlY2xpbmUnLCB7fSlcbiAgICAgICAgfVxuICAgICAgICBzZXRTaG93QXV0b01vZGVPcHRJbihmYWxzZSlcbiAgICAgICAgaWYgKGF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnQpIHtcbiAgICAgICAgICBjbGVhclRpbWVvdXQoYXV0b01vZGVPcHRJblRpbWVvdXRSZWYuY3VycmVudClcbiAgICAgICAgICBhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50ID0gbnVsbFxuICAgICAgICB9XG4gICAgICAgIHNldFByZXZpb3VzTW9kZUJlZm9yZUF1dG8obnVsbClcbiAgICAgICAgLy8gRmFsbCB0aHJvdWdoIOKAlCBtb2RlIGlzICdhdXRvJywgY3ljbGVQZXJtaXNzaW9uTW9kZSBiZWxvdyBnb2VzIHRvICdkZWZhdWx0Jy5cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBOb3cgdGhhdCB3ZSBrbm93IHRoaXMgaXMgTk9UIHRoZSBmaXJzdC10aW1lIGF1dG8gbW9kZSBwYXRoLFxuICAgIC8vIGNhbGwgY3ljbGVQZXJtaXNzaW9uTW9kZSB0byBhcHBseSBzaWRlIGVmZmVjdHMgKGUuZy4gc3RyaXBcbiAgICAvLyBkYW5nZXJvdXMgcGVybWlzc2lvbnMsIGFjdGl2YXRlIGNsYXNzaWZpZXIpXG4gICAgY29uc3QgeyBjb250ZXh0OiBwcmVwYXJlZENvbnRleHQgfSA9IGN5Y2xlUGVybWlzc2lvbk1vZGUoXG4gICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICB0ZWFtQ29udGV4dCxcbiAgICApXG5cbiAgICBsb2dFdmVudCgndGVuZ3VfbW9kZV9jeWNsZScsIHtcbiAgICAgIHRvOiBuZXh0TW9kZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgIH0pXG5cbiAgICAvLyBUcmFjayB3aGVuIHVzZXIgZW50ZXJzIHBsYW4gbW9kZVxuICAgIGlmIChuZXh0TW9kZSA9PT0gJ3BsYW4nKSB7XG4gICAgICBzYXZlR2xvYmFsQ29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAgICAgLi4uY3VycmVudCxcbiAgICAgICAgbGFzdFBsYW5Nb2RlVXNlOiBEYXRlLm5vdygpLFxuICAgICAgfSkpXG4gICAgfVxuXG4gICAgLy8gU2V0IHRoZSBtb2RlIHZpYSBzZXRBcHBTdGF0ZSBkaXJlY3RseSBiZWNhdXNlIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dFxuICAgIC8vIGludGVudGlvbmFsbHkgcHJlc2VydmVzIHRoZSBleGlzdGluZyBtb2RlICh0byBwcmV2ZW50IGNvb3JkaW5hdG9yIG1vZGVcbiAgICAvLyBjb3JydXB0aW9uIGZyb20gd29ya2VycykuIFRoZW4gY2FsbCBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQgdG8gdHJpZ2dlclxuICAgIC8vIHJlY2hlY2sgb2YgcXVldWVkIHBlcm1pc3Npb24gcHJvbXB0cy5cbiAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAuLi5wcmV2LFxuICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiB7XG4gICAgICAgIC4uLnByZXBhcmVkQ29udGV4dCxcbiAgICAgICAgbW9kZTogbmV4dE1vZGUsXG4gICAgICB9LFxuICAgIH0pKVxuICAgIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCh7XG4gICAgICAuLi5wcmVwYXJlZENvbnRleHQsXG4gICAgICBtb2RlOiBuZXh0TW9kZSxcbiAgICB9KVxuXG4gICAgLy8gSWYgdGhpcyBpcyBhIHRlYW1tYXRlLCB1cGRhdGUgY29uZmlnLmpzb24gc28gdGVhbSBsZWFkIHNlZXMgdGhlIGNoYW5nZVxuICAgIHN5bmNUZWFtbWF0ZU1vZGUobmV4dE1vZGUsIHRlYW1Db250ZXh0Py50ZWFtTmFtZSlcblxuICAgIC8vIENsb3NlIGhlbHAgdGlwcyBpZiB0aGV5J3JlIG9wZW4gd2hlbiBtb2RlIGlzIGN5Y2xlZFxuICAgIGlmIChoZWxwT3Blbikge1xuICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgfVxuICB9LCBbXG4gICAgdG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICAgIHRlYW1Db250ZXh0LFxuICAgIHZpZXdpbmdBZ2VudFRhc2tJZCxcbiAgICB2aWV3ZWRUZWFtbWF0ZSxcbiAgICBzZXRBcHBTdGF0ZSxcbiAgICBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgaGVscE9wZW4sXG4gICAgc2hvd0F1dG9Nb2RlT3B0SW4sXG4gIF0pXG5cbiAgLy8gSGFuZGxlciBmb3IgYXV0byBtb2RlIG9wdC1pbiBkaWFsb2cgYWNjZXB0YW5jZVxuICBjb25zdCBoYW5kbGVBdXRvTW9kZU9wdEluQWNjZXB0ID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgICAgc2V0U2hvd0F1dG9Nb2RlT3B0SW4oZmFsc2UpXG4gICAgICBzZXRQcmV2aW91c01vZGVCZWZvcmVBdXRvKG51bGwpXG5cbiAgICAgIC8vIE5vdyB0aGF0IHRoZSB1c2VyIGFjY2VwdGVkLCBhcHBseSB0aGUgZnVsbCB0cmFuc2l0aW9uOiBhY3RpdmF0ZSB0aGVcbiAgICAgIC8vIGF1dG8gbW9kZSBiYWNrZW5kIChjbGFzc2lmaWVyLCBiZXRhIGhlYWRlcnMpIGFuZCBzdHJpcCBkYW5nZXJvdXNcbiAgICAgIC8vIHBlcm1pc3Npb25zIChlLmcuIEJhc2goKikgYWx3YXlzLWFsbG93IHJ1bGVzKS5cbiAgICAgIGNvbnN0IHN0cmlwcGVkQ29udGV4dCA9IHRyYW5zaXRpb25QZXJtaXNzaW9uTW9kZShcbiAgICAgICAgcHJldmlvdXNNb2RlQmVmb3JlQXV0byA/PyB0b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSxcbiAgICAgICAgJ2F1dG8nLFxuICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICApXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgIC4uLnByZXYsXG4gICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dDoge1xuICAgICAgICAgIC4uLnN0cmlwcGVkQ29udGV4dCxcbiAgICAgICAgICBtb2RlOiAnYXV0bycsXG4gICAgICAgIH0sXG4gICAgICB9KSlcbiAgICAgIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCh7XG4gICAgICAgIC4uLnN0cmlwcGVkQ29udGV4dCxcbiAgICAgICAgbW9kZTogJ2F1dG8nLFxuICAgICAgfSlcblxuICAgICAgLy8gQ2xvc2UgaGVscCB0aXBzIGlmIHRoZXkncmUgb3BlbiB3aGVuIGF1dG8gbW9kZSBpcyBlbmFibGVkXG4gICAgICBpZiAoaGVscE9wZW4pIHtcbiAgICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgICB9XG4gICAgfVxuICB9LCBbXG4gICAgaGVscE9wZW4sXG4gICAgc2V0SGVscE9wZW4sXG4gICAgcHJldmlvdXNNb2RlQmVmb3JlQXV0byxcbiAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgc2V0QXBwU3RhdGUsXG4gICAgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICBdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGF1dG8gbW9kZSBvcHQtaW4gZGlhbG9nIGRlY2xpbmVcbiAgY29uc3QgaGFuZGxlQXV0b01vZGVPcHRJbkRlY2xpbmUgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgaWYgKGZlYXR1cmUoJ1RSQU5TQ1JJUFRfQ0xBU1NJRklFUicpKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgIGBbYXV0by1tb2RlXSBoYW5kbGVBdXRvTW9kZU9wdEluRGVjbGluZTogcmV2ZXJ0aW5nIHRvICR7cHJldmlvdXNNb2RlQmVmb3JlQXV0b30sIHNldHRpbmcgaXNBdXRvTW9kZUF2YWlsYWJsZT1mYWxzZWAsXG4gICAgICApXG4gICAgICBzZXRTaG93QXV0b01vZGVPcHRJbihmYWxzZSlcbiAgICAgIGlmIChhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50KSB7XG4gICAgICAgIGNsZWFyVGltZW91dChhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50KVxuICAgICAgICBhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50ID0gbnVsbFxuICAgICAgfVxuXG4gICAgICAvLyBSZXZlcnQgdG8gcHJldmlvdXMgbW9kZSBhbmQgcmVtb3ZlIGF1dG8gZnJvbSB0aGUgY2Fyb3VzZWxcbiAgICAgIC8vIGZvciB0aGUgcmVzdCBvZiB0aGlzIHNlc3Npb25cbiAgICAgIGlmIChwcmV2aW91c01vZGVCZWZvcmVBdXRvKSB7XG4gICAgICAgIHNldEF1dG9Nb2RlQWN0aXZlKGZhbHNlKVxuICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQ6IHtcbiAgICAgICAgICAgIC4uLnByZXYudG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICAgICAgICAgICAgbW9kZTogcHJldmlvdXNNb2RlQmVmb3JlQXV0byxcbiAgICAgICAgICAgIGlzQXV0b01vZGVBdmFpbGFibGU6IGZhbHNlLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pKVxuICAgICAgICBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQoe1xuICAgICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgICBtb2RlOiBwcmV2aW91c01vZGVCZWZvcmVBdXRvLFxuICAgICAgICAgIGlzQXV0b01vZGVBdmFpbGFibGU6IGZhbHNlLFxuICAgICAgICB9KVxuICAgICAgICBzZXRQcmV2aW91c01vZGVCZWZvcmVBdXRvKG51bGwpXG4gICAgICB9XG4gICAgfVxuICB9LCBbXG4gICAgcHJldmlvdXNNb2RlQmVmb3JlQXV0byxcbiAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgc2V0QXBwU3RhdGUsXG4gICAgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICBdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6aW1hZ2VQYXN0ZSAtIHBhc3RlIGltYWdlIGZyb20gY2xpcGJvYXJkXG4gIGNvbnN0IGhhbmRsZUltYWdlUGFzdGUgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgdm9pZCBnZXRJbWFnZUZyb21DbGlwYm9hcmQoKS50aGVuKGltYWdlRGF0YSA9PiB7XG4gICAgICBpZiAoaW1hZ2VEYXRhKSB7XG4gICAgICAgIG9uSW1hZ2VQYXN0ZShpbWFnZURhdGEuYmFzZTY0LCBpbWFnZURhdGEubWVkaWFUeXBlKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3Qgc2hvcnRjdXREaXNwbGF5ID0gZ2V0U2hvcnRjdXREaXNwbGF5KFxuICAgICAgICAgICdjaGF0OmltYWdlUGFzdGUnLFxuICAgICAgICAgICdDaGF0JyxcbiAgICAgICAgICAnY3RybCt2JyxcbiAgICAgICAgKVxuICAgICAgICBjb25zdCBtZXNzYWdlID0gZW52LmlzU1NIKClcbiAgICAgICAgICA/IFwiTm8gaW1hZ2UgZm91bmQgaW4gY2xpcGJvYXJkLiBZb3UncmUgU1NIJ2Q7IHRyeSBzY3A/XCJcbiAgICAgICAgICA6IGBObyBpbWFnZSBmb3VuZCBpbiBjbGlwYm9hcmQuIFVzZSAke3Nob3J0Y3V0RGlzcGxheX0gdG8gcGFzdGUgaW1hZ2VzLmBcbiAgICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAgICBrZXk6ICduby1pbWFnZS1pbi1jbGlwYm9hcmQnLFxuICAgICAgICAgIHRleHQ6IG1lc3NhZ2UsXG4gICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgIHRpbWVvdXRNczogMTAwMCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9KVxuICB9LCBbYWRkTm90aWZpY2F0aW9uLCBvbkltYWdlUGFzdGVdKVxuXG4gIC8vIFJlZ2lzdGVyIGNoYXQ6c3VibWl0IGhhbmRsZXIgZGlyZWN0bHkgaW4gdGhlIGhhbmRsZXIgcmVnaXN0cnkgKG5vdCB2aWFcbiAgLy8gdXNlS2V5YmluZGluZ3MpIHNvIHRoYXQgb25seSB0aGUgQ2hvcmRJbnRlcmNlcHRvciBjYW4gaW52b2tlIGl0IGZvciBjaG9yZFxuICAvLyBjb21wbGV0aW9ucyAoZS5nLiwgXCJjdHJsK2Ugc1wiKS4gVGhlIGRlZmF1bHQgRW50ZXIgYmluZGluZyBmb3Igc3VibWl0IGlzXG4gIC8vIGhhbmRsZWQgYnkgVGV4dElucHV0IGRpcmVjdGx5ICh2aWEgb25TdWJtaXQgcHJvcCkgYW5kIHVzZVR5cGVhaGVhZCAoZm9yXG4gIC8vIGF1dG9jb21wbGV0ZSBhY2NlcHRhbmNlKS4gVXNpbmcgdXNlS2V5YmluZGluZ3Mgd291bGQgY2F1c2VcbiAgLy8gc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uIG9uIEVudGVyLCBibG9ja2luZyBhdXRvY29tcGxldGUgZnJvbSBzZWVpbmcgdGhlIGtleS5cbiAgY29uc3Qga2V5YmluZGluZ0NvbnRleHQgPSB1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0KClcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoIWtleWJpbmRpbmdDb250ZXh0IHx8IGlzTW9kYWxPdmVybGF5QWN0aXZlKSByZXR1cm5cbiAgICByZXR1cm4ga2V5YmluZGluZ0NvbnRleHQucmVnaXN0ZXJIYW5kbGVyKHtcbiAgICAgIGFjdGlvbjogJ2NoYXQ6c3VibWl0JyxcbiAgICAgIGNvbnRleHQ6ICdDaGF0JyxcbiAgICAgIGhhbmRsZXI6ICgpID0+IHtcbiAgICAgICAgdm9pZCBvblN1Ym1pdChpbnB1dClcbiAgICAgIH0sXG4gICAgfSlcbiAgfSwgW2tleWJpbmRpbmdDb250ZXh0LCBpc01vZGFsT3ZlcmxheUFjdGl2ZSwgb25TdWJtaXQsIGlucHV0XSlcblxuICAvLyBDaGF0IGNvbnRleHQga2V5YmluZGluZ3MgZm9yIGVkaXRpbmcgc2hvcnRjdXRzXG4gIC8vIE5vdGU6IGhpc3Rvcnk6cHJldmlvdXMvaGlzdG9yeTpuZXh0IGFyZSBOT1QgaGFuZGxlZCBoZXJlLiBUaGV5IGFyZSBwYXNzZWQgYXNcbiAgLy8gb25IaXN0b3J5VXAvb25IaXN0b3J5RG93biBwcm9wcyB0byBUZXh0SW5wdXQsIHNvIHRoYXQgdXNlVGV4dElucHV0J3NcbiAgLy8gdXBPckhpc3RvcnlVcC9kb3duT3JIaXN0b3J5RG93biBjYW4gdHJ5IGN1cnNvciBtb3ZlbWVudCBmaXJzdCBhbmQgb25seVxuICAvLyBmYWxsIHRocm91Z2ggdG8gaGlzdG9yeSB3aGVuIHRoZSBjdXJzb3IgY2FuJ3QgbW92ZSBmdXJ0aGVyLlxuICBjb25zdCBjaGF0SGFuZGxlcnMgPSB1c2VNZW1vKFxuICAgICgpID0+ICh7XG4gICAgICAnY2hhdDp1bmRvJzogaGFuZGxlVW5kbyxcbiAgICAgICdjaGF0Om5ld2xpbmUnOiBoYW5kbGVOZXdsaW5lLFxuICAgICAgJ2NoYXQ6ZXh0ZXJuYWxFZGl0b3InOiBoYW5kbGVFeHRlcm5hbEVkaXRvcixcbiAgICAgICdjaGF0OnN0YXNoJzogaGFuZGxlU3Rhc2gsXG4gICAgICAnY2hhdDptb2RlbFBpY2tlcic6IGhhbmRsZU1vZGVsUGlja2VyLFxuICAgICAgJ2NoYXQ6dGhpbmtpbmdUb2dnbGUnOiBoYW5kbGVUaGlua2luZ1RvZ2dsZSxcbiAgICAgICdjaGF0OmN5Y2xlTW9kZSc6IGhhbmRsZUN5Y2xlTW9kZSxcbiAgICAgICdjaGF0OmltYWdlUGFzdGUnOiBoYW5kbGVJbWFnZVBhc3RlLFxuICAgIH0pLFxuICAgIFtcbiAgICAgIGhhbmRsZVVuZG8sXG4gICAgICBoYW5kbGVOZXdsaW5lLFxuICAgICAgaGFuZGxlRXh0ZXJuYWxFZGl0b3IsXG4gICAgICBoYW5kbGVTdGFzaCxcbiAgICAgIGhhbmRsZU1vZGVsUGlja2VyLFxuICAgICAgaGFuZGxlVGhpbmtpbmdUb2dnbGUsXG4gICAgICBoYW5kbGVDeWNsZU1vZGUsXG4gICAgICBoYW5kbGVJbWFnZVBhc3RlLFxuICAgIF0sXG4gIClcblxuICB1c2VLZXliaW5kaW5ncyhjaGF0SGFuZGxlcnMsIHtcbiAgICBjb250ZXh0OiAnQ2hhdCcsXG4gICAgaXNBY3RpdmU6ICFpc01vZGFsT3ZlcmxheUFjdGl2ZSxcbiAgfSlcblxuICAvLyBTaGlmdCvihpEgZW50ZXJzIG1lc3NhZ2UtYWN0aW9ucyBjdXJzb3IuIFNlcGFyYXRlIGlzQWN0aXZlIHNvIGN0cmwrciBzZWFyY2hcbiAgLy8gZG9lc24ndCBsZWF2ZSBzdGFsZSBpc1NlYXJjaGluZ0hpc3Rvcnkgb24gY3Vyc29yLWV4aXQgcmVtb3VudC5cbiAgdXNlS2V5YmluZGluZygnY2hhdDptZXNzYWdlQWN0aW9ucycsICgpID0+IG9uTWVzc2FnZUFjdGlvbnNFbnRlcj8uKCksIHtcbiAgICBjb250ZXh0OiAnQ2hhdCcsXG4gICAgaXNBY3RpdmU6ICFpc01vZGFsT3ZlcmxheUFjdGl2ZSAmJiAhaXNTZWFyY2hpbmdIaXN0b3J5LFxuICB9KVxuXG4gIC8vIEZhc3QgbW9kZSBrZXliaW5kaW5nIGlzIG9ubHkgYWN0aXZlIHdoZW4gZmFzdCBtb2RlIGlzIGVuYWJsZWQgYW5kIGF2YWlsYWJsZVxuICB1c2VLZXliaW5kaW5nKCdjaGF0OmZhc3RNb2RlJywgaGFuZGxlRmFzdE1vZGVQaWNrZXIsIHtcbiAgICBjb250ZXh0OiAnQ2hhdCcsXG4gICAgaXNBY3RpdmU6XG4gICAgICAhaXNNb2RhbE92ZXJsYXlBY3RpdmUgJiYgaXNGYXN0TW9kZUVuYWJsZWQoKSAmJiBpc0Zhc3RNb2RlQXZhaWxhYmxlKCksXG4gIH0pXG5cbiAgLy8gSGFuZGxlIGhlbHA6ZGlzbWlzcyBrZXliaW5kaW5nIChFU0MgY2xvc2VzIGhlbHAgbWVudSlcbiAgLy8gVGhpcyBpcyByZWdpc3RlcmVkIHNlcGFyYXRlbHkgZnJvbSBDaGF0IGNvbnRleHQgc28gaXQgaGFzIHByaW9yaXR5IG92ZXJcbiAgLy8gQ2FuY2VsUmVxdWVzdEhhbmRsZXIgd2hlbiBoZWxwIG1lbnUgaXMgb3BlblxuICB1c2VLZXliaW5kaW5nKFxuICAgICdoZWxwOmRpc21pc3MnLFxuICAgICgpID0+IHtcbiAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnSGVscCcsIGlzQWN0aXZlOiBoZWxwT3BlbiB9LFxuICApXG5cbiAgLy8gUXVpY2sgT3BlbiAvIEdsb2JhbCBTZWFyY2guIEhvb2sgY2FsbHMgYXJlIHVuY29uZGl0aW9uYWwgKFJ1bGVzIG9mIEhvb2tzKTtcbiAgLy8gdGhlIGhhbmRsZXIgYm9keSBpcyBmZWF0dXJlKCktZ2F0ZWQgc28gdGhlIHNldFN0YXRlIGNhbGxzIGFuZCBjb21wb25lbnRcbiAgLy8gcmVmZXJlbmNlcyBnZXQgdHJlZS1zaGFrZW4gaW4gZXh0ZXJuYWwgYnVpbGRzLlxuICBjb25zdCBxdWlja1NlYXJjaEFjdGl2ZSA9IGZlYXR1cmUoJ1FVSUNLX1NFQVJDSCcpXG4gICAgPyAhaXNNb2RhbE92ZXJsYXlBY3RpdmVcbiAgICA6IGZhbHNlXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2FwcDpxdWlja09wZW4nLFxuICAgICgpID0+IHtcbiAgICAgIGlmIChmZWF0dXJlKCdRVUlDS19TRUFSQ0gnKSkge1xuICAgICAgICBzZXRTaG93UXVpY2tPcGVuKHRydWUpXG4gICAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgICAgfVxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnR2xvYmFsJywgaXNBY3RpdmU6IHF1aWNrU2VhcmNoQWN0aXZlIH0sXG4gIClcbiAgdXNlS2V5YmluZGluZyhcbiAgICAnYXBwOmdsb2JhbFNlYXJjaCcsXG4gICAgKCkgPT4ge1xuICAgICAgaWYgKGZlYXR1cmUoJ1FVSUNLX1NFQVJDSCcpKSB7XG4gICAgICAgIHNldFNob3dHbG9iYWxTZWFyY2godHJ1ZSlcbiAgICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgICB9XG4gICAgfSxcbiAgICB7IGNvbnRleHQ6ICdHbG9iYWwnLCBpc0FjdGl2ZTogcXVpY2tTZWFyY2hBY3RpdmUgfSxcbiAgKVxuXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2hpc3Rvcnk6c2VhcmNoJyxcbiAgICAoKSA9PiB7XG4gICAgICBpZiAoZmVhdHVyZSgnSElTVE9SWV9QSUNLRVInKSkge1xuICAgICAgICBzZXRTaG93SGlzdG9yeVBpY2tlcih0cnVlKVxuICAgICAgICBzZXRIZWxwT3BlbihmYWxzZSlcbiAgICAgIH1cbiAgICB9LFxuICAgIHtcbiAgICAgIGNvbnRleHQ6ICdHbG9iYWwnLFxuICAgICAgaXNBY3RpdmU6IGZlYXR1cmUoJ0hJU1RPUllfUElDS0VSJykgPyAhaXNNb2RhbE92ZXJsYXlBY3RpdmUgOiBmYWxzZSxcbiAgICB9LFxuICApXG5cbiAgLy8gSGFuZGxlIEN0cmwrQyB0byBhYm9ydCBzcGVjdWxhdGlvbiB3aGVuIGlkbGUgKG5vdCBsb2FkaW5nKVxuICAvLyBDYW5jZWxSZXF1ZXN0SGFuZGxlciBvbmx5IGhhbmRsZXMgQ3RybCtDIGR1cmluZyBhY3RpdmUgdGFza3NcbiAgdXNlS2V5YmluZGluZyhcbiAgICAnYXBwOmludGVycnVwdCcsXG4gICAgKCkgPT4ge1xuICAgICAgYWJvcnRTcGVjdWxhdGlvbihzZXRBcHBTdGF0ZSlcbiAgICB9LFxuICAgIHtcbiAgICAgIGNvbnRleHQ6ICdHbG9iYWwnLFxuICAgICAgaXNBY3RpdmU6ICFpc0xvYWRpbmcgJiYgc3BlY3VsYXRpb24uc3RhdHVzID09PSAnYWN0aXZlJyxcbiAgICB9LFxuICApXG5cbiAgLy8gRm9vdGVyIGluZGljYXRvciBuYXZpZ2F0aW9uIGtleWJpbmRpbmdzLiDihpEv4oaTIGxpdmUgaGVyZSAobm90IGluXG4gIC8vIGhhbmRsZUhpc3RvcnlVcC9Eb3duKSBiZWNhdXNlIFRleHRJbnB1dCBmb2N1cz1mYWxzZSB3aGVuIGEgcGlsbCBpc1xuICAvLyBzZWxlY3RlZCDigJQgaXRzIHVzZUlucHV0IGlzIGluYWN0aXZlLCBzbyB0aGlzIGlzIHRoZSBvbmx5IHBhdGguXG4gIHVzZUtleWJpbmRpbmdzKFxuICAgIHtcbiAgICAgICdmb290ZXI6dXAnOiAoKSA9PiB7XG4gICAgICAgIC8vIOKGkSBzY3JvbGxzIHdpdGhpbiB0aGUgY29vcmRpbmF0b3IgdGFzayBsaXN0IGJlZm9yZSBsZWF2aW5nIHRoZSBwaWxsXG4gICAgICAgIGlmIChcbiAgICAgICAgICB0YXNrc1NlbGVjdGVkICYmXG4gICAgICAgICAgXCJleHRlcm5hbFwiID09PSAnYW50JyAmJlxuICAgICAgICAgIGNvb3JkaW5hdG9yVGFza0NvdW50ID4gMCAmJlxuICAgICAgICAgIGNvb3JkaW5hdG9yVGFza0luZGV4ID4gbWluQ29vcmRpbmF0b3JJbmRleFxuICAgICAgICApIHtcbiAgICAgICAgICBzZXRDb29yZGluYXRvclRhc2tJbmRleChwcmV2ID0+IHByZXYgLSAxKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIG5hdmlnYXRlRm9vdGVyKC0xLCB0cnVlKVxuICAgICAgfSxcbiAgICAgICdmb290ZXI6ZG93bic6ICgpID0+IHtcbiAgICAgICAgLy8g4oaTIHNjcm9sbHMgd2l0aGluIHRoZSBjb29yZGluYXRvciB0YXNrIGxpc3QsIG5ldmVyIGxlYXZlcyB0aGUgcGlsbFxuICAgICAgICBpZiAoXG4gICAgICAgICAgdGFza3NTZWxlY3RlZCAmJlxuICAgICAgICAgIFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiZcbiAgICAgICAgICBjb29yZGluYXRvclRhc2tDb3VudCA+IDBcbiAgICAgICAgKSB7XG4gICAgICAgICAgaWYgKGNvb3JkaW5hdG9yVGFza0luZGV4IDwgY29vcmRpbmF0b3JUYXNrQ291bnQgLSAxKSB7XG4gICAgICAgICAgICBzZXRDb29yZGluYXRvclRhc2tJbmRleChwcmV2ID0+IHByZXYgKyAxKVxuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAodGFza3NTZWxlY3RlZCAmJiAhaXNUZWFtbWF0ZU1vZGUpIHtcbiAgICAgICAgICBzZXRTaG93QmFzaGVzRGlhbG9nKHRydWUpXG4gICAgICAgICAgc2VsZWN0Rm9vdGVySXRlbShudWxsKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIG5hdmlnYXRlRm9vdGVyKDEpXG4gICAgICB9LFxuICAgICAgJ2Zvb3RlcjpuZXh0JzogKCkgPT4ge1xuICAgICAgICAvLyBUZWFtbWF0ZSBtb2RlOiDihpAv4oaSIGN5Y2xlcyB3aXRoaW4gdGhlIHRlYW0gbWVtYmVyIGxpc3RcbiAgICAgICAgaWYgKHRhc2tzU2VsZWN0ZWQgJiYgaXNUZWFtbWF0ZU1vZGUpIHtcbiAgICAgICAgICBjb25zdCB0b3RhbEFnZW50cyA9IDEgKyBpblByb2Nlc3NUZWFtbWF0ZXMubGVuZ3RoXG4gICAgICAgICAgc2V0VGVhbW1hdGVGb290ZXJJbmRleChwcmV2ID0+IChwcmV2ICsgMSkgJSB0b3RhbEFnZW50cylcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBuYXZpZ2F0ZUZvb3RlcigxKVxuICAgICAgfSxcbiAgICAgICdmb290ZXI6cHJldmlvdXMnOiAoKSA9PiB7XG4gICAgICAgIGlmICh0YXNrc1NlbGVjdGVkICYmIGlzVGVhbW1hdGVNb2RlKSB7XG4gICAgICAgICAgY29uc3QgdG90YWxBZ2VudHMgPSAxICsgaW5Qcm9jZXNzVGVhbW1hdGVzLmxlbmd0aFxuICAgICAgICAgIHNldFRlYW1tYXRlRm9vdGVySW5kZXgocHJldiA9PiAocHJldiAtIDEgKyB0b3RhbEFnZW50cykgJSB0b3RhbEFnZW50cylcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBuYXZpZ2F0ZUZvb3RlcigtMSlcbiAgICAgIH0sXG4gICAgICAnZm9vdGVyOm9wZW5TZWxlY3RlZCc6ICgpID0+IHtcbiAgICAgICAgaWYgKHZpZXdTZWxlY3Rpb25Nb2RlID09PSAnc2VsZWN0aW5nLWFnZW50Jykge1xuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIHN3aXRjaCAoZm9vdGVySXRlbVNlbGVjdGVkKSB7XG4gICAgICAgICAgY2FzZSAnY29tcGFuaW9uJzpcbiAgICAgICAgICAgIGlmIChmZWF0dXJlKCdCVUREWScpKSB7XG4gICAgICAgICAgICAgIHNlbGVjdEZvb3Rlckl0ZW0obnVsbClcbiAgICAgICAgICAgICAgdm9pZCBvblN1Ym1pdCgnL2J1ZGR5JylcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSAndGFza3MnOlxuICAgICAgICAgICAgaWYgKGlzVGVhbW1hdGVNb2RlKSB7XG4gICAgICAgICAgICAgIC8vIEVudGVyIHN3aXRjaGVzIHRvIHRoZSBzZWxlY3RlZCBhZ2VudCdzIHZpZXdcbiAgICAgICAgICAgICAgaWYgKHRlYW1tYXRlRm9vdGVySW5kZXggPT09IDApIHtcbiAgICAgICAgICAgICAgICBleGl0VGVhbW1hdGVWaWV3KHNldEFwcFN0YXRlKVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnN0IHRlYW1tYXRlID0gaW5Qcm9jZXNzVGVhbW1hdGVzW3RlYW1tYXRlRm9vdGVySW5kZXggLSAxXVxuICAgICAgICAgICAgICAgIGlmICh0ZWFtbWF0ZSkgZW50ZXJUZWFtbWF0ZVZpZXcodGVhbW1hdGUuaWQsIHNldEFwcFN0YXRlKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGNvb3JkaW5hdG9yVGFza0luZGV4ID09PSAwICYmIGNvb3JkaW5hdG9yVGFza0NvdW50ID4gMCkge1xuICAgICAgICAgICAgICBleGl0VGVhbW1hdGVWaWV3KHNldEFwcFN0YXRlKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0ZWRUYXNrSWQgPVxuICAgICAgICAgICAgICAgIGdldFZpc2libGVBZ2VudFRhc2tzKHRhc2tzKVtjb29yZGluYXRvclRhc2tJbmRleCAtIDFdPy5pZFxuICAgICAgICAgICAgICBpZiAoc2VsZWN0ZWRUYXNrSWQpIHtcbiAgICAgICAgICAgICAgICBlbnRlclRlYW1tYXRlVmlldyhzZWxlY3RlZFRhc2tJZCwgc2V0QXBwU3RhdGUpXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc2V0U2hvd0Jhc2hlc0RpYWxvZyh0cnVlKVxuICAgICAgICAgICAgICAgIHNlbGVjdEZvb3Rlckl0ZW0obnVsbClcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICBjYXNlICd0bXV4JzpcbiAgICAgICAgICAgIGlmIChcImV4dGVybmFsXCIgPT09ICdhbnQnKSB7XG4gICAgICAgICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT5cbiAgICAgICAgICAgICAgICBwcmV2LnR1bmdzdGVuUGFuZWxBdXRvSGlkZGVuXG4gICAgICAgICAgICAgICAgICA/IHsgLi4ucHJldiwgdHVuZ3N0ZW5QYW5lbEF1dG9IaWRkZW46IGZhbHNlIH1cbiAgICAgICAgICAgICAgICAgIDoge1xuICAgICAgICAgICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgICAgICAgICAgdHVuZ3N0ZW5QYW5lbFZpc2libGU6ICEoXG4gICAgICAgICAgICAgICAgICAgICAgICBwcmV2LnR1bmdzdGVuUGFuZWxWaXNpYmxlID8/IHRydWVcbiAgICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgJ2JhZ2VsJzpcbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSAndGVhbXMnOlxuICAgICAgICAgICAgc2V0U2hvd1RlYW1zRGlhbG9nKHRydWUpXG4gICAgICAgICAgICBzZWxlY3RGb290ZXJJdGVtKG51bGwpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgJ2JyaWRnZSc6XG4gICAgICAgICAgICBzZXRTaG93QnJpZGdlRGlhbG9nKHRydWUpXG4gICAgICAgICAgICBzZWxlY3RGb290ZXJJdGVtKG51bGwpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgJ2Zvb3RlcjpjbGVhclNlbGVjdGlvbic6ICgpID0+IHtcbiAgICAgICAgc2VsZWN0Rm9vdGVySXRlbShudWxsKVxuICAgICAgfSxcbiAgICAgICdmb290ZXI6Y2xvc2UnOiAoKSA9PiB7XG4gICAgICAgIGlmICh0YXNrc1NlbGVjdGVkICYmIGNvb3JkaW5hdG9yVGFza0luZGV4ID49IDEpIHtcbiAgICAgICAgICBjb25zdCB0YXNrID0gZ2V0VmlzaWJsZUFnZW50VGFza3ModGFza3MpW2Nvb3JkaW5hdG9yVGFza0luZGV4IC0gMV1cbiAgICAgICAgICBpZiAoIXRhc2spIHJldHVybiBmYWxzZVxuICAgICAgICAgIC8vIFdoZW4gdGhlIHNlbGVjdGVkIHJvdyBJUyB0aGUgdmlld2VkIGFnZW50LCAneCcgdHlwZXMgaW50byB0aGVcbiAgICAgICAgICAvLyBzdGVlcmluZyBpbnB1dC4gQW55IG90aGVyIHJvdyDigJQgZGlzbWlzcyBpdC5cbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICB2aWV3U2VsZWN0aW9uTW9kZSA9PT0gJ3ZpZXdpbmctYWdlbnQnICYmXG4gICAgICAgICAgICB0YXNrLmlkID09PSB2aWV3aW5nQWdlbnRUYXNrSWRcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIG9uQ2hhbmdlKFxuICAgICAgICAgICAgICBpbnB1dC5zbGljZSgwLCBjdXJzb3JPZmZzZXQpICsgJ3gnICsgaW5wdXQuc2xpY2UoY3Vyc29yT2Zmc2V0KSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHNldEN1cnNvck9mZnNldChjdXJzb3JPZmZzZXQgKyAxKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuICAgICAgICAgIHN0b3BPckRpc21pc3NBZ2VudCh0YXNrLmlkLCBzZXRBcHBTdGF0ZSlcbiAgICAgICAgICBpZiAodGFzay5zdGF0dXMgIT09ICdydW5uaW5nJykge1xuICAgICAgICAgICAgc2V0Q29vcmRpbmF0b3JUYXNrSW5kZXgoaSA9PiBNYXRoLm1heChtaW5Db29yZGluYXRvckluZGV4LCBpIC0gMSkpXG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIE5vdCBoYW5kbGVkIOKAlCBsZXQgJ3gnIGZhbGwgdGhyb3VnaCB0byB0eXBlLXRvLWV4aXRcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9LFxuICAgIH0sXG4gICAge1xuICAgICAgY29udGV4dDogJ0Zvb3RlcicsXG4gICAgICBpc0FjdGl2ZTogISFmb290ZXJJdGVtU2VsZWN0ZWQgJiYgIWlzTW9kYWxPdmVybGF5QWN0aXZlLFxuICAgIH0sXG4gIClcblxuICB1c2VJbnB1dCgoY2hhciwga2V5KSA9PiB7XG4gICAgLy8gU2tpcCBhbGwgaW5wdXQgaGFuZGxpbmcgd2hlbiBhIGZ1bGwtc2NyZWVuIGRpYWxvZyBpcyBvcGVuLiBUaGVzZSBkaWFsb2dzXG4gICAgLy8gcmVuZGVyIHZpYSBlYXJseSByZXR1cm4sIGJ1dCBob29rcyBydW4gdW5jb25kaXRpb25hbGx5IOKAlCBzbyB3aXRob3V0IHRoaXNcbiAgICAvLyBndWFyZCwgRXNjYXBlIGluc2lkZSBhIGRpYWxvZyBsZWFrcyB0byB0aGUgZG91YmxlLXByZXNzIG1lc3NhZ2Utc2VsZWN0b3IuXG4gICAgaWYgKFxuICAgICAgc2hvd1RlYW1zRGlhbG9nIHx8XG4gICAgICBzaG93UXVpY2tPcGVuIHx8XG4gICAgICBzaG93R2xvYmFsU2VhcmNoIHx8XG4gICAgICBzaG93SGlzdG9yeVBpY2tlclxuICAgICkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gRGV0ZWN0IGZhaWxlZCBBbHQgc2hvcnRjdXRzIG9uIG1hY09TIChPcHRpb24ga2V5IHByb2R1Y2VzIHNwZWNpYWwgY2hhcmFjdGVycylcbiAgICBpZiAoZ2V0UGxhdGZvcm0oKSA9PT0gJ21hY29zJyAmJiBpc01hY29zT3B0aW9uQ2hhcihjaGFyKSkge1xuICAgICAgY29uc3Qgc2hvcnRjdXQgPSBNQUNPU19PUFRJT05fU1BFQ0lBTF9DSEFSU1tjaGFyXVxuICAgICAgY29uc3QgdGVybWluYWxOYW1lID0gZ2V0TmF0aXZlQ1NJdVRlcm1pbmFsRGlzcGxheU5hbWUoKVxuICAgICAgY29uc3QganN4ID0gdGVybWluYWxOYW1lID8gKFxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICBUbyBlbmFibGUge3Nob3J0Y3V0fSwgc2V0IDxUZXh0IGJvbGQ+T3B0aW9uIGFzIE1ldGE8L1RleHQ+IGlueycgJ31cbiAgICAgICAgICB7dGVybWluYWxOYW1lfSBwcmVmZXJlbmNlcyAo4oyYLClcbiAgICAgICAgPC9UZXh0PlxuICAgICAgKSA6IChcbiAgICAgICAgPFRleHQgZGltQ29sb3I+VG8gZW5hYmxlIHtzaG9ydGN1dH0sIHJ1biAvdGVybWluYWwtc2V0dXA8L1RleHQ+XG4gICAgICApXG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICdvcHRpb24tbWV0YS1oaW50JyxcbiAgICAgICAganN4LFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogNTAwMCxcbiAgICAgIH0pXG4gICAgICAvLyBEb24ndCByZXR1cm4gLSBsZXQgdGhlIGNoYXJhY3RlciBiZSB0eXBlZCBzbyB1c2VyIHNlZXMgdGhlIGlzc3VlXG4gICAgfVxuXG4gICAgLy8gRm9vdGVyIG5hdmlnYXRpb24gaXMgaGFuZGxlZCB2aWEgdXNlS2V5YmluZGluZ3MgYWJvdmUgKEZvb3RlciBjb250ZXh0KVxuXG4gICAgLy8gTk9URTogY3RybCtfLCBjdHJsK2csIGN0cmwrcyBhcmUgaGFuZGxlZCB2aWEgQ2hhdCBjb250ZXh0IGtleWJpbmRpbmdzIGFib3ZlXG5cbiAgICAvLyBUeXBlLXRvLWV4aXQgZm9vdGVyOiBwcmludGFibGUgY2hhcnMgd2hpbGUgYSBwaWxsIGlzIHNlbGVjdGVkIHJlZm9jdXNcbiAgICAvLyB0aGUgaW5wdXQgYW5kIHR5cGUgdGhlIGNoYXIuIE5hdiBrZXlzIGFyZSBjYXB0dXJlZCBieSB1c2VLZXliaW5kaW5nc1xuICAgIC8vIGFib3ZlLCBzbyBhbnl0aGluZyByZWFjaGluZyBoZXJlIGlzIGdlbnVpbmVseSBub3QgYSBmb290ZXIgYWN0aW9uLlxuICAgIC8vIG9uQ2hhbmdlIGNsZWFycyBmb290ZXJTZWxlY3Rpb24sIHNvIG5vIGV4cGxpY2l0IGRlc2VsZWN0LlxuICAgIGlmIChcbiAgICAgIGZvb3Rlckl0ZW1TZWxlY3RlZCAmJlxuICAgICAgY2hhciAmJlxuICAgICAgIWtleS5jdHJsICYmXG4gICAgICAha2V5Lm1ldGEgJiZcbiAgICAgICFrZXkuZXNjYXBlICYmXG4gICAgICAha2V5LnJldHVyblxuICAgICkge1xuICAgICAgb25DaGFuZ2UoaW5wdXQuc2xpY2UoMCwgY3Vyc29yT2Zmc2V0KSArIGNoYXIgKyBpbnB1dC5zbGljZShjdXJzb3JPZmZzZXQpKVxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KGN1cnNvck9mZnNldCArIGNoYXIubGVuZ3RoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gRXhpdCBzcGVjaWFsIG1vZGVzIHdoZW4gYmFja3NwYWNlL2VzY2FwZS9kZWxldGUvY3RybCt1IGlzIHByZXNzZWQgYXQgY3Vyc29yIHBvc2l0aW9uIDBcbiAgICBpZiAoXG4gICAgICBjdXJzb3JPZmZzZXQgPT09IDAgJiZcbiAgICAgIChrZXkuZXNjYXBlIHx8IGtleS5iYWNrc3BhY2UgfHwga2V5LmRlbGV0ZSB8fCAoa2V5LmN0cmwgJiYgY2hhciA9PT0gJ3UnKSlcbiAgICApIHtcbiAgICAgIG9uTW9kZUNoYW5nZSgncHJvbXB0JylcbiAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgIH1cblxuICAgIC8vIEV4aXQgaGVscCBtb2RlIHdoZW4gYmFja3NwYWNlIGlzIHByZXNzZWQgYW5kIGlucHV0IGlzIGVtcHR5XG4gICAgaWYgKGhlbHBPcGVuICYmIGlucHV0ID09PSAnJyAmJiAoa2V5LmJhY2tzcGFjZSB8fCBrZXkuZGVsZXRlKSkge1xuICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgfVxuXG4gICAgLy8gZXNjIGlzIGEgbGl0dGxlIG92ZXJsb2FkZWQ6XG4gICAgLy8gLSB3aGVuIHdlJ3JlIGxvYWRpbmcgYSByZXNwb25zZSwgaXQncyB1c2VkIHRvIGNhbmNlbCB0aGUgcmVxdWVzdFxuICAgIC8vIC0gb3RoZXJ3aXNlLCBpdCdzIHVzZWQgdG8gc2hvdyB0aGUgbWVzc2FnZSBzZWxlY3RvclxuICAgIC8vIC0gd2hlbiBkb3VibGUgcHJlc3NlZCwgaXQncyB1c2VkIHRvIGNsZWFyIHRoZSBpbnB1dFxuICAgIC8vIC0gd2hlbiBpbnB1dCBpcyBlbXB0eSwgcG9wIGZyb20gY29tbWFuZCBxdWV1ZVxuXG4gICAgLy8gSGFuZGxlIEVTQyBrZXkgcHJlc3NcbiAgICBpZiAoa2V5LmVzY2FwZSkge1xuICAgICAgLy8gQWJvcnQgYWN0aXZlIHNwZWN1bGF0aW9uXG4gICAgICBpZiAoc3BlY3VsYXRpb24uc3RhdHVzID09PSAnYWN0aXZlJykge1xuICAgICAgICBhYm9ydFNwZWN1bGF0aW9uKHNldEFwcFN0YXRlKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gRGlzbWlzcyBzaWRlIHF1ZXN0aW9uIHJlc3BvbnNlIGlmIHZpc2libGVcbiAgICAgIGlmIChpc1NpZGVRdWVzdGlvblZpc2libGUgJiYgb25EaXNtaXNzU2lkZVF1ZXN0aW9uKSB7XG4gICAgICAgIG9uRGlzbWlzc1NpZGVRdWVzdGlvbigpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBDbG9zZSBoZWxwIG1lbnUgaWYgb3BlblxuICAgICAgaWYgKGhlbHBPcGVuKSB7XG4gICAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gRm9vdGVyIHNlbGVjdGlvbiBjbGVhcmluZyBpcyBub3cgaGFuZGxlZCB2aWEgRm9vdGVyIGNvbnRleHQga2V5YmluZGluZ3NcbiAgICAgIC8vIChmb290ZXI6Y2xlYXJTZWxlY3Rpb24gYWN0aW9uIGJvdW5kIHRvIGVzY2FwZSlcbiAgICAgIC8vIElmIGEgZm9vdGVyIGl0ZW0gaXMgc2VsZWN0ZWQsIGxldCB0aGUgRm9vdGVyIGtleWJpbmRpbmcgaGFuZGxlIGl0XG4gICAgICBpZiAoZm9vdGVySXRlbVNlbGVjdGVkKSB7XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBJZiB0aGVyZSdzIGFuIGVkaXRhYmxlIHF1ZXVlZCBjb21tYW5kLCBtb3ZlIGl0IHRvIHRoZSBpbnB1dCBmb3IgZWRpdGluZyB3aGVuIEVTQyBpcyBwcmVzc2VkXG4gICAgICBjb25zdCBoYXNFZGl0YWJsZUNvbW1hbmQgPSBxdWV1ZWRDb21tYW5kcy5zb21lKGlzUXVldWVkQ29tbWFuZEVkaXRhYmxlKVxuICAgICAgaWYgKGhhc0VkaXRhYmxlQ29tbWFuZCkge1xuICAgICAgICB2b2lkIHBvcEFsbENvbW1hbmRzRnJvbVF1ZXVlKClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGlmIChtZXNzYWdlcy5sZW5ndGggPiAwICYmICFpbnB1dCAmJiAhaXNMb2FkaW5nKSB7XG4gICAgICAgIGRvdWJsZVByZXNzRXNjRnJvbUVtcHR5KClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoa2V5LnJldHVybiAmJiBoZWxwT3Blbikge1xuICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgfVxuICB9KVxuXG4gIGNvbnN0IHN3YXJtQmFubmVyID0gdXNlU3dhcm1CYW5uZXIoKVxuXG4gIGNvbnN0IGZhc3RNb2RlQ29vbGRvd24gPSBpc0Zhc3RNb2RlRW5hYmxlZCgpID8gaXNGYXN0TW9kZUNvb2xkb3duKCkgOiBmYWxzZVxuICBjb25zdCBzaG93RmFzdEljb24gPSBpc0Zhc3RNb2RlRW5hYmxlZCgpXG4gICAgPyBpc0Zhc3RNb2RlICYmIChpc0Zhc3RNb2RlQXZhaWxhYmxlKCkgfHwgZmFzdE1vZGVDb29sZG93bilcbiAgICA6IGZhbHNlXG5cbiAgY29uc3Qgc2hvd0Zhc3RJY29uSGludCA9IHVzZVNob3dGYXN0SWNvbkhpbnQoc2hvd0Zhc3RJY29uID8/IGZhbHNlKVxuXG4gIC8vIFNob3cgZWZmb3J0IG5vdGlmaWNhdGlvbiBvbiBzdGFydHVwIGFuZCB3aGVuIGVmZm9ydCBjaGFuZ2VzLlxuICAvLyBTdXBwcmVzc2VkIGluIGJyaWVmL2Fzc2lzdGFudCBtb2RlIOKAlCB0aGUgdmFsdWUgcmVmbGVjdHMgdGhlIGxvY2FsXG4gIC8vIGNsaWVudCdzIGVmZm9ydCwgbm90IHRoZSBjb25uZWN0ZWQgYWdlbnQncy5cbiAgY29uc3QgZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCA9IGJyaWVmT3duc0dhcFxuICAgID8gdW5kZWZpbmVkXG4gICAgOiBnZXRFZmZvcnROb3RpZmljYXRpb25UZXh0KGVmZm9ydFZhbHVlLCBtYWluTG9vcE1vZGVsKVxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCkge1xuICAgICAgcmVtb3ZlTm90aWZpY2F0aW9uKCdlZmZvcnQtbGV2ZWwnKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICBrZXk6ICdlZmZvcnQtbGV2ZWwnLFxuICAgICAgdGV4dDogZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCxcbiAgICAgIHByaW9yaXR5OiAnaGlnaCcsXG4gICAgICB0aW1lb3V0TXM6IDEyXzAwMCxcbiAgICB9KVxuICB9LCBbZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCwgYWRkTm90aWZpY2F0aW9uLCByZW1vdmVOb3RpZmljYXRpb25dKVxuXG4gIHVzZUJ1ZGR5Tm90aWZpY2F0aW9uKClcblxuICBjb25zdCBjb21wYW5pb25TcGVha2luZyA9IGZlYXR1cmUoJ0JVRERZJylcbiAgICA/IC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgICAgIHVzZUFwcFN0YXRlKHMgPT4gcy5jb21wYW5pb25SZWFjdGlvbiAhPT0gdW5kZWZpbmVkKVxuICAgIDogZmFsc2VcbiAgY29uc3QgeyBjb2x1bW5zLCByb3dzIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCB0ZXh0SW5wdXRDb2x1bW5zID1cbiAgICBjb2x1bW5zIC0gMyAtIGNvbXBhbmlvblJlc2VydmVkQ29sdW1ucyhjb2x1bW5zLCBjb21wYW5pb25TcGVha2luZylcblxuICAvLyBQT0M6IGNsaWNrLXRvLXBvc2l0aW9uLWN1cnNvci4gTW91c2UgdHJhY2tpbmcgaXMgb25seSBlbmFibGVkIGluc2lkZVxuICAvLyA8QWx0ZXJuYXRlU2NyZWVuPiwgc28gdGhpcyBpcyBkb3JtYW50IGluIHRoZSBub3JtYWwgbWFpbi1zY3JlZW4gUkVQTC5cbiAgLy8gbG9jYWxDb2wvbG9jYWxSb3cgYXJlIHJlbGF0aXZlIHRvIHRoZSBvbkNsaWNrIEJveCdzIHRvcC1sZWZ0OyB0aGUgQm94XG4gIC8vIHRpZ2h0bHkgd3JhcHMgdGhlIHRleHQgaW5wdXQgc28gdGhleSBtYXAgZGlyZWN0bHkgdG8gKGNvbHVtbiwgbGluZSlcbiAgLy8gaW4gdGhlIEN1cnNvciB3cmFwIG1vZGVsLiBNZWFzdXJlZFRleHQuZ2V0T2Zmc2V0RnJvbVBvc2l0aW9uIGhhbmRsZXNcbiAgLy8gd2lkZSBjaGFycywgd3JhcHBlZCBsaW5lcywgYW5kIGNsYW1wcyBwYXN0LWVuZCBjbGlja3MgdG8gbGluZSBlbmQuXG4gIGNvbnN0IG1heFZpc2libGVMaW5lcyA9IGlzRnVsbHNjcmVlbkVudkVuYWJsZWQoKVxuICAgID8gTWF0aC5tYXgoXG4gICAgICAgIE1JTl9JTlBVVF9WSUVXUE9SVF9MSU5FUyxcbiAgICAgICAgTWF0aC5mbG9vcihyb3dzIC8gMikgLSBQUk9NUFRfRk9PVEVSX0xJTkVTLFxuICAgICAgKVxuICAgIDogdW5kZWZpbmVkXG5cbiAgY29uc3QgaGFuZGxlSW5wdXRDbGljayA9IHVzZUNhbGxiYWNrKFxuICAgIChlOiBDbGlja0V2ZW50KSA9PiB7XG4gICAgICAvLyBEdXJpbmcgaGlzdG9yeSBzZWFyY2ggdGhlIGRpc3BsYXllZCB0ZXh0IGlzIGhpc3RvcnlNYXRjaCwgbm90XG4gICAgICAvLyBpbnB1dCwgYW5kIHNob3dDdXJzb3IgaXMgZmFsc2UgYW55d2F5IOKAlCBza2lwIHJhdGhlciB0aGFuXG4gICAgICAvLyBjb21wdXRlIGFuIG9mZnNldCBhZ2FpbnN0IHRoZSB3cm9uZyBzdHJpbmcuXG4gICAgICBpZiAoIWlucHV0IHx8IGlzU2VhcmNoaW5nSGlzdG9yeSkgcmV0dXJuXG4gICAgICBjb25zdCBjID0gQ3Vyc29yLmZyb21UZXh0KGlucHV0LCB0ZXh0SW5wdXRDb2x1bW5zLCBjdXJzb3JPZmZzZXQpXG4gICAgICBjb25zdCB2aWV3cG9ydFN0YXJ0ID0gYy5nZXRWaWV3cG9ydFN0YXJ0TGluZShtYXhWaXNpYmxlTGluZXMpXG4gICAgICBjb25zdCBvZmZzZXQgPSBjLm1lYXN1cmVkVGV4dC5nZXRPZmZzZXRGcm9tUG9zaXRpb24oe1xuICAgICAgICBsaW5lOiBlLmxvY2FsUm93ICsgdmlld3BvcnRTdGFydCxcbiAgICAgICAgY29sdW1uOiBlLmxvY2FsQ29sLFxuICAgICAgfSlcbiAgICAgIHNldEN1cnNvck9mZnNldChvZmZzZXQpXG4gICAgfSxcbiAgICBbXG4gICAgICBpbnB1dCxcbiAgICAgIHRleHRJbnB1dENvbHVtbnMsXG4gICAgICBpc1NlYXJjaGluZ0hpc3RvcnksXG4gICAgICBjdXJzb3JPZmZzZXQsXG4gICAgICBtYXhWaXNpYmxlTGluZXMsXG4gICAgXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZU9wZW5UYXNrc0RpYWxvZyA9IHVzZUNhbGxiYWNrKFxuICAgICh0YXNrSWQ/OiBzdHJpbmcpID0+IHNldFNob3dCYXNoZXNEaWFsb2codGFza0lkID8/IHRydWUpLFxuICAgIFtzZXRTaG93QmFzaGVzRGlhbG9nXSxcbiAgKVxuXG4gIGNvbnN0IHBsYWNlaG9sZGVyID1cbiAgICBzaG93UHJvbXB0U3VnZ2VzdGlvbiAmJiBwcm9tcHRTdWdnZXN0aW9uXG4gICAgICA/IHByb21wdFN1Z2dlc3Rpb25cbiAgICAgIDogZGVmYXVsdFBsYWNlaG9sZGVyXG5cbiAgLy8gQ2FsY3VsYXRlIGlmIGlucHV0IGhhcyBtdWx0aXBsZSBsaW5lc1xuICBjb25zdCBpc0lucHV0V3JhcHBlZCA9IHVzZU1lbW8oKCkgPT4gaW5wdXQuaW5jbHVkZXMoJ1xcbicpLCBbaW5wdXRdKVxuXG4gIC8vIE1lbW9pemVkIGNhbGxiYWNrcyBmb3IgbW9kZWwgcGlja2VyIHRvIHByZXZlbnQgcmUtcmVuZGVycyB3aGVuIHVucmVsYXRlZFxuICAvLyBzdGF0ZSAobGlrZSBub3RpZmljYXRpb25zKSBjaGFuZ2VzLiBUaGlzIHByZXZlbnRzIHRoZSBpbmxpbmUgbW9kZWwgcGlja2VyXG4gIC8vIGZyb20gdmlzdWFsbHkgXCJqdW1waW5nXCIgd2hlbiBub3RpZmljYXRpb25zIGFycml2ZS5cbiAgY29uc3QgaGFuZGxlTW9kZWxTZWxlY3QgPSB1c2VDYWxsYmFjayhcbiAgICAobW9kZWw6IHN0cmluZyB8IG51bGwsIF9lZmZvcnQ6IEVmZm9ydExldmVsIHwgdW5kZWZpbmVkKSA9PiB7XG4gICAgICBsZXQgd2FzRmFzdE1vZGVEaXNhYmxlZCA9IGZhbHNlXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgd2FzRmFzdE1vZGVEaXNhYmxlZCA9XG4gICAgICAgICAgaXNGYXN0TW9kZUVuYWJsZWQoKSAmJlxuICAgICAgICAgICFpc0Zhc3RNb2RlU3VwcG9ydGVkQnlNb2RlbChtb2RlbCkgJiZcbiAgICAgICAgICAhIXByZXYuZmFzdE1vZGVcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgIG1haW5Mb29wTW9kZWw6IG1vZGVsLFxuICAgICAgICAgIG1haW5Mb29wTW9kZWxGb3JTZXNzaW9uOiBudWxsLFxuICAgICAgICAgIC8vIFR1cm4gb2ZmIGZhc3QgbW9kZSBpZiBzd2l0Y2hpbmcgdG8gYSBtb2RlbCB0aGF0IGRvZXNuJ3Qgc3VwcG9ydCBpdFxuICAgICAgICAgIC4uLih3YXNGYXN0TW9kZURpc2FibGVkICYmIHsgZmFzdE1vZGU6IGZhbHNlIH0pLFxuICAgICAgICB9XG4gICAgICB9KVxuICAgICAgc2V0U2hvd01vZGVsUGlja2VyKGZhbHNlKVxuICAgICAgY29uc3QgZWZmZWN0aXZlRmFzdE1vZGUgPSAoaXNGYXN0TW9kZSA/PyBmYWxzZSkgJiYgIXdhc0Zhc3RNb2RlRGlzYWJsZWRcbiAgICAgIGxldCBtZXNzYWdlID0gYE1vZGVsIHNldCB0byAke21vZGVsRGlzcGxheVN0cmluZyhtb2RlbCl9YFxuICAgICAgaWYgKFxuICAgICAgICBpc0JpbGxlZEFzRXh0cmFVc2FnZShtb2RlbCwgZWZmZWN0aXZlRmFzdE1vZGUsIGlzT3B1czFtTWVyZ2VFbmFibGVkKCkpXG4gICAgICApIHtcbiAgICAgICAgbWVzc2FnZSArPSAnIMK3IEJpbGxlZCBhcyBleHRyYSB1c2FnZSdcbiAgICAgIH1cbiAgICAgIGlmICh3YXNGYXN0TW9kZURpc2FibGVkKSB7XG4gICAgICAgIG1lc3NhZ2UgKz0gJyDCtyBGYXN0IG1vZGUgT0ZGJ1xuICAgICAgfVxuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAnbW9kZWwtc3dpdGNoZWQnLFxuICAgICAgICBqc3g6IDxUZXh0PnttZXNzYWdlfTwvVGV4dD4sXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgdGltZW91dE1zOiAzMDAwLFxuICAgICAgfSlcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9tb2RlbF9waWNrZXJfaG90a2V5Jywge1xuICAgICAgICBtb2RlbDpcbiAgICAgICAgICBtb2RlbCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcbiAgICB9LFxuICAgIFtzZXRBcHBTdGF0ZSwgYWRkTm90aWZpY2F0aW9uLCBpc0Zhc3RNb2RlXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZU1vZGVsQ2FuY2VsID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHNldFNob3dNb2RlbFBpY2tlcihmYWxzZSlcbiAgfSwgW10pXG5cbiAgLy8gTWVtb2l6ZSB0aGUgbW9kZWwgcGlja2VyIGVsZW1lbnQgdG8gcHJldmVudCB1bm5lY2Vzc2FyeSByZS1yZW5kZXJzXG4gIC8vIHdoZW4gQXBwU3RhdGUgY2hhbmdlcyBmb3IgdW5yZWxhdGVkIHJlYXNvbnMgKGUuZy4sIG5vdGlmaWNhdGlvbnMgYXJyaXZpbmcpXG4gIGNvbnN0IG1vZGVsUGlja2VyRWxlbWVudCA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmICghc2hvd01vZGVsUGlja2VyKSByZXR1cm4gbnVsbFxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8TW9kZWxQaWNrZXJcbiAgICAgICAgICBpbml0aWFsPXttYWluTG9vcE1vZGVsX31cbiAgICAgICAgICBzZXNzaW9uTW9kZWw9e21haW5Mb29wTW9kZWxGb3JTZXNzaW9ufVxuICAgICAgICAgIG9uU2VsZWN0PXtoYW5kbGVNb2RlbFNlbGVjdH1cbiAgICAgICAgICBvbkNhbmNlbD17aGFuZGxlTW9kZWxDYW5jZWx9XG4gICAgICAgICAgaXNTdGFuZGFsb25lQ29tbWFuZFxuICAgICAgICAgIHNob3dGYXN0TW9kZU5vdGljZT17XG4gICAgICAgICAgICBpc0Zhc3RNb2RlRW5hYmxlZCgpICYmXG4gICAgICAgICAgICBpc0Zhc3RNb2RlICYmXG4gICAgICAgICAgICBpc0Zhc3RNb2RlU3VwcG9ydGVkQnlNb2RlbChtYWluTG9vcE1vZGVsXykgJiZcbiAgICAgICAgICAgIGlzRmFzdE1vZGVBdmFpbGFibGUoKVxuICAgICAgICAgIH1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfSwgW1xuICAgIHNob3dNb2RlbFBpY2tlcixcbiAgICBtYWluTG9vcE1vZGVsXyxcbiAgICBtYWluTG9vcE1vZGVsRm9yU2Vzc2lvbixcbiAgICBoYW5kbGVNb2RlbFNlbGVjdCxcbiAgICBoYW5kbGVNb2RlbENhbmNlbCxcbiAgXSlcblxuICBjb25zdCBoYW5kbGVGYXN0TW9kZVNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgIChyZXN1bHQ/OiBzdHJpbmcpID0+IHtcbiAgICAgIHNldFNob3dGYXN0TW9kZVBpY2tlcihmYWxzZSlcbiAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAgICBrZXk6ICdmYXN0LW1vZGUtdG9nZ2xlZCcsXG4gICAgICAgICAganN4OiA8VGV4dD57cmVzdWx0fTwvVGV4dD4sXG4gICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgIHRpbWVvdXRNczogMzAwMCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9LFxuICAgIFthZGROb3RpZmljYXRpb25dLFxuICApXG5cbiAgLy8gTWVtb2l6ZSB0aGUgZmFzdCBtb2RlIHBpY2tlciBlbGVtZW50XG4gIGNvbnN0IGZhc3RNb2RlUGlja2VyRWxlbWVudCA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmICghc2hvd0Zhc3RNb2RlUGlja2VyKSByZXR1cm4gbnVsbFxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8RmFzdE1vZGVQaWNrZXJcbiAgICAgICAgICBvbkRvbmU9e2hhbmRsZUZhc3RNb2RlU2VsZWN0fVxuICAgICAgICAgIHVuYXZhaWxhYmxlUmVhc29uPXtnZXRGYXN0TW9kZVVuYXZhaWxhYmxlUmVhc29uKCl9XG4gICAgICAgIC8+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH0sIFtzaG93RmFzdE1vZGVQaWNrZXIsIGhhbmRsZUZhc3RNb2RlU2VsZWN0XSlcblxuICAvLyBNZW1vaXplZCBjYWxsYmFja3MgZm9yIHRoaW5raW5nIHRvZ2dsZVxuICBjb25zdCBoYW5kbGVUaGlua2luZ1NlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgIChlbmFibGVkOiBib29sZWFuKSA9PiB7XG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgIC4uLnByZXYsXG4gICAgICAgIHRoaW5raW5nRW5hYmxlZDogZW5hYmxlZCxcbiAgICAgIH0pKVxuICAgICAgc2V0U2hvd1RoaW5raW5nVG9nZ2xlKGZhbHNlKVxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3RoaW5raW5nX3RvZ2dsZWRfaG90a2V5JywgeyBlbmFibGVkIH0pXG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICd0aGlua2luZy10b2dnbGVkLWhvdGtleScsXG4gICAgICAgIGpzeDogKFxuICAgICAgICAgIDxUZXh0IGNvbG9yPXtlbmFibGVkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfSBkaW1Db2xvcj17IWVuYWJsZWR9PlxuICAgICAgICAgICAgVGhpbmtpbmcge2VuYWJsZWQgPyAnb24nIDogJ29mZid9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApLFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogMzAwMCxcbiAgICAgIH0pXG4gICAgfSxcbiAgICBbc2V0QXBwU3RhdGUsIGFkZE5vdGlmaWNhdGlvbl0sXG4gIClcblxuICBjb25zdCBoYW5kbGVUaGlua2luZ0NhbmNlbCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTaG93VGhpbmtpbmdUb2dnbGUoZmFsc2UpXG4gIH0sIFtdKVxuXG4gIC8vIE1lbW9pemUgdGhlIHRoaW5raW5nIHRvZ2dsZSBlbGVtZW50XG4gIGNvbnN0IHRoaW5raW5nVG9nZ2xlRWxlbWVudCA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmICghc2hvd1RoaW5raW5nVG9nZ2xlKSByZXR1cm4gbnVsbFxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8VGhpbmtpbmdUb2dnbGVcbiAgICAgICAgICBjdXJyZW50VmFsdWU9e3RoaW5raW5nRW5hYmxlZCA/PyB0cnVlfVxuICAgICAgICAgIG9uU2VsZWN0PXtoYW5kbGVUaGlua2luZ1NlbGVjdH1cbiAgICAgICAgICBvbkNhbmNlbD17aGFuZGxlVGhpbmtpbmdDYW5jZWx9XG4gICAgICAgICAgaXNNaWRDb252ZXJzYXRpb249e21lc3NhZ2VzLnNvbWUobSA9PiBtLnR5cGUgPT09ICdhc3Npc3RhbnQnKX1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfSwgW1xuICAgIHNob3dUaGlua2luZ1RvZ2dsZSxcbiAgICB0aGlua2luZ0VuYWJsZWQsXG4gICAgaGFuZGxlVGhpbmtpbmdTZWxlY3QsXG4gICAgaGFuZGxlVGhpbmtpbmdDYW5jZWwsXG4gICAgbWVzc2FnZXMubGVuZ3RoLFxuICBdKVxuXG4gIC8vIFBvcnRhbCBkaWFsb2cgdG8gRGlhbG9nT3ZlcmxheSBpbiBmdWxsc2NyZWVuIHNvIGl0IGVzY2FwZXMgdGhlIGJvdHRvbVxuICAvLyBzbG90J3Mgb3ZlcmZsb3dZOmhpZGRlbiBjbGlwIChzYW1lIHBhdHRlcm4gYXMgU3VnZ2VzdGlvbnNPdmVybGF5KS5cbiAgLy8gTXVzdCBiZSBjYWxsZWQgYmVmb3JlIGVhcmx5IHJldHVybnMgYmVsb3cgdG8gc2F0aXNmeSBydWxlcy1vZi1ob29rcy5cbiAgLy8gTWVtb2l6ZWQgc28gdGhlIHBvcnRhbCB1c2VFZmZlY3QgZG9lc24ndCBjaHVybiBvbiBldmVyeSBQcm9tcHRJbnB1dCByZW5kZXIuXG4gIGNvbnN0IGF1dG9Nb2RlT3B0SW5EaWFsb2cgPSB1c2VNZW1vKFxuICAgICgpID0+XG4gICAgICBmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSAmJiBzaG93QXV0b01vZGVPcHRJbiA/IChcbiAgICAgICAgPEF1dG9Nb2RlT3B0SW5EaWFsb2dcbiAgICAgICAgICBvbkFjY2VwdD17aGFuZGxlQXV0b01vZGVPcHRJbkFjY2VwdH1cbiAgICAgICAgICBvbkRlY2xpbmU9e2hhbmRsZUF1dG9Nb2RlT3B0SW5EZWNsaW5lfVxuICAgICAgICAvPlxuICAgICAgKSA6IG51bGwsXG4gICAgW3Nob3dBdXRvTW9kZU9wdEluLCBoYW5kbGVBdXRvTW9kZU9wdEluQWNjZXB0LCBoYW5kbGVBdXRvTW9kZU9wdEluRGVjbGluZV0sXG4gIClcbiAgdXNlU2V0UHJvbXB0T3ZlcmxheURpYWxvZyhcbiAgICBpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgPyBhdXRvTW9kZU9wdEluRGlhbG9nIDogbnVsbCxcbiAgKVxuXG4gIGlmIChzaG93QmFzaGVzRGlhbG9nKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCYWNrZ3JvdW5kVGFza3NEaWFsb2dcbiAgICAgICAgb25Eb25lPXsoKSA9PiBzZXRTaG93QmFzaGVzRGlhbG9nKGZhbHNlKX1cbiAgICAgICAgdG9vbFVzZUNvbnRleHQ9e2dldFRvb2xVc2VDb250ZXh0KFxuICAgICAgICAgIG1lc3NhZ2VzLFxuICAgICAgICAgIFtdLFxuICAgICAgICAgIG5ldyBBYm9ydENvbnRyb2xsZXIoKSxcbiAgICAgICAgICBtYWluTG9vcE1vZGVsLFxuICAgICAgICApfVxuICAgICAgICBpbml0aWFsRGV0YWlsVGFza0lkPXtcbiAgICAgICAgICB0eXBlb2Ygc2hvd0Jhc2hlc0RpYWxvZyA9PT0gJ3N0cmluZycgPyBzaG93QmFzaGVzRGlhbG9nIDogdW5kZWZpbmVkXG4gICAgICAgIH1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgaWYgKGlzQWdlbnRTd2FybXNFbmFibGVkKCkgJiYgc2hvd1RlYW1zRGlhbG9nKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxUZWFtc0RpYWxvZ1xuICAgICAgICBpbml0aWFsVGVhbXM9e2NhY2hlZFRlYW1zfVxuICAgICAgICBvbkRvbmU9eygpID0+IHtcbiAgICAgICAgICBzZXRTaG93VGVhbXNEaWFsb2coZmFsc2UpXG4gICAgICAgIH19XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdRVUlDS19TRUFSQ0gnKSkge1xuICAgIGNvbnN0IGluc2VydFdpdGhTcGFjaW5nID0gKHRleHQ6IHN0cmluZykgPT4ge1xuICAgICAgY29uc3QgY3Vyc29yQ2hhciA9IGlucHV0W2N1cnNvck9mZnNldCAtIDFdID8/ICcgJ1xuICAgICAgaW5zZXJ0VGV4dEF0Q3Vyc29yKC9cXHMvLnRlc3QoY3Vyc29yQ2hhcikgPyB0ZXh0IDogYCAke3RleHR9YClcbiAgICB9XG4gICAgaWYgKHNob3dRdWlja09wZW4pIHtcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxRdWlja09wZW5EaWFsb2dcbiAgICAgICAgICBvbkRvbmU9eygpID0+IHNldFNob3dRdWlja09wZW4oZmFsc2UpfVxuICAgICAgICAgIG9uSW5zZXJ0PXtpbnNlcnRXaXRoU3BhY2luZ31cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICB9XG4gICAgaWYgKHNob3dHbG9iYWxTZWFyY2gpIHtcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxHbG9iYWxTZWFyY2hEaWFsb2dcbiAgICAgICAgICBvbkRvbmU9eygpID0+IHNldFNob3dHbG9iYWxTZWFyY2goZmFsc2UpfVxuICAgICAgICAgIG9uSW5zZXJ0PXtpbnNlcnRXaXRoU3BhY2luZ31cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICB9XG4gIH1cblxuICBpZiAoZmVhdHVyZSgnSElTVE9SWV9QSUNLRVInKSAmJiBzaG93SGlzdG9yeVBpY2tlcikge1xuICAgIHJldHVybiAoXG4gICAgICA8SGlzdG9yeVNlYXJjaERpYWxvZ1xuICAgICAgICBpbml0aWFsUXVlcnk9e2lucHV0fVxuICAgICAgICBvblNlbGVjdD17ZW50cnkgPT4ge1xuICAgICAgICAgIGNvbnN0IGVudHJ5TW9kZSA9IGdldE1vZGVGcm9tSW5wdXQoZW50cnkuZGlzcGxheSlcbiAgICAgICAgICBjb25zdCB2YWx1ZSA9IGdldFZhbHVlRnJvbUlucHV0KGVudHJ5LmRpc3BsYXkpXG4gICAgICAgICAgb25Nb2RlQ2hhbmdlKGVudHJ5TW9kZSlcbiAgICAgICAgICB0cmFja0FuZFNldElucHV0KHZhbHVlKVxuICAgICAgICAgIHNldFBhc3RlZENvbnRlbnRzKGVudHJ5LnBhc3RlZENvbnRlbnRzKVxuICAgICAgICAgIHNldEN1cnNvck9mZnNldCh2YWx1ZS5sZW5ndGgpXG4gICAgICAgICAgc2V0U2hvd0hpc3RvcnlQaWNrZXIoZmFsc2UpXG4gICAgICAgIH19XG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBzZXRTaG93SGlzdG9yeVBpY2tlcihmYWxzZSl9XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIC8vIFNob3cgbG9vcCBtb2RlIG1lbnUgd2hlbiByZXF1ZXN0ZWQgKGFudC1vbmx5LCBlbGltaW5hdGVkIGZyb20gZXh0ZXJuYWwgYnVpbGRzKVxuICBpZiAobW9kZWxQaWNrZXJFbGVtZW50KSB7XG4gICAgcmV0dXJuIG1vZGVsUGlja2VyRWxlbWVudFxuICB9XG5cbiAgaWYgKGZhc3RNb2RlUGlja2VyRWxlbWVudCkge1xuICAgIHJldHVybiBmYXN0TW9kZVBpY2tlckVsZW1lbnRcbiAgfVxuXG4gIGlmICh0aGlua2luZ1RvZ2dsZUVsZW1lbnQpIHtcbiAgICByZXR1cm4gdGhpbmtpbmdUb2dnbGVFbGVtZW50XG4gIH1cblxuICBpZiAoc2hvd0JyaWRnZURpYWxvZykge1xuICAgIHJldHVybiAoXG4gICAgICA8QnJpZGdlRGlhbG9nXG4gICAgICAgIG9uRG9uZT17KCkgPT4ge1xuICAgICAgICAgIHNldFNob3dCcmlkZ2VEaWFsb2coZmFsc2UpXG4gICAgICAgICAgc2VsZWN0Rm9vdGVySXRlbShudWxsKVxuICAgICAgICB9fVxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICBjb25zdCBiYXNlUHJvcHM6IEJhc2VUZXh0SW5wdXRQcm9wcyA9IHtcbiAgICBtdWx0aWxpbmU6IHRydWUsXG4gICAgb25TdWJtaXQsXG4gICAgb25DaGFuZ2UsXG4gICAgdmFsdWU6IGhpc3RvcnlNYXRjaFxuICAgICAgPyBnZXRWYWx1ZUZyb21JbnB1dChcbiAgICAgICAgICB0eXBlb2YgaGlzdG9yeU1hdGNoID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgPyBoaXN0b3J5TWF0Y2hcbiAgICAgICAgICAgIDogaGlzdG9yeU1hdGNoLmRpc3BsYXksXG4gICAgICAgIClcbiAgICAgIDogaW5wdXQsXG4gICAgLy8gSGlzdG9yeSBuYXZpZ2F0aW9uIGlzIGhhbmRsZWQgdmlhIFRleHRJbnB1dCBwcm9wcyAob25IaXN0b3J5VXAvb25IaXN0b3J5RG93biksXG4gICAgLy8gTk9UIHZpYSB1c2VLZXliaW5kaW5ncy4gVGhpcyBhbGxvd3MgdXNlVGV4dElucHV0J3MgdXBPckhpc3RvcnlVcC9kb3duT3JIaXN0b3J5RG93blxuICAgIC8vIHRvIHRyeSBjdXJzb3IgbW92ZW1lbnQgZmlyc3QgYW5kIG9ubHkgZmFsbCB0aHJvdWdoIHRvIGhpc3RvcnkgbmF2aWdhdGlvbiB3aGVuIHRoZVxuICAgIC8vIGN1cnNvciBjYW4ndCBtb3ZlIGZ1cnRoZXIgKGltcG9ydGFudCBmb3Igd3JhcHBlZCB0ZXh0IGFuZCBtdWx0aS1saW5lIGlucHV0KS5cbiAgICBvbkhpc3RvcnlVcDogaGFuZGxlSGlzdG9yeVVwLFxuICAgIG9uSGlzdG9yeURvd246IGhhbmRsZUhpc3RvcnlEb3duLFxuICAgIG9uSGlzdG9yeVJlc2V0OiByZXNldEhpc3RvcnksXG4gICAgcGxhY2Vob2xkZXIsXG4gICAgb25FeGl0LFxuICAgIG9uRXhpdE1lc3NhZ2U6IChzaG93LCBrZXkpID0+IHNldEV4aXRNZXNzYWdlKHsgc2hvdywga2V5IH0pLFxuICAgIG9uSW1hZ2VQYXN0ZSxcbiAgICBjb2x1bW5zOiB0ZXh0SW5wdXRDb2x1bW5zLFxuICAgIG1heFZpc2libGVMaW5lcyxcbiAgICBkaXNhYmxlQ3Vyc29yTW92ZW1lbnRGb3JVcERvd25LZXlzOlxuICAgICAgc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCB8fCAhIWZvb3Rlckl0ZW1TZWxlY3RlZCxcbiAgICBkaXNhYmxlRXNjYXBlRG91YmxlUHJlc3M6IHN1Z2dlc3Rpb25zLmxlbmd0aCA+IDAsXG4gICAgY3Vyc29yT2Zmc2V0LFxuICAgIG9uQ2hhbmdlQ3Vyc29yT2Zmc2V0OiBzZXRDdXJzb3JPZmZzZXQsXG4gICAgb25QYXN0ZTogb25UZXh0UGFzdGUsXG4gICAgb25Jc1Bhc3RpbmdDaGFuZ2U6IHNldElzUGFzdGluZyxcbiAgICBmb2N1czogIWlzU2VhcmNoaW5nSGlzdG9yeSAmJiAhaXNNb2RhbE92ZXJsYXlBY3RpdmUgJiYgIWZvb3Rlckl0ZW1TZWxlY3RlZCxcbiAgICBzaG93Q3Vyc29yOlxuICAgICAgIWZvb3Rlckl0ZW1TZWxlY3RlZCAmJiAhaXNTZWFyY2hpbmdIaXN0b3J5ICYmICFjdXJzb3JBdEltYWdlQ2hpcCxcbiAgICBhcmd1bWVudEhpbnQ6IGNvbW1hbmRBcmd1bWVudEhpbnQsXG4gICAgb25VbmRvOiBjYW5VbmRvXG4gICAgICA/ICgpID0+IHtcbiAgICAgICAgICBjb25zdCBwcmV2aW91c1N0YXRlID0gdW5kbygpXG4gICAgICAgICAgaWYgKHByZXZpb3VzU3RhdGUpIHtcbiAgICAgICAgICAgIHRyYWNrQW5kU2V0SW5wdXQocHJldmlvdXNTdGF0ZS50ZXh0KVxuICAgICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KHByZXZpb3VzU3RhdGUuY3Vyc29yT2Zmc2V0KVxuICAgICAgICAgICAgc2V0UGFzdGVkQ29udGVudHMocHJldmlvdXNTdGF0ZS5wYXN0ZWRDb250ZW50cylcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIDogdW5kZWZpbmVkLFxuICAgIGhpZ2hsaWdodHM6IGNvbWJpbmVkSGlnaGxpZ2h0cyxcbiAgICBpbmxpbmVHaG9zdFRleHQsXG4gICAgaW5wdXRGaWx0ZXI6IGxhenlTcGFjZUlucHV0RmlsdGVyLFxuICB9XG5cbiAgY29uc3QgZ2V0Qm9yZGVyQ29sb3IgPSAoKToga2V5b2YgVGhlbWUgPT4ge1xuICAgIGNvbnN0IG1vZGVDb2xvcnM6IFJlY29yZDxzdHJpbmcsIGtleW9mIFRoZW1lPiA9IHtcbiAgICAgIGJhc2g6ICdiYXNoQm9yZGVyJyxcbiAgICB9XG5cbiAgICAvLyBNb2RlIGNvbG9ycyB0YWtlIHByaW9yaXR5LCB0aGVuIHRlYW1tYXRlIGNvbG9yLCB0aGVuIGRlZmF1bHRcbiAgICBpZiAobW9kZUNvbG9yc1ttb2RlXSkge1xuICAgICAgcmV0dXJuIG1vZGVDb2xvcnNbbW9kZV1cbiAgICB9XG5cbiAgICAvLyBJbi1wcm9jZXNzIHRlYW1tYXRlcyBydW4gaGVhZGxlc3MgLSBkb24ndCBhcHBseSB0ZWFtbWF0ZSBjb2xvcnMgdG8gbGVhZGVyIFVJXG4gICAgaWYgKGlzSW5Qcm9jZXNzVGVhbW1hdGUoKSkge1xuICAgICAgcmV0dXJuICdwcm9tcHRCb3JkZXInXG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgZm9yIHRlYW1tYXRlIGNvbG9yIGZyb20gZW52aXJvbm1lbnRcbiAgICBjb25zdCB0ZWFtbWF0ZUNvbG9yTmFtZSA9IGdldFRlYW1tYXRlQ29sb3IoKVxuICAgIGlmIChcbiAgICAgIHRlYW1tYXRlQ29sb3JOYW1lICYmXG4gICAgICBBR0VOVF9DT0xPUlMuaW5jbHVkZXModGVhbW1hdGVDb2xvck5hbWUgYXMgQWdlbnRDb2xvck5hbWUpXG4gICAgKSB7XG4gICAgICByZXR1cm4gQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1JbdGVhbW1hdGVDb2xvck5hbWUgYXMgQWdlbnRDb2xvck5hbWVdXG4gICAgfVxuXG4gICAgcmV0dXJuICdwcm9tcHRCb3JkZXInXG4gIH1cblxuICBpZiAoaXNFeHRlcm5hbEVkaXRvckFjdGl2ZSkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94XG4gICAgICAgIGZsZXhEaXJlY3Rpb249XCJyb3dcIlxuICAgICAgICBhbGlnbkl0ZW1zPVwiY2VudGVyXCJcbiAgICAgICAganVzdGlmeUNvbnRlbnQ9XCJjZW50ZXJcIlxuICAgICAgICBib3JkZXJDb2xvcj17Z2V0Qm9yZGVyQ29sb3IoKX1cbiAgICAgICAgYm9yZGVyU3R5bGU9XCJyb3VuZFwiXG4gICAgICAgIGJvcmRlckxlZnQ9e2ZhbHNlfVxuICAgICAgICBib3JkZXJSaWdodD17ZmFsc2V9XG4gICAgICAgIGJvcmRlckJvdHRvbVxuICAgICAgICB3aWR0aD1cIjEwMCVcIlxuICAgICAgPlxuICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAgU2F2ZSBhbmQgY2xvc2UgZWRpdG9yIHRvIGNvbnRpbnVlLi4uXG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IHRleHRJbnB1dEVsZW1lbnQgPSBpc1ZpbU1vZGVFbmFibGVkKCkgPyAoXG4gICAgPFZpbVRleHRJbnB1dFxuICAgICAgey4uLmJhc2VQcm9wc31cbiAgICAgIGluaXRpYWxNb2RlPXt2aW1Nb2RlfVxuICAgICAgb25Nb2RlQ2hhbmdlPXtzZXRWaW1Nb2RlfVxuICAgIC8+XG4gICkgOiAoXG4gICAgPFRleHRJbnB1dCB7Li4uYmFzZVByb3BzfSAvPlxuICApXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9e2JyaWVmT3duc0dhcCA/IDAgOiAxfT5cbiAgICAgIHshaXNGdWxsc2NyZWVuRW52RW5hYmxlZCgpICYmIDxQcm9tcHRJbnB1dFF1ZXVlZENvbW1hbmRzIC8+fVxuICAgICAge2hhc1N1cHByZXNzZWREaWFsb2dzICYmIChcbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9IG1hcmdpbkxlZnQ9ezJ9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPldhaXRpbmcgZm9yIHBlcm1pc3Npb27igKY8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICAgIDxQcm9tcHRJbnB1dFN0YXNoTm90aWNlIGhhc1N0YXNoPXtzdGFzaGVkUHJvbXB0ICE9PSB1bmRlZmluZWR9IC8+XG4gICAgICB7c3dhcm1CYW5uZXIgPyAoXG4gICAgICAgIDw+XG4gICAgICAgICAgPFRleHQgY29sb3I9e3N3YXJtQmFubmVyLmJnQ29sb3J9PlxuICAgICAgICAgICAge3N3YXJtQmFubmVyLnRleHQgPyAoXG4gICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgeyfilIAnLnJlcGVhdChcbiAgICAgICAgICAgICAgICAgIE1hdGgubWF4KDAsIGNvbHVtbnMgLSBzdHJpbmdXaWR0aChzd2FybUJhbm5lci50ZXh0KSAtIDQpLFxuICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgICAgPFRleHQgYmFja2dyb3VuZENvbG9yPXtzd2FybUJhbm5lci5iZ0NvbG9yfSBjb2xvcj1cImludmVyc2VUZXh0XCI+XG4gICAgICAgICAgICAgICAgICB7JyAnfVxuICAgICAgICAgICAgICAgICAge3N3YXJtQmFubmVyLnRleHR9eycgJ31cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgeyfilIDilIAnfVxuICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICfilIAnLnJlcGVhdChjb2x1bW5zKVxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgd2lkdGg9XCIxMDAlXCI+XG4gICAgICAgICAgICA8UHJvbXB0SW5wdXRNb2RlSW5kaWNhdG9yXG4gICAgICAgICAgICAgIG1vZGU9e21vZGV9XG4gICAgICAgICAgICAgIGlzTG9hZGluZz17aXNMb2FkaW5nfVxuICAgICAgICAgICAgICB2aWV3aW5nQWdlbnROYW1lPXt2aWV3aW5nQWdlbnROYW1lfVxuICAgICAgICAgICAgICB2aWV3aW5nQWdlbnRDb2xvcj17dmlld2luZ0FnZW50Q29sb3J9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAgPEJveCBmbGV4R3Jvdz17MX0gZmxleFNocmluaz17MX0gb25DbGljaz17aGFuZGxlSW5wdXRDbGlja30+XG4gICAgICAgICAgICAgIHt0ZXh0SW5wdXRFbGVtZW50fVxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPFRleHQgY29sb3I9e3N3YXJtQmFubmVyLmJnQ29sb3J9Pnsn4pSAJy5yZXBlYXQoY29sdW1ucyl9PC9UZXh0PlxuICAgICAgICA8Lz5cbiAgICAgICkgOiAoXG4gICAgICAgIDxCb3hcbiAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwicm93XCJcbiAgICAgICAgICBhbGlnbkl0ZW1zPVwiZmxleC1zdGFydFwiXG4gICAgICAgICAganVzdGlmeUNvbnRlbnQ9XCJmbGV4LXN0YXJ0XCJcbiAgICAgICAgICBib3JkZXJDb2xvcj17Z2V0Qm9yZGVyQ29sb3IoKX1cbiAgICAgICAgICBib3JkZXJTdHlsZT1cInJvdW5kXCJcbiAgICAgICAgICBib3JkZXJMZWZ0PXtmYWxzZX1cbiAgICAgICAgICBib3JkZXJSaWdodD17ZmFsc2V9XG4gICAgICAgICAgYm9yZGVyQm90dG9tXG4gICAgICAgICAgd2lkdGg9XCIxMDAlXCJcbiAgICAgICAgICBib3JkZXJUZXh0PXtidWlsZEJvcmRlclRleHQoXG4gICAgICAgICAgICBzaG93RmFzdEljb24gPz8gZmFsc2UsXG4gICAgICAgICAgICBzaG93RmFzdEljb25IaW50LFxuICAgICAgICAgICAgZmFzdE1vZGVDb29sZG93bixcbiAgICAgICAgICApfVxuICAgICAgICA+XG4gICAgICAgICAgPFByb21wdElucHV0TW9kZUluZGljYXRvclxuICAgICAgICAgICAgbW9kZT17bW9kZX1cbiAgICAgICAgICAgIGlzTG9hZGluZz17aXNMb2FkaW5nfVxuICAgICAgICAgICAgdmlld2luZ0FnZW50TmFtZT17dmlld2luZ0FnZW50TmFtZX1cbiAgICAgICAgICAgIHZpZXdpbmdBZ2VudENvbG9yPXt2aWV3aW5nQWdlbnRDb2xvcn1cbiAgICAgICAgICAvPlxuICAgICAgICAgIDxCb3ggZmxleEdyb3c9ezF9IGZsZXhTaHJpbms9ezF9IG9uQ2xpY2s9e2hhbmRsZUlucHV0Q2xpY2t9PlxuICAgICAgICAgICAge3RleHRJbnB1dEVsZW1lbnR9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICAgIDxQcm9tcHRJbnB1dEZvb3RlclxuICAgICAgICBhcGlLZXlTdGF0dXM9e2FwaUtleVN0YXR1c31cbiAgICAgICAgZGVidWc9e2RlYnVnfVxuICAgICAgICBleGl0TWVzc2FnZT17ZXhpdE1lc3NhZ2V9XG4gICAgICAgIHZpbU1vZGU9e2lzVmltTW9kZUVuYWJsZWQoKSA/IHZpbU1vZGUgOiB1bmRlZmluZWR9XG4gICAgICAgIG1vZGU9e21vZGV9XG4gICAgICAgIGF1dG9VcGRhdGVyUmVzdWx0PXthdXRvVXBkYXRlclJlc3VsdH1cbiAgICAgICAgaXNBdXRvVXBkYXRpbmc9e2lzQXV0b1VwZGF0aW5nfVxuICAgICAgICB2ZXJib3NlPXt2ZXJib3NlfVxuICAgICAgICBvbkF1dG9VcGRhdGVyUmVzdWx0PXtvbkF1dG9VcGRhdGVyUmVzdWx0fVxuICAgICAgICBvbkNoYW5nZUlzVXBkYXRpbmc9e3NldElzQXV0b1VwZGF0aW5nfVxuICAgICAgICBzdWdnZXN0aW9ucz17c3VnZ2VzdGlvbnN9XG4gICAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbj17c2VsZWN0ZWRTdWdnZXN0aW9ufVxuICAgICAgICBtYXhDb2x1bW5XaWR0aD17bWF4Q29sdW1uV2lkdGh9XG4gICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dD17ZWZmZWN0aXZlVG9vbFBlcm1pc3Npb25Db250ZXh0fVxuICAgICAgICBoZWxwT3Blbj17aGVscE9wZW59XG4gICAgICAgIHN1cHByZXNzSGludD17aW5wdXQubGVuZ3RoID4gMH1cbiAgICAgICAgaXNMb2FkaW5nPXtpc0xvYWRpbmd9XG4gICAgICAgIHRhc2tzU2VsZWN0ZWQ9e3Rhc2tzU2VsZWN0ZWR9XG4gICAgICAgIHRlYW1zU2VsZWN0ZWQ9e3RlYW1zU2VsZWN0ZWR9XG4gICAgICAgIGJyaWRnZVNlbGVjdGVkPXticmlkZ2VTZWxlY3RlZH1cbiAgICAgICAgdG11eFNlbGVjdGVkPXt0bXV4U2VsZWN0ZWR9XG4gICAgICAgIHRlYW1tYXRlRm9vdGVySW5kZXg9e3RlYW1tYXRlRm9vdGVySW5kZXh9XG4gICAgICAgIGlkZVNlbGVjdGlvbj17aWRlU2VsZWN0aW9ufVxuICAgICAgICBtY3BDbGllbnRzPXttY3BDbGllbnRzfVxuICAgICAgICBpc1Bhc3Rpbmc9e2lzUGFzdGluZ31cbiAgICAgICAgaXNJbnB1dFdyYXBwZWQ9e2lzSW5wdXRXcmFwcGVkfVxuICAgICAgICBtZXNzYWdlcz17bWVzc2FnZXN9XG4gICAgICAgIGlzU2VhcmNoaW5nPXtpc1NlYXJjaGluZ0hpc3Rvcnl9XG4gICAgICAgIGhpc3RvcnlRdWVyeT17aGlzdG9yeVF1ZXJ5fVxuICAgICAgICBzZXRIaXN0b3J5UXVlcnk9e3NldEhpc3RvcnlRdWVyeX1cbiAgICAgICAgaGlzdG9yeUZhaWxlZE1hdGNoPXtoaXN0b3J5RmFpbGVkTWF0Y2h9XG4gICAgICAgIG9uT3BlblRhc2tzRGlhbG9nPXtcbiAgICAgICAgICBpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgPyBoYW5kbGVPcGVuVGFza3NEaWFsb2cgOiB1bmRlZmluZWRcbiAgICAgICAgfVxuICAgICAgLz5cbiAgICAgIHtpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgPyBudWxsIDogYXV0b01vZGVPcHRJbkRpYWxvZ31cbiAgICAgIHtpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgPyAoXG4gICAgICAgIC8vIHBvc2l0aW9uPWFic29sdXRlIHRha2VzIHplcm8gbGF5b3V0IGhlaWdodCBzbyB0aGUgc3Bpbm5lclxuICAgICAgICAvLyBkb2Vzbid0IHNoaWZ0IHdoZW4gYSBub3RpZmljYXRpb24gYXBwZWFycy9kaXNhcHBlYXJzLiBZb2dhXG4gICAgICAgIC8vIGFuY2hvcnMgYWJzb2x1dGUgY2hpbGRyZW4gYXQgdGhlIHBhcmVudCdzIGNvbnRlbnQtYm94IG9yaWdpbjtcbiAgICAgICAgLy8gbWFyZ2luVG9wPS0xIHB1bGxzIGl0IGludG8gdGhlIG1hcmdpblRvcD0xIGdhcCByb3cgYWJvdmUgdGhlXG4gICAgICAgIC8vIHByb21wdCBib3JkZXIuIEluIGJyaWVmIG1vZGUgdGhlcmUgaXMgbm8gc3VjaCBnYXAgKGJyaWVmT3duc0dhcFxuICAgICAgICAvLyBzdHJpcHMgb3VyIG1hcmdpblRvcCkgYW5kIEJyaWVmU3Bpbm5lciBzaXRzIGZsdXNoIGFnYWluc3QgdGhlXG4gICAgICAgIC8vIGJvcmRlciDigJQgbWFyZ2luVG9wPS0yIHNraXBzIG92ZXIgdGhlIHNwaW5uZXIgY29udGVudCBpbnRvXG4gICAgICAgIC8vIEJyaWVmU3Bpbm5lcidzIG93biBtYXJnaW5Ub3A9MSBibGFuayByb3cuIGhlaWdodD0xICtcbiAgICAgICAgLy8gb3ZlcmZsb3c9aGlkZGVuIGNsaXBzIG11bHRpLWxpbmUgbm90aWZpY2F0aW9ucyB0byBhIHNpbmdsZSByb3cuXG4gICAgICAgIC8vIGZsZXgtZW5kIGFuY2hvcnMgdGhlIGJvdHRvbSBsaW5lIHNvIHRoZSB2aXNpYmxlIHJvdyBpcyBhbHdheXNcbiAgICAgICAgLy8gdGhlIG1vc3QgcmVjZW50LiBTdXBwcmVzc2VkIHdoaWxlIHRoZSBzbGFzaCBvdmVybGF5IG9yXG4gICAgICAgIC8vIGF1dG8tbW9kZSBvcHQtaW4gZGlhbG9nIGlzIHVwIGJ5IGhlaWdodD0wIChOT1QgdW5tb3VudCkg4oCUIHRoaXNcbiAgICAgICAgLy8gQm94IHJlbmRlcnMgbGF0ZXIgaW4gdHJlZSBvcmRlciBzbyBpdCB3b3VsZCBwYWludCBvdmVyIHRoZWlyXG4gICAgICAgIC8vIGJvdHRvbSByb3cuIEtlZXBpbmcgTm90aWZpY2F0aW9ucyBtb3VudGVkIHByZXZlbnRzIEF1dG9VcGRhdGVyJ3NcbiAgICAgICAgLy8gaW5pdGlhbC1jaGVjayBlZmZlY3QgZnJvbSByZS1maXJpbmcgb24gZXZlcnkgc2xhc2gtY29tcGxldGlvblxuICAgICAgICAvLyB0b2dnbGUgKFBSIzIyNDEzKS5cbiAgICAgICAgPEJveFxuICAgICAgICAgIHBvc2l0aW9uPVwiYWJzb2x1dGVcIlxuICAgICAgICAgIG1hcmdpblRvcD17YnJpZWZPd25zR2FwID8gLTIgOiAtMX1cbiAgICAgICAgICBoZWlnaHQ9e3N1Z2dlc3Rpb25zLmxlbmd0aCA9PT0gMCAmJiAhc2hvd0F1dG9Nb2RlT3B0SW4gPyAxIDogMH1cbiAgICAgICAgICB3aWR0aD1cIjEwMCVcIlxuICAgICAgICAgIHBhZGRpbmdMZWZ0PXsyfVxuICAgICAgICAgIHBhZGRpbmdSaWdodD17MX1cbiAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICBqdXN0aWZ5Q29udGVudD1cImZsZXgtZW5kXCJcbiAgICAgICAgICBvdmVyZmxvdz1cImhpZGRlblwiXG4gICAgICAgID5cbiAgICAgICAgICA8Tm90aWZpY2F0aW9uc1xuICAgICAgICAgICAgYXBpS2V5U3RhdHVzPXthcGlLZXlTdGF0dXN9XG4gICAgICAgICAgICBhdXRvVXBkYXRlclJlc3VsdD17YXV0b1VwZGF0ZXJSZXN1bHR9XG4gICAgICAgICAgICBkZWJ1Zz17ZGVidWd9XG4gICAgICAgICAgICBpc0F1dG9VcGRhdGluZz17aXNBdXRvVXBkYXRpbmd9XG4gICAgICAgICAgICB2ZXJib3NlPXt2ZXJib3NlfVxuICAgICAgICAgICAgbWVzc2FnZXM9e21lc3NhZ2VzfVxuICAgICAgICAgICAgb25BdXRvVXBkYXRlclJlc3VsdD17b25BdXRvVXBkYXRlclJlc3VsdH1cbiAgICAgICAgICAgIG9uQ2hhbmdlSXNVcGRhdGluZz17c2V0SXNBdXRvVXBkYXRpbmd9XG4gICAgICAgICAgICBpZGVTZWxlY3Rpb249e2lkZVNlbGVjdGlvbn1cbiAgICAgICAgICAgIG1jcENsaWVudHM9e21jcENsaWVudHN9XG4gICAgICAgICAgICBpc0lucHV0V3JhcHBlZD17aXNJbnB1dFdyYXBwZWR9XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApIDogbnVsbH1cbiAgICA8L0JveD5cbiAgKVxufVxuXG4vKipcbiAqIENvbXB1dGUgdGhlIGluaXRpYWwgcGFzdGUgSUQgYnkgZmluZGluZyB0aGUgbWF4IElEIHVzZWQgaW4gZXhpc3RpbmcgbWVzc2FnZXMuXG4gKiBUaGlzIGhhbmRsZXMgLS1jb250aW51ZS8tLXJlc3VtZSBzY2VuYXJpb3Mgd2hlcmUgd2UgbmVlZCB0byBhdm9pZCBJRCBjb2xsaXNpb25zLlxuICovXG5mdW5jdGlvbiBnZXRJbml0aWFsUGFzdGVJZChtZXNzYWdlczogTWVzc2FnZVtdKTogbnVtYmVyIHtcbiAgbGV0IG1heElkID0gMFxuICBmb3IgKGNvbnN0IG1lc3NhZ2Ugb2YgbWVzc2FnZXMpIHtcbiAgICBpZiAobWVzc2FnZS50eXBlID09PSAndXNlcicpIHtcbiAgICAgIC8vIENoZWNrIGltYWdlIHBhc3RlIElEc1xuICAgICAgaWYgKG1lc3NhZ2UuaW1hZ2VQYXN0ZUlkcykge1xuICAgICAgICBmb3IgKGNvbnN0IGlkIG9mIG1lc3NhZ2UuaW1hZ2VQYXN0ZUlkcykge1xuICAgICAgICAgIGlmIChpZCA+IG1heElkKSBtYXhJZCA9IGlkXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIENoZWNrIHRleHQgcGFzdGUgcmVmZXJlbmNlcyBpbiBtZXNzYWdlIGNvbnRlbnRcbiAgICAgIGlmIChBcnJheS5pc0FycmF5KG1lc3NhZ2UubWVzc2FnZS5jb250ZW50KSkge1xuICAgICAgICBmb3IgKGNvbnN0IGJsb2NrIG9mIG1lc3NhZ2UubWVzc2FnZS5jb250ZW50KSB7XG4gICAgICAgICAgaWYgKGJsb2NrLnR5cGUgPT09ICd0ZXh0Jykge1xuICAgICAgICAgICAgY29uc3QgcmVmcyA9IHBhcnNlUmVmZXJlbmNlcyhibG9jay50ZXh0KVxuICAgICAgICAgICAgZm9yIChjb25zdCByZWYgb2YgcmVmcykge1xuICAgICAgICAgICAgICBpZiAocmVmLmlkID4gbWF4SWQpIG1heElkID0gcmVmLmlkXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBtYXhJZCArIDFcbn1cblxuZnVuY3Rpb24gYnVpbGRCb3JkZXJUZXh0KFxuICBzaG93RmFzdEljb246IGJvb2xlYW4sXG4gIHNob3dGYXN0SWNvbkhpbnQ6IGJvb2xlYW4sXG4gIGZhc3RNb2RlQ29vbGRvd246IGJvb2xlYW4sXG4pOiBCb3JkZXJUZXh0T3B0aW9ucyB8IHVuZGVmaW5lZCB7XG4gIGlmICghc2hvd0Zhc3RJY29uKSByZXR1cm4gdW5kZWZpbmVkXG4gIGNvbnN0IGZhc3RTZWcgPSBzaG93RmFzdEljb25IaW50XG4gICAgPyBgJHtnZXRGYXN0SWNvblN0cmluZyh0cnVlLCBmYXN0TW9kZUNvb2xkb3duKX0gJHtjaGFsay5kaW0oJy9mYXN0Jyl9YFxuICAgIDogZ2V0RmFzdEljb25TdHJpbmcodHJ1ZSwgZmFzdE1vZGVDb29sZG93bilcbiAgcmV0dXJuIHtcbiAgICBjb250ZW50OiBgICR7ZmFzdFNlZ30gYCxcbiAgICBwb3NpdGlvbjogJ3RvcCcsXG4gICAgYWxpZ246ICdlbmQnLFxuICAgIG9mZnNldDogMCxcbiAgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBSZWFjdC5tZW1vKFByb21wdElucHV0KVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxPQUFPLFFBQVEsWUFBWTtBQUNwQyxPQUFPQyxLQUFLLE1BQU0sT0FBTztBQUN6QixPQUFPLEtBQUtDLElBQUksTUFBTSxNQUFNO0FBQzVCLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FDRUMsV0FBVyxFQUNYQyxTQUFTLEVBQ1RDLE9BQU8sRUFDUEMsTUFBTSxFQUNOQyxRQUFRLEVBQ1JDLG9CQUFvQixRQUNmLE9BQU87QUFDZCxTQUFTQyxnQkFBZ0IsUUFBUSw4QkFBOEI7QUFDL0QsU0FBU0MsZUFBZSxRQUFRLDhCQUE4QjtBQUM5RCxTQUNFLEtBQUtDLGNBQWMsRUFDbkJDLGlCQUFpQixRQUNaLGdDQUFnQztBQUN2QyxTQUNFLEtBQUtDLDBEQUEwRCxFQUMvREMsUUFBUSxRQUNILGlDQUFpQztBQUN4QyxTQUNFLEtBQUtDLFFBQVEsRUFDYkMsV0FBVyxFQUNYQyxnQkFBZ0IsRUFDaEJDLGNBQWMsUUFDVCx1QkFBdUI7QUFDOUIsY0FBY0MsVUFBVSxRQUFRLDRCQUE0QjtBQUM1RCxTQUFTQyxNQUFNLFFBQVEsa0JBQWtCO0FBQ3pDLFNBQ0VDLHVCQUF1QixFQUN2QkMsY0FBYyxRQUNULGtDQUFrQztBQUN6QyxPQUFPQyxTQUFTLE1BQU0sWUFBWTtBQUNsQyxTQUFTQyx3QkFBd0IsUUFBUSxnQ0FBZ0M7QUFDekUsU0FDRUMseUJBQXlCLEVBQ3pCQyxvQkFBb0IsUUFDZixxQ0FBcUM7QUFDNUMsU0FBU0MsY0FBYyxRQUFRLDZCQUE2QjtBQUM1RCxTQUFTQyxvQkFBb0IsUUFBUSw2Q0FBNkM7QUFDbEYsU0FBU0MsZ0NBQWdDLFFBQVEsK0NBQStDO0FBQ2hHLFNBQVMsS0FBS0MsT0FBTyxFQUFFQyxVQUFVLFFBQVEsbUJBQW1CO0FBQzVELFNBQVNDLHVCQUF1QixRQUFRLGlDQUFpQztBQUN6RSxTQUFTQyx5QkFBeUIsUUFBUSx1Q0FBdUM7QUFDakYsU0FDRUMsY0FBYyxFQUNkQyxtQkFBbUIsRUFDbkJDLHdCQUF3QixFQUN4QkMsZUFBZSxRQUNWLGtCQUFrQjtBQUN6QixjQUFjQyxrQkFBa0IsUUFBUSxzQ0FBc0M7QUFDOUUsU0FDRSxLQUFLQyxXQUFXLEVBQ2hCQyxrQkFBa0IsUUFDYixtQ0FBbUM7QUFDMUMsU0FBU0MsY0FBYyxRQUFRLCtCQUErQjtBQUM5RCxTQUFTQyxnQkFBZ0IsUUFBUSxpQ0FBaUM7QUFDbEUsY0FBY0MsWUFBWSxRQUFRLGdDQUFnQztBQUNsRSxTQUFTQyxjQUFjLFFBQVEsK0JBQStCO0FBQzlELFNBQVNDLGdCQUFnQixRQUFRLGlDQUFpQztBQUNsRSxTQUFTQyxtQkFBbUIsUUFBUSxvQ0FBb0M7QUFDeEUsU0FBU0MsZUFBZSxRQUFRLGdDQUFnQztBQUNoRSxTQUFTQyxZQUFZLFFBQVEsNkJBQTZCO0FBQzFELGNBQWNDLGlCQUFpQixRQUFRLDRCQUE0QjtBQUNuRSxTQUFTQyxXQUFXLFFBQVEsMEJBQTBCO0FBQ3RELFNBQVNDLEdBQUcsRUFBRSxLQUFLQyxVQUFVLEVBQUUsS0FBS0MsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxjQUFjO0FBQzdFLFNBQVNDLDRCQUE0QixRQUFRLHdDQUF3QztBQUNyRixTQUFTQyxrQkFBa0IsUUFBUSxxQ0FBcUM7QUFDeEUsU0FDRUMsYUFBYSxFQUNiQyxjQUFjLFFBQ1Qsb0NBQW9DO0FBQzNDLGNBQWNDLG1CQUFtQixRQUFRLDZCQUE2QjtBQUN0RSxTQUNFQyxxQkFBcUIsRUFDckJDLHVCQUF1QixRQUNsQixxREFBcUQ7QUFDNUQsU0FDRSxLQUFLQyxzQkFBc0IsRUFDM0JDLGdCQUFnQixRQUNYLGdEQUFnRDtBQUN2RCxTQUNFQyxzQkFBc0IsRUFDdEJDLHFCQUFxQixRQUNoQiwwQkFBMEI7QUFDakMsU0FDRUMsaUJBQWlCLEVBQ2pCQyxnQkFBZ0IsRUFDaEJDLGtCQUFrQixRQUNiLG9DQUFvQztBQUMzQyxjQUFjQyxxQkFBcUIsUUFBUSxlQUFlO0FBQzFELFNBQVNDLHlCQUF5QixRQUFRLDREQUE0RDtBQUN0RyxjQUFjQywwQkFBMEIsUUFBUSw0Q0FBNEM7QUFDNUYsU0FDRUMsZ0JBQWdCLEVBQ2hCLEtBQUtDLG1CQUFtQixRQUNuQiw4Q0FBOEM7QUFDckQsU0FBU0MsZ0JBQWdCLFFBQVEsc0JBQXNCO0FBQ3ZELFNBQ0VDLDBCQUEwQixFQUMxQkMsWUFBWSxFQUNaLEtBQUtDLGNBQWMsUUFDZCw0Q0FBNEM7QUFDbkQsY0FBY0MsZUFBZSxRQUFRLHdDQUF3QztBQUM3RSxjQUFjQyxPQUFPLFFBQVEsd0JBQXdCO0FBQ3JELGNBQWNDLGNBQWMsUUFBUSw0QkFBNEI7QUFDaEUsY0FDRUMsa0JBQWtCLEVBQ2xCQyxlQUFlLEVBQ2ZDLE9BQU8sUUFDRiwrQkFBK0I7QUFDdEMsU0FBU0Msb0JBQW9CLFFBQVEsbUNBQW1DO0FBQ3hFLFNBQVNDLEtBQUssUUFBUSxzQkFBc0I7QUFDNUMsY0FBY0MsaUJBQWlCLFFBQVEsNEJBQTRCO0FBQ25FLFNBQVNDLE1BQU0sUUFBUSx1QkFBdUI7QUFDOUMsU0FDRUMsZUFBZSxFQUNmLEtBQUtDLGFBQWEsRUFDbEJDLGdCQUFnQixRQUNYLHVCQUF1QjtBQUM5QixTQUFTQyxlQUFlLFFBQVEsc0JBQXNCO0FBQ3RELFNBQ0VDLHdCQUF3QixFQUN4QkMsdUJBQXVCLFFBQ2xCLG9DQUFvQztBQUMzQyxjQUFjQyxXQUFXLFFBQVEsdUJBQXVCO0FBQ3hELFNBQVNDLEdBQUcsUUFBUSxvQkFBb0I7QUFDeEMsU0FBU0MsWUFBWSxRQUFRLHVCQUF1QjtBQUNwRCxTQUFTQyxvQkFBb0IsUUFBUSwyQkFBMkI7QUFDaEUsU0FDRUMsNEJBQTRCLEVBQzVCQyxtQkFBbUIsRUFDbkJDLGtCQUFrQixFQUNsQkMsaUJBQWlCLEVBQ2pCQywwQkFBMEIsUUFDckIseUJBQXlCO0FBQ2hDLFNBQVNDLHNCQUFzQixRQUFRLDJCQUEyQjtBQUNsRSxjQUFjQyxrQkFBa0IsUUFBUSxtQ0FBbUM7QUFDM0UsU0FDRUMscUJBQXFCLEVBQ3JCQyxlQUFlLFFBQ1YsMkJBQTJCO0FBQ2xDLGNBQWNDLGVBQWUsUUFBUSw2QkFBNkI7QUFDbEUsU0FBU0MsY0FBYyxFQUFFQyxVQUFVLFFBQVEsMkJBQTJCO0FBQ3RFLFNBQ0VDLGlCQUFpQixFQUNqQkMsMEJBQTBCLFFBQ3JCLGtDQUFrQztBQUN6QyxTQUFTQyxRQUFRLFFBQVEsb0JBQW9CO0FBQzdDLFNBQ0VDLG9CQUFvQixFQUNwQkMsa0JBQWtCLFFBQ2IsNEJBQTRCO0FBQ25DLFNBQVNDLGlCQUFpQixRQUFRLDBDQUEwQztBQUM1RSxTQUNFQyxtQkFBbUIsRUFDbkJDLHFCQUFxQixRQUNoQixrREFBa0Q7QUFDekQsU0FBU0Msd0JBQXdCLFFBQVEsNENBQTRDO0FBQ3JGLFNBQVNDLFdBQVcsUUFBUSx5QkFBeUI7QUFDckQsY0FBY0MsdUJBQXVCLFFBQVEsa0RBQWtEO0FBQy9GLFNBQVNDLGtCQUFrQixRQUFRLDZCQUE2QjtBQUNoRSxTQUFTQyxnQkFBZ0IsUUFBUSxrQ0FBa0M7QUFDbkUsU0FBU0MsdUJBQXVCLFFBQVEsNkJBQTZCO0FBQ3JFLFNBQVNDLHlCQUF5QixRQUFRLCtDQUErQztBQUN6RixTQUNFQyx5QkFBeUIsRUFDekJDLHVCQUF1QixFQUN2QkMsaUJBQWlCLEVBQ2pCQyxzQkFBc0IsUUFDakIsb0RBQW9EO0FBQzNELFNBQVNDLGtCQUFrQixRQUFRLHdDQUF3QztBQUMzRSxTQUFTQyxnQkFBZ0IsUUFBUSxrQ0FBa0M7QUFDbkUsY0FBY0MsV0FBVyxRQUFRLDhCQUE4QjtBQUMvRCxTQUFTQyxnQkFBZ0IsUUFBUSx5QkFBeUI7QUFDMUQsU0FBU0MsbUJBQW1CLFFBQVEsZ0NBQWdDO0FBQ3BFLFNBQVNDLGNBQWMsUUFBUSxnQ0FBZ0M7QUFDL0QsY0FBY0MsYUFBYSxRQUFRLGlDQUFpQztBQUNwRSxjQUFjQyxLQUFLLFFBQVEsc0JBQXNCO0FBQ2pELFNBQ0VDLDRCQUE0QixFQUM1QkMsZUFBZSxFQUNmQyxtQkFBbUIsUUFDZCx5QkFBeUI7QUFDaEMsU0FBU0Msd0JBQXdCLFFBQVEsNEJBQTRCO0FBQ3JFLFNBQ0VDLDZCQUE2QixFQUM3QkMsK0JBQStCLFFBQzFCLGtDQUFrQztBQUN6QyxTQUFTQyxtQkFBbUIsUUFBUSwyQkFBMkI7QUFDL0QsU0FBU0MsWUFBWSxRQUFRLG9CQUFvQjtBQUNqRCxTQUFTQyx3QkFBd0IsUUFBUSxnQ0FBZ0M7QUFDekUsU0FDRUMsb0JBQW9CLEVBQ3BCQyx1QkFBdUIsUUFDbEIsOEJBQThCO0FBQ3JDLFNBQVNDLHlCQUF5QixRQUFRLHVCQUF1QjtBQUNqRSxTQUFTQyxpQkFBaUIsUUFBUSxnQkFBZ0I7QUFDbEQsU0FBU0Msa0JBQWtCLFFBQVEsMEJBQTBCO0FBQzdELFNBQVNDLG1CQUFtQixRQUFRLDJCQUEyQjtBQUMvRCxTQUFTQyxXQUFXLFFBQVEsbUJBQW1CO0FBQy9DLFNBQVNDLGVBQWUsUUFBUSx1QkFBdUI7QUFDdkQsT0FBT0MsU0FBUyxNQUFNLGlCQUFpQjtBQUN2QyxTQUFTQyxjQUFjLFFBQVEsc0JBQXNCO0FBQ3JELFNBQVNDLHFCQUFxQixRQUFRLG1DQUFtQztBQUN6RSxTQUFTQyxxQkFBcUIsUUFBUSw2QkFBNkI7QUFDbkUsU0FBU0MsV0FBVyxRQUFRLHlCQUF5QjtBQUNyRCxPQUFPQyxZQUFZLE1BQU0sb0JBQW9CO0FBQzdDLFNBQVNDLGdCQUFnQixFQUFFQyxpQkFBaUIsUUFBUSxpQkFBaUI7QUFDckUsU0FDRUMsK0JBQStCLEVBQy9CQyxhQUFhLFFBQ1Isb0JBQW9CO0FBQzNCLE9BQU9DLGlCQUFpQixNQUFNLHdCQUF3QjtBQUN0RCxjQUFjQyxjQUFjLFFBQVEsbUNBQW1DO0FBQ3ZFLFNBQVNDLHdCQUF3QixRQUFRLCtCQUErQjtBQUN4RSxTQUFTQyx5QkFBeUIsUUFBUSxnQ0FBZ0M7QUFDMUUsU0FBU0Msc0JBQXNCLFFBQVEsNkJBQTZCO0FBQ3BFLFNBQVNDLHFCQUFxQixRQUFRLDRCQUE0QjtBQUNsRSxTQUFTQyx5QkFBeUIsUUFBUSxnQ0FBZ0M7QUFDMUUsU0FBU0MsbUJBQW1CLFFBQVEsMEJBQTBCO0FBQzlELFNBQVNDLGNBQWMsUUFBUSxxQkFBcUI7QUFDcEQsU0FBU0MsbUJBQW1CLEVBQUVDLGdCQUFnQixRQUFRLFlBQVk7QUFFbEUsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLEtBQUssRUFBRSxPQUFPO0VBQ2RDLFlBQVksRUFBRXZJLFlBQVksR0FBRyxTQUFTO0VBQ3RDd0kscUJBQXFCLEVBQUU3RyxxQkFBcUI7RUFDNUM4Ryx3QkFBd0IsRUFBRSxDQUFDQyxHQUFHLEVBQUUvRyxxQkFBcUIsRUFBRSxHQUFHLElBQUk7RUFDOURnSCxZQUFZLEVBQUVoSixrQkFBa0I7RUFDaENpSixRQUFRLEVBQUV6SixPQUFPLEVBQUU7RUFDbkIwSixNQUFNLEVBQUV6RyxlQUFlLEVBQUU7RUFDekIwRyxTQUFTLEVBQUUsT0FBTztFQUNsQkMsT0FBTyxFQUFFLE9BQU87RUFDaEJDLFFBQVEsRUFBRTNHLE9BQU8sRUFBRTtFQUNuQjRHLG1CQUFtQixFQUFFLENBQUNDLE1BQU0sRUFBRXRHLGlCQUFpQixFQUFFLEdBQUcsSUFBSTtFQUN4RHVHLGlCQUFpQixFQUFFdkcsaUJBQWlCLEdBQUcsSUFBSTtFQUMzQ3dHLEtBQUssRUFBRSxNQUFNO0VBQ2JDLGFBQWEsRUFBRSxDQUFDQyxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSTtFQUN0Q0MsSUFBSSxFQUFFL0csZUFBZTtFQUNyQmdILFlBQVksRUFBRSxDQUFDRCxJQUFJLEVBQUUvRyxlQUFlLEVBQUUsR0FBRyxJQUFJO0VBQzdDaUgsYUFBYSxFQUNUO0lBQ0VDLElBQUksRUFBRSxNQUFNO0lBQ1pDLFlBQVksRUFBRSxNQUFNO0lBQ3BCQyxjQUFjLEVBQUVDLE1BQU0sQ0FBQyxNQUFNLEVBQUU5RyxhQUFhLENBQUM7RUFDL0MsQ0FBQyxHQUNELFNBQVM7RUFDYitHLGdCQUFnQixFQUFFLENBQ2hCUixLQUFLLEVBQ0Q7SUFDRUksSUFBSSxFQUFFLE1BQU07SUFDWkMsWUFBWSxFQUFFLE1BQU07SUFDcEJDLGNBQWMsRUFBRUMsTUFBTSxDQUFDLE1BQU0sRUFBRTlHLGFBQWEsQ0FBQztFQUMvQyxDQUFDLEdBQ0QsU0FBUyxFQUNiLEdBQUcsSUFBSTtFQUNUZ0gsV0FBVyxFQUFFLE1BQU07RUFDbkJDLHFCQUFxQixFQUFFLEdBQUcsR0FBRyxJQUFJO0VBQ2pDO0VBQ0FDLHFCQUFxQixDQUFDLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDbENDLFVBQVUsRUFBRWpKLG1CQUFtQixFQUFFO0VBQ2pDMkksY0FBYyxFQUFFQyxNQUFNLENBQUMsTUFBTSxFQUFFOUcsYUFBYSxDQUFDO0VBQzdDb0gsaUJBQWlCLEVBQUU1TSxLQUFLLENBQUM2TSxRQUFRLENBQy9CN00sS0FBSyxDQUFDOE0sY0FBYyxDQUFDUixNQUFNLENBQUMsTUFBTSxFQUFFOUcsYUFBYSxDQUFDLENBQUMsQ0FDcEQ7RUFDRHVILE9BQU8sRUFBRTdILE9BQU87RUFDaEI4SCxVQUFVLEVBQUUsQ0FBQ2hCLElBQUksRUFBRTlHLE9BQU8sRUFBRSxHQUFHLElBQUk7RUFDbkMrSCxnQkFBZ0IsRUFBRSxNQUFNLEdBQUcsT0FBTztFQUNsQ0MsbUJBQW1CLEVBQUUsQ0FBQ0MsSUFBSSxFQUFFLE1BQU0sR0FBRyxPQUFPLEVBQUUsR0FBRyxJQUFJO0VBQ3JEQyxNQUFNLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDbEJDLGlCQUFpQixFQUFFLENBQ2pCNUIsUUFBUSxFQUFFM0csT0FBTyxFQUFFLEVBQ25Cd0ksV0FBVyxFQUFFeEksT0FBTyxFQUFFLEVBQ3RCeUksZUFBZSxFQUFFQyxlQUFlLEVBQ2hDQyxhQUFhLEVBQUUsTUFBTSxFQUNyQixHQUFHbEcsdUJBQXVCO0VBQzVCbUcsUUFBUSxFQUFFLENBQ1I3QixLQUFLLEVBQUUsTUFBTSxFQUNiOEIsT0FBTyxFQUFFcEgsa0JBQWtCLEVBQzNCcUgsaUJBSUMsQ0FKaUIsRUFBRTtJQUNsQkMsS0FBSyxFQUFFaEssc0JBQXNCO0lBQzdCaUssNkJBQTZCLEVBQUUsTUFBTTtJQUNyQ0MsV0FBVyxFQUFFLENBQUNDLENBQUMsRUFBRSxDQUFDQyxJQUFJLEVBQUVwTixRQUFRLEVBQUUsR0FBR0EsUUFBUSxFQUFFLEdBQUcsSUFBSTtFQUN4RCxDQUFDLEVBQ0RxTixPQUFzQyxDQUE5QixFQUFFO0lBQUVDLGNBQWMsQ0FBQyxFQUFFLE9BQU87RUFBQyxDQUFDLEVBQ3RDLEdBQUdDLE9BQU8sQ0FBQyxJQUFJLENBQUM7RUFDbEJDLGFBQWEsQ0FBQyxFQUFFLENBQ2R4QyxLQUFLLEVBQUUsTUFBTSxFQUNieUMsSUFBSSxFQUFFaEssMEJBQTBCLEdBQUdFLG1CQUFtQixFQUN0RG1KLE9BQU8sRUFBRXBILGtCQUFrQixFQUMzQixHQUFHNkgsT0FBTyxDQUFDLElBQUksQ0FBQztFQUNsQkcsa0JBQWtCLEVBQUUsT0FBTztFQUMzQkMscUJBQXFCLEVBQUUsQ0FBQ0MsV0FBVyxFQUFFLE9BQU8sRUFBRSxHQUFHLElBQUk7RUFDckRDLHFCQUFxQixDQUFDLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDbENDLHFCQUFxQixDQUFDLEVBQUUsT0FBTztFQUMvQkMsUUFBUSxFQUFFLE9BQU87RUFDakJDLFdBQVcsRUFBRTdPLEtBQUssQ0FBQzZNLFFBQVEsQ0FBQzdNLEtBQUssQ0FBQzhNLGNBQWMsQ0FBQyxPQUFPLENBQUMsQ0FBQztFQUMxRGdDLG9CQUFvQixDQUFDLEVBQUUsT0FBTztFQUM5QkMsdUJBQXVCLENBQUMsRUFBRSxPQUFPO0VBQ2pDQyxhQUFhLENBQUMsRUFBRWhQLEtBQUssQ0FBQ2lQLGdCQUFnQixDQUFDO0lBQ3JDQyxNQUFNLEVBQUUsQ0FBQy9DLElBQUksRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0lBQzlCZ0Qsa0JBQWtCLEVBQUUsQ0FBQ3BELEtBQUssRUFBRSxNQUFNLEVBQUVxRCxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSTtJQUMzRGhELFlBQVksRUFBRSxNQUFNO0VBQ3RCLENBQUMsR0FBRyxJQUFJLENBQUM7RUFDVGlELGlCQUFpQixDQUFDLEVBQUU7SUFBRUMsS0FBSyxFQUFFLE1BQU07SUFBRUMsR0FBRyxFQUFFLE1BQU07RUFBQyxDQUFDLEdBQUcsSUFBSTtBQUMzRCxDQUFDOztBQUVEO0FBQ0EsTUFBTUMsbUJBQW1CLEdBQUcsQ0FBQztBQUM3QixNQUFNQyx3QkFBd0IsR0FBRyxDQUFDO0FBRWxDLFNBQVNDLFdBQVdBLENBQUM7RUFDbkIzRSxLQUFLO0VBQ0xDLFlBQVk7RUFDWkMscUJBQXFCO0VBQ3JCQyx3QkFBd0I7RUFDeEJFLFlBQVk7RUFDWkMsUUFBUTtFQUNSQyxNQUFNO0VBQ05DLFNBQVM7RUFDVEMsT0FBTztFQUNQQyxRQUFRO0VBQ1JDLG1CQUFtQjtFQUNuQkUsaUJBQWlCO0VBQ2pCQyxLQUFLO0VBQ0xDLGFBQWE7RUFDYkUsSUFBSTtFQUNKQyxZQUFZO0VBQ1pDLGFBQWE7RUFDYkssZ0JBQWdCO0VBQ2hCQyxXQUFXO0VBQ1hDLHFCQUFxQjtFQUNyQkMscUJBQXFCO0VBQ3JCQyxVQUFVO0VBQ1ZOLGNBQWM7RUFDZE8saUJBQWlCO0VBQ2pCRyxPQUFPO0VBQ1BDLFVBQVU7RUFDVkMsZ0JBQWdCO0VBQ2hCQyxtQkFBbUI7RUFDbkJFLE1BQU07RUFDTkMsaUJBQWlCO0VBQ2pCSyxRQUFRLEVBQUVpQyxZQUFZO0VBQ3RCdEIsYUFBYTtFQUNiRSxrQkFBa0I7RUFDbEJDLHFCQUFxQjtFQUNyQkUscUJBQXFCO0VBQ3JCQyxxQkFBcUI7RUFDckJDLFFBQVE7RUFDUkMsV0FBVztFQUNYQyxvQkFBb0I7RUFDcEJDLHVCQUF1QixHQUFHLEtBQUs7RUFDL0JDLGFBQWE7RUFDYks7QUFDSyxDQUFOLEVBQUV2RSxLQUFLLENBQUMsRUFBRTlLLEtBQUssQ0FBQzRQLFNBQVMsQ0FBQztFQUN6QixNQUFNbkMsYUFBYSxHQUFHOUssZ0JBQWdCLENBQUMsQ0FBQztFQUN4QztFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsTUFBTWtOLG9CQUFvQixHQUN4Qi9OLHVCQUF1QixDQUFDLENBQUMsSUFBSWlOLHVCQUF1QjtFQUN0RCxNQUFNLENBQUNlLGNBQWMsRUFBRUMsaUJBQWlCLENBQUMsR0FBRzFQLFFBQVEsQ0FBQyxLQUFLLENBQUM7RUFDM0QsTUFBTSxDQUFDMlAsV0FBVyxFQUFFQyxjQUFjLENBQUMsR0FBRzVQLFFBQVEsQ0FBQztJQUM3QzhNLElBQUksRUFBRSxPQUFPO0lBQ2IrQyxHQUFHLENBQUMsRUFBRSxNQUFNO0VBQ2QsQ0FBQyxDQUFDLENBQUM7SUFBRS9DLElBQUksRUFBRTtFQUFNLENBQUMsQ0FBQztFQUNuQixNQUFNLENBQUNmLFlBQVksRUFBRStELGVBQWUsQ0FBQyxHQUFHOVAsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDd0wsS0FBSyxDQUFDdUUsTUFBTSxDQUFDO0VBQ3RFO0VBQ0E7RUFDQSxNQUFNQyxvQkFBb0IsR0FBR3JRLEtBQUssQ0FBQ0ksTUFBTSxDQUFDeUwsS0FBSyxDQUFDO0VBQ2hELElBQUlBLEtBQUssS0FBS3dFLG9CQUFvQixDQUFDQyxPQUFPLEVBQUU7SUFDMUM7SUFDQUgsZUFBZSxDQUFDdEUsS0FBSyxDQUFDdUUsTUFBTSxDQUFDO0lBQzdCQyxvQkFBb0IsQ0FBQ0MsT0FBTyxHQUFHekUsS0FBSztFQUN0QztFQUNBO0VBQ0EsTUFBTTBFLGdCQUFnQixHQUFHdlEsS0FBSyxDQUFDQyxXQUFXLENBQ3hDLENBQUM4TCxLQUFLLEVBQUUsTUFBTSxLQUFLO0lBQ2pCc0Usb0JBQW9CLENBQUNDLE9BQU8sR0FBR3ZFLEtBQUs7SUFDcENELGFBQWEsQ0FBQ0MsS0FBSyxDQUFDO0VBQ3RCLENBQUMsRUFDRCxDQUFDRCxhQUFhLENBQ2hCLENBQUM7RUFDRDtFQUNBO0VBQ0EsSUFBSWtELGFBQWEsRUFBRTtJQUNqQkEsYUFBYSxDQUFDc0IsT0FBTyxHQUFHO01BQ3RCbEUsWUFBWTtNQUNaOEMsTUFBTSxFQUFFQSxDQUFDL0MsSUFBSSxFQUFFLE1BQU0sS0FBSztRQUN4QixNQUFNcUUsVUFBVSxHQUNkcEUsWUFBWSxLQUFLUCxLQUFLLENBQUN1RSxNQUFNLElBQzdCdkUsS0FBSyxDQUFDdUUsTUFBTSxHQUFHLENBQUMsSUFDaEIsQ0FBQyxLQUFLLENBQUNLLElBQUksQ0FBQzVFLEtBQUssQ0FBQztRQUNwQixNQUFNNkUsVUFBVSxHQUFHRixVQUFVLEdBQUcsR0FBRyxHQUFHckUsSUFBSSxHQUFHQSxJQUFJO1FBQ2pELE1BQU13RSxRQUFRLEdBQ1o5RSxLQUFLLENBQUMrRSxLQUFLLENBQUMsQ0FBQyxFQUFFeEUsWUFBWSxDQUFDLEdBQUdzRSxVQUFVLEdBQUc3RSxLQUFLLENBQUMrRSxLQUFLLENBQUN4RSxZQUFZLENBQUM7UUFDdkVpRSxvQkFBb0IsQ0FBQ0MsT0FBTyxHQUFHSyxRQUFRO1FBQ3ZDN0UsYUFBYSxDQUFDNkUsUUFBUSxDQUFDO1FBQ3ZCUixlQUFlLENBQUMvRCxZQUFZLEdBQUdzRSxVQUFVLENBQUNOLE1BQU0sQ0FBQztNQUNuRCxDQUFDO01BQ0RqQixrQkFBa0IsRUFBRUEsQ0FBQ3BELEtBQUssRUFBRSxNQUFNLEVBQUVxRCxNQUFNLEVBQUUsTUFBTSxLQUFLO1FBQ3JEaUIsb0JBQW9CLENBQUNDLE9BQU8sR0FBR3ZFLEtBQUs7UUFDcENELGFBQWEsQ0FBQ0MsS0FBSyxDQUFDO1FBQ3BCb0UsZUFBZSxDQUFDZixNQUFNLENBQUM7TUFDekI7SUFDRixDQUFDO0VBQ0g7RUFDQSxNQUFNeUIsS0FBSyxHQUFHOVAsZ0JBQWdCLENBQUMsQ0FBQztFQUNoQyxNQUFNZ04sV0FBVyxHQUFHL00sY0FBYyxDQUFDLENBQUM7RUFDcEMsTUFBTThQLEtBQUssR0FBR2hRLFdBQVcsQ0FBQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDRCxLQUFLLENBQUM7RUFDdkMsTUFBTUUsbUJBQW1CLEdBQUdsUSxXQUFXLENBQUNpUSxDQUFDLElBQUlBLENBQUMsQ0FBQ0MsbUJBQW1CLENBQUM7RUFDbkUsTUFBTUMsa0JBQWtCLEdBQUduUSxXQUFXLENBQUNpUSxDQUFDLElBQUlBLENBQUMsQ0FBQ0Usa0JBQWtCLENBQUM7RUFDakUsTUFBTUMsc0JBQXNCLEdBQUdwUSxXQUFXLENBQUNpUSxDQUFDLElBQUlBLENBQUMsQ0FBQ0csc0JBQXNCLENBQUM7RUFDekU7RUFDQTtFQUNBO0VBQ0EsTUFBTUMsbUJBQW1CLEdBQ3ZCSCxtQkFBbUIsS0FBS0Msa0JBQWtCLElBQUlDLHNCQUFzQixDQUFDO0VBQ3ZFO0VBQ0EsTUFBTUUsa0JBQWtCLEdBQUd0USxXQUFXLENBQ3BDaVEsQ0FBQyxJQUNDLFVBQVUsS0FBSyxLQUFLLElBQUlBLENBQUMsQ0FBQ00scUJBQXFCLEtBQUtDLFNBQ3hELENBQUM7RUFDRCxNQUFNQyxpQkFBaUIsR0FDckIsVUFBVSxLQUFLLEtBQUssSUFBSUgsa0JBQWtCO0VBQzVDO0VBQ0EsTUFBTUksa0JBQWtCLEdBQUcxUSxXQUFXLENBQUNpUSxDQUFDLElBQ2xDLEtBQ04sQ0FBQztFQUNELE1BQU1VLFdBQVcsR0FBRzNRLFdBQVcsQ0FBQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDVSxXQUFXLENBQUM7RUFDbkQsTUFBTUMsY0FBYyxHQUFHbFIsZUFBZSxDQUFDLENBQUM7RUFDeEMsTUFBTW1SLHFCQUFxQixHQUFHN1EsV0FBVyxDQUFDaVEsQ0FBQyxJQUFJQSxDQUFDLENBQUNhLGdCQUFnQixDQUFDO0VBQ2xFLE1BQU1DLFdBQVcsR0FBRy9RLFdBQVcsQ0FBQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDYyxXQUFXLENBQUM7RUFDbkQsTUFBTS9ELDZCQUE2QixHQUFHaE4sV0FBVyxDQUMvQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDakQsNkJBQ1QsQ0FBQztFQUNELE1BQU1nRSxrQkFBa0IsR0FBR2hSLFdBQVcsQ0FBQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDZSxrQkFBa0IsQ0FBQztFQUNqRSxNQUFNQyxpQkFBaUIsR0FBR2pSLFdBQVcsQ0FBQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDZ0IsaUJBQWlCLENBQUM7RUFDL0QsTUFBTUMsZUFBZSxHQUFHbFIsV0FBVyxDQUFDaVEsQ0FBQyxJQUFJQSxDQUFDLENBQUNrQixZQUFZLENBQUMsS0FBSyxXQUFXO0VBQ3hFLE1BQU07SUFBRUMsU0FBUyxFQUFFQyxVQUFVO0lBQUVDO0VBQWUsQ0FBQyxHQUFHdlMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxHQUM5RDBGLGVBQWUsQ0FBQyxDQUFDLEdBQ2pCO0lBQUUyTSxTQUFTLEVBQUVaLFNBQVM7SUFBRWMsY0FBYyxFQUFFZDtFQUFVLENBQUM7RUFDdkQsTUFBTWUsc0JBQXNCLEdBQUcsQ0FBQyxDQUFDRixVQUFVLElBQUksQ0FBQ0MsY0FBYztFQUM5RDtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsTUFBTUUsWUFBWSxHQUNoQnpTLE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSUEsT0FBTyxDQUFDLGNBQWMsQ0FBQztFQUN4QztFQUNBaUIsV0FBVyxDQUFDaVEsQ0FBQyxJQUFJQSxDQUFDLENBQUN3QixXQUFXLENBQUMsSUFBSSxDQUFDVCxrQkFBa0IsR0FDdEQsS0FBSztFQUNYLE1BQU1VLGNBQWMsR0FBRzFSLFdBQVcsQ0FBQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDdEQsYUFBYSxDQUFDO0VBQ3hELE1BQU1nRix1QkFBdUIsR0FBRzNSLFdBQVcsQ0FBQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDMEIsdUJBQXVCLENBQUM7RUFDM0UsTUFBTUMsZUFBZSxHQUFHNVIsV0FBVyxDQUFDaVEsQ0FBQyxJQUFJQSxDQUFDLENBQUMyQixlQUFlLENBQUM7RUFDM0QsTUFBTUMsVUFBVSxHQUFHN1IsV0FBVyxDQUFDaVEsQ0FBQyxJQUM5QjNLLGlCQUFpQixDQUFDLENBQUMsR0FBRzJLLENBQUMsQ0FBQzZCLFFBQVEsR0FBRyxLQUNyQyxDQUFDO0VBQ0QsTUFBTUMsV0FBVyxHQUFHL1IsV0FBVyxDQUFDaVEsQ0FBQyxJQUFJQSxDQUFDLENBQUM4QixXQUFXLENBQUM7RUFDbkQsTUFBTUMsY0FBYyxHQUFHOU8scUJBQXFCLENBQUM2TSxLQUFLLENBQUNrQyxRQUFRLENBQUMsQ0FBQyxDQUFDO0VBQzlELE1BQU1DLGdCQUFnQixHQUFHRixjQUFjLEVBQUVHLFFBQVEsQ0FBQ0MsU0FBUztFQUMzRDtFQUNBO0VBQ0E7RUFDQSxNQUFNQyxpQkFBaUIsR0FDckJMLGNBQWMsRUFBRUcsUUFBUSxDQUFDRyxLQUFLLElBQzlCek8sWUFBWSxDQUFDME8sUUFBUSxDQUFDUCxjQUFjLENBQUNHLFFBQVEsQ0FBQ0csS0FBSyxJQUFJeE8sY0FBYyxDQUFDLEdBQ2pFa08sY0FBYyxDQUFDRyxRQUFRLENBQUNHLEtBQUssSUFBSXhPLGNBQWMsR0FDaEQwTSxTQUFTO0VBQ2Y7RUFDQSxNQUFNZ0Msa0JBQWtCLEdBQUduVCxPQUFPLENBQ2hDLE1BQU1rRSx5QkFBeUIsQ0FBQ3lNLEtBQUssQ0FBQyxFQUN0QyxDQUFDQSxLQUFLLENBQ1IsQ0FBQzs7RUFFRDtFQUNBLE1BQU15QyxjQUFjLEdBQ2xCRCxrQkFBa0IsQ0FBQ2xELE1BQU0sR0FBRyxDQUFDLElBQUkwQyxjQUFjLEtBQUt4QixTQUFTOztFQUUvRDtFQUNBLE1BQU1rQyw4QkFBOEIsR0FBR3JULE9BQU8sQ0FBQyxFQUFFLEVBQUVpRSxxQkFBcUIsSUFBSTtJQUMxRSxJQUFJME8sY0FBYyxFQUFFO01BQ2xCLE9BQU87UUFDTCxHQUFHN0gscUJBQXFCO1FBQ3hCZSxJQUFJLEVBQUU4RyxjQUFjLENBQUNXO01BQ3ZCLENBQUM7SUFDSDtJQUNBLE9BQU94SSxxQkFBcUI7RUFDOUIsQ0FBQyxFQUFFLENBQUM2SCxjQUFjLEVBQUU3SCxxQkFBcUIsQ0FBQyxDQUFDO0VBQzNDLE1BQU07SUFBRXlJLFlBQVk7SUFBRUMsZUFBZTtJQUFFQyxZQUFZO0lBQUVDO0VBQW1CLENBQUMsR0FDdkVyUixnQkFBZ0IsQ0FDZHNSLEtBQUssSUFBSTtJQUNQbEgsaUJBQWlCLENBQUNrSCxLQUFLLENBQUN6SCxjQUFjLENBQUM7SUFDdkMsS0FBS3FCLFFBQVEsQ0FBQ29HLEtBQUssQ0FBQ0MsT0FBTyxDQUFDO0VBQzlCLENBQUMsRUFDRGxJLEtBQUssRUFDTDBFLGdCQUFnQixFQUNoQkosZUFBZSxFQUNmL0QsWUFBWSxFQUNaSCxZQUFZLEVBQ1pELElBQUksRUFDSnVDLGtCQUFrQixFQUNsQkMscUJBQXFCLEVBQ3JCNUIsaUJBQWlCLEVBQ2pCUCxjQUNGLENBQUM7RUFDSDtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsTUFBTTJILGNBQWMsR0FBRzVULE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztFQUNqQyxJQUFJNFQsY0FBYyxDQUFDMUQsT0FBTyxLQUFLLENBQUMsQ0FBQyxFQUFFO0lBQ2pDMEQsY0FBYyxDQUFDMUQsT0FBTyxHQUFHMkQsaUJBQWlCLENBQUN4SSxRQUFRLENBQUM7RUFDdEQ7RUFDQTtFQUNBO0VBQ0E7RUFDQSxNQUFNeUksd0JBQXdCLEdBQUc5VCxNQUFNLENBQUMsS0FBSyxDQUFDO0VBRTlDLE1BQU0sQ0FBQytULGVBQWUsRUFBRUMsa0JBQWtCLENBQUMsR0FBRy9ULFFBQVEsQ0FBQyxLQUFLLENBQUM7RUFDN0QsTUFBTSxDQUFDZ1UsZ0JBQWdCLEVBQUVDLG1CQUFtQixDQUFDLEdBQUdqVSxRQUFRLENBQUMsS0FBSyxDQUFDO0VBQy9ELE1BQU0sQ0FBQ2tVLG1CQUFtQixFQUFFQyxzQkFBc0IsQ0FBQyxHQUFHblUsUUFBUSxDQUFDLENBQUMsQ0FBQztFQUNqRTtFQUNBO0VBQ0E7RUFDQSxNQUFNb1Usb0JBQW9CLEdBQUczVCxXQUFXLENBQUNpUSxDQUFDLElBQUlBLENBQUMsQ0FBQzBELG9CQUFvQixDQUFDO0VBQ3JFLE1BQU1DLHVCQUF1QixHQUFHelUsV0FBVyxDQUN6QyxDQUFDMFUsQ0FBQyxFQUFFLE1BQU0sR0FBRyxDQUFDLENBQUMxRyxJQUFJLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxDQUFDLEtBQ3JDRixXQUFXLENBQUNFLElBQUksSUFBSTtJQUNsQixNQUFNMkcsSUFBSSxHQUFHLE9BQU9ELENBQUMsS0FBSyxVQUFVLEdBQUdBLENBQUMsQ0FBQzFHLElBQUksQ0FBQ3dHLG9CQUFvQixDQUFDLEdBQUdFLENBQUM7SUFDdkUsSUFBSUMsSUFBSSxLQUFLM0csSUFBSSxDQUFDd0csb0JBQW9CLEVBQUUsT0FBT3hHLElBQUk7SUFDbkQsT0FBTztNQUFFLEdBQUdBLElBQUk7TUFBRXdHLG9CQUFvQixFQUFFRztJQUFLLENBQUM7RUFDaEQsQ0FBQyxDQUFDLEVBQ0osQ0FBQzdHLFdBQVcsQ0FDZCxDQUFDO0VBQ0QsTUFBTThHLG9CQUFvQixHQUFHM0wsdUJBQXVCLENBQUMsQ0FBQztFQUN0RDtFQUNBO0VBQ0E7RUFDQTtFQUNBLE1BQU00TCxhQUFhLEdBQUczVSxPQUFPLENBQzNCLE1BQ0U0VSxNQUFNLENBQUNDLE1BQU0sQ0FBQ2xFLEtBQUssQ0FBQyxDQUFDbUUsSUFBSSxDQUN2QkMsQ0FBQyxJQUNDelEsZ0JBQWdCLENBQUN5USxDQUFDLENBQUMsSUFDbkIsRUFBRSxVQUFVLEtBQUssS0FBSyxJQUFJM1EsZ0JBQWdCLENBQUMyUSxDQUFDLENBQUMsQ0FDakQsQ0FBQyxFQUNILENBQUNwRSxLQUFLLENBQ1IsQ0FBQztFQUNELE1BQU1xRSxtQkFBbUIsR0FBR0wsYUFBYSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUM7RUFDbEQ7RUFDQTVVLFNBQVMsQ0FBQyxNQUFNO0lBQ2QsSUFBSXVVLG9CQUFvQixJQUFJSSxvQkFBb0IsRUFBRTtNQUNoREgsdUJBQXVCLENBQ3JCVSxJQUFJLENBQUNDLEdBQUcsQ0FBQ0YsbUJBQW1CLEVBQUVOLG9CQUFvQixHQUFHLENBQUMsQ0FDeEQsQ0FBQztJQUNILENBQUMsTUFBTSxJQUFJSixvQkFBb0IsR0FBR1UsbUJBQW1CLEVBQUU7TUFDckRULHVCQUF1QixDQUFDUyxtQkFBbUIsQ0FBQztJQUM5QztFQUNGLENBQUMsRUFBRSxDQUFDTixvQkFBb0IsRUFBRUosb0JBQW9CLEVBQUVVLG1CQUFtQixDQUFDLENBQUM7RUFDckUsTUFBTSxDQUFDRyxTQUFTLEVBQUVDLFlBQVksQ0FBQyxHQUFHbFYsUUFBUSxDQUFDLEtBQUssQ0FBQztFQUNqRCxNQUFNLENBQUNtVixzQkFBc0IsRUFBRUMseUJBQXlCLENBQUMsR0FBR3BWLFFBQVEsQ0FBQyxLQUFLLENBQUM7RUFDM0UsTUFBTSxDQUFDcVYsZUFBZSxFQUFFQyxrQkFBa0IsQ0FBQyxHQUFHdFYsUUFBUSxDQUFDLEtBQUssQ0FBQztFQUM3RCxNQUFNLENBQUN1VixhQUFhLEVBQUVDLGdCQUFnQixDQUFDLEdBQUd4VixRQUFRLENBQUMsS0FBSyxDQUFDO0VBQ3pELE1BQU0sQ0FBQ3lWLGdCQUFnQixFQUFFQyxtQkFBbUIsQ0FBQyxHQUFHMVYsUUFBUSxDQUFDLEtBQUssQ0FBQztFQUMvRCxNQUFNLENBQUMyVixpQkFBaUIsRUFBRUMsb0JBQW9CLENBQUMsR0FBRzVWLFFBQVEsQ0FBQyxLQUFLLENBQUM7RUFDakUsTUFBTSxDQUFDNlYsa0JBQWtCLEVBQUVDLHFCQUFxQixDQUFDLEdBQUc5VixRQUFRLENBQUMsS0FBSyxDQUFDO0VBQ25FLE1BQU0sQ0FBQytWLGtCQUFrQixFQUFFQyxxQkFBcUIsQ0FBQyxHQUFHaFcsUUFBUSxDQUFDLEtBQUssQ0FBQztFQUNuRSxNQUFNLENBQUNpVyxpQkFBaUIsRUFBRUMsb0JBQW9CLENBQUMsR0FBR2xXLFFBQVEsQ0FBQyxLQUFLLENBQUM7RUFDakUsTUFBTSxDQUFDbVcsc0JBQXNCLEVBQUVDLHlCQUF5QixDQUFDLEdBQ3ZEcFcsUUFBUSxDQUFDMEUsY0FBYyxHQUFHLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQztFQUN2QyxNQUFNMlIsdUJBQXVCLEdBQUd0VyxNQUFNLENBQUN1VyxNQUFNLENBQUNDLE9BQU8sR0FBRyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUM7O0VBRW5FO0VBQ0EsTUFBTUMsbUJBQW1CLEdBQUcxVyxPQUFPLENBQUMsTUFBTTtJQUN4QyxNQUFNMlcsaUJBQWlCLEdBQUdqTCxLQUFLLENBQUNrTCxPQUFPLENBQUMsSUFBSSxDQUFDO0lBQzdDLElBQUlELGlCQUFpQixLQUFLLENBQUMsQ0FBQyxFQUFFO01BQzVCLE9BQU8sSUFBSSxFQUFDO0lBQ2Q7SUFDQSxPQUFPMUssWUFBWSxJQUFJMEssaUJBQWlCO0VBQzFDLENBQUMsRUFBRSxDQUFDakwsS0FBSyxFQUFFTyxZQUFZLENBQUMsQ0FBQztFQUV6QixNQUFNNEssa0JBQWtCLEdBQUc3VyxPQUFPLENBQUMsTUFBTTtJQUN2QyxNQUFNOFcsZ0JBQWdCLEdBQUdwTCxLQUFLLENBQUNxTCxXQUFXLENBQUMsSUFBSSxDQUFDO0lBQ2hELElBQUlELGdCQUFnQixLQUFLLENBQUMsQ0FBQyxFQUFFO01BQzNCLE9BQU8sSUFBSSxFQUFDO0lBQ2Q7SUFDQSxPQUFPN0ssWUFBWSxHQUFHNkssZ0JBQWdCO0VBQ3hDLENBQUMsRUFBRSxDQUFDcEwsS0FBSyxFQUFFTyxZQUFZLENBQUMsQ0FBQzs7RUFFekI7RUFDQTtFQUNBLE1BQU0rSyxXQUFXLEVBQUVqUCxXQUFXLEVBQUUsR0FBRy9ILE9BQU8sQ0FBQyxNQUFNO0lBQy9DLElBQUksQ0FBQ2dGLG9CQUFvQixDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUU7SUFDdEM7SUFDQSxJQUFJNkMsa0JBQWtCLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRTtJQUNuQyxJQUFJLENBQUN5SixXQUFXLEVBQUU7TUFDaEIsT0FBTyxFQUFFO0lBQ1g7SUFDQSxNQUFNMkYsYUFBYSxHQUFHaFMsS0FBSyxDQUN6QjJQLE1BQU0sQ0FBQ0MsTUFBTSxDQUFDdkQsV0FBVyxDQUFDNEYsU0FBUyxDQUFDLEVBQ3BDbkMsQ0FBQyxJQUFJQSxDQUFDLENBQUNvQyxJQUFJLEtBQUssV0FDbEIsQ0FBQztJQUNELE9BQU8sQ0FDTDtNQUNFQSxJQUFJLEVBQUU3RixXQUFXLENBQUM4RixRQUFRO01BQzFCQyxXQUFXLEVBQUVKLGFBQWE7TUFDMUJLLFlBQVksRUFBRSxDQUFDO01BQ2ZDLFNBQVMsRUFBRTtJQUNiLENBQUMsQ0FDRjtFQUNILENBQUMsRUFBRSxDQUFDakcsV0FBVyxDQUFDLENBQUM7O0VBRWpCO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsTUFBTWtHLGdCQUFnQixHQUFHeFgsT0FBTyxDQUM5QixNQUFNaUYsS0FBSyxDQUFDMlAsTUFBTSxDQUFDQyxNQUFNLENBQUNsRSxLQUFLLENBQUMsRUFBRW9FLENBQUMsSUFBSUEsQ0FBQyxDQUFDMEMsTUFBTSxLQUFLLFNBQVMsQ0FBQyxFQUM5RCxDQUFDOUcsS0FBSyxDQUNSLENBQUM7RUFDRDtFQUNBO0VBQ0E7RUFDQSxNQUFNK0csa0JBQWtCLEdBQ3RCLENBQUNGLGdCQUFnQixHQUFHLENBQUMsSUFDbEIsVUFBVSxLQUFLLEtBQUssSUFBSTlDLG9CQUFvQixHQUFHLENBQUUsS0FDcEQsQ0FBQ2pMLHFCQUFxQixDQUFDa0gsS0FBSyxFQUFFa0IsZUFBZSxDQUFDO0VBQ2hELE1BQU04RixrQkFBa0IsR0FBR1gsV0FBVyxDQUFDL0csTUFBTSxHQUFHLENBQUM7RUFFakQsTUFBTTJILFdBQVcsR0FBRzVYLE9BQU8sQ0FDekIsTUFDRSxDQUNFMFgsa0JBQWtCLElBQUksT0FBTyxFQUM3QnRHLGlCQUFpQixJQUFJLE1BQU0sRUFDM0JDLGtCQUFrQixJQUFJLE9BQU8sRUFDN0JzRyxrQkFBa0IsSUFBSSxPQUFPLEVBQzdCM0csbUJBQW1CLElBQUksUUFBUSxFQUMvQmtCLHNCQUFzQixJQUFJLFdBQVcsQ0FDdEMsQ0FBQzJGLE1BQU0sQ0FBQ0MsT0FBTyxDQUFDLElBQUloWCxVQUFVLEVBQUUsRUFDbkMsQ0FDRTRXLGtCQUFrQixFQUNsQnRHLGlCQUFpQixFQUNqQkMsa0JBQWtCLEVBQ2xCc0csa0JBQWtCLEVBQ2xCM0csbUJBQW1CLEVBQ25Ca0Isc0JBQXNCLENBRTFCLENBQUM7O0VBRUQ7RUFDQTtFQUNBO0VBQ0E7RUFDQSxNQUFNNkYsa0JBQWtCLEdBQUdwWCxXQUFXLENBQUNpUSxDQUFDLElBQUlBLENBQUMsQ0FBQ29ILGVBQWUsQ0FBQztFQUM5RCxNQUFNQyxrQkFBa0IsR0FDdEJGLGtCQUFrQixJQUFJSCxXQUFXLENBQUMxRSxRQUFRLENBQUM2RSxrQkFBa0IsQ0FBQyxHQUMxREEsa0JBQWtCLEdBQ2xCLElBQUk7RUFFVmhZLFNBQVMsQ0FBQyxNQUFNO0lBQ2QsSUFBSWdZLGtCQUFrQixJQUFJLENBQUNFLGtCQUFrQixFQUFFO01BQzdDckssV0FBVyxDQUFDRSxJQUFJLElBQ2RBLElBQUksQ0FBQ2tLLGVBQWUsS0FBSyxJQUFJLEdBQ3pCbEssSUFBSSxHQUNKO1FBQUUsR0FBR0EsSUFBSTtRQUFFa0ssZUFBZSxFQUFFO01BQUssQ0FDdkMsQ0FBQztJQUNIO0VBQ0YsQ0FBQyxFQUFFLENBQUNELGtCQUFrQixFQUFFRSxrQkFBa0IsRUFBRXJLLFdBQVcsQ0FBQyxDQUFDO0VBRXpELE1BQU1zSyxhQUFhLEdBQUdELGtCQUFrQixLQUFLLE9BQU87RUFDcEQsTUFBTUUsWUFBWSxHQUFHRixrQkFBa0IsS0FBSyxNQUFNO0VBQ2xELE1BQU1HLGFBQWEsR0FBR0gsa0JBQWtCLEtBQUssT0FBTztFQUNwRCxNQUFNSSxhQUFhLEdBQUdKLGtCQUFrQixLQUFLLE9BQU87RUFDcEQsTUFBTUssY0FBYyxHQUFHTCxrQkFBa0IsS0FBSyxRQUFRO0VBRXRELFNBQVNNLGdCQUFnQkEsQ0FBQ0MsSUFBSSxFQUFFMVgsVUFBVSxHQUFHLElBQUksQ0FBQyxFQUFFLElBQUksQ0FBQztJQUN2RDhNLFdBQVcsQ0FBQ0UsSUFBSSxJQUNkQSxJQUFJLENBQUNrSyxlQUFlLEtBQUtRLElBQUksR0FBRzFLLElBQUksR0FBRztNQUFFLEdBQUdBLElBQUk7TUFBRWtLLGVBQWUsRUFBRVE7SUFBSyxDQUMxRSxDQUFDO0lBQ0QsSUFBSUEsSUFBSSxLQUFLLE9BQU8sRUFBRTtNQUNwQm5FLHNCQUFzQixDQUFDLENBQUMsQ0FBQztNQUN6QkUsdUJBQXVCLENBQUNTLG1CQUFtQixDQUFDO0lBQzlDO0VBQ0Y7O0VBRUE7RUFDQTtFQUNBLFNBQVN5RCxjQUFjQSxDQUFDQyxLQUFLLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFQyxXQUFXLEdBQUcsS0FBSyxDQUFDLEVBQUUsT0FBTyxDQUFDO0lBQ25FLE1BQU1DLEdBQUcsR0FBR1gsa0JBQWtCLEdBQzFCTCxXQUFXLENBQUNoQixPQUFPLENBQUNxQixrQkFBa0IsQ0FBQyxHQUN2QyxDQUFDLENBQUM7SUFDTixNQUFNeEQsSUFBSSxHQUFHbUQsV0FBVyxDQUFDZ0IsR0FBRyxHQUFHRixLQUFLLENBQUM7SUFDckMsSUFBSWpFLElBQUksRUFBRTtNQUNSOEQsZ0JBQWdCLENBQUM5RCxJQUFJLENBQUM7TUFDdEIsT0FBTyxJQUFJO0lBQ2I7SUFDQSxJQUFJaUUsS0FBSyxHQUFHLENBQUMsSUFBSUMsV0FBVyxFQUFFO01BQzVCSixnQkFBZ0IsQ0FBQyxJQUFJLENBQUM7TUFDdEIsT0FBTyxJQUFJO0lBQ2I7SUFDQSxPQUFPLEtBQUs7RUFDZDs7RUFFQTtFQUNBLE1BQU07SUFDSk0sVUFBVSxFQUFFcEgsZ0JBQWdCO0lBQzVCcUgsWUFBWTtJQUNaQyxzQkFBc0I7SUFDdEJDO0VBQ0YsQ0FBQyxHQUFHdlcsbUJBQW1CLENBQUM7SUFDdEJ3VyxVQUFVLEVBQUV2TixLQUFLO0lBQ2pCd04scUJBQXFCLEVBQUU5TjtFQUN6QixDQUFDLENBQUM7RUFFRixNQUFNK04sY0FBYyxHQUFHblosT0FBTyxDQUM1QixNQUNFb08sa0JBQWtCLElBQUlxRixZQUFZLEdBQzlCNUosaUJBQWlCLENBQ2YsT0FBTzRKLFlBQVksS0FBSyxRQUFRLEdBQzVCQSxZQUFZLEdBQ1pBLFlBQVksQ0FBQ0csT0FDbkIsQ0FBQyxHQUNEbEksS0FBSyxFQUNYLENBQUMwQyxrQkFBa0IsRUFBRXFGLFlBQVksRUFBRS9ILEtBQUssQ0FDMUMsQ0FBQztFQUVELE1BQU0wTixhQUFhLEdBQUdwWixPQUFPLENBQzNCLE1BQU1xSSw0QkFBNEIsQ0FBQzhRLGNBQWMsQ0FBQyxFQUNsRCxDQUFDQSxjQUFjLENBQ2pCLENBQUM7RUFFRCxNQUFNRSxtQkFBbUIsR0FBRzFZLFdBQVcsQ0FBQ2lRLENBQUMsSUFBSUEsQ0FBQyxDQUFDeUksbUJBQW1CLENBQUM7RUFDbkUsTUFBTUMsa0JBQWtCLEdBQUczWSxXQUFXLENBQUNpUSxDQUFDLElBQUlBLENBQUMsQ0FBQzBJLGtCQUFrQixDQUFDO0VBQ2pFLE1BQU1DLGlCQUFpQixHQUFHdlosT0FBTyxDQUMvQixNQUNFTixPQUFPLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQzJaLG1CQUFtQixJQUFJLENBQUNDLGtCQUFrQixHQUMvRDdRLDZCQUE2QixDQUFDMFEsY0FBYyxDQUFDLEdBQzdDLEVBQUUsRUFDUixDQUFDQSxjQUFjLEVBQUVFLG1CQUFtQixFQUFFQyxrQkFBa0IsQ0FDMUQsQ0FBQztFQUVELE1BQU1FLG1CQUFtQixHQUFHeFosT0FBTyxDQUNqQyxNQUNFdUIsb0JBQW9CLENBQUMsQ0FBQyxHQUNsQm1ILCtCQUErQixDQUFDeVEsY0FBYyxDQUFDLEdBQy9DLEVBQUUsRUFDUixDQUFDQSxjQUFjLENBQ2pCLENBQUM7RUFFRCxNQUFNTSxXQUFXLEdBQUd6WixPQUFPLENBQ3pCLE1BQU11SCx1QkFBdUIsQ0FBQzRSLGNBQWMsQ0FBQyxFQUM3QyxDQUFDQSxjQUFjLENBQ2pCLENBQUM7RUFFRCxNQUFNTyxhQUFhLEdBQUcxWixPQUFPLENBQzNCLE1BQU1vQix5QkFBeUIsQ0FBQytYLGNBQWMsQ0FBQyxFQUMvQyxDQUFDQSxjQUFjLENBQ2pCLENBQUM7RUFFRCxNQUFNUSxvQkFBb0IsR0FBRzNaLE9BQU8sQ0FBQyxNQUFNO0lBQ3pDLE1BQU00WixTQUFTLEdBQUdwUyx5QkFBeUIsQ0FBQzJSLGNBQWMsQ0FBQztJQUMzRDtJQUNBLE9BQU9TLFNBQVMsQ0FBQy9CLE1BQU0sQ0FBQ2dDLEdBQUcsSUFBSTtNQUM3QixNQUFNQyxXQUFXLEdBQUdYLGNBQWMsQ0FBQzFJLEtBQUssQ0FBQ29KLEdBQUcsQ0FBQzFLLEtBQUssR0FBRyxDQUFDLEVBQUUwSyxHQUFHLENBQUN6SyxHQUFHLENBQUMsRUFBQztNQUNqRSxPQUFPMU4sVUFBVSxDQUFDb1ksV0FBVyxFQUFFNU8sUUFBUSxDQUFDO0lBQzFDLENBQUMsQ0FBQztFQUNKLENBQUMsRUFBRSxDQUFDaU8sY0FBYyxFQUFFak8sUUFBUSxDQUFDLENBQUM7RUFFOUIsTUFBTTZPLG1CQUFtQixHQUFHL1osT0FBTyxDQUNqQyxNQUNFTixPQUFPLENBQUMsY0FBYyxDQUFDLEdBQUc4SSx3QkFBd0IsQ0FBQzJRLGNBQWMsQ0FBQyxHQUFHLEVBQUUsRUFDekUsQ0FBQ0EsY0FBYyxDQUNqQixDQUFDO0VBRUQsTUFBTWEsb0JBQW9CLEdBQUc3WixvQkFBb0IsQ0FDL0N5SCxzQkFBc0IsRUFDdEJGLHVCQUNGLENBQUM7RUFDRCxNQUFNdVMsb0JBQW9CLEdBQUdqYSxPQUFPLENBQ2xDLE1BQ0UySCxpQkFBaUIsQ0FBQytJLEtBQUssQ0FBQ2tDLFFBQVEsQ0FBQyxDQUFDLENBQUNzSCxHQUFHLENBQUNDLE9BQU8sQ0FBQyxHQUMzQzFTLHlCQUF5QixDQUFDMFIsY0FBYyxDQUFDLEdBQ3pDLEVBQUU7RUFDUjtFQUNBLENBQUNBLGNBQWMsRUFBRWEsb0JBQW9CLENBQ3ZDLENBQUM7O0VBRUQ7RUFDQSxNQUFNSSx1QkFBdUIsR0FBR3BhLE9BQU8sQ0FBQyxFQUFFLEVBQUVxYSxLQUFLLENBQUM7SUFDaERsTCxLQUFLLEVBQUUsTUFBTTtJQUNiQyxHQUFHLEVBQUUsTUFBTTtJQUNYa0wsVUFBVSxFQUFFLE1BQU1sUyxLQUFLO0VBQ3pCLENBQUMsQ0FBQyxJQUFJO0lBQ0osSUFBSSxDQUFDcEQsb0JBQW9CLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRTtJQUN0QyxJQUFJLENBQUNzTSxXQUFXLEVBQUU0RixTQUFTLEVBQUUsT0FBTyxFQUFFO0lBRXRDLE1BQU1xRCxVQUFVLEVBQUVGLEtBQUssQ0FBQztNQUN0QmxMLEtBQUssRUFBRSxNQUFNO01BQ2JDLEdBQUcsRUFBRSxNQUFNO01BQ1hrTCxVQUFVLEVBQUUsTUFBTWxTLEtBQUs7SUFDekIsQ0FBQyxDQUFDLEdBQUcsRUFBRTtJQUNQLE1BQU1vUyxPQUFPLEdBQUdsSixXQUFXLENBQUM0RixTQUFTO0lBQ3JDLElBQUksQ0FBQ3NELE9BQU8sRUFBRSxPQUFPRCxVQUFVOztJQUUvQjtJQUNBLE1BQU1FLEtBQUssR0FBRyxrQkFBa0I7SUFDaEMsTUFBTUMsWUFBWSxHQUFHOUYsTUFBTSxDQUFDQyxNQUFNLENBQUMyRixPQUFPLENBQUM7SUFDM0MsSUFBSUcsS0FBSztJQUNULE9BQU8sQ0FBQ0EsS0FBSyxHQUFHRixLQUFLLENBQUNHLElBQUksQ0FBQ3pCLGNBQWMsQ0FBQyxNQUFNLElBQUksRUFBRTtNQUNwRCxNQUFNMEIsWUFBWSxHQUFHRixLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRTtNQUNuQyxNQUFNRyxTQUFTLEdBQUdILEtBQUssQ0FBQ0ksS0FBSyxHQUFHRixZQUFZLENBQUM1SyxNQUFNO01BQ25ELE1BQU0rSyxTQUFTLEdBQUdMLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQ00sU0FBUyxDQUFDLENBQUM7TUFDdEMsTUFBTTlELElBQUksR0FBR3dELEtBQUssQ0FBQyxDQUFDLENBQUM7O01BRXJCO01BQ0EsTUFBTU8sTUFBTSxHQUFHUixZQUFZLENBQUNTLElBQUksQ0FBQ3BHLENBQUMsSUFBSUEsQ0FBQyxDQUFDb0MsSUFBSSxLQUFLQSxJQUFJLENBQUM7TUFDdEQsSUFBSStELE1BQU0sRUFBRWpJLEtBQUssRUFBRTtRQUNqQixNQUFNcUgsVUFBVSxHQUNkL1YsMEJBQTBCLENBQUMyVyxNQUFNLENBQUNqSSxLQUFLLElBQUl4TyxjQUFjLENBQUM7UUFDNUQsSUFBSTZWLFVBQVUsRUFBRTtVQUNkQyxVQUFVLENBQUNhLElBQUksQ0FBQztZQUNkak0sS0FBSyxFQUFFMkwsU0FBUztZQUNoQjFMLEdBQUcsRUFBRTBMLFNBQVMsR0FBR0UsU0FBUyxDQUFDL0ssTUFBTTtZQUNqQ3FLO1VBQ0YsQ0FBQyxDQUFDO1FBQ0o7TUFDRjtJQUNGO0lBQ0EsT0FBT0MsVUFBVTtFQUNuQixDQUFDLEVBQUUsQ0FBQ3BCLGNBQWMsRUFBRTdILFdBQVcsQ0FBQyxDQUFDO0VBRWpDLE1BQU0rSixpQkFBaUIsR0FBR3JiLE9BQU8sQ0FDL0IsTUFDRWdDLGVBQWUsQ0FBQ21YLGNBQWMsQ0FBQyxDQUM1QnRCLE1BQU0sQ0FBQ3lELENBQUMsSUFBSUEsQ0FBQyxDQUFDWCxLQUFLLENBQUNZLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUN6Q0MsR0FBRyxDQUFDRixDQUFDLEtBQUs7SUFBRW5NLEtBQUssRUFBRW1NLENBQUMsQ0FBQ1AsS0FBSztJQUFFM0wsR0FBRyxFQUFFa00sQ0FBQyxDQUFDUCxLQUFLLEdBQUdPLENBQUMsQ0FBQ1gsS0FBSyxDQUFDMUs7RUFBTyxDQUFDLENBQUMsQ0FBQyxFQUNsRSxDQUFDa0osY0FBYyxDQUNqQixDQUFDOztFQUVEO0VBQ0E7RUFDQTtFQUNBLE1BQU1zQyxpQkFBaUIsR0FBR0osaUJBQWlCLENBQUN2RyxJQUFJLENBQzlDd0csQ0FBQyxJQUFJQSxDQUFDLENBQUNuTSxLQUFLLEtBQUtsRCxZQUNuQixDQUFDOztFQUVEO0VBQ0E7RUFDQTtFQUNBbE0sU0FBUyxDQUFDLE1BQU07SUFDZCxNQUFNMmIsTUFBTSxHQUFHTCxpQkFBaUIsQ0FBQ0YsSUFBSSxDQUNuQ0csQ0FBQyxJQUFJclAsWUFBWSxHQUFHcVAsQ0FBQyxDQUFDbk0sS0FBSyxJQUFJbEQsWUFBWSxHQUFHcVAsQ0FBQyxDQUFDbE0sR0FDbEQsQ0FBQztJQUNELElBQUlzTSxNQUFNLEVBQUU7TUFDVixNQUFNQyxHQUFHLEdBQUcsQ0FBQ0QsTUFBTSxDQUFDdk0sS0FBSyxHQUFHdU0sTUFBTSxDQUFDdE0sR0FBRyxJQUFJLENBQUM7TUFDM0NZLGVBQWUsQ0FBQy9ELFlBQVksR0FBRzBQLEdBQUcsR0FBR0QsTUFBTSxDQUFDdk0sS0FBSyxHQUFHdU0sTUFBTSxDQUFDdE0sR0FBRyxDQUFDO0lBQ2pFO0VBQ0YsQ0FBQyxFQUFFLENBQUNuRCxZQUFZLEVBQUVvUCxpQkFBaUIsRUFBRXJMLGVBQWUsQ0FBQyxDQUFDO0VBRXRELE1BQU00TCxrQkFBa0IsR0FBRzViLE9BQU8sQ0FBQyxFQUFFLEVBQUVtSSxhQUFhLEVBQUUsSUFBSTtJQUN4RCxNQUFNb1MsVUFBVSxFQUFFcFMsYUFBYSxFQUFFLEdBQUcsRUFBRTs7SUFFdEM7SUFDQTtJQUNBLEtBQUssTUFBTTBULEdBQUcsSUFBSVIsaUJBQWlCLEVBQUU7TUFDbkMsSUFBSXBQLFlBQVksS0FBSzRQLEdBQUcsQ0FBQzFNLEtBQUssRUFBRTtRQUM5Qm9MLFVBQVUsQ0FBQ2EsSUFBSSxDQUFDO1VBQ2RqTSxLQUFLLEVBQUUwTSxHQUFHLENBQUMxTSxLQUFLO1VBQ2hCQyxHQUFHLEVBQUV5TSxHQUFHLENBQUN6TSxHQUFHO1VBQ1o2RCxLQUFLLEVBQUU5QixTQUFTO1VBQ2hCMkssT0FBTyxFQUFFLElBQUk7VUFDYkMsUUFBUSxFQUFFO1FBQ1osQ0FBQyxDQUFDO01BQ0o7SUFDRjtJQUVBLElBQUkzTixrQkFBa0IsSUFBSXFGLFlBQVksSUFBSSxDQUFDQyxrQkFBa0IsRUFBRTtNQUM3RDZHLFVBQVUsQ0FBQ2EsSUFBSSxDQUFDO1FBQ2RqTSxLQUFLLEVBQUVsRCxZQUFZO1FBQ25CbUQsR0FBRyxFQUFFbkQsWUFBWSxHQUFHc0gsWUFBWSxDQUFDdEQsTUFBTTtRQUN2Q2dELEtBQUssRUFBRSxTQUFTO1FBQ2hCOEksUUFBUSxFQUFFO01BQ1osQ0FBQyxDQUFDO0lBQ0o7O0lBRUE7SUFDQSxLQUFLLE1BQU1DLE9BQU8sSUFBSXZDLFdBQVcsRUFBRTtNQUNqQ2MsVUFBVSxDQUFDYSxJQUFJLENBQUM7UUFDZGpNLEtBQUssRUFBRTZNLE9BQU8sQ0FBQzdNLEtBQUs7UUFDcEJDLEdBQUcsRUFBRTRNLE9BQU8sQ0FBQzVNLEdBQUc7UUFDaEI2RCxLQUFLLEVBQUUsU0FBUztRQUNoQjhJLFFBQVEsRUFBRTtNQUNaLENBQUMsQ0FBQztJQUNKOztJQUVBO0lBQ0EsS0FBSyxNQUFNQyxPQUFPLElBQUlyQyxvQkFBb0IsRUFBRTtNQUMxQ1ksVUFBVSxDQUFDYSxJQUFJLENBQUM7UUFDZGpNLEtBQUssRUFBRTZNLE9BQU8sQ0FBQzdNLEtBQUs7UUFDcEJDLEdBQUcsRUFBRTRNLE9BQU8sQ0FBQzVNLEdBQUc7UUFDaEI2RCxLQUFLLEVBQUUsWUFBWTtRQUNuQjhJLFFBQVEsRUFBRTtNQUNaLENBQUMsQ0FBQztJQUNKOztJQUVBO0lBQ0EsS0FBSyxNQUFNQyxPQUFPLElBQUlqQyxtQkFBbUIsRUFBRTtNQUN6Q1EsVUFBVSxDQUFDYSxJQUFJLENBQUM7UUFDZGpNLEtBQUssRUFBRTZNLE9BQU8sQ0FBQzdNLEtBQUs7UUFDcEJDLEdBQUcsRUFBRTRNLE9BQU8sQ0FBQzVNLEdBQUc7UUFDaEI2RCxLQUFLLEVBQUUsWUFBWTtRQUNuQjhJLFFBQVEsRUFBRTtNQUNaLENBQUMsQ0FBQztJQUNKO0lBRUEsS0FBSyxNQUFNQyxPQUFPLElBQUkvQixvQkFBb0IsRUFBRTtNQUMxQ00sVUFBVSxDQUFDYSxJQUFJLENBQUM7UUFDZGpNLEtBQUssRUFBRTZNLE9BQU8sQ0FBQzdNLEtBQUs7UUFDcEJDLEdBQUcsRUFBRTRNLE9BQU8sQ0FBQzVNLEdBQUc7UUFDaEI2RCxLQUFLLEVBQUUsWUFBWTtRQUNuQjhJLFFBQVEsRUFBRTtNQUNaLENBQUMsQ0FBQztJQUNKOztJQUVBO0lBQ0EsS0FBSyxNQUFNRSxPQUFPLElBQUk3Qix1QkFBdUIsRUFBRTtNQUM3Q0csVUFBVSxDQUFDYSxJQUFJLENBQUM7UUFDZGpNLEtBQUssRUFBRThNLE9BQU8sQ0FBQzlNLEtBQUs7UUFDcEJDLEdBQUcsRUFBRTZNLE9BQU8sQ0FBQzdNLEdBQUc7UUFDaEI2RCxLQUFLLEVBQUVnSixPQUFPLENBQUMzQixVQUFVO1FBQ3pCeUIsUUFBUSxFQUFFO01BQ1osQ0FBQyxDQUFDO0lBQ0o7O0lBRUE7SUFDQSxJQUFJN00saUJBQWlCLEVBQUU7TUFDckJxTCxVQUFVLENBQUNhLElBQUksQ0FBQztRQUNkak0sS0FBSyxFQUFFRCxpQkFBaUIsQ0FBQ0MsS0FBSztRQUM5QkMsR0FBRyxFQUFFRixpQkFBaUIsQ0FBQ0UsR0FBRztRQUMxQjZELEtBQUssRUFBRTlCLFNBQVM7UUFDaEIrSyxRQUFRLEVBQUUsSUFBSTtRQUNkSCxRQUFRLEVBQUU7TUFDWixDQUFDLENBQUM7SUFDSjs7SUFFQTtJQUNBLElBQUl4VCxtQkFBbUIsQ0FBQyxDQUFDLEVBQUU7TUFDekIsS0FBSyxNQUFNeVQsT0FBTyxJQUFJNUMsYUFBYSxFQUFFO1FBQ25DLEtBQUssSUFBSStDLENBQUMsR0FBR0gsT0FBTyxDQUFDN00sS0FBSyxFQUFFZ04sQ0FBQyxHQUFHSCxPQUFPLENBQUM1TSxHQUFHLEVBQUUrTSxDQUFDLEVBQUUsRUFBRTtVQUNoRDVCLFVBQVUsQ0FBQ2EsSUFBSSxDQUFDO1lBQ2RqTSxLQUFLLEVBQUVnTixDQUFDO1lBQ1IvTSxHQUFHLEVBQUUrTSxDQUFDLEdBQUcsQ0FBQztZQUNWbEosS0FBSyxFQUFFM0ssZUFBZSxDQUFDNlQsQ0FBQyxHQUFHSCxPQUFPLENBQUM3TSxLQUFLLENBQUM7WUFDekNpTixZQUFZLEVBQUU5VCxlQUFlLENBQUM2VCxDQUFDLEdBQUdILE9BQU8sQ0FBQzdNLEtBQUssRUFBRSxJQUFJLENBQUM7WUFDdEQ0TSxRQUFRLEVBQUU7VUFDWixDQUFDLENBQUM7UUFDSjtNQUNGO0lBQ0Y7O0lBRUE7SUFDQSxJQUFJcmMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFO01BQ3hCLEtBQUssTUFBTXNjLE9BQU8sSUFBSXpDLGlCQUFpQixFQUFFO1FBQ3ZDLEtBQUssSUFBSTRDLENBQUMsR0FBR0gsT0FBTyxDQUFDN00sS0FBSyxFQUFFZ04sQ0FBQyxHQUFHSCxPQUFPLENBQUM1TSxHQUFHLEVBQUUrTSxDQUFDLEVBQUUsRUFBRTtVQUNoRDVCLFVBQVUsQ0FBQ2EsSUFBSSxDQUFDO1lBQ2RqTSxLQUFLLEVBQUVnTixDQUFDO1lBQ1IvTSxHQUFHLEVBQUUrTSxDQUFDLEdBQUcsQ0FBQztZQUNWbEosS0FBSyxFQUFFM0ssZUFBZSxDQUFDNlQsQ0FBQyxHQUFHSCxPQUFPLENBQUM3TSxLQUFLLENBQUM7WUFDekNpTixZQUFZLEVBQUU5VCxlQUFlLENBQUM2VCxDQUFDLEdBQUdILE9BQU8sQ0FBQzdNLEtBQUssRUFBRSxJQUFJLENBQUM7WUFDdEQ0TSxRQUFRLEVBQUU7VUFDWixDQUFDLENBQUM7UUFDSjtNQUNGO0lBQ0Y7O0lBRUE7SUFDQSxLQUFLLE1BQU1DLE9BQU8sSUFBSXhDLG1CQUFtQixFQUFFO01BQ3pDLEtBQUssSUFBSTJDLENBQUMsR0FBR0gsT0FBTyxDQUFDN00sS0FBSyxFQUFFZ04sQ0FBQyxHQUFHSCxPQUFPLENBQUM1TSxHQUFHLEVBQUUrTSxDQUFDLEVBQUUsRUFBRTtRQUNoRDVCLFVBQVUsQ0FBQ2EsSUFBSSxDQUFDO1VBQ2RqTSxLQUFLLEVBQUVnTixDQUFDO1VBQ1IvTSxHQUFHLEVBQUUrTSxDQUFDLEdBQUcsQ0FBQztVQUNWbEosS0FBSyxFQUFFM0ssZUFBZSxDQUFDNlQsQ0FBQyxHQUFHSCxPQUFPLENBQUM3TSxLQUFLLENBQUM7VUFDekNpTixZQUFZLEVBQUU5VCxlQUFlLENBQUM2VCxDQUFDLEdBQUdILE9BQU8sQ0FBQzdNLEtBQUssRUFBRSxJQUFJLENBQUM7VUFDdEQ0TSxRQUFRLEVBQUU7UUFDWixDQUFDLENBQUM7TUFDSjtJQUNGOztJQUVBO0lBQ0EsS0FBSyxNQUFNQyxPQUFPLElBQUl0QyxhQUFhLEVBQUU7TUFDbkMsS0FBSyxJQUFJeUMsQ0FBQyxHQUFHSCxPQUFPLENBQUM3TSxLQUFLLEVBQUVnTixDQUFDLEdBQUdILE9BQU8sQ0FBQzVNLEdBQUcsRUFBRStNLENBQUMsRUFBRSxFQUFFO1FBQ2hENUIsVUFBVSxDQUFDYSxJQUFJLENBQUM7VUFDZGpNLEtBQUssRUFBRWdOLENBQUM7VUFDUi9NLEdBQUcsRUFBRStNLENBQUMsR0FBRyxDQUFDO1VBQ1ZsSixLQUFLLEVBQUUzSyxlQUFlLENBQUM2VCxDQUFDLEdBQUdILE9BQU8sQ0FBQzdNLEtBQUssQ0FBQztVQUN6Q2lOLFlBQVksRUFBRTlULGVBQWUsQ0FBQzZULENBQUMsR0FBR0gsT0FBTyxDQUFDN00sS0FBSyxFQUFFLElBQUksQ0FBQztVQUN0RDRNLFFBQVEsRUFBRTtRQUNaLENBQUMsQ0FBQztNQUNKO0lBQ0Y7SUFFQSxPQUFPeEIsVUFBVTtFQUNuQixDQUFDLEVBQUUsQ0FDRG5NLGtCQUFrQixFQUNsQm1GLFlBQVksRUFDWkUsWUFBWSxFQUNaQyxrQkFBa0IsRUFDbEJ6SCxZQUFZLEVBQ1p3TixXQUFXLEVBQ1g0QixpQkFBaUIsRUFDakJqQix1QkFBdUIsRUFDdkJULG9CQUFvQixFQUNwQkksbUJBQW1CLEVBQ25CRSxvQkFBb0IsRUFDcEJkLGNBQWMsRUFDZGpLLGlCQUFpQixFQUNqQmtLLGFBQWEsRUFDYkcsaUJBQWlCLEVBQ2pCQyxtQkFBbUIsRUFDbkJFLGFBQWEsQ0FDZCxDQUFDO0VBRUYsTUFBTTtJQUFFMkMsZUFBZTtJQUFFQztFQUFtQixDQUFDLEdBQUdsYyxnQkFBZ0IsQ0FBQyxDQUFDOztFQUVsRTtFQUNBTCxTQUFTLENBQUMsTUFBTTtJQUNkLElBQUlxWixhQUFhLENBQUNuSixNQUFNLElBQUkxSCxtQkFBbUIsQ0FBQyxDQUFDLEVBQUU7TUFDakQ4VCxlQUFlLENBQUM7UUFDZHRNLEdBQUcsRUFBRSxtQkFBbUI7UUFDeEIvRCxJQUFJLEVBQUUsa0NBQWtDO1FBQ3hDK1AsUUFBUSxFQUFFLFdBQVc7UUFDckJRLFNBQVMsRUFBRTtNQUNiLENBQUMsQ0FBQztJQUNKLENBQUMsTUFBTTtNQUNMRCxrQkFBa0IsQ0FBQyxtQkFBbUIsQ0FBQztJQUN6QztFQUNGLENBQUMsRUFBRSxDQUFDRCxlQUFlLEVBQUVDLGtCQUFrQixFQUFFbEQsYUFBYSxDQUFDbkosTUFBTSxDQUFDLENBQUM7RUFFL0RsUSxTQUFTLENBQUMsTUFBTTtJQUNkLElBQUlMLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSTZaLGlCQUFpQixDQUFDdEosTUFBTSxFQUFFO01BQ3BEb00sZUFBZSxDQUFDO1FBQ2R0TSxHQUFHLEVBQUUsa0JBQWtCO1FBQ3ZCL0QsSUFBSSxFQUFFLHdFQUF3RTtRQUM5RStQLFFBQVEsRUFBRSxXQUFXO1FBQ3JCUSxTQUFTLEVBQUU7TUFDYixDQUFDLENBQUM7SUFDSixDQUFDLE1BQU07TUFDTEQsa0JBQWtCLENBQUMsa0JBQWtCLENBQUM7SUFDeEM7RUFDRixDQUFDLEVBQUUsQ0FBQ0QsZUFBZSxFQUFFQyxrQkFBa0IsRUFBRS9DLGlCQUFpQixDQUFDdEosTUFBTSxDQUFDLENBQUM7RUFFbkVsUSxTQUFTLENBQUMsTUFBTTtJQUNkLElBQUl3QixvQkFBb0IsQ0FBQyxDQUFDLElBQUlpWSxtQkFBbUIsQ0FBQ3ZKLE1BQU0sRUFBRTtNQUN4RG9NLGVBQWUsQ0FBQztRQUNkdE0sR0FBRyxFQUFFLG9CQUFvQjtRQUN6Qi9ELElBQUksRUFBRSw2RUFBNkU7UUFDbkYrUCxRQUFRLEVBQUUsV0FBVztRQUNyQlEsU0FBUyxFQUFFO01BQ2IsQ0FBQyxDQUFDO0lBQ0o7RUFDRixDQUFDLEVBQUUsQ0FBQ0YsZUFBZSxFQUFFN0MsbUJBQW1CLENBQUN2SixNQUFNLENBQUMsQ0FBQzs7RUFFakQ7RUFDQSxNQUFNdU0sa0JBQWtCLEdBQUd2YyxNQUFNLENBQUN5TCxLQUFLLENBQUN1RSxNQUFNLENBQUM7RUFDL0MsTUFBTXdNLGtCQUFrQixHQUFHeGMsTUFBTSxDQUFDeUwsS0FBSyxDQUFDdUUsTUFBTSxDQUFDOztFQUUvQztFQUNBLE1BQU15TSxnQkFBZ0IsR0FBRzVjLFdBQVcsQ0FBQyxNQUFNO0lBQ3pDd2Msa0JBQWtCLENBQUMsWUFBWSxDQUFDO0VBQ2xDLENBQUMsRUFBRSxDQUFDQSxrQkFBa0IsQ0FBQyxDQUFDOztFQUV4QjtFQUNBdmMsU0FBUyxDQUFDLE1BQU07SUFDZCxNQUFNNGMsVUFBVSxHQUFHSCxrQkFBa0IsQ0FBQ3JNLE9BQU87SUFDN0MsTUFBTXlNLFVBQVUsR0FBR0gsa0JBQWtCLENBQUN0TSxPQUFPO0lBQzdDLE1BQU0wTSxhQUFhLEdBQUduUixLQUFLLENBQUN1RSxNQUFNO0lBQ2xDdU0sa0JBQWtCLENBQUNyTSxPQUFPLEdBQUcwTSxhQUFhOztJQUUxQztJQUNBLElBQUlBLGFBQWEsR0FBR0QsVUFBVSxFQUFFO01BQzlCSCxrQkFBa0IsQ0FBQ3RNLE9BQU8sR0FBRzBNLGFBQWE7TUFDMUM7SUFDRjs7SUFFQTtJQUNBLElBQUlBLGFBQWEsS0FBSyxDQUFDLEVBQUU7TUFDdkJKLGtCQUFrQixDQUFDdE0sT0FBTyxHQUFHLENBQUM7TUFDOUI7SUFDRjs7SUFFQTtJQUNBO0lBQ0EsTUFBTTJNLHVCQUF1QixHQUFHRixVQUFVLElBQUksRUFBRSxJQUFJQyxhQUFhLElBQUksQ0FBQztJQUN0RSxNQUFNRSxhQUFhLEdBQUdKLFVBQVUsSUFBSSxFQUFFLElBQUlFLGFBQWEsSUFBSSxDQUFDO0lBRTVELElBQUlDLHVCQUF1QixJQUFJLENBQUNDLGFBQWEsRUFBRTtNQUM3QyxNQUFNQyxNQUFNLEdBQUc1WCxlQUFlLENBQUMsQ0FBQztNQUNoQyxJQUFJLENBQUM0WCxNQUFNLENBQUNDLFlBQVksRUFBRTtRQUN4QlosZUFBZSxDQUFDO1VBQ2R0TSxHQUFHLEVBQUUsWUFBWTtVQUNqQm1OLEdBQUcsRUFDRCxDQUFDLElBQUksQ0FBQyxRQUFRO0FBQzFCLGtCQUFrQixDQUFDLEdBQUc7QUFDdEIsY0FBYyxDQUFDLHdCQUF3QixDQUN2QixNQUFNLENBQUMsWUFBWSxDQUNuQixPQUFPLENBQUMsTUFBTSxDQUNkLFFBQVEsQ0FBQyxRQUFRLENBQ2pCLFdBQVcsQ0FBQyxPQUFPO0FBRW5DLFlBQVksRUFBRSxJQUFJLENBQ1A7VUFDRG5CLFFBQVEsRUFBRSxXQUFXO1VBQ3JCUSxTQUFTLEVBQUV6UztRQUNiLENBQUMsQ0FBQztNQUNKO01BQ0EyUyxrQkFBa0IsQ0FBQ3RNLE9BQU8sR0FBRzBNLGFBQWE7SUFDNUM7RUFDRixDQUFDLEVBQUUsQ0FBQ25SLEtBQUssQ0FBQ3VFLE1BQU0sRUFBRW9NLGVBQWUsQ0FBQyxDQUFDOztFQUVuQztFQUNBLE1BQU07SUFBRWMsWUFBWTtJQUFFQyxJQUFJO0lBQUVDLE9BQU87SUFBRUM7RUFBWSxDQUFDLEdBQUcvYSxjQUFjLENBQUM7SUFDbEVnYixhQUFhLEVBQUUsRUFBRTtJQUNqQkMsVUFBVSxFQUFFO0VBQ2QsQ0FBQyxDQUFDO0VBRUZuVCxxQkFBcUIsQ0FBQztJQUNwQnFCLEtBQUs7SUFDTFEsY0FBYztJQUNkUCxhQUFhLEVBQUV5RSxnQkFBZ0I7SUFDL0JKLGVBQWU7SUFDZnZEO0VBQ0YsQ0FBQyxDQUFDO0VBRUYsTUFBTWdSLGtCQUFrQixHQUFHblQseUJBQXlCLENBQUM7SUFDbkRvQixLQUFLO0lBQ0xXLFdBQVc7SUFDWHdHO0VBQ0YsQ0FBQyxDQUFDO0VBRUYsTUFBTTZLLFFBQVEsR0FBRzVkLFdBQVcsQ0FDMUIsQ0FBQzhMLEtBQUssRUFBRSxNQUFNLEtBQUs7SUFDakIsSUFBSUEsS0FBSyxLQUFLLEdBQUcsRUFBRTtNQUNqQm5MLFFBQVEsQ0FBQyxvQkFBb0IsRUFBRSxDQUFDLENBQUMsQ0FBQztNQUNsQ2lPLFdBQVcsQ0FBQzhGLENBQUMsSUFBSSxDQUFDQSxDQUFDLENBQUM7TUFDcEI7SUFDRjtJQUNBOUYsV0FBVyxDQUFDLEtBQUssQ0FBQzs7SUFFbEI7SUFDQWdPLGdCQUFnQixDQUFDLENBQUM7O0lBRWxCO0lBQ0FsWixxQkFBcUIsQ0FBQyxDQUFDO0lBQ3ZCRyxnQkFBZ0IsQ0FBQ2lLLFdBQVcsQ0FBQzs7SUFFN0I7SUFDQSxNQUFNK1AscUJBQXFCLEdBQUcvUixLQUFLLENBQUNxRSxNQUFNLEtBQUt2RSxLQUFLLENBQUN1RSxNQUFNLEdBQUcsQ0FBQztJQUMvRCxNQUFNMk4sZUFBZSxHQUFHM1IsWUFBWSxLQUFLLENBQUM7SUFDMUMsTUFBTUosSUFBSSxHQUFHakMsZ0JBQWdCLENBQUNnQyxLQUFLLENBQUM7SUFFcEMsSUFBSWdTLGVBQWUsSUFBSS9SLElBQUksS0FBSyxRQUFRLEVBQUU7TUFDeEMsSUFBSThSLHFCQUFxQixFQUFFO1FBQ3pCN1IsWUFBWSxDQUFDRCxJQUFJLENBQUM7UUFDbEI7TUFDRjtNQUNBO01BQ0EsSUFBSUgsS0FBSyxDQUFDdUUsTUFBTSxLQUFLLENBQUMsRUFBRTtRQUN0Qm5FLFlBQVksQ0FBQ0QsSUFBSSxDQUFDO1FBQ2xCLE1BQU1nUyxnQkFBZ0IsR0FBR2hVLGlCQUFpQixDQUFDK0IsS0FBSyxDQUFDLENBQUNrUyxVQUFVLENBQzFELElBQUksRUFDSixNQUNGLENBQUM7UUFDRFgsWUFBWSxDQUFDelIsS0FBSyxFQUFFTyxZQUFZLEVBQUVDLGNBQWMsQ0FBQztRQUNqRGtFLGdCQUFnQixDQUFDeU4sZ0JBQWdCLENBQUM7UUFDbEM3TixlQUFlLENBQUM2TixnQkFBZ0IsQ0FBQzVOLE1BQU0sQ0FBQztRQUN4QztNQUNGO0lBQ0Y7SUFFQSxNQUFNOE4sY0FBYyxHQUFHblMsS0FBSyxDQUFDa1MsVUFBVSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUM7O0lBRXJEO0lBQ0EsSUFBSXBTLEtBQUssS0FBS3FTLGNBQWMsRUFBRTtNQUM1QlosWUFBWSxDQUFDelIsS0FBSyxFQUFFTyxZQUFZLEVBQUVDLGNBQWMsQ0FBQztJQUNuRDs7SUFFQTtJQUNBMEIsV0FBVyxDQUFDRSxJQUFJLElBQ2RBLElBQUksQ0FBQ2tLLGVBQWUsS0FBSyxJQUFJLEdBQ3pCbEssSUFBSSxHQUNKO01BQUUsR0FBR0EsSUFBSTtNQUFFa0ssZUFBZSxFQUFFO0lBQUssQ0FDdkMsQ0FBQztJQUVENUgsZ0JBQWdCLENBQUMyTixjQUFjLENBQUM7RUFDbEMsQ0FBQyxFQUNELENBQ0UzTixnQkFBZ0IsRUFDaEJ0RSxZQUFZLEVBQ1pKLEtBQUssRUFDTE8sWUFBWSxFQUNaa1IsWUFBWSxFQUNaalIsY0FBYyxFQUNkd1EsZ0JBQWdCLEVBQ2hCOU8sV0FBVyxDQUVmLENBQUM7RUFFRCxNQUFNO0lBQ0pvUSxZQUFZO0lBQ1pDLFdBQVc7SUFDWEMsYUFBYTtJQUNiQyxpQkFBaUI7SUFDakJDO0VBQ0YsQ0FBQyxHQUFHamMsa0JBQWtCLENBQ3BCLENBQ0V5SixLQUFLLEVBQUUsTUFBTSxFQUNieVMsV0FBVyxFQUFFbmMsV0FBVyxFQUN4QmdLLGNBQWMsRUFBRUMsTUFBTSxDQUFDLE1BQU0sRUFBRTlHLGFBQWEsQ0FBQyxLQUMxQztJQUNIcVksUUFBUSxDQUFDOVIsS0FBSyxDQUFDO0lBQ2ZFLFlBQVksQ0FBQ3VTLFdBQVcsQ0FBQztJQUN6QjVSLGlCQUFpQixDQUFDUCxjQUFjLENBQUM7RUFDbkMsQ0FBQyxFQUNEUixLQUFLLEVBQ0xRLGNBQWMsRUFDZDhELGVBQWUsRUFDZm5FLElBQ0YsQ0FBQzs7RUFFRDtFQUNBOUwsU0FBUyxDQUFDLE1BQU07SUFDZCxJQUFJcU8sa0JBQWtCLEVBQUU7TUFDdEIrUCxpQkFBaUIsQ0FBQyxDQUFDO0lBQ3JCO0VBQ0YsQ0FBQyxFQUFFLENBQUMvUCxrQkFBa0IsRUFBRStQLGlCQUFpQixDQUFDLENBQUM7O0VBRTNDO0VBQ0E7RUFDQTtFQUNBLFNBQVNHLGVBQWVBLENBQUEsRUFBRztJQUN6QixJQUFJQyxXQUFXLENBQUN0TyxNQUFNLEdBQUcsQ0FBQyxFQUFFO01BQzFCO0lBQ0Y7O0lBRUE7SUFDQTtJQUNBO0lBQ0EsSUFBSSxDQUFDeUcsbUJBQW1CLEVBQUU7TUFDeEI7SUFDRjs7SUFFQTtJQUNBLE1BQU04SCxrQkFBa0IsR0FBR2pOLGNBQWMsQ0FBQ3VELElBQUksQ0FBQzlULHVCQUF1QixDQUFDO0lBQ3ZFLElBQUl3ZCxrQkFBa0IsRUFBRTtNQUN0QixLQUFLQyx1QkFBdUIsQ0FBQyxDQUFDO01BQzlCO0lBQ0Y7SUFFQVIsV0FBVyxDQUFDLENBQUM7RUFDZjtFQUVBLFNBQVNTLGlCQUFpQkEsQ0FBQSxFQUFHO0lBQzNCLElBQUlILFdBQVcsQ0FBQ3RPLE1BQU0sR0FBRyxDQUFDLEVBQUU7TUFDMUI7SUFDRjs7SUFFQTtJQUNBO0lBQ0E7SUFDQSxJQUFJLENBQUM0RyxrQkFBa0IsRUFBRTtNQUN2QjtJQUNGOztJQUVBO0lBQ0EsSUFBSXFILGFBQWEsQ0FBQyxDQUFDLElBQUl0RyxXQUFXLENBQUMzSCxNQUFNLEdBQUcsQ0FBQyxFQUFFO01BQzdDLE1BQU0wTyxLQUFLLEdBQUcvRyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7TUFDN0JXLGdCQUFnQixDQUFDb0csS0FBSyxDQUFDO01BQ3ZCLElBQUlBLEtBQUssS0FBSyxPQUFPLElBQUksQ0FBQ3ZaLGVBQWUsQ0FBQyxDQUFDLENBQUN3WixnQkFBZ0IsRUFBRTtRQUM1RHRaLGdCQUFnQixDQUFDdVosQ0FBQyxJQUNoQkEsQ0FBQyxDQUFDRCxnQkFBZ0IsR0FBR0MsQ0FBQyxHQUFHO1VBQUUsR0FBR0EsQ0FBQztVQUFFRCxnQkFBZ0IsRUFBRTtRQUFLLENBQzFELENBQUM7TUFDSDtJQUNGO0VBQ0Y7O0VBRUE7RUFDQSxNQUFNLENBQUNFLGdCQUFnQixFQUFFQyxzQkFBc0IsQ0FBQyxHQUFHN2UsUUFBUSxDQUFDO0lBQzFEcWUsV0FBVyxFQUFFdFUsY0FBYyxFQUFFO0lBQzdCK1Usa0JBQWtCLEVBQUUsTUFBTTtJQUMxQkMsbUJBQW1CLENBQUMsRUFBRSxNQUFNO0VBQzlCLENBQUMsQ0FBQyxDQUFDO0lBQ0RWLFdBQVcsRUFBRSxFQUFFO0lBQ2ZTLGtCQUFrQixFQUFFLENBQUMsQ0FBQztJQUN0QkMsbUJBQW1CLEVBQUU5TjtFQUN2QixDQUFDLENBQUM7O0VBRUY7RUFDQSxNQUFNK04sbUJBQW1CLEdBQUdwZixXQUFXLENBQ3JDLENBQ0VxZixPQUFPLEVBQ0gsT0FBT0wsZ0JBQWdCLEdBQ3ZCLENBQUMsQ0FBQ2hSLElBQUksRUFBRSxPQUFPZ1IsZ0JBQWdCLEVBQUUsR0FBRyxPQUFPQSxnQkFBZ0IsQ0FBQyxLQUM3RDtJQUNIQyxzQkFBc0IsQ0FBQ2pSLElBQUksSUFDekIsT0FBT3FSLE9BQU8sS0FBSyxVQUFVLEdBQUdBLE9BQU8sQ0FBQ3JSLElBQUksQ0FBQyxHQUFHcVIsT0FDbEQsQ0FBQztFQUNILENBQUMsRUFDRCxFQUNGLENBQUM7RUFFRCxNQUFNNVIsUUFBUSxHQUFHek4sV0FBVyxDQUMxQixPQUFPc2YsVUFBVSxFQUFFLE1BQU0sRUFBRUMsd0JBQXdCLEdBQUcsS0FBSyxLQUFLO0lBQzlERCxVQUFVLEdBQUdBLFVBQVUsQ0FBQ0UsT0FBTyxDQUFDLENBQUM7O0lBRWpDO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQSxNQUFNNVIsS0FBSyxHQUFHZ0QsS0FBSyxDQUFDa0MsUUFBUSxDQUFDLENBQUM7SUFDOUIsSUFDRWxGLEtBQUssQ0FBQ3NLLGVBQWUsSUFDckJKLFdBQVcsQ0FBQzFFLFFBQVEsQ0FBQ3hGLEtBQUssQ0FBQ3NLLGVBQWUsQ0FBQyxFQUMzQztNQUNBO0lBQ0Y7O0lBRUE7SUFDQTtJQUNBO0lBQ0EsSUFBSXRLLEtBQUssQ0FBQ2tFLGlCQUFpQixLQUFLLGlCQUFpQixFQUFFO01BQ2pEO0lBQ0Y7O0lBRUE7SUFDQSxNQUFNMk4sU0FBUyxHQUFHM0ssTUFBTSxDQUFDQyxNQUFNLENBQUMzSSxjQUFjLENBQUMsQ0FBQzRJLElBQUksQ0FDbEQrSixDQUFDLElBQUlBLENBQUMsQ0FBQ1csSUFBSSxLQUFLLE9BQ2xCLENBQUM7O0lBRUQ7SUFDQTtJQUNBO0lBQ0E7SUFDQSxNQUFNQyxjQUFjLEdBQUdqTyxxQkFBcUIsQ0FBQ3hGLElBQUk7SUFDakQsTUFBTTBULHNCQUFzQixHQUMxQk4sVUFBVSxDQUFDTyxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSVAsVUFBVSxLQUFLSyxjQUFjO0lBQzNELElBQ0VDLHNCQUFzQixJQUN0QkQsY0FBYyxJQUNkLENBQUNGLFNBQVMsSUFDVixDQUFDN1IsS0FBSyxDQUFDaUUsa0JBQWtCLEVBQ3pCO01BQ0E7TUFDQSxJQUFJRCxXQUFXLENBQUMrRixNQUFNLEtBQUssUUFBUSxFQUFFO1FBQ25DcUIsWUFBWSxDQUFDLENBQUM7UUFDZDtRQUNBQyxzQkFBc0IsQ0FBQzBHLGNBQWMsRUFBRTtVQUFFRyxTQUFTLEVBQUU7UUFBSyxDQUFDLENBQUM7UUFFM0QsS0FBS3BRLFlBQVksQ0FDZmlRLGNBQWMsRUFDZDtVQUNFelAsZUFBZTtVQUNmc04sV0FBVztVQUNYVTtRQUNGLENBQUMsRUFDRDtVQUNFdFEsS0FBSyxFQUFFZ0UsV0FBVztVQUNsQi9ELDZCQUE2QixFQUFFQSw2QkFBNkI7VUFDNURDO1FBQ0YsQ0FDRixDQUFDO1FBQ0QsT0FBTSxDQUFDO01BQ1Q7O01BRUE7TUFDQSxJQUFJNEQscUJBQXFCLENBQUNxTyxPQUFPLEdBQUcsQ0FBQyxFQUFFO1FBQ3JDL0csWUFBWSxDQUFDLENBQUM7UUFDZHNHLFVBQVUsR0FBR0ssY0FBYztNQUM3QjtJQUNGOztJQUVBO0lBQ0EsSUFBSXphLG9CQUFvQixDQUFDLENBQUMsRUFBRTtNQUMxQixNQUFNOGEsYUFBYSxHQUFHdGEsd0JBQXdCLENBQUM0WixVQUFVLENBQUM7TUFDMUQsSUFBSVUsYUFBYSxFQUFFO1FBQ2pCLE1BQU10VSxNQUFNLEdBQUcsTUFBTS9GLHVCQUF1QixDQUMxQ3FhLGFBQWEsQ0FBQ0MsYUFBYSxFQUMzQkQsYUFBYSxDQUFDRSxPQUFPLEVBQ3JCMU8sV0FBVyxFQUNYcEosY0FDRixDQUFDO1FBRUQsSUFBSXNELE1BQU0sQ0FBQ3lVLE9BQU8sRUFBRTtVQUNsQjVELGVBQWUsQ0FBQztZQUNkdE0sR0FBRyxFQUFFLHFCQUFxQjtZQUMxQi9ELElBQUksRUFBRSxZQUFZUixNQUFNLENBQUN1VSxhQUFhLEVBQUU7WUFDeENoRSxRQUFRLEVBQUUsV0FBVztZQUNyQlEsU0FBUyxFQUFFO1VBQ2IsQ0FBQyxDQUFDO1VBQ0ZuTSxnQkFBZ0IsQ0FBQyxFQUFFLENBQUM7VUFDcEJKLGVBQWUsQ0FBQyxDQUFDLENBQUM7VUFDbEJzTixXQUFXLENBQUMsQ0FBQztVQUNiVSxZQUFZLENBQUMsQ0FBQztVQUNkO1FBQ0YsQ0FBQyxNQUFNLElBQUl4UyxNQUFNLENBQUMwVSxLQUFLLEtBQUssaUJBQWlCLEVBQUU7VUFDN0M7UUFBQSxDQUNELE1BQU07VUFDTDtVQUNBO1FBQUE7TUFFSjtJQUNGOztJQUVBO0lBQ0EsSUFBSWQsVUFBVSxDQUFDTyxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDSixTQUFTLEVBQUU7TUFDMUM7SUFDRjs7SUFFQTtJQUNBO0lBQ0EsTUFBTVksdUJBQXVCLEdBQzNCckIsZ0JBQWdCLENBQUNQLFdBQVcsQ0FBQ3RPLE1BQU0sR0FBRyxDQUFDLElBQ3ZDNk8sZ0JBQWdCLENBQUNQLFdBQVcsQ0FBQzZCLEtBQUssQ0FBQ3hQLENBQUMsSUFBSUEsQ0FBQyxDQUFDeVAsV0FBVyxLQUFLLFdBQVcsQ0FBQztJQUV4RSxJQUNFdkIsZ0JBQWdCLENBQUNQLFdBQVcsQ0FBQ3RPLE1BQU0sR0FBRyxDQUFDLElBQ3ZDLENBQUNvUCx3QkFBd0IsSUFDekIsQ0FBQ2MsdUJBQXVCLEVBQ3hCO01BQ0E1YSxlQUFlLENBQ2IsdURBQXVEdVosZ0JBQWdCLENBQUNQLFdBQVcsQ0FBQ3RPLE1BQU0sR0FDNUYsQ0FBQztNQUNELE9BQU0sQ0FBQztJQUNUOztJQUVBO0lBQ0EsSUFBSXVCLHFCQUFxQixDQUFDeEYsSUFBSSxJQUFJd0YscUJBQXFCLENBQUNxTyxPQUFPLEdBQUcsQ0FBQyxFQUFFO01BQ25FOUcsc0JBQXNCLENBQUNxRyxVQUFVLENBQUM7SUFDcEM7O0lBRUE7SUFDQTlDLGtCQUFrQixDQUFDLFlBQVksQ0FBQzs7SUFFaEM7SUFDQSxNQUFNZ0UsV0FBVyxHQUFHMWMsc0JBQXNCLENBQUM4TSxLQUFLLENBQUNrQyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQzVELElBQUkwTixXQUFXLENBQUNkLElBQUksS0FBSyxRQUFRLElBQUl0UixhQUFhLEVBQUU7TUFDbER6TixRQUFRLENBQUMsb0NBQW9DLEVBQUUsQ0FBQyxDQUFDLENBQUM7TUFDbEQsTUFBTXlOLGFBQWEsQ0FBQ2tSLFVBQVUsRUFBRWtCLFdBQVcsQ0FBQ25TLElBQUksRUFBRTtRQUNoRDZCLGVBQWU7UUFDZnNOLFdBQVc7UUFDWFU7TUFDRixDQUFDLENBQUM7TUFDRjtJQUNGOztJQUVBO0lBQ0EsTUFBTXhPLFlBQVksQ0FBQzRQLFVBQVUsRUFBRTtNQUM3QnBQLGVBQWU7TUFDZnNOLFdBQVc7TUFDWFU7SUFDRixDQUFDLENBQUM7RUFDSixDQUFDLEVBQ0QsQ0FDRXhNLHFCQUFxQixFQUNyQkUsV0FBVyxFQUNYL0QsNkJBQTZCLEVBQzdCMkQsV0FBVyxFQUNYWixLQUFLLEVBQ0xrSCxXQUFXLEVBQ1hrSCxnQkFBZ0IsQ0FBQ1AsV0FBVyxFQUM1Qi9PLFlBQVksRUFDWnRCLGFBQWEsRUFDYm9QLFdBQVcsRUFDWFUsWUFBWSxFQUNaakYsc0JBQXNCLEVBQ3RCbkwsV0FBVyxFQUNYa0wsWUFBWSxFQUNaNU0sY0FBYyxFQUNkb1Esa0JBQWtCLENBRXRCLENBQUM7RUFFRCxNQUFNO0lBQ0ppQyxXQUFXO0lBQ1hTLGtCQUFrQjtJQUNsQkMsbUJBQW1CO0lBQ25Cc0IsZUFBZTtJQUNmQztFQUNGLENBQUMsR0FBRzdkLFlBQVksQ0FBQztJQUNmdUksUUFBUTtJQUNSUyxhQUFhLEVBQUV5RSxnQkFBZ0I7SUFDL0I3QyxRQUFRO0lBQ1J5QyxlQUFlO0lBQ2Z0RSxLQUFLO0lBQ0xPLFlBQVk7SUFDWkosSUFBSTtJQUNKVixNQUFNO0lBQ04rVCxtQkFBbUI7SUFDbkJKLGdCQUFnQjtJQUNoQjJCLG1CQUFtQixFQUFFclMsa0JBQWtCLElBQUlnUSxZQUFZLEdBQUcsQ0FBQztJQUMzRHRGLFlBQVk7SUFDWmhOO0VBQ0YsQ0FBQyxDQUFDOztFQUVGO0VBQ0E7RUFDQSxNQUFNNFUsb0JBQW9CLEdBQ3hCN1UsSUFBSSxLQUFLLFFBQVEsSUFDakIwUyxXQUFXLENBQUN0TyxNQUFNLEtBQUssQ0FBQyxJQUN4QndCLGdCQUFnQixJQUNoQixDQUFDRSxrQkFBa0I7RUFDckIsSUFBSStPLG9CQUFvQixFQUFFO0lBQ3hCMUgsU0FBUyxDQUFDLENBQUM7RUFDYjs7RUFFQTtFQUNBO0VBQ0E7RUFDQSxJQUNFeEgscUJBQXFCLENBQUN4RixJQUFJLElBQzFCLENBQUN5RixnQkFBZ0IsSUFDakJELHFCQUFxQixDQUFDcU8sT0FBTyxLQUFLLENBQUMsSUFDbkMsQ0FBQ2xPLGtCQUFrQixFQUNuQjtJQUNBbE8sdUJBQXVCLENBQUMsUUFBUSxFQUFFK04scUJBQXFCLENBQUN4RixJQUFJLENBQUM7SUFDN0Q0QixXQUFXLENBQUNFLElBQUksS0FBSztNQUNuQixHQUFHQSxJQUFJO01BQ1AyRCxnQkFBZ0IsRUFBRTtRQUNoQnpGLElBQUksRUFBRSxJQUFJO1FBQ1YyVSxRQUFRLEVBQUUsSUFBSTtRQUNkZCxPQUFPLEVBQUUsQ0FBQztRQUNWZSxVQUFVLEVBQUUsQ0FBQztRQUNiQyxtQkFBbUIsRUFBRTtNQUN2QjtJQUNGLENBQUMsQ0FBQyxDQUFDO0VBQ0w7RUFFQSxTQUFTQyxZQUFZQSxDQUNuQkMsS0FBSyxFQUFFLE1BQU0sRUFDYkMsU0FBa0IsQ0FBUixFQUFFLE1BQU0sRUFDbEJDLFFBQWlCLENBQVIsRUFBRSxNQUFNLEVBQ2pCQyxVQUE0QixDQUFqQixFQUFFM2EsZUFBZSxFQUM1QjRhLFVBQW1CLENBQVIsRUFBRSxNQUFNLEVBQ25CO0lBQ0ExZ0IsUUFBUSxDQUFDLG1CQUFtQixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQ2pDcUwsWUFBWSxDQUFDLFFBQVEsQ0FBQztJQUV0QixNQUFNc1YsT0FBTyxHQUFHdk4sY0FBYyxDQUFDMUQsT0FBTyxFQUFFO0lBRXhDLE1BQU1rUixVQUFVLEVBQUVoYyxhQUFhLEdBQUc7TUFDaENpYyxFQUFFLEVBQUVGLE9BQU87TUFDWDVCLElBQUksRUFBRSxPQUFPO01BQ2IrQixPQUFPLEVBQUVSLEtBQUs7TUFDZEMsU0FBUyxFQUFFQSxTQUFTLElBQUksV0FBVztNQUFFO01BQ3JDQyxRQUFRLEVBQUVBLFFBQVEsSUFBSSxjQUFjO01BQ3BDQyxVQUFVO01BQ1ZDO0lBQ0YsQ0FBQzs7SUFFRDtJQUNBM2EsY0FBYyxDQUFDNmEsVUFBVSxDQUFDOztJQUUxQjtJQUNBLEtBQUs1YSxVQUFVLENBQUM0YSxVQUFVLENBQUM7O0lBRTNCO0lBQ0E1VSxpQkFBaUIsQ0FBQ3FCLElBQUksS0FBSztNQUFFLEdBQUdBLElBQUk7TUFBRSxDQUFDc1QsT0FBTyxHQUFHQztJQUFXLENBQUMsQ0FBQyxDQUFDO0lBQy9EO0lBQ0E7SUFDQTtJQUNBLE1BQU1HLE1BQU0sR0FBR3pOLHdCQUF3QixDQUFDNUQsT0FBTyxHQUFHLEdBQUcsR0FBRyxFQUFFO0lBQzFEc1Isa0JBQWtCLENBQUNELE1BQU0sR0FBRzNmLGNBQWMsQ0FBQ3VmLE9BQU8sQ0FBQyxDQUFDO0lBQ3BEck4sd0JBQXdCLENBQUM1RCxPQUFPLEdBQUcsSUFBSTtFQUN6Qzs7RUFFQTtFQUNBO0VBQ0E7RUFDQTtFQUNBcFEsU0FBUyxDQUFDLE1BQU07SUFDZCxNQUFNMmhCLGFBQWEsR0FBRyxJQUFJQyxHQUFHLENBQUMzZixlQUFlLENBQUMwSixLQUFLLENBQUMsQ0FBQzhQLEdBQUcsQ0FBQ0YsQ0FBQyxJQUFJQSxDQUFDLENBQUNnRyxFQUFFLENBQUMsQ0FBQztJQUNwRTdVLGlCQUFpQixDQUFDcUIsSUFBSSxJQUFJO01BQ3hCLE1BQU04VCxRQUFRLEdBQUdoTixNQUFNLENBQUNDLE1BQU0sQ0FBQy9HLElBQUksQ0FBQyxDQUFDK0osTUFBTSxDQUN6Q2dILENBQUMsSUFBSUEsQ0FBQyxDQUFDVyxJQUFJLEtBQUssT0FBTyxJQUFJLENBQUNrQyxhQUFhLENBQUNHLEdBQUcsQ0FBQ2hELENBQUMsQ0FBQ3lDLEVBQUUsQ0FDcEQsQ0FBQztNQUNELElBQUlNLFFBQVEsQ0FBQzNSLE1BQU0sS0FBSyxDQUFDLEVBQUUsT0FBT25DLElBQUk7TUFDdEMsTUFBTTJHLElBQUksR0FBRztRQUFFLEdBQUczRztNQUFLLENBQUM7TUFDeEIsS0FBSyxNQUFNZ1UsR0FBRyxJQUFJRixRQUFRLEVBQUUsT0FBT25OLElBQUksQ0FBQ3FOLEdBQUcsQ0FBQ1IsRUFBRSxDQUFDO01BQy9DLE9BQU83TSxJQUFJO0lBQ2IsQ0FBQyxDQUFDO0VBQ0osQ0FBQyxFQUFFLENBQUMvSSxLQUFLLEVBQUVlLGlCQUFpQixDQUFDLENBQUM7RUFFOUIsU0FBU3NWLFdBQVdBLENBQUNDLE9BQU8sRUFBRSxNQUFNLEVBQUU7SUFDcENqTyx3QkFBd0IsQ0FBQzVELE9BQU8sR0FBRyxLQUFLO0lBQ3hDO0lBQ0EsSUFBSW5FLElBQUksR0FBRzlLLFNBQVMsQ0FBQzhnQixPQUFPLENBQUMsQ0FBQ0MsT0FBTyxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsQ0FBQ25FLFVBQVUsQ0FBQyxJQUFJLEVBQUUsTUFBTSxDQUFDOztJQUUzRTtJQUNBLElBQUlwUyxLQUFLLENBQUN1RSxNQUFNLEtBQUssQ0FBQyxFQUFFO01BQ3RCLE1BQU1pUyxVQUFVLEdBQUd0WSxnQkFBZ0IsQ0FBQ29DLElBQUksQ0FBQztNQUN6QyxJQUFJa1csVUFBVSxLQUFLLFFBQVEsRUFBRTtRQUMzQnBXLFlBQVksQ0FBQ29XLFVBQVUsQ0FBQztRQUN4QmxXLElBQUksR0FBR25DLGlCQUFpQixDQUFDbUMsSUFBSSxDQUFDO01BQ2hDO0lBQ0Y7SUFFQSxNQUFNbVcsUUFBUSxHQUFHcGdCLHdCQUF3QixDQUFDaUssSUFBSSxDQUFDO0lBQy9DO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQSxNQUFNb1csUUFBUSxHQUFHbk4sSUFBSSxDQUFDb04sR0FBRyxDQUFDQyxJQUFJLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQzs7SUFFdkM7SUFDQTtJQUNBLElBQUl0VyxJQUFJLENBQUNpRSxNQUFNLEdBQUczSixlQUFlLElBQUk2YixRQUFRLEdBQUdDLFFBQVEsRUFBRTtNQUN4RCxNQUFNaEIsT0FBTyxHQUFHdk4sY0FBYyxDQUFDMUQsT0FBTyxFQUFFO01BRXhDLE1BQU1rUixVQUFVLEVBQUVoYyxhQUFhLEdBQUc7UUFDaENpYyxFQUFFLEVBQUVGLE9BQU87UUFDWDVCLElBQUksRUFBRSxNQUFNO1FBQ1orQixPQUFPLEVBQUV2VjtNQUNYLENBQUM7TUFFRFMsaUJBQWlCLENBQUNxQixJQUFJLEtBQUs7UUFBRSxHQUFHQSxJQUFJO1FBQUUsQ0FBQ3NULE9BQU8sR0FBR0M7TUFBVyxDQUFDLENBQUMsQ0FBQztNQUUvREksa0JBQWtCLENBQUMzZixtQkFBbUIsQ0FBQ3NmLE9BQU8sRUFBRWUsUUFBUSxDQUFDLENBQUM7SUFDNUQsQ0FBQyxNQUFNO01BQ0w7TUFDQVYsa0JBQWtCLENBQUN6VixJQUFJLENBQUM7SUFDMUI7RUFDRjtFQUVBLE1BQU11VyxvQkFBb0IsR0FBR3ppQixXQUFXLENBQ3RDLENBQUM0TCxLQUFLLEVBQUUsTUFBTSxFQUFFcUUsR0FBRyxFQUFFL00sR0FBRyxDQUFDLEVBQUUsTUFBTSxJQUFJO0lBQ25DLElBQUksQ0FBQytRLHdCQUF3QixDQUFDNUQsT0FBTyxFQUFFLE9BQU96RSxLQUFLO0lBQ25EcUksd0JBQXdCLENBQUM1RCxPQUFPLEdBQUcsS0FBSztJQUN4QyxJQUFJMUYsbUJBQW1CLENBQUNpQixLQUFLLEVBQUVxRSxHQUFHLENBQUMsRUFBRSxPQUFPLEdBQUcsR0FBR3JFLEtBQUs7SUFDdkQsT0FBT0EsS0FBSztFQUNkLENBQUMsRUFDRCxFQUNGLENBQUM7RUFFRCxTQUFTK1Ysa0JBQWtCQSxDQUFDelYsSUFBSSxFQUFFLE1BQU0sRUFBRTtJQUN4QztJQUNBbVIsWUFBWSxDQUFDelIsS0FBSyxFQUFFTyxZQUFZLEVBQUVDLGNBQWMsQ0FBQztJQUVqRCxNQUFNc1csUUFBUSxHQUNaOVcsS0FBSyxDQUFDK0UsS0FBSyxDQUFDLENBQUMsRUFBRXhFLFlBQVksQ0FBQyxHQUFHRCxJQUFJLEdBQUdOLEtBQUssQ0FBQytFLEtBQUssQ0FBQ3hFLFlBQVksQ0FBQztJQUNqRW1FLGdCQUFnQixDQUFDb1MsUUFBUSxDQUFDO0lBQzFCeFMsZUFBZSxDQUFDL0QsWUFBWSxHQUFHRCxJQUFJLENBQUNpRSxNQUFNLENBQUM7RUFDN0M7RUFFQSxNQUFNd1MsdUJBQXVCLEdBQUdyZ0IsY0FBYyxDQUM1QyxNQUFNLENBQUMsQ0FBQyxFQUNSLE1BQU1rSyxxQkFBcUIsQ0FBQyxDQUM5QixDQUFDOztFQUVEO0VBQ0EsTUFBTW1TLHVCQUF1QixHQUFHM2UsV0FBVyxDQUFDLEVBQUUsRUFBRSxPQUFPLElBQUk7SUFDekQsTUFBTTBMLE1BQU0sR0FBR3ZLLGNBQWMsQ0FBQ3lLLEtBQUssRUFBRU8sWUFBWSxDQUFDO0lBQ2xELElBQUksQ0FBQ1QsTUFBTSxFQUFFO01BQ1gsT0FBTyxLQUFLO0lBQ2Q7SUFFQTRFLGdCQUFnQixDQUFDNUUsTUFBTSxDQUFDUSxJQUFJLENBQUM7SUFDN0JGLFlBQVksQ0FBQyxRQUFRLENBQUMsRUFBQztJQUN2QmtFLGVBQWUsQ0FBQ3hFLE1BQU0sQ0FBQ1MsWUFBWSxDQUFDOztJQUVwQztJQUNBLElBQUlULE1BQU0sQ0FBQ2tYLE1BQU0sQ0FBQ3pTLE1BQU0sR0FBRyxDQUFDLEVBQUU7TUFDNUJ4RCxpQkFBaUIsQ0FBQ3FCLElBQUksSUFBSTtRQUN4QixNQUFNNlUsV0FBVyxHQUFHO1VBQUUsR0FBRzdVO1FBQUssQ0FBQztRQUMvQixLQUFLLE1BQU1pVCxLQUFLLElBQUl2VixNQUFNLENBQUNrWCxNQUFNLEVBQUU7VUFDakNDLFdBQVcsQ0FBQzVCLEtBQUssQ0FBQ08sRUFBRSxDQUFDLEdBQUdQLEtBQUs7UUFDL0I7UUFDQSxPQUFPNEIsV0FBVztNQUNwQixDQUFDLENBQUM7SUFDSjtJQUVBLE9BQU8sSUFBSTtFQUNiLENBQUMsRUFBRSxDQUFDdlMsZ0JBQWdCLEVBQUV0RSxZQUFZLEVBQUVKLEtBQUssRUFBRU8sWUFBWSxFQUFFUSxpQkFBaUIsQ0FBQyxDQUFDOztFQUU1RTtFQUNBO0VBQ0EsTUFBTW1XLGdCQUFnQixHQUFHLFNBQUFBLENBQVVDLFdBQVcsRUFBRXZpQixjQUFjLEVBQUU7SUFDOURHLFFBQVEsQ0FBQyx3QkFBd0IsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUN0QyxJQUFJcWlCLGVBQWUsRUFBRSxNQUFNO0lBQzNCLE1BQU1DLFlBQVksR0FBR25qQixJQUFJLENBQUNvakIsUUFBUSxDQUFDamlCLE1BQU0sQ0FBQyxDQUFDLEVBQUU4aEIsV0FBVyxDQUFDSSxRQUFRLENBQUM7SUFDbEUsSUFBSUosV0FBVyxDQUFDSyxTQUFTLElBQUlMLFdBQVcsQ0FBQ00sT0FBTyxFQUFFO01BQ2hETCxlQUFlLEdBQ2JELFdBQVcsQ0FBQ0ssU0FBUyxLQUFLTCxXQUFXLENBQUNNLE9BQU8sR0FDekMsSUFBSUosWUFBWSxLQUFLRixXQUFXLENBQUNLLFNBQVMsR0FBRyxHQUM3QyxJQUFJSCxZQUFZLEtBQUtGLFdBQVcsQ0FBQ0ssU0FBUyxJQUFJTCxXQUFXLENBQUNNLE9BQU8sR0FBRztJQUM1RSxDQUFDLE1BQU07TUFDTEwsZUFBZSxHQUFHLElBQUlDLFlBQVksR0FBRztJQUN2QztJQUNBLE1BQU1LLFVBQVUsR0FBRzFYLEtBQUssQ0FBQ08sWUFBWSxHQUFHLENBQUMsQ0FBQyxJQUFJLEdBQUc7SUFDakQsSUFBSSxDQUFDLElBQUksQ0FBQ3FFLElBQUksQ0FBQzhTLFVBQVUsQ0FBQyxFQUFFO01BQzFCTixlQUFlLEdBQUcsSUFBSUEsZUFBZSxFQUFFO0lBQ3pDO0lBQ0FyQixrQkFBa0IsQ0FBQ3FCLGVBQWUsQ0FBQztFQUNyQyxDQUFDO0VBQ0R2aUIsaUJBQWlCLENBQUNpTSxVQUFVLEVBQUVvVyxnQkFBZ0IsQ0FBQzs7RUFFL0M7RUFDQSxNQUFNUyxVQUFVLEdBQUd2akIsV0FBVyxDQUFDLE1BQU07SUFDbkMsSUFBSXVkLE9BQU8sRUFBRTtNQUNYLE1BQU1pRyxhQUFhLEdBQUdsRyxJQUFJLENBQUMsQ0FBQztNQUM1QixJQUFJa0csYUFBYSxFQUFFO1FBQ2pCbFQsZ0JBQWdCLENBQUNrVCxhQUFhLENBQUN0WCxJQUFJLENBQUM7UUFDcENnRSxlQUFlLENBQUNzVCxhQUFhLENBQUNyWCxZQUFZLENBQUM7UUFDM0NRLGlCQUFpQixDQUFDNlcsYUFBYSxDQUFDcFgsY0FBYyxDQUFDO01BQ2pEO0lBQ0Y7RUFDRixDQUFDLEVBQUUsQ0FBQ21SLE9BQU8sRUFBRUQsSUFBSSxFQUFFaE4sZ0JBQWdCLEVBQUUzRCxpQkFBaUIsQ0FBQyxDQUFDOztFQUV4RDtFQUNBLE1BQU04VyxhQUFhLEdBQUd6akIsV0FBVyxDQUFDLE1BQU07SUFDdENxZCxZQUFZLENBQUN6UixLQUFLLEVBQUVPLFlBQVksRUFBRUMsY0FBYyxDQUFDO0lBQ2pELE1BQU1zVyxRQUFRLEdBQ1o5VyxLQUFLLENBQUMrRSxLQUFLLENBQUMsQ0FBQyxFQUFFeEUsWUFBWSxDQUFDLEdBQUcsSUFBSSxHQUFHUCxLQUFLLENBQUMrRSxLQUFLLENBQUN4RSxZQUFZLENBQUM7SUFDakVtRSxnQkFBZ0IsQ0FBQ29TLFFBQVEsQ0FBQztJQUMxQnhTLGVBQWUsQ0FBQy9ELFlBQVksR0FBRyxDQUFDLENBQUM7RUFDbkMsQ0FBQyxFQUFFLENBQ0RQLEtBQUssRUFDTE8sWUFBWSxFQUNabUUsZ0JBQWdCLEVBQ2hCSixlQUFlLEVBQ2ZtTixZQUFZLEVBQ1pqUixjQUFjLENBQ2YsQ0FBQzs7RUFFRjtFQUNBLE1BQU1zWCxvQkFBb0IsR0FBRzFqQixXQUFXLENBQUMsWUFBWTtJQUNuRFcsUUFBUSxDQUFDLDRCQUE0QixFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzFDNlUseUJBQXlCLENBQUMsSUFBSSxDQUFDO0lBRS9CLElBQUk7TUFDRjtNQUNBLE1BQU05SixNQUFNLEdBQUcsTUFBTW5FLGtCQUFrQixDQUFDcUUsS0FBSyxFQUFFUSxjQUFjLENBQUM7TUFFOUQsSUFBSVYsTUFBTSxDQUFDMFUsS0FBSyxFQUFFO1FBQ2hCN0QsZUFBZSxDQUFDO1VBQ2R0TSxHQUFHLEVBQUUsdUJBQXVCO1VBQzVCL0QsSUFBSSxFQUFFUixNQUFNLENBQUMwVSxLQUFLO1VBQ2xCak4sS0FBSyxFQUFFLFNBQVM7VUFDaEI4SSxRQUFRLEVBQUU7UUFDWixDQUFDLENBQUM7TUFDSjtNQUVBLElBQUl2USxNQUFNLENBQUMrVixPQUFPLEtBQUssSUFBSSxJQUFJL1YsTUFBTSxDQUFDK1YsT0FBTyxLQUFLN1YsS0FBSyxFQUFFO1FBQ3ZEO1FBQ0F5UixZQUFZLENBQUN6UixLQUFLLEVBQUVPLFlBQVksRUFBRUMsY0FBYyxDQUFDO1FBRWpEa0UsZ0JBQWdCLENBQUM1RSxNQUFNLENBQUMrVixPQUFPLENBQUM7UUFDaEN2UixlQUFlLENBQUN4RSxNQUFNLENBQUMrVixPQUFPLENBQUN0UixNQUFNLENBQUM7TUFDeEM7SUFDRixDQUFDLENBQUMsT0FBT3dULEdBQUcsRUFBRTtNQUNaLElBQUlBLEdBQUcsWUFBWUMsS0FBSyxFQUFFO1FBQ3hCOWMsUUFBUSxDQUFDNmMsR0FBRyxDQUFDO01BQ2Y7TUFDQXBILGVBQWUsQ0FBQztRQUNkdE0sR0FBRyxFQUFFLHVCQUF1QjtRQUM1Qi9ELElBQUksRUFBRSwyQkFBMkJwRyxZQUFZLENBQUM2ZCxHQUFHLENBQUMsRUFBRTtRQUNwRHhRLEtBQUssRUFBRSxTQUFTO1FBQ2hCOEksUUFBUSxFQUFFO01BQ1osQ0FBQyxDQUFDO0lBQ0osQ0FBQyxTQUFTO01BQ1J6Ryx5QkFBeUIsQ0FBQyxLQUFLLENBQUM7SUFDbEM7RUFDRixDQUFDLEVBQUUsQ0FDRDVKLEtBQUssRUFDTE8sWUFBWSxFQUNaQyxjQUFjLEVBQ2RpUixZQUFZLEVBQ1ovTSxnQkFBZ0IsRUFDaEJpTSxlQUFlLENBQ2hCLENBQUM7O0VBRUY7RUFDQSxNQUFNc0gsV0FBVyxHQUFHN2pCLFdBQVcsQ0FBQyxNQUFNO0lBQ3BDLElBQUk0TCxLQUFLLENBQUNpVSxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsSUFBSTVULGFBQWEsS0FBS29GLFNBQVMsRUFBRTtNQUN0RDtNQUNBZixnQkFBZ0IsQ0FBQ3JFLGFBQWEsQ0FBQ0MsSUFBSSxDQUFDO01BQ3BDZ0UsZUFBZSxDQUFDakUsYUFBYSxDQUFDRSxZQUFZLENBQUM7TUFDM0NRLGlCQUFpQixDQUFDVixhQUFhLENBQUNHLGNBQWMsQ0FBQztNQUMvQ0UsZ0JBQWdCLENBQUMrRSxTQUFTLENBQUM7SUFDN0IsQ0FBQyxNQUFNLElBQUl6RixLQUFLLENBQUNpVSxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtNQUM5QjtNQUNBdlQsZ0JBQWdCLENBQUM7UUFBRUosSUFBSSxFQUFFTixLQUFLO1FBQUVPLFlBQVk7UUFBRUM7TUFBZSxDQUFDLENBQUM7TUFDL0RrRSxnQkFBZ0IsQ0FBQyxFQUFFLENBQUM7TUFDcEJKLGVBQWUsQ0FBQyxDQUFDLENBQUM7TUFDbEJ2RCxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsQ0FBQztNQUNyQjtNQUNBbkgsZ0JBQWdCLENBQUN1WixDQUFDLElBQUk7UUFDcEIsSUFBSUEsQ0FBQyxDQUFDNUIsWUFBWSxFQUFFLE9BQU80QixDQUFDO1FBQzVCLE9BQU87VUFBRSxHQUFHQSxDQUFDO1VBQUU1QixZQUFZLEVBQUU7UUFBSyxDQUFDO01BQ3JDLENBQUMsQ0FBQztJQUNKO0VBQ0YsQ0FBQyxFQUFFLENBQ0R2UixLQUFLLEVBQ0xPLFlBQVksRUFDWkYsYUFBYSxFQUNicUUsZ0JBQWdCLEVBQ2hCaEUsZ0JBQWdCLEVBQ2hCRixjQUFjLEVBQ2RPLGlCQUFpQixDQUNsQixDQUFDOztFQUVGO0VBQ0EsTUFBTW1YLGlCQUFpQixHQUFHOWpCLFdBQVcsQ0FBQyxNQUFNO0lBQzFDMFYsa0JBQWtCLENBQUMxSCxJQUFJLElBQUksQ0FBQ0EsSUFBSSxDQUFDO0lBQ2pDLElBQUlXLFFBQVEsRUFBRTtNQUNaQyxXQUFXLENBQUMsS0FBSyxDQUFDO0lBQ3BCO0VBQ0YsQ0FBQyxFQUFFLENBQUNELFFBQVEsQ0FBQyxDQUFDOztFQUVkO0VBQ0EsTUFBTW9WLG9CQUFvQixHQUFHL2pCLFdBQVcsQ0FBQyxNQUFNO0lBQzdDa1cscUJBQXFCLENBQUNsSSxJQUFJLElBQUksQ0FBQ0EsSUFBSSxDQUFDO0lBQ3BDLElBQUlXLFFBQVEsRUFBRTtNQUNaQyxXQUFXLENBQUMsS0FBSyxDQUFDO0lBQ3BCO0VBQ0YsQ0FBQyxFQUFFLENBQUNELFFBQVEsQ0FBQyxDQUFDOztFQUVkO0VBQ0EsTUFBTXFWLG9CQUFvQixHQUFHaGtCLFdBQVcsQ0FBQyxNQUFNO0lBQzdDb1cscUJBQXFCLENBQUNwSSxJQUFJLElBQUksQ0FBQ0EsSUFBSSxDQUFDO0lBQ3BDLElBQUlXLFFBQVEsRUFBRTtNQUNaQyxXQUFXLENBQUMsS0FBSyxDQUFDO0lBQ3BCO0VBQ0YsQ0FBQyxFQUFFLENBQUNELFFBQVEsQ0FBQyxDQUFDOztFQUVkO0VBQ0EsTUFBTXNWLGVBQWUsR0FBR2prQixXQUFXLENBQUMsTUFBTTtJQUN4QztJQUNBLElBQUlrRixvQkFBb0IsQ0FBQyxDQUFDLElBQUkyTixjQUFjLElBQUloQixrQkFBa0IsRUFBRTtNQUNsRSxNQUFNcVMsZUFBZSxFQUFFL2YscUJBQXFCLEdBQUc7UUFDN0MsR0FBRzZHLHFCQUFxQjtRQUN4QmUsSUFBSSxFQUFFOEcsY0FBYyxDQUFDVztNQUN2QixDQUFDO01BQ0Q7TUFDQSxNQUFNMlEsUUFBUSxHQUFHaGQscUJBQXFCLENBQUMrYyxlQUFlLEVBQUU3UyxTQUFTLENBQUM7TUFFbEUxUSxRQUFRLENBQUMsa0JBQWtCLEVBQUU7UUFDM0J5akIsRUFBRSxFQUFFRCxRQUFRLElBQUl6akI7TUFDbEIsQ0FBQyxDQUFDO01BRUYsTUFBTTJqQixjQUFjLEdBQUd4UyxrQkFBa0I7TUFDekMvRCxXQUFXLENBQUNFLElBQUksSUFBSTtRQUNsQixNQUFNSyxJQUFJLEdBQUdMLElBQUksQ0FBQzZDLEtBQUssQ0FBQ3dULGNBQWMsQ0FBQztRQUN2QyxJQUFJLENBQUNoVyxJQUFJLElBQUlBLElBQUksQ0FBQ3FSLElBQUksS0FBSyxxQkFBcUIsRUFBRTtVQUNoRCxPQUFPMVIsSUFBSTtRQUNiO1FBQ0EsSUFBSUssSUFBSSxDQUFDbUYsY0FBYyxLQUFLMlEsUUFBUSxFQUFFO1VBQ3BDLE9BQU9uVyxJQUFJO1FBQ2I7UUFDQSxPQUFPO1VBQ0wsR0FBR0EsSUFBSTtVQUNQNkMsS0FBSyxFQUFFO1lBQ0wsR0FBRzdDLElBQUksQ0FBQzZDLEtBQUs7WUFDYixDQUFDd1QsY0FBYyxHQUFHO2NBQ2hCLEdBQUdoVyxJQUFJO2NBQ1BtRixjQUFjLEVBQUUyUTtZQUNsQjtVQUNGO1FBQ0YsQ0FBQztNQUNILENBQUMsQ0FBQztNQUVGLElBQUl4VixRQUFRLEVBQUU7UUFDWkMsV0FBVyxDQUFDLEtBQUssQ0FBQztNQUNwQjtNQUNBO0lBQ0Y7O0lBRUE7SUFDQW5KLGVBQWUsQ0FDYiw0Q0FBNEN1RixxQkFBcUIsQ0FBQ2UsSUFBSSx3QkFBd0JmLHFCQUFxQixDQUFDc1osbUJBQW1CLHNCQUFzQmpPLGlCQUFpQixtQkFBbUIsQ0FBQyxDQUFDSSx1QkFBdUIsQ0FBQ3BHLE9BQU8sRUFDcE8sQ0FBQztJQUNELE1BQU04VCxRQUFRLEdBQUdoZCxxQkFBcUIsQ0FBQzZELHFCQUFxQixFQUFFd0csV0FBVyxDQUFDOztJQUUxRTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0EsSUFBSStTLDJCQUEyQixHQUFHLEtBQUs7SUFDdkMsSUFBSTNrQixPQUFPLENBQUMsdUJBQXVCLENBQUMsRUFBRTtNQUNwQzJrQiwyQkFBMkIsR0FDekJKLFFBQVEsS0FBSyxNQUFNLElBQ25CbloscUJBQXFCLENBQUNlLElBQUksS0FBSyxNQUFNLElBQ3JDLENBQUN2RSxnQkFBZ0IsQ0FBQyxDQUFDLElBQ25CLENBQUNxSyxrQkFBa0IsRUFBQztJQUN4QjtJQUVBLElBQUlqUyxPQUFPLENBQUMsdUJBQXVCLENBQUMsRUFBRTtNQUNwQyxJQUFJMmtCLDJCQUEyQixFQUFFO1FBQy9CO1FBQ0EvTix5QkFBeUIsQ0FBQ3hMLHFCQUFxQixDQUFDZSxJQUFJLENBQUM7O1FBRXJEO1FBQ0E7UUFDQStCLFdBQVcsQ0FBQ0UsSUFBSSxLQUFLO1VBQ25CLEdBQUdBLElBQUk7VUFDUGhELHFCQUFxQixFQUFFO1lBQ3JCLEdBQUdnRCxJQUFJLENBQUNoRCxxQkFBcUI7WUFDN0JlLElBQUksRUFBRTtVQUNSO1FBQ0YsQ0FBQyxDQUFDLENBQUM7UUFDSGQsd0JBQXdCLENBQUM7VUFDdkIsR0FBR0QscUJBQXFCO1VBQ3hCZSxJQUFJLEVBQUU7UUFDUixDQUFDLENBQUM7O1FBRUY7UUFDQSxJQUFJMEssdUJBQXVCLENBQUNwRyxPQUFPLEVBQUU7VUFDbkNtVSxZQUFZLENBQUMvTix1QkFBdUIsQ0FBQ3BHLE9BQU8sQ0FBQztRQUMvQztRQUNBb0csdUJBQXVCLENBQUNwRyxPQUFPLEdBQUdvVSxVQUFVLENBQzFDLENBQUNuTyxvQkFBb0IsRUFBRUcsdUJBQXVCLEtBQUs7VUFDakRILG9CQUFvQixDQUFDLElBQUksQ0FBQztVQUMxQkcsdUJBQXVCLENBQUNwRyxPQUFPLEdBQUcsSUFBSTtRQUN4QyxDQUFDLEVBQ0QsR0FBRyxFQUNIaUcsb0JBQW9CLEVBQ3BCRyx1QkFDRixDQUFDO1FBRUQsSUFBSTlILFFBQVEsRUFBRTtVQUNaQyxXQUFXLENBQUMsS0FBSyxDQUFDO1FBQ3BCO1FBQ0E7TUFDRjtJQUNGOztJQUVBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQSxJQUFJaFAsT0FBTyxDQUFDLHVCQUF1QixDQUFDLEVBQUU7TUFDcEMsSUFBSXlXLGlCQUFpQixJQUFJSSx1QkFBdUIsQ0FBQ3BHLE9BQU8sRUFBRTtRQUN4RCxJQUFJZ0csaUJBQWlCLEVBQUU7VUFDckIxVixRQUFRLENBQUMsdUNBQXVDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDdkQ7UUFDQTJWLG9CQUFvQixDQUFDLEtBQUssQ0FBQztRQUMzQixJQUFJRyx1QkFBdUIsQ0FBQ3BHLE9BQU8sRUFBRTtVQUNuQ21VLFlBQVksQ0FBQy9OLHVCQUF1QixDQUFDcEcsT0FBTyxDQUFDO1VBQzdDb0csdUJBQXVCLENBQUNwRyxPQUFPLEdBQUcsSUFBSTtRQUN4QztRQUNBbUcseUJBQXlCLENBQUMsSUFBSSxDQUFDO1FBQy9CO01BQ0Y7SUFDRjs7SUFFQTtJQUNBO0lBQ0E7SUFDQSxNQUFNO01BQUVrTyxPQUFPLEVBQUVDO0lBQWdCLENBQUMsR0FBR3pkLG1CQUFtQixDQUN0RDhELHFCQUFxQixFQUNyQndHLFdBQ0YsQ0FBQztJQUVEN1EsUUFBUSxDQUFDLGtCQUFrQixFQUFFO01BQzNCeWpCLEVBQUUsRUFBRUQsUUFBUSxJQUFJempCO0lBQ2xCLENBQUMsQ0FBQzs7SUFFRjtJQUNBLElBQUl5akIsUUFBUSxLQUFLLE1BQU0sRUFBRTtNQUN2QjNlLGdCQUFnQixDQUFDNkssT0FBTyxLQUFLO1FBQzNCLEdBQUdBLE9BQU87UUFDVnVVLGVBQWUsRUFBRUMsSUFBSSxDQUFDQyxHQUFHLENBQUM7TUFDNUIsQ0FBQyxDQUFDLENBQUM7SUFDTDs7SUFFQTtJQUNBO0lBQ0E7SUFDQTtJQUNBaFgsV0FBVyxDQUFDRSxJQUFJLEtBQUs7TUFDbkIsR0FBR0EsSUFBSTtNQUNQaEQscUJBQXFCLEVBQUU7UUFDckIsR0FBRzJaLGVBQWU7UUFDbEI1WSxJQUFJLEVBQUVvWTtNQUNSO0lBQ0YsQ0FBQyxDQUFDLENBQUM7SUFDSGxaLHdCQUF3QixDQUFDO01BQ3ZCLEdBQUcwWixlQUFlO01BQ2xCNVksSUFBSSxFQUFFb1k7SUFDUixDQUFDLENBQUM7O0lBRUY7SUFDQW5jLGdCQUFnQixDQUFDbWMsUUFBUSxFQUFFM1MsV0FBVyxFQUFFOEYsUUFBUSxDQUFDOztJQUVqRDtJQUNBLElBQUkzSSxRQUFRLEVBQUU7TUFDWkMsV0FBVyxDQUFDLEtBQUssQ0FBQztJQUNwQjtFQUNGLENBQUMsRUFBRSxDQUNENUQscUJBQXFCLEVBQ3JCd0csV0FBVyxFQUNYSyxrQkFBa0IsRUFDbEJnQixjQUFjLEVBQ2QvRSxXQUFXLEVBQ1g3Qyx3QkFBd0IsRUFDeEIwRCxRQUFRLEVBQ1IwSCxpQkFBaUIsQ0FDbEIsQ0FBQzs7RUFFRjtFQUNBLE1BQU0wTyx5QkFBeUIsR0FBRy9rQixXQUFXLENBQUMsTUFBTTtJQUNsRCxJQUFJSixPQUFPLENBQUMsdUJBQXVCLENBQUMsRUFBRTtNQUNwQzBXLG9CQUFvQixDQUFDLEtBQUssQ0FBQztNQUMzQkUseUJBQXlCLENBQUMsSUFBSSxDQUFDOztNQUUvQjtNQUNBO01BQ0E7TUFDQSxNQUFNd08sZUFBZSxHQUFHNWQsd0JBQXdCLENBQzlDbVAsc0JBQXNCLElBQUl2TCxxQkFBcUIsQ0FBQ2UsSUFBSSxFQUNwRCxNQUFNLEVBQ05mLHFCQUNGLENBQUM7TUFDRDhDLFdBQVcsQ0FBQ0UsSUFBSSxLQUFLO1FBQ25CLEdBQUdBLElBQUk7UUFDUGhELHFCQUFxQixFQUFFO1VBQ3JCLEdBQUdnYSxlQUFlO1VBQ2xCalosSUFBSSxFQUFFO1FBQ1I7TUFDRixDQUFDLENBQUMsQ0FBQztNQUNIZCx3QkFBd0IsQ0FBQztRQUN2QixHQUFHK1osZUFBZTtRQUNsQmpaLElBQUksRUFBRTtNQUNSLENBQUMsQ0FBQzs7TUFFRjtNQUNBLElBQUk0QyxRQUFRLEVBQUU7UUFDWkMsV0FBVyxDQUFDLEtBQUssQ0FBQztNQUNwQjtJQUNGO0VBQ0YsQ0FBQyxFQUFFLENBQ0RELFFBQVEsRUFDUkMsV0FBVyxFQUNYMkgsc0JBQXNCLEVBQ3RCdkwscUJBQXFCLEVBQ3JCOEMsV0FBVyxFQUNYN0Msd0JBQXdCLENBQ3pCLENBQUM7O0VBRUY7RUFDQSxNQUFNZ2EsMEJBQTBCLEdBQUdqbEIsV0FBVyxDQUFDLE1BQU07SUFDbkQsSUFBSUosT0FBTyxDQUFDLHVCQUF1QixDQUFDLEVBQUU7TUFDcEM2RixlQUFlLENBQ2Isd0RBQXdEOFEsc0JBQXNCLHFDQUNoRixDQUFDO01BQ0RELG9CQUFvQixDQUFDLEtBQUssQ0FBQztNQUMzQixJQUFJRyx1QkFBdUIsQ0FBQ3BHLE9BQU8sRUFBRTtRQUNuQ21VLFlBQVksQ0FBQy9OLHVCQUF1QixDQUFDcEcsT0FBTyxDQUFDO1FBQzdDb0csdUJBQXVCLENBQUNwRyxPQUFPLEdBQUcsSUFBSTtNQUN4Qzs7TUFFQTtNQUNBO01BQ0EsSUFBSWtHLHNCQUFzQixFQUFFO1FBQzFCdFAsaUJBQWlCLENBQUMsS0FBSyxDQUFDO1FBQ3hCNkcsV0FBVyxDQUFDRSxJQUFJLEtBQUs7VUFDbkIsR0FBR0EsSUFBSTtVQUNQaEQscUJBQXFCLEVBQUU7WUFDckIsR0FBR2dELElBQUksQ0FBQ2hELHFCQUFxQjtZQUM3QmUsSUFBSSxFQUFFd0ssc0JBQXNCO1lBQzVCK04sbUJBQW1CLEVBQUU7VUFDdkI7UUFDRixDQUFDLENBQUMsQ0FBQztRQUNIclosd0JBQXdCLENBQUM7VUFDdkIsR0FBR0QscUJBQXFCO1VBQ3hCZSxJQUFJLEVBQUV3SyxzQkFBc0I7VUFDNUIrTixtQkFBbUIsRUFBRTtRQUN2QixDQUFDLENBQUM7UUFDRjlOLHlCQUF5QixDQUFDLElBQUksQ0FBQztNQUNqQztJQUNGO0VBQ0YsQ0FBQyxFQUFFLENBQ0RELHNCQUFzQixFQUN0QnZMLHFCQUFxQixFQUNyQjhDLFdBQVcsRUFDWDdDLHdCQUF3QixDQUN6QixDQUFDOztFQUVGO0VBQ0EsTUFBTWlhLGdCQUFnQixHQUFHbGxCLFdBQVcsQ0FBQyxNQUFNO0lBQ3pDLEtBQUt1RyxxQkFBcUIsQ0FBQyxDQUFDLENBQUM0ZSxJQUFJLENBQUNDLFNBQVMsSUFBSTtNQUM3QyxJQUFJQSxTQUFTLEVBQUU7UUFDYnBFLFlBQVksQ0FBQ29FLFNBQVMsQ0FBQ0MsTUFBTSxFQUFFRCxTQUFTLENBQUNsRSxTQUFTLENBQUM7TUFDckQsQ0FBQyxNQUFNO1FBQ0wsTUFBTW9FLGVBQWUsR0FBR2hpQixrQkFBa0IsQ0FDeEMsaUJBQWlCLEVBQ2pCLE1BQU0sRUFDTixRQUNGLENBQUM7UUFDRCxNQUFNNGMsT0FBTyxHQUFHcmEsR0FBRyxDQUFDMGYsS0FBSyxDQUFDLENBQUMsR0FDdkIscURBQXFELEdBQ3JELG9DQUFvQ0QsZUFBZSxtQkFBbUI7UUFDMUUvSSxlQUFlLENBQUM7VUFDZHRNLEdBQUcsRUFBRSx1QkFBdUI7VUFDNUIvRCxJQUFJLEVBQUVnVSxPQUFPO1VBQ2JqRSxRQUFRLEVBQUUsV0FBVztVQUNyQlEsU0FBUyxFQUFFO1FBQ2IsQ0FBQyxDQUFDO01BQ0o7SUFDRixDQUFDLENBQUM7RUFDSixDQUFDLEVBQUUsQ0FBQ0YsZUFBZSxFQUFFeUUsWUFBWSxDQUFDLENBQUM7O0VBRW5DO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBLE1BQU13RSxpQkFBaUIsR0FBR25pQiw0QkFBNEIsQ0FBQyxDQUFDO0VBQ3hEcEQsU0FBUyxDQUFDLE1BQU07SUFDZCxJQUFJLENBQUN1bEIsaUJBQWlCLElBQUk1VixvQkFBb0IsRUFBRTtJQUNoRCxPQUFPNFYsaUJBQWlCLENBQUNDLGVBQWUsQ0FBQztNQUN2Q0MsTUFBTSxFQUFFLGFBQWE7TUFDckJoQixPQUFPLEVBQUUsTUFBTTtNQUNmaUIsT0FBTyxFQUFFQSxDQUFBLEtBQU07UUFDYixLQUFLbFksUUFBUSxDQUFDN0IsS0FBSyxDQUFDO01BQ3RCO0lBQ0YsQ0FBQyxDQUFDO0VBQ0osQ0FBQyxFQUFFLENBQUM0WixpQkFBaUIsRUFBRTVWLG9CQUFvQixFQUFFbkMsUUFBUSxFQUFFN0IsS0FBSyxDQUFDLENBQUM7O0VBRTlEO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQSxNQUFNZ2EsWUFBWSxHQUFHMWxCLE9BQU8sQ0FDMUIsT0FBTztJQUNMLFdBQVcsRUFBRXFqQixVQUFVO0lBQ3ZCLGNBQWMsRUFBRUUsYUFBYTtJQUM3QixxQkFBcUIsRUFBRUMsb0JBQW9CO0lBQzNDLFlBQVksRUFBRUcsV0FBVztJQUN6QixrQkFBa0IsRUFBRUMsaUJBQWlCO0lBQ3JDLHFCQUFxQixFQUFFRSxvQkFBb0I7SUFDM0MsZ0JBQWdCLEVBQUVDLGVBQWU7SUFDakMsaUJBQWlCLEVBQUVpQjtFQUNyQixDQUFDLENBQUMsRUFDRixDQUNFM0IsVUFBVSxFQUNWRSxhQUFhLEVBQ2JDLG9CQUFvQixFQUNwQkcsV0FBVyxFQUNYQyxpQkFBaUIsRUFDakJFLG9CQUFvQixFQUNwQkMsZUFBZSxFQUNmaUIsZ0JBQWdCLENBRXBCLENBQUM7RUFFRDFoQixjQUFjLENBQUNvaUIsWUFBWSxFQUFFO0lBQzNCbEIsT0FBTyxFQUFFLE1BQU07SUFDZm1CLFFBQVEsRUFBRSxDQUFDalc7RUFDYixDQUFDLENBQUM7O0VBRUY7RUFDQTtFQUNBck0sYUFBYSxDQUFDLHFCQUFxQixFQUFFLE1BQU1rSixxQkFBcUIsR0FBRyxDQUFDLEVBQUU7SUFDcEVpWSxPQUFPLEVBQUUsTUFBTTtJQUNmbUIsUUFBUSxFQUFFLENBQUNqVyxvQkFBb0IsSUFBSSxDQUFDdEI7RUFDdEMsQ0FBQyxDQUFDOztFQUVGO0VBQ0EvSyxhQUFhLENBQUMsZUFBZSxFQUFFd2dCLG9CQUFvQixFQUFFO0lBQ25EVyxPQUFPLEVBQUUsTUFBTTtJQUNmbUIsUUFBUSxFQUNOLENBQUNqVyxvQkFBb0IsSUFBSXpKLGlCQUFpQixDQUFDLENBQUMsSUFBSUYsbUJBQW1CLENBQUM7RUFDeEUsQ0FBQyxDQUFDOztFQUVGO0VBQ0E7RUFDQTtFQUNBMUMsYUFBYSxDQUNYLGNBQWMsRUFDZCxNQUFNO0lBQ0pxTCxXQUFXLENBQUMsS0FBSyxDQUFDO0VBQ3BCLENBQUMsRUFDRDtJQUFFOFYsT0FBTyxFQUFFLE1BQU07SUFBRW1CLFFBQVEsRUFBRWxYO0VBQVMsQ0FDeEMsQ0FBQzs7RUFFRDtFQUNBO0VBQ0E7RUFDQSxNQUFNbVgsaUJBQWlCLEdBQUdsbUIsT0FBTyxDQUFDLGNBQWMsQ0FBQyxHQUM3QyxDQUFDZ1Esb0JBQW9CLEdBQ3JCLEtBQUs7RUFDVHJNLGFBQWEsQ0FDWCxlQUFlLEVBQ2YsTUFBTTtJQUNKLElBQUkzRCxPQUFPLENBQUMsY0FBYyxDQUFDLEVBQUU7TUFDM0JnVyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUM7TUFDdEJoSCxXQUFXLENBQUMsS0FBSyxDQUFDO0lBQ3BCO0VBQ0YsQ0FBQyxFQUNEO0lBQUU4VixPQUFPLEVBQUUsUUFBUTtJQUFFbUIsUUFBUSxFQUFFQztFQUFrQixDQUNuRCxDQUFDO0VBQ0R2aUIsYUFBYSxDQUNYLGtCQUFrQixFQUNsQixNQUFNO0lBQ0osSUFBSTNELE9BQU8sQ0FBQyxjQUFjLENBQUMsRUFBRTtNQUMzQmtXLG1CQUFtQixDQUFDLElBQUksQ0FBQztNQUN6QmxILFdBQVcsQ0FBQyxLQUFLLENBQUM7SUFDcEI7RUFDRixDQUFDLEVBQ0Q7SUFBRThWLE9BQU8sRUFBRSxRQUFRO0lBQUVtQixRQUFRLEVBQUVDO0VBQWtCLENBQ25ELENBQUM7RUFFRHZpQixhQUFhLENBQ1gsZ0JBQWdCLEVBQ2hCLE1BQU07SUFDSixJQUFJM0QsT0FBTyxDQUFDLGdCQUFnQixDQUFDLEVBQUU7TUFDN0JvVyxvQkFBb0IsQ0FBQyxJQUFJLENBQUM7TUFDMUJwSCxXQUFXLENBQUMsS0FBSyxDQUFDO0lBQ3BCO0VBQ0YsQ0FBQyxFQUNEO0lBQ0U4VixPQUFPLEVBQUUsUUFBUTtJQUNqQm1CLFFBQVEsRUFBRWptQixPQUFPLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxDQUFDZ1Esb0JBQW9CLEdBQUc7RUFDaEUsQ0FDRixDQUFDOztFQUVEO0VBQ0E7RUFDQXJNLGFBQWEsQ0FDWCxlQUFlLEVBQ2YsTUFBTTtJQUNKTSxnQkFBZ0IsQ0FBQ2lLLFdBQVcsQ0FBQztFQUMvQixDQUFDLEVBQ0Q7SUFDRTRXLE9BQU8sRUFBRSxRQUFRO0lBQ2pCbUIsUUFBUSxFQUFFLENBQUN2YSxTQUFTLElBQUlzRyxXQUFXLENBQUMrRixNQUFNLEtBQUs7RUFDakQsQ0FDRixDQUFDOztFQUVEO0VBQ0E7RUFDQTtFQUNBblUsY0FBYyxDQUNaO0lBQ0UsV0FBVyxFQUFFdWlCLENBQUEsS0FBTTtNQUNqQjtNQUNBLElBQ0UzTixhQUFhLElBQ2IsVUFBVSxLQUFLLEtBQUssSUFDcEJ4RCxvQkFBb0IsR0FBRyxDQUFDLElBQ3hCSixvQkFBb0IsR0FBR1UsbUJBQW1CLEVBQzFDO1FBQ0FULHVCQUF1QixDQUFDekcsSUFBSSxJQUFJQSxJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBQ3pDO01BQ0Y7TUFDQTJLLGNBQWMsQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUM7SUFDMUIsQ0FBQztJQUNELGFBQWEsRUFBRXFOLENBQUEsS0FBTTtNQUNuQjtNQUNBLElBQ0U1TixhQUFhLElBQ2IsVUFBVSxLQUFLLEtBQUssSUFDcEJ4RCxvQkFBb0IsR0FBRyxDQUFDLEVBQ3hCO1FBQ0EsSUFBSUosb0JBQW9CLEdBQUdJLG9CQUFvQixHQUFHLENBQUMsRUFBRTtVQUNuREgsdUJBQXVCLENBQUN6RyxJQUFJLElBQUlBLElBQUksR0FBRyxDQUFDLENBQUM7UUFDM0M7UUFDQTtNQUNGO01BQ0EsSUFBSW9LLGFBQWEsSUFBSSxDQUFDOUUsY0FBYyxFQUFFO1FBQ3BDckcsbUJBQW1CLENBQUMsSUFBSSxDQUFDO1FBQ3pCd0wsZ0JBQWdCLENBQUMsSUFBSSxDQUFDO1FBQ3RCO01BQ0Y7TUFDQUUsY0FBYyxDQUFDLENBQUMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsYUFBYSxFQUFFc04sQ0FBQSxLQUFNO01BQ25CO01BQ0EsSUFBSTdOLGFBQWEsSUFBSTlFLGNBQWMsRUFBRTtRQUNuQyxNQUFNNFMsV0FBVyxHQUFHLENBQUMsR0FBRzdTLGtCQUFrQixDQUFDbEQsTUFBTTtRQUNqRG9FLHNCQUFzQixDQUFDdkcsSUFBSSxJQUFJLENBQUNBLElBQUksR0FBRyxDQUFDLElBQUlrWSxXQUFXLENBQUM7UUFDeEQ7TUFDRjtNQUNBdk4sY0FBYyxDQUFDLENBQUMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsaUJBQWlCLEVBQUV3TixDQUFBLEtBQU07TUFDdkIsSUFBSS9OLGFBQWEsSUFBSTlFLGNBQWMsRUFBRTtRQUNuQyxNQUFNNFMsV0FBVyxHQUFHLENBQUMsR0FBRzdTLGtCQUFrQixDQUFDbEQsTUFBTTtRQUNqRG9FLHNCQUFzQixDQUFDdkcsSUFBSSxJQUFJLENBQUNBLElBQUksR0FBRyxDQUFDLEdBQUdrWSxXQUFXLElBQUlBLFdBQVcsQ0FBQztRQUN0RTtNQUNGO01BQ0F2TixjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDcEIsQ0FBQztJQUNELHFCQUFxQixFQUFFeU4sQ0FBQSxLQUFNO01BQzNCLElBQUl0VSxpQkFBaUIsS0FBSyxpQkFBaUIsRUFBRTtRQUMzQztNQUNGO01BQ0EsUUFBUXFHLGtCQUFrQjtRQUN4QixLQUFLLFdBQVc7VUFDZCxJQUFJdlksT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQ3BCNlksZ0JBQWdCLENBQUMsSUFBSSxDQUFDO1lBQ3RCLEtBQUtoTCxRQUFRLENBQUMsUUFBUSxDQUFDO1VBQ3pCO1VBQ0E7UUFDRixLQUFLLE9BQU87VUFDVixJQUFJNkYsY0FBYyxFQUFFO1lBQ2xCO1lBQ0EsSUFBSWdCLG1CQUFtQixLQUFLLENBQUMsRUFBRTtjQUM3QnJRLGdCQUFnQixDQUFDNkosV0FBVyxDQUFDO1lBQy9CLENBQUMsTUFBTTtjQUNMLE1BQU11WSxRQUFRLEdBQUdoVCxrQkFBa0IsQ0FBQ2lCLG1CQUFtQixHQUFHLENBQUMsQ0FBQztjQUM1RCxJQUFJK1IsUUFBUSxFQUFFcmlCLGlCQUFpQixDQUFDcWlCLFFBQVEsQ0FBQzdFLEVBQUUsRUFBRTFULFdBQVcsQ0FBQztZQUMzRDtVQUNGLENBQUMsTUFBTSxJQUFJMEcsb0JBQW9CLEtBQUssQ0FBQyxJQUFJSSxvQkFBb0IsR0FBRyxDQUFDLEVBQUU7WUFDakUzUSxnQkFBZ0IsQ0FBQzZKLFdBQVcsQ0FBQztVQUMvQixDQUFDLE1BQU07WUFDTCxNQUFNd1ksY0FBYyxHQUNsQnRkLG9CQUFvQixDQUFDNkgsS0FBSyxDQUFDLENBQUMyRCxvQkFBb0IsR0FBRyxDQUFDLENBQUMsRUFBRWdOLEVBQUU7WUFDM0QsSUFBSThFLGNBQWMsRUFBRTtjQUNsQnRpQixpQkFBaUIsQ0FBQ3NpQixjQUFjLEVBQUV4WSxXQUFXLENBQUM7WUFDaEQsQ0FBQyxNQUFNO2NBQ0xiLG1CQUFtQixDQUFDLElBQUksQ0FBQztjQUN6QndMLGdCQUFnQixDQUFDLElBQUksQ0FBQztZQUN4QjtVQUNGO1VBQ0E7UUFDRixLQUFLLE1BQU07VUFDVCxJQUFJLFVBQVUsS0FBSyxLQUFLLEVBQUU7WUFDeEIzSyxXQUFXLENBQUNFLElBQUksSUFDZEEsSUFBSSxDQUFDdVksdUJBQXVCLEdBQ3hCO2NBQUUsR0FBR3ZZLElBQUk7Y0FBRXVZLHVCQUF1QixFQUFFO1lBQU0sQ0FBQyxHQUMzQztjQUNFLEdBQUd2WSxJQUFJO2NBQ1B3WSxvQkFBb0IsRUFBRSxFQUNwQnhZLElBQUksQ0FBQ3dZLG9CQUFvQixJQUFJLElBQUk7WUFFckMsQ0FDTixDQUFDO1VBQ0g7VUFDQTtRQUNGLEtBQUssT0FBTztVQUNWO1FBQ0YsS0FBSyxPQUFPO1VBQ1ZyUyxrQkFBa0IsQ0FBQyxJQUFJLENBQUM7VUFDeEJzRSxnQkFBZ0IsQ0FBQyxJQUFJLENBQUM7VUFDdEI7UUFDRixLQUFLLFFBQVE7VUFDWHBFLG1CQUFtQixDQUFDLElBQUksQ0FBQztVQUN6Qm9FLGdCQUFnQixDQUFDLElBQUksQ0FBQztVQUN0QjtNQUNKO0lBQ0YsQ0FBQztJQUNELHVCQUF1QixFQUFFZ08sQ0FBQSxLQUFNO01BQzdCaE8sZ0JBQWdCLENBQUMsSUFBSSxDQUFDO0lBQ3hCLENBQUM7SUFDRCxjQUFjLEVBQUVpTyxDQUFBLEtBQU07TUFDcEIsSUFBSXRPLGFBQWEsSUFBSTVELG9CQUFvQixJQUFJLENBQUMsRUFBRTtRQUM5QyxNQUFNbkcsSUFBSSxHQUFHckYsb0JBQW9CLENBQUM2SCxLQUFLLENBQUMsQ0FBQzJELG9CQUFvQixHQUFHLENBQUMsQ0FBQztRQUNsRSxJQUFJLENBQUNuRyxJQUFJLEVBQUUsT0FBTyxLQUFLO1FBQ3ZCO1FBQ0E7UUFDQSxJQUNFeUQsaUJBQWlCLEtBQUssZUFBZSxJQUNyQ3pELElBQUksQ0FBQ21ULEVBQUUsS0FBSzNQLGtCQUFrQixFQUM5QjtVQUNBK0wsUUFBUSxDQUNOaFMsS0FBSyxDQUFDK0UsS0FBSyxDQUFDLENBQUMsRUFBRXhFLFlBQVksQ0FBQyxHQUFHLEdBQUcsR0FBR1AsS0FBSyxDQUFDK0UsS0FBSyxDQUFDeEUsWUFBWSxDQUMvRCxDQUFDO1VBQ0QrRCxlQUFlLENBQUMvRCxZQUFZLEdBQUcsQ0FBQyxDQUFDO1VBQ2pDO1FBQ0Y7UUFDQWpJLGtCQUFrQixDQUFDbUssSUFBSSxDQUFDbVQsRUFBRSxFQUFFMVQsV0FBVyxDQUFDO1FBQ3hDLElBQUlPLElBQUksQ0FBQ3NKLE1BQU0sS0FBSyxTQUFTLEVBQUU7VUFDN0JsRCx1QkFBdUIsQ0FBQzRILENBQUMsSUFBSWxILElBQUksQ0FBQ0MsR0FBRyxDQUFDRixtQkFBbUIsRUFBRW1ILENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNwRTtRQUNBO01BQ0Y7TUFDQTtNQUNBLE9BQU8sS0FBSztJQUNkO0VBQ0YsQ0FBQyxFQUNEO0lBQ0VxSSxPQUFPLEVBQUUsUUFBUTtJQUNqQm1CLFFBQVEsRUFBRSxDQUFDLENBQUMxTixrQkFBa0IsSUFBSSxDQUFDdkk7RUFDckMsQ0FDRixDQUFDO0VBRUR4TSxRQUFRLENBQUMsQ0FBQ3VqQixJQUFJLEVBQUUxVyxHQUFHLEtBQUs7SUFDdEI7SUFDQTtJQUNBO0lBQ0EsSUFDRWlFLGVBQWUsSUFDZnlCLGFBQWEsSUFDYkUsZ0JBQWdCLElBQ2hCRSxpQkFBaUIsRUFDakI7TUFDQTtJQUNGOztJQUVBO0lBQ0EsSUFBSTFPLFdBQVcsQ0FBQyxDQUFDLEtBQUssT0FBTyxJQUFJVCxpQkFBaUIsQ0FBQytmLElBQUksQ0FBQyxFQUFFO01BQ3hELE1BQU1DLFFBQVEsR0FBRy9mLDBCQUEwQixDQUFDOGYsSUFBSSxDQUFDO01BQ2pELE1BQU1FLFlBQVksR0FBR25sQixnQ0FBZ0MsQ0FBQyxDQUFDO01BQ3ZELE1BQU0wYixHQUFHLEdBQUd5SixZQUFZLEdBQ3RCLENBQUMsSUFBSSxDQUFDLFFBQVE7QUFDdEIsb0JBQW9CLENBQUNELFFBQVEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHO0FBQzNFLFVBQVUsQ0FBQ0MsWUFBWSxDQUFDO0FBQ3hCLFFBQVEsRUFBRSxJQUFJLENBQUMsR0FFUCxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDRCxRQUFRLENBQUMscUJBQXFCLEVBQUUsSUFBSSxDQUMvRDtNQUNEckssZUFBZSxDQUFDO1FBQ2R0TSxHQUFHLEVBQUUsa0JBQWtCO1FBQ3ZCbU4sR0FBRztRQUNIbkIsUUFBUSxFQUFFLFdBQVc7UUFDckJRLFNBQVMsRUFBRTtNQUNiLENBQUMsQ0FBQztNQUNGO0lBQ0Y7O0lBRUE7O0lBRUE7O0lBRUE7SUFDQTtJQUNBO0lBQ0E7SUFDQSxJQUNFdEUsa0JBQWtCLElBQ2xCd08sSUFBSSxJQUNKLENBQUMxVyxHQUFHLENBQUM2VyxJQUFJLElBQ1QsQ0FBQzdXLEdBQUcsQ0FBQzhXLElBQUksSUFDVCxDQUFDOVcsR0FBRyxDQUFDK1csTUFBTSxJQUNYLENBQUMvVyxHQUFHLENBQUNnWCxNQUFNLEVBQ1g7TUFDQXJKLFFBQVEsQ0FBQ2hTLEtBQUssQ0FBQytFLEtBQUssQ0FBQyxDQUFDLEVBQUV4RSxZQUFZLENBQUMsR0FBR3dhLElBQUksR0FBRy9hLEtBQUssQ0FBQytFLEtBQUssQ0FBQ3hFLFlBQVksQ0FBQyxDQUFDO01BQ3pFK0QsZUFBZSxDQUFDL0QsWUFBWSxHQUFHd2EsSUFBSSxDQUFDeFcsTUFBTSxDQUFDO01BQzNDO0lBQ0Y7O0lBRUE7SUFDQSxJQUNFaEUsWUFBWSxLQUFLLENBQUMsS0FDakI4RCxHQUFHLENBQUMrVyxNQUFNLElBQUkvVyxHQUFHLENBQUNpWCxTQUFTLElBQUlqWCxHQUFHLENBQUNrWCxNQUFNLElBQUtsWCxHQUFHLENBQUM2VyxJQUFJLElBQUlILElBQUksS0FBSyxHQUFJLENBQUMsRUFDekU7TUFDQTNhLFlBQVksQ0FBQyxRQUFRLENBQUM7TUFDdEI0QyxXQUFXLENBQUMsS0FBSyxDQUFDO0lBQ3BCOztJQUVBO0lBQ0EsSUFBSUQsUUFBUSxJQUFJL0MsS0FBSyxLQUFLLEVBQUUsS0FBS3FFLEdBQUcsQ0FBQ2lYLFNBQVMsSUFBSWpYLEdBQUcsQ0FBQ2tYLE1BQU0sQ0FBQyxFQUFFO01BQzdEdlksV0FBVyxDQUFDLEtBQUssQ0FBQztJQUNwQjs7SUFFQTtJQUNBO0lBQ0E7SUFDQTtJQUNBOztJQUVBO0lBQ0EsSUFBSXFCLEdBQUcsQ0FBQytXLE1BQU0sRUFBRTtNQUNkO01BQ0EsSUFBSXBWLFdBQVcsQ0FBQytGLE1BQU0sS0FBSyxRQUFRLEVBQUU7UUFDbkM5VCxnQkFBZ0IsQ0FBQ2lLLFdBQVcsQ0FBQztRQUM3QjtNQUNGOztNQUVBO01BQ0EsSUFBSVkscUJBQXFCLElBQUlELHFCQUFxQixFQUFFO1FBQ2xEQSxxQkFBcUIsQ0FBQyxDQUFDO1FBQ3ZCO01BQ0Y7O01BRUE7TUFDQSxJQUFJRSxRQUFRLEVBQUU7UUFDWkMsV0FBVyxDQUFDLEtBQUssQ0FBQztRQUNsQjtNQUNGOztNQUVBO01BQ0E7TUFDQTtNQUNBLElBQUl1SixrQkFBa0IsRUFBRTtRQUN0QjtNQUNGOztNQUVBO01BQ0EsTUFBTXVHLGtCQUFrQixHQUFHak4sY0FBYyxDQUFDdUQsSUFBSSxDQUFDOVQsdUJBQXVCLENBQUM7TUFDdkUsSUFBSXdkLGtCQUFrQixFQUFFO1FBQ3RCLEtBQUtDLHVCQUF1QixDQUFDLENBQUM7UUFDOUI7TUFDRjtNQUVBLElBQUluVCxRQUFRLENBQUMyRSxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUN2RSxLQUFLLElBQUksQ0FBQ04sU0FBUyxFQUFFO1FBQy9DcVgsdUJBQXVCLENBQUMsQ0FBQztNQUMzQjtJQUNGO0lBRUEsSUFBSTFTLEdBQUcsQ0FBQ2dYLE1BQU0sSUFBSXRZLFFBQVEsRUFBRTtNQUMxQkMsV0FBVyxDQUFDLEtBQUssQ0FBQztJQUNwQjtFQUNGLENBQUMsQ0FBQztFQUVGLE1BQU13WSxXQUFXLEdBQUcxYyxjQUFjLENBQUMsQ0FBQztFQUVwQyxNQUFNMmMsZ0JBQWdCLEdBQUdsaEIsaUJBQWlCLENBQUMsQ0FBQyxHQUFHRCxrQkFBa0IsQ0FBQyxDQUFDLEdBQUcsS0FBSztFQUMzRSxNQUFNb2hCLFlBQVksR0FBR25oQixpQkFBaUIsQ0FBQyxDQUFDLEdBQ3BDdU0sVUFBVSxLQUFLek0sbUJBQW1CLENBQUMsQ0FBQyxJQUFJb2hCLGdCQUFnQixDQUFDLEdBQ3pELEtBQUs7RUFFVCxNQUFNRSxnQkFBZ0IsR0FBRzljLG1CQUFtQixDQUFDNmMsWUFBWSxJQUFJLEtBQUssQ0FBQzs7RUFFbkU7RUFDQTtFQUNBO0VBQ0EsTUFBTUUsc0JBQXNCLEdBQUduVixZQUFZLEdBQ3ZDaEIsU0FBUyxHQUNUbkkseUJBQXlCLENBQUMwSixXQUFXLEVBQUVwRixhQUFhLENBQUM7RUFDekR2TixTQUFTLENBQUMsTUFBTTtJQUNkLElBQUksQ0FBQ3VuQixzQkFBc0IsRUFBRTtNQUMzQmhMLGtCQUFrQixDQUFDLGNBQWMsQ0FBQztNQUNsQztJQUNGO0lBQ0FELGVBQWUsQ0FBQztNQUNkdE0sR0FBRyxFQUFFLGNBQWM7TUFDbkIvRCxJQUFJLEVBQUVzYixzQkFBc0I7TUFDNUJ2TCxRQUFRLEVBQUUsTUFBTTtNQUNoQlEsU0FBUyxFQUFFO0lBQ2IsQ0FBQyxDQUFDO0VBQ0osQ0FBQyxFQUFFLENBQUMrSyxzQkFBc0IsRUFBRWpMLGVBQWUsRUFBRUMsa0JBQWtCLENBQUMsQ0FBQztFQUVqRWpiLG9CQUFvQixDQUFDLENBQUM7RUFFdEIsTUFBTWttQixpQkFBaUIsR0FBRzduQixPQUFPLENBQUMsT0FBTyxDQUFDO0VBQ3RDO0VBQ0FpQixXQUFXLENBQUNpUSxDQUFDLElBQUlBLENBQUMsQ0FBQzRXLGlCQUFpQixLQUFLclcsU0FBUyxDQUFDLEdBQ25ELEtBQUs7RUFDVCxNQUFNO0lBQUVzVyxPQUFPO0lBQUVuRjtFQUFLLENBQUMsR0FBRzVmLGVBQWUsQ0FBQyxDQUFDO0VBQzNDLE1BQU1nbEIsZ0JBQWdCLEdBQ3BCRCxPQUFPLEdBQUcsQ0FBQyxHQUFHdG1CLHdCQUF3QixDQUFDc21CLE9BQU8sRUFBRUYsaUJBQWlCLENBQUM7O0VBRXBFO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBLE1BQU1JLGVBQWUsR0FBR3hoQixzQkFBc0IsQ0FBQyxDQUFDLEdBQzVDOE8sSUFBSSxDQUFDQyxHQUFHLENBQ041Rix3QkFBd0IsRUFDeEIyRixJQUFJLENBQUMyUyxLQUFLLENBQUN0RixJQUFJLEdBQUcsQ0FBQyxDQUFDLEdBQUdqVCxtQkFDekIsQ0FBQyxHQUNEOEIsU0FBUztFQUViLE1BQU0wVyxnQkFBZ0IsR0FBRy9uQixXQUFXLENBQ2xDLENBQUNnb0IsQ0FBQyxFQUFFL2tCLFVBQVUsS0FBSztJQUNqQjtJQUNBO0lBQ0E7SUFDQSxJQUFJLENBQUMySSxLQUFLLElBQUkwQyxrQkFBa0IsRUFBRTtJQUNsQyxNQUFNeVEsQ0FBQyxHQUFHMVosTUFBTSxDQUFDNGlCLFFBQVEsQ0FBQ3JjLEtBQUssRUFBRWdjLGdCQUFnQixFQUFFemIsWUFBWSxDQUFDO0lBQ2hFLE1BQU0rYixhQUFhLEdBQUduSixDQUFDLENBQUNvSixvQkFBb0IsQ0FBQ04sZUFBZSxDQUFDO0lBQzdELE1BQU1PLE1BQU0sR0FBR3JKLENBQUMsQ0FBQ3NKLFlBQVksQ0FBQ0MscUJBQXFCLENBQUM7TUFDbERDLElBQUksRUFBRVAsQ0FBQyxDQUFDUSxRQUFRLEdBQUdOLGFBQWE7TUFDaENPLE1BQU0sRUFBRVQsQ0FBQyxDQUFDVTtJQUNaLENBQUMsQ0FBQztJQUNGeFksZUFBZSxDQUFDa1ksTUFBTSxDQUFDO0VBQ3pCLENBQUMsRUFDRCxDQUNFeGMsS0FBSyxFQUNMZ2MsZ0JBQWdCLEVBQ2hCdFosa0JBQWtCLEVBQ2xCbkMsWUFBWSxFQUNaMGIsZUFBZSxDQUVuQixDQUFDO0VBRUQsTUFBTWMscUJBQXFCLEdBQUczb0IsV0FBVyxDQUN2QyxDQUFDNG9CLE1BQWUsQ0FBUixFQUFFLE1BQU0sS0FBSzNiLG1CQUFtQixDQUFDMmIsTUFBTSxJQUFJLElBQUksQ0FBQyxFQUN4RCxDQUFDM2IsbUJBQW1CLENBQ3RCLENBQUM7RUFFRCxNQUFNNGIsV0FBVyxHQUNmakksb0JBQW9CLElBQUlqUCxnQkFBZ0IsR0FDcENBLGdCQUFnQixHQUNoQmdNLGtCQUFrQjs7RUFFeEI7RUFDQSxNQUFNbUwsY0FBYyxHQUFHNW9CLE9BQU8sQ0FBQyxNQUFNMEwsS0FBSyxDQUFDd0gsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUN4SCxLQUFLLENBQUMsQ0FBQzs7RUFFbkU7RUFDQTtFQUNBO0VBQ0EsTUFBTW1kLGlCQUFpQixHQUFHL29CLFdBQVcsQ0FDbkMsQ0FBQ2dwQixLQUFLLEVBQUUsTUFBTSxHQUFHLElBQUksRUFBRUMsT0FBTyxFQUFFcmpCLFdBQVcsR0FBRyxTQUFTLEtBQUs7SUFDMUQsSUFBSXNqQixtQkFBbUIsR0FBRyxLQUFLO0lBQy9CcGIsV0FBVyxDQUFDRSxJQUFJLElBQUk7TUFDbEJrYixtQkFBbUIsR0FDakIvaUIsaUJBQWlCLENBQUMsQ0FBQyxJQUNuQixDQUFDQywwQkFBMEIsQ0FBQzRpQixLQUFLLENBQUMsSUFDbEMsQ0FBQyxDQUFDaGIsSUFBSSxDQUFDMkUsUUFBUTtNQUNqQixPQUFPO1FBQ0wsR0FBRzNFLElBQUk7UUFDUFIsYUFBYSxFQUFFd2IsS0FBSztRQUNwQnhXLHVCQUF1QixFQUFFLElBQUk7UUFDN0I7UUFDQSxJQUFJMFcsbUJBQW1CLElBQUk7VUFBRXZXLFFBQVEsRUFBRTtRQUFNLENBQUM7TUFDaEQsQ0FBQztJQUNILENBQUMsQ0FBQztJQUNGK0Msa0JBQWtCLENBQUMsS0FBSyxDQUFDO0lBQ3pCLE1BQU15VCxpQkFBaUIsR0FBRyxDQUFDelcsVUFBVSxJQUFJLEtBQUssS0FBSyxDQUFDd1csbUJBQW1CO0lBQ3ZFLElBQUloSixPQUFPLEdBQUcsZ0JBQWdCbFosa0JBQWtCLENBQUNnaUIsS0FBSyxDQUFDLEVBQUU7SUFDekQsSUFDRWpqQixvQkFBb0IsQ0FBQ2lqQixLQUFLLEVBQUVHLGlCQUFpQixFQUFFcGlCLG9CQUFvQixDQUFDLENBQUMsQ0FBQyxFQUN0RTtNQUNBbVosT0FBTyxJQUFJLDBCQUEwQjtJQUN2QztJQUNBLElBQUlnSixtQkFBbUIsRUFBRTtNQUN2QmhKLE9BQU8sSUFBSSxrQkFBa0I7SUFDL0I7SUFDQTNELGVBQWUsQ0FBQztNQUNkdE0sR0FBRyxFQUFFLGdCQUFnQjtNQUNyQm1OLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDOEMsT0FBTyxDQUFDLEVBQUUsSUFBSSxDQUFDO01BQzNCakUsUUFBUSxFQUFFLFdBQVc7TUFDckJRLFNBQVMsRUFBRTtJQUNiLENBQUMsQ0FBQztJQUNGOWIsUUFBUSxDQUFDLDJCQUEyQixFQUFFO01BQ3BDcW9CLEtBQUssRUFDSEEsS0FBSyxJQUFJdG9CO0lBQ2IsQ0FBQyxDQUFDO0VBQ0osQ0FBQyxFQUNELENBQUNvTixXQUFXLEVBQUV5TyxlQUFlLEVBQUU3SixVQUFVLENBQzNDLENBQUM7RUFFRCxNQUFNMFcsaUJBQWlCLEdBQUdwcEIsV0FBVyxDQUFDLE1BQU07SUFDMUMwVixrQkFBa0IsQ0FBQyxLQUFLLENBQUM7RUFDM0IsQ0FBQyxFQUFFLEVBQUUsQ0FBQzs7RUFFTjtFQUNBO0VBQ0EsTUFBTTJULGtCQUFrQixHQUFHbnBCLE9BQU8sQ0FBQyxNQUFNO0lBQ3ZDLElBQUksQ0FBQ3VWLGVBQWUsRUFBRSxPQUFPLElBQUk7SUFDakMsT0FDRSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUMvQyxRQUFRLENBQUMsV0FBVyxDQUNWLE9BQU8sQ0FBQyxDQUFDbEQsY0FBYyxDQUFDLENBQ3hCLFlBQVksQ0FBQyxDQUFDQyx1QkFBdUIsQ0FBQyxDQUN0QyxRQUFRLENBQUMsQ0FBQ3VXLGlCQUFpQixDQUFDLENBQzVCLFFBQVEsQ0FBQyxDQUFDSyxpQkFBaUIsQ0FBQyxDQUM1QixtQkFBbUIsQ0FDbkIsa0JBQWtCLENBQUMsQ0FDakJqakIsaUJBQWlCLENBQUMsQ0FBQyxJQUNuQnVNLFVBQVUsSUFDVnRNLDBCQUEwQixDQUFDbU0sY0FBYyxDQUFDLElBQzFDdE0sbUJBQW1CLENBQUMsQ0FDdEIsQ0FBQztBQUVYLE1BQU0sRUFBRSxHQUFHLENBQUM7RUFFVixDQUFDLEVBQUUsQ0FDRHdQLGVBQWUsRUFDZmxELGNBQWMsRUFDZEMsdUJBQXVCLEVBQ3ZCdVcsaUJBQWlCLEVBQ2pCSyxpQkFBaUIsQ0FDbEIsQ0FBQztFQUVGLE1BQU1FLG9CQUFvQixHQUFHdHBCLFdBQVcsQ0FDdEMsQ0FBQzBMLE1BQWUsQ0FBUixFQUFFLE1BQU0sS0FBSztJQUNuQndLLHFCQUFxQixDQUFDLEtBQUssQ0FBQztJQUM1QixJQUFJeEssTUFBTSxFQUFFO01BQ1Y2USxlQUFlLENBQUM7UUFDZHRNLEdBQUcsRUFBRSxtQkFBbUI7UUFDeEJtTixHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQzFSLE1BQU0sQ0FBQyxFQUFFLElBQUksQ0FBQztRQUMxQnVRLFFBQVEsRUFBRSxXQUFXO1FBQ3JCUSxTQUFTLEVBQUU7TUFDYixDQUFDLENBQUM7SUFDSjtFQUNGLENBQUMsRUFDRCxDQUFDRixlQUFlLENBQ2xCLENBQUM7O0VBRUQ7RUFDQSxNQUFNZ04scUJBQXFCLEdBQUdycEIsT0FBTyxDQUFDLE1BQU07SUFDMUMsSUFBSSxDQUFDK1Ysa0JBQWtCLEVBQUUsT0FBTyxJQUFJO0lBQ3BDLE9BQ0UsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDL0MsUUFBUSxDQUFDLGNBQWMsQ0FDYixNQUFNLENBQUMsQ0FBQ3FULG9CQUFvQixDQUFDLENBQzdCLGlCQUFpQixDQUFDLENBQUN0akIsNEJBQTRCLENBQUMsQ0FBQyxDQUFDO0FBRTVELE1BQU0sRUFBRSxHQUFHLENBQUM7RUFFVixDQUFDLEVBQUUsQ0FBQ2lRLGtCQUFrQixFQUFFcVQsb0JBQW9CLENBQUMsQ0FBQzs7RUFFOUM7RUFDQSxNQUFNRSxvQkFBb0IsR0FBR3hwQixXQUFXLENBQ3RDLENBQUN5cEIsT0FBTyxFQUFFLE9BQU8sS0FBSztJQUNwQjNiLFdBQVcsQ0FBQ0UsSUFBSSxLQUFLO01BQ25CLEdBQUdBLElBQUk7TUFDUHlFLGVBQWUsRUFBRWdYO0lBQ25CLENBQUMsQ0FBQyxDQUFDO0lBQ0hyVCxxQkFBcUIsQ0FBQyxLQUFLLENBQUM7SUFDNUJ6VixRQUFRLENBQUMsK0JBQStCLEVBQUU7TUFBRThvQjtJQUFRLENBQUMsQ0FBQztJQUN0RGxOLGVBQWUsQ0FBQztNQUNkdE0sR0FBRyxFQUFFLHlCQUF5QjtNQUM5Qm1OLEdBQUcsRUFDRCxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQ3FNLE9BQU8sR0FBRyxZQUFZLEdBQUdwWSxTQUFTLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDb1ksT0FBTyxDQUFDO0FBQzlFLHFCQUFxQixDQUFDQSxPQUFPLEdBQUcsSUFBSSxHQUFHLEtBQUs7QUFDNUMsVUFBVSxFQUFFLElBQUksQ0FDUDtNQUNEeE4sUUFBUSxFQUFFLFdBQVc7TUFDckJRLFNBQVMsRUFBRTtJQUNiLENBQUMsQ0FBQztFQUNKLENBQUMsRUFDRCxDQUFDM08sV0FBVyxFQUFFeU8sZUFBZSxDQUMvQixDQUFDO0VBRUQsTUFBTW1OLG9CQUFvQixHQUFHMXBCLFdBQVcsQ0FBQyxNQUFNO0lBQzdDb1cscUJBQXFCLENBQUMsS0FBSyxDQUFDO0VBQzlCLENBQUMsRUFBRSxFQUFFLENBQUM7O0VBRU47RUFDQSxNQUFNdVQscUJBQXFCLEdBQUd6cEIsT0FBTyxDQUFDLE1BQU07SUFDMUMsSUFBSSxDQUFDaVcsa0JBQWtCLEVBQUUsT0FBTyxJQUFJO0lBQ3BDLE9BQ0UsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDL0MsUUFBUSxDQUFDLGNBQWMsQ0FDYixZQUFZLENBQUMsQ0FBQzFELGVBQWUsSUFBSSxJQUFJLENBQUMsQ0FDdEMsUUFBUSxDQUFDLENBQUMrVyxvQkFBb0IsQ0FBQyxDQUMvQixRQUFRLENBQUMsQ0FBQ0Usb0JBQW9CLENBQUMsQ0FDL0IsaUJBQWlCLENBQUMsQ0FBQ2xlLFFBQVEsQ0FBQ3dKLElBQUksQ0FBQzRVLENBQUMsSUFBSUEsQ0FBQyxDQUFDbEssSUFBSSxLQUFLLFdBQVcsQ0FBQyxDQUFDO0FBRXhFLE1BQU0sRUFBRSxHQUFHLENBQUM7RUFFVixDQUFDLEVBQUUsQ0FDRHZKLGtCQUFrQixFQUNsQjFELGVBQWUsRUFDZitXLG9CQUFvQixFQUNwQkUsb0JBQW9CLEVBQ3BCbGUsUUFBUSxDQUFDMkUsTUFBTSxDQUNoQixDQUFDOztFQUVGO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsTUFBTTBaLG1CQUFtQixHQUFHM3BCLE9BQU8sQ0FDakMsTUFDRU4sT0FBTyxDQUFDLHVCQUF1QixDQUFDLElBQUl5VyxpQkFBaUIsR0FDbkQsQ0FBQyxtQkFBbUIsQ0FDbEIsUUFBUSxDQUFDLENBQUMwTyx5QkFBeUIsQ0FBQyxDQUNwQyxTQUFTLENBQUMsQ0FBQ0UsMEJBQTBCLENBQUMsR0FDdEMsR0FDQSxJQUFJLEVBQ1YsQ0FBQzVPLGlCQUFpQixFQUFFME8seUJBQXlCLEVBQUVFLDBCQUEwQixDQUMzRSxDQUFDO0VBQ0RuakIseUJBQXlCLENBQ3ZCdUUsc0JBQXNCLENBQUMsQ0FBQyxHQUFHd2pCLG1CQUFtQixHQUFHLElBQ25ELENBQUM7RUFFRCxJQUFJN2MsZ0JBQWdCLEVBQUU7SUFDcEIsT0FDRSxDQUFDLHFCQUFxQixDQUNwQixNQUFNLENBQUMsQ0FBQyxNQUFNQyxtQkFBbUIsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUN6QyxjQUFjLENBQUMsQ0FBQ0csaUJBQWlCLENBQy9CNUIsUUFBUSxFQUNSLEVBQUUsRUFDRixJQUFJK0IsZUFBZSxDQUFDLENBQUMsRUFDckJDLGFBQ0YsQ0FBQyxDQUFDLENBQ0YsbUJBQW1CLENBQUMsQ0FDbEIsT0FBT1IsZ0JBQWdCLEtBQUssUUFBUSxHQUFHQSxnQkFBZ0IsR0FBR3FFLFNBQzVELENBQUMsR0FDRDtFQUVOO0VBRUEsSUFBSW5NLG9CQUFvQixDQUFDLENBQUMsSUFBSWdQLGVBQWUsRUFBRTtJQUM3QyxPQUNFLENBQUMsV0FBVyxDQUNWLFlBQVksQ0FBQyxDQUFDZ0QsV0FBVyxDQUFDLENBQzFCLE1BQU0sQ0FBQyxDQUFDLE1BQU07TUFDWi9DLGtCQUFrQixDQUFDLEtBQUssQ0FBQztJQUMzQixDQUFDLENBQUMsR0FDRjtFQUVOO0VBRUEsSUFBSXZVLE9BQU8sQ0FBQyxjQUFjLENBQUMsRUFBRTtJQUMzQixNQUFNa3FCLGlCQUFpQixHQUFHQSxDQUFDNWQsSUFBSSxFQUFFLE1BQU0sS0FBSztNQUMxQyxNQUFNb1gsVUFBVSxHQUFHMVgsS0FBSyxDQUFDTyxZQUFZLEdBQUcsQ0FBQyxDQUFDLElBQUksR0FBRztNQUNqRHdWLGtCQUFrQixDQUFDLElBQUksQ0FBQ25SLElBQUksQ0FBQzhTLFVBQVUsQ0FBQyxHQUFHcFgsSUFBSSxHQUFHLElBQUlBLElBQUksRUFBRSxDQUFDO0lBQy9ELENBQUM7SUFDRCxJQUFJeUosYUFBYSxFQUFFO01BQ2pCLE9BQ0UsQ0FBQyxlQUFlLENBQ2QsTUFBTSxDQUFDLENBQUMsTUFBTUMsZ0JBQWdCLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FDdEMsUUFBUSxDQUFDLENBQUNrVSxpQkFBaUIsQ0FBQyxHQUM1QjtJQUVOO0lBQ0EsSUFBSWpVLGdCQUFnQixFQUFFO01BQ3BCLE9BQ0UsQ0FBQyxrQkFBa0IsQ0FDakIsTUFBTSxDQUFDLENBQUMsTUFBTUMsbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FDekMsUUFBUSxDQUFDLENBQUNnVSxpQkFBaUIsQ0FBQyxHQUM1QjtJQUVOO0VBQ0Y7RUFFQSxJQUFJbHFCLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJbVcsaUJBQWlCLEVBQUU7SUFDbEQsT0FDRSxDQUFDLG1CQUFtQixDQUNsQixZQUFZLENBQUMsQ0FBQ25LLEtBQUssQ0FBQyxDQUNwQixRQUFRLENBQUMsQ0FBQ2lJLEtBQUssSUFBSTtNQUNqQixNQUFNa1csU0FBUyxHQUFHamdCLGdCQUFnQixDQUFDK0osS0FBSyxDQUFDQyxPQUFPLENBQUM7TUFDakQsTUFBTWhJLEtBQUssR0FBRy9CLGlCQUFpQixDQUFDOEosS0FBSyxDQUFDQyxPQUFPLENBQUM7TUFDOUM5SCxZQUFZLENBQUMrZCxTQUFTLENBQUM7TUFDdkJ6WixnQkFBZ0IsQ0FBQ3hFLEtBQUssQ0FBQztNQUN2QmEsaUJBQWlCLENBQUNrSCxLQUFLLENBQUN6SCxjQUFjLENBQUM7TUFDdkM4RCxlQUFlLENBQUNwRSxLQUFLLENBQUNxRSxNQUFNLENBQUM7TUFDN0I2RixvQkFBb0IsQ0FBQyxLQUFLLENBQUM7SUFDN0IsQ0FBQyxDQUFDLENBQ0YsUUFBUSxDQUFDLENBQUMsTUFBTUEsb0JBQW9CLENBQUMsS0FBSyxDQUFDLENBQUMsR0FDNUM7RUFFTjs7RUFFQTtFQUNBLElBQUlxVCxrQkFBa0IsRUFBRTtJQUN0QixPQUFPQSxrQkFBa0I7RUFDM0I7RUFFQSxJQUFJRSxxQkFBcUIsRUFBRTtJQUN6QixPQUFPQSxxQkFBcUI7RUFDOUI7RUFFQSxJQUFJSSxxQkFBcUIsRUFBRTtJQUN6QixPQUFPQSxxQkFBcUI7RUFDOUI7RUFFQSxJQUFJdlYsZ0JBQWdCLEVBQUU7SUFDcEIsT0FDRSxDQUFDLFlBQVksQ0FDWCxNQUFNLENBQUMsQ0FBQyxNQUFNO01BQ1pDLG1CQUFtQixDQUFDLEtBQUssQ0FBQztNQUMxQm9FLGdCQUFnQixDQUFDLElBQUksQ0FBQztJQUN4QixDQUFDLENBQUMsR0FDRjtFQUVOO0VBRUEsTUFBTXVSLFNBQVMsRUFBRWpsQixrQkFBa0IsR0FBRztJQUNwQ2tsQixTQUFTLEVBQUUsSUFBSTtJQUNmeGMsUUFBUTtJQUNSbVEsUUFBUTtJQUNSOVIsS0FBSyxFQUFFNkgsWUFBWSxHQUNmNUosaUJBQWlCLENBQ2YsT0FBTzRKLFlBQVksS0FBSyxRQUFRLEdBQzVCQSxZQUFZLEdBQ1pBLFlBQVksQ0FBQ0csT0FDbkIsQ0FBQyxHQUNEbEksS0FBSztJQUNUO0lBQ0E7SUFDQTtJQUNBO0lBQ0F1UyxXQUFXLEVBQUVLLGVBQWU7SUFDNUJKLGFBQWEsRUFBRVEsaUJBQWlCO0lBQ2hDc0wsY0FBYyxFQUFFaE0sWUFBWTtJQUM1QjJLLFdBQVc7SUFDWDFiLE1BQU07SUFDTmdkLGFBQWEsRUFBRUEsQ0FBQ2pkLElBQUksRUFBRStDLEdBQUcsS0FBS0QsY0FBYyxDQUFDO01BQUU5QyxJQUFJO01BQUUrQztJQUFJLENBQUMsQ0FBQztJQUMzRCtRLFlBQVk7SUFDWjJHLE9BQU8sRUFBRUMsZ0JBQWdCO0lBQ3pCQyxlQUFlO0lBQ2Z1QyxrQ0FBa0MsRUFDaEMzTCxXQUFXLENBQUN0TyxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQ2dJLGtCQUFrQjtJQUNoRGtTLHdCQUF3QixFQUFFNUwsV0FBVyxDQUFDdE8sTUFBTSxHQUFHLENBQUM7SUFDaERoRSxZQUFZO0lBQ1ptZSxvQkFBb0IsRUFBRXBhLGVBQWU7SUFDckNxYSxPQUFPLEVBQUV0SSxXQUFXO0lBQ3BCdUksaUJBQWlCLEVBQUVsVixZQUFZO0lBQy9CbVYsS0FBSyxFQUFFLENBQUNuYyxrQkFBa0IsSUFBSSxDQUFDc0Isb0JBQW9CLElBQUksQ0FBQ3VJLGtCQUFrQjtJQUMxRXVTLFVBQVUsRUFDUixDQUFDdlMsa0JBQWtCLElBQUksQ0FBQzdKLGtCQUFrQixJQUFJLENBQUNxTixpQkFBaUI7SUFDbEVnUCxZQUFZLEVBQUV4TCxtQkFBbUI7SUFDakN5TCxNQUFNLEVBQUVyTixPQUFPLEdBQ1gsTUFBTTtNQUNKLE1BQU1pRyxhQUFhLEdBQUdsRyxJQUFJLENBQUMsQ0FBQztNQUM1QixJQUFJa0csYUFBYSxFQUFFO1FBQ2pCbFQsZ0JBQWdCLENBQUNrVCxhQUFhLENBQUN0WCxJQUFJLENBQUM7UUFDcENnRSxlQUFlLENBQUNzVCxhQUFhLENBQUNyWCxZQUFZLENBQUM7UUFDM0NRLGlCQUFpQixDQUFDNlcsYUFBYSxDQUFDcFgsY0FBYyxDQUFDO01BQ2pEO0lBQ0YsQ0FBQyxHQUNEaUYsU0FBUztJQUNib0osVUFBVSxFQUFFcUIsa0JBQWtCO0lBQzlCMkUsZUFBZTtJQUNmb0ssV0FBVyxFQUFFcEk7RUFDZixDQUFDO0VBRUQsTUFBTXFJLGNBQWMsR0FBR0EsQ0FBQSxDQUFFLEVBQUUsTUFBTXhpQixLQUFLLElBQUk7SUFDeEMsTUFBTXlpQixVQUFVLEVBQUUxZSxNQUFNLENBQUMsTUFBTSxFQUFFLE1BQU0vRCxLQUFLLENBQUMsR0FBRztNQUM5QzBpQixJQUFJLEVBQUU7SUFDUixDQUFDOztJQUVEO0lBQ0EsSUFBSUQsVUFBVSxDQUFDaGYsSUFBSSxDQUFDLEVBQUU7TUFDcEIsT0FBT2dmLFVBQVUsQ0FBQ2hmLElBQUksQ0FBQztJQUN6Qjs7SUFFQTtJQUNBLElBQUk1RCxtQkFBbUIsQ0FBQyxDQUFDLEVBQUU7TUFDekIsT0FBTyxjQUFjO0lBQ3ZCOztJQUVBO0lBQ0EsTUFBTThpQixpQkFBaUIsR0FBRy9pQixnQkFBZ0IsQ0FBQyxDQUFDO0lBQzVDLElBQ0UraUIsaUJBQWlCLElBQ2pCdm1CLFlBQVksQ0FBQzBPLFFBQVEsQ0FBQzZYLGlCQUFpQixJQUFJdG1CLGNBQWMsQ0FBQyxFQUMxRDtNQUNBLE9BQU9GLDBCQUEwQixDQUFDd21CLGlCQUFpQixJQUFJdG1CLGNBQWMsQ0FBQztJQUN4RTtJQUVBLE9BQU8sY0FBYztFQUN2QixDQUFDO0VBRUQsSUFBSTRRLHNCQUFzQixFQUFFO0lBQzFCLE9BQ0UsQ0FBQyxHQUFHLENBQ0YsYUFBYSxDQUFDLEtBQUssQ0FDbkIsVUFBVSxDQUFDLFFBQVEsQ0FDbkIsY0FBYyxDQUFDLFFBQVEsQ0FDdkIsV0FBVyxDQUFDLENBQUN1VixjQUFjLENBQUMsQ0FBQyxDQUFDLENBQzlCLFdBQVcsQ0FBQyxPQUFPLENBQ25CLFVBQVUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUNsQixXQUFXLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FDbkIsWUFBWSxDQUNaLEtBQUssQ0FBQyxNQUFNO0FBRXBCLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU07QUFDN0I7QUFDQSxRQUFRLEVBQUUsSUFBSTtBQUNkLE1BQU0sRUFBRSxHQUFHLENBQUM7RUFFVjtFQUVBLE1BQU1JLGdCQUFnQixHQUFHdGdCLGdCQUFnQixDQUFDLENBQUMsR0FDekMsQ0FBQyxZQUFZLENBQ1gsSUFBSW9mLFNBQVMsQ0FBQyxDQUNkLFdBQVcsQ0FBQyxDQUFDbGQsT0FBTyxDQUFDLENBQ3JCLFlBQVksQ0FBQyxDQUFDQyxVQUFVLENBQUMsR0FDekIsR0FFRixDQUFDLFNBQVMsQ0FBQyxJQUFJaWQsU0FBUyxDQUFDLEdBQzFCO0VBRUQsT0FDRSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDM1gsWUFBWSxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7QUFDaEUsTUFBTSxDQUFDLENBQUNoTSxzQkFBc0IsQ0FBQyxDQUFDLElBQUksQ0FBQyx5QkFBeUIsR0FBRztBQUNqRSxNQUFNLENBQUN3SSxvQkFBb0IsSUFDbkIsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3pDLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLHVCQUF1QixFQUFFLElBQUk7QUFDdEQsUUFBUSxFQUFFLEdBQUcsQ0FDTjtBQUNQLE1BQU0sQ0FBQyxzQkFBc0IsQ0FBQyxRQUFRLENBQUMsQ0FBQzVDLGFBQWEsS0FBS29GLFNBQVMsQ0FBQztBQUNwRSxNQUFNLENBQUMrVixXQUFXLEdBQ1Y7QUFDUixVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDQSxXQUFXLENBQUMrRCxPQUFPLENBQUM7QUFDM0MsWUFBWSxDQUFDL0QsV0FBVyxDQUFDbGIsSUFBSSxHQUNmO0FBQ2QsZ0JBQWdCLENBQUMsR0FBRyxDQUFDa2YsTUFBTSxDQUNUalcsSUFBSSxDQUFDQyxHQUFHLENBQUMsQ0FBQyxFQUFFdVMsT0FBTyxHQUFHNWtCLFdBQVcsQ0FBQ3FrQixXQUFXLENBQUNsYixJQUFJLENBQUMsR0FBRyxDQUFDLENBQ3pELENBQUM7QUFDakIsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxDQUFDa2IsV0FBVyxDQUFDK0QsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLGFBQWE7QUFDL0Usa0JBQWtCLENBQUMsR0FBRztBQUN0QixrQkFBa0IsQ0FBQy9ELFdBQVcsQ0FBQ2xiLElBQUksQ0FBQyxDQUFDLEdBQUc7QUFDeEMsZ0JBQWdCLEVBQUUsSUFBSTtBQUN0QixnQkFBZ0IsQ0FBQyxJQUFJO0FBQ3JCLGNBQWMsR0FBRyxHQUVILEdBQUcsQ0FBQ2tmLE1BQU0sQ0FBQ3pELE9BQU8sQ0FDbkI7QUFDYixVQUFVLEVBQUUsSUFBSTtBQUNoQixVQUFVLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU07QUFDL0MsWUFBWSxDQUFDLHdCQUF3QixDQUN2QixJQUFJLENBQUMsQ0FBQzViLElBQUksQ0FBQyxDQUNYLFNBQVMsQ0FBQyxDQUFDVCxTQUFTLENBQUMsQ0FDckIsZ0JBQWdCLENBQUMsQ0FBQ3lILGdCQUFnQixDQUFDLENBQ25DLGlCQUFpQixDQUFDLENBQUNHLGlCQUFpQixDQUFDO0FBRW5ELFlBQVksQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUM2VSxnQkFBZ0IsQ0FBQztBQUN2RSxjQUFjLENBQUNtRCxnQkFBZ0I7QUFDL0IsWUFBWSxFQUFFLEdBQUc7QUFDakIsVUFBVSxFQUFFLEdBQUc7QUFDZixVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDOUQsV0FBVyxDQUFDK0QsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUNDLE1BQU0sQ0FBQ3pELE9BQU8sQ0FBQyxDQUFDLEVBQUUsSUFBSTtBQUN2RSxRQUFRLEdBQUcsR0FFSCxDQUFDLEdBQUcsQ0FDRixhQUFhLENBQUMsS0FBSyxDQUNuQixVQUFVLENBQUMsWUFBWSxDQUN2QixjQUFjLENBQUMsWUFBWSxDQUMzQixXQUFXLENBQUMsQ0FBQ21ELGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FDOUIsV0FBVyxDQUFDLE9BQU8sQ0FDbkIsVUFBVSxDQUFDLENBQUMsS0FBSyxDQUFDLENBQ2xCLFdBQVcsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUNuQixZQUFZLENBQ1osS0FBSyxDQUFDLE1BQU0sQ0FDWixVQUFVLENBQUMsQ0FBQ08sZUFBZSxDQUN6Qi9ELFlBQVksSUFBSSxLQUFLLEVBQ3JCQyxnQkFBZ0IsRUFDaEJGLGdCQUNGLENBQUMsQ0FBQztBQUVaLFVBQVUsQ0FBQyx3QkFBd0IsQ0FDdkIsSUFBSSxDQUFDLENBQUN0YixJQUFJLENBQUMsQ0FDWCxTQUFTLENBQUMsQ0FBQ1QsU0FBUyxDQUFDLENBQ3JCLGdCQUFnQixDQUFDLENBQUN5SCxnQkFBZ0IsQ0FBQyxDQUNuQyxpQkFBaUIsQ0FBQyxDQUFDRyxpQkFBaUIsQ0FBQztBQUVqRCxVQUFVLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDNlUsZ0JBQWdCLENBQUM7QUFDckUsWUFBWSxDQUFDbUQsZ0JBQWdCO0FBQzdCLFVBQVUsRUFBRSxHQUFHO0FBQ2YsUUFBUSxFQUFFLEdBQUcsQ0FDTjtBQUNQLE1BQU0sQ0FBQyxpQkFBaUIsQ0FDaEIsWUFBWSxDQUFDLENBQUMvZixZQUFZLENBQUMsQ0FDM0IsS0FBSyxDQUFDLENBQUNMLEtBQUssQ0FBQyxDQUNiLFdBQVcsQ0FBQyxDQUFDaUYsV0FBVyxDQUFDLENBQ3pCLE9BQU8sQ0FBQyxDQUFDbkYsZ0JBQWdCLENBQUMsQ0FBQyxHQUFHa0MsT0FBTyxHQUFHdUUsU0FBUyxDQUFDLENBQ2xELElBQUksQ0FBQyxDQUFDdEYsSUFBSSxDQUFDLENBQ1gsaUJBQWlCLENBQUMsQ0FBQ0osaUJBQWlCLENBQUMsQ0FDckMsY0FBYyxDQUFDLENBQUNrRSxjQUFjLENBQUMsQ0FDL0IsT0FBTyxDQUFDLENBQUN0RSxPQUFPLENBQUMsQ0FDakIsbUJBQW1CLENBQUMsQ0FBQ0UsbUJBQW1CLENBQUMsQ0FDekMsa0JBQWtCLENBQUMsQ0FBQ3FFLGlCQUFpQixDQUFDLENBQ3RDLFdBQVcsQ0FBQyxDQUFDMk8sV0FBVyxDQUFDLENBQ3pCLGtCQUFrQixDQUFDLENBQUNTLGtCQUFrQixDQUFDLENBQ3ZDLGNBQWMsQ0FBQyxDQUFDd0IsY0FBYyxDQUFDLENBQy9CLHFCQUFxQixDQUFDLENBQUNuTiw4QkFBOEIsQ0FBQyxDQUN0RCxRQUFRLENBQUMsQ0FBQzVFLFFBQVEsQ0FBQyxDQUNuQixZQUFZLENBQUMsQ0FBQy9DLEtBQUssQ0FBQ3VFLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FDL0IsU0FBUyxDQUFDLENBQUM3RSxTQUFTLENBQUMsQ0FDckIsYUFBYSxDQUFDLENBQUM4TSxhQUFhLENBQUMsQ0FDN0IsYUFBYSxDQUFDLENBQUNHLGFBQWEsQ0FBQyxDQUM3QixjQUFjLENBQUMsQ0FBQ0MsY0FBYyxDQUFDLENBQy9CLFlBQVksQ0FBQyxDQUFDSCxZQUFZLENBQUMsQ0FDM0IsbUJBQW1CLENBQUMsQ0FBQy9ELG1CQUFtQixDQUFDLENBQ3pDLFlBQVksQ0FBQyxDQUFDdkosWUFBWSxDQUFDLENBQzNCLFVBQVUsQ0FBQyxDQUFDMkIsVUFBVSxDQUFDLENBQ3ZCLFNBQVMsQ0FBQyxDQUFDMkksU0FBUyxDQUFDLENBQ3JCLGNBQWMsQ0FBQyxDQUFDeVQsY0FBYyxDQUFDLENBQy9CLFFBQVEsQ0FBQyxDQUFDdGQsUUFBUSxDQUFDLENBQ25CLFdBQVcsQ0FBQyxDQUFDOEMsa0JBQWtCLENBQUMsQ0FDaEMsWUFBWSxDQUFDLENBQUNtRixZQUFZLENBQUMsQ0FDM0IsZUFBZSxDQUFDLENBQUNDLGVBQWUsQ0FBQyxDQUNqQyxrQkFBa0IsQ0FBQyxDQUFDRSxrQkFBa0IsQ0FBQyxDQUN2QyxpQkFBaUIsQ0FBQyxDQUNoQnZOLHNCQUFzQixDQUFDLENBQUMsR0FBR3NpQixxQkFBcUIsR0FBR3RYLFNBQ3JELENBQUM7QUFFVCxNQUFNLENBQUNoTCxzQkFBc0IsQ0FBQyxDQUFDLEdBQUcsSUFBSSxHQUFHd2pCLG1CQUFtQjtBQUM1RCxNQUFNLENBQUN4akIsc0JBQXNCLENBQUMsQ0FBQztJQUN2QjtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBLENBQUMsR0FBRyxDQUNGLFFBQVEsQ0FBQyxVQUFVLENBQ25CLFNBQVMsQ0FBQyxDQUFDZ00sWUFBWSxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQ2xDLE1BQU0sQ0FBQyxDQUFDb00sV0FBVyxDQUFDdE8sTUFBTSxLQUFLLENBQUMsSUFBSSxDQUFDa0csaUJBQWlCLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUMvRCxLQUFLLENBQUMsTUFBTSxDQUNaLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUNmLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUNoQixhQUFhLENBQUMsUUFBUSxDQUN0QixjQUFjLENBQUMsVUFBVSxDQUN6QixRQUFRLENBQUMsUUFBUTtBQUUzQixVQUFVLENBQUMsYUFBYSxDQUNaLFlBQVksQ0FBQyxDQUFDbEwsWUFBWSxDQUFDLENBQzNCLGlCQUFpQixDQUFDLENBQUNRLGlCQUFpQixDQUFDLENBQ3JDLEtBQUssQ0FBQyxDQUFDYixLQUFLLENBQUMsQ0FDYixjQUFjLENBQUMsQ0FBQytFLGNBQWMsQ0FBQyxDQUMvQixPQUFPLENBQUMsQ0FBQ3RFLE9BQU8sQ0FBQyxDQUNqQixRQUFRLENBQUMsQ0FBQ0MsUUFBUSxDQUFDLENBQ25CLG1CQUFtQixDQUFDLENBQUNDLG1CQUFtQixDQUFDLENBQ3pDLGtCQUFrQixDQUFDLENBQUNxRSxpQkFBaUIsQ0FBQyxDQUN0QyxZQUFZLENBQUMsQ0FBQy9FLFlBQVksQ0FBQyxDQUMzQixVQUFVLENBQUMsQ0FBQzJCLFVBQVUsQ0FBQyxDQUN2QixjQUFjLENBQUMsQ0FBQ29jLGNBQWMsQ0FBQztBQUUzQyxRQUFRLEVBQUUsR0FBRyxDQUFDLEdBQ0osSUFBSTtBQUNkLElBQUksRUFBRSxHQUFHLENBQUM7QUFFVjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVM5VSxpQkFBaUJBLENBQUN4SSxRQUFRLEVBQUUzRyxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sQ0FBQztFQUN0RCxJQUFJeW1CLEtBQUssR0FBRyxDQUFDO0VBQ2IsS0FBSyxNQUFNcEwsT0FBTyxJQUFJMVUsUUFBUSxFQUFFO0lBQzlCLElBQUkwVSxPQUFPLENBQUNSLElBQUksS0FBSyxNQUFNLEVBQUU7TUFDM0I7TUFDQSxJQUFJUSxPQUFPLENBQUNxTCxhQUFhLEVBQUU7UUFDekIsS0FBSyxNQUFNL0osRUFBRSxJQUFJdEIsT0FBTyxDQUFDcUwsYUFBYSxFQUFFO1VBQ3RDLElBQUkvSixFQUFFLEdBQUc4SixLQUFLLEVBQUVBLEtBQUssR0FBRzlKLEVBQUU7UUFDNUI7TUFDRjtNQUNBO01BQ0EsSUFBSWpILEtBQUssQ0FBQ2lSLE9BQU8sQ0FBQ3RMLE9BQU8sQ0FBQ0EsT0FBTyxDQUFDdUIsT0FBTyxDQUFDLEVBQUU7UUFDMUMsS0FBSyxNQUFNZ0ssS0FBSyxJQUFJdkwsT0FBTyxDQUFDQSxPQUFPLENBQUN1QixPQUFPLEVBQUU7VUFDM0MsSUFBSWdLLEtBQUssQ0FBQy9MLElBQUksS0FBSyxNQUFNLEVBQUU7WUFDekIsTUFBTWdNLElBQUksR0FBR3hwQixlQUFlLENBQUN1cEIsS0FBSyxDQUFDdmYsSUFBSSxDQUFDO1lBQ3hDLEtBQUssTUFBTTZQLEdBQUcsSUFBSTJQLElBQUksRUFBRTtjQUN0QixJQUFJM1AsR0FBRyxDQUFDeUYsRUFBRSxHQUFHOEosS0FBSyxFQUFFQSxLQUFLLEdBQUd2UCxHQUFHLENBQUN5RixFQUFFO1lBQ3BDO1VBQ0Y7UUFDRjtNQUNGO0lBQ0Y7RUFDRjtFQUNBLE9BQU84SixLQUFLLEdBQUcsQ0FBQztBQUNsQjtBQUVBLFNBQVNELGVBQWVBLENBQ3RCL0QsWUFBWSxFQUFFLE9BQU8sRUFDckJDLGdCQUFnQixFQUFFLE9BQU8sRUFDekJGLGdCQUFnQixFQUFFLE9BQU8sQ0FDMUIsRUFBRXZrQixpQkFBaUIsR0FBRyxTQUFTLENBQUM7RUFDL0IsSUFBSSxDQUFDd2tCLFlBQVksRUFBRSxPQUFPalcsU0FBUztFQUNuQyxNQUFNc2EsT0FBTyxHQUFHcEUsZ0JBQWdCLEdBQzVCLEdBQUdwZSxpQkFBaUIsQ0FBQyxJQUFJLEVBQUVrZSxnQkFBZ0IsQ0FBQyxJQUFJeG5CLEtBQUssQ0FBQytyQixHQUFHLENBQUMsT0FBTyxDQUFDLEVBQUUsR0FDcEV6aUIsaUJBQWlCLENBQUMsSUFBSSxFQUFFa2UsZ0JBQWdCLENBQUM7RUFDN0MsT0FBTztJQUNMNUYsT0FBTyxFQUFFLElBQUlrSyxPQUFPLEdBQUc7SUFDdkJFLFFBQVEsRUFBRSxLQUFLO0lBQ2ZDLEtBQUssRUFBRSxLQUFLO0lBQ1oxRCxNQUFNLEVBQUU7RUFDVixDQUFDO0FBQ0g7QUFFQSxlQUFlcm9CLEtBQUssQ0FBQ2dzQixJQUFJLENBQUN0YyxXQUFXLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/PromptInput/PromptInputFooter.tsx b/components/PromptInput/PromptInputFooter.tsx new file mode 100644 index 0000000..e50bcdc --- /dev/null +++ b/components/PromptInput/PromptInputFooter.tsx @@ -0,0 +1,191 @@ +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import { memo, type ReactNode, useMemo, useRef } from 'react'; +import { isBridgeEnabled } from '../../bridge/bridgeEnabled.js'; +import { getBridgeStatus } from '../../bridge/bridgeStatusUtil.js'; +import { useSetPromptOverlay } from '../../context/promptOverlayContext.js'; +import type { VerificationStatus } from '../../hooks/useApiKeyVerification.js'; +import type { IDESelection } from '../../hooks/useIdeSelection.js'; +import { useSettings } from '../../hooks/useSettings.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { Box, Text } from '../../ink.js'; +import type { MCPServerConnection } from '../../services/mcp/types.js'; +import { useAppState } from '../../state/AppState.js'; +import type { ToolPermissionContext } from '../../Tool.js'; +import type { Message } from '../../types/message.js'; +import type { PromptInputMode, VimMode } from '../../types/textInputTypes.js'; +import type { AutoUpdaterResult } from '../../utils/autoUpdater.js'; +import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js'; +import { isUndercover } from '../../utils/undercover.js'; +import { CoordinatorTaskPanel, useCoordinatorTaskCount } from '../CoordinatorAgentStatus.js'; +import { getLastAssistantMessageId, StatusLine, statusLineShouldDisplay } from '../StatusLine.js'; +import { Notifications } from './Notifications.js'; +import { PromptInputFooterLeftSide } from './PromptInputFooterLeftSide.js'; +import { PromptInputFooterSuggestions, type SuggestionItem } from './PromptInputFooterSuggestions.js'; +import { PromptInputHelpMenu } from './PromptInputHelpMenu.js'; +type Props = { + apiKeyStatus: VerificationStatus; + debug: boolean; + exitMessage: { + show: boolean; + key?: string; + }; + vimMode: VimMode | undefined; + mode: PromptInputMode; + autoUpdaterResult: AutoUpdaterResult | null; + isAutoUpdating: boolean; + verbose: boolean; + onAutoUpdaterResult: (result: AutoUpdaterResult) => void; + onChangeIsUpdating: (isUpdating: boolean) => void; + suggestions: SuggestionItem[]; + selectedSuggestion: number; + maxColumnWidth?: number; + toolPermissionContext: ToolPermissionContext; + helpOpen: boolean; + suppressHint: boolean; + isLoading: boolean; + tasksSelected: boolean; + teamsSelected: boolean; + bridgeSelected: boolean; + tmuxSelected: boolean; + teammateFooterIndex?: number; + ideSelection: IDESelection | undefined; + mcpClients?: MCPServerConnection[]; + isPasting?: boolean; + isInputWrapped?: boolean; + messages: Message[]; + isSearching: boolean; + historyQuery: string; + setHistoryQuery: (query: string) => void; + historyFailedMatch: boolean; + onOpenTasksDialog?: (taskId?: string) => void; +}; +function PromptInputFooter({ + apiKeyStatus, + debug, + exitMessage, + vimMode, + mode, + autoUpdaterResult, + isAutoUpdating, + verbose, + onAutoUpdaterResult, + onChangeIsUpdating, + suggestions, + selectedSuggestion, + maxColumnWidth, + toolPermissionContext, + helpOpen, + suppressHint: suppressHintFromProps, + isLoading, + tasksSelected, + teamsSelected, + bridgeSelected, + tmuxSelected, + teammateFooterIndex, + ideSelection, + mcpClients, + isPasting = false, + isInputWrapped = false, + messages, + isSearching, + historyQuery, + setHistoryQuery, + historyFailedMatch, + onOpenTasksDialog +}: Props): ReactNode { + const settings = useSettings(); + const { + columns, + rows + } = useTerminalSize(); + const messagesRef = useRef(messages); + messagesRef.current = messages; + const lastAssistantMessageId = useMemo(() => getLastAssistantMessageId(messages), [messages]); + const isNarrow = columns < 80; + // In fullscreen the bottom slot is flexShrink:0, so every row here is a row + // stolen from the ScrollBox. Drop the optional StatusLine first. Non-fullscreen + // has terminal scrollback to absorb overflow, so we never hide StatusLine there. + const isFullscreen = isFullscreenEnvEnabled(); + const isShort = isFullscreen && rows < 24; + + // Pill highlights when tasks is the active footer item AND no specific + // agent row is selected. When coordinatorTaskIndex >= 0 the pointer has + // moved into CoordinatorTaskPanel, so the pill should un-highlight. + // coordinatorTaskCount === 0 covers the bash-only case (no agent rows + // exist, pill is the only selectable item). + const coordinatorTaskCount = useCoordinatorTaskCount(); + const coordinatorTaskIndex = useAppState(s => s.coordinatorTaskIndex); + const pillSelected = tasksSelected && (coordinatorTaskCount === 0 || coordinatorTaskIndex < 0); + + // Hide `? for shortcuts` if the user has a custom status line, or during ctrl-r + const suppressHint = suppressHintFromProps || statusLineShouldDisplay(settings) || isSearching; + // Fullscreen: portal data to FullscreenLayout — see promptOverlayContext.tsx + const overlayData = useMemo(() => isFullscreen && suggestions.length ? { + suggestions, + selectedSuggestion, + maxColumnWidth + } : null, [isFullscreen, suggestions, selectedSuggestion, maxColumnWidth]); + useSetPromptOverlay(overlayData); + if (suggestions.length && !isFullscreen) { + return + + ; + } + if (helpOpen) { + return ; + } + return <> + + + {mode === 'prompt' && !isShort && !exitMessage.show && !isPasting && statusLineShouldDisplay(settings) && } + + + + {isFullscreen ? null : } + {"external" === 'ant' && isUndercover() && undercover} + + + + {"external" === 'ant' && } + ; +} +export default memo(PromptInputFooter); +type BridgeStatusProps = { + bridgeSelected: boolean; +}; +function BridgeStatusIndicator({ + bridgeSelected +}: BridgeStatusProps): React.ReactNode { + if (!feature('BRIDGE_MODE')) return null; + + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const enabled = useAppState(s => s.replBridgeEnabled); + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const connected = useAppState(s_0 => s_0.replBridgeConnected); + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const sessionActive = useAppState(s_1 => s_1.replBridgeSessionActive); + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const reconnecting = useAppState(s_2 => s_2.replBridgeReconnecting); + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const explicit = useAppState(s_3 => s_3.replBridgeExplicit); + + // Failed state is surfaced via notification (useReplBridge), not a footer pill. + if (!isBridgeEnabled() || !enabled) return null; + const status = getBridgeStatus({ + error: undefined, + connected, + sessionActive, + reconnecting + }); + + // For implicit (config-driven) remote, only show the reconnecting state + if (!explicit && status.label !== 'Remote Control reconnecting') { + return null; + } + return + {status.label} + {bridgeSelected && · Enter to view} + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJtZW1vIiwiUmVhY3ROb2RlIiwidXNlTWVtbyIsInVzZVJlZiIsImlzQnJpZGdlRW5hYmxlZCIsImdldEJyaWRnZVN0YXR1cyIsInVzZVNldFByb21wdE92ZXJsYXkiLCJWZXJpZmljYXRpb25TdGF0dXMiLCJJREVTZWxlY3Rpb24iLCJ1c2VTZXR0aW5ncyIsInVzZVRlcm1pbmFsU2l6ZSIsIkJveCIsIlRleHQiLCJNQ1BTZXJ2ZXJDb25uZWN0aW9uIiwidXNlQXBwU3RhdGUiLCJUb29sUGVybWlzc2lvbkNvbnRleHQiLCJNZXNzYWdlIiwiUHJvbXB0SW5wdXRNb2RlIiwiVmltTW9kZSIsIkF1dG9VcGRhdGVyUmVzdWx0IiwiaXNGdWxsc2NyZWVuRW52RW5hYmxlZCIsImlzVW5kZXJjb3ZlciIsIkNvb3JkaW5hdG9yVGFza1BhbmVsIiwidXNlQ29vcmRpbmF0b3JUYXNrQ291bnQiLCJnZXRMYXN0QXNzaXN0YW50TWVzc2FnZUlkIiwiU3RhdHVzTGluZSIsInN0YXR1c0xpbmVTaG91bGREaXNwbGF5IiwiTm90aWZpY2F0aW9ucyIsIlByb21wdElucHV0Rm9vdGVyTGVmdFNpZGUiLCJQcm9tcHRJbnB1dEZvb3RlclN1Z2dlc3Rpb25zIiwiU3VnZ2VzdGlvbkl0ZW0iLCJQcm9tcHRJbnB1dEhlbHBNZW51IiwiUHJvcHMiLCJhcGlLZXlTdGF0dXMiLCJkZWJ1ZyIsImV4aXRNZXNzYWdlIiwic2hvdyIsImtleSIsInZpbU1vZGUiLCJtb2RlIiwiYXV0b1VwZGF0ZXJSZXN1bHQiLCJpc0F1dG9VcGRhdGluZyIsInZlcmJvc2UiLCJvbkF1dG9VcGRhdGVyUmVzdWx0IiwicmVzdWx0Iiwib25DaGFuZ2VJc1VwZGF0aW5nIiwiaXNVcGRhdGluZyIsInN1Z2dlc3Rpb25zIiwic2VsZWN0ZWRTdWdnZXN0aW9uIiwibWF4Q29sdW1uV2lkdGgiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJoZWxwT3BlbiIsInN1cHByZXNzSGludCIsImlzTG9hZGluZyIsInRhc2tzU2VsZWN0ZWQiLCJ0ZWFtc1NlbGVjdGVkIiwiYnJpZGdlU2VsZWN0ZWQiLCJ0bXV4U2VsZWN0ZWQiLCJ0ZWFtbWF0ZUZvb3RlckluZGV4IiwiaWRlU2VsZWN0aW9uIiwibWNwQ2xpZW50cyIsImlzUGFzdGluZyIsImlzSW5wdXRXcmFwcGVkIiwibWVzc2FnZXMiLCJpc1NlYXJjaGluZyIsImhpc3RvcnlRdWVyeSIsInNldEhpc3RvcnlRdWVyeSIsInF1ZXJ5IiwiaGlzdG9yeUZhaWxlZE1hdGNoIiwib25PcGVuVGFza3NEaWFsb2ciLCJ0YXNrSWQiLCJQcm9tcHRJbnB1dEZvb3RlciIsInN1cHByZXNzSGludEZyb21Qcm9wcyIsInNldHRpbmdzIiwiY29sdW1ucyIsInJvd3MiLCJtZXNzYWdlc1JlZiIsImN1cnJlbnQiLCJsYXN0QXNzaXN0YW50TWVzc2FnZUlkIiwiaXNOYXJyb3ciLCJpc0Z1bGxzY3JlZW4iLCJpc1Nob3J0IiwiY29vcmRpbmF0b3JUYXNrQ291bnQiLCJjb29yZGluYXRvclRhc2tJbmRleCIsInMiLCJwaWxsU2VsZWN0ZWQiLCJvdmVybGF5RGF0YSIsImxlbmd0aCIsIkJyaWRnZVN0YXR1c1Byb3BzIiwiQnJpZGdlU3RhdHVzSW5kaWNhdG9yIiwiZW5hYmxlZCIsInJlcGxCcmlkZ2VFbmFibGVkIiwiY29ubmVjdGVkIiwicmVwbEJyaWRnZUNvbm5lY3RlZCIsInNlc3Npb25BY3RpdmUiLCJyZXBsQnJpZGdlU2Vzc2lvbkFjdGl2ZSIsInJlY29ubmVjdGluZyIsInJlcGxCcmlkZ2VSZWNvbm5lY3RpbmciLCJleHBsaWNpdCIsInJlcGxCcmlkZ2VFeHBsaWNpdCIsInN0YXR1cyIsImVycm9yIiwidW5kZWZpbmVkIiwibGFiZWwiLCJjb2xvciJdLCJzb3VyY2VzIjpbIlByb21wdElucHV0Rm9vdGVyLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgbWVtbywgdHlwZSBSZWFjdE5vZGUsIHVzZU1lbW8sIHVzZVJlZiB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgaXNCcmlkZ2VFbmFibGVkIH0gZnJvbSAnLi4vLi4vYnJpZGdlL2JyaWRnZUVuYWJsZWQuanMnXG5pbXBvcnQgeyBnZXRCcmlkZ2VTdGF0dXMgfSBmcm9tICcuLi8uLi9icmlkZ2UvYnJpZGdlU3RhdHVzVXRpbC5qcydcbmltcG9ydCB7IHVzZVNldFByb21wdE92ZXJsYXkgfSBmcm9tICcuLi8uLi9jb250ZXh0L3Byb21wdE92ZXJsYXlDb250ZXh0LmpzJ1xuaW1wb3J0IHR5cGUgeyBWZXJpZmljYXRpb25TdGF0dXMgfSBmcm9tICcuLi8uLi9ob29rcy91c2VBcGlLZXlWZXJpZmljYXRpb24uanMnXG5pbXBvcnQgdHlwZSB7IElERVNlbGVjdGlvbiB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUlkZVNlbGVjdGlvbi5qcydcbmltcG9ydCB7IHVzZVNldHRpbmdzIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlU2V0dGluZ3MuanMnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IE1DUFNlcnZlckNvbm5lY3Rpb24gfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvdHlwZXMuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSB9IGZyb20gJy4uLy4uL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBUb29sUGVybWlzc2lvbkNvbnRleHQgfSBmcm9tICcuLi8uLi9Ub29sLmpzJ1xuaW1wb3J0IHR5cGUgeyBNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB0eXBlIHsgUHJvbXB0SW5wdXRNb2RlLCBWaW1Nb2RlIH0gZnJvbSAnLi4vLi4vdHlwZXMvdGV4dElucHV0VHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IEF1dG9VcGRhdGVyUmVzdWx0IH0gZnJvbSAnLi4vLi4vdXRpbHMvYXV0b1VwZGF0ZXIuanMnXG5pbXBvcnQgeyBpc0Z1bGxzY3JlZW5FbnZFbmFibGVkIH0gZnJvbSAnLi4vLi4vdXRpbHMvZnVsbHNjcmVlbi5qcydcbmltcG9ydCB7IGlzVW5kZXJjb3ZlciB9IGZyb20gJy4uLy4uL3V0aWxzL3VuZGVyY292ZXIuanMnXG5pbXBvcnQge1xuICBDb29yZGluYXRvclRhc2tQYW5lbCxcbiAgdXNlQ29vcmRpbmF0b3JUYXNrQ291bnQsXG59IGZyb20gJy4uL0Nvb3JkaW5hdG9yQWdlbnRTdGF0dXMuanMnXG5pbXBvcnQge1xuICBnZXRMYXN0QXNzaXN0YW50TWVzc2FnZUlkLFxuICBTdGF0dXNMaW5lLFxuICBzdGF0dXNMaW5lU2hvdWxkRGlzcGxheSxcbn0gZnJvbSAnLi4vU3RhdHVzTGluZS5qcydcbmltcG9ydCB7IE5vdGlmaWNhdGlvbnMgfSBmcm9tICcuL05vdGlmaWNhdGlvbnMuanMnXG5pbXBvcnQgeyBQcm9tcHRJbnB1dEZvb3RlckxlZnRTaWRlIH0gZnJvbSAnLi9Qcm9tcHRJbnB1dEZvb3RlckxlZnRTaWRlLmpzJ1xuaW1wb3J0IHtcbiAgUHJvbXB0SW5wdXRGb290ZXJTdWdnZXN0aW9ucyxcbiAgdHlwZSBTdWdnZXN0aW9uSXRlbSxcbn0gZnJvbSAnLi9Qcm9tcHRJbnB1dEZvb3RlclN1Z2dlc3Rpb25zLmpzJ1xuaW1wb3J0IHsgUHJvbXB0SW5wdXRIZWxwTWVudSB9IGZyb20gJy4vUHJvbXB0SW5wdXRIZWxwTWVudS5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgYXBpS2V5U3RhdHVzOiBWZXJpZmljYXRpb25TdGF0dXNcbiAgZGVidWc6IGJvb2xlYW5cbiAgZXhpdE1lc3NhZ2U6IHtcbiAgICBzaG93OiBib29sZWFuXG4gICAga2V5Pzogc3RyaW5nXG4gIH1cbiAgdmltTW9kZTogVmltTW9kZSB8IHVuZGVmaW5lZFxuICBtb2RlOiBQcm9tcHRJbnB1dE1vZGVcbiAgYXV0b1VwZGF0ZXJSZXN1bHQ6IEF1dG9VcGRhdGVyUmVzdWx0IHwgbnVsbFxuICBpc0F1dG9VcGRhdGluZzogYm9vbGVhblxuICB2ZXJib3NlOiBib29sZWFuXG4gIG9uQXV0b1VwZGF0ZXJSZXN1bHQ6IChyZXN1bHQ6IEF1dG9VcGRhdGVyUmVzdWx0KSA9PiB2b2lkXG4gIG9uQ2hhbmdlSXNVcGRhdGluZzogKGlzVXBkYXRpbmc6IGJvb2xlYW4pID0+IHZvaWRcbiAgc3VnZ2VzdGlvbnM6IFN1Z2dlc3Rpb25JdGVtW11cbiAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBudW1iZXJcbiAgbWF4Q29sdW1uV2lkdGg/OiBudW1iZXJcbiAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHRcbiAgaGVscE9wZW46IGJvb2xlYW5cbiAgc3VwcHJlc3NIaW50OiBib29sZWFuXG4gIGlzTG9hZGluZzogYm9vbGVhblxuICB0YXNrc1NlbGVjdGVkOiBib29sZWFuXG4gIHRlYW1zU2VsZWN0ZWQ6IGJvb2xlYW5cbiAgYnJpZGdlU2VsZWN0ZWQ6IGJvb2xlYW5cbiAgdG11eFNlbGVjdGVkOiBib29sZWFuXG4gIHRlYW1tYXRlRm9vdGVySW5kZXg/OiBudW1iZXJcbiAgaWRlU2VsZWN0aW9uOiBJREVTZWxlY3Rpb24gfCB1bmRlZmluZWRcbiAgbWNwQ2xpZW50cz86IE1DUFNlcnZlckNvbm5lY3Rpb25bXVxuICBpc1Bhc3Rpbmc/OiBib29sZWFuXG4gIGlzSW5wdXRXcmFwcGVkPzogYm9vbGVhblxuICBtZXNzYWdlczogTWVzc2FnZVtdXG4gIGlzU2VhcmNoaW5nOiBib29sZWFuXG4gIGhpc3RvcnlRdWVyeTogc3RyaW5nXG4gIHNldEhpc3RvcnlRdWVyeTogKHF1ZXJ5OiBzdHJpbmcpID0+IHZvaWRcbiAgaGlzdG9yeUZhaWxlZE1hdGNoOiBib29sZWFuXG4gIG9uT3BlblRhc2tzRGlhbG9nPzogKHRhc2tJZD86IHN0cmluZykgPT4gdm9pZFxufVxuXG5mdW5jdGlvbiBQcm9tcHRJbnB1dEZvb3Rlcih7XG4gIGFwaUtleVN0YXR1cyxcbiAgZGVidWcsXG4gIGV4aXRNZXNzYWdlLFxuICB2aW1Nb2RlLFxuICBtb2RlLFxuICBhdXRvVXBkYXRlclJlc3VsdCxcbiAgaXNBdXRvVXBkYXRpbmcsXG4gIHZlcmJvc2UsXG4gIG9uQXV0b1VwZGF0ZXJSZXN1bHQsXG4gIG9uQ2hhbmdlSXNVcGRhdGluZyxcbiAgc3VnZ2VzdGlvbnMsXG4gIHNlbGVjdGVkU3VnZ2VzdGlvbixcbiAgbWF4Q29sdW1uV2lkdGgsXG4gIHRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgaGVscE9wZW4sXG4gIHN1cHByZXNzSGludDogc3VwcHJlc3NIaW50RnJvbVByb3BzLFxuICBpc0xvYWRpbmcsXG4gIHRhc2tzU2VsZWN0ZWQsXG4gIHRlYW1zU2VsZWN0ZWQsXG4gIGJyaWRnZVNlbGVjdGVkLFxuICB0bXV4U2VsZWN0ZWQsXG4gIHRlYW1tYXRlRm9vdGVySW5kZXgsXG4gIGlkZVNlbGVjdGlvbixcbiAgbWNwQ2xpZW50cyxcbiAgaXNQYXN0aW5nID0gZmFsc2UsXG4gIGlzSW5wdXRXcmFwcGVkID0gZmFsc2UsXG4gIG1lc3NhZ2VzLFxuICBpc1NlYXJjaGluZyxcbiAgaGlzdG9yeVF1ZXJ5LFxuICBzZXRIaXN0b3J5UXVlcnksXG4gIGhpc3RvcnlGYWlsZWRNYXRjaCxcbiAgb25PcGVuVGFza3NEaWFsb2csXG59OiBQcm9wcyk6IFJlYWN0Tm9kZSB7XG4gIGNvbnN0IHNldHRpbmdzID0gdXNlU2V0dGluZ3MoKVxuICBjb25zdCB7IGNvbHVtbnMsIHJvd3MgfSA9IHVzZVRlcm1pbmFsU2l6ZSgpXG4gIGNvbnN0IG1lc3NhZ2VzUmVmID0gdXNlUmVmKG1lc3NhZ2VzKVxuICBtZXNzYWdlc1JlZi5jdXJyZW50ID0gbWVzc2FnZXNcbiAgY29uc3QgbGFzdEFzc2lzdGFudE1lc3NhZ2VJZCA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZ2V0TGFzdEFzc2lzdGFudE1lc3NhZ2VJZChtZXNzYWdlcyksXG4gICAgW21lc3NhZ2VzXSxcbiAgKVxuICBjb25zdCBpc05hcnJvdyA9IGNvbHVtbnMgPCA4MFxuICAvLyBJbiBmdWxsc2NyZWVuIHRoZSBib3R0b20gc2xvdCBpcyBmbGV4U2hyaW5rOjAsIHNvIGV2ZXJ5IHJvdyBoZXJlIGlzIGEgcm93XG4gIC8vIHN0b2xlbiBmcm9tIHRoZSBTY3JvbGxCb3guIERyb3AgdGhlIG9wdGlvbmFsIFN0YXR1c0xpbmUgZmlyc3QuIE5vbi1mdWxsc2NyZWVuXG4gIC8vIGhhcyB0ZXJtaW5hbCBzY3JvbGxiYWNrIHRvIGFic29yYiBvdmVyZmxvdywgc28gd2UgbmV2ZXIgaGlkZSBTdGF0dXNMaW5lIHRoZXJlLlxuICBjb25zdCBpc0Z1bGxzY3JlZW4gPSBpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKClcbiAgY29uc3QgaXNTaG9ydCA9IGlzRnVsbHNjcmVlbiAmJiByb3dzIDwgMjRcblxuICAvLyBQaWxsIGhpZ2hsaWdodHMgd2hlbiB0YXNrcyBpcyB0aGUgYWN0aXZlIGZvb3RlciBpdGVtIEFORCBubyBzcGVjaWZpY1xuICAvLyBhZ2VudCByb3cgaXMgc2VsZWN0ZWQuIFdoZW4gY29vcmRpbmF0b3JUYXNrSW5kZXggPj0gMCB0aGUgcG9pbnRlciBoYXNcbiAgLy8gbW92ZWQgaW50byBDb29yZGluYXRvclRhc2tQYW5lbCwgc28gdGhlIHBpbGwgc2hvdWxkIHVuLWhpZ2hsaWdodC5cbiAgLy8gY29vcmRpbmF0b3JUYXNrQ291bnQgPT09IDAgY292ZXJzIHRoZSBiYXNoLW9ubHkgY2FzZSAobm8gYWdlbnQgcm93c1xuICAvLyBleGlzdCwgcGlsbCBpcyB0aGUgb25seSBzZWxlY3RhYmxlIGl0ZW0pLlxuICBjb25zdCBjb29yZGluYXRvclRhc2tDb3VudCA9IHVzZUNvb3JkaW5hdG9yVGFza0NvdW50KClcbiAgY29uc3QgY29vcmRpbmF0b3JUYXNrSW5kZXggPSB1c2VBcHBTdGF0ZShzID0+IHMuY29vcmRpbmF0b3JUYXNrSW5kZXgpXG4gIGNvbnN0IHBpbGxTZWxlY3RlZCA9XG4gICAgdGFza3NTZWxlY3RlZCAmJiAoY29vcmRpbmF0b3JUYXNrQ291bnQgPT09IDAgfHwgY29vcmRpbmF0b3JUYXNrSW5kZXggPCAwKVxuXG4gIC8vIEhpZGUgYD8gZm9yIHNob3J0Y3V0c2AgaWYgdGhlIHVzZXIgaGFzIGEgY3VzdG9tIHN0YXR1cyBsaW5lLCBvciBkdXJpbmcgY3RybC1yXG4gIGNvbnN0IHN1cHByZXNzSGludCA9XG4gICAgc3VwcHJlc3NIaW50RnJvbVByb3BzIHx8IHN0YXR1c0xpbmVTaG91bGREaXNwbGF5KHNldHRpbmdzKSB8fCBpc1NlYXJjaGluZ1xuICAvLyBGdWxsc2NyZWVuOiBwb3J0YWwgZGF0YSB0byBGdWxsc2NyZWVuTGF5b3V0IOKAlCBzZWUgcHJvbXB0T3ZlcmxheUNvbnRleHQudHN4XG4gIGNvbnN0IG92ZXJsYXlEYXRhID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgaXNGdWxsc2NyZWVuICYmIHN1Z2dlc3Rpb25zLmxlbmd0aFxuICAgICAgICA/IHsgc3VnZ2VzdGlvbnMsIHNlbGVjdGVkU3VnZ2VzdGlvbiwgbWF4Q29sdW1uV2lkdGggfVxuICAgICAgICA6IG51bGwsXG4gICAgW2lzRnVsbHNjcmVlbiwgc3VnZ2VzdGlvbnMsIHNlbGVjdGVkU3VnZ2VzdGlvbiwgbWF4Q29sdW1uV2lkdGhdLFxuICApXG4gIHVzZVNldFByb21wdE92ZXJsYXkob3ZlcmxheURhdGEpXG5cbiAgaWYgKHN1Z2dlc3Rpb25zLmxlbmd0aCAmJiAhaXNGdWxsc2NyZWVuKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggcGFkZGluZ1g9ezJ9IHBhZGRpbmdZPXswfT5cbiAgICAgICAgPFByb21wdElucHV0Rm9vdGVyU3VnZ2VzdGlvbnNcbiAgICAgICAgICBzdWdnZXN0aW9ucz17c3VnZ2VzdGlvbnN9XG4gICAgICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uPXtzZWxlY3RlZFN1Z2dlc3Rpb259XG4gICAgICAgICAgbWF4Q29sdW1uV2lkdGg9e21heENvbHVtbldpZHRofVxuICAgICAgICAvPlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgaWYgKGhlbHBPcGVuKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxQcm9tcHRJbnB1dEhlbHBNZW51IGRpbUNvbG9yPXt0cnVlfSBmaXhlZFdpZHRoPXt0cnVlfSBwYWRkaW5nWD17Mn0gLz5cbiAgICApXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8Qm94XG4gICAgICAgIGZsZXhEaXJlY3Rpb249e2lzTmFycm93ID8gJ2NvbHVtbicgOiAncm93J31cbiAgICAgICAganVzdGlmeUNvbnRlbnQ9e2lzTmFycm93ID8gJ2ZsZXgtc3RhcnQnIDogJ3NwYWNlLWJldHdlZW4nfVxuICAgICAgICBwYWRkaW5nWD17Mn1cbiAgICAgICAgZ2FwPXtpc05hcnJvdyA/IDAgOiAxfVxuICAgICAgPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBmbGV4U2hyaW5rPXtpc05hcnJvdyA/IDAgOiAxfT5cbiAgICAgICAgICB7bW9kZSA9PT0gJ3Byb21wdCcgJiZcbiAgICAgICAgICAgICFpc1Nob3J0ICYmXG4gICAgICAgICAgICAhZXhpdE1lc3NhZ2Uuc2hvdyAmJlxuICAgICAgICAgICAgIWlzUGFzdGluZyAmJlxuICAgICAgICAgICAgc3RhdHVzTGluZVNob3VsZERpc3BsYXkoc2V0dGluZ3MpICYmIChcbiAgICAgICAgICAgICAgPFN0YXR1c0xpbmVcbiAgICAgICAgICAgICAgICBtZXNzYWdlc1JlZj17bWVzc2FnZXNSZWZ9XG4gICAgICAgICAgICAgICAgbGFzdEFzc2lzdGFudE1lc3NhZ2VJZD17bGFzdEFzc2lzdGFudE1lc3NhZ2VJZH1cbiAgICAgICAgICAgICAgICB2aW1Nb2RlPXt2aW1Nb2RlfVxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8UHJvbXB0SW5wdXRGb290ZXJMZWZ0U2lkZVxuICAgICAgICAgICAgZXhpdE1lc3NhZ2U9e2V4aXRNZXNzYWdlfVxuICAgICAgICAgICAgdmltTW9kZT17dmltTW9kZX1cbiAgICAgICAgICAgIG1vZGU9e21vZGV9XG4gICAgICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQ9e3Rvb2xQZXJtaXNzaW9uQ29udGV4dH1cbiAgICAgICAgICAgIHN1cHByZXNzSGludD17c3VwcHJlc3NIaW50fVxuICAgICAgICAgICAgaXNMb2FkaW5nPXtpc0xvYWRpbmd9XG4gICAgICAgICAgICB0YXNrc1NlbGVjdGVkPXtwaWxsU2VsZWN0ZWR9XG4gICAgICAgICAgICB0ZWFtc1NlbGVjdGVkPXt0ZWFtc1NlbGVjdGVkfVxuICAgICAgICAgICAgdGVhbW1hdGVGb290ZXJJbmRleD17dGVhbW1hdGVGb290ZXJJbmRleH1cbiAgICAgICAgICAgIHRtdXhTZWxlY3RlZD17dG11eFNlbGVjdGVkfVxuICAgICAgICAgICAgaXNQYXN0aW5nPXtpc1Bhc3Rpbmd9XG4gICAgICAgICAgICBpc1NlYXJjaGluZz17aXNTZWFyY2hpbmd9XG4gICAgICAgICAgICBoaXN0b3J5UXVlcnk9e2hpc3RvcnlRdWVyeX1cbiAgICAgICAgICAgIHNldEhpc3RvcnlRdWVyeT17c2V0SGlzdG9yeVF1ZXJ5fVxuICAgICAgICAgICAgaGlzdG9yeUZhaWxlZE1hdGNoPXtoaXN0b3J5RmFpbGVkTWF0Y2h9XG4gICAgICAgICAgICBvbk9wZW5UYXNrc0RpYWxvZz17b25PcGVuVGFza3NEaWFsb2d9XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggZmxleFNocmluaz17MX0gZ2FwPXsxfT5cbiAgICAgICAgICB7aXNGdWxsc2NyZWVuID8gbnVsbCA6IChcbiAgICAgICAgICAgIDxOb3RpZmljYXRpb25zXG4gICAgICAgICAgICAgIGFwaUtleVN0YXR1cz17YXBpS2V5U3RhdHVzfVxuICAgICAgICAgICAgICBhdXRvVXBkYXRlclJlc3VsdD17YXV0b1VwZGF0ZXJSZXN1bHR9XG4gICAgICAgICAgICAgIGRlYnVnPXtkZWJ1Z31cbiAgICAgICAgICAgICAgaXNBdXRvVXBkYXRpbmc9e2lzQXV0b1VwZGF0aW5nfVxuICAgICAgICAgICAgICB2ZXJib3NlPXt2ZXJib3NlfVxuICAgICAgICAgICAgICBtZXNzYWdlcz17bWVzc2FnZXN9XG4gICAgICAgICAgICAgIG9uQXV0b1VwZGF0ZXJSZXN1bHQ9e29uQXV0b1VwZGF0ZXJSZXN1bHR9XG4gICAgICAgICAgICAgIG9uQ2hhbmdlSXNVcGRhdGluZz17b25DaGFuZ2VJc1VwZGF0aW5nfVxuICAgICAgICAgICAgICBpZGVTZWxlY3Rpb249e2lkZVNlbGVjdGlvbn1cbiAgICAgICAgICAgICAgbWNwQ2xpZW50cz17bWNwQ2xpZW50c31cbiAgICAgICAgICAgICAgaXNJbnB1dFdyYXBwZWQ9e2lzSW5wdXRXcmFwcGVkfVxuICAgICAgICAgICAgICBpc05hcnJvdz17aXNOYXJyb3d9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgICl9XG4gICAgICAgICAge1wiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiYgaXNVbmRlcmNvdmVyKCkgJiYgKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+dW5kZXJjb3ZlcjwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICAgIDxCcmlkZ2VTdGF0dXNJbmRpY2F0b3IgYnJpZGdlU2VsZWN0ZWQ9e2JyaWRnZVNlbGVjdGVkfSAvPlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuICAgICAge1wiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiYgPENvb3JkaW5hdG9yVGFza1BhbmVsIC8+fVxuICAgIDwvPlxuICApXG59XG5cbmV4cG9ydCBkZWZhdWx0IG1lbW8oUHJvbXB0SW5wdXRGb290ZXIpXG5cbnR5cGUgQnJpZGdlU3RhdHVzUHJvcHMgPSB7XG4gIGJyaWRnZVNlbGVjdGVkOiBib29sZWFuXG59XG5cbmZ1bmN0aW9uIEJyaWRnZVN0YXR1c0luZGljYXRvcih7XG4gIGJyaWRnZVNlbGVjdGVkLFxufTogQnJpZGdlU3RhdHVzUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoIWZlYXR1cmUoJ0JSSURHRV9NT0RFJykpIHJldHVybiBudWxsXG5cbiAgLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICBjb25zdCBlbmFibGVkID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VFbmFibGVkKVxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gIGNvbnN0IGNvbm5lY3RlZCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlQ29ubmVjdGVkKVxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gIGNvbnN0IHNlc3Npb25BY3RpdmUgPSB1c2VBcHBTdGF0ZShzID0+IHMucmVwbEJyaWRnZVNlc3Npb25BY3RpdmUpXG4gIC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgY29uc3QgcmVjb25uZWN0aW5nID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VSZWNvbm5lY3RpbmcpXG4gIC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgY29uc3QgZXhwbGljaXQgPSB1c2VBcHBTdGF0ZShzID0+IHMucmVwbEJyaWRnZUV4cGxpY2l0KVxuXG4gIC8vIEZhaWxlZCBzdGF0ZSBpcyBzdXJmYWNlZCB2aWEgbm90aWZpY2F0aW9uICh1c2VSZXBsQnJpZGdlKSwgbm90IGEgZm9vdGVyIHBpbGwuXG4gIGlmICghaXNCcmlkZ2VFbmFibGVkKCkgfHwgIWVuYWJsZWQpIHJldHVybiBudWxsXG5cbiAgY29uc3Qgc3RhdHVzID0gZ2V0QnJpZGdlU3RhdHVzKHtcbiAgICBlcnJvcjogdW5kZWZpbmVkLFxuICAgIGNvbm5lY3RlZCxcbiAgICBzZXNzaW9uQWN0aXZlLFxuICAgIHJlY29ubmVjdGluZyxcbiAgfSlcblxuICAvLyBGb3IgaW1wbGljaXQgKGNvbmZpZy1kcml2ZW4pIHJlbW90ZSwgb25seSBzaG93IHRoZSByZWNvbm5lY3Rpbmcgc3RhdGVcbiAgaWYgKCFleHBsaWNpdCAmJiBzdGF0dXMubGFiZWwgIT09ICdSZW1vdGUgQ29udHJvbCByZWNvbm5lY3RpbmcnKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPFRleHRcbiAgICAgIGNvbG9yPXticmlkZ2VTZWxlY3RlZCA/ICdiYWNrZ3JvdW5kJyA6IHN0YXR1cy5jb2xvcn1cbiAgICAgIGludmVyc2U9e2JyaWRnZVNlbGVjdGVkfVxuICAgICAgd3JhcD1cInRydW5jYXRlXCJcbiAgICA+XG4gICAgICB7c3RhdHVzLmxhYmVsfVxuICAgICAge2JyaWRnZVNlbGVjdGVkICYmIDxUZXh0IGRpbUNvbG9yPiDCtyBFbnRlciB0byB2aWV3PC9UZXh0Pn1cbiAgICA8L1RleHQ+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxJQUFJLEVBQUUsS0FBS0MsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLE1BQU0sUUFBUSxPQUFPO0FBQzdELFNBQVNDLGVBQWUsUUFBUSwrQkFBK0I7QUFDL0QsU0FBU0MsZUFBZSxRQUFRLGtDQUFrQztBQUNsRSxTQUFTQyxtQkFBbUIsUUFBUSx1Q0FBdUM7QUFDM0UsY0FBY0Msa0JBQWtCLFFBQVEsc0NBQXNDO0FBQzlFLGNBQWNDLFlBQVksUUFBUSxnQ0FBZ0M7QUFD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 \ No newline at end of file diff --git a/components/PromptInput/PromptInputFooterLeftSide.tsx b/components/PromptInput/PromptInputFooterLeftSide.tsx new file mode 100644 index 0000000..2f1bbd1 --- /dev/null +++ b/components/PromptInput/PromptInputFooterLeftSide.tsx @@ -0,0 +1,517 @@ +import { c as _c } from "react/compiler-runtime"; +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +import { feature } from 'bun:bundle'; +// Dead code elimination: conditional import for COORDINATOR_MODE +/* eslint-disable @typescript-eslint/no-require-imports */ +const coordinatorModule = feature('COORDINATOR_MODE') ? require('../../coordinator/coordinatorMode.js') as typeof import('../../coordinator/coordinatorMode.js') : undefined; +/* eslint-enable @typescript-eslint/no-require-imports */ +import { Box, Text, Link } from '../../ink.js'; +import * as React from 'react'; +import figures from 'figures'; +import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'; +import type { VimMode, PromptInputMode } from '../../types/textInputTypes.js'; +import type { ToolPermissionContext } from '../../Tool.js'; +import { isVimModeEnabled } from './utils.js'; +import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'; +import { isDefaultMode, permissionModeSymbol, permissionModeTitle, getModeColor } from '../../utils/permissions/PermissionMode.js'; +import { BackgroundTaskStatus } from '../tasks/BackgroundTaskStatus.js'; +import { isBackgroundTask } from '../../tasks/types.js'; +import { isPanelAgentTask } from '../../tasks/LocalAgentTask/LocalAgentTask.js'; +import { getVisibleAgentTasks } from '../CoordinatorAgentStatus.js'; +import { count } from '../../utils/array.js'; +import { shouldHideTasksFooter } from '../tasks/taskStatusUtils.js'; +import { isAgentSwarmsEnabled } from '../../utils/agentSwarmsEnabled.js'; +import { TeamStatus } from '../teams/TeamStatus.js'; +import { isInProcessEnabled } from '../../utils/swarm/backends/registry.js'; +import { useAppState, useAppStateStore } from 'src/state/AppState.js'; +import { getIsRemoteMode } from '../../bootstrap/state.js'; +import HistorySearchInput from './HistorySearchInput.js'; +import { usePrStatus } from '../../hooks/usePrStatus.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { Byline } from '../design-system/Byline.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { useTasksV2 } from '../../hooks/useTasksV2.js'; +import { formatDuration } from '../../utils/format.js'; +import { VoiceWarmupHint } from './VoiceIndicator.js'; +import { useVoiceEnabled } from '../../hooks/useVoiceEnabled.js'; +import { useVoiceState } from '../../context/voice.js'; +import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js'; +import { isXtermJs } from '../../ink/terminal.js'; +import { useHasSelection, useSelection } from '../../ink/hooks/use-selection.js'; +import { getGlobalConfig, saveGlobalConfig } from '../../utils/config.js'; +import { getPlatform } from '../../utils/platform.js'; +import { PrBadge } from '../PrBadge.js'; + +// Dead code elimination: conditional import for proactive mode +/* eslint-disable @typescript-eslint/no-require-imports */ +const proactiveModule = feature('PROACTIVE') || feature('KAIROS') ? require('../../proactive/index.js') : null; +/* eslint-enable @typescript-eslint/no-require-imports */ +const NO_OP_SUBSCRIBE = (_cb: () => void) => () => {}; +const NULL = () => null; +const MAX_VOICE_HINT_SHOWS = 3; +type Props = { + exitMessage: { + show: boolean; + key?: string; + }; + vimMode: VimMode | undefined; + mode: PromptInputMode; + toolPermissionContext: ToolPermissionContext; + suppressHint: boolean; + isLoading: boolean; + showMemoryTypeSelector?: boolean; + tasksSelected: boolean; + teamsSelected: boolean; + tmuxSelected: boolean; + teammateFooterIndex?: number; + isPasting?: boolean; + isSearching: boolean; + historyQuery: string; + setHistoryQuery: (query: string) => void; + historyFailedMatch: boolean; + onOpenTasksDialog?: (taskId?: string) => void; +}; +function ProactiveCountdown() { + const $ = _c(7); + const nextTickAt = useSyncExternalStore(proactiveModule?.subscribeToProactiveChanges ?? NO_OP_SUBSCRIBE, proactiveModule?.getNextTickAt ?? NULL, NULL); + const [remainingSeconds, setRemainingSeconds] = useState(null); + let t0; + let t1; + if ($[0] !== nextTickAt) { + t0 = () => { + if (nextTickAt === null) { + setRemainingSeconds(null); + return; + } + const update = function update() { + const remaining = Math.max(0, Math.ceil((nextTickAt - Date.now()) / 1000)); + setRemainingSeconds(remaining); + }; + update(); + const interval = setInterval(update, 1000); + return () => clearInterval(interval); + }; + t1 = [nextTickAt]; + $[0] = nextTickAt; + $[1] = t0; + $[2] = t1; + } else { + t0 = $[1]; + t1 = $[2]; + } + useEffect(t0, t1); + if (remainingSeconds === null) { + return null; + } + const t2 = remainingSeconds * 1000; + let t3; + if ($[3] !== t2) { + t3 = formatDuration(t2, { + mostSignificantOnly: true + }); + $[3] = t2; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== t3) { + t4 = waiting{" "}{t3}; + $[5] = t3; + $[6] = t4; + } else { + t4 = $[6]; + } + return t4; +} +export function PromptInputFooterLeftSide(t0) { + const $ = _c(27); + const { + exitMessage, + vimMode, + mode, + toolPermissionContext, + suppressHint, + isLoading, + tasksSelected, + teamsSelected, + tmuxSelected, + teammateFooterIndex, + isPasting, + isSearching, + historyQuery, + setHistoryQuery, + historyFailedMatch, + onOpenTasksDialog + } = t0; + if (exitMessage.show) { + let t1; + if ($[0] !== exitMessage.key) { + t1 = Press {exitMessage.key} again to exit; + $[0] = exitMessage.key; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + if (isPasting) { + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Pasting text…; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; + } + let t1; + if ($[3] !== isSearching || $[4] !== vimMode) { + t1 = isVimModeEnabled() && vimMode === "INSERT" && !isSearching; + $[3] = isSearching; + $[4] = vimMode; + $[5] = t1; + } else { + t1 = $[5]; + } + const showVim = t1; + let t2; + if ($[6] !== historyFailedMatch || $[7] !== historyQuery || $[8] !== isSearching || $[9] !== setHistoryQuery) { + t2 = isSearching && ; + $[6] = historyFailedMatch; + $[7] = historyQuery; + $[8] = isSearching; + $[9] = setHistoryQuery; + $[10] = t2; + } else { + t2 = $[10]; + } + let t3; + if ($[11] !== showVim) { + t3 = showVim ? -- INSERT -- : null; + $[11] = showVim; + $[12] = t3; + } else { + t3 = $[12]; + } + const t4 = !suppressHint && !showVim; + let t5; + if ($[13] !== isLoading || $[14] !== mode || $[15] !== onOpenTasksDialog || $[16] !== t4 || $[17] !== tasksSelected || $[18] !== teammateFooterIndex || $[19] !== teamsSelected || $[20] !== tmuxSelected || $[21] !== toolPermissionContext) { + t5 = ; + $[13] = isLoading; + $[14] = mode; + $[15] = onOpenTasksDialog; + $[16] = t4; + $[17] = tasksSelected; + $[18] = teammateFooterIndex; + $[19] = teamsSelected; + $[20] = tmuxSelected; + $[21] = toolPermissionContext; + $[22] = t5; + } else { + t5 = $[22]; + } + let t6; + if ($[23] !== t2 || $[24] !== t3 || $[25] !== t5) { + t6 = {t2}{t3}{t5}; + $[23] = t2; + $[24] = t3; + $[25] = t5; + $[26] = t6; + } else { + t6 = $[26]; + } + return t6; +} +type ModeIndicatorProps = { + mode: PromptInputMode; + toolPermissionContext: ToolPermissionContext; + showHint: boolean; + isLoading: boolean; + tasksSelected: boolean; + teamsSelected: boolean; + tmuxSelected: boolean; + teammateFooterIndex?: number; + onOpenTasksDialog?: (taskId?: string) => void; +}; +function ModeIndicator({ + mode, + toolPermissionContext, + showHint, + isLoading, + tasksSelected, + teamsSelected, + tmuxSelected, + teammateFooterIndex, + onOpenTasksDialog +}: ModeIndicatorProps): React.ReactNode { + const { + columns + } = useTerminalSize(); + const modeCycleShortcut = useShortcutDisplay('chat:cycleMode', 'Chat', 'shift+tab'); + const tasks = useAppState(s => s.tasks); + const teamContext = useAppState(s_0 => s_0.teamContext); + // Set once in initialState (main.tsx --remote mode) and never mutated — lazy + // init captures the immutable value without a subscription. + const store = useAppStateStore(); + const [remoteSessionUrl] = useState(() => store.getState().remoteSessionUrl); + const viewSelectionMode = useAppState(s_1 => s_1.viewSelectionMode); + const viewingAgentTaskId = useAppState(s_2 => s_2.viewingAgentTaskId); + const expandedView = useAppState(s_3 => s_3.expandedView); + const showSpinnerTree = expandedView === 'teammates'; + const prStatus = usePrStatus(isLoading, isPrStatusEnabled()); + const hasTmuxSession = useAppState(s_4 => "external" === 'ant' && s_4.tungstenActiveSession !== undefined); + const nextTickAt = useSyncExternalStore(proactiveModule?.subscribeToProactiveChanges ?? NO_OP_SUBSCRIBE, proactiveModule?.getNextTickAt ?? NULL, NULL); + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const voiceEnabled = feature('VOICE_MODE') ? useVoiceEnabled() : false; + const voiceState = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useVoiceState(s_5 => s_5.voiceState) : 'idle' as const; + const voiceWarmingUp = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useVoiceState(s_6 => s_6.voiceWarmingUp) : false; + const hasSelection = useHasSelection(); + const selGetState = useSelection().getState; + const hasNextTick = nextTickAt !== null; + const isCoordinator = feature('COORDINATOR_MODE') ? coordinatorModule?.isCoordinatorMode() === true : false; + const runningTaskCount = useMemo(() => count(Object.values(tasks), t => isBackgroundTask(t) && !("external" === 'ant' && isPanelAgentTask(t))), [tasks]); + const tasksV2 = useTasksV2(); + const hasTaskItems = tasksV2 !== undefined && tasksV2.length > 0; + const escShortcut = useShortcutDisplay('chat:cancel', 'Chat', 'esc').toLowerCase(); + const todosShortcut = useShortcutDisplay('app:toggleTodos', 'Global', 'ctrl+t'); + const killAgentsShortcut = useShortcutDisplay('chat:killAgents', 'Chat', 'ctrl+x ctrl+k'); + const voiceKeyShortcut = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useShortcutDisplay('voice:pushToTalk', 'Chat', 'Space') : ''; + // Captured at mount so the hint doesn't flicker mid-session if another + // CC instance increments the counter. Incremented once via useEffect the + // first time voice is enabled in this session — approximates "hint was + // shown" without tracking the exact render-time condition (which depends + // on parts/hintParts computed after the early-return hooks boundary). + const [voiceHintUnderCap] = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useState(() => (getGlobalConfig().voiceFooterHintSeenCount ?? 0) < MAX_VOICE_HINT_SHOWS) : [false]; + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const voiceHintIncrementedRef = feature('VOICE_MODE') ? useRef(false) : null; + useEffect(() => { + if (feature('VOICE_MODE')) { + if (!voiceEnabled || !voiceHintUnderCap) return; + if (voiceHintIncrementedRef?.current) return; + if (voiceHintIncrementedRef) voiceHintIncrementedRef.current = true; + const newCount = (getGlobalConfig().voiceFooterHintSeenCount ?? 0) + 1; + saveGlobalConfig(prev => { + if ((prev.voiceFooterHintSeenCount ?? 0) >= newCount) return prev; + return { + ...prev, + voiceFooterHintSeenCount: newCount + }; + }); + } + }, [voiceEnabled, voiceHintUnderCap]); + const isKillAgentsConfirmShowing = useAppState(s_7 => s_7.notifications.current?.key === 'kill-agents-confirm'); + + // Derive team info from teamContext (no filesystem I/O needed) + // Match the same logic as TeamStatus to avoid trailing separator + // In-process mode uses Shift+Down/Up navigation, not footer teams menu + const hasTeams = isAgentSwarmsEnabled() && !isInProcessEnabled() && teamContext !== undefined && count(Object.values(teamContext.teammates), t_0 => t_0.name !== 'team-lead') > 0; + if (mode === 'bash') { + return ! for bash mode; + } + const currentMode = toolPermissionContext?.mode; + const hasActiveMode = !isDefaultMode(currentMode); + const viewedTask = viewingAgentTaskId ? tasks[viewingAgentTaskId] : undefined; + const isViewingTeammate = viewSelectionMode === 'viewing-agent' && viewedTask?.type === 'in_process_teammate'; + const isViewingCompletedTeammate = isViewingTeammate && viewedTask != null && viewedTask.status !== 'running'; + const hasBackgroundTasks = runningTaskCount > 0 || isViewingTeammate; + + // Count primary items (permission mode or coordinator mode, background tasks, and teams) + const primaryItemCount = (isCoordinator || hasActiveMode ? 1 : 0) + (hasBackgroundTasks ? 1 : 0) + (hasTeams ? 1 : 0); + + // PR indicator is short (~10 chars) — unlike the old diff indicator the + // >=100 threshold was tuned for. Now that auto mode is effectively the + // baseline, primaryItemCount is ≥1 for most sessions; keep the threshold + // low enough to show PR status on standard 80-col terminals. + const shouldShowPrStatus = isPrStatusEnabled() && prStatus.number !== null && prStatus.reviewState !== null && prStatus.url !== null && primaryItemCount < 2 && (primaryItemCount === 0 || columns >= 80); + + // Hide the shift+tab hint when there are 2 primary items + const shouldShowModeHint = primaryItemCount < 2; + + // Check if we have in-process teammates (showing pills) + // In spinner-tree mode, pills are disabled - teammates appear in the spinner tree instead + const hasInProcessTeammates = !showSpinnerTree && hasBackgroundTasks && Object.values(tasks).some(t_1 => t_1.type === 'in_process_teammate'); + const hasTeammatePills = hasInProcessTeammates || !showSpinnerTree && isViewingTeammate; + + // In remote mode (`claude assistant`, --teleport) the agent runs elsewhere; + // the local permission mode shown here doesn't reflect the agent's state. + // Rendered before the tasks pill so a long pill label (e.g. ultraplan URL) + // doesn't push the mode indicator off-screen. + const modePart = currentMode && hasActiveMode && !getIsRemoteMode() ? + {permissionModeSymbol(currentMode)}{' '} + {permissionModeTitle(currentMode).toLowerCase()} on + {shouldShowModeHint && + {' '} + + } + : null; + + // Build parts array - exclude BackgroundTaskStatus when we have teammate pills + // (teammate pills get their own row) + const parts = [ + // Remote session indicator + ...(remoteSessionUrl ? [ + {figures.circleDouble} remote + ] : []), + // BackgroundTaskStatus is NOT in parts — it renders as a Box sibling so + // its click-target Box isn't nested inside the + // wrapper (reconciler throws on Box-in-Text). + // Tmux pill (ant-only) — appears right after tasks in nav order + ...("external" === 'ant' && hasTmuxSession ? [] : []), ...(isAgentSwarmsEnabled() && hasTeams ? [] : []), ...(shouldShowPrStatus ? [] : [])]; + + // Check if any in-process teammates exist (for hint text cycling) + const hasAnyInProcessTeammates = Object.values(tasks).some(t_2 => t_2.type === 'in_process_teammate' && t_2.status === 'running'); + const hasRunningAgentTasks = Object.values(tasks).some(t_3 => t_3.type === 'local_agent' && t_3.status === 'running'); + + // Get hint parts separately for potential second-line rendering + const hintParts = showHint ? getSpinnerHintParts(isLoading, escShortcut, todosShortcut, killAgentsShortcut, hasTaskItems, expandedView, hasAnyInProcessTeammates, hasRunningAgentTasks, isKillAgentsConfirmShowing) : []; + if (isViewingCompletedTeammate) { + parts.push( + + ); + } else if ((feature('PROACTIVE') || feature('KAIROS')) && hasNextTick) { + parts.push(); + } else if (!hasTeammatePills && showHint) { + parts.push(...hintParts); + } + + // When we have teammate pills, always render them on their own line above other parts + if (hasTeammatePills) { + // Don't append spinner hints when viewing a completed teammate — + // the "esc to return to team lead" hint already replaces "esc to interrupt" + const otherParts = [...(modePart ? [modePart] : []), ...parts, ...(isViewingCompletedTeammate ? [] : hintParts)]; + return + + + + {otherParts.length > 0 && + {otherParts} + } + ; + } + + // Add "↓ to manage tasks" hint when panel has visible rows + const hasCoordinatorTasks = "external" === 'ant' && getVisibleAgentTasks(tasks).length > 0; + + // Tasks pill renders as a Box sibling (not a parts entry) so its + // click-target Box isn't nested inside — the + // reconciler throws on Box-in-Text. Computed here so the empty-checks + // below still treat "pill present" as non-empty. + const tasksPart = hasBackgroundTasks && !hasTeammatePills && !shouldHideTasksFooter(tasks, showSpinnerTree) ? : null; + if (parts.length === 0 && !tasksPart && !modePart && showHint) { + parts.push( + ? for shortcuts + ); + } + + // Only replace the idle voice hint when there's something to say — otherwise + // fall through instead of showing an empty Byline. "esc to clear" was removed + // (looked like "esc to interrupt" when idle; esc-clears-selection is standard + // UX) leaving only ctrl+c (copyOnSelect off) and the xterm.js native-select hint. + const copyOnSelect = getGlobalConfig().copyOnSelect ?? true; + const selectionHintHasContent = hasSelection && (!copyOnSelect || isXtermJs()); + + // Warmup hint takes priority — when the user is actively holding + // the activation key, show feedback regardless of other hints. + if (feature('VOICE_MODE') && voiceEnabled && voiceWarmingUp) { + parts.push(); + } else if (isFullscreenEnvEnabled() && selectionHintHasContent) { + // xterm.js (VS Code/Cursor/Windsurf) force-selection modifier is + // platform-specific and gated on macOS (SelectionService.shouldForceSelection): + // macOS: altKey && macOptionClickForcesSelection (VS Code default: false) + // non-macOS: shiftKey + // On macOS, if we RECEIVED an alt+click (lastPressHadAlt), the VS Code + // setting is off — xterm.js would have consumed the event otherwise. + // Tell the user the exact setting to flip instead of repeating the + // option+click hint they just tried. + // Non-reactive getState() read is safe: lastPressHadAlt is immutable + // while hasSelection is true (set pre-drag, cleared with selection). + const isMac = getPlatform() === 'macos'; + const altClickFailed = isMac && (selGetState()?.lastPressHadAlt ?? false); + parts.push( + + {!copyOnSelect && } + {isXtermJs() && (altClickFailed ? set macOptionClickForcesSelection in VS Code settings : )} + + ); + } else if (feature('VOICE_MODE') && parts.length > 0 && showHint && voiceEnabled && voiceState === 'idle' && hintParts.length === 0 && voiceHintUnderCap) { + parts.push( + hold {voiceKeyShortcut} to speak + ); + } + if ((tasksPart || hasCoordinatorTasks) && showHint && !hasTeams) { + parts.push( + {tasksSelected ? : } + ); + } + + // In fullscreen the bottom section is flexShrink:0 — every row here + // is a row stolen from the ScrollBox. This component must have a STABLE + // height so the footer never grows/shrinks and shifts scroll content. + // Returning null when parts is empty (e.g. StatusLine on → suppressHint + // → showHint=false → no "? for shortcuts") would let a later-added + // part (e.g. the selection copy/native-select hints) grow the column + // from 0→1 row. Always render 1 row in fullscreen; return a space when + // empty so Yoga reserves the row without painting anything visible. + if (parts.length === 0 && !tasksPart && !modePart) { + return isFullscreenEnvEnabled() ? : null; + } + + // flexShrink=0 keeps mode + pill at natural width; the remaining parts + // truncate at the tail as one string inside the Text wrapper. + return + {modePart && + {modePart} + {(tasksPart || parts.length > 0) && · } + } + {tasksPart && + {tasksPart} + {parts.length > 0 && · } + } + {parts.length > 0 && + {parts} + } + ; +} +function getSpinnerHintParts(isLoading: boolean, escShortcut: string, todosShortcut: string, killAgentsShortcut: string, hasTaskItems: boolean, expandedView: 'none' | 'tasks' | 'teammates', hasTeammates: boolean, hasRunningAgentTasks: boolean, isKillAgentsConfirmShowing: boolean): React.ReactElement[] { + let toggleAction: string; + if (hasTeammates) { + // Cycling: none → tasks → teammates → none + switch (expandedView) { + case 'none': + toggleAction = 'show tasks'; + break; + case 'tasks': + toggleAction = 'show teammates'; + break; + case 'teammates': + toggleAction = 'hide'; + break; + } + } else { + toggleAction = expandedView === 'tasks' ? 'hide tasks' : 'show tasks'; + } + + // Show the toggle hint only when there are task items to display or + // teammates to cycle to + const showToggleHint = hasTaskItems || hasTeammates; + return [...(isLoading ? [ + + ] : []), ...(!isLoading && hasRunningAgentTasks && !isKillAgentsConfirmShowing ? [ + + ] : []), ...(showToggleHint ? [ + + ] : [])]; +} +function isPrStatusEnabled(): boolean { + return getGlobalConfig().prStatusFooterEnabled ?? true; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiY29vcmRpbmF0b3JNb2R1bGUiLCJyZXF1aXJlIiwidW5kZWZpbmVkIiwiQm94IiwiVGV4dCIsIkxpbmsiLCJSZWFjdCIsImZpZ3VyZXMiLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwidXNlUmVmIiwidXNlU3RhdGUiLCJ1c2VTeW5jRXh0ZXJuYWxTdG9yZSIsIlZpbU1vZGUiLCJQcm9tcHRJbnB1dE1vZGUiLCJUb29sUGVybWlzc2lvbkNvbnRleHQiLCJpc1ZpbU1vZGVFbmFibGVkIiwidXNlU2hvcnRjdXREaXNwbGF5IiwiaXNEZWZhdWx0TW9kZSIsInBlcm1pc3Npb25Nb2RlU3ltYm9sIiwicGVybWlzc2lvbk1vZGVUaXRsZSIsImdldE1vZGVDb2xvciIsIkJhY2tncm91bmRUYXNrU3RhdHVzIiwiaXNCYWNrZ3JvdW5kVGFzayIsImlzUGFuZWxBZ2VudFRhc2siLCJnZXRWaXNpYmxlQWdlbnRUYXNrcyIsImNvdW50Iiwic2hvdWxkSGlkZVRhc2tzRm9vdGVyIiwiaXNBZ2VudFN3YXJtc0VuYWJsZWQiLCJUZWFtU3RhdHVzIiwiaXNJblByb2Nlc3NFbmFibGVkIiwidXNlQXBwU3RhdGUiLCJ1c2VBcHBTdGF0ZVN0b3JlIiwiZ2V0SXNSZW1vdGVNb2RlIiwiSGlzdG9yeVNlYXJjaElucHV0IiwidXNlUHJTdGF0dXMiLCJLZXlib2FyZFNob3J0Y3V0SGludCIsIkJ5bGluZSIsInVzZVRlcm1pbmFsU2l6ZSIsInVzZVRhc2tzVjIiLCJmb3JtYXREdXJhdGlvbiIsIlZvaWNlV2FybXVwSGludCIsInVzZVZvaWNlRW5hYmxlZCIsInVzZVZvaWNlU3RhdGUiLCJpc0Z1bGxzY3JlZW5FbnZFbmFibGVkIiwiaXNYdGVybUpzIiwidXNlSGFzU2VsZWN0aW9uIiwidXNlU2VsZWN0aW9uIiwiZ2V0R2xvYmFsQ29uZmlnIiwic2F2ZUdsb2JhbENvbmZpZyIsImdldFBsYXRmb3JtIiwiUHJCYWRnZSIsInByb2FjdGl2ZU1vZHVsZSIsIk5PX09QX1NVQlNDUklCRSIsIl9jYiIsIk5VTEwiLCJNQVhfVk9JQ0VfSElOVF9TSE9XUyIsIlByb3BzIiwiZXhpdE1lc3NhZ2UiLCJzaG93Iiwia2V5IiwidmltTW9kZSIsIm1vZGUiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJzdXBwcmVzc0hpbnQiLCJpc0xvYWRpbmciLCJzaG93TWVtb3J5VHlwZVNlbGVjdG9yIiwidGFza3NTZWxlY3RlZCIsInRlYW1zU2VsZWN0ZWQiLCJ0bXV4U2VsZWN0ZWQiLCJ0ZWFtbWF0ZUZvb3RlckluZGV4IiwiaXNQYXN0aW5nIiwiaXNTZWFyY2hpbmciLCJoaXN0b3J5UXVlcnkiLCJzZXRIaXN0b3J5UXVlcnkiLCJxdWVyeSIsImhpc3RvcnlGYWlsZWRNYXRjaCIsIm9uT3BlblRhc2tzRGlhbG9nIiwidGFza0lkIiwiUHJvYWN0aXZlQ291bnRkb3duIiwiJCIsIl9jIiwibmV4dFRpY2tBdCIsInN1YnNjcmliZVRvUHJvYWN0aXZlQ2hhbmdlcyIsImdldE5leHRUaWNrQXQiLCJyZW1haW5pbmdTZWNvbmRzIiwic2V0UmVtYWluaW5nU2Vjb25kcyIsInQwIiwidDEiLCJ1cGRhdGUiLCJyZW1haW5pbmciLCJNYXRoIiwibWF4IiwiY2VpbCIsIkRhdGUiLCJub3ciLCJpbnRlcnZhbCIsInNldEludGVydmFsIiwiY2xlYXJJbnRlcnZhbCIsInQyIiwidDMiLCJtb3N0U2lnbmlmaWNhbnRPbmx5IiwidDQiLCJQcm9tcHRJbnB1dEZvb3RlckxlZnRTaWRlIiwiU3ltYm9sIiwiZm9yIiwic2hvd1ZpbSIsInQ1IiwidDYiLCJNb2RlSW5kaWNhdG9yUHJvcHMiLCJzaG93SGludCIsIk1vZGVJbmRpY2F0b3IiLCJSZWFjdE5vZGUiLCJjb2x1bW5zIiwibW9kZUN5Y2xlU2hvcnRjdXQiLCJ0YXNrcyIsInMiLCJ0ZWFtQ29udGV4dCIsInN0b3JlIiwicmVtb3RlU2Vzc2lvblVybCIsImdldFN0YXRlIiwidmlld1NlbGVjdGlvbk1vZGUiLCJ2aWV3aW5nQWdlbnRUYXNrSWQiLCJleHBhbmRlZFZpZXciLCJzaG93U3Bpbm5lclRyZWUiLCJwclN0YXR1cyIsImlzUHJTdGF0dXNFbmFibGVkIiwiaGFzVG11eFNlc3Npb24iLCJ0dW5nc3RlbkFjdGl2ZVNlc3Npb24iLCJ2b2ljZUVuYWJsZWQiLCJ2b2ljZVN0YXRlIiwiY29uc3QiLCJ2b2ljZVdhcm1pbmdVcCIsImhhc1NlbGVjdGlvbiIsInNlbEdldFN0YXRlIiwiaGFzTmV4dFRpY2siLCJpc0Nvb3JkaW5hdG9yIiwiaXNDb29yZGluYXRvck1vZGUiLCJydW5uaW5nVGFza0NvdW50IiwiT2JqZWN0IiwidmFsdWVzIiwidCIsInRhc2tzVjIiLCJoYXNUYXNrSXRlbXMiLCJsZW5ndGgiLCJlc2NTaG9ydGN1dCIsInRvTG93ZXJDYXNlIiwidG9kb3NTaG9ydGN1dCIsImtpbGxBZ2VudHNTaG9ydGN1dCIsInZvaWNlS2V5U2hvcnRjdXQiLCJ2b2ljZUhpbnRVbmRlckNhcCIsInZvaWNlRm9vdGVySGludFNlZW5Db3VudCIsInZvaWNlSGludEluY3JlbWVudGVkUmVmIiwiY3VycmVudCIsIm5ld0NvdW50IiwicHJldiIsImlzS2lsbEFnZW50c0NvbmZpcm1TaG93aW5nIiwibm90aWZpY2F0aW9ucyIsImhhc1RlYW1zIiwidGVhbW1hdGVzIiwibmFtZSIsImN1cnJlbnRNb2RlIiwiaGFzQWN0aXZlTW9kZSIsInZpZXdlZFRhc2siLCJpc1ZpZXdpbmdUZWFtbWF0ZSIsInR5cGUiLCJpc1ZpZXdpbmdDb21wbGV0ZWRUZWFtbWF0ZSIsInN0YXR1cyIsImhhc0JhY2tncm91bmRUYXNrcyIsInByaW1hcnlJdGVtQ291bnQiLCJzaG91bGRTaG93UHJTdGF0dXMiLCJudW1iZXIiLCJyZXZpZXdTdGF0ZSIsInVybCIsInNob3VsZFNob3dNb2RlSGludCIsImhhc0luUHJvY2Vzc1RlYW1tYXRlcyIsInNvbWUiLCJoYXNUZWFtbWF0ZVBpbGxzIiwibW9kZVBhcnQiLCJwYXJ0cyIsImNpcmNsZURvdWJsZSIsImhhc0FueUluUHJvY2Vzc1RlYW1tYXRlcyIsImhhc1J1bm5pbmdBZ2VudFRhc2tzIiwiaGludFBhcnRzIiwiZ2V0U3Bpbm5lckhpbnRQYXJ0cyIsInB1c2giLCJvdGhlclBhcnRzIiwiaGFzQ29vcmRpbmF0b3JUYXNrcyIsInRhc2tzUGFydCIsImNvcHlPblNlbGVjdCIsInNlbGVjdGlvbkhpbnRIYXNDb250ZW50IiwiaXNNYWMiLCJhbHRDbGlja0ZhaWxlZCIsImxhc3RQcmVzc0hhZEFsdCIsImhhc1RlYW1tYXRlcyIsIlJlYWN0RWxlbWVudCIsInRvZ2dsZUFjdGlvbiIsInNob3dUb2dnbGVIaW50IiwicHJTdGF0dXNGb290ZXJFbmFibGVkIl0sInNvdXJjZXMiOlsiUHJvbXB0SW5wdXRGb290ZXJMZWZ0U2lkZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLy8gYmlvbWUtaWdub3JlLWFsbCBhc3Npc3Qvc291cmNlL29yZ2FuaXplSW1wb3J0czogQU5ULU9OTFkgaW1wb3J0IG1hcmtlcnMgbXVzdCBub3QgYmUgcmVvcmRlcmVkXG5pbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbi8vIERlYWQgY29kZSBlbGltaW5hdGlvbjogY29uZGl0aW9uYWwgaW1wb3J0IGZvciBDT09SRElOQVRPUl9NT0RFXG4vKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG5jb25zdCBjb29yZGluYXRvck1vZHVsZSA9IGZlYXR1cmUoJ0NPT1JESU5BVE9SX01PREUnKVxuICA/IChyZXF1aXJlKCcuLi8uLi9jb29yZGluYXRvci9jb29yZGluYXRvck1vZGUuanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuLi8uLi9jb29yZGluYXRvci9jb29yZGluYXRvck1vZGUuanMnKSlcbiAgOiB1bmRlZmluZWRcbi8qIGVzbGludC1lbmFibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuaW1wb3J0IHsgQm94LCBUZXh0LCBMaW5rIH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0IHtcbiAgdXNlRWZmZWN0LFxuICB1c2VNZW1vLFxuICB1c2VSZWYsXG4gIHVzZVN0YXRlLFxuICB1c2VTeW5jRXh0ZXJuYWxTdG9yZSxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IFZpbU1vZGUsIFByb21wdElucHV0TW9kZSB9IGZyb20gJy4uLy4uL3R5cGVzL3RleHRJbnB1dFR5cGVzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUb29sUGVybWlzc2lvbkNvbnRleHQgfSBmcm9tICcuLi8uLi9Ub29sLmpzJ1xuaW1wb3J0IHsgaXNWaW1Nb2RlRW5hYmxlZCB9IGZyb20gJy4vdXRpbHMuanMnXG5pbXBvcnQgeyB1c2VTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VTaG9ydGN1dERpc3BsYXkuanMnXG5pbXBvcnQge1xuICBpc0RlZmF1bHRNb2RlLFxuICBwZXJtaXNzaW9uTW9kZVN5bWJvbCxcbiAgcGVybWlzc2lvbk1vZGVUaXRsZSxcbiAgZ2V0TW9kZUNvbG9yLFxufSBmcm9tICcuLi8uLi91dGlscy9wZXJtaXNzaW9ucy9QZXJtaXNzaW9uTW9kZS5qcydcbmltcG9ydCB7IEJhY2tncm91bmRUYXNrU3RhdHVzIH0gZnJvbSAnLi4vdGFza3MvQmFja2dyb3VuZFRhc2tTdGF0dXMuanMnXG5pbXBvcnQgeyBpc0JhY2tncm91bmRUYXNrIH0gZnJvbSAnLi4vLi4vdGFza3MvdHlwZXMuanMnXG5pbXBvcnQgeyBpc1BhbmVsQWdlbnRUYXNrIH0gZnJvbSAnLi4vLi4vdGFza3MvTG9jYWxBZ2VudFRhc2svTG9jYWxBZ2VudFRhc2suanMnXG5pbXBvcnQgeyBnZXRWaXNpYmxlQWdlbnRUYXNrcyB9IGZyb20gJy4uL0Nvb3JkaW5hdG9yQWdlbnRTdGF0dXMuanMnXG5pbXBvcnQgeyBjb3VudCB9IGZyb20gJy4uLy4uL3V0aWxzL2FycmF5LmpzJ1xuaW1wb3J0IHsgc2hvdWxkSGlkZVRhc2tzRm9vdGVyIH0gZnJvbSAnLi4vdGFza3MvdGFza1N0YXR1c1V0aWxzLmpzJ1xuaW1wb3J0IHsgaXNBZ2VudFN3YXJtc0VuYWJsZWQgfSBmcm9tICcuLi8uLi91dGlscy9hZ2VudFN3YXJtc0VuYWJsZWQuanMnXG5pbXBvcnQgeyBUZWFtU3RhdHVzIH0gZnJvbSAnLi4vdGVhbXMvVGVhbVN0YXR1cy5qcydcbmltcG9ydCB7IGlzSW5Qcm9jZXNzRW5hYmxlZCB9IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL2JhY2tlbmRzL3JlZ2lzdHJ5LmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUsIHVzZUFwcFN0YXRlU3RvcmUgfSBmcm9tICdzcmMvc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgeyBnZXRJc1JlbW90ZU1vZGUgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgSGlzdG9yeVNlYXJjaElucHV0IGZyb20gJy4vSGlzdG9yeVNlYXJjaElucHV0LmpzJ1xuaW1wb3J0IHsgdXNlUHJTdGF0dXMgfSBmcm9tICcuLi8uLi9ob29rcy91c2VQclN0YXR1cy5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vQnlsaW5lLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgdXNlVGFza3NWMiB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZVRhc2tzVjIuanMnXG5pbXBvcnQgeyBmb3JtYXREdXJhdGlvbiB9IGZyb20gJy4uLy4uL3V0aWxzL2Zvcm1hdC5qcydcbmltcG9ydCB7IFZvaWNlV2FybXVwSGludCB9IGZyb20gJy4vVm9pY2VJbmRpY2F0b3IuanMnXG5pbXBvcnQgeyB1c2VWb2ljZUVuYWJsZWQgfSBmcm9tICcuLi8uLi9ob29rcy91c2VWb2ljZUVuYWJsZWQuanMnXG5pbXBvcnQgeyB1c2VWb2ljZVN0YXRlIH0gZnJvbSAnLi4vLi4vY29udGV4dC92b2ljZS5qcydcbmltcG9ydCB7IGlzRnVsbHNjcmVlbkVudkVuYWJsZWQgfSBmcm9tICcuLi8uLi91dGlscy9mdWxsc2NyZWVuLmpzJ1xuaW1wb3J0IHsgaXNYdGVybUpzIH0gZnJvbSAnLi4vLi4vaW5rL3Rlcm1pbmFsLmpzJ1xuaW1wb3J0IHsgdXNlSGFzU2VsZWN0aW9uLCB1c2VTZWxlY3Rpb24gfSBmcm9tICcuLi8uLi9pbmsvaG9va3MvdXNlLXNlbGVjdGlvbi5qcydcbmltcG9ydCB7IGdldEdsb2JhbENvbmZpZywgc2F2ZUdsb2JhbENvbmZpZyB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGdldFBsYXRmb3JtIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGxhdGZvcm0uanMnXG5pbXBvcnQgeyBQckJhZGdlIH0gZnJvbSAnLi4vUHJCYWRnZS5qcydcblxuLy8gRGVhZCBjb2RlIGVsaW1pbmF0aW9uOiBjb25kaXRpb25hbCBpbXBvcnQgZm9yIHByb2FjdGl2ZSBtb2RlXG4vKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG5jb25zdCBwcm9hY3RpdmVNb2R1bGUgPVxuICBmZWF0dXJlKCdQUk9BQ1RJVkUnKSB8fCBmZWF0dXJlKCdLQUlST1MnKVxuICAgID8gcmVxdWlyZSgnLi4vLi4vcHJvYWN0aXZlL2luZGV4LmpzJylcbiAgICA6IG51bGxcbi8qIGVzbGludC1lbmFibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuY29uc3QgTk9fT1BfU1VCU0NSSUJFID0gKF9jYjogKCkgPT4gdm9pZCkgPT4gKCkgPT4ge31cbmNvbnN0IE5VTEwgPSAoKSA9PiBudWxsXG5jb25zdCBNQVhfVk9JQ0VfSElOVF9TSE9XUyA9IDNcblxudHlwZSBQcm9wcyA9IHtcbiAgZXhpdE1lc3NhZ2U6IHtcbiAgICBzaG93OiBib29sZWFuXG4gICAga2V5Pzogc3RyaW5nXG4gIH1cbiAgdmltTW9kZTogVmltTW9kZSB8IHVuZGVmaW5lZFxuICBtb2RlOiBQcm9tcHRJbnB1dE1vZGVcbiAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHRcbiAgc3VwcHJlc3NIaW50OiBib29sZWFuXG4gIGlzTG9hZGluZzogYm9vbGVhblxuICBzaG93TWVtb3J5VHlwZVNlbGVjdG9yPzogYm9vbGVhblxuICB0YXNrc1NlbGVjdGVkOiBib29sZWFuXG4gIHRlYW1zU2VsZWN0ZWQ6IGJvb2xlYW5cbiAgdG11eFNlbGVjdGVkOiBib29sZWFuXG4gIHRlYW1tYXRlRm9vdGVySW5kZXg/OiBudW1iZXJcbiAgaXNQYXN0aW5nPzogYm9vbGVhblxuICBpc1NlYXJjaGluZzogYm9vbGVhblxuICBoaXN0b3J5UXVlcnk6IHN0cmluZ1xuICBzZXRIaXN0b3J5UXVlcnk6IChxdWVyeTogc3RyaW5nKSA9PiB2b2lkXG4gIGhpc3RvcnlGYWlsZWRNYXRjaDogYm9vbGVhblxuICBvbk9wZW5UYXNrc0RpYWxvZz86ICh0YXNrSWQ/OiBzdHJpbmcpID0+IHZvaWRcbn1cblxuZnVuY3Rpb24gUHJvYWN0aXZlQ291bnRkb3duKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IG5leHRUaWNrQXQgPSB1c2VTeW5jRXh0ZXJuYWxTdG9yZShcbiAgICBwcm9hY3RpdmVNb2R1bGU/LnN1YnNjcmliZVRvUHJvYWN0aXZlQ2hhbmdlcyA/PyBOT19PUF9TVUJTQ1JJQkUsXG4gICAgcHJvYWN0aXZlTW9kdWxlPy5nZXROZXh0VGlja0F0ID8/IE5VTEwsXG4gICAgTlVMTCxcbiAgKVxuXG4gIGNvbnN0IFtyZW1haW5pbmdTZWNvbmRzLCBzZXRSZW1haW5pbmdTZWNvbmRzXSA9IHVzZVN0YXRlPG51bWJlciB8IG51bGw+KG51bGwpXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAobmV4dFRpY2tBdCA9PT0gbnVsbCkge1xuICAgICAgc2V0UmVtYWluaW5nU2Vjb25kcyhudWxsKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gdXBkYXRlKCk6IHZvaWQge1xuICAgICAgY29uc3QgcmVtYWluaW5nID0gTWF0aC5tYXgoXG4gICAgICAgIDAsXG4gICAgICAgIE1hdGguY2VpbCgobmV4dFRpY2tBdCEgLSBEYXRlLm5vdygpKSAvIDEwMDApLFxuICAgICAgKVxuICAgICAgc2V0UmVtYWluaW5nU2Vjb25kcyhyZW1haW5pbmcpXG4gICAgfVxuXG4gICAgdXBkYXRlKClcbiAgICBjb25zdCBpbnRlcnZhbCA9IHNldEludGVydmFsKHVwZGF0ZSwgMTAwMClcbiAgICByZXR1cm4gKCkgPT4gY2xlYXJJbnRlcnZhbChpbnRlcnZhbClcbiAgfSwgW25leHRUaWNrQXRdKVxuXG4gIGlmIChyZW1haW5pbmdTZWNvbmRzID09PSBudWxsKSByZXR1cm4gbnVsbFxuXG4gIHJldHVybiAoXG4gICAgPFRleHQgZGltQ29sb3I+XG4gICAgICB3YWl0aW5neycgJ31cbiAgICAgIHtmb3JtYXREdXJhdGlvbihyZW1haW5pbmdTZWNvbmRzICogMTAwMCwgeyBtb3N0U2lnbmlmaWNhbnRPbmx5OiB0cnVlIH0pfVxuICAgIDwvVGV4dD5cbiAgKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gUHJvbXB0SW5wdXRGb290ZXJMZWZ0U2lkZSh7XG4gIGV4aXRNZXNzYWdlLFxuICB2aW1Nb2RlLFxuICBtb2RlLFxuICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gIHN1cHByZXNzSGludCxcbiAgaXNMb2FkaW5nLFxuICB0YXNrc1NlbGVjdGVkLFxuICB0ZWFtc1NlbGVjdGVkLFxuICB0bXV4U2VsZWN0ZWQsXG4gIHRlYW1tYXRlRm9vdGVySW5kZXgsXG4gIGlzUGFzdGluZyxcbiAgaXNTZWFyY2hpbmcsXG4gIGhpc3RvcnlRdWVyeSxcbiAgc2V0SGlzdG9yeVF1ZXJ5LFxuICBoaXN0b3J5RmFpbGVkTWF0Y2gsXG4gIG9uT3BlblRhc2tzRGlhbG9nLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoZXhpdE1lc3NhZ2Uuc2hvdykge1xuICAgIHJldHVybiAoXG4gICAgICA8VGV4dCBkaW1Db2xvciBrZXk9XCJleGl0LW1lc3NhZ2VcIj5cbiAgICAgICAgUHJlc3Mge2V4aXRNZXNzYWdlLmtleX0gYWdhaW4gdG8gZXhpdFxuICAgICAgPC9UZXh0PlxuICAgIClcbiAgfVxuICBpZiAoaXNQYXN0aW5nKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxUZXh0IGRpbUNvbG9yIGtleT1cInBhc3RpbmctbWVzc2FnZVwiPlxuICAgICAgICBQYXN0aW5nIHRleHTigKZcbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH1cblxuICBjb25zdCBzaG93VmltID0gaXNWaW1Nb2RlRW5hYmxlZCgpICYmIHZpbU1vZGUgPT09ICdJTlNFUlQnICYmICFpc1NlYXJjaGluZ1xuXG4gIHJldHVybiAoXG4gICAgPEJveCBqdXN0aWZ5Q29udGVudD1cImZsZXgtc3RhcnRcIiBnYXA9ezF9PlxuICAgICAge2lzU2VhcmNoaW5nICYmIChcbiAgICAgICAgPEhpc3RvcnlTZWFyY2hJbnB1dFxuICAgICAgICAgIHZhbHVlPXtoaXN0b3J5UXVlcnl9XG4gICAgICAgICAgb25DaGFuZ2U9e3NldEhpc3RvcnlRdWVyeX1cbiAgICAgICAgICBoaXN0b3J5RmFpbGVkTWF0Y2g9e2hpc3RvcnlGYWlsZWRNYXRjaH1cbiAgICAgICAgLz5cbiAgICAgICl9XG4gICAgICB7c2hvd1ZpbSA/IChcbiAgICAgICAgPFRleHQgZGltQ29sb3Iga2V5PVwidmltLWluc2VydFwiPlxuICAgICAgICAgIC0tIElOU0VSVCAtLVxuICAgICAgICA8L1RleHQ+XG4gICAgICApIDogbnVsbH1cbiAgICAgIDxNb2RlSW5kaWNhdG9yXG4gICAgICAgIG1vZGU9e21vZGV9XG4gICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dD17dG9vbFBlcm1pc3Npb25Db250ZXh0fVxuICAgICAgICBzaG93SGludD17IXN1cHByZXNzSGludCAmJiAhc2hvd1ZpbX1cbiAgICAgICAgaXNMb2FkaW5nPXtpc0xvYWRpbmd9XG4gICAgICAgIHRhc2tzU2VsZWN0ZWQ9e3Rhc2tzU2VsZWN0ZWR9XG4gICAgICAgIHRlYW1zU2VsZWN0ZWQ9e3RlYW1zU2VsZWN0ZWR9XG4gICAgICAgIHRlYW1tYXRlRm9vdGVySW5kZXg9e3RlYW1tYXRlRm9vdGVySW5kZXh9XG4gICAgICAgIHRtdXhTZWxlY3RlZD17dG11eFNlbGVjdGVkfVxuICAgICAgICBvbk9wZW5UYXNrc0RpYWxvZz17b25PcGVuVGFza3NEaWFsb2d9XG4gICAgICAvPlxuICAgIDwvQm94PlxuICApXG59XG5cbnR5cGUgTW9kZUluZGljYXRvclByb3BzID0ge1xuICBtb2RlOiBQcm9tcHRJbnB1dE1vZGVcbiAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHRcbiAgc2hvd0hpbnQ6IGJvb2xlYW5cbiAgaXNMb2FkaW5nOiBib29sZWFuXG4gIHRhc2tzU2VsZWN0ZWQ6IGJvb2xlYW5cbiAgdGVhbXNTZWxlY3RlZDogYm9vbGVhblxuICB0bXV4U2VsZWN0ZWQ6IGJvb2xlYW5cbiAgdGVhbW1hdGVGb290ZXJJbmRleD86IG51bWJlclxuICBvbk9wZW5UYXNrc0RpYWxvZz86ICh0YXNrSWQ/OiBzdHJpbmcpID0+IHZvaWRcbn1cblxuZnVuY3Rpb24gTW9kZUluZGljYXRvcih7XG4gIG1vZGUsXG4gIHRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgc2hvd0hpbnQsXG4gIGlzTG9hZGluZyxcbiAgdGFza3NTZWxlY3RlZCxcbiAgdGVhbXNTZWxlY3RlZCxcbiAgdG11eFNlbGVjdGVkLFxuICB0ZWFtbWF0ZUZvb3RlckluZGV4LFxuICBvbk9wZW5UYXNrc0RpYWxvZyxcbn06IE1vZGVJbmRpY2F0b3JQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcbiAgY29uc3QgbW9kZUN5Y2xlU2hvcnRjdXQgPSB1c2VTaG9ydGN1dERpc3BsYXkoXG4gICAgJ2NoYXQ6Y3ljbGVNb2RlJyxcbiAgICAnQ2hhdCcsXG4gICAgJ3NoaWZ0K3RhYicsXG4gIClcbiAgY29uc3QgdGFza3MgPSB1c2VBcHBTdGF0ZShzID0+IHMudGFza3MpXG4gIGNvbnN0IHRlYW1Db250ZXh0ID0gdXNlQXBwU3RhdGUocyA9PiBzLnRlYW1Db250ZXh0KVxuICAvLyBTZXQgb25jZSBpbiBpbml0aWFsU3RhdGUgKG1haW4udHN4IC0tcmVtb3RlIG1vZGUpIGFuZCBuZXZlciBtdXRhdGVkIOKAlCBsYXp5XG4gIC8vIGluaXQgY2FwdHVyZXMgdGhlIGltbXV0YWJsZSB2YWx1ZSB3aXRob3V0IGEgc3Vic2NyaXB0aW9uLlxuICBjb25zdCBzdG9yZSA9IHVzZUFwcFN0YXRlU3RvcmUoKVxuICBjb25zdCBbcmVtb3RlU2Vzc2lvblVybF0gPSB1c2VTdGF0ZSgoKSA9PiBzdG9yZS5nZXRTdGF0ZSgpLnJlbW90ZVNlc3Npb25VcmwpXG4gIGNvbnN0IHZpZXdTZWxlY3Rpb25Nb2RlID0gdXNlQXBwU3RhdGUocyA9PiBzLnZpZXdTZWxlY3Rpb25Nb2RlKVxuICBjb25zdCB2aWV3aW5nQWdlbnRUYXNrSWQgPSB1c2VBcHBTdGF0ZShzID0+IHMudmlld2luZ0FnZW50VGFza0lkKVxuICBjb25zdCBleHBhbmRlZFZpZXcgPSB1c2VBcHBTdGF0ZShzID0+IHMuZXhwYW5kZWRWaWV3KVxuICBjb25zdCBzaG93U3Bpbm5lclRyZWUgPSBleHBhbmRlZFZpZXcgPT09ICd0ZWFtbWF0ZXMnXG4gIGNvbnN0IHByU3RhdHVzID0gdXNlUHJTdGF0dXMoaXNMb2FkaW5nLCBpc1ByU3RhdHVzRW5hYmxlZCgpKVxuICBjb25zdCBoYXNUbXV4U2Vzc2lvbiA9IHVzZUFwcFN0YXRlKFxuICAgIHMgPT5cbiAgICAgIFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiYgcy50dW5nc3RlbkFjdGl2ZVNlc3Npb24gIT09IHVuZGVmaW5lZCxcbiAgKVxuXG4gIGNvbnN0IG5leHRUaWNrQXQgPSB1c2VTeW5jRXh0ZXJuYWxTdG9yZShcbiAgICBwcm9hY3RpdmVNb2R1bGU/LnN1YnNjcmliZVRvUHJvYWN0aXZlQ2hhbmdlcyA/PyBOT19PUF9TVUJTQ1JJQkUsXG4gICAgcHJvYWN0aXZlTW9kdWxlPy5nZXROZXh0VGlja0F0ID8/IE5VTEwsXG4gICAgTlVMTCxcbiAgKVxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gIGNvbnN0IHZvaWNlRW5hYmxlZCA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKSA/IHVzZVZvaWNlRW5hYmxlZCgpIDogZmFsc2VcbiAgY29uc3Qgdm9pY2VTdGF0ZSA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKVxuICAgID8gLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICAgICAgdXNlVm9pY2VTdGF0ZShzID0+IHMudm9pY2VTdGF0ZSlcbiAgICA6ICgnaWRsZScgYXMgY29uc3QpXG4gIGNvbnN0IHZvaWNlV2FybWluZ1VwID0gZmVhdHVyZSgnVk9JQ0VfTU9ERScpXG4gICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICB1c2VWb2ljZVN0YXRlKHMgPT4gcy52b2ljZVdhcm1pbmdVcClcbiAgICA6IGZhbHNlXG4gIGNvbnN0IGhhc1NlbGVjdGlvbiA9IHVzZUhhc1NlbGVjdGlvbigpXG4gIGNvbnN0IHNlbEdldFN0YXRlID0gdXNlU2VsZWN0aW9uKCkuZ2V0U3RhdGVcbiAgY29uc3QgaGFzTmV4dFRpY2sgPSBuZXh0VGlja0F0ICE9PSBudWxsXG4gIGNvbnN0IGlzQ29vcmRpbmF0b3IgPSBmZWF0dXJlKCdDT09SRElOQVRPUl9NT0RFJylcbiAgICA/IGNvb3JkaW5hdG9yTW9kdWxlPy5pc0Nvb3JkaW5hdG9yTW9kZSgpID09PSB0cnVlXG4gICAgOiBmYWxzZVxuICBjb25zdCBydW5uaW5nVGFza0NvdW50ID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgY291bnQoXG4gICAgICAgIE9iamVjdC52YWx1ZXModGFza3MpLFxuICAgICAgICB0ID0+XG4gICAgICAgICAgaXNCYWNrZ3JvdW5kVGFzayh0KSAmJlxuICAgICAgICAgICEoXCJleHRlcm5hbFwiID09PSAnYW50JyAmJiBpc1BhbmVsQWdlbnRUYXNrKHQpKSxcbiAgICAgICksXG4gICAgW3Rhc2tzXSxcbiAgKVxuICBjb25zdCB0YXNrc1YyID0gdXNlVGFza3NWMigpXG4gIGNvbnN0IGhhc1Rhc2tJdGVtcyA9IHRhc2tzVjIgIT09IHVuZGVmaW5lZCAmJiB0YXNrc1YyLmxlbmd0aCA+IDBcbiAgY29uc3QgZXNjU2hvcnRjdXQgPSB1c2VTaG9ydGN1dERpc3BsYXkoXG4gICAgJ2NoYXQ6Y2FuY2VsJyxcbiAgICAnQ2hhdCcsXG4gICAgJ2VzYycsXG4gICkudG9Mb3dlckNhc2UoKVxuICBjb25zdCB0b2Rvc1Nob3J0Y3V0ID0gdXNlU2hvcnRjdXREaXNwbGF5KFxuICAgICdhcHA6dG9nZ2xlVG9kb3MnLFxuICAgICdHbG9iYWwnLFxuICAgICdjdHJsK3QnLFxuICApXG4gIGNvbnN0IGtpbGxBZ2VudHNTaG9ydGN1dCA9IHVzZVNob3J0Y3V0RGlzcGxheShcbiAgICAnY2hhdDpraWxsQWdlbnRzJyxcbiAgICAnQ2hhdCcsXG4gICAgJ2N0cmwreCBjdHJsK2snLFxuICApXG4gIGNvbnN0IHZvaWNlS2V5U2hvcnRjdXQgPSBmZWF0dXJlKCdWT0lDRV9NT0RFJylcbiAgICA/IC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgICAgIHVzZVNob3J0Y3V0RGlzcGxheSgndm9pY2U6cHVzaFRvVGFsaycsICdDaGF0JywgJ1NwYWNlJylcbiAgICA6ICcnXG4gIC8vIENhcHR1cmVkIGF0IG1vdW50IHNvIHRoZSBoaW50IGRvZXNuJ3QgZmxpY2tlciBtaWQtc2Vzc2lvbiBpZiBhbm90aGVyXG4gIC8vIENDIGluc3RhbmNlIGluY3JlbWVudHMgdGhlIGNvdW50ZXIuIEluY3JlbWVudGVkIG9uY2UgdmlhIHVzZUVmZmVjdCB0aGVcbiAgLy8gZmlyc3QgdGltZSB2b2ljZSBpcyBlbmFibGVkIGluIHRoaXMgc2Vzc2lvbiDigJQgYXBwcm94aW1hdGVzIFwiaGludCB3YXNcbiAgLy8gc2hvd25cIiB3aXRob3V0IHRyYWNraW5nIHRoZSBleGFjdCByZW5kZXItdGltZSBjb25kaXRpb24gKHdoaWNoIGRlcGVuZHNcbiAgLy8gb24gcGFydHMvaGludFBhcnRzIGNvbXB1dGVkIGFmdGVyIHRoZSBlYXJseS1yZXR1cm4gaG9va3MgYm91bmRhcnkpLlxuICBjb25zdCBbdm9pY2VIaW50VW5kZXJDYXBdID0gZmVhdHVyZSgnVk9JQ0VfTU9ERScpXG4gICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICB1c2VTdGF0ZShcbiAgICAgICAgKCkgPT5cbiAgICAgICAgICAoZ2V0R2xvYmFsQ29uZmlnKCkudm9pY2VGb290ZXJIaW50U2VlbkNvdW50ID8/IDApIDxcbiAgICAgICAgICBNQVhfVk9JQ0VfSElOVF9TSE9XUyxcbiAgICAgIClcbiAgICA6IFtmYWxzZV1cbiAgLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICBjb25zdCB2b2ljZUhpbnRJbmNyZW1lbnRlZFJlZiA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKSA/IHVzZVJlZihmYWxzZSkgOiBudWxsXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGZlYXR1cmUoJ1ZPSUNFX01PREUnKSkge1xuICAgICAgaWYgKCF2b2ljZUVuYWJsZWQgfHwgIXZvaWNlSGludFVuZGVyQ2FwKSByZXR1cm5cbiAgICAgIGlmICh2b2ljZUhpbnRJbmNyZW1lbnRlZFJlZj8uY3VycmVudCkgcmV0dXJuXG4gICAgICBpZiAodm9pY2VIaW50SW5jcmVtZW50ZWRSZWYpIHZvaWNlSGludEluY3JlbWVudGVkUmVmLmN1cnJlbnQgPSB0cnVlXG4gICAgICBjb25zdCBuZXdDb3VudCA9IChnZXRHbG9iYWxDb25maWcoKS52b2ljZUZvb3RlckhpbnRTZWVuQ291bnQgPz8gMCkgKyAxXG4gICAgICBzYXZlR2xvYmFsQ29uZmlnKHByZXYgPT4ge1xuICAgICAgICBpZiAoKHByZXYudm9pY2VGb290ZXJIaW50U2VlbkNvdW50ID8/IDApID49IG5ld0NvdW50KSByZXR1cm4gcHJldlxuICAgICAgICByZXR1cm4geyAuLi5wcmV2LCB2b2ljZUZvb3RlckhpbnRTZWVuQ291bnQ6IG5ld0NvdW50IH1cbiAgICAgIH0pXG4gICAgfVxuICB9LCBbdm9pY2VFbmFibGVkLCB2b2ljZUhpbnRVbmRlckNhcF0pXG4gIGNvbnN0IGlzS2lsbEFnZW50c0NvbmZpcm1TaG93aW5nID0gdXNlQXBwU3RhdGUoXG4gICAgcyA9PiBzLm5vdGlmaWNhdGlvbnMuY3VycmVudD8ua2V5ID09PSAna2lsbC1hZ2VudHMtY29uZmlybScsXG4gIClcblxuICAvLyBEZXJpdmUgdGVhbSBpbmZvIGZyb20gdGVhbUNvbnRleHQgKG5vIGZpbGVzeXN0ZW0gSS9PIG5lZWRlZClcbiAgLy8gTWF0Y2ggdGhlIHNhbWUgbG9naWMgYXMgVGVhbVN0YXR1cyB0byBhdm9pZCB0cmFpbGluZyBzZXBhcmF0b3JcbiAgLy8gSW4tcHJvY2VzcyBtb2RlIHVzZXMgU2hpZnQrRG93bi9VcCBuYXZpZ2F0aW9uLCBub3QgZm9vdGVyIHRlYW1zIG1lbnVcbiAgY29uc3QgaGFzVGVhbXMgPVxuICAgIGlzQWdlbnRTd2FybXNFbmFibGVkKCkgJiZcbiAgICAhaXNJblByb2Nlc3NFbmFibGVkKCkgJiZcbiAgICB0ZWFtQ29udGV4dCAhPT0gdW5kZWZpbmVkICYmXG4gICAgY291bnQoT2JqZWN0LnZhbHVlcyh0ZWFtQ29udGV4dC50ZWFtbWF0ZXMpLCB0ID0+IHQubmFtZSAhPT0gJ3RlYW0tbGVhZCcpID4gMFxuXG4gIGlmIChtb2RlID09PSAnYmFzaCcpIHtcbiAgICByZXR1cm4gPFRleHQgY29sb3I9XCJiYXNoQm9yZGVyXCI+ISBmb3IgYmFzaCBtb2RlPC9UZXh0PlxuICB9XG5cbiAgY29uc3QgY3VycmVudE1vZGUgPSB0b29sUGVybWlzc2lvbkNvbnRleHQ/Lm1vZGVcbiAgY29uc3QgaGFzQWN0aXZlTW9kZSA9ICFpc0RlZmF1bHRNb2RlKGN1cnJlbnRNb2RlKVxuICBjb25zdCB2aWV3ZWRUYXNrID0gdmlld2luZ0FnZW50VGFza0lkID8gdGFza3Nbdmlld2luZ0FnZW50VGFza0lkXSA6IHVuZGVmaW5lZFxuICBjb25zdCBpc1ZpZXdpbmdUZWFtbWF0ZSA9XG4gICAgdmlld1NlbGVjdGlvbk1vZGUgPT09ICd2aWV3aW5nLWFnZW50JyAmJlxuICAgIHZpZXdlZFRhc2s/LnR5cGUgPT09ICdpbl9wcm9jZXNzX3RlYW1tYXRlJ1xuICBjb25zdCBpc1ZpZXdpbmdDb21wbGV0ZWRUZWFtbWF0ZSA9XG4gICAgaXNWaWV3aW5nVGVhbW1hdGUgJiYgdmlld2VkVGFzayAhPSBudWxsICYmIHZpZXdlZFRhc2suc3RhdHVzICE9PSAncnVubmluZydcbiAgY29uc3QgaGFzQmFja2dyb3VuZFRhc2tzID0gcnVubmluZ1Rhc2tDb3VudCA+IDAgfHwgaXNWaWV3aW5nVGVhbW1hdGVcblxuICAvLyBDb3VudCBwcmltYXJ5IGl0ZW1zIChwZXJtaXNzaW9uIG1vZGUgb3IgY29vcmRpbmF0b3IgbW9kZSwgYmFja2dyb3VuZCB0YXNrcywgYW5kIHRlYW1zKVxuICBjb25zdCBwcmltYXJ5SXRlbUNvdW50ID1cbiAgICAoaXNDb29yZGluYXRvciB8fCBoYXNBY3RpdmVNb2RlID8gMSA6IDApICtcbiAgICAoaGFzQmFja2dyb3VuZFRhc2tzID8gMSA6IDApICtcbiAgICAoaGFzVGVhbXMgPyAxIDogMClcblxuICAvLyBQUiBpbmRpY2F0b3IgaXMgc2hvcnQgKH4xMCBjaGFycykg4oCUIHVubGlrZSB0aGUgb2xkIGRpZmYgaW5kaWNhdG9yIHRoZVxuICAvLyA+PTEwMCB0aHJlc2hvbGQgd2FzIHR1bmVkIGZvci4gTm93IHRoYXQgYXV0byBtb2RlIGlzIGVmZmVjdGl2ZWx5IHRoZVxuICAvLyBiYXNlbGluZSwgcHJpbWFyeUl0ZW1Db3VudCBpcyDiiaUxIGZvciBtb3N0IHNlc3Npb25zOyBrZWVwIHRoZSB0aHJlc2hvbGRcbiAgLy8gbG93IGVub3VnaCB0byBzaG93IFBSIHN0YXR1cyBvbiBzdGFuZGFyZCA4MC1jb2wgdGVybWluYWxzLlxuICBjb25zdCBzaG91bGRTaG93UHJTdGF0dXMgPVxuICAgIGlzUHJTdGF0dXNFbmFibGVkKCkgJiZcbiAgICBwclN0YXR1cy5udW1iZXIgIT09IG51bGwgJiZcbiAgICBwclN0YXR1cy5yZXZpZXdTdGF0ZSAhPT0gbnVsbCAmJlxuICAgIHByU3RhdHVzLnVybCAhPT0gbnVsbCAmJlxuICAgIHByaW1hcnlJdGVtQ291bnQgPCAyICYmXG4gICAgKHByaW1hcnlJdGVtQ291bnQgPT09IDAgfHwgY29sdW1ucyA+PSA4MClcblxuICAvLyBIaWRlIHRoZSBzaGlmdCt0YWIgaGludCB3aGVuIHRoZXJlIGFyZSAyIHByaW1hcnkgaXRlbXNcbiAgY29uc3Qgc2hvdWxkU2hvd01vZGVIaW50ID0gcHJpbWFyeUl0ZW1Db3VudCA8IDJcblxuICAvLyBDaGVjayBpZiB3ZSBoYXZlIGluLXByb2Nlc3MgdGVhbW1hdGVzIChzaG93aW5nIHBpbGxzKVxuICAvLyBJbiBzcGlubmVyLXRyZWUgbW9kZSwgcGlsbHMgYXJlIGRpc2FibGVkIC0gdGVhbW1hdGVzIGFwcGVhciBpbiB0aGUgc3Bpbm5lciB0cmVlIGluc3RlYWRcbiAgY29uc3QgaGFzSW5Qcm9jZXNzVGVhbW1hdGVzID1cbiAgICAhc2hvd1NwaW5uZXJUcmVlICYmXG4gICAgaGFzQmFja2dyb3VuZFRhc2tzICYmXG4gICAgT2JqZWN0LnZhbHVlcyh0YXNrcykuc29tZSh0ID0+IHQudHlwZSA9PT0gJ2luX3Byb2Nlc3NfdGVhbW1hdGUnKVxuICBjb25zdCBoYXNUZWFtbWF0ZVBpbGxzID1cbiAgICBoYXNJblByb2Nlc3NUZWFtbWF0ZXMgfHwgKCFzaG93U3Bpbm5lclRyZWUgJiYgaXNWaWV3aW5nVGVhbW1hdGUpXG5cbiAgLy8gSW4gcmVtb3RlIG1vZGUgKGBjbGF1ZGUgYXNzaXN0YW50YCwgLS10ZWxlcG9ydCkgdGhlIGFnZW50IHJ1bnMgZWxzZXdoZXJlO1xuICAvLyB0aGUgbG9jYWwgcGVybWlzc2lvbiBtb2RlIHNob3duIGhlcmUgZG9lc24ndCByZWZsZWN0IHRoZSBhZ2VudCdzIHN0YXRlLlxuICAvLyBSZW5kZXJlZCBiZWZvcmUgdGhlIHRhc2tzIHBpbGwgc28gYSBsb25nIHBpbGwgbGFiZWwgKGUuZy4gdWx0cmFwbGFuIFVSTClcbiAgLy8gZG9lc24ndCBwdXNoIHRoZSBtb2RlIGluZGljYXRvciBvZmYtc2NyZWVuLlxuICBjb25zdCBtb2RlUGFydCA9XG4gICAgY3VycmVudE1vZGUgJiYgaGFzQWN0aXZlTW9kZSAmJiAhZ2V0SXNSZW1vdGVNb2RlKCkgPyAoXG4gICAgICA8VGV4dCBjb2xvcj17Z2V0TW9kZUNvbG9yKGN1cnJlbnRNb2RlKX0ga2V5PVwibW9kZVwiPlxuICAgICAgICB7cGVybWlzc2lvbk1vZGVTeW1ib2woY3VycmVudE1vZGUpfXsnICd9XG4gICAgICAgIHtwZXJtaXNzaW9uTW9kZVRpdGxlKGN1cnJlbnRNb2RlKS50b0xvd2VyQ2FzZSgpfSBvblxuICAgICAgICB7c2hvdWxkU2hvd01vZGVIaW50ICYmIChcbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHsnICd9XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgc2hvcnRjdXQ9e21vZGVDeWNsZVNob3J0Y3V0fVxuICAgICAgICAgICAgICBhY3Rpb249XCJjeWNsZVwiXG4gICAgICAgICAgICAgIHBhcmVuc1xuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICA8L1RleHQ+XG4gICAgKSA6IG51bGxcblxuICAvLyBCdWlsZCBwYXJ0cyBhcnJheSAtIGV4Y2x1ZGUgQmFja2dyb3VuZFRhc2tTdGF0dXMgd2hlbiB3ZSBoYXZlIHRlYW1tYXRlIHBpbGxzXG4gIC8vICh0ZWFtbWF0ZSBwaWxscyBnZXQgdGhlaXIgb3duIHJvdylcbiAgY29uc3QgcGFydHMgPSBbXG4gICAgLy8gUmVtb3RlIHNlc3Npb24gaW5kaWNhdG9yXG4gICAgLi4uKHJlbW90ZVNlc3Npb25VcmxcbiAgICAgID8gW1xuICAgICAgICAgIDxMaW5rIHVybD17cmVtb3RlU2Vzc2lvblVybH0ga2V5PVwicmVtb3RlXCI+XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImlkZVwiPntmaWd1cmVzLmNpcmNsZURvdWJsZX0gcmVtb3RlPC9UZXh0PlxuICAgICAgICAgIDwvTGluaz4sXG4gICAgICAgIF1cbiAgICAgIDogW10pLFxuICAgIC8vIEJhY2tncm91bmRUYXNrU3RhdHVzIGlzIE5PVCBpbiBwYXJ0cyDigJQgaXQgcmVuZGVycyBhcyBhIEJveCBzaWJsaW5nIHNvXG4gICAgLy8gaXRzIGNsaWNrLXRhcmdldCBCb3ggaXNuJ3QgbmVzdGVkIGluc2lkZSB0aGUgPFRleHQgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgLy8gd3JhcHBlciAocmVjb25jaWxlciB0aHJvd3Mgb24gQm94LWluLVRleHQpLlxuICAgIC8vIFRtdXggcGlsbCAoYW50LW9ubHkpIOKAlCBhcHBlYXJzIHJpZ2h0IGFmdGVyIHRhc2tzIGluIG5hdiBvcmRlclxuICAgIC4uLihcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIGhhc1RtdXhTZXNzaW9uXG4gICAgICA/IFs8VHVuZ3N0ZW5QaWxsIGtleT1cInRtdXhcIiBzZWxlY3RlZD17dG11eFNlbGVjdGVkfSAvPl1cbiAgICAgIDogW10pLFxuICAgIC4uLihpc0FnZW50U3dhcm1zRW5hYmxlZCgpICYmIGhhc1RlYW1zXG4gICAgICA/IFtcbiAgICAgICAgICA8VGVhbVN0YXR1c1xuICAgICAgICAgICAga2V5PVwidGVhbXNcIlxuICAgICAgICAgICAgdGVhbXNTZWxlY3RlZD17dGVhbXNTZWxlY3RlZH1cbiAgICAgICAgICAgIHNob3dIaW50PXtzaG93SGludCAmJiAhaGFzQmFja2dyb3VuZFRhc2tzfVxuICAgICAgICAgIC8+LFxuICAgICAgICBdXG4gICAgICA6IFtdKSxcbiAgICAuLi4oc2hvdWxkU2hvd1ByU3RhdHVzXG4gICAgICA/IFtcbiAgICAgICAgICA8UHJCYWRnZVxuICAgICAgICAgICAga2V5PVwicHItc3RhdHVzXCJcbiAgICAgICAgICAgIG51bWJlcj17cHJTdGF0dXMubnVtYmVyIX1cbiAgICAgICAgICAgIHVybD17cHJTdGF0dXMudXJsIX1cbiAgICAgICAgICAgIHJldmlld1N0YXRlPXtwclN0YXR1cy5yZXZpZXdTdGF0ZSF9XG4gICAgICAgICAgLz4sXG4gICAgICAgIF1cbiAgICAgIDogW10pLFxuICBdXG5cbiAgLy8gQ2hlY2sgaWYgYW55IGluLXByb2Nlc3MgdGVhbW1hdGVzIGV4aXN0IChmb3IgaGludCB0ZXh0IGN5Y2xpbmcpXG4gIGNvbnN0IGhhc0FueUluUHJvY2Vzc1RlYW1tYXRlcyA9IE9iamVjdC52YWx1ZXModGFza3MpLnNvbWUoXG4gICAgdCA9PiB0LnR5cGUgPT09ICdpbl9wcm9jZXNzX3RlYW1tYXRlJyAmJiB0LnN0YXR1cyA9PT0gJ3J1bm5pbmcnLFxuICApXG4gIGNvbnN0IGhhc1J1bm5pbmdBZ2VudFRhc2tzID0gT2JqZWN0LnZhbHVlcyh0YXNrcykuc29tZShcbiAgICB0ID0+IHQudHlwZSA9PT0gJ2xvY2FsX2FnZW50JyAmJiB0LnN0YXR1cyA9PT0gJ3J1bm5pbmcnLFxuICApXG5cbiAgLy8gR2V0IGhpbnQgcGFydHMgc2VwYXJhdGVseSBmb3IgcG90ZW50aWFsIHNlY29uZC1saW5lIHJlbmRlcmluZ1xuICBjb25zdCBoaW50UGFydHMgPSBzaG93SGludFxuICAgID8gZ2V0U3Bpbm5lckhpbnRQYXJ0cyhcbiAgICAgICAgaXNMb2FkaW5nLFxuICAgICAgICBlc2NTaG9ydGN1dCxcbiAgICAgICAgdG9kb3NTaG9ydGN1dCxcbiAgICAgICAga2lsbEFnZW50c1Nob3J0Y3V0LFxuICAgICAgICBoYXNUYXNrSXRlbXMsXG4gICAgICAgIGV4cGFuZGVkVmlldyxcbiAgICAgICAgaGFzQW55SW5Qcm9jZXNzVGVhbW1hdGVzLFxuICAgICAgICBoYXNSdW5uaW5nQWdlbnRUYXNrcyxcbiAgICAgICAgaXNLaWxsQWdlbnRzQ29uZmlybVNob3dpbmcsXG4gICAgICApXG4gICAgOiBbXVxuXG4gIGlmIChpc1ZpZXdpbmdDb21wbGV0ZWRUZWFtbWF0ZSkge1xuICAgIHBhcnRzLnB1c2goXG4gICAgICA8VGV4dCBkaW1Db2xvciBrZXk9XCJlc2MtcmV0dXJuXCI+XG4gICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludFxuICAgICAgICAgIHNob3J0Y3V0PXtlc2NTaG9ydGN1dH1cbiAgICAgICAgICBhY3Rpb249XCJyZXR1cm4gdG8gdGVhbSBsZWFkXCJcbiAgICAgICAgLz5cbiAgICAgIDwvVGV4dD4sXG4gICAgKVxuICB9IGVsc2UgaWYgKChmZWF0dXJlKCdQUk9BQ1RJVkUnKSB8fCBmZWF0dXJlKCdLQUlST1MnKSkgJiYgaGFzTmV4dFRpY2spIHtcbiAgICBwYXJ0cy5wdXNoKDxQcm9hY3RpdmVDb3VudGRvd24ga2V5PVwicHJvYWN0aXZlXCIgLz4pXG4gIH0gZWxzZSBpZiAoIWhhc1RlYW1tYXRlUGlsbHMgJiYgc2hvd0hpbnQpIHtcbiAgICBwYXJ0cy5wdXNoKC4uLmhpbnRQYXJ0cylcbiAgfVxuXG4gIC8vIFdoZW4gd2UgaGF2ZSB0ZWFtbWF0ZSBwaWxscywgYWx3YXlzIHJlbmRlciB0aGVtIG9uIHRoZWlyIG93biBsaW5lIGFib3ZlIG90aGVyIHBhcnRzXG4gIGlmIChoYXNUZWFtbWF0ZVBpbGxzKSB7XG4gICAgLy8gRG9uJ3QgYXBwZW5kIHNwaW5uZXIgaGludHMgd2hlbiB2aWV3aW5nIGEgY29tcGxldGVkIHRlYW1tYXRlIOKAlFxuICAgIC8vIHRoZSBcImVzYyB0byByZXR1cm4gdG8gdGVhbSBsZWFkXCIgaGludCBhbHJlYWR5IHJlcGxhY2VzIFwiZXNjIHRvIGludGVycnVwdFwiXG4gICAgY29uc3Qgb3RoZXJQYXJ0cyA9IFtcbiAgICAgIC4uLihtb2RlUGFydCA/IFttb2RlUGFydF0gOiBbXSksXG4gICAgICAuLi5wYXJ0cyxcbiAgICAgIC4uLihpc1ZpZXdpbmdDb21wbGV0ZWRUZWFtbWF0ZSA/IFtdIDogaGludFBhcnRzKSxcbiAgICBdXG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxCYWNrZ3JvdW5kVGFza1N0YXR1c1xuICAgICAgICAgICAgdGFza3NTZWxlY3RlZD17dGFza3NTZWxlY3RlZH1cbiAgICAgICAgICAgIGlzVmlld2luZ1RlYW1tYXRlPXtpc1ZpZXdpbmdUZWFtbWF0ZX1cbiAgICAgICAgICAgIHRlYW1tYXRlRm9vdGVySW5kZXg9e3RlYW1tYXRlRm9vdGVySW5kZXh9XG4gICAgICAgICAgICBpc0xlYWRlcklkbGU9eyFpc0xvYWRpbmd9XG4gICAgICAgICAgICBvbk9wZW5EaWFsb2c9e29uT3BlblRhc2tzRGlhbG9nfVxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7b3RoZXJQYXJ0cy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgPEJ5bGluZT57b3RoZXJQYXJ0c308L0J5bGluZT5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIC8vIEFkZCBcIuKGkyB0byBtYW5hZ2UgdGFza3NcIiBoaW50IHdoZW4gcGFuZWwgaGFzIHZpc2libGUgcm93c1xuICBjb25zdCBoYXNDb29yZGluYXRvclRhc2tzID1cbiAgICBcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIGdldFZpc2libGVBZ2VudFRhc2tzKHRhc2tzKS5sZW5ndGggPiAwXG5cbiAgLy8gVGFza3MgcGlsbCByZW5kZXJzIGFzIGEgQm94IHNpYmxpbmcgKG5vdCBhIHBhcnRzIGVudHJ5KSBzbyBpdHNcbiAgLy8gY2xpY2stdGFyZ2V0IEJveCBpc24ndCBuZXN0ZWQgaW5zaWRlIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPiDigJQgdGhlXG4gIC8vIHJlY29uY2lsZXIgdGhyb3dzIG9uIEJveC1pbi1UZXh0LiBDb21wdXRlZCBoZXJlIHNvIHRoZSBlbXB0eS1jaGVja3NcbiAgLy8gYmVsb3cgc3RpbGwgdHJlYXQgXCJwaWxsIHByZXNlbnRcIiBhcyBub24tZW1wdHkuXG4gIGNvbnN0IHRhc2tzUGFydCA9XG4gICAgaGFzQmFja2dyb3VuZFRhc2tzICYmXG4gICAgIWhhc1RlYW1tYXRlUGlsbHMgJiZcbiAgICAhc2hvdWxkSGlkZVRhc2tzRm9vdGVyKHRhc2tzLCBzaG93U3Bpbm5lclRyZWUpID8gKFxuICAgICAgPEJhY2tncm91bmRUYXNrU3RhdHVzXG4gICAgICAgIHRhc2tzU2VsZWN0ZWQ9e3Rhc2tzU2VsZWN0ZWR9XG4gICAgICAgIGlzVmlld2luZ1RlYW1tYXRlPXtpc1ZpZXdpbmdUZWFtbWF0ZX1cbiAgICAgICAgdGVhbW1hdGVGb290ZXJJbmRleD17dGVhbW1hdGVGb290ZXJJbmRleH1cbiAgICAgICAgaXNMZWFkZXJJZGxlPXshaXNMb2FkaW5nfVxuICAgICAgICBvbk9wZW5EaWFsb2c9e29uT3BlblRhc2tzRGlhbG9nfVxuICAgICAgLz5cbiAgICApIDogbnVsbFxuXG4gIGlmIChwYXJ0cy5sZW5ndGggPT09IDAgJiYgIXRhc2tzUGFydCAmJiAhbW9kZVBhcnQgJiYgc2hvd0hpbnQpIHtcbiAgICBwYXJ0cy5wdXNoKFxuICAgICAgPFRleHQgZGltQ29sb3Iga2V5PVwic2hvcnRjdXRzLWhpbnRcIj5cbiAgICAgICAgPyBmb3Igc2hvcnRjdXRzXG4gICAgICA8L1RleHQ+LFxuICAgIClcbiAgfVxuXG4gIC8vIE9ubHkgcmVwbGFjZSB0aGUgaWRsZSB2b2ljZSBoaW50IHdoZW4gdGhlcmUncyBzb21ldGhpbmcgdG8gc2F5IOKAlCBvdGhlcndpc2VcbiAgLy8gZmFsbCB0aHJvdWdoIGluc3RlYWQgb2Ygc2hvd2luZyBhbiBlbXB0eSBCeWxpbmUuIFwiZXNjIHRvIGNsZWFyXCIgd2FzIHJlbW92ZWRcbiAgLy8gKGxvb2tlZCBsaWtlIFwiZXNjIHRvIGludGVycnVwdFwiIHdoZW4gaWRsZTsgZXNjLWNsZWFycy1zZWxlY3Rpb24gaXMgc3RhbmRhcmRcbiAgLy8gVVgpIGxlYXZpbmcgb25seSBjdHJsK2MgKGNvcHlPblNlbGVjdCBvZmYpIGFuZCB0aGUgeHRlcm0uanMgbmF0aXZlLXNlbGVjdCBoaW50LlxuICBjb25zdCBjb3B5T25TZWxlY3QgPSBnZXRHbG9iYWxDb25maWcoKS5jb3B5T25TZWxlY3QgPz8gdHJ1ZVxuICBjb25zdCBzZWxlY3Rpb25IaW50SGFzQ29udGVudCA9IGhhc1NlbGVjdGlvbiAmJiAoIWNvcHlPblNlbGVjdCB8fCBpc1h0ZXJtSnMoKSlcblxuICAvLyBXYXJtdXAgaGludCB0YWtlcyBwcmlvcml0eSDigJQgd2hlbiB0aGUgdXNlciBpcyBhY3RpdmVseSBob2xkaW5nXG4gIC8vIHRoZSBhY3RpdmF0aW9uIGtleSwgc2hvdyBmZWVkYmFjayByZWdhcmRsZXNzIG9mIG90aGVyIGhpbnRzLlxuICBpZiAoZmVhdHVyZSgnVk9JQ0VfTU9ERScpICYmIHZvaWNlRW5hYmxlZCAmJiB2b2ljZVdhcm1pbmdVcCkge1xuICAgIHBhcnRzLnB1c2goPFZvaWNlV2FybXVwSGludCBrZXk9XCJ2b2ljZS13YXJtdXBcIiAvPilcbiAgfSBlbHNlIGlmIChpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgJiYgc2VsZWN0aW9uSGludEhhc0NvbnRlbnQpIHtcbiAgICAvLyB4dGVybS5qcyAoVlMgQ29kZS9DdXJzb3IvV2luZHN1cmYpIGZvcmNlLXNlbGVjdGlvbiBtb2RpZmllciBpc1xuICAgIC8vIHBsYXRmb3JtLXNwZWNpZmljIGFuZCBnYXRlZCBvbiBtYWNPUyAoU2VsZWN0aW9uU2VydmljZS5zaG91bGRGb3JjZVNlbGVjdGlvbik6XG4gICAgLy8gICBtYWNPUzogICAgIGFsdEtleSAmJiBtYWNPcHRpb25DbGlja0ZvcmNlc1NlbGVjdGlvbiAoVlMgQ29kZSBkZWZhdWx0OiBmYWxzZSlcbiAgICAvLyAgIG5vbi1tYWNPUzogc2hpZnRLZXlcbiAgICAvLyBPbiBtYWNPUywgaWYgd2UgUkVDRUlWRUQgYW4gYWx0K2NsaWNrIChsYXN0UHJlc3NIYWRBbHQpLCB0aGUgVlMgQ29kZVxuICAgIC8vIHNldHRpbmcgaXMgb2ZmIOKAlCB4dGVybS5qcyB3b3VsZCBoYXZlIGNvbnN1bWVkIHRoZSBldmVudCBvdGhlcndpc2UuXG4gICAgLy8gVGVsbCB0aGUgdXNlciB0aGUgZXhhY3Qgc2V0dGluZyB0byBmbGlwIGluc3RlYWQgb2YgcmVwZWF0aW5nIHRoZVxuICAgIC8vIG9wdGlvbitjbGljayBoaW50IHRoZXkganVzdCB0cmllZC5cbiAgICAvLyBOb24tcmVhY3RpdmUgZ2V0U3RhdGUoKSByZWFkIGlzIHNhZmU6IGxhc3RQcmVzc0hhZEFsdCBpcyBpbW11dGFibGVcbiAgICAvLyB3aGlsZSBoYXNTZWxlY3Rpb24gaXMgdHJ1ZSAoc2V0IHByZS1kcmFnLCBjbGVhcmVkIHdpdGggc2VsZWN0aW9uKS5cbiAgICBjb25zdCBpc01hYyA9IGdldFBsYXRmb3JtKCkgPT09ICdtYWNvcydcbiAgICBjb25zdCBhbHRDbGlja0ZhaWxlZCA9IGlzTWFjICYmIChzZWxHZXRTdGF0ZSgpPy5sYXN0UHJlc3NIYWRBbHQgPz8gZmFsc2UpXG4gICAgcGFydHMucHVzaChcbiAgICAgIDxUZXh0IGRpbUNvbG9yIGtleT1cInNlbGVjdGlvbi1jb3B5XCI+XG4gICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgeyFjb3B5T25TZWxlY3QgJiYgKFxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiY3RybCtjXCIgYWN0aW9uPVwiY29weVwiIC8+XG4gICAgICAgICAgKX1cbiAgICAgICAgICB7aXNYdGVybUpzKCkgJiZcbiAgICAgICAgICAgIChhbHRDbGlja0ZhaWxlZCA/IChcbiAgICAgICAgICAgICAgPFRleHQ+c2V0IG1hY09wdGlvbkNsaWNrRm9yY2VzU2VsZWN0aW9uIGluIFZTIENvZGUgc2V0dGluZ3M8L1RleHQ+XG4gICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgICBzaG9ydGN1dD17aXNNYWMgPyAnb3B0aW9uK2NsaWNrJyA6ICdzaGlmdCtjbGljayd9XG4gICAgICAgICAgICAgICAgYWN0aW9uPVwibmF0aXZlIHNlbGVjdFwiXG4gICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICApKX1cbiAgICAgICAgPC9CeWxpbmU+XG4gICAgICA8L1RleHQ+LFxuICAgIClcbiAgfSBlbHNlIGlmIChcbiAgICBmZWF0dXJlKCdWT0lDRV9NT0RFJykgJiZcbiAgICBwYXJ0cy5sZW5ndGggPiAwICYmXG4gICAgc2hvd0hpbnQgJiZcbiAgICB2b2ljZUVuYWJsZWQgJiZcbiAgICB2b2ljZVN0YXRlID09PSAnaWRsZScgJiZcbiAgICBoaW50UGFydHMubGVuZ3RoID09PSAwICYmXG4gICAgdm9pY2VIaW50VW5kZXJDYXBcbiAgKSB7XG4gICAgcGFydHMucHVzaChcbiAgICAgIDxUZXh0IGRpbUNvbG9yIGtleT1cInZvaWNlLWhpbnRcIj5cbiAgICAgICAgaG9sZCB7dm9pY2VLZXlTaG9ydGN1dH0gdG8gc3BlYWtcbiAgICAgIDwvVGV4dD4sXG4gICAgKVxuICB9XG5cbiAgaWYgKCh0YXNrc1BhcnQgfHwgaGFzQ29vcmRpbmF0b3JUYXNrcykgJiYgc2hvd0hpbnQgJiYgIWhhc1RlYW1zKSB7XG4gICAgcGFydHMucHVzaChcbiAgICAgIDxUZXh0IGRpbUNvbG9yIGtleT1cIm1hbmFnZS10YXNrc1wiPlxuICAgICAgICB7dGFza3NTZWxlY3RlZCA/IChcbiAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cInZpZXcgdGFza3NcIiAvPlxuICAgICAgICApIDogKFxuICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIuKGk1wiIGFjdGlvbj1cIm1hbmFnZVwiIC8+XG4gICAgICAgICl9XG4gICAgICA8L1RleHQ+LFxuICAgIClcbiAgfVxuXG4gIC8vIEluIGZ1bGxzY3JlZW4gdGhlIGJvdHRvbSBzZWN0aW9uIGlzIGZsZXhTaHJpbms6MCDigJQgZXZlcnkgcm93IGhlcmVcbiAgLy8gaXMgYSByb3cgc3RvbGVuIGZyb20gdGhlIFNjcm9sbEJveC4gVGhpcyBjb21wb25lbnQgbXVzdCBoYXZlIGEgU1RBQkxFXG4gIC8vIGhlaWdodCBzbyB0aGUgZm9vdGVyIG5ldmVyIGdyb3dzL3Nocmlua3MgYW5kIHNoaWZ0cyBzY3JvbGwgY29udGVudC5cbiAgLy8gUmV0dXJuaW5nIG51bGwgd2hlbiBwYXJ0cyBpcyBlbXB0eSAoZS5nLiBTdGF0dXNMaW5lIG9uIOKGkiBzdXBwcmVzc0hpbnRcbiAgLy8g4oaSIHNob3dIaW50PWZhbHNlIOKGkiBubyBcIj8gZm9yIHNob3J0Y3V0c1wiKSB3b3VsZCBsZXQgYSBsYXRlci1hZGRlZFxuICAvLyBwYXJ0IChlLmcuIHRoZSBzZWxlY3Rpb24gY29weS9uYXRpdmUtc2VsZWN0IGhpbnRzKSBncm93IHRoZSBjb2x1bW5cbiAgLy8gZnJvbSAw4oaSMSByb3cuIEFsd2F5cyByZW5kZXIgMSByb3cgaW4gZnVsbHNjcmVlbjsgcmV0dXJuIGEgc3BhY2Ugd2hlblxuICAvLyBlbXB0eSBzbyBZb2dhIHJlc2VydmVzIHRoZSByb3cgd2l0aG91dCBwYWludGluZyBhbnl0aGluZyB2aXNpYmxlLlxuICBpZiAocGFydHMubGVuZ3RoID09PSAwICYmICF0YXNrc1BhcnQgJiYgIW1vZGVQYXJ0KSB7XG4gICAgcmV0dXJuIGlzRnVsbHNjcmVlbkVudkVuYWJsZWQoKSA/IDxUZXh0PiA8L1RleHQ+IDogbnVsbFxuICB9XG5cbiAgLy8gZmxleFNocmluaz0wIGtlZXBzIG1vZGUgKyBwaWxsIGF0IG5hdHVyYWwgd2lkdGg7IHRoZSByZW1haW5pbmcgcGFydHNcbiAgLy8gdHJ1bmNhdGUgYXQgdGhlIHRhaWwgYXMgb25lIHN0cmluZyBpbnNpZGUgdGhlIFRleHQgd3JhcHBlci5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGhlaWdodD17MX0gb3ZlcmZsb3c9XCJoaWRkZW5cIj5cbiAgICAgIHttb2RlUGFydCAmJiAoXG4gICAgICAgIDxCb3ggZmxleFNocmluaz17MH0+XG4gICAgICAgICAge21vZGVQYXJ0fVxuICAgICAgICAgIHsodGFza3NQYXJ0IHx8IHBhcnRzLmxlbmd0aCA+IDApICYmIDxUZXh0IGRpbUNvbG9yPiDCtyA8L1RleHQ+fVxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgICB7dGFza3NQYXJ0ICYmIChcbiAgICAgICAgPEJveCBmbGV4U2hyaW5rPXswfT5cbiAgICAgICAgICB7dGFza3NQYXJ0fVxuICAgICAgICAgIHtwYXJ0cy5sZW5ndGggPiAwICYmIDxUZXh0IGRpbUNvbG9yPiDCtyA8L1RleHQ+fVxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgICB7cGFydHMubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgIDxCeWxpbmU+e3BhcnRzfTwvQnlsaW5lPlxuICAgICAgICA8L1RleHQ+XG4gICAgICApfVxuICAgIDwvQm94PlxuICApXG59XG5cbmZ1bmN0aW9uIGdldFNwaW5uZXJIaW50UGFydHMoXG4gIGlzTG9hZGluZzogYm9vbGVhbixcbiAgZXNjU2hvcnRjdXQ6IHN0cmluZyxcbiAgdG9kb3NTaG9ydGN1dDogc3RyaW5nLFxuICBraWxsQWdlbnRzU2hvcnRjdXQ6IHN0cmluZyxcbiAgaGFzVGFza0l0ZW1zOiBib29sZWFuLFxuICBleHBhbmRlZFZpZXc6ICdub25lJyB8ICd0YXNrcycgfCAndGVhbW1hdGVzJyxcbiAgaGFzVGVhbW1hdGVzOiBib29sZWFuLFxuICBoYXNSdW5uaW5nQWdlbnRUYXNrczogYm9vbGVhbixcbiAgaXNLaWxsQWdlbnRzQ29uZmlybVNob3dpbmc6IGJvb2xlYW4sXG4pOiBSZWFjdC5SZWFjdEVsZW1lbnRbXSB7XG4gIGxldCB0b2dnbGVBY3Rpb246IHN0cmluZ1xuICBpZiAoaGFzVGVhbW1hdGVzKSB7XG4gICAgLy8gQ3ljbGluZzogbm9uZSDihpIgdGFza3Mg4oaSIHRlYW1tYXRlcyDihpIgbm9uZVxuICAgIHN3aXRjaCAoZXhwYW5kZWRWaWV3KSB7XG4gICAgICBjYXNlICdub25lJzpcbiAgICAgICAgdG9nZ2xlQWN0aW9uID0gJ3Nob3cgdGFza3MnXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICd0YXNrcyc6XG4gICAgICAgIHRvZ2dsZUFjdGlvbiA9ICdzaG93IHRlYW1tYXRlcydcbiAgICAgICAgYnJlYWtcbiAgICAgIGNhc2UgJ3RlYW1tYXRlcyc6XG4gICAgICAgIHRvZ2dsZUFjdGlvbiA9ICdoaWRlJ1xuICAgICAgICBicmVha1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICB0b2dnbGVBY3Rpb24gPSBleHBhbmRlZFZpZXcgPT09ICd0YXNrcycgPyAnaGlkZSB0YXNrcycgOiAnc2hvdyB0YXNrcydcbiAgfVxuXG4gIC8vIFNob3cgdGhlIHRvZ2dsZSBoaW50IG9ubHkgd2hlbiB0aGVyZSBhcmUgdGFzayBpdGVtcyB0byBkaXNwbGF5IG9yXG4gIC8vIHRlYW1tYXRlcyB0byBjeWNsZSB0b1xuICBjb25zdCBzaG93VG9nZ2xlSGludCA9IGhhc1Rhc2tJdGVtcyB8fCBoYXNUZWFtbWF0ZXNcblxuICByZXR1cm4gW1xuICAgIC4uLihpc0xvYWRpbmdcbiAgICAgID8gW1xuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGtleT1cImVzY1wiPlxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PXtlc2NTaG9ydGN1dH0gYWN0aW9uPVwiaW50ZXJydXB0XCIgLz5cbiAgICAgICAgICA8L1RleHQ+LFxuICAgICAgICBdXG4gICAgICA6IFtdKSxcbiAgICAuLi4oIWlzTG9hZGluZyAmJiBoYXNSdW5uaW5nQWdlbnRUYXNrcyAmJiAhaXNLaWxsQWdlbnRzQ29uZmlybVNob3dpbmdcbiAgICAgID8gW1xuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGtleT1cImtpbGwtYWdlbnRzXCI+XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgc2hvcnRjdXQ9e2tpbGxBZ2VudHNTaG9ydGN1dH1cbiAgICAgICAgICAgICAgYWN0aW9uPVwic3RvcCBhZ2VudHNcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L1RleHQ+LFxuICAgICAgICBdXG4gICAgICA6IFtdKSxcbiAgICAuLi4oc2hvd1RvZ2dsZUhpbnRcbiAgICAgID8gW1xuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGtleT1cInRvZ2dsZS10YXNrc1wiPlxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgIHNob3J0Y3V0PXt0b2Rvc1Nob3J0Y3V0fVxuICAgICAgICAgICAgICBhY3Rpb249e3RvZ2dsZUFjdGlvbn1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9UZXh0PixcbiAgICAgICAgXVxuICAgICAgOiBbXSksXG4gIF1cbn1cblxuZnVuY3Rpb24gaXNQclN0YXR1c0VuYWJsZWQoKTogYm9vbGVhbiB7XG4gIHJldHVybiBnZXRH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dEosS0FBSyxDQUFDNkwsWUFBWSxFQUFFLENBQUM7RUFDdEIsSUFBSUMsWUFBWSxFQUFFLE1BQU07RUFDeEIsSUFBSUYsWUFBWSxFQUFFO0lBQ2hCO0lBQ0EsUUFBUXZFLFlBQVk7TUFDbEIsS0FBSyxNQUFNO1FBQ1R5RSxZQUFZLEdBQUcsWUFBWTtRQUMzQjtNQUNGLEtBQUssT0FBTztRQUNWQSxZQUFZLEdBQUcsZ0JBQWdCO1FBQy9CO01BQ0YsS0FBSyxXQUFXO1FBQ2RBLFlBQVksR0FBRyxNQUFNO1FBQ3JCO0lBQ0o7RUFDRixDQUFDLE1BQU07SUFDTEEsWUFBWSxHQUFHekUsWUFBWSxLQUFLLE9BQU8sR0FBRyxZQUFZLEdBQUcsWUFBWTtFQUN2RTs7RUFFQTtFQUNBO0VBQ0EsTUFBTTBFLGNBQWMsR0FBR3RELFlBQVksSUFBSW1ELFlBQVk7RUFFbkQsT0FBTyxDQUNMLElBQUlqSSxTQUFTLEdBQ1QsQ0FDRSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLEtBQUs7QUFDbEMsWUFBWSxDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxDQUFDZ0YsV0FBVyxDQUFDLENBQUMsTUFBTSxDQUFDLFdBQVc7QUFDM0UsVUFBVSxFQUFFLElBQUksQ0FBQyxDQUNSLEdBQ0QsRUFBRSxDQUFDLEVBQ1AsSUFBSSxDQUFDaEYsU0FBUyxJQUFJcUgsb0JBQW9CLElBQUksQ0FBQzFCLDBCQUEwQixHQUNqRSxDQUNFLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsYUFBYTtBQUMxQyxZQUFZLENBQUMsb0JBQW9CLENBQ25CLFFBQVEsQ0FBQyxDQUFDUixrQkFBa0IsQ0FBQyxDQUM3QixNQUFNLENBQUMsYUFBYTtBQUVsQyxVQUFVLEVBQUUsSUFBSSxDQUFDLENBQ1IsR0FDRCxFQUFFLENBQUMsRUFDUCxJQUFJaUQsY0FBYyxHQUNkLENBQ0UsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxjQUFjO0FBQzNDLFlBQVksQ0FBQyxvQkFBb0IsQ0FDbkIsUUFBUSxDQUFDLENBQUNsRCxhQUFhLENBQUMsQ0FDeEIsTUFBTSxDQUFDLENBQUNpRCxZQUFZLENBQUM7QUFFbkMsVUFBVSxFQUFFLElBQUksQ0FBQyxDQUNSLEdBQ0QsRUFBRSxDQUFDLENBQ1I7QUFDSDtBQUVBLFNBQVN0RSxpQkFBaUJBLENBQUEsQ0FBRSxFQUFFLE9BQU8sQ0FBQztFQUNwQyxPQUFPOUUsZUFBZSxDQUFDLENBQUMsQ0FBQ3NKLHFCQUFxQixJQUFJLElBQUk7QUFDeEQiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/PromptInput/PromptInputFooterSuggestions.tsx b/components/PromptInput/PromptInputFooterSuggestions.tsx new file mode 100644 index 0000000..98dcfee --- /dev/null +++ b/components/PromptInput/PromptInputFooterSuggestions.tsx @@ -0,0 +1,293 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { memo, type ReactNode } from 'react'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, Text } from '../../ink.js'; +import { truncatePathMiddle, truncateToWidth } from '../../utils/format.js'; +import type { Theme } from '../../utils/theme.js'; +export type SuggestionItem = { + id: string; + displayText: string; + tag?: string; + description?: string; + metadata?: unknown; + color?: keyof Theme; +}; +export type SuggestionType = 'command' | 'file' | 'directory' | 'agent' | 'shell' | 'custom-title' | 'slack-channel' | 'none'; +export const OVERLAY_MAX_ITEMS = 5; + +/** + * Get the icon for a suggestion based on its type + * Icons: + for files, ◇ for MCP resources, * for agents + */ +function getIcon(itemId: string): string { + if (itemId.startsWith('file-')) return '+'; + if (itemId.startsWith('mcp-resource-')) return '◇'; + if (itemId.startsWith('agent-')) return '*'; + return '+'; +} + +/** + * Check if an item is a unified suggestion type (file, mcp-resource, or agent) + */ +function isUnifiedSuggestion(itemId: string): boolean { + return itemId.startsWith('file-') || itemId.startsWith('mcp-resource-') || itemId.startsWith('agent-'); +} +const SuggestionItemRow = memo(function SuggestionItemRow(t0) { + const $ = _c(36); + const { + item, + maxColumnWidth, + isSelected + } = t0; + const columns = useTerminalSize().columns; + const isUnified = isUnifiedSuggestion(item.id); + if (isUnified) { + let t1; + if ($[0] !== item.id) { + t1 = getIcon(item.id); + $[0] = item.id; + $[1] = t1; + } else { + t1 = $[1]; + } + const icon = t1; + const textColor = isSelected ? "suggestion" : undefined; + const dimColor = !isSelected; + const isFile = item.id.startsWith("file-"); + const isMcpResource = item.id.startsWith("mcp-resource-"); + const separatorWidth = item.description ? 3 : 0; + let displayText; + if (isFile) { + let t2; + if ($[2] !== item.description) { + t2 = item.description ? Math.min(20, stringWidth(item.description)) : 0; + $[2] = item.description; + $[3] = t2; + } else { + t2 = $[3]; + } + const descReserve = t2; + const maxPathLength = columns - 2 - 4 - separatorWidth - descReserve; + let t3; + if ($[4] !== item.displayText || $[5] !== maxPathLength) { + t3 = truncatePathMiddle(item.displayText, maxPathLength); + $[4] = item.displayText; + $[5] = maxPathLength; + $[6] = t3; + } else { + t3 = $[6]; + } + displayText = t3; + } else { + if (isMcpResource) { + let t2; + if ($[7] !== item.displayText) { + t2 = truncateToWidth(item.displayText, 30); + $[7] = item.displayText; + $[8] = t2; + } else { + t2 = $[8]; + } + displayText = t2; + } else { + displayText = item.displayText; + } + } + const availableWidth = columns - 2 - stringWidth(displayText) - separatorWidth - 4; + let lineContent; + if (item.description) { + const maxDescLength = Math.max(0, availableWidth); + let t2; + if ($[9] !== item.description || $[10] !== maxDescLength) { + t2 = truncateToWidth(item.description.replace(/\s+/g, " "), maxDescLength); + $[9] = item.description; + $[10] = maxDescLength; + $[11] = t2; + } else { + t2 = $[11]; + } + const truncatedDesc = t2; + lineContent = `${icon} ${displayText} – ${truncatedDesc}`; + } else { + lineContent = `${icon} ${displayText}`; + } + let t2; + if ($[12] !== dimColor || $[13] !== lineContent || $[14] !== textColor) { + t2 = {lineContent}; + $[12] = dimColor; + $[13] = lineContent; + $[14] = textColor; + $[15] = t2; + } else { + t2 = $[15]; + } + return t2; + } + const maxNameWidth = Math.floor(columns * 0.4); + const displayTextWidth = Math.min(maxColumnWidth ?? stringWidth(item.displayText) + 5, maxNameWidth); + const textColor_0 = item.color || (isSelected ? "suggestion" : undefined); + const shouldDim = !isSelected; + let displayText_0 = item.displayText; + if (stringWidth(displayText_0) > displayTextWidth - 2) { + const t1 = displayTextWidth - 2; + let t2; + if ($[16] !== displayText_0 || $[17] !== t1) { + t2 = truncateToWidth(displayText_0, t1); + $[16] = displayText_0; + $[17] = t1; + $[18] = t2; + } else { + t2 = $[18]; + } + displayText_0 = t2; + } + const paddedDisplayText = displayText_0 + " ".repeat(Math.max(0, displayTextWidth - stringWidth(displayText_0))); + const tagText = item.tag ? `[${item.tag}] ` : ""; + const tagWidth = stringWidth(tagText); + const descriptionWidth = Math.max(0, columns - displayTextWidth - tagWidth - 4); + let t1; + if ($[19] !== descriptionWidth || $[20] !== item.description) { + t1 = item.description ? truncateToWidth(item.description.replace(/\s+/g, " "), descriptionWidth) : ""; + $[19] = descriptionWidth; + $[20] = item.description; + $[21] = t1; + } else { + t1 = $[21]; + } + const truncatedDescription = t1; + let t2; + if ($[22] !== paddedDisplayText || $[23] !== shouldDim || $[24] !== textColor_0) { + t2 = {paddedDisplayText}; + $[22] = paddedDisplayText; + $[23] = shouldDim; + $[24] = textColor_0; + $[25] = t2; + } else { + t2 = $[25]; + } + let t3; + if ($[26] !== tagText) { + t3 = tagText ? {tagText} : null; + $[26] = tagText; + $[27] = t3; + } else { + t3 = $[27]; + } + const t4 = isSelected ? "suggestion" : undefined; + const t5 = !isSelected; + let t6; + if ($[28] !== t4 || $[29] !== t5 || $[30] !== truncatedDescription) { + t6 = {truncatedDescription}; + $[28] = t4; + $[29] = t5; + $[30] = truncatedDescription; + $[31] = t6; + } else { + t6 = $[31]; + } + let t7; + if ($[32] !== t2 || $[33] !== t3 || $[34] !== t6) { + t7 = {t2}{t3}{t6}; + $[32] = t2; + $[33] = t3; + $[34] = t6; + $[35] = t7; + } else { + t7 = $[35]; + } + return t7; +}); +type Props = { + suggestions: SuggestionItem[]; + selectedSuggestion: number; + maxColumnWidth?: number; + /** + * When true, the suggestions are rendered inside a position=absolute + * overlay. We omit minHeight and flex-end so the y-clamp in the + * renderer doesn't push fewer items down into the prompt area. + */ + overlay?: boolean; +}; +export function PromptInputFooterSuggestions(t0) { + const $ = _c(22); + const { + suggestions, + selectedSuggestion, + maxColumnWidth: maxColumnWidthProp, + overlay + } = t0; + const { + rows + } = useTerminalSize(); + const maxVisibleItems = overlay ? OVERLAY_MAX_ITEMS : Math.min(6, Math.max(1, rows - 3)); + if (suggestions.length === 0) { + return null; + } + let t1; + if ($[0] !== maxColumnWidthProp || $[1] !== suggestions) { + t1 = maxColumnWidthProp ?? Math.max(...suggestions.map(_temp)) + 5; + $[0] = maxColumnWidthProp; + $[1] = suggestions; + $[2] = t1; + } else { + t1 = $[2]; + } + const maxColumnWidth = t1; + const startIndex = Math.max(0, Math.min(selectedSuggestion - Math.floor(maxVisibleItems / 2), suggestions.length - maxVisibleItems)); + const endIndex = Math.min(startIndex + maxVisibleItems, suggestions.length); + let T0; + let t2; + let t3; + let t4; + if ($[3] !== endIndex || $[4] !== maxColumnWidth || $[5] !== overlay || $[6] !== selectedSuggestion || $[7] !== startIndex || $[8] !== suggestions) { + const visibleItems = suggestions.slice(startIndex, endIndex); + T0 = Box; + t2 = "column"; + t3 = overlay ? undefined : "flex-end"; + let t5; + if ($[13] !== maxColumnWidth || $[14] !== selectedSuggestion || $[15] !== suggestions) { + t5 = item_0 => ; + $[13] = maxColumnWidth; + $[14] = selectedSuggestion; + $[15] = suggestions; + $[16] = t5; + } else { + t5 = $[16]; + } + t4 = visibleItems.map(t5); + $[3] = endIndex; + $[4] = maxColumnWidth; + $[5] = overlay; + $[6] = selectedSuggestion; + $[7] = startIndex; + $[8] = suggestions; + $[9] = T0; + $[10] = t2; + $[11] = t3; + $[12] = t4; + } else { + T0 = $[9]; + t2 = $[10]; + t3 = $[11]; + t4 = $[12]; + } + let t5; + if ($[17] !== T0 || $[18] !== t2 || $[19] !== t3 || $[20] !== t4) { + t5 = {t4}; + $[17] = T0; + $[18] = t2; + $[19] = t3; + $[20] = t4; + $[21] = t5; + } else { + t5 = $[21]; + } + return t5; +} +function _temp(item) { + return stringWidth(item.displayText); +} +export default memo(PromptInputFooterSuggestions); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIm1lbW8iLCJSZWFjdE5vZGUiLCJ1c2VUZXJtaW5hbFNpemUiLCJzdHJpbmdXaWR0aCIsIkJveCIsIlRleHQiLCJ0cnVuY2F0ZVBhdGhNaWRkbGUiLCJ0cnVuY2F0ZVRvV2lkdGgiLCJUaGVtZSIsIlN1Z2dlc3Rpb25JdGVtIiwiaWQiLCJkaXNwbGF5VGV4dCIsInRhZyIsImRlc2NyaXB0aW9uIiwibWV0YWRhdGEiLCJjb2xvciIsIlN1Z2dlc3Rpb25UeXBlIiwiT1ZFUkxBWV9NQVhfSVRFTVMiLCJnZXRJY29uIiwiaXRlbUlkIiwic3RhcnRzV2l0aCIsImlzVW5pZmllZFN1Z2dlc3Rpb24iLCJTdWdnZXN0aW9uSXRlbVJvdyIsInQwIiwiJCIsIl9jIiwiaXRlbSIsIm1heENvbHVtbldpZHRoIiwiaXNTZWxlY3RlZCIsImNvbHVtbnMiLCJpc1VuaWZpZWQiLCJ0MSIsImljb24iLCJ0ZXh0Q29sb3IiLCJ1bmRlZmluZWQiLCJkaW1Db2xvciIsImlzRmlsZSIsImlzTWNwUmVzb3VyY2UiLCJzZXBhcmF0b3JXaWR0aCIsInQyIiwiTWF0aCIsIm1pbiIsImRlc2NSZXNlcnZlIiwibWF4UGF0aExlbmd0aCIsInQzIiwiYXZhaWxhYmxlV2lkdGgiLCJsaW5lQ29udGVudCIsIm1heERlc2NMZW5ndGgiLCJtYXgiLCJyZXBsYWNlIiwidHJ1bmNhdGVkRGVzYyIsIm1heE5hbWVXaWR0aCIsImZsb29yIiwiZGlzcGxheVRleHRXaWR0aCIsInRleHRDb2xvcl8wIiwic2hvdWxkRGltIiwiZGlzcGxheVRleHRfMCIsInBhZGRlZERpc3BsYXlUZXh0IiwicmVwZWF0IiwidGFnVGV4dCIsInRhZ1dpZHRoIiwiZGVzY3JpcHRpb25XaWR0aCIsInRydW5jYXRlZERlc2NyaXB0aW9uIiwidDQiLCJ0NSIsInQ2IiwidDciLCJQcm9wcyIsInN1Z2dlc3Rpb25zIiwic2VsZWN0ZWRTdWdnZXN0aW9uIiwib3ZlcmxheSIsIlByb21wdElucHV0Rm9vdGVyU3VnZ2VzdGlvbnMiLCJtYXhDb2x1bW5XaWR0aFByb3AiLCJyb3dzIiwibWF4VmlzaWJsZUl0ZW1zIiwibGVuZ3RoIiwibWFwIiwiX3RlbXAiLCJzdGFydEluZGV4IiwiZW5kSW5kZXgiLCJUMCIsInZpc2libGVJdGVtcyIsInNsaWNlIiwiaXRlbV8wIl0sInNvdXJjZXMiOlsiUHJvbXB0SW5wdXRGb290ZXJTdWdnZXN0aW9ucy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBtZW1vLCB0eXBlIFJlYWN0Tm9kZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi8uLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB0cnVuY2F0ZVBhdGhNaWRkbGUsIHRydW5jYXRlVG9XaWR0aCB9IGZyb20gJy4uLy4uL3V0aWxzL2Zvcm1hdC5qcydcbmltcG9ydCB0eXBlIHsgVGhlbWUgfSBmcm9tICcuLi8uLi91dGlscy90aGVtZS5qcydcblxuZXhwb3J0IHR5cGUgU3VnZ2VzdGlvbkl0ZW0gPSB7XG4gIGlkOiBzdHJpbmdcbiAgZGlzcGxheVRleHQ6IHN0cmluZ1xuICB0YWc/OiBzdHJpbmdcbiAgZGVzY3JpcHRpb24/OiBzdHJpbmdcbiAgbWV0YWRhdGE/OiB1bmtub3duXG4gIGNvbG9yPzoga2V5b2YgVGhlbWVcbn1cblxuZXhwb3J0IHR5cGUgU3VnZ2VzdGlvblR5cGUgPVxuICB8ICdjb21tYW5kJ1xuICB8ICdmaWxlJ1xuICB8ICdkaXJlY3RvcnknXG4gIHwgJ2FnZW50J1xuICB8ICdzaGVsbCdcbiAgfCAnY3VzdG9tLXRpdGxlJ1xuICB8ICdzbGFjay1jaGFubmVsJ1xuICB8ICdub25lJ1xuXG5leHBvcnQgY29uc3QgT1ZFUkxBWV9NQVhfSVRFTVMgPSA1XG5cbi8qKlxuICogR2V0IHRoZSBpY29uIGZvciBhIHN1Z2dlc3Rpb24gYmFzZWQgb24gaXRzIHR5cGVcbiAqIEljb25zOiArIGZvciBmaWxlcywg4peHIGZvciBNQ1AgcmVzb3VyY2VzLCAqIGZvciBhZ2VudHNcbiAqL1xuZnVuY3Rpb24gZ2V0SWNvbihpdGVtSWQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIGlmIChpdGVtSWQuc3RhcnRzV2l0aCgnZmlsZS0nKSkgcmV0dXJuICcrJ1xuICBpZiAoaXRlbUlkLnN0YXJ0c1dpdGgoJ21jcC1yZXNvdXJjZS0nKSkgcmV0dXJuICfil4cnXG4gIGlmIChpdGVtSWQuc3RhcnRzV2l0aCgnYWdlbnQtJykpIHJldHVybiAnKidcbiAgcmV0dXJuICcrJ1xufVxuXG4vKipcbiAqIENoZWNrIGlmIGFuIGl0ZW0gaXMgYSB1bmlmaWVkIHN1Z2dlc3Rpb24gdHlwZSAoZmlsZSwgbWNwLXJlc291cmNlLCBvciBhZ2VudClcbiAqL1xuZnVuY3Rpb24gaXNVbmlmaWVkU3VnZ2VzdGlvbihpdGVtSWQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICByZXR1cm4gKFxuICAgIGl0ZW1JZC5zdGFydHNXaXRoKCdmaWxlLScpIHx8XG4gICAgaXRlbUlkLnN0YXJ0c1dpdGgoJ21jcC1yZXNvdXJjZS0nKSB8fFxuICAgIGl0ZW1JZC5zdGFydHNXaXRoKCdhZ2VudC0nKVxuICApXG59XG5cbmNvbnN0IFN1Z2dlc3Rpb25JdGVtUm93ID0gbWVtbyhmdW5jdGlvbiBTdWdnZXN0aW9uSXRlbVJvdyh7XG4gIGl0ZW0sXG4gIG1heENvbHVtbldpZHRoLFxuICBpc1NlbGVjdGVkLFxufToge1xuICBpdGVtOiBTdWdnZXN0aW9uSXRlbVxuICBtYXhDb2x1bW5XaWR0aD86IG51bWJlclxuICBpc1NlbGVjdGVkOiBib29sZWFuXG59KTogUmVhY3ROb2RlIHtcbiAgY29uc3QgY29sdW1ucyA9IHVzZVRlcm1pbmFsU2l6ZSgpLmNvbHVtbnNcbiAgY29uc3QgaXNVbmlmaWVkID0gaXNVbmlmaWVkU3VnZ2VzdGlvbihpdGVtLmlkKVxuXG4gIC8vIEZvciB1bmlmaWVkIHN1Z2dlc3Rpb25zIChmaWxlLCBtY3AtcmVzb3VyY2UsIGFnZW50KSwgdXNlIHNpbmdsZS1saW5lIGxheW91dCB3aXRoIGljb25cbiAgaWYgKGlzVW5pZmllZCkge1xuICAgIGNvbnN0IGljb24gPSBnZXRJY29uKGl0ZW0uaWQpXG4gICAgY29uc3QgdGV4dENvbG9yOiBrZXlvZiBUaGVtZSB8IHVuZGVmaW5lZCA9IGlzU2VsZWN0ZWRcbiAgICAgID8gJ3N1Z2dlc3Rpb24nXG4gICAgICA6IHVuZGVmaW5lZFxuICAgIGNvbnN0IGRpbUNvbG9yID0gIWlzU2VsZWN0ZWRcblxuICAgIGNvbnN0IGlzRmlsZSA9IGl0ZW0uaWQuc3RhcnRzV2l0aCgnZmlsZS0nKVxuICAgIGNvbnN0IGlzTWNwUmVzb3VyY2UgPSBpdGVtLmlkLnN0YXJ0c1dpdGgoJ21jcC1yZXNvdXJjZS0nKVxuXG4gICAgLy8gQ2FsY3VsYXRlIGxheW91dCB3aWR0aHNcbiAgICAvLyBMYXlvdXQ6IFwiWCBcIiAoMikgKyBkaXNwbGF5VGV4dCArIFwiIOKAkyBcIiAoMykgKyBkZXNjcmlwdGlvbiArIHBhZGRpbmcgKDQpXG4gICAgY29uc3QgaWNvbldpZHRoID0gMiAvLyBpY29uICsgc3BhY2UgKGZpeGVkKVxuICAgIGNvbnN0IHBhZGRpbmdXaWR0aCA9IDRcbiAgICBjb25zdCBzZXBhcmF0b3JXaWR0aCA9IGl0ZW0uZGVzY3JpcHRpb24gPyAzIDogMCAvLyAnIOKAkyAnIHNlcGFyYXRvclxuXG4gICAgLy8gRm9yIGZpbGVzLCB0cnVuY2F0ZSBtaWRkbGUgb2YgcGF0aCB0byBzaG93IGJvdGggZGlyZWN0b3J5IGNvbnRleHQgYW5kIGZpbGVuYW1lXG4gICAgLy8gRm9yIE1DUCByZXNvdXJjZXMsIGxpbWl0IGRpc3BsYXlUZXh0IHRvIDMwIGNoYXJzICh0cnVuY2F0ZSBmcm9tIGVuZClcbiAgICAvLyBGb3IgYWdlbnRzLCBubyB0cnVuY2F0aW9uXG4gICAgbGV0IGRpc3BsYXlUZXh0OiBzdHJpbmdcbiAgICBpZiAoaXNGaWxlKSB7XG4gICAgICAvLyBSZXNlcnZlIHNwYWNlIGZvciBkZXNjcmlwdGlvbiBpZiBwcmVzZW50LCBvdGhlcndpc2UgdXNlIGFsbCBhdmFpbGFibGUgc3BhY2VcbiAgICAgIGNvbnN0IGRlc2NSZXNlcnZlID0gaXRlbS5kZXNjcmlwdGlvblxuICAgICAgICA/IE1hdGgubWluKDIwLCBzdHJpbmdXaWR0aChpdGVtLmRlc2NyaXB0aW9uKSlcbiAgICAgICAgOiAwXG4gICAgICBjb25zdCBtYXhQYXRoTGVuZ3RoID1cbiAgICAgICAgY29sdW1ucyAtIGljb25XaWR0aCAtIHBhZGRpbmdXaWR0aCAtIHNlcGFyYXRvcldpZHRoIC0gZGVzY1Jlc2VydmVcbiAgICAgIGRpc3BsYXlUZXh0ID0gdHJ1bmNhdGVQYXRoTWlkZGxlKGl0ZW0uZGlzcGxheVRleHQsIG1heFBhdGhMZW5ndGgpXG4gICAgfSBlbHNlIGlmIChpc01jcFJlc291cmNlKSB7XG4gICAgICBjb25zdCBtYXhEaXNwbGF5VGV4dExlbmd0aCA9IDMwXG4gICAgICBkaXNwbGF5VGV4dCA9IHRydW5jYXRlVG9XaWR0aChpdGVtLmRpc3BsYXlUZXh0LCBtYXhEaXNwbGF5VGV4dExlbmd0aClcbiAgICB9IGVsc2Uge1xuICAgICAgZGlzcGxheVRleHQgPSBpdGVtLmRpc3BsYXlUZXh0XG4gICAgfVxuXG4gICAgY29uc3QgYXZhaWxhYmxlV2lkdGggPVxuICAgICAgY29sdW1ucyAtXG4gICAgICBpY29uV2lkdGggLVxuICAgICAgc3RyaW5nV2lkdGgoZGlzcGxheVRleHQpIC1cbiAgICAgIHNlcGFyYXRvcldpZHRoIC1cbiAgICAgIHBhZGRpbmdXaWR0aFxuXG4gICAgLy8gQnVpbGQgdGhlIGZ1bGwgbGluZSBhcyBhIHNpbmdsZSBzdHJpbmcgdG8gcHJldmVudCB3cmFwcGluZ1xuICAgIGxldCBsaW5lQ29udGVudDogc3RyaW5nXG4gICAgaWYgKGl0ZW0uZGVzY3JpcHRpb24pIHtcbiAgICAgIGNvbnN0IG1heERlc2NMZW5ndGggPSBNYXRoLm1heCgwLCBhdmFpbGFibGVXaWR0aClcbiAgICAgIGNvbnN0IHRydW5jYXRlZERlc2MgPSB0cnVuY2F0ZVRvV2lkdGgoXG4gICAgICAgIGl0ZW0uZGVzY3JpcHRpb24ucmVwbGFjZSgvXFxzKy9nLCAnICcpLFxuICAgICAgICBtYXhEZXNjTGVuZ3RoLFxuICAgICAgKVxuICAgICAgbGluZUNvbnRlbnQgPSBgJHtpY29ufSAke2Rpc3BsYXlUZXh0fSDigJMgJHt0cnVuY2F0ZWREZXNjfWBcbiAgICB9IGVsc2Uge1xuICAgICAgbGluZUNvbnRlbnQgPSBgJHtpY29ufSAke2Rpc3BsYXlUZXh0fWBcbiAgICB9XG5cbiAgICByZXR1cm4gKFxuICAgICAgPFRleHQgY29sb3I9e3RleHRDb2xvcn0gZGltQ29sb3I9e2RpbUNvbG9yfSB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAge2xpbmVDb250ZW50fVxuICAgICAgPC9UZXh0PlxuICAgIClcbiAgfVxuXG4gIC8vIEZvciBub24tdW5pZmllZCBzdWdnZXN0aW9ucyAoY29tbWFuZHMsIHNoZWxsLCBldGMuKSwgdXNlIGltcHJvdmVkIGxheW91dCBmcm9tIG1haW5cbiAgLy8gQ2FwIHRoZSBjb21tYW5kIG5hbWUgY29sdW1uIGF0IDQwJSBvZiB0ZXJtaW5hbCB3aWR0aCB0byBlbnN1cmUgZGVzY3JpcHRpb24gaGFzIHNwYWNlXG4gIGNvbnN0IG1heE5hbWVXaWR0aCA9IE1hdGguZmxvb3IoY29sdW1ucyAqIDAuNClcbiAgY29uc3QgZGlzcGxheVRleHRXaWR0aCA9IE1hdGgubWluKFxuICAgIG1heENvbHVtbldpZHRoID8/IHN0cmluZ1dpZHRoKGl0ZW0uZGlzcGxheVRleHQpICsgNSxcbiAgICBtYXhOYW1lV2lkdGgsXG4gIClcblxuICBjb25zdCB0ZXh0Q29sb3IgPSBpdGVtLmNvbG9yIHx8IChpc1NlbGVjdGVkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkKVxuICBjb25zdCBzaG91bGREaW0gPSAhaXNTZWxlY3RlZFxuXG4gIC8vIFRydW5jYXRlIGFuZCBwYWQgdGhlIGRpc3BsYXkgdGV4dCB0byBmaXhlZCB3aWR0aFxuICBsZXQgZGlzcGxheVRleHQgPSBpdGVtLmRpc3BsYXlUZXh0XG4gIGlmIChzdHJpbmdXaWR0aChkaXNwbGF5VGV4dCkgPiBkaXNwbGF5VGV4dFdpZHRoIC0gMikge1xuICAgIGRpc3BsYXlUZXh0ID0gdHJ1bmNhdGVUb1dpZHRoKGRpc3BsYXlUZXh0LCBkaXNwbGF5VGV4dFdpZHRoIC0gMilcbiAgfVxuICBjb25zdCBwYWRkZWREaXNwbGF5VGV4dCA9XG4gICAgZGlzcGxheVRleHQgK1xuICAgICcgJy5yZXBlYXQoTWF0aC5tYXgoMCwgZGlzcGxheVRleHRXaWR0aCAtIHN0cmluZ1dpZHRoKGRpc3BsYXlUZXh0KSkpXG5cbiAgY29uc3QgdGFnVGV4dCA9IGl0ZW0udGFnID8gYFske2l0ZW0udGFnfV0gYCA6ICcnXG4gIGNvbnN0IHRhZ1dpZHRoID0gc3RyaW5nV2lkdGgodGFnVGV4dClcbiAgY29uc3QgZGVzY3JpcHRpb25XaWR0aCA9IE1hdGgubWF4KFxuICAgIDAsXG4gICAgY29sdW1ucyAtIGRpc3BsYXlUZXh0V2lkdGggLSB0YWdXaWR0aCAtIDQsXG4gIClcbiAgLy8gU2tpbGwgZGVzY3JpcHRpb25zIGNhbiBjb250YWluIG5ld2xpbmVzIChlLmcuIC9jbGF1ZGUtYXBpJ3MgXCJUUklHR0VSXG4gIC8vIHdoZW46XCIgYmxvY2spLiBBIG11bHRpLWxpbmUgcm93IGdyb3dzIHRoZSBvdmVybGF5IHBhc3QgbWluSGVpZ2h0OyB3aGVuXG4gIC8vIHRoZSBmaWx0ZXIgbmFycm93cyBwYXN0IHRoYXQgc2tpbGwsIHRoZSBvdmVybGF5IHNocmlua3MgYW5kIGxlYXZlc1xuICAvLyBnaG9zdCByb3dzLiBGbGF0dGVuIHRvIG9uZSBsaW5lIGJlZm9yZSB0cnVuY2F0aW5nLlxuICBjb25zdCB0cnVuY2F0ZWREZXNjcmlwdGlvbiA9IGl0ZW0uZGVzY3JpcHRpb25cbiAgICA/IHRydW5jYXRlVG9XaWR0aChpdGVtLmRlc2NyaXB0aW9uLnJlcGxhY2UoL1xccysvZywgJyAnKSwgZGVzY3JpcHRpb25XaWR0aClcbiAgICA6ICcnXG5cbiAgcmV0dXJuIChcbiAgICA8VGV4dCB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgIDxUZXh0IGNvbG9yPXt0ZXh0Q29sb3J9IGRpbUNvbG9yPXtzaG91bGREaW19PlxuICAgICAgICB7cGFkZGVkRGlzcGxheVRleHR9XG4gICAgICA8L1RleHQ+XG4gICAgICB7dGFnVGV4dCA/IDxUZXh0IGRpbUNvbG9yPnt0YWdUZXh0fTwvVGV4dD4gOiBudWxsfVxuICAgICAgPFRleHRcbiAgICAgICAgY29sb3I9e2lzU2VsZWN0ZWQgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9XG4gICAgICAgIGRpbUNvbG9yPXshaXNTZWxlY3RlZH1cbiAgICAgID5cbiAgICAgICAge3RydW5jYXRlZERlc2NyaXB0aW9ufVxuICAgICAgPC9UZXh0PlxuICAgIDwvVGV4dD5cbiAgKVxufSlcblxudHlwZSBQcm9wcyA9IHtcbiAgc3VnZ2VzdGlvbnM6IFN1Z2dlc3Rpb25JdGVtW11cbiAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBudW1iZXJcbiAgbWF4Q29sdW1uV2lkdGg/OiBudW1iZXJcbiAgLyoqXG4gICAqIFdoZW4gdHJ1ZSwgdGhlIHN1Z2dlc3Rpb25zIGFyZSByZW5kZXJlZCBpbnNpZGUgYSBwb3NpdGlvbj1hYnNvbHV0ZVxuICAgKiBvdmVybGF5LiBXZSBvbWl0IG1pbkhlaWdodCBhbmQgZmxleC1lbmQgc28gdGhlIHktY2xhbXAgaW4gdGhlXG4gICAqIHJlbmRlcmVyIGRvZXNuJ3QgcHVzaCBmZXdlciBpdGVtcyBkb3duIGludG8gdGhlIHByb21wdCBhcmVhLlxuICAgKi9cbiAgb3ZlcmxheT86IGJvb2xlYW5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFByb21wdElucHV0Rm9vdGVyU3VnZ2VzdGlvbnMoe1xuICBzdWdnZXN0aW9ucyxcbiAgc2VsZWN0ZWRTdWdnZXN0aW9uLFxuICBtYXhDb2x1bW5XaWR0aDogbWF4Q29sdW1uV2lkdGhQcm9wLFxuICBvdmVybGF5LFxufTogUHJvcHMpOiBSZWFjdE5vZGUge1xuICBjb25zdCB7IHJvd3MgfSA9IHVzZVRlcm1pbmFsU2l6ZSgpXG4gIC8vIE1heGltdW0gbnVtYmVyIG9mIHN1Z2dlc3Rpb25zIHRvIHNob3cgYXQgb25jZSAobGVhdmluZyBzcGFjZSBmb3IgcHJvbXB0KS5cbiAgLy8gT3ZlcmxheSBtb2RlIChmdWxsc2NyZWVuKSB1c2VzIGEgZml4ZWQgNSDigJQgdGhlIGZsb2F0aW5nIGJveCBzaXRzIG92ZXJcbiAgLy8gdGhlIFNjcm9sbEJveCwgc28gdGVybWluYWwgaGVpZ2h0IGlzbid0IHRoZSBjb25zdHJhaW50LlxuICBjb25zdCBtYXhWaXNpYmxlSXRlbXMgPSBvdmVybGF5XG4gICAgPyBPVkVSTEFZX01BWF9JVEVNU1xuICAgIDogTWF0aC5taW4oNiwgTWF0aC5tYXgoMSwgcm93cyAtIDMpKVxuXG4gIC8vIE5vIHN1Z2dlc3Rpb25zIHRvIGRpc3BsYXlcbiAgaWYgKHN1Z2dlc3Rpb25zLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBVc2UgcHJvcCBpZiBwcm92aWRlZCAoc3RhYmxlIHdpZHRoIGZyb20gYWxsIGNvbW1hbmRzKSwgb3RoZXJ3aXNlIGNhbGN1bGF0ZSBmcm9tIHZpc2libGVcbiAgY29uc3QgbWF4Q29sdW1uV2lkdGggPVxuICAgIG1heENvbHVtbldpZHRoUHJvcCA/P1xuICAgIE1hdGgubWF4KC4uLnN1Z2dlc3Rpb25zLm1hcChpdGVtID0+IHN0cmluZ1dpZHRoKGl0ZW0uZGlzcGxheVRleHQpKSkgKyA1XG5cbiAgLy8gQ2FsY3VsYXRlIHZpc2libGUgaXRlbXMgcmFuZ2UgYmFzZWQgb24gc2VsZWN0ZWQgaW5kZXhcbiAgY29uc3Qgc3RhcnRJbmRleCA9IE1hdGgubWF4KFxuICAgIDAsXG4gICAgTWF0aC5taW4oXG4gICAgICBzZWxlY3RlZFN1Z2dlc3Rpb24gLSBNYXRoLmZsb29yKG1heFZpc2libGVJdGVtcyAvIDIpLFxuICAgICAgc3VnZ2VzdGlvbnMubGVuZ3RoIC0gbWF4VmlzaWJsZUl0ZW1zLFxuICAgICksXG4gIClcbiAgY29uc3QgZW5kSW5kZXggPSBNYXRoLm1pbihzdGFydEluZGV4ICsgbWF4VmlzaWJsZUl0ZW1zLCBzdWdnZXN0aW9ucy5sZW5ndGgpXG4gIGNvbnN0IHZpc2libGVJdGVtcyA9IHN1Z2dlc3Rpb25zLnNsaWNlKHN0YXJ0SW5kZXgsIGVuZEluZGV4KVxuXG4gIC8vIEluIG5vbi1vdmVybGF5IChpbmxpbmUpIG1vZGUsIGp1c3RpZnlDb250ZW50IGtlZXBzIHN1Z2dlc3Rpb25zXG4gIC8vIGFuY2hvcmVkIHRvIHRoZSBib3R0b20gKG5lYXIgdGhlIHByb21wdCkuIEluIG92ZXJsYXkgbW9kZSB3ZSBvbWl0XG4gIC8vIGJvdGggbWluSGVpZ2h0IGFuZCBmbGV4LWVuZDogdGhlIHBhcmVudCBpcyBwb3NpdGlvbj1hYnNvbHV0ZSB3aXRoXG4gIC8vIGJvdHRvbT0nMTAwJScsIHNvIGl0cyB5IGlzIGNsYW1wZWQgdG8gMCBieSB0aGUgcmVuZGVyZXIgd2hlbiBpdFxuICAvLyB3b3VsZCBnbyBuZWdhdGl2ZS4gQWRkaW5nIG1pbkhlaWdodCArIGZsZXgtZW5kIHdvdWxkIGNyZWF0ZSBlbXB0eVxuICAvLyBwYWRkaW5nIHJvd3MgdGhhdCBzaGlmdCB0aGUgdmlzaWJsZSBpdGVtcyBkb3duIGludG8gdGhlIHByb21wdCBhcmVhXG4gIC8vIHdoZW4gdGhlIGxpc3QgaGFzIGZld2VyIGl0ZW1zIHRoYW4gbWF4VmlzaWJsZUl0ZW1zLlxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAganVzdGlmeUNvbnRlbnQ9e292ZXJsYXkgPyB1bmRlZmluZWQgOiAnZmxleC1lbmQnfVxuICAgID5cbiAgICAgIHt2aXNpYmxlSXRlbXMubWFwKGl0ZW0gPT4gKFxuICAgICAgICA8U3VnZ2VzdGlvbkl0ZW1Sb3dcbiAgICAgICAgICBrZXk9e2l0ZW0uaWR9XG4gICAgICAgICAgaXRlbT17aXRlbX1cbiAgICAgICAgICBtYXhDb2x1bW5XaWR0aD17bWF4Q29sdW1uV2lkdGh9XG4gICAgICAgICAgaXNTZWxlY3RlZD17aXRlbS5pZCA9PT0gc3VnZ2VzdGlvbnNbc2VsZWN0ZWRTdWdnZXN0aW9uXT8uaWR9XG4gICAgICAgIC8+XG4gICAgICApKX1cbiAgICA8L0JveD5cbiAgKVxufVxuXG5leHBvcnQgZGVmYXVsdCBtZW1vKFByb21wdElucHV0Rm9vdGVyU3VnZ2VzdGlvbnMpXG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLElBQUksRUFBRSxLQUFLQyxTQUFTLFFBQVEsT0FBTztBQUM1QyxTQUFTQyxlQUFlLFFBQVEsZ0NBQWdDO0FBQ2hFLFNBQVNDLFdBQVcsUUFBUSwwQkFBMEI7QUFDdEQsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxrQkFBa0IsRUFBRUMsZUFBZSxRQUFRLHVCQUF1QjtBQUMzRSxjQUFjQyxLQUFLLFFBQVEsc0JBQXNCO0FBRWpELE9BQU8sS0FBS0MsY0FBYyxHQUFHO0VBQzNCQyxFQUFFLEVBQUUsTUFBTTtFQUNWQyxXQUFXLEVBQUUsTUFBTTtFQUNuQkMsR0FBRyxDQUFDLEVBQUUsTUFBTTtFQUNaQyxXQUFXLENBQUMsRUFBRSxNQUFNO0VBQ3BCQyxRQUFRLENBQUMsRUFBRSxPQUFPO0VBQ2xCQyxLQUFLLENBQUMsRUFBRSxNQUFNUCxLQUFLO0FBQ3JCLENBQUM7QUFFRCxPQUFPLEtBQUtRLGNBQWMsR0FDdEIsU0FBUyxHQUNULE1BQU0sR0FDTixXQUFXLEdBQ1gsT0FBTyxHQUNQLE9BQU8sR0FDUCxjQUFjLEdBQ2QsZUFBZSxHQUNmLE1BQU07QUFFVixPQUFPLE1BQU1DLGlCQUFpQixHQUFHLENBQUM7O0FBRWxDO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBU0MsT0FBT0EsQ0FBQ0MsTUFB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 \ No newline at end of file diff --git a/components/PromptInput/PromptInputHelpMenu.tsx b/components/PromptInput/PromptInputHelpMenu.tsx new file mode 100644 index 0000000..53fdcc9 --- /dev/null +++ b/components/PromptInput/PromptInputHelpMenu.tsx @@ -0,0 +1,358 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import { Box, Text } from 'src/ink.js'; +import { getPlatform } from 'src/utils/platform.js'; +import { isKeybindingCustomizationEnabled } from '../../keybindings/loadUserBindings.js'; +import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'; +import { isFastModeAvailable, isFastModeEnabled } from '../../utils/fastMode.js'; +import { getNewlineInstructions } from './utils.js'; + +/** Format a shortcut for display in the help menu (e.g., "ctrl+o" → "ctrl + o") */ +function formatShortcut(shortcut: string): string { + return shortcut.replace(/\+/g, ' + '); +} +type Props = { + dimColor?: boolean; + fixedWidth?: boolean; + gap?: number; + paddingX?: number; +}; +export function PromptInputHelpMenu(props) { + const $ = _c(99); + const { + dimColor, + fixedWidth, + gap, + paddingX + } = props; + const t0 = useShortcutDisplay("app:toggleTranscript", "Global", "ctrl+o"); + let t1; + if ($[0] !== t0) { + t1 = formatShortcut(t0); + $[0] = t0; + $[1] = t1; + } else { + t1 = $[1]; + } + const transcriptShortcut = t1; + const t2 = useShortcutDisplay("app:toggleTodos", "Global", "ctrl+t"); + let t3; + if ($[2] !== t2) { + t3 = formatShortcut(t2); + $[2] = t2; + $[3] = t3; + } else { + t3 = $[3]; + } + const todosShortcut = t3; + const t4 = useShortcutDisplay("chat:undo", "Chat", "ctrl+_"); + let t5; + if ($[4] !== t4) { + t5 = formatShortcut(t4); + $[4] = t4; + $[5] = t5; + } else { + t5 = $[5]; + } + const undoShortcut = t5; + const t6 = useShortcutDisplay("chat:stash", "Chat", "ctrl+s"); + let t7; + if ($[6] !== t6) { + t7 = formatShortcut(t6); + $[6] = t6; + $[7] = t7; + } else { + t7 = $[7]; + } + const stashShortcut = t7; + const t8 = useShortcutDisplay("chat:cycleMode", "Chat", "shift+tab"); + let t9; + if ($[8] !== t8) { + t9 = formatShortcut(t8); + $[8] = t8; + $[9] = t9; + } else { + t9 = $[9]; + } + const cycleModeShortcut = t9; + const t10 = useShortcutDisplay("chat:modelPicker", "Chat", "alt+p"); + let t11; + if ($[10] !== t10) { + t11 = formatShortcut(t10); + $[10] = t10; + $[11] = t11; + } else { + t11 = $[11]; + } + const modelPickerShortcut = t11; + const t12 = useShortcutDisplay("chat:fastMode", "Chat", "alt+o"); + let t13; + if ($[12] !== t12) { + t13 = formatShortcut(t12); + $[12] = t12; + $[13] = t13; + } else { + t13 = $[13]; + } + const fastModeShortcut = t13; + const t14 = useShortcutDisplay("chat:externalEditor", "Chat", "ctrl+g"); + let t15; + if ($[14] !== t14) { + t15 = formatShortcut(t14); + $[14] = t14; + $[15] = t15; + } else { + t15 = $[15]; + } + const externalEditorShortcut = t15; + const t16 = useShortcutDisplay("app:toggleTerminal", "Global", "meta+j"); + let t17; + if ($[16] !== t16) { + t17 = formatShortcut(t16); + $[16] = t16; + $[17] = t17; + } else { + t17 = $[17]; + } + const terminalShortcut = t17; + const t18 = useShortcutDisplay("chat:imagePaste", "Chat", "ctrl+v"); + let t19; + if ($[18] !== t18) { + t19 = formatShortcut(t18); + $[18] = t18; + $[19] = t19; + } else { + t19 = $[19]; + } + const imagePasteShortcut = t19; + let t20; + if ($[20] !== dimColor || $[21] !== terminalShortcut) { + t20 = feature("TERMINAL_PANEL") ? getFeatureValue_CACHED_MAY_BE_STALE("tengu_terminal_panel", false) ? {terminalShortcut} for terminal : null : null; + $[20] = dimColor; + $[21] = terminalShortcut; + $[22] = t20; + } else { + t20 = $[22]; + } + const terminalShortcutElement = t20; + const t21 = fixedWidth ? 24 : undefined; + let t22; + if ($[23] !== dimColor) { + t22 = ! for bash mode; + $[23] = dimColor; + $[24] = t22; + } else { + t22 = $[24]; + } + let t23; + if ($[25] !== dimColor) { + t23 = / for commands; + $[25] = dimColor; + $[26] = t23; + } else { + t23 = $[26]; + } + let t24; + if ($[27] !== dimColor) { + t24 = @ for file paths; + $[27] = dimColor; + $[28] = t24; + } else { + t24 = $[28]; + } + let t25; + if ($[29] !== dimColor) { + t25 = {"& for background"}; + $[29] = dimColor; + $[30] = t25; + } else { + t25 = $[30]; + } + let t26; + if ($[31] !== dimColor) { + t26 = /btw for side question; + $[31] = dimColor; + $[32] = t26; + } else { + t26 = $[32]; + } + let t27; + if ($[33] !== t21 || $[34] !== t22 || $[35] !== t23 || $[36] !== t24 || $[37] !== t25 || $[38] !== t26) { + t27 = {t22}{t23}{t24}{t25}{t26}; + $[33] = t21; + $[34] = t22; + $[35] = t23; + $[36] = t24; + $[37] = t25; + $[38] = t26; + $[39] = t27; + } else { + t27 = $[39]; + } + const t28 = fixedWidth ? 35 : undefined; + let t29; + if ($[40] !== dimColor) { + t29 = double tap esc to clear input; + $[40] = dimColor; + $[41] = t29; + } else { + t29 = $[41]; + } + let t30; + if ($[42] !== cycleModeShortcut || $[43] !== dimColor) { + t30 = {cycleModeShortcut}{" "}{false ? "to cycle modes" : "to auto-accept edits"}; + $[42] = cycleModeShortcut; + $[43] = dimColor; + $[44] = t30; + } else { + t30 = $[44]; + } + let t31; + if ($[45] !== dimColor || $[46] !== transcriptShortcut) { + t31 = {transcriptShortcut} for verbose output; + $[45] = dimColor; + $[46] = transcriptShortcut; + $[47] = t31; + } else { + t31 = $[47]; + } + let t32; + if ($[48] !== dimColor || $[49] !== todosShortcut) { + t32 = {todosShortcut} to toggle tasks; + $[48] = dimColor; + $[49] = todosShortcut; + $[50] = t32; + } else { + t32 = $[50]; + } + let t33; + if ($[51] === Symbol.for("react.memo_cache_sentinel")) { + t33 = getNewlineInstructions(); + $[51] = t33; + } else { + t33 = $[51]; + } + let t34; + if ($[52] !== dimColor) { + t34 = {t33}; + $[52] = dimColor; + $[53] = t34; + } else { + t34 = $[53]; + } + let t35; + if ($[54] !== t28 || $[55] !== t29 || $[56] !== t30 || $[57] !== t31 || $[58] !== t32 || $[59] !== t34 || $[60] !== terminalShortcutElement) { + t35 = {t29}{t30}{t31}{t32}{terminalShortcutElement}{t34}; + $[54] = t28; + $[55] = t29; + $[56] = t30; + $[57] = t31; + $[58] = t32; + $[59] = t34; + $[60] = terminalShortcutElement; + $[61] = t35; + } else { + t35 = $[61]; + } + let t36; + if ($[62] !== dimColor || $[63] !== undoShortcut) { + t36 = {undoShortcut} to undo; + $[62] = dimColor; + $[63] = undoShortcut; + $[64] = t36; + } else { + t36 = $[64]; + } + let t37; + if ($[65] !== dimColor) { + t37 = getPlatform() !== "windows" && ctrl + z to suspend; + $[65] = dimColor; + $[66] = t37; + } else { + t37 = $[66]; + } + let t38; + if ($[67] !== dimColor || $[68] !== imagePasteShortcut) { + t38 = {imagePasteShortcut} to paste images; + $[67] = dimColor; + $[68] = imagePasteShortcut; + $[69] = t38; + } else { + t38 = $[69]; + } + let t39; + if ($[70] !== dimColor || $[71] !== modelPickerShortcut) { + t39 = {modelPickerShortcut} to switch model; + $[70] = dimColor; + $[71] = modelPickerShortcut; + $[72] = t39; + } else { + t39 = $[72]; + } + let t40; + if ($[73] !== dimColor || $[74] !== fastModeShortcut) { + t40 = isFastModeEnabled() && isFastModeAvailable() && {fastModeShortcut} to toggle fast mode; + $[73] = dimColor; + $[74] = fastModeShortcut; + $[75] = t40; + } else { + t40 = $[75]; + } + let t41; + if ($[76] !== dimColor || $[77] !== stashShortcut) { + t41 = {stashShortcut} to stash prompt; + $[76] = dimColor; + $[77] = stashShortcut; + $[78] = t41; + } else { + t41 = $[78]; + } + let t42; + if ($[79] !== dimColor || $[80] !== externalEditorShortcut) { + t42 = {externalEditorShortcut} to edit in $EDITOR; + $[79] = dimColor; + $[80] = externalEditorShortcut; + $[81] = t42; + } else { + t42 = $[81]; + } + let t43; + if ($[82] !== dimColor) { + t43 = isKeybindingCustomizationEnabled() && /keybindings to customize; + $[82] = dimColor; + $[83] = t43; + } else { + t43 = $[83]; + } + let t44; + if ($[84] !== t36 || $[85] !== t37 || $[86] !== t38 || $[87] !== t39 || $[88] !== t40 || $[89] !== t41 || $[90] !== t42 || $[91] !== t43) { + t44 = {t36}{t37}{t38}{t39}{t40}{t41}{t42}{t43}; + $[84] = t36; + $[85] = t37; + $[86] = t38; + $[87] = t39; + $[88] = t40; + $[89] = t41; + $[90] = t42; + $[91] = t43; + $[92] = t44; + } else { + t44 = $[92]; + } + let t45; + if ($[93] !== gap || $[94] !== paddingX || $[95] !== t27 || $[96] !== t35 || $[97] !== t44) { + t45 = {t27}{t35}{t44}; + $[93] = gap; + $[94] = paddingX; + $[95] = t27; + $[96] = t35; + $[97] = t44; + $[98] = t45; + } else { + t45 = $[98]; + } + return t45; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwiZ2V0UGxhdGZvcm0iLCJpc0tleWJpbmRpbmdDdXN0b21pemF0aW9uRW5hYmxlZCIsInVzZVNob3J0Y3V0RGlzcGxheSIsImdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFIiwiaXNGYXN0TW9kZUF2YWlsYWJsZSIsImlzRmFzdE1vZGVFbmFibGVkIiwiZ2V0TmV3bGluZUluc3RydWN0aW9ucyIsImZvcm1hdFNob3J0Y3V0Iiwic2hvcnRjdXQiLCJyZXBsYWNlIiwiUHJvcHMiLCJkaW1Db2xvciIsImZpeGVkV2lkdGgiLCJnYXAiLCJwYWRkaW5nWCIsIlByb21wdElucHV0SGVscE1lbnUiLCJwcm9wcyIsIiQiLCJfYyIsInQwIiwidDEiLCJ0cmFuc2NyaXB0U2hvcnRjdXQiLCJ0MiIsInQzIiwidG9kb3NTaG9ydGN1dCIsInQ0IiwidDUiLCJ1bmRvU2hvcnRjdXQiLCJ0NiIsInQ3Iiwic3Rhc2hTaG9ydGN1dCIsInQ4IiwidDkiLCJjeWNsZU1vZGVTaG9ydGN1dCIsInQxMCIsInQxMSIsIm1vZGVsUGlja2VyU2hvcnRjdXQiLCJ0MTIiLCJ0MTMiLCJmYXN0TW9kZVNob3J0Y3V0IiwidDE0IiwidDE1IiwiZXh0ZXJuYWxFZGl0b3JTaG9ydGN1dCIsInQxNiIsInQxNyIsInRlcm1pbmFsU2hvcnRjdXQiLCJ0MTgiLCJ0MTkiLCJpbWFnZVBhc3RlU2hvcnRjdXQiLCJ0MjAiLCJ0ZXJtaW5hbFNob3J0Y3V0RWxlbWVudCIsInQyMSIsInVuZGVmaW5lZCIsInQyMiIsInQyMyIsInQyNCIsInQyNSIsInQyNiIsInQyNyIsInQyOCIsInQyOSIsInQzMCIsInQzMSIsInQzMiIsInQzMyIsIlN5bWJvbCIsImZvciIsInQzNCIsInQzNSIsInQzNiIsInQzNyIsInQzOCIsInQzOSIsInQ0MCIsInQ0MSIsInQ0MiIsInQ0MyIsInQ0NCIsInQ0NSJdLCJzb3VyY2VzIjpbIlByb21wdElucHV0SGVscE1lbnUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICdzcmMvaW5rLmpzJ1xuaW1wb3J0IHsgZ2V0UGxhdGZvcm0gfSBmcm9tICdzcmMvdXRpbHMvcGxhdGZvcm0uanMnXG5pbXBvcnQgeyBpc0tleWJpbmRpbmdDdXN0b21pemF0aW9uRW5hYmxlZCB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL2xvYWRVc2VyQmluZGluZ3MuanMnXG5pbXBvcnQgeyB1c2VTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VTaG9ydGN1dERpc3BsYXkuanMnXG5pbXBvcnQgeyBnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2FuYWx5dGljcy9ncm93dGhib29rLmpzJ1xuaW1wb3J0IHsgaXNGYXN0TW9kZUF2YWlsYWJsZSwgaXNGYXN0TW9kZUVuYWJsZWQgfSBmcm9tICcuLi8uLi91dGlscy9mYXN0TW9kZS5qcydcbmltcG9ydCB7IGdldE5ld2xpbmVJbnN0cnVjdGlvbnMgfSBmcm9tICcuL3V0aWxzLmpzJ1xuXG4vKiogRm9ybWF0IGEgc2hvcnRjdXQgZm9yIGRpc3BsYXkgaW4gdGhlIGhlbHAgbWVudSAoZS5nLiwgXCJjdHJsK29cIiDihpIgXCJjdHJsICsgb1wiKSAqL1xuZnVuY3Rpb24gZm9ybWF0U2hvcnRjdXQoc2hvcnRjdXQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBzaG9ydGN1dC5yZXBsYWNlKC9cXCsvZywgJyArICcpXG59XG5cbnR5cGUgUHJvcHMgPSB7XG4gIGRpbUNvbG9yPzogYm9vbGVhblxuICBmaXhlZFdpZHRoPzogYm9vbGVhblxuICBnYXA/OiBudW1iZXJcbiAgcGFkZGluZ1g/OiBudW1iZXJcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFByb21wdElucHV0SGVscE1lbnUocHJvcHM6IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBkaW1Db2xvciwgZml4ZWRXaWR0aCwgZ2FwLCBwYWRkaW5nWCB9ID0gcHJvcHNcblxuICAvLyBHZXQgY29uZmlndXJlZCBzaG9ydGN1dHMgZnJvbSBrZXliaW5kaW5nIHN5c3RlbVxuICBjb25zdCB0cmFuc2NyaXB0U2hvcnRjdXQgPSBmb3JtYXRTaG9ydGN1dChcbiAgICB1c2VTaG9ydGN1dERpc3BsYXkoJ2FwcDp0b2dnbGVUcmFuc2NyaXB0JywgJ0dsb2JhbCcsICdjdHJsK28nKSxcbiAgKVxuICBjb25zdCB0b2Rvc1Nob3J0Y3V0ID0gZm9ybWF0U2hvcnRjdXQoXG4gICAgdXNlU2hvcnRjdXREaXNwbGF5KCdhcHA6dG9nZ2xlVG9kb3MnLCAnR2xvYmFsJywgJ2N0cmwrdCcpLFxuICApXG4gIGNvbnN0IHVuZG9TaG9ydGN1dCA9IGZvcm1hdFNob3J0Y3V0KFxuICAgIHVzZVNob3J0Y3V0RGlzcGxheSgnY2hhdDp1bmRvJywgJ0NoYXQnLCAnY3RybCtfJyksXG4gIClcbiAgY29uc3Qgc3Rhc2hTaG9ydGN1dCA9IGZvcm1hdFNob3J0Y3V0KFxuICAgIHVzZVNob3J0Y3V0RGlzcGxheSgnY2hhdDpzdGFzaCcsICdDaGF0JywgJ2N0cmwrcycpLFxuICApXG4gIGNvbnN0IGN5Y2xlTW9kZVNob3J0Y3V0ID0gZm9ybWF0U2hvcnRjdXQoXG4gICAgdXNlU2hvcnRjdXREaXNwbGF5KCdjaGF0OmN5Y2xlTW9kZScsICdDaGF0JywgJ3NoaWZ0K3RhYicpLFxuICApXG4gIGNvbnN0IG1vZGVsUGlja2VyU2hvcnRjdXQgPSBmb3JtYXRTaG9ydGN1dChcbiAgICB1c2VTaG9ydGN1dERpc3BsYXkoJ2NoYXQ6bW9kZWxQaWNrZXInLCAnQ2hhdCcsICdhbHQrcCcpLFxuICApXG4gIGNvbnN0IGZhc3RNb2RlU2hvcnRjdXQgPSBmb3JtYXRTaG9ydGN1dChcbiAgICB1c2VTaG9ydGN1dERpc3BsYXkoJ2NoYXQ6ZmFzdE1vZGUnLCAnQ2hhdCcsICdhbHQrbycpLFxuICApXG4gIGNvbnN0IGV4dGVybmFsRWRpdG9yU2hvcnRjdXQgPSBmb3JtYXRTaG9ydGN1dChcbiAgICB1c2VTaG9ydGN1dERpc3BsYXkoJ2NoYXQ6ZXh0ZXJuYWxFZGl0b3InLCAnQ2hhdCcsICdjdHJsK2cnKSxcbiAgKVxuICBjb25zdCB0ZXJtaW5hbFNob3J0Y3V0ID0gZm9ybWF0U2hvcnRjdXQoXG4gICAgdXNlU2hvcnRjdXREaXNwbGF5KCdhcHA6dG9nZ2xlVGVybWluYWwnLCAnR2xvYmFsJywgJ21ldGEraicpLFxuICApXG4gIGNvbnN0IGltYWdlUGFzdGVTaG9ydGN1dCA9IGZvcm1hdFNob3J0Y3V0KFxuICAgIHVzZVNob3J0Y3V0RGlzcGxheSgnY2hhdDppbWFnZVBhc3RlJywgJ0NoYXQnLCAnY3RybCt2JyksXG4gIClcblxuICAvLyBDb21wdXRlIHRlcm1pbmFsIHNob3J0Y3V0IGVsZW1lbnQgb3V0c2lkZSBKU1ggdG8gc2F0aXNmeSBmZWF0dXJlKCkgY29uc3RyYWludFxuICBjb25zdCB0ZXJtaW5hbFNob3J0Y3V0RWxlbWVudCA9IGZlYXR1cmUoJ1RFUk1JTkFMX1BBTkVMJykgPyAoXG4gICAgZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUoJ3Rlbmd1X3Rlcm1pbmFsX3BhbmVsJywgZmFsc2UpID8gKFxuICAgICAgPEJveD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I9e2RpbUNvbG9yfT57dGVybWluYWxTaG9ydGN1dH0gZm9yIHRlcm1pbmFsPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKSA6IG51bGxcbiAgKSA6IG51bGxcblxuICByZXR1cm4gKFxuICAgIDxCb3ggcGFkZGluZ1g9e3BhZGRpbmdYfSBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXtnYXB9PlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9e2ZpeGVkV2lkdGggPyAyNCA6IHVuZGVmaW5lZH0+XG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I9e2RpbUNvbG9yfT4hIGZvciBiYXNoIG1vZGU8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPXtkaW1Db2xvcn0+LyBmb3IgY29tbWFuZHM8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPXtkaW1Db2xvcn0+QCBmb3IgZmlsZSBwYXRoczwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I9e2RpbUNvbG9yfT4mIGZvciBiYWNrZ3JvdW5kPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj17ZGltQ29sb3J9Pi9idHcgZm9yIHNpZGUgcXVlc3Rpb248L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiB3aWR0aD17Zml4ZWRXaWR0aCA/IDM1IDogdW5kZWZpbmVkfT5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj17ZGltQ29sb3J9PmRvdWJsZSB0YXAgZXNjIHRvIGNsZWFyIGlucHV0PC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj17ZGltQ29sb3J9PlxuICAgICAgICAgICAge2N5Y2xlTW9kZVNob3J0Y3V0fXsnICd9XG4gICAgICAgICAgICB7XCJleHRlcm5hbFwiID09PSAnYW50J1xuICAgICAgICAgICAgICA/ICd0byBjeWNsZSBtb2RlcydcbiAgICAgICAgICAgICAgOiAndG8gYXV0by1hY2NlcHQgZWRpdHMnfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I9e2RpbUNvbG9yfT5cbiAgICAgICAgICAgIHt0cmFuc2NyaXB0U2hvcnRjdXR9IGZvciB2ZXJib3NlIG91dHB1dFxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I9e2RpbUNvbG9yfT57dG9kb3NTaG9ydGN1dH0gdG8gdG9nZ2xlIHRhc2tzPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAge3Rlcm1pbmFsU2hvcnRjdXRFbGVtZW50fVxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPXtkaW1Db2xvcn0+e2dldE5ld2xpbmVJbnN0cnVjdGlvbnMoKX08L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj17ZGltQ29sb3J9Pnt1bmRvU2hvcnRjdXR9IHRvIHVuZG88L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7Z2V0UGxhdGZvcm0oKSAhPT0gJ3dpbmRvd3MnICYmIChcbiAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I9e2RpbUNvbG9yfT5jdHJsICsgeiB0byBzdXNwZW5kPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApfVxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPXtkaW1Db2xvcn0+e2ltYWdlUGFzdGVTaG9ydGN1dH0gdG8gcGFzdGUgaW1hZ2VzPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj17ZGltQ29sb3J9Pnttb2RlbFBpY2tlclNob3J0Y3V0fSB0byBzd2l0Y2ggbW9kZWw8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7aXNGYXN0TW9kZUVuYWJsZWQoKSAmJiBpc0Zhc3RNb2RlQXZhaWxhYmxlKCkgJiYgKFxuICAgICAgICAgIDxCb3g+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj17ZGltQ29sb3J9PlxuICAgICAgICAgICAgICB7ZmFzdE1vZGVTaG9ydGN1dH0gdG8gdG9nZ2xlIGZhc3QgbW9kZVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApfVxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPXtkaW1Db2xvcn0+e3N0YXNoU2hvcnRjdXR9IHRvIHN0YXNoIHByb21wdDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I9e2RpbUNvbG9yfT5cbiAgICAgICAgICAgIHtleHRlcm5hbEVkaXRvclNob3J0Y3V0fSB0byBlZGl0IGluICRFRElUT1JcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7aXNLZXliaW5kaW5nQ3VzdG9taXphdGlvbkVuYWJsZWQoKSAmJiAoXG4gICAgICAgICAgPEJveD5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPXtkaW1Db2xvcn0+L2tleWJpbmRpbmdzIHRvIGN1c3RvbWl6ZTwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxPQUFPLFFBQVEsWUFBWTtBQUNwQyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFlBQVk7QUFDdEMsU0FBU0MsV0FBVyxRQUFRLHVCQUF1QjtBQUNuRCxTQUFTQyxnQ0FBZ0MsUUFBUSx1Q0FBdUM7QUFDeEYsU0FBU0Msa0JBQWtCLFFBQVEseUNBQXlDO0FBQzVFLFNBQVNDLG1DQUFtQyxR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QUF1RCxHQUFBLElBQUF2RCxDQUFBLFNBQUF3RCxHQUFBLElBQUF4RCxDQUFBLFNBQUF5RCxHQUFBLElBQUF6RCxDQUFBLFNBQUEwRCxHQUFBLElBQUExRCxDQUFBLFNBQUEyRCxHQUFBO0lBbENIQyxHQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQ3pCLENBQUFSLEdBRUssQ0FDSixDQUFBQyxHQUlELENBQ0EsQ0FBQUMsR0FFSyxDQUNMLENBQUFDLEdBRUssQ0FDSixDQUFBQyxHQU1ELENBQ0EsQ0FBQUMsR0FFSyxDQUNMLENBQUFDLEdBSUssQ0FDSixDQUFBQyxHQUlELENBQ0YsRUFuQ0MsR0FBRyxDQW1DRTtJQUFBM0QsQ0FBQSxPQUFBb0QsR0FBQTtJQUFBcEQsQ0FBQSxPQUFBcUQsR0FBQTtJQUFBckQsQ0FBQSxPQUFBc0QsR0FBQTtJQUFBdEQsQ0FBQSxPQUFBdUQsR0FBQTtJQUFBdkQsQ0FBQSxPQUFBd0QsR0FBQTtJQUFBeEQsQ0FBQSxPQUFBeUQsR0FBQTtJQUFBekQsQ0FBQSxPQUFBMEQsR0FBQTtJQUFBMUQsQ0FBQSxPQUFBMkQsR0FBQTtJQUFBM0QsQ0FBQSxPQUFBNEQsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQTVELENBQUE7RUFBQTtFQUFBLElBQUE2RCxHQUFBO0VBQUEsSUFBQTdELENBQUEsU0FBQUosR0FBQSxJQUFBSSxDQUFBLFNBQUFILFFBQUEsSUFBQUcsQ0FBQSxTQUFBeUMsR0FBQSxJQUFBekMsQ0FBQSxTQUFBbUQsR0FBQSxJQUFBbkQsQ0FBQSxTQUFBNEQsR0FBQTtJQTlFUkMsR0FBQSxJQUFDLEdBQUcsQ0FBV2hFLFFBQVEsQ0FBUkEsU0FBTyxDQUFDLENBQWdCLGFBQUssQ0FBTCxLQUFLLENBQU1ELEdBQUcsQ0FBSEEsSUFBRSxDQUFDLENBQ25ELENBQUE2QyxHQWdCSyxDQUNMLENBQUFVLEdBd0JLLENBQ0wsQ0FBQVMsR0FtQ0ssQ0FDUCxFQS9FQyxHQUFHLENBK0VFO0lBQUE1RCxDQUFBLE9BQUFKLEdBQUE7SUFBQUksQ0FBQSxPQUFBSCxRQUFBO0lBQUFHLENBQUEsT0FBQXlDLEdBQUE7SUFBQXpDLENBQUEsT0FBQW1ELEdBQUE7SUFBQW5ELENBQUEsT0FBQTRELEdBQUE7SUFBQTVELENBQUEsT0FBQTZELEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUE3RCxDQUFBO0VBQUE7RUFBQSxPQS9FTjZELEdBK0VNO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/PromptInput/PromptInputModeIndicator.tsx b/components/PromptInput/PromptInputModeIndicator.tsx new file mode 100644 index 0000000..bfbd57a --- /dev/null +++ b/components/PromptInput/PromptInputModeIndicator.tsx @@ -0,0 +1,93 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import * as React from 'react'; +import { Box, Text } from 'src/ink.js'; +import { AGENT_COLOR_TO_THEME_COLOR, AGENT_COLORS, type AgentColorName } from 'src/tools/AgentTool/agentColorManager.js'; +import type { PromptInputMode } from 'src/types/textInputTypes.js'; +import { getTeammateColor } from 'src/utils/teammate.js'; +import type { Theme } from 'src/utils/theme.js'; +import { isAgentSwarmsEnabled } from '../../utils/agentSwarmsEnabled.js'; +type Props = { + mode: PromptInputMode; + isLoading: boolean; + viewingAgentName?: string; + viewingAgentColor?: AgentColorName; +}; + +/** + * Gets the theme color key for the teammate's assigned color. + * Returns undefined if not a teammate or if the color is invalid. + */ +function getTeammateThemeColor(): keyof Theme | undefined { + if (!isAgentSwarmsEnabled()) { + return undefined; + } + const colorName = getTeammateColor(); + if (!colorName) { + return undefined; + } + if (AGENT_COLORS.includes(colorName as AgentColorName)) { + return AGENT_COLOR_TO_THEME_COLOR[colorName as AgentColorName]; + } + return undefined; +} +type PromptCharProps = { + isLoading: boolean; + // Dead code elimination: parameter named themeColor to avoid "teammate" string in external builds + themeColor?: keyof Theme; +}; + +/** + * Renders the prompt character (❯). + * Teammate color overrides the default color when set. + */ +function PromptChar(t0) { + const $ = _c(3); + const { + isLoading, + themeColor + } = t0; + const teammateColor = themeColor; + const color = teammateColor ?? (false ? "subtle" : undefined); + let t1; + if ($[0] !== color || $[1] !== isLoading) { + t1 = {figures.pointer} ; + $[0] = color; + $[1] = isLoading; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} +export function PromptInputModeIndicator(t0) { + const $ = _c(6); + const { + mode, + isLoading, + viewingAgentName, + viewingAgentColor + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = getTeammateThemeColor(); + $[0] = t1; + } else { + t1 = $[0]; + } + const teammateColor = t1; + const viewedTeammateThemeColor = viewingAgentColor ? AGENT_COLOR_TO_THEME_COLOR[viewingAgentColor] : undefined; + let t2; + if ($[1] !== isLoading || $[2] !== mode || $[3] !== viewedTeammateThemeColor || $[4] !== viewingAgentName) { + t2 = {viewingAgentName ? : mode === "bash" ? : }; + $[1] = isLoading; + $[2] = mode; + $[3] = viewedTeammateThemeColor; + $[4] = viewingAgentName; + $[5] = t2; + } else { + t2 = $[5]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwiQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1IiLCJBR0VOVF9DT0xPUlMiLCJBZ2VudENvbG9yTmFtZSIsIlByb21wdElucHV0TW9kZSIsImdldFRlYW1tYXRlQ29sb3IiLCJUaGVtZSIsImlzQWdlbnRTd2FybXNFbmFibGVkIiwiUHJvcHMiLCJtb2RlIiwiaXNMb2FkaW5nIiwidmlld2luZ0FnZW50TmFtZSIsInZpZXdpbmdBZ2VudENvbG9yIiwiZ2V0VGVhbW1hdGVUaGVtZUNvbG9yIiwidW5kZWZpbmVkIiwiY29sb3JOYW1lIiwiaW5jbHVkZXMiLCJQcm9tcHRDaGFyUHJvcHMiLCJ0aGVtZUNvbG9yIiwiUHJvbXB0Q2hhciIsInQwIiwiJCIsIl9jIiwidGVhbW1hdGVDb2xvciIsImNvbG9yIiwidDEiLCJwb2ludGVyIiwiUHJvbXB0SW5wdXRNb2RlSW5kaWNhdG9yIiwiU3ltYm9sIiwiZm9yIiwidmlld2VkVGVhbW1hdGVUaGVtZUNvbG9yIiwidDIiXSwic291cmNlcyI6WyJQcm9tcHRJbnB1dE1vZGVJbmRpY2F0b3IudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJ3NyYy9pbmsuanMnXG5pbXBvcnQge1xuICBBR0VOVF9DT0xPUl9UT19USEVNRV9DT0xPUixcbiAgQUdFTlRfQ09MT1JTLFxuICB0eXBlIEFnZW50Q29sb3JOYW1lLFxufSBmcm9tICdzcmMvdG9vbHMvQWdlbnRUb29sL2FnZW50Q29sb3JNYW5hZ2VyLmpzJ1xuaW1wb3J0IHR5cGUgeyBQcm9tcHRJbnB1dE1vZGUgfSBmcm9tICdzcmMvdHlwZXMvdGV4dElucHV0VHlwZXMuanMnXG5pbXBvcnQgeyBnZXRUZWFtbWF0ZUNvbG9yIH0gZnJvbSAnc3JjL3V0aWxzL3RlYW1tYXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJ3NyYy91dGlscy90aGVtZS5qcydcbmltcG9ydCB7IGlzQWdlbnRTd2FybXNFbmFibGVkIH0gZnJvbSAnLi4vLi4vdXRpbHMvYWdlbnRTd2FybXNFbmFibGVkLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBtb2RlOiBQcm9tcHRJbnB1dE1vZGVcbiAgaXNMb2FkaW5nOiBib29sZWFuXG4gIHZpZXdpbmdBZ2VudE5hbWU/OiBzdHJpbmdcbiAgdmlld2luZ0FnZW50Q29sb3I/OiBBZ2VudENvbG9yTmFtZVxufVxuXG4vKipcbiAqIEdldHMgdGhlIHRoZW1lIGNvbG9yIGtleSBmb3IgdGhlIHRlYW1tYXRlJ3MgYXNzaWduZWQgY29sb3IuXG4gKiBSZXR1cm5zIHVuZGVmaW5lZCBpZiBub3QgYSB0ZWFtbWF0ZSBvciBpZiB0aGUgY29sb3IgaXMgaW52YWxpZC5cbiAqL1xuZnVuY3Rpb24gZ2V0VGVhbW1hdGVUaGVtZUNvbG9yKCk6IGtleW9mIFRoZW1lIHwgdW5kZWZpbmVkIHtcbiAgaWYgKCFpc0FnZW50U3dhcm1zRW5hYmxlZCgpKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZFxuICB9XG4gIGNvbnN0IGNvbG9yTmFtZSA9IGdldFRlYW1tYXRlQ29sb3IoKVxuICBpZiAoIWNvbG9yTmFtZSkge1xuICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxuICBpZiAoQUdFTlRfQ09MT1JTLmluY2x1ZGVzKGNvbG9yTmFtZSBhcyBBZ2VudENvbG9yTmFtZSkpIHtcbiAgICByZXR1cm4gQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1JbY29sb3JOYW1lIGFzIEFnZW50Q29sb3JOYW1lXVxuICB9XG4gIHJldHVybiB1bmRlZmluZWRcbn1cblxudHlwZSBQcm9tcHRDaGFyUHJvcHMgPSB7XG4gIGlzTG9hZGluZzogYm9vbGVhblxuICAvLyBEZWFkIGNvZGUgZWxpbWluYXRpb246IHBhcmFtZXRlciBuYW1lZCB0aGVtZUNvbG9yIHRvIGF2b2lkIFwidGVhbW1hdGVcIiBzdHJpbmcgaW4gZXh0ZXJuYWwgYnVpbGRzXG4gIHRoZW1lQ29sb3I/OiBrZXlvZiBUaGVtZVxufVxuXG4vKipcbiAqIFJlbmRlcnMgdGhlIHByb21wdCBjaGFyYWN0ZXIgKOKdrykuXG4gKiBUZWFtbWF0ZSBjb2xvciBvdmVycmlkZXMgdGhlIGRlZmF1bHQgY29sb3Igd2hlbiBzZXQuXG4gKi9cbmZ1bmN0aW9uIFByb21wdENoYXIoe1xuICBpc0xvYWRpbmcsXG4gIHRoZW1lQ29sb3IsXG59OiBQcm9tcHRDaGFyUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBBc3NpZ24gdG8gb3JpZ2luYWwgbmFtZSBmb3IgY2xhcml0eSB3aXRoaW4gdGhlIGZ1bmN0aW9uXG4gIGNvbnN0IHRlYW1tYXRlQ29sb3IgPSB0aGVtZUNvbG9yXG4gIGNvbnN0IGlzQW50ID0gXCJleHRlcm5hbFwiID09PSAnYW50J1xuICBjb25zdCBjb2xvciA9IHRlYW1tYXRlQ29sb3IgPz8gKGlzQW50ID8gJ3N1YnRsZScgOiB1bmRlZmluZWQpXG5cbiAgcmV0dXJuIChcbiAgICA8VGV4dCBjb2xvcj17Y29sb3J9IGRpbUNvbG9yPXtpc0xvYWRpbmd9PlxuICAgICAge2ZpZ3VyZXMucG9pbnRlcn0mbmJzcDtcbiAgICA8L1RleHQ+XG4gIClcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFByb21wdElucHV0TW9kZUluZGljYXRvcih7XG4gIG1vZGUsXG4gIGlzTG9hZGluZyxcbiAgdmlld2luZ0FnZW50TmFtZSxcbiAgdmlld2luZ0FnZW50Q29sb3IsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHRlYW1tYXRlQ29sb3IgPSBnZXRUZWFtbWF0ZVRoZW1lQ29sb3IoKVxuXG4gIC8vIENvbnZlcnQgdmlld2VkIHRlYW1tYXRlJ3MgY29sb3IgdG8gdGhlbWUgY29sb3JcbiAgLy8gRmFsbHMgYmFjayB0byBQcm9tcHRDaGFyJ3MgZGVmYXVsdCAoc3VidGxlIGZvciBhbnRzLCB1bmRlZmluZWQgZm9yIGV4dGVybmFsKVxuICBjb25zdCB2aWV3ZWRUZWFtbWF0ZVRoZW1lQ29sb3IgPSB2aWV3aW5nQWdlbnRDb2xvclxuICAgID8gQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1Jbdmlld2luZ0FnZW50Q29sb3JdXG4gICAgOiB1bmRlZmluZWRcblxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGFsaWduSXRlbXM9XCJmbGV4LXN0YXJ0XCJcbiAgICAgIGFsaWduU2VsZj1cImZsZXgtc3RhcnRcIlxuICAgICAgZmxleFdyYXA9XCJub3dyYXBcIlxuICAgICAganVzdGlmeUNvbnRlbnQ9XCJmbGV4LXN0YXJ0XCJcbiAgICA+XG4gICAgICB7dmlld2luZ0FnZW50TmFtZSA/IChcbiAgICAgICAgLy8gVXNlIHRlYW1tYXRlJ3MgY29sb3Igb24gdGhlIHN0YW5kYXJkIHByb21wdCBjaGFyYWN0ZXIsIG1hdGNoaW5nIGVzdGFibGlzaGVkIHN0eWxlXG4gICAgICAgIDxQcm9tcHRDaGFyXG4gICAgICAgICAgaXNMb2FkaW5nPXtpc0xvYWRpbmd9XG4gICAgICAgICAgdGhlbWVDb2xvcj17dmlld2VkVGVhbW1hdGVUaGVtZUNvbG9yfVxuICAgICAgICAvPlxuICAgICAgKSA6IG1vZGUgPT09ICdiYXNoJyA/IChcbiAgICAgICAgPFRleHQgY29sb3I9XCJiYXNoQm9yZGVyXCIgZGltQ29sb3I9e2lzTG9hZGluZ30+XG4gICAgICAgICAgISZuYnNwO1xuICAgICAgICA8L1RleHQ+XG4gICAgICApIDogKFxuICAgICAgICA8UHJvbXB0Q2hhclxuICAgICAgICAgIGlzTG9hZGluZz17aXNMb2FkaW5nfVxuICAgICAgICAgIHRoZW1lQ29sb3I9e2lzQWdlbnRTd2FybXNFbmFibGVkKCkgPyB0ZWFtbWF0ZUNvbG9yIDogdW5kZWZpbmVkfVxuICAgICAgICAvPlxuICAgICAgKX1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBTyxLQUFLQyxLQUFLLE1B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 \ No newline at end of file diff --git a/components/PromptInput/PromptInputQueuedCommands.tsx b/components/PromptInput/PromptInputQueuedCommands.tsx new file mode 100644 index 0000000..1612969 --- /dev/null +++ b/components/PromptInput/PromptInputQueuedCommands.tsx @@ -0,0 +1,117 @@ +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import { useMemo } from 'react'; +import { Box } from 'src/ink.js'; +import { useAppState } from 'src/state/AppState.js'; +import { STATUS_TAG, SUMMARY_TAG, TASK_NOTIFICATION_TAG } from '../../constants/xml.js'; +import { QueuedMessageProvider } from '../../context/QueuedMessageContext.js'; +import { useCommandQueue } from '../../hooks/useCommandQueue.js'; +import type { QueuedCommand } from '../../types/textInputTypes.js'; +import { isQueuedCommandVisible } from '../../utils/messageQueueManager.js'; +import { createUserMessage, EMPTY_LOOKUPS, normalizeMessages } from '../../utils/messages.js'; +import { jsonParse } from '../../utils/slowOperations.js'; +import { Message } from '../Message.js'; +const EMPTY_SET = new Set(); + +/** + * Check if a command value is an idle notification that should be hidden. + * Idle notifications are processed silently without showing to the user. + */ +function isIdleNotification(value: string): boolean { + try { + const parsed = jsonParse(value); + return parsed?.type === 'idle_notification'; + } catch { + return false; + } +} + +// Maximum number of task notification lines to show +const MAX_VISIBLE_NOTIFICATIONS = 3; + +/** + * Create a synthetic overflow notification message for capped task notifications. + */ +function createOverflowNotificationMessage(count: number): string { + return `<${TASK_NOTIFICATION_TAG}> +<${SUMMARY_TAG}>+${count} more tasks completed +<${STATUS_TAG}>completed +`; +} + +/** + * Process queued commands to cap task notifications at MAX_VISIBLE_NOTIFICATIONS lines. + * Other command types are always shown in full. + * Idle notifications are filtered out entirely. + */ +function processQueuedCommands(queuedCommands: QueuedCommand[]): QueuedCommand[] { + // Filter out idle notifications - they are processed silently + const filteredCommands = queuedCommands.filter(cmd => typeof cmd.value !== 'string' || !isIdleNotification(cmd.value)); + + // Separate task notifications from other commands + const taskNotifications = filteredCommands.filter(cmd => cmd.mode === 'task-notification'); + const otherCommands = filteredCommands.filter(cmd => cmd.mode !== 'task-notification'); + + // If notifications fit within limit, return all commands as-is + if (taskNotifications.length <= MAX_VISIBLE_NOTIFICATIONS) { + return [...otherCommands, ...taskNotifications]; + } + + // Show first (MAX_VISIBLE_NOTIFICATIONS - 1) notifications, then a summary + const visibleNotifications = taskNotifications.slice(0, MAX_VISIBLE_NOTIFICATIONS - 1); + const overflowCount = taskNotifications.length - (MAX_VISIBLE_NOTIFICATIONS - 1); + + // Create synthetic overflow message + const overflowCommand: QueuedCommand = { + value: createOverflowNotificationMessage(overflowCount), + mode: 'task-notification' + }; + return [...otherCommands, ...visibleNotifications, overflowCommand]; +} +function PromptInputQueuedCommandsImpl(): React.ReactNode { + const queuedCommands = useCommandQueue(); + const viewingAgent = useAppState(s => !!s.viewingAgentTaskId); + // Brief layout: dim queue items + skip the paddingX (brief messages + // already indent themselves). Gate mirrors the brief-spinner/message + // check elsewhere — no teammate-view override needed since this + // component early-returns when viewing a teammate. + const useBriefLayout = feature('KAIROS') || feature('KAIROS_BRIEF') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s_0 => s_0.isBriefOnly) : false; + + // createUserMessage mints a fresh UUID per call; without memoization, streaming + // re-renders defeat Message's areMessagePropsEqual (compares uuid) → flicker. + const messages = useMemo(() => { + if (queuedCommands.length === 0) return null; + // task-notification is shown via useInboxNotification; most isMeta commands + // (scheduled tasks, proactive ticks) are system-generated and hidden. + // Channel messages are the exception — isMeta but shown so the keyboard + // user sees what arrived. + const visibleCommands = queuedCommands.filter(isQueuedCommandVisible); + if (visibleCommands.length === 0) return null; + const processedCommands = processQueuedCommands(visibleCommands); + return normalizeMessages(processedCommands.map(cmd => { + let content = cmd.value; + if (cmd.mode === 'bash' && typeof content === 'string') { + content = `${content}`; + } + // [Image #N] placeholders are inline in the text value (inserted at + // paste time), so the queue preview shows them without stub blocks. + return createUserMessage({ + content + }); + })); + }, [queuedCommands]); + + // Don't show leader's queued commands when viewing any agent's transcript + if (viewingAgent || messages === null) { + return null; + } + return + {messages.map((message, i) => + + )} + ; +} +export const PromptInputQueuedCommands = React.memo(PromptInputQueuedCommandsImpl); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJ1c2VNZW1vIiwiQm94IiwidXNlQXBwU3RhdGUiLCJTVEFUVVNfVEFHIiwiU1VNTUFSWV9UQUciLCJUQVNLX05PVElGSUNBVElPTl9UQUciLCJRdWV1ZWRNZXNzYWdlUHJvdmlkZXIiLCJ1c2VDb21tYW5kUXVldWUiLCJRdWV1ZWRDb21tYW5kIiwiaXNRdWV1ZWRDb21tYW5kVmlzaWJsZSIsImNyZWF0ZVVzZXJNZXNzYWdlIiwiRU1QVFlfTE9PS1VQUyIsIm5vcm1hbGl6ZU1lc3NhZ2VzIiwianNvblBhcnNlIiwiTWVzc2FnZSIsIkVNUFRZX1NFVCIsIlNldCIsImlzSWRsZU5vdGlmaWNhdGlvbiIsInZhbHVlIiwicGFyc2VkIiwidHlwZSIsIk1BWF9WSVNJQkxFX05PVElGSUNBVElPTlMiLCJjcmVhdGVPdmVyZmxvd05vdGlmaWNhdGlvbk1lc3NhZ2UiLCJjb3VudCIsInByb2Nlc3NRdWV1ZWRDb21tYW5kcyIsInF1ZXVlZENvbW1hbmRzIiwiZmlsdGVyZWRDb21tYW5kcyIsImZpbHRlciIsImNtZCIsInRhc2tOb3RpZmljYXRpb25zIiwibW9kZSIsIm90aGVyQ29tbWFuZHMiLCJsZW5ndGgiLCJ2aXNpYmxlTm90aWZpY2F0aW9ucyIsInNsaWNlIiwib3ZlcmZsb3dDb3VudCIsIm92ZXJmbG93Q29tbWFuZCIsIlByb21wdElucHV0UXVldWVkQ29tbWFuZHNJbXBsIiwiUmVhY3ROb2RlIiwidmlld2luZ0FnZW50IiwicyIsInZpZXdpbmdBZ2VudFRhc2tJZCIsInVzZUJyaWVmTGF5b3V0IiwiaXNCcmllZk9ubHkiLCJtZXNzYWdlcyIsInZpc2libGVDb21tYW5kcyIsInByb2Nlc3NlZENvbW1hbmRzIiwibWFwIiwiY29udGVudCIsIm1lc3NhZ2UiLCJpIiwiUHJvbXB0SW5wdXRRdWV1ZWRDb21tYW5kcyIsIm1lbW8iXSwic291cmNlcyI6WyJQcm9tcHRJbnB1dFF1ZXVlZENvbW1hbmRzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTWVtbyB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94IH0gZnJvbSAnc3JjL2luay5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlIH0gZnJvbSAnc3JjL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHtcbiAgU1RBVFVTX1RBRyxcbiAgU1VNTUFSWV9UQUcsXG4gIFRBU0tfTk9USUZJQ0FUSU9OX1RBRyxcbn0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL3htbC5qcydcbmltcG9ydCB7IFF1ZXVlZE1lc3NhZ2VQcm92aWRlciB9IGZyb20gJy4uLy4uL2NvbnRleHQvUXVldWVkTWVzc2FnZUNvbnRleHQuanMnXG5pbXBvcnQgeyB1c2VDb21tYW5kUXVldWUgfSBmcm9tICcuLi8uLi9ob29rcy91c2VDb21tYW5kUXVldWUuanMnXG5pbXBvcnQgdHlwZSB7IFF1ZXVlZENvbW1hbmQgfSBmcm9tICcuLi8uLi90eXBlcy90ZXh0SW5wdXRUeXBlcy5qcydcbmltcG9ydCB7IGlzUXVldWVkQ29tbWFuZFZpc2libGUgfSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlUXVldWVNYW5hZ2VyLmpzJ1xuaW1wb3J0IHtcbiAgY3JlYXRlVXNlck1lc3NhZ2UsXG4gIEVNUFRZX0xPT0tVUFMsXG4gIG5vcm1hbGl6ZU1lc3NhZ2VzLFxufSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB7IGpzb25QYXJzZSB9IGZyb20gJy4uLy4uL3V0aWxzL3Nsb3dPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZSB9IGZyb20gJy4uL01lc3NhZ2UuanMnXG5cbmNvbnN0IEVNUFRZX1NFVCA9IG5ldyBTZXQ8c3RyaW5nPigpXG5cbi8qKlxuICogQ2hlY2sgaWYgYSBjb21tYW5kIHZhbHVlIGlzIGFuIGlkbGUgbm90aWZpY2F0aW9uIHRoYXQgc2hvdWxkIGJlIGhpZGRlbi5cbiAqIElkbGUgbm90aWZpY2F0aW9ucyBhcmUgcHJvY2Vzc2VkIHNpbGVudGx5IHdpdGhvdXQgc2hvd2luZyB0byB0aGUgdXNlci5cbiAqL1xuZnVuY3Rpb24gaXNJZGxlTm90aWZpY2F0aW9uKHZhbHVlOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgdHJ5IHtcbiAgICBjb25zdCBwYXJzZWQgPSBqc29uUGFyc2UodmFsdWUpXG4gICAgcmV0dXJuIHBhcnNlZD8udHlwZSA9PT0gJ2lkbGVfbm90aWZpY2F0aW9uJ1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxufVxuXG4vLyBNYXhpbXVtIG51bWJlciBvZiB0YXNrIG5vdGlmaWNhdGlvbiBsaW5lcyB0byBzaG93XG5jb25zdCBNQVhfVklTSUJMRV9OT1RJRklDQVRJT05TID0gM1xuXG4vKipcbiAqIENyZWF0ZSBhIHN5bnRoZXRpYyBvdmVyZmxvdyBub3RpZmljYXRpb24gbWVzc2FnZSBmb3IgY2FwcGVkIHRhc2sgbm90aWZpY2F0aW9ucy5cbiAqL1xuZnVuY3Rpb24gY3JlYXRlT3ZlcmZsb3dOb3RpZmljYXRpb25NZXNzYWdlKGNvdW50OiBudW1iZXIpOiBzdHJpbmcge1xuICByZXR1cm4gYDwke1RBU0tfTk9USUZJQ0FUSU9OX1RBR30+XG48JHtTVU1NQVJZX1RBR30+KyR7Y291bnR9IG1vcmUgdGFza3MgY29tcGxldGVkPC8ke1NVTU1BUllfVEFHfT5cbjwke1NUQVRVU19UQUd9PmNvbXBsZXRlZDwvJHtTVEFUVVNfVEFHfT5cbjwvJHtUQVNLX05PVElGSUNBVElPTl9UQUd9PmBcbn1cblxuLyoqXG4gKiBQcm9jZXNzIHF1ZXVlZCBjb21tYW5kcyB0byBjYXAgdGFzayBub3RpZmljYXRpb25zIGF0IE1BWF9WSVNJQkxFX05PVElGSUNBVElPTlMgbGluZXMuXG4gKiBPdGhlciBjb21tYW5kIHR5cGVzIGFyZSBhbHdheXMgc2hvd24gaW4gZnVsbC5cbiAqIElkbGUgbm90aWZpY2F0aW9ucyBhcmUgZmlsdGVyZWQgb3V0IGVudGlyZWx5LlxuICovXG5mdW5jdGlvbiBwcm9jZXNzUXVldWVkQ29tbWFuZHMoXG4gIHF1ZXVlZENvbW1hbmRzOiBRdWV1ZWRDb21tYW5kW10sXG4pOiBRdWV1ZWRDb21tYW5kW10ge1xuICAvLyBGaWx0ZXIgb3V0IGlkbGUgbm90aWZpY2F0aW9ucyAtIHRoZXkgYXJlIHByb2Nlc3NlZCBzaWxlbnRseVxuICBjb25zdCBmaWx0ZXJlZENvbW1hbmRzID0gcXVldWVkQ29tbWFuZHMuZmlsdGVyKFxuICAgIGNtZCA9PiB0eXBlb2YgY21kLnZhbHVlICE9PSAnc3RyaW5nJyB8fCAhaXNJZGxlTm90aWZpY2F0aW9uKGNtZC52YWx1ZSksXG4gIClcblxuICAvLyBTZXBhcmF0ZSB0YXNrIG5vdGlmaWNhdGlvbnMgZnJvbSBvdGhlciBjb21tYW5kc1xuICBjb25zdCB0YXNrTm90aWZpY2F0aW9ucyA9IGZpbHRlcmVkQ29tbWFuZHMuZmlsdGVyKFxuICAgIGNtZCA9PiBjbWQubW9kZSA9PT0gJ3Rhc2stbm90aWZpY2F0aW9uJyxcbiAgKVxuICBjb25zdCBvdGhlckNvbW1hbmRzID0gZmlsdGVyZWRDb21tYW5kcy5maWx0ZXIoXG4gICAgY21kID0+IGNtZC5tb2RlICE9PSAndGFzay1ub3RpZmljYXRpb24nLFxuICApXG5cbiAgLy8gSWYgbm90aWZpY2F0aW9ucyBmaXQgd2l0aGluIGxpbWl0LCByZXR1cm4gYWxsIGNvbW1hbmRzIGFzLWlzXG4gIGlmICh0YXNrTm90aWZpY2F0aW9ucy5sZW5ndGggPD0gTUFYX1ZJU0lCTEVfTk9USUZJQ0FUSU9OUykge1xuICAgIHJldHVybiBbLi4ub3RoZXJDb21tYW5kcywgLi4udGFza05vdGlmaWNhdGlvbnNdXG4gIH1cblxuICAvLyBTaG93IGZpcnN0IChNQVhfVklTSUJMRV9OT1RJRklDQVRJT05TIC0gMSkgbm90aWZpY2F0aW9ucywgdGhlbiBhIHN1bW1hcnlcbiAgY29uc3QgdmlzaWJsZU5vdGlmaWNhdGlvbnMgPSB0YXNrTm90aWZpY2F0aW9ucy5zbGljZShcbiAgICAwLFxuICAgIE1BWF9WSVNJQkxFX05PVElGSUNBVElPTlMgLSAxLFxuICApXG4gIGNvbnN0IG92ZXJmbG93Q291bnQgPVxuICAgIHRhc2tOb3RpZmljYXRpb25zLmxlbmd0aCAtIChNQVhfVklTSUJMRV9OT1RJRklDQVRJT05TIC0gMSlcblxuICAvLyBDcmVhdGUgc3ludGhldGljIG92ZXJmbG93IG1lc3NhZ2VcbiAgY29uc3Qgb3ZlcmZsb3dDb21tYW5kOiBRdWV1ZWRDb21tYW5kID0ge1xuICAgIHZhbHVlOiBjcmVhdGVPdmVyZmxvd05vdGlmaWNhdGlvbk1lc3NhZ2Uob3ZlcmZsb3dDb3VudCksXG4gICAgbW9kZTogJ3Rhc2stbm90aWZpY2F0aW9uJyxcbiAgfVxuXG4gIHJldHVybiBbLi4ub3RoZXJDb21tYW5kcywgLi4udmlzaWJsZU5vdGlmaWNhdGlvbnMsIG92ZXJmbG93Q29tbWFuZF1cbn1cblxuZnVuY3Rpb24gUHJvbXB0SW5wdXRRdWV1ZWRDb21tYW5kc0ltcGwoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgcXVldWVkQ29tbWFuZHMgPSB1c2VDb21tYW5kUXVldWUoKVxuICBjb25zdCB2aWV3aW5nQWdlbnQgPSB1c2VBcHBTdGF0ZShzID0+ICEhcy52aWV3aW5nQWdlbnRUYXNrSWQpXG4gIC8vIEJyaWVmIGxheW91dDogZGltIHF1ZXVlIGl0ZW1zICsgc2tpcCB0aGUgcGFkZGluZ1ggKGJyaWVmIG1lc3NhZ2VzXG4gIC8vIGFscmVhZHkgaW5kZW50IHRoZW1zZWx2ZXMpLiBHYXRlIG1pcnJvcnMgdGhlIGJyaWVmLXNwaW5uZXIvbWVzc2FnZVxuICAvLyBjaGVjayBlbHNld2hlcmUg4oCUIG5vIHRlYW1tYXRlLXZpZXcgb3ZlcnJpZGUgbmVlZGVkIHNpbmNlIHRoaXNcbiAgLy8gY29tcG9uZW50IGVhcmx5LXJldHVybnMgd2hlbiB2aWV3aW5nIGEgdGVhbW1hdGUuXG4gIGNvbnN0IHVzZUJyaWVmTGF5b3V0ID1cbiAgICBmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKVxuICAgICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICAgIHVzZUFwcFN0YXRlKHMgPT4gcy5pc0JyaWVmT25seSlcbiAgICAgIDogZmFsc2VcblxuICAvLyBjcmVhdGVVc2VyTWVzc2FnZSBtaW50cyBhIGZyZXNoIFVVSUQgcGVyIGNhbGw7IHdpdGhvdXQgbWVtb2l6YXRpb24sIHN0cmVhbWluZ1xuICAvLyByZS1yZW5kZXJzIGRlZmVhdCBNZXNzYWdlJ3MgYXJlTWVzc2FnZVByb3BzRXF1YWwgKGNvbXBhcmVzIHV1aWQpIOKGkiBmbGlja2VyLlxuICBjb25zdCBtZXNzYWdlcyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmIChxdWV1ZWRDb21tYW5kcy5sZW5ndGggPT09IDApIHJldHVybiBudWxsXG4gICAgLy8gdGFzay1ub3RpZmljYXRpb24gaXMgc2hvd24gdmlhIHVzZUluYm94Tm90aWZpY2F0aW9uOyBtb3N0IGlzTWV0YSBjb21tYW5kc1xuICAgIC8vIChzY2hlZHVsZWQgdGFza3MsIHByb2FjdGl2ZSB0aWNrcykgYXJlIHN5c3RlbS1nZW5lcmF0ZWQgYW5kIGhpZGRlbi5cbiAgICAvLyBDaGFubmVsIG1lc3NhZ2VzIGFyZSB0aGUgZXhjZXB0aW9uIOKAlCBpc01ldGEgYnV0IHNob3duIHNvIHRoZSBrZXlib2FyZFxuICAgIC8vIHVzZXIgc2VlcyB3aGF0IGFycml2ZWQuXG4gICAgY29uc3QgdmlzaWJsZUNvbW1hbmRzID0gcXVldWVkQ29tbWFuZHMuZmlsdGVyKGlzUXVldWVkQ29tbWFuZFZpc2libGUpXG4gICAgaWYgKHZpc2libGVDb21tYW5kcy5sZW5ndGggPT09IDApIHJldHVybiBudWxsXG4gICAgY29uc3QgcHJvY2Vzc2VkQ29tbWFuZHMgPSBwcm9jZXNzUXVldWVkQ29tbWFuZHModmlzaWJsZUNvbW1hbmRzKVxuICAgIHJldHVybiBub3JtYWxpemVNZXNzYWdlcyhcbiAgICAgIHByb2Nlc3NlZENvbW1hbmRzLm1hcChjbWQgPT4ge1xuICAgICAgICBsZXQgY29udGVudCA9IGNtZC52YWx1ZVxuICAgICAgICBpZiAoY21kLm1vZGUgPT09ICdiYXNoJyAmJiB0eXBlb2YgY29udGVudCA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgICBjb250ZW50ID0gYDxiYXNoLWlucHV0PiR7Y29udGVudH08L2Jhc2gtaW5wdXQ+YFxuICAgICAgICB9XG4gICAgICAgIC8vIFtJbWFnZSAjTl0gcGxhY2Vob2xkZXJzIGFyZSBpbmxpbmUgaW4gdGhlIHRleHQgdmFsdWUgKGluc2VydGVkIGF0XG4gICAgICAgIC8vIHBhc3RlIHRpbWUpLCBzbyB0aGUgcXVldWUgcHJldmlldyBzaG93cyB0aGVtIHdpdGhvdXQgc3R1YiBibG9ja3MuXG4gICAgICAgIHJldHVybiBjcmVhdGVVc2VyTWVzc2FnZSh7IGNvbnRlbnQgfSlcbiAgICAgIH0pLFxuICAgIClcbiAgfSwgW3F1ZXVlZENvbW1hbmRzXSlcblxuICAvLyBEb24ndCBzaG93IGxlYWRlcidzIHF1ZXVlZCBjb21tYW5kcyB3aGVuIHZpZXdpbmcgYW55IGFnZW50J3MgdHJhbnNjcmlwdFxuICBpZiAodmlld2luZ0FnZW50IHx8IG1lc3NhZ2VzID09PSBudWxsKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBtYXJnaW5Ub3A9ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgIHttZXNzYWdlcy5tYXAoKG1lc3NhZ2UsIGkpID0+IChcbiAgICAgICAgPFF1ZXVlZE1lc3NhZ2VQcm92aWRlclxuICAgICAgICAgIGtleT17aX1cbiAgICAgICAgICBpc0ZpcnN0PXtpID09PSAwfVxuICAgICAgICAgIHVzZUJyaWVmTGF5b3V0PXt1c2VCcmllZkxheW91dH1cbiAgICAgICAgPlxuICAgICAgICAgIDxNZXNzYWdlXG4gICAgICAgICAgICBtZXNzYWdlPXttZXNzYWdlfVxuICAgICAgICAgICAgbG9va3Vwcz17RU1QVFlfTE9PS1VQU31cbiAgICAgICAgICAgIGFkZE1hcmdpbj17ZmFsc2V9XG4gICAgICAgICAgICB0b29scz17W119XG4gICAgICAgICAgICBjb21tYW5kcz17W119XG4gICAgICAgICAgICB2ZXJib3NlPXtmYWxzZX1cbiAgICAgICAgICAgIGluUHJvZ3Jlc3NUb29sVXNlSURzPXtFTVBUWV9TRVR9XG4gICAgICAgICAgICBwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZT17W119XG4gICAgICAgICAgICBzaG91bGRBbmltYXRlPXtmYWxzZX1cbiAgICAgICAgICAgIHNob3VsZFNob3dEb3Q9e2ZhbHNlfVxuICAgICAgICAgICAgaXNUcmFuc2NyaXB0TW9kZT17ZmFsc2V9XG4gICAgICAgICAgICBpc1N0YXRpYz17dHJ1ZX1cbiAgICAgICAgICAvPlxuICAgICAgICA8L1F1ZXVlZE1lc3NhZ2VQcm92aWRlcj5cbiAgICAgICkpfVxuICAgIDwvQm94PlxuICApXG59XG5cbmV4cG9ydCBjb25zdCBQcm9tcHRJbnB1dFF1ZXVlZENvbW1hbmRzID0gUmVhY3QubWVtbyhcbiAgUHJvbXB0SW5wdXRRdWV1ZWRDb21tYW5kc0ltcGwsXG4pXG4iXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLE9BQU8sUUFBUSxZQUFZO0FBQ3BDLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsT0FBTyxRQUFRLE9BQU87QUFDL0IsU0FBU0MsR0FBRyxRQUFRLFlBQVk7QUFDaEMsU0FBU0MsV0FBVyxRQUFRLHVCQUF1QjtBQUNuRCxTQUNFQyxVQUFVLEVBQ1ZDLFdBQVcsRUFDWEMscUJBQXFCLFFBQ2hCLHdCQUF3QjtBQUMvQixTQUFTQyxxQkFBcUIsUUFBUSx1Q0FBdUM7QUFDN0UsU0FBU0MsZUFBZSxRQUFRLGdDQUFnQztBQUNoRSxjQUFjQyxhQUFhLFFBQVEsK0JBQStCO0FBQ2xFLFNBQVNDLHNCQUFzQixRQUFRLG9DQUFvQztBQUMzRSxTQUNFQyxp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 \ No newline at end of file diff --git a/components/PromptInput/PromptInputStashNotice.tsx b/components/PromptInput/PromptInputStashNotice.tsx new file mode 100644 index 0000000..5ef01d7 --- /dev/null +++ b/components/PromptInput/PromptInputStashNotice.tsx @@ -0,0 +1,25 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import * as React from 'react'; +import { Box, Text } from 'src/ink.js'; +type Props = { + hasStash: boolean; +}; +export function PromptInputStashNotice(t0) { + const $ = _c(1); + const { + hasStash + } = t0; + if (!hasStash) { + return null; + } + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {figures.pointerSmall} Stashed (auto-restores after submit); + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwiUHJvcHMiLCJoYXNTdGFzaCIsIlByb21wdElucHV0U3Rhc2hOb3RpY2UiLCJ0MCIsIiQiLCJfYyIsInQxIiwiU3ltYm9sIiwiZm9yIiwicG9pbnRlclNtYWxsIl0sInNvdXJjZXMiOlsiUHJvbXB0SW5wdXRTdGFzaE5vdGljZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnc3JjL2luay5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgaGFzU3Rhc2g6IGJvb2xlYW5cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFByb21wdElucHV0U3Rhc2hOb3RpY2UoeyBoYXNTdGFzaCB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGlmICghaGFzU3Rhc2gpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IHBhZGRpbmdMZWZ0PXsyfT5cbiAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICB7ZmlndXJlcy5wb2ludGVyU21hbGx9IFN0YXNoZWQgKGF1dG8tcmVzdG9yZXMgYWZ0ZXIgc3VibWl0KVxuICAgICAgPC9UZXh0PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFlBQVk7QUFFdEMsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLFFBQVEsRUFBRSxPQUFPO0FBQ25CLENBQUM7QUFFRCxPQUFPLFNBQUFDLHVCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWdDO0lBQUFKO0VBQUEsSUFBQUUsRUFBbUI7RUFDeEQsSUFBSSxDQUFDRixRQUFRO0lBQUEsT0FDSixJQUFJO0VBQUE7RUFDWixJQUFBSyxFQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBRyxNQUFBLENBQUFDLEdBQUE7SUFHQ0YsRUFBQSxJQUFDLEdBQUcsQ0FBYyxXQUFDLENBQUQsR0FBQyxDQUNqQixDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQ1gsQ0FBQVYsT0FBTyxDQUFBYSxZQUFZLENBQUUscUNBQ3hCLEVBRkMsSUFBSSxDQUdQLEVBSkMsR0FBRyxDQUlFO0lBQUFMLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsT0FKTkUsRUFJTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/PromptInput/SandboxPromptFooterHint.tsx b/components/PromptInput/SandboxPromptFooterHint.tsx new file mode 100644 index 0000000..430b4c0 --- /dev/null +++ b/components/PromptInput/SandboxPromptFooterHint.tsx @@ -0,0 +1,64 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { type ReactNode, useEffect, useRef, useState } from 'react'; +import { Box, Text } from '../../ink.js'; +import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'; +import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js'; +export function SandboxPromptFooterHint() { + const $ = _c(6); + const [recentViolationCount, setRecentViolationCount] = useState(0); + const timerRef = useRef(null); + const detailsShortcut = useShortcutDisplay("app:toggleTranscript", "Global", "ctrl+o"); + let t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + if (!SandboxManager.isSandboxingEnabled()) { + return; + } + const store = SandboxManager.getSandboxViolationStore(); + let lastCount = store.getTotalCount(); + const unsubscribe = store.subscribe(() => { + const currentCount = store.getTotalCount(); + const newViolations = currentCount - lastCount; + if (newViolations > 0) { + setRecentViolationCount(newViolations); + lastCount = currentCount; + if (timerRef.current) { + clearTimeout(timerRef.current); + } + timerRef.current = setTimeout(setRecentViolationCount, 5000, 0); + } + }); + return () => { + unsubscribe(); + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + }; + t1 = []; + $[0] = t0; + $[1] = t1; + } else { + t0 = $[0]; + t1 = $[1]; + } + useEffect(t0, t1); + if (!SandboxManager.isSandboxingEnabled() || recentViolationCount === 0) { + return null; + } + const t2 = recentViolationCount === 1 ? "operation" : "operations"; + let t3; + if ($[2] !== detailsShortcut || $[3] !== recentViolationCount || $[4] !== t2) { + t3 = ⧈ Sandbox blocked {recentViolationCount}{" "}{t2} ·{" "}{detailsShortcut} for details · /sandbox to disable; + $[2] = detailsShortcut; + $[3] = recentViolationCount; + $[4] = t2; + $[5] = t3; + } else { + t3 = $[5]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZVN0YXRlIiwiQm94IiwiVGV4dCIsInVzZVNob3J0Y3V0RGlzcGxheSIsIlNhbmRib3hNYW5hZ2VyIiwiU2FuZGJveFByb21wdEZvb3RlckhpbnQiLCIkIiwiX2MiLCJyZWNlbnRWaW9sYXRpb25Db3VudCIsInNldFJlY2VudFZpb2xhdGlvbkNvdW50IiwidGltZXJSZWYiLCJkZXRhaWxzU2hvcnRjdXQiLCJ0MCIsInQxIiwiU3ltYm9sIiwiZm9yIiwiaXNTYW5kYm94aW5nRW5hYmxlZCIsInN0b3JlIiwiZ2V0U2FuZGJveFZpb2xhdGlvblN0b3JlIiwibGFzdENvdW50IiwiZ2V0VG90YWxDb3VudCIsInVuc3Vic2NyaWJlIiwic3Vic2NyaWJlIiwiY3VycmVudENvdW50IiwibmV3VmlvbGF0aW9ucyIsImN1cnJlbnQiLCJjbGVhclRpbWVvdXQiLCJzZXRUaW1lb3V0IiwidDIiLCJ0MyJdLCJzb3VyY2VzIjpbIlNhbmRib3hQcm9tcHRGb290ZXJIaW50LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHR5cGUgUmVhY3ROb2RlLCB1c2VFZmZlY3QsIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZVNob3J0Y3V0RGlzcGxheSB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZVNob3J0Y3V0RGlzcGxheS5qcydcbmltcG9ydCB7IFNhbmRib3hNYW5hZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvc2FuZGJveC9zYW5kYm94LWFkYXB0ZXIuanMnXG5cbmV4cG9ydCBmdW5jdGlvbiBTYW5kYm94UHJvbXB0Rm9vdGVySGludCgpOiBSZWFjdE5vZGUge1xuICBjb25zdCBbcmVjZW50VmlvbGF0aW9uQ291bnQsIHNldFJlY2VudFZpb2xhdGlvbkNvdW50XSA9IHVzZVN0YXRlKDApXG4gIGNvbnN0IHRpbWVyUmVmID0gdXNlUmVmPE5vZGVKUy5UaW1lb3V0IHwgbnVsbD4obnVsbClcbiAgY29uc3QgZGV0YWlsc1Nob3J0Y3V0ID0gdXNlU2hvcnRjdXREaXNwbGF5KFxuICAgICdhcHA6dG9nZ2xlVHJhbnNjcmlwdCcsXG4gICAgJ0dsb2JhbCcsXG4gICAgJ2N0cmwrbycsXG4gIClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghU2FuZGJveE1hbmFnZXIuaXNTYW5kYm94aW5nRW5hYmxlZCgpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjb25zdCBzdG9yZSA9IFNhbmRib3hNYW5hZ2VyLmdldFNhbmRib3hWaW9sYXRpb25TdG9yZSgpXG4gICAgbGV0IGxhc3RDb3VudCA9IHN0b3JlLmdldFRvdGFsQ291bnQoKVxuXG4gICAgY29uc3QgdW5zdWJzY3JpYmUgPSBzdG9yZS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgY29uc3QgY3VycmVudENvdW50ID0gc3RvcmUuZ2V0VG90YWxDb3VudCgpXG4gICAgICBjb25zdCBuZXdWaW9sYXRpb25zID0gY3VycmVudENvdW50IC0gbGFzdENvdW50XG5cbiAgICAgIGlmIChuZXdWaW9sYXRpb25zID4gMCkge1xuICAgICAgICBzZXRSZWNlbnRWaW9sYXRpb25Db3VudChuZXdWaW9sYXRpb25zKVxuICAgICAgICBsYXN0Q291bnQgPSBjdXJyZW50Q291bnRcblxuICAgICAgICBpZiAodGltZXJSZWYuY3VycmVudCkge1xuICAgICAgICAgIGNsZWFyVGltZW91dCh0aW1lclJlZi5jdXJyZW50KVxuICAgICAgICB9XG5cbiAgICAgICAgdGltZXJSZWYuY3VycmVudCA9IHNldFRpbWVvdXQoc2V0UmVjZW50VmlvbGF0aW9uQ291bnQsIDUwMDAsIDApXG4gICAgICB9XG4gICAgfSlcblxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICB1bnN1YnNjcmliZSgpXG4gICAgICBpZiAodGltZXJSZWYuY3VycmVudCkge1xuICAgICAgICBjbGVhclRpbWVvdXQodGltZXJSZWYuY3VycmVudClcbiAgICAgIH1cbiAgICB9XG4gIH0sIFtdKVxuXG4gIGlmICghU2FuZGJveE1hbmFnZXIuaXNTYW5kYm94aW5nRW5hYmxlZCgpIHx8IHJlY2VudFZpb2xhdGlvbkNvdW50ID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBwYWRkaW5nWD17MH0gcGFkZGluZ1k9ezB9PlxuICAgICAgPFRleHQgY29sb3I9XCJpbmFjdGl2ZVwiIHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICDip4ggU2FuZGJveCBibG9ja2VkIHtyZWNlbnRWaW9sYXRpb25Db3VudH17JyAnfVxuICAgICAgICB7cmVjZW50VmlvbGF0aW9uQ291bnQgPT09IDEgPyAnb3BlcmF0aW9uJyA6ICdvcGVyYXRpb25zJ30gwrd7JyAnfVxuICAgICAgICB7ZGV0YWlsc1Nob3J0Y3V0fSBmb3IgZGV0YWlscyDCtyAvc2FuZGJveCB0byBkaXNhYmxlXG4gICAgICA8L1RleHQ+XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBUyxLQUFLQyxTQUFTLEVBQUVDLFNBQVMsRUFBRUMsTUFBTSxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUNuRSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQVNDLGtCQUFrQixRQUFRLHlDQUF5QztBQUM1RSxTQUFTQyxjQUFjLFFBQVEsd0NBQXdDO0FBRXZFLE9BQU8sU0FBQUMsd0JBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFDTCxPQUFBQyxvQkFBQSxFQUFBQyx1QkFBQSxJQUF3RFQsUUFBUSxDQUFDLENBQUMsQ0FBQztFQUNuRSxNQUFBVSxRQUFBLEdBQWlCWCxNQUFNLENBQXdCLElBQUksQ0FBQztFQUNwRCxNQUFB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 \ No newline at end of file diff --git a/components/PromptInput/ShimmeredInput.tsx b/components/PromptInput/ShimmeredInput.tsx new file mode 100644 index 0000000..b6890e5 --- /dev/null +++ b/components/PromptInput/ShimmeredInput.tsx @@ -0,0 +1,143 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Ansi, Box, Text, useAnimationFrame } from '../../ink.js'; +import { segmentTextByHighlights, type TextHighlight } from '../../utils/textHighlighting.js'; +import { ShimmerChar } from '../Spinner/ShimmerChar.js'; +type Props = { + text: string; + highlights: TextHighlight[]; +}; +type LinePart = { + text: string; + highlight: TextHighlight | undefined; + start: number; +}; +export function HighlightedInput(t0) { + const $ = _c(23); + const { + text, + highlights + } = t0; + let lines; + if ($[0] !== highlights || $[1] !== text) { + const segments = segmentTextByHighlights(text, highlights); + lines = [[]]; + let pos = 0; + for (const segment of segments) { + const parts = segment.text.split("\n"); + for (let i = 0; i < parts.length; i++) { + if (i > 0) { + lines.push([]); + pos = pos + 1; + } + const part = parts[i]; + if (part.length > 0) { + lines[lines.length - 1].push({ + text: part, + highlight: segment.highlight, + start: pos + }); + } + pos = pos + part.length; + } + } + $[0] = highlights; + $[1] = text; + $[2] = lines; + } else { + lines = $[2]; + } + let t1; + if ($[3] !== highlights) { + t1 = highlights.some(_temp); + $[3] = highlights; + $[4] = t1; + } else { + t1 = $[4]; + } + const hasShimmer = t1; + let sweepStart = 0; + let cycleLength = 1; + if (hasShimmer) { + let lo = Infinity; + let hi = -Infinity; + if ($[5] !== hi || $[6] !== highlights || $[7] !== lo) { + for (const h_0 of highlights) { + if (h_0.shimmerColor) { + lo = Math.min(lo, h_0.start); + hi = Math.max(hi, h_0.end); + } + } + $[5] = hi; + $[6] = highlights; + $[7] = lo; + $[8] = lo; + $[9] = hi; + } else { + lo = $[8]; + hi = $[9]; + } + sweepStart = lo - 10; + cycleLength = hi - lo + 20; + } + let t2; + if ($[10] !== cycleLength || $[11] !== hasShimmer || $[12] !== lines || $[13] !== sweepStart) { + t2 = { + lines, + hasShimmer, + sweepStart, + cycleLength + }; + $[10] = cycleLength; + $[11] = hasShimmer; + $[12] = lines; + $[13] = sweepStart; + $[14] = t2; + } else { + t2 = $[14]; + } + const { + lines: lines_0, + hasShimmer: hasShimmer_0, + sweepStart: sweepStart_0, + cycleLength: cycleLength_0 + } = t2; + const [ref, time] = useAnimationFrame(hasShimmer_0 ? 50 : null); + const glimmerIndex = hasShimmer_0 ? sweepStart_0 + Math.floor(time / 50) % cycleLength_0 : -100; + let t3; + if ($[15] !== glimmerIndex || $[16] !== lines_0) { + let t4; + if ($[18] !== glimmerIndex) { + t4 = (lineParts, lineIndex) => {lineParts.length === 0 ? : lineParts.map((part_0, partIndex) => { + if (part_0.highlight?.shimmerColor && part_0.highlight.color) { + return {part_0.text.split("").map((char, charIndex) => )}; + } + return {part_0.text}; + })}; + $[18] = glimmerIndex; + $[19] = t4; + } else { + t4 = $[19]; + } + t3 = lines_0.map(t4); + $[15] = glimmerIndex; + $[16] = lines_0; + $[17] = t3; + } else { + t3 = $[17]; + } + let t4; + if ($[20] !== ref || $[21] !== t3) { + t4 = {t3}; + $[20] = ref; + $[21] = t3; + $[22] = t4; + } else { + t4 = $[22]; + } + return t4; +} +function _temp(h) { + return h.shimmerColor; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkFuc2kiLCJCb3giLCJUZXh0IiwidXNlQW5pbWF0aW9uRnJhbWUiLCJzZWdtZW50VGV4dEJ5SGlnaGxpZ2h0cyIsIlRleHRIaWdobGlnaHQiLCJTaGltbWVyQ2hhciIsIlByb3BzIiwidGV4dCIsImhpZ2hsaWdodHMiLCJMaW5lUGFydCIsImhpZ2hsaWdodCIsInN0YXJ0IiwiSGlnaGxpZ2h0ZWRJbnB1dCIsInQwIiwiJCIsIl9jIiwibGluZXMiLCJzZWdtZW50cyIsInBvcyIsInNlZ21lbnQiLCJwYXJ0cyIsInNwbGl0IiwiaSIsImxlbmd0aCIsInB1c2giLCJwYXJ0IiwidDEiLCJzb21lIiwiX3RlbXAiLCJoYXNTaGltbWVyIiwic3dlZXBTdGFydCIsImN5Y2xlTGVuZ3RoIiwibG8iLCJJbmZpbml0eSIsImhpIiwiaF8wIiwiaCIsInNoaW1tZXJDb2xvciIsIk1hdGgiLCJtaW4iLCJtYXgiLCJlbmQiLCJ0MiIsImxpbmVzXzAiLCJoYXNTaGltbWVyXzAiLCJzd2VlcFN0YXJ0XzAiLCJjeWNsZUxlbmd0aF8wIiwicmVmIiwidGltZSIsImdsaW1tZXJJbmRleCIsImZsb29yIiwidDMiLCJ0NCIsImxpbmVQYXJ0cyIsImxpbmVJbmRleCIsIm1hcCIsInBhcnRfMCIsInBhcnRJbmRleCIsImNvbG9yIiwiY2hhciIsImNoYXJJbmRleCIsImRpbUNvbG9yIiwiaW52ZXJzZSJdLCJzb3VyY2VzIjpbIlNoaW1tZXJlZElucHV0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEFuc2ksIEJveCwgVGV4dCwgdXNlQW5pbWF0aW9uRnJhbWUgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQge1xuICBzZWdtZW50VGV4dEJ5SGlnaGxpZ2h0cyxcbiAgdHlwZSBUZXh0SGlnaGxpZ2h0LFxufSBmcm9tICcuLi8uLi91dGlscy90ZXh0SGlnaGxpZ2h0aW5nLmpzJ1xuaW1wb3J0IHsgU2hpbW1lckNoYXIgfSBmcm9tICcuLi9TcGlubmVyL1NoaW1tZXJDaGFyLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0ZXh0OiBzdHJpbmdcbiAgaGlnaGxpZ2h0czogVGV4dEhpZ2hsaWdodFtdXG59XG5cbnR5cGUgTGluZVBhcnQgPSB7XG4gIHRleHQ6IHN0cmluZ1xuICBoaWdobGlnaHQ6IFRleHRIaWdobGlnaHQgfCB1bmRlZmluZWRcbiAgc3RhcnQ6IG51bWJlclxufVxuXG5leHBvcnQgZnVuY3Rpb24gSGlnaGxpZ2h0ZWRJbnB1dCh7IHRleHQsIGhpZ2hsaWdodHMgfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBUaGUgc2hpbW1lciBhbmltYXRpb24gKGJlbG93KSByZS1yZW5kZXJzIHRoaXMgY29tcG9uZW50IGF0IDIwZnBzIHdoaWxlIHRoZVxuICAvLyB1bHRyYXRoaW5rIGtleXdvcmQgaXMgcHJlc2VudC4gdGV4dC9oaWdobGlnaHRzIGFyZSByZWZlcmVudGlhbGx5IHN0YWJsZVxuICAvLyBhY3Jvc3MgYW5pbWF0aW9uIHRpY2tzIChwYXJlbnQgZG9lc24ndCByZS1yZW5kZXIpLCBzbyBtZW1vaXplIGV2ZXJ5dGhpbmdcbiAgLy8gdGhhdCBkZXJpdmVzIGZyb20gdGhlbTogc2VnbWVudFRleHRCeUhpZ2hsaWdodHMgYWxvbmUgaXMgfjg1wrVzL2NhbGxcbiAgLy8gKHRva2VuaXplICsgc29ydCArIE8obsKyKSBvdmVybGFwKSwgd2hpY2ggYWRkcyB1cCBmYXN0IGF0IDIwZnBzLlxuICBjb25zdCB7IGxpbmVzLCBoYXNTaGltbWVyLCBzd2VlcFN0YXJ0LCBjeWNsZUxlbmd0aCB9ID0gUmVhY3QudXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3Qgc2VnbWVudHMgPSBzZWdtZW50VGV4dEJ5SGlnaGxpZ2h0cyh0ZXh0LCBoaWdobGlnaHRzKVxuXG4gICAgLy8gU3BsaXQgc2VnbWVudHMgYnkgbmV3bGluZXMgaW50byBwZXItbGluZSBncm91cHMuIEluaydzIHJvdy1kaXJlY3Rpb24gQm94XG4gICAgLy8gaW5kZW50cyBjb250aW51YXRpb24gbGluZXMgb2YgYSBtdWx0aS1saW5lIGNoaWxkIHRvIHRoYXQgY2hpbGQncyBYIG9mZnNldC5cbiAgICAvLyBCeSBzcGxpdHRpbmcgYXQgbmV3bGluZXMsIGVhY2ggbGluZSByZW5kZXJzIGFzIGl0cyBvd24gcm93LCBhdm9pZGluZyB0aGVcbiAgICAvLyBpbmNvcnJlY3QgaW5kZW50YXRpb24gd2hlbiBoaWdobGlnaHRlZCB0ZXh0IGlzIGZvbGxvd2VkIGJ5IHdyYXBwZWQgY29udGVudC5cbiAgICBjb25zdCBsaW5lczogTGluZVBhcnRbXVtdID0gW1tdXVxuICAgIGxldCBwb3MgPSAwXG4gICAgZm9yIChjb25zdCBzZWdtZW50IG9mIHNlZ21lbnRzKSB7XG4gICAgICBjb25zdCBwYXJ0cyA9IHNlZ21lbnQudGV4dC5zcGxpdCgnXFxuJylcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGkgPiAwKSB7XG4gICAgICAgICAgbGluZXMucHVzaChbXSlcbiAgICAgICAgICBwb3MgKz0gMVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHBhcnQgPSBwYXJ0c1tpXSFcbiAgICAgICAgaWYgKHBhcnQubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGxpbmVzW2xpbmVzLmxlbmd0aCAtIDFdIS5wdXNoKHtcbiAgICAgICAgICAgIHRleHQ6IHBhcnQsXG4gICAgICAgICAgICBoaWdobGlnaHQ6IHNlZ21lbnQuaGlnaGxpZ2h0LFxuICAgICAgICAgICAgc3RhcnQ6IHBvcyxcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICAgIHBvcyArPSBwYXJ0Lmxlbmd0aFxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFNjb3BlIHRoZSBzd2VlcCB0byBzaGltbWVyLWhpZ2hsaWdodGVkIHJhbmdlcyBzbyBjeWNsZSB0aW1lIGRvZXNuJ3QgZ3Jvd1xuICAgIC8vIHdpdGggaW5wdXQgbGVuZ3RoLiBQYWRkaW5nIGNyZWF0ZXMgYW4gb2Zmc2NyZWVuIHBhdXNlIGJldHdlZW4gc3dlZXBzLlxuICAgIGNvbnN0IGhhc1NoaW1tZXIgPSBoaWdobGlnaHRzLnNvbWUoaCA9PiBoLnNoaW1tZXJDb2xvcilcbiAgICBsZXQgc3dlZXBTdGFydCA9IDBcbiAgICBsZXQgY3ljbGVMZW5ndGggPSAxXG4gICAgaWYgKGhhc1NoaW1tZXIpIHtcbiAgICAgIGNvbnN0IHBhZGRpbmcgPSAxMFxuICAgICAgbGV0IGxvID0gSW5maW5pdHlcbiAgICAgIGxldCBoaSA9IC1JbmZpbml0eVxuICAgICAgZm9yIChjb25zdCBoIG9mIGhpZ2hsaWdodHMpIHtcbiAgICAgICAgaWYgKGguc2hpbW1lckNvbG9yKSB7XG4gICAgICAgICAgbG8gPSBNYXRoLm1pbihsbywgaC5zdGFydClcbiAgICAgICAgICBoaSA9IE1hdGgubWF4KGhpLCBoLmVuZClcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgc3dlZXBTdGFydCA9IGxvIC0gcGFkZGluZ1xuICAgICAgY3ljbGVMZW5ndGggPSBoaSAtIGxvICsgcGFkZGluZyAqIDJcbiAgICB9XG5cbiAgICByZXR1cm4geyBsaW5lcywgaGFzU2hpbW1lciwgc3dlZXBTdGFydCwgY3ljbGVMZW5ndGggfVxuICB9LCBbdGV4dCwgaGlnaGxpZ2h0c10pXG5cbiAgY29uc3QgW3JlZiwgdGltZV0gPSB1c2VBbmltYXRpb25GcmFtZShoYXNTaGltbWVyID8gNTAgOiBudWxsKVxuICBjb25zdCBnbGltbWVySW5kZXggPSBoYXNTaGltbWVyXG4gICAgPyBzd2VlcFN0YXJ0ICsgKE1hdGguZmxvb3IodGltZSAvIDUwKSAlIGN5Y2xlTGVuZ3RoKVxuICAgIDogLTEwMFxuXG4gIHJldHVybiAoXG4gICAgPEJveCByZWY9e3JlZn0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAge2xpbmVzLm1hcCgobGluZVBhcnRzLCBsaW5lSW5kZXgpID0+IChcbiAgICAgICAgPEJveCBrZXk9e2xpbmVJbmRleH0+XG4gICAgICAgICAge2xpbmVQYXJ0cy5sZW5ndGggPT09IDAgPyAoXG4gICAgICAgICAgICA8VGV4dD4gPC9UZXh0PlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICBsaW5lUGFydHMubWFwKChwYXJ0LCBwYXJ0SW5kZXgpID0+IHtcbiAgICAgICAgICAgICAgaWYgKHBhcnQuaGlnaGxpZ2h0Py5zaGltbWVyQ29sb3IgJiYgcGFydC5oaWdobGlnaHQuY29sb3IpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICAgICAgPFRleHQga2V5PXtwYXJ0SW5kZXh9PlxuICAgICAgICAgICAgICAgICAgICB7cGFydC50ZXh0LnNwbGl0KCcnKS5tYXAoKGNoYXIsIGNoYXJJbmRleCkgPT4gKFxuICAgICAgICAgICAgICAgICAgICAgIDxTaGltbWVyQ2hhclxuICAgICAgICAgICAgICAgICAgICAgICAga2V5PXtjaGFySW5kZXh9XG4gICAgICAgICAgICAgICAgICAgICAgICBjaGFyPXtjaGFyfVxuICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXg9e3BhcnQuc3RhcnQgKyBjaGFySW5kZXh9XG4gICAgICAgICAgICAgICAgICAgICAgICBnbGltbWVySW5kZXg9e2dsaW1tZXJJbmRleH1cbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VDb2xvcj17cGFydC5oaWdobGlnaHQhLmNvbG9yIX1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNoaW1tZXJDb2xvcj17cGFydC5oaWdobGlnaHQhLnNoaW1tZXJDb2xvciF9XG4gICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICAgIGtleT17cGFydEluZGV4fVxuICAgICAgICAgICAgICAgICAgY29sb3I9e3BhcnQuaGlnaGxpZ2h0Py5jb2xvcn1cbiAgICAgICAgICAgICAgICAgIGRpbUNvbG9yPXtwYXJ0LmhpZ2hsaWdodD8uZGltQ29sb3J9XG4gICAgICAgICAgICAgICAgICBpbnZlcnNlPXtwYXJ0LmhpZ2hsaWdodD8uaW52ZXJzZX1cbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICA8QW5zaT57cGFydC50ZXh0fTwvQW5zaT5cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgKX1cbiAgICAgICAgPC9Cb3g+XG4gICAgICApKX1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxJQUFJLEVBQUVDLEdBQUcsRUFBRUMsSUFBSSxFQUFFQyxpQkFBaUIsUUFBUSxjQUFjO0FBQ2pFLFNBQ0VDLHVCQUF1QixFQUN2QixLQUFLQyxhQUFhLFFBQ2IsaUNBQWlDO0FBQ3hDLFNBQVNDLFdBQVcsUUFBUSwyQkFBMkI7QUFFdkQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLElBQUksRUFBRSxNQUFNO0VBQ1pDLFVBQVUsRUFBRUosYUFBYSxFQUFFO0FBQzdCLENBQUM7QUFFRCxLQUFLSyxRQUFRLEdBQUc7RUFDZEYsSUFBSSxFQUFFLE1BQU07RUFDWkcsU0FBUyxFQUFFTixhQUFhLEdBQUcsU0FBUztFQUNwQ08sS0FBSyxFQUFFLE1BQU07QUFDZixDQUFDO0FBRUQsT0FBTyxTQUFBQyxpQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUEwQjtJQUFBUixJQUFBO0lBQUFDO0VBQUEsSUFBQUssRUFBMkI7RUFBQSxJQUFBRyxLQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBTixVQUFBLElBQUFNLENBQUEsUUFBQVAsSUFBQTtJQU94RCxNQUFBVSxRQUFBLEdBQWlCZCx1QkFB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 \ No newline at end of file diff --git a/components/PromptInput/VoiceIndicator.tsx b/components/PromptInput/VoiceIndicator.tsx new file mode 100644 index 0000000..5a5bb20 --- /dev/null +++ b/components/PromptInput/VoiceIndicator.tsx @@ -0,0 +1,137 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import { useSettings } from '../../hooks/useSettings.js'; +import { Box, Text, useAnimationFrame } from '../../ink.js'; +import { interpolateColor, toRGBColor } from '../Spinner/utils.js'; +type Props = { + voiceState: 'idle' | 'recording' | 'processing'; +}; + +// Processing shimmer colors: dim gray to lighter gray (matches ThinkingShimmerText) +const PROCESSING_DIM = { + r: 153, + g: 153, + b: 153 +}; +const PROCESSING_BRIGHT = { + r: 185, + g: 185, + b: 185 +}; +const PULSE_PERIOD_S = 2; // 2 second period for all pulsing animations + +export function VoiceIndicator(props) { + const $ = _c(2); + if (!feature("VOICE_MODE")) { + return null; + } + let t0; + if ($[0] !== props) { + t0 = ; + $[0] = props; + $[1] = t0; + } else { + t0 = $[1]; + } + return t0; +} +function VoiceIndicatorImpl(t0) { + const $ = _c(2); + const { + voiceState + } = t0; + switch (voiceState) { + case "recording": + { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = listening…; + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; + } + case "processing": + { + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + case "idle": + { + return null; + } + } +} + +// Static — the warmup window (~120ms between space #2 and activation) +// is too brief for a 1s-period shimmer to register, and a 50ms animation +// timer here runs concurrently with auto-repeat spaces arriving every +// 30-80ms, compounding re-renders during an already-busy window. +export function VoiceWarmupHint() { + const $ = _c(1); + if (!feature("VOICE_MODE")) { + return null; + } + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = keep holding…; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +function ProcessingShimmer() { + const $ = _c(8); + const settings = useSettings(); + const reducedMotion = settings.prefersReducedMotion ?? false; + const [ref, time] = useAnimationFrame(reducedMotion ? null : 50); + if (reducedMotion) { + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = Voice: processing…; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + const elapsedSec = time / 1000; + const opacity = (Math.sin(elapsedSec * Math.PI * 2 / PULSE_PERIOD_S) + 1) / 2; + let t0; + if ($[1] !== opacity) { + t0 = toRGBColor(interpolateColor(PROCESSING_DIM, PROCESSING_BRIGHT, opacity)); + $[1] = opacity; + $[2] = t0; + } else { + t0 = $[2]; + } + const color = t0; + let t1; + if ($[3] !== color) { + t1 = Voice: processing…; + $[3] = color; + $[4] = t1; + } else { + t1 = $[4]; + } + let t2; + if ($[5] !== ref || $[6] !== t1) { + t2 = {t1}; + $[5] = ref; + $[6] = t1; + $[7] = t2; + } else { + t2 = $[7]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJ1c2VTZXR0aW5ncyIsIkJveCIsIlRleHQiLCJ1c2VBbmltYXRpb25GcmFtZSIsImludGVycG9sYXRlQ29sb3IiLCJ0b1JHQkNvbG9yIiwiUHJvcHMiLCJ2b2ljZVN0YXRlIiwiUFJPQ0VTU0lOR19ESU0iLCJyIiwiZyIsImIiLCJQUk9DRVNTSU5HX0JSSUdIVCIsIlBVTFNFX1BFUklPRF9TIiwiVm9pY2VJbmRpY2F0b3IiLCJwcm9wcyIsIiQiLCJfYyIsInQwIiwiVm9pY2VJbmRpY2F0b3JJbXBsIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJWb2ljZVdhcm11cEhpbnQiLCJQcm9jZXNzaW5nU2hpbW1lciIsInNldHRpbmdzIiwicmVkdWNlZE1vdGlvbiIsInByZWZlcnNSZWR1Y2VkTW90aW9uIiwicmVmIiwidGltZSIsImVsYXBzZWRTZWMiLCJvcGFjaXR5IiwiTWF0aCIsInNpbiIsIlBJIiwiY29sb3IiLCJ0MiJdLCJzb3VyY2VzIjpbIlZvaWNlSW5kaWNhdG9yLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlU2V0dGluZ3MgfSBmcm9tICcuLi8uLi9ob29rcy91c2VTZXR0aW5ncy5qcydcbmltcG9ydCB7IEJveCwgVGV4dCwgdXNlQW5pbWF0aW9uRnJhbWUgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBpbnRlcnBvbGF0ZUNvbG9yLCB0b1JHQkNvbG9yIH0gZnJvbSAnLi4vU3Bpbm5lci91dGlscy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgdm9pY2VTdGF0ZTogJ2lkbGUnIHwgJ3JlY29yZGluZycgfCAncHJvY2Vzc2luZydcbn1cblxuLy8gUHJvY2Vzc2luZyBzaGltbWVyIGNvbG9yczogZGltIGdyYXkgdG8gbGlnaHRlciBncmF5IChtYXRjaGVzIFRoaW5raW5nU2hpbW1lclRleHQpXG5jb25zdCBQUk9DRVNTSU5HX0RJTSA9IHsgcjogMTUzLCBnOiAxNTMsIGI6IDE1MyB9XG5jb25zdCBQUk9DRVNTSU5HX0JSSUdIVCA9IHsgcjogMTg1LCBnOiAxODUsIGI6IDE4NSB9XG5cbmNvbnN0IFBVTFNFX1BFUklPRF9TID0gMiAvLyAyIHNlY29uZCBwZXJpb2QgZm9yIGFsbCBwdWxzaW5nIGFuaW1hdGlvbnNcblxuZXhwb3J0IGZ1bmN0aW9uIFZvaWNlSW5kaWNhdG9yKHByb3BzOiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGlmICghZmVhdHVyZSgnVk9JQ0VfTU9ERScpKSByZXR1cm4gbnVsbFxuICByZXR1cm4gPFZvaWNlSW5kaWNhdG9ySW1wbCB7Li4ucHJvcHN9IC8+XG59XG5cbmZ1bmN0aW9uIFZvaWNlSW5kaWNhdG9ySW1wbCh7IHZvaWNlU3RhdGUgfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBzd2l0Y2ggKHZvaWNlU3RhdGUpIHtcbiAgICBjYXNlICdyZWNvcmRpbmcnOlxuICAgICAgcmV0dXJuIDxUZXh0IGRpbUNvbG9yPmxpc3RlbmluZ+KApjwvVGV4dD5cbiAgICBjYXNlICdwcm9jZXNzaW5nJzpcbiAgICAgIHJldHVybiA8UHJvY2Vzc2luZ1NoaW1tZXIgLz5cbiAgICBjYXNlICdpZGxlJzpcbiAgICAgIHJldHVybiBudWxsXG4gIH1cbn1cblxuLy8gU3RhdGljIOKAlCB0aGUgd2FybXVwIHdpbmRvdyAofjEyMG1zIGJldHdlZW4gc3BhY2UgIzIgYW5kIGFjdGl2YXRpb24pXG4vLyBpcyB0b28gYnJpZWYgZm9yIGEgMXMtcGVyaW9kIHNoaW1tZXIgdG8gcmVnaXN0ZXIsIGFuZCBhIDUwbXMgYW5pbWF0aW9uXG4vLyB0aW1lciBoZXJlIHJ1bnMgY29uY3VycmVudGx5IHdpdGggYXV0by1yZXBlYXQgc3BhY2VzIGFycml2aW5nIGV2ZXJ5XG4vLyAzMC04MG1zLCBjb21wb3VuZGluZyByZS1yZW5kZXJzIGR1cmluZyBhbiBhbHJlYWR5LWJ1c3kgd2luZG93LlxuZXhwb3J0IGZ1bmN0aW9uIFZvaWNlV2FybXVwSGludCgpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoIWZlYXR1cmUoJ1ZPSUNFX01PREUnKSkgcmV0dXJuIG51bGxcbiAgcmV0dXJuIDxUZXh0IGRpbUNvbG9yPmtlZXAgaG9sZGluZ+KApjwvVGV4dD5cbn1cblxuZnVuY3Rpb24gUHJvY2Vzc2luZ1NoaW1tZXIoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3Qgc2V0dGluZ3MgPSB1c2VTZXR0aW5ncygpXG4gIGNvbnN0IHJlZHVjZWRNb3Rpb24gPSBzZXR0aW5ncy5wcmVmZXJzUmVkdWNlZE1vdGlvbiA/PyBmYWxzZVxuICBjb25zdCBbcmVmLCB0aW1lXSA9IHVzZUFuaW1hdGlvbkZyYW1lKHJlZHVjZWRNb3Rpb24gPyBudWxsIDogNTApXG5cbiAgaWYgKHJlZHVjZWRNb3Rpb24pIHtcbiAgICByZXR1cm4gPFRleHQgY29sb3I9XCJ3YXJuaW5nXCI+Vm9pY2U6IHByb2Nlc3NpbmfigKY8L1RleHQ+XG4gIH1cblxuICBjb25zdCBlbGFwc2VkU2VjID0gdGltZSAvIDEwMDBcbiAgY29uc3Qgb3BhY2l0eSA9XG4gICAgKE1hdGguc2luKChlbGFwc2VkU2VjICogTWF0aC5QSSAqIDIpIC8gUFVMU0VfUEVSSU9EX1MpICsgMSkgLyAyXG4gIGNvbnN0IGNvbG9yID0gdG9SR0JDb2xvcihcbiAgICBpbnRlcnBvbGF0ZUNvbG9yKFBST0NFU1NJTkdfRElNLCBQUk9DRVNTSU5HX0JSSUdIVCwgb3BhY2l0eSksXG4gIClcblxuICByZXR1cm4gKFxuICAgIDxCb3ggcmVmPXtyZWZ9PlxuICAgICAgPFRleHQgY29sb3I9e2NvbG9yfT5Wb2ljZTogcHJvY2Vzc2luZ+KApjwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxXQUFXLFFB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QUVoRSxJQUFJQSxhQUFhO0lBQUEsSUFBQVIsRUFBQTtJQUFBLElBQUFGLENBQUEsUUFBQUssTUFBQSxDQUFBQyxHQUFBO01BQ1JKLEVBQUEsSUFBQyxJQUFJLENBQU8sS0FBUyxDQUFULFNBQVMsQ0FBQyxrQkFBa0IsRUFBdkMsSUFBSSxDQUEwQztNQUFBRixDQUFBLE1BQUFFLEVBQUE7SUFBQTtNQUFBQSxFQUFBLEdBQUFGLENBQUE7SUFBQTtJQUFBLE9BQS9DRSxFQUErQztFQUFBO0VBR3hELE1BQUFZLFVBQUEsR0FBbUJELElBQUksR0FBRyxJQUFJO0VBQzlCLE1BQUFFLE9BQUEsR0FDRSxDQUFDQyxJQUFJLENBQUFDLEdBQUksQ0FBRUgsVUFBVSxHQUFHRSxJQUFJLENBQUFFLEVBQUcsR0FBRyxDQUFDLEdBQUlyQixjQUFjLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQztFQUFBLElBQUFLLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFlLE9BQUE7SUFDbkRiLEVBQUEsR0FBQWIsVUFBVSxDQUN0QkQsZ0JBQWdCLENBQUNJLGNBQWMsRUFBRUksaUJBQWlCLEVBQUVtQixPQUFPLENBQzdELENBQUM7SUFBQWYsQ0FBQSxNQUFBZSxPQUFBO0lBQUFmLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBRkQsTUFBQW1CLEtBQUEsR0FBY2pCLEVBRWI7RUFBQSxJQUFBRSxFQUFBO0VBQUEsSUFBQUosQ0FBQSxRQUFBbUIsS0FBQTtJQUlHZixFQUFBLElBQUMsSUFBSSxDQUFRZSxLQUFLLENBQUxBLE1BQUksQ0FBQyxDQUFFLGtCQUFrQixFQUFyQyxJQUFJLENBQXdDO0lBQUFuQixDQUFBLE1BQUFtQixLQUFBO0lBQUFuQixDQUFBLE1BQUFJLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFKLENBQUE7RUFBQTtFQUFBLElBQUFvQixFQUFBO0VBQUEsSUFBQXBCLENBQUEsUUFBQVksR0FBQSxJQUFBWixDQUFBLFFBQUFJLEVBQUE7SUFEL0NnQixFQUFBLElBQUMsR0FBRyxDQUFNUixHQUFHLENBQUhBLElBQUUsQ0FBQyxDQUNYLENBQUFSLEVBQTRDLENBQzlDLEVBRkMsR0FBRyxDQUVFO0lBQUFKLENBQUEsTUFBQVksR0FBQTtJQUFBWixDQUFBLE1BQUFJLEVBQUE7SUFBQUosQ0FBQSxNQUFBb0IsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQXBCLENBQUE7RUFBQTtFQUFBLE9BRk5vQixFQUVNO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/PromptInput/inputModes.ts b/components/PromptInput/inputModes.ts new file mode 100644 index 0000000..f464a20 --- /dev/null +++ b/components/PromptInput/inputModes.ts @@ -0,0 +1,33 @@ +import type { HistoryMode } from 'src/hooks/useArrowKeyHistory.js' +import type { PromptInputMode } from 'src/types/textInputTypes.js' + +export function prependModeCharacterToInput( + input: string, + mode: PromptInputMode, +): string { + switch (mode) { + case 'bash': + return `!${input}` + default: + return input + } +} + +export function getModeFromInput(input: string): HistoryMode { + if (input.startsWith('!')) { + return 'bash' + } + return 'prompt' +} + +export function getValueFromInput(input: string): string { + const mode = getModeFromInput(input) + if (mode === 'prompt') { + return input + } + return input.slice(1) +} + +export function isInputModeCharacter(input: string): boolean { + return input === '!' +} diff --git a/components/PromptInput/inputPaste.ts b/components/PromptInput/inputPaste.ts new file mode 100644 index 0000000..03fbd89 --- /dev/null +++ b/components/PromptInput/inputPaste.ts @@ -0,0 +1,90 @@ +import { getPastedTextRefNumLines } from 'src/history.js' +import type { PastedContent } from 'src/utils/config.js' + +const TRUNCATION_THRESHOLD = 10000 // Characters before we truncate +const PREVIEW_LENGTH = 1000 // Characters to show at start and end + +type TruncatedMessage = { + truncatedText: string + placeholderContent: string +} + +/** + * Determines whether the input text should be truncated. If so, it adds a + * truncated text placeholder and neturns + * + * @param text The input text + * @param nextPasteId The reference id to use + * @returns The new text to display and separate placeholder content if applicable. + */ +export function maybeTruncateMessageForInput( + text: string, + nextPasteId: number, +): TruncatedMessage { + // If the text is short enough, return it as-is + if (text.length <= TRUNCATION_THRESHOLD) { + return { + truncatedText: text, + placeholderContent: '', + } + } + + // Calculate how much text to keep from start and end + const startLength = Math.floor(PREVIEW_LENGTH / 2) + const endLength = Math.floor(PREVIEW_LENGTH / 2) + + // Extract the portions we'll keep + const startText = text.slice(0, startLength) + const endText = text.slice(-endLength) + + // Calculate the number of lines that will be truncated + const placeholderContent = text.slice(startLength, -endLength) + const truncatedLines = getPastedTextRefNumLines(placeholderContent) + + // Create a placeholder reference similar to pasted text + const placeholderId = nextPasteId + const placeholderRef = formatTruncatedTextRef(placeholderId, truncatedLines) + + // Combine the parts with the placeholder + const truncatedText = startText + placeholderRef + endText + + return { + truncatedText, + placeholderContent, + } +} + +function formatTruncatedTextRef(id: number, numLines: number): string { + return `[...Truncated text #${id} +${numLines} lines...]` +} + +export function maybeTruncateInput( + input: string, + pastedContents: Record, +): { newInput: string; newPastedContents: Record } { + // Get the next available ID for the truncated content + const existingIds = Object.keys(pastedContents).map(Number) + const nextPasteId = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 1 + + // Apply truncation + const { truncatedText, placeholderContent } = maybeTruncateMessageForInput( + input, + nextPasteId, + ) + + if (!placeholderContent) { + return { newInput: input, newPastedContents: pastedContents } + } + + return { + newInput: truncatedText, + newPastedContents: { + ...pastedContents, + [nextPasteId]: { + id: nextPasteId, + type: 'text', + content: placeholderContent, + }, + }, + } +} diff --git a/components/PromptInput/useMaybeTruncateInput.ts b/components/PromptInput/useMaybeTruncateInput.ts new file mode 100644 index 0000000..61de64f --- /dev/null +++ b/components/PromptInput/useMaybeTruncateInput.ts @@ -0,0 +1,58 @@ +import { useEffect, useState } from 'react' +import type { PastedContent } from 'src/utils/config.js' +import { maybeTruncateInput } from './inputPaste.js' + +type Props = { + input: string + pastedContents: Record + onInputChange: (input: string) => void + setCursorOffset: (offset: number) => void + setPastedContents: (contents: Record) => void +} + +export function useMaybeTruncateInput({ + input, + pastedContents, + onInputChange, + setCursorOffset, + setPastedContents, +}: Props) { + // Track if we've initialized this specific input value + const [hasAppliedTruncationToInput, setHasAppliedTruncationToInput] = + useState(false) + + // Process input for truncation and pasted images from MessageSelector. + useEffect(() => { + if (hasAppliedTruncationToInput) { + return + } + + if (input.length <= 10_000) { + return + } + + const { newInput, newPastedContents } = maybeTruncateInput( + input, + pastedContents, + ) + + onInputChange(newInput) + setCursorOffset(newInput.length) + setPastedContents(newPastedContents) + setHasAppliedTruncationToInput(true) + }, [ + input, + hasAppliedTruncationToInput, + pastedContents, + onInputChange, + setPastedContents, + setCursorOffset, + ]) + + // Reset hasInitializedInput when input is cleared (e.g., after submission) + useEffect(() => { + if (input === '') { + setHasAppliedTruncationToInput(false) + } + }, [input]) +} diff --git a/components/PromptInput/usePromptInputPlaceholder.ts b/components/PromptInput/usePromptInputPlaceholder.ts new file mode 100644 index 0000000..36d8d36 --- /dev/null +++ b/components/PromptInput/usePromptInputPlaceholder.ts @@ -0,0 +1,76 @@ +import { feature } from 'bun:bundle' +import { useMemo } from 'react' +import { useCommandQueue } from 'src/hooks/useCommandQueue.js' +import { useAppState } from 'src/state/AppState.js' +import { getGlobalConfig } from 'src/utils/config.js' +import { getExampleCommandFromCache } from 'src/utils/exampleCommands.js' +import { isQueuedCommandEditable } from 'src/utils/messageQueueManager.js' + +// Dead code elimination: conditional import for proactive mode +/* eslint-disable @typescript-eslint/no-require-imports */ +const proactiveModule = + feature('PROACTIVE') || feature('KAIROS') + ? require('../../proactive/index.js') + : null + +type Props = { + input: string + submitCount: number + viewingAgentName?: string +} + +const NUM_TIMES_QUEUE_HINT_SHOWN = 3 +const MAX_TEAMMATE_NAME_LENGTH = 20 + +export function usePromptInputPlaceholder({ + input, + submitCount, + viewingAgentName, +}: Props): string | undefined { + const queuedCommands = useCommandQueue() + const promptSuggestionEnabled = useAppState(s => s.promptSuggestionEnabled) + const placeholder = useMemo(() => { + if (input !== '') { + return + } + + // Show teammate hint when viewing teammate + if (viewingAgentName) { + const displayName = + viewingAgentName.length > MAX_TEAMMATE_NAME_LENGTH + ? viewingAgentName.slice(0, MAX_TEAMMATE_NAME_LENGTH - 3) + '...' + : viewingAgentName + return `Message @${displayName}…` + } + + // Show queue hint if user has not seen it yet. + // Only count user-editable commands — task-notification and isMeta + // are hidden from the prompt area (see PromptInputQueuedCommands). + if ( + queuedCommands.some(isQueuedCommandEditable) && + (getGlobalConfig().queuedCommandUpHintCount || 0) < + NUM_TIMES_QUEUE_HINT_SHOWN + ) { + return 'Press up to edit queued messages' + } + + // Show example command if user has not submitted yet and suggestions are enabled. + // Skip in proactive mode — the model drives the conversation so onboarding + // examples are irrelevant and block prompt suggestions from showing. + if ( + submitCount < 1 && + promptSuggestionEnabled && + !proactiveModule?.isProactiveActive() + ) { + return getExampleCommandFromCache() + } + }, [ + input, + queuedCommands, + submitCount, + promptSuggestionEnabled, + viewingAgentName, + ]) + + return placeholder +} diff --git a/components/PromptInput/useShowFastIconHint.ts b/components/PromptInput/useShowFastIconHint.ts new file mode 100644 index 0000000..3a49cd2 --- /dev/null +++ b/components/PromptInput/useShowFastIconHint.ts @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react' + +const HINT_DISPLAY_DURATION_MS = 5000 + +let hasShownThisSession = false + +/** + * Hook to manage the /fast hint display next to the fast icon. + * Shows the hint for 5 seconds once per session. + */ +export function useShowFastIconHint(showFastIcon: boolean): boolean { + const [showHint, setShowHint] = useState(false) + + useEffect(() => { + if (hasShownThisSession || !showFastIcon) { + return + } + + hasShownThisSession = true + setShowHint(true) + + const timer = setTimeout(setShowHint, HINT_DISPLAY_DURATION_MS, false) + + return () => { + clearTimeout(timer) + setShowHint(false) + } + }, [showFastIcon]) + + return showHint +} diff --git a/components/PromptInput/useSwarmBanner.ts b/components/PromptInput/useSwarmBanner.ts new file mode 100644 index 0000000..2ce6ba9 --- /dev/null +++ b/components/PromptInput/useSwarmBanner.ts @@ -0,0 +1,155 @@ +import * as React from 'react' +import { useAppState, useAppStateStore } from '../../state/AppState.js' +import { + getActiveAgentForInput, + getViewedTeammateTask, +} from '../../state/selectors.js' +import { + AGENT_COLOR_TO_THEME_COLOR, + AGENT_COLORS, + type AgentColorName, + getAgentColor, +} from '../../tools/AgentTool/agentColorManager.js' +import { getStandaloneAgentName } from '../../utils/standaloneAgent.js' +import { isInsideTmux } from '../../utils/swarm/backends/detection.js' +import { + getCachedDetectionResult, + isInProcessEnabled, +} from '../../utils/swarm/backends/registry.js' +import { getSwarmSocketName } from '../../utils/swarm/constants.js' +import { + getAgentName, + getTeammateColor, + getTeamName, + isTeammate, +} from '../../utils/teammate.js' +import { isInProcessTeammate } from '../../utils/teammateContext.js' +import type { Theme } from '../../utils/theme.js' + +type SwarmBannerInfo = { + text: string + bgColor: keyof Theme +} | null + +/** + * Hook that returns banner information for swarm, standalone agent, or --agent CLI context. + * - Leader (not in tmux): Returns "tmux -L ... attach" command with cyan background + * - Leader (in tmux / in-process): Falls through to standalone-agent check — shows + * /rename name + /color background if set, else null + * - Teammate: Returns "teammate@team" format with their assigned color background + * - Viewing a background agent (CoordinatorTaskPanel): Returns agent name with its color + * - Standalone agent: Returns agent name with their color background (no @team) + * - --agent CLI flag: Returns "@agentName" with cyan background + */ +export function useSwarmBanner(): SwarmBannerInfo { + const teamContext = useAppState(s => s.teamContext) + const standaloneAgentContext = useAppState(s => s.standaloneAgentContext) + const agent = useAppState(s => s.agent) + // Subscribe so the banner updates on enter/exit teammate view even though + // getActiveAgentForInput reads it from store.getState(). + useAppState(s => s.viewingAgentTaskId) + const store = useAppStateStore() + const [insideTmux, setInsideTmux] = React.useState(null) + + React.useEffect(() => { + void isInsideTmux().then(setInsideTmux) + }, []) + + const state = store.getState() + + // Teammate process: show @agentName with assigned color. + // In-process teammates run headless — their banner shows in the leader UI instead. + if (isTeammate() && !isInProcessTeammate()) { + const agentName = getAgentName() + if (agentName && getTeamName()) { + return { + text: `@${agentName}`, + bgColor: toThemeColor( + teamContext?.selfAgentColor ?? getTeammateColor(), + ), + } + } + } + + // Leader with spawned teammates: tmux-attach hint when external, else show + // the viewed teammate's name when inside tmux / native panes / in-process. + const hasTeammates = + teamContext?.teamName && + teamContext.teammates && + Object.keys(teamContext.teammates).length > 0 + if (hasTeammates) { + const viewedTeammate = getViewedTeammateTask(state) + const viewedColor = toThemeColor(viewedTeammate?.identity.color) + const inProcessMode = isInProcessEnabled() + const nativePanes = getCachedDetectionResult()?.isNative ?? false + + if (insideTmux === false && !inProcessMode && !nativePanes) { + return { + text: `View teammates: \`tmux -L ${getSwarmSocketName()} a\``, + bgColor: viewedColor, + } + } + if ( + (insideTmux === true || inProcessMode || nativePanes) && + viewedTeammate + ) { + return { + text: `@${viewedTeammate.identity.agentName}`, + bgColor: viewedColor, + } + } + // insideTmux === null: still loading — fall through. + // Not viewing a teammate: fall through so /rename and /color are honored. + } + + // Viewing a background agent (CoordinatorTaskPanel): local_agent tasks aren't + // InProcessTeammates, so getViewedTeammateTask misses them. Reverse-lookup the + // name from agentNameRegistry the same way CoordinatorAgentStatus does. + const active = getActiveAgentForInput(state) + if (active.type === 'named_agent') { + const task = active.task + let name: string | undefined + for (const [n, id] of state.agentNameRegistry) { + if (id === task.id) { + name = n + break + } + } + return { + text: name ? `@${name}` : task.description, + bgColor: getAgentColor(task.agentType) ?? 'cyan_FOR_SUBAGENTS_ONLY', + } + } + + // Standalone agent (/rename, /color): name and/or custom color, no @team. + const standaloneName = getStandaloneAgentName(state) + const standaloneColor = standaloneAgentContext?.color + if (standaloneName || standaloneColor) { + return { + text: standaloneName ?? '', + bgColor: toThemeColor(standaloneColor), + } + } + + // --agent CLI flag (when not handled above). + if (agent) { + const agentDef = state.agentDefinitions.activeAgents.find( + a => a.agentType === agent, + ) + return { + text: agent, + bgColor: toThemeColor(agentDef?.color, 'promptBorder'), + } + } + + return null +} + +function toThemeColor( + colorName: string | undefined, + fallback: keyof Theme = 'cyan_FOR_SUBAGENTS_ONLY', +): keyof Theme { + return colorName && AGENT_COLORS.includes(colorName as AgentColorName) + ? AGENT_COLOR_TO_THEME_COLOR[colorName as AgentColorName] + : fallback +} diff --git a/components/PromptInput/utils.ts b/components/PromptInput/utils.ts new file mode 100644 index 0000000..eb5cc81 --- /dev/null +++ b/components/PromptInput/utils.ts @@ -0,0 +1,60 @@ +import { + hasUsedBackslashReturn, + isShiftEnterKeyBindingInstalled, +} from '../../commands/terminalSetup/terminalSetup.js' +import type { Key } from '../../ink.js' +import { getGlobalConfig } from '../../utils/config.js' +import { env } from '../../utils/env.js' +/** + * Helper function to check if vim mode is currently enabled + * @returns boolean indicating if vim mode is active + */ +export function isVimModeEnabled(): boolean { + const config = getGlobalConfig() + return config.editorMode === 'vim' +} + +export function getNewlineInstructions(): string { + // Apple Terminal on macOS uses native modifier key detection for Shift+Enter + if (env.terminal === 'Apple_Terminal' && process.platform === 'darwin') { + return 'shift + ⏎ for newline' + } + + // For iTerm2 and VSCode, show Shift+Enter instructions if installed + if (isShiftEnterKeyBindingInstalled()) { + return 'shift + ⏎ for newline' + } + + // Otherwise show backslash+return instructions + return hasUsedBackslashReturn() + ? '\\⏎ for newline' + : 'backslash (\\) + return (⏎) for newline' +} + +/** + * True when the keystroke is a printable character that does not begin + * with whitespace — i.e., a normal letter/digit/symbol the user typed. + * Used to gate the lazy space inserted after an image pill. + */ +export function isNonSpacePrintable(input: string, key: Key): boolean { + if ( + key.ctrl || + key.meta || + key.escape || + key.return || + key.tab || + key.backspace || + key.delete || + key.upArrow || + key.downArrow || + key.leftArrow || + key.rightArrow || + key.pageUp || + key.pageDown || + key.home || + key.end + ) { + return false + } + return input.length > 0 && !/^\s/.test(input) && !input.startsWith('\x1b') +} diff --git a/components/QuickOpenDialog.tsx b/components/QuickOpenDialog.tsx new file mode 100644 index 0000000..23c12af --- /dev/null +++ b/components/QuickOpenDialog.tsx @@ -0,0 +1,244 @@ +import { c as _c } from "react/compiler-runtime"; +import * as path from 'path'; +import * as React from 'react'; +import { useEffect, useRef, useState } from 'react'; +import { useRegisterOverlay } from '../context/overlayContext.js'; +import { generateFileSuggestions } from '../hooks/fileSuggestions.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { Text } from '../ink.js'; +import { logEvent } from '../services/analytics/index.js'; +import { getCwd } from '../utils/cwd.js'; +import { openFileInExternalEditor } from '../utils/editor.js'; +import { truncatePathMiddle, truncateToWidth } from '../utils/format.js'; +import { highlightMatch } from '../utils/highlightMatch.js'; +import { readFileInRange } from '../utils/readFileInRange.js'; +import { FuzzyPicker } from './design-system/FuzzyPicker.js'; +import { LoadingState } from './design-system/LoadingState.js'; +type Props = { + onDone: () => void; + onInsert: (text: string) => void; +}; +const VISIBLE_RESULTS = 8; +const PREVIEW_LINES = 20; + +/** + * Quick Open dialog (ctrl+shift+p / cmd+shift+p). + * Fuzzy file finder with a syntax-highlighted preview of the focused file. + */ +export function QuickOpenDialog(t0) { + const $ = _c(35); + const { + onDone, + onInsert + } = t0; + useRegisterOverlay("quick-open"); + const { + columns, + rows + } = useTerminalSize(); + const visibleResults = Math.min(VISIBLE_RESULTS, Math.max(4, rows - 14)); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = []; + $[0] = t1; + } else { + t1 = $[0]; + } + const [results, setResults] = useState(t1); + const [query, setQuery] = useState(""); + const [focusedPath, setFocusedPath] = useState(undefined); + const [preview, setPreview] = useState(null); + const queryGenRef = useRef(0); + let t2; + let t3; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => () => { + queryGenRef.current = queryGenRef.current + 1; + return void queryGenRef.current; + }; + t3 = []; + $[1] = t2; + $[2] = t3; + } else { + t2 = $[1]; + t3 = $[2]; + } + useEffect(t2, t3); + const previewOnRight = columns >= 120; + const effectivePreviewLines = previewOnRight ? VISIBLE_RESULTS - 1 : PREVIEW_LINES; + let t4; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t4 = q => { + setQuery(q); + const gen = queryGenRef.current = queryGenRef.current + 1; + if (!q.trim()) { + setResults([]); + return; + } + generateFileSuggestions(q, true).then(items => { + if (gen !== queryGenRef.current) { + return; + } + const paths = items.filter(_temp).map(_temp2).filter(_temp3).map(_temp4); + setResults(paths); + }); + }; + $[3] = t4; + } else { + t4 = $[3]; + } + const handleQueryChange = t4; + let t5; + let t6; + if ($[4] !== effectivePreviewLines || $[5] !== focusedPath) { + t5 = () => { + if (!focusedPath) { + setPreview(null); + return; + } + const controller = new AbortController(); + const absolute = path.resolve(getCwd(), focusedPath); + readFileInRange(absolute, 0, effectivePreviewLines, undefined, controller.signal).then(r => { + if (controller.signal.aborted) { + return; + } + setPreview({ + path: focusedPath, + content: r.content + }); + }).catch(() => { + if (controller.signal.aborted) { + return; + } + setPreview({ + path: focusedPath, + content: "(preview unavailable)" + }); + }); + return () => controller.abort(); + }; + t6 = [focusedPath, effectivePreviewLines]; + $[4] = effectivePreviewLines; + $[5] = focusedPath; + $[6] = t5; + $[7] = t6; + } else { + t5 = $[6]; + t6 = $[7]; + } + useEffect(t5, t6); + const maxPathWidth = previewOnRight ? Math.max(20, Math.floor((columns - 10) * 0.4)) : Math.max(20, columns - 8); + const previewWidth = previewOnRight ? Math.max(40, columns - maxPathWidth - 14) : columns - 6; + let t7; + if ($[8] !== onDone || $[9] !== results.length) { + t7 = p_1 => { + const opened = openFileInExternalEditor(path.resolve(getCwd(), p_1)); + logEvent("tengu_quick_open_select", { + result_count: results.length, + opened_editor: opened + }); + onDone(); + }; + $[8] = onDone; + $[9] = results.length; + $[10] = t7; + } else { + t7 = $[10]; + } + const handleOpen = t7; + let t8; + if ($[11] !== onDone || $[12] !== onInsert || $[13] !== results.length) { + t8 = (p_2, mention) => { + onInsert(mention ? `@${p_2} ` : `${p_2} `); + logEvent("tengu_quick_open_insert", { + result_count: results.length, + mention + }); + onDone(); + }; + $[11] = onDone; + $[12] = onInsert; + $[13] = results.length; + $[14] = t8; + } else { + t8 = $[14]; + } + const handleInsert = t8; + const t9 = previewOnRight ? "right" : "bottom"; + let t10; + if ($[15] !== handleInsert) { + t10 = { + action: "mention", + handler: p_4 => handleInsert(p_4, true) + }; + $[15] = handleInsert; + $[16] = t10; + } else { + t10 = $[16]; + } + let t11; + if ($[17] !== handleInsert) { + t11 = { + action: "insert path", + handler: p_5 => handleInsert(p_5, false) + }; + $[17] = handleInsert; + $[18] = t11; + } else { + t11 = $[18]; + } + let t12; + if ($[19] !== maxPathWidth) { + t12 = (p_6, isFocused) => {truncatePathMiddle(p_6, maxPathWidth)}; + $[19] = maxPathWidth; + $[20] = t12; + } else { + t12 = $[20]; + } + let t13; + if ($[21] !== preview || $[22] !== previewWidth || $[23] !== query) { + t13 = p_7 => preview ? <>{truncatePathMiddle(p_7, previewWidth)}{preview.path !== p_7 ? " \xB7 loading\u2026" : ""}{preview.content.split("\n").map((line, i_1) => {highlightMatch(truncateToWidth(line, previewWidth), query)})} : ; + $[21] = preview; + $[22] = previewWidth; + $[23] = query; + $[24] = t13; + } else { + t13 = $[24]; + } + let t14; + if ($[25] !== handleOpen || $[26] !== onDone || $[27] !== results || $[28] !== t10 || $[29] !== t11 || $[30] !== t12 || $[31] !== t13 || $[32] !== t9 || $[33] !== visibleResults) { + t14 = ; + $[25] = handleOpen; + $[26] = onDone; + $[27] = results; + $[28] = t10; + $[29] = t11; + $[30] = t12; + $[31] = t13; + $[32] = t9; + $[33] = visibleResults; + $[34] = t14; + } else { + t14 = $[34]; + } + return t14; +} +function _temp6(q_0) { + return q_0 ? "No matching files" : "Start typing to search\u2026"; +} +function _temp5(p_3) { + return p_3; +} +function _temp4(p_0) { + return p_0.split(path.sep).join("/"); +} +function _temp3(p) { + return !p.endsWith(path.sep); +} +function _temp2(i_0) { + return i_0.displayText; +} +function _temp(i) { + return i.id.startsWith("file-"); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJwYXRoIiwiUmVhY3QiLCJ1c2VFZmZlY3QiLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsInVzZVJlZ2lzdGVyT3ZlcmxheSIsImdlbmVyYXRlRmlsZVN1Z2dlc3Rpb25zIiwidXNlVGVybWluYWxTaXplIiwiVGV4dCIsImxvZ0V2ZW50IiwiZ2V0Q3dkIiwib3BlbkZpbGVJbkV4dGVybmFsRWRpdG9yIiwidHJ1bmNhdGVQYXRoTWlkZGxlIiwidHJ1bmNhdGVUb1dpZHRoIiwiaGlnaGxpZ2h0TWF0Y2giLCJyZWFkRmlsZUluUmFuZ2UiLCJGdXp6eVBpY2tlciIsIkxvYWRpbmdTdGF0ZSIsIlByb3BzIiwib25Eb25lIiwib25JbnNlcnQiLCJ0ZXh0IiwiVklTSUJMRV9SRVNVTFRTIiwiUFJFVklFV19MSU5FUyIsIlF1aWNrT3BlbkRpYWxvZyIsInQwIiwiJCIsIl9jIiwiY29sdW1ucyIsInJvd3MiLCJ2aXNpYmxlUmVzdWx0cyIsIk1hdGgiLCJtaW4iLCJtYXgiLCJ0MSIsIlN5bWJvbCIsImZvciIsInJlc3VsdHMiLCJzZXRSZXN1bHRzIiwicXVlcnkiLCJzZXRRdWVyeSIsImZvY3VzZWRQYXRoIiwic2V0Rm9jdXNlZFBhdGgiLCJ1bmRlZmluZWQiLCJwcmV2aWV3Iiwic2V0UHJldmlldyIsInF1ZXJ5R2VuUmVmIiwidDIiLCJ0MyIsImN1cnJlbnQiLCJwcmV2aWV3T25SaWdodCIsImVmZmVjdGl2ZVByZXZpZXdMaW5lcyIsInQ0IiwicSIsImdlbiIsInRyaW0iLCJ0aGVuIiwiaXRlbXMiLCJwYXRocyIsImZpbHRlciIsIl90ZW1wIiwibWFwIiwiX3RlbXAyIiwiX3RlbXAzIiwiX3RlbXA0IiwiaGFuZGxlUXVlcnlDaGFuZ2UiLCJ0NSIsInQ2IiwiY29udHJvbGxlciIsIkFib3J0Q29udHJvbGxlciIsImFic29sdXRlIiwicmVzb2x2ZSIsInNpZ25hbCIsInIiLCJhYm9ydGVkIiwiY29udGVudCIsImNhdGNoIiwiYWJvcnQiLCJtYXhQYXRoV2lkdGgiLCJmbG9vciIsInByZXZpZXdXaWR0aCIsInQ3IiwibGVuZ3RoIiwicF8xIiwib3BlbmVkIiwicCIsInJlc3VsdF9jb3VudCIsIm9wZW5lZF9lZGl0b3IiLCJoYW5kbGVPcGVuIiwidDgiLCJwXzIiLCJtZW50aW9uIiwiaGFuZGxlSW5zZXJ0IiwidDkiLCJ0MTAiLCJhY3Rpb24iLCJoYW5kbGVyIiwicF80IiwidDExIiwicF81IiwidDEyIiwicF82IiwiaXNGb2N1c2VkIiwidDEzIiwicF83Iiwic3BsaXQiLCJsaW5lIiwiaV8xIiwiaSIsInQxNCIsIl90ZW1wNSIsIl90ZW1wNiIsInFfMCIsInBfMyIsInBfMCIsInNlcCIsImpvaW4iLCJlbmRzV2l0aCIsImlfMCIsImRpc3BsYXlUZXh0IiwiaWQiLCJzdGFydHNXaXRoIl0sInNvdXJjZXMiOlsiUXVpY2tPcGVuRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlUmVnaXN0ZXJPdmVybGF5IH0gZnJvbSAnLi4vY29udGV4dC9vdmVybGF5Q29udGV4dC5qcydcbmltcG9ydCB7IGdlbmVyYXRlRmlsZVN1Z2dlc3Rpb25zIH0gZnJvbSAnLi4vaG9va3MvZmlsZVN1Z2dlc3Rpb25zLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgZ2V0Q3dkIH0gZnJvbSAnLi4vdXRpbHMvY3dkLmpzJ1xuaW1wb3J0IHsgb3BlbkZpbGVJbkV4dGVybmFsRWRpdG9yIH0gZnJvbSAnLi4vdXRpbHMvZWRpdG9yLmpzJ1xuaW1wb3J0IHsgdHJ1bmNhdGVQYXRoTWlkZGxlLCB0cnVuY2F0ZVRvV2lkdGggfSBmcm9tICcuLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQgeyBoaWdobGlnaHRNYXRjaCB9IGZyb20gJy4uL3V0aWxzL2hpZ2hsaWdodE1hdGNoLmpzJ1xuaW1wb3J0IHsgcmVhZEZpbGVJblJhbmdlIH0gZnJvbSAnLi4vdXRpbHMvcmVhZEZpbGVJblJhbmdlLmpzJ1xuaW1wb3J0IHsgRnV6enlQaWNrZXIgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vRnV6enlQaWNrZXIuanMnXG5pbXBvcnQgeyBMb2FkaW5nU3RhdGUgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vTG9hZGluZ1N0YXRlLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBvbkRvbmU6ICgpID0+IHZvaWRcbiAgb25JbnNlcnQ6ICh0ZXh0OiBzdHJpbmcpID0+IHZvaWRcbn1cblxuY29uc3QgVklTSUJMRV9SRVNVTFRTID0gOFxuY29uc3QgUFJFVklFV19MSU5FUyA9IDIwXG5cbi8qKlxuICogUXVpY2sgT3BlbiBkaWFsb2cgKGN0cmwrc2hpZnQrcCAvIGNtZCtzaGlmdCtwKS5cbiAqIEZ1enp5IGZpbGUgZmluZGVyIHdpdGggYSBzeW50YXgtaGlnaGxpZ2h0ZWQgcHJldmlldyBvZiB0aGUgZm9jdXNlZCBmaWxlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gUXVpY2tPcGVuRGlhbG9nKHsgb25Eb25lLCBvbkluc2VydCB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHVzZVJlZ2lzdGVyT3ZlcmxheSgncXVpY2stb3BlbicpXG4gIGNvbnN0IHsgY29sdW1ucywgcm93cyB9ID0gdXNlVGVybWluYWxTaXplKClcbiAgLy8gQ2hyb21lICh0aXRsZSArIHNlYXJjaCArIGhpbnRzICsgcGFuZSBib3JkZXIgKyBnYXBzKSBlYXRzIH4xNCByb3dzLlxuICAvLyBTaHJpbmsgdGhlIGxpc3Qgb24gc2hvcnQgdGVybWluYWxzIHNvIHRoZSBkaWFsb2cgZG9lc24ndCBjbGlwLlxuICBjb25zdCB2aXNpYmxlUmVzdWx0cyA9IE1hdGgubWluKFZJU0lCTEVfUkVTVUxUUywgTWF0aC5tYXgoNCwgcm93cyAtIDE0KSlcblxuICBjb25zdCBbcmVzdWx0cywgc2V0UmVzdWx0c10gPSB1c2VTdGF0ZTxzdHJpbmdbXT4oW10pXG4gIGNvbnN0IFtxdWVyeSwgc2V0UXVlcnldID0gdXNlU3RhdGUoJycpXG4gIGNvbnN0IFtmb2N1c2VkUGF0aCwgc2V0Rm9jdXNlZFBhdGhdID0gdXNlU3RhdGU8c3RyaW5nIHwgdW5kZWZpbmVkPih1bmRlZmluZWQpXG4gIGNvbnN0IFtwcmV2aWV3LCBzZXRQcmV2aWV3XSA9IHVzZVN0YXRlPHtcbiAgICBwYXRoOiBzdHJpbmdcbiAgICBjb250ZW50OiBzdHJpbmdcbiAgfSB8IG51bGw+KG51bGwpXG4gIGNvbnN0IHF1ZXJ5R2VuUmVmID0gdXNlUmVmKDApXG4gIHVzZUVmZmVjdCgoKSA9PiAoKSA9PiB2b2lkIHF1ZXJ5R2VuUmVmLmN1cnJlbnQrKywgW10pXG5cbiAgY29uc3QgcHJldmlld09uUmlnaHQgPSBjb2x1bW5zID49IDEyMFxuICAvLyBTaWRlIHByZXZpZXcgc2l0cyBpbiBhIGZpeGVkLWhlaWdodCByb3cgYWxvbmdzaWRlIHRoZSBsaXN0ICh2aXNpYmxlQ291bnRcbiAgLy8gcm93cyksIHNvIG92ZXJmbG93aW5nIHRoYXQgaGVpZ2h0IGdhcmJsZXMgdGhlIGxheW91dCDigJQgY2FwIHRvIGZpdCwgbWludXNcbiAgLy8gb25lIGZvciB0aGUgcGF0aCBoZWFkZXIgbGluZS5cbiAgY29uc3QgZWZmZWN0aXZlUHJldmlld0xpbmVzID0gcHJldmlld09uUmlnaHRcbiAgICA/IFZJU0lCTEVfUkVTVUxUUyAtIDFcbiAgICA6IFBSRVZJRVdfTElORVNcblxuICAvLyBBIGdlbmVyYXRpb24gY291bnRlciBpbnZhbGlkYXRlcyBzdGFsZSByZXN1bHRzIGlmIHRoZSB1c2VyIHR5cGVzIGZhc3RlclxuICAvLyB0aGFuIHRoZSBpbmRleCBjYW4gcmVzcG9uZC5cbiAgY29uc3QgaGFuZGxlUXVlcnlDaGFuZ2UgPSAocTogc3RyaW5nKSA9PiB7XG4gICAgc2V0UXVlcnkocSlcbiAgICBjb25zdCBnZW4gPSArK3F1ZXJ5R2VuUmVmLmN1cnJlbnRcbiAgICBpZiAoIXEudHJpbSgpKSB7XG4gICAgICAvLyBnZW5lcmF0ZUZpbGVTdWdnZXN0aW9ucygnJykgcmV0dXJucyByYXcgcmVhZGRpcigpIG9mIGN3ZCAoZGVzaWduZWQgZm9yXG4gICAgICAvLyBALW1lbnRpb25zKS4gRm9yIFF1aWNrIE9wZW4gdGhhdCdzIGp1c3Qgbm9pc2Ug4oCUIHNob3cgdGhlIGVtcHR5IHN0YXRlLlxuICAgICAgc2V0UmVzdWx0cyhbXSlcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICB2b2lkIGdlbmVyYXRlRmlsZVN1Z2dlc3Rpb25zKHEsIHRydWUpLnRoZW4oaXRlbXMgPT4ge1xuICAgICAgaWYgKGdlbiAhPT0gcXVlcnlHZW5SZWYuY3VycmVudCkgcmV0dXJuXG4gICAgICAvLyBGaWx0ZXIgb3V0IGRpcmVjdG9yeSBlbnRyaWVzIOKAlCB0aGV5IGNvbWUgYmFjayB3aXRoIGEgdHJhaWxpbmcgcGF0aC5zZXBcbiAgICAgIC8vIGZyb20gZ2V0VG9wTGV2ZWxQYXRocygpIGFuZCB3b3VsZCBjYXVzZSByZWFkRmlsZUluUmFuZ2UgdG8gdGhyb3cgRUlTRElSLFxuICAgICAgLy8gbGVhdmluZyB0aGUgcHJldmlldyBwYW5lIHN0dWNrIG9uIFwiTG9hZGluZyBwcmV2aWV34oCmXCIuXG4gICAgICAvLyBOb3JtYWxpemUgc2VwYXJhdG9ycyB0byAnLycgc28gdHJ1bmNhdGVQYXRoTWlkZGxlICh3aGljaCB1c2VzXG4gICAgICAvLyBsYXN0SW5kZXhPZignLycpKSBjYW4gZmluZCB0aGUgZmlsZW5hbWUgb24gV2luZG93cyB0b28uXG4gICAgICBjb25zdCBwYXRocyA9IGl0ZW1zXG4gICAgICAgIC5maWx0ZXIoaSA9PiBpLmlkLnN0YXJ0c1dpdGgoJ2ZpbGUtJykpXG4gICAgICAgIC5tYXAoaSA9PiBpLmRpc3BsYXlUZXh0KVxuICAgICAgICAuZmlsdGVyKHAgPT4gIXAuZW5kc1dpdGgocGF0aC5zZXApKVxuICAgICAgICAubWFwKHAgPT4gcC5zcGxpdChwYXRoLnNlcCkuam9pbignLycpKVxuICAgICAgc2V0UmVzdWx0cyhwYXRocylcbiAgICB9KVxuICB9XG5cbiAgLy8gTG9hZCBhIHNob3J0IHByZXZpZXcgb2YgdGhlIGZvY3VzZWQgZmlsZS4gRWFjaCBuYXZpZ2F0aW9uIGFib3J0cyB0aGVcbiAgLy8gcHJldmlvdXMgcmVhZCBzbyBob2xkaW5nIOKGkyBkb2Vzbid0IHBpbGUgdXAgd2hvbGUtZmlsZSByZWFkcyBhbmQgc28gYVxuICAvLyBzbG93IGVhcmx5IHJlYWQgY2FuJ3Qgb3ZlcndyaXRlIGEgZmFzdGVyIGxhdGVyIG9uZS4gVGhlIHN0YWxlIHByZXZpZXdcbiAgLy8gc3RheXMgdmlzaWJsZSB1bnRpbCB0aGUgbmV3IG9uZSBhcnJpdmVzIOKAlCByZW5kZXJQcmV2aWV3IG92ZXJsYXlzIGEgZGltXG4gIC8vIGxvYWRpbmcgaW5kaWNhdG9yIHJhdGhlciB0aGFuIGJsYW5raW5nIHRoZSBwYW5lLlxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghZm9jdXNlZFBhdGgpIHtcbiAgICAgIC8vIE5vIHJlc3VsdHMg4oCUIGNsZWFyIHNvIHRoZSBlbXB0eS1zdGF0ZSByZW5kZXJzIGluc3RlYWQgb2YgYSBzdGFsZVxuICAgICAgLy8gcHJldmlldyBmcm9tIGEgcHJldmlvdXMgcXVlcnkuXG4gICAgICBzZXRQcmV2aWV3KG51bGwpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKVxuICAgIGNvbnN0IGFic29sdXRlID0gcGF0aC5yZXNvbHZlKGdldEN3ZCgpLCBmb2N1c2VkUGF0aClcbiAgICB2b2lkIHJlYWRGaWxlSW5SYW5nZShcbiAgICAgIGFic29sdXRlLFxuICAgICAgMCxcbiAgICAgIGVmZmVjdGl2ZVByZXZpZXdMaW5lcyxcbiAgICAgIHVuZGVmaW5lZCxcbiAgICAgIGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgIClcbiAgICAgIC50aGVuKHIgPT4ge1xuICAgICAgICBpZiAoY29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkgcmV0dXJuXG4gICAgICAgIHNldFByZXZpZXcoeyBwYXRoOiBmb2N1c2VkUGF0aCwgY29udGVudDogci5jb250ZW50IH0pXG4gICAgICB9KVxuICAgICAgLmNhdGNoKCgpID0+IHtcbiAgICAgICAgaWYgKGNvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHJldHVyblxuICAgICAgICBzZXRQcmV2aWV3KHsgcGF0aDogZm9jdXNlZFBhdGgsIGNvbnRlbnQ6ICcocHJldmlldyB1bmF2YWlsYWJsZSknIH0pXG4gICAgICB9KVxuICAgIHJldHVybiAoKSA9PiBjb250cm9sbGVyLmFib3J0KClcbiAgfSwgW2ZvY3VzZWRQYXRoLCBlZmZlY3RpdmVQcmV2aWV3TGluZXNdKVxuXG4gIGNvbnN0IG1heFBhdGhXaWR0aCA9IHByZXZpZXdPblJpZ2h0XG4gICAgPyBNYXRoLm1heCgyMCwgTWF0aC5mbG9vcigoY29sdW1ucyAtIDEwKSAqIDAuNCkpXG4gICAgOiBNYXRoLm1heCgyMCwgY29sdW1ucyAtIDgpXG4gIGNvbnN0IHByZXZpZXdXaWR0aCA9IHByZXZpZXdPblJpZ2h0XG4gICAgPyBNYXRoLm1heCg0MCwgY29sdW1ucyAtIG1heFBhdGhXaWR0aCAtIDE0KVxuICAgIDogY29sdW1ucyAtIDZcblxuICBjb25zdCBoYW5kbGVPcGVuID0gKHA6IHN0cmluZykgPT4ge1xuICAgIGNvbnN0IG9wZW5lZCA9IG9wZW5GaWxlSW5FeHRlcm5hbEVkaXRvcihwYXRoLnJlc29sdmUoZ2V0Q3dkKCksIHApKVxuICAgIGxvZ0V2ZW50KCd0ZW5ndV9xdWlja19vcGVuX3NlbGVjdCcsIHtcbiAgICAgIHJlc3VsdF9jb3VudDogcmVzdWx0cy5sZW5ndGgsXG4gICAgICBvcGVuZWRfZWRpdG9yOiBvcGVuZWQsXG4gICAgfSlcbiAgICBvbkRvbmUoKVxuICB9XG5cbiAgY29uc3QgaGFuZGxlSW5zZXJ0ID0gKHA6IHN0cmluZywgbWVudGlvbjogYm9vbGVhbikgPT4ge1xuICAgIG9uSW5zZXJ0KG1lbnRpb24gPyBgQCR7cH0gYCA6IGAke3B9IGApXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X3F1aWNrX29wZW5faW5zZXJ0Jywge1xuICAgICAgcmVzdWx0X2NvdW50OiByZXN1bHRzLmxlbmd0aCxcbiAgICAgIG1lbnRpb24sXG4gICAgfSlcbiAgICBvbkRvbmUoKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8RnV6enlQaWNrZXJcbiAgICAgIHRpdGxlPVwiUXVpY2sgT3BlblwiXG4gICAgICBwbGFjZWhvbGRlcj1cIlR5cGUgdG8gc2VhcmNoIGZpbGVz4oCmXCJcbiAgICAgIGl0ZW1zPXtyZXN1bHRzfVxuICAgICAgZ2V0S2V5PXtwID0+IHB9XG4gICAgICB2aXNpYmxlQ291bnQ9e3Zpc2libGVSZXN1bHRzfVxuICAgICAgZGlyZWN0aW9uPVwidXBcIlxuICAgICAgcHJldmlld1Bvc2l0aW9uPXtwcmV2aWV3T25SaWdodCA/ICdyaWdodCcgOiAnYm90dG9tJ31cbiAgICAgIG9uUXVlcnlDaGFuZ2U9e2hhbmRsZVF1ZXJ5Q2hhbmdlfVxuICAgICAgb25Gb2N1cz17c2V0Rm9jdXNlZFBhdGh9XG4gICAgICBvblNlbGVjdD17aGFuZGxlT3Blbn1cbiAgICAgIG9uVGFiPXt7IGFjdGlvbjogJ21lbnRpb24nLCBoYW5kbGVyOiBwID0+IGhhbmRsZUluc2VydChwLCB0cnVlKSB9fVxuICAgICAgb25TaGlmdFRhYj17e1xuICAgICAgICBhY3Rpb246ICdpbnNlcnQgcGF0aCcsXG4gICAgICAgIGhhbmRsZXI6IHAgPT4gaGFuZGxlSW5zZXJ0KHAsIGZhbHNlKSxcbiAgICAgIH19XG4gICAgICBvbkNhbmNlbD17b25Eb25lfVxuICAgICAgZW1wdHlNZXNzYWdlPXtxID0+IChxID8gJ05vIG1hdGNoaW5nIGZpbGVzJyA6ICdTdGFydCB0eXBpbmcgdG8gc2VhcmNo4oCmJyl9XG4gICAgICBzZWxlY3RBY3Rpb249XCJvcGVuIGluIGVkaXRvclwiXG4gICAgICByZW5kZXJJdGVtPXsocCwgaXNGb2N1c2VkKSA9PiAoXG4gICAgICAgIDxUZXh0IGNvbG9yPXtpc0ZvY3VzZWQgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9PlxuICAgICAgICAgIHt0cnVuY2F0ZVBhdGhNaWRkbGUocCwgbWF4UGF0aFdpZHRoKX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKX1cbiAgICAgIHJlbmRlclByZXZpZXc9e3AgPT5cbiAgICAgICAgcHJldmlldyA/IChcbiAgICAgICAgICA8PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIHt0cnVuY2F0ZVBhdGhNaWRkbGUocCwgcHJldmlld1dpZHRoKX1cbiAgICAgICAgICAgICAge3ByZXZpZXcucGF0aCAhPT0gcCA/ICcgwrcgbG9hZGluZ+KApicgOiAnJ31cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIHtwcmV2aWV3LmNvbnRlbnQuc3BsaXQoJ1xcbicpLm1hcCgobGluZSwgaSkgPT4gKFxuICAgICAgICAgICAgICA8VGV4dCBrZXk9e2l9PlxuICAgICAgICAgICAgICAgIHtoaWdobGlnaHRNYXRjaCh0cnVuY2F0ZVRvV2lkdGgobGluZSwgcHJldmlld1dpZHRoKSwgcXVlcnkpfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICApKX1cbiAgICAgICAgICA8Lz5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8TG9hZGluZ1N0YXRlIG1lc3NhZ2U9XCJMb2FkaW5nIHByZXZpZXfigKZcIiBkaW1Db2xvciAvPlxuICAgICAgICApXG4gICAgICB9XG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxJQUFJLE1BQU0sTUFBTTtBQUM1QixPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNB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 \ No newline at end of file diff --git a/components/RemoteCallout.tsx b/components/RemoteCallout.tsx new file mode 100644 index 0000000..4710b1d --- /dev/null +++ b/components/RemoteCallout.tsx @@ -0,0 +1,76 @@ +import React, { useCallback, useEffect, useRef } from 'react'; +import { isBridgeEnabled } from '../bridge/bridgeEnabled.js'; +import { Box, Text } from '../ink.js'; +import { getClaudeAIOAuthTokens } from '../utils/auth.js'; +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js'; +import type { OptionWithDescription } from './CustomSelect/select.js'; +import { Select } from './CustomSelect/select.js'; +import { PermissionDialog } from './permissions/PermissionDialog.js'; +type RemoteCalloutSelection = 'enable' | 'dismiss'; +type Props = { + onDone: (selection: RemoteCalloutSelection) => void; +}; +export function RemoteCallout({ + onDone +}: Props): React.ReactNode { + const onDoneRef = useRef(onDone); + onDoneRef.current = onDone; + const handleCancel = useCallback((): void => { + onDoneRef.current('dismiss'); + }, []); + + // Permanently mark as seen on mount so it only shows once + useEffect(() => { + saveGlobalConfig(current => { + if (current.remoteDialogSeen) return current; + return { + ...current, + remoteDialogSeen: true + }; + }); + }, []); + const handleSelect = useCallback((value: RemoteCalloutSelection): void => { + onDoneRef.current(value); + }, []); + const options: OptionWithDescription[] = [{ + label: 'Enable Remote Control for this session', + description: 'Opens a secure connection to claude.ai.', + value: 'enable' + }, { + label: 'Never mind', + description: 'You can always enable it later with /remote-control.', + value: 'dismiss' + }]; + return + + + + Remote Control lets you access this CLI session from the web + (claude.ai/code) or the Claude app, so you can pick up where you + left off on any device. + + + + You can disconnect remote access anytime by running /remote-control + again. + + + + onSelect("cancel")} layout="compact-vertical" />; + $[8] = environments; + $[9] = loadingState; + $[10] = onSelect; + $[11] = selectedEnvironment.environment_id; + $[12] = t5; + } else { + t5 = $[12]; + } + let t6; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t6 = ; + $[13] = t6; + } else { + t6 = $[13]; + } + let t7; + if ($[14] !== onCancel || $[15] !== subtitle || $[16] !== t5) { + t7 = {t4}{t5}{t6}; + $[14] = onCancel; + $[15] = subtitle; + $[16] = t5; + $[17] = t7; + } else { + t7 = $[17]; + } + return t7; +} +function _temp(env) { + return { + label: {env.name} ({env.environment_id}), + value: env.environment_id + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjaGFsayIsImZpZ3VyZXMiLCJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiVGV4dCIsInVzZUtleWJpbmRpbmciLCJ0b0Vycm9yIiwibG9nRXJyb3IiLCJnZXRTZXR0aW5nU291cmNlTmFtZSIsIlNldHRpbmdTb3VyY2UiLCJ1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSIsImdldEVudmlyb25tZW50U2VsZWN0aW9uSW5mbyIsIkVudmlyb25tZW50UmVzb3VyY2UiLCJDb25maWd1cmFibGVTaG9ydGN1dEhpbnQiLCJTZWxlY3QiLCJCeWxpbmUiLCJEaWFsb2ciLCJLZXlib2FyZFNob3J0Y3V0SGludCIsIkxvYWRpbmdTdGF0ZSIsIkRJQUxPR19USVRMRSIsIlNFVFVQX0hJTlQiLCJQcm9wcyIsIm9uRG9uZSIsIm1lc3NhZ2UiLCJSZW1vdGVFbnZpcm9ubWVudERpYWxvZyIsInQwIiwiJCIsIl9jIiwibG9hZGluZ1N0YXRlIiwic2V0TG9hZGluZ1N0YXRlIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJlbnZpcm9ubWVudHMiLCJzZXRFbnZpcm9ubWVudHMiLCJzZWxlY3RlZEVudmlyb25tZW50Iiwic2V0U2VsZWN0ZWRFbnZpcm9ubWVudCIsInNlbGVjdGVkRW52aXJvbm1lbnRTb3VyY2UiLCJzZXRTZWxlY3RlZEVudmlyb25tZW50U291cmNlIiwiZXJyb3IiLCJzZXRFcnJvciIsInQyIiwidDMiLCJjYW5jZWxsZWQiLCJmZXRjaEluZm8iLCJyZXN1bHQiLCJhdmFpbGFibGVFbnZpcm9ubWVudHMiLCJ0NCIsImVyciIsImZldGNoRXJyb3IiLCJoYW5kbGVTZWxlY3QiLCJ2YWx1ZSIsInNlbGVjdGVkRW52IiwiZmluZCIsImVudiIsImVudmlyb25tZW50X2lkIiwicmVtb3RlIiwiZGVmYXVsdEVudmlyb25tZW50SWQiLCJib2xkIiwibmFtZSIsInQ1IiwidDYiLCJsZW5ndGgiLCJFbnZpcm9ubWVudExhYmVsIiwiZW52aXJvbm1lbnQiLCJ0aWNrIiwiU2luZ2xlRW52aXJvbm1lbnRDb250ZW50IiwiY29udGV4dCIsIk11bHRpcGxlRW52aXJvbm1lbnRzQ29udGVudCIsIm9uU2VsZWN0Iiwib25DYW5jZWwiLCJzb3VyY2VTdWZmaXgiLCJzdWJ0aXRsZSIsIm1hcCIsIl90ZW1wIiwidDciLCJsYWJlbCJdLCJzb3VyY2VzIjpbIlJlbW90ZUVudmlyb25tZW50RGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgY2hhbGsgZnJvbSAnY2hhbGsnXG5pbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyB0b0Vycm9yIH0gZnJvbSAnLi4vdXRpbHMvZXJyb3JzLmpzJ1xuaW1wb3J0IHsgbG9nRXJyb3IgfSBmcm9tICcuLi91dGlscy9sb2cuanMnXG5pbXBvcnQge1xuICBnZXRTZXR0aW5nU291cmNlTmFtZSxcbiAgdHlwZSBTZXR0aW5nU291cmNlLFxufSBmcm9tICcuLi91dGlscy9zZXR0aW5ncy9jb25zdGFudHMuanMnXG5pbXBvcnQgeyB1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSB9IGZyb20gJy4uL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuaW1wb3J0IHsgZ2V0RW52aXJvbm1lbnRTZWxlY3Rpb25JbmZvIH0gZnJvbSAnLi4vdXRpbHMvdGVsZXBvcnQvZW52aXJvbm1lbnRTZWxlY3Rpb24uanMnXG5pbXBvcnQgdHlwZSB7IEVudmlyb25tZW50UmVzb3VyY2UgfSBmcm9tICcuLi91dGlscy90ZWxlcG9ydC9lbnZpcm9ubWVudHMuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuL0NvbmZpZ3VyYWJsZVNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBMb2FkaW5nU3RhdGUgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vTG9hZGluZ1N0YXRlLmpzJ1xuXG5jb25zdCBESUFMT0dfVElUTEUgPSAnU2VsZWN0IFJlbW90ZSBFbnZpcm9ubWVudCdcbmNvbnN0IFNFVFVQX0hJTlQgPSBgQ29uZmlndXJlIGVudmlyb25tZW50cyBhdDogaHR0cHM6Ly9jbGF1ZGUuYWkvY29kZWBcblxudHlwZSBQcm9wcyA9IHtcbiAgb25Eb25lOiAobWVzc2FnZT86IHN0cmluZykgPT4gdm9pZFxufVxuXG50eXBlIExvYWRpbmdTdGF0ZSA9ICdsb2FkaW5nJyB8ICd1cGRhdGluZycgfCBudWxsXG5cbmV4cG9ydCBmdW5jdGlvbiBSZW1vdGVFbnZpcm9ubWVudERpYWxvZyh7IG9uRG9uZSB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtsb2FkaW5nU3RhdGUsIHNldExvYWRpbmdTdGF0ZV0gPSB1c2VTdGF0ZTxMb2FkaW5nU3RhdGU+KCdsb2FkaW5nJylcbiAgY29uc3QgW2Vudmlyb25tZW50cywgc2V0RW52aXJvbm1lbnRzXSA9IHVzZVN0YXRlPEVudmlyb25tZW50UmVzb3VyY2VbXT4oW10pXG4gIGNvbnN0IFtzZWxlY3RlZEVudmlyb25tZW50LCBzZXRTZWxlY3RlZEVudmlyb25tZW50XSA9XG4gICAgdXNlU3RhdGU8RW52aXJvbm1lbnRSZXNvdXJjZSB8IG51bGw+KG51bGwpXG4gIGNvbnN0IFtzZWxlY3RlZEVudmlyb25tZW50U291cmNlLCBzZXRTZWxlY3RlZEVudmlyb25tZW50U291cmNlXSA9XG4gICAgdXNlU3RhdGU8U2V0dGluZ1NvdXJjZSB8IG51bGw+KG51bGwpXG4gIGNvbnN0IFtlcnJvciwgc2V0RXJyb3JdID0gdXNlU3RhdGU8c3RyaW5nIHwgbnVsbD4obnVsbClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGxldCBjYW5jZWxsZWQgPSBmYWxzZVxuICAgIGFzeW5jIGZ1bmN0aW9uIGZldGNoSW5mbygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGdldEVudmlyb25tZW50U2VsZWN0aW9uSW5mbygpXG4gICAgICAgIGlmIChjYW5jZWxsZWQpIHJldHVyblxuICAgICAgICBzZXRFbnZpcm9ubWVudHMocmVzdWx0LmF2YWlsYWJsZUVudmlyb25tZW50cylcbiAgICAgICAgc2V0U2VsZWN0ZWRFbnZpcm9ubWVudChyZXN1bHQuc2VsZWN0ZWRFbnZpcm9ubWVudClcbiAgICAgICAgc2V0U2VsZWN0ZWRFbnZpcm9ubWVudFNvdXJjZShyZXN1bHQuc2VsZWN0ZWRFbnZpcm9ubWVudFNvdXJjZSlcbiAgICAgICAgc2V0TG9hZGluZ1N0YXRlKG51bGwpXG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgaWYgKGNhbmNlbGxlZCkgcmV0dXJuXG4gICAgICAgIGNvbnN0IGZldGNoRXJyb3IgPSB0b0Vycm9yKGVycilcbiAgICAgICAgbG9nRXJyb3IoZmV0Y2hFcnJvcilcbiAgICAgICAgc2V0RXJyb3IoZmV0Y2hFcnJvci5tZXNzYWdlKVxuICAgICAgICBzZXRMb2FkaW5nU3RhdGUobnVsbClcbiAgICAgIH1cbiAgICB9XG4gICAgdm9pZCBmZXRjaEluZm8oKVxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBjYW5jZWxsZWQgPSB0cnVlXG4gICAgfVxuICB9LCBbXSlcblxuICBmdW5jdGlvbiBoYW5kbGVTZWxlY3QodmFsdWU6IHN0cmluZyk6IHZvaWQge1xuICAgIGlmICh2YWx1ZSA9PT0gJ2NhbmNlbCcpIHtcbiAgICAgIG9uRG9uZSgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBzZXRMb2FkaW5nU3RhdGUoJ3VwZGF0aW5nJylcblxuICAgIGNvbnN0IHNlbGVjdGVkRW52ID0gZW52aXJvbm1lbnRzLmZpbmQoZW52ID0+IGVudi5lbnZpcm9ubWVudF9pZCA9PT0gdmFsdWUpXG5cbiAgICBpZiAoIXNlbGVjdGVkRW52KSB7XG4gICAgICBvbkRvbmUoJ0Vycm9yOiBTZWxlY3RlZCBlbnZpcm9ubWVudCBub3QgZm91bmQnKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgdXBkYXRlU2V0dGluZ3NGb3JTb3VyY2UoJ2xvY2FsU2V0dGluZ3MnLCB7XG4gICAgICByZW1vdGU6IHtcbiAgICAgICAgZGVmYXVsdEVudmlyb25tZW50SWQ6IHNlbGVjdGVkRW52LmVudmlyb25tZW50X2lkLFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgb25Eb25lKFxuICAgICAgYFNldCBkZWZhdWx0IHJlbW90ZSBlbnZpcm9ubWVudCB0byAke2NoYWxrLmJvbGQoc2VsZWN0ZWRFbnYubmFtZSl9ICgke3NlbGVjdGVkRW52LmVudmlyb25tZW50X2lkfSlgLFxuICAgIClcbiAgfVxuXG4gIC8vIExvYWRpbmcgc3RhdGVcbiAgaWYgKGxvYWRpbmdTdGF0ZSA9PT0gJ2xvYWRpbmcnKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxEaWFsb2cgdGl0bGU9e0RJQUxPR19USVRMRX0gb25DYW5jZWw9e29uRG9uZX0gaGlkZUlucHV0R3VpZGU+XG4gICAgICAgIDxMb2FkaW5nU3RhdGUgbWVzc2FnZT1cIkxvYWRpbmcgZW52aXJvbm1lbnRz4oCmXCIgLz5cbiAgICAgIDwvRGlhbG9nPlxuICAgIClcbiAgfVxuXG4gIC8vIEVycm9yIHN0YXRlXG4gIGlmIChlcnJvcikge1xuICAgIHJldHVybiAoXG4gICAgICA8RGlhbG9nIHRpdGxlPXtESUFMT0dfVElUTEV9IG9uQ2FuY2VsPXtvbkRvbmV9PlxuICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+RXJyb3I6IHtlcnJvcn08L1RleHQ+XG4gICAgICA8L0RpYWxvZz5cbiAgICApXG4gIH1cblxuICAvLyBObyBlbnZpcm9ubWVudHMgYXZhaWxhYmxlXG4gIGlmICghc2VsZWN0ZWRFbnZpcm9ubWVudCkge1xuICAgIHJldHVybiAoXG4gICAgICA8RGlhbG9nIHRpdGxlPXtESUFMT0dfVElUTEV9IHN1YnRpdGxlPXtTRVRVUF9ISU5UfSBvbkNhbmNlbD17b25Eb25lfT5cbiAgICAgICAgPFRleHQ+Tm8gcmVtb3RlIGVudmlyb25tZW50cyBhdmFpbGFibGUuPC9UZXh0PlxuICAgICAgPC9EaWFsb2c+XG4gICAgKVxuICB9XG5cbiAgLy8gU2luZ2xlIGVudmlyb25tZW50IC0ganVzdCBzaG93IGluZm9cbiAgaWYgKGVudmlyb25tZW50cy5sZW5ndGggPT09IDEpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPFNpbmdsZUVudmlyb25tZW50Q29udGVudFxuICAgICAgICBlbnZpcm9ubWVudD17c2VsZWN0ZWRFbnZpcm9ubWVudH1cbiAgICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIC8vIE11bHRpcGxlIGVudmlyb25tZW50cyAtIHNob3cgc2VsZWN0aW9uIFVJXG4gIHJldHVybiAoXG4gICAgPE11bHRpcGxlRW52aXJvbm1lbnRzQ29udGVudFxuICAgICAgZW52aXJvbm1lbnRzPXtlbnZpcm9ubWVudHN9XG4gICAgICBzZWxlY3RlZEVudmlyb25tZW50PXtzZWxlY3RlZEVudmlyb25tZW50fVxuICAgICAgc2VsZWN0ZWRFbnZpcm9ubWVudFNvdXJjZT17c2VsZWN0ZWRFbnZpcm9ubWVudFNvdXJjZX1cbiAgICAgIGxvYWRpbmdTdGF0ZT17bG9hZGluZ1N0YXRlfVxuICAgICAgb25TZWxlY3Q9e2hhbmRsZVNlbGVjdH1cbiAgICAgIG9uQ2FuY2VsPXtvbkRvbmV9XG4gICAgLz5cbiAgKVxufVxuXG5mdW5jdGlvbiBFbnZpcm9ubWVudExhYmVsKHtcbiAgZW52aXJvbm1lbnQsXG59OiB7XG4gIGVudmlyb25tZW50OiBFbnZpcm9ubWVudFJlc291cmNlXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8VGV4dD5cbiAgICAgIHtmaWd1cmVzLnRpY2t9IFVzaW5nIDxUZXh0IGJvbGQ+e2Vudmlyb25tZW50Lm5hbWV9PC9UZXh0PnsnICd9XG4gICAgICA8VGV4dCBkaW1Db2xvcj4oe2Vudmlyb25tZW50LmVudmlyb25tZW50X2lkfSk8L1RleHQ+XG4gICAgPC9UZXh0PlxuICApXG59XG5cbmZ1bmN0aW9uIFNpbmdsZUVudmlyb25tZW50Q29udGVudCh7XG4gIGVudmlyb25tZW50LFxuICBvbkRvbmUsXG59OiB7XG4gIGVudmlyb25tZW50OiBFbnZpcm9ubWVudFJlc291cmNlXG4gIG9uRG9uZTogKCkgPT4gdm9pZFxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIEhhbmRsZSBFbnRlciB0byBjb250aW51ZVxuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOnllcycsIG9uRG9uZSwgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJyB9KVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZyB0aXRsZT17RElBTE9HX1RJVExFfSBzdWJ0aXRsZT17U0VUVVBfSElOVH0gb25DYW5jZWw9e29uRG9uZX0+XG4gICAgICA8RW52aXJvbm1lbnRMYWJlbCBlbnZpcm9ubWVudD17ZW52aXJvbm1lbnR9IC8+XG4gICAgPC9EaWFsb2c+XG4gIClcbn1cblxuZnVuY3Rpb24gTXVsdGlwbGVFbnZpcm9ubWVudHNDb250ZW50KHtcbiAgZW52aXJvbm1lbnRzLFxuICBzZWxlY3RlZEVudmlyb25tZW50LFxuICBzZWxlY3RlZEVudmlyb25tZW50U291cmNlLFxuICBsb2FkaW5nU3RhdGUsXG4gIG9uU2VsZWN0LFxuICBvbkNhbmNlbCxcbn06IHtcbiAgZW52aXJvbm1lbnRzOiBFbnZpcm9ubWVudFJlc291cmNlW11cbiAgc2VsZWN0ZWRFbnZpcm9ubWVudDogRW52aXJvbm1lbnRSZXNvdXJjZVxuICBzZWxlY3RlZEVudmlyb25tZW50U291cmNlOiBTZXR0aW5nU291cmNlIHwgbnVsbFxuICBsb2FkaW5nU3RhdGU6IExvYWRpbmdTdGF0ZVxuICBvblNlbGVjdDogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWRcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBzb3VyY2VTdWZmaXggPVxuICAgIHNlbGVjdGVkRW52aXJvbm1lbnRTb3VyY2UgJiYgc2VsZWN0ZWRFbnZpcm9ubWVudFNvdXJjZSAhPT0gJ2xvY2FsU2V0dGluZ3MnXG4gICAgICA/IGAgKGZyb20gJHtnZXRTZXR0aW5nU291cmNlTmFtZShzZWxlY3RlZEVudmlyb25tZW50U291cmNlKX0gc2V0dGluZ3MpYFxuICAgICAgOiAnJ1xuXG4gIGNvbnN0IHN1YnRpdGxlID0gKFxuICAgIDxUZXh0PlxuICAgICAgQ3VycmVudGx5IHVzaW5nOiA8VGV4dCBib2xkPntzZWxlY3RlZEVudmlyb25tZW50Lm5hbWV9PC9UZXh0PlxuICAgICAge3NvdXJjZVN1ZmZpeH1cbiAgICA8L1RleHQ+XG4gIClcblxuICByZXR1cm4gKFxuICAgIDxEaWFsb2dcbiAgICAgIHRpdGxlPXtESUFMT0dfVElUTEV9XG4gICAgICBzdWJ0aXRsZT17c3VidGl0bGV9XG4gICAgICBvbkNhbmNlbD17b25DYW5jZWx9XG4gICAgICBoaWRlSW5wdXRHdWlkZVxuICAgID5cbiAgICAgIDxUZXh0IGRpbUNvbG9yPntTRVRVUF9ISU5UfTwvVGV4dD5cbiAgICAgIHtsb2FkaW5nU3RhdGUgPT09ICd1cGRhdGluZycgPyAoXG4gICAgICAgIDxMb2FkaW5nU3RhdGUgbWVzc2FnZT1cIlVwZGF0aW5n4oCmXCIgLz5cbiAgICAgICkgOiAoXG4gICAgICAgIDxTZWxlY3RcbiAgICAgICAgICBvcHRpb25zPXtlbnZpcm9ubWVudHMubWFwKGVudiA9PiAoe1xuICAgICAgICAgICAgbGFiZWw6IChcbiAgICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgICAge2Vudi5uYW1lfSA8VGV4dCBkaW1Db2xvcj4oe2Vudi5lbnZpcm9ubWVudF9pZH0pPC9UZXh0PlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICApLFxuICAgICAgICAgICAgdmFsdWU6IGVudi5lbnZpcm9ubWVudF9pZCxcbiAgICAgICAgICB9KSl9XG4gICAgICAgICAgZGVmYXVsdFZhbHVlPXtzZWxlY3RlZEVudmlyb25tZW50LmVudmlyb25tZW50X2lkfVxuICAgICAgICAgIG9uQ2hhbmdlPXtvblNlbGVjdH1cbiAgICAgICAgICBvbkNhbmNlbD17KCkgPT4gb25TZWxlY3QoJ2NhbmNlbCcpfVxuICAgICAgICAgIGxheW91dD1cImNvbXBhY3QtdmVydGljYWxcIlxuICAgICAgICAvPlxuICAgICAgKX1cbiAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICA8QnlsaW5lPlxuICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVudGVyXCIgYWN0aW9uPVwic2VsZWN0XCIgLz5cbiAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgIGNvbnRleHQ9XCJDb25maXJtYXRpb25cIlxuICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQnlsaW5lPlxuICAgICAgPC9UZXh0PlxuICAgIDwvRGlhbG9nPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixPQUFPQyxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPLEtBQUtD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 \ No newline at end of file diff --git a/components/ResumeTask.tsx b/components/ResumeTask.tsx new file mode 100644 index 0000000..d6f9620 --- /dev/null +++ b/components/ResumeTask.tsx @@ -0,0 +1,268 @@ +import React, { useCallback, useState } from 'react'; +import { useTerminalSize } from 'src/hooks/useTerminalSize.js'; +import { type CodeSession, fetchCodeSessionsFromSessionsAPI } from 'src/utils/teleport/api.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw j/k/arrow list navigation +import { Box, Text, useInput } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js'; +import { logForDebugging } from '../utils/debug.js'; +import { detectCurrentRepository } from '../utils/detectRepository.js'; +import { formatRelativeTime } from '../utils/format.js'; +import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js'; +import { Select } from './CustomSelect/index.js'; +import { Byline } from './design-system/Byline.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +import { Spinner } from './Spinner.js'; +import { TeleportError } from './TeleportError.js'; +type Props = { + onSelect: (session: CodeSession) => void; + onCancel: () => void; + isEmbedded?: boolean; +}; +type LoadErrorType = 'network' | 'auth' | 'api' | 'other'; +const UPDATED_STRING = 'Updated'; +const SPACE_BETWEEN_TABLE_COLUMNS = ' '; +export function ResumeTask({ + onSelect, + onCancel, + isEmbedded = false +}: Props): React.ReactNode { + const { + rows + } = useTerminalSize(); + const [sessions, setSessions] = useState([]); + const [currentRepo, setCurrentRepo] = useState(null); + const [loading, setLoading] = useState(true); + const [loadErrorType, setLoadErrorType] = useState(null); + const [retrying, setRetrying] = useState(false); + const [hasCompletedTeleportErrorFlow, setHasCompletedTeleportErrorFlow] = useState(false); + + // Track focused index for scroll position display in title + const [focusedIndex, setFocusedIndex] = useState(1); + const escKey = useShortcutDisplay('confirm:no', 'Confirmation', 'Esc'); + const loadSessions = useCallback(async () => { + try { + setLoading(true); + setLoadErrorType(null); + + // Detect current repository + const detectedRepo = await detectCurrentRepository(); + setCurrentRepo(detectedRepo); + logForDebugging(`Current repository: ${detectedRepo || 'not detected'}`); + const codeSessions = await fetchCodeSessionsFromSessionsAPI(); + + // Filter sessions by current repository if detected + let filteredSessions = codeSessions; + if (detectedRepo) { + filteredSessions = codeSessions.filter(session => { + if (!session.repo) return false; + const sessionRepo = `${session.repo.owner.login}/${session.repo.name}`; + return sessionRepo === detectedRepo; + }); + logForDebugging(`Filtered ${filteredSessions.length} sessions for repo ${detectedRepo} from ${codeSessions.length} total`); + } + + // Sort by updated_at (newest first) + const sortedSessions = [...filteredSessions].sort((a, b) => { + const dateA = new Date(a.updated_at); + const dateB = new Date(b.updated_at); + return dateB.getTime() - dateA.getTime(); + }); + setSessions(sortedSessions); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : String(err); + logForDebugging(`Error loading code sessions: ${errorMessage}`); + setLoadErrorType(determineErrorType(errorMessage)); + } finally { + setLoading(false); + setRetrying(false); + } + }, []); + const handleRetry = () => { + setRetrying(true); + void loadSessions(); + }; + + // Handle escape via keybinding + useKeybinding('confirm:no', onCancel, { + context: 'Confirmation' + }); + useInput((input, key) => { + // We need to handle ctrl+c in case we don't render a { + const session_1 = sessions.find(s => s.id === value); + if (session_1) { + onSelect(session_1); + } + }} onFocus={value_0 => { + const index = options.findIndex(o => o.value === value_0); + if (index >= 0) { + setFocusedIndex(index + 1); + } + }} /> + + + + + + + + + + + ; +} + +/** + * Determines the type of error based on the error message + */ +function determineErrorType(errorMessage: string): LoadErrorType { + const message = errorMessage.toLowerCase(); + if (message.includes('fetch') || message.includes('network') || message.includes('timeout')) { + return 'network'; + } + if (message.includes('auth') || message.includes('token') || message.includes('permission') || message.includes('oauth') || message.includes('not authenticated') || message.includes('/login') || message.includes('console account') || message.includes('403')) { + return 'auth'; + } + if (message.includes('api') || message.includes('rate limit') || message.includes('500') || message.includes('529')) { + return 'api'; + } + return 'other'; +} + +/** + * Renders error-specific troubleshooting guidance + */ +function renderErrorSpecificGuidance(errorType: LoadErrorType): React.ReactNode { + switch (errorType) { + case 'network': + return + Check your internet connection + ; + case 'auth': + return + Teleport requires a Claude account + + Run /login and select "Claude account with + subscription" + + ; + case 'api': + return + Sorry, Claude encountered an error + ; + case 'other': + return + Sorry, Claude Code encountered an error + ; + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlU3RhdGUiLCJ1c2VUZXJtaW5hbFNpemUiLCJDb2RlU2Vzc2lvbiIsImZldGNoQ29kZVNlc3Npb25zRnJvbVNlc3Npb25zQVBJIiwiQm94IiwiVGV4dCIsInVzZUlucHV0IiwidXNlS2V5YmluZGluZyIsInVzZVNob3J0Y3V0RGlzcGxheSIsImxvZ0ZvckRlYnVnZ2luZyIsImRldGVjdEN1cnJlbnRSZXBvc2l0b3J5IiwiZm9ybWF0UmVsYXRpdmVUaW1lIiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiU2VsZWN0IiwiQnlsaW5lIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJTcGlubmVyIiwiVGVsZXBvcnRFcnJvciIsIlByb3BzIiwib25TZWxlY3QiLCJzZXNzaW9uIiwib25DYW5jZWwiLCJpc0VtYmVkZGVkIiwiTG9hZEVycm9yVHlwZSIsIlVQREFURURfU1RSSU5HIiwiU1BBQ0VfQkVUV0VFTl9UQUJMRV9DT0xVTU5TIiwiUmVzdW1lVGFzayIsIlJlYWN0Tm9kZSIsInJvd3MiLCJzZXNzaW9ucyIsInNldFNlc3Npb25zIiwiY3VycmVudFJlcG8iLCJzZXRDdXJyZW50UmVwbyIsImxvYWRpbmciLCJzZXRMb2FkaW5nIiwibG9hZEVycm9yVHlwZSIsInNldExvYWRFcnJvclR5cGUiLCJyZXRyeWluZyIsInNldFJldHJ5aW5nIiwiaGFzQ29tcGxldGVkVGVsZXBvcnRFcnJvckZsb3ciLCJzZXRIYXNDb21wbGV0ZWRUZWxlcG9ydEVycm9yRmxvdyIsImZvY3VzZWRJbmRleCIsInNldEZvY3VzZWRJbmRleCIsImVzY0tleSIsImxvYWRTZXNzaW9ucyIsImRldGVjdGVkUmVwbyIsImNvZGVTZXNzaW9ucyIsImZpbHRlcmVkU2Vzc2lvbnMiLCJmaWx0ZXIiLCJyZXBvIiwic2Vzc2lvblJlcG8iLCJvd25lciIsImxvZ2luIiwibmFtZSIsImxlbmd0aCIsInNvcnRlZFNlc3Npb25zIiwic29ydCIsImEiLCJiIiwiZGF0ZUEiLCJEYXRlIiwidXBkYXRlZF9hdCIsImRhdGVCIiwiZ2V0VGltZSIsImVyciIsImVycm9yTWVzc2FnZSIsIkVycm9yIiwibWVzc2FnZSIsIlN0cmluZyIsImRldGVybWluZUVycm9yVHlwZSIsImhhbmRsZVJldHJ5IiwiY29udGV4dCIsImlucHV0Iiwia2V5IiwiY3RybCIsInJldHVybiIsImhhbmRsZUVycm9yQ29tcGxldGUiLCJyZW5kZXJFcnJvclNwZWNpZmljR3VpZGFuY2UiLCJzZXNzaW9uTWV0YWRhdGEiLCJtYXAiLCJ0aW1lU3RyaW5nIiwibWF4VGltZVN0cmluZ0xlbmd0aCIsIk1hdGgiLCJtYXgiLCJtZXRhIiwib3B0aW9ucyIsInRpdGxlIiwiaWQiLCJwYWRkZWRUaW1lIiwicGFkRW5kIiwibGFiZWwiLCJ2YWx1ZSIsImxheW91dE92ZXJoZWFkIiwibWF4VmlzaWJsZU9wdGlvbnMiLCJtaW4iLCJtYXhIZWlnaHQiLCJzaG93U2Nyb2xsUG9zaXRpb24iLCJmaW5kIiwicyIsImluZGV4IiwiZmluZEluZGV4IiwibyIsInRvTG93ZXJDYXNlIiwiaW5jbHVkZXMiLCJlcnJvclR5cGUiXSwic291cmNlcyI6WyJSZXN1bWVUYXNrLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlQ2FsbGJhY2ssIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICdzcmMvaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBDb2RlU2Vzc2lvbixcbiAgZmV0Y2hDb2RlU2Vzc2lvbnNGcm9tU2Vzc2lvbnNBUEksXG59IGZyb20gJ3NyYy91dGlscy90ZWxlcG9ydC9hcGkuanMnXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL3ByZWZlci11c2Uta2V5YmluZGluZ3MgLS0gcmF3IGovay9hcnJvdyBsaXN0IG5hdmlnYXRpb25cbmltcG9ydCB7IEJveCwgVGV4dCwgdXNlSW5wdXQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7IHVzZVNob3J0Y3V0RGlzcGxheSB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZVNob3J0Y3V0RGlzcGxheS5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHsgZGV0ZWN0Q3VycmVudFJlcG9zaXRvcnkgfSBmcm9tICcuLi91dGlscy9kZXRlY3RSZXBvc2l0b3J5LmpzJ1xuaW1wb3J0IHsgZm9ybWF0UmVsYXRpdmVUaW1lIH0gZnJvbSAnLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuL0N1c3RvbVNlbGVjdC9pbmRleC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IFNwaW5uZXIgfSBmcm9tICcuL1NwaW5uZXIuanMnXG5pbXBvcnQgeyBUZWxlcG9ydEVycm9yIH0gZnJvbSAnLi9UZWxlcG9ydEVycm9yLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBvblNlbGVjdDogKHNlc3Npb246IENvZGVTZXNzaW9uKSA9PiB2b2lkXG4gIG9uQ2FuY2VsOiAoKSA9PiB2b2lkXG4gIGlzRW1iZWRkZWQ/OiBib29sZWFuXG59XG5cbnR5cGUgTG9hZEVycm9yVHlwZSA9ICduZXR3b3JrJyB8ICdhdXRoJyB8ICdhcGknIHwgJ290aGVyJ1xuXG5jb25zdCBVUERBVEVEX1NUUklORyA9ICdVcGRhdGVkJ1xuY29uc3QgU1BBQ0VfQkVUV0VFTl9UQUJMRV9DT0xVTU5TID0gJyAgJ1xuXG5leHBvcnQgZnVuY3Rpb24gUmVzdW1lVGFzayh7XG4gIG9uU2VsZWN0LFxuICBvbkNhbmNlbCxcbiAgaXNFbWJlZGRlZCA9IGZhbHNlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB7IHJvd3MgfSA9IHVzZVRlcm1pbmFsU2l6ZSgpXG4gIGNvbnN0IFtzZXNzaW9ucywgc2V0U2Vzc2lvbnNdID0gdXNlU3RhdGU8Q29kZVNlc3Npb25bXT4oW10pXG4gIGNvbnN0IFtjdXJyZW50UmVwbywgc2V0Q3VycmVudFJlcG9dID0gdXNlU3RhdGU8c3RyaW5nIHwgbnVsbD4obnVsbClcblxuICBjb25zdCBbbG9hZGluZywgc2V0TG9hZGluZ10gPSB1c2VTdGF0ZSh0cnVlKVxuICBjb25zdCBbbG9hZEVycm9yVHlwZSwgc2V0TG9hZEVycm9yVHlwZV0gPSB1c2VTdGF0ZTxMb2FkRXJyb3JUeXBlIHwgbnVsbD4obnVsbClcbiAgY29uc3QgW3JldHJ5aW5nLCBzZXRSZXRyeWluZ10gPSB1c2VTdGF0ZShmYWxzZSlcblxuICBjb25zdCBbaGFzQ29tcGxldGVkVGVsZXBvcnRFcnJvckZsb3csIHNldEhhc0NvbXBsZXRlZFRlbGVwb3J0RXJyb3JGbG93XSA9XG4gICAgdXNlU3RhdGUoZmFsc2UpXG5cbiAgLy8gVHJhY2sgZm9jdXNlZCBpbmRleCBmb3Igc2Nyb2xsIHBvc2l0aW9uIGRpc3BsYXkgaW4gdGl0bGVcbiAgY29uc3QgW2ZvY3VzZWRJbmRleCwgc2V0Rm9jdXNlZEluZGV4XSA9IHVzZVN0YXRlKDEpXG5cbiAgY29uc3QgZXNjS2V5ID0gdXNlU2hvcnRjdXREaXNwbGF5KCdjb25maXJtOm5vJywgJ0NvbmZpcm1hdGlvbicsICdFc2MnKVxuXG4gIGNvbnN0IGxvYWRTZXNzaW9ucyA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICB0cnkge1xuICAgICAgc2V0TG9hZGluZyh0cnVlKVxuICAgICAgc2V0TG9hZEVycm9yVHlwZShudWxsKVxuXG4gICAgICAvLyBEZXRlY3QgY3VycmVudCByZXBvc2l0b3J5XG4gICAgICBjb25zdCBkZXRlY3RlZFJlcG8gPSBhd2FpdCBkZXRlY3RDdXJyZW50UmVwb3NpdG9yeSgpXG4gICAgICBzZXRDdXJyZW50UmVwbyhkZXRlY3RlZFJlcG8pXG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoYEN1cnJlbnQgcmVwb3NpdG9yeTogJHtkZXRlY3RlZFJlcG8gfHwgJ25vdCBkZXRlY3RlZCd9YClcblxuICAgICAgY29uc3QgY29kZVNlc3Npb25zID0gYXdhaXQgZmV0Y2hDb2RlU2Vzc2lvbnNGcm9tU2Vzc2lvbnNBUEkoKVxuXG4gICAgICAvLyBGaWx0ZXIgc2Vzc2lvbnMgYnkgY3VycmVudCByZXBvc2l0b3J5IGlmIGRldGVjdGVkXG4gICAgICBsZXQgZmlsdGVyZWRTZXNzaW9ucyA9IGNvZGVTZXNzaW9uc1xuICAgICAgaWYgKGRldGVjdGVkUmVwbykge1xuICAgICAgICBmaWx0ZXJlZFNlc3Npb25zID0gY29kZVNlc3Npb25zLmZpbHRlcihzZXNzaW9uID0+IHtcbiAgICAgICAgICBpZiAoIXNlc3Npb24ucmVwbykgcmV0dXJuIGZhbHNlXG4gICAgICAgICAgY29uc3Qgc2Vzc2lvblJlcG8gPSBgJHtzZXNzaW9uLnJlcG8ub3duZXIubG9naW59LyR7c2Vzc2lvbi5yZXBvLm5hbWV9YFxuICAgICAgICAgIHJldHVybiBzZXNzaW9uUmVwbyA9PT0gZGV0ZWN0ZWRSZXBvXG4gICAgICAgIH0pXG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICBgRmlsdGVyZWQgJHtmaWx0ZXJlZFNlc3Npb25zLmxlbmd0aH0gc2Vzc2lvbnMgZm9yIHJlcG8gJHtkZXRlY3RlZFJlcG99IGZyb20gJHtjb2RlU2Vzc2lvbnMubGVuZ3RofSB0b3RhbGAsXG4gICAgICAgIClcbiAgICAgIH1cblxuICAgICAgLy8gU29ydCBieSB1cGRhdGVkX2F0IChuZXdlc3QgZmlyc3QpXG4gICAgICBjb25zdCBzb3J0ZWRTZXNzaW9ucyA9IFsuLi5maWx0ZXJlZFNlc3Npb25zXS5zb3J0KChhLCBiKSA9PiB7XG4gICAgICAgIGNvbnN0IGRhdGVBID0gbmV3IERhdGUoYS51cGRhdGVkX2F0KVxuICAgICAgICBjb25zdCBkYXRlQiA9IG5ldyBEYXRlKGIudXBkYXRlZF9hdClcbiAgICAgICAgcmV0dXJuIGRhdGVCLmdldFRpbWUoKSAtIGRhdGVBLmdldFRpbWUoKVxuICAgICAgfSlcblxuICAgICAgc2V0U2Vzc2lvbnMoc29ydGVkU2Vzc2lvbnMpXG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zdCBlcnJvck1lc3NhZ2UgPSBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycilcbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgRXJyb3IgbG9hZGluZyBjb2RlIHNlc3Npb25zOiAke2Vycm9yTWVzc2FnZX1gKVxuICAgICAgc2V0TG9hZEVycm9yVHlwZShkZXRlcm1pbmVFcnJvclR5cGUoZXJyb3JNZXNzYWdlKSlcbiAgICB9IGZpbmFsbHkge1xuICAgICAgc2V0TG9hZGluZyhmYWxzZSlcbiAgICAgIHNldFJldHJ5aW5nKGZhbHNlKVxuICAgIH1cbiAgfSwgW10pXG5cbiAgY29uc3QgaGFuZGxlUmV0cnkgPSAoKSA9PiB7XG4gICAgc2V0UmV0cnlpbmcodHJ1ZSlcbiAgICB2b2lkIGxvYWRTZXNzaW9ucygpXG4gIH1cblxuICAvLyBIYW5kbGUgZXNjYXBlIHZpYSBrZXliaW5kaW5nXG4gIHVzZUtleWJpbmRpbmcoJ2NvbmZpcm06bm8nLCBvbkNhbmNlbCwgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJyB9KVxuXG4gIHVzZUlucHV0KChpbnB1dCwga2V5KSA9PiB7XG4gICAgLy8gV2UgbmVlZCB0byBoYW5kbGUgY3RybCtjIGluIGNhc2Ugd2UgZG9uJ3QgcmVuZGVyIGEgPFNlbGVjdD5cbiAgICBpZiAoa2V5LmN0cmwgJiYgaW5wdXQgPT09ICdjJykge1xuICAgICAgb25DYW5jZWwoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIHJldHJ5IGluIGVycm9yIHN0YXRlIHdpdGggJ2N0cmwrcidcbiAgICBpZiAoa2V5LmN0cmwgJiYgaW5wdXQgPT09ICdyJyAmJiBsb2FkRXJyb3JUeXBlKSB7XG4gICAgICBoYW5kbGVSZXRyeSgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgZW50ZXIga2V5IGZvciBlcnJvciBzdGF0ZXMgdG8gYWxsb3cgY29udGludWF0aW9uIHdpdGggcmVndWxhciB0ZWxlcG9ydFxuICAgIGlmIChsb2FkRXJyb3JUeXBlICE9PSBudWxsICYmIGtleS5yZXR1cm4pIHtcbiAgICAgIG9uQ2FuY2VsKCkgLy8gVGhpcyB3aWxsIGNvbnRpbnVlIHdpdGggcmVndWxhciB0ZWxlcG9ydCBmbG93XG4gICAgICByZXR1cm5cbiAgICB9XG4gIH0pXG5cbiAgY29uc3QgaGFuZGxlRXJyb3JDb21wbGV0ZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRIYXNDb21wbGV0ZWRUZWxlcG9ydEVycm9yRmxvdyh0cnVlKVxuICAgIHZvaWQgbG9hZFNlc3Npb25zKClcbiAgfSwgW3NldEhhc0NvbXBsZXRlZFRlbGVwb3J0RXJyb3JGbG93LCBsb2FkU2Vzc2lvbnNdKVxuXG4gIC8vIFNob3cgZXJyb3IgZGlhbG9nIGlmIG5lZWRlZFxuICBpZiAoIWhhc0NvbXBsZXRlZFRlbGVwb3J0RXJyb3JGbG93KSB7XG4gICAgcmV0dXJuIDxUZWxlcG9ydEVycm9yIG9uQ29tcGxldGU9e2hhbmRsZUVycm9yQ29tcGxldGV9IC8+XG4gIH1cblxuICBpZiAobG9hZGluZykge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nPXsxfT5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCI+XG4gICAgICAgICAgPFNwaW5uZXIgLz5cbiAgICAgICAgICA8VGV4dCBib2xkPkxvYWRpbmcgQ2xhdWRlIENvZGUgc2Vzc2lvbnPigKY8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICB7cmV0cnlpbmcgPyAnUmV0cnlpbmfigKYnIDogJ0ZldGNoaW5nIHlvdXIgQ2xhdWRlIENvZGUgc2Vzc2lvbnPigKYnfVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBpZiAobG9hZEVycm9yVHlwZSkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nPXsxfT5cbiAgICAgICAgPFRleHQgYm9sZCBjb2xvcj1cImVycm9yXCI+XG4gICAgICAgICAgRXJyb3IgbG9hZGluZyBDbGF1ZGUgQ29kZSBzZXNzaW9uc1xuICAgICAgICA8L1RleHQ+XG5cbiAgICAgICAge3JlbmRlckVycm9yU3BlY2lmaWNHdWlkYW5jZShsb2FkRXJyb3JUeXBlKX1cblxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICBQcmVzcyA8VGV4dCBib2xkPkN0cmwrUjwvVGV4dD4gdG8gcmV0cnkgwrcgUHJlc3N7JyAnfVxuICAgICAgICAgIDxUZXh0IGJvbGQ+e2VzY0tleX08L1RleHQ+IHRvIGNhbmNlbFxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBpZiAoc2Vzc2lvbnMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmc9ezF9PlxuICAgICAgICA8VGV4dCBib2xkPlxuICAgICAgICAgIE5vIENsYXVkZSBDb2RlIHNlc3Npb25zIGZvdW5kXG4gICAgICAgICAge2N1cnJlbnRSZXBvICYmIDxUZXh0PiBmb3Ige2N1cnJlbnRSZXBvfTwvVGV4dD59XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgUHJlc3MgPFRleHQgYm9sZD57ZXNjS2V5fTwvVGV4dD4gdG8gY2FuY2VsXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IHNlc3Npb25NZXRhZGF0YSA9IHNlc3Npb25zLm1hcChzZXNzaW9uID0+ICh7XG4gICAgLi4uc2Vzc2lvbixcbiAgICB0aW1lU3RyaW5nOiBmb3JtYXRSZWxhdGl2ZVRpbWUobmV3IERhdGUoc2Vzc2lvbi51cGRhdGVkX2F0KSksXG4gIH0pKVxuICBjb25zdCBtYXhUaW1lU3RyaW5nTGVuZ3RoID0gTWF0aC5tYXgoXG4gICAgVVBEQVRFRF9TVFJJTkcubGVuZ3RoLFxuICAgIC4uLnNlc3Npb25NZXRhZGF0YS5tYXAobWV0YSA9PiBtZXRhLnRpbWVTdHJpbmcubGVuZ3RoKSxcbiAgKVxuXG4gIGNvbnN0IG9wdGlvbnMgPSBzZXNzaW9uTWV0YWRhdGEubWFwKCh7IHRpbWVTdHJpbmcsIHRpdGxlLCBpZCB9KSA9PiB7XG4gICAgY29uc3QgcGFkZGVkVGltZSA9IHRpbWVTdHJpbmcucGFkRW5kKG1heFRpbWVTdHJpbmdMZW5ndGgsICcgJylcblxuICAgIC8vIFRPRE86IGluY2x1ZGUgYnJhbmNoIG5hbWUgd2hlbiBBUEkgcmV0dXJucyBpdFxuICAgIHJldHVybiB7XG4gICAgICBsYWJlbDogYCR7cGFkZGVkVGltZX0gICR7dGl0bGV9YCxcbiAgICAgIHZhbHVlOiBpZCxcbiAgICB9XG4gIH0pXG5cbiAgLy8gQWRqdXN0IGxheW91dCBmb3IgZW1iZWRkZWQgdnMgZnVsbC1zY3JlZW4gcmVuZGVyaW5nXG4gIC8vIE92ZXJoZWFkOiBwYWRkaW5nICgyKSArIHRpdGxlICgxKSArIG1hcmdpblkgKDIpICsgaGVhZGVyICgxKSArIGZvb3RlciAoMSkgPSA3XG4gIGNvbnN0IGxheW91dE92ZXJoZWFkID0gN1xuICBjb25zdCBtYXhWaXNpYmxlT3B0aW9ucyA9IE1hdGgubWF4KFxuICAgIDEsXG4gICAgaXNFbWJlZGRlZFxuICAgICAgPyBNYXRoLm1pbihzZXNzaW9ucy5sZW5ndGgsIDUsIHJvd3MgLSA2IC0gbGF5b3V0T3ZlcmhlYWQpXG4gICAgICA6IE1hdGgubWluKHNlc3Npb25zLmxlbmd0aCwgcm93cyAtIDEgLSBsYXlvdXRPdmVyaGVhZCksXG4gIClcbiAgY29uc3QgbWF4SGVpZ2h0ID0gbWF4VmlzaWJsZU9wdGlvbnMgKyBsYXlvdXRPdmVyaGVhZFxuXG4gIC8vIFNob3cgc2Nyb2xsIHBvc2l0aW9uIGluIHRpdGxlIHdoZW4gbGlzdCBuZWVkcyBzY3JvbGxpbmdcbiAgY29uc3Qgc2hvd1Njcm9sbFBvc2l0aW9uID0gc2Vzc2lvbnMubGVuZ3RoID4gbWF4VmlzaWJsZU9wdGlvbnNcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmc9ezF9IGhlaWdodD17bWF4SGVpZ2h0fT5cbiAgICAgIDxUZXh0IGJvbGQ+XG4gICAgICAgIFNlbGVjdCBhIHNlc3Npb24gdG8gcmVzdW1lXG4gICAgICAgIHtzaG93U2Nyb2xsUG9zaXRpb24gJiYgKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgeycgJ31cbiAgICAgICAgICAgICh7Zm9jdXNlZEluZGV4fSBvZiB7c2Vzc2lvbnMubGVuZ3RofSlcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIHtjdXJyZW50UmVwbyAmJiA8VGV4dCBkaW1Db2xvcj4gKHtjdXJyZW50UmVwb30pPC9UZXh0Pn06XG4gICAgICA8L1RleHQ+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9IGZsZXhHcm93PXsxfT5cbiAgICAgICAgPEJveCBtYXJnaW5MZWZ0PXsyfT5cbiAgICAgICAgICA8VGV4dCBib2xkPlxuICAgICAgICAgICAge1VQREFURURfU1RSSU5HLnBhZEVuZChtYXhUaW1lU3RyaW5nTGVuZ3RoLCAnICcpfVxuICAgICAgICAgICAge1NQQUNFX0JFVFdFRU5fVEFCTEVfQ09MVU1OU31cbiAgICAgICAgICAgIHsnU2Vzc2lvbiBUaXRsZSd9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPFNlbGVjdFxuICAgICAgICAgIHZpc2libGVPcHRpb25Db3VudD17bWF4VmlzaWJsZU9wdGlvbnN9XG4gICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICBvbkNoYW5nZT17dmFsdWUgPT4ge1xuICAgICAgICAgICAgY29uc3Qgc2Vzc2lvbiA9IHNlc3Npb25zLmZpbmQocyA9PiBzLmlkID09PSB2YWx1ZSlcbiAgICAgICAgICAgIGlmIChzZXNzaW9uKSB7XG4gICAgICAgICAgICAgIG9uU2VsZWN0KHNlc3Npb24pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfX1cbiAgICAgICAgICBvbkZvY3VzPXt2YWx1ZSA9PiB7XG4gICAgICAgICAgICBjb25zdCBpbmRleCA9IG9wdGlvbnMuZmluZEluZGV4KG8gPT4gby52YWx1ZSA9PT0gdmFsdWUpXG4gICAgICAgICAgICBpZiAoaW5kZXggPj0gMCkge1xuICAgICAgICAgICAgICBzZXRGb2N1c2VkSW5kZXgoaW5kZXggKyAxKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH19XG4gICAgICAgIC8+XG4gICAgICA8L0JveD5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICA8QnlsaW5lPlxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwi4oaRL+KGk1wiIGFjdGlvbj1cInNlbGVjdFwiIC8+XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cImNvbmZpcm1cIiAvPlxuICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuLyoqXG4gKiBEZXRlcm1pbmVzIHRoZSB0eXBlIG9mIGVycm9yIGJhc2VkIG9uIHRoZSBlcnJvciBtZXNzYWdlXG4gKi9cbmZ1bmN0aW9uIGRldGVybWluZUVycm9yVHlwZShlcnJvck1lc3NhZ2U6IHN0cmluZyk6IExvYWRFcnJvclR5cGUge1xuICBjb25zdCBtZXNzYWdlID0gZXJyb3JNZXNzYWdlLnRvTG93ZXJDYXNlKClcblxuICBpZiAoXG4gICAgbWVzc2FnZS5pbmNsdWRlcygnZmV0Y2gnKSB8fFxuICAgIG1lc3NhZ2UuaW5jbHVkZXMoJ25ldHdvcmsnKSB8fFxuICAgIG1lc3NhZ2UuaW5jbHVkZXMoJ3RpbWVvdXQnKVxuICApIHtcbiAgICByZXR1cm4gJ25ldHdvcmsnXG4gIH1cblxuICBpZiAoXG4gICAgbWVzc2FnZS5pbmNsdWRlcygnYXV0aCcpIHx8XG4gICAgbWVzc2FnZS5pbmNsdWRlcygndG9rZW4nKSB8fFxuICAgIG1lc3NhZ2UuaW5jbHVkZXMoJ3Blcm1pc3Npb24nKSB8fFxuICAgIG1lc3NhZ2UuaW5jbHVkZXMoJ29hdXRoJykgfHxcbiAgICBtZXNzYWdlLmluY2x1ZGVzKCdub3QgYXV0aGVudGljYXRlZCcpIHx8XG4gICAgbWVzc2FnZS5pbmNsdWRlcygnL2xvZ2luJykgfHxcbiAgICBtZXNzYWdlLmluY2x1ZGVzKCdjb25zb2xlIGFjY291bnQnKSB8fFxuICAgIG1lc3NhZ2UuaW5jbHVkZXMoJzQwMycpXG4gICkge1xuICAgIHJldHVybiAnYXV0aCdcbiAgfVxuXG4gIGlmIChcbiAgICBtZXNzYWdlLmluY2x1ZGVzKCdhcGknKSB8fFxuICAgIG1lc3NhZ2UuaW5jbHVkZXMoJ3JhdGUgbGltaXQnKSB8fFxuICAgIG1lc3NhZ2UuaW5jbHVkZXMoJzUwMCcpIHx8XG4gICAgbWVzc2FnZS5pbmNsdWRlcygnNTI5JylcbiAgKSB7XG4gICAgcmV0dXJuICdhcGknXG4gIH1cblxuICByZXR1cm4gJ290aGVyJ1xufVxuXG4vKipcbiAqIFJlbmRlcnMgZXJyb3Itc3BlY2lmaWMgdHJvdWJsZXNob290aW5nIGd1aWRhbmNlXG4gKi9cbmZ1bmN0aW9uIHJlbmRlckVycm9yU3BlY2lmaWNHdWlkYW5jZShcbiAgZXJyb3JUeXBlOiBMb2FkRXJyb3JUeXBlLFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgc3dpdGNoIChlcnJvclR5cGUpIHtcbiAgICBjYXNlICduZXR3b3JrJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxCb3ggbWFyZ2luWT17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPkNoZWNrIHlvdXIgaW50ZXJuZXQgY29ubmVjdGlvbjwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApXG5cbiAgICBjYXNlICdhdXRoJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxCb3ggbWFyZ2luWT17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlRlbGVwb3J0IHJlcXVpcmVzIGEgQ2xhdWRlIGFjY291bnQ8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICBSdW4gPFRleHQgYm9sZD4vbG9naW48L1RleHQ+IGFuZCBzZWxlY3QgJnF1b3Q7Q2xhdWRlIGFjY291bnQgd2l0aFxuICAgICAgICAgICAgc3Vic2NyaXB0aW9uJnF1b3Q7XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIClcblxuICAgIGNhc2UgJ2FwaSc6XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8Qm94IG1hcmdpblk9ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5Tb3JyeSwgQ2xhdWRlIGVuY291bnRlcmVkIGFuIGVycm9yPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIClcblxuICAgIGNhc2UgJ290aGVyJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxCb3ggbWFyZ2luWT17MX0gZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlNvcnJ5LCBDbGF1ZGUgQ29kZSBlbmNvdW50ZXJlZCBhbiBlcnJvcjwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApXG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxJQUFJQyxXQUFXLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3BELFNBQVNDLGVBQWUsUUFBUSw4QkFBOEI7QUFDOUQsU0FDRSxLQUFLQyxXQUFXLEVBQ2hCQyxnQ0FBZ0MsUUFDM0IsMkJBQTJCO0FBQ2xDO0FBQ0EsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxXQUFXO0FBQy9DLFNBQVNDLGFBQWEsUUFBUSxpQ0FBaUM7QUFDL0QsU0FBU0Msa0JBQWtCLFFBQVEsc0NBQXNDO0FBQ3pFLFNBQVNDLGVBQWUsUUFBUSxtQkFBbUI7QUFDbkQsU0FBU0MsdUJBQXVCLFFBQVEsOEJBQThCO0FBQ3RFLFNBQVNDLGtCQUFrQixRQUFRLG9CQUFvQjtBQUN2RCxTQUFTQyx3QkFBd0IsUUFBUSwrQkFBK0I7QUFDeEUsU0FBU0MsTUFBTSxRQUFRLHlCQUF5QjtBQUNoRCxTQUFTQyxNQUFNLFFBQVEsMkJBQTJCO0FBQ2xELFNBQVNDLG9CQUFvQixRQUFRLHlDQUF5QztBQUM5RSxTQUFTQyxPQUFPLFFBQVEsY0FBYztBQUN0QyxTQUFTQyxhQUFhLFFBQVEsb0JB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 \ No newline at end of file diff --git a/components/SandboxViolationExpandedView.tsx b/components/SandboxViolationExpandedView.tsx new file mode 100644 index 0000000..8eefd59 --- /dev/null +++ b/components/SandboxViolationExpandedView.tsx @@ -0,0 +1,99 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { type ReactNode, useEffect, useState } from 'react'; +import { Box, Text } from '../ink.js'; +import type { SandboxViolationEvent } from '../utils/sandbox/sandbox-adapter.js'; +import { SandboxManager } from '../utils/sandbox/sandbox-adapter.js'; + +/** + * Format a timestamp as "h:mm:ssa" (e.g., "1:30:45pm"). + * Replaces date-fns format() to avoid pulling in a 39MB dependency for one call. + */ +function formatTime(date: Date): string { + const h = date.getHours() % 12 || 12; + const m = String(date.getMinutes()).padStart(2, '0'); + const s = String(date.getSeconds()).padStart(2, '0'); + const ampm = date.getHours() < 12 ? 'am' : 'pm'; + return `${h}:${m}:${s}${ampm}`; +} +import { getPlatform } from 'src/utils/platform.js'; +export function SandboxViolationExpandedView() { + const $ = _c(15); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = []; + $[0] = t0; + } else { + t0 = $[0]; + } + const [violations, setViolations] = useState(t0); + const [totalCount, setTotalCount] = useState(0); + let t1; + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + const store = SandboxManager.getSandboxViolationStore(); + const unsubscribe = store.subscribe(allViolations => { + setViolations(allViolations.slice(-10)); + setTotalCount(store.getTotalCount()); + }); + return unsubscribe; + }; + t2 = []; + $[1] = t1; + $[2] = t2; + } else { + t1 = $[1]; + t2 = $[2]; + } + useEffect(t1, t2); + if (!SandboxManager.isSandboxingEnabled() || getPlatform() === "linux") { + return null; + } + if (totalCount === 0) { + return null; + } + const t3 = totalCount === 1 ? "operation" : "operations"; + let t4; + if ($[3] !== t3 || $[4] !== totalCount) { + t4 = ⧈ Sandbox blocked {totalCount} total{" "}{t3}; + $[3] = t3; + $[4] = totalCount; + $[5] = t4; + } else { + t4 = $[5]; + } + let t5; + if ($[6] !== violations) { + t5 = violations.map(_temp); + $[6] = violations; + $[7] = t5; + } else { + t5 = $[7]; + } + const t6 = Math.min(10, violations.length); + let t7; + if ($[8] !== t6 || $[9] !== totalCount) { + t7 = … showing last {t6} of {totalCount}; + $[8] = t6; + $[9] = totalCount; + $[10] = t7; + } else { + t7 = $[10]; + } + let t8; + if ($[11] !== t4 || $[12] !== t5 || $[13] !== t7) { + t8 = {t4}{t5}{t7}; + $[11] = t4; + $[12] = t5; + $[13] = t7; + $[14] = t8; + } else { + t8 = $[14]; + } + return t8; +} +function _temp(v, i) { + return {formatTime(v.timestamp)}{v.command ? ` ${v.command}:` : ""} {v.line}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiQm94IiwiVGV4dCIsIlNhbmRib3hWaW9sYXRpb25FdmVudCIsIlNhbmRib3hNYW5hZ2VyIiwiZm9ybWF0VGltZSIsImRhdGUiLCJEYXRlIiwiaCIsImdldEhvdXJzIiwibSIsIlN0cmluZyIsImdldE1pbnV0ZXMiLCJwYWRTdGFydCIsInMiLCJnZXRTZWNvbmRzIiwiYW1wbSIsImdldFBsYXRmb3JtIiwiU2FuZGJveFZpb2xhdGlvbkV4cGFuZGVkVmlldyIsIiQiLCJfYyIsInQwIiwiU3ltYm9sIiwiZm9yIiwidmlvbGF0aW9ucyIsInNldFZpb2xhdGlvbnMiLCJ0b3RhbENvdW50Iiwic2V0VG90YWxDb3VudCIsInQxIiwidDIiLCJzdG9yZSIsImdldFNhbmRib3hWaW9sYXRpb25TdG9yZSIsInVuc3Vic2NyaWJlIiwic3Vic2NyaWJlIiwiYWxsVmlvbGF0aW9ucyIsInNsaWNlIiwiZ2V0VG90YWxDb3VudCIsImlzU2FuZGJveGluZ0VuYWJsZWQiLCJ0MyIsInQ0IiwidDUiLCJtYXAiLCJfdGVtcCIsInQ2IiwiTWF0aCIsIm1pbiIsImxlbmd0aCIsInQ3IiwidDgiLCJ2IiwiaSIsInRpbWVzdGFtcCIsImdldFRpbWUiLCJjb21tYW5kIiwibGluZSJdLCJzb3VyY2VzIjpbIlNhbmRib3hWaW9sYXRpb25FeHBhbmRlZFZpZXcudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdHlwZSBSZWFjdE5vZGUsIHVzZUVmZmVjdCwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgU2FuZGJveFZpb2xhdGlvbkV2ZW50IH0gZnJvbSAnLi4vdXRpbHMvc2FuZGJveC9zYW5kYm94LWFkYXB0ZXIuanMnXG5pbXBvcnQgeyBTYW5kYm94TWFuYWdlciB9IGZyb20gJy4uL3V0aWxzL3NhbmRib3gvc2FuZGJveC1hZGFwdGVyLmpzJ1xuXG4vKipcbiAqIEZvcm1hdCBhIHRpbWVzdGFtcCBhcyBcImg6bW06c3NhXCIgKGUuZy4sIFwiMTozMDo0NXBtXCIpLlxuICogUmVwbGFjZXMgZGF0ZS1mbnMgZm9ybWF0KCkgdG8gYXZvaWQgcHVsbGluZyBpbiBhIDM5TUIgZGVwZW5kZW5jeSBmb3Igb25lIGNhbGwuXG4gKi9cbmZ1bmN0aW9uIGZvcm1hdFRpbWUoZGF0ZTogRGF0ZSk6IHN0cmluZyB7XG4gIGNvbnN0IGggPSBkYXRlLmdldEhvdXJzKCkgJSAxMiB8fCAxMlxuICBjb25zdCBtID0gU3RyaW5nKGRhdGUuZ2V0TWludXRlcygpKS5wYWRTdGFydCgyLCAnMCcpXG4gIGNvbnN0IHMgPSBTdHJpbmcoZGF0ZS5nZXRTZWNvbmRzKCkpLnBhZFN0YXJ0KDIsICcwJylcbiAgY29uc3QgYW1wbSA9IGRhdGUuZ2V0SG91cnMoKSA8IDEyID8gJ2FtJyA6ICdwbSdcbiAgcmV0dXJuIGAke2h9OiR7bX06JHtzfSR7YW1wbX1gXG59XG5cbmltcG9ydCB7IGdldFBsYXRmb3JtIH0gZnJvbSAnc3JjL3V0aWxzL3BsYXRmb3JtLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gU2FuZGJveFZpb2xhdGlvbkV4cGFuZGVkVmlldygpOiBSZWFjdE5vZGUge1xuICBjb25zdCBbdmlvbGF0aW9ucywgc2V0VmlvbGF0aW9uc10gPSB1c2VTdGF0ZTxTYW5kYm94VmlvbGF0aW9uRXZlbnRbXT4oW10pXG4gIGNvbnN0IFt0b3RhbENvdW50LCBzZXRUb3RhbENvdW50XSA9IHVzZVN0YXRlKDApXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICAvLyBUaGlzIGlzIGhhcm1sZXNzIGlmIHNhbmRib3hpbmcgaXMgbm90IGVuYWJsZWRcbiAgICBjb25zdCBzdG9yZSA9IFNhbmRib3hNYW5hZ2VyLmdldFNhbmRib3hWaW9sYXRpb25TdG9yZSgpXG4gICAgY29uc3QgdW5zdWJzY3JpYmUgPSBzdG9yZS5zdWJzY3JpYmUoXG4gICAgICAoYWxsVmlvbGF0aW9uczogU2FuZGJveFZpb2xhdGlvbkV2ZW50W10pID0+IHtcbiAgICAgICAgc2V0VmlvbGF0aW9ucyhhbGxWaW9sYXRpb25zLnNsaWNlKC0xMCkpXG4gICAgICAgIHNldFRvdGFsQ291bnQoc3RvcmUuZ2V0VG90YWxDb3VudCgpKVxuICAgICAgfSxcbiAgICApXG4gICAgcmV0dXJuIHVuc3Vic2NyaWJlXG4gIH0sIFtdKVxuXG4gIGlmICghU2FuZGJveE1hbmFnZXIuaXNTYW5kYm94aW5nRW5hYmxlZCgpIHx8IGdldFBsYXRmb3JtKCkgPT09ICdsaW51eCcpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKHRvdGFsQ291bnQgPT09IDApIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgPEJveCBtYXJnaW5MZWZ0PXswfT5cbiAgICAgICAgPFRleHQgY29sb3I9XCJwZXJtaXNzaW9uXCI+XG4gICAgICAgICAg4qeIIFNhbmRib3ggYmxvY2tlZCB7dG90YWxDb3VudH0gdG90YWx7JyAnfVxuICAgICAgICAgIHt0b3RhbENvdW50ID09PSAxID8gJ29wZXJhdGlvbicgOiAnb3BlcmF0aW9ucyd9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgICAge3Zpb2xhdGlvbnMubWFwKCh2LCBpKSA9PiAoXG4gICAgICAgIDxCb3gga2V5PXtgJHt2LnRpbWVzdGFtcC5nZXRUaW1lKCl9LSR7aX1gfSBwYWRkaW5nTGVmdD17Mn0+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7Zm9ybWF0VGltZSh2LnRpbWVzdGFtcCl9XG4gICAgICAgICAgICB7di5jb21tYW5kID8gYCAke3YuY29tbWFuZH06YCA6ICcnfSB7di5saW5lfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApKX1cbiAgICAgIDxCb3ggcGFkZGluZ0xlZnQ9ezJ9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICDigKYgc2hvd2luZyBsYXN0IHtNYXRoLm1pbigxMCwgdmlvbGF0aW9ucy5sZW5ndGgpfSBvZiB7dG90YWxDb3VudH1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBUyxLQUFLQyxTQUFTLEVBQUVDLFNBQVMsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDM0QsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUNyQyxjQUFjQyxxQkFBcUIsUUFBUSxxQ0FBcUM7QUFDaEYsU0FBU0MsY0FBYyxRQUFRLHFDQUFxQzs7QUFFcEU7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTQyxVQUFVQSxDQUFDQyxJQUFJLEVBQUVDLElBQUksQ0FBQyxFQUFFLE1BQU0sQ0FBQztFQUN0QyxNQUFNQyxDQUFDLEdBQUdGLElBQUksQ0FBQ0csUUFBUSxDQUFDLENBQUMsR0FBRyxFQUFFLElBQUksRUFBRTtFQUNwQyxNQUFNQyxDQUFDLEdBQUdDLE1BQU0sQ0FBQ0wsSUFBSSxDQUFDTSxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUNDLFFBQVEsQ0FBQyxDQUFDLEVBQUUsR0FBRyxDQUFDO0VBQ3BELE1BQU1DLENBQUMsR0FBR0gsTUFBTSxDQUFDTCxJQUFJLENBQUNTLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQ0YsUUFBUSxDQUFDLENBQUMsRUFBRSxHQUFHLENBQUM7RUFDcEQsTUFBTUcsSUFBSSxHQUFHVixJQUFJLENBQUNHLFFBQVEsQ0FBQyxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksR0FBRyxJQUFJO0VBQy9DLE9BQU8sR0FBR0QsQ0FBQyxJQUFJRSxDQUFDLElBQUlJLENBQUMsR0FBR0UsSUFBSSxFQUFFO0FBQ2hDO0FBRUEs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 \ No newline at end of file diff --git a/components/ScrollKeybindingHandler.tsx b/components/ScrollKeybindingHandler.tsx new file mode 100644 index 0000000..55c4be5 --- /dev/null +++ b/components/ScrollKeybindingHandler.tsx @@ -0,0 +1,1012 @@ +import React, { type RefObject, useEffect, useRef } from 'react'; +import { useNotifications } from '../context/notifications.js'; +import { useCopyOnSelect, useSelectionBgColor } from '../hooks/useCopyOnSelect.js'; +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js'; +import { useSelection } from '../ink/hooks/use-selection.js'; +import type { FocusMove, SelectionState } from '../ink/selection.js'; +import { isXtermJs } from '../ink/terminal.js'; +import { getClipboardPath } from '../ink/termio/osc.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- Esc needs conditional propagation based on selection state +import { type Key, useInput } from '../ink.js'; +import { useKeybindings } from '../keybindings/useKeybinding.js'; +import { logForDebugging } from '../utils/debug.js'; +type Props = { + scrollRef: RefObject; + isActive: boolean; + /** Called after every scroll action with the resulting sticky state and + * the handle (for reading scrollTop/scrollHeight post-scroll). */ + onScroll?: (sticky: boolean, handle: ScrollBoxHandle) => void; + /** Enables modal pager keys (g/G, ctrl+u/d/b/f). Only safe when there + * is no text input competing for those characters — i.e. transcript + * mode. Defaults to false. When true, G works regardless of editorMode + * and sticky state; ctrl+u/d/b/f don't conflict with kill-line/exit/ + * task:background/kill-agents (none are mounted, or they mount after + * this component so stopImmediatePropagation wins). */ + isModal?: boolean; +}; + +// Terminals send one SGR wheel event per intended row (verified in Ghostty +// src/Surface.zig: `for (0..@abs(y.delta)) |_| { mouseReport(.four, ...) }`). +// Ghostty already 3×'s discrete wheel ticks before that loop; trackpad +// precision scroll is pixels/cell_size. 1 event = 1 row intended — use it +// as the base, and ramp a multiplier when events arrive rapidly. The +// pendingScrollDelta accumulator + proportional drain in +// render-node-to-output handles smooth catch-up on big bursts. +// +// xterm.js (VS Code/Cursor/Windsurf integrated terminals) sends exactly 1 +// event per wheel notch — no pre-amplification. A separate exponential +// decay curve (below) compensates for the lower event rate, with burst +// detection and gap-dependent caps tuned to VS Code's event patterns. + +// Native terminals: hard-window linear ramp. Events closer than the window +// ramp the multiplier; idle gaps reset to `base` (default 1). Some emulators +// pre-multiply at their layer (ghostty discrete=3 sends 3 SGR events/notch; +// iTerm2 "faster scroll" similar) — base=1 is correct there. Others send 1 +// event/notch — users on those can set CLAUDE_CODE_SCROLL_SPEED=3 to match +// vim/nvim/opencode app-side defaults. We can't detect which, so knob it. +const WHEEL_ACCEL_WINDOW_MS = 40; +const WHEEL_ACCEL_STEP = 0.3; +const WHEEL_ACCEL_MAX = 6; + +// Encoder bounce debounce + wheel-mode decay curve. Worn/cheap optical +// encoders emit spurious reverse-direction ticks during fast spins — measured +// 28% of events on Boris's mouse (2026-03-17, iTerm2). Pattern is always +// flip-then-flip-back; trackpads produce ZERO flips (0/458 in same recording). +// A confirmed bounce proves a physical wheel is attached — engage the same +// exponential-decay curve the xterm.js path uses (it's already tuned), with +// a higher cap to compensate for the lower event rate (~9/sec vs VS Code's +// ~30/sec). Trackpad can't reach this path. +// +// The decay curve gives: 1st click after idle = 1 row (precision), 2nd = 10, +// 3rd = cap. Slowing down decays smoothly toward 1 — no separate idle +// threshold needed, large gaps just have m≈0 → mult→1. Wheel mode is STICKY: +// once a bounce confirms it's a mouse, the decay curve applies until an idle +// gap or trackpad-flick-burst signals a possible device switch. +const WHEEL_BOUNCE_GAP_MAX_MS = 200; // flip-back must arrive within this +// Mouse is ~9 events/sec vs VS Code's ~30 — STEP is 3× xterm.js's 5 to +// compensate. At gap=100ms (m≈0.63): one click gives 1+15*0.63≈10.5. +const WHEEL_MODE_STEP = 15; +const WHEEL_MODE_CAP = 15; +// Max mult growth per event. Without this, the +STEP*m term jumps mult +// from 1→10 in one event when wheelMode engages mid-scroll (bounce +// detected after N events in trackpad mode at mult=1). User sees scroll +// suddenly go 10× faster. Cap=3 gives 1→4→7→10→13→15 over ~0.5s at +// 9 events/sec — smooth ramp instead of a jump. Decay is unaffected +// (target1500ms OR a + * trackpad-signature burst (see burstCount). State lives in a useRef so + * it persists across device switches; the disengages handle mouse→trackpad. */ + wheelMode: boolean; + /** Consecutive <5ms events. Trackpad flick produces 100+ at <5ms; mouse + * produces ≤3 (verified in /tmp/wheel-tune.txt). 5+ in a row → trackpad + * signature → disengage wheel mode so device-switch doesn't leak mouse + * accel to trackpad. */ + burstCount: number; +}; + +/** Compute rows for one wheel event, mutating accel state. Returns 0 when + * a direction flip is deferred for bounce detection — call sites no-op on + * step=0 (scrollBy(0) is a no-op, onScroll(false) is idempotent). Exported + * for tests. */ +export function computeWheelStep(state: WheelAccelState, dir: 1 | -1, now: number): number { + if (!state.xtermJs) { + // Device-switch guard ①: idle disengage. Runs BEFORE pendingFlip resolve + // so a pending bounce (28% of last-mouse-events) doesn't bypass it via + // the real-reversal early return. state.time is either the last committed + // event OR the deferred flip — both count as "last activity". + if (state.wheelMode && now - state.time > WHEEL_MODE_IDLE_DISENGAGE_MS) { + state.wheelMode = false; + state.burstCount = 0; + state.mult = state.base; + } + + // Resolve any deferred flip BEFORE touching state.time/dir — we need the + // pre-flip state.dir to distinguish bounce (flip-back) from real reversal + // (flip persisted), and state.time (= bounce timestamp) for the gap check. + if (state.pendingFlip) { + state.pendingFlip = false; + if (dir !== state.dir || now - state.time > WHEEL_BOUNCE_GAP_MAX_MS) { + // Real reversal: new dir persisted, OR flip-back arrived too late. + // Commit. The deferred event's 1 row is lost (acceptable latency). + state.dir = dir; + state.time = now; + state.mult = state.base; + return Math.floor(state.mult); + } + // Bounce confirmed: flipped back to original dir within the window. + // state.dir/mult unchanged from pre-bounce. state.time was advanced to + // the bounce below, so gap here = flip-back interval — reflects the + // user's actual click cadence (bounce IS a physical click, just noisy). + state.wheelMode = true; + } + const gap = now - state.time; + if (dir !== state.dir && state.dir !== 0) { + // Flip. Defer — next event decides bounce vs. real reversal. Advance + // time (but NOT dir/mult): if this turns out to be a bounce, the + // confirm event's gap will be the flip-back interval, which reflects + // the user's actual click rate. The bounce IS a physical wheel click, + // just misread by the encoder — it should count toward cadence. + state.pendingFlip = true; + state.time = now; + return 0; + } + state.dir = dir; + state.time = now; + + // ─── MOUSE (wheel mode, sticky until device-switch signal) ─── + if (state.wheelMode) { + if (gap < WHEEL_BURST_MS) { + // Same-batch burst check (ported from xterm.js): iTerm2 proportional + // reporting sends 2+ SGR events for one detent when macOS gives + // delta>1. Without this, the 2nd event at gap<1ms has m≈1 → STEP*m=15 + // → one gentle click gives 1+15=16 rows. + // + // Device-switch guard ②: trackpad flick produces 100+ events at <5ms + // (measured); mouse produces ≤3. 5+ consecutive → trackpad flick. + if (++state.burstCount >= 5) { + state.wheelMode = false; + state.burstCount = 0; + state.mult = state.base; + } else { + return 1; + } + } else { + state.burstCount = 0; + } + } + // Re-check: may have disengaged above. + if (state.wheelMode) { + // xterm.js decay curve with STEP×3, higher cap. No idle threshold — + // the curve handles it (gap=1000ms → m≈0.01 → mult≈1). No frac — + // rounding loss is minor at high mult, and frac persisting across idle + // was causing off-by-one on the first click back. + const m = Math.pow(0.5, gap / WHEEL_DECAY_HALFLIFE_MS); + const cap = Math.max(WHEEL_MODE_CAP, state.base * 2); + const next = 1 + (state.mult - 1) * m + WHEEL_MODE_STEP * m; + state.mult = Math.min(cap, next, state.mult + WHEEL_MODE_RAMP); + return Math.floor(state.mult); + } + + // ─── TRACKPAD / HI-RES (native, non-wheel-mode) ─── + // Tight 40ms burst window: sub-40ms events ramp, anything slower resets. + // Trackpad flick delivers 200+ events at <20ms gaps → rails to cap 6. + // Trackpad slow swipe at 40-400ms gaps → resets every event → 1 row each. + if (gap > WHEEL_ACCEL_WINDOW_MS) { + state.mult = state.base; + } else { + const cap = Math.max(WHEEL_ACCEL_MAX, state.base * 2); + state.mult = Math.min(cap, state.mult + WHEEL_ACCEL_STEP); + } + return Math.floor(state.mult); + } + + // ─── VSCODE (xterm.js, browser wheel events) ─── + // Browser wheel events — no encoder bounce, no SGR bursts. Decay curve + // unchanged from the original tuning. Same formula shape as wheel mode + // above (keep in sync) but STEP=5 not 15 — higher event rate here. + const gap = now - state.time; + const sameDir = dir === state.dir; + state.time = now; + state.dir = dir; + // xterm.js path. Debug log shows two patterns: (a) 20-50ms gaps during + // sustained scroll (~30 Hz), (b) <5ms same-batch bursts on flicks. For + // (b) give 1 row/event — the burst count IS the acceleration, same as + // native. For (a) the decay curve gives 3-5 rows. For sparse events + // (100ms+, slow deliberate scroll) the curve gives 1-3. + if (sameDir && gap < WHEEL_BURST_MS) return 1; + if (!sameDir || gap > WHEEL_DECAY_IDLE_MS) { + // Direction reversal or long idle: start at 2 (not 1) so the first + // click after a pause moves a visible amount. Without this, idle- + // then-resume in the same direction decays to mult≈1 (1 row). + state.mult = 2; + state.frac = 0; + } else { + const m = Math.pow(0.5, gap / WHEEL_DECAY_HALFLIFE_MS); + const cap = gap >= WHEEL_DECAY_GAP_MS ? WHEEL_DECAY_CAP_SLOW : WHEEL_DECAY_CAP_FAST; + state.mult = Math.min(cap, 1 + (state.mult - 1) * m + WHEEL_DECAY_STEP * m); + } + const total = state.mult + state.frac; + const rows = Math.floor(total); + state.frac = total - rows; + return rows; +} + +/** Read CLAUDE_CODE_SCROLL_SPEED, default 1, clamp (0, 20]. + * Some terminals pre-multiply wheel events (ghostty discrete=3, iTerm2 + * "faster scroll") — base=1 is correct there. Others send 1 event/notch — + * set CLAUDE_CODE_SCROLL_SPEED=3 to match vim/nvim/opencode. We can't + * detect which kind of terminal we're in, hence the knob. Called lazily + * from initAndLogWheelAccel so globalSettings.env has loaded. */ +export function readScrollSpeedBase(): number { + const raw = process.env.CLAUDE_CODE_SCROLL_SPEED; + if (!raw) return 1; + const n = parseFloat(raw); + return Number.isNaN(n) || n <= 0 ? 1 : Math.min(n, 20); +} + +/** Initial wheel accel state. xtermJs=true selects the decay curve. + * base is the native-path baseline rows/event (default 1). */ +export function initWheelAccel(xtermJs = false, base = 1): WheelAccelState { + return { + time: 0, + mult: base, + dir: 0, + xtermJs, + frac: 0, + base, + pendingFlip: false, + wheelMode: false, + burstCount: 0 + }; +} + +// Lazy-init helper. isXtermJs() combines the TERM_PROGRAM env check + async +// XTVERSION probe — the probe may not have resolved at render time, so this +// is called on the first wheel event (>>50ms after startup) when it's settled. +// Logs detected mode once so --debug users can verify SSH detection worked. +// The renderer also calls isXtermJsHost() (in render-node-to-output) to +// select the drain algorithm — no state to pass through. +function initAndLogWheelAccel(): WheelAccelState { + const xtermJs = isXtermJs(); + const base = readScrollSpeedBase(); + logForDebugging(`wheel accel: ${xtermJs ? 'decay (xterm.js)' : 'window (native)'} · base=${base} · TERM_PROGRAM=${process.env.TERM_PROGRAM ?? 'unset'}`); + return initWheelAccel(xtermJs, base); +} + +// Drag-to-scroll: when dragging past the viewport edge, scroll by this many +// rows every AUTOSCROLL_INTERVAL_MS. Mode 1002 mouse tracking only fires on +// cell change, so a timer is needed to continue scrolling while stationary. +const AUTOSCROLL_LINES = 2; +const AUTOSCROLL_INTERVAL_MS = 50; +// Hard cap on consecutive auto-scroll ticks. If the release event is lost +// (mouse released outside terminal window — some emulators don't capture the +// pointer and drop the release), isDragging stays true and the timer would +// run until a scroll boundary. Cap bounds the damage; any new drag motion +// event restarts the count via check()→start(). +const AUTOSCROLL_MAX_TICKS = 200; // 10s @ 50ms + +/** + * Keyboard scroll navigation for the fullscreen layout's message scroll box. + * PgUp/PgDn scroll by half-viewport. Mouse wheel scrolls by a few lines. + * Scrolling breaks sticky mode; Ctrl+End re-enables it. Wheeling down at + * the bottom also re-enables sticky so new content follows naturally. + */ +export function ScrollKeybindingHandler({ + scrollRef, + isActive, + onScroll, + isModal = false +}: Props): React.ReactNode { + const selection = useSelection(); + const { + addNotification + } = useNotifications(); + // Lazy-inited on first wheel event so the XTVERSION probe (fired at + // raw-mode-enable time) has resolved by then — initializing in useRef() + // would read getWheelBase() before the probe reply arrives over SSH. + const wheelAccel = useRef(null); + function showCopiedToast(text: string): void { + // getClipboardPath reads env synchronously — predicts what setClipboard + // did (native pbcopy / tmux load-buffer / raw OSC 52) so we can tell + // the user whether paste will Just Work or needs prefix+]. + const path = getClipboardPath(); + const n = text.length; + let msg: string; + switch (path) { + case 'native': + msg = `copied ${n} chars to clipboard`; + break; + case 'tmux-buffer': + msg = `copied ${n} chars to tmux buffer · paste with prefix + ]`; + break; + case 'osc52': + msg = `sent ${n} chars via OSC 52 · check terminal clipboard settings if paste fails`; + break; + } + addNotification({ + key: 'selection-copied', + text: msg, + color: 'suggestion', + priority: 'immediate', + timeoutMs: path === 'native' ? 2000 : 4000 + }); + } + function copyAndToast(): void { + const text_0 = selection.copySelection(); + if (text_0) showCopiedToast(text_0); + } + + // Translate selection to track a keyboard page jump. Selection coords are + // screen-buffer-local; a scrollTo that moves content by N rows must also + // shift anchor+focus by N so the highlight stays on the same text (native + // terminal behavior: selection moves with content, clips at viewport + // edges). Rows that scroll out of the viewport are captured into + // scrolledOffAbove/Below before the scroll so getSelectedText still + // returns the full text. Wheel scroll (scroll:lineUp/Down via scrollBy) + // still clears — its async pendingScrollDelta drain means the actual + // delta isn't known synchronously (follow-up). + function translateSelectionForJump(s: ScrollBoxHandle, delta: number): void { + const sel = selection.getState(); + if (!sel?.anchor || !sel.focus) return; + const top = s.getViewportTop(); + const bottom = top + s.getViewportHeight() - 1; + // Only translate if the selection is ON scrollbox content. Selections + // in the footer/prompt/StickyPromptHeader are on static text — the + // scroll doesn't move what's under them. Same guard as ink.tsx's + // auto-follow translate (commit 36a8d154). + if (sel.anchor.row < top || sel.anchor.row > bottom) return; + // Cross-boundary: anchor in scrollbox, focus in footer/header. Mirror + // ink.tsx's Flag-3 guard — fall through without shifting OR capturing. + // The static endpoint pins the selection; shifting would teleport it + // into scrollbox content. + if (sel.focus.row < top || sel.focus.row > bottom) return; + const max = Math.max(0, s.getScrollHeight() - s.getViewportHeight()); + const cur = s.getScrollTop() + s.getPendingDelta(); + // Actual scroll distance after boundary clamp. jumpBy may call + // scrollToBottom when target >= max but the view can't move past max, + // so the selection shift is bounded here. + const actual = Math.max(0, Math.min(max, cur + delta)) - cur; + if (actual === 0) return; + if (actual > 0) { + // Scrolling down: content moves up. Rows at the TOP leave viewport. + // Anchor+focus shift -actual so they track the content that moved up. + selection.captureScrolledRows(top, top + actual - 1, 'above'); + selection.shiftSelection(-actual, top, bottom); + } else { + // Scrolling up: content moves down. Rows at the BOTTOM leave viewport. + const a = -actual; + selection.captureScrolledRows(bottom - a + 1, bottom, 'below'); + selection.shiftSelection(a, top, bottom); + } + } + useKeybindings({ + 'scroll:pageUp': () => { + const s_0 = scrollRef.current; + if (!s_0) return; + const d = -Math.max(1, Math.floor(s_0.getViewportHeight() / 2)); + translateSelectionForJump(s_0, d); + const sticky = jumpBy(s_0, d); + onScroll?.(sticky, s_0); + }, + 'scroll:pageDown': () => { + const s_1 = scrollRef.current; + if (!s_1) return; + const d_0 = Math.max(1, Math.floor(s_1.getViewportHeight() / 2)); + translateSelectionForJump(s_1, d_0); + const sticky_0 = jumpBy(s_1, d_0); + onScroll?.(sticky_0, s_1); + }, + 'scroll:lineUp': () => { + // Wheel: scrollBy accumulates into pendingScrollDelta, drained async + // by the renderer. captureScrolledRows can't read the outgoing rows + // before they leave (drain is non-deterministic). Clear for now. + selection.clearSelection(); + const s_2 = scrollRef.current; + // Return false (not consumed) when the ScrollBox content fits — + // scroll would be a no-op. Lets a child component's handler take + // the wheel event instead (e.g. Settings Config's list navigation + // inside the centered Modal, where the paginated slice always fits). + if (!s_2 || s_2.getScrollHeight() <= s_2.getViewportHeight()) return false; + wheelAccel.current ??= initAndLogWheelAccel(); + scrollUp(s_2, computeWheelStep(wheelAccel.current, -1, performance.now())); + onScroll?.(false, s_2); + }, + 'scroll:lineDown': () => { + selection.clearSelection(); + const s_3 = scrollRef.current; + if (!s_3 || s_3.getScrollHeight() <= s_3.getViewportHeight()) return false; + wheelAccel.current ??= initAndLogWheelAccel(); + const step = computeWheelStep(wheelAccel.current, 1, performance.now()); + const reachedBottom = scrollDown(s_3, step); + onScroll?.(reachedBottom, s_3); + }, + 'scroll:top': () => { + const s_4 = scrollRef.current; + if (!s_4) return; + translateSelectionForJump(s_4, -(s_4.getScrollTop() + s_4.getPendingDelta())); + s_4.scrollTo(0); + onScroll?.(false, s_4); + }, + 'scroll:bottom': () => { + const s_5 = scrollRef.current; + if (!s_5) return; + const max_0 = Math.max(0, s_5.getScrollHeight() - s_5.getViewportHeight()); + translateSelectionForJump(s_5, max_0 - (s_5.getScrollTop() + s_5.getPendingDelta())); + // scrollTo(max) eager-writes scrollTop so the render-phase sticky + // follow computes followDelta=0. Without this, scrollToBottom() + // alone leaves scrollTop stale → followDelta=max-stale → + // shiftSelectionForFollow applies the SAME shift we already did + // above, 2× offset. scrollToBottom() then re-enables sticky. + s_5.scrollTo(max_0); + s_5.scrollToBottom(); + onScroll?.(true, s_5); + }, + 'selection:copy': copyAndToast + }, { + context: 'Scroll', + isActive + }); + + // scroll:halfPage*/fullPage* have no default key bindings — ctrl+u/d/b/f + // all have real owners in normal mode (kill-line/exit/task:background/ + // kill-agents). Transcript mode gets them via the isModal raw useInput + // below. These handlers stay for custom rebinds only. + useKeybindings({ + 'scroll:halfPageUp': () => { + const s_6 = scrollRef.current; + if (!s_6) return; + const d_1 = -Math.max(1, Math.floor(s_6.getViewportHeight() / 2)); + translateSelectionForJump(s_6, d_1); + const sticky_1 = jumpBy(s_6, d_1); + onScroll?.(sticky_1, s_6); + }, + 'scroll:halfPageDown': () => { + const s_7 = scrollRef.current; + if (!s_7) return; + const d_2 = Math.max(1, Math.floor(s_7.getViewportHeight() / 2)); + translateSelectionForJump(s_7, d_2); + const sticky_2 = jumpBy(s_7, d_2); + onScroll?.(sticky_2, s_7); + }, + 'scroll:fullPageUp': () => { + const s_8 = scrollRef.current; + if (!s_8) return; + const d_3 = -Math.max(1, s_8.getViewportHeight()); + translateSelectionForJump(s_8, d_3); + const sticky_3 = jumpBy(s_8, d_3); + onScroll?.(sticky_3, s_8); + }, + 'scroll:fullPageDown': () => { + const s_9 = scrollRef.current; + if (!s_9) return; + const d_4 = Math.max(1, s_9.getViewportHeight()); + translateSelectionForJump(s_9, d_4); + const sticky_4 = jumpBy(s_9, d_4); + onScroll?.(sticky_4, s_9); + } + }, { + context: 'Scroll', + isActive + }); + + // Modal pager keys — transcript mode only. less/tmux copy-mode lineage: + // ctrl+u/d (half-page), ctrl+b/f (full-page), g/G (top/bottom). Tom's + // resolution (2026-03-15): "In ctrl-o mode, ctrl-u, ctrl-d, etc. should + // roughly just work!" — transcript is the copy-mode container. + // + // Safe because the conflicting handlers aren't reachable here: + // ctrl+u → kill-line, ctrl+d → exit: PromptInput not mounted + // ctrl+b → task:background: SessionBackgroundHint not mounted + // ctrl+f → chat:killAgents moved to ctrl+x ctrl+k; no conflict + // g/G → printable chars: no prompt to eat them, no vim/sticky gate needed + // + // TODO(search): `/`, n/N — build on Richard Kim's d94b07add4 (branch + // claude/jump-recent-message-CEPcq). getItemY Yoga-walk + computeOrigin + + // anchorY already solve scroll-to-index. jumpToPrevTurn is the n/N + // template. Single-shot via OVERSCAN_ROWS=80; two-phase was tried and + // abandoned (❯ oscillation). See team memory scroll-copy-mode-design.md. + useInput((input, key, event) => { + const s_10 = scrollRef.current; + if (!s_10) return; + const sticky_5 = applyModalPagerAction(s_10, modalPagerAction(input, key), d_5 => translateSelectionForJump(s_10, d_5)); + if (sticky_5 === null) return; + onScroll?.(sticky_5, s_10); + event.stopImmediatePropagation(); + }, { + isActive: isActive && isModal + }); + + // Esc clears selection; any other keystroke also clears it (matches + // native terminal behavior where selection disappears on input). + // Ctrl+C copies when a selection exists — needed on legacy terminals + // where ctrl+shift+c sends the same byte (\x03, shift is lost) and + // cmd+c never reaches the pty (terminal intercepts it for Edit > Copy). + // Handled via raw useInput so we can conditionally consume: Esc/Ctrl+C + // only stop propagation when a selection exists, letting them still work + // for cancel-request / interrupt otherwise. Other keys never stop + // propagation — they're observed to clear selection as a side-effect. + // The selection:copy keybinding (ctrl+shift+c / cmd+c) registers above + // via useKeybindings and consumes its event before reaching here. + useInput((input_0, key_0, event_0) => { + if (!selection.hasSelection()) return; + if (key_0.escape) { + selection.clearSelection(); + event_0.stopImmediatePropagation(); + return; + } + if (key_0.ctrl && !key_0.shift && !key_0.meta && input_0 === 'c') { + copyAndToast(); + event_0.stopImmediatePropagation(); + return; + } + const move = selectionFocusMoveForKey(key_0); + if (move) { + selection.moveFocus(move); + event_0.stopImmediatePropagation(); + return; + } + if (shouldClearSelectionOnKey(key_0)) { + selection.clearSelection(); + } + }, { + isActive + }); + useDragToScroll(scrollRef, selection, isActive, onScroll); + useCopyOnSelect(selection, isActive, showCopiedToast); + useSelectionBgColor(selection); + return null; +} + +/** + * Auto-scroll the ScrollBox when the user drags a selection past its top or + * bottom edge. The anchor is shifted in the opposite direction so it stays + * on the same content (content that was at viewport row N is now at row N±d + * after scrolling by d). Focus stays at the mouse position (edge row). + * + * Selection coords are screen-buffer-local, so the anchor is clamped to the + * viewport bounds once the original content scrolls out. To preserve the full + * selection, rows about to scroll out are captured into scrolledOffAbove/ + * scrolledOffBelow before each scroll step and joined back in by + * getSelectedText. + */ +function useDragToScroll(scrollRef: RefObject, selection: ReturnType, isActive: boolean, onScroll: Props['onScroll']): void { + const timerRef = useRef(null); + const dirRef = useRef<-1 | 0 | 1>(0); // -1 scrolling up, +1 down, 0 idle + // Survives stop() — reset only on drag-finish. See check() for semantics. + const lastScrolledDirRef = useRef<-1 | 0 | 1>(0); + const ticksRef = useRef(0); + // onScroll may change identity every render (if not memoized by caller). + // Read through a ref so the effect doesn't re-subscribe and kill the timer + // on each scroll-induced re-render. + const onScrollRef = useRef(onScroll); + onScrollRef.current = onScroll; + useEffect(() => { + if (!isActive) return; + function stop(): void { + dirRef.current = 0; + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + } + } + function tick(): void { + const sel = selection.getState(); + const s = scrollRef.current; + const dir = dirRef.current; + // dir === 0 defends against a stale interval (start() may have set one + // after the immediate tick already called stop() at a scroll boundary). + // ticks cap defends against a lost release event (mouse released + // outside terminal window) leaving isDragging stuck true. + if (!sel?.isDragging || !sel.focus || !s || dir === 0 || ++ticksRef.current > AUTOSCROLL_MAX_TICKS) { + stop(); + return; + } + // scrollBy accumulates into pendingScrollDelta; the screen buffer + // doesn't update until the next render drains it. If a previous + // tick's scroll hasn't drained yet, captureScrolledRows would read + // stale content (same rows as last tick → duplicated in the + // accumulator AND missing the rows that actually scrolled out). + // Skip this tick; the 50ms interval will retry after Ink's 16ms + // render catches up. Also prevents shiftAnchor from desyncing. + if (s.getPendingDelta() !== 0) return; + const top = s.getViewportTop(); + const bottom = top + s.getViewportHeight() - 1; + // Clamp anchor within [top, bottom]. Not [0, bottom]: the ScrollBox + // padding row at 0 would produce a blank line between scrolledOffAbove + // and the on-screen content in getSelectedText. The padding-row + // highlight was a minor visual nicety; text correctness wins. + if (dir < 0) { + if (s.getScrollTop() <= 0) { + stop(); + return; + } + // Scrolling up: content moves down in viewport, so anchor row +N. + // Clamp to actual scroll distance so anchor stays in sync when near + // the top boundary (renderer clamps scrollTop to 0 on drain). + const actual = Math.min(AUTOSCROLL_LINES, s.getScrollTop()); + // Capture rows about to scroll out the BOTTOM before scrollBy + // overwrites them. Only rows inside the selection are captured + // (captureScrolledRows intersects with selection bounds). + selection.captureScrolledRows(bottom - actual + 1, bottom, 'below'); + selection.shiftAnchor(actual, 0, bottom); + s.scrollBy(-AUTOSCROLL_LINES); + } else { + const max = Math.max(0, s.getScrollHeight() - s.getViewportHeight()); + if (s.getScrollTop() >= max) { + stop(); + return; + } + // Scrolling down: content moves up in viewport, so anchor row -N. + // Clamp to actual scroll distance so anchor stays in sync when near + // the bottom boundary (renderer clamps scrollTop to max on drain). + const actual_0 = Math.min(AUTOSCROLL_LINES, max - s.getScrollTop()); + // Capture rows about to scroll out the TOP. + selection.captureScrolledRows(top, top + actual_0 - 1, 'above'); + selection.shiftAnchor(-actual_0, top, bottom); + s.scrollBy(AUTOSCROLL_LINES); + } + onScrollRef.current?.(false, s); + } + function start(dir_0: -1 | 1): void { + // Record BEFORE early-return: the empty-accumulator reset in check() + // may have zeroed this during the pre-crossing phase (accumulators + // empty until the anchor row enters the capture range). Re-record + // on every call so the corruption is instantly healed. + lastScrolledDirRef.current = dir_0; + if (dirRef.current === dir_0) return; // already going this way + stop(); + dirRef.current = dir_0; + ticksRef.current = 0; + tick(); + // tick() may have hit a scroll boundary and called stop() (dir reset to + // 0). Only start the interval if we're still going — otherwise the + // interval would run forever with dir === 0 doing nothing useful. + if (dirRef.current === dir_0) { + timerRef.current = setInterval(tick, AUTOSCROLL_INTERVAL_MS); + } + } + + // Re-evaluated on every selection change (start/drag/finish/clear). + // Drives drag-to-scroll autoscroll when the drag leaves the viewport. + // Prior versions broke sticky here on drag-start to prevent selection + // drift during streaming — ink.tsx now translates selection coords by + // the follow delta instead (native terminal behavior: view keeps + // scrolling, highlight walks up with the text). Keeping sticky also + // avoids useVirtualScroll's tail-walk → forward-walk phantom growth. + function check(): void { + const s_0 = scrollRef.current; + if (!s_0) { + stop(); + return; + } + const top_0 = s_0.getViewportTop(); + const bottom_0 = top_0 + s_0.getViewportHeight() - 1; + const sel_0 = selection.getState(); + // Pass the LAST-scrolled direction (not dirRef) so the anchor guard is + // bypassed after shiftAnchor has clamped anchor toward row 0. Using + // lastScrolledDirRef (survives stop()) lets autoscroll resume after a + // brief mouse dip into the viewport. Same-direction only — a mouse + // jump from below-bottom to above-top must stop, since reversing while + // the scrolledOffAbove/Below accumulators hold the prior direction's + // rows would duplicate text in getSelectedText. Reset on drag-finish + // OR when both accumulators are empty: startSelection clears them + // (selection.ts), so a new drag after a lost-release (isDragging + // stuck true, the reason AUTOSCROLL_MAX_TICKS exists) still resets. + // Safe: start() below re-records lastScrolledDirRef before its + // early-return, so a mid-scroll reset here is instantly undone. + if (!sel_0?.isDragging || sel_0.scrolledOffAbove.length === 0 && sel_0.scrolledOffBelow.length === 0) { + lastScrolledDirRef.current = 0; + } + const dir_1 = dragScrollDirection(sel_0, top_0, bottom_0, lastScrolledDirRef.current); + if (dir_1 === 0) { + // Blocked reversal: focus jumped to the opposite edge (off-window + // drag return, fast flick). handleSelectionDrag already moved focus + // past the anchor, flipping selectionBounds — the accumulator is + // now orphaned (holds rows on the wrong side). Clear it so + // getSelectedText matches the visible highlight. + if (lastScrolledDirRef.current !== 0 && sel_0?.focus) { + const want = sel_0.focus.row < top_0 ? -1 : sel_0.focus.row > bottom_0 ? 1 : 0; + if (want !== 0 && want !== lastScrolledDirRef.current) { + sel_0.scrolledOffAbove = []; + sel_0.scrolledOffBelow = []; + sel_0.scrolledOffAboveSW = []; + sel_0.scrolledOffBelowSW = []; + lastScrolledDirRef.current = 0; + } + } + stop(); + } else start(dir_1); + } + const unsubscribe = selection.subscribe(check); + return () => { + unsubscribe(); + stop(); + lastScrolledDirRef.current = 0; + }; + }, [isActive, scrollRef, selection]); +} + +/** + * Compute autoscroll direction for a drag selection relative to the ScrollBox + * viewport. Returns 0 when not dragging, anchor/focus missing, or the anchor + * is outside the viewport — a multi-click or drag that started in the input + * area must not commandeer the message scroll (double-click in the input area + * while scrolled up previously corrupted the anchor via shiftAnchor and + * spuriously scrolled the message history every 50ms until release). + * + * alreadyScrollingDir bypasses the anchor-in-viewport guard once autoscroll + * is active (shiftAnchor legitimately clamps the anchor toward row 0, below + * `top`) but only allows SAME-direction continuation. If the focus jumps to + * the opposite edge (below→above or above→below — possible with a fast flick + * or off-window drag since mode 1002 reports on cell change, not per cell), + * returns 0 to stop — reversing without clearing scrolledOffAbove/Below + * would duplicate captured rows when they scroll back on-screen. + */ +export function dragScrollDirection(sel: SelectionState | null, top: number, bottom: number, alreadyScrollingDir: -1 | 0 | 1 = 0): -1 | 0 | 1 { + if (!sel?.isDragging || !sel.anchor || !sel.focus) return 0; + const row = sel.focus.row; + const want: -1 | 0 | 1 = row < top ? -1 : row > bottom ? 1 : 0; + if (alreadyScrollingDir !== 0) { + // Same-direction only. Focus on the opposite side, or back inside the + // viewport, stops the scroll — captured rows stay in scrolledOffAbove/ + // Below but never scroll back on-screen, so getSelectedText is correct. + return want === alreadyScrollingDir ? want : 0; + } + // Anchor must be inside the viewport for us to own this drag. If the + // user started selecting in the input box or header, autoscrolling the + // message history is surprising and corrupts the anchor via shiftAnchor. + if (sel.anchor.row < top || sel.anchor.row > bottom) return 0; + return want; +} + +// Keyboard page jumps: scrollTo() writes scrollTop directly and clears +// pendingScrollDelta — one frame, no drain. scrollBy() accumulates into +// pendingScrollDelta which the renderer drains over several frames +// (render-node-to-output.ts drainProportional/drainAdaptive) — correct for +// wheel smoothness, wrong for PgUp/ctrl+u where the user expects a snap. +// Target is relative to scrollTop+pendingDelta so a jump mid-wheel-burst +// lands where the wheel was heading. +export function jumpBy(s: ScrollBoxHandle, delta: number): boolean { + const max = Math.max(0, s.getScrollHeight() - s.getViewportHeight()); + const target = s.getScrollTop() + s.getPendingDelta() + delta; + if (target >= max) { + // Eager-write scrollTop so follow-scroll sees followDelta=0. Callers + // that ran translateSelectionForJump already shifted; scrollToBottom() + // alone would double-shift via the render-phase sticky follow. + s.scrollTo(max); + s.scrollToBottom(); + return true; + } + s.scrollTo(Math.max(0, target)); + return false; +} + +// Wheel-down past maxScroll re-enables sticky so wheeling at the bottom +// naturally re-pins (matches typical chat-app behavior). Returns the +// resulting sticky state so callers can propagate it. +function scrollDown(s: ScrollBoxHandle, amount: number): boolean { + const max = Math.max(0, s.getScrollHeight() - s.getViewportHeight()); + // Include pendingDelta: scrollBy accumulates into pendingScrollDelta + // without updating scrollTop, so getScrollTop() alone is stale within + // a batch of wheel events. Without this, wheeling to the bottom never + // re-enables sticky scroll. + const effectiveTop = s.getScrollTop() + s.getPendingDelta(); + if (effectiveTop + amount >= max) { + s.scrollToBottom(); + return true; + } + s.scrollBy(amount); + return false; +} + +// Wheel-up past scrollTop=0 clamps via scrollTo(0), clearing +// pendingScrollDelta so aggressive wheel bursts (e.g. MX Master free-spin) +// don't accumulate an unbounded negative delta. Without this clamp, +// useVirtualScroll's [effLo, effHi] span grows past what MAX_MOUNTED_ITEMS +// can cover and intermediate drain frames render at scrollTops with no +// mounted children — blank viewport. +export function scrollUp(s: ScrollBoxHandle, amount: number): void { + // Include pendingDelta: scrollBy accumulates without updating scrollTop, + // so getScrollTop() alone is stale within a batch of wheel events. + const effectiveTop = s.getScrollTop() + s.getPendingDelta(); + if (effectiveTop - amount <= 0) { + s.scrollTo(0); + return; + } + s.scrollBy(-amount); +} +export type ModalPagerAction = 'lineUp' | 'lineDown' | 'halfPageUp' | 'halfPageDown' | 'fullPageUp' | 'fullPageDown' | 'top' | 'bottom'; + +/** + * Maps a keystroke to a modal pager action. Exported for testing. + * Returns null for keys the modal pager doesn't handle (they fall through). + * + * ctrl+u/d/b/f are the less-lineage bindings. g/G are bare letters (only + * safe when no prompt is mounted). G arrives as input='G' shift=false on + * legacy terminals, or input='g' shift=true on kitty-protocol terminals. + * Lowercase g needs the !shift guard so it doesn't also match kitty-G. + * + * Key-repeat: stdin coalesces held-down printables into one multi-char + * string (e.g. 'ggg'). Only uniform-char batches are handled — mixed input + * like 'gG' isn't key-repeat. g/G are idempotent absolute jumps, so the + * count is irrelevant (consuming the batch just prevents it from leaking + * to the selection-clear-on-printable handler). + */ +export function modalPagerAction(input: string, key: Pick): ModalPagerAction | null { + if (key.meta) return null; + // Special keys first — arrows/home/end arrive with empty or junk input, + // so these must be checked before any input-string logic. shift is + // reserved for selection-extend (selectionFocusMoveForKey); ctrl+home/end + // already has a useKeybindings route to scroll:top/bottom. + if (!key.ctrl && !key.shift) { + if (key.upArrow) return 'lineUp'; + if (key.downArrow) return 'lineDown'; + if (key.home) return 'top'; + if (key.end) return 'bottom'; + } + if (key.ctrl) { + if (key.shift) return null; + switch (input) { + case 'u': + return 'halfPageUp'; + case 'd': + return 'halfPageDown'; + case 'b': + return 'fullPageUp'; + case 'f': + return 'fullPageDown'; + // emacs-style line scroll (less accepts both ctrl+n/p and ctrl+e/y). + // Works during search nav — fine-adjust after a jump without + // leaving modal. No !searchOpen gate on this useInput's isActive. + case 'n': + return 'lineDown'; + case 'p': + return 'lineUp'; + default: + return null; + } + } + // Bare letters. Key-repeat batches: only act on uniform runs. + const c = input[0]; + if (!c || input !== c.repeat(input.length)) return null; + // kitty sends G as input='g' shift=true; legacy as 'G' shift=false. + // Check BEFORE the shift-gate so both hit 'bottom'. + if (c === 'G' || c === 'g' && key.shift) return 'bottom'; + if (key.shift) return null; + switch (c) { + case 'g': + return 'top'; + // j/k re-added per Tom Mar 18 — reversal of Mar 16 removal. Works + // during search nav (fine-adjust after n/N lands) since isModal is + // independent of searchOpen. + case 'j': + return 'lineDown'; + case 'k': + return 'lineUp'; + // less: space = page down, b = page up. ctrl+b already maps above; + // bare b is the less-native version. + case ' ': + return 'fullPageDown'; + case 'b': + return 'fullPageUp'; + default: + return null; + } +} + +/** + * Applies a modal pager action to a ScrollBox. Returns the resulting sticky + * state, or null if the action was null (nothing to do — caller should fall + * through). Calls onBeforeJump(delta) before scrolling so the caller can + * translate the text selection by the scroll delta (capture outgoing rows, + * shift anchor+focus) instead of clearing it. Exported for testing. + */ +export function applyModalPagerAction(s: ScrollBoxHandle, act: ModalPagerAction | null, onBeforeJump: (delta: number) => void): boolean | null { + switch (act) { + case null: + return null; + case 'lineUp': + case 'lineDown': + { + const d = act === 'lineDown' ? 1 : -1; + onBeforeJump(d); + return jumpBy(s, d); + } + case 'halfPageUp': + case 'halfPageDown': + { + const half = Math.max(1, Math.floor(s.getViewportHeight() / 2)); + const d = act === 'halfPageDown' ? half : -half; + onBeforeJump(d); + return jumpBy(s, d); + } + case 'fullPageUp': + case 'fullPageDown': + { + const page = Math.max(1, s.getViewportHeight()); + const d = act === 'fullPageDown' ? page : -page; + onBeforeJump(d); + return jumpBy(s, d); + } + case 'top': + onBeforeJump(-(s.getScrollTop() + s.getPendingDelta())); + s.scrollTo(0); + return false; + case 'bottom': + { + const max = Math.max(0, s.getScrollHeight() - s.getViewportHeight()); + onBeforeJump(max - (s.getScrollTop() + s.getPendingDelta())); + // Eager-write scrollTop before scrollToBottom — same double-shift + // fix as scroll:bottom and jumpBy's max branch. + s.scrollTo(max); + s.scrollToBottom(); + return true; + } + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlZk9iamVjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZU5vdGlmaWNhdGlvbnMiLCJ1c2VDb3B5T25TZWxlY3QiLCJ1c2VTZWxlY3Rpb25CZ0NvbG9yIiwiU2Nyb2xsQm94SGFuZGxlIiwidXNlU2VsZWN0aW9uIiwiRm9jdXNNb3ZlIiwiU2VsZWN0aW9uU3RhdGUiLCJpc1h0ZXJtSnMiLCJnZXRDbGlwYm9hcmRQYXRoIiwiS2V5IiwidXNlSW5wdXQiLCJ1c2VLZXliaW5kaW5ncyIsImxvZ0ZvckRlYnVnZ2luZyIsIlByb3BzIiwic2Nyb2xsUmVmIiwiaXNBY3RpdmUiLCJvblNjcm9sbCIsInN0aWNreSIsImhhbmRsZSIsImlzTW9kYWwiLCJXSEVFTF9BQ0NFTF9XSU5ET1dfTVMiLCJXSEVFTF9BQ0NFTF9TVEVQIiwiV0hFRUxfQUNDRUxfTUFYIiwiV0hFRUxfQk9VTkNFX0dBUF9NQVhfTVMiLCJXSEVFTF9NT0RFX1NURVAiLCJXSEVFTF9NT0RFX0NBUCIsIldIRUVMX01PREVfUkFNUCIsIldIRUVMX01PREVfSURMRV9ESVNFTkdBR0VfTVMiLCJXSEVFTF9ERUNBWV9IQUxGTElGRV9NUyIsIldIRUVMX0RFQ0FZX1NURVAiLCJXSEVFTF9CVVJTVF9NUyIsIldIRUVMX0RFQ0FZX0dBUF9NUyIsIldIRUVMX0RFQ0FZX0NBUF9TTE9XIiwiV0hFRUxfREVDQVlfQ0FQX0ZBU1QiLCJXSEVFTF9ERUNBWV9JRExFX01TIiwic2hvdWxkQ2xlYXJTZWxlY3Rpb25PbktleSIsImtleSIsIndoZWVsVXAiLCJ3aGVlbERvd24iLCJpc05hdiIsImxlZnRBcnJvdyIsInJpZ2h0QXJyb3ciLCJ1cEFycm93IiwiZG93bkFycm93IiwiaG9tZSIsImVuZCIsInBhZ2VVcCIsInBhZ2VEb3duIiwic2hpZnQiLCJtZXRhIiwic3VwZXIiLCJzZWxlY3Rpb25Gb2N1c01vdmVGb3JLZXkiLCJXaGVlbEFjY2VsU3RhdGUiLCJ0aW1lIiwibXVsdCIsImRpciIsInh0ZXJtSnMiLCJmcmFjIiwiYmFzZSIsInBlbmRpbmdGbGlwIiwid2hlZWxNb2RlIiwiYnVyc3RDb3VudCIsImNvbXB1dGVXaGVlbFN0ZXAiLCJzdGF0ZSIsIm5vdyIsIk1hdGgiLCJmbG9vciIsImdhcCIsIm0iLCJwb3ciLCJjYXAiLCJtYXgiLCJuZXh0IiwibWluIiwic2FtZURpciIsInRvdGFsIiwicm93cyIsInJlYWRTY3JvbGxTcGVlZEJhc2UiLCJyYXciLCJwcm9jZXNzIiwiZW52IiwiQ0xBVURFX0NPREVfU0NST0xMX1NQRUVEIiwibiIsInBhcnNlRmxvYXQiLCJOdW1iZXIiLCJpc05hTiIsImluaXRXaGVlbEFjY2VsIiwiaW5pdEFuZExvZ1doZWVsQWNjZWwiLCJURVJNX1BST0dSQU0iLCJBVVRPU0NST0xMX0xJTkVTIiwiQVVUT1NDUk9MTF9JTlRFUlZBTF9NUyIsIkFVVE9TQ1JPTExfTUFYX1RJQ0tTIiwiU2Nyb2xsS2V5YmluZGluZ0hhbmRsZXIiLCJSZWFjdE5vZGUiLCJzZWxlY3Rpb24iLCJhZGROb3RpZmljYXRpb24iLCJ3aGVlbEFjY2VsIiwic2hvd0NvcGllZFRvYXN0IiwidGV4dCIsInBhdGgiLCJsZW5ndGgiLCJtc2ciLCJjb2xvciIsInByaW9yaXR5IiwidGltZW91dE1zIiwiY29weUFuZFRvYXN0IiwiY29weVNlbGVjdGlvbiIsInRyYW5zbGF0ZVNlbGVjdGlvbkZvckp1bXAiLCJzIiwiZGVsdGEiLCJzZWwiLCJnZXRTdGF0ZSIsImFuY2hvciIsImZvY3VzIiwidG9wIiwiZ2V0Vmlld3BvcnRUb3AiLCJib3R0b20iLCJnZXRWaWV3cG9ydEhlaWdodCIsInJvdyIsImdldFNjcm9sbEhlaWdodCIsImN1ciIsImdldFNjcm9sbFRvcCIsImdldFBlbmRpbmdEZWx0YSIsImFjdHVhbCIsImNhcHR1cmVTY3JvbGxlZFJvd3MiLCJzaGlmdFNlbGVjdGlvbiIsImEiLCJzY3JvbGw6cGFnZVVwIiwiY3VycmVudCIsImQiLCJqdW1wQnkiLCJzY3JvbGw6cGFnZURvd24iLCJzY3JvbGw6bGluZVVwIiwiY2xlYXJTZWxlY3Rpb24iLCJzY3JvbGxVcCIsInBlcmZvcm1hbmNlIiwic2Nyb2xsOmxpbmVEb3duIiwic3RlcCIsInJlYWNoZWRCb3R0b20iLCJzY3JvbGxEb3duIiwic2Nyb2xsOnRvcCIsInNjcm9sbFRvIiwic2Nyb2xsOmJvdHRvbSIsInNjcm9sbFRvQm90dG9tIiwiY29udGV4dCIsInNjcm9sbDpoYWxmUGFnZVVwIiwic2Nyb2xsOmhhbGZQYWdlRG93biIsInNjcm9sbDpmdWxsUGFnZVVwIiwic2Nyb2xsOmZ1bGxQYWdlRG93biIsImlucHV0IiwiZXZlbnQiLCJhcHBseU1vZGFsUGFnZXJBY3Rpb24iLCJtb2RhbFBhZ2VyQWN0aW9uIiwic3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uIiwiaGFzU2VsZWN0aW9uIiwiZXNjYXBlIiwiY3RybCIsIm1vdmUiLCJtb3ZlRm9jdXMiLCJ1c2VEcmFnVG9TY3JvbGwiLCJSZXR1cm5UeXBlIiwidGltZXJSZWYiLCJOb2RlSlMiLCJUaW1lb3V0IiwiZGlyUmVmIiwibGFzdFNjcm9sbGVkRGlyUmVmIiwidGlja3NSZWYiLCJvblNjcm9sbFJlZiIsInN0b3AiLCJjbGVhckludGVydmFsIiwidGljayIsImlzRHJhZ2dpbmciLCJzaGlmdEFuY2hvciIsInNjcm9sbEJ5Iiwic3RhcnQiLCJzZXRJbnRlcnZhbCIsImNoZWNrIiwic2Nyb2xsZWRPZmZBYm92ZSIsInNjcm9sbGVkT2ZmQmVsb3ciLCJkcmFnU2Nyb2xsRGlyZWN0aW9uIiwid2FudCIsInNjcm9sbGVkT2ZmQWJvdmVTVyIsInNjcm9sbGVkT2ZmQmVsb3dTVyIsInVuc3Vic2NyaWJlIiwic3Vic2NyaWJlIiwiYWxyZWFkeVNjcm9sbGluZ0RpciIsInRhcmdldCIsImFtb3VudCIsImVmZmVjdGl2ZVRvcCIsIk1vZGFsUGFnZXJBY3Rpb24iLCJQaWNrIiwiYyIsInJlcGVhdCIsImFjdCIsIm9uQmVmb3JlSnVtcCIsImhhbGYiLCJwYWdlIl0sInNvdXJjZXMiOlsiU2Nyb2xsS2V5YmluZGluZ0hhbmRsZXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB0eXBlIFJlZk9iamVjdCwgdXNlRWZmZWN0LCB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZU5vdGlmaWNhdGlvbnMgfSBmcm9tICcuLi9jb250ZXh0L25vdGlmaWNhdGlvbnMuanMnXG5pbXBvcnQge1xuICB1c2VDb3B5T25TZWxlY3QsXG4gIHVzZVNlbGVjdGlvbkJnQ29sb3IsXG59IGZyb20gJy4uL2hvb2tzL3VzZUNvcHlPblNlbGVjdC5qcydcbmltcG9ydCB0eXBlIHsgU2Nyb2xsQm94SGFuZGxlIH0gZnJvbSAnLi4vaW5rL2NvbXBvbmVudHMvU2Nyb2xsQm94LmpzJ1xuaW1wb3J0IHsgdXNlU2VsZWN0aW9uIH0gZnJvbSAnLi4vaW5rL2hvb2tzL3VzZS1zZWxlY3Rpb24uanMnXG5pbXBvcnQgdHlwZSB7IEZvY3VzTW92ZSwgU2VsZWN0aW9uU3RhdGUgfSBmcm9tICcuLi9pbmsvc2VsZWN0aW9uLmpzJ1xuaW1wb3J0IHsgaXNYdGVybUpzIH0gZnJvbSAnLi4vaW5rL3Rlcm1pbmFsLmpzJ1xuaW1wb3J0IHsgZ2V0Q2xpcGJvYXJkUGF0aCB9IGZyb20gJy4uL2luay90ZXJtaW8vb3NjLmpzJ1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9wcmVmZXItdXNlLWtleWJpbmRpbmdzIC0tIEVzYyBuZWVkcyBjb25kaXRpb25hbCBwcm9wYWdhdGlvbiBiYXNlZCBvbiBzZWxlY3Rpb24gc3RhdGVcbmltcG9ydCB7IHR5cGUgS2V5LCB1c2VJbnB1dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmdzIH0gZnJvbSAnLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uL3V0aWxzL2RlYnVnLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBzY3JvbGxSZWY6IFJlZk9iamVjdDxTY3JvbGxCb3hIYW5kbGUgfCBudWxsPlxuICBpc0FjdGl2ZTogYm9vbGVhblxuICAvKiogQ2FsbGVkIGFmdGVyIGV2ZXJ5IHNjcm9sbCBhY3Rpb24gd2l0aCB0aGUgcmVzdWx0aW5nIHN0aWNreSBzdGF0ZSBhbmRcbiAgICogIHRoZSBoYW5kbGUgKGZvciByZWFkaW5nIHNjcm9sbFRvcC9zY3JvbGxIZWlnaHQgcG9zdC1zY3JvbGwpLiAqL1xuICBvblNjcm9sbD86IChzdGlja3k6IGJvb2xlYW4sIGhhbmRsZTogU2Nyb2xsQm94SGFuZGxlKSA9PiB2b2lkXG4gIC8qKiBFbmFibGVzIG1vZGFsIHBhZ2VyIGtleXMgKGcvRywgY3RybCt1L2QvYi9mKS4gT25seSBzYWZlIHdoZW4gdGhlcmVcbiAgICogIGlzIG5vIHRleHQgaW5wdXQgY29tcGV0aW5nIGZvciB0aG9zZSBjaGFyYWN0ZXJzIOKAlCBpLmUuIHRyYW5zY3JpcHRcbiAgICogIG1vZGUuIERlZmF1bHRzIHRvIGZhbHNlLiBXaGVuIHRydWUsIEcgd29ya3MgcmVnYXJkbGVzcyBvZiBlZGl0b3JNb2RlXG4gICAqICBhbmQgc3RpY2t5IHN0YXRlOyBjdHJsK3UvZC9iL2YgZG9uJ3QgY29uZmxpY3Qgd2l0aCBraWxsLWxpbmUvZXhpdC9cbiAgICogIHRhc2s6YmFja2dyb3VuZC9raWxsLWFnZW50cyAobm9uZSBhcmUgbW91bnRlZCwgb3IgdGhleSBtb3VudCBhZnRlclxuICAgKiAgdGhpcyBjb21wb25lbnQgc28gc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uIHdpbnMpLiAqL1xuICBpc01vZGFsPzogYm9vbGVhblxufVxuXG4vLyBUZXJtaW5hbHMgc2VuZCBvbmUgU0dSIHdoZWVsIGV2ZW50IHBlciBpbnRlbmRlZCByb3cgKHZlcmlmaWVkIGluIEdob3N0dHlcbi8vIHNyYy9TdXJmYWNlLnppZzogYGZvciAoMC4uQGFicyh5LmRlbHRhKSkgfF98IHsgbW91c2VSZXBvcnQoLmZvdXIsIC4uLikgfWApLlxuLy8gR2hvc3R0eSBhbHJlYWR5IDPDlydzIGRpc2NyZXRlIHdoZWVsIHRpY2tzIGJlZm9yZSB0aGF0IGxvb3A7IHRyYWNrcGFkXG4vLyBwcmVjaXNpb24gc2Nyb2xsIGlzIHBpeGVscy9jZWxsX3NpemUuIDEgZXZlbnQgPSAxIHJvdyBpbnRlbmRlZCDigJQgdXNlIGl0XG4vLyBhcyB0aGUgYmFzZSwgYW5kIHJhbXAgYSBtdWx0aXBsaWVyIHdoZW4gZXZlbnRzIGFycml2ZSByYXBpZGx5LiBUaGVcbi8vIHBlbmRpbmdTY3JvbGxEZWx0YSBhY2N1bXVsYXRvciArIHByb3BvcnRpb25hbCBkcmFpbiBpblxuLy8gcmVuZGVyLW5vZGUtdG8tb3V0cHV0IGhhbmRsZXMgc21vb3RoIGNhdGNoLXVwIG9uIGJpZyBidXJzdHMuXG4vL1xuLy8geHRlcm0uanMgKFZTIENvZGUvQ3Vyc29yL1dpbmRzdXJmIGludGVncmF0ZWQgdGVybWluYWxzKSBzZW5kcyBleGFjdGx5IDFcbi8vIGV2ZW50IHBlciB3aGVlbCBub3RjaCDigJQgbm8gcHJlLWFtcGxpZmljYXRpb24uIEEgc2VwYXJhdGUgZXhwb25lbnRpYWxcbi8vIGRlY2F5IGN1cnZlIChiZWxvdykgY29tcGVuc2F0ZXMgZm9yIHRoZSBsb3dlciBldmVudCByYXRlLCB3aXRoIGJ1cnN0XG4vLyBkZXRlY3Rpb24gYW5kIGdhcC1kZXBlbmRlbnQgY2FwcyB0dW5lZCB0byBWUyBDb2RlJ3MgZXZlbnQgcGF0dGVybnMuXG5cbi8vIE5hdGl2ZSB0ZXJtaW5hbHM6IGhhcmQtd2luZG93IGxpbmVhciByYW1wLiBFdmVudHMgY2xvc2VyIHRoYW4gdGhlIHdpbmRvd1xuLy8gcmFtcCB0aGUgbXVsdGlwbGllcjsgaWRsZSBnYXBzIHJlc2V0IHRvIGBiYXNlYCAoZGVmYXVsdCAxKS4gU29tZSBlbXVsYXRvcnNcbi8vIHByZS1tdWx0aXBseSBhdCB0aGVpciBsYXllciAoZ2hvc3R0eSBkaXNjcmV0ZT0zIHNlbmRzIDMgU0dSIGV2ZW50cy9ub3RjaDtcbi8vIGlUZXJtMiBcImZhc3RlciBzY3JvbGxcIiBzaW1pbGFyKSDigJQgYmFzZT0xIGlzIGNvcnJlY3QgdGhlcmUuIE90aGVycyBzZW5kIDFcbi8vIGV2ZW50L25vdGNoIOKAlCB1c2VycyBvbiB0aG9zZSBjYW4gc2V0IENMQVVERV9DT0RFX1NDUk9MTF9TUEVFRD0zIHRvIG1hdGNoXG4vLyB2aW0vbnZpbS9vcGVuY29kZSBhcHAtc2lkZSBkZWZhdWx0cy4gV2UgY2FuJ3QgZGV0ZWN0IHdoaWNoLCBzbyBrbm9iIGl0LlxuY29uc3QgV0hFRUxfQUNDRUxfV0lORE9XX01TID0gNDBcbmNvbnN0IFdIRUVMX0FDQ0VMX1NURVAgPSAwLjNcbmNvbnN0IFdIRUVMX0FDQ0VMX01BWCA9IDZcblxuLy8gRW5jb2RlciBib3VuY2UgZGVib3VuY2UgKyB3aGVlbC1tb2RlIGRlY2F5IGN1cnZlLiBXb3JuL2NoZWFwIG9wdGljYWxcbi8vIGVuY29kZXJzIGVtaXQgc3B1cmlvdXMgcmV2ZXJzZS1kaXJlY3Rpb24gdGlja3MgZHVyaW5nIGZhc3Qgc3BpbnMg4oCUIG1lYXN1cmVkXG4vLyAyOCUgb2YgZXZlbnRzIG9uIEJvcmlzJ3MgbW91c2UgKDIwMjYtMDMtMTcsIGlUZXJtMikuIFBhdHRlcm4gaXMgYWx3YXlzXG4vLyBmbGlwLXRoZW4tZmxpcC1iYWNrOyB0cmFja3BhZHMgcHJvZHVjZSBaRVJPIGZsaXBzICgwLzQ1OCBpbiBzYW1lIHJlY29yZGluZykuXG4vLyBBIGNvbmZpcm1lZCBib3VuY2UgcHJvdmVzIGEgcGh5c2ljYWwgd2hlZWwgaXMgYXR0YWNoZWQg4oCUIGVuZ2FnZSB0aGUgc2FtZVxuLy8gZXhwb25lbnRpYWwtZGVjYXkgY3VydmUgdGhlIHh0ZXJtLmpzIHBhdGggdXNlcyAoaXQncyBhbHJlYWR5IHR1bmVkKSwgd2l0aFxuLy8gYSBoaWdoZXIgY2FwIHRvIGNvbXBlbnNhdGUgZm9yIHRoZSBsb3dlciBldmVudCByYXRlICh+OS9zZWMgdnMgVlMgQ29kZSdzXG4vLyB+MzAvc2VjKS4gVHJhY2twYWQgY2FuJ3QgcmVhY2ggdGhpcyBwYXRoLlxuLy9cbi8vIFRoZSBkZWNheSBjdXJ2ZSBnaXZlczogMXN0IGNsaWNrIGFmdGVyIGlkbGUgPSAxIHJvdyAocHJlY2lzaW9uKSwgMm5kID0gMTAsXG4vLyAzcmQgPSBjYXAuIFNsb3dpbmcgZG93biBkZWNheXMgc21vb3RobHkgdG93YXJkIDEg4oCUIG5vIHNlcGFyYXRlIGlkbGVcbi8vIHRocmVzaG9sZCBuZWVkZWQsIGxhcmdlIGdhcHMganVzdCBoYXZlIG3iiYgwIOKGkiBtdWx04oaSMS4gV2hlZWwgbW9kZSBpcyBTVElDS1k6XG4vLyBvbmNlIGEgYm91bmNlIGNvbmZpcm1zIGl0J3MgYSBtb3VzZSwgdGhlIGRlY2F5IGN1cnZlIGFwcGxpZXMgdW50aWwgYW4gaWRsZVxuLy8gZ2FwIG9yIHRyYWNrcGFkLWZsaWNrLWJ1cnN0IHNpZ25hbHMgYSBwb3NzaWJsZSBkZXZpY2Ugc3dpdGNoLlxuY29uc3QgV0hFRUxfQk9VTkNFX0dBUF9NQVhfTVMgPSAyMDAgLy8gZmxpcC1iYWNrIG11c3QgYXJyaXZlIHdpdGhpbiB0aGlzXG4vLyBNb3VzZSBpcyB+OSBldmVudHMvc2VjIHZzIFZTIENvZGUncyB+MzAg4oCUIFNURVAgaXMgM8OXIHh0ZXJtLmpzJ3MgNSB0b1xuLy8gY29tcGVuc2F0ZS4gQXQgZ2FwPTEwMG1zICht4omIMC42Myk6IG9uZSBjbGljayBnaXZlcyAxKzE1KjAuNjPiiYgxMC41LlxuY29uc3QgV0hFRUxfTU9ERV9TVEVQID0gMTVcbmNvbnN0IFdIRUVMX01PREVfQ0FQID0gMTVcbi8vIE1heCBtdWx0IGdyb3d0aCBwZXIgZXZlbnQuIFdpdGhvdXQgdGhpcywgdGhlICtTVEVQKm0gdGVybSBqdW1wcyBtdWx0XG4vLyBmcm9tIDHihpIxMCBpbiBvbmUgZXZlbnQgd2hlbiB3aGVlbE1vZGUgZW5nYWdlcyBtaWQtc2Nyb2xsIChib3VuY2Vcbi8vIGRldGVjdGVkIGFmdGVyIE4gZXZlbnRzIGluIHRyYWNrcGFkIG1vZGUgYXQgbXVsdD0xKS4gVXNlciBzZWVzIHNjcm9sbFxuLy8gc3VkZGVubHkgZ28gMTDDlyBmYXN0ZXIuIENhcD0zIGdpdmVzIDHihpI04oaSN+KGkjEw4oaSMTPihpIxNSBvdmVyIH4wLjVzIGF0XG4vLyA5IGV2ZW50cy9zZWMg4oCUIHNtb290aCByYW1wIGluc3RlYWQgb2YgYSBqdW1wLiBEZWNheSBpcyB1bmFmZmVjdGVkXG4vLyAodGFyZ2V0PG11bHQgd2lucyB0aGUgbWluKS5cbmNvbnN0IFdIRUVMX01PREVfUkFNUCA9IDNcbi8vIERldmljZS1zd2l0Y2ggZGlzZW5nYWdlOiBtb3VzZSBmaW5nZXItcmVwb3NpdGlvbnMgbWF4IGF0IH44MzBtcyAobWVhc3VyZWQpO1xuLy8gdHJhY2twYWQgYmV0d2Vlbi1nZXN0dXJlIHBhdXNlcyBhcmUgMjAwMG1zKy4gQW4gaWRsZSBnYXAgYWJvdmUgdGhpcyBtZWFuc1xuLy8gdGhlIHVzZXIgc3RvcHBlZCDigJQgbWlnaHQgaGF2ZSBzd2l0Y2hlZCBkZXZpY2VzLiBEaXNlbmdhZ2U7IHRoZSBuZXh0IG1vdXNlXG4vLyBib3VuY2UgcmUtZW5nYWdlcy4gVHJhY2twYWQgc2xvdyBzd2lwZSAobm8gPDVtcyBidXJzdHMsIHNvIHRoZSBidXJzdC1jb3VudFxuLy8gZ3VhcmQgZG9lc24ndCBjYXRjaCBpdCkgaXMgd2hhdCB0aGlzIHByb3RlY3RzIGFnYWluc3QuXG5jb25zdCBXSEVFTF9NT0RFX0lETEVfRElTRU5HQUdFX01TID0gMTUwMFxuXG4vLyB4dGVybS5qczogZXhwb25lbnRpYWwgZGVjYXkuIG1vbWVudHVtPTAuNV4oZ2FwL2hsKSDigJQgc2xvdyBjbGljayDihpIgbeKJiDBcbi8vIOKGkiBtdWx04oaSMSAocHJlY2lzaW9uKTsgZmFzdCDihpIgbeKJiDEg4oaSIGNhcnJpZXMgbW9tZW50dW0uIFN0ZWFkeS1zdGF0ZVxuLy8gPSAxICsgc3RlcMOXbS8oMS1tKSwgY2FwcGVkLiBNZWFzdXJlZCBldmVudCByYXRlcyBpbiBWUyBDb2RlICh3aGVlbC5sb2cpOlxuLy8gc3VzdGFpbmVkIHNjcm9sbCBzZW5kcyBldmVudHMgYXQgMjAtNTBtcyBnYXBzICgyMC00MCBIeiksIHBsdXMgMC0ybXNcbi8vIHNhbWUtYmF0Y2ggYnVyc3RzIG9uIGZsaWNrcy4gQ2FwIGlzIGxvdyAoM+KAkzYsIGdhcC1kZXBlbmRlbnQpIGJlY2F1c2UgZXZlbnRcbi8vIGZyZXF1ZW5jeSBpcyBoaWdoIOKAlCBhdCA0MCBIeiDDlyA2ID0gMjQwIHJvd3Mvc2VjIG1heCBkZW1hbmQsIHdoaWNoIHRoZVxuLy8gYWRhcHRpdmUgZHJhaW4gYXQgfjIwMGZwcyAobWVhc3VyZWQpIGhhbmRsZXMuIEhpZ2hlciBjYXAg4oaSIHBlbmRpbmcgZXhwbG9zaW9uLlxuLy8gVHVuZWQgZW1waXJpY2FsbHkgKGJvcmlzIDIwMjYtMDMpLiBTZWUgZG9jcy9yZXNlYXJjaC90ZXJtaW5hbC1zY3JvbGwtKi5cbmNvbnN0IFdIRUVMX0RFQ0FZX0hBTEZMSUZFX01TID0gMTUwXG5jb25zdCBXSEVFTF9ERUNBWV9TVEVQID0gNVxuLy8gU2FtZS1iYXRjaCBldmVudHMgKDxCVVJTVF9NUykgYXJyaXZlIGluIG9uZSBzdGRpbiBiYXRjaCDigJQgdGhlIHRlcm1pbmFsXG4vLyBpcyBkb2luZyBwcm9wb3J0aW9uYWwgcmVwb3J0aW5nLiBUcmVhdCBhcyAxIHJvdy9ldmVudCBsaWtlIG5hdGl2ZS5cbmNvbnN0IFdIRUVMX0JVUlNUX01TID0gNVxuLy8gQ2FwIGJvdW5kYXJ5OiBzbG93IGV2ZW50cyAo4omlR0FQX01TKSBjYXAgbG93IGZvciBzaG9ydCBzbW9vdGggZHJhaW5zO1xuLy8gZmFzdCBldmVudHMgY2FwIGhpZ2hlciBmb3IgdGhyb3VnaHB1dCAoYWRhcHRpdmUgZHJhaW4gaGFuZGxlcyBiYWNrbG9nKS5cbmNvbnN0IFdIRUVMX0RFQ0FZX0dBUF9NUyA9IDgwXG5jb25zdCBXSEVFTF9ERUNBWV9DQVBfU0xPVyA9IDMgLy8gZ2FwIOKJpSBHQVBfTVM6IHByZWNpc2lvblxuY29uc3QgV0hFRUxfREVDQVlfQ0FQX0ZBU1QgPSA2IC8vIGdhcCA8IEdBUF9NUzogdGhyb3VnaHB1dFxuLy8gSWRsZSB0aHJlc2hvbGQ6IGdhcHMgYmV5b25kIHRoaXMgcmVzZXQgdG8gdGhlIGtpY2sgdmFsdWUgKDIpIHNvIHRoZVxuLy8gZmlyc3QgY2xpY2sgYWZ0ZXIgYSBwYXVzZSBmZWVscyByZXNwb25zaXZlIHJlZ2FyZGxlc3Mgb2YgZGlyZWN0aW9uLlxuY29uc3QgV0hFRUxfREVDQVlfSURMRV9NUyA9IDUwMFxuXG4vKipcbiAqIFdoZXRoZXIgYSBrZXlwcmVzcyBzaG91bGQgY2xlYXIgdGhlIHZpcnR1YWwgdGV4dCBzZWxlY3Rpb24uIE1pbWljc1xuICogbmF0aXZlIHRlcm1pbmFsIHNlbGVjdGlvbjogYW55IGtleXN0cm9rZSBjbGVhcnMsIEVYQ0VQVCBtb2RpZmllZCBuYXZcbiAqIGtleXMgKHNoaWZ0L29wdC9jbWQgKyBhcnJvdy9ob21lL2VuZC9wYWdlKikuIEluIG5hdGl2ZSBtYWNPUyBjb250ZXh0cyxcbiAqIHNoaWZ0K25hdiBleHRlbmRzIHNlbGVjdGlvbiwgYW5kIGNtZC9vcHQrbmF2IGFyZSBvZnRlbiBpbnRlcmNlcHRlZCBieVxuICogdGhlIHRlcm1pbmFsIGVtdWxhdG9yIGZvciBzY3JvbGxiYWNrIG5hdiDigJQgbmVpdGhlciBkaXN0dXJicyBzZWxlY3Rpb24uXG4gKiBCYXJlIGFycm93cyBETyBjbGVhciAodXNlcidzIGN1cnNvciBtb3ZlcywgbmF0aXZlIGRlc2VsZWN0cykuIFdoZWVsIGlzXG4gKiBleGNsdWRlZCDigJQgc2Nyb2xsOmxpbmVVcC9Eb3duIGFscmVhZHkgY2xlYXJzIHZpYSB0aGUga2V5YmluZGluZyBwYXRoLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2hvdWxkQ2xlYXJTZWxlY3Rpb25PbktleShrZXk6IEtleSk6IGJvb2xlYW4ge1xuICBpZiAoa2V5LndoZWVsVXAgfHwga2V5LndoZWVsRG93bikgcmV0dXJuIGZhbHNlXG4gIGNvbnN0IGlzTmF2ID1cbiAgICBrZXkubGVmdEFycm93IHx8XG4gICAga2V5LnJpZ2h0QXJyb3cgfHxcbiAgICBrZXkudXBBcnJvdyB8fFxuICAgIGtleS5kb3duQXJyb3cgfHxcbiAgICBrZXkuaG9tZSB8fFxuICAgIGtleS5lbmQgfHxcbiAgICBrZXkucGFnZVVwIHx8XG4gICAga2V5LnBhZ2VEb3duXG4gIGlmIChpc05hdiAmJiAoa2V5LnNoaWZ0IHx8IGtleS5tZXRhIHx8IGtleS5zdXBlcikpIHJldHVybiBmYWxzZVxuICByZXR1cm4gdHJ1ZVxufVxuXG4vKipcbiAqIE1hcCBhIGtleXByZXNzIHRvIGEgc2VsZWN0aW9uIGZvY3VzIG1vdmUgKGtleWJvYXJkIGV4dGVuc2lvbikuIE9ubHlcbiAqIHNoaWZ0IGV4dGVuZHMg4oCUIHRoYXQncyB0aGUgdW5pdmVyc2FsIHRleHQtc2VsZWN0aW9uIG1vZGlmaWVyLiBjbWRcbiAqIChzdXBlcikgb25seSBhcnJpdmVzIHZpYSBraXR0eSBrZXlib2FyZCBwcm90b2NvbCDigJQgaW4gbW9zdCB0ZXJtaW5hbHNcbiAqIGNtZCthcnJvdyBpcyBpbnRlcmNlcHRlZCBieSB0aGUgZW11bGF0b3IgYW5kIG5ldmVyIHJlYWNoZXMgdGhlIHB0eSwgc29cbiAqIG5vIHN1cGVyIGJyYW5jaC4gc2hpZnQraG9tZS9lbmQgY292ZXJzIGxpbmUtZWRnZSBqdW1wcyAoYW5kIGZuK3NoaWZ0K1xuICogbGVmdC9yaWdodCBvbiBtYWMgbGFwdG9wcyA9IHNoaWZ0K2hvbWUvZW5kKS4gc2hpZnQrb3B0ICh3b3JkLWp1bXApIG5vdFxuICogeWV0IGltcGxlbWVudGVkIOKAlCBmYWxscyB0aHJvdWdoIHRvIHNob3VsZENsZWFyU2VsZWN0aW9uT25LZXkgd2hpY2hcbiAqIHByZXNlcnZlcyAobW9kaWZpZWQgbmF2KS4gUmV0dXJucyBudWxsIGZvciBub24tZXh0ZW5kIGtleXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZWxlY3Rpb25Gb2N1c01vdmVGb3JLZXkoa2V5OiBLZXkpOiBGb2N1c01vdmUgfCBudWxsIHtcbiAgaWYgKCFrZXkuc2hpZnQgfHwga2V5Lm1ldGEpIHJldHVybiBudWxsXG4gIGlmIChrZXkubGVmdEFycm93KSByZXR1cm4gJ2xlZnQnXG4gIGlmIChrZXkucmlnaHRBcnJvdykgcmV0dXJuICdyaWdodCdcbiAgaWYgKGtleS51cEFycm93KSByZXR1cm4gJ3VwJ1xuICBpZiAoa2V5LmRvd25BcnJvdykgcmV0dXJuICdkb3duJ1xuICBpZiAoa2V5LmhvbWUpIHJldHVybiAnbGluZVN0YXJ0J1xuICBpZiAoa2V5LmVuZCkgcmV0dXJuICdsaW5lRW5kJ1xuICByZXR1cm4gbnVsbFxufVxuXG5leHBvcnQgdHlwZSBXaGVlbEFjY2VsU3RhdGUgPSB7XG4gIHRpbWU6IG51bWJlclxuICBtdWx0OiBudW1iZXJcbiAgZGlyOiAwIHwgMSB8IC0xXG4gIHh0ZXJtSnM6IGJvb2xlYW5cbiAgLyoqIENhcnJpZWQgZnJhY3Rpb25hbCBzY3JvbGwgKHh0ZXJtLmpzIG9ubHkpLiBzY3JvbGxCeSBmbG9vcnMsIHNvIHdpdGhvdXRcbiAgICogIHRoaXMgYSBtdWx0IG9mIDEuNSBnaXZlcyAxIHJvdyBldmVyeSB0aW1lLiBDYXJyeWluZyB0aGUgcmVtYWluZGVyIGdpdmVzXG4gICAqICAxLDIsMSwyIG9uIGF2ZXJhZ2UgZm9yIG11bHQ9MS41IOKAlCBjb3JyZWN0IHRocm91Z2hwdXQgb3ZlciB0aW1lLiAqL1xuICBmcmFjOiBudW1iZXJcbiAgLyoqIE5hdGl2ZS1wYXRoIGJhc2VsaW5lIHJvd3MvZXZlbnQuIFJlc2V0IHZhbHVlIG9uIGlkbGUvcmV2ZXJzYWw7IHJhbXBcbiAgICogIGJ1aWxkcyBvbiB0b3AuIHh0ZXJtLmpzIHBhdGggaWdub3JlcyB0aGlzIChvd24ga2ljaz0yIHR1bmluZykuICovXG4gIGJhc2U6IG51bWJlclxuICAvKiogRGVmZXJyZWQgZGlyZWN0aW9uIGZsaXAgKG5hdGl2ZSBvbmx5KS4gTWlnaHQgYmUgZW5jb2RlciBib3VuY2Ugb3IgYVxuICAgKiAgcmVhbCByZXZlcnNhbCDigJQgcmVzb2x2ZWQgYnkgdGhlIE5FWFQgZXZlbnQuIFJlYWwgcmV2ZXJzYWwgbG9zZXMgMSByb3dcbiAgICogIG9mIGxhdGVuY3k7IGJvdW5jZSBpcyBzd2FsbG93ZWQgYW5kIHRyaWdnZXJzIHdoZWVsIG1vZGUuIFRoZSBmbGlwJ3NcbiAgICogIGRpcmVjdGlvbiBhbmQgdGltZXN0YW1wIGFyZSBkZXJpdmFibGUgKGl0J3MgYWx3YXlzIC1zdGF0ZS5kaXIgYXRcbiAgICogIHN0YXRlLnRpbWUpIHNvIHRoaXMgaXMganVzdCBhIG1hcmtlci4gKi9cbiAgcGVuZGluZ0ZsaXA6IGJvb2xlYW5cbiAgLyoqIFNldCB0cnVlIG9uY2UgYSBib3VuY2UgaXMgY29uZmlybWVkIChmbGlwLXRoZW4tZmxpcC1iYWNrIHdpdGhpblxuICAgKiAgQk9VTkNFX0dBUF9NQVgpLiBTdGlja3kg4oCUIGJ1dCBkaXNlbmdhZ2VkIG9uIGlkbGUgZ2FwID4xNTAwbXMgT1IgYVxuICAgKiAgdHJhY2twYWQtc2lnbmF0dXJlIGJ1cnN0IChzZWUgYnVyc3RDb3VudCkuIFN0YXRlIGxpdmVzIGluIGEgdXNlUmVmIHNvXG4gICAqICBpdCBwZXJzaXN0cyBhY3Jvc3MgZGV2aWNlIHN3aXRjaGVzOyB0aGUgZGlzZW5nYWdlcyBoYW5kbGUgbW91c2XihpJ0cmFja3BhZC4gKi9cbiAgd2hlZWxNb2RlOiBib29sZWFuXG4gIC8qKiBDb25zZWN1dGl2ZSA8NW1zIGV2ZW50cy4gVHJhY2twYWQgZmxpY2sgcHJvZHVjZXMgMTAwKyBhdCA8NW1zOyBtb3VzZVxuICAgKiAgcHJvZHVjZXMg4omkMyAodmVyaWZpZWQgaW4gL3RtcC93aGVlbC10dW5lLnR4dCkuIDUrIGluIGEgcm93IOKGkiB0cmFja3BhZFxuICAgKiAgc2lnbmF0dXJlIOKGkiBkaXNlbmdhZ2Ugd2hlZWwgbW9kZSBzbyBkZXZpY2Utc3dpdGNoIGRvZXNuJ3QgbGVhayBtb3VzZVxuICAgKiAgYWNjZWwgdG8gdHJhY2twYWQuICovXG4gIGJ1cnN0Q291bnQ6IG51bWJlclxufVxuXG4vKiogQ29tcHV0ZSByb3dzIGZvciBvbmUgd2hlZWwgZXZlbnQsIG11dGF0aW5nIGFjY2VsIHN0YXRlLiBSZXR1cm5zIDAgd2hlblxuICogIGEgZGlyZWN0aW9uIGZsaXAgaXMgZGVmZXJyZWQgZm9yIGJvdW5jZSBkZXRlY3Rpb24g4oCUIGNhbGwgc2l0ZXMgbm8tb3Agb25cbiAqICBzdGVwPTAgKHNjcm9sbEJ5KDApIGlzIGEgbm8tb3AsIG9uU2Nyb2xsKGZhbHNlKSBpcyBpZGVtcG90ZW50KS4gRXhwb3J0ZWRcbiAqICBmb3IgdGVzdHMuICovXG5leHBvcnQgZnVuY3Rpb24gY29tcHV0ZVdoZWVsU3RlcChcbiAgc3RhdGU6IFdoZWVsQWNjZWxTdGF0ZSxcbiAgZGlyOiAxIHwgLTEsXG4gIG5vdzogbnVtYmVyLFxuKTogbnVtYmVyIHtcbiAgaWYgKCFzdGF0ZS54dGVybUpzKSB7XG4gICAgLy8gRGV2aWNlLXN3aXRjaCBndWFyZCDikaA6IGlkbGUgZGlzZW5nYWdlLiBSdW5zIEJFRk9SRSBwZW5kaW5nRmxpcCByZXNvbHZlXG4gICAgLy8gc28gYSBwZW5kaW5nIGJvdW5jZSAoMjglIG9mIGxhc3QtbW91c2UtZXZlbnRzKSBkb2Vzbid0IGJ5cGFzcyBpdCB2aWFcbiAgICAvLyB0aGUgcmVhbC1yZXZlcnNhbCBlYXJseSByZXR1cm4uIHN0YXRlLnRpbWUgaXMgZWl0aGVyIHRoZSBsYXN0IGNvbW1pdHRlZFxuICAgIC8vIGV2ZW50IE9SIHRoZSBkZWZlcnJlZCBmbGlwIOKAlCBib3RoIGNvdW50IGFzIFwibGFzdCBhY3Rpdml0eVwiLlxuICAgIGlmIChzdGF0ZS53aGVlbE1vZGUgJiYgbm93IC0gc3RhdGUudGltZSA+IFdIRUVMX01PREVfSURMRV9ESVNFTkdBR0VfTVMpIHtcbiAgICAgIHN0YXRlLndoZWVsTW9kZSA9IGZhbHNlXG4gICAgICBzdGF0ZS5idXJzdENvdW50ID0gMFxuICAgICAgc3RhdGUubXVsdCA9IHN0YXRlLmJhc2VcbiAgICB9XG5cbiAgICAvLyBSZXNvbHZlIGFueSBkZWZlcnJlZCBmbGlwIEJFRk9SRSB0b3VjaGluZyBzdGF0ZS50aW1lL2RpciDigJQgd2UgbmVlZCB0aGVcbiAgICAvLyBwcmUtZmxpcCBzdGF0ZS5kaXIgdG8gZGlzdGluZ3Vpc2ggYm91bmNlIChmbGlwLWJhY2spIGZyb20gcmVhbCByZXZlcnNhbFxuICAgIC8vIChmbGlwIHBlcnNpc3RlZCksIGFuZCBzdGF0ZS50aW1lICg9IGJvdW5jZSB0aW1lc3RhbXApIGZvciB0aGUgZ2FwIGNoZWNrLlxuICAgIGlmIChzdGF0ZS5wZW5kaW5nRmxpcCkge1xuICAgICAgc3RhdGUucGVuZGluZ0ZsaXAgPSBmYWxzZVxuICAgICAgaWYgKGRpciAhPT0gc3RhdGUuZGlyIHx8IG5vdyAtIHN0YXRlLnRpbWUgPiBXSEVFTF9CT1VOQ0VfR0FQX01BWF9NUykge1xuICAgICAgICAvLyBSZWFsIHJldmVyc2FsOiBuZXcgZGlyIHBlcnNpc3RlZCwgT1IgZmxpcC1iYWNrIGFycml2ZWQgdG9vIGxhdGUuXG4gICAgICAgIC8vIENvbW1pdC4gVGhlIGRlZmVycmVkIGV2ZW50J3MgMSByb3cgaXMgbG9zdCAoYWNjZXB0YWJsZSBsYXRlbmN5KS5cbiAgICAgICAgc3RhdGUuZGlyID0gZGlyXG4gICAgICAgIHN0YXRlLnRpbWUgPSBub3dcbiAgICAgICAgc3RhdGUubXVsdCA9IHN0YXRlLmJhc2VcbiAgICAgICAgcmV0dXJuIE1hdGguZmxvb3Ioc3RhdGUubXVsdClcbiAgICAgIH1cbiAgICAgIC8vIEJvdW5jZSBjb25maXJtZWQ6IGZsaXBwZWQgYmFjayB0byBvcmlnaW5hbCBkaXIgd2l0aGluIHRoZSB3aW5kb3cuXG4gICAgICAvLyBzdGF0ZS5kaXIvbXVsdCB1bmNoYW5nZWQgZnJvbSBwcmUtYm91bmNlLiBzdGF0ZS50aW1lIHdhcyBhZHZhbmNlZCB0b1xuICAgICAgLy8gdGhlIGJvdW5jZSBiZWxvdywgc28gZ2FwIGhlcmUgPSBmbGlwLWJhY2sgaW50ZXJ2YWwg4oCUIHJlZmxlY3RzIHRoZVxuICAgICAgLy8gdXNlcidzIGFjdHVhbCBjbGljayBjYWRlbmNlIChib3VuY2UgSVMgYSBwaHlzaWNhbCBjbGljaywganVzdCBub2lzeSkuXG4gICAgICBzdGF0ZS53aGVlbE1vZGUgPSB0cnVlXG4gICAgfVxuXG4gICAgY29uc3QgZ2FwID0gbm93IC0gc3RhdGUudGltZVxuICAgIGlmIChkaXIgIT09IHN0YXRlLmRpciAmJiBzdGF0ZS5kaXIgIT09IDApIHtcbiAgICAgIC8vIEZsaXAuIERlZmVyIOKAlCBuZXh0IGV2ZW50IGRlY2lkZXMgYm91bmNlIHZzLiByZWFsIHJldmVyc2FsLiBBZHZhbmNlXG4gICAgICAvLyB0aW1lIChidXQgTk9UIGRpci9tdWx0KTogaWYgdGhpcyB0dXJucyBvdXQgdG8gYmUgYSBib3VuY2UsIHRoZVxuICAgICAgLy8gY29uZmlybSBldmVudCdzIGdhcCB3aWxsIGJlIHRoZSBmbGlwLWJhY2sgaW50ZXJ2YWwsIHdoaWNoIHJlZmxlY3RzXG4gICAgICAvLyB0aGUgdXNlcidzIGFjdHVhbCBjbGljayByYXRlLiBUaGUgYm91bmNlIElTIGEgcGh5c2ljYWwgd2hlZWwgY2xpY2ssXG4gICAgICAvLyBqdXN0IG1pc3JlYWQgYnkgdGhlIGVuY29kZXIg4oCUIGl0IHNob3VsZCBjb3VudCB0b3dhcmQgY2FkZW5jZS5cbiAgICAgIHN0YXRlLnBlbmRpbmdGbGlwID0gdHJ1ZVxuICAgICAgc3RhdGUudGltZSA9IG5vd1xuICAgICAgcmV0dXJuIDBcbiAgICB9XG4gICAgc3RhdGUuZGlyID0gZGlyXG4gICAgc3RhdGUudGltZSA9IG5vd1xuXG4gICAgLy8g4pSA4pSA4pSAIE1PVVNFICh3aGVlbCBtb2RlLCBzdGlja3kgdW50aWwgZGV2aWNlLXN3aXRjaCBzaWduYWwpIOKUgOKUgOKUgFxuICAgIGlmIChzdGF0ZS53aGVlbE1vZGUpIHtcbiAgICAgIGlmIChnYXAgPCBXSEVFTF9CVVJTVF9NUykge1xuICAgICAgICAvLyBTYW1lLWJhdGNoIGJ1cnN0IGNoZWNrIChwb3J0ZWQgZnJvbSB4dGVybS5qcyk6IGlUZXJtMiBwcm9wb3J0aW9uYWxcbiAgICAgICAgLy8gcmVwb3J0aW5nIHNlbmRzIDIrIFNHUiBldmVudHMgZm9yIG9uZSBkZXRlbnQgd2hlbiBtYWNPUyBnaXZlc1xuICAgICAgICAvLyBkZWx0YT4xLiBXaXRob3V0IHRoaXMsIHRoZSAybmQgZXZlbnQgYXQgZ2FwPDFtcyBoYXMgbeKJiDEg4oaSIFNURVAqbT0xNVxuICAgICAgICAvLyDihpIgb25lIGdlbnRsZSBjbGljayBnaXZlcyAxKzE1PTE2IHJvd3MuXG4gICAgICAgIC8vXG4gICAgICAgIC8vIERldmljZS1zd2l0Y2ggZ3VhcmQg4pGhOiB0cmFja3BhZCBmbGljayBwcm9kdWNlcyAxMDArIGV2ZW50cyBhdCA8NW1zXG4gICAgICAgIC8vIChtZWFzdXJlZCk7IG1vdXNlIHByb2R1Y2VzIOKJpDMuIDUrIGNvbnNlY3V0aXZlIOKGkiB0cmFja3BhZCBmbGljay5cbiAgICAgICAgaWYgKCsrc3RhdGUuYnVyc3RDb3VudCA+PSA1KSB7XG4gICAgICAgICAgc3RhdGUud2hlZWxNb2RlID0gZmFsc2VcbiAgICAgICAgICBzdGF0ZS5idXJzdENvdW50ID0gMFxuICAgICAgICAgIHN0YXRlLm11bHQgPSBzdGF0ZS5iYXNlXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIDFcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc3RhdGUuYnVyc3RDb3VudCA9IDBcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gUmUtY2hlY2s6IG1heSBoYXZlIGRpc2VuZ2FnZWQgYWJvdmUuXG4gICAgaWYgKHN0YXRlLndoZWVsTW9kZSkge1xuICAgICAgLy8geHRlcm0uanMgZGVjYXkgY3VydmUgd2l0aCBTVEVQw5czLCBoaWdoZXIgY2FwLiBObyBpZGxlIHRocmVzaG9sZCDigJRcbiAgICAgIC8vIHRoZSBjdXJ2ZSBoYW5kbGVzIGl0IChnYXA9MTAwMG1zIOKGkiBt4omIMC4wMSDihpIgbXVsdOKJiDEpLiBObyBmcmFjIOKAlFxuICAgICAgLy8gcm91bmRpbmcgbG9zcyBpcyBtaW5vciBhdCBoaWdoIG11bHQsIGFuZCBmcmFjIHBlcnNpc3RpbmcgYWNyb3NzIGlkbGVcbiAgICAgIC8vIHdhcyBjYXVzaW5nIG9mZi1ieS1vbmUgb24gdGhlIGZpcnN0IGNsaWNrIGJhY2suXG4gICAgICBjb25zdCBtID0gTWF0aC5wb3coMC41LCBnYXAgLyBXSEVFTF9ERUNBWV9IQUxGTElGRV9NUylcbiAgICAgIGNvbnN0IGNhcCA9IE1hdGgubWF4KFdIRUVMX01PREVfQ0FQLCBzdGF0ZS5iYXNlICogMilcbiAgICAgIGNvbnN0IG5leHQgPSAxICsgKHN0YXRlLm11bHQgLSAxKSAqIG0gKyBXSEVFTF9NT0RFX1NURVAgKiBtXG4gICAgICBzdGF0ZS5tdWx0ID0gTWF0aC5taW4oY2FwLCBuZXh0LCBzdGF0ZS5tdWx0ICsgV0hFRUxfTU9ERV9SQU1QKVxuICAgICAgcmV0dXJuIE1hdGguZmxvb3Ioc3RhdGUubXVsdClcbiAgICB9XG5cbiAgICAvLyDilIDilIDilIAgVFJBQ0tQQUQgLyBISS1SRVMgKG5hdGl2ZSwgbm9uLXdoZWVsLW1vZGUpIOKUgOKUgOKUgFxuICAgIC8vIFRpZ2h0IDQwbXMgYnVyc3Qgd2luZG93OiBzdWItNDBtcyBldmVudHMgcmFtcCwgYW55dGhpbmcgc2xvd2VyIHJlc2V0cy5cbiAgICAvLyBUcmFja3BhZCBmbGljayBkZWxpdmVycyAyMDArIGV2ZW50cyBhdCA8MjBtcyBnYXBzIOKGkiByYWlscyB0byBjYXAgNi5cbiAgICAvLyBUcmFja3BhZCBzbG93IHN3aXBlIGF0IDQwLTQwMG1zIGdhcHMg4oaSIHJlc2V0cyBldmVyeSBldmVudCDihpIgMSByb3cgZWFjaC5cbiAgICBpZiAoZ2FwID4gV0hFRUxfQUNDRUxfV0lORE9XX01TKSB7XG4gICAgICBzdGF0ZS5tdWx0ID0gc3RhdGUuYmFzZVxuICAgIH0gZWxzZSB7XG4gICAgICBjb25zdCBjYXAgPSBNYXRoLm1heChXSEVFTF9BQ0NFTF9NQVgsIHN0YXRlLmJhc2UgKiAyKVxuICAgICAgc3RhdGUubXVsdCA9IE1hdGgubWluKGNhcCwgc3RhdGUubXVsdCArIFdIRUVMX0FDQ0VMX1NURVApXG4gICAgfVxuICAgIHJldHVybiBNYXRoLmZsb29yKHN0YXRlLm11bHQpXG4gIH1cblxuICAvLyDilIDilIDilIAgVlNDT0RFICh4dGVybS5qcywgYnJvd3NlciB3aGVlbCBldmVudHMpIOKUgOKUgOKUgFxuICAvLyBCcm93c2VyIHdoZWVsIGV2ZW50cyDigJQgbm8gZW5jb2RlciBib3VuY2UsIG5vIFNHUiBidXJzdHMuIERlY2F5IGN1cnZlXG4gIC8vIHVuY2hhbmdlZCBmcm9tIHRoZSBvcmlnaW5hbCB0dW5pbmcuIFNhbWUgZm9ybXVsYSBzaGFwZSBhcyB3aGVlbCBtb2RlXG4gIC8vIGFib3ZlIChrZWVwIGluIHN5bmMpIGJ1dCBTVEVQPTUgbm90IDE1IOKAlCBoaWdoZXIgZXZlbnQgcmF0ZSBoZXJlLlxuICBjb25zdCBnYXAgPSBub3cgLSBzdGF0ZS50aW1lXG4gIGNvbnN0IHNhbWVEaXIgPSBkaXIgPT09IHN0YXRlLmRpclxuICBzdGF0ZS50aW1lID0gbm93XG4gIHN0YXRlLmRpciA9IGRpclxuICAvLyB4dGVybS5qcyBwYXRoLiBEZWJ1ZyBsb2cgc2hvd3MgdHdvIHBhdHRlcm5zOiAoYSkgMjAtNTBtcyBnYXBzIGR1cmluZ1xuICAvLyBzdXN0YWluZWQgc2Nyb2xsICh+MzAgSHopLCAoYikgPDVtcyBzYW1lLWJhdGNoIGJ1cnN0cyBvbiBmbGlja3MuIEZvclxuICAvLyAoYikgZ2l2ZSAxIHJvdy9ldmVudCDigJQgdGhlIGJ1cnN0IGNvdW50IElTIHRoZSBhY2NlbGVyYXRpb24sIHNhbWUgYXNcbiAgLy8gbmF0aXZlLiBGb3IgKGEpIHRoZSBkZWNheSBjdXJ2ZSBnaXZlcyAzLTUgcm93cy4gRm9yIHNwYXJzZSBldmVudHNcbiAgLy8gKDEwMG1zKywgc2xvdyBkZWxpYmVyYXRlIHNjcm9sbCkgdGhlIGN1cnZlIGdpdmVzIDEtMy5cbiAgaWYgKHNhbWVEaXIgJiYgZ2FwIDwgV0hFRUxfQlVSU1RfTVMpIHJldHVybiAxXG4gIGlmICghc2FtZURpciB8fCBnYXAgPiBXSEVFTF9ERUNBWV9JRExFX01TKSB7XG4gICAgLy8gRGlyZWN0aW9uIHJldmVyc2FsIG9yIGxvbmcgaWRsZTogc3RhcnQgYXQgMiAobm90IDEpIHNvIHRoZSBmaXJzdFxuICAgIC8vIGNsaWNrIGFmdGVyIGEgcGF1c2UgbW92ZXMgYSB2aXNpYmxlIGFtb3VudC4gV2l0aG91dCB0aGlzLCBpZGxlLVxuICAgIC8vIHRoZW4tcmVzdW1lIGluIHRoZSBzYW1lIGRpcmVjdGlvbiBkZWNheXMgdG8gbXVsdOKJiDEgKDEgcm93KS5cbiAgICBzdGF0ZS5tdWx0ID0gMlxuICAgIHN0YXRlLmZyYWMgPSAwXG4gIH0gZWxzZSB7XG4gICAgY29uc3QgbSA9IE1hdGgucG93KDAuNSwgZ2FwIC8gV0hFRUxfREVDQVlfSEFMRkxJRkVfTVMpXG4gICAgY29uc3QgY2FwID1cbiAgICAgIGdhcCA+PSBXSEVFTF9ERUNBWV9HQVBfTVMgPyBXSEVFTF9ERUNBWV9DQVBfU0xPVyA6IFdIRUVMX0RFQ0FZX0NBUF9GQVNUXG4gICAgc3RhdGUubXVsdCA9IE1hdGgubWluKGNhcCwgMSArIChzdGF0ZS5tdWx0IC0gMSkgKiBtICsgV0hFRUxfREVDQVlfU1RFUCAqIG0pXG4gIH1cbiAgY29uc3QgdG90YWwgPSBzdGF0ZS5tdWx0ICsgc3RhdGUuZnJhY1xuICBjb25zdCByb3dzID0gTWF0aC5mbG9vcih0b3RhbClcbiAgc3RhdGUuZnJhYyA9IHRvdGFsIC0gcm93c1xuICByZXR1cm4gcm93c1xufVxuXG4vKiogUmVhZCBDTEFVREVfQ09ERV9TQ1JPTExfU1BFRUQsIGRlZmF1bHQgMSwgY2xhbXAgKDAsIDIwXS5cbiAqICBTb21lIHRlcm1pbmFscyBwcmUtbXVsdGlwbHkgd2hlZWwgZXZlbnRzIChnaG9zdHR5IGRpc2NyZXRlPTMsIGlUZXJtMlxuICogIFwiZmFzdGVyIHNjcm9sbFwiKSDigJQgYmFzZT0xIGlzIGNvcnJlY3QgdGhlcmUuIE90aGVycyBzZW5kIDEgZXZlbnQvbm90Y2gg4oCUXG4gKiAgc2V0IENMQVVERV9DT0RFX1NDUk9MTF9TUEVFRD0zIHRvIG1hdGNoIHZpbS9udmltL29wZW5jb2RlLiBXZSBjYW4ndFxuICogIGRldGVjdCB3aGljaCBraW5kIG9mIHRlcm1pbmFsIHdlJ3JlIGluLCBoZW5jZSB0aGUga25vYi4gQ2FsbGVkIGxhemlseVxuICogIGZyb20gaW5pdEFuZExvZ1doZWVsQWNjZWwgc28gZ2xvYmFsU2V0dGluZ3MuZW52IGhhcyBsb2FkZWQuICovXG5leHBvcnQgZnVuY3Rpb24gcmVhZFNjcm9sbFNwZWVkQmFzZSgpOiBudW1iZXIge1xuICBjb25zdCByYXcgPSBwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9TQ1JPTExfU1BFRURcbiAgaWYgKCFyYXcpIHJldHVybiAxXG4gIGNvbnN0IG4gPSBwYXJzZUZsb2F0KHJhdylcbiAgcmV0dXJuIE51bWJlci5pc05hTihuKSB8fCBuIDw9IDAgPyAxIDogTWF0aC5taW4obiwgMjApXG59XG5cbi8qKiBJbml0aWFsIHdoZWVsIGFjY2VsIHN0YXRlLiB4dGVybUpzPXRydWUgc2VsZWN0cyB0aGUgZGVjYXkgY3VydmUuXG4gKiAgYmFzZSBpcyB0aGUgbmF0aXZlLXBhdGggYmFzZWxpbmUgcm93cy9ldmVudCAoZGVmYXVsdCAxKS4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpbml0V2hlZWxBY2NlbCh4dGVybUpzID0gZmFsc2UsIGJhc2UgPSAxKTogV2hlZWxBY2NlbFN0YXRlIHtcbiAgcmV0dXJuIHtcbiAgICB0aW1lOiAwLFxuICAgIG11bHQ6IGJhc2UsXG4gICAgZGlyOiAwLFxuICAgIHh0ZXJtSnMsXG4gICAgZnJhYzogMCxcbiAgICBiYXNlLFxuICAgIHBlbmRpbmdGbGlwOiBmYWxzZSxcbiAgICB3aGVlbE1vZGU6IGZhbHNlLFxuICAgIGJ1cnN0Q291bnQ6IDAsXG4gIH1cbn1cblxuLy8gTGF6eS1pbml0IGhlbHBlci4gaXNYdGVybUpzKCkgY29tYmluZXMgdGhlIFRFUk1fUFJPR1JBTSBlbnYgY2hlY2sgKyBhc3luY1xuLy8gWFRWRVJTSU9OIHByb2JlIOKAlCB0aGUgcHJvYmUgbWF5IG5vdCBoYXZlIHJlc29sdmVkIGF0IHJlbmRlciB0aW1lLCBzbyB0aGlzXG4vLyBpcyBjYWxsZWQgb24gdGhlIGZpcnN0IHdoZWVsIGV2ZW50ICg+PjUwbXMgYWZ0ZXIgc3RhcnR1cCkgd2hlbiBpdCdzIHNldHRsZWQuXG4vLyBMb2dzIGRldGVjdGVkIG1vZGUgb25jZSBzbyAtLWRlYnVnIHVzZXJzIGNhbiB2ZXJpZnkgU1NIIGRldGVjdGlvbiB3b3JrZWQuXG4vLyBUaGUgcmVuZGVyZXIgYWxzbyBjYWxscyBpc1h0ZXJtSnNIb3N0KCkgKGluIHJlbmRlci1ub2RlLXRvLW91dHB1dCkgdG9cbi8vIHNlbGVjdCB0aGUgZHJhaW4gYWxnb3JpdGhtIOKAlCBubyBzdGF0ZSB0byBwYXNzIHRocm91Z2guXG5mdW5jdGlvbiBpbml0QW5kTG9nV2hlZWxBY2NlbCgpOiBXaGVlbEFjY2VsU3RhdGUge1xuICBjb25zdCB4dGVybUpzID0gaXNYdGVybUpzKClcbiAgY29uc3QgYmFzZSA9IHJlYWRTY3JvbGxTcGVlZEJhc2UoKVxuICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgYHdoZWVsIGFjY2VsOiAke3h0ZXJtSnMgPyAnZGVjYXkgKHh0ZXJtLmpzKScgOiAnd2luZG93IChuYXRpdmUpJ30gwrcgYmFzZT0ke2Jhc2V9IMK3IFRFUk1fUFJPR1JBTT0ke3Byb2Nlc3MuZW52LlRFUk1fUFJPR1JBTSA/PyAndW5zZXQnfWAsXG4gIClcbiAgcmV0dXJuIGluaXRXaGVlbEFjY2VsKHh0ZXJtSnMsIGJhc2UpXG59XG5cbi8vIERyYWctdG8tc2Nyb2xsOiB3aGVuIGRyYWdnaW5nIHBhc3QgdGhlIHZpZXdwb3J0IGVkZ2UsIHNjcm9sbCBieSB0aGlzIG1hbnlcbi8vIHJvd3MgZXZlcnkgQVVUT1NDUk9MTF9JTlRFUlZBTF9NUy4gTW9kZSAxMDAyIG1vdXNlIHRyYWNraW5nIG9ubHkgZmlyZXMgb25cbi8vIGNlbGwgY2hhbmdlLCBzbyBhIHRpbWVyIGlzIG5lZWRlZCB0byBjb250aW51ZSBzY3JvbGxpbmcgd2hpbGUgc3RhdGlvbmFyeS5cbmNvbnN0IEFVVE9TQ1JPTExfTElORVMgPSAyXG5jb25zdCBBVVRPU0NST0xMX0lOVEVSVkFMX01TID0gNTBcbi8vIEhhcmQgY2FwIG9uIGNvbnNlY3V0aXZlIGF1dG8tc2Nyb2xsIHRpY2tzLiBJZiB0aGUgcmVsZWFzZSBldmVudCBpcyBsb3N0XG4vLyAobW91c2UgcmVsZWFzZWQgb3V0c2lkZSB0ZXJtaW5hbCB3aW5kb3cg4oCUIHNvbWUgZW11bGF0b3JzIGRvbid0IGNhcHR1cmUgdGhlXG4vLyBwb2ludGVyIGFuZCBkcm9wIHRoZSByZWxlYXNlKSwgaXNEcmFnZ2luZyBzdGF5cyB0cnVlIGFuZCB0aGUgdGltZXIgd291bGRcbi8vIHJ1biB1bnRpbCBhIHNjcm9sbCBib3VuZGFyeS4gQ2FwIGJvdW5kcyB0aGUgZGFtYWdlOyBhbnkgbmV3IGRyYWcgbW90aW9uXG4vLyBldmVudCByZXN0YXJ0cyB0aGUgY291bnQgdmlhIGNoZWNrKCnihpJzdGFydCgpLlxuY29uc3QgQVVUT1NDUk9MTF9NQVhfVElDS1MgPSAyMDAgLy8gMTBzIEAgNTBtc1xuXG4vKipcbiAqIEtleWJvYXJkIHNjcm9sbCBuYXZpZ2F0aW9uIGZvciB0aGUgZnVsbHNjcmVlbiBsYXlvdXQncyBtZXNzYWdlIHNjcm9sbCBib3guXG4gKiBQZ1VwL1BnRG4gc2Nyb2xsIGJ5IGhhbGYtdmlld3BvcnQuIE1vdXNlIHdoZWVsIHNjcm9sbHMgYnkgYSBmZXcgbGluZXMuXG4gKiBTY3JvbGxpbmcgYnJlYWtzIHN0aWNreSBtb2RlOyBDdHJsK0VuZCByZS1lbmFibGVzIGl0LiBXaGVlbGluZyBkb3duIGF0XG4gKiB0aGUgYm90dG9tIGFsc28gcmUtZW5hYmxlcyBzdGlja3kgc28gbmV3IGNvbnRlbnQgZm9sbG93cyBuYXR1cmFsbHkuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBTY3JvbGxLZXliaW5kaW5nSGFuZGxlcih7XG4gIHNjcm9sbFJlZixcbiAgaXNBY3RpdmUsXG4gIG9uU2Nyb2xsLFxuICBpc01vZGFsID0gZmFsc2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHNlbGVjdGlvbiA9IHVzZVNlbGVjdGlvbigpXG4gIGNvbnN0IHsgYWRkTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcbiAgLy8gTGF6eS1pbml0ZWQgb24gZmlyc3Qgd2hlZWwgZXZlbnQgc28gdGhlIFhUVkVSU0lPTiBwcm9iZSAoZmlyZWQgYXRcbiAgLy8gcmF3LW1vZGUtZW5hYmxlIHRpbWUpIGhhcyByZXNvbHZlZCBieSB0aGVuIOKAlCBpbml0aWFsaXppbmcgaW4gdXNlUmVmKClcbiAgLy8gd291bGQgcmVhZCBnZXRXaGVlbEJhc2UoKSBiZWZvcmUgdGhlIHByb2JlIHJlcGx5IGFycml2ZXMgb3ZlciBTU0guXG4gIGNvbnN0IHdoZWVsQWNjZWwgPSB1c2VSZWY8V2hlZWxBY2NlbFN0YXRlIHwgbnVsbD4obnVsbClcblxuICBmdW5jdGlvbiBzaG93Q29waWVkVG9hc3QodGV4dDogc3RyaW5nKTogdm9pZCB7XG4gICAgLy8gZ2V0Q2xpcGJvYXJkUGF0aCByZWFkcyBlbnYgc3luY2hyb25vdXNseSDigJQgcHJlZGljdHMgd2hhdCBzZXRDbGlwYm9hcmRcbiAgICAvLyBkaWQgKG5hdGl2ZSBwYmNvcHkgLyB0bXV4IGxvYWQtYnVmZmVyIC8gcmF3IE9TQyA1Mikgc28gd2UgY2FuIHRlbGxcbiAgICAvLyB0aGUgdXNlciB3aGV0aGVyIHBhc3RlIHdpbGwgSnVzdCBXb3JrIG9yIG5lZWRzIHByZWZpeCtdLlxuICAgIGNvbnN0IHBhdGggPSBnZXRDbGlwYm9hcmRQYXRoKClcbiAgICBjb25zdCBuID0gdGV4dC5sZW5ndGhcbiAgICBsZXQgbXNnOiBzdHJpbmdcbiAgICBzd2l0Y2ggKHBhdGgpIHtcbiAgICAgIGNhc2UgJ25hdGl2ZSc6XG4gICAgICAgIG1zZyA9IGBjb3BpZWQgJHtufSBjaGFycyB0byBjbGlwYm9hcmRgXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICd0bXV4LWJ1ZmZlcic6XG4gICAgICAgIG1zZyA9IGBjb3BpZWQgJHtufSBjaGFycyB0byB0bXV4IGJ1ZmZlciDCtyBwYXN0ZSB3aXRoIHByZWZpeCArIF1gXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdvc2M1Mic6XG4gICAgICAgIG1zZyA9IGBzZW50ICR7bn0gY2hhcnMgdmlhIE9TQyA1MiDCtyBjaGVjayB0ZXJtaW5hbCBjbGlwYm9hcmQgc2V0dGluZ3MgaWYgcGFzdGUgZmFpbHNgXG4gICAgICAgIGJyZWFrXG4gICAgfVxuICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICBrZXk6ICdzZWxlY3Rpb24tY29waWVkJyxcbiAgICAgIHRleHQ6IG1zZyxcbiAgICAgIGNvbG9yOiAnc3VnZ2VzdGlvbicsXG4gICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICB0aW1lb3V0TXM6IHBhdGggPT09ICduYXRpdmUnID8gMjAwMCA6IDQwMDAsXG4gICAgfSlcbiAgfVxuXG4gIGZ1bmN0aW9uIGNvcHlBbmRUb2FzdCgpOiB2b2lkIHtcbiAgICBjb25zdCB0ZXh0ID0gc2VsZWN0aW9uLmNvcHlTZWxlY3Rpb24oKVxuICAgIGlmICh0ZXh0KSBzaG93Q29waWVkVG9hc3QodGV4dClcbiAgfVxuXG4gIC8vIFRyYW5zbGF0ZSBzZWxlY3Rpb24gdG8gdHJhY2sgYSBrZXlib2FyZCBwYWdlIGp1bXAuIFNlbGVjdGlvbiBjb29yZHMgYXJlXG4gIC8vIHNjcmVlbi1idWZmZXItbG9jYWw7IGEgc2Nyb2xsVG8gdGhhdCBtb3ZlcyBjb250ZW50IGJ5IE4gcm93cyBtdXN0IGFsc29cbiAgLy8gc2hpZnQgYW5jaG9yK2ZvY3VzIGJ5IE4gc28gdGhlIGhpZ2hsaWdodCBzdGF5cyBvbiB0aGUgc2FtZSB0ZXh0IChuYXRpdmVcbiAgLy8gdGVybWluYWwgYmVoYXZpb3I6IHNlbGVjdGlvbiBtb3ZlcyB3aXRoIGNvbnRlbnQsIGNsaXBzIGF0IHZpZXdwb3J0XG4gIC8vIGVkZ2VzKS4gUm93cyB0aGF0IHNjcm9sbCBvdXQgb2YgdGhlIHZpZXdwb3J0IGFyZSBjYXB0dXJlZCBpbnRvXG4gIC8vIHNjcm9sbGVkT2ZmQWJvdmUvQmVsb3cgYmVmb3JlIHRoZSBzY3JvbGwgc28gZ2V0U2VsZWN0ZWRUZXh0IHN0aWxsXG4gIC8vIHJldHVybnMgdGhlIGZ1bGwgdGV4dC4gV2hlZWwgc2Nyb2xsIChzY3JvbGw6bGluZVVwL0Rvd24gdmlhIHNjcm9sbEJ5KVxuICAvLyBzdGlsbCBjbGVhcnMg4oCUIGl0cyBhc3luYyBwZW5kaW5nU2Nyb2xsRGVsdGEgZHJhaW4gbWVhbnMgdGhlIGFjdHVhbFxuICAvLyBkZWx0YSBpc24ndCBrbm93biBzeW5jaHJvbm91c2x5IChmb2xsb3ctdXApLlxuICBmdW5jdGlvbiB0cmFuc2xhdGVTZWxlY3Rpb25Gb3JKdW1wKHM6IFNjcm9sbEJveEhhbmRsZSwgZGVsdGE6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IHNlbCA9IHNlbGVjdGlvbi5nZXRTdGF0ZSgpXG4gICAgaWYgKCFzZWw/LmFuY2hvciB8fCAhc2VsLmZvY3VzKSByZXR1cm5cbiAgICBjb25zdCB0b3AgPSBzLmdldFZpZXdwb3J0VG9wKClcbiAgICBjb25zdCBib3R0b20gPSB0b3AgKyBzLmdldFZpZXdwb3J0SGVpZ2h0KCkgLSAxXG4gICAgLy8gT25seSB0cmFuc2xhdGUgaWYgdGhlIHNlbGVjdGlvbiBpcyBPTiBzY3JvbGxib3ggY29udGVudC4gU2VsZWN0aW9uc1xuICAgIC8vIGluIHRoZSBmb290ZXIvcHJvbXB0L1N0aWNreVByb21wdEhlYWRlciBhcmUgb24gc3RhdGljIHRleHQg4oCUIHRoZVxuICAgIC8vIHNjcm9sbCBkb2Vzbid0IG1vdmUgd2hhdCdzIHVuZGVyIHRoZW0uIFNhbWUgZ3VhcmQgYXMgaW5rLnRzeCdzXG4gICAgLy8gYXV0by1mb2xsb3cgdHJhbnNsYXRlIChjb21taXQgMzZhOGQxNTQpLlxuICAgIGlmIChzZWwuYW5jaG9yLnJvdyA8IHRvcCB8fCBzZWwuYW5jaG9yLnJvdyA+IGJvdHRvbSkgcmV0dXJuXG4gICAgLy8gQ3Jvc3MtYm91bmRhcnk6IGFuY2hvciBpbiBzY3JvbGxib3gsIGZvY3VzIGluIGZvb3Rlci9oZWFkZXIuIE1pcnJvclxuICAgIC8vIGluay50c3gncyBGbGFnLTMgZ3VhcmQg4oCUIGZhbGwgdGhyb3VnaCB3aXRob3V0IHNoaWZ0aW5nIE9SIGNhcHR1cmluZy5cbiAgICAvLyBUaGUgc3RhdGljIGVuZHBvaW50IHBpbnMgdGhlIHNlbGVjdGlvbjsgc2hpZnRpbmcgd291bGQgdGVsZXBvcnQgaXRcbiAgICAvLyBpbnRvIHNjcm9sbGJveCBjb250ZW50LlxuICAgIGlmIChzZWwuZm9jdXMucm93IDwgdG9wIHx8IHNlbC5mb2N1cy5yb3cgPiBib3R0b20pIHJldHVyblxuICAgIGNvbnN0IG1heCA9IE1hdGgubWF4KDAsIHMuZ2V0U2Nyb2xsSGVpZ2h0KCkgLSBzLmdldFZpZXdwb3J0SGVpZ2h0KCkpXG4gICAgY29uc3QgY3VyID0gcy5nZXRTY3JvbGxUb3AoKSArIHMuZ2V0UGVuZGluZ0RlbHRhKClcbiAgICAvLyBBY3R1YWwgc2Nyb2xsIGRpc3RhbmNlIGFmdGVyIGJvdW5kYXJ5IGNsYW1wLiBqdW1wQnkgbWF5IGNhbGxcbiAgICAvLyBzY3JvbGxUb0JvdHRvbSB3aGVuIHRhcmdldCA+PSBtYXggYnV0IHRoZSB2aWV3IGNhbid0IG1vdmUgcGFzdCBtYXgsXG4gICAgLy8gc28gdGhlIHNlbGVjdGlvbiBzaGlmdCBpcyBib3VuZGVkIGhlcmUuXG4gICAgY29uc3QgYWN0dWFsID0gTWF0aC5tYXgoMCwgTWF0aC5taW4obWF4LCBjdXIgKyBkZWx0YSkpIC0gY3VyXG4gICAgaWYgKGFjdHVhbCA9PT0gMCkgcmV0dXJuXG4gICAgaWYgKGFjdHVhbCA+IDApIHtcbiAgICAgIC8vIFNjcm9sbGluZyBkb3duOiBjb250ZW50IG1vdmVzIHVwLiBSb3dzIGF0IHRoZSBUT1AgbGVhdmUgdmlld3BvcnQuXG4gICAgICAvLyBBbmNob3IrZm9jdXMgc2hpZnQgLWFjdHVhbCBzbyB0aGV5IHRyYWNrIHRoZSBjb250ZW50IHRoYXQgbW92ZWQgdXAuXG4gICAgICBzZWxlY3Rpb24uY2FwdHVyZVNjcm9sbGVkUm93cyh0b3AsIHRvcCArIGFjdHVhbCAtIDEsICdhYm92ZScpXG4gICAgICBzZWxlY3Rpb24uc2hpZnRTZWxlY3Rpb24oLWFjdHVhbCwgdG9wLCBib3R0b20pXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIFNjcm9sbGluZyB1cDogY29udGVudCBtb3ZlcyBkb3duLiBSb3dzIGF0IHRoZSBCT1RUT00gbGVhdmUgdmlld3BvcnQuXG4gICAgICBjb25zdCBhID0gLWFjdHVhbFxuICAgICAgc2VsZWN0aW9uLmNhcHR1cmVTY3JvbGxlZFJvd3MoYm90dG9tIC0gYSArIDEsIGJvdHRvbSwgJ2JlbG93JylcbiAgICAgIHNlbGVjdGlvbi5zaGlmdFNlbGVjdGlvbihhLCB0b3AsIGJvdHRvbSlcbiAgICB9XG4gIH1cblxuICB1c2VLZXliaW5kaW5ncyhcbiAgICB7XG4gICAgICAnc2Nyb2xsOnBhZ2VVcCc6ICgpID0+IHtcbiAgICAgICAgY29uc3QgcyA9IHNjcm9sbFJlZi5jdXJyZW50XG4gICAgICAgIGlmICghcykgcmV0dXJuXG4gICAgICAgIGNvbnN0IGQgPSAtTWF0aC5tYXgoMSwgTWF0aC5mbG9vcihzLmdldFZpZXdwb3J0SGVpZ2h0KCkgLyAyKSlcbiAgICAgICAgdHJhbnNsYXRlU2VsZWN0aW9uRm9ySnVtcChzLCBkKVxuICAgICAgICBjb25zdCBzdGlja3kgPSBqdW1wQnkocywgZClcbiAgICAgICAgb25TY3JvbGw/LihzdGlja3ksIHMpXG4gICAgICB9LFxuICAgICAgJ3Njcm9sbDpwYWdlRG93bic6ICgpID0+IHtcbiAgICAgICAgY29uc3QgcyA9IHNjcm9sbFJlZi5jdXJyZW50XG4gICAgICAgIGlmICghcykgcmV0dXJuXG4gICAgICAgIGNvbnN0IGQgPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKHMuZ2V0Vmlld3BvcnRIZWlnaHQoKSAvIDIpKVxuICAgICAgICB0cmFuc2xhdGVTZWxlY3Rpb25Gb3JKdW1wKHMsIGQpXG4gICAgICAgIGNvbnN0IHN0aWNreSA9IGp1bXBCeShzLCBkKVxuICAgICAgICBvblNjcm9sbD8uKHN0aWNreSwgcylcbiAgICAgIH0sXG4gICAgICAnc2Nyb2xsOmxpbmVVcCc6ICgpID0+IHtcbiAgICAgICAgLy8gV2hlZWw6IHNjcm9sbEJ5IGFjY3VtdWxhdGVzIGludG8gcGVuZGluZ1Njcm9sbERlbHRhLCBkcmFpbmVkIGFzeW5jXG4gICAgICAgIC8vIGJ5IHRoZSByZW5kZXJlci4gY2FwdHVyZVNjcm9sbGVkUm93cyBjYW4ndCByZWFkIHRoZSBvdXRnb2luZyByb3dzXG4gICAgICAgIC8vIGJlZm9yZSB0aGV5IGxlYXZlIChkcmFpbiBpcyBub24tZGV0ZXJtaW5pc3RpYykuIENsZWFyIGZvciBub3cuXG4gICAgICAgIHNlbGVjdGlvbi5jbGVhclNlbGVjdGlvbigpXG4gICAgICAgIGNvbnN0IHMgPSBzY3JvbGxSZWYuY3VycmVudFxuICAgICAgICAvLyBSZXR1cm4gZmFsc2UgKG5vdCBjb25zdW1lZCkgd2hlbiB0aGUgU2Nyb2xsQm94IGNvbnRlbnQgZml0cyDigJRcbiAgICAgICAgLy8gc2Nyb2xsIHdvdWxkIGJlIGEgbm8tb3AuIExldHMgYSBjaGlsZCBjb21wb25lbnQncyBoYW5kbGVyIHRha2VcbiAgICAgICAgLy8gdGhlIHdoZWVsIGV2ZW50IGluc3RlYWQgKGUuZy4gU2V0dGluZ3MgQ29uZmlnJ3MgbGlzdCBuYXZpZ2F0aW9uXG4gICAgICAgIC8vIGluc2lkZSB0aGUgY2VudGVyZWQgTW9kYWwsIHdoZXJlIHRoZSBwYWdpbmF0ZWQgc2xpY2UgYWx3YXlzIGZpdHMpLlxuICAgICAgICBpZiAoIXMgfHwgcy5nZXRTY3JvbGxIZWlnaHQoKSA8PSBzLmdldFZpZXdwb3J0SGVpZ2h0KCkpIHJldHVybiBmYWxzZVxuICAgICAgICB3aGVlbEFjY2VsLmN1cnJlbnQgPz89IGluaXRBbmRMb2dXaGVlbEFjY2VsKClcbiAgICAgICAgc2Nyb2xsVXAocywgY29tcHV0ZVdoZWVsU3RlcCh3aGVlbEFjY2VsLmN1cnJlbnQsIC0xLCBwZXJmb3JtYW5jZS5ub3coKSkpXG4gICAgICAgIG9uU2Nyb2xsPy4oZmFsc2UsIHMpXG4gICAgICB9LFxuICAgICAgJ3Njcm9sbDpsaW5lRG93bic6ICgpID0+IHtcbiAgICAgICAgc2VsZWN0aW9uLmNsZWFyU2VsZWN0aW9uKClcbiAgICAgICAgY29uc3QgcyA9IHNjcm9sbFJlZi5jdXJyZW50XG4gICAgICAgIGlmICghcyB8fCBzLmdldFNjcm9sbEhlaWdodCgpIDw9IHMuZ2V0Vmlld3BvcnRIZWlnaHQoKSkgcmV0dXJuIGZhbHNlXG4gICAgICAgIHdoZWVsQWNjZWwuY3VycmVudCA/Pz0gaW5pdEFuZExvZ1doZWVsQWNjZWwoKVxuICAgICAgICBjb25zdCBzdGVwID0gY29tcHV0ZVdoZWVsU3RlcCh3aGVlbEFjY2VsLmN1cnJlbnQsIDEsIHBlcmZvcm1hbmNlLm5vdygpKVxuICAgICAgICBjb25zdCByZWFjaGVkQm90dG9tID0gc2Nyb2xsRG93bihzLCBzdGVwKVxuICAgICAgICBvblNjcm9sbD8uKHJlYWNoZWRCb3R0b20sIHMpXG4gICAgICB9LFxuICAgICAgJ3Njcm9sbDp0b3AnOiAoKSA9PiB7XG4gICAgICAgIGNvbnN0IHMgPSBzY3JvbGxSZWYuY3VycmVudFxuICAgICAgICBpZiAoIXMpIHJldHVyblxuICAgICAgICB0cmFuc2xhdGVTZWxlY3Rpb25Gb3JKdW1wKHMsIC0ocy5nZXRTY3JvbGxUb3AoKSArIHMuZ2V0UGVuZGluZ0RlbHRhKCkpKVxuICAgICAgICBzLnNjcm9sbFRvKDApXG4gICAgICAgIG9uU2Nyb2xsPy4oZmFsc2UsIHMpXG4gICAgICB9LFxuICAgICAgJ3Njcm9sbDpib3R0b20nOiAoKSA9PiB7XG4gICAgICAgIGNvbnN0IHMgPSBzY3JvbGxSZWYuY3VycmVudFxuICAgICAgICBpZiAoIXMpIHJldHVyblxuICAgICAgICBjb25zdCBtYXggPSBNYXRoLm1heCgwLCBzLmdldFNjcm9sbEhlaWdodCgpIC0gcy5nZXRWaWV3cG9ydEhlaWdodCgpKVxuICAgICAgICB0cmFuc2xhdGVTZWxlY3Rpb25Gb3JKdW1wKFxuICAgICAgICAgIHMsXG4gICAgICAgICAgbWF4IC0gKHMuZ2V0U2Nyb2xsVG9wKCkgKyBzLmdldFBlbmRpbmdEZWx0YSgpKSxcbiAgICAgICAgKVxuICAgICAgICAvLyBzY3JvbGxUbyhtYXgpIGVhZ2VyLXdyaXRlcyBzY3JvbGxUb3Agc28gdGhlIHJlbmRlci1waGFzZSBzdGlja3lcbiAgICAgICAgLy8gZm9sbG93IGNvbXB1dGVzIGZvbGxvd0RlbHRhPTAuIFdpdGhvdXQgdGhpcywgc2Nyb2xsVG9Cb3R0b20oKVxuICAgICAgICAvLyBhbG9uZSBsZWF2ZXMgc2Nyb2xsVG9wIHN0YWxlIOKGkiBmb2xsb3dEZWx0YT1tYXgtc3RhbGUg4oaSXG4gICAgICAgIC8vIHNoaWZ0U2VsZWN0aW9uRm9yRm9sbG93IGFwcGxpZXMgdGhlIFNBTUUgc2hpZnQgd2UgYWxyZWFkeSBkaWRcbiAgICAgICAgLy8gYWJvdmUsIDLDlyBvZmZzZXQuIHNjcm9sbFRvQm90dG9tKCkgdGhlbiByZS1lbmFibGVzIHN0aWNreS5cbiAgICAgICAgcy5zY3JvbGxUbyhtYXgpXG4gICAgICAgIHMuc2Nyb2xsVG9Cb3R0b20oKVxuICAgICAgICBvblNjcm9sbD8uKHRydWUsIHMpXG4gICAgICB9LFxuICAgICAgJ3NlbGVjdGlvbjpjb3B5JzogY29weUFuZFRvYXN0LFxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnU2Nyb2xsJywgaXNBY3RpdmUgfSxcbiAgKVxuXG4gIC8vIHNjcm9sbDpoYWxmUGFnZSovZnVsbFBhZ2UqIGhhdmUgbm8gZGVmYXVsdCBrZXkgYmluZGluZ3Mg4oCUIGN0cmwrdS9kL2IvZlxuICAvLyBhbGwgaGF2ZSByZWFsIG93bmVycyBpbiBub3JtYWwgbW9kZSAoa2lsbC1saW5lL2V4aXQvdGFzazpiYWNrZ3JvdW5kL1xuICAvLyBraWxsLWFnZW50cykuIFRyYW5zY3JpcHQgbW9kZSBnZXRzIHRoZW0gdmlhIHRoZSBpc01vZGFsIHJhdyB1c2VJbnB1dFxuICAvLyBiZWxvdy4gVGhlc2UgaGFuZGxlcnMgc3RheSBmb3IgY3VzdG9tIHJlYmluZHMgb25seS5cbiAgdXNlS2V5YmluZGluZ3MoXG4gICAge1xuICAgICAgJ3Njcm9sbDpoYWxmUGFnZVVwJzogKCkgPT4ge1xuICAgICAgICBjb25zdCBzID0gc2Nyb2xsUmVmLmN1cnJlbnRcbiAgICAgICAgaWYgKCFzKSByZXR1cm5cbiAgICAgICAgY29uc3QgZCA9IC1NYXRoLm1heCgxLCBNYXRoLmZsb29yKHMuZ2V0Vmlld3BvcnRIZWlnaHQoKSAvIDIpKVxuICAgICAgICB0cmFuc2xhdGVTZWxlY3Rpb25Gb3JKdW1wKHMsIGQpXG4gICAgICAgIGNvbnN0IHN0aWNreSA9IGp1bXBCeShzLCBkKVxuICAgICAgICBvblNjcm9sbD8uKHN0aWNreSwgcylcbiAgICAgIH0sXG4gICAgICAnc2Nyb2xsOmhhbGZQYWdlRG93bic6ICgpID0+IHtcbiAgICAgICAgY29uc3QgcyA9IHNjcm9sbFJlZi5jdXJyZW50XG4gICAgICAgIGlmICghcykgcmV0dXJuXG4gICAgICAgIGNvbnN0IGQgPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKHMuZ2V0Vmlld3BvcnRIZWlnaHQoKSAvIDIpKVxuICAgICAgICB0cmFuc2xhdGVTZWxlY3Rpb25Gb3JKdW1wKHMsIGQpXG4gICAgICAgIGNvbnN0IHN0aWNreSA9IGp1bXBCeShzLCBkKVxuICAgICAgICBvblNjcm9sbD8uKHN0aWNreSwgcylcbiAgICAgIH0sXG4gICAgICAnc2Nyb2xsOmZ1bGxQYWdlVXAnOiAoKSA9PiB7XG4gICAgICAgIGNvbnN0IHMgPSBzY3JvbGxSZWYuY3VycmVudFxuICAgICAgICBpZiAoIXMpIHJldHVyblxuICAgICAgICBjb25zdCBkID0gLU1hdGgubWF4KDEsIHMuZ2V0Vmlld3BvcnRIZWlnaHQoKSlcbiAgICAgICAgdHJhbnNsYXRlU2VsZWN0aW9uRm9ySnVtcChzLCBkKVxuICAgICAgICBjb25zdCBzdGlja3kgPSBqdW1wQnkocywgZClcbiAgICAgICAgb25TY3JvbGw/LihzdGlja3ksIHMpXG4gICAgICB9LFxuICAgICAgJ3Njcm9sbDpmdWxsUGFnZURvd24nOiAoKSA9PiB7XG4gICAgICAgIGNvbnN0IHMgPSBzY3JvbGxSZWYuY3VycmVudFxuICAgICAgICBpZiAoIXMpIHJldHVyblxuICAgICAgICBjb25zdCBkID0gTWF0aC5tYXgoMSwgcy5nZXRWaWV3cG9ydEhlaWdodCgpKVxuICAgICAgICB0cmFuc2xhdGVTZWxlY3Rpb25Gb3JKdW1wKHMsIGQpXG4gICAgICAgIGNvbnN0IHN0aWNreSA9IGp1bXBCeShzLCBkKVxuICAgICAgICBvblNjcm9sbD8uKHN0aWNreSwgcylcbiAgICAgIH0sXG4gICAgfSxcbiAgICB7IGNvbnRleHQ6ICdTY3JvbGwnLCBpc0FjdGl2ZSB9LFxuICApXG5cbiAgLy8gTW9kYWwgcGFnZXIga2V5cyDigJQgdHJhbnNjcmlwdCBtb2RlIG9ubHkuIGxlc3MvdG11eCBjb3B5LW1vZGUgbGluZWFnZTpcbiAgLy8gY3RybCt1L2QgKGhhbGYtcGFnZSksIGN0cmwrYi9mIChmdWxsLXBhZ2UpLCBnL0cgKHRvcC9ib3R0b20pLiBUb20nc1xuICAvLyByZXNvbHV0aW9uICgyMDI2LTAzLTE1KTogXCJJbiBjdHJsLW8gbW9kZSwgY3RybC11LCBjdHJsLWQsIGV0Yy4gc2hvdWxkXG4gIC8vIHJvdWdobHkganVzdCB3b3JrIVwiIOKAlCB0cmFuc2NyaXB0IGlzIHRoZSBjb3B5LW1vZGUgY29udGFpbmVyLlxuICAvL1xuICAvLyBTYWZlIGJlY2F1c2UgdGhlIGNvbmZsaWN0aW5nIGhhbmRsZXJzIGFyZW4ndCByZWFjaGFibGUgaGVyZTpcbiAgLy8gICBjdHJsK3Ug4oaSIGtpbGwtbGluZSwgY3RybCtkIOKGkiBleGl0OiBQcm9tcHRJbnB1dCBub3QgbW91bnRlZFxuICAvLyAgIGN0cmwrYiDihpIgdGFzazpiYWNrZ3JvdW5kOiBTZXNzaW9uQmFja2dyb3VuZEhpbnQgbm90IG1vdW50ZWRcbiAgLy8gICBjdHJsK2Yg4oaSIGNoYXQ6a2lsbEFnZW50cyBtb3ZlZCB0byBjdHJsK3ggY3RybCtrOyBubyBjb25mbGljdFxuICAvLyAgIGcvRyDihpIgcHJpbnRhYmxlIGNoYXJzOiBubyBwcm9tcHQgdG8gZWF0IHRoZW0sIG5vIHZpbS9zdGlja3kgZ2F0ZSBuZWVkZWRcbiAgLy9cbiAgLy8gVE9ETyhzZWFyY2gpOiBgL2AsIG4vTiDigJQgYnVpbGQgb24gUmljaGFyZCBLaW0ncyBkOTRiMDdhZGQ0IChicmFuY2hcbiAgLy8gY2xhdWRlL2p1bXAtcmVjZW50LW1lc3NhZ2UtQ0VQY3EpLiBnZXRJdGVtWSBZb2dhLXdhbGsgKyBjb21wdXRlT3JpZ2luICtcbiAgLy8gYW5jaG9yWSBhbHJlYWR5IHNvbHZlIHNjcm9sbC10by1pbmRleC4ganVtcFRvUHJldlR1cm4gaXMgdGhlIG4vTlxuICAvLyB0ZW1wbGF0ZS4gU2luZ2xlLXNob3QgdmlhIE9WRVJTQ0FOX1JPV1M9ODA7IHR3by1waGFzZSB3YXMgdHJpZWQgYW5kXG4gIC8vIGFiYW5kb25lZCAo4p2vIG9zY2lsbGF0aW9uKS4gU2VlIHRlYW0gbWVtb3J5IHNjcm9sbC1jb3B5LW1vZGUtZGVzaWduLm1kLlxuICB1c2VJbnB1dChcbiAgICAoaW5wdXQsIGtleSwgZXZlbnQpID0+IHtcbiAgICAgIGNvbnN0IHMgPSBzY3JvbGxSZWYuY3VycmVudFxuICAgICAgaWYgKCFzKSByZXR1cm5cbiAgICAgIGNvbnN0IHN0aWNreSA9IGFwcGx5TW9kYWxQYWdlckFjdGlvbihzLCBtb2RhbFBhZ2VyQWN0aW9uKGlucHV0LCBrZXkpLCBkID0+XG4gICAgICAgIHRyYW5zbGF0ZVNlbGVjdGlvbkZvckp1bXAocywgZCksXG4gICAgICApXG4gICAgICBpZiAoc3RpY2t5ID09PSBudWxsKSByZXR1cm5cbiAgICAgIG9uU2Nyb2xsPy4oc3RpY2t5LCBzKVxuICAgICAgZXZlbnQuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKClcbiAgICB9LFxuICAgIHsgaXNBY3RpdmU6IGlzQWN0aXZlICYmIGlzTW9kYWwgfSxcbiAgKVxuXG4gIC8vIEVzYyBjbGVhcnMgc2VsZWN0aW9uOyBhbnkgb3RoZXIga2V5c3Ryb2tlIGFsc28gY2xlYXJzIGl0IChtYXRjaGVzXG4gIC8vIG5hdGl2ZSB0ZXJtaW5hbCBiZWhhdmlvciB3aGVyZSBzZWxlY3Rpb24gZGlzYXBwZWFycyBvbiBpbnB1dCkuXG4gIC8vIEN0cmwrQyBjb3BpZXMgd2hlbiBhIHNlbGVjdGlvbiBleGlzdHMg4oCUIG5lZWRlZCBvbiBsZWdhY3kgdGVybWluYWxzXG4gIC8vIHdoZXJlIGN0cmwrc2hpZnQrYyBzZW5kcyB0aGUgc2FtZSBieXRlIChcXHgwMywgc2hpZnQgaXMgbG9zdCkgYW5kXG4gIC8vIGNtZCtjIG5ldmVyIHJlYWNoZXMgdGhlIHB0eSAodGVybWluYWwgaW50ZXJjZXB0cyBpdCBmb3IgRWRpdCA+IENvcHkpLlxuICAvLyBIYW5kbGVkIHZpYSByYXcgdXNlSW5wdXQgc28gd2UgY2FuIGNvbmRpdGlvbmFsbHkgY29uc3VtZTogRXNjL0N0cmwrQ1xuICAvLyBvbmx5IHN0b3AgcHJvcGFnYXRpb24gd2hlbiBhIHNlbGVjdGlvbiBleGlzdHMsIGxldHRpbmcgdGhlbSBzdGlsbCB3b3JrXG4gIC8vIGZvciBjYW5jZWwtcmVxdWVzdCAvIGludGVycnVwdCBvdGhlcndpc2UuIE90aGVyIGtleXMgbmV2ZXIgc3RvcFxuICAvLyBwcm9wYWdhdGlvbiDigJQgdGhleSdyZSBvYnNlcnZlZCB0byBjbGVhciBzZWxlY3Rpb24gYXMgYSBzaWRlLWVmZmVjdC5cbiAgLy8gVGhlIHNlbGVjdGlvbjpjb3B5IGtleWJpbmRpbmcgKGN0cmwrc2hpZnQrYyAvIGNtZCtjKSByZWdpc3RlcnMgYWJvdmVcbiAgLy8gdmlhIHVzZUtleWJpbmRpbmdzIGFuZCBjb25zdW1lcyBpdHMgZXZlbnQgYmVmb3JlIHJlYWNoaW5nIGhlcmUuXG4gIHVzZUlucHV0KFxuICAgIChpbnB1dCwga2V5LCBldmVudCkgPT4ge1xuICAgICAgaWYgKCFzZWxlY3Rpb24uaGFzU2VsZWN0aW9uKCkpIHJldHVyblxuICAgICAgaWYgKGtleS5lc2NhcGUpIHtcbiAgICAgICAgc2VsZWN0aW9uLmNsZWFyU2VsZWN0aW9uKClcbiAgICAgICAgZXZlbnQuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBpZiAoa2V5LmN0cmwgJiYgIWtleS5zaGlmdCAmJiAha2V5Lm1ldGEgJiYgaW5wdXQgPT09ICdjJykge1xuICAgICAgICBjb3B5QW5kVG9hc3QoKVxuICAgICAgICBldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIGNvbnN0IG1vdmUgPSBzZWxlY3Rpb25Gb2N1c01vdmVGb3JLZXkoa2V5KVxuICAgICAgaWYgKG1vdmUpIHtcbiAgICAgICAgc2VsZWN0aW9uLm1vdmVGb2N1cyhtb3ZlKVxuICAgICAgICBldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIGlmIChzaG91bGRDbGVhclNlbGVjdGlvbk9uS2V5KGtleSkpIHtcbiAgICAgICAgc2VsZWN0aW9uLmNsZWFyU2VsZWN0aW9uKClcbiAgICAgIH1cbiAgICB9LFxuICAgIHsgaXNBY3RpdmUgfSxcbiAgKVxuXG4gIHVzZURyYWdUb1Njcm9sbChzY3JvbGxSZWYsIHNlbGVjdGlvbiwgaXNBY3RpdmUsIG9uU2Nyb2xsKVxuICB1c2VDb3B5T25TZWxlY3Qoc2VsZWN0aW9uLCBpc0FjdGl2ZSwgc2hvd0NvcGllZFRvYXN0KVxuICB1c2VTZWxlY3Rpb25CZ0NvbG9yKHNlbGVjdGlvbilcblxuICByZXR1cm4gbnVsbFxufVxuXG4vKipcbiAqIEF1dG8tc2Nyb2xsIHRoZSBTY3JvbGxCb3ggd2hlbiB0aGUgdXNlciBkcmFncyBhIHNlbGVjdGlvbiBwYXN0IGl0cyB0b3Agb3JcbiAqIGJvdHRvbSBlZGdlLiBUaGUgYW5jaG9yIGlzIHNoaWZ0ZWQgaW4gdGhlIG9wcG9zaXRlIGRpcmVjdGlvbiBzbyBpdCBzdGF5c1xuICogb24gdGhlIHNhbWUgY29udGVudCAoY29udGVudCB0aGF0IHdhcyBhdCB2aWV3cG9ydCByb3cgTiBpcyBub3cgYXQgcm93IE7CsWRcbiAqIGFmdGVyIHNjcm9sbGluZyBieSBkKS4gRm9jdXMgc3RheXMgYXQgdGhlIG1vdXNlIHBvc2l0aW9uIChlZGdlIHJvdykuXG4gKlxuICogU2VsZWN0aW9uIGNvb3JkcyBhcmUgc2NyZWVuLWJ1ZmZlci1sb2NhbCwgc28gdGhlIGFuY2hvciBpcyBjbGFtcGVkIHRvIHRoZVxuICogdmlld3BvcnQgYm91bmRzIG9uY2UgdGhlIG9yaWdpbmFsIGNvbnRlbnQgc2Nyb2xscyBvdXQuIFRvIHByZXNlcnZlIHRoZSBmdWxsXG4gKiBzZWxlY3Rpb24sIHJvd3MgYWJvdXQgdG8gc2Nyb2xsIG91dCBhcmUgY2FwdHVyZWQgaW50byBzY3JvbGxlZE9mZkFib3ZlL1xuICogc2Nyb2xsZWRPZmZCZWxvdyBiZWZvcmUgZWFjaCBzY3JvbGwgc3RlcCBhbmQgam9pbmVkIGJhY2sgaW4gYnlcbiAqIGdldFNlbGVjdGVkVGV4dC5cbiAqL1xuZnVuY3Rpb24gdXNlRHJhZ1RvU2Nyb2xsKFxuICBzY3JvbGxSZWY6IFJlZk9iamVjdDxTY3JvbGxCb3hIYW5kbGUgfCBudWxsPixcbiAgc2VsZWN0aW9uOiBSZXR1cm5UeXBlPHR5cGVvZiB1c2VTZWxlY3Rpb24+LFxuICBpc0FjdGl2ZTogYm9vbGVhbixcbiAgb25TY3JvbGw6IFByb3BzWydvblNjcm9sbCddLFxuKTogdm9pZCB7XG4gIGNvbnN0IHRpbWVyUmVmID0gdXNlUmVmPE5vZGVKUy5UaW1lb3V0IHwgbnVsbD4obnVsbClcbiAgY29uc3QgZGlyUmVmID0gdXNlUmVmPC0xIHwgMCB8IDE+KDApIC8vIC0xIHNjcm9sbGluZyB1cCwgKzEgZG93biwgMCBpZGxlXG4gIC8vIFN1cnZpdmVzIHN0b3AoKSDigJQgcmVzZXQgb25seSBvbiBkcmFnLWZpbmlzaC4gU2VlIGNoZWNrKCkgZm9yIHNlbWFudGljcy5cbiAgY29uc3QgbGFzdFNjcm9sbGVkRGlyUmVmID0gdXNlUmVmPC0xIHwgMCB8IDE+KDApXG4gIGNvbnN0IHRpY2tzUmVmID0gdXNlUmVmKDApXG4gIC8vIG9uU2Nyb2xsIG1heSBjaGFuZ2UgaWRlbnRpdHkgZXZlcnkgcmVuZGVyIChpZiBub3QgbWVtb2l6ZWQgYnkgY2FsbGVyKS5cbiAgLy8gUmVhZCB0aHJvdWdoIGEgcmVmIHNvIHRoZSBlZmZlY3QgZG9lc24ndCByZS1zdWJzY3JpYmUgYW5kIGtpbGwgdGhlIHRpbWVyXG4gIC8vIG9uIGVhY2ggc2Nyb2xsLWluZHVjZWQgcmUtcmVuZGVyLlxuICBjb25zdCBvblNjcm9sbFJlZiA9IHVzZVJlZihvblNjcm9sbClcbiAgb25TY3JvbGxSZWYuY3VycmVudCA9IG9uU2Nyb2xsXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoIWlzQWN0aXZlKSByZXR1cm5cblxuICAgIGZ1bmN0aW9uIHN0b3AoKTogdm9pZCB7XG4gICAgICBkaXJSZWYuY3VycmVudCA9IDBcbiAgICAgIGlmICh0aW1lclJlZi5jdXJyZW50KSB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwodGltZXJSZWYuY3VycmVudClcbiAgICAgICAgdGltZXJSZWYuY3VycmVudCA9IG51bGxcbiAgICAgIH1cbiAgICB9XG5cbiAgICBmdW5jdGlvbiB0aWNrKCk6IHZvaWQge1xuICAgICAgY29uc3Qgc2VsID0gc2VsZWN0aW9uLmdldFN0YXRlKClcbiAgICAgIGNvbnN0IHMgPSBzY3JvbGxSZWYuY3VycmVudFxuICAgICAgY29uc3QgZGlyID0gZGlyUmVmLmN1cnJlbnRcbiAgICAgIC8vIGRpciA9PT0gMCBkZWZlbmRzIGFnYWluc3QgYSBzdGFsZSBpbnRlcnZhbCAoc3RhcnQoKSBtYXkgaGF2ZSBzZXQgb25lXG4gICAgICAvLyBhZnRlciB0aGUgaW1tZWRpYXRlIHRpY2sgYWxyZWFkeSBjYWxsZWQgc3RvcCgpIGF0IGEgc2Nyb2xsIGJvdW5kYXJ5KS5cbiAgICAgIC8vIHRpY2tzIGNhcCBkZWZlbmRzIGFnYWluc3QgYSBsb3N0IHJlbGVhc2UgZXZlbnQgKG1vdXNlIHJlbGVhc2VkXG4gICAgICAvLyBvdXRzaWRlIHRlcm1pbmFsIHdpbmRvdykgbGVhdmluZyBpc0RyYWdnaW5nIHN0dWNrIHRydWUuXG4gICAgICBpZiAoXG4gICAgICAgICFzZWw/LmlzRHJhZ2dpbmcgfHxcbiAgICAgICAgIXNlbC5mb2N1cyB8fFxuICAgICAgICAhcyB8fFxuICAgICAgICBkaXIgPT09IDAgfHxcbiAgICAgICAgKyt0aWNrc1JlZi5jdXJyZW50ID4gQVVUT1NDUk9MTF9NQVhfVElDS1NcbiAgICAgICkge1xuICAgICAgICBzdG9wKClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICAvLyBzY3JvbGxCeSBhY2N1bXVsYXRlcyBpbnRvIHBlbmRpbmdTY3JvbGxEZWx0YTsgdGhlIHNjcmVlbiBidWZmZXJcbiAgICAgIC8vIGRvZXNuJ3QgdXBkYXRlIHVudGlsIHRoZSBuZXh0IHJlbmRlciBkcmFpbnMgaXQuIElmIGEgcHJldmlvdXNcbiAgICAgIC8vIHRpY2sncyBzY3JvbGwgaGFzbid0IGRyYWluZWQgeWV0LCBjYXB0dXJlU2Nyb2xsZWRSb3dzIHdvdWxkIHJlYWRcbiAgICAgIC8vIHN0YWxlIGNvbnRlbnQgKHNhbWUgcm93cyBhcyBsYXN0IHRpY2sg4oaSIGR1cGxpY2F0ZWQgaW4gdGhlXG4gICAgICAvLyBhY2N1bXVsYXRvciBBTkQgbWlzc2luZyB0aGUgcm93cyB0aGF0IGFjdHVhbGx5IHNjcm9sbGVkIG91dCkuXG4gICAgICAvLyBTa2lwIHRoaXMgdGljazsgdGhlIDUwbXMgaW50ZXJ2YWwgd2lsbCByZXRyeSBhZnRlciBJbmsncyAxNm1zXG4gICAgICAvLyByZW5kZXIgY2F0Y2hlcyB1cC4gQWxzbyBwcmV2ZW50cyBzaGlmdEFuY2hvciBmcm9tIGRlc3luY2luZy5cbiAgICAgIGlmIChzLmdldFBlbmRpbmdEZWx0YSgpICE9PSAwKSByZXR1cm5cbiAgICAgIGNvbnN0IHRvcCA9IHMuZ2V0Vmlld3BvcnRUb3AoKVxuICAgICAgY29uc3QgYm90dG9tID0gdG9wICsgcy5nZXRWaWV3cG9ydEhlaWdodCgpIC0gMVxuICAgICAgLy8gQ2xhbXAgYW5jaG9yIHdpdGhpbiBbdG9wLCBib3R0b21dLiBOb3QgWzAsIGJvdHRvbV06IHRoZSBTY3JvbGxCb3hcbiAgICAgIC8vIHBhZGRpbmcgcm93IGF0IDAgd291bGQgcHJvZHVjZSBhIGJsYW5rIGxpbmUgYmV0d2VlbiBzY3JvbGxlZE9mZkFib3ZlXG4gICAgICAvLyBhbmQgdGhlIG9uLXNjcmVlbiBjb250ZW50IGluIGdldFNlbGVjdGVkVGV4dC4gVGhlIHBhZGRpbmctcm93XG4gICAgICAvLyBoaWdobGlnaHQgd2FzIGEgbWlub3IgdmlzdWFsIG5pY2V0eTsgdGV4dCBjb3JyZWN0bmVzcyB3aW5zLlxuICAgICAgaWYgKGRpciA8IDApIHtcbiAgICAgICAgaWYgKHMuZ2V0U2Nyb2xsVG9wKCkgPD0gMCkge1xuICAgICAgICAgIHN0b3AoKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIFNjcm9sbGluZyB1cDogY29udGVudCBtb3ZlcyBkb3duIGluIHZpZXdwb3J0LCBzbyBhbmNob3Igcm93ICtOLlxuICAgICAgICAvLyBDbGFtcCB0byBhY3R1YWwgc2Nyb2xsIGRpc3RhbmNlIHNvIGFuY2hvciBzdGF5cyBpbiBzeW5jIHdoZW4gbmVhclxuICAgICAgICAvLyB0aGUgdG9wIGJvdW5kYXJ5IChyZW5kZXJlciBjbGFtcHMgc2Nyb2xsVG9wIHRvIDAgb24gZHJhaW4pLlxuICAgICAgICBjb25zdCBhY3R1YWwgPSBNYXRoLm1pbihBVVRPU0NST0xMX0xJTkVTLCBzLmdldFNjcm9sbFRvcCgpKVxuICAgICAgICAvLyBDYXB0dXJlIHJvd3MgYWJvdXQgdG8gc2Nyb2xsIG91dCB0aGUgQk9UVE9NIGJlZm9yZSBzY3JvbGxCeVxuICAgICAgICAvLyBvdmVyd3JpdGVzIHRoZW0uIE9ubHkgcm93cyBpbnNpZGUgdGhlIHNlbGVjdGlvbiBhcmUgY2FwdHVyZWRcbiAgICAgICAgLy8gKGNhcHR1cmVTY3JvbGxlZFJvd3MgaW50ZXJzZWN0cyB3aXRoIHNlbGVjdGlvbiBib3VuZHMpLlxuICAgICAgICBzZWxlY3Rpb24uY2FwdHVyZVNjcm9sbGVkUm93cyhib3R0b20gLSBhY3R1YWwgKyAxLCBib3R0b20sICdiZWxvdycpXG4gICAgICAgIHNlbGVjdGlvbi5zaGlmdEFuY2hvcihhY3R1YWwsIDAsIGJvdHRvbSlcbiAgICAgICAgcy5zY3JvbGxCeSgtQVVUT1NDUk9MTF9MSU5FUylcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IG1heCA9IE1hdGgubWF4KDAsIHMuZ2V0U2Nyb2xsSGVpZ2h0KCkgLSBzLmdldFZpZXdwb3J0SGVpZ2h0KCkpXG4gICAgICAgIGlmIChzLmdldFNjcm9sbFRvcCgpID49IG1heCkge1xuICAgICAgICAgIHN0b3AoKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIFNjcm9sbGluZyBkb3duOiBjb250ZW50IG1vdmVzIHVwIGluIHZpZXdwb3J0LCBzbyBhbmNob3Igcm93IC1OLlxuICAgICAgICAvLyBDbGFtcCB0byBhY3R1YWwgc2Nyb2xsIGRpc3RhbmNlIHNvIGFuY2hvciBzdGF5cyBpbiBzeW5jIHdoZW4gbmVhclxuICAgICAgICAvLyB0aGUgYm90dG9tIGJvdW5kYXJ5IChyZW5kZXJlciBjbGFtcHMgc2Nyb2xsVG9wIHRvIG1heCBvbiBkcmFpbikuXG4gICAgICAgIGNvbnN0IGFjdHVhbCA9IE1hdGgubWluKEFVVE9TQ1JPTExfTElORVMsIG1heCAtIHMuZ2V0U2Nyb2xsVG9wKCkpXG4gICAgICAgIC8vIENhcHR1cmUgcm93cyBhYm91dCB0byBzY3JvbGwgb3V0IHRoZSBUT1AuXG4gICAgICAgIHNlbGVjdGlvbi5jYXB0dXJlU2Nyb2xsZWRSb3dzKHRvcCwgdG9wICsgYWN0dWFsIC0gMSwgJ2Fib3ZlJylcbiAgICAgICAgc2VsZWN0aW9uLnNoaWZ0QW5jaG9yKC1hY3R1YWwsIHRvcCwgYm90dG9tKVxuICAgICAgICBzLnNjcm9sbEJ5KEFVVE9TQ1JPTExfTElORVMpXG4gICAgICB9XG4gICAgICBvblNjcm9sbFJlZi5jdXJyZW50Py4oZmFsc2UsIHMpXG4gICAgfVxuXG4gICAgZnVuY3Rpb24gc3RhcnQoZGlyOiAtMSB8IDEpOiB2b2lkIHtcbiAgICAgIC8vIFJlY29yZCBCRUZPUkUgZWFybHktcmV0dXJuOiB0aGUgZW1wdHktYWNjdW11bGF0b3IgcmVzZXQgaW4gY2hlY2soKVxuICAgICAgLy8gbWF5IGhhdmUgemVyb2VkIHRoaXMgZHVyaW5nIHRoZSBwcmUtY3Jvc3NpbmcgcGhhc2UgKGFjY3VtdWxhdG9yc1xuICAgICAgLy8gZW1wdHkgdW50aWwgdGhlIGFuY2hvciByb3cgZW50ZXJzIHRoZSBjYXB0dXJlIHJhbmdlKS4gUmUtcmVjb3JkXG4gICAgICAvLyBvbiBldmVyeSBjYWxsIHNvIHRoZSBjb3JydXB0aW9uIGlzIGluc3RhbnRseSBoZWFsZWQuXG4gICAgICBsYXN0U2Nyb2xsZWREaXJSZWYuY3VycmVudCA9IGRpclxuICAgICAgaWYgKGRpclJlZi5jdXJyZW50ID09PSBkaXIpIHJldHVybiAvLyBhbHJlYWR5IGdvaW5nIHRoaXMgd2F5XG4gICAgICBzdG9wKClcbiAgICAgIGRpclJlZi5jdXJyZW50ID0gZGlyXG4gICAgICB0aWNrc1JlZi5jdXJyZW50ID0gMFxuICAgICAgdGljaygpXG4gICAgICAvLyB0aWNrKCkgbWF5IGhhdmUgaGl0IGEgc2Nyb2xsIGJvdW5kYXJ5IGFuZCBjYWxsZWQgc3RvcCgpIChkaXIgcmVzZXQgdG9cbiAgICAgIC8vIDApLiBPbmx5IHN0YXJ0IHRoZSBpbnRlcnZhbCBpZiB3ZSdyZSBzdGlsbCBnb2luZyDigJQgb3RoZXJ3aXNlIHRoZVxuICAgICAgLy8gaW50ZXJ2YWwgd291bGQgcnVuIGZvcmV2ZXIgd2l0aCBkaXIgPT09IDAgZG9pbmcgbm90aGluZyB1c2VmdWwuXG4gICAgICBpZiAoZGlyUmVmLmN1cnJlbnQgPT09IGRpcikge1xuICAgICAgICB0aW1lclJlZi5jdXJyZW50ID0gc2V0SW50ZXJ2YWwodGljaywgQVVUT1NDUk9MTF9JTlRFUlZBTF9NUylcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBSZS1ldmFsdWF0ZWQgb24gZXZlcnkgc2VsZWN0aW9uIGNoYW5nZSAoc3RhcnQvZHJhZy9maW5pc2gvY2xlYXIpLlxuICAgIC8vIERyaXZlcyBkcmFnLXRvLXNjcm9sbCBhdXRvc2Nyb2xsIHdoZW4gdGhlIGRyYWcgbGVhdmVzIHRoZSB2aWV3cG9ydC5cbiAgICAvLyBQcmlvciB2ZXJzaW9ucyBicm9rZSBzdGlja3kgaGVyZSBvbiBkcmFnLXN0YXJ0IHRvIHByZXZlbnQgc2VsZWN0aW9uXG4gICAgLy8gZHJpZnQgZHVyaW5nIHN0cmVhbWluZyDigJQgaW5rLnRzeCBub3cgdHJhbnNsYXRlcyBzZWxlY3Rpb24gY29vcmRzIGJ5XG4gICAgLy8gdGhlIGZvbGxvdyBkZWx0YSBpbnN0ZWFkIChuYXRpdmUgdGVybWluYWwgYmVoYXZpb3I6IHZpZXcga2VlcHNcbiAgICAvLyBzY3JvbGxpbmcsIGhpZ2hsaWdodCB3YWxrcyB1cCB3aXRoIHRoZSB0ZXh0KS4gS2VlcGluZyBzdGlja3kgYWxzb1xuICAgIC8vIGF2b2lkcyB1c2VWaXJ0dWFsU2Nyb2xsJ3MgdGFpbC13YWxrIOKGkiBmb3J3YXJkLXdhbGsgcGhhbnRvbSBncm93dGguXG4gICAgZnVuY3Rpb24gY2hlY2soKTogdm9pZCB7XG4gICAgICBjb25zdCBzID0gc2Nyb2xsUmVmLmN1cnJlbnRcbiAgICAgIGlmICghcykge1xuICAgICAgICBzdG9wKClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBjb25zdCB0b3AgPSBzLmdldFZpZXdwb3J0VG9wKClcbiAgICAgIGNvbnN0IGJvdHRvbSA9IHRvcCArIHMuZ2V0Vmlld3BvcnRIZWlnaHQoKSAtIDFcbiAgICAgIGNvbnN0IHNlbCA9IHNlbGVjdGlvbi5nZXRTdGF0ZSgpXG4gICAgICAvLyBQYXNzIHRoZSBMQVNULXNjcm9sbGVkIGRpcmVjdGlvbiAobm90IGRpclJlZikgc28gdGhlIGFuY2hvciBndWFyZCBpc1xuICAgICAgLy8gYnlwYXNzZWQgYWZ0ZXIgc2hpZnRBbmNob3IgaGFzIGNsYW1wZWQgYW5jaG9yIHRvd2FyZCByb3cgMC4gVXNpbmdcbiAgICAgIC8vIGxhc3RTY3JvbGxlZERpclJlZiAoc3Vydml2ZXMgc3RvcCgpKSBsZXRzIGF1dG9zY3JvbGwgcmVzdW1lIGFmdGVyIGFcbiAgICAgIC8vIGJyaWVmIG1vdXNlIGRpcCBpbnRvIHRoZSB2aWV3cG9ydC4gU2FtZS1kaXJlY3Rpb24gb25seSDigJQgYSBtb3VzZVxuICAgICAgLy8ganVtcCBmcm9tIGJlbG93LWJvdHRvbSB0byBhYm92ZS10b3AgbXVzdCBzdG9wLCBzaW5jZSByZXZlcnNpbmcgd2hpbGVcbiAgICAgIC8vIHRoZSBzY3JvbGxlZE9mZkFib3ZlL0JlbG93IGFjY3VtdWxhdG9ycyBob2xkIHRoZSBwcmlvciBkaXJlY3Rpb24nc1xuICAgICAgLy8gcm93cyB3b3VsZCBkdXBsaWNhdGUgdGV4dCBpbiBnZXRTZWxlY3RlZFRleHQuIFJlc2V0IG9uIGRyYWctZmluaXNoXG4gICAgICAvLyBPUiB3aGVuIGJvdGggYWNjdW11bGF0b3JzIGFyZSBlbXB0eTogc3RhcnRTZWxlY3Rpb24gY2xlYXJzIHRoZW1cbiAgICAgIC8vIChzZWxlY3Rpb24udHMpLCBzbyBhIG5ldyBkcmFnIGFmdGVyIGEgbG9zdC1yZWxlYXNlIChpc0RyYWdnaW5nXG4gICAgICAvLyBzdHVjayB0cnVlLCB0aGUgcmVhc29uIEFVVE9TQ1JPTExfTUFYX1RJQ0tTIGV4aXN0cykgc3RpbGwgcmVzZXRzLlxuICAgICAgLy8gU2FmZTogc3RhcnQoKSBiZWxvdyByZS1yZWNvcmRzIGxhc3RTY3JvbGxlZERpclJlZiBiZWZvcmUgaXRzXG4gICAgICAvLyBlYXJseS1yZXR1cm4sIHNvIGEgbWlkLXNjcm9sbCByZXNldCBoZXJlIGlzIGluc3RhbnRseSB1bmRvbmUuXG4gICAgICBpZiAoXG4gICAgICAgICFzZWw/LmlzRHJhZ2dpbmcgfHxcbiAgICAgICAgKHNlbC5zY3JvbGxlZE9mZkFib3ZlLmxlbmd0aCA9PT0gMCAmJiBzZWwuc2Nyb2xsZWRPZmZCZWxvdy5sZW5ndGggPT09IDApXG4gICAgICApIHtcbiAgICAgICAgbGFzdFNjcm9sbGVkRGlyUmVmLmN1cnJlbnQgPSAwXG4gICAgICB9XG4gICAgICBjb25zdCBkaXIgPSBkcmFnU2Nyb2xsRGlyZWN0aW9uKFxuICAgICAgICBzZWwsXG4gICAgICAgIHRvcCxcbiAgICAgICAgYm90dG9tLFxuICAgICAgICBsYXN0U2Nyb2xsZWREaXJSZWYuY3VycmVudCxcbiAgICAgIClcbiAgICAgIGlmIChkaXIgPT09IDApIHtcbiAgICAgICAgLy8gQmxvY2tlZCByZXZlcnNhbDogZm9jdXMganVtcGVkIHRvIHRoZSBvcHBvc2l0ZSBlZGdlIChvZmYtd2luZG93XG4gICAgICAgIC8vIGRyYWcgcmV0dXJuLCBmYXN0IGZsaWNrKS4gaGFuZGxlU2VsZWN0aW9uRHJhZyBhbHJlYWR5IG1vdmVkIGZvY3VzXG4gICAgICAgIC8vIHBhc3QgdGhlIGFuY2hvciwgZmxpcHBpbmcgc2VsZWN0aW9uQm91bmRzIOKAlCB0aGUgYWNjdW11bGF0b3IgaXNcbiAgICAgICAgLy8gbm93IG9ycGhhbmVkIChob2xkcyByb3dzIG9uIHRoZSB3cm9uZyBzaWRlKS4gQ2xlYXIgaXQgc29cbiAgICAgICAgLy8gZ2V0U2VsZWN0ZWRUZXh0IG1hdGNoZXMgdGhlIHZpc2libGUgaGlnaGxpZ2h0LlxuICAgICAgICBpZiAobGFzdFNjcm9sbGVkRGlyUmVmLmN1cnJlbnQgIT09IDAgJiYgc2VsPy5mb2N1cykge1xuICAgICAgICAgIGNvbnN0IHdhbnQgPSBzZWwuZm9jdXMucm93IDwgdG9wID8gLTEgOiBzZWwuZm9jdXMucm93ID4gYm90dG9tID8gMSA6IDBcbiAgICAgICAgICBpZiAod2FudCAhPT0gMCAmJiB3YW50ICE9PSBsYXN0U2Nyb2xsZWREaXJSZWYuY3VycmVudCkge1xuICAgICAgICAgICAgc2VsLnNjcm9sbGVkT2ZmQWJvdmUgPSBbXVxuICAgICAgICAgICAgc2VsLnNjcm9sbGVkT2ZmQmVsb3cgPSBbXVxuICAgICAgICAgICAgc2VsLnNjcm9sbGVkT2ZmQWJvdmVTVyA9IFtdXG4gICAgICAgICAgICBzZWwuc2Nyb2xsZWRPZmZCZWxvd1NXID0gW11cbiAgICAgICAgICAgIGxhc3RTY3JvbGxlZERpclJlZi5jdXJyZW50ID0gMFxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBzdG9wKClcbiAgICAgIH0gZWxzZSBzdGFydChkaXIpXG4gICAgfVxuXG4gICAgY29uc3QgdW5zdWJzY3JpYmUgPSBzZWxlY3Rpb24uc3Vic2NyaWJlKGNoZWNrKVxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICB1bnN1YnNjcmliZSgpXG4gICAgICBzdG9wKClcbiAgICAgIGxhc3RTY3JvbGxlZERpclJlZi5jdXJyZW50ID0gMFxuICAgIH1cbiAgfSwgW2lzQWN0aXZlLCBzY3JvbGxSZWYsIHNlbGVjdGlvbl0pXG59XG5cbi8qKlxuICogQ29tcHV0ZSBhdXRvc2Nyb2xsIGRpcmVjdGlvbiBmb3IgYSBkcmFnIHNlbGVjdGlvbiByZWxhdGl2ZSB0byB0aGUgU2Nyb2xsQm94XG4gKiB2aWV3cG9ydC4gUmV0dXJucyAwIHdoZW4gbm90IGRyYWdnaW5nLCBhbmNob3IvZm9jdXMgbWlzc2luZywgb3IgdGhlIGFuY2hvclxuICogaXMgb3V0c2lkZSB0aGUgdmlld3BvcnQg4oCUIGEgbXVsdGktY2xpY2sgb3IgZHJhZyB0aGF0IHN0YXJ0ZWQgaW4gdGhlIGlucHV0XG4gKiBhcmVhIG11c3Qgbm90IGNvbW1hbmRlZXIgdGhlIG1lc3NhZ2Ugc2Nyb2xsIChkb3VibGUtY2xpY2sgaW4gdGhlIGlucHV0IGFyZWFcbiAqIHdoaWxlIHNjcm9sbGVkIHVwIHByZXZpb3VzbHkgY29ycnVwdGVkIHRoZSBhbmNob3IgdmlhIHNoaWZ0QW5jaG9yIGFuZFxuICogc3B1cmlvdXNseSBzY3JvbGxlZCB0aGUgbWVzc2FnZSBoaXN0b3J5IGV2ZXJ5IDUwbXMgdW50aWwgcmVsZWFzZSkuXG4gKlxuICogYWxyZWFkeVNjcm9sbGluZ0RpciBieXBhc3NlcyB0aGUgYW5jaG9yLWluLXZpZXdwb3J0IGd1YXJkIG9uY2UgYXV0b3Njcm9sbFxuICogaXMgYWN0aXZlIChzaGlmdEFuY2hvciBsZWdpdGltYXRlbHkgY2xhbXBzIHRoZSBhbmNob3IgdG93YXJkIHJvdyAwLCBiZWxvd1xuICogYHRvcGApIGJ1dCBvbmx5IGFsbG93cyBTQU1FLWRpcmVjdGlvbiBjb250aW51YXRpb24uIElmIHRoZSBmb2N1cyBqdW1wcyB0b1xuICogdGhlIG9wcG9zaXRlIGVkZ2UgKGJlbG934oaSYWJvdmUgb3IgYWJvdmXihpJiZWxvdyDigJQgcG9zc2libGUgd2l0aCBhIGZhc3QgZmxpY2tcbiAqIG9yIG9mZi13aW5kb3cgZHJhZyBzaW5jZSBtb2RlIDEwMDIgcmVwb3J0cyBvbiBjZWxsIGNoYW5nZSwgbm90IHBlciBjZWxsKSxcbiAqIHJldHVybnMgMCB0byBzdG9wIOKAlCByZXZlcnNpbmcgd2l0aG91dCBjbGVhcmluZyBzY3JvbGxlZE9mZkFib3ZlL0JlbG93XG4gKiB3b3VsZCBkdXBsaWNhdGUgY2FwdHVyZWQgcm93cyB3aGVuIHRoZXkgc2Nyb2xsIGJhY2sgb24tc2NyZWVuLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZHJhZ1Njcm9sbERpcmVjdGlvbihcbiAgc2VsOiBTZWxlY3Rpb25TdGF0ZSB8IG51bGwsXG4gIHRvcDogbnVtYmVyLFxuICBib3R0b206IG51bWJlcixcbiAgYWxyZWFkeVNjcm9sbGluZ0RpcjogLTEgfCAwIHwgMSA9IDAsXG4pOiAtMSB8IDAgfCAxIHtcbiAgaWYgKCFzZWw/LmlzRHJhZ2dpbmcgfHwgIXNlbC5hbmNob3IgfHwgIXNlbC5mb2N1cykgcmV0dXJuIDBcbiAgY29uc3Qgcm93ID0gc2VsLmZvY3VzLnJvd1xuICBjb25zdCB3YW50OiAtMSB8IDAgfCAxID0gcm93IDwgdG9wID8gLTEgOiByb3cgPiBib3R0b20gPyAxIDogMFxuICBpZiAoYWxyZWFkeVNjcm9sbGluZ0RpciAhPT0gMCkge1xuICAgIC8vIFNhbWUtZGlyZWN0aW9uIG9ubHkuIEZvY3VzIG9uIHRoZSBvcHBvc2l0ZSBzaWRlLCBvciBiYWNrIGluc2lkZSB0aGVcbiAgICAvLyB2aWV3cG9ydCwgc3RvcHMgdGhlIHNjcm9sbCDigJQgY2FwdHVyZWQgcm93cyBzdGF5IGluIHNjcm9sbGVkT2ZmQWJvdmUvXG4gICAgLy8gQmVsb3cgYnV0IG5ldmVyIHNjcm9sbCBiYWNrIG9uLXNjcmVlbiwgc28gZ2V0U2VsZWN0ZWRUZXh0IGlzIGNvcnJlY3QuXG4gICAgcmV0dXJuIHdhbnQgPT09IGFscmVhZHlTY3JvbGxpbmdEaXIgPyB3YW50IDogMFxuICB9XG4gIC8vIEFuY2hvciBtdXN0IGJlIGluc2lkZSB0aGUgdmlld3BvcnQgZm9yIHVzIHRvIG93biB0aGlzIGRyYWcuIElmIHRoZVxuICAvLyB1c2VyIHN0YXJ0ZWQgc2VsZWN0aW5nIGluIHRoZSBpbnB1dCBib3ggb3IgaGVhZGVyLCBhdXRvc2Nyb2xsaW5nIHRoZVxuICAvLyBtZXNzYWdlIGhpc3RvcnkgaXMgc3VycHJpc2luZyBhbmQgY29ycnVwdHMgdGhlIGFuY2hvciB2aWEgc2hpZnRBbmNob3IuXG4gIGlmIChzZWwuYW5jaG9yLnJvdyA8IHRvcCB8fCBzZWwuYW5jaG9yLnJvdyA+IGJvdHRvbSkgcmV0dXJuIDBcbiAgcmV0dXJuIHdhbnRcbn1cblxuLy8gS2V5Ym9hcmQgcGFnZSBqdW1wczogc2Nyb2xsVG8oKSB3cml0ZXMgc2Nyb2xsVG9wIGRpcmVjdGx5IGFuZCBjbGVhcnNcbi8vIHBlbmRpbmdTY3JvbGxEZWx0YSDigJQgb25lIGZyYW1lLCBubyBkcmFpbi4gc2Nyb2xsQnkoKSBhY2N1bXVsYXRlcyBpbnRvXG4vLyBwZW5kaW5nU2Nyb2xsRGVsdGEgd2hpY2ggdGhlIHJlbmRlcmVyIGRyYWlucyBvdmVyIHNldmVyYWwgZnJhbWVzXG4vLyAocmVuZGVyLW5vZGUtdG8tb3V0cHV0LnRzIGRyYWluUHJvcG9ydGlvbmFsL2RyYWluQWRhcHRpdmUpIOKAlCBjb3JyZWN0IGZvclxuLy8gd2hlZWwgc21vb3RobmVzcywgd3JvbmcgZm9yIFBnVXAvY3RybCt1IHdoZXJlIHRoZSB1c2VyIGV4cGVjdHMgYSBzbmFwLlxuLy8gVGFyZ2V0IGlzIHJlbGF0aXZlIHRvIHNjcm9sbFRvcCtwZW5kaW5nRGVsdGEgc28gYSBqdW1wIG1pZC13aGVlbC1idXJzdFxuLy8gbGFuZHMgd2hlcmUgdGhlIHdoZWVsIHdhcyBoZWFkaW5nLlxuZXhwb3J0IGZ1bmN0aW9uIGp1bXBCeShzOiBTY3JvbGxCb3hIYW5kbGUsIGRlbHRhOiBudW1iZXIpOiBib29sZWFuIHtcbiAgY29uc3QgbWF4ID0gTWF0aC5tYXgoMCwgcy5nZXRTY3JvbGxIZWlnaHQoKSAtIHMuZ2V0Vmlld3BvcnRIZWlnaHQoKSlcbiAgY29uc3QgdGFyZ2V0ID0gcy5nZXRTY3JvbGxUb3AoKSArIHMuZ2V0UGVuZGluZ0RlbHRhKCkgKyBkZWx0YVxuICBpZiAodGFyZ2V0ID49IG1heCkge1xuICAgIC8vIEVhZ2VyLXdyaXRlIHNjcm9sbFRvcCBzbyBmb2xsb3ctc2Nyb2xsIHNlZXMgZm9sbG93RGVsdGE9MC4gQ2FsbGVyc1xuICAgIC8vIHRoYXQgcmFuIHRyYW5zbGF0ZVNlbGVjdGlvbkZvckp1bXAgYWxyZWFkeSBzaGlmdGVkOyBzY3JvbGxUb0JvdHRvbSgpXG4gICAgLy8gYWxvbmUgd291bGQgZG91YmxlLXNoaWZ0IHZpYSB0aGUgcmVuZGVyLXBoYXNlIHN0aWNreSBmb2xsb3cuXG4gICAgcy5zY3JvbGxUbyhtYXgpXG4gICAgcy5zY3JvbGxUb0JvdHRvbSgpXG4gICAgcmV0dXJuIHRydWVcbiAgfVxuICBzLnNjcm9sbFRvKE1hdGgubWF4KDAsIHRhcmdldCkpXG4gIHJldHVybiBmYWxzZVxufVxuXG4vLyBXaGVlbC1kb3duIHBhc3QgbWF4U2Nyb2xsIHJlLWVuYWJsZXMgc3RpY2t5IHNvIHdoZWVsaW5nIGF0IHRoZSBib3R0b21cbi8vIG5hdHVyYWxseSByZS1waW5zIChtYXRjaGVzIHR5cGljYWwgY2hhdC1hcHAgYmVoYXZpb3IpLiBSZXR1cm5zIHRoZVxuLy8gcmVzdWx0aW5nIHN0aWNreSBzdGF0ZSBzbyBjYWxsZXJzIGNhbiBwcm9wYWdhdGUgaXQuXG5mdW5jdGlvbiBzY3JvbGxEb3duKHM6IFNjcm9sbEJveEhhbmRsZSwgYW1vdW50OiBudW1iZXIpOiBib29sZWFuIHtcbiAgY29uc3QgbWF4ID0gTWF0aC5tYXgoMCwgcy5nZXRTY3JvbGxIZWlnaHQoKSAtIHMuZ2V0Vmlld3BvcnRIZWlnaHQoKSlcbiAgLy8gSW5jbHVkZSBwZW5kaW5nRGVsdGE6IHNjcm9sbEJ5IGFjY3VtdWxhdGVzIGludG8gcGVuZGluZ1Njcm9sbERlbHRhXG4gIC8vIHdpdGhvdXQgdXBkYXRpbmcgc2Nyb2xsVG9wLCBzbyBnZXRTY3JvbGxUb3AoKSBhbG9uZSBpcyBzdGFsZSB3aXRoaW5cbiAgLy8gYSBiYXRjaCBvZiB3aGVlbCBldmVudHMuIFdpdGhvdXQgdGhpcywgd2hlZWxpbmcgdG8gdGhlIGJvdHRvbSBuZXZlclxuICAvLyByZS1lbmFibGVzIHN0aWNreSBzY3JvbGwuXG4gIGNvbnN0IGVmZmVjdGl2ZVRvcCA9IHMuZ2V0U2Nyb2xsVG9wKCkgKyBzLmdldFBlbmRpbmdEZWx0YSgpXG4gIGlmIChlZmZlY3RpdmVUb3AgKyBhbW91bnQgPj0gbWF4KSB7XG4gICAgcy5zY3JvbGxUb0JvdHRvbSgpXG4gICAgcmV0dXJuIHRydWVcbiAgfVxuICBzLnNjcm9sbEJ5KGFtb3VudClcbiAgcmV0dXJuIGZhbHNlXG59XG5cbi8vIFdoZWVsLXVwIHBhc3Qgc2Nyb2xsVG9wPTAgY2xhbXBzIHZpYSBzY3JvbGxUbygwKSwgY2xlYXJpbmdcbi8vIHBlbmRpbmdTY3JvbGxEZWx0YSBzbyBhZ2dyZXNzaXZlIHdoZWVsIGJ1cnN0cyAoZS5nLiBNWCBNYXN0ZXIgZnJlZS1zcGluKVxuLy8gZG9uJ3QgYWNjdW11bGF0ZSBhbiB1bmJvdW5kZWQgbmVnYXRpdmUgZGVsdGEuIFdpdGhvdXQgdGhpcyBjbGFtcCxcbi8vIHVzZVZpcnR1YWxTY3JvbGwncyBbZWZmTG8sIGVmZkhpXSBzcGFuIGdyb3dzIHBhc3Qgd2hhdCBNQVhfTU9VTlRFRF9JVEVNU1xuLy8gY2FuIGNvdmVyIGFuZCBpbnRlcm1lZGlhdGUgZHJhaW4gZnJhbWVzIHJlbmRlciBhdCBzY3JvbGxUb3BzIHdpdGggbm9cbi8vIG1vdW50ZWQgY2hpbGRyZW4g4oCUIGJsYW5rIHZpZXdwb3J0LlxuZXhwb3J0IGZ1bmN0aW9uIHNjcm9sbFVwKHM6IFNjcm9sbEJveEhhbmRsZSwgYW1vdW50OiBudW1iZXIpOiB2b2lkIHtcbiAgLy8gSW5jbHVkZSBwZW5kaW5nRGVsdGE6IHNjcm9sbEJ5IGFjY3VtdWxhdGVzIHdpdGhvdXQgdXBkYXRpbmcgc2Nyb2xsVG9wLFxuICAvLyBzbyBnZXRTY3JvbGxUb3AoKSBhbG9uZSBpcyBzdGFsZSB3aXRoaW4gYSBiYXRjaCBvZiB3aGVlbCBldmVudHMuXG4gIGNvbnN0IGVmZmVjdGl2ZVRvcCA9IHMuZ2V0U2Nyb2xsVG9wKCkgKyBzLmdldFBlbmRpbmdEZWx0YSgpXG4gIGlmIChlZmZlY3RpdmVUb3AgLSBhbW91bnQgPD0gMCkge1xuICAgIHMuc2Nyb2xsVG8oMClcbiAgICByZXR1cm5cbiAgfVxuICBzLnNjcm9sbEJ5KC1hbW91bnQpXG59XG5cbmV4cG9ydCB0eXBlIE1vZGFsUGFnZXJBY3Rpb24gPVxuICB8ICdsaW5lVXAnXG4gIHwgJ2xpbmVEb3duJ1xuICB8ICdoYWxmUGFnZVVwJ1xuICB8ICdoYWxmUGFnZURvd24nXG4gIHwgJ2Z1bGxQYWdlVXAnXG4gIHwgJ2Z1bGxQYWdlRG93bidcbiAgfCAndG9wJ1xuICB8ICdib3R0b20nXG5cbi8qKlxuICogTWFwcyBhIGtleXN0cm9rZSB0byBhIG1vZGFsIHBhZ2VyIGFjdGlvbi4gRXhwb3J0ZWQgZm9yIHRlc3RpbmcuXG4gKiBSZXR1cm5zIG51bGwgZm9yIGtleXMgdGhlIG1vZGFsIHBhZ2VyIGRvZXNuJ3QgaGFuZGxlICh0aGV5IGZhbGwgdGhyb3VnaCkuXG4gKlxuICogY3RybCt1L2QvYi9mIGFyZSB0aGUgbGVzcy1saW5lYWdlIGJpbmRpbmdzLiBnL0cgYXJlIGJhcmUgbGV0dGVycyAob25seVxuICogc2FmZSB3aGVuIG5vIHByb21wdCBpcyBtb3VudGVkKS4gRyBhcnJpdmVzIGFzIGlucHV0PSdHJyBzaGlmdD1mYWxzZSBvblxuICogbGVnYWN5IHRlcm1pbmFscywgb3IgaW5wdXQ9J2cnIHNoaWZ0PXRydWUgb24ga2l0dHktcHJvdG9jb2wgdGVybWluYWxzLlxuICogTG93ZXJjYXNlIGcgbmVlZHMgdGhlICFzaGlmdCBndWFyZCBzbyBpdCBkb2Vzbid0IGFsc28gbWF0Y2gga2l0dHktRy5cbiAqXG4gKiBLZXktcmVwZWF0OiBzdGRpbiBjb2FsZXNjZXMgaGVsZC1kb3duIHByaW50YWJsZXMgaW50byBvbmUgbXVsdGktY2hhclxuICogc3RyaW5nIChlLmcuICdnZ2cnKS4gT25seSB1bmlmb3JtLWNoYXIgYmF0Y2hlcyBhcmUgaGFuZGxlZCDigJQgbWl4ZWQgaW5wdXRcbiAqIGxpa2UgJ2dHJyBpc24ndCBrZXktcmVwZWF0LiBnL0cgYXJlIGlkZW1wb3RlbnQgYWJzb2x1dGUganVtcHMsIHNvIHRoZVxuICogY291bnQgaXMgaXJyZWxldmFudCAoY29uc3VtaW5nIHRoZSBiYXRjaCBqdXN0IHByZXZlbnRzIGl0IGZyb20gbGVha2luZ1xuICogdG8gdGhlIHNlbGVjdGlvbi1jbGVhci1vbi1wcmludGFibGUgaGFuZGxlcikuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBtb2RhbFBhZ2VyQWN0aW9uKFxuICBpbnB1dDogc3RyaW5nLFxuICBrZXk6IFBpY2s8XG4gICAgS2V5LFxuICAgICdjdHJsJyB8ICdtZXRhJyB8ICdzaGlmdCcgfCAndXBBcnJvdycgfCAnZG93bkFycm93JyB8ICdob21lJyB8ICdlbmQnXG4gID4sXG4pOiBNb2RhbFBhZ2VyQWN0aW9uIHwgbnVsbCB7XG4gIGlmIChrZXkubWV0YSkgcmV0dXJuIG51bGxcbiAgLy8gU3BlY2lhbCBrZXlzIGZpcnN0IOKAlCBhcnJvd3MvaG9tZS9lbmQgYXJyaXZlIHdpdGggZW1wdHkgb3IganVuayBpbnB1dCxcbiAgLy8gc28gdGhlc2UgbXVzdCBiZSBjaGVja2VkIGJlZm9yZSBhbnkgaW5wdXQtc3RyaW5nIGxvZ2ljLiBzaGlmdCBpc1xuICAvLyByZXNlcnZlZCBmb3Igc2VsZWN0aW9uLWV4dGVuZCAoc2VsZWN0aW9uRm9jdXNNb3ZlRm9yS2V5KTsgY3RybCtob21lL2VuZFxuICAvLyBhbHJlYWR5IGhhcyBhIHVzZUtleWJpbmRpbmdzIHJvdXRlIHRvIHNjcm9sbDp0b3AvYm90dG9tLlxuICBpZiAoIWtleS5jdHJsICYmICFrZXkuc2hpZnQpIHtcbiAgICBpZiAoa2V5LnVwQXJyb3cpIHJldHVybiAnbGluZVVwJ1xuICAgIGlmIChrZXkuZG93bkFycm93KSByZXR1cm4gJ2xpbmVEb3duJ1xuICAgIGlmIChrZXkuaG9tZSkgcmV0dXJuICd0b3AnXG4gICAgaWYgKGtleS5lbmQpIHJldHVybiAnYm90dG9tJ1xuICB9XG4gIGlmIChrZXkuY3RybCkge1xuICAgIGlmIChrZXkuc2hpZnQpIHJldHVybiBudWxsXG4gICAgc3dpdGNoIChpbnB1dCkge1xuICAgICAgY2FzZSAndSc6XG4gICAgICAgIHJldHVybiAnaGFsZlBhZ2VVcCdcbiAgICAgIGNhc2UgJ2QnOlxuICAgICAgICByZXR1cm4gJ2hhbGZQYWdlRG93bidcbiAgICAgIGNhc2UgJ2InOlxuICAgICAgICByZXR1cm4gJ2Z1bGxQYWdlVXAnXG4gICAgICBjYXNlICdmJzpcbiAgICAgICAgcmV0dXJuICdmdWxsUGFnZURvd24nXG4gICAgICAvLyBlbWFjcy1zdHlsZSBsaW5lIHNjcm9sbCAobGVzcyBhY2NlcHRzIGJvdGggY3RybCtuL3AgYW5kIGN0cmwrZS95KS5cbiAgICAgIC8vIFdvcmtzIGR1cmluZyBzZWFyY2ggbmF2IOKAlCBmaW5lLWFkanVzdCBhZnRlciBhIGp1bXAgd2l0aG91dFxuICAgICAgLy8gbGVhdmluZyBtb2RhbC4gTm8gIXNlYXJjaE9wZW4gZ2F0ZSBvbiB0aGlzIHVzZUlucHV0J3MgaXNBY3RpdmUuXG4gICAgICBjYXNlICduJzpcbiAgICAgICAgcmV0dXJuICdsaW5lRG93bidcbiAgICAgIGNhc2UgJ3AnOlxuICAgICAgICByZXR1cm4gJ2xpbmVVcCdcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBudWxsXG4gICAgfVxuICB9XG4gIC8vIEJhcmUgbGV0dGVycy4gS2V5LXJlcGVhdCBiYXRjaGVzOiBvbmx5IGFjdCBvbiB1bmlmb3JtIHJ1bnMuXG4gIGNvbnN0IGMgPSBpbnB1dFswXVxuICBpZiAoIWMgfHwgaW5wdXQgIT09IGMucmVwZWF0KGlucHV0Lmxlbmd0aCkpIHJldHVybiBudWxsXG4gIC8vIGtpdHR5IHNlbmRzIEcgYXMgaW5wdXQ9J2cnIHNoaWZ0PXRydWU7IGxlZ2FjeSBhcyAnRycgc2hpZnQ9ZmFsc2UuXG4gIC8vIENoZWNrIEJFRk9SRSB0aGUgc2hpZnQtZ2F0ZSBzbyBib3RoIGhpdCAnYm90dG9tJy5cbiAgaWYgKGMgPT09ICdHJyB8fCAoYyA9PT0gJ2cnICYmIGtleS5zaGlmdCkpIHJldHVybiAnYm90dG9tJ1xuICBpZiAoa2V5LnNoaWZ0KSByZXR1cm4gbnVsbFxuICBzd2l0Y2ggKGMpIHtcbiAgICBjYXNlICdnJzpcbiAgICAgIHJldHVybiAndG9wJ1xuICAgIC8vIGovayByZS1hZGRlZCBwZXIgVG9tIE1hciAxOCDigJQgcmV2ZXJzYWwgb2YgTWFyIDE2IHJlbW92YWwuIFdvcmtzXG4gICAgLy8gZHVyaW5nIHNlYXJjaCBuYXYgKGZpbmUtYWRqdXN0IGFmdGVyIG4vTiBsYW5kcykgc2luY2UgaXNNb2RhbCBpc1xuICAgIC8vIGluZGVwZW5kZW50IG9mIHNlYXJjaE9wZW4uXG4gICAgY2FzZSAnaic6XG4gICAgICByZXR1cm4gJ2xpbmVEb3duJ1xuICAgIGNhc2UgJ2snOlxuICAgICAgcmV0dXJuICdsaW5lVXAnXG4gICAgLy8gbGVzczogc3BhY2UgPSBwYWdlIGRvd24sIGIgPSBwYWdlIHVwLiBjdHJsK2IgYWxyZWFkeSBtYXBzIGFib3ZlO1xuICAgIC8vIGJhcmUgYiBpcyB0aGUgbGVzcy1uYXRpdmUgdmVyc2lvbi5cbiAgICBjYXNlICcgJzpcbiAgICAgIHJldHVybiAnZnVsbFBhZ2VEb3duJ1xuICAgIGNhc2UgJ2InOlxuICAgICAgcmV0dXJuICdmdWxsUGFnZVVwJ1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gbnVsbFxuICB9XG59XG5cbi8qKlxuICogQXBwbGllcyBhIG1vZGFsIHBhZ2VyIGFjdGlvbiB0byBhIFNjcm9sbEJveC4gUmV0dXJucyB0aGUgcmVzdWx0aW5nIHN0aWNreVxuICogc3RhdGUsIG9yIG51bGwgaWYgdGhlIGFjdGlvbiB3YXMgbnVsbCAobm90aGluZyB0byBkbyDigJQgY2FsbGVyIHNob3VsZCBmYWxsXG4gKiB0aHJvdWdoKS4gQ2FsbHMgb25CZWZvcmVKdW1wKGRlbHRhKSBiZWZvcmUgc2Nyb2xsaW5nIHNvIHRoZSBjYWxsZXIgY2FuXG4gKiB0cmFuc2xhdGUgdGhlIHRleHQgc2VsZWN0aW9uIGJ5IHRoZSBzY3JvbGwgZGVsdGEgKGNhcHR1cmUgb3V0Z29pbmcgcm93cyxcbiAqIHNoaWZ0IGFuY2hvcitmb2N1cykgaW5zdGVhZCBvZiBjbGVhcmluZyBpdC4gRXhwb3J0ZWQgZm9yIHRlc3RpbmcuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhcHBseU1vZGFsUGFnZXJBY3Rpb24oXG4gIHM6IFNjcm9sbEJveEhhbmRsZSxcbiAgYWN0OiBNb2RhbFBhZ2VyQWN0aW9uIHwgbnVsbCxcbiAgb25CZWZvcmVKdW1wOiAoZGVsdGE6IG51bWJlcikgPT4gdm9pZCxcbik6IGJvb2xlYW4gfCBudWxsIHtcbiAgc3dpdGNoIChhY3QpIHtcbiAgICBjYXNlIG51bGw6XG4gICAgICByZXR1cm4gbnVsbFxuICAgIGNhc2UgJ2xpbmVVcCc6XG4gICAgY2FzZSAnbGluZURvd24nOiB7XG4gICAgICBjb25zdCBkID0gYWN0ID09PSAnbGluZURvd24nID8gMSA6IC0xXG4gICAgICBvbkJlZm9yZUp1bXAoZClcbiAgICAgIHJldHVybiBqdW1wQnkocywgZClcbiAgICB9XG4gICAgY2FzZSAnaGFsZlBhZ2VVcCc6XG4gICAgY2FzZSAnaGFsZlBhZ2VEb3duJzoge1xuICAgICAgY29uc3QgaGFsZiA9IE1hdGgubWF4KDEsIE1hdGguZmxvb3Iocy5nZXRWaWV3cG9ydEhlaWdodCgpIC8gMikpXG4gICAgICBjb25zdCBkID0gYWN0ID09PSAnaGFsZlBhZ2VEb3duJyA/IGhhbGYgOiAtaGFsZlxuICAgICAgb25CZWZvcmVKdW1wKGQpXG4gICAgICByZXR1cm4ganVtcEJ5KHMsIGQpXG4gICAgfVxuICAgIGNhc2UgJ2Z1bGxQYWdlVXAnOlxuICAgIGNhc2UgJ2Z1bGxQYWdlRG93bic6IHtcbiAgICAgIGNvbnN0IHBhZ2UgPSBNYXRoLm1heCgxLCBzLmdldFZpZXdwb3J0SGVpZ2h0KCkpXG4gICAgICBjb25zdCBkID0gYWN0ID09PSAnZnVsbFBhZ2VEb3duJyA/IHBhZ2UgOiAtcGFnZVxuICAgICAgb25CZWZvcmVKdW1wKGQpXG4gICAgICByZXR1cm4ganVtcEJ5KHMsIGQpXG4gICAgfVxuICAgIGNhc2UgJ3RvcCc6XG4gICAgICBvbkJlZm9yZUp1bXAoLShzLmdldFNjcm9sbFRvcCgpICsgcy5nZXRQZW5kaW5nRGVsdGEoKSkpXG4gICAgICBzLnNjcm9sbFRvKDApXG4gICAgICByZXR1cm4gZmFsc2VcbiAgICBjYXNlICdib3R0b20nOiB7XG4gICAgICBjb25zdCBtYXggPSBNYXRoLm1heCgwLCBzLmdldFNjcm9sbEhlaWdodCgpIC0gcy5nZXRWaWV3cG9ydEhlaWdodCgpKVxuICAgICAgb25CZWZvcmVKdW1wKG1heCAtIChzLmdldFNjcm9sbFRvcCgpICsgcy5nZXRQZW5kaW5nRGVsdGEoKSkpXG4gICAgICAvLyBFYWdlci13cml0ZSBzY3JvbGxUb3AgYmVmb3JlIHNjcm9sbFRvQm90dG9tIOKAlCBzYW1lIGRvdWJsZS1zaGlmdFxuICAgICAgLy8gZml4IGFzIHNjcm9sbDpib3R0b20gYW5kIGp1bXBCeSdzIG1heCBicmFuY2guXG4gICAgICBzLnNjcm9sbFRvKG1heClcbiAgICAgIHMuc2Nyb2xsVG9Cb3R0b20oKVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxJQUFJLEtBQUtDLFNBQVMs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 \ No newline at end of file diff --git a/components/SearchBox.tsx b/components/SearchBox.tsx new file mode 100644 index 0000000..96338a7 --- /dev/null +++ b/components/SearchBox.tsx @@ -0,0 +1,72 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, Text } from '../ink.js'; +type Props = { + query: string; + placeholder?: string; + isFocused: boolean; + isTerminalFocused: boolean; + prefix?: string; + width?: number | string; + cursorOffset?: number; + borderless?: boolean; +}; +export function SearchBox(t0) { + const $ = _c(17); + const { + query, + placeholder: t1, + isFocused, + isTerminalFocused, + prefix: t2, + width, + cursorOffset, + borderless: t3 + } = t0; + const placeholder = t1 === undefined ? "Search\u2026" : t1; + const prefix = t2 === undefined ? "\u2315" : t2; + const borderless = t3 === undefined ? false : t3; + const offset = cursorOffset ?? query.length; + const t4 = borderless ? undefined : "round"; + const t5 = isFocused ? "suggestion" : undefined; + const t6 = !isFocused; + const t7 = borderless ? 0 : 1; + const t8 = !isFocused; + let t9; + if ($[0] !== isFocused || $[1] !== isTerminalFocused || $[2] !== offset || $[3] !== placeholder || $[4] !== query) { + t9 = isFocused ? <>{query ? isTerminalFocused ? <>{query.slice(0, offset)}{offset < query.length ? query[offset] : " "}{offset < query.length && {query.slice(offset + 1)}} : {query} : isTerminalFocused ? <>{placeholder.charAt(0)}{placeholder.slice(1)} : {placeholder}} : query ? {query} : {placeholder}; + $[0] = isFocused; + $[1] = isTerminalFocused; + $[2] = offset; + $[3] = placeholder; + $[4] = query; + $[5] = t9; + } else { + t9 = $[5]; + } + let t10; + if ($[6] !== prefix || $[7] !== t8 || $[8] !== t9) { + t10 = {prefix}{" "}{t9}; + $[6] = prefix; + $[7] = t8; + $[8] = t9; + $[9] = t10; + } else { + t10 = $[9]; + } + let t11; + if ($[10] !== t10 || $[11] !== t4 || $[12] !== t5 || $[13] !== t6 || $[14] !== t7 || $[15] !== width) { + t11 = {t10}; + $[10] = t10; + $[11] = t4; + $[12] = t5; + $[13] = t6; + $[14] = t7; + $[15] = width; + $[16] = t11; + } else { + t11 = $[16]; + } + return t11; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJQcm9wcyIsInF1ZXJ5IiwicGxhY2Vob2xkZXIiLCJpc0ZvY3VzZWQiLCJpc1Rlcm1pbmFsRm9jdXNlZCIsInByZWZpeCIsIndpZHRoIiwiY3Vyc29yT2Zmc2V0IiwiYm9yZGVybGVzcyIsIlNlYXJjaEJveCIsInQwIiwiJCIsIl9jIiwidDEiLCJ0MiIsInQzIiwidW5kZWZpbmVkIiwib2Zmc2V0IiwibGVuZ3RoIiwidDQiLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5Iiwic2xpY2UiLCJjaGFyQXQiLCJ0MTAiLCJ0MTEiXSwic291cmNlcyI6WyJTZWFyY2hCb3gudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgcXVlcnk6IHN0cmluZ1xuICBwbGFjZWhvbGRlcj86IHN0cmluZ1xuICBpc0ZvY3VzZWQ6IGJvb2xlYW5cbiAgaXNUZXJtaW5hbEZvY3VzZWQ6IGJvb2xlYW5cbiAgcHJlZml4Pzogc3RyaW5nXG4gIHdpZHRoPzogbnVtYmVyIHwgc3RyaW5nXG4gIGN1cnNvck9mZnNldD86IG51bWJlclxuICBib3JkZXJsZXNzPzogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gU2VhcmNoQm94KHtcbiAgcXVlcnksXG4gIHBsYWNlaG9sZGVyID0gJ1NlYXJjaOKApicsXG4gIGlzRm9jdXNlZCxcbiAgaXNUZXJtaW5hbEZvY3VzZWQsXG4gIHByZWZpeCA9ICfijJUnLFxuICB3aWR0aCxcbiAgY3Vyc29yT2Zmc2V0LFxuICBib3JkZXJsZXNzID0gZmFsc2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IG9mZnNldCA9IGN1cnNvck9mZnNldCA/PyBxdWVyeS5sZW5ndGhcblxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhTaHJpbms9ezB9XG4gICAgICBib3JkZXJTdHlsZT17Ym9yZGVybGVzcyA/IHVuZGVmaW5lZCA6ICdyb3VuZCd9XG4gICAgICBib3JkZXJDb2xvcj17aXNGb2N1c2VkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfVxuICAgICAgYm9yZGVyRGltQ29sb3I9eyFpc0ZvY3VzZWR9XG4gICAgICBwYWRkaW5nWD17Ym9yZGVybGVzcyA/IDAgOiAxfVxuICAgICAgd2lkdGg9e3dpZHRofVxuICAgID5cbiAgICAgIDxUZXh0IGRpbUNvbG9yPXshaXNGb2N1c2VkfT5cbiAgICAgICAge3ByZWZpeH17JyAnfVxuICAgICAgICB7aXNGb2N1c2VkID8gKFxuICAgICAgICAgIDw+XG4gICAgICAgICAgICB7cXVlcnkgPyAoXG4gICAgICAgICAgICAgIGlzVGVybWluYWxGb2N1c2VkID8gKFxuICAgICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgICA8VGV4dD57cXVlcnkuc2xpY2UoMCwgb2Zmc2V0KX08L1RleHQ+XG4gICAgICAgICAgICAgICAgICA8VGV4dCBpbnZlcnNlPlxuICAgICAgICAgICAgICAgICAgICB7b2Zmc2V0IDwgcXVlcnkubGVuZ3RoID8gcXVlcnlbb2Zmc2V0XSA6ICcgJ31cbiAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgIHtvZmZzZXQgPCBxdWVyeS5sZW5ndGggJiYgKFxuICAgICAgICAgICAgICAgICAgICA8VGV4dD57cXVlcnkuc2xpY2Uob2Zmc2V0ICsgMSl9PC9UZXh0PlxuICAgICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgICA8VGV4dD57cXVlcnl9PC9UZXh0PlxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICApIDogaXNUZXJtaW5hbEZvY3VzZWQgPyAoXG4gICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgPFRleHQgaW52ZXJzZT57cGxhY2Vob2xkZXIuY2hhckF0KDApfTwvVGV4dD5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57cGxhY2Vob2xkZXIuc2xpY2UoMSl9PC9UZXh0PlxuICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntwbGFjZWhvbGRlcn08L1RleHQ+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvPlxuICAgICAgICApIDogcXVlcnkgPyAoXG4gICAgICAgICAgPFRleHQ+e3F1ZXJ5fTwvVGV4dD5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8VGV4dD57cGxhY2Vob2xkZXJ9PC9UZXh0PlxuICAgICAgICApfVxuICAgICAgPC9UZXh0PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBRXJDLEtBQUtDLEtBQUssR0FBRztFQUNYQyxLQUFLLEVBQUUsTUFBTTtFQUNiQyxXQUFXLENBQUMsRUFBRSxNQUFNO0VBQ3BCQyxTQUFTLEVBQUUsT0FBTztFQUNsQkMsaUJBQWlCLEVBQUUsT0FBTztFQUMxQkMsTUFBTSxDQUFDLEVBQUUsTUFBTTtFQUNmQyxLQUFLLENBQUMsRUFBRSxNQUFNLEdBQUcsTUFBTTtFQUN2QkMsWUFBWSxDQUFDLEVBQUUsTUFBTTtFQUNyQkMsVUFBVSxDQUFDLEVBQUUsT0FBTztBQUN0QixDQUFDO0FBRUQsT0FBTyxTQUFBQyxVQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQW1CO0lBQUFYLEtBQUE7SUFBQUMsV0FBQSxFQUFBVyxFQUFBO0lBQUFWLFNBQUE7SUFBQUMsaUJBQUE7SUFBQUMsTUFBQSxFQUFBUyxFQUFBO0lBQUFSLEtBQUE7SUFBQUMsWUFBQTtJQUFBQyxVQUFBLEVBQUFPO0VBQUEsSUFBQUwsRUFTbEI7RUFQTixNQUFBUixXQUFBLEdBQUFXLEVBQXVCLEtBQXZCRyxTQUF1QixHQUF2QixjQUF1QixHQUF2QkgsRUFBdUI7RUFHdkIsTUFBQVIsTUFBQSxHQUFBUyxFQUFZLEtBQVpFLFNBQVksR0FBWixRQUFZLEdBQVpGLEVBQVk7RUFHWixNQUFBTixVQUFBLEdBQUFPLEVBQWtCLEtBQWxCQyxTQUFrQixHQUFsQixLQUFrQixHQUFsQkQsRUFBa0I7RUFFbEIsTUFBQUUsTUFBQSxHQUFlVixZQUE0QixJQUFaTixLQUFLLENBQUFpQixNQUFPO0VBSzFCLE1BQUFDLEVBQUEsR0FBQVgsVUFBVSxHQUFWUSxTQUFnQyxHQUFoQyxPQUFnQztFQUNoQyxNQUFBSSxFQUFBLEdBQUFqQixTQUFTLEdBQVQsWUFBb0MsR0FBcENhLFNBQW9DO0VBQ2pDLE1BQUFLLEVBQUEsSUFBQ2xCLFNBQVM7RUFDaEIsTUFBQW1CLEVBQUEsR0FBQWQsVUFBVSxHQUFWLENBQWtCLEdBQWxCLENBQWtCO0VBR1osTUFBQWUsRUFBQSxJQUFDcEIsU0FBUztFQUFBLElBQUFxQixFQUFBO0VBQUEsSUFBQWIsQ0FBQSxRQUFBUixTQUFBLElBQUFRLENBQUEsUUFBQVAsaUJBQUEsSUFBQU8sQ0FBQSxRQUFBTSxNQUFBLElBQUFOLENBQUEsUUFBQVQsV0FBQSxJQUFBUyxDQUFBLFFBQUFWLEtBQUE7SUFFdkJ1QixFQUFBLEdBQUFyQixTQUFTLEdBQVQsRUFFSSxDQUFBRixLQUFLLEdBQ0pHLGlCQUFpQixHQUFqQixFQUVJLENBQUMsSUFBSSxDQUFFLENBQUFILEtBQUssQ0FBQXdCLEtBQU0sQ0FBQyxDQUFDLEVBQUVSLE1BQU0sRUFBRSxFQUE3QixJQUFJLENBQ0wsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFQLEtBQU0sQ0FBQyxDQUNWLENBQUFBLE1BQU0sR0FBR2hCLEtBQUssQ0FBQWlCLE1BQTZCLEdBQW5CakIsS0FBSyxDQUFDZ0IsTUFBTSxDQUFPLEdBQTNDLEdBQTBDLENBQzdDLEVBRkMsSUFBSSxDQUdKLENBQUFBLE1BQU0sR0FBR2hCLEtBQUssQ0FBQWlCLE1BRWQsSUFEQyxDQUFDLElBQUksQ0FBRSxDQUFBakIsS0FBSyxDQUFBd0IsS0FBTSxDQUFDUixNQUFNLEdBQUcsQ0FBQyxFQUFFLEVBQTlCLElBQUksQ0FDUCxDQUFDLEdBSUosR0FEQyxDQUFDLElBQUksQ0FBRWhCLE1BQUksQ0FBRSxFQUFaLElBQUksQ0FTUixHQVBHRyxpQkFBaUIsR0FBakIsRUFFQSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQVAsS0FBTSxDQUFDLENBQUUsQ0FBQUYsV0FBVyxDQUFBd0IsTUFBTyxDQUFDLENBQUMsRUFBRSxFQUFwQyxJQUFJLENBQ0wsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFFLENBQUF4QixXQUFXLENBQUF1QixLQUFNLENBQUMsQ0FBQyxFQUFFLEVBQXBDLElBQUksQ0FBdUMsR0FJL0MsR0FEQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUV2QixZQUFVLENBQUUsRUFBM0IsSUFBSSxDQUNQLENBQUMsR0FNSixHQUpHRCxLQUFLLEdBQ1AsQ0FBQyxJQUFJLENBQUVBLE1BQUksQ0FBRSxFQUFaLElBQUksQ0FHTixHQURDLENBQUMsSUFBSSxDQUFFQyxZQUFVLENBQUUsRUFBbEIsSUFBSSxDQUNOO0lBQUFTLENBQUEsTUFBQVIsU0FBQTtJQUFBUSxDQUFBLE1BQUFQLGlCQUFBO0lBQUFPLENBQUEsTUFBQU0sTUFBQTtJQUFBTixDQUFBLE1BQUFULFdBQUE7SUFBQVMsQ0FBQSxNQUFBVixLQUFBO0lBQUFVLENBQUEsTUFBQWEsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQWIsQ0FBQTtFQUFBO0VBQUEsSUFBQWdCLEdBQUE7RUFBQSxJQUFBaEIsQ0FBQSxRQUFBTixNQUFBLElBQUFNLENBQUEsUUFBQVksRUFBQSxJQUFBWixDQUFBLFFBQUFhLEVBQUE7SUEvQkhHLEdBQUEsSUFBQyxJQUFJLENBQVcsUUFBVSxDQUFWLENBQUFKLEVBQVMsQ0FBQyxDQUN2QmxCLE9BQUssQ0FBRyxJQUFFLENBQ1YsQ0FBQW1CLEVBNkJELENBQ0YsRUFoQ0MsSUFBSSxDQWdDRTtJQUFBYixDQUFBLE1BQUFOLE1BQUE7SUFBQU0sQ0FBQSxNQUFBWSxFQUFBO0lBQUFaLENBQUEsTUFBQWEsRUFBQTtJQUFBYixDQUFBLE1BQUFnQixHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBaEIsQ0FBQTtFQUFBO0VBQUEsSUFBQWlCLEdBQUE7RUFBQSxJQUFBakIsQ0FBQSxTQUFBZ0IsR0FBQSxJQUFBaEIsQ0FBQSxTQUFBUSxFQUFBLElBQUFSLENBQUEsU0FBQVMsRUFBQSxJQUFBVCxDQUFBLFNBQUFVLEVBQUEsSUFBQVYsQ0FBQSxTQUFBVyxFQUFBLElBQUFYLENBQUEsU0FBQUwsS0FBQTtJQXhDVHNCLEdBQUEsSUFBQyxHQUFHLENBQ1UsVUFBQyxDQUFELEdBQUMsQ0FDQSxXQUFnQyxDQUFoQyxDQUFBVCxFQUErQixDQUFDLENBQ2hDLFdBQW9DLENBQXBDLENBQUFDLEVBQW1DLENBQUMsQ0FDakMsY0FBVSxDQUFWLENBQUFDLEVBQVMsQ0FBQyxDQUNoQixRQUFrQixDQUFsQixDQUFBQyxFQUFpQixDQUFDLENBQ3JCaEIsS0FBSyxDQUFMQSxNQUFJLENBQUMsQ0FFWixDQUFBcUIsR0FnQ00sQ0FDUixFQXpDQyxHQUFHLENBeUNFO0lBQUFoQixDQUFBLE9BQUFnQixHQUFBO0lBQUFoQixDQUFBLE9BQUFRLEVBQUE7SUFBQVIsQ0FBQSxPQUFBUyxFQUFBO0lBQUFULENBQUEsT0FBQVUsRUFBQTtJQUFBVixDQUFBLE9BQUFXLEVBQUE7SUFBQVgsQ0FBQSxPQUFBTCxLQUFBO0lBQUFLLENBQUEsT0FBQWlCLEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUFqQixDQUFBO0VBQUE7RUFBQSxPQXpDTmlCLEdBeUNNO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/SentryErrorBoundary.ts b/components/SentryErrorBoundary.ts new file mode 100644 index 0000000..11bf1fa --- /dev/null +++ b/components/SentryErrorBoundary.ts @@ -0,0 +1,28 @@ +import * as React from 'react' + +interface Props { + children: React.ReactNode +} + +interface State { + hasError: boolean +} + +export class SentryErrorBoundary extends React.Component { + constructor(props: Props) { + super(props) + this.state = { hasError: false } + } + + static getDerivedStateFromError(): State { + return { hasError: true } + } + + render(): React.ReactNode { + if (this.state.hasError) { + return null + } + + return this.props.children + } +} diff --git a/components/SessionBackgroundHint.tsx b/components/SessionBackgroundHint.tsx new file mode 100644 index 0000000..ece9ffb --- /dev/null +++ b/components/SessionBackgroundHint.tsx @@ -0,0 +1,108 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useCallback, useState } from 'react'; +import { useDoublePress } from '../hooks/useDoublePress.js'; +import { Box, Text } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js'; +import { useAppState, useAppStateStore, useSetAppState } from '../state/AppState.js'; +import { backgroundAll, hasForegroundTasks } from '../tasks/LocalShellTask/LocalShellTask.js'; +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js'; +import { env } from '../utils/env.js'; +import { isEnvTruthy } from '../utils/envUtils.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +type Props = { + onBackgroundSession: () => void; + isLoading: boolean; +}; + +/** + * Shows a hint when user presses Ctrl+B to background the current session. + * Uses double-press pattern: first press shows hint, second press within 800ms backgrounds. + * + * Only activates when: + * 1. isLoading is true (a query is in progress) + * 2. No foreground tasks (bash/agent) are running (those take priority for Ctrl+B) + */ +export function SessionBackgroundHint(t0) { + const $ = _c(10); + const { + onBackgroundSession, + isLoading + } = t0; + const setAppState = useSetAppState(); + const appStateStore = useAppStateStore(); + const [showSessionHint, setShowSessionHint] = useState(false); + const handleDoublePress = useDoublePress(setShowSessionHint, onBackgroundSession, _temp); + let t1; + if ($[0] !== appStateStore || $[1] !== handleDoublePress || $[2] !== isLoading || $[3] !== setAppState) { + t1 = () => { + if (isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS)) { + return; + } + const state = appStateStore.getState(); + if (hasForegroundTasks(state)) { + backgroundAll(() => appStateStore.getState(), setAppState); + if (!getGlobalConfig().hasUsedBackgroundTask) { + saveGlobalConfig(_temp2); + } + } else { + if (isEnvTruthy("false") && isLoading) { + handleDoublePress(); + } + } + }; + $[0] = appStateStore; + $[1] = handleDoublePress; + $[2] = isLoading; + $[3] = setAppState; + $[4] = t1; + } else { + t1 = $[4]; + } + const handleBackground = t1; + const hasForeground = useAppState(hasForegroundTasks); + let t2; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t2 = isEnvTruthy("false"); + $[5] = t2; + } else { + t2 = $[5]; + } + const sessionBgEnabled = t2; + const t3 = hasForeground || sessionBgEnabled && isLoading; + let t4; + if ($[6] !== t3) { + t4 = { + context: "Task", + isActive: t3 + }; + $[6] = t3; + $[7] = t4; + } else { + t4 = $[7]; + } + useKeybinding("task:background", handleBackground, t4); + const baseShortcut = useShortcutDisplay("task:background", "Task", "ctrl+b"); + const shortcut = env.terminal === "tmux" && baseShortcut === "ctrl+b" ? "ctrl+b ctrl+b" : baseShortcut; + if (!isLoading || !showSessionHint) { + return null; + } + let t5; + if ($[8] !== shortcut) { + t5 = ; + $[8] = shortcut; + $[9] = t5; + } else { + t5 = $[9]; + } + return t5; +} +function _temp2(c) { + return c.hasUsedBackgroundTask ? c : { + ...c, + hasUsedBackgroundTask: true + }; +} +function _temp() {} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlU3RhdGUiLCJ1c2VEb3VibGVQcmVzcyIsIkJveCIsIlRleHQiLCJ1c2VLZXliaW5kaW5nIiwidXNlU2hvcnRjdXREaXNwbGF5IiwidXNlQXBwU3RhdGUiLCJ1c2VBcHBTdGF0ZVN0b3JlIiwidXNlU2V0QXBwU3RhdGUiLCJiYWNrZ3JvdW5kQWxsIiwiaGFzRm9yZWdyb3VuZFRhc2tzIiwiZ2V0R2xvYmFsQ29uZmlnIiwic2F2ZUdsb2JhbENvbmZpZyIsImVudiIsImlzRW52VHJ1dGh5IiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJQcm9wcyIsIm9uQmFja2dyb3VuZFNlc3Npb24iLCJpc0xvYWRpbmciLCJTZXNzaW9uQmFja2dyb3VuZEhpbnQiLCJ0MCIsIiQiLCJfYyIsInNldEFwcFN0YXRlIiwiYXBwU3RhdGVTdG9yZSIsInNob3dTZXNzaW9uSGludCIsInNldFNob3dTZXNzaW9uSGludCIsImhhbmRsZURvdWJsZVByZXNzIiwiX3RlbXAiLCJ0MSIsInByb2Nlc3MiLCJDTEFVREVfQ09ERV9ESVNBQkxFX0JBQ0tHUk9VTkRfVEFTS1MiLCJzdGF0ZSIsImdldFN0YXRlIiwiaGFzVXNlZEJhY2tncm91bmRUYXNrIiwiX3RlbXAyIiwiaGFuZGxlQmFja2dyb3VuZCIsImhhc0ZvcmVncm91bmQiLCJ0MiIsIlN5bWJvbCIsImZvciIsInNlc3Npb25CZ0VuYWJsZWQiLCJ0MyIsInQ0IiwiY29udGV4dCIsImlzQWN0aXZlIiwiYmFzZVNob3J0Y3V0Iiwic2hvcnRjdXQiLCJ0ZXJtaW5hbCIsInQ1IiwiYyJdLCJzb3VyY2VzIjpbIlNlc3Npb25CYWNrZ3JvdW5kSGludC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VDYWxsYmFjaywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZURvdWJsZVByZXNzIH0gZnJvbSAnLi4vaG9va3MvdXNlRG91YmxlUHJlc3MuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7IHVzZVNob3J0Y3V0RGlzcGxheSB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZVNob3J0Y3V0RGlzcGxheS5qcydcbmltcG9ydCB7XG4gIHVzZUFwcFN0YXRlLFxuICB1c2VBcHBTdGF0ZVN0b3JlLFxuICB1c2VTZXRBcHBTdGF0ZSxcbn0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQge1xuICBiYWNrZ3JvdW5kQWxsLFxuICBoYXNGb3JlZ3JvdW5kVGFza3MsXG59IGZyb20gJy4uL3Rhc2tzL0xvY2FsU2hlbGxUYXNrL0xvY2FsU2hlbGxUYXNrLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYmFsQ29uZmlnLCBzYXZlR2xvYmFsQ29uZmlnIH0gZnJvbSAnLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgZW52IH0gZnJvbSAnLi4vdXRpbHMvZW52LmpzJ1xuaW1wb3J0IHsgaXNFbnZUcnV0aHkgfSBmcm9tICcuLi91dGlscy9lbnZVdGlscy5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBvbkJhY2tncm91bmRTZXNzaW9uOiAoKSA9PiB2b2lkXG4gIGlzTG9hZGluZzogYm9vbGVhblxufVxuXG4vKipcbiAqIFNob3dzIGEgaGludCB3aGVuIHVzZXIgcHJlc3NlcyBDdHJsK0IgdG8gYmFja2dyb3VuZCB0aGUgY3VycmVudCBzZXNzaW9uLlxuICogVXNlcyBkb3VibGUtcHJlc3MgcGF0dGVybjogZmlyc3QgcHJlc3Mgc2hvd3MgaGludCwgc2Vjb25kIHByZXNzIHdpdGhpbiA4MDBtcyBiYWNrZ3JvdW5kcy5cbiAqXG4gKiBPbmx5IGFjdGl2YXRlcyB3aGVuOlxuICogMS4gaXNMb2FkaW5nIGlzIHRydWUgKGEgcXVlcnkgaXMgaW4gcHJvZ3Jlc3MpXG4gKiAyLiBObyBmb3JlZ3JvdW5kIHRhc2tzIChiYXNoL2FnZW50KSBhcmUgcnVubmluZyAodGhvc2UgdGFrZSBwcmlvcml0eSBmb3IgQ3RybCtCKVxuICovXG5leHBvcnQgZnVuY3Rpb24gU2Vzc2lvbkJhY2tncm91bmRIaW50KHtcbiAgb25CYWNrZ3JvdW5kU2Vzc2lvbixcbiAgaXNMb2FkaW5nLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdEVsZW1lbnQgfCBudWxsIHtcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IGFwcFN0YXRlU3RvcmUgPSB1c2VBcHBTdGF0ZVN0b3JlKClcblxuICBjb25zdCBbc2hvd1Nlc3Npb25IaW50LCBzZXRTaG93U2Vzc2lvbkhpbnRdID0gdXNlU3RhdGUoZmFsc2UpXG5cbiAgY29uc3QgaGFuZGxlRG91YmxlUHJlc3MgPSB1c2VEb3VibGVQcmVzcyhcbiAgICBzZXRTaG93U2Vzc2lvbkhpbnQsXG4gICAgb25CYWNrZ3JvdW5kU2Vzc2lvbixcbiAgICAoKSA9PiB7fSwgLy8gRmlyc3QgcHJlc3MganVzdCBzaG93cyB0aGUgaGludFxuICApXG5cbiAgLy8gSGFuZGxlciBmb3IgdGFzazpiYWNrZ3JvdW5kIC0gcHJpb3JpdGl6ZXMgZm9yZWdyb3VuZCB0YXNrcywgZmFsbHMgYmFjayB0byBzZXNzaW9uIGJhY2tncm91bmRpbmdcbiAgLy8gU2tpcCBhbGwgYmFja2dyb3VuZCBmdW5jdGlvbmFsaXR5IGlmIGJhY2tncm91bmQgdGFza3MgYXJlIGRpc2FibGVkXG4gIGNvbnN0IGhhbmRsZUJhY2tncm91bmQgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgaWYgKGlzRW52VHJ1dGh5KHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0RJU0FCTEVfQkFDS0dST1VORF9UQVNLUykpIHtcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBjb25zdCBzdGF0ZSA9IGFwcFN0YXRlU3RvcmUuZ2V0U3RhdGUoKVxuICAgIGlmIChoYXNGb3JlZ3JvdW5kVGFza3Moc3RhdGUpKSB7XG4gICAgICAvLyBFeGlzdGluZyBiZWhhdmlvciAtIGJhY2tncm91bmQgcnVubmluZyBiYXNoL2FnZW50IHRhc2tzXG4gICAgICBiYWNrZ3JvdW5kQWxsKCgpID0+IGFwcFN0YXRlU3RvcmUuZ2V0U3RhdGUoKSwgc2V0QXBwU3RhdGUpXG4gICAgICBpZiAoIWdldEdsb2JhbENvbmZpZygpLmhhc1VzZWRCYWNrZ3JvdW5kVGFzaykge1xuICAgICAgICBzYXZlR2xvYmFsQ29uZmlnKGMgPT5cbiAgICAgICAgICBjLmhhc1VzZWRCYWNrZ3JvdW5kVGFzayA/IGMgOiB7IC4uLmMsIGhhc1VzZWRCYWNrZ3JvdW5kVGFzazogdHJ1ZSB9LFxuICAgICAgICApXG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChcbiAgICAgIGlzRW52VHJ1dGh5KFwiZmFsc2VcIikgJiZcbiAgICAgIGlzTG9hZGluZ1xuICAgICkge1xuICAgICAgLy8gTmV3IGJlaGF2aW9yIC0gZG91YmxlLXByZXNzIHRvIGJhY2tncm91bmQgc2Vzc2lvbiAoZ2F0ZWQpXG4gICAgICBoYW5kbGVEb3VibGVQcmVzcygpXG4gICAgfVxuICB9LCBbc2V0QXBwU3RhdGUsIGFwcFN0YXRlU3RvcmUsIGlzTG9hZGluZywgaGFuZGxlRG91YmxlUHJlc3NdKVxuXG4gIC8vIE9ubHkgZWF0IGN0cmwrYiB3aGVuIHRoZXJlJ3Mgc29tZXRoaW5nIHRvIGJhY2tncm91bmQuIFdpdGhvdXQgdGhpcyBnYXRlXG4gIC8vIHRoZSBiaW5kaW5nIGRvdWJsZS1maXJlcyB3aXRoIHJlYWRsaW5lIGJhY2t3YXJkLWNoYXIgYXQgYW4gaWRsZSBwcm9tcHQuXG4gIGNvbnN0IGhhc0ZvcmVncm91bmQgPSB1c2VBcHBTdGF0ZShoYXNGb3JlZ3JvdW5kVGFza3MpXG4gIGNvbnN0IHNlc3Npb25CZ0VuYWJsZWQgPSBpc0VudlRydXRoeShcImZhbHNlXCIpXG4gIHVzZUtleWJpbmRpbmcoJ3Rhc2s6YmFja2dyb3VuZCcsIGhhbmRsZUJhY2tncm91bmQsIHtcbiAgICBjb250ZXh0OiAnVGFzaycsXG4gICAgaXNBY3RpdmU6IGhhc0ZvcmVncm91bmQgfHwgKHNlc3Npb25CZ0VuYWJsZWQgJiYgaXNMb2FkaW5nKSxcbiAgfSlcblxuICAvLyBHZXQgdGhlIGNvbmZpZ3VyZWQgc2hvcnRjdXQgZm9yIHRhc2s6YmFja2dyb3VuZFxuICBjb25zdCBiYXNlU2hvcnRjdXQgPSB1c2VTaG9ydGN1dERpc3BsYXkoJ3Rhc2s6YmFja2dyb3VuZCcsICdUYXNrJywgJ2N0cmwrYicpXG4gIC8vIEluIHRtdXgsIGN0cmwrYiBpcyB0aGUgcHJlZml4IGtleSwgc28gdXNlcnMgbmVlZCB0byBwcmVzcyBpdCB0d2ljZSB0byBzZW5kIGN0cmwrYlxuICBjb25zdCBzaG9ydGN1dCA9XG4gICAgZW52LnRlcm1pbmFsID09PSAndG11eCcgJiYgYmFzZVNob3J0Y3V0ID09PSAnY3RybCtiJ1xuICAgICAgPyAnY3RybCtiIGN0cmwrYidcbiAgICAgIDogYmFzZVNob3J0Y3V0XG5cbiAgaWYgKCFpc0xvYWRpbmcgfHwgIXNob3dTZXNzaW9uSGludCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxCb3ggcGFkZGluZ0xlZnQ9ezJ9PlxuICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD17c2hvcnRjdXR9IGFjdGlvbj1cImJhY2tncm91bmRcIiAvPlxuICAgICAgPC9UZXh0PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFdBQVcsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDN0MsU0FBU0MsY0FBYyxRQUFRLDRCQUE0QjtBQUMzRCxTQUFTQyxHQUFHLEVBQUVDLElB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 \ No newline at end of file diff --git a/components/SessionPreview.tsx b/components/SessionPreview.tsx new file mode 100644 index 0000000..459474f --- /dev/null +++ b/components/SessionPreview.tsx @@ -0,0 +1,194 @@ +import { c as _c } from "react/compiler-runtime"; +import type { UUID } from 'crypto'; +import React, { useCallback } from 'react'; +import { Box, Text } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { getAllBaseTools } from '../tools.js'; +import type { LogOption } from '../types/logs.js'; +import { formatRelativeTimeAgo } from '../utils/format.js'; +import { getSessionIdFromLog, isLiteLog, loadFullLog } from '../utils/sessionStorage.js'; +import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js'; +import { Byline } from './design-system/Byline.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +import { LoadingState } from './design-system/LoadingState.js'; +import { Messages } from './Messages.js'; +type Props = { + log: LogOption; + onExit: () => void; + onSelect: (log: LogOption) => void; +}; +export function SessionPreview(t0) { + const $ = _c(33); + const { + log, + onExit, + onSelect + } = t0; + const [fullLog, setFullLog] = React.useState(null); + let t1; + let t2; + if ($[0] !== log) { + t1 = () => { + setFullLog(null); + if (isLiteLog(log)) { + loadFullLog(log).then(setFullLog); + } + }; + t2 = [log]; + $[0] = log; + $[1] = t1; + $[2] = t2; + } else { + t1 = $[1]; + t2 = $[2]; + } + React.useEffect(t1, t2); + const isLoading = isLiteLog(log) && fullLog === null; + const displayLog = fullLog ?? log; + let t3; + if ($[3] !== displayLog) { + t3 = getSessionIdFromLog(displayLog) || "" as UUID; + $[3] = displayLog; + $[4] = t3; + } else { + t3 = $[4]; + } + const conversationId = t3; + let t4; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t4 = getAllBaseTools(); + $[5] = t4; + } else { + t4 = $[5]; + } + const tools = t4; + let t5; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + context: "Confirmation" + }; + $[6] = t5; + } else { + t5 = $[6]; + } + useKeybinding("confirm:no", onExit, t5); + let t6; + if ($[7] !== fullLog || $[8] !== log || $[9] !== onSelect) { + t6 = () => { + onSelect(fullLog ?? log); + }; + $[7] = fullLog; + $[8] = log; + $[9] = onSelect; + $[10] = t6; + } else { + t6 = $[10]; + } + const handleSelect = t6; + let t7; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t7 = { + context: "Confirmation" + }; + $[11] = t7; + } else { + t7 = $[11]; + } + useKeybinding("confirm:yes", handleSelect, t7); + if (isLoading) { + let t8; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t8 = ; + $[12] = t8; + } else { + t8 = $[12]; + } + let t9; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t9 = {t8}; + $[13] = t9; + } else { + t9 = $[13]; + } + return t9; + } + let t8; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t8 = []; + $[14] = t8; + } else { + t8 = $[14]; + } + let t10; + let t9; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t9 = []; + t10 = new Set(); + $[15] = t10; + $[16] = t9; + } else { + t10 = $[15]; + t9 = $[16]; + } + let t11; + if ($[17] === Symbol.for("react.memo_cache_sentinel")) { + t11 = []; + $[17] = t11; + } else { + t11 = $[17]; + } + let t12; + if ($[18] !== conversationId || $[19] !== displayLog.messages) { + t12 = ; + $[18] = conversationId; + $[19] = displayLog.messages; + $[20] = t12; + } else { + t12 = $[20]; + } + let t13; + if ($[21] !== displayLog.modified) { + t13 = formatRelativeTimeAgo(displayLog.modified); + $[21] = displayLog.modified; + $[22] = t13; + } else { + t13 = $[22]; + } + const t14 = displayLog.gitBranch ? ` · ${displayLog.gitBranch}` : ""; + let t15; + if ($[23] !== displayLog.messageCount || $[24] !== t13 || $[25] !== t14) { + t15 = {t13} ·{" "}{displayLog.messageCount} messages{t14}; + $[23] = displayLog.messageCount; + $[24] = t13; + $[25] = t14; + $[26] = t15; + } else { + t15 = $[26]; + } + let t16; + if ($[27] === Symbol.for("react.memo_cache_sentinel")) { + t16 = ; + $[27] = t16; + } else { + t16 = $[27]; + } + let t17; + if ($[28] !== t15) { + t17 = {t15}{t16}; + $[28] = t15; + $[29] = t17; + } else { + t17 = $[29]; + } + let t18; + if ($[30] !== t12 || $[31] !== t17) { + t18 = {t12}{t17}; + $[30] = t12; + $[31] = t17; + $[32] = t18; + } else { + t18 = $[32]; + } + return t18; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJVVUlEIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsIkJveCIsIlRleHQiLCJ1c2VLZXliaW5kaW5nIiwiZ2V0QWxsQmFzZVRvb2xzIiwiTG9nT3B0aW9uIiwiZm9ybWF0UmVsYXRpdmVUaW1lQWdvIiwiZ2V0U2Vzc2lvbklkRnJvbUxvZyIsImlzTGl0ZUxvZyIsImxvYWRGdWxsTG9nIiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiQnlsaW5lIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJMb2FkaW5nU3RhdGUiLCJNZXNzYWdlcyIsIlByb3BzIiwibG9nIiwib25FeGl0Iiwib25TZWxlY3QiLCJTZXNzaW9uUHJldmlldyIsInQwIiwiJCIsIl9jIiwiZnVsbExvZyIsInNldEZ1bGxMb2ciLCJ1c2VTdGF0ZSIsInQxIiwidDIiLCJ0aGVuIiwidXNlRWZmZWN0IiwiaXNMb2FkaW5nIiwiZGlzcGxheUxvZyIsInQzIiwiY29udmVyc2F0aW9uSWQiLCJ0NCIsIlN5bWJvbCIsImZvciIsInRvb2xzIiwidDUiLCJjb250ZXh0IiwidDYiLCJoYW5kbGVTZWxlY3QiLCJ0NyIsInQ4IiwidDkiLCJ0MTAiLCJTZXQiLCJ0MTEiLCJ0MTIiLCJtZXNzYWdlcyIsInQxMyIsIm1vZGlmaWVkIiwidDE0IiwiZ2l0QnJhbmNoIiwidDE1IiwibWVzc2FnZUNvdW50IiwidDE2IiwidDE3IiwidDE4Il0sInNvdXJjZXMiOlsiU2Vzc2lvblByZXZpZXcudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgVVVJRCB9IGZyb20gJ2NyeXB0bydcbmltcG9ydCBSZWFjdCwgeyB1c2VDYWxsYmFjayB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyBnZXRBbGxCYXNlVG9vbHMgfSBmcm9tICcuLi90b29scy5qcydcbmltcG9ydCB0eXBlIHsgTG9nT3B0aW9uIH0gZnJvbSAnLi4vdHlwZXMvbG9ncy5qcydcbmltcG9ydCB7IGZvcm1hdFJlbGF0aXZlVGltZUFnbyB9IGZyb20gJy4uL3V0aWxzL2Zvcm1hdC5qcydcbmltcG9ydCB7XG4gIGdldFNlc3Npb25JZEZyb21Mb2csXG4gIGlzTGl0ZUxvZyxcbiAgbG9hZEZ1bGxMb2csXG59IGZyb20gJy4uL3V0aWxzL3Nlc3Npb25TdG9yYWdlLmpzJ1xuaW1wb3J0IHsgQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vQnlsaW5lLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBMb2FkaW5nU3RhdGUgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vTG9hZGluZ1N0YXRlLmpzJ1xuaW1wb3J0IHsgTWVzc2FnZXMgfSBmcm9tICcuL01lc3NhZ2VzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBsb2c6IExvZ09wdGlvblxuICBvbkV4aXQ6ICgpID0+IHZvaWRcbiAgb25TZWxlY3Q6IChsb2c6IExvZ09wdGlvbikgPT4gdm9pZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gU2Vzc2lvblByZXZpZXcoe1xuICBsb2csXG4gIG9uRXhpdCxcbiAgb25TZWxlY3QsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIGZ1bGxMb2cgaG9sZHMgdGhlIGNvbXBsZXRlIGxvZyB3aXRoIG1lc3NhZ2VzIGxvYWRlZC5cbiAgLy8gVGhlIGlucHV0IGBsb2dgIG1heSBiZSBhIFwibGl0ZSBsb2dcIiAoZW1wdHkgbWVzc2FnZXMgYXJyYXkpLFxuICAvLyBzbyB3ZSBsb2FkIHRoZSBmdWxsIG1lc3NhZ2VzIG9uIG1vdW50IGFuZCBzdG9yZSB0aGVtIGhlcmUuXG4gIGNvbnN0IFtmdWxsTG9nLCBzZXRGdWxsTG9nXSA9IFJlYWN0LnVzZVN0YXRlPExvZ09wdGlvbiB8IG51bGw+KG51bGwpXG5cbiAgLy8gTG9hZCBmdWxsIG1lc3NhZ2VzIGlmIHRoaXMgaXMgYSBsaXRlIGxvZ1xuICBSZWFjdC51c2VFZmZlY3QoKCkgPT4ge1xuICAgIHNldEZ1bGxMb2cobnVsbClcbiAgICBpZiAoaXNMaXRlTG9nKGxvZykpIHtcbiAgICAgIHZvaWQgbG9hZEZ1bGxMb2cobG9nKS50aGVuKHNldEZ1bGxMb2cpXG4gICAgfVxuICB9LCBbbG9nXSlcblxuICBjb25zdCBpc0xvYWRpbmcgPSBpc0xpdGVMb2cobG9nKSAmJiBmdWxsTG9nID09PSBudWxsXG4gIGNvbnN0IGRpc3BsYXlMb2cgPSBmdWxsTG9nID8/IGxvZ1xuICBjb25zdCBjb252ZXJzYXRpb25JZCA9IGdldFNlc3Npb25JZEZyb21Mb2coZGlzcGxheUxvZykgfHwgKCcnIGFzIFVVSUQpXG5cbiAgLy8gR2V0IGFsbCBiYXNlIHRvb2xzIGZvciBwcmV2aWV3IChubyBwZXJtaXNzaW9ucyBuZWVkZWQgZm9yIHJlYWQtb25seSB2aWV3KVxuICBjb25zdCB0b29scyA9IGdldEFsbEJhc2VUb29scygpXG5cbiAgLy8gSGFuZGxlIGtleWJvYXJkIGlucHV0IHZpYSBrZXliaW5kaW5nc1xuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOm5vJywgb25FeGl0LCB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nIH0pXG5cbiAgY29uc3QgaGFuZGxlU2VsZWN0ID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIG9uU2VsZWN0KGZ1bGxMb2cgPz8gbG9nKVxuICB9LCBbb25TZWxlY3QsIGZ1bGxMb2csIGxvZ10pXG5cbiAgdXNlS2V5YmluZGluZygnY29uZmlybTp5ZXMnLCBoYW5kbGVTZWxlY3QsIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicgfSlcblxuICAvLyBTaG93IGxvYWRpbmcgc3RhdGUgd2hpbGUgZmV0Y2hpbmcgZnVsbCBsb2dcbiAgaWYgKGlzTG9hZGluZykge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nPXsxfT5cbiAgICAgICAgPExvYWRpbmdTdGF0ZSBtZXNzYWdlPVwiTG9hZGluZyBzZXNzaW9u4oCmXCIgLz5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgYWN0aW9uPVwiY29uZmlybTpub1wiXG4gICAgICAgICAgICAgIGNvbnRleHQ9XCJDb25maXJtYXRpb25cIlxuICAgICAgICAgICAgICBmYWxsYmFjaz1cIkVzY1wiXG4gICAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwiY2FuY2VsXCJcbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9CeWxpbmU+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8TWVzc2FnZXNcbiAgICAgICAgbWVzc2FnZXM9e2Rpc3BsYXlMb2cubWVzc2FnZXN9XG4gICAgICAgIHRvb2xzPXt0b29sc31cbiAgICAgICAgY29tbWFuZHM9e1tdfVxuICAgICAgICB2ZXJib3NlPXt0cnVlfVxuICAgICAgICB0b29sSlNYPXtudWxsfVxuICAgICAgICB0b29sVXNlQ29uZmlybVF1ZXVlPXtbXX1cbiAgICAgICAgaW5Qcm9ncmVzc1Rvb2xVc2VJRHM9e25ldyBTZXQoKX1cbiAgICAgICAgaXNNZXNzYWdlU2VsZWN0b3JWaXNpYmxlPXtmYWxzZX1cbiAgICAgICAgY29udmVyc2F0aW9uSWQ9e2NvbnZlcnNhdGlvbklkfVxuICAgICAgICBzY3JlZW49XCJ0cmFuc2NyaXB0XCJcbiAgICAgICAgc3RyZWFtaW5nVG9vbFVzZXM9e1tdfVxuICAgICAgICBzaG93QWxsSW5UcmFuc2NyaXB0PXt0cnVlfVxuICAgICAgICBpc0xvYWRpbmc9e2ZhbHNlfVxuICAgICAgLz5cbiAgICAgIDxCb3hcbiAgICAgICAgZmxleFNocmluaz17MH1cbiAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgIGJvcmRlclRvcERpbUNvbG9yXG4gICAgICAgIGJvcmRlckJvdHRvbT17ZmFsc2V9XG4gICAgICAgIGJvcmRlckxlZnQ9e2ZhbHNlfVxuICAgICAgICBib3JkZXJSaWdodD17ZmFsc2V9XG4gICAgICAgIGJvcmRlclN0eWxlPVwic2luZ2xlXCJcbiAgICAgICAgcGFkZGluZ0xlZnQ9ezJ9XG4gICAgICA+XG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIHtmb3JtYXRSZWxhdGl2ZVRpbWVBZ28oZGlzcGxheUxvZy5tb2RpZmllZCl9IMK3eycgJ31cbiAgICAgICAgICB7ZGlzcGxheUxvZy5tZXNzYWdlQ291bnR9IG1lc3NhZ2VzXG4gICAgICAgICAge2Rpc3BsYXlMb2cuZ2l0QnJhbmNoID8gYCDCtyAke2Rpc3BsYXlMb2cuZ2l0QnJhbmNofWAgOiAnJ31cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICA8QnlsaW5lPlxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiRW50ZXJcIiBhY3Rpb249XCJyZXN1bWVcIiAvPlxuICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgPC9UZXh0PlxuICAg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 \ No newline at end of file diff --git a/components/Settings/Config.tsx b/components/Settings/Config.tsx new file mode 100644 index 0000000..37ee93c --- /dev/null +++ b/components/Settings/Config.tsx @@ -0,0 +1,1822 @@ +import { c as _c } from "react/compiler-runtime"; +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +import { feature } from 'bun:bundle'; +import { Box, Text, useTheme, useThemeSetting, useTerminalFocus } from '../../ink.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import * as React from 'react'; +import { useState, useCallback } from 'react'; +import { useKeybinding, useKeybindings } from '../../keybindings/useKeybinding.js'; +import figures from 'figures'; +import { type GlobalConfig, saveGlobalConfig, getCurrentProjectConfig, type OutputStyle } from '../../utils/config.js'; +import { normalizeApiKeyForConfig } from '../../utils/authPortable.js'; +import { getGlobalConfig, getAutoUpdaterDisabledReason, formatAutoUpdaterDisabledReason, getRemoteControlAtStartup } from '../../utils/config.js'; +import chalk from 'chalk'; +import { permissionModeTitle, permissionModeFromString, toExternalPermissionMode, isExternalPermissionMode, EXTERNAL_PERMISSION_MODES, PERMISSION_MODES, type ExternalPermissionMode, type PermissionMode } from '../../utils/permissions/PermissionMode.js'; +import { getAutoModeEnabledState, hasAutoModeOptInAnySource, transitionPlanAutoMode } from '../../utils/permissions/permissionSetup.js'; +import { logError } from '../../utils/log.js'; +import { logEvent, type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from 'src/services/analytics/index.js'; +import { isBridgeEnabled } from '../../bridge/bridgeEnabled.js'; +import { ThemePicker } from '../ThemePicker.js'; +import { useAppState, useSetAppState, useAppStateStore } from '../../state/AppState.js'; +import { ModelPicker } from '../ModelPicker.js'; +import { modelDisplayString, isOpus1mMergeEnabled } from '../../utils/model/model.js'; +import { isBilledAsExtraUsage } from '../../utils/extraUsage.js'; +import { ClaudeMdExternalIncludesDialog } from '../ClaudeMdExternalIncludesDialog.js'; +import { ChannelDowngradeDialog, type ChannelDowngradeChoice } from '../ChannelDowngradeDialog.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { Select } from '../CustomSelect/index.js'; +import { OutputStylePicker } from '../OutputStylePicker.js'; +import { LanguagePicker } from '../LanguagePicker.js'; +import { getExternalClaudeMdIncludes, getMemoryFiles, hasExternalClaudeMdIncludes } from 'src/utils/claudemd.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { Byline } from '../design-system/Byline.js'; +import { useTabHeaderFocus } from '../design-system/Tabs.js'; +import { useIsInsideModal } from '../../context/modalContext.js'; +import { SearchBox } from '../SearchBox.js'; +import { isSupportedTerminal, hasAccessToIDEExtensionDiffFeature } from '../../utils/ide.js'; +import { getInitialSettings, getSettingsForSource, updateSettingsForSource } from '../../utils/settings/settings.js'; +import { getUserMsgOptIn, setUserMsgOptIn } from '../../bootstrap/state.js'; +import { DEFAULT_OUTPUT_STYLE_NAME } from 'src/constants/outputStyles.js'; +import { isEnvTruthy, isRunningOnHomespace } from 'src/utils/envUtils.js'; +import type { LocalJSXCommandContext, CommandResultDisplay } from '../../commands.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../services/analytics/growthbook.js'; +import { isAgentSwarmsEnabled } from '../../utils/agentSwarmsEnabled.js'; +import { getCliTeammateModeOverride, clearCliTeammateModeOverride } from '../../utils/swarm/backends/teammateModeSnapshot.js'; +import { getHardcodedTeammateModelFallback } from '../../utils/swarm/teammateModel.js'; +import { useSearchInput } from '../../hooks/useSearchInput.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { clearFastModeCooldown, FAST_MODE_MODEL_DISPLAY, isFastModeAvailable, isFastModeEnabled, getFastModeModel, isFastModeSupportedByModel } from '../../utils/fastMode.js'; +import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js'; +type Props = { + onClose: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; + context: LocalJSXCommandContext; + setTabsHidden: (hidden: boolean) => void; + onIsSearchModeChange?: (inSearchMode: boolean) => void; + contentHeight?: number; +}; +type SettingBase = { + id: string; + label: string; +} | { + id: string; + label: React.ReactNode; + searchText: string; +}; +type Setting = (SettingBase & { + value: boolean; + onChange(value: boolean): void; + type: 'boolean'; +}) | (SettingBase & { + value: string; + options: string[]; + onChange(value: string): void; + type: 'enum'; +}) | (SettingBase & { + // For enums that are set by a custom component, we don't need to pass options, + // but we still need a value to display in the top-level config menu + value: string; + onChange(value: string): void; + type: 'managedEnum'; +}); +type SubMenu = 'Theme' | 'Model' | 'TeammateModel' | 'ExternalIncludes' | 'OutputStyle' | 'ChannelDowngrade' | 'Language' | 'EnableAutoUpdates'; +export function Config({ + onClose, + context, + setTabsHidden, + onIsSearchModeChange, + contentHeight +}: Props): React.ReactNode { + const { + headerFocused, + focusHeader + } = useTabHeaderFocus(); + const insideModal = useIsInsideModal(); + const [, setTheme] = useTheme(); + const themeSetting = useThemeSetting(); + const [globalConfig, setGlobalConfig] = useState(getGlobalConfig()); + const initialConfig = React.useRef(getGlobalConfig()); + const [settingsData, setSettingsData] = useState(getInitialSettings()); + const initialSettingsData = React.useRef(getInitialSettings()); + const [currentOutputStyle, setCurrentOutputStyle] = useState(settingsData?.outputStyle || DEFAULT_OUTPUT_STYLE_NAME); + const initialOutputStyle = React.useRef(currentOutputStyle); + const [currentLanguage, setCurrentLanguage] = useState(settingsData?.language); + const initialLanguage = React.useRef(currentLanguage); + const [selectedIndex, setSelectedIndex] = useState(0); + const [scrollOffset, setScrollOffset] = useState(0); + const [isSearchMode, setIsSearchMode] = useState(true); + const isTerminalFocused = useTerminalFocus(); + const { + rows + } = useTerminalSize(); + // contentHeight is set by Settings.tsx (same value passed to Tabs to fix + // pane height across all tabs — prevents layout jank when switching). + // Reserve ~10 rows for chrome (search box, gaps, footer, scroll hints). + // Fallback calc for standalone rendering (tests). + const paneCap = contentHeight ?? Math.min(Math.floor(rows * 0.8), 30); + const maxVisible = Math.max(5, paneCap - 10); + const mainLoopModel = useAppState(s => s.mainLoopModel); + const verbose = useAppState(s_0 => s_0.verbose); + const thinkingEnabled = useAppState(s_1 => s_1.thinkingEnabled); + const isFastMode = useAppState(s_2 => isFastModeEnabled() ? s_2.fastMode : false); + const promptSuggestionEnabled = useAppState(s_3 => s_3.promptSuggestionEnabled); + // Show auto in the default-mode dropdown when the user has opted in OR the + // config is fully 'enabled' — even if currently circuit-broken ('disabled'), + // an opted-in user should still see it in settings (it's a temporary state). + const showAutoInDefaultModePicker = feature('TRANSCRIPT_CLASSIFIER') ? hasAutoModeOptInAnySource() || getAutoModeEnabledState() === 'enabled' : false; + // Chat/Transcript view picker is visible to entitled users (pass the GB + // gate) even if they haven't opted in this session — it IS the persistent + // opt-in. 'chat' written here is read at next startup by main.tsx which + // sets userMsgOptIn if still entitled. + /* eslint-disable @typescript-eslint/no-require-imports */ + const showDefaultViewPicker = feature('KAIROS') || feature('KAIROS_BRIEF') ? (require('../../tools/BriefTool/BriefTool.js') as typeof import('../../tools/BriefTool/BriefTool.js')).isBriefEntitled() : false; + /* eslint-enable @typescript-eslint/no-require-imports */ + const setAppState = useSetAppState(); + const [changes, setChanges] = useState<{ + [key: string]: unknown; + }>({}); + const initialThinkingEnabled = React.useRef(thinkingEnabled); + // Per-source settings snapshots for revert-on-escape. getInitialSettings() + // returns merged-across-sources which can't tell us what to delete vs + // restore; per-source snapshots + updateSettingsForSource's + // undefined-deletes-key semantics can. Lazy-init via useState (no setter) to + // avoid reading settings files on every render — useRef evaluates its arg + // eagerly even though only the first result is kept. + const [initialLocalSettings] = useState(() => getSettingsForSource('localSettings')); + const [initialUserSettings] = useState(() => getSettingsForSource('userSettings')); + const initialThemeSetting = React.useRef(themeSetting); + // AppState fields Config may modify — snapshot once at mount. + const store = useAppStateStore(); + const [initialAppState] = useState(() => { + const s_4 = store.getState(); + return { + mainLoopModel: s_4.mainLoopModel, + mainLoopModelForSession: s_4.mainLoopModelForSession, + verbose: s_4.verbose, + thinkingEnabled: s_4.thinkingEnabled, + fastMode: s_4.fastMode, + promptSuggestionEnabled: s_4.promptSuggestionEnabled, + isBriefOnly: s_4.isBriefOnly, + replBridgeEnabled: s_4.replBridgeEnabled, + replBridgeOutboundOnly: s_4.replBridgeOutboundOnly, + settings: s_4.settings + }; + }); + // Bootstrap state snapshot — userMsgOptIn is outside AppState, so + // revertChanges needs to restore it separately. Without this, cycling + // defaultView to 'chat' then Escape leaves the tool active while the + // display filter reverts — the exact ambient-activation behavior this + // PR's entitlement/opt-in split is meant to prevent. + const [initialUserMsgOptIn] = useState(() => getUserMsgOptIn()); + // Set on first user-visible change; gates revertChanges() on Escape so + // opening-then-closing doesn't trigger redundant disk writes. + const isDirty = React.useRef(false); + const [showThinkingWarning, setShowThinkingWarning] = useState(false); + const [showSubmenu, setShowSubmenu] = useState(null); + const { + query: searchQuery, + setQuery: setSearchQuery, + cursorOffset: searchCursorOffset + } = useSearchInput({ + isActive: isSearchMode && showSubmenu === null && !headerFocused, + onExit: () => setIsSearchMode(false), + onExitUp: focusHeader, + // Ctrl+C/D must reach Settings' useExitOnCtrlCD; 'd' also avoids + // double-action (delete-char + exit-pending). + passthroughCtrlKeys: ['c', 'd'] + }); + + // Tell the parent when Config's own Esc handler is active so Settings cedes + // confirm:no. Only true when search mode owns the keyboard — not when the + // tab header is focused (then Settings must handle Esc-to-close). + const ownsEsc = isSearchMode && !headerFocused; + React.useEffect(() => { + onIsSearchModeChange?.(ownsEsc); + }, [ownsEsc, onIsSearchModeChange]); + const isConnectedToIde = hasAccessToIDEExtensionDiffFeature(context.options.mcpClients); + const isFileCheckpointingAvailable = !isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_FILE_CHECKPOINTING); + const memoryFiles = React.use(getMemoryFiles(true)); + const shouldShowExternalIncludesToggle = hasExternalClaudeMdIncludes(memoryFiles); + const autoUpdaterDisabledReason = getAutoUpdaterDisabledReason(); + function onChangeMainModelConfig(value: string | null): void { + const previousModel = mainLoopModel; + logEvent('tengu_config_model_changed', { + from_model: previousModel as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + to_model: value as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + setAppState(prev => ({ + ...prev, + mainLoopModel: value, + mainLoopModelForSession: null + })); + setChanges(prev_0 => { + const valStr = modelDisplayString(value) + (isBilledAsExtraUsage(value, false, isOpus1mMergeEnabled()) ? ' · Billed as extra usage' : ''); + if ('model' in prev_0) { + const { + model, + ...rest + } = prev_0; + return { + ...rest, + model: valStr + }; + } + return { + ...prev_0, + model: valStr + }; + }); + } + function onChangeVerbose(value_0: boolean): void { + // Update the global config to persist the setting + saveGlobalConfig(current => ({ + ...current, + verbose: value_0 + })); + setGlobalConfig({ + ...getGlobalConfig(), + verbose: value_0 + }); + + // Update the app state for immediate UI feedback + setAppState(prev_1 => ({ + ...prev_1, + verbose: value_0 + })); + setChanges(prev_2 => { + if ('verbose' in prev_2) { + const { + verbose: verbose_0, + ...rest_0 + } = prev_2; + return rest_0; + } + return { + ...prev_2, + verbose: value_0 + }; + }); + } + + // TODO: Add MCP servers + const settingsItems: Setting[] = [ + // Global settings + { + id: 'autoCompactEnabled', + label: 'Auto-compact', + value: globalConfig.autoCompactEnabled, + type: 'boolean' as const, + onChange(autoCompactEnabled: boolean) { + saveGlobalConfig(current_0 => ({ + ...current_0, + autoCompactEnabled + })); + setGlobalConfig({ + ...getGlobalConfig(), + autoCompactEnabled + }); + logEvent('tengu_auto_compact_setting_changed', { + enabled: autoCompactEnabled + }); + } + }, { + id: 'spinnerTipsEnabled', + label: 'Show tips', + value: settingsData?.spinnerTipsEnabled ?? true, + type: 'boolean' as const, + onChange(spinnerTipsEnabled: boolean) { + updateSettingsForSource('localSettings', { + spinnerTipsEnabled + }); + // Update local state to reflect the change immediately + setSettingsData(prev_3 => ({ + ...prev_3, + spinnerTipsEnabled + })); + logEvent('tengu_tips_setting_changed', { + enabled: spinnerTipsEnabled + }); + } + }, { + id: 'prefersReducedMotion', + label: 'Reduce motion', + value: settingsData?.prefersReducedMotion ?? false, + type: 'boolean' as const, + onChange(prefersReducedMotion: boolean) { + updateSettingsForSource('localSettings', { + prefersReducedMotion + }); + setSettingsData(prev_4 => ({ + ...prev_4, + prefersReducedMotion + })); + // Sync to AppState so components react immediately + setAppState(prev_5 => ({ + ...prev_5, + settings: { + ...prev_5.settings, + prefersReducedMotion + } + })); + logEvent('tengu_reduce_motion_setting_changed', { + enabled: prefersReducedMotion + }); + } + }, { + id: 'thinkingEnabled', + label: 'Thinking mode', + value: thinkingEnabled ?? true, + type: 'boolean' as const, + onChange(enabled: boolean) { + setAppState(prev_6 => ({ + ...prev_6, + thinkingEnabled: enabled + })); + updateSettingsForSource('userSettings', { + alwaysThinkingEnabled: enabled ? undefined : false + }); + logEvent('tengu_thinking_toggled', { + enabled + }); + } + }, + // Fast mode toggle (ant-only, eliminated from external builds) + ...(isFastModeEnabled() && isFastModeAvailable() ? [{ + id: 'fastMode', + label: `Fast mode (${FAST_MODE_MODEL_DISPLAY} only)`, + value: !!isFastMode, + type: 'boolean' as const, + onChange(enabled_0: boolean) { + clearFastModeCooldown(); + updateSettingsForSource('userSettings', { + fastMode: enabled_0 ? true : undefined + }); + if (enabled_0) { + setAppState(prev_7 => ({ + ...prev_7, + mainLoopModel: getFastModeModel(), + mainLoopModelForSession: null, + fastMode: true + })); + setChanges(prev_8 => ({ + ...prev_8, + model: getFastModeModel(), + 'Fast mode': 'ON' + })); + } else { + setAppState(prev_9 => ({ + ...prev_9, + fastMode: false + })); + setChanges(prev_10 => ({ + ...prev_10, + 'Fast mode': 'OFF' + })); + } + } + }] : []), ...(getFeatureValue_CACHED_MAY_BE_STALE('tengu_chomp_inflection', false) ? [{ + id: 'promptSuggestionEnabled', + label: 'Prompt suggestions', + value: promptSuggestionEnabled, + type: 'boolean' as const, + onChange(enabled_1: boolean) { + setAppState(prev_11 => ({ + ...prev_11, + promptSuggestionEnabled: enabled_1 + })); + updateSettingsForSource('userSettings', { + promptSuggestionEnabled: enabled_1 ? undefined : false + }); + } + }] : []), + // Speculation toggle (ant-only) + ...("external" === 'ant' ? [{ + id: 'speculationEnabled', + label: 'Speculative execution', + value: globalConfig.speculationEnabled ?? true, + type: 'boolean' as const, + onChange(enabled_2: boolean) { + saveGlobalConfig(current_1 => { + if (current_1.speculationEnabled === enabled_2) return current_1; + return { + ...current_1, + speculationEnabled: enabled_2 + }; + }); + setGlobalConfig({ + ...getGlobalConfig(), + speculationEnabled: enabled_2 + }); + logEvent('tengu_speculation_setting_changed', { + enabled: enabled_2 + }); + } + }] : []), ...(isFileCheckpointingAvailable ? [{ + id: 'fileCheckpointingEnabled', + label: 'Rewind code (checkpoints)', + value: globalConfig.fileCheckpointingEnabled, + type: 'boolean' as const, + onChange(enabled_3: boolean) { + saveGlobalConfig(current_2 => ({ + ...current_2, + fileCheckpointingEnabled: enabled_3 + })); + setGlobalConfig({ + ...getGlobalConfig(), + fileCheckpointingEnabled: enabled_3 + }); + logEvent('tengu_file_history_snapshots_setting_changed', { + enabled: enabled_3 + }); + } + }] : []), { + id: 'verbose', + label: 'Verbose output', + value: verbose, + type: 'boolean', + onChange: onChangeVerbose + }, { + id: 'terminalProgressBarEnabled', + label: 'Terminal progress bar', + value: globalConfig.terminalProgressBarEnabled, + type: 'boolean' as const, + onChange(terminalProgressBarEnabled: boolean) { + saveGlobalConfig(current_3 => ({ + ...current_3, + terminalProgressBarEnabled + })); + setGlobalConfig({ + ...getGlobalConfig(), + terminalProgressBarEnabled + }); + logEvent('tengu_terminal_progress_bar_setting_changed', { + enabled: terminalProgressBarEnabled + }); + } + }, ...(getFeatureValue_CACHED_MAY_BE_STALE('tengu_terminal_sidebar', false) ? [{ + id: 'showStatusInTerminalTab', + label: 'Show status in terminal tab', + value: globalConfig.showStatusInTerminalTab ?? false, + type: 'boolean' as const, + onChange(showStatusInTerminalTab: boolean) { + saveGlobalConfig(current_4 => ({ + ...current_4, + showStatusInTerminalTab + })); + setGlobalConfig({ + ...getGlobalConfig(), + showStatusInTerminalTab + }); + logEvent('tengu_terminal_tab_status_setting_changed', { + enabled: showStatusInTerminalTab + }); + } + }] : []), { + id: 'showTurnDuration', + label: 'Show turn duration', + value: globalConfig.showTurnDuration, + type: 'boolean' as const, + onChange(showTurnDuration: boolean) { + saveGlobalConfig(current_5 => ({ + ...current_5, + showTurnDuration + })); + setGlobalConfig({ + ...getGlobalConfig(), + showTurnDuration + }); + logEvent('tengu_show_turn_duration_setting_changed', { + enabled: showTurnDuration + }); + } + }, { + id: 'defaultPermissionMode', + label: 'Default permission mode', + value: settingsData?.permissions?.defaultMode || 'default', + options: (() => { + const priorityOrder: PermissionMode[] = ['default', 'plan']; + const allModes: readonly PermissionMode[] = feature('TRANSCRIPT_CLASSIFIER') ? PERMISSION_MODES : EXTERNAL_PERMISSION_MODES; + const excluded: PermissionMode[] = ['bypassPermissions']; + if (feature('TRANSCRIPT_CLASSIFIER') && !showAutoInDefaultModePicker) { + excluded.push('auto'); + } + return [...priorityOrder, ...allModes.filter(m => !priorityOrder.includes(m) && !excluded.includes(m))]; + })(), + type: 'enum' as const, + onChange(mode: string) { + const parsedMode = permissionModeFromString(mode); + // Internal modes (e.g. auto) are stored directly + const validatedMode = isExternalPermissionMode(parsedMode) ? toExternalPermissionMode(parsedMode) : parsedMode; + const result = updateSettingsForSource('userSettings', { + permissions: { + ...settingsData?.permissions, + defaultMode: validatedMode as ExternalPermissionMode + } + }); + if (result.error) { + logError(result.error); + return; + } + + // Update local state to reflect the change immediately. + // validatedMode is typed as the wide PermissionMode union but at + // runtime is always a PERMISSION_MODES member (the options dropdown + // is built from that array above), so this narrowing is sound. + setSettingsData(prev_12 => ({ + ...prev_12, + permissions: { + ...prev_12?.permissions, + defaultMode: validatedMode as (typeof PERMISSION_MODES)[number] + } + })); + // Track changes + setChanges(prev_13 => ({ + ...prev_13, + defaultPermissionMode: mode + })); + logEvent('tengu_config_changed', { + setting: 'defaultPermissionMode' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + value: mode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }, ...(feature('TRANSCRIPT_CLASSIFIER') && showAutoInDefaultModePicker ? [{ + id: 'useAutoModeDuringPlan', + label: 'Use auto mode during plan', + value: (settingsData as { + useAutoModeDuringPlan?: boolean; + } | undefined)?.useAutoModeDuringPlan ?? true, + type: 'boolean' as const, + onChange(useAutoModeDuringPlan: boolean) { + updateSettingsForSource('userSettings', { + useAutoModeDuringPlan + }); + setSettingsData(prev_14 => ({ + ...prev_14, + useAutoModeDuringPlan + })); + // Internal writes suppress the file watcher, so + // applySettingsChange won't fire. Reconcile directly so + // mid-plan toggles take effect immediately. + setAppState(prev_15 => { + const next = transitionPlanAutoMode(prev_15.toolPermissionContext); + if (next === prev_15.toolPermissionContext) return prev_15; + return { + ...prev_15, + toolPermissionContext: next + }; + }); + setChanges(prev_16 => ({ + ...prev_16, + 'Use auto mode during plan': useAutoModeDuringPlan + })); + } + }] : []), { + id: 'respectGitignore', + label: 'Respect .gitignore in file picker', + value: globalConfig.respectGitignore, + type: 'boolean' as const, + onChange(respectGitignore: boolean) { + saveGlobalConfig(current_6 => ({ + ...current_6, + respectGitignore + })); + setGlobalConfig({ + ...getGlobalConfig(), + respectGitignore + }); + logEvent('tengu_respect_gitignore_setting_changed', { + enabled: respectGitignore + }); + } + }, { + id: 'copyFullResponse', + label: 'Always copy full response (skip /copy picker)', + value: globalConfig.copyFullResponse, + type: 'boolean' as const, + onChange(copyFullResponse: boolean) { + saveGlobalConfig(current_7 => ({ + ...current_7, + copyFullResponse + })); + setGlobalConfig({ + ...getGlobalConfig(), + copyFullResponse + }); + logEvent('tengu_config_changed', { + setting: 'copyFullResponse' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + value: String(copyFullResponse) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }, + // Copy-on-select is only meaningful with in-app selection (fullscreen + // alt-screen mode). In inline mode the terminal emulator owns selection. + ...(isFullscreenEnvEnabled() ? [{ + id: 'copyOnSelect', + label: 'Copy on select', + value: globalConfig.copyOnSelect ?? true, + type: 'boolean' as const, + onChange(copyOnSelect: boolean) { + saveGlobalConfig(current_8 => ({ + ...current_8, + copyOnSelect + })); + setGlobalConfig({ + ...getGlobalConfig(), + copyOnSelect + }); + logEvent('tengu_config_changed', { + setting: 'copyOnSelect' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + value: String(copyOnSelect) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }] : []), + // autoUpdates setting is hidden - use DISABLE_AUTOUPDATER env var to control + autoUpdaterDisabledReason ? { + id: 'autoUpdatesChannel', + label: 'Auto-update channel', + value: 'disabled', + type: 'managedEnum' as const, + onChange() {} + } : { + id: 'autoUpdatesChannel', + label: 'Auto-update channel', + value: settingsData?.autoUpdatesChannel ?? 'latest', + type: 'managedEnum' as const, + onChange() { + // Handled via toggleSetting -> 'ChannelDowngrade' + } + }, { + id: 'theme', + label: 'Theme', + value: themeSetting, + type: 'managedEnum', + onChange: setTheme + }, { + id: 'notifChannel', + label: feature('KAIROS') || feature('KAIROS_PUSH_NOTIFICATION') ? 'Local notifications' : 'Notifications', + value: globalConfig.preferredNotifChannel, + options: ['auto', 'iterm2', 'terminal_bell', 'iterm2_with_bell', 'kitty', 'ghostty', 'notifications_disabled'], + type: 'enum', + onChange(notifChannel: GlobalConfig['preferredNotifChannel']) { + saveGlobalConfig(current_9 => ({ + ...current_9, + preferredNotifChannel: notifChannel + })); + setGlobalConfig({ + ...getGlobalConfig(), + preferredNotifChannel: notifChannel + }); + } + }, ...(feature('KAIROS') || feature('KAIROS_PUSH_NOTIFICATION') ? [{ + id: 'taskCompleteNotifEnabled', + label: 'Push when idle', + value: globalConfig.taskCompleteNotifEnabled ?? false, + type: 'boolean' as const, + onChange(taskCompleteNotifEnabled: boolean) { + saveGlobalConfig(current_10 => ({ + ...current_10, + taskCompleteNotifEnabled + })); + setGlobalConfig({ + ...getGlobalConfig(), + taskCompleteNotifEnabled + }); + } + }, { + id: 'inputNeededNotifEnabled', + label: 'Push when input needed', + value: globalConfig.inputNeededNotifEnabled ?? false, + type: 'boolean' as const, + onChange(inputNeededNotifEnabled: boolean) { + saveGlobalConfig(current_11 => ({ + ...current_11, + inputNeededNotifEnabled + })); + setGlobalConfig({ + ...getGlobalConfig(), + inputNeededNotifEnabled + }); + } + }, { + id: 'agentPushNotifEnabled', + label: 'Push when Claude decides', + value: globalConfig.agentPushNotifEnabled ?? false, + type: 'boolean' as const, + onChange(agentPushNotifEnabled: boolean) { + saveGlobalConfig(current_12 => ({ + ...current_12, + agentPushNotifEnabled + })); + setGlobalConfig({ + ...getGlobalConfig(), + agentPushNotifEnabled + }); + } + }] : []), { + id: 'outputStyle', + label: 'Output style', + value: currentOutputStyle, + type: 'managedEnum' as const, + onChange: () => {} // handled by OutputStylePicker submenu + }, ...(showDefaultViewPicker ? [{ + id: 'defaultView', + label: 'What you see by default', + // 'default' means the setting is unset — currently resolves to + // transcript (main.tsx falls through when defaultView !== 'chat'). + // String() narrows the conditional-schema-spread union to string. + value: settingsData?.defaultView === undefined ? 'default' : String(settingsData.defaultView), + options: ['transcript', 'chat', 'default'], + type: 'enum' as const, + onChange(selected: string) { + const defaultView = selected === 'default' ? undefined : selected as 'chat' | 'transcript'; + updateSettingsForSource('localSettings', { + defaultView + }); + setSettingsData(prev_17 => ({ + ...prev_17, + defaultView + })); + const nextBrief = defaultView === 'chat'; + setAppState(prev_18 => { + if (prev_18.isBriefOnly === nextBrief) return prev_18; + return { + ...prev_18, + isBriefOnly: nextBrief + }; + }); + // Keep userMsgOptIn in sync so the tool list follows the view. + // Two-way now (same as /brief) — accepting a cache invalidation + // is better than leaving the tool on after switching away. + // Reverted on Escape via initialUserMsgOptIn snapshot. + setUserMsgOptIn(nextBrief); + setChanges(prev_19 => ({ + ...prev_19, + 'Default view': selected + })); + logEvent('tengu_default_view_setting_changed', { + value: (defaultView ?? 'unset') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }] : []), { + id: 'language', + label: 'Language', + value: currentLanguage ?? 'Default (English)', + type: 'managedEnum' as const, + onChange: () => {} // handled by LanguagePicker submenu + }, { + id: 'editorMode', + label: 'Editor mode', + // Convert 'emacs' to 'normal' for backward compatibility + value: globalConfig.editorMode === 'emacs' ? 'normal' : globalConfig.editorMode || 'normal', + options: ['normal', 'vim'], + type: 'enum', + onChange(value_1: string) { + saveGlobalConfig(current_13 => ({ + ...current_13, + editorMode: value_1 as GlobalConfig['editorMode'] + })); + setGlobalConfig({ + ...getGlobalConfig(), + editorMode: value_1 as GlobalConfig['editorMode'] + }); + logEvent('tengu_editor_mode_changed', { + mode: value_1 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + source: 'config_panel' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }, { + id: 'prStatusFooterEnabled', + label: 'Show PR status footer', + value: globalConfig.prStatusFooterEnabled ?? true, + type: 'boolean' as const, + onChange(enabled_4: boolean) { + saveGlobalConfig(current_14 => { + if (current_14.prStatusFooterEnabled === enabled_4) return current_14; + return { + ...current_14, + prStatusFooterEnabled: enabled_4 + }; + }); + setGlobalConfig({ + ...getGlobalConfig(), + prStatusFooterEnabled: enabled_4 + }); + logEvent('tengu_pr_status_footer_setting_changed', { + enabled: enabled_4 + }); + } + }, { + id: 'model', + label: 'Model', + value: mainLoopModel === null ? 'Default (recommended)' : mainLoopModel, + type: 'managedEnum' as const, + onChange: onChangeMainModelConfig + }, ...(isConnectedToIde ? [{ + id: 'diffTool', + label: 'Diff tool', + value: globalConfig.diffTool ?? 'auto', + options: ['terminal', 'auto'], + type: 'enum' as const, + onChange(diffTool: string) { + saveGlobalConfig(current_15 => ({ + ...current_15, + diffTool: diffTool as GlobalConfig['diffTool'] + })); + setGlobalConfig({ + ...getGlobalConfig(), + diffTool: diffTool as GlobalConfig['diffTool'] + }); + logEvent('tengu_diff_tool_changed', { + tool: diffTool as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + source: 'config_panel' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }] : []), ...(!isSupportedTerminal() ? [{ + id: 'autoConnectIde', + label: 'Auto-connect to IDE (external terminal)', + value: globalConfig.autoConnectIde ?? false, + type: 'boolean' as const, + onChange(autoConnectIde: boolean) { + saveGlobalConfig(current_16 => ({ + ...current_16, + autoConnectIde + })); + setGlobalConfig({ + ...getGlobalConfig(), + autoConnectIde + }); + logEvent('tengu_auto_connect_ide_changed', { + enabled: autoConnectIde, + source: 'config_panel' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }] : []), ...(isSupportedTerminal() ? [{ + id: 'autoInstallIdeExtension', + label: 'Auto-install IDE extension', + value: globalConfig.autoInstallIdeExtension ?? true, + type: 'boolean' as const, + onChange(autoInstallIdeExtension: boolean) { + saveGlobalConfig(current_17 => ({ + ...current_17, + autoInstallIdeExtension + })); + setGlobalConfig({ + ...getGlobalConfig(), + autoInstallIdeExtension + }); + logEvent('tengu_auto_install_ide_extension_changed', { + enabled: autoInstallIdeExtension, + source: 'config_panel' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }] : []), { + id: 'claudeInChromeDefaultEnabled', + label: 'Claude in Chrome enabled by default', + value: globalConfig.claudeInChromeDefaultEnabled ?? true, + type: 'boolean' as const, + onChange(enabled_5: boolean) { + saveGlobalConfig(current_18 => ({ + ...current_18, + claudeInChromeDefaultEnabled: enabled_5 + })); + setGlobalConfig({ + ...getGlobalConfig(), + claudeInChromeDefaultEnabled: enabled_5 + }); + logEvent('tengu_claude_in_chrome_setting_changed', { + enabled: enabled_5 + }); + } + }, + // Teammate mode (only shown when agent swarms are enabled) + ...(isAgentSwarmsEnabled() ? (() => { + const cliOverride = getCliTeammateModeOverride(); + const label = cliOverride ? `Teammate mode [overridden: ${cliOverride}]` : 'Teammate mode'; + return [{ + id: 'teammateMode', + label, + value: globalConfig.teammateMode ?? 'auto', + options: ['auto', 'tmux', 'in-process'], + type: 'enum' as const, + onChange(mode_0: string) { + if (mode_0 !== 'auto' && mode_0 !== 'tmux' && mode_0 !== 'in-process') { + return; + } + // Clear CLI override and set new mode (pass mode to avoid race condition) + clearCliTeammateModeOverride(mode_0); + saveGlobalConfig(current_19 => ({ + ...current_19, + teammateMode: mode_0 + })); + setGlobalConfig({ + ...getGlobalConfig(), + teammateMode: mode_0 + }); + logEvent('tengu_teammate_mode_changed', { + mode: mode_0 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + }, { + id: 'teammateDefaultModel', + label: 'Default teammate model', + value: teammateModelDisplayString(globalConfig.teammateDefaultModel), + type: 'managedEnum' as const, + onChange() {} + }]; + })() : []), + // Remote at startup toggle — gated on build flag + GrowthBook + policy + ...(feature('BRIDGE_MODE') && isBridgeEnabled() ? [{ + id: 'remoteControlAtStartup', + label: 'Enable Remote Control for all sessions', + value: globalConfig.remoteControlAtStartup === undefined ? 'default' : String(globalConfig.remoteControlAtStartup), + options: ['true', 'false', 'default'], + type: 'enum' as const, + onChange(selected_0: string) { + if (selected_0 === 'default') { + // Unset the config key so it falls back to the platform default + saveGlobalConfig(current_20 => { + if (current_20.remoteControlAtStartup === undefined) return current_20; + const next_0 = { + ...current_20 + }; + delete next_0.remoteControlAtStartup; + return next_0; + }); + setGlobalConfig({ + ...getGlobalConfig(), + remoteControlAtStartup: undefined + }); + } else { + const enabled_6 = selected_0 === 'true'; + saveGlobalConfig(current_21 => { + if (current_21.remoteControlAtStartup === enabled_6) return current_21; + return { + ...current_21, + remoteControlAtStartup: enabled_6 + }; + }); + setGlobalConfig({ + ...getGlobalConfig(), + remoteControlAtStartup: enabled_6 + }); + } + // Sync to AppState so useReplBridge reacts immediately + const resolved = getRemoteControlAtStartup(); + setAppState(prev_20 => { + if (prev_20.replBridgeEnabled === resolved && !prev_20.replBridgeOutboundOnly) return prev_20; + return { + ...prev_20, + replBridgeEnabled: resolved, + replBridgeOutboundOnly: false + }; + }); + } + }] : []), ...(shouldShowExternalIncludesToggle ? [{ + id: 'showExternalIncludesDialog', + label: 'External CLAUDE.md includes', + value: (() => { + const projectConfig = getCurrentProjectConfig(); + if (projectConfig.hasClaudeMdExternalIncludesApproved) { + return 'true'; + } else { + return 'false'; + } + })(), + type: 'managedEnum' as const, + onChange() { + // Will be handled by toggleSetting function + } + }] : []), ...(process.env.ANTHROPIC_API_KEY && !isRunningOnHomespace() ? [{ + id: 'apiKey', + label: + Use custom API key:{' '} + + {normalizeApiKeyForConfig(process.env.ANTHROPIC_API_KEY)} + + , + searchText: 'Use custom API key', + value: Boolean(process.env.ANTHROPIC_API_KEY && globalConfig.customApiKeyResponses?.approved?.includes(normalizeApiKeyForConfig(process.env.ANTHROPIC_API_KEY))), + type: 'boolean' as const, + onChange(useCustomKey: boolean) { + saveGlobalConfig(current_22 => { + const updated = { + ...current_22 + }; + if (!updated.customApiKeyResponses) { + updated.customApiKeyResponses = { + approved: [], + rejected: [] + }; + } + if (!updated.customApiKeyResponses.approved) { + updated.customApiKeyResponses = { + ...updated.customApiKeyResponses, + approved: [] + }; + } + if (!updated.customApiKeyResponses.rejected) { + updated.customApiKeyResponses = { + ...updated.customApiKeyResponses, + rejected: [] + }; + } + if (process.env.ANTHROPIC_API_KEY) { + const truncatedKey = normalizeApiKeyForConfig(process.env.ANTHROPIC_API_KEY); + if (useCustomKey) { + updated.customApiKeyResponses = { + ...updated.customApiKeyResponses, + approved: [...(updated.customApiKeyResponses.approved ?? []).filter(k => k !== truncatedKey), truncatedKey], + rejected: (updated.customApiKeyResponses.rejected ?? []).filter(k_0 => k_0 !== truncatedKey) + }; + } else { + updated.customApiKeyResponses = { + ...updated.customApiKeyResponses, + approved: (updated.customApiKeyResponses.approved ?? []).filter(k_1 => k_1 !== truncatedKey), + rejected: [...(updated.customApiKeyResponses.rejected ?? []).filter(k_2 => k_2 !== truncatedKey), truncatedKey] + }; + } + } + return updated; + }); + setGlobalConfig(getGlobalConfig()); + } + }] : [])]; + + // Filter settings based on search query + const filteredSettingsItems = React.useMemo(() => { + if (!searchQuery) return settingsItems; + const lowerQuery = searchQuery.toLowerCase(); + return settingsItems.filter(setting => { + if (setting.id.toLowerCase().includes(lowerQuery)) return true; + const searchableText = 'searchText' in setting ? setting.searchText : setting.label; + return searchableText.toLowerCase().includes(lowerQuery); + }); + }, [settingsItems, searchQuery]); + + // Adjust selected index when filtered list shrinks, and keep the selected + // item visible when maxVisible changes (e.g., terminal resize). + React.useEffect(() => { + if (selectedIndex >= filteredSettingsItems.length) { + const newIndex = Math.max(0, filteredSettingsItems.length - 1); + setSelectedIndex(newIndex); + setScrollOffset(Math.max(0, newIndex - maxVisible + 1)); + return; + } + setScrollOffset(prev_21 => { + if (selectedIndex < prev_21) return selectedIndex; + if (selectedIndex >= prev_21 + maxVisible) return selectedIndex - maxVisible + 1; + return prev_21; + }); + }, [filteredSettingsItems.length, selectedIndex, maxVisible]); + + // Keep the selected item visible within the scroll window. + // Called synchronously from navigation handlers to avoid a render frame + // where the selected item falls outside the visible window. + const adjustScrollOffset = useCallback((newIndex_0: number) => { + setScrollOffset(prev_22 => { + if (newIndex_0 < prev_22) return newIndex_0; + if (newIndex_0 >= prev_22 + maxVisible) return newIndex_0 - maxVisible + 1; + return prev_22; + }); + }, [maxVisible]); + + // Enter: keep all changes (already persisted by onChange handlers), close + // with a summary of what changed. + const handleSaveAndClose = useCallback(() => { + // Submenu handling: each submenu has its own Enter/Esc — don't close + // the whole panel while one is open. + if (showSubmenu !== null) { + return; + } + // Log any changes that were made + // TODO: Make these proper messages + const formattedChanges: string[] = Object.entries(changes).map(([key, value_2]) => { + logEvent('tengu_config_changed', { + key: key as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + value: value_2 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + return `Set ${key} to ${chalk.bold(value_2)}`; + }); + // Check for API key changes + // On homespace, ANTHROPIC_API_KEY is preserved in process.env for child + // processes but ignored by Claude Code itself (see auth.ts). + const effectiveApiKey = isRunningOnHomespace() ? undefined : process.env.ANTHROPIC_API_KEY; + const initialUsingCustomKey = Boolean(effectiveApiKey && initialConfig.current.customApiKeyResponses?.approved?.includes(normalizeApiKeyForConfig(effectiveApiKey))); + const currentUsingCustomKey = Boolean(effectiveApiKey && globalConfig.customApiKeyResponses?.approved?.includes(normalizeApiKeyForConfig(effectiveApiKey))); + if (initialUsingCustomKey !== currentUsingCustomKey) { + formattedChanges.push(`${currentUsingCustomKey ? 'Enabled' : 'Disabled'} custom API key`); + logEvent('tengu_config_changed', { + key: 'env.ANTHROPIC_API_KEY' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + value: currentUsingCustomKey as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + if (globalConfig.theme !== initialConfig.current.theme) { + formattedChanges.push(`Set theme to ${chalk.bold(globalConfig.theme)}`); + } + if (globalConfig.preferredNotifChannel !== initialConfig.current.preferredNotifChannel) { + formattedChanges.push(`Set notifications to ${chalk.bold(globalConfig.preferredNotifChannel)}`); + } + if (currentOutputStyle !== initialOutputStyle.current) { + formattedChanges.push(`Set output style to ${chalk.bold(currentOutputStyle)}`); + } + if (currentLanguage !== initialLanguage.current) { + formattedChanges.push(`Set response language to ${chalk.bold(currentLanguage ?? 'Default (English)')}`); + } + if (globalConfig.editorMode !== initialConfig.current.editorMode) { + formattedChanges.push(`Set editor mode to ${chalk.bold(globalConfig.editorMode || 'emacs')}`); + } + if (globalConfig.diffTool !== initialConfig.current.diffTool) { + formattedChanges.push(`Set diff tool to ${chalk.bold(globalConfig.diffTool)}`); + } + if (globalConfig.autoConnectIde !== initialConfig.current.autoConnectIde) { + formattedChanges.push(`${globalConfig.autoConnectIde ? 'Enabled' : 'Disabled'} auto-connect to IDE`); + } + if (globalConfig.autoInstallIdeExtension !== initialConfig.current.autoInstallIdeExtension) { + formattedChanges.push(`${globalConfig.autoInstallIdeExtension ? 'Enabled' : 'Disabled'} auto-install IDE extension`); + } + if (globalConfig.autoCompactEnabled !== initialConfig.current.autoCompactEnabled) { + formattedChanges.push(`${globalConfig.autoCompactEnabled ? 'Enabled' : 'Disabled'} auto-compact`); + } + if (globalConfig.respectGitignore !== initialConfig.current.respectGitignore) { + formattedChanges.push(`${globalConfig.respectGitignore ? 'Enabled' : 'Disabled'} respect .gitignore in file picker`); + } + if (globalConfig.copyFullResponse !== initialConfig.current.copyFullResponse) { + formattedChanges.push(`${globalConfig.copyFullResponse ? 'Enabled' : 'Disabled'} always copy full response`); + } + if (globalConfig.copyOnSelect !== initialConfig.current.copyOnSelect) { + formattedChanges.push(`${globalConfig.copyOnSelect ? 'Enabled' : 'Disabled'} copy on select`); + } + if (globalConfig.terminalProgressBarEnabled !== initialConfig.current.terminalProgressBarEnabled) { + formattedChanges.push(`${globalConfig.terminalProgressBarEnabled ? 'Enabled' : 'Disabled'} terminal progress bar`); + } + if (globalConfig.showStatusInTerminalTab !== initialConfig.current.showStatusInTerminalTab) { + formattedChanges.push(`${globalConfig.showStatusInTerminalTab ? 'Enabled' : 'Disabled'} terminal tab status`); + } + if (globalConfig.showTurnDuration !== initialConfig.current.showTurnDuration) { + formattedChanges.push(`${globalConfig.showTurnDuration ? 'Enabled' : 'Disabled'} turn duration`); + } + if (globalConfig.remoteControlAtStartup !== initialConfig.current.remoteControlAtStartup) { + const remoteLabel = globalConfig.remoteControlAtStartup === undefined ? 'Reset Remote Control to default' : `${globalConfig.remoteControlAtStartup ? 'Enabled' : 'Disabled'} Remote Control for all sessions`; + formattedChanges.push(remoteLabel); + } + if (settingsData?.autoUpdatesChannel !== initialSettingsData.current?.autoUpdatesChannel) { + formattedChanges.push(`Set auto-update channel to ${chalk.bold(settingsData?.autoUpdatesChannel ?? 'latest')}`); + } + if (formattedChanges.length > 0) { + onClose(formattedChanges.join('\n')); + } else { + onClose('Config dialog dismissed', { + display: 'system' + }); + } + }, [showSubmenu, changes, globalConfig, mainLoopModel, currentOutputStyle, currentLanguage, settingsData?.autoUpdatesChannel, isFastModeEnabled() ? (settingsData as Record | undefined)?.fastMode : undefined, onClose]); + + // Restore all state stores to their mount-time snapshots. Changes are + // applied to disk/AppState immediately on toggle, so "cancel" means + // actively writing the old values back. + const revertChanges = useCallback(() => { + // Theme: restores ThemeProvider React state. Must run before the global + // config overwrite since setTheme internally calls saveGlobalConfig with + // a partial update — we want the full snapshot to be the last write. + if (themeSetting !== initialThemeSetting.current) { + setTheme(initialThemeSetting.current); + } + // Global config: full overwrite from snapshot. saveGlobalConfig skips if + // the returned ref equals current (test mode checks ref; prod writes to + // disk but content is identical). + saveGlobalConfig(() => initialConfig.current); + // Settings files: restore each key Config may have touched. undefined + // deletes the key (updateSettingsForSource customizer at settings.ts:368). + const il = initialLocalSettings; + updateSettingsForSource('localSettings', { + spinnerTipsEnabled: il?.spinnerTipsEnabled, + prefersReducedMotion: il?.prefersReducedMotion, + defaultView: il?.defaultView, + outputStyle: il?.outputStyle + }); + const iu = initialUserSettings; + updateSettingsForSource('userSettings', { + alwaysThinkingEnabled: iu?.alwaysThinkingEnabled, + fastMode: iu?.fastMode, + promptSuggestionEnabled: iu?.promptSuggestionEnabled, + autoUpdatesChannel: iu?.autoUpdatesChannel, + minimumVersion: iu?.minimumVersion, + language: iu?.language, + ...(feature('TRANSCRIPT_CLASSIFIER') ? { + useAutoModeDuringPlan: (iu as { + useAutoModeDuringPlan?: boolean; + } | undefined)?.useAutoModeDuringPlan + } : {}), + // ThemePicker's Ctrl+T writes this key directly — include it so the + // disk state reverts along with the in-memory AppState.settings restore. + syntaxHighlightingDisabled: iu?.syntaxHighlightingDisabled, + // permissions: the defaultMode onChange (above) spreads the MERGED + // settingsData.permissions into userSettings — project/policy allow/deny + // arrays can leak to disk. Spread the full initial snapshot so the + // mergeWith array-customizer (settings.ts:375) replaces leaked arrays. + // Explicitly include defaultMode so undefined triggers the customizer's + // delete path even when iu.permissions lacks that key. + permissions: iu?.permissions === undefined ? undefined : { + ...iu.permissions, + defaultMode: iu.permissions.defaultMode + } + }); + // AppState: batch-restore all possibly-touched fields. + const ia = initialAppState; + setAppState(prev_23 => ({ + ...prev_23, + mainLoopModel: ia.mainLoopModel, + mainLoopModelForSession: ia.mainLoopModelForSession, + verbose: ia.verbose, + thinkingEnabled: ia.thinkingEnabled, + fastMode: ia.fastMode, + promptSuggestionEnabled: ia.promptSuggestionEnabled, + isBriefOnly: ia.isBriefOnly, + replBridgeEnabled: ia.replBridgeEnabled, + replBridgeOutboundOnly: ia.replBridgeOutboundOnly, + settings: ia.settings, + // Reconcile auto-mode state after useAutoModeDuringPlan revert above — + // the onChange handler may have activated/deactivated auto mid-plan. + toolPermissionContext: transitionPlanAutoMode(prev_23.toolPermissionContext) + })); + // Bootstrap state: restore userMsgOptIn. Only touched by the defaultView + // onChange above, so no feature() guard needed here (that path only + // exists when showDefaultViewPicker is true). + if (getUserMsgOptIn() !== initialUserMsgOptIn) { + setUserMsgOptIn(initialUserMsgOptIn); + } + }, [themeSetting, setTheme, initialLocalSettings, initialUserSettings, initialAppState, initialUserMsgOptIn, setAppState]); + + // Escape: revert all changes (if any) and close. + const handleEscape = useCallback(() => { + if (showSubmenu !== null) { + return; + } + if (isDirty.current) { + revertChanges(); + } + onClose('Config dialog dismissed', { + display: 'system' + }); + }, [showSubmenu, revertChanges, onClose]); + + // Disable when submenu is open so the submenu's Dialog handles ESC, and in + // search mode so the onKeyDown handler (which clears-then-exits search) + // wins — otherwise Escape in search would jump straight to revert+close. + useKeybinding('confirm:no', handleEscape, { + context: 'Settings', + isActive: showSubmenu === null && !isSearchMode && !headerFocused + }); + // Save-and-close fires on Enter only when not in search mode (Enter there + // exits search to the list — see the isSearchMode branch in handleKeyDown). + useKeybinding('settings:close', handleSaveAndClose, { + context: 'Settings', + isActive: showSubmenu === null && !isSearchMode && !headerFocused + }); + + // Settings navigation and toggle actions via configurable keybindings. + // Only active when not in search mode and no submenu is open. + const toggleSetting = useCallback(() => { + const setting_0 = filteredSettingsItems[selectedIndex]; + if (!setting_0 || !setting_0.onChange) { + return; + } + if (setting_0.type === 'boolean') { + isDirty.current = true; + setting_0.onChange(!setting_0.value); + if (setting_0.id === 'thinkingEnabled') { + const newValue = !setting_0.value; + const backToInitial = newValue === initialThinkingEnabled.current; + if (backToInitial) { + setShowThinkingWarning(false); + } else if (context.messages.some(m_0 => m_0.type === 'assistant')) { + setShowThinkingWarning(true); + } + } + return; + } + if (setting_0.id === 'theme' || setting_0.id === 'model' || setting_0.id === 'teammateDefaultModel' || setting_0.id === 'showExternalIncludesDialog' || setting_0.id === 'outputStyle' || setting_0.id === 'language') { + // managedEnum items open a submenu — isDirty is set by the submenu's + // completion callback, not here (submenu may be cancelled). + switch (setting_0.id) { + case 'theme': + setShowSubmenu('Theme'); + setTabsHidden(true); + return; + case 'model': + setShowSubmenu('Model'); + setTabsHidden(true); + return; + case 'teammateDefaultModel': + setShowSubmenu('TeammateModel'); + setTabsHidden(true); + return; + case 'showExternalIncludesDialog': + setShowSubmenu('ExternalIncludes'); + setTabsHidden(true); + return; + case 'outputStyle': + setShowSubmenu('OutputStyle'); + setTabsHidden(true); + return; + case 'language': + setShowSubmenu('Language'); + setTabsHidden(true); + return; + } + } + if (setting_0.id === 'autoUpdatesChannel') { + if (autoUpdaterDisabledReason) { + // Auto-updates are disabled - show enable dialog instead + setShowSubmenu('EnableAutoUpdates'); + setTabsHidden(true); + return; + } + const currentChannel = settingsData?.autoUpdatesChannel ?? 'latest'; + if (currentChannel === 'latest') { + // Switching to stable - show downgrade dialog + setShowSubmenu('ChannelDowngrade'); + setTabsHidden(true); + } else { + // Switching to latest - just do it and clear minimumVersion + isDirty.current = true; + updateSettingsForSource('userSettings', { + autoUpdatesChannel: 'latest', + minimumVersion: undefined + }); + setSettingsData(prev_24 => ({ + ...prev_24, + autoUpdatesChannel: 'latest', + minimumVersion: undefined + })); + logEvent('tengu_autoupdate_channel_changed', { + channel: 'latest' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + return; + } + if (setting_0.type === 'enum') { + isDirty.current = true; + const currentIndex = setting_0.options.indexOf(setting_0.value); + const nextIndex = (currentIndex + 1) % setting_0.options.length; + setting_0.onChange(setting_0.options[nextIndex]!); + return; + } + }, [autoUpdaterDisabledReason, filteredSettingsItems, selectedIndex, settingsData?.autoUpdatesChannel, setTabsHidden]); + const moveSelection = (delta: -1 | 1): void => { + setShowThinkingWarning(false); + const newIndex_1 = Math.max(0, Math.min(filteredSettingsItems.length - 1, selectedIndex + delta)); + setSelectedIndex(newIndex_1); + adjustScrollOffset(newIndex_1); + }; + useKeybindings({ + 'select:previous': () => { + if (selectedIndex === 0) { + // ↑ at top enters search mode so users can type-to-filter after + // reaching the list boundary. Wheel-up (scroll:lineUp) clamps + // instead — overshoot shouldn't move focus away from the list. + setShowThinkingWarning(false); + setIsSearchMode(true); + setScrollOffset(0); + } else { + moveSelection(-1); + } + }, + 'select:next': () => moveSelection(1), + // Wheel. ScrollKeybindingHandler's scroll:line* returns false (not + // consumed) when the ScrollBox content fits — which it always does + // here because the list is paginated (slice). The event falls through + // to this handler which navigates the list, clamping at boundaries. + 'scroll:lineUp': () => moveSelection(-1), + 'scroll:lineDown': () => moveSelection(1), + 'select:accept': toggleSetting, + 'settings:search': () => { + setIsSearchMode(true); + setSearchQuery(''); + } + }, { + context: 'Settings', + isActive: showSubmenu === null && !isSearchMode && !headerFocused + }); + + // Combined key handling across search/list modes. Branch order mirrors + // the original useInput gate priority: submenu and header short-circuit + // first (their own handlers own input), then search vs. list. + const handleKeyDown = useCallback((e: KeyboardEvent) => { + if (showSubmenu !== null) return; + if (headerFocused) return; + // Search mode: Esc clears then exits, Enter/↓ moves to the list. + if (isSearchMode) { + if (e.key === 'escape') { + e.preventDefault(); + if (searchQuery.length > 0) { + setSearchQuery(''); + } else { + setIsSearchMode(false); + } + return; + } + if (e.key === 'return' || e.key === 'down' || e.key === 'wheeldown') { + e.preventDefault(); + setIsSearchMode(false); + setSelectedIndex(0); + setScrollOffset(0); + } + return; + } + // List mode: left/right/tab cycle the selected option's value. These + // keys used to switch tabs; now they only do so when the tab row is + // explicitly focused (see headerFocused in Settings.tsx). + if (e.key === 'left' || e.key === 'right' || e.key === 'tab') { + e.preventDefault(); + toggleSetting(); + return; + } + // Fallback: printable characters (other than those bound to actions) + // enter search mode. Carve out j/k// — useKeybindings (still on the + // useInput path) consumes these via stopImmediatePropagation, but + // onKeyDown dispatches independently so we must skip them explicitly. + if (e.ctrl || e.meta) return; + if (e.key === 'j' || e.key === 'k' || e.key === '/') return; + if (e.key.length === 1 && e.key !== ' ') { + e.preventDefault(); + setIsSearchMode(true); + setSearchQuery(e.key); + } + }, [showSubmenu, headerFocused, isSearchMode, searchQuery, setSearchQuery, toggleSetting]); + return + {showSubmenu === 'Theme' ? <> + { + isDirty.current = true; + setTheme(setting_1); + setShowSubmenu(null); + setTabsHidden(false); + }} onCancel={() => { + setShowSubmenu(null); + setTabsHidden(false); + }} hideEscToCancel skipExitHandling={true} // Skip exit handling as Config already handles it + /> + + + + + + + + + : showSubmenu === 'Model' ? <> + { + isDirty.current = true; + onChangeMainModelConfig(model_0); + setShowSubmenu(null); + setTabsHidden(false); + }} onCancel={() => { + setShowSubmenu(null); + setTabsHidden(false); + }} showFastModeNotice={isFastModeEnabled() ? isFastMode && isFastModeSupportedByModel(mainLoopModel) && isFastModeAvailable() : false} /> + + + + + + + : showSubmenu === 'TeammateModel' ? <> + { + setShowSubmenu(null); + setTabsHidden(false); + // First-open-then-Enter from unset: picker highlights "Default" + // (initial=null) and confirming would write null, silently + // switching Opus-fallback → follow-leader. Treat as no-op. + if (globalConfig.teammateDefaultModel === undefined && model_1 === null) { + return; + } + isDirty.current = true; + saveGlobalConfig(current_23 => current_23.teammateDefaultModel === model_1 ? current_23 : { + ...current_23, + teammateDefaultModel: model_1 + }); + setGlobalConfig({ + ...getGlobalConfig(), + teammateDefaultModel: model_1 + }); + setChanges(prev_25 => ({ + ...prev_25, + teammateDefaultModel: teammateModelDisplayString(model_1) + })); + logEvent('tengu_teammate_default_model_changed', { + model: model_1 as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + }} onCancel={() => { + setShowSubmenu(null); + setTabsHidden(false); + }} /> + + + + + + + : showSubmenu === 'ExternalIncludes' ? <> + { + setShowSubmenu(null); + setTabsHidden(false); + }} externalIncludes={getExternalClaudeMdIncludes(memoryFiles)} /> + + + + + + + : showSubmenu === 'OutputStyle' ? <> + { + isDirty.current = true; + setCurrentOutputStyle(style ?? DEFAULT_OUTPUT_STYLE_NAME); + setShowSubmenu(null); + setTabsHidden(false); + + // Save to local settings + updateSettingsForSource('localSettings', { + outputStyle: style + }); + void logEvent('tengu_output_style_changed', { + style: (style ?? DEFAULT_OUTPUT_STYLE_NAME) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + source: 'config_panel' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + settings_source: 'localSettings' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + }} onCancel={() => { + setShowSubmenu(null); + setTabsHidden(false); + }} /> + + + + + + + : showSubmenu === 'Language' ? <> + { + isDirty.current = true; + setCurrentLanguage(language); + setShowSubmenu(null); + setTabsHidden(false); + + // Save to user settings + updateSettingsForSource('userSettings', { + language + }); + void logEvent('tengu_language_changed', { + language: (language ?? 'default') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + source: 'config_panel' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + }} onCancel={() => { + setShowSubmenu(null); + setTabsHidden(false); + }} /> + + + + + + + : showSubmenu === 'EnableAutoUpdates' ? { + setShowSubmenu(null); + setTabsHidden(false); + }} hideBorder hideInputGuide> + {autoUpdaterDisabledReason?.type !== 'config' ? <> + + {autoUpdaterDisabledReason?.type === 'env' ? 'Auto-updates are controlled by an environment variable and cannot be changed here.' : 'Auto-updates are disabled in development builds.'} + + {autoUpdaterDisabledReason?.type === 'env' && + Unset {autoUpdaterDisabledReason.envVar} to re-enable + auto-updates. + } + : ; + $[20] = onInputModeToggle; + $[21] = options; + $[22] = t6; + $[23] = t7; + $[24] = t8; + $[25] = t9; + } else { + t9 = $[25]; + } + let t10; + if ($[26] !== t5 || $[27] !== t9) { + t10 = {t5}{t9}; + $[26] = t5; + $[27] = t9; + $[28] = t10; + } else { + t10 = $[28]; + } + const t11 = (focusedOption === "yes" && !yesInputMode || focusedOption === "no" && !noInputMode) && " \xB7 Tab to amend"; + let t12; + if ($[29] !== t11) { + t12 = Esc to cancel{t11}; + $[29] = t11; + $[30] = t12; + } else { + t12 = $[30]; + } + let t13; + if ($[31] !== t1 || $[32] !== t10 || $[33] !== t12 || $[34] !== t2) { + t13 = {t1}{t2}{t3}{t10}{t12}; + $[31] = t1; + $[32] = t10; + $[33] = t12; + $[34] = t2; + $[35] = t13; + } else { + t13 = $[35]; + } + return t13; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJiYXNlbmFtZSIsInJlbGF0aXZlIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwiZ2V0Q3dkIiwiaXNTdXBwb3J0ZWRWU0NvZGVUZXJtaW5hbCIsIlNlbGVjdCIsIlBhbmUiLCJQZXJtaXNzaW9uT3B0aW9uIiwiUGVybWlzc2lvbk9wdGlvbldpdGhMYWJlbCIsIlByb3BzIiwiZmlsZVBhdGgiLCJpbnB1dCIsIkEiLCJvbkNoYW5nZSIsIm9wdGlvbiIsImFyZ3MiLCJmZWVkYmFjayIsIm9wdGlvbnMiLCJpZGVOYW1lIiwic3ltbGlua1RhcmdldCIsInJlamVjdEZlZWRiYWNrIiwiYWNjZXB0RmVlZGJhY2siLCJzZXRGb2N1c2VkT3B0aW9uIiwidmFsdWUiLCJvbklucHV0TW9kZVRvZ2dsZSIsImZvY3VzZWRPcHRpb24iLCJ5ZXNJbnB1dE1vZGUiLCJub0lucHV0TW9kZSIsIlNob3dJbklERVByb21wdCIsInQwIiwiJCIsIl9jIiwidDEiLCJ0MiIsInN0YXJ0c1dpdGgiLCJ0MyIsIlN5bWJvbCIsImZvciIsInQ0IiwidDUiLCJ0NiIsInNlbGVjdGVkIiwiZmluZCIsIm9wdCIsInR5cGUiLCJ0cmltbWVkRmVlZGJhY2siLCJ0cmltIiwidW5kZWZpbmVkIiwidHJpbW1lZEZlZWRiYWNrXzAiLCJ0NyIsInQ4IiwidmFsdWVfMCIsInQ5IiwidDEwIiwidDExIiwidDEyIiwidDEzIl0sInNvdXJjZXMiOlsiU2hvd0luSURFUHJvbXB0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBiYXNlbmFtZSwgcmVsYXRpdmUgfSBmcm9tICdwYXRoJ1xuaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgZ2V0Q3dkIH0gZnJvbSAnLi4vdXRpbHMvY3dkLmpzJ1xuaW1wb3J0IHsgaXNTdXBwb3J0ZWRWU0NvZGVUZXJtaW5hbCB9IGZyb20gJy4uL3V0aWxzL2lkZS5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L2luZGV4LmpzJ1xuaW1wb3J0IHsgUGFuZSB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9QYW5lLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBQZXJtaXNzaW9uT3B0aW9uLFxuICBQZXJtaXNzaW9uT3B0aW9uV2l0aExhYmVsLFxufSBmcm9tICcuL3Blcm1pc3Npb25zL0ZpbGVQZXJtaXNzaW9uRGlhbG9nL3Blcm1pc3Npb25PcHRpb25zLmpzJ1xuXG50eXBlIFByb3BzPEE+ID0ge1xuICBmaWxlUGF0aDogc3RyaW5nXG4gIGlucHV0OiBBXG4gIG9uQ2hhbmdlOiAob3B0aW9uOiBQZXJtaXNzaW9uT3B0aW9uLCBhcmdzOiBBLCBmZWVkYmFjaz86IHN0cmluZykgPT4gdm9pZFxuICBvcHRpb25zOiBQZXJtaXNzaW9uT3B0aW9uV2l0aExhYmVsW11cbiAgaWRlTmFtZTogc3RyaW5nXG4gIHN5bWxpbmtUYXJnZXQ/OiBzdHJpbmcgfCBudWxsXG4gIHJlamVjdEZlZWRiYWNrOiBzdHJpbmdcbiAgYWNjZXB0RmVlZGJhY2s6IHN0cmluZ1xuICBzZXRGb2N1c2VkT3B0aW9uOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZFxuICBvbklucHV0TW9kZVRvZ2dsZTogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWRcbiAgZm9jdXNlZE9wdGlvbjogc3RyaW5nXG4gIHllc0lucHV0TW9kZTogYm9vbGVhblxuICBub0lucHV0TW9kZTogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gU2hvd0luSURFUHJvbXB0PEE+KHtcbiAgb25DaGFuZ2UsXG4gIG9wdGlvbnMsXG4gIGlucHV0LFxuICBmaWxlUGF0aCxcbiAgaWRlTmFtZSxcbiAgc3ltbGlua1RhcmdldCxcbiAgcmVqZWN0RmVlZGJhY2ssXG4gIGFjY2VwdEZlZWRiYWNrLFxuICBzZXRGb2N1c2VkT3B0aW9uLFxuICBvbklucHV0TW9kZVRvZ2dsZSxcbiAgZm9jdXNlZE9wdGlvbixcbiAgeWVzSW5wdXRNb2RlLFxuICBub0lucHV0TW9kZSxcbn06IFByb3BzPEE+KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8UGFuZSBjb2xvcj1cInBlcm1pc3Npb25cIj5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgIDxUZXh0IGJvbGQgY29sb3I9XCJwZXJtaXNzaW9uXCI+XG4gICAgICAgICAgT3BlbmVkIGNoYW5nZXMgaW4ge2lkZU5hbWV9IOKniVxuICAgICAgICA8L1RleHQ+XG4gICAgICAgIHtzeW1saW5rVGFyZ2V0ICYmIChcbiAgICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj5cbiAgICAgICAgICAgIHtyZWxhdGl2ZShnZXRDd2QoKSwgc3ltbGlua1RhcmdldCkuc3RhcnRzV2l0aCgnLi4nKVxuICAgICAgICAgICAgICA/IGBUaGlzIHdpbGwgbW9kaWZ5ICR7c3ltbGlua1RhcmdldH0gKG91dHNpZGUgd29ya2luZyBkaXJlY3RvcnkpIHZpYSBhIHN5bWxpbmtgXG4gICAgICAgICAgICAgIDogYFN5bWxpbmsgdGFyZ2V0OiAke3N5bWxpbmtUYXJnZXR9YH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIHtpc1N1cHBvcnRlZFZTQ29kZVRlcm1pbmFsKCkgJiYgKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlNhdmUgZmlsZSB0byBjb250aW51ZeKApjwvVGV4dD5cbiAgICAgICAgKX1cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICBEbyB5b3Ugd2FudCB0byBtYWtlIHRoaXMgZWRpdCB0b3snICd9XG4gICAgICAgICAgICA8VGV4dCBib2xkPntiYXNlbmFtZShmaWxlUGF0aCl9PC9UZXh0Pj9cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICAgIGlubGluZURlc2NyaXB0aW9uc1xuICAgICAgICAgICAgb25DaGFuZ2U9e3ZhbHVlID0+IHtcbiAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0ZWQgPSBvcHRpb25zLmZpbmQob3B0ID0+IG9wdC52YWx1ZSA9PT0gdmFsdWUpXG4gICAgICAgICAgICAgIGlmIChzZWxlY3RlZCkge1xuICAgICAgICAgICAgICAgIC8vIEZvciByZWplY3Qgb3B0aW9uXG4gICAgICAgICAgICAgICAgaWYgKHNlbGVjdGVkLm9wdGlvbi50eXBlID09PSAncmVqZWN0Jykge1xuICAgICAgICAgICAgICAgICAgY29uc3QgdHJpbW1lZEZlZWRiYWNrID0gcmVqZWN0RmVlZGJhY2sudHJpbSgpXG4gICAgICAgICAgICAgICAgICBvbkNoYW5nZShzZWxlY3RlZC5vcHRpb24sIGlucHV0LCB0cmltbWVkRmVlZGJhY2sgfHwgdW5kZWZpbmVkKVxuICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIEZvciBhY2NlcHQtb25jZSBvcHRpb24sIHBhc3MgYWNjZXB0IGZlZWRiYWNrIGlmIHByZXNlbnRcbiAgICAgICAgICAgICAgICBpZiAoc2VsZWN0ZWQub3B0aW9uLnR5cGUgPT09ICdhY2NlcHQtb25jZScpIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHRyaW1tZWRGZWVkYmFjayA9IGFjY2VwdEZlZWRiYWNrLnRyaW0oKVxuICAgICAgICAgICAgICAgICAgb25DaGFuZ2Uoc2VsZWN0ZWQub3B0aW9uLCBpbnB1dCwgdHJpbW1lZEZlZWRiYWNrIHx8IHVuZGVmaW5lZClcbiAgICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBvbkNoYW5nZShzZWxlY3RlZC5vcHRpb24sIGlucHV0KVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9fVxuICAgICAgICAgICAgb25DYW5jZWw9eygpID0+IG9uQ2hhbmdlKHsgdHlwZTogJ3JlamVjdCcgfSwgaW5wdXQpfVxuICAgICAgICAgICAgb25Gb2N1cz17dmFsdWUgPT4gc2V0Rm9jdXNlZE9wdGlvbih2YWx1ZSl9XG4gICAgICAgICAgICBvbklucHV0TW9kZVRvZ2dsZT17b25JbnB1dE1vZGVUb2dnbGV9XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIEVzYyB0byBjYW5jZWxcbiAgICAgICAgICAgIHsoKGZvY3VzZWRPcHRpb24gPT09ICd5ZXMnICYmICF5ZXNJbnB1dE1vZGUpIHx8XG4gICAgICAgICAgICAgIChmb2N1c2VkT3B0aW9uID09PSAnbm8nICYmICFub0lucHV0TW9kZSkpICYmXG4gICAgICAgICAgICAgICcgwrcgVGFiIHRvIGFtZW5kJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgPC9QYW5lPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxRQUFRLEVBQUVDLFFBQVEsUUFBUSxNQUFNO0FBQ3pDLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDckMsU0FBU0MsTUFBTSxRQUFRLGlCQUFpQjtBQUN4QyxTQUFTQyx5QkFBeUIsUUFBUSxpQkFBaUI7QUFDM0QsU0FBU0MsTUFBTSxRQUFRLHlCQUF5QjtBQUNoRCxTQUFTQyxJQUFJLFFBQVEseUJBQXlCO0FBQzlDLGNBQ0VDLGdCQUFnQixFQUNoQkMseUJBQXlCLFFBQ3BCLHlEQUF5RDtBQUVoRSxLQUFLQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUc7RUFDZEMsUUFBUSxFQUFFLE1BQU07RUFDaEJDLEtBQUssRUFBRUMsQ0FBQztFQUNSQyxRQUFRLEVBQUUsQ0FBQ0MsTUFBTSxFQUFFUCxnQkFBZ0IsRUFBRVEsSUFBSSxFQUFFSCxDQUFDLEVB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 \ No newline at end of file diff --git a/components/SkillImprovementSurvey.tsx b/components/SkillImprovementSurvey.tsx new file mode 100644 index 0000000..beae0c4 --- /dev/null +++ b/components/SkillImprovementSurvey.tsx @@ -0,0 +1,152 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useEffect, useRef } from 'react'; +import { BLACK_CIRCLE, BULLET_OPERATOR } from '../constants/figures.js'; +import { Box, Text } from '../ink.js'; +import type { SkillUpdate } from '../utils/hooks/skillImprovement.js'; +import { normalizeFullWidthDigits } from '../utils/stringUtils.js'; +import { isValidResponseInput } from './FeedbackSurvey/FeedbackSurveyView.js'; +import type { FeedbackSurveyResponse } from './FeedbackSurvey/utils.js'; +type Props = { + isOpen: boolean; + skillName: string; + updates: SkillUpdate[]; + handleSelect: (selected: FeedbackSurveyResponse) => void; + inputValue: string; + setInputValue: (value: string) => void; +}; +export function SkillImprovementSurvey(t0) { + const $ = _c(6); + const { + isOpen, + skillName, + updates, + handleSelect, + inputValue, + setInputValue + } = t0; + if (!isOpen) { + return null; + } + if (inputValue && !isValidResponseInput(inputValue)) { + return null; + } + let t1; + if ($[0] !== handleSelect || $[1] !== inputValue || $[2] !== setInputValue || $[3] !== skillName || $[4] !== updates) { + t1 = ; + $[0] = handleSelect; + $[1] = inputValue; + $[2] = setInputValue; + $[3] = skillName; + $[4] = updates; + $[5] = t1; + } else { + t1 = $[5]; + } + return t1; +} +type ViewProps = { + skillName: string; + updates: SkillUpdate[]; + onSelect: (option: FeedbackSurveyResponse) => void; + inputValue: string; + setInputValue: (value: string) => void; +}; + +// Only 1 (apply) and 0 (dismiss) are valid for this survey +const VALID_INPUTS = ['0', '1'] as const; +function isValidInput(input: string): boolean { + return (VALID_INPUTS as readonly string[]).includes(input); +} +function SkillImprovementSurveyView(t0) { + const $ = _c(17); + const { + skillName, + updates, + onSelect, + inputValue, + setInputValue + } = t0; + const initialInputValue = useRef(inputValue); + let t1; + let t2; + if ($[0] !== inputValue || $[1] !== onSelect || $[2] !== setInputValue) { + t1 = () => { + if (inputValue !== initialInputValue.current) { + const lastChar = normalizeFullWidthDigits(inputValue.slice(-1)); + if (isValidInput(lastChar)) { + setInputValue(inputValue.slice(0, -1)); + onSelect(lastChar === "1" ? "good" : "dismissed"); + } + } + }; + t2 = [inputValue, onSelect, setInputValue]; + $[0] = inputValue; + $[1] = onSelect; + $[2] = setInputValue; + $[3] = t1; + $[4] = t2; + } else { + t1 = $[3]; + t2 = $[4]; + } + useEffect(t1, t2); + let t3; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t3 = {BLACK_CIRCLE} ; + $[5] = t3; + } else { + t3 = $[5]; + } + let t4; + if ($[6] !== skillName) { + t4 = {t3}Skill improvement suggested for "{skillName}"; + $[6] = skillName; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] !== updates) { + t5 = updates.map(_temp); + $[8] = updates; + $[9] = t5; + } else { + t5 = $[9]; + } + let t6; + if ($[10] !== t5) { + t6 = {t5}; + $[10] = t5; + $[11] = t6; + } else { + t6 = $[11]; + } + let t7; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t7 = 1: Apply; + $[12] = t7; + } else { + t7 = $[12]; + } + let t8; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t8 = {t7}0: Dismiss; + $[13] = t8; + } else { + t8 = $[13]; + } + let t9; + if ($[14] !== t4 || $[15] !== t6) { + t9 = {t4}{t6}{t8}; + $[14] = t4; + $[15] = t6; + $[16] = t9; + } else { + t9 = $[16]; + } + return t9; +} +function _temp(u, i) { + return {BULLET_OPERATOR} {u.change}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsIkJMQUNLX0NJUkNMRSIsIkJVTExFVF9PUEVSQVRPUiIsIkJveCIsIlRleHQiLCJTa2lsbFVwZGF0ZSIsIm5vcm1hbGl6ZUZ1bGxXaWR0aERpZ2l0cyIsImlzVmFsaWRSZXNwb25zZUlucHV0IiwiRmVlZGJhY2tTdXJ2ZXlSZXNwb25zZSIsIlByb3BzIiwiaXNPcGVuIiwic2tpbGxOYW1lIiwidXBkYXRlcyIsImhhbmRsZVNlbGVjdCIsInNlbGVjdGVkIiwiaW5wdXRWYWx1ZSIsInNldElucHV0VmFsdWUiLCJ2YWx1ZSIsIlNraWxsSW1wcm92ZW1lbnRTdXJ2ZXkiLCJ0MCIsIiQiLCJfYyIsInQxIiwiVmlld1Byb3BzIiwib25TZWxlY3QiLCJvcHRpb24iLCJWQUxJRF9JTlBVVFMiLCJjb25zdCIsImlzVmFsaWRJbnB1dCIsImlucHV0IiwiaW5jbHVkZXMiLCJTa2lsbEltcHJvdmVtZW50U3VydmV5VmlldyIsImluaXRpYWxJbnB1dFZhbHVlIiwidDIiLCJjdXJyZW50IiwibGFzdENoYXIiLCJzbGljZSIsInQzIiwiU3ltYm9sIiwiZm9yIiwidDQiLCJ0NSIsIm1hcCIsIl90ZW1wIiwidDYiLCJ0NyIsInQ4IiwidDkiLCJ1IiwiaSIsImNoYW5nZSJdLCJzb3VyY2VzIjpbIlNraWxsSW1wcm92ZW1lbnRTdXJ2ZXkudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VFZmZlY3QsIHVzZVJlZiB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQkxBQ0tfQ0lSQ0xFLCBCVUxMRVRfT1BFUkFUT1IgfSBmcm9tICcuLi9jb25zdGFudHMvZmlndXJlcy5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgU2tpbGxVcGRhdGUgfSBmcm9tICcuLi91dGlscy9ob29rcy9za2lsbEltcHJvdmVtZW50LmpzJ1xuaW1wb3J0IHsgbm9ybWFsaXplRnVsbFdpZHRoRGlnaXRzIH0gZnJvbSAnLi4vdXRpbHMvc3RyaW5nVXRpbHMuanMnXG5pbXBvcnQgeyBpc1ZhbGlkUmVzcG9uc2VJbnB1dCB9IGZyb20gJy4vRmVlZGJhY2tTdXJ2ZXkvRmVlZGJhY2tTdXJ2ZXlWaWV3LmpzJ1xuaW1wb3J0IHR5cGUgeyBGZWVkYmFja1N1cnZleVJlc3BvbnNlIH0gZnJvbSAnLi9GZWVkYmFja1N1cnZleS91dGlscy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgaXNPcGVuOiBib29sZWFuXG4gIHNraWxsTmFtZTogc3RyaW5nXG4gIHVwZGF0ZXM6IFNraWxsVXBkYXRlW11cbiAgaGFuZGxlU2VsZWN0OiAoc2VsZWN0ZWQ6IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UpID0+IHZvaWRcbiAgaW5wdXRWYWx1ZTogc3RyaW5nXG4gIHNldElucHV0VmFsdWU6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTa2lsbEltcHJvdmVtZW50U3VydmV5KHtcbiAgaXNPcGVuLFxuICBza2lsbE5hbWUsXG4gIHVwZGF0ZXMsXG4gIGhhbmRsZVNlbGVjdCxcbiAgaW5wdXRWYWx1ZSxcbiAgc2V0SW5wdXRWYWx1ZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKCFpc09wZW4pIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gSGlkZSB0aGUgc3VydmV5IGlmIHRoZSB1c2VyIGlzIHR5cGluZyBhbnl0aGluZyBvdGhlciB0aGFuIGEgc3VydmV5IHJlc3BvbnNlXG4gIGlmIChpbnB1dFZhbHVlICYmICFpc1ZhbGlkUmVzcG9uc2VJbnB1dChpbnB1dFZhbHVlKSkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxTa2lsbEltcHJvdmVtZW50U3VydmV5Vmlld1xuICAgICAgc2tpbGxOYW1lPXtza2lsbE5hbWV9XG4gICAgICB1cGRhdGVzPXt1cGRhdGVzfVxuICAgICAgb25TZWxlY3Q9e2hhbmRsZVNlbGVjdH1cbiAgICAgIGlucHV0VmFsdWU9e2lucHV0VmFsdWV9XG4gICAgICBzZXRJbnB1dFZhbHVlPXtzZXRJbnB1dFZhbHVlfVxuICAgIC8+XG4gIClcbn1cblxudHlwZSBWaWV3UHJvcHMgPSB7XG4gIHNraWxsTmFtZTogc3RyaW5nXG4gIHVwZGF0ZXM6IFNraWxsVXBkYXRlW11cbiAgb25TZWxlY3Q6IChvcHRpb246IEZlZWRiYWNrU3VydmV5UmVzcG9uc2UpID0+IHZvaWRcbiAgaW5wdXRWYWx1ZTogc3RyaW5nXG4gIHNldElucHV0VmFsdWU6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG59XG5cbi8vIE9ubHkgMSAoYXBwbHkpIGFuZCAwIChkaXNtaXNzKSBhcmUgdmFsaWQgZm9yIHRoaXMgc3VydmV5XG5jb25zdCBWQUxJRF9JTlBVVFMgPSBbJzAnLCAnMSddIGFzIGNvbnN0XG5cbmZ1bmN0aW9uIGlzVmFsaWRJbnB1dChpbnB1dDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiAoVkFMSURfSU5QVVRTIGFzIHJlYWRvbmx5IHN0cmluZ1tdKS5pbmNsdWRlcyhpbnB1dClcbn1cblxuZnVuY3Rpb24gU2tpbGxJbXByb3ZlbWVudFN1cnZleVZpZXcoe1xuICBza2lsbE5hbWUsXG4gIHVwZGF0ZXMsXG4gIG9uU2VsZWN0LFxuICBpbnB1dFZhbHVlLFxuICBzZXRJbnB1dFZhbHVlLFxufTogVmlld1Byb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgaW5pdGlhbElucHV0VmFsdWUgPSB1c2VSZWYoaW5wdXRWYWx1ZSlcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChpbnB1dFZhbHVlICE9PSBpbml0aWFsSW5wdXRWYWx1ZS5jdXJyZW50KSB7XG4gICAgICBjb25zdCBsYXN0Q2hhciA9IG5vcm1hbGl6ZUZ1bGxXaWR0aERpZ2l0cyhpbnB1dFZhbHVlLnNsaWNlKC0xKSlcbiAgICAgIGlmIChpc1ZhbGlkSW5wdXQobGFzdENoYXIpKSB7XG4gICAgICAgIHNldElucHV0VmFsdWUoaW5wdXRWYWx1ZS5zbGljZSgwLCAtMSkpXG4gICAgICAgIC8vIE1hcDogMSA9IFwiZ29vZFwiIChhcHBseSksIDAgPSBcImRpc21pc3NlZFwiXG4gICAgICAgIG9uU2VsZWN0KGxhc3RDaGFyID09PSAnMScgPyAnZ29vZCcgOiAnZGlzbWlzc2VkJylcbiAgICAgIH1cbiAgICB9XG4gIH0sIFtpbnB1dFZhbHVlLCBvblNlbGVjdCwgc2V0SW5wdXRWYWx1ZV0pXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgPEJveD5cbiAgICAgICAgPFRleHQgY29sb3I9XCJhbnNpOmN5YW5cIj57QkxBQ0tfQ0lSQ0xFfSA8L1RleHQ+XG4gICAgICAgIDxUZXh0IGJvbGQ+XG4gICAgICAgICAgU2tpbGwgaW1wcm92ZW1lbnQgc3VnZ2VzdGVkIGZvciAmcXVvdDt7c2tpbGxOYW1lfSZxdW90O1xuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cblxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luTGVmdD17Mn0+XG4gICAgICAgIHt1cGRhdGVzLm1hcCgodSwgaSkgPT4gKFxuICAgICAgICAgIDxUZXh0IGtleT17aX0gZGltQ29sb3I+XG4gICAgICAgICAgICB7QlVMTEVUX09QRVJBVE9SfSB7dS5jaGFuZ2V9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApKX1cbiAgICAgIDwvQm94PlxuXG4gICAgICA8Qm94IG1hcmdpbkxlZnQ9ezJ9IG1hcmdpblRvcD17MX0+XG4gICAgICAgIDxCb3ggd2lkdGg9ezEyfT5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiYW5zaTpjeWFuXCI+MTwvVGV4dD46IEFwcGx5XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCB3aWR0aD17MTR9PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJhbnNpOmN5YW5cIj4wPC9UZXh0PjogRGlzbWlzc1xuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJQyxTQUFTLEVBQUVDLE1BQU0sUUFBUSxPQUFPO0FBQ2hELFNBQVNDLFlBQVksRUFBRUMsZUFBZSxRQUFRLHlCQUF5QjtBQUN2RSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLGNBQWNDLFdBQVcsUUFBUSxvQ0FBb0M7QUFDckUsU0FBU0Msd0JBQXdCLFFBQVEseUJBQXlCO0FBQ2xFLFNBQVNDLG9CQUFvQixRQUFRLHdDQUF3QztBQUM3RSxjQUFjQyxzQkFBc0IsUUFBUSwyQkFBMkI7QUFFdkUsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLE1BQU0sRUFBRSxPQUFPO0VBQ2ZDLFNBQVMsRUFBRSxNQUFNO0VBQ2pCQyxPQUFPLEVBQUVQLFdBQVcsRUFBRTtFQUN0QlEsWUFBWSxFQUFFLENBQUNDLFFBQVEsRUFBRU4sc0JBQXNCLEVBQUUsR0FBRyxJQUFJO0VBQ3hETyxVQUFVLEVBQUUsTUFBTTtFQUNsQkMsYUFBYSxFQUFFLENBQUNDLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0FBQ3hDLENBQUM7QUFFRCxPQUFPLFNBQUFDLHVCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWdDO0lBQUFYLE1BQUE7SUFBQUMsU0FBQTtJQUFBQyxPQUFBO0lBQUFDLFlBQUE7SUFBQUUsVUFBQTtJQUFBQztFQUFBLElBQUFHLEVBTy9CO0VBQ04sSUFBSSxDQUFDVCxNQUFNO0lBQUEsT0FDRixJQUFJO0VBQUE7RUFJYixJQUFJSyxVQUErQyxJQUEvQyxDQUFlUixvQkFBb0IsQ0FBQ1EsVUFBVSxDQUFDO0lBQUEsT0FDMUMsSUFBSTtFQUFBO0VBQ1osSUFBQU8sRUFBQTtFQUFBLElBQUFGLENB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 \ No newline at end of file diff --git a/components/Spinner.tsx b/components/Spinner.tsx new file mode 100644 index 0000000..c170c86 --- /dev/null +++ b/components/Spinner.tsx @@ -0,0 +1,562 @@ +import { c as _c } from "react/compiler-runtime"; +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +import { Box, Text } from '../ink.js'; +import * as React from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { computeGlimmerIndex, computeShimmerSegments, SHIMMER_INTERVAL_MS } from '../bridge/bridgeStatusUtil.js'; +import { feature } from 'bun:bundle'; +import { getKairosActive, getUserMsgOptIn } from '../bootstrap/state.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'; +import { isEnvTruthy } from '../utils/envUtils.js'; +import { count } from '../utils/array.js'; +import sample from 'lodash-es/sample.js'; +import { formatDuration, formatNumber, formatSecondsShort } from '../utils/format.js'; +import type { Theme } from 'src/utils/theme.js'; +import { activityManager } from '../utils/activityManager.js'; +import { getSpinnerVerbs } from '../constants/spinnerVerbs.js'; +import { MessageResponse } from './MessageResponse.js'; +import { TaskListV2 } from './TaskListV2.js'; +import { useTasksV2 } from '../hooks/useTasksV2.js'; +import type { Task } from '../utils/tasks.js'; +import { useAppState } from '../state/AppState.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { stringWidth } from '../ink/stringWidth.js'; +import { getDefaultCharacters, type SpinnerMode } from './Spinner/index.js'; +import { SpinnerAnimationRow } from './Spinner/SpinnerAnimationRow.js'; +import { useSettings } from '../hooks/useSettings.js'; +import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js'; +import { isBackgroundTask } from '../tasks/types.js'; +import { getAllInProcessTeammateTasks } from '../tasks/InProcessTeammateTask/InProcessTeammateTask.js'; +import { getEffortSuffix } from '../utils/effort.js'; +import { getMainLoopModel } from '../utils/model/model.js'; +import { getViewedTeammateTask } from '../state/selectors.js'; +import { TEARDROP_ASTERISK } from '../constants/figures.js'; +import figures from 'figures'; +import { getCurrentTurnTokenBudget, getTurnOutputTokens } from '../bootstrap/state.js'; +import { TeammateSpinnerTree } from './Spinner/TeammateSpinnerTree.js'; +import { useAnimationFrame } from '../ink.js'; +import { getGlobalConfig } from '../utils/config.js'; +export type { SpinnerMode } from './Spinner/index.js'; +const DEFAULT_CHARACTERS = getDefaultCharacters(); +const SPINNER_FRAMES = [...DEFAULT_CHARACTERS, ...[...DEFAULT_CHARACTERS].reverse()]; +type Props = { + mode: SpinnerMode; + loadingStartTimeRef: React.RefObject; + totalPausedMsRef: React.RefObject; + pauseStartTimeRef: React.RefObject; + spinnerTip?: string; + responseLengthRef: React.RefObject; + overrideColor?: keyof Theme | null; + overrideShimmerColor?: keyof Theme | null; + overrideMessage?: string | null; + spinnerSuffix?: string | null; + verbose: boolean; + hasActiveTools?: boolean; + /** Leader's turn has completed (no active query). Used to suppress stall-red spinner when only teammates are running. */ + leaderIsIdle?: boolean; +}; + +// Thin wrapper: branches on isBriefOnly so the two variants have independent +// hook call chains. Without this split, toggling /brief mid-render would +// violate Rules of Hooks (the inner variant calls ~10 more hooks). +export function SpinnerWithVerb(props: Props): React.ReactNode { + const isBriefOnly = useAppState(s => s.isBriefOnly); + // REPL overrides isBriefOnly→false when viewing a teammate transcript + // (see isBriefOnly={viewedTeammateTask ? false : isBriefOnly}). That + // prop isn't threaded here, so replicate the gate from the store — + // teammate view needs the real spinner (which shows teammate status). + const viewingAgentTaskId = useAppState(s_0 => s_0.viewingAgentTaskId); + // Hoisted to mount-time — this component re-renders at animation framerate. + const briefEnvEnabled = feature('KAIROS') || feature('KAIROS_BRIEF') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_BRIEF), []) : false; + + // Runtime gate mirrors isBriefEnabled() but inlined — importing from + // BriefTool.ts would leak tool-name strings into external builds. Single + // spinner instance → hooks stay unconditional (two subs, negligible). + if ((feature('KAIROS') || feature('KAIROS_BRIEF')) && (getKairosActive() || getUserMsgOptIn() && (briefEnvEnabled || getFeatureValue_CACHED_MAY_BE_STALE('tengu_kairos_brief', false))) && isBriefOnly && !viewingAgentTaskId) { + return ; + } + return ; +} +function SpinnerWithVerbInner({ + mode, + loadingStartTimeRef, + totalPausedMsRef, + pauseStartTimeRef, + spinnerTip, + responseLengthRef, + overrideColor, + overrideShimmerColor, + overrideMessage, + spinnerSuffix, + verbose, + hasActiveTools = false, + leaderIsIdle = false +}: Props): React.ReactNode { + const settings = useSettings(); + const reducedMotion = settings.prefersReducedMotion ?? false; + + // NOTE: useAnimationFrame(50) lives in SpinnerAnimationRow, not here. + // This component only re-renders when props or app state change — + // it is no longer on the 50ms clock. All `time`-derived values + // (frame, glimmer, stalled intensity, token counter, thinking shimmer, + // elapsed-time timer) are computed inside the child. + + const tasks = useAppState(s => s.tasks); + const viewingAgentTaskId = useAppState(s_0 => s_0.viewingAgentTaskId); + const expandedView = useAppState(s_1 => s_1.expandedView); + const showExpandedTodos = expandedView === 'tasks'; + const showSpinnerTree = expandedView === 'teammates'; + const selectedIPAgentIndex = useAppState(s_2 => s_2.selectedIPAgentIndex); + const viewSelectionMode = useAppState(s_3 => s_3.viewSelectionMode); + // Get foregrounded teammate (if viewing a teammate's transcript) + const foregroundedTeammate = viewingAgentTaskId ? getViewedTeammateTask({ + viewingAgentTaskId, + tasks + }) : undefined; + const { + columns + } = useTerminalSize(); + const tasksV2 = useTasksV2(); + + // Track thinking status: 'thinking' | number (duration in ms) | null + // Shows each state for minimum 2s to avoid UI jank + const [thinkingStatus, setThinkingStatus] = useState<'thinking' | number | null>(null); + const thinkingStartRef = useRef(null); + useEffect(() => { + let showDurationTimer: ReturnType | null = null; + let clearStatusTimer: ReturnType | null = null; + if (mode === 'thinking') { + // Started thinking + if (thinkingStartRef.current === null) { + thinkingStartRef.current = Date.now(); + setThinkingStatus('thinking'); + } + } else if (thinkingStartRef.current !== null) { + // Stopped thinking - calculate duration and ensure 2s minimum display + const duration = Date.now() - thinkingStartRef.current; + const elapsed = Date.now() - thinkingStartRef.current; + const remainingThinkingTime = Math.max(0, 2000 - elapsed); + thinkingStartRef.current = null; + + // Show "thinking..." for remaining time if < 2s elapsed, then show duration + const showDuration = (): void => { + setThinkingStatus(duration); + // Clear after 2s + clearStatusTimer = setTimeout(setThinkingStatus, 2000, null); + }; + if (remainingThinkingTime > 0) { + showDurationTimer = setTimeout(showDuration, remainingThinkingTime); + } else { + showDuration(); + } + } + return () => { + if (showDurationTimer) clearTimeout(showDurationTimer); + if (clearStatusTimer) clearTimeout(clearStatusTimer); + }; + }, [mode]); + + // Find the current in-progress task and next pending task + const currentTodo = tasksV2?.find(task => task.status !== 'pending' && task.status !== 'completed'); + const nextTask = findNextPendingTask(tasksV2); + + // Use useState with initializer to pick a random verb once on mount + const [randomVerb] = useState(() => sample(getSpinnerVerbs())); + + // Leader's own verb (always the leader's, regardless of who is foregrounded) + const leaderVerb = overrideMessage ?? currentTodo?.activeForm ?? currentTodo?.subject ?? randomVerb; + const effectiveVerb = foregroundedTeammate && !foregroundedTeammate.isIdle ? foregroundedTeammate.spinnerVerb ?? randomVerb : leaderVerb; + const message = effectiveVerb + '…'; + + // Track CLI activity when spinner is active + useEffect(() => { + const operationId = 'spinner-' + mode; + activityManager.startCLIActivity(operationId); + return () => { + activityManager.endCLIActivity(operationId); + }; + }, [mode]); + const effortValue = useAppState(s_4 => s_4.effortValue); + const effortSuffix = getEffortSuffix(getMainLoopModel(), effortValue); + + // Check if any running in-process teammates exist (needed for both modes) + const runningTeammates = getAllInProcessTeammateTasks(tasks).filter(t => t.status === 'running'); + const hasRunningTeammates = runningTeammates.length > 0; + const allIdle = hasRunningTeammates && runningTeammates.every(t_0 => t_0.isIdle); + + // Gather aggregate token stats from all running swarm teammates + // In spinner-tree mode, skip aggregation (teammates have their own lines in the tree) + let teammateTokens = 0; + if (!showSpinnerTree) { + for (const task_0 of Object.values(tasks)) { + if (isInProcessTeammateTask(task_0) && task_0.status === 'running') { + if (task_0.progress?.tokenCount) { + teammateTokens += task_0.progress.tokenCount; + } + } + } + } + + // Stale read of the refs for showBtwTip below — we're off the 50ms clock + // so this only updates when props/app state change, which is sufficient for + // a coarse 30s threshold. + const elapsedSnapshot = pauseStartTimeRef.current !== null ? pauseStartTimeRef.current - loadingStartTimeRef.current - totalPausedMsRef.current : Date.now() - loadingStartTimeRef.current - totalPausedMsRef.current; + + // Leader token count for TeammateSpinnerTree — read raw (non-animated) from + // the ref. The tree is only shown when teammates are running; teammate + // progress updates to s.tasks trigger re-renders that keep this fresh. + const leaderTokenCount = Math.round(responseLengthRef.current / 4); + const defaultColor: keyof Theme = 'claude'; + const defaultShimmerColor = 'claudeShimmer'; + const messageColor = overrideColor ?? defaultColor; + const shimmerColor = overrideShimmerColor ?? defaultShimmerColor; + + // Compute TTFT string here (off the 50ms animation clock) and pass to + // SpinnerAnimationRow so it folds into the `(thought for Ns · ...)` status + // line instead of taking a separate row. apiMetricsRef is a ref so this + // doesn't trigger re-renders; we pick up updates on the parent's ~25x/turn + // re-render cadence, same as the old ApiMetricsLine did. + let ttftText: string | null = null; + if ("external" === 'ant' && apiMetricsRef?.current && apiMetricsRef.current.length > 0) { + ttftText = computeTtftText(apiMetricsRef.current); + } + + // When leader is idle but teammates are running (and we're viewing the leader), + // show a static dim idle display instead of the animated spinner — otherwise + // useStalledAnimation detects no new tokens after 3s and turns the spinner red. + if (leaderIsIdle && hasRunningTeammates && !foregroundedTeammate) { + return + + + {TEARDROP_ASTERISK} Idle + {!allIdle && ' · teammates running'} + + + {showSpinnerTree && } + ; + } + + // When viewing an idle teammate, show static idle display instead of animated spinner + if (foregroundedTeammate?.isIdle) { + const idleText = allIdle ? `${TEARDROP_ASTERISK} Worked for ${formatDuration(Date.now() - foregroundedTeammate.startTime)}` : `${TEARDROP_ASTERISK} Idle`; + return + + {idleText} + + {showSpinnerTree && hasRunningTeammates && } + ; + } + + // Time-based tip overrides: coarse thresholds so a stale ref read (we're + // off the 50ms clock) is fine. Other triggers (mode change, setMessages) + // cause re-renders that refresh this in practice. + let contextTipsActive = false; + const tipsEnabled = settings.spinnerTipsEnabled !== false; + const showClearTip = tipsEnabled && elapsedSnapshot > 1_800_000; + const showBtwTip = tipsEnabled && elapsedSnapshot > 30_000 && !getGlobalConfig().btwUseCount; + const effectiveTip = contextTipsActive ? undefined : showClearTip && !nextTask ? 'Use /clear to start fresh when switching topics and free up context' : showBtwTip && !nextTask ? "Use /btw to ask a quick side question without interrupting Claude's current work" : spinnerTip; + + // Budget text (ant-only) — shown above the tip line + let budgetText: string | null = null; + if (feature('TOKEN_BUDGET')) { + const budget = getCurrentTurnTokenBudget(); + if (budget !== null && budget > 0) { + const tokens = getTurnOutputTokens(); + if (tokens >= budget) { + budgetText = `Target: ${formatNumber(tokens)} used (${formatNumber(budget)} min ${figures.tick})`; + } else { + const pct = Math.round(tokens / budget * 100); + const remaining = budget - tokens; + const rate = elapsedSnapshot > 5000 && tokens >= 2000 ? tokens / elapsedSnapshot : 0; + const eta = rate > 0 ? ` \u00B7 ~${formatDuration(remaining / rate, { + mostSignificantOnly: true + })}` : ''; + budgetText = `Target: ${formatNumber(tokens)} / ${formatNumber(budget)} (${pct}%)${eta}`; + } + } + } + return + + {showSpinnerTree && hasRunningTeammates ? : showExpandedTodos && tasksV2 && tasksV2.length > 0 ? + + + + : nextTask || effectiveTip || budgetText ? + // IMPORTANT: we need this width="100%" to avoid an Ink bug where the + // tip gets duplicated over and over while the spinner is running if + // the terminal is very small. TODO: fix this in Ink. + + {budgetText && + {budgetText} + } + {(nextTask || effectiveTip) && + + {nextTask ? `Next: ${nextTask.subject}` : `Tip: ${effectiveTip}`} + + } + : null} + ; +} + +// Brief/assistant mode spinner: single status line. PromptInput drops its +// own marginTop when isBriefOnly is active, so this component owns the +// 2-row footprint between messages and input. Footprint is [blank, content] +// — one blank row above (breathing room under the messages list), spinner +// flush against the input bar. PromptInput's absolute-positioned +// Notifications overlay compensates with marginTop=-2 in brief mode +// (PromptInput.tsx:~2928) so it floats into the blank row above the +// spinner, not over the spinner content. Paired with BriefIdleStatus which +// keeps the same footprint when idle. +type BriefSpinnerProps = { + mode: SpinnerMode; + overrideMessage?: string | null; +}; +function BriefSpinner(t0) { + const $ = _c(31); + const { + mode, + overrideMessage + } = t0; + const settings = useSettings(); + const reducedMotion = settings.prefersReducedMotion ?? false; + const [randomVerb] = useState(_temp4); + const verb = overrideMessage ?? randomVerb; + const connStatus = useAppState(_temp5); + let t1; + let t2; + if ($[0] !== mode) { + t1 = () => { + const operationId = "spinner-" + mode; + activityManager.startCLIActivity(operationId); + return () => { + activityManager.endCLIActivity(operationId); + }; + }; + t2 = [mode]; + $[0] = mode; + $[1] = t1; + $[2] = t2; + } else { + t1 = $[1]; + t2 = $[2]; + } + useEffect(t1, t2); + const [, time] = useAnimationFrame(reducedMotion ? null : 120); + const runningCount = useAppState(_temp6); + const showConnWarning = connStatus === "reconnecting" || connStatus === "disconnected"; + const connText = connStatus === "reconnecting" ? "Reconnecting" : "Disconnected"; + const dotFrame = Math.floor(time / 300) % 3; + let t3; + if ($[3] !== dotFrame || $[4] !== reducedMotion) { + t3 = reducedMotion ? "\u2026 " : ".".repeat(dotFrame + 1).padEnd(3); + $[3] = dotFrame; + $[4] = reducedMotion; + $[5] = t3; + } else { + t3 = $[5]; + } + const dots = t3; + let t4; + if ($[6] !== verb) { + t4 = stringWidth(verb); + $[6] = verb; + $[7] = t4; + } else { + t4 = $[7]; + } + const verbWidth = t4; + let t5; + if ($[8] !== reducedMotion || $[9] !== showConnWarning || $[10] !== time || $[11] !== verb || $[12] !== verbWidth) { + const glimmerIndex = reducedMotion || showConnWarning ? -100 : computeGlimmerIndex(Math.floor(time / SHIMMER_INTERVAL_MS), verbWidth); + t5 = computeShimmerSegments(verb, glimmerIndex); + $[8] = reducedMotion; + $[9] = showConnWarning; + $[10] = time; + $[11] = verb; + $[12] = verbWidth; + $[13] = t5; + } else { + t5 = $[13]; + } + const { + before, + shimmer, + after + } = t5; + const { + columns + } = useTerminalSize(); + const rightText = runningCount > 0 ? `${runningCount} in background` : ""; + let t6; + if ($[14] !== connText || $[15] !== showConnWarning || $[16] !== verbWidth) { + t6 = showConnWarning ? stringWidth(connText) : verbWidth; + $[14] = connText; + $[15] = showConnWarning; + $[16] = verbWidth; + $[17] = t6; + } else { + t6 = $[17]; + } + const leftWidth = t6 + 3; + const pad = Math.max(1, columns - 2 - leftWidth - stringWidth(rightText)); + let t7; + if ($[18] !== after || $[19] !== before || $[20] !== connText || $[21] !== dots || $[22] !== shimmer || $[23] !== showConnWarning) { + t7 = showConnWarning ? {connText + dots} : <>{before ? {before} : null}{shimmer ? {shimmer} : null}{after ? {after} : null}{dots}; + $[18] = after; + $[19] = before; + $[20] = connText; + $[21] = dots; + $[22] = shimmer; + $[23] = showConnWarning; + $[24] = t7; + } else { + t7 = $[24]; + } + let t8; + if ($[25] !== pad || $[26] !== rightText) { + t8 = rightText ? <>{" ".repeat(pad)}{rightText} : null; + $[25] = pad; + $[26] = rightText; + $[27] = t8; + } else { + t8 = $[27]; + } + let t9; + if ($[28] !== t7 || $[29] !== t8) { + t9 = {t7}{t8}; + $[28] = t7; + $[29] = t8; + $[30] = t9; + } else { + t9 = $[30]; + } + return t9; +} + +// Idle placeholder for brief mode. Same 2-row [blank, content] footprint +// as BriefSpinner so the input bar never jumps when toggling between +// working/idle/disconnected. See BriefSpinner's comment for the +// Notifications overlay coupling. +function _temp6(s_0) { + return count(Object.values(s_0.tasks), isBackgroundTask) + s_0.remoteBackgroundTaskCount; +} +function _temp5(s) { + return s.remoteConnectionStatus; +} +function _temp4() { + return sample(getSpinnerVerbs()) ?? "Working"; +} +export function BriefIdleStatus() { + const $ = _c(9); + const connStatus = useAppState(_temp7); + const runningCount = useAppState(_temp8); + const { + columns + } = useTerminalSize(); + const showConnWarning = connStatus === "reconnecting" || connStatus === "disconnected"; + const connText = connStatus === "reconnecting" ? "Reconnecting\u2026" : "Disconnected"; + const leftText = showConnWarning ? connText : ""; + const rightText = runningCount > 0 ? `${runningCount} in background` : ""; + if (!leftText && !rightText) { + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = ; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; + } + const pad = Math.max(1, columns - 2 - stringWidth(leftText) - stringWidth(rightText)); + let t0; + if ($[1] !== leftText) { + t0 = leftText ? {leftText} : null; + $[1] = leftText; + $[2] = t0; + } else { + t0 = $[2]; + } + let t1; + if ($[3] !== pad || $[4] !== rightText) { + t1 = rightText ? <>{" ".repeat(pad)}{rightText} : null; + $[3] = pad; + $[4] = rightText; + $[5] = t1; + } else { + t1 = $[5]; + } + let t2; + if ($[6] !== t0 || $[7] !== t1) { + t2 = {t0}{t1}; + $[6] = t0; + $[7] = t1; + $[8] = t2; + } else { + t2 = $[8]; + } + return t2; +} +function _temp8(s_0) { + return count(Object.values(s_0.tasks), isBackgroundTask) + s_0.remoteBackgroundTaskCount; +} +function _temp7(s) { + return s.remoteConnectionStatus; +} +export function Spinner() { + const $ = _c(8); + const settings = useSettings(); + const reducedMotion = settings.prefersReducedMotion ?? false; + const [ref, time] = useAnimationFrame(reducedMotion ? null : 120); + if (reducedMotion) { + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = ; + $[0] = t0; + } else { + t0 = $[0]; + } + let t1; + if ($[1] !== ref) { + t1 = {t0}; + $[1] = ref; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; + } + const frame = Math.floor(time / 120) % SPINNER_FRAMES.length; + const t0 = SPINNER_FRAMES[frame]; + let t1; + if ($[3] !== t0) { + t1 = {t0}; + $[3] = t0; + $[4] = t1; + } else { + t1 = $[4]; + } + let t2; + if ($[5] !== ref || $[6] !== t1) { + t2 = {t1}; + $[5] = ref; + $[6] = t1; + $[7] = t2; + } else { + t2 = $[7]; + } + return t2; +} +function findNextPendingTask(tasks: Task[] | undefined): Task | undefined { + if (!tasks) { + return undefined; + } + const pendingTasks = tasks.filter(t => t.status === 'pending'); + if (pendingTasks.length === 0) { + return undefined; + } + const unresolvedIds = new Set(tasks.filter(t => t.status !== 'completed').map(t => t.id)); + return pendingTasks.find(t => !t.blockedBy.some(id => unresolvedIds.has(id))) ?? pendingTasks[0]; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJCb3giLCJUZXh0IiwiUmVhY3QiLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwidXNlUmVmIiwidXNlU3RhdGUiLCJjb21wdXRlR2xpbW1lckluZGV4IiwiY29tcHV0ZVNoaW1tZXJTZWdtZW50cyIsIlNISU1NRVJfSU5URVJWQUxfTVMiLCJmZWF0dXJlIiwiZ2V0S2Fpcm9zQWN0aXZlIiwiZ2V0VXNlck1zZ09wdEluIiwiZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUiLCJpc0VudlRydXRoeSIsImNvdW50Iiwic2FtcGxlIiwiZm9ybWF0RHVyYXRpb24iLCJmb3JtYXROdW1iZXIiLCJmb3JtYXRTZWNvbmRzU2hvcnQiLCJUaGVtZSIsImFjdGl2aXR5TWFuYWdlciIsImdldFNwaW5uZXJWZXJicyIsIk1lc3NhZ2VSZXNwb25zZSIsIlRhc2tMaXN0VjIiLCJ1c2VUYXNrc1YyIiwiVGFzayIsInVzZUFwcFN0YXRlIiwidXNlVGVybWluYWxTaXplIiwic3RyaW5nV2lkdGgiLCJnZXREZWZhdWx0Q2hhcmFjdGVycyIsIlNwaW5uZXJNb2RlIiwiU3Bpbm5lckFuaW1hdGlvblJvdyIsInVzZVNldHRpbmdzIiwiaXNJblByb2Nlc3NUZWFtbWF0ZVRhc2siLCJpc0JhY2tncm91bmRUYXNrIiwiZ2V0QWxsSW5Qcm9jZXNzVGVhbW1hdGVUYXNrcyIsImdldEVmZm9ydFN1ZmZpeCIsImdldE1haW5Mb29wTW9kZWwiLCJnZXRWaWV3ZWRUZWFtbWF0ZVRhc2siLCJURUFSRFJPUF9BU1RFUklTSyIsImZpZ3VyZXMiLCJnZXRDdXJyZW50VHVyblRva2VuQnVkZ2V0IiwiZ2V0VHVybk91dHB1dFRva2VucyIsIlRlYW1tYXRlU3Bpbm5lclRyZWUiLCJ1c2VBbmltYXRpb25GcmFtZSIsImdldEdsb2JhbENvbmZpZyIsIkRFRkFVTFRfQ0hBUkFDVEVSUyIsIlNQSU5ORVJfRlJBTUVTIiwicmV2ZXJzZSIsIlByb3BzIiwibW9kZSIsImxvYWRpbmdTdGFydFRpbWVSZWYiLCJSZWZPYmplY3QiLCJ0b3RhbFBhdXNlZE1zUmVmIiwicGF1c2VTdGFydFRpbWVSZWYiLCJzcGlubmVyVGlwIiwicmVzcG9uc2VMZW5ndGhSZWYiLCJvdmVycmlkZUNvbG9yIiwib3ZlcnJpZGVTaGltbWVyQ29sb3IiLCJvdmVycmlkZU1lc3NhZ2UiLCJzcGlubmVyU3VmZml4IiwidmVyYm9zZSIsImhhc0FjdGl2ZVRvb2xzIiwibGVhZGVySXNJZGxlIiwiU3Bpbm5lcldpdGhWZXJiIiwicHJvcHMiLCJSZWFjdE5vZGUiLCJpc0JyaWVmT25seSIsInMiLCJ2aWV3aW5nQWdlbnRUYXNrSWQiLCJicmllZkVudkVuYWJsZWQiLCJwcm9jZXNzIiwiZW52IiwiQ0xBVURFX0NPREVfQlJJRUYiLCJTcGlubmVyV2l0aFZlcmJJbm5lciIsInNldHRpbmdzIiwicmVkdWNlZE1vdGlvbiIsInByZWZlcnNSZWR1Y2VkTW90aW9uIiwidGFza3MiLCJleHBhbmRlZFZpZXciLCJzaG93RXhwYW5kZWRUb2RvcyIsInNob3dTcGlubmVyVHJlZSIsInNlbGVjdGVkSVBBZ2VudEluZGV4Iiwidmlld1NlbGVjdGlvbk1vZGUiLCJmb3JlZ3JvdW5kZWRUZWFtbWF0ZSIsInVuZGVmaW5lZCIsImNvbHVtbnMiLCJ0YXNrc1YyIiwidGhpbmtpbmdTdGF0dXMiLCJzZXRUaGlua2luZ1N0YXR1cyIsInRoaW5raW5nU3RhcnRSZWYiLCJzaG93RHVyYXRpb25UaW1lciIsIlJldHVyblR5cGUiLCJzZXRUaW1lb3V0IiwiY2xlYXJTdGF0dXNUaW1lciIsImN1cnJlbnQiLCJEYXRlIiwibm93IiwiZHVyYXRpb24iLCJlbGFwc2VkIiwicmVtYWluaW5nVGhpbmtpbmdUaW1lIiwiTWF0aCIsIm1heCIsInNob3dEdXJhdGlvbiIsImNsZWFyVGltZW91dCIsImN1cnJlbnRUb2RvIiwiZmluZCIsInRhc2siLCJzdGF0dXMiLCJuZXh0VGFzayIsImZpbmROZXh0UGVuZGluZ1Rhc2siLCJyYW5kb21WZXJiIiwibGVhZGVyVmVyYiIsImFjdGl2ZUZvcm0iLCJzdWJqZWN0IiwiZWZmZWN0aXZlVmVyYiIsImlzSWRsZSIsInNwaW5uZXJWZXJiIiwibWVzc2FnZSIsIm9wZXJhdGlvbklkIiwic3RhcnRDTElBY3Rpdml0eSIsImVuZENMSUFjdGl2aXR5IiwiZWZmb3J0VmFsdWUiLCJlZmZvcnRTdWZmaXgiLCJydW5uaW5nVGVhbW1hdGVzIiwiZmlsdGVyIiwidCIsImhhc1J1bm5pbmdUZWFtbWF0ZXMiLCJsZW5ndGgiLCJhbGxJZGxlIiwiZXZlcnkiLCJ0ZWFtbWF0ZVRva2VucyIsIk9iamVjdCIsInZhbHVlcyIsInByb2dyZXNzIiwidG9rZW5Db3VudCIsImVsYXBzZWRTbmFwc2hvdCIsImxlYWRlclRva2VuQ291bnQiLCJyb3VuZCIsImRlZmF1bHRDb2xvciIsImRlZmF1bHRTaGltbWVyQ29sb3IiLCJtZXNzYWdlQ29sb3IiLCJzaGltbWVyQ29sb3IiLCJ0dGZ0VGV4dCIsImFwaU1ldHJpY3NSZWYiLCJjb21wdXRlVHRmdFRleHQiLCJpZGxlVGV4dCIsInN0YXJ0VGltZSIsImNvbnRleHRUaXBzQWN0aXZlIiwidGlwc0VuYWJsZWQiLCJzcGlubmVyVGlwc0VuYWJsZWQiLCJzaG93Q2xlYXJUaXAiLCJzaG93QnR3VGlwIiwiYnR3VXNlQ291bnQiLCJlZmZlY3RpdmVUaXAiLCJidWRnZXRUZXh0IiwiYnVkZ2V0IiwidG9rZW5zIiwidGljayIsInBjdCIsInJlbWFpbmluZyIsInJhdGUiLCJldGEiLCJtb3N0U2lnbmlmaWNhbnRPbmx5IiwiQnJpZWZTcGlubmVyUHJvcHMiLCJCcmllZlNwaW5uZXIiLCJ0MCIsIiQiLCJfYyIsIl90ZW1wNCIsInZlcmIiLCJjb25uU3RhdHVzIiwiX3RlbXA1IiwidDEiLCJ0MiIsInRpbWUiLCJydW5uaW5nQ291bnQiLCJfdGVtcDYiLCJzaG93Q29ubldhcm5pbmciLCJjb25uVGV4dCIsImRvdEZyYW1lIiwiZmxvb3IiLCJ0MyIsInJlcGVhdCIsInBhZEVuZCIsImRvdHMiLCJ0NCIsInZlcmJXaWR0aCIsInQ1IiwiZ2xpbW1lckluZGV4IiwiYmVmb3JlIiwic2hpbW1lciIsImFmdGVyIiwicmlnaHRUZXh0IiwidDYiLCJsZWZ0V2lkdGgiLCJwYWQiLCJ0NyIsInQ4IiwidDkiLCJzXzAiLCJyZW1vdGVCYWNrZ3JvdW5kVGFza0NvdW50IiwicmVtb3RlQ29ubmVjdGlvblN0YXR1cyIsIkJyaWVmSWRsZVN0YXR1cyIsIl90ZW1wNyIsIl90ZW1wOCIsImxlZnRUZXh0IiwiU3ltYm9sIiwiZm9yIiwiU3Bpbm5lciIsInJlZiIsImZyYW1lIiwicGVuZGluZ1Rhc2tzIiwidW5yZXNvbHZlZElkcyIsIlNldCIsIm1hcCIsImlkIiwiYmxvY2tlZEJ5Iiwic29tZSIsImhhcyJdLCJzb3VyY2VzIjpbIlNwaW5uZXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8vIGJpb21lLWlnbm9yZS1hbGwgYXNzaXN0L3NvdXJjZS9vcmdhbml6ZUltcG9ydHM6IEFOVC1PTkxZIGltcG9ydCBtYXJrZXJzIG11c3Qgbm90IGJlIHJlb3JkZXJlZFxuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZU1lbW8sIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7XG4gIGNvbXB1dGVHbGltbWVySW5kZXgsXG4gIGNvbXB1dGVTaGltbWVyU2VnbWVudHMsXG4gIFNISU1NRVJfSU5URVJWQUxfTVMsXG59IGZyb20gJy4uL2JyaWRnZS9icmlkZ2VTdGF0dXNVdGlsLmpzJ1xuaW1wb3J0IHsgZmVhdHVyZSB9IGZyb20gJ2J1bjpidW5kbGUnXG5pbXBvcnQgeyBnZXRLYWlyb3NBY3RpdmUsIGdldFVzZXJNc2dPcHRJbiB9IGZyb20gJy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7IGdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFIH0gZnJvbSAnLi4vc2VydmljZXMvYW5hbHl0aWNzL2dyb3d0aGJvb2suanMnXG5pbXBvcnQgeyBpc0VudlRydXRoeSB9IGZyb20gJy4uL3V0aWxzL2VudlV0aWxzLmpzJ1xuaW1wb3J0IHsgY291bnQgfSBmcm9tICcuLi91dGlscy9hcnJheS5qcydcbmltcG9ydCBzYW1wbGUgZnJvbSAnbG9kYXNoLWVzL3NhbXBsZS5qcydcbmltcG9ydCB7XG4gIGZvcm1hdER1cmF0aW9uLFxuICBmb3JtYXROdW1iZXIsXG4gIGZvcm1hdFNlY29uZHNTaG9ydCxcbn0gZnJvbSAnLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJ3NyYy91dGlscy90aGVtZS5qcydcbmltcG9ydCB7IGFjdGl2aXR5TWFuYWdlciB9IGZyb20gJy4uL3V0aWxzL2FjdGl2aXR5TWFuYWdlci5qcydcbmltcG9ydCB7IGdldFNwaW5uZXJWZXJicyB9IGZyb20gJy4uL2NvbnN0YW50cy9zcGlubmVyVmVyYnMuanMnXG5pbXBvcnQgeyBNZXNzYWdlUmVzcG9uc2UgfSBmcm9tICcuL01lc3NhZ2VSZXNwb25zZS5qcydcbmltcG9ydCB7IFRhc2tMaXN0VjIgfSBmcm9tICcuL1Rhc2tMaXN0VjIuanMnXG5pbXBvcnQgeyB1c2VUYXNrc1YyIH0gZnJvbSAnLi4vaG9va3MvdXNlVGFza3NWMi5qcydcbmltcG9ydCB0eXBlIHsgVGFzayB9IGZyb20gJy4uL3V0aWxzL3Rhc2tzLmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUgfSBmcm9tICcuLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IHVzZVRlcm1pbmFsU2l6ZSB9IGZyb20gJy4uL2hvb2tzL3VzZVRlcm1pbmFsU2l6ZS5qcydcbmltcG9ydCB7IHN0cmluZ1dpZHRoIH0gZnJvbSAnLi4vaW5rL3N0cmluZ1dpZHRoLmpzJ1xuaW1wb3J0IHsgZ2V0RGVmYXVsdENoYXJhY3RlcnMsIHR5cGUgU3Bpbm5lck1vZGUgfSBmcm9tICcuL1NwaW5uZXIvaW5kZXguanMnXG5pbXBvcnQgeyBTcGlubmVyQW5pbWF0aW9uUm93IH0gZnJvbSAnLi9TcGlubmVyL1NwaW5uZXJBbmltYXRpb25Sb3cuanMnXG5pbXBvcnQgeyB1c2VTZXR0aW5ncyB9IGZyb20gJy4uL2hvb2tzL3VzZVNldHRpbmdzLmpzJ1xuaW1wb3J0IHsgaXNJblByb2Nlc3NUZWFtbWF0ZVRhc2sgfSBmcm9tICcuLi90YXNrcy9JblByb2Nlc3NUZWFtbWF0ZVRhc2svdHlwZXMuanMnXG5pbXBvcnQgeyBpc0JhY2tncm91bmRUYXNrIH0gZnJvbSAnLi4vdGFza3MvdHlwZXMuanMnXG5pbXBvcnQgeyBnZXRBbGxJblByb2Nlc3NUZWFtbWF0ZVRhc2tzIH0gZnJvbSAnLi4vdGFza3MvSW5Qcm9jZXNzVGVhbW1hdGVUYXNrL0luUHJvY2Vzc1RlYW1tYXRlVGFzay5qcydcbmltcG9ydCB7IGdldEVmZm9ydFN1ZmZpeCB9IGZyb20gJy4uL3V0aWxzL2VmZm9ydC5qcydcbmltcG9ydCB7IGdldE1haW5Mb29wTW9kZWwgfSBmcm9tICcuLi91dGlscy9tb2RlbC9tb2RlbC5qcydcbmltcG9ydCB7IGdldFZpZXdlZFRlYW1tYXRlVGFzayB9IGZyb20gJy4uL3N0YXRlL3NlbGVjdG9ycy5qcydcbmltcG9ydCB7IFRFQVJEUk9QX0FTVEVSSVNLIH0gZnJvbSAnLi4vY29uc3RhbnRzL2ZpZ3VyZXMuanMnXG5pbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0IHtcbiAgZ2V0Q3VycmVudFR1cm5Ub2tlbkJ1ZGdldCxcbiAgZ2V0VHVybk91dHB1dFRva2Vucyxcbn0gZnJvbSAnLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuXG5pbXBvcnQgeyBUZWFtbWF0ZVNwaW5uZXJUcmVlIH0gZnJvbSAnLi9TcGlubmVyL1RlYW1tYXRlU3Bpbm5lclRyZWUuanMnXG5pbXBvcnQgeyB1c2VBbmltYXRpb25GcmFtZSB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGdldEdsb2JhbENvbmZpZyB9IGZyb20gJy4uL3V0aWxzL2NvbmZpZy5qcydcbmV4cG9ydCB0eXBlIHsgU3Bpbm5lck1vZGUgfSBmcm9tICcuL1NwaW5uZXIvaW5kZXguanMnXG5cbmNvbnN0IERFRkFVTFRfQ0hBUkFDVEVSUyA9IGdldERlZmF1bHRDaGFyYWN0ZXJzKClcblxuY29uc3QgU1BJTk5FUl9GUkFNRVMgPSBbXG4gIC4uLkRFRkFVTFRfQ0hBUkFDVEVSUyxcbiAgLi4uWy4uLkRFRkFVTFRfQ0hBUkFDVEVSU10ucmV2ZXJzZSgpLFxuXVxuXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG1vZGU6IFNwaW5uZXJNb2RlXG4gIGxvYWRpbmdTdGFydFRpbWVSZWY6IFJlYWN0LlJlZk9iamVjdDxudW1iZXI+XG4gIHRvdGFsUGF1c2VkTXNSZWY6IFJlYWN0LlJlZk9iamVjdDxudW1iZXI+XG4gIHBhdXNlU3RhcnRUaW1lUmVmOiBSZWFjdC5SZWZPYmplY3Q8bnVtYmVyIHwgbnVsbD5cbiAgc3Bpbm5lclRpcD86IHN0cmluZ1xuICByZXNwb25zZUxlbmd0aFJlZjogUmVhY3QuUmVmT2JqZWN0PG51bWJlcj5cbiAgb3ZlcnJpZGVDb2xvcj86IGtleW9mIFRoZW1lIHwgbnVsbFxuICBvdmVycmlkZVNoaW1tZXJDb2xvcj86IGtleW9mIFRoZW1lIHwgbnVsbFxuICBvdmVycmlkZU1lc3NhZ2U/OiBzdHJpbmcgfCBudWxsXG4gIHNwaW5uZXJTdWZmaXg/OiBzdHJpbmcgfCBudWxsXG4gIHZlcmJvc2U6IGJvb2xlYW5cbiAgaGFzQWN0aXZlVG9vbHM/OiBib29sZWFuXG4gIC8qKiBMZWFkZXIncyB0dXJuIGhhcyBjb21wbGV0ZWQgKG5vIGFjdGl2ZSBxdWVyeSkuIFVzZWQgdG8gc3VwcHJlc3Mgc3RhbGwtcmVkIHNwaW5uZXIgd2hlbiBvbmx5IHRlYW1tYXRlcyBhcmUgcnVubmluZy4gKi9cbiAgbGVhZGVySXNJZGxlPzogYm9vbGVhblxufVxuXG4vLyBUaGluIHdyYXBwZXI6IGJyYW5jaGVzIG9uIGlzQnJpZWZPbmx5IHNvIHRoZSB0d28gdmFyaWFudHMgaGF2ZSBpbmRlcGVuZGVudFxuLy8gaG9vayBjYWxsIGNoYWlucy4gV2l0aG91dCB0aGlzIHNwbGl0LCB0b2dnbGluZyAvYnJpZWYgbWlkLXJlbmRlciB3b3VsZFxuLy8gdmlvbGF0ZSBSdWxlcyBvZiBIb29rcyAodGhlIGlubmVyIHZhcmlhbnQgY2FsbHMgfjEwIG1vcmUgaG9va3MpLlxuZXhwb3J0IGZ1bmN0aW9uIFNwaW5uZXJXaXRoVmVyYihwcm9wczogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBpc0JyaWVmT25seSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5pc0JyaWVmT25seSlcbiAgLy8gUkVQTCBvdmVycmlkZXMgaXNCcmllZk9ubHnihpJmYWxzZSB3aGVuIHZpZXdpbmcgYSB0ZWFtbWF0ZSB0cmFuc2NyaXB0XG4gIC8vIChzZWUgaXNCcmllZk9ubHk9e3ZpZXdlZFRlYW1tYXRlVGFzayA/IGZhbHNlIDogaXNCcmllZk9ubHl9KS4gVGhhdFxuICAvLyBwcm9wIGlzbid0IHRocmVhZGVkIGhlcmUsIHNvIHJlcGxpY2F0ZSB0aGUgZ2F0ZSBmcm9tIHRoZSBzdG9yZSDigJRcbiAgLy8gdGVhbW1hdGUgdmlldyBuZWVkcyB0aGUgcmVhbCBzcGlubmVyICh3aGljaCBzaG93cyB0ZWFtbWF0ZSBzdGF0dXMpLlxuICBjb25zdCB2aWV3aW5nQWdlbnRUYXNrSWQgPSB1c2VBcHBTdGF0ZShzID0+IHMudmlld2luZ0FnZW50VGFza0lkKVxuICAvLyBIb2lzdGVkIHRvIG1vdW50LXRpbWUg4oCUIHRoaXMgY29tcG9uZW50IHJlLXJlbmRlcnMgYXQgYW5pbWF0aW9uIGZyYW1lcmF0ZS5cbiAgY29uc3QgYnJpZWZFbnZFbmFibGVkID1cbiAgICBmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKVxuICAgICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICAgIHVzZU1lbW8oKCkgPT4gaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfQlJJRUYpLCBbXSlcbiAgICAgIDogZmFsc2VcblxuICAvLyBSdW50aW1lIGdhdGUgbWlycm9ycyBpc0JyaWVmRW5hYmxlZCgpIGJ1dCBpbmxpbmVkIOKAlCBpbXBvcnRpbmcgZnJvbVxuICAvLyBCcmllZlRvb2wudHMgd291bGQgbGVhayB0b29sLW5hbWUgc3RyaW5ncyBpbnRvIGV4dGVybmFsIGJ1aWxkcy4gU2luZ2xlXG4gIC8vIHNwaW5uZXIgaW5zdGFuY2Ug4oaSIGhvb2tzIHN0YXkgdW5jb25kaXRpb25hbCAodHdvIHN1YnMsIG5lZ2xpZ2libGUpLlxuICBpZiAoXG4gICAgKGZlYXR1cmUoJ0tBSVJPUycpIHx8IGZlYXR1cmUoJ0tBSVJPU19CUklFRicpKSAmJlxuICAgIChnZXRLYWlyb3NBY3RpdmUoKSB8fFxuICAgICAgKGdldFVzZXJNc2dPcHRJbigpICYmXG4gICAgICAgIChicmllZkVudkVuYWJsZWQgfHxcbiAgICAgICAgICBnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSgndGVuZ3Vfa2Fpcm9zX2JyaWVmJywgZmFsc2UpKSkpICYmXG4gICAgaXNCcmllZk9ubHkgJiZcbiAgICAhdmlld2luZ0FnZW50VGFza0lkXG4gICkge1xuICAgIHJldHVybiAoXG4gICAgICA8QnJpZWZTcGlubmVyIG1vZGU9e3Byb3BzLm1vZGV9IG92ZXJyaWRlTWVzc2FnZT17cHJvcHMub3ZlcnJpZGVNZXNzYWdlfSAvPlxuICAgIClcbiAgfVxuXG4gIHJldHVybiA8U3Bpbm5lcldpdGhWZXJiSW5uZXIgey4uLnByb3BzfSAvPlxufVxuXG5mdW5jdGlvbiBTcGlubmVyV2l0aFZlcmJJbm5lcih7XG4gIG1vZGUsXG4gIGxvYWRpbmdTdGFydFRpbWVSZWYsXG4gIHRvdGFsUGF1c2VkTXNSZWYsXG4gIHBhdXNlU3RhcnRUaW1lUmVmLFxuICBzcGlubmVyVGlwLFxuICByZXNwb25zZUxlbmd0aFJlZixcbiAgb3ZlcnJpZGVDb2xvcixcbiAgb3ZlcnJpZGVTaGltbWVyQ29sb3IsXG4gIG92ZXJyaWRlTWVzc2FnZSxcbiAgc3Bpbm5lclN1ZmZpeCxcbiAgdmVyYm9zZSxcbiAgaGFzQWN0aXZlVG9vbHMgPSBmYWxzZSxcbiAgbGVhZGVySXNJZGxlID0gZmFsc2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHNldHRpbmdzID0gdXNlU2V0dGluZ3MoKVxuICBjb25zdCByZWR1Y2VkTW90aW9uID0gc2V0dGluZ3MucHJlZmVyc1JlZHVjZWRNb3Rpb24gPz8gZmFsc2VcblxuICAvLyBOT1RFOiB1c2VBbmltYXRpb25GcmFtZSg1MCkgbGl2ZXMgaW4gU3Bpbm5lckFuaW1hdGlvblJvdywgbm90IGhlcmUuXG4gIC8vIFRoaXMgY29tcG9uZW50IG9ubHkgcmUtcmVuZGVycyB3aGVuIHByb3BzIG9yIGFwcCBzdGF0ZSBjaGFuZ2Ug4oCUXG4gIC8vIGl0IGlzIG5vIGxvbmdlciBvbiB0aGUgNTBtcyBjbG9jay4gQWxsIGB0aW1lYC1kZXJpdmVkIHZhbHVlc1xuICAvLyAoZnJhbWUsIGdsaW1tZXIsIHN0YWxsZWQgaW50ZW5zaXR5LCB0b2tlbiBjb3VudGVyLCB0aGlua2luZyBzaGltbWVyLFxuICAvLyBlbGFwc2VkLXRpbWUgdGltZXIpIGFyZSBjb21wdXRlZCBpbnNpZGUgdGhlIGNoaWxkLlxuXG4gIGNvbnN0IHRhc2tzID0gdXNlQXBwU3RhdGUocyA9PiBzLnRhc2tzKVxuICBjb25zdCB2aWV3aW5nQWdlbnRUYXNrSWQgPSB1c2VBcHBTdGF0ZShzID0+IHMudmlld2luZ0FnZW50VGFza0lkKVxuICBjb25zdCBleHBhbmRlZFZpZXcgPSB1c2VBcHBTdGF0ZShzID0+IHMuZXhwYW5kZWRWaWV3KVxuICBjb25zdCBzaG93RXhwYW5kZWRUb2RvcyA9IGV4cGFuZGVkVmlldyA9PT0gJ3Rhc2tzJ1xuICBjb25zdCBzaG93U3Bpbm5lclRyZWUgPSBleHBhbmRlZFZpZXcgPT09ICd0ZWFtbWF0ZXMnXG4gIGNvbnN0IHNlbGVjdGVkSVBBZ2VudEluZGV4ID0gdXNlQXBwU3RhdGUocyA9PiBzLnNlbGVjdGVkSVBBZ2VudEluZGV4KVxuICBjb25zdCB2aWV3U2VsZWN0aW9uTW9kZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy52aWV3U2VsZWN0aW9uTW9kZSlcbiAgLy8gR2V0IGZvcmVncm91bmRlZCB0ZWFtbWF0ZSAoaWYgdmlld2luZyBhIHRlYW1tYXRlJ3MgdHJhbnNjcmlwdClcbiAgY29uc3QgZm9yZWdyb3VuZGVkVGVhbW1hdGUgPSB2aWV3aW5nQWdlbnRUYXNrSWRcbiAgICA/IGdldFZpZXdlZFRlYW1tYXRlVGFzayh7IHZpZXdpbmdBZ2VudFRhc2tJZCwgdGFza3MgfSlcbiAgICA6IHVuZGVmaW5lZFxuICBjb25zdCB7IGNvbHVtbnMgfSA9IHVzZVRlcm1pbmFsU2l6ZSgpXG4gIGNvbnN0IHRhc2tzVjIgPSB1c2VUYXNrc1YyKClcblxuICAvLyBUcmFjayB0aGlua2luZyBzdGF0dXM6ICd0aGlua2luZycgfCBudW1iZXIgKGR1cmF0aW9uIGluIG1zKSB8IG51bGxcbiAgLy8gU2hvd3MgZWFjaCBzdGF0ZSBmb3IgbWluaW11bSAycyB0byBhdm9pZCBVSSBqYW5rXG4gIGNvbnN0IFt0aGlua2luZ1N0YXR1cywgc2V0VGhpbmtpbmdTdGF0dXNdID0gdXNlU3RhdGU8XG4gICAgJ3RoaW5raW5nJyB8IG51bWJlciB8IG51bGxcbiAgPihudWxsKVxuICBjb25zdCB0aGlua2luZ1N0YXJ0UmVmID0gdXNlUmVmPG51bWJlciB8IG51bGw+KG51bGwpXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBsZXQgc2hvd0R1cmF0aW9uVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgbnVsbCA9IG51bGxcbiAgICBsZXQgY2xlYXJTdGF0dXNUaW1lcjogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCBudWxsID0gbnVsbFxuXG4gICAgaWYgKG1vZGUgPT09ICd0aGlua2luZycpIHtcbiAgICAgIC8vIFN0YXJ0ZWQgdGhpbmtpbmdcbiAgICAgIGlmICh0aGlua2luZ1N0YXJ0UmVmLmN1cnJlbnQgPT09IG51bGwpIHtcbiAgICAgICAgdGhpbmtpbmdTdGFydFJlZi5jdXJyZW50ID0gRGF0ZS5ub3coKVxuICAgICAgICBzZXRUaGlua2luZ1N0YXR1cygndGhpbmtpbmcnKVxuICAgICAgfVxuICAgIH0gZWxzZSBpZiAodGhpbmtpbmdTdGFydFJlZi5jdXJyZW50ICE9PSBudWxsKSB7XG4gICAgICAvLyBTdG9wcGVkIHRoaW5raW5nIC0gY2FsY3VsYXRlIGR1cmF0aW9uIGFuZCBlbnN1cmUgMnMgbWluaW11bSBkaXNwbGF5XG4gICAgICBjb25zdCBkdXJhdGlvbiA9IERhdGUubm93KCkgLSB0aGlua2luZ1N0YXJ0UmVmLmN1cnJlbnRcbiAgICAgIGNvbnN0IGVsYXBzZWQgPSBEYXRlLm5vdygpIC0gdGhpbmtpbmdTdGFydFJlZi5jdXJyZW50XG4gICAgICBjb25zdCByZW1haW5pbmdUaGlua2luZ1RpbWUgPSBNYXRoLm1heCgwLCAyMDAwIC0gZWxhcHNlZClcblxuICAgICAgdGhpbmtpbmdTdGFydFJlZi5jdXJyZW50ID0gbnVsbFxuXG4gICAgICAvLyBTaG93IFwidGhpbmtpbmcuLi5cIiBmb3IgcmVtYWluaW5nIHRpbWUgaWYgPCAycyBlbGFwc2VkLCB0aGVuIHNob3cgZHVyYXRpb25cbiAgICAgIGNvbnN0IHNob3dEdXJhdGlvbiA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgc2V0VGhpbmtpbmdTdGF0dXMoZHVyYXRpb24pXG4gICAgICAgIC8vIENsZWFyIGFmdGVyIDJzXG4gICAgICAgIGNsZWFyU3RhdHVzVGltZXIgPSBzZXRUaW1lb3V0KHNldFRoaW5raW5nU3RhdHVzLCAyMDAwLCBudWxsKVxuICAgICAgfVxuXG4gICAgICBpZiAocmVtYWluaW5nVGhpbmtpbmdUaW1lID4gMCkge1xuICAgICAgICBzaG93RHVyYXRpb25UaW1lciA9IHNldFRpbWVvdXQoc2hvd0R1cmF0aW9uLCByZW1haW5pbmdUaGlua2luZ1RpbWUpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzaG93RHVyYXRpb24oKVxuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBpZiAoc2hvd0R1cmF0aW9uVGltZXIpIGNsZWFyVGltZW91dChzaG93RHVyYXRpb25UaW1lcilcbiAgICAgIGlmIChjbGVhclN0YXR1c1RpbWVyKSBjbGVhclRpbWVvdXQoY2xlYXJTdGF0dXNUaW1lcilcbiAgICB9XG4gIH0sIFttb2RlXSlcblxuICAvLyBGaW5kIHRoZSBjdXJyZW50IGluLXByb2dyZXNzIHRhc2sgYW5kIG5leHQgcGVuZGluZyB0YXNrXG4gIGNvbnN0IGN1cnJlbnRUb2RvID0gdGFza3NWMj8uZmluZChcbiAgICB0YXNrID0+IHRhc2suc3RhdHVzICE9PSAncGVuZGluZycgJiYgdGFzay5zdGF0dXMgIT09ICdjb21wbGV0ZWQnLFxuICApXG4gIGNvbnN0IG5leHRUYXNrID0gZmluZE5leHRQZW5kaW5nVGFzayh0YXNrc1YyKVxuXG4gIC8vIFVzZSB1c2VTdGF0ZSB3aXRoIGluaXRpYWxpemVyIHRvIHBpY2sgYSByYW5kb20gdmVyYiBvbmNlIG9uIG1vdW50XG4gIGNvbnN0IFtyYW5kb21WZXJiXSA9IHVzZVN0YXRlKCgpID0+IHNhbXBsZShnZXRTcGlubmVyVmVyYnMoKSkpXG5cbiAgLy8gTGVhZGVyJ3Mgb3duIHZlcmIgKGFsd2F5cyB0aGUgbGVhZGVyJ3MsIHJlZ2FyZGxlc3Mgb2Ygd2hvIGlzIGZvcmVncm91bmRlZClcbiAgY29uc3QgbGVhZGVyVmVyYiA9XG4gICAgb3ZlcnJpZGVNZXNzYWdlID8/XG4gICAgY3VycmVudFRvZG8/LmFjdGl2ZUZvcm0gPz9cbiAgICBjdXJyZW50VG9kbz8uc3ViamVjdCA/P1xuICAgIHJhbmRvbVZlcmJcblxuICBjb25zdCBlZmZlY3RpdmVWZXJiID1cbiAgICBmb3JlZ3JvdW5kZWRUZWFtbWF0ZSAmJiAhZm9yZWdyb3VuZGVkVGVhbW1hdGUuaXNJZGxlXG4gICAgICA/IChmb3JlZ3JvdW5kZWRUZWFtbWF0ZS5zcGlubmVyVmVyYiA/PyByYW5kb21WZXJiKVxuICAgICAgOiBsZWFkZXJWZXJiXG4gIGNvbnN0IG1lc3NhZ2UgPSBlZmZlY3RpdmVWZXJiICsgJ+KApidcblxuICAvLyBUcmFjayBDTEkgYWN0aXZpdHkgd2hlbiBzcGlubmVyIGlzIGFjdGl2ZVxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IG9wZXJhdGlvbklkID0gJ3NwaW5uZXItJyArIG1vZGVcbiAgICBhY3Rpdml0eU1hbmFnZXIuc3RhcnRDTElBY3Rpdml0eShvcGVyYXRpb25JZClcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgYWN0aXZpdHlNYW5hZ2VyLmVuZENMSUFjdGl2aXR5KG9wZXJhdGlvbklkKVxuICAgIH1cbiAgfSwgW21vZGVdKVxuXG4gIGNvbnN0IGVmZm9ydFZhbHVlID0gdXNlQXBwU3RhdGUocyA9PiBzLmVmZm9ydFZhbHVlKVxuICBjb25zdCBlZmZvcnRTdWZmaXggPSBnZXRFZmZvcnRTdWZmaXgoZ2V0TWFpbkxvb3BNb2RlbCgpLCBlZmZvcnRWYWx1ZSlcblxuICAvLyBDaGVjayBpZiBhbnkgcnVubmluZyBpbi1wcm9jZXNzIHRlYW1tYXRlcyBleGlzdCAobmVlZGVkIGZvciBib3RoIG1vZGVzKVxuICBjb25zdCBydW5uaW5nVGVhbW1hdGVzID0gZ2V0QWxsSW5Qcm9jZXNzVGVhbW1hdGVUYXNrcyh0YXNrcykuZmlsdGVyKFxuICAgIHQgPT4gdC5zdGF0dXMgPT09ICdydW5uaW5nJyxcbiAgKVxuICBjb25zdCBoYXNSdW5uaW5nVGVhbW1hdGVzID0gcnVubmluZ1RlYW1tYXRlcy5sZW5ndGggPiAwXG4gIGNvbnN0IGFsbElkbGUgPSBoYXNSdW5uaW5nVGVhbW1hdGVzICYmIHJ1bm5pbmdUZWFtbWF0ZXMuZXZlcnkodCA9PiB0LmlzSWRsZSlcblxuICAvLyBHYXRoZXIgYWdncmVnYXRlIHRva2VuIHN0YXRzIGZyb20gYWxsIHJ1bm5pbmcgc3dhcm0gdGVhbW1hdGVzXG4gIC8vIEluIHNwaW5uZXItdHJlZSBtb2RlLCBza2lwIGFnZ3JlZ2F0aW9uICh0ZWFtbWF0ZXMgaGF2ZSB0aGVpciBvd24gbGluZXMgaW4gdGhlIHRyZWUpXG4gIGxldCB0ZWFtbWF0ZVRva2VucyA9IDBcbiAgaWYgKCFzaG93U3Bpbm5lclRyZWUpIHtcbiAgICBmb3IgKGNvbnN0IHRhc2sgb2YgT2JqZWN0LnZhbHVlcyh0YXNrcykpIHtcbiAgICAgIGlmIChpc0luUHJvY2Vzc1RlYW1tYXRlVGFzayh0YXNrKSAmJiB0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnKSB7XG4gICAgICAgIGlmICh0YXNrLnByb2dyZXNzPy50b2tlbkNvdW50KSB7XG4gICAgICAgICAgdGVhbW1hdGVUb2tlbnMgKz0gdGFzay5wcm9ncmVzcy50b2tlbkNvdW50XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBTdGFsZSByZWFkIG9mIHRoZSByZWZzIGZvciBzaG93QnR3VGlwIGJlbG93IOKAlCB3ZSdyZSBvZmYgdGhlIDUwbXMgY2xvY2tcbiAgLy8gc28gdGhpcyBvbmx5IHVwZGF0ZXMgd2hlbiBwcm9wcy9hcHAgc3RhdGUgY2hhbmdlLCB3aGljaCBpcyBzdWZmaWNpZW50IGZvclxuICAvLyBhIGNvYXJzZSAzMHMgdGhyZXNob2xkLlxuICBjb25zdCBlbGFwc2VkU25hcHNob3QgPVxuICAgIHBhdXNlU3RhcnRUaW1lUmVmLmN1cnJlbnQgIT09IG51bGxcbiAgICAgID8gcGF1c2VTdGFydFRpbWVSZWYuY3VycmVudCAtXG4gICAgICAgIGxvYWRpbmdTdGFydFRpbWVSZWYuY3VycmVudCAtXG4gICAgICAgIHRvdGFsUGF1c2VkTXNSZWYuY3VycmVudFxuICAgICAgOiBEYXRlLm5vdygpIC0gbG9hZGluZ1N0YXJ0VGltZVJlZi5jdXJyZW50IC0gdG90YWxQYXVzZWRNc1JlZi5jdXJyZW50XG5cbiAgLy8gTGVhZGVyIHRva2VuIGNvdW50IGZvciBUZWFtbWF0ZVNwaW5uZXJUcmVlIOKAlCByZWFkIHJhdyAobm9uLWFuaW1hdGVkKSBmcm9tXG4gIC8vIHRoZSByZWYuIFRoZSB0cmVlIGlzIG9ubHkgc2hvd24gd2hlbiB0ZWFtbWF0ZXMgYXJlIHJ1bm5pbmc7IHRlYW1tYXRlXG4gIC8vIHByb2dyZXNzIHVwZGF0ZXMgdG8gcy50YXNrcyB0cmlnZ2VyIHJlLXJlbmRlcnMgdGhhdCBrZWVwIHRoaXMgZnJlc2guXG4gIGNvbnN0IGxlYWRlclRva2VuQ291bnQgPSBNYXRoLnJvdW5kKHJlc3BvbnNlTGVuZ3RoUmVmLmN1cnJlbnQgLyA0KVxuXG4gIGNvbnN0IGRlZmF1bHRDb2xvcjoga2V5b2YgVGhlbWUgPSAnY2xhdWRlJ1xuICBjb25zdCBkZWZhdWx0U2hpbW1lckNvbG9yID0gJ2NsYXVkZVNoaW1tZXInXG4gIGNvbnN0IG1lc3NhZ2VDb2xvciA9IG92ZXJyaWRlQ29sb3IgPz8gZGVmYXVsdENvbG9yXG4gIGNvbnN0IHNoaW1tZXJDb2xvciA9IG92ZXJyaWRlU2hpbW1lckNvbG9yID8/IGRlZmF1bHRTaGltbWVyQ29sb3JcblxuICAvLyBDb21wdXRlIFRURlQgc3RyaW5nIGhlcmUgKG9mZiB0aGUgNTBtcyBhbmltYXRpb24gY2xvY2spIGFuZCBwYXNzIHRvXG4gIC8vIFNwaW5uZXJBbmltYXRpb25Sb3cgc28gaXQgZm9sZHMgaW50byB0aGUgYCh0aG91Z2h0IGZvciBOcyDCtyAuLi4pYCBzdGF0dXNcbiAgLy8gbGluZSBpbnN0ZWFkIG9mIHRha2luZyBhIHNlcGFyYXRlIHJvdy4gYXBpTWV0cmljc1JlZiBpcyBhIHJlZiBzbyB0aGlzXG4gIC8vIGRvZXNuJ3QgdHJpZ2dlciByZS1yZW5kZXJzOyB3ZSBwaWNrIHVwIHVwZGF0ZXMgb24gdGhlIHBhcmVudCdzIH4yNXgvdHVyblxuICAvLyByZS1yZW5kZXIgY2FkZW5jZSwgc2FtZSBhcyB0aGUgb2xkIEFwaU1ldHJpY3NMaW5lIGRpZC5cbiAgbGV0IHR0ZnRUZXh0OiBzdHJpbmcgfCBudWxsID0gbnVsbFxuICBpZiAoXG4gICAgXCJleHRlcm5hbFwiID09PSAnYW50JyAmJlxuICAgIGFwaU1ldHJpY3NSZWY/LmN1cnJlbnQgJiZcbiAgICBhcGlNZXRyaWNzUmVmLmN1cnJlbnQubGVuZ3RoID4gMFxuICApIHtcbiAgICB0dGZ0VGV4dCA9IGNvbXB1dGVUdGZ0VGV4dChhcGlNZXRyaWNzUmVmLmN1cnJlbnQpXG4gIH1cblxuICAvLyBXaGVuIGxlYWRlciBpcyBpZGxlIGJ1dCB0ZWFtbWF0ZXMgYXJlIHJ1bm5pbmcgKGFuZCB3ZSdyZSB2aWV3aW5nIHRoZSBsZWFkZXIpLFxuICAvLyBzaG93IGEgc3RhdGljIGRpbSBpZGxlIGRpc3BsYXkgaW5zdGVhZCBvZiB0aGUgYW5pbWF0ZWQgc3Bpbm5lciDigJQgb3RoZXJ3aXNlXG4gIC8vIHVzZVN0YWxsZWRBbmltYXRpb24gZGV0ZWN0cyBubyBuZXcgdG9rZW5zIGFmdGVyIDNzIGFuZCB0dXJucyB0aGUgc3Bpbm5lciByZWQuXG4gIGlmIChsZWFkZXJJc0lkbGUgJiYgaGFzUnVubmluZ1RlYW1tYXRlcyAmJiAhZm9yZWdyb3VuZGVkVGVhbW1hdGUpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9XCIxMDAlXCIgYWxpZ25JdGVtcz1cImZsZXgtc3RhcnRcIj5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZmxleFdyYXA9XCJ3cmFwXCIgbWFyZ2luVG9wPXsxfSB3aWR0aD1cIjEwMCVcIj5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHtURUFSRFJPUF9BU1RFUklTS30gSWRsZVxuICAgICAgICAgICAgeyFhbGxJZGxlICYmICcgwrcgdGVhbW1hdGVzIHJ1bm5pbmcnfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHtzaG93U3Bpbm5lclRyZWUgJiYgKFxuICAgICAgICAgIDxUZWFtbWF0ZVNwaW5uZXJUcmVlXG4gICAgICAgICAgICBzZWxlY3RlZEluZGV4PXtzZWxlY3RlZElQQWdlbnRJbmRleH1cbiAgICAgICAgICAgIGlzSW5TZWxlY3Rpb25Nb2RlPXt2aWV3U2VsZWN0aW9uTW9kZSA9PT0gJ3NlbGVjdGluZy1hZ2VudCd9XG4gICAgICAgICAgICBhbGxJZGxlPXthbGxJZGxlfVxuICAgICAgICAgICAgbGVhZGVyVG9rZW5Db3VudD17bGVhZGVyVG9rZW5Db3VudH1cbiAgICAgICAgICAgIGxlYWRlcklkbGVUZXh0PVwiSWRsZVwiXG4gICAgICAgICAgLz5cbiAgICAgICAgKX1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIC8vIFdoZW4gdmlld2luZyBhbiBpZGxlIHRlYW1tYXRlLCBzaG93IHN0YXRpYyBpZGxlIGRpc3BsYXkgaW5zdGVhZCBvZiBhbmltYXRlZCBzcGlubmVyXG4gIGlmIChmb3JlZ3JvdW5kZWRUZWFtbWF0ZT8uaXNJZGxlKSB7XG4gICAgY29uc3QgaWRsZVRleHQgPSBhbGxJZGxlXG4gICAgICA/IGAke1RFQVJEUk9QX0FTVEVSSVNLfSBXb3JrZWQgZm9yICR7Zm9ybWF0RHVyYXRpb24oRGF0ZS5ub3coKSAtIGZvcmVncm91bmRlZFRlYW1tYXRlLnN0YXJ0VGltZSl9YFxuICAgICAgOiBgJHtURUFSRFJPUF9BU1RFUklTS30gSWRsZWBcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9XCIxMDAlXCIgYWxpZ25JdGVtcz1cImZsZXgtc3RhcnRcIj5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZmxleFdyYXA9XCJ3cmFwXCIgbWFyZ2luVG9wPXsxfSB3aWR0aD1cIjEwMCVcIj5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57aWRsZVRleHR9PC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAge3Nob3dTcGlubmVyVHJlZSAmJiBoYXNSdW5uaW5nVGVhbW1hdGVzICYmIChcbiAgICAgICAgICA8VGVhbW1hdGVTcGlubmVyVHJlZVxuICAgICAgICAgICAgc2VsZWN0ZWRJbmRleD17c2VsZWN0ZWRJUEFnZW50SW5kZXh9XG4gICAgICAgICAgICBpc0luU2VsZWN0aW9uTW9kZT17dmlld1NlbGVjdGlvbk1vZGUgPT09ICdzZWxlY3RpbmctYWdlbnQnfVxuICAgICAgICAgICAgYWxsSWRsZT17YWxsSWRsZX1cbiAgICAgICAgICAgIGxlYWRlclZlcmI9e2xlYWRlcklzSWRsZSA/IHVuZGVmaW5lZCA6IGxlYWRlclZlcmJ9XG4gICAgICAgICAgICBsZWFkZXJJZGxlVGV4dD17bGVhZGVySXNJZGxlID8gJ0lkbGUnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgbGVhZGVyVG9rZW5Db3VudD17bGVhZGVyVG9rZW5Db3VudH1cbiAgICAgICAgICAvPlxuICAgICAgICApfVxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgLy8gVGltZS1iYXNlZCB0aXAgb3ZlcnJpZGVzOiBjb2Fyc2UgdGhyZXNob2xkcyBzbyBhIHN0YWxlIHJlZiByZWFkICh3ZSdyZVxuICAvLyBvZmYgdGhlIDUwbXMgY2xvY2spIGlzIGZpbmUuIE90aGVyIHRyaWdnZXJzIChtb2RlIGNoYW5nZSwgc2V0TWVzc2FnZXMpXG4gIC8vIGNhdXNlIHJlLXJlbmRlcnMgdGhhdCByZWZyZXNoIHRoaXMgaW4gcHJhY3RpY2UuXG4gIGxldCBjb250ZXh0VGlwc0FjdGl2ZSA9IGZhbHNlXG4gIGNvbnN0IHRpcHNFbmFibGVkID0gc2V0dGluZ3Muc3Bpbm5lclRpcHNFbmFibGVkICE9PSBmYWxzZVxuICBjb25zdCBzaG93Q2xlYXJUaXAgPSB0aXBzRW5hYmxlZCAmJiBlbGFwc2VkU25hcHNob3QgPiAxXzgwMF8wMDBcbiAgY29uc3Qgc2hvd0J0d1RpcCA9XG4gICAgdGlwc0VuYWJsZWQgJiYgZWxhcHNlZFNuYXBzaG90ID4gMzBfMDAwICYmICFnZXRHbG9iYWxDb25maWcoKS5idHdVc2VDb3VudFxuXG4gIGNvbnN0IGVmZmVjdGl2ZVRpcCA9IGNvbnRleHRUaXBzQWN0aXZlXG4gICAgPyB1bmRlZmluZWRcbiAgICA6IHNob3dDbGVhclRpcCAmJiAhbmV4dFRhc2tcbiAgICAgID8gJ1VzZSAvY2xlYXIgdG8gc3RhcnQgZnJlc2ggd2hlbiBzd2l0Y2hpbmcgdG9waWNzIGFuZCBmcmVlIHVwIGNvbnRleHQnXG4gICAgICA6IHNob3dCdHdUaXAgJiYgIW5leHRUYXNrXG4gICAgICAgID8gXCJVc2UgL2J0dyB0byBhc2sgYSBxdWljayBzaWRlIHF1ZXN0aW9uIHdpdGhvdXQgaW50ZXJydXB0aW5nIENsYXVkZSdzIGN1cnJlbnQgd29ya1wiXG4gICAgICAgIDogc3Bpbm5lclRpcFxuXG4gIC8vIEJ1ZGdldCB0ZXh0IChhbnQtb25seSkg4oCUIHNob3duIGFib3ZlIHRoZSB0aXAgbGluZVxuICBsZXQgYnVkZ2V0VGV4dDogc3RyaW5nIHwgbnVsbCA9IG51bGxcbiAgaWYgKGZlYXR1cmUoJ1RPS0VOX0JVREdFVCcpKSB7XG4gICAgY29uc3QgYnVkZ2V0ID0gZ2V0Q3VycmVudFR1cm5Ub2tlbkJ1ZGdldCgpXG4gICAgaWYgKGJ1ZGdldCAhPT0gbnVsbCAmJiBidWRnZXQgPiAwKSB7XG4gICAgICBjb25zdCB0b2tlbnMgPSBnZXRUdXJuT3V0cHV0VG9rZW5zKClcbiAgICAgIGlmICh0b2tlbnMgPj0gYnVkZ2V0KSB7XG4gICAgICAgIGJ1ZGdldFRleHQgPSBgVGFyZ2V0OiAke2Zvcm1hdE51bWJlcih0b2tlbnMpfSB1c2VkICgke2Zvcm1hdE51bWJlcihidWRnZXQpfSBtaW4gJHtmaWd1cmVzLnRpY2t9KWBcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHBjdCA9IE1hdGgucm91bmQoKHRva2VucyAvIGJ1ZGdldCkgKiAxMDApXG4gICAgICAgIGNvbnN0IHJlbWFpbmluZyA9IGJ1ZGdldCAtIHRva2Vuc1xuICAgICAgICBjb25zdCByYXRlID1cbiAgICAgICAgICBlbGFwc2VkU25hcHNob3QgPiA1MDAwICYmIHRva2VucyA+PSAyMDAwXG4gICAgICAgICAgICA/IHRva2VucyAvIGVsYXBzZWRTbmFwc2hvdFxuICAgICAgICAgICAgOiAwXG4gICAgICAgIGNvbnN0IGV0YSA9XG4gICAgICAgICAgcmF0ZSA+IDBcbiAgICAgICAgICAgID8gYCBcXHUwMEI3IH4ke2Zvcm1hdER1cmF0aW9uKHJlbWFpbmluZyAvIHJhdGUsIHsgbW9zdFNpZ25pZmljYW50T25seTogdHJ1ZSB9KX1gXG4gICAgICAgICAgICA6ICcnXG4gICAgICAgIGJ1ZGdldFRleHQgPSBgVGFyZ2V0OiAke2Zvcm1hdE51bWJlcih0b2tlbnMpfSAvICR7Zm9ybWF0TnVtYmVyKGJ1ZGdldCl9ICgke3BjdH0lKSR7ZXRhfWBcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHdpZHRoPVwiMTAwJVwiIGFsaWduSXRlbXM9XCJmbGV4LXN0YXJ0XCI+XG4gICAgICA8U3Bpbm5lckFuaW1hdGlvblJvd1xuICAgICAgICBtb2RlPXttb2RlfVxuICAgICAgICByZWR1Y2VkTW90aW9uPXtyZWR1Y2VkTW90aW9ufVxuICAgICAgICBoYXNBY3RpdmVUb29scz17aGFzQWN0aXZlVG9vbHN9XG4gICAgICAgIHJlc3BvbnNlTGVuZ3RoUmVmPXtyZXNwb25zZUxlbmd0aFJlZn1cbiAgICAgICAgbWVzc2FnZT17bWVzc2FnZX1cbiAgICAgICAgbWVzc2FnZUNvbG9yPXttZXNzYWdlQ29sb3J9XG4gICAgICAgIHNoaW1tZXJDb2xvcj17c2hpbW1lckNvbG9yfVxuICAgICAgICBvdmVycmlkZUNvbG9yPXtvdmVycmlkZUNvbG9yfVxuICAgICAgICBsb2FkaW5nU3RhcnRUaW1lUmVmPXtsb2FkaW5nU3RhcnRUaW1lUmVmfVxuICAgICAgICB0b3RhbFBhdXNlZE1zUmVmPXt0b3RhbFBhdXNlZE1zUmVmfVxuICAgICAgICBwYXVzZVN0YXJ0VGltZVJlZj17cGF1c2VTdGFydFRpbWVSZWZ9XG4gICAgICAgIHNwaW5uZXJTdWZmaXg9e3NwaW5uZXJTdWZmaXh9XG4gICAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICAgIGNvbHVtbnM9e2NvbHVtbnN9XG4gICAgICAgIGhhc1J1bm5pbmdUZWFtbWF0ZXM9e2hhc1J1bm5pbmdUZWFtbWF0ZXN9XG4gICAgICAgIHRlYW1tYXRlVG9rZW5zPXt0ZWFtbWF0ZVRva2Vuc31cbiAgICAgICAgZm9yZWdyb3VuZGVkVGVhbW1hdGU9e2ZvcmVncm91bmRlZFRlYW1tYXRlfVxuICAgICAgICBsZWFkZXJJc0lkbGU9e2xlYWRlcklzSWRsZX1cbiAgICAgICAgdGhpbmtpbmdTdGF0dXM9e3RoaW5raW5nU3RhdHVzfVxuICAgICAgICBlZmZvcnRTdWZmaXg9e2VmZm9ydFN1ZmZpeH1cbiAgICAgIC8+XG4gICAgICB7c2hvd1NwaW5uZXJUcmVlICYmIGhhc1J1bm5pbmdUZWFtbWF0ZXMgPyAoXG4gICAgICAgIDxUZWFtbWF0ZVNwaW5uZXJUcmVlXG4gICAgICAgICAgc2VsZWN0ZWRJbmRleD17c2VsZWN0ZWRJUEFnZW50SW5kZXh9XG4gICAgICAgICAgaXNJblNlbGVjdGlvbk1vZGU9e3ZpZXdTZWxlY3Rpb25Nb2RlID09PSAnc2VsZWN0aW5nLWFnZW50J31cbiAgICAgICAgICBhbGxJZGxlPXthbGxJZGxlfVxuICAgICAgICAgIGxlYWRlclZlcmI9e2xlYWRlcklzSWRsZSA/IHVuZGVmaW5lZCA6IGxlYWRlclZlcmJ9XG4gICAgICAgICAgbGVhZGVySWRsZVRleHQ9e2xlYWRlcklzSWRsZSA/ICdJZGxlJyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICBsZWFkZXJUb2tlbkNvdW50PXtsZWFkZXJUb2tlbkNvdW50fVxuICAgICAgICAvPlxuICAgICAgKSA6IHNob3dFeHBhbmRlZFRvZG9zICYmIHRhc2tzVjIgJiYgdGFza3NWMi5sZW5ndGggPiAwID8gKFxuICAgICAgICA8Qm94IHdpZHRoPVwiMTAwJVwiIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8TWVzc2FnZVJlc3BvbnNlPlxuICAgICAgICAgICAgPFRhc2tMaXN0VjIgdGFza3M9e3Rhc2tzVjJ9IC8+XG4gICAgICAgICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gICAgICAgIDwvQm94PlxuICAgICAgKSA6IG5leHRUYXNrIHx8IGVmZmVjdGl2ZVRpcCB8fCBidWRnZXRUZXh0ID8gKFxuICAgICAgICAvLyBJTVBPUlRBTlQ6IHdlIG5lZWQgdGhpcyB3aWR0aD1cIjEwMCVcIiB0byBhdm9pZCBhbiBJbmsgYnVnIHdoZXJlIHRoZVxuICAgICAgICAvLyB0aXAgZ2V0cyBkdXBsaWNhdGVkIG92ZXIgYW5kIG92ZXIgd2hpbGUgdGhlIHNwaW5uZXIgaXMgcnVubmluZyBpZlxuICAgICAgICAvLyB0aGUgdGVybWluYWwgaXMgdmVyeSBzbWFsbC4gVE9ETzogZml4IHRoaXMgaW4gSW5rLlxuICAgICAgICA8Qm94IHdpZHRoPVwiMTAwJVwiIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICB7YnVkZ2V0VGV4dCAmJiAoXG4gICAgICAgICAgICA8TWVzc2FnZVJlc3BvbnNlPlxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57YnVkZ2V0VGV4dH08L1RleHQ+XG4gICAgICAgICAgICA8L01lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgICApfVxuICAgICAgICAgIHsobmV4dFRhc2sgfHwgZWZmZWN0aXZlVGlwKSAmJiAoXG4gICAgICAgICAgICA8TWVzc2FnZVJlc3BvbnNlPlxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICB7bmV4dFRhc2tcbiAgICAgICAgICAgICAgICAgID8gYE5leHQ6ICR7bmV4dFRhc2suc3ViamVjdH1gXG4gICAgICAgICAgICAgICAgICA6IGBUaXA6ICR7ZWZmZWN0aXZlVGlwfWB9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvTWVzc2FnZVJlc3BvbnNlPlxuICAgICAgICAgICl9XG4gICAgICAgIDwvQm94PlxuICAgICAgKSA6IG51bGx9XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuLy8gQnJpZWYvYXNzaXN0YW50IG1vZGUgc3Bpbm5lcjogc2luZ2xlIHN0YXR1cyBsaW5lLiBQcm9tcHRJbnB1dCBkcm9wcyBpdHNcbi8vIG93biBtYXJnaW5Ub3Agd2hlbiBpc0JyaWVmT25seSBpcyBhY3RpdmUsIHNvIHRoaXMgY29tcG9uZW50IG93bnMgdGhlXG4vLyAyLXJvdyBmb290cHJpbnQgYmV0d2VlbiBtZXNzYWdlcyBhbmQgaW5wdXQuIEZvb3RwcmludCBpcyBbYmxhbmssIGNvbnRlbnRdXG4vLyDigJQgb25lIGJsYW5rIHJvdyBhYm92ZSAoYnJlYXRoaW5nIHJvb20gdW5kZXIgdGhlIG1lc3NhZ2VzIGxpc3QpLCBzcGlubmVyXG4vLyBmbHVzaCBhZ2FpbnN0IHRoZSBpbnB1dCBiYXIuIFByb21wdElucHV0J3MgYWJzb2x1dGUtcG9zaXRpb25lZFxuLy8gTm90aWZpY2F0aW9ucyBvdmVybGF5IGNvbXBlbnNhdGVzIHdpdGggbWFyZ2luVG9wPS0yIGluIGJyaWVmIG1vZGVcbi8vIChQcm9tcHRJbnB1dC50c3g6fjI5MjgpIHNvIGl0IGZsb2F0cyBpbnRvIHRoZSBibGFuayByb3cgYWJvdmUgdGhlXG4vLyBzcGlubmVyLCBub3Qgb3ZlciB0aGUgc3Bpbm5lciBjb250ZW50LiBQYWlyZWQgd2l0aCBCcmllZklkbGVTdGF0dXMgd2hpY2hcbi8vIGtlZXBzIHRoZSBzYW1lIGZvb3RwcmludCB3aGVuIGlkbGUuXG50eXBlIEJyaWVmU3Bpbm5lclByb3BzID0ge1xuICBtb2RlOiBTcGlubmVyTW9kZVxuICBvdmVycmlkZU1lc3NhZ2U/OiBzdHJpbmcgfCBudWxsXG59XG5cbmZ1bmN0aW9uIEJyaWVmU3Bpbm5lcih7XG4gIG1vZGUsXG4gIG92ZXJyaWRlTWVzc2FnZSxcbn06IEJyaWVmU3Bpbm5lclByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3Qgc2V0dGluZ3MgPSB1c2VTZXR0aW5ncygpXG4gIGNvbnN0IHJlZHVjZWRNb3Rpb24gPSBzZXR0aW5ncy5wcmVmZXJzUmVkdWNlZE1vdGlvbiA/PyBmYWxzZVxuICBjb25zdCBbcmFuZG9tVmVyYl0gPSB1c2VTdGF0ZSgoKSA9PiBzYW1wbGUoZ2V0U3Bpbm5lclZlcmJzKCkpID8/ICdXb3JraW5nJylcbiAgY29uc3QgdmVyYiA9IG92ZXJyaWRlTWVzc2FnZSA/PyByYW5kb21WZXJiXG4gIGNvbnN0IGNvbm5TdGF0dXMgPSB1c2VBcHBTdGF0ZShzID0+IHMucmVtb3RlQ29ubmVjdGlvblN0YXR1cylcblxuICAvLyBUcmFjayBDTEkgYWN0aXZpdHkgc28gT1MvSURFIFwiYnVzeVwiIGluZGljYXRvcnMgZmlyZSBpbiBicmllZiBtb2RlIHRvb1xuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IG9wZXJhdGlvbklkID0gJ3NwaW5uZXItJyArIG1vZGVcbiAgICBhY3Rpdml0eU1hbmFnZXIuc3RhcnRDTElBY3Rpdml0eShvcGVyYXRpb25JZClcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgYWN0aXZpdHlNYW5hZ2VyLmVuZENMSUFjdGl2aXR5KG9wZXJhdGlvbklkKVxuICAgIH1cbiAgfSwgW21vZGVdKVxuXG4gIC8vIERyaXZlIGJvdGggZG90IGN5Y2xlIGFuZCBzaGltbWVyIGZyb20gdGhlIHNoYXJlZCBjbG9jay4gVGhlIHZpZXdwb3J0XG4gIC8vIHJlZiBpcyB1bnVzZWQg4oCUIHRoZSBzcGlubmVyIHVubW91bnRzIG9uIHR1cm4gZW5kIHNvIHZpZXdwb3J0LWJhc2VkXG4gIC8vIHBhdXNpbmcgaXNuJ3QgbmVlZGVkLlxuICBjb25zdCBbLCB0aW1lXSA9IHVzZUFuaW1hdGlvbkZyYW1lKHJlZHVjZWRNb3Rpb24gPyBudWxsIDogMTIwKVxuXG4gIC8vIExvY2FsIHRhc2tzICsgcmVtb3RlIHRhc2tzIGFyZSBtdXR1YWxseSBleGNsdXNpdmUgKHZpZXdlciBtb2RlIGhhcyBhblxuICAvLyBlbXB0eSBsb2NhbCBBcHBTdGF0ZS50YXNrczsgbG9jYWwgbW9kZSBoYXMgcmVtb3RlQmFja2dyb3VuZFRhc2tDb3VudD0wKS5cbiAgLy8gU3VtbWluZyBhdm9pZHMgYSBtb2RlIGJyYW5jaC5cbiAgY29uc3QgcnVubmluZ0NvdW50ID0gdXNlQXBwU3RhdGUoXG4gICAgcyA9PlxuICAgICAgY291bnQoT2JqZWN0LnZhbHVlcyhzLnRhc2tzKSwgaXNCYWNrZ3JvdW5kVGFzaykgK1xuICAgICAgcy5yZW1vdGVCYWNrZ3JvdW5kVGFza0NvdW50LFxuICApXG5cbiAgLy8gQ29ubmVjdGlvbiB0cm91YmxlIG92ZXJyaWRlcyB0aGUgdmVyYiDigJQgYGNsYXVkZSBhc3Npc3RhbnRgIGlzIGEgcHVyZSB2aWV3ZXIsXG4gIC8vIG5vdGhpbmcgdXNlZnVsIGlzIGhhcHBlbmluZyB3aGlsZSB0aGUgV1MgaXMgZG93bi5cbiAgY29uc3Qgc2hvd0Nvbm5XYXJuaW5nID1cbiAgICBjb25uU3RhdHVzID09PSAncmVjb25uZWN0aW5nJyB8fCBjb25uU3RhdHVzID09PSAnZGlzY29ubmVjdGVkJ1xuICBjb25zdCBjb25uVGV4dCA9XG4gICAgY29ublN0YXR1cyA9PT0gJ3JlY29ubmVjdGluZycgPyAnUmVjb25uZWN0aW5nJyA6ICdEaXNjb25uZWN0ZWQnXG5cbiAgLy8gRG90cyBwYWRkZWQgdG8gYSBmaXhlZCAzIGNvbHVtbnMgc28gdGhlIHJpZ2h0LWFsaWduZWQgY291bnQgZG9lc24ndFxuICAvLyBqaXR0ZXIgYXMgdGhlIGN5Y2xlIGFkdmFuY2VzLlxuICBjb25zdCBkb3RGcmFtZSA9IE1hdGguZmxvb3IodGltZSAvIDMwMCkgJSAzXG4gIGNvbnN0IGRvdHMgPSByZWR1Y2VkTW90aW9uID8gJ+KApiAgJyA6ICcuJy5yZXBlYXQoZG90RnJhbWUgKyAxKS5wYWRFbmQoMylcblxuICAvLyBTaGltbWVyOiByZXZlcnNlLXN3ZWVwIGhpZ2hsaWdodCBhY3Jvc3MgdGhlIHZlcmIuIFNraXAgZm9yIGNvbm5lY3Rpb25cbiAgLy8gd2FybmluZ3MgKHNoaW1tZXIgcmVhZHMgYXMgXCJ3b3JraW5nXCI7IFJlY29ubmVjdGluZy9EaXNjb25uZWN0ZWQgaXMgbm90KS5cbiAgY29uc3QgdmVyYldpZHRoID0gdXNlTWVtbygoKSA9PiBzdHJpbmdXaWR0aCh2ZXJiKSwgW3ZlcmJdKVxuICBjb25zdCBnbGltbWVySW5kZXggPVxuICAgIHJlZHVjZWRNb3Rpb24gfHwgc2hvd0Nvbm5XYXJuaW5nXG4gICAgICA/IC0xMDBcbiAgICAgIDogY29tcHV0ZUdsaW1tZXJJbmRleChNYXRoLmZsb29yKHRpbWUgLyBTSElNTUVSX0lOVEVSVkFMX01TKSwgdmVyYldpZHRoKVxuICBjb25zdCB7IGJlZm9yZSwgc2hpbW1lciwgYWZ0ZXIgfSA9IGNvbXB1dGVTaGltbWVyU2VnbWVudHModmVyYiwgZ2xpbW1lckluZGV4KVxuXG4gIGNvbnN0IHsgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcbiAgY29uc3QgcmlnaHRUZXh0ID0gcnVubmluZ0NvdW50ID4gMCA/IGAke3J1bm5pbmdDb3VudH0gaW4gYmFja2dyb3VuZGAgOiAnJ1xuICAvLyBNYW51YWwgcmlnaHQtYWxpZ24gdmlhIHNwYWNlIHBhZGRpbmcg4oCUIGZsZXhHcm93IHNwYWNlcnMgaW5zaWRlXG4gIC8vIEZ1bGxzY3JlZW5MYXlvdXQncyBgbWFpbmAgc2xvdCBkb24ndCByZXNvbHZlIGEgd2lkdGggYW5kIGNhdXNlZCB0aGVcbiAgLy8gZGlmZiBlbmdpbmUgdG8gbWlzcyBkb3QtZnJhbWUgdXBkYXRlcy5cbiAgY29uc3QgbGVmdFdpZHRoID0gKHNob3dDb25uV2FybmluZyA/IHN0cmluZ1dpZHRoKGNvbm5UZXh0KSA6IHZlcmJXaWR0aCkgKyAzXG4gIGNvbnN0IHBhZCA9IE1hdGgubWF4KDEsIGNvbHVtbnMgLSAyIC0gbGVmdFdpZHRoIC0gc3RyaW5nV2lkdGgocmlnaHRUZXh0KSlcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiIHdpZHRoPVwiMTAwJVwiIG1hcmdpblRvcD17MX0gcGFkZGluZ0xlZnQ9ezJ9PlxuICAgICAge3Nob3dDb25uV2FybmluZyA/IChcbiAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPntjb25uVGV4dCArIGRvdHN9PC9UZXh0PlxuICAgICAgKSA6IChcbiAgICAgICAgPD5cbiAgICAgICAgICB7YmVmb3JlID8gPFRleHQgZGltQ29sb3I+e2JlZm9yZX08L1RleHQ+IDogbnVsbH1cbiAgICAgICAgICB7c2hpbW1lciA/IDxUZXh0PntzaGltbWVyfTwvVGV4dD4gOiBudWxsfVxuICAgICAgICAgIHthZnRlciA/IDxUZXh0IGRpbUNvbG9yPnthZnRlcn08L1RleHQ+IDogbnVsbH1cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57ZG90c308L1RleHQ+XG4gICAgICAgIDwvPlxuICAgICAgKX1cbiAgICAgIHtyaWdodFRleHQgPyAoXG4gICAgICAgIDw+XG4gICAgICAgICAgPFRleHQ+eycgJy5yZXBlYXQocGFkKX08L1RleHQ+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJzdWJ0bGVcIj57cmlnaHRUZXh0fTwvVGV4dD5cbiAgICAgICAgPC8+XG4gICAgICApIDogbnVsbH1cbiAgICA8L0JveD5cbiAgKVxufVxuXG4vLyBJZGxlIHBsYWNlaG9sZGVyIGZvciBicmllZiBtb2RlLiBTYW1lIDItcm93IFtibGFuaywgY29udGVudF0gZm9vdHByaW50XG4vLyBhcyBCcmllZlNwaW5uZXIgc28gdGhlIGlucHV0IGJhciBuZXZlciBqdW1wcyB3aGVuIHRvZ2dsaW5nIGJldHdlZW5cbi8vIHdvcmtpbmcvaWRsZS9kaXNjb25uZWN0ZWQuIFNlZSBCcmllZlNwaW5uZXIncyBjb21tZW50IGZvciB0aGVcbi8vIE5vdGlmaWNhdGlvbnMgb3ZlcmxheSBjb3VwbGluZy5cbmV4cG9ydCBmdW5jdGlvbiBCcmllZklkbGVTdGF0dXMoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgY29ublN0YXR1cyA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZW1vdGVDb25uZWN0aW9uU3RhdHVzKVxuICBjb25zdCBydW5uaW5nQ291bnQgPSB1c2VBcHBTdGF0ZShcbiAgICBzID0+XG4gICAgICBjb3VudChPYmplY3QudmFsdWVzKHMudGFza3MpLCBpc0JhY2tncm91bmRUYXNrKSArXG4gICAgICBzLnJlbW90ZUJhY2tncm91bmRUYXNrQ291bnQsXG4gIClcbiAgY29uc3QgeyBjb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuXG4gIGNvbnN0IHNob3dDb25uV2FybmluZyA9XG4gICAgY29ublN0YXR1cyA9PT0gJ3JlY29ubmVjdGluZycgfHwgY29ublN0YXR1cyA9PT0gJ2Rpc2Nvbm5lY3RlZCdcbiAgY29uc3QgY29ublRleHQgPVxuICAgIGNvbm5TdGF0dXMgPT09ICdyZWNvbm5lY3RpbmcnID8gJ1JlY29ubmVjdGluZ+KApicgOiAnRGlzY29ubmVjdGVkJ1xuICBjb25zdCBsZWZ0VGV4dCA9IHNob3dDb25uV2FybmluZyA/IGNvbm5UZXh0IDogJydcbiAgY29uc3QgcmlnaHRUZXh0ID0gcnVubmluZ0NvdW50ID4gMCA/IGAke3J1bm5pbmdDb3VudH0gaW4gYmFja2dyb3VuZGAgOiAnJ1xuXG4gIGlmICghbGVmdFRleHQgJiYgIXJpZ2h0VGV4dCkgcmV0dXJuIDxCb3ggaGVpZ2h0PXsyfSAvPlxuXG4gIGNvbnN0IHBhZCA9IE1hdGgubWF4KFxuICAgIDEsXG4gICAgY29sdW1ucyAtIDIgLSBzdHJpbmdXaWR0aChsZWZ0VGV4dCkgLSBzdHJpbmdXaWR0aChyaWdodFRleHQpLFxuICApXG4gIHJldHVybiAoXG4gICAgPEJveCBtYXJnaW5Ub3A9ezF9IHBhZGRpbmdMZWZ0PXsyfT5cbiAgICAgIDxUZXh0PlxuICAgICAgICB7bGVmdFRleHQgPyA8VGV4dCBjb2xvcj1cImVycm9yXCI+e2xlZnRUZXh0fTwvVGV4dD4gOiBudWxsfVxuICAgICAgICB7cmlnaHRUZXh0ID8gKFxuICAgICAgICAgIDw+XG4gICAgICAgICAgICA8VGV4dD57JyAnLnJlcGVhdChwYWQpfTwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCI+e3JpZ2h0VGV4dH08L1RleHQ+XG4gICAgICAgICAgPC8+XG4gICAgICAgICkgOiBudWxsfVxuICAgICAgPC9UZXh0PlxuICAgIDwvQm94PlxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTcGlubmVyKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHNldHRpbmdzID0gdXNlU2V0dGluZ3MoKVxuICBjb25zdCByZWR1Y2VkTW90aW9uID0gc2V0dGluZ3MucHJlZmVyc1JlZHVjZWRNb3Rpb24gPz8gZmFsc2VcbiAgY29uc3QgW3JlZiwgdGltZV0gPSB1c2VBbmltYXRpb25GcmFtZShyZWR1Y2VkTW90aW9uID8gbnVsbCA6IDEyMClcblxuICAvLyBSZWR1Y2VkIG1vdGlvbjogc3RhdGljIGRvdCBpbnN0ZWFkIG9mIGFuaW1hdGVkIHNwaW5uZXJcbiAgaWYgKHJlZHVjZWRNb3Rpb24pIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCByZWY9e3JlZn0gZmxleFdyYXA9XCJ3cmFwXCIgaGVpZ2h0PXsxfSB3aWR0aD17Mn0+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwidGV4dFwiPuKXjzwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIC8vIERlcml2ZSBmcmFtZSBmcm9tIHN5bmNlZCB0aW1lIC0gYWxsIHNwaW5uZXJzIGFuaW1hdGUgdG9nZXRoZXJcbiAgY29uc3QgZnJhbWUgPSBNYXRoLmZsb29yKHRpbWUgLyAxMjApICUgU1BJTk5FUl9GUkFNRVMubGVuZ3RoXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IHJlZj17cmVmfSBmbGV4V3JhcD1cIndyYXBcIiBoZWlnaHQ9ezF9IHdpZHRoPXsyfT5cbiAgICAgIDxUZXh0IGNvbG9yPVwidGV4dFwiPntTUElOTkVSX0ZSQU1FU1tmcmFtZV19PC9UZXh0PlxuICAgIDwvQm94PlxuICApXG59XG5cblxuZnVuY3Rpb24gZmluZE5leHRQZW5kaW5nVGFzayh0YXNrczogVGFza1tdIHwgdW5kZWZpbmVkKTogVGFzayB8IHVuZGVmaW5lZCB7XG4gIGlmICghdGFza3MpIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH1cbiAgY29uc3QgcGVuZGluZ1Rhc2tzID0gdGFza3MuZmlsdGVyKHQgPT4gdC5zdGF0dXMgPT09ICdwZW5kaW5nJylcbiAgaWYgKHBlbmRpbmdUYXNrcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkXG4gIH1cbiAgY29uc3QgdW5yZXNvbHZlZElkcyA9IG5ldyBTZXQoXG4gICAgdGFza3MuZmlsdGVyKHQgPT4gdC5zdGF0dXMgIT09ICdjb21wbGV0ZWQnKS5tYXAodCA9PiB0LmlkKSxcbiAgKVxuICByZXR1cm4gKFxuICAgIHBlbmRpbmdUYXNrcy5maW5kKHQgPT4gIXQuYmxvY2tlZEJ5LnNvbWUoaWQgPT4gdW5yZXNvbHZlZElkcy5oYXMoaWQpKSkgPz9cbiAgICBwZW5kaW5nVGFza3NbMF1cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUE7QUFDQSxTQUFTQSxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLE1BQU0sRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDNUQsU0FDRUMsbUJBQW1CLEVBQ25CQyxzQkFBc0IsRUFDdEJDLG1CQUFtQixRQUNkLCtCQUErQjtBQUN0QyxTQUFTQyxPQUFPLFFBQVEsWUFBWTtBQUNwQyxTQUFTQyxlQUFlLEVBQUVDLGVBQWUsUUFBUSx1QkFBdUI7QUFDeEUsU0FBU0MsbUNBQW1DLFFBQVEscUNBQXFDO0FBQ3pGLFNBQVNDLFdBQVcsUUFBUSxzQkFBc0I7QUFDbEQsU0FBU0MsS0FBSyxRQUFRLG1CQUFtQjtBQUN6QyxPQUFPQyxNQUFNLE1BQU0scUJBQXFCO0FBQ3hDLFNBQ0VDLGNBQWMsRUFDZEMsWUFBWSxFQUNaQyxrQkFBa0IsUUFDYixvQkFBb0I7QUFDM0IsY0FBY0MsS0FBSyxRQUFRLG9CQUFvQjtBQUMvQyxTQUFTQyxlQUFlLFFBQVEsNkJBQTZCO0FBQzdELFNBQVNDLGVBQWUsUUFBUSw4QkFBOEI7QUFDOUQsU0FBU0MsZUFBZSxRQUFRLHNCQUFzQjtBQUN0RCxTQUFTQyxVQUFVLFFBQVEsaUJB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 \ No newline at end of file diff --git a/components/Spinner/FlashingChar.tsx b/components/Spinner/FlashingChar.tsx new file mode 100644 index 0000000..b05c484 --- /dev/null +++ b/components/Spinner/FlashingChar.tsx @@ -0,0 +1,61 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Text, useTheme } from '../../ink.js'; +import { getTheme, type Theme } from '../../utils/theme.js'; +import { interpolateColor, parseRGB, toRGBColor } from './utils.js'; +type Props = { + char: string; + flashOpacity: number; + messageColor: keyof Theme; + shimmerColor: keyof Theme; +}; +export function FlashingChar(t0) { + const $ = _c(9); + const { + char, + flashOpacity, + messageColor, + shimmerColor + } = t0; + const [themeName] = useTheme(); + let t1; + if ($[0] !== char || $[1] !== flashOpacity || $[2] !== messageColor || $[3] !== shimmerColor || $[4] !== themeName) { + t1 = Symbol.for("react.early_return_sentinel"); + bb0: { + const theme = getTheme(themeName); + const baseColorStr = theme[messageColor]; + const shimmerColorStr = theme[shimmerColor]; + const baseRGB = baseColorStr ? parseRGB(baseColorStr) : null; + const shimmerRGB = shimmerColorStr ? parseRGB(shimmerColorStr) : null; + if (baseRGB && shimmerRGB) { + const interpolated = interpolateColor(baseRGB, shimmerRGB, flashOpacity); + t1 = {char}; + break bb0; + } + } + $[0] = char; + $[1] = flashOpacity; + $[2] = messageColor; + $[3] = shimmerColor; + $[4] = themeName; + $[5] = t1; + } else { + t1 = $[5]; + } + if (t1 !== Symbol.for("react.early_return_sentinel")) { + return t1; + } + const shouldUseShimmer = flashOpacity > 0.5; + const t2 = shouldUseShimmer ? shimmerColor : messageColor; + let t3; + if ($[6] !== char || $[7] !== t2) { + t3 = {char}; + $[6] = char; + $[7] = t2; + $[8] = t3; + } else { + t3 = $[8]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJ1c2VUaGVtZSIsImdldFRoZW1lIiwiVGhlbWUiLCJpbnRlcnBvbGF0ZUNvbG9yIiwicGFyc2VSR0IiLCJ0b1JHQkNvbG9yIiwiUHJvcHMiLCJjaGFyIiwiZmxhc2hPcGFjaXR5IiwibWVzc2FnZUNvbG9yIiwic2hpbW1lckNvbG9yIiwiRmxhc2hpbmdDaGFyIiwidDAiLCIkIiwiX2MiLCJ0aGVtZU5hbWUiLCJ0MSIsIlN5bWJvbCIsImZvciIsImJiMCIsInRoZW1lIiwiYmFzZUNvbG9yU3RyIiwic2hpbW1lckNvbG9yU3RyIiwiYmFzZVJHQiIsInNoaW1tZXJSR0IiLCJpbnRlcnBvbGF0ZWQiLCJzaG91bGRVc2VTaGltbWVyIiwidDIiLCJ0MyJdLCJzb3VyY2VzIjpbIkZsYXNoaW5nQ2hhci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0LCB1c2VUaGVtZSB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGdldFRoZW1lLCB0eXBlIFRoZW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGhlbWUuanMnXG5pbXBvcnQgeyBpbnRlcnBvbGF0ZUNvbG9yLCBwYXJzZVJHQiwgdG9SR0JDb2xvciB9IGZyb20gJy4vdXRpbHMuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGNoYXI6IHN0cmluZ1xuICBmbGFzaE9wYWNpdHk6IG51bWJlclxuICBtZXNzYWdlQ29sb3I6IGtleW9mIFRoZW1lXG4gIHNoaW1tZXJDb2xvcjoga2V5b2YgVGhlbWVcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIEZsYXNoaW5nQ2hhcih7XG4gIGNoYXIsXG4gIGZsYXNoT3BhY2l0eSxcbiAgbWVzc2FnZUNvbG9yLFxuICBzaGltbWVyQ29sb3IsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZU5hbWVdID0gdXNlVGhlbWUoKVxuICBjb25zdCB0aGVtZSA9IGdldFRoZW1lKHRoZW1lTmFtZSlcblxuICBjb25zdCBiYXNlQ29sb3JTdHIgPSB0aGVtZVttZXNzYWdlQ29sb3JdXG4gIGNvbnN0IHNoaW1tZXJDb2xvclN0ciA9IHRoZW1lW3NoaW1tZXJDb2xvcl1cblxuICBjb25zdCBiYXNlUkdCID0gYmFzZUNvbG9yU3RyID8gcGFyc2VSR0IoYmFzZUNvbG9yU3RyKSA6IG51bGxcbiAgY29uc3Qgc2hpbW1lclJHQiA9IHNoaW1tZXJDb2xvclN0ciA/IHBhcnNlUkdCKHNoaW1tZXJDb2xvclN0cikgOiBudWxsXG5cbiAgaWYgKGJhc2VSR0IgJiYgc2hpbW1lclJHQikge1xuICAgIC8vIFNtb290aCBpbnRlcnBvbGF0aW9uIGJldHdlZW4gY29sb3JzXG4gICAgY29uc3QgaW50ZXJwb2xhdGVkID0gaW50ZXJwb2xhdGVDb2xvcihiYXNlUkdCLCBzaGltbWVyUkdCLCBmbGFzaE9wYWNpdHkpXG4gICAgcmV0dXJuIDxUZXh0IGNvbG9yPXt0b1JHQkNvbG9yKGludGVycG9sYXRlZCl9PntjaGFyfTwvVGV4dD5cbiAgfVxuXG4gIC8vIEZhbGxiYWNrIGZvciBBTlNJIHRoZW1lczogYmluYXJ5IHN3aXRjaFxuICBjb25zdCBzaG91bGRVc2VTaGltbWVyID0gZmxhc2hPcGFjaXR5ID4gMC41XG4gIHJldHVybiAoXG4gICAgPFRleHQgY29sb3I9e3Nob3VsZFVzZVNoaW1tZXIgPyBzaGltbWVyQ29sb3IgOiBtZXNzYWdlQ29sb3J9PntjaGFyfTwvVGV4dD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxjQUFjO0FBQzdDLFNBQVNDLFFBQVEsRUFBRSxLQUFLQyxLQUFLLFFBQVEsc0JBQXNCO0FBQzNELFNBQVNDLGdCQUFnQixFQUFFQyxRQUFRLEVBQUVDLFVBQVUsUUFBUSxZQUFZO0FBRW5FLEtBQUtDLEtBQUssR0FBRztFQUNYQyxJQUFJLEVBQUUsTUFBTTtFQUNa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 \ No newline at end of file diff --git a/components/Spinner/GlimmerMessage.tsx b/components/Spinner/GlimmerMessage.tsx new file mode 100644 index 0000000..255a49c --- /dev/null +++ b/components/Spinner/GlimmerMessage.tsx @@ -0,0 +1,328 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Text, useTheme } from '../../ink.js'; +import { getGraphemeSegmenter } from '../../utils/intl.js'; +import { getTheme, type Theme } from '../../utils/theme.js'; +import type { SpinnerMode } from './types.js'; +import { interpolateColor, parseRGB, toRGBColor } from './utils.js'; +type Props = { + message: string; + mode: SpinnerMode; + messageColor: keyof Theme; + glimmerIndex: number; + flashOpacity: number; + shimmerColor: keyof Theme; + stalledIntensity?: number; +}; +const ERROR_RED = { + r: 171, + g: 43, + b: 63 +}; +export function GlimmerMessage(t0) { + const $ = _c(75); + const { + message, + mode, + messageColor, + glimmerIndex, + flashOpacity, + shimmerColor, + stalledIntensity: t1 + } = t0; + const stalledIntensity = t1 === undefined ? 0 : t1; + const [themeName] = useTheme(); + let messageWidth; + let segments; + let t2; + if ($[0] !== flashOpacity || $[1] !== message || $[2] !== messageColor || $[3] !== mode || $[4] !== shimmerColor || $[5] !== stalledIntensity || $[6] !== themeName) { + t2 = Symbol.for("react.early_return_sentinel"); + bb0: { + const theme = getTheme(themeName); + let segs; + if ($[10] !== message) { + segs = []; + for (const { + segment + } of getGraphemeSegmenter().segment(message)) { + segs.push({ + segment, + width: stringWidth(segment) + }); + } + $[10] = message; + $[11] = segs; + } else { + segs = $[11]; + } + let t3; + if ($[12] !== message) { + t3 = stringWidth(message); + $[12] = message; + $[13] = t3; + } else { + t3 = $[13]; + } + let t4; + if ($[14] !== segs || $[15] !== t3) { + t4 = { + segments: segs, + messageWidth: t3 + }; + $[14] = segs; + $[15] = t3; + $[16] = t4; + } else { + t4 = $[16]; + } + ({ + segments, + messageWidth + } = t4); + if (!message) { + t2 = null; + break bb0; + } + if (stalledIntensity > 0) { + const baseColorStr = theme[messageColor]; + const baseRGB = baseColorStr ? parseRGB(baseColorStr) : null; + if (baseRGB) { + const interpolated = interpolateColor(baseRGB, ERROR_RED, stalledIntensity); + const color = toRGBColor(interpolated); + let t5; + if ($[17] !== color) { + t5 = ; + $[17] = color; + $[18] = t5; + } else { + t5 = $[18]; + } + t2 = <>{message}{t5}; + break bb0; + } + const color_0 = stalledIntensity > 0.5 ? "error" : messageColor; + let t5; + if ($[19] !== color_0 || $[20] !== message) { + t5 = {message}; + $[19] = color_0; + $[20] = message; + $[21] = t5; + } else { + t5 = $[21]; + } + let t6; + if ($[22] !== color_0) { + t6 = ; + $[22] = color_0; + $[23] = t6; + } else { + t6 = $[23]; + } + let t7; + if ($[24] !== t5 || $[25] !== t6) { + t7 = <>{t5}{t6}; + $[24] = t5; + $[25] = t6; + $[26] = t7; + } else { + t7 = $[26]; + } + t2 = t7; + break bb0; + } + if (mode === "tool-use") { + const baseColorStr_0 = theme[messageColor]; + const shimmerColorStr = theme[shimmerColor]; + const baseRGB_0 = baseColorStr_0 ? parseRGB(baseColorStr_0) : null; + const shimmerRGB = shimmerColorStr ? parseRGB(shimmerColorStr) : null; + if (baseRGB_0 && shimmerRGB) { + const interpolated_0 = interpolateColor(baseRGB_0, shimmerRGB, flashOpacity); + const t5 = {message}; + let t6; + if ($[27] !== messageColor) { + t6 = ; + $[27] = messageColor; + $[28] = t6; + } else { + t6 = $[28]; + } + let t7; + if ($[29] !== t5 || $[30] !== t6) { + t7 = <>{t5}{t6}; + $[29] = t5; + $[30] = t6; + $[31] = t7; + } else { + t7 = $[31]; + } + t2 = t7; + break bb0; + } + const color_1 = flashOpacity > 0.5 ? shimmerColor : messageColor; + let t5; + if ($[32] !== color_1 || $[33] !== message) { + t5 = {message}; + $[32] = color_1; + $[33] = message; + $[34] = t5; + } else { + t5 = $[34]; + } + let t6; + if ($[35] !== messageColor) { + t6 = ; + $[35] = messageColor; + $[36] = t6; + } else { + t6 = $[36]; + } + let t7; + if ($[37] !== t5 || $[38] !== t6) { + t7 = <>{t5}{t6}; + $[37] = t5; + $[38] = t6; + $[39] = t7; + } else { + t7 = $[39]; + } + t2 = t7; + break bb0; + } + } + $[0] = flashOpacity; + $[1] = message; + $[2] = messageColor; + $[3] = mode; + $[4] = shimmerColor; + $[5] = stalledIntensity; + $[6] = themeName; + $[7] = messageWidth; + $[8] = segments; + $[9] = t2; + } else { + messageWidth = $[7]; + segments = $[8]; + t2 = $[9]; + } + if (t2 !== Symbol.for("react.early_return_sentinel")) { + return t2; + } + const shimmerStart = glimmerIndex - 1; + const shimmerEnd = glimmerIndex + 1; + if (shimmerStart >= messageWidth || shimmerEnd < 0) { + let t3; + if ($[40] !== message || $[41] !== messageColor) { + t3 = {message}; + $[40] = message; + $[41] = messageColor; + $[42] = t3; + } else { + t3 = $[42]; + } + let t4; + if ($[43] !== messageColor) { + t4 = ; + $[43] = messageColor; + $[44] = t4; + } else { + t4 = $[44]; + } + let t5; + if ($[45] !== t3 || $[46] !== t4) { + t5 = <>{t3}{t4}; + $[45] = t3; + $[46] = t4; + $[47] = t5; + } else { + t5 = $[47]; + } + return t5; + } + const clampedStart = Math.max(0, shimmerStart); + let colPos = 0; + let before = ""; + let shim = ""; + let after = ""; + if ($[48] !== after || $[49] !== before || $[50] !== clampedStart || $[51] !== colPos || $[52] !== segments || $[53] !== shim || $[54] !== shimmerEnd) { + for (const { + segment: segment_0, + width + } of segments) { + if (colPos + width <= clampedStart) { + before = before + segment_0; + } else { + if (colPos > shimmerEnd) { + after = after + segment_0; + } else { + shim = shim + segment_0; + } + } + colPos = colPos + width; + } + $[48] = after; + $[49] = before; + $[50] = clampedStart; + $[51] = colPos; + $[52] = segments; + $[53] = shim; + $[54] = shimmerEnd; + $[55] = before; + $[56] = after; + $[57] = shim; + $[58] = colPos; + } else { + before = $[55]; + after = $[56]; + shim = $[57]; + colPos = $[58]; + } + let t3; + if ($[59] !== before || $[60] !== messageColor) { + t3 = before && {before}; + $[59] = before; + $[60] = messageColor; + $[61] = t3; + } else { + t3 = $[61]; + } + let t4; + if ($[62] !== shim || $[63] !== shimmerColor) { + t4 = {shim}; + $[62] = shim; + $[63] = shimmerColor; + $[64] = t4; + } else { + t4 = $[64]; + } + let t5; + if ($[65] !== after || $[66] !== messageColor) { + t5 = after && {after}; + $[65] = after; + $[66] = messageColor; + $[67] = t5; + } else { + t5 = $[67]; + } + let t6; + if ($[68] !== messageColor) { + t6 = ; + $[68] = messageColor; + $[69] = t6; + } else { + t6 = $[69]; + } + let t7; + if ($[70] !== t3 || $[71] !== t4 || $[72] !== t5 || $[73] !== t6) { + t7 = <>{t3}{t4}{t5}{t6}; + $[70] = t3; + $[71] = t4; + $[72] = t5; + $[73] = t6; + $[74] = t7; + } else { + t7 = $[74]; + } + return t7; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInN0cmluZ1dpZHRoIiwiVGV4dCIsInVzZVRoZW1lIiwiZ2V0R3JhcGhlbWVTZWdtZW50ZXIiLCJnZXRUaGVtZSIsIlRoZW1lIiwiU3Bpbm5lck1vZGUiLCJpbnRlcnBvbGF0ZUNvbG9yIiwicGFyc2VSR0IiLCJ0b1JHQkNvbG9yIiwiUHJvcHMiLCJtZXNzYWdlIiwibW9kZSIsIm1lc3NhZ2VDb2xvciIsImdsaW1tZXJJbmRleCIsImZsYXNoT3BhY2l0eSIsInNoaW1tZXJDb2xvciIsInN0YWxsZWRJbnRlbnNpdHkiLCJFUlJPUl9SRUQiLCJyIiwiZyIsImIiLCJHbGltbWVyTWVzc2FnZSIsInQwIiwiJCIsIl9jIiwidDEiLCJ1bmRlZmluZWQiLCJ0aGVtZU5hbWUiLCJtZXNzYWdlV2lkdGgiLCJzZWdtZW50cyIsInQyIiwiU3ltYm9sIiwiZm9yIiwiYmIwIiwidGhlbWUiLCJzZWdzIiwic2VnbWVudCIsInB1c2giLCJ3aWR0aCIsInQzIiwidDQiLCJiYXNlQ29sb3JTdHIiLCJiYXNlUkdCIiwiaW50ZXJwb2xhdGVkIiwiY29sb3IiLCJ0NSIsImNvbG9yXzAiLCJ0NiIsInQ3IiwiYmFzZUNvbG9yU3RyXzAiLCJzaGltbWVyQ29sb3JTdHIiLCJiYXNlUkdCXzAiLCJzaGltbWVyUkdCIiwiaW50ZXJwb2xhdGVkXzAiLCJjb2xvcl8xIiwic2hpbW1lclN0YXJ0Iiwic2hpbW1lckVuZCIsImNsYW1wZWRTdGFydCIsIk1hdGgiLCJtYXgiLCJjb2xQb3MiLCJiZWZvcmUiLCJzaGltIiwiYWZ0ZXIiLCJzZWdtZW50XzAiXSwic291cmNlcyI6WyJHbGltbWVyTWVzc2FnZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBzdHJpbmdXaWR0aCB9IGZyb20gJy4uLy4uL2luay9zdHJpbmdXaWR0aC5qcydcbmltcG9ydCB7IFRleHQsIHVzZVRoZW1lIH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgZ2V0R3JhcGhlbWVTZWdtZW50ZXIgfSBmcm9tICcuLi8uLi91dGlscy9pbnRsLmpzJ1xuaW1wb3J0IHsgZ2V0VGhlbWUsIHR5cGUgVGhlbWUgfSBmcm9tICcuLi8uLi91dGlscy90aGVtZS5qcydcbmltcG9ydCB0eXBlIHsgU3Bpbm5lck1vZGUgfSBmcm9tICcuL3R5cGVzLmpzJ1xuaW1wb3J0IHsgaW50ZXJwb2xhdGVDb2xvciwgcGFyc2VSR0IsIHRvUkdCQ29sb3IgfSBmcm9tICcuL3V0aWxzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBtZXNzYWdlOiBzdHJpbmdcbiAgbW9kZTogU3Bpbm5lck1vZGVcbiAgbWVzc2FnZUNvbG9yOiBrZXlvZiBUaGVtZVxuICBnbGltbWVySW5kZXg6IG51bWJlclxuICBmbGFzaE9wYWNpdHk6IG51bWJlclxuICBzaGltbWVyQ29sb3I6IGtleW9mIFRoZW1lXG4gIHN0YWxsZWRJbnRlbnNpdHk/OiBudW1iZXJcbn1cblxuY29uc3QgRVJST1JfUkVEID0geyByOiAxNzEsIGc6IDQzLCBiOiA2MyB9XG5cbmV4cG9ydCBmdW5jdGlvbiBHbGltbWVyTWVzc2FnZSh7XG4gIG1lc3NhZ2UsXG4gIG1vZGUsXG4gIG1lc3NhZ2VDb2xvcixcbiAgZ2xpbW1lckluZGV4LFxuICBmbGFzaE9wYWNpdHksXG4gIHNoaW1tZXJDb2xvcixcbiAgc3RhbGxlZEludGVuc2l0eSA9IDAsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZU5hbWVdID0gdXNlVGhlbWUoKVxuICBjb25zdCB0aGVtZSA9IGdldFRoZW1lKHRoZW1lTmFtZSlcblxuICAvLyBUaGlzIGNvbXBvbmVudCByZS1yZW5kZXJzIGF0IDIwZnBzIChnbGltbWVySW5kZXggY2hhbmdlcyBldmVyeSA1MG1zKSBidXRcbiAgLy8gbWVzc2FnZSBpcyBzdGFibGUgd2l0aGluIGEgdHVybi4gUHJlY29tcHV0ZSBncmFwaGVtZSBzZWdtZW50YXRpb24gKyB3aWR0aHNcbiAgLy8gb25jZSBwZXIgbWVzc2FnZSBpbnN0ZWFkIG9mIHBlciBmcmFtZS4gTWVhc3VyZWQgLTgxJSBvbiB0aGUgc2hpbW1lciBwYXRoLlxuICBjb25zdCB7IHNlZ21lbnRzLCBtZXNzYWdlV2lkdGggfSA9IFJlYWN0LnVzZU1lbW8oKCkgPT4ge1xuICAgIGNvbnN0IHNlZ3M6IHsgc2VnbWVudDogc3RyaW5nOyB3aWR0aDogbnVtYmVyIH1bXSA9IFtdXG4gICAgZm9yIChjb25zdCB7IHNlZ21lbnQgfSBvZiBnZXRHcmFwaGVtZVNlZ21lbnRlcigpLnNlZ21lbnQobWVzc2FnZSkpIHtcbiAgICAgIHNlZ3MucHVzaCh7IHNlZ21lbnQsIHdpZHRoOiBzdHJpbmdXaWR0aChzZWdtZW50KSB9KVxuICAgIH1cbiAgICByZXR1cm4geyBzZWdtZW50czogc2VncywgbWVzc2FnZVdpZHRoOiBzdHJpbmdXaWR0aChtZXNzYWdlKSB9XG4gIH0sIFttZXNzYWdlXSlcblxuICBpZiAoIW1lc3NhZ2UpIHJldHVybiBudWxsXG5cbiAgLy8gV2hlbiBzdGFsbGVkLCBzaG93IHRleHQgdGhhdCBzbW9vdGhseSB0cmFuc2l0aW9ucyB0byByZWRcbiAgaWYgKHN0YWxsZWRJbnRlbnNpdHkgPiAwKSB7XG4gICAgY29uc3QgYmFzZUNvbG9yU3RyID0gdGhlbWVbbWVzc2FnZUNvbG9yXVxuICAgIGNvbnN0IGJhc2VSR0IgPSBiYXNlQ29sb3JTdHIgPyBwYXJzZVJHQihiYXNlQ29sb3JTdHIpIDogbnVsbFxuXG4gICAgaWYgKGJhc2VSR0IpIHtcbiAgICAgIGNvbnN0IGludGVycG9sYXRlZCA9IGludGVycG9sYXRlQ29sb3IoXG4gICAgICAgIGJhc2VSR0IsXG4gICAgICAgIEVSUk9SX1JFRCxcbiAgICAgICAgc3RhbGxlZEludGVuc2l0eSxcbiAgICAgIClcbiAgICAgIGNvbnN0IGNvbG9yID0gdG9SR0JDb2xvcihpbnRlcnBvbGF0ZWQpXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPXtjb2xvcn0+e21lc3NhZ2V9PC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPXtjb2xvcn0+IDwvVGV4dD5cbiAgICAgICAgPC8+XG4gICAgICApXG4gICAgfVxuXG4gICAgLy8gRmFsbGJhY2sgZm9yIEFOU0kgdGhlbWVzOiB1c2UgbWVzc2FnZUNvbG9yIHVudGlsIGZ1bGx5IHN0YWxsZWQsIHRoZW4gZXJyb3JcbiAgICBjb25zdCBjb2xvciA9IHN0YWxsZWRJbnRlbnNpdHkgPiAwLjUgPyAnZXJyb3InIDogbWVzc2FnZUNvbG9yXG4gICAgcmV0dXJuIChcbiAgICAgIDw+XG4gICAgICAgIDxUZXh0IGNvbG9yPXtjb2xvcn0+e21lc3NhZ2V9PC9UZXh0PlxuICAgICAgICA8VGV4dCBjb2xvcj17Y29sb3J9PiA8L1RleHQ+XG4gICAgICA8Lz5cbiAgICApXG4gIH1cblxuICAvLyB0b29sLXVzZSBtb2RlOiBhbGwgY2hhcnMgZmxhc2ggd2l0aCB0aGUgc2FtZSBvcGFjaXR5LCBzbyByZW5kZXIgYXMgYVxuICAvLyBzaW5nbGUgPFRleHQ+IGluc3RlYWQgb2YgTiBpbmRpdmlkdWFsIEZsYXNoaW5nQ2hhciBjb21wb25lbnRzLlxuICBpZiAobW9kZSA9PT0gJ3Rvb2wtdXNlJykge1xuICAgIGNvbnN0IGJhc2VDb2xvclN0ciA9IHRoZW1lW21lc3NhZ2VDb2xvcl1cbiAgICBjb25zdCBzaGltbWVyQ29sb3JTdHIgPSB0aGVtZVtzaGltbWVyQ29sb3JdXG4gICAgY29uc3QgYmFzZVJHQiA9IGJhc2VDb2xvclN0ciA/IHBhcnNlUkdCKGJhc2VDb2xvclN0cikgOiBudWxsXG4gICAgY29uc3Qgc2hpbW1lclJHQiA9IHNoaW1tZXJDb2xvclN0ciA/IHBhcnNlUkdCKHNoaW1tZXJDb2xvclN0cikgOiBudWxsXG5cbiAgICBpZiAoYmFzZVJHQiAmJiBzaGltbWVyUkdCKSB7XG4gICAgICBjb25zdCBpbnRlcnBvbGF0ZWQgPSBpbnRlcnBvbGF0ZUNvbG9yKGJhc2VSR0IsIHNoaW1tZXJSR0IsIGZsYXNoT3BhY2l0eSlcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDw+XG4gICAgICAgICAgPFRleHQgY29sb3I9e3RvUkdCQ29sb3IoaW50ZXJwb2xhdGVkKX0+e21lc3NhZ2V9PC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPXttZXNzYWdlQ29sb3J9PiA8L1RleHQ+XG4gICAgICAgIDwvPlxuICAgICAgKVxuICAgIH1cblxuICAgIGNvbnN0IGNvbG9yID0gZmxhc2hPcGFjaXR5ID4gMC41ID8gc2hpbW1lckNvbG9yIDogbWVzc2FnZUNvbG9yXG4gICAgcmV0dXJuIChcbiAgICAgIDw+XG4gICAgICAgIDxUZXh0IGNvbG9yPXtjb2xvcn0+e21lc3NhZ2V9PC9UZXh0PlxuICAgICAgICA8VGV4dCBjb2xvcj17bWVzc2FnZUNvbG9yfT4gPC9UZXh0PlxuICAgICAgPC8+XG4gICAgKVxuICB9XG5cbiAgLy8gU2hpbW1lciBtb2RlOiBvbmx5IGNoYXJzIHdpdGhpbiDCsTEgb2YgZ2xpbW1lckluZGV4IG5lZWQgdGhlIHNoaW1tZXJcbiAgLy8gY29sb3IuIFdoZW4gZ2xpbW1lciBpcyBvZmZzY3JlZW4sIHJlbmRlciBhcyBhIHNpbmdsZSA8VGV4dD4uXG4gIGNvbnN0IHNoaW1tZXJTdGFydCA9IGdsaW1tZXJJbmRleCAtIDFcbiAgY29uc3Qgc2hpbW1lckVuZCA9IGdsaW1tZXJJbmRleCArIDFcblxuICBpZiAoc2hpbW1lclN0YXJ0ID49IG1lc3NhZ2VXaWR0aCB8fCBzaGltbWVyRW5kIDwgMCkge1xuICAgIHJldHVybiAoXG4gICAgICA8PlxuICAgICAgICA8VGV4dCBjb2xvcj17bWVzc2FnZUNvbG9yfT57bWVzc2FnZX08L1RleHQ+XG4gICAgICAgIDxUZXh0IGNvbG9yPXttZXNzYWdlQ29sb3J9PiA8L1RleHQ+XG4gICAgICA8Lz5cbiAgICApXG4gIH1cblxuICAvLyBTcGxpdCBpbnRvIGF0IG1vc3QgMyBzZWdtZW50cyBieSB2aXN1YWwgY29sdW1uIHBvc2l0aW9uXG4gIGNvbnN0IGNsYW1wZWRTdGFydCA9IE1hdGgubWF4KDAsIHNoaW1tZXJTdGFydClcbiAgbGV0IGNvbFBvcyA9IDBcbiAgbGV0IGJlZm9yZSA9ICcnXG4gIGxldCBzaGltID0gJydcbiAgbGV0IGFmdGVyID0gJydcbiAgZm9yIChjb25zdCB7IHNlZ21lbnQsIHdpZHRoIH0gb2Ygc2VnbWVudHMpIHtcbiAgICBpZiAoY29sUG9zICsgd2lkdGggPD0gY2xhbXBlZFN0YXJ0KSB7XG4gICAgICBiZWZvcmUgKz0gc2VnbWVudFxuICAgIH0gZWxzZSBpZiAoY29sUG9zID4gc2hpbW1lckVuZCkge1xuICAgICAgYWZ0ZXIgKz0gc2VnbWVudFxuICAgIH0gZWxzZSB7XG4gICAgICBzaGltICs9IHNlZ21lbnRcbiAgICB9XG4gICAgY29sUG9zICs9IHdpZHRoXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICB7YmVmb3JlICYmIDxUZXh0IGNvbG9yPXttZXNzYWdlQ29sb3J9PntiZWZvcmV9PC9UZXh0Pn1cbiAgICAgIDxUZXh0IGNvbG9yPXtzaGltbWVyQ29sb3J9PntzaGltfTwvVGV4dD5cbiAgICAgIHthZnRlciAmJiA8VGV4dCBjb2xvcj17bWVzc2FnZUNvbG9yfT57YWZ0ZXJ9PC9UZXh0Pn1cbiAgICAgIDxUZXh0IGNvbG9yPXttZXNzYWdlQ29sb3J9PiA8L1RleHQ+XG4gICAgPC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsV0FBVyxRQUFRLDBCQUEwQjtBQUN0RCxTQUFTQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxjQUFjO0FBQzdDLFNBQVNDLG9CQUFvQixRQUFRLHFCQUFxQjtBQUMxRCxTQUFTQyxRQUFRLEVBQUUsS0FBS0MsS0FBSyxRQUFRLHNCQUFzQjtBQUMzRCxjQUFjQyxXQUFXLFFBQVEsWUFBWTtBQUM3QyxTQUFTQyxnQkFBZ0IsRUFBRUMsUUFBUSxFQUFFQyxVQUFVLFFBQVEsWUFBWTtBQUVuRSxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsT0FBTyxFQUFFLE1B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 \ No newline at end of file diff --git a/components/Spinner/ShimmerChar.tsx b/components/Spinner/ShimmerChar.tsx new file mode 100644 index 0000000..dd3a8ed --- /dev/null +++ b/components/Spinner/ShimmerChar.tsx @@ -0,0 +1,36 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Text } from '../../ink.js'; +import type { Theme } from '../../utils/theme.js'; +type Props = { + char: string; + index: number; + glimmerIndex: number; + messageColor: keyof Theme; + shimmerColor: keyof Theme; +}; +export function ShimmerChar(t0) { + const $ = _c(3); + const { + char, + index, + glimmerIndex, + messageColor, + shimmerColor + } = t0; + const isHighlighted = index === glimmerIndex; + const isNearHighlight = Math.abs(index - glimmerIndex) === 1; + const shouldUseShimmer = isHighlighted || isNearHighlight; + const t1 = shouldUseShimmer ? shimmerColor : messageColor; + let t2; + if ($[0] !== char || $[1] !== t1) { + t2 = {char}; + $[0] = char; + $[1] = t1; + $[2] = t2; + } else { + t2 = $[2]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJUaGVtZSIsIlByb3BzIiwiY2hhciIsImluZGV4IiwiZ2xpbW1lckluZGV4IiwibWVzc2FnZUNvbG9yIiwic2hpbW1lckNvbG9yIiwiU2hpbW1lckNoYXIiLCJ0MCIsIiQiLCJfYyIsImlzSGlnaGxpZ2h0ZWQiLCJpc05lYXJIaWdobGlnaHQiLCJNYXRoIiwiYWJzIiwic2hvdWxkVXNlU2hpbW1lciIsInQxIiwidDIiXSwic291cmNlcyI6WyJTaGltbWVyQ2hhci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJy4uLy4uL3V0aWxzL3RoZW1lLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBjaGFyOiBzdHJpbmdcbiAgaW5kZXg6IG51bWJlclxuICBnbGltbWVySW5kZXg6IG51bWJlclxuICBtZXNzYWdlQ29sb3I6IGtleW9mIFRoZW1lXG4gIHNoaW1tZXJDb2xvcjoga2V5b2YgVGhlbWVcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFNoaW1tZXJDaGFyKHtcbiAgY2hhcixcbiAgaW5kZXgsXG4gIGdsaW1tZXJJbmRleCxcbiAgbWVzc2FnZUNvbG9yLFxuICBzaGltbWVyQ29sb3IsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGlzSGlnaGxpZ2h0ZWQgPSBpbmRleCA9PT0gZ2xpbW1lckluZGV4XG4gIGNvbnN0IGlzTmVhckhpZ2hsaWdodCA9IE1hdGguYWJzKGluZGV4IC0gZ2xpbW1lckluZGV4KSA9PT0gMVxuICBjb25zdCBzaG91bGRVc2VTaGltbWVyID0gaXNIaWdobGlnaHRlZCB8fCBpc05lYXJIaWdobGlnaHRcblxuICByZXR1cm4gKFxuICAgIDxUZXh0IGNvbG9yPXtzaG91bGRVc2VTaGltbWVyID8gc2hpbW1lckNvbG9yIDogbWVzc2FnZUNvbG9yfT57Y2hhcn08L1RleHQ+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsSUFBSSxRQUFRLGNBQWM7QUFDbkMsY0FBY0MsS0FBSyxRQUFRLHNCQUFzQjtBQUVqRCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsSUFBSSxFQUFFLE1BQU07RUFDWkMsS0FBSyxFQUFFLE1BQU07RUFDYkMsWUFBWSxFQUFFLE1BQU07RUFDcEJDLFlBQVksRUFBRSxNQUFNTCxLQUFLO0VBQ3pCTSxZQUFZLEVBQUUsTUFBTU4sS0FBSztBQUMzQixDQUFDO0FBRUQsT0FBTyxTQUFBTyxZQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXFCO0lBQUFSLElBQUE7SUFBQUMsS0FBQTtJQUFBQyxZQUFBO0lBQUFDLFlBQUE7SUFBQUM7RUFBQSxJQUFBRSxFQU1wQjtFQUNOLE1BQUFHLGFBQUEsR0FBc0JSLEtBQUssS0FBS0MsWUFBWTtFQUM1QyxNQUFBUSxlQUFBLEdBQXdCQyxJQUFJLENBQUFDLEdBQUksQ0FBQ1gsS0FBSyxHQUFHQyxZQUFZLENBQUMsS0FBSyxDQUFDO0VBQzVELE1BQUFXLGdCQUFBLEdBQXlCSixhQUFnQyxJQUFoQ0MsZUFBZ0M7RUFHMUMsTUFBQUksRUFBQSxHQUFBRCxnQkFBZ0IsR0FBaEJULFlBQThDLEdBQTlDRCxZQUE4QztFQUFBLElBQUFZLEVBQUE7RUFBQSxJQUFBUixDQUFBLFFBQUFQLElBQUEsSUFBQU8sQ0FBQSxRQUFBTyxFQUFBO0lBQTNEQyxFQUFBLElBQUMsSUFBSSxDQUFRLEtBQThDLENBQTlDLENBQUFELEVBQTZDLENBQUMsQ0FBR2QsS0FBRyxDQUFFLEVBQWxFLElBQUksQ0FBcUU7SUFBQU8sQ0FBQSxNQUFBUCxJQUFBO0lBQUFPLENBQUEsTUFBQU8sRUFBQTtJQUFBUCxDQUFBLE1BQUFRLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFSLENBQUE7RUFBQTtFQUFBLE9BQTFFUSxFQUEwRTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/Spinner/SpinnerAnimationRow.tsx b/components/Spinner/SpinnerAnimationRow.tsx new file mode 100644 index 0000000..4e77bf9 --- /dev/null +++ b/components/Spinner/SpinnerAnimationRow.tsx @@ -0,0 +1,265 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import * as React from 'react'; +import { useMemo, useRef } from 'react'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, Text, useAnimationFrame } from '../../ink.js'; +import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js'; +import { formatDuration, formatNumber } from '../../utils/format.js'; +import { toInkColor } from '../../utils/ink.js'; +import type { Theme } from '../../utils/theme.js'; +import { Byline } from '../design-system/Byline.js'; +import { GlimmerMessage } from './GlimmerMessage.js'; +import { SpinnerGlyph } from './SpinnerGlyph.js'; +import type { SpinnerMode } from './types.js'; +import { useStalledAnimation } from './useStalledAnimation.js'; +import { interpolateColor, toRGBColor } from './utils.js'; +const SEP_WIDTH = stringWidth(' · '); +const THINKING_BARE_WIDTH = stringWidth('thinking'); +const SHOW_TOKENS_AFTER_MS = 30_000; + +// Thinking shimmer constants. Previously lived in a separate ThinkingShimmerText +// component with its own useAnimationFrame(50) — inlined here to reuse our +// existing 50ms clock and eliminate the redundant subscriber. +const THINKING_INACTIVE = { + r: 153, + g: 153, + b: 153 +}; +const THINKING_INACTIVE_SHIMMER = { + r: 185, + g: 185, + b: 185 +}; +const THINKING_DELAY_MS = 3000; +const THINKING_GLOW_PERIOD_S = 2; +export type SpinnerAnimationRowProps = { + // Animation inputs + mode: SpinnerMode; + reducedMotion: boolean; + hasActiveTools: boolean; + responseLengthRef: React.RefObject; + + // Message (stable within a turn) + message: string; + messageColor: keyof Theme; + shimmerColor: keyof Theme; + overrideColor?: keyof Theme | null; + + // Timer refs (stable references) + loadingStartTimeRef: React.RefObject; + totalPausedMsRef: React.RefObject; + pauseStartTimeRef: React.RefObject; + + // Display flags + spinnerSuffix?: string | null; + verbose: boolean; + columns: number; + + // Teammate-derived (computed by parent from tasks) + hasRunningTeammates: boolean; + teammateTokens: number; + foregroundedTeammate: InProcessTeammateTaskState | undefined; + /** Leader's turn has completed. Suppresses stall-red since responseLengthRef/hasActiveTools track leader state only. */ + leaderIsIdle?: boolean; + + // Thinking (state owned by parent, mode-dependent) + thinkingStatus: 'thinking' | number | null; + effortSuffix: string; +}; + +/** + * The 50ms-animated portion of SpinnerWithVerb. Owns useAnimationFrame(50) + * and all values derived from the animation clock (frame, glimmer, token + * counter animation, elapsed-time, stalled intensity, thinking shimmer). + * + * The parent SpinnerWithVerb is freed from the 50ms render loop and only + * re-renders when its props/app state change (~25x/turn instead of ~383x). + * That keeps the outer Box shells, useAppState selectors, task filtering, + * and tip/tree subtrees out of the hot animation path. + */ +export function SpinnerAnimationRow({ + mode, + reducedMotion, + hasActiveTools, + responseLengthRef, + message, + messageColor, + shimmerColor, + overrideColor, + loadingStartTimeRef, + totalPausedMsRef, + pauseStartTimeRef, + spinnerSuffix, + verbose, + columns, + hasRunningTeammates, + teammateTokens, + foregroundedTeammate, + leaderIsIdle = false, + thinkingStatus, + effortSuffix +}: SpinnerAnimationRowProps): React.ReactNode { + const [viewportRef, time] = useAnimationFrame(reducedMotion ? null : 50); + + // === Elapsed time (wall-clock, derived from refs each frame) === + const now = Date.now(); + const elapsedTimeMs = pauseStartTimeRef.current !== null ? pauseStartTimeRef.current - loadingStartTimeRef.current - totalPausedMsRef.current : now - loadingStartTimeRef.current - totalPausedMsRef.current; + + // Track wall-clock turn start for teammates. While a swarm is running the + // leader's elapsedTimeMs may jump around (new API calls reset + // loadingStartTimeRef; pauses freeze it), so we anchor to the earliest + // derived start seen so far. When no teammates are running this just tracks + // derivedStart every frame, effectively resetting for the next swarm. + const derivedStart = now - elapsedTimeMs; + const turnStartRef = useRef(derivedStart); + if (!hasRunningTeammates || derivedStart < turnStartRef.current) { + turnStartRef.current = derivedStart; + } + + // === Animation derivations from `time` === + const currentResponseLength = responseLengthRef.current; + + // Suppress stall detection when leader is idle — responseLengthRef and + // hasActiveTools both track leader state. When viewing an active teammate + // while leader is idle, they'd otherwise flag a false stall after 3s. + // Treating leaderIsIdle like hasActiveTools resets the stall timer. + const { + isStalled, + stalledIntensity + } = useStalledAnimation(time, currentResponseLength, hasActiveTools || leaderIsIdle, reducedMotion); + const frame = reducedMotion ? 0 : Math.floor(time / 120); + const glimmerSpeed = mode === 'requesting' ? 50 : 200; + // message is stable within a turn; stringWidth is expensive enough (Bun native + // call per code point) to memoize explicitly across the 50ms loop. + const glimmerMessageWidth = useMemo(() => stringWidth(message), [message]); + const cycleLength = glimmerMessageWidth + 20; + const cyclePosition = Math.floor(time / glimmerSpeed); + const glimmerIndex = reducedMotion ? -100 : isStalled ? -100 : mode === 'requesting' ? cyclePosition % cycleLength - 10 : glimmerMessageWidth + 10 - cyclePosition % cycleLength; + const flashOpacity = reducedMotion ? 0 : mode === 'tool-use' ? (Math.sin(time / 1000 * Math.PI) + 1) / 2 : 0; + + // === Token counter animation (smooth increment, driven by 50ms clock) === + const tokenCounterRef = useRef(currentResponseLength); + if (reducedMotion) { + tokenCounterRef.current = currentResponseLength; + } else { + const gap = currentResponseLength - tokenCounterRef.current; + if (gap > 0) { + let increment; + if (gap < 70) { + increment = 3; + } else if (gap < 200) { + increment = Math.max(8, Math.ceil(gap * 0.15)); + } else { + increment = 50; + } + tokenCounterRef.current = Math.min(tokenCounterRef.current + increment, currentResponseLength); + } + } + const displayedResponseLength = tokenCounterRef.current; + const leaderTokens = Math.round(displayedResponseLength / 4); + const effectiveElapsedMs = hasRunningTeammates ? Math.max(elapsedTimeMs, now - turnStartRef.current) : elapsedTimeMs; + const timerText = formatDuration(effectiveElapsedMs); + const timerWidth = stringWidth(timerText); + + // === Token count (leader + teammates, or foregrounded teammate) === + const totalTokens = foregroundedTeammate && !foregroundedTeammate.isIdle ? foregroundedTeammate.progress?.tokenCount ?? 0 : leaderTokens + teammateTokens; + const tokenCount = formatNumber(totalTokens); + const tokensText = hasRunningTeammates ? `${tokenCount} tokens` : `${figures.arrowDown} ${tokenCount} tokens`; + const tokensWidth = stringWidth(tokensText); + + // === Thinking text (may shrink to fit) === + let thinkingText = thinkingStatus === 'thinking' ? `thinking${effortSuffix}` : typeof thinkingStatus === 'number' ? `thought for ${Math.max(1, Math.round(thinkingStatus / 1000))}s` : null; + let thinkingWidthValue = thinkingText ? stringWidth(thinkingText) : 0; + + // === Progressive width gating === + const messageWidth = glimmerMessageWidth + 2; + const sep = SEP_WIDTH; + const wantsThinking = thinkingStatus !== null; + const wantsTimerAndTokens = verbose || hasRunningTeammates || effectiveElapsedMs > SHOW_TOKENS_AFTER_MS; + const availableSpace = columns - messageWidth - 5; + let showThinking = wantsThinking && availableSpace > thinkingWidthValue; + if (!showThinking && wantsThinking && thinkingStatus === 'thinking' && effortSuffix) { + if (availableSpace > THINKING_BARE_WIDTH) { + thinkingText = 'thinking'; + thinkingWidthValue = THINKING_BARE_WIDTH; + showThinking = true; + } + } + const usedAfterThinking = showThinking ? thinkingWidthValue + sep : 0; + const showTimer = wantsTimerAndTokens && availableSpace > usedAfterThinking + timerWidth; + const usedAfterTimer = usedAfterThinking + (showTimer ? timerWidth + sep : 0); + const showTokens = wantsTimerAndTokens && totalTokens > 0 && availableSpace > usedAfterTimer + tokensWidth; + const thinkingOnly = showThinking && thinkingStatus === 'thinking' && !spinnerSuffix && !showTimer && !showTokens && true; + + // === Thinking shimmer color (formerly ThinkingShimmerText's own timer) === + // Same sine-wave opacity, but derived from our shared `time` instead of a + // second useAnimationFrame(50) subscription. + const thinkingElapsedSec = (time - THINKING_DELAY_MS) / 1000; + const thinkingOpacity = time < THINKING_DELAY_MS ? 0 : (Math.sin(thinkingElapsedSec * Math.PI * 2 / THINKING_GLOW_PERIOD_S) + 1) / 2; + const thinkingShimmerColor = toRGBColor(interpolateColor(THINKING_INACTIVE, THINKING_INACTIVE_SHIMMER, thinkingOpacity)); + + // === Build status parts === + const parts = [...(spinnerSuffix ? [ + {spinnerSuffix} + ] : []), ...(showTimer ? [ + {timerText} + ] : []), ...(showTokens ? [ + {!hasRunningTeammates && } + {tokenCount} tokens + ] : []), ...(showThinking && thinkingText ? [thinkingStatus === 'thinking' && !reducedMotion ? + {thinkingOnly ? `(${thinkingText})` : thinkingText} + : + {thinkingText} + ] : [])]; + const status = foregroundedTeammate && !foregroundedTeammate.isIdle ? <> + (esc to interrupt + + {foregroundedTeammate.identity.agentName} + + ) + : !foregroundedTeammate && parts.length > 0 ? thinkingOnly ? {parts} : <> + ( + {parts} + ) + : null; + return + + + {status} + ; +} +function SpinnerModeGlyph(t0) { + const $ = _c(2); + const { + mode + } = t0; + switch (mode) { + case "tool-input": + case "tool-use": + case "responding": + case "thinking": + { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {figures.arrowDown}; + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; + } + case "requesting": + { + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {figures.arrowUp}; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VNZW1vIiwidXNlUmVmIiwic3RyaW5nV2lkdGgiLCJCb3giLCJUZXh0IiwidXNlQW5pbWF0aW9uRnJhbWUiLCJJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZSIsImZvcm1hdER1cmF0aW9uIiwiZm9ybWF0TnVtYmVyIiwidG9JbmtDb2xvciIsIlRoZW1lIiwiQnlsaW5lIiwiR2xpbW1lck1lc3NhZ2UiLCJTcGlubmVyR2x5cGgiLCJTcGlubmVyTW9kZSIsInVzZVN0YWxsZWRBbmltYXRpb24iLCJpbnRlcnBvbGF0ZUNvbG9yIiwidG9SR0JDb2xvciIsIlNFUF9XSURUSCIsIlRISU5LSU5HX0JBUkVfV0lEVEgiLCJTSE9XX1RPS0VOU19BRlRFUl9NUyIsIlRISU5LSU5HX0lOQUNUSVZFIiwiciIsImciLCJiIiwiVEhJTktJTkdfSU5BQ1RJVkVfU0hJTU1FUiIsIlRISU5LSU5HX0RFTEFZX01TIiwiVEhJTktJTkdfR0xPV19QRVJJT0RfUyIsIlNwaW5uZXJBbmltYXRpb25Sb3dQcm9wcyIsIm1vZGUiLCJyZWR1Y2VkTW90aW9uIiwiaGFzQWN0aXZlVG9vbHMiLCJyZXNwb25zZUxlbmd0aFJlZiIsIlJlZk9iamVjdCIsIm1lc3NhZ2UiLCJtZXNzYWdlQ29sb3IiLCJzaGltbWVyQ29sb3IiLCJvdmVycmlkZUNvbG9yIiwibG9hZGluZ1N0YXJ0VGltZVJlZiIsInRvdGFsUGF1c2VkTXNSZWYiLCJwYXVzZVN0YXJ0VGltZVJlZiIsInNwaW5uZXJTdWZmaXgiLCJ2ZXJib3NlIiwiY29sdW1ucyIsImhhc1J1bm5pbmdUZWFtbWF0ZXMiLCJ0ZWFtbWF0ZVRva2VucyIsImZvcmVncm91bmRlZFRlYW1tYXRlIiwibGVhZGVySXNJZGxlIiwidGhpbmtpbmdTdGF0dXMiLCJlZmZvcnRTdWZmaXgiLCJTcGlubmVyQW5pbWF0aW9uUm93IiwiUmVhY3ROb2RlIiwidmlld3BvcnRSZWYiLCJ0aW1lIiwibm93IiwiRGF0ZSIsImVsYXBzZWRUaW1lTXMiLCJjdXJyZW50IiwiZGVyaXZlZFN0YXJ0IiwidHVyblN0YXJ0UmVmIiwiY3VycmVudFJlc3BvbnNlTGVuZ3RoIiwiaXNTdGFsbGVkIiwic3RhbGxlZEludGVuc2l0eSIsImZyYW1lIiwiTWF0aCIsImZsb29yIiwiZ2xpbW1lclNwZWVkIiwiZ2xpbW1lck1lc3NhZ2VXaWR0aCIsImN5Y2xlTGVuZ3RoIiwiY3ljbGVQb3NpdGlvbiIsImdsaW1tZXJJbmRleCIsImZsYXNoT3BhY2l0eSIsInNpbiIsIlBJIiwidG9rZW5Db3VudGVyUmVmIiwiZ2FwIiwiaW5jcmVtZW50IiwibWF4IiwiY2VpbCIsIm1pbiIsImRpc3BsYXllZFJlc3BvbnNlTGVuZ3RoIiwibGVhZGVyVG9rZW5zIiwicm91bmQiLCJlZmZlY3RpdmVFbGFwc2VkTXMiLCJ0aW1lclRleHQiLCJ0aW1lcldpZHRoIiwidG90YWxUb2tlbnMiLCJpc0lkbGUiLCJwcm9ncmVzcyIsInRva2VuQ291bnQiLCJ0b2tlbnNUZXh0IiwiYXJyb3dEb3duIiwidG9rZW5zV2lkdGgiLCJ0aGlua2luZ1RleHQiLCJ0aGlua2luZ1dpZHRoVmFsdWUiLCJtZXNzYWdlV2lkdGgiLCJzZXAiLCJ3YW50c1RoaW5raW5nIiwid2FudHNUaW1lckFuZFRva2VucyIsImF2YWlsYWJsZVNwYWNlIiwic2hvd1RoaW5raW5nIiwidXNlZEFmdGVyVGhpbmtpbmciLCJzaG93VGltZXIiLCJ1c2VkQWZ0ZXJUaW1lciIsInNob3dUb2tlbnMiLCJ0aGlua2luZ09ubHkiLCJ0aGlua2luZ0VsYXBzZWRTZWMiLCJ0aGlua2luZ09wYWNpdHkiLCJ0aGlua2luZ1NoaW1tZXJDb2xvciIsInBhcnRzIiwic3RhdHVzIiwiaWRlbnRpdHkiLCJjb2xvciIsImFnZW50TmFtZSIsImxlbmd0aCIsIlNwaW5uZXJNb2RlR2x5cGgiLCJ0MCIsIiQiLCJfYyIsInQxIiwiU3ltYm9sIiwiZm9yIiwiYXJyb3dVcCJdLCJzb3VyY2VzIjpbIlNwaW5uZXJBbmltYXRpb25Sb3cudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZU1lbW8sIHVzZVJlZiB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi8uLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyBCb3gsIFRleHQsIHVzZUFuaW1hdGlvbkZyYW1lIH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZSB9IGZyb20gJy4uLy4uL3Rhc2tzL0luUHJvY2Vzc1RlYW1tYXRlVGFzay90eXBlcy5qcydcbmltcG9ydCB7IGZvcm1hdER1cmF0aW9uLCBmb3JtYXROdW1iZXIgfSBmcm9tICcuLi8uLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQgeyB0b0lua0NvbG9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJy4uLy4uL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBHbGltbWVyTWVzc2FnZSB9IGZyb20gJy4vR2xpbW1lck1lc3NhZ2UuanMnXG5pbXBvcnQgeyBTcGlubmVyR2x5cGggfSBmcm9tICcuL1NwaW5uZXJHbHlwaC5qcydcbmltcG9ydCB0eXBlIHsgU3Bpbm5lck1vZGUgfSBmcm9tICcuL3R5cGVzLmpzJ1xuaW1wb3J0IHsgdXNlU3RhbGxlZEFuaW1hdGlvbiB9IGZyb20gJy4vdXNlU3RhbGxlZEFuaW1hdGlvbi5qcydcbmltcG9ydCB7IGludGVycG9sYXRlQ29sb3IsIHRvUkdCQ29sb3IgfSBmcm9tICcuL3V0aWxzLmpzJ1xuXG5jb25zdCBTRVBfV0lEVEggPSBzdHJpbmdXaWR0aCgnIMK3ICcpXG5jb25zdCBUSElOS0lOR19CQVJFX1dJRFRIID0gc3RyaW5nV2lkdGgoJ3RoaW5raW5nJylcbmNvbnN0IFNIT1dfVE9LRU5TX0FGVEVSX01TID0gMzBfMDAwXG5cbi8vIFRoaW5raW5nIHNoaW1tZXIgY29uc3RhbnRzLiBQcmV2aW91c2x5IGxpdmVkIGluIGEgc2VwYXJhdGUgVGhpbmtpbmdTaGltbWVyVGV4dFxuLy8gY29tcG9uZW50IHdpdGggaXRzIG93biB1c2VBbmltYXRpb25GcmFtZSg1MCkg4oCUIGlubGluZWQgaGVyZSB0byByZXVzZSBvdXJcbi8vIGV4aXN0aW5nIDUwbXMgY2xvY2sgYW5kIGVsaW1pbmF0ZSB0aGUgcmVkdW5kYW50IHN1YnNjcmliZXIuXG5jb25zdCBUSElOS0lOR19JTkFDVElWRSA9IHsgcjogMTUzLCBnOiAxNTMsIGI6IDE1MyB9XG5jb25zdCBUSElOS0lOR19JTkFDVElWRV9TSElNTUVSID0geyByOiAxODUsIGc6IDE4NSwgYjogMTg1IH1cbmNvbnN0IFRISU5LSU5HX0RFTEFZX01TID0gMzAwMFxuY29uc3QgVEhJTktJTkdfR0xPV19QRVJJT0RfUyA9IDJcblxuZXhwb3J0IHR5cGUgU3Bpbm5lckFuaW1hdGlvblJvd1Byb3BzID0ge1xuICAvLyBBbmltYXRpb24gaW5wdXRzXG4gIG1vZGU6IFNwaW5uZXJNb2RlXG4gIHJlZHVjZWRNb3Rpb246IGJvb2xlYW5cbiAgaGFzQWN0aXZlVG9vbHM6IGJvb2xlYW5cbiAgcmVzcG9uc2VMZW5ndGhSZWY6IFJlYWN0LlJlZk9iamVjdDxudW1iZXI+XG5cbiAgLy8gTWVzc2FnZSAoc3RhYmxlIHdpdGhpbiBhIHR1cm4pXG4gIG1lc3NhZ2U6IHN0cmluZ1xuICBtZXNzYWdlQ29sb3I6IGtleW9mIFRoZW1lXG4gIHNoaW1tZXJDb2xvcjoga2V5b2YgVGhlbWVcbiAgb3ZlcnJpZGVDb2xvcj86IGtleW9mIFRoZW1lIHwgbnVsbFxuXG4gIC8vIFRpbWVyIHJlZnMgKHN0YWJsZSByZWZlcmVuY2VzKVxuICBsb2FkaW5nU3RhcnRUaW1lUmVmOiBSZWFjdC5SZWZPYmplY3Q8bnVtYmVyPlxuICB0b3RhbFBhdXNlZE1zUmVmOiBSZWFjdC5SZWZPYmplY3Q8bnVtYmVyPlxuICBwYXVzZVN0YXJ0VGltZVJlZjogUmVhY3QuUmVmT2JqZWN0PG51bWJlciB8IG51bGw+XG5cbiAgLy8gRGlzcGxheSBmbGFnc1xuICBzcGlubmVyU3VmZml4Pzogc3RyaW5nIHwgbnVsbFxuICB2ZXJib3NlOiBib29sZWFuXG4gIGNvbHVtbnM6IG51bWJlclxuXG4gIC8vIFRlYW1tYXRlLWRlcml2ZWQgKGNvbXB1dGVkIGJ5IHBhcmVudCBmcm9tIHRhc2tzKVxuICBoYXNSdW5uaW5nVGVhbW1hdGVzOiBib29sZWFuXG4gIHRlYW1tYXRlVG9rZW5zOiBudW1iZXJcbiAgZm9yZWdyb3VuZGVkVGVhbW1hdGU6IEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlIHwgdW5kZWZpbmVkXG4gIC8qKiBMZWFkZXIncyB0dXJuIGhhcyBjb21wbGV0ZWQuIFN1cHByZXNzZXMgc3RhbGwtcmVkIHNpbmNlIHJlc3BvbnNlTGVuZ3RoUmVmL2hhc0FjdGl2ZVRvb2xzIHRyYWNrIGxlYWRlciBzdGF0ZSBvbmx5LiAqL1xuICBsZWFkZXJJc0lkbGU/OiBib29sZWFuXG5cbiAgLy8gVGhpbmtpbmcgKHN0YXRlIG93bmVkIGJ5IHBhcmVudCwgbW9kZS1kZXBlbmRlbnQpXG4gIHRoaW5raW5nU3RhdHVzOiAndGhpbmtpbmcnIHwgbnVtYmVyIHwgbnVsbFxuICBlZmZvcnRTdWZmaXg6IHN0cmluZ1xuXG59XG5cbi8qKlxuICogVGhlIDUwbXMtYW5pbWF0ZWQgcG9ydGlvbiBvZiBTcGlubmVyV2l0aFZlcmIuIE93bnMgdXNlQW5pbWF0aW9uRnJhbWUoNTApXG4gKiBhbmQgYWxsIHZhbHVlcyBkZXJpdmVkIGZyb20gdGhlIGFuaW1hdGlvbiBjbG9jayAoZnJhbWUsIGdsaW1tZXIsIHRva2VuXG4gKiBjb3VudGVyIGFuaW1hdGlvbiwgZWxhcHNlZC10aW1lLCBzdGFsbGVkIGludGVuc2l0eSwgdGhpbmtpbmcgc2hpbW1lcikuXG4gKlxuICogVGhlIHBhcmVudCBTcGlubmVyV2l0aFZlcmIgaXMgZnJlZWQgZnJvbSB0aGUgNTBtcyByZW5kZXIgbG9vcCBhbmQgb25seVxuICogcmUtcmVuZGVycyB3aGVuIGl0cyBwcm9wcy9hcHAgc3RhdGUgY2hhbmdlICh+MjV4L3R1cm4gaW5zdGVhZCBvZiB+MzgzeCkuXG4gKiBUaGF0IGtlZXBzIHRoZSBvdXRlciBCb3ggc2hlbGxzLCB1c2VBcHBTdGF0ZSBzZWxlY3RvcnMsIHRhc2sgZmlsdGVyaW5nLFxuICogYW5kIHRpcC90cmVlIHN1YnRyZWVzIG91dCBvZiB0aGUgaG90IGFuaW1hdGlvbiBwYXRoLlxuICovXG5leHBvcnQgZnVuY3Rpb24gU3Bpbm5lckFuaW1hdGlvblJvdyh7XG4gIG1vZGUsXG4gIHJlZHVjZWRNb3Rpb24sXG4gIGhhc0FjdGl2ZVRvb2xzLFxuICByZXNwb25zZUxlbmd0aFJlZixcbiAgbWVzc2FnZSxcbiAgbWVzc2FnZUNvbG9yLFxuICBzaGltbWVyQ29sb3IsXG4gIG92ZXJyaWRlQ29sb3IsXG4gIGxvYWRpbmdTdGFydFRpbWVSZWYsXG4gIHRvdGFsUGF1c2VkTXNSZWYsXG4gIHBhdXNlU3RhcnRUaW1lUmVmLFxuICBzcGlubmVyU3VmZml4LFxuICB2ZXJib3NlLFxuICBjb2x1bW5zLFxuICBoYXNSdW5uaW5nVGVhbW1hdGVzLFxuICB0ZWFtbWF0ZVRva2VucyxcbiAgZm9yZWdyb3VuZGVkVGVhbW1hdGUsXG4gIGxlYWRlcklzSWRsZSA9IGZhbHNlLFxuICB0aGlua2luZ1N0YXR1cyxcbiAgZWZmb3J0U3VmZml4LFxufTogU3Bpbm5lckFuaW1hdGlvblJvd1Byb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3ZpZXdwb3J0UmVmLCB0aW1lXSA9IHVzZUFuaW1hdGlvbkZyYW1lKHJlZHVjZWRNb3Rpb24gPyBudWxsIDogNTApXG5cbiAgLy8gPT09IEVsYXBzZWQgdGltZSAod2FsbC1jbG9jaywgZGVyaXZlZCBmcm9tIHJlZnMgZWFjaCBmcmFtZSkgPT09XG4gIGNvbnN0IG5vdyA9IERhdGUubm93KClcbiAgY29uc3QgZWxhcHNlZFRpbWVNcyA9XG4gICAgcGF1c2VTdGFydFRpbWVSZWYuY3VycmVudCAhPT0gbnVsbFxuICAgICAgPyBwYXVzZVN0YXJ0VGltZVJlZi5jdXJyZW50IC1cbiAgICAgICAgbG9hZGluZ1N0YXJ0VGltZVJlZi5jdXJyZW50IC1cbiAgICAgICAgdG90YWxQYXVzZWRNc1JlZi5jdXJyZW50XG4gICAgICA6IG5vdyAtIGxvYWRpbmdTdGFydFRpbWVSZWYuY3VycmVudCAtIHRvdGFsUGF1c2VkTXNSZWYuY3VycmVudFxuXG4gIC8vIFRyYWNrIHdhbGwtY2xvY2sgdHVybiBzdGFydCBmb3IgdGVhbW1hdGVzLiBXaGlsZSBhIHN3YXJtIGlzIHJ1bm5pbmcgdGhlXG4gIC8vIGxlYWRlcidzIGVsYXBzZWRUaW1lTXMgbWF5IGp1bXAgYXJvdW5kIChuZXcgQVBJIGNhbGxzIHJlc2V0XG4gIC8vIGxvYWRpbmdTdGFydFRpbWVSZWY7IHBhdXNlcyBmcmVlemUgaXQpLCBzbyB3ZSBhbmNob3IgdG8gdGhlIGVhcmxpZXN0XG4gIC8vIGRlcml2ZWQgc3RhcnQgc2VlbiBzbyBmYXIuIFdoZW4gbm8gdGVhbW1hdGVzIGFyZSBydW5uaW5nIHRoaXMganVzdCB0cmFja3NcbiAgLy8gZGVyaXZlZFN0YXJ0IGV2ZXJ5IGZyYW1lLCBlZmZlY3RpdmVseSByZXNldHRpbmcgZm9yIHRoZSBuZXh0IHN3YXJtLlxuICBjb25zdCBkZXJpdmVkU3RhcnQgPSBub3cgLSBlbGFwc2VkVGltZU1zXG4gIGNvbnN0IHR1cm5TdGFydFJlZiA9IHVzZVJlZihkZXJpdmVkU3RhcnQpXG4gIGlmICghaGFzUnVubmluZ1RlYW1tYXRlcyB8fCBkZXJpdmVkU3RhcnQgPCB0dXJuU3RhcnRSZWYuY3VycmVudCkge1xuICAgIHR1cm5TdGFydFJlZi5jdXJyZW50ID0gZGVyaXZlZFN0YXJ0XG4gIH1cblxuICAvLyA9PT0gQW5pbWF0aW9uIGRlcml2YXRpb25zIGZyb20gYHRpbWVgID09PVxuICBjb25zdCBjdXJyZW50UmVzcG9uc2VMZW5ndGggPSByZXNwb25zZUxlbmd0aFJlZi5jdXJyZW50XG5cbiAgLy8gU3VwcHJlc3Mgc3RhbGwgZGV0ZWN0aW9uIHdoZW4gbGVhZGVyIGlzIGlkbGUg4oCUIHJlc3BvbnNlTGVuZ3RoUmVmIGFuZFxuICAvLyBoYXNBY3RpdmVUb29scyBib3RoIHRyYWNrIGxlYWRlciBzdGF0ZS4gV2hlbiB2aWV3aW5nIGFuIGFjdGl2ZSB0ZWFtbWF0ZVxuICAvLyB3aGlsZSBsZWFkZXIgaXMgaWRsZSwgdGhleSdkIG90aGVyd2lzZSBmbGFnIGEgZmFsc2Ugc3RhbGwgYWZ0ZXIgM3MuXG4gIC8vIFRyZWF0aW5nIGxlYWRlcklzSWRsZSBsaWtlIGhhc0FjdGl2ZVRvb2xzIHJlc2V0cyB0aGUgc3RhbGwgdGltZXIuXG4gIGNvbnN0IHsgaXNTdGFsbGVkLCBzdGFsbGVkSW50ZW5zaXR5IH0gPSB1c2VTdGFsbGVkQW5pbWF0aW9uKFxuICAgIHRpbWUsXG4gICAgY3VycmVudFJlc3BvbnNlTGVuZ3RoLFxuICAgIGhhc0FjdGl2ZVRvb2xzIHx8IGxlYWRlcklzSWRsZSxcbiAgICByZWR1Y2VkTW90aW9uLFxuICApXG5cbiAgY29uc3QgZnJhbWUgPSByZWR1Y2VkTW90aW9uID8gMCA6IE1hdGguZmxvb3IodGltZSAvIDEyMClcblxuICBjb25zdCBnbGltbWVyU3BlZWQgPSBtb2RlID09PSAncmVxdWVzdGluZycgPyA1MCA6IDIwMFxuICAvLyBtZXNzYWdlIGlzIHN0YWJsZSB3aXRoaW4gYSB0dXJuOyBzdHJpbmdXaWR0aCBpcyBleHBlbnNpdmUgZW5vdWdoIChCdW4gbmF0aXZlXG4gIC8vIGNhbGwgcGVyIGNvZGUgcG9pbnQpIHRvIG1lbW9pemUgZXhwbGljaXRseSBhY3Jvc3MgdGhlIDUwbXMgbG9vcC5cbiAgY29uc3QgZ2xpbW1lck1lc3NhZ2VXaWR0aCA9IHVzZU1lbW8oKCkgPT4gc3RyaW5nV2lkdGgobWVzc2FnZSksIFttZXNzYWdlXSlcbiAgY29uc3QgY3ljbGVMZW5ndGggPSBnbGltbWVyTWVzc2FnZVdpZHRoICsgMjBcbiAgY29uc3QgY3ljbGVQb3NpdGlvbiA9IE1hdGguZmxvb3IodGltZSAvIGdsaW1tZXJTcGVlZClcbiAgY29uc3QgZ2xpbW1lckluZGV4ID0gcmVkdWNlZE1vdGlvblxuICAgID8gLTEwMFxuICAgIDogaXNTdGFsbGVkXG4gICAgICA/IC0xMDBcbiAgICAgIDogbW9kZSA9PT0gJ3JlcXVlc3RpbmcnXG4gICAgICAgID8gKGN5Y2xlUG9zaXRpb24gJSBjeWNsZUxlbmd0aCkgLSAxMFxuICAgICAgICA6IGdsaW1tZXJNZXNzYWdlV2lkdGggKyAxMCAtIChjeWNsZVBvc2l0aW9uICUgY3ljbGVMZW5ndGgpXG5cbiAgY29uc3QgZmxhc2hPcGFjaXR5ID0gcmVkdWNlZE1vdGlvblxuICAgID8gMFxuICAgIDogbW9kZSA9PT0gJ3Rvb2wtdXNlJ1xuICAgICAgPyAoTWF0aC5zaW4oKHRpbWUgLyAxMDAwKSAqIE1hdGguUEkpICsgMSkgLyAyXG4gICAgICA6IDBcblxuICAvLyA9PT0gVG9rZW4gY291bnRlciBhbmltYXRpb24gKHNtb290aCBpbmNyZW1lbnQsIGRyaXZlbiBieSA1MG1zIGNsb2NrKSA9PT1cbiAgY29uc3QgdG9rZW5Db3VudGVyUmVmID0gdXNlUmVmKGN1cnJlbnRSZXNwb25zZUxlbmd0aClcbiAgaWYgKHJlZHVjZWRNb3Rpb24pIHtcbiAgICB0b2tlbkNvdW50ZXJSZWYuY3VycmVudCA9IGN1cnJlbnRSZXNwb25zZUxlbmd0aFxuICB9IGVsc2Uge1xuICAgIGNvbnN0IGdhcCA9IGN1cnJlbnRSZXNwb25zZUxlbmd0aCAtIHRva2VuQ291bnRlclJlZi5jdXJyZW50XG4gICAgaWYgKGdhcCA+IDApIHtcbiAgICAgIGxldCBpbmNyZW1lbnRcbiAgICAgIGlmIChnYXAgPCA3MCkge1xuICAgICAgICBpbmNyZW1lbnQgPSAzXG4gICAgICB9IGVsc2UgaWYgKGdhcCA8IDIwMCkge1xuICAgICAgICBpbmNyZW1lbnQgPSBNYXRoLm1heCg4LCBNYXRoLmNlaWwoZ2FwICogMC4xNSkpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpbmNyZW1lbnQgPSA1MFxuICAgICAgfVxuICAgICAgdG9rZW5Db3VudGVyUmVmLmN1cnJlbnQgPSBNYXRoLm1pbihcbiAgICAgICAgdG9rZW5Db3VudGVyUmVmLmN1cnJlbnQgKyBpbmNyZW1lbnQsXG4gICAgICAgIGN1cnJlbnRSZXNwb25zZUxlbmd0aCxcbiAgICAgIClcbiAgICB9XG4gIH1cbiAgY29uc3QgZGlzcGxheWVkUmVzcG9uc2VMZW5ndGggPSB0b2tlbkNvdW50ZXJSZWYuY3VycmVudFxuICBjb25zdCBsZWFkZXJUb2tlbnMgPSBNYXRoLnJvdW5kKGRpc3BsYXllZFJlc3BvbnNlTGVuZ3RoIC8gNClcblxuICBjb25zdCBlZmZlY3RpdmVFbGFwc2VkTXMgPSBoYXNSdW5uaW5nVGVhbW1hdGVzXG4gICAgPyBNYXRoLm1heChlbGFwc2VkVGltZU1zLCBub3cgLSB0dXJuU3RhcnRSZWYuY3VycmVudClcbiAgICA6IGVsYXBzZWRUaW1lTXNcbiAgY29uc3QgdGltZXJUZXh0ID0gZm9ybWF0RHVyYXRpb24oZWZmZWN0aXZlRWxhcHNlZE1zKVxuICBjb25zdCB0aW1lcldpZHRoID0gc3RyaW5nV2lkdGgodGltZXJUZXh0KVxuXG4gIC8vID09PSBUb2tlbiBjb3VudCAobGVhZGVyICsgdGVhbW1hdGVzLCBvciBmb3JlZ3JvdW5kZWQgdGVhbW1hdGUpID09PVxuICBjb25zdCB0b3RhbFRva2VucyA9XG4gICAgZm9yZWdyb3VuZGVkVGVhbW1hdGUgJiYgIWZvcmVncm91bmRlZFRlYW1tYXRlLmlzSWRsZVxuICAgICAgPyAoZm9yZWdyb3VuZGVkVGVhbW1hdGUucHJvZ3Jlc3M/LnRva2VuQ291bnQgPz8gMClcbiAgICAgIDogbGVhZGVyVG9rZW5zICsgdGVhbW1hdGVUb2tlbnNcbiAgY29uc3QgdG9rZW5Db3VudCA9IGZvcm1hdE51bWJlcih0b3RhbFRva2VucylcbiAgY29uc3QgdG9rZW5zVGV4dCA9IGhhc1J1bm5pbmdUZWFtbWF0ZXNcbiAgICA/IGAke3Rva2VuQ291bnR9IHRva2Vuc2BcbiAgICA6IGAke2ZpZ3VyZXMuYXJyb3dEb3dufSAke3Rva2VuQ291bnR9IHRva2Vuc2BcbiAgY29uc3QgdG9rZW5zV2lkdGggPSBzdHJpbmdXaWR0aCh0b2tlbnNUZXh0KVxuXG4gIC8vID09PSBUaGlua2luZyB0ZXh0IChtYXkgc2hyaW5rIHRvIGZpdCkgPT09XG4gIGxldCB0aGlua2luZ1RleHQgPVxuICAgIHRoaW5raW5nU3RhdHVzID09PSAndGhpbmtpbmcnXG4gICAgICA/IGB0aGlua2luZyR7ZWZmb3J0U3VmZml4fWBcbiAgICAgIDogdHlwZW9mIHRoaW5raW5nU3RhdHVzID09PSAnbnVtYmVyJ1xuICAgICAgICA/IGB0aG91Z2h0IGZvciAke01hdGgubWF4KDEsIE1hdGgucm91bmQodGhpbmtpbmdTdGF0dXMgLyAxMDAwKSl9c2BcbiAgICAgICAgOiBudWxsXG4gIGxldCB0aGlua2luZ1dpZHRoVmFsdWUgPSB0aGlua2luZ1RleHQgPyBzdHJpbmdXaWR0aCh0aGlua2luZ1RleHQpIDogMFxuXG4gIC8vID09PSBQcm9ncmVzc2l2ZSB3aWR0aCBnYXRpbmcgPT09XG4gIGNvbnN0IG1lc3NhZ2VXaWR0aCA9IGdsaW1tZXJNZXNzYWdlV2lkdGggKyAyXG4gIGNvbnN0IHNlcCA9IFNFUF9XSURUSFxuXG4gIGNvbnN0IHdhbnRzVGhpbmtpbmcgPSB0aGlua2luZ1N0YXR1cyAhPT0gbnVsbFxuICBjb25zdCB3YW50c1RpbWVyQW5kVG9rZW5zID1cbiAgICB2ZXJib3NlIHx8IGhhc1J1bm5pbmdUZWFtbWF0ZXMgfHwgZWZmZWN0aXZlRWxhcHNlZE1zID4gU0hPV19UT0tFTlNfQUZURVJfTVNcblxuICBjb25zdCBhdmFpbGFibGVTcGFjZSA9IGNvbHVtbnMgLSBtZXNzYWdlV2lkdGggLSA1XG5cbiAgbGV0IHNob3dUaGlua2luZyA9IHdhbnRzVGhpbmtpbmcgJiYgYXZhaWxhYmxlU3BhY2UgPiB0aGlua2luZ1dpZHRoVmFsdWVcbiAgaWYgKFxuICAgICFzaG93VGhpbmtpbmcgJiZcbiAgICB3YW50c1RoaW5raW5nICYmXG4gICAgdGhpbmtpbmdTdGF0dXMgPT09ICd0aGlua2luZycgJiZcbiAgICBlZmZvcnRTdWZmaXhcbiAgKSB7XG4gICAgaWYgKGF2YWlsYWJsZVNwYWNlID4gVEhJTktJTkdfQkFSRV9XSURUSCkge1xuICAgICAgdGhpbmtpbmdUZXh0ID0gJ3RoaW5raW5nJ1xuICAgICAgdGhpbmtpbmdXaWR0aFZhbHVlID0gVEhJTktJTkdfQkFSRV9XSURUSFxuICAgICAgc2hvd1RoaW5raW5nID0gdHJ1ZVxuICAgIH1cbiAgfVxuICBjb25zdCB1c2VkQWZ0ZXJUaGlua2luZyA9IHNob3dUaGlua2luZyA/IHRoaW5raW5nV2lkdGhWYWx1ZSArIHNlcCA6IDBcblxuICBjb25zdCBzaG93VGltZXIgPVxuICAgIHdhbnRzVGltZXJBbmRUb2tlbnMgJiYgYXZhaWxhYmxlU3BhY2UgPiB1c2VkQWZ0ZXJUaGlua2luZyArIHRpbWVyV2lkdGhcbiAgY29uc3QgdXNlZEFmdGVyVGltZXIgPSB1c2VkQWZ0ZXJUaGlua2luZyArIChzaG93VGltZXIgPyB0aW1lcldpZHRoICsgc2VwIDogMClcblxuICBjb25zdCBzaG93VG9rZW5zID1cbiAgICB3YW50c1RpbWVyQW5kVG9rZW5zICYmXG4gICAgdG90YWxUb2tlbnMgPiAwICYmXG4gICAgYXZhaWxhYmxlU3BhY2UgPiB1c2VkQWZ0ZXJUaW1lciArIHRva2Vuc1dpZHRoXG5cblxuICBjb25zdCB0aGlua2luZ09ubHkgPVxuICAgIHNob3dUaGlua2luZyAmJlxuICAgIHRoaW5raW5nU3RhdHVzID09PSAndGhpbmtpbmcnICYmXG4gICAgIXNwaW5uZXJTdWZmaXggJiZcbiAgICAhc2hvd1RpbWVyICYmXG4gICAgIXNob3dUb2tlbnMgJiZcbiAgICB0cnVlXG5cbiAgLy8gPT09IFRoaW5raW5nIHNoaW1tZXIgY29sb3IgKGZvcm1lcmx5IFRoaW5raW5nU2hpbW1lclRleHQncyBvd24gdGltZXIpID09PVxuICAvLyBTYW1lIHNpbmUtd2F2ZSBvcGFjaXR5LCBidXQgZGVyaXZlZCBmcm9tIG91ciBzaGFyZWQgYHRpbWVgIGluc3RlYWQgb2YgYVxuICAvLyBzZWNvbmQgdXNlQW5pbWF0aW9uRnJhbWUoNTApIHN1YnNjcmlwdGlvbi5cbiAgY29uc3QgdGhpbmtpbmdFbGFwc2VkU2VjID0gKHRpbWUgLSBUSElOS0lOR19ERUxBWV9NUykgLyAxMDAwXG4gIGNvbnN0IHRoaW5raW5nT3BhY2l0eSA9XG4gICAgdGltZSA8IFRISU5LSU5HX0RFTEFZX01TXG4gICAgICA/IDBcbiAgICAgIDogKE1hdGguc2luKCh0aGlua2luZ0VsYXBzZWRTZWMgKiBNYXRoLlBJICogMikgLyBUSElOS0lOR19HTE9XX1BFUklPRF9TKSArXG4gICAgICAgICAgMSkgL1xuICAgICAgICAyXG4gIGNvbnN0IHRoaW5raW5nU2hpbW1lckNvbG9yID0gdG9SR0JDb2xvcihcbiAgICBpbnRlcnBvbGF0ZUNvbG9yKFxuICAgICAgVEhJTktJTkdfSU5BQ1RJVkUsXG4gICAgICBUSElOS0lOR19JTkFDVElWRV9TSElNTUVSLFxuICAgICAgdGhpbmtpbmdPcGFjaXR5LFxuICAgICksXG4gIClcblxuICAvLyA9PT0gQnVpbGQgc3RhdHVzIHBhcnRzID09PVxuICBjb25zdCBwYXJ0cyA9IFtcbiAgICAuLi4oc3Bpbm5lclN1ZmZpeFxuICAgICAgPyBbXG4gICAgICAgICAgPFRleHQgZGltQ29sb3Iga2V5PVwic3VmZml4XCI+XG4gICAgICAgICAgICB7c3Bpbm5lclN1ZmZpeH1cbiAgICAgICAgICA8L1RleHQ+LFxuICAgICAgICBdXG4gICAgICA6IFtdKSxcbiAgICAuLi4oc2hvd1RpbWVyXG4gICAgICA/IFtcbiAgICAgICAgICA8VGV4dCBkaW1Db2xvciBrZXk9XCJlbGFwc2VkVGltZVwiPlxuICAgICAgICAgICAge3RpbWVyVGV4dH1cbiAgICAgICAgICA8L1RleHQ+LFxuICAgICAgICBdXG4gICAgICA6IFtdKSxcbiAgICAuLi4oc2hvd1Rva2Vuc1xuICAgICAgPyBbXG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIga2V5PVwidG9rZW5zXCI+XG4gICAgICAgICAgICB7IWhhc1J1bm5pbmdUZWFtbWF0ZXMgJiYgPFNwaW5uZXJNb2RlR2x5cGggbW9kZT17bW9kZX0gLz59XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57dG9rZW5Db3VudH0gdG9rZW5zPC9UZXh0PlxuICAgICAgICAgIDwvQm94PixcbiAgICAgICAgXVxuICAgICAgOiBbXSksXG4gICAgLi4uKHNob3dUaGlua2luZyAmJiB0aGlua2luZ1RleHRcbiAgICAgID8gW1xuICAgICAgICAgIHRoaW5raW5nU3RhdHVzID09PSAndGhpbmtpbmcnICYmICFyZWR1Y2VkTW90aW9uID8gKFxuICAgICAgICAgICAgPFRleHQga2V5PVwidGhpbmtpbmdcIiBjb2xvcj17dGhpbmtpbmdTaGltbWVyQ29sb3J9PlxuICAgICAgICAgICAgICB7dGhpbmtpbmdPbmx5ID8gYCgke3RoaW5raW5nVGV4dH0pYCA6IHRoaW5raW5nVGV4dH1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3Iga2V5PVwidGhpbmtpbmdcIj5cbiAgICAgICAgICAgICAge3RoaW5raW5nVGV4dH1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApLFxuICAgICAgICBdXG4gICAgICA6IFtdKSxcbiAgXVxuXG4gIGNvbnN0IHN0YXR1cyA9XG4gICAgZm9yZWdyb3VuZGVkVGVhbW1hdGUgJiYgIWZvcmVncm91bmRlZFRlYW1tYXRlLmlzSWRsZSA/IChcbiAgICAgIDw+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPihlc2MgdG8gaW50ZXJydXB0IDwvVGV4dD5cbiAgICAgICAgPFRleHQgY29sb3I9e3RvSW5rQ29sb3IoZm9yZWdyb3VuZGVkVGVhbW1hdGUuaWRlbnRpdHkuY29sb3IpfT5cbiAgICAgICAgICB7Zm9yZWdyb3VuZGVkVGVhbW1hdGUuaWRlbnRpdHkuYWdlbnROYW1lfVxuICAgICAgICA8L1RleHQ+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPik8L1RleHQ+XG4gICAgICA8Lz5cbiAgICApIDogIWZvcmVncm91bmRlZFRlYW1tYXRlICYmIHBhcnRzLmxlbmd0aCA+IDAgPyAoXG4gICAgICB0aGlua2luZ09ubHkgPyAoXG4gICAgICAgIDxCeWxpbmU+e3BhcnRzfTwvQnlsaW5lPlxuICAgICAgKSA6IChcbiAgICAgICAgPD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj4oPC9UZXh0PlxuICAgICAgICAgIDxCeWxpbmU+e3BhcnRzfTwvQnlsaW5lPlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPik8L1RleHQ+XG4gICAgICAgIDwvPlxuICAgICAgKVxuICAgICkgOiBudWxsXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICByZWY9e3ZpZXdwb3J0UmVmfVxuICAgICAgZmxleERpcmVjdGlvbj1cInJvd1wiXG4gICAgICBmbGV4V3JhcD1cIndyYXBcIlxuICAgICAgbWFyZ2luVG9wPXsxfVxuICAgICAgd2lkdGg9XCIxMDAlXCJcbiAgICA+XG4gICAgICA8U3Bpbm5lckdseXBoXG4gICAgICAgIGZyYW1lPXtmcmFtZX1cbiAgICAgICAgbWVzc2FnZUNvbG9yPXttZXNzYWdlQ29sb3J9XG4gICAgICAgIHN0YWxsZWRJbnRlbnNpdHk9e292ZXJyaWRlQ29sb3IgPyAwIDogc3RhbGxlZEludGVuc2l0eX1cbiAgICAgICAgcmVkdWNlZE1vdGlvbj17cmVkdWNlZE1vdGlvbn1cbiAgICAgICAgdGltZT17dGltZX1cbiAgICAgIC8+XG4gICAgICA8R2xpbW1lck1lc3NhZ2VcbiAgICAgICAgbWVzc2FnZT17bWVzc2FnZX1cbiAgICAgICAgbW9kZT17bW9kZX1cbiAgICAgICAgbWVzc2FnZUNvbG9yPXttZXNzYWdlQ29sb3J9XG4gICAgICAgIGdsaW1tZXJJbmRleD17Z2xpbW1lckluZGV4fVxuICAgICAgICBmbGFzaE9wYWNpdHk9e2ZsYXNoT3BhY2l0eX1cbiAgICAgICAgc2hpbW1lckNvbG9yPXtzaGltbWVyQ29sb3J9XG4gICAgICAgIHN0YWxsZWRJbnRlbnNpdHk9e292ZXJyaWRlQ29sb3IgPyAwIDogc3RhbGxlZEludGVuc2l0eX1cbiAgICAgIC8+XG4gICAgICB7c3RhdHVzfVxuICAgIDwvQm94PlxuICApXG59XG5cbmZ1bmN0aW9uIFNwaW5uZXJNb2RlR2x5cGgoeyBtb2RlIH06IHsgbW9kZTogU3Bpbm5lck1vZGUgfSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHN3aXRjaCAobW9kZSkge1xuICAgIGNhc2UgJ3Rvb2wtaW5wdXQnOlxuICAgIGNhc2UgJ3Rvb2wtdXNlJzpcbiAgICBjYXNlICdyZXNwb25kaW5nJzpcbiAgICBjYXNlICd0aGlua2luZyc6XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8Qm94IHdpZHRoPXsyfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57ZmlndXJlcy5hcnJvd0Rvd259PC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIClcbiAgICBjYXNlICdyZXF1ZXN0aW5nJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxCb3ggd2lkdGg9ezJ9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntmaWd1cmVzLmFycm93VXB9PC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIClcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxPQUFPLEVBQUVDLE1BQU0sUUFBUSxPQUFPO0FBQ3ZDLFNBQVNDLFdBQVcsUUFBUSwwQkFBMEI7QUFDdEQsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLGlCQUFpQixRQUFRLGNBQWM7QUFDM0QsY0FBY0MsMEJBQTBCLFFBQVEsNENBQTRDO0FBQzVGLFNBQVNDLGNBQWMsRUFBRUMsWUFBWSxRQUFRLHVCQUF1QjtBQUNwRSxTQUFTQyxVQUFVLFFBQVEsb0JBQW9C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 \ No newline at end of file diff --git a/components/Spinner/SpinnerGlyph.tsx b/components/Spinner/SpinnerGlyph.tsx new file mode 100644 index 0000000..e4d71d4 --- /dev/null +++ b/components/Spinner/SpinnerGlyph.tsx @@ -0,0 +1,80 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Box, Text, useTheme } from '../../ink.js'; +import { getTheme, type Theme } from '../../utils/theme.js'; +import { getDefaultCharacters, interpolateColor, parseRGB, toRGBColor } from './utils.js'; +const DEFAULT_CHARACTERS = getDefaultCharacters(); +const SPINNER_FRAMES = [...DEFAULT_CHARACTERS, ...[...DEFAULT_CHARACTERS].reverse()]; +const REDUCED_MOTION_DOT = '●'; +const REDUCED_MOTION_CYCLE_MS = 2000; // 2-second cycle: 1s visible, 1s dim +const ERROR_RED = { + r: 171, + g: 43, + b: 63 +}; +type Props = { + frame: number; + messageColor: keyof Theme; + stalledIntensity?: number; + reducedMotion?: boolean; + time?: number; +}; +export function SpinnerGlyph(t0) { + const $ = _c(9); + const { + frame, + messageColor, + stalledIntensity: t1, + reducedMotion: t2, + time: t3 + } = t0; + const stalledIntensity = t1 === undefined ? 0 : t1; + const reducedMotion = t2 === undefined ? false : t2; + const time = t3 === undefined ? 0 : t3; + const [themeName] = useTheme(); + const theme = getTheme(themeName); + if (reducedMotion) { + const isDim = Math.floor(time / (REDUCED_MOTION_CYCLE_MS / 2)) % 2 === 1; + let t4; + if ($[0] !== isDim || $[1] !== messageColor) { + t4 = {REDUCED_MOTION_DOT}; + $[0] = isDim; + $[1] = messageColor; + $[2] = t4; + } else { + t4 = $[2]; + } + return t4; + } + const spinnerChar = SPINNER_FRAMES[frame % SPINNER_FRAMES.length]; + if (stalledIntensity > 0) { + const baseColorStr = theme[messageColor]; + const baseRGB = baseColorStr ? parseRGB(baseColorStr) : null; + if (baseRGB) { + const interpolated = interpolateColor(baseRGB, ERROR_RED, stalledIntensity); + return {spinnerChar}; + } + const color = stalledIntensity > 0.5 ? "error" : messageColor; + let t4; + if ($[3] !== color || $[4] !== spinnerChar) { + t4 = {spinnerChar}; + $[3] = color; + $[4] = spinnerChar; + $[5] = t4; + } else { + t4 = $[5]; + } + return t4; + } + let t4; + if ($[6] !== messageColor || $[7] !== spinnerChar) { + t4 = {spinnerChar}; + $[6] = messageColor; + $[7] = spinnerChar; + $[8] = t4; + } else { + t4 = $[8]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJ1c2VUaGVtZSIsImdldFRoZW1lIiwiVGhlbWUiLCJnZXREZWZhdWx0Q2hhcmFjdGVycyIsImludGVycG9sYXRlQ29sb3IiLCJwYXJzZVJHQiIsInRvUkdCQ29sb3IiLCJERUZBVUxUX0NIQVJBQ1RFUlMiLCJTUElOTkVSX0ZSQU1FUyIsInJldmVyc2UiLCJSRURVQ0VEX01PVElPTl9ET1QiLCJSRURVQ0VEX01PVElPTl9DWUNMRV9NUyIsIkVSUk9SX1JFRCIsInIiLCJnIiwiYiIsIlByb3BzIiwiZnJhbWUiLCJtZXNzYWdlQ29sb3IiLCJzdGFsbGVkSW50ZW5zaXR5IiwicmVkdWNlZE1vdGlvbiIsInRpbWUiLCJTcGlubmVyR2x5cGgiLCJ0MCIsIiQiLCJfYyIsInQxIiwidDIiLCJ0MyIsInVuZGVmaW5lZCIsInRoZW1lTmFtZSIsInRoZW1lIiwiaXNEaW0iLCJNYXRoIiwiZmxvb3IiLCJ0NCIsInNwaW5uZXJDaGFyIiwibGVuZ3RoIiwiYmFzZUNvbG9yU3RyIiwiYmFzZVJHQiIsImludGVycG9sYXRlZCIsImNvbG9yIl0sInNvdXJjZXMiOlsiU3Bpbm5lckdseXBoLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCwgdXNlVGhlbWUgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBnZXRUaGVtZSwgdHlwZSBUaGVtZSB9IGZyb20gJy4uLy4uL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0RGVmYXVsdENoYXJhY3RlcnMsXG4gIGludGVycG9sYXRlQ29sb3IsXG4gIHBhcnNlUkdCLFxuICB0b1JHQkNvbG9yLFxufSBmcm9tICcuL3V0aWxzLmpzJ1xuXG5jb25zdCBERUZBVUxUX0NIQVJBQ1RFUlMgPSBnZXREZWZhdWx0Q2hhcmFjdGVycygpXG5cbmNvbnN0IFNQSU5ORVJfRlJBTUVTID0gW1xuICAuLi5ERUZBVUxUX0NIQVJBQ1RFUlMsXG4gIC4uLlsuLi5ERUZBVUxUX0NIQVJBQ1RFUlNdLnJldmVyc2UoKSxcbl1cblxuY29uc3QgUkVEVUNFRF9NT1RJT05fRE9UID0gJ+KXjydcbmNvbnN0IFJFRFVDRURfTU9USU9OX0NZQ0xFX01TID0gMjAwMCAvLyAyLXNlY29uZCBjeWNsZTogMXMgdmlzaWJsZSwgMXMgZGltXG5jb25zdCBFUlJPUl9SRUQgPSB7IHI6IDE3MSwgZzogNDMsIGI6IDYzIH1cblxudHlwZSBQcm9wcyA9IHtcbiAgZnJhbWU6IG51bWJlclxuICBtZXNzYWdlQ29sb3I6IGtleW9mIFRoZW1lXG4gIHN0YWxsZWRJbnRlbnNpdHk/OiBudW1iZXJcbiAgcmVkdWNlZE1vdGlvbj86IGJvb2xlYW5cbiAgdGltZT86IG51bWJlclxufVxuXG5leHBvcnQgZnVuY3Rpb24gU3Bpbm5lckdseXBoKHtcbiAgZnJhbWUsXG4gIG1lc3NhZ2VDb2xvcixcbiAgc3RhbGxlZEludGVuc2l0eSA9IDAsXG4gIHJlZHVjZWRNb3Rpb24gPSBmYWxzZSxcbiAgdGltZSA9IDAsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZU5hbWVdID0gdXNlVGhlbWUoKVxuICBjb25zdCB0aGVtZSA9IGdldFRoZW1lKHRoZW1lTmFtZSlcblxuICAvLyBSZWR1Y2VkIG1vdGlvbjogc2xvd2x5IGZsYXNoaW5nIG9yYW5nZSBkb3RcbiAgaWYgKHJlZHVjZWRNb3Rpb24pIHtcbiAgICBjb25zdCBpc0RpbSA9IE1hdGguZmxvb3IodGltZSAvIChSRURVQ0VEX01PVElPTl9DWUNMRV9NUyAvIDIpKSAlIDIgPT09IDFcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4V3JhcD1cIndyYXBcIiBoZWlnaHQ9ezF9IHdpZHRoPXsyfT5cbiAgICAgICAgPFRleHQgY29sb3I9e21lc3NhZ2VDb2xvcn0gZGltQ29sb3I9e2lzRGltfT5cbiAgICAgICAgICB7UkVEVUNFRF9NT1RJT05fRE9UfVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBjb25zdCBzcGlubmVyQ2hhciA9IFNQSU5ORVJfRlJBTUVTW2ZyYW1lICUgU1BJTk5FUl9GUkFNRVMubGVuZ3RoXVxuXG4gIC8vIFNtb290aGx5IGludGVycG9sYXRlIGZyb20gY3VycmVudCBjb2xvciB0byByZWQgd2hlbiBzdGFsbGVkXG4gIGlmIChzdGFsbGVkSW50ZW5zaXR5ID4gMCkge1xuICAgIGNvbnN0IGJhc2VDb2xvclN0ciA9IHRoZW1lW21lc3NhZ2VDb2xvcl1cbiAgICBjb25zdCBiYXNlUkdCID0gYmFzZUNvbG9yU3RyID8gcGFyc2VSR0IoYmFzZUNvbG9yU3RyKSA6IG51bGxcblxuICAgIGlmIChiYXNlUkdCKSB7XG4gICAgICBjb25zdCBpbnRlcnBvbGF0ZWQgPSBpbnRlcnBvbGF0ZUNvbG9yKFxuICAgICAgICBiYXNlUkdCLFxuICAgICAgICBFUlJPUl9SRUQsXG4gICAgICAgIHN0YWxsZWRJbnRlbnNpdHksXG4gICAgICApXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8Qm94IGZsZXhXcmFwPVwid3JhcFwiIGhlaWdodD17MX0gd2lkdGg9ezJ9PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPXt0b1JHQkNvbG9yKGludGVycG9sYXRlZCl9PntzcGlubmVyQ2hhcn08L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKVxuICAgIH1cblxuICAgIC8vIEZhbGxiYWNrIGZvciBBTlNJIHRoZW1lc1xuICAgIGNvbnN0IGNvbG9yID0gc3RhbGxlZEludGVuc2l0eSA+IDAuNSA/ICdlcnJvcicgOiBtZXNzYWdlQ29sb3JcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4V3JhcD1cIndyYXBcIiBoZWlnaHQ9ezF9IHdpZHRoPXsyfT5cbiAgICAgICAgPFRleHQgY29sb3I9e2NvbG9yfT57c3Bpbm5lckNoYXJ9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhXcmFwPVwid3JhcFwiIGhlaWdodD17MX0gd2lkdGg9ezJ9PlxuICAgICAgPFRleHQgY29sb3I9e21lc3NhZ2VDb2xvcn0+e3NwaW5uZXJDaGFyfTwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxHQUFHLEVBQUVDLElBQUksRUFBRUMsUUFBUSxRQUFRLGNBQWM7QUFDbEQsU0FBU0MsUUFBUSxFQUFFLEtBQUtDLEtBQUssUUFBUSxzQkFBc0I7QUFDM0QsU0FDRUMsb0JBQW9CLEVBQ3BCQyxnQkFBZ0IsRUFDaEJDLFFBQVEsRUFDUkMsVUFBVSxRQUNMLFlBQVk7QUFFbkIsTUFBTUMsa0JBQWtCLEdBQUdKLG9CQUFvQixDQUFDLENBQUM7QUFFakQsTUFBTUssY0FBYyxHQUFHLENBQ3JCLEdBQUdELGtCQUFrQixFQUNyQixHQUFHLENBQUMsR0FBR0Esa0JBQWtCLENBQUMsQ0FBQ0UsT0FBTyxDQUFDLENBQUMsQ0FDckM7QUFFRCxNQUFNQyxrQkFBa0IsR0FBRyxHQUFHO0FBQzlCLE1BQU1DLHVCQUF1QixHQUFHLElBQUksRUFBQztBQUNyQyxNQUFNQyxTQUFTLEdBQUc7RUFBRUMsQ0FBQyxFQUFFLEdBQUc7RUFBRUMsQ0FBQyxFQUFFLEVBQUU7RUFBRUMsQ0FBQyxFQUFFO0FBQUcsQ0FBQztBQUUxQyxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsS0FBSyxFQUFFLE1BQU07RUFDYkMsWUFBWSxFQUFFLE1BQU1oQixLQUFLO0VBQ3pCaUIsZ0JBQWdCLENBQUMsRUFBRSxNQUFNO0VBQ3pCQyxhQUFhLENBQUMsRUFBRSxPQUFPO0VBQ3ZCQyxJQUFJLENBQUMsRUFBRSxNQUFNO0FBQ2YsQ0FBQztBQUVELE9BQU8sU0FBQUMsYUFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFzQjtJQUFBUixLQUFBO0lBQUFDLFlBQUE7SUFBQUMsZ0JBQUEsRUFBQU8sRUFBQTtJQUFBTixhQUFBLEVBQUFPLEVB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 \ No newline at end of file diff --git a/components/Spinner/TeammateSpinnerLine.tsx b/components/Spinner/TeammateSpinnerLine.tsx new file mode 100644 index 0000000..638667b --- /dev/null +++ b/components/Spinner/TeammateSpinnerLine.tsx @@ -0,0 +1,233 @@ +import figures from 'figures'; +import sample from 'lodash-es/sample.js'; +import * as React from 'react'; +import { useRef, useState } from 'react'; +import { getSpinnerVerbs } from '../../constants/spinnerVerbs.js'; +import { TURN_COMPLETION_VERBS } from '../../constants/turnCompletionVerbs.js'; +import { useElapsedTime } from '../../hooks/useElapsedTime.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, Text } from '../../ink.js'; +import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js'; +import { summarizeRecentActivities } from '../../utils/collapseReadSearch.js'; +import { formatDuration, formatNumber, truncateToWidth } from '../../utils/format.js'; +import { toInkColor } from '../../utils/ink.js'; +import { TEAMMATE_SELECT_HINT } from './teammateSelectHint.js'; +type Props = { + teammate: InProcessTeammateTaskState; + isLast: boolean; + isSelected?: boolean; + isForegrounded?: boolean; + allIdle?: boolean; + showPreview?: boolean; +}; + +/** + * Extract the last 3 lines of content from a teammate's conversation. + * Shows recent activity from any message type (user or assistant). + */ +function getMessagePreview(messages: InProcessTeammateTaskState['messages']): string[] { + if (!messages?.length) return []; + const allLines: string[] = []; + const maxLineLength = 80; + + // Collect lines from recent messages (newest first) + for (let i = messages.length - 1; i >= 0 && allLines.length < 3; i--) { + const msg = messages[i]; + // Only process messages that have content (user/assistant messages) + if (!msg || msg.type !== 'user' && msg.type !== 'assistant' || !msg.message?.content?.length) { + continue; + } + const content = msg.message.content; + for (const block of content) { + if (allLines.length >= 3) break; + if (!block || typeof block !== 'object') continue; + if ('type' in block && block.type === 'tool_use' && 'name' in block) { + // Try to show meaningful info from tool input + const input = 'input' in block ? block.input as Record : null; + let toolLine = `Using ${block.name}…`; + if (input) { + // Look for common descriptive fields + const desc = input.description as string | undefined || input.prompt as string | undefined || input.command as string | undefined || input.query as string | undefined || input.pattern as string | undefined; + if (desc) { + toolLine = desc.split('\n')[0] ?? toolLine; + } + } + allLines.push(truncateToWidth(toolLine, maxLineLength)); + } else if ('type' in block && block.type === 'text' && 'text' in block) { + const textLines = (block.text as string).split('\n').filter(l => l.trim()); + // Take from end of text (most recent lines) + for (let j = textLines.length - 1; j >= 0 && allLines.length < 3; j--) { + const line = textLines[j]; + if (!line) continue; + allLines.push(truncateToWidth(line, maxLineLength)); + } + } + } + } + + // Reverse so oldest of the 3 is first (reading order) + return allLines.reverse(); +} +export function TeammateSpinnerLine({ + teammate, + isLast, + isSelected, + isForegrounded, + allIdle, + showPreview +}: Props): React.ReactNode { + const [randomVerb] = useState(() => teammate.spinnerVerb ?? sample(getSpinnerVerbs())); + const [pastTenseVerb] = useState(() => teammate.pastTenseVerb ?? sample(TURN_COMPLETION_VERBS)); + const isHighlighted = isSelected || isForegrounded; + const treeChar = isHighlighted ? isLast ? '╘═' : '╞═' : isLast ? '└─' : '├─'; + const nameColor = toInkColor(teammate.identity.color); + const { + columns + } = useTerminalSize(); + + // Track when teammate became idle (for "Idle for X..." display) + const idleStartRef = useRef(null); + // Freeze elapsed time when entering all-idle state + const frozenDurationRef = useRef(null); + + // Track idle start time + if (teammate.isIdle && idleStartRef.current === null) { + idleStartRef.current = Date.now(); + } else if (!teammate.isIdle) { + idleStartRef.current = null; + } + + // Reset frozen duration when leaving all-idle state + if (!allIdle && frozenDurationRef.current !== null) { + frozenDurationRef.current = null; + } + + // Get elapsed idle time (how long they've been idle) - for "Idle for X..." display + const idleElapsedTime = useElapsedTime(idleStartRef.current ?? Date.now(), teammate.isIdle && !allIdle); + + // Freeze the duration when we first detect all idle + // Use the teammate's actual work time (since task started) for the past-tense display + if (allIdle && frozenDurationRef.current === null) { + frozenDurationRef.current = formatDuration(Math.max(0, Date.now() - teammate.startTime - (teammate.totalPausedMs ?? 0))); + } + + // Use frozen work duration when all idle, otherwise use idle elapsed time + const displayTime = allIdle ? frozenDurationRef.current ?? (() => { + throw new Error(`frozenDurationRef is null for idle teammate ${teammate.identity.agentName}`); + })() : idleElapsedTime; + + // Layout: paddingLeft(3) + pointer(1) + space(1) + treeChar(2) + space(1) = 8 fixed chars + // Then optionally: @name + ": " OR just ": " + // Then: activity text + optional extras (stats, hints) + const basePrefix = 8; + const fullAgentName = `@${teammate.identity.agentName}`; + const fullNameWidth = stringWidth(fullAgentName); + + // Get stats from progress + const toolUseCount = teammate.progress?.toolUseCount ?? 0; + const tokenCount = teammate.progress?.tokenCount ?? 0; + const statsText = ` · ${toolUseCount} tool ${toolUseCount === 1 ? 'use' : 'uses'} · ${formatNumber(tokenCount)} tokens`; + const statsWidth = stringWidth(statsText); + const selectHintText = ` · ${TEAMMATE_SELECT_HINT}`; + const selectHintWidth = stringWidth(selectHintText); + const viewHintText = ' · enter to view'; + const viewHintWidth = stringWidth(viewHintText); + + // Progressive responsive layout: + // Wide (80+): full name + activity + stats + hint + // Medium (60-80): full name + activity + // Narrow (<60): hide name, just show activity + const minActivityWidth = 25; + + // Hide name on narrow terminals (< 60 cols) or if there's not enough room + const spaceWithFullName = columns - basePrefix - fullNameWidth - 2; + const showName = columns >= 60 && spaceWithFullName >= minActivityWidth; + const nameWidth = showName ? fullNameWidth + 2 : 0; // +2 for ": " when name shown + const availableForActivity = columns - basePrefix - nameWidth; + + // Progressive hiding: view hint → select hint → stats + // Stats always visible (dimmed when not selected); hints only when highlighted/selected + const showViewHint = isSelected && !isForegrounded && availableForActivity > viewHintWidth + statsWidth + minActivityWidth + 5; + const showSelectHint = isHighlighted && availableForActivity > selectHintWidth + (showViewHint ? viewHintWidth : 0) + statsWidth + minActivityWidth + 5; + const showStats = availableForActivity > statsWidth + minActivityWidth + 5; + + // Activity text gets remaining space + const extrasCost = (showStats ? statsWidth : 0) + (showSelectHint ? selectHintWidth : 0) + (showViewHint ? viewHintWidth : 0); + const activityMaxWidth = Math.max(minActivityWidth, availableForActivity - extrasCost - 1); + + // Format the activity text for active teammates, rolling up search/read ops + const activityText = (() => { + const activities = teammate.progress?.recentActivities; + if (activities && activities.length > 0) { + const summary = summarizeRecentActivities(activities); + if (summary) return truncateToWidth(summary, activityMaxWidth); + } + const desc = teammate.progress?.lastActivity?.activityDescription; + if (desc) return truncateToWidth(desc, activityMaxWidth); + return randomVerb; + })(); + + // Status rendering logic + const renderStatus = (): React.ReactNode => { + if (teammate.shutdownRequested) { + return [stopping]; + } + if (teammate.awaitingPlanApproval) { + return [awaiting approval]; + } + if (teammate.isIdle) { + if (allIdle) { + return + {pastTenseVerb} for {displayTime} + ; + } + return Idle for {idleElapsedTime}; + } + // Active - show spinner glyph + activity description (only when not highlighted; + // when highlighted, the main spinner above already shows the verb) + if (isHighlighted) { + return null; + } + return + {activityText?.endsWith('…') ? activityText : `${activityText}…`} + ; + }; + + // Get preview lines if enabled + const previewLines = showPreview ? getMessagePreview(teammate.messages) : []; + + // Tree continuation character for preview lines + const previewTreeChar = isLast ? ' ' : '│ '; + return + + {/* Selection indicator: pointer when selected, otherwise space */} + + {isSelected ? figures.pointer : ' '} + + {treeChar} + {/* Agent name: hidden on very narrow screens */} + {showName && + @{teammate.identity.agentName} + } + {showName && : } + {renderStatus()} + {/* Stats: only shown when selected and terminal is wide enough */} + {showStats && + {' '} + · {toolUseCount} tool {toolUseCount === 1 ? 'use' : 'uses'} ·{' '} + {formatNumber(tokenCount)} tokens + } + {/* Hints: select hint when highlighted, view hint when selected but not foregrounded */} + {showSelectHint && · {TEAMMATE_SELECT_HINT}} + {showViewHint && · enter to view} + + {/* Preview lines */} + {previewLines.map((line, idx) => + + {previewTreeChar} + {line} + )} + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwic2FtcGxlIiwiUmVhY3QiLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsImdldFNwaW5uZXJWZXJicyIsIlRVUk5fQ09NUExFVElPTl9WRVJCUyIsInVzZUVsYXBzZWRUaW1lIiwidXNlVGVybWluYWxTaXplIiwic3RyaW5nV2lkdGgiLCJCb3giLCJUZXh0IiwiSW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGUiLCJzdW1tYXJpemVSZWNlbnRBY3Rpdml0aWVzIiwiZm9ybWF0RHVyYXRpb24iLCJmb3JtYXROdW1iZXIiLCJ0cnVuY2F0ZVRvV2lkdGgiLCJ0b0lua0NvbG9yIiwiVEVBTU1BVEVfU0VMRUNUX0hJTlQiLCJQcm9wcyIsInRlYW1tYXRlIiwiaXNMYXN0IiwiaXNTZWxlY3RlZCIsImlzRm9yZWdyb3VuZGVkIiwiYWxsSWRsZSIsInNob3dQcmV2aWV3IiwiZ2V0TWVzc2FnZVByZXZpZXciLCJtZXNzYWdlcyIsImxlbmd0aCIsImFsbExpbmVzIiwibWF4TGluZUxlbmd0aCIsImkiLCJtc2ciLCJ0eXBlIiwibWVzc2FnZSIsImNvbnRlbnQiLCJibG9jayIsImlucHV0IiwiUmVjb3JkIiwidG9vbExpbmUiLCJuYW1lIiwiZGVzYyIsImRlc2NyaXB0aW9uIiwicHJvbXB0IiwiY29tbWFuZCIsInF1ZXJ5IiwicGF0dGVybiIsInNwbGl0IiwicHVzaCIsInRleHRMaW5lcyIsInRleHQiLCJmaWx0ZXIiLCJsIiwidHJpbSIsImoiLCJsaW5lIiwicmV2ZXJzZSIsIlRlYW1tYXRlU3Bpbm5lckxpbmUiLCJSZWFjdE5vZGUiLCJyYW5kb21WZXJiIiwic3Bpbm5lclZlcmIiLCJwYXN0VGVuc2VWZXJiIiwiaXNIaWdobGlnaHRlZCIsInRyZWVDaGFyIiwibmFtZUNvbG9yIiwiaWRlbnRpdHkiLCJjb2xvciIsImNvbHVtbnMiLCJpZGxlU3RhcnRSZWYiLCJmcm96ZW5EdXJhdGlvblJlZiIsImlzSWRsZSIsImN1cnJlbnQiLCJEYXRlIiwibm93IiwiaWRsZUVsYXBzZWRUaW1lIiwiTWF0aCIsIm1heCIsInN0YXJ0VGltZSIsInRvdGFsUGF1c2VkTXMiLCJkaXNwbGF5VGltZSIsIkVycm9yIiwiYWdlbnROYW1lIiwiYmFzZVByZWZpeCIsImZ1bGxBZ2VudE5hbWUiLCJmdWxsTmFtZVdpZHRoIiwidG9vbFVzZUNvdW50IiwicHJvZ3Jlc3MiLCJ0b2tlbkNvdW50Iiwic3RhdHNUZXh0Iiwic3RhdHNXaWR0aCIsInNlbGVjdEhpbnRUZXh0Iiwic2VsZWN0SGludFdpZHRoIiwidmlld0hpbnRUZXh0Iiwidmlld0hpbnRXaWR0aCIsIm1pbkFjdGl2aXR5V2lkdGgiLCJzcGFjZVdpdGhGdWxsTmFtZSIsInNob3dOYW1lIiwibmFtZVdpZHRoIiwiYXZhaWxhYmxlRm9yQWN0aXZpdHkiLCJzaG93Vmlld0hpbnQiLCJzaG93U2VsZWN0SGludCIsInNob3dTdGF0cyIsImV4dHJhc0Nvc3QiLCJhY3Rpdml0eU1heFdpZHRoIiwiYWN0aXZpdHlUZXh0IiwiYWN0aXZpdGllcyIsInJlY2VudEFjdGl2aXRpZXMiLCJzdW1tYXJ5IiwibGFzdEFjdGl2aXR5IiwiYWN0aXZpdHlEZXNjcmlwdGlvbiIsInJlbmRlclN0YXR1cyIsInNodXRkb3duUmVxdWVzdGVkIiwiYXdhaXRpbmdQbGFuQXBwcm92YWwiLCJlbmRzV2l0aCIsInByZXZpZXdMaW5lcyIsInByZXZpZXdUcmVlQ2hhciIsInVuZGVmaW5lZCIsInBvaW50ZXIiLCJtYXAiLCJpZHgiXSwic291cmNlcyI6WyJUZWFtbWF0ZVNwaW5uZXJMaW5lLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0IHNhbXBsZSBmcm9tICdsb2Rhc2gtZXMvc2FtcGxlLmpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBnZXRTcGlubmVyVmVyYnMgfSBmcm9tICcuLi8uLi9jb25zdGFudHMvc3Bpbm5lclZlcmJzLmpzJ1xuaW1wb3J0IHsgVFVSTl9DT01QTEVUSU9OX1ZFUkJTIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL3R1cm5Db21wbGV0aW9uVmVyYnMuanMnXG5pbXBvcnQgeyB1c2VFbGFwc2VkVGltZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUVsYXBzZWRUaW1lLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi8uLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlIH0gZnJvbSAnLi4vLi4vdGFza3MvSW5Qcm9jZXNzVGVhbW1hdGVUYXNrL3R5cGVzLmpzJ1xuaW1wb3J0IHsgc3VtbWFyaXplUmVjZW50QWN0aXZpdGllcyB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbGxhcHNlUmVhZFNlYXJjaC5qcydcbmltcG9ydCB7XG4gIGZvcm1hdER1cmF0aW9uLFxuICBmb3JtYXROdW1iZXIsXG4gIHRydW5jYXRlVG9XaWR0aCxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgdG9JbmtDb2xvciB9IGZyb20gJy4uLy4uL3V0aWxzL2luay5qcydcbmltcG9ydCB7IFRFQU1NQVRFX1NFTEVDVF9ISU5UIH0gZnJvbSAnLi90ZWFtbWF0ZVNlbGVjdEhpbnQuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHRlYW1tYXRlOiBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZVxuICBpc0xhc3Q6IGJvb2xlYW5cbiAgaXNTZWxlY3RlZD86IGJvb2xlYW5cbiAgaXNGb3JlZ3JvdW5kZWQ/OiBib29sZWFuXG4gIGFsbElkbGU/OiBib29sZWFuXG4gIHNob3dQcmV2aWV3PzogYm9vbGVhblxufVxuXG4vKipcbiAqIEV4dHJhY3QgdGhlIGxhc3QgMyBsaW5lcyBvZiBjb250ZW50IGZyb20gYSB0ZWFtbWF0ZSdzIGNvbnZlcnNhdGlvbi5cbiAqIFNob3dzIHJlY2VudCBhY3Rpdml0eSBmcm9tIGFueSBtZXNzYWdlIHR5cGUgKHVzZXIgb3IgYXNzaXN0YW50KS5cbiAqL1xuZnVuY3Rpb24gZ2V0TWVzc2FnZVByZXZpZXcoXG4gIG1lc3NhZ2VzOiBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZVsnbWVzc2FnZXMnXSxcbik6IHN0cmluZ1tdIHtcbiAgaWYgKCFtZXNzYWdlcz8ubGVuZ3RoKSByZXR1cm4gW11cblxuICBjb25zdCBhbGxMaW5lczogc3RyaW5nW10gPSBbXVxuICBjb25zdCBtYXhMaW5lTGVuZ3RoID0gODBcblxuICAvLyBDb2xsZWN0IGxpbmVzIGZyb20gcmVjZW50IG1lc3NhZ2VzIChuZXdlc3QgZmlyc3QpXG4gIGZvciAobGV0IGkgPSBtZXNzYWdlcy5sZW5ndGggLSAxOyBpID49IDAgJiYgYWxsTGluZXMubGVuZ3RoIDwgMzsgaS0tKSB7XG4gICAgY29uc3QgbXNnID0gbWVzc2FnZXNbaV1cbiAgICAvLyBPbmx5IHByb2Nlc3MgbWVzc2FnZXMgdGhhdCBoYXZlIGNvbnRlbnQgKHVzZXIvYXNzaXN0YW50IG1lc3NhZ2VzKVxuICAgIGlmIChcbiAgICAgICFtc2cgfHxcbiAgICAgIChtc2cudHlwZSAhPT0gJ3VzZXInICYmIG1zZy50eXBlICE9PSAnYXNzaXN0YW50JykgfHxcbiAgICAgICFtc2cubWVzc2FnZT8uY29udGVudD8ubGVuZ3RoXG4gICAgKSB7XG4gICAgICBjb250aW51ZVxuICAgIH1cbiAgICBjb25zdCBjb250ZW50ID0gbXNnLm1lc3NhZ2UuY29udGVudFxuXG4gICAgZm9yIChjb25zdCBibG9jayBvZiBjb250ZW50KSB7XG4gICAgICBpZiAoYWxsTGluZXMubGVuZ3RoID49IDMpIGJyZWFrXG4gICAgICBpZiAoIWJsb2NrIHx8IHR5cGVvZiBibG9jayAhPT0gJ29iamVjdCcpIGNvbnRpbnVlXG5cbiAgICAgIGlmICgndHlwZScgaW4gYmxvY2sgJiYgYmxvY2sudHlwZSA9PT0gJ3Rvb2xfdXNlJyAmJiAnbmFtZScgaW4gYmxvY2spIHtcbiAgICAgICAgLy8gVHJ5IHRvIHNob3cgbWVhbmluZ2Z1bCBpbmZvIGZyb20gdG9vbCBpbnB1dFxuICAgICAgICBjb25zdCBpbnB1dCA9XG4gICAgICAgICAgJ2lucHV0JyBpbiBibG9jayA/IChibG9jay5pbnB1dCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgOiBudWxsXG4gICAgICAgIGxldCB0b29sTGluZSA9IGBVc2luZyAke2Jsb2NrLm5hbWV94oCmYFxuICAgICAgICBpZiAoaW5wdXQpIHtcbiAgICAgICAgICAvLyBMb29rIGZvciBjb21tb24gZGVzY3JpcHRpdmUgZmllbGRzXG4gICAgICAgICAgY29uc3QgZGVzYyA9XG4gICAgICAgICAgICAoaW5wdXQuZGVzY3JpcHRpb24gYXMgc3RyaW5nIHwgdW5kZWZpbmVkKSB8fFxuICAgICAgICAgICAgKGlucHV0LnByb21wdCBhcyBzdHJpbmcgfCB1bmRlZmluZWQpIHx8XG4gICAgICAgICAgICAoaW5wdXQuY29tbWFuZCBhcyBzdHJpbmcgfCB1bmRlZmluZWQpIHx8XG4gICAgICAgICAgICAoaW5wdXQucXVlcnkgYXMgc3RyaW5nIHwgdW5kZWZpbmVkKSB8fFxuICAgICAgICAgICAgKGlucHV0LnBhdHRlcm4gYXMgc3RyaW5nIHwgdW5kZWZpbmVkKVxuICAgICAgICAgIGlmIChkZXNjKSB7XG4gICAgICAgICAgICB0b29sTGluZSA9IGRlc2Muc3BsaXQoJ1xcbicpWzBdID8/IHRvb2xMaW5lXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGFsbExpbmVzLnB1c2godHJ1bmNhdGVUb1dpZHRoKHRvb2xMaW5lLCBtYXhMaW5lTGVuZ3RoKSlcbiAgICAgIH0gZWxzZSBpZiAoJ3R5cGUnIGluIGJsb2NrICYmIGJsb2NrLnR5cGUgPT09ICd0ZXh0JyAmJiAndGV4dCcgaW4gYmxvY2spIHtcbiAgICAgICAgY29uc3QgdGV4dExpbmVzID0gKGJsb2NrLnRleHQgYXMgc3RyaW5nKVxuICAgICAgICAgIC5zcGxpdCgnXFxuJylcbiAgICAgICAgICAuZmlsdGVyKGwgPT4gbC50cmltKCkpXG4gICAgICAgIC8vIFRha2UgZnJvbSBlbmQgb2YgdGV4dCAobW9zdCByZWNlbnQgbGluZXMpXG4gICAgICAgIGZvciAobGV0IGogPSB0ZXh0TGluZXMubGVuZ3RoIC0gMTsgaiA+PSAwICYmIGFsbExpbmVzLmxlbmd0aCA8IDM7IGotLSkge1xuICAgICAgICAgIGNvbnN0IGxpbmUgPSB0ZXh0TGluZXNbal1cbiAgICAgICAgICBpZiAoIWxpbmUpIGNvbnRpbnVlXG4gICAgICAgICAgYWxsTGluZXMucHVzaCh0cnVuY2F0ZVRvV2lkdGgobGluZSwgbWF4TGluZUxlbmd0aCkpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBSZXZlcnNlIHNvIG9sZGVzdCBvZiB0aGUgMyBpcyBmaXJzdCAocmVhZGluZyBvcmRlcilcbiAgcmV0dXJuIGFsbExpbmVzLnJldmVyc2UoKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gVGVhbW1hdGVTcGlubmVyTGluZSh7XG4gIHRlYW1tYXRlLFxuICBpc0xhc3QsXG4gIGlzU2VsZWN0ZWQsXG4gIGlzRm9yZWdyb3VuZGVkLFxuICBhbGxJZGxlLFxuICBzaG93UHJldmlldyxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3JhbmRvbVZlcmJdID0gdXNlU3RhdGUoXG4gICAgKCkgPT4gdGVhbW1hdGUuc3Bpbm5lclZlcmIgPz8gc2FtcGxlKGdldFNwaW5uZXJWZXJicygpKSxcbiAgKVxuICBjb25zdCBbcGFzdFRlbnNlVmVyYl0gPSB1c2VTdGF0ZShcbiAgICAoKSA9PiB0ZWFtbWF0ZS5wYXN0VGVuc2VWZXJiID8/IHNhbXBsZShUVVJOX0NPTVBMRVRJT05fVkVSQlMpLFxuICApXG4gIGNvbnN0IGlzSGlnaGxpZ2h0ZWQgPSBpc1NlbGVjdGVkIHx8IGlzRm9yZWdyb3VuZGVkXG4gIGNvbnN0IHRyZWVDaGFyID0gaXNIaWdobGlnaHRlZCA/IChpc0xhc3QgPyAn4pWY4pWQJyA6ICfilZ7ilZAnKSA6IGlzTGFzdCA/ICfilJTilIAnIDogJ+KUnOKUgCdcbiAgY29uc3QgbmFtZUNvbG9yID0gdG9JbmtDb2xvcih0ZWFtbWF0ZS5pZGVudGl0eS5jb2xvcilcbiAgY29uc3QgeyBjb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuXG4gIC8vIFRyYWNrIHdoZW4gdGVhbW1hdGUgYmVjYW1lIGlkbGUgKGZvciBcIklkbGUgZm9yIFguLi5cIiBkaXNwbGF5KVxuICBjb25zdCBpZGxlU3RhcnRSZWYgPSB1c2VSZWY8bnVtYmVyIHwgbnVsbD4obnVsbClcbiAgLy8gRnJlZXplIGVsYXBzZWQgdGltZSB3aGVuIGVudGVyaW5nIGFsbC1pZGxlIHN0YXRlXG4gIGNvbnN0IGZyb3plbkR1cmF0aW9uUmVmID0gdXNlUmVmPHN0cmluZyB8IG51bGw+KG51bGwpXG5cbiAgLy8gVHJhY2sgaWRsZSBzdGFydCB0aW1lXG4gIGlmICh0ZWFtbWF0ZS5pc0lkbGUgJiYgaWRsZVN0YXJ0UmVmLmN1cnJlbnQgPT09IG51bGwpIHtcbiAgICBpZGxlU3RhcnRSZWYuY3VycmVudCA9IERhdGUubm93KClcbiAgfSBlbHNlIGlmICghdGVhbW1hdGUuaXNJZGxlKSB7XG4gICAgaWRsZVN0YXJ0UmVmLmN1cnJlbnQgPSBudWxsXG4gIH1cblxuICAvLyBSZXNldCBmcm96ZW4gZHVyYXRpb24gd2hlbiBsZWF2aW5nIGFsbC1pZGxlIHN0YXRlXG4gIGlmICghYWxsSWRsZSAmJiBmcm96ZW5EdXJhdGlvblJlZi5jdXJyZW50ICE9PSBudWxsKSB7XG4gICAgZnJvemVuRHVyYXRpb25SZWYuY3VycmVudCA9IG51bGxcbiAgfVxuXG4gIC8vIEdldCBlbGFwc2VkIGlkbGUgdGltZSAoaG93IGxvbmcgdGhleSd2ZSBiZWVuIGlkbGUpIC0gZm9yIFwiSWRsZSBmb3IgWC4uLlwiIGRpc3BsYXlcbiAgY29uc3QgaWRsZUVsYXBzZWRUaW1lID0gdXNlRWxhcHNlZFRpbWUoXG4gICAgaWRsZVN0YXJ0UmVmLmN1cnJlbnQgPz8gRGF0ZS5ub3coKSxcbiAgICB0ZWFtbWF0ZS5pc0lkbGUgJiYgIWFsbElkbGUsXG4gIClcblxuICAvLyBGcmVlemUgdGhlIGR1cmF0aW9uIHdoZW4gd2UgZmlyc3QgZGV0ZWN0IGFsbCBpZGxlXG4gIC8vIFVzZSB0aGUgdGVhbW1hdGUncyBhY3R1YWwgd29yayB0aW1lIChzaW5jZSB0YXNrIHN0YXJ0ZWQpIGZvciB0aGUgcGFzdC10ZW5zZSBkaXNwbGF5XG4gIGlmIChhbGxJZGxlICYmIGZyb3plbkR1cmF0aW9uUmVmLmN1cnJlbnQgPT09IG51bGwpIHtcbiAgICBmcm96ZW5EdXJhdGlvblJlZi5jdXJyZW50ID0gZm9ybWF0RHVyYXRpb24oXG4gICAgICBNYXRoLm1heChcbiAgICAgICAgMCxcbiAgICAgICAgRGF0ZS5ub3coKSAtIHRlYW1tYXRlLnN0YXJ0VGltZSAtICh0ZWFtbWF0ZS50b3RhbFBhdXNlZE1zID8/IDApLFxuICAgICAgKSxcbiAgICApXG4gIH1cblxuICAvLyBVc2UgZnJvemVuIHdvcmsgZHVyYXRpb24gd2hlbiBhbGwgaWRsZSwgb3RoZXJ3aXNlIHVzZSBpZGxlIGVsYXBzZWQgdGltZVxuICBjb25zdCBkaXNwbGF5VGltZSA9IGFsbElkbGVcbiAgICA/IChmcm96ZW5EdXJhdGlvblJlZi5jdXJyZW50ID8/XG4gICAgICAoKCkgPT4ge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYGZyb3plbkR1cmF0aW9uUmVmIGlzIG51bGwgZm9yIGlkbGUgdGVhbW1hdGUgJHt0ZWFtbWF0ZS5pZGVudGl0eS5hZ2VudE5hbWV9YCxcbiAgICAgICAgKVxuICAgICAgfSkoKSlcbiAgICA6IGlkbGVFbGFwc2VkVGltZVxuXG4gIC8vIExheW91dDogcGFkZGluZ0xlZnQoMykgKyBwb2ludGVyKDEpICsgc3BhY2UoMSkgKyB0cmVlQ2hhcigyKSArIHNwYWNlKDEpID0gOCBmaXhlZCBjaGFyc1xuICAvLyBUaGVuIG9wdGlvbmFsbHk6IEBuYW1lICsgXCI6IFwiIE9SIGp1c3QgXCI6IFwiXG4gIC8vIFRoZW46IGFjdGl2aXR5IHRleHQgKyBvcHRpb25hbCBleHRyYXMgKHN0YXRzLCBoaW50cylcbiAgY29uc3QgYmFzZVByZWZpeCA9IDhcbiAgY29uc3QgZnVsbEFnZW50TmFtZSA9IGBAJHt0ZWFtbWF0ZS5pZGVudGl0eS5hZ2VudE5hbWV9YFxuICBjb25zdCBmdWxsTmFtZVdpZHRoID0gc3RyaW5nV2lkdGgoZnVsbEFnZW50TmFtZSlcblxuICAvLyBHZXQgc3RhdHMgZnJvbSBwcm9ncmVzc1xuICBjb25zdCB0b29sVXNlQ291bnQgPSB0ZWFtbWF0ZS5wcm9ncmVzcz8udG9vbFVzZUNvdW50ID8/IDBcbiAgY29uc3QgdG9rZW5Db3VudCA9IHRlYW1tYXRlLnByb2dyZXNzPy50b2tlbkNvdW50ID8/IDBcbiAgY29uc3Qgc3RhdHNUZXh0ID0gYCDCtyAke3Rvb2xVc2VDb3VudH0gdG9vbCAke3Rvb2xVc2VDb3VudCA9PT0gMSA/ICd1c2UnIDogJ3VzZXMnfSDCtyAke2Zvcm1hdE51bWJlcih0b2tlbkNvdW50KX0gdG9rZW5zYFxuICBjb25zdCBzdGF0c1dpZHRoID0gc3RyaW5nV2lkdGgoc3RhdHNUZXh0KVxuICBjb25zdCBzZWxlY3RIaW50VGV4dCA9IGAgwrcgJHtURUFNTUFURV9TRUxFQ1RfSElOVH1gXG4gIGNvbnN0IHNlbGVjdEhpbnRXaWR0aCA9IHN0cmluZ1dpZHRoKHNlbGVjdEhpbnRUZXh0KVxuICBjb25zdCB2aWV3SGludFRleHQgPSAnIMK3IGVudGVyIHRvIHZpZXcnXG4gIGNvbnN0IHZpZXdIaW50V2lkdGggPSBzdHJpbmdXaWR0aCh2aWV3SGludFRleHQpXG5cbiAgLy8gUHJvZ3Jlc3NpdmUgcmVzcG9uc2l2ZSBsYXlvdXQ6XG4gIC8vIFdpZGUgKDgwKyk6IGZ1bGwgbmFtZSArIGFjdGl2aXR5ICsgc3RhdHMgKyBoaW50XG4gIC8vIE1lZGl1bSAoNjAtODApOiBmdWxsIG5hbWUgKyBhY3Rpdml0eVxuICAvLyBOYXJyb3cgKDw2MCk6IGhpZGUgbmFtZSwganVzdCBzaG93IGFjdGl2aXR5XG4gIGNvbnN0IG1pbkFjdGl2aXR5V2lkdGggPSAyNVxuXG4gIC8vIEhpZGUgbmFtZSBvbiBuYXJyb3cgdGVybWluYWxzICg8IDYwIGNvbHMpIG9yIGlmIHRoZXJlJ3Mgbm90IGVub3VnaCByb29tXG4gIGNvbnN0IHNwYWNlV2l0aEZ1bGxOYW1lID0gY29sdW1ucyAtIGJhc2VQcmVmaXggLSBmdWxsTmFtZVdpZHRoIC0gMlxuICBjb25zdCBzaG93TmFtZSA9IGNvbHVtbnMgPj0gNjAgJiYgc3BhY2VXaXRoRnVsbE5hbWUgPj0gbWluQWN0aXZpdHlXaWR0aFxuICBjb25zdCBuYW1lV2lkdGggPSBzaG93TmFtZSA/IGZ1bGxOYW1lV2lkdGggKyAyIDogMCAvLyArMiBmb3IgXCI6IFwiIHdoZW4gbmFtZSBzaG93blxuICBjb25zdCBhdmFpbGFibGVGb3JBY3Rpdml0eSA9IGNvbHVtbnMgLSBiYXNlUHJlZml4IC0gbmFtZVdpZHRoXG5cbiAgLy8gUHJvZ3Jlc3NpdmUgaGlkaW5nOiB2aWV3IGhpbnQg4oaSIHNlbGVjdCBoaW50IOKGkiBzdGF0c1xuICAvLyBTdGF0cyBhbHdheXMgdmlzaWJsZSAoZGltbWVkIHdoZW4gbm90IHNlbGVjdGVkKTsgaGludHMgb25seSB3aGVuIGhpZ2hsaWdodGVkL3NlbGVjdGVkXG4gIGNvbnN0IHNob3dWaWV3SGludCA9XG4gICAgaXNTZWxlY3RlZCAmJlxuICAgICFpc0ZvcmVncm91bmRlZCAmJlxuICAgIGF2YWlsYWJsZUZvckFjdGl2aXR5ID4gdmlld0hpbnRXaWR0aCArIHN0YXRzV2lkdGggKyBtaW5BY3Rpdml0eVdpZHRoICsgNVxuICBjb25zdCBzaG93U2VsZWN0SGludCA9XG4gICAgaXNIaWdobGlnaHRlZCAmJlxuICAgIGF2YWlsYWJsZUZvckFjdGl2aXR5ID5cbiAgICAgIHNlbGVjdEhpbnRXaWR0aCArXG4gICAgICAgIChzaG93Vmlld0hpbnQgPyB2aWV3SGludFdpZHRoIDogMCkgK1xuICAgICAgICBzdGF0c1dpZHRoICtcbiAgICAgICAgbWluQWN0aXZpdHlXaWR0aCArXG4gICAgICAgIDVcbiAgY29uc3Qgc2hvd1N0YXRzID0gYXZhaWxhYmxlRm9yQWN0aXZpdHkgPiBzdGF0c1dpZHRoICsgbWluQWN0aXZpdHlXaWR0aCArIDVcblxuICAvLyBBY3Rpdml0eSB0ZXh0IGdldHMgcmVtYWluaW5nIHNwYWNlXG4gIGNvbnN0IGV4dHJhc0Nvc3QgPVxuICAgIChzaG93U3RhdHMgPyBzdGF0c1dpZHRoIDogMCkgK1xuICAgIChzaG93U2VsZWN0SGludCA/IHNlbGVjdEhpbnRXaWR0aCA6IDApICtcbiAgICAoc2hvd1ZpZXdIaW50ID8gdmlld0hpbnRXaWR0aCA6IDApXG4gIGNvbnN0IGFjdGl2aXR5TWF4V2lkdGggPSBNYXRoLm1heChcbiAgICBtaW5BY3Rpdml0eVdpZHRoLFxuICAgIGF2YWlsYWJsZUZvckFjdGl2aXR5IC0gZXh0cmFzQ29zdCAtIDEsXG4gIClcblxuICAvLyBGb3JtYXQgdGhlIGFjdGl2aXR5IHRleHQgZm9yIGFjdGl2ZSB0ZWFtbWF0ZXMsIHJvbGxpbmcgdXAgc2VhcmNoL3JlYWQgb3BzXG4gIGNvbnN0IGFjdGl2aXR5VGV4dCA9ICgoKSA9PiB7XG4gICAgY29uc3QgYWN0aXZpdGllcyA9IHRlYW1tYXRlLnByb2dyZXNzPy5yZWNlbnRBY3Rpdml0aWVzXG4gICAgaWYgKGFjdGl2aXRpZXMgJiYgYWN0aXZpdGllcy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBzdW1tYXJ5ID0gc3VtbWFyaXplUmVjZW50QWN0aXZpdGllcyhhY3Rpdml0aWVzKVxuICAgICAgaWYgKHN1bW1hcnkpIHJldHVybiB0cnVuY2F0ZVRvV2lkdGgoc3VtbWFyeSwgYWN0aXZpdHlNYXhXaWR0aClcbiAgICB9XG4gICAgY29uc3QgZGVzYyA9IHRlYW1tYXRlLnByb2dyZXNzPy5sYXN0QWN0aXZpdHk/LmFjdGl2aXR5RGVzY3JpcHRpb25cbiAgICBpZiAoZGVzYykgcmV0dXJuIHRydW5jYXRlVG9XaWR0aChkZXNjLCBhY3Rpdml0eU1heFdpZHRoKVxuICAgIHJldHVybiByYW5kb21WZXJiXG4gIH0pKClcblxuICAvLyBTdGF0dXMgcmVuZGVyaW5nIGxvZ2ljXG4gIGNvbnN0IHJlbmRlclN0YXR1cyA9ICgpOiBSZWFjdC5SZWFjdE5vZGUgPT4ge1xuICAgIGlmICh0ZWFtbWF0ZS5zaHV0ZG93blJlcXVlc3RlZCkge1xuICAgICAgcmV0dXJuIDxUZXh0IGRpbUNvbG9yPltzdG9wcGluZ108L1RleHQ+XG4gICAgfVxuICAgIGlmICh0ZWFtbWF0ZS5hd2FpdGluZ1BsYW5BcHByb3ZhbCkge1xuICAgICAgcmV0dXJuIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPlthd2FpdGluZyBhcHByb3ZhbF08L1RleHQ+XG4gICAgfVxuICAgIGlmICh0ZWFtbWF0ZS5pc0lkbGUpIHtcbiAgICAgIGlmIChhbGxJZGxlKSB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7cGFzdFRlbnNlVmVyYn0gZm9yIHtkaXNwbGF5VGltZX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIHJldHVybiA8VGV4dCBkaW1Db2xvcj5JZGxlIGZvciB7aWRsZUVsYXBzZWRUaW1lfTwvVGV4dD5cbiAgICB9XG4gICAgLy8gQWN0aXZlIC0gc2hvdyBzcGlubmVyIGdseXBoICsgYWN0aXZpdHkgZGVzY3JpcHRpb24gKG9ubHkgd2hlbiBub3QgaGlnaGxpZ2h0ZWQ7XG4gICAgLy8gd2hlbiBoaWdobGlnaHRlZCwgdGhlIG1haW4gc3Bpbm5lciBhYm92ZSBhbHJlYWR5IHNob3dzIHRoZSB2ZXJiKVxuICAgIGlmIChpc0hpZ2hsaWdodGVkKSB7XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cbiAgICByZXR1cm4gKFxuICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgIHthY3Rpdml0eVRleHQ/LmVuZHNXaXRoKCfigKYnKSA/IGFjdGl2aXR5VGV4dCA6IGAke2FjdGl2aXR5VGV4dH3igKZgfVxuICAgICAgPC9UZXh0PlxuICAgIClcbiAgfVxuXG4gIC8vIEdldCBwcmV2aWV3IGxpbmVzIGlmIGVuYWJsZWRcbiAgY29uc3QgcHJldmlld0xpbmVzID0gc2hvd1ByZXZpZXcgPyBnZXRNZXNzYWdlUHJldmlldyh0ZWFtbWF0ZS5tZXNzYWdlcykgOiBbXVxuXG4gIC8vIFRyZWUgY29udGludWF0aW9uIGNoYXJhY3RlciBmb3IgcHJldmlldyBsaW5lc1xuICBjb25zdCBwcmV2aWV3VHJlZUNoYXIgPSBpc0xhc3QgPyAnICAgJyA6ICfilIIgICdcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPEJveCBwYWRkaW5nTGVmdD17M30+XG4gICAgICAgIHsvKiBTZWxlY3Rpb24gaW5kaWNhdG9yOiBwb2ludGVyIHdoZW4gc2VsZWN0ZWQsIG90aGVyd2lzZSBzcGFjZSAqL31cbiAgICAgICAgPFRleHQgY29sb3I9e2lzU2VsZWN0ZWQgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9IGJvbGQ9e2lzU2VsZWN0ZWR9PlxuICAgICAgICAgIHtpc1NlbGVjdGVkID8gZmlndXJlcy5wb2ludGVyIDogJyAnfVxuICAgICAgICA8L1RleHQ+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPXshaXNTZWxlY3RlZH0+e3RyZWVDaGFyfSA8L1RleHQ+XG4gICAgICAgIHsvKiBBZ2VudCBuYW1lOiBoaWRkZW4gb24gdmVyeSBuYXJyb3cgc2NyZWVucyAqL31cbiAgICAgICAge3Nob3dOYW1lICYmIChcbiAgICAgICAgICA8VGV4dCBjb2xvcj17aXNTZWxlY3RlZCA/ICdzdWdnZXN0aW9uJyA6IG5hbWVDb2xvcn0+XG4gICAgICAgICAgICBAe3RlYW1tYXRlLmlkZW50aXR5LmFnZW50TmFtZX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIHtzaG93TmFtZSAmJiA8VGV4dCBkaW1Db2xvcj17IWlzU2VsZWN0ZWR9PjogPC9UZXh0Pn1cbiAgICAgICAge3JlbmRlclN0YXR1cygpfVxuICAgICAgICB7LyogU3RhdHM6IG9ubHkgc2hvd24gd2hlbiBzZWxlY3RlZCBhbmQgdGVybWluYWwgaXMgd2lkZSBlbm91Z2ggKi99XG4gICAgICAgIHtzaG93U3RhdHMgJiYgKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgeycgJ31cbiAgICAgICAgICAgIMK3IHt0b29sVXNlQ291bnR9IHRvb2wge3Rvb2xVc2VDb3VudCA9PT0gMSA/ICd1c2UnIDogJ3VzZXMnfSDCt3snICd9XG4gICAgICAgICAgICB7Zm9ybWF0TnVtYmVyKHRva2VuQ291bnQpfSB0b2tlbnNcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIHsvKiBIaW50czogc2VsZWN0IGhpbnQgd2hlbiBoaWdobGlnaHRlZCwgdmlldyBoaW50IHdoZW4gc2VsZWN0ZWQgYnV0IG5vdCBmb3JlZ3JvdW5kZWQgKi99XG4gICAgICAgIHtzaG93U2VsZWN0SGludCAmJiA8VGV4dCBkaW1Db2xvcj4gwrcge1RFQU1NQVRFX1NFTEVDVF9ISU5UfTwvVGV4dD59XG4gICAgICAgIHtzaG93Vmlld0hpbnQgJiYgPFRleHQgZGltQ29sb3I+IMK3IGVudGVyIHRvIHZpZXc8L1RleHQ+fVxuICAgICAgPC9Cb3g+XG4gICAgICB7LyogUHJldmlldyBsaW5lcyAqL31cbiAgICAgIHtwcmV2aWV3TGluZXMubWFwKChsaW5lLCBpZHgpID0+IChcbiAgICAgICAgPEJveCBrZXk9e2lkeH0gcGFkZGluZ0xlZnQ9ezN9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+e3ByZXZpZXdUcmVlQ2hhcn0gPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntsaW5lfTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApKX1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPQSxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPQyxNQUFNLE1BQU0scUJBQXFCO0FBQ3hDLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsTUFBTSxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUN4QyxTQUFTQyxlQUFlLFFBQVEsaUNBQWlDO0FBQ2pFLFNBQVNDLHFCQUFxQixRQUFRLHdDQUF3QztBQUM5RSxTQUFTQyxjQUFjLFFBQVEsK0JBQStCO0FBQzlELFNBQVNDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDaEUsU0FBU0MsV0FBVyxRQUFRLDBCQUEwQjtBQUN0RCxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLGNBQWNDLDBCQUEwQixRQUFRLDRDQUE0QztBQUM1RixTQUFTQyx5QkFBeUIsUUFBUSxtQ0FBbUM7QUFDN0UsU0FDRUMsY0FBYyxFQUNkQyxZQUFZLEVBQ1pDLGVBQWUsUUFDVix1QkFBdUI7QUFDOUIsU0FBU0MsVUFBVSxRQUFRLG9CQUFvQjtBQUMvQyxTQUFTQyxvQkFBb0IsUUFBUSx5QkFBeUI7QUFFOUQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLFFBQVEsRUFBRVIsMEJBQTBCO0VBQ3BDUyxNQUFNLEVBQUUsT0FBTztFQUNmQyxVQUFVLENBQUMsRUFBRSxPQUFPO0VBQ3BCQyxjQUFjLENBQUMsRUFBRSxPQUFPO0VBQ3hCQyxPQUFPLENBQUMsRUFBRSxPQUFPO0VBQ2pCQyxXQUFXLENBQUMsRUFBRSxPQUFPO0FBQ3ZCLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTQyxpQkFBaUJBLENBQ3hCQyxRQUFRLEVBQUVmLDBCQUEwQixDQUFDLFVBQVUsQ0FBQyxDQUNqRCxFQUFFLE1BQU0sRUFBRSxDQUFDO0VBQ1YsSUFBSSxDQUFDZSxRQUFRLEVBQUVDLE1BQU0sRUFBRSxPQUFPLEVBQUU7RUFFaEMsTUFBTUMsUUFBUSxFQUFFLE1BQU0sRUFBRSxHQUFHLEVBQUU7RUFDN0IsTUFBTUMsYUFBYSxHQUFHLEVBQUU7O0VBRXhCO0VBQ0EsS0FBSyxJQUFJQyxDQUFDLEdBQUdKLFFBQVEsQ0FBQ0MsTUFBTSxHQUFHLENBQUMsRUFBRUcsQ0FBQyxJQUFJLENBQUMsSUFBSUYsUUFBUSxDQUFDRCxNQUFNLEdBQUcsQ0FBQyxFQUFFRyxDQUFDLEVBQUUsRUFBRTtJQUNwRSxNQUFNQyxHQUFHLEdBQUdMLFFBQVEsQ0FBQ0ksQ0FBQyxDQUFDO0lBQ3ZCO0lBQ0EsSUFDRSxDQUFDQyxHQUFHLElBQ0hBLEdBQUcsQ0FBQ0MsSUFBSSxLQUFLLE1BQU0sSUFBSUQsR0FBRyxDQUFDQyxJQUFJLEtBQUssV0FBWSxJQUNqRCxDQUFDRCxHQUFHLENBQUNFLE9BQU8sRUFBRUMsT0FBTyxFQUFFUCxNQUFNLEVBQzdCO01BQ0E7SUFDRjtJQUNBLE1BQU1PLE9BQU8sR0FBR0gsR0FBRyxDQUFDRSxPQUFPLENBQUNDLE9BQU87SUFFbkMsS0FBSyxNQUFNQyxLQUFLLElBQUlELE9BQU8sRUFBRTtNQUMzQixJQUFJTixRQUFRLENBQUNELE1BQU0sSUFBSSxDQUFDLEVBQUU7TUFDMUIsSUFBSSxDQUFDUSxLQUFLLElBQUksT0FBT0EsS0FBSyxLQUFLLFFBQVEsRUFBRTtNQUV6QyxJQUFJLE1BQU0sSUFBSUEsS0FBSyxJQUFJQSxLQUFLLENBQUNILElBQUksS0FBSyxVQUFVLElBQUksTUFBTSxJQUFJRyxLQUFLLEVBQUU7UUFDbkU7UUFDQSxNQUFNQyxLQUFLLEdBQ1QsT0FBTyxJQUFJRCxLQUFLLEdBQUlBLEtBQUssQ0FBQ0MsS0FBSyxJQUFJQyxNQUFNLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxHQUFJLElBQUk7UUFDcEUsSUFBSUMsUUFBUSxHQUFHLFNBQVNILEtBQUssQ0FBQ0ksSUFBSSxHQUFHO1FBQ3JDLElBQUlILEtBQUssRUFBRTtVQUNUO1VBQ0EsTUFBTUksSUFBSSxHQUNQSixLQUFLLENBQUNLLFdBQVcsSUFBSSxNQUFNLEdBQUcsU0FBUyxJQUN2Q0wsS0FBSyxDQUFDTSxNQUFNLElBQUksTUFBTSxHQUFHLFNBQVUsSUFDbkNOLEtBQUssQ0FBQ08sT0FBTyxJQUFJLE1BQU0sR0FBRyxTQUFVLElBQ3BDUCxLQUFLLENBQUNRLEtBQUssSUFBSSxNQUFNLEdBQUcsU0FBVSxJQUNsQ1IsS0FBSyxDQUFDUyxPQUFPLElBQUksTUFBTSxHQUFHLFNBQVU7VUFDdkMsSUFBSUwsSUFBSSxFQUFFO1lBQ1JGLFFBQVEsR0FBR0UsSUFBSSxDQUFDTSxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUlSLFFBQVE7VUFDNUM7UUFDRjtRQUNBVixRQUFRLENBQUNtQixJQUFJLENBQUNoQyxlQUFlLENBQUN1QixRQUFRLEVBQUVULGFBQWEsQ0FBQyxDQUFDO01BQ3pELENBQUMsTUFBTSxJQUFJLE1BQU0sSUFBSU0sS0FBSyxJQUFJQSxLQUFLLENBQUNILElBQUksS0FBSyxNQUFNLElBQUksTUFBTSxJQUFJRyxLQUFLLEVBQUU7UUFDdEUsTUFBTWEsU0FBUyxHQUFHLENBQUNiLEtBQUssQ0FBQ2MsSUFBSSxJQUFJLE1BQU0sRUFDcENILEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FDWEksTUFBTSxDQUFDQyxDQUFDLElBQUlBLENBQUMsQ0FBQ0MsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUN4QjtRQUNBLEtBQUssSUFBSUMsQ0FBQyxHQUFHTCxTQUFTLENBQUNyQixNQUFNLEdBQUcsQ0FBQyxFQUFFMEIsQ0FBQyxJQUFJLENBQUMsSUFBSXpCLFFBQVEsQ0FBQ0QsTUFBTSxHQUFHLENBQUMsRUFBRTBCLENBQUMsRUFBRSxFQUFFO1VBQ3JFLE1BQU1DLElBQUksR0FBR04sU0FBUyxDQUFDSyxDQUFDLENBQUM7VUFDekIsSUFBSSxDQUFDQyxJQUFJLEVBQUU7VUFDWDFCLFFBQVEsQ0FBQ21CLElBQUksQ0FBQ2hDLGVBQWUsQ0FBQ3VDLElBQUksRUFBRXpCLGFBQWEsQ0FBQyxDQUFDO1FBQ3JEO01BQ0Y7SUFDRjtFQUNGOztFQUVBO0VBQ0EsT0FBT0QsUUFBUSxDQUFDMkIsT0FBTyxDQUFDLENBQUM7QUFDM0I7QUFFQSxPQUFPLFNBQVNDLG1CQUFtQkEsQ0FBQztFQUNsQ3JDLFFBQVE7RUFDUkMsTUFBTTtFQUNOQyxVQUFVO0VBQ1ZDLGNBQWM7RUFDZEMsT0FBTztFQUNQQztBQUNLLENBQU4sRUFBRU4sS0FBSyxDQUFDLEVBQUVqQixLQUFLLENBQUN3RCxTQUFTLENBQUM7RUFDekIsTUFBTSxDQUFDQyxVQUFVLENBQUMsR0FBR3ZELFFBQVEsQ0FDM0IsTUFBTWdCLFFBQVEsQ0FBQ3dDLFdBQVcsSUFBSTNELE1BQU0sQ0FBQ0ksZUFBZSxDQUFDLENBQUMsQ0FDeEQsQ0FBQztFQUNELE1BQU0sQ0FBQ3dELGFBQWEsQ0FBQyxHQUFHekQsUUFBUSxDQUM5QixNQUFNZ0IsUUFBUSxDQUFDeUMsYUFBYSxJQUFJNUQsTUFBTSxDQUFDSyxxQkFBcUIsQ0FDOUQsQ0FBQztFQUNELE1BQU13RCxhQUFhLEdBQUd4QyxVQUFVLElBQUlDLGNBQWM7RUFDbEQsTUFBTXdDLFFBQVEsR0FBR0QsYUFBYSxHQUFJekMsTUFBTSxHQUFHLElBQUksR0FBRyxJQUFJLEdBQUlBLE1BQU0sR0FBRyxJQUFJLEdBQUcsSUFBSTtFQUM5RSxNQUFNMkMsU0FBUyxHQUFHL0MsVUFBVSxDQUFDRyxRQUFRLENBQUM2QyxRQUFRLENBQUNDLEtBQUssQ0FBQztFQUNyRCxNQUFNO0lBQUVDO0VBQVEsQ0FBQyxHQUFHM0QsZUFBZSxDQUFDLENBQUM7O0VBRXJDO0VBQ0EsTUFBTTRELFlBQVksR0FBR2pFLE1BQU0sQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDO0VBQ2hEO0VBQ0EsTUFBTWtFLGlCQUFpQixHQUFHbEUsTUFBTSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUM7O0VBRXJEO0VBQ0EsSUFBSWlCLFFBQVEsQ0FBQ2tELE1BQU0sSUFBSUYsWUFBWSxDQUFDRyxPQUFPLEtBQUssSUFBSSxFQUFFO0lBQ3BESCxZQUFZLENBQUNHLE9BQU8sR0FBR0MsSUFBSSxDQUFDQyxHQUFHLENBQUMsQ0FBQztFQUNuQyxDQUFDLE1BQU0sSUFBSSxDQUFDckQsUUFBUSxDQUFDa0QsTUFBTSxFQUFFO0lBQzNCRixZQUFZLENBQUNHLE9BQU8sR0FBRyxJQUFJO0VBQzdCOztFQUVBO0VBQ0EsSUFBSSxDQUFDL0MsT0FBTyxJQUFJNkMsaUJBQWlCLENBQUNFLE9BQU8sS0FBSyxJQUFJLEVBQUU7SUFDbERGLGlCQUFpQixDQUFDRSxPQUFPLEdBQUcsSUFBSTtFQUNsQzs7RUFFQTtFQUNBLE1BQU1HLGVBQWUsR0FBR25FLGNBQWMsQ0FDcEM2RCxZQUFZLENBQUNHLE9BQU8sSUFBSUMsSUFBSSxDQUFDQyxHQUFHLENBQUMsQ0FBQyxFQUNsQ3JELFFBQVEsQ0FBQ2tELE1BQU0sSUFBSSxDQUFDOUMsT0FDdEIsQ0FBQzs7RUFFRDtFQUNBO0VBQ0EsSUFBSUEsT0FBTyxJQUFJNkMsaUJBQWlCLENBQUNFLE9BQU8sS0FBSyxJQUFJLEVBQUU7SUFDakRGLGlCQUFpQixDQUFDRSxPQUFPLEdBQUd6RCxjQUFjLENBQ3hDNkQsSUFBSSxDQUFDQyxHQUFHLENBQ04sQ0FBQyxFQUNESixJQUFJLENBQUNDLEdBQUcsQ0FBQyxDQUFDLEdBQUdyRCxRQUFRLENBQUN5RCxTQUFTLElBQUl6RCxRQUFRLENBQUMwRCxhQUFhLElBQUksQ0FBQyxDQUNoRSxDQUNGLENBQUM7RUFDSDs7RUFFQTtFQUNBLE1BQU1DLFdBQVcsR0FBR3ZELE9BQU8sR0FDdEI2QyxpQkFBaUIsQ0FBQ0UsT0FBTyxJQUMxQixDQUFDLE1BQU07SUFDTCxNQUFNLElBQUlTLEtBQUssQ0FDYiwrQ0FBK0M1RCxRQUFRLENBQUM2QyxRQUFRLENBQUNnQixTQUFTLEVBQzVFLENBQUM7RUFDSCxDQUFDLEVBQUUsQ0FBQyxHQUNKUCxlQUFlOztFQUVuQjtFQUNBO0VBQ0E7RUFDQSxNQUFNUSxVQUFVLEdBQUcsQ0FBQztFQUNwQixNQUFNQyxhQUFhLEdBQUcsSUFBSS9ELFFBQVEsQ0FBQzZDLFFBQVEsQ0FBQ2dCLFNBQVMsRUFBRTtFQUN2RCxNQUFNRyxhQUFhLEdBQUczRSxXQUFXLENBQUMwRSxhQUFhLENBQUM7O0VBRWhEO0VBQ0EsTUFBTUUsWUFBWSxHQUFHakUsUUFBUSxDQUFDa0UsUUFBUSxFQUFFRCxZQUFZLElBQUksQ0FBQztFQUN6RCxNQUFNRSxVQUFVLEdBQUduRSxRQUFRLENBQUNrRSxRQUFRLEVBQUVDLFVBQVUsSUFBSSxDQUFDO0VBQ3JELE1BQU1DLFNBQVMsR0FBRyxNQUFNSCxZQUFZLFNBQVNBLFlBQVksS0FBSyxDQUFDLEdBQUcsS0FBSyxHQUFHLE1BQU0sTUFBTXRFLFlBQVksQ0FBQ3dFLFVBQVUsQ0FBQyxTQUFTO0VBQ3ZILE1BQU1FLFVBQVUsR0FBR2hGLFdBQVcsQ0FBQytFLFNBQVMsQ0FBQztFQUN6QyxNQUFNRSxjQUFjLEdBQUcsTUFBTXhFLG9CQUFvQixFQUFFO0VBQ25ELE1BQU15RSxlQUFlLEdBQUdsRixXQUFXLENBQUNpRixjQUFjLENBQUM7RUFDbkQsTUFBTUUsWUFBWSxHQUFHLGtCQUFrQjtFQUN2QyxNQUFNQyxhQUFhLEdBQUdwRixXQUFXLENBQUNtRixZQUFZLENBQUM7O0VBRS9DO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsTUFBTUUsZ0JBQWdCLEdBQUcsRUFBRTs7RUFFM0I7RUFDQSxNQUFNQyxpQkFBaUIsR0FBRzVCLE9BQU8sR0FBR2UsVUFBVSxHQUFHRSxhQUFhLEdBQUcsQ0FBQztFQUNsRSxNQUFNWSxRQUFRLEdBQUc3QixPQUFPLElBQUksRUFBRSxJQUFJNEIsaUJBQWlCLElBQUlELGdCQUFnQjtFQUN2RSxNQUFNRyxTQUFTLEdBQUdELFFBQVEsR0FBR1osYUFBYSxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUM7RUFDbkQsTUFBTWMsb0JBQW9CLEdBQUcvQixPQUFPLEdBQUdlLFVBQVUsR0FBR2UsU0FBUzs7RUFFN0Q7RUFDQTtFQUNBLE1BQU1FLFlBQVksR0FDaEI3RSxVQUFVLElBQ1YsQ0FBQ0MsY0FBYyxJQUNmMkUsb0JBQW9CLEdBQUdMLGFBQWEsR0FBR0osVUFBVSxHQUFHSyxnQkFBZ0IsR0FBRyxDQUFDO0VBQzFFLE1BQU1NLGNBQWMsR0FDbEJ0QyxhQUFhLElBQ2JvQyxvQkFBb0IsR0FDbEJQLGVBQWUsSUFDWlEsWUFBWSxHQUFHTixhQUFhLEdBQUcsQ0FBQyxDQUFDLEdBQ2xDSixVQUFVLEdBQ1ZLLGdCQUFnQixHQUNoQixDQUFDO0VBQ1AsTUFBTU8sU0FBUyxHQUFHSCxvQkFBb0IsR0FBR1QsVUFBVSxHQUFHSyxnQkFBZ0IsR0FBRyxDQUFDOztFQUUxRTtFQUNBLE1BQU1RLFVBQVUsR0FDZCxDQUFDRCxTQUFTLEdBQUdaLFVBQVUsR0FBRyxDQUFDLEtBQzFCVyxjQUFjLEdBQUdULGVBQWUsR0FBRyxDQUFDLENBQUMsSUFDckNRLFlBQVksR0FBR04sYUFBYSxHQUFHLENBQUMsQ0FBQztFQUNwQyxNQUFNVSxnQkFBZ0IsR0FBRzVCLElBQUksQ0FBQ0MsR0FBRyxDQUMvQmtCLGdCQUFnQixFQUNoQkksb0JBQW9CLEdBQUdJLFVBQVUsR0FBRyxDQUN0QyxDQUFDOztFQUVEO0VBQ0EsTUFBTUUsWUFBWSxHQUFHLENBQUMsTUFBTTtJQUMxQixNQUFNQyxVQUFVLEdBQUdyRixRQUFRLENBQUNrRSxRQUFRLEVBQUVvQixnQkFBZ0I7SUFDdEQsSUFBSUQsVUFBVSxJQUFJQSxVQUFVLENBQUM3RSxNQUFNLEdBQUcsQ0FBQyxFQUFFO01BQ3ZDLE1BQU0rRSxPQUFPLEdBQUc5Rix5QkFBeUIsQ0FBQzRGLFVBQVUsQ0FBQztNQUNyRCxJQUFJRSxPQUFPLEVBQUUsT0FBTzNGLGVBQWUsQ0FBQzJGLE9BQU8sRUFBRUosZ0JBQWdCLENBQUM7SUFDaEU7SUFDQSxNQUFNOUQsSUFBSSxHQUFHckIsUUFBUSxDQUFDa0UsUUFBUSxFQUFFc0IsWUFBWSxFQUFFQyxtQkFBbUI7SUFDakUsSUFBSXBFLElBQUksRUFBRSxPQUFPekIsZUFBZSxDQUFDeUIsSUFBSSxFQUFFOEQsZ0JBQWdCLENBQUM7SUFDeEQsT0FBTzVDLFVBQVU7RUFDbkIsQ0FBQyxFQUFFLENBQUM7O0VBRUo7RUFDQSxNQUFNbUQsWUFBWSxHQUFHQSxDQUFBLENBQUUsRUFBRTVHLEtBQUssQ0FBQ3dELFNBQVMsSUFBSTtJQUMxQyxJQUFJdEMsUUFBUSxDQUFDMkYsaUJBQWlCLEVBQUU7TUFDOUIsT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxFQUFFLElBQUksQ0FBQztJQUN6QztJQUNBLElBQUkzRixRQUFRLENBQUM0RixvQkFBb0IsRUFBRTtNQUNqQyxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsbUJBQW1CLEVBQUUsSUFBSSxDQUFDO0lBQ3pEO0lBQ0EsSUFBSTVGLFFBQVEsQ0FBQ2tELE1BQU0sRUFBRTtNQUNuQixJQUFJOUMsT0FBTyxFQUFFO1FBQ1gsT0FDRSxDQUFDLElBQUksQ0FBQyxRQUFRO0FBQ3hCLFlBQVksQ0FBQ3FDLGFBQWEsQ0FBQyxLQUFLLENBQUNrQixXQUFXO0FBQzVDLFVBQVUsRUFBRSxJQUFJLENBQUM7TUFFWDtNQUNBLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQ0wsZUFBZSxDQUFDLEVBQUUsSUFBSSxDQUFDO0lBQ3pEO0lBQ0E7SUFDQTtJQUNBLElBQUlaLGFBQWEsRUFBRTtNQUNqQixPQUFPLElBQUk7SUFDYjtJQUNBLE9BQ0UsQ0FBQyxJQUFJLENBQUMsUUFBUTtBQUNwQixRQUFRLENBQUMwQyxZQUFZLEVBQUVTLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBR1QsWUFBWSxHQUFHLEdBQUdBLFlBQVksR0FBRztBQUN4RSxNQUFNLEVBQUUsSUFBSSxDQUFDO0VBRVgsQ0FBQzs7RUFFRDtFQUNBLE1BQU1VLFlBQVksR0FBR3pGLFdBQVcsR0FBR0MsaUJBQWlCLENBQUNOLFFBQVEsQ0FBQ08sUUFBUSxDQUFDLEdBQUcsRUFBRTs7RUFFNUU7RUFDQSxNQUFNd0YsZUFBZSxHQUFHOUYsTUFBTSxHQUFHLEtBQUssR0FBRyxLQUFLO0VBRTlDLE9BQ0UsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLFFBQVE7QUFDL0IsTUFBTSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDMUIsUUFBUSxDQUFDLGlFQUFpRTtBQUMxRSxRQUFRLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDQyxVQUFVLEdBQUcsWUFBWSxHQUFHOEYsU0FBUyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM5RixVQUFVLENBQUM7QUFDN0UsVUFBVSxDQUFDQSxVQUFVLEdBQUd0QixPQUFPLENBQUNxSCxPQUFPLEdBQUcsR0FBRztBQUM3QyxRQUFRLEVBQUUsSUFBSTtBQUNkLFFBQVEsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQy9GLFVBQVUsQ0FBQyxDQUFDLENBQUN5QyxRQUFRLENBQUMsQ0FBQyxFQUFFLElBQUk7QUFDdEQsUUFBUSxDQUFDLCtDQUErQztBQUN4RCxRQUFRLENBQUNpQyxRQUFRLElBQ1AsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMxRSxVQUFVLEdBQUcsWUFBWSxHQUFHMEMsU0FBUyxDQUFDO0FBQzdELGFBQWEsQ0FBQzVDLFFBQVEsQ0FBQzZDLFFBQVEsQ0FBQ2dCLFNBQVM7QUFDekMsVUFBVSxFQUFFLElBQUksQ0FDUDtBQUNULFFBQVEsQ0FBQ2UsUUFBUSxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMxRSxVQUFVLENBQUMsQ0FBQyxFQUFFLEVBQUUsSUFBSSxDQUFDO0FBQzNELFFBQVEsQ0FBQ3dGLFlBQVksQ0FBQyxDQUFDO0FBQ3ZCLFFBQVEsQ0FBQyxpRUFBaUU7QUFDMUUsUUFBUSxDQUFDVCxTQUFTLElBQ1IsQ0FBQyxJQUFJLENBQUMsUUFBUTtBQUN4QixZQUFZLENBQUMsR0FBRztBQUNoQixjQUFjLENBQUNoQixZQUFZLENBQUMsTUFBTSxDQUFDQSxZQUFZLEtBQUssQ0FBQyxHQUFHLEtBQUssR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDLEdBQUc7QUFDN0UsWUFBWSxDQUFDdEUsWUFBWSxDQUFDd0UsVUFBVSxDQUFDLENBQUM7QUFDdEMsVUFBVSxFQUFFLElBQUksQ0FDUDtBQUNULFFBQVEsQ0FBQyx1RkFBdUY7QUFDaEcsUUFBUSxDQUFDYSxjQUFjLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQ2xGLG9CQUFvQixDQUFDLEVBQUUsSUFBSSxDQUFDO0FBQzFFLFFBQVEsQ0FBQ2lGLFlBQVksSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDO0FBQy9ELE1BQU0sRUFBRSxHQUFHO0FBQ1gsTUFBTSxDQUFDLG1CQUFtQjtBQUMxQixNQUFNLENBQUNlLFlBQVksQ0FBQ0ksR0FBRyxDQUFDLENBQUMvRCxJQUFJLEVBQUVnRSxHQUFHLEtBQzFCLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDQSxHQUFHLENBQUMsQ0FBQyxXQUFXLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDdEMsVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxFQUFFLElBQUk7QUFDaEMsVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQ0osZUFBZSxDQUFDLENBQUMsRUFBRSxJQUFJO0FBQ2pELFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM1RCxJQUFJLENBQUMsRUFBRSxJQUFJO0FBQ3JDLFFBQVEsRUFBRSxHQUFHLENBQ04sQ0FBQztBQUNSLElBQUksRUFBRSxHQUFHLENBQUM7QUFFViIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/Spinner/TeammateSpinnerTree.tsx b/components/Spinner/TeammateSpinnerTree.tsx new file mode 100644 index 0000000..da7c232 --- /dev/null +++ b/components/Spinner/TeammateSpinnerTree.tsx @@ -0,0 +1,272 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import * as React from 'react'; +import { Box, Text, type TextProps } from '../../ink.js'; +import { useAppState } from '../../state/AppState.js'; +import { getRunningTeammatesSorted } from '../../tasks/InProcessTeammateTask/InProcessTeammateTask.js'; +import { formatNumber } from '../../utils/format.js'; +import { TeammateSpinnerLine } from './TeammateSpinnerLine.js'; +import { TEAMMATE_SELECT_HINT } from './teammateSelectHint.js'; +type Props = { + selectedIndex?: number; + isInSelectionMode?: boolean; + allIdle?: boolean; + /** Leader's active verb (when leader is actively processing) */ + leaderVerb?: string; + /** Leader's token count (when leader is actively processing) */ + leaderTokenCount?: number; + /** Leader's idle status text (when leader is idle, e.g. "✻ Idle for 3s") */ + leaderIdleText?: string; +}; +export function TeammateSpinnerTree(t0) { + const $ = _c(61); + const { + selectedIndex, + isInSelectionMode, + allIdle, + leaderVerb, + leaderTokenCount, + leaderIdleText + } = t0; + const tasks = useAppState(_temp); + const viewingAgentTaskId = useAppState(_temp2); + const showTeammateMessagePreview = useAppState(_temp3); + let T0; + let isHideSelected; + let t1; + let t2; + let t3; + let t4; + let t5; + if ($[0] !== allIdle || $[1] !== isInSelectionMode || $[2] !== leaderIdleText || $[3] !== leaderTokenCount || $[4] !== leaderVerb || $[5] !== selectedIndex || $[6] !== showTeammateMessagePreview || $[7] !== tasks || $[8] !== viewingAgentTaskId) { + t5 = Symbol.for("react.early_return_sentinel"); + bb0: { + const teammateTasks = getRunningTeammatesSorted(tasks); + if (teammateTasks.length === 0) { + t5 = null; + break bb0; + } + const isLeaderForegrounded = viewingAgentTaskId === undefined; + const isLeaderSelected = isInSelectionMode && selectedIndex === -1; + const isLeaderHighlighted = isLeaderForegrounded || isLeaderSelected; + isHideSelected = isInSelectionMode === true && selectedIndex === teammateTasks.length; + T0 = Box; + t1 = "column"; + t2 = 1; + const t6 = isLeaderSelected ? "suggestion" : undefined; + const t7 = isLeaderSelected ? figures.pointer : " "; + let t8; + if ($[16] !== isLeaderHighlighted || $[17] !== t6 || $[18] !== t7) { + t8 = {t7}; + $[16] = isLeaderHighlighted; + $[17] = t6; + $[18] = t7; + $[19] = t8; + } else { + t8 = $[19]; + } + const t9 = !isLeaderHighlighted; + const t10 = isLeaderHighlighted ? "\u2552\u2550" : "\u250C\u2500"; + let t11; + if ($[20] !== isLeaderHighlighted || $[21] !== t10 || $[22] !== t9) { + t11 = {t10}{" "}; + $[20] = isLeaderHighlighted; + $[21] = t10; + $[22] = t9; + $[23] = t11; + } else { + t11 = $[23]; + } + const t12 = isLeaderSelected ? "suggestion" : "cyan_FOR_SUBAGENTS_ONLY"; + let t13; + if ($[24] !== isLeaderHighlighted || $[25] !== t12) { + t13 = team-lead; + $[24] = isLeaderHighlighted; + $[25] = t12; + $[26] = t13; + } else { + t13 = $[26]; + } + let t14; + if ($[27] !== isLeaderForegrounded || $[28] !== leaderVerb) { + t14 = !isLeaderForegrounded && leaderVerb && : {leaderVerb}…; + $[27] = isLeaderForegrounded; + $[28] = leaderVerb; + $[29] = t14; + } else { + t14 = $[29]; + } + let t15; + if ($[30] !== isLeaderForegrounded || $[31] !== leaderIdleText || $[32] !== leaderVerb) { + t15 = !isLeaderForegrounded && !leaderVerb && leaderIdleText && : {leaderIdleText}; + $[30] = isLeaderForegrounded; + $[31] = leaderIdleText; + $[32] = leaderVerb; + $[33] = t15; + } else { + t15 = $[33]; + } + let t16; + if ($[34] !== isLeaderHighlighted || $[35] !== leaderTokenCount) { + t16 = leaderTokenCount !== undefined && leaderTokenCount > 0 && {" "}· {formatNumber(leaderTokenCount)} tokens; + $[34] = isLeaderHighlighted; + $[35] = leaderTokenCount; + $[36] = t16; + } else { + t16 = $[36]; + } + let t17; + if ($[37] !== isLeaderHighlighted) { + t17 = isLeaderHighlighted && · {TEAMMATE_SELECT_HINT}; + $[37] = isLeaderHighlighted; + $[38] = t17; + } else { + t17 = $[38]; + } + let t18; + if ($[39] !== isLeaderForegrounded || $[40] !== isLeaderSelected) { + t18 = isLeaderSelected && !isLeaderForegrounded && · enter to view; + $[39] = isLeaderForegrounded; + $[40] = isLeaderSelected; + $[41] = t18; + } else { + t18 = $[41]; + } + if ($[42] !== t11 || $[43] !== t13 || $[44] !== t14 || $[45] !== t15 || $[46] !== t16 || $[47] !== t17 || $[48] !== t18 || $[49] !== t8) { + t3 = {t8}{t11}{t13}{t14}{t15}{t16}{t17}{t18}; + $[42] = t11; + $[43] = t13; + $[44] = t14; + $[45] = t15; + $[46] = t16; + $[47] = t17; + $[48] = t18; + $[49] = t8; + $[50] = t3; + } else { + t3 = $[50]; + } + t4 = teammateTasks.map((teammate, index) => ); + } + $[0] = allIdle; + $[1] = isInSelectionMode; + $[2] = leaderIdleText; + $[3] = leaderTokenCount; + $[4] = leaderVerb; + $[5] = selectedIndex; + $[6] = showTeammateMessagePreview; + $[7] = tasks; + $[8] = viewingAgentTaskId; + $[9] = T0; + $[10] = isHideSelected; + $[11] = t1; + $[12] = t2; + $[13] = t3; + $[14] = t4; + $[15] = t5; + } else { + T0 = $[9]; + isHideSelected = $[10]; + t1 = $[11]; + t2 = $[12]; + t3 = $[13]; + t4 = $[14]; + t5 = $[15]; + } + if (t5 !== Symbol.for("react.early_return_sentinel")) { + return t5; + } + let t6; + if ($[51] !== isHideSelected || $[52] !== isInSelectionMode) { + t6 = isInSelectionMode && ; + $[51] = isHideSelected; + $[52] = isInSelectionMode; + $[53] = t6; + } else { + t6 = $[53]; + } + let t7; + if ($[54] !== T0 || $[55] !== t1 || $[56] !== t2 || $[57] !== t3 || $[58] !== t4 || $[59] !== t6) { + t7 = {t3}{t4}{t6}; + $[54] = T0; + $[55] = t1; + $[56] = t2; + $[57] = t3; + $[58] = t4; + $[59] = t6; + $[60] = t7; + } else { + t7 = $[60]; + } + return t7; +} +function _temp3(s_1) { + return s_1.showTeammateMessagePreview; +} +function _temp2(s_0) { + return s_0.viewingAgentTaskId; +} +function _temp(s) { + return s.tasks; +} +function HideRow(t0) { + const $ = _c(18); + const { + isSelected + } = t0; + const t1 = isSelected ? "suggestion" : undefined; + const t2 = isSelected ? figures.pointer : " "; + let t3; + if ($[0] !== isSelected || $[1] !== t1 || $[2] !== t2) { + t3 = {t2}; + $[0] = isSelected; + $[1] = t1; + $[2] = t2; + $[3] = t3; + } else { + t3 = $[3]; + } + const t4 = !isSelected; + const t5 = isSelected ? "\u2558\u2550" : "\u2514\u2500"; + let t6; + if ($[4] !== isSelected || $[5] !== t4 || $[6] !== t5) { + t6 = {t5}{" "}; + $[4] = isSelected; + $[5] = t4; + $[6] = t5; + $[7] = t6; + } else { + t6 = $[7]; + } + const t7 = !isSelected; + let t8; + if ($[8] !== isSelected || $[9] !== t7) { + t8 = hide; + $[8] = isSelected; + $[9] = t7; + $[10] = t8; + } else { + t8 = $[10]; + } + let t9; + if ($[11] !== isSelected) { + t9 = isSelected && · enter to collapse; + $[11] = isSelected; + $[12] = t9; + } else { + t9 = $[12]; + } + let t10; + if ($[13] !== t3 || $[14] !== t6 || $[15] !== t8 || $[16] !== t9) { + t10 = {t3}{t6}{t8}{t9}; + $[13] = t3; + $[14] = t6; + $[15] = t8; + $[16] = t9; + $[17] = t10; + } else { + t10 = $[17]; + } + return t10; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwiVGV4dFByb3BzIiwidXNlQXBwU3RhdGUiLCJnZXRSdW5uaW5nVGVhbW1hdGVzU29ydGVkIiwiZm9ybWF0TnVtYmVyIiwiVGVhbW1hdGVTcGlubmVyTGluZSIsIlRFQU1NQVRFX1NFTEVDVF9ISU5UIiwiUHJvcHMiLCJzZWxlY3RlZEluZGV4IiwiaXNJblNlbGVjdGlvbk1vZGUiLCJhbGxJZGxlIiwibGVhZGVyVmVyYiIsImxlYWRlclRva2VuQ291bnQiLCJsZWFkZXJJZGxlVGV4dCIsIlRlYW1tYXRlU3Bpbm5lclRyZWUiLCJ0MCIsIiQiLCJfYyIsInRhc2tzIiwiX3RlbXAiLCJ2aWV3aW5nQWdlbnRUYXNrSWQiLCJfdGVtcDIiLCJzaG93VGVhbW1hdGVNZXNzYWdlUHJldmlldyIsIl90ZW1wMyIsIlQwIiwiaXNIaWRlU2VsZWN0ZWQiLCJ0MSIsInQyIiwidDMiLCJ0NCIsInQ1IiwiU3ltYm9sIiwiZm9yIiwiYmIwIiwidGVhbW1hdGVUYXNrcyIsImxlbmd0aCIsImlzTGVhZGVyRm9yZWdyb3VuZGVkIiwidW5kZWZpbmVkIiwiaXNMZWFkZXJTZWxlY3RlZCIsImlzTGVhZGVySGlnaGxpZ2h0ZWQiLCJ0NiIsInQ3IiwicG9pbnRlciIsInQ4IiwidDkiLCJ0MTAiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJ0MTYiLCJ0MTciLCJ0MTgiLCJtYXAiLCJ0ZWFtbWF0ZSIsImluZGV4IiwiaWQiLCJzXzEiLCJzIiwic18wIiwiSGlkZVJvdyIsImlzU2VsZWN0ZWQiXSwic291cmNlcyI6WyJUZWFtbWF0ZVNwaW5uZXJUcmVlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQsIHR5cGUgVGV4dFByb3BzIH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IGdldFJ1bm5pbmdUZWFtbWF0ZXNTb3J0ZWQgfSBmcm9tICcuLi8uLi90YXNrcy9JblByb2Nlc3NUZWFtbWF0ZVRhc2svSW5Qcm9jZXNzVGVhbW1hdGVUYXNrLmpzJ1xuaW1wb3J0IHsgZm9ybWF0TnVtYmVyIH0gZnJvbSAnLi4vLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgVGVhbW1hdGVTcGlubmVyTGluZSB9IGZyb20gJy4vVGVhbW1hdGVTcGlubmVyTGluZS5qcydcbmltcG9ydCB7IFRFQU1NQVRFX1NFTEVDVF9ISU5UIH0gZnJvbSAnLi90ZWFtbWF0ZVNlbGVjdEhpbnQuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHNlbGVjdGVkSW5kZXg/OiBudW1iZXJcbiAgaXNJblNlbGVjdGlvbk1vZGU/OiBib29sZWFuXG4gIGFsbElkbGU/OiBib29sZWFuXG4gIC8qKiBMZWFkZXIncyBhY3RpdmUgdmVyYiAod2hlbiBsZWFkZXIgaXMgYWN0aXZlbHkgcHJvY2Vzc2luZykgKi9cbiAgbGVhZGVyVmVyYj86IHN0cmluZ1xuICAvKiogTGVhZGVyJ3MgdG9rZW4gY291bnQgKHdoZW4gbGVhZGVyIGlzIGFjdGl2ZWx5IHByb2Nlc3NpbmcpICovXG4gIGxlYWRlclRva2VuQ291bnQ/OiBudW1iZXJcbiAgLyoqIExlYWRlcidzIGlkbGUgc3RhdHVzIHRleHQgKHdoZW4gbGVhZGVyIGlzIGlkbGUsIGUuZy4gXCLinLsgSWRsZSBmb3IgM3NcIikgKi9cbiAgbGVhZGVySWRsZVRleHQ/OiBzdHJpbmdcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFRlYW1tYXRlU3Bpbm5lclRyZWUoe1xuICBzZWxlY3RlZEluZGV4LFxuICBpc0luU2VsZWN0aW9uTW9kZSxcbiAgYWxsSWRsZSxcbiAgbGVhZGVyVmVyYixcbiAgbGVhZGVyVG9rZW5Db3VudCxcbiAgbGVhZGVySWRsZVRleHQsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHRhc2tzID0gdXNlQXBwU3RhdGUocyA9PiBzLnRhc2tzKVxuICBjb25zdCB2aWV3aW5nQWdlbnRUYXNrSWQgPSB1c2VBcHBTdGF0ZShzID0+IHMudmlld2luZ0FnZW50VGFza0lkKVxuICBjb25zdCBzaG93VGVhbW1hdGVNZXNzYWdlUHJldmlldyA9IHVzZUFwcFN0YXRlKFxuICAgIHMgPT4gcy5zaG93VGVhbW1hdGVNZXNzYWdlUHJldmlldyxcbiAgKVxuXG4gIGNvbnN0IHRlYW1tYXRlVGFza3MgPSBnZXRSdW5uaW5nVGVhbW1hdGVzU29ydGVkKHRhc2tzKVxuXG4gIC8vIERvbid0IHJlbmRlciBpZiBubyBydW5uaW5nIHRlYW1tYXRlc1xuICBpZiAodGVhbW1hdGVUYXNrcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gTGVhZGVyIGhpZ2hsaWdodGluZyBmb2xsb3dzIHNhbWUgcGF0dGVybiBhcyB0ZWFtbWF0ZXM6XG4gIC8vIGlzSGlnaGxpZ2h0ZWQgPSBpc0ZvcmVncm91bmRlZCB8fCBpc1NlbGVjdGVkXG4gIGNvbnN0IGlzTGVhZGVyRm9yZWdyb3VuZGVkID0gdmlld2luZ0FnZW50VGFza0lkID09PSB1bmRlZmluZWRcbiAgY29uc3QgaXNMZWFkZXJTZWxlY3RlZCA9IGlzSW5TZWxlY3Rpb25Nb2RlICYmIHNlbGVjdGVkSW5kZXggPT09IC0xXG4gIGNvbnN0IGlzTGVhZGVySGlnaGxpZ2h0ZWQgPSBpc0xlYWRlckZvcmVncm91bmRlZCB8fCBpc0xlYWRlclNlbGVjdGVkXG4gIGNvbnN0IGxlYWRlckNvbG9yOiBUZXh0UHJvcHNbJ2NvbG9yJ10gPSAnY3lhbl9GT1JfU1VCQUdFTlRTX09OTFknXG5cbiAgLy8gSXMgdGhlIFwiaGlkZVwiIHJvdyBzZWxlY3RlZD8gKGluZGV4ID09PSB0ZWFtbWF0ZUNvdW50IGluIHNlbGVjdGlvbiBtb2RlKVxuICBjb25zdCBpc0hpZGVTZWxlY3RlZCA9XG4gICAgaXNJblNlbGVjdGlvbk1vZGUgPT09IHRydWUgJiYgc2VsZWN0ZWRJbmRleCA9PT0gdGVhbW1hdGVUYXNrcy5sZW5ndGhcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpblRvcD17MX0+XG4gICAgICB7LyogTGVhZGVyIHJvdyAtIGFsd2F5cyB2aXNpYmxlLCB1c2VzIOKUjOKUgCB0byBlbmNsb3NlIHRoZSB0cmVlICovfVxuICAgICAge1xuICAgICAgICA8Qm94IHBhZGRpbmdMZWZ0PXszfT5cbiAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgY29sb3I9e2lzTGVhZGVyU2VsZWN0ZWQgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICBib2xkPXtpc0xlYWRlckhpZ2hsaWdodGVkfVxuICAgICAgICAgID5cbiAgICAgICAgICAgIHtpc0xlYWRlclNlbGVjdGVkID8gZmlndXJlcy5wb2ludGVyIDogJyAnfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj17IWlzTGVhZGVySGlnaGxpZ2h0ZWR9IGJvbGQ9e2lzTGVhZGVySGlnaGxpZ2h0ZWR9PlxuICAgICAgICAgICAge2lzTGVhZGVySGlnaGxpZ2h0ZWQgPyAn4pWS4pWQJyA6ICfilIzilIAnfXsnICd9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICBib2xkPXtpc0xlYWRlckhpZ2hsaWdodGVkfVxuICAgICAgICAgICAgY29sb3I9e2lzTGVhZGVyU2VsZWN0ZWQgPyAnc3VnZ2VzdGlvbicgOiBsZWFkZXJDb2xvcn1cbiAgICAgICAgICA+XG4gICAgICAgICAgICB0ZWFtLWxlYWRcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgey8qIFdoZW4gYmFja2dyb3VuZGVkIGFuZCBhY3RpdmU6IHNob3cgc3Bpbm5lciArIHZlcmIgKi99XG4gICAgICAgICAgeyFpc0xlYWRlckZvcmVncm91bmRlZCAmJiBsZWFkZXJWZXJiICYmIChcbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPjoge2xlYWRlclZlcmJ94oCmPC9UZXh0PlxuICAgICAgICAgICl9XG4gICAgICAgICAgey8qIFdoZW4gYmFja2dyb3VuZGVkIGFuZCBpZGxlOiBzaG93IGlkbGUgdGV4dCAqL31cbiAgICAgICAgICB7IWlzTGVhZGVyRm9yZWdyb3VuZGVkICYmICFsZWFkZXJWZXJiICYmIGxlYWRlcklkbGVUZXh0ICYmIChcbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPjoge2xlYWRlcklkbGVUZXh0fTwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICAgIHsvKiBTdGF0cyAodG9rZW5zKSAtIHNhbWUgZGltQ29sb3IgbG9naWMgYXMgdGVhbW1hdGVzICovfVxuICAgICAgICAgIHtsZWFkZXJUb2tlbkNvdW50ICE9PSB1bmRlZmluZWQgJiYgbGVhZGVyVG9rZW5Db3VudCA+IDAgJiYgKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I9eyFpc0xlYWRlckhpZ2hsaWdodGVkfT5cbiAgICAgICAgICAgICAgeycgJ31cbiAgICAgICAgICAgICAgwrcge2Zvcm1hdE51bWJlcihsZWFkZXJUb2tlbkNvdW50KX0gdG9rZW5zXG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgKX1cbiAgICAgICAgICB7LyogSGludHMgLSBzZWxlY3QgaGludCB3aGVuIGhpZ2hsaWdodGVkLCB2aWV3IGhpbnQgd2hlbiBzZWxlY3RlZCBidXQgbm90IGZvcmVncm91bmRlZCAqL31cbiAgICAgICAgICB7aXNMZWFkZXJIaWdobGlnaHRlZCAmJiAoXG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj4gwrcge1RFQU1NQVRFX1NFTEVDVF9ISU5UfTwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICAgIHtpc0xlYWRlclNlbGVjdGVkICYmICFpc0xlYWRlckZvcmVncm91bmRlZCAmJiAoXG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj4gwrcgZW50ZXIgdG8gdmlldzwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgIH1cbiAgICAgIHt0ZWFtbWF0ZVRhc2tzLm1hcCgodGVhbW1hdGUsIGluZGV4KSA9PiAoXG4gICAgICAgIDxUZWFtbWF0ZVNwaW5uZXJMaW5lXG4gICAgICAgICAga2V5PXt0ZWFtbWF0ZS5pZH1cbiAgICAgICAgICB0ZWFtbWF0ZT17dGVhbW1hdGV9XG4gICAgICAgICAgaXNMYXN0PXshaXNJblNlbGVjdGlvbk1vZGUgJiYgaW5kZXggPT09IHRlYW1tYXRlVGFza3MubGVuZ3RoIC0gMX1cbiAgICAgICAgICBpc1NlbGVjdGVkPXtpc0luU2VsZWN0aW9uTW9kZSAmJiBzZWxlY3RlZEluZGV4ID09PSBpbmRleH1cbiAgICAgICAgICBpc0ZvcmVncm91bmRlZD17dmlld2luZ0FnZW50VGFza0lkID09PSB0ZWFtbWF0ZS5pZH1cbiAgICAgICAgICBhbGxJZGxlPXthbGxJZGxlfVxuICAgICAgICAgIHNob3dQcmV2aWV3PXtzaG93VGVhbW1hdGVNZXNzYWdlUHJldmlld31cbiAgICAgICAgLz5cbiAgICAgICkpfVxuICAgICAgey8qIEhpZGUgcm93IC0gb25seSB2aXNpYmxlIGR1cmluZyBzZWxlY3Rpb24gbW9kZSAqL31cbiAgICAgIHtpc0luU2VsZWN0aW9uTW9kZSAmJiA8SGlkZVJvdyBpc1NlbGVjdGVkPXtpc0hpZGVTZWxlY3RlZH0gLz59XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuZnVuY3Rpb24gSGlkZVJvdyh7IGlzU2VsZWN0ZWQgfTogeyBpc1NlbGVjdGVkOiBib29sZWFuIH0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDxCb3ggcGFkZGluZ0xlZnQ9ezN9PlxuICAgICAgPFRleHQgY29sb3I9e2lzU2VsZWN0ZWQgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9IGJvbGQ9e2lzU2VsZWN0ZWR9PlxuICAgICAgICB7aXNTZWxlY3RlZCA/IGZpZ3VyZXMucG9pbnRlciA6ICcgJ31cbiAgICAgIDwvVGV4dD5cbiAgICAgIDxUZXh0IGRpbUNvbG9yPXshaXNTZWxlY3RlZH0gYm9sZD17aXNTZWxlY3RlZH0+XG4gICAgICAgIHtpc1NlbGVjdGVkID8gJ+KVmOKVkCcgOiAn4pSU4pSAJ317JyAnfVxuICAgICAgPC9UZXh0PlxuICAgICAgPFRleHQgZGltQ29sb3I9eyFpc1NlbGVjdGVkfSBib2xkPXtpc1NlbGVjdGVkfT5cbiAgICAgICAgaGlkZVxuICAgICAgPC9UZXh0PlxuICAgICAge2lzU2VsZWN0ZWQgJiYgPFRleHQgZGltQ29sb3I+IMK3IGVudGVyIHRvIGNvbGxhcHNlPC9UZXh0Pn1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxHQUFHLEVBQUVDLElBQUksRUFBRSxLQUFLQyxTQUFTLFFBQVEsY0FBYztBQUN4RCxTQUFTQyxXQUFXLFFBQVEseUJBQXlCO0FBQ3JELFNBQVNDLHlCQUF5QixRQUFRLDREQUE0RDtBQUN0RyxTQUFTQyxZQUFZLFFBQVEsdUJBQXVCO0FBQ3BELFNBQVNDLG1CQUFtQixRQUFRLDBCQUEwQjtBQUM5RCxTQUFTQyxvQkFBb0IsUUFBUSx5QkFBeUI7QUFFOUQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLGFBQWEsQ0FBQyxFQUFFLE1BQU07RUFDdEJDLGlCQUFpQixDQUFDLEVBQUUsT0FBTztFQUMzQkMsT0FBTyxDQUFDLEVBQUUsT0FBTztFQUNqQjtFQUNBQyxVQUFVLENBQUMsRUFBRSxNQUFNO0VBQ25CO0VBQ0FDLGdCQUFnQixDQUFDLEVBQUUsTUFBTTtFQUN6QjtFQUNBQyxjQUFjLENBQUMsRUFBRSxNQUFNO0FBQ3pCLENBQUM7QUFFRCxPQUFPLFNBQUFDLG9CQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQTZCO0lBQUFULGFBQUE7SUFBQUMsaUJBQUE7SUFBQUMsT0FBQTtJQUFBQyxVQUFBO0lBQUFDLGdCQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFPNUI7RUFDTixNQUFBRyxLQUFBLEdBQWNoQixXQUFXLENBQUNpQixLQUFZLENBQUM7RUFDdkMsTUFBQUMsa0JBQUEsR0FBMkJsQixXQUFXLENBQUNtQixNQUF5QixDQUFDO0VBQ2pFLE1BQUFDLDBCQUFBLEdBQW1DcEIsV0FBVyxDQUM1Q3FCLE1BQ0YsQ0FBQztFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBQyxjQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBZCxDQUFBLFFBQUFOLE9BQUEsSUFBQU0sQ0FBQSxRQUFBUCxpQkFBQSxJQUFBTyxDQUFBLFFBQUFILGNBQUEsSUFBQUcsQ0FBQSxRQUFBSixnQkFBQSxJQUFBSSxDQUFBLFFBQUFMLFVBQUEsSUFBQUssQ0FBQSxRQUFBUixhQUFBLElBQUFRLENBQUEsUUFBQU0sMEJBQUEsSUFBQU4sQ0FBQSxRQUFBRSxLQUFBLElBQUFGLENBQUEsUUFBQUksa0JBQUE7SUFNUVUsRUFBQSxHQUFBQyxNQUFJLENBQUFDLEdBQUEsQ0FBSiw2QkFBRyxDQUFDO0lBQUFDLEdBQUE7TUFKYixNQUFBQyxhQUFBLEdBQXNCL0IseUJBQXlCLENBQUNlLEtBQUssQ0FBQztNQUd0RCxJQUFJZ0IsYUFBYSxDQUFBQyxNQUFPLEtBQUssQ0FBQztRQUNyQkwsRUFBQSxPQUFJO1FBQUosTUFBQUcsR0FBQTtNQUFJO01BS2IsTUFBQUcsb0JBQUEsR0FBNkJoQixrQkFBa0IsS0FBS2lCLFNBQVM7TUFDN0QsTUFBQUMsZ0JBQUEsR0FBeUI3QixpQkFBeUMsSUFBcEJELGFBQWEsS0FBSyxFQUFFO01BQ2xFLE1BQUErQixtQkFBQSxHQUE0Qkgsb0JBQXdDLElBQXhDRSxnQkFBd0M7TUFJcEViLGNBQUEsR0FDRWhCLGlCQUFpQixLQUFLLElBQThDLElBQXRDRCxhQUFhLEtBQUswQixhQUFhLENBQUFDLE1BQU87TUFHbkVYLEVBQUEsR0FBQXpCLEdBQUc7TUFBZTJCLEVBQUEsV0FBUTtNQUFZQyxFQUFBLElBQUM7TUFLekIsTUFBQWEsRUFBQSxHQUFBRixnQkFBZ0IsR0FBaEIsWUFBMkMsR0FBM0NELFNBQTJDO01BR2pELE1BQUFJLEVBQUEsR0FBQUgsZ0JBQWdCLEdBQUd6QyxPQUFPLENBQUE2QyxPQUFjLEdBQXhDLEdBQXdDO01BQUEsSUFBQUMsRUFBQTtNQUFBLElBQUEzQixDQUFBLFNBQUF1QixtQkFBQSxJQUFBdkIsQ0FBQSxTQUFBd0IsRUFBQSxJQUFBeEIsQ0FBQSxTQUFBeUIsRUFBQTtRQUozQ0UsRUFBQSxJQUFDLElBQUksQ0FDSSxLQUEyQyxDQUEzQyxDQUFBSCxFQUEwQyxDQUFDLENBQzVDRCxJQUFtQixDQUFuQkEsb0JBQWtCLENBQUMsQ0FFeEIsQ0FBQUUsRUFBdUMsQ0FDMUMsRUFMQyxJQUFJLENBS0U7UUFBQXpCLENBQUEsT0FBQXVCLG1CQUFBO1FBQUF2QixDQUFBLE9BQUF3QixFQUFBO1FBQUF4QixDQUFBLE9BQUF5QixFQUFBO1FBQUF6QixDQUFBLE9BQUEyQixFQUFBO01BQUE7UUFBQUEsRUFBQSxHQUFBM0IsQ0FBQTtNQUFBO01BQ1MsTUFBQTRCLEVBQUEsSUFBQ0wsbUJBQW1CO01BQ2pDLE1BQUFNLEdBQUEsR0FBQU4sbUJBQW1CLEdBQW5CLGNBQWlDLEdBQWpDLGNBQWlDO01BQUEsSUFBQU8sR0FBQTtNQUFBLElBQUE5QixDQUFBLFNBQUF1QixtQkFBQSxJQUFBdkIsQ0FBQSxTQUFBNkIsR0FBQSxJQUFBN0IsQ0FBQSxTQUFBNEIsRUFBQTtRQURwQ0UsR0FBQSxJQUFDLElBQUksQ0FBVyxRQUFvQixDQUFwQixDQUFBRixFQUFtQixDQUFDLENBQVFMLElBQW1CLENBQW5CQSxvQkFBa0IsQ0FBQyxDQUM1RCxDQUFBTSxHQUFnQyxDQUFHLElBQUUsQ0FDeEMsRUFGQyxJQUFJLENBRUU7UUFBQTdCLENBQUEsT0FBQXVCLG1CQUFBO1FBQUF2QixDQUFBLE9BQUE2QixHQUFBO1FBQUE3QixDQUFBLE9BQUE0QixFQUFBO1FBQUE1QixDQUFBLE9BQUE4QixHQUFBO01BQUE7UUFBQUEsR0FBQSxHQUFBOUIsQ0FBQTtNQUFBO01BR0UsTUFBQStCLEdBQUEsR0FBQVQsZ0JBQWdCLEdBQWhCLFlBQTZDLEdBQTdDLHlCQUE2QztNQUFBLElBQUFVLEdBQUE7TUFBQSxJQUFBaEMsQ0FBQSxTQUFBdUIsbUJBQUEsSUFBQXZCLENBQUEsU0FBQStCLEdBQUE7UUFGdERDLEdBQUEsSUFBQyxJQUFJLENBQ0dULElBQW1CLENBQW5CQSxvQkFBa0IsQ0FBQyxDQUNsQixLQUE2QyxDQUE3QyxDQUFBUSxHQUE0QyxDQUFDLENBQ3JELFNBRUQsRUFMQyxJQUFJLENBS0U7UUFBQS9CLENBQUEsT0FBQXVCLG1CQUFBO1FBQUF2QixDQUFBLE9BQUErQixHQUFBO1FBQUEvQixDQUFBLE9BQUFnQyxHQUFBO01BQUE7UUFBQUEsR0FBQSxHQUFBaEMsQ0FBQTtNQUFBO01BQUEsSUFBQWlDLEdBQUE7TUFBQSxJQUFBakMsQ0FBQSxTQUFBb0Isb0JBQUEsSUFBQXBCLENBQUEsU0FBQUwsVUFBQTtRQUVOc0MsR0FBQSxJQUFDYixvQkFBa0MsSUFBbkN6QixVQUVBLElBREMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLEVBQUdBLFdBQVMsQ0FBRSxDQUFDLEVBQTdCLElBQUksQ0FDTjtRQUFBSyxDQUFBLE9BQUFvQixvQkFBQTtRQUFBcEIsQ0FBQSxPQUFBTCxVQUFBO1FBQUFLLENBQUEsT0FBQWlDLEdBQUE7TUFBQTtRQUFBQSxHQUFBLEdBQUFqQyxDQUFBO01BQUE7TUFBQSxJQUFBa0MsR0FBQTtNQUFBLElBQUFsQyxDQUFBLFNBQUFvQixvQkFBQSxJQUFBcEIsQ0FBQSxTQUFBSCxjQUFBLElBQUFHLENBQUEsU0FBQUwsVUFBQTtRQUVBdUMsR0FBQSxJQUFDZCxvQkFBbUMsSUFBcEMsQ0FBMEJ6QixVQUE0QixJQUF0REUsY0FFQSxJQURDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxFQUFHQSxlQUFhLENBQUUsRUFBaEMsSUFBSSxDQUNOO1FBQUFHLENBQUEsT0FBQW9CLG9CQUFBO1FBQUFwQixDQUFBLE9BQUFILGNBQUE7UUFBQUcsQ0FBQSxPQUFBTCxVQUFBO1FBQUFLLENBQUEsT0FBQWtDLEdBQUE7TUFBQTtRQUFBQSxHQUFBLEdBQUFsQyxDQUFBO01BQUE7TUFBQSxJQUFBbUMsR0FBQTtNQUFBLElBQUFuQyxDQUFBLFNBQUF1QixtQkFBQSxJQUFBdkIsQ0FBQSxTQUFBSixnQkFBQTtRQUVBdUMsR0FBQSxHQUFBdkMsZ0JBQWdCLEtBQUt5QixTQUFpQyxJQUFwQnpCLGdCQUFnQixHQUFHLENBS3JELElBSkMsQ0FBQyxJQUFJLENBQVcsUUFBb0IsQ0FBcEIsRUFBQzJCLG1CQUFrQixDQUFDLENBQ2pDLElBQUUsQ0FBRSxFQUNGLENBQUFuQyxZQUFZLENBQUNRLGdCQUFnQixFQUFFLE9BQ3BDLEVBSEMsSUFBSSxDQUlOO1FBQUFJLENBQUEsT0FBQXVCLG1CQUFBO1FBQUF2QixDQUFBLE9BQUFKLGdCQUFBO1FBQUFJLENBQUEsT0FBQW1DLEdBQUE7TUFBQTtRQUFBQSxHQUFBLEdBQUFuQyxDQUFBO01BQUE7TUFBQSxJQUFBb0MsR0FBQTtNQUFBLElBQUFwQyxDQUFBLFNBQUF1QixtQkFBQTtRQUVBYSxHQUFBLEdBQUFiLG1CQUVBLElBREMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLEdBQUlqQyxxQkFBbUIsQ0FBRSxFQUF2QyxJQUFJLENBQ047UUFBQVUsQ0FBQSxPQUFBdUIsbUJBQUE7UUFBQXZCLENBQUEsT0FBQW9DLEdBQUE7TUFBQTtRQUFBQSxHQUFBLEdBQUFwQyxDQUFBO01BQUE7TUFBQSxJQUFBcUMsR0FBQTtNQUFBLElBQUFyQyxDQUFBLFNBQUFvQixvQkFBQSxJQUFBcEIsQ0FBQSxTQUFBc0IsZ0JBQUE7UUFDQWUsR0FBQSxHQUFBZixnQkFBeUMsSUFBekMsQ0FBcUJGLG9CQUVyQixJQURDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxnQkFBZ0IsRUFBOUIsSUFBSSxDQUNOO1FBQUFwQixDQUFBLE9BQUFvQixvQkFBQTtRQUFBcEIsQ0FBQSxPQUFBc0IsZ0JBQUE7UUFBQXRCLENBQUEsT0FBQXFDLEdBQUE7TUFBQTtRQUFBQSxHQUFBLEdBQUFyQyxDQUFBO01BQUE7TUFBQSxJQUFBQSxDQUFBLFNBQUE4QixHQUFBLElBQUE5QixDQUFBLFNBQUFnQyxHQUFBLElBQUFoQyxDQUFBLFNBQUFpQyxHQUFBLElBQUFqQyxDQUFBLFNBQUFrQyxHQUFBLElBQUFsQyxDQUFBLFNBQUFtQyxHQUFBLElBQUFuQyxDQUFBLFNBQUFvQyxHQUFBLElBQUFwQyxDQUFBLFNBQUFxQyxHQUFBLElBQUFyQyxDQUFBLFNBQUEyQixFQUFBO1FBckNIZixFQUFBLElBQUMsR0FBRyxDQUFjLFdBQUMsQ0FBRCxHQUFDLENBQ2pCLENBQUFlLEVBS00sQ0FDTixDQUFBRyxHQUVNLENBQ04sQ0FBQUUsR0FLTSxDQUVMLENBQUFDLEdBRUQsQ0FFQyxDQUFBQyxHQUVELENBRUMsQ0FBQUMsR0FLRCxDQUVDLENBQUFDLEdBRUQsQ0FDQyxDQUFBQyxHQUVELENBQ0YsRUF0Q0MsR0FBRyxDQXNDRTtRQUFBckMsQ0FBQSxPQUFBOEIsR0FBQTtRQUFBOUIsQ0FBQSxPQUFBZ0MsR0FBQTtRQUFBaEMsQ0FBQSxPQUFBaUMsR0FBQTtRQUFBakMsQ0FBQSxPQUFBa0MsR0FBQTtRQUFBbEMsQ0FBQSxPQUFBbUMsR0FBQTtRQUFBbkMsQ0FBQSxPQUFBb0MsR0FBQTtRQUFBcEMsQ0FBQSxPQUFBcUMsR0FBQTtRQUFBckMsQ0FBQSxPQUFBMkIsRUFBQTtRQUFBM0IsQ0FBQSxPQUFBWSxFQUFBO01BQUE7UUFBQUEsRUFBQSxHQUFBWixDQUFBO01BQUE7TUFFUGEsRUFBQSxHQUFBSyxhQUFhLENBQUFvQixHQUFJLENBQUMsQ0FBQUMsUUFBQSxFQUFBQyxLQUFBLEtBQ2pCLENBQUMsbUJBQW1CLENBQ2IsR0FBVyxDQUFYLENBQUFELFFBQVEsQ0FBQUUsRUFBRSxDQUFDLENBQ05GLFFBQVEsQ0FBUkEsU0FBTyxDQUFDLENBQ1YsTUFBd0QsQ0FBeEQsRUFBQzlDLGlCQUF1RCxJQUFsQytDLEtBQUssS0FBS3RCLGFBQWEsQ0FBQUMsTUFBTyxHQUFHLEVBQUMsQ0FDcEQsVUFBNEMsQ0FBNUMsQ0FBQTFCLGlCQUE0QyxJQUF2QkQsYUFBYSxLQUFLZ0QsS0FBSSxDQUFDLENBQ3hDLGNBQWtDLENBQWxDLENBQUFwQyxrQkFBa0IsS0FBS21DLFFBQVEsQ0FBQUUsRUFBRSxDQUFDLENBQ3pDL0MsT0FBTyxDQUFQQSxRQUFNLENBQUMsQ0FDSFksV0FBMEIsQ0FBMUJBLDJCQUF5QixDQUFDLEdBRTFDLENBQUM7SUFBQTtJQUFBTixDQUFBLE1BQUFOLE9BQUE7SUFBQU0sQ0FBQSxNQUFBUCxpQkFBQTtJQUFBTyxDQUFBLE1BQUFILGNBQUE7SUFBQUcsQ0FBQSxNQUFBSixnQkFBQTtJQUFBSSxDQUFBLE1BQUFMLFVBQUE7SUFBQUssQ0FBQSxNQUFBUixhQUFBO0lBQUFRLENBQUEsTUFBQU0sMEJBQUE7SUFBQU4sQ0FBQSxNQUFBRSxLQUFBO0lBQUFGLENBQUEsTUFBQUksa0JBQUE7SUFBQUosQ0FBQSxNQUFBUSxFQUFBO0lBQUFSLENBQUEsT0FBQVMsY0FBQTtJQUFBVCxDQUFBLE9BQUFVLEVBQUE7SUFBQVYsQ0FBQSxPQUFBVyxFQUFBO0lBQUFYLENBQUEsT0FBQVksRUFBQTtJQUFBWixDQUFBLE9BQUFhLEVBQUE7SUFBQWIsQ0FBQSxPQUFBYyxFQUFBO0VBQUE7SUFBQU4sRUFBQSxHQUFBUixDQUFBO0lBQUFTLGNBQUEsR0FBQVQsQ0FBQTtJQUFBVSxFQUFBLEdBQUFWLENBQUE7SUFBQVcsRUFBQSxHQUFBWCxDQUFBO0lBQUFZLEVBQUEsR0FBQVosQ0FBQTtJQUFBYSxFQUFBLEdBQUFiLENBQUE7SUFBQWMsRUFBQSxHQUFBZCxDQUFBO0VBQUE7RUFBQSxJQUFBYyxFQUFBLEtBQUFDLE1BQUEsQ0FBQUMsR0FBQTtJQUFBLE9BQUFGLEVBQUE7RUFBQTtFQUFBLElBQUFVLEVBQUE7RUFBQSxJQUFBeEIsQ0FBQSxTQUFBUyxjQUFBLElBQUFULENBQUEsU0FBQVAsaUJBQUE7SUFFRCtCLEVBQUEsR0FBQS9CLGlCQUE0RCxJQUF2QyxDQUFDLE9BQU8sQ0FBYWdCLFVBQWMsQ0FBZEEsZUFBYSxDQUFDLEdBQUk7SUFBQVQsQ0FBQSxPQUFBUyxjQUFBO0lBQUFULENBQUEsT0FBQVAsaUJBQUE7SUFBQU8sQ0FBQSxPQUFBd0IsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQXhCLENBQUE7RUFBQTtFQUFBLElBQUF5QixFQUFBO0VBQUEsSUFBQXpCLENBQUEsU0FBQVEsRUFBQSxJQUFBUixDQUFBLFNBQUFVLEVBQUEsSUFBQVYsQ0FBQSxTQUFBVyxFQUFBLElBQUFYLENBQUEsU0FBQVksRUFBQSxJQUFBWixDQUFBLFNBQUFhLEVBQUEsSUFBQWIsQ0FBQSxTQUFBd0IsRUFBQTtJQXZEL0RDLEVBQUEsSUFBQyxFQUFHLENBQWUsYUFBUSxDQUFSLENBQUFmLEVBQU8sQ0FBQyxDQUFZLFNBQUMsQ0FBRCxDQUFBQyxFQUFBLENBQUMsQ0FHcEMsQ0FBQUMsRUFzQ0ssQ0FFTixDQUFBQyxFQVVBLENBRUEsQ0FBQVcsRUFBMkQsQ0FDOUQsRUF4REMsRUFBRyxDQXdERTtJQUFBeEIsQ0FBQSxPQUFBUSxFQUFBO0lBQUFSLENBQUEsT0FBQVUsRUFBQTtJQUFBVixDQUFBLE9BQUFXLEVBQUE7SUFBQVgsQ0FBQSxPQUFBWSxFQUFBO0lBQUFaLENBQUEsT0FBQWEsRUFBQTtJQUFBYixDQUFBLE9BQUF3QixFQUFBO0lBQUF4QixDQUFBLE9BQUF5QixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBekIsQ0FBQTtFQUFBO0VBQUEsT0F4RE55QixFQXdETTtBQUFBO0FBekZILFNBQUFsQixPQUFBbUMsR0FBQTtFQUFBLE9BV0VDLEdBQUMsQ0FBQXJDLDBCQUEyQjtBQUFBO0FBWDlCLFNBQUFELE9BQUF1QyxHQUFBO0VBQUEsT0FTdUNELEdBQUMsQ0FBQXZDLGtCQUFtQjtBQUFBO0FBVDNELFNBQUFELE1BQUF3QyxDQUFBO0VBQUEsT0FRMEJBLENBQUMsQ0FBQXpDLEtBQU07QUFBQTtBQXFGeEMsU0FBQTJDLFFBQUE5QyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWlCO0lBQUE2QztFQUFBLElBQUEvQyxFQUF1QztFQUdyQyxNQUFBVyxFQUFBLEdBQUFvQyxVQUFVLEdBQVYsWUFBcUMsR0FBckN6QixTQUFxQztFQUMvQyxNQUFBVixFQUFBLEdBQUFtQyxVQUFVLEdBQUdqRSxPQUFPLENBQUE2QyxPQUFjLEdBQWxDLEdBQWtDO0VBQUEsSUFBQWQsRUFBQTtFQUFBLElBQUFaLENBQUEsUUFBQThDLFVBQUEsSUFBQTlDLENBQUEsUUFBQVUsRUFBQSxJQUFBVixDQUFBLFFBQUFXLEVBQUE7SUFEckNDLEVBQUEsSUFBQyxJQUFJLENBQVEsS0FBcUMsQ0FBckMsQ0FBQUYsRUFBb0MsQ0FBQyxDQUFRb0MsSUFBVSxDQUFWQSxXQUFTLENBQUMsQ0FDakUsQ0FBQW5DLEVBQWlDLENBQ3BDLEVBRkMsSUFBSSxDQUVFO0lBQUFYLENBQUEsTUFBQThDLFVBQUE7SUFBQTlDLENBQUEsTUFBQVUsRUFBQTtJQUFBVixDQUFBLE1BQUFXLEVBQUE7SUFBQVgsQ0FBQSxNQUFBWSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBWixDQUFBO0VBQUE7RUFDUyxNQUFBYSxFQUFBLElBQUNpQyxVQUFVO0VBQ3hCLE1BQUFoQyxFQUFBLEdBQUFnQyxVQUFVLEdBQVYsY0FBd0IsR0FBeEIsY0FBd0I7RUFBQSxJQUFBdEIsRUFBQTtFQUFBLElBQUF4QixDQUFBLFFBQUE4QyxVQUFBLElBQUE5QyxDQUFBLFFBQUFhLEVBQUEsSUFBQWIsQ0FBQSxRQUFBYyxFQUFBO0lBRDNCVSxFQUFBLElBQUMsSUFBSSxDQUFXLFFBQVcsQ0FBWCxDQUFBWCxFQUFVLENBQUMsQ0FBUWlDLElBQVUsQ0FBVkEsV0FBUyxDQUFDLENBQzFDLENBQUFoQyxFQUF1QixDQUFHLElBQUUsQ0FDL0IsRUFGQyxJQUFJLENBRUU7SUFBQWQsQ0FBQSxNQUFBOEMsVUFBQTtJQUFBOUMsQ0FBQSxNQUFBYSxFQUFBO0lBQUFiLENBQUEsTUFBQWMsRUFBQTtJQUFBZCxDQUFBLE1BQUF3QixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBeEIsQ0FBQTtFQUFBO0VBQ1MsTUFBQXlCLEVBQUEsSUFBQ3FCLFVBQVU7RUFBQSxJQUFBbkIsRUFBQTtFQUFBLElBQUEzQixDQUFBLFFBQUE4QyxVQUFBLElBQUE5QyxDQUFBLFFBQUF5QixFQUFBO0lBQTNCRSxFQUFBLElBQUMsSUFBSSxDQUFXLFFBQVcsQ0FBWCxDQUFBRixFQUFVLENBQUMsQ0FBUXFCLElBQVUsQ0FBVkEsV0FBUyxDQUFDLENBQUUsSUFFL0MsRUFGQyxJQUFJLENBRUU7SUFBQTlDLENBQUEsTUFBQThDLFVBQUE7SUFBQTlDLENBQUEsTUFBQXlCLEVBQUE7SUFBQXpCLENBQUEsT0FBQTJCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUEzQixDQUFBO0VBQUE7RUFBQSxJQUFBNEIsRUFBQTtFQUFBLElBQUE1QixDQUFBLFNBQUE4QyxVQUFBO0lBQ05sQixFQUFBLEdBQUFrQixVQUF3RCxJQUExQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsb0JBQW9CLEVBQWxDLElBQUksQ0FBcUM7SUFBQTlDLENBQUEsT0FBQThDLFVBQUE7SUFBQTlDLENBQUEsT0FBQTRCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUE1QixDQUFBO0VBQUE7RUFBQSxJQUFBNkIsR0FBQTtFQUFBLElBQUE3QixDQUFBLFNBQUFZLEVBQUEsSUFBQVosQ0FBQSxTQUFBd0IsRUFBQSxJQUFBeEIsQ0FBQSxTQUFBMkIsRUFBQSxJQUFBM0IsQ0FBQSxTQUFBNEIsRUFBQTtJQVYzREMsR0FBQSxJQUFDLEdBQUcsQ0FBYyxXQUFDLENBQUQsR0FBQyxDQUNqQixDQUFBakIsRUFFTSxDQUNOLENBQUFZLEVBRU0sQ0FDTixDQUFBRyxFQUVNLENBQ0wsQ0FBQUMsRUFBdUQsQ0FDMUQsRUFYQyxHQUFHLENBV0U7SUFBQTVCLENBQUEsT0FBQVksRUFBQTtJQUFBWixDQUFBLE9BQUF3QixFQUFBO0lBQUF4QixDQUFBLE9BQUEyQixFQUFBO0lBQUEzQixDQUFBLE9BQUE0QixFQUFBO0lBQUE1QixDQUFBLE9BQUE2QixHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBN0IsQ0FBQTtFQUFBO0VBQUEsT0FYTjZCLEdBV007QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/Spinner/index.ts b/components/Spinner/index.ts new file mode 100644 index 0000000..25d9fe0 --- /dev/null +++ b/components/Spinner/index.ts @@ -0,0 +1,10 @@ +export { FlashingChar } from './FlashingChar.js' +export { GlimmerMessage } from './GlimmerMessage.js' +export { ShimmerChar } from './ShimmerChar.js' +export { SpinnerGlyph } from './SpinnerGlyph.js' +export type { SpinnerMode } from './types.js' +export { useShimmerAnimation } from './useShimmerAnimation.js' +export { useStalledAnimation } from './useStalledAnimation.js' +export { getDefaultCharacters, interpolateColor } from './utils.js' +// Teammate components are NOT exported here - use dynamic require() to enable dead code elimination +// See REPL.tsx and Spinner.tsx for the correct import pattern diff --git a/components/Spinner/teammateSelectHint.ts b/components/Spinner/teammateSelectHint.ts new file mode 100644 index 0000000..420f949 --- /dev/null +++ b/components/Spinner/teammateSelectHint.ts @@ -0,0 +1 @@ +export const TEAMMATE_SELECT_HINT = 'shift + ↑/↓ to select' diff --git a/components/Spinner/useShimmerAnimation.ts b/components/Spinner/useShimmerAnimation.ts new file mode 100644 index 0000000..d1d4ea9 --- /dev/null +++ b/components/Spinner/useShimmerAnimation.ts @@ -0,0 +1,31 @@ +import { useMemo } from 'react' +import { stringWidth } from '../../ink/stringWidth.js' +import { type DOMElement, useAnimationFrame } from '../../ink.js' +import type { SpinnerMode } from './types.js' + +export function useShimmerAnimation( + mode: SpinnerMode, + message: string, + isStalled: boolean, +): [ref: (element: DOMElement | null) => void, glimmerIndex: number] { + const glimmerSpeed = mode === 'requesting' ? 50 : 200 + // Pass null when stalled to unsubscribe from the clock — otherwise the + // setInterval keeps firing at 20fps even when the shimmer isn't visible. + // Notably, if the caller never attaches `ref` (e.g. conditional JSX), + // useTerminalViewport stays at its initial isVisible:true and the + // viewport-pause never kicks in, so this is the only stop mechanism. + const [ref, time] = useAnimationFrame(isStalled ? null : glimmerSpeed) + const messageWidth = useMemo(() => stringWidth(message), [message]) + + if (isStalled) { + return [ref, -100] + } + + const cyclePosition = Math.floor(time / glimmerSpeed) + const cycleLength = messageWidth + 20 + + if (mode === 'requesting') { + return [ref, (cyclePosition % cycleLength) - 10] + } + return [ref, messageWidth + 10 - (cyclePosition % cycleLength)] +} diff --git a/components/Spinner/useStalledAnimation.ts b/components/Spinner/useStalledAnimation.ts new file mode 100644 index 0000000..a3af4fa --- /dev/null +++ b/components/Spinner/useStalledAnimation.ts @@ -0,0 +1,75 @@ +import { useRef } from 'react' + +// Hook to handle the transition to red when tokens stop flowing. +// Driven by the parent's animation clock time instead of independent intervals, +// so it slows down when the terminal is blurred. +export function useStalledAnimation( + time: number, + currentResponseLength: number, + hasActiveTools = false, + reducedMotion = false, +): { + isStalled: boolean + stalledIntensity: number +} { + const lastTokenTime = useRef(time) + const lastResponseLength = useRef(currentResponseLength) + const mountTime = useRef(time) + const stalledIntensityRef = useRef(0) + const lastSmoothTime = useRef(time) + + // Reset timer when new tokens arrive (check actual length change) + if (currentResponseLength > lastResponseLength.current) { + lastTokenTime.current = time + lastResponseLength.current = currentResponseLength + stalledIntensityRef.current = 0 + lastSmoothTime.current = time + } + + // Derive time since last token from animation clock + let timeSinceLastToken: number + if (hasActiveTools) { + timeSinceLastToken = 0 + lastTokenTime.current = time + } else if (currentResponseLength > 0) { + timeSinceLastToken = time - lastTokenTime.current + } else { + timeSinceLastToken = time - mountTime.current + } + + // Calculate stalled intensity based on time since last token + // Start showing red after 3 seconds of no new tokens (only when no tools are active) + const isStalled = timeSinceLastToken > 3000 && !hasActiveTools + const intensity = isStalled + ? Math.min((timeSinceLastToken - 3000) / 2000, 1) // Fade over 2 seconds + : 0 + + // Smooth intensity transition driven by animation frame ticks + if (!reducedMotion && (intensity > 0 || stalledIntensityRef.current > 0)) { + const dt = time - lastSmoothTime.current + if (dt >= 50) { + const steps = Math.floor(dt / 50) + let current = stalledIntensityRef.current + for (let i = 0; i < steps; i++) { + const diff = intensity - current + if (Math.abs(diff) < 0.01) { + current = intensity + break + } + current += diff * 0.1 + } + stalledIntensityRef.current = current + lastSmoothTime.current = time + } + } else { + stalledIntensityRef.current = intensity + lastSmoothTime.current = time + } + + // When reducedMotion is enabled, use instant intensity change + const effectiveIntensity = reducedMotion + ? intensity + : stalledIntensityRef.current + + return { isStalled, stalledIntensity: effectiveIntensity } +} diff --git a/components/Spinner/utils.ts b/components/Spinner/utils.ts new file mode 100644 index 0000000..7c0c54d --- /dev/null +++ b/components/Spinner/utils.ts @@ -0,0 +1,84 @@ +import type { RGBColor as RGBColorString } from '../../ink/styles.js' +import type { RGBColor as RGBColorType } from './types.js' + +export function getDefaultCharacters(): string[] { + if (process.env.TERM === 'xterm-ghostty') { + return ['·', '✢', '✳', '✶', '✻', '*'] // Use * instead of ✽ for Ghostty because the latter renders in a way that's slightly offset + } + return process.platform === 'darwin' + ? ['·', '✢', '✳', '✶', '✻', '✽'] + : ['·', '✢', '*', '✶', '✻', '✽'] +} + +// Interpolate between two RGB colors +export function interpolateColor( + color1: RGBColorType, + color2: RGBColorType, + t: number, // 0 to 1 +): RGBColorType { + return { + r: Math.round(color1.r + (color2.r - color1.r) * t), + g: Math.round(color1.g + (color2.g - color1.g) * t), + b: Math.round(color1.b + (color2.b - color1.b) * t), + } +} + +// Convert RGB object to rgb() color string for Text component +export function toRGBColor(color: RGBColorType): RGBColorString { + return `rgb(${color.r},${color.g},${color.b})` +} + +// HSL hue (0-360) to RGB, using voice-mode waveform parameters (s=0.7, l=0.6). +export function hueToRgb(hue: number): RGBColorType { + const h = ((hue % 360) + 360) % 360 + const s = 0.7 + const l = 0.6 + const c = (1 - Math.abs(2 * l - 1)) * s + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)) + const m = l - c / 2 + let r = 0 + let g = 0 + let b = 0 + if (h < 60) { + r = c + g = x + } else if (h < 120) { + r = x + g = c + } else if (h < 180) { + g = c + b = x + } else if (h < 240) { + g = x + b = c + } else if (h < 300) { + r = x + b = c + } else { + r = c + b = x + } + return { + r: Math.round((r + m) * 255), + g: Math.round((g + m) * 255), + b: Math.round((b + m) * 255), + } +} + +const RGB_CACHE = new Map() + +export function parseRGB(colorStr: string): RGBColorType | null { + const cached = RGB_CACHE.get(colorStr) + if (cached !== undefined) return cached + + const match = colorStr.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/) + const result = match + ? { + r: parseInt(match[1]!, 10), + g: parseInt(match[2]!, 10), + b: parseInt(match[3]!, 10), + } + : null + RGB_CACHE.set(colorStr, result) + return result +} diff --git a/components/Stats.tsx b/components/Stats.tsx new file mode 100644 index 0000000..e229891 --- /dev/null +++ b/components/Stats.tsx @@ -0,0 +1,1228 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import { plot as asciichart } from 'asciichart'; +import chalk from 'chalk'; +import figures from 'figures'; +import React, { Suspense, use, useCallback, useEffect, useMemo, useState } from 'react'; +import stripAnsi from 'strip-ansi'; +import type { CommandResultDisplay } from '../commands.js'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { applyColor } from '../ink/colorize.js'; +import { stringWidth as getStringWidth } from '../ink/stringWidth.js'; +import type { Color } from '../ink/styles.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw j/k/arrow stats navigation +import { Ansi, Box, Text, useInput } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { getGlobalConfig } from '../utils/config.js'; +import { formatDuration, formatNumber } from '../utils/format.js'; +import { generateHeatmap } from '../utils/heatmap.js'; +import { renderModelName } from '../utils/model/model.js'; +import { copyAnsiToClipboard } from '../utils/screenshotClipboard.js'; +import { aggregateClaudeCodeStatsForRange, type ClaudeCodeStats, type DailyModelTokens, type StatsDateRange } from '../utils/stats.js'; +import { resolveThemeSetting } from '../utils/systemTheme.js'; +import { getTheme, themeColorToAnsi } from '../utils/theme.js'; +import { Pane } from './design-system/Pane.js'; +import { Tab, Tabs, useTabHeaderFocus } from './design-system/Tabs.js'; +import { Spinner } from './Spinner.js'; +function formatPeakDay(dateStr: string): string { + const date = new Date(dateStr); + return date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric' + }); +} +type Props = { + onClose: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; +}; +type StatsResult = { + type: 'success'; + data: ClaudeCodeStats; +} | { + type: 'error'; + message: string; +} | { + type: 'empty'; +}; +const DATE_RANGE_LABELS: Record = { + '7d': 'Last 7 days', + '30d': 'Last 30 days', + all: 'All time' +}; +const DATE_RANGE_ORDER: StatsDateRange[] = ['all', '7d', '30d']; +function getNextDateRange(current: StatsDateRange): StatsDateRange { + const currentIndex = DATE_RANGE_ORDER.indexOf(current); + return DATE_RANGE_ORDER[(currentIndex + 1) % DATE_RANGE_ORDER.length]!; +} + +/** + * Creates a stats loading promise that never rejects. + * Always loads all-time stats for the heatmap. + */ +function createAllTimeStatsPromise(): Promise { + return aggregateClaudeCodeStatsForRange('all').then((data): StatsResult => { + if (!data || data.totalSessions === 0) { + return { + type: 'empty' + }; + } + return { + type: 'success', + data + }; + }).catch((err): StatsResult => { + const message = err instanceof Error ? err.message : 'Failed to load stats'; + return { + type: 'error', + message + }; + }); +} +export function Stats(t0) { + const $ = _c(4); + const { + onClose + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = createAllTimeStatsPromise(); + $[0] = t1; + } else { + t1 = $[0]; + } + const allTimePromise = t1; + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = Loading your Claude Code stats…; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== onClose) { + t3 = ; + $[2] = onClose; + $[3] = t3; + } else { + t3 = $[3]; + } + return t3; +} +type StatsContentProps = { + allTimePromise: Promise; + onClose: Props['onClose']; +}; + +/** + * Inner component that uses React 19's use() to read the stats promise. + * Suspends while loading all-time stats, then handles date range changes without suspending. + */ +function StatsContent(t0) { + const $ = _c(34); + const { + allTimePromise, + onClose + } = t0; + const allTimeResult = use(allTimePromise); + const [dateRange, setDateRange] = useState("all"); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {}; + $[0] = t1; + } else { + t1 = $[0]; + } + const [statsCache, setStatsCache] = useState(t1); + const [isLoadingFiltered, setIsLoadingFiltered] = useState(false); + const [activeTab, setActiveTab] = useState("Overview"); + const [copyStatus, setCopyStatus] = useState(null); + let t2; + let t3; + if ($[1] !== dateRange || $[2] !== statsCache) { + t2 = () => { + if (dateRange === "all") { + return; + } + if (statsCache[dateRange]) { + return; + } + let cancelled = false; + setIsLoadingFiltered(true); + aggregateClaudeCodeStatsForRange(dateRange).then(data => { + if (!cancelled) { + setStatsCache(prev => ({ + ...prev, + [dateRange]: data + })); + setIsLoadingFiltered(false); + } + }).catch(() => { + if (!cancelled) { + setIsLoadingFiltered(false); + } + }); + return () => { + cancelled = true; + }; + }; + t3 = [dateRange, statsCache]; + $[1] = dateRange; + $[2] = statsCache; + $[3] = t2; + $[4] = t3; + } else { + t2 = $[3]; + t3 = $[4]; + } + useEffect(t2, t3); + const displayStats = dateRange === "all" ? allTimeResult.type === "success" ? allTimeResult.data : null : statsCache[dateRange] ?? (allTimeResult.type === "success" ? allTimeResult.data : null); + const allTimeStats = allTimeResult.type === "success" ? allTimeResult.data : null; + let t4; + if ($[5] !== onClose) { + t4 = () => { + onClose("Stats dialog dismissed", { + display: "system" + }); + }; + $[5] = onClose; + $[6] = t4; + } else { + t4 = $[6]; + } + const handleClose = t4; + let t5; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + context: "Confirmation" + }; + $[7] = t5; + } else { + t5 = $[7]; + } + useKeybinding("confirm:no", handleClose, t5); + let t6; + if ($[8] !== activeTab || $[9] !== dateRange || $[10] !== displayStats || $[11] !== onClose) { + t6 = (input, key) => { + if (key.ctrl && (input === "c" || input === "d")) { + onClose("Stats dialog dismissed", { + display: "system" + }); + } + if (key.tab) { + setActiveTab(_temp); + } + if (input === "r" && !key.ctrl && !key.meta) { + setDateRange(getNextDateRange(dateRange)); + } + if (key.ctrl && input === "s" && displayStats) { + handleScreenshot(displayStats, activeTab, setCopyStatus); + } + }; + $[8] = activeTab; + $[9] = dateRange; + $[10] = displayStats; + $[11] = onClose; + $[12] = t6; + } else { + t6 = $[12]; + } + useInput(t6); + if (allTimeResult.type === "error") { + let t7; + if ($[13] !== allTimeResult.message) { + t7 = Failed to load stats: {allTimeResult.message}; + $[13] = allTimeResult.message; + $[14] = t7; + } else { + t7 = $[14]; + } + return t7; + } + if (allTimeResult.type === "empty") { + let t7; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t7 = No stats available yet. Start using Claude Code!; + $[15] = t7; + } else { + t7 = $[15]; + } + return t7; + } + if (!displayStats || !allTimeStats) { + let t7; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t7 = Loading stats…; + $[16] = t7; + } else { + t7 = $[16]; + } + return t7; + } + let t7; + if ($[17] !== allTimeStats || $[18] !== dateRange || $[19] !== displayStats || $[20] !== isLoadingFiltered) { + t7 = ; + $[17] = allTimeStats; + $[18] = dateRange; + $[19] = displayStats; + $[20] = isLoadingFiltered; + $[21] = t7; + } else { + t7 = $[21]; + } + let t8; + if ($[22] !== dateRange || $[23] !== displayStats || $[24] !== isLoadingFiltered) { + t8 = ; + $[22] = dateRange; + $[23] = displayStats; + $[24] = isLoadingFiltered; + $[25] = t8; + } else { + t8 = $[25]; + } + let t9; + if ($[26] !== t7 || $[27] !== t8) { + t9 = {t7}{t8}; + $[26] = t7; + $[27] = t8; + $[28] = t9; + } else { + t9 = $[28]; + } + const t10 = copyStatus ? ` · ${copyStatus}` : ""; + let t11; + if ($[29] !== t10) { + t11 = Esc to cancel · r to cycle dates · ctrl+s to copy{t10}; + $[29] = t10; + $[30] = t11; + } else { + t11 = $[30]; + } + let t12; + if ($[31] !== t11 || $[32] !== t9) { + t12 = {t9}{t11}; + $[31] = t11; + $[32] = t9; + $[33] = t12; + } else { + t12 = $[33]; + } + return t12; +} +function _temp(prev_0) { + return prev_0 === "Overview" ? "Models" : "Overview"; +} +function DateRangeSelector(t0) { + const $ = _c(9); + const { + dateRange, + isLoading + } = t0; + let t1; + if ($[0] !== dateRange) { + t1 = DATE_RANGE_ORDER.map((range, i) => {i > 0 && · }{range === dateRange ? {DATE_RANGE_LABELS[range]} : {DATE_RANGE_LABELS[range]}}); + $[0] = dateRange; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== t1) { + t2 = {t1}; + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== isLoading) { + t3 = isLoading && ; + $[4] = isLoading; + $[5] = t3; + } else { + t3 = $[5]; + } + let t4; + if ($[6] !== t2 || $[7] !== t3) { + t4 = {t2}{t3}; + $[6] = t2; + $[7] = t3; + $[8] = t4; + } else { + t4 = $[8]; + } + return t4; +} +function OverviewTab({ + stats, + allTimeStats, + dateRange, + isLoading +}: { + stats: ClaudeCodeStats; + allTimeStats: ClaudeCodeStats; + dateRange: StatsDateRange; + isLoading: boolean; +}): React.ReactNode { + const { + columns: terminalWidth + } = useTerminalSize(); + + // Calculate favorite model and total tokens + const modelEntries = Object.entries(stats.modelUsage).sort(([, a], [, b]) => b.inputTokens + b.outputTokens - (a.inputTokens + a.outputTokens)); + const favoriteModel = modelEntries[0]; + const totalTokens = modelEntries.reduce((sum, [, usage]) => sum + usage.inputTokens + usage.outputTokens, 0); + + // Memoize the factoid so it doesn't change when switching tabs + const factoid = useMemo(() => generateFunFactoid(stats, totalTokens), [stats, totalTokens]); + + // Calculate range days based on selected date range + const rangeDays = dateRange === '7d' ? 7 : dateRange === '30d' ? 30 : stats.totalDays; + + // Compute shot stats data (ant-only, gated by feature flag) + let shotStatsData: { + avgShots: string; + buckets: { + label: string; + count: number; + pct: number; + }[]; + } | null = null; + if (feature('SHOT_STATS') && stats.shotDistribution) { + const dist = stats.shotDistribution; + const total = Object.values(dist).reduce((s, n) => s + n, 0); + if (total > 0) { + const totalShots = Object.entries(dist).reduce((s_0, [count, sessions]) => s_0 + parseInt(count, 10) * sessions, 0); + const bucket = (min: number, max?: number) => Object.entries(dist).filter(([k]) => { + const n_0 = parseInt(k, 10); + return n_0 >= min && (max === undefined || n_0 <= max); + }).reduce((s_1, [, v]) => s_1 + v, 0); + const pct = (n_1: number) => Math.round(n_1 / total * 100); + const b1 = bucket(1, 1); + const b2_5 = bucket(2, 5); + const b6_10 = bucket(6, 10); + const b11 = bucket(11); + shotStatsData = { + avgShots: (totalShots / total).toFixed(1), + buckets: [{ + label: '1-shot', + count: b1, + pct: pct(b1) + }, { + label: '2\u20135 shot', + count: b2_5, + pct: pct(b2_5) + }, { + label: '6\u201310 shot', + count: b6_10, + pct: pct(b6_10) + }, { + label: '11+ shot', + count: b11, + pct: pct(b11) + }] + }; + } + } + return + {/* Activity Heatmap - always shows all-time data */} + {allTimeStats.dailyActivity.length > 0 && + + {generateHeatmap(allTimeStats.dailyActivity, { + terminalWidth + })} + + } + + {/* Date range selector */} + + + {/* Section 1: Usage */} + + + {favoriteModel && + Favorite model:{' '} + + {renderModelName(favoriteModel[0])} + + } + + + + Total tokens:{' '} + {formatNumber(totalTokens)} + + + + + {/* Section 2: Activity - Row 1: Sessions | Longest session */} + + + + Sessions:{' '} + {formatNumber(stats.totalSessions)} + + + + {stats.longestSession && + Longest session:{' '} + + {formatDuration(stats.longestSession.duration)} + + } + + + + {/* Row 2: Active days | Longest streak */} + + + + Active days: {stats.activeDays} + /{rangeDays} + + + + + Longest streak:{' '} + + {stats.streaks.longestStreak} + {' '} + {stats.streaks.longestStreak === 1 ? 'day' : 'days'} + + + + + {/* Row 3: Most active day | Current streak */} + + + {stats.peakActivityDay && + Most active day:{' '} + {formatPeakDay(stats.peakActivityDay)} + } + + + + Current streak:{' '} + + {allTimeStats.streaks.currentStreak} + {' '} + {allTimeStats.streaks.currentStreak === 1 ? 'day' : 'days'} + + + + + {/* Speculation time saved (ant-only) */} + {"external" === 'ant' && stats.totalSpeculationTimeSavedMs > 0 && + + + Speculation saved:{' '} + + {formatDuration(stats.totalSpeculationTimeSavedMs)} + + + + } + + {/* Shot stats (ant-only) */} + {shotStatsData && <> + + Shot distribution + + + + + {shotStatsData.buckets[0]!.label}:{' '} + {shotStatsData.buckets[0]!.count} + ({shotStatsData.buckets[0]!.pct}%) + + + + + {shotStatsData.buckets[1]!.label}:{' '} + {shotStatsData.buckets[1]!.count} + ({shotStatsData.buckets[1]!.pct}%) + + + + + + + {shotStatsData.buckets[2]!.label}:{' '} + {shotStatsData.buckets[2]!.count} + ({shotStatsData.buckets[2]!.pct}%) + + + + + {shotStatsData.buckets[3]!.label}:{' '} + {shotStatsData.buckets[3]!.count} + ({shotStatsData.buckets[3]!.pct}%) + + + + + + + Avg/session:{' '} + {shotStatsData.avgShots} + + + + } + + {/* Fun factoid */} + {factoid && + {factoid} + } + ; +} + +// Famous books and their approximate token counts (words * ~1.3) +// Sorted by tokens ascending for comparison logic +const BOOK_COMPARISONS = [{ + name: 'The Little Prince', + tokens: 22000 +}, { + name: 'The Old Man and the Sea', + tokens: 35000 +}, { + name: 'A Christmas Carol', + tokens: 37000 +}, { + name: 'Animal Farm', + tokens: 39000 +}, { + name: 'Fahrenheit 451', + tokens: 60000 +}, { + name: 'The Great Gatsby', + tokens: 62000 +}, { + name: 'Slaughterhouse-Five', + tokens: 64000 +}, { + name: 'Brave New World', + tokens: 83000 +}, { + name: 'The Catcher in the Rye', + tokens: 95000 +}, { + name: "Harry Potter and the Philosopher's Stone", + tokens: 103000 +}, { + name: 'The Hobbit', + tokens: 123000 +}, { + name: '1984', + tokens: 123000 +}, { + name: 'To Kill a Mockingbird', + tokens: 130000 +}, { + name: 'Pride and Prejudice', + tokens: 156000 +}, { + name: 'Dune', + tokens: 244000 +}, { + name: 'Moby-Dick', + tokens: 268000 +}, { + name: 'Crime and Punishment', + tokens: 274000 +}, { + name: 'A Game of Thrones', + tokens: 381000 +}, { + name: 'Anna Karenina', + tokens: 468000 +}, { + name: 'Don Quixote', + tokens: 520000 +}, { + name: 'The Lord of the Rings', + tokens: 576000 +}, { + name: 'The Count of Monte Cristo', + tokens: 603000 +}, { + name: 'Les Misérables', + tokens: 689000 +}, { + name: 'War and Peace', + tokens: 730000 +}]; + +// Time equivalents for session durations +const TIME_COMPARISONS = [{ + name: 'a TED talk', + minutes: 18 +}, { + name: 'an episode of The Office', + minutes: 22 +}, { + name: 'listening to Abbey Road', + minutes: 47 +}, { + name: 'a yoga class', + minutes: 60 +}, { + name: 'a World Cup soccer match', + minutes: 90 +}, { + name: 'a half marathon (average time)', + minutes: 120 +}, { + name: 'the movie Inception', + minutes: 148 +}, { + name: 'watching Titanic', + minutes: 195 +}, { + name: 'a transatlantic flight', + minutes: 420 +}, { + name: 'a full night of sleep', + minutes: 480 +}]; +function generateFunFactoid(stats: ClaudeCodeStats, totalTokens: number): string { + const factoids: string[] = []; + if (totalTokens > 0) { + const matchingBooks = BOOK_COMPARISONS.filter(book => totalTokens >= book.tokens); + for (const book of matchingBooks) { + const times = totalTokens / book.tokens; + if (times >= 2) { + factoids.push(`You've used ~${Math.floor(times)}x more tokens than ${book.name}`); + } else { + factoids.push(`You've used the same number of tokens as ${book.name}`); + } + } + } + if (stats.longestSession) { + const sessionMinutes = stats.longestSession.duration / (1000 * 60); + for (const comparison of TIME_COMPARISONS) { + const ratio = sessionMinutes / comparison.minutes; + if (ratio >= 2) { + factoids.push(`Your longest session is ~${Math.floor(ratio)}x longer than ${comparison.name}`); + } + } + } + if (factoids.length === 0) { + return ''; + } + const randomIndex = Math.floor(Math.random() * factoids.length); + return factoids[randomIndex]!; +} +function ModelsTab(t0) { + const $ = _c(15); + const { + stats, + dateRange, + isLoading + } = t0; + const { + headerFocused, + focusHeader + } = useTabHeaderFocus(); + const [scrollOffset, setScrollOffset] = useState(0); + const { + columns: terminalWidth + } = useTerminalSize(); + const modelEntries = Object.entries(stats.modelUsage).sort(_temp7); + const t1 = !headerFocused; + let t2; + if ($[0] !== t1) { + t2 = { + isActive: t1 + }; + $[0] = t1; + $[1] = t2; + } else { + t2 = $[1]; + } + useInput((_input, key) => { + if (key.downArrow && scrollOffset < modelEntries.length - 4) { + setScrollOffset(prev => Math.min(prev + 2, modelEntries.length - 4)); + } + if (key.upArrow) { + if (scrollOffset > 0) { + setScrollOffset(_temp8); + } else { + focusHeader(); + } + } + }, t2); + if (modelEntries.length === 0) { + let t3; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t3 = No model usage data available; + $[2] = t3; + } else { + t3 = $[2]; + } + return t3; + } + const totalTokens = modelEntries.reduce(_temp9, 0); + const chartOutput = generateTokenChart(stats.dailyModelTokens, modelEntries.map(_temp0), terminalWidth); + const visibleModels = modelEntries.slice(scrollOffset, scrollOffset + 4); + const midpoint = Math.ceil(visibleModels.length / 2); + const leftModels = visibleModels.slice(0, midpoint); + const rightModels = visibleModels.slice(midpoint); + const canScrollUp = scrollOffset > 0; + const canScrollDown = scrollOffset < modelEntries.length - 4; + const showScrollHint = modelEntries.length > 4; + let t3; + if ($[3] !== dateRange || $[4] !== isLoading) { + t3 = ; + $[3] = dateRange; + $[4] = isLoading; + $[5] = t3; + } else { + t3 = $[5]; + } + const T0 = Box; + const t5 = "column"; + const t6 = 36; + const t8 = rightModels.map(t7 => { + const [model_1, usage_1] = t7; + return ; + }); + let t9; + if ($[6] !== T0 || $[7] !== t8) { + t9 = {t8}; + $[6] = T0; + $[7] = t8; + $[8] = t9; + } else { + t9 = $[8]; + } + let t10; + if ($[9] !== canScrollDown || $[10] !== canScrollUp || $[11] !== modelEntries || $[12] !== scrollOffset || $[13] !== showScrollHint) { + t10 = showScrollHint && {canScrollUp ? figures.arrowUp : " "}{" "}{canScrollDown ? figures.arrowDown : " "} {scrollOffset + 1}-{Math.min(scrollOffset + 4, modelEntries.length)} of{" "}{modelEntries.length} models (↑↓ to scroll); + $[9] = canScrollDown; + $[10] = canScrollUp; + $[11] = modelEntries; + $[12] = scrollOffset; + $[13] = showScrollHint; + $[14] = t10; + } else { + t10 = $[14]; + } + return {chartOutput && Tokens per Day{chartOutput.chart}{chartOutput.xAxisLabels}{chartOutput.legend.map(_temp1)}}{t3}{leftModels.map(t4 => { + const [model_0, usage_0] = t4; + return ; + })}{t9}{t10}; +} +function _temp1(item, i) { + return {i > 0 ? " \xB7 " : ""}{item.coloredBullet} {item.model}; +} +function _temp0(t0) { + const [model] = t0; + return model; +} +function _temp9(sum, t0) { + const [, usage] = t0; + return sum + usage.inputTokens + usage.outputTokens; +} +function _temp8(prev_0) { + return Math.max(prev_0 - 2, 0); +} +function _temp7(t0, t1) { + const [, a] = t0; + const [, b] = t1; + return b.inputTokens + b.outputTokens - (a.inputTokens + a.outputTokens); +} +type ModelEntryProps = { + model: string; + usage: { + inputTokens: number; + outputTokens: number; + cacheReadInputTokens: number; + }; + totalTokens: number; +}; +function ModelEntry(t0) { + const $ = _c(21); + const { + model, + usage, + totalTokens + } = t0; + const modelTokens = usage.inputTokens + usage.outputTokens; + const t1 = modelTokens / totalTokens * 100; + let t2; + if ($[0] !== t1) { + t2 = t1.toFixed(1); + $[0] = t1; + $[1] = t2; + } else { + t2 = $[1]; + } + const percentage = t2; + let t3; + if ($[2] !== model) { + t3 = renderModelName(model); + $[2] = model; + $[3] = t3; + } else { + t3 = $[3]; + } + let t4; + if ($[4] !== t3) { + t4 = {t3}; + $[4] = t3; + $[5] = t4; + } else { + t4 = $[5]; + } + let t5; + if ($[6] !== percentage) { + t5 = ({percentage}%); + $[6] = percentage; + $[7] = t5; + } else { + t5 = $[7]; + } + let t6; + if ($[8] !== t4 || $[9] !== t5) { + t6 = {figures.bullet} {t4}{" "}{t5}; + $[8] = t4; + $[9] = t5; + $[10] = t6; + } else { + t6 = $[10]; + } + let t7; + if ($[11] !== usage.inputTokens) { + t7 = formatNumber(usage.inputTokens); + $[11] = usage.inputTokens; + $[12] = t7; + } else { + t7 = $[12]; + } + let t8; + if ($[13] !== usage.outputTokens) { + t8 = formatNumber(usage.outputTokens); + $[13] = usage.outputTokens; + $[14] = t8; + } else { + t8 = $[14]; + } + let t9; + if ($[15] !== t7 || $[16] !== t8) { + t9 = {" "}In: {t7} · Out:{" "}{t8}; + $[15] = t7; + $[16] = t8; + $[17] = t9; + } else { + t9 = $[17]; + } + let t10; + if ($[18] !== t6 || $[19] !== t9) { + t10 = {t6}{t9}; + $[18] = t6; + $[19] = t9; + $[20] = t10; + } else { + t10 = $[20]; + } + return t10; +} +type ChartLegend = { + model: string; + coloredBullet: string; // Pre-colored bullet using chalk +}; +type ChartOutput = { + chart: string; + legend: ChartLegend[]; + xAxisLabels: string; +}; +function generateTokenChart(dailyTokens: DailyModelTokens[], models: string[], terminalWidth: number): ChartOutput | null { + if (dailyTokens.length < 2 || models.length === 0) { + return null; + } + + // Y-axis labels take about 6 characters, plus some padding + // Cap at ~52 to align with heatmap width (1 year of data) + const yAxisWidth = 7; + const availableWidth = terminalWidth - yAxisWidth; + const chartWidth = Math.min(52, Math.max(20, availableWidth)); + + // Distribute data across the available chart width + let recentData: DailyModelTokens[]; + if (dailyTokens.length >= chartWidth) { + // More data than space: take most recent N days + recentData = dailyTokens.slice(-chartWidth); + } else { + // Less data than space: expand by repeating each point + const repeatCount = Math.floor(chartWidth / dailyTokens.length); + recentData = []; + for (const day of dailyTokens) { + for (let i = 0; i < repeatCount; i++) { + recentData.push(day); + } + } + } + + // Color palette for different models - use theme colors + const theme = getTheme(resolveThemeSetting(getGlobalConfig().theme)); + const colors = [themeColorToAnsi(theme.suggestion), themeColorToAnsi(theme.success), themeColorToAnsi(theme.warning)]; + + // Prepare series data for each model + const series: number[][] = []; + const legend: ChartLegend[] = []; + + // Only show top 3 models to keep chart readable + const topModels = models.slice(0, 3); + for (let i = 0; i < topModels.length; i++) { + const model = topModels[i]!; + const data = recentData.map(day => day.tokensByModel[model] || 0); + + // Only include if there's actual data + if (data.some(v => v > 0)) { + series.push(data); + // Use theme colors that match the chart + const bulletColors = [theme.suggestion, theme.success, theme.warning]; + legend.push({ + model: renderModelName(model), + coloredBullet: applyColor(figures.bullet, bulletColors[i % bulletColors.length] as Color) + }); + } + } + if (series.length === 0) { + return null; + } + const chart = asciichart(series, { + height: 8, + colors: colors.slice(0, series.length), + format: (x: number) => { + let label: string; + if (x >= 1_000_000) { + label = (x / 1_000_000).toFixed(1) + 'M'; + } else if (x >= 1_000) { + label = (x / 1_000).toFixed(0) + 'k'; + } else { + label = x.toFixed(0); + } + return label.padStart(6); + } + }); + + // Generate x-axis labels with dates + const xAxisLabels = generateXAxisLabels(recentData, recentData.length, yAxisWidth); + return { + chart, + legend, + xAxisLabels + }; +} +function generateXAxisLabels(data: DailyModelTokens[], _chartWidth: number, yAxisOffset: number): string { + if (data.length === 0) return ''; + + // Show 3-4 date labels evenly spaced, but leave room for last label + const numLabels = Math.min(4, Math.max(2, Math.floor(data.length / 8))); + // Don't use the very last position - leave room for the label text + const usableLength = data.length - 6; // Reserve ~6 chars for last label (e.g., "Dec 7") + const step = Math.floor(usableLength / (numLabels - 1)) || 1; + const labelPositions: { + pos: number; + label: string; + }[] = []; + for (let i = 0; i < numLabels; i++) { + const idx = Math.min(i * step, data.length - 1); + const date = new Date(data[idx]!.date); + const label = date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric' + }); + labelPositions.push({ + pos: idx, + label + }); + } + + // Build the label string with proper spacing + let result = ' '.repeat(yAxisOffset); + let currentPos = 0; + for (const { + pos, + label + } of labelPositions) { + const spaces = Math.max(1, pos - currentPos); + result += ' '.repeat(spaces) + label; + currentPos = pos + label.length; + } + return result; +} + +// Screenshot functionality +async function handleScreenshot(stats: ClaudeCodeStats, activeTab: 'Overview' | 'Models', setStatus: (status: string | null) => void): Promise { + setStatus('copying…'); + const ansiText = renderStatsToAnsi(stats, activeTab); + const result = await copyAnsiToClipboard(ansiText); + setStatus(result.success ? 'copied!' : 'copy failed'); + + // Clear status after 2 seconds + setTimeout(setStatus, 2000, null); +} +function renderStatsToAnsi(stats: ClaudeCodeStats, activeTab: 'Overview' | 'Models'): string { + const lines: string[] = []; + if (activeTab === 'Overview') { + lines.push(...renderOverviewToAnsi(stats)); + } else { + lines.push(...renderModelsToAnsi(stats)); + } + + // Trim trailing empty lines + while (lines.length > 0 && stripAnsi(lines[lines.length - 1]!).trim() === '') { + lines.pop(); + } + + // Add "/stats" right-aligned on the last line + if (lines.length > 0) { + const lastLine = lines[lines.length - 1]!; + const lastLineLen = getStringWidth(lastLine); + // Use known content widths based on layout: + // Overview: two-column stats = COL2_START(40) + COL2_LABEL_WIDTH(18) + max_value(~12) = 70 + // Models: chart width = 80 + const contentWidth = activeTab === 'Overview' ? 70 : 80; + const statsLabel = '/stats'; + const padding = Math.max(2, contentWidth - lastLineLen - statsLabel.length); + lines[lines.length - 1] = lastLine + ' '.repeat(padding) + chalk.gray(statsLabel); + } + return lines.join('\n'); +} +function renderOverviewToAnsi(stats: ClaudeCodeStats): string[] { + const lines: string[] = []; + const theme = getTheme(resolveThemeSetting(getGlobalConfig().theme)); + const h = (text: string) => applyColor(text, theme.claude as Color); + + // Two-column helper with fixed spacing + // Column 1: label (18 chars) + value + padding to reach col 2 + // Column 2 starts at character position 40 + const COL1_LABEL_WIDTH = 18; + const COL2_START = 40; + const COL2_LABEL_WIDTH = 18; + const row = (l1: string, v1: string, l2: string, v2: string): string => { + // Build column 1: label + value + const label1 = (l1 + ':').padEnd(COL1_LABEL_WIDTH); + const col1PlainLen = label1.length + v1.length; + + // Calculate spaces needed between col1 value and col2 label + const spaceBetween = Math.max(2, COL2_START - col1PlainLen); + + // Build column 2: label + value + const label2 = (l2 + ':').padEnd(COL2_LABEL_WIDTH); + + // Assemble with colors applied to values only + return label1 + h(v1) + ' '.repeat(spaceBetween) + label2 + h(v2); + }; + + // Heatmap - use fixed width for screenshot (56 = 52 weeks + 4 for day labels) + if (stats.dailyActivity.length > 0) { + lines.push(generateHeatmap(stats.dailyActivity, { + terminalWidth: 56 + })); + lines.push(''); + } + + // Calculate values + const modelEntries = Object.entries(stats.modelUsage).sort(([, a], [, b]) => b.inputTokens + b.outputTokens - (a.inputTokens + a.outputTokens)); + const favoriteModel = modelEntries[0]; + const totalTokens = modelEntries.reduce((sum, [, usage]) => sum + usage.inputTokens + usage.outputTokens, 0); + + // Row 1: Favorite model | Total tokens + if (favoriteModel) { + lines.push(row('Favorite model', renderModelName(favoriteModel[0]), 'Total tokens', formatNumber(totalTokens))); + } + lines.push(''); + + // Row 2: Sessions | Longest session + lines.push(row('Sessions', formatNumber(stats.totalSessions), 'Longest session', stats.longestSession ? formatDuration(stats.longestSession.duration) : 'N/A')); + + // Row 3: Current streak | Longest streak + const currentStreakVal = `${stats.streaks.currentStreak} ${stats.streaks.currentStreak === 1 ? 'day' : 'days'}`; + const longestStreakVal = `${stats.streaks.longestStreak} ${stats.streaks.longestStreak === 1 ? 'day' : 'days'}`; + lines.push(row('Current streak', currentStreakVal, 'Longest streak', longestStreakVal)); + + // Row 4: Active days | Peak hour + const activeDaysVal = `${stats.activeDays}/${stats.totalDays}`; + const peakHourVal = stats.peakActivityHour !== null ? `${stats.peakActivityHour}:00-${stats.peakActivityHour + 1}:00` : 'N/A'; + lines.push(row('Active days', activeDaysVal, 'Peak hour', peakHourVal)); + + // Speculation time saved (ant-only) + if ("external" === 'ant' && stats.totalSpeculationTimeSavedMs > 0) { + const label = 'Speculation saved:'.padEnd(COL1_LABEL_WIDTH); + lines.push(label + h(formatDuration(stats.totalSpeculationTimeSavedMs))); + } + + // Shot stats (ant-only) + if (feature('SHOT_STATS') && stats.shotDistribution) { + const dist = stats.shotDistribution; + const totalWithShots = Object.values(dist).reduce((s, n) => s + n, 0); + if (totalWithShots > 0) { + const totalShots = Object.entries(dist).reduce((s, [count, sessions]) => s + parseInt(count, 10) * sessions, 0); + const avgShots = (totalShots / totalWithShots).toFixed(1); + const bucket = (min: number, max?: number) => Object.entries(dist).filter(([k]) => { + const n = parseInt(k, 10); + return n >= min && (max === undefined || n <= max); + }).reduce((s, [, v]) => s + v, 0); + const pct = (n: number) => Math.round(n / totalWithShots * 100); + const fmtBucket = (count: number, p: number) => `${count} (${p}%)`; + const b1 = bucket(1, 1); + const b2_5 = bucket(2, 5); + const b6_10 = bucket(6, 10); + const b11 = bucket(11); + lines.push(''); + lines.push('Shot distribution'); + lines.push(row('1-shot', fmtBucket(b1, pct(b1)), '2\u20135 shot', fmtBucket(b2_5, pct(b2_5)))); + lines.push(row('6\u201310 shot', fmtBucket(b6_10, pct(b6_10)), '11+ shot', fmtBucket(b11, pct(b11)))); + lines.push(`${'Avg/session:'.padEnd(COL1_LABEL_WIDTH)}${h(avgShots)}`); + } + } + lines.push(''); + + // Fun factoid + const factoid = generateFunFactoid(stats, totalTokens); + lines.push(h(factoid)); + lines.push(chalk.gray(`Stats from the last ${stats.totalDays} days`)); + return lines; +} +function renderModelsToAnsi(stats: ClaudeCodeStats): string[] { + const lines: string[] = []; + const modelEntries = Object.entries(stats.modelUsage).sort(([, a], [, b]) => b.inputTokens + b.outputTokens - (a.inputTokens + a.outputTokens)); + if (modelEntries.length === 0) { + lines.push(chalk.gray('No model usage data available')); + return lines; + } + const favoriteModel = modelEntries[0]; + const totalTokens = modelEntries.reduce((sum, [, usage]) => sum + usage.inputTokens + usage.outputTokens, 0); + + // Generate chart if we have data - use fixed width for screenshot + const chartOutput = generateTokenChart(stats.dailyModelTokens, modelEntries.map(([model]) => model), 80 // Fixed width for screenshot + ); + if (chartOutput) { + lines.push(chalk.bold('Tokens per Day')); + lines.push(chartOutput.chart); + lines.push(chalk.gray(chartOutput.xAxisLabels)); + // Legend - use pre-colored bullets from chart output + const legendLine = chartOutput.legend.map(item => `${item.coloredBullet} ${item.model}`).join(' · '); + lines.push(legendLine); + lines.push(''); + } + + // Summary + lines.push(`${figures.star} Favorite: ${chalk.magenta.bold(renderModelName(favoriteModel?.[0] || ''))} · ${figures.circle} Total: ${chalk.magenta(formatNumber(totalTokens))} tokens`); + lines.push(''); + + // Model breakdown - only show top 3 for screenshot + const topModels = modelEntries.slice(0, 3); + for (const [model, usage] of topModels) { + const modelTokens = usage.inputTokens + usage.outputTokens; + const percentage = (modelTokens / totalTokens * 100).toFixed(1); + lines.push(`${figures.bullet} ${chalk.bold(renderModelName(model))} ${chalk.gray(`(${percentage}%)`)}`); + lines.push(chalk.dim(` In: ${formatNumber(usage.inputTokens)} · Out: ${formatNumber(usage.outputTokens)}`)); + } + return lines; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwicGxvdCIsImFzY2lpY2hhcnQiLCJjaGFsayIsImZpZ3VyZXMiLCJSZWFjdCIsIlN1c3BlbnNlIiwidXNlIiwidXNlQ2FsbGJhY2siLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwidXNlU3RhdGUiLCJzdHJpcEFuc2kiLCJDb21tYW5kUmVzdWx0RGlzcGxheSIsInVzZVRlcm1pbmFsU2l6ZSIsImFwcGx5Q29sb3IiLCJzdHJpbmdXaWR0aCIsImdldFN0cmluZ1dpZHRoIiwiQ29sb3IiLCJBbnNpIiwiQm94IiwiVGV4dCIsInVzZUlucHV0IiwidXNlS2V5YmluZGluZyIsImdldEdsb2JhbENvbmZpZyIsImZvcm1hdER1cmF0aW9uIiwiZm9ybWF0TnVtYmVyIiwiZ2VuZXJhdGVIZWF0bWFwIiwicmVuZGVyTW9kZWxOYW1lIiwiY29weUFuc2lUb0NsaXBib2FyZCIsImFnZ3JlZ2F0ZUNsYXVkZUNvZGVTdGF0c0ZvclJhbmdlIiwiQ2xhdWRlQ29kZVN0YXRzIiwiRGFpbHlNb2RlbFRva2VucyIsIlN0YXRzRGF0ZVJhbmdlIiwicmVzb2x2ZVRoZW1lU2V0dGluZyIsImdldFRoZW1lIiwidGhlbWVDb2xvclRvQW5zaSIsIlBhbmUiLCJUYWIiLCJUYWJzIiwidXNlVGFiSGVhZGVyRm9jdXMiLCJTcGlubmVyIiwiZm9ybWF0UGVha0RheSIsImRhdGVTdHIiLCJkYXRlIiwiRGF0ZSIsInRvTG9jYWxlRGF0ZVN0cmluZyIsIm1vbnRoIiwiZGF5IiwiUHJvcHMiLCJvbkNsb3NlIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJTdGF0c1Jlc3VsdCIsInR5cGUiLCJkYXRhIiwibWVzc2FnZSIsIkRBVEVfUkFOR0VfTEFCRUxTIiwiUmVjb3JkIiwiYWxsIiwiREFURV9SQU5HRV9PUkRFUiIsImdldE5leHREYXRlUmFuZ2UiLCJjdXJyZW50IiwiY3VycmVudEluZGV4IiwiaW5kZXhPZiIsImxlbmd0aCIsImNyZWF0ZUFsbFRpbWVTdGF0c1Byb21pc2UiLCJQcm9taXNlIiwidGhlbiIsInRvdGFsU2Vzc2lvbnMiLCJjYXRjaCIsImVyciIsIkVycm9yIiwiU3RhdHMiLCJ0MCIsIiQiLCJfYyIsInQxIiwiU3ltYm9sIiwiZm9yIiwiYWxsVGltZVByb21pc2UiLCJ0MiIsInQzIiwiU3RhdHNDb250ZW50UHJvcHMiLCJTdGF0c0NvbnRlbnQiLCJhbGxUaW1lUmVzdWx0IiwiZGF0ZVJhbmdlIiwic2V0RGF0ZVJhbmdlIiwic3RhdHNDYWNoZSIsInNldFN0YXRzQ2FjaGUiLCJpc0xvYWRpbmdGaWx0ZXJlZCIsInNldElzTG9hZGluZ0ZpbHRlcmVkIiwiYWN0aXZlVGFiIiwic2V0QWN0aXZlVGFiIiwiY29weVN0YXR1cyIsInNldENvcHlTdGF0dXMiLCJjYW5jZWxsZWQiLCJwcmV2IiwiZGlzcGxheVN0YXRzIiwiYWxsVGltZVN0YXRzIiwidDQiLCJoYW5kbGVDbG9zZSIsInQ1IiwiY29udGV4dCIsInQ2IiwiaW5wdXQiLCJrZXkiLCJjdHJsIiwidGFiIiwiX3RlbXAiLCJtZXRhIiwiaGFuZGxlU2NyZWVuc2hvdCIsInQ3IiwidDgiLCJ0OSIsInQxMCIsInQxMSIsInQxMiIsInByZXZfMCIsIkRhdGVSYW5nZVNlbGVjdG9yIiwiaXNMb2FkaW5nIiwibWFwIiwicmFuZ2UiLCJpIiwiT3ZlcnZpZXdUYWIiLCJzdGF0cyIsIlJlYWN0Tm9kZSIsImNvbHVtbnMiLCJ0ZXJtaW5hbFdpZHRoIiwibW9kZWxFbnRyaWVzIiwiT2JqZWN0IiwiZW50cmllcyIsIm1vZGVsVXNhZ2UiLCJzb3J0IiwiYSIsImIiLCJpbnB1dFRva2VucyIsIm91dHB1dFRva2VucyIsImZhdm9yaXRlTW9kZWwiLCJ0b3RhbFRva2VucyIsInJlZHVjZSIsInN1bSIsInVzYWdlIiwiZmFjdG9pZCIsImdlbmVyYXRlRnVuRmFjdG9pZCIsInJhbmdlRGF5cyIsInRvdGFsRGF5cyIsInNob3RTdGF0c0RhdGEiLCJhdmdTaG90cyIsImJ1Y2tldHMiLCJsYWJlbCIsImNvdW50IiwicGN0Iiwic2hvdERpc3RyaWJ1dGlvbiIsImRpc3QiLCJ0b3RhbCIsInZhbHVlcyIsInMiLCJuIiwidG90YWxTaG90cyIsInNlc3Npb25zIiwicGFyc2VJbnQiLCJidWNrZXQiLCJtaW4iLCJtYXgiLCJmaWx0ZXIiLCJrIiwidW5kZWZpbmVkIiwidiIsIk1hdGgiLCJyb3VuZCIsImIxIiwiYjJfNSIsImI2XzEwIiwiYjExIiwidG9GaXhlZCIsImRhaWx5QWN0aXZpdHkiLCJsb25nZXN0U2Vzc2lvbiIsImR1cmF0aW9uIiwiYWN0aXZlRGF5cyIsInN0cmVha3MiLCJsb25nZXN0U3RyZWFrIiwicGVha0FjdGl2aXR5RGF5IiwiY3VycmVudFN0cmVhayIsInRvdGFsU3BlY3VsYXRpb25UaW1lU2F2ZWRNcyIsIkJPT0tfQ09NUEFSSVNPTlMiLCJuYW1lIiwidG9rZW5zIiwiVElNRV9DT01QQVJJU09OUyIsIm1pbnV0ZXMiLCJmYWN0b2lkcyIsIm1hdGNoaW5nQm9va3MiLCJib29rIiwidGltZXMiLCJwdXNoIiwiZmxvb3IiLCJzZXNzaW9uTWludXRlcyIsImNvbXBhcmlzb24iLCJyYXRpbyIsInJhbmRvbUluZGV4IiwicmFuZG9tIiwiTW9kZWxzVGFiIiwiaGVhZGVyRm9jdXNlZCIsImZvY3VzSGVhZGVyIiwic2Nyb2xsT2Zmc2V0Iiwic2V0U2Nyb2xsT2Zmc2V0IiwiX3RlbXA3IiwiaXNBY3RpdmUiLCJfaW5wdXQiLCJkb3duQXJyb3ciLCJ1cEFycm93IiwiX3RlbXA4IiwiX3RlbXA5IiwiY2hhcnRPdXRwdXQiLCJnZW5lcmF0ZVRva2VuQ2hhcnQiLCJkYWlseU1vZGVsVG9rZW5zIiwiX3RlbXAwIiwidmlzaWJsZU1vZGVscyIsInNsaWNlIiwibWlkcG9pbnQiLCJjZWlsIiwibGVmdE1vZGVscyIsInJpZ2h0TW9kZWxzIiwiY2FuU2Nyb2xsVXAiLCJjYW5TY3JvbGxEb3duIiwic2hvd1Njcm9sbEhpbnQiLCJUMCIsIm1vZGVsXzEiLCJ1c2FnZV8xIiwibW9kZWwiLCJhcnJvd1VwIiwiYXJyb3dEb3duIiwiY2hhcnQiLCJ4QXhpc0xhYmVscyIsImxlZ2VuZCIsIl90ZW1wMSIsIm1vZGVsXzAiLCJ1c2FnZV8wIiwiaXRlbSIsImNvbG9yZWRCdWxsZXQiLCJNb2RlbEVudHJ5UHJvcHMiLCJjYWNoZVJlYWRJbnB1dFRva2VucyIsIk1vZGVsRW50cnkiLCJtb2RlbFRva2VucyIsInBlcmNlbnRhZ2UiLCJidWxsZXQiLCJDaGFydExlZ2VuZCIsIkNoYXJ0T3V0cHV0IiwiZGFpbHlUb2tlbnMiLCJtb2RlbHMiLCJ5QXhpc1dpZHRoIiwiYXZhaWxhYmxlV2lkdGgiLCJjaGFydFdpZHRoIiwicmVjZW50RGF0YSIsInJlcGVhdENvdW50IiwidGhlbWUiLCJjb2xvcnMiLCJzdWdnZXN0aW9uIiwic3VjY2VzcyIsIndhcm5pbmciLCJzZXJpZXMiLCJ0b3BNb2RlbHMiLCJ0b2tlbnNCeU1vZGVsIiwic29tZSIsImJ1bGxldENvbG9ycyIsImhlaWdodCIsImZvcm1hdCIsIngiLCJwYWRTdGFydCIsImdlbmVyYXRlWEF4aXNMYWJlbHMiLCJfY2hhcnRXaWR0aCIsInlBeGlzT2Zmc2V0IiwibnVtTGFiZWxzIiwidXNhYmxlTGVuZ3RoIiwic3RlcCIsImxhYmVsUG9zaXRpb25zIiwicG9zIiwiaWR4IiwicmVwZWF0IiwiY3VycmVudFBvcyIsInNwYWNlcyIsInNldFN0YXR1cyIsInN0YXR1cyIsImFuc2lUZXh0IiwicmVuZGVyU3RhdHNUb0Fuc2kiLCJzZXRUaW1lb3V0IiwibGluZXMiLCJyZW5kZXJPdmVydmlld1RvQW5zaSIsInJlbmRlck1vZGVsc1RvQW5zaSIsInRyaW0iLCJwb3AiLCJsYXN0TGluZSIsImxhc3RMaW5lTGVuIiwiY29udGVudFdpZHRoIiwic3RhdHNMYWJlbCIsInBhZGRpbmciLCJncmF5Iiwiam9pbiIsImgiLCJ0ZXh0IiwiY2xhdWRlIiwiQ09MMV9MQUJFTF9XSURUSCIsIkNPTDJfU1RBUlQiLCJDT0wyX0xBQkVMX1dJRFRIIiwicm93IiwibDEiLCJ2MSIsImwyIiwidjIiLCJsYWJlbDEiLCJwYWRFbmQiLCJjb2wxUGxhaW5MZW4iLCJzcGFjZUJldHdlZW4iLCJsYWJlbDIiLCJjdXJyZW50U3RyZWFrVmFsIiwibG9uZ2VzdFN0cmVha1ZhbCIsImFjdGl2ZURheXNWYWwiLCJwZWFrSG91clZhbCIsInBlYWtBY3Rpdml0eUhvdXIiLCJ0b3RhbFdpdGhTaG90cyIsImZtdEJ1Y2tldCIsInAiLCJib2xkIiwibGVnZW5kTGluZSIsInN0YXIiLCJtYWdlbnRhIiwiY2lyY2xlIiwiZGltIl0sInNvdXJjZXMiOlsiU3RhdHMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0IHsgcGxvdCBhcyBhc2NpaWNoYXJ0IH0gZnJvbSAnYXNjaWljaGFydCdcbmltcG9ydCBjaGFsayBmcm9tICdjaGFsaydcbmltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgUmVhY3QsIHtcbiAgU3VzcGVuc2UsXG4gIHVzZSxcbiAgdXNlQ2FsbGJhY2ssXG4gIHVzZUVmZmVjdCxcbiAgdXNlTWVtbyxcbiAgdXNlU3RhdGUsXG59IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHN0cmlwQW5zaSBmcm9tICdzdHJpcC1hbnNpJ1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgYXBwbHlDb2xvciB9IGZyb20gJy4uL2luay9jb2xvcml6ZS5qcydcbmltcG9ydCB7IHN0cmluZ1dpZHRoIGFzIGdldFN0cmluZ1dpZHRoIH0gZnJvbSAnLi4vaW5rL3N0cmluZ1dpZHRoLmpzJ1xuaW1wb3J0IHR5cGUgeyBDb2xvciB9IGZyb20gJy4uL2luay9zdHlsZXMuanMnXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL3ByZWZlci11c2Uta2V5YmluZGluZ3MgLS0gcmF3IGovay9hcnJvdyBzdGF0cyBuYXZpZ2F0aW9uXG5pbXBvcnQgeyBBbnNpLCBCb3gsIFRleHQsIHVzZUlucHV0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyBnZXRHbG9iYWxDb25maWcgfSBmcm9tICcuLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgeyBmb3JtYXREdXJhdGlvbiwgZm9ybWF0TnVtYmVyIH0gZnJvbSAnLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgZ2VuZXJhdGVIZWF0bWFwIH0gZnJvbSAnLi4vdXRpbHMvaGVhdG1hcC5qcydcbmltcG9ydCB7IHJlbmRlck1vZGVsTmFtZSB9IGZyb20gJy4uL3V0aWxzL21vZGVsL21vZGVsLmpzJ1xuaW1wb3J0IHsgY29weUFuc2lUb0NsaXBib2FyZCB9IGZyb20gJy4uL3V0aWxzL3NjcmVlbnNob3RDbGlwYm9hcmQuanMnXG5pbXBvcnQge1xuICBhZ2dyZWdhdGVDbGF1ZGVDb2RlU3RhdHNGb3JSYW5nZSxcbiAgdHlwZSBDbGF1ZGVDb2RlU3RhdHMsXG4gIHR5cGUgRGFpbHlNb2RlbFRva2VucyxcbiAgdHlwZSBTdGF0c0RhdGVSYW5nZSxcbn0gZnJvbSAnLi4vdXRpbHMvc3RhdHMuanMnXG5pbXBvcnQgeyByZXNvbHZlVGhlbWVTZXR0aW5nIH0gZnJvbSAnLi4vdXRpbHMvc3lzdGVtVGhlbWUuanMnXG5pbXBvcnQgeyBnZXRUaGVtZSwgdGhlbWVDb2xvclRvQW5zaSB9IGZyb20gJy4uL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHsgUGFuZSB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9QYW5lLmpzJ1xuaW1wb3J0IHsgVGFiLCBUYWJzLCB1c2VUYWJIZWFkZXJGb2N1cyB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9UYWJzLmpzJ1xuaW1wb3J0IHsgU3Bpbm5lciB9IGZyb20gJy4vU3Bpbm5lci5qcydcblxuZnVuY3Rpb24gZm9ybWF0UGVha0RheShkYXRlU3RyOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBkYXRlID0gbmV3IERhdGUoZGF0ZVN0cilcbiAgcmV0dXJuIGRhdGUudG9Mb2NhbGVEYXRlU3RyaW5nKCdlbi1VUycsIHtcbiAgICBtb250aDogJ3Nob3J0JyxcbiAgICBkYXk6ICdudW1lcmljJyxcbiAgfSlcbn1cblxudHlwZSBQcm9wcyA9IHtcbiAgb25DbG9zZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkXG59XG5cbnR5cGUgU3RhdHNSZXN1bHQgPVxuICB8IHsgdHlwZTogJ3N1Y2Nlc3MnOyBkYXRhOiBDbGF1ZGVDb2RlU3RhdHMgfVxuICB8IHsgdHlwZTogJ2Vycm9yJzsgbWVzc2FnZTogc3RyaW5nIH1cbiAgfCB7IHR5cGU6ICdlbXB0eScgfVxuXG5jb25zdCBEQVRFX1JBTkdFX0xBQkVMUzogUmVjb3JkPFN0YXRzRGF0ZVJhbmdlLCBzdHJpbmc+ID0ge1xuICAnN2QnOiAnTGFzdCA3IGRheXMnLFxuICAnMzBkJzogJ0xhc3QgMzAgZGF5cycsXG4gIGFsbDogJ0FsbCB0aW1lJyxcbn1cblxuY29uc3QgREFURV9SQU5HRV9PUkRFUjogU3RhdHNEYXRlUmFuZ2VbXSA9IFsnYWxsJywgJzdkJywgJzMwZCddXG5cbmZ1bmN0aW9uIGdldE5leHREYXRlUmFuZ2UoY3VycmVudDogU3RhdHNEYXRlUmFuZ2UpOiBTdGF0c0RhdGVSYW5nZSB7XG4gIGNvbnN0IGN1cnJlbnRJbmRleCA9IERBVEVfUkFOR0VfT1JERVIuaW5kZXhPZihjdXJyZW50KVxuICByZXR1cm4gREFURV9SQU5HRV9PUkRFUlsoY3VycmVudEluZGV4ICsgMSkgJSBEQVRFX1JBTkdFX09SREVSLmxlbmd0aF0hXG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIHN0YXRzIGxvYWRpbmcgcHJvbWlzZSB0aGF0IG5ldmVyIHJlamVjdHMuXG4gKiBBbHdheXMgbG9hZHMgYWxsLXRpbWUgc3RhdHMgZm9yIHRoZSBoZWF0bWFwLlxuICovXG5mdW5jdGlvbiBjcmVhdGVBbGxUaW1lU3RhdHNQcm9taXNlKCk6IFByb21pc2U8U3RhdHNSZXN1bHQ+IHtcbiAgcmV0dXJuIGFnZ3JlZ2F0ZUNsYXVkZUNvZGVTdGF0c0ZvclJhbmdlKCdhbGwnKVxuICAgIC50aGVuKChkYXRhKTogU3RhdHNSZXN1bHQgPT4ge1xuICAgICAgaWYgKCFkYXRhIHx8IGRhdGEudG90YWxTZXNzaW9ucyA9PT0gMCkge1xuICAgICAgICByZXR1cm4geyB0eXBlOiAnZW1wdHknIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiB7IHR5cGU6ICdzdWNjZXNzJywgZGF0YSB9XG4gICAgfSlcbiAgICAuY2F0Y2goKGVycik6IFN0YXRzUmVzdWx0ID0+IHtcbiAgICAgIGNvbnN0IG1lc3NhZ2UgPVxuICAgICAgICBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogJ0ZhaWxlZCB0byBsb2FkIHN0YXRzJ1xuICAgICAgcmV0dXJuIHsgdHlwZTogJ2Vycm9yJywgbWVzc2FnZSB9XG4gICAgfSlcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFN0YXRzKHsgb25DbG9zZSB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIEFsd2F5cyBsb2FkIGFsbC10aW1lIHN0YXRzIGZpcnN0IChmb3IgaGVhdG1hcClcbiAgY29uc3QgYWxsVGltZVByb21pc2UgPSB1c2VNZW1vKCgpID0+IGNyZWF0ZUFsbFRpbWVTdGF0c1Byb21pc2UoKSwgW10pXG5cbiAgcmV0dXJuIChcbiAgICA8U3VzcGVuc2VcbiAgICAgIGZhbGxiYWNrPXtcbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxTcGlubmVyIC8+XG4gICAgICAgICAgPFRleHQ+IExvYWRpbmcgeW91ciBDbGF1ZGUgQ29kZSBzdGF0c+KApjwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICB9XG4gICAgPlxuICAgICAgPFN0YXRzQ29udGVudCBhbGxUaW1lUHJvbWlzZT17YWxsVGltZVByb21pc2V9IG9uQ2xvc2U9e29uQ2xvc2V9IC8+XG4gICAgPC9TdXNwZW5zZT5cbiAgKVxufVxuXG50eXBlIFN0YXRzQ29udGVudFByb3BzID0ge1xuICBhbGxUaW1lUHJvbWlzZTogUHJvbWlzZTxTdGF0c1Jlc3VsdD5cbiAgb25DbG9zZTogUHJvcHNbJ29uQ2xvc2UnXVxufVxuXG4vKipcbiAqIElubmVyIGNvbXBvbmVudCB0aGF0IHVzZXMgUmVhY3QgMTkncyB1c2UoKSB0byByZWFkIHRoZSBzdGF0cyBwcm9taXNlLlxuICogU3VzcGVuZHMgd2hpbGUgbG9hZGluZyBhbGwtdGltZSBzdGF0cywgdGhlbiBoYW5kbGVzIGRhdGUgcmFuZ2UgY2hhbmdlcyB3aXRob3V0IHN1c3BlbmRpbmcuXG4gKi9cbmZ1bmN0aW9uIFN0YXRzQ29udGVudCh7XG4gIGFsbFRpbWVQcm9taXNlLFxuICBvbkNsb3NlLFxufTogU3RhdHNDb250ZW50UHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBhbGxUaW1lUmVzdWx0ID0gdXNlKGFsbFRpbWVQcm9taXNlKVxuICBjb25zdCBbZGF0ZVJhbmdlLCBzZXREYXRlUmFuZ2VdID0gdXNlU3RhdGU8U3RhdHNEYXRlUmFuZ2U+KCdhbGwnKVxuICBjb25zdCBbc3RhdHNDYWNoZSwgc2V0U3RhdHNDYWNoZV0gPSB1c2VTdGF0ZTxcbiAgICBQYXJ0aWFsPFJlY29yZDxTdGF0c0RhdGVSYW5nZSwgQ2xhdWRlQ29kZVN0YXRzPj5cbiAgPih7fSlcbiAgY29uc3QgW2lzTG9hZGluZ0ZpbHRlcmVkLCBzZXRJc0xvYWRpbmdGaWx0ZXJlZF0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW2FjdGl2ZVRhYiwgc2V0QWN0aXZlVGFiXSA9IHVzZVN0YXRlPCdPdmVydmlldycgfCAnTW9kZWxzJz4oJ092ZXJ2aWV3JylcbiAgY29uc3QgW2NvcHlTdGF0dXMsIHNldENvcHlTdGF0dXNdID0gdXNlU3RhdGU8c3RyaW5nIHwgbnVsbD4obnVsbClcblxuICAvLyBMb2FkIGZpbHRlcmVkIHN0YXRzIHdoZW4gZGF0ZSByYW5nZSBjaGFuZ2VzICh3aXRoIGNhY2hpbmcpXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGRhdGVSYW5nZSA9PT0gJ2FsbCcpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIEFscmVhZHkgY2FjaGVkXG4gICAgaWYgKHN0YXRzQ2FjaGVbZGF0ZVJhbmdlXSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgbGV0IGNhbmNlbGxlZCA9IGZhbHNlXG4gICAgc2V0SXNMb2FkaW5nRmlsdGVyZWQodHJ1ZSlcblxuICAgIGFnZ3JlZ2F0ZUNsYXVkZUNvZGVTdGF0c0ZvclJhbmdlKGRhdGVSYW5nZSlcbiAgICAgIC50aGVuKGRhdGEgPT4ge1xuICAgICAgICBpZiAoIWNhbmNlbGxlZCkge1xuICAgICAgICAgIHNldFN0YXRzQ2FjaGUocHJldiA9PiAoeyAuLi5wcmV2LCBbZGF0ZVJhbmdlXTogZGF0YSB9KSlcbiAgICAgICAgICBzZXRJc0xvYWRpbmdGaWx0ZXJlZChmYWxzZSlcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICAgIC5jYXRjaCgoKSA9PiB7XG4gICAgICAgIGlmICghY2FuY2VsbGVkKSB7XG4gICAgICAgICAgc2V0SXNMb2FkaW5nRmlsdGVyZWQoZmFsc2UpXG4gICAgICAgIH1cbiAgICAgIH0pXG5cbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgY2FuY2VsbGVkID0gdHJ1ZVxuICAgIH1cbiAgfSwgW2RhdGVSYW5nZSwgc3RhdHNDYWNoZV0pXG5cbiAgLy8gVXNlIGNhY2hlZCBzdGF0cyBmb3IgY3VycmVudCByYW5nZVxuICBjb25zdCBkaXNwbGF5U3RhdHMgPVxuICAgIGRhdGVSYW5nZSA9PT0gJ2FsbCdcbiAgICAgID8gYWxsVGltZVJlc3VsdC50eXBlID09PSAnc3VjY2VzcydcbiAgICAgICAgPyBhbGxUaW1lUmVzdWx0LmRhdGFcbiAgICAgICAgOiBudWxsXG4gICAgICA6IChzdGF0c0NhY2hlW2RhdGVSYW5nZV0gPz9cbiAgICAgICAgKGFsbFRpbWVSZXN1bHQudHlwZSA9PT0gJ3N1Y2Nlc3MnID8gYWxsVGltZVJlc3VsdC5kYXRhIDogbnVsbCkpXG5cbiAgLy8gQWxsLXRpbWUgc3RhdHMgZm9yIHRoZSBoZWF0bWFwIChhbHdheXMgdXNlIGFsbC10aW1lKVxuICBjb25zdCBhbGxUaW1lU3RhdHMgPVxuICAgIGFsbFRpbWVSZXN1bHQudHlwZSA9PT0gJ3N1Y2Nlc3MnID8gYWxsVGltZVJlc3VsdC5kYXRhIDogbnVsbFxuXG4gIGNvbnN0IGhhbmRsZUNsb3NlID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIG9uQ2xvc2UoJ1N0YXRzIGRpYWxvZyBkaXNtaXNzZWQnLCB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0pXG4gIH0sIFtvbkNsb3NlXSlcblxuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOm5vJywgaGFuZGxlQ2xvc2UsIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicgfSlcblxuICB1c2VJbnB1dCgoaW5wdXQsIGtleSkgPT4ge1xuICAgIC8vIEhhbmRsZSBjdHJsK2MgYW5kIGN0cmwrZCBmb3IgY2xvc2luZ1xuICAgIGlmIChrZXkuY3RybCAmJiAoaW5wdXQgPT09ICdjJyB8fCBpbnB1dCA9PT0gJ2QnKSkge1xuICAgICAgb25DbG9zZSgnU3RhdHMgZGlhbG9nIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgICB9XG4gICAgLy8gVHJhY2sgdGFiIGNoYW5nZXNcbiAgICBpZiAoa2V5LnRhYikge1xuICAgICAgc2V0QWN0aXZlVGFiKHByZXYgPT4gKHByZXYgPT09ICdPdmVydmlldycgPyAnTW9kZWxzJyA6ICdPdmVydmlldycpKVxuICAgIH1cbiAgICAvLyByIHRvIGN5Y2xlIGRhdGUgcmFuZ2VcbiAgICBpZiAoaW5wdXQgPT09ICdyJyAmJiAha2V5LmN0cmwgJiYgIWtleS5tZXRhKSB7XG4gICAgICBzZXREYXRlUmFuZ2UoZ2V0TmV4dERhdGVSYW5nZShkYXRlUmFuZ2UpKVxuICAgIH1cbiAgICAvLyBDdHJsK1MgdG8gY29weSBzY3JlZW5zaG90IHRvIGNsaXBib2FyZFxuICAgIGlmIChrZXkuY3RybCAmJiBpbnB1dCA9PT0gJ3MnICYmIGRpc3BsYXlTdGF0cykge1xuICAgICAgdm9pZCBoYW5kbGVTY3JlZW5zaG90KGRpc3BsYXlTdGF0cywgYWN0aXZlVGFiLCBzZXRDb3B5U3RhdHVzKVxuICAgIH1cbiAgfSlcblxuICBpZiAoYWxsVGltZVJlc3VsdC50eXBlID09PSAnZXJyb3InKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPkZhaWxlZCB0byBsb2FkIHN0YXRzOiB7YWxsVGltZVJlc3VsdC5tZXNzYWdlfTwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGlmIChhbGxUaW1lUmVzdWx0LnR5cGUgPT09ICdlbXB0eScpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj5cbiAgICAgICAgICBObyBzdGF0cyBhdmFpbGFibGUgeWV0LiBTdGFydCB1c2luZyBDbGF1ZGUgQ29kZSFcbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgaWYgKCFkaXNwbGF5U3RhdHMgfHwgIWFsbFRpbWVTdGF0cykge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgIDxTcGlubmVyIC8+XG4gICAgICAgIDxUZXh0PiBMb2FkaW5nIHN0YXRz4oCmPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8UGFuZSBjb2xvcj1cImNsYXVkZVwiPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXsxfSBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICA8VGFicyB0aXRsZT1cIlwiIGNvbG9yPVwiY2xhdWRlXCIgZGVmYXVsdFRhYj1cIk92ZXJ2aWV3XCI+XG4gICAgICAgICAgPFRhYiB0aXRsZT1cIk92ZXJ2aWV3XCI+XG4gICAgICAgICAgICA8T3ZlcnZpZXdUYWJcbiAgICAgICAgICAgICAgc3RhdHM9e2Rpc3BsYXlTdGF0c31cbiAgICAgICAgICAgICAgYWxsVGltZVN0YXRzPXthbGxUaW1lU3RhdHN9XG4gICAgICAgICAgICAgIGRhdGVSYW5nZT17ZGF0ZVJhbmdlfVxuICAgICAgICAgICAgICBpc0xvYWRpbmc9e2lzTG9hZGluZ0ZpbHRlcmVkfVxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L1RhYj5cbiAgICAgICAgICA8VGFiIHRpdGxlPVwiTW9kZWxzXCI+XG4gICAgICAgICAgICA8TW9kZWxzVGFiXG4gICAgICAgICAgICAgIHN0YXRzPXtkaXNwbGF5U3RhdHN9XG4gICAgICAgICAgICAgIGRhdGVSYW5nZT17ZGF0ZVJhbmdlfVxuICAgICAgICAgICAgICBpc0xvYWRpbmc9e2lzTG9hZGluZ0ZpbHRlcmVkfVxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L1RhYj5cbiAgICAgICAgPC9UYWJzPlxuICAgICAgPC9Cb3g+XG4gICAgICA8Qm94IHBhZGRpbmdMZWZ0PXsyfT5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgRXNjIHRvIGNhbmNlbCDCtyByIHRvIGN5Y2xlIGRhdGVzIMK3IGN0cmwrcyB0byBjb3B5XG4gICAgICAgICAge2NvcHlTdGF0dXMgPyBgIMK3ICR7Y29weVN0YXR1c31gIDogJyd9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvUGFuZT5cbiAgKVxufVxuXG5mdW5jdGlvbiBEYXRlUmFuZ2VTZWxlY3Rvcih7XG4gIGRhdGVSYW5nZSxcbiAgaXNMb2FkaW5nLFxufToge1xuICBkYXRlUmFuZ2U6IFN0YXRzRGF0ZVJhbmdlXG4gIGlzTG9hZGluZzogYm9vbGVhblxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPEJveCBtYXJnaW5Cb3R0b209ezF9IGdhcD17MX0+XG4gICAgICA8Qm94PlxuICAgICAgICB7REFURV9SQU5HRV9PUkRFUi5tYXAoKHJhbmdlLCBpKSA9PiAoXG4gICAgICAgICAgPFRleHQga2V5PXtyYW5nZX0+XG4gICAgICAgICAgICB7aSA+IDAgJiYgPFRleHQgZGltQ29sb3I+IMK3IDwvVGV4dD59XG4gICAgICAgICAgICB7cmFuZ2UgPT09IGRhdGVSYW5nZSA/IChcbiAgICAgICAgICAgICAgPFRleHQgYm9sZCBjb2xvcj1cImNsYXVkZVwiPlxuICAgICAgICAgICAgICAgIHtEQVRFX1JBTkdFX0xBQkVMU1tyYW5nZV19XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntEQVRFX1JBTkdFX0xBQkVMU1tyYW5nZV19PC9UZXh0PlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICkpfVxuICAgICAgPC9Cb3g+XG4gICAgICB7aXNMb2FkaW5nICYmIDxTcGlubmVyIC8+fVxuICAgIDwvQm94PlxuICApXG59XG5cbmZ1bmN0aW9uIE92ZXJ2aWV3VGFiKHtcbiAgc3RhdHMsXG4gIGFsbFRpbWVTdGF0cyxcbiAgZGF0ZVJhbmdlLFxuICBpc0xvYWRpbmcsXG59OiB7XG4gIHN0YXRzOiBDbGF1ZGVDb2RlU3RhdHNcbiAgYWxsVGltZVN0YXRzOiBDbGF1ZGVDb2RlU3RhdHNcbiAgZGF0ZVJhbmdlOiBTdGF0c0RhdGVSYW5nZVxuICBpc0xvYWRpbmc6IGJvb2xlYW5cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB7IGNvbHVtbnM6IHRlcm1pbmFsV2lkdGggfSA9IHVzZVRlcm1pbmFsU2l6ZSgpXG5cbiAgLy8gQ2FsY3VsYXRlIGZhdm9yaXRlIG1vZGVsIGFuZCB0b3RhbCB0b2tlbnNcbiAgY29uc3QgbW9kZWxFbnRyaWVzID0gT2JqZWN0LmVudHJpZXMoc3RhdHMubW9kZWxVc2FnZSkuc29ydChcbiAgICAoWywgYV0sIFssIGJdKSA9PlxuICAgICAgYi5pbnB1dFRva2VucyArIGIub3V0cHV0VG9rZW5zIC0gKGEuaW5wdXRUb2tlbnMgKyBhLm91dHB1dFRva2VucyksXG4gIClcbiAgY29uc3QgZmF2b3JpdGVNb2RlbCA9IG1vZGVsRW50cmllc1swXVxuICBjb25zdCB0b3RhbFRva2VucyA9IG1vZGVsRW50cmllcy5yZWR1Y2UoXG4gICAgKHN1bSwgWywgdXNhZ2VdKSA9PiBzdW0gKyB1c2FnZS5pbnB1dFRva2VucyArIHVzYWdlLm91dHB1dFRva2VucyxcbiAgICAwLFxuICApXG5cbiAgLy8gTWVtb2l6ZSB0aGUgZmFjdG9pZCBzbyBpdCBkb2Vzbid0IGNoYW5nZSB3aGVuIHN3aXRjaGluZyB0YWJzXG4gIGNvbnN0IGZhY3RvaWQgPSB1c2VNZW1vKFxuICAgICgpID0+IGdlbmVyYXRlRnVuRmFjdG9pZChzdGF0cywgdG90YWxUb2tlbnMpLFxuICAgIFtzdGF0cywgdG90YWxUb2tlbnNdLFxuICApXG5cbiAgLy8gQ2FsY3VsYXRlIHJhbmdlIGRheXMgYmFzZWQgb24gc2VsZWN0ZWQgZGF0ZSByYW5nZVxuICBjb25zdCByYW5nZURheXMgPVxuICAgIGRhdGVSYW5nZSA9PT0gJzdkJyA/IDcgOiBkYXRlUmFuZ2UgPT09ICczMGQnID8gMzAgOiBzdGF0cy50b3RhbERheXNcblxuICAvLyBDb21wdXRlIHNob3Qgc3RhdHMgZGF0YSAoYW50LW9ubHksIGdhdGVkIGJ5IGZlYXR1cmUgZmxhZylcbiAgbGV0IHNob3RTdGF0c0RhdGE6IHtcbiAgICBhdmdTaG90czogc3RyaW5nXG4gICAgYnVja2V0czogeyBsYWJlbDogc3RyaW5nOyBjb3VudDogbnVtYmVyOyBwY3Q6IG51bWJlciB9W11cbiAgfSB8IG51bGwgPSBudWxsXG4gIGlmIChmZWF0dXJlKCdTSE9UX1NUQVRTJykgJiYgc3RhdHMuc2hvdERpc3RyaWJ1dGlvbikge1xuICAgIGNvbnN0IGRpc3QgPSBzdGF0cy5zaG90RGlzdHJpYnV0aW9uXG4gICAgY29uc3QgdG90YWwgPSBPYmplY3QudmFsdWVzKGRpc3QpLnJlZHVjZSgocywgbikgPT4gcyArIG4sIDApXG4gICAgaWYgKHRvdGFsID4gMCkge1xuICAgICAgY29uc3QgdG90YWxTaG90cyA9IE9iamVjdC5lbnRyaWVzKGRpc3QpLnJlZHVjZShcbiAgICAgICAgKHMsIFtjb3VudCwgc2Vzc2lvbnNdKSA9PiBzICsgcGFyc2VJbnQoY291bnQsIDEwKSAqIHNlc3Npb25zLFxuICAgICAgICAwLFxuICAgICAgKVxuICAgICAgY29uc3QgYnVja2V0ID0gKG1pbjogbnVtYmVyLCBtYXg/OiBudW1iZXIpID0+XG4gICAgICAgIE9iamVjdC5lbnRyaWVzKGRpc3QpXG4gICAgICAgICAgLmZpbHRlcigoW2tdKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBuID0gcGFyc2VJbnQoaywgMTApXG4gICAgICAgICAgICByZXR1cm4gbiA+PSBtaW4gJiYgKG1heCA9PT0gdW5kZWZpbmVkIHx8IG4gPD0gbWF4KVxuICAgICAgICAgIH0pXG4gICAgICAgICAgLnJlZHVjZSgocywgWywgdl0pID0+IHMgKyB2LCAwKVxuICAgICAgY29uc3QgcGN0ID0gKG46IG51bWJlcikgPT4gTWF0aC5yb3VuZCgobiAvIHRvdGFsKSAqIDEwMClcbiAgICAgIGNvbnN0IGIxID0gYnVja2V0KDEsIDEpXG4gICAgICBjb25zdCBiMl81ID0gYnVja2V0KDIsIDUpXG4gICAgICBjb25zdCBiNl8xMCA9IGJ1Y2tldCg2LCAxMClcbiAgICAgIGNvbnN0IGIxMSA9IGJ1Y2tldCgxMSlcbiAgICAgIHNob3RTdGF0c0RhdGEgPSB7XG4gICAgICAgIGF2Z1Nob3RzOiAodG90YWxTaG90cyAvIHRvdGFsKS50b0ZpeGVkKDEpLFxuICAgICAgICBidWNrZXRzOiBbXG4gICAgICAgICAgeyBsYWJlbDogJzEtc2hvdCcsIGNvdW50OiBiMSwgcGN0OiBwY3QoYjEpIH0sXG4gICAgICAgICAgeyBsYWJlbDogJzJcXHUyMDEzNSBzaG90JywgY291bnQ6IGIyXzUsIHBjdDogcGN0KGIyXzUpIH0sXG4gICAgICAgICAgeyBsYWJlbDogJzZcXHUyMDEzMTAgc2hvdCcsIGNvdW50OiBiNl8xMCwgcGN0OiBwY3QoYjZfMTApIH0sXG4gICAgICAgICAgeyBsYWJlbDogJzExKyBzaG90JywgY291bnQ6IGIxMSwgcGN0OiBwY3QoYjExKSB9LFxuICAgICAgICBdLFxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgIHsvKiBBY3Rpdml0eSBIZWF0bWFwIC0gYWx3YXlzIHNob3dzIGFsbC10aW1lIGRhdGEgKi99XG4gICAgICB7YWxsVGltZVN0YXRzLmRhaWx5QWN0aXZpdHkubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgPEFuc2k+XG4gICAgICAgICAgICB7Z2VuZXJhdGVIZWF0bWFwKGFsbFRpbWVTdGF0cy5kYWlseUFjdGl2aXR5LCB7IHRlcm1pbmFsV2lkdGggfSl9XG4gICAgICAgICAgPC9BbnNpPlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG5cbiAgICAgIHsvKiBEYXRlIHJhbmdlIHNlbGVjdG9yICovfVxuICAgICAgPERhdGVSYW5nZVNlbGVjdG9yIGRhdGVSYW5nZT17ZGF0ZVJhbmdlfSBpc0xvYWRpbmc9e2lzTG9hZGluZ30gLz5cblxuICAgICAgey8qIFNlY3Rpb24gMTogVXNhZ2UgKi99XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezR9IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHdpZHRoPXsyOH0+XG4gICAgICAgICAge2Zhdm9yaXRlTW9kZWwgJiYgKFxuICAgICAgICAgICAgPFRleHQgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICAgIEZhdm9yaXRlIG1vZGVsOnsnICd9XG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCIgYm9sZD5cbiAgICAgICAgICAgICAgICB7cmVuZGVyTW9kZWxOYW1lKGZhdm9yaXRlTW9kZWxbMF0pfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHdpZHRoPXsyOH0+XG4gICAgICAgICAgPFRleHQgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICBUb3RhbCB0b2tlbnM6eycgJ31cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+e2Zvcm1hdE51bWJlcih0b3RhbFRva2Vucyl9PC9UZXh0PlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cblxuICAgICAgey8qIFNlY3Rpb24gMjogQWN0aXZpdHkgLSBSb3cgMTogU2Vzc2lvbnMgfCBMb25nZXN0IHNlc3Npb24gKi99XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezR9PlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiB3aWR0aD17Mjh9PlxuICAgICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAgU2Vzc2lvbnM6eycgJ31cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+e2Zvcm1hdE51bWJlcihzdGF0cy50b3RhbFNlc3Npb25zKX08L1RleHQ+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9ezI4fT5cbiAgICAgICAgICB7c3RhdHMubG9uZ2VzdFNlc3Npb24gJiYgKFxuICAgICAgICAgICAgPFRleHQgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICAgIExvbmdlc3Qgc2Vzc2lvbjp7JyAnfVxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cImNsYXVkZVwiPlxuICAgICAgICAgICAgICAgIHtmb3JtYXREdXJhdGlvbihzdGF0cy5sb25nZXN0U2Vzc2lvbi5kdXJhdGlvbil9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuXG4gICAgICB7LyogUm93IDI6IEFjdGl2ZSBkYXlzIHwgTG9uZ2VzdCBzdHJlYWsgKi99XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezR9PlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiB3aWR0aD17Mjh9PlxuICAgICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAgQWN0aXZlIGRheXM6IDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+e3N0YXRzLmFjdGl2ZURheXN9PC9UZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWJ0bGVcIj4ve3JhbmdlRGF5c308L1RleHQ+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9ezI4fT5cbiAgICAgICAgICA8VGV4dCB3cmFwPVwidHJ1bmNhdGVcIj5cbiAgICAgICAgICAgIExvbmdlc3Qgc3RyZWFrOnsnICd9XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImNsYXVkZVwiIGJvbGQ+XG4gICAgICAgICAgICAgIHtzdGF0cy5zdHJlYWtzLmxvbmdlc3RTdHJlYWt9XG4gICAgICAgICAgICA8L1RleHQ+eycgJ31cbiAgICAgICAgICAgIHtzdGF0cy5zdHJlYWtzLmxvbmdlc3RTdHJlYWsgPT09IDEgPyAnZGF5JyA6ICdkYXlzJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG5cbiAgICAgIHsvKiBSb3cgMzogTW9zdCBhY3RpdmUgZGF5IHwgQ3VycmVudCBzdHJlYWsgKi99XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezR9PlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiB3aWR0aD17Mjh9PlxuICAgICAgICAgIHtzdGF0cy5wZWFrQWN0aXZpdHlEYXkgJiYgKFxuICAgICAgICAgICAgPFRleHQgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICAgIE1vc3QgYWN0aXZlIGRheTp7JyAnfVxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cImNsYXVkZVwiPntmb3JtYXRQZWFrRGF5KHN0YXRzLnBlYWtBY3Rpdml0eURheSl9PC9UZXh0PlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICl9XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiB3aWR0aD17Mjh9PlxuICAgICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAgQ3VycmVudCBzdHJlYWs6eycgJ31cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCIgYm9sZD5cbiAgICAgICAgICAgICAge2FsbFRpbWVTdGF0cy5zdHJlYWtzLmN1cnJlbnRTdHJlYWt9XG4gICAgICAgICAgICA8L1RleHQ+eycgJ31cbiAgICAgICAgICAgIHthbGxUaW1lU3RhdHMuc3RyZWFrcy5jdXJyZW50U3RyZWFrID09PSAxID8gJ2RheScgOiAnZGF5cyd9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuXG4gICAgICB7LyogU3BlY3VsYXRpb24gdGltZSBzYXZlZCAoYW50LW9ubHkpICovfVxuICAgICAge1wiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiZcbiAgICAgICAgc3RhdHMudG90YWxTcGVjdWxhdGlvblRpbWVTYXZlZE1zID4gMCAmJiAoXG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXs0fT5cbiAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHdpZHRoPXsyOH0+XG4gICAgICAgICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAgICAgIFNwZWN1bGF0aW9uIHNhdmVkOnsnICd9XG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJjbGF1ZGVcIj5cbiAgICAgICAgICAgICAgICAgIHtmb3JtYXREdXJhdGlvbihzdGF0cy50b3RhbFNwZWN1bGF0aW9uVGltZVNhdmVkTXMpfVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG5cbiAgICAgIHsvKiBTaG90IHN0YXRzIChhbnQtb25seSkgKi99XG4gICAgICB7c2hvdFN0YXRzRGF0YSAmJiAoXG4gICAgICAgIDw+XG4gICAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgPFRleHQ+U2hvdCBkaXN0cmlidXRpb248L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXs0fT5cbiAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHdpZHRoPXsyOH0+XG4gICAgICAgICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAgICAgIHtzaG90U3RhdHNEYXRhLmJ1Y2tldHNbMF0hLmxhYmVsfTp7JyAnfVxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+e3Nob3RTdGF0c0RhdGEuYnVja2V0c1swXSEuY291bnR9PC9UZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCI+ICh7c2hvdFN0YXRzRGF0YS5idWNrZXRzWzBdIS5wY3R9JSk8L1RleHQ+XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9ezI4fT5cbiAgICAgICAgICAgICAgPFRleHQgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICAgICAge3Nob3RTdGF0c0RhdGEuYnVja2V0c1sxXSEubGFiZWx9OnsnICd9XG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJjbGF1ZGVcIj57c2hvdFN0YXRzRGF0YS5idWNrZXRzWzFdIS5jb3VudH08L1RleHQ+XG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWJ0bGVcIj4gKHtzaG90U3RhdHNEYXRhLmJ1Y2tldHNbMV0hLnBjdH0lKTwvVGV4dD5cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXs0fT5cbiAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHdpZHRoPXsyOH0+XG4gICAgICAgICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAgICAgIHtzaG90U3RhdHNEYXRhLmJ1Y2tldHNbMl0hLmxhYmVsfTp7JyAnfVxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+e3Nob3RTdGF0c0RhdGEuYnVja2V0c1syXSEuY291bnR9PC9UZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCI+ICh7c2hvdFN0YXRzRGF0YS5idWNrZXRzWzJdIS5wY3R9JSk8L1RleHQ+XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9ezI4fT5cbiAgICAgICAgICAgICAgPFRleHQgd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgICAgICAge3Nob3RTdGF0c0RhdGEuYnVja2V0c1szXSEubGFiZWx9OnsnICd9XG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJjbGF1ZGVcIj57c2hvdFN0YXRzRGF0YS5idWNrZXRzWzNdIS5jb3VudH08L1RleHQ+XG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWJ0bGVcIj4gKHtzaG90U3RhdHNEYXRhLmJ1Y2tldHNbM10hLnBjdH0lKTwvVGV4dD5cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXs0fT5cbiAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHdpZHRoPXsyOH0+XG4gICAgICAgICAgICAgIDxUZXh0IHdyYXA9XCJ0cnVuY2F0ZVwiPlxuICAgICAgICAgICAgICAgIEF2Zy9zZXNzaW9uOnsnICd9XG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJjbGF1ZGVcIj57c2hvdFN0YXRzRGF0YS5hdmdTaG90c308L1RleHQ+XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8Lz5cbiAgICAgICl9XG5cbiAgICAgIHsvKiBGdW4gZmFjdG9pZCAqL31cbiAgICAgIHtmYWN0b2lkICYmIChcbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPntmYWN0b2lkfTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuICAgIDwvQm94PlxuICApXG59XG5cbi8vIEZhbW91cyBib29rcyBhbmQgdGhlaXIgYXBwcm94aW1hdGUgdG9rZW4gY291bnRzICh3b3JkcyAqIH4xLjMpXG4vLyBTb3J0ZWQgYnkgdG9rZW5zIGFzY2VuZGluZyBmb3IgY29tcGFyaXNvbiBsb2dpY1xuY29uc3QgQk9PS19DT01QQVJJU09OUyA9IFtcbiAgeyBuYW1lOiAnVGhlIExpdHRsZSBQcmluY2UnLCB0b2tlbnM6IDIyMDAwIH0sXG4gIHsgbmFtZTogJ1RoZSBPbGQgTWFuIGFuZCB0aGUgU2VhJywgdG9rZW5zOiAzNTAwMCB9LFxuICB7IG5hbWU6ICdBIENocmlzdG1hcyBDYXJvbCcsIHRva2VuczogMzcwMDAgfSxcbiAgeyBuYW1lOiAnQW5pbWFsIEZhcm0nLCB0b2tlbnM6IDM5MDAwIH0sXG4gIHsgbmFtZTogJ0ZhaHJlbmhlaXQgNDUxJywgdG9rZW5zOiA2MDAwMCB9LFxuICB7IG5hbWU6ICdUaGUgR3JlYXQgR2F0c2J5JywgdG9rZW5zOiA2MjAwMCB9LFxuICB7IG5hbWU6ICdTbGF1Z2h0ZXJob3VzZS1GaXZlJywgdG9rZW5zOiA2NDAwMCB9LFxuICB7IG5hbWU6ICdCcmF2ZSBOZXcgV29ybGQnLCB0b2tlbnM6IDgzMDAwIH0sXG4gIHsgbmFtZTogJ1RoZSBDYXRjaGVyIGluIHRoZSBSeWUnLCB0b2tlbnM6IDk1MDAwIH0sXG4gIHsgbmFtZTogXCJIYXJyeSBQb3R0ZXIgYW5kIHRoZSBQaGlsb3NvcGhlcidzIFN0b25lXCIsIHRva2VuczogMTAzMDAwIH0sXG4gIHsgbmFtZTogJ1RoZSBIb2JiaXQnLCB0b2tlbnM6IDEyMzAwMCB9LFxuICB7IG5hbWU6ICcxOTg0JywgdG9rZW5zOiAxMjMwMDAgfSxcbiAgeyBuYW1lOiAnVG8gS2lsbCBhIE1vY2tpbmdiaXJkJywgdG9rZW5zOiAxMzAwMDAgfSxcbiAgeyBuYW1lOiAnUHJpZGUgYW5kIFByZWp1ZGljZScsIHRva2VuczogMTU2MDAwIH0sXG4gIHsgbmFtZTogJ0R1bmUnLCB0b2tlbnM6IDI0NDAwMCB9LFxuICB7IG5hbWU6ICdNb2J5LURpY2snLCB0b2tlbnM6IDI2ODAwMCB9LFxuICB7IG5hbWU6ICdDcmltZSBhbmQgUHVuaXNobWVudCcsIHRva2VuczogMjc0MDAwIH0sXG4gIHsgbmFtZTogJ0EgR2FtZSBvZiBUaHJvbmVzJywgdG9rZW5zOiAzODEwMDAgfSxcbiAgeyBuYW1lOiAnQW5uYSBLYXJlbmluYScsIHRva2VuczogNDY4MDAwIH0sXG4gIHsgbmFtZTogJ0RvbiBRdWl4b3RlJywgdG9rZW5zOiA1MjAwMDAgfSxcbiAgeyBuYW1lOiAnVGhlIExvcmQgb2YgdGhlIFJpbmdzJywgdG9rZW5zOiA1NzYwMDAgfSxcbiAgeyBuYW1lOiAnVGhlIENvdW50IG9mIE1vbnRlIENyaXN0bycsIHRva2VuczogNjAzMDAwIH0sXG4gIHsgbmFtZTogJ0xlcyBNaXPDqXJhYmxlcycsIHRva2VuczogNjg5MDAwIH0sXG4gIHsgbmFtZTogJ1dhciBhbmQgUGVhY2UnLCB0b2tlbnM6IDczMDAwMCB9LFxuXVxuXG4vLyBUaW1lIGVxdWl2YWxlbnRzIGZvciBzZXNzaW9uIGR1cmF0aW9uc1xuY29uc3QgVElNRV9DT01QQVJJU09OUyA9IFtcbiAgeyBuYW1lOiAnYSBURUQgdGFsaycsIG1pbnV0ZXM6IDE4IH0sXG4gIHsgbmFtZTogJ2FuIGVwaXNvZGUgb2YgVGhlIE9mZmljZScsIG1pbnV0ZXM6IDIyIH0sXG4gIHsgbmFtZTogJ2xpc3RlbmluZyB0byBBYmJleSBSb2FkJywgbWludXRlczogNDcgfSxcbiAgeyBuYW1lOiAnYSB5b2dhIGNsYXNzJywgbWludXRlczogNjAgfSxcbiAgeyBuYW1lOiAnYSBXb3JsZCBDdXAgc29jY2VyIG1hdGNoJywgbWludXRlczogOTAgfSxcbiAgeyBuYW1lOiAnYSBoYWxmIG1hcmF0aG9uIChhdmVyYWdlIHRpbWUpJywgbWludXRlczogMTIwIH0sXG4gIHsgbmFtZTogJ3RoZSBtb3ZpZSBJbmNlcHRpb24nLCBtaW51dGVzOiAxNDggfSxcbiAgeyBuYW1lOiAnd2F0Y2hpbmcgVGl0YW5pYycsIG1pbnV0ZXM6IDE5NSB9LFxuICB7IG5hbWU6ICdhIHRyYW5zYXRsYW50aWMgZmxpZ2h0JywgbWludXRlczogNDIwIH0sXG4gIHsgbmFtZTogJ2EgZnVsbCBuaWdodCBvZiBzbGVlcCcsIG1pbnV0ZXM6IDQ4MCB9LFxuXVxuXG5mdW5jdGlvbiBnZW5lcmF0ZUZ1bkZhY3RvaWQoXG4gIHN0YXRzOiBDbGF1ZGVDb2RlU3RhdHMsXG4gIHRvdGFsVG9rZW5zOiBudW1iZXIsXG4pOiBzdHJpbmcge1xuICBjb25zdCBmYWN0b2lkczogc3RyaW5nW10gPSBbXVxuXG4gIGlmICh0b3RhbFRva2VucyA+IDApIHtcbiAgICBjb25zdCBtYXRjaGluZ0Jvb2tzID0gQk9PS19DT01QQVJJU09OUy5maWx0ZXIoXG4gICAgICBib29rID0+IHRvdGFsVG9rZW5zID49IGJvb2sudG9rZW5zLFxuICAgIClcblxuICAgIGZvciAoY29uc3QgYm9vayBvZiBtYXRjaGluZ0Jvb2tzKSB7XG4gICAgICBjb25zdCB0aW1lcyA9IHRvdGFsVG9rZW5zIC8gYm9vay50b2tlbnNcbiAgICAgIGlmICh0aW1lcyA+PSAyKSB7XG4gICAgICAgIGZhY3RvaWRzLnB1c2goXG4gICAgICAgICAgYFlvdSd2ZSB1c2VkIH4ke01hdGguZmxvb3IodGltZXMpfXggbW9yZSB0b2tlbnMgdGhhbiAke2Jvb2submFtZX1gLFxuICAgICAgICApXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBmYWN0b2lkcy5wdXNoKGBZb3UndmUgdXNlZCB0aGUgc2FtZSBudW1iZXIgb2YgdG9rZW5zIGFzICR7Ym9vay5uYW1lfWApXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgaWYgKHN0YXRzLmxvbmdlc3RTZXNzaW9uKSB7XG4gICAgY29uc3Qgc2Vzc2lvbk1pbnV0ZXMgPSBzdGF0cy5sb25nZXN0U2Vzc2lvbi5kdXJhdGlvbiAvICgxMDAwICogNjApXG4gICAgZm9yIChjb25zdCBjb21wYXJpc29uIG9mIFRJTUVfQ09NUEFSSVNPTlMpIHtcbiAgICAgIGNvbnN0IHJhdGlvID0gc2Vzc2lvbk1pbnV0ZXMgLyBjb21wYXJpc29uLm1pbnV0ZXNcbiAgICAgIGlmIChyYXRpbyA+PSAyKSB7XG4gICAgICAgIGZhY3RvaWRzLnB1c2goXG4gICAgICAgICAgYFlvdXIgbG9uZ2VzdCBzZXNzaW9uIGlzIH4ke01hdGguZmxvb3IocmF0aW8pfXggbG9uZ2VyIHRoYW4gJHtjb21wYXJpc29uLm5hbWV9YCxcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGlmIChmYWN0b2lkcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gJydcbiAgfVxuICBjb25zdCByYW5kb21JbmRleCA9IE1hdGguZmxvb3IoTWF0aC5yYW5kb20oKSAqIGZhY3RvaWRzLmxlbmd0aClcbiAgcmV0dXJuIGZhY3RvaWRzW3JhbmRvbUluZGV4XSFcbn1cblxuZnVuY3Rpb24gTW9kZWxzVGFiKHtcbiAgc3RhdHMsXG4gIGRhdGVSYW5nZSxcbiAgaXNMb2FkaW5nLFxufToge1xuICBzdGF0czogQ2xhdWRlQ29kZVN0YXRzXG4gIGRhdGVSYW5nZTogU3RhdHNEYXRlUmFuZ2VcbiAgaXNMb2FkaW5nOiBib29sZWFuXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBoZWFkZXJGb2N1c2VkLCBmb2N1c0hlYWRlciB9ID0gdXNlVGFiSGVhZGVyRm9jdXMoKVxuICBjb25zdCBbc2Nyb2xsT2Zmc2V0LCBzZXRTY3JvbGxPZmZzZXRdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgeyBjb2x1bW5zOiB0ZXJtaW5hbFdpZHRoIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCBWSVNJQkxFX01PREVMUyA9IDQgLy8gU2hvdyA0IG1vZGVscyBhdCBhIHRpbWUgKDIgcGVyIGNvbHVtbilcblxuICBjb25zdCBtb2RlbEVudHJpZXMgPSBPYmplY3QuZW50cmllcyhzdGF0cy5tb2RlbFVzYWdlKS5zb3J0KFxuICAgIChbLCBhXSwgWywgYl0pID0+XG4gICAgICBiLmlucHV0VG9rZW5zICsgYi5vdXRwdXRUb2tlbnMgLSAoYS5pbnB1dFRva2VucyArIGEub3V0cHV0VG9rZW5zKSxcbiAgKVxuXG4gIC8vIEhhbmRsZSBzY3JvbGxpbmcgd2l0aCBhcnJvdyBrZXlzXG4gIHVzZUlucHV0KFxuICAgIChfaW5wdXQsIGtleSkgPT4ge1xuICAgICAgaWYgKFxuICAgICAgICBrZXkuZG93bkFycm93ICYmXG4gICAgICAgIHNjcm9sbE9mZnNldCA8IG1vZGVsRW50cmllcy5sZW5ndGggLSBWSVNJQkxFX01PREVMU1xuICAgICAgKSB7XG4gICAgICAgIHNldFNjcm9sbE9mZnNldChwcmV2ID0+XG4gICAgICAgICAgTWF0aC5taW4ocHJldiArIDIsIG1vZGVsRW50cmllcy5sZW5ndGggLSBWSVNJQkxFX01PREVMUyksXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGlmIChrZXkudXBBcnJvdykge1xuICAgICAgICBpZiAoc2Nyb2xsT2Zmc2V0ID4gMCkge1xuICAgICAgICAgIHNldFNjcm9sbE9mZnNldChwcmV2ID0+IE1hdGgubWF4KHByZXYgLSAyLCAwKSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBmb2N1c0hlYWRlcigpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuICAgIHsgaXNBY3RpdmU6ICFoZWFkZXJGb2N1c2VkIH0sXG4gIClcblxuICBpZiAobW9kZWxFbnRyaWVzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94PlxuICAgICAgICA8VGV4dCBjb2xvcj1cInN1YnRsZVwiPk5vIG1vZGVsIHVzYWdlIGRhdGEgYXZhaWxhYmxlPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgY29uc3QgdG90YWxUb2tlbnMgPSBtb2RlbEVudHJpZXMucmVkdWNlKFxuICAgIChzdW0sIFssIHVzYWdlXSkgPT4gc3VtICsgdXNhZ2UuaW5wdXRUb2tlbnMgKyB1c2FnZS5vdXRwdXRUb2tlbnMsXG4gICAgMCxcbiAgKVxuXG4gIC8vIEdlbmVyYXRlIHRva2VuIHVzYWdlIGNoYXJ0IC0gdXNlIHRlcm1pbmFsIHdpZHRoIGZvciByZXNwb25zaXZlIHNpemluZ1xuICBjb25zdCBjaGFydE91dHB1dCA9IGdlbmVyYXRlVG9rZW5DaGFydChcbiAgICBzdGF0cy5kYWlseU1vZGVsVG9rZW5zLFxuICAgIG1vZGVsRW50cmllcy5tYXAoKFttb2RlbF0pID0+IG1vZGVsKSxcbiAgICB0ZXJtaW5hbFdpZHRoLFxuICApXG5cbiAgLy8gR2V0IHZpc2libGUgbW9kZWxzIGFuZCBzcGxpdCBpbnRvIHR3byBjb2x1bW5zXG4gIGNvbnN0IHZpc2libGVNb2RlbHMgPSBtb2RlbEVudHJpZXMuc2xpY2UoXG4gICAgc2Nyb2xsT2Zmc2V0LFxuICAgIHNjcm9sbE9mZnNldCArIFZJU0lCTEVfTU9ERUxTLFxuICApXG4gIGNvbnN0IG1pZHBvaW50ID0gTWF0aC5jZWlsKHZpc2libGVNb2RlbHMubGVuZ3RoIC8gMilcbiAgY29uc3QgbGVmdE1vZGVscyA9IHZpc2libGVNb2RlbHMuc2xpY2UoMCwgbWlkcG9pbnQpXG4gIGNvbnN0IHJpZ2h0TW9kZWxzID0gdmlzaWJsZU1vZGVscy5zbGljZShtaWRwb2ludClcblxuICBjb25zdCBjYW5TY3JvbGxVcCA9IHNjcm9sbE9mZnNldCA+IDBcbiAgY29uc3QgY2FuU2Nyb2xsRG93biA9IHNjcm9sbE9mZnNldCA8IG1vZGVsRW50cmllcy5sZW5ndGggLSBWSVNJQkxFX01PREVMU1xuICBjb25zdCBzaG93U2Nyb2xsSGludCA9IG1vZGVsRW50cmllcy5sZW5ndGggPiBWSVNJQkxFX01PREVMU1xuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgIHsvKiBUb2tlbiB1c2FnZSBjaGFydCAqL31cbiAgICAgIHtjaGFydE91dHB1dCAmJiAoXG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgPFRleHQgYm9sZD5Ub2tlbnMgcGVyIERheTwvVGV4dD5cbiAgICAgICAgICA8QW5zaT57Y2hhcnRPdXRwdXQuY2hhcnR9PC9BbnNpPlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCI+e2NoYXJ0T3V0cHV0LnhBeGlzTGFiZWxzfTwvVGV4dD5cbiAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAge2NoYXJ0T3V0cHV0LmxlZ2VuZC5tYXAoKGl0ZW0sIGkpID0+IChcbiAgICAgICAgICAgICAgPFRleHQga2V5PXtpdGVtLm1vZGVsfT5cbiAgICAgICAgICAgICAgICB7aSA+IDAgPyAnIMK3ICcgOiAnJ31cbiAgICAgICAgICAgICAgICA8QW5zaT57aXRlbS5jb2xvcmVkQnVsbGV0fTwvQW5zaT4ge2l0ZW0ubW9kZWx9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICkpfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG5cbiAgICAgIHsvKiBEYXRlIHJhbmdlIHNlbGVjdG9yICovfVxuICAgICAgPERhdGVSYW5nZVNlbGVjdG9yIGRhdGVSYW5nZT17ZGF0ZVJhbmdlfSBpc0xvYWRpbmc9e2lzTG9hZGluZ30gLz5cblxuICAgICAgey8qIE1vZGVsIGJyZWFrZG93biAtIHR3byBjb2x1bW5zIHdpdGggZml4ZWQgd2lkdGggKi99XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezR9PlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiB3aWR0aD17MzZ9PlxuICAgICAgICAgIHtsZWZ0TW9kZWxzLm1hcCgoW21vZGVsLCB1c2FnZV0pID0+IChcbiAgICAgICAgICAgIDxNb2RlbEVudHJ5XG4gICAgICAgICAgICAgIGtleT17bW9kZWx9XG4gICAgICAgICAgICAgIG1vZGVsPXttb2RlbH1cbiAgICAgICAgICAgICAgdXNhZ2U9e3VzYWdlfVxuICAgICAgICAgICAgICB0b3RhbFRva2Vucz17dG90YWxUb2tlbnN9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgICkpfVxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgd2lkdGg9ezM2fT5cbiAgICAgICAgICB7cmlnaHRNb2RlbHMubWFwKChbbW9kZWwsIHVzYWdlXSkgPT4gKFxuICAgICAgICAgICAgPE1vZGVsRW50cnlcbiAgICAgICAgICAgICAga2V5PXttb2RlbH1cbiAgICAgICAgICAgICAgbW9kZWw9e21vZGVsfVxuICAgICAgICAgICAgICB1c2FnZT17dXNhZ2V9XG4gICAgICAgICAgICAgIHRvdGFsVG9rZW5zPXt0b3RhbFRva2Vuc31cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgKSl9XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG5cbiAgICAgIHsvKiBTY3JvbGwgaGludCAqL31cbiAgICAgIHtzaG93U2Nyb2xsSGludCAmJiAoXG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1YnRsZVwiPlxuICAgICAgICAgICAge2NhblNjcm9sbFVwID8gZmlndXJlcy5hcnJvd1VwIDogJyAnfXsnICd9XG4gICAgICAgICAgICB7Y2FuU2Nyb2xsRG93biA/IGZpZ3VyZXMuYXJyb3dEb3duIDogJyAnfSB7c2Nyb2xsT2Zmc2V0ICsgMX0tXG4gICAgICAgICAgICB7TWF0aC5taW4oc2Nyb2xsT2Zmc2V0ICsgVklTSUJMRV9NT0RFTFMsIG1vZGVsRW50cmllcy5sZW5ndGgpfSBvZnsnICd9XG4gICAgICAgICAgICB7bW9kZWxFbnRyaWVzLmxlbmd0aH0gbW9kZWxzICjihpHihpMgdG8gc2Nyb2xsKVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuICAgIDwvQm94PlxuICApXG59XG5cbnR5cGUgTW9kZWxFbnRyeVByb3BzID0ge1xuICBtb2RlbDogc3RyaW5nXG4gIHVzYWdlOiB7XG4gICAgaW5wdXRUb2tlbnM6IG51bWJlclxuICAgIG91dHB1dFRva2VuczogbnVtYmVyXG4gICAgY2FjaGVSZWFkSW5wdXRUb2tlbnM6IG51bWJlclxuICB9XG4gIHRvdGFsVG9rZW5zOiBudW1iZXJcbn1cblxuZnVuY3Rpb24gTW9kZWxFbnRyeSh7XG4gIG1vZGVsLFxuICB1c2FnZSxcbiAgdG90YWxUb2tlbnMsXG59OiBNb2RlbEVudHJ5UHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBtb2RlbFRva2VucyA9IHVzYWdlLmlucHV0VG9rZW5zICsgdXNhZ2Uub3V0cHV0VG9rZW5zXG4gIGNvbnN0IHBlcmNlbnRhZ2UgPSAoKG1vZGVsVG9rZW5zIC8gdG90YWxUb2tlbnMpICogMTAwKS50b0ZpeGVkKDEpXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgIDxUZXh0PlxuICAgICAgICB7ZmlndXJlcy5idWxsZXR9IDxUZXh0IGJvbGQ+e3JlbmRlck1vZGVsTmFtZShtb2RlbCl9PC9UZXh0PnsnICd9XG4gICAgICAgIDxUZXh0IGNvbG9yPVwic3VidGxlXCI+KHtwZXJjZW50YWdlfSUpPC9UZXh0PlxuICAgICAgPC9UZXh0PlxuICAgICAgPFRleHQgY29sb3I9XCJzdWJ0bGVcIj5cbiAgICAgICAgeycgICd9SW46IHtmb3JtYXROdW1iZXIodXNhZ2UuaW5wdXRUb2tlbnMpfSDCtyBPdXQ6eycgJ31cbiAgICAgICAge2Zvcm1hdE51bWJlcih1c2FnZS5vdXRwdXRUb2tlbnMpfVxuICAgICAgPC9UZXh0PlxuICAgIDwvQm94PlxuICApXG59XG5cbnR5cGUgQ2hhcnRMZWdlbmQgPSB7XG4gIG1vZGVsOiBzdHJpbmdcbiAgY29sb3JlZEJ1bGxldDogc3RyaW5nIC8vIFByZS1jb2xvcmVkIGJ1bGxldCB1c2luZyBjaGFsa1xufVxuXG50eXBlIENoYXJ0T3V0cHV0ID0ge1xuICBjaGFydDogc3RyaW5nXG4gIGxlZ2VuZDogQ2hhcnRMZWdlbmRbXVxuICB4QXhpc0xhYmVsczogc3RyaW5nXG59XG5cbmZ1bmN0aW9uIGdlbmVyYXRlVG9rZW5DaGFydChcbiAgZGFpbHlUb2tlbnM6IERhaWx5TW9kZWxUb2tlbnNbXSxcbiAgbW9kZWxzOiBzdHJpbmdbXSxcbiAgdGVybWluYWxXaWR0aDogbnVtYmVyLFxuKTogQ2hhcnRPdXRwdXQgfCBudWxsIHtcbiAgaWYgKGRhaWx5VG9rZW5zLmxlbmd0aCA8IDIgfHwgbW9kZWxzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBZLWF4aXMgbGFiZWxzIHRha2UgYWJvdXQgNiBjaGFyYWN0ZXJzLCBwbHVzIHNvbWUgcGFkZGluZ1xuICAvLyBDYXAgYXQgfjUyIHRvIGFsaWduIHdpdGggaGVhdG1hcCB3aWR0aCAoMSB5ZWFyIG9mIGRhdGEpXG4gIGNvbnN0IHlBeGlzV2lkdGggPSA3XG4gIGNvbnN0IGF2YWlsYWJsZVdpZHRoID0gdGVybWluYWxXaWR0aCAtIHlBeGlzV2lkdGhcbiAgY29uc3QgY2hhcnRXaWR0aCA9IE1hdGgubWluKDUyLCBNYXRoLm1heCgyMCwgYXZhaWxhYmxlV2lkdGgpKVxuXG4gIC8vIERpc3RyaWJ1dGUgZGF0YSBhY3Jvc3MgdGhlIGF2YWlsYWJsZSBjaGFydCB3aWR0aFxuICBsZXQgcmVjZW50RGF0YTogRGFpbHlNb2RlbFRva2Vuc1tdXG4gIGlmIChkYWlseVRva2Vucy5sZW5ndGggPj0gY2hhcnRXaWR0aCkge1xuICAgIC8vIE1vcmUgZGF0YSB0aGFuIHNwYWNlOiB0YWtlIG1vc3QgcmVjZW50IE4gZGF5c1xuICAgIHJlY2VudERhdGEgPSBkYWlseVRva2Vucy5zbGljZSgtY2hhcnRXaWR0aClcbiAgfSBlbHNlIHtcbiAgICAvLyBMZXNzIGRhdGEgdGhhbiBzcGFjZTogZXhwYW5kIGJ5IHJlcGVhdGluZyBlYWNoIHBvaW50XG4gICAgY29uc3QgcmVwZWF0Q291bnQgPSBNYXRoLmZsb29yKGNoYXJ0V2lkdGggLyBkYWlseVRva2Vucy5sZW5ndGgpXG4gICAgcmVjZW50RGF0YSA9IFtdXG4gICAgZm9yIChjb25zdCBkYXkgb2YgZGFpbHlUb2tlbnMpIHtcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcmVwZWF0Q291bnQ7IGkrKykge1xuICAgICAgICByZWNlbnREYXRhLnB1c2goZGF5KVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIENvbG9yIHBhbGV0dGUgZm9yIGRpZmZlcmVudCBtb2RlbHMgLSB1c2UgdGhlbWUgY29sb3JzXG4gIGNvbnN0IHRoZW1lID0gZ2V0VGhlbWUocmVzb2x2ZVRoZW1lU2V0dGluZyhnZXRHbG9iYWxDb25maWcoKS50aGVtZSkpXG4gIGNvbnN0IGNvbG9ycyA9IFtcbiAgICB0aGVtZUNvbG9yVG9BbnNpKHRoZW1lLnN1Z2dlc3Rpb24pLFxuICAgIHRoZW1lQ29sb3JUb0Fuc2kodGhlbWUuc3VjY2VzcyksXG4gICAgdGhlbWVDb2xvclRvQW5zaSh0aGVtZS53YXJuaW5nKSxcbiAgXVxuXG4gIC8vIFByZXBhcmUgc2VyaWVzIGRhdGEgZm9yIGVhY2ggbW9kZWxcbiAgY29uc3Qgc2VyaWVzOiBudW1iZXJbXVtdID0gW11cbiAgY29uc3QgbGVnZW5kOiBDaGFydExlZ2VuZFtdID0gW11cblxuICAvLyBPbmx5IHNob3cgdG9wIDMgbW9kZWxzIHRvIGtlZXAgY2hhcnQgcmVhZGFibGVcbiAgY29uc3QgdG9wTW9kZWxzID0gbW9kZWxzLnNsaWNlKDAsIDMpXG5cbiAgZm9yIChsZXQgaSA9IDA7IGkgPCB0b3BNb2RlbHMubGVuZ3RoOyBpKyspIHtcbiAgICBjb25zdCBtb2RlbCA9IHRvcE1vZGVsc1tpXSFcbiAgICBjb25zdCBkYXRhID0gcmVjZW50RGF0YS5tYXAoZGF5ID0+IGRheS50b2tlbnNCeU1vZGVsW21vZGVsXSB8fCAwKVxuXG4gICAgLy8gT25seSBpbmNsdWRlIGlmIHRoZXJlJ3MgYWN0dWFsIGRhdGFcbiAgICBpZiAoZGF0YS5zb21lKHYgPT4gdiA+IDApKSB7XG4gICAgICBzZXJpZXMucHVzaChkYXRhKVxuICAgICAgLy8gVXNlIHRoZW1lIGNvbG9ycyB0aGF0IG1hdGNoIHRoZSBjaGFydFxuICAgICAgY29uc3QgYnVsbGV0Q29sb3JzID0gW3RoZW1lLnN1Z2dlc3Rpb24sIHRoZW1lLnN1Y2Nlc3MsIHRoZW1lLndhcm5pbmddXG4gICAgICBsZWdlbmQucHVzaCh7XG4gICAgICAgIG1vZGVsOiByZW5kZXJNb2RlbE5hbWUobW9kZWwpLFxuICAgICAgICBjb2xvcmVkQnVsbGV0OiBhcHBseUNvbG9yKFxuICAgICAgICAgIGZpZ3VyZXMuYnVsbGV0LFxuICAgICAgICAgIGJ1bGxldENvbG9yc1tpICUgYnVsbGV0Q29sb3JzLmxlbmd0aF0gYXMgQ29sb3IsXG4gICAgICAgICksXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIGlmIChzZXJpZXMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IGNoYXJ0ID0gYXNjaWljaGFydChzZXJpZXMsIHtcbiAgICBoZWlnaHQ6IDgsXG4gICAgY29sb3JzOiBjb2xvcnMuc2xpY2UoMCwgc2VyaWVzLmxlbmd0aCksXG4gICAgZm9ybWF0OiAoeDogbnVtYmVyKSA9PiB7XG4gICAgICBsZXQgbGFiZWw6IHN0cmluZ1xuICAgICAgaWYgKHggPj0gMV8wMDBfMDAwKSB7XG4gICAgICAgIGxhYmVsID0gKHggLyAxXzAwMF8wMDApLnRvRml4ZWQoMSkgKyAnTSdcbiAgICAgIH0gZWxzZSBpZiAoeCA+PSAxXzAwMCkge1xuICAgICAgICBsYWJlbCA9ICh4IC8gMV8wMDApLnRvRml4ZWQoMCkgKyAnaydcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGxhYmVsID0geC50b0ZpeGVkKDApXG4gICAgICB9XG4gICAgICByZXR1cm4gbGFiZWwucGFkU3RhcnQoNilcbiAgICB9LFxuICB9KVxuXG4gIC8vIEdlbmVyYXRlIHgtYXhpcyBsYWJlbHMgd2l0aCBkYXRlc1xuICBjb25zdCB4QXhpc0xhYmVscyA9IGdlbmVyYXRlWEF4aXNMYWJlbHMoXG4gICAgcmVjZW50RGF0YSxcbiAgICByZWNlbnREYXRhLmxlbmd0aCxcbiAgICB5QXhpc1dpZHRoLFxuICApXG5cbiAgcmV0dXJuIHsgY2hhcnQsIGxlZ2VuZCwgeEF4aXNMYWJlbHMgfVxufVxuXG5mdW5jdGlvbiBnZW5lcmF0ZVhBeGlzTGFiZWxzKFxuICBkYXRhOiBEYWlseU1vZGVsVG9rZW5zW10sXG4gIF9jaGFydFdpZHRoOiBudW1iZXIsXG4gIHlBeGlzT2Zmc2V0OiBudW1iZXIsXG4pOiBzdHJpbmcge1xuICBpZiAoZGF0YS5sZW5ndGggPT09IDApIHJldHVybiAnJ1xuXG4gIC8vIFNob3cgMy00IGRhdGUgbGFiZWxzIGV2ZW5seSBzcGFjZWQsIGJ1dCBsZWF2ZSByb29tIGZvciBsYXN0IGxhYmVsXG4gIGNvbnN0IG51bUxhYmVscyA9IE1hdGgubWluKDQsIE1hdGgubWF4KDIsIE1hdGguZmxvb3IoZGF0YS5sZW5ndGggLyA4KSkpXG4gIC8vIERvbid0IHVzZSB0aGUgdmVyeSBsYXN0IHBvc2l0aW9uIC0gbGVhdmUgcm9vbSBmb3IgdGhlIGxhYmVsIHRleHRcbiAgY29uc3QgdXNhYmxlTGVuZ3RoID0gZGF0YS5sZW5ndGggLSA2IC8vIFJlc2VydmUgfjYgY2hhcnMgZm9yIGxhc3QgbGFiZWwgKGUuZy4sIFwiRGVjIDdcIilcbiAgY29uc3Qgc3RlcCA9IE1hdGguZmxvb3IodXNhYmxlTGVuZ3RoIC8gKG51bUxhYmVscyAtIDEpKSB8fCAxXG5cbiAgY29uc3QgbGFiZWxQb3NpdGlvbnM6IHsgcG9zOiBudW1iZXI7IGxhYmVsOiBzdHJpbmcgfVtdID0gW11cblxuICBmb3IgKGxldCBpID0gMDsgaSA8IG51bUxhYmVsczsgaSsrKSB7XG4gICAgY29uc3QgaWR4ID0gTWF0aC5taW4oaSAqIHN0ZXAsIGRhdGEubGVuZ3RoIC0gMSlcbiAgICBjb25zdCBkYXRlID0gbmV3IERhdGUoZGF0YVtpZHhdIS5kYXRlKVxuICAgIGNvbnN0IGxhYmVsID0gZGF0ZS50b0xvY2FsZURhdGVTdHJpbmcoJ2VuLVVTJywge1xuICAgICAgbW9udGg6ICdzaG9ydCcsXG4gICAgICBkYXk6ICdudW1lcmljJyxcbiAgICB9KVxuICAgIGxhYmVsUG9zaXRpb25zLnB1c2goeyBwb3M6IGlkeCwgbGFiZWwgfSlcbiAgfVxuXG4gIC8vIEJ1aWxkIHRoZSBsYWJlbCBzdHJpbmcgd2l0aCBwcm9wZXIgc3BhY2luZ1xuICBsZXQgcmVzdWx0ID0gJyAnLnJlcGVhdCh5QXhpc09mZnNldClcbiAgbGV0IGN1cnJlbnRQb3MgPSAwXG5cbiAgZm9yIChjb25zdCB7IHBvcywgbGFiZWwgfSBvZiBsYWJlbFBvc2l0aW9ucykge1xuICAgIGNvbnN0IHNwYWNlcyA9IE1hdGgubWF4KDEsIHBvcyAtIGN1cnJlbnRQb3MpXG4gICAgcmVzdWx0ICs9ICcgJy5yZXBlYXQoc3BhY2VzKSArIGxhYmVsXG4gICAgY3VycmVudFBvcyA9IHBvcyArIGxhYmVsLmxlbmd0aFxuICB9XG5cbiAgcmV0dXJuIHJlc3VsdFxufVxuXG4vLyBTY3JlZW5zaG90IGZ1bmN0aW9uYWxpdHlcbmFzeW5jIGZ1bmN0aW9uIGhhbmRsZVNjcmVlbnNob3QoXG4gIHN0YXRzOiBDbGF1ZGVDb2RlU3RhdHMsXG4gIGFjdGl2ZVRhYjogJ092ZXJ2aWV3JyB8ICdNb2RlbHMnLFxuICBzZXRTdGF0dXM6IChzdGF0dXM6IHN0cmluZyB8IG51bGwpID0+IHZvaWQsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgc2V0U3RhdHVzKCdjb3B5aW5n4oCmJylcblxuICBjb25zdCBhbnNpVGV4dCA9IHJlbmRlclN0YXRzVG9BbnNpKHN0YXRzLCBhY3RpdmVUYWIpXG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNvcHlBbnNpVG9DbGlwYm9hcmQoYW5zaVRleHQpXG5cbiAgc2V0U3RhdHVzKHJlc3VsdC5zdWNjZXNzID8gJ2NvcGllZCEnIDogJ2NvcHkgZmFpbGVkJylcblxuICAvLyBDbGVhciBzdGF0dXMgYWZ0ZXIgMiBzZWNvbmRzXG4gIHNldFRpbWVvdXQoc2V0U3RhdHVzLCAyMDAwLCBudWxsKVxufVxuXG5mdW5jdGlvbiByZW5kZXJTdGF0c1RvQW5zaShcbiAgc3RhdHM6IENsYXVkZUNvZGVTdGF0cyxcbiAgYWN0aXZlVGFiOiAnT3ZlcnZpZXcnIHwgJ01vZGVscycsXG4pOiBzdHJpbmcge1xuICBjb25zdCBsaW5lczogc3RyaW5nW10gPSBbXVxuXG4gIGlmIChhY3RpdmVUYWIgPT09ICdPdmVydmlldycpIHtcbiAgICBsaW5lcy5wdXNoKC4uLnJlbmRlck92ZXJ2aWV3VG9BbnNpKHN0YXRzKSlcbiAgfSBlbHNlIHtcbiAgICBsaW5lcy5wdXNoKC4uLnJlbmRlck1vZGVsc1RvQW5zaShzdGF0cykpXG4gIH1cblxuICAvLyBUcmltIHRyYWlsaW5nIGVtcHR5IGxpbmVzXG4gIHdoaWxlIChcbiAgICBsaW5lcy5sZW5ndGggPiAwICYmXG4gICAgc3RyaXBBbnNpKGxpbmVzW2xpbmVzLmxlbmd0aCAtIDFdISkudHJpbSgpID09PSAnJ1xuICApIHtcbiAgICBsaW5lcy5wb3AoKVxuICB9XG5cbiAgLy8gQWRkIFwiL3N0YXRzXCIgcmlnaHQtYWxpZ25lZCBvbiB0aGUgbGFzdCBsaW5lXG4gIGlmIChsaW5lcy5sZW5ndGggPiAwKSB7XG4gICAgY29uc3QgbGFzdExpbmUgPSBsaW5lc1tsaW5lcy5sZW5ndGggLSAxXSFcbiAgICBjb25zdCBsYXN0TGluZUxlbiA9IGdldFN0cmluZ1dpZHRoKGxhc3RMaW5lKVxuICAgIC8vIFVzZSBrbm93biBjb250ZW50IHdpZHRocyBiYXNlZCBvbiBsYXlvdXQ6XG4gICAgLy8gT3ZlcnZpZXc6IHR3by1jb2x1bW4gc3RhdHMgPSBDT0wyX1NUQVJUKDQwKSArIENPTDJfTEFCRUxfV0lEVEgoMTgpICsgbWF4X3ZhbHVlKH4xMikgPSA3MFxuICAgIC8vIE1vZGVsczogY2hhcnQgd2lkdGggPSA4MFxuICAgIGNvbnN0IGNvbnRlbnRXaWR0aCA9IGFjdGl2ZVRhYiA9PT0gJ092ZXJ2aWV3JyA/IDcwIDogODBcbiAgICBjb25zdCBzdGF0c0xhYmVsID0gJy9zdGF0cydcbiAgICBjb25zdCBwYWRkaW5nID0gTWF0aC5tYXgoMiwgY29udGVudFdpZHRoIC0gbGFzdExpbmVMZW4gLSBzdGF0c0xhYmVsLmxlbmd0aClcbiAgICBsaW5lc1tsaW5lcy5sZW5ndGggLSAxXSA9XG4gICAgICBsYXN0TGluZSArICcgJy5yZXBlYXQocGFkZGluZykgKyBjaGFsay5ncmF5KHN0YXRzTGFiZWwpXG4gIH1cblxuICByZXR1cm4gbGluZXMuam9pbignXFxuJylcbn1cblxuZnVuY3Rpb24gcmVuZGVyT3ZlcnZpZXdUb0Fuc2koc3RhdHM6IENsYXVkZUNvZGVTdGF0cyk6IHN0cmluZ1tdIHtcbiAgY29uc3QgbGluZXM6IHN0cmluZ1tdID0gW11cbiAgY29uc3QgdGhlbWUgPSBnZXRUaGVtZShyZXNvbHZlVGhlbWVTZXR0aW5nKGdldEdsb2JhbENvbmZpZygpLnRoZW1lKSlcbiAgY29uc3QgaCA9ICh0ZXh0OiBzdHJpbmcpID0+IGFwcGx5Q29sb3IodGV4dCwgdGhlbWUuY2xhdWRlIGFzIENvbG9yKVxuXG4gIC8vIFR3by1jb2x1bW4gaGVscGVyIHdpdGggZml4ZWQgc3BhY2luZ1xuICAvLyBDb2x1bW4gMTogbGFiZWwgKDE4IGNoYXJzKSArIHZhbHVlICsgcGFkZGluZyB0byByZWFjaCBjb2wgMlxuICAvLyBDb2x1bW4gMiBzdGFydHMgYXQgY2hhcmFjdGVyIHBvc2l0aW9uIDQwXG4gIGNvbnN0IENPTDFfTEFCRUxfV0lEVEggPSAxOFxuICBjb25zdCBDT0wyX1NUQVJUID0gNDBcbiAgY29uc3QgQ09MMl9MQUJFTF9XSURUSCA9IDE4XG5cbiAgY29uc3Qgcm93ID0gKGwxOiBzdHJpbmcsIHYxOiBzdHJpbmcsIGwyOiBzdHJpbmcsIHYyOiBzdHJpbmcpOiBzdHJpbmcgPT4ge1xuICAgIC8vIEJ1aWxkIGNvbHVtbiAxOiBsYWJlbCArIHZhbHVlXG4gICAgY29uc3QgbGFiZWwxID0gKGwxICsgJzonKS5wYWRFbmQoQ09MMV9MQUJFTF9XSURUSClcbiAgICBjb25zdCBjb2wxUGxhaW5MZW4gPSBsYWJlbDEubGVuZ3RoICsgdjEubGVuZ3RoXG5cbiAgICAvLyBDYWxjdWxhdGUgc3BhY2VzIG5lZWRlZCBiZXR3ZWVuIGNvbDEgdmFsdWUgYW5kIGNvbDIgbGFiZWxcbiAgICBjb25zdCBzcGFjZUJldHdlZW4gPSBNYXRoLm1heCgyLCBDT0wyX1NUQVJUIC0gY29sMVBsYWluTGVuKVxuXG4gICAgLy8gQnVpbGQgY29sdW1uIDI6IGxhYmVsICsgdmFsdWVcbiAgICBjb25zdCBsYWJlbDIgPSAobDIgKyAnOicpLnBhZEVuZChDT0wyX0xBQkVMX1dJRFRIKVxuXG4gICAgLy8gQXNzZW1ibGUgd2l0aCBjb2xvcnMgYXBwbGllZCB0byB2YWx1ZXMgb25seVxuICAgIHJldHVybiBsYWJlbDEgKyBoKHYxKSArICcgJy5yZXBlYXQoc3BhY2VCZXR3ZWVuKSArIGxhYmVsMiArIGgodjIpXG4gIH1cblxuICAvLyBIZWF0bWFwIC0gdXNlIGZpeGVkIHdpZHRoIGZvciBzY3JlZW5zaG90ICg1NiA9IDUyIHdlZWtzICsgNCBmb3IgZGF5IGxhYmVscylcbiAgaWYgKHN0YXRzLmRhaWx5QWN0aXZpdHkubGVuZ3RoID4gMCkge1xuICAgIGxpbmVzLnB1c2goZ2VuZXJhdGVIZWF0bWFwKHN0YXRzLmRhaWx5QWN0aXZpdHksIHsgdGVybWluYWxXaWR0aDogNTYgfSkpXG4gICAgbGluZXMucHVzaCgnJylcbiAgfVxuXG4gIC8vIENhbGN1bGF0ZSB2YWx1ZXNcbiAgY29uc3QgbW9kZWxFbnRyaWVzID0gT2JqZWN0LmVudHJpZXMoc3RhdHMubW9kZWxVc2FnZSkuc29ydChcbiAgICAoWywgYV0sIFssIGJdKSA9PlxuICAgICAgYi5pbnB1dFRva2VucyArIGIub3V0cHV0VG9rZW5zIC0gKGEuaW5wdXRUb2tlbnMgKyBhLm91dHB1dFRva2VucyksXG4gIClcbiAgY29uc3QgZmF2b3JpdGVNb2RlbCA9IG1vZGVsRW50cmllc1swXVxuICBjb25zdCB0b3RhbFRva2VucyA9IG1vZGVsRW50cmllcy5yZWR1Y2UoXG4gICAgKHN1bSwgWywgdXNhZ2VdKSA9PiBzdW0gKyB1c2FnZS5pbnB1dFRva2VucyArIHVzYWdlLm91dHB1dFRva2VucyxcbiAgICAwLFxuICApXG5cbiAgLy8gUm93IDE6IEZhdm9yaXRlIG1vZGVsIHwgVG90YWwgdG9rZW5zXG4gIGlmIChmYXZvcml0ZU1vZGVsKSB7XG4gICAgbGluZXMucHVzaChcbiAgICAgIHJvdyhcbiAgICAgICAgJ0Zhdm9yaXRlIG1vZGVsJyxcbiAgICAgICAgcmVuZGVyTW9kZWxOYW1lKGZhdm9yaXRlTW9kZWxbMF0pLFxuICAgICAgICAnVG90YWwgdG9rZW5zJyxcbiAgICAgICAgZm9ybWF0TnVtYmVyKHRvdGFsVG9rZW5zKSxcbiAgICAgICksXG4gICAgKVxuICB9XG4gIGxpbmVzLnB1c2goJycpXG5cbiAgLy8gUm93IDI6IFNlc3Npb25zIHwgTG9uZ2VzdCBzZXNzaW9uXG4gIGxpbmVzLnB1c2goXG4gICAgcm93KFxuICAgICAgJ1Nlc3Npb25zJyxcbiAgICAgIGZvcm1hdE51bWJlcihzdGF0cy50b3RhbFNlc3Npb25zKSxcbiAgICAgICdMb25nZXN0IHNlc3Npb24nLFxuICAgICAgc3RhdHMubG9uZ2VzdFNlc3Npb25cbiAgICAgICAgPyBmb3JtYXREdXJhdGlvbihzdGF0cy5sb25nZXN0U2Vzc2lvbi5kdXJhdGlvbilcbiAgICAgICAgOiAnTi9BJyxcbiAgICApLFxuICApXG5cbiAgLy8gUm93IDM6IEN1cnJlbnQgc3RyZWFrIHwgTG9uZ2VzdCBzdHJlYWtcbiAgY29uc3QgY3VycmVudFN0cmVha1ZhbCA9IGAke3N0YXRzLnN0cmVha3MuY3VycmVudFN0cmVha30gJHtzdGF0cy5zdHJlYWtzLmN1cnJlbnRTdHJlYWsgPT09IDEgPyAnZGF5JyA6ICdkYXlzJ31gXG4gIGNvbnN0IGxvbmdlc3RTdHJlYWtWYWwgPSBgJHtzdGF0cy5zdHJlYWtzLmxvbmdlc3RTdHJlYWt9ICR7c3RhdHMuc3RyZWFrcy5sb25nZXN0U3RyZWFrID09PSAxID8gJ2RheScgOiAnZGF5cyd9YFxuICBsaW5lcy5wdXNoKFxuICAgIHJvdygnQ3VycmVudCBzdHJlYWsnLCBjdXJyZW50U3RyZWFrVmFsLCAnTG9uZ2VzdCBzdHJlYWsnLCBsb25nZXN0U3RyZWFrVmFsKSxcbiAgKVxuXG4gIC8vIFJvdyA0OiBBY3RpdmUgZGF5cyB8IFBlYWsgaG91clxuICBjb25zdCBhY3RpdmVEYXlzVmFsID0gYCR7c3RhdHMuYWN0aXZlRGF5c30vJHtzdGF0cy50b3RhbERheXN9YFxuICBjb25zdCBwZWFrSG91clZhbCA9XG4gICAgc3RhdHMucGVha0FjdGl2aXR5SG91ciAhPT0gbnVsbFxuICAgICAgPyBgJHtzdGF0cy5wZWFrQWN0aXZpdHlIb3VyfTowMC0ke3N0YXRzLnBlYWtBY3Rpdml0eUhvdXIgKyAxfTowMGBcbiAgICAgIDogJ04vQSdcbiAgbGluZXMucHVzaChyb3coJ0FjdGl2ZSBkYXlzJywgYWN0aXZlRGF5c1ZhbCwgJ1BlYWsgaG91cicsIHBlYWtIb3VyVmFsKSlcblxuICAvLyBTcGVjdWxhdGlvbiB0aW1lIHNhdmVkIChhbnQtb25seSlcbiAgaWYgKFxuICAgIFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiZcbiAgICBzdGF0cy50b3RhbFNwZWN1bGF0aW9uVGltZVNhdmVkTXMgPiAwXG4gICkge1xuICAgIGNvbnN0IGxhYmVsID0gJ1NwZWN1bGF0aW9uIHNhdmVkOicucGFkRW5kKENPTDFfTEFCRUxfV0lEVEgpXG4gICAgbGluZXMucHVzaChsYWJlbCArIGgoZm9ybWF0RHVyYXRpb24oc3RhdHMudG90YWxTcGVjdWxhdGlvblRpbWVTYXZlZE1zKSkpXG4gIH1cblxuICAvLyBTaG90IHN0YXRzIChhbnQtb25seSlcbiAgaWYgKGZlYXR1cmUoJ1NIT1RfU1RBVFMnKSAmJiBzdGF0cy5zaG90RGlzdHJpYnV0aW9uKSB7XG4gICAgY29uc3QgZGlzdCA9IHN0YXRzLnNob3REaXN0cmlidXRpb25cbiAgICBjb25zdCB0b3RhbFdpdGhTaG90cyA9IE9iamVjdC52YWx1ZXMoZGlzdCkucmVkdWNlKChzLCBuKSA9PiBzICsgbiwgMClcbiAgICBpZiAodG90YWxXaXRoU2hvdHMgPiAwKSB7XG4gICAgICBjb25zdCB0b3RhbFNob3RzID0gT2JqZWN0LmVudHJpZXMoZGlzdCkucmVkdWNlKFxuICAgICAgICAocywgW2NvdW50LCBzZXNzaW9uc10pID0+IHMgKyBwYXJzZUludChjb3VudCwgMTApICogc2Vzc2lvbnMsXG4gICAgICAgIDAsXG4gICAgICApXG4gICAgICBjb25zdCBhdmdTaG90cyA9ICh0b3RhbFNob3RzIC8gdG90YWxXaXRoU2hvdHMpLnRvRml4ZWQoMSlcbiAgICAgIGNvbnN0IGJ1Y2tldCA9IChtaW46IG51bWJlciwgbWF4PzogbnVtYmVyKSA9PlxuICAgICAgICBPYmplY3QuZW50cmllcyhkaXN0KVxuICAgICAgICAgIC5maWx0ZXIoKFtrXSkgPT4ge1xuICAgICAgICAgICAgY29uc3QgbiA9IHBhcnNlSW50KGssIDEwKVxuICAgICAgICAgICAgcmV0dXJuIG4gPj0gbWluICYmIChtYXggPT09IHVuZGVmaW5lZCB8fCBuIDw9IG1heClcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5yZWR1Y2UoKHMsIFssIHZdKSA9PiBzICsgdiwgMClcbiAgICAgIGNvbnN0IHBjdCA9IChuOiBudW1iZXIpID0+IE1hdGgucm91bmQoKG4gLyB0b3RhbFdpdGhTaG90cykgKiAxMDApXG4gICAgICBjb25zdCBmbXRCdWNrZXQgPSAoY291bnQ6IG51bWJlciwgcDogbnVtYmVyKSA9PiBgJHtjb3VudH0gKCR7cH0lKWBcbiAgICAgIGNvbnN0IGIxID0gYnVja2V0KDEsIDEpXG4gICAgICBjb25zdCBiMl81ID0gYnVja2V0KDIsIDUpXG4gICAgICBjb25zdCBiNl8xMCA9IGJ1Y2tldCg2LCAxMClcbiAgICAgIGNvbnN0IGIxMSA9IGJ1Y2tldCgxMSlcbiAgICAgIGxpbmVzLnB1c2goJycpXG4gICAgICBsaW5lcy5wdXNoKCdTaG90IGRpc3RyaWJ1dGlvbicpXG4gICAgICBsaW5lcy5wdXNoKFxuICAgICAgICByb3coXG4gICAgICAgICAgJzEtc2hvdCcsXG4gICAgICAgICAgZm10QnVja2V0KGIxLCBwY3QoYjEpKSxcbiAgICAgICAgICAnMlxcdTIwMTM1IHNob3QnLFxuICAgICAgICAgIGZtdEJ1Y2tldChiMl81LCBwY3QoYjJfNSkpLFxuICAgICAgICApLFxuICAgICAgKVxuICAgICAgbGluZXMucHVzaChcbiAgICAgICAgcm93KFxuICAgICAgICAgICc2XFx1MjAxMzEwIHNob3QnLFxuICAgICAgICAgIGZtdEJ1Y2tldChiNl8xMCwgcGN0KGI2XzEwKSksXG4gICAgICAgICAgJzExKyBzaG90JyxcbiAgICAgICAgICBmbXRCdWNrZXQoYjExLCBwY3QoYjExKSksXG4gICAgICAgICksXG4gICAgICApXG4gICAgICBsaW5lcy5wdXNoKGAkeydBdmcvc2Vzc2lvbjonLnBhZEVuZChDT0wxX0xBQkVMX1dJRFRIKX0ke2goYXZnU2hvdHMpfWApXG4gICAgfVxuICB9XG5cbiAgbGluZXMucHVzaCgnJylcblxuICAvLyBGdW4gZmFjdG9pZFxuICBjb25zdCBmYWN0b2lkID0gZ2VuZXJhdGVGdW5GYWN0b2lkKHN0YXRzLCB0b3RhbFRva2VucylcbiAgbGluZXMucHVzaChoKGZhY3RvaWQpKVxuICBsaW5lcy5wdXNoKGNoYWxrLmdyYXkoYFN0YXRzIGZyb20gdGhlIGxhc3QgJHtzdGF0cy50b3RhbERheXN9IGRheXNgKSlcblxuICByZXR1cm4gbGluZXNcbn1cblxuZnVuY3Rpb24gcmVuZGVyTW9kZWxzVG9BbnNpKHN0YXRzOiBDbGF1ZGVDb2RlU3RhdHMpOiBzdHJpbmdbXSB7XG4gIGNvbnN0IGxpbmVzOiBzdHJpbmdbXSA9IFtdXG5cbiAgY29uc3QgbW9kZWxFbnRyaWVzID0gT2JqZWN0LmVudHJpZXMoc3RhdHMubW9kZWxVc2FnZSkuc29ydChcbiAgICAoWywgYV0sIFssIGJdKSA9PlxuICAgICAgYi5pbnB1dFRva2VucyArIGIub3V0cHV0VG9rZW5zIC0gKGEuaW5wdXRUb2tlbnMgKyBhLm91dHB1dFRva2VucyksXG4gIClcblxuICBpZiAobW9kZWxFbnRyaWVzLmxlbmd0aCA9PT0gMCkge1xuICAgIGxpbmVzLnB1c2goY2hhbGsuZ3JheSgnTm8gbW9kZWwgdXNhZ2UgZGF0YSBhdmFpbGFibGUnKSlcbiAgICByZXR1cm4gbGluZXNcbiAgfVxuXG4gIGNvbnN0IGZhdm9yaXRlTW9kZWwgPSBtb2RlbEVudHJpZXNbMF1cbiAgY29uc3QgdG90YWxUb2tlbnMgPSBtb2RlbEVudHJpZXMucmVkdWNlKFxuICAgIChzdW0sIFssIHVzYWdlXSkgPT4gc3VtICsgdXNhZ2UuaW5wdXRUb2tlbnMgKyB1c2FnZS5vdXRwdXRUb2tlbnMsXG4gICAgMCxcbiAgKVxuXG4gIC8vIEdlbmVyYXRlIGNoYXJ0IGlmIHdlIGhhdmUgZGF0YSAtIHVzZSBmaXhlZCB3aWR0aCBmb3Igc2NyZWVuc2hvdFxuICBjb25zdCBjaGFydE91dHB1dCA9IGdlbmVyYXRlVG9rZW5DaGFydChcbiAgICBzdGF0cy5kYWlseU1vZGVsVG9rZW5zLFxuICAgIG1vZGVsRW50cmllcy5tYXAoKFttb2RlbF0pID0+IG1vZGVsKSxcbiAgICA4MCwgLy8gRml4ZWQgd2lkdGggZm9yIHNjcmVlbnNob3RcbiAgKVxuXG4gIGlmIChjaGFydE91dHB1dCkge1xuICAgIGxpbmVzLnB1c2goY2hhbGsuYm9sZCgnVG9rZW5zIHBlciBEYXknKSlcbiAgICBsaW5lcy5wdXNoKGNoYXJ0T3V0cHV0LmNoYXJ0KVxuICAgIGxpbmVzLnB1c2goY2hhbGsuZ3JheShjaGFydE91dHB1dC54QXhpc0xhYmVscykpXG4gICAgLy8gTGVnZW5kIC0gdXNlIHByZS1jb2xvcmVkIGJ1bGxldHMgZnJvbSBjaGFydCBvdXRwdXRcbiAgICBjb25zdCBsZWdlbmRMaW5lID0gY2hhcnRPdXRwdXQubGVnZW5kXG4gICAgICAubWFwKGl0ZW0gPT4gYCR7aXRlbS5jb2xvcmVkQnVsbGV0fSAke2l0ZW0ubW9kZWx9YClcbiAgICAgIC5qb2luKCcgwrcgJylcbiAgICBsaW5lcy5wdXNoKGxlZ2VuZExpbmUpXG4gICAgbGluZXMucHVzaCgnJylcbiAgfVxuXG4gIC8vIFN1bW1hcnlcbiAgbGluZXMucHVzaChcbiAgICBgJHtmaWd1cmVzLnN0YXJ9IEZhdm9yaXRlOiAke2NoYWxrLm1hZ2VudGEuYm9sZChyZW5kZXJNb2RlbE5hbWUoZmF2b3JpdGVNb2RlbD8uWzBdIHx8ICcnKSl9IMK3ICR7ZmlndXJlcy5jaXJjbGV9IFRvdGFsOiAke2NoYWxrLm1hZ2VudGEoZm9ybWF0TnVtYmVyKHRvdGFsVG9rZW5zKSl9IHRva2Vuc2AsXG4gIClcbiAgbGluZXMucHVzaCgnJylcblxuICAvLyBNb2RlbCBicmVha2Rvd24gLSBvbmx5IHNob3cgdG9wIDMgZm9yIHNjcmVlbnNob3RcbiAgY29uc3QgdG9wTW9kZWxzID0gbW9kZWxFbnRyaWVzLnNsaWNlKDAsIDMpXG4gIGZvciAoY29uc3QgW21vZGVsLCB1c2FnZV0gb2YgdG9wTW9kZWxzKSB7XG4gICAgY29uc3QgbW9kZWxUb2tlbnMgPSB1c2FnZS5pbnB1dFRva2VucyArIHVzYWdlLm91dHB1dFRva2Vuc1xuICAgIGNvbnN0IHBlcmNlbnRhZ2UgPSAoKG1vZGVsVG9rZW5zIC8gdG90YWxUb2tlbnMpICogMTAwKS50b0ZpeGVkKDEpXG4gICAgbGluZXMucHVzaChcbiAgICAgIGAke2ZpZ3VyZXMuYnVsbGV0fSAke2NoYWxrLmJvbGQocmVuZGVyTW9kZWxOYW1lKG1vZGVsKSl9ICR7Y2hhbGsuZ3JheShgKCR7cGVyY2VudGFnZX0lKWApfWAsXG4gICAgKVxuICAgIGxpbmVzLnB1c2goXG4gICAgICBjaGFsay5kaW0oXG4gICAgICAgIGAgIEluOiAke2Zvcm1hdE51bWJlcih1c2FnZS5pbnB1dFRva2Vucyl9IMK3IE91dDogJHtmb3JtYXROdW1iZXIodXNhZ2Uub3V0cHV0VG9rZW5zKX1gLFxuICAgICAgKSxcbiAgICApXG4gIH1cblxuICByZXR1cm4gbGluZXNcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLE9BQU8sUUFBUSxZQUFZO0FBQ3BDLFNBQVNDLElBQUksSUFBSUMsVUFBVSxRQUFRLFlBQVk7QUFDL0MsT0FBT0MsS0FBSyxNQUFNLE9BQU87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 \ No newline at end of file diff --git a/components/StatusLine.tsx b/components/StatusLine.tsx new file mode 100644 index 0000000..eafb49a --- /dev/null +++ b/components/StatusLine.tsx @@ -0,0 +1,324 @@ +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import { memo, useCallback, useEffect, useRef } from 'react'; +import { logEvent } from 'src/services/analytics/index.js'; +import { useAppState, useSetAppState } from 'src/state/AppState.js'; +import type { PermissionMode } from 'src/utils/permissions/PermissionMode.js'; +import { getIsRemoteMode, getKairosActive, getMainThreadAgentType, getOriginalCwd, getSdkBetas, getSessionId } from '../bootstrap/state.js'; +import { DEFAULT_OUTPUT_STYLE_NAME } from '../constants/outputStyles.js'; +import { useNotifications } from '../context/notifications.js'; +import { getTotalAPIDuration, getTotalCost, getTotalDuration, getTotalInputTokens, getTotalLinesAdded, getTotalLinesRemoved, getTotalOutputTokens } from '../cost-tracker.js'; +import { useMainLoopModel } from '../hooks/useMainLoopModel.js'; +import { type ReadonlySettings, useSettings } from '../hooks/useSettings.js'; +import { Ansi, Box, Text } from '../ink.js'; +import { getRawUtilization } from '../services/claudeAiLimits.js'; +import type { Message } from '../types/message.js'; +import type { StatusLineCommandInput } from '../types/statusLine.js'; +import type { VimMode } from '../types/textInputTypes.js'; +import { checkHasTrustDialogAccepted } from '../utils/config.js'; +import { calculateContextPercentages, getContextWindowForModel } from '../utils/context.js'; +import { getCwd } from '../utils/cwd.js'; +import { logForDebugging } from '../utils/debug.js'; +import { isFullscreenEnvEnabled } from '../utils/fullscreen.js'; +import { createBaseHookInput, executeStatusLineCommand } from '../utils/hooks.js'; +import { getLastAssistantMessage } from '../utils/messages.js'; +import { getRuntimeMainLoopModel, type ModelName, renderModelName } from '../utils/model/model.js'; +import { getCurrentSessionTitle } from '../utils/sessionStorage.js'; +import { doesMostRecentAssistantMessageExceed200k, getCurrentUsage } from '../utils/tokens.js'; +import { getCurrentWorktreeSession } from '../utils/worktree.js'; +import { isVimModeEnabled } from './PromptInput/utils.js'; +export function statusLineShouldDisplay(settings: ReadonlySettings): boolean { + // Assistant mode: statusline fields (model, permission mode, cwd) reflect the + // REPL/daemon process, not what the agent child is actually running. Hide it. + if (feature('KAIROS') && getKairosActive()) return false; + return settings?.statusLine !== undefined; +} +function buildStatusLineCommandInput(permissionMode: PermissionMode, exceeds200kTokens: boolean, settings: ReadonlySettings, messages: Message[], addedDirs: string[], mainLoopModel: ModelName, vimMode?: VimMode): StatusLineCommandInput { + const agentType = getMainThreadAgentType(); + const worktreeSession = getCurrentWorktreeSession(); + const runtimeModel = getRuntimeMainLoopModel({ + permissionMode, + mainLoopModel, + exceeds200kTokens + }); + const outputStyleName = settings?.outputStyle || DEFAULT_OUTPUT_STYLE_NAME; + const currentUsage = getCurrentUsage(messages); + const contextWindowSize = getContextWindowForModel(runtimeModel, getSdkBetas()); + const contextPercentages = calculateContextPercentages(currentUsage, contextWindowSize); + const sessionId = getSessionId(); + const sessionName = getCurrentSessionTitle(sessionId); + const rawUtil = getRawUtilization(); + const rateLimits: StatusLineCommandInput['rate_limits'] = { + ...(rawUtil.five_hour && { + five_hour: { + used_percentage: rawUtil.five_hour.utilization * 100, + resets_at: rawUtil.five_hour.resets_at + } + }), + ...(rawUtil.seven_day && { + seven_day: { + used_percentage: rawUtil.seven_day.utilization * 100, + resets_at: rawUtil.seven_day.resets_at + } + }) + }; + return { + ...createBaseHookInput(), + ...(sessionName && { + session_name: sessionName + }), + model: { + id: runtimeModel, + display_name: renderModelName(runtimeModel) + }, + workspace: { + current_dir: getCwd(), + project_dir: getOriginalCwd(), + added_dirs: addedDirs + }, + version: MACRO.VERSION, + output_style: { + name: outputStyleName + }, + cost: { + total_cost_usd: getTotalCost(), + total_duration_ms: getTotalDuration(), + total_api_duration_ms: getTotalAPIDuration(), + total_lines_added: getTotalLinesAdded(), + total_lines_removed: getTotalLinesRemoved() + }, + context_window: { + total_input_tokens: getTotalInputTokens(), + total_output_tokens: getTotalOutputTokens(), + context_window_size: contextWindowSize, + current_usage: currentUsage, + used_percentage: contextPercentages.used, + remaining_percentage: contextPercentages.remaining + }, + exceeds_200k_tokens: exceeds200kTokens, + ...((rateLimits.five_hour || rateLimits.seven_day) && { + rate_limits: rateLimits + }), + ...(isVimModeEnabled() && { + vim: { + mode: vimMode ?? 'INSERT' + } + }), + ...(agentType && { + agent: { + name: agentType + } + }), + ...(getIsRemoteMode() && { + remote: { + session_id: getSessionId() + } + }), + ...(worktreeSession && { + worktree: { + name: worktreeSession.worktreeName, + path: worktreeSession.worktreePath, + branch: worktreeSession.worktreeBranch, + original_cwd: worktreeSession.originalCwd, + original_branch: worktreeSession.originalBranch + } + }) + }; +} +type Props = { + // messages stays behind a ref (read only in the debounced callback); + // lastAssistantMessageId is the actual re-render trigger. + messagesRef: React.RefObject; + lastAssistantMessageId: string | null; + vimMode?: VimMode; +}; +export function getLastAssistantMessageId(messages: Message[]): string | null { + return getLastAssistantMessage(messages)?.uuid ?? null; +} +function StatusLineInner({ + messagesRef, + lastAssistantMessageId, + vimMode +}: Props): React.ReactNode { + const abortControllerRef = useRef(undefined); + const permissionMode = useAppState(s => s.toolPermissionContext.mode); + const additionalWorkingDirectories = useAppState(s => s.toolPermissionContext.additionalWorkingDirectories); + const statusLineText = useAppState(s => s.statusLineText); + const setAppState = useSetAppState(); + const settings = useSettings(); + const { + addNotification + } = useNotifications(); + // AppState-sourced model — same source as API requests. getMainLoopModel() + // re-reads settings.json on every call, so another session's /model write + // would leak into this session's statusline (anthropics/claude-code#37596). + const mainLoopModel = useMainLoopModel(); + + // Keep latest values in refs for stable callback access + const settingsRef = useRef(settings); + settingsRef.current = settings; + const vimModeRef = useRef(vimMode); + vimModeRef.current = vimMode; + const permissionModeRef = useRef(permissionMode); + permissionModeRef.current = permissionMode; + const addedDirsRef = useRef(additionalWorkingDirectories); + addedDirsRef.current = additionalWorkingDirectories; + const mainLoopModelRef = useRef(mainLoopModel); + mainLoopModelRef.current = mainLoopModel; + + // Track previous state to detect changes and cache expensive calculations + const previousStateRef = useRef<{ + messageId: string | null; + exceeds200kTokens: boolean; + permissionMode: PermissionMode; + vimMode: VimMode | undefined; + mainLoopModel: ModelName; + }>({ + messageId: null, + exceeds200kTokens: false, + permissionMode, + vimMode, + mainLoopModel + }); + + // Debounce timer ref + const debounceTimerRef = useRef | undefined>(undefined); + + // True when the next invocation should log its result (first run or after settings reload) + const logNextResultRef = useRef(true); + + // Stable update function — reads latest values from refs + const doUpdate = useCallback(async () => { + // Cancel any in-flight requests + abortControllerRef.current?.abort(); + const controller = new AbortController(); + abortControllerRef.current = controller; + const msgs = messagesRef.current; + const logResult = logNextResultRef.current; + logNextResultRef.current = false; + try { + let exceeds200kTokens = previousStateRef.current.exceeds200kTokens; + + // Only recalculate 200k check if messages changed + const currentMessageId = getLastAssistantMessageId(msgs); + if (currentMessageId !== previousStateRef.current.messageId) { + exceeds200kTokens = doesMostRecentAssistantMessageExceed200k(msgs); + previousStateRef.current.messageId = currentMessageId; + previousStateRef.current.exceeds200kTokens = exceeds200kTokens; + } + const statusInput = buildStatusLineCommandInput(permissionModeRef.current, exceeds200kTokens, settingsRef.current, msgs, Array.from(addedDirsRef.current.keys()), mainLoopModelRef.current, vimModeRef.current); + const text = await executeStatusLineCommand(statusInput, controller.signal, undefined, logResult); + if (!controller.signal.aborted) { + setAppState(prev => { + if (prev.statusLineText === text) return prev; + return { + ...prev, + statusLineText: text + }; + }); + } + } catch { + // Silently ignore errors in status line updates + } + }, [messagesRef, setAppState]); + + // Stable debounced schedule function — no deps, uses refs + const scheduleUpdate = useCallback(() => { + if (debounceTimerRef.current !== undefined) { + clearTimeout(debounceTimerRef.current); + } + debounceTimerRef.current = setTimeout((ref, doUpdate) => { + ref.current = undefined; + void doUpdate(); + }, 300, debounceTimerRef, doUpdate); + }, [doUpdate]); + + // Only trigger update when assistant message, permission mode, vim mode, or model actually changes + useEffect(() => { + if (lastAssistantMessageId !== previousStateRef.current.messageId || permissionMode !== previousStateRef.current.permissionMode || vimMode !== previousStateRef.current.vimMode || mainLoopModel !== previousStateRef.current.mainLoopModel) { + // Don't update messageId here — let doUpdate handle it so + // exceeds200kTokens is recalculated with the latest messages + previousStateRef.current.permissionMode = permissionMode; + previousStateRef.current.vimMode = vimMode; + previousStateRef.current.mainLoopModel = mainLoopModel; + scheduleUpdate(); + } + }, [lastAssistantMessageId, permissionMode, vimMode, mainLoopModel, scheduleUpdate]); + + // When the statusLine command changes (hot reload), log the next result + const statusLineCommand = settings?.statusLine?.command; + const isFirstSettingsRender = useRef(true); + useEffect(() => { + if (isFirstSettingsRender.current) { + isFirstSettingsRender.current = false; + return; + } + logNextResultRef.current = true; + void doUpdate(); + }, [statusLineCommand, doUpdate]); + + // Separate effect for logging on mount + useEffect(() => { + const statusLine = settings?.statusLine; + if (statusLine) { + logEvent('tengu_status_line_mount', { + command_length: statusLine.command.length, + padding: statusLine.padding + }); + // Log if status line is configured but disabled by disableAllHooks + if (settings.disableAllHooks === true) { + logForDebugging('Status line is configured but disableAllHooks is true', { + level: 'warn' + }); + } + // executeStatusLineCommand (hooks.ts) returns undefined when trust is + // blocked — statusLineText stays undefined forever, user sees nothing, + // and tengu_status_line_mount above fires anyway so telemetry looks fine. + if (!checkHasTrustDialogAccepted()) { + addNotification({ + key: 'statusline-trust-blocked', + text: 'statusline skipped · restart to fix', + color: 'warning', + priority: 'low' + }); + logForDebugging('Status line command skipped: workspace trust not accepted', { + level: 'warn' + }); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + // biome-ignore lint/correctness/useExhaustiveDependencies: intentional + }, []); // Only run once on mount - settings stable for initial logging + + // Initial update on mount + cleanup on unmount + useEffect(() => { + void doUpdate(); + return () => { + abortControllerRef.current?.abort(); + if (debounceTimerRef.current !== undefined) { + clearTimeout(debounceTimerRef.current); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + // biome-ignore lint/correctness/useExhaustiveDependencies: intentional + }, []); // Only run once on mount, not when doUpdate changes + + // Get padding from settings or default to 0 + const paddingX = settings?.statusLine?.padding ?? 0; + + // StatusLine must have stable height in fullscreen — the footer is + // flexShrink:0 so a 0→1 row change when the command finishes steals + // a row from ScrollBox and shifts content. Reserve the row while loading + // (same trick as PromptInputFooterLeftSide). + return + {statusLineText ? + {statusLineText} + : isFullscreenEnvEnabled() ? : null} + ; +} + +// Parent (PromptInputFooter) re-renders on every setMessages, but StatusLine's +// own props now only change when lastAssistantMessageId flips — memo keeps it +// from being dragged along (previously ~18 no-prop-change renders per session). +export const StatusLine = memo(StatusLineInner); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJtZW1vIiwidXNlQ2FsbGJhY2siLCJ1c2VFZmZlY3QiLCJ1c2VSZWYiLCJsb2dFdmVudCIsInVzZUFwcFN0YXRlIiwidXNlU2V0QXBwU3RhdGUiLCJQZXJtaXNzaW9uTW9kZSIsImdldElzUmVtb3RlTW9kZSIsImdldEthaXJvc0FjdGl2ZSIsImdldE1haW5UaHJlYWRBZ2VudFR5cGUiLCJnZXRPcmlnaW5hbEN3ZCIsImdldFNka0JldGFzIiwiZ2V0U2Vzc2lvbklkIiwiREVGQVVMVF9PVVRQVVRfU1RZTEVfTkFNRSIsInVzZU5vdGlmaWNhdGlvbnMiLCJnZXRUb3RhbEFQSUR1cmF0aW9uIiwiZ2V0VG90YWxDb3N0IiwiZ2V0VG90YWxEdXJhdGlvbiIsImdldFRvdGFsSW5wdXRUb2tlbnMiLCJnZXRUb3RhbExpbmVzQWRkZWQiLCJnZXRUb3RhbExpbmVzUmVtb3ZlZCIsImdldFRvdGFsT3V0cHV0VG9rZW5zIiwidXNlTWFpbkxvb3BNb2RlbCIsIlJlYWRvbmx5U2V0dGluZ3MiLCJ1c2VTZXR0aW5ncyIsIkFuc2kiLCJCb3giLCJUZXh0IiwiZ2V0UmF3VXRpbGl6YXRpb24iLCJNZXNzYWdlIiwiU3RhdHVzTGluZUNvbW1hbmRJbnB1dCIsIlZpbU1vZGUiLCJjaGVja0hhc1RydXN0RGlhbG9nQWNjZXB0ZWQiLCJjYWxjdWxhdGVDb250ZXh0UGVyY2VudGFnZXMiLCJnZXRDb250ZXh0V2luZG93Rm9yTW9kZWwiLCJnZXRDd2QiLCJsb2dGb3JEZWJ1Z2dpbmciLCJpc0Z1bGxzY3JlZW5FbnZFbmFibGVkIiwiY3JlYXRlQmFzZUhvb2tJbnB1dCIsImV4ZWN1dGVTdGF0dXNMaW5lQ29tbWFuZCIsImdldExhc3RBc3Npc3RhbnRNZXNzYWdlIiwiZ2V0UnVudGltZU1haW5Mb29wTW9kZWwiLCJNb2RlbE5hbWUiLCJyZW5kZXJNb2RlbE5hbWUiLCJnZXRDdXJyZW50U2Vzc2lvblRpdGxlIiwiZG9lc01vc3RSZWNlbnRBc3Npc3RhbnRNZXNzYWdlRXhjZWVkMjAwayIsImdldEN1cnJlbnRVc2FnZSIsImdldEN1cnJlbnRXb3JrdHJlZVNlc3Npb24iLCJpc1ZpbU1vZGVFbmFibGVkIiwic3RhdHVzTGluZVNob3VsZERpc3BsYXkiLCJzZXR0aW5ncyIsInN0YXR1c0xpbmUiLCJ1bmRlZmluZWQiLCJidWlsZFN0YXR1c0xpbmVDb21tYW5kSW5wdXQiLCJwZXJtaXNzaW9uTW9kZSIsImV4Y2VlZHMyMDBrVG9rZW5zIiwibWVzc2FnZXMiLCJhZGRlZERpcnMiLCJtYWluTG9vcE1vZGVsIiwidmltTW9kZSIsImFnZW50VHlwZSIsIndvcmt0cmVlU2Vzc2lvbiIsInJ1bnRpbWVNb2RlbCIsIm91dHB1dFN0eWxlTmFtZSIsIm91dHB1dFN0eWxlIiwiY3VycmVudFVzYWdlIiwiY29udGV4dFdpbmRvd1NpemUiLCJjb250ZXh0UGVyY2VudGFnZXMiLCJzZXNzaW9uSWQiLCJzZXNzaW9uTmFtZSIsInJhd1V0aWwiLCJyYXRlTGltaXRzIiwiZml2ZV9ob3VyIiwidXNlZF9wZXJjZW50YWdlIiwidXRpbGl6YXRpb24iLCJyZXNldHNfYXQiLCJzZXZlbl9kYXkiLCJzZXNzaW9uX25hbWUiLCJtb2RlbCIsImlkIiwiZGlzcGxheV9uYW1lIiwid29ya3NwYWNlIiwiY3VycmVudF9kaXIiLCJwcm9qZWN0X2RpciIsImFkZGVkX2RpcnMiLCJ2ZXJzaW9uIiwiTUFDUk8iLCJWRVJTSU9OIiwib3V0cHV0X3N0eWxlIiwibmFtZSIsImNvc3QiLCJ0b3RhbF9jb3N0X3VzZCIsInRvdGFsX2R1cmF0aW9uX21zIiwidG90YWxfYXBpX2R1cmF0aW9uX21zIiwidG90YWxfbGluZXNfYWRkZWQiLCJ0b3RhbF9saW5lc19yZW1vdmVkIiwiY29udGV4dF93aW5kb3ciLCJ0b3RhbF9pbnB1dF90b2tlbnMiLCJ0b3RhbF9vdXRwdXRfdG9rZW5zIiwiY29udGV4dF93aW5kb3dfc2l6ZSIsImN1cnJlbnRfdXNhZ2UiLCJ1c2VkIiwicmVtYWluaW5nX3BlcmNlbnRhZ2UiLCJyZW1haW5pbmciLCJleGNlZWRzXzIwMGtfdG9rZW5zIiwicmF0ZV9saW1pdHMiLCJ2aW0iLCJtb2RlIiwiYWdlbnQiLCJyZW1vdGUiLCJzZXNzaW9uX2lkIiwid29ya3RyZWUiLCJ3b3JrdHJlZU5hbWUiLCJwYXRoIiwid29ya3RyZWVQYXRoIiwiYnJhbmNoIiwid29ya3RyZWVCcmFuY2giLCJvcmlnaW5hbF9jd2QiLCJvcmlnaW5hbEN3ZCIsIm9yaWdpbmFsX2JyYW5jaCIsIm9yaWdpbmFsQnJhbmNoIiwiUHJvcHMiLCJtZXNzYWdlc1JlZiIsIlJlZk9iamVjdCIsImxhc3RBc3Npc3RhbnRNZXNzYWdlSWQiLCJnZXRMYXN0QXNzaXN0YW50TWVzc2FnZUlkIiwidXVpZCIsIlN0YXR1c0xpbmVJbm5lciIsIlJlYWN0Tm9kZSIsImFib3J0Q29udHJvbGxlclJlZiIsIkFib3J0Q29udHJvbGxlciIsInMiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJhZGRpdGlvbmFsV29ya2luZ0RpcmVjdG9yaWVzIiwic3RhdHVzTGluZVRleHQiLCJzZXRBcHBTdGF0ZSIsImFkZE5vdGlmaWNhdGlvbiIsInNldHRpbmdzUmVmIiwiY3VycmVudCIsInZpbU1vZGVSZWYiLCJwZXJtaXNzaW9uTW9kZVJlZiIsImFkZGVkRGlyc1JlZiIsIm1haW5Mb29wTW9kZWxSZWYiLCJwcmV2aW91c1N0YXRlUmVmIiwibWVzc2FnZUlkIiwiZGVib3VuY2VUaW1lclJlZiIsIlJldHVyblR5cGUiLCJzZXRUaW1lb3V0IiwibG9nTmV4dFJlc3VsdFJlZiIsImRvVXBkYXRlIiwiYWJvcnQiLCJjb250cm9sbGVyIiwibXNncyIsImxvZ1Jlc3VsdCIsImN1cnJlbnRNZXNzYWdlSWQiLCJzdGF0dXNJbnB1dCIsIkFycmF5IiwiZnJvbSIsImtleXMiLCJ0ZXh0Iiwic2lnbmFsIiwiYWJvcnRlZCIsInByZXYiLCJzY2hlZHVsZVVwZGF0ZSIsImNsZWFyVGltZW91dCIsInJlZiIsInN0YXR1c0xpbmVDb21tYW5kIiwiY29tbWFuZCIsImlzRmlyc3RTZXR0aW5nc1JlbmRlciIsImNvbW1hbmRfbGVuZ3RoIiwibGVuZ3RoIiwicGFkZGluZyIsImRpc2FibGVBbGxIb29rcyIsImxldmVsIiwia2V5IiwiY29sb3IiLCJwcmlvcml0eSIsInBhZGRpbmdYIiwiU3RhdHVzTGluZSJdLCJzb3VyY2VzIjpbIlN0YXR1c0xpbmUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBtZW1vLCB1c2VDYWxsYmFjaywgdXNlRWZmZWN0LCB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlLCB1c2VTZXRBcHBTdGF0ZSB9IGZyb20gJ3NyYy9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvbk1vZGUgfSBmcm9tICdzcmMvdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvbk1vZGUuanMnXG5pbXBvcnQge1xuICBnZXRJc1JlbW90ZU1vZGUsXG4gIGdldEthaXJvc0FjdGl2ZSxcbiAgZ2V0TWFpblRocmVhZEFnZW50VHlwZSxcbiAgZ2V0T3JpZ2luYWxDd2QsXG4gIGdldFNka0JldGFzLFxuICBnZXRTZXNzaW9uSWQsXG59IGZyb20gJy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7IERFRkFVTFRfT1VUUFVUX1NUWUxFX05BTUUgfSBmcm9tICcuLi9jb25zdGFudHMvb3V0cHV0U3R5bGVzLmpzJ1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJy4uL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB7XG4gIGdldFRvdGFsQVBJRHVyYXRpb24sXG4gIGdldFRvdGFsQ29zdCxcbiAgZ2V0VG90YWxEdXJhdGlvbixcbiAgZ2V0VG90YWxJbnB1dFRva2VucyxcbiAgZ2V0VG90YWxMaW5lc0FkZGVkLFxuICBnZXRUb3RhbExpbmVzUmVtb3ZlZCxcbiAgZ2V0VG90YWxPdXRwdXRUb2tlbnMsXG59IGZyb20gJy4uL2Nvc3QtdHJhY2tlci5qcydcbmltcG9ydCB7IHVzZU1haW5Mb29wTW9kZWwgfSBmcm9tICcuLi9ob29rcy91c2VNYWluTG9vcE1vZGVsLmpzJ1xuaW1wb3J0IHsgdHlwZSBSZWFkb25seVNldHRpbmdzLCB1c2VTZXR0aW5ncyB9IGZyb20gJy4uL2hvb2tzL3VzZVNldHRpbmdzLmpzJ1xuaW1wb3J0IHsgQW5zaSwgQm94LCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgZ2V0UmF3VXRpbGl6YXRpb24gfSBmcm9tICcuLi9zZXJ2aWNlcy9jbGF1ZGVBaUxpbWl0cy5qcydcbmltcG9ydCB0eXBlIHsgTWVzc2FnZSB9IGZyb20gJy4uL3R5cGVzL21lc3NhZ2UuanMnXG5pbXBvcnQgdHlwZSB7IFN0YXR1c0xpbmVDb21tYW5kSW5wdXQgfSBmcm9tICcuLi90eXBlcy9zdGF0dXNMaW5lLmpzJ1xuaW1wb3J0IHR5cGUgeyBWaW1Nb2RlIH0gZnJvbSAnLi4vdHlwZXMvdGV4dElucHV0VHlwZXMuanMnXG5pbXBvcnQgeyBjaGVja0hhc1RydXN0RGlhbG9nQWNjZXB0ZWQgfSBmcm9tICcuLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQge1xuICBjYWxjdWxhdGVDb250ZXh0UGVyY2VudGFnZXMsXG4gIGdldENvbnRleHRXaW5kb3dGb3JNb2RlbCxcbn0gZnJvbSAnLi4vdXRpbHMvY29udGV4dC5qcydcbmltcG9ydCB7IGdldEN3ZCB9IGZyb20gJy4uL3V0aWxzL2N3ZC5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHsgaXNGdWxsc2NyZWVuRW52RW5hYmxlZCB9IGZyb20gJy4uL3V0aWxzL2Z1bGxzY3JlZW4uanMnXG5pbXBvcnQge1xuICBjcmVhdGVCYXNlSG9va0lucHV0LFxuICBleGVjdXRlU3RhdHVzTGluZUNvbW1hbmQsXG59IGZyb20gJy4uL3V0aWxzL2hvb2tzLmpzJ1xuaW1wb3J0IHsgZ2V0TGFzdEFzc2lzdGFudE1lc3NhZ2UgfSBmcm9tICcuLi91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB7XG4gIGdldFJ1bnRpbWVNYWluTG9vcE1vZGVsLFxuICB0eXBlIE1vZGVsTmFtZSxcbiAgcmVuZGVyTW9kZWxOYW1lLFxufSBmcm9tICcuLi91dGlscy9tb2RlbC9tb2RlbC5qcydcbmltcG9ydCB7IGdldEN1cnJlbnRTZXNzaW9uVGl0bGUgfSBmcm9tICcuLi91dGlscy9zZXNzaW9uU3RvcmFnZS5qcydcbmltcG9ydCB7XG4gIGRvZXNNb3N0UmVjZW50QXNzaXN0YW50TWVzc2FnZUV4Y2VlZDIwMGssXG4gIGdldEN1cnJlbnRVc2FnZSxcbn0gZnJvbSAnLi4vdXRpbHMvdG9rZW5zLmpzJ1xuaW1wb3J0IHsgZ2V0Q3VycmVudFdvcmt0cmVlU2Vzc2lvbiB9IGZyb20gJy4uL3V0aWxzL3dvcmt0cmVlLmpzJ1xuaW1wb3J0IHsgaXNWaW1Nb2RlRW5hYmxlZCB9IGZyb20gJy4vUHJvbXB0SW5wdXQvdXRpbHMuanMnXG5cbmV4cG9ydCBmdW5jdGlvbiBzdGF0dXNMaW5lU2hvdWxkRGlzcGxheShzZXR0aW5nczogUmVhZG9ubHlTZXR0aW5ncyk6IGJvb2xlYW4ge1xuICAvLyBBc3Npc3RhbnQgbW9kZTogc3RhdHVzbGluZSBmaWVsZHMgKG1vZGVsLCBwZXJtaXNzaW9uIG1vZGUsIGN3ZCkgcmVmbGVjdCB0aGVcbiAgLy8gUkVQTC9kYWVtb24gcHJvY2Vzcywgbm90IHdoYXQgdGhlIGFnZW50IGNoaWxkIGlzIGFjdHVhbGx5IHJ1bm5pbmcuIEhpZGUgaXQuXG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSAmJiBnZXRLYWlyb3NBY3RpdmUoKSkgcmV0dXJuIGZhbHNlXG4gIHJldHVybiBzZXR0aW5ncz8uc3RhdHVzTGluZSAhPT0gdW5kZWZpbmVkXG59XG5cbmZ1bmN0aW9uIGJ1aWxkU3RhdHVzTGluZUNvbW1hbmRJbnB1dChcbiAgcGVybWlzc2lvbk1vZGU6IFBlcm1pc3Npb25Nb2RlLFxuICBleGNlZWRzMjAwa1Rva2VuczogYm9vbGVhbixcbiAgc2V0dGluZ3M6IFJlYWRvbmx5U2V0dGluZ3MsXG4gIG1lc3NhZ2VzOiBNZXNzYWdlW10sXG4gIGFkZGVkRGlyczogc3RyaW5nW10sXG4gIG1haW5Mb29wTW9kZWw6IE1vZGVsTmFtZSxcbiAgdmltTW9kZT86IFZpbU1vZGUsXG4pOiBTdGF0dXNMaW5lQ29tbWFuZElucHV0IHtcbiAgY29uc3QgYWdlbnRUeXBlID0gZ2V0TWFpblRocmVhZEFnZW50VHlwZSgpXG4gIGNvbnN0IHdvcmt0cmVlU2Vzc2lvbiA9IGdldEN1cnJlbnRXb3JrdHJlZVNlc3Npb24oKVxuICBjb25zdCBydW50aW1lTW9kZWwgPSBnZXRSdW50aW1lTWFpbkxvb3BNb2RlbCh7XG4gICAgcGVybWlzc2lvbk1vZGUsXG4gICAgbWFpbkxvb3BNb2RlbCxcbiAgICBleGNlZWRzMjAwa1Rva2VucyxcbiAgfSlcbiAgY29uc3Qgb3V0cHV0U3R5bGVOYW1lID0gc2V0dGluZ3M/Lm91dHB1dFN0eWxlIHx8IERFRkFVTFRfT1VUUFVUX1NUWUxFX05BTUVcblxuICBjb25zdCBjdXJyZW50VXNhZ2UgPSBnZXRDdXJyZW50VXNhZ2UobWVzc2FnZXMpXG4gIGNvbnN0IGNvbnRleHRXaW5kb3dTaXplID0gZ2V0Q29udGV4dFdpbmRvd0Zvck1vZGVsKFxuICAgIHJ1bnRpbWVNb2RlbCxcbiAgICBnZXRTZGtCZXRhcygpLFxuICApXG4gIGNvbnN0IGNvbnRleHRQZXJjZW50YWdlcyA9IGNhbGN1bGF0ZUNvbnRleHRQZXJjZW50YWdlcyhcbiAgICBjdXJyZW50VXNhZ2UsXG4gICAgY29udGV4dFdpbmRvd1NpemUsXG4gIClcblxuICBjb25zdCBzZXNzaW9uSWQgPSBnZXRTZXNzaW9uSWQoKVxuICBjb25zdCBzZXNzaW9uTmFtZSA9IGdldEN1cnJlbnRTZXNzaW9uVGl0bGUoc2Vzc2lvbklkKVxuICBjb25zdCByYXdVdGlsID0gZ2V0UmF3VXRpbGl6YXRpb24oKVxuICBjb25zdCByYXRlTGltaXRzOiBTdGF0dXNMaW5lQ29tbWFuZElucHV0WydyYXRlX2xpbWl0cyddID0ge1xuICAgIC4uLihyYXdVdGlsLmZpdmVfaG91ciAmJiB7XG4gICAgICBmaXZlX2hvdXI6IHtcbiAgICAgICAgdXNlZF9wZXJjZW50YWdlOiByYXdVdGlsLmZpdmVfaG91ci51dGlsaXphdGlvbiAqIDEwMCxcbiAgICAgICAgcmVzZXRzX2F0OiByYXdVdGlsLmZpdmVfaG91ci5yZXNldHNfYXQsXG4gICAgICB9LFxuICAgIH0pLFxuICAgIC4uLihyYXdVdGlsLnNldmVuX2RheSAmJiB7XG4gICAgICBzZXZlbl9kYXk6IHtcbiAgICAgICAgdXNlZF9wZXJjZW50YWdlOiByYXdVdGlsLnNldmVuX2RheS51dGlsaXphdGlvbiAqIDEwMCxcbiAgICAgICAgcmVzZXRzX2F0OiByYXdVdGlsLnNldmVuX2RheS5yZXNldHNfYXQsXG4gICAgICB9LFxuICAgIH0pLFxuICB9XG4gIHJldHVybiB7XG4gICAgLi4uY3JlYXRlQmFzZUhvb2tJbnB1dCgpLFxuICAgIC4uLihzZXNzaW9uTmFtZSAmJiB7IHNlc3Npb25fbmFtZTogc2Vzc2lvbk5hbWUgfSksXG4gICAgbW9kZWw6IHtcbiAgICAgIGlkOiBydW50aW1lTW9kZWwsXG4gICAgICBkaXNwbGF5X25hbWU6IHJlbmRlck1vZGVsTmFtZShydW50aW1lTW9kZWwpLFxuICAgIH0sXG4gICAgd29ya3NwYWNlOiB7XG4gICAgICBjdXJyZW50X2RpcjogZ2V0Q3dkKCksXG4gICAgICBwcm9qZWN0X2RpcjogZ2V0T3JpZ2luYWxDd2QoKSxcbiAgICAgIGFkZGVkX2RpcnM6IGFkZGVkRGlycyxcbiAgICB9LFxuICAgIHZlcnNpb246IE1BQ1JPLlZFUlNJT04sXG4gICAgb3V0cHV0X3N0eWxlOiB7XG4gICAgICBuYW1lOiBvdXRwdXRTdHlsZU5hbWUsXG4gICAgfSxcbiAgICBjb3N0OiB7XG4gICAgICB0b3RhbF9jb3N0X3VzZDogZ2V0VG90YWxDb3N0KCksXG4gICAgICB0b3RhbF9kdXJhdGlvbl9tczogZ2V0VG90YWxEdXJhdGlvbigpLFxuICAgICAgdG90YWxfYXBpX2R1cmF0aW9uX21zOiBnZXRUb3RhbEFQSUR1cmF0aW9uKCksXG4gICAgICB0b3RhbF9saW5lc19hZGRlZDogZ2V0VG90YWxMaW5lc0FkZGVkKCksXG4gICAgICB0b3RhbF9saW5lc19yZW1vdmVkOiBnZXRUb3RhbExpbmVzUmVtb3ZlZCgpLFxuICAgIH0sXG4gICAgY29udGV4dF93aW5kb3c6IHtcbiAgICAgIHRvdGFsX2lucHV0X3Rva2VuczogZ2V0VG90YWxJbnB1dFRva2VucygpLFxuICAgICAgdG90YWxfb3V0cHV0X3Rva2VuczogZ2V0VG90YWxPdXRwdXRUb2tlbnMoKSxcbiAgICAgIGNvbnRleHRfd2luZG93X3NpemU6IGNvbnRleHRXaW5kb3dTaXplLFxuICAgICAgY3VycmVudF91c2FnZTogY3VycmVudFVzYWdlLFxuICAgICAgdXNlZF9wZXJjZW50YWdlOiBjb250ZXh0UGVyY2VudGFnZXMudXNlZCxcbiAgICAgIHJlbWFpbmluZ19wZXJjZW50YWdlOiBjb250ZXh0UGVyY2VudGFnZXMucmVtYWluaW5nLFxuICAgIH0sXG4gICAgZXhjZWVkc18yMDBrX3Rva2VuczogZXhjZWVkczIwMGtUb2tlbnMsXG4gICAgLi4uKChyYXRlTGltaXRzLmZpdmVfaG91ciB8fCByYXRlTGltaXRzLnNldmVuX2RheSkgJiYge1xuICAgICAgcmF0ZV9saW1pdHM6IHJhdGVMaW1pdHMsXG4gICAgfSksXG4gICAgLi4uKGlzVmltTW9kZUVuYWJsZWQoKSAmJiB7XG4gICAgICB2aW06IHtcbiAgICAgICAgbW9kZTogdmltTW9kZSA/PyAnSU5TRVJUJyxcbiAgICAgIH0sXG4gICAgfSksXG4gICAgLi4uKGFnZW50VHlwZSAmJiB7XG4gICAgICBhZ2VudDoge1xuICAgICAgICBuYW1lOiBhZ2VudFR5cGUsXG4gICAgICB9LFxuICAgIH0pLFxuICAgIC4uLihnZXRJc1JlbW90ZU1vZGUoKSAmJiB7XG4gICAgICByZW1vdGU6IHtcbiAgICAgICAgc2Vzc2lvbl9pZDogZ2V0U2Vzc2lvbklkKCksXG4gICAgICB9LFxuICAgIH0pLFxuICAgIC4uLih3b3JrdHJlZVNlc3Npb24gJiYge1xuICAgICAgd29ya3RyZWU6IHtcbiAgICAgICAgbmFtZTogd29ya3RyZWVTZXNzaW9uLndvcmt0cmVlTmFtZSxcbiAgICAgICAgcGF0aDogd29ya3RyZWVTZXNzaW9uLndvcmt0cmVlUGF0aCxcbiAgICAgICAgYnJhbmNoOiB3b3JrdHJlZVNlc3Npb24ud29ya3RyZWVCcmFuY2gsXG4gICAgICAgIG9yaWdpbmFsX2N3ZDogd29ya3RyZWVTZXNzaW9uLm9yaWdpbmFsQ3dkLFxuICAgICAgICBvcmlnaW5hbF9icmFuY2g6IHdvcmt0cmVlU2Vzc2lvbi5vcmlnaW5hbEJyYW5jaCxcbiAgICAgIH0sXG4gICAgfSksXG4gIH1cbn1cblxudHlwZSBQcm9wcyA9IHtcbiAgLy8gbWVzc2FnZXMgc3RheXMgYmVoaW5kIGEgcmVmIChyZWFkIG9ubHkgaW4gdGhlIGRlYm91bmNlZCBjYWxsYmFjayk7XG4gIC8vIGxhc3RBc3Npc3RhbnRNZXNzYWdlSWQgaXMgdGhlIGFjdHVhbCByZS1yZW5kZXIgdHJpZ2dlci5cbiAgbWVzc2FnZXNSZWY6IFJlYWN0LlJlZk9iamVjdDxNZXNzYWdlW10+XG4gIGxhc3RBc3Npc3RhbnRNZXNzYWdlSWQ6IHN0cmluZyB8IG51bGxcbiAgdmltTW9kZT86IFZpbU1vZGVcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldExhc3RBc3Npc3RhbnRNZXNzYWdlSWQobWVzc2FnZXM6IE1lc3NhZ2VbXSk6IHN0cmluZyB8IG51bGwge1xuICByZXR1cm4gZ2V0TGFzdEFzc2lzdGFudE1lc3NhZ2UobWVzc2FnZXMpPy51dWlkID8/IG51bGxcbn1cblxuZnVuY3Rpb24gU3RhdHVzTGluZUlubmVyKHtcbiAgbWVzc2FnZXNSZWYsXG4gIGxhc3RBc3Npc3RhbnRNZXNzYWdlSWQsXG4gIHZpbU1vZGUsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGFib3J0Q29udHJvbGxlclJlZiA9IHVzZVJlZjxBYm9ydENvbnRyb2xsZXIgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZClcbiAgY29uc3QgcGVybWlzc2lvbk1vZGUgPSB1c2VBcHBTdGF0ZShzID0+IHMudG9vbFBlcm1pc3Npb25Db250ZXh0Lm1vZGUpXG4gIGNvbnN0IGFkZGl0aW9uYWxXb3JraW5nRGlyZWN0b3JpZXMgPSB1c2VBcHBTdGF0ZShcbiAgICBzID0+IHMudG9vbFBlcm1pc3Npb25Db250ZXh0LmFkZGl0aW9uYWxXb3JraW5nRGlyZWN0b3JpZXMsXG4gIClcbiAgY29uc3Qgc3RhdHVzTGluZVRleHQgPSB1c2VBcHBTdGF0ZShzID0+IHMuc3RhdHVzTGluZVRleHQpXG4gIGNvbnN0IHNldEFwcFN0YXRlID0gdXNlU2V0QXBwU3RhdGUoKVxuICBjb25zdCBzZXR0aW5ncyA9IHVzZVNldHRpbmdzKClcbiAgY29uc3QgeyBhZGROb3RpZmljYXRpb24gfSA9IHVzZU5vdGlmaWNhdGlvbnMoKVxuICAvLyBBcHBTdGF0ZS1zb3VyY2VkIG1vZGVsIOKAlCBzYW1lIHNvdXJjZSBhcyBBUEkgcmVxdWVzdHMuIGdldE1haW5Mb29wTW9kZWwoKVxuICAvLyByZS1yZWFkcyBzZXR0aW5ncy5qc29uIG9uIGV2ZXJ5IGNhbGwsIHNvIGFub3RoZXIgc2Vzc2lvbidzIC9tb2RlbCB3cml0ZVxuICAvLyB3b3VsZCBsZWFrIGludG8gdGhpcyBzZXNzaW9uJ3Mgc3RhdHVzbGluZSAoYW50aHJvcGljcy9jbGF1ZGUtY29kZSMzNzU5NikuXG4gIGNvbnN0IG1haW5Mb29wTW9kZWwgPSB1c2VNYWluTG9vcE1vZGVsKClcblxuICAvLyBLZWVwIGxhdGVzdCB2YWx1ZXMgaW4gcmVmcyBmb3Igc3RhYmxlIGNhbGxiYWNrIGFjY2Vzc1xuICBjb25zdCBzZXR0aW5nc1JlZiA9IHVzZVJlZihzZXR0aW5ncylcbiAgc2V0dGluZ3NSZWYuY3VycmVudCA9IHNldHRpbmdzXG4gIGNvbnN0IHZpbU1vZGVSZWYgPSB1c2VSZWYodmltTW9kZSlcbiAgdmltTW9kZVJlZi5jdXJyZW50ID0gdmltTW9kZVxuICBjb25zdCBwZXJtaXNzaW9uTW9kZVJlZiA9IHVzZVJlZihwZXJtaXNzaW9uTW9kZSlcbiAgcGVybWlzc2lvbk1vZGVSZWYuY3VycmVudCA9IHBlcm1pc3Npb25Nb2RlXG4gIGNvbnN0IGFkZGVkRGlyc1JlZiA9IHVzZVJlZihhZGRpdGlvbmFsV29ya2luZ0RpcmVjdG9yaWVzKVxuICBhZGRlZERpcnNSZWYuY3VycmVudCA9IGFkZGl0aW9uYWxXb3JraW5nRGlyZWN0b3JpZXNcbiAgY29uc3QgbWFpbkxvb3BNb2RlbFJlZiA9IHVzZVJlZihtYWluTG9vcE1vZGVsKVxuICBtYWluTG9vcE1vZGVsUmVmLmN1cnJlbnQgPSBtYWluTG9vcE1vZGVsXG5cbiAgLy8gVHJhY2sgcHJldmlvdXMgc3RhdGUgdG8gZGV0ZWN0IGNoYW5nZXMgYW5kIGNhY2hlIGV4cGVuc2l2ZSBjYWxjdWxhdGlvbnNcbiAgY29uc3QgcHJldmlvdXNTdGF0ZVJlZiA9IHVzZVJlZjx7XG4gICAgbWVzc2FnZUlkOiBzdHJpbmcgfCBudWxsXG4gICAgZXhjZWVkczIwMGtUb2tlbnM6IGJvb2xlYW5cbiAgICBwZXJtaXNzaW9uTW9kZTogUGVybWlzc2lvbk1vZGVcbiAgICB2aW1Nb2RlOiBWaW1Nb2RlIHwgdW5kZWZpbmVkXG4gICAgbWFpbkxvb3BNb2RlbDogTW9kZWxOYW1lXG4gIH0+KHtcbiAgICBtZXNzYWdlSWQ6IG51bGwsXG4gICAgZXhjZWVkczIwMGtUb2tlbnM6IGZhbHNlLFxuICAgIHBlcm1pc3Npb25Nb2RlLFxuICAgIHZpbU1vZGUsXG4gICAgbWFpbkxvb3BNb2RlbCxcbiAgfSlcblxuICAvLyBEZWJvdW5jZSB0aW1lciByZWZcbiAgY29uc3QgZGVib3VuY2VUaW1lclJlZiA9IHVzZVJlZjxSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IHVuZGVmaW5lZD4oXG4gICAgdW5kZWZpbmVkLFxuICApXG5cbiAgLy8gVHJ1ZSB3aGVuIHRoZSBuZXh0IGludm9jYXRpb24gc2hvdWxkIGxvZyBpdHMgcmVzdWx0IChmaXJzdCBydW4gb3IgYWZ0ZXIgc2V0dGluZ3MgcmVsb2FkKVxuICBjb25zdCBsb2dOZXh0UmVzdWx0UmVmID0gdXNlUmVmKHRydWUpXG5cbiAgLy8gU3RhYmxlIHVwZGF0ZSBmdW5jdGlvbiDigJQgcmVhZHMgbGF0ZXN0IHZhbHVlcyBmcm9tIHJlZnNcbiAgY29uc3QgZG9VcGRhdGUgPSB1c2VDYWxsYmFjayhhc3luYyAoKSA9PiB7XG4gICAgLy8gQ2FuY2VsIGFueSBpbi1mbGlnaHQgcmVxdWVzdHNcbiAgICBhYm9ydENvbnRyb2xsZXJSZWYuY3VycmVudD8uYWJvcnQoKVxuXG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKVxuICAgIGFib3J0Q29udHJvbGxlclJlZi5jdXJyZW50ID0gY29udHJvbGxlclxuXG4gICAgY29uc3QgbXNncyA9IG1lc3NhZ2VzUmVmLmN1cnJlbnRcblxuICAgIGNvbnN0IGxvZ1Jlc3VsdCA9IGxvZ05leHRSZXN1bHRSZWYuY3VycmVudFxuICAgIGxvZ05leHRSZXN1bHRSZWYuY3VycmVudCA9IGZhbHNlXG5cbiAgICB0cnkge1xuICAgICAgbGV0IGV4Y2VlZHMyMDBrVG9rZW5zID0gcHJldmlvdXNTdGF0ZVJlZi5jdXJyZW50LmV4Y2VlZHMyMDBrVG9rZW5zXG5cbiAgICAgIC8vIE9ubHkgcmVjYWxjdWxhdGUgMjAwayBjaGVjayBpZiBtZXNzYWdlcyBjaGFuZ2VkXG4gICAgICBjb25zdCBjdXJyZW50TWVzc2FnZUlkID0gZ2V0TGFzdEFzc2lzdGFudE1lc3NhZ2VJZChtc2dzKVxuICAgICAgaWYgKGN1cnJlbnRNZXNzYWdlSWQgIT09IHByZXZpb3VzU3RhdGVSZWYuY3VycmVudC5tZXNzYWdlSWQpIHtcbiAgICAgICAgZXhjZWVkczIwMGtUb2tlbnMgPSBkb2VzTW9zdFJlY2VudEFzc2lzdGFudE1lc3NhZ2VFeGNlZWQyMDBrKG1zZ3MpXG4gICAgICAgIHByZXZpb3VzU3RhdGVSZWYuY3VycmVudC5tZXNzYWdlSWQgPSBjdXJyZW50TWVzc2FnZUlkXG4gICAgICAgIHByZXZpb3VzU3RhdGVSZWYuY3VycmVudC5leGNlZWRzMjAwa1Rva2VucyA9IGV4Y2VlZHMyMDBrVG9rZW5zXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHN0YXR1c0lucHV0ID0gYnVpbGRTdGF0dXNMaW5lQ29tbWFuZElucHV0KFxuICAgICAgICBwZXJtaXNzaW9uTW9kZVJlZi5jdXJyZW50LFxuICAgICAgICBleGNlZWRzMjAwa1Rva2VucyxcbiAgICAgICAgc2V0dGluZ3NSZWYuY3VycmVudCxcbiAgICAgICAgbXNncyxcbiAgICAgICAgQXJyYXkuZnJvbShhZGRlZERpcnNSZWYuY3VycmVudC5rZXlzKCkpLFxuICAgICAgICBtYWluTG9vcE1vZGVsUmVmLmN1cnJlbnQsXG4gICAgICAgIHZpbU1vZGVSZWYuY3VycmVudCxcbiAgICAgIClcblxuICAgICAgY29uc3QgdGV4dCA9IGF3YWl0IGV4ZWN1dGVTdGF0dXNMaW5lQ29tbWFuZChcbiAgICAgICAgc3RhdHVzSW5wdXQsXG4gICAgICAgIGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgICAgICB1bmRlZmluZWQsXG4gICAgICAgIGxvZ1Jlc3VsdCxcbiAgICAgIClcbiAgICAgIGlmICghY29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkge1xuICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgICBpZiAocHJldi5zdGF0dXNMaW5lVGV4dCA9PT0gdGV4dCkgcmV0dXJuIHByZXZcbiAgICAgICAgICByZXR1cm4geyAuLi5wcmV2LCBzdGF0dXNMaW5lVGV4dDogdGV4dCB9XG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfSBjYXRjaCB7XG4gICAgICAvLyBTaWxlbnRseSBpZ25vcmUgZXJyb3JzIGluIHN0YXR1cyBsaW5lIHVwZGF0ZXNcbiAgICB9XG4gIH0sIFttZXNzYWdlc1JlZiwgc2V0QXBwU3RhdGVdKVxuXG4gIC8vIFN0YWJsZSBkZWJvdW5jZWQgc2NoZWR1bGUgZnVuY3Rpb24g4oCUIG5vIGRlcHMsIHVzZXMgcmVmc1xuICBjb25zdCBzY2hlZHVsZVVwZGF0ZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoZGVib3VuY2VUaW1lclJlZi5jdXJyZW50ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNsZWFyVGltZW91dChkZWJvdW5jZVRpbWVyUmVmLmN1cnJlbnQpXG4gICAgfVxuICAgIGRlYm91bmNlVGltZXJSZWYuY3VycmVudCA9IHNldFRpbWVvdXQoXG4gICAgICAocmVmLCBkb1VwZGF0ZSkgPT4ge1xuICAgICAgICByZWYuY3VycmVudCA9IHVuZGVmaW5lZFxuICAgICAgICB2b2lkIGRvVXBkYXRlKClcbiAgICAgIH0sXG4gICAgICAzMDAsXG4gICAgICBkZWJvdW5jZVRpbWVyUmVmLFxuICAgICAgZG9VcGRhdGUsXG4gICAgKVxuICB9LCBbZG9VcGRhdGVdKVxuXG4gIC8vIE9ubHkgdHJpZ2dlciB1cGRhdGUgd2hlbiBhc3Npc3RhbnQgbWVzc2FnZSwgcGVybWlzc2lvbiBtb2RlLCB2aW0gbW9kZSwgb3IgbW9kZWwgYWN0dWFsbHkgY2hhbmdlc1xuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChcbiAgICAgIGxhc3RBc3Npc3RhbnRNZXNzYWdlSWQgIT09IHByZXZpb3VzU3RhdGVSZWYuY3VycmVudC5tZXNzYWdlSWQgfHxcbiAgICAgIHBlcm1pc3Npb25Nb2RlICE9PSBwcmV2aW91c1N0YXRlUmVmLmN1cnJlbnQucGVybWlzc2lvbk1vZGUgfHxcbiAgICAgIHZpbU1vZGUgIT09IHByZXZpb3VzU3RhdGVSZWYuY3VycmVudC52aW1Nb2RlIHx8XG4gICAgICBtYWluTG9vcE1vZGVsICE9PSBwcmV2aW91c1N0YXRlUmVmLmN1cnJlbnQubWFpbkxvb3BNb2RlbFxuICAgICkge1xuICAgICAgLy8gRG9uJ3QgdXBkYXRlIG1lc3NhZ2VJZCBoZXJlIOKAlCBsZXQgZG9VcGRhdGUgaGFuZGxlIGl0IHNvXG4gICAgICAvLyBleGNlZWRzMjAwa1Rva2VucyBpcyByZWNhbGN1bGF0ZWQgd2l0aCB0aGUgbGF0ZXN0IG1lc3NhZ2VzXG4gICAgICBwcmV2aW91c1N0YXRlUmVmLmN1cnJlbnQucGVybWlzc2lvbk1vZGUgPSBwZXJtaXNzaW9uTW9kZVxuICAgICAgcHJldmlvdXNTdGF0ZVJlZi5jdXJyZW50LnZpbU1vZGUgPSB2aW1Nb2RlXG4gICAgICBwcmV2aW91c1N0YXRlUmVmLmN1cnJlbnQubWFpbkxvb3BNb2RlbCA9IG1haW5Mb29wTW9kZWxcbiAgICAgIHNjaGVkdWxlVXBkYXRlKClcbiAgICB9XG4gIH0sIFtcbiAgICBsYXN0QXNzaXN0YW50TWVzc2FnZUlkLFxuICAgIHBlcm1pc3Npb25Nb2RlLFxuICAgIHZpbU1vZGUsXG4gICAgbWFpbkxvb3BNb2RlbCxcbiAgICBzY2hlZHVsZVVwZGF0ZSxcbiAgXSlcblxuICAvLyBXaGVuIHRoZSBzdGF0dXNMaW5lIGNvbW1hbmQgY2hhbmdlcyAoaG90IHJlbG9hZCksIGxvZyB0aGUgbmV4dCByZXN1bHRcbiAgY29uc3Qgc3RhdHVzTGluZUNvbW1hbmQgPSBzZXR0aW5ncz8uc3RhdHVzTGluZT8uY29tbWFuZFxuICBjb25zdCBpc0ZpcnN0U2V0dGluZ3NSZW5kZXIgPSB1c2VSZWYodHJ1ZSlcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoaXNGaXJzdFNldHRpbmdzUmVuZGVyLmN1cnJlbnQpIHtcbiAgICAgIGlzRmlyc3RTZXR0aW5nc1JlbmRlci5jdXJyZW50ID0gZmFsc2VcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBsb2dOZXh0UmVzdWx0UmVmLmN1cnJlbnQgPSB0cnVlXG4gICAgdm9pZCBkb1VwZGF0ZSgpXG4gIH0sIFtzdGF0dXNMaW5lQ29tbWFuZCwgZG9VcGRhdGVdKVxuXG4gIC8vIFNlcGFyYXRlIGVmZmVjdCBmb3IgbG9nZ2luZyBvbiBtb3VudFxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IHN0YXR1c0xpbmUgPSBzZXR0aW5ncz8uc3RhdHVzTGluZVxuICAgIGlmIChzdGF0dXNMaW5lKSB7XG4gICAgICBsb2dFdmVudCgndGVuZ3Vfc3RhdHVzX2xpbmVfbW91bnQnLCB7XG4gICAgICAgIGNvbW1hbmRfbGVuZ3RoOiBzdGF0dXNMaW5lLmNvbW1hbmQubGVuZ3RoLFxuICAgICAgICBwYWRkaW5nOiBzdGF0dXNMaW5lLnBhZGRpbmcsXG4gICAgICB9KVxuICAgICAgLy8gTG9nIGlmIHN0YXR1cyBsaW5lIGlzIGNvbmZpZ3VyZWQgYnV0IGRpc2FibGVkIGJ5IGRpc2FibGVBbGxIb29rc1xuICAgICAgaWYgKHNldHRpbmdzLmRpc2FibGVBbGxIb29rcyA9PT0gdHJ1ZSkge1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgJ1N0YXR1cyBsaW5lIGlzIGNvbmZpZ3VyZWQgYnV0IGRpc2FibGVBbGxIb29rcyBpcyB0cnVlJyxcbiAgICAgICAgICB7IGxldmVsOiAnd2FybicgfSxcbiAgICAgICAgKVxuICAgICAgfVxuICAgICAgLy8gZXhlY3V0ZVN0YXR1c0xpbmVDb21tYW5kIChob29rcy50cykgcmV0dXJucyB1bmRlZmluZWQgd2hlbiB0cnVzdCBpc1xuICAgICAgLy8gYmxvY2tlZCDigJQgc3RhdHVzTGluZVRleHQgc3RheXMgdW5kZWZpbmVkIGZvcmV2ZXIsIHVzZXIgc2VlcyBub3RoaW5nLFxuICAgICAgLy8gYW5kIHRlbmd1X3N0YXR1c19saW5lX21vdW50IGFib3ZlIGZpcmVzIGFueXdheSBzbyB0ZWxlbWV0cnkgbG9va3MgZmluZS5cbiAgICAgIGlmICghY2hlY2tIYXNUcnVzdERpYWxvZ0FjY2VwdGVkKCkpIHtcbiAgICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAgICBrZXk6ICdzdGF0dXNsaW5lLXRydXN0LWJsb2NrZWQnLFxuICAgICAgICAgIHRleHQ6ICdzdGF0dXNsaW5lIHNraXBwZWQgwrcgcmVzdGFydCB0byBmaXgnLFxuICAgICAgICAgIGNvbG9yOiAnd2FybmluZycsXG4gICAgICAgICAgcHJpb3JpdHk6ICdsb3cnLFxuICAgICAgICB9KVxuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgJ1N0YXR1cyBsaW5lIGNvbW1hbmQgc2tpcHBlZDogd29ya3NwYWNlIHRydXN0IG5vdCBhY2NlcHRlZCcsXG4gICAgICAgICAgeyBsZXZlbDogJ3dhcm4nIH0sXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwc1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUV4aGF1c3RpdmVEZXBlbmRlbmNpZXM6IGludGVudGlvbmFsXG4gIH0sIFtdKSAvLyBPbmx5IHJ1biBvbmNlIG9uIG1vdW50IC0gc2V0dGluZ3Mgc3RhYmxlIGZvciBpbml0aWFsIGxvZ2dpbmdcblxuICAvLyBJbml0aWFsIHVwZGF0ZSBvbiBtb3VudCArIGNsZWFudXAgb24gdW5tb3VudFxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIHZvaWQgZG9VcGRhdGUoKVxuXG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGFib3J0Q29udHJvbGxlclJlZi5jdXJyZW50Py5hYm9ydCgpXG4gICAgICBpZiAoZGVib3VuY2VUaW1lclJlZi5jdXJyZW50ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KGRlYm91bmNlVGltZXJSZWYuY3VycmVudClcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwc1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUV4aGF1c3RpdmVEZXBlbmRlbmNpZXM6IGludGVudGlvbmFsXG4gIH0sIFtdKSAvLyBPbmx5IHJ1biBvbmNlIG9uIG1vdW50LCBub3Qgd2hlbiBkb1VwZGF0ZSBjaGFuZ2VzXG5cbiAgLy8gR2V0IHBhZGRpbmcgZnJvbSBzZXR0aW5ncyBvciBkZWZhdWx0IHRvIDBcbiAgY29uc3QgcGFkZGluZ1ggPSBzZXR0aW5ncz8uc3RhdHVzTGluZT8ucGFkZGluZyA/PyAwXG5cbiAgLy8gU3RhdHVzTGluZSBtdXN0IGhhdmUgc3RhYmxlIGhlaWdodCBpbiBmdWxsc2NyZWVuIOKAlCB0aGUgZm9vdGVyIGlzXG4gIC8vIGZsZXhTaHJpbms6MCBzbyBhIDDihpIxIHJvdyBjaGFuZ2Ugd2hlbiB0aGUgY29tbWFuZCBmaW5pc2hlcyBzdGVhbHNcbiAgLy8gYSByb3cgZnJvbSBTY3JvbGxCb3ggYW5kIHNoaWZ0cyBjb250ZW50LiBSZXNlcnZlIHRoZSByb3cgd2hpbGUgbG9hZGluZ1xuICAvLyAoc2FtZSB0cmljayBhcyBQcm9tcHRJbnB1dEZvb3RlckxlZnRTaWRlKS5cbiAgcmV0dXJuIChcbiAgICA8Qm94IHBhZGRpbmdYPXtwYWRkaW5nWH0gZ2FwPXsyfT5cbiAgICAgIHtzdGF0dXNMaW5lVGV4dCA/IChcbiAgICAgICAgPFRleHQgZGltQ29sb3Igd3JhcD1cInRydW5jYXRlXCI+XG4gICAgICAgICAgPEFuc2k+e3N0YXR1c0xpbmVUZXh0fTwvQW5zaT5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKSA6IGlzRnVsbHNjcmVlbkVudkVuYWJsZWQoKSA/IChcbiAgICAgICAgPFRleHQ+IDwvVGV4dD5cbiAgICAgICkgOiBudWxsfVxuICAgIDwvQm94PlxuICApXG59XG5cbi8vIFBhcmVudCAoUHJvbXB0SW5wdXRGb290ZXIpIHJlLXJlbmRlcnMgb24gZXZlcnkgc2V0TWVzc2FnZXMsIGJ1dCBTdGF0dXNMaW5lJ3Ncbi8vIG93biBwcm9wcyBub3cgb25seSBjaGFuZ2Ugd2hlbiBsYXN0QXNzaXN0YW50TWVzc2FnZUlkIGZsaXBzIOKAlCBtZW1vIGtlZXBzIGl0XG4vLyBmcm9tIGJlaW5nIGRyYWdnZWQgYWxvbmcgKHByZXZpb3VzbHkgfjE4IG5vLXByb3AtY2hhbmdlIHJlbmRlcnMgcGVyIHNlc3Npb24pLlxuZXhwb3J0IGNvbnN0IFN0YXR1c0xpbmUgPSBtZW1vKFN0YXR1c0xpbmVJbm5lcilcbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxJQUFJLEVBQUVDLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxNQUFNLFFBQVEsT0FBTztBQUM1RCxTQUFTQyxRQUFRLFFBQVEsaUNBQWlDO0FBQzFELFNBQVNDLFdBQVcsRUFBRUMsY0FBYyxRQUFRLHVCQUF1QjtBQUNuRSxjQUFjQyxjQUFjLFFBQVEseUNBQXlDO0FBQzdFLFNBQ0VDLGVBQWUsRUFDZkMsZUFBZSxFQUNmQyxzQkFBc0IsRUFDdEJDLGNBQWMsRUFDZEMsV0FBVyxFQUNYQyxZQUFZLFFBQ1AsdUJBQXVCO0FBQzlCLFNBQVNDLHlCQUF5QixRQUFRLDhCQUE4QjtBQUN4RSxTQUFTQyxnQkFBZ0IsUUFBUSw2QkFBNkI7QUFDOUQsU0FDRUMsbUJBQW1CLEVBQ25CQyxZQUFZLEVBQ1pDLGdCQUFnQixFQUNoQkMsbUJBQW1CLEVBQ25CQyxrQkFBa0IsRUFDbEJDLG9CQUFvQixFQUNwQkMsb0JBQW9CLFFBQ2Ysb0JBQW9CO0FBQzNCLFNBQVNDLGdCQUFnQixRQUFRLDhCQUE4QjtBQUMvRCxTQUFTLEtBQUtDLGdCQUFnQixFQUFFQyxXQUFXLFFBQVEseUJBQXlCO0FBQzVFLFNBQVNDLElBQUksRUFBRUMsR0FBRyxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUMzQyxTQUFTQyxpQkFBaUIsUUFBUSwrQkFBK0I7QUFDakUsY0FBY0MsT0FBTyxRQUFRLHFCQUFxQjtBQUNsRCxjQUFjQyxzQkFBc0IsUUFBUSx3QkFBd0I7QUFDcEUsY0FBY0MsT0FBTyxRQUFRLDRCQUE0QjtBQUN6RCxTQUFTQywyQkFBMkIsUUFBUSxvQkFBb0I7QUFDaEUsU0FDRUMsMkJBQTJCLEVBQzNCQyx3QkFBd0IsUUFDbkIscUJBQXFC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 \ No newline at end of file diff --git a/components/StatusNotices.tsx b/components/StatusNotices.tsx new file mode 100644 index 0000000..cb4ac2a --- /dev/null +++ b/components/StatusNotices.tsx @@ -0,0 +1,55 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { use } from 'react'; +import { Box } from '../ink.js'; +import type { AgentDefinitionsResult } from '../tools/AgentTool/loadAgentsDir.js'; +import { getMemoryFiles } from '../utils/claudemd.js'; +import { getGlobalConfig } from '../utils/config.js'; +import { getActiveNotices, type StatusNoticeContext } from '../utils/statusNoticeDefinitions.js'; +type Props = { + agentDefinitions?: AgentDefinitionsResult; +}; + +/** + * StatusNotices contains the information displayed to users at startup. We have + * moved neutral or positive status to src/components/Status.tsx instead, which + * users can access through /status. + */ +export function StatusNotices(t0) { + const $ = _c(4); + const { + agentDefinitions + } = t0 === undefined ? {} : t0; + const t1 = getGlobalConfig(); + let t2; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t2 = getMemoryFiles(); + $[0] = t2; + } else { + t2 = $[0]; + } + const context = { + config: t1, + agentDefinitions, + memoryFiles: use(t2) + }; + const activeNotices = getActiveNotices(context); + if (activeNotices.length === 0) { + return null; + } + const T0 = Box; + const t3 = "column"; + const t4 = 1; + const t5 = activeNotices.map(notice => {notice.render(context)}); + let t6; + if ($[1] !== T0 || $[2] !== t5) { + t6 = {t5}; + $[1] = T0; + $[2] = t5; + $[3] = t6; + } else { + t6 = $[3]; + } + return t6; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZSIsIkJveCIsIkFnZW50RGVmaW5pdGlvbnNSZXN1bHQiLCJnZXRNZW1vcnlGaWxlcyIsImdldEdsb2JhbENvbmZpZyIsImdldEFjdGl2ZU5vdGljZXMiLCJTdGF0dXNOb3RpY2VDb250ZXh0IiwiUHJvcHMiLCJhZ2VudERlZmluaXRpb25zIiwiU3RhdHVzTm90aWNlcyIsInQwIiwiJCIsIl9jIiwidW5kZWZpbmVkIiwidDEiLCJ0MiIsIlN5bWJvbCIsImZvciIsImNvbnRleHQiLCJjb25maWciLCJtZW1vcnlGaWxlcyIsImFjdGl2ZU5vdGljZXMiLCJsZW5ndGgiLCJUMCIsInQzIiwidDQiLCJ0NSIsIm1hcCIsIm5vdGljZSIsImlkIiwicmVuZGVyIiwidDYiXSwic291cmNlcyI6WyJTdGF0dXNOb3RpY2VzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBBZ2VudERlZmluaXRpb25zUmVzdWx0IH0gZnJvbSAnLi4vdG9vbHMvQWdlbnRUb29sL2xvYWRBZ2VudHNEaXIuanMnXG5pbXBvcnQgeyBnZXRNZW1vcnlGaWxlcyB9IGZyb20gJy4uL3V0aWxzL2NsYXVkZW1kLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYmFsQ29uZmlnIH0gZnJvbSAnLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0QWN0aXZlTm90aWNlcyxcbiAgdHlwZSBTdGF0dXNOb3RpY2VDb250ZXh0LFxufSBmcm9tICcuLi91dGlscy9zdGF0dXNOb3RpY2VEZWZpbml0aW9ucy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgYWdlbnREZWZpbml0aW9ucz86IEFnZW50RGVmaW5pdGlvbnNSZXN1bHRcbn1cblxuLyoqXG4gKiBTdGF0dXNOb3RpY2VzIGNvbnRhaW5zIHRoZSBpbmZvcm1hdGlvbiBkaXNwbGF5ZWQgdG8gdXNlcnMgYXQgc3RhcnR1cC4gV2UgaGF2ZVxuICogbW92ZWQgbmV1dHJhbCBvciBwb3NpdGl2ZSBzdGF0dXMgdG8gc3JjL2NvbXBvbmVudHMvU3RhdHVzLnRzeCBpbnN0ZWFkLCB3aGljaFxuICogdXNlcnMgY2FuIGFjY2VzcyB0aHJvdWdoIC9zdGF0dXMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBTdGF0dXNOb3RpY2VzKHtcbiAgYWdlbnREZWZpbml0aW9ucyxcbn06IFByb3BzID0ge30pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBjb250ZXh0OiBTdGF0dXNOb3RpY2VDb250ZXh0ID0ge1xuICAgIGNvbmZpZzogZ2V0R2xvYmFsQ29uZmlnKCksXG4gICAgYWdlbnREZWZpbml0aW9ucyxcbiAgICBtZW1vcnlGaWxlczogdXNlKGdldE1lbW9yeUZpbGVzKCkpLFxuICB9XG4gIGNvbnN0IGFjdGl2ZU5vdGljZXMgPSBnZXRBY3RpdmVOb3RpY2VzKGNvbnRleHQpXG4gIGlmIChhY3RpdmVOb3RpY2VzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdMZWZ0PXsxfT5cbiAgICAgIHthY3RpdmVOb3RpY2VzLm1hcChub3RpY2UgPT4gKFxuICAgICAgICA8UmVhY3QuRnJhZ21lbnQga2V5PXtub3RpY2UuaWR9PlxuICAgICAgICAgIHtub3RpY2UucmVuZGVyKGNvbnRleHQpfVxuICAgICAgICA8L1JlYWN0LkZyYWdtZW50PlxuICAgICAgKSl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsR0FBRyxRQUFRLE9BQU87QUFDM0IsU0FBU0MsR0FBRyxRQUFRLFdBQVc7QUFDL0IsY0FBY0Msc0JBQXNCLFFBQVEscUNBQXFDO0FBQ2pGLFNBQVNDLGNBQWMsUUFBUSxzQkFBc0I7QUFDckQsU0FBU0MsZUFBZSxRQUFRLG9CQUFvQjtBQUNwRCxTQUNFQyxnQkFBZ0IsRUFDaEIsS0FBS0MsbUJBQW1CLFFBQ25CLHFDQUFxQztBQUU1QyxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsZ0JBQWdCLENBQUMsRUFBRU4sc0JBQXNCO0FBQzNDLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU8sU0FBQU8sY0FBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUF1QjtJQUFBSjtFQUFBLElBQUFFLEVBRWpCLEtBRmlCRyxTQUVqQixHQUZpQixDQUVsQixDQUFDLEdBRmlCSCxFQUVqQjtFQUVELE1BQUFJLEVBQUEsR0FBQVYsZUFBZSxDQUFDLENBQUM7RUFBQSxJQUFBVyxFQUFBO0VBQUEsSUFBQUosQ0FBQSxRQUFBSyxNQUFBLENBQUFDLEdBQUE7SUFFUkYsRUFBQSxHQUFBWixjQUFjLENBQUMsQ0FBQztJQUFBUSxDQUFBLE1BQUFJLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFKLENBQUE7RUFBQTtFQUhuQyxNQUFBTyxPQUFBLEdBQXFDO0lBQUFDLE1BQUEsRUFDM0JMLEVBQWlCO0lBQUFOLGdCQUFBO0lBQUFZLFdBQUEsRUFFWnBCLEdBQUcsQ0FBQ2UsRUFBZ0I7RUFDbkMsQ0FBQztFQUNELE1BQUFNLGFBQUEsR0FBc0JoQixnQkFBZ0IsQ0FBQ2EsT0FBTyxDQUFDO0VBQy9DLElBQUlHLGFBQWEsQ0FBQUMsTUFBTyxLQUFLLENBQUM7SUFBQSxPQUNyQixJQUFJO0VBQUE7RUFJVixNQUFBQyxFQUFBLEdBQUF0QixHQUFHO0VBQWUsTUFBQXVCLEVBQUEsV0FBUTtFQUFjLE1BQUFDLEVBQUEsSUFBQztFQUN2QyxNQUFBQyxFQUFBLEdBQUFMLGFBQWEsQ0FBQU0sR0FBSSxDQUFDQyxNQUFBLElBQ2pCLGdCQUFxQixHQUFTLENBQVQsQ0FBQUEsTUFBTSxDQUFBQyxFQUFFLENBQUMsQ0FDM0IsQ0FBQUQsTUFBTSxDQUFBRSxNQUFPLENBQUNaLE9BQU8sRUFDeEIsaUJBQ0QsQ0FBQztFQUFBLElBQUFhLEVBQUE7RUFBQSxJQUFBcEIsQ0FBQSxRQUFBWSxFQUFBLElBQUFaLENBQUEsUUFBQWUsRUFBQTtJQUxKSyxFQUFBLElBQUMsRUFBRyxDQUFlLGFBQVEsQ0FBUixDQUFBUCxFQUFPLENBQUMsQ0FBYyxXQUFDLENBQUQsQ0FBQUMsRUFBQSxDQUFDLENBQ3ZDLENBQUFDLEVBSUEsQ0FDSCxFQU5DLEVBQUcsQ0FNRTtJQUFBZixDQUFBLE1BQUFZLEVBQUE7SUFBQVosQ0FBQSxNQUFBZSxFQUFBO0lBQUFmLENBQUEsTUFBQW9CLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFwQixDQUFBO0VBQUE7RUFBQSxPQU5Ob0IsRUFNTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/StructuredDiff.tsx b/components/StructuredDiff.tsx new file mode 100644 index 0000000..a6fe672 --- /dev/null +++ b/components/StructuredDiff.tsx @@ -0,0 +1,190 @@ +import { c as _c } from "react/compiler-runtime"; +import type { StructuredPatchHunk } from 'diff'; +import * as React from 'react'; +import { memo } from 'react'; +import { useSettings } from '../hooks/useSettings.js'; +import { Box, NoSelect, RawAnsi, useTheme } from '../ink.js'; +import { isFullscreenEnvEnabled } from '../utils/fullscreen.js'; +import sliceAnsi from '../utils/sliceAnsi.js'; +import { expectColorDiff } from './StructuredDiff/colorDiff.js'; +import { StructuredDiffFallback } from './StructuredDiff/Fallback.js'; +type Props = { + patch: StructuredPatchHunk; + dim: boolean; + filePath: string; // File path for language detection + firstLine: string | null; // First line of file for shebang detection + fileContent?: string; // Full file content for syntax context (multiline strings, etc.) + width: number; + skipHighlighting?: boolean; // Skip syntax highlighting +}; + +// REPL.tsx renders at two disjoint tree positions (transcript +// early-return vs prompt-mode nested in FullscreenLayout), so ctrl+o +// unmounts/remounts the entire message tree and React's memo cache is lost. +// Keep both the NAPI result AND the pre-split gutter/content columns at +// module level so the only work on remount is a WeakMap lookup plus two +// leaves — not a fresh syntax highlight, nor N sliceAnsi +// calls + 6N Yoga nodes. +// +// PR #21439 (fullscreen default-on) made gutterWidth>0 the default path, +// reactivating the per-line branch that PR #20378 had bypassed. +// Caching the split here restores the O(1)-leaves-per-diff invariant. +type CachedRender = { + lines: string[]; + // Two RawAnsi columns replace what was N DiffLine rows. sliceAnsi work + // moves from per-remount to cold-cache-only; parseToSpans is eliminated + // entirely (RawAnsi bypasses Ansi parsing). + gutterWidth: number; + gutters: string[] | null; + contents: string[] | null; +}; +const RENDER_CACHE = new WeakMap>(); + +// Gutter width matches the Rust module's layout: marker (1) + space + +// right-aligned line number (max_digits) + space. Depends only on patch +// identity (the WeakMap key), so it's cacheable alongside the NAPI output. +function computeGutterWidth(patch: StructuredPatchHunk): number { + const maxLineNumber = Math.max(patch.oldStart + patch.oldLines - 1, patch.newStart + patch.newLines - 1, 1); + return maxLineNumber.toString().length + 3; // marker + 2 padding spaces +} +function renderColorDiff(patch: StructuredPatchHunk, firstLine: string | null, filePath: string, fileContent: string | null, theme: string, width: number, dim: boolean, splitGutter: boolean): CachedRender | null { + const ColorDiff = expectColorDiff(); + if (!ColorDiff) return null; + + // Defensive: if the gutter would eat the whole render width (narrow + // terminal), skip the split. Rust already wraps to `width` so the + // single-column output stays correct; we just lose noSelect. Without + // this, sliceAnsi(line, gutterWidth) would return empty content and + // RawAnsi(width<=0) is untested. + const rawGutterWidth = splitGutter ? computeGutterWidth(patch) : 0; + const gutterWidth = rawGutterWidth > 0 && rawGutterWidth < width ? rawGutterWidth : 0; + const key = `${theme}|${width}|${dim ? 1 : 0}|${gutterWidth}|${firstLine ?? ''}|${filePath}`; + let perHunk = RENDER_CACHE.get(patch); + const hit = perHunk?.get(key); + if (hit) return hit; + const lines = new ColorDiff(patch, firstLine, filePath, fileContent).render(theme, width, dim); + if (lines === null) return null; + + // Pre-split the gutter column once (cold-cache). sliceAnsi preserves + // styles across the cut; the Rust module already pads the gutter to + // gutterWidth so the narrow RawAnsi column's width matches its cells. + let gutters: string[] | null = null; + let contents: string[] | null = null; + if (gutterWidth > 0) { + gutters = lines.map(l => sliceAnsi(l, 0, gutterWidth)); + contents = lines.map(l => sliceAnsi(l, gutterWidth)); + } + const entry: CachedRender = { + lines, + gutterWidth, + gutters, + contents + }; + if (!perHunk) { + perHunk = new Map(); + RENDER_CACHE.set(patch, perHunk); + } + // Cap the inner map: width is part of the key, so terminal resize while a + // diff is visible accumulates a full render copy per distinct width. Four + // variants (two widths × dim on/off) covers the steady state; beyond that + // the user is actively resizing and old widths are stale. + if (perHunk.size >= 4) perHunk.clear(); + perHunk.set(key, entry); + return entry; +} +export const StructuredDiff = memo(function StructuredDiff(t0) { + const $ = _c(26); + const { + patch, + dim, + filePath, + firstLine, + fileContent, + width, + skipHighlighting: t1 + } = t0; + const skipHighlighting = t1 === undefined ? false : t1; + const [theme] = useTheme(); + const settings = useSettings(); + const syntaxHighlightingDisabled = settings.syntaxHighlightingDisabled ?? false; + const safeWidth = Math.max(1, Math.floor(width)); + let t2; + if ($[0] !== dim || $[1] !== fileContent || $[2] !== filePath || $[3] !== firstLine || $[4] !== patch || $[5] !== safeWidth || $[6] !== skipHighlighting || $[7] !== syntaxHighlightingDisabled || $[8] !== theme) { + const splitGutter = isFullscreenEnvEnabled(); + t2 = skipHighlighting || syntaxHighlightingDisabled ? null : renderColorDiff(patch, firstLine, filePath, fileContent ?? null, theme, safeWidth, dim, splitGutter); + $[0] = dim; + $[1] = fileContent; + $[2] = filePath; + $[3] = firstLine; + $[4] = patch; + $[5] = safeWidth; + $[6] = skipHighlighting; + $[7] = syntaxHighlightingDisabled; + $[8] = theme; + $[9] = t2; + } else { + t2 = $[9]; + } + const cached = t2; + if (!cached) { + let t3; + if ($[10] !== dim || $[11] !== patch || $[12] !== width) { + t3 = ; + $[10] = dim; + $[11] = patch; + $[12] = width; + $[13] = t3; + } else { + t3 = $[13]; + } + return t3; + } + const { + lines, + gutterWidth, + gutters, + contents + } = cached; + if (gutterWidth > 0 && gutters && contents) { + let t3; + if ($[14] !== gutterWidth || $[15] !== gutters) { + t3 = ; + $[14] = gutterWidth; + $[15] = gutters; + $[16] = t3; + } else { + t3 = $[16]; + } + const t4 = safeWidth - gutterWidth; + let t5; + if ($[17] !== contents || $[18] !== t4) { + t5 = ; + $[17] = contents; + $[18] = t4; + $[19] = t5; + } else { + t5 = $[19]; + } + let t6; + if ($[20] !== t3 || $[21] !== t5) { + t6 = {t3}{t5}; + $[20] = t3; + $[21] = t5; + $[22] = t6; + } else { + t6 = $[22]; + } + return t6; + } + let t3; + if ($[23] !== lines || $[24] !== safeWidth) { + t3 = ; + $[23] = lines; + $[24] = safeWidth; + $[25] = t3; + } else { + t3 = $[25]; + } + return t3; +}); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTdHJ1Y3R1cmVkUGF0Y2hIdW5rIiwiUmVhY3QiLCJtZW1vIiwidXNlU2V0dGluZ3MiLCJCb3giLCJOb1NlbGVjdCIsIlJhd0Fuc2kiLCJ1c2VUaGVtZSIsImlzRnVsbHNjcmVlbkVudkVuYWJsZWQiLCJzbGljZUFuc2kiLCJleHBlY3RDb2xvckRpZmYiLCJTdHJ1Y3R1cmVkRGlmZkZhbGxiYWNrIiwiUHJvcHMiLCJwYXRjaCIsImRpbSIsImZpbGVQYXRoIiwiZmlyc3RMaW5lIiwiZmlsZUNvbnRlbnQiLCJ3aWR0aCIsInNraXBIaWdobGlnaHRpbmciLCJDYWNoZWRSZW5kZXIiLCJsaW5lcyIsImd1dHRlcldpZHRoIiwiZ3V0dGVycyIsImNvbnRlbnRzIiwiUkVOREVSX0NBQ0hFIiwiV2Vha01hcCIsIk1hcCIsImNvbXB1dGVHdXR0ZXJXaWR0aCIsIm1heExpbmVOdW1iZXIiLCJNYXRoIiwibWF4Iiwib2xkU3RhcnQiLCJvbGRMaW5lcyIsIm5ld1N0YXJ0IiwibmV3TGluZXMiLCJ0b1N0cmluZyIsImxlbmd0aCIsInJlbmRlckNvbG9yRGlmZiIsInRoZW1lIiwic3BsaXRHdXR0ZXIiLCJDb2xvckRpZmYiLCJyYXdHdXR0ZXJXaWR0aCIsImtleSIsInBlckh1bmsiLCJnZXQiLCJoaXQiLCJyZW5kZXIiLCJtYXAiLCJsIiwiZW50cnkiLCJzZXQiLCJzaXplIiwiY2xlYXIiLCJTdHJ1Y3R1cmVkRGlmZiIsInQwIiwiJCIsIl9jIiwidDEiLCJ1bmRlZmluZWQiLCJzZXR0aW5ncyIsInN5bnRheEhpZ2hsaWdodGluZ0Rpc2FibGVkIiwic2FmZVdpZHRoIiwiZmxvb3IiLCJ0MiIsImNhY2hlZCIsInQzIiwidDQiLCJ0NSIsInQ2Il0sInNvdXJjZXMiOlsiU3RydWN0dXJlZERpZmYudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgU3RydWN0dXJlZFBhdGNoSHVuayB9IGZyb20gJ2RpZmYnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IG1lbW8gfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZVNldHRpbmdzIH0gZnJvbSAnLi4vaG9va3MvdXNlU2V0dGluZ3MuanMnXG5pbXBvcnQgeyBCb3gsIE5vU2VsZWN0LCBSYXdBbnNpLCB1c2VUaGVtZSB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGlzRnVsbHNjcmVlbkVudkVuYWJsZWQgfSBmcm9tICcuLi91dGlscy9mdWxsc2NyZWVuLmpzJ1xuaW1wb3J0IHNsaWNlQW5zaSBmcm9tICcuLi91dGlscy9zbGljZUFuc2kuanMnXG5pbXBvcnQgeyBleHBlY3RDb2xvckRpZmYgfSBmcm9tICcuL1N0cnVjdHVyZWREaWZmL2NvbG9yRGlmZi5qcydcbmltcG9ydCB7IFN0cnVjdHVyZWREaWZmRmFsbGJhY2sgfSBmcm9tICcuL1N0cnVjdHVyZWREaWZmL0ZhbGxiYWNrLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBwYXRjaDogU3RydWN0dXJlZFBhdGNoSHVua1xuICBkaW06IGJvb2xlYW5cbiAgZmlsZVBhdGg6IHN0cmluZyAvLyBGaWxlIHBhdGggZm9yIGxhbmd1YWdlIGRldGVjdGlvblxuICBmaXJzdExpbmU6IHN0cmluZyB8IG51bGwgLy8gRmlyc3QgbGluZSBvZiBmaWxlIGZvciBzaGViYW5nIGRldGVjdGlvblxuICBmaWxlQ29udGVudD86IHN0cmluZyAvLyBGdWxsIGZpbGUgY29udGVudCBmb3Igc3ludGF4IGNvbnRleHQgKG11bHRpbGluZSBzdHJpbmdzLCBldGMuKVxuICB3aWR0aDogbnVtYmVyXG4gIHNraXBIaWdobGlnaHRpbmc/OiBib29sZWFuIC8vIFNraXAgc3ludGF4IGhpZ2hsaWdodGluZ1xufVxuXG4vLyBSRVBMLnRzeCByZW5kZXJzIDxNZXNzYWdlcz4gYXQgdHdvIGRpc2pvaW50IHRyZWUgcG9zaXRpb25zICh0cmFuc2NyaXB0XG4vLyBlYXJseS1yZXR1cm4gdnMgcHJvbXB0LW1vZGUgbmVzdGVkIGluIEZ1bGxzY3JlZW5MYXlvdXQpLCBzbyBjdHJsK29cbi8vIHVubW91bnRzL3JlbW91bnRzIHRoZSBlbnRpcmUgbWVzc2FnZSB0cmVlIGFuZCBSZWFjdCdzIG1lbW8gY2FjaGUgaXMgbG9zdC5cbi8vIEtlZXAgYm90aCB0aGUgTkFQSSByZXN1bHQgQU5EIHRoZSBwcmUtc3BsaXQgZ3V0dGVyL2NvbnRlbnQgY29sdW1ucyBhdFxuLy8gbW9kdWxlIGxldmVsIHNvIHRoZSBvbmx5IHdvcmsgb24gcmVtb3VudCBpcyBhIFdlYWtNYXAgbG9va3VwIHBsdXMgdHdvXG4vLyA8aW5rLXJhdy1hbnNpPiBsZWF2ZXMg4oCUIG5vdCBhIGZyZXNoIHN5bnRheCBoaWdobGlnaHQsIG5vciBOIHNsaWNlQW5zaVxuLy8gY2FsbHMgKyA2TiBZb2dhIG5vZGVzLlxuLy9cbi8vIFBSICMyMTQzOSAoZnVsbHNjcmVlbiBkZWZhdWx0LW9uKSBtYWRlIGd1dHRlcldpZHRoPjAgdGhlIGRlZmF1bHQgcGF0aCxcbi8vIHJlYWN0aXZhdGluZyB0aGUgcGVyLWxpbmUgPERpZmZMaW5lPiBicmFuY2ggdGhhdCBQUiAjMjAzNzggaGFkIGJ5cGFzc2VkLlxuLy8gQ2FjaGluZyB0aGUgc3BsaXQgaGVyZSByZXN0b3JlcyB0aGUgTygxKS1sZWF2ZXMtcGVyLWRpZmYgaW52YXJpYW50LlxudHlwZSBDYWNoZWRSZW5kZXIgPSB7XG4gIGxpbmVzOiBzdHJpbmdbXVxuICAvLyBUd28gUmF3QW5zaSBjb2x1bW5zIHJlcGxhY2Ugd2hhdCB3YXMgTiBEaWZmTGluZSByb3dzLiBzbGljZUFuc2kgd29ya1xuICAvLyBtb3ZlcyBmcm9tIHBlci1yZW1vdW50IHRvIGNvbGQtY2FjaGUtb25seTsgcGFyc2VUb1NwYW5zIGlzIGVsaW1pbmF0ZWRcbiAgLy8gZW50aXJlbHkgKFJhd0Fuc2kgYnlwYXNzZXMgQW5zaSBwYXJzaW5nKS5cbiAgZ3V0dGVyV2lkdGg6IG51bWJlclxuICBndXR0ZXJzOiBzdHJpbmdbXSB8IG51bGxcbiAgY29udGVudHM6IHN0cmluZ1tdIHwgbnVsbFxufVxuY29uc3QgUkVOREVSX0NBQ0hFID0gbmV3IFdlYWtNYXA8XG4gIFN0cnVjdHVyZWRQYXRjaEh1bmssXG4gIE1hcDxzdHJpbmcsIENhY2hlZFJlbmRlcj5cbj4oKVxuXG4vLyBHdXR0ZXIgd2lkdGggbWF0Y2hlcyB0aGUgUnVzdCBtb2R1bGUncyBsYXlvdXQ6IG1hcmtlciAoMSkgKyBzcGFjZSArXG4vLyByaWdodC1hbGlnbmVkIGxpbmUgbnVtYmVyIChtYXhfZGlnaXRzKSArIHNwYWNlLiBEZXBlbmRzIG9ubHkgb24gcGF0Y2hcbi8vIGlkZW50aXR5ICh0aGUgV2Vha01hcCBrZXkpLCBzbyBpdCdzIGNhY2hlYWJsZSBhbG9uZ3NpZGUgdGhlIE5BUEkgb3V0cHV0LlxuZnVuY3Rpb24gY29tcHV0ZUd1dHRlcldpZHRoKHBhdGNoOiBTdHJ1Y3R1cmVkUGF0Y2hIdW5rKTogbnVtYmVyIHtcbiAgY29uc3QgbWF4TGluZU51bWJlciA9IE1hdGgubWF4KFxuICAgIHBhdGNoLm9sZFN0YXJ0ICsgcGF0Y2gub2xkTGluZXMgLSAxLFxuICAgIHBhdGNoLm5ld1N0YXJ0ICsgcGF0Y2gubmV3TGluZXMgLSAxLFxuICAgIDEsXG4gIClcbiAgcmV0dXJuIG1heExpbmVOdW1iZXIudG9TdHJpbmcoKS5sZW5ndGggKyAzIC8vIG1hcmtlciArIDIgcGFkZGluZyBzcGFjZXNcbn1cblxuZnVuY3Rpb24gcmVuZGVyQ29sb3JEaWZmKFxuICBwYXRjaDogU3RydWN0dXJlZFBhdGNoSHVuayxcbiAgZmlyc3RMaW5lOiBzdHJpbmcgfCBudWxsLFxuICBmaWxlUGF0aDogc3RyaW5nLFxuICBmaWxlQ29udGVudDogc3RyaW5nIHwgbnVsbCxcbiAgdGhlbWU6IHN0cmluZyxcbiAgd2lkdGg6IG51bWJlcixcbiAgZGltOiBib29sZWFuLFxuICBzcGxpdEd1dHRlcjogYm9vbGVhbixcbik6IENhY2hlZFJlbmRlciB8IG51bGwge1xuICBjb25zdCBDb2xvckRpZmYgPSBleHBlY3RDb2xvckRpZmYoKVxuICBpZiAoIUNvbG9yRGlmZikgcmV0dXJuIG51bGxcblxuICAvLyBEZWZlbnNpdmU6IGlmIHRoZSBndXR0ZXIgd291bGQgZWF0IHRoZSB3aG9sZSByZW5kZXIgd2lkdGggKG5hcnJvd1xuICAvLyB0ZXJtaW5hbCksIHNraXAgdGhlIHNwbGl0LiBSdXN0IGFscmVhZHkgd3JhcHMgdG8gYHdpZHRoYCBzbyB0aGVcbiAgLy8gc2luZ2xlLWNvbHVtbiBvdXRwdXQgc3RheXMgY29ycmVjdDsgd2UganVzdCBsb3NlIG5vU2VsZWN0LiBXaXRob3V0XG4gIC8vIHRoaXMsIHNsaWNlQW5zaShsaW5lLCBndXR0ZXJXaWR0aCkgd291bGQgcmV0dXJuIGVtcHR5IGNvbnRlbnQgYW5kXG4gIC8vIFJhd0Fuc2kod2lkdGg8PTApIGlzIHVudGVzdGVkLlxuICBjb25zdCByYXdHdXR0ZXJXaWR0aCA9IHNwbGl0R3V0dGVyID8gY29tcHV0ZUd1dHRlcldpZHRoKHBhdGNoKSA6IDBcbiAgY29uc3QgZ3V0dGVyV2lkdGggPVxuICAgIHJhd0d1dHRlcldpZHRoID4gMCAmJiByYXdHdXR0ZXJXaWR0aCA8IHdpZHRoID8gcmF3R3V0dGVyV2lkdGggOiAwXG5cbiAgY29uc3Qga2V5ID0gYCR7dGhlbWV9fCR7d2lkdGh9fCR7ZGltID8gMSA6IDB9fCR7Z3V0dGVyV2lkdGh9fCR7Zmlyc3RMaW5lID8/ICcnfXwke2ZpbGVQYXRofWBcblxuICBsZXQgcGVySHVuayA9IFJFTkRFUl9DQUNIRS5nZXQocGF0Y2gpXG4gIGNvbnN0IGhpdCA9IHBlckh1bms/LmdldChrZXkpXG4gIGlmIChoaXQpIHJldHVybiBoaXRcblxuICBjb25zdCBsaW5lcyA9IG5ldyBDb2xvckRpZmYocGF0Y2gsIGZpcnN0TGluZSwgZmlsZVBhdGgsIGZpbGVDb250ZW50KS5yZW5kZXIoXG4gICAgdGhlbWUsXG4gICAgd2lkdGgsXG4gICAgZGltLFxuICApXG4gIGlmIChsaW5lcyA9PT0gbnVsbCkgcmV0dXJuIG51bGxcblxuICAvLyBQcmUtc3BsaXQgdGhlIGd1dHRlciBjb2x1bW4gb25jZSAoY29sZC1jYWNoZSkuIHNsaWNlQW5zaSBwcmVzZXJ2ZXNcbiAgLy8gc3R5bGVzIGFjcm9zcyB0aGUgY3V0OyB0aGUgUnVzdCBtb2R1bGUgYWxyZWFkeSBwYWRzIHRoZSBndXR0ZXIgdG9cbiAgLy8gZ3V0dGVyV2lkdGggc28gdGhlIG5hcnJvdyBSYXdBbnNpIGNvbHVtbidzIHdpZHRoIG1hdGNoZXMgaXRzIGNlbGxzLlxuICBsZXQgZ3V0dGVyczogc3RyaW5nW10gfCBudWxsID0gbnVsbFxuICBsZXQgY29udGVudHM6IHN0cmluZ1tdIHwgbnVsbCA9IG51bGxcbiAgaWYgKGd1dHRlcldpZHRoID4gMCkge1xuICAgIGd1dHRlcnMgPSBsaW5lcy5tYXAobCA9PiBzbGljZUFuc2kobCwgMCwgZ3V0dGVyV2lkdGgpKVxuICAgIGNvbnRlbnRzID0gbGluZXMubWFwKGwgPT4gc2xpY2VBbnNpKGwsIGd1dHRlcldpZHRoKSlcbiAgfVxuXG4gIGNvbnN0IGVudHJ5OiBDYWNoZWRSZW5kZXIgPSB7IGxpbmVzLCBndXR0ZXJXaWR0aCwgZ3V0dGVycywgY29udGVudHMgfVxuXG4gIGlmICghcGVySHVuaykge1xuICAgIHBlckh1bmsgPSBuZXcgTWFwKClcbiAgICBSRU5ERVJfQ0FDSEUuc2V0KHBhdGNoLCBwZXJIdW5rKVxuICB9XG4gIC8vIENhcCB0aGUgaW5uZXIgbWFwOiB3aWR0aCBpcyBwYXJ0IG9mIHRoZSBrZXksIHNvIHRlcm1pbmFsIHJlc2l6ZSB3aGlsZSBhXG4gIC8vIGRpZmYgaXMgdmlzaWJsZSBhY2N1bXVsYXRlcyBhIGZ1bGwgcmVuZGVyIGNvcHkgcGVyIGRpc3RpbmN0IHdpZHRoLiBGb3VyXG4gIC8vIHZhcmlhbnRzICh0d28gd2lkdGhzIMOXIGRpbSBvbi9vZmYpIGNvdmVycyB0aGUgc3RlYWR5IHN0YXRlOyBiZXlvbmQgdGhhdFxuICAvLyB0aGUgdXNlciBpcyBhY3RpdmVseSByZXNpemluZyBhbmQgb2xkIHdpZHRocyBhcmUgc3RhbGUuXG4gIGlmIChwZXJIdW5rLnNpemUgPj0gNCkgcGVySHVuay5jbGVhcigpXG4gIHBlckh1bmsuc2V0KGtleSwgZW50cnkpXG4gIHJldHVybiBlbnRyeVxufVxuXG5leHBvcnQgY29uc3QgU3RydWN0dXJlZERpZmYgPSBtZW1vKGZ1bmN0aW9uIFN0cnVjdHVyZWREaWZmKHtcbiAgcGF0Y2gsXG4gIGRpbSxcbiAgZmlsZVBhdGgsXG4gIGZpcnN0TGluZSxcbiAgZmlsZUNvbnRlbnQsXG4gIHdpZHRoLFxuICBza2lwSGlnaGxpZ2h0aW5nID0gZmFsc2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZV0gPSB1c2VUaGVtZSgpXG4gIGNvbnN0IHNldHRpbmdzID0gdXNlU2V0dGluZ3MoKVxuICBjb25zdCBzeW50YXhIaWdobGlnaHRpbmdEaXNhYmxlZCA9XG4gICAgc2V0dGluZ3Muc3ludGF4SGlnaGxpZ2h0aW5nRGlzYWJsZWQgPz8gZmFsc2VcblxuICAvLyBFbnN1cmUgd2lkdGggaXMgYXQgbGVhc3QgMSB0byBwcmV2ZW50IGNyYXNoZXMgaW4gdGhlIFJ1c3QgTkFQSSBtb2R1bGVcbiAgLy8gd2hpY2ggZXhwZWN0cyB1MzIgKGNhbid0IGhhbmRsZSBuZWdhdGl2ZSBudW1iZXJzKVxuICBjb25zdCBzYWZlV2lkdGggPSBNYXRoLm1heCgxLCBNYXRoLmZsb29yKHdpZHRoKSlcblxuICAvLyBPbmx5IHNwbGl0IG91dCBhIG5vU2VsZWN0IGd1dHRlciBpbiBmdWxsc2NyZWVuIG1vZGUg4oCUIHRlcm1pbmFsIG5hdGl2ZVxuICAvLyBzZWxlY3Rpb24gaXMgdXNlZCBvdGhlcndpc2UgYW5kIG5vU2VsZWN0IGlzIG1lYW5pbmdsZXNzLiBCb3RoIGJyYW5jaGVzXG4gIC8vIGFyZSBub3cgTygxKSBZb2dhIGxlYXZlcyBwZXIgZGlmZiBvbiByZW1vdW50ICgyIHZzIDEpLCBzbyB0aGlzIGdhdGVcbiAgLy8gb25seSBzYXZlcyBjb2xkLWNhY2hlIHNsaWNlQW5zaSB3b3JrIHdoZW4gZnVsbHNjcmVlbiBpcyBvZmYuXG4gIGNvbnN0IHNwbGl0R3V0dGVyID0gaXNGdWxsc2NyZWVuRW52RW5hYmxlZCgpXG5cbiAgY29uc3QgY2FjaGVkID1cbiAgICBza2lwSGlnaGxpZ2h0aW5nIHx8IHN5bnRheEhpZ2hsaWdodGluZ0Rpc2FibGVkXG4gICAgICA/IG51bGxcbiAgICAgIDogcmVuZGVyQ29sb3JEaWZmKFxuICAgICAgICAgIHBhdGNoLFxuICAgICAgICAgIGZpcnN0TGluZSxcbiAgICAgICAgICBmaWxlUGF0aCxcbiAgICAgICAgICBmaWxlQ29udGVudCA/PyBudWxsLFxuICAgICAgICAgIHRoZW1lLFxuICAgICAgICAgIHNhZmVXaWR0aCxcbiAgICAgICAgICBkaW0sXG4gICAgICAgICAgc3BsaXRHdXR0ZXIsXG4gICAgICAgIClcblxuICBpZiAoIWNhY2hlZCkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94PlxuICAgICAgICA8U3RydWN0dXJlZERpZmZGYWxsYmFjayBwYXRjaD17cGF0Y2h9IGRpbT17ZGltfSB3aWR0aD17d2lkdGh9IC8+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBjb25zdCB7IGxpbmVzLCBndXR0ZXJXaWR0aCwgZ3V0dGVycywgY29udGVudHMgfSA9IGNhY2hlZFxuXG4gIC8vIFR3by1jb2x1bW4gbGF5b3V0OiBndXR0ZXIgKG5vU2VsZWN0KSArIGNvbnRlbnQuIE5vU2VsZWN0IG1hcmtzIHRoZVxuICAvLyBCb3gncyBjb21wdXRlZCBib3VuZHMgbm9uLXNlbGVjdGFibGU7IFJhd0Fuc2kncyBtZWFzdXJlIGZ1bmMgc2V0c1xuICAvLyByYXdIZWlnaHQ9bGluZXMubGVuZ3RoLCBzbyBvbmUgdGFsbCBsZWFmIGdldHMgdGhlIHNhbWUgbm9TZWxlY3RcbiAgLy8gY292ZXJhZ2UgTiBwZXItcm93IEJveGVzIHdvdWxkIOKAlCB3aXRob3V0IHRoZSBwZXItcm93IFlvZ2EgY29zdC5cbiAgaWYgKGd1dHRlcldpZHRoID4gMCAmJiBndXR0ZXJzICYmIGNvbnRlbnRzKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgICA8Tm9TZWxlY3QgZnJvbUxlZnRFZGdlPlxuICAgICAgICAgIDxSYXdBbnNpIGxpbmVzPXtndXR0ZXJzfSB3aWR0aD17Z3V0dGVyV2lkdGh9IC8+XG4gICAgICAgIDwvTm9TZWxlY3Q+XG4gICAgICAgIDxSYXdBbnNpIGxpbmVzPXtjb250ZW50c30gd2lkdGg9e3NhZmVXaWR0aCAtIGd1dHRlcldpZHRofSAvPlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94PlxuICAgICAgPFJhd0Fuc2kgbGluZXM9e2xpbmVzfSB3aWR0aD17c2FmZVdpZHRofSAvPlxuICAgIDwvQm94PlxuICApXG59KVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsY0FBY0EsbUJBQW1CLFFBQVEsTUFBTTtBQUMvQyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLElBQUksUUFBUSxPQUFPO0FBQzVCLFNBQVNDLFdBQVcsUUFBUSx5QkFBeUI7QUFDckQsU0FBU0MsR0FBRyxFQUFFQyxRQUFRLEVBQUVDLE9BQU8sRUFBRUMsUUFBUSxRQUFRLFdBQVc7QUFDNUQsU0FBU0Msc0JBQXNCLFFBQVEsd0JBQXdCO0FBQy9ELE9BQU9DLFNBQVMsTUFBTSx1QkFBdUI7QUFDN0MsU0FBU0MsZUFBZSxRQUFRLCtCQUErQjtBQUMvRCxTQUFTQyxzQkFBc0IsUUFBUSw4QkFBOEI7QUFFckUsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLEtBQUssRUFBRWIsbUJBQW1CO0VBQzFCYyxHQUFHLEVBQUUsT0FBTztFQUNaQyxRQUFRLEVBQUUsTUFBTSxFQUFDO0VBQ2pCQyxTQUFTLEVBQUUsTUFBTSxHQUFHLElBQUksRUFBQztFQUN6QkMsV0FBVyxDQUFDLEVBQUUsTUFBTSxFQUFDO0VBQ3JCQyxLQUFLLEVBQUUsTUFBTTtFQUNiQyxnQkFBZ0IsQ0FBQyxFQUFFLE9BQU8sRUFBQztBQUM3QixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtB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 \ No newline at end of file diff --git a/components/StructuredDiff/Fallback.tsx b/components/StructuredDiff/Fallback.tsx new file mode 100644 index 0000000..8948d76 --- /dev/null +++ b/components/StructuredDiff/Fallback.tsx @@ -0,0 +1,487 @@ +import { c as _c } from "react/compiler-runtime"; +import { diffWordsWithSpace, type StructuredPatchHunk } from 'diff'; +import * as React from 'react'; +import { useMemo } from 'react'; +import type { ThemeName } from 'src/utils/theme.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, NoSelect, Text, useTheme, wrapText } from '../../ink.js'; + +/* + * StructuredDiffFallback Component: Word-Level Diff Highlighting Example + * + * This component shows diff changes with word-level highlighting. Here's a walkthrough: + * + * Example: + * ``` + * // Original code + * function oldName(param) { + * return param.oldProperty; + * } + * + * // Changed code + * function newName(param) { + * return param.newProperty; + * } + * ``` + * + * Processing flow: + * 1. Component receives a patch with lines including '+' and '-' prefixes + * 2. Lines are transformed into objects with type (add/remove/nochange) + * 3. Related add/remove lines are paired (e.g., oldName with newName) + * 4. Word-level diffing identifies specific changed parts: + * [ + * { value: 'function ', added: undefined, removed: undefined }, // Common + * { value: 'oldName', removed: true }, // Removed + * { value: 'newName', added: true }, // Added + * { value: '(param) {', added: undefined, removed: undefined } // Common + * ] + * 5. Renders with enhanced highlighting: + * - Common parts are shown normally + * - Removed words get a darker red background + * - Added words get a darker green background + * + * This produces a visually clear diff where users can see exactly which words + * changed rather than just which lines were modified. + */ + +// Define DiffLine interface to be used throughout the file +interface DiffLine { + code: string; + type: 'add' | 'remove' | 'nochange'; + i: number; + originalCode: string; + wordDiff?: boolean; // Flag for word-level diffing + matchedLine?: DiffLine; +} + +// Line object type for internal functions +export interface LineObject { + code: string; + i: number; + type: 'add' | 'remove' | 'nochange'; + originalCode: string; + wordDiff?: boolean; + matchedLine?: LineObject; +} + +// Type for word-level diff parts +interface DiffPart { + added?: boolean; + removed?: boolean; + value: string; +} +type Props = { + patch: StructuredPatchHunk; + dim: boolean; + width: number; +}; + +// Threshold for when we show a full-line diff instead of word-level diffing +const CHANGE_THRESHOLD = 0.4; +export function StructuredDiffFallback(t0) { + const $ = _c(10); + const { + patch, + dim, + width + } = t0; + const [theme] = useTheme(); + let t1; + if ($[0] !== dim || $[1] !== patch.lines || $[2] !== patch.oldStart || $[3] !== theme || $[4] !== width) { + t1 = formatDiff(patch.lines, patch.oldStart, width, dim, theme); + $[0] = dim; + $[1] = patch.lines; + $[2] = patch.oldStart; + $[3] = theme; + $[4] = width; + $[5] = t1; + } else { + t1 = $[5]; + } + const diff = t1; + let t2; + if ($[6] !== diff) { + t2 = diff.map(_temp); + $[6] = diff; + $[7] = t2; + } else { + t2 = $[7]; + } + let t3; + if ($[8] !== t2) { + t3 = {t2}; + $[8] = t2; + $[9] = t3; + } else { + t3 = $[9]; + } + return t3; +} + +// Transform lines to line objects with type information +function _temp(node, i) { + return {node}; +} +export function transformLinesToObjects(lines: string[]): LineObject[] { + return lines.map(code => { + if (code.startsWith('+')) { + return { + code: code.slice(1), + i: 0, + type: 'add', + originalCode: code.slice(1) + }; + } + if (code.startsWith('-')) { + return { + code: code.slice(1), + i: 0, + type: 'remove', + originalCode: code.slice(1) + }; + } + return { + code: code.slice(1), + i: 0, + type: 'nochange', + originalCode: code.slice(1) + }; + }); +} + +// Group adjacent add/remove lines for word-level diffing +export function processAdjacentLines(lineObjects: LineObject[]): LineObject[] { + const processedLines: LineObject[] = []; + let i = 0; + while (i < lineObjects.length) { + const current = lineObjects[i]; + if (!current) { + i++; + continue; + } + + // Find a sequence of remove followed by add (possible word-level diff candidates) + if (current.type === 'remove') { + const removeLines: LineObject[] = [current]; + let j = i + 1; + + // Collect consecutive remove lines + while (j < lineObjects.length && lineObjects[j]?.type === 'remove') { + const line = lineObjects[j]; + if (line) { + removeLines.push(line); + } + j++; + } + + // Check if there are add lines following the remove lines + const addLines: LineObject[] = []; + while (j < lineObjects.length && lineObjects[j]?.type === 'add') { + const line = lineObjects[j]; + if (line) { + addLines.push(line); + } + j++; + } + + // If we have both remove and add lines, perform word-level diffing + if (removeLines.length > 0 && addLines.length > 0) { + // For word diffing, we'll compare each pair of lines or the closest available match + const pairCount = Math.min(removeLines.length, addLines.length); + + // Add paired lines with word diff info + for (let k = 0; k < pairCount; k++) { + const removeLine = removeLines[k]; + const addLine = addLines[k]; + if (removeLine && addLine) { + removeLine.wordDiff = true; + addLine.wordDiff = true; + + // Store the matched pair for later word diffing + removeLine.matchedLine = addLine; + addLine.matchedLine = removeLine; + } + } + + // Add all remove lines (both paired and unpaired) + processedLines.push(...removeLines.filter(Boolean)); + + // Then add all add lines (both paired and unpaired) + processedLines.push(...addLines.filter(Boolean)); + i = j; // Skip all the lines we've processed + } else { + // No matching add lines, just add the current remove line + processedLines.push(current); + i++; + } + } else { + // Not a remove line, just add it + processedLines.push(current); + i++; + } + } + return processedLines; +} + +// Calculate word-level diffs between two text strings +export function calculateWordDiffs(oldText: string, newText: string): DiffPart[] { + // Use diffWordsWithSpace instead of diffWords to preserve whitespace + // This ensures spaces between tokens like > and { are preserved + const result = diffWordsWithSpace(oldText, newText, { + ignoreCase: false + }); + return result; +} + +// Process word-level diffs with manual wrapping support +function generateWordDiffElements(item: DiffLine, width: number, maxWidth: number, dim: boolean, overrideTheme?: ThemeName): React.ReactNode[] | null { + const { + type, + i, + wordDiff, + matchedLine, + originalCode + } = item; + if (!wordDiff || !matchedLine) { + return null; // This function only handles word-level diff rendering + } + const removedLineText = type === 'remove' ? originalCode : matchedLine.originalCode; + const addedLineText = type === 'remove' ? matchedLine.originalCode : originalCode; + const wordDiffs = calculateWordDiffs(removedLineText, addedLineText); + + // Check if we should use word-level diffing + const totalLength = removedLineText.length + addedLineText.length; + const changedLength = wordDiffs.filter(part => part.added || part.removed).reduce((sum, part) => sum + part.value.length, 0); + const changeRatio = changedLength / totalLength; + if (changeRatio > CHANGE_THRESHOLD || dim) { + return null; // Fall back to standard rendering for major changes + } + + // Calculate available width for content + const diffPrefix = type === 'add' ? '+' : '-'; + const diffPrefixWidth = diffPrefix.length; + const availableContentWidth = Math.max(1, width - maxWidth - 1 - diffPrefixWidth); + + // Manually wrap the word diff parts with better space efficiency + const wrappedLines: { + content: React.ReactNode[]; + contentWidth: number; + }[] = []; + let currentLine: React.ReactNode[] = []; + let currentLineWidth = 0; + wordDiffs.forEach((part, partIndex) => { + // Determine if this part should be shown for this line type + let shouldShow = false; + let partBgColor: 'diffAddedWord' | 'diffRemovedWord' | undefined; + if (type === 'add') { + if (part.added) { + shouldShow = true; + partBgColor = 'diffAddedWord'; + } else if (!part.removed) { + shouldShow = true; + } + } else if (type === 'remove') { + if (part.removed) { + shouldShow = true; + partBgColor = 'diffRemovedWord'; + } else if (!part.added) { + shouldShow = true; + } + } + if (!shouldShow) return; + + // Use wrapText to wrap this individual part if it's long + const partWrapped = wrapText(part.value, availableContentWidth, 'wrap'); + const partLines = partWrapped.split('\n'); + partLines.forEach((partLine, lineIdx) => { + if (!partLine) return; + + // Check if we need to start a new line + if (lineIdx > 0 || currentLineWidth + stringWidth(partLine) > availableContentWidth) { + if (currentLine.length > 0) { + wrappedLines.push({ + content: [...currentLine], + contentWidth: currentLineWidth + }); + currentLine = []; + currentLineWidth = 0; + } + } + currentLine.push( + {partLine} + ); + currentLineWidth += stringWidth(partLine); + }); + }); + if (currentLine.length > 0) { + wrappedLines.push({ + content: currentLine, + contentWidth: currentLineWidth + }); + } + + // Render each wrapped line as a separate Text element + return wrappedLines.map(({ + content, + contentWidth + }, lineIndex) => { + const key = `${type}-${i}-${lineIndex}`; + const lineBgColor = type === 'add' ? dim ? 'diffAddedDimmed' : 'diffAdded' : dim ? 'diffRemovedDimmed' : 'diffRemoved'; + const lineNum = lineIndex === 0 ? i : undefined; + const lineNumStr = (lineNum !== undefined ? lineNum.toString().padStart(maxWidth) : ' '.repeat(maxWidth)) + ' '; + // Calculate padding to fill the entire terminal width + const usedWidth = lineNumStr.length + diffPrefixWidth + contentWidth; + const padding = Math.max(0, width - usedWidth); + return + + + {lineNumStr} + {diffPrefix} + + + + {content} + {' '.repeat(padding)} + + ; + }); +} +function formatDiff(lines: string[], startingLineNumber: number, width: number, dim: boolean, overrideTheme?: ThemeName): React.ReactNode[] { + // Ensure width is at least 1 to prevent rendering issues with very narrow terminals + const safeWidth = Math.max(1, Math.floor(width)); + + // Step 1: Transform lines to line objects with type information + const lineObjects = transformLinesToObjects(lines); + + // Step 2: Group adjacent add/remove lines for word-level diffing + const processedLines = processAdjacentLines(lineObjects); + + // Step 3: Number the diff lines + const ls = numberDiffLines(processedLines, startingLineNumber); + + // Find max line number width for alignment + const maxLineNumber = Math.max(...ls.map(({ + i + }) => i), 0); + const maxWidth = Math.max(maxLineNumber.toString().length + 1, 0); + + // Step 4: Render formatting + return ls.flatMap((item): React.ReactNode[] => { + const { + type, + code, + i, + wordDiff, + matchedLine + } = item; + + // Handle word-level diffing for add/remove pairs + if (wordDiff && matchedLine) { + const wordDiffElements = generateWordDiffElements(item, safeWidth, maxWidth, dim, overrideTheme); + + // word-diff might refuse (e.g. due to lines being substantially different) in which + // case we'll fall through to normal renderin gbelow + if (wordDiffElements !== null) { + return wordDiffElements; + } + } + + // Standard rendering for lines without word diffing or as fallback + // Calculate available width accounting for line number + space + diff prefix + const diffPrefixWidth = 2; // " " for unchanged, "+ " or "- " for changes + const availableContentWidth = Math.max(1, safeWidth - maxWidth - 1 - diffPrefixWidth); // -1 for space after line number + const wrappedText = wrapText(code, availableContentWidth, 'wrap'); + const wrappedLines = wrappedText.split('\n'); + return wrappedLines.map((line, lineIndex) => { + const key = `${type}-${i}-${lineIndex}`; + const lineNum = lineIndex === 0 ? i : undefined; + const lineNumStr = (lineNum !== undefined ? lineNum.toString().padStart(maxWidth) : ' '.repeat(maxWidth)) + ' '; + const sigil = type === 'add' ? '+' : type === 'remove' ? '-' : ' '; + // Calculate padding to fill the entire terminal width + const contentWidth = lineNumStr.length + 1 + stringWidth(line); // lineNum + sigil + code + const padding = Math.max(0, safeWidth - contentWidth); + const bgColor = type === 'add' ? dim ? 'diffAddedDimmed' : 'diffAdded' : type === 'remove' ? dim ? 'diffRemovedDimmed' : 'diffRemoved' : undefined; + + // Gutter (line number + sigil) is wrapped in so fullscreen + // text selection yields clean code. bgColor carries across both boxes + // so the visual continuity (solid red/green bar) is unchanged. + return + + + {lineNumStr} + {sigil} + + + + {line} + {' '.repeat(padding)} + + ; + }); + }); +} +export function numberDiffLines(diff: LineObject[], startLine: number): DiffLine[] { + let i = startLine; + const result: DiffLine[] = []; + const queue = [...diff]; + while (queue.length > 0) { + const current = queue.shift()!; + const { + code, + type, + originalCode, + wordDiff, + matchedLine + } = current; + const line = { + code, + type, + i, + originalCode, + wordDiff, + matchedLine + }; + + // Update counters based on change type + switch (type) { + case 'nochange': + i++; + result.push(line); + break; + case 'add': + i++; + result.push(line); + break; + case 'remove': + { + result.push(line); + let numRemoved = 0; + while (queue[0]?.type === 'remove') { + i++; + const current = queue.shift()!; + const { + code, + type, + originalCode, + wordDiff, + matchedLine + } = current; + const line = { + code, + type, + i, + originalCode, + wordDiff, + matchedLine + }; + result.push(line); + numRemoved++; + } + i -= numRemoved; + break; + } + } + } + return result; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJkaWZmV29yZHNXaXRoU3BhY2UiLCJTdHJ1Y3R1cmVkUGF0Y2hIdW5rIiwiUmVhY3QiLCJ1c2VNZW1vIiwiVGhlbWVOYW1lIiwic3RyaW5nV2lkdGgiLCJCb3giLCJOb1NlbGVjdCIsIlRleHQiLCJ1c2VUaGVtZSIsIndyYXBUZXh0IiwiRGlmZkxpbmUiLCJjb2RlIiwidHlwZSIsImkiLCJvcmlnaW5hbENvZGUiLCJ3b3JkRGlmZiIsIm1hdGNoZWRMaW5lIiwiTGluZU9iamVjdCIsIkRpZmZQYXJ0IiwiYWRkZWQiLCJyZW1vdmVkIiwidmFsdWUiLCJQcm9wcyIsInBhdGNoIiwiZGltIiwid2lkdGgiLCJDSEFOR0VfVEhSRVNIT0xEIiwiU3RydWN0dXJlZERpZmZGYWxsYmFjayIsInQwIiwiJCIsIl9jIiwidGhlbWUiLCJ0MSIsImxpbmVzIiwib2xkU3RhcnQiLCJmb3JtYXREaWZmIiwiZGlmZiIsInQyIiwibWFwIiwiX3RlbXAiLCJ0MyIsIm5vZGUiLCJ0cmFuc2Zvcm1MaW5lc1RvT2JqZWN0cyIsInN0YXJ0c1dpdGgiLCJzbGljZSIsInByb2Nlc3NBZGphY2VudExpbmVzIiwibGluZU9iamVjdHMiLCJwcm9jZXNzZWRMaW5lcyIsImxlbmd0aCIsImN1cnJlbnQiLCJyZW1vdmVMaW5lcyIsImoiLCJsaW5lIiwicHVzaCIsImFkZExpbmVzIiwicGFpckNvdW50IiwiTWF0aCIsIm1pbiIsImsiLCJyZW1vdmVMaW5lIiwiYWRkTGluZSIsImZpbHRlciIsIkJvb2xlYW4iLCJjYWxjdWxhdGVXb3JkRGlmZnMiLCJvbGRUZXh0IiwibmV3VGV4dCIsInJlc3VsdCIsImlnbm9yZUNhc2UiLCJnZW5lcmF0ZVdvcmREaWZmRWxlbWVudHMiLCJpdGVtIiwibWF4V2lkdGgiLCJvdmVycmlkZVRoZW1lIiwiUmVhY3ROb2RlIiwicmVtb3ZlZExpbmVUZXh0IiwiYWRkZWRMaW5lVGV4dCIsIndvcmREaWZmcyIsInRvdGFsTGVuZ3RoIiwiY2hhbmdlZExlbmd0aCIsInBhcnQiLCJyZWR1Y2UiLCJzdW0iLCJjaGFuZ2VSYXRpbyIsImRpZmZQcmVmaXgiLCJkaWZmUHJlZml4V2lkdGgiLCJhdmFpbGFibGVDb250ZW50V2lkdGgiLCJtYXgiLCJ3cmFwcGVkTGluZXMiLCJjb250ZW50IiwiY29udGVudFdpZHRoIiwiY3VycmVudExpbmUiLCJjdXJyZW50TGluZVdpZHRoIiwiZm9yRWFjaCIsInBhcnRJbmRleCIsInNob3VsZFNob3ciLCJwYXJ0QmdDb2xvciIsInBhcnRXcmFwcGVkIiwicGFydExpbmVzIiwic3BsaXQiLCJwYXJ0TGluZSIsImxpbmVJZHgiLCJsaW5lSW5kZXgiLCJrZXkiLCJsaW5lQmdDb2xvciIsImxpbmVOdW0iLCJ1bmRlZmluZWQiLCJsaW5lTnVtU3RyIiwidG9TdHJpbmciLCJwYWRTdGFydCIsInJlcGVhdCIsInVzZWRXaWR0aCIsInBhZGRpbmciLCJzdGFydGluZ0xpbmVOdW1iZXIiLCJzYWZlV2lkdGgiLCJmbG9vciIsImxzIiwibnVtYmVyRGlmZkxpbmVzIiwibWF4TGluZU51bWJlciIsImZsYXRNYXAiLCJ3b3JkRGlmZkVsZW1lbnRzIiwid3JhcHBlZFRleHQiLCJzaWdpbCIsImJnQ29sb3IiLCJzdGFydExpbmUiLCJxdWV1ZSIsInNoaWZ0IiwibnVtUmVtb3ZlZCJdLCJzb3VyY2VzIjpbIkZhbGxiYWNrLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBkaWZmV29yZHNXaXRoU3BhY2UsIHR5cGUgU3RydWN0dXJlZFBhdGNoSHVuayB9IGZyb20gJ2RpZmYnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZU1lbW8gfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgVGhlbWVOYW1lIH0gZnJvbSAnc3JjL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi8uLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyBCb3gsIE5vU2VsZWN0LCBUZXh0LCB1c2VUaGVtZSwgd3JhcFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5cbi8qXG4gKiBTdHJ1Y3R1cmVkRGlmZkZhbGxiYWNrIENvbXBvbmVudDogV29yZC1MZXZlbCBEaWZmIEhpZ2hsaWdodGluZyBFeGFtcGxlXG4gKlxuICogVGhpcyBjb21wb25lbnQgc2hvd3MgZGlmZiBjaGFuZ2VzIHdpdGggd29yZC1sZXZlbCBoaWdobGlnaHRpbmcuIEhlcmUncyBhIHdhbGt0aHJvdWdoOlxuICpcbiAqIEV4YW1wbGU6XG4gKiBgYGBcbiAqIC8vIE9yaWdpbmFsIGNvZGVcbiAqIGZ1bmN0aW9uIG9sZE5hbWUocGFyYW0pIHtcbiAqICAgcmV0dXJuIHBhcmFtLm9sZFByb3BlcnR5O1xuICogfVxuICpcbiAqIC8vIENoYW5nZWQgY29kZVxuICogZnVuY3Rpb24gbmV3TmFtZShwYXJhbSkge1xuICogICByZXR1cm4gcGFyYW0ubmV3UHJvcGVydHk7XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBQcm9jZXNzaW5nIGZsb3c6XG4gKiAxLiBDb21wb25lbnQgcmVjZWl2ZXMgYSBwYXRjaCB3aXRoIGxpbmVzIGluY2x1ZGluZyAnKycgYW5kICctJyBwcmVmaXhlc1xuICogMi4gTGluZXMgYXJlIHRyYW5zZm9ybWVkIGludG8gb2JqZWN0cyB3aXRoIHR5cGUgKGFkZC9yZW1vdmUvbm9jaGFuZ2UpXG4gKiAzLiBSZWxhdGVkIGFkZC9yZW1vdmUgbGluZXMgYXJlIHBhaXJlZCAoZS5nLiwgb2xkTmFtZSB3aXRoIG5ld05hbWUpXG4gKiA0LiBXb3JkLWxldmVsIGRpZmZpbmcgaWRlbnRpZmllcyBzcGVjaWZpYyBjaGFuZ2VkIHBhcnRzOlxuICogICAgW1xuICogICAgICB7IHZhbHVlOiAnZnVuY3Rpb24gJywgYWRkZWQ6IHVuZGVmaW5lZCwgcmVtb3ZlZDogdW5kZWZpbmVkIH0sICAvLyBDb21tb25cbiAqICAgICAgeyB2YWx1ZTogJ29sZE5hbWUnLCByZW1vdmVkOiB0cnVlIH0sICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gUmVtb3ZlZFxuICogICAgICB7IHZhbHVlOiAnbmV3TmFtZScsIGFkZGVkOiB0cnVlIH0sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBZGRlZFxuICogICAgICB7IHZhbHVlOiAnKHBhcmFtKSB7JywgYWRkZWQ6IHVuZGVmaW5lZCwgcmVtb3ZlZDogdW5kZWZpbmVkIH0gICAvLyBDb21tb25cbiAqICAgIF1cbiAqIDUuIFJlbmRlcnMgd2l0aCBlbmhhbmNlZCBoaWdobGlnaHRpbmc6XG4gKiAgICAtIENvbW1vbiBwYXJ0cyBhcmUgc2hvd24gbm9ybWFsbHlcbiAqICAgIC0gUmVtb3ZlZCB3b3JkcyBnZXQgYSBkYXJrZXIgcmVkIGJhY2tncm91bmRcbiAqICAgIC0gQWRkZWQgd29yZHMgZ2V0IGEgZGFya2VyIGdyZWVuIGJhY2tncm91bmRcbiAqXG4gKiBUaGlzIHByb2R1Y2VzIGEgdmlzdWFsbHkgY2xlYXIgZGlmZiB3aGVyZSB1c2VycyBjYW4gc2VlIGV4YWN0bHkgd2hpY2ggd29yZHNcbiAqIGNoYW5nZWQgcmF0aGVyIHRoYW4ganVzdCB3aGljaCBsaW5lcyB3ZXJlIG1vZGlmaWVkLlxuICovXG5cbi8vIERlZmluZSBEaWZmTGluZSBpbnRlcmZhY2UgdG8gYmUgdXNlZCB0aHJvdWdob3V0IHRoZSBmaWxlXG5pbnRlcmZhY2UgRGlmZkxpbmUge1xuICBjb2RlOiBzdHJpbmdcbiAgdHlwZTogJ2FkZCcgfCAncmVtb3ZlJyB8ICdub2NoYW5nZSdcbiAgaTogbnVtYmVyXG4gIG9yaWdpbmFsQ29kZTogc3RyaW5nXG4gIHdvcmREaWZmPzogYm9vbGVhbiAvLyBGbGFnIGZvciB3b3JkLWxldmVsIGRpZmZpbmdcbiAgbWF0Y2hlZExpbmU/OiBEaWZmTGluZVxufVxuXG4vLyBMaW5lIG9iamVjdCB0eXBlIGZvciBpbnRlcm5hbCBmdW5jdGlvbnNcbmV4cG9ydCBpbnRlcmZhY2UgTGluZU9iamVjdCB7XG4gIGNvZGU6IHN0cmluZ1xuICBpOiBudW1iZXJcbiAgdHlwZTogJ2FkZCcgfCAncmVtb3ZlJyB8ICdub2NoYW5nZSdcbiAgb3JpZ2luYWxDb2RlOiBzdHJpbmdcbiAgd29yZERpZmY/OiBib29sZWFuXG4gIG1hdGNoZWRMaW5lPzogTGluZU9iamVjdFxufVxuXG4vLyBUeXBlIGZvciB3b3JkLWxldmVsIGRpZmYgcGFydHNcbmludGVyZmFjZSBEaWZmUGFydCB7XG4gIGFkZGVkPzogYm9vbGVhblxuICByZW1vdmVkPzogYm9vbGVhblxuICB2YWx1ZTogc3RyaW5nXG59XG5cbnR5cGUgUHJvcHMgPSB7XG4gIHBhdGNoOiBTdHJ1Y3R1cmVkUGF0Y2hIdW5rXG4gIGRpbTogYm9vbGVhblxuICB3aWR0aDogbnVtYmVyXG59XG5cbi8vIFRocmVzaG9sZCBmb3Igd2hlbiB3ZSBzaG93IGEgZnVsbC1saW5lIGRpZmYgaW5zdGVhZCBvZiB3b3JkLWxldmVsIGRpZmZpbmdcbmNvbnN0IENIQU5HRV9USFJFU0hPTEQgPSAwLjRcblxuZXhwb3J0IGZ1bmN0aW9uIFN0cnVjdHVyZWREaWZmRmFsbGJhY2soe1xuICBwYXRjaCxcbiAgZGltLFxuICB3aWR0aCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3RoZW1lXSA9IHVzZVRoZW1lKClcbiAgY29uc3QgZGlmZiA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZm9ybWF0RGlmZihwYXRjaC5saW5lcywgcGF0Y2gub2xkU3RhcnQsIHdpZHRoLCBkaW0sIHRoZW1lKSxcbiAgICBbcGF0Y2gubGluZXMsIHBhdGNoLm9sZFN0YXJ0LCB3aWR0aCwgZGltLCB0aGVtZV0sXG4gIClcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGZsZXhHcm93PXsxfT5cbiAgICAgIHtkaWZmLm1hcCgobm9kZSwgaSkgPT4gKFxuICAgICAgICA8Qm94IGtleT17aX0+e25vZGV9PC9Cb3g+XG4gICAgICApKX1cbiAgICA8L0JveD5cbiAgKVxufVxuXG4vLyBUcmFuc2Zvcm0gbGluZXMgdG8gbGluZSBvYmplY3RzIHdpdGggdHlwZSBpbmZvcm1hdGlvblxuZXhwb3J0IGZ1bmN0aW9uIHRyYW5zZm9ybUxpbmVzVG9PYmplY3RzKGxpbmVzOiBzdHJpbmdbXSk6IExpbmVPYmplY3RbXSB7XG4gIHJldHVybiBsaW5lcy5tYXAoY29kZSA9PiB7XG4gICAgaWYgKGNvZGUuc3RhcnRzV2l0aCgnKycpKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBjb2RlOiBjb2RlLnNsaWNlKDEpLFxuICAgICAgICBpOiAwLFxuICAgICAgICB0eXBlOiAnYWRkJyxcbiAgICAgICAgb3JpZ2luYWxDb2RlOiBjb2RlLnNsaWNlKDEpLFxuICAgICAgfVxuICAgIH1cbiAgICBpZiAoY29kZS5zdGFydHNXaXRoKCctJykpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvZGU6IGNvZGUuc2xpY2UoMSksXG4gICAgICAgIGk6IDAsXG4gICAgICAgIHR5cGU6ICdyZW1vdmUnLFxuICAgICAgICBvcmlnaW5hbENvZGU6IGNvZGUuc2xpY2UoMSksXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBjb2RlOiBjb2RlLnNsaWNlKDEpLFxuICAgICAgaTogMCxcbiAgICAgIHR5cGU6ICdub2NoYW5nZScsXG4gICAgICBvcmlnaW5hbENvZGU6IGNvZGUuc2xpY2UoMSksXG4gICAgfVxuICB9KVxufVxuXG4vLyBHcm91cCBhZGphY2VudCBhZGQvcmVtb3ZlIGxpbmVzIGZvciB3b3JkLWxldmVsIGRpZmZpbmdcbmV4cG9ydCBmdW5jdGlvbiBwcm9jZXNzQWRqYWNlbnRMaW5lcyhsaW5lT2JqZWN0czogTGluZU9iamVjdFtdKTogTGluZU9iamVjdFtdIHtcbiAgY29uc3QgcHJvY2Vzc2VkTGluZXM6IExpbmVPYmplY3RbXSA9IFtdXG4gIGxldCBpID0gMFxuXG4gIHdoaWxlIChpIDwgbGluZU9iamVjdHMubGVuZ3RoKSB7XG4gICAgY29uc3QgY3VycmVudCA9IGxpbmVPYmplY3RzW2ldXG4gICAgaWYgKCFjdXJyZW50KSB7XG4gICAgICBpKytcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgLy8gRmluZCBhIHNlcXVlbmNlIG9mIHJlbW92ZSBmb2xsb3dlZCBieSBhZGQgKHBvc3NpYmxlIHdvcmQtbGV2ZWwgZGlmZiBjYW5kaWRhdGVzKVxuICAgIGlmIChjdXJyZW50LnR5cGUgPT09ICdyZW1vdmUnKSB7XG4gICAgICBjb25zdCByZW1vdmVMaW5lczogTGluZU9iamVjdFtdID0gW2N1cnJlbnRdXG4gICAgICBsZXQgaiA9IGkgKyAxXG5cbiAgICAgIC8vIENvbGxlY3QgY29uc2VjdXRpdmUgcmVtb3ZlIGxpbmVzXG4gICAgICB3aGlsZSAoaiA8IGxpbmVPYmplY3RzLmxlbmd0aCAmJiBsaW5lT2JqZWN0c1tqXT8udHlwZSA9PT0gJ3JlbW92ZScpIHtcbiAgICAgICAgY29uc3QgbGluZSA9IGxpbmVPYmplY3RzW2pdXG4gICAgICAgIGlmIChsaW5lKSB7XG4gICAgICAgICAgcmVtb3ZlTGluZXMucHVzaChsaW5lKVxuICAgICAgICB9XG4gICAgICAgIGorK1xuICAgICAgfVxuXG4gICAgICAvLyBDaGVjayBpZiB0aGVyZSBhcmUgYWRkIGxpbmVzIGZvbGxvd2luZyB0aGUgcmVtb3ZlIGxpbmVzXG4gICAgICBjb25zdCBhZGRMaW5lczogTGluZU9iamVjdFtdID0gW11cbiAgICAgIHdoaWxlIChqIDwgbGluZU9iamVjdHMubGVuZ3RoICYmIGxpbmVPYmplY3RzW2pdPy50eXBlID09PSAnYWRkJykge1xuICAgICAgICBjb25zdCBsaW5lID0gbGluZU9iamVjdHNbal1cbiAgICAgICAgaWYgKGxpbmUpIHtcbiAgICAgICAgICBhZGRMaW5lcy5wdXNoKGxpbmUpXG4gICAgICAgIH1cbiAgICAgICAgaisrXG4gICAgICB9XG5cbiAgICAgIC8vIElmIHdlIGhhdmUgYm90aCByZW1vdmUgYW5kIGFkZCBsaW5lcywgcGVyZm9ybSB3b3JkLWxldmVsIGRpZmZpbmdcbiAgICAgIGlmIChyZW1vdmVMaW5lcy5sZW5ndGggPiAwICYmIGFkZExpbmVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgLy8gRm9yIHdvcmQgZGlmZmluZywgd2UnbGwgY29tcGFyZSBlYWNoIHBhaXIgb2YgbGluZXMgb3IgdGhlIGNsb3Nlc3QgYXZhaWxhYmxlIG1hdGNoXG4gICAgICAgIGNvbnN0IHBhaXJDb3VudCA9IE1hdGgubWluKHJlbW92ZUxpbmVzLmxlbmd0aCwgYWRkTGluZXMubGVuZ3RoKVxuXG4gICAgICAgIC8vIEFkZCBwYWlyZWQgbGluZXMgd2l0aCB3b3JkIGRpZmYgaW5mb1xuICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IHBhaXJDb3VudDsgaysrKSB7XG4gICAgICAgICAgY29uc3QgcmVtb3ZlTGluZSA9IHJlbW92ZUxpbmVzW2tdXG4gICAgICAgICAgY29uc3QgYWRkTGluZSA9IGFkZExpbmVzW2tdXG5cbiAgICAgICAgICBpZiAocmVtb3ZlTGluZSAmJiBhZGRMaW5lKSB7XG4gICAgICAgICAgICByZW1vdmVMaW5lLndvcmREaWZmID0gdHJ1ZVxuICAgICAgICAgICAgYWRkTGluZS53b3JkRGlmZiA9IHRydWVcblxuICAgICAgICAgICAgLy8gU3RvcmUgdGhlIG1hdGNoZWQgcGFpciBmb3IgbGF0ZXIgd29yZCBkaWZmaW5nXG4gICAgICAgICAgICByZW1vdmVMaW5lLm1hdGNoZWRMaW5lID0gYWRkTGluZVxuICAgICAgICAgICAgYWRkTGluZS5tYXRjaGVkTGluZSA9IHJlbW92ZUxpbmVcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBZGQgYWxsIHJlbW92ZSBsaW5lcyAoYm90aCBwYWlyZWQgYW5kIHVucGFpcmVkKVxuICAgICAgICBwcm9jZXNzZWRMaW5lcy5wdXNoKC4uLnJlbW92ZUxpbmVzLmZpbHRlcihCb29sZWFuKSlcblxuICAgICAgICAvLyBUaGVuIGFkZCBhbGwgYWRkIGxpbmVzIChib3RoIHBhaXJlZCBhbmQgdW5wYWlyZWQpXG4gICAgICAgIHByb2Nlc3NlZExpbmVzLnB1c2goLi4uYWRkTGluZXMuZmlsdGVyKEJvb2xlYW4pKVxuXG4gICAgICAgIGkgPSBqIC8vIFNraXAgYWxsIHRoZSBsaW5lcyB3ZSd2ZSBwcm9jZXNzZWRcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIE5vIG1hdGNoaW5nIGFkZCBsaW5lcywganVzdCBhZGQgdGhlIGN1cnJlbnQgcmVtb3ZlIGxpbmVcbiAgICAgICAgcHJvY2Vzc2VkTGluZXMucHVzaChjdXJyZW50KVxuICAgICAgICBpKytcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gTm90IGEgcmVtb3ZlIGxpbmUsIGp1c3QgYWRkIGl0XG4gICAgICBwcm9jZXNzZWRMaW5lcy5wdXNoKGN1cnJlbnQpXG4gICAgICBpKytcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcHJvY2Vzc2VkTGluZXNcbn1cblxuLy8gQ2FsY3VsYXRlIHdvcmQtbGV2ZWwgZGlmZnMgYmV0d2VlbiB0d28gdGV4dCBzdHJpbmdzXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlV29yZERpZmZzKFxuICBvbGRUZXh0OiBzdHJpbmcsXG4gIG5ld1RleHQ6IHN0cmluZyxcbik6IERpZmZQYXJ0W10ge1xuICAvLyBVc2UgZGlmZldvcmRzV2l0aFNwYWNlIGluc3RlYWQgb2YgZGlmZldvcmRzIHRvIHByZXNlcnZlIHdoaXRlc3BhY2VcbiAgLy8gVGhpcyBlbnN1cmVzIHNwYWNlcyBiZXR3ZWVuIHRva2VucyBsaWtlID4gYW5kIHsgYXJlIHByZXNlcnZlZFxuICBjb25zdCByZXN1bHQgPSBkaWZmV29yZHNXaXRoU3BhY2Uob2xkVGV4dCwgbmV3VGV4dCwgeyBpZ25vcmVDYXNlOiBmYWxzZSB9KVxuXG4gIHJldHVybiByZXN1bHRcbn1cblxuLy8gUHJvY2VzcyB3b3JkLWxldmVsIGRpZmZzIHdpdGggbWFudWFsIHdyYXBwaW5nIHN1cHBvcnRcbmZ1bmN0aW9uIGdlbmVyYXRlV29yZERpZmZFbGVtZW50cyhcbiAgaXRlbTogRGlmZkxpbmUsXG4gIHdpZHRoOiBudW1iZXIsXG4gIG1heFdpZHRoOiBudW1iZXIsXG4gIGRpbTogYm9vbGVhbixcbiAgb3ZlcnJpZGVUaGVtZT86IFRoZW1lTmFtZSxcbik6IFJlYWN0LlJlYWN0Tm9kZVtdIHwgbnVsbCB7XG4gIGNvbnN0IHsgdHlwZSwgaSwgd29yZERpZmYsIG1hdGNoZWRMaW5lLCBvcmlnaW5hbENvZGUgfSA9IGl0ZW1cblxuICBpZiAoIXdvcmREaWZmIHx8ICFtYXRjaGVkTGluZSkge1xuICAgIHJldHVybiBudWxsIC8vIFRoaXMgZnVuY3Rpb24gb25seSBoYW5kbGVzIHdvcmQtbGV2ZWwgZGlmZiByZW5kZXJpbmdcbiAgfVxuXG4gIGNvbnN0IHJlbW92ZWRMaW5lVGV4dCA9XG4gICAgdHlwZSA9PT0gJ3JlbW92ZScgPyBvcmlnaW5hbENvZGUgOiBtYXRjaGVkTGluZS5vcmlnaW5hbENvZGVcbiAgY29uc3QgYWRkZWRMaW5lVGV4dCA9XG4gICAgdHlwZSA9PT0gJ3JlbW92ZScgPyBtYXRjaGVkTGluZS5vcmlnaW5hbENvZGUgOiBvcmlnaW5hbENvZGVcblxuICBjb25zdCB3b3JkRGlmZnMgPSBjYWxjdWxhdGVXb3JkRGlmZnMocmVtb3ZlZExpbmVUZXh0LCBhZGRlZExpbmVUZXh0KVxuXG4gIC8vIENoZWNrIGlmIHdlIHNob3VsZCB1c2Ugd29yZC1sZXZlbCBkaWZmaW5nXG4gIGNvbnN0IHRvdGFsTGVuZ3RoID0gcmVtb3ZlZExpbmVUZXh0Lmxlbmd0aCArIGFkZGVkTGluZVRleHQubGVuZ3RoXG4gIGNvbnN0IGNoYW5nZWRMZW5ndGggPSB3b3JkRGlmZnNcbiAgICAuZmlsdGVyKHBhcnQgPT4gcGFydC5hZGRlZCB8fCBwYXJ0LnJlbW92ZWQpXG4gICAgLnJlZHVjZSgoc3VtLCBwYXJ0KSA9PiBzdW0gKyBwYXJ0LnZhbHVlLmxlbmd0aCwgMClcbiAgY29uc3QgY2hhbmdlUmF0aW8gPSBjaGFuZ2VkTGVuZ3RoIC8gdG90YWxMZW5ndGhcblxuICBpZiAoY2hhbmdlUmF0aW8gPiBDSEFOR0VfVEhSRVNIT0xEIHx8IGRpbSkge1xuICAgIHJldHVybiBudWxsIC8vIEZhbGwgYmFjayB0byBzdGFuZGFyZCByZW5kZXJpbmcgZm9yIG1ham9yIGNoYW5nZXNcbiAgfVxuXG4gIC8vIENhbGN1bGF0ZSBhdmFpbGFibGUgd2lkdGggZm9yIGNvbnRlbnRcbiAgY29uc3QgZGlmZlByZWZpeCA9IHR5cGUgPT09ICdhZGQnID8gJysnIDogJy0nXG4gIGNvbnN0IGRpZmZQcmVmaXhXaWR0aCA9IGRpZmZQcmVmaXgubGVuZ3RoXG4gIGNvbnN0IGF2YWlsYWJsZUNvbnRlbnRXaWR0aCA9IE1hdGgubWF4KFxuICAgIDEsXG4gICAgd2lkdGggLSBtYXhXaWR0aCAtIDEgLSBkaWZmUHJlZml4V2lkdGgsXG4gIClcblxuICAvLyBNYW51YWxseSB3cmFwIHRoZSB3b3JkIGRpZmYgcGFydHMgd2l0aCBiZXR0ZXIgc3BhY2UgZWZmaWNpZW5jeVxuICBjb25zdCB3cmFwcGVkTGluZXM6IHsgY29udGVudDogUmVhY3QuUmVhY3ROb2RlW107IGNvbnRlbnRXaWR0aDogbnVtYmVyIH1bXSA9XG4gICAgW11cbiAgbGV0IGN1cnJlbnRMaW5lOiBSZWFjdC5SZWFjdE5vZGVbXSA9IFtdXG4gIGxldCBjdXJyZW50TGluZVdpZHRoID0gMFxuXG4gIHdvcmREaWZmcy5mb3JFYWNoKChwYXJ0LCBwYXJ0SW5kZXgpID0+IHtcbiAgICAvLyBEZXRlcm1pbmUgaWYgdGhpcyBwYXJ0IHNob3VsZCBiZSBzaG93biBmb3IgdGhpcyBsaW5lIHR5cGVcbiAgICBsZXQgc2hvdWxkU2hvdyA9IGZhbHNlXG4gICAgbGV0IHBhcnRCZ0NvbG9yOiAnZGlmZkFkZGVkV29yZCcgfCAnZGlmZlJlbW92ZWRXb3JkJyB8IHVuZGVmaW5lZFxuXG4gICAgaWYgKHR5cGUgPT09ICdhZGQnKSB7XG4gICAgICBpZiAocGFydC5hZGRlZCkge1xuICAgICAgICBzaG91bGRTaG93ID0gdHJ1ZVxuICAgICAgICBwYXJ0QmdDb2xvciA9ICdkaWZmQWRkZWRXb3JkJ1xuICAgICAgfSBlbHNlIGlmICghcGFydC5yZW1vdmVkKSB7XG4gICAgICAgIHNob3VsZFNob3cgPSB0cnVlXG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAncmVtb3ZlJykge1xuICAgICAgaWYgKHBhcnQucmVtb3ZlZCkge1xuICAgICAgICBzaG91bGRTaG93ID0gdHJ1ZVxuICAgICAgICBwYXJ0QmdDb2xvciA9ICdkaWZmUmVtb3ZlZFdvcmQnXG4gICAgICB9IGVsc2UgaWYgKCFwYXJ0LmFkZGVkKSB7XG4gICAgICAgIHNob3VsZFNob3cgPSB0cnVlXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFzaG91bGRTaG93KSByZXR1cm5cblxuICAgIC8vIFVzZSB3cmFwVGV4dCB0byB3cmFwIHRoaXMgaW5kaXZpZHVhbCBwYXJ0IGlmIGl0J3MgbG9uZ1xuICAgIGNvbnN0IHBhcnRXcmFwcGVkID0gd3JhcFRleHQocGFydC52YWx1ZSwgYXZhaWxhYmxlQ29udGVudFdpZHRoLCAnd3JhcCcpXG4gICAgY29uc3QgcGFydExpbmVzID0gcGFydFdyYXBwZWQuc3BsaXQoJ1xcbicpXG5cbiAgICBwYXJ0TGluZXMuZm9yRWFjaCgocGFydExpbmUsIGxpbmVJZHgpID0+IHtcbiAgICAgIGlmICghcGFydExpbmUpIHJldHVyblxuXG4gICAgICAvLyBDaGVjayBpZiB3ZSBuZWVkIHRvIHN0YXJ0IGEgbmV3IGxpbmVcbiAgICAgIGlmIChcbiAgICAgICAgbGluZUlkeCA+IDAgfHxcbiAgICAgICAgY3VycmVudExpbmVXaWR0aCArIHN0cmluZ1dpZHRoKHBhcnRMaW5lKSA+IGF2YWlsYWJsZUNvbnRlbnRXaWR0aFxuICAgICAgKSB7XG4gICAgICAgIGlmIChjdXJyZW50TGluZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgd3JhcHBlZExpbmVzLnB1c2goe1xuICAgICAgICAgICAgY29udGVudDogWy4uLmN1cnJlbnRMaW5lXSxcbiAgICAgICAgICAgIGNvbnRlbnRXaWR0aDogY3VycmVudExpbmVXaWR0aCxcbiAgICAgICAgICB9KVxuICAgICAgICAgIGN1cnJlbnRMaW5lID0gW11cbiAgICAgICAgICBjdXJyZW50TGluZVdpZHRoID0gMFxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGN1cnJlbnRMaW5lLnB1c2goXG4gICAgICAgIDxUZXh0XG4gICAgICAgICAga2V5PXtgcGFydC0ke3BhcnRJbmRleH0tJHtsaW5lSWR4fWB9XG4gICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtwYXJ0QmdDb2xvcn1cbiAgICAgICAgPlxuICAgICAgICAgIHtwYXJ0TGluZX1cbiAgICAgICAgPC9UZXh0PixcbiAgICAgIClcblxuICAgICAgY3VycmVudExpbmVXaWR0aCArPSBzdHJpbmdXaWR0aChwYXJ0TGluZSlcbiAgICB9KVxuICB9KVxuXG4gIGlmIChjdXJyZW50TGluZS5sZW5ndGggPiAwKSB7XG4gICAgd3JhcHBlZExpbmVzLnB1c2goeyBjb250ZW50OiBjdXJyZW50TGluZSwgY29udGVudFdpZHRoOiBjdXJyZW50TGluZVdpZHRoIH0pXG4gIH1cblxuICAvLyBSZW5kZXIgZWFjaCB3cmFwcGVkIGxpbmUgYXMgYSBzZXBhcmF0ZSBUZXh0IGVsZW1lbnRcbiAgcmV0dXJuIHdyYXBwZWRMaW5lcy5tYXAoKHsgY29udGVudCwgY29udGVudFdpZHRoIH0sIGxpbmVJbmRleCkgPT4ge1xuICAgIGNvbnN0IGtleSA9IGAke3R5cGV9LSR7aX0tJHtsaW5lSW5kZXh9YFxuICAgIGNvbnN0IGxpbmVCZ0NvbG9yID1cbiAgICAgIHR5cGUgPT09ICdhZGQnXG4gICAgICAgID8gZGltXG4gICAgICAgICAgPyAnZGlmZkFkZGVkRGltbWVkJ1xuICAgICAgICAgIDogJ2RpZmZBZGRlZCdcbiAgICAgICAgOiBkaW1cbiAgICAgICAgICA/ICdkaWZmUmVtb3ZlZERpbW1lZCdcbiAgICAgICAgICA6ICdkaWZmUmVtb3ZlZCdcbiAgICBjb25zdCBsaW5lTnVtID0gbGluZUluZGV4ID09PSAwID8gaSA6IHVuZGVmaW5lZFxuICAgIGNvbnN0IGxpbmVOdW1TdHIgPVxuICAgICAgKGxpbmVOdW0gIT09IHVuZGVmaW5lZFxuICAgICAgICA/IGxpbmVOdW0udG9TdHJpbmcoKS5wYWRTdGFydChtYXhXaWR0aClcbiAgICAgICAgOiAnICcucmVwZWF0KG1heFdpZHRoKSkgKyAnICdcbiAgICAvLyBDYWxjdWxhdGUgcGFkZGluZyB0byBmaWxsIHRoZSBlbnRpcmUgdGVybWluYWwgd2lkdGhcbiAgICBjb25zdCB1c2VkV2lkdGggPSBsaW5lTnVtU3RyLmxlbmd0aCArIGRpZmZQcmVmaXhXaWR0aCArIGNvbnRlbnRXaWR0aFxuICAgIGNvbnN0IHBhZGRpbmcgPSBNYXRoLm1heCgwLCB3aWR0aCAtIHVzZWRXaWR0aClcblxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGtleT17a2V5fSBmbGV4RGlyZWN0aW9uPVwicm93XCI+XG4gICAgICAgIDxOb1NlbGVjdCBmcm9tTGVmdEVkZ2U+XG4gICAgICAgICAgPFRleHRcbiAgICAgICAgICAgIGNvbG9yPXtvdmVycmlkZVRoZW1lID8gJ3RleHQnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtsaW5lQmdDb2xvcn1cbiAgICAgICAgICAgIGRpbUNvbG9yPXtkaW19XG4gICAgICAgICAgPlxuICAgICAgICAgICAge2xpbmVOdW1TdHJ9XG4gICAgICAgICAgICB7ZGlmZlByZWZpeH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvTm9TZWxlY3Q+XG4gICAgICAgIDxUZXh0XG4gICAgICAgICAgY29sb3I9e292ZXJyaWRlVGhlbWUgPyAndGV4dCcgOiB1bmRlZmluZWR9XG4gICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtsaW5lQmdDb2xvcn1cbiAgICAgICAgICBkaW1Db2xvcj17ZGltfVxuICAgICAgICA+XG4gICAgICAgICAge2NvbnRlbnR9XG4gICAgICAgICAgeycgJy5yZXBlYXQocGFkZGluZyl9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfSlcbn1cblxuZnVuY3Rpb24gZm9ybWF0RGlmZihcbiAgbGluZXM6IHN0cmluZ1tdLFxuICBzdGFydGluZ0xpbmVOdW1iZXI6IG51bWJlcixcbiAgd2lkdGg6IG51bWJlcixcbiAgZGltOiBib29sZWFuLFxuICBvdmVycmlkZVRoZW1lPzogVGhlbWVOYW1lLFxuKTogUmVhY3QuUmVhY3ROb2RlW10ge1xuICAvLyBFbnN1cmUgd2lkdGggaXMgYXQgbGVhc3QgMSB0byBwcmV2ZW50IHJlbmRlcmluZyBpc3N1ZXMgd2l0aCB2ZXJ5IG5hcnJvdyB0ZXJtaW5hbHNcbiAgY29uc3Qgc2FmZVdpZHRoID0gTWF0aC5tYXgoMSwgTWF0aC5mbG9vcih3aWR0aCkpXG5cbiAgLy8gU3RlcCAxOiBUcmFuc2Zvcm0gbGluZXMgdG8gbGluZSBvYmplY3RzIHdpdGggdHlwZSBpbmZvcm1hdGlvblxuICBjb25zdCBsaW5lT2JqZWN0cyA9IHRyYW5zZm9ybUxpbmVzVG9PYmplY3RzKGxpbmVzKVxuXG4gIC8vIFN0ZXAgMjogR3JvdXAgYWRqYWNlbnQgYWRkL3JlbW92ZSBsaW5lcyBmb3Igd29yZC1sZXZlbCBkaWZmaW5nXG4gIGNvbnN0IHByb2Nlc3NlZExpbmVzID0gcHJvY2Vzc0FkamFjZW50TGluZXMobGluZU9iamVjdHMpXG5cbiAgLy8gU3RlcCAzOiBOdW1iZXIgdGhlIGRpZmYgbGluZXNcbiAgY29uc3QgbHMgPSBudW1iZXJEaWZmTGluZXMocHJvY2Vzc2VkTGluZXMsIHN0YXJ0aW5nTGluZU51bWJlcilcblxuICAvLyBGaW5kIG1heCBsaW5lIG51bWJlciB3aWR0aCBmb3IgYWxpZ25tZW50XG4gIGNvbnN0IG1heExpbmVOdW1iZXIgPSBNYXRoLm1heCguLi5scy5tYXAoKHsgaSB9KSA9PiBpKSwgMClcbiAgY29uc3QgbWF4V2lkdGggPSBNYXRoLm1heChtYXhMaW5lTnVtYmVyLnRvU3RyaW5nKCkubGVuZ3RoICsgMSwgMClcblxuICAvLyBTdGVwIDQ6IFJlbmRlciBmb3JtYXR0aW5nXG4gIHJldHVybiBscy5mbGF0TWFwKChpdGVtKTogUmVhY3QuUmVhY3ROb2RlW10gPT4ge1xuICAgIGNvbnN0IHsgdHlwZSwgY29kZSwgaSwgd29yZERpZmYsIG1hdGNoZWRMaW5lIH0gPSBpdGVtXG5cbiAgICAvLyBIYW5kbGUgd29yZC1sZXZlbCBkaWZmaW5nIGZvciBhZGQvcmVtb3ZlIHBhaXJzXG4gICAgaWYgKHdvcmREaWZmICYmIG1hdGNoZWRMaW5lKSB7XG4gICAgICBjb25zdCB3b3JkRGlmZkVsZW1lbnRzID0gZ2VuZXJhdGVXb3JkRGlmZkVsZW1lbnRzKFxuICAgICAgICBpdGVtLFxuICAgICAgICBzYWZlV2lkdGgsXG4gICAgICAgIG1heFdpZHRoLFxuICAgICAgICBkaW0sXG4gICAgICAgIG92ZXJyaWRlVGhlbWUsXG4gICAgICApXG5cbiAgICAgIC8vIHdvcmQtZGlmZiBtaWdodCByZWZ1c2UgKGUuZy4gZHVlIHRvIGxpbmVzIGJlaW5nIHN1YnN0YW50aWFsbHkgZGlmZmVyZW50KSBpbiB3aGljaFxuICAgICAgLy8gY2FzZSB3ZSdsbCBmYWxsIHRocm91Z2ggdG8gbm9ybWFsIHJlbmRlcmluIGdiZWxvd1xuICAgICAgaWYgKHdvcmREaWZmRWxlbWVudHMgIT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuIHdvcmREaWZmRWxlbWVudHNcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBTdGFuZGFyZCByZW5kZXJpbmcgZm9yIGxpbmVzIHdpdGhvdXQgd29yZCBkaWZmaW5nIG9yIGFzIGZhbGxiYWNrXG4gICAgLy8gQ2FsY3VsYXRlIGF2YWlsYWJsZSB3aWR0aCBhY2NvdW50aW5nIGZvciBsaW5lIG51bWJlciArIHNwYWNlICsgZGlmZiBwcmVmaXhcbiAgICBjb25zdCBkaWZmUHJlZml4V2lkdGggPSAyIC8vIFwiICBcIiBmb3IgdW5jaGFuZ2VkLCBcIisgXCIgb3IgXCItIFwiIGZvciBjaGFuZ2VzXG4gICAgY29uc3QgYXZhaWxhYmxlQ29udGVudFdpZHRoID0gTWF0aC5tYXgoXG4gICAgICAxLFxuICAgICAgc2FmZVdpZHRoIC0gbWF4V2lkdGggLSAxIC0gZGlmZlByZWZpeFdpZHRoLFxuICAgICkgLy8gLTEgZm9yIHNwYWNlIGFmdGVyIGxpbmUgbnVtYmVyXG4gICAgY29uc3Qgd3JhcHBlZFRleHQgPSB3cmFwVGV4dChjb2RlLCBhdmFpbGFibGVDb250ZW50V2lkdGgsICd3cmFwJylcbiAgICBjb25zdCB3cmFwcGVkTGluZXMgPSB3cmFwcGVkVGV4dC5zcGxpdCgnXFxuJylcblxuICAgIHJldHVybiB3cmFwcGVkTGluZXMubWFwKChsaW5lLCBsaW5lSW5kZXgpID0+IHtcbiAgICAgIGNvbnN0IGtleSA9IGAke3R5cGV9LSR7aX0tJHtsaW5lSW5kZXh9YFxuICAgICAgY29uc3QgbGluZU51bSA9IGxpbmVJbmRleCA9PT0gMCA/IGkgOiB1bmRlZmluZWRcbiAgICAgIGNvbnN0IGxpbmVOdW1TdHIgPVxuICAgICAgICAobGluZU51bSAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgPyBsaW5lTnVtLnRvU3RyaW5nKCkucGFkU3RhcnQobWF4V2lkdGgpXG4gICAgICAgICAgOiAnICcucmVwZWF0KG1heFdpZHRoKSkgKyAnICdcbiAgICAgIGNvbnN0IHNpZ2lsID0gdHlwZSA9PT0gJ2FkZCcgPyAnKycgOiB0eXBlID09PSAncmVtb3ZlJyA/ICctJyA6ICcgJ1xuICAgICAgLy8gQ2FsY3VsYXRlIHBhZGRpbmcgdG8gZmlsbCB0aGUgZW50aXJlIHRlcm1pbmFsIHdpZHRoXG4gICAgICBjb25zdCBjb250ZW50V2lkdGggPSBsaW5lTnVtU3RyLmxlbmd0aCArIDEgKyBzdHJpbmdXaWR0aChsaW5lKSAvLyBsaW5lTnVtICsgc2lnaWwgKyBjb2RlXG4gICAgICBjb25zdCBwYWRkaW5nID0gTWF0aC5tYXgoMCwgc2FmZVdpZHRoIC0gY29udGVudFdpZHRoKVxuXG4gICAgICBjb25zdCBiZ0NvbG9yID1cbiAgICAgICAgdHlwZSA9PT0gJ2FkZCdcbiAgICAgICAgICA/IGRpbVxuICAgICAgICAgICAgPyAnZGlmZkFkZGVkRGltbWVkJ1xuICAgICAgICAgICAgOiAnZGlmZkFkZGVkJ1xuICAgICAgICAgIDogdHlwZSA9PT0gJ3JlbW92ZSdcbiAgICAgICAgICAgID8gZGltXG4gICAgICAgICAgICAgID8gJ2RpZmZSZW1vdmVkRGltbWVkJ1xuICAgICAgICAgICAgICA6ICdkaWZmUmVtb3ZlZCdcbiAgICAgICAgICAgIDogdW5kZWZpbmVkXG5cbiAgICAgIC8vIEd1dHRlciAobGluZSBudW1iZXIgKyBzaWdpbCkgaXMgd3JhcHBlZCBpbiA8Tm9TZWxlY3Q+IHNvIGZ1bGxzY3JlZW5cbiAgICAgIC8vIHRleHQgc2VsZWN0aW9uIHlpZWxkcyBjbGVhbiBjb2RlLiBiZ0NvbG9yIGNhcnJpZXMgYWNyb3NzIGJvdGggYm94ZXNcbiAgICAgIC8vIHNvIHRoZSB2aXN1YWwgY29udGludWl0eSAoc29saWQgcmVkL2dyZWVuIGJhcikgaXMgdW5jaGFuZ2VkLlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPEJveCBrZXk9e2tleX0gZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgICAgIDxOb1NlbGVjdCBmcm9tTGVmdEVkZ2U+XG4gICAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgICBjb2xvcj17b3ZlcnJpZGVUaGVtZSA/ICd0ZXh0JyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtiZ0NvbG9yfVxuICAgICAgICAgICAgICBkaW1Db2xvcj17ZGltIHx8IHR5cGUgPT09ICdub2NoYW5nZSd9XG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgIHtsaW5lTnVtU3RyfVxuICAgICAgICAgICAgICB7c2lnaWx9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9Ob1NlbGVjdD5cbiAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgY29sb3I9e292ZXJyaWRlVGhlbWUgPyAndGV4dCcgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3I9e2JnQ29sb3J9XG4gICAgICAgICAgICBkaW1Db2xvcj17ZGltfVxuICAgICAgICAgID5cbiAgICAgICAgICAgIHtsaW5lfVxuICAgICAgICAgICAgeycgJy5yZXBlYXQocGFkZGluZyl9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIClcbiAgICB9KVxuICB9KVxufVxuXG5leHBvcnQgZnVuY3Rpb24gbnVtYmVyRGlmZkxpbmVzKFxuICBkaWZmOiBMaW5lT2JqZWN0W10sXG4gIHN0YXJ0TGluZTogbnVtYmVyLFxuKTogRGlmZkxpbmVbXSB7XG4gIGxldCBpID0gc3RhcnRMaW5lXG4gIGNvbnN0IHJlc3VsdDogRGlmZkxpbmVbXSA9IFtdXG4gIGNvbnN0IHF1ZXVlID0gWy4uLmRpZmZdXG5cbiAgd2hpbGUgKHF1ZXVlLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBjdXJyZW50ID0gcXVldWUuc2hpZnQoKSFcbiAgICBjb25zdCB7IGNvZGUsIHR5cGUsIG9yaWdpbmFsQ29kZSwgd29yZERpZmYsIG1hdGNoZWRMaW5lIH0gPSBjdXJyZW50XG4gICAgY29uc3QgbGluZSA9IHtcbiAgICAgIGNvZGUsXG4gICAgICB0eXBlLFxuICAgICAgaSxcbiAgICAgIG9yaWdpbmFsQ29kZSxcbiAgICAgIHdvcmREaWZmLFxuICAgICAgbWF0Y2hlZExpbmUsXG4gICAgfVxuXG4gICAgLy8gVXBkYXRlIGNvdW50ZXJzIGJhc2VkIG9uIGNoYW5nZSB0eXBlXG4gICAgc3dpdGNoICh0eXBlKSB7XG4gICAgICBjYXNlICdub2NoYW5nZSc6XG4gICAgICAgIGkrK1xuICAgICAgICByZXN1bHQucHVzaChsaW5lKVxuICAgICAgICBicmVha1xuICAgICAgY2FzZSAnYWRkJzpcbiAgICAgICAgaSsrXG4gICAgICAgIHJlc3VsdC5wdXNoKGxpbmUpXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdyZW1vdmUnOiB7XG4gICAgICAgIHJlc3VsdC5wdXNoKGxpbmUpXG4gICAgICAgIGxldCBudW1SZW1vdmVkID0gMFxuICAgICAgICB3aGlsZSAocXVldWVbMF0/LnR5cGUgPT09ICdyZW1vdmUnKSB7XG4gICAgICAgICAgaSsrXG4gICAgICAgICAgY29uc3QgY3VycmVudCA9IHF1ZXVlLnNoaWZ0KCkhXG4gICAgICAgICAgY29uc3QgeyBjb2RlLCB0eXBlLCBvcmlnaW5hbENvZGUsIHdvcmREaWZmLCBtYXRjaGVkTGluZSB9ID0gY3VycmVudFxuICAgICAgICAgIGNvbnN0IGxpbmUgPSB7XG4gICAgICAgICAgICBjb2RlLFxuICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgIGksXG4gICAgICAgICAgICBvcmlnaW5hbENvZGUsXG4gICAgICAgICAgICB3b3JkRGlmZixcbiAgICAgICAgICAgIG1hdGNoZWRMaW5lLFxuICAgICAgICAgIH1cbiAgICAgICAgICByZXN1bHQucHVzaChsaW5lKVxuICAgICAgICAgIG51bVJlbW92ZWQrK1xuICAgICAgICB9XG4gICAgICAgIGkgLT0gbnVtUmVtb3ZlZFxuICAgICAgICBicmVha1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXN1bHRcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLGtCQUFrQixFQUFFLEtBQUtDLG1CQUFtQixRQUFRLE1BQU07QUFDbkUsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxPQUFPLFFBQVEsT0FBTztBQUMvQixjQUFjQyxTQUFTLFFBQVEsb0JBQW9CO0FBQ25ELFNBQVNDLFdBQVcsUUFBUSwwQkFBMEI7QUFDdEQsU0FBU0MsR0FBRyxFQUFFQyxRQUFRLEVBQUVDLElBQUksRUFBRUMsUUFBUSxFQUFFQyxRQUFRLFFBQVEsY0FBYzs7QUFFdEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxVQUFVQyxRQUFRLENBQUM7RUFD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 \ No newline at end of file diff --git a/components/StructuredDiff/colorDiff.ts b/components/StructuredDiff/colorDiff.ts new file mode 100644 index 0000000..d3abaa2 --- /dev/null +++ b/components/StructuredDiff/colorDiff.ts @@ -0,0 +1,37 @@ +import { + ColorDiff, + ColorFile, + getSyntaxTheme as nativeGetSyntaxTheme, + type SyntaxTheme, +} from 'color-diff-napi' +import { isEnvDefinedFalsy } from '../../utils/envUtils.js' + +export type ColorModuleUnavailableReason = 'env' + +/** + * Returns a static reason why the color-diff module is unavailable, or null if available. + * 'env' = disabled via CLAUDE_CODE_SYNTAX_HIGHLIGHT + * + * The TS port of color-diff works in all build modes, so the only way to + * disable it is via the env var. + */ +export function getColorModuleUnavailableReason(): ColorModuleUnavailableReason | null { + if (isEnvDefinedFalsy(process.env.CLAUDE_CODE_SYNTAX_HIGHLIGHT)) { + return 'env' + } + return null +} + +export function expectColorDiff(): typeof ColorDiff | null { + return getColorModuleUnavailableReason() === null ? ColorDiff : null +} + +export function expectColorFile(): typeof ColorFile | null { + return getColorModuleUnavailableReason() === null ? ColorFile : null +} + +export function getSyntaxTheme(themeName: string): SyntaxTheme | null { + return getColorModuleUnavailableReason() === null + ? nativeGetSyntaxTheme(themeName) + : null +} diff --git a/components/StructuredDiffList.tsx b/components/StructuredDiffList.tsx new file mode 100644 index 0000000..31583c2 --- /dev/null +++ b/components/StructuredDiffList.tsx @@ -0,0 +1,30 @@ +import type { StructuredPatchHunk } from 'diff'; +import * as React from 'react'; +import { Box, NoSelect, Text } from '../ink.js'; +import { intersperse } from '../utils/array.js'; +import { StructuredDiff } from './StructuredDiff.js'; +type Props = { + hunks: StructuredPatchHunk[]; + dim: boolean; + width: number; + filePath: string; + firstLine: string | null; + fileContent?: string; +}; + +/** Renders a list of diff hunks with ellipsis separators between them. */ +export function StructuredDiffList({ + hunks, + dim, + width, + filePath, + firstLine, + fileContent +}: Props): React.ReactNode { + return intersperse(hunks.map(hunk => + + ), i => + ... + ); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTdHJ1Y3R1cmVkUGF0Y2hIdW5rIiwiUmVhY3QiLCJCb3giLCJOb1NlbGVjdCIsIlRleHQiLCJpbnRlcnNwZXJzZSIsIlN0cnVjdHVyZWREaWZmIiwiUHJvcHMiLCJodW5rcyIsImRpbSIsIndpZHRoIiwiZmlsZVBhdGgiLCJmaXJzdExpbmUiLCJmaWxlQ29udGVudCIsIlN0cnVjdHVyZWREaWZmTGlzdCIsIlJlYWN0Tm9kZSIsIm1hcCIsImh1bmsiLCJuZXdTdGFydCIsImkiXSwic291cmNlcyI6WyJTdHJ1Y3R1cmVkRGlmZkxpc3QudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgU3RydWN0dXJlZFBhdGNoSHVuayB9IGZyb20gJ2RpZmYnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgTm9TZWxlY3QsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBpbnRlcnNwZXJzZSB9IGZyb20gJy4uL3V0aWxzL2FycmF5LmpzJ1xuaW1wb3J0IHsgU3RydWN0dXJlZERpZmYgfSBmcm9tICcuL1N0cnVjdHVyZWREaWZmLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBodW5rczogU3RydWN0dXJlZFBhdGNoSHVua1tdXG4gIGRpbTogYm9vbGVhblxuICB3aWR0aDogbnVtYmVyXG4gIGZpbGVQYXRoOiBzdHJpbmdcbiAgZmlyc3RMaW5lOiBzdHJpbmcgfCBudWxsXG4gIGZpbGVDb250ZW50Pzogc3RyaW5nXG59XG5cbi8qKiBSZW5kZXJzIGEgbGlzdCBvZiBkaWZmIGh1bmtzIHdpdGggZWxsaXBzaXMgc2VwYXJhdG9ycyBiZXR3ZWVuIHRoZW0uICovXG5leHBvcnQgZnVuY3Rpb24gU3RydWN0dXJlZERpZmZMaXN0KHtcbiAgaHVua3MsXG4gIGRpbSxcbiAgd2lkdGgsXG4gIGZpbGVQYXRoLFxuICBmaXJzdExpbmUsXG4gIGZpbGVDb250ZW50LFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gaW50ZXJzcGVyc2UoXG4gICAgaHVua3MubWFwKGh1bmsgPT4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIga2V5PXtodW5rLm5ld1N0YXJ0fT5cbiAgICAgICAgPFN0cnVjdHVyZWREaWZmXG4gICAgICAgICAgcGF0Y2g9e2h1bmt9XG4gICAgICAgICAgZGltPXtkaW19XG4gICAgICAgICAgd2lkdGg9e3dpZHRofVxuICAgICAgICAgIGZpbGVQYXRoPXtmaWxlUGF0aH1cbiAgICAgICAgICBmaXJzdExpbmU9e2ZpcnN0TGluZX1cbiAgICAgICAgICBmaWxlQ29udGVudD17ZmlsZUNvbnRlbnR9XG4gICAgICAgIC8+XG4gICAgICA8L0JveD5cbiAgICApKSxcbiAgICBpID0+IChcbiAgICAgIDxOb1NlbGVjdCBmcm9tTGVmdEVkZ2Uga2V5PXtgZWxsaXBzaXMtJHtpfWB9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj4uLi48L1RleHQ+XG4gICAgICA8L05vU2VsZWN0PlxuICAgICksXG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsY0FBY0EsbUJBQW1CLFFBQVEsTUFBTTtBQUMvQyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsUUFBUSxFQUFFQyxJQUFJLFFBQVEsV0FBVztBQUMvQyxTQUFTQyxXQUFXLFFBQVEsbUJBQW1CO0FBQy9DLFNBQVNDLGNBQWMsUUFBUSxxQkFBcUI7QUFFcEQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLEtBQUssRUFBRVIsbUJBQW1CLEVBQUU7RUFDNUJTLEdBQUcsRUFBRSxPQUFPO0VBQ1pDLEtBQUssRUFBRSxNQUFNO0VBQ2JDLFFBQVEsRUFBRSxNQUFNO0VBQ2hCQyxTQUFTLEVBQUUsTUFBTSxHQUFHLElBQUk7RUFDeEJDLFdBQVcsQ0FBQyxFQUFFLE1BQU07QUFDdEIsQ0FBQzs7QUFFRDtBQUNBLE9BQU8sU0FBU0Msa0JBQWtCQSxDQUFDO0VBQ2pDTixLQUFLO0VBQ0xDLEdBQUc7RUFDSEMsS0FBSztFQUNMQyxRQUFRO0VBQ1JDLFNBQVM7RUFDVEM7QUFDSyxDQUFOLEVBQUVOLEtBQUssQ0FBQyxFQUFFTixLQUFLLENBQUNjLFNBQVMsQ0FBQztFQUN6QixPQUFPVixXQUFXLENBQ2hCRyxLQUFLLENBQUNRLEdBQUcsQ0FBQ0MsSUFBSSxJQUNaLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUNBLElBQUksQ0FBQ0MsUUFBUSxDQUFDO0FBQ3JELFFBQVEsQ0FBQyxjQUFjLENBQ2IsS0FBSyxDQUFDLENBQUNELElBQUksQ0FBQyxDQUNaLEdBQUcsQ0FBQyxDQUFDUixHQUFHLENBQUMsQ0FDVCxLQUFLLENBQUMsQ0FBQ0MsS0FBSyxDQUFDLENBQ2IsUUFBUSxDQUFDLENBQUNDLFFBQVEsQ0FBQyxDQUNuQixTQUFTLENBQUMsQ0FBQ0MsU0FBUyxDQUFDLENBQ3JCLFdBQVcsQ0FBQyxDQUFDQyxXQUFXLENBQUM7QUFFbkMsTUFBTSxFQUFFLEdBQUcsQ0FDTixDQUFDLEVBQ0ZNLENBQUMsSUFDQyxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsWUFBWUEsQ0FBQyxFQUFFLENBQUM7QUFDbEQsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxFQUFFLElBQUk7QUFDaEMsTUFBTSxFQUFFLFFBQVEsQ0FFZCxDQUFDO0FBQ0giLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/TagTabs.tsx b/components/TagTabs.tsx new file mode 100644 index 0000000..0451bb1 --- /dev/null +++ b/components/TagTabs.tsx @@ -0,0 +1,139 @@ +import React from 'react'; +import { stringWidth } from '../ink/stringWidth.js'; +import { Box, Text } from '../ink.js'; +import { truncateToWidth } from '../utils/format.js'; + +// Constants for width calculations - derived from actual rendered strings +const ALL_TAB_LABEL = 'All'; +const TAB_PADDING = 2; // Space before and after tab text: " {tab} " +const HASH_PREFIX_LENGTH = 1; // "#" prefix for non-All tabs +const LEFT_ARROW_PREFIX = '← '; +const RIGHT_HINT_WITH_COUNT_PREFIX = '→'; +const RIGHT_HINT_SUFFIX = ' (tab to cycle)'; +const RIGHT_HINT_NO_COUNT = '(tab to cycle)'; +const MAX_OVERFLOW_DIGITS = 2; // Assume max 99 hidden tabs for width calculation + +// Computed widths +const LEFT_ARROW_WIDTH = LEFT_ARROW_PREFIX.length + MAX_OVERFLOW_DIGITS + 1; // "← NN " with gap +const RIGHT_HINT_WIDTH_WITH_COUNT = RIGHT_HINT_WITH_COUNT_PREFIX.length + MAX_OVERFLOW_DIGITS + RIGHT_HINT_SUFFIX.length; // "→NN (tab to cycle)" +const RIGHT_HINT_WIDTH_NO_COUNT = RIGHT_HINT_NO_COUNT.length; +type Props = { + tabs: string[]; + selectedIndex: number; + availableWidth: number; + showAllProjects?: boolean; +}; + +/** + * Calculate the display width of a tab + */ +function getTabWidth(tab: string, maxWidth?: number): number { + if (tab === ALL_TAB_LABEL) { + return ALL_TAB_LABEL.length + TAB_PADDING; + } + // For non-All tabs: " #{tag} " but truncate tag if needed + const tagWidth = stringWidth(tab); + const effectiveTagWidth = maxWidth ? Math.min(tagWidth, maxWidth - TAB_PADDING - HASH_PREFIX_LENGTH) : tagWidth; + return Math.max(0, effectiveTagWidth) + TAB_PADDING + HASH_PREFIX_LENGTH; +} + +/** + * Truncate a tag to fit within maxWidth, accounting for padding and hash prefix + */ +function truncateTag(tag: string, maxWidth: number): string { + // Available space for the tag text itself: maxWidth - " #" - " " + const availableForTag = maxWidth - TAB_PADDING - HASH_PREFIX_LENGTH; + if (stringWidth(tag) <= availableForTag) { + return tag; + } + if (availableForTag <= 1) { + return tag.charAt(0); + } + return truncateToWidth(tag, availableForTag); +} +export function TagTabs({ + tabs, + selectedIndex, + availableWidth, + showAllProjects = false +}: Props): React.ReactNode { + const resumeLabel = showAllProjects ? 'Resume (All Projects)' : 'Resume'; + const resumeLabelWidth = resumeLabel.length + 1; // +1 for gap + + // Calculate how much space we have for tabs (use worst-case hint width) + const rightHintWidth = Math.max(RIGHT_HINT_WIDTH_WITH_COUNT, RIGHT_HINT_WIDTH_NO_COUNT); + const maxTabsWidth = availableWidth - resumeLabelWidth - rightHintWidth - 2; // 2 for gaps + + // Clamp selectedIndex to valid range + const safeSelectedIndex = Math.max(0, Math.min(selectedIndex, tabs.length - 1)); + + // Calculate width of each tab, with truncation for very long tags + const maxSingleTabWidth = Math.max(20, Math.floor(maxTabsWidth / 2)); // At least show half the space for one tab + const tabWidths = tabs.map(tab => getTabWidth(tab, maxSingleTabWidth)); + + // Find a window of tabs that fits, centered around selectedIndex + let startIndex = 0; + let endIndex = tabs.length; + + // Calculate total width of all tabs + const totalTabsWidth = tabWidths.reduce((sum, w, i) => sum + w + (i < tabWidths.length - 1 ? 1 : 0), 0); // +1 for gaps between tabs + + if (totalTabsWidth > maxTabsWidth) { + // Need to show a subset - account for left arrow when not at start + const effectiveMaxWidth = maxTabsWidth - LEFT_ARROW_WIDTH; + + // Start with the selected tab + let windowWidth = tabWidths[safeSelectedIndex] ?? 0; + startIndex = safeSelectedIndex; + endIndex = safeSelectedIndex + 1; + + // Expand window to include more tabs + while (startIndex > 0 || endIndex < tabs.length) { + const canExpandLeft = startIndex > 0; + const canExpandRight = endIndex < tabs.length; + if (canExpandLeft) { + const leftWidth = (tabWidths[startIndex - 1] ?? 0) + 1; // +1 for gap + if (windowWidth + leftWidth <= effectiveMaxWidth) { + startIndex--; + windowWidth += leftWidth; + continue; + } + } + if (canExpandRight) { + const rightWidth = (tabWidths[endIndex] ?? 0) + 1; // +1 for gap + if (windowWidth + rightWidth <= effectiveMaxWidth) { + endIndex++; + windowWidth += rightWidth; + continue; + } + } + break; + } + } + const hiddenLeft = startIndex; + const hiddenRight = tabs.length - endIndex; + const visibleTabs = tabs.slice(startIndex, endIndex); + const visibleIndices = visibleTabs.map((_, i_0) => startIndex + i_0); + return + {resumeLabel} + {hiddenLeft > 0 && + {LEFT_ARROW_PREFIX} + {hiddenLeft} + } + {visibleTabs.map((tab_0, i_1) => { + const actualIndex = visibleIndices[i_1]!; + const isSelected = actualIndex === safeSelectedIndex; + const displayText = tab_0 === ALL_TAB_LABEL ? tab_0 : `#${truncateTag(tab_0, maxSingleTabWidth - TAB_PADDING)}`; + return + {' '} + {displayText}{' '} + ; + })} + {hiddenRight > 0 ? + {RIGHT_HINT_WITH_COUNT_PREFIX} + {hiddenRight} + {RIGHT_HINT_SUFFIX} + : {RIGHT_HINT_NO_COUNT}} + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInN0cmluZ1dpZHRoIiwiQm94IiwiVGV4dCIsInRydW5jYXRlVG9XaWR0aCIsIkFMTF9UQUJfTEFCRUwiLCJUQUJfUEFERElORyIsIkhBU0hfUFJFRklYX0xFTkdUSCIsIkxFRlRfQVJST1dfUFJFRklYIiwiUklHSFRfSElOVF9XSVRIX0NPVU5UX1BSRUZJWCIsIlJJR0hUX0hJTlRfU1VGRklYIiwiUklHSFRfSElOVF9OT19DT1VOVCIsIk1BWF9PVkVSRkxPV19ESUdJVFMiLCJMRUZUX0FSUk9XX1dJRFRIIiwibGVuZ3RoIiwiUklHSFRfSElOVF9XSURUSF9XSVRIX0NPVU5UIiwiUklHSFRfSElOVF9XSURUSF9OT19DT1VOVCIsIlByb3BzIiwidGFicyIsInNlbGVjdGVkSW5kZXgiLCJhdmFpbGFibGVXaWR0aCIsInNob3dBbGxQcm9qZWN0cyIsImdldFRhYldpZHRoIiwidGFiIiwibWF4V2lkdGgiLCJ0YWdXaWR0aCIsImVmZmVjdGl2ZVRhZ1dpZHRoIiwiTWF0aCIsIm1pbiIsIm1heCIsInRydW5jYXRlVGFnIiwidGFnIiwiYXZhaWxhYmxlRm9yVGFnIiwiY2hhckF0IiwiVGFnVGFicyIsIlJlYWN0Tm9kZSIsInJlc3VtZUxhYmVsIiwicmVzdW1lTGFiZWxXaWR0aCIsInJpZ2h0SGludFdpZHRoIiwibWF4VGFic1dpZHRoIiwic2FmZVNlbGVjdGVkSW5kZXgiLCJtYXhTaW5nbGVUYWJXaWR0aCIsImZsb29yIiwidGFiV2lkdGhzIiwibWFwIiwic3RhcnRJbmRleCIsImVuZEluZGV4IiwidG90YWxUYWJzV2lkdGgiLCJyZWR1Y2UiLCJzdW0iLCJ3IiwiaSIsImVmZmVjdGl2ZU1heFdpZHRoIiwid2luZG93V2lkdGgiLCJjYW5FeHBhbmRMZWZ0IiwiY2FuRXhwYW5kUmlnaHQiLCJsZWZ0V2lkdGgiLCJyaWdodFdpZHRoIiwiaGlkZGVuTGVmdCIsImhpZGRlblJpZ2h0IiwidmlzaWJsZVRhYnMiLCJzbGljZSIsInZpc2libGVJbmRpY2VzIiwiXyIsImFjdHVhbEluZGV4IiwiaXNTZWxlY3RlZCIsImRpc3BsYXlUZXh0IiwidW5kZWZpbmVkIl0sInNvdXJjZXMiOlsiVGFnVGFicy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyB0cnVuY2F0ZVRvV2lkdGggfSBmcm9tICcuLi91dGlscy9mb3JtYXQuanMnXG5cbi8vIENvbnN0YW50cyBmb3Igd2lkdGggY2FsY3VsYXRpb25zIC0gZGVyaXZlZCBmcm9tIGFjdHVhbCByZW5kZXJlZCBzdHJpbmdzXG5jb25zdCBBTExfVEFCX0xBQkVMID0gJ0FsbCdcbmNvbnN0IFRBQl9QQURESU5HID0gMiAvLyBTcGFjZSBiZWZvcmUgYW5kIGFmdGVyIHRhYiB0ZXh0OiBcIiB7dGFifSBcIlxuY29uc3QgSEFTSF9QUkVGSVhfTEVOR1RIID0gMSAvLyBcIiNcIiBwcmVmaXggZm9yIG5vbi1BbGwgdGFic1xuY29uc3QgTEVGVF9BUlJPV19QUkVGSVggPSAn4oaQICdcbmNvbnN0IFJJR0hUX0hJTlRfV0lUSF9DT1VOVF9QUkVGSVggPSAn4oaSJ1xuY29uc3QgUklHSFRfSElOVF9TVUZGSVggPSAnICh0YWIgdG8gY3ljbGUpJ1xuY29uc3QgUklHSFRfSElOVF9OT19DT1VOVCA9ICcodGFiIHRvIGN5Y2xlKSdcbmNvbnN0IE1BWF9PVkVSRkxPV19ESUdJVFMgPSAyIC8vIEFzc3VtZSBtYXggOTkgaGlkZGVuIHRhYnMgZm9yIHdpZHRoIGNhbGN1bGF0aW9uXG5cbi8vIENvbXB1dGVkIHdpZHRoc1xuY29uc3QgTEVGVF9BUlJPV19XSURUSCA9IExFRlRfQVJST1dfUFJFRklYLmxlbmd0aCArIE1BWF9PVkVSRkxPV19ESUdJVFMgKyAxIC8vIFwi4oaQIE5OIFwiIHdpdGggZ2FwXG5jb25zdCBSSUdIVF9ISU5UX1dJRFRIX1dJVEhfQ09VTlQgPVxuICBSSUdIVF9ISU5UX1dJVEhfQ09VTlRfUFJFRklYLmxlbmd0aCArXG4gIE1BWF9PVkVSRkxPV19ESUdJVFMgK1xuICBSSUdIVF9ISU5UX1NVRkZJWC5sZW5ndGggLy8gXCLihpJOTiAodGFiIHRvIGN5Y2xlKVwiXG5jb25zdCBSSUdIVF9ISU5UX1dJRFRIX05PX0NPVU5UID0gUklHSFRfSElOVF9OT19DT1VOVC5sZW5ndGhcblxudHlwZSBQcm9wcyA9IHtcbiAgdGFiczogc3RyaW5nW11cbiAgc2VsZWN0ZWRJbmRleDogbnVtYmVyXG4gIGF2YWlsYWJsZVdpZHRoOiBudW1iZXJcbiAgc2hvd0FsbFByb2plY3RzPzogYm9vbGVhblxufVxuXG4vKipcbiAqIENhbGN1bGF0ZSB0aGUgZGlzcGxheSB3aWR0aCBvZiBhIHRhYlxuICovXG5mdW5jdGlvbiBnZXRUYWJXaWR0aCh0YWI6IHN0cmluZywgbWF4V2lkdGg/OiBudW1iZXIpOiBudW1iZXIge1xuICBpZiAodGFiID09PSBBTExfVEFCX0xBQkVMKSB7XG4gICAgcmV0dXJuIEFMTF9UQUJfTEFCRUwubGVuZ3RoICsgVEFCX1BBRERJTkdcbiAgfVxuICAvLyBGb3Igbm9uLUFsbCB0YWJzOiBcIiAje3RhZ30gXCIgYnV0IHRydW5jYXRlIHRhZyBpZiBuZWVkZWRcbiAgY29uc3QgdGFnV2lkdGggPSBzdHJpbmdXaWR0aCh0YWIpXG4gIGNvbnN0IGVmZmVjdGl2ZVRhZ1dpZHRoID0gbWF4V2lkdGhcbiAgICA/IE1hdGgubWluKHRhZ1dpZHRoLCBtYXhXaWR0aCAtIFRBQl9QQURESU5HIC0gSEFTSF9QUkVGSVhfTEVOR1RIKVxuICAgIDogdGFnV2lkdGhcbiAgcmV0dXJuIE1hdGgubWF4KDAsIGVmZmVjdGl2ZVRhZ1dpZHRoKSArIFRBQl9QQURESU5HICsgSEFTSF9QUkVGSVhfTEVOR1RIXG59XG5cbi8qKlxuICogVHJ1bmNhdGUgYSB0YWcgdG8gZml0IHdpdGhpbiBtYXhXaWR0aCwgYWNjb3VudGluZyBmb3IgcGFkZGluZyBhbmQgaGFzaCBwcmVmaXhcbiAqL1xuZnVuY3Rpb24gdHJ1bmNhdGVUYWcodGFnOiBzdHJpbmcsIG1heFdpZHRoOiBudW1iZXIpOiBzdHJpbmcge1xuICAvLyBBdmFpbGFibGUgc3BhY2UgZm9yIHRoZSB0YWcgdGV4dCBpdHNlbGY6IG1heFdpZHRoIC0gXCIgI1wiIC0gXCIgXCJcbiAgY29uc3QgYXZhaWxhYmxlRm9yVGFnID0gbWF4V2lkdGggLSBUQUJfUEFERElORyAtIEhBU0hfUFJFRklYX0xFTkdUSFxuICBpZiAoc3RyaW5nV2lkdGgodGFnKSA8PSBhdmFpbGFibGVGb3JUYWcpIHtcbiAgICByZXR1cm4gdGFnXG4gIH1cbiAgaWYgKGF2YWlsYWJsZUZvclRhZyA8PSAxKSB7XG4gICAgcmV0dXJuIHRhZy5jaGFyQXQoMClcbiAgfVxuICByZXR1cm4gdHJ1bmNhdGVUb1dpZHRoKHRhZywgYXZhaWxhYmxlRm9yVGFnKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gVGFnVGFicyh7XG4gIHRhYnMsXG4gIHNlbGVjdGVkSW5kZXgsXG4gIGF2YWlsYWJsZVdpZHRoLFxuICBzaG93QWxsUHJvamVjdHMgPSBmYWxzZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgcmVzdW1lTGFiZWwgPSBzaG93QWxsUHJvamVjdHMgPyAnUmVzdW1lIChBbGwgUHJvamVjdHMpJyA6ICdSZXN1bWUnXG4gIGNvbnN0IHJlc3VtZUxhYmVsV2lkdGggPSByZXN1bWVMYWJlbC5sZW5ndGggKyAxIC8vICsxIGZvciBnYXBcblxuICAvLyBDYWxjdWxhdGUgaG93IG11Y2ggc3BhY2Ugd2UgaGF2ZSBmb3IgdGFicyAodXNlIHdvcnN0LWNhc2UgaGludCB3aWR0aClcbiAgY29uc3QgcmlnaHRIaW50V2lkdGggPSBNYXRoLm1heChcbiAgICBSSUdIVF9ISU5UX1dJRFRIX1dJVEhfQ09VTlQsXG4gICAgUklHSFRfSElOVF9XSURUSF9OT19DT1VOVCxcbiAgKVxuICBjb25zdCBtYXhUYWJzV2lkdGggPSBhdmFpbGFibGVXaWR0aCAtIHJlc3VtZUxhYmVsV2lkdGggLSByaWdodEhpbnRXaWR0aCAtIDIgLy8gMiBmb3IgZ2Fwc1xuXG4gIC8vIENsYW1wIHNlbGVjdGVkSW5kZXggdG8gdmFsaWQgcmFuZ2VcbiAgY29uc3Qgc2FmZVNlbGVjdGVkSW5kZXggPSBNYXRoLm1heChcbiAgICAwLFxuICAgIE1hdGgubWluKHNlbGVjdGVkSW5kZXgsIHRhYnMubGVuZ3RoIC0gMSksXG4gIClcblxuICAvLyBDYWxjdWxhdGUgd2lkdGggb2YgZWFjaCB0YWIsIHdpdGggdHJ1bmNhdGlvbiBmb3IgdmVyeSBsb25nIHRhZ3NcbiAgY29uc3QgbWF4U2luZ2xlVGFiV2lkdGggPSBNYXRoLm1heCgyMCwgTWF0aC5mbG9vcihtYXhUYWJzV2lkdGggLyAyKSkgLy8gQXQgbGVhc3Qgc2hvdyBoYWxmIHRoZSBzcGFjZSBmb3Igb25lIHRhYlxuICBjb25zdCB0YWJXaWR0aHMgPSB0YWJzLm1hcCh0YWIgPT4gZ2V0VGFiV2lkdGgodGFiLCBtYXhTaW5nbGVUYWJXaWR0aCkpXG5cbiAgLy8gRmluZCBhIHdpbmRvdyBvZiB0YWJzIHRoYXQgZml0cywgY2VudGVyZWQgYXJvdW5kIHNlbGVjdGVkSW5kZXhcbiAgbGV0IHN0YXJ0SW5kZXggPSAwXG4gIGxldCBlbmRJbmRleCA9IHRhYnMubGVuZ3RoXG5cbiAgLy8gQ2FsY3VsYXRlIHRvdGFsIHdpZHRoIG9mIGFsbCB0YWJzXG4gIGNvbnN0IHRvdGFsVGFic1dpZHRoID0gdGFiV2lkdGhzLnJlZHVjZShcbiAgICAoc3VtLCB3LCBpKSA9PiBzdW0gKyB3ICsgKGkgPCB0YWJXaWR0aHMubGVuZ3RoIC0gMSA/IDEgOiAwKSxcbiAgICAwLFxuICApIC8vICsxIGZvciBnYXBzIGJldHdlZW4gdGFic1xuXG4gIGlmICh0b3RhbFRhYnNXaWR0aCA+IG1heFRhYnNXaWR0aCkge1xuICAgIC8vIE5lZWQgdG8gc2hvdyBhIHN1YnNldCAtIGFjY291bnQgZm9yIGxlZnQgYXJyb3cgd2hlbiBub3QgYXQgc3RhcnRcbiAgICBjb25zdCBlZmZlY3RpdmVNYXhXaWR0aCA9IG1heFRhYnNXaWR0aCAtIExFRlRfQVJST1dfV0lEVEhcblxuICAgIC8vIFN0YXJ0IHdpdGggdGhlIHNlbGVjdGVkIHRhYlxuICAgIGxldCB3aW5kb3dXaWR0aCA9IHRhYldpZHRoc1tzYWZlU2VsZWN0ZWRJbmRleF0gPz8gMFxuICAgIHN0YXJ0SW5kZXggPSBzYWZlU2VsZWN0ZWRJbmRleFxuICAgIGVuZEluZGV4ID0gc2FmZVNlbGVjdGVkSW5kZXggKyAxXG5cbiAgICAvLyBFeHBhbmQgd2luZG93IHRvIGluY2x1ZGUgbW9yZSB0YWJzXG4gICAgd2hpbGUgKHN0YXJ0SW5kZXggPiAwIHx8IGVuZEluZGV4IDwgdGFicy5sZW5ndGgpIHtcbiAgICAgIGNvbnN0IGNhbkV4cGFuZExlZnQgPSBzdGFydEluZGV4ID4gMFxuICAgICAgY29uc3QgY2FuRXhwYW5kUmlnaHQgPSBlbmRJbmRleCA8IHRhYnMubGVuZ3RoXG5cbiAgICAgIGlmIChjYW5FeHBhbmRMZWZ0KSB7XG4gICAgICAgIGNvbnN0IGxlZnRXaWR0aCA9ICh0YWJXaWR0aHNbc3RhcnRJbmRleCAtIDFdID8/IDApICsgMSAvLyArMSBmb3IgZ2FwXG4gICAgICAgIGlmICh3aW5kb3dXaWR0aCArIGxlZnRXaWR0aCA8PSBlZmZlY3RpdmVNYXhXaWR0aCkge1xuICAgICAgICAgIHN0YXJ0SW5kZXgtLVxuICAgICAgICAgIHdpbmRvd1dpZHRoICs9IGxlZnRXaWR0aFxuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGNhbkV4cGFuZFJpZ2h0KSB7XG4gICAgICAgIGNvbnN0IHJpZ2h0V2lkdGggPSAodGFiV2lkdGhzW2VuZEluZGV4XSA/PyAwKSArIDEgLy8gKzEgZm9yIGdhcFxuICAgICAgICBpZiAod2luZG93V2lkdGggKyByaWdodFdpZHRoIDw9IGVmZmVjdGl2ZU1heFdpZHRoKSB7XG4gICAgICAgICAgZW5kSW5kZXgrK1xuICAgICAgICAgIHdpbmRvd1dpZHRoICs9IHJpZ2h0V2lkdGhcbiAgICAgICAgICBjb250aW51ZVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGJyZWFrXG4gICAgfVxuICB9XG5cbiAgY29uc3QgaGlkZGVuTGVmdCA9IHN0YXJ0SW5kZXhcbiAgY29uc3QgaGlkZGVuUmlnaHQgPSB0YWJzLmxlbmd0aCAtIGVuZEluZGV4XG4gIGNvbnN0IHZpc2libGVUYWJzID0gdGFicy5zbGljZShzdGFydEluZGV4LCBlbmRJbmRleClcbiAgY29uc3QgdmlzaWJsZUluZGljZXMgPSB2aXNpYmxlVGFicy5tYXAoKF8sIGkpID0+IHN0YXJ0SW5kZXggKyBpKVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXsxfT5cbiAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPntyZXN1bWVMYWJlbH08L1RleHQ+XG4gICAgICB7aGlkZGVuTGVmdCA+IDAgJiYgKFxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICB7TEVGVF9BUlJPV19QUkVGSVh9XG4gICAgICAgICAge2hpZGRlbkxlZnR9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICl9XG4gICAgICB7dmlzaWJsZVRhYnMubWFwKCh0YWIsIGkpID0+IHtcbiAgICAgICAgY29uc3QgYWN0dWFsSW5kZXggPSB2aXNpYmxlSW5kaWNlc1tpXSFcbiAgICAgICAgY29uc3QgaXNTZWxlY3RlZCA9IGFjdHVhbEluZGV4ID09PSBzYWZlU2VsZWN0ZWRJbmRleFxuICAgICAgICBjb25zdCBkaXNwbGF5VGV4dCA9XG4gICAgICAgICAgdGFiID09PSBBTExfVEFCX0xBQkVMXG4gICAgICAgICAgICA/IHRhYlxuICAgICAgICAgICAgOiBgIyR7dHJ1bmNhdGVUYWcodGFiLCBtYXhTaW5nbGVUYWJXaWR0aCAtIFRBQl9QQURESU5HKX1gXG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgPFRleHRcbiAgICAgICAgICAgIGtleT17dGFifVxuICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtpc1NlbGVjdGVkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgY29sb3I9e2lzU2VsZWN0ZWQgPyAnaW52ZXJzZVRleHQnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgYm9sZD17aXNTZWxlY3RlZH1cbiAgICAgICAgICA+XG4gICAgICAgICAgICB7JyAnfVxuICAgICAgICAgICAge2Rpc3BsYXlUZXh0fXsnICd9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApXG4gICAgICB9KX1cbiAgICAgIHtoaWRkZW5SaWdodCA+IDAgPyAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgIHtSSUdIVF9ISU5UX1dJVEhfQ09VTlRfUFJFRklYfVxuICAgICAgICAgIHtoaWRkZW5SaWdodH1cbiAgICAgICAgICB7UklHSFRfSElOVF9TVUZGSVh9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICkgOiAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPntSSUdIVF9ISU5UX05PX0NPVU5UfTwvVGV4dD5cbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsV0FBVyxRQUFRLHVCQUF1QjtBQUNuRCxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLFNBQVNDLGVBQWUsUUFBUSxvQkFBb0I7O0FBRXBEO0FBQ0EsTUFBTUMsYUFBYSxHQUFHLEtBQUs7QUFDM0IsTUFBTUMsV0FBVyxHQUFHLENBQUMsRUFBQztBQUN0QixNQUFNQyxrQkFBa0IsR0FBRyxDQUFDLEVBQUM7QUFDN0IsTUFBTUMsaUJBQWlCLEdBQUcsSUFBSTtBQUM5QixNQUFNQyw0QkFBNEIsR0FBRyxHQUFHO0FBQ3hDLE1BQU1DLGlCQUFpQixHQUFHLGlCQUFpQjtBQUMzQyxNQUFNQyxtQkFBbUIsR0FBRyxnQkFBZ0I7QUFDNUMsTUFBTUMsbUJB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 \ No newline at end of file diff --git a/components/TaskListV2.tsx b/components/TaskListV2.tsx new file mode 100644 index 0000000..addc083 --- /dev/null +++ b/components/TaskListV2.tsx @@ -0,0 +1,378 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import * as React from 'react'; +import { useTerminalSize } from '../hooks/useTerminalSize.js'; +import { stringWidth } from '../ink/stringWidth.js'; +import { Box, Text } from '../ink.js'; +import { useAppState } from '../state/AppState.js'; +import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js'; +import { AGENT_COLOR_TO_THEME_COLOR, type AgentColorName } from '../tools/AgentTool/agentColorManager.js'; +import { isAgentSwarmsEnabled } from '../utils/agentSwarmsEnabled.js'; +import { count } from '../utils/array.js'; +import { summarizeRecentActivities } from '../utils/collapseReadSearch.js'; +import { truncateToWidth } from '../utils/format.js'; +import { isTodoV2Enabled, type Task } from '../utils/tasks.js'; +import type { Theme } from '../utils/theme.js'; +import ThemedText from './design-system/ThemedText.js'; +type Props = { + tasks: Task[]; + isStandalone?: boolean; +}; +const RECENT_COMPLETED_TTL_MS = 30_000; +function byIdAsc(a: Task, b: Task): number { + const aNum = parseInt(a.id, 10); + const bNum = parseInt(b.id, 10); + if (!isNaN(aNum) && !isNaN(bNum)) { + return aNum - bNum; + } + return a.id.localeCompare(b.id); +} +export function TaskListV2({ + tasks, + isStandalone = false +}: Props): React.ReactNode { + const teamContext = useAppState(s => s.teamContext); + const appStateTasks = useAppState(s_0 => s_0.tasks); + const [, forceUpdate] = React.useState(0); + const { + rows, + columns + } = useTerminalSize(); + + // Track when each task was last observed transitioning to completed + const completionTimestampsRef = React.useRef(new Map()); + const previousCompletedIdsRef = React.useRef | null>(null); + if (previousCompletedIdsRef.current === null) { + previousCompletedIdsRef.current = new Set(tasks.filter(t => t.status === 'completed').map(t_0 => t_0.id)); + } + const maxDisplay = rows <= 10 ? 0 : Math.min(10, Math.max(3, rows - 14)); + + // Update completion timestamps: reset when a task transitions to completed + const currentCompletedIds = new Set(tasks.filter(t_1 => t_1.status === 'completed').map(t_2 => t_2.id)); + const now = Date.now(); + for (const id of currentCompletedIds) { + if (!previousCompletedIdsRef.current.has(id)) { + completionTimestampsRef.current.set(id, now); + } + } + for (const id_0 of completionTimestampsRef.current.keys()) { + if (!currentCompletedIds.has(id_0)) { + completionTimestampsRef.current.delete(id_0); + } + } + previousCompletedIdsRef.current = currentCompletedIds; + + // Schedule re-render when the next recent completion expires. + // Depend on `tasks` so the timer is only reset when the task list changes, + // not on every render (which was causing unnecessary work). + React.useEffect(() => { + if (completionTimestampsRef.current.size === 0) { + return; + } + const currentNow = Date.now(); + let earliestExpiry = Infinity; + for (const ts of completionTimestampsRef.current.values()) { + const expiry = ts + RECENT_COMPLETED_TTL_MS; + if (expiry > currentNow && expiry < earliestExpiry) { + earliestExpiry = expiry; + } + } + if (earliestExpiry === Infinity) { + return; + } + const timer = setTimeout(forceUpdate_0 => forceUpdate_0((n: number) => n + 1), earliestExpiry - currentNow, forceUpdate); + return () => clearTimeout(timer); + }, [tasks]); + if (!isTodoV2Enabled()) { + return null; + } + if (tasks.length === 0) { + return null; + } + + // Build a map of teammate name -> theme color + const teammateColors: Record = {}; + if (isAgentSwarmsEnabled() && teamContext?.teammates) { + for (const teammate of Object.values(teamContext.teammates)) { + if (teammate.color) { + const themeColor = AGENT_COLOR_TO_THEME_COLOR[teammate.color as AgentColorName]; + if (themeColor) { + teammateColors[teammate.name] = themeColor; + } + } + } + } + + // Build a map of teammate name -> current activity description + // Map both agentName ("researcher") and agentId ("researcher@team") so + // task owners match regardless of which format the model used. + // Rolls up consecutive search/read tool uses into a compact summary. + // Also track which teammates are still running (not shut down). + const teammateActivity: Record = {}; + const activeTeammates = new Set(); + if (isAgentSwarmsEnabled()) { + for (const bgTask of Object.values(appStateTasks)) { + if (isInProcessTeammateTask(bgTask) && bgTask.status === 'running') { + activeTeammates.add(bgTask.identity.agentName); + activeTeammates.add(bgTask.identity.agentId); + const activities = bgTask.progress?.recentActivities; + const desc = (activities && summarizeRecentActivities(activities)) ?? bgTask.progress?.lastActivity?.activityDescription; + if (desc) { + teammateActivity[bgTask.identity.agentName] = desc; + teammateActivity[bgTask.identity.agentId] = desc; + } + } + } + } + + // Get task counts for display + const completedCount = count(tasks, t_3 => t_3.status === 'completed'); + const pendingCount = count(tasks, t_4 => t_4.status === 'pending'); + const inProgressCount = tasks.length - completedCount - pendingCount; + // Unresolved tasks (open or in_progress) block dependent tasks + const unresolvedTaskIds = new Set(tasks.filter(t_5 => t_5.status !== 'completed').map(t_6 => t_6.id)); + + // Check if we need to truncate + const needsTruncation = tasks.length > maxDisplay; + let visibleTasks: Task[]; + let hiddenTasks: Task[]; + if (needsTruncation) { + // Prioritize: recently completed (within 30s), in-progress, pending, older completed + const recentCompleted: Task[] = []; + const olderCompleted: Task[] = []; + for (const task of tasks.filter(t_7 => t_7.status === 'completed')) { + const ts_0 = completionTimestampsRef.current.get(task.id); + if (ts_0 && now - ts_0 < RECENT_COMPLETED_TTL_MS) { + recentCompleted.push(task); + } else { + olderCompleted.push(task); + } + } + recentCompleted.sort(byIdAsc); + olderCompleted.sort(byIdAsc); + const inProgress = tasks.filter(t_8 => t_8.status === 'in_progress').sort(byIdAsc); + const pending = tasks.filter(t_9 => t_9.status === 'pending').sort((a, b) => { + const aBlocked = a.blockedBy.some(id_1 => unresolvedTaskIds.has(id_1)); + const bBlocked = b.blockedBy.some(id_2 => unresolvedTaskIds.has(id_2)); + if (aBlocked !== bBlocked) { + return aBlocked ? 1 : -1; + } + return byIdAsc(a, b); + }); + const prioritized = [...recentCompleted, ...inProgress, ...pending, ...olderCompleted]; + visibleTasks = prioritized.slice(0, maxDisplay); + hiddenTasks = prioritized.slice(maxDisplay); + } else { + // No truncation needed — sort by ID for stable ordering + visibleTasks = [...tasks].sort(byIdAsc); + hiddenTasks = []; + } + let hiddenSummary = ''; + if (hiddenTasks.length > 0) { + const parts: string[] = []; + const hiddenPending = count(hiddenTasks, t_10 => t_10.status === 'pending'); + const hiddenInProgress = count(hiddenTasks, t_11 => t_11.status === 'in_progress'); + const hiddenCompleted = count(hiddenTasks, t_12 => t_12.status === 'completed'); + if (hiddenInProgress > 0) { + parts.push(`${hiddenInProgress} in progress`); + } + if (hiddenPending > 0) { + parts.push(`${hiddenPending} pending`); + } + if (hiddenCompleted > 0) { + parts.push(`${hiddenCompleted} completed`); + } + hiddenSummary = ` … +${parts.join(', ')}`; + } + const content = <> + {visibleTasks.map(task_0 => unresolvedTaskIds.has(id_3))} activity={task_0.owner ? teammateActivity[task_0.owner] : undefined} ownerActive={task_0.owner ? activeTeammates.has(task_0.owner) : false} columns={columns} />)} + {maxDisplay > 0 && hiddenSummary && {hiddenSummary}} + ; + if (isStandalone) { + return + + + {tasks.length} + {' tasks ('} + {completedCount} + {' done, '} + {inProgressCount > 0 && <> + {inProgressCount} + {' in progress, '} + } + {pendingCount} + {' open)'} + + + {content} + ; + } + return {content}; +} +type TaskItemProps = { + task: Task; + ownerColor?: keyof Theme; + openBlockers: string[]; + activity?: string; + ownerActive: boolean; + columns: number; +}; +function getTaskIcon(status: Task['status']): { + icon: string; + color: keyof Theme | undefined; +} { + switch (status) { + case 'completed': + return { + icon: figures.tick, + color: 'success' + }; + case 'in_progress': + return { + icon: figures.squareSmallFilled, + color: 'claude' + }; + case 'pending': + return { + icon: figures.squareSmall, + color: undefined + }; + } +} +function TaskItem(t0) { + const $ = _c(37); + const { + task, + ownerColor, + openBlockers, + activity, + ownerActive, + columns + } = t0; + const isCompleted = task.status === "completed"; + const isInProgress = task.status === "in_progress"; + const isBlocked = openBlockers.length > 0; + let t1; + if ($[0] !== task.status) { + t1 = getTaskIcon(task.status); + $[0] = task.status; + $[1] = t1; + } else { + t1 = $[1]; + } + const { + icon, + color + } = t1; + const showActivity = isInProgress && !isBlocked && activity; + const showOwner = columns >= 60 && task.owner && ownerActive; + let t2; + if ($[2] !== showOwner || $[3] !== task.owner) { + t2 = showOwner ? stringWidth(` (@${task.owner})`) : 0; + $[2] = showOwner; + $[3] = task.owner; + $[4] = t2; + } else { + t2 = $[4]; + } + const ownerWidth = t2; + const maxSubjectWidth = Math.max(15, columns - 15 - ownerWidth); + let t3; + if ($[5] !== maxSubjectWidth || $[6] !== task.subject) { + t3 = truncateToWidth(task.subject, maxSubjectWidth); + $[5] = maxSubjectWidth; + $[6] = task.subject; + $[7] = t3; + } else { + t3 = $[7]; + } + const displaySubject = t3; + const maxActivityWidth = Math.max(15, columns - 15); + let t4; + if ($[8] !== activity || $[9] !== maxActivityWidth) { + t4 = activity ? truncateToWidth(activity, maxActivityWidth) : undefined; + $[8] = activity; + $[9] = maxActivityWidth; + $[10] = t4; + } else { + t4 = $[10]; + } + const displayActivity = t4; + let t5; + if ($[11] !== color || $[12] !== icon) { + t5 = {icon} ; + $[11] = color; + $[12] = icon; + $[13] = t5; + } else { + t5 = $[13]; + } + const t6 = isCompleted || isBlocked; + let t7; + if ($[14] !== displaySubject || $[15] !== isCompleted || $[16] !== isInProgress || $[17] !== t6) { + t7 = {displaySubject}; + $[14] = displaySubject; + $[15] = isCompleted; + $[16] = isInProgress; + $[17] = t6; + $[18] = t7; + } else { + t7 = $[18]; + } + let t8; + if ($[19] !== ownerColor || $[20] !== showOwner || $[21] !== task.owner) { + t8 = showOwner && {" ("}{ownerColor ? @{task.owner} : `@${task.owner}`}{")"}; + $[19] = ownerColor; + $[20] = showOwner; + $[21] = task.owner; + $[22] = t8; + } else { + t8 = $[22]; + } + let t9; + if ($[23] !== isBlocked || $[24] !== openBlockers) { + t9 = isBlocked && {" "}{figures.pointerSmall} blocked by{" "}{[...openBlockers].sort(_temp).map(_temp2).join(", ")}; + $[23] = isBlocked; + $[24] = openBlockers; + $[25] = t9; + } else { + t9 = $[25]; + } + let t10; + if ($[26] !== t5 || $[27] !== t7 || $[28] !== t8 || $[29] !== t9) { + t10 = {t5}{t7}{t8}{t9}; + $[26] = t5; + $[27] = t7; + $[28] = t8; + $[29] = t9; + $[30] = t10; + } else { + t10 = $[30]; + } + let t11; + if ($[31] !== displayActivity || $[32] !== showActivity) { + t11 = showActivity && displayActivity && {" "}{displayActivity}{figures.ellipsis}; + $[31] = displayActivity; + $[32] = showActivity; + $[33] = t11; + } else { + t11 = $[33]; + } + let t12; + if ($[34] !== t10 || $[35] !== t11) { + t12 = {t10}{t11}; + $[34] = t10; + $[35] = t11; + $[36] = t12; + } else { + t12 = $[36]; + } + return t12; +} +function _temp2(id) { + return `#${id}`; +} +function _temp(a, b) { + return parseInt(a, 10) - parseInt(b, 10); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VUZXJtaW5hbFNpemUiLCJzdHJpbmdXaWR0aCIsIkJveCIsIlRleHQiLCJ1c2VBcHBTdGF0ZSIsImlzSW5Qcm9jZXNzVGVhbW1hdGVUYXNrIiwiQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1IiLCJBZ2VudENvbG9yTmFtZSIsImlzQWdlbnRTd2FybXNFbmFibGVkIiwiY291bnQiLCJzdW1tYXJpemVSZWNlbnRBY3Rpdml0aWVzIiwidHJ1bmNhdGVUb1dpZHRoIiwiaXNUb2RvVjJFbmFibGVkIiwiVGFzayIsIlRoZW1lIiwiVGhlbWVkVGV4dCIsIlByb3BzIiwidGFza3MiLCJpc1N0YW5kYWxvbmUiLCJSRUNFTlRfQ09NUExFVEVEX1RUTF9NUyIsImJ5SWRBc2MiLCJhIiwiYiIsImFOdW0iLCJwYXJzZUludCIsImlkIiwiYk51bSIsImlzTmFOIiwibG9jYWxlQ29tcGFyZSIsIlRhc2tMaXN0VjIiLCJSZWFjdE5vZGUiLCJ0ZWFtQ29udGV4dCIsInMiLCJhcHBTdGF0ZVRhc2tzIiwiZm9yY2VVcGRhdGUiLCJ1c2VTdGF0ZSIsInJvd3MiLCJjb2x1bW5zIiwiY29tcGxldGlvblRpbWVzdGFtcHNSZWYiLCJ1c2VSZWYiLCJNYXAiLCJwcmV2aW91c0NvbXBsZXRlZElkc1JlZiIsIlNldCIsImN1cnJlbnQiLCJmaWx0ZXIiLCJ0Iiwic3RhdHVzIiwibWFwIiwibWF4RGlzcGxheSIsIk1hdGgiLCJtaW4iLCJtYXgiLCJjdXJyZW50Q29tcGxldGVkSWRzIiwibm93IiwiRGF0ZSIsImhhcyIsInNldCIsImtleXMiLCJkZWxldGUiLCJ1c2VFZmZlY3QiLCJzaXplIiwiY3VycmVudE5vdyIsImVhcmxpZXN0RXhwaXJ5IiwiSW5maW5pdHkiLCJ0cyIsInZhbHVlcyIsImV4cGlyeSIsInRpbWVyIiwic2V0VGltZW91dCIsIm4iLCJjbGVhclRpbWVvdXQiLCJsZW5ndGgiLCJ0ZWFtbWF0ZUNvbG9ycyIsIlJlY29yZCIsInRlYW1tYXRlcyIsInRlYW1tYXRlIiwiT2JqZWN0IiwiY29sb3IiLCJ0aGVtZUNvbG9yIiwibmFtZSIsInRlYW1tYXRlQWN0aXZpdHkiLCJhY3RpdmVUZWFtbWF0ZXMiLCJiZ1Rhc2siLCJhZGQiLCJpZGVudGl0eSIsImFnZW50TmFtZSIsImFnZW50SWQiLCJhY3Rpdml0aWVzIiwicHJvZ3Jlc3MiLCJyZWNlbnRBY3Rpdml0aWVzIiwiZGVzYyIsImxhc3RBY3Rpdml0eSIsImFjdGl2aXR5RGVzY3JpcHRpb24iLCJjb21wbGV0ZWRDb3VudCIsInBlbmRpbmdDb3VudCIsImluUHJvZ3Jlc3NDb3VudCIsInVucmVzb2x2ZWRUYXNrSWRzIiwibmVlZHNUcnVuY2F0aW9uIiwidmlzaWJsZVRhc2tzIiwiaGlkZGVuVGFza3MiLCJyZWNlbnRDb21wbGV0ZWQiLCJvbGRlckNvbXBsZXRlZCIsInRhc2siLCJnZXQiLCJwdXNoIiwic29ydCIsImluUHJvZ3Jlc3MiLCJwZW5kaW5nIiwiYUJsb2NrZWQiLCJibG9ja2VkQnkiLCJzb21lIiwiYkJsb2NrZWQiLCJwcmlvcml0aXplZCIsInNsaWNlIiwiaGlkZGVuU3VtbWFyeSIsInBhcnRzIiwiaGlkZGVuUGVuZGluZyIsImhpZGRlbkluUHJvZ3Jlc3MiLCJoaWRkZW5Db21wbGV0ZWQiLCJqb2luIiwiY29udGVudCIsIm93bmVyIiwidW5kZWZpbmVkIiwiVGFza0l0ZW1Qcm9wcyIsIm93bmVyQ29sb3IiLCJvcGVuQmxvY2tlcnMiLCJhY3Rpdml0eSIsIm93bmVyQWN0aXZlIiwiZ2V0VGFza0ljb24iLCJpY29uIiwidGljayIsInNxdWFyZVNtYWxsRmlsbGVkIiwic3F1YXJlU21hbGwiLCJUYXNrSXRlbSIsInQwIiwiJCIsIl9jIiwiaXNDb21wbGV0ZWQiLCJpc0luUHJvZ3Jlc3MiLCJpc0Jsb2NrZWQiLCJ0MSIsInNob3dBY3Rpdml0eSIsInNob3dPd25lciIsInQyIiwib3duZXJXaWR0aCIsIm1heFN1YmplY3RXaWR0aCIsInQzIiwic3ViamVjdCIsImRpc3BsYXlTdWJqZWN0IiwibWF4QWN0aXZpdHlXaWR0aCIsInQ0IiwiZGlzcGxheUFjdGl2aXR5IiwidDUiLCJ0NiIsInQ3IiwidDgiLCJ0OSIsInBvaW50ZXJTbWFsbCIsIl90ZW1wIiwiX3RlbXAyIiwidDEwIiwidDExIiwiZWxsaXBzaXMiLCJ0MTIiXSwic291cmNlcyI6WyJUYXNrTGlzdFYyLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyBzdHJpbmdXaWR0aCB9IGZyb20gJy4uL2luay9zdHJpbmdXaWR0aC5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlIH0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgeyBpc0luUHJvY2Vzc1RlYW1tYXRlVGFzayB9IGZyb20gJy4uL3Rhc2tzL0luUHJvY2Vzc1RlYW1tYXRlVGFzay90eXBlcy5qcydcbmltcG9ydCB7XG4gIEFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SLFxuICB0eXBlIEFnZW50Q29sb3JOYW1lLFxufSBmcm9tICcuLi90b29scy9BZ2VudFRvb2wvYWdlbnRDb2xvck1hbmFnZXIuanMnXG5pbXBvcnQgeyBpc0FnZW50U3dhcm1zRW5hYmxlZCB9IGZyb20gJy4uL3V0aWxzL2FnZW50U3dhcm1zRW5hYmxlZC5qcydcbmltcG9ydCB7IGNvdW50IH0gZnJvbSAnLi4vdXRpbHMvYXJyYXkuanMnXG5pbXBvcnQgeyBzdW1tYXJpemVSZWNlbnRBY3Rpdml0aWVzIH0gZnJvbSAnLi4vdXRpbHMvY29sbGFwc2VSZWFkU2VhcmNoLmpzJ1xuaW1wb3J0IHsgdHJ1bmNhdGVUb1dpZHRoIH0gZnJvbSAnLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgaXNUb2RvVjJFbmFibGVkLCB0eXBlIFRhc2sgfSBmcm9tICcuLi91dGlscy90YXNrcy5qcydcbmltcG9ydCB0eXBlIHsgVGhlbWUgfSBmcm9tICcuLi91dGlscy90aGVtZS5qcydcbmltcG9ydCBUaGVtZWRUZXh0IGZyb20gJy4vZGVzaWduLXN5c3RlbS9UaGVtZWRUZXh0LmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0YXNrczogVGFza1tdXG4gIGlzU3RhbmRhbG9uZT86IGJvb2xlYW5cbn1cblxuY29uc3QgUkVDRU5UX0NPTVBMRVRFRF9UVExfTVMgPSAzMF8wMDBcblxuZnVuY3Rpb24gYnlJZEFzYyhhOiBUYXNrLCBiOiBUYXNrKTogbnVtYmVyIHtcbiAgY29uc3QgYU51bSA9IHBhcnNlSW50KGEuaWQsIDEwKVxuICBjb25zdCBiTnVtID0gcGFyc2VJbnQoYi5pZCwgMTApXG4gIGlmICghaXNOYU4oYU51bSkgJiYgIWlzTmFOKGJOdW0pKSB7XG4gICAgcmV0dXJuIGFOdW0gLSBiTnVtXG4gIH1cbiAgcmV0dXJuIGEuaWQubG9jYWxlQ29tcGFyZShiLmlkKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gVGFza0xpc3RWMih7XG4gIHRhc2tzLFxuICBpc1N0YW5kYWxvbmUgPSBmYWxzZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgdGVhbUNvbnRleHQgPSB1c2VBcHBTdGF0ZShzID0+IHMudGVhbUNvbnRleHQpXG4gIGNvbnN0IGFwcFN0YXRlVGFza3MgPSB1c2VBcHBTdGF0ZShzID0+IHMudGFza3MpXG4gIGNvbnN0IFssIGZvcmNlVXBkYXRlXSA9IFJlYWN0LnVzZVN0YXRlKDApXG4gIGNvbnN0IHsgcm93cywgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcblxuICAvLyBUcmFjayB3aGVuIGVhY2ggdGFzayB3YXMgbGFzdCBvYnNlcnZlZCB0cmFuc2l0aW9uaW5nIHRvIGNvbXBsZXRlZFxuICBjb25zdCBjb21wbGV0aW9uVGltZXN0YW1wc1JlZiA9IFJlYWN0LnVzZVJlZihuZXcgTWFwPHN0cmluZywgbnVtYmVyPigpKVxuICBjb25zdCBwcmV2aW91c0NvbXBsZXRlZElkc1JlZiA9IFJlYWN0LnVzZVJlZjxTZXQ8c3RyaW5nPiB8IG51bGw+KG51bGwpXG4gIGlmIChwcmV2aW91c0NvbXBsZXRlZElkc1JlZi5jdXJyZW50ID09PSBudWxsKSB7XG4gICAgcHJldmlvdXNDb21wbGV0ZWRJZHNSZWYuY3VycmVudCA9IG5ldyBTZXQoXG4gICAgICB0YXNrcy5maWx0ZXIodCA9PiB0LnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcpLm1hcCh0ID0+IHQuaWQpLFxuICAgIClcbiAgfVxuICBjb25zdCBtYXhEaXNwbGF5ID0gcm93cyA8PSAxMCA/IDAgOiBNYXRoLm1pbigxMCwgTWF0aC5tYXgoMywgcm93cyAtIDE0KSlcblxuICAvLyBVcGRhdGUgY29tcGxldGlvbiB0aW1lc3RhbXBzOiByZXNldCB3aGVuIGEgdGFzayB0cmFuc2l0aW9ucyB0byBjb21wbGV0ZWRcbiAgY29uc3QgY3VycmVudENvbXBsZXRlZElkcyA9IG5ldyBTZXQoXG4gICAgdGFza3MuZmlsdGVyKHQgPT4gdC5zdGF0dXMgPT09ICdjb21wbGV0ZWQnKS5tYXAodCA9PiB0LmlkKSxcbiAgKVxuICBjb25zdCBub3cgPSBEYXRlLm5vdygpXG4gIGZvciAoY29uc3QgaWQgb2YgY3VycmVudENvbXBsZXRlZElkcykge1xuICAgIGlmICghcHJldmlvdXNDb21wbGV0ZWRJZHNSZWYuY3VycmVudC5oYXMoaWQpKSB7XG4gICAgICBjb21wbGV0aW9uVGltZXN0YW1wc1JlZi5jdXJyZW50LnNldChpZCwgbm93KVxuICAgIH1cbiAgfVxuICBmb3IgKGNvbnN0IGlkIG9mIGNvbXBsZXRpb25UaW1lc3RhbXBzUmVmLmN1cnJlbnQua2V5cygpKSB7XG4gICAgaWYgKCFjdXJyZW50Q29tcGxldGVkSWRzLmhhcyhpZCkpIHtcbiAgICAgIGNvbXBsZXRpb25UaW1lc3RhbXBzUmVmLmN1cnJlbnQuZGVsZXRlKGlkKVxuICAgIH1cbiAgfVxuICBwcmV2aW91c0NvbXBsZXRlZElkc1JlZi5jdXJyZW50ID0gY3VycmVudENvbXBsZXRlZElkc1xuXG4gIC8vIFNjaGVkdWxlIHJlLXJlbmRlciB3aGVuIHRoZSBuZXh0IHJlY2VudCBjb21wbGV0aW9uIGV4cGlyZXMuXG4gIC8vIERlcGVuZCBvbiBgdGFza3NgIHNvIHRoZSB0aW1lciBpcyBvbmx5IHJlc2V0IHdoZW4gdGhlIHRhc2sgbGlzdCBjaGFuZ2VzLFxuICAvLyBub3Qgb24gZXZlcnkgcmVuZGVyICh3aGljaCB3YXMgY2F1c2luZyB1bm5lY2Vzc2FyeSB3b3JrKS5cbiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoY29tcGxldGlvblRpbWVzdGFtcHNSZWYuY3VycmVudC5zaXplID09PSAwKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgY29uc3QgY3VycmVudE5vdyA9IERhdGUubm93KClcbiAgICBsZXQgZWFybGllc3RFeHBpcnkgPSBJbmZpbml0eVxuICAgIGZvciAoY29uc3QgdHMgb2YgY29tcGxldGlvblRpbWVzdGFtcHNSZWYuY3VycmVudC52YWx1ZXMoKSkge1xuICAgICAgY29uc3QgZXhwaXJ5ID0gdHMgKyBSRUNFTlRfQ09NUExFVEVEX1RUTF9NU1xuICAgICAgaWYgKGV4cGlyeSA+IGN1cnJlbnROb3cgJiYgZXhwaXJ5IDwgZWFybGllc3RFeHBpcnkpIHtcbiAgICAgICAgZWFybGllc3RFeHBpcnkgPSBleHBpcnlcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGVhcmxpZXN0RXhwaXJ5ID09PSBJbmZpbml0eSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGNvbnN0IHRpbWVyID0gc2V0VGltZW91dChcbiAgICAgIGZvcmNlVXBkYXRlID0+IGZvcmNlVXBkYXRlKChuOiBudW1iZXIpID0+IG4gKyAxKSxcbiAgICAgIGVhcmxpZXN0RXhwaXJ5IC0gY3VycmVudE5vdyxcbiAgICAgIGZvcmNlVXBkYXRlLFxuICAgIClcbiAgICByZXR1cm4gKCkgPT4gY2xlYXJUaW1lb3V0KHRpbWVyKVxuICB9LCBbdGFza3NdKVxuXG4gIGlmICghaXNUb2RvVjJFbmFibGVkKCkpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKHRhc2tzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBCdWlsZCBhIG1hcCBvZiB0ZWFtbWF0ZSBuYW1lIC0+IHRoZW1lIGNvbG9yXG4gIGNvbnN0IHRlYW1tYXRlQ29sb3JzOiBSZWNvcmQ8c3RyaW5nLCBrZXlvZiBUaGVtZT4gPSB7fVxuICBpZiAoaXNBZ2VudFN3YXJtc0VuYWJsZWQoKSAmJiB0ZWFtQ29udGV4dD8udGVhbW1hdGVzKSB7XG4gICAgZm9yIChjb25zdCB0ZWFtbWF0ZSBvZiBPYmplY3QudmFsdWVzKHRlYW1Db250ZXh0LnRlYW1tYXRlcykpIHtcbiAgICAgIGlmICh0ZWFtbWF0ZS5jb2xvcikge1xuICAgICAgICBjb25zdCB0aGVtZUNvbG9yID1cbiAgICAgICAgICBBR0VOVF9DT0xPUl9UT19USEVNRV9DT0xPUlt0ZWFtbWF0ZS5jb2xvciBhcyBBZ2VudENvbG9yTmFtZV1cbiAgICAgICAgaWYgKHRoZW1lQ29sb3IpIHtcbiAgICAgICAgICB0ZWFtbWF0ZUNvbG9yc1t0ZWFtbWF0ZS5uYW1lXSA9IHRoZW1lQ29sb3JcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIEJ1aWxkIGEgbWFwIG9mIHRlYW1tYXRlIG5hbWUgLT4gY3VycmVudCBhY3Rpdml0eSBkZXNjcmlwdGlvblxuICAvLyBNYXAgYm90aCBhZ2VudE5hbWUgKFwicmVzZWFyY2hlclwiKSBhbmQgYWdlbnRJZCAoXCJyZXNlYXJjaGVyQHRlYW1cIikgc29cbiAgLy8gdGFzayBvd25lcnMgbWF0Y2ggcmVnYXJkbGVzcyBvZiB3aGljaCBmb3JtYXQgdGhlIG1vZGVsIHVzZWQuXG4gIC8vIFJvbGxzIHVwIGNvbnNlY3V0aXZlIHNlYXJjaC9yZWFkIHRvb2wgdXNlcyBpbnRvIGEgY29tcGFjdCBzdW1tYXJ5LlxuICAvLyBBbHNvIHRyYWNrIHdoaWNoIHRlYW1tYXRlcyBhcmUgc3RpbGwgcnVubmluZyAobm90IHNodXQgZG93bikuXG4gIGNvbnN0IHRlYW1tYXRlQWN0aXZpdHk6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7fVxuICBjb25zdCBhY3RpdmVUZWFtbWF0ZXMgPSBuZXcgU2V0PHN0cmluZz4oKVxuICBpZiAoaXNBZ2VudFN3YXJtc0VuYWJsZWQoKSkge1xuICAgIGZvciAoY29uc3QgYmdUYXNrIG9mIE9iamVjdC52YWx1ZXMoYXBwU3RhdGVUYXNrcykpIHtcbiAgICAgIGlmIChpc0luUHJvY2Vzc1RlYW1tYXRlVGFzayhiZ1Rhc2spICYmIGJnVGFzay5zdGF0dXMgPT09ICdydW5uaW5nJykge1xuICAgICAgICBhY3RpdmVUZWFtbWF0ZXMuYWRkKGJnVGFzay5pZGVudGl0eS5hZ2VudE5hbWUpXG4gICAgICAgIGFjdGl2ZVRlYW1tYXRlcy5hZGQoYmdUYXNrLmlkZW50aXR5LmFnZW50SWQpXG4gICAgICAgIGNvbnN0IGFjdGl2aXRpZXMgPSBiZ1Rhc2sucHJvZ3Jlc3M/LnJlY2VudEFjdGl2aXRpZXNcbiAgICAgICAgY29uc3QgZGVzYyA9XG4gICAgICAgICAgKGFjdGl2aXRpZXMgJiYgc3VtbWFyaXplUmVjZW50QWN0aXZpdGllcyhhY3Rpdml0aWVzKSkgPz9cbiAgICAgICAgICBiZ1Rhc2sucHJvZ3Jlc3M/Lmxhc3RBY3Rpdml0eT8uYWN0aXZpdHlEZXNjcmlwdGlvblxuICAgICAgICBpZiAoZGVzYykge1xuICAgICAgICAgIHRlYW1tYXRlQWN0aXZpdHlbYmdUYXNrLmlkZW50aXR5LmFnZW50TmFtZV0gPSBkZXNjXG4gICAgICAgICAgdGVhbW1hdGVBY3Rpdml0eVtiZ1Rhc2suaWRlbnRpdHkuYWdlbnRJZF0gPSBkZXNjXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBHZXQgdGFzayBjb3VudHMgZm9yIGRpc3BsYXlcbiAgY29uc3QgY29tcGxldGVkQ291bnQgPSBjb3VudCh0YXNrcywgdCA9PiB0LnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcpXG4gIGNvbnN0IHBlbmRpbmdDb3VudCA9IGNvdW50KHRhc2tzLCB0ID0+IHQuc3RhdHVzID09PSAncGVuZGluZycpXG4gIGNvbnN0IGluUHJvZ3Jlc3NDb3VudCA9IHRhc2tzLmxlbmd0aCAtIGNvbXBsZXRlZENvdW50IC0gcGVuZGluZ0NvdW50XG4gIC8vIFVucmVzb2x2ZWQgdGFza3MgKG9wZW4gb3IgaW5fcHJvZ3Jlc3MpIGJsb2NrIGRlcGVuZGVudCB0YXNrc1xuICBjb25zdCB1bnJlc29sdmVkVGFza0lkcyA9IG5ldyBTZXQoXG4gICAgdGFza3MuZmlsdGVyKHQgPT4gdC5zdGF0dXMgIT09ICdjb21wbGV0ZWQnKS5tYXAodCA9PiB0LmlkKSxcbiAgKVxuXG4gIC8vIENoZWNrIGlmIHdlIG5lZWQgdG8gdHJ1bmNhdGVcbiAgY29uc3QgbmVlZHNUcnVuY2F0aW9uID0gdGFza3MubGVuZ3RoID4gbWF4RGlzcGxheVxuXG4gIGxldCB2aXNpYmxlVGFza3M6IFRhc2tbXVxuICBsZXQgaGlkZGVuVGFza3M6IFRhc2tbXVxuXG4gIGlmIChuZWVkc1RydW5jYXRpb24pIHtcbiAgICAvLyBQcmlvcml0aXplOiByZWNlbnRseSBjb21wbGV0ZWQgKHdpdGhpbiAzMHMpLCBpbi1wcm9ncmVzcywgcGVuZGluZywgb2xkZXIgY29tcGxldGVkXG4gICAgY29uc3QgcmVjZW50Q29tcGxldGVkOiBUYXNrW10gPSBbXVxuICAgIGNvbnN0IG9sZGVyQ29tcGxldGVkOiBUYXNrW10gPSBbXVxuICAgIGZvciAoY29uc3QgdGFzayBvZiB0YXNrcy5maWx0ZXIodCA9PiB0LnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcpKSB7XG4gICAgICBjb25zdCB0cyA9IGNvbXBsZXRpb25UaW1lc3RhbXBzUmVmLmN1cnJlbnQuZ2V0KHRhc2suaWQpXG4gICAgICBpZiAodHMgJiYgbm93IC0gdHMgPCBSRUNFTlRfQ09NUExFVEVEX1RUTF9NUykge1xuICAgICAgICByZWNlbnRDb21wbGV0ZWQucHVzaCh0YXNrKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb2xkZXJDb21wbGV0ZWQucHVzaCh0YXNrKVxuICAgICAgfVxuICAgIH1cbiAgICByZWNlbnRDb21wbGV0ZWQuc29ydChieUlkQXNjKVxuICAgIG9sZGVyQ29tcGxldGVkLnNvcnQoYnlJZEFzYylcbiAgICBjb25zdCBpblByb2dyZXNzID0gdGFza3NcbiAgICAgIC5maWx0ZXIodCA9PiB0LnN0YXR1cyA9PT0gJ2luX3Byb2dyZXNzJylcbiAgICAgIC5zb3J0KGJ5SWRBc2MpXG4gICAgY29uc3QgcGVuZGluZyA9IHRhc2tzXG4gICAgICAuZmlsdGVyKHQgPT4gdC5zdGF0dXMgPT09ICdwZW5kaW5nJylcbiAgICAgIC5zb3J0KChhLCBiKSA9PiB7XG4gICAgICAgIGNvbnN0IGFCbG9ja2VkID0gYS5ibG9ja2VkQnkuc29tZShpZCA9PiB1bnJlc29sdmVkVGFza0lkcy5oYXMoaWQpKVxuICAgICAgICBjb25zdCBiQmxvY2tlZCA9IGIuYmxvY2tlZEJ5LnNvbWUoaWQgPT4gdW5yZXNvbHZlZFRhc2tJZHMuaGFzKGlkKSlcbiAgICAgICAgaWYgKGFCbG9ja2VkICE9PSBiQmxvY2tlZCkge1xuICAgICAgICAgIHJldHVybiBhQmxvY2tlZCA/IDEgOiAtMVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBieUlkQXNjKGEsIGIpXG4gICAgICB9KVxuXG4gICAgY29uc3QgcHJpb3JpdGl6ZWQgPSBbXG4gICAgICAuLi5yZWNlbnRDb21wbGV0ZWQsXG4gICAgICAuLi5pblByb2dyZXNzLFxuICAgICAgLi4ucGVuZGluZyxcbiAgICAgIC4uLm9sZGVyQ29tcGxldGVkLFxuICAgIF1cbiAgICB2aXNpYmxlVGFza3MgPSBwcmlvcml0aXplZC5zbGljZSgwLCBtYXhEaXNwbGF5KVxuICAgIGhpZGRlblRhc2tzID0gcHJpb3JpdGl6ZWQuc2xpY2UobWF4RGlzcGxheSlcbiAgfSBlbHNlIHtcbiAgICAvLyBObyB0cnVuY2F0aW9uIG5lZWRlZCDigJQgc29ydCBieSBJRCBmb3Igc3RhYmxlIG9yZGVyaW5nXG4gICAgdmlzaWJsZVRhc2tzID0gWy4uLnRhc2tzXS5zb3J0KGJ5SWRBc2MpXG4gICAgaGlkZGVuVGFza3MgPSBbXVxuICB9XG5cbiAgbGV0IGhpZGRlblN1bW1hcnkgPSAnJ1xuICBpZiAoaGlkZGVuVGFza3MubGVuZ3RoID4gMCkge1xuICAgIGNvbnN0IHBhcnRzOiBzdHJpbmdbXSA9IFtdXG4gICAgY29uc3QgaGlkZGVuUGVuZGluZyA9IGNvdW50KGhpZGRlblRhc2tzLCB0ID0+IHQuc3RhdHVzID09PSAncGVuZGluZycpXG4gICAgY29uc3QgaGlkZGVuSW5Qcm9ncmVzcyA9IGNvdW50KGhpZGRlblRhc2tzLCB0ID0+IHQuc3RhdHVzID09PSAnaW5fcHJvZ3Jlc3MnKVxuICAgIGNvbnN0IGhpZGRlbkNvbXBsZXRlZCA9IGNvdW50KGhpZGRlblRhc2tzLCB0ID0+IHQuc3RhdHVzID09PSAnY29tcGxldGVkJylcbiAgICBpZiAoaGlkZGVuSW5Qcm9ncmVzcyA+IDApIHtcbiAgICAgIHBhcnRzLnB1c2goYCR7aGlkZGVuSW5Qcm9ncmVzc30gaW4gcHJvZ3Jlc3NgKVxuICAgIH1cbiAgICBpZiAoaGlkZGVuUGVuZGluZyA+IDApIHtcbiAgICAgIHBhcnRzLnB1c2goYCR7aGlkZGVuUGVuZGluZ30gcGVuZGluZ2ApXG4gICAgfVxuICAgIGlmIChoaWRkZW5Db21wbGV0ZWQgPiAwKSB7XG4gICAgICBwYXJ0cy5wdXNoKGAke2hpZGRlbkNvbXBsZXRlZH0gY29tcGxldGVkYClcbiAgICB9XG4gICAgaGlkZGVuU3VtbWFyeSA9IGAg4oCmICske3BhcnRzLmpvaW4oJywgJyl9YFxuICB9XG5cbiAgY29uc3QgY29udGVudCA9IChcbiAgICA8PlxuICAgICAge3Zpc2libGVUYXNrcy5tYXAodGFzayA9PiAoXG4gICAgICAgIDxUYXNrSXRlbVxuICAgICAgICAgIGtleT17dGFzay5pZH1cbiAgICAgICAgICB0YXNrPXt0YXNrfVxuICAgICAgICAgIG93bmVyQ29sb3I9e3Rhc2sub3duZXIgPyB0ZWFtbWF0ZUNvbG9yc1t0YXNrLm93bmVyXSA6IHVuZGVmaW5lZH1cbiAgICAgICAgICBvcGVuQmxvY2tlcnM9e3Rhc2suYmxvY2tlZEJ5LmZpbHRlcihpZCA9PiB1bnJlc29sdmVkVGFza0lkcy5oYXMoaWQpKX1cbiAgICAgICAgICBhY3Rpdml0eT17dGFzay5vd25lciA/IHRlYW1tYXRlQWN0aXZpdHlbdGFzay5vd25lcl0gOiB1bmRlZmluZWR9XG4gICAgICAgICAgb3duZXJBY3RpdmU9e3Rhc2sub3duZXIgPyBhY3RpdmVUZWFtbWF0ZXMuaGFzKHRhc2sub3duZXIpIDogZmFsc2V9XG4gICAgICAgICAgY29sdW1ucz17Y29sdW1uc31cbiAgICAgICAgLz5cbiAgICAgICkpfVxuICAgICAge21heERpc3BsYXkgPiAwICYmIGhpZGRlblN1bW1hcnkgJiYgPFRleHQgZGltQ29sb3I+e2hpZGRlblN1bW1hcnl9PC9UZXh0Pn1cbiAgICA8Lz5cbiAgKVxuXG4gIGlmIChpc1N0YW5kYWxvbmUpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfSBtYXJnaW5MZWZ0PXsyfT5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+e3Rhc2tzLmxlbmd0aH08L1RleHQ+XG4gICAgICAgICAgICB7JyB0YXNrcyAoJ31cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+e2NvbXBsZXRlZENvdW50fTwvVGV4dD5cbiAgICAgICAgICAgIHsnIGRvbmUsICd9XG4gICAgICAgICAgICB7aW5Qcm9ncmVzc0NvdW50ID4gMCAmJiAoXG4gICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgPFRleHQgYm9sZD57aW5Qcm9ncmVzc0NvdW50fTwvVGV4dD5cbiAgICAgICAgICAgICAgICB7JyBpbiBwcm9ncmVzcywgJ31cbiAgICAgICAgICAgICAgPC8+XG4gICAgICAgICAgICApfVxuICAgICAgICAgICAgPFRleHQgYm9sZD57cGVuZGluZ0NvdW50fTwvVGV4dD5cbiAgICAgICAgICAgIHsnIG9wZW4pJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICB7Y29udGVudH1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIHJldHVybiA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj57Y29udGVudH08L0JveD5cbn1cblxudHlwZSBUYXNrSXRlbVByb3BzID0ge1xuICB0YXNrOiBUYXNrXG4gIG93bmVyQ29sb3I/OiBrZXlvZiBUaGVtZVxuICBvcGVuQmxvY2tlcnM6IHN0cmluZ1tdXG4gIGFjdGl2aXR5Pzogc3RyaW5nXG4gIG93bmVyQWN0aXZlOiBib29sZWFuXG4gIGNvbHVtbnM6IG51bWJlclxufVxuXG5mdW5jdGlvbiBnZXRUYXNrSWNvbihzdGF0dXM6IFRhc2tbJ3N0YXR1cyddKToge1xuICBpY29uOiBzdHJpbmdcbiAgY29sb3I6IGtleW9mIFRoZW1lIHwgdW5kZWZpbmVkXG59IHtcbiAgc3dpdGNoIChzdGF0dXMpIHtcbiAgICBjYXNlICdjb21wbGV0ZWQnOlxuICAgICAgcmV0dXJuIHsgaWNvbjogZmlndXJlcy50aWNrLCBjb2xvcjogJ3N1Y2Nlc3MnIH1cbiAgICBjYXNlICdpbl9wcm9ncmVzcyc6XG4gICAgICByZXR1cm4geyBpY29uOiBmaWd1cmVzLnNxdWFyZVNtYWxsRmlsbGVkLCBjb2xvcjogJ2NsYXVkZScgfVxuICAgIGNhc2UgJ3BlbmRpbmcnOlxuICAgICAgcmV0dXJuIHsgaWNvbjogZmlndXJlcy5zcXVhcmVTbWFsbCwgY29sb3I6IHVuZGVmaW5lZCB9XG4gIH1cbn1cblxuZnVuY3Rpb24gVGFza0l0ZW0oe1xuICB0YXNrLFxuICBvd25lckNvbG9yLFxuICBvcGVuQmxvY2tlcnMsXG4gIGFjdGl2aXR5LFxuICBvd25lckFjdGl2ZSxcbiAgY29sdW1ucyxcbn06IFRhc2tJdGVtUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBpc0NvbXBsZXRlZCA9IHRhc2suc3RhdHVzID09PSAnY29tcGxldGVkJ1xuICBjb25zdCBpc0luUHJvZ3Jlc3MgPSB0YXNrLnN0YXR1cyA9PT0gJ2luX3Byb2dyZXNzJ1xuICBjb25zdCBpc0Jsb2NrZWQgPSBvcGVuQmxvY2tlcnMubGVuZ3RoID4gMFxuXG4gIGNvbnN0IHsgaWNvbiwgY29sb3IgfSA9IGdldFRhc2tJY29uKHRhc2suc3RhdHVzKVxuXG4gIGNvbnN0IHNob3dBY3Rpdml0eSA9IGlzSW5Qcm9ncmVzcyAmJiAhaXNCbG9ja2VkICYmIGFjdGl2aXR5XG5cbiAgLy8gUmVzcG9uc2l2ZSBsYXlvdXQ6IGhpZGUgb3duZXIgb24gbmFycm93IHNjcmVlbnMgKDw2MCBjb2xzKVxuICAvLyBUcnVuY2F0ZSBzdWJqZWN0IGJhc2VkIG9uIGF2YWlsYWJsZSBzcGFjZVxuICBjb25zdCBzaG93T3duZXIgPSBjb2x1bW5zID49IDYwICYmIHRhc2sub3duZXIgJiYgb3duZXJBY3RpdmVcbiAgY29uc3Qgb3duZXJXaWR0aCA9IHNob3dPd25lciA/IHN0cmluZ1dpZHRoKGAgKEAke3Rhc2sub3duZXJ9KWApIDogMFxuICAvLyBBY2NvdW50IGZvcjogaWNvbigyKSArIGluZGVudGF0aW9uKH44IHdoZW4gbmVzdGVkIHVuZGVyIHNwaW5uZXIpICsgb3duZXIgKyBzYWZldHlcbiAgLy8gVXNlIGNvbHVtbnMgLSAxNSBhcyBhIGNvbnNlcnZhdGl2ZSBlc3RpbWF0ZSBmb3IgbmVzdGVkIGxheW91dHNcbiAgY29uc3QgbWF4U3ViamVjdFdpZHRoID0gTWF0aC5tYXgoMTUsIGNvbHVtbnMgLSAxNSAtIG93bmVyV2lkdGgpXG4gIGNvbnN0IGRpc3BsYXlTdWJqZWN0ID0gdHJ1bmNhdGVUb1dpZHRoKHRhc2suc3ViamVjdCwgbWF4U3ViamVjdFdpZHRoKVxuXG4gIC8vIFRydW5jYXRlIGFjdGl2aXR5IGZvciBuYXJyb3cgc2NyZWVuc1xuICBjb25zdCBtYXhBY3Rpdml0eVdpZHRoID0gTWF0aC5tYXgoMTUsIGNvbHVtbnMgLSAxNSlcbiAgY29uc3QgZGlzcGxheUFjdGl2aXR5ID0gYWN0aXZpdHlcbiAgICA/IHRydW5jYXRlVG9XaWR0aChhY3Rpdml0eSwgbWF4QWN0aXZpdHlXaWR0aClcbiAgICA6IHVuZGVmaW5lZFxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8Qm94PlxuICAgICAgICA8VGV4dCBjb2xvcj17Y29sb3J9PntpY29ufSA8L1RleHQ+XG4gICAgICAgIDxUZXh0XG4gICAgICAgICAgYm9sZD17aXNJblByb2dyZXNzfVxuICAgICAgICAgIHN0cmlrZXRocm91Z2g9e2lzQ29tcGxldGVkfVxuICAgICAgICAgIGRpbUNvbG9yPXtpc0NvbXBsZXRlZCB8fCBpc0Jsb2NrZWR9XG4gICAgICAgID5cbiAgICAgICAgICB7ZGlzcGxheVN1YmplY3R9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAge3Nob3dPd25lciAmJiAoXG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7JyAoJ31cbiAgICAgICAgICAgIHtvd25lckNvbG9yID8gKFxuICAgICAgICAgICAgICA8VGhlbWVkVGV4dCBjb2xvcj17b3duZXJDb2xvcn0+QHt0YXNrLm93bmVyfTwvVGhlbWVkVGV4dD5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgIGBAJHt0YXNrLm93bmVyfWBcbiAgICAgICAgICAgICl9XG4gICAgICAgICAgICB7JyknfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKX1cbiAgICAgICAge2lzQmxvY2tlZCAmJiAoXG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7JyAnfVxuICAgICAgICAgICAge2ZpZ3VyZXMucG9pbnRlclNtYWxsfSBibG9ja2VkIGJ5eycgJ31cbiAgICAgICAgICAgIHtbLi4ub3BlbkJsb2NrZXJzXVxuICAgICAgICAgICAgICAuc29ydCgoYSwgYikgPT4gcGFyc2VJbnQoYSwgMTApIC0gcGFyc2VJbnQoYiwgMTApKVxuICAgICAgICAgICAgICAubWFwKGlkID0+IGAjJHtpZH1gKVxuICAgICAgICAgICAgICAuam9pbignLCAnKX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICA8L0JveD5cbiAgICAgIHtzaG93QWN0aXZpdHkgJiYgZGlzcGxheUFjdGl2aXR5ICYmIChcbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHsnICAnfVxuICAgICAgICAgICAge2Rpc3BsYXlBY3Rpdml0eX1cbiAgICAgICAgICAgIHtmaWd1cmVzLmVsbGlwc2lzfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLGVBQWUsUUFBUSw2QkFBNkI7QUFDN0QsU0FBU0MsV0FBVyxRQUFRLHVCQUF1QjtBQUNuRCxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLFNBQVNDLFdBQVcsUUFBUSxzQkFBc0I7QUFDbEQsU0FBU0MsdUJBQXVCLFFBQVEseUNBQXlDO0FBQ2pGLFNBQ0VDLDBCQUEwQixFQUMxQixLQUFLQyxjQUFjLFFBQ2QseUNBQXlDO0FBQ2hELFNBQVNDLG9CQUFvQixRQUFRLGdDQUFnQztBQUNyRSxTQUFTQyxLQUFLLFFBQVEsbUJBQW1CO0FBQ3pDLFNBQVNDLHlCQUF5QixRQUFRLGdDQUFnQztBQUMxRSxTQUFTQyxlQUFlLFFBQVEsb0JBQW9CO0FBQ3BELFNBQVNDLGVBQWUsRUFBRSxLQUFLQyxJQUFJLFFBQVEsbUJBQW1CO0FBQzlELGNBQWNDLEtBQUssUUFBUSxtQkFBbUI7QUFDOUMsT0FBT0MsVUFBVSxNQUFNLCtC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 \ No newline at end of file diff --git a/components/TeammateViewHeader.tsx b/components/TeammateViewHeader.tsx new file mode 100644 index 0000000..ea65ddc --- /dev/null +++ b/components/TeammateViewHeader.tsx @@ -0,0 +1,82 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Box, Text } from '../ink.js'; +import { useAppState } from '../state/AppState.js'; +import { getViewedTeammateTask } from '../state/selectors.js'; +import { toInkColor } from '../utils/ink.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +import { OffscreenFreeze } from './OffscreenFreeze.js'; + +/** + * Header shown when viewing a teammate's transcript. + * Displays teammate name (colored), task description, and exit hint. + */ +export function TeammateViewHeader() { + const $ = _c(14); + const viewedTeammate = useAppState(_temp); + if (!viewedTeammate) { + return null; + } + let t0; + if ($[0] !== viewedTeammate.identity.color) { + t0 = toInkColor(viewedTeammate.identity.color); + $[0] = viewedTeammate.identity.color; + $[1] = t0; + } else { + t0 = $[1]; + } + const nameColor = t0; + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Viewing ; + $[2] = t1; + } else { + t1 = $[2]; + } + let t2; + if ($[3] !== nameColor || $[4] !== viewedTeammate.identity.agentName) { + t2 = @{viewedTeammate.identity.agentName}; + $[3] = nameColor; + $[4] = viewedTeammate.identity.agentName; + $[5] = t2; + } else { + t2 = $[5]; + } + let t3; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t3 = {" \xB7 "}; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] !== t2) { + t4 = {t1}{t2}{t3}; + $[7] = t2; + $[8] = t4; + } else { + t4 = $[8]; + } + let t5; + if ($[9] !== viewedTeammate.prompt) { + t5 = {viewedTeammate.prompt}; + $[9] = viewedTeammate.prompt; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] !== t4 || $[12] !== t5) { + t6 = {t4}{t5}; + $[11] = t4; + $[12] = t5; + $[13] = t6; + } else { + t6 = $[13]; + } + return t6; +} +function _temp(s) { + return getViewedTeammateTask(s); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJ1c2VBcHBTdGF0ZSIsImdldFZpZXdlZFRlYW1tYXRlVGFzayIsInRvSW5rQ29sb3IiLCJLZXlib2FyZFNob3J0Y3V0SGludCIsIk9mZnNjcmVlbkZyZWV6ZSIsIlRlYW1tYXRlVmlld0hlYWRlciIsIiQiLCJfYyIsInZpZXdlZFRlYW1tYXRlIiwiX3RlbXAiLCJ0MCIsImlkZW50aXR5IiwiY29sb3IiLCJuYW1lQ29sb3IiLCJ0MSIsIlN5bWJvbCIsImZvciIsInQyIiwiYWdlbnROYW1lIiwidDMiLCJ0NCIsInQ1IiwicHJvbXB0IiwidDYiLCJzIl0sInNvdXJjZXMiOlsiVGVhbW1hdGVWaWV3SGVhZGVyLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlIH0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgeyBnZXRWaWV3ZWRUZWFtbWF0ZVRhc2sgfSBmcm9tICcuLi9zdGF0ZS9zZWxlY3RvcnMuanMnXG5pbXBvcnQgeyB0b0lua0NvbG9yIH0gZnJvbSAnLi4vdXRpbHMvaW5rLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBPZmZzY3JlZW5GcmVlemUgfSBmcm9tICcuL09mZnNjcmVlbkZyZWV6ZS5qcydcblxuLyoqXG4gKiBIZWFkZXIgc2hvd24gd2hlbiB2aWV3aW5nIGEgdGVhbW1hdGUncyB0cmFuc2NyaXB0LlxuICogRGlzcGxheXMgdGVhbW1hdGUgbmFtZSAoY29sb3JlZCksIHRhc2sgZGVzY3JpcHRpb24sIGFuZCBleGl0IGhpbnQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBUZWFtbWF0ZVZpZXdIZWFkZXIoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3Qgdmlld2VkVGVhbW1hdGUgPSB1c2VBcHBTdGF0ZShzID0+IGdldFZpZXdlZFRlYW1tYXRlVGFzayhzKSlcblxuICBpZiAoIXZpZXdlZFRlYW1tYXRlKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IG5hbWVDb2xvciA9IHRvSW5rQ29sb3Iodmlld2VkVGVhbW1hdGUuaWRlbnRpdHkuY29sb3IpXG5cbiAgcmV0dXJuIChcbiAgICA8T2Zmc2NyZWVuRnJlZXplPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dD5WaWV3aW5nIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBjb2xvcj17bmFtZUNvbG9yfSBib2xkPlxuICAgICAgICAgICAgQHt2aWV3ZWRUZWFtbWF0ZS5pZGVudGl0eS5hZ2VudE5hbWV9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgeycgwrcgJ31cbiAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cImVzY1wiIGFjdGlvbj1cInJldHVyblwiIC8+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+e3ZpZXdlZFRlYW1tYXRlLnByb21wdH08L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L09mZnNjcmVlbkZyZWV6ZT5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxXQUFXO0FBQ3JDLFNBQVNDLFdBQVcsUUFBUSxzQkFBc0I7QUFDbEQsU0FBU0MscUJBQXFCLFFBQVEsdUJBQXVCO0FBQzdELFNBQVNDLFVBQVUsUUFBUSxpQkFBaUI7QUFDNUMsU0FBU0Msb0JBQW9C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 \ No newline at end of file diff --git a/components/TeleportError.tsx b/components/TeleportError.tsx new file mode 100644 index 0000000..585ed37 --- /dev/null +++ b/components/TeleportError.tsx @@ -0,0 +1,189 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useEffect, useState } from 'react'; +import { checkIsGitClean, checkNeedsClaudeAiLogin } from 'src/utils/background/remote/preconditions.js'; +import { gracefulShutdownSync } from 'src/utils/gracefulShutdown.js'; +import { Box, Text } from '../ink.js'; +import { ConsoleOAuthFlow } from './ConsoleOAuthFlow.js'; +import { Select } from './CustomSelect/index.js'; +import { Dialog } from './design-system/Dialog.js'; +import { TeleportStash } from './TeleportStash.js'; +export type TeleportLocalErrorType = 'needsLogin' | 'needsGitStash'; +type TeleportErrorProps = { + onComplete: () => void; + errorsToIgnore?: ReadonlySet; +}; + +// Module-level sentinel so the default parameter has stable identity. +// Previously `= new Set()` created a fresh Set every render, which put +// a new object in checkErrors' deps and caused the mount effect to +// re-fire on every render. +const EMPTY_ERRORS_TO_IGNORE: ReadonlySet = new Set(); +export function TeleportError(t0) { + const $ = _c(18); + const { + onComplete, + errorsToIgnore: t1 + } = t0; + const errorsToIgnore = t1 === undefined ? EMPTY_ERRORS_TO_IGNORE : t1; + const [currentError, setCurrentError] = useState(null); + const [isLoggingIn, setIsLoggingIn] = useState(false); + let t2; + if ($[0] !== errorsToIgnore || $[1] !== onComplete) { + t2 = async () => { + const currentErrors = await getTeleportErrors(); + const filteredErrors = new Set(Array.from(currentErrors).filter(error => !errorsToIgnore.has(error))); + if (filteredErrors.size === 0) { + onComplete(); + return; + } + if (filteredErrors.has("needsLogin")) { + setCurrentError("needsLogin"); + } else { + if (filteredErrors.has("needsGitStash")) { + setCurrentError("needsGitStash"); + } + } + }; + $[0] = errorsToIgnore; + $[1] = onComplete; + $[2] = t2; + } else { + t2 = $[2]; + } + const checkErrors = t2; + let t3; + let t4; + if ($[3] !== checkErrors) { + t3 = () => { + checkErrors(); + }; + t4 = [checkErrors]; + $[3] = checkErrors; + $[4] = t3; + $[5] = t4; + } else { + t3 = $[4]; + t4 = $[5]; + } + useEffect(t3, t4); + const onCancel = _temp; + let t5; + if ($[6] !== checkErrors) { + t5 = () => { + setIsLoggingIn(false); + checkErrors(); + }; + $[6] = checkErrors; + $[7] = t5; + } else { + t5 = $[7]; + } + const handleLoginComplete = t5; + let t6; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t6 = () => { + setIsLoggingIn(true); + }; + $[8] = t6; + } else { + t6 = $[8]; + } + const handleLoginWithClaudeAI = t6; + let t7; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t7 = value => { + if (value === "login") { + handleLoginWithClaudeAI(); + } else { + onCancel(); + } + }; + $[9] = t7; + } else { + t7 = $[9]; + } + const handleLoginDialogSelect = t7; + let t8; + if ($[10] !== checkErrors) { + t8 = () => { + checkErrors(); + }; + $[10] = checkErrors; + $[11] = t8; + } else { + t8 = $[11]; + } + const handleStashComplete = t8; + if (!currentError) { + return null; + } + switch (currentError) { + case "needsGitStash": + { + let t9; + if ($[12] !== handleStashComplete) { + t9 = ; + $[12] = handleStashComplete; + $[13] = t9; + } else { + t9 = $[13]; + } + return t9; + } + case "needsLogin": + { + if (isLoggingIn) { + let t9; + if ($[14] !== handleLoginComplete) { + t9 = ; + $[14] = handleLoginComplete; + $[15] = t9; + } else { + t9 = $[15]; + } + return t9; + } + let t9; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t9 = Teleport requires a Claude.ai account.Your Claude Pro/Max subscription will be used by Claude Code.; + $[16] = t9; + } else { + t9 = $[16]; + } + let t10; + if ($[17] === Symbol.for("react.memo_cache_sentinel")) { + t10 = {t9} void handleChange(value_0)} />} : {errorMessage && {errorMessage}}Run claude --teleport from a checkout of {targetRepo}; + $[8] = availablePaths.length; + $[9] = errorMessage; + $[10] = handleChange; + $[11] = options; + $[12] = targetRepo; + $[13] = validating; + $[14] = t3; + } else { + t3 = $[14]; + } + let t4; + if ($[15] !== onCancel || $[16] !== t3) { + t4 = {t3}; + $[15] = onCancel; + $[16] = t3; + $[17] = t4; + } else { + t4 = $[17]; + } + return t4; +} +function _temp(path) { + return { + label: Use {getDisplayPath(path)}, + value: path + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlU3RhdGUiLCJCb3giLCJUZXh0IiwiZ2V0RGlzcGxheVBhdGgiLCJyZW1vdmVQYXRoRnJvbVJlcG8iLCJ2YWxpZGF0ZVJlcG9BdFBhdGgiLCJTZWxlY3QiLCJEaWFsb2ciLCJTcGlubmVyIiwiUHJvcHMiLCJ0YXJnZXRSZXBvIiwiaW5pdGlhbFBhdGhzIiwib25TZWxlY3RQYXRoIiwicGF0aCIsIm9uQ2FuY2VsIiwiVGVsZXBvcnRSZXBvTWlzbWF0Y2hEaWFsb2ciLCJ0MCIsIiQiLCJfYyIsImF2YWlsYWJsZVBhdGhzIiwic2V0QXZhaWxhYmxlUGF0aHMiLCJlcnJvck1lc3NhZ2UiLCJzZXRFcnJvck1lc3NhZ2UiLCJ2YWxpZGF0aW5nIiwic2V0VmFsaWRhdGluZyIsInQxIiwidmFsdWUiLCJpc1ZhbGlkIiwidXBkYXRlZFBhdGhzIiwiZmlsdGVyIiwicCIsImhhbmRsZUNoYW5nZSIsInQyIiwidDMiLCJTeW1ib2wiLCJmb3IiLCJsYWJlbCIsIm1hcCIsIl90ZW1wIiwib3B0aW9ucyIsImxlbmd0aCIsInZhbHVlXzAiLCJ0NCJdLCJzb3VyY2VzIjpbIlRlbGVwb3J0UmVwb01pc21hdGNoRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlQ2FsbGJhY2ssIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBnZXREaXNwbGF5UGF0aCB9IGZyb20gJy4uL3V0aWxzL2ZpbGUuanMnXG5pbXBvcnQge1xuICByZW1vdmVQYXRoRnJvbVJlcG8sXG4gIHZhbGlkYXRlUmVwb0F0UGF0aCxcbn0gZnJvbSAnLi4vdXRpbHMvZ2l0aHViUmVwb1BhdGhNYXBwaW5nLmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi9DdXN0b21TZWxlY3QvaW5kZXguanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgU3Bpbm5lciB9IGZyb20gJy4vU3Bpbm5lci5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgdGFyZ2V0UmVwbzogc3RyaW5nXG4gIGluaXRpYWxQYXRoczogc3RyaW5nW11cbiAgb25TZWxlY3RQYXRoOiAocGF0aDogc3RyaW5nKSA9PiB2b2lkXG4gIG9uQ2FuY2VsOiAoKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBUZWxlcG9ydFJlcG9NaXNtYXRjaERpYWxvZyh7XG4gIHRhcmdldFJlcG8sXG4gIGluaXRpYWxQYXRocyxcbiAgb25TZWxlY3RQYXRoLFxuICBvbkNhbmNlbCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW2F2YWlsYWJsZVBhdGhzLCBzZXRBdmFpbGFibGVQYXRoc10gPSB1c2VTdGF0ZTxzdHJpbmdbXT4oaW5pdGlhbFBhdGhzKVxuICBjb25zdCBbZXJyb3JNZXNzYWdlLCBzZXRFcnJvck1lc3NhZ2VdID0gdXNlU3RhdGU8c3RyaW5nIHwgbnVsbD4obnVsbClcbiAgY29uc3QgW3ZhbGlkYXRpbmcsIHNldFZhbGlkYXRpbmddID0gdXNlU3RhdGUoZmFsc2UpXG5cbiAgY29uc3QgaGFuZGxlQ2hhbmdlID0gdXNlQ2FsbGJhY2soXG4gICAgYXN5bmMgKHZhbHVlOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgIGlmICh2YWx1ZSA9PT0gJ2NhbmNlbCcpIHtcbiAgICAgICAgb25DYW5jZWwoKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgc2V0VmFsaWRhdGluZyh0cnVlKVxuICAgICAgc2V0RXJyb3JNZXNzYWdlKG51bGwpXG5cbiAgICAgIGNvbnN0IGlzVmFsaWQgPSBhd2FpdCB2YWxpZGF0ZVJlcG9BdFBhdGgodmFsdWUsIHRhcmdldFJlcG8pXG5cbiAgICAgIGlmIChpc1ZhbGlkKSB7XG4gICAgICAgIG9uU2VsZWN0UGF0aCh2YWx1ZSlcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIFBhdGggaXMgaW52YWxpZCAtIHJlbW92ZSBpdCBmcm9tIGNvbmZpZyBhbmQgdXBkYXRlIHN0YXRlXG4gICAgICByZW1vdmVQYXRoRnJvbVJlcG8odGFyZ2V0UmVwbywgdmFsdWUpXG4gICAgICBjb25zdCB1cGRhdGVkUGF0aHMgPSBhdmFpbGFibGVQYXRocy5maWx0ZXIocCA9PiBwICE9PSB2YWx1ZSlcbiAgICAgIHNldEF2YWlsYWJsZVBhdGhzKHVwZGF0ZWRQYXRocylcbiAgICAgIHNldFZhbGlkYXRpbmcoZmFsc2UpXG5cbiAgICAgIHNldEVycm9yTWVzc2FnZShcbiAgICAgICAgYCR7Z2V0RGlzcGxheVBhdGgodmFsdWUpfSBubyBsb25nZXIgY29udGFpbnMgdGhlIGNvcnJlY3QgcmVwb3NpdG9yeS4gU2VsZWN0IGFub3RoZXIgcGF0aC5gLFxuICAgICAgKVxuICAgIH0sXG4gICAgW3RhcmdldFJlcG8sIGF2YWlsYWJsZVBhdGhzLCBvblNlbGVjdFBhdGgsIG9uQ2FuY2VsXSxcbiAgKVxuXG4gIGNvbnN0IG9wdGlvbnMgPSBbXG4gICAgLi4uYXZhaWxhYmxlUGF0aHMubWFwKHBhdGggPT4gKHtcbiAgICAgIGxhYmVsOiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIFVzZSA8VGV4dCBib2xkPntnZXREaXNwbGF5UGF0aChwYXRoKX08L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICksXG4gICAgICB2YWx1ZTogcGF0aCxcbiAgICB9KSksXG4gICAgeyBsYWJlbDogJ0NhbmNlbCcsIHZhbHVlOiAnY2FuY2VsJyB9LFxuICBdXG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nIHRpdGxlPVwiVGVsZXBvcnQgdG8gUmVwb1wiIG9uQ2FuY2VsPXtvbkNhbmNlbH0gY29sb3I9XCJiYWNrZ3JvdW5kXCI+XG4gICAgICB7YXZhaWxhYmxlUGF0aHMubGVuZ3RoID4gMCA/IChcbiAgICAgICAgPD5cbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICAgICAge2Vycm9yTWVzc2FnZSAmJiA8VGV4dCBjb2xvcj1cImVycm9yXCI+e2Vycm9yTWVzc2FnZX08L1RleHQ+fVxuICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgIE9wZW4gQ2xhdWRlIENvZGUgaW4gPFRleHQgYm9sZD57dGFyZ2V0UmVwb308L1RleHQ+OlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuXG4gICAgICAgICAge3ZhbGlkYXRpbmcgPyAoXG4gICAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgICA8U3Bpbm5lciAvPlxuICAgICAgICAgICAgICA8VGV4dD4gVmFsaWRhdGluZyByZXBvc2l0b3J54oCmPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKSA6IChcbiAgICAgICAgICAgIDxTZWxlY3RcbiAgICAgICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICAgICAgb25DaGFuZ2U9e3ZhbHVlID0+IHZvaWQgaGFuZGxlQ2hhbmdlKHZhbHVlKX1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC8+XG4gICAgICApIDogKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICAgIHtlcnJvck1lc3NhZ2UgJiYgPFRleHQgY29sb3I9XCJlcnJvclwiPntlcnJvck1lc3NhZ2V9PC9UZXh0Pn1cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIFJ1biBjbGF1ZGUgLS10ZWxlcG9ydCBmcm9tIGEgY2hlY2tvdXQgb2Yge3RhcmdldFJlcG99XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgPC9EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFBSUMsV0FBVyxFQUFFQyxRQUFRLFFBQVEs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 \ No newline at end of file diff --git a/components/TeleportResumeWrapper.tsx b/components/TeleportResumeWrapper.tsx new file mode 100644 index 0000000..ead5fdb --- /dev/null +++ b/components/TeleportResumeWrapper.tsx @@ -0,0 +1,167 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useEffect } from 'react'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import type { TeleportRemoteResponse } from 'src/utils/conversationRecovery.js'; +import type { CodeSession } from 'src/utils/teleport/api.js'; +import { type TeleportSource, useTeleportResume } from '../hooks/useTeleportResume.js'; +import { Box, Text } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { ResumeTask } from './ResumeTask.js'; +import { Spinner } from './Spinner.js'; +interface TeleportResumeWrapperProps { + onComplete: (result: TeleportRemoteResponse) => void; + onCancel: () => void; + onError?: (error: string, formattedMessage?: string) => void; + isEmbedded?: boolean; + source: TeleportSource; +} + +/** + * Wrapper component that manages the full teleport resume flow, + * including session selection, loading state, and error handling + */ +export function TeleportResumeWrapper(t0) { + const $ = _c(25); + const { + onComplete, + onCancel, + onError, + isEmbedded: t1, + source + } = t0; + const isEmbedded = t1 === undefined ? false : t1; + const { + resumeSession, + isResuming, + error, + selectedSession + } = useTeleportResume(source); + let t2; + let t3; + if ($[0] !== source) { + t2 = () => { + logEvent("tengu_teleport_started", { + source: source as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + }; + t3 = [source]; + $[0] = source; + $[1] = t2; + $[2] = t3; + } else { + t2 = $[1]; + t3 = $[2]; + } + useEffect(t2, t3); + let t4; + if ($[3] !== error || $[4] !== onComplete || $[5] !== onError || $[6] !== resumeSession) { + t4 = async session => { + const result = await resumeSession(session); + if (result) { + onComplete(result); + } else { + if (error) { + if (onError) { + onError(error.message, error.formattedMessage); + } + } + } + }; + $[3] = error; + $[4] = onComplete; + $[5] = onError; + $[6] = resumeSession; + $[7] = t4; + } else { + t4 = $[7]; + } + const handleSelect = t4; + let t5; + if ($[8] !== onCancel) { + t5 = () => { + logEvent("tengu_teleport_cancelled", {}); + onCancel(); + }; + $[8] = onCancel; + $[9] = t5; + } else { + t5 = $[9]; + } + const handleCancel = t5; + const t6 = !!error && !onError; + let t7; + if ($[10] !== t6) { + t7 = { + context: "Global", + isActive: t6 + }; + $[10] = t6; + $[11] = t7; + } else { + t7 = $[11]; + } + useKeybinding("app:interrupt", handleCancel, t7); + if (isResuming && selectedSession) { + let t8; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t8 = Resuming session…; + $[12] = t8; + } else { + t8 = $[12]; + } + let t9; + if ($[13] !== selectedSession.title) { + t9 = {t8}Loading "{selectedSession.title}"…; + $[13] = selectedSession.title; + $[14] = t9; + } else { + t9 = $[14]; + } + return t9; + } + if (error && !onError) { + let t8; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t8 = Failed to resume session; + $[15] = t8; + } else { + t8 = $[15]; + } + let t9; + if ($[16] !== error.message) { + t9 = {error.message}; + $[16] = error.message; + $[17] = t9; + } else { + t9 = $[17]; + } + let t10; + if ($[18] === Symbol.for("react.memo_cache_sentinel")) { + t10 = Press Esc to cancel; + $[18] = t10; + } else { + t10 = $[18]; + } + let t11; + if ($[19] !== t9) { + t11 = {t8}{t9}{t10}; + $[19] = t9; + $[20] = t11; + } else { + t11 = $[20]; + } + return t11; + } + let t8; + if ($[21] !== handleCancel || $[22] !== handleSelect || $[23] !== isEmbedded) { + t8 = ; + $[21] = handleCancel; + $[22] = handleSelect; + $[23] = isEmbedded; + $[24] = t8; + } else { + t8 = $[24]; + } + return t8; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsIlRlbGVwb3J0UmVtb3RlUmVzcG9uc2UiLCJDb2RlU2Vzc2lvbiIsIlRlbGVwb3J0U291cmNlIiwidXNlVGVsZXBvcnRSZXN1bWUiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZyIsIlJlc3VtZVRhc2siLCJTcGlubmVyIiwiVGVsZXBvcnRSZXN1bWVXcmFwcGVyUHJvcHMiLCJvbkNvbXBsZXRlIiwicmVzdWx0Iiwib25DYW5jZWwiLCJvbkVycm9yIiwiZXJyb3IiLCJmb3JtYXR0ZWRNZXNzYWdlIiwiaXNFbWJlZGRlZCIsInNvdXJjZSIsIlRlbGVwb3J0UmVzdW1lV3JhcHBlciIsInQwIiwiJCIsIl9jIiwidDEiLCJ1bmRlZmluZWQiLCJyZXN1bWVTZXNzaW9uIiwiaXNSZXN1bWluZyIsInNlbGVjdGVkU2Vzc2lvbiIsInQyIiwidDMiLCJ0NCIsInNlc3Npb24iLCJtZXNzYWdlIiwiaGFuZGxlU2VsZWN0IiwidDUiLCJoYW5kbGVDYW5jZWwiLCJ0NiIsInQ3IiwiY29udGV4dCIsImlzQWN0aXZlIiwidDgiLCJTeW1ib2wiLCJmb3IiLCJ0OSIsInRpdGxlIiwidDEwIiwidDExIl0sInNvdXJjZXMiOlsiVGVsZXBvcnRSZXN1bWVXcmFwcGVyLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICB0eXBlIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIGxvZ0V2ZW50LFxufSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHR5cGUgeyBUZWxlcG9ydFJlbW90ZVJlc3BvbnNlIH0gZnJvbSAnc3JjL3V0aWxzL2NvbnZlcnNhdGlvblJlY292ZXJ5LmpzJ1xuaW1wb3J0IHR5cGUgeyBDb2RlU2Vzc2lvbiB9IGZyb20gJ3NyYy91dGlscy90ZWxlcG9ydC9hcGkuanMnXG5pbXBvcnQge1xuICB0eXBlIFRlbGVwb3J0U291cmNlLFxuICB1c2VUZWxlcG9ydFJlc3VtZSxcbn0gZnJvbSAnLi4vaG9va3MvdXNlVGVsZXBvcnRSZXN1bWUuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7IFJlc3VtZVRhc2sgfSBmcm9tICcuL1Jlc3VtZVRhc2suanMnXG5pbXBvcnQgeyBTcGlubmVyIH0gZnJvbSAnLi9TcGlubmVyLmpzJ1xuXG5pbnRlcmZhY2UgVGVsZXBvcnRSZXN1bWVXcmFwcGVyUHJvcHMge1xuICBvbkNvbXBsZXRlOiAocmVzdWx0OiBUZWxlcG9ydFJlbW90ZVJlc3BvbnNlKSA9PiB2b2lkXG4gIG9uQ2FuY2VsOiAoKSA9PiB2b2lkXG4gIG9uRXJyb3I/OiAoZXJyb3I6IHN0cmluZywgZm9ybWF0dGVkTWVzc2FnZT86IHN0cmluZykgPT4gdm9pZFxuICBpc0VtYmVkZGVkPzogYm9vbGVhblxuICBzb3VyY2U6IFRlbGVwb3J0U291cmNlXG59XG5cbi8qKlxuICogV3JhcHBlciBjb21wb25lbnQgdGhhdCBtYW5hZ2VzIHRoZSBmdWxsIHRlbGVwb3J0IHJlc3VtZSBmbG93LFxuICogaW5jbHVkaW5nIHNlc3Npb24gc2VsZWN0aW9uLCBsb2FkaW5nIHN0YXRlLCBhbmQgZXJyb3IgaGFuZGxpbmdcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFRlbGVwb3J0UmVzdW1lV3JhcHBlcih7XG4gIG9uQ29tcGxldGUsXG4gIG9uQ2FuY2VsLFxuICBvbkVycm9yLFxuICBpc0VtYmVkZGVkID0gZmFsc2UsXG4gIHNvdXJjZSxcbn06IFRlbGVwb3J0UmVzdW1lV3JhcHBlclByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyByZXN1bWVTZXNzaW9uLCBpc1Jlc3VtaW5nLCBlcnJvciwgc2VsZWN0ZWRTZXNzaW9uIH0gPVxuICAgIHVzZVRlbGVwb3J0UmVzdW1lKHNvdXJjZSlcblxuICAvLyBMb2cgd2hlbiB0ZWxlcG9ydCBmbG93IHN0YXJ0cyAoZm9yIGZ1bm5lbCB0cmFja2luZylcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBsb2dFdmVudCgndGVuZ3VfdGVsZXBvcnRfc3RhcnRlZCcsIHtcbiAgICAgIHNvdXJjZTpcbiAgICAgICAgc291cmNlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgfSlcbiAgfSwgW3NvdXJjZV0pXG5cbiAgY29uc3QgaGFuZGxlU2VsZWN0ID0gYXN5bmMgKHNlc3Npb246IENvZGVTZXNzaW9uKSA9PiB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcmVzdW1lU2Vzc2lvbihzZXNzaW9uKVxuICAgIGlmIChyZXN1bHQpIHtcbiAgICAgIG9uQ29tcGxldGUocmVzdWx0KVxuICAgIH0gZWxzZSBpZiAoZXJyb3IpIHtcbiAgICAgIC8vIElmIHRoZXJlJ3MgYW4gZXJyb3IgaGFuZGxlciBwcm92aWRlZCwgdXNlIGl0XG4gICAgICBpZiAob25FcnJvcikge1xuICAgICAgICBvbkVycm9yKGVycm9yLm1lc3NhZ2UsIGVycm9yLmZvcm1hdHRlZE1lc3NhZ2UpXG4gICAgICB9XG4gICAgICAvLyBPdGhlcndpc2UgdGhlIGVycm9yIHdpbGwgYmUgZGlzcGxheWVkIGluIHRoZSBVSVxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IGhhbmRsZUNhbmNlbCA9ICgpID0+IHtcbiAgICBsb2dFdmVudCgndGVuZ3VfdGVsZXBvcnRfY2FuY2VsbGVkJywge30pXG4gICAgb25DYW5jZWwoKVxuICB9XG5cbiAgLy8gQWxsb3cgRXNjIHRvIGRpc21pc3MgdGhlIGVycm9yIHN0YXRlXG4gIHVzZUtleWJpbmRpbmcoJ2FwcDppbnRlcnJ1cHQnLCBoYW5kbGVDYW5jZWwsIHtcbiAgICBjb250ZXh0OiAnR2xvYmFsJyxcbiAgICBpc0FjdGl2ZTogISFlcnJvciAmJiAhb25FcnJvcixcbiAgfSlcblxuICAvLyBTaG93IGxvYWRpbmcgc3Bpbm5lciB3aGVuIHJlc3VtaW5nXG4gIGlmIChpc1Jlc3VtaW5nICYmIHNlbGVjdGVkU2Vzc2lvbikge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nPXsxfT5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCI+XG4gICAgICAgICAgPFNwaW5uZXIgLz5cbiAgICAgICAgICA8VGV4dCBib2xkPlJlc3VtaW5nIHNlc3Npb27igKY8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5Mb2FkaW5nICZxdW90O3tzZWxlY3RlZFNlc3Npb24udGl0bGV9JnF1b3Q74oCmPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgLy8gU2hvdyBlcnJvciBpZiB0aGVyZSB3YXMgYSBwcm9ibGVtIHJlc3VtaW5nXG4gIGlmIChlcnJvciAmJiAhb25FcnJvcikge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nPXsxfT5cbiAgICAgICAgPFRleHQgYm9sZCBjb2xvcj1cImVycm9yXCI+XG4gICAgICAgICAgRmFpbGVkIHRvIHJlc3VtZSBzZXNzaW9uXG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+e2Vycm9yLm1lc3NhZ2V9PC9UZXh0PlxuICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICBQcmVzcyA8VGV4dCBib2xkPkVzYzwvVGV4dD4gdG8gY2FuY2VsXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPFJlc3VtZVRhc2tcbiAgICAgIG9uU2VsZWN0PXtoYW5kbGVTZWxlY3R9XG4gICAgICBvbkNhbmNlbD17aGFuZGxlQ2FuY2VsfVxuICAgICAgaXNFbWJlZGRlZD17aXNFbWJlZGRlZH1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUlDLFNBQVMsUUFBUSxPQUFPO0FBQ3hDLFNBQ0UsS0FBS0MsMERBQTBELEVBQy9EQyxRQUFRLFFBQ0gsaUNBQWlDO0FBQ3hDLGNBQWNDLHNCQUFzQixRQUFRLG1DQUFtQztBQUMvRSxjQUFjQyxXQUFXLFFBQVEsMkJBQTJCO0FBQzVELFNBQ0UsS0FBS0MsY0FBYyxFQUNuQkMsaUJBQWlCLFFBQ1osK0JBQStCO0FBQ3RDLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDckMsU0FBU0MsYUFBYSxRQUFRLGlDQUFpQztBQUMvRCxTQUFTQyxVQUFVLFFBQVEsaUJBQWlCO0FBQzVDLFNBQVNDLE9BQU8sUUFBUSxjQUFjO0FBRXRDLFVBQVVDLDBCQUEwQixDQUFDO0VBQ25DQyxVQUFVLEVBQUUsQ0FBQ0MsTUFBTSxFQUFFWCxzQkFBc0IsRUFBRSxHQUFHLElBQUk7RUFDcERZLFFBQVEsRUFBRSxHQUFHLEdBQUcsSUFBSTtFQUNwQkMsT0FBTyxDQUFDLEVBQUUsQ0FBQ0MsS0FBSyxFQUFFLE1BQU0sRUFBRUMsZ0JBQXlCLENBQVIsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQzVEQyxVQUFVLENBQUMsRUFBRSxPQUFPO0VBQ3BCQyxNQUFNLEVBQUVmLGNBQWM7QUFDeEI7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFnQixzQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUErQjtJQUFBWCxVQUFBO0lBQUFFLFFBQUE7SUFBQUMsT0FBQTtJQUFBRyxVQUFBLEVBQUFNLEVB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 \ No newline at end of file diff --git a/components/TeleportStash.tsx b/components/TeleportStash.tsx new file mode 100644 index 0000000..cca93bd --- /dev/null +++ b/components/TeleportStash.tsx @@ -0,0 +1,116 @@ +import figures from 'figures'; +import React, { useEffect, useState } from 'react'; +import { Box, Text } from '../ink.js'; +import { logForDebugging } from '../utils/debug.js'; +import type { GitFileStatus } from '../utils/git.js'; +import { getFileStatus, stashToCleanState } from '../utils/git.js'; +import { Select } from './CustomSelect/index.js'; +import { Dialog } from './design-system/Dialog.js'; +import { Spinner } from './Spinner.js'; +type TeleportStashProps = { + onStashAndContinue: () => void; + onCancel: () => void; +}; +export function TeleportStash({ + onStashAndContinue, + onCancel +}: TeleportStashProps): React.ReactNode { + const [gitFileStatus, setGitFileStatus] = useState(null); + const changedFiles = gitFileStatus !== null ? [...gitFileStatus.tracked, ...gitFileStatus.untracked] : []; + const [loading, setLoading] = useState(true); + const [stashing, setStashing] = useState(false); + const [error, setError] = useState(null); + + // Load changed files on mount + useEffect(() => { + const loadChangedFiles = async () => { + try { + const fileStatus = await getFileStatus(); + setGitFileStatus(fileStatus); + } catch (err) { + const errorMessage = err instanceof Error ? err.message : String(err); + logForDebugging(`Error getting changed files: ${errorMessage}`, { + level: 'error' + }); + setError('Failed to get changed files'); + } finally { + setLoading(false); + } + }; + void loadChangedFiles(); + }, []); + const handleStash = async () => { + setStashing(true); + try { + logForDebugging('Stashing changes before teleport...'); + const success = await stashToCleanState('Teleport auto-stash'); + if (success) { + logForDebugging('Successfully stashed changes'); + onStashAndContinue(); + } else { + setError('Failed to stash changes'); + } + } catch (err_0) { + const errorMessage_0 = err_0 instanceof Error ? err_0.message : String(err_0); + logForDebugging(`Error stashing changes: ${errorMessage_0}`, { + level: 'error' + }); + setError('Failed to stash changes'); + } finally { + setStashing(false); + } + }; + const handleSelectChange = (value: string) => { + if (value === 'stash') { + void handleStash(); + } else { + onCancel(); + } + }; + if (loading) { + return + + + Checking git status{figures.ellipsis} + + ; + } + if (error) { + return + + Error: {error} + + + Press + Escape + to cancel + + ; + } + const showFileCount = changedFiles.length > 8; + return + + Teleport will switch git branches. The following changes were found: + + + + {changedFiles.length > 0 ? showFileCount ? {changedFiles.length} files changed : changedFiles.map((file: string, index: number) => {file}) : No changes detected} + + + + Would you like to stash these changes and continue with teleport? + + + {stashing ? + + Stashing changes... + : ; + $[25] = t15; + $[26] = t16; + $[27] = t17; + $[28] = themeSetting; + $[29] = t18; + } else { + t18 = $[29]; + } + let t19; + if ($[30] !== t11 || $[31] !== t14 || $[32] !== t18) { + t19 = {t11}{t14}{t18}; + $[30] = t11; + $[31] = t14; + $[32] = t18; + $[33] = t19; + } else { + t19 = $[33]; + } + let t20; + if ($[34] === Symbol.for("react.memo_cache_sentinel")) { + t20 = { + oldStart: 1, + newStart: 1, + oldLines: 3, + newLines: 3, + lines: [" function greet() {", "- console.log(\"Hello, World!\");", "+ console.log(\"Hello, Claude!\");", " }"] + }; + $[34] = t20; + } else { + t20 = $[34]; + } + let t21; + if ($[35] !== columns) { + t21 = ; + $[35] = columns; + $[36] = t21; + } else { + t21 = $[36]; + } + const t22 = colorModuleUnavailableReason === "env" ? `Syntax highlighting disabled (via CLAUDE_CODE_SYNTAX_HIGHLIGHT=${process.env.CLAUDE_CODE_SYNTAX_HIGHLIGHT})` : syntaxHighlightingDisabled ? `Syntax highlighting disabled (${syntaxToggleShortcut} to enable)` : syntaxTheme ? `Syntax theme: ${syntaxTheme.theme}${syntaxTheme.source ? ` (from ${syntaxTheme.source})` : ""} (${syntaxToggleShortcut} to disable)` : `Syntax highlighting enabled (${syntaxToggleShortcut} to disable)`; + let t23; + if ($[37] !== t22) { + t23 = {" "}{t22}; + $[37] = t22; + $[38] = t23; + } else { + t23 = $[38]; + } + let t24; + if ($[39] !== t21 || $[40] !== t23) { + t24 = {t21}{t23}; + $[39] = t21; + $[40] = t23; + $[41] = t24; + } else { + t24 = $[41]; + } + let t25; + if ($[42] !== t19 || $[43] !== t24) { + t25 = {t19}{t24}; + $[42] = t19; + $[43] = t24; + $[44] = t25; + } else { + t25 = $[44]; + } + const content = t25; + if (!showIntroText) { + let t26; + if ($[45] !== content) { + t26 = {content}; + $[45] = content; + $[46] = t26; + } else { + t26 = $[46]; + } + let t27; + if ($[47] !== helpText || $[48] !== showHelpTextBelow) { + t27 = showHelpTextBelow && helpText && {helpText}; + $[47] = helpText; + $[48] = showHelpTextBelow; + $[49] = t27; + } else { + t27 = $[49]; + } + let t28; + if ($[50] !== exitState || $[51] !== hideEscToCancel) { + t28 = !hideEscToCancel && {exitState.pending ? <>Press {exitState.keyName} again to exit : }; + $[50] = exitState; + $[51] = hideEscToCancel; + $[52] = t28; + } else { + t28 = $[52]; + } + let t29; + if ($[53] !== t27 || $[54] !== t28) { + t29 = {t27}{t28}; + $[53] = t27; + $[54] = t28; + $[55] = t29; + } else { + t29 = $[55]; + } + let t30; + if ($[56] !== t26 || $[57] !== t29) { + t30 = <>{t26}{t29}; + $[56] = t26; + $[57] = t29; + $[58] = t30; + } else { + t30 = $[58]; + } + return t30; + } + return content; +} +function _temp2() {} +function _temp(s) { + return s.settings.syntaxHighlightingDisabled; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJ1c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MiLCJ1c2VUZXJtaW5hbFNpemUiLCJCb3giLCJUZXh0IiwidXNlUHJldmlld1RoZW1lIiwidXNlVGhlbWUiLCJ1c2VUaGVtZVNldHRpbmciLCJ1c2VSZWdpc3RlcktleWJpbmRpbmdDb250ZXh0IiwidXNlS2V5YmluZGluZyIsInVzZVNob3J0Y3V0RGlzcGxheSIsInVzZUFwcFN0YXRlIiwidXNlU2V0QXBwU3RhdGUiLCJncmFjZWZ1bFNodXRkb3duIiwidXBkYXRlU2V0dGluZ3NGb3JTb3VyY2UiLCJUaGVtZVNldHRpbmciLCJTZWxlY3QiLCJCeWxpbmUiLCJLZXlib2FyZFNob3J0Y3V0SGludCIsImdldENvbG9yTW9kdWxlVW5hdmFpbGFibGVSZWFzb24iLCJnZXRTeW50YXhUaGVtZSIsIlN0cnVjdHVyZWREaWZmIiwiVGhlbWVQaWNrZXJQcm9wcyIsIm9uVGhlbWVTZWxlY3QiLCJzZXR0aW5nIiwic2hvd0ludHJvVGV4dCIsImhlbHBUZXh0Iiwic2hvd0hlbHBUZXh0QmVsb3ciLCJoaWRlRXNjVG9DYW5jZWwiLCJza2lwRXhpdEhhbmRsaW5nIiwib25DYW5jZWwiLCJUaGVtZVBpY2tlciIsInQwIiwiJCIsIl9jIiwidDEiLCJ0MiIsInQzIiwidDQiLCJ0NSIsIm9uQ2FuY2VsUHJvcCIsInVuZGVmaW5lZCIsInRoZW1lIiwidGhlbWVTZXR0aW5nIiwiY29sdW1ucyIsInQ2IiwiU3ltYm9sIiwiZm9yIiwiY29sb3JNb2R1bGVVbmF2YWlsYWJsZVJlYXNvbiIsInQ3Iiwic3ludGF4VGhlbWUiLCJzZXRQcmV2aWV3VGhlbWUiLCJzYXZlUHJldmlldyIsImNhbmNlbFByZXZpZXciLCJzeW50YXhIaWdobGlnaHRpbmdEaXNhYmxlZCIsIl90ZW1wIiwic2V0QXBwU3RhdGUiLCJzeW50YXhUb2dnbGVTaG9ydGN1dCIsInQ4IiwibmV3VmFsdWUiLCJwcmV2Iiwic2V0dGluZ3MiLCJ0OSIsImNvbnRleHQiLCJleGl0U3RhdGUiLCJfdGVtcDIiLCJ0MTAiLCJsYWJlbCIsInZhbHVlIiwiY29uc3QiLCJ0aGVtZU9wdGlvbnMiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJ0MTYiLCJzZXR0aW5nXzAiLCJ0MTciLCJ0MTgiLCJsZW5ndGgiLCJ0MTkiLCJ0MjAiLCJvbGRTdGFydCIsIm5ld1N0YXJ0Iiwib2xkTGluZXMiLCJuZXdMaW5lcyIsImxpbmVzIiwidDIxIiwidDIyIiwicHJvY2VzcyIsImVudiIsIkNMQVVERV9DT0RFX1NZTlRBWF9ISUdITElHSFQiLCJzb3VyY2UiLCJ0MjMiLCJ0MjQiLCJ0MjUiLCJjb250ZW50IiwidDI2IiwidDI3IiwidDI4IiwicGVuZGluZyIsImtleU5hbWUiLCJ0MjkiLCJ0MzAiLCJzIl0sInNvdXJjZXMiOlsiVGhlbWVQaWNrZXIudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MgfSBmcm9tICcuLi9ob29rcy91c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MuanMnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQge1xuICBCb3gsXG4gIFRleHQsXG4gIHVzZVByZXZpZXdUaGVtZSxcbiAgdXNlVGhlbWUsXG4gIHVzZVRoZW1lU2V0dGluZyxcbn0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlUmVnaXN0ZXJLZXliaW5kaW5nQ29udGV4dCB9IGZyb20gJy4uL2tleWJpbmRpbmdzL0tleWJpbmRpbmdDb250ZXh0LmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyB1c2VTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi9rZXliaW5kaW5ncy91c2VTaG9ydGN1dERpc3BsYXkuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSwgdXNlU2V0QXBwU3RhdGUgfSBmcm9tICcuLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IGdyYWNlZnVsU2h1dGRvd24gfSBmcm9tICcuLi91dGlscy9ncmFjZWZ1bFNodXRkb3duLmpzJ1xuaW1wb3J0IHsgdXBkYXRlU2V0dGluZ3NGb3JTb3VyY2UgfSBmcm9tICcuLi91dGlscy9zZXR0aW5ncy9zZXR0aW5ncy5qcydcbmltcG9ydCB0eXBlIHsgVGhlbWVTZXR0aW5nIH0gZnJvbSAnLi4vdXRpbHMvdGhlbWUuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuL0N1c3RvbVNlbGVjdC9pbmRleC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7XG4gIGdldENvbG9yTW9kdWxlVW5hdmFpbGFibGVSZWFzb24sXG4gIGdldFN5bnRheFRoZW1lLFxufSBmcm9tICcuL1N0cnVjdHVyZWREaWZmL2NvbG9yRGlmZi5qcydcbmltcG9ydCB7IFN0cnVjdHVyZWREaWZmIH0gZnJvbSAnLi9TdHJ1Y3R1cmVkRGlmZi5qcydcblxuZXhwb3J0IHR5cGUgVGhlbWVQaWNrZXJQcm9wcyA9IHtcbiAgb25UaGVtZVNlbGVjdDogKHNldHRpbmc6IFRoZW1lU2V0dGluZykgPT4gdm9pZFxuICBzaG93SW50cm9UZXh0PzogYm9vbGVhblxuICBoZWxwVGV4dD86IHN0cmluZ1xuICBzaG93SGVscFRleHRCZWxvdz86IGJvb2xlYW5cbiAgaGlkZUVzY1RvQ2FuY2VsPzogYm9vbGVhblxuICAvKiogU2tpcCBleGl0IGhhbmRsaW5nIHdoZW4gcnVubmluZyBpbiBhIGNvbnRleHQgdGhhdCBhbHJlYWR5IGhhcyBpdCAoZS5nLiwgb25ib2FyZGluZykgKi9cbiAgc2tpcEV4aXRIYW5kbGluZz86IGJvb2xlYW5cbiAgLyoqIENhbGxlZCB3aGVuIHRoZSB1c2VyIGNhbmNlbHMgKHByZXNzZXMgRXNjYXBlKS4gSWYgc2tpcEV4aXRIYW5kbGluZyBpcyB0cnVlIGFuZCB0aGlzIGlzIHByb3ZpZGVkLCBpdCB3aWxsIGJlIGNhbGxlZCBpbnN0ZWFkIG9mIGp1c3Qgc2F2aW5nIHRoZSBwcmV2aWV3LiAqL1xuICBvbkNhbmNlbD86ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFRoZW1lUGlja2VyKHtcbiAgb25UaGVtZVNlbGVjdCxcbiAgc2hvd0ludHJvVGV4dCA9IGZhbHNlLFxuICBoZWxwVGV4dCA9ICcnLFxuICBzaG93SGVscFRleHRCZWxvdyA9IGZhbHNlLFxuICBoaWRlRXNjVG9DYW5jZWwgPSBmYWxzZSxcbiAgc2tpcEV4aXRIYW5kbGluZyA9IGZhbHNlLFxuICBvbkNhbmNlbDogb25DYW5jZWxQcm9wLFxufTogVGhlbWVQaWNrZXJQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZV0gPSB1c2VUaGVtZSgpXG4gIGNvbnN0IHRoZW1lU2V0dGluZyA9IHVzZVRoZW1lU2V0dGluZygpXG4gIGNvbnN0IHsgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcbiAgY29uc3QgY29sb3JNb2R1bGVVbmF2YWlsYWJsZVJlYXNvbiA9IGdldENvbG9yTW9kdWxlVW5hdmFpbGFibGVSZWFzb24oKVxuICBjb25zdCBzeW50YXhUaGVtZSA9XG4gICAgY29sb3JNb2R1bGVVbmF2YWlsYWJsZVJlYXNvbiA9PT0gbnVsbCA/IGdldFN5bnRheFRoZW1lKHRoZW1lKSA6IG51bGxcbiAgY29uc3QgeyBzZXRQcmV2aWV3VGhlbWUsIHNhdmVQcmV2aWV3LCBjYW5jZWxQcmV2aWV3IH0gPSB1c2VQcmV2aWV3VGhlbWUoKVxuICBjb25zdCBzeW50YXhIaWdobGlnaHRpbmdEaXNhYmxlZCA9XG4gICAgdXNlQXBwU3RhdGUocyA9PiBzLnNldHRpbmdzLnN5bnRheEhpZ2hsaWdodGluZ0Rpc2FibGVkKSA/PyBmYWxzZVxuICBjb25zdCBzZXRBcHBTdGF0ZSA9IHVzZVNldEFwcFN0YXRlKClcblxuICAvLyBSZWdpc3RlciBUaGVtZVBpY2tlciBjb250ZXh0IHNvIGl0cyBrZXliaW5kaW5ncyB0YWtlIHByZWNlZGVuY2Ugb3ZlciBHbG9iYWxcbiAgdXNlUmVnaXN0ZXJLZXliaW5kaW5nQ29udGV4dCgnVGhlbWVQaWNrZXInKVxuXG4gIGNvbnN0IHN5bnRheFRvZ2dsZVNob3J0Y3V0ID0gdXNlU2hvcnRjdXREaXNwbGF5KFxuICAgICd0aGVtZTp0b2dnbGVTeW50YXhIaWdobGlnaHRpbmcnLFxuICAgICdUaGVtZVBpY2tlcicsXG4gICAgJ2N0cmwrdCcsXG4gIClcblxuICB1c2VLZXliaW5kaW5nKFxuICAgICd0aGVtZTp0b2dnbGVTeW50YXhIaWdobGlnaHRpbmcnLFxuICAgICgpID0+IHtcbiAgICAgIGlmIChjb2xvck1vZHVsZVVuYXZhaWxhYmxlUmVhc29uID09PSBudWxsKSB7XG4gICAgICAgIGNvbnN0IG5ld1ZhbHVlID0gIXN5bnRheEhpZ2hsaWdodGluZ0Rpc2FibGVkXG4gICAgICAgIHVwZGF0ZVNldHRpbmdzRm9yU291cmNlKCd1c2VyU2V0dGluZ3MnLCB7XG4gICAgICAgICAgc3ludGF4SGlnaGxpZ2h0aW5nRGlzYWJsZWQ6IG5ld1ZhbHVlLFxuICAgICAgICB9KVxuICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICBzZXR0aW5nczogeyAuLi5wcmV2LnNldHRpbmdzLCBzeW50YXhIaWdobGlnaHRpbmdEaXNhYmxlZDogbmV3VmFsdWUgfSxcbiAgICAgICAgfSkpXG4gICAgICB9XG4gICAgfSxcbiAgICB7IGNvbnRleHQ6ICdUaGVtZVBpY2tlcicgfSxcbiAgKVxuICAvLyBBbHdheXMgY2FsbCB0aGUgaG9vayB0byBmb2xsb3cgUmVhY3QgcnVsZXMsIGJ1dCBjb25kaXRpb25hbGx5IGFzc2lnbiB0aGUgZXhpdCBoYW5kbGVyXG4gIGNvbnN0IGV4aXRTdGF0ZSA9IHVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncyhcbiAgICBza2lwRXhpdEhhbmRsaW5nID8gKCkgPT4ge30gOiB1bmRlZmluZWQsXG4gIClcblxuICBjb25zdCB0aGVtZU9wdGlvbnM6IHsgbGFiZWw6IHN0cmluZzsgdmFsdWU6IFRoZW1lU2V0dGluZyB9W10gPSBbXG4gICAgLi4uKGZlYXR1cmUoJ0FVVE9fVEhFTUUnKVxuICAgICAgPyBbeyBsYWJlbDogJ0F1dG8gKG1hdGNoIHRlcm1pbmFsKScsIHZhbHVlOiAnYXV0bycgYXMgY29uc3QgfV1cbiAgICAgIDogW10pLFxuICAgIHsgbGFiZWw6ICdEYXJrIG1vZGUnLCB2YWx1ZTogJ2RhcmsnIH0sXG4gICAgeyBsYWJlbDogJ0xpZ2h0IG1vZGUnLCB2YWx1ZTogJ2xpZ2h0JyB9LFxuICAgIHtcbiAgICAgIGxhYmVsOiAnRGFyayBtb2RlIChjb2xvcmJsaW5kLWZyaWVuZGx5KScsXG4gICAgICB2YWx1ZTogJ2RhcmstZGFsdG9uaXplZCcsXG4gICAgfSxcbiAgICB7XG4gICAgICBsYWJlbDogJ0xpZ2h0IG1vZGUgKGNvbG9yYmxpbmQtZnJpZW5kbHkpJyxcbiAgICAgIHZhbHVlOiAnbGlnaHQtZGFsdG9uaXplZCcsXG4gICAgfSxcbiAgICB7XG4gICAgICBsYWJlbDogJ0RhcmsgbW9kZSAoQU5TSSBjb2xvcnMgb25seSknLFxuICAgICAgdmFsdWU6ICdkYXJrLWFuc2knLFxuICAgIH0sXG4gICAge1xuICAgICAgbGFiZWw6ICdMaWdodCBtb2RlIChBTlNJIGNvbG9ycyBvbmx5KScsXG4gICAgICB2YWx1ZTogJ2xpZ2h0LWFuc2knLFxuICAgIH0sXG4gIF1cblxuICBjb25zdCBjb250ZW50ID0gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICB7c2hvd0ludHJvVGV4dCA/IChcbiAgICAgICAgICA8VGV4dD5MZXQmYXBvcztzIGdldCBzdGFydGVkLjwvVGV4dD5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8VGV4dCBib2xkIGNvbG9yPVwicGVybWlzc2lvblwiPlxuICAgICAgICAgICAgVGhlbWVcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0IGJvbGQ+XG4gICAgICAgICAgICBDaG9vc2UgdGhlIHRleHQgc3R5bGUgdGhhdCBsb29rcyBiZXN0IHdpdGggeW91ciB0ZXJtaW5hbFxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICB7aGVscFRleHQgJiYgIXNob3dIZWxwVGV4dEJlbG93ICYmIDxUZXh0IGRpbUNvbG9yPntoZWxwVGV4dH08L1RleHQ+fVxuICAgICAgICA8L0JveD5cbiAgICAgICAgPFNlbGVjdFxuICAgICAgICAgIG9wdGlvbnM9e3RoZW1lT3B0aW9uc31cbiAgICAgICAgICBvbkZvY3VzPXtzZXR0aW5nID0+IHtcbiAgICAgICAgICAgIHNldFByZXZpZXdUaGVtZShzZXR0aW5nIGFzIFRoZW1lU2V0dGluZylcbiAgICAgICAgICB9fVxuICAgICAgICAgIG9uQ2hhbmdlPXsoc2V0dGluZzogc3RyaW5nKSA9PiB7XG4gICAgICAgICAgICBzYXZlUHJldmlldygpXG4gICAgICAgICAgICBvblRoZW1lU2VsZWN0KHNldHRpbmcgYXMgVGhlbWVTZXR0aW5nKVxuICAgICAgICAgIH19XG4gICAgICAgICAgb25DYW5jZWw9e1xuICAgICAgICAgICAgc2tpcEV4aXRIYW5kbGluZ1xuICAgICAgICAgICAgICA/ICgpID0+IHtcbiAgICAgICAgICAgICAgICAgIGNhbmNlbFByZXZpZXcoKVxuICAgICAgICAgICAgICAgICAgb25DYW5jZWxQcm9wPy4oKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgOiBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICBjYW5jZWxQcmV2aWV3KClcbiAgICAgICAgICAgICAgICAgIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHZpc2libGVPcHRpb25Db3VudD17dGhlbWVPcHRpb25zLmxlbmd0aH1cbiAgICAgICAgICBkZWZhdWx0VmFsdWU9e3RoZW1lU2V0dGluZ31cbiAgICAgICAgICBkZWZhdWx0Rm9jdXNWYWx1ZT17dGhlbWVTZXR0aW5nfVxuICAgICAgICAvPlxuICAgICAgPC9Cb3g+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiB3aWR0aD1cIjEwMCVcIj5cbiAgICAgICAgPEJveFxuICAgICAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgICAgIGJvcmRlclRvcFxuICAgICAgICAgIGJvcmRlckJvdHRvbVxuICAgICAgICAgIGJvcmRlckxlZnQ9e2ZhbHNlfVxuICAgICAgICAgIGJvcmRlclJpZ2h0PXtmYWxzZX1cbiAgICAgICAgICBib3JkZXJTdHlsZT1cImRhc2hlZFwiXG4gICAgICAgICAgYm9yZGVyQ29sb3I9XCJzdWJ0bGVcIlxuICAgICAgICA+XG4gICAgICAgICAgPFN0cnVjdHVyZWREaWZmXG4gICAgICAgICAgICBwYXRjaD17e1xuICAgICAgICAgICAgICBvbGRTdGFydDogMSxcbiAgICAgICAgICAgICAgbmV3U3RhcnQ6IDEsXG4gICAgICAgICAgICAgIG9sZExpbmVzOiAzLFxuICAgICAgICAgICAgICBuZXdMaW5lczogMyxcbiAgICAgICAgICAgICAgbGluZXM6IFtcbiAgICAgICAgICAgICAgICAnIGZ1bmN0aW9uIGdyZWV0KCkgeycsXG4gICAgICAgICAgICAgICAgJy0gIGNvbnNvbGUubG9nKFwiSGVsbG8sIFdvcmxkIVwiKTsnLFxuICAgICAgICAgICAgICAgICcrICBjb25zb2xlLmxvZyhcIkhlbGxvLCBDbGF1ZGUhXCIpOycsXG4gICAgICAgICAgICAgICAgJyB9JyxcbiAgICAgICAgICAgICAgXSxcbiAgICAgICAgICAgIH19XG4gICAgICAgICAgICBkaW09e2ZhbHNlfVxuICAgICAgICAgICAgZmlsZVBhdGg9XCJkZW1vLmpzXCJcbiAgICAgICAgICAgIGZpcnN0TGluZT17bnVsbH1cbiAgICAgICAgICAgIHdpZHRoPXtjb2x1bW5zfVxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICB7JyAnfVxuICAgICAgICAgIHtjb2xvck1vZHVsZVVuYXZhaWxhYmxlUmVhc29uID09PSAnZW52J1xuICAgICAgICAgICAgPyBgU3ludGF4IGhpZ2hsaWdodGluZyBkaXNhYmxlZCAodmlhIENMQVVERV9DT0RFX1NZTlRBWF9ISUdITElHSFQ9JHtwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9TWU5UQVhfSElHSExJR0hUfSlgXG4gICAgICAgICAgICA6IHN5bnRheEhpZ2hsaWdodGluZ0Rpc2FibGVkXG4gICAgICAgICAgICAgID8gYFN5bnRheCBoaWdobGlnaHRpbmcgZGlzYWJsZWQgKCR7c3ludGF4VG9nZ2xlU2hvcnRjdXR9IHRvIGVuYWJsZSlgXG4gICAgICAgICAgICAgIDogc3ludGF4VGhlbWVcbiAgICAgICAgICAgICAgICA/IGBTeW50YXggdGhlbWU6ICR7c3ludGF4VGhlbWUudGhlbWV9JHtzeW50YXhUaGVtZS5zb3VyY2UgPyBgIChmcm9tICR7c3ludGF4VGhlbWUuc291cmNlfSlgIDogJyd9ICgke3N5bnRheFRvZ2dsZVNob3J0Y3V0fSB0byBkaXNhYmxlKWBcbiAgICAgICAgICAgICAgICA6IGBTeW50YXggaGlnaGxpZ2h0aW5nIGVuYWJsZWQgKCR7c3ludGF4VG9nZ2xlU2hvcnRjdXR9IHRvIGRpc2FibGUpYH1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgPC9Cb3g+XG4gIClcblxuICAvLyBPbmx5IHdyYXAgaW4gYSBib3ggd2hlbiBub3QgaW4gb25ib2FyZGluZ1xuICBpZiAoIXNob3dJbnRyb1RleHQpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPD5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+e2NvbnRlbnR9PC9Cb3g+XG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICB7c2hvd0hlbHBUZXh0QmVsb3cgJiYgaGVscFRleHQgJiYgKFxuICAgICAgICAgICAgPEJveCBtYXJnaW5MZWZ0PXszfT5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+e2hlbHBUZXh0fTwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICl9XG4gICAgICAgICAgeyFoaWRlRXNjVG9DYW5jZWwgJiYgKFxuICAgICAgICAgICAgPEJveD5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3IgaXRhbGljPlxuICAgICAgICAgICAgICAgIHtleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICAgICAgICAgIDw+UHJlc3Mge2V4aXRTdGF0ZS5rZXlOYW1lfSBhZ2FpbiB0byBleGl0PC8+XG4gICAgICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVudGVyXCIgYWN0aW9uPVwic2VsZWN0XCIgLz5cbiAgICAgICAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiRXNjXCIgYWN0aW9uPVwiY2FuY2VsXCIgLz5cbiAgICAgICAgICAgICAgICAgIDwvQnlsaW5lPlxuICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICl9XG4gICAgICAg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 \ No newline at end of file diff --git a/components/ThinkingToggle.tsx b/components/ThinkingToggle.tsx new file mode 100644 index 0000000..a7b7a1b --- /dev/null +++ b/components/ThinkingToggle.tsx @@ -0,0 +1,153 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useState } from 'react'; +import { useExitOnCtrlCDWithKeybindings } from 'src/hooks/useExitOnCtrlCDWithKeybindings.js'; +import { Box, Text } from '../ink.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js'; +import { Select } from './CustomSelect/index.js'; +import { Byline } from './design-system/Byline.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +import { Pane } from './design-system/Pane.js'; +export type Props = { + currentValue: boolean; + onSelect: (enabled: boolean) => void; + onCancel?: () => void; + isMidConversation?: boolean; +}; +export function ThinkingToggle(t0) { + const $ = _c(27); + const { + currentValue, + onSelect, + onCancel, + isMidConversation + } = t0; + const exitState = useExitOnCtrlCDWithKeybindings(); + const [confirmationPending, setConfirmationPending] = useState(null); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = [{ + value: "true", + label: "Enabled", + description: "Claude will think before responding" + }, { + value: "false", + label: "Disabled", + description: "Claude will respond without extended thinking" + }]; + $[0] = t1; + } else { + t1 = $[0]; + } + const options = t1; + let t2; + if ($[1] !== confirmationPending || $[2] !== onCancel) { + t2 = () => { + if (confirmationPending !== null) { + setConfirmationPending(null); + } else { + onCancel?.(); + } + }; + $[1] = confirmationPending; + $[2] = onCancel; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t3 = { + context: "Confirmation" + }; + $[4] = t3; + } else { + t3 = $[4]; + } + useKeybinding("confirm:no", t2, t3); + let t4; + if ($[5] !== confirmationPending || $[6] !== onSelect) { + t4 = () => { + if (confirmationPending !== null) { + onSelect(confirmationPending); + } + }; + $[5] = confirmationPending; + $[6] = onSelect; + $[7] = t4; + } else { + t4 = $[7]; + } + const t5 = confirmationPending !== null; + let t6; + if ($[8] !== t5) { + t6 = { + context: "Confirmation", + isActive: t5 + }; + $[8] = t5; + $[9] = t6; + } else { + t6 = $[9]; + } + useKeybinding("confirm:yes", t4, t6); + let t7; + if ($[10] !== currentValue || $[11] !== isMidConversation || $[12] !== onSelect) { + t7 = function handleSelectChange(value) { + const selected = value === "true"; + if (isMidConversation && selected !== currentValue) { + setConfirmationPending(selected); + } else { + onSelect(selected); + } + }; + $[10] = currentValue; + $[11] = isMidConversation; + $[12] = onSelect; + $[13] = t7; + } else { + t7 = $[13]; + } + const handleSelectChange = t7; + let t8; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t8 = Toggle thinking modeEnable or disable thinking for this session.; + $[14] = t8; + } else { + t8 = $[14]; + } + let t9; + if ($[15] !== confirmationPending || $[16] !== currentValue || $[17] !== handleSelectChange || $[18] !== onCancel) { + t9 = {t8}{confirmationPending !== null ? Changing thinking mode mid-conversation will increase latency and may reduce quality. For best results, set this at the start of a session.Do you want to proceed? : onChange(value_0 as 'enable_all' | 'exit')} onCancel={() => onChange("exit")} />; + $[25] = onChange; + $[26] = t21; + } else { + t21 = $[26]; + } + let t22; + if ($[27] !== exitState.keyName || $[28] !== exitState.pending) { + t22 = {exitState.pending ? <>Press {exitState.keyName} again to exit : <>Enter to confirm · Esc to cancel}; + $[27] = exitState.keyName; + $[28] = exitState.pending; + $[29] = t22; + } else { + t22 = $[29]; + } + let t23; + if ($[30] !== t21 || $[31] !== t22) { + t23 = {t16}{t17}{t18}{t19}{t21}{t22}; + $[30] = t21; + $[31] = t22; + $[32] = t23; + } else { + t23 = $[32]; + } + return t23; +} +function _temp7() { + gracefulShutdownSync(0); +} +function _temp6() { + return gracefulShutdownSync(1); +} +function _temp5(current) { + return { + ...current, + hasTrustDialogAccepted: true + }; +} +function _temp4(command_0) { + return command_0.type === "prompt" && (command_0.loadedFrom === "skills" || command_0.loadedFrom === "plugin") && (command_0.source === "projectSettings" || command_0.source === "localSettings" || command_0.source === "plugin") && command_0.allowedTools?.some(_temp3); +} +function _temp3(tool_0) { + return tool_0 === BASH_TOOL_NAME || tool_0.startsWith(BASH_TOOL_NAME + "("); +} +function _temp2(command) { + return command.type === "prompt" && command.loadedFrom === "commands_DEPRECATED" && (command.source === "projectSettings" || command.source === "localSettings") && command.allowedTools?.some(_temp); +} +function _temp(tool) { + return tool === BASH_TOOL_NAME || tool.startsWith(BASH_TOOL_NAME + "("); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJob21lZGlyIiwiUmVhY3QiLCJsb2dFdmVudCIsInNldFNlc3Npb25UcnVzdEFjY2VwdGVkIiwiQ29tbWFuZCIsInVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncyIsIkJveCIsIkxpbmsiLCJUZXh0IiwidXNlS2V5YmluZGluZyIsImdldE1jcENvbmZpZ3NCeVNjb3BlIiwiQkFTSF9UT09MX05BTUUiLCJjaGVja0hhc1RydXN0RGlhbG9nQWNjZXB0ZWQiLCJzYXZlQ3VycmVudFByb2plY3RDb25maWciLCJnZXRDd2QiLCJnZXRGc0ltcGxlbWVudGF0aW9uIiwiZ3JhY2VmdWxTaHV0ZG93blN5bmMiLCJTZWxlY3QiLCJQZXJtaXNzaW9uRGlhbG9nIiwiZ2V0QXBpS2V5SGVscGVyU291cmNlcyIsImdldEF3c0NvbW1hbmRzU291cmNlcyIsImdldEJhc2hQZXJtaXNzaW9uU291cmNlcyIsImdldERhbmdlcm91c0VudlZhcnNTb3VyY2VzIiwiZ2V0R2NwQ29tbWFuZHNTb3VyY2VzIiwiZ2V0SG9va3NTb3VyY2VzIiwiZ2V0T3RlbEhlYWRlcnNIZWxwZXJTb3VyY2VzIiwiUHJvcHMiLCJvbkRvbmUiLCJjb21tYW5kcyIsIlRydXN0RGlhbG9nIiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsInNlcnZlcnMiLCJwcm9qZWN0U2VydmVycyIsInQyIiwiT2JqZWN0Iiwia2V5cyIsImhhc01jcFNlcnZlcnMiLCJsZW5ndGgiLCJ0MyIsImhvb2tzU2V0dGluZ1NvdXJjZXMiLCJoYXNIb29rcyIsInQ0IiwiYmFzaFNldHRpbmdTb3VyY2VzIiwidDUiLCJhcGlLZXlIZWxwZXJTb3VyY2VzIiwiaGFzQXBpS2V5SGVscGVyIiwidDYiLCJhd3NDb21tYW5kc1NvdXJjZXMiLCJoYXNBd3NDb21tYW5kcyIsInQ3IiwiZ2NwQ29tbWFuZHNTb3VyY2VzIiwiaGFzR2NwQ29tbWFuZHMiLCJ0OCIsIm90ZWxIZWFkZXJzSGVscGVyU291cmNlcyIsImhhc090ZWxIZWFkZXJzSGVscGVyIiwidDkiLCJkYW5nZXJvdXNFbnZWYXJzU291cmNlcyIsImhhc0Rhbmdlcm91c0VudlZhcnMiLCJ0MTAiLCJzb21lIiwiX3RlbXAyIiwiaGFzU2xhc2hDb21tYW5kQmFzaCIsInQxMSIsIl90ZW1wNCIsImhhc1NraWxsc0Jhc2giLCJoYXNBbnlCYXNoRXhlY3V0aW9uIiwiaGFzVHJ1c3REaWFsb2dBY2NlcHRlZCIsInQxMiIsInQxMyIsImlzSG9tZURpciIsImhhc0Jhc2hFeGVjdXRpb24iLCJ1c2VFZmZlY3QiLCJ0MTQiLCJvbkNoYW5nZSIsInZhbHVlIiwiaXNIb21lRGlyXzAiLCJfdGVtcDUiLCJleGl0U3RhdGUiLCJfdGVtcDYiLCJ0MTUiLCJjb250ZXh0IiwiX3RlbXA3Iiwic2V0VGltZW91dCIsInQxNiIsInQxNyIsInQxOCIsImN3ZCIsInQxOSIsInQyMCIsImxhYmVsIiwidDIxIiwidmFsdWVfMCIsInQyMiIsImtleU5hbWUiLCJwZW5kaW5nIiwidDIzIiwiY3VycmVudCIsImNvbW1hbmRfMCIsImNvbW1hbmQiLCJ0eXBlIiwibG9hZGVkRnJvbSIsInNvdXJjZSIsImFsbG93ZWRUb29scyIsIl90ZW1wMyIsInRvb2xfMCIsInRvb2wiLCJzdGFydHNXaXRoIiwiX3RlbXAiXSwic291cmNlcyI6WyJUcnVzdERpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgaG9tZWRpciB9IGZyb20gJ29zJ1xuaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgbG9nRXZlbnQgfSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgc2V0U2Vzc2lvblRydXN0QWNjZXB0ZWQgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IENvbW1hbmQgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IHVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncyB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncy5qcydcbmltcG9ydCB7IEJveCwgTGluaywgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgZ2V0TWNwQ29uZmlnc0J5U2NvcGUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvY29uZmlnLmpzJ1xuaW1wb3J0IHsgQkFTSF9UT09MX05BTUUgfSBmcm9tICcuLi8uLi90b29scy9CYXNoVG9vbC90b29sTmFtZS5qcydcbmltcG9ydCB7XG4gIGNoZWNrSGFzVHJ1c3REaWFsb2dBY2NlcHRlZCxcbiAgc2F2ZUN1cnJlbnRQcm9qZWN0Q29uZmlnLFxufSBmcm9tICcuLi8uLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgeyBnZXRDd2QgfSBmcm9tICcuLi8uLi91dGlscy9jd2QuanMnXG5pbXBvcnQgeyBnZXRGc0ltcGxlbWVudGF0aW9uIH0gZnJvbSAnLi4vLi4vdXRpbHMvZnNPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHsgZ3JhY2VmdWxTaHV0ZG93blN5bmMgfSBmcm9tICcuLi8uLi91dGlscy9ncmFjZWZ1bFNodXRkb3duLmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi4vQ3VzdG9tU2VsZWN0L2luZGV4LmpzJ1xuaW1wb3J0IHsgUGVybWlzc2lvbkRpYWxvZyB9IGZyb20gJy4uL3Blcm1pc3Npb25zL1Blcm1pc3Npb25EaWFsb2cuanMnXG5pbXBvcnQge1xuICBnZXRBcGlLZXlIZWxwZXJTb3VyY2VzLFxuICBnZXRBd3NDb21tYW5kc1NvdXJjZXMsXG4gIGdldEJhc2hQZXJtaXNzaW9uU291cmNlcyxcbiAgZ2V0RGFuZ2Vyb3VzRW52VmFyc1NvdXJjZXMsXG4gIGdldEdjcENvbW1hbmRzU291cmNlcyxcbiAgZ2V0SG9va3NTb3VyY2VzLFxuICBnZXRPdGVsSGVhZGVyc0hlbHBlclNvdXJjZXMsXG59IGZyb20gJy4vdXRpbHMuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG9uRG9uZSgpOiB2b2lkXG4gIGNvbW1hbmRzPzogQ29tbWFuZFtdXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBUcnVzdERpYWxvZyh7IG9uRG9uZSwgY29tbWFuZHMgfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB7IHNlcnZlcnM6IHByb2plY3RTZXJ2ZXJzIH0gPSBnZXRNY3BDb25maWdzQnlTY29wZSgncHJvamVjdCcpXG5cbiAgLy8gSW4gYWxsIGNhc2VzLCB3ZSBnZW5lcmFsbHkgY2hlY2sgb25seSB0aGUgcHJvamVjdC1sZXZlbCBhbmRcbiAgLy8gcHJvamVjdC1sb2NhbC1sZXZlbCBzZXR0aW5ncywgd2hpY2ggd2UgYXNzdW1lIHRoYXQgdXNlcnMgZG8gbm90IGNvbmZpZ3VyZVxuICAvLyBkaXJlY3RseSBjb21wYXJlZCB0byB1c2VyLWxldmVsIHNldHRpbmdzLlxuXG4gIC8vIENoZWNrIGZvciBNQ1BzXG4gIGNvbnN0IGhhc01jcFNlcnZlcnMgPSBPYmplY3Qua2V5cyhwcm9qZWN0U2VydmVycykubGVuZ3RoID4gMFxuICAvLyBDaGVjayBmb3IgaG9va3NcbiAgY29uc3QgaG9va3NTZXR0aW5nU291cmNlcyA9IGdldEhvb2tzU291cmNlcygpXG4gIGNvbnN0IGhhc0hvb2tzID0gaG9va3NTZXR0aW5nU291cmNlcy5sZW5ndGggPiAwXG4gIC8vIENoZWNrIHdoZXRoZXIgY29kZSBleGVjdXRpb24gaXMgYWxsb3dlZCBpbiBwZXJtaXNzaW9ucyBhbmQgc2xhc2ggY29tbWFuZHNcbiAgY29uc3QgYmFzaFNldHRpbmdTb3VyY2VzID0gZ2V0QmFzaFBlcm1pc3Npb25Tb3VyY2VzKClcbiAgLy8gQ2hlY2sgZm9yIGFwaUtleUhlbHBlciB3aGljaCBleGVjdXRlcyBhcmJpdHJhcnkgY29tbWFuZHNcbiAgY29uc3QgYXBpS2V5SGVscGVyU291cmNlcyA9IGdldEFwaUtleUhlbHBlclNvdXJjZXMoKVxuICBjb25zdCBoYXNBcGlLZXlIZWxwZXIgPSBhcGlLZXlIZWxwZXJTb3VyY2VzLmxlbmd0aCA+IDBcbiAgLy8gQ2hlY2sgZm9yIEFXUyBjb21tYW5kcyB3aGljaCBleGVjdXRlIGFyYml0cmFyeSBjb21tYW5kc1xuICBjb25zdCBhd3NDb21tYW5kc1NvdXJjZXMgPSBnZXRBd3NDb21tYW5kc1NvdXJjZXMoKVxuICBjb25zdCBoYXNBd3NDb21tYW5kcyA9IGF3c0NvbW1hbmRzU291cmNlcy5sZW5ndGggPiAwXG4gIC8vIENoZWNrIGZvciBHQ1AgY29tbWFuZHMgd2hpY2ggZXhlY3V0ZSBhcmJpdHJhcnkgY29tbWFuZHNcbiAgY29uc3QgZ2NwQ29tbWFuZHNTb3VyY2VzID0gZ2V0R2NwQ29tbWFuZHNTb3VyY2VzKClcbiAgY29uc3QgaGFzR2NwQ29tbWFuZHMgPSBnY3BDb21tYW5kc1NvdXJjZXMubGVuZ3RoID4gMFxuICAvLyBDaGVjayBmb3Igb3RlbEhlYWRlcnNIZWxwZXIgd2hpY2ggZXhlY3V0ZXMgYXJiaXRyYXJ5IGNvbW1hbmRzXG4gIGNvbnN0IG90ZWxIZWFkZXJzSGVscGVyU291cmNlcyA9IGdldE90ZWxIZWFkZXJzSGVscGVyU291cmNlcygpXG4gIGNvbnN0IGhhc090ZWxIZWFkZXJzSGVscGVyID0gb3RlbEhlYWRlcnNIZWxwZXJTb3VyY2VzLmxlbmd0aCA+IDBcbiAgLy8gQ2hlY2sgZm9yIGRhbmdlcm91cyBlbnZpcm9ubWVudCB2YXJpYWJsZXMgKG5vdCBpbiBTQUZFX0VOVl9WQVJTKVxuICBjb25zdCBkYW5nZXJvdXNFbnZWYXJzU291cmNlcyA9IGdldERhbmdlcm91c0VudlZhcnNTb3VyY2VzKClcbiAgY29uc3QgaGFzRGFuZ2Vyb3VzRW52VmFycyA9IGRhbmdlcm91c0VudlZhcnNTb3VyY2VzLmxlbmd0aCA+IDBcblxuICBjb25zdCBoYXNTbGFzaENvbW1hbmRCYXNoID1cbiAgICBjb21tYW5kcz8uc29tZShcbiAgICAgIGNvbW1hbmQgPT5cbiAgICAgICAgY29tbWFuZC50eXBlID09PSAncHJvbXB0JyAmJlxuICAgICAgICBjb21tYW5kLmxvYWRlZEZyb20gPT09ICdjb21tYW5kc19ERVBSRUNBVEVEJyAmJlxuICAgICAgICAoY29tbWFuZC5zb3VyY2UgPT09ICdwcm9qZWN0U2V0dGluZ3MnIHx8XG4gICAgICAgICAgY29tbWFuZC5zb3VyY2UgPT09ICdsb2NhbFNldHRpbmdzJykgJiZcbiAgICAgICAgY29tbWFuZC5hbGxvd2VkVG9vbHM/LnNvbWUoXG4gICAgICAgICAgKHRvb2w6IHN0cmluZykgPT5cbiAgICAgICAgICAgIHRvb2wgPT09IEJBU0hfVE9PTF9OQU1FIHx8IHRvb2wuc3RhcnRzV2l0aChCQVNIX1RPT0xfTkFNRSArICcoJyksXG4gICAgICAgICksXG4gICAgKSA/PyBmYWxzZVxuXG4gIGNvbnN0IGhhc1NraWxsc0Jhc2ggPVxuICAgIGNvbW1hbmRzPy5zb21lKFxuICAgICAgY29tbWFuZCA9PlxuICAgICAgICBjb21tYW5kLnR5cGUgPT09ICdwcm9tcHQnICYmXG4gICAgICAgIChjb21tYW5kLmxvYWRlZEZyb20gPT09ICdza2lsbHMnIHx8IGNvbW1hbmQubG9hZGVkRnJvbSA9PT0gJ3BsdWdpbicpICYmXG4gICAgICAgIChjb21tYW5kLnNvdXJjZSA9PT0gJ3Byb2plY3RTZXR0aW5ncycgfHxcbiAgICAgICAgICBjb21tYW5kLnNvdXJjZSA9PT0gJ2xvY2FsU2V0dGluZ3MnIHx8XG4gICAgICAgICAgY29tbWFuZC5zb3VyY2UgPT09ICdwbHVnaW4nKSAmJlxuICAgICAgICBjb21tYW5kLmFsbG93ZWRUb29scz8uc29tZShcbiAgICAgICAgICAodG9vbDogc3RyaW5nKSA9PlxuICAgICAgICAgICAgdG9vbCA9PT0gQkFTSF9UT09MX05BTUUgfHwgdG9vbC5zdGFydHNXaXRoKEJBU0hfVE9PTF9OQU1FICsgJygnKSxcbiAgICAgICAgKSxcbiAgICApID8/IGZhbHNlXG5cbiAgY29uc3QgaGFzQW55QmFzaEV4ZWN1dGlvbiA9XG4gICAgYmFzaFNldHRpbmdTb3VyY2VzLmxlbmd0aCA+IDAgfHwgaGFzU2xhc2hDb21tYW5kQmFzaCB8fCBoYXNTa2lsbHNCYXNoXG5cbiAgY29uc3QgaGFzVHJ1c3REaWFsb2dBY2NlcHRlZCA9IGNoZWNrSGFzVHJ1c3REaWFsb2dBY2NlcHRlZCgpXG5cbiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHtcbiAgICBjb25zdCBpc0hvbWVEaXIgPSBob21lZGlyKCkgPT09IGdldEN3ZCgpXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X3RydXN0X2RpYWxvZ19zaG93bicsIHtcbiAgICAgIGlzSG9tZURpcixcbiAgICAgIGhhc01jcFNlcnZlcnMsXG4gICAgICBoYXNIb29rcyxcbiAgICAgIGhhc0Jhc2hFeGVjdXRpb246IGhhc0FueUJhc2hFeGVjdXRpb24sXG4gICAgICBoYXNBcGlLZXlIZWxwZXIsXG4gICAgICBoYXNBd3NDb21tYW5kcyxcbiAgICAgIGhhc0djcENvbW1hbmRzLFxuICAgICAgaGFzT3RlbEhlYWRlcnNIZWxwZXIsXG4gICAgICBoYXNEYW5nZXJvdXNFbnZWYXJzLFxuICAgIH0pXG4gIH0sIFtcbiAgICBoYXNNY3BTZXJ2ZXJzLFxuICAgIGhhc0hvb2tzLFxuICAgIGhhc0FueUJhc2hFeGVjdXRpb24sXG4gICAgaGFzQXBpS2V5SGVscGVyLFxuICAgIGhhc0F3c0NvbW1hbmRzLFxuICAgIGhhc0djcENvbW1hbmRzLFxuICAgIGhhc090ZWxIZWFkZXJzSGVscGVyLFxuICAgIGhhc0Rhbmdlcm91c0VudlZhcnMsXG4gIF0pXG5cbiAgZnVuY3Rpb24gb25DaGFuZ2UodmFsdWU6ICdlbmFibGVfYWxsJyB8ICdleGl0Jykge1xuICAgIGlmICh2YWx1ZSA9PT0gJ2V4aXQnKSB7XG4gICAgICBncmFjZWZ1bFNodXRkb3duU3luYygxKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3QgaXNIb21lRGlyID0gaG9tZWRpcigpID09PSBnZXRDd2QoKVxuXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X3RydXN0X2RpYWxvZ19hY2NlcHQnLCB7XG4gICAgICBpc0hvbWVEaXIsXG4gICAgICBoYXNNY3BTZXJ2ZXJzLFxuICAgICAgaGFzSG9va3MsXG4gICAgICBoYXNCYXNoRXhlY3V0aW9uOiBoYXNBbnlCYXNoRXhlY3V0aW9uLFxuICAgICAgaGFzQXBpS2V5SGVscGVyLFxuICAgICAgaGFzQXdzQ29tbWFuZHMsXG4gICAgICBoYXNHY3BDb21tYW5kcyxcbiAgICAgIGhhc090ZWxIZWFkZXJzSGVscGVyLFxuICAgICAgaGFzRGFuZ2Vyb3VzRW52VmFycyxcbiAgICB9KVxuXG4gICAgaWYgKGlzSG9tZURpcikge1xuICAgICAgLy8gRm9yIGhvbWUgZGlyZWN0b3J5LCBzdG9yZSB0cnVzdCBpbiBzZXNzaW9uIG1lbW9yeSBvbmx5IChub3QgcGVyc2lzdGVkIHRvIGRpc2spXG4gICAgICAvLyBUaGlzIGFsbG93cyBob29rcyBhbmQgb3RoZXIgdHJ1c3QtcmVxdWlyaW5nIGZlYXR1cmVzIHRvIHdvcmsgZHVyaW5nIHRoaXMgc2Vzc2lvblxuICAgICAgLy8gd2hpbGUgcHJlc2VydmluZyB0aGUgc2VjdXJpdHkgaW50ZW50IG9mIG5vdCBwZXJtYW5lbnRseSB0cnVzdGluZyBob21lIGRpclxuICAgICAgc2V0U2Vzc2lvblRydXN0QWNjZXB0ZWQodHJ1ZSlcbiAgICB9IGVsc2Uge1xuICAgICAgc2F2ZUN1cnJlbnRQcm9qZWN0Q29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAgICAgLi4uY3VycmVudCxcbiAgICAgICAgaGFzVHJ1c3REaWFsb2dBY2NlcHRlZDogdHJ1ZSxcbiAgICAgIH0pKVxuICAgIH1cblxuICAgIC8vIERvIE5PVCB3cml0ZSBNQ1Agc2VydmVyIHNldHRpbmdzIGhlcmUuIGhhbmRsZU1jcGpzb25TZXJ2ZXJBcHByb3ZhbHMgaW5cbiAgICAvLyBpbnRlcmFjdGl2ZUhlbHBlcnMudHN4IHJ1bnMgcmlnaHQgYWZ0ZXIgdGhpcyBkaWFsb2cgYW5kIHNob3dzIHRoZSBwZXItc2VydmVyIGFwcHJvdmFsXG4gICAgLy8gVUkuIFdyaXRpbmcgZW5hYmxlZE1jcGpzb25TZXJ2ZXJzL2VuYWJsZUFsbFByb2plY3RNY3BTZXJ2ZXJzIGhlcmUgd291bGRcbiAgICAvLyBtYXJrIGV2ZXJ5IHNlcnZlciAnYXBwcm92ZWQnIGFuZCBzaWxlbnRseSBza2lwIHRoYXQgZGlhbG9nLiBTZWUgIzE1NTU4LlxuXG4gICAgb25Eb25lKClcbiAgfVxuXG4gIC8vIERlZmF1bHQgb25FeGl0IGlzIHVzZUFwcCgpLmV4aXQoKSDihpIgSW5rLnVubW91bnQoKSwgd2hpY2ggdGVhcnMgZG93biB0aGVcbiAgLy8gUmVhY3QgdHJlZSBidXQgbmV2ZXIgY2FsbHMgb25Eb25lKCkuIHNob3dTZXR1cFNjcmVlbnMoKSBpblxuICAvLyBpbnRlcmFjdGl2ZUhlbHBlcnMudHN4IGF3YWl0cyBhIFByb21pc2UgdGhhdCBvbmx5IHJlc29sdmVzIHZpYSBvbkRvbmUsXG4gIC8vIHNvIHRoZSBkZWZhdWx0IHdvdWxkIGhhbmcgdGhlIGF3YWl0IGZvcmV2ZXIuIFdpdGgga2V5YmluZGluZ1xuICAvLyBjdXN0b21pemF0aW9uIGVuYWJsZWQsIHRoZSBjaG9raWRhciB3YXRjaGVyIChwZXJzaXN0ZW50OiB0cnVlKSBrZWVwcyB0aGVcbiAgLy8gZXZlbnQgbG9vcCBhbGl2ZSBhbmQgdGhlIHByb2Nlc3MgZnJlZXplcy4gRXhwbGljaXRseSBleGl0IDEgbGlrZSBcIk5vXCIuXG4gIGNvbnN0IGV4aXRTdGF0ZSA9IHVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncygoKSA9PlxuICAgIGdyYWNlZnVsU2h1dGRvd25TeW5jKDEpLFxuICApXG5cbiAgLy8gVXNlIGNvbmZpZ3VyYWJsZSBrZXliaW5kaW5nIGZvciBFU0MgdG8gY2FuY2VsL2V4aXRcbiAgdXNlS2V5YmluZGluZyhcbiAgICAnY29uZmlybTpubycsXG4gICAgKCkgPT4ge1xuICAgICAgZ3JhY2VmdWxTaHV0ZG93blN5bmMoMClcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicgfSxcbiAgKVxuXG4gIC8vIEF1dG9tYXRpY2FsbHkgcmVzb2x2ZSB0aGUgdHJ1c3QgZGlhbG9nIGlmIHRoZXJlIGlzIG5vdGhpbmcgdG8gYmUgc2hvd24uXG4gIGlmIChoYXNUcnVzdERpYWxvZ0FjY2VwdGVkKSB7XG4gICAgc2V0VGltZW91dChvbkRvbmUpXG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPFBlcm1pc3Npb25EaWFsb2dcbiAgICAgIGNvbG9yPVwid2FybmluZ1wiXG4gICAgICB0aXRsZUNvbG9yPVwid2FybmluZ1wiXG4gICAgICB0aXRsZT1cIkFjY2Vzc2luZyB3b3Jrc3BhY2U6XCJcbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9IHBhZGRpbmdUb3A9ezF9PlxuICAgICAgICA8VGV4dCBib2xkPntnZXRGc0ltcGxlbWVudGF0aW9uKCkuY3dkKCl9PC9UZXh0PlxuXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIFF1aWNrIHNhZmV0eSBjaGVjazogSXMgdGhpcyBhIHByb2plY3QgeW91IGNyZWF0ZWQgb3Igb25lIHlvdSB0cnVzdD9cbiAgICAgICAgICAoTGlrZSB5b3VyIG93biBjb2RlLCBhIHdlbGwta25vd24gb3BlbiBzb3VyY2UgcHJvamVjdCwgb3Igd29yayBmcm9tXG4gICAgICAgICAgeW91ciB0ZWFtKS4gSWYgbm90LCB0YWtlIGEgbW9tZW50IHRvIHJldmlldyB3aGF0e1wiJ1wifXMgaW4gdGhpcyBmb2xkZXJcbiAgICAgICAgICBmaXJzdC5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICBDbGF1ZGUgQ29kZXtcIidcIn1sbCBiZSBhYmxlIHRvIHJlYWQsIGVkaXQsIGFuZCBleGVjdXRlIGZpbGVzIGhlcmUuXG4gICAgICAgIDwvVGV4dD5cblxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICA8TGluayB1cmw9XCJodHRwczovL2NvZGUuY2xhdWRlLmNvbS9kb2NzL2VuL3NlY3VyaXR5XCI+XG4gICAgICAgICAgICBTZWN1cml0eSBndWlkZVxuICAgICAgICAgIDwvTGluaz5cbiAgICAgICAgPC9UZXh0PlxuXG4gICAgICAgIDxTZWxlY3RcbiAgICAgICAgICBvcHRpb25zPXtbXG4gICAgICAgICAgICB7IGxhYmVsOiAnWWVzLCBJIHRydXN0IHRoaXMgZm9sZGVyJywgdmFsdWU6ICdlbmFibGVfYWxsJyB9LFxuICAgICAgICAgICAgeyBsYWJlbDogJ05vLCBleGl0JywgdmFsdWU6ICdleGl0JyB9LFxuICAgICAgICAgIF19XG4gICAgICAgICAgb25DaGFuZ2U9e3ZhbHVlID0+IG9uQ2hhbmdlKHZhbHVlIGFzICdlbmFibGVfYWxsJyB8ICdleGl0Jyl9XG4gICAgICAgICAgb25DYW5jZWw9eygpID0+IG9uQ2hhbmdlKCdleGl0Jyl9XG4gICAgICAgIC8+XG5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAge2V4aXRTdGF0ZS5wZW5kaW5nID8gKFxuICAgICAgICAgICAgPD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8Lz5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPD5FbnRlciB0byBjb25maXJtIMK3IEVzYyB0byBjYW5jZWw8Lz5cbiAgICAgICAgICApfVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L1Blcm1pc3Npb25EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLE9BQU8sUUFBUSxJQUFJO0FBQzVCLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLFFBQVEsUUFBUSxpQ0FBaUM7QUFDMUQsU0FBU0MsdUJBQXVCLFFBQVEsMEJBQTBCO0FBQ2xFLGNBQWNDLE9BQU8sUUFBUSxtQkFBbUI7QUFDaEQsU0FBU0MsOEJBQThCLFFBQVEsK0NBQStDO0FBQzlGLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUM5QyxTQUFTQyxhQUFhLFFBQVEsb0NB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 \ No newline at end of file diff --git a/components/TrustDialog/utils.ts b/components/TrustDialog/utils.ts new file mode 100644 index 0000000..0be335a --- /dev/null +++ b/components/TrustDialog/utils.ts @@ -0,0 +1,245 @@ +import type { PermissionRule } from 'src/utils/permissions/PermissionRule.js' +import { getSettingsForSource } from 'src/utils/settings/settings.js' +import type { SettingsJson } from 'src/utils/settings/types.js' +import { BASH_TOOL_NAME } from '../../tools/BashTool/toolName.js' +import { SAFE_ENV_VARS } from '../../utils/managedEnvConstants.js' +import { getPermissionRulesForSource } from '../../utils/permissions/permissionsLoader.js' + +function hasHooks(settings: SettingsJson | null): boolean { + if (settings === null || settings.disableAllHooks) { + return false + } + if (settings.statusLine) { + return true + } + if (settings.fileSuggestion) { + return true + } + if (!settings.hooks) { + return false + } + for (const hookConfig of Object.values(settings.hooks)) { + if (hookConfig.length > 0) { + return true + } + } + return false +} + +export function getHooksSources(): string[] { + const sources: string[] = [] + + const projectSettings = getSettingsForSource('projectSettings') + if (hasHooks(projectSettings)) { + sources.push('.claude/settings.json') + } + + const localSettings = getSettingsForSource('localSettings') + if (hasHooks(localSettings)) { + sources.push('.claude/settings.local.json') + } + + return sources +} + +function hasBashPermission(rules: PermissionRule[]): boolean { + return rules.some( + rule => + rule.ruleBehavior === 'allow' && + (rule.ruleValue.toolName === BASH_TOOL_NAME || + rule.ruleValue.toolName.startsWith(BASH_TOOL_NAME + '(')), + ) +} + +/** + * Get which setting sources have bash allow rules. + * Returns an array of file paths that have bash permissions. + */ +export function getBashPermissionSources(): string[] { + const sources: string[] = [] + + const projectRules = getPermissionRulesForSource('projectSettings') + if (hasBashPermission(projectRules)) { + sources.push('.claude/settings.json') + } + + const localRules = getPermissionRulesForSource('localSettings') + if (hasBashPermission(localRules)) { + sources.push('.claude/settings.local.json') + } + + return sources +} + +/** + * Format a list of items with proper "and" conjunction. + * @param items - Array of items to format + * @param limit - Optional limit for how many items to show before summarizing (ignored if 0) + */ +export function formatListWithAnd(items: string[], limit?: number): string { + if (items.length === 0) return '' + + // Ignore limit if it's 0 + const effectiveLimit = limit === 0 ? undefined : limit + + // If no limit or items are within limit, use normal formatting + if (!effectiveLimit || items.length <= effectiveLimit) { + if (items.length === 1) return items[0]! + if (items.length === 2) return `${items[0]} and ${items[1]}` + + const lastItem = items[items.length - 1]! + const allButLast = items.slice(0, -1) + return `${allButLast.join(', ')}, and ${lastItem}` + } + + // If we have more items than the limit, show first few and count the rest + const shown = items.slice(0, effectiveLimit) + const remaining = items.length - effectiveLimit + + if (shown.length === 1) { + return `${shown[0]} and ${remaining} more` + } + + return `${shown.join(', ')}, and ${remaining} more` +} + +/** + * Check if settings have otelHeadersHelper configured + */ +function hasOtelHeadersHelper(settings: SettingsJson | null): boolean { + return !!settings?.otelHeadersHelper +} + +/** + * Get which setting sources have otelHeadersHelper configured. + * Returns an array of file paths that have otelHeadersHelper. + */ +export function getOtelHeadersHelperSources(): string[] { + const sources: string[] = [] + + const projectSettings = getSettingsForSource('projectSettings') + if (hasOtelHeadersHelper(projectSettings)) { + sources.push('.claude/settings.json') + } + + const localSettings = getSettingsForSource('localSettings') + if (hasOtelHeadersHelper(localSettings)) { + sources.push('.claude/settings.local.json') + } + + return sources +} + +/** + * Check if settings have apiKeyHelper configured + */ +function hasApiKeyHelper(settings: SettingsJson | null): boolean { + return !!settings?.apiKeyHelper +} + +/** + * Get which setting sources have apiKeyHelper configured. + * Returns an array of file paths that have apiKeyHelper. + */ +export function getApiKeyHelperSources(): string[] { + const sources: string[] = [] + + const projectSettings = getSettingsForSource('projectSettings') + if (hasApiKeyHelper(projectSettings)) { + sources.push('.claude/settings.json') + } + + const localSettings = getSettingsForSource('localSettings') + if (hasApiKeyHelper(localSettings)) { + sources.push('.claude/settings.local.json') + } + + return sources +} + +/** + * Check if settings have AWS commands configured + */ +function hasAwsCommands(settings: SettingsJson | null): boolean { + return !!(settings?.awsAuthRefresh || settings?.awsCredentialExport) +} + +/** + * Get which setting sources have AWS commands configured. + * Returns an array of file paths that have awsAuthRefresh or awsCredentialExport. + */ +export function getAwsCommandsSources(): string[] { + const sources: string[] = [] + + const projectSettings = getSettingsForSource('projectSettings') + if (hasAwsCommands(projectSettings)) { + sources.push('.claude/settings.json') + } + + const localSettings = getSettingsForSource('localSettings') + if (hasAwsCommands(localSettings)) { + sources.push('.claude/settings.local.json') + } + + return sources +} + +/** + * Check if settings have GCP commands configured + */ +function hasGcpCommands(settings: SettingsJson | null): boolean { + return !!settings?.gcpAuthRefresh +} + +/** + * Get which setting sources have GCP commands configured. + * Returns an array of file paths that have gcpAuthRefresh. + */ +export function getGcpCommandsSources(): string[] { + const sources: string[] = [] + + const projectSettings = getSettingsForSource('projectSettings') + if (hasGcpCommands(projectSettings)) { + sources.push('.claude/settings.json') + } + + const localSettings = getSettingsForSource('localSettings') + if (hasGcpCommands(localSettings)) { + sources.push('.claude/settings.local.json') + } + + return sources +} + +/** + * Check if settings have dangerous environment variables configured. + * Any env var NOT in SAFE_ENV_VARS is considered dangerous. + */ +function hasDangerousEnvVars(settings: SettingsJson | null): boolean { + if (!settings?.env) { + return false + } + return Object.keys(settings.env).some( + key => !SAFE_ENV_VARS.has(key.toUpperCase()), + ) +} + +/** + * Get which setting sources have dangerous environment variables configured. + * Returns an array of file paths that have env vars not in SAFE_ENV_VARS. + */ +export function getDangerousEnvVarsSources(): string[] { + const sources: string[] = [] + + const projectSettings = getSettingsForSource('projectSettings') + if (hasDangerousEnvVars(projectSettings)) { + sources.push('.claude/settings.json') + } + + const localSettings = getSettingsForSource('localSettings') + if (hasDangerousEnvVars(localSettings)) { + sources.push('.claude/settings.local.json') + } + + return sources +} diff --git a/components/ValidationErrorsList.tsx b/components/ValidationErrorsList.tsx new file mode 100644 index 0000000..233306d --- /dev/null +++ b/components/ValidationErrorsList.tsx @@ -0,0 +1,148 @@ +import { c as _c } from "react/compiler-runtime"; +import setWith from 'lodash-es/setWith.js'; +import * as React from 'react'; +import { Box, Text, useTheme } from '../ink.js'; +import type { ValidationError } from '../utils/settings/validation.js'; +import { type TreeNode, treeify } from '../utils/treeify.js'; + +/** + * Builds a nested tree structure from dot-notation paths + * Uses lodash setWith to avoid automatic array creation + */ +function buildNestedTree(errors: ValidationError[]): TreeNode { + const tree: TreeNode = {}; + errors.forEach(error => { + if (!error.path) { + // Root level error - use empty string as key + tree[''] = error.message; + return; + } + + // Try to enhance the path with meaningful values + const pathParts = error.path.split('.'); + let modifiedPath = error.path; + + // If we have an invalid value, try to make the path more readable + if (error.invalidValue !== null && error.invalidValue !== undefined && pathParts.length > 0) { + const newPathParts: string[] = []; + for (let i = 0; i < pathParts.length; i++) { + const part = pathParts[i]; + if (!part) continue; + const numericPart = parseInt(part, 10); + + // If this is a numeric index and it's the last part where we have the invalid value + if (!isNaN(numericPart) && i === pathParts.length - 1) { + // Format the value for display + let displayValue: string; + if (typeof error.invalidValue === 'string') { + displayValue = `"${error.invalidValue}"`; + } else if (error.invalidValue === null) { + displayValue = 'null'; + } else if (error.invalidValue === undefined) { + displayValue = 'undefined'; + } else { + displayValue = String(error.invalidValue); + } + newPathParts.push(displayValue); + } else { + // Keep other parts as-is + newPathParts.push(part); + } + } + modifiedPath = newPathParts.join('.'); + } + setWith(tree, modifiedPath, error.message, Object); + }); + return tree; +} + +/** + * Groups and displays validation errors using treeify with deduplication + */ +export function ValidationErrorsList(t0) { + const $ = _c(9); + const { + errors + } = t0; + const [themeName] = useTheme(); + if (errors.length === 0) { + return null; + } + let T0; + let t1; + let t2; + if ($[0] !== errors || $[1] !== themeName) { + const errorsByFile = errors.reduce(_temp, {}); + const sortedFiles = Object.keys(errorsByFile).sort(); + T0 = Box; + t1 = "column"; + t2 = sortedFiles.map(file_0 => { + const fileErrors = errorsByFile[file_0] || []; + fileErrors.sort(_temp2); + const errorTree = buildNestedTree(fileErrors); + const suggestionPairs = new Map(); + fileErrors.forEach(error_0 => { + if (error_0.suggestion || error_0.docLink) { + const key = `${error_0.suggestion || ""}|${error_0.docLink || ""}`; + if (!suggestionPairs.has(key)) { + suggestionPairs.set(key, { + suggestion: error_0.suggestion, + docLink: error_0.docLink + }); + } + } + }); + const treeOutput = treeify(errorTree, { + showValues: true, + themeName, + treeCharColors: { + treeChar: "inactive", + key: "text", + value: "inactive" + } + }); + return {file_0}{treeOutput}{suggestionPairs.size > 0 && {Array.from(suggestionPairs.values()).map(_temp3)}}; + }); + $[0] = errors; + $[1] = themeName; + $[2] = T0; + $[3] = t1; + $[4] = t2; + } else { + T0 = $[2]; + t1 = $[3]; + t2 = $[4]; + } + let t3; + if ($[5] !== T0 || $[6] !== t1 || $[7] !== t2) { + t3 = {t2}; + $[5] = T0; + $[6] = t1; + $[7] = t2; + $[8] = t3; + } else { + t3 = $[8]; + } + return t3; +} +function _temp3(pair, index) { + return {pair.suggestion && {pair.suggestion}}{pair.docLink && Learn more: {pair.docLink}}; +} +function _temp2(a, b) { + if (!a.path && b.path) { + return -1; + } + if (a.path && !b.path) { + return 1; + } + return (a.path || "").localeCompare(b.path || ""); +} +function _temp(acc, error) { + const file = error.file || "(file not specified)"; + if (!acc[file]) { + acc[file] = []; + } + acc[file].push(error); + return acc; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJzZXRXaXRoIiwiUmVhY3QiLCJCb3giLCJUZXh0IiwidXNlVGhlbWUiLCJWYWxpZGF0aW9uRXJyb3IiLCJUcmVlTm9kZSIsInRyZWVpZnkiLCJidWlsZE5lc3RlZFRyZWUiLCJlcnJvcnMiLCJ0cmVlIiwiZm9yRWFjaCIsImVycm9yIiwicGF0aCIsIm1lc3NhZ2UiLCJwYXRoUGFydHMiLCJzcGxpdCIsIm1vZGlmaWVkUGF0aCIsImludmFsaWRWYWx1ZSIsInVuZGVmaW5lZCIsImxlbmd0aCIsIm5ld1BhdGhQYXJ0cyIsImkiLCJwYXJ0IiwibnVtZXJpY1BhcnQiLCJwYXJzZUludCIsImlzTmFOIiwiZGlzcGxheVZhbHVlIiwiU3RyaW5nIiwicHVzaCIsImpvaW4iLCJPYmplY3QiLCJWYWxpZGF0aW9uRXJyb3JzTGlzdCIsInQwIiwiJCIsIl9jIiwidGhlbWVOYW1lIiwiVDAiLCJ0MSIsInQyIiwiZXJyb3JzQnlGaWxlIiwicmVkdWNlIiwiX3RlbXAiLCJzb3J0ZWRGaWxlcyIsImtleXMiLCJzb3J0IiwibWFwIiwiZmlsZV8wIiwiZmlsZUVycm9ycyIsImZpbGUiLCJfdGVtcDIiLCJlcnJvclRyZWUiLCJzdWdnZXN0aW9uUGFpcnMiLCJNYXAiLCJlcnJvcl8wIiwic3VnZ2VzdGlvbiIsImRvY0xpbmsiLCJrZXkiLCJoYXMiLCJzZXQiLCJ0cmVlT3V0cHV0Iiwic2hvd1ZhbHVlcyIsInRyZWVDaGFyQ29sb3JzIiwidHJlZUNoYXIiLCJ2YWx1ZSIsInNpemUiLCJBcnJheSIsImZyb20iLCJ2YWx1ZXMiLCJfdGVtcDMiLCJ0MyIsInBhaXIiLCJpbmRleCIsImEiLCJiIiwibG9jYWxlQ29tcGFyZSIsImFjYyJdLCJzb3VyY2VzIjpbIlZhbGlkYXRpb25FcnJvcnNMaXN0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgc2V0V2l0aCBmcm9tICdsb2Rhc2gtZXMvc2V0V2l0aC5qcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0LCB1c2VUaGVtZSB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgVmFsaWRhdGlvbkVycm9yIH0gZnJvbSAnLi4vdXRpbHMvc2V0dGluZ3MvdmFsaWRhdGlvbi5qcydcbmltcG9ydCB7IHR5cGUgVHJlZU5vZGUsIHRyZWVpZnkgfSBmcm9tICcuLi91dGlscy90cmVlaWZ5LmpzJ1xuXG4vKipcbiAqIEJ1aWxkcyBhIG5lc3RlZCB0cmVlIHN0cnVjdHVyZSBmcm9tIGRvdC1ub3RhdGlvbiBwYXRoc1xuICogVXNlcyBsb2Rhc2ggc2V0V2l0aCB0byBhdm9pZCBhdXRvbWF0aWMgYXJyYXkgY3JlYXRpb25cbiAqL1xuZnVuY3Rpb24gYnVpbGROZXN0ZWRUcmVlKGVycm9yczogVmFsaWRhdGlvbkVycm9yW10pOiBUcmVlTm9kZSB7XG4gIGNvbnN0IHRyZWU6IFRyZWVOb2RlID0ge31cblxuICBlcnJvcnMuZm9yRWFjaChlcnJvciA9PiB7XG4gICAgaWYgKCFlcnJvci5wYXRoKSB7XG4gICAgICAvLyBSb290IGxldmVsIGVycm9yIC0gdXNlIGVtcHR5IHN0cmluZyBhcyBrZXlcbiAgICAgIHRyZWVbJyddID0gZXJyb3IubWVzc2FnZVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gVHJ5IHRvIGVuaGFuY2UgdGhlIHBhdGggd2l0aCBtZWFuaW5nZnVsIHZhbHVlc1xuICAgIGNvbnN0IHBhdGhQYXJ0cyA9IGVycm9yLnBhdGguc3BsaXQoJy4nKVxuICAgIGxldCBtb2RpZmllZFBhdGggPSBlcnJvci5wYXRoXG5cbiAgICAvLyBJZiB3ZSBoYXZlIGFuIGludmFsaWQgdmFsdWUsIHRyeSB0byBtYWtlIHRoZSBwYXRoIG1vcmUgcmVhZGFibGVcbiAgICBpZiAoXG4gICAgICBlcnJvci5pbnZhbGlkVmFsdWUgIT09IG51bGwgJiZcbiAgICAgIGVycm9yLmludmFsaWRWYWx1ZSAhPT0gdW5kZWZpbmVkICYmXG4gICAgICBwYXRoUGFydHMubGVuZ3RoID4gMFxuICAgICkge1xuICAgICAgY29uc3QgbmV3UGF0aFBhcnRzOiBzdHJpbmdbXSA9IFtdXG5cbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGF0aFBhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IHBhcnQgPSBwYXRoUGFydHNbaV1cbiAgICAgICAgaWYgKCFwYXJ0KSBjb250aW51ZVxuXG4gICAgICAgIGNvbnN0IG51bWVyaWNQYXJ0ID0gcGFyc2VJbnQocGFydCwgMTApXG5cbiAgICAgICAgLy8gSWYgdGhpcyBpcyBhIG51bWVyaWMgaW5kZXggYW5kIGl0J3MgdGhlIGxhc3QgcGFydCB3aGVyZSB3ZSBoYXZlIHRoZSBpbnZhbGlkIHZhbHVlXG4gICAgICAgIGlmICghaXNOYU4obnVtZXJpY1BhcnQpICYmIGkgPT09IHBhdGhQYXJ0cy5sZW5ndGggLSAxKSB7XG4gICAgICAgICAgLy8gRm9ybWF0IHRoZSB2YWx1ZSBmb3IgZGlzcGxheVxuICAgICAgICAgIGxldCBkaXNwbGF5VmFsdWU6IHN0cmluZ1xuICAgICAgICAgIGlmICh0eXBlb2YgZXJyb3IuaW52YWxpZFZhbHVlID09PSAnc3RyaW5nJykge1xuICAgICAgICAgICAgZGlzcGxheVZhbHVlID0gYFwiJHtlcnJvci5pbnZhbGlkVmFsdWV9XCJgXG4gICAgICAgICAgfSBlbHNlIGlmIChlcnJvci5pbnZhbGlkVmFsdWUgPT09IG51bGwpIHtcbiAgICAgICAgICAgIGRpc3BsYXlWYWx1ZSA9ICdudWxsJ1xuICAgICAgICAgIH0gZWxzZSBpZiAoZXJyb3IuaW52YWxpZFZhbHVlID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGRpc3BsYXlWYWx1ZSA9ICd1bmRlZmluZWQnXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRpc3BsYXlWYWx1ZSA9IFN0cmluZyhlcnJvci5pbnZhbGlkVmFsdWUpXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgbmV3UGF0aFBhcnRzLnB1c2goZGlzcGxheVZhbHVlKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIEtlZXAgb3RoZXIgcGFydHMgYXMtaXNcbiAgICAgICAgICBuZXdQYXRoUGFydHMucHVzaChwYXJ0KVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIG1vZGlmaWVkUGF0aCA9IG5ld1BhdGhQYXJ0cy5qb2luKCcuJylcbiAgICB9XG5cbiAgICBzZXRXaXRoKHRyZWUsIG1vZGlmaWVkUGF0aCwgZXJyb3IubWVzc2FnZSwgT2JqZWN0KVxuICB9KVxuXG4gIHJldHVybiB0cmVlXG59XG5cbi8qKlxuICogR3JvdXBzIGFuZCBkaXNwbGF5cyB2YWxpZGF0aW9uIGVycm9ycyB1c2luZyB0cmVlaWZ5IHdpdGggZGVkdXBsaWNhdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gVmFsaWRhdGlvbkVycm9yc0xpc3Qoe1xuICBlcnJvcnMsXG59OiB7XG4gIGVycm9yczogVmFsaWRhdGlvbkVycm9yW11cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbdGhlbWVOYW1lXSA9IHVzZVRoZW1lKClcblxuICBpZiAoZXJyb3JzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBHcm91cCBlcnJvcnMgYnkgZmlsZVxuICBjb25zdCBlcnJvcnNCeUZpbGUgPSBlcnJvcnMucmVkdWNlPFJlY29yZDxzdHJpbmcsIFZhbGlkYXRpb25FcnJvcltdPj4oXG4gICAgKGFjYywgZXJyb3IpID0+IHtcbiAgICAgIGNvbnN0IGZpbGUgPSBlcnJvci5maWxlIHx8ICcoZmlsZSBub3Qgc3BlY2lmaWVkKSdcbiAgICAgIGlmICghYWNjW2ZpbGVdKSB7XG4gICAgICAgIGFjY1tmaWxlXSA9IFtdXG4gICAgICB9XG4gICAgICBhY2NbZmlsZV0hLnB1c2goZXJyb3IpXG4gICAgICByZXR1cm4gYWNjXG4gICAgfSxcbiAgICB7fSxcbiAgKVxuXG4gIC8vIFNvcnQgZmlsZXMgYWxwaGFiZXRpY2FsbHlcbiAgY29uc3Qgc29ydGVkRmlsZXMgPSBPYmplY3Qua2V5cyhlcnJvcnNCeUZpbGUpLnNvcnQoKVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICB7c29ydGVkRmlsZXMubWFwKGZpbGUgPT4ge1xuICAgICAgICBjb25zdCBmaWxlRXJyb3JzID0gZXJyb3JzQnlGaWxlW2ZpbGVdIHx8IFtdXG5cbiAgICAgICAgLy8gU29ydCBlcnJvcnMgYnkgcGF0aFxuICAgICAgICBmaWxlRXJyb3JzLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgICAgICBpZiAoIWEucGF0aCAmJiBiLnBhdGgpIHJldHVybiAtMVxuICAgICAgICAgIGlmIChhLnBhdGggJiYgIWIucGF0aCkgcmV0dXJuIDFcbiAgICAgICAgICByZXR1cm4gKGEucGF0aCB8fCAnJykubG9jYWxlQ29tcGFyZShiLnBhdGggfHwgJycpXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gQnVpbGQgbmVzdGVkIHRyZWUgc3RydWN0dXJlIGZyb20gZXJyb3IgcGF0aHNcbiAgICAgICAgY29uc3QgZXJyb3JUcmVlID0gYnVpbGROZXN0ZWRUcmVlKGZpbGVFcnJvcnMpXG5cbiAgICAgICAgLy8gQ29sbGVjdCB1bmlxdWUgc3VnZ2VzdGlvbitkb2NMaW5rIHBhaXJzXG4gICAgICAgIGNvbnN0IHN1Z2dlc3Rpb25QYWlycyA9IG5ldyBNYXA8XG4gICAgICAgICAgc3RyaW5nLFxuICAgICAgICAgIHsgc3VnZ2VzdGlvbj86IHN0cmluZzsgZG9jTGluaz86IHN0cmluZyB9XG4gICAgICAgID4oKVxuXG4gICAgICAgIGZpbGVFcnJvcnMuZm9yRWFjaChlcnJvciA9PiB7XG4gICAgICAgICAgaWYgKGVycm9yLnN1Z2dlc3Rpb24gfHwgZXJyb3IuZG9jTGluaykge1xuICAgICAgICAgICAgLy8gQ3JlYXRlIGEga2V5IGZyb20gc3VnZ2VzdGlvbitkb2NMaW5rIGNvbWJpbmF0aW9uXG4gICAgICAgICAgICBjb25zdCBrZXkgPSBgJHtlcnJvci5zdWdnZXN0aW9uIHx8ICcnfXwke2Vycm9yLmRvY0xpbmsgfHwgJyd9YFxuICAgICAgICAgICAgaWYgKCFzdWdnZXN0aW9uUGFpcnMuaGFzKGtleSkpIHtcbiAgICAgICAgICAgICAgc3VnZ2VzdGlvblBhaXJzLnNldChrZXksIHtcbiAgICAgICAgICAgICAgICBzdWdnZXN0aW9uOiBlcnJvci5zdWdnZXN0aW9uLFxuICAgICAgICAgICAgICAgIGRvY0xpbms6IGVycm9yLmRvY0xpbmssXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9KVxuXG4gICAgICAgIC8vIFJlbmRlciB0aGUgdHJlZVxuICAgICAgICBjb25zdCB0cmVlT3V0cHV0ID0gdHJlZWlmeShlcnJvclRyZWUsIHtcbiAgICAgICAgICBzaG93VmFsdWVzOiB0cnVlLFxuICAgICAgICAgIHRoZW1lTmFtZSxcbiAgICAgICAgICB0cmVlQ2hhckNvbG9yczoge1xuICAgICAgICAgICAgdHJlZUNoYXI6ICdpbmFjdGl2ZScsXG4gICAgICAgICAgICBrZXk6ICd0ZXh0JyxcbiAgICAgICAgICAgIHZhbHVlOiAnaW5hY3RpdmUnLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pXG5cbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICA8Qm94IGtleT17ZmlsZX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPFRleHQ+e2ZpbGV9PC9UZXh0PlxuICAgICAgICAgICAgPEJveCBtYXJnaW5MZWZ0PXsxfT5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+e3RyZWVPdXRwdXR9PC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICB7LyogRGlzcGxheSB1bmlxdWUgc3VnZ2VzdGlvbitkb2NMaW5rIHBhaXJzICovfVxuICAgICAgICAgICAge3N1Z2dlc3Rpb25QYWlycy5zaXplID4gMCAmJiAoXG4gICAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICAgICAge0FycmF5LmZyb20oc3VnZ2VzdGlvblBhaXJzLnZhbHVlcygpKS5tYXAoKHBhaXIsIGluZGV4KSA9PiAoXG4gICAgICAgICAgICAgICAgICA8Qm94XG4gICAgICAgICAgICAgICAgICAgIGtleT17YHN1Z2dlc3Rpb24tcGFpci0ke2luZGV4fWB9XG4gICAgICAgICAgICAgICAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgICAgICAgICAgICAgICBtYXJnaW5Cb3R0b209ezF9XG4gICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgIHtwYWlyLnN1Z2dlc3Rpb24gJiYgKFxuICAgICAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIHdyYXA9XCJ3cmFwXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICB7cGFpci5zdWdnZXN0aW9ufVxuICAgICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICAgICAge3BhaXIuZG9jTGluayAmJiAoXG4gICAgICAgICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3Igd3JhcD1cIndyYXBcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIExlYXJuIG1vcmU6IHtwYWlyLmRvY0xpbmt9XG4gICAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKVxuICAgICAgfSl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLE9BQU8sTUFBTSxzQkFBc0I7QUFDMUMsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxHQUFHLEVBQUVDLElBQUks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 \ No newline at end of file diff --git a/components/VimTextInput.tsx b/components/VimTextInput.tsx new file mode 100644 index 0000000..7c2e6c5 --- /dev/null +++ b/components/VimTextInput.tsx @@ -0,0 +1,140 @@ +import { c as _c } from "react/compiler-runtime"; +import chalk from 'chalk'; +import React from 'react'; +import { useClipboardImageHint } from '../hooks/useClipboardImageHint.js'; +import { useVimInput } from '../hooks/useVimInput.js'; +import { Box, color, useTerminalFocus, useTheme } from '../ink.js'; +import type { VimTextInputProps } from '../types/textInputTypes.js'; +import type { TextHighlight } from '../utils/textHighlighting.js'; +import { BaseTextInput } from './BaseTextInput.js'; +export type Props = VimTextInputProps & { + highlights?: TextHighlight[]; +}; +export default function VimTextInput(props) { + const $ = _c(38); + const [theme] = useTheme(); + const isTerminalFocused = useTerminalFocus(); + useClipboardImageHint(isTerminalFocused, !!props.onImagePaste); + const t0 = props.value; + const t1 = props.onChange; + const t2 = props.onSubmit; + const t3 = props.onExit; + const t4 = props.onExitMessage; + const t5 = props.onHistoryReset; + const t6 = props.onHistoryUp; + const t7 = props.onHistoryDown; + const t8 = props.onClearInput; + const t9 = props.focus; + const t10 = props.mask; + const t11 = props.multiline; + const t12 = props.showCursor ? " " : ""; + const t13 = props.highlightPastedText; + const t14 = isTerminalFocused ? chalk.inverse : _temp; + let t15; + if ($[0] !== theme) { + t15 = color("text", theme); + $[0] = theme; + $[1] = t15; + } else { + t15 = $[1]; + } + let t16; + if ($[2] !== props.columns || $[3] !== props.cursorOffset || $[4] !== props.disableCursorMovementForUpDownKeys || $[5] !== props.disableEscapeDoublePress || $[6] !== props.focus || $[7] !== props.highlightPastedText || $[8] !== props.inputFilter || $[9] !== props.mask || $[10] !== props.maxVisibleLines || $[11] !== props.multiline || $[12] !== props.onChange || $[13] !== props.onChangeCursorOffset || $[14] !== props.onClearInput || $[15] !== props.onExit || $[16] !== props.onExitMessage || $[17] !== props.onHistoryDown || $[18] !== props.onHistoryReset || $[19] !== props.onHistoryUp || $[20] !== props.onImagePaste || $[21] !== props.onModeChange || $[22] !== props.onSubmit || $[23] !== props.onUndo || $[24] !== props.value || $[25] !== t12 || $[26] !== t14 || $[27] !== t15) { + t16 = { + value: t0, + onChange: t1, + onSubmit: t2, + onExit: t3, + onExitMessage: t4, + onHistoryReset: t5, + onHistoryUp: t6, + onHistoryDown: t7, + onClearInput: t8, + focus: t9, + mask: t10, + multiline: t11, + cursorChar: t12, + highlightPastedText: t13, + invert: t14, + themeText: t15, + columns: props.columns, + maxVisibleLines: props.maxVisibleLines, + onImagePaste: props.onImagePaste, + disableCursorMovementForUpDownKeys: props.disableCursorMovementForUpDownKeys, + disableEscapeDoublePress: props.disableEscapeDoublePress, + externalOffset: props.cursorOffset, + onOffsetChange: props.onChangeCursorOffset, + inputFilter: props.inputFilter, + onModeChange: props.onModeChange, + onUndo: props.onUndo + }; + $[2] = props.columns; + $[3] = props.cursorOffset; + $[4] = props.disableCursorMovementForUpDownKeys; + $[5] = props.disableEscapeDoublePress; + $[6] = props.focus; + $[7] = props.highlightPastedText; + $[8] = props.inputFilter; + $[9] = props.mask; + $[10] = props.maxVisibleLines; + $[11] = props.multiline; + $[12] = props.onChange; + $[13] = props.onChangeCursorOffset; + $[14] = props.onClearInput; + $[15] = props.onExit; + $[16] = props.onExitMessage; + $[17] = props.onHistoryDown; + $[18] = props.onHistoryReset; + $[19] = props.onHistoryUp; + $[20] = props.onImagePaste; + $[21] = props.onModeChange; + $[22] = props.onSubmit; + $[23] = props.onUndo; + $[24] = props.value; + $[25] = t12; + $[26] = t14; + $[27] = t15; + $[28] = t16; + } else { + t16 = $[28]; + } + const vimInputState = useVimInput(t16); + const { + mode, + setMode + } = vimInputState; + let t17; + let t18; + if ($[29] !== mode || $[30] !== props.initialMode || $[31] !== setMode) { + t17 = () => { + if (props.initialMode && props.initialMode !== mode) { + setMode(props.initialMode); + } + }; + t18 = [props.initialMode, mode, setMode]; + $[29] = mode; + $[30] = props.initialMode; + $[31] = setMode; + $[32] = t17; + $[33] = t18; + } else { + t17 = $[32]; + t18 = $[33]; + } + React.useEffect(t17, t18); + let t19; + if ($[34] !== isTerminalFocused || $[35] !== props || $[36] !== vimInputState) { + t19 = ; + $[34] = isTerminalFocused; + $[35] = props; + $[36] = vimInputState; + $[37] = t19; + } else { + t19 = $[37]; + } + return t19; +} +function _temp(text) { + return text; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjaGFsayIsIlJlYWN0IiwidXNlQ2xpcGJvYXJkSW1hZ2VIaW50IiwidXNlVmltSW5wdXQiLCJCb3giLCJjb2xvciIsInVzZVRlcm1pbmFsRm9jdXMiLCJ1c2VUaGVtZSIsIlZpbVRleHRJbnB1dFByb3BzIiwiVGV4dEhpZ2hsaWdodCIsIkJhc2VUZXh0SW5wdXQiLCJQcm9wcyIsImhpZ2hsaWdodHMiLCJWaW1UZXh0SW5wdXQiLCJwcm9wcyIsIiQiLCJfYyIsInRoZW1lIiwiaXNUZXJtaW5hbEZvY3VzZWQiLCJvbkltYWdlUGFzdGUiLCJ0MCIsInZhbHVlIiwidDEiLCJvbkNoYW5nZSIsInQyIiwib25TdWJtaXQiLCJ0MyIsIm9uRXhpdCIsInQ0Iiwib25FeGl0TWVzc2FnZSIsInQ1Iiwib25IaXN0b3J5UmVzZXQiLCJ0NiIsIm9uSGlzdG9yeVVwIiwidDciLCJvbkhpc3RvcnlEb3duIiwidDgiLCJvbkNsZWFySW5wdXQiLCJ0OSIsImZvY3VzIiwidDEwIiwibWFzayIsInQxMSIsIm11bHRpbGluZSIsInQxMiIsInNob3dDdXJzb3IiLCJ0MTMiLCJoaWdobGlnaHRQYXN0ZWRUZXh0IiwidDE0IiwiaW52ZXJzZSIsIl90ZW1wIiwidDE1IiwidDE2IiwiY29sdW1ucyIsImN1cnNvck9mZnNldCIsImRpc2FibGVDdXJzb3JNb3ZlbWVudEZvclVwRG93bktleXMiLCJkaXNhYmxlRXNjYXBlRG91YmxlUHJlc3MiLCJpbnB1dEZpbHRlciIsIm1heFZpc2libGVMaW5lcyIsIm9uQ2hhbmdlQ3Vyc29yT2Zmc2V0Iiwib25Nb2RlQ2hhbmdlIiwib25VbmRvIiwiY3Vyc29yQ2hhciIsImludmVydCIsInRoZW1lVGV4dCIsImV4dGVybmFsT2Zmc2V0Iiwib25PZmZzZXRDaGFuZ2UiLCJ2aW1JbnB1dFN0YXRlIiwibW9kZSIsInNldE1vZGUiLCJ0MTciLCJ0MTgiLCJpbml0aWFsTW9kZSIsInVzZUVmZmVjdCIsInQxOSIsInRleHQiXSwic291cmNlcyI6WyJWaW1UZXh0SW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjaGFsayBmcm9tICdjaGFsaydcbmltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUNsaXBib2FyZEltYWdlSGludCB9IGZyb20gJy4uL2hvb2tzL3VzZUNsaXBib2FyZEltYWdlSGludC5qcydcbmltcG9ydCB7IHVzZVZpbUlucHV0IH0gZnJvbSAnLi4vaG9va3MvdXNlVmltSW5wdXQuanMnXG5pbXBvcnQgeyBCb3gsIGNvbG9yLCB1c2VUZXJtaW5hbEZvY3VzLCB1c2VUaGVtZSB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgVmltVGV4dElucHV0UHJvcHMgfSBmcm9tICcuLi90eXBlcy90ZXh0SW5wdXRUeXBlcy5qcydcbmltcG9ydCB0eXBlIHsgVGV4dEhpZ2hsaWdodCB9IGZyb20gJy4uL3V0aWxzL3RleHRIaWdobGlnaHRpbmcuanMnXG5pbXBvcnQgeyBCYXNlVGV4dElucHV0IH0gZnJvbSAnLi9CYXNlVGV4dElucHV0LmpzJ1xuXG5leHBvcnQgdHlwZSBQcm9wcyA9IFZpbVRleHRJbnB1dFByb3BzICYge1xuICBoaWdobGlnaHRzPzogVGV4dEhpZ2hsaWdodFtdXG59XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIFZpbVRleHRJbnB1dChwcm9wczogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbdGhlbWVdID0gdXNlVGhlbWUoKVxuICBjb25zdCBpc1Rlcm1pbmFsRm9jdXNlZCA9IHVzZVRlcm1pbmFsRm9jdXMoKVxuXG4gIC8vIFNob3cgaGludCB3aGVuIHRlcm1pbmFsIHJlZ2FpbnMgZm9jdXMgYW5kIGNsaXBib2FyZCBoYXMgYW4gaW1hZ2VcbiAgdXNlQ2xpcGJvYXJkSW1hZ2VIaW50KGlzVGVybWluYWxGb2N1c2VkLCAhIXByb3BzLm9uSW1hZ2VQYXN0ZSlcblxuICBjb25zdCB2aW1JbnB1dFN0YXRlID0gdXNlVmltSW5wdXQoe1xuICAgIHZhbHVlOiBwcm9wcy52YWx1ZSxcbiAgICBvbkNoYW5nZTogcHJvcHMub25DaGFuZ2UsXG4gICAgb25TdWJtaXQ6IHByb3BzLm9uU3VibWl0LFxuICAgIG9uRXhpdDogcHJvcHMub25FeGl0LFxuICAgIG9uRXhpdE1lc3NhZ2U6IHByb3BzLm9uRXhpdE1lc3NhZ2UsXG4gICAgb25IaXN0b3J5UmVzZXQ6IHByb3BzLm9uSGlzdG9yeVJlc2V0LFxuICAgIG9uSGlzdG9yeVVwOiBwcm9wcy5vbkhpc3RvcnlVcCxcbiAgICBvbkhpc3RvcnlEb3duOiBwcm9wcy5vbkhpc3RvcnlEb3duLFxuICAgIG9uQ2xlYXJJbnB1dDogcHJvcHMub25DbGVhcklucHV0LFxuICAgIGZvY3VzOiBwcm9wcy5mb2N1cyxcbiAgICBtYXNrOiBwcm9wcy5tYXNrLFxuICAgIG11bHRpbGluZTogcHJvcHMubXVsdGlsaW5lLFxuICAgIGN1cnNvckNoYXI6IHByb3BzLnNob3dDdXJzb3IgPyAnICcgOiAnJyxcbiAgICBoaWdobGlnaHRQYXN0ZWRUZXh0OiBwcm9wcy5oaWdobGlnaHRQYXN0ZWRUZXh0LFxuICAgIGludmVydDogaXNUZXJtaW5hbEZvY3VzZWQgPyBjaGFsay5pbnZlcnNlIDogKHRleHQ6IHN0cmluZykgPT4gdGV4dCxcbiAgICB0aGVtZVRleHQ6IGNvbG9yKCd0ZXh0JywgdGhlbWUpLFxuICAgIGNvbHVtbnM6IHByb3BzLmNvbHVtbnMsXG4gICAgbWF4VmlzaWJsZUxpbmVzOiBwcm9wcy5tYXhWaXNpYmxlTGluZXMsXG4gICAgb25JbWFnZVBhc3RlOiBwcm9wcy5vbkltYWdlUGFzdGUsXG4gICAgZGlzYWJsZUN1cnNvck1vdmVtZW50Rm9yVXBEb3duS2V5czpcbiAgICAgIHByb3BzLmRpc2FibGVDdXJzb3JNb3ZlbWVudEZvclVwRG93bktleXMsXG4gICAgZGlzYWJsZUVzY2FwZURvdWJsZVByZXNzOiBwcm9wcy5kaXNhYmxlRXNjYXBlRG91YmxlUHJlc3MsXG4gICAgZXh0ZXJuYWxPZmZzZXQ6IHByb3BzLmN1cnNvck9mZnNldCxcbiAgICBvbk9mZnNldENoYW5nZTogcHJvcHMub25DaGFuZ2VDdXJzb3JPZmZzZXQsXG4gICAgaW5wdXRGaWx0ZXI6IHByb3BzLmlucHV0RmlsdGVyLFxuICAgIG9uTW9kZUNoYW5nZTogcHJvcHMub25Nb2RlQ2hhbmdlLFxuICAgIG9uVW5kbzogcHJvcHMub25VbmRvLFxuICB9KVxuXG4gIGNvbnN0IHsgbW9kZSwgc2V0TW9kZSB9ID0gdmltSW5wdXRTdGF0ZVxuXG4gIFJlYWN0LnVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKHByb3BzLmluaXRpYWxNb2RlICYmIHByb3BzLmluaXRpYWxNb2RlICE9PSBtb2RlKSB7XG4gICAgICBzZXRNb2RlKHByb3BzLmluaXRpYWxNb2RlKVxuICAgIH1cbiAgfSwgW3Byb3BzLmluaXRpYWxNb2RlLCBtb2RlLCBzZXRNb2RlXSlcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPEJhc2VUZXh0SW5wdXRcbiAgICAgICAgaW5wdXRTdGF0ZT17dmltSW5wdXRTdGF0ZX1cbiAgICAgICAgdGVybWluYWxGb2N1cz17aXNUZXJtaW5hbEZvY3VzZWR9XG4gICAgICAgIGhpZ2hsaWdodHM9e3Byb3BzLmhpZ2hsaWdodHN9XG4gICAgICAgIHsuLi5wcm9wc31cbiAgICAgIC8+XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLHFCQUFxQixRQUFRLG1DQUFtQztBQUN6RSxTQUFTQyxXQUFXLFFBQVEseUJBQXlCO0FBQ3JELFNBQVNDLEdBQUcsRUFBRUMsS0FBSyxFQUFFQyxnQkFBZ0IsRUFBRUMsUUFBUSxRQUFRLFdBQVc7QUFDbEUsY0FBY0MsaUJBQWlCLFFBQVEsNEJBQTRCO0FBQ25FLGNBQWNDLGFBQWEsUUFBUSw4QkFBOEI7QUFDakUsU0FBU0MsYUFBYSxRQUFRLG9CQUFvQjtBQUVsRCxPQUFPLEtB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 \ No newline at end of file diff --git a/components/VirtualMessageList.tsx b/components/VirtualMessageList.tsx new file mode 100644 index 0000000..b9a8d7a --- /dev/null +++ b/components/VirtualMessageList.tsx @@ -0,0 +1,1082 @@ +import { c as _c } from "react/compiler-runtime"; +import type { RefObject } from 'react'; +import * as React from 'react'; +import { useCallback, useContext, useEffect, useImperativeHandle, useRef, useState, useSyncExternalStore } from 'react'; +import { useVirtualScroll } from '../hooks/useVirtualScroll.js'; +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js'; +import type { DOMElement } from '../ink/dom.js'; +import type { MatchPosition } from '../ink/render-to-screen.js'; +import { Box } from '../ink.js'; +import type { RenderableMessage } from '../types/message.js'; +import { TextHoverColorContext } from './design-system/ThemedText.js'; +import { ScrollChromeContext } from './FullscreenLayout.js'; + +// Rows of breathing room above the target when we scrollTo. +const HEADROOM = 3; +import { logForDebugging } from '../utils/debug.js'; +import { sleep } from '../utils/sleep.js'; +import { renderableSearchText } from '../utils/transcriptSearch.js'; +import { isNavigableMessage, type MessageActionsNav, type MessageActionsState, type NavigableMessage, stripSystemReminders, toolCallOf } from './messageActions.js'; + +// Fallback extractor: lower + cache here for callers without the +// Messages.tsx tool-lookup path (tests, static contexts). Messages.tsx +// provides its own lowering cache that also handles tool extractSearchText. +const fallbackLowerCache = new WeakMap(); +function defaultExtractSearchText(msg: RenderableMessage): string { + const cached = fallbackLowerCache.get(msg); + if (cached !== undefined) return cached; + const lowered = renderableSearchText(msg); + fallbackLowerCache.set(msg, lowered); + return lowered; +} +export type StickyPrompt = { + text: string; + scrollTo: () => void; +} +// Click sets this — header HIDES but padding stays collapsed (0) so +// the content ❯ lands at screen row 0 instead of row 1. Cleared on +// the next sticky-prompt compute (user scrolls again). +| 'clicked'; + +/** Huge pasted prompts (cat file | claude) can be MBs. Header wraps into + * 2 rows via overflow:hidden — this just bounds the React prop size. */ +const STICKY_TEXT_CAP = 500; + +/** Imperative handle for transcript navigation. Methods compute matches + * HERE (renderableMessages indices are only valid inside this component — + * Messages.tsx filters and reorders, REPL can't compute externally). */ +export type JumpHandle = { + jumpToIndex: (i: number) => void; + setSearchQuery: (q: string) => void; + nextMatch: () => void; + prevMatch: () => void; + /** Capture current scrollTop as the incsearch anchor. Typing jumps + * around as preview; 0-matches snaps back here. Enter/n/N never + * restore (they don't call setSearchQuery with empty). Next / call + * overwrites. */ + setAnchor: () => void; + /** Warm the search-text cache by extracting every message's text. + * Returns elapsed ms, or 0 if already warm (subsequent / in same + * transcript session). Yields before work so the caller can paint + * "indexing…" first. Caller shows "indexed in Xms" on resolve. */ + warmSearchIndex: () => Promise; + /** Manual scroll (j/k/PgUp/wheel) exited the search context. Clear + * positions (yellow goes away, inverse highlights stay). Next n/N + * re-establishes via step()→jump(). Wired from ScrollKeybindingHandler's + * onScroll — only fires for keyboard/wheel, not programmatic scrollTo. */ + disarmSearch: () => void; +}; +type Props = { + messages: RenderableMessage[]; + scrollRef: RefObject; + /** Invalidates heightCache on change — cached heights from a different + * width are wrong (text rewrap → black screen on scroll-up after widen). */ + columns: number; + itemKey: (msg: RenderableMessage) => string; + renderItem: (msg: RenderableMessage, index: number) => React.ReactNode; + /** Fires when a message Box is clicked (toggle per-message verbose). */ + onItemClick?: (msg: RenderableMessage) => void; + /** Per-item filter — suppress hover/click for messages where the verbose + * toggle does nothing (text, file edits, etc). Defaults to all-clickable. */ + isItemClickable?: (msg: RenderableMessage) => boolean; + /** Expanded items get a persistent grey bg (not just on hover). */ + isItemExpanded?: (msg: RenderableMessage) => boolean; + /** PRE-LOWERED search text. Messages.tsx caches the lowered result + * once at warm time so setSearchQuery's per-keystroke loop does + * only indexOf (zero toLowerCase alloc). Falls back to a lowering + * wrapper on renderableSearchText for callers without the cache. */ + extractSearchText?: (msg: RenderableMessage) => string; + /** Enable the sticky-prompt tracker. StickyTracker writes via + * ScrollChromeContext (not a callback prop) so state lives in + * FullscreenLayout instead of REPL. */ + trackStickyPrompt?: boolean; + selectedIndex?: number; + /** Nav handle lives here because height measurement lives here. */ + cursorNavRef?: React.Ref; + setCursor?: (c: MessageActionsState | null) => void; + jumpRef?: RefObject; + /** Fires when search matches change (query edit, n/N). current is + * 1-based for "3/47" display; 0 means no matches. */ + onSearchMatchesChange?: (count: number, current: number) => void; + /** Paint existing DOM subtree to fresh Screen, scan. Element from the + * main tree (all providers). Message-relative positions (row 0 = el + * top). Works for any height — closes the tall-message gap. */ + scanElement?: (el: DOMElement) => MatchPosition[]; + /** Position-based CURRENT highlight. Positions known upfront (from + * scanElement), navigation = index arithmetic + scrollTo. rowOffset + * = message's current screen-top; positions stay stable. */ + setPositions?: (state: { + positions: MatchPosition[]; + rowOffset: number; + currentIdx: number; + } | null) => void; +}; + +/** + * Returns the text of a real user prompt, or null for anything else. + * "Real" = what the human typed: not tool results, not XML-wrapped payloads + * (, , , etc.), not meta. + * + * Two shapes land here: NormalizedUserMessage (normal prompts) and + * AttachmentMessage with type==='queued_command' (prompts sent mid-turn + * while a tool was executing — they get drained as attachments on the + * next turn, see query.ts:1410). Both render as ❯-prefixed UserTextMessage + * in the UI so both should stick. + * + * Leading blocks are stripped before checking — they get + * prepended to the stored text for Claude's context (memory updates, auto + * mode reminders) but aren't what the user typed. Without stripping, any + * prompt that happened to get a reminder is rejected by the startsWith('<') + * check. Shows up on `cc -c` resumes where memory-update reminders are dense. + */ +const promptTextCache = new WeakMap(); +function stickyPromptText(msg: RenderableMessage): string | null { + // Cache keyed on message object — messages are append-only and don't + // mutate, so a WeakMap hit is always valid. The walk (StickyTracker, + // per-scroll-tick) calls this 5-50+ times with the SAME messages every + // tick; the system-reminder strip allocates a fresh string on each + // parse. WeakMap self-GCs on compaction/clear (messages[] replaced). + const cached = promptTextCache.get(msg); + if (cached !== undefined) return cached; + const result = computeStickyPromptText(msg); + promptTextCache.set(msg, result); + return result; +} +function computeStickyPromptText(msg: RenderableMessage): string | null { + let raw: string | null = null; + if (msg.type === 'user') { + if (msg.isMeta || msg.isVisibleInTranscriptOnly) return null; + const block = msg.message.content[0]; + if (block?.type !== 'text') return null; + raw = block.text; + } else if (msg.type === 'attachment' && msg.attachment.type === 'queued_command' && msg.attachment.commandMode !== 'task-notification' && !msg.attachment.isMeta) { + const p = msg.attachment.prompt; + raw = typeof p === 'string' ? p : p.flatMap(b => b.type === 'text' ? [b.text] : []).join('\n'); + } + if (raw === null) return null; + const t = stripSystemReminders(raw); + if (t.startsWith('<') || t === '') return null; + return t; +} + +/** + * Virtualized message list for fullscreen mode. Split from Messages.tsx so + * useVirtualScroll is called unconditionally (rules-of-hooks) — Messages.tsx + * conditionally renders either this or a plain .map(). + * + * The wrapping is the measurement anchor — MessageRow doesn't take + * a ref. Single-child column Box passes Yoga height through unchanged. + */ +type VirtualItemProps = { + itemKey: string; + msg: RenderableMessage; + idx: number; + measureRef: (key: string) => (el: DOMElement | null) => void; + expanded: boolean | undefined; + hovered: boolean; + clickable: boolean; + onClickK: (msg: RenderableMessage, cellIsBlank: boolean) => void; + onEnterK: (k: string) => void; + onLeaveK: (k: string) => void; + renderItem: (msg: RenderableMessage, idx: number) => React.ReactNode; +}; + +// Item wrapper with stable click handlers. The per-item closures were the +// `operationNewArrowFunction` leafs → `FunctionExecutable::finalizeUnconditionally` +// GC cleanup (16% of GC time during fast scroll). 3 closures × 60 mounted × +// 10 commits/sec = 1800 closures/sec. With stable onClickK/onEnterK/onLeaveK +// threaded via itemKey, the closures here are per-item-per-render but CHEAP +// (just wrap the stable callback with k bound) and don't close over msg/idx +// which lets JIT inline them. The bigger win is inside: MessageRow.memo +// bails for unchanged msgs, skipping marked.lexer + formatToken. +// +// NOT React.memo'd — renderItem captures changing state (cursor, selectedIdx, +// verbose). Memoing with a comparator that ignores renderItem would use a +// STALE closure on bail (wrong selection highlight, stale verbose). Including +// renderItem in the comparator defeats memo since it's fresh each render. +function VirtualItem(t0) { + const $ = _c(30); + const { + itemKey: k, + msg, + idx, + measureRef, + expanded, + hovered, + clickable, + onClickK, + onEnterK, + onLeaveK, + renderItem + } = t0; + let t1; + if ($[0] !== k || $[1] !== measureRef) { + t1 = measureRef(k); + $[0] = k; + $[1] = measureRef; + $[2] = t1; + } else { + t1 = $[2]; + } + const t2 = expanded ? "userMessageBackgroundHover" : undefined; + const t3 = expanded ? 1 : undefined; + let t4; + if ($[3] !== clickable || $[4] !== msg || $[5] !== onClickK) { + t4 = clickable ? e => onClickK(msg, e.cellIsBlank) : undefined; + $[3] = clickable; + $[4] = msg; + $[5] = onClickK; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] !== clickable || $[8] !== k || $[9] !== onEnterK) { + t5 = clickable ? () => onEnterK(k) : undefined; + $[7] = clickable; + $[8] = k; + $[9] = onEnterK; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] !== clickable || $[12] !== k || $[13] !== onLeaveK) { + t6 = clickable ? () => onLeaveK(k) : undefined; + $[11] = clickable; + $[12] = k; + $[13] = onLeaveK; + $[14] = t6; + } else { + t6 = $[14]; + } + const t7 = hovered && !expanded ? "text" : undefined; + let t8; + if ($[15] !== idx || $[16] !== msg || $[17] !== renderItem) { + t8 = renderItem(msg, idx); + $[15] = idx; + $[16] = msg; + $[17] = renderItem; + $[18] = t8; + } else { + t8 = $[18]; + } + let t9; + if ($[19] !== t7 || $[20] !== t8) { + t9 = {t8}; + $[19] = t7; + $[20] = t8; + $[21] = t9; + } else { + t9 = $[21]; + } + let t10; + if ($[22] !== t1 || $[23] !== t2 || $[24] !== t3 || $[25] !== t4 || $[26] !== t5 || $[27] !== t6 || $[28] !== t9) { + t10 = {t9}; + $[22] = t1; + $[23] = t2; + $[24] = t3; + $[25] = t4; + $[26] = t5; + $[27] = t6; + $[28] = t9; + $[29] = t10; + } else { + t10 = $[29]; + } + return t10; +} +export function VirtualMessageList({ + messages, + scrollRef, + columns, + itemKey, + renderItem, + onItemClick, + isItemClickable, + isItemExpanded, + extractSearchText = defaultExtractSearchText, + trackStickyPrompt, + selectedIndex, + cursorNavRef, + setCursor, + jumpRef, + onSearchMatchesChange, + scanElement, + setPositions +}: Props): React.ReactNode { + // Incremental key array. Streaming appends one message at a time; rebuilding + // the full string array on every commit allocates O(n) per message (~1MB + // churn at 27k messages). Append-only delta push when the prefix matches; + // fall back to full rebuild on compaction, /clear, or itemKey change. + const keysRef = useRef([]); + const prevMessagesRef = useRef(messages); + const prevItemKeyRef = useRef(itemKey); + if (prevItemKeyRef.current !== itemKey || messages.length < keysRef.current.length || messages[0] !== prevMessagesRef.current[0]) { + keysRef.current = messages.map(m => itemKey(m)); + } else { + for (let i = keysRef.current.length; i < messages.length; i++) { + keysRef.current.push(itemKey(messages[i]!)); + } + } + prevMessagesRef.current = messages; + prevItemKeyRef.current = itemKey; + const keys = keysRef.current; + const { + range, + topSpacer, + bottomSpacer, + measureRef, + spacerRef, + offsets, + getItemTop, + getItemElement, + getItemHeight, + scrollToIndex + } = useVirtualScroll(scrollRef, keys, columns); + const [start, end] = range; + + // Unmeasured (undefined height) falls through — assume visible. + const isVisible = useCallback((i: number) => { + const h = getItemHeight(i); + if (h === 0) return false; + return isNavigableMessage(messages[i]!); + }, [getItemHeight, messages]); + useImperativeHandle(cursorNavRef, (): MessageActionsNav => { + const select = (m: NavigableMessage) => setCursor?.({ + uuid: m.uuid, + msgType: m.type, + expanded: false, + toolName: toolCallOf(m)?.name + }); + const selIdx = selectedIndex ?? -1; + const scan = (from: number, dir: 1 | -1, pred: (i: number) => boolean = isVisible) => { + for (let i = from; i >= 0 && i < messages.length; i += dir) { + if (pred(i)) { + select(messages[i]!); + return true; + } + } + return false; + }; + const isUser = (i: number) => isVisible(i) && messages[i]!.type === 'user'; + return { + // Entry via shift+↑ = same semantic as in-cursor shift+↑ (prevUser). + enterCursor: () => scan(messages.length - 1, -1, isUser), + navigatePrev: () => scan(selIdx - 1, -1), + navigateNext: () => { + if (scan(selIdx + 1, 1)) return; + // Past last visible → exit + repin. Last message's TOP is at viewport + // top (selection-scroll effect); its BOTTOM may be below the fold. + scrollRef.current?.scrollToBottom(); + setCursor?.(null); + }, + // type:'user' only — queued_command attachments look like prompts but have no raw UserMessage to rewind to. + navigatePrevUser: () => scan(selIdx - 1, -1, isUser), + navigateNextUser: () => scan(selIdx + 1, 1, isUser), + navigateTop: () => scan(0, 1), + navigateBottom: () => scan(messages.length - 1, -1), + getSelected: () => selIdx >= 0 ? messages[selIdx] ?? null : null + }; + }, [messages, selectedIndex, setCursor, isVisible]); + // Two-phase jump + search engine. Read-through-ref so the handle stays + // stable across renders — offsets/messages identity changes every render, + // can't go in useImperativeHandle deps without recreating the handle. + const jumpState = useRef({ + offsets, + start, + getItemElement, + getItemTop, + messages, + scrollToIndex + }); + jumpState.current = { + offsets, + start, + getItemElement, + getItemTop, + messages, + scrollToIndex + }; + + // Keep cursor-selected message visible. offsets rebuilds every render + // — as a bare dep this re-pinned on every mousewheel tick. Read through + // jumpState instead; past-overscan jumps land via scrollToIndex, next + // nav is precise. + useEffect(() => { + if (selectedIndex === undefined) return; + const s = jumpState.current; + const el = s.getItemElement(selectedIndex); + if (el) { + scrollRef.current?.scrollToElement(el, 1); + } else { + s.scrollToIndex(selectedIndex); + } + }, [selectedIndex, scrollRef]); + + // Pending seek request. jump() sets this + bumps seekGen. The seek + // effect fires post-paint (passive effect — after resetAfterCommit), + // checks if target is mounted. Yes → scan+highlight. No → re-estimate + // with a fresher anchor (start moved toward idx) and scrollTo again. + const scanRequestRef = useRef<{ + idx: number; + wantLast: boolean; + tries: number; + } | null>(null); + // Message-relative positions from scanElement. Row 0 = message top. + // Stable across scroll — highlight computes rowOffset fresh. msgIdx + // for computing rowOffset = getItemTop(msgIdx) - scrollTop. + const elementPositions = useRef<{ + msgIdx: number; + positions: MatchPosition[]; + }>({ + msgIdx: -1, + positions: [] + }); + // Wraparound guard. Auto-advance stops if ptr wraps back to here. + const startPtrRef = useRef(-1); + // Phantom-burst cap. Resets on scan success. + const phantomBurstRef = useRef(0); + // One-deep queue: n/N arriving mid-seek gets stored (not dropped) and + // fires after the seek completes. Holding n stays smooth without + // queueing 30 jumps. Latest press overwrites — we want the direction + // the user is going NOW, not where they were 10 keypresses ago. + const pendingStepRef = useRef<1 | -1 | 0>(0); + // step + highlight via ref so the seek effect reads latest without + // closure-capture or deps churn. + const stepRef = useRef<(d: 1 | -1) => void>(() => {}); + const highlightRef = useRef<(ord: number) => void>(() => {}); + const searchState = useRef({ + matches: [] as number[], + // deduplicated msg indices + ptr: 0, + screenOrd: 0, + // Cumulative engine-occurrence count before each matches[k]. Lets us + // compute a global current index: prefixSum[ptr] + screenOrd + 1. + // Engine-counted (indexOf on extractSearchText), not render-counted — + // close enough for the badge; exact counts would need scanElement on + // every matched message (~1-3ms × N). total = prefixSum[matches.length]. + prefixSum: [] as number[] + }); + // scrollTop at the moment / was pressed. Incsearch preview-jumps snap + // back here when matches drop to 0. -1 = no anchor (before first /). + const searchAnchor = useRef(-1); + const indexWarmed = useRef(false); + + // Scroll target for message i: land at MESSAGE TOP. est = top - HEADROOM + // so lo = top - est = HEADROOM ≥ 0 (or lo = top if est clamped to 0). + // Post-clamp read-back in jump() handles the scrollHeight boundary. + // No frac (render transform didn't respect it), no monotone clamp + // (was a safety net for frac garbage — without frac, est IS the next + // message's top, spam-n/N converges because message tops are ordered). + function targetFor(i: number): number { + const top = jumpState.current.getItemTop(i); + return Math.max(0, top - HEADROOM); + } + + // Highlight positions[ord]. Positions are MESSAGE-RELATIVE (row 0 = + // element top, from scanElement). Compute rowOffset = getItemTop - + // scrollTop fresh. If ord's position is off-viewport, scroll to bring + // it in, recompute rowOffset. setPositions triggers overlay write. + function highlight(ord: number): void { + const s = scrollRef.current; + const { + msgIdx, + positions + } = elementPositions.current; + if (!s || positions.length === 0 || msgIdx < 0) { + setPositions?.(null); + return; + } + const idx = Math.max(0, Math.min(ord, positions.length - 1)); + const p = positions[idx]!; + const top = jumpState.current.getItemTop(msgIdx); + // lo = item's position within scroll content (wrapper-relative). + // viewportTop = where the scroll content starts on SCREEN (after + // ScrollBox padding/border + any chrome above). Highlight writes to + // screen-absolute, so rowOffset = viewportTop + lo. Observed: off-by- + // 1+ without viewportTop (FullscreenLayout has paddingTop=1 on the + // ScrollBox, plus any header above). + const vpTop = s.getViewportTop(); + let lo = top - s.getScrollTop(); + const vp = s.getViewportHeight(); + let screenRow = vpTop + lo + p.row; + // Off viewport → scroll to bring it in (HEADROOM from top). + // scrollTo commits sync; read-back after gives fresh lo. + if (screenRow < vpTop || screenRow >= vpTop + vp) { + s.scrollTo(Math.max(0, top + p.row - HEADROOM)); + lo = top - s.getScrollTop(); + screenRow = vpTop + lo + p.row; + } + setPositions?.({ + positions, + rowOffset: vpTop + lo, + currentIdx: idx + }); + // Badge: global current = sum of occurrences before this msg + ord+1. + // prefixSum[ptr] is engine-counted (indexOf on extractSearchText); + // may drift from render-count for ghost messages but close enough — + // badge is a rough location hint, not a proof. + const st = searchState.current; + const total = st.prefixSum.at(-1) ?? 0; + const current = (st.prefixSum[st.ptr] ?? 0) + idx + 1; + onSearchMatchesChange?.(total, current); + logForDebugging(`highlight(i=${msgIdx}, ord=${idx}/${positions.length}): ` + `pos={row:${p.row},col:${p.col}} lo=${lo} screenRow=${screenRow} ` + `badge=${current}/${total}`); + } + highlightRef.current = highlight; + + // Seek effect. jump() sets scanRequestRef + scrollToIndex + bump. + // bump → re-render → useVirtualScroll mounts the target (scrollToIndex + // guarantees this — scrollTop and topSpacer agree via the same + // offsets value) → resetAfterCommit paints → this passive effect + // fires POST-PAINT with the element mounted. Precise scrollTo + scan. + // + // Dep is ONLY seekGen — effect doesn't re-run on random renders + // (onSearchMatchesChange churn during incsearch). + const [seekGen, setSeekGen] = useState(0); + const bumpSeek = useCallback(() => setSeekGen(g => g + 1), []); + useEffect(() => { + const req = scanRequestRef.current; + if (!req) return; + const { + idx, + wantLast, + tries + } = req; + const s = scrollRef.current; + if (!s) return; + const { + getItemElement, + getItemTop, + scrollToIndex + } = jumpState.current; + const el = getItemElement(idx); + const h = el?.yogaNode?.getComputedHeight() ?? 0; + if (!el || h === 0) { + // Not mounted after scrollToIndex. Shouldn't happen — scrollToIndex + // guarantees mount by construction (scrollTop and topSpacer agree + // via the same offsets value). Sanity: retry once, then skip. + if (tries > 1) { + scanRequestRef.current = null; + logForDebugging(`seek(i=${idx}): no mount after scrollToIndex, skip`); + stepRef.current(wantLast ? -1 : 1); + return; + } + scanRequestRef.current = { + idx, + wantLast, + tries: tries + 1 + }; + scrollToIndex(idx); + bumpSeek(); + return; + } + scanRequestRef.current = null; + // Precise scrollTo — scrollToIndex got us in the neighborhood + // (item is mounted, maybe a few-dozen rows off due to overscan + // estimate drift). Now land it at top-HEADROOM. + s.scrollTo(Math.max(0, getItemTop(idx) - HEADROOM)); + const positions = scanElement?.(el) ?? []; + elementPositions.current = { + msgIdx: idx, + positions + }; + logForDebugging(`seek(i=${idx} t=${tries}): ${positions.length} positions`); + if (positions.length === 0) { + // Phantom — engine matched, render didn't. Auto-advance. + if (++phantomBurstRef.current > 20) { + phantomBurstRef.current = 0; + return; + } + stepRef.current(wantLast ? -1 : 1); + return; + } + phantomBurstRef.current = 0; + const ord = wantLast ? positions.length - 1 : 0; + searchState.current.screenOrd = ord; + startPtrRef.current = -1; + highlightRef.current(ord); + const pending = pendingStepRef.current; + if (pending) { + pendingStepRef.current = 0; + stepRef.current(pending); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [seekGen]); + + // Scroll to message i's top, arm scanPending. scan-effect reads fresh + // screen next tick. wantLast: N-into-message — screenOrd = length-1. + function jump(i: number, wantLast: boolean): void { + const s = scrollRef.current; + if (!s) return; + const js = jumpState.current; + const { + getItemElement, + scrollToIndex + } = js; + // offsets is a Float64Array whose .length is the allocated buffer (only + // grows) — messages.length is the logical item count. + if (i < 0 || i >= js.messages.length) return; + // Clear stale highlight before scroll. Between now and the seek + // effect's highlight, inverse-only from scan-highlight shows. + setPositions?.(null); + elementPositions.current = { + msgIdx: -1, + positions: [] + }; + scanRequestRef.current = { + idx: i, + wantLast, + tries: 0 + }; + const el = getItemElement(i); + const h = el?.yogaNode?.getComputedHeight() ?? 0; + // Mounted → precise scrollTo. Unmounted → scrollToIndex mounts it + // (scrollTop and topSpacer agree via the same offsets value — exact + // by construction, no estimation). Seek effect does the precise + // scrollTo after paint either way. + if (el && h > 0) { + s.scrollTo(targetFor(i)); + } else { + scrollToIndex(i); + } + bumpSeek(); + } + + // Advance screenOrd within elementPositions. Exhausted → ptr advances, + // jump to next matches[ptr], re-scan. Phantom (scan found 0 after + // jump) triggers auto-advance from scan-effect. Wraparound guard stops + // if every message is a phantom. + function step(delta: 1 | -1): void { + const st = searchState.current; + const { + matches, + prefixSum + } = st; + const total = prefixSum.at(-1) ?? 0; + if (matches.length === 0) return; + + // Seek in-flight — queue this press (one-deep, latest overwrites). + // The seek effect fires it after highlight. + if (scanRequestRef.current) { + pendingStepRef.current = delta; + return; + } + if (startPtrRef.current < 0) startPtrRef.current = st.ptr; + const { + positions + } = elementPositions.current; + const newOrd = st.screenOrd + delta; + if (newOrd >= 0 && newOrd < positions.length) { + st.screenOrd = newOrd; + highlight(newOrd); // updates badge internally + startPtrRef.current = -1; + return; + } + + // Exhausted visible. Advance ptr → jump → re-scan. + const ptr = (st.ptr + delta + matches.length) % matches.length; + if (ptr === startPtrRef.current) { + setPositions?.(null); + startPtrRef.current = -1; + logForDebugging(`step: wraparound at ptr=${ptr}, all ${matches.length} msgs phantoms`); + return; + } + st.ptr = ptr; + st.screenOrd = 0; // resolved after scan (wantLast → length-1) + jump(matches[ptr]!, delta < 0); + // screenOrd will resolve after scan. Best-effort: prefixSum[ptr] + 0 + // for n (first pos), prefixSum[ptr+1] for N (last pos = count-1). + // The scan-effect's highlight will be the real value; this is a + // pre-scan placeholder so the badge updates immediately. + const placeholder = delta < 0 ? prefixSum[ptr + 1] ?? total : prefixSum[ptr]! + 1; + onSearchMatchesChange?.(total, placeholder); + } + stepRef.current = step; + useImperativeHandle(jumpRef, () => ({ + // Non-search jump (sticky header click, etc). No scan, no positions. + jumpToIndex: (i: number) => { + const s = scrollRef.current; + if (s) s.scrollTo(targetFor(i)); + }, + setSearchQuery: (q: string) => { + // New search invalidates everything. + scanRequestRef.current = null; + elementPositions.current = { + msgIdx: -1, + positions: [] + }; + startPtrRef.current = -1; + setPositions?.(null); + const lq = q.toLowerCase(); + // One entry per MESSAGE (deduplicated). Boolean "does this msg + // contain the query". ~10ms for 9k messages with cached lowered. + const matches: number[] = []; + // Per-message occurrence count → prefixSum for global current + // index. Engine-counted (cheap indexOf loop); may differ from + // render-count (scanElement) for ghost/phantom messages but close + // enough for the badge. The badge is a rough location hint. + const prefixSum: number[] = [0]; + if (lq) { + const msgs = jumpState.current.messages; + for (let i = 0; i < msgs.length; i++) { + const text = extractSearchText(msgs[i]!); + let pos = text.indexOf(lq); + let cnt = 0; + while (pos >= 0) { + cnt++; + pos = text.indexOf(lq, pos + lq.length); + } + if (cnt > 0) { + matches.push(i); + prefixSum.push(prefixSum.at(-1)! + cnt); + } + } + } + const total = prefixSum.at(-1)!; + // Nearest MESSAGE to the anchor. <= so ties go to later. + let ptr = 0; + const s = scrollRef.current; + const { + offsets, + start, + getItemTop + } = jumpState.current; + const firstTop = getItemTop(start); + const origin = firstTop >= 0 ? firstTop - offsets[start]! : 0; + if (matches.length > 0 && s) { + const curTop = searchAnchor.current >= 0 ? searchAnchor.current : s.getScrollTop(); + let best = Infinity; + for (let k = 0; k < matches.length; k++) { + const d = Math.abs(origin + offsets[matches[k]!]! - curTop); + if (d <= best) { + best = d; + ptr = k; + } + } + logForDebugging(`setSearchQuery('${q}'): ${matches.length} msgs · ptr=${ptr} ` + `msgIdx=${matches[ptr]} curTop=${curTop} origin=${origin}`); + } + searchState.current = { + matches, + ptr, + screenOrd: 0, + prefixSum + }; + if (matches.length > 0) { + // wantLast=true: preview the LAST occurrence in the nearest + // message. At sticky-bottom (common / entry), nearest is the + // last msg; its last occurrence is closest to where the user + // was — minimal view movement. n advances forward from there. + jump(matches[ptr]!, true); + } else if (searchAnchor.current >= 0 && s) { + // /foob → 0 matches → snap back to anchor. less/vim incsearch. + s.scrollTo(searchAnchor.current); + } + // Global occurrence count + 1-based current. wantLast=true so the + // scan will land on the last occurrence in matches[ptr]. Placeholder + // = prefixSum[ptr+1] (count through this msg). highlight() updates + // to the exact value after scan completes. + onSearchMatchesChange?.(total, matches.length > 0 ? prefixSum[ptr + 1] ?? total : 0); + }, + nextMatch: () => step(1), + prevMatch: () => step(-1), + setAnchor: () => { + const s = scrollRef.current; + if (s) searchAnchor.current = s.getScrollTop(); + }, + disarmSearch: () => { + // Manual scroll invalidates screen-absolute positions. + setPositions?.(null); + scanRequestRef.current = null; + elementPositions.current = { + msgIdx: -1, + positions: [] + }; + startPtrRef.current = -1; + }, + warmSearchIndex: async () => { + if (indexWarmed.current) return 0; + const msgs = jumpState.current.messages; + const CHUNK = 500; + let workMs = 0; + const wallStart = performance.now(); + for (let i = 0; i < msgs.length; i += CHUNK) { + await sleep(0); + const t0 = performance.now(); + const end = Math.min(i + CHUNK, msgs.length); + for (let j = i; j < end; j++) { + extractSearchText(msgs[j]!); + } + workMs += performance.now() - t0; + } + const wallMs = Math.round(performance.now() - wallStart); + logForDebugging(`warmSearchIndex: ${msgs.length} msgs · work=${Math.round(workMs)}ms wall=${wallMs}ms chunks=${Math.ceil(msgs.length / CHUNK)}`); + indexWarmed.current = true; + return Math.round(workMs); + } + }), + // Closures over refs + callbacks. scrollRef stable; others are + // useCallback([]) or prop-drilled from REPL (stable). + // eslint-disable-next-line react-hooks/exhaustive-deps + [scrollRef]); + + // StickyTracker goes AFTER the list content. It returns null (no DOM node) + // so order shouldn't matter for layout — but putting it first means every + // fine-grained commit from its own scroll subscription reconciles THROUGH + // the sibling items (React walks children in order). After the items, it's + // a leaf reconcile. Defensive: also avoids any Yoga child-index quirks if + // the Ink reconciler ever materializes a placeholder for null returns. + const [hoveredKey, setHoveredKey] = useState(null); + // Stable click/hover handlers — called with k, dispatch from a ref so + // closure identity doesn't change per render. The per-item handler + // closures (`e => ...`, `() => setHoveredKey(k)`) were the + // `operationNewArrowFunction` leafs in the scroll CPU profile; their + // cleanup was 16% of GC time (`FunctionExecutable::finalizeUnconditionally`). + // Allocating 3 closures × 60 mounted items × 10 commits/sec during fast + // scroll = 1800 short-lived closures/sec. With stable refs the item + // wrapper props don't change → VirtualItem.memo bails for the ~35 + // unchanged items, only ~25 fresh items pay createElement cost. + const handlersRef = useRef({ + onItemClick, + setHoveredKey + }); + handlersRef.current = { + onItemClick, + setHoveredKey + }; + const onClickK = useCallback((msg: RenderableMessage, cellIsBlank: boolean) => { + const h = handlersRef.current; + if (!cellIsBlank && h.onItemClick) h.onItemClick(msg); + }, []); + const onEnterK = useCallback((k: string) => { + handlersRef.current.setHoveredKey(k); + }, []); + const onLeaveK = useCallback((k: string) => { + handlersRef.current.setHoveredKey(prev => prev === k ? null : prev); + }, []); + return <> + + {messages.slice(start, end).map((msg, i) => { + const idx = start + i; + const k = keys[idx]!; + const clickable = !!onItemClick && (isItemClickable?.(msg) ?? true); + const hovered = clickable && hoveredKey === k; + const expanded = isItemExpanded?.(msg); + return ; + })} + {bottomSpacer > 0 && } + {trackStickyPrompt && } + ; +} +const NOOP_UNSUB = () => {}; + +/** + * Effect-only child that tracks the last user-prompt scrolled above the + * viewport top and fires onChange when it changes. + * + * Rendered as a separate component (not a hook in VirtualMessageList) so it + * can subscribe to scroll at FINER granularity than SCROLL_QUANTUM=40. The + * list needs the coarse quantum to avoid per-wheel-tick Yoga relayouts; this + * tracker is just a walk + comparison and can afford to run every tick. When + * it re-renders alone, the list's reconciled output is unchanged (same props + * from the parent's last commit) — no Yoga work. Without this split, the + * header lags by ~one conversation turn (40 rows ≈ one prompt + response). + * + * firstVisible derivation: item Boxes are direct Yoga children of the + * ScrollBox content wrapper (fragments collapse in the Ink DOM), so + * yoga.getComputedTop is content-wrapper-relative — same coordinate space as + * scrollTop. Compare against scrollTop + pendingDelta (the scroll TARGET — + * scrollBy only sets pendingDelta, committed scrollTop lags). Walk backward + * from the mount-range end; break when an item's top is above target. + */ +function StickyTracker({ + messages, + start, + end, + offsets, + getItemTop, + getItemElement, + scrollRef +}: { + messages: RenderableMessage[]; + start: number; + end: number; + offsets: ArrayLike; + getItemTop: (index: number) => number; + getItemElement: (index: number) => DOMElement | null; + scrollRef: RefObject; +}): null { + const { + setStickyPrompt + } = useContext(ScrollChromeContext); + // Fine-grained subscription — snapshot is unquantized scrollTop+delta so + // every scroll action (wheel tick, PgUp, drag) triggers a re-render of + // THIS component only. Sticky bit folded into the sign so sticky→broken + // also triggers (scrollToBottom sets sticky without moving scrollTop). + const subscribe = useCallback((listener: () => void) => scrollRef.current?.subscribe(listener) ?? NOOP_UNSUB, [scrollRef]); + useSyncExternalStore(subscribe, () => { + const s = scrollRef.current; + if (!s) return NaN; + const t = s.getScrollTop() + s.getPendingDelta(); + return s.isSticky() ? -1 - t : t; + }); + + // Read live scroll state on every render. + const isSticky = scrollRef.current?.isSticky() ?? true; + const target = Math.max(0, (scrollRef.current?.getScrollTop() ?? 0) + (scrollRef.current?.getPendingDelta() ?? 0)); + + // Walk the mounted range to find the first item at-or-below the viewport + // top. `range` is from the parent's coarse-quantum render (may be slightly + // stale) but overscan guarantees it spans well past the viewport in both + // directions. Items without a Yoga layout yet (newly mounted this frame) + // are treated as at-or-below — they're somewhere in view, and assuming + // otherwise would show a sticky for a prompt that's actually on screen. + let firstVisible = start; + let firstVisibleTop = -1; + for (let i = end - 1; i >= start; i--) { + const top = getItemTop(i); + if (top >= 0) { + if (top < target) break; + firstVisibleTop = top; + } + firstVisible = i; + } + let idx = -1; + let text: string | null = null; + if (firstVisible > 0 && !isSticky) { + for (let i = firstVisible - 1; i >= 0; i--) { + const t = stickyPromptText(messages[i]!); + if (t === null) continue; + // The prompt's wrapping Box top is above target (that's why it's in + // the [0, firstVisible) range), but its ❯ is at top+1 (marginTop=1). + // If the ❯ is at-or-below target, it's VISIBLE at viewport top — + // showing the same text in the header would duplicate it. Happens + // in the 1-row gap between Box top scrolling past and ❯ scrolling + // past. Skip to the next-older prompt (its ❯ is definitely above). + const top = getItemTop(i); + if (top >= 0 && top + 1 >= target) continue; + idx = i; + text = t; + break; + } + } + const baseOffset = firstVisibleTop >= 0 ? firstVisibleTop - offsets[firstVisible]! : 0; + const estimate = idx >= 0 ? Math.max(0, baseOffset + offsets[idx]!) : -1; + + // For click-jumps to items not yet mounted (user scrolled far past, + // prompt is in the topSpacer). Click handler scrolls to the estimate + // to mount it; this anchors by element once it appears. scrollToElement + // defers the Yoga-position read to render time (render-node-to-output + // reads el.yogaNode.getComputedTop() in the SAME calculateLayout pass + // that produces scrollHeight) — no throttle race. Cap retries: a /clear + // race could unmount the item mid-sequence. + const pending = useRef({ + idx: -1, + tries: 0 + }); + // Suppression state machine. The click handler arms; the onChange effect + // consumes (armed→force) then fires-and-clears on the render AFTER that + // (force→none). The force step poisons the dedup: after click, idx often + // recomputes to the SAME prompt (its top is still above target), so + // without force the last.idx===idx guard would hold 'clicked' until the + // user crossed a prompt boundary. Previously encoded in last.idx as + // -1/-2/-3 which overlapped with real indices — too clever. + type Suppress = 'none' | 'armed' | 'force'; + const suppress = useRef('none'); + // Dedup on idx only — estimate derives from firstVisibleTop which shifts + // every scroll tick, so including it in the key made the guard dead + // (setStickyPrompt fired a fresh {text,scrollTo} per-frame). The scrollTo + // closure still captures the current estimate; it just doesn't need to + // re-fire when only estimate moved. + const lastIdx = useRef(-1); + + // setStickyPrompt effect FIRST — must see pending.idx before the + // correction effect below clears it. On the estimate-fallback path, the + // render that mounts the item is ALSO the render where correction clears + // pending; if this ran second, the pending gate would be dead and + // setStickyPrompt(prevPrompt) would fire mid-jump, re-mounting the + // header over 'clicked'. + useEffect(() => { + // Hold while two-phase correction is in flight. + if (pending.current.idx >= 0) return; + if (suppress.current === 'armed') { + suppress.current = 'force'; + return; + } + const force = suppress.current === 'force'; + suppress.current = 'none'; + if (!force && lastIdx.current === idx) return; + lastIdx.current = idx; + if (text === null) { + setStickyPrompt(null); + return; + } + // First paragraph only (split on blank line) — a prompt like + // "still seeing bugs:\n\n1. foo\n2. bar" previews as just the + // lead-in. trimStart so a leading blank line (queued_command mid- + // turn messages sometimes have one) doesn't find paraEnd at 0. + const trimmed = text.trimStart(); + const paraEnd = trimmed.search(/\n\s*\n/); + const collapsed = (paraEnd >= 0 ? trimmed.slice(0, paraEnd) : trimmed).slice(0, STICKY_TEXT_CAP).replace(/\s+/g, ' ').trim(); + if (collapsed === '') { + setStickyPrompt(null); + return; + } + const capturedIdx = idx; + const capturedEstimate = estimate; + setStickyPrompt({ + text: collapsed, + scrollTo: () => { + // Hide header, keep padding collapsed — FullscreenLayout's + // 'clicked' sentinel → scrollBox_y=0 + pad=0 → viewportTop=0. + setStickyPrompt('clicked'); + suppress.current = 'armed'; + // scrollToElement anchors by DOMElement ref, not a number: + // render-node-to-output reads el.yogaNode.getComputedTop() at + // paint time (same Yoga pass as scrollHeight). No staleness from + // the throttled render — the ref is stable, the position read is + // deferred. offset=1 = UserPromptMessage marginTop. + const el = getItemElement(capturedIdx); + if (el) { + scrollRef.current?.scrollToElement(el, 1); + } else { + // Not mounted (scrolled far past — in topSpacer). Jump to + // estimate to mount it; correction effect re-anchors once it + // appears. Estimate is DEFAULT_ESTIMATE-based — lands short. + scrollRef.current?.scrollTo(capturedEstimate); + pending.current = { + idx: capturedIdx, + tries: 0 + }; + } + } + }); + // No deps — must run every render. Suppression state lives in a ref + // (not idx/estimate), so a deps-gated effect would never see it tick. + // Body's own guards short-circuit when nothing changed. + // eslint-disable-next-line react-hooks/exhaustive-deps + }); + + // Correction: for click-jumps to unmounted items. Click handler scrolled + // to the estimate; this re-anchors by element once the item appears. + // scrollToElement defers the Yoga read to paint time — deterministic. + // SECOND so it clears pending AFTER the onChange gate above has seen it. + useEffect(() => { + if (pending.current.idx < 0) return; + const el = getItemElement(pending.current.idx); + if (el) { + scrollRef.current?.scrollToElement(el, 1); + pending.current = { + idx: -1, + tries: 0 + }; + } else if (++pending.current.tries > 5) { + pending.current = { + idx: -1, + tries: 0 + }; + } + }); + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWZPYmplY3QiLCJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlQ29udGV4dCIsInVzZUVmZmVjdCIsInVzZUltcGVyYXRpdmVIYW5kbGUiLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsInVzZVN5bmNFeHRlcm5hbFN0b3JlIiwidXNlVmlydHVhbFNjcm9sbCIsIlNjcm9sbEJveEhhbmRsZSIsIkRPTUVsZW1lbnQiLCJNYXRjaFBvc2l0aW9uIiwiQm94IiwiUmVuZGVyYWJsZU1lc3NhZ2UiLCJUZXh0SG92ZXJDb2xvckNvbnRleHQiLCJTY3JvbGxDaHJvbWVDb250ZXh0IiwiSEVBRFJPT00iLCJsb2dGb3JEZWJ1Z2dpbmciLCJzbGVlcCIsInJlbmRlcmFibGVTZWFyY2hUZXh0IiwiaXNOYXZpZ2FibGVNZXNzYWdlIiwiTWVzc2FnZUFjdGlvbnNOYXYiLCJNZXNzYWdlQWN0aW9uc1N0YXRlIiwiTmF2aWdhYmxlTWVzc2FnZSIsInN0cmlwU3lzdGVtUmVtaW5kZXJzIiwidG9vbENhbGxPZiIsImZhbGxiYWNrTG93ZXJDYWNoZSIsIldlYWtNYXAiLCJkZWZhdWx0RXh0cmFjdFNlYXJjaFRleHQiLCJtc2ciLCJjYWNoZWQiLCJnZXQiLCJ1bmRlZmluZWQiLCJsb3dlcmVkIiwic2V0IiwiU3RpY2t5UHJvbXB0IiwidGV4dCIsInNjcm9sbFRvIiwiU1RJQ0tZX1RFWFRfQ0FQIiwiSnVtcEhhbmRsZSIsImp1bXBUb0luZGV4IiwiaSIsInNldFNlYXJjaFF1ZXJ5IiwicSIsIm5leHRNYXRjaCIsInByZXZNYXRjaCIsInNldEFuY2hvciIsIndhcm1TZWFyY2hJbmRleCIsIlByb21pc2UiLCJkaXNhcm1TZWFyY2giLCJQcm9wcyIsIm1lc3NhZ2VzIiwic2Nyb2xsUmVmIiwiY29sdW1ucyIsIml0ZW1LZXkiLCJyZW5kZXJJdGVtIiwiaW5kZXgiLCJSZWFjdE5vZGUiLCJvbkl0ZW1DbGljayIsImlzSXRlbUNsaWNrYWJsZSIsImlzSXRlbUV4cGFuZGVkIiwiZXh0cmFjdFNlYXJjaFRleHQiLCJ0cmFja1N0aWNreVByb21wdCIsInNlbGVjdGVkSW5kZXgiLCJjdXJzb3JOYXZSZWYiLCJSZWYiLCJzZXRDdXJzb3IiLCJjIiwianVtcFJlZiIsIm9uU2VhcmNoTWF0Y2hlc0NoYW5nZSIsImNvdW50IiwiY3VycmVudCIsInNjYW5FbGVtZW50IiwiZWwiLCJzZXRQb3NpdGlvbnMiLCJzdGF0ZSIsInBvc2l0aW9ucyIsInJvd09mZnNldCIsImN1cnJlbnRJZHgiLCJwcm9tcHRUZXh0Q2FjaGUiLCJzdGlja3lQcm9tcHRUZXh0IiwicmVzdWx0IiwiY29tcHV0ZVN0aWNreVByb21wdFRleHQiLCJyYXciLCJ0eXBlIiwiaXNNZXRhIiwiaXNWaXNpYmxlSW5UcmFuc2NyaXB0T25seSIsImJsb2NrIiwibWVzc2FnZSIsImNvbnRlbnQiLCJhdHRhY2htZW50IiwiY29tbWFuZE1vZGUiLCJwIiwicHJvbXB0IiwiZmxhdE1hcCIsImIiLCJqb2luIiwidCIsInN0YXJ0c1dpdGgiLCJWaXJ0dWFsSXRlbVByb3BzIiwiaWR4IiwibWVhc3VyZVJlZiIsImtleSIsImV4cGFuZGVkIiwiaG92ZXJlZCIsImNsaWNrYWJsZSIsIm9uQ2xpY2tLIiwiY2VsbElzQmxhbmsiLCJvbkVudGVySyIsImsiLCJvbkxlYXZlSyIsIlZpcnR1YWxJdGVtIiwidDAiLCIkIiwiX2MiLCJ0MSIsInQyIiwidDMiLCJ0NCIsImUiLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5IiwidDEwIiwiVmlydHVhbE1lc3NhZ2VMaXN0Iiwia2V5c1JlZiIsInByZXZNZXNzYWdlc1JlZiIsInByZXZJdGVtS2V5UmVmIiwibGVuZ3RoIiwibWFwIiwibSIsInB1c2giLCJrZXlzIiwicmFuZ2UiLCJ0b3BTcGFjZXIiLCJib3R0b21TcGFjZXIiLCJzcGFjZXJSZWYiLCJvZmZzZXRzIiwiZ2V0SXRlbVRvcCIsImdldEl0ZW1FbGVtZW50IiwiZ2V0SXRlbUhlaWdodCIsInNjcm9sbFRvSW5kZXgiLCJzdGFydCIsImVuZCIsImlzVmlzaWJsZSIsImgiLCJzZWxlY3QiLCJ1dWlkIiwibXNnVHlwZSIsInRvb2xOYW1lIiwibmFtZSIsInNlbElkeCIsInNjYW4iLCJmcm9tIiwiZGlyIiwicHJlZCIsImlzVXNlciIsImVudGVyQ3Vyc29yIiwibmF2aWdhdGVQcmV2IiwibmF2aWdhdGVOZXh0Iiwic2Nyb2xsVG9Cb3R0b20iLCJuYXZpZ2F0ZVByZXZVc2VyIiwibmF2aWdhdGVOZXh0VXNlciIsIm5hdmlnYXRlVG9wIiwibmF2aWdhdGVCb3R0b20iLCJnZXRTZWxlY3RlZCIsImp1bXBTdGF0ZSIsInMiLCJzY3JvbGxUb0VsZW1lbnQiLCJzY2FuUmVxdWVzdFJlZiIsIndhbnRMYXN0IiwidHJpZXMiLCJlbGVtZW50UG9zaXRpb25zIiwibXNnSWR4Iiwic3RhcnRQdHJSZWYiLCJwaGFudG9tQnVyc3RSZWYiLCJwZW5kaW5nU3RlcFJlZiIsInN0ZXBSZWYiLCJkIiwiaGlnaGxpZ2h0UmVmIiwib3JkIiwic2VhcmNoU3RhdGUiLCJtYXRjaGVzIiwicHRyIiwic2NyZWVuT3JkIiwicHJlZml4U3VtIiwic2VhcmNoQW5jaG9yIiwiaW5kZXhXYXJtZWQiLCJ0YXJnZXRGb3IiLCJ0b3AiLCJNYXRoIiwibWF4IiwiaGlnaGxpZ2h0IiwibWluIiwidnBUb3AiLCJnZXRWaWV3cG9ydFRvcCIsImxvIiwiZ2V0U2Nyb2xsVG9wIiwidnAiLCJnZXRWaWV3cG9ydEhlaWdodCIsInNjcmVlblJvdyIsInJvdyIsInN0IiwidG90YWwiLCJhdCIsImNvbCIsInNlZWtHZW4iLCJzZXRTZWVrR2VuIiwiYnVtcFNlZWsiLCJnIiwicmVxIiwieW9nYU5vZGUiLCJnZXRDb21wdXRlZEhlaWdodCIsInBlbmRpbmciLCJqdW1wIiwianMiLCJzdGVwIiwiZGVsdGEiLCJuZXdPcmQiLCJwbGFjZWhvbGRlciIsImxxIiwidG9Mb3dlckNhc2UiLCJtc2dzIiwicG9zIiwiaW5kZXhPZiIsImNudCIsImZpcnN0VG9wIiwib3JpZ2luIiwiY3VyVG9wIiwiYmVzdCIsIkluZmluaXR5IiwiYWJzIiwiQ0hVTksiLCJ3b3JrTXMiLCJ3YWxsU3RhcnQiLCJwZXJmb3JtYW5jZSIsIm5vdyIsImoiLCJ3YWxsTXMiLCJyb3VuZCIsImNlaWwiLCJob3ZlcmVkS2V5Iiwic2V0SG92ZXJlZEtleSIsImhhbmRsZXJzUmVmIiwicHJldiIsInNsaWNlIiwiTk9PUF9VTlNVQiIsIlN0aWNreVRyYWNrZXIiLCJBcnJheUxpa2UiLCJzZXRTdGlja3lQcm9tcHQiLCJzdWJzY3JpYmUiLCJsaXN0ZW5lciIsIk5hTiIsImdldFBlbmRpbmdEZWx0YSIsImlzU3RpY2t5IiwidGFyZ2V0IiwiZmlyc3RWaXNpYmxlIiwiZmlyc3RWaXNpYmxlVG9wIiwiYmFzZU9mZnNldCIsImVzdGltYXRlIiwiU3VwcHJlc3MiLCJzdXBwcmVzcyIsImxhc3RJZHgiLCJmb3JjZSIsInRyaW1tZWQiLCJ0cmltU3RhcnQiLCJwYXJhRW5kIiwic2VhcmNoIiwiY29sbGFwc2VkIiwicmVwbGFjZSIsInRyaW0iLCJjYXB0dXJlZElkeCIsImNhcHR1cmVkRXN0aW1hdGUiXSwic291cmNlcyI6WyJWaXJ0dWFsTWVzc2FnZUxpc3QudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgUmVmT2JqZWN0IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7XG4gIHVzZUNhbGxiYWNrLFxuICB1c2VDb250ZXh0LFxuICB1c2VFZmZlY3QsXG4gIHVzZUltcGVyYXRpdmVIYW5kbGUsXG4gIHVzZVJlZixcbiAgdXNlU3RhdGUsXG4gIHVzZVN5bmNFeHRlcm5hbFN0b3JlLFxufSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZVZpcnR1YWxTY3JvbGwgfSBmcm9tICcuLi9ob29rcy91c2VWaXJ0dWFsU2Nyb2xsLmpzJ1xuaW1wb3J0IHR5cGUgeyBTY3JvbGxCb3hIYW5kbGUgfSBmcm9tICcuLi9pbmsvY29tcG9uZW50cy9TY3JvbGxCb3guanMnXG5pbXBvcnQgdHlwZSB7IERPTUVsZW1lbnQgfSBmcm9tICcuLi9pbmsvZG9tLmpzJ1xuaW1wb3J0IHR5cGUgeyBNYXRjaFBvc2l0aW9uIH0gZnJvbSAnLi4vaW5rL3JlbmRlci10by1zY3JlZW4uanMnXG5pbXBvcnQgeyBCb3ggfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IFJlbmRlcmFibGVNZXNzYWdlIH0gZnJvbSAnLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IFRleHRIb3ZlckNvbG9yQ29udGV4dCB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9UaGVtZWRUZXh0LmpzJ1xuaW1wb3J0IHsgU2Nyb2xsQ2hyb21lQ29udGV4dCB9IGZyb20gJy4vRnVsbHNjcmVlbkxheW91dC5qcydcblxuLy8gUm93cyBvZiBicmVhdGhpbmcgcm9vbSBhYm92ZSB0aGUgdGFyZ2V0IHdoZW4gd2Ugc2Nyb2xsVG8uXG5jb25zdCBIRUFEUk9PTSA9IDNcblxuaW1wb3J0IHsgbG9nRm9yRGVidWdnaW5nIH0gZnJvbSAnLi4vdXRpbHMvZGVidWcuanMnXG5pbXBvcnQgeyBzbGVlcCB9IGZyb20gJy4uL3V0aWxzL3NsZWVwLmpzJ1xuaW1wb3J0IHsgcmVuZGVyYWJsZVNlYXJjaFRleHQgfSBmcm9tICcuLi91dGlscy90cmFuc2NyaXB0U2VhcmNoLmpzJ1xuaW1wb3J0IHtcbiAgaXNOYXZpZ2FibGVNZXNzYWdlLFxuICB0eXBlIE1lc3NhZ2VBY3Rpb25zTmF2LFxuICB0eXBlIE1lc3NhZ2VBY3Rpb25zU3RhdGUsXG4gIHR5cGUgTmF2aWdhYmxlTWVzc2FnZSxcbiAgc3RyaXBTeXN0ZW1SZW1pbmRlcnMsXG4gIHRvb2xDYWxsT2YsXG59IGZyb20gJy4vbWVzc2FnZUFjdGlvbnMuanMnXG5cbi8vIEZhbGxiYWNrIGV4dHJhY3RvcjogbG93ZXIgKyBjYWNoZSBoZXJlIGZvciBjYWxsZXJzIHdpdGhvdXQgdGhlXG4vLyBNZXNzYWdlcy50c3ggdG9vbC1sb29rdXAgcGF0aCAodGVzdHMsIHN0YXRpYyBjb250ZXh0cykuIE1lc3NhZ2VzLnRzeFxuLy8gcHJvdmlkZXMgaXRzIG93biBsb3dlcmluZyBjYWNoZSB0aGF0IGFsc28gaGFuZGxlcyB0b29sIGV4dHJhY3RTZWFyY2hUZXh0LlxuY29uc3QgZmFsbGJhY2tMb3dlckNhY2hlID0gbmV3IFdlYWtNYXA8UmVuZGVyYWJsZU1lc3NhZ2UsIHN0cmluZz4oKVxuZnVuY3Rpb24gZGVmYXVsdEV4dHJhY3RTZWFyY2hUZXh0KG1zZzogUmVuZGVyYWJsZU1lc3NhZ2UpOiBzdHJpbmcge1xuICBjb25zdCBjYWNoZWQgPSBmYWxsYmFja0xvd2VyQ2FjaGUuZ2V0KG1zZylcbiAgaWYgKGNhY2hlZCAhPT0gdW5kZWZpbmVkKSByZXR1cm4gY2FjaGVkXG4gIGNvbnN0IGxvd2VyZWQgPSByZW5kZXJhYmxlU2VhcmNoVGV4dChtc2cpXG4gIGZhbGxiYWNrTG93ZXJDYWNoZS5zZXQobXNnLCBsb3dlcmVkKVxuICByZXR1cm4gbG93ZXJlZFxufVxuXG5leHBvcnQgdHlwZSBTdGlja3lQcm9tcHQgPVxuICB8IHsgdGV4dDogc3RyaW5nOyBzY3JvbGxUbzogKCkgPT4gdm9pZCB9XG4gIC8vIENsaWNrIHNldHMgdGhpcyDigJQgaGVhZGVyIEhJREVTIGJ1dCBwYWRkaW5nIHN0YXlzIGNvbGxhcHNlZCAoMCkgc29cbiAgLy8gdGhlIGNvbnRlbnQg4p2vIGxhbmRzIGF0IHNjcmVlbiByb3cgMCBpbnN0ZWFkIG9mIHJvdyAxLiBDbGVhcmVkIG9uXG4gIC8vIHRoZSBuZXh0IHN0aWNreS1wcm9tcHQgY29tcHV0ZSAodXNlciBzY3JvbGxzIGFnYWluKS5cbiAgfCAnY2xpY2tlZCdcblxuLyoqIEh1Z2UgcGFzdGVkIHByb21wdHMgKGNhdCBmaWxlIHwgY2xhdWRlKSBjYW4gYmUgTUJzLiBIZWFkZXIgd3JhcHMgaW50b1xuICogIDIgcm93cyB2aWEgb3ZlcmZsb3c6aGlkZGVuIOKAlCB0aGlzIGp1c3QgYm91bmRzIHRoZSBSZWFjdCBwcm9wIHNpemUuICovXG5jb25zdCBTVElDS1lfVEVYVF9DQVAgPSA1MDBcblxuLyoqIEltcGVyYXRpdmUgaGFuZGxlIGZvciB0cmFuc2NyaXB0IG5hdmlnYXRpb24uIE1ldGhvZHMgY29tcHV0ZSBtYXRjaGVzXG4gKiAgSEVSRSAocmVuZGVyYWJsZU1lc3NhZ2VzIGluZGljZXMgYXJlIG9ubHkgdmFsaWQgaW5zaWRlIHRoaXMgY29tcG9uZW50IOKAlFxuICogIE1lc3NhZ2VzLnRzeCBmaWx0ZXJzIGFuZCByZW9yZGVycywgUkVQTCBjYW4ndCBjb21wdXRlIGV4dGVybmFsbHkpLiAqL1xuZXhwb3J0IHR5cGUgSnVtcEhhbmRsZSA9IHtcbiAganVtcFRvSW5kZXg6IChpOiBudW1iZXIpID0+IHZvaWRcbiAgc2V0U2VhcmNoUXVlcnk6IChxOiBzdHJpbmcpID0+IHZvaWRcbiAgbmV4dE1hdGNoOiAoKSA9PiB2b2lkXG4gIHByZXZNYXRjaDogKCkgPT4gdm9pZFxuICAvKiogQ2FwdHVyZSBjdXJyZW50IHNjcm9sbFRvcCBhcyB0aGUgaW5jc2VhcmNoIGFuY2hvci4gVHlwaW5nIGp1bXBzXG4gICAqICBhcm91bmQgYXMgcHJldmlldzsgMC1tYXRjaGVzIHNuYXBzIGJhY2sgaGVyZS4gRW50ZXIvbi9OIG5ldmVyXG4gICAqICByZXN0b3JlICh0aGV5IGRvbid0IGNhbGwgc2V0U2VhcmNoUXVlcnkgd2l0aCBlbXB0eSkuIE5leHQgLyBjYWxsXG4gICAqICBvdmVyd3JpdGVzLiAqL1xuICBzZXRBbmNob3I6ICgpID0+IHZvaWRcbiAgLyoqIFdhcm0gdGhlIHNlYXJjaC10ZXh0IGNhY2hlIGJ5IGV4dHJhY3RpbmcgZXZlcnkgbWVzc2FnZSdzIHRleHQuXG4gICAqICBSZXR1cm5zIGVsYXBzZWQgbXMsIG9yIDAgaWYgYWxyZWFkeSB3YXJtIChzdWJzZXF1ZW50IC8gaW4gc2FtZVxuICAgKiAgdHJhbnNjcmlwdCBzZXNzaW9uKS4gWWllbGRzIGJlZm9yZSB3b3JrIHNvIHRoZSBjYWxsZXIgY2FuIHBhaW50XG4gICAqICBcImluZGV4aW5n4oCmXCIgZmlyc3QuIENhbGxlciBzaG93cyBcImluZGV4ZWQgaW4gWG1zXCIgb24gcmVzb2x2ZS4gKi9cbiAgd2FybVNlYXJjaEluZGV4OiAoKSA9PiBQcm9taXNlPG51bWJlcj5cbiAgLyoqIE1hbnVhbCBzY3JvbGwgKGovay9QZ1VwL3doZWVsKSBleGl0ZWQgdGhlIHNlYXJjaCBjb250ZXh0LiBDbGVhclxuICAgKiAgcG9zaXRpb25zICh5ZWxsb3cgZ29lcyBhd2F5LCBpbnZlcnNlIGhpZ2hsaWdodHMgc3RheSkuIE5leHQgbi9OXG4gICAqICByZS1lc3RhYmxpc2hlcyB2aWEgc3RlcCgp4oaSanVtcCgpLiBXaXJlZCBmcm9tIFNjcm9sbEtleWJpbmRpbmdIYW5kbGVyJ3NcbiAgICogIG9uU2Nyb2xsIOKAlCBvbmx5IGZpcmVzIGZvciBrZXlib2FyZC93aGVlbCwgbm90IHByb2dyYW1tYXRpYyBzY3JvbGxUby4gKi9cbiAgZGlzYXJtU2VhcmNoOiAoKSA9PiB2b2lkXG59XG5cbnR5cGUgUHJvcHMgPSB7XG4gIG1lc3NhZ2VzOiBSZW5kZXJhYmxlTWVzc2FnZVtdXG4gIHNjcm9sbFJlZjogUmVmT2JqZWN0PFNjcm9sbEJveEhhbmRsZSB8IG51bGw+XG4gIC8qKiBJbnZhbGlkYXRlcyBoZWlnaHRDYWNoZSBvbiBjaGFuZ2Ug4oCUIGNhY2hlZCBoZWlnaHRzIGZyb20gYSBkaWZmZXJlbnRcbiAgICogIHdpZHRoIGFyZSB3cm9uZyAodGV4dCByZXdyYXAg4oaSIGJsYWNrIHNjcmVlbiBvbiBzY3JvbGwtdXAgYWZ0ZXIgd2lkZW4pLiAqL1xuICBjb2x1bW5zOiBudW1iZXJcbiAgaXRlbUtleTogKG1zZzogUmVuZGVyYWJsZU1lc3NhZ2UpID0+IHN0cmluZ1xuICByZW5kZXJJdGVtOiAobXNnOiBSZW5kZXJhYmxlTWVzc2FnZSwgaW5kZXg6IG51bWJlcikgPT4gUmVhY3QuUmVhY3ROb2RlXG4gIC8qKiBGaXJlcyB3aGVuIGEgbWVzc2FnZSBCb3ggaXMgY2xpY2tlZCAodG9nZ2xlIHBlci1tZXNzYWdlIHZlcmJvc2UpLiAqL1xuICBvbkl0ZW1DbGljaz86IChtc2c6IFJlbmRlcmFibGVNZXNzYWdlKSA9PiB2b2lkXG4gIC8qKiBQZXItaXRlbSBmaWx0ZXIg4oCUIHN1cHByZXNzIGhvdmVyL2NsaWNrIGZvciBtZXNzYWdlcyB3aGVyZSB0aGUgdmVyYm9zZVxuICAgKiAgdG9nZ2xlIGRvZXMgbm90aGluZyAodGV4dCwgZmlsZSBlZGl0cywgZXRjKS4gRGVmYXVsdHMgdG8gYWxsLWNsaWNrYWJsZS4gKi9cbiAgaXNJdGVtQ2xpY2thYmxlPzogKG1zZzogUmVuZGVyYWJsZU1lc3NhZ2UpID0+IGJvb2xlYW5cbiAgLyoqIEV4cGFuZGVkIGl0ZW1zIGdldCBhIHBlcnNpc3RlbnQgZ3JleSBiZyAobm90IGp1c3Qgb24gaG92ZXIpLiAqL1xuICBpc0l0ZW1FeHBhbmRlZD86IChtc2c6IFJlbmRlcmFibGVNZXNzYWdlKSA9PiBib29sZWFuXG4gIC8qKiBQUkUtTE9XRVJFRCBzZWFyY2ggdGV4dC4gTWVzc2FnZXMudHN4IGNhY2hlcyB0aGUgbG93ZXJlZCByZXN1bHRcbiAgICogIG9uY2UgYXQgd2FybSB0aW1lIHNvIHNldFNlYXJjaFF1ZXJ5J3MgcGVyLWtleXN0cm9rZSBsb29wIGRvZXNcbiAgICogIG9ubHkgaW5kZXhPZiAoemVybyB0b0xvd2VyQ2FzZSBhbGxvYykuIEZhbGxzIGJhY2sgdG8gYSBsb3dlcmluZ1xuICAgKiAgd3JhcHBlciBvbiByZW5kZXJhYmxlU2VhcmNoVGV4dCBmb3IgY2FsbGVycyB3aXRob3V0IHRoZSBjYWNoZS4gKi9cbiAgZXh0cmFjdFNlYXJjaFRleHQ/OiAobXNnOiBSZW5kZXJhYmxlTWVzc2FnZSkgPT4gc3RyaW5nXG4gIC8qKiBFbmFibGUgdGhlIHN0aWNreS1wcm9tcHQgdHJhY2tlci4gU3RpY2t5VHJhY2tlciB3cml0ZXMgdmlhXG4gICAqICBTY3JvbGxDaHJvbWVDb250ZXh0IChub3QgYSBjYWxsYmFjayBwcm9wKSBzbyBzdGF0ZSBsaXZlcyBpblxuICAgKiAgRnVsbHNjcmVlbkxheW91dCBpbnN0ZWFkIG9mIFJFUEwuICovXG4gIHRyYWNrU3RpY2t5UHJvbXB0PzogYm9vbGVhblxuICBzZWxlY3RlZEluZGV4PzogbnVtYmVyXG4gIC8qKiBOYXYgaGFuZGxlIGxpdmVzIGhlcmUgYmVjYXVzZSBoZWlnaHQgbWVhc3VyZW1lbnQgbGl2ZXMgaGVyZS4gKi9cbiAgY3Vyc29yTmF2UmVmPzogUmVhY3QuUmVmPE1lc3NhZ2VBY3Rpb25zTmF2PlxuICBzZXRDdXJzb3I/OiAoYzogTWVzc2FnZUFjdGlvbnNTdGF0ZSB8IG51bGwpID0+IHZvaWRcbiAganVtcFJlZj86IFJlZk9iamVjdDxKdW1wSGFuZGxlIHwgbnVsbD5cbiAgLyoqIEZpcmVzIHdoZW4gc2VhcmNoIG1hdGNoZXMgY2hhbmdlIChxdWVyeSBlZGl0LCBuL04pLiBjdXJyZW50IGlzXG4gICAqICAxLWJhc2VkIGZvciBcIjMvNDdcIiBkaXNwbGF5OyAwIG1lYW5zIG5vIG1hdGNoZXMuICovXG4gIG9uU2VhcmNoTWF0Y2hlc0NoYW5nZT86IChjb3VudDogbnVtYmVyLCBjdXJyZW50OiBudW1iZXIpID0+IHZvaWRcbiAgLyoqIFBhaW50IGV4aXN0aW5nIERPTSBzdWJ0cmVlIHRvIGZyZXNoIFNjcmVlbiwgc2Nhbi4gRWxlbWVudCBmcm9tIHRoZVxuICAgKiAgbWFpbiB0cmVlIChhbGwgcHJvdmlkZXJzKS4gTWVzc2FnZS1yZWxhdGl2ZSBwb3NpdGlvbnMgKHJvdyAwID0gZWxcbiAgICogIHRvcCkuIFdvcmtzIGZvciBhbnkgaGVpZ2h0IOKAlCBjbG9zZXMgdGhlIHRhbGwtbWVzc2FnZSBnYXAuICovXG4gIHNjYW5FbGVtZW50PzogKGVsOiBET01FbGVtZW50KSA9PiBNYXRjaFBvc2l0aW9uW11cbiAgLyoqIFBvc2l0aW9uLWJhc2VkIENVUlJFTlQgaGlnaGxpZ2h0LiBQb3NpdGlvbnMga25vd24gdXBmcm9udCAoZnJvbVxuICAgKiAgc2NhbkVsZW1lbnQpLCBuYXZpZ2F0aW9uID0gaW5kZXggYXJpdGhtZXRpYyArIHNjcm9sbFRvLiByb3dPZmZzZXRcbiAgICogID0gbWVzc2FnZSdzIGN1cnJlbnQgc2NyZWVuLXRvcDsgcG9zaXRpb25zIHN0YXkgc3RhYmxlLiAqL1xuICBzZXRQb3NpdGlvbnM/OiAoXG4gICAgc3RhdGU6IHtcbiAgICAgIHBvc2l0aW9uczogTWF0Y2hQb3NpdGlvbltdXG4gICAgICByb3dPZmZzZXQ6IG51bWJlclxuICAgICAgY3VycmVudElkeDogbnVtYmVyXG4gICAgfSB8IG51bGwsXG4gICkgPT4gdm9pZFxufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIHRleHQgb2YgYSByZWFsIHVzZXIgcHJvbXB0LCBvciBudWxsIGZvciBhbnl0aGluZyBlbHNlLlxuICogXCJSZWFsXCIgPSB3aGF0IHRoZSBodW1hbiB0eXBlZDogbm90IHRvb2wgcmVzdWx0cywgbm90IFhNTC13cmFwcGVkIHBheWxvYWRzXG4gKiAoPGJhc2gtc3Rkb3V0PiwgPGNvbW1hbmQtbWVzc2FnZT4sIDx0ZWFtbWF0ZS1tZXNzYWdlPiwgZXRjLiksIG5vdCBtZXRhLlxuICpcbiAqIFR3byBzaGFwZXMgbGFuZCBoZXJlOiBOb3JtYWxpemVkVXNlck1lc3NhZ2UgKG5vcm1hbCBwcm9tcHRzKSBhbmRcbiAqIEF0dGFjaG1lbnRNZXNzYWdlIHdpdGggdHlwZT09PSdxdWV1ZWRfY29tbWFuZCcgKHByb21wdHMgc2VudCBtaWQtdHVyblxuICogd2hpbGUgYSB0b29sIHdhcyBleGVjdXRpbmcg4oCUIHRoZXkgZ2V0IGRyYWluZWQgYXMgYXR0YWNobWVudHMgb24gdGhlXG4gKiBuZXh0IHR1cm4sIHNlZSBxdWVyeS50czoxNDEwKS4gQm90aCByZW5kZXIgYXMg4p2vLXByZWZpeGVkIFVzZXJUZXh0TWVzc2FnZVxuICogaW4gdGhlIFVJIHNvIGJvdGggc2hvdWxkIHN0aWNrLlxuICpcbiAqIExlYWRpbmcgPHN5c3RlbS1yZW1pbmRlcj4gYmxvY2tzIGFyZSBzdHJpcHBlZCBiZWZvcmUgY2hlY2tpbmcg4oCUIHRoZXkgZ2V0XG4gKiBwcmVwZW5kZWQgdG8gdGhlIHN0b3JlZCB0ZXh0IGZvciBDbGF1ZGUncyBjb250ZXh0IChtZW1vcnkgdXBkYXRlcywgYXV0b1xuICogbW9kZSByZW1pbmRlcnMpIGJ1dCBhcmVuJ3Qgd2hhdCB0aGUgdXNlciB0eXBlZC4gV2l0aG91dCBzdHJpcHBpbmcsIGFueVxuICogcHJvbXB0IHRoYXQgaGFwcGVuZWQgdG8gZ2V0IGEgcmVtaW5kZXIgaXMgcmVqZWN0ZWQgYnkgdGhlIHN0YXJ0c1dpdGgoJzwnKVxuICogY2hlY2suIFNob3dzIHVwIG9uIGBjYyAtY2AgcmVzdW1lcyB3aGVyZSBtZW1vcnktdXBkYXRlIHJlbWluZGVycyBhcmUgZGVuc2UuXG4gKi9cbmNvbnN0IHByb21wdFRleHRDYWNoZSA9IG5ldyBXZWFrTWFwPFJlbmRlcmFibGVNZXNzYWdlLCBzdHJpbmcgfCBudWxsPigpXG5cbmZ1bmN0aW9uIHN0aWNreVByb21wdFRleHQobXNnOiBSZW5kZXJhYmxlTWVzc2FnZSk6IHN0cmluZyB8IG51bGwge1xuICAvLyBDYWNoZSBrZXllZCBvbiBtZXNzYWdlIG9iamVjdCDigJQgbWVzc2FnZXMgYXJlIGFwcGVuZC1vbmx5IGFuZCBkb24ndFxuICAvLyBtdXRhdGUsIHNvIGEgV2Vha01hcCBoaXQgaXMgYWx3YXlzIHZhbGlkLiBUaGUgd2FsayAoU3RpY2t5VHJhY2tlcixcbiAgLy8gcGVyLXNjcm9sbC10aWNrKSBjYWxscyB0aGlzIDUtNTArIHRpbWVzIHdpdGggdGhlIFNBTUUgbWVzc2FnZXMgZXZlcnlcbiAgLy8gdGljazsgdGhlIHN5c3RlbS1yZW1pbmRlciBzdHJpcCBhbGxvY2F0ZXMgYSBmcmVzaCBzdHJpbmcgb24gZWFjaFxuICAvLyBwYXJzZS4gV2Vha01hcCBzZWxmLUdDcyBvbiBjb21wYWN0aW9uL2NsZWFyIChtZXNzYWdlc1tdIHJlcGxhY2VkKS5cbiAgY29uc3QgY2FjaGVkID0gcHJvbXB0VGV4dENhY2hlLmdldChtc2cpXG4gIGlmIChjYWNoZWQgIT09IHVuZGVmaW5lZCkgcmV0dXJuIGNhY2hlZFxuICBjb25zdCByZXN1bHQgPSBjb21wdXRlU3RpY2t5UHJvbXB0VGV4dChtc2cpXG4gIHByb21wdFRleHRDYWNoZS5zZXQobXNnLCByZXN1bHQpXG4gIHJldHVybiByZXN1bHRcbn1cblxuZnVuY3Rpb24gY29tcHV0ZVN0aWNreVByb21wdFRleHQobXNnOiBSZW5kZXJhYmxlTWVzc2FnZSk6IHN0cmluZyB8IG51bGwge1xuICBsZXQgcmF3OiBzdHJpbmcgfCBudWxsID0gbnVsbFxuICBpZiAobXNnLnR5cGUgPT09ICd1c2VyJykge1xuICAgIGlmIChtc2cuaXNNZXRhIHx8IG1zZy5pc1Zpc2libGVJblRyYW5zY3JpcHRPbmx5KSByZXR1cm4gbnVsbFxuICAgIGNvbnN0IGJsb2NrID0gbXNnLm1lc3NhZ2UuY29udGVudFswXVxuICAgIGlmIChibG9jaz8udHlwZSAhPT0gJ3RleHQnKSByZXR1cm4gbnVsbFxuICAgIHJhdyA9IGJsb2NrLnRleHRcbiAgfSBlbHNlIGlmIChcbiAgICBtc2cudHlwZSA9PT0gJ2F0dGFjaG1lbnQnICYmXG4gICAgbXNnLmF0dGFjaG1lbnQudHlwZSA9PT0gJ3F1ZXVlZF9jb21tYW5kJyAmJlxuICAgIG1zZy5hdHRhY2htZW50LmNvbW1hbmRNb2RlICE9PSAndGFzay1ub3RpZmljYXRpb24nICYmXG4gICAgIW1zZy5hdHRhY2htZW50LmlzTWV0YVxuICApIHtcbiAgICBjb25zdCBwID0gbXNnLmF0dGFjaG1lbnQucHJvbXB0XG4gICAgcmF3ID1cbiAgICAgIHR5cGVvZiBwID09PSAnc3RyaW5nJ1xuICAgICAgICA/IHBcbiAgICAgICAgOiBwLmZsYXRNYXAoYiA9PiAoYi50eXBlID09PSAndGV4dCcgPyBbYi50ZXh0XSA6IFtdKSkuam9pbignXFxuJylcbiAgfVxuICBpZiAocmF3ID09PSBudWxsKSByZXR1cm4gbnVsbFxuXG4gIGNvbnN0IHQgPSBzdHJpcFN5c3RlbVJlbWluZGVycyhyYXcpXG4gIGlmICh0LnN0YXJ0c1dpdGgoJzwnKSB8fCB0ID09PSAnJykgcmV0dXJuIG51bGxcbiAgcmV0dXJuIHRcbn1cblxuLyoqXG4gKiBWaXJ0dWFsaXplZCBtZXNzYWdlIGxpc3QgZm9yIGZ1bGxzY3JlZW4gbW9kZS4gU3BsaXQgZnJvbSBNZXNzYWdlcy50c3ggc29cbiAqIHVzZVZpcnR1YWxTY3JvbGwgaXMgY2FsbGVkIHVuY29uZGl0aW9uYWxseSAocnVsZXMtb2YtaG9va3MpIOKAlCBNZXNzYWdlcy50c3hcbiAqIGNvbmRpdGlvbmFsbHkgcmVuZGVycyBlaXRoZXIgdGhpcyBvciBhIHBsYWluIC5tYXAoKS5cbiAqXG4gKiBUaGUgd3JhcHBpbmcgPEJveCByZWY+IGlzIHRoZSBtZWFzdXJlbWVudCBhbmNob3Ig4oCUIE1lc3NhZ2VSb3cgZG9lc24ndCB0YWtlXG4gKiBhIHJlZi4gU2luZ2xlLWNoaWxkIGNvbHVtbiBCb3ggcGFzc2VzIFlvZ2EgaGVpZ2h0IHRocm91Z2ggdW5jaGFuZ2VkLlxuICovXG50eXBlIFZpcnR1YWxJdGVtUHJvcHMgPSB7XG4gIGl0ZW1LZXk6IHN0cmluZ1xuICBtc2c6IFJlbmRlcmFibGVNZXNzYWdlXG4gIGlkeDogbnVtYmVyXG4gIG1lYXN1cmVSZWY6IChrZXk6IHN0cmluZykgPT4gKGVsOiBET01FbGVtZW50IHwgbnVsbCkgPT4gdm9pZFxuICBleHBhbmRlZDogYm9vbGVhbiB8IHVuZGVmaW5lZFxuICBob3ZlcmVkOiBib29sZWFuXG4gIGNsaWNrYWJsZTogYm9vbGVhblxuICBvbkNsaWNrSzogKG1zZzogUmVuZGVyYWJsZU1lc3NhZ2UsIGNlbGxJc0JsYW5rOiBib29sZWFuKSA9PiB2b2lkXG4gIG9uRW50ZXJLOiAoazogc3RyaW5nKSA9PiB2b2lkXG4gIG9uTGVhdmVLOiAoazogc3RyaW5nKSA9PiB2b2lkXG4gIHJlbmRlckl0ZW06IChtc2c6IFJlbmRlcmFibGVNZXNzYWdlLCBpZHg6IG51bWJlcikgPT4gUmVhY3QuUmVhY3ROb2RlXG59XG5cbi8vIEl0ZW0gd3JhcHBlciB3aXRoIHN0YWJsZSBjbGljayBoYW5kbGVycy4gVGhlIHBlci1pdGVtIGNsb3N1cmVzIHdlcmUgdGhlXG4vLyBgb3BlcmF0aW9uTmV3QXJyb3dGdW5jdGlvbmAgbGVhZnMg4oaSIGBGdW5jdGlvbkV4ZWN1dGFibGU6OmZpbmFsaXplVW5jb25kaXRpb25hbGx5YFxuLy8gR0MgY2xlYW51cCAoMTYlIG9mIEdDIHRpbWUgZHVyaW5nIGZhc3Qgc2Nyb2xsKS4gMyBjbG9zdXJlcyDDlyA2MCBtb3VudGVkIMOXXG4vLyAxMCBjb21taXRzL3NlYyA9IDE4MDAgY2xvc3VyZXMvc2VjLiBXaXRoIHN0YWJsZSBvbkNsaWNrSy9vbkVudGVySy9vbkxlYXZlS1xuLy8gdGhyZWFkZWQgdmlhIGl0ZW1LZXksIHRoZSBjbG9zdXJlcyBoZXJlIGFyZSBwZXItaXRlbS1wZXItcmVuZGVyIGJ1dCBDSEVBUFxuLy8gKGp1c3Qgd3JhcCB0aGUgc3RhYmxlIGNhbGxiYWNrIHdpdGggayBib3VuZCkgYW5kIGRvbid0IGNsb3NlIG92ZXIgbXNnL2lkeFxuLy8gd2hpY2ggbGV0cyBKSVQgaW5saW5lIHRoZW0uIFRoZSBiaWdnZXIgd2luIGlzIGluc2lkZTogTWVzc2FnZVJvdy5tZW1vXG4vLyBiYWlscyBmb3IgdW5jaGFuZ2VkIG1zZ3MsIHNraXBwaW5nIG1hcmtlZC5sZXhlciArIGZvcm1hdFRva2VuLlxuLy9cbi8vIE5PVCBSZWFjdC5tZW1vJ2Qg4oCUIHJlbmRlckl0ZW0gY2FwdHVyZXMgY2hhbmdpbmcgc3RhdGUgKGN1cnNvciwgc2VsZWN0ZWRJZHgsXG4vLyB2ZXJib3NlKS4gTWVtb2luZyB3aXRoIGEgY29tcGFyYXRvciB0aGF0IGlnbm9yZXMgcmVuZGVySXRlbSB3b3VsZCB1c2UgYVxuLy8gU1RBTEUgY2xvc3VyZSBvbiBiYWlsICh3cm9uZyBzZWxlY3Rpb24gaGlnaGxpZ2h0LCBzdGFsZSB2ZXJib3NlKS4gSW5jbHVkaW5nXG4vLyByZW5kZXJJdGVtIGluIHRoZSBjb21wYXJhdG9yIGRlZmVhdHMgbWVtbyBzaW5jZSBpdCdzIGZyZXNoIGVhY2ggcmVuZGVyLlxuZnVuY3Rpb24gVmlydHVhbEl0ZW0oe1xuICBpdGVtS2V5OiBrLFxuICBtc2csXG4gIGlkeCxcbiAgbWVhc3VyZVJlZixcbiAgZXhwYW5kZWQsXG4gIGhvdmVyZWQsXG4gIGNsaWNrYWJsZSxcbiAgb25DbGlja0ssXG4gIG9uRW50ZXJLLFxuICBvbkxlYXZlSyxcbiAgcmVuZGVySXRlbSxcbn06IFZpcnR1YWxJdGVtUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIHJlZj17bWVhc3VyZVJlZihrKX1cbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgYmFja2dyb3VuZENvbG9yPXtleHBhbmRlZCA/ICd1c2VyTWVzc2FnZUJhY2tncm91bmRIb3ZlcicgOiB1bmRlZmluZWR9XG4gICAgICAvLyBiZyBoZXJlIG1hc2tzIHVzZVZpcnR1YWxTY3JvbGwncyBvbmUtZnJhbWUgb2Zmc2V0IGxhZyBvbiBleHBhbmQg4oCUXG4gICAgICAvLyBkb24ndCBtb3ZlIHRvIHRoZSBtYXJnaW5lZCBCb3ggaW5zaWRlLiBwYWRkaW5nQm90dG9tIG1pcnJvcnMgdGhlXG4gICAgICAvLyB0aW50ZWQgbWFyZ2luVG9wLlxuICAgICAgcGFkZGluZ0JvdHRvbT17ZXhwYW5kZWQgPyAxIDogdW5kZWZpbmVkfVxuICAgICAgb25DbGljaz17Y2xpY2thYmxlID8gZSA9PiBvbkNsaWNrSyhtc2csIGUuY2VsbElzQmxhbmspIDogdW5kZWZpbmVkfVxuICAgICAgb25Nb3VzZUVudGVyPXtjbGlja2FibGUgPyAoKSA9PiBvbkVudGVySyhrKSA6IHVuZGVmaW5lZH1cbiAgICAgIG9uTW91c2VMZWF2ZT17Y2xpY2thYmxlID8gKCkgPT4gb25MZWF2ZUsoaykgOiB1bmRlZmluZWR9XG4gICAgPlxuICAgICAgPFRleHRIb3ZlckNvbG9yQ29udGV4dC5Qcm92aWRlclxuICAgICAgICB2YWx1ZT17aG92ZXJlZCAmJiAhZXhwYW5kZWQgPyAndGV4dCcgOiB1bmRlZmluZWR9XG4gICAgICA+XG4gICAgICAgIHtyZW5kZXJJdGVtKG1zZywgaWR4KX1cbiAgICAgIDwvVGV4dEhvdmVyQ29sb3JDb250ZXh0LlByb3ZpZGVyPlxuICAgIDwvQm94PlxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBWaXJ0dWFsTWVzc2FnZUxpc3Qoe1xuICBtZXNzYWdlcyxcbiAgc2Nyb2xsUmVmLFxuICBjb2x1bW5zLFxuICBpdGVtS2V5LFxuICByZW5kZXJJdGVtLFxuICBvbkl0ZW1DbGljayxcbiAgaXNJdGVtQ2xpY2thYmxlLFxuICBpc0l0ZW1FeHBhbmRlZCxcbiAgZXh0cmFjdFNlYXJjaFRleHQgPSBkZWZhdWx0RXh0cmFjdFNlYXJjaFRleHQsXG4gIHRyYWNrU3RpY2t5UHJvbXB0LFxuICBzZWxlY3RlZEluZGV4LFxuICBjdXJzb3JOYXZSZWYsXG4gIHNldEN1cnNvcixcbiAganVtcFJlZixcbiAgb25TZWFyY2hNYXRjaGVzQ2hhbmdlLFxuICBzY2FuRWxlbWVudCxcbiAgc2V0UG9zaXRpb25zLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBJbmNyZW1lbnRhbCBrZXkgYXJyYXkuIFN0cmVhbWluZyBhcHBlbmRzIG9uZSBtZXNzYWdlIGF0IGEgdGltZTsgcmVidWlsZGluZ1xuICAvLyB0aGUgZnVsbCBzdHJpbmcgYXJyYXkgb24gZXZlcnkgY29tbWl0IGFsbG9jYXRlcyBPKG4pIHBlciBtZXNzYWdlICh+MU1CXG4gIC8vIGNodXJuIGF0IDI3ayBtZXNzYWdlcykuIEFwcGVuZC1vbmx5IGRlbHRhIHB1c2ggd2hlbiB0aGUgcHJlZml4IG1hdGNoZXM7XG4gIC8vIGZhbGwgYmFjayB0byBmdWxsIHJlYnVpbGQgb24gY29tcGFjdGlvbiwgL2NsZWFyLCBvciBpdGVtS2V5IGNoYW5nZS5cbiAgY29uc3Qga2V5c1JlZiA9IHVzZVJlZjxzdHJpbmdbXT4oW10pXG4gIGNvbnN0IHByZXZNZXNzYWdlc1JlZiA9IHVzZVJlZjx0eXBlb2YgbWVzc2FnZXM+KG1lc3NhZ2VzKVxuICBjb25zdCBwcmV2SXRlbUtleVJlZiA9IHVzZVJlZihpdGVtS2V5KVxuICBpZiAoXG4gICAgcHJldkl0ZW1LZXlSZWYuY3VycmVudCAhPT0gaXRlbUtleSB8fFxuICAgIG1lc3NhZ2VzLmxlbmd0aCA8IGtleXNSZWYuY3VycmVudC5sZW5ndGggfHxcbiAgICBtZXNzYWdlc1swXSAhPT0gcHJldk1lc3NhZ2VzUmVmLmN1cnJlbnRbMF1cbiAgKSB7XG4gICAga2V5c1JlZi5jdXJyZW50ID0gbWVzc2FnZXMubWFwKG0gPT4gaXRlbUtleShtKSlcbiAgfSBlbHNlIHtcbiAgICBmb3IgKGxldCBpID0ga2V5c1JlZi5jdXJyZW50Lmxlbmd0aDsgaSA8IG1lc3NhZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBrZXlzUmVmLmN1cnJlbnQucHVzaChpdGVtS2V5KG1lc3NhZ2VzW2ldISkpXG4gICAgfVxuICB9XG4gIHByZXZNZXNzYWdlc1JlZi5jdXJyZW50ID0gbWVzc2FnZXNcbiAgcHJldkl0ZW1LZXlSZWYuY3VycmVudCA9IGl0ZW1LZXlcbiAgY29uc3Qga2V5cyA9IGtleXNSZWYuY3VycmVudFxuICBjb25zdCB7XG4gICAgcmFuZ2UsXG4gICAgdG9wU3BhY2VyLFxuICAgIGJvdHRvbVNwYWNlcixcbiAgICBtZWFzdXJlUmVmLFxuICAgIHNwYWNlclJlZixcbiAgICBvZmZzZXRzLFxuICAgIGdldEl0ZW1Ub3AsXG4gICAgZ2V0SXRlbUVsZW1lbnQsXG4gICAgZ2V0SXRlbUhlaWdodCxcbiAgICBzY3JvbGxUb0luZGV4LFxuICB9ID0gdXNlVmlydHVhbFNjcm9sbChzY3JvbGxSZWYsIGtleXMsIGNvbHVtbnMpXG4gIGNvbnN0IFtzdGFydCwgZW5kXSA9IHJhbmdlXG5cbiAgLy8gVW5tZWFzdXJlZCAodW5kZWZpbmVkIGhlaWdodCkgZmFsbHMgdGhyb3VnaCDigJQgYXNzdW1lIHZpc2libGUuXG4gIGNvbnN0IGlzVmlzaWJsZSA9IHVzZUNhbGxiYWNrKFxuICAgIChpOiBudW1iZXIpID0+IHtcbiAgICAgIGNvbnN0IGggPSBnZXRJdGVtSGVpZ2h0KGkpXG4gICAgICBpZiAoaCA9PT0gMCkgcmV0dXJuIGZhbHNlXG4gICAgICByZXR1cm4gaXNOYXZpZ2FibGVNZXNzYWdlKG1lc3NhZ2VzW2ldISlcbiAgICB9LFxuICAgIFtnZXRJdGVtSGVpZ2h0LCBtZXNzYWdlc10sXG4gIClcbiAgdXNlSW1wZXJhdGl2ZUhhbmRsZShjdXJzb3JOYXZSZWYsICgpOiBNZXNzYWdlQWN0aW9uc05hdiA9PiB7XG4gICAgY29uc3Qgc2VsZWN0ID0gKG06IE5hdmlnYWJsZU1lc3NhZ2UpID0+XG4gICAgICBzZXRDdXJzb3I/Lih7XG4gICAgICAgIHV1aWQ6IG0udXVpZCxcbiAgICAgICAgbXNnVHlwZTogbS50eXBlLFxuICAgICAgICBleHBhbmRlZDogZmFsc2UsXG4gICAgICAgIHRvb2xOYW1lOiB0b29sQ2FsbE9mKG0pPy5uYW1lLFxuICAgICAgfSlcbiAgICBjb25zdCBzZWxJZHggPSBzZWxlY3RlZEluZGV4ID8/IC0xXG4gICAgY29uc3Qgc2NhbiA9IChcbiAgICAgIGZyb206IG51bWJlcixcbiAgICAgIGRpcjogMSB8IC0xLFxuICAgICAgcHJlZDogKGk6IG51bWJlcikgPT4gYm9vbGVhbiA9IGlzVmlzaWJsZSxcbiAgICApID0+IHtcbiAgICAgIGZvciAobGV0IGkgPSBmcm9tOyBpID49IDAgJiYgaSA8IG1lc3NhZ2VzLmxlbmd0aDsgaSArPSBkaXIpIHtcbiAgICAgICAgaWYgKHByZWQoaSkpIHtcbiAgICAgICAgICBzZWxlY3QobWVzc2FnZXNbaV0hKVxuICAgICAgICAgIHJldHVybiB0cnVlXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZVxuICAgIH1cbiAgICBjb25zdCBpc1VzZXIgPSAoaTogbnVtYmVyKSA9PiBpc1Zpc2libGUoaSkgJiYgbWVzc2FnZXNbaV0hLnR5cGUgPT09ICd1c2VyJ1xuICAgIHJldHVybiB7XG4gICAgICAvLyBFbnRyeSB2aWEgc2hpZnQr4oaRID0gc2FtZSBzZW1hbnRpYyBhcyBpbi1jdXJzb3Igc2hpZnQr4oaRIChwcmV2VXNlcikuXG4gICAgICBlbnRlckN1cnNvcjogKCkgPT4gc2NhbihtZXNzYWdlcy5sZW5ndGggLSAxLCAtMSwgaXNVc2VyKSxcbiAgICAgIG5hdmlnYXRlUHJldjogKCkgPT4gc2NhbihzZWxJZHggLSAxLCAtMSksXG4gICAgICBuYXZpZ2F0ZU5leHQ6ICgpID0+IHtcbiAgICAgICAgaWYgKHNjYW4oc2VsSWR4ICsgMSwgMSkpIHJldHVyblxuICAgICAgICAvLyBQYXN0IGxhc3QgdmlzaWJsZSDihpIgZXhpdCArIHJlcGluLiBMYXN0IG1lc3NhZ2UncyBUT1AgaXMgYXQgdmlld3BvcnRcbiAgICAgICAgLy8gdG9wIChzZWxlY3Rpb24tc2Nyb2xsIGVmZmVjdCk7IGl0cyBCT1RUT00gbWF5IGJlIGJlbG93IHRoZSBmb2xkLlxuICAgICAgICBzY3JvbGxSZWYuY3VycmVudD8uc2Nyb2xsVG9Cb3R0b20oKVxuICAgICAgICBzZXRDdXJzb3I/LihudWxsKVxuICAgICAgfSxcbiAgICAgIC8vIHR5cGU6J3VzZXInIG9ubHkg4oCUIHF1ZXVlZF9jb21tYW5kIGF0dGFjaG1lbnRzIGxvb2sgbGlrZSBwcm9tcHRzIGJ1dCBoYXZlIG5vIHJhdyBVc2VyTWVzc2FnZSB0byByZXdpbmQgdG8uXG4gICAgICBuYXZpZ2F0ZVByZXZVc2VyOiAoKSA9PiBzY2FuKHNlbElkeCAtIDEsIC0xLCBpc1VzZXIpLFxuICAgICAgbmF2aWdhdGVOZXh0VXNlcjogKCkgPT4gc2NhbihzZWxJZHggKyAxLCAxLCBpc1VzZXIpLFxuICAgICAgbmF2aWdhdGVUb3A6ICgpID0+IHNjYW4oMCwgMSksXG4gICAgICBuYXZpZ2F0ZUJvdHRvbTogKCkgPT4gc2NhbihtZXNzYWdlcy5sZW5ndGggLSAxLCAtMSksXG4gICAgICBnZXRTZWxlY3RlZDogKCkgPT4gKHNlbElkeCA+PSAwID8gKG1lc3NhZ2VzW3NlbElkeF0gPz8gbnVsbCkgOiBudWxsKSxcbiAgICB9XG4gIH0sIFttZXNzYWdlcywgc2VsZWN0ZWRJbmRleCwgc2V0Q3Vyc29yLCBpc1Zpc2libGVdKVxuICAvLyBUd28tcGhhc2UganVtcCArIHNlYXJjaCBlbmdpbmUuIFJlYWQtdGhyb3VnaC1yZWYgc28gdGhlIGhhbmRsZSBzdGF5c1xuICAvLyBzdGFibGUgYWNyb3NzIHJlbmRlcnMg4oCUIG9mZnNldHMvbWVzc2FnZXMgaWRlbnRpdHkgY2hhbmdlcyBldmVyeSByZW5kZXIsXG4gIC8vIGNhbid0IGdvIGluIHVzZUltcGVyYXRpdmVIYW5kbGUgZGVwcyB3aXRob3V0IHJlY3JlYXRpbmcgdGhlIGhhbmRsZS5cbiAgY29uc3QganVtcFN0YXRlID0gdXNlUmVmKHtcbiAgICBvZmZzZXRzLFxuICAgIHN0YXJ0LFxuICAgIGdldEl0ZW1FbGVtZW50LFxuICAgIGdldEl0ZW1Ub3AsXG4gICAgbWVzc2FnZXMsXG4gICAgc2Nyb2xsVG9JbmRleCxcbiAgfSlcbiAganVtcFN0YXRlLmN1cnJlbnQgPSB7XG4gICAgb2Zmc2V0cyxcbiAgICBzdGFydCxcbiAgICBnZXRJdGVtRWxlbWVudCxcbiAgICBnZXRJdGVtVG9wLFxuICAgIG1lc3NhZ2VzLFxuICAgIHNjcm9sbFRvSW5kZXgsXG4gIH1cblxuICAvLyBLZWVwIGN1cnNvci1zZWxlY3RlZCBtZXNzYWdlIHZpc2libGUuIG9mZnNldHMgcmVidWlsZHMgZXZlcnkgcmVuZGVyXG4gIC8vIOKAlCBhcyBhIGJhcmUgZGVwIHRoaXMgcmUtcGlubmVkIG9uIGV2ZXJ5IG1vdXNld2hlZWwgdGljay4gUmVhZCB0aHJvdWdoXG4gIC8vIGp1bXBTdGF0ZSBpbnN0ZWFkOyBwYXN0LW92ZXJzY2FuIGp1bXBzIGxhbmQgdmlhIHNjcm9sbFRvSW5kZXgsIG5leHRcbiAgLy8gbmF2IGlzIHByZWNpc2UuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKHNlbGVjdGVkSW5kZXggPT09IHVuZGVmaW5lZCkgcmV0dXJuXG4gICAgY29uc3QgcyA9IGp1bXBTdGF0ZS5jdXJyZW50XG4gICAgY29uc3QgZWwgPSBzLmdldEl0ZW1FbGVtZW50KHNlbGVjdGVkSW5kZXgpXG4gICAgaWYgKGVsKSB7XG4gICAgICBzY3JvbGxSZWYuY3VycmVudD8uc2Nyb2xsVG9FbGVtZW50KGVsLCAxKVxuICAgIH0gZWxzZSB7XG4gICAgICBzLnNjcm9sbFRvSW5kZXgoc2VsZWN0ZWRJbmRleClcbiAgICB9XG4gIH0sIFtzZWxlY3RlZEluZGV4LCBzY3JvbGxSZWZdKVxuXG4gIC8vIFBlbmRpbmcgc2VlayByZXF1ZXN0LiBqdW1wKCkgc2V0cyB0aGlzICsgYnVtcHMgc2Vla0dlbi4gVGhlIHNlZWtcbiAgLy8gZWZmZWN0IGZpcmVzIHBvc3QtcGFpbnQgKHBhc3NpdmUgZWZmZWN0IOKAlCBhZnRlciByZXNldEFmdGVyQ29tbWl0KSxcbiAgLy8gY2hlY2tzIGlmIHRhcmdldCBpcyBtb3VudGVkLiBZZXMg4oaSIHNjYW4raGlnaGxpZ2h0LiBObyDihpIgcmUtZXN0aW1hdGVcbiAgLy8gd2l0aCBhIGZyZXNoZXIgYW5jaG9yIChzdGFydCBtb3ZlZCB0b3dhcmQgaWR4KSBhbmQgc2Nyb2xsVG8gYWdhaW4uXG4gIGNvbnN0IHNjYW5SZXF1ZXN0UmVmID0gdXNlUmVmPHtcbiAgICBpZHg6IG51bWJlclxuICAgIHdhbnRMYXN0OiBib29sZWFuXG4gICAgdHJpZXM6IG51bWJlclxuICB9IHwgbnVsbD4obnVsbClcbiAgLy8gTWVzc2FnZS1yZWxhdGl2ZSBwb3NpdGlvbnMgZnJvbSBzY2FuRWxlbWVudC4gUm93IDAgPSBtZXNzYWdlIHRvcC5cbiAgLy8gU3RhYmxlIGFjcm9zcyBzY3JvbGwg4oCUIGhpZ2hsaWdodCBjb21wdXRlcyByb3dPZmZzZXQgZnJlc2guIG1zZ0lkeFxuICAvLyBmb3IgY29tcHV0aW5nIHJvd09mZnNldCA9IGdldEl0ZW1Ub3AobXNnSWR4KSAtIHNjcm9sbFRvcC5cbiAgY29uc3QgZWxlbWVudFBvc2l0aW9ucyA9IHVzZVJlZjx7XG4gICAgbXNnSWR4OiBudW1iZXJcbiAgICBwb3NpdGlvbnM6IE1hdGNoUG9zaXRpb25bXVxuICB9Pih7IG1zZ0lkeDogLTEsIHBvc2l0aW9uczogW10gfSlcbiAgLy8gV3JhcGFyb3VuZCBndWFyZC4gQXV0by1hZHZhbmNlIHN0b3BzIGlmIHB0ciB3cmFwcyBiYWNrIHRvIGhlcmUuXG4gIGNvbnN0IHN0YXJ0UHRyUmVmID0gdXNlUmVmKC0xKVxuICAvLyBQaGFudG9tLWJ1cnN0IGNhcC4gUmVzZXRzIG9uIHNjYW4gc3VjY2Vzcy5cbiAgY29uc3QgcGhhbnRvbUJ1cnN0UmVmID0gdXNlUmVmKDApXG4gIC8vIE9uZS1kZWVwIHF1ZXVlOiBuL04gYXJyaXZpbmcgbWlkLXNlZWsgZ2V0cyBzdG9yZWQgKG5vdCBkcm9wcGVkKSBhbmRcbiAgLy8gZmlyZXMgYWZ0ZXIgdGhlIHNlZWsgY29tcGxldGVzLiBIb2xkaW5nIG4gc3RheXMgc21vb3RoIHdpdGhvdXRcbiAgLy8gcXVldWVpbmcgMzAganVtcHMuIExhdGVzdCBwcmVzcyBvdmVyd3JpdGVzIOKAlCB3ZSB3YW50IHRoZSBkaXJlY3Rpb25cbiAgLy8gdGhlIHVzZXIgaXMgZ29pbmcgTk9XLCBub3Qgd2hlcmUgdGhleSB3ZXJlIDEwIGtleXByZXNzZXMgYWdvLlxuICBjb25zdCBwZW5kaW5nU3RlcFJlZiA9IHVzZVJlZjwxIHwgLTEgfCAwPigwKVxuICAvLyBzdGVwICsgaGlnaGxpZ2h0IHZpYSByZWYgc28gdGhlIHNlZWsgZWZmZWN0IHJlYWRzIGxhdGVzdCB3aXRob3V0XG4gIC8vIGNsb3N1cmUtY2FwdHVyZSBvciBkZXBzIGNodXJuLlxuICBjb25zdCBzdGVwUmVmID0gdXNlUmVmPChkOiAxIHwgLTEpID0+IHZvaWQ+KCgpID0+IHt9KVxuICBjb25zdCBoaWdobGlnaHRSZWYgPSB1c2VSZWY8KG9yZDogbnVtYmVyKSA9PiB2b2lkPigoKSA9PiB7fSlcbiAgY29uc3Qgc2VhcmNoU3RhdGUgPSB1c2VSZWYoe1xuICAgIG1hdGNoZXM6IFtdIGFzIG51bWJlcltdLCAvLyBkZWR1cGxpY2F0ZWQgbXNnIGluZGljZXNcbiAgICBwdHI6IDAsXG4gICAgc2NyZWVuT3JkOiAwLFxuICAgIC8vIEN1bXVsYXRpdmUgZW5naW5lLW9jY3VycmVuY2UgY291bnQgYmVmb3JlIGVhY2ggbWF0Y2hlc1trXS4gTGV0cyB1c1xuICAgIC8vIGNvbXB1dGUgYSBnbG9iYWwgY3VycmVudCBpbmRleDogcHJlZml4U3VtW3B0cl0gKyBzY3JlZW5PcmQgKyAxLlxuICAgIC8vIEVuZ2luZS1jb3VudGVkIChpbmRleE9mIG9uIGV4dHJhY3RTZWFyY2hUZXh0KSwgbm90IHJlbmRlci1jb3VudGVkIOKAlFxuICAgIC8vIGNsb3NlIGVub3VnaCBmb3IgdGhlIGJhZGdlOyBleGFjdCBjb3VudHMgd291bGQgbmVlZCBzY2FuRWxlbWVudCBvblxuICAgIC8vIGV2ZXJ5IG1hdGNoZWQgbWVzc2FnZSAofjEtM21zIMOXIE4pLiB0b3RhbCA9IHByZWZpeFN1bVttYXRjaGVzLmxlbmd0aF0uXG4gICAgcHJlZml4U3VtOiBbXSBhcyBudW1iZXJbXSxcbiAgfSlcbiAgLy8gc2Nyb2xsVG9wIGF0IHRoZSBtb21lbnQgLyB3YXMgcHJlc3NlZC4gSW5jc2VhcmNoIHByZXZpZXctanVtcHMgc25hcFxuICAvLyBiYWNrIGhlcmUgd2hlbiBtYXRjaGVzIGRyb3AgdG8gMC4gLTEgPSBubyBhbmNob3IgKGJlZm9yZSBmaXJzdCAvKS5cbiAgY29uc3Qgc2VhcmNoQW5jaG9yID0gdXNlUmVmKC0xKVxuICBjb25zdCBpbmRleFdhcm1lZCA9IHVzZVJlZihmYWxzZSlcblxuICAvLyBTY3JvbGwgdGFyZ2V0IGZvciBtZXNzYWdlIGk6IGxhbmQgYXQgTUVTU0FHRSBUT1AuIGVzdCA9IHRvcCAtIEhFQURST09NXG4gIC8vIHNvIGxvID0gdG9wIC0gZXN0ID0gSEVBRFJPT00g4omlIDAgKG9yIGxvID0gdG9wIGlmIGVzdCBjbGFtcGVkIHRvIDApLlxuICAvLyBQb3N0LWNsYW1wIHJlYWQtYmFjayBpbiBqdW1wKCkgaGFuZGxlcyB0aGUgc2Nyb2xsSGVpZ2h0IGJvdW5kYXJ5LlxuICAvLyBObyBmcmFjIChyZW5kZXIgdHJhbnNmb3JtIGRpZG4ndCByZXNwZWN0IGl0KSwgbm8gbW9ub3RvbmUgY2xhbXBcbiAgLy8gKHdhcyBhIHNhZmV0eSBuZXQgZm9yIGZyYWMgZ2FyYmFnZSDigJQgd2l0aG91dCBmcmFjLCBlc3QgSVMgdGhlIG5leHRcbiAgLy8gbWVzc2FnZSdzIHRvcCwgc3BhbS1uL04gY29udmVyZ2VzIGJlY2F1c2UgbWVzc2FnZSB0b3BzIGFyZSBvcmRlcmVkKS5cbiAgZnVuY3Rpb24gdGFyZ2V0Rm9yKGk6IG51bWJlcik6IG51bWJlciB7XG4gICAgY29uc3QgdG9wID0ganVtcFN0YXRlLmN1cnJlbnQuZ2V0SXRlbVRvcChpKVxuICAgIHJldHVybiBNYXRoLm1heCgwLCB0b3AgLSBIRUFEUk9PTSlcbiAgfVxuXG4gIC8vIEhpZ2hsaWdodCBwb3NpdGlvbnNbb3JkXS4gUG9zaXRpb25zIGFyZSBNRVNTQUdFLVJFTEFUSVZFIChyb3cgMCA9XG4gIC8vIGVsZW1lbnQgdG9wLCBmcm9tIHNjYW5FbGVtZW50KS4gQ29tcHV0ZSByb3dPZmZzZXQgPSBnZXRJdGVtVG9wIC1cbiAgLy8gc2Nyb2xsVG9wIGZyZXNoLiBJZiBvcmQncyBwb3NpdGlvbiBpcyBvZmYtdmlld3BvcnQsIHNjcm9sbCB0byBicmluZ1xuICAvLyBpdCBpbiwgcmVjb21wdXRlIHJvd09mZnNldC4gc2V0UG9zaXRpb25zIHRyaWdnZXJzIG92ZXJsYXkgd3JpdGUuXG4gIGZ1bmN0aW9uIGhpZ2hsaWdodChvcmQ6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IHMgPSBzY3JvbGxSZWYuY3VycmVudFxuICAgIGNvbnN0IHsgbXNnSWR4LCBwb3NpdGlvbnMgfSA9IGVsZW1lbnRQb3NpdGlvbnMuY3VycmVudFxuICAgIGlmICghcyB8fCBwb3NpdGlvbnMubGVuZ3RoID09PSAwIHx8IG1zZ0lkeCA8IDApIHtcbiAgICAgIHNldFBvc2l0aW9ucz8uKG51bGwpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgY29uc3QgaWR4ID0gTWF0aC5tYXgoMCwgTWF0aC5taW4ob3JkLCBwb3NpdGlvbnMubGVuZ3RoIC0gMSkpXG4gICAgY29uc3QgcCA9IHBvc2l0aW9uc1tpZHhdIVxuICAgIGNvbnN0IHRvcCA9IGp1bXBTdGF0ZS5jdXJyZW50LmdldEl0ZW1Ub3AobXNnSWR4KVxuICAgIC8vIGxvID0gaXRlbSdzIHBvc2l0aW9uIHdpdGhpbiBzY3JvbGwgY29udGVudCAod3JhcHBlci1yZWxhdGl2ZSkuXG4gICAgLy8gdmlld3BvcnRUb3AgPSB3aGVyZSB0aGUgc2Nyb2xsIGNvbnRlbnQgc3RhcnRzIG9uIFNDUkVFTiAoYWZ0ZXJcbiAgICAvLyBTY3JvbGxCb3ggcGFkZGluZy9ib3JkZXIgKyBhbnkgY2hyb21lIGFib3ZlKS4gSGlnaGxpZ2h0IHdyaXRlcyB0b1xuICAgIC8vIHNjcmVlbi1hYnNvbHV0ZSwgc28gcm93T2Zmc2V0ID0gdmlld3BvcnRUb3AgKyBsby4gT2JzZXJ2ZWQ6IG9mZi1ieS1cbiAgICAvLyAxKyB3aXRob3V0IHZpZXdwb3J0VG9wIChGdWxsc2NyZWVuTGF5b3V0IGhhcyBwYWRkaW5nVG9wPTEgb24gdGhlXG4gICAgLy8gU2Nyb2xsQm94LCBwbHVzIGFueSBoZWFkZXIgYWJvdmUpLlxuICAgIGNvbnN0IHZwVG9wID0gcy5nZXRWaWV3cG9ydFRvcCgpXG4gICAgbGV0IGxvID0gdG9wIC0gcy5nZXRTY3JvbGxUb3AoKVxuICAgIGNvbnN0IHZwID0gcy5nZXRWaWV3cG9ydEhlaWdodCgpXG4gICAgbGV0IHNjcmVlblJvdyA9IHZwVG9wICsgbG8gKyBwLnJvd1xuICAgIC8vIE9mZiB2aWV3cG9ydCDihpIgc2Nyb2xsIHRvIGJyaW5nIGl0IGluIChIRUFEUk9PTSBmcm9tIHRvcCkuXG4gICAgLy8gc2Nyb2xsVG8gY29tbWl0cyBzeW5jOyByZWFkLWJhY2sgYWZ0ZXIgZ2l2ZXMgZnJlc2ggbG8uXG4gICAgaWYgKHNjcmVlblJvdyA8IHZwVG9wIHx8IHNjcmVlblJvdyA+PSB2cFRvcCArIHZwKSB7XG4gICAgICBzLnNjcm9sbFRvKE1hdGgubWF4KDAsIHRvcCArIHAucm93IC0gSEVBRFJPT00pKVxuICAgICAgbG8gPSB0b3AgLSBzLmdldFNjcm9sbFRvcCgpXG4gICAgICBzY3JlZW5Sb3cgPSB2cFRvcCArIGxvICsgcC5yb3dcbiAgICB9XG4gICAgc2V0UG9zaXRpb25zPy4oeyBwb3NpdGlvbnMsIHJvd09mZnNldDogdnBUb3AgKyBsbywgY3VycmVudElkeDogaWR4IH0pXG4gICAgLy8gQmFkZ2U6IGdsb2JhbCBjdXJyZW50ID0gc3VtIG9mIG9jY3VycmVuY2VzIGJlZm9yZSB0aGlzIG1zZyArIG9yZCsxLlxuICAgIC8vIHByZWZpeFN1bVtwdHJdIGlzIGVuZ2luZS1jb3VudGVkIChpbmRleE9mIG9uIGV4dHJhY3RTZWFyY2hUZXh0KTtcbiAgICAvLyBtYXkgZHJpZnQgZnJvbSByZW5kZXItY291bnQgZm9yIGdob3N0IG1lc3NhZ2VzIGJ1dCBjbG9zZSBlbm91Z2gg4oCUXG4gICAgLy8gYmFkZ2UgaXMgYSByb3VnaCBsb2NhdGlvbiBoaW50LCBub3QgYSBwcm9vZi5cbiAgICBjb25zdCBzdCA9IHNlYXJjaFN0YXRlLmN1cnJlbnRcbiAgICBjb25zdCB0b3RhbCA9IHN0LnByZWZpeFN1bS5hdCgtMSkgPz8gMFxuICAgIGNvbnN0IGN1cnJlbnQgPSAoc3QucHJlZml4U3VtW3N0LnB0cl0gPz8gMCkgKyBpZHggKyAxXG4gICAgb25TZWFyY2hNYXRjaGVzQ2hhbmdlPy4odG90YWwsIGN1cnJlbnQpXG4gICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgYGhpZ2hsaWdodChpPSR7bXNnSWR4fSwgb3JkPSR7aWR4fS8ke3Bvc2l0aW9ucy5sZW5ndGh9KTogYCArXG4gICAgICAgIGBwb3M9e3Jvdzoke3Aucm93fSxjb2w6JHtwLmNvbH19IGxvPSR7bG99IHNjcmVlblJvdz0ke3NjcmVlblJvd30gYCArXG4gICAgICAgIGBiYWRnZT0ke2N1cnJlbnR9LyR7dG90YWx9YCxcbiAgICApXG4gIH1cbiAgaGlnaGxpZ2h0UmVmLmN1cnJlbnQgPSBoaWdobGlnaHRcblxuICAvLyBTZWVrIGVmZmVjdC4ganVtcCgpIHNldHMgc2NhblJlcXVlc3RSZWYgKyBzY3JvbGxUb0luZGV4ICsgYnVtcC5cbiAgLy8gYnVtcCDihpIgcmUtcmVuZGVyIOKGkiB1c2VWaXJ0dWFsU2Nyb2xsIG1vdW50cyB0aGUgdGFyZ2V0IChzY3JvbGxUb0luZGV4XG4gIC8vIGd1YXJhbnRlZXMgdGhpcyDigJQgc2Nyb2xsVG9wIGFuZCB0b3BTcGFjZXIgYWdyZWUgdmlhIHRoZSBzYW1lXG4gIC8vIG9mZnNldHMgdmFsdWUpIOKGkiByZXNldEFmdGVyQ29tbWl0IHBhaW50cyDihpIgdGhpcyBwYXNzaXZlIGVmZmVjdFxuICAvLyBmaXJlcyBQT1NULVBBSU5UIHdpdGggdGhlIGVsZW1lbnQgbW91bnRlZC4gUHJlY2lzZSBzY3JvbGxUbyArIHNjYW4uXG4gIC8vXG4gIC8vIERlcCBpcyBPTkxZIHNlZWtHZW4g4oCUIGVmZmVjdCBkb2Vzbid0IHJlLXJ1biBvbiByYW5kb20gcmVuZGVyc1xuICAvLyAob25TZWFyY2hNYXRjaGVzQ2hhbmdlIGNodXJuIGR1cmluZyBpbmNzZWFyY2gpLlxuICBjb25zdCBbc2Vla0dlbiwgc2V0U2Vla0dlbl0gPSB1c2VTdGF0ZSgwKVxuICBjb25zdCBidW1wU2VlayA9IHVzZUNhbGxiYWNrKCgpID0+IHNldFNlZWtHZW4oZyA9PiBnICsgMSksIFtdKVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc3QgcmVxID0gc2NhblJlcXVlc3RSZWYuY3VycmVudFxuICAgIGlmICghcmVxKSByZXR1cm5cbiAgICBjb25zdCB7IGlkeCwgd2FudExhc3QsIHRyaWVzIH0gPSByZXFcbiAgICBjb25zdCBzID0gc2Nyb2xsUmVmLmN1cnJlbnRcbiAgICBpZiAoIXMpIHJldHVyblxuICAgIGNvbnN0IHsgZ2V0SXRlbUVsZW1lbnQsIGdldEl0ZW1Ub3AsIHNjcm9sbFRvSW5kZXggfSA9IGp1bXBTdGF0ZS5jdXJyZW50XG4gICAgY29uc3QgZWwgPSBnZXRJdGVtRWxlbWVudChpZHgpXG4gICAgY29uc3QgaCA9IGVsPy55b2dhTm9kZT8uZ2V0Q29tcHV0ZWRIZWlnaHQoKSA/PyAwXG5cbiAgICBpZiAoIWVsIHx8IGggPT09IDApIHtcbiAgICAgIC8vIE5vdCBtb3VudGVkIGFmdGVyIHNjcm9sbFRvSW5kZXguIFNob3VsZG4ndCBoYXBwZW4g4oCUIHNjcm9sbFRvSW5kZXhcbiAgICAgIC8vIGd1YXJhbnRlZXMgbW91bnQgYnkgY29uc3RydWN0aW9uIChzY3JvbGxUb3AgYW5kIHRvcFNwYWNlciBhZ3JlZVxuICAgICAgLy8gdmlhIHRoZSBzYW1lIG9mZnNldHMgdmFsdWUpLiBTYW5pdHk6IHJldHJ5IG9uY2UsIHRoZW4gc2tpcC5cbiAgICAgIGlmICh0cmllcyA+IDEpIHtcbiAgICAgICAgc2NhblJlcXVlc3RSZWYuY3VycmVudCA9IG51bGxcbiAgICAgICAgbG9nRm9yRGVidWdnaW5nKGBzZWVrKGk9JHtpZHh9KTogbm8gbW91bnQgYWZ0ZXIgc2Nyb2xsVG9JbmRleCwgc2tpcGApXG4gICAgICAgIHN0ZXBSZWYuY3VycmVudCh3YW50TGFzdCA/IC0xIDogMSlcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBzY2FuUmVxdWVzdFJlZi5jdXJyZW50ID0geyBpZHgsIHdhbnRMYXN0LCB0cmllczogdHJpZXMgKyAxIH1cbiAgICAgIHNjcm9sbFRvSW5kZXgoaWR4KVxuICAgICAgYnVtcFNlZWsoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgc2NhblJlcXVlc3RSZWYuY3VycmVudCA9IG51bGxcbiAgICAvLyBQcmVjaXNlIHNjcm9sbFRvIOKAlCBzY3JvbGxUb0luZGV4IGdvdCB1cyBpbiB0aGUgbmVpZ2hib3Job29kXG4gICAgLy8gKGl0ZW0gaXMgbW91bnRlZCwgbWF5YmUgYSBmZXctZG96ZW4gcm93cyBvZmYgZHVlIHRvIG92ZXJzY2FuXG4gICAgLy8gZXN0aW1hdGUgZHJpZnQpLiBOb3cgbGFuZCBpdCBhdCB0b3AtSEVBRFJPT00uXG4gICAgcy5zY3JvbGxUbyhNYXRoLm1heCgwLCBnZXRJdGVtVG9wKGlkeCkgLSBIRUFEUk9PTSkpXG4gICAgY29uc3QgcG9zaXRpb25zID0gc2NhbkVsZW1lbnQ/LihlbCkgPz8gW11cbiAgICBlbGVtZW50UG9zaXRpb25zLmN1cnJlbnQgPSB7IG1zZ0lkeDogaWR4LCBwb3NpdGlvbnMgfVxuICAgIGxvZ0ZvckRlYnVnZ2luZyhgc2VlayhpPSR7aWR4fSB0PSR7dHJpZXN9KTogJHtwb3NpdGlvbnMubGVuZ3RofSBwb3NpdGlvbnNgKVxuICAgIGlmIChwb3NpdGlvbnMubGVuZ3RoID09PSAwKSB7XG4gICAgICAvLyBQaGFudG9tIOKAlCBlbmdpbmUgbWF0Y2hlZCwgcmVuZGVyIGRpZG4ndC4gQXV0by1hZHZhbmNlLlxuICAgICAgaWYgKCsrcGhhbnRvbUJ1cnN0UmVmLmN1cnJlbnQgPiAyMCkge1xuICAgICAgICBwaGFudG9tQnVyc3RSZWYuY3VycmVudCA9IDBcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBzdGVwUmVmLmN1cnJlbnQod2FudExhc3QgPyAtMSA6IDEpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgcGhhbnRvbUJ1cnN0UmVmLmN1cnJlbnQgPSAwXG4gICAgY29uc3Qgb3JkID0gd2FudExhc3QgPyBwb3NpdGlvbnMubGVuZ3RoIC0gMSA6IDBcbiAgICBzZWFyY2hTdGF0ZS5jdXJyZW50LnNjcmVlbk9yZCA9IG9yZFxuICAgIHN0YXJ0UHRyUmVmLmN1cnJlbnQgPSAtMVxuICAgIGhpZ2hsaWdodFJlZi5jdXJyZW50KG9yZClcbiAgICBjb25zdCBwZW5kaW5nID0gcGVuZGluZ1N0ZXBSZWYuY3VycmVudFxuICAgIGlmIChwZW5kaW5nKSB7XG4gICAgICBwZW5kaW5nU3RlcFJlZi5jdXJyZW50ID0gMFxuICAgICAgc3RlcFJlZi5jdXJyZW50KHBlbmRpbmcpXG4gICAgfVxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZWFjdC1ob29rcy9leGhhdXN0aXZlLWRlcHNcbiAgfSwgW3NlZWtHZW5dKVxuXG4gIC8vIFNjcm9sbCB0byBtZXNzYWdlIGkncyB0b3AsIGFybSBzY2FuUGVuZGluZy4gc2Nhbi1lZmZlY3QgcmVhZHMgZnJlc2hcbiAgLy8gc2NyZWVuIG5leHQgdGljay4gd2FudExhc3Q6IE4taW50by1tZXNzYWdlIOKAlCBzY3JlZW5PcmQgPSBsZW5ndGgtMS5cbiAgZnVuY3Rpb24ganVtcChpOiBudW1iZXIsIHdhbnRMYXN0OiBib29sZWFuKTogdm9pZCB7XG4gICAgY29uc3QgcyA9IHNjcm9sbFJlZi5jdXJyZW50XG4gICAgaWYgKCFzKSByZXR1cm5cbiAgICBjb25zdCBqcyA9IGp1bXBTdGF0ZS5jdXJyZW50XG4gICAgY29uc3QgeyBnZXRJdGVtRWxlbWVudCwgc2Nyb2xsVG9JbmRleCB9ID0ganNcbiAgICAvLyBvZmZzZXRzIGlzIGEgRmxvYXQ2NEFycmF5IHdob3NlIC5sZW5ndGggaXMgdGhlIGFsbG9jYXRlZCBidWZmZXIgKG9ubHlcbiAgICAvLyBncm93cykg4oCUIG1lc3NhZ2VzLmxlbmd0aCBpcyB0aGUgbG9naWNhbCBpdGVtIGNvdW50LlxuICAgIGlmIChpIDwgMCB8fCBpID49IGpzLm1lc3NhZ2VzLmxlbmd0aCkgcmV0dXJuXG4gICAgLy8gQ2xlYXIgc3RhbGUgaGlnaGxpZ2h0IGJlZm9yZSBzY3JvbGwuIEJldHdlZW4gbm93IGFuZCB0aGUgc2Vla1xuICAgIC8vIGVmZmVjdCdzIGhpZ2hsaWdodCwgaW52ZXJzZS1vbmx5IGZyb20gc2Nhbi1oaWdobGlnaHQgc2hvd3MuXG4gICAgc2V0UG9zaXRpb25zPy4obnVsbClcbiAgICBlbGVtZW50UG9zaXRpb25zLmN1cnJlbnQgPSB7IG1zZ0lkeDogLTEsIHBvc2l0aW9uczogW10gfVxuICAgIHNjYW5SZXF1ZXN0UmVmLmN1cnJlbnQgPSB7IGlkeDogaSwgd2FudExhc3QsIHRyaWVzOiAwIH1cbiAgICBjb25zdCBlbCA9IGdldEl0ZW1FbGVtZW50KGkpXG4gICAgY29uc3QgaCA9IGVsPy55b2dhTm9kZT8uZ2V0Q29tcHV0ZWRIZWlnaHQoKSA/PyAwXG4gICAgLy8gTW91bnRlZCDihpIgcHJlY2lzZSBzY3JvbGxUby4gVW5tb3VudGVkIOKGkiBzY3JvbGxUb0luZGV4IG1vdW50cyBpdFxuICAgIC8vIChzY3JvbGxUb3AgYW5kIHRvcFNwYWNlciBhZ3JlZSB2aWEgdGhlIHNhbWUgb2Zmc2V0cyB2YWx1ZSDigJQgZXhhY3RcbiAgICAvLyBieSBjb25zdHJ1Y3Rpb24sIG5vIGVzdGltYXRpb24pLiBTZWVrIGVmZmVjdCBkb2VzIHRoZSBwcmVjaXNlXG4gICAgLy8gc2Nyb2xsVG8gYWZ0ZXIgcGFpbnQgZWl0aGVyIHdheS5cbiAgICBpZiAoZWwgJiYgaCA+IDApIHtcbiAgICAgIHMuc2Nyb2xsVG8odGFyZ2V0Rm9yKGkpKVxuICAgIH0gZWxzZSB7XG4gICAgICBzY3JvbGxUb0luZGV4KGkpXG4gICAgfVxuICAgIGJ1bXBTZWVrKClcbiAgfVxuXG4gIC8vIEFkdmFuY2Ugc2NyZWVuT3JkIHdpdGhpbiBlbGVtZW50UG9zaXRpb25zLiBFeGhhdXN0ZWQg4oaSIHB0ciBhZHZhbmNlcyxcbiAgLy8ganVtcCB0byBuZXh0IG1hdGNoZXNbcHRyXSwgcmUtc2Nhbi4gUGhhbnRvbSAoc2NhbiBmb3VuZCAwIGFmdGVyXG4gIC8vIGp1bXApIHRyaWdnZXJzIGF1dG8tYWR2YW5jZSBmcm9tIHNjYW4tZWZmZWN0LiBXcmFwYXJvdW5kIGd1YXJkIHN0b3BzXG4gIC8vIGlmIGV2ZXJ5IG1lc3NhZ2UgaXMgYSBwaGFudG9tLlxuICBmdW5jdGlvbiBzdGVwKGRlbHRhOiAxIHwgLTEpOiB2b2lkIHtcbiAgICBjb25zdCBzdCA9IHNlYXJjaFN0YXRlLmN1cnJlbnRcbiAgICBjb25zdCB7IG1hdGNoZXMsIHByZWZpeFN1bSB9ID0gc3RcbiAgICBjb25zdCB0b3RhbCA9IHByZWZpeFN1bS5hdCgtMSkgPz8gMFxuICAgIGlmIChtYXRjaGVzLmxlbmd0aCA9PT0gMCkgcmV0dXJuXG5cbiAgICAvLyBTZWVrIGluLWZsaWdodCDigJQgcXVldWUgdGhpcyBwcmVzcyAob25lLWRlZXAsIGxhdGVzdCBvdmVyd3JpdGVzKS5cbiAgICAvLyBUaGUgc2VlayBlZmZlY3QgZmlyZXMgaXQgYWZ0ZXIgaGlnaGxpZ2h0LlxuICAgIGlmIChzY2FuUmVxdWVzdFJlZi5jdXJyZW50KSB7XG4gICAgICBwZW5kaW5nU3RlcFJlZi5jdXJyZW50ID0gZGVsdGFcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGlmIChzdGFydFB0clJlZi5jdXJyZW50IDwgMCkgc3RhcnRQdHJSZWYuY3VycmVudCA9IHN0LnB0clxuXG4gICAgY29uc3QgeyBwb3NpdGlvbnMgfSA9IGVsZW1lbnRQb3NpdGlvbnMuY3VycmVudFxuICAgIGNvbnN0IG5ld09yZCA9IHN0LnNjcmVlbk9yZCArIGRlbHRhXG4gICAgaWYgKG5ld09yZCA+PSAwICYmIG5ld09yZCA8IHBvc2l0aW9ucy5sZW5ndGgpIHtcbiAgICAgIHN0LnNjcmVlbk9yZCA9IG5ld09yZFxuICAgICAgaGlnaGxpZ2h0KG5ld09yZCkgLy8gdXBkYXRlcyBiYWRnZSBpbnRlcm5hbGx5XG4gICAgICBzdGFydFB0clJlZi5jdXJyZW50ID0gLTFcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIEV4aGF1c3RlZCB2aXNpYmxlLiBBZHZhbmNlIHB0ciDihpIganVtcCDihpIgcmUtc2Nhbi5cbiAgICBjb25zdCBwdHIgPSAoc3QucHRyICsgZGVsdGEgKyBtYXRjaGVzLmxlbmd0aCkgJSBtYXRjaGVzLmxlbmd0aFxuICAgIGlmIChwdHIgPT09IHN0YXJ0UHRyUmVmLmN1cnJlbnQpIHtcbiAgICAgIHNldFBvc2l0aW9ucz8uKG51bGwpXG4gICAgICBzdGFydFB0clJlZi5jdXJyZW50ID0gLTFcbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgYHN0ZXA6IHdyYXBhcm91bmQgYXQgcHRyPSR7cHRyfSwgYWxsICR7bWF0Y2hlcy5sZW5ndGh9IG1zZ3MgcGhhbnRvbXNgLFxuICAgICAgKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIHN0LnB0ciA9IHB0clxuICAgIHN0LnNjcmVlbk9yZCA9IDAgLy8gcmVzb2x2ZWQgYWZ0ZXIgc2NhbiAod2FudExhc3Qg4oaSIGxlbmd0aC0xKVxuICAgIGp1bXAobWF0Y2hlc1twdHJdISwgZGVsdGEgPCAwKVxuICAgIC8vIHNjcmVlbk9yZCB3aWxsIHJlc29sdmUgYWZ0ZXIgc2Nhbi4gQmVzdC1lZmZvcnQ6IHByZWZpeFN1bVtwdHJdICsgMFxuICAgIC8vIGZvciBuIChmaXJzdCBwb3MpLCBwcmVmaXhTdW1bcHRyKzFdIGZvciBOIChsYXN0IHBvcyA9IGNvdW50LTEpLlxuICAgIC8vIFRoZSBzY2FuLWVmZmVjdCdzIGhpZ2hsaWdodCB3aWxsIGJlIHRoZSByZWFsIHZhbHVlOyB0aGlzIGlzIGFcbiAgICAvLyBwcmUtc2NhbiBwbGFjZWhvbGRlciBzbyB0aGUgYmFkZ2UgdXBkYXRlcyBpbW1lZGlhdGVseS5cbiAgICBjb25zdCBwbGFjZWhvbGRlciA9XG4gICAgICBkZWx0YSA8IDAgPyAocHJlZml4U3VtW3B0ciArIDFdID8/IHRvdGFsKSA6IHByZWZpeFN1bVtwdHJdISArIDFcbiAgICBvblNlYXJjaE1hdGNoZXNDaGFuZ2U/Lih0b3RhbCwgcGxhY2Vob2xkZXIpXG4gIH1cbiAgc3RlcFJlZi5jdXJyZW50ID0gc3RlcFxuXG4gIHVzZUltcGVyYXRpdmVIYW5kbGUoXG4gICAganVtcFJlZixcbiAgICAoKSA9PiAoe1xuICAgICAgLy8gTm9uLXNlYXJjaCBqdW1wIChzdGlja3kgaGVhZGVyIGNsaWNrLCBldGMpLiBObyBzY2FuLCBubyBwb3NpdGlvbnMuXG4gICAgICBqdW1wVG9JbmRleDogKGk6IG51bWJlcikgPT4ge1xuICAgICAgICBjb25zdCBzID0gc2Nyb2xsUmVmLmN1cnJlbnRcbiAgICAgICAgaWYgKHMpIHMuc2Nyb2xsVG8odGFyZ2V0Rm9yKGkpKVxuICAgICAgfSxcbiAgICAgIHNldFNlYXJjaFF1ZXJ5OiAocTogc3RyaW5nKSA9PiB7XG4gICAgICAgIC8vIE5ldyBzZWFyY2ggaW52YWxpZGF0ZXMgZXZlcnl0aGluZy5cbiAgICAgICAgc2NhblJlcXVlc3RSZWYuY3VycmVudCA9IG51bGxcbiAgICAgICAgZWxlbWVudFBvc2l0aW9ucy5jdXJyZW50ID0geyBtc2dJZHg6IC0xLCBwb3NpdGlvbnM6IFtdIH1cbiAgICAgICAgc3RhcnRQdHJSZWYuY3VycmVudCA9IC0xXG4gICAgICAgIHNldFBvc2l0aW9ucz8uKG51bGwpXG4gICAgICAgIGNvbnN0IGxxID0gcS50b0xvd2VyQ2FzZSgpXG4gICAgICAgIC8vIE9uZSBlbnRyeSBwZXIgTUVTU0FHRSAoZGVkdXBsaWNhdGVkKS4gQm9vbGVhbiBcImRvZXMgdGhpcyBtc2dcbiAgICAgICAgLy8gY29udGFpbiB0aGUgcXVlcnlcIi4gfjEwbXMgZm9yIDlrIG1lc3NhZ2VzIHdpdGggY2FjaGVkIGxvd2VyZWQuXG4gICAgICAgIGNvbnN0IG1hdGNoZXM6IG51bWJlcltdID0gW11cbiAgICAgICAgLy8gUGVyLW1lc3NhZ2Ugb2NjdXJyZW5jZSBjb3VudCDihpIgcHJlZml4U3VtIGZvciBnbG9iYWwgY3VycmVudFxuICAgICAgICAvLyBpbmRleC4gRW5naW5lLWNvdW50ZWQgKGNoZWFwIGluZGV4T2YgbG9vcCk7IG1heSBkaWZmZXIgZnJvbVxuICAgICAgICAvLyByZW5kZXItY291bnQgKHNjYW5FbGVtZW50KSBmb3IgZ2hvc3QvcGhhbnRvbSBtZXNzYWdlcyBidXQgY2xvc2VcbiAgICAgICAgLy8gZW5vdWdoIGZvciB0aGUgYmFkZ2UuIFRoZSBiYWRnZSBpcyBhIHJvdWdoIGxvY2F0aW9uIGhpbnQuXG4gICAgICAgIGNvbnN0IHByZWZpeFN1bTogbnVtYmVyW10gPSBbMF1cbiAgICAgICAgaWYgKGxxKSB7XG4gICAgICAgICAgY29uc3QgbXNncyA9IGp1bXBTdGF0ZS5jdXJyZW50Lm1lc3NhZ2VzXG4gICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBtc2dzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgICBjb25zdCB0ZXh0ID0gZXh0cmFjdFNlYXJjaFRleHQobXNnc1tpXSEpXG4gICAgICAgICAgICBsZXQgcG9zID0gdGV4dC5pbmRleE9mKGxxKVxuICAgICAgICAgICAgbGV0IGNudCA9IDBcbiAgICAgICAgICAgIHdoaWxlIChwb3MgPj0gMCkge1xuICAgICAgICAgICAgICBjbnQrK1xuICAgICAgICAgICAgICBwb3MgPSB0ZXh0LmluZGV4T2YobHEsIHBvcyArIGxxLmxlbmd0aClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChjbnQgPiAwKSB7XG4gICAgICAgICAgICAgIG1hdGNoZXMucHVzaChpKVxuICAgICAgICAgICAgICBwcmVmaXhTdW0ucHVzaChwcmVmaXhTdW0uYXQoLTEpISArIGNudClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdG90YWwgPSBwcmVmaXhTdW0uYXQoLTEpIVxuICAgICAgICAvLyBOZWFyZXN0IE1FU1NBR0UgdG8gdGhlIGFuY2hvci4gPD0gc28gdGllcyBnbyB0byBsYXRlci5cbiAgICAgICAgbGV0IHB0ciA9IDBcbiAgICAgICAgY29uc3QgcyA9IHNjcm9sbFJlZi5jdXJyZW50XG4gICAgICAgIGNvbnN0IHsgb2Zmc2V0cywgc3RhcnQsIGdldEl0ZW1Ub3AgfSA9IGp1bXBTdGF0ZS5jdXJyZW50XG4gICAgICAgIGNvbnN0IGZpcnN0VG9wID0gZ2V0SXRlbVRvcChzdGFydClcbiAgICAgICAgY29uc3Qgb3JpZ2luID0gZmlyc3RUb3AgPj0gMCA/IGZpcnN0VG9wIC0gb2Zmc2V0c1tzdGFydF0hIDogMFxuICAgICAgICBpZiAobWF0Y2hlcy5sZW5ndGggPiAwICYmIHMpIHtcbiAgICAgICAgICBjb25zdCBjdXJUb3AgPVxuICAgICAgICAgICAgc2VhcmNoQW5jaG9yLmN1cnJlbnQgPj0gMCA/IHNlYXJjaEFuY2hvci5jdXJyZW50IDogcy5nZXRTY3JvbGxUb3AoKVxuICAgICAgICAgIGxldCBiZXN0ID0gSW5maW5pdHlcbiAgICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IG1hdGNoZXMubGVuZ3RoOyBrKyspIHtcbiAgICAgICAgICAgIGNvbnN0IGQgPSBNYXRoLmFicyhvcmlnaW4gKyBvZmZzZXRzW21hdGNoZXNba10hXSEgLSBjdXJUb3ApXG4gICAgICAgICAgICBpZiAoZCA8PSBiZXN0KSB7XG4gICAgICAgICAgICAgIGJlc3QgPSBkXG4gICAgICAgICAgICAgIHB0ciA9IGtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYHNldFNlYXJjaFF1ZXJ5KCcke3F9Jyk6ICR7bWF0Y2hlcy5sZW5ndGh9IG1zZ3MgwrcgcHRyPSR7cHRyfSBgICtcbiAgICAgICAgICAgICAgYG1zZ0lkeD0ke21hdGNoZXNbcHRyXX0gY3VyVG9wPSR7Y3VyVG9wfSBvcmlnaW49JHtvcmlnaW59YCxcbiAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgICAgc2VhcmNoU3RhdGUuY3VycmVudCA9IHsgbWF0Y2hlcywgcHRyLCBzY3JlZW5PcmQ6IDAsIHByZWZpeFN1bSB9XG4gICAgICAgIGlmIChtYXRjaGVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAvLyB3YW50TGFzdD10cnVlOiBwcmV2aWV3IHRoZSBMQVNUIG9jY3VycmVuY2UgaW4gdGhlIG5lYXJlc3RcbiAgICAgICAgICAvLyBtZXNzYWdlLiBBdCBzdGlja3ktYm90dG9tIChjb21tb24gLyBlbnRyeSksIG5lYXJlc3QgaXMgdGhlXG4gICAgICAgICAgLy8gbGFzdCBtc2c7IGl0cyBsYXN0IG9jY3VycmVuY2UgaXMgY2xvc2VzdCB0byB3aGVyZSB0aGUgdXNlclxuICAgICAgICAgIC8vIHdhcyDigJQgbWluaW1hbCB2aWV3IG1vdmVtZW50LiBuIGFkdmFuY2VzIGZvcndhcmQgZnJvbSB0aGVyZS5cbiAgICAgICAgICBqdW1wKG1hdGNoZXNbcHRyXSEsIHRydWUpXG4gICAgICAgIH0gZWxzZSBpZiAoc2VhcmNoQW5jaG9yLmN1cnJlbnQgPj0gMCAmJiBzKSB7XG4gICAgICAgICAgLy8gL2Zvb2Ig4oaSIDAgbWF0Y2hlcyDihpIgc25hcCBiYWNrIHRvIGFuY2hvci4gbGVzcy92aW0gaW5jc2VhcmNoLlxuICAgICAgICAgIHMuc2Nyb2xsVG8oc2VhcmNoQW5jaG9yLmN1cnJlbnQpXG4gICAgICAgIH1cbiAgICAgICAgLy8gR2xvYmFsIG9jY3VycmVuY2UgY291bnQgKyAxLWJhc2VkIGN1cnJlbnQuIHdhbnRMYXN0PXRydWUgc28gdGhlXG4gICAgICAgIC8vIHNjYW4gd2lsbCBsYW5kIG9uIHRoZSBsYXN0IG9jY3VycmVuY2UgaW4gbWF0Y2hlc1twdHJdLiBQbGFjZWhvbGRlclxuICAgICAgICAvLyA9IHByZWZpeFN1bVtwdHIrMV0gKGNvdW50IHRocm91Z2ggdGhpcyBtc2cpLiBoaWdobGlnaHQoKSB1cGRhdGVzXG4gICAgICAgIC8vIHRvIHRoZSBleGFjdCB2YWx1ZSBhZnRlciBzY2FuIGNvbXBsZXRlcy5cbiAgICAgICAgb25TZWFyY2hNYXRjaGVzQ2hhbmdlPy4oXG4gICAgICAgICAgdG90YWwsXG4gICAgICAgICAgbWF0Y2hlcy5sZW5ndGggPiAwID8gKHByZWZpeFN1bVtwdHIgKyAxXSA/PyB0b3RhbCkgOiAwLFxuICAgICAgICApXG4gICAgICB9LFxuICAgICAgbmV4dE1hdGNoOiAoKSA9PiBzdGVwKDEpLFxuICAgICAgcHJldk1hdGNoOiAoKSA9PiBzdGVwKC0xKSxcbiAgICAgIHNldEFuY2hvcjogKCkgPT4ge1xuICAgICAgICBjb25zdCBzID0gc2Nyb2xsUmVmLmN1cnJlbnRcbiAgICAgICAgaWYgKHMpIHNlYXJjaEFuY2hvci5jdXJyZW50ID0gcy5nZXRTY3JvbGxUb3AoKVxuICAgICAgfSxcbiAgICAgIGRpc2FybVNlYXJjaDogKCkgPT4ge1xuICAgICAgICAvLyBNYW51YWwgc2Nyb2xsIGludmFsaWRhdGVzIHNjcmVlbi1hYnNvbHV0ZSBwb3NpdGlvbnMuXG4gICAgICAgIHNldFBvc2l0aW9ucz8uKG51bGwpXG4gICAgICAgIHNjYW5SZXF1ZXN0UmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICAgIGVsZW1lbnRQb3NpdGlvbnMuY3VycmVudCA9IHsgbXNnSWR4OiAtMSwgcG9zaXRpb25zOiBbXSB9XG4gICAgICAgIHN0YXJ0UHRyUmVmLmN1cnJlbnQgPSAtMVxuICAgICAgfSxcbiAgICAgIHdhcm1TZWFyY2hJbmRleDogYXN5bmMgKCkgPT4ge1xuICAgICAgICBpZiAoaW5kZXhXYXJtZWQuY3VycmVudCkgcmV0dXJuIDBcbiAgICAgICAgY29uc3QgbXNncyA9IGp1bXBTdGF0ZS5jdXJyZW50Lm1lc3NhZ2VzXG4gICAgICAgIGNvbnN0IENIVU5LID0gNTAwXG4gICAgICAgIGxldCB3b3JrTXMgPSAwXG4gICAgICAgIGNvbnN0IHdhbGxTdGFydCA9IHBlcmZvcm1hbmNlLm5vdygpXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbXNncy5sZW5ndGg7IGkgKz0gQ0hVTkspIHtcbiAgICAgICAgICBhd2FpdCBzbGVlcCgwKVxuICAgICAgICAgIGNvbnN0IHQwID0gcGVyZm9ybWFuY2Uubm93KClcbiAgICAgICAgICBjb25zdCBlbmQgPSBNYXRoLm1pbihpICsgQ0hVTkssIG1zZ3MubGVuZ3RoKVxuICAgICAgICAgIGZvciAobGV0IGogPSBpOyBqIDwgZW5kOyBqKyspIHtcbiAgICAgICAgICAgIGV4dHJhY3RTZWFyY2hUZXh0KG1zZ3Nbal0hKVxuICAgICAgICAgIH1cbiAgICAgICAgICB3b3JrTXMgKz0gcGVyZm9ybWFuY2Uubm93KCkgLSB0MFxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHdhbGxNcyA9IE1hdGgucm91bmQocGVyZm9ybWFuY2Uubm93KCkgLSB3YWxsU3RhcnQpXG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICBgd2FybVNlYXJjaEluZGV4OiAke21zZ3MubGVuZ3RofSBtc2dzIMK3IHdvcms9JHtNYXRoLnJvdW5kKHdvcmtNcyl9bXMgd2FsbD0ke3dhbGxNc31tcyBjaHVua3M9JHtNYXRoLmNlaWwobXNncy5sZW5ndGggLyBDSFVOSyl9YCxcbiAgICAgICAgKVxuICAgICAgICBpbmRleFdhcm1lZC5jdXJyZW50ID0gdHJ1ZVxuICAgICAgICByZXR1cm4gTWF0aC5yb3VuZCh3b3JrTXMpXG4gICAgICB9LFxuICAgIH0pLFxuICAgIC8vIENsb3N1cmVzIG92ZXIgcmVmcyArIGNhbGxiYWNrcy4gc2Nyb2xsUmVmIHN0YWJsZTsgb3RoZXJzIGFyZVxuICAgIC8vIHVzZUNhbGxiYWNrKFtdKSBvciBwcm9wLWRyaWxsZWQgZnJvbSBSRVBMIChzdGFibGUpLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZWFjdC1ob29rcy9leGhhdXN0aXZlLWRlcHNcbiAgICBbc2Nyb2xsUmVmXSxcbiAgKVxuXG4gIC8vIFN0aWNreVRyYWNrZXIgZ29lcyBBRlRFUiB0aGUgbGlzdCBjb250ZW50LiBJdCByZXR1cm5zIG51bGwgKG5vIERPTSBub2RlKVxuICAvLyBzbyBvcmRlciBzaG91bGRuJ3QgbWF0dGVyIGZvciBsYXlvdXQg4oCUIGJ1dCBwdXR0aW5nIGl0IGZpcnN0IG1lYW5zIGV2ZXJ5XG4gIC8vIGZpbmUtZ3JhaW5lZCBjb21taXQgZnJvbSBpdHMgb3duIHNjcm9sbCBzdWJzY3JpcHRpb24gcmVjb25jaWxlcyBUSFJPVUdIXG4gIC8vIHRoZSBzaWJsaW5nIGl0ZW1zIChSZWFjdCB3YWxrcyBjaGlsZHJlbiBpbiBvcmRlcikuIEFmdGVyIHRoZSBpdGVtcywgaXQnc1xuICAvLyBhIGxlYWYgcmVjb25jaWxlLiBEZWZlbnNpdmU6IGFsc28gYXZvaWRzIGFueSBZb2dhIGNoaWxkLWluZGV4IHF1aXJrcyBpZlxuICAvLyB0aGUgSW5rIHJlY29uY2lsZXIgZXZlciBtYXRlcmlhbGl6ZXMgYSBwbGFjZWhvbGRlciBmb3IgbnVsbCByZXR1cm5zLlxuICBjb25zdCBbaG92ZXJlZEtleSwgc2V0SG92ZXJlZEtleV0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICAvLyBTdGFibGUgY2xpY2svaG92ZXIgaGFuZGxlcnMg4oCUIGNhbGxlZCB3aXRoIGssIGRpc3BhdGNoIGZyb20gYSByZWYgc29cbiAgLy8gY2xvc3VyZSBpZGVudGl0eSBkb2Vzbid0IGNoYW5nZSBwZXIgcmVuZGVyLiBUaGUgcGVyLWl0ZW0gaGFuZGxlclxuICAvLyBjbG9zdXJlcyAoYGUgPT4gLi4uYCwgYCgpID0+IHNldEhvdmVyZWRLZXkoaylgKSB3ZXJlIHRoZVxuICAvLyBgb3BlcmF0aW9uTmV3QXJyb3dGdW5jdGlvbmAgbGVhZnMgaW4gdGhlIHNjcm9sbCBDUFUgcHJvZmlsZTsgdGhlaXJcbiAgLy8gY2xlYW51cCB3YXMgMTYlIG9mIEdDIHRpbWUgKGBGdW5jdGlvbkV4ZWN1dGFibGU6OmZpbmFsaXplVW5jb25kaXRpb25hbGx5YCkuXG4gIC8vIEFsbG9jYXRpbmcgMyBjbG9zdXJlcyDDlyA2MCBtb3VudGVkIGl0ZW1zIMOXIDEwIGNvbW1pdHMvc2VjIGR1cmluZyBmYXN0XG4gIC8vIHNjcm9sbCA9IDE4MDAgc2hvcnQtbGl2ZWQgY2xvc3VyZXMvc2VjLiBXaXRoIHN0YWJsZSByZWZzIHRoZSBpdGVtXG4gIC8vIHdyYXBwZXIgcHJvcHMgZG9uJ3QgY2hhbmdlIOKGkiBWaXJ0dWFsSXRlbS5tZW1vIGJhaWxzIGZvciB0aGUgfjM1XG4gIC8vIHVuY2hhbmdlZCBpdGVtcywgb25seSB+MjUgZnJlc2ggaXRlbXMgcGF5IGNyZWF0ZUVsZW1lbnQgY29zdC5cbiAgY29uc3QgaGFuZGxlcnNSZWYgPSB1c2VSZWYoeyBvbkl0ZW1DbGljaywgc2V0SG92ZXJlZEtleSB9KVxuICBoYW5kbGVyc1JlZi5jdXJyZW50ID0geyBvbkl0ZW1DbGljaywgc2V0SG92ZXJlZEtleSB9XG4gIGNvbnN0IG9uQ2xpY2tLID0gdXNlQ2FsbGJhY2soXG4gICAgKG1zZzogUmVuZGVyYWJsZU1lc3NhZ2UsIGNlbGxJc0JsYW5rOiBib29sZWFuKSA9PiB7XG4gICAgICBjb25zdCBoID0gaGFuZGxlcnNSZWYuY3VycmVudFxuICAgICAgaWYgKCFjZWxsSXNCbGFuayAmJiBoLm9uSXRlbUNsaWNrKSBoLm9uSXRlbUNsaWNrKG1zZylcbiAgICB9LFxuICAgIFtdLFxuICApXG4gIGNvbnN0IG9uRW50ZXJLID0gdXNlQ2FsbGJhY2soKGs6IHN0cmluZykgPT4ge1xuICAgIGhhbmRsZXJzUmVmLmN1cnJlbnQuc2V0SG92ZXJlZEtleShrKVxuICB9LCBbXSlcbiAgY29uc3Qgb25MZWF2ZUsgPSB1c2VDYWxsYmFjaygoazogc3RyaW5nKSA9PiB7XG4gICAgaGFuZGxlcnNSZWYuY3VycmVudC5zZXRIb3ZlcmVkS2V5KHByZXYgPT4gKHByZXYgPT09IGsgPyBudWxsIDogcHJldikpXG4gIH0sIFtdKVxuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxCb3ggcmVmPXtzcGFjZXJSZWZ9IGhlaWdodD17dG9wU3BhY2VyfSBmbGV4U2hyaW5rPXswfSAvPlxuICAgICAge21lc3NhZ2VzLnNsaWNlKHN0YXJ0LCBlbmQpLm1hcCgobXNnLCBpKSA9PiB7XG4gICAgICAgIGNvbnN0IGlkeCA9IHN0YXJ0ICsgaVxuICAgICAgICBjb25zdCBrID0ga2V5c1tpZHhdIVxuICAgICAgICBjb25zdCBjbGlja2FibGUgPSAhIW9uSXRlbUNsaWNrICYmIChpc0l0ZW1DbGlja2FibGU/Lihtc2cpID8/IHRydWUpXG4gICAgICAgIGNvbnN0IGhvdmVyZWQgPSBjbGlja2FibGUgJiYgaG92ZXJlZEtleSA9PT0ga1xuICAgICAgICBjb25zdCBleHBhbmRlZCA9IGlzSXRlbUV4cGFuZGVkPy4obXNnKVxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIDxWaXJ0dWFsSXRlbVxuICAgICAgICAgICAga2V5PXtrfVxuICAgICAgICAgICAgaXRlbUtleT17a31cbiAgICAgICAgICAgIG1zZz17bXNnfVxuICAgICAgICAgICAgaWR4PXtpZHh9XG4gICAgICAgICAgICBtZWFzdXJlUmVmPXttZWFzdXJlUmVmfVxuICAgICAgICAgICAgZXhwYW5kZWQ9e2V4cGFuZGVkfVxuICAgICAgICAgICAgaG92ZXJlZD17aG92ZXJlZH1cbiAgICAgICAgICAgIGNsaWNrYWJsZT17Y2xpY2thYmxlfVxuICAgICAgICAgICAgb25DbGlja0s9e29uQ2xpY2tLfVxuICAgICAgICAgICAgb25FbnRlcks9e29uRW50ZXJLfVxuICAgICAgICAgICAgb25MZWF2ZUs9e29uTGVhdmVLfVxuICAgICAgICAgICAgcmVuZGVySXRlbT17cmVuZGVySXRlbX1cbiAgICAgICAgICAvPlxuICAgICAgICApXG4gICAgICB9KX1cbiAgICAgIHtib3R0b21TcGFjZXIgPiAwICYmIDxCb3ggaGVpZ2h0PXtib3R0b21TcGFjZXJ9IGZsZXhTaHJpbms9ezB9IC8+fVxuICAgICAge3RyYWNrU3RpY2t5UHJvbXB0ICYmIChcbiAgICAgICAgPFN0aWNreVRyYWNrZXJcbiAgICAgICAgICBtZXNzYWdlcz17bWVzc2FnZXN9XG4gICAgICAgICAgc3RhcnQ9e3N0YXJ0fVxuICAgICAgICAgIGVuZD17ZW5kfVxuICAgICAgICAgIG9mZnNldHM9e29mZnNldHN9XG4gICAgICAgICAgZ2V0SXRlbVRvcD17Z2V0SXRlbVRvcH1cbiAgICAgICAgICBnZXRJdGVtRWxlbWVudD17Z2V0SXRlbUVsZW1lbnR9XG4gICAgICAgICAgc2Nyb2xsUmVmPXtzY3JvbGxSZWZ9XG4gICAgICAgIC8+XG4gICAgICApfVxuICAgIDwvPlxuICApXG59XG5cbmNvbnN0IE5PT1BfVU5TVUIgPSAoKSA9PiB7fVxuXG4vKipcbiAqIEVmZmVjdC1vbmx5IGNoaWxkIHRoYXQgdHJhY2tzIHRoZSBsYXN0IHVzZXItcHJvbXB0IHNjcm9sbGVkIGFib3ZlIHRoZVxuICogdmlld3BvcnQgdG9wIGFuZCBmaXJlcyBvbkNoYW5nZSB3aGVuIGl0IGNoYW5nZXMuXG4gKlxuICogUmVuZGVyZWQgYXMgYSBzZXBhcmF0ZSBjb21wb25lbnQgKG5vdCBhIGhvb2sgaW4gVmlydHVhbE1lc3NhZ2VMaXN0KSBzbyBpdFxuICogY2FuIHN1YnNjcmliZSB0byBzY3JvbGwgYXQgRklORVIgZ3JhbnVsYXJpdHkgdGhhbiBTQ1JPTExfUVVBTlRVTT00MC4gVGhlXG4gKiBsaXN0IG5lZWRzIHRoZSBjb2Fyc2UgcXVhbnR1bSB0byBhdm9pZCBwZXItd2hlZWwtdGljayBZb2dhIHJlbGF5b3V0czsgdGhpc1xuICogdHJhY2tlciBpcyBqdXN0IGEgd2FsayArIGNvbXBhcmlzb24gYW5kIGNhbiBhZmZvcmQgdG8gcnVuIGV2ZXJ5IHRpY2suIFdoZW5cbiAqIGl0IHJlLXJlbmRlcnMgYWxvbmUsIHRoZSBsaXN0J3MgcmVjb25jaWxlZCBvdXRwdXQgaXMgdW5jaGFuZ2VkIChzYW1lIHByb3BzXG4gKiBmcm9tIHRoZSBwYXJlbnQncyBsYXN0IGNvbW1pdCkg4oCUIG5vIFlvZ2Egd29yay4gV2l0aG91dCB0aGlzIHNwbGl0LCB0aGVcbiAqIGhlYWRlciBsYWdzIGJ5IH5vbmUgY29udmVyc2F0aW9uIHR1cm4gKDQwIHJvd3Mg4omIIG9uZSBwcm9tcHQgKyByZXNwb25zZSkuXG4gKlxuICogZmlyc3RWaXNpYmxlIGRlcml2YXRpb246IGl0ZW0gQm94ZXMgYXJlIGRpcmVjdCBZb2dhIGNoaWxkcmVuIG9mIHRoZVxuICogU2Nyb2xsQm94IGNvbnRlbnQgd3JhcHBlciAoZnJhZ21lbnRzIGNvbGxhcHNlIGluIHRoZSBJbmsgRE9NKSwgc29cbiAqIHlvZ2EuZ2V0Q29tcHV0ZWRUb3AgaXMgY29udGVudC13cmFwcGVyLXJlbGF0aXZlIOKAlCBzYW1lIGNvb3JkaW5hdGUgc3BhY2UgYXNcbiAqIHNjcm9sbFRvcC4gQ29tcGFyZSBhZ2FpbnN0IHNjcm9sbFRvcCArIHBlbmRpbmdEZWx0YSAodGhlIHNjcm9sbCBUQVJHRVQg4oCUXG4gKiBzY3JvbGxCeSBvbmx5IHNldHMgcGVuZGluZ0RlbHRhLCBjb21taXR0ZWQgc2Nyb2xsVG9wIGxhZ3MpLiBXYWxrIGJhY2t3YXJkXG4gKiBmcm9tIHRoZSBtb3VudC1yYW5nZSBlbmQ7IGJyZWFrIHdoZW4gYW4gaXRlbSdzIHRvcCBpcyBhYm92ZSB0YXJnZXQuXG4gKi9cbmZ1bmN0aW9uIFN0aWNreVRyYWNrZXIoe1xuICBtZXNzYWdlcyxcbiAgc3RhcnQsXG4gIGVuZCxcbiAgb2Zmc2V0cyxcbiAgZ2V0SXRlbVRvcCxcbiAgZ2V0SXRlbUVsZW1lbnQsXG4gIHNjcm9sbFJlZixcbn06IHtcbiAgbWVzc2FnZXM6IFJlbmRlcmFibGVNZXNzYWdlW11cbiAgc3RhcnQ6IG51bWJlclxuICBlbmQ6IG51bWJlclxuICBvZmZzZXRzOiBBcnJheUxpa2U8bnVtYmVyPlxuICBnZXRJdGVtVG9wOiAoaW5kZXg6IG51bWJlcikgPT4gbnVtYmVyXG4gIGdldEl0ZW1FbGVtZW50OiAoaW5kZXg6IG51bWJlcikgPT4gRE9NRWxlbWVudCB8IG51bGxcbiAgc2Nyb2xsUmVmOiBSZWZPYmplY3Q8U2Nyb2xsQm94SGFuZGxlIHwgbnVsbD5cbn0pOiBudWxsIHtcbiAgY29uc3QgeyBzZXRTdGlja3lQcm9tcHQgfSA9IHVzZUNvbnRleHQoU2Nyb2xsQ2hyb21lQ29udGV4dClcbiAgLy8gRmluZS1ncmFpbmVkIHN1YnNjcmlwdGlvbiDigJQgc25hcHNob3QgaXMgdW5xdWFudGl6ZWQgc2Nyb2xsVG9wK2RlbHRhIHNvXG4gIC8vIGV2ZXJ5IHNjcm9sbCBhY3Rpb24gKHdoZWVsIHRpY2ssIFBnVXAsIGRyYWcpIHRyaWdnZXJzIGEgcmUtcmVuZGVyIG9mXG4gIC8vIFRISVMgY29tcG9uZW50IG9ubHkuIFN0aWNreSBiaXQgZm9sZGVkIGludG8gdGhlIHNpZ24gc28gc3RpY2t54oaSYnJva2VuXG4gIC8vIGFsc28gdHJpZ2dlcnMgKHNjcm9sbFRvQm90dG9tIHNldHMgc3RpY2t5IHdpdGhvdXQgbW92aW5nIHNjcm9sbFRvcCkuXG4gIGNvbnN0IHN1YnNjcmliZSA9IHVzZUNhbGxiYWNrKFxuICAgIChsaXN0ZW5lcjogKCkgPT4gdm9pZCkgPT5cbiAgICAgIHNjcm9sbFJlZi5jdXJyZW50Py5zdWJzY3JpYmUobGlzdGVuZXIpID8/IE5PT1BfVU5TVUIsXG4gICAgW3Njcm9sbFJlZl0sXG4gIClcbiAgdXNlU3luY0V4dGVybmFsU3RvcmUoc3Vic2NyaWJlLCAoKSA9PiB7XG4gICAgY29uc3QgcyA9IHNjcm9sbFJlZi5jdXJyZW50XG4gICAgaWYgKCFzKSByZXR1cm4gTmFOXG4gICAgY29uc3QgdCA9IHMuZ2V0U2Nyb2xsVG9wKCkgKyBzLmdldFBlbmRpbmdEZWx0YSgpXG4gICAgcmV0dXJuIHMuaXNTdGlja3koKSA/IC0xIC0gdCA6IHRcbiAgfSlcblxuICAvLyBSZWFkIGxpdmUgc2Nyb2xsIHN0YXRlIG9uIGV2ZXJ5IHJlbmRlci5cbiAgY29uc3QgaXNTdGlja3kgPSBzY3JvbGxSZWYuY3VycmVudD8uaXNTdGlja3koKSA/PyB0cnVlXG4gIGNvbnN0IHRhcmdldCA9IE1hdGgubWF4KFxuICAgIDAsXG4gICAgKHNjcm9sbFJlZi5jdXJyZW50Py5nZXRTY3JvbGxUb3AoKSA/PyAwKSArXG4gICAgICAoc2Nyb2xsUmVmLmN1cnJlbnQ/LmdldFBlbmRpbmdEZWx0YSgpID8/IDApLFxuICApXG5cbiAgLy8gV2FsayB0aGUgbW91bnRlZCByYW5nZSB0byBmaW5kIHRoZSBmaXJzdCBpdGVtIGF0LW9yLWJlbG93IHRoZSB2aWV3cG9ydFxuICAvLyB0b3AuIGByYW5nZWAgaXMgZnJvbSB0aGUgcGFyZW50J3MgY29hcnNlLXF1YW50dW0gcmVuZGVyIChtYXkgYmUgc2xpZ2h0bHlcbiAgLy8gc3RhbGUpIGJ1dCBvdmVyc2NhbiBndWFyYW50ZWVzIGl0IHNwYW5zIHdlbGwgcGFzdCB0aGUgdmlld3BvcnQgaW4gYm90aFxuICAvLyBkaXJlY3Rpb25zLiBJdGVtcyB3aXRob3V0IGEgWW9nYSBsYXlvdXQgeWV0IChuZXdseSBtb3VudGVkIHRoaXMgZnJhbWUpXG4gIC8vIGFyZSB0cmVhdGVkIGFzIGF0LW9yLWJlbG93IOKAlCB0aGV5J3JlIHNvbWV3aGVyZSBpbiB2aWV3LCBhbmQgYXNzdW1pbmdcbiAgLy8gb3RoZXJ3aXNlIHdvdWxkIHNob3cgYSBzdGlja3kgZm9yIGEgcHJvbXB0IHRoYXQncyBhY3R1YWxseSBvbiBzY3JlZW4uXG4gIGxldCBmaXJzdFZpc2libGUgPSBzdGFydFxuICBsZXQgZmlyc3RWaXNpYmxlVG9wID0gLTFcbiAgZm9yIChsZXQgaSA9IGVuZCAtIDE7IGkgPj0gc3RhcnQ7IGktLSkge1xuICAgIGNvbnN0IHRvcCA9IGdldEl0ZW1Ub3AoaSlcbiAgICBpZiAodG9wID49IDApIHtcbiAgICAgIGlmICh0b3AgPCB0YXJnZXQpIGJyZWFrXG4gICAgICBmaXJzdFZpc2libGVUb3AgPSB0b3BcbiAgICB9XG4gICAgZmlyc3RWaXNpYmxlID0gaVxuICB9XG5cbiAgbGV0IGlkeCA9IC0xXG4gIGxldCB0ZXh0OiBzdHJpbmcgfCBudWxsID0gbnVsbFxuICBpZiAoZmlyc3RWaXNpYmxlID4gMCAmJiAhaXNTdGlja3kpIHtcbiAgICBmb3IgKGxldCBpID0gZmlyc3RWaXNpYmxlIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIGNvbnN0IHQgPSBzdGlja3lQcm9tcHRUZXh0KG1lc3NhZ2VzW2ldISlcbiAgICAgIGlmICh0ID09PSBudWxsKSBjb250aW51ZVxuICAgICAgLy8gVGhlIHByb21wdCdzIHdyYXBwaW5nIEJveCB0b3AgaXMgYWJvdmUgdGFyZ2V0ICh0aGF0J3Mgd2h5IGl0J3MgaW5cbiAgICAgIC8vIHRoZSBbMCwgZmlyc3RWaXNpYmxlKSByYW5nZSksIGJ1dCBpdHMg4p2vIGlzIGF0IHRvcCsxIChtYXJnaW5Ub3A9MSkuXG4gICAgICAvLyBJZiB0aGUg4p2vIGlzIGF0LW9yLWJlbG93IHRhcmdldCwgaXQncyBWSVNJQkxFIGF0IHZpZXdwb3J0IHRvcCDigJRcbiAgICAgIC8vIHNob3dpbmcgdGhlIHNhbWUgdGV4dCBpbiB0aGUgaGVhZGVyIHdvdWxkIGR1cGxpY2F0ZSBpdC4gSGFwcGVuc1xuICAgICAgLy8gaW4gdGhlIDEtcm93IGdhcCBiZXR3ZWVuIEJveCB0b3Agc2Nyb2xsaW5nIHBhc3QgYW5kIOKdryBzY3JvbGxpbmdcbiAgICAgIC8vIHBhc3QuIFNraXAgdG8gdGhlIG5leHQtb2xkZXIgcHJvbXB0IChpdHMg4p2vIGlzIGRlZmluaXRlbHkgYWJvdmUpLlxuICAgICAgY29uc3QgdG9wID0gZ2V0SXRlbVRvcChpKVxuICAgICAgaWYgKHRvcCA+PSAwICYmIHRvcCArIDEgPj0gdGFyZ2V0KSBjb250aW51ZVxuICAgICAgaWR4ID0gaVxuICAgICAgdGV4dCA9IHRcbiAgICAgIGJyZWFrXG4gICAgfVxuICB9XG5cbiAgY29uc3QgYmFzZU9mZnNldCA9XG4gICAgZmlyc3RWaXNpYmxlVG9wID49IDAgPyBmaXJzdFZpc2libGVUb3AgLSBvZmZzZXRzW2ZpcnN0VmlzaWJsZV0hIDogMFxuICBjb25zdCBlc3RpbWF0ZSA9IGlkeCA+PSAwID8gTWF0aC5tYXgoMCwgYmFzZU9mZnNldCArIG9mZnNldHNbaWR4XSEpIDogLTFcblxuICAvLyBGb3IgY2xpY2stanVtcHMgdG8gaXRlbXMgbm90IHlldCBtb3VudGVkICh1c2VyIHNjcm9sbGVkIGZhciBwYXN0LFxuICAvLyBwcm9tcHQgaXMgaW4gdGhlIHRvcFNwYWNlcikuIENsaWNrIGhhbmRsZXIgc2Nyb2xscyB0byB0aGUgZXN0aW1hdGVcbiAgLy8gdG8gbW91bnQgaXQ7IHRoaXMgYW5jaG9ycyBieSBlbGVtZW50IG9uY2UgaXQgYXBwZWFycy4gc2Nyb2xsVG9FbGVtZW50XG4gIC8vIGRlZmVycyB0aGUgWW9nYS1wb3NpdGlvbiByZWFkIHRvIHJlbmRlciB0aW1lIChyZW5kZXItbm9kZS10by1vdXRwdXRcbiAgLy8gcmVhZHMgZWwueW9nYU5vZGUuZ2V0Q29tcHV0ZWRUb3AoKSBpbiB0aGUgU0FNRSBjYWxjdWxhdGVMYXlvdXQgcGFzc1xuICAvLyB0aGF0IHByb2R1Y2VzIHNjcm9sbEhlaWdodCkg4oCUIG5vIHRocm90dGxlIHJhY2UuIENhcCByZXRyaWVzOiBhIC9jbGVhclxuICAvLyByYWNlIGNvdWxkIHVubW91bnQgdGhlIGl0ZW0gbWlkLXNlcXVlbmNlLlxuICBjb25zdCBwZW5kaW5nID0gdXNlUmVmKHsgaWR4OiAtMSwgdHJpZXM6IDAgfSlcbiAgLy8gU3VwcHJlc3Npb24gc3RhdGUgbWFjaGluZS4gVGhlIGNsaWNrIGhhbmRsZXIgYXJtczsgdGhlIG9uQ2hhbmdlIGVmZmVjdFxuICAvLyBjb25zdW1lcyAoYXJtZWTihpJmb3JjZSkgdGhlbiBmaXJlcy1hbmQtY2xlYXJzIG9uIHRoZSByZW5kZXIgQUZURVIgdGhhdFxuICAvLyAoZm9yY2XihpJub25lKS4gVGhlIGZvcmNlIHN0ZXAgcG9pc29ucyB0aGUgZGVkdXA6IGFmdGVyIGNsaWNrLCBpZHggb2Z0ZW5cbiAgLy8gcmVjb21wdXRlcyB0byB0aGUgU0FNRSBwcm9tcHQgKGl0cyB0b3AgaXMgc3RpbGwgYWJvdmUgdGFyZ2V0KSwgc29cbiAgLy8gd2l0aG91dCBmb3JjZSB0aGUgbGFzdC5pZHg9PT1pZHggZ3VhcmQgd291bGQgaG9sZCAnY2xpY2tlZCcgdW50aWwgdGhlXG4gIC8vIHVzZXIgY3Jvc3NlZCBhIHByb21wdCBib3VuZGFyeS4gUHJldmlvdXNseSBlbmNvZGVkIGluIGxhc3QuaWR4IGFzXG4gIC8vIC0xLy0yLy0zIHdoaWNoIG92ZXJsYXBwZWQgd2l0aCByZWFsIGluZGljZXMg4oCUIHRvbyBjbGV2ZXIuXG4gIHR5cGUgU3VwcHJlc3MgPSAnbm9uZScgfCAnYXJtZWQnIHwgJ2ZvcmNlJ1xuICBjb25zdCBzdXBwcmVzcyA9IHVzZVJlZjxTdXBwcmVzcz4oJ25vbmUnKVxuICAvLyBEZWR1cCBvbiBpZHggb25seSDigJQgZXN0aW1hdGUgZGVyaXZlcyBmcm9tIGZpcnN0VmlzaWJsZVRvcCB3aGljaCBzaGlmdHNcbiAgLy8gZXZlcnkgc2Nyb2xsIHRpY2ssIHNvIGluY2x1ZGluZyBpdCBpbiB0aGUga2V5IG1hZGUgdGhlIGd1YXJkIGRlYWRcbiAgLy8gKHNldFN0aWNreVByb21wdCBmaXJlZCBhIGZyZXNoIHt0ZXh0LHNjcm9sbFRvfSBwZXItZnJhbWUpLiBUaGUgc2Nyb2xsVG9cbiAgLy8gY2xvc3VyZSBzdGlsbCBjYXB0dXJlcyB0aGUgY3VycmVudCBlc3RpbWF0ZTsgaXQganVzdCBkb2Vzbid0IG5lZWQgdG9cbiAgLy8gcmUtZmlyZSB3aGVuIG9ubHkgZXN0aW1hdGUgbW92ZWQuXG4gIGNvbnN0IGxhc3RJZHggPSB1c2VSZWYoLTEpXG5cbiAgLy8gc2V0U3RpY2t5UHJvbXB0IGVmZmVjdCBGSVJTVCDigJQgbXVzdCBzZWUgcGVuZGluZy5pZHggYmVmb3JlIHRoZVxuICAvLyBjb3JyZWN0aW9uIGVmZmVjdCBiZWxvdyBjbGVhcnMgaXQuIE9uIHRoZSBlc3RpbWF0ZS1mYWxsYmFjayBwYXRoLCB0aGVcbiAgLy8gcmVuZGVyIHRoYXQgbW91bnRzIHRoZSBpdGVtIGlzIEFMU08gdGhlIHJlbmRlciB3aGVyZSBjb3JyZWN0aW9uIGNsZWFyc1xuICAvLyBwZW5kaW5nOyBpZiB0aGlzIHJhbiBzZWNvbmQsIHRoZSBwZW5kaW5nIGdhdGUgd291bGQgYmUgZGVhZCBhbmRcbiAgLy8gc2V0U3RpY2t5UHJvbXB0KHByZXZQcm9tcHQpIHdvdWxkIGZpcmUgbWlkLWp1bXAsIHJlLW1vdW50aW5nIHRoZVxuICAvLyBoZWFkZXIgb3ZlciAnY2xpY2tlZCcuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgLy8gSG9sZCB3aGlsZSB0d28tcGhhc2UgY29ycmVjdGlvbiBpcyBpbiBmbGlnaHQuXG4gICAgaWYgKHBlbmRpbmcuY3VycmVudC5pZHggPj0gMCkgcmV0dXJuXG4gICAgaWYgKHN1cHByZXNzLmN1cnJlbnQgPT09ICdhcm1lZCcpIHtcbiAgICAgIHN1cHByZXNzLmN1cnJlbnQgPSAnZm9yY2UnXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgY29uc3QgZm9yY2UgPSBzdXBwcmVzcy5jdXJyZW50ID09PSAnZm9yY2UnXG4gICAgc3VwcHJlc3MuY3VycmVudCA9ICdub25lJ1xuICAgIGlmICghZm9yY2UgJiYgbGFzdElkeC5jdXJyZW50ID09PSBpZHgpIHJldHVyblxuICAgIGxhc3RJZHguY3VycmVudCA9IGlkeFxuICAgIGlmICh0ZXh0ID09PSBudWxsKSB7XG4gICAgICBzZXRTdGlja3lQcm9tcHQobnVsbClcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICAvLyBGaXJzdCBwYXJhZ3JhcGggb25seSAoc3BsaXQgb24gYmxhbmsgbGluZSkg4oCUIGEgcHJvbXB0IGxpa2VcbiAgICAvLyBcInN0aWxsIHNlZWluZyBidWdzOlxcblxcbjEuIGZvb1xcbjIuIGJhclwiIHByZXZpZXdzIGFzIGp1c3QgdGhlXG4gICAgLy8gbGVhZC1pbi4gdHJpbVN0YXJ0IHNvIGEgbGVhZGluZyBibGFuayBsaW5lIChxdWV1ZWRfY29tbWFuZCBtaWQtXG4gICAgLy8gdHVybiBtZXNzYWdlcyBzb21ldGltZXMgaGF2ZSBvbmUpIGRvZXNuJ3QgZmluZCBwYXJhRW5kIGF0IDAuXG4gICAgY29uc3QgdHJpbW1lZCA9IHRleHQudHJpbVN0YXJ0KClcbiAgICBjb25zdCBwYXJhRW5kID0gdHJpbW1lZC5zZWFyY2goL1xcblxccypcXG4vKVxuICAgIGNvbnN0IGNvbGxhcHNlZCA9IChwYXJhRW5kID49IDAgPyB0cmltbWVkLnNsaWNlKDAsIHBhcmFFbmQpIDogdHJpbW1lZClcbiAgICAgIC5zbGljZSgwLCBTVElDS1lfVEVYVF9DQVApXG4gICAgICAucmVwbGFjZSgvXFxzKy9nLCAnICcpXG4gICAgICAudHJpbSgpXG4gICAgaWYgKGNvbGxhcHNlZCA9PT0gJycpIHtcbiAgICAgIHNldFN0aWNreVByb21wdChudWxsKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGNvbnN0IGNhcHR1cmVkSWR4ID0gaWR4XG4gICAgY29uc3QgY2FwdHVyZWRFc3RpbWF0ZSA9IGVzdGltYXRlXG4gICAgc2V0U3RpY2t5UHJvbXB0KHtcbiAgICAgIHRleHQ6IGNvbGxhcHNlZCxcbiAgICAgIHNjcm9sbFRvOiAoKSA9PiB7XG4gICAgICAgIC8vIEhpZGUgaGVhZGVyLCBrZWVwIHBhZGRpbmcgY29sbGFwc2VkIOKAlCBGdWxsc2NyZWVuTGF5b3V0J3NcbiAgICAgICAgLy8gJ2NsaWNrZWQnIHNlbnRpbmVsIOKGkiBzY3JvbGxCb3hfeT0wICsgcGFkPTAg4oaSIHZpZXdwb3J0VG9wPTAuXG4gICAgICAgIHNldFN0aWNreVByb21wdCgnY2xpY2tlZCcpXG4gICAgICAgIHN1cHByZXNzLmN1cnJlbnQgPSAnYXJtZWQnXG4gICAgICAgIC8vIHNjcm9sbFRvRWxlbWVudCBhbmNob3JzIGJ5IERPTUVsZW1lbnQgcmVmLCBub3QgYSBudW1iZXI6XG4gICAgICAgIC8vIHJlbmRlci1ub2RlLXRvLW91dHB1dCByZWFkcyBlbC55b2dhTm9kZS5nZXRDb21wdXRlZFRvcCgpIGF0XG4gICAgICAgIC8vIHBhaW50IHRpbWUgKHNhbWUgWW9nYSBwYXNzIGFzIHNjcm9sbEhlaWdodCkuIE5vIHN0YWxlbmVzcyBmcm9tXG4gICAgICAgIC8vIHRoZSB0aHJvdHRsZWQgcmVuZGVyIOKAlCB0aGUgcmVmIGlzIHN0YWJsZSwgdGhlIHBvc2l0aW9uIHJlYWQgaXNcbiAgICAgICAgLy8gZGVmZXJyZWQuIG9mZnNldD0xID0gVXNlclByb21wdE1lc3NhZ2UgbWFyZ2luVG9wLlxuICAgICAgICBjb25zdCBlbCA9IGdldEl0ZW1FbGVtZW50KGNhcHR1cmVkSWR4KVxuICAgICAgICBpZiAoZWwpIHtcbiAgICAgICAgICBzY3JvbGxSZWYuY3VycmVudD8uc2Nyb2xsVG9FbGVtZW50KGVsLCAxKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIE5vdCBtb3VudGVkIChzY3JvbGxlZCBmYXIgcGFzdCDigJQgaW4gdG9wU3BhY2VyKS4gSnVtcCB0b1xuICAgICAgICAgIC8vIGVzdGltYXRlIHRvIG1vdW50IGl0OyBjb3JyZWN0aW9uIGVmZmVjdCByZS1hbmNob3JzIG9uY2UgaXRcbiAgICAgICAgICAvLyBhcHBlYXJzLiBFc3RpbWF0ZSBpcyBERUZBVUxUX0VTVElNQVRFLWJhc2VkIOKAlCBsYW5kcyBzaG9ydC5cbiAgICAgICAgICBzY3JvbGxSZWYuY3VycmVudD8uc2Nyb2xsVG8oY2FwdHVyZWRFc3RpbWF0ZSlcbiAgICAgICAgICBwZW5kaW5nLmN1cnJlbnQgPSB7IGlkeDogY2FwdHVyZWRJZHgsIHRyaWVzOiAwIH1cbiAgICAgICAgfVxuICAgICAgfSxcbiAgICB9KVxuICAgIC8vIE5vIGRlcHMg4oCUIG11c3QgcnVuIGV2ZXJ5IHJlbmRlci4gU3VwcHJlc3Npb24gc3RhdGUgbGl2ZXMgaW4gYSByZWZcbiAgICAvLyAobm90IGlkeC9lc3RpbWF0ZSksIHNvIGEgZGVwcy1nYXRlZCBlZmZlY3Qgd291bGQgbmV2ZXIgc2VlIGl0IHRpY2suXG4gICAgLy8gQm9keSdzIG93biBndWFyZHMgc2hvcnQtY2lyY3VpdCB3aGVuIG5vdGhpbmcgY2hhbmdlZC5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcmVhY3QtaG9va3MvZXhoYXVzdGl2ZS1kZXBzXG4gIH0pXG5cbiAgLy8gQ29ycmVjdGlvbjogZm9yIGNsaWNrLWp1bXBzIHRvIHVubW91bnRlZCBpdGVtcy4gQ2xpY2sgaGFuZGxlciBzY3JvbGxlZFxuICAvLyB0byB0aGUgZXN0aW1hdGU7IHRoaXMgcmUtYW5jaG9ycyBieSBlbGVtZW50IG9uY2UgdGhlIGl0ZW0gYXBwZWFycy5cbiAgLy8gc2Nyb2xsVG9FbGVtZW50IGRlZmVycyB0aGUgWW9nYSByZWFkIHRvIHBhaW50IHRpbWUg4oCUIGRldGVybWluaXN0aWMuXG4gIC8vIFNFQ09ORCBzbyBpdCBjbGVhcnMgcGVuZGluZyBBRlRFUiB0aGUgb25DaGFuZ2UgZ2F0ZSBhYm92ZSBoYXMgc2VlbiBpdC5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAocGVuZGluZy5jdXJyZW50LmlkeCA8IDApIHJldHVyblxuICAgIGNvbnN0IGVsID0gZ2V0SXRlbUVsZW1lbnQocGVuZGluZy5jdXJyZW50LmlkeClcbiAgICBpZiAoZWwpIHtcbiAgICAgIHNjcm9sbFJlZi5jdXJyZW50Py5zY3JvbGxUb0VsZW1lbnQoZWwsIDEpXG4gICAgICBwZW5kaW5nLmN1cnJlbnQgPSB7IGlkeDogLTEsIHRyaWVzOiAwIH1cbiAgICB9IGVsc2UgaWYgKCsrcGVuZGluZy5jdXJyZW50LnRyaWVzID4gNSkge1xuICAgICAgcGVuZGluZy5jdXJyZW50ID0geyBpZHg6IC0xLCB0cmllczogMCB9XG4gICAgfVxuICB9KVxuXG4gIHJldHVybiBudWxsXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxjQUFjQSxTQUFTLFFBQVEsT0FBTztBQUN0QyxPQUFPLEtBQUtDLEtB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 \ No newline at end of file diff --git a/components/WorkflowMultiselectDialog.tsx b/components/WorkflowMultiselectDialog.tsx new file mode 100644 index 0000000..283a10e --- /dev/null +++ b/components/WorkflowMultiselectDialog.tsx @@ -0,0 +1,128 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useState } from 'react'; +import type { Workflow } from '../commands/install-github-app/types.js'; +import type { ExitState } from '../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { Box, Link, Text } from '../ink.js'; +import { ConfigurableShortcutHint } from './ConfigurableShortcutHint.js'; +import { SelectMulti } from './CustomSelect/SelectMulti.js'; +import { Byline } from './design-system/Byline.js'; +import { Dialog } from './design-system/Dialog.js'; +import { KeyboardShortcutHint } from './design-system/KeyboardShortcutHint.js'; +type WorkflowOption = { + value: Workflow; + label: string; +}; +type Props = { + onSubmit: (selectedWorkflows: Workflow[]) => void; + defaultSelections: Workflow[]; +}; +const WORKFLOWS: WorkflowOption[] = [{ + value: 'claude' as const, + label: '@Claude Code - Tag @claude in issues and PR comments' +}, { + value: 'claude-review' as const, + label: 'Claude Code Review - Automated code review on new PRs' +}]; +function renderInputGuide(exitState: ExitState): React.ReactNode { + if (exitState.pending) { + return Press {exitState.keyName} again to exit; + } + return + + + + + ; +} +export function WorkflowMultiselectDialog(t0) { + const $ = _c(14); + const { + onSubmit, + defaultSelections + } = t0; + const [showError, setShowError] = useState(false); + let t1; + if ($[0] !== onSubmit) { + t1 = selectedValues => { + if (selectedValues.length === 0) { + setShowError(true); + return; + } + setShowError(false); + onSubmit(selectedValues); + }; + $[0] = onSubmit; + $[1] = t1; + } else { + t1 = $[1]; + } + const handleSubmit = t1; + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => { + setShowError(false); + }; + $[2] = t2; + } else { + t2 = $[2]; + } + const handleChange = t2; + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = () => { + setShowError(true); + }; + $[3] = t3; + } else { + t3 = $[3]; + } + const handleCancel = t3; + let t4; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t4 = More workflow examples (issue triage, CI fixes, etc.) at:{" "}https://github.com/anthropics/claude-code-action/blob/main/examples/; + $[4] = t4; + } else { + t4 = $[4]; + } + let t5; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t5 = WORKFLOWS.map(_temp); + $[5] = t5; + } else { + t5 = $[5]; + } + let t6; + if ($[6] !== defaultSelections || $[7] !== handleSubmit) { + t6 = ; + $[6] = defaultSelections; + $[7] = handleSubmit; + $[8] = t6; + } else { + t6 = $[8]; + } + let t7; + if ($[9] !== showError) { + t7 = showError && You must select at least one workflow to continue; + $[9] = showError; + $[10] = t7; + } else { + t7 = $[10]; + } + let t8; + if ($[11] !== t6 || $[12] !== t7) { + t8 = {t4}{t6}{t7}; + $[11] = t6; + $[12] = t7; + $[13] = t8; + } else { + t8 = $[13]; + } + return t8; +} +function _temp(workflow) { + return { + label: workflow.label, + value: workflow.value + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlU3RhdGUiLCJXb3JrZmxvdyIsIkV4aXRTdGF0ZSIsIkJveCIsIkxpbmsiLCJUZXh0IiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiU2VsZWN0TXVsdGkiLCJCeWxpbmUiLCJEaWFsb2ciLCJLZXlib2FyZFNob3J0Y3V0SGludCIsIldvcmtmbG93T3B0aW9uIiwidmFsdWUiLCJsYWJlbCIsIlByb3BzIiwib25TdWJtaXQiLCJzZWxlY3RlZFdvcmtmbG93cyIsImRlZmF1bHRTZWxlY3Rpb25zIiwiV09SS0ZMT1dTIiwiY29uc3QiLCJyZW5kZXJJbnB1dEd1aWRlIiwiZXhpdFN0YXRlIiwiUmVhY3ROb2RlIiwicGVuZGluZyIsImtleU5hbWUiLCJXb3JrZmxvd011bHRpc2VsZWN0RGlhbG9nIiwidDAiLCIkIiwiX2MiLCJzaG93RXJyb3IiLCJzZXRTaG93RXJyb3IiLCJ0MSIsInNlbGVjdGVkVmFsdWVzIiwibGVuZ3RoIiwiaGFuZGxlU3VibWl0IiwidDIiLCJTeW1ib2wiLCJmb3IiLCJoYW5kbGVDaGFuZ2UiLCJ0MyIsImhhbmRsZUNhbmNlbCIsInQ0IiwidDUiLCJtYXAiLCJfdGVtcCIsInQ2IiwidDciLCJ0OCIsIndvcmtmbG93Il0sInNvdXJjZXMiOlsiV29ya2Zsb3dNdWx0aXNlbGVjdERpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHVzZUNhbGxiYWNrLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBXb3JrZmxvdyB9IGZyb20gJy4uL2NvbW1hbmRzL2luc3RhbGwtZ2l0aHViLWFwcC90eXBlcy5qcydcbmltcG9ydCB0eXBlIHsgRXhpdFN0YXRlIH0gZnJvbSAnLi4vaG9va3MvdXNlRXhpdE9uQ3RybENEV2l0aEtleWJpbmRpbmdzLmpzJ1xuaW1wb3J0IHsgQm94LCBMaW5rLCBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IH0gZnJvbSAnLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBTZWxlY3RNdWx0aSB9IGZyb20gJy4vQ3VzdG9tU2VsZWN0L1NlbGVjdE11bHRpLmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4vZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcblxudHlwZSBXb3JrZmxvd09wdGlvbiA9IHtcbiAgdmFsdWU6IFdvcmtmbG93XG4gIGxhYmVsOiBzdHJpbmdcbn1cblxudHlwZSBQcm9wcyA9IHtcbiAgb25TdWJtaXQ6IChzZWxlY3RlZFdvcmtmbG93czogV29ya2Zsb3dbXSkgPT4gdm9pZFxuICBkZWZhdWx0U2VsZWN0aW9uczogV29ya2Zsb3dbXVxufVxuXG5jb25zdCBXT1JLRkxPV1M6IFdvcmtmbG93T3B0aW9uW10gPSBbXG4gIHtcbiAgICB2YWx1ZTogJ2NsYXVkZScgYXMgY29uc3QsXG4gICAgbGFiZWw6ICdAQ2xhdWRlIENvZGUgLSBUYWcgQGNsYXVkZSBpbiBpc3N1ZXMgYW5kIFBSIGNvbW1lbnRzJyxcbiAgfSxcbiAge1xuICAgIHZhbHVlOiAnY2xhdWRlLXJldmlldycgYXMgY29uc3QsXG4gICAgbGFiZWw6ICdDbGF1ZGUgQ29kZSBSZXZpZXcgLSBBdXRvbWF0ZWQgY29kZSByZXZpZXcgb24gbmV3IFBScycsXG4gIH0sXG5dXG5cbmZ1bmN0aW9uIHJlbmRlcklucHV0R3VpZGUoZXhpdFN0YXRlOiBFeGl0U3RhdGUpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoZXhpdFN0YXRlLnBlbmRpbmcpIHtcbiAgICByZXR1cm4gPFRleHQ+UHJlc3Mge2V4aXRTdGF0ZS5rZXlOYW1lfSBhZ2FpbiB0byBleGl0PC9UZXh0PlxuICB9XG4gIHJldHVybiAoXG4gICAgPEJ5bGluZT5cbiAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIuKGkeKGk1wiIGFjdGlvbj1cIm5hdmlnYXRlXCIgLz5cbiAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIlNwYWNlXCIgYWN0aW9uPVwidG9nZ2xlXCIgLz5cbiAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVudGVyXCIgYWN0aW9uPVwiY29uZmlybVwiIC8+XG4gICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICBkZXNjcmlwdGlvbj1cImNhbmNlbFwiXG4gICAgICAvPlxuICAgIDwvQnlsaW5lPlxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBXb3JrZmxvd011bHRpc2VsZWN0RGlhbG9nKHtcbiAgb25TdWJtaXQsXG4gIGRlZmF1bHRTZWxlY3Rpb25zLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbc2hvd0Vycm9yLCBzZXRTaG93RXJyb3JdID0gdXNlU3RhdGUoZmFsc2UpXG5cbiAgY29uc3QgaGFuZGxlU3VibWl0ID0gdXNlQ2FsbGJhY2soXG4gICAgKHNlbGVjdGVkVmFsdWVzOiBXb3JrZmxvd1tdKSA9PiB7XG4gICAgICBpZiAoc2VsZWN0ZWRWYWx1ZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHNldFNob3dFcnJvcih0cnVlKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIHNldFNob3dFcnJvcihmYWxzZSlcbiAgICAgIG9uU3VibWl0KHNlbGVjdGVkVmFsdWVzKVxuICAgIH0sXG4gICAgW29uU3VibWl0XSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZUNoYW5nZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTaG93RXJyb3IoZmFsc2UpXG4gIH0sIFtdKVxuXG4gIC8vIENhbmNlbCBqdXN0IHNob3dzIHRoZSBlcnJvciAtIHVzZXIgbXVzdCBzZWxlY3QgYXQgbGVhc3Qgb25lIHdvcmtmbG93XG4gIGNvbnN0IGhhbmRsZUNhbmNlbCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTaG93RXJyb3IodHJ1ZSlcbiAgfSwgW10pXG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT1cIlNlbGVjdCBHaXRIdWIgd29ya2Zsb3dzIHRvIGluc3RhbGxcIlxuICAgICAgc3VidGl0bGU9XCJXZSdsbCBjcmVhdGUgYSB3b3JrZmxvdyBmaWxlIGluIHlvdXIgcmVwb3NpdG9yeSBmb3IgZWFjaCBvbmUgeW91IHNlbGVjdC5cIlxuICAgICAgb25DYW5jZWw9e2hhbmRsZUNhbmNlbH1cbiAgICAgIGlucHV0R3VpZGU9e3JlbmRlcklucHV0R3VpZGV9XG4gICAgPlxuICAgICAgPEJveD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgTW9yZSB3b3JrZmxvdyBleGFtcGxlcyAoaXNzdWUgdHJpYWdlLCBDSSBmaXhlcywgZXRjLikgYXQ6eycgJ31cbiAgICAgICAgICA8TGluayB1cmw9XCJodHRwczovL2dpdGh1Yi5jb20vYW50aHJvcGljcy9jbGF1ZGUtY29kZS1hY3Rpb24vYmxvYi9tYWluL2V4YW1wbGVzL1wiPlxuICAgICAgICAgICAgaHR0cHM6Ly9naXRodWIuY29tL2FudGhyb3BpY3MvY2xhdWRlLWNvZGUtYWN0aW9uL2Jsb2IvbWFpbi9leGFtcGxlcy9cbiAgICAgICAgICA8L0xpbms+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuXG4gICAgICA8U2VsZWN0TXVsdGlcbiAgICAgICAgb3B0aW9ucz17V09SS0ZMT1dTLm1hcCh3b3JrZmxvdyA9PiAoe1xuICAgICAgICAgIGxhYmVsOiB3b3JrZmxvdy5sYWJlbCxcbiAgICAgICAgICB2YWx1ZTogd29ya2Zsb3cudmFsdWUsXG4gICAgICAgIH0pKX1cbiAgICAgICAgZGVmYXVsdFZhbHVlPXtkZWZhdWx0U2VsZWN0aW9uc31cbiAgICAgICAgb25TdWJtaXQ9e2hhbmRsZVN1Ym1pdH1cbiAgICAgICAgb25DaGFuZ2U9e2hhbmRsZUNoYW5nZX1cbiAgICAgICAgb25DYW5jZWw9e2hhbmRsZUNhbmNlbH1cbiAgICAgICAgaGlkZUluZGV4ZXNcbiAgICAgIC8+XG5cbiAgICAgIHtzaG93RXJyb3IgJiYgKFxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5cbiAgICAgICAgICAgIFlvdSBtdXN0IHNlbGVjdCBhdCBsZWFzdCBvbmUgd29ya2Zsb3cgdG8gY29udGludWVcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJQyxXQUFXLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3BELGNBQWNDLFFBQVEsUUFBUSx5Q0FBeUM7QUFDdkUsY0FBY0MsU0FBUyxRQUFRLDRDQUE0QztBQUMzRSxTQUFTQyxHQUFHLEVBQUVDLElBQUksRUFBRUMsSUFBSSxRQUFRLFdBQVc7QUFDM0MsU0FBU0Msd0JBQXdCLFFBQVEsK0JBQStCO0FBQ3hFLFNBQVNDLFdBQVcsUUFBUSwrQkFBK0I7QUFDM0QsU0FBU0MsTUFBTSxRQUFRLDJCQUEyQjtBQUNsRCxTQUFTQyxNQUFNLFFBQVEsMkJBQTJCO0FBQ2xELFNBQVNDLG9CQUFvQixRQUFRLHlDQUF5QztBQUU5RSxLQUFLQyxjQUFjLEdBQUc7RUFDcEJDLEtBQUssRUFBRVgsUUFBUTtFQUNmWSxLQUFLLEVBQUUsTUFBTTtBQUNmLENBQUM7QUFFRCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsUUFBUSxFQUFFLENBQUNDLGlCQUFpQixFQUFFZixRQUFRLEVBQUUsRUFBRSxHQUFHLElBQUk7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 \ No newline at end of file diff --git a/components/WorktreeExitDialog.tsx b/components/WorktreeExitDialog.tsx new file mode 100644 index 0000000..c51c939 --- /dev/null +++ b/components/WorktreeExitDialog.tsx @@ -0,0 +1,231 @@ +import React, { useEffect, useState } from 'react'; +import type { CommandResultDisplay } from 'src/commands.js'; +import { logEvent } from 'src/services/analytics/index.js'; +import { logForDebugging } from 'src/utils/debug.js'; +import { Box, Text } from '../ink.js'; +import { execFileNoThrow } from '../utils/execFileNoThrow.js'; +import { getPlansDirectory } from '../utils/plans.js'; +import { setCwd } from '../utils/Shell.js'; +import { cleanupWorktree, getCurrentWorktreeSession, keepWorktree, killTmuxSession } from '../utils/worktree.js'; +import { Select } from './CustomSelect/select.js'; +import { Dialog } from './design-system/Dialog.js'; +import { Spinner } from './Spinner.js'; + +// Inline require breaks the cycle this file would otherwise close: +// sessionStorage → commands → exit → ExitFlow → here. All call sites +// are inside callbacks, so the lazy require never sees an undefined import. +function recordWorktreeExit(): void { + /* eslint-disable @typescript-eslint/no-require-imports */ + ; + (require('../utils/sessionStorage.js') as typeof import('../utils/sessionStorage.js')).saveWorktreeState(null); + /* eslint-enable @typescript-eslint/no-require-imports */ +} +type Props = { + onDone: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; + onCancel?: () => void; +}; +export function WorktreeExitDialog({ + onDone, + onCancel +}: Props): React.ReactNode { + const [status, setStatus] = useState<'loading' | 'asking' | 'keeping' | 'removing' | 'done'>('loading'); + const [changes, setChanges] = useState([]); + const [commitCount, setCommitCount] = useState(0); + const [resultMessage, setResultMessage] = useState(); + const worktreeSession = getCurrentWorktreeSession(); + useEffect(() => { + async function loadChanges() { + let changeLines: string[] = []; + const gitStatus = await execFileNoThrow('git', ['status', '--porcelain']); + if (gitStatus.stdout) { + changeLines = gitStatus.stdout.split('\n').filter(_ => _.trim() !== ''); + setChanges(changeLines); + } + + // Check for commits to eject + if (worktreeSession) { + // Get commits in worktree that are not in original branch + const { + stdout: commitsStr + } = await execFileNoThrow('git', ['rev-list', '--count', `${worktreeSession.originalHeadCommit}..HEAD`]); + const count = parseInt(commitsStr.trim()) || 0; + setCommitCount(count); + + // If no changes and no commits, clean up silently + if (changeLines.length === 0 && count === 0) { + setStatus('removing'); + void cleanupWorktree().then(() => { + process.chdir(worktreeSession.originalCwd); + setCwd(worktreeSession.originalCwd); + recordWorktreeExit(); + getPlansDirectory.cache.clear?.(); + setResultMessage('Worktree removed (no changes)'); + }).catch(error => { + logForDebugging(`Failed to clean up worktree: ${error}`, { + level: 'error' + }); + setResultMessage('Worktree cleanup failed, exiting anyway'); + }).then(() => { + setStatus('done'); + }); + return; + } else { + setStatus('asking'); + } + } + } + void loadChanges(); + // eslint-disable-next-line react-hooks/exhaustive-deps + // biome-ignore lint/correctness/useExhaustiveDependencies: intentional + }, [worktreeSession]); + useEffect(() => { + if (status === 'done') { + onDone(resultMessage); + } + }, [status, onDone, resultMessage]); + if (!worktreeSession) { + onDone('No active worktree session found', { + display: 'system' + }); + return null; + } + if (status === 'loading' || status === 'done') { + return null; + } + async function handleSelect(value: string) { + if (!worktreeSession) return; + const hasTmux = Boolean(worktreeSession.tmuxSessionName); + if (value === 'keep' || value === 'keep-with-tmux') { + setStatus('keeping'); + logEvent('tengu_worktree_kept', { + commits: commitCount, + changed_files: changes.length + }); + await keepWorktree(); + process.chdir(worktreeSession.originalCwd); + setCwd(worktreeSession.originalCwd); + recordWorktreeExit(); + getPlansDirectory.cache.clear?.(); + if (hasTmux) { + setResultMessage(`Worktree kept. Your work is saved at ${worktreeSession.worktreePath} on branch ${worktreeSession.worktreeBranch}. Reattach to tmux session with: tmux attach -t ${worktreeSession.tmuxSessionName}`); + } else { + setResultMessage(`Worktree kept. Your work is saved at ${worktreeSession.worktreePath} on branch ${worktreeSession.worktreeBranch}`); + } + setStatus('done'); + } else if (value === 'keep-kill-tmux') { + setStatus('keeping'); + logEvent('tengu_worktree_kept', { + commits: commitCount, + changed_files: changes.length + }); + if (worktreeSession.tmuxSessionName) { + await killTmuxSession(worktreeSession.tmuxSessionName); + } + await keepWorktree(); + process.chdir(worktreeSession.originalCwd); + setCwd(worktreeSession.originalCwd); + recordWorktreeExit(); + getPlansDirectory.cache.clear?.(); + setResultMessage(`Worktree kept at ${worktreeSession.worktreePath} on branch ${worktreeSession.worktreeBranch}. Tmux session terminated.`); + setStatus('done'); + } else if (value === 'remove' || value === 'remove-with-tmux') { + setStatus('removing'); + logEvent('tengu_worktree_removed', { + commits: commitCount, + changed_files: changes.length + }); + if (worktreeSession.tmuxSessionName) { + await killTmuxSession(worktreeSession.tmuxSessionName); + } + try { + await cleanupWorktree(); + process.chdir(worktreeSession.originalCwd); + setCwd(worktreeSession.originalCwd); + recordWorktreeExit(); + getPlansDirectory.cache.clear?.(); + } catch (error) { + logForDebugging(`Failed to clean up worktree: ${error}`, { + level: 'error' + }); + setResultMessage('Worktree cleanup failed, exiting anyway'); + setStatus('done'); + return; + } + const tmuxNote = hasTmux ? ' Tmux session terminated.' : ''; + if (commitCount > 0 && changes.length > 0) { + setResultMessage(`Worktree removed. ${commitCount} ${commitCount === 1 ? 'commit' : 'commits'} and uncommitted changes were discarded.${tmuxNote}`); + } else if (commitCount > 0) { + setResultMessage(`Worktree removed. ${commitCount} ${commitCount === 1 ? 'commit' : 'commits'} on ${worktreeSession.worktreeBranch} ${commitCount === 1 ? 'was' : 'were'} discarded.${tmuxNote}`); + } else if (changes.length > 0) { + setResultMessage(`Worktree removed. Uncommitted changes were discarded.${tmuxNote}`); + } else { + setResultMessage(`Worktree removed.${tmuxNote}`); + } + setStatus('done'); + } + } + if (status === 'keeping') { + return + + Keeping worktree… + ; + } + if (status === 'removing') { + return + + Removing worktree… + ; + } + const branchName = worktreeSession.worktreeBranch; + const hasUncommitted = changes.length > 0; + const hasCommits = commitCount > 0; + let subtitle = ''; + if (hasUncommitted && hasCommits) { + subtitle = `You have ${changes.length} uncommitted ${changes.length === 1 ? 'file' : 'files'} and ${commitCount} ${commitCount === 1 ? 'commit' : 'commits'} on ${branchName}. All will be lost if you remove.`; + } else if (hasUncommitted) { + subtitle = `You have ${changes.length} uncommitted ${changes.length === 1 ? 'file' : 'files'}. These will be lost if you remove the worktree.`; + } else if (hasCommits) { + subtitle = `You have ${commitCount} ${commitCount === 1 ? 'commit' : 'commits'} on ${branchName}. The branch will be deleted if you remove the worktree.`; + } else { + subtitle = 'You are working in a worktree. Keep it to continue working there, or remove it to clean up.'; + } + function handleCancel() { + if (onCancel) { + // Abort exit and return to the session + onCancel(); + return; + } + // Fallback: treat Escape as "keep" if no onCancel provided + void handleSelect('keep'); + } + const removeDescription = hasUncommitted || hasCommits ? 'All changes and commits will be lost.' : 'Clean up the worktree directory.'; + const hasTmuxSession = Boolean(worktreeSession.tmuxSessionName); + const options = hasTmuxSession ? [{ + label: 'Keep worktree and tmux session', + value: 'keep-with-tmux', + description: `Stays at ${worktreeSession.worktreePath}. Reattach with: tmux attach -t ${worktreeSession.tmuxSessionName}` + }, { + label: 'Keep worktree, kill tmux session', + value: 'keep-kill-tmux', + description: `Keeps worktree at ${worktreeSession.worktreePath}, terminates tmux session.` + }, { + label: 'Remove worktree and tmux session', + value: 'remove-with-tmux', + description: removeDescription + }] : [{ + label: 'Keep worktree', + value: 'keep', + description: `Stays at ${worktreeSession.worktreePath}` + }, { + label: 'Remove worktree', + value: 'remove', + description: removeDescription + }]; + const defaultValue = hasTmuxSession ? 'keep-with-tmux' : 'keep'; + return + ; + $[73] = handleMenuSelect; + $[74] = menuItems; + $[75] = t20; + $[76] = t21; + } else { + t21 = $[76]; + } + let t22; + if ($[77] !== changes) { + t22 = changes.length > 0 && {changes[changes.length - 1]}; + $[77] = changes; + $[78] = t22; + } else { + t22 = $[78]; + } + let t23; + if ($[79] !== t21 || $[80] !== t22) { + t23 = {t21}{t22}; + $[79] = t21; + $[80] = t22; + $[81] = t23; + } else { + t23 = $[81]; + } + let t24; + if ($[82] !== modeState.agent.agentType || $[83] !== t19 || $[84] !== t23) { + t24 = {t23}; + $[82] = modeState.agent.agentType; + $[83] = t19; + $[84] = t23; + $[85] = t24; + } else { + t24 = $[85]; + } + let t25; + if ($[86] === Symbol.for("react.memo_cache_sentinel")) { + t25 = ; + $[86] = t25; + } else { + t25 = $[86]; + } + let t26; + if ($[87] !== t24) { + t26 = <>{t24}{t25}; + $[87] = t24; + $[88] = t26; + } else { + t26 = $[88]; + } + return t26; + } + case "view-agent": + { + let t13; + if ($[89] !== allAgents || $[90] !== modeState.agent) { + let t14; + if ($[92] !== modeState.agent) { + t14 = a_8 => a_8.agentType === modeState.agent.agentType && a_8.source === modeState.agent.source; + $[92] = modeState.agent; + $[93] = t14; + } else { + t14 = $[93]; + } + t13 = allAgents.find(t14); + $[89] = allAgents; + $[90] = modeState.agent; + $[91] = t13; + } else { + t13 = $[91]; + } + const freshAgent_0 = t13; + const agentToDisplay = freshAgent_0 || modeState.agent; + let t14; + if ($[94] !== agentToDisplay || $[95] !== modeState.previousMode) { + t14 = () => setModeState({ + mode: "agent-menu", + agent: agentToDisplay, + previousMode: modeState.previousMode + }); + $[94] = agentToDisplay; + $[95] = modeState.previousMode; + $[96] = t14; + } else { + t14 = $[96]; + } + let t15; + if ($[97] !== agentToDisplay || $[98] !== modeState.previousMode) { + t15 = () => setModeState({ + mode: "agent-menu", + agent: agentToDisplay, + previousMode: modeState.previousMode + }); + $[97] = agentToDisplay; + $[98] = modeState.previousMode; + $[99] = t15; + } else { + t15 = $[99]; + } + let t16; + if ($[100] !== agentToDisplay || $[101] !== allAgents || $[102] !== mergedTools || $[103] !== t15) { + t16 = ; + $[100] = agentToDisplay; + $[101] = allAgents; + $[102] = mergedTools; + $[103] = t15; + $[104] = t16; + } else { + t16 = $[104]; + } + let t17; + if ($[105] !== agentToDisplay.agentType || $[106] !== t14 || $[107] !== t16) { + t17 = {t16}; + $[105] = agentToDisplay.agentType; + $[106] = t14; + $[107] = t16; + $[108] = t17; + } else { + t17 = $[108]; + } + let t18; + if ($[109] === Symbol.for("react.memo_cache_sentinel")) { + t18 = ; + $[109] = t18; + } else { + t18 = $[109]; + } + let t19; + if ($[110] !== t17) { + t19 = <>{t17}{t18}; + $[110] = t17; + $[111] = t19; + } else { + t19 = $[111]; + } + return t19; + } + case "delete-confirm": + { + let t13; + if ($[112] === Symbol.for("react.memo_cache_sentinel")) { + t13 = [{ + label: "Yes, delete", + value: "yes" + }, { + label: "No, cancel", + value: "no" + }]; + $[112] = t13; + } else { + t13 = $[112]; + } + const deleteOptions = t13; + let t14; + if ($[113] !== modeState) { + t14 = () => { + if ("previousMode" in modeState) { + setModeState(modeState.previousMode); + } + }; + $[113] = modeState; + $[114] = t14; + } else { + t14 = $[114]; + } + let t15; + if ($[115] !== modeState.agent.agentType) { + t15 = Are you sure you want to delete the agent{" "}{modeState.agent.agentType}?; + $[115] = modeState.agent.agentType; + $[116] = t15; + } else { + t15 = $[116]; + } + let t16; + if ($[117] !== modeState.agent.source) { + t16 = Source: {modeState.agent.source}; + $[117] = modeState.agent.source; + $[118] = t16; + } else { + t16 = $[118]; + } + let t17; + if ($[119] !== handleAgentDeleted || $[120] !== modeState) { + t17 = value => { + if (value === "yes") { + handleAgentDeleted(modeState.agent); + } else { + if ("previousMode" in modeState) { + setModeState(modeState.previousMode); + } + } + }; + $[119] = handleAgentDeleted; + $[120] = modeState; + $[121] = t17; + } else { + t17 = $[121]; + } + let t18; + if ($[122] !== modeState) { + t18 = () => { + if ("previousMode" in modeState) { + setModeState(modeState.previousMode); + } + }; + $[122] = modeState; + $[123] = t18; + } else { + t18 = $[123]; + } + let t19; + if ($[124] !== t17 || $[125] !== t18) { + t19 = ; + $[6] = defaultModel; + $[7] = modelOptions; + $[8] = onComplete; + $[9] = t3; + $[10] = t4; + } else { + t4 = $[10]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJnZXRBZ2VudE1vZGVsT3B0aW9ucyIsIlNlbGVjdCIsIk1vZGVsU2VsZWN0b3JQcm9wcyIsImluaXRpYWxNb2RlbCIsIm9uQ29tcGxldGUiLCJtb2RlbCIsIm9uQ2FuY2VsIiwiTW9kZWxTZWxlY3RvciIsInQwIiwiJCIsIl9jIiwidDEiLCJiYjAiLCJiYXNlIiwic29tZSIsIm8iLCJ2YWx1ZSIsImxhYmVsIiwiZGVzY3JpcHRpb24iLCJtb2RlbE9wdGlvbnMiLCJkZWZhdWx0TW9kZWwiLCJ0MiIsIlN5bWJvbCIsImZvciIsInQzIiwidW5kZWZpbmVkIiwidDQiXSwic291cmNlcyI6WyJNb2RlbFNlbGVjdG9yLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGdldEFnZW50TW9kZWxPcHRpb25zIH0gZnJvbSAnLi4vLi4vdXRpbHMvbW9kZWwvYWdlbnQuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi9DdXN0b21TZWxlY3Qvc2VsZWN0LmpzJ1xuXG5pbnRlcmZhY2UgTW9kZWxTZWxlY3RvclByb3BzIHtcbiAgaW5pdGlhbE1vZGVsPzogc3RyaW5nXG4gIG9uQ29tcGxldGU6IChtb2RlbD86IHN0cmluZykgPT4gdm9pZFxuICBvbkNhbmNlbD86ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIE1vZGVsU2VsZWN0b3Ioe1xuICBpbml0aWFsTW9kZWwsXG4gIG9uQ29tcGxldGUsXG4gIG9uQ2FuY2VsLFxufTogTW9kZWxTZWxlY3RvclByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgbW9kZWxPcHRpb25zID0gUmVhY3QudXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3QgYmFzZSA9IGdldEFnZW50TW9kZWxPcHRpb25zKClcbiAgICAvLyBJZiB0aGUgYWdlbnQncyBjdXJyZW50IG1vZGVsIGlzIGEgZnVsbCBJRCAoZS5nLiAnY2xhdWRlLW9wdXMtNC01Jykgbm90XG4gICAgLy8gaW4gdGhlIGFsaWFzIGxpc3QsIGluamVjdCBpdCBhcyBhbiBvcHRpb24gc28gaXQgY2FuIHJvdW5kLXRyaXAgdGhyb3VnaFxuICAgIC8vIGNvbmZpcm0gd2l0aG91dCBiZWluZyBvdmVyd3JpdHRlbi5cbiAgICBpZiAoaW5pdGlhbE1vZGVsICYmICFiYXNlLnNvbWUobyA9PiBvLnZhbHVlID09PSBpbml0aWFsTW9kZWwpKSB7XG4gICAgICByZXR1cm4gW1xuICAgICAgICB7XG4gICAgICAgICAgdmFsdWU6IGluaXRpYWxNb2RlbCxcbiAgICAgICAgICBsYWJlbDogaW5pdGlhbE1vZGVsLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ3VycmVudCBtb2RlbCAoY3VzdG9tIElEKScsXG4gICAgICAgIH0sXG4gICAgICAgIC4uLmJhc2UsXG4gICAgICBdXG4gICAgfVxuICAgIHJldHVybiBiYXNlXG4gIH0sIFtpbml0aWFsTW9kZWxdKVxuXG4gIGNvbnN0IGRlZmF1bHRNb2RlbCA9IGluaXRpYWxNb2RlbCA/PyAnc29ubmV0J1xuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgIE1vZGVsIGRldGVybWluZXMgdGhlIGFnZW50JmFwb3M7cyByZWFzb25pbmcgY2FwYWJpbGl0aWVzIGFuZCBzcGVlZC5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgICA8U2VsZWN0XG4gICAgICAgIG9wdGlvbnM9e21vZGVsT3B0aW9uc31cbiAgICAgICAgZGVmYXVsdFZhbHVlPXtkZWZhdWx0TW9kZWx9XG4gICAgICAgIG9uQ2hhbmdlPXtvbkNvbXBsZXRlfVxuICAgICAgICBvbkNhbmNlbD17KCkgPT4gKG9uQ2FuY2VsID8gb25DYW5jZWwoKSA6IG9uQ29tcGxldGUodW5kZWZpbmVkKSl9XG4gICAgICAvPlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsU0FBU0Msb0JBQW9CLFFBQVEsNEJBQTRCO0FBQ2pFLFNBQVNDLE1BQU0sUUFBUSwyQkFBMkI7QUFFbEQsVUFBVUMsa0JBQWtCLENBQUM7RUFDM0JDLFlBQVksQ0FBQyxFQUFFLE1BQU07RUFDckJDLFVBQVUsRUFBRSxDQUFDQyxLQUFjLENBQVIsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQ3BDQyxRQUFRLENBQUMsRUFBRSxHQUFHLEdBQUcsSUFBSTtBQUN2QjtBQUVBLE9BQU8sU0FBQUMsY0FBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUF1QjtJQUFBUCxZQUFBO0lBQUFDLFVBQUE7SUFBQUU7RUFBQSxJQUFBRSxFQUlUO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQU4sWUFBQTtJQUFBUyxHQUFBO01BRWpCLE1BQUFDLElBQUEsR0FBYWIsb0JBQW9CLENBQUMsQ0FBQztNQUluQyxJQUFJRyxZQUF5RCxJQUF6RCxDQUFpQlUsSUFBSSxDQUFBQyxJQUFLLENBQUNDLENBQUEsSUFBS0EsQ0FBQyxDQUFBQyxLQUFNLEtBQUtiLFlBQVksQ0FBQztRQUMzRFEsRUFBQSxHQUFPLENBQ0w7VUFBQUssS0FBQSxFQUNTYixZQUFZO1VBQUFjLEtBQUEsRUFDWmQsWUFBWTtVQUFBZSxXQUFBLEVBQ047UUFDZixDQUFDLEtBQ0VMLElBQUksQ0FDUjtRQVBELE1BQUFELEdBQUE7TUFPQztNQUVIRCxFQUFBLEdBQU9FLElBQUk7SUFBQTtJQUFBSixDQUFBLE1BQUFOLFlBQUE7SUFBQU0sQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFmYixNQUFBVSxZQUFBLEdBQXFCUixFQWdCSDtFQUVsQixNQUFBUyxZQUFBLEdBQXFCakIsWUFBd0IsSUFBeEIsUUFBd0I7RUFBQSxJQUFBa0IsRUFBQTtFQUFBLElBQUFaLENBQUEsUUFBQWEsTUFBQSxDQUFBQyxHQUFBO0lBSXpDRixFQUFBLElBQUMsR0FBRyxDQUFlLFlBQUMsQ0FBRCxHQUFDLENBQ2xCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyw4REFFZixFQUZDLElBQUksQ0FHUCxFQUpDLEdBQUcsQ0FJRTtJQUFBWixDQUFBLE1BQUFZLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFaLENBQUE7RUFBQTtFQUFBLElBQUFlLEVBQUE7RUFBQSxJQUFBZixDQUFBLFFBQUFILFFBQUEsSUFBQUcsQ0FBQSxRQUFBTCxVQUFBO0lBS01vQixFQUFBLEdBQUFBLENBQUEsS0FBT2xCLFFBQVEsR0FBR0EsUUFBUSxDQUF5QixDQUFDLEdBQXJCRixVQUFVLENBQUNxQixTQUFTLENBQUU7SUFBQWhCLENBQUEsTUFBQUgsUUFBQTtJQUFBRyxDQUFBLE1BQUFMLFVBQUE7SUFBQUssQ0FBQSxNQUFBZSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBZixDQUFBO0VBQUE7RUFBQSxJQUFBaUIsRUFBQTtFQUFBLElBQUFqQixDQUFBLFFBQUFXLFlBQUEsSUFBQVgsQ0FBQSxRQUFBVSxZQUFBLElBQUFWLENBQUEsUUFBQUwsVUFBQSxJQUFBSyxDQUFBLFFBQUFlLEVBQUE7SUFWbkVFLEVBQUEsSUFBQyxHQUFHLENBQWUsYUFBUSxDQUFSLFFBQVEsQ0FDekIsQ0FBQUwsRUFJSyxDQUNMLENBQUMsTUFBTSxDQUNJRixPQUFZLENBQVpBLGFBQVcsQ0FBQyxDQUNQQyxZQUFZLENBQVpBLGFBQVcsQ0FBQyxDQUNoQmhCLFFBQVUsQ0FBVkEsV0FBUyxDQUFDLENBQ1YsUUFBcUQsQ0FBckQsQ0FBQW9CLEVBQW9ELENBQUMsR0FFbkUsRUFaQyxHQUFHLENBWUU7SUFBQWYsQ0FBQSxNQUFBVyxZQUFBO0lBQUFYLENBQUEsTUFBQVUsWUFBQTtJQUFBVixDQUFBLE1BQUFMLFVBQUE7SUFBQUssQ0FBQSxNQUFBZSxFQUFBO0lBQUFmLENBQUEsT0FBQWlCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFqQixDQUFBO0VBQUE7RUFBQSxPQVpOaUIsRUFZTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/agents/ToolSelector.tsx b/components/agents/ToolSelector.tsx new file mode 100644 index 0000000..3eb61d1 --- /dev/null +++ b/components/agents/ToolSelector.tsx @@ -0,0 +1,562 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import React, { useCallback, useMemo, useState } from 'react'; +import { mcpInfoFromString } from 'src/services/mcp/mcpStringUtils.js'; +import { isMcpTool } from 'src/services/mcp/utils.js'; +import type { Tool, Tools } from 'src/Tool.js'; +import { filterToolsForAgent } from 'src/tools/AgentTool/agentToolUtils.js'; +import { AGENT_TOOL_NAME } from 'src/tools/AgentTool/constants.js'; +import { BashTool } from 'src/tools/BashTool/BashTool.js'; +import { ExitPlanModeV2Tool } from 'src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.js'; +import { FileEditTool } from 'src/tools/FileEditTool/FileEditTool.js'; +import { FileReadTool } from 'src/tools/FileReadTool/FileReadTool.js'; +import { FileWriteTool } from 'src/tools/FileWriteTool/FileWriteTool.js'; +import { GlobTool } from 'src/tools/GlobTool/GlobTool.js'; +import { GrepTool } from 'src/tools/GrepTool/GrepTool.js'; +import { ListMcpResourcesTool } from 'src/tools/ListMcpResourcesTool/ListMcpResourcesTool.js'; +import { NotebookEditTool } from 'src/tools/NotebookEditTool/NotebookEditTool.js'; +import { ReadMcpResourceTool } from 'src/tools/ReadMcpResourceTool/ReadMcpResourceTool.js'; +import { TaskOutputTool } from 'src/tools/TaskOutputTool/TaskOutputTool.js'; +import { TaskStopTool } from 'src/tools/TaskStopTool/TaskStopTool.js'; +import { TodoWriteTool } from 'src/tools/TodoWriteTool/TodoWriteTool.js'; +import { TungstenTool } from 'src/tools/TungstenTool/TungstenTool.js'; +import { WebFetchTool } from 'src/tools/WebFetchTool/WebFetchTool.js'; +import { WebSearchTool } from 'src/tools/WebSearchTool/WebSearchTool.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { count } from '../../utils/array.js'; +import { plural } from '../../utils/stringUtils.js'; +import { Divider } from '../design-system/Divider.js'; +type Props = { + tools: Tools; + initialTools: string[] | undefined; + onComplete: (selectedTools: string[] | undefined) => void; + onCancel?: () => void; +}; +type ToolBucket = { + name: string; + toolNames: Set; + isMcp?: boolean; +}; +type ToolBuckets = { + READ_ONLY: ToolBucket; + EDIT: ToolBucket; + EXECUTION: ToolBucket; + MCP: ToolBucket; + OTHER: ToolBucket; +}; +function getToolBuckets(): ToolBuckets { + return { + READ_ONLY: { + name: 'Read-only tools', + toolNames: new Set([GlobTool.name, GrepTool.name, ExitPlanModeV2Tool.name, FileReadTool.name, WebFetchTool.name, TodoWriteTool.name, WebSearchTool.name, TaskStopTool.name, TaskOutputTool.name, ListMcpResourcesTool.name, ReadMcpResourceTool.name]) + }, + EDIT: { + name: 'Edit tools', + toolNames: new Set([FileEditTool.name, FileWriteTool.name, NotebookEditTool.name]) + }, + EXECUTION: { + name: 'Execution tools', + toolNames: new Set([BashTool.name, "external" === 'ant' ? TungstenTool.name : undefined].filter(n => n !== undefined)) + }, + MCP: { + name: 'MCP tools', + toolNames: new Set(), + // Dynamic - no static list + isMcp: true + }, + OTHER: { + name: 'Other tools', + toolNames: new Set() // Dynamic - catch-all for uncategorized tools + } + }; +} + +// Helper to get MCP server buckets dynamically +function getMcpServerBuckets(tools: Tools): Array<{ + serverName: string; + tools: Tools; +}> { + const serverMap = new Map(); + tools.forEach(tool => { + if (isMcpTool(tool)) { + const mcpInfo = mcpInfoFromString(tool.name); + if (mcpInfo?.serverName) { + const existing = serverMap.get(mcpInfo.serverName) || []; + existing.push(tool); + serverMap.set(mcpInfo.serverName, existing); + } + } + }); + return Array.from(serverMap.entries()).map(([serverName, tools]) => ({ + serverName, + tools + })).sort((a, b) => a.serverName.localeCompare(b.serverName)); +} +export function ToolSelector(t0) { + const $ = _c(69); + const { + tools, + initialTools, + onComplete, + onCancel + } = t0; + let t1; + if ($[0] !== tools) { + t1 = filterToolsForAgent({ + tools, + isBuiltIn: false, + isAsync: false + }); + $[0] = tools; + $[1] = t1; + } else { + t1 = $[1]; + } + const customAgentTools = t1; + let t2; + if ($[2] !== customAgentTools || $[3] !== initialTools) { + t2 = !initialTools || initialTools.includes("*") ? customAgentTools.map(_temp) : initialTools; + $[2] = customAgentTools; + $[3] = initialTools; + $[4] = t2; + } else { + t2 = $[4]; + } + const expandedInitialTools = t2; + const [selectedTools, setSelectedTools] = useState(expandedInitialTools); + const [focusIndex, setFocusIndex] = useState(0); + const [showIndividualTools, setShowIndividualTools] = useState(false); + let t3; + if ($[5] !== customAgentTools) { + t3 = new Set(customAgentTools.map(_temp2)); + $[5] = customAgentTools; + $[6] = t3; + } else { + t3 = $[6]; + } + const toolNames = t3; + let t4; + if ($[7] !== selectedTools || $[8] !== toolNames) { + let t5; + if ($[10] !== toolNames) { + t5 = name => toolNames.has(name); + $[10] = toolNames; + $[11] = t5; + } else { + t5 = $[11]; + } + t4 = selectedTools.filter(t5); + $[7] = selectedTools; + $[8] = toolNames; + $[9] = t4; + } else { + t4 = $[9]; + } + const validSelectedTools = t4; + let t5; + if ($[12] !== validSelectedTools) { + t5 = new Set(validSelectedTools); + $[12] = validSelectedTools; + $[13] = t5; + } else { + t5 = $[13]; + } + const selectedSet = t5; + const isAllSelected = validSelectedTools.length === customAgentTools.length && customAgentTools.length > 0; + let t6; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t6 = toolName => { + if (!toolName) { + return; + } + setSelectedTools(current => current.includes(toolName) ? current.filter(t_1 => t_1 !== toolName) : [...current, toolName]); + }; + $[14] = t6; + } else { + t6 = $[14]; + } + const handleToggleTool = t6; + let t7; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t7 = (toolNames_0, select) => { + setSelectedTools(current_0 => { + if (select) { + const toolsToAdd = toolNames_0.filter(t_2 => !current_0.includes(t_2)); + return [...current_0, ...toolsToAdd]; + } else { + return current_0.filter(t_3 => !toolNames_0.includes(t_3)); + } + }); + }; + $[15] = t7; + } else { + t7 = $[15]; + } + const handleToggleTools = t7; + let t8; + if ($[16] !== customAgentTools || $[17] !== onComplete || $[18] !== validSelectedTools) { + t8 = () => { + const allToolNames = customAgentTools.map(_temp3); + const areAllToolsSelected = validSelectedTools.length === allToolNames.length && allToolNames.every(name_0 => validSelectedTools.includes(name_0)); + const finalTools = areAllToolsSelected ? undefined : validSelectedTools; + onComplete(finalTools); + }; + $[16] = customAgentTools; + $[17] = onComplete; + $[18] = validSelectedTools; + $[19] = t8; + } else { + t8 = $[19]; + } + const handleConfirm = t8; + let buckets; + if ($[20] !== customAgentTools) { + const toolBuckets = getToolBuckets(); + buckets = { + readOnly: [] as Tool[], + edit: [] as Tool[], + execution: [] as Tool[], + mcp: [] as Tool[], + other: [] as Tool[] + }; + customAgentTools.forEach(tool => { + if (isMcpTool(tool)) { + buckets.mcp.push(tool); + } else { + if (toolBuckets.READ_ONLY.toolNames.has(tool.name)) { + buckets.readOnly.push(tool); + } else { + if (toolBuckets.EDIT.toolNames.has(tool.name)) { + buckets.edit.push(tool); + } else { + if (toolBuckets.EXECUTION.toolNames.has(tool.name)) { + buckets.execution.push(tool); + } else { + if (tool.name !== AGENT_TOOL_NAME) { + buckets.other.push(tool); + } + } + } + } + } + }); + $[20] = customAgentTools; + $[21] = buckets; + } else { + buckets = $[21]; + } + const toolsByBucket = buckets; + let t9; + if ($[22] !== selectedSet) { + t9 = bucketTools => { + const selected = count(bucketTools, t_5 => selectedSet.has(t_5.name)); + const needsSelection = selected < bucketTools.length; + return () => { + const toolNames_1 = bucketTools.map(_temp4); + handleToggleTools(toolNames_1, needsSelection); + }; + }; + $[22] = selectedSet; + $[23] = t9; + } else { + t9 = $[23]; + } + const createBucketToggleAction = t9; + let navigableItems; + if ($[24] !== createBucketToggleAction || $[25] !== customAgentTools || $[26] !== focusIndex || $[27] !== handleConfirm || $[28] !== isAllSelected || $[29] !== selectedSet || $[30] !== showIndividualTools || $[31] !== toolsByBucket.edit || $[32] !== toolsByBucket.execution || $[33] !== toolsByBucket.mcp || $[34] !== toolsByBucket.other || $[35] !== toolsByBucket.readOnly) { + navigableItems = []; + navigableItems.push({ + id: "continue", + label: "Continue", + action: handleConfirm, + isContinue: true + }); + let t10; + if ($[37] !== customAgentTools || $[38] !== isAllSelected) { + t10 = () => { + const allToolNames_0 = customAgentTools.map(_temp5); + handleToggleTools(allToolNames_0, !isAllSelected); + }; + $[37] = customAgentTools; + $[38] = isAllSelected; + $[39] = t10; + } else { + t10 = $[39]; + } + navigableItems.push({ + id: "bucket-all", + label: `${isAllSelected ? figures.checkboxOn : figures.checkboxOff} All tools`, + action: t10 + }); + const toolBuckets_0 = getToolBuckets(); + const bucketConfigs = [{ + id: "bucket-readonly", + name: toolBuckets_0.READ_ONLY.name, + tools: toolsByBucket.readOnly + }, { + id: "bucket-edit", + name: toolBuckets_0.EDIT.name, + tools: toolsByBucket.edit + }, { + id: "bucket-execution", + name: toolBuckets_0.EXECUTION.name, + tools: toolsByBucket.execution + }, { + id: "bucket-mcp", + name: toolBuckets_0.MCP.name, + tools: toolsByBucket.mcp + }, { + id: "bucket-other", + name: toolBuckets_0.OTHER.name, + tools: toolsByBucket.other + }]; + bucketConfigs.forEach(t11 => { + const { + id, + name: name_1, + tools: bucketTools_0 + } = t11; + if (bucketTools_0.length === 0) { + return; + } + const selected_0 = count(bucketTools_0, t_8 => selectedSet.has(t_8.name)); + const isFullySelected = selected_0 === bucketTools_0.length; + navigableItems.push({ + id, + label: `${isFullySelected ? figures.checkboxOn : figures.checkboxOff} ${name_1}`, + action: createBucketToggleAction(bucketTools_0) + }); + }); + const toggleButtonIndex = navigableItems.length; + let t12; + if ($[40] !== focusIndex || $[41] !== showIndividualTools || $[42] !== toggleButtonIndex) { + t12 = () => { + setShowIndividualTools(!showIndividualTools); + if (showIndividualTools && focusIndex > toggleButtonIndex) { + setFocusIndex(toggleButtonIndex); + } + }; + $[40] = focusIndex; + $[41] = showIndividualTools; + $[42] = toggleButtonIndex; + $[43] = t12; + } else { + t12 = $[43]; + } + navigableItems.push({ + id: "toggle-individual", + label: showIndividualTools ? "Hide advanced options" : "Show advanced options", + action: t12, + isToggle: true + }); + const mcpServerBuckets = getMcpServerBuckets(customAgentTools); + if (showIndividualTools) { + if (mcpServerBuckets.length > 0) { + navigableItems.push({ + id: "mcp-servers-header", + label: "MCP Servers:", + action: _temp6, + isHeader: true + }); + mcpServerBuckets.forEach(t13 => { + const { + serverName, + tools: serverTools + } = t13; + const selected_1 = count(serverTools, t_9 => selectedSet.has(t_9.name)); + const isFullySelected_0 = selected_1 === serverTools.length; + navigableItems.push({ + id: `mcp-server-${serverName}`, + label: `${isFullySelected_0 ? figures.checkboxOn : figures.checkboxOff} ${serverName} (${serverTools.length} ${plural(serverTools.length, "tool")})`, + action: () => { + const toolNames_2 = serverTools.map(_temp7); + handleToggleTools(toolNames_2, !isFullySelected_0); + } + }); + }); + navigableItems.push({ + id: "tools-header", + label: "Individual Tools:", + action: _temp8, + isHeader: true + }); + } + customAgentTools.forEach(tool_0 => { + let displayName = tool_0.name; + if (tool_0.name.startsWith("mcp__")) { + const mcpInfo = mcpInfoFromString(tool_0.name); + displayName = mcpInfo ? `${mcpInfo.toolName} (${mcpInfo.serverName})` : tool_0.name; + } + navigableItems.push({ + id: `tool-${tool_0.name}`, + label: `${selectedSet.has(tool_0.name) ? figures.checkboxOn : figures.checkboxOff} ${displayName}`, + action: () => handleToggleTool(tool_0.name) + }); + }); + } + $[24] = createBucketToggleAction; + $[25] = customAgentTools; + $[26] = focusIndex; + $[27] = handleConfirm; + $[28] = isAllSelected; + $[29] = selectedSet; + $[30] = showIndividualTools; + $[31] = toolsByBucket.edit; + $[32] = toolsByBucket.execution; + $[33] = toolsByBucket.mcp; + $[34] = toolsByBucket.other; + $[35] = toolsByBucket.readOnly; + $[36] = navigableItems; + } else { + navigableItems = $[36]; + } + let t10; + if ($[44] !== initialTools || $[45] !== onCancel || $[46] !== onComplete) { + t10 = () => { + if (onCancel) { + onCancel(); + } else { + onComplete(initialTools); + } + }; + $[44] = initialTools; + $[45] = onCancel; + $[46] = onComplete; + $[47] = t10; + } else { + t10 = $[47]; + } + const handleCancel = t10; + let t11; + if ($[48] === Symbol.for("react.memo_cache_sentinel")) { + t11 = { + context: "Confirmation" + }; + $[48] = t11; + } else { + t11 = $[48]; + } + useKeybinding("confirm:no", handleCancel, t11); + let t12; + if ($[49] !== focusIndex || $[50] !== navigableItems) { + t12 = e => { + if (e.key === "return") { + e.preventDefault(); + const item = navigableItems[focusIndex]; + if (item && !item.isHeader) { + item.action(); + } + } else { + if (e.key === "up") { + e.preventDefault(); + let newIndex = focusIndex - 1; + while (newIndex > 0 && navigableItems[newIndex]?.isHeader) { + newIndex--; + } + setFocusIndex(Math.max(0, newIndex)); + } else { + if (e.key === "down") { + e.preventDefault(); + let newIndex_0 = focusIndex + 1; + while (newIndex_0 < navigableItems.length - 1 && navigableItems[newIndex_0]?.isHeader) { + newIndex_0++; + } + setFocusIndex(Math.min(navigableItems.length - 1, newIndex_0)); + } + } + } + }; + $[49] = focusIndex; + $[50] = navigableItems; + $[51] = t12; + } else { + t12 = $[51]; + } + const handleKeyDown = t12; + const t13 = focusIndex === 0 ? "suggestion" : undefined; + const t14 = focusIndex === 0; + const t15 = focusIndex === 0 ? `${figures.pointer} ` : " "; + let t16; + if ($[52] !== t13 || $[53] !== t14 || $[54] !== t15) { + t16 = {t15}[ Continue ]; + $[52] = t13; + $[53] = t14; + $[54] = t15; + $[55] = t16; + } else { + t16 = $[55]; + } + let t17; + if ($[56] === Symbol.for("react.memo_cache_sentinel")) { + t17 = ; + $[56] = t17; + } else { + t17 = $[56]; + } + let t18; + if ($[57] !== navigableItems) { + t18 = navigableItems.slice(1); + $[57] = navigableItems; + $[58] = t18; + } else { + t18 = $[58]; + } + let t19; + if ($[59] !== focusIndex || $[60] !== t18) { + t19 = t18.map((item_0, index) => { + const isCurrentlyFocused = index + 1 === focusIndex; + const isToggleButton = item_0.isToggle; + const isHeader = item_0.isHeader; + return {isToggleButton && }{isHeader && index > 0 && }{isHeader ? "" : isCurrentlyFocused ? `${figures.pointer} ` : " "}{isToggleButton ? `[ ${item_0.label} ]` : item_0.label}; + }); + $[59] = focusIndex; + $[60] = t18; + $[61] = t19; + } else { + t19 = $[61]; + } + const t20 = isAllSelected ? "All tools selected" : `${selectedSet.size} of ${customAgentTools.length} tools selected`; + let t21; + if ($[62] !== t20) { + t21 = {t20}; + $[62] = t20; + $[63] = t21; + } else { + t21 = $[63]; + } + let t22; + if ($[64] !== handleKeyDown || $[65] !== t16 || $[66] !== t19 || $[67] !== t21) { + t22 = {t16}{t17}{t19}{t21}; + $[64] = handleKeyDown; + $[65] = t16; + $[66] = t19; + $[67] = t21; + $[68] = t22; + } else { + t22 = $[68]; + } + return t22; +} +function _temp8() {} +function _temp7(t_10) { + return t_10.name; +} +function _temp6() {} +function _temp5(t_7) { + return t_7.name; +} +function _temp4(t_6) { + return t_6.name; +} +function _temp3(t_4) { + return t_4.name; +} +function _temp2(t_0) { + return t_0.name; +} +function _temp(t) { + return t.name; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZU1lbW8iLCJ1c2VTdGF0ZSIsIm1jcEluZm9Gcm9tU3RyaW5nIiwiaXNNY3BUb29sIiwiVG9vbCIsIlRvb2xzIiwiZmlsdGVyVG9vbHNGb3JBZ2VudCIsIkFHRU5UX1RPT0xfTkFNRSIsIkJhc2hUb29sIiwiRXhpdFBsYW5Nb2RlVjJUb29sIiwiRmlsZUVkaXRUb29sIiwiRmlsZVJlYWRUb29sIiwiRmlsZVdyaXRlVG9vbCIsIkdsb2JUb29sIiwiR3JlcFRvb2wiLCJMaXN0TWNwUmVzb3VyY2VzVG9vbCIsIk5vdGVib29rRWRpdFRvb2wiLCJSZWFkTWNwUmVzb3VyY2VUb29sIiwiVGFza091dHB1dFRvb2wiLCJUYXNrU3RvcFRvb2wiLCJUb2RvV3JpdGVUb29sIiwiVHVuZ3N0ZW5Ub29sIiwiV2ViRmV0Y2hUb29sIiwiV2ViU2VhcmNoVG9vbCIsIktleWJvYXJkRXZlbnQiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZyIsImNvdW50IiwicGx1cmFsIiwiRGl2aWRlciIsIlByb3BzIiwidG9vbHMiLCJpbml0aWFsVG9vbHMiLCJvbkNvbXBsZXRlIiwic2VsZWN0ZWRUb29scyIsIm9uQ2FuY2VsIiwiVG9vbEJ1Y2tldCIsIm5hbWUiLCJ0b29sTmFtZXMiLCJTZXQiLCJpc01jcCIsIlRvb2xCdWNrZXRzIiwiUkVBRF9PTkxZIiwiRURJVCIsIkVYRUNVVElPTiIsIk1DUCIsIk9USEVSIiwiZ2V0VG9vbEJ1Y2tldHMiLCJ1bmRlZmluZWQiLCJmaWx0ZXIiLCJuIiwiZ2V0TWNwU2VydmVyQnVja2V0cyIsIkFycmF5Iiwic2VydmVyTmFtZSIsInNlcnZlck1hcCIsIk1hcCIsImZvckVhY2giLCJ0b29sIiwibWNwSW5mbyIsImV4aXN0aW5nIiwiZ2V0IiwicHVzaCIsInNldCIsImZyb20iLCJlbnRyaWVzIiwibWFwIiwic29ydCIsImEiLCJiIiwibG9jYWxlQ29tcGFyZSIsIlRvb2xTZWxlY3RvciIsInQwIiwiJCIsIl9jIiwidDEiLCJpc0J1aWx0SW4iLCJpc0FzeW5jIiwiY3VzdG9tQWdlbnRUb29scyIsInQyIiwiaW5jbHVkZXMiLCJfdGVtcCIsImV4cGFuZGVkSW5pdGlhbFRvb2xzIiwic2V0U2VsZWN0ZWRUb29scyIsImZvY3VzSW5kZXgiLCJzZXRGb2N1c0luZGV4Iiwic2hvd0luZGl2aWR1YWxUb29scyIsInNldFNob3dJbmRpdmlkdWFsVG9vbHMiLCJ0MyIsIl90ZW1wMiIsInQ0IiwidDUiLCJoYXMiLCJ2YWxpZFNlbGVjdGVkVG9vbHMiLCJzZWxlY3RlZFNldCIsImlzQWxsU2VsZWN0ZWQiLCJsZW5ndGgiLCJ0NiIsIlN5bWJvbCIsImZvciIsInRvb2xOYW1lIiwiY3VycmVudCIsInRfMSIsInQiLCJoYW5kbGVUb2dnbGVUb29sIiwidDciLCJ0b29sTmFtZXNfMCIsInNlbGVjdCIsImN1cnJlbnRfMCIsInRvb2xzVG9BZGQiLCJ0XzIiLCJ0XzMiLCJoYW5kbGVUb2dnbGVUb29scyIsInQ4IiwiYWxsVG9vbE5hbWVzIiwiX3RlbXAzIiwiYXJlQWxsVG9vbHNTZWxlY3RlZCIsImV2ZXJ5IiwibmFtZV8wIiwiZmluYWxUb29scyIsImhhbmRsZUNvbmZpcm0iLCJidWNrZXRzIiwidG9vbEJ1Y2tldHMiLCJyZWFkT25seSIsImVkaXQiLCJleGVjdXRpb24iLCJtY3AiLCJvdGhlciIsInRvb2xzQnlCdWNrZXQiLCJ0OSIsImJ1Y2tldFRvb2xzIiwic2VsZWN0ZWQiLCJ0XzUiLCJuZWVkc1NlbGVjdGlvbiIsInRvb2xOYW1lc18xIiwiX3RlbXA0IiwiY3JlYXRlQnVja2V0VG9nZ2xlQWN0aW9uIiwibmF2aWdhYmxlSXRlbXMiLCJpZCIsImxhYmVsIiwiYWN0aW9uIiwiaXNDb250aW51ZSIsInQxMCIsImFsbFRvb2xOYW1lc18wIiwiX3RlbXA1IiwiY2hlY2tib3hPbiIsImNoZWNrYm94T2ZmIiwidG9vbEJ1Y2tldHNfMCIsImJ1Y2tldENvbmZpZ3MiLCJ0MTEiLCJuYW1lXzEiLCJidWNrZXRUb29sc18wIiwic2VsZWN0ZWRfMCIsInRfOCIsImlzRnVsbHlTZWxlY3RlZCIsInRvZ2dsZUJ1dHRvbkluZGV4IiwidDEyIiwiaXNUb2dnbGUiLCJtY3BTZXJ2ZXJCdWNrZXRzIiwiX3RlbXA2IiwiaXNIZWFkZXIiLCJ0MTMiLCJzZXJ2ZXJUb29scyIsInNlbGVjdGVkXzEiLCJ0XzkiLCJpc0Z1bGx5U2VsZWN0ZWRfMCIsInRvb2xOYW1lc18yIiwiX3RlbXA3IiwiX3RlbXA4IiwidG9vbF8wIiwiZGlzcGxheU5hbWUiLCJzdGFydHNXaXRoIiwiaGFuZGxlQ2FuY2VsIiwiY29udGV4dCIsImUiLCJrZXkiLCJwcmV2ZW50RGVmYXVsdCIsIml0ZW0iLCJuZXdJbmRleCIsIk1hdGgiLCJtYXgiLCJuZXdJbmRleF8wIiwibWluIiwiaGFuZGxlS2V5RG93biIsInQxNCIsInQxNSIsInBvaW50ZXIiLCJ0MTYiLCJ0MTciLCJ0MTgiLCJzbGljZSIsInQxOSIsIml0ZW1fMCIsImluZGV4IiwiaXNDdXJyZW50bHlGb2N1c2VkIiwiaXNUb2dnbGVCdXR0b24iLCJ0MjAiLCJzaXplIiwidDIxIiwidDIyIiwidF8xMCIsInRfNyIsInRfNiIsInRfNCIsInRfMCJdLCJzb3VyY2VzIjpbIlRvb2xTZWxlY3Rvci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCBSZWFjdCwgeyB1c2VDYWxsYmFjaywgdXNlTWVtbywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IG1jcEluZm9Gcm9tU3RyaW5nIH0gZnJvbSAnc3JjL3NlcnZpY2VzL21jcC9tY3BTdHJpbmdVdGlscy5qcydcbmltcG9ydCB7IGlzTWNwVG9vbCB9IGZyb20gJ3NyYy9zZXJ2aWNlcy9tY3AvdXRpbHMuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2wsIFRvb2xzIH0gZnJvbSAnc3JjL1Rvb2wuanMnXG5pbXBvcnQgeyBmaWx0ZXJUb29sc0ZvckFnZW50IH0gZnJvbSAnc3JjL3Rvb2xzL0FnZW50VG9vbC9hZ2VudFRvb2xVdGlscy5qcydcbmltcG9ydCB7IEFHRU5UX1RPT0xfTkFNRSB9IGZyb20gJ3NyYy90b29scy9BZ2VudFRvb2wvY29uc3RhbnRzLmpzJ1xuaW1wb3J0IHsgQmFzaFRvb2wgfSBmcm9tICdzcmMvdG9vbHMvQmFzaFRvb2wvQmFzaFRvb2wuanMnXG5pbXBvcnQgeyBFeGl0UGxhbk1vZGVWMlRvb2wgfSBmcm9tICdzcmMvdG9vbHMvRXhpdFBsYW5Nb2RlVG9vbC9FeGl0UGxhbk1vZGVWMlRvb2wuanMnXG5pbXBvcnQgeyBGaWxlRWRpdFRvb2wgfSBmcm9tICdzcmMvdG9vbHMvRmlsZUVkaXRUb29sL0ZpbGVFZGl0VG9vbC5qcydcbmltcG9ydCB7IEZpbGVSZWFkVG9vbCB9IGZyb20gJ3NyYy90b29scy9GaWxlUmVhZFRvb2wvRmlsZVJlYWRUb29sLmpzJ1xuaW1wb3J0IHsgRmlsZVdyaXRlVG9vbCB9IGZyb20gJ3NyYy90b29scy9GaWxlV3JpdGVUb29sL0ZpbGVXcml0ZVRvb2wuanMnXG5pbXBvcnQgeyBHbG9iVG9vbCB9IGZyb20gJ3NyYy90b29scy9HbG9iVG9vbC9HbG9iVG9vbC5qcydcbmltcG9ydCB7IEdyZXBUb29sIH0gZnJvbSAnc3JjL3Rvb2xzL0dyZXBUb29sL0dyZXBUb29sLmpzJ1xuaW1wb3J0IHsgTGlzdE1jcFJlc291cmNlc1Rvb2wgfSBmcm9tICdzcmMvdG9vbHMvTGlzdE1jcFJlc291cmNlc1Rvb2wvTGlzdE1jcFJlc291cmNlc1Rvb2wuanMnXG5pbXBvcnQgeyBOb3RlYm9va0VkaXRUb29sIH0gZnJvbSAnc3JjL3Rvb2xzL05vdGVib29rRWRpdFRvb2wvTm90ZWJvb2tFZGl0VG9vbC5qcydcbmltcG9ydCB7IFJlYWRNY3BSZXNvdXJjZVRvb2wgfSBmcm9tICdzcmMvdG9vbHMvUmVhZE1jcFJlc291cmNlVG9vbC9SZWFkTWNwUmVzb3VyY2VUb29sLmpzJ1xuaW1wb3J0IHsgVGFza091dHB1dFRvb2wgfSBmcm9tICdzcmMvdG9vbHMvVGFza091dHB1dFRvb2wvVGFza091dHB1dFRvb2wuanMnXG5pbXBvcnQgeyBUYXNrU3RvcFRvb2wgfSBmcm9tICdzcmMvdG9vbHMvVGFza1N0b3BUb29sL1Rhc2tTdG9wVG9vbC5qcydcbmltcG9ydCB7IFRvZG9Xcml0ZVRvb2wgfSBmcm9tICdzcmMvdG9vbHMvVG9kb1dyaXRlVG9vbC9Ub2RvV3JpdGVUb29sLmpzJ1xuaW1wb3J0IHsgVHVuZ3N0ZW5Ub29sIH0gZnJvbSAnc3JjL3Rvb2xzL1R1bmdzdGVuVG9vbC9UdW5nc3RlblRvb2wuanMnXG5pbXBvcnQgeyBXZWJGZXRjaFRvb2wgfSBmcm9tICdzcmMvdG9vbHMvV2ViRmV0Y2hUb29sL1dlYkZldGNoVG9vbC5qcydcbmltcG9ydCB7IFdlYlNlYXJjaFRvb2wgfSBmcm9tICdzcmMvdG9vbHMvV2ViU2VhcmNoVG9vbC9XZWJTZWFyY2hUb29sLmpzJ1xuaW1wb3J0IHR5cGUgeyBLZXlib2FyZEV2ZW50IH0gZnJvbSAnLi4vLi4vaW5rL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgY291bnQgfSBmcm9tICcuLi8uLi91dGlscy9hcnJheS5qcydcbmltcG9ydCB7IHBsdXJhbCB9IGZyb20gJy4uLy4uL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuaW1wb3J0IHsgRGl2aWRlciB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vRGl2aWRlci5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgdG9vbHM6IFRvb2xzXG4gIGluaXRpYWxUb29sczogc3RyaW5nW10gfCB1bmRlZmluZWRcbiAgb25Db21wbGV0ZTogKHNlbGVjdGVkVG9vbHM6IHN0cmluZ1tdIHwgdW5kZWZpbmVkKSA9PiB2b2lkXG4gIG9uQ2FuY2VsPzogKCkgPT4gdm9pZFxufVxuXG50eXBlIFRvb2xCdWNrZXQgPSB7XG4gIG5hbWU6IHN0cmluZ1xuICB0b29sTmFtZXM6IFNldDxzdHJpbmc+XG4gIGlzTWNwPzogYm9vbGVhblxufVxuXG50eXBlIFRvb2xCdWNrZXRzID0ge1xuICBSRUFEX09OTFk6IFRvb2xCdWNrZXRcbiAgRURJVDogVG9vbEJ1Y2tldFxuICBFWEVDVVRJT046IFRvb2xCdWNrZXRcbiAgTUNQOiBUb29sQnVja2V0XG4gIE9USEVSOiBUb29sQnVja2V0XG59XG5cbmZ1bmN0aW9uIGdldFRvb2xCdWNrZXRzKCk6IFRvb2xCdWNrZXRzIHtcbiAgcmV0dXJuIHtcbiAgICBSRUFEX09OTFk6IHtcbiAgICAgIG5hbWU6ICdSZWFkLW9ubHkgdG9vbHMnLFxuICAgICAgdG9vbE5hbWVzOiBuZXcgU2V0KFtcbiAgICAgICAgR2xvYlRvb2wubmFtZSxcbiAgICAgICAgR3JlcFRvb2wubmFtZSxcbiAgICAgICAgRXhpdFBsYW5Nb2RlVjJUb29sLm5hbWUsXG4gICAgICAgIEZpbGVSZWFkVG9vbC5uYW1lLFxuICAgICAgICBXZWJGZXRjaFRvb2wubmFtZSxcbiAgICAgICAgVG9kb1dyaXRlVG9vbC5uYW1lLFxuICAgICAgICBXZWJTZWFyY2hUb29sLm5hbWUsXG4gICAgICAgIFRhc2tTdG9wVG9vbC5uYW1lLFxuICAgICAgICBUYXNrT3V0cHV0VG9vbC5uYW1lLFxuICAgICAgICBMaXN0TWNwUmVzb3VyY2VzVG9vbC5uYW1lLFxuICAgICAgICBSZWFkTWNwUmVzb3VyY2VUb29sLm5hbWUsXG4gICAgICBdKSxcbiAgICB9LFxuICAgIEVESVQ6IHtcbiAgICAgIG5hbWU6ICdFZGl0IHRvb2xzJyxcbiAgICAgIHRvb2xOYW1lczogbmV3IFNldChbXG4gICAgICAgIEZpbGVFZGl0VG9vbC5uYW1lLFxuICAgICAgICBGaWxlV3JpdGVUb29sLm5hbWUsXG4gICAgICAgIE5vdGVib29rRWRpdFRvb2wubmFtZSxcbiAgICAgIF0pLFxuICAgIH0sXG4gICAgRVhFQ1VUSU9OOiB7XG4gICAgICBuYW1lOiAnRXhlY3V0aW9uIHRvb2xzJyxcbiAgICAgIHRvb2xOYW1lczogbmV3IFNldChcbiAgICAgICAgW1xuICAgICAgICAgIEJhc2hUb29sLm5hbWUsXG4gICAgICAgICAgXCJleHRlcm5hbFwiID09PSAnYW50JyA/IFR1bmdzdGVuVG9vbC5uYW1lIDogdW5kZWZpbmVkLFxuICAgICAgICBdLmZpbHRlcihuID0+IG4gIT09IHVuZGVmaW5lZCksXG4gICAgICApLFxuICAgIH0sXG4gICAgTUNQOiB7XG4gICAgICBuYW1lOiAnTUNQIHRvb2xzJyxcbiAgICAgIHRvb2xOYW1lczogbmV3IFNldCgpLCAvLyBEeW5hbWljIC0gbm8gc3RhdGljIGxpc3RcbiAgICAgIGlzTWNwOiB0cnVlLFxuICAgIH0sXG4gICAgT1RIRVI6IHtcbiAgICAgIG5hbWU6ICdPdGhlciB0b29scycsXG4gICAgICB0b29sTmFtZXM6IG5ldyBTZXQoKSwgLy8gRHluYW1pYyAtIGNhdGNoLWFsbCBmb3IgdW5jYXRlZ29yaXplZCB0b29sc1xuICAgIH0sXG4gIH1cbn1cblxuLy8gSGVscGVyIHRvIGdldCBNQ1Agc2VydmVyIGJ1Y2tldHMgZHluYW1pY2FsbHlcbmZ1bmN0aW9uIGdldE1jcFNlcnZlckJ1Y2tldHModG9vbHM6IFRvb2xzKTogQXJyYXk8e1xuICBzZXJ2ZXJOYW1lOiBzdHJpbmdcbiAgdG9vbHM6IFRvb2xzXG59PiB7XG4gIGNvbnN0IHNlcnZlck1hcCA9IG5ldyBNYXA8c3RyaW5nLCBUb29sW10+KClcblxuICB0b29scy5mb3JFYWNoKHRvb2wgPT4ge1xuICAgIGlmIChpc01jcFRvb2wodG9vbCkpIHtcbiAgICAgIGNvbnN0IG1jcEluZm8gPSBtY3BJbmZvRnJvbVN0cmluZyh0b29sLm5hbWUpXG4gICAgICBpZiAobWNwSW5mbz8uc2VydmVyTmFtZSkge1xuICAgICAgICBjb25zdCBleGlzdGluZyA9IHNlcnZlck1hcC5nZXQobWNwSW5mby5zZXJ2ZXJOYW1lKSB8fCBbXVxuICAgICAgICBleGlzdGluZy5wdXNoKHRvb2wpXG4gICAgICAgIHNlcnZlck1hcC5zZXQobWNwSW5mby5zZXJ2ZXJOYW1lLCBleGlzdGluZylcbiAgICAgIH1cbiAgICB9XG4gIH0pXG5cbiAgcmV0dXJuIEFycmF5LmZyb20oc2VydmVyTWFwLmVudHJpZXMoKSlcbiAgICAubWFwKChbc2VydmVyTmFtZSwgdG9vbHNdKSA9PiAoeyBzZXJ2ZXJOYW1lLCB0b29scyB9KSlcbiAgICAuc29ydCgoYSwgYikgPT4gYS5zZXJ2ZXJOYW1lLmxvY2FsZUNvbXBhcmUoYi5zZXJ2ZXJOYW1lKSlcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFRvb2xTZWxlY3Rvcih7XG4gIHRvb2xzLFxuICBpbml0aWFsVG9vbHMsXG4gIG9uQ29tcGxldGUsXG4gIG9uQ2FuY2VsLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBGaWx0ZXIgdG9vbHMgZm9yIGN1c3RvbSBhZ2VudHNcbiAgY29uc3QgY3VzdG9tQWdlbnRUb29scyA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZmlsdGVyVG9vbHNGb3JBZ2VudCh7IHRvb2xzLCBpc0J1aWx0SW46IGZhbHNlLCBpc0FzeW5jOiBmYWxzZSB9KSxcbiAgICBbdG9vbHNdLFxuICApXG5cbiAgLy8gRXhwYW5kIHdpbGRjYXJkIG9yIHVuZGVmaW5lZCB0byBleHBsaWNpdCB0b29sIGxpc3QgZm9yIGludGVybmFsIHN0YXRlXG4gIGNvbnN0IGV4cGFuZGVkSW5pdGlhbFRvb2xzID1cbiAgICAhaW5pdGlhbFRvb2xzIHx8IGluaXRpYWxUb29scy5pbmNsdWRlcygnKicpXG4gICAgICA/IGN1c3RvbUFnZW50VG9vbHMubWFwKHQgPT4gdC5uYW1lKVxuICAgICAgOiBpbml0aWFsVG9vbHNcblxuICBjb25zdCBbc2VsZWN0ZWRUb29scywgc2V0U2VsZWN0ZWRUb29sc10gPVxuICAgIHVzZVN0YXRlPHN0cmluZ1tdPihleHBhbmRlZEluaXRpYWxUb29scylcbiAgY29uc3QgW2ZvY3VzSW5kZXgsIHNldEZvY3VzSW5kZXhdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgW3Nob3dJbmRpdmlkdWFsVG9vbHMsIHNldFNob3dJbmRpdmlkdWFsVG9vbHNdID0gdXNlU3RhdGUoZmFsc2UpXG5cbiAgLy8gRmlsdGVyIHNlbGVjdGVkVG9vbHMgdG8gb25seSBpbmNsdWRlIHRvb2xzIHRoYXQgY3VycmVudGx5IGV4aXN0XG4gIC8vIFRoaXMgaGFuZGxlcyBNQ1AgdG9vbHMgdGhhdCBkaXNjb25uZWN0IHdoaWxlIHNlbGVjdGVkXG4gIGNvbnN0IHZhbGlkU2VsZWN0ZWRUb29scyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGNvbnN0IHRvb2xOYW1lcyA9IG5ldyBTZXQoY3VzdG9tQWdlbnRUb29scy5tYXAodCA9PiB0Lm5hbWUpKVxuICAgIHJldHVybiBzZWxlY3RlZFRvb2xzLmZpbHRlcihuYW1lID0+IHRvb2xOYW1lcy5oYXMobmFtZSkpXG4gIH0sIFtzZWxlY3RlZFRvb2xzLCBjdXN0b21BZ2VudFRvb2xzXSlcblxuICBjb25zdCBzZWxlY3RlZFNldCA9IG5ldyBTZXQodmFsaWRTZWxlY3RlZFRvb2xzKVxuICBjb25zdCBpc0FsbFNlbGVjdGVkID1cbiAgICB2YWxpZFNlbGVjdGVkVG9vbHMubGVuZ3RoID09PSBjdXN0b21BZ2VudFRvb2xzLmxlbmd0aCAmJlxuICAgIGN1c3RvbUFnZW50VG9vbHMubGVuZ3RoID4gMFxuXG4gIGNvbnN0IGhhbmRsZVRvZ2dsZVRvb2wgPSAodG9vbE5hbWU6IHN0cmluZykgPT4ge1xuICAgIGlmICghdG9vbE5hbWUpIHJldHVyblxuXG4gICAgc2V0U2VsZWN0ZWRUb29scyhjdXJyZW50ID0+XG4gICAgICBjdXJyZW50LmluY2x1ZGVzKHRvb2xOYW1lKVxuICAgICAgICA/IGN1cnJlbnQuZmlsdGVyKHQgPT4gdCAhPT0gdG9vbE5hbWUpXG4gICAgICAgIDogWy4uLmN1cnJlbnQsIHRvb2xOYW1lXSxcbiAgICApXG4gIH1cblxuICBjb25zdCBoYW5kbGVUb2dnbGVUb29scyA9ICh0b29sTmFtZXM6IHN0cmluZ1tdLCBzZWxlY3Q6IGJvb2xlYW4pID0+IHtcbiAgICBzZXRTZWxlY3RlZFRvb2xzKGN1cnJlbnQgPT4ge1xuICAgICAgaWYgKHNlbGVjdCkge1xuICAgICAgICBjb25zdCB0b29sc1RvQWRkID0gdG9vbE5hbWVzLmZpbHRlcih0ID0+ICFjdXJyZW50LmluY2x1ZGVzKHQpKVxuICAgICAgICByZXR1cm4gWy4uLmN1cnJlbnQsIC4uLnRvb2xzVG9BZGRdXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gY3VycmVudC5maWx0ZXIodCA9PiAhdG9vbE5hbWVzLmluY2x1ZGVzKHQpKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICBjb25zdCBoYW5kbGVDb25maXJtID0gKCkgPT4ge1xuICAgIC8vIENvbnZlcnQgdG8gdW5kZWZpbmVkIGlmIGFsbCB0b29scyBhcmUgc2VsZWN0ZWQgKGZvciBjbGVhbmVyIGZpbGUgZm9ybWF0KVxuICAgIGNvbnN0IGFsbFRvb2xOYW1lcyA9IGN1c3RvbUFnZW50VG9vbHMubWFwKHQgPT4gdC5uYW1lKVxuICAgIGNvbnN0IGFyZUFsbFRvb2xzU2VsZWN0ZWQgPVxuICAgICAgdmFsaWRTZWxlY3RlZFRvb2xzLmxlbmd0aCA9PT0gYWxsVG9vbE5hbWVzLmxlbmd0aCAmJlxuICAgICAgYWxsVG9vbE5hbWVzLmV2ZXJ5KG5hbWUgPT4gdmFsaWRTZWxlY3RlZFRvb2xzLmluY2x1ZGVzKG5hbWUpKVxuICAgIGNvbnN0IGZpbmFsVG9vbHMgPSBhcmVBbGxUb29sc1NlbGVjdGVkID8gdW5kZWZpbmVkIDogdmFsaWRTZWxlY3RlZFRvb2xzXG5cbiAgICBvbkNvbXBsZXRlKGZpbmFsVG9vbHMpXG4gIH1cblxuICAvLyBHcm91cCB0b29scyBieSBidWNrZXRcbiAgY29uc3QgdG9vbHNCeUJ1Y2tldCA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGNvbnN0IHRvb2xCdWNrZXRzID0gZ2V0VG9vbEJ1Y2tldHMoKVxuICAgIGNvbnN0IGJ1Y2tldHMgPSB7XG4gICAgICByZWFkT25seTogW10gYXMgVG9vbFtdLFxuICAgICAgZWRpdDogW10gYXMgVG9vbFtdLFxuICAgICAgZXhlY3V0aW9uOiBbXSBhcyBUb29sW10sXG4gICAgICBtY3A6IFtdIGFzIFRvb2xbXSxcbiAgICAgIG90aGVyOiBbXSBhcyBUb29sW10sXG4gICAgfVxuXG4gICAgY3VzdG9tQWdlbnRUb29scy5mb3JFYWNoKHRvb2wgPT4ge1xuICAgICAgLy8gQ2hlY2sgaWYgaXQncyBhbiBNQ1AgdG9vbCBmaXJzdFxuICAgICAgaWYgKGlzTWNwVG9vbCh0b29sKSkge1xuICAgICAgICBidWNrZXRzLm1jcC5wdXNoKHRvb2wpXG4gICAgICB9IGVsc2UgaWYgKHRvb2xCdWNrZXRzLlJFQURfT05MWS50b29sTmFtZXMuaGFzKHRvb2wubmFtZSkpIHtcbiAgICAgICAgYnVja2V0cy5yZWFkT25seS5wdXNoKHRvb2wpXG4gICAgICB9IGVsc2UgaWYgKHRvb2xCdWNrZXRzLkVESVQudG9vbE5hbWVzLmhhcyh0b29sLm5hbWUpKSB7XG4gICAgICAgIGJ1Y2tldHMuZWRpdC5wdXNoKHRvb2wpXG4gICAgICB9IGVsc2UgaWYgKHRvb2xCdWNrZXRzLkVYRUNVVElPTi50b29sTmFtZXMuaGFzKHRvb2wubmFtZSkpIHtcbiAgICAgICAgYnVja2V0cy5leGVjdXRpb24ucHVzaCh0b29sKVxuICAgICAgfSBlbHNlIGlmICh0b29sLm5hbWUgIT09IEFHRU5UX1RPT0xfTkFNRSkge1xuICAgICAgICAvLyBDYXRjaC1hbGwgZm9yIHVuY2F0ZWdvcml6ZWQgdG9vbHMgKGV4Y2VwdCBUYXNrKVxuICAgICAgICBidWNrZXRzLm90aGVyLnB1c2godG9vbClcbiAgICAgIH1cbiAgICB9KVxuXG4gICAgcmV0dXJuIGJ1Y2tldHNcbiAgfSwgW2N1c3RvbUFnZW50VG9vbHNdKVxuXG4gIGNvbnN0IGNyZWF0ZUJ1Y2tldFRvZ2dsZUFjdGlvbiA9IChidWNrZXRUb29sczogVG9vbFtdKSA9PiB7XG4gICAgY29uc3Qgc2VsZWN0ZWQgPSBjb3VudChidWNrZXRUb29scywgdCA9PiBzZWxlY3RlZFNldC5oYXModC5uYW1lKSlcbiAgICBjb25zdCBuZWVkc1NlbGVjdGlvbiA9IHNlbGVjdGVkIDwgYnVja2V0VG9vbHMubGVuZ3RoXG5cbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgY29uc3QgdG9vbE5hbWVzID0gYnVja2V0VG9vbHMubWFwKHQgPT4gdC5uYW1lKVxuICAgICAgaGFuZGxlVG9nZ2xlVG9vbHModG9vbE5hbWVzLCBuZWVkc1NlbGVjdGlvbilcbiAgICB9XG4gIH1cblxuICAvLyBCdWlsZCBuYXZpZ2FibGUgaXRlbXMgKG5vIHNlcGFyYXRvcnMpXG4gIGNvbnN0IG5hdmlnYWJsZUl0ZW1zOiBBcnJheTx7XG4gICAgaWQ6IHN0cmluZ1xuICAgIGxhYmVsOiBzdHJpbmdcbiAgICBhY3Rpb246ICgpID0+IHZvaWRcbiAgICBpc0NvbnRpbnVlPzogYm9vbGVhblxuICAgIGlzVG9nZ2xlPzogYm9vbGVhblxuICAgIGlzSGVhZGVyPzogYm9vbGVhblxuICB9PiA9IFtdXG5cbiAgLy8gQ29udGludWUgYnV0dG9uXG4gIG5hdmlnYWJsZUl0ZW1zLnB1c2goe1xuICAgIGlkOiAnY29udGludWUnLFxuICAgIGxhYmVsOiAnQ29udGludWUnLFxuICAgIGFjdGlvbjogaGFuZGxlQ29uZmlybSxcbiAgICBpc0NvbnRpbnVlOiB0cnVlLFxuICB9KVxuXG4gIC8vIEFsbCB0b29sc1xuICBuYXZpZ2FibGVJdGVtcy5wdXNoKHtcbiAgICBpZDogJ2J1Y2tldC1hbGwnLFxuICAgIGxhYmVsOiBgJHtpc0FsbFNlbGVjdGVkID8gZmlndXJlcy5jaGVja2JveE9uIDogZmlndXJlcy5jaGVja2JveE9mZn0gQWxsIHRvb2xzYCxcbiAgICBhY3Rpb246ICgpID0+IHtcbiAgICAgIGNvbnN0IGFsbFRvb2xOYW1lcyA9IGN1c3RvbUFnZW50VG9vbHMubWFwKHQgPT4gdC5uYW1lKVxuICAgICAgaGFuZGxlVG9nZ2xlVG9vbHMoYWxsVG9vbE5hbWVzLCAhaXNBbGxTZWxlY3RlZClcbiAgICB9LFxuICB9KVxuXG4gIC8vIENyZWF0ZSBidWNrZXQgbWVudSBpdGVtc1xuICBjb25zdCB0b29sQnVja2V0cyA9IGdldFRvb2xCdWNrZXRzKClcbiAgY29uc3QgYnVja2V0Q29uZmlncyA9IFtcbiAgICB7XG4gICAgICBpZDogJ2J1Y2tldC1yZWFkb25seScsXG4gICAgICBuYW1lOiB0b29sQnVja2V0cy5SRUFEX09OTFkubmFtZSxcbiAgICAgIHRvb2xzOiB0b29sc0J5QnVja2V0LnJlYWRPbmx5LFxuICAgIH0sXG4gICAge1xuICAgICAgaWQ6ICdidWNrZXQtZWRpdCcsXG4gICAgICBuYW1lOiB0b29sQnVja2V0cy5FRElULm5hbWUsXG4gICAgICB0b29sczogdG9vbHNCeUJ1Y2tldC5lZGl0LFxuICAgIH0sXG4gICAge1xuICAgICAgaWQ6ICdidWNrZXQtZXhlY3V0aW9uJyxcbiAgICAgIG5hbWU6IHRvb2xCdWNrZXRzLkVYRUNVVElPTi5uYW1lLFxuICAgICAgdG9vbHM6IHRvb2xzQnlCdWNrZXQuZXhlY3V0aW9uLFxuICAgIH0sXG4gICAge1xuICAgICAgaWQ6ICdidWNrZXQtbWNwJyxcbiAgICAgIG5hbWU6IHRvb2xCdWNrZXRzLk1DUC5uYW1lLFxuICAgICAgdG9vbHM6IHRvb2xzQnlCdWNrZXQubWNwLFxuICAgIH0sXG4gICAge1xuICAgICAgaWQ6ICdidWNrZXQtb3RoZXInLFxuICAgICAgbmFtZTogdG9vbEJ1Y2tldHMuT1RIRVIubmFtZSxcbiAgICAgIHRvb2xzOiB0b29sc0J5QnVja2V0Lm90aGVyLFxuICAgIH0sXG4gIF1cblxuICBidWNrZXRDb25maWdzLmZvckVhY2goKHsgaWQsIG5hbWUsIHRvb2xzOiBidWNrZXRUb29scyB9KSA9PiB7XG4gICAgaWYgKGJ1Y2tldFRvb2xzLmxlbmd0aCA9PT0gMCkgcmV0dXJuXG5cbiAgICBjb25zdCBzZWxlY3RlZCA9IGNvdW50KGJ1Y2tldFRvb2xzLCB0ID0+IHNlbGVjdGVkU2V0Lmhhcyh0Lm5hbWUpKVxuICAgIGNvbnN0IGlzRnVsbHlTZWxlY3RlZCA9IHNlbGVjdGVkID09PSBidWNrZXRUb29scy5sZW5ndGhcblxuICAgIG5hdmlnYWJsZUl0ZW1zLnB1c2goe1xuICAgICAgaWQsXG4gICAgICBsYWJlbDogYCR7aXNGdWxseVNlbGVjdGVkID8gZmlndXJlcy5jaGVja2JveE9uIDogZmlndXJlcy5jaGVja2JveE9mZn0gJHtuYW1lfWAsXG4gICAgICBhY3Rpb246IGNyZWF0ZUJ1Y2tldFRvZ2dsZUFjdGlvbihidWNrZXRUb29scyksXG4gICAgfSlcbiAgfSlcblxuICAvLyBUb2dnbGUgYnV0dG9uIGZvciBpbmRpdmlkdWFsIHRvb2xzXG4gIGNvbnN0IHRvZ2dsZUJ1dHRvbkluZGV4ID0gbmF2aWdhYmxlSXRlbXMubGVuZ3RoXG4gIG5hdmlnYWJsZUl0ZW1zLnB1c2goe1xuICAgIGlkOiAndG9nZ2xlLWluZGl2aWR1YWwnLFxuICAgIGxhYmVsOiBzaG93SW5kaXZpZHVhbFRvb2xzXG4gICAgICA/ICdIaWRlIGFkdmFuY2VkIG9wdGlvbnMnXG4gICAgICA6ICdTaG93IGFkdmFuY2VkIG9wdGlvbnMnLFxuICAgIGFjdGlvbjogKCkgPT4ge1xuICAgICAgc2V0U2hvd0luZGl2aWR1YWxUb29scyghc2hvd0luZGl2aWR1YWxUb29scylcbiAgICAgIC8vIElmIGhpZGluZyB0b29scyBhbmQgZm9jdXMgaXMgb24gYW4gaW5kaXZpZHVhbCB0b29sLCBtb3ZlIGZvY3VzIHRvIHRvZ2dsZSBidXR0b25cbiAgICAgIGlmIChzaG93SW5kaXZpZHVhbFRvb2xzICYmIGZvY3VzSW5kZXggPiB0b2dnbGVCdXR0b25JbmRleCkge1xuICAgICAgICBzZXRGb2N1c0luZGV4KHRvZ2dsZUJ1dHRvbkluZGV4KVxuICAgICAgfVxuICAgIH0sXG4gICAgaXNUb2dnbGU6IHRydWUsXG4gIH0pXG5cbiAgLy8gTWVtb2l6ZSBNQ1Agc2VydmVyIGJ1Y2tldHMgKG11c3QgYmUgb3V0c2lkZSBjb25kaXRpb25hbCBmb3IgaG9va3MgcnVsZXMpXG4gIGNvbnN0IG1jcFNlcnZlckJ1Y2tldHMgPSB1c2VNZW1vKFxuICAgICgpID0+IGdldE1jcFNlcnZlckJ1Y2tldHMoY3VzdG9tQWdlbnRUb29scyksXG4gICAgW2N1c3RvbUFnZW50VG9vbHNdLFxuICApXG5cbiAgLy8gSW5kaXZpZHVhbCB0b29scyAob25seSBpZiBleHBhbmRlZClcbiAgaWYgKHNob3dJbmRpdmlkdWFsVG9vbHMpIHtcbiAgICAvLyBBZGQgTUNQIHNlcnZlciBidWNrZXRzIGlmIGFueSBleGlzdFxuICAgIGlmIChtY3BTZXJ2ZXJCdWNrZXRzLmxlbmd0aCA+IDApIHtcbiAgICAgIG5hdmlnYWJsZUl0ZW1zLnB1c2goe1xuICAgICAgICBpZDogJ21jcC1zZXJ2ZXJzLWhlYWRlcicsXG4gICAgICAgIGxhYmVsOiAnTUNQIFNlcnZlcnM6JyxcbiAgICAgICAgYWN0aW9uOiAoKSA9PiB7fSwgLy8gTm8gYWN0aW9uIC0ganVzdCBhIGhlYWRlclxuICAgICAgICBpc0hlYWRlcjogdHJ1ZSxcbiAgICAgIH0pXG5cbiAgICAgIG1jcFNlcnZlckJ1Y2tldHMuZm9yRWFjaCgoeyBzZXJ2ZXJOYW1lLCB0b29sczogc2VydmVyVG9vbHMgfSkgPT4ge1xuICAgICAgICBjb25zdCBzZWxlY3RlZCA9IGNvdW50KHNlcnZlclRvb2xzLCB0ID0+IHNlbGVjdGVkU2V0Lmhhcyh0Lm5hbWUpKVxuICAgICAgICBjb25zdCBpc0Z1bGx5U2VsZWN0ZWQgPSBzZWxlY3RlZCA9PT0gc2VydmVyVG9vbHMubGVuZ3RoXG5cbiAgICAgICAgbmF2aWdhYmxlSXRlbXMucHVzaCh7XG4gICAgICAgICAgaWQ6IGBtY3Atc2VydmVyLSR7c2VydmVyTmFtZX1gLFxuICAgICAgICAgIGxhYmVsOiBgJHtpc0Z1bGx5U2VsZWN0ZWQgPyBmaWd1cmVzLmNoZWNrYm94T24gOiBmaWd1cmVzLmNoZWNrYm94T2ZmfSAke3NlcnZlck5hbWV9ICgke3NlcnZlclRvb2xzLmxlbmd0aH0gJHtwbHVyYWwoc2VydmVyVG9vbHMubGVuZ3RoLCAndG9vbCcpfSlgLFxuICAgICAgICAgIGFjdGlvbjogKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdG9vbE5hbWVzID0gc2VydmVyVG9vbHMubWFwKHQgPT4gdC5uYW1lKVxuICAgICAgICAgICAgaGFuZGxlVG9nZ2xlVG9vbHModG9vbE5hbWVzLCAhaXNGdWxseVNlbGVjdGVkKVxuICAgICAgICAgIH0sXG4gICAgICAgIH0pXG4gICAgICB9KVxuXG4gICAgICAvLyBBZGQgc2VwYXJhdG9yIGhlYWRlciBiZWZvcmUgaW5kaXZpZHVhbCB0b29sc1xuICAgICAgbmF2aWdhYmxlSXRlbXMucHVzaCh7XG4gICAgICAgIGlkOiAndG9vbHMtaGVhZGVyJyxcbiAgICAgICAgbGFiZWw6ICdJbmRpdmlkdWFsIFRvb2xzOicsXG4gICAgICAgIGFjdGlvbjogKCkgPT4ge30sXG4gICAgICAgIGlzSGVhZGVyOiB0cnVlLFxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBBZGQgaW5kaXZpZHVhbCB0b29sc1xuICAgIGN1c3RvbUFnZW50VG9vbHMuZm9yRWFjaCh0b29sID0+IHtcbiAgICAgIGxldCBkaXNwbGF5TmFtZSA9IHRvb2wubmFtZVxuICAgICAgaWYgKHRvb2wubmFtZS5zdGFydHNXaXRoKCdtY3BfXycpKSB7XG4gICAgICAgIGNvbnN0IG1jcEluZm8gPSBtY3BJbmZvRnJvbVN0cmluZyh0b29sLm5hbWUpXG4gICAgICAgIGRpc3BsYXlOYW1lID0gbWNwSW5mb1xuICAgICAgICAgID8gYCR7bWNwSW5mby50b29sTmFtZX0gKCR7bWNwSW5mby5zZXJ2ZXJOYW1lfSlgXG4gICAgICAgICAgOiB0b29sLm5hbWVcbiAgICAgIH1cblxuICAgICAgbmF2aWdhYmxlSXRlbXMucHVzaCh7XG4gICAgICAgIGlkOiBgdG9vbC0ke3Rvb2wubmFtZX1gLFxuICAgICAgICBsYWJlbDogYCR7c2VsZWN0ZWRTZXQuaGFzKHRvb2wubmFtZSkgPyBmaWd1cmVzLmNoZWNrYm94T24gOiBmaWd1cmVzLmNoZWNrYm94T2ZmfSAke2Rpc3BsYXlOYW1lfWAsXG4gICAgICAgIGFjdGlvbjogKCkgPT4gaGFuZGxlVG9nZ2xlVG9vbCh0b29sLm5hbWUpLFxuICAgICAgfSlcbiAgICB9KVxuICB9XG5cbiAgY29uc3QgaGFuZGxlQ2FuY2VsID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChvbkNhbmNlbCkge1xuICAgICAgb25DYW5jZWwoKVxuICAgIH0gZWxzZSB7XG4gICAgICBvbkNvbXBsZXRlKGluaXRpYWxUb29scylcbiAgICB9XG4gIH0sIFtvbkNhbmNlbCwgb25Db21wbGV0ZSwgaW5pdGlhbFRvb2xzXSlcblxuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOm5vJywgaGFuZGxlQ2FuY2VsLCB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nIH0pXG5cbiAgY29uc3QgaGFuZGxlS2V5RG93biA9IChlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgaWYgKGUua2V5ID09PSAncmV0dXJuJykge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBjb25zdCBpdGVtID0gbmF2aWdhYmxlSXRlbXNbZm9jdXNJbmRleF1cbiAgICAgIGlmIChpdGVtICYmICFpdGVtLmlzSGVhZGVyKSB7XG4gICAgICAgIGl0ZW0uYWN0aW9uKClcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGUua2V5ID09PSAndXAnKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIGxldCBuZXdJbmRleCA9IGZvY3VzSW5kZXggLSAxXG4gICAgICAvLyBTa2lwIGhlYWRlcnMgd2hlbiBuYXZpZ2F0aW5nIHVwXG4gICAgICB3aGlsZSAobmV3SW5kZXggPiAwICYmIG5hdmlnYWJsZUl0ZW1zW25ld0luZGV4XT8uaXNIZWFkZXIpIHtcbiAgICAgICAgbmV3SW5kZXgtLVxuICAgICAgfVxuICAgICAgc2V0Rm9jdXNJbmRleChNYXRoLm1heCgwLCBuZXdJbmRleCkpXG4gICAgfSBlbHNlIGlmIChlLmtleSA9PT0gJ2Rvd24nKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIGxldCBuZXdJbmRleCA9IGZvY3VzSW5kZXggKyAxXG4gICAgICAvLyBTa2lwIGhlYWRlcnMgd2hlbiBuYXZpZ2F0aW5nIGRvd25cbiAgICAgIHdoaWxlIChcbiAgICAgICAgbmV3SW5kZXggPCBuYXZpZ2FibGVJdGVtcy5sZW5ndGggLSAxICYmXG4gICAgICAgIG5hdmlnYWJsZUl0ZW1zW25ld0luZGV4XT8uaXNIZWFkZXJcbiAgICAgICkge1xuICAgICAgICBuZXdJbmRleCsrXG4gICAgICB9XG4gICAgICBzZXRGb2N1c0luZGV4KE1hdGgubWluKG5hdmlnYWJsZUl0ZW1zLmxlbmd0aCAtIDEsIG5ld0luZGV4KSlcbiAgICB9XG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgbWFyZ2luVG9wPXsxfVxuICAgICAgdGFiSW5kZXg9ezB9XG4gICAgICBhdXRvRm9jdXNcbiAgICAgIG9uS2V5RG93bj17aGFuZGxlS2V5RG93bn1cbiAgICA+XG4gICAgICB7LyogUmVuZGVyIENvbnRpbnVlIGJ1dHRvbiAqL31cbiAgICAgIDxUZXh0XG4gICAgICAgIGNvbG9yPXtmb2N1c0luZGV4ID09PSAwID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfVxuICAgICAgICBib2xkPXtmb2N1c0luZGV4ID09PSAwfVxuICAgICAgPlxuICAgICAgICB7Zm9jdXNJbmRleCA9PT0gMCA/IGAke2ZpZ3VyZXMucG9pbnRlcn0gYCA6ICcgICd9WyBDb250aW51ZSBdXG4gICAgICA8L1RleHQ+XG5cbiAgICAgIHsvKiBTZXBhcmF0b3IgKi99XG4gICAgICA8RGl2aWRlciB3aWR0aD17NDB9IC8+XG5cbiAgICAgIHsvKiBSZW5kZXIgYWxsIG5hdmlnYWJsZSBpdGVtcyBleGNlcHQgQ29udGludWUgKHdoaWNoIGlzIGF0IGluZGV4IDApICovfVxuICAgICAge25hdmlnYWJsZUl0ZW1zLnNsaWNlKDEpLm1hcCgoaXRlbSwgaW5kZXgpID0+IHtcbiAgICAgICAgY29uc3QgaXNDdXJyZW50bHlGb2N1c2VkID0gaW5kZXggKyAxID09PSBmb2N1c0luZGV4XG4gICAgICAgIGNvbnN0IGlzVG9nZ2xlQnV0dG9uID0gaXRlbS5pc1RvZ2dsZVxuICAgICAgICBjb25zdCBpc0hlYWRlciA9IGl0ZW0uaXNIZWFkZXJcblxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIDxSZWFjdC5GcmFnbWVudCBrZXk9e2l0ZW0uaWR9PlxuICAgICAgICAgICAgey8qIEFkZCBzZXBhcmF0b3IgYmVmb3JlIHRvZ2dsZSBidXR0b24gKi99XG4gICAgICAgICAgICB7aXNUb2dnbGVCdXR0b24gJiYgPERpdmlkZXIgd2lkdGg9ezQwfSAvPn1cblxuICAgICAgICAgICAgey8qIEFkZCBtYXJnaW4gYmVmb3JlIGhlYWRlcnMgKi99XG4gICAgICAgICAgICB7aXNIZWFkZXIgJiYgaW5kZXggPiAwICYmIDxCb3ggbWFyZ2luVG9wPXsxfSAvPn1cblxuICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgY29sb3I9e1xuICAgICAgICAgICAgICAgIGlzSGVhZGVyXG4gICAgICAgICAgICAgICAgICA/IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgOiBpc0N1cnJlbnRseUZvY3VzZWRcbiAgICAgICAgICAgICAgICAgICAgPyAnc3VnZ2VzdGlvbidcbiAgICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBkaW1Db2xvcj17aXNIZWFkZXJ9XG4gICAgICAgICAgICAgIGJvbGQ9e2lzVG9nZ2xlQnV0dG9uICYmIGlzQ3VycmVudGx5Rm9jdXNlZH1cbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAge2lzSGVhZGVyXG4gICAgICAgICAgICAgICAgPyAnJ1xuICAgICAgICAgICAgICAgIDogaXNDdXJyZW50bHlGb2N1c2VkXG4gICAgICAgICAgICAgICAgICA/IGAke2ZpZ3VyZXMucG9pbnRlcn0gYFxuICAgICAgICAgICAgICAgICAgOiAnICAnfVxuICAgICAgICAgICAgICB7aXNUb2dnbGVCdXR0b24gPyBgWyAke2l0ZW0ubGFiZWx9IF1gIDogaXRlbS5sYWJlbH1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L1JlYWN0LkZyYWdtZW50PlxuICAgICAgICApXG4gICAgICB9KX1cblxuICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAge2lzQWxsU2VsZWN0ZWRcbiAgICAgICAgICAgID8gJ0FsbCB0b29scyBzZWxlY3RlZCdcbiAgICAgICAgICAgIDogYCR7c2VsZWN0ZWRTZXQuc2l6ZX0gb2YgJHtjdXN0b21BZ2VudFRvb2xzLmxlbmd0aH0gdG9vbHMgc2VsZWN0ZWRgfVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBT0MsS0FBSyxJQUFJQyxXQUFXLEVBQUVDLE9BQU8sRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDN0QsU0FBU0MsaUJBQWlCLFFBQVEsb0NBQW9DO0FBQ3RFLFNBQVNDLFNBQVMsUUFBUSwyQkFBMkI7QUFDckQsY0FBY0MsSUFBSSxFQUFFQyxLQUFLLFFBQVEsYUFBYTtBQUM5QyxTQUFTQyxtQkFBbUIsUUFBUSx1Q0FBdUM7QUFDM0UsU0FBU0MsZUFBZSxRQUFRLGtDQUFrQztBQUNsRSxTQUFTQyxRQUFRLFFBQVEsZ0NBQWdDO0FBQ3pELFNBQVNDLGtCQUFrQixRQUFRLGtEQUFrRDtBQUNyRixTQUFTQyxZQUFZLFFBQVEsd0NBQXdDO0FBQ3JFLFNBQVNDLFlBQVksUUFBUSx3Q0FBd0M7QUFDckUsU0FBU0MsYUFBYSxRQUFRLDBDQUEwQztBQUN4RSxTQUFTQyxRQUFRLFFBQVEsZ0NBQWdDO0FBQ3pELFNBQVNDLFFBQVEsUUFBUSxnQ0FBZ0M7QUFDekQsU0FBU0Msb0JBQW9CLFFBQVEsd0RBQXdEO0FBQzdG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 \ No newline at end of file diff --git a/components/agents/agentFileUtils.ts b/components/agents/agentFileUtils.ts new file mode 100644 index 0000000..87e4e4b --- /dev/null +++ b/components/agents/agentFileUtils.ts @@ -0,0 +1,272 @@ +import { mkdir, open, unlink } from 'fs/promises' +import { join } from 'path' +import type { SettingSource } from 'src/utils/settings/constants.js' +import { getManagedFilePath } from 'src/utils/settings/managedPath.js' +import type { AgentMemoryScope } from '../../tools/AgentTool/agentMemory.js' +import { + type AgentDefinition, + isBuiltInAgent, + isPluginAgent, +} from '../../tools/AgentTool/loadAgentsDir.js' +import { getCwd } from '../../utils/cwd.js' +import type { EffortValue } from '../../utils/effort.js' +import { getClaudeConfigHomeDir } from '../../utils/envUtils.js' +import { getErrnoCode } from '../../utils/errors.js' +import { AGENT_PATHS } from './types.js' + +/** + * Formats agent data as markdown file content + */ +export function formatAgentAsMarkdown( + agentType: string, + whenToUse: string, + tools: string[] | undefined, + systemPrompt: string, + color?: string, + model?: string, + memory?: AgentMemoryScope, + effort?: EffortValue, +): string { + // For YAML double-quoted strings, we need to escape: + // - Backslashes: \ -> \\ + // - Double quotes: " -> \" + // - Newlines: \n -> \\n (so yaml reads it as literal backslash-n, not newline) + const escapedWhenToUse = whenToUse + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/"/g, '\\"') // Escape double quotes + .replace(/\n/g, '\\\\n') // Escape newlines as \\n so yaml preserves them as \n + + // Omit tools field entirely when tools is undefined or ['*'] (all tools allowed) + const isAllTools = + tools === undefined || (tools.length === 1 && tools[0] === '*') + const toolsLine = isAllTools ? '' : `\ntools: ${tools.join(', ')}` + const modelLine = model ? `\nmodel: ${model}` : '' + const effortLine = effort !== undefined ? `\neffort: ${effort}` : '' + const colorLine = color ? `\ncolor: ${color}` : '' + const memoryLine = memory ? `\nmemory: ${memory}` : '' + + return `--- +name: ${agentType} +description: "${escapedWhenToUse}"${toolsLine}${modelLine}${effortLine}${colorLine}${memoryLine} +--- + +${systemPrompt} +` +} + +/** + * Gets the directory path for an agent location + */ +function getAgentDirectoryPath(location: SettingSource): string { + switch (location) { + case 'flagSettings': + throw new Error(`Cannot get directory path for ${location} agents`) + case 'userSettings': + return join(getClaudeConfigHomeDir(), AGENT_PATHS.AGENTS_DIR) + case 'projectSettings': + return join(getCwd(), AGENT_PATHS.FOLDER_NAME, AGENT_PATHS.AGENTS_DIR) + case 'policySettings': + return join( + getManagedFilePath(), + AGENT_PATHS.FOLDER_NAME, + AGENT_PATHS.AGENTS_DIR, + ) + case 'localSettings': + return join(getCwd(), AGENT_PATHS.FOLDER_NAME, AGENT_PATHS.AGENTS_DIR) + } +} + +function getRelativeAgentDirectoryPath(location: SettingSource): string { + switch (location) { + case 'projectSettings': + return join('.', AGENT_PATHS.FOLDER_NAME, AGENT_PATHS.AGENTS_DIR) + default: + return getAgentDirectoryPath(location) + } +} + +/** + * Gets the file path for a new agent based on its name + * Used when creating new agent files + */ +export function getNewAgentFilePath(agent: { + source: SettingSource + agentType: string +}): string { + const dirPath = getAgentDirectoryPath(agent.source) + return join(dirPath, `${agent.agentType}.md`) +} + +/** + * Gets the actual file path for an agent (handles filename vs agentType mismatch) + * Always use this for existing agents to get their real file location + */ +export function getActualAgentFilePath(agent: AgentDefinition): string { + if (agent.source === 'built-in') { + return 'Built-in' + } + if (agent.source === 'plugin') { + throw new Error('Cannot get file path for plugin agents') + } + + const dirPath = getAgentDirectoryPath(agent.source) + const filename = agent.filename || agent.agentType + return join(dirPath, `${filename}.md`) +} + +/** + * Gets the relative file path for a new agent based on its name + * Used for displaying where new agent files will be created + */ +export function getNewRelativeAgentFilePath(agent: { + source: SettingSource | 'built-in' + agentType: string +}): string { + if (agent.source === 'built-in') { + return 'Built-in' + } + const dirPath = getRelativeAgentDirectoryPath(agent.source) + return join(dirPath, `${agent.agentType}.md`) +} + +/** + * Gets the actual relative file path for an agent (handles filename vs agentType mismatch) + */ +export function getActualRelativeAgentFilePath(agent: AgentDefinition): string { + if (isBuiltInAgent(agent)) { + return 'Built-in' + } + if (isPluginAgent(agent)) { + return `Plugin: ${agent.plugin || 'Unknown'}` + } + if (agent.source === 'flagSettings') { + return 'CLI argument' + } + + const dirPath = getRelativeAgentDirectoryPath(agent.source) + const filename = agent.filename || agent.agentType + return join(dirPath, `${filename}.md`) +} + +/** + * Ensures the directory for an agent location exists + */ +async function ensureAgentDirectoryExists( + source: SettingSource, +): Promise { + const dirPath = getAgentDirectoryPath(source) + await mkdir(dirPath, { recursive: true }) + return dirPath +} + +/** + * Saves an agent to the filesystem + * @param checkExists - If true, throws error if file already exists + */ +export async function saveAgentToFile( + source: SettingSource | 'built-in', + agentType: string, + whenToUse: string, + tools: string[] | undefined, + systemPrompt: string, + checkExists = true, + color?: string, + model?: string, + memory?: AgentMemoryScope, + effort?: EffortValue, +): Promise { + if (source === 'built-in') { + throw new Error('Cannot save built-in agents') + } + + await ensureAgentDirectoryExists(source) + const filePath = getNewAgentFilePath({ source, agentType }) + + const content = formatAgentAsMarkdown( + agentType, + whenToUse, + tools, + systemPrompt, + color, + model, + memory, + effort, + ) + try { + await writeFileAndFlush(filePath, content, checkExists ? 'wx' : 'w') + } catch (e: unknown) { + if (getErrnoCode(e) === 'EEXIST') { + throw new Error(`Agent file already exists: ${filePath}`) + } + throw e + } +} + +/** + * Updates an existing agent file + */ +export async function updateAgentFile( + agent: AgentDefinition, + newWhenToUse: string, + newTools: string[] | undefined, + newSystemPrompt: string, + newColor?: string, + newModel?: string, + newMemory?: AgentMemoryScope, + newEffort?: EffortValue, +): Promise { + if (agent.source === 'built-in') { + throw new Error('Cannot update built-in agents') + } + + const filePath = getActualAgentFilePath(agent) + + const content = formatAgentAsMarkdown( + agent.agentType, + newWhenToUse, + newTools, + newSystemPrompt, + newColor, + newModel, + newMemory, + newEffort, + ) + + await writeFileAndFlush(filePath, content) +} + +/** + * Deletes an agent file + */ +export async function deleteAgentFromFile( + agent: AgentDefinition, +): Promise { + if (agent.source === 'built-in') { + throw new Error('Cannot delete built-in agents') + } + + const filePath = getActualAgentFilePath(agent) + + try { + await unlink(filePath) + } catch (e: unknown) { + const code = getErrnoCode(e) + if (code !== 'ENOENT') { + throw e + } + } +} + +async function writeFileAndFlush( + filePath: string, + content: string, + flag: 'w' | 'wx' = 'w', +): Promise { + const handle = await open(filePath, flag) + try { + await handle.writeFile(content, { encoding: 'utf-8' }) + await handle.datasync() + } finally { + await handle.close() + } +} diff --git a/components/agents/generateAgent.ts b/components/agents/generateAgent.ts new file mode 100644 index 0000000..04fd624 --- /dev/null +++ b/components/agents/generateAgent.ts @@ -0,0 +1,197 @@ +import type { ContentBlock } from '@anthropic-ai/sdk/resources/index.mjs' +import { getUserContext } from 'src/context.js' +import { queryModelWithoutStreaming } from 'src/services/api/claude.js' +import { getEmptyToolPermissionContext } from 'src/Tool.js' +import { AGENT_TOOL_NAME } from 'src/tools/AgentTool/constants.js' +import { prependUserContext } from 'src/utils/api.js' +import { + createUserMessage, + normalizeMessagesForAPI, +} from 'src/utils/messages.js' +import type { ModelName } from 'src/utils/model/model.js' +import { isAutoMemoryEnabled } from '../../memdir/paths.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../../services/analytics/index.js' +import { jsonParse } from '../../utils/slowOperations.js' +import { asSystemPrompt } from '../../utils/systemPromptType.js' + +type GeneratedAgent = { + identifier: string + whenToUse: string + systemPrompt: string +} + +const AGENT_CREATION_SYSTEM_PROMPT = `You are an elite AI agent architect specializing in crafting high-performance agent configurations. Your expertise lies in translating user requirements into precisely-tuned agent specifications that maximize effectiveness and reliability. + +**Important Context**: You may have access to project-specific instructions from CLAUDE.md files and other context that may include coding standards, project structure, and custom requirements. Consider this context when creating agents to ensure they align with the project's established patterns and practices. + +When a user describes what they want an agent to do, you will: + +1. **Extract Core Intent**: Identify the fundamental purpose, key responsibilities, and success criteria for the agent. Look for both explicit requirements and implicit needs. Consider any project-specific context from CLAUDE.md files. For agents that are meant to review code, you should assume that the user is asking to review recently written code and not the whole codebase, unless the user has explicitly instructed you otherwise. + +2. **Design Expert Persona**: Create a compelling expert identity that embodies deep domain knowledge relevant to the task. The persona should inspire confidence and guide the agent's decision-making approach. + +3. **Architect Comprehensive Instructions**: Develop a system prompt that: + - Establishes clear behavioral boundaries and operational parameters + - Provides specific methodologies and best practices for task execution + - Anticipates edge cases and provides guidance for handling them + - Incorporates any specific requirements or preferences mentioned by the user + - Defines output format expectations when relevant + - Aligns with project-specific coding standards and patterns from CLAUDE.md + +4. **Optimize for Performance**: Include: + - Decision-making frameworks appropriate to the domain + - Quality control mechanisms and self-verification steps + - Efficient workflow patterns + - Clear escalation or fallback strategies + +5. **Create Identifier**: Design a concise, descriptive identifier that: + - Uses lowercase letters, numbers, and hyphens only + - Is typically 2-4 words joined by hyphens + - Clearly indicates the agent's primary function + - Is memorable and easy to type + - Avoids generic terms like "helper" or "assistant" + +6 **Example agent descriptions**: + - in the 'whenToUse' field of the JSON object, you should include examples of when this agent should be used. + - examples should be of the form: + - + Context: The user is creating a test-runner agent that should be called after a logical chunk of code is written. + user: "Please write a function that checks if a number is prime" + assistant: "Here is the relevant function: " + + + Since a significant piece of code was written, use the ${AGENT_TOOL_NAME} tool to launch the test-runner agent to run the tests. + + assistant: "Now let me use the test-runner agent to run the tests" + + - + Context: User is creating an agent to respond to the word "hello" with a friendly jok. + user: "Hello" + assistant: "I'm going to use the ${AGENT_TOOL_NAME} tool to launch the greeting-responder agent to respond with a friendly joke" + + Since the user is greeting, use the greeting-responder agent to respond with a friendly joke. + + + - If the user mentioned or implied that the agent should be used proactively, you should include examples of this. +- NOTE: Ensure that in the examples, you are making the assistant use the Agent tool and not simply respond directly to the task. + +Your output must be a valid JSON object with exactly these fields: +{ + "identifier": "A unique, descriptive identifier using lowercase letters, numbers, and hyphens (e.g., 'test-runner', 'api-docs-writer', 'code-formatter')", + "whenToUse": "A precise, actionable description starting with 'Use this agent when...' that clearly defines the triggering conditions and use cases. Ensure you include examples as described above.", + "systemPrompt": "The complete system prompt that will govern the agent's behavior, written in second person ('You are...', 'You will...') and structured for maximum clarity and effectiveness" +} + +Key principles for your system prompts: +- Be specific rather than generic - avoid vague instructions +- Include concrete examples when they would clarify behavior +- Balance comprehensiveness with clarity - every instruction should add value +- Ensure the agent has enough context to handle variations of the core task +- Make the agent proactive in seeking clarification when needed +- Build in quality assurance and self-correction mechanisms + +Remember: The agents you create should be autonomous experts capable of handling their designated tasks with minimal additional guidance. Your system prompts are their complete operational manual. +` + +// Agent memory instructions to include in the system prompt when memory is mentioned or relevant +const AGENT_MEMORY_INSTRUCTIONS = ` + +7. **Agent Memory Instructions**: If the user mentions "memory", "remember", "learn", "persist", or similar concepts, OR if the agent would benefit from building up knowledge across conversations (e.g., code reviewers learning patterns, architects learning codebase structure, etc.), include domain-specific memory update instructions in the systemPrompt. + + Add a section like this to the systemPrompt, tailored to the agent's specific domain: + + "**Update your agent memory** as you discover [domain-specific items]. This builds up institutional knowledge across conversations. Write concise notes about what you found and where. + + Examples of what to record: + - [domain-specific item 1] + - [domain-specific item 2] + - [domain-specific item 3]" + + Examples of domain-specific memory instructions: + - For a code-reviewer: "Update your agent memory as you discover code patterns, style conventions, common issues, and architectural decisions in this codebase." + - For a test-runner: "Update your agent memory as you discover test patterns, common failure modes, flaky tests, and testing best practices." + - For an architect: "Update your agent memory as you discover codepaths, library locations, key architectural decisions, and component relationships." + - For a documentation writer: "Update your agent memory as you discover documentation patterns, API structures, and terminology conventions." + + The memory instructions should be specific to what the agent would naturally learn while performing its core tasks. +` + +export async function generateAgent( + userPrompt: string, + model: ModelName, + existingIdentifiers: string[], + abortSignal: AbortSignal, +): Promise { + const existingList = + existingIdentifiers.length > 0 + ? `\n\nIMPORTANT: The following identifiers already exist and must NOT be used: ${existingIdentifiers.join(', ')}` + : '' + + const prompt = `Create an agent configuration based on this request: "${userPrompt}".${existingList} + Return ONLY the JSON object, no other text.` + + const userMessage = createUserMessage({ content: prompt }) + + // Fetch user and system contexts + const userContext = await getUserContext() + + // Prepend user context to messages and append system context to system prompt + const messagesWithContext = prependUserContext([userMessage], userContext) + + // Include memory instructions when the feature is enabled + const systemPrompt = isAutoMemoryEnabled() + ? AGENT_CREATION_SYSTEM_PROMPT + AGENT_MEMORY_INSTRUCTIONS + : AGENT_CREATION_SYSTEM_PROMPT + + const response = await queryModelWithoutStreaming({ + messages: normalizeMessagesForAPI(messagesWithContext), + systemPrompt: asSystemPrompt([systemPrompt]), + thinkingConfig: { type: 'disabled' as const }, + tools: [], + signal: abortSignal, + options: { + getToolPermissionContext: async () => getEmptyToolPermissionContext(), + model, + toolChoice: undefined, + agents: [], + isNonInteractiveSession: false, + hasAppendSystemPrompt: false, + querySource: 'agent_creation', + mcpTools: [], + }, + }) + + const textBlocks = response.message.content.filter( + (block): block is ContentBlock & { type: 'text' } => block.type === 'text', + ) + const responseText = textBlocks.map(block => block.text).join('\n') + + let parsed: GeneratedAgent + try { + parsed = jsonParse(responseText.trim()) + } catch { + const jsonMatch = responseText.match(/\{[\s\S]*\}/) + if (!jsonMatch) { + throw new Error('No JSON object found in response') + } + parsed = jsonParse(jsonMatch[0]) + } + + if (!parsed.identifier || !parsed.whenToUse || !parsed.systemPrompt) { + throw new Error('Invalid agent configuration generated') + } + + logEvent('tengu_agent_definition_generated', { + agent_identifier: + parsed.identifier as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + return { + identifier: parsed.identifier, + whenToUse: parsed.whenToUse, + systemPrompt: parsed.systemPrompt, + } +} diff --git a/components/agents/new-agent-creation/CreateAgentWizard.tsx b/components/agents/new-agent-creation/CreateAgentWizard.tsx new file mode 100644 index 0000000..4ca9ba3 --- /dev/null +++ b/components/agents/new-agent-creation/CreateAgentWizard.tsx @@ -0,0 +1,97 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode } from 'react'; +import { isAutoMemoryEnabled } from '../../../memdir/paths.js'; +import type { Tools } from '../../../Tool.js'; +import type { AgentDefinition } from '../../../tools/AgentTool/loadAgentsDir.js'; +import { WizardProvider } from '../../wizard/index.js'; +import type { WizardStepComponent } from '../../wizard/types.js'; +import type { AgentWizardData } from './types.js'; +import { ColorStep } from './wizard-steps/ColorStep.js'; +import { ConfirmStepWrapper } from './wizard-steps/ConfirmStepWrapper.js'; +import { DescriptionStep } from './wizard-steps/DescriptionStep.js'; +import { GenerateStep } from './wizard-steps/GenerateStep.js'; +import { LocationStep } from './wizard-steps/LocationStep.js'; +import { MemoryStep } from './wizard-steps/MemoryStep.js'; +import { MethodStep } from './wizard-steps/MethodStep.js'; +import { ModelStep } from './wizard-steps/ModelStep.js'; +import { PromptStep } from './wizard-steps/PromptStep.js'; +import { ToolsStep } from './wizard-steps/ToolsStep.js'; +import { TypeStep } from './wizard-steps/TypeStep.js'; +type Props = { + tools: Tools; + existingAgents: AgentDefinition[]; + onComplete: (message: string) => void; + onCancel: () => void; +}; +export function CreateAgentWizard(t0) { + const $ = _c(17); + const { + tools, + existingAgents, + onComplete, + onCancel + } = t0; + let t1; + if ($[0] !== existingAgents) { + t1 = () => ; + $[0] = existingAgents; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== tools) { + t2 = () => ; + $[2] = tools; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t3 = isAutoMemoryEnabled() ? [MemoryStep] : []; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== existingAgents || $[6] !== onComplete || $[7] !== tools) { + t4 = () => ; + $[5] = existingAgents; + $[6] = onComplete; + $[7] = tools; + $[8] = t4; + } else { + t4 = $[8]; + } + let t5; + if ($[9] !== t1 || $[10] !== t2 || $[11] !== t4) { + t5 = [LocationStep, MethodStep, GenerateStep, t1, PromptStep, DescriptionStep, t2, ModelStep, ColorStep, ...t3, t4]; + $[9] = t1; + $[10] = t2; + $[11] = t4; + $[12] = t5; + } else { + t5 = $[12]; + } + const steps = t5; + let t6; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t6 = {}; + $[13] = t6; + } else { + t6 = $[13]; + } + let t7; + if ($[14] !== onCancel || $[15] !== steps) { + t7 = ; + $[14] = onCancel; + $[15] = steps; + $[16] = t7; + } else { + t7 = $[16]; + } + return t7; +} +function _temp() {} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsImlzQXV0b01lbW9yeUVuYWJsZWQiLCJUb29scyIsIkFnZW50RGVmaW5pdGlvbiIsIldpemFyZFByb3ZpZGVyIiwiV2l6YXJkU3RlcENvbXBvbmVudCIsIkFnZW50V2l6YXJkRGF0YSIsIkNvbG9yU3RlcCIsIkNvbmZpcm1TdGVwV3JhcHBlciIsIkRlc2NyaXB0aW9uU3RlcCIsIkdlbmVyYXRlU3RlcCIsIkxvY2F0aW9uU3RlcCIsIk1lbW9yeVN0ZXAiLCJNZXRob2RTdGVwIiwiTW9kZWxTdGVwIiwiUHJvbXB0U3RlcCIsIlRvb2xzU3RlcCIsIlR5cGVTdGVwIiwiUHJvcHMiLCJ0b29scyIsImV4aXN0aW5nQWdlbnRzIiwib25Db21wbGV0ZSIsIm1lc3NhZ2UiLCJvbkNhbmNlbCIsIkNyZWF0ZUFnZW50V2l6YXJkIiwidDAiLCIkIiwiX2MiLCJ0MSIsInQyIiwidDMiLCJTeW1ib2wiLCJmb3IiLCJ0NCIsInQ1Iiwic3RlcHMiLCJ0NiIsInQ3IiwiX3RlbXAiXSwic291cmNlcyI6WyJDcmVhdGVBZ2VudFdpemFyZC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHR5cGUgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBpc0F1dG9NZW1vcnlFbmFibGVkIH0gZnJvbSAnLi4vLi4vLi4vbWVtZGlyL3BhdGhzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUb29scyB9IGZyb20gJy4uLy4uLy4uL1Rvb2wuanMnXG5pbXBvcnQgdHlwZSB7IEFnZW50RGVmaW5pdGlvbiB9IGZyb20gJy4uLy4uLy4uL3Rvb2xzL0FnZW50VG9vbC9sb2FkQWdlbnRzRGlyLmpzJ1xuaW1wb3J0IHsgV2l6YXJkUHJvdmlkZXIgfSBmcm9tICcuLi8uLi93aXphcmQvaW5kZXguanMnXG5pbXBvcnQgdHlwZSB7IFdpemFyZFN0ZXBDb21wb25lbnQgfSBmcm9tICcuLi8uLi93aXphcmQvdHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IEFnZW50V2l6YXJkRGF0YSB9IGZyb20gJy4vdHlwZXMuanMnXG5pbXBvcnQgeyBDb2xvclN0ZXAgfSBmcm9tICcuL3dpemFyZC1zdGVwcy9Db2xvclN0ZXAuanMnXG5pbXBvcnQgeyBDb25maXJtU3RlcFdyYXBwZXIgfSBmcm9tICcuL3dpemFyZC1zdGVwcy9Db25maXJtU3RlcFdyYXBwZXIuanMnXG5pbXBvcnQgeyBEZXNjcmlwdGlvblN0ZXAgfSBmcm9tICcuL3dpemFyZC1zdGVwcy9EZXNjcmlwdGlvblN0ZXAuanMnXG5pbXBvcnQgeyBHZW5lcmF0ZVN0ZXAgfSBmcm9tICcuL3dpemFyZC1zdGVwcy9HZW5lcmF0ZVN0ZXAuanMnXG5pbXBvcnQgeyBMb2NhdGlvblN0ZXAgfSBmcm9tICcuL3dpemFyZC1zdGVwcy9Mb2NhdGlvblN0ZXAuanMnXG5pbXBvcnQgeyBNZW1vcnlTdGVwIH0gZnJvbSAnLi93aXphcmQtc3RlcHMvTWVtb3J5U3RlcC5qcydcbmltcG9ydCB7IE1ldGhvZFN0ZXAgfSBmcm9tICcuL3dpemFyZC1zdGVwcy9NZXRob2RTdGVwLmpzJ1xuaW1wb3J0IHsgTW9kZWxTdGVwIH0gZnJvbSAnLi93aXphcmQtc3RlcHMvTW9kZWxTdGVwLmpzJ1xuaW1wb3J0IHsgUHJvbXB0U3RlcCB9IGZyb20gJy4vd2l6YXJkLXN0ZXBzL1Byb21wdFN0ZXAuanMnXG5pbXBvcnQgeyBUb29sc1N0ZXAgfSBmcm9tICcuL3dpemFyZC1zdGVwcy9Ub29sc1N0ZXAuanMnXG5pbXBvcnQgeyBUeXBlU3RlcCB9IGZyb20gJy4vd2l6YXJkLXN0ZXBzL1R5cGVTdGVwLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0b29sczogVG9vbHNcbiAgZXhpc3RpbmdBZ2VudHM6IEFnZW50RGVmaW5pdGlvbltdXG4gIG9uQ29tcGxldGU6IChtZXNzYWdlOiBzdHJpbmcpID0+IHZvaWRcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIENyZWF0ZUFnZW50V2l6YXJkKHtcbiAgdG9vbHMsXG4gIGV4aXN0aW5nQWdlbnRzLFxuICBvbkNvbXBsZXRlLFxuICBvbkNhbmNlbCxcbn06IFByb3BzKTogUmVhY3ROb2RlIHtcbiAgLy8gQ3JlYXRlIHN0ZXAgY29tcG9uZW50cyB3aXRoIHByb3BzXG4gIGNvbnN0IHN0ZXBzOiBXaXphcmRTdGVwQ29tcG9uZW50PEFnZW50V2l6YXJkRGF0YT5bXSA9IFtcbiAgICBMb2NhdGlvblN0ZXAsIC8vIDBcbiAgICBNZXRob2RTdGVwLCAvLyAxXG4gICAgR2VuZXJhdGVTdGVwLCAvLyAyXG4gICAgKCkgPT4gPFR5cGVTdGVwIGV4aXN0aW5nQWdlbnRzPXtleGlzdGluZ0FnZW50c30gLz4sIC8vIDNcbiAgICBQcm9tcHRTdGVwLCAvLyA0XG4gICAgRGVzY3JpcHRpb25TdGVwLCAvLyA1XG4gICAgKCkgPT4gPFRvb2xzU3RlcCB0b29scz17dG9vbHN9IC8+LCAvLyA2XG4gICAgTW9kZWxTdGVwLCAvLyA3XG4gICAgQ29sb3JTdGVwLCAvLyA4XG4gICAgLy8gTWVtb3J5U3RlcCBpcyBjb25kaXRpb25hbGx5IGluY2x1ZGVkIGJhc2VkIG9uIEdyb3d0aEJvb2sgZ2F0ZVxuICAgIC4uLihpc0F1dG9NZW1vcnlFbmFibGVkKCkgPyBbTWVtb3J5U3RlcF0gOiBbXSksXG4gICAgKCkgPT4gKFxuICAgICAgPENvbmZpcm1TdGVwV3JhcHBlclxuICAgICAgICB0b29scz17dG9vbHN9XG4gICAgICAgIGV4aXN0aW5nQWdlbnRzPXtleGlzdGluZ0FnZW50c31cbiAgICAgICAgb25Db21wbGV0ZT17b25Db21wbGV0ZX1cbiAgICAgIC8+XG4gICAgKSxcbiAgXVxuXG4gIHJldHVybiAoXG4gICAgPFdpemFyZFByb3ZpZGVyPEFnZW50V2l6YXJkRGF0YT5cbiAgICAgIHN0ZXBzPXtzdGVwc31cbiAgICAgIGluaXRpYWxEYXRhPXt7fX1cbiAgICAgIG9uQ29tcGxldGU9eygpID0+IHtcbiAgICAgICAgLy8gV2l6YXJkIGNvbXBsZXRpb24gaXMgaGFuZGxlZCBieSBDb25maXJtU3RlcFdyYXBwZXJcbiAgICAgICAgLy8gd2hpY2ggY2FsbHMgb25Db21wbGV0ZSB3aXRoIHRoZSBhcHByb3ByaWF0ZSBtZXNzYWdlXG4gICAgICB9fVxuICAgICAgb25DYW5jZWw9e29uQ2FuY2VsfVxuICAgICAgdGl0bGU9XCJDcmVhdGUgbmV3IGFnZW50XCJcbiAgICAgIHNob3dTdGVwQ291bnRlcj17ZmFsc2V9XG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJLEtBQUtDLFNBQVMsUUFBUSxPQUFPO0FBQzdDLFNBQVNDLG1CQUFtQixRQUFRLDBCQUEwQjtBQUM5RCxjQUFjQyxLQUFLLFFBQVEsa0JBQWtCO0FBQzdDLGNBQWNDLGVBQWUsUUFBUSwyQ0FBMkM7QUFDaEYsU0FBU0MsY0FBYyxRQUFRLHVCQUF1QjtBQUN0RCxjQUFjQyxtQkFBbUIsUUFBUSx1QkFBdUI7QUFDaEUsY0FBY0MsZUFBZSxRQUFRLFlBQVk7QUFDakQsU0FBU0MsU0FBUyxRQUFRLDZCQUE2QjtBQUN2RCxTQUFTQyxrQkFBa0IsUUFBUSxzQ0FBc0M7QUFDekUsU0FBU0MsZUFBZSxRQUFRLG1DQUFtQztBQUNuRSxTQUFTQyxZQUFZLFFBQVEsZ0NBQWdDO0FBQzdELFNBQVNDLFlBQVksUUFBUSxnQ0FBZ0M7QUFDN0QsU0FBU0MsVUFBVSxRQUFRLDhCQUE4QjtBQUN6RCxTQUFTQyxVQUFVLFFBQVEsOEJBQThCO0FBQ3pELFNBQVNDLFNBQVMsUUFBUSw2QkFBNkI7QUFDdkQsU0FBU0MsVUFBVSxRQUFRLDhCQUE4QjtBQUN6RCxTQUFTQyxTQUFTLFFBQVEsNkJBQTZCO0FBQ3ZELFNBQVNDLFFBQVEsUUFBUSw0QkFBNEI7QUFFckQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLEtBQUssRUFBRWpCLEtBQUs7RUFDWmtCLGNBQWMsRUFBRWpCLGVBQWUsRUFBRTtFQUNqQ2tCLFVBQVUsRUFBRSxDQUFDQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSTtFQUNyQ0MsUUFBUSxFQUFFLEdBQUcsR0FBRyxJQUFJO0FBQ3RCLENBQUM7QUFFRCxPQUFPLFNBQUFDLGtCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQTJCO0lBQUFSLEtBQUE7SUFBQUMsY0FBQTtJQUFBQyxVQUFBO0lBQUFFO0VBQUEsSUFBQUUsRUFLMUI7RUFBQSxJQUFBRyxFQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBTixjQUFBO0lBTUpRLEVBQUEsR0FBQUEsQ0FBQSxLQUFNLENBQUMsUUFBUSxDQUFpQlIsY0FBYyxDQUFkQSxlQUFhLENBQUMsR0FBSTtJQUFBTSxDQUFBLE1BQUFOLGNBQUE7SUFBQU0sQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFBQSxJQUFBRyxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBUCxLQUFBO0lBR2xEVSxFQUFBLEdBQUFBLENBQUEsS0FBTSxDQUFDLFNBQVMsQ0FBUVYsS0FBSyxDQUFMQSxNQUFJLENBQUMsR0FBSTtJQUFBTyxDQUFBLE1BQUFQLEtBQUE7SUFBQU8sQ0FBQSxNQUFBRyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSCxDQUFBO0VBQUE7RUFBQSxJQUFBSSxFQUFBO0VBQUEsSUFBQUosQ0FBQSxRQUFBSyxNQUFBLENBQUFDLEdBQUE7SUFJN0JGLEVBQUEsR0FBQTdCLG1CQUFtQixDQUFxQixDQUFDLEdBQXpDLENBQXlCVyxVQUFVLENBQU0sR0FBekMsRUFBeUM7SUFBQWMsQ0FBQSxNQUFBSSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSixDQUFBO0VBQUE7RUFBQSxJQUFBTyxFQUFBO0VBQUEsSUFBQVAsQ0FBQSxRQUFBTixjQUFBLElBQUFNLENBQUEsUUFBQUwsVUFBQSxJQUFBSyxDQUFBLFFBQUFQLEtBQUE7SUFDN0NjLEVBQUEsR0FBQUEsQ0FBQSxLQUNFLENBQUMsa0JBQWtCLENBQ1ZkLEtBQUssQ0FBTEEsTUFBSSxDQUFDLENBQ0lDLGNBQWMsQ0FBZEEsZUFBYSxDQUFDLENBQ2xCQyxVQUFVLENBQVZBLFdBQVMsQ0FBQyxHQUV6QjtJQUFBSyxDQUFBLE1BQUFOLGNBQUE7SUFBQU0sQ0FBQSxNQUFBTCxVQUFBO0lBQUFLLENBQUEsTUFBQVAsS0FBQTtJQUFBTyxDQUFBLE1BQUFPLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFQLENBQUE7RUFBQTtFQUFBLElBQUFRLEVBQUE7RUFBQSxJQUFBUixDQUFBLFFBQUFFLEVBQUEsSUFBQUYsQ0FBQSxTQUFBRyxFQUFBLElBQUFILENBQUEsU0FBQU8sRUFBQTtJQWxCbURDLEVBQUEsSUFDcER2QixZQUFZLEVBQ1pFLFVBQVUsRUFDVkgsWUFBWSxFQUNaa0IsRUFBa0QsRUFDbERiLFVBQVUsRUFDVk4sZUFBZSxFQUNmb0IsRUFBaUMsRUFDakNmLFNBQVMsRUFDVFAsU0FBUyxLQUVMdUIsRUFBeUMsRUFDN0NHLEVBTUMsQ0FDRjtJQUFBUCxDQUFBLE1BQUFFLEVBQUE7SUFBQUYsQ0FBQSxPQUFBRyxFQUFBO0lBQUFILENBQUEsT0FBQU8sRUFBQTtJQUFBUCxDQUFBLE9BQUFRLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFSLENBQUE7RUFBQTtFQW5CRCxNQUFBUyxLQUFBLEdBQXNERCxFQW1CckQ7RUFBQSxJQUFBRSxFQUFBO0VBQUEsSUFBQVYsQ0FBQSxTQUFBSyxNQUFBLENBQUFDLEdBQUE7SUFLZ0JJLEVBQUEsSUFBQyxDQUFDO0lBQUFWLENBQUEsT0FBQVUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVYsQ0FBQTtFQUFBO0VBQUEsSUFBQVcsRUFBQTtFQUFBLElBQUFYLENBQUEsU0FBQUgsUUFBQSxJQUFBRyxDQUFBLFNBQUFTLEtBQUE7SUFGakJFLEVBQUEsSUFBQyxjQUFjLENBQ05GLEtBQUssQ0FBTEEsTUFBSSxDQUFDLENBQ0MsV0FBRSxDQUFGLENBQUFDLEVBQUMsQ0FBQyxDQUNILFVBR1gsQ0FIVyxDQUFBRSxLQUdaLENBQUMsQ0FDU2YsUUFBUSxDQUFSQSxTQUFPLENBQUMsQ0FDWixLQUFrQixDQUFsQixrQkFBa0IsQ0FDUCxlQUFLLENBQUwsTUFBSSxDQUFDLEdBQ3RCO0lBQUFHLENBQUEsT0FBQUgsUUFBQTtJQUFBRyxDQUFBLE9BQUFTLEtBQUE7SUFBQVQsQ0FBQSxPQUFBVyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBWCxDQUFBO0VBQUE7RUFBQSxPQVZGVyxFQVVFO0FBQUE7QUF2Q0MsU0FBQUMsTUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx b/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx new file mode 100644 index 0000000..4d5d338 --- /dev/null +++ b/components/agents/new-agent-creation/wizard-steps/ColorStep.tsx @@ -0,0 +1,84 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode } from 'react'; +import { Box } from '../../../../ink.js'; +import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; +import type { AgentColorName } from '../../../../tools/AgentTool/agentColorManager.js'; +import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; +import { Byline } from '../../../design-system/Byline.js'; +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js'; +import { useWizard } from '../../../wizard/index.js'; +import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js'; +import { ColorPicker } from '../../ColorPicker.js'; +import type { AgentWizardData } from '../types.js'; +export function ColorStep() { + const $ = _c(14); + const { + goNext, + goBack, + updateWizardData, + wizardData + } = useWizard(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { + context: "Confirmation" + }; + $[0] = t0; + } else { + t0 = $[0]; + } + useKeybinding("confirm:no", goBack, t0); + let t1; + if ($[1] !== goNext || $[2] !== updateWizardData || $[3] !== wizardData.agentType || $[4] !== wizardData.location || $[5] !== wizardData.selectedModel || $[6] !== wizardData.selectedTools || $[7] !== wizardData.systemPrompt || $[8] !== wizardData.whenToUse) { + t1 = color => { + updateWizardData({ + selectedColor: color, + finalAgent: { + agentType: wizardData.agentType, + whenToUse: wizardData.whenToUse, + getSystemPrompt: () => wizardData.systemPrompt, + tools: wizardData.selectedTools, + ...(wizardData.selectedModel ? { + model: wizardData.selectedModel + } : {}), + ...(color ? { + color: color as AgentColorName + } : {}), + source: wizardData.location + } + }); + goNext(); + }; + $[1] = goNext; + $[2] = updateWizardData; + $[3] = wizardData.agentType; + $[4] = wizardData.location; + $[5] = wizardData.selectedModel; + $[6] = wizardData.selectedTools; + $[7] = wizardData.systemPrompt; + $[8] = wizardData.whenToUse; + $[9] = t1; + } else { + t1 = $[9]; + } + const handleConfirm = t1; + let t2; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t2 = ; + $[10] = t2; + } else { + t2 = $[10]; + } + const t3 = wizardData.agentType || "agent"; + let t4; + if ($[11] !== handleConfirm || $[12] !== t3) { + t4 = ; + $[11] = handleConfirm; + $[12] = t3; + $[13] = t4; + } else { + t4 = $[13]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsIkJveCIsInVzZUtleWJpbmRpbmciLCJBZ2VudENvbG9yTmFtZSIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIkJ5bGluZSIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwidXNlV2l6YXJkIiwiV2l6YXJkRGlhbG9nTGF5b3V0IiwiQ29sb3JQaWNrZXIiLCJBZ2VudFdpemFyZERhdGEiLCJDb2xvclN0ZXAiLCIkIiwiX2MiLCJnb05leHQiLCJnb0JhY2siLCJ1cGRhdGVXaXphcmREYXRhIiwid2l6YXJkRGF0YSIsInQwIiwiU3ltYm9sIiwiZm9yIiwiY29udGV4dCIsInQxIiwiYWdlbnRUeXBlIiwibG9jYXRpb24iLCJzZWxlY3RlZE1vZGVsIiwic2VsZWN0ZWRUb29scyIsInN5c3RlbVByb21wdCIsIndoZW5Ub1VzZSIsImNvbG9yIiwic2VsZWN0ZWRDb2xvciIsImZpbmFsQWdlbnQiLCJnZXRTeXN0ZW1Qcm9tcHQiLCJ0b29scyIsIm1vZGVsIiwic291cmNlIiwiaGFuZGxlQ29uZmlybSIsInQyIiwidDMiLCJ0NCJdLCJzb3VyY2VzIjpbIkNvbG9yU3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHR5cGUgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3ggfSBmcm9tICcuLi8uLi8uLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4vLi4vLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB0eXBlIHsgQWdlbnRDb2xvck5hbWUgfSBmcm9tICcuLi8uLi8uLi8uLi90b29scy9BZ2VudFRvb2wvYWdlbnRDb2xvck1hbmFnZXIuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi8uLi8uLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi8uLi8uLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vLi4vLi4vZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IHVzZVdpemFyZCB9IGZyb20gJy4uLy4uLy4uL3dpemFyZC9pbmRleC5qcydcbmltcG9ydCB7IFdpemFyZERpYWxvZ0xheW91dCB9IGZyb20gJy4uLy4uLy4uL3dpemFyZC9XaXphcmREaWFsb2dMYXlvdXQuanMnXG5pbXBvcnQgeyBDb2xvclBpY2tlciB9IGZyb20gJy4uLy4uL0NvbG9yUGlja2VyLmpzJ1xuaW1wb3J0IHR5cGUgeyBBZ2VudFdpemFyZERhdGEgfSBmcm9tICcuLi90eXBlcy5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIENvbG9yU3RlcCgpOiBSZWFjdE5vZGUge1xuICBjb25zdCB7IGdvTmV4dCwgZ29CYWNrLCB1cGRhdGVXaXphcmREYXRhLCB3aXphcmREYXRhIH0gPVxuICAgIHVzZVdpemFyZDxBZ2VudFdpemFyZERhdGE+KClcblxuICAvLyBIYW5kbGUgZXNjYXBlIGtleSAtIENvbG9yUGlja2VyIGhhbmRsZXMgaXRzIG93biBlc2NhcGUgaW50ZXJuYWxseVxuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOm5vJywgZ29CYWNrLCB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nIH0pXG5cbiAgY29uc3QgaGFuZGxlQ29uZmlybSA9IChjb2xvcj86IHN0cmluZyk6IHZvaWQgPT4ge1xuICAgIHVwZGF0ZVdpemFyZERhdGEoe1xuICAgICAgc2VsZWN0ZWRDb2xvcjogY29sb3IsXG4gICAgICAvLyBQcmVwYXJlIGZpbmFsIGFnZW50IGZvciBjb25maXJtYXRpb25cbiAgICAgIGZpbmFsQWdlbnQ6IHtcbiAgICAgICAgYWdlbnRUeXBlOiB3aXphcmREYXRhLmFnZW50VHlwZSEsXG4gICAgICAgIHdoZW5Ub1VzZTogd2l6YXJkRGF0YS53aGVuVG9Vc2UhLFxuICAgICAgICBnZXRTeXN0ZW1Qcm9tcHQ6ICgpID0+IHdpemFyZERhdGEuc3lzdGVtUHJvbXB0ISxcbiAgICAgICAgdG9vbHM6IHdpemFyZERhdGEuc2VsZWN0ZWRUb29scyxcbiAgICAgICAgLi4uKHdpemFyZERhdGEuc2VsZWN0ZWRNb2RlbFxuICAgICAgICAgID8geyBtb2RlbDogd2l6YXJkRGF0YS5zZWxlY3RlZE1vZGVsIH1cbiAgICAgICAgICA6IHt9KSxcbiAgICAgICAgLi4uKGNvbG9yID8geyBjb2xvcjogY29sb3IgYXMgQWdlbnRDb2xvck5hbWUgfSA6IHt9KSxcbiAgICAgICAgc291cmNlOiB3aXphcmREYXRhLmxvY2F0aW9uISxcbiAgICAgIH0sXG4gICAgfSlcbiAgICBnb05leHQoKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8V2l6YXJkRGlhbG9nTGF5b3V0XG4gICAgICBzdWJ0aXRsZT1cIkNob29zZSBiYWNrZ3JvdW5kIGNvbG9yXCJcbiAgICAgIGZvb3RlclRleHQ9e1xuICAgICAgICA8QnlsaW5lPlxuICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIuKGkeKGk1wiIGFjdGlvbj1cIm5hdmlnYXRlXCIgLz5cbiAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cInNlbGVjdFwiIC8+XG4gICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgYWN0aW9uPVwiY29uZmlybTpub1wiXG4gICAgICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwiZ28gYmFja1wiXG4gICAgICAgICAgLz5cbiAgICAgICAgPC9CeWxpbmU+XG4gICAgICB9XG4gICAgPlxuICAgICAgPEJveD5cbiAgICAgICAgPENvbG9yUGlja2VyXG4gICAgICAgICAgYWdlbnROYW1lPXt3aXphcmREYXRhLmFnZW50VHlwZSB8fCAnYWdlbnQnfVxuICAgICAgICAgIGN1cnJlbnRDb2xvcj1cImF1dG9tYXRpY1wiXG4gICAgICAgICAgb25Db25maXJtPXtoYW5kbGVDb25maXJtfVxuICAgICAgICAvPlxuICAgICAgPC9Cb3g+XG4gICAgPC9XaXphcmREaWFsb2dMYXlvdXQ+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFBSSxLQUFLQyxTQUFTLFFBQVEsT0FBTztBQUM3QyxTQUFTQyxHQUFHLFFBQVEsb0JBQW9CO0FBQ3hDLFNBQVNDLGFBQWEsUUFBUSwwQ0FBMEM7QUFDeEUsY0FBY0MsY0FBYyxRQUFRLGtEQUFrRDtBQUN0RixTQUFTQyx3QkFBd0IsUUFBUSxzQ0FBc0M7QUFDL0UsU0FBU0MsTUFBTSxRQUFRLGtDQUFrQztBQUN6RCxTQUFTQyxvQkFBb0IsUUFBUSxnREFBZ0Q7QUFDckYsU0FBU0MsU0FBUyxRQUFRLDBCQUEwQjtBQUNwRCxTQUFTQyxrQkFBa0IsUUFBUSx1Q0FBdUM7QUFDMUUsU0FBU0MsV0FBVyxRQUFRLHNCQUFzQjtBQUNsRCxjQUFjQyxlQUFlLFFBQVEsYUFBYTtBQUVsRCxPQUFPLFNBQUFDLFVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFDTDtJQUFBQyxNQUFBO0lBQUFDLE1BQUE7SUFBQUMsZ0JBQUE7SUFBQUM7RUFBQSxJQUNFVixTQUFTLENBQWtCLENBQUM7RUFBQSxJQUFBVyxFQUFBO0VBQUEsSUFBQU4sQ0FBQSxRQUFBTyxNQUFBLENBQUFDLEdBQUE7SUFHTUYsRUFBQTtNQUFBRyxPQUFBLEVBQVc7SUFBZSxDQUFDO0lBQUFULENBQUEsTUFBQU0sRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQU4sQ0FBQTtFQUFBO0VBQS9EVixhQUFhLENBQUMsWUFBWSxFQUFFYSxNQUFNLEVBQUVHLEVBQTJCLENBQUM7RUFBQSxJQUFBSSxFQUFBO0VBQUEsSUFBQVYsQ0FBQSxRQUFBRSxNQUFBLElBQUFGLENBQUEsUUFBQUks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 \ No newline at end of file diff --git a/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx b/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx new file mode 100644 index 0000000..308b808 --- /dev/null +++ b/components/agents/new-agent-creation/wizard-steps/ConfirmStep.tsx @@ -0,0 +1,378 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode } from 'react'; +import type { KeyboardEvent } from '../../../../ink/events/keyboard-event.js'; +import { Box, Text } from '../../../../ink.js'; +import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; +import { isAutoMemoryEnabled } from '../../../../memdir/paths.js'; +import type { Tools } from '../../../../Tool.js'; +import { getMemoryScopeDisplay } from '../../../../tools/AgentTool/agentMemory.js'; +import type { AgentDefinition } from '../../../../tools/AgentTool/loadAgentsDir.js'; +import { truncateToWidth } from '../../../../utils/format.js'; +import { getAgentModelDisplay } from '../../../../utils/model/agent.js'; +import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; +import { Byline } from '../../../design-system/Byline.js'; +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js'; +import { useWizard } from '../../../wizard/index.js'; +import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js'; +import { getNewRelativeAgentFilePath } from '../../agentFileUtils.js'; +import { validateAgent } from '../../validateAgent.js'; +import type { AgentWizardData } from '../types.js'; +type Props = { + tools: Tools; + existingAgents: AgentDefinition[]; + onSave: () => void; + onSaveAndEdit: () => void; + error?: string | null; +}; +export function ConfirmStep(t0) { + const $ = _c(88); + const { + tools, + existingAgents, + onSave, + onSaveAndEdit, + error + } = t0; + const { + goBack, + wizardData + } = useWizard(); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + context: "Confirmation" + }; + $[0] = t1; + } else { + t1 = $[0]; + } + useKeybinding("confirm:no", goBack, t1); + let t2; + if ($[1] !== onSave || $[2] !== onSaveAndEdit) { + t2 = e => { + if (e.key === "s" || e.key === "return") { + e.preventDefault(); + onSave(); + } else { + if (e.key === "e") { + e.preventDefault(); + onSaveAndEdit(); + } + } + }; + $[1] = onSave; + $[2] = onSaveAndEdit; + $[3] = t2; + } else { + t2 = $[3]; + } + const handleKeyDown = t2; + const agent = wizardData.finalAgent; + let T0; + let T1; + let t10; + let t11; + let t12; + let t13; + let t14; + let t15; + let t16; + let t17; + let t18; + let t19; + let t3; + let t4; + let t5; + let t6; + let t7; + let t8; + let t9; + if ($[4] !== agent || $[5] !== existingAgents || $[6] !== handleKeyDown || $[7] !== tools || $[8] !== wizardData.location) { + const validation = validateAgent(agent, tools, existingAgents); + let t20; + if ($[28] !== agent) { + t20 = truncateToWidth(agent.getSystemPrompt(), 240); + $[28] = agent; + $[29] = t20; + } else { + t20 = $[29]; + } + const systemPromptPreview = t20; + let t21; + if ($[30] !== agent.whenToUse) { + t21 = truncateToWidth(agent.whenToUse, 240); + $[30] = agent.whenToUse; + $[31] = t21; + } else { + t21 = $[31]; + } + const whenToUsePreview = t21; + const getToolsDisplay = _temp; + let t22; + if ($[32] !== agent.memory) { + t22 = isAutoMemoryEnabled() ? Memory: {getMemoryScopeDisplay(agent.memory)} : null; + $[32] = agent.memory; + $[33] = t22; + } else { + t22 = $[33]; + } + const memoryDisplayElement = t22; + T1 = WizardDialogLayout; + t18 = "Confirm and save"; + if ($[34] === Symbol.for("react.memo_cache_sentinel")) { + t19 = ; + $[34] = t19; + } else { + t19 = $[34]; + } + T0 = Box; + t3 = "column"; + t4 = 0; + t5 = true; + t6 = handleKeyDown; + let t23; + if ($[35] === Symbol.for("react.memo_cache_sentinel")) { + t23 = Name; + $[35] = t23; + } else { + t23 = $[35]; + } + if ($[36] !== agent.agentType) { + t7 = {t23}: {agent.agentType}; + $[36] = agent.agentType; + $[37] = t7; + } else { + t7 = $[37]; + } + let t24; + if ($[38] === Symbol.for("react.memo_cache_sentinel")) { + t24 = Location; + $[38] = t24; + } else { + t24 = $[38]; + } + let t25; + if ($[39] !== agent.agentType || $[40] !== wizardData.location) { + t25 = getNewRelativeAgentFilePath({ + source: wizardData.location, + agentType: agent.agentType + }); + $[39] = agent.agentType; + $[40] = wizardData.location; + $[41] = t25; + } else { + t25 = $[41]; + } + if ($[42] !== t25) { + t8 = {t24}:{" "}{t25}; + $[42] = t25; + $[43] = t8; + } else { + t8 = $[43]; + } + let t26; + if ($[44] === Symbol.for("react.memo_cache_sentinel")) { + t26 = Tools; + $[44] = t26; + } else { + t26 = $[44]; + } + let t27; + if ($[45] !== agent.tools) { + t27 = getToolsDisplay(agent.tools); + $[45] = agent.tools; + $[46] = t27; + } else { + t27 = $[46]; + } + if ($[47] !== t27) { + t9 = {t26}: {t27}; + $[47] = t27; + $[48] = t9; + } else { + t9 = $[48]; + } + let t28; + if ($[49] === Symbol.for("react.memo_cache_sentinel")) { + t28 = Model; + $[49] = t28; + } else { + t28 = $[49]; + } + let t29; + if ($[50] !== agent.model) { + t29 = getAgentModelDisplay(agent.model); + $[50] = agent.model; + $[51] = t29; + } else { + t29 = $[51]; + } + if ($[52] !== t29) { + t10 = {t28}: {t29}; + $[52] = t29; + $[53] = t10; + } else { + t10 = $[53]; + } + t11 = memoryDisplayElement; + if ($[54] === Symbol.for("react.memo_cache_sentinel")) { + t12 = Description (tells Claude when to use this agent):; + $[54] = t12; + } else { + t12 = $[54]; + } + if ($[55] !== whenToUsePreview) { + t13 = {whenToUsePreview}; + $[55] = whenToUsePreview; + $[56] = t13; + } else { + t13 = $[56]; + } + if ($[57] === Symbol.for("react.memo_cache_sentinel")) { + t14 = System prompt:; + $[57] = t14; + } else { + t14 = $[57]; + } + if ($[58] !== systemPromptPreview) { + t15 = {systemPromptPreview}; + $[58] = systemPromptPreview; + $[59] = t15; + } else { + t15 = $[59]; + } + t16 = validation.warnings.length > 0 && Warnings:{validation.warnings.map(_temp2)}; + t17 = validation.errors.length > 0 && Errors:{validation.errors.map(_temp3)}; + $[4] = agent; + $[5] = existingAgents; + $[6] = handleKeyDown; + $[7] = tools; + $[8] = wizardData.location; + $[9] = T0; + $[10] = T1; + $[11] = t10; + $[12] = t11; + $[13] = t12; + $[14] = t13; + $[15] = t14; + $[16] = t15; + $[17] = t16; + $[18] = t17; + $[19] = t18; + $[20] = t19; + $[21] = t3; + $[22] = t4; + $[23] = t5; + $[24] = t6; + $[25] = t7; + $[26] = t8; + $[27] = t9; + } else { + T0 = $[9]; + T1 = $[10]; + t10 = $[11]; + t11 = $[12]; + t12 = $[13]; + t13 = $[14]; + t14 = $[15]; + t15 = $[16]; + t16 = $[17]; + t17 = $[18]; + t18 = $[19]; + t19 = $[20]; + t3 = $[21]; + t4 = $[22]; + t5 = $[23]; + t6 = $[24]; + t7 = $[25]; + t8 = $[26]; + t9 = $[27]; + } + let t20; + if ($[60] !== error) { + t20 = error && {error}; + $[60] = error; + $[61] = t20; + } else { + t20 = $[61]; + } + let t21; + if ($[62] === Symbol.for("react.memo_cache_sentinel")) { + t21 = s; + $[62] = t21; + } else { + t21 = $[62]; + } + let t22; + if ($[63] === Symbol.for("react.memo_cache_sentinel")) { + t22 = Enter; + $[63] = t22; + } else { + t22 = $[63]; + } + let t23; + if ($[64] === Symbol.for("react.memo_cache_sentinel")) { + t23 = Press {t21} or {t22} to save,{" "}e to save and edit; + $[64] = t23; + } else { + t23 = $[64]; + } + let t24; + if ($[65] !== T0 || $[66] !== t10 || $[67] !== t11 || $[68] !== t12 || $[69] !== t13 || $[70] !== t14 || $[71] !== t15 || $[72] !== t16 || $[73] !== t17 || $[74] !== t20 || $[75] !== t3 || $[76] !== t4 || $[77] !== t5 || $[78] !== t6 || $[79] !== t7 || $[80] !== t8 || $[81] !== t9) { + t24 = {t7}{t8}{t9}{t10}{t11}{t12}{t13}{t14}{t15}{t16}{t17}{t20}{t23}; + $[65] = T0; + $[66] = t10; + $[67] = t11; + $[68] = t12; + $[69] = t13; + $[70] = t14; + $[71] = t15; + $[72] = t16; + $[73] = t17; + $[74] = t20; + $[75] = t3; + $[76] = t4; + $[77] = t5; + $[78] = t6; + $[79] = t7; + $[80] = t8; + $[81] = t9; + $[82] = t24; + } else { + t24 = $[82]; + } + let t25; + if ($[83] !== T1 || $[84] !== t18 || $[85] !== t19 || $[86] !== t24) { + t25 = {t24}; + $[83] = T1; + $[84] = t18; + $[85] = t19; + $[86] = t24; + $[87] = t25; + } else { + t25 = $[87]; + } + return t25; +} +function _temp3(err, i_0) { + return {" "}• {err}; +} +function _temp2(warning, i) { + return {" "}• {warning}; +} +function _temp(toolNames) { + if (toolNames === undefined) { + return "All tools"; + } + if (toolNames.length === 0) { + return "None"; + } + if (toolNames.length === 1) { + return toolNames[0] || "None"; + } + if (toolNames.length === 2) { + return toolNames.join(" and "); + } + return `${toolNames.slice(0, -1).join(", ")}, and ${toolNames[toolNames.length - 1]}`; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsIktleWJvYXJkRXZlbnQiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZyIsImlzQXV0b01lbW9yeUVuYWJsZWQiLCJUb29scyIsImdldE1lbW9yeVNjb3BlRGlzcGxheSIsIkFnZW50RGVmaW5pdGlvbiIsInRydW5jYXRlVG9XaWR0aCIsImdldEFnZW50TW9kZWxEaXNwbGF5IiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiQnlsaW5lIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJ1c2VXaXphcmQiLCJXaXphcmREaWFsb2dMYXlvdXQiLCJnZXROZXdSZWxhdGl2ZUFnZW50RmlsZVBhdGgiLCJ2YWxpZGF0ZUFnZW50IiwiQWdlbnRXaXphcmREYXRhIiwiUHJvcHMiLCJ0b29scyIsImV4aXN0aW5nQWdlbnRzIiwib25TYXZlIiwib25TYXZlQW5kRWRpdCIsImVycm9yIiwiQ29uZmlybVN0ZXAiLCJ0MCIsIiQiLCJfYyIsImdvQmFjayIsIndpemFyZERhdGEiLCJ0MSIsIlN5bWJvbCIsImZvciIsImNvbnRleHQiLCJ0MiIsImUiLCJrZXkiLCJwcmV2ZW50RGVmYXVsdCIsImhhbmRsZUtleURvd24iLCJhZ2VudCIsImZpbmFsQWdlbnQiLCJUMCIsIlQxIiwidDEwIiwidDExIiwidDEyIiwidDEzIiwidDE0IiwidDE1IiwidDE2IiwidDE3IiwidDE4IiwidDE5IiwidDMiLCJ0NCIsInQ1IiwidDYiLCJ0NyIsInQ4IiwidDkiLCJsb2NhdGlvbiIsInZhbGlkYXRpb24iLCJ0MjAiLCJnZXRTeXN0ZW1Qcm9tcHQiLCJzeXN0ZW1Qcm9tcHRQcmV2aWV3IiwidDIxIiwid2hlblRvVXNlIiwid2hlblRvVXNlUHJldmlldyIsImdldFRvb2xzRGlzcGxheSIsIl90ZW1wIiwidDIyIiwibWVtb3J5IiwibWVtb3J5RGlzcGxheUVsZW1lbnQiLCJ0MjMiLCJhZ2VudFR5cGUiLCJ0MjQiLCJ0MjUiLCJzb3VyY2UiLCJ0MjYiLCJ0MjciLCJ0MjgiLCJ0MjkiLCJtb2RlbCIsIndhcm5pbmdzIiwibGVuZ3RoIiwibWFwIiwiX3RlbXAyIiwiZXJyb3JzIiwiX3RlbXAzIiwiZXJyIiwiaV8wIiwiaSIsIndhcm5pbmciLCJ0b29sTmFtZXMiLCJ1bmRlZmluZWQiLCJqb2luIiwic2xpY2UiXSwic291cmNlcyI6WyJDb25maXJtU3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHR5cGUgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IEtleWJvYXJkRXZlbnQgfSBmcm9tICcuLi8uLi8uLi8uLi9pbmsvZXZlbnRzL2tleWJvYXJkLWV2ZW50LmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uLy4uLy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyBpc0F1dG9NZW1vcnlFbmFibGVkIH0gZnJvbSAnLi4vLi4vLi4vLi4vbWVtZGlyL3BhdGhzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUb29scyB9IGZyb20gJy4uLy4uLy4uLy4uL1Rvb2wuanMnXG5pbXBvcnQgeyBnZXRNZW1vcnlTY29wZURpc3BsYXkgfSBmcm9tICcuLi8uLi8uLi8uLi90b29scy9BZ2VudFRvb2wvYWdlbnRNZW1vcnkuanMnXG5pbXBvcnQgdHlwZSB7IEFnZW50RGVmaW5pdGlvbiB9IGZyb20gJy4uLy4uLy4uLy4uL3Rvb2xzL0FnZW50VG9vbC9sb2FkQWdlbnRzRGlyLmpzJ1xuaW1wb3J0IHsgdHJ1bmNhdGVUb1dpZHRoIH0gZnJvbSAnLi4vLi4vLi4vLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgZ2V0QWdlbnRNb2RlbERpc3BsYXkgfSBmcm9tICcuLi8uLi8uLi8uLi91dGlscy9tb2RlbC9hZ2VudC5qcydcbmltcG9ydCB7IENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCB9IGZyb20gJy4uLy4uLy4uL0NvbmZpZ3VyYWJsZVNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4uLy4uLy4uL2Rlc2lnbi1zeXN0ZW0vQnlsaW5lLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi8uLi8uLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgdXNlV2l6YXJkIH0gZnJvbSAnLi4vLi4vLi4vd2l6YXJkL2luZGV4LmpzJ1xuaW1wb3J0IHsgV2l6YXJkRGlhbG9nTGF5b3V0IH0gZnJvbSAnLi4vLi4vLi4vd2l6YXJkL1dpemFyZERpYWxvZ0xheW91dC5qcydcbmltcG9ydCB7IGdldE5ld1JlbGF0aXZlQWdlbnRGaWxlUGF0aCB9IGZyb20gJy4uLy4uL2FnZW50RmlsZVV0aWxzLmpzJ1xuaW1wb3J0IHsgdmFsaWRhdGVBZ2VudCB9IGZyb20gJy4uLy4uL3ZhbGlkYXRlQWdlbnQuanMnXG5pbXBvcnQgdHlwZSB7IEFnZW50V2l6YXJkRGF0YSB9IGZyb20gJy4uL3R5cGVzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0b29sczogVG9vbHNcbiAgZXhpc3RpbmdBZ2VudHM6IEFnZW50RGVmaW5pdGlvbltdXG4gIG9uU2F2ZTogKCkgPT4gdm9pZFxuICBvblNhdmVBbmRFZGl0OiAoKSA9PiB2b2lkXG4gIGVycm9yPzogc3RyaW5nIHwgbnVsbFxufVxuXG5leHBvcnQgZnVuY3Rpb24gQ29uZmlybVN0ZXAoe1xuICB0b29scyxcbiAgZXhpc3RpbmdBZ2VudHMsXG4gIG9uU2F2ZSxcbiAgb25TYXZlQW5kRWRpdCxcbiAgZXJyb3IsXG59OiBQcm9wcyk6IFJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgZ29CYWNrLCB3aXphcmREYXRhIH0gPSB1c2VXaXphcmQ8QWdlbnRXaXphcmREYXRhPigpXG5cbiAgdXNlS2V5YmluZGluZygnY29uZmlybTpubycsIGdvQmFjaywgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJyB9KVxuXG4gIGNvbnN0IGhhbmRsZUtleURvd24gPSAoZTogS2V5Ym9hcmRFdmVudCkgPT4ge1xuICAgIGlmIChlLmtleSA9PT0gJ3MnIHx8IGUua2V5ID09PSAncmV0dXJuJykge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBvblNhdmUoKVxuICAgIH0gZWxzZSBpZiAoZS5rZXkgPT09ICdlJykge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBvblNhdmVBbmRFZGl0KClcbiAgICB9XG4gIH1cblxuICBjb25zdCBhZ2VudCA9IHdpemFyZERhdGEuZmluYWxBZ2VudCFcbiAgY29uc3QgdmFsaWRhdGlvbiA9IHZhbGlkYXRlQWdlbnQoYWdlbnQsIHRvb2xzLCBleGlzdGluZ0FnZW50cylcblxuICBjb25zdCBzeXN0ZW1Qcm9tcHRQcmV2aWV3ID0gdHJ1bmNhdGVUb1dpZHRoKGFnZW50LmdldFN5c3RlbVByb21wdCgpLCAyNDApXG4gIGNvbnN0IHdoZW5Ub1VzZVByZXZpZXcgPSB0cnVuY2F0ZVRvV2lkdGgoYWdlbnQud2hlblRvVXNlLCAyNDApXG5cbiAgY29uc3QgZ2V0VG9vbHNEaXNwbGF5ID0gKHRvb2xOYW1lczogc3RyaW5nW10gfCB1bmRlZmluZWQpOiBzdHJpbmcgPT4ge1xuICAgIC8vIHVuZGVmaW5lZCBtZWFucyBcImFsbCB0b29sc1wiIHBlciBQUiBzZW1hbnRpY1xuICAgIGlmICh0b29sTmFtZXMgPT09IHVuZGVmaW5lZCkgcmV0dXJuICdBbGwgdG9vbHMnXG4gICAgaWYgKHRvb2xOYW1lcy5sZW5ndGggPT09IDApIHJldHVybiAnTm9uZSdcbiAgICBpZiAodG9vbE5hbWVzLmxlbmd0aCA9PT0gMSkgcmV0dXJuIHRvb2xOYW1lc1swXSB8fCAnTm9uZSdcbiAgICBpZiAodG9vbE5hbWVzLmxlbmd0aCA9PT0gMikgcmV0dXJuIHRvb2xOYW1lcy5qb2luKCcgYW5kICcpXG4gICAgcmV0dXJuIGAke3Rvb2xOYW1lcy5zbGljZSgwLCAtMSkuam9pbignLCAnKX0sIGFuZCAke3Rvb2xOYW1lc1t0b29sTmFtZXMubGVuZ3RoIC0gMV19YFxuICB9XG5cbiAgLy8gQ29tcHV0ZSBtZW1vcnkgZGlzcGxheSBvdXRzaWRlIEpTWFxuICBjb25zdCBtZW1vcnlEaXNwbGF5RWxlbWVudCA9IGlzQXV0b01lbW9yeUVuYWJsZWQoKSA/IChcbiAgICA8VGV4dD5cbiAgICAgIDxUZXh0IGJvbGQ+TWVtb3J5PC9UZXh0Pjoge2dldE1lbW9yeVNjb3BlRGlzcGxheShhZ2VudC5tZW1vcnkpfVxuICAgIDwvVGV4dD5cbiAgKSA6IG51bGxcblxuICByZXR1cm4gKFxuICAgIDxXaXphcmREaWFsb2dMYXlvdXRcbiAgICAgIHN1YnRpdGxlPVwiQ29uZmlybSBhbmQgc2F2ZVwiXG4gICAgICBmb290ZXJUZXh0PXtcbiAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJzL0VudGVyXCIgYWN0aW9uPVwic2F2ZVwiIC8+XG4gICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiZVwiIGFjdGlvbj1cImVkaXQgaW4geW91ciBlZGl0b3JcIiAvPlxuICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgICAgICBmYWxsYmFjaz1cIkVzY1wiXG4gICAgICAgICAgICBkZXNjcmlwdGlvbj1cImNhbmNlbFwiXG4gICAgICAgICAgLz5cbiAgICAgICAgPC9CeWxpbmU+XG4gICAgICB9XG4gICAgPlxuICAgICAgPEJveFxuICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgdGFiSW5kZXg9ezB9XG4gICAgICAgIGF1dG9Gb2N1c1xuICAgICAgICBvbktleURvd249e2hhbmRsZUtleURvd259XG4gICAgICA+XG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIDxUZXh0IGJvbGQ+TmFtZTwvVGV4dD46IHthZ2VudC5hZ2VudFR5cGV9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgPFRleHQgYm9sZD5Mb2NhdGlvbjwvVGV4dD46eycgJ31cbiAgICAgICAgICB7Z2V0TmV3UmVsYXRpdmVBZ2VudEZpbGVQYXRoKHtcbiAgICAgICAgICAgIHNvdXJjZTogd2l6YXJkRGF0YS5sb2NhdGlvbiEsXG4gICAgICAgICAgICBhZ2VudFR5cGU6IGFnZW50LmFnZW50VHlwZSxcbiAgICAgICAgICB9KX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICA8VGV4dCBib2xkPlRvb2xzPC9UZXh0Pjoge2dldFRvb2xzRGlzcGxheShhZ2VudC50b29scyl9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgPFRleHQgYm9sZD5Nb2RlbDwvVGV4dD46IHtnZXRBZ2VudE1vZGVsRGlzcGxheShhZ2VudC5tb2RlbCl9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAge21lbW9yeURpc3BsYXlFbGVtZW50fVxuXG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+RGVzY3JpcHRpb248L1RleHQ+ICh0ZWxscyBDbGF1ZGUgd2hlbiB0byB1c2UgdGhpcyBhZ2VudCk6XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCBtYXJnaW5MZWZ0PXsyfSBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxUZXh0Pnt3aGVuVG9Vc2VQcmV2aWV3fTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgYm9sZD5TeXN0ZW0gcHJvbXB0PC9UZXh0PjpcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94IG1hcmdpbkxlZnQ9ezJ9IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgPFRleHQ+e3N5c3RlbVByb21wdFByZXZpZXd9PC9UZXh0PlxuICAgICAgICA8L0JveD5cblxuICAgICAgICB7dmFsaWRhdGlvbi53YXJuaW5ncy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJ3YXJuaW5nXCI+V2FybmluZ3M6PC9UZXh0PlxuICAgICAgICAgICAge3ZhbGlkYXRpb24ud2FybmluZ3MubWFwKCh3YXJuaW5nLCBpKSA9PiAoXG4gICAgICAgICAgICAgIDxUZXh0IGtleT17aX0gZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgeycgJ31cbiAgICAgICAgICAgICAgICDigKIge3dhcm5pbmd9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICkpfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApfVxuXG4gICAgICAgIHt2YWxpZGF0aW9uLmVycm9ycy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPkVycm9yczo8L1RleHQ+XG4gICAgICAgICAgICB7dmFsaWRhdGlvbi5lcnJvcnMubWFwKChlcnIsIGkpID0+IChcbiAgICAgICAgICAgICAgPFRleHQga2V5PXtpfSBjb2xvcj1cImVycm9yXCI+XG4gICAgICAgICAgICAgICAgeycgJ31cbiAgICAgICAgICAgICAgICDigKIge2Vycn1cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgKSl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG5cbiAgICAgICAge2Vycm9yICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+e2Vycm9yfTwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cblxuICAgICAgICA8Qm94IG1hcmdpblRvcD17Mn0+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+XG4gICAgICAgICAgICBQcmVzcyA8VGV4dCBib2xkPnM8L1RleHQ+IG9yIDxUZXh0IGJvbGQ+RW50ZXI8L1RleHQ+IHRvIHNhdmUseycgJ31cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+ZTwvVGV4dD4gdG8gc2F2ZSBhbmQgZWRpdFxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cbiAgICA8L1dpemFyZERpYWxvZ0xheW91dD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJLEtBQUtDLFNBQVMsUUFBUSxPQUFPO0FBQzdDLGNBQWNDLGFBQWEsUUFBUSwwQ0FBMEM7QUFDN0UsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsb0JBQW9CO0FBQzlDLFNBQVNDLGFBQWEsUUFBUSwwQ0FBMEM7QUFDeEUsU0FBU0MsbUJBQW1CLFFBQVEsNkJBQTZCO0FBQ2pFLGNBQWNDLEtBQUssUUFBUSxxQkFBcUI7QUFDaEQsU0FBU0MscUJBQXFCLFFBQVEsNENBQTRDO0FBQ2xGLGNBQWNDLGVBQWUsUUFBUSw4Q0FBOEM7QUFDbkYsU0FBU0MsZUFBZSxRQUFRLDZCQUE2QjtBQUM3RCxTQUFTQyxvQkFBb0IsUUFBUSxrQ0FBa0M7QUFDdkUsU0FBU0Msd0JBQXdCLFFBQVEsc0NBQXNDO0FBQy9FLFNBQVNDLE1BQU0sUUFBUSxrQ0FBa0M7QUFDekQsU0FBU0Msb0JBQW9CLFFBQVEsZ0RBQWdEO0FBQ3JGLFNBQVNDLFNBQVMsUUFBUSwwQkFBMEI7QUFDcEQsU0FBU0Msa0JBQWtCLFFBQVEsdUNBQXVDO0FBQzFFLFNBQVNDLDJCQUEyQixRQUFRLHlCQUF5QjtB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 \ No newline at end of file diff --git a/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx b/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx new file mode 100644 index 0000000..343eca2 --- /dev/null +++ b/components/agents/new-agent-creation/wizard-steps/ConfirmStepWrapper.tsx @@ -0,0 +1,74 @@ +import chalk from 'chalk'; +import React, { type ReactNode, useCallback, useState } from 'react'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { useSetAppState } from 'src/state/AppState.js'; +import type { Tools } from '../../../../Tool.js'; +import type { AgentDefinition } from '../../../../tools/AgentTool/loadAgentsDir.js'; +import { getActiveAgentsFromList } from '../../../../tools/AgentTool/loadAgentsDir.js'; +import { editFileInEditor } from '../../../../utils/promptEditor.js'; +import { useWizard } from '../../../wizard/index.js'; +import { getNewAgentFilePath, saveAgentToFile } from '../../agentFileUtils.js'; +import type { AgentWizardData } from '../types.js'; +import { ConfirmStep } from './ConfirmStep.js'; +type Props = { + tools: Tools; + existingAgents: AgentDefinition[]; + onComplete: (message: string) => void; +}; +export function ConfirmStepWrapper({ + tools, + existingAgents, + onComplete +}: Props): ReactNode { + const { + wizardData + } = useWizard(); + const [saveError, setSaveError] = useState(null); + const setAppState = useSetAppState(); + const saveAgent = useCallback(async (openInEditor: boolean): Promise => { + if (!wizardData?.finalAgent) return; + try { + await saveAgentToFile(wizardData.location!, wizardData.finalAgent.agentType, wizardData.finalAgent.whenToUse, wizardData.finalAgent.tools, wizardData.finalAgent.getSystemPrompt(), true, wizardData.finalAgent.color, wizardData.finalAgent.model, wizardData.finalAgent.memory); + setAppState(state => { + if (!wizardData.finalAgent) return state; + const allAgents = state.agentDefinitions.allAgents.concat(wizardData.finalAgent); + return { + ...state, + agentDefinitions: { + ...state.agentDefinitions, + activeAgents: getActiveAgentsFromList(allAgents), + allAgents + } + }; + }); + if (openInEditor) { + const filePath = getNewAgentFilePath({ + source: wizardData.location!, + agentType: wizardData.finalAgent.agentType + }); + await editFileInEditor(filePath); + } + logEvent('tengu_agent_created', { + agent_type: wizardData.finalAgent.agentType, + generation_method: wizardData.wasGenerated ? 'generated' : 'manual', + source: wizardData.location!, + tool_count: wizardData.finalAgent.tools?.length ?? 'all', + has_custom_model: !!wizardData.finalAgent.model, + has_custom_color: !!wizardData.finalAgent.color, + has_memory: !!wizardData.finalAgent.memory, + memory_scope: wizardData.finalAgent.memory ?? 'none', + ...(openInEditor ? { + opened_in_editor: true + } : {}) + } as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS); + const message = openInEditor ? `Created agent: ${chalk.bold(wizardData.finalAgent.agentType)} and opened in editor. ` + `If you made edits, restart to load the latest version.` : `Created agent: ${chalk.bold(wizardData.finalAgent.agentType)}`; + onComplete(message); + } catch (err) { + setSaveError(err instanceof Error ? err.message : 'Failed to save agent'); + } + }, [wizardData, onComplete, setAppState]); + const handleSave = useCallback(() => saveAgent(false), [saveAgent]); + const handleSaveAndEdit = useCallback(() => saveAgent(true), [saveAgent]); + return ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjaGFsayIsIlJlYWN0IiwiUmVhY3ROb2RlIiwidXNlQ2FsbGJhY2siLCJ1c2VTdGF0ZSIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsInVzZVNldEFwcFN0YXRlIiwiVG9vbHMiLCJBZ2VudERlZmluaXRpb24iLCJnZXRBY3RpdmVBZ2VudHNGcm9tTGlzdCIsImVkaXRGaWxlSW5FZGl0b3IiLCJ1c2VXaXphcmQiLCJnZXROZXdBZ2VudEZpbGVQYXRoIiwic2F2ZUFnZW50VG9GaWxlIiwiQWdlbnRXaXphcmREYXRhIiwiQ29uZmlybVN0ZXAiLCJQcm9wcyIsInRvb2xzIiwiZXhpc3RpbmdBZ2VudHMiLCJvbkNvbXBsZXRlIiwibWVzc2FnZSIsIkNvbmZpcm1TdGVwV3JhcHBlciIsIndpemFyZERhdGEiLCJzYXZlRXJyb3IiLCJzZXRTYXZlRXJyb3IiLCJzZXRBcHBTdGF0ZSIsInNhdmVBZ2VudCIsIm9wZW5JbkVkaXRvciIsIlByb21pc2UiLCJmaW5hbEFnZW50IiwibG9jYXRpb24iLCJhZ2VudFR5cGUiLCJ3aGVuVG9Vc2UiLCJnZXRTeXN0ZW1Qcm9tcHQiLCJjb2xvciIsIm1vZGVsIiwibWVtb3J5Iiwic3RhdGUiLCJhbGxBZ2VudHMiLCJhZ2VudERlZmluaXRpb25zIiwiY29uY2F0IiwiYWN0aXZlQWdlbnRzIiwiZmlsZVBhdGgiLCJzb3VyY2UiLCJhZ2VudF90eXBlIiwiZ2VuZXJhdGlvbl9tZXRob2QiLCJ3YXNHZW5lcmF0ZWQiLCJ0b29sX2NvdW50IiwibGVuZ3RoIiwiaGFzX2N1c3RvbV9tb2RlbCIsImhhc19jdXN0b21fY29sb3IiLCJoYXNfbWVtb3J5IiwibWVtb3J5X3Njb3BlIiwib3BlbmVkX2luX2VkaXRvciIsImJvbGQiLCJlcnIiLCJFcnJvciIsImhhbmRsZVNhdmUiLCJoYW5kbGVTYXZlQW5kRWRpdCJdLCJzb3VyY2VzIjpbIkNvbmZpcm1TdGVwV3JhcHBlci50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJ1xuaW1wb3J0IFJlYWN0LCB7IHR5cGUgUmVhY3ROb2RlLCB1c2VDYWxsYmFjaywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7XG4gIHR5cGUgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgbG9nRXZlbnQsXG59IGZyb20gJ3NyYy9zZXJ2aWNlcy9hbmFseXRpY3MvaW5kZXguanMnXG5pbXBvcnQgeyB1c2VTZXRBcHBTdGF0ZSB9IGZyb20gJ3NyYy9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgVG9vbHMgfSBmcm9tICcuLi8uLi8uLi8uLi9Ub29sLmpzJ1xuaW1wb3J0IHR5cGUgeyBBZ2VudERlZmluaXRpb24gfSBmcm9tICcuLi8uLi8uLi8uLi90b29scy9BZ2VudFRvb2wvbG9hZEFnZW50c0Rpci5qcydcbmltcG9ydCB7IGdldEFjdGl2ZUFnZW50c0Zyb21MaXN0IH0gZnJvbSAnLi4vLi4vLi4vLi4vdG9vbHMvQWdlbnRUb29sL2xvYWRBZ2VudHNEaXIuanMnXG5pbXBvcnQgeyBlZGl0RmlsZUluRWRpdG9yIH0gZnJvbSAnLi4vLi4vLi4vLi4vdXRpbHMvcHJvbXB0RWRpdG9yLmpzJ1xuaW1wb3J0IHsgdXNlV2l6YXJkIH0gZnJvbSAnLi4vLi4vLi4vd2l6YXJkL2luZGV4LmpzJ1xuaW1wb3J0IHsgZ2V0TmV3QWdlbnRGaWxlUGF0aCwgc2F2ZUFnZW50VG9GaWxlIH0gZnJvbSAnLi4vLi4vYWdlbnRGaWxlVXRpbHMuanMnXG5pbXBvcnQgdHlwZSB7IEFnZW50V2l6YXJkRGF0YSB9IGZyb20gJy4uL3R5cGVzLmpzJ1xuaW1wb3J0IHsgQ29uZmlybVN0ZXAgfSBmcm9tICcuL0NvbmZpcm1TdGVwLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0b29sczogVG9vbHNcbiAgZXhpc3RpbmdBZ2VudHM6IEFnZW50RGVmaW5pdGlvbltdXG4gIG9uQ29tcGxldGU6IChtZXNzYWdlOiBzdHJpbmcpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIENvbmZpcm1TdGVwV3JhcHBlcih7XG4gIHRvb2xzLFxuICBleGlzdGluZ0FnZW50cyxcbiAgb25Db21wbGV0ZSxcbn06IFByb3BzKTogUmVhY3ROb2RlIHtcbiAgY29uc3QgeyB3aXphcmREYXRhIH0gPSB1c2VXaXphcmQ8QWdlbnRXaXphcmREYXRhPigpXG4gIGNvbnN0IFtzYXZlRXJyb3IsIHNldFNhdmVFcnJvcl0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICBjb25zdCBzZXRBcHBTdGF0ZSA9IHVzZVNldEFwcFN0YXRlKClcblxuICBjb25zdCBzYXZlQWdlbnQgPSB1c2VDYWxsYmFjayhcbiAgICBhc3luYyAob3BlbkluRWRpdG9yOiBib29sZWFuKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgICBpZiAoIXdpemFyZERhdGE/LmZpbmFsQWdlbnQpIHJldHVyblxuXG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBzYXZlQWdlbnRUb0ZpbGUoXG4gICAgICAgICAgd2l6YXJkRGF0YS5sb2NhdGlvbiEsXG4gICAgICAgICAgd2l6YXJkRGF0YS5maW5hbEFnZW50LmFnZW50VHlwZSxcbiAgICAgICAgICB3aXphcmREYXRhLmZpbmFsQWdlbnQud2hlblRvVXNlLFxuICAgICAgICAgIHdpemFyZERhdGEuZmluYWxBZ2VudC50b29scyxcbiAgICAgICAgICB3aXphcmREYXRhLmZpbmFsQWdlbnQuZ2V0U3lzdGVtUHJvbXB0KCksXG4gICAgICAgICAgdHJ1ZSxcbiAgICAgICAgICB3aXphcmREYXRhLmZpbmFsQWdlbnQuY29sb3IsXG4gICAgICAgICAgd2l6YXJkRGF0YS5maW5hbEFnZW50Lm1vZGVsLFxuICAgICAgICAgIHdpemFyZERhdGEuZmluYWxBZ2VudC5tZW1vcnksXG4gICAgICAgIClcblxuICAgICAgICBzZXRBcHBTdGF0ZShzdGF0ZSA9PiB7XG4gICAgICAgICAgaWYgKCF3aXphcmREYXRhLmZpbmFsQWdlbnQpIHJldHVybiBzdGF0ZVxuXG4gICAgICAgICAgY29uc3QgYWxsQWdlbnRzID0gc3RhdGUuYWdlbnREZWZpbml0aW9ucy5hbGxBZ2VudHMuY29uY2F0KFxuICAgICAgICAgICAgd2l6YXJkRGF0YS5maW5hbEFnZW50LFxuICAgICAgICAgIClcbiAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgLi4uc3RhdGUsXG4gICAgICAgICAgICBhZ2VudERlZmluaXRpb25zOiB7XG4gICAgICAgICAgICAgIC4uLnN0YXRlLmFnZW50RGVmaW5pdGlvbnMsXG4gICAgICAgICAgICAgIGFjdGl2ZUFnZW50czogZ2V0QWN0aXZlQWdlbnRzRnJvbUxpc3QoYWxsQWdlbnRzKSxcbiAgICAgICAgICAgICAgYWxsQWdlbnRzLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9XG4gICAgICAgIH0pXG5cbiAgICAgICAgaWYgKG9wZW5JbkVkaXRvcikge1xuICAgICAgICAgIGNvbnN0IGZpbGVQYXRoID0gZ2V0TmV3QWdlbnRGaWxlUGF0aCh7XG4gICAgICAgICAgICBzb3VyY2U6IHdpemFyZERhdGEubG9jYXRpb24hLFxuICAgICAgICAgICAgYWdlbnRUeXBlOiB3aXphcmREYXRhLmZpbmFsQWdlbnQuYWdlbnRUeXBlLFxuICAgICAgICAgIH0pXG4gICAgICAgICAgYXdhaXQgZWRpdEZpbGVJbkVkaXRvcihmaWxlUGF0aClcbiAgICAgICAgfVxuXG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9hZ2VudF9jcmVhdGVkJywge1xuICAgICAgICAgIGFnZW50X3R5cGU6IHdpemFyZERhdGEuZmluYWxBZ2VudC5hZ2VudFR5cGUsXG4gICAgICAgICAgZ2VuZXJhdGlvbl9tZXRob2Q6IHdpemFyZERhdGEud2FzR2VuZXJhdGVkID8gJ2dlbmVyYXRlZCcgOiAnbWFudWFsJyxcbiAgICAgICAgICBzb3VyY2U6IHdpemFyZERhdGEubG9jYXRpb24hLFxuICAgICAgICAgIHRvb2xfY291bnQ6IHdpemFyZERhdGEuZmluYWxBZ2VudC50b29scz8ubGVuZ3RoID8/ICdhbGwnLFxuICAgICAgICAgIGhhc19jdXN0b21fbW9kZWw6ICEhd2l6YXJkRGF0YS5maW5hbEFnZW50Lm1vZGVsLFxuICAgICAgICAgIGhhc19jdXN0b21fY29sb3I6ICEhd2l6YXJkRGF0YS5maW5hbEFnZW50LmNvbG9yLFxuICAgICAgICAgIGhhc19tZW1vcnk6ICEhd2l6YXJkRGF0YS5maW5hbEFnZW50Lm1lbW9yeSxcbiAgICAgICAgICBtZW1vcnlfc2NvcGU6IHdpemFyZERhdGEuZmluYWxBZ2VudC5tZW1vcnkgPz8gJ25vbmUnLFxuICAgICAgICAgIC4uLihvcGVuSW5FZGl0b3IgPyB7IG9wZW5lZF9pbl9lZGl0b3I6IHRydWUgfSA6IHt9KSxcbiAgICAgICAgfSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTKVxuXG4gICAgICAgIGNvbnN0IG1lc3NhZ2UgPSBvcGVuSW5FZGl0b3JcbiAgICAgICAgICA/IGBDcmVhdGVkIGFnZW50OiAke2NoYWxrLmJvbGQod2l6YXJkRGF0YS5maW5hbEFnZW50LmFnZW50VHlwZSl9IGFuZCBvcGVuZWQgaW4gZWRpdG9yLiBgICtcbiAgICAgICAgICAgIGBJZiB5b3UgbWFkZSBlZGl0cywgcmVzdGFydCB0byBsb2FkIHRoZSBsYXRlc3QgdmVyc2lvbi5gXG4gICAgICAgICAgOiBgQ3JlYXRlZCBhZ2VudDogJHtjaGFsay5ib2xkKHdpemFyZERhdGEuZmluYWxBZ2VudC5hZ2VudFR5cGUpfWBcbiAgICAgICAgb25Db21wbGV0ZShtZXNzYWdlKVxuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIHNldFNhdmVFcnJvcihcbiAgICAgICAgICBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogJ0ZhaWxlZCB0byBzYXZlIGFnZW50JyxcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH0sXG4gICAgW3dpemFyZERhdGEsIG9uQ29tcGxldGUsIHNldEFwcFN0YXRlXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZVNhdmUgPSB1c2VDYWxsYmFjaygoKSA9PiBzYXZlQWdlbnQoZmFsc2UpLCBbc2F2ZUFnZW50XSlcblxuICBjb25zdCBoYW5kbGVTYXZlQW5kRWRpdCA9IHVzZUNhbGxiYWNrKCgpID0+IHNhdmVBZ2VudCh0cnVlKSwgW3NhdmVBZ2VudF0pXG5cbiAgcmV0dXJuIChcbiAgICA8Q29uZmlybVN0ZXBcbiAgICAgIHRvb2xzPXt0b29sc31cbiAgICAgIGV4aXN0aW5nQWdlbnRzPXtleGlzdGluZ0FnZW50c31cbiAgICAgIG9uU2F2ZT17aGFuZGxlU2F2ZX1cbiAgICAgIG9uU2F2ZUFuZEVkaXQ9e2hhbmRsZVNhdmVBbmRFZGl0fVxuICAgICAgZXJyb3I9e3NhdmVFcnJvcn1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLE9BQU9DLEtBQUssSUFBSSxLQUFLQyxTQUFTLEVBQUVDLFdBQVcsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDcEUsU0FDRSxLQUFLQywwREFBMEQsRUFDL0RDLFFBQVEsUUFDSCxpQ0FBaUM7QUFDeEMsU0FBU0MsY0FBYyxRQUFRLHVCQUF1QjtBQUN0RCxjQUFjQyxLQUFLLFFBQVEscUJBQXFCO0FBQ2hELGNBQWNDLGVBQWUsUUFBUSw4Q0FBOEM7QUFDbkYsU0FBU0MsdUJBQXVCLFFBQVEsOENBQThDO0FBQ3RGLFNBQVNDLGdCQUFnQixRQUFRLG1DQUFtQztBQUNwRSxTQUFTQyxTQUFTLFFBQVEsMEJBQTBCO0FBQ3BELFNBQVNDLG1CQUFtQixFQUFFQyxlQUFlLFFBQVEseUJBQXlCO0FBQzlFLGNBQWNDLGVBQWUsUUFBUSxhQUFhO0FBQ2xELFNBQVNDLFdBQVcsUUFBUSxrQkFBa0I7QUFFOUMsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLEtBQUssRUFBRVYsS0FBSztFQUNaVyxjQUFjLEVBQUVW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 \ No newline at end of file diff --git a/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx b/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx new file mode 100644 index 0000000..ff6c3a7 --- /dev/null +++ b/components/agents/new-agent-creation/wizard-steps/DescriptionStep.tsx @@ -0,0 +1,123 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode, useCallback, useState } from 'react'; +import { Box, Text } from '../../../../ink.js'; +import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; +import { editPromptInEditor } from '../../../../utils/promptEditor.js'; +import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; +import { Byline } from '../../../design-system/Byline.js'; +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js'; +import TextInput from '../../../TextInput.js'; +import { useWizard } from '../../../wizard/index.js'; +import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js'; +import type { AgentWizardData } from '../types.js'; +export function DescriptionStep() { + const $ = _c(18); + const { + goNext, + goBack, + updateWizardData, + wizardData + } = useWizard(); + const [whenToUse, setWhenToUse] = useState(wizardData.whenToUse || ""); + const [cursorOffset, setCursorOffset] = useState(whenToUse.length); + const [error, setError] = useState(null); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { + context: "Settings" + }; + $[0] = t0; + } else { + t0 = $[0]; + } + useKeybinding("confirm:no", goBack, t0); + let t1; + if ($[1] !== whenToUse) { + t1 = async () => { + const result = await editPromptInEditor(whenToUse); + if (result.content !== null) { + setWhenToUse(result.content); + setCursorOffset(result.content.length); + } + }; + $[1] = whenToUse; + $[2] = t1; + } else { + t1 = $[2]; + } + const handleExternalEditor = t1; + let t2; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t2 = { + context: "Chat" + }; + $[3] = t2; + } else { + t2 = $[3]; + } + useKeybinding("chat:externalEditor", handleExternalEditor, t2); + let t3; + if ($[4] !== goNext || $[5] !== updateWizardData) { + t3 = value => { + const trimmedValue = value.trim(); + if (!trimmedValue) { + setError("Description is required"); + return; + } + setError(null); + updateWizardData({ + whenToUse: trimmedValue + }); + goNext(); + }; + $[4] = goNext; + $[5] = updateWizardData; + $[6] = t3; + } else { + t3 = $[6]; + } + const handleSubmit = t3; + let t4; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t4 = ; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t5 = When should Claude use this agent?; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] !== cursorOffset || $[10] !== handleSubmit || $[11] !== whenToUse) { + t6 = ; + $[9] = cursorOffset; + $[10] = handleSubmit; + $[11] = whenToUse; + $[12] = t6; + } else { + t6 = $[12]; + } + let t7; + if ($[13] !== error) { + t7 = error && {error}; + $[13] = error; + $[14] = t7; + } else { + t7 = $[14]; + } + let t8; + if ($[15] !== t6 || $[16] !== t7) { + t8 = {t5}{t6}{t7}; + $[15] = t6; + $[16] = t7; + $[17] = t8; + } else { + t8 = $[17]; + } + return t8; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsInVzZUNhbGxiYWNrIiwidXNlU3RhdGUiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZyIsImVkaXRQcm9tcHRJbkVkaXRvciIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIkJ5bGluZSIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwiVGV4dElucHV0IiwidXNlV2l6YXJkIiwiV2l6YXJkRGlhbG9nTGF5b3V0IiwiQWdlbnRXaXphcmREYXRhIiwiRGVzY3JpcHRpb25TdGVwIiwiJCIsIl9jIiwiZ29OZXh0IiwiZ29CYWNrIiwidXBkYXRlV2l6YXJkRGF0YSIsIndpemFyZERhdGEiLCJ3aGVuVG9Vc2UiLCJzZXRXaGVuVG9Vc2UiLCJjdXJzb3JPZmZzZXQiLCJzZXRDdXJzb3JPZmZzZXQiLCJsZW5ndGgiLCJlcnJvciIsInNldEVycm9yIiwidDAiLCJTeW1ib2wiLCJmb3IiLCJjb250ZXh0IiwidDEiLCJyZXN1bHQiLCJjb250ZW50IiwiaGFuZGxlRXh0ZXJuYWxFZGl0b3IiLCJ0MiIsInQzIiwidmFsdWUiLCJ0cmltbWVkVmFsdWUiLCJ0cmltIiwiaGFuZGxlU3VibWl0IiwidDQiLCJ0NSIsInQ2IiwidDciLCJ0OCJdLCJzb3VyY2VzIjpbIkRlc2NyaXB0aW9uU3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHR5cGUgUmVhY3ROb2RlLCB1c2VDYWxsYmFjaywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uLy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi8uLi8uLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgZWRpdFByb21wdEluRWRpdG9yIH0gZnJvbSAnLi4vLi4vLi4vLi4vdXRpbHMvcHJvbXB0RWRpdG9yLmpzJ1xuaW1wb3J0IHsgQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vLi4vLi4vQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vLi4vLi4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4uLy4uLy4uL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgVGV4dElucHV0IGZyb20gJy4uLy4uLy4uL1RleHRJbnB1dC5qcydcbmltcG9ydCB7IHVzZVdpemFyZCB9IGZyb20gJy4uLy4uLy4uL3dpemFyZC9pbmRleC5qcydcbmltcG9ydCB7IFdpemFyZERpYWxvZ0xheW91dCB9IGZyb20gJy4uLy4uLy4uL3dpemFyZC9XaXphcmREaWFsb2dMYXlvdXQuanMnXG5pbXBvcnQgdHlwZSB7IEFnZW50V2l6YXJkRGF0YSB9IGZyb20gJy4uL3R5cGVzLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gRGVzY3JpcHRpb25TdGVwKCk6IFJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgZ29OZXh0LCBnb0JhY2ssIHVwZGF0ZVdpemFyZERhdGEsIHdpemFyZERhdGEgfSA9XG4gICAgdXNlV2l6YXJkPEFnZW50V2l6YXJkRGF0YT4oKVxuICBjb25zdCBbd2hlblRvVXNlLCBzZXRXaGVuVG9Vc2VdID0gdXNlU3RhdGUod2l6YXJkRGF0YS53aGVuVG9Vc2UgfHwgJycpXG4gIGNvbnN0IFtjdXJzb3JPZmZzZXQsIHNldEN1cnNvck9mZnNldF0gPSB1c2VTdGF0ZSh3aGVuVG9Vc2UubGVuZ3RoKVxuICBjb25zdCBbZXJyb3IsIHNldEVycm9yXSA9IHVzZVN0YXRlPHN0cmluZyB8IG51bGw+KG51bGwpXG5cbiAgLy8gSGFuZGxlIGVzY2FwZSBrZXkgLSB1c2UgU2V0dGluZ3MgY29udGV4dCBzbyAnbicga2V5IGRvZXNuJ3QgY2FuY2VsIChhbGxvd3MgdHlwaW5nICduJyBpbiBpbnB1dClcbiAgdXNlS2V5YmluZGluZygnY29uZmlybTpubycsIGdvQmFjaywgeyBjb250ZXh0OiAnU2V0dGluZ3MnIH0pXG5cbiAgY29uc3QgaGFuZGxlRXh0ZXJuYWxFZGl0b3IgPSB1c2VDYWxsYmFjayhhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZWRpdFByb21wdEluRWRpdG9yKHdoZW5Ub1VzZSlcbiAgICBpZiAocmVzdWx0LmNvbnRlbnQgIT09IG51bGwpIHtcbiAgICAgIHNldFdoZW5Ub1VzZShyZXN1bHQuY29udGVudClcbiAgICAgIHNldEN1cnNvck9mZnNldChyZXN1bHQuY29udGVudC5sZW5ndGgpXG4gICAgfVxuICB9LCBbd2hlblRvVXNlXSlcblxuICB1c2VLZXliaW5kaW5nKCdjaGF0OmV4dGVybmFsRWRpdG9yJywgaGFuZGxlRXh0ZXJuYWxFZGl0b3IsIHtcbiAgICBjb250ZXh0OiAnQ2hhdCcsXG4gIH0pXG5cbiAgY29uc3QgaGFuZGxlU3VibWl0ID0gKHZhbHVlOiBzdHJpbmcpOiB2b2lkID0+IHtcbiAgICBjb25zdCB0cmltbWVkVmFsdWUgPSB2YWx1ZS50cmltKClcbiAgICBpZiAoIXRyaW1tZWRWYWx1ZSkge1xuICAgICAgc2V0RXJyb3IoJ0Rlc2NyaXB0aW9uIGlzIHJlcXVpcmVkJylcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHNldEVycm9yKG51bGwpXG4gICAgdXBkYXRlV2l6YXJkRGF0YSh7IHdoZW5Ub1VzZTogdHJpbW1lZFZhbHVlIH0pXG4gICAgZ29OZXh0KClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPFdpemFyZERpYWxvZ0xheW91dFxuICAgICAgc3VidGl0bGU9XCJEZXNjcmlwdGlvbiAodGVsbCBDbGF1ZGUgd2hlbiB0byB1c2UgdGhpcyBhZ2VudClcIlxuICAgICAgZm9vdGVyVGV4dD17XG4gICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiVHlwZVwiIGFjdGlvbj1cImVudGVyIHRleHRcIiAvPlxuICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVudGVyXCIgYWN0aW9uPVwiY29udGludWVcIiAvPlxuICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgIGFjdGlvbj1cImNoYXQ6ZXh0ZXJuYWxFZGl0b3JcIlxuICAgICAgICAgICAgY29udGV4dD1cIkNoYXRcIlxuICAgICAgICAgICAgZmFsbGJhY2s9XCJjdHJsK2dcIlxuICAgICAgICAgICAgZGVzY3JpcHRpb249XCJvcGVuIGluIGVkaXRvclwiXG4gICAgICAgICAgLz5cbiAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgIGNvbnRleHQ9XCJTZXR0aW5nc1wiXG4gICAgICAgICAgICBmYWxsYmFjaz1cIkVzY1wiXG4gICAgICAgICAgICBkZXNjcmlwdGlvbj1cImdvIGJhY2tcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQnlsaW5lPlxuICAgICAgfVxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8VGV4dD5XaGVuIHNob3VsZCBDbGF1ZGUgdXNlIHRoaXMgYWdlbnQ/PC9UZXh0PlxuXG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dElucHV0XG4gICAgICAgICAgICB2YWx1ZT17d2hlblRvVXNlfVxuICAgICAgICAgICAgb25DaGFuZ2U9e3NldFdoZW5Ub1VzZX1cbiAgICAgICAgICAgIG9uU3VibWl0PXtoYW5kbGVTdWJtaXR9XG4gICAgICAgICAgICBwbGFjZWhvbGRlcj1cImUuZy4sIHVzZSB0aGlzIGFnZW50IGFmdGVyIHlvdSdyZSBkb25lIHdyaXRpbmcgY29kZS4uLlwiXG4gICAgICAgICAgICBjb2x1bW5zPXs4MH1cbiAgICAgICAgICAgIGN1cnNvck9mZnNldD17Y3Vyc29yT2Zmc2V0fVxuICAgICAgICAgICAgb25DaGFuZ2VDdXJzb3JPZmZzZXQ9e3NldEN1cnNvck9mZnNldH1cbiAgICAgICAgICAgIGZvY3VzXG4gICAgICAgICAgICBzaG93Q3Vyc29yXG4gICAgICAgICAgLz5cbiAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAge2Vycm9yICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+e2Vycm9yfTwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgIDwvQm94PlxuICAgIDwvV2l6YXJkRGlhbG9nTGF5b3V0PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUksS0FBS0MsU0FBUyxFQUFFQyxXQUFXLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3BFLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLG9CQUFvQjtBQUM5QyxTQUFTQyxhQUFhLFFBQVEsMENBQTBDO0FBQ3hFLFNBQVNDLGtCQUFrQixRQUFRLG1DQUFtQztBQUN0RSxTQUFTQyx3QkFBd0IsUUFBUSxzQ0FBc0M7QUFDL0UsU0FBU0MsTUFBTSxRQUFRLGtDQUFrQztBQUN6RCxTQUFTQyxvQkFBb0IsUUFBUSxnREFBZ0Q7QUFDckYsT0FBT0MsU0FBUyxNQUFNLHVCQUF1QjtBQUM3QyxTQUFTQyxTQUFTLFFBQVEsMEJBQTBCO0FBQ3BELFNBQVNDLGtCQUFrQixRQUFRLHVDQUF1QztBQUMxRSxjQUFjQyxlQUFlLFFBQVEsYUFBYTtBQUVsRCxPQUFPLFNBQUFDLGdCQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQ0w7SUFBQUMsTUFBQTtJQUFBQyxNQUFBO0lBQUFDLGdCQUFBO0lBQUFDO0VBQUEsSUFDRVQsU0FBUyxDQUFrQixDQUFDO0VBQzlCLE9BQUFVLFNBQUEsRUFBQUMsWUFBQSxJQUFrQ3BCLFFBQVEsQ0FBQ2tCLFVBQVUsQ0FBQUMsU0FBZ0IsSUFBMUIsRUFBMEIsQ0FB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 \ No newline at end of file diff --git a/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx b/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx new file mode 100644 index 0000000..d17ee69 --- /dev/null +++ b/components/agents/new-agent-creation/wizard-steps/GenerateStep.tsx @@ -0,0 +1,143 @@ +import { APIUserAbortError } from '@anthropic-ai/sdk'; +import React, { type ReactNode, useCallback, useRef, useState } from 'react'; +import { useMainLoopModel } from '../../../../hooks/useMainLoopModel.js'; +import { Box, Text } from '../../../../ink.js'; +import { useKeybinding } from '../../../../keybindings/useKeybinding.js'; +import { createAbortController } from '../../../../utils/abortController.js'; +import { editPromptInEditor } from '../../../../utils/promptEditor.js'; +import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; +import { Byline } from '../../../design-system/Byline.js'; +import { Spinner } from '../../../Spinner.js'; +import TextInput from '../../../TextInput.js'; +import { useWizard } from '../../../wizard/index.js'; +import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js'; +import { generateAgent } from '../../generateAgent.js'; +import type { AgentWizardData } from '../types.js'; +export function GenerateStep(): ReactNode { + const { + updateWizardData, + goBack, + goToStep, + wizardData + } = useWizard(); + const [prompt, setPrompt] = useState(wizardData.generationPrompt || ''); + const [isGenerating, setIsGenerating] = useState(false); + const [error, setError] = useState(null); + const [cursorOffset, setCursorOffset] = useState(prompt.length); + const model = useMainLoopModel(); + const abortControllerRef = useRef(null); + + // Cancel generation when escape pressed during generation + const handleCancelGeneration = useCallback(() => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + abortControllerRef.current = null; + setIsGenerating(false); + setError('Generation cancelled'); + } + }, []); + + // Use Settings context so 'n' key doesn't cancel (allows typing 'n' in prompt input) + useKeybinding('confirm:no', handleCancelGeneration, { + context: 'Settings', + isActive: isGenerating + }); + const handleExternalEditor = useCallback(async () => { + const result = await editPromptInEditor(prompt); + if (result.content !== null) { + setPrompt(result.content); + setCursorOffset(result.content.length); + } + }, [prompt]); + useKeybinding('chat:externalEditor', handleExternalEditor, { + context: 'Chat', + isActive: !isGenerating + }); + + // Go back when escape pressed while not generating + const handleGoBack = useCallback(() => { + updateWizardData({ + generationPrompt: '', + agentType: '', + systemPrompt: '', + whenToUse: '', + generatedAgent: undefined, + wasGenerated: false + }); + setPrompt(''); + setError(null); + goBack(); + }, [updateWizardData, goBack]); + + // Use Settings context so 'n' key doesn't cancel (allows typing 'n' in prompt input) + useKeybinding('confirm:no', handleGoBack, { + context: 'Settings', + isActive: !isGenerating + }); + const handleGenerate = async (): Promise => { + const trimmedPrompt = prompt.trim(); + if (!trimmedPrompt) { + setError('Please describe what the agent should do'); + return; + } + setError(null); + setIsGenerating(true); + updateWizardData({ + generationPrompt: trimmedPrompt, + isGenerating: true + }); + + // Create abort controller for this generation + const controller = createAbortController(); + abortControllerRef.current = controller; + try { + const generated = await generateAgent(trimmedPrompt, model, [], controller.signal); + updateWizardData({ + agentType: generated.identifier, + whenToUse: generated.whenToUse, + systemPrompt: generated.systemPrompt, + generatedAgent: generated, + isGenerating: false, + wasGenerated: true + }); + + // Skip directly to ToolsStep (index 6) - matching original flow + goToStep(6); + } catch (err) { + // Don't show error if it was cancelled (already set in escape handler) + if (err instanceof APIUserAbortError) { + // User cancelled - no error to show + } else if (err instanceof Error && !err.message.includes('No assistant message found')) { + setError(err.message || 'Failed to generate agent'); + } + updateWizardData({ + isGenerating: false + }); + } finally { + setIsGenerating(false); + abortControllerRef.current = null; + } + }; + const subtitle = 'Describe what this agent should do and when it should be used (be comprehensive for best results)'; + if (isGenerating) { + return }> + + + Generating agent from description... + + ; + } + return + + + + }> + + {error && + {error} + } + + + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJBUElVc2VyQWJvcnRFcnJvciIsIlJlYWN0IiwiUmVhY3ROb2RlIiwidXNlQ2FsbGJhY2siLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsInVzZU1haW5Mb29wTW9kZWwiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZyIsImNyZWF0ZUFib3J0Q29udHJvbGxlciIsImVkaXRQcm9tcHRJbkVkaXRvciIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIkJ5bGluZSIsIlNwaW5uZXIiLCJUZXh0SW5wdXQiLCJ1c2VXaXphcmQiLCJXaXphcmREaWFsb2dMYXlvdXQiLCJnZW5lcmF0ZUFnZW50IiwiQWdlbnRXaXphcmREYXRhIiwiR2VuZXJhdGVTdGVwIiwidXBkYXRlV2l6YXJkRGF0YSIsImdvQmFjayIsImdvVG9TdGVwIiwid2l6YXJkRGF0YSIsInByb21wdCIsInNldFByb21wdCIsImdlbmVyYXRpb25Qcm9tcHQiLCJpc0dlbmVyYXRpbmciLCJzZXRJc0dlbmVyYXRpbmciLCJlcnJvciIsInNldEVycm9yIiwiY3Vyc29yT2Zmc2V0Iiwic2V0Q3Vyc29yT2Zmc2V0IiwibGVuZ3RoIiwibW9kZWwiLCJhYm9ydENvbnRyb2xsZXJSZWYiLCJBYm9ydENvbnRyb2xsZXIiLCJoYW5kbGVDYW5jZWxHZW5lcmF0aW9uIiwiY3VycmVudCIsImFib3J0IiwiY29udGV4dCIsImlzQWN0aXZlIiwiaGFuZGxlRXh0ZXJuYWxFZGl0b3IiLCJyZXN1bHQiLCJjb250ZW50IiwiaGFuZGxlR29CYWNrIiwiYWdlbnRUeXBlIiwic3lzdGVtUHJvbXB0Iiwid2hlblRvVXNlIiwiZ2VuZXJhdGVkQWdlbnQiLCJ1bmRlZmluZWQiLCJ3YXNHZW5lcmF0ZWQiLCJoYW5kbGVHZW5lcmF0ZSIsIlByb21pc2UiLCJ0cmltbWVkUHJvbXB0IiwidHJpbSIsImNvbnRyb2xsZXIiLCJnZW5lcmF0ZWQiLCJzaWduYWwiLCJpZGVudGlmaWVyIiwiZXJyIiwiRXJyb3IiLCJtZXNzYWdlIiwiaW5jbHVkZXMiLCJzdWJ0aXRsZSJdLCJzb3VyY2VzIjpbIkdlbmVyYXRlU3RlcC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQVBJVXNlckFib3J0RXJyb3IgfSBmcm9tICdAYW50aHJvcGljLWFpL3NkaydcbmltcG9ydCBSZWFjdCwgeyB0eXBlIFJlYWN0Tm9kZSwgdXNlQ2FsbGJhY2ssIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZU1haW5Mb29wTW9kZWwgfSBmcm9tICcuLi8uLi8uLi8uLi9ob29rcy91c2VNYWluTG9vcE1vZGVsLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uLy4uLy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyBjcmVhdGVBYm9ydENvbnRyb2xsZXIgfSBmcm9tICcuLi8uLi8uLi8uLi91dGlscy9hYm9ydENvbnRyb2xsZXIuanMnXG5pbXBvcnQgeyBlZGl0UHJvbXB0SW5FZGl0b3IgfSBmcm9tICcuLi8uLi8uLi8uLi91dGlscy9wcm9tcHRFZGl0b3IuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi8uLi8uLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi8uLi8uLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IFNwaW5uZXIgfSBmcm9tICcuLi8uLi8uLi9TcGlubmVyLmpzJ1xuaW1wb3J0IFRleHRJbnB1dCBmcm9tICcuLi8uLi8uLi9UZXh0SW5wdXQuanMnXG5pbXBvcnQgeyB1c2VXaXphcmQgfSBmcm9tICcuLi8uLi8uLi93aXphcmQvaW5kZXguanMnXG5pbXBvcnQgeyBXaXphcmREaWFsb2dMYXlvdXQgfSBmcm9tICcuLi8uLi8uLi93aXphcmQvV2l6YXJkRGlhbG9nTGF5b3V0LmpzJ1xuaW1wb3J0IHsgZ2VuZXJhdGVBZ2VudCB9IGZyb20gJy4uLy4uL2dlbmVyYXRlQWdlbnQuanMnXG5pbXBvcnQgdHlwZSB7IEFnZW50V2l6YXJkRGF0YSB9IGZyb20gJy4uL3R5cGVzLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gR2VuZXJhdGVTdGVwKCk6IFJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgdXBkYXRlV2l6YXJkRGF0YSwgZ29CYWNrLCBnb1RvU3RlcCwgd2l6YXJkRGF0YSB9ID1cbiAgICB1c2VXaXphcmQ8QWdlbnRXaXphcmREYXRhPigpXG4gIGNvbnN0IFtwcm9tcHQsIHNldFByb21wdF0gPSB1c2VTdGF0ZSh3aXphcmREYXRhLmdlbmVyYXRpb25Qcm9tcHQgfHwgJycpXG4gIGNvbnN0IFtpc0dlbmVyYXRpbmcsIHNldElzR2VuZXJhdGluZ10gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW2Vycm9yLCBzZXRFcnJvcl0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICBjb25zdCBbY3Vyc29yT2Zmc2V0LCBzZXRDdXJzb3JPZmZzZXRdID0gdXNlU3RhdGUocHJvbXB0Lmxlbmd0aClcbiAgY29uc3QgbW9kZWwgPSB1c2VNYWluTG9vcE1vZGVsKClcbiAgY29uc3QgYWJvcnRDb250cm9sbGVyUmVmID0gdXNlUmVmPEFib3J0Q29udHJvbGxlciB8IG51bGw+KG51bGwpXG5cbiAgLy8gQ2FuY2VsIGdlbmVyYXRpb24gd2hlbiBlc2NhcGUgcHJlc3NlZCBkdXJpbmcgZ2VuZXJhdGlvblxuICBjb25zdCBoYW5kbGVDYW5jZWxHZW5lcmF0aW9uID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChhYm9ydENvbnRyb2xsZXJSZWYuY3VycmVudCkge1xuICAgICAgYWJvcnRDb250cm9sbGVyUmVmLmN1cnJlbnQuYWJvcnQoKVxuICAgICAgYWJvcnRDb250cm9sbGVyUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICBzZXRJc0dlbmVyYXRpbmcoZmFsc2UpXG4gICAgICBzZXRFcnJvcignR2VuZXJhdGlvbiBjYW5jZWxsZWQnKVxuICAgIH1cbiAgfSwgW10pXG5cbiAgLy8gVXNlIFNldHRpbmdzIGNvbnRleHQgc28gJ24nIGtleSBkb2Vzbid0IGNhbmNlbCAoYWxsb3dzIHR5cGluZyAnbicgaW4gcHJvbXB0IGlucHV0KVxuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOm5vJywgaGFuZGxlQ2FuY2VsR2VuZXJhdGlvbiwge1xuICAgIGNvbnRleHQ6ICdTZXR0aW5ncycsXG4gICAgaXNBY3RpdmU6IGlzR2VuZXJhdGluZyxcbiAgfSlcblxuICBjb25zdCBoYW5kbGVFeHRlcm5hbEVkaXRvciA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBlZGl0UHJvbXB0SW5FZGl0b3IocHJvbXB0KVxuICAgIGlmIChyZXN1bHQuY29udGVudCAhPT0gbnVsbCkge1xuICAgICAgc2V0UHJvbXB0KHJlc3VsdC5jb250ZW50KVxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KHJlc3VsdC5jb250ZW50Lmxlbmd0aClcbiAgICB9XG4gIH0sIFtwcm9tcHRdKVxuXG4gIHVzZUtleWJpbmRpbmcoJ2NoYXQ6ZXh0ZXJuYWxFZGl0b3InLCBoYW5kbGVFeHRlcm5hbEVkaXRvciwge1xuICAgIGNvbnRleHQ6ICdDaGF0JyxcbiAgICBpc0FjdGl2ZTogIWlzR2VuZXJhdGluZyxcbiAgfSlcblxuICAvLyBHbyBiYWNrIHdoZW4gZXNjYXBlIHByZXNzZWQgd2hpbGUgbm90IGdlbmVyYXRpbmdcbiAgY29uc3QgaGFuZGxlR29CYWNrID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHVwZGF0ZVdpemFyZERhdGEoe1xuICAgICAgZ2VuZXJhdGlvblByb21wdDogJycsXG4gICAgICBhZ2VudFR5cGU6ICcnLFxuICAgICAgc3lzdGVtUHJvbXB0OiAnJyxcbiAgICAgIHdoZW5Ub1VzZTogJycsXG4gICAgICBnZW5lcmF0ZWRBZ2VudDogdW5kZWZpbmVkLFxuICAgICAgd2FzR2VuZXJhdGVkOiBmYWxzZSxcbiAgICB9KVxuICAgIHNldFByb21wdCgnJylcbiAgICBzZXRFcnJvcihudWxsKVxuICAgIGdvQmFjaygpXG4gIH0sIFt1cGRhdGVXaXphcmREYXRhLCBnb0JhY2tdKVxuXG4gIC8vIFVzZSBTZXR0aW5ncyBjb250ZXh0IHNvICduJyBrZXkgZG9lc24ndCBjYW5jZWwgKGFsbG93cyB0eXBpbmcgJ24nIGluIHByb21wdCBpbnB1dClcbiAgdXNlS2V5YmluZGluZygnY29uZmlybTpubycsIGhhbmRsZUdvQmFjaywge1xuICAgIGNvbnRleHQ6ICdTZXR0aW5ncycsXG4gICAgaXNBY3RpdmU6ICFpc0dlbmVyYXRpbmcsXG4gIH0pXG5cbiAgY29uc3QgaGFuZGxlR2VuZXJhdGUgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgY29uc3QgdHJpbW1lZFByb21wdCA9IHByb21wdC50cmltKClcbiAgICBpZiAoIXRyaW1tZWRQcm9tcHQpIHtcbiAgICAgIHNldEVycm9yKCdQbGVhc2UgZGVzY3JpYmUgd2hhdCB0aGUgYWdlbnQgc2hvdWxkIGRvJylcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHNldEVycm9yKG51bGwpXG4gICAgc2V0SXNHZW5lcmF0aW5nKHRydWUpXG4gICAgdXBkYXRlV2l6YXJkRGF0YSh7XG4gICAgICBnZW5lcmF0aW9uUHJvbXB0OiB0cmltbWVkUHJvbXB0LFxuICAgICAgaXNHZW5lcmF0aW5nOiB0cnVlLFxuICAgIH0pXG5cbiAgICAvLyBDcmVhdGUgYWJvcnQgY29udHJvbGxlciBmb3IgdGhpcyBnZW5lcmF0aW9uXG4gICAgY29uc3QgY29udHJvbGxlciA9IGNyZWF0ZUFib3J0Q29udHJvbGxlcigpXG4gICAgYWJvcnRDb250cm9sbGVyUmVmLmN1cnJlbnQgPSBjb250cm9sbGVyXG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgZ2VuZXJhdGVkID0gYXdhaXQgZ2VuZXJhdGVBZ2VudChcbiAgICAgICAgdHJpbW1lZFByb21wdCxcbiAgICAgICAgbW9kZWwsXG4gICAgICAgIFtdLFxuICAgICAgICBjb250cm9sbGVyLnNpZ25hbCxcbiAgICAgIClcblxuICAgICAgdXBkYXRlV2l6YXJkRGF0YSh7XG4gICAgICAgIGFnZW50VHlwZTogZ2VuZXJhdGVkLmlkZW50aWZpZXIsXG4gICAgICAgIHdoZW5Ub1VzZTogZ2VuZXJhdGVkLndoZW5Ub1VzZSxcbiAgICAgICAgc3lzdGVtUHJvbXB0OiBnZW5lcmF0ZWQuc3lzdGVtUHJvbXB0LFxuICAgICAgICBnZW5lcmF0ZWRBZ2VudDogZ2VuZXJhdGVkLFxuICAgICAgICBpc0dlbmVyYXRpbmc6IGZhbHNlLFxuICAgICAgICB3YXNHZW5lcmF0ZWQ6IHRydWUsXG4gICAgICB9KVxuXG4gICAgICAvLyBTa2lwIGRpcmVjdGx5IHRvIFRvb2xzU3RlcCAoaW5kZXggNikgLSBtYXRjaGluZyBvcmlnaW5hbCBmbG93XG4gICAgICBnb1RvU3RlcCg2KVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgLy8gRG9uJ3Qgc2hvdyBlcnJvciBpZiBpdCB3YXMgY2FuY2VsbGVkIChhbHJlYWR5IHNldCBpbiBlc2NhcGUgaGFuZGxlcilcbiAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBBUElVc2VyQWJvcnRFcnJvcikge1xuICAgICAgICAvLyBVc2VyIGNhbmNlbGxlZCAtIG5vIGVycm9yIHRvIHNob3dcbiAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgIGVyciBpbnN0YW5jZW9mIEVycm9yICYmXG4gICAgICAgICFlcnIubWVzc2FnZS5pbmNsdWRlcygnTm8gYXNzaXN0YW50IG1lc3NhZ2UgZm91bmQnKVxuICAgICAgKSB7XG4gICAgICAgIHNldEVycm9yKGVyci5tZXNzYWdlIHx8ICdGYWlsZWQgdG8gZ2VuZXJhdGUgYWdlbnQnKVxuICAgICAgfVxuICAgICAgdXBkYXRlV2l6YXJkRGF0YSh7IGlzR2VuZXJhdGluZzogZmFsc2UgfSlcbiAgICB9IGZpbmFsbHkge1xuICAgICAgc2V0SXNHZW5lcmF0aW5nKGZhbHNlKVxuICAgICAgYWJvcnRDb250cm9sbGVyUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgfVxuICB9XG5cbiAgY29uc3Qgc3VidGl0bGUgPVxuICAgICdEZXNjcmliZSB3aGF0IHRoaXMgYWdlbnQgc2hvdWxkIGRvIGFuZCB3aGVuIGl0IHNob3VsZCBiZSB1c2VkIChiZSBjb21wcmVoZW5zaXZlIGZvciBiZXN0IHJlc3VsdHMpJ1xuXG4gIGlmIChpc0dlbmVyYXRpbmcpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPFdpemFyZERpYWxvZ0xheW91dFxuICAgICAgICBzdWJ0aXRsZT17c3VidGl0bGV9XG4gICAgICAgIGZvb3RlclRleHQ9e1xuICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgY29udGV4dD1cIlNldHRpbmdzXCJcbiAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwiY2FuY2VsXCJcbiAgICAgICAgICAvPlxuICAgICAgICB9XG4gICAgICA+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiIGFsaWduSXRlbXM9XCJjZW50ZXJcIj5cbiAgICAgICAgICA8U3Bpbm5lciAvPlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPiBHZW5lcmF0aW5nIGFnZW50IGZyb20gZGVzY3JpcHRpb24uLi48L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9XaXphcmREaWFsb2dMYXlvdXQ+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8V2l6YXJkRGlhbG9nTGF5b3V0XG4gICAgICBzdWJ0aXRsZT17c3VidGl0bGV9XG4gICAgICBmb290ZXJUZXh0PXtcbiAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOnllc1wiXG4gICAgICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgICAgIGZhbGxiYWNrPVwiRW50ZXJcIlxuICAgICAgICAgICAgZGVzY3JpcHRpb249XCJzdWJtaXRcIlxuICAgICAgICAgIC8+XG4gICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgYWN0aW9uPVwiY2hhdDpleHRlcm5hbEVkaXRvclwiXG4gICAgICAgICAgICBjb250ZXh0PVwiQ2hhdFwiXG4gICAgICAgICAgICBmYWxsYmFjaz1cImN0cmwrZ1wiXG4gICAgICAgICAgICBkZXNjcmlwdGlvbj1cIm9wZW4gaW4gZWRpdG9yXCJcbiAgICAgICAgICAvPlxuICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgY29udGV4dD1cIlNldHRpbmdzXCJcbiAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwiZ28gYmFja1wiXG4gICAgICAgICAgLz5cbiAgICAgICAgPC9CeWxpbmU+XG4gICAgICB9XG4gICAgPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIHtlcnJvciAmJiAoXG4gICAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPntlcnJvcn08L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICAgIDxUZXh0SW5wdXRcbiAgICAgICAgICB2YWx1ZT17cHJvbXB0fVxuICAgICAgICAgIG9uQ2hhbmdlPXtzZXRQcm9tcHR9XG4gICAgICAgICAgb25TdWJtaXQ9e2hhbmRsZUdlbmVyYXRlfVxuICAgICAgICAgIHBsYWNlaG9sZGVyPVwiZS5nLiwgSGVscCBtZSB3cml0ZSB1bml0IHRlc3RzIGZvciBteSBjb2RlLi4uXCJcbiAgICAgICAgICBjb2x1bW5zPXs4MH1cbiAgICAgICAgICBjdXJzb3JPZmZzZXQ9e2N1cnNvck9mZnNldH1cbiAgICAgICAgICBvbkNoYW5nZUN1cnNvck9mZnNldD17c2V0Q3Vyc29yT2Zmc2V0fVxuICAgICAgICAgIGZvY3VzXG4gICAgICAgICAgc2hvd0N1cnNvclxuICAgICAgICAvPlxuICAgICAgPC9Cb3g+XG4gICAgPC9XaXphcmREaWFsb2dMYXlvdXQ+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsaUJBQWlCLFFBQVEsbUJBQW1CO0FBQ3JELE9BQU9DLEtBQUssSUFBSSxLQUFLQyxTQUFTLEVBQUVDLFdBQVcsRUFBRUMsTUFBTSxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUM1RSxTQUFTQyxnQkFBZ0IsUUFBUSx1Q0FBdUM7QUFDeEUsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsb0JBQW9CO0FBQzlDLFNBQVNDLGFBQWEsUUFBUSwwQ0FBMEM7QUFDeEUsU0FBU0MscUJBQXFCLFFBQVEsc0NBQXNDO0FBQzVFLFNBQVNDLGtCQUFrQixRQUFRLG1DQUFtQztBQUN0RSxTQUFTQyx3QkFBd0IsUUFBUSxzQ0FBc0M7QUFDL0UsU0FBU0MsTUFBTSxRQUFRLGtDQUFrQztBQUN6RCxTQUFTQyxPQUFPLFFBQVEscUJBQXFCO0FBQzdDLE9BQU9DLFNBQVMsTUFBTSx1QkFBdUI7QUFDN0MsU0FBU0MsU0FBUyxRQUFRLDBCQUEwQjtBQUNwRCxTQUFTQyxrQkFBa0IsUUFBUSx1Q0FBdUM7QUFDMUUsU0FBU0MsYUFBYSxRQUFRLHdCQUF3QjtBQUN0RCxjQUFjQyxlQUFlLFFBQVEsYUFBYTtBQUVsRCxPQUFPLFNBQVNDLFlBQVlBLENBQUEsQ0FBRSxFQUFFbEIsU0FBUyxDQUFDO0VBQ3hDLE1BQU07SUFBRW1CLGdCQUFnQjtJQUFFQyxNQUFNO0lBQUVDLFFBQVE7SUFBRUM7RUFBVyxDQUFDLEdBQ3REUixTQUFTLENBQUNHLGVBQWUsQ0FBQyxDQUFDLENBQUM7RUFDOUIsTUFBTSxDQUFD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 \ No newline at end of file diff --git a/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx b/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx new file mode 100644 index 0000000..d64c165 --- /dev/null +++ b/components/agents/new-agent-creation/wizard-steps/LocationStep.tsx @@ -0,0 +1,80 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode } from 'react'; +import { Box } from '../../../../ink.js'; +import type { SettingSource } from '../../../../utils/settings/constants.js'; +import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; +import { Select } from '../../../CustomSelect/select.js'; +import { Byline } from '../../../design-system/Byline.js'; +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js'; +import { useWizard } from '../../../wizard/index.js'; +import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js'; +import type { AgentWizardData } from '../types.js'; +export function LocationStep() { + const $ = _c(11); + const { + goNext, + updateWizardData, + cancel + } = useWizard(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { + label: "Project (.claude/agents/)", + value: "projectSettings" as SettingSource + }; + $[0] = t0; + } else { + t0 = $[0]; + } + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = [t0, { + label: "Personal (~/.claude/agents/)", + value: "userSettings" as SettingSource + }]; + $[1] = t1; + } else { + t1 = $[1]; + } + const locationOptions = t1; + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = ; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] !== goNext || $[4] !== updateWizardData) { + t3 = value => { + updateWizardData({ + location: value as SettingSource + }); + goNext(); + }; + $[3] = goNext; + $[4] = updateWizardData; + $[5] = t3; + } else { + t3 = $[5]; + } + let t4; + if ($[6] !== cancel) { + t4 = () => cancel(); + $[6] = cancel; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] !== t3 || $[9] !== t4) { + t5 = ; + $[9] = goBack; + $[10] = handleSelect; + $[11] = memoryOptions; + $[12] = t4; + } else { + t4 = $[12]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsIkJveCIsInVzZUtleWJpbmRpbmciLCJpc0F1dG9NZW1vcnlFbmFibGVkIiwiQWdlbnRNZW1vcnlTY29wZSIsImxvYWRBZ2VudE1lbW9yeVByb21wdCIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIlNlbGVjdCIsIkJ5bGluZSIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwidXNlV2l6YXJkIiwiV2l6YXJkRGlhbG9nTGF5b3V0IiwiQWdlbnRXaXphcmREYXRhIiwiTWVtb3J5T3B0aW9uIiwibGFiZWwiLCJ2YWx1ZSIsIk1lbW9yeVN0ZXAiLCIkIiwiX2MiLCJnb05leHQiLCJnb0JhY2siLCJ1cGRhdGVXaXphcmREYXRhIiwid2l6YXJkRGF0YSIsInQwIiwiU3ltYm9sIiwiZm9yIiwiY29udGV4dCIsImlzVXNlclNjb3BlIiwibG9jYXRpb24iLCJ0MSIsIm1lbW9yeU9wdGlvbnMiLCJ0MiIsImZpbmFsQWdlbnQiLCJzeXN0ZW1Qcm9tcHQiLCJtZW1vcnkiLCJ1bmRlZmluZWQiLCJhZ2VudFR5cGUiLCJzZWxlY3RlZE1lbW9yeSIsImdldFN5c3RlbVByb21wdCIsImhhbmRsZVNlbGVjdCIsInQzIiwidDQiXSwic291cmNlcyI6WyJNZW1vcnlTdGVwLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdHlwZSBSZWFjdE5vZGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCB9IGZyb20gJy4uLy4uLy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi8uLi8uLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgaXNBdXRvTWVtb3J5RW5hYmxlZCB9IGZyb20gJy4uLy4uLy4uLy4uL21lbWRpci9wYXRocy5qcydcbmltcG9ydCB7XG4gIHR5cGUgQWdlbnRNZW1vcnlTY29wZSxcbiAgbG9hZEFnZW50TWVtb3J5UHJvbXB0LFxufSBmcm9tICcuLi8uLi8uLi8uLi90b29scy9BZ2VudFRvb2wvYWdlbnRNZW1vcnkuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi8uLi8uLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi8uLi8uLi9DdXN0b21TZWxlY3Qvc2VsZWN0LmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vLi4vLi4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4uLy4uLy4uL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyB1c2VXaXphcmQgfSBmcm9tICcuLi8uLi8uLi93aXphcmQvaW5kZXguanMnXG5pbXBvcnQgeyBXaXphcmREaWFsb2dMYXlvdXQgfSBmcm9tICcuLi8uLi8uLi93aXphcmQvV2l6YXJkRGlhbG9nTGF5b3V0LmpzJ1xuaW1wb3J0IHR5cGUgeyBBZ2VudFdpemFyZERhdGEgfSBmcm9tICcuLi90eXBlcy5qcydcblxudHlwZSBNZW1vcnlPcHRpb24gPSB7XG4gIGxhYmVsOiBzdHJpbmdcbiAgdmFsdWU6IEFnZW50TWVtb3J5U2NvcGUgfCAnbm9uZSdcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIE1lbW9yeVN0ZXAoKTogUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBnb05leHQsIGdvQmFjaywgdXBkYXRlV2l6YXJkRGF0YSwgd2l6YXJkRGF0YSB9ID1cbiAgICB1c2VXaXphcmQ8QWdlbnRXaXphcmREYXRhPigpXG5cbiAgdXNlS2V5YmluZGluZygnY29uZmlybTpubycsIGdvQmFjaywgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJyB9KVxuXG4gIGNvbnN0IGlzVXNlclNjb3BlID0gd2l6YXJkRGF0YS5sb2NhdGlvbiA9PT0gJ3VzZXJTZXR0aW5ncydcblxuICAvLyBCdWlsZCBvcHRpb25zIHdpdGggdGhlIHJlY29tbWVuZGVkIGRlZmF1bHQgZmlyc3QsIHRoZW4gYWx0ZXJuYXRpdmVzXG4gIC8vIFRoZSByZWNvbW1lbmRlZCBzY29wZSBtYXRjaGVzIHRoZSBhZ2VudCdzIGxvY2F0aW9uIChwcm9qZWN0IGFnZW50IOKGkiBwcm9qZWN0IG1lbW9yeSwgdXNlciBhZ2VudCDihpIgdXNlciBtZW1vcnkpXG4gIGNvbnN0IG1lbW9yeU9wdGlvbnM6IE1lbW9yeU9wdGlvbltdID0gaXNVc2VyU2NvcGVcbiAgICA/IFtcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnVXNlciBzY29wZSAofi8uY2xhdWRlL2FnZW50LW1lbW9yeS8pIChSZWNvbW1lbmRlZCknLFxuICAgICAgICAgIHZhbHVlOiAndXNlcicsXG4gICAgICAgIH0sXG4gICAgICAgIHsgbGFiZWw6ICdOb25lIChubyBwZXJzaXN0ZW50IG1lbW9yeSknLCB2YWx1ZTogJ25vbmUnIH0sXG4gICAgICAgIHsgbGFiZWw6ICdQcm9qZWN0IHNjb3BlICguY2xhdWRlL2FnZW50LW1lbW9yeS8pJywgdmFsdWU6ICdwcm9qZWN0JyB9LFxuICAgICAgICB7IGxhYmVsOiAnTG9jYWwgc2NvcGUgKC5jbGF1ZGUvYWdlbnQtbWVtb3J5LWxvY2FsLyknLCB2YWx1ZTogJ2xvY2FsJyB9LFxuICAgICAgXVxuICAgIDogW1xuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6ICdQcm9qZWN0IHNjb3BlICguY2xhdWRlL2FnZW50LW1lbW9yeS8pIChSZWNvbW1lbmRlZCknLFxuICAgICAgICAgIHZhbHVlOiAncHJvamVjdCcsXG4gICAgICAgIH0sXG4gICAgICAgIHsgbGFiZWw6ICdOb25lIChubyBwZXJzaXN0ZW50IG1lbW9yeSknLCB2YWx1ZTogJ25vbmUnIH0sXG4gICAgICAgIHsgbGFiZWw6ICdVc2VyIHNjb3BlICh+Ly5jbGF1ZGUvYWdlbnQtbWVtb3J5LyknLCB2YWx1ZTogJ3VzZXInIH0sXG4gICAgICAgIHsgbGFiZWw6ICdMb2NhbCBzY29wZSAoLmNsYXVkZS9hZ2VudC1tZW1vcnktbG9jYWwvKScsIHZhbHVlOiAnbG9jYWwnIH0sXG4gICAgICBdXG5cbiAgY29uc3QgaGFuZGxlU2VsZWN0ID0gKHZhbHVlOiBzdHJpbmcpOiB2b2lkID0+IHtcbiAgICBjb25zdCBtZW1vcnkgPSB2YWx1ZSA9PT0gJ25vbmUnID8gdW5kZWZpbmVkIDogKHZhbHVlIGFzIEFnZW50TWVtb3J5U2NvcGUpXG4gICAgY29uc3QgYWdlbnRUeXBlID0gd2l6YXJkRGF0YS5maW5hbEFnZW50Py5hZ2VudFR5cGVcbiAgICB1cGRhdGVXaXphcmREYXRhKHtcbiAgICAgIHNlbGVjdGVkTWVtb3J5OiBtZW1vcnksXG4gICAgICAvLyBVcGRhdGUgZmluYWxBZ2VudCB3aXRoIG1lbW9yeSBhbmQgcmV3aXJlIGdldFN5c3RlbVByb21wdCB0byBpbmNsdWRlIG1lbW9yeSBsb2FkaW5nLlxuICAgICAgLy8gRXhwbGljaXRseSBzZXQgbWVtb3J5IChub3QgY29uZGl0aW9uYWwgc3ByZWFkKSBzbyBzZWxlY3RpbmcgJ25vbmUnIGFmdGVyIGdvaW5nIGJhY2sgY2xlYXJzIGl0LlxuICAgICAgZmluYWxBZ2VudDogd2l6YXJkRGF0YS5maW5hbEFnZW50XG4gICAgICAgID8ge1xuICAgICAgICAgICAgLi4ud2l6YXJkRGF0YS5maW5hbEFnZW50LFxuICAgICAgICAgICAgbWVtb3J5LFxuICAgICAgICAgICAgZ2V0U3lzdGVtUHJvbXB0OlxuICAgICAgICAgICAgICBpc0F1dG9NZW1vcnlFbmFibGVkKCkgJiYgbWVtb3J5ICYmIGFnZW50VHlwZVxuICAgICAgICAgICAgICAgID8gKCkgPT5cbiAgICAgICAgICAgICAgICAgICAgd2l6YXJkRGF0YS5zeXN0ZW1Qcm9tcHQhICtcbiAgICAgICAgICAgICAgICAgICAgJ1xcblxcbicgK1xuICAgICAgICAgICAgICAgICAgICBsb2FkQWdlbnRNZW1vcnlQcm9tcHQoYWdlbnRUeXBlLCBtZW1vcnkpXG4gICAgICAgICAgICAgICAgOiAoKSA9PiB3aXphcmREYXRhLnN5c3RlbVByb21wdCEsXG4gICAgICAgICAgfVxuICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICB9KVxuICAgIGdvTmV4dCgpXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxXaXphcmREaWFsb2dMYXlvdXRcbiAgICAgIHN1YnRpdGxlPVwiQ29uZmlndXJlIGFnZW50IG1lbW9yeVwiXG4gICAgICBmb290ZXJUZXh0PXtcbiAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCLihpHihpNcIiBhY3Rpb249XCJuYXZpZ2F0ZVwiIC8+XG4gICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiRW50ZXJcIiBhY3Rpb249XCJzZWxlY3RcIiAvPlxuICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgICAgICBmYWxsYmFjaz1cIkVzY1wiXG4gICAgICAgICAgICBkZXNjcmlwdGlvbj1cImdvIGJhY2tcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQnlsaW5lPlxuICAgICAgfVxuICAgID5cbiAgICAgIDxCb3g+XG4gICAgICAgIDxTZWxlY3RcbiAgICAgICAgICBrZXk9XCJtZW1vcnktc2VsZWN0XCJcbiAgICAgICAgICBvcHRpb25zPXttZW1vcnlPcHRpb25zfVxuICAgICAgICAgIG9uQ2hhbmdlPXtoYW5kbGVTZWxlY3R9XG4gICAgICAgICAgb25DYW5jZWw9e2dvQmFja31cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIDwvV2l6YXJkRGlhbG9nTGF5b3V0PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUksS0FBS0MsU0FBUyxRQUFRLE9BQU87QUFDN0MsU0FBU0MsR0FBRyxRQUFRLG9CQUFvQjtBQUN4QyxTQUFTQyxhQUFhLFFBQVEsMENBQTBDO0FBQ3hFLFNBQVNDLG1CQUFtQixRQUFRLDZCQUE2QjtBQUNqRSxTQUNFLEtBQUtDLGdCQUFnQixFQUNyQkMscUJBQXFCLFFBQ2hCLDRDQUE0QztBQUNuRCxTQUFTQyx3QkFBd0IsUUFBUSxzQ0FBc0M7QUFDL0UsU0FBU0MsTUFBTSxRQUFRLGlDQUFpQztBQUN4RCxTQUFTQyxNQUFNLFFBQVEsa0NBQWtDO0FBQ3pELFNBQVNDLG9CQUFvQixRQUFRLGdEQUFnRDtBQUNyRixTQUFTQyxTQUFTLFFBQVEsMEJBQTBCO0FBQ3BELFNBQVNDLGtCQUFrQixRQUFRLHVDQUF1QztBQUMxRSxjQUFjQyxlQUFlLFFBQVEsYUFBYTtBQUVsRCxLQUFLQyxZQUFZLEdBQUc7RUFDbEJDLEtBQUssRUFBRSxNQUFNO0VBQ2JDLEtBQUssRUFBRVgsZ0JBQWdCLEdBQUcsTUFBTTtBQUNsQyxDQUFDO0FBRUQsT0FBTyxTQUFBWSxXQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQ0w7SUFBQUMsTUFBQTtJQUFBQyxNQUFBO0lBQUFDLGdCQUFBO0lBQUFDO0VBQUEsSUFDRVosU0FBUyxDQUFrQixDQUFDO0VBQUEsSUFBQWEsRUFBQTtFQUFBLElBQUFOLENBQUEsUUFBQU8sTUFBQSxDQUFBQyxHQUFBO0lBRU1GLEVBQUE7TUFBQUcsT0FBQSxFQUFXO0lBQWUsQ0FBQztJQUFBVCxDQUFBLE1BQUFNLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFOLENBQUE7RUFBQTtFQUEvRGYsYUFBYSxDQUFDLFlBQVksRUFBRWtCLE1BQU0sRUFBRUcsRUFBMkIsQ0FBQztFQUVoRSxNQUFBSSxXQUFBLEdBQW9CTCxVQUFVLENBQUFNLFFBQVMsS0FBSyxjQUFjO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFaLENBQUEsUUFBQVUsV0FBQTtJQUlwQkUsRUFBQSxHQUFBRixXQUFXLEdBQVgsQ0FFaEM7TUFBQWIsS0FBQSxFQUNTLG9EQUFvRDtNQUFBQyxLQUFBLEVBQ3BEO0lBQ1QsQ0FBQyxFQUNEO01BQUFELEtBQUEsRUFBUyw2QkFBNkI7TUFBQUMsS0FBQSxFQUFTO0lBQU8sQ0FBQyxFQUN2RDtNQUFBRCxLQUFBLEVBQVMsdUNBQXVDO01BQUFDLEtBQUEsRUFBUztJQUFVLENBQUMsRUFDcEU7TUFBQUQsS0FBQSxFQUFTLDJDQUEyQztNQUFBQyxLQUFBLEVBQVM7SUFBUSxDQUFDLENBVXZFLEdBbEJpQyxDQVdoQztNQUFBRCxLQUFBLEVBQ1MscURBQXFEO01BQUFDLEtBQUEsRUFDckQ7SUFDVCxDQUFDLEVBQ0Q7TUFBQUQsS0FBQSxFQUFTLDZCQUE2QjtNQUFBQyxLQUFBLEVBQVM7SUFBTyxDQUFDLEVBQ3ZEO01BQUFELEtBQUEsRUFBUyxzQ0FBc0M7TUFBQUMsS0FBQSxFQUFTO0lBQU8sQ0FBQyxFQUNoRTtNQUFBRCxLQUFBLEVBQVMsMkNBQTJDO01BQUFDLEtBQUEsRUFBUztJQUFRLENBQUMsQ0FDdkU7SUFBQUUsQ0FBQSxNQUFBVSxXQUFBO0lBQUFWLENBQUEsTUFBQVksRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVosQ0FBQTtFQUFBO0VBbEJMLE1BQUFhLGFBQUEsR0FBc0NELEVBa0JqQztFQUFBLElBQUFFLEVBQUE7RUFBQSxJQUFBZCxDQUFBLFFBQUFFLE1BQUEsSUFBQUYsQ0FBQSxRQUFBSSxnQkFBQSxJQUFBSixDQUFBLFFBQUFLLFVBQUEsQ0FBQVUsVUFBQSxJQUFBZixDQUFBLFFBQUFLLFVBQUEsQ0FBQVcsWUFBQTtJQUVnQkYsRUFBQSxHQUFBaEIsS0FBQTtNQUNuQixNQUFBbUIsTUFBQSxHQUFlbkIsS0FBSyxLQUFLLE1BQWdELEdBQTFEb0IsU0FBMEQsR0FBMUJwQixLQUFLLElBQUlYLGdCQUFpQjtNQUN6RSxNQUFBZ0MsU0FBQSxHQUFrQmQsVUFBVSxDQUFBVSxVQUFzQixFQUFBSSxTQUFBO01BQ2xEZixnQkFBZ0IsQ0FBQztRQUFBZ0IsY0FBQSxFQUNDSCxNQUFNO1FBQUFGLFVBQUEsRUFHVlYsVUFBVSxDQUFBVSxVQVlULEdBWkQ7VUFBQSxHQUVIVixVQUFVLENBQUFVLFVBQVc7VUFBQUUsTUFBQTtVQUFBSSxlQUFBLEVBR3RCbkMsbUJBQW1CLENBQVcsQ0FBQyxJQUEvQitCLE1BQTRDLElBQTVDRSxTQUtrQyxHQUxsQyxNQUVNZCxVQUFVLENBQUFXLFlBQWEsR0FDdkIsTUFBTSxHQUNONUIscUJBQXFCLENBQUMrQixTQUFTLEVBQUVGLE1BQU0sQ0FDWCxHQUxsQyxNQUtVWixVQUFVLENBQUFXO1FBRWhCLENBQUMsR0FaREU7TUFhZCxDQUFDLENBQUM7TUFDRmhCLE1BQU0sQ0FBQyxDQUFDO0lBQUEsQ0FDVDtJQUFBRixDQUFBLE1BQUFFLE1BQUE7SUFBQUYsQ0FBQSxNQUFBSSxnQkFBQTtJQUFBSixDQUFBLE1BQUFLLFVBQUEsQ0FBQVUsVUFBQTtJQUFBZixDQUFBLE1BQUFLLFVBQUEsQ0FBQVcsWUFBQTtJQUFBaEIsQ0FBQSxNQUFBYyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBZCxDQUFBO0VBQUE7RUF0QkQsTUFBQXNCLFlBQUEsR0FBcUJSLEVBc0JwQjtFQUFBLElBQUFTLEVBQUE7RUFBQSxJQUFBdkIsQ0FBQSxRQUFBTyxNQUFBLENBQUFDLEdBQUE7SUFNS2UsRUFBQSxJQUFDLE1BQU0sQ0FDTCxDQUFDLG9CQUFvQixDQUFVLFFBQUksQ0FBSixlQUFHLENBQUMsQ0FBUSxNQUFVLENBQVYsVUFBVSxHQUNyRCxDQUFDLG9CQUFvQixDQUFVLFFBQU8sQ0FBUCxPQUFPLENBQVEsTUFBUSxDQUFSLFFBQVEsR0FDdEQsQ0FBQyx3QkFBd0IsQ0FDaEIsTUFBWSxDQUFaLFlBQVksQ0FDWCxPQUFjLENBQWQsY0FBYyxDQUNiLFFBQUssQ0FBTCxLQUFLLENBQ0YsV0FBUyxDQUFULFNBQVMsR0FFekIsRUFUQyxNQUFNLENBU0U7SUFBQXZCLENBQUEsTUFBQXVCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUF2QixDQUFBO0VBQUE7RUFBQSxJQUFBd0IsRUFBQTtFQUFBLElBQUF4QixDQUFBLFFBQUFHLE1BQUEsSUFBQUgsQ0FBQSxTQUFBc0IsWUFBQSxJQUFBdEIsQ0FBQSxTQUFBYSxhQUFBO0lBWmJXLEVBQUEsSUFBQyxrQkFBa0IsQ0FDUixRQUF3QixDQUF4Qix3QkFBd0IsQ0FFL0IsVUFTUyxDQVRULENBQUFELEVBU1EsQ0FBQyxDQUdYLENBQUMsR0FBRyxDQUNGLENBQUMsTUFBTSxDQUNELEdBQWUsQ0FBZixlQUFlLENBQ1ZWLE9BQWEsQ0FBYkEsY0FBWSxDQUFDLENBQ1pTLFFBQVksQ0FBWkEsYUFBVyxDQUFDLENBQ1puQixRQUFNLENBQU5BLE9BQUssQ0FBQyxHQUVwQixFQVBDLEdBQUcsQ0FRTixFQXZCQyxrQkFBa0IsQ0F1QkU7SUFBQUgsQ0FBQSxNQUFBRyxNQUFBO0lBQUFILENBQUEsT0FBQXNCLFlBQUE7SUFBQXRCLENBQUEsT0FBQWEsYUFBQTtJQUFBYixDQUFBLE9BQUF3QixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBeEIsQ0FBQTtFQUFBO0VBQUEsT0F2QnJCd0IsRUF1QnFCO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx b/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx new file mode 100644 index 0000000..cfcb450 --- /dev/null +++ b/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx @@ -0,0 +1,80 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode } from 'react'; +import { Box } from '../../../../ink.js'; +import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; +import { Select } from '../../../CustomSelect/select.js'; +import { Byline } from '../../../design-system/Byline.js'; +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js'; +import { useWizard } from '../../../wizard/index.js'; +import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js'; +import type { AgentWizardData } from '../types.js'; +export function MethodStep() { + const $ = _c(11); + const { + goNext, + goBack, + updateWizardData, + goToStep + } = useWizard(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = [{ + label: "Generate with Claude (recommended)", + value: "generate" + }, { + label: "Manual configuration", + value: "manual" + }]; + $[0] = t0; + } else { + t0 = $[0]; + } + const methodOptions = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== goNext || $[3] !== goToStep || $[4] !== updateWizardData) { + t2 = value => { + const method = value as 'generate' | 'manual'; + updateWizardData({ + method, + wasGenerated: method === "generate" + }); + if (method === "generate") { + goNext(); + } else { + goToStep(3); + } + }; + $[2] = goNext; + $[3] = goToStep; + $[4] = updateWizardData; + $[5] = t2; + } else { + t2 = $[5]; + } + let t3; + if ($[6] !== goBack) { + t3 = () => goBack(); + $[6] = goBack; + $[7] = t3; + } else { + t3 = $[7]; + } + let t4; + if ($[8] !== t2 || $[9] !== t3) { + t4 = ; + $[26] = handleCancel; + $[27] = t11; + $[28] = t12; + $[29] = t13; + } else { + t13 = $[29]; + } + let t14; + if ($[30] !== handleCancel || $[31] !== t13 || $[32] !== t8) { + t14 = {t8}{t13}; + $[30] = handleCancel; + $[31] = t13; + $[32] = t8; + $[33] = t14; + } else { + t14 = $[33]; + } + return t14; +} +function _temp(exitState) { + return exitState.pending ? Press {exitState.keyName} again to exit : ; +} +type PrivacySettingsDialogProps = { + settings: AccountSettings; + domainExcluded?: boolean; + onDone(): void; +}; +export function PrivacySettingsDialog(t0) { + const $ = _c(17); + const { + settings, + domainExcluded, + onDone + } = t0; + const [groveEnabled, setGroveEnabled] = useState(settings.grove_enabled); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = []; + $[0] = t1; + } else { + t1 = $[0]; + } + React.useEffect(_temp2, t1); + let t2; + if ($[1] !== domainExcluded || $[2] !== groveEnabled) { + t2 = async (input, key) => { + if (!domainExcluded && (key.tab || key.return || input === " ")) { + const newValue = !groveEnabled; + setGroveEnabled(newValue); + await updateGroveSettings(newValue); + } + }; + $[1] = domainExcluded; + $[2] = groveEnabled; + $[3] = t2; + } else { + t2 = $[3]; + } + useInput(t2); + let t3; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t3 = false; + $[4] = t3; + } else { + t3 = $[4]; + } + let valueComponent = t3; + if (domainExcluded) { + let t4; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t4 = false (for emails with your domain); + $[5] = t4; + } else { + t4 = $[5]; + } + valueComponent = t4; + } else { + if (groveEnabled) { + let t4; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t4 = true; + $[6] = t4; + } else { + t4 = $[6]; + } + valueComponent = t4; + } + } + let t4; + if ($[7] !== domainExcluded) { + t4 = exitState => exitState.pending ? Press {exitState.keyName} again to exit : domainExcluded ? : ; + $[7] = domainExcluded; + $[8] = t4; + } else { + t4 = $[8]; + } + let t5; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t5 = Review and manage your privacy settings at{" "}; + $[9] = t5; + } else { + t5 = $[9]; + } + let t6; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t6 = Help improve Claude; + $[10] = t6; + } else { + t6 = $[10]; + } + let t7; + if ($[11] !== valueComponent) { + t7 = {t6}{valueComponent}; + $[11] = valueComponent; + $[12] = t7; + } else { + t7 = $[12]; + } + let t8; + if ($[13] !== onDone || $[14] !== t4 || $[15] !== t7) { + t8 = {t5}{t7}; + $[13] = onDone; + $[14] = t4; + $[15] = t7; + $[16] = t8; + } else { + t8 = $[16]; + } + return t8; +} +function _temp2() { + logEvent("tengu_grove_privacy_settings_viewed", {}); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyIsImxvZ0V2ZW50IiwiQm94IiwiTGluayIsIlRleHQiLCJ1c2VJbnB1dCIsIkFjY291bnRTZXR0aW5ncyIsImNhbGN1bGF0ZVNob3VsZFNob3dHcm92ZSIsIkdyb3ZlQ29uZmlnIiwiZ2V0R3JvdmVOb3RpY2VDb25maWciLCJnZXRHcm92ZVNldHRpbmdzIiwibWFya0dyb3ZlTm90aWNlVmlld2VkIiwidXBkYXRlR3JvdmVTZXR0aW5ncyIsIlNlbGVjdCIsIkJ5bGluZSIsIkRpYWxvZyIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwiR3JvdmVEZWNpc2lvbiIsIlByb3BzIiwic2hvd0lmQWxyZWFkeVZpZXdlZCIsImxvY2F0aW9uIiwib25Eb25lIiwiZGVjaXNpb24iLCJORVdfVEVSTVNfQVNDSUkiLCJHcmFjZVBlcmlvZENvbnRlbnRCb2R5IiwiJCIsIl9jIiwidDAiLCJTeW1ib2wiLCJmb3IiLCJ0MSIsInQyIiwidDMiLCJ0NCIsInQ1IiwidDYiLCJ0NyIsInQ4IiwiUG9zdEdyYWNlUGVyaW9kQ29udGVudEJvZHkiLCJHcm92ZURpYWxvZyIsInNob3VsZFNob3dEaWFsb2ciLCJzZXRTaG91bGRTaG93RGlhbG9nIiwiZ3JvdmVDb25maWciLCJzZXRHcm92ZUNvbmZpZyIsImNoZWNrR3JvdmVTZXR0aW5ncyIsInNldHRpbmdzUmVzdWx0IiwiY29uZmlnUmVzdWx0IiwiUHJvbWlzZSIsImFsbCIsImNvbmZpZyIsInN1Y2Nlc3MiLCJkYXRhIiwic2hvdWxkU2hvdyIsImRpc21pc3NhYmxlIiwibm90aWNlX2lzX2dyYWNlX3BlcmlvZCIsIm9uQ2hhbmdlIiwidmFsdWUiLCJiYjIxIiwic3RhdGUiLCJkb21haW5fZXhjbHVkZWQiLCJsYWJlbCIsImFjY2VwdE9wdGlvbnMiLCJoYW5kbGVDYW5jZWwiLCJ0OSIsInQxMCIsInQxMSIsInQxMiIsInZhbHVlXzAiLCJ0MTMiLCJ0MTQiLCJfdGVtcCIsImV4aXRTdGF0ZSIsInBlbmRpbmciLCJrZXlOYW1lIiwiUHJpdmFjeVNldHRpbmdzRGlhbG9nUHJvcHMiLCJzZXR0aW5ncyIsImRvbWFpbkV4Y2x1ZGVkIiwiUHJpdmFjeVNldHRpbmdzRGlhbG9nIiwiZ3JvdmVFbmFibGVkIiwic2V0R3JvdmVFbmFibGVkIiwiZ3JvdmVfZW5hYmxlZCIsIl90ZW1wMiIsImlucHV0Iiwia2V5IiwidGFiIiwicmV0dXJuIiwibmV3VmFsdWUiLCJ2YWx1ZUNvbXBvbmVudCJdLCJzb3VyY2VzIjpbIkdyb3ZlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IEJveCwgTGluaywgVGV4dCwgdXNlSW5wdXQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQge1xuICB0eXBlIEFjY291bnRTZXR0aW5ncyxcbiAgY2FsY3VsYXRlU2hvdWxkU2hvd0dyb3ZlLFxuICB0eXBlIEdyb3ZlQ29uZmlnLFxuICBnZXRHcm92ZU5vdGljZUNvbmZpZyxcbiAgZ2V0R3JvdmVTZXR0aW5ncyxcbiAgbWFya0dyb3ZlTm90aWNlVmlld2VkLFxuICB1cGRhdGVHcm92ZVNldHRpbmdzLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hcGkvZ3JvdmUuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi9DdXN0b21TZWxlY3QvaW5kZXguanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuXG5leHBvcnQgdHlwZSBHcm92ZURlY2lzaW9uID1cbiAgfCAnYWNjZXB0X29wdF9pbidcbiAgfCAnYWNjZXB0X29wdF9vdXQnXG4gIHwgJ2RlZmVyJ1xuICB8ICdlc2NhcGUnXG4gIHwgJ3NraXBfcmVuZGVyaW5nJ1xuXG50eXBlIFByb3BzID0ge1xuICBzaG93SWZBbHJlYWR5Vmlld2VkOiBib29sZWFuXG4gIGxvY2F0aW9uOiAnc2V0dGluZ3MnIHwgJ3BvbGljeV91cGRhdGVfbW9kYWwnIHwgJ29uYm9hcmRpbmcnXG4gIG9uRG9uZShkZWNpc2lvbjogR3JvdmVEZWNpc2lvbik6IHZvaWRcbn1cblxuY29uc3QgTkVXX1RFUk1TX0FTQ0lJID0gYCBfX19fX19fX19fX19fXG4gfCAgICAgICAgICBcXFxcICBcXFxcXG4gfCBORVcgVEVSTVMgXFxcXF9fXFxcXFxuIHwgICAgICAgICAgICAgIHxcbiB8ICAtLS0tLS0tLS0tICB8XG4gfCAgLS0tLS0tLS0tLSAgfFxuIHwgIC0tLS0tLS0tLS0gIHxcbiB8ICAtLS0tLS0tLS0tICB8XG4gfCAgLS0tLS0tLS0tLSAgfFxuIHwgICAgICAgICAgICAgIHxcbiB8X19fX19fX19fX19fX198YFxuXG5mdW5jdGlvbiBHcmFjZVBlcmlvZENvbnRlbnRCb2R5KCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxUZXh0PlxuICAgICAgICBBbiB1cGRhdGUgdG8gb3VyIENvbnN1bWVyIFRlcm1zIGFuZCBQcml2YWN5IFBvbGljeSB3aWxsIHRha2UgZWZmZWN0IG9ueycgJ31cbiAgICAgICAgPFRleHQgYm9sZD5PY3RvYmVyIDgsIDIwMjU8L1RleHQ+LiBZb3UgY2FuIGFjY2VwdCB0aGUgdXBkYXRlZCB0ZXJtc1xuICAgICAgICB0b2RheS5cbiAgICAgIDwvVGV4dD5cblxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIDxUZXh0PldoYXQmYXBvcztzIGNoYW5naW5nPzwvVGV4dD5cblxuICAgICAgICA8Qm94IHBhZGRpbmdMZWZ0PXsxfT5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIDxUZXh0PsK3IDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+WW91IGNhbiBoZWxwIGltcHJvdmUgQ2xhdWRlIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICDigJQgQWxsb3cgdGhlIHVzZSBvZiB5b3VyIGNoYXRzIGFuZCBjb2Rpbmcgc2Vzc2lvbnMgdG8gdHJhaW4gYW5kXG4gICAgICAgICAgICAgIGltcHJvdmUgQW50aHJvcGljIEFJIG1vZGVscy4gQ2hhbmdlIGFueXRpbWUgaW4geW91ciBQcml2YWN5XG4gICAgICAgICAgICAgIFNldHRpbmdzIChcbiAgICAgICAgICAgICAgPExpbmtcbiAgICAgICAgICAgICAgICB1cmw9eydodHRwczovL2NsYXVkZS5haS9zZXR0aW5ncy9kYXRhLXByaXZhY3ktY29udHJvbHMnfVxuICAgICAgICAgICAgICA+PC9MaW5rPlxuICAgICAgICAgICAgICApLlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggcGFkZGluZ0xlZnQ9ezF9PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQ+wrcgPC9UZXh0PlxuICAgICAgICAgICAgPFRleHQgYm9sZD5VcGRhdGVzIHRvIGRhdGEgcmV0ZW50aW9uIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICDigJQgVG8gaGVscCB1cyBpbXByb3ZlIG91ciBBSSBtb2RlbHMgYW5kIHNhZmV0eSBwcm90ZWN0aW9ucyxcbiAgICAgICAgICAgICAgd2UmYXBvcztyZSBleHRlbmRpbmcgZGF0YSByZXRlbnRpb24gdG8gNSB5ZWFycy5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG5cbiAgICAgIDxUZXh0PlxuICAgICAgICBMZWFybiBtb3JlIChcbiAgICAgICAgPExpbmtcbiAgICAgICAgICB1cmw9eydodHRwczovL3d3dy5hbnRocm9waWMuY29tL25ld3MvdXBkYXRlcy10by1vdXItY29uc3VtZXItdGVybXMnfVxuICAgICAgICA+PC9MaW5rPlxuICAgICAgICApIG9yIHJlYWQgdGhlIHVwZGF0ZWQgQ29uc3VtZXIgVGVybXMgKFxuICAgICAgICA8TGluayB1cmw9eydodHRwczovL2FudGhyb3BpYy5jb20vbGVnYWwvdGVybXMnfT48L0xpbms+KSBhbmQgUHJpdmFjeVxuICAgICAgICBQb2xpY3kgKDxMaW5rIHVybD17J2h0dHBzOi8vYW50aHJvcGljLmNvbS9sZWdhbC9wcml2YWN5J30+PC9MaW5rPilcbiAgICAgIDwvVGV4dD5cbiAgICA8Lz5cbiAgKVxufVxuXG5mdW5jdGlvbiBQb3N0R3JhY2VQZXJpb2RDb250ZW50Qm9keSgpOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8VGV4dD5XZSZhcG9zO3ZlIHVwZGF0ZWQgb3VyIENvbnN1bWVyIFRlcm1zIGFuZCBQcml2YWN5IFBvbGljeS48L1RleHQ+XG5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgIDxUZXh0PldoYXQmYXBvcztzIGNoYW5naW5nPzwvVGV4dD5cblxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBib2xkPkhlbHAgaW1wcm92ZSBDbGF1ZGU8L1RleHQ+XG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICBBbGxvdyB0aGUgdXNlIG9mIHlvdXIgY2hhdHMgYW5kIGNvZGluZyBzZXNzaW9ucyB0byB0cmFpbiBhbmQgaW1wcm92ZVxuICAgICAgICAgICAgQW50aHJvcGljIEFJIG1vZGVscy4gWW91IGNhbiBjaGFuZ2UgdGhpcyBhbnl0aW1lIGluIFByaXZhY3kgU2V0dGluZ3NcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPExpbmsgdXJsPXsnaHR0cHM6Ly9jbGF1ZGUuYWkvc2V0dGluZ3MvZGF0YS1wcml2YWN5LWNvbnRyb2xzJ30+PC9MaW5rPlxuICAgICAgICA8L0JveD5cblxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBib2xkPkhvdyB0aGlzIGFmZmVjdHMgZGF0YSByZXRlbnRpb248L1RleHQ+XG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICBUdXJuaW5nIE9OIHRoZSBpbXByb3ZlIENsYXVkZSBzZXR0aW5nIGV4dGVuZHMgZGF0YSByZXRlbnRpb24gZnJvbSAzMFxuICAgICAgICAgICAgZGF5cyB0byA1IHllYXJzLiBUdXJuaW5nIGl0IE9GRiBrZWVwcyB0aGUgZGVmYXVsdCAzMC1kYXkgZGF0YVxuICAgICAgICAgICAgcmV0ZW50aW9uLiBEZWxldGUgZGF0YSBhbnl0aW1lLlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cblxuICAgICAgPFRleHQ+XG4gICAgICAgIExlYXJuIG1vcmUgKFxuICAgICAgICA8TGlua1xuICAgICAgICAgIHVybD17J2h0dHBzOi8vd3d3LmFudGhyb3BpYy5jb20vbmV3cy91cGRhdGVzLXRvLW91ci1jb25zdW1lci10ZXJtcyd9XG4gICAgICAgID48L0xpbms+XG4gICAgICAgICkgb3IgcmVhZCB0aGUgdXBkYXRlZCBDb25zdW1lciBUZXJtcyAoXG4gICAgICAgIDxMaW5rIHVybD17J2h0dHBzOi8vYW50aHJvcGljLmNvbS9sZWdhbC90ZXJtcyd9PjwvTGluaz4pIGFuZCBQcml2YWN5XG4gICAgICAgIFBvbGljeSAoPExpbmsgdXJsPXsnaHR0cHM6Ly9hbnRocm9waWMuY29tL2xlZ2FsL3ByaXZhY3knfT48L0xpbms+KVxuICAgICAgPC9UZXh0PlxuICAgIDwvPlxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBHcm92ZURpYWxvZyh7XG4gIHNob3dJZkFscmVhZHlWaWV3ZWQsXG4gIGxvY2F0aW9uLFxuICBvbkRvbmUsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtzaG91bGRTaG93RGlhbG9nLCBzZXRTaG91bGRTaG93RGlhbG9nXSA9IHVzZVN0YXRlPGJvb2xlYW4gfCBudWxsPihudWxsKVxuICBjb25zdCBbZ3JvdmVDb25maWcsIHNldEdyb3ZlQ29uZmlnXSA9IHVzZVN0YXRlPEdyb3ZlQ29uZmlnIHwgbnVsbD4obnVsbClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGFzeW5jIGZ1bmN0aW9uIGNoZWNrR3JvdmVTZXR0aW5ncygpIHtcbiAgICAgIGNvbnN0IFtzZXR0aW5nc1Jlc3VsdCwgY29uZmlnUmVzdWx0XSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgICAgZ2V0R3JvdmVTZXR0aW5ncygpLFxuICAgICAgICBnZXRHcm92ZU5vdGljZUNvbmZpZygpLFxuICAgICAgXSlcblxuICAgICAgLy8gRXh0cmFjdCBjb25maWcgZGF0YSBpZiBzdWNjZXNzZnVsLCBvdGhlcndpc2UgbnVsbFxuICAgICAgY29uc3QgY29uZmlnID0gY29uZmlnUmVzdWx0LnN1Y2Nlc3MgPyBjb25maWdSZXN1bHQuZGF0YSA6IG51bGxcbiAgICAgIHNldEdyb3ZlQ29uZmlnKGNvbmZpZylcblxuICAgICAgLy8gRGV0ZXJtaW5lIGlmIHdlIHNob3VsZCBzaG93IHRoZSBkaWFsb2cgKHJldHVybnMgZmFsc2Ugb24gQVBJIGZhaWx1cmUpXG4gICAgICBjb25zdCBzaG91bGRTaG93ID0gY2FsY3VsYXRlU2hvdWxkU2hvd0dyb3ZlKFxuICAgICAgICBzZXR0aW5nc1Jlc3VsdCxcbiAgICAgICAgY29uZmlnUmVzdWx0LFxuICAgICAgICBzaG93SWZBbHJlYWR5Vmlld2VkLFxuICAgICAgKVxuXG4gICAgICBzZXRTaG91bGRTaG93RGlhbG9nKHNob3VsZFNob3cpXG4gICAgICAvLyBJZiB3ZSBzaG91bGRuJ3Qgc2hvdyB0aGUgZGlhbG9nLCBpbW1lZGlhdGVseSBjYWxsIG9uRG9uZVxuICAgICAgaWYgKCFzaG91bGRTaG93KSB7XG4gICAgICAgIG9uRG9uZSgnc2tpcF9yZW5kZXJpbmcnKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIC8vIE1hcmsgYXMgdmlld2VkIGV2ZXJ5IHRpbWUgd2Ugc2hvdyB0aGUgZGlhbG9nIChmb3IgcmVtaW5kZXIgZnJlcXVlbmN5IHRyYWNraW5nKVxuICAgICAgdm9pZCBtYXJrR3JvdmVOb3RpY2VWaWV3ZWQoKVxuICAgICAgLy8gTG9nIHRoYXQgdGhlIEdyb3ZlIHBvbGljeSBkaWFsb2cgd2FzIHNob3duXG4gICAgICBsb2dFdmVudCgndGVuZ3VfZ3JvdmVfcG9saWN5X3ZpZXdlZCcsIHtcbiAgICAgICAgbG9jYXRpb246XG4gICAgICAgICAgbG9jYXRpb24gYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgZGlzbWlzc2FibGU6XG4gICAgICAgICAgY29uZmlnPy5ub3RpY2VfaXNfZ3JhY2VfcGVyaW9kIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KVxuICAgIH1cblxuICAgIHZvaWQgY2hlY2tHcm92ZVNldHRpbmdzKClcbiAgfSwgW3Nob3dJZkFscmVhZHlWaWV3ZWQsIGxvY2F0aW9uLCBvbkRvbmVdKVxuXG4gIC8vIExvYWRpbmcgc3RhdGVcbiAgaWYgKHNob3VsZFNob3dEaWFsb2cgPT09IG51bGwpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gVXNlciBoYXMgYWxyZWFkeSBzZXQgcHJlZmVyZW5jZXMsIGRvbid0IHNob3cgZGlhbG9nXG4gIGlmICghc2hvdWxkU2hvd0RpYWxvZykge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBhc3luYyBmdW5jdGlvbiBvbkNoYW5nZShcbiAgICB2YWx1ZTogJ2FjY2VwdF9vcHRfaW4nIHwgJ2FjY2VwdF9vcHRfb3V0JyB8ICdkZWZlcicgfCAnZXNjYXBlJyxcbiAgKSB7XG4gICAgc3dpdGNoICh2YWx1ZSkge1xuICAgICAgY2FzZSAnYWNjZXB0X29wdF9pbic6IHtcbiAgICAgICAgYXdhaXQgdXBkYXRlR3JvdmVTZXR0aW5ncyh0cnVlKVxuICAgICAgICBsb2dFdmVudCgndGVuZ3VfZ3JvdmVfcG9saWN5X3N1Ym1pdHRlZCcsIHtcbiAgICAgICAgICBzdGF0ZTogdHJ1ZSxcbiAgICAgICAgICBkaXNtaXNzYWJsZTpcbiAgICAgICAgICAgIGdyb3ZlQ29uZmlnPy5ub3RpY2VfaXNfZ3JhY2VfcGVyaW9kIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIH0pXG4gICAgICAgIGJyZWFrXG4gICAgICB9XG4gICAgICBjYXNlICdhY2NlcHRfb3B0X291dCc6IHtcbiAgICAgICAgYXdhaXQgdXBkYXRlR3JvdmVTZXR0aW5ncyhmYWxzZSlcbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2dyb3ZlX3BvbGljeV9zdWJtaXR0ZWQnLCB7XG4gICAgICAgICAgc3RhdGU6IGZhbHNlLFxuICAgICAgICAgIGRpc21pc3NhYmxlOlxuICAgICAgICAgICAgZ3JvdmVDb25maWc/Lm5vdGljZV9pc19ncmFjZV9wZXJpb2QgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgfSlcbiAgICAgICAgYnJlYWtcbiAgICAgIH1cbiAgICAgIGNhc2UgJ2RlZmVyJzpcbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2dyb3ZlX3BvbGljeV9kaXNtaXNzZWQnLCB7XG4gICAgICAgICAgc3RhdGU6IHRydWUsXG4gICAgICAgIH0pXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdlc2NhcGUnOlxuICAgICAgICBsb2dFdmVudCgndGVuZ3VfZ3JvdmVfcG9saWN5X2VzY2FwZWQnLCB7fSlcbiAgICAgICAgYnJlYWtcbiAgICB9XG5cbiAgICBvbkRvbmUodmFsdWUpXG4gIH1cblxuICBjb25zdCBhY2NlcHRPcHRpb25zID0gZ3JvdmVDb25maWc/LmRvbWFpbl9leGNsdWRlZFxuICAgID8gW1xuICAgICAgICB7XG4gICAgICAgICAgbGFiZWw6XG4gICAgICAgICAgICAnQWNjZXB0IHRlcm1zIMK3IEhlbHAgaW1wcm92ZSBDbGF1ZGU6IE9GRiAoZm9yIGVtYWlscyB3aXRoIHlvdXIgZG9tYWluKScsXG4gICAgICAgICAgdmFsdWU6ICdhY2NlcHRfb3B0X291dCcsXG4gICAgICAgIH0sXG4gICAgICBdXG4gICAgOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBsYWJlbDogJ0FjY2VwdCB0ZXJtcyDCtyBIZWxwIGltcHJvdmUgQ2xhdWRlOiBPTicsXG4gICAgICAgICAgdmFsdWU6ICdhY2NlcHRfb3B0X2luJyxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgIGxhYmVsOiAnQWNjZXB0IHRlcm1zIMK3IEhlbHAgaW1wcm92ZSBDbGF1ZGU6IE9GRicsXG4gICAgICAgICAgdmFsdWU6ICdhY2NlcHRfb3B0X291dCcsXG4gICAgICAgIH0sXG4gICAgICBdXG5cbiAgZnVuY3Rpb24gaGFuZGxlQ2FuY2VsKCk6IHZvaWQge1xuICAgIGlmIChncm92ZUNvbmZpZz8ubm90aWNlX2lzX2dyYWNlX3BlcmlvZCkge1xuICAgICAgdm9pZCBvbkNoYW5nZSgnZGVmZXInKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIHZvaWQgb25DaGFuZ2UoJ2VzY2FwZScpXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxEaWFsb2dcbiAgICAgIHRpdGxlPVwiVXBkYXRlcyB0byBDb25zdW1lciBUZXJtcyBhbmQgUG9saWNpZXNcIlxuICAgICAgY29sb3I9XCJwcm9mZXNzaW9uYWxCbHVlXCJcbiAgICAgIG9uQ2FuY2VsPXtoYW5kbGVDYW5jZWx9XG4gICAgICBpbnB1dEd1aWRlPXtleGl0U3RhdGUgPT5cbiAgICAgICAgZXhpdFN0YXRlLnBlbmRpbmcgPyAoXG4gICAgICAgICAgPFRleHQ+UHJlc3Mge2V4aXRTdGF0ZS5rZXlOYW1lfSBhZ2FpbiB0byBleGl0PC9UZXh0PlxuICAgICAgICApIDogKFxuICAgICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cImNvbmZpcm1cIiAvPlxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiRXNjXCIgYWN0aW9uPVwiY2FuY2VsXCIgLz5cbiAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgKVxuICAgICAgfVxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9IGZsZXhHcm93PXsxfT5cbiAgICAgICAgICB7Z3JvdmVDb25maWc/Lm5vdGljZV9pc19ncmFjZV9wZXJpb2QgPyAoXG4gICAgICAgICAgICA8R3JhY2VQZXJpb2RDb250ZW50Qm9keSAvPlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICA8UG9zdEdyYWNlUGVyaW9kQ29udGVudEJvZHkgLz5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveCBmbGV4U2hyaW5rPXswfT5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cInByb2Zlc3Npb25hbEJsdWVcIj57TkVXX1RFUk1TX0FTQ0lJfTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cblxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPFRleHQgYm9sZD5QbGVhc2Ugc2VsZWN0IGhvdyB5b3UmYXBvcztkIGxpa2UgdG8gY29udGludWU8L1RleHQ+XG4gICAgICAgICAgPFRleHQ+WW91ciBjaG9pY2UgdGFrZXMgZWZmZWN0IGltbWVkaWF0ZWx5IHVwb24gY29uZmlybWF0aW9uLjwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgPFNlbGVjdFxuICAgICAgICAgIG9wdGlvbnM9e1tcbiAgICAgICAgICAgIC4uLmFjY2VwdE9wdGlvbnMsXG4gICAgICAgICAgICAvLyBPbmx5IHNob3cgXCJOb3Qgbm93XCIgaWYgaW4gZ3JhY2UgcGVyaW9kXG4gICAgICAgICAgICAuLi4oZ3JvdmVDb25maWc/Lm5vdGljZV9pc19ncmFjZV9wZXJpb2RcbiAgICAgICAgICAgICAgPyBbeyBsYWJlbDogJ05vdCBub3cnLCB2YWx1ZTogJ2RlZmVyJyB9XVxuICAgICAgICAgICAgICA6IFtdKSxcbiAgICAgICAgICBdfVxuICAgICAgICAgIG9uQ2hhbmdlPXt2YWx1ZSA9PlxuICAgICAgICAgICAgb25DaGFuZ2UodmFsdWUgYXMgJ2FjY2VwdF9vcHRfaW4nIHwgJ2FjY2VwdF9vcHRfb3V0JyB8ICdkZWZlcicpXG4gICAgICAgICAgfVxuICAgICAgICAgIG9uQ2FuY2VsPXtoYW5kbGVDYW5jZWx9XG4gICAgICAgIC8+XG4gICAgICA8L0JveD5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuXG50eXBlIFByaXZhY3lTZXR0aW5nc0RpYWxvZ1Byb3BzID0ge1xuICBzZXR0aW5nczogQWNjb3VudFNldHRpbmdzXG4gIGRvbWFpbkV4Y2x1ZGVkPzogYm9vbGVhblxuICBvbkRvbmUoKTogdm9pZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gUHJpdmFjeVNldHRpbmdzRGlhbG9nKHtcbiAgc2V0dGluZ3MsXG4gIGRvbWFpbkV4Y2x1ZGVkLFxuICBvbkRvbmUsXG59OiBQcml2YWN5U2V0dGluZ3NEaWFsb2dQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtncm92ZUVuYWJsZWQsIHNldEdyb3ZlRW5hYmxlZF0gPSB1c2VTdGF0ZShzZXR0aW5ncy5ncm92ZV9lbmFibGVkKVxuXG4gIFJlYWN0LnVzZUVmZmVjdCgoKSA9PiB7XG4gICAgbG9nRXZlbnQoJ3Rlbmd1X2dyb3ZlX3ByaXZhY3lfc2V0dGluZ3Nfdmlld2VkJywge30pXG4gIH0sIFtdKVxuXG4gIHVzZUlucHV0KGFzeW5jIChpbnB1dCwga2V5KSA9PiB7XG4gICAgLy8gVG9nZ2xlIHRoZSBzZXR0aW5nIHdoZW4gZW50ZXIvdGFiL3NwYWNlIGlzIHByZXNzZWRcbiAgICBpZiAoIWRvbWFpbkV4Y2x1ZGVkICYmIChrZXkudGFiIHx8IGtleS5yZXR1cm4gfHwgaW5wdXQgPT09ICcgJykpIHtcbiAgICAgIGNvbnN0IG5ld1ZhbHVlID0gIWdyb3ZlRW5hYmxlZFxuICAgICAgc2V0R3JvdmVFbmFibGVkKG5ld1ZhbHVlKVxuICAgICAgYXdhaXQgdXBkYXRlR3JvdmVTZXR0aW5ncyhuZXdWYWx1ZSlcbiAgICB9XG4gIH0pXG5cbiAgbGV0IHZhbHVlQ29tcG9uZW50ID0gPFRleHQgY29sb3I9XCJlcnJvclwiPmZhbHNlPC9UZXh0PlxuICBpZiAoZG9tYWluRXhjbHVkZWQpIHtcbiAgICB2YWx1ZUNvbXBvbmVudCA9IChcbiAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5mYWxzZSAoZm9yIGVtYWlscyB3aXRoIHlvdXIgZG9tYWluKTwvVGV4dD5cbiAgICApXG4gIH0gZWxzZSBpZiAoZ3JvdmVFbmFibGVkKSB7XG4gICAgdmFsdWVDb21wb25lbnQgPSA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj50cnVlPC9UZXh0PlxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT1cIkRhdGEgUHJpdmFjeVwiXG4gICAgICBjb2xvcj1cInByb2Zlc3Npb25hbEJsdWVcIlxuICAgICAgb25DYW5jZWw9e29uRG9uZX1cbiAgICAgIGlucHV0R3VpZGU9e2V4aXRTdGF0ZSA9PlxuICAgICAgICBleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICA8VGV4dD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8L1RleHQ+XG4gICAgICAgICkgOiBkb21haW5FeGNsdWRlZCA/IChcbiAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFc2NcIiBhY3Rpb249XCJjYW5jZWxcIiAvPlxuICAgICAgICApIDogKFxuICAgICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlci9UYWIvU3BhY2VcIiBhY3Rpb249XCJ0b2dnbGVcIiAvPlxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiRXNjXCIgYWN0aW9uPVwiY2FuY2VsXCIgLz5cbiAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgKVxuICAgICAgfVxuICAgID5cbiAgICAgIDxUZXh0PlxuICAgICAgICBSZXZpZXcgYW5kIG1hbmFnZSB5b3VyIHByaXZhY3kgc2V0dGluZ3MgYXR7JyAnfVxuICAgICAgICA8TGluayB1cmw9eydodHRwczovL2NsYXVkZS5haS9zZXR0aW5ncy9kYXRhLXByaXZhY3ktY29udHJvbHMnfT48L0xpbms+XG4gICAgICA8L1RleHQ+XG5cbiAgICAgIDxCb3g+XG4gICAgICAgIDxCb3ggd2lkdGg9ezQ0fT5cbiAgICAgICAgICA8VGV4dCBib2xkPkhlbHAgaW1wcm92ZSBDbGF1ZGU8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94Pnt2YWx1ZUNvbXBvbmVudH08L0JveD5cbiAgICAgIDwvQm94PlxuICAgIDwvRGlhbG9nPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUlDLFNBQVMs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 \ No newline at end of file diff --git a/components/hooks/HooksConfigMenu.tsx b/components/hooks/HooksConfigMenu.tsx new file mode 100644 index 0000000..ea39f82 --- /dev/null +++ b/components/hooks/HooksConfigMenu.tsx @@ -0,0 +1,578 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * HooksConfigMenu is a read-only browser for configured hooks. + * + * Users can drill into each hook event, see configured matchers and hooks + * (of any type: command, prompt, agent, http), and view individual hook + * details. To add or modify hooks, users should edit settings.json directly + * or ask Claude — the menu directs them there. + * + * The menu is read-only because the old editing UI only supported + * command-type hooks and duplicating the settings.json editing surface + * in-menu for all four types would be a maintenance burden. + */ +import * as React from 'react'; +import { useCallback, useMemo, useState } from 'react'; +import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'; +import { useAppState, useAppStateStore } from 'src/state/AppState.js'; +import type { CommandResultDisplay } from '../../commands.js'; +import { useSettingsChange } from '../../hooks/useSettingsChange.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { getHookEventMetadata, getHooksForMatcher, getMatcherMetadata, getSortedMatchersForEvent, groupHooksByEventAndMatcher } from '../../utils/hooks/hooksConfigManager.js'; +import type { IndividualHookConfig } from '../../utils/hooks/hooksSettings.js'; +import { getSettings_DEPRECATED, getSettingsForSource } from '../../utils/settings/settings.js'; +import { plural } from '../../utils/stringUtils.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { SelectEventMode } from './SelectEventMode.js'; +import { SelectHookMode } from './SelectHookMode.js'; +import { SelectMatcherMode } from './SelectMatcherMode.js'; +import { ViewHookMode } from './ViewHookMode.js'; +type Props = { + toolNames: string[]; + onExit: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; +}; +type ModeState = { + mode: 'select-event'; +} | { + mode: 'select-matcher'; + event: HookEvent; +} | { + mode: 'select-hook'; + event: HookEvent; + matcher: string; +} | { + mode: 'view-hook'; + event: HookEvent; + hook: IndividualHookConfig; +}; +export function HooksConfigMenu(t0) { + const $ = _c(100); + const { + toolNames, + onExit + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + mode: "select-event" + }; + $[0] = t1; + } else { + t1 = $[0]; + } + const [modeState, setModeState] = useState(t1); + const [disabledByPolicy, setDisabledByPolicy] = useState(_temp); + const [restrictedByPolicy, setRestrictedByPolicy] = useState(_temp2); + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = source => { + if (source === "policySettings") { + const settings_0 = getSettings_DEPRECATED(); + const hooksDisabled_0 = settings_0?.disableAllHooks === true; + setDisabledByPolicy(hooksDisabled_0 && getSettingsForSource("policySettings")?.disableAllHooks === true); + setRestrictedByPolicy(getSettingsForSource("policySettings")?.allowManagedHooksOnly === true); + } + }; + $[1] = t2; + } else { + t2 = $[1]; + } + useSettingsChange(t2); + const mode = modeState.mode; + const selectedEvent = "event" in modeState ? modeState.event : "PreToolUse"; + const selectedMatcher = "matcher" in modeState ? modeState.matcher : null; + const mcp = useAppState(_temp3); + const appStateStore = useAppStateStore(); + let t3; + if ($[2] !== mcp.tools || $[3] !== toolNames) { + t3 = [...toolNames, ...mcp.tools.map(_temp4)]; + $[2] = mcp.tools; + $[3] = toolNames; + $[4] = t3; + } else { + t3 = $[4]; + } + const combinedToolNames = t3; + let t4; + if ($[5] !== appStateStore || $[6] !== combinedToolNames) { + t4 = groupHooksByEventAndMatcher(appStateStore.getState(), combinedToolNames); + $[5] = appStateStore; + $[6] = combinedToolNames; + $[7] = t4; + } else { + t4 = $[7]; + } + const hooksByEventAndMatcher = t4; + let t5; + if ($[8] !== hooksByEventAndMatcher || $[9] !== selectedEvent) { + t5 = getSortedMatchersForEvent(hooksByEventAndMatcher, selectedEvent); + $[8] = hooksByEventAndMatcher; + $[9] = selectedEvent; + $[10] = t5; + } else { + t5 = $[10]; + } + const sortedMatchersForSelectedEvent = t5; + let t6; + if ($[11] !== hooksByEventAndMatcher || $[12] !== selectedEvent || $[13] !== selectedMatcher) { + t6 = getHooksForMatcher(hooksByEventAndMatcher, selectedEvent, selectedMatcher); + $[11] = hooksByEventAndMatcher; + $[12] = selectedEvent; + $[13] = selectedMatcher; + $[14] = t6; + } else { + t6 = $[14]; + } + const hooksForSelectedMatcher = t6; + let t7; + if ($[15] !== onExit) { + t7 = () => { + onExit("Hooks dialog dismissed", { + display: "system" + }); + }; + $[15] = onExit; + $[16] = t7; + } else { + t7 = $[16]; + } + const handleExit = t7; + const t8 = mode === "select-event"; + let t9; + if ($[17] !== t8) { + t9 = { + context: "Confirmation", + isActive: t8 + }; + $[17] = t8; + $[18] = t9; + } else { + t9 = $[18]; + } + useKeybinding("confirm:no", handleExit, t9); + let t10; + if ($[19] === Symbol.for("react.memo_cache_sentinel")) { + t10 = () => { + setModeState({ + mode: "select-event" + }); + }; + $[19] = t10; + } else { + t10 = $[19]; + } + const t11 = mode === "select-matcher"; + let t12; + if ($[20] !== t11) { + t12 = { + context: "Confirmation", + isActive: t11 + }; + $[20] = t11; + $[21] = t12; + } else { + t12 = $[21]; + } + useKeybinding("confirm:no", t10, t12); + let t13; + if ($[22] !== combinedToolNames || $[23] !== modeState) { + t13 = () => { + if ("event" in modeState) { + if (getMatcherMetadata(modeState.event, combinedToolNames) !== undefined) { + setModeState({ + mode: "select-matcher", + event: modeState.event + }); + } else { + setModeState({ + mode: "select-event" + }); + } + } + }; + $[22] = combinedToolNames; + $[23] = modeState; + $[24] = t13; + } else { + t13 = $[24]; + } + const t14 = mode === "select-hook"; + let t15; + if ($[25] !== t14) { + t15 = { + context: "Confirmation", + isActive: t14 + }; + $[25] = t14; + $[26] = t15; + } else { + t15 = $[26]; + } + useKeybinding("confirm:no", t13, t15); + let t16; + if ($[27] !== modeState) { + t16 = () => { + if (modeState.mode === "view-hook") { + const { + event, + hook + } = modeState; + setModeState({ + mode: "select-hook", + event, + matcher: hook.matcher || "" + }); + } + }; + $[27] = modeState; + $[28] = t16; + } else { + t16 = $[28]; + } + const t17 = mode === "view-hook"; + let t18; + if ($[29] !== t17) { + t18 = { + context: "Confirmation", + isActive: t17 + }; + $[29] = t17; + $[30] = t18; + } else { + t18 = $[30]; + } + useKeybinding("confirm:no", t16, t18); + let t19; + if ($[31] !== combinedToolNames) { + t19 = getHookEventMetadata(combinedToolNames); + $[31] = combinedToolNames; + $[32] = t19; + } else { + t19 = $[32]; + } + const hookEventMetadata = t19; + const settings_1 = getSettings_DEPRECATED(); + const hooksDisabled_1 = settings_1?.disableAllHooks === true; + let t20; + if ($[33] !== hooksByEventAndMatcher) { + const byEvent = {}; + let total = 0; + for (const [event_0, matchers] of Object.entries(hooksByEventAndMatcher)) { + const eventCount = Object.values(matchers).reduce(_temp5, 0); + byEvent[event_0 as HookEvent] = eventCount; + total = total + eventCount; + } + t20 = { + hooksByEvent: byEvent, + totalHooksCount: total + }; + $[33] = hooksByEventAndMatcher; + $[34] = t20; + } else { + t20 = $[34]; + } + const { + hooksByEvent, + totalHooksCount + } = t20; + if (hooksDisabled_1) { + let t21; + if ($[35] === Symbol.for("react.memo_cache_sentinel")) { + t21 = disabled; + $[35] = t21; + } else { + t21 = $[35]; + } + const t22 = disabledByPolicy && " by a managed settings file"; + let t23; + if ($[36] !== totalHooksCount) { + t23 = {totalHooksCount}; + $[36] = totalHooksCount; + $[37] = t23; + } else { + t23 = $[37]; + } + let t24; + if ($[38] !== totalHooksCount) { + t24 = plural(totalHooksCount, "hook"); + $[38] = totalHooksCount; + $[39] = t24; + } else { + t24 = $[39]; + } + let t25; + if ($[40] !== totalHooksCount) { + t25 = plural(totalHooksCount, "is", "are"); + $[40] = totalHooksCount; + $[41] = t25; + } else { + t25 = $[41]; + } + let t26; + if ($[42] !== t22 || $[43] !== t23 || $[44] !== t24 || $[45] !== t25) { + t26 = All hooks are currently {t21}{t22}. You have{" "}{t23} configured{" "}{t24} that{" "}{t25} not running.; + $[42] = t22; + $[43] = t23; + $[44] = t24; + $[45] = t25; + $[46] = t26; + } else { + t26 = $[46]; + } + let t27; + let t28; + let t29; + let t30; + if ($[47] === Symbol.for("react.memo_cache_sentinel")) { + t27 = When hooks are disabled:; + t28 = · No hook commands will execute; + t29 = · StatusLine will not be displayed; + t30 = · Tool operations will proceed without hook validation; + $[47] = t27; + $[48] = t28; + $[49] = t29; + $[50] = t30; + } else { + t27 = $[47]; + t28 = $[48]; + t29 = $[49]; + t30 = $[50]; + } + let t31; + if ($[51] !== t26) { + t31 = {t26}{t27}{t28}{t29}{t30}; + $[51] = t26; + $[52] = t31; + } else { + t31 = $[52]; + } + let t32; + if ($[53] !== disabledByPolicy) { + t32 = !disabledByPolicy && To re-enable hooks, remove "disableAllHooks" from settings.json or ask Claude.; + $[53] = disabledByPolicy; + $[54] = t32; + } else { + t32 = $[54]; + } + let t33; + if ($[55] !== t31 || $[56] !== t32) { + t33 = {t31}{t32}; + $[55] = t31; + $[56] = t32; + $[57] = t33; + } else { + t33 = $[57]; + } + let t34; + if ($[58] !== handleExit || $[59] !== t33) { + t34 = {t33}; + $[58] = handleExit; + $[59] = t33; + $[60] = t34; + } else { + t34 = $[60]; + } + return t34; + } + switch (modeState.mode) { + case "select-event": + { + let t21; + if ($[61] !== combinedToolNames) { + t21 = event_2 => { + if (getMatcherMetadata(event_2, combinedToolNames) !== undefined) { + setModeState({ + mode: "select-matcher", + event: event_2 + }); + } else { + setModeState({ + mode: "select-hook", + event: event_2, + matcher: "" + }); + } + }; + $[61] = combinedToolNames; + $[62] = t21; + } else { + t21 = $[62]; + } + let t22; + if ($[63] !== handleExit || $[64] !== hookEventMetadata || $[65] !== hooksByEvent || $[66] !== restrictedByPolicy || $[67] !== t21 || $[68] !== totalHooksCount) { + t22 = ; + $[63] = handleExit; + $[64] = hookEventMetadata; + $[65] = hooksByEvent; + $[66] = restrictedByPolicy; + $[67] = t21; + $[68] = totalHooksCount; + $[69] = t22; + } else { + t22 = $[69]; + } + return t22; + } + case "select-matcher": + { + const t21 = hookEventMetadata[modeState.event]; + let t22; + if ($[70] !== modeState.event) { + t22 = matcher => { + setModeState({ + mode: "select-hook", + event: modeState.event, + matcher + }); + }; + $[70] = modeState.event; + $[71] = t22; + } else { + t22 = $[71]; + } + let t23; + if ($[72] === Symbol.for("react.memo_cache_sentinel")) { + t23 = () => { + setModeState({ + mode: "select-event" + }); + }; + $[72] = t23; + } else { + t23 = $[72]; + } + let t24; + if ($[73] !== hooksByEventAndMatcher || $[74] !== modeState.event || $[75] !== sortedMatchersForSelectedEvent || $[76] !== t21.description || $[77] !== t22) { + t24 = ; + $[73] = hooksByEventAndMatcher; + $[74] = modeState.event; + $[75] = sortedMatchersForSelectedEvent; + $[76] = t21.description; + $[77] = t22; + $[78] = t24; + } else { + t24 = $[78]; + } + return t24; + } + case "select-hook": + { + const t21 = hookEventMetadata[modeState.event]; + let t22; + if ($[79] !== modeState.event) { + t22 = hook_1 => { + setModeState({ + mode: "view-hook", + event: modeState.event, + hook: hook_1 + }); + }; + $[79] = modeState.event; + $[80] = t22; + } else { + t22 = $[80]; + } + let t23; + if ($[81] !== combinedToolNames || $[82] !== modeState.event) { + t23 = () => { + if (getMatcherMetadata(modeState.event, combinedToolNames) !== undefined) { + setModeState({ + mode: "select-matcher", + event: modeState.event + }); + } else { + setModeState({ + mode: "select-event" + }); + } + }; + $[81] = combinedToolNames; + $[82] = modeState.event; + $[83] = t23; + } else { + t23 = $[83]; + } + let t24; + if ($[84] !== hooksForSelectedMatcher || $[85] !== modeState.event || $[86] !== modeState.matcher || $[87] !== t21 || $[88] !== t22 || $[89] !== t23) { + t24 = ; + $[84] = hooksForSelectedMatcher; + $[85] = modeState.event; + $[86] = modeState.matcher; + $[87] = t21; + $[88] = t22; + $[89] = t23; + $[90] = t24; + } else { + t24 = $[90]; + } + return t24; + } + case "view-hook": + { + const t21 = modeState.hook; + let t22; + if ($[91] !== combinedToolNames || $[92] !== modeState.event) { + t22 = getMatcherMetadata(modeState.event, combinedToolNames); + $[91] = combinedToolNames; + $[92] = modeState.event; + $[93] = t22; + } else { + t22 = $[93]; + } + const t23 = t22 !== undefined; + let t24; + if ($[94] !== modeState) { + t24 = () => { + const { + event: event_1, + hook: hook_0 + } = modeState; + setModeState({ + mode: "select-hook", + event: event_1, + matcher: hook_0.matcher || "" + }); + }; + $[94] = modeState; + $[95] = t24; + } else { + t24 = $[95]; + } + let t25; + if ($[96] !== modeState.hook || $[97] !== t23 || $[98] !== t24) { + t25 = ; + $[96] = modeState.hook; + $[97] = t23; + $[98] = t24; + $[99] = t25; + } else { + t25 = $[99]; + } + return t25; + } + } +} +function _temp6() { + return Esc to close; +} +function _temp5(sum, hooks) { + return sum + hooks.length; +} +function _temp4(tool) { + return tool.name; +} +function _temp3(s) { + return s.mcp; +} +function _temp2() { + return getSettingsForSource("policySettings")?.allowManagedHooksOnly === true; +} +function _temp() { + const settings = getSettings_DEPRECATED(); + const hooksDisabled = settings?.disableAllHooks === true; + return hooksDisabled && getSettingsForSource("policySettings")?.disableAllHooks === true; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlTWVtbyIsInVzZVN0YXRlIiwiSG9va0V2ZW50IiwidXNlQXBwU3RhdGUiLCJ1c2VBcHBTdGF0ZVN0b3JlIiwiQ29tbWFuZFJlc3VsdERpc3BsYXkiLCJ1c2VTZXR0aW5nc0NoYW5nZSIsIkJveCIsIlRleHQiLCJ1c2VLZXliaW5kaW5nIiwiZ2V0SG9va0V2ZW50TWV0YWRhdGEiLCJnZXRIb29rc0Zvck1hdGNoZXIiLCJnZXRNYXRjaGVyTWV0YWRhdGEiLCJnZXRTb3J0ZWRNYXRjaGVyc0ZvckV2ZW50IiwiZ3JvdXBIb29rc0J5RXZlbnRBbmRNYXRjaGVyIiwiSW5kaXZpZHVhbEhvb2tDb25maWciLCJnZXRTZXR0aW5nc19ERVBSRUNBVEVEIiwiZ2V0U2V0dGluZ3NGb3JTb3VyY2UiLCJwbHVyYWwiLCJEaWFsb2ciLCJTZWxlY3RFdmVudE1vZGUiLCJTZWxlY3RIb29rTW9kZSIsIlNlbGVjdE1hdGNoZXJNb2RlIiwiVmlld0hvb2tNb2RlIiwiUHJvcHMiLCJ0b29sTmFtZXMiLCJvbkV4aXQiLCJyZXN1bHQiLCJvcHRpb25zIiwiZGlzcGxheSIsIk1vZGVTdGF0ZSIsIm1vZGUiLCJldmVudCIsIm1hdGNoZXIiLCJob29rIiwiSG9va3NDb25maWdNZW51IiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN5bWJvbCIsImZvciIsIm1vZGVTdGF0ZSIsInNldE1vZGVTdGF0ZSIsImRpc2FibGVkQnlQb2xpY3kiLCJzZXREaXNhYmxlZEJ5UG9saWN5IiwiX3RlbXAiLCJyZXN0cmljdGVkQnlQb2xpY3kiLCJzZXRSZXN0cmljdGVkQnlQb2xpY3kiLCJfdGVtcDIiLCJ0MiIsInNvdXJjZSIsInNldHRpbmdzXzAiLCJob29rc0Rpc2FibGVkXzAiLCJzZXR0aW5ncyIsImRpc2FibGVBbGxIb29rcyIsImFsbG93TWFuYWdlZEhvb2tzT25seSIsInNlbGVjdGVkRXZlbnQiLCJzZWxlY3RlZE1hdGNoZXIiLCJtY3AiLCJfdGVtcDMiLCJhcHBTdGF0ZVN0b3JlIiwidDMiLCJ0b29scyIsIm1hcCIsIl90ZW1wNCIsImNvbWJpbmVkVG9vbE5hbWVzIiwidDQiLCJnZXRTdGF0ZSIsImhvb2tzQnlFdmVudEFuZE1hdGNoZXIiLCJ0NSIsInNvcnRlZE1hdGNoZXJzRm9yU2VsZWN0ZWRFdmVudCIsInQ2IiwiaG9va3NGb3JTZWxlY3RlZE1hdGNoZXIiLCJ0NyIsImhhbmRsZUV4aXQiLCJ0OCIsInQ5IiwiY29udGV4dCIsImlzQWN0aXZlIiwidDEwIiwidDExIiwidDEyIiwidDEzIiwidW5kZWZpbmVkIiwidDE0IiwidDE1IiwidDE2IiwidDE3IiwidDE4IiwidDE5IiwiaG9va0V2ZW50TWV0YWRhdGEiLCJzZXR0aW5nc18xIiwiaG9va3NEaXNhYmxlZF8xIiwidDIwIiwiYnlFdmVudCIsInRvdGFsIiwiZXZlbnRfMCIsIm1hdGNoZXJzIiwiT2JqZWN0IiwiZW50cmllcyIsImV2ZW50Q291bnQiLCJ2YWx1ZXMiLCJyZWR1Y2UiLCJfdGVtcDUiLCJob29rc0J5RXZlbnQiLCJ0b3RhbEhvb2tzQ291bnQiLCJob29rc0Rpc2FibGVkIiwidDIxIiwidDIyIiwidDIzIiwidDI0IiwidDI1IiwidDI2IiwidDI3IiwidDI4IiwidDI5IiwidDMwIiwidDMxIiwidDMyIiwidDMzIiwidDM0IiwiX3RlbXA2IiwiZXZlbnRfMiIsImRlc2NyaXB0aW9uIiwiaG9va18xIiwiZXZlbnRfMSIsImhvb2tfMCIsInN1bSIsImhvb2tzIiwibGVuZ3RoIiwidG9vbCIsIm5hbWUiLCJzIl0sInNvdXJjZXMiOlsiSG9va3NDb25maWdNZW51LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEhvb2tzQ29uZmlnTWVudSBpcyBhIHJlYWQtb25seSBicm93c2VyIGZvciBjb25maWd1cmVkIGhvb2tzLlxuICpcbiAqIFVzZXJzIGNhbiBkcmlsbCBpbnRvIGVhY2ggaG9vayBldmVudCwgc2VlIGNvbmZpZ3VyZWQgbWF0Y2hlcnMgYW5kIGhvb2tzXG4gKiAob2YgYW55IHR5cGU6IGNvbW1hbmQsIHByb21wdCwgYWdlbnQsIGh0dHApLCBhbmQgdmlldyBpbmRpdmlkdWFsIGhvb2tcbiAqIGRldGFpbHMuIFRvIGFkZCBvciBtb2RpZnkgaG9va3MsIHVzZXJzIHNob3VsZCBlZGl0IHNldHRpbmdzLmpzb24gZGlyZWN0bHlcbiAqIG9yIGFzayBDbGF1ZGUg4oCUIHRoZSBtZW51IGRpcmVjdHMgdGhlbSB0aGVyZS5cbiAqXG4gKiBUaGUgbWVudSBpcyByZWFkLW9ubHkgYmVjYXVzZSB0aGUgb2xkIGVkaXRpbmcgVUkgb25seSBzdXBwb3J0ZWRcbiAqIGNvbW1hbmQtdHlwZSBob29rcyBhbmQgZHVwbGljYXRpbmcgdGhlIHNldHRpbmdzLmpzb24gZWRpdGluZyBzdXJmYWNlXG4gKiBpbi1tZW51IGZvciBhbGwgZm91ciB0eXBlcyB3b3VsZCBiZSBhIG1haW50ZW5hbmNlIGJ1cmRlbi5cbiAqL1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VDYWxsYmFjaywgdXNlTWVtbywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgSG9va0V2ZW50IH0gZnJvbSAnc3JjL2VudHJ5cG9pbnRzL2FnZW50U2RrVHlwZXMuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSwgdXNlQXBwU3RhdGVTdG9yZSB9IGZyb20gJ3NyYy9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgQ29tbWFuZFJlc3VsdERpc3BsYXkgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IHVzZVNldHRpbmdzQ2hhbmdlIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlU2V0dGluZ3NDaGFuZ2UuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7XG4gIGdldEhvb2tFdmVudE1ldGFkYXRhLFxuICBnZXRIb29rc0Zvck1hdGNoZXIsXG4gIGdldE1hdGNoZXJNZXRhZGF0YSxcbiAgZ2V0U29ydGVkTWF0Y2hlcnNGb3JFdmVudCxcbiAgZ3JvdXBIb29rc0J5RXZlbnRBbmRNYXRjaGVyLFxufSBmcm9tICcuLi8uLi91dGlscy9ob29rcy9ob29rc0NvbmZpZ01hbmFnZXIuanMnXG5pbXBvcnQgdHlwZSB7IEluZGl2aWR1YWxIb29rQ29uZmlnIH0gZnJvbSAnLi4vLi4vdXRpbHMvaG9va3MvaG9va3NTZXR0aW5ncy5qcydcbmltcG9ydCB7XG4gIGdldFNldHRpbmdzX0RFUFJFQ0FURUQsXG4gIGdldFNldHRpbmdzRm9yU291cmNlLFxufSBmcm9tICcuLi8uLi91dGlscy9zZXR0aW5ncy9zZXR0aW5ncy5qcydcbmltcG9ydCB7IHBsdXJhbCB9IGZyb20gJy4uLy4uL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5pbXBvcnQgeyBTZWxlY3RFdmVudE1vZGUgfSBmcm9tICcuL1NlbGVjdEV2ZW50TW9kZS5qcydcbmltcG9ydCB7IFNlbGVjdEhvb2tNb2RlIH0gZnJvbSAnLi9TZWxlY3RIb29rTW9kZS5qcydcbmltcG9ydCB7IFNlbGVjdE1hdGNoZXJNb2RlIH0gZnJvbSAnLi9TZWxlY3RNYXRjaGVyTW9kZS5qcydcbmltcG9ydCB7IFZpZXdIb29rTW9kZSB9IGZyb20gJy4vVmlld0hvb2tNb2RlLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0b29sTmFtZXM6IHN0cmluZ1tdXG4gIG9uRXhpdDogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkXG59XG5cbnR5cGUgTW9kZVN0YXRlID1cbiAgfCB7IG1vZGU6ICdzZWxlY3QtZXZlbnQnIH1cbiAgfCB7IG1vZGU6ICdzZWxlY3QtbWF0Y2hlcic7IGV2ZW50OiBIb29rRXZlbnQgfVxuICB8IHsgbW9kZTogJ3NlbGVjdC1ob29rJzsgZXZlbnQ6IEhvb2tFdmVudDsgbWF0Y2hlcjogc3RyaW5nIH1cbiAgfCB7IG1vZGU6ICd2aWV3LWhvb2snOyBldmVudDogSG9va0V2ZW50OyBob29rOiBJbmRpdmlkdWFsSG9va0NvbmZpZyB9XG5cbmV4cG9ydCBmdW5jdGlvbiBIb29rc0NvbmZpZ01lbnUoeyB0b29sTmFtZXMsIG9uRXhpdCB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFttb2RlU3RhdGUsIHNldE1vZGVTdGF0ZV0gPSB1c2VTdGF0ZTxNb2RlU3RhdGU+KHtcbiAgICBtb2RlOiAnc2VsZWN0LWV2ZW50JyxcbiAgfSlcbiAgLy8gQ2FjaGUgd2hldGhlciBob29rcyBhcmUgZGlzYWJsZWQgYnkgcG9saWN5IHNldHRpbmdzLlxuICAvLyBnZXRTZXR0aW5nc0ZvclNvdXJjZSgpIGlzIGV4cGVuc2l2ZSAoZmlsZSByZWFkICsgSlNPTiBwYXJzZSArIHZhbGlkYXRpb24pLFxuICAvLyBzbyB3ZSBjb21wdXRlIGl0IG9uY2Ugb24gbW91bnQgYW5kIG9ubHkgcmUtY29tcHV0ZSB3aGVuIHBvbGljeSBzZXR0aW5ncyBjaGFuZ2UuXG4gIC8vIFNob3J0LWNpcmN1aXQgZXZhbHVhdGlvbiBlbnN1cmVzIHdlIHNraXAgdGhlIGV4cGVuc2l2ZSBjaGVjayB3aGVuIGhvb2tzIGFyZW4ndCBkaXNhYmxlZC5cbiAgY29uc3QgW2Rpc2FibGVkQnlQb2xpY3ksIHNldERpc2FibGVkQnlQb2xpY3ldID0gdXNlU3RhdGUoKCkgPT4ge1xuICAgIGNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3NfREVQUkVDQVRFRCgpXG4gICAgY29uc3QgaG9va3NEaXNhYmxlZCA9IHNldHRpbmdzPy5kaXNhYmxlQWxsSG9va3MgPT09IHRydWVcbiAgICByZXR1cm4gKFxuICAgICAgaG9va3NEaXNhYmxlZCAmJlxuICAgICAgZ2V0U2V0dGluZ3NGb3JTb3VyY2UoJ3BvbGljeVNldHRpbmdzJyk/LmRpc2FibGVBbGxIb29rcyA9PT0gdHJ1ZVxuICAgIClcbiAgfSlcblxuICAvLyBDaGVjayBpZiBob29rcyBhcmUgcmVzdHJpY3RlZCB0byBtYW5hZ2VkLW9ubHkgYnkgcG9saWN5XG4gIGNvbnN0IFtyZXN0cmljdGVkQnlQb2xpY3ksIHNldFJlc3RyaWN0ZWRCeVBvbGljeV0gPSB1c2VTdGF0ZSgoKSA9PiB7XG4gICAgcmV0dXJuIChcbiAgICAgIGdldFNldHRpbmdzRm9yU291cmNlKCdwb2xpY3lTZXR0aW5ncycpPy5hbGxvd01hbmFnZWRIb29rc09ubHkgPT09IHRydWVcbiAgICApXG4gIH0pXG5cbiAgLy8gVXBkYXRlIGNhY2hlZCB2YWx1ZXMgd2hlbiBwb2xpY3kgc2V0dGluZ3MgY2hhbmdlXG4gIHVzZVNldHRpbmdzQ2hhbmdlKHNvdXJjZSA9PiB7XG4gICAgaWYgKHNvdXJjZSA9PT0gJ3BvbGljeVNldHRpbmdzJykge1xuICAgICAgY29uc3Qgc2V0dGluZ3MgPSBnZXRTZXR0aW5nc19ERVBSRUNBVEVEKClcbiAgICAgIGNvbnN0IGhvb2tzRGlzYWJsZWQgPSBzZXR0aW5ncz8uZGlzYWJsZUFsbEhvb2tzID09PSB0cnVlXG4gICAgICBzZXREaXNhYmxlZEJ5UG9saWN5KFxuICAgICAgICBob29rc0Rpc2FibGVkICYmXG4gICAgICAgICAgZ2V0U2V0dGluZ3NGb3JTb3VyY2UoJ3BvbGljeVNldHRpbmdzJyk/LmRpc2FibGVBbGxIb29rcyA9PT0gdHJ1ZSxcbiAgICAgIClcbiAgICAgIHNldFJlc3RyaWN0ZWRCeVBvbGljeShcbiAgICAgICAgZ2V0U2V0dGluZ3NGb3JTb3VyY2UoJ3BvbGljeVNldHRpbmdzJyk/LmFsbG93TWFuYWdlZEhvb2tzT25seSA9PT0gdHJ1ZSxcbiAgICAgIClcbiAgICB9XG4gIH0pXG5cbiAgLy8gRXh0cmFjdCBjb21tb25seSB1c2VkIHZhbHVlcyBmcm9tIG1vZGVTdGF0ZSBmb3IgY29udmVuaWVuY2VcbiAgY29uc3QgbW9kZSA9IG1vZGVTdGF0ZS5tb2RlXG4gIGNvbnN0IHNlbGVjdGVkRXZlbnQgPSAnZXZlbnQnIGluIG1vZGVTdGF0ZSA/IG1vZGVTdGF0ZS5ldmVudCA6ICdQcmVUb29sVXNlJ1xuICBjb25zdCBzZWxlY3RlZE1hdGNoZXIgPSAnbWF0Y2hlcicgaW4gbW9kZVN0YXRlID8gbW9kZVN0YXRlLm1hdGNoZXIgOiBudWxsXG5cbiAgY29uc3QgbWNwID0gdXNlQXBwU3RhdGUocyA9PiBzLm1jcClcbiAgY29uc3QgYXBwU3RhdGVTdG9yZSA9IHVzZUFwcFN0YXRlU3RvcmUoKVxuICBjb25zdCBjb21iaW5lZFRvb2xOYW1lcyA9IHVzZU1lbW8oXG4gICAgKCkgPT4gWy4uLnRvb2xOYW1lcywgLi4ubWNwLnRvb2xzLm1hcCh0b29sID0+IHRvb2wubmFtZSldLFxuICAgIFt0b29sTmFtZXMsIG1jcC50b29sc10sXG4gIClcblxuICBjb25zdCBob29rc0J5RXZlbnRBbmRNYXRjaGVyID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgZ3JvdXBIb29rc0J5RXZlbnRBbmRNYXRjaGVyKGFwcFN0YXRlU3RvcmUuZ2V0U3RhdGUoKSwgY29tYmluZWRUb29sTmFtZXMpLFxuICAgIFtjb21iaW5lZFRvb2xOYW1lcywgYXBwU3RhdGVTdG9yZV0sXG4gIClcblxuICBjb25zdCBzb3J0ZWRNYXRjaGVyc0ZvclNlbGVjdGVkRXZlbnQgPSB1c2VNZW1vKFxuICAgICgpID0+IGdldFNvcnRlZE1hdGNoZXJzRm9yRXZlbnQoaG9va3NCeUV2ZW50QW5kTWF0Y2hlciwgc2VsZWN0ZWRFdmVudCksXG4gICAgW2hvb2tzQnlFdmVudEFuZE1hdGNoZXIsIHNlbGVjdGVkRXZlbnRdLFxuICApXG5cbiAgY29uc3QgaG9va3NGb3JTZWxlY3RlZE1hdGNoZXIgPSB1c2VNZW1vKFxuICAgICgpID0+XG4gICAgICBnZXRIb29rc0Zvck1hdGNoZXIoXG4gICAgICAgIGhvb2tzQnlFdmVudEFuZE1hdGNoZXIsXG4gICAgICAgIHNlbGVjdGVkRXZlbnQsXG4gICAgICAgIHNlbGVjdGVkTWF0Y2hlcixcbiAgICAgICksXG4gICAgW2hvb2tzQnlFdmVudEFuZE1hdGNoZXIsIHNlbGVjdGVkRXZlbnQsIHNlbGVjdGVkTWF0Y2hlcl0sXG4gIClcblxuICAvLyBIYW5kbGVyIGZvciBleGl0aW5nIHRoZSBkaWFsb2dcbiAgY29uc3QgaGFuZGxlRXhpdCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBvbkV4aXQoJ0hvb2tzIGRpYWxvZyBkaXNtaXNzZWQnLCB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0pXG4gIH0sIFtvbkV4aXRdKVxuXG4gIC8vIEVzY2FwZSBoYW5kbGluZyBmb3Igc2VsZWN0LWV2ZW50IG1vZGUgLSBleGl0IHRoZSBtZW51XG4gIHVzZUtleWJpbmRpbmcoJ2NvbmZpcm06bm8nLCBoYW5kbGVFeGl0LCB7XG4gICAgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsXG4gICAgaXNBY3RpdmU6IG1vZGUgPT09ICdzZWxlY3QtZXZlbnQnLFxuICB9KVxuXG4gIC8vIEVzY2FwZSBoYW5kbGluZyBmb3Igc2VsZWN0LW1hdGNoZXIgbW9kZSAtIGdvIHRvIHNlbGVjdC1ldmVudFxuICB1c2VLZXliaW5kaW5nKFxuICAgICdjb25maXJtOm5vJyxcbiAgICAoKSA9PiB7XG4gICAgICBzZXRNb2RlU3RhdGUoeyBtb2RlOiAnc2VsZWN0LWV2ZW50JyB9KVxuICAgIH0sXG4gICAge1xuICAgICAgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsXG4gICAgICBpc0FjdGl2ZTogbW9kZSA9PT0gJ3NlbGVjdC1tYXRjaGVyJyxcbiAgICB9LFxuICApXG5cbiAgLy8gRXNjYXBlIGhhbmRsaW5nIGZvciBzZWxlY3QtaG9vayBtb2RlIC0gZ28gdG8gc2VsZWN0LW1hdGNoZXIgb3Igc2VsZWN0LWV2ZW50XG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2NvbmZpcm06bm8nLFxuICAgICgpID0+IHtcbiAgICAgIGlmICgnZXZlbnQnIGluIG1vZGVTdGF0ZSkge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgZ2V0TWF0Y2hlck1ldGFkYXRhKG1vZGVTdGF0ZS5ldmVudCwgY29tYmluZWRUb29sTmFtZXMpICE9PSB1bmRlZmluZWRcbiAgICAgICAgKSB7XG4gICAgICAgICAgc2V0TW9kZVN0YXRlKHsgbW9kZTogJ3NlbGVjdC1tYXRjaGVyJywgZXZlbnQ6IG1vZGVTdGF0ZS5ldmVudCB9KVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHNldE1vZGVTdGF0ZSh7IG1vZGU6ICdzZWxlY3QtZXZlbnQnIH0pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuICAgIHtcbiAgICAgIGNvbnRleHQ6ICdDb25maXJtYXRpb24nLFxuICAgICAgaXNBY3RpdmU6IG1vZGUgPT09ICdzZWxlY3QtaG9vaycsXG4gICAgfSxcbiAgKVxuXG4gIC8vIEVzY2FwZSBoYW5kbGluZyBmb3Igdmlldy1ob29rIG1vZGUgLSBnbyB0byBzZWxlY3QtaG9va1xuICB1c2VLZXliaW5kaW5nKFxuICAgICdjb25maXJtOm5vJyxcbiAgICAoKSA9PiB7XG4gICAgICBpZiAobW9kZVN0YXRlLm1vZGUgPT09ICd2aWV3LWhvb2snKSB7XG4gICAgICAgIGNvbnN0IHsgZXZlbnQsIGhvb2sgfSA9IG1vZGVTdGF0ZVxuICAgICAgICBzZXRNb2RlU3RhdGUoe1xuICAgICAgICAgIG1vZGU6ICdzZWxlY3QtaG9vaycsXG4gICAgICAgICAgZXZlbnQsXG4gICAgICAgICAgbWF0Y2hlcjogaG9vay5tYXRjaGVyIHx8ICcnLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH0sXG4gICAge1xuICAgICAgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsXG4gICAgICBpc0FjdGl2ZTogbW9kZSA9PT0gJ3ZpZXctaG9vaycsXG4gICAgfSxcbiAgKVxuXG4gIGNvbnN0IGhvb2tFdmVudE1ldGFkYXRhID0gZ2V0SG9va0V2ZW50TWV0YWRhdGEoY29tYmluZWRUb29sTmFtZXMpXG5cbiAgLy8gQ2hlY2sgaWYgaG9va3MgYXJlIGRpc2FibGVkXG4gIGNvbnN0IHNldHRpbmdzID0gZ2V0U2V0dGluZ3NfREVQUkVDQVRFRCgpXG4gIGNvbnN0IGhvb2tzRGlzYWJsZWQgPSBzZXR0aW5ncz8uZGlzYWJsZUFsbEhvb2tzID09PSB0cnVlXG5cbiAgLy8gQ291bnQgaG9va3MgcGVyIGV2ZW50IGZvciB0aGUgZXZlbnQtc2VsZWN0aW9uIHZpZXcsIGFuZCB0aGUgdG90YWwuXG4gIGNvbnN0IHsgaG9va3NCeUV2ZW50LCB0b3RhbEhvb2tzQ291bnQgfSA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGNvbnN0IGJ5RXZlbnQ6IFBhcnRpYWw8UmVjb3JkPEhvb2tFdmVudCwgbnVtYmVyPj4gPSB7fVxuICAgIGxldCB0b3RhbCA9IDBcbiAgICBmb3IgKGNvbnN0IFtldmVudCwgbWF0Y2hlcnNdIG9mIE9iamVjdC5lbnRyaWVzKGhvb2tzQnlFdmVudEFuZE1hdGNoZXIpKSB7XG4gICAgICBjb25zdCBldmVudENvdW50ID0gT2JqZWN0LnZhbHVlcyhtYXRjaGVycykucmVkdWNlKFxuICAgICAgICAoc3VtLCBob29rcykgPT4gc3VtICsgaG9va3MubGVuZ3RoLFxuICAgICAgICAwLFxuICAgICAgKVxuICAgICAgYnlFdmVudFtldmVudCBhcyBIb29rRXZlbnRdID0gZXZlbnRDb3VudFxuICAgICAgdG90YWwgKz0gZXZlbnRDb3VudFxuICAgIH1cbiAgICByZXR1cm4geyBob29rc0J5RXZlbnQ6IGJ5RXZlbnQsIHRvdGFsSG9va3NDb3VudDogdG90YWwgfVxuICB9LCBbaG9va3NCeUV2ZW50QW5kTWF0Y2hlcl0pXG5cbiAgLy8gSWYgaG9va3MgYXJlIGRpc2FibGVkLCBzaG93IGFuIGluZm9ybWF0aW9uYWwgc2NyZWVuLlxuICAvLyBUaGUgbWVudSBpcyByZWFkLW9ubHksIHNvIHdlIGRvbid0IG9mZmVyIGEgcmUtZW5hYmxlIGJ1dHRvbiDigJRcbiAgLy8gdXNlcnMgY2FuIGVkaXQgc2V0dGluZ3MuanNvbiBvciBhc2sgQ2xhdWRlIGluc3RlYWQuXG4gIGlmIChob29rc0Rpc2FibGVkKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxEaWFsb2dcbiAgICAgICAgdGl0bGU9XCJIb29rIENvbmZpZ3VyYXRpb24gLSBEaXNhYmxlZFwiXG4gICAgICAgIG9uQ2FuY2VsPXtoYW5kbGVFeGl0fVxuICAgICAgICBpbnB1dEd1aWRlPXsoKSA9PiA8VGV4dD5Fc2MgdG8gY2xvc2U8L1RleHQ+fVxuICAgICAgPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgIEFsbCBob29rcyBhcmUgY3VycmVudGx5IDxUZXh0IGJvbGQ+ZGlzYWJsZWQ8L1RleHQ+XG4gICAgICAgICAgICAgIHtkaXNhYmxlZEJ5UG9saWN5ICYmICcgYnkgYSBtYW5hZ2VkIHNldHRpbmdzIGZpbGUnfS4gWW91IGhhdmV7JyAnfVxuICAgICAgICAgICAgICA8VGV4dCBib2xkPnt0b3RhbEhvb2tzQ291bnR9PC9UZXh0PiBjb25maWd1cmVkeycgJ31cbiAgICAgICAgICAgICAge3BsdXJhbCh0b3RhbEhvb2tzQ291bnQsICdob29rJyl9IHRoYXR7JyAnfVxuICAgICAgICAgICAgICB7cGx1cmFsKHRvdGFsSG9va3NDb3VudCwgJ2lzJywgJ2FyZScpfSBub3QgcnVubmluZy5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+V2hlbiBob29rcyBhcmUgZGlzYWJsZWQ6PC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj7CtyBObyBob29rIGNvbW1hbmRzIHdpbGwgZXhlY3V0ZTwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPsK3IFN0YXR1c0xpbmUgd2lsbCBub3QgYmUgZGlzcGxheWVkPC9UZXh0PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIMK3IFRvb2wgb3BlcmF0aW9ucyB3aWxsIHByb2NlZWQgd2l0aG91dCBob29rIHZhbGlkYXRpb25cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgICB7IWRpc2FibGVkQnlQb2xpY3kgJiYgKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIFRvIHJlLWVuYWJsZSBob29rcywgcmVtb3ZlICZxdW90O2Rpc2FibGVBbGxIb29rcyZxdW90OyBmcm9tXG4gICAgICAgICAgICAgIHNldHRpbmdzLmpzb24gb3IgYXNrIENsYXVkZS5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgIDwvRGlhbG9nPlxuICAgIClcbiAgfVxuXG4gIHN3aXRjaCAobW9kZVN0YXRlLm1vZGUpIHtcbiAgICBjYXNlICdzZWxlY3QtZXZlbnQnOlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPFNlbGVjdEV2ZW50TW9kZVxuICAgICAgICAgIGhvb2tFdmVudE1ldGFkYXRhPXtob29rRXZlbnRNZXRhZGF0YX1cbiAgICAgICAgICBob29rc0J5RXZlbnQ9e2hvb2tzQnlFdmVudH1cbiAgICAgICAgICB0b3RhbEhvb2tzQ291bnQ9e3RvdGFsSG9va3NDb3VudH1cbiAgICAgICAgICByZXN0cmljdGVkQnlQb2xpY3k9e3Jlc3RyaWN0ZWRCeVBvbGljeX1cbiAgICAgICAgICBvblNlbGVjdEV2ZW50PXtldmVudCA9PiB7XG4gICAgICAgICAgICBpZiAoZ2V0TWF0Y2hlck1ldGFkYXRhKGV2ZW50LCBjb21iaW5lZFRvb2xOYW1lcykgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICBzZXRNb2RlU3RhdGUoeyBtb2RlOiAnc2VsZWN0LW1hdGNoZXInLCBldmVudCB9KVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgc2V0TW9kZVN0YXRlKHsgbW9kZTogJ3NlbGVjdC1ob29rJywgZXZlbnQsIG1hdGNoZXI6ICcnIH0pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfX1cbiAgICAgICAgICBvbkNhbmNlbD17aGFuZGxlRXhpdH1cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICBjYXNlICdzZWxlY3QtbWF0Y2hlcic6XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8U2VsZWN0TWF0Y2hlck1vZGVcbiAgICAgICAgICBzZWxlY3RlZEV2ZW50PXttb2RlU3RhdGUuZXZlbnR9XG4gICAgICAgICAgbWF0Y2hlcnNGb3JTZWxlY3RlZEV2ZW50PXtzb3J0ZWRNYXRjaGVyc0ZvclNlbGVjdGVkRXZlbnR9XG4gICAgICAgICAgaG9va3NCeUV2ZW50QW5kTWF0Y2hlcj17aG9va3NCeUV2ZW50QW5kTWF0Y2hlcn1cbiAgICAgICAgICBldmVudERlc2NyaXB0aW9uPXtob29rRXZlbnRNZXRhZGF0YVttb2RlU3RhdGUuZXZlbnRdLmRlc2NyaXB0aW9ufVxuICAgICAgICAgIG9uU2VsZWN0PXttYXRjaGVyID0+IHtcbiAgICAgICAgICAgIHNldE1vZGVTdGF0ZSh7XG4gICAgICAgICAgICAgIG1vZGU6ICdzZWxlY3QtaG9vaycsXG4gICAgICAgICAgICAgIGV2ZW50OiBtb2RlU3RhdGUuZXZlbnQsXG4gICAgICAgICAgICAgIG1hdGNoZXIsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH19XG4gICAgICAgICAgb25DYW5jZWw9eygpID0+IHtcbiAgICAgICAgICAgIHNldE1vZGVTdGF0ZSh7IG1vZGU6ICdzZWxlY3QtZXZlbnQnIH0pXG4gICAgICAgICAgfX1cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICBjYXNlICdzZWxlY3QtaG9vayc6XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8U2VsZWN0SG9va01vZGVcbiAgICAgICAgICBzZWxlY3RlZEV2ZW50PXttb2RlU3RhdGUuZXZlbnR9XG4gICAgICAgICAgc2VsZWN0ZWRNYXRjaGVyPXttb2RlU3RhdGUubWF0Y2hlcn1cbiAgICAgICAgICBob29rc0ZvclNlbGVjdGVkTWF0Y2hlcj17aG9va3NGb3JTZWxlY3RlZE1hdGNoZXJ9XG4gICAgICAgICAgaG9va0V2ZW50TWV0YWRhdGE9e2hvb2tFdmVudE1ldGFkYXRhW21vZGVTdGF0ZS5ldmVudF19XG4gICAgICAgICAgb25TZWxlY3Q9e2hvb2sgPT4ge1xuICAgICAgICAgICAgc2V0TW9kZVN0YXRlKHtcbiAgICAgICAgICAgICAgbW9kZTogJ3ZpZXctaG9vaycsXG4gICAgICAgICAgICAgIGV2ZW50OiBtb2RlU3RhdGUuZXZlbnQsXG4gICAgICAgICAgICAgIGhvb2ssXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH19XG4gICAgICAgICAgb25DYW5jZWw9eygpID0+IHtcbiAgICAgICAgICAgIC8vIEdvIGJhY2sgdG8gbWF0Y2hlciBzZWxlY3Rpb24gb3IgZXZlbnQgc2VsZWN0aW9uXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIGdldE1hdGNoZXJNZXRhZGF0YShtb2RlU3RhdGUuZXZlbnQsIGNvbWJpbmVkVG9vbE5hbWVzKSAhPT1cbiAgICAgICAgICAgICAgdW5kZWZpbmVkXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgc2V0TW9kZVN0YXRlKHtcbiAgICAgICAgICAgICAgICBtb2RlOiAnc2VsZWN0LW1hdGNoZXInLFxuICAgICAgICAgICAgICAgIGV2ZW50OiBtb2RlU3RhdGUuZXZlbnQsXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBzZXRNb2RlU3RhdGUoeyBtb2RlOiAnc2VsZWN0LWV2ZW50JyB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH19XG4gICAgICAgIC8+XG4gICAgICApXG4gICAgY2FzZSAndmlldy1ob29rJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxWaWV3SG9va01vZGVcbiAgICAgICAgICBzZWxlY3RlZEhvb2s9e21vZGVTdGF0ZS5ob29rfVxuICAgICAgICAgIGV2ZW50U3VwcG9ydHNNYXRjaGVyPXtcbiAgICAgICAgICAgIGdldE1hdGNoZXJNZXRhZGF0YShtb2RlU3RhdGUuZXZlbnQsIGNvbWJpbmVkVG9vbE5hbWVzKSAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgfVxuICAgICAgICAgIG9uQ2FuY2VsPXsoKSA9PiB7XG4gICAgICAgICAgICBjb25zdCB7IGV2ZW50LCBob29rIH0gPSBtb2RlU3RhdGVcbiAgICAgICAgICAgIHNldE1vZGVTdGF0ZSh7XG4gICAgICAgICAgICAgIG1vZGU6ICdzZWxlY3QtaG9vaycsXG4gICAgICAgICAgICAgIGV2ZW50LFxuICAgICAgICAgICAgICBtYXRjaGVyOiBob29rLm1hdGNoZXIgfHwgJycsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH19XG4gICAgICAgIC8+XG4gICAgICApXG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsV0FBVyxFQUFFQyxPQUFPLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3RELGNBQWNDLFNBQVMsUUFBUSxrQ0FBa0M7QUFDakUsU0FBU0MsV0FBVyxFQUFFQyxnQkFBZ0IsUUFBUSx1QkFBdUI7QUFDckUsY0FBY0Msb0JBQW9CLFFBQVEsbUJBQW1CO0FBQzdELFNBQVNDLGlCQUFpQixRQUFRLGtDQUFrQztBQUNwRSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQVNDLGFBQWEsUUFBUSxvQ0FBb0M7QUFDbEUsU0FDRUMsb0JBQW9CLEVBQ3BCQyxrQkFBa0IsRUFDbEJDLGtCQUFrQixFQUNsQkMseUJBQXlCLEVBQ3pCQywyQkFBMkIsUUFDdEIseUNBQXlDO0FBQ2hELGNBQWNDLG9CQUFvQixRQUFRLG9DQUFvQztBQUM5RSxTQUNFQyxzQkFB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 \ No newline at end of file diff --git a/components/hooks/PromptDialog.tsx b/components/hooks/PromptDialog.tsx new file mode 100644 index 0000000..1521786 --- /dev/null +++ b/components/hooks/PromptDialog.tsx @@ -0,0 +1,90 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Box, Text } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import type { PromptRequest } from '../../types/hooks.js'; +import { Select } from '../CustomSelect/select.js'; +import { PermissionDialog } from '../permissions/PermissionDialog.js'; +type Props = { + title: string; + toolInputSummary?: string | null; + request: PromptRequest; + onRespond: (key: string) => void; + onAbort: () => void; +}; +export function PromptDialog(t0) { + const $ = _c(15); + const { + title, + toolInputSummary, + request, + onRespond, + onAbort + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + isActive: true + }; + $[0] = t1; + } else { + t1 = $[0]; + } + useKeybinding("app:interrupt", onAbort, t1); + let t2; + if ($[1] !== request.options) { + t2 = request.options.map(_temp); + $[1] = request.options; + $[2] = t2; + } else { + t2 = $[2]; + } + const options = t2; + let t3; + if ($[3] !== toolInputSummary) { + t3 = toolInputSummary ? {toolInputSummary} : undefined; + $[3] = toolInputSummary; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== onRespond) { + t4 = value => { + onRespond(value); + }; + $[5] = onRespond; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] !== options || $[8] !== t4) { + t5 = ; + $[12] = onCancel; + $[13] = t4; + $[14] = t6; + $[15] = t7; + } else { + t7 = $[15]; + } + let t8; + if ($[16] !== t2 || $[17] !== t7) { + t8 = {t2}{t3}{t7}; + $[16] = t2; + $[17] = t7; + $[18] = t8; + } else { + t8 = $[18]; + } + let t9; + if ($[19] !== onCancel || $[20] !== subtitle || $[21] !== t8) { + t9 = {t8}; + $[19] = onCancel; + $[20] = subtitle; + $[21] = t8; + $[22] = t9; + } else { + t9 = $[22]; + } + return t9; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJIb29rRXZlbnQiLCJIb29rRXZlbnRNZXRhZGF0YSIsIkJveCIsIkxpbmsiLCJUZXh0IiwicGx1cmFsIiwiU2VsZWN0IiwiRGlhbG9nIiwiUHJvcHMiLCJob29rRXZlbnRNZXRhZGF0YSIsIlJlY29yZCIsImhvb2tzQnlFdmVudCIsIlBhcnRpYWwiLCJ0b3RhbEhvb2tzQ291bnQiLCJyZXN0cmljdGVkQnlQb2xpY3kiLCJvblNlbGVjdEV2ZW50IiwiZXZlbnQiLCJvbkNhbmNlbCIsIlNlbGVjdEV2ZW50TW9kZSIsInQwIiwiJCIsIl9jIiwidDEiLCJzdWJ0aXRsZSIsInQyIiwiaW5mbyIsInQzIiwiU3ltYm9sIiwiZm9yIiwidDQiLCJ2YWx1ZSIsInQ1IiwiT2JqZWN0IiwiZW50cmllcyIsInQ2IiwibWFwIiwidDciLCJuYW1lIiwibWV0YWRhdGEiLCJjb3VudCIsImxhYmVsIiwiZGVzY3JpcHRpb24iLCJzdW1tYXJ5IiwidDgiLCJ0OSJdLCJzb3VyY2VzIjpbIlNlbGVjdEV2ZW50TW9kZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBTZWxlY3RFdmVudE1vZGUgaXMgdGhlIGVudHJ5cG9pbnQgb2YgdGhlIEhvb2tzIGNvbmZpZyBtZW51LCB3aGVyZSB0aGUgdXNlclxuICogc2VlcyB0aGUgbGlzdCBvZiBhdmFpbGFibGUgaG9vayBldmVudHMuXG4gKlxuICogVGhlIC9ob29rcyBtZW51IGlzIHJlYWQtb25seTogc2VsZWN0aW5nIGFuIGV2ZW50IGxldHMgeW91IGJyb3dzZSBpdHNcbiAqIGNvbmZpZ3VyZWQgaG9va3MgYnV0IG5vdCBtb2RpZnkgdGhlbS4gVG8gYWRkIG9yIGNoYW5nZSBob29rcywgdXNlcnMgc2hvdWxkXG4gKiBlZGl0IHNldHRpbmdzLmpzb24gZGlyZWN0bHkgb3IgYXNrIENsYXVkZS5cbiAqL1xuXG5pbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IEhvb2tFdmVudCB9IGZyb20gJ3NyYy9lbnRyeXBvaW50cy9hZ2VudFNka1R5cGVzLmpzJ1xuaW1wb3J0IHR5cGUgeyBIb29rRXZlbnRNZXRhZGF0YSB9IGZyb20gJ3NyYy91dGlscy9ob29rcy9ob29rc0NvbmZpZ01hbmFnZXIuanMnXG5pbXBvcnQgeyBCb3gsIExpbmssIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBwbHVyYWwgfSBmcm9tICcuLi8uLi91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4uL0N1c3RvbVNlbGVjdC9zZWxlY3QuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgaG9va0V2ZW50TWV0YWRhdGE6IFJlY29yZDxIb29rRXZlbnQsIEhvb2tFdmVudE1ldGFkYXRhPlxuICBob29rc0J5RXZlbnQ6IFBhcnRpYWw8UmVjb3JkPEhvb2tFdmVudCwgbnVtYmVyPj5cbiAgdG90YWxIb29rc0NvdW50OiBudW1iZXJcbiAgcmVzdHJpY3RlZEJ5UG9saWN5OiBib29sZWFuXG4gIG9uU2VsZWN0RXZlbnQ6IChldmVudDogSG9va0V2ZW50KSA9PiB2b2lkXG4gIG9uQ2FuY2VsOiAoKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTZWxlY3RFdmVudE1vZGUoe1xuICBob29rRXZlbnRNZXRhZGF0YSxcbiAgaG9va3NCeUV2ZW50LFxuICB0b3RhbEhvb2tzQ291bnQsXG4gIHJlc3RyaWN0ZWRCeVBvbGljeSxcbiAgb25TZWxlY3RFdmVudCxcbiAgb25DYW5jZWwsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHN1YnRpdGxlID0gYCR7dG90YWxIb29rc0NvdW50fSAke3BsdXJhbCh0b3RhbEhvb2tzQ291bnQsICdob29rJyl9IGNvbmZpZ3VyZWRgXG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nIHRpdGxlPVwiSG9va3NcIiBzdWJ0aXRsZT17c3VidGl0bGV9IG9uQ2FuY2VsPXtvbkNhbmNlbH0+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICB7cmVzdHJpY3RlZEJ5UG9saWN5ICYmIChcbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPlxuICAgICAgICAgICAgICB7ZmlndXJlcy5pbmZvfSBIb29rcyBSZXN0cmljdGVkIGJ5IFBvbGljeVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIE9ubHkgaG9va3MgZnJvbSBtYW5hZ2VkIHNldHRpbmdzIGNhbiBydW4uIFVzZXItZGVmaW5lZCBob29rcyBmcm9tXG4gICAgICAgICAgICAgIH4vLmNsYXVkZS9zZXR0aW5ncy5qc29uLCAuY2xhdWRlL3NldHRpbmdzLmpzb24sIGFuZFxuICAgICAgICAgICAgICAuY2xhdWRlL3NldHRpbmdzLmxvY2FsLmpzb24gYXJlIGJsb2NrZWQuXG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7ZmlndXJlcy5pbmZvfSBUaGlzIG1lbnUgaXMgcmVhZC1vbmx5LiBUbyBhZGQgb3IgbW9kaWZ5IGhvb2tzLCBlZGl0XG4gICAgICAgICAgICBzZXR0aW5ncy5qc29uIGRpcmVjdGx5IG9yIGFzayBDbGF1ZGUueycgJ31cbiAgICAgICAgICAgIDxMaW5rIHVybD1cImh0dHBzOi8vY29kZS5jbGF1ZGUuY29tL2RvY3MvZW4vaG9va3NcIj5MZWFybiBtb3JlPC9MaW5rPlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgb25DaGFuZ2U9e3ZhbHVlID0+IHtcbiAgICAgICAgICAgICAgb25TZWxlY3RFdmVudCh2YWx1ZSBhcyBIb29rRXZlbnQpXG4gICAgICAgICAgICB9fVxuICAgICAgICAgICAgb25DYW5jZWw9e29uQ2FuY2VsfVxuICAgICAgICAgICAgb3B0aW9ucz17T2JqZWN0LmVudHJpZXMoaG9va0V2ZW50TWV0YWRhdGEpLm1hcChcbiAgICAgICAgICAgICAgKFtuYW1lLCBtZXRhZGF0YV0pID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCBjb3VudCA9IGhvb2tzQnlFdmVudFtuYW1lIGFzIEhvb2tFdmVudF0gfHwgMFxuICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICBsYWJlbDpcbiAgICAgICAgICAgICAgICAgICAgY291bnQgPiAwID8gKFxuICAgICAgICAgICAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAgICAgICAgICAge25hbWV9IDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPih7Y291bnR9KTwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgICAgICAgbmFtZVxuICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgdmFsdWU6IG5hbWUsXG4gICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogbWV0YWRhdGEuc3VtbWFyeSxcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICApfVxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgPC9EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5n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 \ No newline at end of file diff --git a/components/hooks/SelectHookMode.tsx b/components/hooks/SelectHookMode.tsx new file mode 100644 index 0000000..cec6c6a --- /dev/null +++ b/components/hooks/SelectHookMode.tsx @@ -0,0 +1,112 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * SelectHookMode shows all hooks configured for a given event+matcher pair. + * + * The /hooks menu is read-only: this view no longer offers "add new hook" + * and selecting a hook shows its read-only details instead of a delete + * confirmation. + */ +import * as React from 'react'; +import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'; +import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js'; +import { Box, Text } from '../../ink.js'; +import { getHookDisplayText, hookSourceHeaderDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js'; +import { Select } from '../CustomSelect/select.js'; +import { Dialog } from '../design-system/Dialog.js'; +type Props = { + selectedEvent: HookEvent; + selectedMatcher: string | null; + hooksForSelectedMatcher: IndividualHookConfig[]; + hookEventMetadata: HookEventMetadata; + onSelect: (hook: IndividualHookConfig) => void; + onCancel: () => void; +}; +export function SelectHookMode(t0) { + const $ = _c(19); + const { + selectedEvent, + selectedMatcher, + hooksForSelectedMatcher, + hookEventMetadata, + onSelect, + onCancel + } = t0; + const title = hookEventMetadata.matcherMetadata !== undefined ? `${selectedEvent} - Matcher: ${selectedMatcher || "(all)"}` : selectedEvent; + if (hooksForSelectedMatcher.length === 0) { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = No hooks configured for this event.To add hooks, edit settings.json directly or ask Claude.; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== hookEventMetadata.description || $[2] !== onCancel || $[3] !== title) { + t2 = {t1}; + $[1] = hookEventMetadata.description; + $[2] = onCancel; + $[3] = title; + $[4] = t2; + } else { + t2 = $[4]; + } + return t2; + } + const t1 = hookEventMetadata.description; + let t2; + if ($[5] !== hooksForSelectedMatcher) { + t2 = hooksForSelectedMatcher.map(_temp2); + $[5] = hooksForSelectedMatcher; + $[6] = t2; + } else { + t2 = $[6]; + } + let t3; + if ($[7] !== hooksForSelectedMatcher || $[8] !== onSelect) { + t3 = value => { + const index_0 = parseInt(value, 10); + const hook_0 = hooksForSelectedMatcher[index_0]; + if (hook_0) { + onSelect(hook_0); + } + }; + $[7] = hooksForSelectedMatcher; + $[8] = onSelect; + $[9] = t3; + } else { + t3 = $[9]; + } + let t4; + if ($[10] !== onCancel || $[11] !== t2 || $[12] !== t3) { + t4 = ; + $[16] = onCancel; + $[17] = t3; + $[18] = t4; + $[19] = t5; + } else { + t5 = $[19]; + } + let t6; + if ($[20] !== eventDescription || $[21] !== onCancel || $[22] !== t2 || $[23] !== t5) { + t6 = {t5}; + $[20] = eventDescription; + $[21] = onCancel; + $[22] = t2; + $[23] = t5; + $[24] = t6; + } else { + t6 = $[24]; + } + return t6; +} +function _temp3(item) { + const sourceText = item.sources.map(hookSourceInlineDisplayString).join(", "); + const matcherLabel = item.matcher || "(all)"; + return { + label: `[${sourceText}] ${matcherLabel}`, + value: item.matcher, + description: `${item.hookCount} ${plural(item.hookCount, "hook")}` + }; +} +function _temp2() { + return Esc to go back; +} +function _temp(h) { + return h.source; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkhvb2tFdmVudCIsIkJveCIsIlRleHQiLCJIb29rU291cmNlIiwiaG9va1NvdXJjZUlubGluZURpc3BsYXlTdHJpbmciLCJJbmRpdmlkdWFsSG9va0NvbmZpZyIsInBsdXJhbCIsIlNlbGVjdCIsIkRpYWxvZyIsIk1hdGNoZXJXaXRoU291cmNlIiwibWF0Y2hlciIsInNvdXJjZXMiLCJob29rQ291bnQiLCJQcm9wcyIsInNlbGVjdGVkRXZlbnQiLCJtYXRjaGVyc0ZvclNlbGVjdGVkRXZlbnQiLCJob29rc0J5RXZlbnRBbmRNYXRjaGVyIiwiUmVjb3JkIiwiZXZlbnREZXNjcmlwdGlvbiIsIm9uU2VsZWN0Iiwib25DYW5jZWwiLCJTZWxlY3RNYXRjaGVyTW9kZSIsInQwIiwiJCIsIl9jIiwidDEiLCJ0MiIsImhvb2tzIiwiQXJyYXkiLCJmcm9tIiwiU2V0IiwibWFwIiwiX3RlbXAiLCJsZW5ndGgiLCJtYXRjaGVyc1dpdGhTb3VyY2VzIiwidDMiLCJTeW1ib2wiLCJmb3IiLCJ0NCIsIl90ZW1wMiIsIl90ZW1wMyIsInZhbHVlIiwidDUiLCJ0NiIsIml0ZW0iLCJzb3VyY2VUZXh0Iiwiam9pbiIsIm1hdGNoZXJMYWJlbCIsImxhYmVsIiwiZGVzY3JpcHRpb24iLCJoIiwic291cmNlIl0sInNvdXJjZXMiOlsiU2VsZWN0TWF0Y2hlck1vZGUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2VsZWN0TWF0Y2hlck1vZGUgc2hvd3MgdGhlIGNvbmZpZ3VyZWQgbWF0Y2hlcnMgZm9yIGEgc2VsZWN0ZWQgaG9vayBldmVudC5cbiAqXG4gKiBUaGUgL2hvb2tzIG1lbnUgaXMgcmVhZC1vbmx5OiB0aGlzIHZpZXcgbm8gbG9uZ2VyIG9mZmVycyBcImFkZCBuZXcgbWF0Y2hlclwiXG4gKiBhbmQgc2ltcGx5IGxldHMgdGhlIHVzZXIgZHJpbGwgaW50byBlYWNoIG1hdGNoZXIgdG8gc2VlIGl0cyBob29rcy5cbiAqL1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IEhvb2tFdmVudCB9IGZyb20gJ3NyYy9lbnRyeXBvaW50cy9hZ2VudFNka1R5cGVzLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBIb29rU291cmNlLFxuICBob29rU291cmNlSW5saW5lRGlzcGxheVN0cmluZyxcbiAgdHlwZSBJbmRpdmlkdWFsSG9va0NvbmZpZyxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvaG9va3MvaG9va3NTZXR0aW5ncy5qcydcbmltcG9ydCB7IHBsdXJhbCB9IGZyb20gJy4uLy4uL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuXG50eXBlIE1hdGNoZXJXaXRoU291cmNlID0ge1xuICBtYXRjaGVyOiBzdHJpbmdcbiAgc291cmNlczogSG9va1NvdXJjZVtdXG4gIGhvb2tDb3VudDogbnVtYmVyXG59XG5cbnR5cGUgUHJvcHMgPSB7XG4gIHNlbGVjdGVkRXZlbnQ6IEhvb2tFdmVudFxuICBtYXRjaGVyc0ZvclNlbGVjdGVkRXZlbnQ6IHN0cmluZ1tdXG4gIGhvb2tzQnlFdmVudEFuZE1hdGNoZXI6IFJlY29yZDxcbiAgICBIb29rRXZlbnQsXG4gICAgUmVjb3JkPHN0cmluZywgSW5kaXZpZHVhbEhvb2tDb25maWdbXT5cbiAgPlxuICBldmVudERlc2NyaXB0aW9uOiBzdHJpbmdcbiAgb25TZWxlY3Q6IChtYXRjaGVyOiBzdHJpbmcpID0+IHZvaWRcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFNlbGVjdE1hdGNoZXJNb2RlKHtcbiAgc2VsZWN0ZWRFdmVudCxcbiAgbWF0Y2hlcnNGb3JTZWxlY3RlZEV2ZW50LFxuICBob29rc0J5RXZlbnRBbmRNYXRjaGVyLFxuICBldmVudERlc2NyaXB0aW9uLFxuICBvblNlbGVjdCxcbiAgb25DYW5jZWwsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIEdyb3VwIG1hdGNoZXJzIHdpdGggdGhlaXIgc291cmNlcyAoYWxyZWFkeSBzb3J0ZWQgYnkgcHJpb3JpdHkgaW4gcGFyZW50KVxuICBjb25zdCBtYXRjaGVyc1dpdGhTb3VyY2VzOiBNYXRjaGVyV2l0aFNvdXJjZVtdID0gUmVhY3QudXNlTWVtbygoKSA9PiB7XG4gICAgcmV0dXJuIG1hdGNoZXJzRm9yU2VsZWN0ZWRFdmVudC5tYXAobWF0Y2hlciA9PiB7XG4gICAgICBjb25zdCBob29rcyA9IGhvb2tzQnlFdmVudEFuZE1hdGNoZXJbc2VsZWN0ZWRFdmVudF0/LlttYXRjaGVyXSB8fCBbXVxuICAgICAgY29uc3Qgc291cmNlcyA9IEFycmF5LmZyb20obmV3IFNldChob29rcy5tYXAoaCA9PiBoLnNvdXJjZSkpKVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbWF0Y2hlcixcbiAgICAgICAgc291cmNlcyxcbiAgICAgICAgaG9va0NvdW50OiBob29rcy5sZW5ndGgsXG4gICAgICB9XG4gICAgfSlcbiAgfSwgW21hdGNoZXJzRm9yU2VsZWN0ZWRFdmVudCwgaG9va3NCeUV2ZW50QW5kTWF0Y2hlciwgc2VsZWN0ZWRFdmVudF0pXG5cbiAgaWYgKG1hdGNoZXJzRm9yU2VsZWN0ZWRFdmVudC5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gKFxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT17YCR7c2VsZWN0ZWRFdmVudH0gLSBNYXRjaGVyc2B9XG4gICAgICAgIHN1YnRpdGxlPXtldmVudERlc2NyaXB0aW9ufVxuICAgICAgICBvbkNhbmNlbD17b25DYW5jZWx9XG4gICAgICAgIGlucHV0R3VpZGU9eygpID0+IDxUZXh0PkVzYyB0byBnbyBiYWNrPC9UZXh0Pn1cbiAgICAgID5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5ObyBob29rcyBjb25maWd1cmVkIGZvciB0aGlzIGV2ZW50LjwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIFRvIGFkZCBob29rcywgZWRpdCBzZXR0aW5ncy5qc29uIGRpcmVjdGx5IG9yIGFzayBDbGF1ZGUuXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvRGlhbG9nPlxuICAgIClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9e2Ake3NlbGVjdGVkRXZlbnR9IC0gTWF0Y2hlcnNgfVxuICAgICAgc3VidGl0bGU9e2V2ZW50RGVzY3JpcHRpb259XG4gICAgICBvbkNhbmNlbD17b25DYW5jZWx9XG4gICAgPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIDxTZWxlY3RcbiAgICAgICAgICBvcHRpb25zPXttYXRjaGVyc1dpdGhTb3VyY2VzLm1hcChpdGVtID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHNvdXJjZVRleHQgPSBpdGVtLnNvdXJjZXNcbiAgICAgICAgICAgICAgLm1hcChob29rU291cmNlSW5saW5lRGlzcGxheVN0cmluZylcbiAgICAgICAgICAgICAgLmpvaW4oJywgJylcbiAgICAgICAgICAgIGNvbnN0IG1hdGNoZXJMYWJlbCA9IGl0ZW0ubWF0Y2hlciB8fCAnKGFsbCknXG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBsYWJlbDogYFske3NvdXJjZVRleHR9XSAke21hdGNoZXJMYWJlbH1gLFxuICAgICAgICAgICAgICB2YWx1ZTogaXRlbS5tYXRjaGVyLFxuICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogYCR7aXRlbS5ob29rQ291bnR9ICR7cGx1cmFsKGl0ZW0uaG9va0NvdW50LCAnaG9vaycpfWAsXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSl9XG4gICAgICAgICAgb25DaGFuZ2U9e3ZhbHVlID0+IHtcbiAgICAgICAgICAgIG9uU2VsZWN0KHZhbHVlKVxuICAgICAgICAgIH19XG4gICAgICAgICAgb25DYW5jZWw9e29uQ2FuY2VsfVxuICAgICAgICAvPlxuICAgICAgPC9Cb3g+XG4gICAgPC9EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsY0FBY0MsU0FBUyxRQUFRLGtDQUFrQztBQUNqRSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQ0UsS0FBS0MsVUFBVSxFQUNmQyw2QkFBNkIsRUFDN0IsS0FBS0Msb0JBQW9CLFFBQ3BCLG9DQUFvQztBQUMzQyxTQUFTQyxNQUFNLFFBQVEsNEJBQTRCO0FBQ25ELFNBQVNDLE1BQU0sUUFBUSwyQkFBMkI7QUFDbEQsU0FBU0MsTUFBTSxRQUFRLDRCQUE0QjtBQUVuRCxLQUFLQyxpQkFBaUIsR0FBRztFQUN2QkMsT0FBTyxFQUFFLE1BQU07RUFDZkMsT0FBTyxFQUFFUixVQUFVLEVBQUU7RUFDckJTLFNBQVMsRUFBRSxNQUFNO0FBQ25CLENBQUM7QUFFRCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsYUFBYSxFQUFFZCxTQUFTO0VBQ3hCZSx3QkFBd0IsRUFBRSxNQUFNLEVBQUU7RUFDbENDLHNCQUFzQixFQUFFQyxNQUFNLENBQzVCakIsU0FBUyxFQUNUaUIsTUFBTSxDQUFDLE1BQU0sRUFBRVosb0JBQW9CLEVBQUUsQ0FBQyxDQUN2QztFQUNEYSxnQkFBZ0IsRUFBRSxNQUFNO0VBQ3hCQyxRQUFRLEVBQUUsQ0FBQ1QsT0FBTyxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUk7RUFDbkNVLFFBQVEsRUFBRSxHQUFHLEdBQUcsSUFBSTtBQUN0QixDQUFDO0FBRUQsT0FBTyxTQUFBQyxrQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMs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 \ No newline at end of file diff --git a/components/hooks/ViewHookMode.tsx b/components/hooks/ViewHookMode.tsx new file mode 100644 index 0000000..b84d4b0 --- /dev/null +++ b/components/hooks/ViewHookMode.tsx @@ -0,0 +1,199 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * ViewHookMode shows read-only details for a single configured hook. + * + * The /hooks menu is read-only; this view replaces the former delete-hook + * confirmation screen and directs users to settings.json or Claude for edits. + */ +import * as React from 'react'; +import { Box, Text } from '../../ink.js'; +import { hookSourceDescriptionDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js'; +import { Dialog } from '../design-system/Dialog.js'; +type Props = { + selectedHook: IndividualHookConfig; + eventSupportsMatcher: boolean; + onCancel: () => void; +}; +export function ViewHookMode(t0) { + const $ = _c(40); + const { + selectedHook, + eventSupportsMatcher, + onCancel + } = t0; + let t1; + if ($[0] !== selectedHook.event) { + t1 = Event: {selectedHook.event}; + $[0] = selectedHook.event; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== eventSupportsMatcher || $[3] !== selectedHook.matcher) { + t2 = eventSupportsMatcher && Matcher: {selectedHook.matcher || "(all)"}; + $[2] = eventSupportsMatcher; + $[3] = selectedHook.matcher; + $[4] = t2; + } else { + t2 = $[4]; + } + let t3; + if ($[5] !== selectedHook.config.type) { + t3 = Type: {selectedHook.config.type}; + $[5] = selectedHook.config.type; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] !== selectedHook.source) { + t4 = hookSourceDescriptionDisplayString(selectedHook.source); + $[7] = selectedHook.source; + $[8] = t4; + } else { + t4 = $[8]; + } + let t5; + if ($[9] !== t4) { + t5 = Source:{" "}{t4}; + $[9] = t4; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] !== selectedHook.pluginName) { + t6 = selectedHook.pluginName && Plugin: {selectedHook.pluginName}; + $[11] = selectedHook.pluginName; + $[12] = t6; + } else { + t6 = $[12]; + } + let t7; + if ($[13] !== t1 || $[14] !== t2 || $[15] !== t3 || $[16] !== t5 || $[17] !== t6) { + t7 = {t1}{t2}{t3}{t5}{t6}; + $[13] = t1; + $[14] = t2; + $[15] = t3; + $[16] = t5; + $[17] = t6; + $[18] = t7; + } else { + t7 = $[18]; + } + let t8; + if ($[19] !== selectedHook.config) { + t8 = getContentFieldLabel(selectedHook.config); + $[19] = selectedHook.config; + $[20] = t8; + } else { + t8 = $[20]; + } + let t9; + if ($[21] !== t8) { + t9 = {t8}:; + $[21] = t8; + $[22] = t9; + } else { + t9 = $[22]; + } + let t10; + if ($[23] !== selectedHook.config) { + t10 = getContentFieldValue(selectedHook.config); + $[23] = selectedHook.config; + $[24] = t10; + } else { + t10 = $[24]; + } + let t11; + if ($[25] !== t10) { + t11 = {t10}; + $[25] = t10; + $[26] = t11; + } else { + t11 = $[26]; + } + let t12; + if ($[27] !== t11 || $[28] !== t9) { + t12 = {t9}{t11}; + $[27] = t11; + $[28] = t9; + $[29] = t12; + } else { + t12 = $[29]; + } + let t13; + if ($[30] !== selectedHook.config) { + t13 = "statusMessage" in selectedHook.config && selectedHook.config.statusMessage && Status message:{" "}{selectedHook.config.statusMessage}; + $[30] = selectedHook.config; + $[31] = t13; + } else { + t13 = $[31]; + } + let t14; + if ($[32] === Symbol.for("react.memo_cache_sentinel")) { + t14 = To modify or remove this hook, edit settings.json directly or ask Claude to help.; + $[32] = t14; + } else { + t14 = $[32]; + } + let t15; + if ($[33] !== t12 || $[34] !== t13 || $[35] !== t7) { + t15 = {t7}{t12}{t13}{t14}; + $[33] = t12; + $[34] = t13; + $[35] = t7; + $[36] = t15; + } else { + t15 = $[36]; + } + let t16; + if ($[37] !== onCancel || $[38] !== t15) { + t16 = {t15}; + $[37] = onCancel; + $[38] = t15; + $[39] = t16; + } else { + t16 = $[39]; + } + return t16; +} + +/** + * Get a human-readable label for the primary content field of a hook + * based on its type. + */ +function _temp() { + return Esc to go back; +} +function getContentFieldLabel(config: IndividualHookConfig['config']): string { + switch (config.type) { + case 'command': + return 'Command'; + case 'prompt': + return 'Prompt'; + case 'agent': + return 'Prompt'; + case 'http': + return 'URL'; + } +} + +/** + * Get the actual content value for a hook's primary field, bypassing + * statusMessage so the detail view always shows the real command/prompt/URL. + */ +function getContentFieldValue(config: IndividualHookConfig['config']): string { + switch (config.type) { + case 'command': + return config.command; + case 'prompt': + return config.prompt; + case 'agent': + return config.prompt; + case 'http': + return config.url; + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJob29rU291cmNlRGVzY3JpcHRpb25EaXNwbGF5U3RyaW5nIiwiSW5kaXZpZHVhbEhvb2tDb25maWciLCJEaWFsb2ciLCJQcm9wcyIsInNlbGVjdGVkSG9vayIsImV2ZW50U3VwcG9ydHNNYXRjaGVyIiwib25DYW5jZWwiLCJWaWV3SG9va01vZGUiLCJ0MCIsIiQiLCJfYyIsInQxIiwiZXZlbnQiLCJ0MiIsIm1hdGNoZXIiLCJ0MyIsImNvbmZpZyIsInR5cGUiLCJ0NCIsInNvdXJjZSIsInQ1IiwidDYiLCJwbHVnaW5OYW1lIiwidDciLCJ0OCIsImdldENvbnRlbnRGaWVsZExhYmVsIiwidDkiLCJ0MTAiLCJnZXRDb250ZW50RmllbGRWYWx1ZSIsInQxMSIsInQxMiIsInQxMyIsInN0YXR1c01lc3NhZ2UiLCJ0MTQiLCJTeW1ib2wiLCJmb3IiLCJ0MTUiLCJ0MTYiLCJfdGVtcCIsImNvbW1hbmQiLCJwcm9tcHQiLCJ1cmwiXSwic291cmNlcyI6WyJWaWV3SG9va01vZGUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogVmlld0hvb2tNb2RlIHNob3dzIHJlYWQtb25seSBkZXRhaWxzIGZvciBhIHNpbmdsZSBjb25maWd1cmVkIGhvb2suXG4gKlxuICogVGhlIC9ob29rcyBtZW51IGlzIHJlYWQtb25seTsgdGhpcyB2aWV3IHJlcGxhY2VzIHRoZSBmb3JtZXIgZGVsZXRlLWhvb2tcbiAqIGNvbmZpcm1hdGlvbiBzY3JlZW4gYW5kIGRpcmVjdHMgdXNlcnMgdG8gc2V0dGluZ3MuanNvbiBvciBDbGF1ZGUgZm9yIGVkaXRzLlxuICovXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7XG4gIGhvb2tTb3VyY2VEZXNjcmlwdGlvbkRpc3BsYXlTdHJpbmcsXG4gIHR5cGUgSW5kaXZpZHVhbEhvb2tDb25maWcsXG59IGZyb20gJy4uLy4uL3V0aWxzL2hvb2tzL2hvb2tzU2V0dGluZ3MuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgc2VsZWN0ZWRIb29rOiBJbmRpdmlkdWFsSG9va0NvbmZpZ1xuICBldmVudFN1cHBvcnRzTWF0Y2hlcjogYm9vbGVhblxuICBvbkNhbmNlbDogKCkgPT4gdm9pZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gVmlld0hvb2tNb2RlKHtcbiAgc2VsZWN0ZWRIb29rLFxuICBldmVudFN1cHBvcnRzTWF0Y2hlcixcbiAgb25DYW5jZWwsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJIb29rIGRldGFpbHNcIlxuICAgICAgb25DYW5jZWw9e29uQ2FuY2VsfVxuICAgICAgaW5wdXRHdWlkZT17KCkgPT4gPFRleHQ+RXNjIHRvIGdvIGJhY2s8L1RleHQ+fVxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgRXZlbnQ6IDxUZXh0IGJvbGQ+e3NlbGVjdGVkSG9vay5ldmVudH08L1RleHQ+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIHtldmVudFN1cHBvcnRzTWF0Y2hlciAmJiAoXG4gICAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgICAgTWF0Y2hlcjogPFRleHQgYm9sZD57c2VsZWN0ZWRIb29rLm1hdGNoZXIgfHwgJyhhbGwpJ308L1RleHQ+XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgKX1cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIFR5cGU6IDxUZXh0IGJvbGQ+e3NlbGVjdGVkSG9vay5jb25maWcudHlwZX08L1RleHQ+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgU291cmNlOnsnICd9XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAge2hvb2tTb3VyY2VEZXNjcmlwdGlvbkRpc3BsYXlTdHJpbmcoc2VsZWN0ZWRIb29rLnNvdXJjZSl9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIHtzZWxlY3RlZEhvb2sucGx1Z2luTmFtZSAmJiAoXG4gICAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgICAgUGx1Z2luOiA8VGV4dCBkaW1Db2xvcj57c2VsZWN0ZWRIb29rLnBsdWdpbk5hbWV9PC9UZXh0PlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICl9XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57Z2V0Q29udGVudEZpZWxkTGFiZWwoc2VsZWN0ZWRIb29rLmNvbmZpZyl9OjwvVGV4dD5cbiAgICAgICAgICA8Qm94XG4gICAgICAgICAgICBib3JkZXJTdHlsZT1cInJvdW5kXCJcbiAgICAgICAgICAgIGJvcmRlckRpbUNvbG9yXG4gICAgICAgICAgICBwYWRkaW5nTGVmdD17MX1cbiAgICAgICAgICAgIHBhZGRpbmdSaWdodD17MX1cbiAgICAgICAgICA+XG4gICAgICAgICAgICA8VGV4dD57Z2V0Q29udGVudEZpZWxkVmFsdWUoc2VsZWN0ZWRIb29rLmNvbmZpZyl9PC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgeydzdGF0dXNNZXNzYWdlJyBpbiBzZWxlY3RlZEhvb2suY29uZmlnICYmXG4gICAgICAgICAgc2VsZWN0ZWRIb29rLmNvbmZpZy5zdGF0dXNNZXNzYWdlICYmIChcbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICBTdGF0dXMgbWVzc2FnZTp7JyAnfVxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57c2VsZWN0ZWRIb29rLmNvbmZpZy5zdGF0dXNNZXNzYWdlfTwvVGV4dD5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApfVxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICBUbyBtb2RpZnkgb3IgcmVtb3ZlIHRoaXMgaG9vaywgZWRpdCBzZXR0aW5ncy5qc29uIGRpcmVjdGx5IG9yIGFza1xuICAgICAgICAgIENsYXVkZSB0byBoZWxwLlxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuXG4vKipcbiAqIEdldCBhIGh1bWFuLXJlYWRhYmxlIGxhYmVsIGZvciB0aGUgcHJpbWFyeSBjb250ZW50IGZpZWxkIG9mIGEgaG9va1xuICogYmFzZWQgb24gaXRzIHR5cGUuXG4gKi9cbmZ1bmN0aW9uIGdldENvbnRlbnRGaWVsZExhYmVsKGNvbmZpZzogSW5kaXZpZHVhbEhvb2tDb25maWdbJ2NvbmZpZyddKTogc3RyaW5nIHtcbiAgc3dpdGNoIChjb25maWcudHlwZSkge1xuICAgIGNhc2UgJ2NvbW1hbmQnOlxuICAgICAgcmV0dXJuICdDb21tYW5kJ1xuICAgIGNhc2UgJ3Byb21wdCc6XG4gICAgICByZXR1cm4gJ1Byb21wdCdcbiAgICBjYXNlICdhZ2VudCc6XG4gICAgICByZXR1cm4gJ1Byb21wdCdcbiAgICBjYXNlICdodHRwJzpcbiAgICAgIHJldHVybiAnVVJMJ1xuICB9XG59XG5cbi8qKlxuICogR2V0IHRoZSBhY3R1YWwgY29udGVudCB2YWx1ZSBmb3IgYSBob29rJ3MgcHJpbWFyeSBmaWVsZCwgYnlwYXNzaW5nXG4gKiBzdGF0dXNNZXNzYWdlIHNvIHRoZSBkZXRhaWwgdmlldyBhbHdheXMgc2hvd3MgdGhlIHJlYWwgY29tbWFuZC9wcm9tcHQvVVJMLlxuICovXG5mdW5jdGlvbiBnZXRDb250ZW50RmllbGRWYWx1ZShjb25maWc6IEluZGl2aWR1YWxIb29rQ29uZmlnWydjb25maWcnXSk6IHN0cmluZyB7XG4gIHN3aXRjaCAoY29uZmlnLnR5cGUpIHtcbiAgICBjYXNlICdjb21tYW5kJzpcbiAgICAgIHJldHVybiBjb25maWcuY29tbWFuZFxuICAgIGNhc2UgJ3Byb21wdCc6XG4gICAgICByZXR1cm4gY29uZmlnLnByb21wdFxuICAgIGNhc2UgJ2FnZW50JzpcbiAgICAgIHJldHVybiBjb25maWcucHJvbXB0XG4gICAgY2FzZSAnaHR0cCc6XG4gICAgICByZXR1cm4gY29uZmlnLnVybFxuICB9XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsU0FDRUMsa0NBQWtDLEVBQ2xDLEtBQUtDLG9CQUFvQixRQUNwQixvQ0FBb0M7QUFDM0MsU0FBU0MsTUFBTSxRQUFRLDRCQUE0QjtBQUVuRCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsWUFBWSxFQUFFSCxvQkFBb0I7RUFDbENJLG9CQUFvQixFQUFFLE9BQU87RUFDN0JDLFFBQVEsRUFBRSxHQUFHLEdBQUcsSUFBSTtBQUN0QixDQUFDO0FBRUQsT0FBTyxTQUFBQyxhQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXNCO0lBQUFOLFlBQUE7SUFBQUMsb0JBQUE7SUFBQUM7RUFBQSxJQUFBRSxFQUlyQjtFQUFBLElBQUFHLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFMLFlBQUEsQ0FBQVEsS0FBQTtJQVNFRCxFQUFBLElBQUMsSUFBSSxDQUFDLE9BQ0csQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFKLEtBQUcsQ0FBQyxDQUFFLENBQUFQLFlBQVksQ0FBQVEsS0FBSyxDQUFFLEVBQTlCLElBQUksQ0FDZCxFQUZDLElBQUksQ0FFRTtJQUFBSCxDQUFBLE1BQUFMLFlBQUEsQ0FBQVEsS0FBQTtJ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 \ No newline at end of file diff --git a/components/mcp/CapabilitiesSection.tsx b/components/mcp/CapabilitiesSection.tsx new file mode 100644 index 0000000..7d136f5 --- /dev/null +++ b/components/mcp/CapabilitiesSection.tsx @@ -0,0 +1,61 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, Text } from '../../ink.js'; +import { Byline } from '../design-system/Byline.js'; +type Props = { + serverToolsCount: number; + serverPromptsCount: number; + serverResourcesCount: number; +}; +export function CapabilitiesSection(t0) { + const $ = _c(9); + const { + serverToolsCount, + serverPromptsCount, + serverResourcesCount + } = t0; + let capabilities; + if ($[0] !== serverPromptsCount || $[1] !== serverResourcesCount || $[2] !== serverToolsCount) { + capabilities = []; + if (serverToolsCount > 0) { + capabilities.push("tools"); + } + if (serverResourcesCount > 0) { + capabilities.push("resources"); + } + if (serverPromptsCount > 0) { + capabilities.push("prompts"); + } + $[0] = serverPromptsCount; + $[1] = serverResourcesCount; + $[2] = serverToolsCount; + $[3] = capabilities; + } else { + capabilities = $[3]; + } + let t1; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Capabilities: ; + $[4] = t1; + } else { + t1 = $[4]; + } + let t2; + if ($[5] !== capabilities) { + t2 = capabilities.length > 0 ? {capabilities} : "none"; + $[5] = capabilities; + $[6] = t2; + } else { + t2 = $[6]; + } + let t3; + if ($[7] !== t2) { + t3 = {t1}{t2}; + $[7] = t2; + $[8] = t3; + } else { + t3 = $[8]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJCeWxpbmUiLCJQcm9wcyIsInNlcnZlclRvb2xzQ291bnQiLCJzZXJ2ZXJQcm9tcHRzQ291bnQiLCJzZXJ2ZXJSZXNvdXJjZXNDb3VudCIsIkNhcGFiaWxpdGllc1NlY3Rpb24iLCJ0MCIsIiQiLCJfYyIsImNhcGFiaWxpdGllcyIsInB1c2giLCJ0MSIsIlN5bWJvbCIsImZvciIsInQyIiwibGVuZ3RoIiwidDMiXSwic291cmNlcyI6WyJDYXBhYmlsaXRpZXNTZWN0aW9uLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgc2VydmVyVG9vbHNDb3VudDogbnVtYmVyXG4gIHNlcnZlclByb21wdHNDb3VudDogbnVtYmVyXG4gIHNlcnZlclJlc291cmNlc0NvdW50OiBudW1iZXJcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIENhcGFiaWxpdGllc1NlY3Rpb24oe1xuICBzZXJ2ZXJUb29sc0NvdW50LFxuICBzZXJ2ZXJQcm9tcHRzQ291bnQsXG4gIHNlcnZlclJlc291cmNlc0NvdW50LFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBjYXBhYmlsaXRpZXMgPSBbXVxuICBpZiAoc2VydmVyVG9vbHNDb3VudCA+IDApIHtcbiAgICBjYXBhYmlsaXRpZXMucHVzaCgndG9vbHMnKVxuICB9XG4gIGlmIChzZXJ2ZXJSZXNvdXJjZXNDb3VudCA+IDApIHtcbiAgICBjYXBhYmlsaXRpZXMucHVzaCgncmVzb3VyY2VzJylcbiAgfVxuICBpZiAoc2VydmVyUHJvbXB0c0NvdW50ID4gMCkge1xuICAgIGNhcGFiaWxpdGllcy5wdXNoKCdwcm9tcHRzJylcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveD5cbiAgICAgIDxUZXh0IGJvbGQ+Q2FwYWJpbGl0aWVzOiA8L1RleHQ+XG4gICAgICA8VGV4dCBjb2xvcj1cInRleHRcIj5cbiAgICAgICAge2NhcGFiaWxpdGllcy5sZW5ndGggPiAwID8gPEJ5bGluZT57Y2FwYWJpbGl0aWVzfTwvQnlsaW5lPiA6ICdub25lJ31cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxNQUFNLFFBQVEsNEJBQTRCO0FBRW5ELEtBQUtDLEtBQUssR0FBRztFQUNYQyxnQkFBZ0IsRUFBRSxNQUFNO0VBQ3hCQyxrQkFBa0IsRUFBRSxNQUFNO0VBQzFCQyxvQkFBb0IsRUFBRSxNQUFNO0FBQzlCLENBQUM7QUFFRCxPQUFPLFNBQUFDLG9CQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQTZCO0lBQUFOLGdCQUFBO0lBQUFDLGtCQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFJNUI7RUFBQSxJQUFBRyxZQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBSixrQkFBQSxJQUFBSSxDQUFBLFFBQUFILG9CQUFBLElBQUFHLENBQUEsUUFBQUwsZ0JBQUE7SUFDTk8sWUFBQSxHQUFxQixFQUFFO0lBQ3ZCLElBQUlQLGdCQUFnQixHQUFHLENBQUM7TUFDdEJPLFlBQVksQ0FBQUMsSUFBSyxDQUFDLE9BQU8sQ0FBQztJQUFBO0lBRTVCLElBQUlOLG9CQUFvQixHQUFHLENBQUM7TUFDMUJLLFlBQVksQ0FBQUMsSUFBSyxDQUFDLFdBQVcsQ0FBQztJQUFBO0lBRWhDLElBQUlQLGtCQUFrQixHQUFHLENBQUM7TUFDeEJNLFlBQVksQ0FBQUMsSUFBSyxDQUFDLFNBQVMsQ0FBQztJQUFBO0lBQzdCSCxDQUFBLE1BQUFKLGtCQUFBO0lBQUFJLENBQUEsTUFBQUgsb0JBQUE7SUFBQUcsQ0FBQSxNQUFBTCxnQkFBQTtJQUFBSyxDQUFBLE1BQUFFLFlBQUE7RUFBQTtJQUFBQSxZQUFBLEdBQUFGLENBQUE7RUFBQTtFQUFBLElBQUFJLEVBQUE7RUFBQSxJQUFBSixDQUFBLFFBQUFLLE1BQUEsQ0FBQUMsR0FBQTtJQUlHRixFQUFBLElBQUMsSUFBSSxDQUFDLElBQUksQ0FBSixLQUFHLENBQUMsQ0FBQyxjQUFjLEVBQXhCLElBQUksQ0FBMkI7SUFBQUosQ0FBQSxNQUFBSSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSixDQUFBO0VBQUE7RUFBQSxJQUFBTyxFQUFBO0VBQUEsSUFBQVAsQ0FBQSxRQUFBRSxZQUFBO0lBRTdCSyxFQUFBLEdBQUFMLFlBQVksQ0FBQU0sTUFBTyxHQUFHLENBQTRDLEdBQXhDLENBQUMsTUFBTSxDQUFFTixhQUFXLENBQUUsRUFBckIsTUFBTSxDQUFpQyxHQUFsRSxNQUFrRTtJQUFBRixDQUFBLE1BQUFFLFlBQUE7SUFBQUYsQ0FBQSxNQUFBTyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBUCxDQUFBO0VBQUE7RUFBQSxJQUFBUyxFQUFBO0VBQUEsSUFBQVQsQ0FBQSxRQUFBTyxFQUFBO0lBSHZFRSxFQUFBLElBQUMsR0FBRyxDQUNGLENBQUFMLEVBQStCLENBQy9CLENBQUMsSUFBSSxDQUFPLEtBQU0sQ0FBTixNQUFNLENBQ2YsQ0FBQUcsRUFBaUUsQ0FDcEUsRUFGQyxJQUFJLENBR1AsRUFMQyxHQUFHLENBS0U7SUFBQVAsQ0FBQSxNQUFBTyxFQUFBO0lBQUFQLENBQUEsTUFBQVMsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVQsQ0FBQTtFQUFBO0VBQUEsT0FMTlMsRUFLTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/mcp/ElicitationDialog.tsx b/components/mcp/ElicitationDialog.tsx new file mode 100644 index 0000000..2fdf0e6 --- /dev/null +++ b/components/mcp/ElicitationDialog.tsx @@ -0,0 +1,1169 @@ +import { c as _c } from "react/compiler-runtime"; +import type { ElicitRequestFormParams, ElicitRequestURLParams, ElicitResult, PrimitiveSchemaDefinition } from '@modelcontextprotocol/sdk/types.js'; +import figures from 'figures'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useRegisterOverlay } from '../../context/overlayContext.js'; +import { useNotifyAfterTimeout } from '../../hooks/useNotifyAfterTimeout.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw text input for elicitation form +import { Box, Text, useInput } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import type { ElicitationRequestEvent } from '../../services/mcp/elicitationHandler.js'; +import { openBrowser } from '../../utils/browser.js'; +import { getEnumLabel, getEnumValues, getMultiSelectLabel, getMultiSelectValues, isDateTimeSchema, isEnumSchema, isMultiSelectEnumSchema, validateElicitationInput, validateElicitationInputAsync } from '../../utils/mcp/elicitationValidation.js'; +import { plural } from '../../utils/stringUtils.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { Byline } from '../design-system/Byline.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import TextInput from '../TextInput.js'; +type Props = { + event: ElicitationRequestEvent; + onResponse: (action: ElicitResult['action'], content?: ElicitResult['content']) => void; + /** Called when the phase 2 waiting state is dismissed (URL elicitations only). */ + onWaitingDismiss?: (action: 'dismiss' | 'retry' | 'cancel') => void; +}; +const isTextField = (s: PrimitiveSchemaDefinition) => ['string', 'number', 'integer'].includes(s.type); +const RESOLVING_SPINNER_CHARS = '\u280B\u2819\u2839\u2838\u283C\u2834\u2826\u2827\u2807\u280F'; +const advanceSpinnerFrame = (f: number) => (f + 1) % RESOLVING_SPINNER_CHARS.length; + +/** Timer callback for enumTypeaheadRef — module-scope to avoid closure capture. */ +function resetTypeahead(ta: { + buffer: string; + timer: ReturnType | undefined; +}): void { + ta.buffer = ''; + ta.timer = undefined; +} + +/** + * Isolated spinner glyph for a field that is being resolved asynchronously. + * Owns its own 80ms animation timer so ticks only re-render this tiny leaf, + * not the entire ElicitationFormDialog (~1200 lines + renderFormFields). + * Mounted/unmounted by the parent via the `isResolving` condition. + * + * Not using the shared from ../Spinner.js: that one renders in a + * with color="text", which would break the 1-col checkbox + * column alignment here (other checkbox states are width-1 glyphs). + */ +function ResolvingSpinner() { + const $ = _c(4); + const [frame, setFrame] = useState(0); + let t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = () => { + const timer = setInterval(setFrame, 80, advanceSpinnerFrame); + return () => clearInterval(timer); + }; + t1 = []; + $[0] = t0; + $[1] = t1; + } else { + t0 = $[0]; + t1 = $[1]; + } + useEffect(t0, t1); + const t2 = RESOLVING_SPINNER_CHARS[frame]; + let t3; + if ($[2] !== t2) { + t3 = {t2}; + $[2] = t2; + $[3] = t3; + } else { + t3 = $[3]; + } + return t3; +} + +/** Format an ISO date/datetime for display, keeping the ISO value for submission. */ +function formatDateDisplay(isoValue: string, schema: PrimitiveSchemaDefinition): string { + try { + const date = new Date(isoValue); + if (Number.isNaN(date.getTime())) return isoValue; + const format = 'format' in schema ? schema.format : undefined; + if (format === 'date-time') { + return date.toLocaleDateString('en-US', { + weekday: 'short', + year: 'numeric', + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: '2-digit', + timeZoneName: 'short' + }); + } + // date-only: parse as local date to avoid timezone shift + const parts = isoValue.split('-'); + if (parts.length === 3) { + const local = new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2])); + return local.toLocaleDateString('en-US', { + weekday: 'short', + year: 'numeric', + month: 'short', + day: 'numeric' + }); + } + return isoValue; + } catch { + return isoValue; + } +} +export function ElicitationDialog(t0) { + const $ = _c(7); + const { + event, + onResponse, + onWaitingDismiss + } = t0; + if (event.params.mode === "url") { + let t1; + if ($[0] !== event || $[1] !== onResponse || $[2] !== onWaitingDismiss) { + t1 = ; + $[0] = event; + $[1] = onResponse; + $[2] = onWaitingDismiss; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; + } + let t1; + if ($[4] !== event || $[5] !== onResponse) { + t1 = ; + $[4] = event; + $[5] = onResponse; + $[6] = t1; + } else { + t1 = $[6]; + } + return t1; +} +function ElicitationFormDialog({ + event, + onResponse +}: { + event: ElicitationRequestEvent; + onResponse: Props['onResponse']; +}): React.ReactNode { + const { + serverName, + signal + } = event; + const request = event.params as ElicitRequestFormParams; + const { + message, + requestedSchema + } = request; + const hasFields = Object.keys(requestedSchema.properties).length > 0; + const [focusedButton, setFocusedButton] = useState<'accept' | 'decline' | null>(hasFields ? null : 'accept'); + const [formValues, setFormValues] = useState>(() => { + const initialValues: Record = {}; + if (requestedSchema.properties) { + for (const [propName, propSchema] of Object.entries(requestedSchema.properties)) { + if (typeof propSchema === 'object' && propSchema !== null) { + if (propSchema.default !== undefined) { + initialValues[propName] = propSchema.default; + } + } + } + } + return initialValues; + }); + const [validationErrors, setValidationErrors] = useState>(() => { + const initialErrors: Record = {}; + for (const [propName_0, propSchema_0] of Object.entries(requestedSchema.properties)) { + if (isTextField(propSchema_0) && propSchema_0?.default !== undefined) { + const validation = validateElicitationInput(String(propSchema_0.default), propSchema_0); + if (!validation.isValid && validation.error) { + initialErrors[propName_0] = validation.error; + } + } + } + return initialErrors; + }); + useEffect(() => { + if (!signal) return; + const handleAbort = () => { + onResponse('cancel'); + }; + if (signal.aborted) { + handleAbort(); + return; + } + signal.addEventListener('abort', handleAbort); + return () => { + signal.removeEventListener('abort', handleAbort); + }; + }, [signal, onResponse]); + const schemaFields = useMemo(() => { + const requiredFields = requestedSchema.required ?? []; + return Object.entries(requestedSchema.properties).map(([name, schema]) => ({ + name, + schema, + isRequired: requiredFields.includes(name) + })); + }, [requestedSchema]); + const [currentFieldIndex, setCurrentFieldIndex] = useState(hasFields ? 0 : undefined); + const [textInputValue, setTextInputValue] = useState(() => { + // Initialize from the first field's value if it's a text field + const firstField = schemaFields[0]; + if (firstField && isTextField(firstField.schema)) { + const val = formValues[firstField.name]; + if (val === undefined) return ''; + return String(val); + } + return ''; + }); + const [textInputCursorOffset, setTextInputCursorOffset] = useState(textInputValue.length); + const [resolvingFields, setResolvingFields] = useState>(() => new Set()); + // Accordion state (shared by multi-select and single-select enum) + const [expandedAccordion, setExpandedAccordion] = useState(); + const [accordionOptionIndex, setAccordionOptionIndex] = useState(0); + const dateDebounceRef = useRef | undefined>(undefined); + const resolveAbortRef = useRef>(new Map()); + const enumTypeaheadRef = useRef({ + buffer: '', + timer: undefined as ReturnType | undefined + }); + + // Clear pending debounce/typeahead timers and abort in-flight async + // validations on unmount so they don't fire against an unmounted component + // (e.g. dialog dismissed mid-debounce or mid-resolve). + useEffect(() => () => { + if (dateDebounceRef.current !== undefined) { + clearTimeout(dateDebounceRef.current); + } + const ta = enumTypeaheadRef.current; + if (ta.timer !== undefined) { + clearTimeout(ta.timer); + } + for (const controller of resolveAbortRef.current.values()) { + controller.abort(); + } + resolveAbortRef.current.clear(); + }, []); + const { + columns, + rows + } = useTerminalSize(); + const currentField = currentFieldIndex !== undefined ? schemaFields[currentFieldIndex] : undefined; + const currentFieldIsText = currentField !== undefined && isTextField(currentField.schema) && !isEnumSchema(currentField.schema); + + // Text fields are always in edit mode when focused — no Enter-to-edit step. + const isEditingTextField = currentFieldIsText && !focusedButton; + useRegisterOverlay('elicitation'); + useNotifyAfterTimeout('Claude Code needs your input', 'elicitation_dialog'); + + // Sync textInputValue when the focused field changes + const syncTextInput = useCallback((fieldIndex: number | undefined) => { + if (fieldIndex === undefined) { + setTextInputValue(''); + setTextInputCursorOffset(0); + return; + } + const field = schemaFields[fieldIndex]; + if (field && isTextField(field.schema) && !isEnumSchema(field.schema)) { + const val_0 = formValues[field.name]; + const text = val_0 !== undefined ? String(val_0) : ''; + setTextInputValue(text); + setTextInputCursorOffset(text.length); + } + }, [schemaFields, formValues]); + function validateMultiSelect(fieldName: string, schema_0: PrimitiveSchemaDefinition) { + if (!isMultiSelectEnumSchema(schema_0)) return; + const selected = formValues[fieldName] as string[] | undefined ?? []; + const fieldRequired = schemaFields.find(f => f.name === fieldName)?.isRequired ?? false; + const min = schema_0.minItems; + const max = schema_0.maxItems; + // Skip minItems check when field is optional and unset + if (min !== undefined && selected.length < min && (selected.length > 0 || fieldRequired)) { + updateValidationError(fieldName, `Select at least ${min} ${plural(min, 'item')}`); + } else if (max !== undefined && selected.length > max) { + updateValidationError(fieldName, `Select at most ${max} ${plural(max, 'item')}`); + } else { + updateValidationError(fieldName); + } + } + function handleNavigation(direction: 'up' | 'down'): void { + // Collapse accordion and validate on navigate away + if (currentField && isMultiSelectEnumSchema(currentField.schema)) { + validateMultiSelect(currentField.name, currentField.schema); + setExpandedAccordion(undefined); + } else if (currentField && isEnumSchema(currentField.schema)) { + setExpandedAccordion(undefined); + } + + // Commit current text field before navigating away + if (isEditingTextField && currentField) { + commitTextField(currentField.name, currentField.schema, textInputValue); + + // Cancel any pending debounce — we're resolving now on navigate-away + if (dateDebounceRef.current !== undefined) { + clearTimeout(dateDebounceRef.current); + dateDebounceRef.current = undefined; + } + + // For date/datetime fields that failed sync validation, try async NL parsing + if (isDateTimeSchema(currentField.schema) && textInputValue.trim() !== '' && validationErrors[currentField.name]) { + resolveFieldAsync(currentField.name, currentField.schema, textInputValue); + } + } + + // Fields + accept + decline + const itemCount = schemaFields.length + 2; + const index = currentFieldIndex ?? (focusedButton === 'accept' ? schemaFields.length : focusedButton === 'decline' ? schemaFields.length + 1 : undefined); + const nextIndex = index !== undefined ? (index + (direction === 'up' ? itemCount - 1 : 1)) % itemCount : 0; + if (nextIndex < schemaFields.length) { + setCurrentFieldIndex(nextIndex); + setFocusedButton(null); + syncTextInput(nextIndex); + } else { + setCurrentFieldIndex(undefined); + setFocusedButton(nextIndex === schemaFields.length ? 'accept' : 'decline'); + setTextInputValue(''); + } + } + function setField(fieldName_0: string, value: number | string | boolean | string[] | undefined) { + setFormValues(prev => { + const next = { + ...prev + }; + if (value === undefined) { + delete next[fieldName_0]; + } else { + next[fieldName_0] = value; + } + return next; + }); + // Clear "required" error when a value is provided + if (value !== undefined && validationErrors[fieldName_0] === 'This field is required') { + updateValidationError(fieldName_0); + } + } + function updateValidationError(fieldName_1: string, error?: string) { + setValidationErrors(prev_0 => { + const next_0 = { + ...prev_0 + }; + if (error) { + next_0[fieldName_1] = error; + } else { + delete next_0[fieldName_1]; + } + return next_0; + }); + } + function unsetField(fieldName_2: string) { + if (!fieldName_2) return; + setField(fieldName_2, undefined); + updateValidationError(fieldName_2); + setTextInputValue(''); + setTextInputCursorOffset(0); + } + function commitTextField(fieldName_3: string, schema_1: PrimitiveSchemaDefinition, value_0: string) { + const trimmedValue = value_0.trim(); + + // Empty input for non-plain-string types means unset + if (trimmedValue === '' && (schema_1.type !== 'string' || 'format' in schema_1 && schema_1.format !== undefined)) { + unsetField(fieldName_3); + return; + } + if (trimmedValue === '') { + // Empty plain string — keep or unset depending on whether it was set + if (formValues[fieldName_3] !== undefined) { + setField(fieldName_3, ''); + } + return; + } + const validation_0 = validateElicitationInput(value_0, schema_1); + setField(fieldName_3, validation_0.isValid ? validation_0.value : value_0); + updateValidationError(fieldName_3, validation_0.isValid ? undefined : validation_0.error); + } + function resolveFieldAsync(fieldName_4: string, schema_2: PrimitiveSchemaDefinition, rawValue: string) { + if (!signal) return; + + // Abort any existing resolution for this field + const existing = resolveAbortRef.current.get(fieldName_4); + if (existing) { + existing.abort(); + } + const controller_0 = new AbortController(); + resolveAbortRef.current.set(fieldName_4, controller_0); + setResolvingFields(prev_1 => new Set(prev_1).add(fieldName_4)); + void validateElicitationInputAsync(rawValue, schema_2, controller_0.signal).then(result => { + resolveAbortRef.current.delete(fieldName_4); + setResolvingFields(prev_2 => { + const next_1 = new Set(prev_2); + next_1.delete(fieldName_4); + return next_1; + }); + if (controller_0.signal.aborted) return; + if (result.isValid) { + setField(fieldName_4, result.value); + updateValidationError(fieldName_4); + // Update the text input if we're still on this field + const isoText = String(result.value); + setTextInputValue(prev_3 => { + // Only replace if the field is still showing the raw input + if (prev_3 === rawValue) { + setTextInputCursorOffset(isoText.length); + return isoText; + } + return prev_3; + }); + } else { + // Keep raw text, show validation error + updateValidationError(fieldName_4, result.error); + } + }, () => { + resolveAbortRef.current.delete(fieldName_4); + setResolvingFields(prev_4 => { + const next_2 = new Set(prev_4); + next_2.delete(fieldName_4); + return next_2; + }); + }); + } + function handleTextInputChange(newValue: string) { + setTextInputValue(newValue); + // Commit immediately on each keystroke (sync validation) + if (currentField) { + commitTextField(currentField.name, currentField.schema, newValue); + + // For date/datetime fields, debounce async NL parsing after 2s of inactivity + if (dateDebounceRef.current !== undefined) { + clearTimeout(dateDebounceRef.current); + dateDebounceRef.current = undefined; + } + if (isDateTimeSchema(currentField.schema) && newValue.trim() !== '' && validationErrors[currentField.name]) { + const fieldName_5 = currentField.name; + const schema_3 = currentField.schema; + dateDebounceRef.current = setTimeout((dateDebounceRef_0, resolveFieldAsync_0, fieldName_6, schema_4, newValue_0) => { + dateDebounceRef_0.current = undefined; + resolveFieldAsync_0(fieldName_6, schema_4, newValue_0); + }, 2000, dateDebounceRef, resolveFieldAsync, fieldName_5, schema_3, newValue); + } + } + } + function handleTextInputSubmit() { + handleNavigation('down'); + } + + /** + * Append a keystroke to the typeahead buffer (reset after 2s idle) and + * call `onMatch` with the index of the first label that prefix-matches. + * Shared by boolean y/n, enum accordion, and multi-select accordion. + */ + function runTypeahead(char: string, labels: string[], onMatch: (index: number) => void) { + const ta_0 = enumTypeaheadRef.current; + if (ta_0.timer !== undefined) clearTimeout(ta_0.timer); + ta_0.buffer += char.toLowerCase(); + ta_0.timer = setTimeout(resetTypeahead, 2000, ta_0); + const match = labels.findIndex(l => l.startsWith(ta_0.buffer)); + if (match !== -1) onMatch(match); + } + + // Esc while a field is focused: cancel the dialog. + // Uses Settings context (escape-only, no 'n' key) since Dialog's + // Confirmation-context cancel is suppressed when a field is focused. + useKeybinding('confirm:no', () => { + // For text fields, revert uncommitted changes first + if (isEditingTextField && currentField) { + const val_1 = formValues[currentField.name]; + setTextInputValue(val_1 !== undefined ? String(val_1) : ''); + setTextInputCursorOffset(0); + } + onResponse('cancel'); + }, { + context: 'Settings', + isActive: !!currentField && !focusedButton && !expandedAccordion + }); + useInput((_input, key) => { + // Text fields handle their own character input; we only intercept + // navigation keys and backspace-on-empty here. + if (isEditingTextField && !key.upArrow && !key.downArrow && !key.return && !key.backspace) { + return; + } + + // Expanded multi-select accordion + if (expandedAccordion && currentField && isMultiSelectEnumSchema(currentField.schema)) { + const msSchema = currentField.schema; + const msValues = getMultiSelectValues(msSchema); + const selected_0 = formValues[currentField.name] as string[] ?? []; + if (key.leftArrow || key.escape) { + setExpandedAccordion(undefined); + validateMultiSelect(currentField.name, msSchema); + return; + } + if (key.upArrow) { + if (accordionOptionIndex === 0) { + setExpandedAccordion(undefined); + validateMultiSelect(currentField.name, msSchema); + } else { + setAccordionOptionIndex(accordionOptionIndex - 1); + } + return; + } + if (key.downArrow) { + if (accordionOptionIndex >= msValues.length - 1) { + setExpandedAccordion(undefined); + handleNavigation('down'); + } else { + setAccordionOptionIndex(accordionOptionIndex + 1); + } + return; + } + if (_input === ' ') { + const optionValue = msValues[accordionOptionIndex]; + if (optionValue !== undefined) { + const newSelected = selected_0.includes(optionValue) ? selected_0.filter(v => v !== optionValue) : [...selected_0, optionValue]; + const newValue_1 = newSelected.length > 0 ? newSelected : undefined; + setField(currentField.name, newValue_1); + const min_0 = msSchema.minItems; + const max_0 = msSchema.maxItems; + if (min_0 !== undefined && newSelected.length < min_0 && (newSelected.length > 0 || currentField.isRequired)) { + updateValidationError(currentField.name, `Select at least ${min_0} ${plural(min_0, 'item')}`); + } else if (max_0 !== undefined && newSelected.length > max_0) { + updateValidationError(currentField.name, `Select at most ${max_0} ${plural(max_0, 'item')}`); + } else { + updateValidationError(currentField.name); + } + } + return; + } + if (key.return) { + // Check (not toggle) the focused item, then collapse and advance + const optionValue_0 = msValues[accordionOptionIndex]; + if (optionValue_0 !== undefined && !selected_0.includes(optionValue_0)) { + setField(currentField.name, [...selected_0, optionValue_0]); + } + setExpandedAccordion(undefined); + handleNavigation('down'); + return; + } + if (_input) { + const labels_0 = msValues.map(v_0 => getMultiSelectLabel(msSchema, v_0).toLowerCase()); + runTypeahead(_input, labels_0, setAccordionOptionIndex); + return; + } + return; + } + + // Expanded single-select enum accordion + if (expandedAccordion && currentField && isEnumSchema(currentField.schema)) { + const enumSchema = currentField.schema; + const enumValues = getEnumValues(enumSchema); + if (key.leftArrow || key.escape) { + setExpandedAccordion(undefined); + return; + } + if (key.upArrow) { + if (accordionOptionIndex === 0) { + setExpandedAccordion(undefined); + } else { + setAccordionOptionIndex(accordionOptionIndex - 1); + } + return; + } + if (key.downArrow) { + if (accordionOptionIndex >= enumValues.length - 1) { + setExpandedAccordion(undefined); + handleNavigation('down'); + } else { + setAccordionOptionIndex(accordionOptionIndex + 1); + } + return; + } + // Space: select and collapse + if (_input === ' ') { + const optionValue_1 = enumValues[accordionOptionIndex]; + if (optionValue_1 !== undefined) { + setField(currentField.name, optionValue_1); + } + setExpandedAccordion(undefined); + return; + } + // Enter: select, collapse, and move to next field + if (key.return) { + const optionValue_2 = enumValues[accordionOptionIndex]; + if (optionValue_2 !== undefined) { + setField(currentField.name, optionValue_2); + } + setExpandedAccordion(undefined); + handleNavigation('down'); + return; + } + if (_input) { + const labels_1 = enumValues.map(v_1 => getEnumLabel(enumSchema, v_1).toLowerCase()); + runTypeahead(_input, labels_1, setAccordionOptionIndex); + return; + } + return; + } + + // Accept / Decline buttons + if (key.return && focusedButton === 'accept') { + if (validateRequired() && Object.keys(validationErrors).length === 0) { + onResponse('accept', formValues); + } else { + // Show "required" validation errors on missing fields + const requiredFields_0 = requestedSchema.required || []; + for (const fieldName_7 of requiredFields_0) { + if (formValues[fieldName_7] === undefined) { + updateValidationError(fieldName_7, 'This field is required'); + } + } + const firstBadIndex = schemaFields.findIndex(f_0 => requiredFields_0.includes(f_0.name) && formValues[f_0.name] === undefined || validationErrors[f_0.name] !== undefined); + if (firstBadIndex !== -1) { + setCurrentFieldIndex(firstBadIndex); + setFocusedButton(null); + syncTextInput(firstBadIndex); + } + } + return; + } + if (key.return && focusedButton === 'decline') { + onResponse('decline'); + return; + } + + // Up/Down navigation + if (key.upArrow || key.downArrow) { + // Reset enum typeahead when leaving a field + const ta_1 = enumTypeaheadRef.current; + ta_1.buffer = ''; + if (ta_1.timer !== undefined) { + clearTimeout(ta_1.timer); + ta_1.timer = undefined; + } + handleNavigation(key.upArrow ? 'up' : 'down'); + return; + } + + // Left/Right to switch between Accept and Decline buttons + if (focusedButton && (key.leftArrow || key.rightArrow)) { + setFocusedButton(focusedButton === 'accept' ? 'decline' : 'accept'); + return; + } + if (!currentField) return; + const { + schema: schema_5, + name: name_0 + } = currentField; + const value_1 = formValues[name_0]; + + // Boolean: Space to toggle, Enter to move on + if (schema_5.type === 'boolean') { + if (_input === ' ') { + setField(name_0, value_1 === undefined ? true : !value_1); + return; + } + if (key.return) { + handleNavigation('down'); + return; + } + if (key.backspace && value_1 !== undefined) { + unsetField(name_0); + return; + } + // y/n typeahead + if (_input && !key.return) { + runTypeahead(_input, ['yes', 'no'], i => setField(name_0, i === 0)); + return; + } + return; + } + + // Enum or multi-select (collapsed) — accordion style + if (isEnumSchema(schema_5) || isMultiSelectEnumSchema(schema_5)) { + if (key.return) { + handleNavigation('down'); + return; + } + if (key.backspace && value_1 !== undefined) { + unsetField(name_0); + return; + } + // Compute option labels + initial focus index for rightArrow expand. + // Single-select focuses on the current value; multi-select starts at 0. + let labels_2: string[]; + let startIdx = 0; + if (isEnumSchema(schema_5)) { + const vals = getEnumValues(schema_5); + labels_2 = vals.map(v_2 => getEnumLabel(schema_5, v_2).toLowerCase()); + if (value_1 !== undefined) { + startIdx = Math.max(0, vals.indexOf(value_1 as string)); + } + } else { + const vals_0 = getMultiSelectValues(schema_5); + labels_2 = vals_0.map(v_3 => getMultiSelectLabel(schema_5, v_3).toLowerCase()); + } + if (key.rightArrow) { + setExpandedAccordion(name_0); + setAccordionOptionIndex(startIdx); + return; + } + // Typeahead: expand and jump to matching option + if (_input && !key.leftArrow) { + runTypeahead(_input, labels_2, i_0 => { + setExpandedAccordion(name_0); + setAccordionOptionIndex(i_0); + }); + return; + } + return; + } + + // Backspace: text fields when empty + if (key.backspace) { + if (isEditingTextField && textInputValue === '') { + unsetField(name_0); + return; + } + } + + // Text field Enter is handled by TextInput's onSubmit + }, { + isActive: true + }); + function validateRequired(): boolean { + const requiredFields_1 = requestedSchema.required || []; + for (const fieldName_8 of requiredFields_1) { + const value_2 = formValues[fieldName_8]; + if (value_2 === undefined || value_2 === null || value_2 === '') { + return false; + } + if (Array.isArray(value_2) && value_2.length === 0) { + return false; + } + } + return true; + } + + // Scroll windowing: compute visible field range + // Overhead: ~9 lines (dialog chrome, buttons, footer). + // Each field: ~3 lines (label + description + validation spacer). + // NOTE(v2): Multi-select accordion expands to N+3 lines when open. + // For now we assume 3 lines per field; an expanded accordion may + // temporarily push content off-screen (terminal scrollback handles it). + // To generalize: track per-field height (3 for collapsed, N+3 for + // expanded multi-select) and compute a pixel-budget window instead + // of a simple item-count window. + const LINES_PER_FIELD = 3; + const DIALOG_OVERHEAD = 14; + const maxVisibleFields = Math.max(2, Math.floor((rows - DIALOG_OVERHEAD) / LINES_PER_FIELD)); + const scrollWindow = useMemo(() => { + const total = schemaFields.length; + if (total <= maxVisibleFields) { + return { + start: 0, + end: total + }; + } + // When buttons are focused (currentFieldIndex undefined), pin to end + const focusIdx = currentFieldIndex ?? total - 1; + let start = Math.max(0, focusIdx - Math.floor(maxVisibleFields / 2)); + const end = Math.min(start + maxVisibleFields, total); + // Adjust start if we hit the bottom + start = Math.max(0, end - maxVisibleFields); + return { + start, + end + }; + }, [schemaFields.length, maxVisibleFields, currentFieldIndex]); + const hasFieldsAbove = scrollWindow.start > 0; + const hasFieldsBelow = scrollWindow.end < schemaFields.length; + function renderFormFields(): React.ReactNode { + if (!schemaFields.length) return null; + return + {hasFieldsAbove && + + {figures.arrowUp} {scrollWindow.start} more above + + } + {schemaFields.slice(scrollWindow.start, scrollWindow.end).map((field_0, visibleIdx) => { + const index_0 = scrollWindow.start + visibleIdx; + const { + name: name_1, + schema: schema_6, + isRequired + } = field_0; + const isActive = index_0 === currentFieldIndex && !focusedButton; + const value_3 = formValues[name_1]; + const hasValue = value_3 !== undefined && (!Array.isArray(value_3) || value_3.length > 0); + const error_0 = validationErrors[name_1]; + + // Checkbox: spinner → ⚠ error → ✔ set → * required → space + const isResolving = resolvingFields.has(name_1); + const checkbox = isResolving ? : error_0 ? {figures.warning} : hasValue ? + {figures.tick} + : isRequired ? * : ; + + // Selection color matches field status + const selectionColor = error_0 ? 'error' : hasValue ? 'success' : isRequired ? 'error' : 'suggestion'; + const activeColor = isActive ? selectionColor : undefined; + const label = + {schema_6.title || name_1} + ; + + // Render the value portion based on field type + let valueContent: React.ReactNode; + let accordionContent: React.ReactNode = null; + if (isMultiSelectEnumSchema(schema_6)) { + const msValues_0 = getMultiSelectValues(schema_6); + const selected_1 = value_3 as string[] | undefined ?? []; + const isExpanded = expandedAccordion === name_1 && isActive; + if (isExpanded) { + valueContent = {figures.triangleDownSmall}; + accordionContent = + {msValues_0.map((optVal, optIdx) => { + const optLabel = getMultiSelectLabel(schema_6, optVal); + const isChecked = selected_1.includes(optVal); + const isFocused = optIdx === accordionOptionIndex; + return + + {isFocused ? figures.pointer : ' '} + + + {isChecked ? figures.checkboxOn : figures.checkboxOff} + + + {optLabel} + + ; + })} + ; + } else { + // Collapsed: ▸ arrow then comma-joined selected items + const arrow = isActive ? {figures.triangleRightSmall} : null; + if (selected_1.length > 0) { + const displayLabels = selected_1.map(v_4 => getMultiSelectLabel(schema_6, v_4)); + valueContent = + {arrow} + + {displayLabels.join(', ')} + + ; + } else { + valueContent = + {arrow} + + not set + + ; + } + } + } else if (isEnumSchema(schema_6)) { + const enumValues_0 = getEnumValues(schema_6); + const isExpanded_0 = expandedAccordion === name_1 && isActive; + if (isExpanded_0) { + valueContent = {figures.triangleDownSmall}; + accordionContent = + {enumValues_0.map((optVal_0, optIdx_0) => { + const optLabel_0 = getEnumLabel(schema_6, optVal_0); + const isSelected = value_3 === optVal_0; + const isFocused_0 = optIdx_0 === accordionOptionIndex; + return + + {isFocused_0 ? figures.pointer : ' '} + + + {isSelected ? figures.radioOn : figures.radioOff} + + + {optLabel_0} + + ; + })} + ; + } else { + // Collapsed: ▸ arrow then current value + const arrow_0 = isActive ? {figures.triangleRightSmall} : null; + if (hasValue) { + valueContent = + {arrow_0} + + {getEnumLabel(schema_6, value_3 as string)} + + ; + } else { + valueContent = + {arrow_0} + + not set + + ; + } + } + } else if (schema_6.type === 'boolean') { + if (isActive) { + valueContent = hasValue ? + {value_3 ? figures.checkboxOn : figures.checkboxOff} + : {figures.checkboxOff}; + } else { + valueContent = hasValue ? + {value_3 ? figures.checkboxOn : figures.checkboxOff} + : + not set + ; + } + } else if (isTextField(schema_6)) { + if (isActive) { + valueContent = ; + } else { + const displayValue = hasValue && isDateTimeSchema(schema_6) ? formatDateDisplay(String(value_3), schema_6) : String(value_3); + valueContent = hasValue ? {displayValue} : + not set + ; + } + } else { + valueContent = hasValue ? {String(value_3)} : + not set + ; + } + return + + + {isActive ? figures.pointer : ' '} + + {checkbox} + + {label} + : + {valueContent} + + + {accordionContent} + {schema_6.description && + {schema_6.description} + } + + {error_0 ? + {error_0} + : } + + ; + })} + {hasFieldsBelow && + + {figures.arrowDown} {schemaFields.length - scrollWindow.end} more + below + + } + ; + } + return onResponse('cancel')} isCancelActive={(!currentField || !!focusedButton) && !expandedAccordion} inputGuide={exitState => exitState.pending ? Press {exitState.keyName} again to exit : + + + {currentField && } + {currentField && currentField.schema.type === 'boolean' && } + {currentField && isEnumSchema(currentField.schema) && (expandedAccordion ? : )} + {currentField && isMultiSelectEnumSchema(currentField.schema) && (expandedAccordion ? : )} + }> + + {renderFormFields()} + + + {focusedButton === 'accept' ? figures.pointer : ' '} + + + {' Accept '} + + + {focusedButton === 'decline' ? figures.pointer : ' '} + + + {' Decline'} + + + + ; +} +function ElicitationURLDialog({ + event, + onResponse, + onWaitingDismiss +}: { + event: ElicitationRequestEvent; + onResponse: Props['onResponse']; + onWaitingDismiss: Props['onWaitingDismiss']; +}): React.ReactNode { + const { + serverName, + signal, + waitingState + } = event; + const urlParams = event.params as ElicitRequestURLParams; + const { + message, + url + } = urlParams; + const [phase, setPhase] = useState<'prompt' | 'waiting'>('prompt'); + const phaseRef = useRef<'prompt' | 'waiting'>('prompt'); + const [focusedButton, setFocusedButton] = useState<'accept' | 'decline' | 'open' | 'action' | 'cancel'>('accept'); + const showCancel = waitingState?.showCancel ?? false; + useNotifyAfterTimeout('Claude Code needs your input', 'elicitation_url_dialog'); + useRegisterOverlay('elicitation-url'); + + // Keep refs in sync for use in abort handler (avoids re-registering listener) + phaseRef.current = phase; + const onWaitingDismissRef = useRef(onWaitingDismiss); + onWaitingDismissRef.current = onWaitingDismiss; + useEffect(() => { + const handleAbort = () => { + if (phaseRef.current === 'waiting') { + onWaitingDismissRef.current?.('cancel'); + } else { + onResponse('cancel'); + } + }; + if (signal.aborted) { + handleAbort(); + return; + } + signal.addEventListener('abort', handleAbort); + return () => signal.removeEventListener('abort', handleAbort); + }, [signal, onResponse]); + + // Parse URL to highlight the domain + let domain = ''; + let urlBeforeDomain = ''; + let urlAfterDomain = ''; + try { + const parsed = new URL(url); + domain = parsed.hostname; + const domainStart = url.indexOf(domain); + urlBeforeDomain = url.slice(0, domainStart); + urlAfterDomain = url.slice(domainStart + domain.length); + } catch { + domain = url; + } + + // Auto-dismiss when the server sends a completion notification (sets completed flag) + useEffect(() => { + if (phase === 'waiting' && event.completed) { + onWaitingDismiss?.(showCancel ? 'retry' : 'dismiss'); + } + }, [phase, event.completed, onWaitingDismiss, showCancel]); + const handleAccept = useCallback(() => { + void openBrowser(url); + onResponse('accept'); + setPhase('waiting'); + phaseRef.current = 'waiting'; + setFocusedButton('open'); + }, [onResponse, url]); + + // eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw input for button navigation + useInput((_input, key) => { + if (phase === 'prompt') { + if (key.leftArrow || key.rightArrow) { + setFocusedButton(prev => prev === 'accept' ? 'decline' : 'accept'); + return; + } + if (key.return) { + if (focusedButton === 'accept') { + handleAccept(); + } else { + onResponse('decline'); + } + } + } else { + // waiting phase — cycle through buttons + type ButtonName = 'accept' | 'decline' | 'open' | 'action' | 'cancel'; + const waitingButtons: readonly ButtonName[] = showCancel ? ['open', 'action', 'cancel'] : ['open', 'action']; + if (key.leftArrow || key.rightArrow) { + setFocusedButton(prev_0 => { + const idx = waitingButtons.indexOf(prev_0); + const delta = key.rightArrow ? 1 : -1; + return waitingButtons[(idx + delta + waitingButtons.length) % waitingButtons.length]!; + }); + return; + } + if (key.return) { + if (focusedButton === 'open') { + void openBrowser(url); + } else if (focusedButton === 'cancel') { + onWaitingDismiss?.('cancel'); + } else { + onWaitingDismiss?.(showCancel ? 'retry' : 'dismiss'); + } + } + } + }); + if (phase === 'waiting') { + const actionLabel = waitingState?.actionLabel ?? 'Continue without waiting'; + return onWaitingDismiss?.('cancel')} isCancelActive inputGuide={exitState => exitState.pending ? Press {exitState.keyName} again to exit : + + + }> + + + + {urlBeforeDomain} + {domain} + {urlAfterDomain} + + + + + Waiting for the server to confirm completion… + + + + + {focusedButton === 'open' ? figures.pointer : ' '} + + + {' Reopen URL '} + + + {focusedButton === 'action' ? figures.pointer : ' '} + + + {` ${actionLabel}`} + + {showCancel && <> + + + {focusedButton === 'cancel' ? figures.pointer : ' '} + + + {' Cancel'} + + } + + + ; + } + return onResponse('cancel')} isCancelActive inputGuide={exitState_0 => exitState_0.pending ? Press {exitState_0.keyName} again to exit : + + + }> + + + + {urlBeforeDomain} + {domain} + {urlAfterDomain} + + + + + {focusedButton === 'accept' ? figures.pointer : ' '} + + + {' Accept '} + + + {focusedButton === 'decline' ? figures.pointer : ' '} + + + {' Decline'} + + + + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJFbGljaXRSZXF1ZXN0Rm9ybVBhcmFtcyIsIkVsaWNpdFJlcXVlc3RVUkxQYXJhbXMiLCJFbGljaXRSZXN1bHQiLCJQcmltaXRpdmVTY2hlbWFEZWZpbml0aW9uIiwiZmlndXJlcyIsIlJlYWN0IiwidXNlQ2FsbGJhY2siLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwidXNlUmVmIiwidXNlU3RhdGUiLCJ1c2VSZWdpc3Rlck92ZXJsYXkiLCJ1c2VOb3RpZnlBZnRlclRpbWVvdXQiLCJ1c2VUZXJtaW5hbFNpemUiLCJCb3giLCJUZXh0IiwidXNlSW5wdXQiLCJ1c2VLZXliaW5kaW5nIiwiRWxpY2l0YXRpb25SZXF1ZXN0RXZlbnQiLCJvcGVuQnJvd3NlciIsImdldEVudW1MYWJlbCIsImdldEVudW1WYWx1ZXMiLCJnZXRNdWx0aVNlbGVjdExhYmVsIiwiZ2V0TXVsdGlTZWxlY3RWYWx1ZXMiLCJpc0RhdGVUaW1lU2NoZW1hIiwiaXNFbnVtU2NoZW1hIiwiaXNNdWx0aVNlbGVjdEVudW1TY2hlbWEiLCJ2YWxpZGF0ZUVsaWNpdGF0aW9uSW5wdXQiLCJ2YWxpZGF0ZUVsaWNpdGF0aW9uSW5wdXRBc3luYyIsInBsdXJhbCIsIkNvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCIsIkJ5bGluZSIsIkRpYWxvZyIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwiVGV4dElucHV0IiwiUHJvcHMiLCJldmVudCIsIm9uUmVzcG9uc2UiLCJhY3Rpb24iLCJjb250ZW50Iiwib25XYWl0aW5nRGlzbWlzcyIsImlzVGV4dEZpZWxkIiwicyIsImluY2x1ZGVzIiwidHlwZSIsIlJFU09MVklOR19TUElOTkVSX0NIQVJTIiwiYWR2YW5jZVNwaW5uZXJGcmFtZSIsImYiLCJsZW5ndGgiLCJyZXNldFR5cGVhaGVhZCIsInRhIiwiYnVmZmVyIiwidGltZXIiLCJSZXR1cm5UeXBlIiwic2V0VGltZW91dCIsInVuZGVmaW5lZCIsIlJlc29sdmluZ1NwaW5uZXIiLCIkIiwiX2MiLCJmcmFtZSIsInNldEZyYW1lIiwidDAiLCJ0MSIsIlN5bWJvbCIsImZvciIsInNldEludGVydmFsIiwiY2xlYXJJbnRlcnZhbCIsInQyIiwidDMiLCJmb3JtYXREYXRlRGlzcGxheSIsImlzb1ZhbHVlIiwic2NoZW1hIiwiZGF0ZSIsIkRhdGUiLCJOdW1iZXIiLCJpc05hTiIsImdldFRpbWUiLCJmb3JtYXQiLCJ0b0xvY2FsZURhdGVTdHJpbmciLCJ3ZWVrZGF5IiwieWVhciIsIm1vbnRoIiwiZGF5IiwiaG91ciIsIm1pbnV0ZSIsInRpbWVab25lTmFtZSIsInBhcnRzIiwic3BsaXQiLCJsb2NhbCIsIkVsaWNpdGF0aW9uRGlhbG9nIiwicGFyYW1zIiwibW9kZSIsIkVsaWNpdGF0aW9uRm9ybURpYWxvZyIsIlJlYWN0Tm9kZSIsInNlcnZlck5hbWUiLCJzaWduYWwiLCJyZXF1ZXN0IiwibWVzc2FnZSIsInJlcXVlc3RlZFNjaGVtYSIsImhhc0ZpZWxkcyIsIk9iamVjdCIsImtleXMiLCJwcm9wZXJ0aWVzIiwiZm9jdXNlZEJ1dHRvbiIsInNldEZvY3VzZWRCdXR0b24iLCJmb3JtVmFsdWVzIiwic2V0Rm9ybVZhbHVlcyIsIlJlY29yZCIsImluaXRpYWxWYWx1ZXMiLCJwcm9wTmFtZSIsInByb3BTY2hlbWEiLCJlbnRyaWVzIiwiZGVmYXVsdCIsInZhbGlkYXRpb25FcnJvcnMiLCJzZXRWYWxpZGF0aW9uRXJyb3JzIiwiaW5pdGlhbEVycm9ycyIsInZhbGlkYXRpb24iLCJTdHJpbmciLCJpc1ZhbGlkIiwiZXJyb3IiLCJoYW5kbGVBYm9ydCIsImFib3J0ZWQiLCJhZGRFdmVudExpc3RlbmVyIiwicmVtb3ZlRXZlbnRMaXN0ZW5lciIsInNjaGVtYUZpZWxkcyIsInJlcXVpcmVkRmllbGRzIiwicmVxdWlyZWQiLCJtYXAiLCJuYW1lIiwiaXNSZXF1aXJlZCIsImN1cnJlbnRGaWVsZEluZGV4Iiwic2V0Q3VycmVudEZpZWxkSW5kZXgiLCJ0ZXh0SW5wdXRWYWx1ZSIsInNldFRleHRJbnB1dFZhbHVlIiwiZmlyc3RGaWVsZCIsInZhbCIsInRleHRJbnB1dEN1cnNvck9mZnNldCIsInNldFRleHRJbnB1dEN1cnNvck9mZnNldCIsInJlc29sdmluZ0ZpZWxkcyIsInNldFJlc29sdmluZ0ZpZWxkcyIsIlNldCIsImV4cGFuZGVkQWNjb3JkaW9uIiwic2V0RXhwYW5kZWRBY2NvcmRpb24iLCJhY2NvcmRpb25PcHRpb25JbmRleCIsInNldEFjY29yZGlvbk9wdGlvbkluZGV4IiwiZGF0ZURlYm91bmNlUmVmIiwicmVzb2x2ZUFib3J0UmVmIiwiTWFwIiwiQWJvcnRDb250cm9sbGVyIiwiZW51bVR5cGVhaGVhZFJlZiIsImN1cnJlbnQiLCJjbGVhclRpbWVvdXQiLCJjb250cm9sbGVyIiwidmFsdWVzIiwiYWJvcnQiLCJjbGVhciIsImNvbHVtbnMiLCJyb3dzIiwiY3VycmVudEZpZWxkIiwiY3VycmVudEZpZWxkSXNUZXh0IiwiaXNFZGl0aW5nVGV4dEZpZWxkIiwic3luY1RleHRJbnB1dCIsImZpZWxkSW5kZXgiLCJmaWVsZCIsInRleHQiLCJ2YWxpZGF0ZU11bHRpU2VsZWN0IiwiZmllbGROYW1lIiwic2VsZWN0ZWQiLCJmaWVsZFJlcXVpcmVkIiwiZmluZCIsIm1pbiIsIm1pbkl0ZW1zIiwibWF4IiwibWF4SXRlbXMiLCJ1cGRhdGVWYWxpZGF0aW9uRXJyb3IiLCJoYW5kbGVOYXZpZ2F0aW9uIiwiZGlyZWN0aW9uIiwiY29tbWl0VGV4dEZpZWxkIiwidHJpbSIsInJlc29sdmVGaWVsZEFzeW5jIiwiaXRlbUNvdW50IiwiaW5kZXgiLCJuZXh0SW5kZXgiLCJzZXRGaWVsZCIsInZhbHVlIiwicHJldiIsIm5leHQiLCJ1bnNldEZpZWxkIiwidHJpbW1lZFZhbHVlIiwicmF3VmFsdWUiLCJleGlzdGluZyIsImdldCIsInNldCIsImFkZCIsInRoZW4iLCJyZXN1bHQiLCJkZWxldGUiLCJpc29UZXh0IiwiaGFuZGxlVGV4dElucHV0Q2hhbmdlIiwibmV3VmFsdWUiLCJoYW5kbGVUZXh0SW5wdXRTdWJtaXQiLCJydW5UeXBlYWhlYWQiLCJjaGFyIiwibGFiZWxzIiwib25NYXRjaCIsInRvTG93ZXJDYXNlIiwibWF0Y2giLCJmaW5kSW5kZXgiLCJsIiwic3RhcnRzV2l0aCIsImNvbnRleHQiLCJpc0FjdGl2ZSIsIl9pbnB1dCIsImtleSIsInVwQXJyb3ciLCJkb3duQXJyb3ciLCJyZXR1cm4iLCJiYWNrc3BhY2UiLCJtc1NjaGVtYSIsIm1zVmFsdWVzIiwibGVmdEFycm93IiwiZXNjYXBlIiwib3B0aW9uVmFsdWUiLCJuZXdTZWxlY3RlZCIsImZpbHRlciIsInYiLCJlbnVtU2NoZW1hIiwiZW51bVZhbHVlcyIsInZhbGlkYXRlUmVxdWlyZWQiLCJmaXJzdEJhZEluZGV4IiwicmlnaHRBcnJvdyIsImkiLCJzdGFydElkeCIsInZhbHMiLCJNYXRoIiwiaW5kZXhPZiIsIkFycmF5IiwiaXNBcnJheSIsIkxJTkVTX1BFUl9GSUVMRCIsIkRJQUxPR19PVkVSSEVBRCIsIm1heFZpc2libGVGaWVsZHMiLCJmbG9vciIsInNjcm9sbFdpbmRvdyIsInRvdGFsIiwic3RhcnQiLCJlbmQiLCJmb2N1c0lkeCIsImhhc0ZpZWxkc0Fib3ZlIiwiaGFzRmllbGRzQmVsb3ciLCJyZW5kZXJGb3JtRmllbGRzIiwiYXJyb3dVcCIsInNsaWNlIiwidmlzaWJsZUlkeCIsImhhc1ZhbHVlIiwiaXNSZXNvbHZpbmciLCJoYXMiLCJjaGVja2JveCIsIndhcm5pbmciLCJ0aWNrIiwic2VsZWN0aW9uQ29sb3IiLCJhY3RpdmVDb2xvciIsImxhYmVsIiwidGl0bGUiLCJ2YWx1ZUNvbnRlbnQiLCJhY2NvcmRpb25Db250ZW50IiwiaXNFeHBhbmRlZCIsInRyaWFuZ2xlRG93blNtYWxsIiwib3B0VmFsIiwib3B0SWR4Iiwib3B0TGFiZWwiLCJpc0NoZWNrZWQiLCJpc0ZvY3VzZWQiLCJwb2ludGVyIiwiY2hlY2tib3hPbiIsImNoZWNrYm94T2ZmIiwiYXJyb3ciLCJ0cmlhbmdsZVJpZ2h0U21hbGwiLCJkaXNwbGF5TGFiZWxzIiwiam9pbiIsImlzU2VsZWN0ZWQiLCJyYWRpb09uIiwicmFkaW9PZmYiLCJkaXNwbGF5VmFsdWUiLCJkZXNjcmlwdGlvbiIsImFycm93RG93biIsImV4aXRTdGF0ZSIsInBlbmRpbmciLCJrZXlOYW1lIiwiRWxpY2l0YXRpb25VUkxEaWFsb2ciLCJ3YWl0aW5nU3RhdGUiLCJ1cmxQYXJhbXMiLCJ1cmwiLCJwaGFzZSIsInNldFBoYXNlIiwicGhhc2VSZWYiLCJzaG93Q2FuY2VsIiwib25XYWl0aW5nRGlzbWlzc1JlZiIsImRvbWFpbiIsInVybEJlZm9yZURvbWFpbiIsInVybEFmdGVyRG9tYWluIiwicGFyc2VkIiwiVVJMIiwiaG9zdG5hbWUiLCJkb21haW5TdGFydCIsImNvbXBsZXRlZCIsImhhbmRsZUFjY2VwdCIsIkJ1dHRvbk5hbWUiLCJ3YWl0aW5nQnV0dG9ucyIsImlkeCIsImRlbHRhIiwiYWN0aW9uTGFiZWwiXSwic291cmNlcyI6WyJFbGljaXRhdGlvbkRpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUge1xuICBFbGljaXRSZXF1ZXN0Rm9ybVBhcmFtcyxcbiAgRWxpY2l0UmVxdWVzdFVSTFBhcmFtcyxcbiAgRWxpY2l0UmVzdWx0LFxuICBQcmltaXRpdmVTY2hlbWFEZWZpbml0aW9uLFxufSBmcm9tICdAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2RrL3R5cGVzLmpzJ1xuaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCBSZWFjdCwgeyB1c2VDYWxsYmFjaywgdXNlRWZmZWN0LCB1c2VNZW1vLCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VSZWdpc3Rlck92ZXJsYXkgfSBmcm9tICcuLi8uLi9jb250ZXh0L292ZXJsYXlDb250ZXh0LmpzJ1xuaW1wb3J0IHsgdXNlTm90aWZ5QWZ0ZXJUaW1lb3V0IH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlTm90aWZ5QWZ0ZXJUaW1lb3V0LmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9wcmVmZXItdXNlLWtleWJpbmRpbmdzIC0tIHJhdyB0ZXh0IGlucHV0IGZvciBlbGljaXRhdGlvbiBmb3JtXG5pbXBvcnQgeyBCb3gsIFRleHQsIHVzZUlucHV0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgdHlwZSB7IEVsaWNpdGF0aW9uUmVxdWVzdEV2ZW50IH0gZnJvbSAnLi4vLi4vc2VydmljZXMvbWNwL2VsaWNpdGF0aW9uSGFuZGxlci5qcydcbmltcG9ydCB7IG9wZW5Ccm93c2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvYnJvd3Nlci5qcydcbmltcG9ydCB7XG4gIGdldEVudW1MYWJlbCxcbiAgZ2V0RW51bVZhbHVlcyxcbiAgZ2V0TXVsdGlTZWxlY3RMYWJlbCxcbiAgZ2V0TXVsdGlTZWxlY3RWYWx1ZXMsXG4gIGlzRGF0ZVRpbWVTY2hlbWEsXG4gIGlzRW51bVNjaGVtYSxcbiAgaXNNdWx0aVNlbGVjdEVudW1TY2hlbWEsXG4gIHZhbGlkYXRlRWxpY2l0YXRpb25JbnB1dCxcbiAgdmFsaWRhdGVFbGljaXRhdGlvbklucHV0QXN5bmMsXG59IGZyb20gJy4uLy4uL3V0aWxzL21jcC9lbGljaXRhdGlvblZhbGlkYXRpb24uanMnXG5pbXBvcnQgeyBwbHVyYWwgfSBmcm9tICcuLi8uLi91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB7IENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCB9IGZyb20gJy4uL0NvbmZpZ3VyYWJsZVNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vQnlsaW5lLmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgVGV4dElucHV0IGZyb20gJy4uL1RleHRJbnB1dC5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgZXZlbnQ6IEVsaWNpdGF0aW9uUmVxdWVzdEV2ZW50XG4gIG9uUmVzcG9uc2U6IChcbiAgICBhY3Rpb246IEVsaWNpdFJlc3VsdFsnYWN0aW9uJ10sXG4gICAgY29udGVudD86IEVsaWNpdFJlc3VsdFsnY29udGVudCddLFxuICApID0+IHZvaWRcbiAgLyoqIENhbGxlZCB3aGVuIHRoZSBwaGFzZSAyIHdhaXRpbmcgc3RhdGUgaXMgZGlzbWlzc2VkIChVUkwgZWxpY2l0YXRpb25zIG9ubHkpLiAqL1xuICBvbldhaXRpbmdEaXNtaXNzPzogKGFjdGlvbjogJ2Rpc21pc3MnIHwgJ3JldHJ5JyB8ICdjYW5jZWwnKSA9PiB2b2lkXG59XG5cbmNvbnN0IGlzVGV4dEZpZWxkID0gKHM6IFByaW1pdGl2ZVNjaGVtYURlZmluaXRpb24pID0+XG4gIFsnc3RyaW5nJywgJ251bWJlcicsICdpbnRlZ2VyJ10uaW5jbHVkZXMocy50eXBlKVxuXG5jb25zdCBSRVNPTFZJTkdfU1BJTk5FUl9DSEFSUyA9XG4gICdcXHUyODBCXFx1MjgxOVxcdTI4MzlcXHUyODM4XFx1MjgzQ1xcdTI4MzRcXHUyODI2XFx1MjgyN1xcdTI4MDdcXHUyODBGJ1xuY29uc3QgYWR2YW5jZVNwaW5uZXJGcmFtZSA9IChmOiBudW1iZXIpID0+XG4gIChmICsgMSkgJSBSRVNPTFZJTkdfU1BJTk5FUl9DSEFSUy5sZW5ndGhcblxuLyoqIFRpbWVyIGNhbGxiYWNrIGZvciBlbnVtVHlwZWFoZWFkUmVmIOKAlCBtb2R1bGUtc2NvcGUgdG8gYXZvaWQgY2xvc3VyZSBjYXB0dXJlLiAqL1xuZnVuY3Rpb24gcmVzZXRUeXBlYWhlYWQodGE6IHtcbiAgYnVmZmVyOiBzdHJpbmdcbiAgdGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgdW5kZWZpbmVkXG59KTogdm9pZCB7XG4gIHRhLmJ1ZmZlciA9ICcnXG4gIHRhLnRpbWVyID0gdW5kZWZpbmVkXG59XG5cbi8qKlxuICogSXNvbGF0ZWQgc3Bpbm5lciBnbHlwaCBmb3IgYSBmaWVsZCB0aGF0IGlzIGJlaW5nIHJlc29sdmVkIGFzeW5jaHJvbm91c2x5LlxuICogT3ducyBpdHMgb3duIDgwbXMgYW5pbWF0aW9uIHRpbWVyIHNvIHRpY2tzIG9ubHkgcmUtcmVuZGVyIHRoaXMgdGlueSBsZWFmLFxuICogbm90IHRoZSBlbnRpcmUgRWxpY2l0YXRpb25Gb3JtRGlhbG9nICh+MTIwMCBsaW5lcyArIHJlbmRlckZvcm1GaWVsZHMpLlxuICogTW91bnRlZC91bm1vdW50ZWQgYnkgdGhlIHBhcmVudCB2aWEgdGhlIGBpc1Jlc29sdmluZ2AgY29uZGl0aW9uLlxuICpcbiAqIE5vdCB1c2luZyB0aGUgc2hhcmVkIDxTcGlubmVyIC8+IGZyb20gLi4vU3Bpbm5lci5qczogdGhhdCBvbmUgcmVuZGVycyBpbiBhXG4gKiA8Qm94IHdpZHRoPXsyfT4gd2l0aCBjb2xvcj1cInRleHRcIiwgd2hpY2ggd291bGQgYnJlYWsgdGhlIDEtY29sIGNoZWNrYm94XG4gKiBjb2x1bW4gYWxpZ25tZW50IGhlcmUgKG90aGVyIGNoZWNrYm94IHN0YXRlcyBhcmUgd2lkdGgtMSBnbHlwaHMpLlxuICovXG5mdW5jdGlvbiBSZXNvbHZpbmdTcGlubmVyKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtmcmFtZSwgc2V0RnJhbWVdID0gdXNlU3RhdGUoMClcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBjb25zdCB0aW1lciA9IHNldEludGVydmFsKHNldEZyYW1lLCA4MCwgYWR2YW5jZVNwaW5uZXJGcmFtZSlcbiAgICByZXR1cm4gKCkgPT4gY2xlYXJJbnRlcnZhbCh0aW1lcilcbiAgfSwgW10pXG4gIHJldHVybiA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj57UkVTT0xWSU5HX1NQSU5ORVJfQ0hBUlNbZnJhbWVdfTwvVGV4dD5cbn1cblxuLyoqIEZvcm1hdCBhbiBJU08gZGF0ZS9kYXRldGltZSBmb3IgZGlzcGxheSwga2VlcGluZyB0aGUgSVNPIHZhbHVlIGZvciBzdWJtaXNzaW9uLiAqL1xuZnVuY3Rpb24gZm9ybWF0RGF0ZURpc3BsYXkoXG4gIGlzb1ZhbHVlOiBzdHJpbmcsXG4gIHNjaGVtYTogUHJpbWl0aXZlU2NoZW1hRGVmaW5pdGlvbixcbik6IHN0cmluZyB7XG4gIHRyeSB7XG4gICAgY29uc3QgZGF0ZSA9IG5ldyBEYXRlKGlzb1ZhbHVlKVxuICAgIGlmIChOdW1iZXIuaXNOYU4oZGF0ZS5nZXRUaW1lKCkpKSByZXR1cm4gaXNvVmFsdWVcbiAgICBjb25zdCBmb3JtYXQgPSAnZm9ybWF0JyBpbiBzY2hlbWEgPyBzY2hlbWEuZm9ybWF0IDogdW5kZWZpbmVkXG4gICAgaWYgKGZvcm1hdCA9PT0gJ2RhdGUtdGltZScpIHtcbiAgICAgIHJldHVybiBkYXRlLnRvTG9jYWxlRGF0ZVN0cmluZygnZW4tVVMnLCB7XG4gICAgICAgIHdlZWtkYXk6ICdzaG9ydCcsXG4gICAgICAgIHllYXI6ICdudW1lcmljJyxcbiAgICAgICAgbW9udGg6ICdzaG9ydCcsXG4gICAgICAgIGRheTogJ251bWVyaWMnLFxuICAgICAgICBob3VyOiAnbnVtZXJpYycsXG4gICAgICAgIG1pbnV0ZTogJzItZGlnaXQnLFxuICAgICAgICB0aW1lWm9uZU5hbWU6ICdzaG9ydCcsXG4gICAgICB9KVxuICAgIH1cbiAgICAvLyBkYXRlLW9ubHk6IHBhcnNlIGFzIGxvY2FsIGRhdGUgdG8gYXZvaWQgdGltZXpvbmUgc2hpZnRcbiAgICBjb25zdCBwYXJ0cyA9IGlzb1ZhbHVlLnNwbGl0KCctJylcbiAgICBpZiAocGFydHMubGVuZ3RoID09PSAzKSB7XG4gICAgICBjb25zdCBsb2NhbCA9IG5ldyBEYXRlKFxuICAgICAgICBOdW1iZXIocGFydHNbMF0pLFxuICAgICAgICBOdW1iZXIocGFydHNbMV0pIC0gMSxcbiAgICAgICAgTnVtYmVyKHBhcnRzWzJdKSxcbiAgICAgIClcbiAgICAgIHJldHVybiBsb2NhbC50b0xvY2FsZURhdGVTdHJpbmcoJ2VuLVVTJywge1xuICAgICAgICB3ZWVrZGF5OiAnc2hvcnQnLFxuICAgICAgICB5ZWFyOiAnbnVtZXJpYycsXG4gICAgICAgIG1vbnRoOiAnc2hvcnQnLFxuICAgICAgICBkYXk6ICdudW1lcmljJyxcbiAgICAgIH0pXG4gICAgfVxuICAgIHJldHVybiBpc29WYWx1ZVxuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gaXNvVmFsdWVcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gRWxpY2l0YXRpb25EaWFsb2coe1xuICBldmVudCxcbiAgb25SZXNwb25zZSxcbiAgb25XYWl0aW5nRGlzbWlzcyxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKGV2ZW50LnBhcmFtcy5tb2RlID09PSAndXJsJykge1xuICAgIHJldHVybiAoXG4gICAgICA8RWxpY2l0YXRpb25VUkxEaWFsb2dcbiAgICAgICAgZXZlbnQ9e2V2ZW50fVxuICAgICAgICBvblJlc3BvbnNlPXtvblJlc3BvbnNlfVxuICAgICAgICBvbldhaXRpbmdEaXNtaXNzPXtvbldhaXRpbmdEaXNtaXNzfVxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICByZXR1cm4gPEVsaWNpdGF0aW9uRm9ybURpYWxvZyBldmVudD17ZXZlbnR9IG9uUmVzcG9uc2U9e29uUmVzcG9uc2V9IC8+XG59XG5cbmZ1bmN0aW9uIEVsaWNpdGF0aW9uRm9ybURpYWxvZyh7XG4gIGV2ZW50LFxuICBvblJlc3BvbnNlLFxufToge1xuICBldmVudDogRWxpY2l0YXRpb25SZXF1ZXN0RXZlbnRcbiAgb25SZXNwb25zZTogUHJvcHNbJ29uUmVzcG9uc2UnXVxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgc2VydmVyTmFtZSwgc2lnbmFsIH0gPSBldmVudFxuICBjb25zdCByZXF1ZXN0ID0gZXZlbnQucGFyYW1zIGFzIEVsaWNpdFJlcXVlc3RGb3JtUGFyYW1zXG4gIGNvbnN0IHsgbWVzc2FnZSwgcmVxdWVzdGVkU2NoZW1hIH0gPSByZXF1ZXN0XG4gIGNvbnN0IGhhc0ZpZWxkcyA9IE9iamVjdC5rZXlzKHJlcXVlc3RlZFNjaGVtYS5wcm9wZXJ0aWVzKS5sZW5ndGggPiAwXG4gIGNvbnN0IFtmb2N1c2VkQnV0dG9uLCBzZXRGb2N1c2VkQnV0dG9uXSA9IHVzZVN0YXRlPFxuICAgICdhY2NlcHQnIHwgJ2RlY2xpbmUnIHwgbnVsbFxuICA+KGhhc0ZpZWxkcyA/IG51bGwgOiAnYWNjZXB0JylcbiAgY29uc3QgW2Zvcm1WYWx1ZXMsIHNldEZvcm1WYWx1ZXNdID0gdXNlU3RhdGU8XG4gICAgUmVjb3JkPHN0cmluZywgc3RyaW5nIHwgbnVtYmVyIHwgYm9vbGVhbiB8IHN0cmluZ1tdPlxuICA+KCgpID0+IHtcbiAgICBjb25zdCBpbml0aWFsVmFsdWVzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmcgfCBudW1iZXIgfCBib29sZWFuIHwgc3RyaW5nW10+ID1cbiAgICAgIHt9XG4gICAgaWYgKHJlcXVlc3RlZFNjaGVtYS5wcm9wZXJ0aWVzKSB7XG4gICAgICBmb3IgKGNvbnN0IFtwcm9wTmFtZSwgcHJvcFNjaGVtYV0gb2YgT2JqZWN0LmVudHJpZXMoXG4gICAgICAgIHJlcXVlc3RlZFNjaGVtYS5wcm9wZXJ0aWVzLFxuICAgICAgKSkge1xuICAgICAgICBpZiAodHlwZW9mIHByb3BTY2hlbWEgPT09ICdvYmplY3QnICYmIHByb3BTY2hlbWEgIT09IG51bGwpIHtcbiAgICAgICAgICBpZiAocHJvcFNjaGVtYS5kZWZhdWx0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGluaXRpYWxWYWx1ZXNbcHJvcE5hbWVdID0gcHJvcFNjaGVtYS5kZWZhdWx0XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBpbml0aWFsVmFsdWVzXG4gIH0pXG5cbiAgY29uc3QgW3ZhbGlkYXRpb25FcnJvcnMsIHNldFZhbGlkYXRpb25FcnJvcnNdID0gdXNlU3RhdGU8XG4gICAgUmVjb3JkPHN0cmluZywgc3RyaW5nPlxuICA+KCgpID0+IHtcbiAgICBjb25zdCBpbml0aWFsRXJyb3JzOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge31cbiAgICBmb3IgKGNvbnN0IFtwcm9wTmFtZSwgcHJvcFNjaGVtYV0gb2YgT2JqZWN0LmVudHJpZXMoXG4gICAgICByZXF1ZXN0ZWRTY2hlbWEucHJvcGVydGllcyxcbiAgICApKSB7XG4gICAgICBpZiAoaXNUZXh0RmllbGQocHJvcFNjaGVtYSkgJiYgcHJvcFNjaGVtYT8uZGVmYXVsdCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNvbnN0IHZhbGlkYXRpb24gPSB2YWxpZGF0ZUVsaWNpdGF0aW9uSW5wdXQoXG4gICAgICAgICAgU3RyaW5nKHByb3BTY2hlbWEuZGVmYXVsdCksXG4gICAgICAgICAgcHJvcFNjaGVtYSxcbiAgICAgICAgKVxuICAgICAgICBpZiAoIXZhbGlkYXRpb24uaXNWYWxpZCAmJiB2YWxpZGF0aW9uLmVycm9yKSB7XG4gICAgICAgICAgaW5pdGlhbEVycm9yc1twcm9wTmFtZV0gPSB2YWxpZGF0aW9uLmVycm9yXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGluaXRpYWxFcnJvcnNcbiAgfSlcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghc2lnbmFsKSByZXR1cm5cblxuICAgIGNvbnN0IGhhbmRsZUFib3J0ID0gKCkgPT4ge1xuICAgICAgb25SZXNwb25zZSgnY2FuY2VsJylcbiAgICB9XG5cbiAgICBpZiAoc2lnbmFsLmFib3J0ZWQpIHtcbiAgICAgIGhhbmRsZUFib3J0KClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKCdhYm9ydCcsIGhhbmRsZUFib3J0KVxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBzaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcignYWJvcnQnLCBoYW5kbGVBYm9ydClcbiAgICB9XG4gIH0sIFtzaWduYWwsIG9uUmVzcG9uc2VdKVxuXG4gIGNvbnN0IHNjaGVtYUZpZWxkcyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGNvbnN0IHJlcXVpcmVkRmllbGRzID0gcmVxdWVzdGVkU2NoZW1hLnJlcXVpcmVkID8/IFtdXG4gICAgcmV0dXJuIE9iamVjdC5lbnRyaWVzKHJlcXVlc3RlZFNjaGVtYS5wcm9wZXJ0aWVzKS5tYXAoKFtuYW1lLCBzY2hlbWFdKSA9PiAoe1xuICAgICAgbmFtZSxcbiAgICAgIHNjaGVtYSxcbiAgICAgIGlzUmVxdWlyZWQ6IHJlcXVpcmVkRmllbGRzLmluY2x1ZGVzKG5hbWUpLFxuICAgIH0pKVxuICB9LCBbcmVxdWVzdGVkU2NoZW1hXSlcblxuICBjb25zdCBbY3VycmVudEZpZWxkSW5kZXgsIHNldEN1cnJlbnRGaWVsZEluZGV4XSA9IHVzZVN0YXRlPFxuICAgIG51bWJlciB8IHVuZGVmaW5lZFxuICA+KGhhc0ZpZWxkcyA/IDAgOiB1bmRlZmluZWQpXG4gIGNvbnN0IFt0ZXh0SW5wdXRWYWx1ZSwgc2V0VGV4dElucHV0VmFsdWVdID0gdXNlU3RhdGUoKCkgPT4ge1xuICAgIC8vIEluaXRpYWxpemUgZnJvbSB0aGUgZmlyc3QgZmllbGQncyB2YWx1ZSBpZiBpdCdzIGEgdGV4dCBmaWVsZFxuICAgIGNvbnN0IGZpcnN0RmllbGQgPSBzY2hlbWFGaWVsZHNbMF1cbiAgICBpZiAoZmlyc3RGaWVsZCAmJiBpc1RleHRGaWVsZChmaXJzdEZpZWxkLnNjaGVtYSkpIHtcbiAgICAgIGNvbnN0IHZhbCA9IGZvcm1WYWx1ZXNbZmlyc3RGaWVsZC5uYW1lXVxuICAgICAgaWYgKHZhbCA9PT0gdW5kZWZpbmVkKSByZXR1cm4gJydcbiAgICAgIHJldHVybiBTdHJpbmcodmFsKVxuICAgIH1cbiAgICByZXR1cm4gJydcbiAgfSlcbiAgY29uc3QgW3RleHRJbnB1dEN1cnNvck9mZnNldCwgc2V0VGV4dElucHV0Q3Vyc29yT2Zmc2V0XSA9IHVzZVN0YXRlKFxuICAgIHRleHRJbnB1dFZhbHVlLmxlbmd0aCxcbiAgKVxuICBjb25zdCBbcmVzb2x2aW5nRmllbGRzLCBzZXRSZXNvbHZpbmdGaWVsZHNdID0gdXNlU3RhdGU8U2V0PHN0cmluZz4+KFxuICAgICgpID0+IG5ldyBTZXQoKSxcbiAgKVxuICAvLyBBY2NvcmRpb24gc3RhdGUgKHNoYXJlZCBieSBtdWx0aS1zZWxlY3QgYW5kIHNpbmdsZS1zZWxlY3QgZW51bSlcbiAgY29uc3QgW2V4cGFuZGVkQWNjb3JkaW9uLCBzZXRFeHBhbmRlZEFjY29yZGlvbl0gPSB1c2VTdGF0ZTxcbiAgICBzdHJpbmcgfCB1bmRlZmluZWRcbiAgPigpXG4gIGNvbnN0IFthY2NvcmRpb25PcHRpb25JbmRleCwgc2V0QWNjb3JkaW9uT3B0aW9uSW5kZXhdID0gdXNlU3RhdGUoMClcblxuICBjb25zdCBkYXRlRGVib3VuY2VSZWYgPSB1c2VSZWY8UmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCB1bmRlZmluZWQ+KFxuICAgIHVuZGVmaW5lZCxcbiAgKVxuICBjb25zdCByZXNvbHZlQWJvcnRSZWYgPSB1c2VSZWY8TWFwPHN0cmluZywgQWJvcnRDb250cm9sbGVyPj4obmV3IE1hcCgpKVxuICBjb25zdCBlbnVtVHlwZWFoZWFkUmVmID0gdXNlUmVmKHtcbiAgICBidWZmZXI6ICcnLFxuICAgIHRpbWVyOiB1bmRlZmluZWQgYXMgUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCB1bmRlZmluZWQsXG4gIH0pXG5cbiAgLy8gQ2xlYXIgcGVuZGluZyBkZWJvdW5jZS90eXBlYWhlYWQgdGltZXJzIGFuZCBhYm9ydCBpbi1mbGlnaHQgYXN5bmNcbiAgLy8gdmFsaWRhdGlvbnMgb24gdW5tb3VudCBzbyB0aGV5IGRvbid0IGZpcmUgYWdhaW5zdCBhbiB1bm1vdW50ZWQgY29tcG9uZW50XG4gIC8vIChlLmcuIGRpYWxvZyBkaXNtaXNzZWQgbWlkLWRlYm91bmNlIG9yIG1pZC1yZXNvbHZlKS5cbiAgdXNlRWZmZWN0KFxuICAgICgpID0+ICgpID0+IHtcbiAgICAgIGlmIChkYXRlRGVib3VuY2VSZWYuY3VycmVudCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNsZWFyVGltZW91dChkYXRlRGVib3VuY2VSZWYuY3VycmVudClcbiAgICAgIH1cbiAgICAgIGNvbnN0IHRhID0gZW51bVR5cGVhaGVhZFJlZi5jdXJyZW50XG4gICAgICBpZiAodGEudGltZXIgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjbGVhclRpbWVvdXQodGEudGltZXIpXG4gICAgICB9XG4gICAgICBmb3IgKGNvbnN0IGNvbnRyb2xsZXIgb2YgcmVzb2x2ZUFib3J0UmVmLmN1cnJlbnQudmFsdWVzKCkpIHtcbiAgICAgICAgY29udHJvbGxlci5hYm9ydCgpXG4gICAgICB9XG4gICAgICByZXNvbHZlQWJvcnRSZWYuY3VycmVudC5jbGVhcigpXG4gICAgfSxcbiAgICBbXSxcbiAgKVxuXG4gIGNvbnN0IHsgY29sdW1ucywgcm93cyB9ID0gdXNlVGVybWluYWxTaXplKClcblxuICBjb25zdCBjdXJyZW50RmllbGQgPVxuICAgIGN1cnJlbnRGaWVsZEluZGV4ICE9PSB1bmRlZmluZWRcbiAgICAgID8gc2NoZW1hRmllbGRzW2N1cnJlbnRGaWVsZEluZGV4XVxuICAgICAgOiB1bmRlZmluZWRcbiAgY29uc3QgY3VycmVudEZpZWxkSXNUZXh0ID1cbiAgICBjdXJyZW50RmllbGQgIT09IHVuZGVmaW5lZCAmJlxuICAgIGlzVGV4dEZpZWxkKGN1cnJlbnRGaWVsZC5zY2hlbWEpICYmXG4gICAgIWlzRW51bVNjaGVtYShjdXJyZW50RmllbGQuc2NoZW1hKVxuXG4gIC8vIFRleHQgZmllbGRzIGFyZSBhbHdheXMgaW4gZWRpdCBtb2RlIHdoZW4gZm9jdXNlZCDigJQgbm8gRW50ZXItdG8tZWRpdCBzdGVwLlxuICBjb25zdCBpc0VkaXRpbmdUZXh0RmllbGQgPSBjdXJyZW50RmllbGRJc1RleHQgJiYgIWZvY3VzZWRCdXR0b25cblxuICB1c2VSZWdpc3Rlck92ZXJsYXkoJ2VsaWNpdGF0aW9uJylcbiAgdXNlTm90aWZ5QWZ0ZXJUaW1lb3V0KCdDbGF1ZGUgQ29kZSBuZWVkcyB5b3VyIGlucHV0JywgJ2VsaWNpdGF0aW9uX2RpYWxvZycpXG5cbiAgLy8gU3luYyB0ZXh0SW5wdXRWYWx1ZSB3aGVuIHRoZSBmb2N1c2VkIGZpZWxkIGNoYW5nZXNcbiAgY29uc3Qgc3luY1RleHRJbnB1dCA9IHVzZUNhbGxiYWNrKFxuICAgIChmaWVsZEluZGV4OiBudW1iZXIgfCB1bmRlZmluZWQpID0+IHtcbiAgICAgIGlmIChmaWVsZEluZGV4ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgc2V0VGV4dElucHV0VmFsdWUoJycpXG4gICAgICAgIHNldFRleHRJbnB1dEN1cnNvck9mZnNldCgwKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIGNvbnN0IGZpZWxkID0gc2NoZW1hRmllbGRzW2ZpZWxkSW5kZXhdXG4gICAgICBpZiAoZmllbGQgJiYgaXNUZXh0RmllbGQoZmllbGQuc2NoZW1hKSAmJiAhaXNFbnVtU2NoZW1hKGZpZWxkLnNjaGVtYSkpIHtcbiAgICAgICAgY29uc3QgdmFsID0gZm9ybVZhbHVlc1tmaWVsZC5uYW1lXVxuICAgICAgICBjb25zdCB0ZXh0ID0gdmFsICE9PSB1bmRlZmluZWQgPyBTdHJpbmcodmFsKSA6ICcnXG4gICAgICAgIHNldFRleHRJbnB1dFZhbHVlKHRleHQpXG4gICAgICAgIHNldFRleHRJbnB1dEN1cnNvck9mZnNldCh0ZXh0Lmxlbmd0aClcbiAgICAgIH1cbiAgICB9LFxuICAgIFtzY2hlbWFGaWVsZHMsIGZvcm1WYWx1ZXNdLFxuICApXG5cbiAgZnVuY3Rpb24gdmFsaWRhdGVNdWx0aVNlbGVjdChcbiAgICBmaWVsZE5hbWU6IHN0cmluZyxcbiAgICBzY2hlbWE6IFByaW1pdGl2ZVNjaGVtYURlZmluaXRpb24sXG4gICkge1xuICAgIGlmICghaXNNdWx0aVNlbGVjdEVudW1TY2hlbWEoc2NoZW1hKSkgcmV0dXJuXG4gICAgY29uc3Qgc2VsZWN0ZWQgPSAoZm9ybVZhbHVlc1tmaWVsZE5hbWVdIGFzIHN0cmluZ1tdIHwgdW5kZWZpbmVkKSA/PyBbXVxuICAgIGNvbnN0IGZpZWxkUmVxdWlyZWQgPVxuICAgICAgc2NoZW1hRmllbGRzLmZpbmQoZiA9PiBmLm5hbWUgPT09IGZpZWxkTmFtZSk/LmlzUmVxdWlyZWQgPz8gZmFsc2VcbiAgICBjb25zdCBtaW4gPSBzY2hlbWEubWluSXRlbXNcbiAgICBjb25zdCBtYXggPSBzY2hlbWEubWF4SXRlbXNcbiAgICAvLyBTa2lwIG1pbkl0ZW1zIGNoZWNrIHdoZW4gZmllbGQgaXMgb3B0aW9uYWwgYW5kIHVuc2V0XG4gICAgaWYgKFxuICAgICAgbWluICE9PSB1bmRlZmluZWQgJiZcbiAgICAgIHNlbGVjdGVkLmxlbmd0aCA8IG1pbiAmJlxuICAgICAgKHNlbGVjdGVkLmxlbmd0aCA+IDAgfHwgZmllbGRSZXF1aXJlZClcbiAgICApIHtcbiAgICAgIHVwZGF0ZVZhbGlkYXRpb25FcnJvcihcbiAgICAgICAgZmllbGROYW1lLFxuICAgICAgICBgU2VsZWN0IGF0IGxlYXN0ICR7bWlufSAke3BsdXJhbChtaW4sICdpdGVtJyl9YCxcbiAgICAgIClcbiAgICB9IGVsc2UgaWYgKG1heCAhPT0gdW5kZWZpbmVkICYmIHNlbGVjdGVkLmxlbmd0aCA+IG1heCkge1xuICAgICAgdXBkYXRlVmFsaWRhdGlvbkVycm9yKFxuICAgICAgICBmaWVsZE5hbWUsXG4gICAgICAgIGBTZWxlY3QgYXQgbW9zdCAke21heH0gJHtwbHVyYWwobWF4LCAnaXRlbScpfWAsXG4gICAgICApXG4gICAgfSBlbHNlIHtcbiAgICAgIHVwZGF0ZVZhbGlkYXRpb25FcnJvcihmaWVsZE5hbWUpXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gaGFuZGxlTmF2aWdhdGlvbihkaXJlY3Rpb246ICd1cCcgfCAnZG93bicpOiB2b2lkIHtcbiAgICAvLyBDb2xsYXBzZSBhY2NvcmRpb24gYW5kIHZhbGlkYXRlIG9uIG5hdmlnYXRlIGF3YXlcbiAgICBpZiAoY3VycmVudEZpZWxkICYmIGlzTXVsdGlTZWxlY3RFbnVtU2NoZW1hKGN1cnJlbnRGaWVsZC5zY2hlbWEpKSB7XG4gICAgICB2YWxpZGF0ZU11bHRpU2VsZWN0KGN1cnJlbnRGaWVsZC5uYW1lLCBjdXJyZW50RmllbGQuc2NoZW1hKVxuICAgICAgc2V0RXhwYW5kZWRBY2NvcmRpb24odW5kZWZpbmVkKVxuICAgIH0gZWxzZSBpZiAoY3VycmVudEZpZWxkICYmIGlzRW51bVNjaGVtYShjdXJyZW50RmllbGQuc2NoZW1hKSkge1xuICAgICAgc2V0RXhwYW5kZWRBY2NvcmRpb24odW5kZWZpbmVkKVxuICAgIH1cblxuICAgIC8vIENvbW1pdCBjdXJyZW50IHRleHQgZmllbGQgYmVmb3JlIG5hdmlnYXRpbmcgYXdheVxuICAgIGlmIChpc0VkaXRpbmdUZXh0RmllbGQgJiYgY3VycmVudEZpZWxkKSB7XG4gICAgICBjb21taXRUZXh0RmllbGQoY3VycmVudEZpZWxkLm5hbWUsIGN1cnJlbnRGaWVsZC5zY2hlbWEsIHRleHRJbnB1dFZhbHVlKVxuXG4gICAgICAvLyBDYW5jZWwgYW55IHBlbmRpbmcgZGVib3VuY2Ug4oCUIHdlJ3JlIHJlc29sdmluZyBub3cgb24gbmF2aWdhdGUtYXdheVxuICAgICAgaWYgKGRhdGVEZWJvdW5jZVJlZi5jdXJyZW50ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KGRhdGVEZWJvdW5jZVJlZi5jdXJyZW50KVxuICAgICAgICBkYXRlRGVib3VuY2VSZWYuY3VycmVudCA9IHVuZGVmaW5lZFxuICAgICAgfVxuXG4gICAgICAvLyBGb3IgZGF0ZS9kYXRldGltZSBmaWVsZHMgdGhhdCBmYWlsZWQgc3luYyB2YWxpZGF0aW9uLCB0cnkgYXN5bmMgTkwgcGFyc2luZ1xuICAgICAgaWYgKFxuICAgICAgICBpc0RhdGVUaW1lU2NoZW1hKGN1cnJlbnRGaWVsZC5zY2hlbWEpICYmXG4gICAgICAgIHRleHRJbnB1dFZhbHVlLnRyaW0oKSAhPT0gJycgJiZcbiAgICAgICAgdmFsaWRhdGlvbkVycm9yc1tjdXJyZW50RmllbGQubmFtZV1cbiAgICAgICkge1xuICAgICAgICByZXNvbHZlRmllbGRBc3luYyhcbiAgICAgICAgICBjdXJyZW50RmllbGQubmFtZSxcbiAgICAgICAgICBjdXJyZW50RmllbGQuc2NoZW1hLFxuICAgICAgICAgIHRleHRJbnB1dFZhbHVlLFxuICAgICAgICApXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRmllbGRzICsgYWNjZXB0ICsgZGVjbGluZVxuICAgIGNvbnN0IGl0ZW1Db3VudCA9IHNjaGVtYUZpZWxkcy5sZW5ndGggKyAyXG4gICAgY29uc3QgaW5kZXggPVxuICAgICAgY3VycmVudEZpZWxkSW5kZXggPz9cbiAgICAgIChmb2N1c2VkQnV0dG9uID09PSAnYWNjZXB0J1xuICAgICAgICA/IHNjaGVtYUZpZWxkcy5sZW5ndGhcbiAgICAgICAgOiBmb2N1c2VkQnV0dG9uID09PSAnZGVjbGluZSdcbiAgICAgICAgICA/IHNjaGVtYUZpZWxkcy5sZW5ndGggKyAxXG4gICAgICAgICAgOiB1bmRlZmluZWQpXG4gICAgY29uc3QgbmV4dEluZGV4ID1cbiAgICAgIGluZGV4ICE9PSB1bmRlZmluZWRcbiAgICAgICAgPyAoaW5kZXggKyAoZGlyZWN0aW9uID09PSAndXAnID8gaXRlbUNvdW50IC0gMSA6IDEpKSAlIGl0ZW1Db3VudFxuICAgICAgICA6IDBcbiAgICBpZiAobmV4dEluZGV4IDwgc2NoZW1hRmllbGRzLmxlbmd0aCkge1xuICAgICAgc2V0Q3VycmVudEZpZWxkSW5kZXgobmV4dEluZGV4KVxuICAgICAgc2V0Rm9jdXNlZEJ1dHRvbihudWxsKVxuICAgICAgc3luY1RleHRJbnB1dChuZXh0SW5kZXgpXG4gICAgfSBlbHNlIHtcbiAgICAgIHNldEN1cnJlbnRGaWVsZEluZGV4KHVuZGVmaW5lZClcbiAgICAgIHNldEZvY3VzZWRCdXR0b24obmV4dEluZGV4ID09PSBzY2hlbWFGaWVsZHMubGVuZ3RoID8gJ2FjY2VwdCcgOiAnZGVjbGluZScpXG4gICAgICBzZXRUZXh0SW5wdXRWYWx1ZSgnJylcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBzZXRGaWVsZChcbiAgICBmaWVsZE5hbWU6IHN0cmluZyxcbiAgICB2YWx1ZTogbnVtYmVyIHwgc3RyaW5nIHwgYm9vbGVhbiB8IHN0cmluZ1tdIHwgdW5kZWZpbmVkLFxuICApIHtcbiAgICBzZXRGb3JtVmFsdWVzKHByZXYgPT4ge1xuICAgICAgY29uc3QgbmV4dCA9IHsgLi4ucHJldiB9XG4gICAgICBpZiAodmFsdWUgPT09IHVuZGVmaW5lZCkge1xuICAgICAgICBkZWxldGUgbmV4dFtmaWVsZE5hbWVdXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBuZXh0W2ZpZWxkTmFtZV0gPSB2YWx1ZVxuICAgICAgfVxuICAgICAgcmV0dXJuIG5leHRcbiAgICB9KVxuICAgIC8vIENsZWFyIFwicmVxdWlyZWRcIiBlcnJvciB3aGVuIGEgdmFsdWUgaXMgcHJvdmlkZWRcbiAgICBpZiAoXG4gICAgICB2YWx1ZSAhPT0gdW5kZWZpbmVkICYmXG4gICAgICB2YWxpZGF0aW9uRXJyb3JzW2ZpZWxkTmFtZV0gPT09ICdUaGlzIGZpZWxkIGlzIHJlcXVpcmVkJ1xuICAgICkge1xuICAgICAgdXBkYXRlVmFsaWRhdGlvbkVycm9yKGZpZWxkTmFtZSlcbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiB1cGRhdGVWYWxpZGF0aW9uRXJyb3IoZmllbGROYW1lOiBzdHJpbmcsIGVycm9yPzogc3RyaW5nKSB7XG4gICAgc2V0VmFsaWRhdGlvbkVycm9ycyhwcmV2ID0+IHtcbiAgICAgIGNvbnN0IG5leHQgPSB7IC4uLnByZXYgfVxuICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgIG5leHRbZmllbGROYW1lXSA9IGVycm9yXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkZWxldGUgbmV4dFtmaWVsZE5hbWVdXG4gICAgICB9XG4gICAgICByZXR1cm4gbmV4dFxuICAgIH0pXG4gIH1cblxuICBmdW5jdGlvbiB1bnNldEZpZWxkKGZpZWxkTmFtZTogc3RyaW5nKSB7XG4gICAgaWYgKCFmaWVsZE5hbWUpIHJldHVyblxuICAgIHNldEZpZWxkKGZpZWxkTmFtZSwgdW5kZWZpbmVkKVxuICAgIHVwZGF0ZVZhbGlkYXRpb25FcnJvcihmaWVsZE5hbWUpXG4gICAgc2V0VGV4dElucHV0VmFsdWUoJycpXG4gICAgc2V0VGV4dElucHV0Q3Vyc29yT2Zmc2V0KDApXG4gIH1cblxuICBmdW5jdGlvbiBjb21taXRUZXh0RmllbGQoXG4gICAgZmllbGROYW1lOiBzdHJpbmcsXG4gICAgc2NoZW1hOiBQcmltaXRpdmVTY2hlbWFEZWZpbml0aW9uLFxuICAgIHZhbHVlOiBzdHJpbmcsXG4gICkge1xuICAgIGNvbnN0IHRyaW1tZWRWYWx1ZSA9IHZhbHVlLnRyaW0oKVxuXG4gICAgLy8gRW1wdHkgaW5wdXQgZm9yIG5vbi1wbGFpbi1zdHJpbmcgdHlwZXMgbWVhbnMgdW5zZXRcbiAgICBpZiAoXG4gICAgICB0cmltbWVkVmFsdWUgPT09ICcnICYmXG4gICAgICAoc2NoZW1hLnR5cGUgIT09ICdzdHJpbmcnIHx8XG4gICAgICAgICgnZm9ybWF0JyBpbiBzY2hlbWEgJiYgc2NoZW1hLmZvcm1hdCAhPT0gdW5kZWZpbmVkKSlcbiAgICApIHtcbiAgICAgIHVuc2V0RmllbGQoZmllbGROYW1lKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgaWYgKHRyaW1tZWRWYWx1ZSA9PT0gJycpIHtcbiAgICAgIC8vIEVtcHR5IHBsYWluIHN0cmluZyDigJQga2VlcCBvciB1bnNldCBkZXBlbmRpbmcgb24gd2hldGhlciBpdCB3YXMgc2V0XG4gICAgICBpZiAoZm9ybVZhbHVlc1tmaWVsZE5hbWVdICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgc2V0RmllbGQoZmllbGROYW1lLCAnJylcbiAgICAgIH1cbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IHZhbGlkYXRpb24gPSB2YWxpZGF0ZUVsaWNpdGF0aW9uSW5wdXQodmFsdWUsIHNjaGVtYSlcbiAgICBzZXRGaWVsZChmaWVsZE5hbWUsIHZhbGlkYXRpb24uaXNWYWxpZCA/IHZhbGlkYXRpb24udmFsdWUgOiB2YWx1ZSlcbiAgICB1cGRhdGVWYWxpZGF0aW9uRXJyb3IoXG4gICAgICBmaWVsZE5hbWUsXG4gICAgICB2YWxpZGF0aW9uLmlzVmFsaWQgPyB1bmRlZmluZWQgOiB2YWxpZGF0aW9uLmVycm9yLFxuICAgIClcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlc29sdmVGaWVsZEFzeW5jKFxuICAgIGZpZWxkTmFtZTogc3RyaW5nLFxuICAgIHNjaGVtYTogUHJpbWl0aXZlU2NoZW1hRGVmaW5pdGlvbixcbiAgICByYXdWYWx1ZTogc3RyaW5nLFxuICApIHtcbiAgICBpZiAoIXNpZ25hbCkgcmV0dXJuXG5cbiAgICAvLyBBYm9ydCBhbnkgZXhpc3RpbmcgcmVzb2x1dGlvbiBmb3IgdGhpcyBmaWVsZFxuICAgIGNvbnN0IGV4aXN0aW5nID0gcmVzb2x2ZUFib3J0UmVmLmN1cnJlbnQuZ2V0KGZpZWxkTmFtZSlcbiAgICBpZiAoZXhpc3RpbmcpIHtcbiAgICAgIGV4aXN0aW5nLmFib3J0KClcbiAgICB9XG5cbiAgICBjb25zdCBjb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpXG4gICAgcmVzb2x2ZUFib3J0UmVmLmN1cnJlbnQuc2V0KGZpZWxkTmFtZSwgY29udHJvbGxlcilcblxuICAgIHNldFJlc29sdmluZ0ZpZWxkcyhwcmV2ID0+IG5ldyBTZXQocHJldikuYWRkKGZpZWxkTmFtZSkpXG5cbiAgICB2b2lkIHZhbGlkYXRlRWxpY2l0YXRpb25JbnB1dEFzeW5jKFxuICAgICAgcmF3VmFsdWUsXG4gICAgICBzY2hlbWEsXG4gICAgICBjb250cm9sbGVyLnNpZ25hbCxcbiAgICApLnRoZW4oXG4gICAgICByZXN1bHQgPT4ge1xuICAgICAgICByZXNvbHZlQWJvcnRSZWYuY3VycmVudC5kZWxldGUoZmllbGROYW1lKVxuICAgICAgICBzZXRSZXNvbHZpbmdGaWVsZHMocHJldiA9PiB7XG4gICAgICAgICAgY29uc3QgbmV4dCA9IG5ldyBTZXQocHJldilcbiAgICAgICAgICBuZXh0LmRlbGV0ZShmaWVsZE5hbWUpXG4gICAgICAgICAgcmV0dXJuIG5leHRcbiAgICAgICAgfSlcbiAgICAgICAgaWYgKGNvbnRyb2xsZXIuc2lnbmFsLmFib3J0ZWQpIHJldHVyblxuXG4gICAgICAgIGlmIChyZXN1bHQuaXNWYWxpZCkge1xuICAgICAgICAgIHNldEZpZWxkKGZpZWxkTmFtZSwgcmVzdWx0LnZhbHVlKVxuICAgICAgICAgIHVwZGF0ZVZhbGlkYXRpb25FcnJvcihmaWVsZE5hbWUpXG4gICAgICAgICAgLy8gVXBkYXRlIHRoZSB0ZXh0IGlucHV0IGlmIHdlJ3JlIHN0aWxsIG9uIHRoaXMgZmllbGRcbiAgICAgICAgICBjb25zdCBpc29UZXh0ID0gU3RyaW5nKHJlc3VsdC52YWx1ZSlcbiAgICAgICAgICBzZXRUZXh0SW5wdXRWYWx1ZShwcmV2ID0+IHtcbiAgICAgICAgICAgIC8vIE9ubHkgcmVwbGFjZSBpZiB0aGUgZmllbGQgaXMgc3RpbGwgc2hvd2luZyB0aGUgcmF3IGlucHV0XG4gICAgICAgICAgICBpZiAocHJldiA9PT0gcmF3VmFsdWUpIHtcbiAgICAgICAgICAgICAgc2V0VGV4dElucHV0Q3Vyc29yT2Zmc2V0KGlzb1RleHQubGVuZ3RoKVxuICAgICAgICAgICAgICByZXR1cm4gaXNvVGV4dFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHByZXZcbiAgICAgICAgICB9KVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIEtlZXAgcmF3IHRleHQsIHNob3cgdmFsaWRhdGlvbiBlcnJvclxuICAgICAgICAgIHVwZGF0ZVZhbGlkYXRpb25FcnJvcihmaWVsZE5hbWUsIHJlc3VsdC5lcnJvcilcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgICgpID0+IHtcbiAgICAgICAgcmVzb2x2ZUFib3J0UmVmLmN1cnJlbnQuZGVsZXRlKGZpZWxkTmFtZSlcbiAgICAgICAgc2V0UmVzb2x2aW5nRmllbGRzKHByZXYgPT4ge1xuICAgICAgICAgIGNvbnN0IG5leHQgPSBuZXcgU2V0KHByZXYpXG4gICAgICAgICAgbmV4dC5kZWxldGUoZmllbGROYW1lKVxuICAgICAgICAgIHJldHVybiBuZXh0XG4gICAgICAgIH0pXG4gICAgICB9LFxuICAgIClcbiAgfVxuXG4gIGZ1bmN0aW9uIGhhbmRsZVRleHRJbnB1dENoYW5nZShuZXdWYWx1ZTogc3RyaW5nKSB7XG4gICAgc2V0VGV4dElucHV0VmFsdWUobmV3VmFsdWUpXG4gICAgLy8gQ29tbWl0IGltbWVkaWF0ZWx5IG9uIGVhY2gga2V5c3Ryb2tlIChzeW5jIHZhbGlkYXRpb24pXG4gICAgaWYgKGN1cnJlbnRGaWVsZCkge1xuICAgICAgY29tbWl0VGV4dEZpZWxkKGN1cnJlbnRGaWVsZC5uYW1lLCBjdXJyZW50RmllbGQuc2NoZW1hLCBuZXdWYWx1ZSlcblxuICAgICAgLy8gRm9yIGRhdGUvZGF0ZXRpbWUgZmllbGRzLCBkZWJvdW5jZSBhc3luYyBOTCBwYXJzaW5nIGFmdGVyIDJzIG9mIGluYWN0aXZpdHlcbiAgICAgIGlmIChkYXRlRGVib3VuY2VSZWYuY3VycmVudCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIGNsZWFyVGltZW91dChkYXRlRGVib3VuY2VSZWYuY3VycmVudClcbiAgICAgICAgZGF0ZURlYm91bmNlUmVmLmN1cnJlbnQgPSB1bmRlZmluZWRcbiAgICAgIH1cbiAgICAgIGlmIChcbiAgICAgICAgaXNEYXRlVGltZVNjaGVtYShjdXJyZW50RmllbGQuc2NoZW1hKSAmJlxuICAgICAgICBuZXdWYWx1ZS50cmltKCkgIT09ICcnICYmXG4gICAgICAgIHZhbGlkYXRpb25FcnJvcnNbY3VycmVudEZpZWxkLm5hbWVdXG4gICAgICApIHtcbiAgICAgICAgY29uc3QgZmllbGROYW1lID0gY3VycmVudEZpZWxkLm5hbWVcbiAgICAgICAgY29uc3Qgc2NoZW1hID0gY3VycmVudEZpZWxkLnNjaGVtYVxuICAgICAgICBkYXRlRGVib3VuY2VSZWYuY3VycmVudCA9IHNldFRpbWVvdXQoXG4gICAgICAgICAgKGRhdGVEZWJvdW5jZVJlZiwgcmVzb2x2ZUZpZWxkQXN5bmMsIGZpZWxkTmFtZSwgc2NoZW1hLCBuZXdWYWx1ZSkgPT4ge1xuICAgICAgICAgICAgZGF0ZURlYm91bmNlUmVmLmN1cnJlbnQgPSB1bmRlZmluZWRcbiAgICAgICAgICAgIHJlc29sdmVGaWVsZEFzeW5jKGZpZWxkTmFtZSwgc2NoZW1hLCBuZXdWYWx1ZSlcbiAgICAgICAgICB9LFxuICAgICAgICAgIDIwMDAsXG4gICAgICAgICAgZGF0ZURlYm91bmNlUmVmLFxuICAgICAgICAgIHJlc29sdmVGaWVsZEFzeW5jLFxuICAgICAgICAgIGZpZWxkTmFtZSxcbiAgICAgICAgICBzY2hlbWEsXG4gICAgICAgICAgbmV3VmFsdWUsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBmdW5jdGlvbiBoYW5kbGVUZXh0SW5wdXRTdWJtaXQoKSB7XG4gICAgaGFuZGxlTmF2aWdhdGlvbignZG93bicpXG4gIH1cblxuICAvKipcbiAgICogQXBwZW5kIGEga2V5c3Ryb2tlIHRvIHRoZSB0eXBlYWhlYWQgYnVmZmVyIChyZXNldCBhZnRlciAycyBpZGxlKSBhbmRcbiAgICogY2FsbCBgb25NYXRjaGAgd2l0aCB0aGUgaW5kZXggb2YgdGhlIGZpcnN0IGxhYmVsIHRoYXQgcHJlZml4LW1hdGNoZXMuXG4gICAqIFNoYXJlZCBieSBib29sZWFuIHkvbiwgZW51bSBhY2NvcmRpb24sIGFuZCBtdWx0aS1zZWxlY3QgYWNjb3JkaW9uLlxuICAgKi9cbiAgZnVuY3Rpb24gcnVuVHlwZWFoZWFkKFxuICAgIGNoYXI6IHN0cmluZyxcbiAgICBsYWJlbHM6IHN0cmluZ1tdLFxuICAgIG9uTWF0Y2g6IChpbmRleDogbnVtYmVyKSA9PiB2b2lkLFxuICApIHtcbiAgICBjb25zdCB0YSA9IGVudW1UeXBlYWhlYWRSZWYuY3VycmVudFxuICAgIGlmICh0YS50aW1lciAhPT0gdW5kZWZpbmVkKSBjbGVhclRpbWVvdXQodGEudGltZXIpXG4gICAgdGEuYnVmZmVyICs9IGNoYXIudG9Mb3dlckNhc2UoKVxuICAgIHRhLnRpbWVyID0gc2V0VGltZW91dChyZXNldFR5cGVhaGVhZCwgMjAwMCwgdGEpXG4gICAgY29uc3QgbWF0Y2ggPSBsYWJlbHMuZmluZEluZGV4KGwgPT4gbC5zdGFydHNXaXRoKHRhLmJ1ZmZlcikpXG4gICAgaWYgKG1hdGNoICE9PSAtMSkgb25NYXRjaChtYXRjaClcbiAgfVxuXG4gIC8vIEVzYyB3aGlsZSBhIGZpZWxkIGlzIGZvY3VzZWQ6IGNhbmNlbCB0aGUgZGlhbG9nLlxuICAvLyBVc2VzIFNldHRpbmdzIGNvbnRleHQgKGVzY2FwZS1vbmx5LCBubyAnbicga2V5KSBzaW5jZSBEaWFsb2cnc1xuICAvLyBDb25maXJtYXRpb24tY29udGV4dCBjYW5jZWwgaXMgc3VwcHJlc3NlZCB3aGVuIGEgZmllbGQgaXMgZm9jdXNlZC5cbiAgdXNlS2V5YmluZGluZyhcbiAgICAnY29uZmlybTpubycsXG4gICAgKCkgPT4ge1xuICAgICAgLy8gRm9yIHRleHQgZmllbGRzLCByZXZlcnQgdW5jb21taXR0ZWQgY2hhbmdlcyBmaXJzdFxuICAgICAgaWYgKGlzRWRpdGluZ1RleHRGaWVsZCAmJiBjdXJyZW50RmllbGQpIHtcbiAgICAgICAgY29uc3QgdmFsID0gZm9ybVZhbHVlc1tjdXJyZW50RmllbGQubmFtZV1cbiAgICAgICAgc2V0VGV4dElucHV0VmFsdWUodmFsICE9PSB1bmRlZmluZWQgPyBTdHJpbmcodmFsKSA6ICcnKVxuICAgICAgICBzZXRUZXh0SW5wdXRDdXJzb3JPZmZzZXQoMClcbiAgICAgIH1cbiAgICAgIG9uUmVzcG9uc2UoJ2NhbmNlbCcpXG4gICAgfSxcbiAgICB7XG4gICAgICBjb250ZXh0OiAnU2V0dGluZ3MnLFxuICAgICAgaXNBY3RpdmU6ICEhY3VycmVudEZpZWxkICYmICFmb2N1c2VkQnV0dG9uICYmICFleHBhbmRlZEFjY29yZGlvbixcbiAgICB9LFxuICApXG5cbiAgdXNlSW5wdXQoXG4gICAgKF9pbnB1dCwga2V5KSA9PiB7XG4gICAgICAvLyBUZXh0IGZpZWxkcyBoYW5kbGUgdGhlaXIgb3duIGNoYXJhY3RlciBpbnB1dDsgd2Ugb25seSBpbnRlcmNlcHRcbiAgICAgIC8vIG5hdmlnYXRpb24ga2V5cyBhbmQgYmFja3NwYWNlLW9uLWVtcHR5IGhlcmUuXG4gICAgICBpZiAoXG4gICAgICAgIGlzRWRpdGluZ1RleHRGaWVsZCAmJlxuICAgICAgICAha2V5LnVwQXJyb3cgJiZcbiAgICAgICAgIWtleS5kb3duQXJyb3cgJiZcbiAgICAgICAgIWtleS5yZXR1cm4gJiZcbiAgICAgICAgIWtleS5iYWNrc3BhY2VcbiAgICAgICkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gRXhwYW5kZWQgbXVsdGktc2VsZWN0IGFjY29yZGlvblxuICAgICAgaWYgKFxuICAgICAgICBleHBhbmRlZEFjY29yZGlvbiAmJlxuICAgICAgICBjdXJyZW50RmllbGQgJiZcbiAgICAgICAgaXNNdWx0aVNlbGVjdEVudW1TY2hlbWEoY3VycmVudEZpZWxkLnNjaGVtYSlcbiAgICAgICkge1xuICAgICAgICBjb25zdCBtc1NjaGVtYSA9IGN1cnJlbnRGaWVsZC5zY2hlbWFcbiAgICAgICAgY29uc3QgbXNWYWx1ZXMgPSBnZXRNdWx0aVNlbGVjdFZhbHVlcyhtc1NjaGVtYSlcbiAgICAgICAgY29uc3Qgc2VsZWN0ZWQgPSAoZm9ybVZhbHVlc1tjdXJyZW50RmllbGQubmFtZV0gYXMgc3RyaW5nW10pID8/IFtdXG5cbiAgICAgICAgaWYgKGtleS5sZWZ0QXJyb3cgfHwga2V5LmVzY2FwZSkge1xuICAgICAgICAgIHNldEV4cGFuZGVkQWNjb3JkaW9uKHVuZGVmaW5lZClcbiAgICAgICAgICB2YWxpZGF0ZU11bHRpU2VsZWN0KGN1cnJlbnRGaWVsZC5uYW1lLCBtc1NjaGVtYSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAoa2V5LnVwQXJyb3cpIHtcbiAgICAgICAgICBpZiAoYWNjb3JkaW9uT3B0aW9uSW5kZXggPT09IDApIHtcbiAgICAgICAgICAgIHNldEV4cGFuZGVkQWNjb3JkaW9uKHVuZGVmaW5lZClcbiAgICAgICAgICAgIHZhbGlkYXRlTXVsdGlTZWxlY3QoY3VycmVudEZpZWxkLm5hbWUsIG1zU2NoZW1hKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBzZXRBY2NvcmRpb25PcHRpb25JbmRleChhY2NvcmRpb25PcHRpb25JbmRleCAtIDEpXG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGlmIChrZXkuZG93bkFycm93KSB7XG4gICAgICAgICAgaWYgKGFjY29yZGlvbk9wdGlvbkluZGV4ID49IG1zVmFsdWVzLmxlbmd0aCAtIDEpIHtcbiAgICAgICAgICAgIHNldEV4cGFuZGVkQWNjb3JkaW9uKHVuZGVmaW5lZClcbiAgICAgICAgICAgIGhhbmRsZU5hdmlnYXRpb24oJ2Rvd24nKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBzZXRBY2NvcmRpb25PcHRpb25JbmRleChhY2NvcmRpb25PcHRpb25JbmRleCArIDEpXG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIGlmIChfaW5wdXQgPT09ICcgJykge1xuICAgICAgICAgIGNvbnN0IG9wdGlvblZhbHVlID0gbXNWYWx1ZXNbYWNjb3JkaW9uT3B0aW9uSW5kZXhdXG4gICAgICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGNvbnN0IG5ld1NlbGVjdGVkID0gc2VsZWN0ZWQuaW5jbHVkZXMob3B0aW9uVmFsdWUpXG4gICAgICAgICAgICAgID8gc2VsZWN0ZWQuZmlsdGVyKHYgPT4gdiAhPT0gb3B0aW9uVmFsdWUpXG4gICAgICAgICAgICAgIDogWy4uLnNlbGVjdGVkLCBvcHRpb25WYWx1ZV1cbiAgICAgICAgICAgIGNvbnN0IG5ld1ZhbHVlID0gbmV3U2VsZWN0ZWQubGVuZ3RoID4gMCA/IG5ld1NlbGVjdGVkIDogdW5kZWZpbmVkXG4gICAgICAgICAgICBzZXRGaWVsZChjdXJyZW50RmllbGQubmFtZSwgbmV3VmFsdWUpXG4gICAgICAgICAgICBjb25zdCBtaW4gPSBtc1NjaGVtYS5taW5JdGVtc1xuICAgICAgICAgICAgY29uc3QgbWF4ID0gbXNTY2hlbWEubWF4SXRlbXNcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgbWluICE9PSB1bmRlZmluZWQgJiZcbiAgICAgICAgICAgICAgbmV3U2VsZWN0ZWQubGVuZ3RoIDwgbWluICYmXG4gICAgICAgICAgICAgIChuZXdTZWxlY3RlZC5sZW5ndGggPiAwIHx8IGN1cnJlbnRGaWVsZC5pc1JlcXVpcmVkKVxuICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgIHVwZGF0ZVZhbGlkYXRpb25FcnJvcihcbiAgICAgICAgICAgICAgICBjdXJyZW50RmllbGQubmFtZSxcbiAgICAgICAgICAgICAgICBgU2VsZWN0IGF0IGxlYXN0ICR7bWlufSAke3BsdXJhbChtaW4sICdpdGVtJyl9YCxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgfSBlbHNlIGlmIChtYXggIT09IHVuZGVmaW5lZCAmJiBuZXdTZWxlY3RlZC5sZW5ndGggPiBtYXgpIHtcbiAgICAgICAgICAgICAgdXBkYXRlVmFsaWRhdGlvbkVycm9yKFxuICAgICAgICAgICAgICAgIGN1cnJlbnRGaWVsZC5uYW1lLFxuICAgICAgICAgICAgICAgIGBTZWxlY3QgYXQgbW9zdCAke21heH0gJHtwbHVyYWwobWF4LCAnaXRlbScpfWAsXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHVwZGF0ZVZhbGlkYXRpb25FcnJvcihjdXJyZW50RmllbGQubmFtZSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGtleS5yZXR1cm4pIHtcbiAgICAgICAgICAvLyBDaGVjayAobm90IHRvZ2dsZSkgdGhlIGZvY3VzZWQgaXRlbSwgdGhlbiBjb2xsYXBzZSBhbmQgYWR2YW5jZVxuICAgICAgICAgIGNvbnN0IG9wdGlvblZhbHVlID0gbXNWYWx1ZXNbYWNjb3JkaW9uT3B0aW9uSW5kZXhdXG4gICAgICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSB1bmRlZmluZWQgJiYgIXNlbGVjdGVkLmluY2x1ZGVzKG9wdGlvblZhbHVlKSkge1xuICAgICAgICAgICAgc2V0RmllbGQoY3VycmVudEZpZWxkLm5hbWUsIFsuLi5zZWxlY3RlZCwgb3B0aW9uVmFsdWVdKVxuICAgICAgICAgIH1cbiAgICAgICAgICBzZXRFeHBhbmRlZEFjY29yZGlvbih1bmRlZmluZWQpXG4gICAgICAgICAgaGFuZGxlTmF2aWdhdGlvbignZG93bicpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKF9pbnB1dCkge1xuICAgICAgICAgIGNvbnN0IGxhYmVscyA9IG1zVmFsdWVzLm1hcCh2ID0+XG4gICAgICAgICAgICBnZXRNdWx0aVNlbGVjdExhYmVsKG1zU2NoZW1hLCB2KS50b0xvd2VyQ2FzZSgpLFxuICAgICAgICAgIClcbiAgICAgICAgICBydW5UeXBlYWhlYWQoX2lucHV0LCBsYWJlbHMsIHNldEFjY29yZGlvbk9wdGlvbkluZGV4KVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBFeHBhbmRlZCBzaW5nbGUtc2VsZWN0IGVudW0gYWNjb3JkaW9uXG4gICAgICBpZiAoXG4gICAgICAgIGV4cGFuZGVkQWNjb3JkaW9uICYmXG4gICAgICAgIGN1cnJlbnRGaWVsZCAmJlxuICAgICAgICBpc0VudW1TY2hlbWEoY3VycmVudEZpZWxkLnNjaGVtYSlcbiAgICAgICkge1xuICAgICAgICBjb25zdCBlbnVtU2NoZW1hID0gY3VycmVudEZpZWxkLnNjaGVtYVxuICAgICAgICBjb25zdCBlbnVtVmFsdWVzID0gZ2V0RW51bVZhbHVlcyhlbnVtU2NoZW1hKVxuXG4gICAgICAgIGlmIChrZXkubGVmdEFycm93IHx8IGtleS5lc2NhcGUpIHtcbiAgICAgICAgICBzZXRFeHBhbmRlZEFjY29yZGlvbih1bmRlZmluZWQpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGtleS51cEFycm93KSB7XG4gICAgICAgICAgaWYgKGFjY29yZGlvbk9wdGlvbkluZGV4ID09PSAwKSB7XG4gICAgICAgICAgICBzZXRFeHBhbmRlZEFjY29yZGlvbih1bmRlZmluZWQpXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHNldEFjY29yZGlvbk9wdGlvbkluZGV4KGFjY29yZGlvbk9wdGlvbkluZGV4IC0gMSlcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGtleS5kb3duQXJyb3cpIHtcbiAgICAgICAgICBpZiAoYWNjb3JkaW9uT3B0aW9uSW5kZXggPj0gZW51bVZhbHVlcy5sZW5ndGggLSAxKSB7XG4gICAgICAgICAgICBzZXRFeHBhbmRlZEFjY29yZGlvbih1bmRlZmluZWQpXG4gICAgICAgICAgICBoYW5kbGVOYXZpZ2F0aW9uKCdkb3duJylcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgc2V0QWNjb3JkaW9uT3B0aW9uSW5kZXgoYWNjb3JkaW9uT3B0aW9uSW5kZXggKyAxKVxuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICAvLyBTcGFjZTogc2VsZWN0IGFuZCBjb2xsYXBzZVxuICAgICAgICBpZiAoX2lucHV0ID09PSAnICcpIHtcbiAgICAgICAgICBjb25zdCBvcHRpb25WYWx1ZSA9IGVudW1WYWx1ZXNbYWNjb3JkaW9uT3B0aW9uSW5kZXhdXG4gICAgICAgICAgaWYgKG9wdGlvblZhbHVlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHNldEZpZWxkKGN1cnJlbnRGaWVsZC5uYW1lLCBvcHRpb25WYWx1ZSlcbiAgICAgICAgICB9XG4gICAgICAgICAgc2V0RXhwYW5kZWRBY2NvcmRpb24odW5kZWZpbmVkKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIEVudGVyOiBzZWxlY3QsIGNvbGxhcHNlLCBhbmQgbW92ZSB0byBuZXh0IGZpZWxkXG4gICAgICAgIGlmIChrZXkucmV0dXJuKSB7XG4gICAgICAgICAgY29uc3Qgb3B0aW9uVmFsdWUgPSBlbnVtVmFsdWVzW2FjY29yZGlvbk9wdGlvbkluZGV4XVxuICAgICAgICAgIGlmIChvcHRpb25WYWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBzZXRGaWVsZChjdXJyZW50RmllbGQubmFtZSwgb3B0aW9uVmFsdWUpXG4gICAgICAgICAgfVxuICAgICAgICAgIHNldEV4cGFuZGVkQWNjb3JkaW9uKHVuZGVmaW5lZClcbiAgICAgICAgICBoYW5kbGVOYXZpZ2F0aW9uKCdkb3duJylcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAoX2lucHV0KSB7XG4gICAgICAgICAgY29uc3QgbGFiZWxzID0gZW51bVZhbHVlcy5tYXAodiA9PlxuICAgICAgICAgICAgZ2V0RW51bUxhYmVsKGVudW1TY2hlbWEsIHYpLnRvTG93ZXJDYXNlKCksXG4gICAgICAgICAgKVxuICAgICAgICAgIHJ1blR5cGVhaGVhZChfaW5wdXQsIGxhYmVscywgc2V0QWNjb3JkaW9uT3B0aW9uSW5kZXgpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIEFjY2VwdCAvIERlY2xpbmUgYnV0dG9uc1xuICAgICAgaWYgKGtleS5yZXR1cm4gJiYgZm9jdXNlZEJ1dHRvbiA9PT0gJ2FjY2VwdCcpIHtcbiAgICAgICAgaWYgKHZhbGlkYXRlUmVxdWlyZWQoKSAmJiBPYmplY3Qua2V5cyh2YWxpZGF0aW9uRXJyb3JzKS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICBvblJlc3BvbnNlKCdhY2NlcHQnLCBmb3JtVmFsdWVzKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIFNob3cgXCJyZXF1aXJlZFwiIHZhbGlkYXRpb24gZXJyb3JzIG9uIG1pc3NpbmcgZmllbGRzXG4gICAgICAgICAgY29uc3QgcmVxdWlyZWRGaWVsZHMgPSByZXF1ZXN0ZWRTY2hlbWEucmVxdWlyZWQgfHwgW11cbiAgICAgICAgICBmb3IgKGNvbnN0IGZpZWxkTmFtZSBvZiByZXF1aXJlZEZpZWxkcykge1xuICAgICAgICAgICAgaWYgKGZvcm1WYWx1ZXNbZmllbGROYW1lXSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgIHVwZGF0ZVZhbGlkYXRpb25FcnJvcihmaWVsZE5hbWUsICdUaGlzIGZpZWxkIGlzIHJlcXVpcmVkJylcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgZmlyc3RCYWRJbmRleCA9IHNjaGVtYUZpZWxkcy5maW5kSW5kZXgoXG4gICAgICAgICAgICBmID0+XG4gICAgICAgICAgICAgIChyZXF1aXJlZEZpZWxkcy5pbmNsdWRlcyhmLm5hbWUpICYmXG4gICAgICAgICAgICAgICAgZm9ybVZhbHVlc1tmLm5hbWVdID09PSB1bmRlZmluZWQpIHx8XG4gICAgICAgICAgICAgIHZhbGlkYXRpb25FcnJvcnNbZi5uYW1lXSAhPT0gdW5kZWZpbmVkLFxuICAgICAgICAgIClcbiAgICAgICAgICBpZiAoZmlyc3RCYWRJbmRleCAhPT0gLTEpIHtcbiAgICAgICAgICAgIHNldEN1cnJlbnRGaWVsZEluZGV4KGZpcnN0QmFkSW5kZXgpXG4gICAgICAgICAgICBzZXRGb2N1c2VkQnV0dG9uKG51bGwpXG4gICAgICAgICAgICBzeW5jVGV4dElucHV0KGZpcnN0QmFkSW5kZXgpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICBpZiAoa2V5LnJldHVybiAmJiBmb2N1c2VkQnV0dG9uID09PSAnZGVjbGluZScpIHtcbiAgICAgICAgb25SZXNwb25zZSgnZGVjbGluZScpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBVcC9Eb3duIG5hdmlnYXRpb25cbiAgICAgIGlmIChrZXkudXBBcnJvdyB8fCBrZXkuZG93bkFycm93KSB7XG4gICAgICAgIC8vIFJlc2V0IGVudW0gdHlwZWFoZWFkIHdoZW4gbGVhdmluZyBhIGZpZWxkXG4gICAgICAgIGNvbnN0IHRhID0gZW51bVR5cGVhaGVhZFJlZi5jdXJyZW50XG4gICAgICAgIHRhLmJ1ZmZlciA9ICcnXG4gICAgICAgIGlmICh0YS50aW1lciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY2xlYXJUaW1lb3V0KHRhLnRpbWVyKVxuICAgICAgICAgIHRhLnRpbWVyID0gdW5kZWZpbmVkXG4gICAgICAgIH1cbiAgICAgICAgaGFuZGxlTmF2aWdhdGlvbihrZXkudXBBcnJvdyA/ICd1cCcgOiAnZG93bicpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBMZWZ0L1JpZ2h0IHRvIHN3aXRjaCBiZXR3ZWVuIEFjY2VwdCBhbmQgRGVjbGluZSBidXR0b25zXG4gICAgICBpZiAoZm9jdXNlZEJ1dHRvbiAmJiAoa2V5LmxlZnRBcnJvdyB8fCBrZXkucmlnaHRBcnJvdykpIHtcbiAgICAgICAgc2V0Rm9jdXNlZEJ1dHRvbihmb2N1c2VkQnV0dG9uID09PSAnYWNjZXB0JyA/ICdkZWNsaW5lJyA6ICdhY2NlcHQnKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgaWYgKCFjdXJyZW50RmllbGQpIHJldHVyblxuICAgICAgY29uc3QgeyBzY2hlbWEsIG5hbWUgfSA9IGN1cnJlbnRGaWVsZFxuICAgICAgY29uc3QgdmFsdWUgPSBmb3JtVmFsdWVzW25hbWVdXG5cbiAgICAgIC8vIEJvb2xlYW46IFNwYWNlIHRvIHRvZ2dsZSwgRW50ZXIgdG8gbW92ZSBvblxuICAgICAgaWYgKHNjaGVtYS50eXBlID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgaWYgKF9pbnB1dCA9PT0gJyAnKSB7XG4gICAgICAgICAgc2V0RmllbGQobmFtZSwgdmFsdWUgPT09IHVuZGVmaW5lZCA/IHRydWUgOiAhdmFsdWUpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGtleS5yZXR1cm4pIHtcbiAgICAgICAgICBoYW5kbGVOYXZpZ2F0aW9uKCdkb3duJylcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAoa2V5LmJhY2tzcGFjZSAmJiB2YWx1ZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgdW5zZXRGaWVsZChuYW1lKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIHkvbiB0eXBlYWhlYWRcbiAgICAgICAgaWYgKF9pbnB1dCAmJiAha2V5LnJldHVybikge1xuICAgICAgICAgIHJ1blR5cGVhaGVhZChfaW5wdXQsIFsneWVzJywgJ25vJ10sIGkgPT4gc2V0RmllbGQobmFtZSwgaSA9PT0gMCkpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIEVudW0gb3IgbXVsdGktc2VsZWN0IChjb2xsYXBzZWQpIOKAlCBhY2NvcmRpb24gc3R5bGVcbiAgICAgIGlmIChpc0VudW1TY2hlbWEoc2NoZW1hKSB8fCBpc011bHRpU2VsZWN0RW51bVNjaGVtYShzY2hlbWEpKSB7XG4gICAgICAgIGlmIChrZXkucmV0dXJuKSB7XG4gICAgICAgICAgaGFuZGxlTmF2aWdhdGlvbignZG93bicpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGtleS5iYWNrc3BhY2UgJiYgdmFsdWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgIHVuc2V0RmllbGQobmFtZSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICAvLyBDb21wdXRlIG9wdGlvbiBsYWJlbHMgKyBpbml0aWFsIGZvY3VzIGluZGV4IGZvciByaWdodEFycm93IGV4cGFuZC5cbiAgICAgICAgLy8gU2luZ2xlLXNlbGVjdCBmb2N1c2VzIG9uIHRoZSBjdXJyZW50IHZhbHVlOyBtdWx0aS1zZWxlY3Qgc3RhcnRzIGF0IDAuXG4gICAgICAgIGxldCBsYWJlbHM6IHN0cmluZ1tdXG4gICAgICAgIGxldCBzdGFydElkeCA9IDBcbiAgICAgICAgaWYgKGlzRW51bVNjaGVtYShzY2hlbWEpKSB7XG4gICAgICAgICAgY29uc3QgdmFscyA9IGdldEVudW1WYWx1ZXMoc2NoZW1hKVxuICAgICAgICAgIGxhYmVscyA9IHZhbHMubWFwKHYgPT4gZ2V0RW51bUxhYmVsKHNjaGVtYSwgdikudG9Mb3dlckNhc2UoKSlcbiAgICAgICAgICBpZiAodmFsdWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgc3RhcnRJZHggPSBNYXRoLm1heCgwLCB2YWxzLmluZGV4T2YodmFsdWUgYXMgc3RyaW5nKSlcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc3QgdmFscyA9IGdldE11bHRpU2VsZWN0VmFsdWVzKHNjaGVtYSlcbiAgICAgICAgICBsYWJlbHMgPSB2YWxzLm1hcCh2ID0+IGdldE11bHRpU2VsZWN0TGFiZWwoc2NoZW1hLCB2KS50b0xvd2VyQ2FzZSgpKVxuICAgICAgICB9XG4gICAgICAgIGlmIChrZXkucmlnaHRBcnJvdykge1xuICAgICAgICAgIHNldEV4cGFuZGVkQWNjb3JkaW9uKG5hbWUpXG4gICAgICAgICAgc2V0QWNjb3JkaW9uT3B0aW9uSW5kZXgoc3RhcnRJZHgpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgICAgLy8gVHlwZWFoZWFkOiBleHBhbmQgYW5kIGp1bXAgdG8gbWF0Y2hpbmcgb3B0aW9uXG4gICAgICAgIGlmIChfaW5wdXQgJiYgIWtleS5sZWZ0QXJyb3cpIHtcbiAgICAgICAgICBydW5UeXBlYWhlYWQoX2lucHV0LCBsYWJlbHMsIGkgPT4ge1xuICAgICAgICAgICAgc2V0RXhwYW5kZWRBY2NvcmRpb24obmFtZSlcbiAgICAgICAgICAgIHNldEFjY29yZGlvbk9wdGlvbkluZGV4KGkpXG4gICAgICAgICAgfSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gQmFja3NwYWNlOiB0ZXh0IGZpZWxkcyB3aGVuIGVtcHR5XG4gICAgICBpZiAoa2V5LmJhY2tzcGFjZSkge1xuICAgICAgICBpZiAoaXNFZGl0aW5nVGV4dEZpZWxkICYmIHRleHRJbnB1dFZhbHVlID09PSAnJykge1xuICAgICAgICAgIHVuc2V0RmllbGQobmFtZSlcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBUZXh0IGZpZWxkIEVudGVyIGlzIGhhbmRsZWQgYnkgVGV4dElucHV0J3Mgb25TdWJtaXRcbiAgICB9LFxuICAgIHsgaXNBY3RpdmU6IHRydWUgfSxcbiAgKVxuXG4gIGZ1bmN0aW9uIHZhbGlkYXRlUmVxdWlyZWQoKTogYm9vbGVhbiB7XG4gICAgY29uc3QgcmVxdWlyZWRGaWVsZHMgPSByZXF1ZXN0ZWRTY2hlbWEucmVxdWlyZWQgfHwgW11cbiAgICBmb3IgKGNvbnN0IGZpZWxkTmFtZSBvZiByZXF1aXJlZEZpZWxkcykge1xuICAgICAgY29uc3QgdmFsdWUgPSBmb3JtVmFsdWVzW2ZpZWxkTmFtZV1cbiAgICAgIGlmICh2YWx1ZSA9PT0gdW5kZWZpbmVkIHx8IHZhbHVlID09PSBudWxsIHx8IHZhbHVlID09PSAnJykge1xuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgIH1cbiAgICAgIGlmIChBcnJheS5pc0FycmF5KHZhbHVlKSAmJiB2YWx1ZS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0cnVlXG4gIH1cblxuICAvLyBTY3JvbGwgd2luZG93aW5nOiBjb21wdXRlIHZpc2libGUgZmllbGQgcmFuZ2VcbiAgLy8gT3ZlcmhlYWQ6IH45IGxpbmVzIChkaWFsb2cgY2hyb21lLCBidXR0b25zLCBmb290ZXIpLlxuICAvLyBFYWNoIGZpZWxkOiB+MyBsaW5lcyAobGFiZWwgKyBkZXNjcmlwdGlvbiArIHZhbGlkYXRpb24gc3BhY2VyKS5cbiAgLy8gTk9URSh2Mik6IE11bHRpLXNlbGVjdCBhY2NvcmRpb24gZXhwYW5kcyB0byBOKzMgbGluZXMgd2hlbiBvcGVuLlxuICAvLyBGb3Igbm93IHdlIGFzc3VtZSAzIGxpbmVzIHBlciBmaWVsZDsgYW4gZXhwYW5kZWQgYWNjb3JkaW9uIG1heVxuICAvLyB0ZW1wb3JhcmlseSBwdXNoIGNvbnRlbnQgb2ZmLXNjcmVlbiAodGVybWluYWwgc2Nyb2xsYmFjayBoYW5kbGVzIGl0KS5cbiAgLy8gVG8gZ2VuZXJhbGl6ZTogdHJhY2sgcGVyLWZpZWxkIGhlaWdodCAoMyBmb3IgY29sbGFwc2VkLCBOKzMgZm9yXG4gIC8vIGV4cGFuZGVkIG11bHRpLXNlbGVjdCkgYW5kIGNvbXB1dGUgYSBwaXhlbC1idWRnZXQgd2luZG93IGluc3RlYWRcbiAgLy8gb2YgYSBzaW1wbGUgaXRlbS1jb3VudCB3aW5kb3cuXG4gIGNvbnN0IExJTkVTX1BFUl9GSUVMRCA9IDNcbiAgY29uc3QgRElBTE9HX09WRVJIRUFEID0gMTRcbiAgY29uc3QgbWF4VmlzaWJsZUZpZWxkcyA9IE1hdGgubWF4KFxuICAgIDIsXG4gICAgTWF0aC5mbG9vcigocm93cyAtIERJQUxPR19PVkVSSEVBRCkgLyBMSU5FU19QRVJfRklFTEQpLFxuICApXG5cbiAgY29uc3Qgc2Nyb2xsV2luZG93ID0gdXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3QgdG90YWwgPSBzY2hlbWFGaWVsZHMubGVuZ3RoXG4gICAgaWYgKHRvdGFsIDw9IG1heFZpc2libGVGaWVsZHMpIHtcbiAgICAgIHJldHVybiB7IHN0YXJ0OiAwLCBlbmQ6IHRvdGFsIH1cbiAgICB9XG4gICAgLy8gV2hlbiBidXR0b25zIGFyZSBmb2N1c2VkIChjdXJyZW50RmllbGRJbmRleCB1bmRlZmluZWQpLCBwaW4gdG8gZW5kXG4gICAgY29uc3QgZm9jdXNJZHggPSBjdXJyZW50RmllbGRJbmRleCA/PyB0b3RhbCAtIDFcbiAgICBsZXQgc3RhcnQgPSBNYXRoLm1heCgwLCBmb2N1c0lkeCAtIE1hdGguZmxvb3IobWF4VmlzaWJsZUZpZWxkcyAvIDIpKVxuICAgIGNvbnN0IGVuZCA9IE1hdGgubWluKHN0YXJ0ICsgbWF4VmlzaWJsZUZpZWxkcywgdG90YWwpXG4gICAgLy8gQWRqdXN0IHN0YXJ0IGlmIHdlIGhpdCB0aGUgYm90dG9tXG4gICAgc3RhcnQgPSBNYXRoLm1heCgwLCBlbmQgLSBtYXhWaXNpYmxlRmllbGRzKVxuICAgIHJldHVybiB7IHN0YXJ0LCBlbmQgfVxuICB9LCBbc2NoZW1hRmllbGRzLmxlbmd0aCwgbWF4VmlzaWJsZUZpZWxkcywgY3VycmVudEZpZWxkSW5kZXhdKVxuXG4gIGNvbnN0IGhhc0ZpZWxkc0Fib3ZlID0gc2Nyb2xsV2luZG93LnN0YXJ0ID4gMFxuICBjb25zdCBoYXNGaWVsZHNCZWxvdyA9IHNjcm9sbFdpbmRvdy5lbmQgPCBzY2hlbWFGaWVsZHMubGVuZ3RoXG5cbiAgZnVuY3Rpb24gcmVuZGVyRm9ybUZpZWxkcygpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAgIGlmICghc2NoZW1hRmllbGRzLmxlbmd0aCkgcmV0dXJuIG51bGxcblxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAge2hhc0ZpZWxkc0Fib3ZlICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpbkxlZnQ9ezJ9PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIHtmaWd1cmVzLmFycm93VXB9IHtzY3JvbGxXaW5kb3cuc3RhcnR9IG1vcmUgYWJvdmVcbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgICAge3NjaGVtYUZpZWxkc1xuICAgICAgICAgIC5zbGljZShzY3JvbGxXaW5kb3cuc3RhcnQsIHNjcm9sbFdpbmRvdy5lbmQpXG4gICAgICAgICAgLm1hcCgoZmllbGQsIHZpc2libGVJZHgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGluZGV4ID0gc2Nyb2xsV2luZG93LnN0YXJ0ICsgdmlzaWJsZUlkeFxuICAgICAgICAgICAgY29uc3QgeyBuYW1lLCBzY2hlbWEsIGlzUmVxdWlyZWQgfSA9IGZpZWxkXG4gICAgICAgICAgICBjb25zdCBpc0FjdGl2ZSA9IGluZGV4ID09PSBjdXJyZW50RmllbGRJbmRleCAmJiAhZm9jdXNlZEJ1dHRvblxuICAgICAgICAgICAgY29uc3QgdmFsdWUgPSBmb3JtVmFsdWVzW25hbWVdXG4gICAgICAgICAgICBjb25zdCBoYXNWYWx1ZSA9XG4gICAgICAgICAgICAgIHZhbHVlICE9PSB1bmRlZmluZWQgJiYgKCFBcnJheS5pc0FycmF5KHZhbHVlKSB8fCB2YWx1ZS5sZW5ndGggPiAwKVxuICAgICAgICAgICAgY29uc3QgZXJyb3IgPSB2YWxpZGF0aW9uRXJyb3JzW25hbWVdXG5cbiAgICAgICAgICAgIC8vIENoZWNrYm94OiBzcGlubmVyIOKGkiDimqAgZXJyb3Ig4oaSIOKclCBzZXQg4oaSICogcmVxdWlyZWQg4oaSIHNwYWNlXG4gICAgICAgICAgICBjb25zdCBpc1Jlc29sdmluZyA9IHJlc29sdmluZ0ZpZWxkcy5oYXMobmFtZSlcbiAgICAgICAgICAgIGNvbnN0IGNoZWNrYm94ID0gaXNSZXNvbHZpbmcgPyAoXG4gICAgICAgICAgICAgIDxSZXNvbHZpbmdTcGlubmVyIC8+XG4gICAgICAgICAgICApIDogZXJyb3IgPyAoXG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj57ZmlndXJlcy53YXJuaW5nfTwvVGV4dD5cbiAgICAgICAgICAgICkgOiBoYXNWYWx1ZSA/IChcbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCIgZGltQ29sb3I9eyFpc0FjdGl2ZX0+XG4gICAgICAgICAgICAgICAge2ZpZ3VyZXMudGlja31cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgKSA6IGlzUmVxdWlyZWQgPyAoXG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj4qPC9UZXh0PlxuICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgPFRleHQ+IDwvVGV4dD5cbiAgICAgICAgICAgIClcblxuICAgICAgICAgICAgLy8gU2VsZWN0aW9uIGNvbG9yIG1hdGNoZXMgZmllbGQgc3RhdHVzXG4gICAgICAgICAgICBjb25zdCBzZWxlY3Rpb25Db2xvciA9IGVycm9yXG4gICAgICAgICAgICAgID8gJ2Vycm9yJ1xuICAgICAgICAgICAgICA6IGhhc1ZhbHVlXG4gICAgICAgICAgICAgICAgPyAnc3VjY2VzcydcbiAgICAgICAgICAgICAgICA6IGlzUmVxdWlyZWRcbiAgICAgICAgICAgICAgICAgID8gJ2Vycm9yJ1xuICAgICAgICAgICAgICAgICAgOiAnc3VnZ2VzdGlvbidcblxuICAgICAgICAgICAgY29uc3QgYWN0aXZlQ29sb3IgPSBpc0FjdGl2ZSA/IHNlbGVjdGlvbkNvbG9yIDogdW5kZWZpbmVkXG5cbiAgICAgICAgICAgIGNvbnN0IGxhYmVsID0gKFxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17YWN0aXZlQ29sb3J9IGJvbGQ9e2lzQWN0aXZlfT5cbiAgICAgICAgICAgICAgICB7c2NoZW1hLnRpdGxlIHx8IG5hbWV9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIClcblxuICAgICAgICAgICAgLy8gUmVuZGVyIHRoZSB2YWx1ZSBwb3J0aW9uIGJhc2VkIG9uIGZpZWxkIHR5cGVcbiAgICAgICAgICAgIGxldCB2YWx1ZUNvbnRlbnQ6IFJlYWN0LlJlYWN0Tm9kZVxuICAgICAgICAgICAgbGV0IGFjY29yZGlvbkNvbnRlbnQ6IFJlYWN0LlJlYWN0Tm9kZSA9IG51bGxcblxuICAgICAgICAgICAgaWYgKGlzTXVsdGlTZWxlY3RFbnVtU2NoZW1hKHNjaGVtYSkpIHtcbiAgICAgICAgICAgICAgY29uc3QgbXNWYWx1ZXMgPSBnZXRNdWx0aVNlbGVjdFZhbHVlcyhzY2hlbWEpXG4gICAgICAgICAgICAgIGNvbnN0IHNlbGVjdGVkID0gKHZhbHVlIGFzIHN0cmluZ1tdIHwgdW5kZWZpbmVkKSA/PyBbXVxuICAgICAgICAgICAgICBjb25zdCBpc0V4cGFuZGVkID0gZXhwYW5kZWRBY2NvcmRpb24gPT09IG5hbWUgJiYgaXNBY3RpdmVcblxuICAgICAgICAgICAgICBpZiAoaXNFeHBhbmRlZCkge1xuICAgICAgICAgICAgICAgIHZhbHVlQ29udGVudCA9IDxUZXh0IGRpbUNvbG9yPntmaWd1cmVzLnRyaWFuZ2xlRG93blNtYWxsfTwvVGV4dD5cbiAgICAgICAgICAgICAgICBhY2NvcmRpb25Db250ZW50ID0gKFxuICAgICAgICAgICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luTGVmdD17Nn0+XG4gICAgICAgICAgICAgICAgICAgIHttc1ZhbHVlcy5tYXAoKG9wdFZhbCwgb3B0SWR4KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgY29uc3Qgb3B0TGFiZWwgPSBnZXRNdWx0aVNlbGVjdExhYmVsKHNjaGVtYSwgb3B0VmFsKVxuICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGlzQ2hlY2tlZCA9IHNlbGVjdGVkLmluY2x1ZGVzKG9wdFZhbClcbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCBpc0ZvY3VzZWQgPSBvcHRJZHggPT09IGFjY29yZGlvbk9wdGlvbkluZGV4XG4gICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICAgICAgICAgICAgIDxCb3gga2V5PXtvcHRWYWx9IGdhcD17MX0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtpc0ZvY3VzZWQgPyBmaWd1cmVzLnBvaW50ZXIgOiAnICd9XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPFRleHQgY29sb3I9e2lzQ2hlY2tlZCA/ICdzdWNjZXNzJyA6IHVuZGVmaW5lZH0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge2lzQ2hlY2tlZFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPyBmaWd1cmVzLmNoZWNrYm94T25cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDogZmlndXJlcy5jaGVja2JveE9mZn1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPXtpc0ZvY3VzZWQgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYm9sZD17aXNGb2N1c2VkfVxuICAgICAgICAgICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge29wdExhYmVsfVxuICAgICAgICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIH0pfVxuICAgICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIENvbGxhcHNlZDog4pa4IGFycm93IHRoZW4gY29tbWEtam9pbmVkIHNlbGVjdGVkIGl0ZW1zXG4gICAgICAgICAgICAgICAgY29uc3QgYXJyb3cgPSBpc0FjdGl2ZSA/IChcbiAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntmaWd1cmVzLnRyaWFuZ2xlUmlnaHRTbWFsbH0gPC9UZXh0PlxuICAgICAgICAgICAgICAgICkgOiBudWxsXG4gICAgICAgICAgICAgICAgaWYgKHNlbGVjdGVkLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGRpc3BsYXlMYWJlbHMgPSBzZWxlY3RlZC5tYXAodiA9PlxuICAgICAgICAgICAgICAgICAgICBnZXRNdWx0aVNlbGVjdExhYmVsKHNjaGVtYSwgdiksXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICB2YWx1ZUNvbnRlbnQgPSAoXG4gICAgICAgICAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAgICAgICAgIHthcnJvd31cbiAgICAgICAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17YWN0aXZlQ29sb3J9IGJvbGQ9e2lzQWN0aXZlfT5cbiAgICAgICAgICAgICAgICAgICAgICAgIHtkaXNwbGF5TGFiZWxzLmpvaW4oJywgJyl9XG4gICAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgIHZhbHVlQ29udGVudCA9IChcbiAgICAgICAgICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgICAgICAgICAge2Fycm93fVxuICAgICAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGl0YWxpYz5cbiAgICAgICAgICAgICAgICAgICAgICAgIG5vdCBzZXRcbiAgICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoaXNFbnVtU2NoZW1hKHNjaGVtYSkpIHtcbiAgICAgICAgICAgICAgY29uc3QgZW51bVZhbHVlcyA9IGdldEVudW1WYWx1ZXMoc2NoZW1hKVxuICAgICAgICAgICAgICBjb25zdCBpc0V4cGFuZGVkID0gZXhwYW5kZWRBY2NvcmRpb24gPT09IG5hbWUgJiYgaXNBY3RpdmVcblxuICAgICAgICAgICAgICBpZiAoaXNFeHBhbmRlZCkge1xuICAgICAgICAgICAgICAgIHZhbHVlQ29udGVudCA9IDxUZXh0IGRpbUNvbG9yPntmaWd1cmVzLnRyaWFuZ2xlRG93blNtYWxsfTwvVGV4dD5cbiAgICAgICAgICAgICAgICBhY2NvcmRpb25Db250ZW50ID0gKFxuICAgICAgICAgICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luTGVmdD17Nn0+XG4gICAgICAgICAgICAgICAgICAgIHtlbnVtVmFsdWVzLm1hcCgob3B0VmFsLCBvcHRJZHgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCBvcHRMYWJlbCA9IGdldEVudW1MYWJlbChzY2hlbWEsIG9wdFZhbClcbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCBpc1NlbGVjdGVkID0gdmFsdWUgPT09IG9wdFZhbFxuICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGlzRm9jdXNlZCA9IG9wdElkeCA9PT0gYWNjb3JkaW9uT3B0aW9uSW5kZXhcbiAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICAgICAgICAgICAgPEJveCBrZXk9e29wdFZhbH0gZ2FwPXsxfT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWdnZXN0aW9uXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge2lzRm9jdXNlZCA/IGZpZ3VyZXMucG9pbnRlciA6ICcgJ31cbiAgICAgICAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17aXNTZWxlY3RlZCA/ICdzdWNjZXNzJyA6IHVuZGVmaW5lZH0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge2lzU2VsZWN0ZWQgPyBmaWd1cmVzLnJhZGlvT24gOiBmaWd1cmVzLnJhZGlvT2ZmfVxuICAgICAgICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9e2lzRm9jdXNlZCA/ICdzdWdnZXN0aW9uJyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBib2xkPXtpc0ZvY3VzZWR9XG4gICAgICAgICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7b3B0TGFiZWx9XG4gICAgICAgICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgfSl9XG4gICAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gQ29sbGFwc2VkOiDilrggYXJyb3cgdGhlbiBjdXJyZW50IHZhbHVlXG4gICAgICAgICAgICAgICAgY29uc3QgYXJyb3cgPSBpc0FjdGl2ZSA/IChcbiAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntmaWd1cmVzLnRyaWFuZ2xlUmlnaHRTbWFsbH0gPC9UZXh0PlxuICAgICAgICAgICAgICAgICkgOiBudWxsXG4gICAgICAgICAgICAgICAgaWYgKGhhc1ZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICB2YWx1ZUNvbnRlbnQgPSAoXG4gICAgICAgICAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAgICAgICAgIHthcnJvd31cbiAgICAgICAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17YWN0aXZlQ29sb3J9IGJvbGQ9e2lzQWN0aXZlfT5cbiAgICAgICAgICAgICAgICAgICAgICAgIHtnZXRFbnVtTGFiZWwoc2NoZW1hLCB2YWx1ZSBhcyBzdHJpbmcpfVxuICAgICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICB2YWx1ZUNvbnRlbnQgPSAoXG4gICAgICAgICAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAgICAgICAgIHthcnJvd31cbiAgICAgICAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAgICAgICAgICAgICAgICBub3Qgc2V0XG4gICAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHNjaGVtYS50eXBlID09PSAnYm9vbGVhbicpIHtcbiAgICAgICAgICAgICAgaWYgKGlzQWN0aXZlKSB7XG4gICAgICAgICAgICAgICAgdmFsdWVDb250ZW50ID0gaGFzVmFsdWUgPyAoXG4gICAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17YWN0aXZlQ29sb3J9IGJvbGQ+XG4gICAgICAgICAgICAgICAgICAgIHt2YWx1ZSA/IGZpZ3VyZXMuY2hlY2tib3hPbiA6IGZpZ3VyZXMuY2hlY2tib3hPZmZ9XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntmaWd1cmVzLmNoZWNrYm94T2ZmfTwvVGV4dD5cbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdmFsdWVDb250ZW50ID0gaGFzVmFsdWUgPyAoXG4gICAgICAgICAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgICAgICAgICAge3ZhbHVlID8gZmlndXJlcy5jaGVja2JveE9uIDogZmlndXJlcy5jaGVja2JveE9mZn1cbiAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3IgaXRhbGljPlxuICAgICAgICAgICAgICAgICAgICBub3Qgc2V0XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGlzVGV4dEZpZWxkKHNjaGVtYSkpIHtcbiAgICAgICAgICAgICAgaWYgKGlzQWN0aXZlKSB7XG4gICAgICAgICAgICAgICAgdmFsdWVDb250ZW50ID0gKFxuICAgICAgICAgICAgICAgICAgPFRleHRJbnB1dFxuICAgICAgICAgICAgICAgICAgICB2YWx1ZT17dGV4dElucHV0VmFsdWV9XG4gICAgICAgICAgICAgICAgICAgIG9uQ2hhbmdlPXtoYW5kbGVUZXh0SW5wdXRDaGFuZ2V9XG4gICAgICAgICAgICAgICAgICAgIG9uU3VibWl0PXtoYW5kbGVUZXh0SW5wdXRTdWJtaXR9XG4gICAgICAgICAgICAgICAgICAgIHBsYWNlaG9sZGVyPXtgVHlwZSBzb21ldGhpbmdcXHV7MjAyNn1gfVxuICAgICAgICAgICAgICAgICAgICBjb2x1bW5zPXtNYXRoLm1pbihjb2x1bW5zIC0gMjAsIDYwKX1cbiAgICAgICAgICAgICAgICAgICAgY3Vyc29yT2Zmc2V0PXt0ZXh0SW5wdXRDdXJzb3JPZmZzZXR9XG4gICAgICAgICAgICAgICAgICAgIG9uQ2hhbmdlQ3Vyc29yT2Zmc2V0PXtzZXRUZXh0SW5wdXRDdXJzb3JPZmZzZXR9XG4gICAgICAgICAgICAgICAgICAgIGZvY3VzXG4gICAgICAgICAgICAgICAgICAgIHNob3dDdXJzb3JcbiAgICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRpc3BsYXlWYWx1ZSA9XG4gICAgICAgICAgICAgICAgICBoYXNWYWx1ZSAmJiBpc0RhdGVUaW1lU2NoZW1hKHNjaGVtYSlcbiAgICAgICAgICAgICAgICAgICAgPyBmb3JtYXREYXRlRGlzcGxheShTdHJpbmcodmFsdWUpLCBzY2hlbWEpXG4gICAgICAgICAgICAgICAgICAgIDogU3RyaW5nKHZhbHVlKVxuICAgICAgICAgICAgICAgIHZhbHVlQ29udGVudCA9IGhhc1ZhbHVlID8gKFxuICAgICAgICAgICAgICAgICAgPFRleHQ+e2Rpc3BsYXlWYWx1ZX08L1RleHQ+XG4gICAgICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGl0YWxpYz5cbiAgICAgICAgICAgICAgICAgICAgbm90IHNldFxuICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdmFsdWVDb250ZW50ID0gaGFzVmFsdWUgPyAoXG4gICAgICAgICAgICAgICAgPFRleHQ+e1N0cmluZyh2YWx1ZSl9PC9UZXh0PlxuICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGl0YWxpYz5cbiAgICAgICAgICAgICAgICAgIG5vdCBzZXRcbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICAgPEJveCBrZXk9e25hbWV9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICA8Qm94IGdhcD17MX0+XG4gICAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj17c2VsZWN0aW9uQ29sb3J9PlxuICAgICAgICAgICAgICAgICAgICB7aXNBY3RpdmUgPyBmaWd1cmVzLnBvaW50ZXIgOiAnICd9XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICB7Y2hlY2tib3h9XG4gICAgICAgICAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgICAgICAgICB7bGFiZWx9XG4gICAgICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPXthY3RpdmVDb2xvcn0+OiA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICAgIHt2YWx1ZUNvbnRlbnR9XG4gICAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgICB7YWNjb3JkaW9uQ29udGVudH1cbiAgICAgICAgICAgICAgICB7c2NoZW1hLmRlc2NyaXB0aW9uICYmIChcbiAgICAgICAgICAgICAgICAgIDxCb3ggbWFyZ2luTGVmdD17Nn0+XG4gICAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntzY2hlbWEuZGVzY3JpcHRpb259PC9UZXh0PlxuICAgICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8Qm94IG1hcmdpbkxlZnQ9ezZ9IGhlaWdodD17MX0+XG4gICAgICAgICAgICAgICAgICB7ZXJyb3IgPyAoXG4gICAgICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIiBpdGFsaWM+XG4gICAgICAgICAgICAgICAgICAgICAge2Vycm9yfVxuICAgICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgICAgICA8VGV4dD4gPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICApXG4gICAgICAgICAgfSl9XG4gICAgICAgIHtoYXNGaWVsZHNCZWxvdyAmJiAoXG4gICAgICAgICAgPEJveCBtYXJnaW5MZWZ0PXsyfT5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICB7ZmlndXJlcy5hcnJvd0Rvd259IHtzY2hlbWFGaWVsZHMubGVuZ3RoIC0gc2Nyb2xsV2luZG93LmVuZH0gbW9yZVxuICAgICAgICAgICAgICBiZWxvd1xuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApfVxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT17YE1DUCBzZXJ2ZXIgXFx1MjAxYyR7c2VydmVyTmFtZX1cXHUyMDFkIHJlcXVlc3RzIHlvdXIgaW5wdXRgfVxuICAgICAgc3VidGl0bGU9e2BcXG4ke21lc3NhZ2V9YH1cbiAgICAgIGNvbG9yPVwicGVybWlzc2lvblwiXG4gICAgICBvbkNhbmNlbD17KCkgPT4gb25SZXNwb25zZSgnY2FuY2VsJyl9XG4gICAgICBpc0NhbmNlbEFjdGl2ZT17KCFjdXJyZW50RmllbGQgfHwgISFmb2N1c2VkQnV0dG9uKSAmJiAhZXhwYW5kZWRBY2NvcmRpb259XG4gICAgICBpbnB1dEd1aWRlPXtleGl0U3RhdGUgPT5cbiAgICAgICAgZXhpdFN0YXRlLnBlbmRpbmcgPyAoXG4gICAgICAgICAgPFRleHQ+UHJlc3Mge2V4aXRTdGF0ZS5rZXlOYW1lfSBhZ2FpbiB0byBleGl0PC9UZXh0PlxuICAgICAgICApIDogKFxuICAgICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgICBkZXNjcmlwdGlvbj1cImNhbmNlbFwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwi4oaR4oaTXCIgYWN0aW9uPVwibmF2aWdhdGVcIiAvPlxuICAgICAgICAgICAge2N1cnJlbnRGaWVsZCAmJiAoXG4gICAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkJhY2tzcGFjZVwiIGFjdGlvbj1cInVuc2V0XCIgLz5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgICB7Y3VycmVudEZpZWxkICYmIGN1cnJlbnRGaWVsZC5zY2hlbWEudHlwZSA9PT0gJ2Jvb2xlYW4nICYmIChcbiAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiU3BhY2VcIiBhY3Rpb249XCJ0b2dnbGVcIiAvPlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIHtjdXJyZW50RmllbGQgJiZcbiAgICAgICAgICAgICAgaXNFbnVtU2NoZW1hKGN1cnJlbnRGaWVsZC5zY2hlbWEpICYmXG4gICAgICAgICAgICAgIChleHBhbmRlZEFjY29yZGlvbiA/IChcbiAgICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJTcGFjZVwiIGFjdGlvbj1cInNlbGVjdFwiIC8+XG4gICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwi4oaSXCIgYWN0aW9uPVwiZXhwYW5kXCIgLz5cbiAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICB7Y3VycmVudEZpZWxkICYmXG4gICAgICAgICAgICAgIGlzTXVsdGlTZWxlY3RFbnVtU2NoZW1hKGN1cnJlbnRGaWVsZC5zY2hlbWEpICYmXG4gICAgICAgICAgICAgIChleHBhbmRlZEFjY29yZGlvbiA/IChcbiAgICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJTcGFjZVwiIGFjdGlvbj1cInRvZ2dsZVwiIC8+XG4gICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwi4oaSXCIgYWN0aW9uPVwiZXhwYW5kXCIgLz5cbiAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgPC9CeWxpbmU+XG4gICAgICAgIClcbiAgICAgIH1cbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAge3JlbmRlckZvcm1GaWVsZHMoKX1cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj5cbiAgICAgICAgICAgIHtmb2N1c2VkQnV0dG9uID09PSAnYWNjZXB0JyA/IGZpZ3VyZXMucG9pbnRlciA6ICcgJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHRcbiAgICAgICAgICAgIGJvbGQ9e2ZvY3VzZWRCdXR0b24gPT09ICdhY2NlcHQnfVxuICAgICAgICAgICAgY29sb3I9e2ZvY3VzZWRCdXR0b24gPT09ICdhY2NlcHQnID8gJ3N1Y2Nlc3MnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgZGltQ29sb3I9e2ZvY3VzZWRCdXR0b24gIT09ICdhY2NlcHQnfVxuICAgICAgICAgID5cbiAgICAgICAgICAgIHsnIEFjY2VwdCAgJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPlxuICAgICAgICAgICAge2ZvY3VzZWRCdXR0b24gPT09ICdkZWNsaW5lJyA/IGZpZ3VyZXMucG9pbnRlciA6ICcgJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHRcbiAgICAgICAgICAgIGJvbGQ9e2ZvY3VzZWRCdXR0b24gPT09ICdkZWNsaW5lJ31cbiAgICAgICAgICAgIGNvbG9yPXtmb2N1c2VkQnV0dG9uID09PSAnZGVjbGluZScgPyAnZXJyb3InIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgZGltQ29sb3I9e2ZvY3VzZWRCdXR0b24gIT09ICdkZWNsaW5lJ31cbiAgICAgICAgICA+XG4gICAgICAgICAgICB7JyBEZWNsaW5lJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgPC9EaWFsb2c+XG4gIClcbn1cblxuZnVuY3Rpb24gRWxpY2l0YXRpb25VUkxEaWFsb2coe1xuICBldmVudCxcbiAgb25SZXNwb25zZSxcbiAgb25XYWl0aW5nRGlzbWlzcyxcbn06IHtcbiAgZXZlbnQ6IEVsaWNpdGF0aW9uUmVxdWVzdEV2ZW50XG4gIG9uUmVzcG9uc2U6IFByb3BzWydvblJlc3BvbnNlJ11cbiAgb25XYWl0aW5nRGlzbWlzczogUHJvcHNbJ29uV2FpdGluZ0Rpc21pc3MnXVxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgc2VydmVyTmFtZSwgc2lnbmFsLCB3YWl0aW5nU3RhdGUgfSA9IGV2ZW50XG4gIGNvbnN0IHVybFBhcmFtcyA9IGV2ZW50LnBhcmFtcyBhcyBFbGljaXRSZXF1ZXN0VVJMUGFyYW1zXG4gIGNvbnN0IHsgbWVzc2FnZSwgdXJsIH0gPSB1cmxQYXJhbXNcbiAgY29uc3QgW3BoYXNlLCBzZXRQaGFzZV0gPSB1c2VTdGF0ZTwncHJvbXB0JyB8ICd3YWl0aW5nJz4oJ3Byb21wdCcpXG4gIGNvbnN0IHBoYXNlUmVmID0gdXNlUmVmPCdwcm9tcHQnIHwgJ3dhaXRpbmcnPigncHJvbXB0JylcbiAgY29uc3QgW2ZvY3VzZWRCdXR0b24sIHNldEZvY3VzZWRCdXR0b25dID0gdXNlU3RhdGU8XG4gICAgJ2FjY2VwdCcgfCAnZGVjbGluZScgfCAnb3BlbicgfCAnYWN0aW9uJyB8ICdjYW5jZWwnXG4gID4oJ2FjY2VwdCcpXG4gIGNvbnN0IHNob3dDYW5jZWwgPSB3YWl0aW5nU3RhdGU/LnNob3dDYW5jZWwgPz8gZmFsc2VcblxuICB1c2VOb3RpZnlBZnRlclRpbWVvdXQoXG4gICAgJ0NsYXVkZSBDb2RlIG5lZWRzIHlvdXIgaW5wdXQnLFxuICAgICdlbGljaXRhdGlvbl91cmxfZGlhbG9nJyxcbiAgKVxuICB1c2VSZWdpc3Rlck92ZXJsYXkoJ2VsaWNpdGF0aW9uLXVybCcpXG5cbiAgLy8gS2VlcCByZWZzIGluIHN5bmMgZm9yIHVzZSBpbiBhYm9ydCBoYW5kbGVyIChhdm9pZHMgcmUtcmVnaXN0ZXJpbmcgbGlzdGVuZXIpXG4gIHBoYXNlUmVmLmN1cnJlbnQgPSBwaGFzZVxuICBjb25zdCBvbldhaXRpbmdEaXNtaXNzUmVmID0gdXNlUmVmKG9uV2FpdGluZ0Rpc21pc3MpXG4gIG9uV2FpdGluZ0Rpc21pc3NSZWYuY3VycmVudCA9IG9uV2FpdGluZ0Rpc21pc3NcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IGhhbmRsZUFib3J0ID0gKCkgPT4ge1xuICAgICAgaWYgKHBoYXNlUmVmLmN1cnJlbnQgPT09ICd3YWl0aW5nJykge1xuICAgICAgICBvbldhaXRpbmdEaXNtaXNzUmVmLmN1cnJlbnQ/LignY2FuY2VsJylcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG9uUmVzcG9uc2UoJ2NhbmNlbCcpXG4gICAgICB9XG4gICAgfVxuICAgIGlmIChzaWduYWwuYWJvcnRlZCkge1xuICAgICAgaGFuZGxlQWJvcnQoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIHNpZ25hbC5hZGRFdmVudExpc3RlbmVyKCdhYm9ydCcsIGhhbmRsZUFib3J0KVxuICAgIHJldHVybiAoKSA9PiBzaWduYWwucmVtb3ZlRXZlbnRMaXN0ZW5lcignYWJvcnQnLCBoYW5kbGVBYm9ydClcbiAgfSwgW3NpZ25hbCwgb25SZXNwb25zZV0pXG5cbiAgLy8gUGFyc2UgVVJMIHRvIGhpZ2hsaWdodCB0aGUgZG9tYWluXG4gIGxldCBkb21haW4gPSAnJ1xuICBsZXQgdXJsQmVmb3JlRG9tYWluID0gJydcbiAgbGV0IHVybEFmdGVyRG9tYWluID0gJydcbiAgdHJ5IHtcbiAgICBjb25zdCBwYXJzZWQgPSBuZXcgVVJMKHVybClcbiAgICBkb21haW4gPSBwYXJzZWQuaG9zdG5hbWVcbiAgICBjb25zdCBkb21haW5TdGFydCA9IHVybC5pbmRleE9mKGRvbWFpbilcbiAgICB1cmxCZWZvcmVEb21haW4gPSB1cmwuc2xpY2UoMCwgZG9tYWluU3RhcnQpXG4gICAgdXJsQWZ0ZXJEb21haW4gPSB1cmwuc2xpY2UoZG9tYWluU3RhcnQgKyBkb21haW4ubGVuZ3RoKVxuICB9IGNhdGNoIHtcbiAgICBkb21haW4gPSB1cmxcbiAgfVxuXG4gIC8vIEF1dG8tZGlzbWlzcyB3aGVuIHRoZSBzZXJ2ZXIgc2VuZHMgYSBjb21wbGV0aW9uIG5vdGlmaWNhdGlvbiAoc2V0cyBjb21wbGV0ZWQgZmxhZylcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAocGhhc2UgPT09ICd3YWl0aW5nJyAmJiBldmVudC5jb21wbGV0ZWQpIHtcbiAgICAgIG9uV2FpdGluZ0Rpc21pc3M/LihzaG93Q2FuY2VsID8gJ3JldHJ5JyA6ICdkaXNtaXNzJylcbiAgICB9XG4gIH0sIFtwaGFzZSwgZXZlbnQuY29tcGxldGVkLCBvbldhaXRpbmdEaXNtaXNzLCBzaG93Q2FuY2VsXSlcblxuICBjb25zdCBoYW5kbGVBY2NlcHQgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgdm9pZCBvcGVuQnJvd3Nlcih1cmwpXG4gICAgb25SZXNwb25zZSgnYWNjZXB0JylcbiAgICBzZXRQaGFzZSgnd2FpdGluZycpXG4gICAgcGhhc2VSZWYuY3VycmVudCA9ICd3YWl0aW5nJ1xuICAgIHNldEZvY3VzZWRCdXR0b24oJ29wZW4nKVxuICB9LCBbb25SZXNwb25zZSwgdXJsXSlcblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL3ByZWZlci11c2Uta2V5YmluZGluZ3MgLS0gcmF3IGlucHV0IGZvciBidXR0b24gbmF2aWdhdGlvblxuICB1c2VJbnB1dCgoX2lucHV0LCBrZXkpID0+IHtcbiAgICBpZiAocGhhc2UgPT09ICdwcm9tcHQnKSB7XG4gICAgICBpZiAoa2V5LmxlZnRBcnJvdyB8fCBrZXkucmlnaHRBcnJvdykge1xuICAgICAgICBzZXRGb2N1c2VkQnV0dG9uKHByZXYgPT4gKHByZXYgPT09ICdhY2NlcHQnID8gJ2RlY2xpbmUnIDogJ2FjY2VwdCcpKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIGlmIChrZXkucmV0dXJuKSB7XG4gICAgICAgIGlmIChmb2N1c2VkQnV0dG9uID09PSAnYWNjZXB0Jykge1xuICAgICAgICAgIGhhbmRsZUFjY2VwdCgpXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgb25SZXNwb25zZSgnZGVjbGluZScpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gd2FpdGluZyBwaGFzZSDigJQgY3ljbGUgdGhyb3VnaCBidXR0b25zXG4gICAgICB0eXBlIEJ1dHRvbk5hbWUgPSAnYWNjZXB0JyB8ICdkZWNsaW5lJyB8ICdvcGVuJyB8ICdhY3Rpb24nIHwgJ2NhbmNlbCdcbiAgICAgIGNvbnN0IHdhaXRpbmdCdXR0b25zOiByZWFkb25seSBCdXR0b25OYW1lW10gPSBzaG93Q2FuY2VsXG4gICAgICAgID8gWydvcGVuJywgJ2FjdGlvbicsICdjYW5jZWwnXVxuICAgICAgICA6IFsnb3BlbicsICdhY3Rpb24nXVxuICAgICAgaWYgKGtleS5sZWZ0QXJyb3cgfHwga2V5LnJpZ2h0QXJyb3cpIHtcbiAgICAgICAgc2V0Rm9jdXNlZEJ1dHRvbihwcmV2ID0+IHtcbiAgICAgICAgICBjb25zdCBpZHggPSB3YWl0aW5nQnV0dG9ucy5pbmRleE9mKHByZXYpXG4gICAgICAgICAgY29uc3QgZGVsdGEgPSBrZXkucmlnaHRBcnJvdyA/IDEgOiAtMVxuICAgICAgICAgIHJldHVybiB3YWl0aW5nQnV0dG9uc1tcbiAgICAgICAgICAgIChpZHggKyBkZWx0YSArIHdhaXRpbmdCdXR0b25zLmxlbmd0aCkgJSB3YWl0aW5nQnV0dG9ucy5sZW5ndGhcbiAgICAgICAgICBdIVxuICAgICAgICB9KVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIGlmIChrZXkucmV0dXJuKSB7XG4gICAgICAgIGlmIChmb2N1c2VkQnV0dG9uID09PSAnb3BlbicpIHtcbiAgICAgICAgICB2b2lkIG9wZW5Ccm93c2VyKHVybClcbiAgICAgICAgfSBlbHNlIGlmIChmb2N1c2VkQnV0dG9uID09PSAnY2FuY2VsJykge1xuICAgICAgICAgIG9uV2FpdGluZ0Rpc21pc3M/LignY2FuY2VsJylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBvbldhaXRpbmdEaXNtaXNzPy4oc2hvd0NhbmNlbCA/ICdyZXRyeScgOiAnZGlzbWlzcycpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH0pXG5cbiAgaWYgKHBoYXNlID09PSAnd2FpdGluZycpIHtcbiAgICBjb25zdCBhY3Rpb25MYWJlbCA9IHdhaXRpbmdTdGF0ZT8uYWN0aW9uTGFiZWwgPz8gJ0NvbnRpbnVlIHdpdGhvdXQgd2FpdGluZydcbiAgICByZXR1cm4gKFxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT17YE1DUCBzZXJ2ZXIgXFx1MjAxYyR7c2VydmVyTmFtZX1cXHUyMDFkIFxcdTIwMTQgd2FpdGluZyBmb3IgY29tcGxldGlvbmB9XG4gICAgICAgIHN1YnRpdGxlPXtgXFxuJHttZXNzYWdlfWB9XG4gICAgICAgIGNvbG9yPVwicGVybWlzc2lvblwiXG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBvbldhaXRpbmdEaXNtaXNzPy4oJ2NhbmNlbCcpfVxuICAgICAgICBpc0NhbmNlbEFjdGl2ZVxuICAgICAgICBpbnB1dEd1aWRlPXtleGl0U3RhdGUgPT5cbiAgICAgICAgICBleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICAgIDxUZXh0PlByZXNzIHtleGl0U3RhdGUua2V5TmFtZX0gYWdhaW4gdG8gZXhpdDwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgICAgIGNvbnRleHQ9XCJDb25maXJtYXRpb25cIlxuICAgICAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbj1cImNhbmNlbFwiXG4gICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIlxcdTIxOTBcXHUyMTkyXCIgYWN0aW9uPVwic3dpdGNoXCIgLz5cbiAgICAgICAgICAgIDwvQnlsaW5lPlxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgIHt1cmxCZWZvcmVEb21haW59XG4gICAgICAgICAgICAgIDxUZXh0IGJvbGQ+e2RvbWFpbn08L1RleHQ+XG4gICAgICAgICAgICAgIHt1cmxBZnRlckRvbWFpbn1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAgICAgIFdhaXRpbmcgZm9yIHRoZSBzZXJ2ZXIgdG8gY29uZmlybSBjb21wbGV0aW9u4oCmXG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPEJveD5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VjY2Vzc1wiPlxuICAgICAgICAgICAgICB7Zm9jdXNlZEJ1dHRvbiA9PT0gJ29wZW4nID8gZmlndXJlcy5wb2ludGVyIDogJyAnfVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgYm9sZD17Zm9jdXNlZEJ1dHRvbiA9PT0gJ29wZW4nfVxuICAgICAgICAgICAgICBjb2xvcj17Zm9jdXNlZEJ1dHRvbiA9PT0gJ29wZW4nID8gJ3N1Y2Nlc3MnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgICBkaW1Db2xvcj17Zm9jdXNlZEJ1dHRvbiAhPT0gJ29wZW4nfVxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICB7JyBSZW9wZW4gVVJMICAnfVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+XG4gICAgICAgICAgICAgIHtmb2N1c2VkQnV0dG9uID09PSAnYWN0aW9uJyA/IGZpZ3VyZXMucG9pbnRlciA6ICcgJ31cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgIGJvbGQ9e2ZvY3VzZWRCdXR0b24gPT09ICdhY3Rpb24nfVxuICAgICAgICAgICAgICBjb2xvcj17Zm9jdXNlZEJ1dHRvbiA9PT0gJ2FjdGlvbicgPyAnc3VjY2VzcycgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICAgIGRpbUNvbG9yPXtmb2N1c2VkQnV0dG9uICE9PSAnYWN0aW9uJ31cbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAge2AgJHthY3Rpb25MYWJlbH1gfVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAge3Nob3dDYW5jZWwgJiYgKFxuICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgIDxUZXh0PiA8L1RleHQ+XG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPlxuICAgICAgICAgICAgICAgICAge2ZvY3VzZWRCdXR0b24gPT09ICdjYW5jZWwnID8gZmlndXJlcy5wb2ludGVyIDogJyAnfVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgICAgICAgYm9sZD17Zm9jdXNlZEJ1dHRvbiA9PT0gJ2NhbmNlbCd9XG4gICAgICAgICAgICAgICAgICBjb2xvcj17Zm9jdXNlZEJ1dHRvbiA9PT0gJ2NhbmNlbCcgPyAnZXJyb3InIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgICAgICAgZGltQ29sb3I9e2ZvY3VzZWRCdXR0b24gIT09ICdjYW5jZWwnfVxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgIHsnIENhbmNlbCd9XG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9EaWFsb2c+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT17YE1DUCBzZXJ2ZXIgXFx1MjAxYyR7c2VydmVyTmFtZX1cXHUyMDFkIHdhbnRzIHRvIG9wZW4gYSBVUkxgfVxuICAgICAgc3VidGl0bGU9e2BcXG4ke21lc3NhZ2V9YH1cbiAgICAgIGNvbG9yPVwicGVybWlzc2lvblwiXG4gICAgICBvbkNhbmNlbD17KCkgPT4gb25SZXNwb25zZSgnY2FuY2VsJyl9XG4gICAgICBpc0NhbmNlbEFjdGl2ZVxuICAgICAgaW5wdXRHdWlkZT17ZXhpdFN0YXRlID0+XG4gICAgICAgIGV4aXRTdGF0ZS5wZW5kaW5nID8gKFxuICAgICAgICAgIDxUZXh0PlByZXNzIHtleGl0U3RhdGUua2V5TmFtZX0gYWdhaW4gdG8gZXhpdDwvVGV4dD5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8QnlsaW5lPlxuICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgY29udGV4dD1cIkNvbmZpcm1hdGlvblwiXG4gICAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIlxcdTIxOTBcXHUyMTkyXCIgYWN0aW9uPVwic3dpdGNoXCIgLz5cbiAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgKVxuICAgICAgfVxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAge3VybEJlZm9yZURvbWFpbn1cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+e2RvbWFpbn08L1RleHQ+XG4gICAgICAgICAgICB7dXJsQWZ0ZXJEb21haW59XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj5cbiAgICAgICAgICAgIHtmb2N1c2VkQnV0dG9uID09PSAnYWNjZXB0JyA/IGZpZ3VyZXMucG9pbnRlciA6ICcgJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHRcbiAgICAgICAgICAgIGJvbGQ9e2ZvY3VzZWRCdXR0b24gPT09ICdhY2NlcHQnfVxuICAgICAgICAgICAgY29sb3I9e2ZvY3VzZWRCdXR0b24gPT09ICdhY2NlcHQnID8gJ3N1Y2Nlc3MnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgZGltQ29sb3I9e2ZvY3VzZWRCdXR0b24gIT09ICdhY2NlcHQnfVxuICAgICAgICAgID5cbiAgICAgICAgICAgIHsnIEFjY2VwdCAgJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPlxuICAgICAgICAgICAge2ZvY3VzZWRCdXR0b24gPT09ICdkZWNsaW5lJyA/IGZpZ3VyZXMucG9pbnRlciA6ICcgJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHRcbiAgICAgICAgICAgIGJvbGQ9e2ZvY3VzZWRCdXR0b24gPT09ICdkZWNsaW5lJ31cbiAgICAgICAgICAgIGNvbG9yPXtmb2N1c2VkQnV0dG9uID09PSAnZGVjbGluZScgPyAnZXJyb3InIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgZGltQ29sb3I9e2ZvY3VzZWRCdXR0b24gIT09ICdkZWNsaW5lJ31cbiAgICAgICAgICA+XG4gICAgICAgICAgICB7JyBEZWNsaW5lJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgPC9EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLGNBQ0VBLHVCQUF1QixFQUN2QkMsc0JBQXNCLEVBQ3RCQyxZQUFZLEVBQ1pDLHlCQUF5QixRQUNwQixvQ0FBb0M7QUFDM0MsT0FBT0MsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBT0MsS0FBSyxJQUFJQyxXQUFXLEVBQUVDLFNBQVMsRUFBRUMsT0FBTyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ2hGLFNBQVNDLGtCQUFrQixRQUFRLGlDQUFpQztBQUNwRSxTQUFTQyxxQkFBcUIsUUFBUSxzQ0FBc0M7QUFDNUUsU0FBU0MsZUFBZSxRQUFRLGdDQUFnQztBQUNoRTtBQUNBLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxFQUFFQyxRQUFRLFFBQVEsY0FBYztBQUNsRCxTQUFTQyxhQUFhLFFBQVEsb0NBQW9DO0FBQ2xFLGNBQWNDLHVCQUF1QixRQUFRLDBDQUEwQztBQUN2RixTQUFTQyxXQUFXLFFBQVEsd0JBQXdCO0FBQ3BELFNBQ0VDLFlBQVksRUFDWkMsYUFBYSxFQUNiQyxtQkFBbUIsRUFDbkJDLG9CQUFvQixFQUNwQkMsZ0JBQWdCLEVBQ2hCQyxZQUFZLEVBQ1pDLHVCQUF1QixFQUN2QkMsd0JBQXdCLEVBQ3hCQyw2QkFBNkIsUUFDeEIsMENBQTBDO0FBQ2pELFNBQVNDLE1BQU0sUUFBUSw0QkFBNEI7QUFDbkQsU0FBU0Msd0JBQXdCLFFBQVEsZ0NBQWdDO0FBQ3pFLFNBQVNDLE1BQU0sUUFBUSw0QkFBNEI7QUFDbkQsU0FBU0MsTUFBTSxRQUFRLDRCQUE0QjtBQUNuRCxTQUFTQyxvQkFBb0IsUUFBUSwwQ0FBMEM7QUFDL0UsT0FBT0MsU0FBUyxNQUFNLGlCQUFpQjtBQUV2QyxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsS0FBSyxFQUFFbEIsdUJBQXVCO0VBQzlCbUIsVUFBVSxFQUFFLENBQ1ZDLE1BQU0sRUFBRXBDLFlBQVksQ0FBQyxRQUFRLENBQUMsRUFDOUJxQyxPQUFpQyxDQUF6QixFQUFFckMsWUFBWSxDQUFDLFNBQVMsQ0FBQyxFQUNqQyxHQUFHLElBQUk7RUFDVDtFQUNBc0MsZ0JBQWdCLENBQUMsRUFBRSxDQUFDRixNQUFNLEVBQUUsU0FBUyxHQUFHLE9BQU8sR0FBRyxRQUFRLEVBQUUsR0FBRyxJQUFJO0FBQ3JFLENBQUM7QUFFRCxNQUFNRyxXQUFXLEdBQUdBLENBQUNDLENBQUMsRUFBRXZDLHlCQUF5QixLQUMvQyxDQUFDLFFBQVEsRUFBRSxRQUFRLEVBQUUsU0FBUyxDQUFDLENBQUN3QyxRQUFRLENBQUNELENBQUMsQ0FBQ0UsSUFBSSxDQUFDO0FBRWxELE1BQU1DLHVCQUF1QixHQUMzQiw4REFBOEQ7QUFDaEUsTUFBTUMsbUJBQW1CLEdBQUdBLENBQUNDLENBQUMsRUFBRSxNQUFNLEtBQ3BDLENBQUNBLENBQUMsR0FBRyxDQUFDLElBQUlGLHVCQUF1QixDQUFDRyxNQUFNOztBQUUxQztBQUNBLFNBQVNDLGNBQWNBLENBQUNDLEVBQUUsRUFBRTtFQUMxQkMsTUFBTSxFQUFFLE1BQU07RUFDZEMsS0FBSyxFQUFFQyxVQUFVLENBQUMsT0FBT0MsVUFBVSxDQUFDLEdBQUcsU0FBUztBQUNsRCxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUM7RUFDUEosRUFBRSxDQUFDQyxNQUFNLEdBQUcsRUFBRTtFQUNkRCxFQUFFLENBQUNFLEtBQUssR0FBR0csU0FBUztBQUN0Qjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQUFDLGlCQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQ0UsT0FBQUMsS0FBQSxFQUFBQyxRQUFBLElBQTBCbEQsUUFBUSxDQUFDLENBQUMsQ0FBQztFQUFBLElBQUFtRCxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFMLENBQUEsUUFBQU0sTUFBQSxDQUFBQyxHQUFBO0lBQzNCSCxFQUFBLEdBQUFBLENBQUE7TUFDUixNQUFBVCxLQUFBLEdBQWNhLFdBQVcsQ0FBQ0wsUUFBUSxFQUFFLEVBQUUsRUFBRWQsbUJBQW1CLENBQUM7TUFBQSxPQUNyRCxNQUFNb0IsYUFBYSxDQUFDZCxLQUFLLENBQUM7SUFBQSxDQUNsQztJQUFFVSxFQUFBLEtBQUU7SUFBQUwsQ0FBQSxNQUFBSSxFQUFBO0lBQUFKLENBQUEsTUFBQUssRUFBQTtFQUFBO0lBQUFELEVBQUEsR0FBQUosQ0FBQTtJQUFBSyxFQUFBLEdBQUFMLENBQUE7RUFBQTtFQUhMbEQsU0FBUyxDQUFDc0QsRUFHVCxFQUFFQyxFQUFFLENBQUM7RUFDd0IsTUFBQUssRUFBQSxHQUFBdEIsdUJBQXVCLENBQUNjLEtBQUssQ0FBQztFQUFBLElBQUFTLEVBQUE7RUFBQSxJQUFBWCxDQUFBLFFBQUFVLEVBQUE7SUFBckRDLEVBQUEsSUFBQyxJQUFJLENBQU8sS0FBUyxDQUFULFNBQVMsQ0FBRSxDQUFBRCxFQUE2QixDQUFFLEVBQXJELElBQUksQ0FBd0Q7SUFBQVYsQ0FBQSxNQUFBVSxFQUFBO0lBQUFWLENBQUEsTUFBQVcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVgsQ0FBQTtFQUFBO0VBQUEsT0FBN0RXLEVBQTZEO0FBQUE7O0FBR3RFO0FBQ0EsU0FBU0MsaUJBQWlCQSxDQUN4QkMsUUFBUSxFQUFFLE1BQU0sRUFDaEJDLE1BQU0sRUFBRXBFLHlCQUF5QixDQUNsQyxFQUFFLE1BQU0sQ0FBQztFQUNSLElBQUk7SUFDRixNQUFNcUUsSUFBSSxHQUFHLElBQUlDLElBQUksQ0FBQ0gsUUFBUSxDQUFDO0lBQy9CLElBQUlJLE1BQU0sQ0FBQ0MsS0FBSyxDQUFDSCxJQUFJLENBQUNJLE9BQU8sQ0FBQyxDQUFDLENBQUMsRUFBRSxPQUFPTixRQUFRO0lBQ2pELE1BQU1PLE1BQU0sR0FBRyxRQUFRLElBQUlOLE1BQU0sR0FBR0EsTUFBTSxDQUFDTSxNQUFNLEdBQUd0QixTQUFTO0lBQzdELElBQUlzQixNQUFNLEtBQUssV0FBVyxFQUFFO01BQzFCLE9BQU9MLElBQUksQ0FBQ00sa0JBQWtCLENBQUMsT0FBTyxFQUFFO1FBQ3RDQyxPQUFPLEVBQUUsT0FBTztRQUNoQkMsSUFBSSxFQUFFLFNBQVM7UUFDZkMsS0FBSyxFQUFFLE9BQU87UUFDZEMsR0FBRyxFQUFFLFNBQVM7UUFDZEMsSUFBSSxFQUFFLFNBQVM7UUFDZkMsTUFBTSxFQUFFLFNBQVM7UUFDakJDLFlBQVksRUFBRTtNQUNoQixDQUFDLENBQUM7SUFDSjtJQUNBO0lBQ0EsTUFBTUMsS0FBSyxHQUFHaEIsUUFBUSxDQUFDaUIsS0FBSyxDQUFDLEdBQUcsQ0FBQztJQUNqQyxJQUFJRCxLQUFLLENBQUN0QyxNQUFNLEtBQUssQ0FBQyxFQUFFO01BQ3RCLE1BQU13QyxLQUFLLEdBQUcsSUFBSWYsSUFBSSxDQUNwQkMsTUFBTSxDQUFDWSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFDaEJaLE1BQU0sQ0FBQ1ksS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUNwQlosTUFBTSxDQUFDWSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQ2pCLENBQUM7TUFDRCxPQUFPRSxLQUFLLENBQUNWLGtCQUFrQixDQUFDLE9BQU8sRUFBRTtRQUN2Q0MsT0FBTyxFQUFFLE9BQU87UUFDaEJDLElBQUksRUFBRSxTQUFTO1FBQ2ZDLEtBQUssRUFBRSxPQUFPO1FBQ2RDLEdBQUcsRUFBRTtNQUNQLENBQUMsQ0FBQztJQUNKO0lBQ0EsT0FBT1osUUFBUTtFQUNqQixDQUFDLENBQUMsTUFBTTtJQUNOLE9BQU9BLFFBQVE7RUFDakI7QUFDRjtBQUVBLE9BQU8sU0FBQW1CLGtCQUFBNUIsRUFBQTtFQUFBLE1BQUFKLENBQUEsR0FBQUMsRUFBQTtFQUEyQjtJQUFBdEIsS0FBQTtJQUFBQyxVQUFBO0lBQUFHO0VBQUEsSUFBQXFCLEVBSTFCO0VBQ04sSUFBSXpCLEtBQUssQ0FBQXNELE1BQU8sQ0FBQUMsSUFBSyxLQUFLLEtBQUs7SUFBQSxJQUFBN0IsRUFBQTtJQUFBLElBQUFMLENBQUEsUUFBQXJCLEtBQUEsSUFBQXFCLENBQUEsUUFBQXBCLFVBQUEsSUFBQW9CLENBQUEsUUFBQWpCLGdCQUFBO01BRTNCc0IsRUFBQSxJQUFDLG9CQUFvQixDQUNaMUIsS0FBSyxDQUFMQSxNQUFJLENBQUMsQ0FDQUMsVUFBVSxDQUFWQSxXQUFTLENBQUMsQ0FDSkcsZ0JBQWdCLENBQWhCQSxpQkFBZSxDQUFDLEdBQ2xDO01BQUFpQixDQUFBLE1BQUFyQixLQUFBO01BQUFxQixDQUFBLE1BQUFwQixVQUFBO01BQUFvQixDQUFBLE1BQUFqQixnQkFBQTtNQUFBaUIsQ0FBQSxNQUFBSyxFQUFBO0lBQUE7TUFBQUEsRUFBQSxHQUFBTCxDQUFBO0lBQUE7SUFBQSxPQUpGSyxFQUlFO0VBQUE7RUFFTCxJQUFBQSxFQUFBO0VBQUEsSUFBQUwsQ0FBQSxRQUFBckIsS0FBQSxJQUFBcUIsQ0FBQSxRQUFBcEIsVUFBQTtJQUVNeUIsRUFBQSxJQUFDLHFCQUFxQixDQUFRMUIsS0FBSyxDQUFMQSxNQUFJLENBQUMsQ0FBY0MsVUFBVSxDQUFWQSxXQUFTLENBQUMsR0FBSTtJQUFBb0IsQ0FBQSxNQUFBckIsS0FBQTtJQUFBcUIsQ0FBQSxNQUFBcEIsVUFBQTtJQUFBb0IsQ0FBQSxNQUFBSyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBTCxDQUFBO0VBQUE7RUFBQSxPQUEvREssRUFBK0Q7QUFBQTtBQUd4RSxTQUFTOEIscUJBQXFCQSxDQUFDO0VBQzdCeEQsS0FBSztFQUNMQztBQUlGLENBSEMsRUFBRTtFQUNERCxLQUFLLEVBQUVsQix1QkFBdUI7RUFDOUJtQixVQUFVLEVBQUVGLEtBQUssQ0FBQyxZQUFZLENBQUM7QUFDakMsQ0FBQyxDQUFDLEVBQUU5QixLQUFLLENBQUN3RixTQUFTLENBQUM7RUFDbEIsTUFBTTtJQUFFQyxVQUFVO0lBQUVDO0VBQU8sQ0FBQyxHQUFHM0QsS0FBSztFQUNwQyxNQUFNNEQsT0FBTyxHQUFHNUQsS0FBSyxDQUFDc0QsTUFBTSxJQUFJMUYsdUJBQXVCO0VBQ3ZELE1BQU07SUFBRWlHLE9BQU87SUFBRUM7RUFBZ0IsQ0FBQyxHQUFHRixPQUFPO0VBQzVDLE1BQU1HLFNBQVMsR0FBR0MsTUFBTSxDQUFDQyxJQUFJLENBQUNILGVBQWUsQ0FBQ0ksVUFBVSxDQUFDLENBQUN0RCxNQUFNLEdBQUcsQ0FBQztFQUNwRSxNQUFNLENBQUN1RCxhQUFhLEVBQUVDLGdCQUFnQixDQUFDLEdBQUc5RixRQUFRLENBQ2hELFFBQVEsR0FBRyxTQUFTLEdBQUcsSUFBSSxDQUM1QixDQUFDeUYsU0FBUyxHQUFHLElBQUksR0FBRyxRQUFRLENBQUM7RUFDOUIsTUFBTSxDQUFDTSxVQUFVLEVBQUVDLGFBQWEsQ0FBQyxHQUFHaEcsUUFBUSxDQUMxQ2lHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxHQUFHLE1BQU0sR0FBRyxPQUFPLEdBQUcsTUFBTSxFQUFFLENBQUMsQ0FDckQsQ0FBQyxNQUFNO0lBQ04sTUFBTUMsYUFBYSxFQUFFRCxNQUFNLENBQUMsTUFBTSxFQUFFLE1BQU0sR0FBRyxNQUFNLEdBQUcsT0FBTyxHQUFHLE1BQU0sRUFBRSxDQUFDLEdBQ3ZFLENBQUMsQ0FBQztJQUNKLElBQUlULGVBQWUsQ0FBQ0ksVUFBVSxFQUFFO01BQzlCLEtBQUssTUFBTSxDQUFDTyxRQUFRLEVBQUVDLFVBQVUsQ0FBQyxJQUFJVixNQUFNLENBQUNXLE9BQU8sQ0FDakRiLGVBQWUsQ0FBQ0ksVUFDbEIsQ0FBQyxFQUFFO1FBQ0QsSUFBSSxPQUFPUSxVQUFVLEtBQUssUUFBUSxJQUFJQSxVQUFVLEtBQUssSUFBSSxFQUFFO1VBQ3pELElBQUlBLFVBQVUsQ0FBQ0UsT0FBTyxLQUFLekQsU0FBUyxFQUFFO1lBQ3BDcUQsYUFBYSxDQUFDQyxRQUFRLENBQUMsR0FBR0MsVUFBVSxDQUFDRSxPQUFPO1VBQzlDO1FBQ0Y7TUFDRjtJQUNGO0lBQ0EsT0FBT0osYUFBYTtFQUN0QixDQUFDLENBQUM7RUFFRixNQUFNLENBQUNLLGdCQUFnQixFQUFFQyxtQkFBbUIsQ0FBQyxHQUFHeEcsUUFBUSxDQUN0RGlHLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDLENBQ3ZCLENBQUMsTUFBTTtJQUNOLE1BQU1RLGFBQWEsRUFBRVIsTUFBTSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDaEQsS0FBSyxNQUFNLENBQUNFLFVBQVEsRUFBRUMsWUFBVSxDQUFDLElBQUlWLE1BQU0sQ0FBQ1csT0FBTyxDQUNqRGIsZUFBZSxDQUFDSSxVQUNsQixDQUFDLEVBQUU7TUFDRCxJQUFJN0QsV0FBVyxDQUFDcUUsWUFBVSxDQUFDLElBQUlBLFlBQVUsRUFBRUUsT0FBTyxLQUFLekQsU0FBUyxFQUFFO1FBQ2hFLE1BQU02RCxVQUFVLEdBQUd6Rix3QkFBd0IsQ0FDekMwRixNQUFNLENBQUNQLFlBQVUsQ0FBQ0UsT0FBTyxDQUFDLEVBQzFCRixZQUNGLENBQUM7UUFDRCxJQUFJLENBQUNNLFVBQVUsQ0FBQ0UsT0FBTyxJQUFJRixVQUFVLENBQUNHLEtBQUssRUFBRTtVQUMzQ0osYUFBYSxDQUFDTixVQUFRLENBQUMsR0FBR08sVUFBVSxDQUFDRyxLQUFLO1FBQzVDO01BQ0Y7SUFDRjtJQUNBLE9BQU9KLGFBQWE7RUFDdEIsQ0FBQyxDQUFDO0VBRUY1RyxTQUFTLENBQUMsTUFBTTtJQUNkLElBQUksQ0FBQ3dGLE1BQU0sRUFBRTtJQUViLE1BQU15QixXQUFXLEdBQUdBLENBQUEsS0FBTTtNQUN4Qm5GLFVBQVUsQ0FBQyxRQUFRLENBQUM7SUFDdEIsQ0FBQztJQUVELElBQUkwRCxNQUFNLENBQUMwQixPQUFPLEVBQUU7TUFDbEJELFdBQVcsQ0FBQyxDQUFDO01BQ2I7SUFDRjtJQUVBekIsTUFBTSxDQUFDMkIsZ0JBQWdCLENBQUMsT0FBTyxFQUFFRixXQUFXLENBQUM7SUFDN0MsT0FBTyxNQUFNO01BQ1h6QixNQUFNLENBQUM0QixtQkFBbUIsQ0FBQyxPQUFPLEVBQUVILFdBQVcsQ0FBQztJQUNsRCxDQUFDO0VBQ0gsQ0FBQyxFQUFFLENBQUN6QixNQUFNLEVBQUUxRCxVQUFVLENBQUMsQ0FBQztFQUV4QixNQUFNdUYsWUFBWSxHQUFHcEgsT0FBTyxDQUFDLE1BQU07SUFDakMsTUFBTXFILGNBQWMsR0FBRzNCLGVBQWUsQ0FBQzRCLFFBQVEsSUFBSSxFQUFFO0lBQ3JELE9BQU8xQixNQUFNLENBQUNXLE9BQU8sQ0FBQ2IsZUFBZSxDQUFDSSxVQUFVLENBQUMsQ0FBQ3lCLEdBQUcsQ0FBQyxDQUFDLENBQUNDLElBQUksRUFBRXpELE1BQU0sQ0FBQyxNQUFNO01BQ3pFeUQsSUFBSTtNQUNKekQsTUFBTTtNQUNOMEQsVUFBVSxFQUFFSixjQUFjLENBQUNsRixRQUFRLENBQUNxRixJQUFJO0lBQzFDLENBQUMsQ0FBQyxDQUFDO0VBQ0wsQ0FBQyxFQUFFLENBQUM5QixlQUFlLENBQUMsQ0FBQztFQUVyQixNQUFNLENBQUNnQyxpQkFBaUIsRUFBRUMsb0JBQW9CLENBQUMsR0FBR3pILFFBQVEsQ0FDeEQsTUFBTSxHQUFHLFNBQVMsQ0FDbkIsQ0FBQ3lGLFNBQVMsR0FBRyxDQUFDLEdBQUc1QyxTQUFTLENBQUM7RUFDNUIsTUFBTSxDQUFDNkUsY0FBYyxFQUFFQyxpQkFBaUIsQ0FBQyxHQUFHM0gsUUFBUSxDQUFDLE1BQU07SUFDekQ7SUFDQSxNQUFNNEgsVUFBVSxHQUFHVixZQUFZLENBQUMsQ0FBQyxDQUFDO0lBQ2xDLElBQUlVLFVBQVUsSUFBSTdGLFdBQVcsQ0FBQzZGLFVBQVUsQ0FBQy9ELE1BQU0sQ0FBQyxFQUFFO01BQ2hELE1BQU1nRSxHQUFHLEdBQUc5QixVQUFVLENBQUM2QixVQUFVLENBQUNOLElBQUksQ0FBQztNQUN2QyxJQUFJTyxHQUFHLEtBQUtoRixTQUFTLEVBQUUsT0FBTyxFQUFFO01BQ2hDLE9BQU84RCxNQUFNLENBQUNrQixHQUFHLENBQUM7SUFDcEI7SUFDQSxPQUFPLEVBQUU7RUFDWCxDQUFDLENBQUM7RUFDRixNQUFNLENBQUNDLHFCQUFxQixFQUFFQyx3QkFBd0IsQ0FBQyxHQUFHL0gsUUFBUSxDQUNoRTBILGNBQWMsQ0FBQ3BGLE1BQ2pCLENBQUM7RUFDRCxNQUFNLENBQUMwRixlQUFlLEVBQUVDLGtCQUFrQixDQUFDLEdBQUdqSSxRQUFRLENBQUNrSSxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FDakUsTUFBTSxJQUFJQSxHQUFHLENBQUMsQ0FDaEIsQ0FBQztFQUNEO0VBQ0EsTUFBTSxDQUFDQyxpQkFBaUIsRUFBRUMsb0JBQW9CLENBQUMsR0FBR3BJLFFBQVEsQ0FDeEQsTUFBTSxHQUFHLFNBQVMsQ0FDbkIsQ0FBQyxDQUFDO0VBQ0gsTUFBTSxDQUFDcUksb0JBQW9CLEVBQUVDLHVCQUF1QixDQUFDLEdBQUd0SSxRQUFRLENBQUMsQ0FBQyxDQUFDO0VBRW5FLE1BQU11SSxlQUFlLEdBQUd4SSxNQUFNLENBQUM0QyxVQUFVLENBQUMsT0FBT0MsVUFBVSxDQUFDLEdBQUcsU0FBUyxDQUFDLENBQ3ZFQyxTQUNGLENBQUM7RUFDRCxNQUFNMkYsZUFBZSxHQUFHekksTUFBTSxDQUFDMEksR0FBRyxDQUFDLE1BQU0sRUFBRUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxJQUFJRCxHQUFHLENBQUMsQ0FBQyxDQUFDO0VBQ3ZFLE1BQU1FLGdCQUFnQixHQUFHNUksTUFBTSxDQUFDO0lBQzlCMEMsTUFBTSxFQUFFLEVBQUU7SUFDVkMsS0FBSyxFQUFFRyxTQUFTLElBQUlGLFVBQVUsQ0FBQyxPQUFPQyxVQUFVLENBQUMsR0FBRztFQUN0RCxDQUFDLENBQUM7O0VBRUY7RUFDQTtFQUNBO0VBQ0EvQyxTQUFTLENBQ1AsTUFBTSxNQUFNO0lBQ1YsSUFBSTBJLGVBQWUsQ0FBQ0ssT0FBTyxLQUFLL0YsU0FBUyxFQUFFO01BQ3pDZ0csWUFBWSxDQUFDTixlQUFlLENBQUNLLE9BQU8sQ0FBQztJQUN2QztJQUNBLE1BQU1wRyxFQUFFLEdBQUdtRyxnQkFBZ0IsQ0FBQ0MsT0FBTztJQUNuQyxJQUFJcEcsRUFBRSxDQUFDRSxLQUFLLEtBQUtHLFNBQVMsRUFBRTtNQUMxQmdHLFlBQVksQ0FBQ3JHLEVBQUUsQ0FBQ0UsS0FBSyxDQUFDO0lBQ3hCO0lBQ0EsS0FBSyxNQUFNb0csVUFBVSxJQUFJTixlQUFlLENBQUNJLE9BQU8sQ0FBQ0csTUFBTSxDQUFDLENBQUMsRUFBRTtNQUN6REQsVUFBVSxDQUFDRSxLQUFLLENBQUMsQ0FBQztJQUNwQjtJQUNBUixlQUFlLENBQUNJLE9BQU8sQ0FBQ0ssS0FBSyxDQUFDLENBQUM7RUFDakMsQ0FBQyxFQUNELEVBQ0YsQ0FBQztFQUVELE1BQU07SUFBRUMsT0FBTztJQUFFQztFQUFLLENBQUMsR0FBR2hKLGVBQWUsQ0FBQyxDQUFDO0VBRTNDLE1BQU1pSixZQUFZLEdBQ2hCNUIsaUJBQWlCLEtBQUszRSxTQUFTLEdBQzNCcUUsWUFBWSxDQUFDTSxpQkFBaUIsQ0FBQyxHQUMvQjNFLFNBQVM7RUFDZixNQUFNd0csa0JBQWtCLEdBQ3RCRCxZQUFZLEtBQUt2RyxTQUFTLElBQzFCZCxXQUFXLENBQUNxSCxZQUFZLENBQUN2RixNQUFNLENBQUMsSUFDaEMsQ0FBQzlDLFlBQVksQ0FBQ3FJLFlBQVksQ0FBQ3ZGLE1BQU0sQ0FBQzs7RUFFcEM7RUFDQSxNQUFNeUYsa0JBQWtCLEdBQUdELGtCQUFrQixJQUFJLENBQUN4RCxhQUFhO0VBRS9ENUYsa0JBQWtCLENBQUMsYUFBYSxDQUFDO0VBQ2pDQyxxQkFBcUIsQ0FBQyw4QkFBOEIsRUFBRSxvQkFBb0IsQ0FBQzs7RUFFM0U7RUFDQSxNQUFNcUosYUFBYSxHQUFHM0osV0FBVyxDQUMvQixDQUFDNEosVUFBVSxFQUFFLE1BQU0sR0FBRyxTQUFTLEtBQUs7SUFDbEMsSUFBSUEsVUFBVSxLQUFLM0csU0FBUyxFQUFFO01BQzVCOEUsaUJBQWlCLENBQUMsRUFBRSxDQUFDO01BQ3JCSSx3QkFBd0IsQ0FBQyxDQUFDLENBQUM7TUFDM0I7SUFDRjtJQUNBLE1BQU0wQixLQUFLLEdBQUd2QyxZQUFZLENBQUNzQyxVQUFVLENBQUM7SUFDdEMsSUFBSUMsS0FBSyxJQUFJMUgsV0FBVyxDQUFDMEgsS0FBSyxDQUFDNUYsTUFBTSxDQUFDLElBQUksQ0FBQzlDLFlBQVksQ0FBQzBJLEtBQUssQ0FBQzVGLE1BQU0sQ0FBQyxFQUFFO01BQ3JFLE1BQU1nRSxLQUFHLEdBQUc5QixVQUFVLENBQUMwRCxLQUFLLENBQUNuQyxJQUFJLENBQUM7TUFDbEMsTUFBTW9DLElBQUksR0FBRzdCLEtBQUcsS0FBS2hGLFNBQVMsR0FBRzhELE1BQU0sQ0FBQ2tCLEtBQUcsQ0FBQyxHQUFHLEVBQUU7TUFDakRGLGlCQUFpQixDQUFDK0IsSUFBSSxDQUFDO01BQ3ZCM0Isd0JBQXdCLENBQUMyQixJQUFJLENBQUNwSCxNQUFNLENBQUM7SUFDdkM7RUFDRixDQUFDLEVBQ0QsQ0FBQzRFLFlBQVksRUFBRW5CLFVBQVUsQ0FDM0IsQ0FBQztFQUVELFNBQVM0RCxtQkFBbUJBLENBQzFCQyxTQUFTLEVBQUUsTUFBTSxFQUNqQi9GLFFBQU0sRUFBRXBFLHlCQUF5QixFQUNqQztJQUNBLElBQUksQ0FBQ3VCLHVCQUF1QixDQUFDNkMsUUFBTSxDQUFDLEVBQUU7SUFDdEMsTUFBTWdHLFFBQVEsR0FBSTlELFVBQVUsQ0FBQzZELFNBQVMsQ0FBQyxJQUFJLE1BQU0sRUFBRSxHQUFHLFNBQVMsSUFBSyxFQUFFO0lBQ3RFLE1BQU1FLGFBQWEsR0FDakI1QyxZQUFZLENBQUM2QyxJQUFJLENBQUMxSCxDQUFDLElBQUlBLENBQUMsQ0FBQ2lGLElBQUksS0FBS3NDLFNBQVMsQ0FBQyxFQUFFckMsVUFBVSxJQUFJLEtBQUs7SUFDbkUsTUFBTXlDLEdBQUcsR0FBR25HLFFBQU0sQ0FBQ29HLFFBQVE7SUFDM0IsTUFBTUMsR0FBRyxHQUFHckcsUUFBTSxDQUFDc0csUUFBUTtJQUMzQjtJQUNBLElBQ0VILEdBQUcsS0FBS25ILFNBQVMsSUFDakJnSCxRQUFRLENBQUN2SCxNQUFNLEdBQUcwSCxHQUFHLEtBQ3BCSCxRQUFRLENBQUN2SCxNQUFNLEdBQUcsQ0FBQyxJQUFJd0gsYUFBYSxDQUFDLEVBQ3RDO01BQ0FNLHFCQUFxQixDQUNuQlIsU0FBUyxFQUNULG1CQUFtQkksR0FBRyxJQUFJN0ksTUFBTSxDQUFDNkksR0FBRyxFQUFFLE1BQU0sQ0FBQyxFQUMvQyxDQUFDO0lBQ0gsQ0FBQyxNQUFNLElBQUlFLEdBQUcsS0FBS3JILFNBQVMsSUFBSWdILFFBQVEsQ0FBQ3ZILE1BQU0sR0FBRzRILEdBQUcsRUFBRTtNQUNyREUscUJBQXFCLENBQ25CUixTQUFTLEVBQ1Qsa0JBQWtCTSxHQUFHLElBQUkvSSxNQUFNLENBQUMrSSxHQUFHLEVBQUUsTUFBTSxDQUFDLEVBQzlDLENBQUM7SUFDSCxDQUFDLE1BQU07TUFDTEUscUJBQXFCLENBQUNSLFNBQVMsQ0FBQztJQUNsQztFQUNGO0VBRUEsU0FBU1MsZ0JBQWdCQSxDQUFDQyxTQUFTLEVBQUUsSUFBSSxHQUFHLE1BQU0sQ0FBQyxFQUFFLElBQUksQ0FBQztJQUN4RDtJQUNBLElBQUlsQixZQUFZLElBQUlwSSx1QkFBdUIsQ0FBQ29JLFlBQVksQ0FBQ3ZGLE1BQU0sQ0FBQyxFQUFFO01BQ2hFOEYsbUJBQW1CLENBQUNQLFlBQVksQ0FBQzlCLElBQUksRUFBRThCLFlBQVksQ0FBQ3ZGLE1BQU0sQ0FBQztNQUMzRHVFLG9CQUFvQixDQUFDdkYsU0FBUyxDQUFDO0lBQ2pDLENBQUMsTUFBTSxJQUFJdUcsWUFBWSxJQUFJckksWUFBWSxDQUFDcUksWUFBWSxDQUFDdkYsTUFBTSxDQUFDLEVBQUU7TUFDNUR1RSxvQkFBb0IsQ0FBQ3ZGLFNBQVMsQ0FBQztJQUNqQzs7SUFFQTtJQUNBLElBQUl5RyxrQkFBa0IsSUFBSUYsWUFBWSxFQUFFO01BQ3RDbUIsZUFBZSxDQUFDbkIsWUFBWSxDQUFDOUIsSUFBSSxFQUFFOEIsWUFBWSxDQUFDdkYsTUFBTSxFQUFFNkQsY0FBYyxDQUFDOztNQUV2RTtNQUNBLElBQUlhLGVBQWUsQ0FBQ0ssT0FBTyxLQUFLL0YsU0FBUyxFQUFFO1FBQ3pDZ0csWUFBWSxDQUFDTixlQUFlLENBQUNLLE9BQU8sQ0FBQztRQUNyQ0wsZUFBZSxDQUFDSyxPQUFPLEdBQUcvRixTQUFTO01BQ3JDOztNQUVBO01BQ0EsSUFDRS9CLGdCQUFnQixDQUFDc0ksWUFBWSxDQUFDdkYsTUFBTSxDQUFDLElBQ3JDNkQsY0FBYyxDQUFDOEMsSUFBSSxDQUFDLENBQUMsS0FBSyxFQUFFLElBQzVCakUsZ0JBQWdCLENBQUM2QyxZQUFZLENBQUM5QixJQUFJLENBQUMsRUFDbkM7UUFDQW1ELGlCQUFpQixDQUNmckIsWUFBWSxDQUFDOUIsSUFBSSxFQUNqQjhCLFlBQVksQ0FBQ3ZGLE1BQU0sRUFDbkI2RCxjQUNGLENBQUM7TUFDSDtJQUNGOztJQUVBO0lBQ0EsTUFBTWdELFNBQVMsR0FBR3hELFlBQVksQ0FBQzVFLE1BQU0sR0FBRyxDQUFDO0lBQ3pDLE1BQU1xSSxLQUFLLEdBQ1RuRCxpQkFBaUIsS0FDaEIzQixhQUFhLEtBQUssUUFBUSxHQUN2QnFCLFlBQVksQ0FBQzVFLE1BQU0sR0FDbkJ1RCxhQUFhLEtBQUssU0FBUyxHQUN6QnFCLFlBQVksQ0FBQzVFLE1BQU0sR0FBRyxDQUFDLEdBQ3ZCTyxTQUFTLENBQUM7SUFDbEIsTUFBTStILFNBQVMsR0FDYkQsS0FBSyxLQUFLOUgsU0FBUyxHQUNmLENBQUM4SCxLQUFLLElBQUlMLFNBQVMsS0FBSyxJQUFJLEdBQUdJLFNBQVMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUlBLFNBQVMsR0FDOUQsQ0FBQztJQUNQLElBQUlFLFNBQVMsR0FBRzFELFlBQVksQ0FBQzVFLE1BQU0sRUFBRTtNQUNuQ21GLG9CQUFvQixDQUFDbUQsU0FBUyxDQUFDO01BQy9COUUsZ0JBQWdCLENBQUMsSUFBSSxDQUFDO01BQ3RCeUQsYUFBYSxDQUFDcUIsU0FBUyxDQUFDO0lBQzFCLENBQUMsTUFBTTtNQUNMbkQsb0JBQW9CLENBQUM1RSxTQUFTLENBQUM7TUFDL0JpRCxnQkFBZ0IsQ0FBQzhFLFNBQVMsS0FBSzFELFlBQVksQ0FBQzVFLE1BQU0sR0FBRyxRQUFRLEdBQUcsU0FBUyxDQUFDO01BQzFFcUYsaUJBQWlCLENBQUMsRUFBRSxDQUFDO0lBQ3ZCO0VBQ0Y7RUFFQSxTQUFTa0QsUUFBUUEsQ0FDZmpCLFdBQVMsRUFBRSxNQUFNLEVBQ2pCa0IsS0FBSyxFQUFFLE1BQU0sR0FBRyxNQUFNLEdBQUcsT0FBTyxHQUFHLE1BQU0sRUFBRSxHQUFHLFNBQVMsRUFDdkQ7SUFDQTlFLGFBQWEsQ0FBQytFLElBQUksSUFBSTtNQUNwQixNQUFNQyxJQUFJLEdBQUc7UUFBRSxHQUFHRDtNQUFLLENBQUM7TUFDeEIsSUFBSUQsS0FBSyxLQUFLakksU0FBUyxFQUFFO1FBQ3ZCLE9BQU9tSSxJQUFJLENBQUNwQixXQUFTLENBQUM7TUFDeEIsQ0FBQyxNQUFNO1FBQ0xvQixJQUFJLENBQUNwQixXQUFTLENBQUMsR0FBR2tCLEtBQUs7TUFDekI7TUFDQSxPQUFPRSxJQUFJO0lBQ2IsQ0FBQyxDQUFDO0lBQ0Y7SUFDQSxJQUNFRixLQUFLLEtBQUtqSSxTQUFTLElBQ25CMEQsZ0JBQWdCLENBQUNxRCxXQUFTLENBQUMsS0FBSyx3QkFBd0IsRUFDeEQ7TUFDQVEscUJBQXFCLENBQUNSLFdBQVMsQ0FBQztJQUNsQztFQUNGO0VBRUEsU0FBU1EscUJBQXFCQSxDQUFDUixXQUFTLEVBQUUsTUFBTSxFQUFFL0MsS0FBYyxDQUFSLEVBQUUsTUFBTSxFQUFFO0lBQ2hFTCxtQkFBbUIsQ0FBQ3VFLE1BQUksSUFBSTtNQUMxQixNQUFNQyxNQUFJLEdBQUc7UUFBRSxHQUFHRDtNQUFLLENBQUM7TUFDeEIsSUFBSWxFLEtBQUssRUFBRTtRQUNUbUUsTUFBSSxDQUFDcEIsV0FBUyxDQUFDLEdBQUcvQyxLQUFLO01BQ3pCLENBQUMsTUFBTTtRQUNMLE9BQU9tRSxNQUFJLENBQUNwQixXQUFTLENBQUM7TUFDeEI7TUFDQSxPQUFPb0IsTUFBSTtJQUNiLENBQUMsQ0FBQztFQUNKO0VBRUEsU0FBU0MsVUFBVUEsQ0FBQ3JCLFdBQVMsRUFBRSxNQUFNLEVBQUU7SUFDckMsSUFBSSxDQUFDQSxXQUFTLEVBQUU7SUFDaEJpQixRQUFRLENBQUNqQixXQUFTLEVBQUUvRyxTQUFTLENBQUM7SUFDOUJ1SCxxQkFBcUIsQ0FBQ1IsV0FBUyxDQUFDO0lBQ2hDakMsaUJBQWlCLENBQUMsRUFBRSxDQUFDO0lBQ3JCSSx3QkFBd0IsQ0FBQyxDQUFDLENBQUM7RUFDN0I7RUFFQSxTQUFTd0MsZUFBZUEsQ0FDdEJYLFdBQVMsRUFBRSxNQUFNLEVBQ2pCL0YsUUFBTSxFQUFFcEUseUJBQXlCLEVBQ2pDcUwsT0FBSyxFQUFFLE1BQU0sRUFDYjtJQUNBLE1BQU1JLFlBQVksR0FBR0osT0FBSyxDQUFDTixJQUFJLENBQUMsQ0FBQzs7SUFFakM7SUFDQSxJQUNFVSxZQUFZLEtBQUssRUFBRSxLQUNsQnJILFFBQU0sQ0FBQzNCLElBQUksS0FBSyxRQUFRLElBQ3RCLFFBQVEsSUFBSTJCLFFBQU0sSUFBSUEsUUFBTSxDQUFDTSxNQUFNLEtBQUt0QixTQUFVLENBQUMsRUFDdEQ7TUFDQW9JLFVBQVUsQ0FBQ3JCLFdBQVMsQ0FBQztNQUNyQjtJQUNGO0lBRUEsSUFBSXNCLFlBQVksS0FBSyxFQUFFLEVBQUU7TUFDdkI7TUFDQSxJQUFJbkYsVUFBVSxDQUFDNkQsV0FBUyxDQUFDLEtBQUsvRyxTQUFTLEVBQUU7UUFDdkNnSSxRQUFRLENBQUNqQixXQUFTLEVBQUUsRUFBRSxDQUFDO01BQ3pCO01BQ0E7SUFDRjtJQUVBLE1BQU1sRCxZQUFVLEdBQUd6Rix3QkFBd0IsQ0FBQzZKLE9BQUssRUFBRWpILFFBQU0sQ0FBQztJQUMxRGdILFFBQVEsQ0FBQ2pCLFdBQVMsRUFBRWxELFlBQVUsQ0FBQ0UsT0FBTyxHQUFHRixZQUFVLENBQUNvRSxLQUFLLEdBQUdBLE9BQUssQ0FBQztJQUNsRVYscUJBQXFCLENBQ25CUixXQUFTLEVBQ1RsRCxZQUFVLENBQUNFLE9BQU8sR0FBRy9ELFNBQVMsR0FBRzZELFlBQVUsQ0FBQ0csS0FDOUMsQ0FBQztFQUNIO0VBRUEsU0FBUzRELGlCQUFpQkEsQ0FDeEJiLFdBQVMsRUFBRSxNQUFNLEVBQ2pCL0YsUUFBTSxFQUFFcEUseUJBQXlCLEVBQ2pDMEwsUUFBUSxFQUFFLE1BQU0sRUFDaEI7SUFDQSxJQUFJLENBQUM5RixNQUFNLEVBQUU7O0lBRWI7SUFDQSxNQUFNK0YsUUFBUSxHQUFHNUMsZUFBZSxDQUFDSSxPQUFPLENBQUN5QyxHQUFHLENBQUN6QixXQUFTLENBQUM7SUFDdkQsSUFBSXdCLFFBQVEsRUFBRTtNQUNaQSxRQUFRLENBQUNwQyxLQUFLLENBQUMsQ0FBQztJQUNsQjtJQUVBLE1BQU1GLFlBQVUsR0FBRyxJQUFJSixlQUFlLENBQUMsQ0FBQztJQUN4Q0YsZUFBZSxDQUFDSSxPQUFPLENBQUMwQyxHQUFHLENBQUMxQixXQUFTLEVBQUVkLFlBQVUsQ0FBQztJQUVsRGIsa0JBQWtCLENBQUM4QyxNQUFJLElBQUksSUFBSTdDLEdBQUcsQ0FBQzZDLE1BQUksQ0FBQyxDQUFDUSxHQUFHLENBQUMzQixXQUFTLENBQUMsQ0FBQztJQUV4RCxLQUFLMUksNkJBQTZCLENBQ2hDaUssUUFBUSxFQUNSdEgsUUFBTSxFQUNOaUYsWUFBVSxDQUFDekQsTUFDYixDQUFDLENBQUNtRyxJQUFJLENBQ0pDLE1BQU0sSUFBSTtNQUNSakQsZUFBZSxDQUFDSSxPQUFPLENBQUM4QyxNQUFNLENBQUM5QixXQUFTLENBQUM7TUFDekMzQixrQkFBa0IsQ0FBQzhDLE1BQUksSUFBSTtRQUN6QixNQUFNQyxNQUFJLEdBQUcsSUFBSTlDLEdBQUcsQ0FBQzZDLE1BQUksQ0FBQztRQUMxQkMsTUFBSSxDQUFDVSxNQUFNLENBQUM5QixXQUFTLENBQUM7UUFDdEIsT0FBT29CLE1BQUk7TUFDYixDQUFDLENBQUM7TUFDRixJQUFJbEMsWUFBVSxDQUFDekQsTUFBTSxDQUFDMEIsT0FBTyxFQUFFO01BRS9CLElBQUkwRSxNQUFNLENBQUM3RSxPQUFPLEVBQUU7UUFDbEJpRSxRQUFRLENBQUNqQixXQUFTLEVBQUU2QixNQUFNLENBQUNYLEtBQUssQ0FBQztRQUNqQ1YscUJBQXFCLENBQUNSLFdBQVMsQ0FBQztRQUNoQztRQUNBLE1BQU0rQixPQUFPLEdBQUdoRixNQUFNLENBQUM4RSxNQUFNLENBQUNYLEtBQUssQ0FBQztRQUNwQ25ELGlCQUFpQixDQUFDb0QsTUFBSSxJQUFJO1VBQ3hCO1VBQ0EsSUFBSUEsTUFBSSxLQUFLSSxRQUFRLEVBQUU7WUFDckJwRCx3QkFBd0IsQ0FBQzRELE9BQU8sQ0FBQ3JKLE1BQU0sQ0FBQztZQUN4QyxPQUFPcUosT0FBTztVQUNoQjtVQUNBLE9BQU9aLE1BQUk7UUFDYixDQUFDLENBQUM7TUFDSixDQUFDLE1BQU07UUFDTDtRQUNBWCxxQkFBcUIsQ0FBQ1IsV0FBUyxFQUFFNkIsTUFBTSxDQUFDNUUsS0FBSyxDQUFDO01BQ2hEO0lBQ0YsQ0FBQyxFQUNELE1BQU07TUFDSjJCLGVBQWUsQ0FBQ0ksT0FBTyxDQUFDOEMsTUFBTSxDQUFDOUIsV0FBUyxDQUFDO01BQ3pDM0Isa0JBQWtCLENBQUM4QyxNQUFJLElBQUk7UUFDekIsTUFBTUMsTUFBSSxHQUFHLElBQUk5QyxHQUFHLENBQUM2QyxNQUFJLENBQUM7UUFDMUJDLE1BQUksQ0FBQ1UsTUFBTSxDQUFDOUIsV0FBUyxDQUFDO1FBQ3RCLE9BQU9vQixNQUFJO01BQ2IsQ0FBQyxDQUFDO0lBQ0osQ0FDRixDQUFDO0VBQ0g7RUFFQSxTQUFTWSxxQkFBcUJBLENBQUNDLFFBQVEsRUFBRSxNQUFNLEVBQUU7SUFDL0NsRSxpQkFBaUIsQ0FBQ2tFLFFBQVEsQ0FBQztJQUMzQjtJQUNBLElBQUl6QyxZQUFZLEVBQUU7TUFDaEJtQixlQUFlLENBQUNuQixZQUFZLENBQUM5QixJQUFJLEVBQUU4QixZQUFZLENBQUN2RixNQUFNLEVBQUVnSSxRQUFRLENBQUM7O01BRWpFO01BQ0EsSUFBSXRELGVBQWUsQ0FBQ0ssT0FBTyxLQUFLL0YsU0FBUyxFQUFFO1FBQ3pDZ0csWUFBWSxDQUFDTixlQUFlLENBQUNLLE9BQU8sQ0FBQztRQUNyQ0wsZUFBZSxDQUFDSyxPQUFPLEdBQUcvRixTQUFTO01BQ3JDO01BQ0EsSUFDRS9CLGdCQUFnQixDQUFDc0ksWUFBWSxDQUFDdkYsTUFBTSxDQUFDLElBQ3JDZ0ksUUFBUSxDQUFDckIsSUFBSSxDQUFDLENBQUMsS0FBSyxFQUFFLElBQ3RCakUsZ0JBQWdCLENBQUM2QyxZQUFZLENBQUM5QixJQUFJLENBQUMsRUFDbkM7UUFDQSxNQUFNc0MsV0FBUyxHQUFHUixZQUFZLENBQUM5QixJQUFJO1FBQ25DLE1BQU16RCxRQUFNLEdBQUd1RixZQUFZLENBQUN2RixNQUFNO1FBQ2xDMEUsZUFBZSxDQUFDSyxPQUFPLEdBQUdoRyxVQUFVLENBQ2xDLENBQUMyRixpQkFBZSxFQUFFa0MsbUJBQWlCLEVBQUViLFdBQVMsRUFBRS9GLFFBQU0sRUFBRWdJLFVBQVEsS0FBSztVQUNuRXRELGlCQUFlLENBQUNLLE9BQU8sR0FBRy9GLFNBQVM7VUFDbkM0SCxtQkFBaUIsQ0FBQ2IsV0FBUyxFQUFFL0YsUUFBTSxFQUFFZ0ksVUFBUSxDQUFDO1FBQ2hELENBQUMsRUFDRCxJQUFJLEVBQ0p0RCxlQUFlLEVBQ2ZrQyxpQkFBaUIsRUFDakJiLFdBQVMsRUFDVC9GLFFBQU0sRUFDTmdJLFFBQ0YsQ0FBQztNQUNIO0lBQ0Y7RUFDRjtFQUVBLFNBQVNDLHFCQUFxQkEsQ0FBQSxFQUFHO0lBQy9CekIsZ0JBQWdCLENBQUMsTUFBTSxDQUFDO0VBQzFCOztFQUVBO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7RUFDRSxTQUFTMEIsWUFBWUEsQ0FDbkJDLElBQUksRUFBRSxNQUFNLEVBQ1pDLE1BQU0sRUFBRSxNQUFNLEVBQUUsRUFDaEJDLE9BQU8sRUFBRSxDQUFDdkIsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUksRUFDaEM7SUFDQSxNQUFNbkksSUFBRSxHQUFHbUcsZ0JBQWdCLENBQUNDLE9BQU87SUFDbkMsSUFBSXBHLElBQUUsQ0FBQ0UsS0FBSyxLQUFLRyxTQUFTLEVBQUVnRyxZQUFZLENBQUNyRyxJQUFFLENBQUNFLEtBQUssQ0FBQztJQUNsREYsSUFBRSxDQUFDQyxNQUFNLElBQUl1SixJQUFJLENBQUNHLFdBQVcsQ0FBQyxDQUFDO0lBQy9CM0osSUFBRSxDQUFDRSxLQUFLLEdBQUdFLFVBQVUsQ0FBQ0wsY0FBYyxFQUFFLElBQUksRUFBRUMsSUFBRSxDQUFDO0lBQy9DLE1BQU00SixLQUFLLEdBQUdILE1BQU0sQ0FBQ0ksU0FBUyxDQUFDQyxDQUFDLElBQUlBLENBQUMsQ0FBQ0MsVUFBVSxDQUFDL0osSUFBRSxDQUFDQyxNQUFNLENBQUMsQ0FBQztJQUM1RCxJQUFJMkosS0FBSyxLQUFLLENBQUMsQ0FBQyxFQUFFRixPQUFPLENBQUNFLEtBQUssQ0FBQztFQUNsQzs7RUFFQTtFQUNBO0VBQ0E7RUFDQTdMLGFBQWEsQ0FDWCxZQUFZLEVBQ1osTUFBTTtJQUNKO0lBQ0EsSUFBSStJLGtCQUFrQixJQUFJRixZQUFZLEVBQUU7TUFDdEMsTUFBTXZCLEtBQUcsR0FBRzlCLFVBQVUsQ0FBQ3FELFlBQVksQ0FBQzlCLElBQUksQ0FBQztNQUN6Q0ssaUJBQWlCLENBQUNFLEtBQUcsS0FBS2hGLFNBQVMsR0FBRzhELE1BQU0sQ0FBQ2tCLEtBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztNQUN2REUsd0JBQXdCLENBQUMsQ0FBQyxDQUFDO0lBQzdCO0lBQ0FwRyxVQUFVLENBQUMsUUFBUSxDQUFDO0VBQ3RCLENBQUMsRUFDRDtJQUNFNkssT0FBTyxFQUFFLFVBQVU7SUFDbkJDLFFBQVEsRUFBRSxDQUFDLENBQUNyRCxZQUFZLElBQUksQ0FBQ3ZELGFBQWEsSUFBSSxDQUFDc0M7RUFDakQsQ0FDRixDQUFDO0VBRUQ3SCxRQUFRLENBQ04sQ0FBQ29NLE1BQU0sRUFBRUMsR0FBRyxLQUFLO0lBQ2Y7SUFDQTtJQUNBLElBQ0VyRCxrQkFBa0IsSUFDbEIsQ0FBQ3FELEdBQUcsQ0FBQ0MsT0FBTyxJQUNaLENBQUNELEdBQUcsQ0FBQ0UsU0FBUyxJQUNkLENBQUNGLEdBQUcsQ0FBQ0csTUFBTSxJQUNYLENBQUNILEdBQUcsQ0FBQ0ksU0FBUyxFQUNkO01BQ0E7SUFDRjs7SUFFQTtJQUNBLElBQ0U1RSxpQkFBaUIsSUFDakJpQixZQUFZLElBQ1pwSSx1QkFBdUIsQ0FBQ29JLFlBQVksQ0FBQ3ZGLE1BQU0sQ0FBQyxFQUM1QztNQUNBLE1BQU1tSixRQUFRLEdBQUc1RCxZQUFZLENBQUN2RixNQUFNO01BQ3BDLE1BQU1vSixRQUFRLEdBQUdwTSxvQkFBb0IsQ0FBQ21NLFFBQVEsQ0FBQztNQUMvQyxNQUFNbkQsVUFBUSxHQUFJOUQsVUFBVSxDQUFDcUQsWUFBWSxDQUFDOUIsSUFBSSxDQUFDLElBQUksTUFBTSxFQUFFLElBQUssRUFBRTtNQUVsRSxJQUFJcUYsR0FBRyxDQUFDTyxTQUFTLElBQUlQLEdBQUcsQ0FBQ1EsTUFBTSxFQUFFO1FBQy9CL0Usb0JBQW9CLENBQUN2RixTQUFTLENBQUM7UUFDL0I4RyxtQkFBbUIsQ0FBQ1AsWUFBWSxDQUFDOUIsSUFBSSxFQUFFMEYsUUFBUSxDQUFDO1FBQ2hEO01BQ0Y7TUFDQSxJQUFJTCxHQUFHLENBQUNDLE9BQU8sRUFBRTtRQUNmLElBQUl2RSxvQkFBb0IsS0FBSyxDQUFDLEVBQUU7VUFDOUJELG9CQUFvQixDQUFDdkYsU0FBUyxDQUFDO1VBQy9COEcsbUJBQW1CLENBQUNQLFlBQVksQ0FBQzlCLElBQUksRUFBRTBGLFFBQVEsQ0FBQztRQUNsRCxDQUFDLE1BQU07VUFDTDFFLHVCQUF1QixDQUFDRCxvQkFBb0IsR0FBRyxDQUFDLENBQUM7UUFDbkQ7UUFDQTtNQUNGO01BQ0EsSUFBSXNFLEdBQUcsQ0FBQ0UsU0FBUyxFQUFFO1FBQ2pCLElBQUl4RSxvQkFBb0IsSUFBSTRFLFFBQVEsQ0FBQzNLLE1BQU0sR0FBRyxDQUFDLEVBQUU7VUFDL0M4RixvQkFBb0IsQ0FBQ3ZGLFNBQVMsQ0FBQztVQUMvQndILGdCQUFnQixDQUFDLE1BQU0sQ0FBQztRQUMxQixDQUFDLE1BQU07VUFDTC9CLHVCQUF1QixDQUFDRCxvQkFBb0IsR0FBRyxDQUFDLENBQUM7UUFDbkQ7UUFDQTtNQUNGO01BQ0EsSUFBSXFFLE1BQU0sS0FBSyxHQUFHLEVBQUU7UUFDbEIsTUFBTVUsV0FBVyxHQUFHSCxRQUFRLENBQUM1RSxvQkFBb0IsQ0FBQztRQUNsRCxJQUFJK0UsV0FBVyxLQUFLdkssU0FBUyxFQUFFO1VBQzdCLE1BQU13SyxXQUFXLEdBQUd4RCxVQUFRLENBQUM1SCxRQUFRLENBQUNtTCxXQUFXLENBQUMsR0FDOUN2RCxVQUFRLENBQUN5RCxNQUFNLENBQUNDLENBQUMsSUFBSUEsQ0FBQyxLQUFLSCxXQUFXLENBQUMsR0FDdkMsQ0FBQyxHQUFHdkQsVUFBUSxFQUFFdUQsV0FBVyxDQUFDO1VBQzlCLE1BQU12QixVQUFRLEdBQUd3QixXQUFXLENBQUMvSyxNQUFNLEdBQUcsQ0FBQyxHQUFHK0ssV0FBVyxHQUFHeEssU0FBUztVQUNqRWdJLFFBQVEsQ0FBQ3pCLFlBQVksQ0FBQzlCLElBQUksRUFBRXVFLFVBQVEsQ0FBQztVQUNyQyxNQUFNN0IsS0FBRyxHQUFHZ0QsUUFBUSxDQUFDL0MsUUFBUTtVQUM3QixNQUFNQyxLQUFHLEdBQUc4QyxRQUFRLENBQUM3QyxRQUFRO1VBQzdCLElBQ0VILEtBQUcsS0FBS25ILFNBQVMsSUFDakJ3SyxXQUFXLENBQUMvSyxNQUFNLEdBQUcwSCxLQUFHLEtBQ3ZCcUQsV0FBVyxDQUFDL0ssTUFBTSxHQUFHLENBQUMsSUFBSThHLFlBQVksQ0FBQzdCLFVBQVUsQ0FBQyxFQUNuRDtZQUNBNkMscUJBQXFCLENBQ25CaEIsWUFBWSxDQUFDOUIsSUFBSSxFQUNqQixtQkFBbUIwQyxLQUFHLElBQUk3SSxNQUFNLENBQUM2SSxLQUFHLEVBQUUsTUFBTSxDQUFDLEVBQy9DLENBQUM7VUFDSCxDQUFDLE1BQU0sSUFBSUUsS0FBRyxLQUFLckgsU0FBUyxJQUFJd0ssV0FBVyxDQUFDL0ssTUFBTSxHQUFHNEgsS0FBRyxFQUFFO1lBQ3hERSxxQkFBcUIsQ0FDbkJoQixZQUFZLENBQUM5QixJQUFJLEVBQ2pCLGtCQUFrQjRDLEtBQUcsSUFBSS9JLE1BQU0sQ0FBQytJLEtBQUcsRUFBRSxNQUFNLENBQUMsRUFDOUMsQ0FBQztVQUNILENBQUMsTUFBTTtZQUNMRSxxQkFBcUIsQ0FBQ2hCLFlBQVksQ0FBQzlCLElBQUksQ0FBQztVQUMxQztRQUNGO1FBQ0E7TUFDRjtNQUNBLElBQUlxRixHQUFHLENBQUNHLE1BQU0sRUFBRTtRQUNkO1FBQ0EsTUFBTU0sYUFBVyxHQUFHSCxRQUFRLENBQUM1RSxvQkFBb0IsQ0FBQztRQUNsRCxJQUFJK0UsYUFBVyxLQUFLdkssU0FBUyxJQUFJLENBQUNnSCxVQUFRLENBQUM1SCxRQUFRLENBQUNtTCxhQUFXLENBQUMsRUFBRTtVQUNoRXZDLFFBQVEsQ0FBQ3pCLFlBQVksQ0FBQzlCLElBQUksRUFBRSxDQUFDLEdBQUd1QyxVQUFRLEVBQUV1RCxhQUFXLENBQUMsQ0FBQztRQUN6RDtRQUNBaEYsb0JBQW9CLENBQUN2RixTQUFTLENBQUM7UUFDL0J3SCxnQkFBZ0IsQ0FBQyxNQUFNLENBQUM7UUFDeEI7TUFDRjtNQUNBLElBQUlxQyxNQUFNLEVBQUU7UUFDVixNQUFNVCxRQUFNLEdBQUdnQixRQUFRLENBQUM1RixHQUFHLENBQUNrRyxHQUFDLElBQzNCM00sbUJBQW1CLENBQUNvTSxRQUFRLEVBQUVPLEdBQUMsQ0FBQyxDQUFDcEIsV0FBVyxDQUFDLENBQy9DLENBQUM7UUFDREosWUFBWSxDQUFDVyxNQUFNLEVBQUVULFFBQU0sRUFBRTNELHVCQUF1QixDQUFDO1FBQ3JEO01BQ0Y7TUFDQTtJQUNGOztJQUVBO0lBQ0EsSUFDRUgsaUJBQWlCLElBQ2pCaUIsWUFBWSxJQUNackksWUFBWSxDQUFDcUksWUFBWSxDQUFDdkYsTUFBTSxDQUFDLEVBQ2pDO01BQ0EsTUFBTTJKLFVBQVUsR0FBR3BFLFlBQVksQ0FBQ3ZGLE1BQU07TUFDdEMsTUFBTTRKLFVBQVUsR0FBRzlNLGFBQWEsQ0FBQzZNLFVBQVUsQ0FBQztNQUU1QyxJQUFJYixHQUFHLENBQUNPLFNBQVMsSUFBSVAsR0FBRyxDQUFDUSxNQUFNLEVBQUU7UUFDL0IvRSxvQkFBb0IsQ0FBQ3ZGLFNBQVMsQ0FBQztRQUMvQjtNQUNGO01BQ0EsSUFBSThKLEdBQUcsQ0FBQ0MsT0FBTyxFQUFFO1FBQ2YsSUFBSXZFLG9CQUFvQixLQUFLLENBQUMsRUFBRTtVQUM5QkQsb0JBQW9CLENBQUN2RixTQUFTLENBQUM7UUFDakMsQ0FBQyxNQUFNO1VBQ0x5Rix1QkFBdUIsQ0FBQ0Qsb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO1FBQ25EO1FBQ0E7TUFDRjtNQUNBLElBQUlzRSxHQUFHLENBQUNFLFNBQVMsRUFBRTtRQUNqQixJQUFJeEUsb0JBQW9CLElBQUlvRixVQUFVLENBQUNuTCxNQUFNLEdBQUcsQ0FBQyxFQUFFO1VBQ2pEOEYsb0JBQW9CLENBQUN2RixTQUFTLENBQUM7VUFDL0J3SCxnQkFBZ0IsQ0FBQyxNQUFNLENBQUM7UUFDMUIsQ0FBQyxNQUFNO1VBQ0wvQix1QkFBdUIsQ0FBQ0Qsb0JBQW9CLEdBQUcsQ0FBQyxDQUFDO1FBQ25EO1FBQ0E7TUFDRjtNQUNBO01BQ0EsSUFBSXFFLE1BQU0sS0FBSyxHQUFHLEVBQUU7UUFDbEIsTUFBTVUsYUFBVyxHQUFHSyxVQUFVLENBQUNwRixvQkFBb0IsQ0FBQztRQUNwRCxJQUFJK0UsYUFBVyxLQUFLdkssU0FBUyxFQUFFO1VBQzdCZ0ksUUFBUSxDQUFDekIsWUFBWSxDQUFDOUIsSUFBSSxFQUFFOEYsYUFBVyxDQUFDO1FBQzFDO1FBQ0FoRixvQkFBb0IsQ0FBQ3ZGLFNBQVMsQ0FBQztRQUMvQjtNQUNGO01BQ0E7TUFDQSxJQUFJOEosR0FBRyxDQUFDRyxNQUFNLEVBQUU7UUFDZCxNQUFNTSxhQUFXLEdBQUdLLFVBQVUsQ0FBQ3BGLG9CQUFvQixDQUFDO1FBQ3BELElBQUkrRSxhQUFXLEtBQUt2SyxTQUFTLEVBQUU7VUFDN0JnSSxRQUFRLENBQUN6QixZQUFZLENBQUM5QixJQUFJLEVBQUU4RixhQUFXLENBQUM7UUFDMUM7UUFDQWhGLG9CQUFvQixDQUFDdkYsU0FBUyxDQUFDO1FBQy9Cd0gsZ0JBQWdCLENBQUMsTUFBTSxDQUFDO1FBQ3hCO01BQ0Y7TUFDQSxJQUFJcUMsTUFBTSxFQUFFO1FBQ1YsTUFBTVQsUUFBTSxHQUFHd0IsVUFBVSxDQUFDcEcsR0FBRyxDQUFDa0csR0FBQyxJQUM3QjdNLFlBQVksQ0FBQzhNLFVBQVUsRUFBRUQsR0FBQyxDQUFDLENBQUNwQixXQUFXLENBQUMsQ0FDMUMsQ0FBQztRQUNESixZQUFZLENBQUNXLE1BQU0sRUFBRVQsUUFBTSxFQUFFM0QsdUJBQXVCLENBQUM7UUFDckQ7TUFDRjtNQUNBO0lBQ0Y7O0lBRUE7SUFDQSxJQUFJcUUsR0FBRyxDQUFDRyxNQUFNLElBQUlqSCxhQUFhLEtBQUssUUFBUSxFQUFFO01BQzVDLElBQUk2SCxnQkFBZ0IsQ0FBQyxDQUFDLElBQUloSSxNQUFNLENBQUNDLElBQUksQ0FBQ1ksZ0JBQWdCLENBQUMsQ0FBQ2pFLE1BQU0sS0FBSyxDQUFDLEVBQUU7UUFDcEVYLFVBQVUsQ0FBQyxRQUFRLEVBQUVvRSxVQUFVLENBQUM7TUFDbEMsQ0FBQyxNQUFNO1FBQ0w7UUFDQSxNQUFNb0IsZ0JBQWMsR0FBRzNCLGVBQWUsQ0FBQzRCLFFBQVEsSUFBSSxFQUFFO1FBQ3JELEtBQUssTUFBTXdDLFdBQVMsSUFBSXpDLGdCQUFjLEVBQUU7VUFDdEMsSUFBSXBCLFVBQVUsQ0FBQzZELFdBQVMsQ0FBQyxLQUFLL0csU0FBUyxFQUFFO1lBQ3ZDdUgscUJBQXFCLENBQUNSLFdBQVMsRUFBRSx3QkFBd0IsQ0FBQztVQUM1RDtRQUNGO1FBQ0EsTUFBTStELGFBQWEsR0FBR3pHLFlBQVksQ0FBQ21GLFNBQVMsQ0FDMUNoSyxHQUFDLElBQ0U4RSxnQkFBYyxDQUFDbEYsUUFBUSxDQUFDSSxHQUFDLENBQUNpRixJQUFJLENBQUMsSUFDOUJ2QixVQUFVLENBQUMxRCxHQUFDLENBQUNpRixJQUFJLENBQUMsS0FBS3pFLFNBQVMsSUFDbEMwRCxnQkFBZ0IsQ0FBQ2xFLEdBQUMsQ0FBQ2lGLElBQUksQ0FBQyxLQUFLekUsU0FDakMsQ0FBQztRQUNELElBQUk4SyxhQUFhLEtBQUssQ0FBQyxDQUFDLEVBQUU7VUFDeEJsRyxvQkFBb0IsQ0FBQ2tHLGFBQWEsQ0FBQztVQUNuQzdILGdCQUFnQixDQUFDLElBQUksQ0FBQztVQUN0QnlELGFBQWEsQ0FBQ29FLGFBQWEsQ0FBQztRQUM5QjtNQUNGO01BQ0E7SUFDRjtJQUVBLElBQUloQixHQUFHLENBQUNHLE1BQU0sSUFBSWpILGFBQWEsS0FBSyxTQUFTLEVBQUU7TUFDN0NsRSxVQUFVLENBQUMsU0FBUyxDQUFDO01BQ3JCO0lBQ0Y7O0lBRUE7SUFDQSxJQUFJZ0wsR0FBRyxDQUFDQyxPQUFPLElBQUlELEdBQUcsQ0FBQ0UsU0FBUyxFQUFFO01BQ2hDO01BQ0EsTUFBTXJLLElBQUUsR0FBR21HLGdCQUFnQixDQUFDQyxPQUFPO01BQ25DcEcsSUFBRSxDQUFDQyxNQUFNLEdBQUcsRUFBRTtNQUNkLElBQUlELElBQUUsQ0FBQ0UsS0FBSyxLQUFLRyxTQUFTLEVBQUU7UUFDMUJnRyxZQUFZLENBQUNyRyxJQUFFLENBQUNFLEtBQUssQ0FBQztRQUN0QkYsSUFBRSxDQUFDRSxLQUFLLEdBQUdHLFNBQVM7TUFDdEI7TUFDQXdILGdCQUFnQixDQUFDc0MsR0FBRyxDQUFDQyxPQUFPLEdBQUcsSUFBSSxHQUFHLE1BQU0sQ0FBQztNQUM3QztJQUNGOztJQUVBO0lBQ0EsSUFBSS9HLGFBQWEsS0FBSzhHLEdBQUcsQ0FBQ08sU0FBUyxJQUFJUCxHQUFHLENBQUNpQixVQUFVLENBQUMsRUFBRTtNQUN0RDlILGdCQUFnQixDQUFDRCxhQUFhLEtBQUssUUFBUSxHQUFHLFNBQVMsR0FBRyxRQUFRLENBQUM7TUFDbkU7SUFDRjtJQUVBLElBQUksQ0FBQ3VELFlBQVksRUFBRTtJQUNuQixNQUFNO01BQUV2RixNQUFNLEVBQU5BLFFBQU07TUFBRXlELElBQUksRUFBSkE7SUFBSyxDQUFDLEdBQUc4QixZQUFZO0lBQ3JDLE1BQU0wQixPQUFLLEdBQUcvRSxVQUFVLENBQUN1QixNQUFJLENBQUM7O0lBRTlCO0lBQ0EsSUFBSXpELFFBQU0sQ0FBQzNCLElBQUksS0FBSyxTQUFTLEVBQUU7TUFDN0IsSUFBSXdLLE1BQU0sS0FBSyxHQUFHLEVBQUU7UUFDbEI3QixRQUFRLENBQUN2RCxNQUFJLEVBQUV3RCxPQUFLLEtBQUtqSSxTQUFTLEdBQUcsSUFBSSxHQUFHLENBQUNpSSxPQUFLLENBQUM7UUFDbkQ7TUFDRjtNQUNBLElBQUk2QixHQUFHLENBQUNHLE1BQU0sRUFBRTtRQUNkekMsZ0JBQWdCLENBQUMsTUFBTSxDQUFDO1FBQ3hCO01BQ0Y7TUFDQSxJQUFJc0MsR0FBRyxDQUFDSSxTQUFTLElBQUlqQyxPQUFLLEtBQUtqSSxTQUFTLEVBQUU7UUFDeENvSSxVQUFVLENBQUMzRCxNQUFJLENBQUM7UUFDaEI7TUFDRjtNQUNBO01BQ0EsSUFBSW9GLE1BQU0sSUFBSSxDQUFDQyxHQUFHLENBQUNHLE1BQU0sRUFBRTtRQUN6QmYsWUFBWSxDQUFDVyxNQUFNLEVBQUUsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEVBQUVtQixDQUFDLElBQUloRCxRQUFRLENBQUN2RCxNQUFJLEVBQUV1RyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDakU7TUFDRjtNQUNBO0lBQ0Y7O0lBRUE7SUFDQSxJQUFJOU0sWUFBWSxDQUFDOEMsUUFBTSxDQUFDLElBQUk3Qyx1QkFBdUIsQ0FBQzZDLFFBQU0sQ0FBQyxFQUFFO01BQzNELElBQUk4SSxHQUFHLENBQUNHLE1BQU0sRUFBRTtRQUNkekMsZ0JBQWdCLENBQUMsTUFBTSxDQUFDO1FBQ3hCO01BQ0Y7TUFDQSxJQUFJc0MsR0FBRyxDQUFDSSxTQUFTLElBQUlqQyxPQUFLLEtBQUtqSSxTQUFTLEVBQUU7UUFDeENvSSxVQUFVLENBQUMzRCxNQUFJLENBQUM7UUFDaEI7TUFDRjtNQUNBO01BQ0E7TUFDQSxJQUFJMkUsUUFBTSxFQUFFLE1BQU0sRUFBRTtNQUNwQixJQUFJNkIsUUFBUSxHQUFHLENBQUM7TUFDaEIsSUFBSS9NLFlBQVksQ0FBQzhDLFFBQU0sQ0FBQyxFQUFFO1FBQ3hCLE1BQU1rSyxJQUFJLEdBQUdwTixhQUFhLENBQUNrRCxRQUFNLENBQUM7UUFDbENvSSxRQUFNLEdBQUc4QixJQUFJLENBQUMxRyxHQUFHLENBQUNrRyxHQUFDLElBQUk3TSxZQUFZLENBQUNtRCxRQUFNLEVBQUUwSixHQUFDLENBQUMsQ0FBQ3BCLFdBQVcsQ0FBQyxDQUFDLENBQUM7UUFDN0QsSUFBSXJCLE9BQUssS0FBS2pJLFNBQVMsRUFBRTtVQUN2QmlMLFFBQVEsR0FBR0UsSUFBSSxDQUFDOUQsR0FBRyxDQUFDLENBQUMsRUFBRTZELElBQUksQ0FBQ0UsT0FBTyxDQUFDbkQsT0FBSyxJQUFJLE1BQU0sQ0FBQyxDQUFDO1FBQ3ZEO01BQ0YsQ0FBQyxNQUFNO1FBQ0wsTUFBTWlELE1BQUksR0FBR2xOLG9CQUFvQixDQUFDZ0QsUUFBTSxDQUFDO1FBQ3pDb0ksUUFBTSxHQUFHOEIsTUFBSSxDQUFDMUcsR0FBRyxDQUFDa0csR0FBQyxJQUFJM00sbUJBQW1CLENBQUNpRCxRQUFNLEVBQUUwSixHQUFDLENBQUMsQ0FBQ3BCLFdBQVcsQ0FBQyxDQUFDLENBQUM7TUFDdEU7TUFDQSxJQUFJUSxHQUFHLENBQUNpQixVQUFVLEVBQUU7UUFDbEJ4RixvQkFBb0IsQ0FBQ2QsTUFBSSxDQUFDO1FBQzFCZ0IsdUJBQXVCLENBQUN3RixRQUFRLENBQUM7UUFDakM7TUFDRjtNQUNBO01BQ0EsSUFBSXBCLE1BQU0sSUFBSSxDQUFDQyxHQUFHLENBQUNPLFNBQVMsRUFBRTtRQUM1Qm5CLFlBQVksQ0FBQ1csTUFBTSxFQUFFVCxRQUFNLEVBQUU0QixHQUFDLElBQUk7VUFDaEN6RixvQkFBb0IsQ0FBQ2QsTUFBSSxDQUFDO1VBQzFCZ0IsdUJBQXVCLENBQUN1RixHQUFDLENBQUM7UUFDNUIsQ0FBQyxDQUFDO1FBQ0Y7TUFDRjtNQUNBO0lBQ0Y7O0lBRUE7SUFDQSxJQUFJbEIsR0FBRyxDQUFDSSxTQUFTLEVBQUU7TUFDakIsSUFBSXpELGtCQUFrQixJQUFJNUIsY0FBYyxLQUFLLEVBQUUsRUFBRTtRQUMvQ3VELFVBQVUsQ0FBQzNELE1BQUksQ0FBQztRQUNoQjtNQUNGO0lBQ0Y7O0lBRUE7RUFDRixDQUFDLEVBQ0Q7SUFBRW1GLFFBQVEsRUFBRTtFQUFLLENBQ25CLENBQUM7RUFFRCxTQUFTaUIsZ0JBQWdCQSxDQUFBLENBQUUsRUFBRSxPQUFPLENBQUM7SUFDbkMsTUFBTXZHLGdCQUFjLEdBQUczQixlQUFlLENBQUM0QixRQUFRLElBQUksRUFBRTtJQUNyRCxLQUFLLE1BQU13QyxXQUFTLElBQUl6QyxnQkFBYyxFQUFFO01BQ3RDLE1BQU0yRCxPQUFLLEdBQUcvRSxVQUFVLENBQUM2RCxXQUFTLENBQUM7TUFDbkMsSUFBSWtCLE9BQUssS0FBS2pJLFNBQVMsSUFBSWlJLE9BQUssS0FBSyxJQUFJLElBQUlBLE9BQUssS0FBSyxFQUFFLEVBQUU7UUFDekQsT0FBTyxLQUFLO01BQ2Q7TUFDQSxJQUFJb0QsS0FBSyxDQUFDQyxPQUFPLENBQUNyRCxPQUFLLENBQUMsSUFBSUEsT0FBSyxDQUFDeEksTUFBTSxLQUFLLENBQUMsRUFBRTtRQUM5QyxPQUFPLEtBQUs7TUFDZDtJQUNGO0lBQ0EsT0FBTyxJQUFJO0VBQ2I7O0VBRUE7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsTUFBTThMLGVBQWUsR0FBRyxDQUFDO0VBQ3pCLE1BQU1DLGVBQWUsR0FBRyxFQUFFO0VBQzFCLE1BQU1DLGdCQUFnQixHQUFHTixJQUFJLENBQUM5RCxHQUFHLENBQy9CLENBQUMsRUFDRDhELElBQUksQ0FBQ08sS0FBSyxDQUFDLENBQUNwRixJQUFJLEdBQUdrRixlQUFlLElBQUlELGVBQWUsQ0FDdkQsQ0FBQztFQUVELE1BQU1JLFlBQVksR0FBRzFPLE9BQU8sQ0FBQyxNQUFNO0lBQ2pDLE1BQU0yTyxLQUFLLEdBQUd2SCxZQUFZLENBQUM1RSxNQUFNO0lBQ2pDLElBQUltTSxLQUFLLElBQUlILGdCQUFnQixFQUFFO01BQzdCLE9BQU87UUFBRUksS0FBSyxFQUFFLENBQUM7UUFBRUMsR0FBRyxFQUFFRjtNQUFNLENBQUM7SUFDakM7SUFDQTtJQUNBLE1BQU1HLFFBQVEsR0FBR3BILGlCQUFpQixJQUFJaUgsS0FBSyxHQUFHLENBQUM7SUFDL0MsSUFBSUMsS0FBSyxHQUFHVixJQUFJLENBQUM5RCxHQUFHLENBQUMsQ0FBQyxFQUFFMEUsUUFBUSxHQUFHWixJQUFJLENBQUNPLEtBQUssQ0FBQ0QsZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDLENBQUM7SUFDcEUsTUFBTUssR0FBRyxHQUFHWCxJQUFJLENBQUNoRSxHQUFHLENBQUMwRSxLQUFLLEdBQUdKLGdCQUFnQixFQUFFRyxLQUFLLENBQUM7SUFDckQ7SUFDQUMsS0FBSyxHQUFHVixJQUFJLENBQUM5RCxHQUFHLENBQUMsQ0FBQyxFQUFFeUUsR0FBRyxHQUFHTCxnQkFBZ0IsQ0FBQztJQUMzQyxPQUFPO01BQUVJLEtBQUs7TUFBRUM7SUFBSSxDQUFDO0VBQ3ZCLENBQUMsRUFBRSxDQUFDekgsWUFBWSxDQUFDNUUsTUFBTSxFQUFFZ00sZ0JBQWdCLEVBQUU5RyxpQkFBaUIsQ0FBQyxDQUFDO0VBRTlELE1BQU1xSCxjQUFjLEdBQUdMLFlBQVksQ0FBQ0UsS0FBSyxHQUFHLENBQUM7RUFDN0MsTUFBTUksY0FBYyxHQUFHTixZQUFZLENBQUNHLEdBQUcsR0FBR3pILFlBQVksQ0FBQzVFLE1BQU07RUFFN0QsU0FBU3lNLGdCQUFnQkEsQ0FBQSxDQUFFLEVBQUVwUCxLQUFLLENBQUN3RixTQUFTLENBQUM7SUFDM0MsSUFBSSxDQUFDK0IsWUFBWSxDQUFDNUUsTUFBTSxFQUFFLE9BQU8sSUFBSTtJQUVyQyxPQUNFLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxRQUFRO0FBQ2pDLFFBQVEsQ0FBQ3VNLGNBQWMsSUFDYixDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDN0IsWUFBWSxDQUFDLElBQUksQ0FBQyxRQUFRO0FBQzFCLGNBQWMsQ0FBQ25QLE9BQU8sQ0FBQ3NQLE9BQU8sQ0FBQyxDQUFDLENBQUNSLFlBQVksQ0FBQ0UsS0FBSyxDQUFDO0FBQ3BELFlBQVksRUFBRSxJQUFJO0FBQ2xCLFVBQVUsRUFBRSxHQUFHLENBQ047QUFDVCxRQUFRLENBQUN4SCxZQUFZLENBQ1YrSCxLQUFLLENBQUNULFlBQVksQ0FBQ0UsS0FBSyxFQUFFRixZQUFZLENBQUNHLEdBQUcsQ0FBQyxDQUMzQ3RILEdBQUcsQ0FBQyxDQUFDb0MsT0FBSyxFQUFFeUYsVUFBVSxLQUFLO1FBQzFCLE1BQU12RSxPQUFLLEdBQUc2RCxZQUFZLENBQUNFLEtBQUssR0FBR1EsVUFBVTtRQUM3QyxNQUFNO1VBQUU1SCxJQUFJLEVBQUpBLE1BQUk7VUFBRXpELE1BQU0sRUFBTkEsUUFBTTtVQUFFMEQ7UUFBVyxDQUFDLEdBQUdrQyxPQUFLO1FBQzFDLE1BQU1nRCxRQUFRLEdBQUc5QixPQUFLLEtBQUtuRCxpQkFBaUIsSUFBSSxDQUFDM0IsYUFBYTtRQUM5RCxNQUFNaUYsT0FBSyxHQUFHL0UsVUFBVSxDQUFDdUIsTUFBSSxDQUFDO1FBQzlCLE1BQU02SCxRQUFRLEdBQ1pyRSxPQUFLLEtBQUtqSSxTQUFTLEtBQUssQ0FBQ3FMLEtBQUssQ0FBQ0MsT0FBTyxDQUFDckQsT0FBSyxDQUFDLElBQUlBLE9BQUssQ0FBQ3hJLE1BQU0sR0FBRyxDQUFDLENBQUM7UUFDcEUsTUFBTXVFLE9BQUssR0FBR04sZ0JBQWdCLENBQUNlLE1BQUksQ0FBQzs7UUFFcEM7UUFDQSxNQUFNOEgsV0FBVyxHQUFHcEgsZUFBZSxDQUFDcUgsR0FBRyxDQUFDL0gsTUFBSSxDQUFDO1FBQzdDLE1BQU1nSSxRQUFRLEdBQUdGLFdBQVcsR0FDMUIsQ0FBQyxnQkFBZ0IsR0FBRyxHQUNsQnZJLE9BQUssR0FDUCxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUNuSCxPQUFPLENBQUM2UCxPQUFPLENBQUMsRUFBRSxJQUFJLENBQUMsR0FDMUNKLFFBQVEsR0FDVixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMxQyxRQUFRLENBQUM7QUFDeEQsZ0JBQWdCLENBQUMvTSxPQUFPLENBQUM4UCxJQUFJO0FBQzdCLGNBQWMsRUFBRSxJQUFJLENBQUMsR0FDTGpJLFVBQVUsR0FDWixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsR0FFNUIsQ0FBQyxJQUFJLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FDZDs7UUFFRDtRQUNBLE1BQU1rSSxjQUFjLEdBQUc1SSxPQUFLLEdBQ3hCLE9BQU8sR0FDUHNJLFFBQVEsR0FDTixTQUFTLEdBQ1Q1SCxVQUFVLEdBQ1IsT0FBTyxHQUNQLFlBQVk7UUFFcEIsTUFBTW1JLFdBQVcsR0FBR2pELFFBQVEsR0FBR2dELGNBQWMsR0FBRzVNLFNBQVM7UUFFekQsTUFBTThNLEtBQUssR0FDVCxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQ0QsV0FBVyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUNqRCxRQUFRLENBQUM7QUFDdkQsZ0JBQWdCLENBQUM1SSxRQUFNLENBQUMrTCxLQUFLLElBQUl0SSxNQUFJO0FBQ3JDLGNBQWMsRUFBRSxJQUFJLENBQ1A7O1FBRUQ7UUFDQSxJQUFJdUksWUFBWSxFQUFFbFEsS0FBSyxDQUFDd0YsU0FBUztRQUNqQyxJQUFJMkssZ0JBQWdCLEVBQUVuUSxLQUFLLENBQUN3RixTQUFTLEdBQUcsSUFBSTtRQUU1QyxJQUFJbkUsdUJBQXVCLENBQUM2QyxRQUFNLENBQUMsRUFBRTtVQUNuQyxNQUFNb0osVUFBUSxHQUFHcE0sb0JBQW9CLENBQUNnRCxRQUFNLENBQUM7VUFDN0MsTUFBTWdHLFVBQVEsR0FBSWlCLE9BQUssSUFBSSxNQUFNLEVBQUUsR0FBRyxTQUFTLElBQUssRUFBRTtVQUN0RCxNQUFNaUYsVUFBVSxHQUFHNUgsaUJBQWlCLEtBQUtiLE1BQUksSUFBSW1GLFFBQVE7VUFFekQsSUFBSXNELFVBQVUsRUFBRTtZQUNkRixZQUFZLEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUNuUSxPQUFPLENBQUNzUSxpQkFBaUIsQ0FBQyxFQUFFLElBQUksQ0FBQztZQUNoRUYsZ0JBQWdCLEdBQ2QsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDNUQsb0JBQW9CLENBQUM3QyxVQUFRLENBQUM1RixHQUFHLENBQUMsQ0FBQzRJLE1BQU0sRUFBRUMsTUFBTSxLQUFLO2dCQUNoQyxNQUFNQyxRQUFRLEdBQUd2UCxtQkFBbUIsQ0FBQ2lELFFBQU0sRUFBRW9NLE1BQU0sQ0FBQztnQkFDcEQsTUFBTUcsU0FBUyxHQUFHdkcsVUFBUSxDQUFDNUgsUUFBUSxDQUFDZ08sTUFBTSxDQUFDO2dCQUMzQyxNQUFNSSxTQUFTLEdBQUdILE1BQU0sS0FBSzdILG9CQUFvQjtnQkFDakQsT0FDRSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQzRILE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUNqRCwwQkFBMEIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFlBQVk7QUFDbEQsNEJBQTRCLENBQUNJLFNBQVMsR0FBRzNRLE9BQU8sQ0FBQzRRLE9BQU8sR0FBRyxHQUFHO0FBQzlELDBCQUEwQixFQUFFLElBQUk7QUFDaEMsMEJBQTBCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDRixTQUFTLEdBQUcsU0FBUyxHQUFHdk4sU0FBUyxDQUFDO0FBQ3pFLDRCQUE0QixDQUFDdU4sU0FBUyxHQUNOMVEsT0FBTyxDQUFDNlEsVUFBVSxHQUNsQjdRLE9BQU8sQ0FBQzhRLFdBQVc7QUFDbkQsMEJBQTBCLEVBQUUsSUFBSTtBQUNoQywwQkFBMEIsQ0FBQyxJQUFJLENBQ0gsS0FBSyxDQUFDLENBQUNILFNBQVMsR0FBRyxZQUFZLEdBQUd4TixTQUFTLENBQUMsQ0FDNUMsSUFBSSxDQUFDLENBQUN3TixTQUFTLENBQUM7QUFFNUMsNEJBQTRCLENBQUNGLFFBQVE7QUFDckMsMEJBQTBCLEVBQUUsSUFBSTtBQUNoQyx3QkFBd0IsRUFBRSxHQUFHLENBQUM7Y0FFVixDQUFDLENBQUM7QUFDdEIsa0JBQWtCLEVBQUUsR0FBRyxDQUNOO1VBQ0gsQ0FBQyxNQUFNO1lBQ0w7WUFDQSxNQUFNTSxLQUFLLEdBQUdoRSxRQUFRLEdBQ3BCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDL00sT0FBTyxDQUFDZ1Isa0JBQWtCLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUNqRCxJQUFJO1lBQ1IsSUFBSTdHLFVBQVEsQ0FBQ3ZILE1BQU0sR0FBRyxDQUFDLEVBQUU7Y0FDdkIsTUFBTXFPLGFBQWEsR0FBRzlHLFVBQVEsQ0FBQ3hDLEdBQUcsQ0FBQ2tHLEdBQUMsSUFDbEMzTSxtQkFBbUIsQ0FBQ2lELFFBQU0sRUFBRTBKLEdBQUMsQ0FDL0IsQ0FBQztjQUNEc0MsWUFBWSxHQUNWLENBQUMsSUFBSTtBQUN6QixzQkFBc0IsQ0FBQ1ksS0FBSztBQUM1QixzQkFBc0IsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUNmLFdBQVcsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDakQsUUFBUSxDQUFDO0FBQy9ELHdCQUF3QixDQUFDa0UsYUFBYSxDQUFDQyxJQUFJLENBQUMsSUFBSSxDQUFDO0FBQ2pELHNCQUFzQixFQUFFLElBQUk7QUFDNUIsb0JBQW9CLEVBQUUsSUFBSSxDQUNQO1lBQ0gsQ0FBQyxNQUFNO2NBQ0xmLFlBQVksR0FDVixDQUFDLElBQUk7QUFDekIsc0JBQXNCLENBQUNZLEtBQUs7QUFDNUIsc0JBQXNCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNO0FBQzNDO0FBQ0Esc0JBQXNCLEVBQUUsSUFBSTtBQUM1QixvQkFBb0IsRUFBRSxJQUFJLENBQ1A7WUFDSDtVQUNGO1FBQ0YsQ0FBQyxNQUFNLElBQUkxUCxZQUFZLENBQUM4QyxRQUFNLENBQUMsRUFBRTtVQUMvQixNQUFNNEosWUFBVSxHQUFHOU0sYUFBYSxDQUFDa0QsUUFBTSxDQUFDO1VBQ3hDLE1BQU1rTSxZQUFVLEdBQUc1SCxpQkFBaUIsS0FBS2IsTUFBSSxJQUFJbUYsUUFBUTtVQUV6RCxJQUFJc0QsWUFBVSxFQUFFO1lBQ2RGLFlBQVksR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQ25RLE9BQU8sQ0FBQ3NRLGlCQUFpQixDQUFDLEVBQUUsSUFBSSxDQUFDO1lBQ2hFRixnQkFBZ0IsR0FDZCxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUM1RCxvQkFBb0IsQ0FBQ3JDLFlBQVUsQ0FBQ3BHLEdBQUcsQ0FBQyxDQUFDNEksUUFBTSxFQUFFQyxRQUFNLEtBQUs7Z0JBQ2xDLE1BQU1DLFVBQVEsR0FBR3pQLFlBQVksQ0FBQ21ELFFBQU0sRUFBRW9NLFFBQU0sQ0FBQztnQkFDN0MsTUFBTVksVUFBVSxHQUFHL0YsT0FBSyxLQUFLbUYsUUFBTTtnQkFDbkMsTUFBTUksV0FBUyxHQUFHSCxRQUFNLEtBQUs3SCxvQkFBb0I7Z0JBQ2pELE9BQ0UsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM0SCxRQUFNLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDakQsMEJBQTBCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZO0FBQ2xELDRCQUE0QixDQUFDSSxXQUFTLEdBQUczUSxPQUFPLENBQUM0USxPQUFPLEdBQUcsR0FBRztBQUM5RCwwQkFBMEIsRUFBRSxJQUFJO0FBQ2hDLDBCQUEwQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQ08sVUFBVSxHQUFHLFNBQVMsR0FBR2hPLFNBQVMsQ0FBQztBQUMxRSw0QkFBNEIsQ0FBQ2dPLFVBQVUsR0FBR25SLE9BQU8sQ0FBQ29SLE9BQU8sR0FBR3BSLE9BQU8sQ0FBQ3FSLFFBQVE7QUFDNUUsMEJBQTBCLEVBQUUsSUFBSTtBQUNoQywwQkFBMEIsQ0FBQyxJQUFJLENBQ0gsS0FBSyxDQUFDLENBQUNWLFdBQVMsR0FBRyxZQUFZLEdBQUd4TixTQUFTLENBQUMsQ0FDNUMsSUFBSSxDQUFDLENBQUN3TixXQUFTLENBQUM7QUFFNUMsNEJBQTRCLENBQUNGLFVBQVE7QUFDckMsMEJBQTBCLEVBQUUsSUFBSTtBQUNoQyx3QkFBd0IsRUFBRSxHQUFHLENBQUM7Y0FFVixDQUFDLENBQUM7QUFDdEIsa0JBQWtCLEVBQUUsR0FBRyxDQUNOO1VBQ0gsQ0FBQyxNQUFNO1lBQ0w7WUFDQSxNQUFNTSxPQUFLLEdBQUdoRSxRQUFRLEdBQ3BCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDL00sT0FBTyxDQUFDZ1Isa0JBQWtCLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUNqRCxJQUFJO1lBQ1IsSUFBSXZCLFFBQVEsRUFBRTtjQUNaVSxZQUFZLEdBQ1YsQ0FBQyxJQUFJO0FBQ3pCLHNCQUFzQixDQUFDWSxPQUFLO0FBQzVCLHNCQUFzQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQ2YsV0FBVyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUNqRCxRQUFRLENBQUM7QUFDL0Qsd0JBQXdCLENBQUMvTCxZQUFZLENBQUNtRCxRQUFNLEVBQUVpSCxPQUFLLElBQUksTUFBTSxDQUFDO0FBQzlELHNCQUFzQixFQUFFLElBQUk7QUFDNUIsb0JBQW9CLEVBQUUsSUFBSSxDQUNQO1lBQ0gsQ0FBQyxNQUFNO2NBQ0wrRSxZQUFZLEdBQ1YsQ0FBQyxJQUFJO0FBQ3pCLHNCQUFzQixDQUFDWSxPQUFLO0FBQzVCLHNCQUFzQixDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTTtBQUMzQztBQUNBLHNCQUFzQixFQUFFLElBQUk7QUFDNUIsb0JBQW9CLEVBQUUsSUFBSSxDQUNQO1lBQ0g7VUFDRjtRQUNGLENBQUMsTUFBTSxJQUFJNU0sUUFBTSxDQUFDM0IsSUFBSSxLQUFLLFNBQVMsRUFBRTtVQUNwQyxJQUFJdUssUUFBUSxFQUFFO1lBQ1pvRCxZQUFZLEdBQUdWLFFBQVEsR0FDckIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUNPLFdBQVcsQ0FBQyxDQUFDLElBQUk7QUFDaEQsb0JBQW9CLENBQUM1RSxPQUFLLEdBQUdwTCxPQUFPLENBQUM2USxVQUFVLEdBQUc3USxPQUFPLENBQUM4USxXQUFXO0FBQ3JFLGtCQUFrQixFQUFFLElBQUksQ0FBQyxHQUVQLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDOVEsT0FBTyxDQUFDOFEsV0FBVyxDQUFDLEVBQUUsSUFBSSxDQUMzQztVQUNILENBQUMsTUFBTTtZQUNMWCxZQUFZLEdBQUdWLFFBQVEsR0FDckIsQ0FBQyxJQUFJO0FBQ3ZCLG9CQUFvQixDQUFDckUsT0FBSyxHQUFHcEwsT0FBTyxDQUFDNlEsVUFBVSxHQUFHN1EsT0FBTyxDQUFDOFEsV0FBVztBQUNyRSxrQkFBa0IsRUFBRSxJQUFJLENBQUMsR0FFUCxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTTtBQUN2QztBQUNBLGtCQUFrQixFQUFFLElBQUksQ0FDUDtVQUNIO1FBQ0YsQ0FBQyxNQUFNLElBQUl6TyxXQUFXLENBQUM4QixRQUFNLENBQUMsRUFBRTtVQUM5QixJQUFJNEksUUFBUSxFQUFFO1lBQ1pvRCxZQUFZLEdBQ1YsQ0FBQyxTQUFTLENBQ1IsS0FBSyxDQUFDLENBQUNuSSxjQUFjLENBQUMsQ0FDdEIsUUFBUSxDQUFDLENBQUNrRSxxQkFBcUIsQ0FBQyxDQUNoQyxRQUFRLENBQUMsQ0FBQ0UscUJBQXFCLENBQUMsQ0FDaEMsV0FBVyxDQUFDLENBQUMsd0JBQXdCLENBQUMsQ0FDdEMsT0FBTyxDQUFDLENBQUNrQyxJQUFJLENBQUNoRSxHQUFHLENBQUNkLE9BQU8sR0FBRyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FDcEMsWUFBWSxDQUFDLENBQUNwQixxQkFBcUIsQ0FBQyxDQUNwQyxvQkFBb0IsQ0FBQyxDQUFDQyx3QkFBd0IsQ0FBQyxDQUMvQyxLQUFLLENBQ0wsVUFBVSxHQUViO1VBQ0gsQ0FBQyxNQUFNO1lBQ0wsTUFBTWlKLFlBQVksR0FDaEI3QixRQUFRLElBQUlyTyxnQkFBZ0IsQ0FBQytDLFFBQU0sQ0FBQyxHQUNoQ0YsaUJBQWlCLENBQUNnRCxNQUFNLENBQUNtRSxPQUFLLENBQUMsRUFBRWpILFFBQU0sQ0FBQyxHQUN4QzhDLE1BQU0sQ0FBQ21FLE9BQUssQ0FBQztZQUNuQitFLFlBQVksR0FBR1YsUUFBUSxHQUNyQixDQUFDLElBQUksQ0FBQyxDQUFDNkIsWUFBWSxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBRTNCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNO0FBQ3ZDO0FBQ0Esa0JBQWtCLEVBQUUsSUFBSSxDQUNQO1VBQ0g7UUFDRixDQUFDLE1BQU07VUFDTG5CLFlBQVksR0FBR1YsUUFBUSxHQUNyQixDQUFDLElBQUksQ0FBQyxDQUFDeEksTUFBTSxDQUFDbUUsT0FBSyxDQUFDLENBQUMsRUFBRSxJQUFJLENBQUMsR0FFNUIsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU07QUFDckM7QUFDQSxnQkFBZ0IsRUFBRSxJQUFJLENBQ1A7UUFDSDtRQUVBLE9BQ0UsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUN4RCxNQUFJLENBQUMsQ0FBQyxhQUFhLENBQUMsUUFBUTtBQUNwRCxnQkFBZ0IsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzVCLGtCQUFrQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQ21JLGNBQWMsQ0FBQztBQUM5QyxvQkFBb0IsQ0FBQ2hELFFBQVEsR0FBRy9NLE9BQU8sQ0FBQzRRLE9BQU8sR0FBRyxHQUFHO0FBQ3JELGtCQUFrQixFQUFFLElBQUk7QUFDeEIsa0JBQWtCLENBQUNoQixRQUFRO0FBQzNCLGtCQUFrQixDQUFDLEdBQUc7QUFDdEIsb0JBQW9CLENBQUNLLEtBQUs7QUFDMUIsb0JBQW9CLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDRCxXQUFXLENBQUMsQ0FBQyxFQUFFLEVBQUUsSUFBSTtBQUN0RCxvQkFBb0IsQ0FBQ0csWUFBWTtBQUNqQyxrQkFBa0IsRUFBRSxHQUFHO0FBQ3ZCLGdCQUFnQixFQUFFLEdBQUc7QUFDckIsZ0JBQWdCLENBQUNDLGdCQUFnQjtBQUNqQyxnQkFBZ0IsQ0FBQ2pNLFFBQU0sQ0FBQ29OLFdBQVcsSUFDakIsQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3JDLG9CQUFvQixDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQ3BOLFFBQU0sQ0FBQ29OLFdBQVcsQ0FBQyxFQUFFLElBQUk7QUFDN0Qsa0JBQWtCLEVBQUUsR0FBRyxDQUNOO0FBQ2pCLGdCQUFnQixDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDOUMsa0JBQWtCLENBQUNwSyxPQUFLLEdBQ0osQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxNQUFNO0FBQzlDLHNCQUFzQixDQUFDQSxPQUFLO0FBQzVCLG9CQUFvQixFQUFFLElBQUksQ0FBQyxHQUVQLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxJQUFJLENBQ2Q7QUFDbkIsZ0JBQWdCLEVBQUUsR0FBRztBQUNyQixjQUFjLEVBQUUsR0FBRyxDQUFDO01BRVYsQ0FBQyxDQUFDO0FBQ1osUUFBUSxDQUFDaUksY0FBYyxJQUNiLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUM3QixZQUFZLENBQUMsSUFBSSxDQUFDLFFBQVE7QUFDMUIsY0FBYyxDQUFDcFAsT0FBTyxDQUFDd1IsU0FBUyxDQUFDLENBQUMsQ0FBQ2hLLFlBQVksQ0FBQzVFLE1BQU0sR0FBR2tNLFlBQVksQ0FBQ0csR0FBRyxDQUFDO0FBQzFFO0FBQ0EsWUFBWSxFQUFFLElBQUk7QUFDbEIsVUFBVSxFQUFFLEdBQUcsQ0FDTjtBQUNULE1BQU0sRUFBRSxHQUFHLENBQUM7RUFFVjtFQUVBLE9BQ0UsQ0FBQyxNQUFNLENBQ0wsS0FBSyxDQUFDLENBQUMsb0JBQW9CdkosVUFBVSw0QkFBNEIsQ0FBQyxDQUNsRSxRQUFRLENBQUMsQ0FBQyxLQUFLRyxPQUFPLEVBQUUsQ0FBQyxDQUN6QixLQUFLLENBQUMsWUFBWSxDQUNsQixRQUFRLENBQUMsQ0FBQyxNQUFNNUQsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQ3JDLGNBQWMsQ0FBQyxDQUFDLENBQUMsQ0FBQ3lILFlBQVksSUFBSSxDQUFDLENBQUN2RCxhQUFhLEtBQUssQ0FBQ3NDLGlCQUFpQixDQUFDLENBQ3pFLFVBQVUsQ0FBQyxDQUFDZ0osU0FBUyxJQUNuQkEsU0FBUyxDQUFDQyxPQUFPLEdBQ2YsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDRCxTQUFTLENBQUNFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLEdBRXBELENBQUMsTUFBTTtBQUNqQixZQUFZLENBQUMsd0JBQXdCLENBQ3ZCLE1BQU0sQ0FBQyxZQUFZLENBQ25CLE9BQU8sQ0FBQyxjQUFjLENBQ3RCLFFBQVEsQ0FBQyxLQUFLLENBQ2QsV0FBVyxDQUFDLFFBQVE7QUFFbEMsWUFBWSxDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFVBQVU7QUFDakUsWUFBWSxDQUFDakksWUFBWSxJQUNYLENBQUMsb0JBQW9CLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxNQUFNLENBQUMsT0FBTyxHQUMxRDtBQUNiLFlBQVksQ0FBQ0EsWUFBWSxJQUFJQSxZQUFZLENBQUN2RixNQUFNLENBQUMzQixJQUFJLEtBQUssU0FBUyxJQUNyRCxDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsR0FDdkQ7QUFDYixZQUFZLENBQUNrSCxZQUFZLElBQ1hySSxZQUFZLENBQUNxSSxZQUFZLENBQUN2RixNQUFNLENBQUMsS0FDaENzRSxpQkFBaUIsR0FDaEIsQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEdBQUcsR0FFekQsQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEdBQ25ELENBQUM7QUFDaEIsWUFBWSxDQUFDaUIsWUFBWSxJQUNYcEksdUJBQXVCLENBQUNvSSxZQUFZLENBQUN2RixNQUFNLENBQUMsS0FDM0NzRSxpQkFBaUIsR0FDaEIsQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEdBQUcsR0FFekQsQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEdBQ25ELENBQUM7QUFDaEIsVUFBVSxFQUFFLE1BQU0sQ0FFWixDQUFDO0FBRVAsTUFBTSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsUUFBUTtBQUNqQyxRQUFRLENBQUM0RyxnQkFBZ0IsQ0FBQyxDQUFDO0FBQzNCLFFBQVEsQ0FBQyxHQUFHO0FBQ1osVUFBVSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUztBQUMvQixZQUFZLENBQUNsSixhQUFhLEtBQUssUUFBUSxHQUFHbkcsT0FBTyxDQUFDNFEsT0FBTyxHQUFHLEdBQUc7QUFDL0QsVUFBVSxFQUFFLElBQUk7QUFDaEIsVUFBVSxDQUFDLElBQUksQ0FDSCxJQUFJLENBQUMsQ0FBQ3pLLGFBQWEsS0FBSyxRQUFRLENBQUMsQ0FDakMsS0FBSyxDQUFDLENBQUNBLGFBQWEsS0FBSyxRQUFRLEdBQUcsU0FBUyxHQUFHaEQsU0FBUyxDQUFDLENBQzFELFFBQVEsQ0FBQyxDQUFDZ0QsYUFBYSxLQUFLLFFBQVEsQ0FBQztBQUVqRCxZQUFZLENBQUMsV0FBVztBQUN4QixVQUFVLEVBQUUsSUFBSTtBQUNoQixVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPO0FBQzdCLFlBQVksQ0FBQ0EsYUFBYSxLQUFLLFNBQVMsR0FBR25HLE9BQU8sQ0FBQzRRLE9BQU8sR0FBRyxHQUFHO0FBQ2hFLFVBQVUsRUFBRSxJQUFJO0FBQ2hCLFVBQVUsQ0FBQyxJQUFJLENBQ0gsSUFBSSxDQUFDLENBQUN6SyxhQUFhLEtBQUssU0FBUyxDQUFDLENBQ2xDLEtBQUssQ0FBQyxDQUFDQSxhQUFhLEtBQUssU0FBUyxHQUFHLE9BQU8sR0FBR2hELFNBQVMsQ0FBQyxDQUN6RCxRQUFRLENBQUMsQ0FBQ2dELGFBQWEsS0FBSyxTQUFTLENBQUM7QUFFbEQsWUFBWSxDQUFDLFVBQVU7QUFDdkIsVUFBVSxFQUFFLElBQUk7QUFDaEIsUUFBUSxFQUFFLEdBQUc7QUFDYixNQUFNLEVBQUUsR0FBRztBQUNYLElBQUksRUFBRSxNQUFNLENBQUM7QUFFYjtBQUVBLFNBQVN5TCxvQkFBb0JBLENBQUM7RUFDNUI1UCxLQUFLO0VBQ0xDLFVBQVU7RUFDVkc7QUFLRixDQUpDLEVBQUU7RUFDREosS0FBSyxFQUFFbEIsdUJBQXVCO0VBQzlCbUIsVUFBVSxFQUFFRixLQUFLLENBQUMsWUFBWSxDQUFDO0VBQy9CSyxnQkFBZ0IsRUFBRUwsS0FBSyxDQUFDLGtCQUFrQixDQUFDO0FBQzdDLENBQUMsQ0FBQyxFQUFFOUIsS0FBSyxDQUFDd0YsU0FBUyxDQUFDO0VBQ2xCLE1BQU07SUFBRUMsVUFBVTtJQUFFQyxNQUFNO0lBQUVrTTtFQUFhLENBQUMsR0FBRzdQLEtBQUs7RUFDbEQsTUFBTThQLFNBQVMsR0FBRzlQLEtBQUssQ0FBQ3NELE1BQU0sSUFBSXpGLHNCQUFzQjtFQUN4RCxNQUFNO0lBQUVnRyxPQUFPO0lBQUVrTTtFQUFJLENBQUMsR0FBR0QsU0FBUztFQUNsQyxNQUFNLENBQUNFLEtBQUssRUFBRUMsUUFBUSxDQUFDLEdBQUczUixRQUFRLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQyxDQUFDLFFBQVEsQ0FBQztFQUNsRSxNQUFNNFIsUUFBUSxHQUFHN1IsTUFBTSxDQUFDLFFBQVEsR0FBRyxTQUFTLENBQUMsQ0FBQyxRQUFRLENBQUM7RUFDdkQsTUFBTSxDQUFDOEYsYUFBYSxFQUFFQyxnQkFBZ0IsQ0FBQyxHQUFHOUYsUUFBUSxDQUNoRCxRQUFRLEdBQUcsU0FBUyxHQUFHLE1BQU0sR0FBRyxRQUFRLEdBQUcsUUFBUSxDQUNwRCxDQUFDLFFBQVEsQ0FBQztFQUNYLE1BQU02UixVQUFVLEdBQUdOLFlBQVksRUFBRU0sVUFBVSxJQUFJLEtBQUs7RUFFcEQzUixxQkFBcUIsQ0FDbkIsOEJBQThCLEVBQzlCLHdCQUNGLENBQUM7RUFDREQsa0JBQWtCLENBQUMsaUJBQWlCLENBQUM7O0VBRXJDO0VBQ0EyUixRQUFRLENBQUNoSixPQUFPLEdBQUc4SSxLQUFLO0VBQ3hCLE1BQU1JLG1CQUFtQixHQUFHL1IsTUFBTSxDQUFDK0IsZ0JBQWdCLENBQUM7RUFDcERnUSxtQkFBbUIsQ0FBQ2xKLE9BQU8sR0FBRzlHLGdCQUFnQjtFQUU5Q2pDLFNBQVMsQ0FBQyxNQUFNO0lBQ2QsTUFBTWlILFdBQVcsR0FBR0EsQ0FBQSxLQUFNO01BQ3hCLElBQUk4SyxRQUFRLENBQUNoSixPQUFPLEtBQUssU0FBUyxFQUFFO1FBQ2xDa0osbUJBQW1CLENBQUNsSixPQUFPLEdBQUcsUUFBUSxDQUFDO01BQ3pDLENBQUMsTUFBTTtRQUNMakgsVUFBVSxDQUFDLFFBQVEsQ0FBQztNQUN0QjtJQUNGLENBQUM7SUFDRCxJQUFJMEQsTUFBTSxDQUFDMEIsT0FBTyxFQUFFO01BQ2xCRCxXQUFXLENBQUMsQ0FBQztNQUNiO0lBQ0Y7SUFDQXpCLE1BQU0sQ0FBQzJCLGdCQUFnQixDQUFDLE9BQU8sRUFBRUYsV0FBVyxDQUFDO0lBQzdDLE9BQU8sTUFBTXpCLE1BQU0sQ0FBQzRCLG1CQUFtQixDQUFDLE9BQU8sRUFBRUgsV0FBVyxDQUFDO0VBQy9ELENBQUMsRUFBRSxDQUFDekIsTUFBTSxFQUFFMUQsVUFBVSxDQUFDLENBQUM7O0VBRXhCO0VBQ0EsSUFBSW9RLE1BQU0sR0FBRyxFQUFFO0VBQ2YsSUFBSUMsZUFBZSxHQUFHLEVBQUU7RUFDeEIsSUFBSUMsY0FBYyxHQUFHLEVBQUU7RUFDdkIsSUFBSTtJQUNGLE1BQU1DLE1BQU0sR0FBRyxJQUFJQyxHQUFHLENBQUNWLEdBQUcsQ0FBQztJQUMzQk0sTUFBTSxHQUFHRyxNQUFNLENBQUNFLFFBQVE7SUFDeEIsTUFBTUMsV0FBVyxHQUFHWixHQUFHLENBQUN4RCxPQUFPLENBQUM4RCxNQUFNLENBQUM7SUFDdkNDLGVBQWUsR0FBR1AsR0FBRyxDQUFDeEMsS0FBSyxDQUFDLENBQUMsRUFBRW9ELFdBQVcsQ0FBQztJQUMzQ0osY0FBYyxHQUFHUixHQUFHLENBQUN4QyxLQUFLLENBQUNvRCxXQUFXLEdBQUdOLE1BQU0sQ0FBQ3pQLE1BQU0sQ0FBQztFQUN6RCxDQUFDLENBQUMsTUFBTTtJQUNOeVAsTUFBTSxHQUFHTixHQUFHO0VBQ2Q7O0VBRUE7RUFDQTVSLFNBQVMsQ0FBQyxNQUFNO0lBQ2QsSUFBSTZSLEtBQUssS0FBSyxTQUFTLElBQUloUSxLQUFLLENBQUM0USxTQUFTLEVBQUU7TUFDMUN4USxnQkFBZ0IsR0FBRytQLFVBQVUsR0FBRyxPQUFPLEdBQUcsU0FBUyxDQUFDO0lBQ3REO0VBQ0YsQ0FBQyxFQUFFLENBQUNILEtBQUssRUFBRWhRLEtBQUssQ0FBQzRRLFNBQVMsRUFBRXhRLGdCQUFnQixFQUFFK1AsVUFBVSxDQUFDLENBQUM7RUFFMUQsTUFBTVUsWUFBWSxHQUFHM1MsV0FBVyxDQUFDLE1BQU07SUFDckMsS0FBS2EsV0FBVyxDQUFDZ1IsR0FBRyxDQUFDO0lBQ3JCOVAsVUFBVSxDQUFDLFFBQVEsQ0FBQztJQUNwQmdRLFFBQVEsQ0FBQyxTQUFTLENBQUM7SUFDbkJDLFFBQVEsQ0FBQ2hKLE9BQU8sR0FBRyxTQUFTO0lBQzVCOUMsZ0JBQWdCLENBQUMsTUFBTSxDQUFDO0VBQzFCLENBQUMsRUFBRSxDQUFDbkUsVUFBVSxFQUFFOFAsR0FBRyxDQUFDLENBQUM7O0VBRXJCO0VBQ0FuUixRQUFRLENBQUMsQ0FBQ29NLE1BQU0sRUFBRUMsR0FBRyxLQUFLO0lBQ3hCLElBQUkrRSxLQUFLLEtBQUssUUFBUSxFQUFFO01BQ3RCLElBQUkvRSxHQUFHLENBQUNPLFNBQVMsSUFBSVAsR0FBRyxDQUFDaUIsVUFBVSxFQUFFO1FBQ25DOUgsZ0JBQWdCLENBQUNpRixJQUFJLElBQUtBLElBQUksS0FBSyxRQUFRLEdBQUcsU0FBUyxHQUFHLFFBQVMsQ0FBQztRQUNwRTtNQUNGO01BQ0EsSUFBSTRCLEdBQUcsQ0FBQ0csTUFBTSxFQUFFO1FBQ2QsSUFBSWpILGFBQWEsS0FBSyxRQUFRLEVBQUU7VUFDOUIwTSxZQUFZLENBQUMsQ0FBQztRQUNoQixDQUFDLE1BQU07VUFDTDVRLFVBQVUsQ0FBQyxTQUFTLENBQUM7UUFDdkI7TUFDRjtJQUNGLENBQUMsTUFBTTtNQUNMO01BQ0EsS0FBSzZRLFVBQVUsR0FBRyxRQUFRLEdBQUcsU0FBUyxHQUFHLE1BQU0sR0FBRyxRQUFRLEdBQUcsUUFBUTtNQUNyRSxNQUFNQyxjQUFjLEVBQUUsU0FBU0QsVUFBVSxFQUFFLEdBQUdYLFVBQVUsR0FDcEQsQ0FBQyxNQUFNLEVBQUUsUUFBUSxFQUFFLFFBQVEsQ0FBQyxHQUM1QixDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUM7TUFDdEIsSUFBSWxGLEdBQUcsQ0FBQ08sU0FBUyxJQUFJUCxHQUFHLENBQUNpQixVQUFVLEVBQUU7UUFDbkM5SCxnQkFBZ0IsQ0FBQ2lGLE1BQUksSUFBSTtVQUN2QixNQUFNMkgsR0FBRyxHQUFHRCxjQUFjLENBQUN4RSxPQUFPLENBQUNsRCxNQUFJLENBQUM7VUFDeEMsTUFBTTRILEtBQUssR0FBR2hHLEdBQUcsQ0FBQ2lCLFVBQVUsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1VBQ3JDLE9BQU82RSxjQUFjLENBQ25CLENBQUNDLEdBQUcsR0FBR0MsS0FBSyxHQUFHRixjQUFjLENBQUNuUSxNQUFNLElBQUltUSxjQUFjLENBQUNuUSxNQUFNLENBQzlELENBQUM7UUFDSixDQUFDLENBQUM7UUFDRjtNQUNGO01BQ0EsSUFBSXFLLEdBQUcsQ0FBQ0csTUFBTSxFQUFFO1FBQ2QsSUFBSWpILGFBQWEsS0FBSyxNQUFNLEVBQUU7VUFDNUIsS0FBS3BGLFdBQVcsQ0FBQ2dSLEdBQUcsQ0FBQztRQUN2QixDQUFDLE1BQU0sSUFBSTVMLGFBQWEsS0FBSyxRQUFRLEVBQUU7VUFDckMvRCxnQkFBZ0IsR0FBRyxRQUFRLENBQUM7UUFDOUIsQ0FBQyxNQUFNO1VBQ0xBLGdCQUFnQixHQUFHK1AsVUFBVSxHQUFHLE9BQU8sR0FBRyxTQUFTLENBQUM7UUFDdEQ7TUFDRjtJQUNGO0VBQ0YsQ0FBQyxDQUFDO0VBRUYsSUFBSUgsS0FBSyxLQUFLLFNBQVMsRUFBRTtJQUN2QixNQUFNa0IsV0FBVyxHQUFHckIsWUFBWSxFQUFFcUIsV0FBVyxJQUFJLDBCQUEwQjtJQUMzRSxPQUNFLENBQUMsTUFBTSxDQUNMLEtBQUssQ0FBQyxDQUFDLG9CQUFvQnhOLFVBQVUsc0NBQXNDLENBQUMsQ0FDNUUsUUFBUSxDQUFDLENBQUMsS0FBS0csT0FBTyxFQUFFLENBQUMsQ0FDekIsS0FBSyxDQUFDLFlBQVksQ0FDbEIsUUFBUSxDQUFDLENBQUMsTUFBTXpELGdCQUFnQixHQUFHLFFBQVEsQ0FBQyxDQUFDLENBQzdDLGNBQWMsQ0FDZCxVQUFVLENBQUMsQ0FBQ3FQLFNBQVMsSUFDbkJBLFNBQVMsQ0FBQ0MsT0FBTyxHQUNmLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQ0QsU0FBUyxDQUFDRSxPQUFPLENBQUMsY0FBYyxFQUFFLElBQUksQ0FBQyxHQUVwRCxDQUFDLE1BQU07QUFDbkIsY0FBYyxDQUFDLHdCQUF3QixDQUN2QixNQUFNLENBQUMsWUFBWSxDQUNuQixPQUFPLENBQUMsY0FBYyxDQUN0QixRQUFRLENBQUMsS0FBSyxDQUNkLFdBQVcsQ0FBQyxRQUFRO0FBRXBDLGNBQWMsQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsY0FBYyxDQUFDLE1BQU0sQ0FBQyxRQUFRO0FBQzNFLFlBQVksRUFBRSxNQUFNLENBRVosQ0FBQztBQUVULFFBQVEsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLFFBQVE7QUFDbkMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxhQUFhLENBQUMsUUFBUTtBQUN0RCxZQUFZLENBQUMsSUFBSTtBQUNqQixjQUFjLENBQUNXLGVBQWU7QUFDOUIsY0FBYyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQ0QsTUFBTSxDQUFDLEVBQUUsSUFBSTtBQUN2QyxjQUFjLENBQUNFLGNBQWM7QUFDN0IsWUFBWSxFQUFFLElBQUk7QUFDbEIsVUFBVSxFQUFFLEdBQUc7QUFDZixVQUFVLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUMvQixZQUFZLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxNQUFNO0FBQ2pDO0FBQ0EsWUFBWSxFQUFFLElBQUk7QUFDbEIsVUFBVSxFQUFFLEdBQUc7QUFDZixVQUFVLENBQUMsR0FBRztBQUNkLFlBQVksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVM7QUFDakMsY0FBYyxDQUFDcE0sYUFBYSxLQUFLLE1BQU0sR0FBR25HLE9BQU8sQ0FBQzRRLE9BQU8sR0FBRyxHQUFHO0FBQy9ELFlBQVksRUFBRSxJQUFJO0FBQ2xCLFlBQVksQ0FBQyxJQUFJLENBQ0gsSUFBSSxDQUFDLENBQUN6SyxhQUFhLEtBQUssTUFBTSxDQUFDLENBQy9CLEtBQUssQ0FBQyxDQUFDQSxhQUFhLEtBQUssTUFBTSxHQUFHLFNBQVMsR0FBR2hELFNBQVMsQ0FBQyxDQUN4RCxRQUFRLENBQUMsQ0FBQ2dELGFBQWEsS0FBSyxNQUFNLENBQUM7QUFFakQsY0FBYyxDQUFDLGVBQWU7QUFDOUIsWUFBWSxFQUFFLElBQUk7QUFDbEIsWUFBWSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsU0FBUztBQUNqQyxjQUFjLENBQUNBLGFBQWEsS0FBSyxRQUFRLEdBQUduRyxPQUFPLENBQUM0USxPQUFPLEdBQUcsR0FBRztBQUNqRSxZQUFZLEVBQUUsSUFBSTtBQUNsQixZQUFZLENBQUMsSUFBSSxDQUNILElBQUksQ0FBQyxDQUFDekssYUFBYSxLQUFLLFFBQVEsQ0FBQyxDQUNqQyxLQUFLLENBQUMsQ0FBQ0EsYUFBYSxLQUFLLFFBQVEsR0FBRyxTQUFTLEdBQUdoRCxTQUFTLENBQUMsQ0FDMUQsUUFBUSxDQUFDLENBQUNnRCxhQUFhLEtBQUssUUFBUSxDQUFDO0FBRW5ELGNBQWMsQ0FBQyxJQUFJK00sV0FBVyxFQUFFO0FBQ2hDLFlBQVksRUFBRSxJQUFJO0FBQ2xCLFlBQVksQ0FBQ2YsVUFBVSxJQUNUO0FBQ2QsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLENBQUMsRUFBRSxJQUFJO0FBQzdCLGdCQUFnQixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTztBQUNuQyxrQkFBa0IsQ0FBQ2hNLGFBQWEsS0FBSyxRQUFRLEdBQUduRyxPQUFPLENBQUM0USxPQUFPLEdBQUcsR0FBRztBQUNyRSxnQkFBZ0IsRUFBRSxJQUFJO0FBQ3RCLGdCQUFnQixDQUFDLElBQUksQ0FDSCxJQUFJLENBQUMsQ0FBQ3pLLGFBQWEsS0FBSyxRQUFRLENBQUMsQ0FDakMsS0FBSyxDQUFDLENBQUNBLGFBQWEsS0FBSyxRQUFRLEdBQUcsT0FBTyxHQUFHaEQsU0FBUyxDQUFDLENBQ3hELFFBQVEsQ0FBQyxDQUFDZ0QsYUFBYSxLQUFLLFFBQVEsQ0FBQztBQUV2RCxrQkFBa0IsQ0FBQyxTQUFTO0FBQzVCLGdCQUFnQixFQUFFLElBQUk7QUFDdEIsY0FBYyxHQUNEO0FBQ2IsVUFBVSxFQUFFLEdBQUc7QUFDZixRQUFRLEVBQUUsR0FBRztBQUNiLE1BQU0sRUFBRSxNQUFNLENBQUM7RUFFYjtFQUVBLE9BQ0UsQ0FBQyxNQUFNLENBQ0wsS0FBSyxDQUFDLENBQUMsb0JBQW9CVCxVQUFVLDRCQUE0QixDQUFDLENBQ2xFLFFBQVEsQ0FBQyxDQUFDLEtBQUtHLE9BQU8sRUFBRSxDQUFDLENBQ3pCLEtBQUssQ0FBQyxZQUFZLENBQ2xCLFFBQVEsQ0FBQyxDQUFDLE1BQU01RCxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FDckMsY0FBYyxDQUNkLFVBQVUsQ0FBQyxDQUFDd1AsV0FBUyxJQUNuQkEsV0FBUyxDQUFDQyxPQUFPLEdBQ2YsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDRCxXQUFTLENBQUNFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLEdBRXBELENBQUMsTUFBTTtBQUNqQixZQUFZLENBQUMsd0JBQXdCLENBQ3ZCLE1BQU0sQ0FBQyxZQUFZLENBQ25CLE9BQU8sQ0FBQyxjQUFjLENBQ3RCLFFBQVEsQ0FBQyxLQUFLLENBQ2QsV0FBVyxDQUFDLFFBQVE7QUFFbEMsWUFBWSxDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLFFBQVE7QUFDekUsVUFBVSxFQUFFLE1BQU0sQ0FFWixDQUFDO0FBRVAsTUFBTSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsUUFBUTtBQUNqQyxRQUFRLENBQUMsR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxRQUFRO0FBQ3BELFVBQVUsQ0FBQyxJQUFJO0FBQ2YsWUFBWSxDQUFDVyxlQUFlO0FBQzVCLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUNELE1BQU0sQ0FBQyxFQUFFLElBQUk7QUFDckMsWUFBWSxDQUFDRSxjQUFjO0FBQzNCLFVBQVUsRUFBRSxJQUFJO0FBQ2hCLFFBQVEsRUFBRSxHQUFHO0FBQ2IsUUFBUSxDQUFDLEdBQUc7QUFDWixVQUFVLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTO0FBQy9CLFlBQVksQ0FBQ3BNLGFBQWEsS0FBSyxRQUFRLEdBQUduRyxPQUFPLENBQUM0USxPQUFPLEdBQUcsR0FBRztBQUMvRCxVQUFVLEVBQUUsSUFBSTtBQUNoQixVQUFVLENBQUMsSUFBSSxDQUNILElBQUksQ0FBQyxDQUFDekssYUFBYSxLQUFLLFFBQVEsQ0FBQyxDQUNqQyxLQUFLLENBQUMsQ0FBQ0EsYUFBYSxLQUFLLFFBQVEsR0FBRyxTQUFTLEdBQUdoRCxTQUFTLENBQUMsQ0FDMUQsUUFBUSxDQUFDLENBQUNnRCxhQUFhLEtBQUssUUFBUSxDQUFDO0FBRWpELFlBQVksQ0FBQyxXQUFXO0FBQ3hCLFVBQVUsRUFBRSxJQUFJO0FBQ2hCLFVBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU87QUFDN0IsWUFBWSxDQUFDQSxhQUFhLEtBQUssU0FBUyxHQUFHbkcsT0FBTyxDQUFDNFEsT0FBTyxHQUFHLEdBQUc7QUFDaEUsVUFBVSxFQUFFLElBQUk7QUFDaEIsVUFBVSxDQUFDLElBQUksQ0FDSCxJQUFJLENBQUMsQ0FBQ3pLLGFBQWEsS0FBSyxTQUFTLENBQUMsQ0FDbEMsS0FBSyxDQUFDLENBQUNBLGFBQWEsS0FBSyxTQUFTLEdBQUcsT0FBTyxHQUFHaEQsU0FBUyxDQUFDLENBQ3pELFFBQVEsQ0FBQyxDQUFDZ0QsYUFBYSxLQUFLLFNBQVMsQ0FBQztBQUVsRCxZQUFZLENBQUMsVUFBVTtBQUN2QixVQUFVLEVBQUUsSUFBSTtBQUNoQixRQUFRLEVBQUUsR0FBRztBQUNiLE1BQU0sRUFBRSxHQUFHO0FBQ1gsSUFBSSxFQUFFLE1BQU0sQ0FBQztBQUViIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/mcp/MCPAgentServerMenu.tsx b/components/mcp/MCPAgentServerMenu.tsx new file mode 100644 index 0000000..367ff2f --- /dev/null +++ b/components/mcp/MCPAgentServerMenu.tsx @@ -0,0 +1,183 @@ +import figures from 'figures'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import type { CommandResultDisplay } from '../../commands.js'; +import { Box, color, Link, Text, useTheme } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { AuthenticationCancelledError, performMCPOAuthFlow } from '../../services/mcp/auth.js'; +import { capitalize } from '../../utils/stringUtils.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { Select } from '../CustomSelect/index.js'; +import { Byline } from '../design-system/Byline.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { Spinner } from '../Spinner.js'; +import type { AgentMcpServerInfo } from './types.js'; +type Props = { + agentServer: AgentMcpServerInfo; + onCancel: () => void; + onComplete?: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; +}; + +/** + * Menu for agent-specific MCP servers. + * These servers are defined in agent frontmatter and only connect when the agent runs. + * For HTTP/SSE servers, this allows pre-authentication before using the agent. + */ +export function MCPAgentServerMenu({ + agentServer, + onCancel, + onComplete +}: Props): React.ReactNode { + const [theme] = useTheme(); + const [isAuthenticating, setIsAuthenticating] = useState(false); + const [error, setError] = useState(null); + const [authorizationUrl, setAuthorizationUrl] = useState(null); + const authAbortControllerRef = useRef(null); + + // Abort OAuth flow on unmount so the callback server is closed even if a + // parent component's Esc handler navigates away before ours fires. + useEffect(() => () => authAbortControllerRef.current?.abort(), []); + + // Handle ESC to cancel authentication flow + const handleEscCancel = useCallback(() => { + if (isAuthenticating) { + authAbortControllerRef.current?.abort(); + authAbortControllerRef.current = null; + setIsAuthenticating(false); + setAuthorizationUrl(null); + } + }, [isAuthenticating]); + useKeybinding('confirm:no', handleEscCancel, { + context: 'Confirmation', + isActive: isAuthenticating + }); + const handleAuthenticate = useCallback(async () => { + if (!agentServer.needsAuth || !agentServer.url) { + return; + } + setIsAuthenticating(true); + setError(null); + const controller = new AbortController(); + authAbortControllerRef.current = controller; + try { + // Create a temporary config for OAuth + const tempConfig = { + type: agentServer.transport as 'http' | 'sse', + url: agentServer.url + }; + await performMCPOAuthFlow(agentServer.name, tempConfig, setAuthorizationUrl, controller.signal); + onComplete?.(`Authentication successful for ${agentServer.name}. The server will connect when the agent runs.`); + } catch (err) { + // Don't show error if it was a cancellation + if (err instanceof Error && !(err instanceof AuthenticationCancelledError)) { + setError(err.message); + } + } finally { + setIsAuthenticating(false); + authAbortControllerRef.current = null; + } + }, [agentServer, onComplete]); + const capitalizedServerName = capitalize(String(agentServer.name)); + if (isAuthenticating) { + return + Authenticating with {agentServer.name}… + + + A browser window will open for authentication + + {authorizationUrl && + + If your browser doesn't open automatically, copy this URL + manually: + + + } + + + Return here after authenticating in your browser.{' '} + + + + ; + } + const menuOptions = []; + + // Only show authenticate option for HTTP/SSE servers + if (agentServer.needsAuth) { + menuOptions.push({ + label: agentServer.isAuthenticated ? 'Re-authenticate' : 'Authenticate', + value: 'auth' + }); + } + menuOptions.push({ + label: 'Back', + value: 'back' + }); + return exitState.pending ? Press {exitState.keyName} again to exit : + + + + }> + + + Type: + {agentServer.transport} + + + {agentServer.url && + URL: + {agentServer.url} + } + + {agentServer.command && + Command: + {agentServer.command} + } + + + Used by: + {agentServer.sourceAgents.join(', ')} + + + + Status: + + {color('inactive', theme)(figures.radioOff)} not connected + (agent-only) + + + + {agentServer.needsAuth && + Auth: + {agentServer.isAuthenticated ? {color('success', theme)(figures.tick)} authenticated : + {color('warning', theme)(figures.triangleUpOutline)} may need + authentication + } + } + + + + This server connects only when running the agent. + + + {error && + Error: {error} + } + + + { + switch (value_0) { + case 'tools': + onViewTools(); + break; + case 'auth': + case 'reauth': + await handleAuthenticate(); + break; + case 'clear-auth': + await handleClearAuth(); + break; + case 'claudeai-auth': + await handleClaudeAIAuth(); + break; + case 'claudeai-clear-auth': + handleClaudeAIClearAuth(); + break; + case 'reconnectMcpServer': + setIsReconnecting(true); + try { + const result_1 = await reconnectMcpServer(server.name); + if (server.config.type === 'claudeai-proxy') { + logEvent('tengu_claudeai_mcp_reconnect', { + success: result_1.client.type === 'connected' + }); + } + const { + message: message_0 + } = handleReconnectResult(result_1, server.name); + onComplete?.(message_0); + } catch (err_2) { + if (server.config.type === 'claudeai-proxy') { + logEvent('tengu_claudeai_mcp_reconnect', { + success: false + }); + } + onComplete?.(handleReconnectError(err_2, server.name)); + } finally { + setIsReconnecting(false); + } + break; + case 'toggle-enabled': + await handleToggleEnabled(); + break; + case 'back': + onCancel(); + break; + } + }} onCancel={onCancel} /> + } + + + + + {exitState.pending ? <>Press {exitState.keyName} again to exit : + + + + } + + + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VFZmZlY3QiLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwiZ2V0T2F1dGhDb25maWciLCJ1c2VFeGl0T25DdHJsQ0RXaXRoS2V5YmluZGluZ3MiLCJ1c2VUZXJtaW5hbFNpemUiLCJzZXRDbGlwYm9hcmQiLCJCb3giLCJjb2xvciIsIkxpbmsiLCJUZXh0IiwidXNlSW5wdXQiLCJ1c2VUaGVtZSIsInVzZUtleWJpbmRpbmciLCJBdXRoZW50aWNhdGlvbkNhbmNlbGxlZEVycm9yIiwicGVyZm9ybU1DUE9BdXRoRmxvdyIsInJldm9rZVNlcnZlclRva2VucyIsImNsZWFyU2VydmVyQ2FjaGUiLCJ1c2VNY3BSZWNvbm5lY3QiLCJ1c2VNY3BUb2dnbGVFbmFibGVkIiwiZGVzY3JpYmVNY3BDb25maWdGaWxlUGF0aCIsImV4Y2x1ZGVDb21tYW5kc0J5U2VydmVyIiwiZXhjbHVkZVJlc291cmNlc0J5U2VydmVyIiwiZXhjbHVkZVRvb2xzQnlTZXJ2ZXIiLCJmaWx0ZXJNY3BQcm9tcHRzQnlTZXJ2ZXIiLCJ1c2VBcHBTdGF0ZSIsInVzZVNldEFwcFN0YXRlIiwiZ2V0T2F1dGhBY2NvdW50SW5mbyIsIm9wZW5Ccm93c2VyIiwiZXJyb3JNZXNzYWdlIiwibG9nTUNQRGVidWciLCJjYXBpdGFsaXplIiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiU2VsZWN0IiwiQnlsaW5lIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJTcGlubmVyIiwiVGV4dElucHV0IiwiQ2FwYWJpbGl0aWVzU2VjdGlvbiIsIkNsYXVkZUFJU2VydmVySW5mbyIsIkhUVFBTZXJ2ZXJJbmZvIiwiU1NFU2VydmVySW5mbyIsImhhbmRsZVJlY29ubmVjdEVycm9yIiwiaGFuZGxlUmVjb25uZWN0UmVzdWx0IiwiUHJvcHMiLCJzZXJ2ZXIiLCJzZXJ2ZXJUb29sc0NvdW50Iiwib25WaWV3VG9vbHMiLCJvbkNhbmNlbCIsIm9uQ29tcGxldGUiLCJyZXN1bHQiLCJvcHRpb25zIiwiZGlzcGxheSIsImJvcmRlcmxlc3MiLCJNQ1BSZW1vdGVTZXJ2ZXJNZW51IiwiUmVhY3ROb2RlIiwidGhlbWUiLCJleGl0U3RhdGUiLCJjb2x1bW5zIiwidGVybWluYWxDb2x1bW5zIiwiaXNBdXRoZW50aWNhdGluZyIsInNldElzQXV0aGVudGljYXRpbmciLCJlcnJvciIsInNldEVycm9yIiwibWNwIiwicyIsInNldEFwcFN0YXRlIiwiYXV0aG9yaXphdGlvblVybCIsInNldEF1dGhvcml6YXRpb25VcmwiLCJpc1JlY29ubmVjdGluZyIsInNldElzUmVjb25uZWN0aW5nIiwiYXV0aEFib3J0Q29udHJvbGxlclJlZiIsIkFib3J0Q29udHJvbGxlciIsImlzQ2xhdWRlQUlBdXRoZW50aWNhdGluZyIsInNldElzQ2xhdWRlQUlBdXRoZW50aWNhdGluZyIsImNsYXVkZUFJQXV0aFVybCIsInNldENsYXVkZUFJQXV0aFVybCIsImlzQ2xhdWRlQUlDbGVhcmluZ0F1dGgiLCJzZXRJc0NsYXVkZUFJQ2xlYXJpbmdBdXRoIiwiY2xhdWRlQUlDbGVhckF1dGhVcmwiLCJzZXRDbGF1ZGVBSUNsZWFyQXV0aFVybCIsImNsYXVkZUFJQ2xlYXJBdXRoQnJvd3Nlck9wZW5lZCIsInNldENsYXVkZUFJQ2xlYXJBdXRoQnJvd3Nlck9wZW5lZCIsInVybENvcGllZCIsInNldFVybENvcGllZCIsImNvcHlUaW1lb3V0UmVmIiwiUmV0dXJuVHlwZSIsInNldFRpbWVvdXQiLCJ1bmRlZmluZWQiLCJ1bm1vdW50ZWRSZWYiLCJjYWxsYmFja1VybElucHV0Iiwic2V0Q2FsbGJhY2tVcmxJbnB1dCIsImNhbGxiYWNrVXJsQ3Vyc29yT2Zmc2V0Iiwic2V0Q2FsbGJhY2tVcmxDdXJzb3JPZmZzZXQiLCJtYW51YWxDYWxsYmFja1N1Ym1pdCIsInNldE1hbnVhbENhbGxiYWNrU3VibWl0IiwidXJsIiwiY3VycmVudCIsImFib3J0IiwiY2xlYXJUaW1lb3V0IiwiaXNFZmZlY3RpdmVseUF1dGhlbnRpY2F0ZWQiLCJpc0F1dGhlbnRpY2F0ZWQiLCJjbGllbnQiLCJ0eXBlIiwicmVjb25uZWN0TWNwU2VydmVyIiwiaGFuZGxlQ2xhdWRlQUlBdXRoQ29tcGxldGUiLCJ1c2VDYWxsYmFjayIsIm5hbWUiLCJzdWNjZXNzIiwiZXJyIiwiaGFuZGxlQ2xhdWRlQUlDbGVhckF1dGhDb21wbGV0ZSIsImNvbmZpZyIsInNjb3BlIiwicHJldiIsIm5ld0NsaWVudHMiLCJjbGllbnRzIiwibWFwIiwiYyIsImNvbnN0IiwibmV3VG9vbHMiLCJ0b29scyIsIm5ld0NvbW1hbmRzIiwiY29tbWFuZHMiLCJuZXdSZXNvdXJjZXMiLCJyZXNvdXJjZXMiLCJjb250ZXh0IiwiaXNBY3RpdmUiLCJpbnB1dCIsImtleSIsInJldHVybiIsImNvbm5lY3RvcnNVcmwiLCJDTEFVREVfQUlfT1JJR0lOIiwidXJsVG9Db3B5IiwidGhlbiIsInJhdyIsInByb2Nlc3MiLCJzdGRvdXQiLCJ3cml0ZSIsImNhcGl0YWxpemVkU2VydmVyTmFtZSIsIlN0cmluZyIsInNlcnZlckNvbW1hbmRzQ291bnQiLCJsZW5ndGgiLCJ0b2dnbGVNY3BTZXJ2ZXIiLCJoYW5kbGVDbGF1ZGVBSUF1dGgiLCJjbGF1ZGVBaUJhc2VVcmwiLCJhY2NvdW50SW5mbyIsIm9yZ1V1aWQiLCJvcmdhbml6YXRpb25VdWlkIiwiYXV0aFVybCIsImlkIiwic2VydmVySWQiLCJzdGFydHNXaXRoIiwic2xpY2UiLCJwcm9kdWN0U3VyZmFjZSIsImVuY29kZVVSSUNvbXBvbmVudCIsImVudiIsIkNMQVVERV9DT0RFX0VOVFJZUE9JTlQiLCJoYW5kbGVDbGF1ZGVBSUNsZWFyQXV0aCIsImhhbmRsZVRvZ2dsZUVuYWJsZWQiLCJ3YXNFbmFibGVkIiwibmV3X3N0YXRlIiwiYWN0aW9uIiwiaGFuZGxlQXV0aGVudGljYXRlIiwiY29udHJvbGxlciIsInByZXNlcnZlU3RlcFVwU3RhdGUiLCJzaWduYWwiLCJvbldhaXRpbmdGb3JDYWxsYmFjayIsInN1Ym1pdCIsIndhc0F1dGhlbnRpY2F0ZWQiLCJtZXNzYWdlIiwiRXJyb3IiLCJoYW5kbGVDbGVhckF1dGgiLCJhdXRoQ29weSIsIm9hdXRoIiwieGFhIiwidmFsdWUiLCJ0cmltIiwibWVudU9wdGlvbnMiLCJwdXNoIiwibGFiZWwiLCJyYWRpb09mZiIsInRpY2siLCJ0cmlhbmdsZVVwT3V0bGluZSIsImNyb3NzIiwidHJhbnNwb3J0IiwicGVuZGluZyIsImtleU5hbWUiXSwic291cmNlcyI6WyJNQ1BSZW1vdGVTZXJ2ZXJNZW51LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0IFJlYWN0LCB7IHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB0eXBlIHsgQ29tbWFuZFJlc3VsdERpc3BsYXkgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IGdldE9hdXRoQ29uZmlnIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL29hdXRoLmpzJ1xuaW1wb3J0IHsgdXNlRXhpdE9uQ3RybENEV2l0aEtleWJpbmRpbmdzIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlRXhpdE9uQ3RybENEV2l0aEtleWJpbmRpbmdzLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgc2V0Q2xpcGJvYXJkIH0gZnJvbSAnLi4vLi4vaW5rL3Rlcm1pby9vc2MuanMnXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL3ByZWZlci11c2Uta2V5YmluZGluZ3MgLS0gcmF3IGovay9hcnJvdyBtZW51IG5hdmlnYXRpb25cbmltcG9ydCB7IEJveCwgY29sb3IsIExpbmssIFRleHQsIHVzZUlucHV0LCB1c2VUaGVtZSB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHtcbiAgQXV0aGVudGljYXRpb25DYW5jZWxsZWRFcnJvcixcbiAgcGVyZm9ybU1DUE9BdXRoRmxvdyxcbiAgcmV2b2tlU2VydmVyVG9rZW5zLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvYXV0aC5qcydcbmltcG9ydCB7IGNsZWFyU2VydmVyQ2FjaGUgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvY2xpZW50LmpzJ1xuaW1wb3J0IHtcbiAgdXNlTWNwUmVjb25uZWN0LFxuICB1c2VNY3BUb2dnbGVFbmFibGVkLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvTUNQQ29ubmVjdGlvbk1hbmFnZXIuanMnXG5pbXBvcnQge1xuICBkZXNjcmliZU1jcENvbmZpZ0ZpbGVQYXRoLFxuICBleGNsdWRlQ29tbWFuZHNCeVNlcnZlcixcbiAgZXhjbHVkZVJlc291cmNlc0J5U2VydmVyLFxuICBleGNsdWRlVG9vbHNCeVNlcnZlcixcbiAgZmlsdGVyTWNwUHJvbXB0c0J5U2VydmVyLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvdXRpbHMuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSwgdXNlU2V0QXBwU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IGdldE9hdXRoQWNjb3VudEluZm8gfSBmcm9tICcuLi8uLi91dGlscy9hdXRoLmpzJ1xuaW1wb3J0IHsgb3BlbkJyb3dzZXIgfSBmcm9tICcuLi8uLi91dGlscy9icm93c2VyLmpzJ1xuaW1wb3J0IHsgZXJyb3JNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdXRpbHMvZXJyb3JzLmpzJ1xuaW1wb3J0IHsgbG9nTUNQRGVidWcgfSBmcm9tICcuLi8uLi91dGlscy9sb2cuanMnXG5pbXBvcnQgeyBjYXBpdGFsaXplIH0gZnJvbSAnLi4vLi4vdXRpbHMvc3RyaW5nVXRpbHMuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi9DdXN0b21TZWxlY3QvaW5kZXguanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IFNwaW5uZXIgfSBmcm9tICcuLi9TcGlubmVyLmpzJ1xuaW1wb3J0IFRleHRJbnB1dCBmcm9tICcuLi9UZXh0SW5wdXQuanMnXG5pbXBvcnQgeyBDYXBhYmlsaXRpZXNTZWN0aW9uIH0gZnJvbSAnLi9DYXBhYmlsaXRpZXNTZWN0aW9uLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBDbGF1ZGVBSVNlcnZlckluZm8sXG4gIEhUVFBTZXJ2ZXJJbmZvLFxuICBTU0VTZXJ2ZXJJbmZvLFxufSBmcm9tICcuL3R5cGVzLmpzJ1xuaW1wb3J0IHtcbiAgaGFuZGxlUmVjb25uZWN0RXJyb3IsXG4gIGhhbmRsZVJlY29ubmVjdFJlc3VsdCxcbn0gZnJvbSAnLi91dGlscy9yZWNvbm5lY3RIZWxwZXJzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBzZXJ2ZXI6IFNTRVNlcnZlckluZm8gfCBIVFRQU2VydmVySW5mbyB8IENsYXVkZUFJU2VydmVySW5mb1xuICBzZXJ2ZXJUb29sc0NvdW50OiBudW1iZXJcbiAgb25WaWV3VG9vbHM6ICgpID0+IHZvaWRcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbiAgb25Db21wbGV0ZT86IChcbiAgICByZXN1bHQ/OiBzdHJpbmcsXG4gICAgb3B0aW9ucz86IHsgZGlzcGxheT86IENvbW1hbmRSZXN1bHREaXNwbGF5IH0sXG4gICkgPT4gdm9pZFxuICBib3JkZXJsZXNzPzogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gTUNQUmVtb3RlU2VydmVyTWVudSh7XG4gIHNlcnZlcixcbiAgc2VydmVyVG9vbHNDb3VudCxcbiAgb25WaWV3VG9vbHMsXG4gIG9uQ2FuY2VsLFxuICBvbkNvbXBsZXRlLFxuICBib3JkZXJsZXNzID0gZmFsc2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZV0gPSB1c2VUaGVtZSgpXG4gIGNvbnN0IGV4aXRTdGF0ZSA9IHVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncygpXG4gIGNvbnN0IHsgY29sdW1uczogdGVybWluYWxDb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCBbaXNBdXRoZW50aWNhdGluZywgc2V0SXNBdXRoZW50aWNhdGluZ10gPSBSZWFjdC51c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW2Vycm9yLCBzZXRFcnJvcl0gPSBSZWFjdC51c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICBjb25zdCBtY3AgPSB1c2VBcHBTdGF0ZShzID0+IHMubWNwKVxuICBjb25zdCBzZXRBcHBTdGF0ZSA9IHVzZVNldEFwcFN0YXRlKClcbiAgY29uc3QgW2F1dGhvcml6YXRpb25VcmwsIHNldEF1dGhvcml6YXRpb25VcmxdID0gUmVhY3QudXNlU3RhdGU8c3RyaW5nIHwgbnVsbD4oXG4gICAgbnVsbCxcbiAgKVxuICBjb25zdCBbaXNSZWNvbm5lY3RpbmcsIHNldElzUmVjb25uZWN0aW5nXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBhdXRoQWJvcnRDb250cm9sbGVyUmVmID0gdXNlUmVmPEFib3J0Q29udHJvbGxlciB8IG51bGw+KG51bGwpXG4gIGNvbnN0IFtpc0NsYXVkZUFJQXV0aGVudGljYXRpbmcsIHNldElzQ2xhdWRlQUlBdXRoZW50aWNhdGluZ10gPVxuICAgIHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbY2xhdWRlQUlBdXRoVXJsLCBzZXRDbGF1ZGVBSUF1dGhVcmxdID0gdXNlU3RhdGU8c3RyaW5nIHwgbnVsbD4obnVsbClcbiAgY29uc3QgW2lzQ2xhdWRlQUlDbGVhcmluZ0F1dGgsIHNldElzQ2xhdWRlQUlDbGVhcmluZ0F1dGhdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtjbGF1ZGVBSUNsZWFyQXV0aFVybCwgc2V0Q2xhdWRlQUlDbGVhckF1dGhVcmxdID0gdXNlU3RhdGU8XG4gICAgc3RyaW5nIHwgbnVsbFxuICA+KG51bGwpXG4gIGNvbnN0IFtjbGF1ZGVBSUNsZWFyQXV0aEJyb3dzZXJPcGVuZWQsIHNldENsYXVkZUFJQ2xlYXJBdXRoQnJvd3Nlck9wZW5lZF0gPVxuICAgIHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbdXJsQ29waWVkLCBzZXRVcmxDb3BpZWRdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IGNvcHlUaW1lb3V0UmVmID0gdXNlUmVmPFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgdW5kZWZpbmVkPihcbiAgICB1bmRlZmluZWQsXG4gIClcbiAgY29uc3QgdW5tb3VudGVkUmVmID0gdXNlUmVmKGZhbHNlKVxuICBjb25zdCBbY2FsbGJhY2tVcmxJbnB1dCwgc2V0Q2FsbGJhY2tVcmxJbnB1dF0gPSB1c2VTdGF0ZSgnJylcbiAgY29uc3QgW2NhbGxiYWNrVXJsQ3Vyc29yT2Zmc2V0LCBzZXRDYWxsYmFja1VybEN1cnNvck9mZnNldF0gPSB1c2VTdGF0ZSgwKVxuICBjb25zdCBbbWFudWFsQ2FsbGJhY2tTdWJtaXQsIHNldE1hbnVhbENhbGxiYWNrU3VibWl0XSA9IHVzZVN0YXRlPFxuICAgICgodXJsOiBzdHJpbmcpID0+IHZvaWQpIHwgbnVsbFxuICA+KG51bGwpXG5cbiAgLy8gSWYgdGhlIGNvbXBvbmVudCB1bm1vdW50cyBtaWQtYXV0aCAoZS5nLiBhIHBhcmVudCBjb21wb25lbnQncyBFc2MgaGFuZGxlclxuICAvLyBuYXZpZ2F0ZXMgYXdheSBiZWZvcmUgb3VycyBmaXJlcyksIGFib3J0IHRoZSBPQXV0aCBmbG93IHNvIHRoZSBjYWxsYmFja1xuICAvLyBzZXJ2ZXIgaXMgY2xvc2VkLiBXaXRob3V0IHRoaXMsIHRoZSBzZXJ2ZXIgc3RheXMgYm91bmQgYW5kIHRoZSBwcm9jZXNzXG4gIC8vIGNhbiBvdXRsaXZlIHRoZSB0ZXJtaW5hbC4gQWxzbyBjbGVhciB0aGUgY29weS1mZWVkYmFjayB0aW1lciBhbmQgbWFya1xuICAvLyB1bm1vdW50ZWQgc28gdGhlIGFzeW5jIHNldENsaXBib2FyZCBjYWxsYmFjayBkb2Vzbid0IHNldFVybENvcGllZCAvXG4gIC8vIHNjaGVkdWxlIGEgbmV3IHRpbWVyIGFmdGVyIHVubW91bnQuXG4gIHVzZUVmZmVjdChcbiAgICAoKSA9PiAoKSA9PiB7XG4gICAgICB1bm1vdW50ZWRSZWYuY3VycmVudCA9IHRydWVcbiAgICAgIGF1dGhBYm9ydENvbnRyb2xsZXJSZWYuY3VycmVudD8uYWJvcnQoKVxuICAgICAgaWYgKGNvcHlUaW1lb3V0UmVmLmN1cnJlbnQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBjbGVhclRpbWVvdXQoY29weVRpbWVvdXRSZWYuY3VycmVudClcbiAgICAgIH1cbiAgICB9LFxuICAgIFtdLFxuICApXG5cbiAgLy8gQSBzZXJ2ZXIgaXMgZWZmZWN0aXZlbHkgYXV0aGVudGljYXRlZCBpZjpcbiAgLy8gMS4gSXQgaGFzIE9BdXRoIHRva2VucyAoc2VydmVyLmlzQXV0aGVudGljYXRlZCksIE9SXG4gIC8vIDIuIEl0J3MgY29ubmVjdGVkIGFuZCBoYXMgdG9vbHMgKG1lYW5pbmcgaXQncyB3b3JraW5nIHZpYSBzb21lIGF1dGggbWVjaGFuaXNtKVxuICBjb25zdCBpc0VmZmVjdGl2ZWx5QXV0aGVudGljYXRlZCA9XG4gICAgc2VydmVyLmlzQXV0aGVudGljYXRlZCB8fFxuICAgIChzZXJ2ZXIuY2xpZW50LnR5cGUgPT09ICdjb25uZWN0ZWQnICYmIHNlcnZlclRvb2xzQ291bnQgPiAwKVxuXG4gIGNvbnN0IHJlY29ubmVjdE1jcFNlcnZlciA9IHVzZU1jcFJlY29ubmVjdCgpXG5cbiAgY29uc3QgaGFuZGxlQ2xhdWRlQUlBdXRoQ29tcGxldGUgPSBSZWFjdC51c2VDYWxsYmFjayhhc3luYyAoKSA9PiB7XG4gICAgc2V0SXNDbGF1ZGVBSUF1dGhlbnRpY2F0aW5nKGZhbHNlKVxuICAgIHNldENsYXVkZUFJQXV0aFVybChudWxsKVxuICAgIHNldElzUmVjb25uZWN0aW5nKHRydWUpXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHJlY29ubmVjdE1jcFNlcnZlcihzZXJ2ZXIubmFtZSlcbiAgICAgIGNvbnN0IHN1Y2Nlc3MgPSByZXN1bHQuY2xpZW50LnR5cGUgPT09ICdjb25uZWN0ZWQnXG4gICAgICBsb2dFdmVudCgndGVuZ3VfY2xhdWRlYWlfbWNwX2F1dGhfY29tcGxldGVkJywgeyBzdWNjZXNzIH0pXG4gICAgICBpZiAoc3VjY2Vzcykge1xuICAgICAgICBvbkNvbXBsZXRlPy4oYEF1dGhlbnRpY2F0aW9uIHN1Y2Nlc3NmdWwuIENvbm5lY3RlZCB0byAke3NlcnZlci5uYW1lfS5gKVxuICAgICAgfSBlbHNlIGlmIChyZXN1bHQuY2xpZW50LnR5cGUgPT09ICduZWVkcy1hdXRoJykge1xuICAgICAgICBvbkNvbXBsZXRlPy4oXG4gICAgICAgICAgJ0F1dGhlbnRpY2F0aW9uIHN1Y2Nlc3NmdWwsIGJ1dCBzZXJ2ZXIgc3RpbGwgcmVxdWlyZXMgYXV0aGVudGljYXRpb24uIFlvdSBtYXkgbmVlZCB0byBtYW51YWxseSByZXN0YXJ0IENsYXVkZSBDb2RlLicsXG4gICAgICAgIClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG9uQ29tcGxldGU/LihcbiAgICAgICAgICAnQXV0aGVudGljYXRpb24gc3VjY2Vzc2Z1bCwgYnV0IHNlcnZlciByZWNvbm5lY3Rpb24gZmFpbGVkLiBZb3UgbWF5IG5lZWQgdG8gbWFudWFsbHkgcmVzdGFydCBDbGF1ZGUgQ29kZSBmb3IgdGhlIGNoYW5nZXMgdG8gdGFrZSBlZmZlY3QuJyxcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2NsYXVkZWFpX21jcF9hdXRoX2NvbXBsZXRlZCcsIHsgc3VjY2VzczogZmFsc2UgfSlcbiAgICAgIG9uQ29tcGxldGU/LihoYW5kbGVSZWNvbm5lY3RFcnJvcihlcnIsIHNlcnZlci5uYW1lKSlcbiAgICB9IGZpbmFsbHkge1xuICAgICAgc2V0SXNSZWNvbm5lY3RpbmcoZmFsc2UpXG4gICAgfVxuICB9LCBbcmVjb25uZWN0TWNwU2VydmVyLCBzZXJ2ZXIubmFtZSwgb25Db21wbGV0ZV0pXG5cbiAgY29uc3QgaGFuZGxlQ2xhdWRlQUlDbGVhckF1dGhDb21wbGV0ZSA9IFJlYWN0LnVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBhd2FpdCBjbGVhclNlcnZlckNhY2hlKHNlcnZlci5uYW1lLCB7XG4gICAgICAuLi5zZXJ2ZXIuY29uZmlnLFxuICAgICAgc2NvcGU6IHNlcnZlci5zY29wZSxcbiAgICB9KVxuXG4gICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICBjb25zdCBuZXdDbGllbnRzID0gcHJldi5tY3AuY2xpZW50cy5tYXAoYyA9PlxuICAgICAgICBjLm5hbWUgPT09IHNlcnZlci5uYW1lID8geyAuLi5jLCB0eXBlOiAnbmVlZHMtYXV0aCcgYXMgY29uc3QgfSA6IGMsXG4gICAgICApXG4gICAgICBjb25zdCBuZXdUb29scyA9IGV4Y2x1ZGVUb29sc0J5U2VydmVyKHByZXYubWNwLnRvb2xzLCBzZXJ2ZXIubmFtZSlcbiAgICAgIGNvbnN0IG5ld0NvbW1hbmRzID0gZXhjbHVkZUNvbW1hbmRzQnlTZXJ2ZXIoXG4gICAgICAgIHByZXYubWNwLmNvbW1hbmRzLFxuICAgICAgICBzZXJ2ZXIubmFtZSxcbiAgICAgIClcbiAgICAgIGNvbnN0IG5ld1Jlc291cmNlcyA9IGV4Y2x1ZGVSZXNvdXJjZXNCeVNlcnZlcihcbiAgICAgICAgcHJldi5tY3AucmVzb3VyY2VzLFxuICAgICAgICBzZXJ2ZXIubmFtZSxcbiAgICAgIClcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4ucHJldixcbiAgICAgICAgbWNwOiB7XG4gICAgICAgICAgLi4ucHJldi5tY3AsXG4gICAgICAgICAgY2xpZW50czogbmV3Q2xpZW50cyxcbiAgICAgICAgICB0b29sczogbmV3VG9vbHMsXG4gICAgICAgICAgY29tbWFuZHM6IG5ld0NvbW1hbmRzLFxuICAgICAgICAgIHJlc291cmNlczogbmV3UmVzb3VyY2VzLFxuICAgICAgICB9LFxuICAgICAgfVxuICAgIH0pXG5cbiAgICBsb2dFdmVudCgndGVuZ3VfY2xhdWRlYWlfbWNwX2NsZWFyX2F1dGhfY29tcGxldGVkJywge30pXG4gICAgb25Db21wbGV0ZT8uKGBEaXNjb25uZWN0ZWQgZnJvbSAke3NlcnZlci5uYW1lfS5gKVxuICAgIHNldElzQ2xhdWRlQUlDbGVhcmluZ0F1dGgoZmFsc2UpXG4gICAgc2V0Q2xhdWRlQUlDbGVhckF1dGhVcmwobnVsbClcbiAgICBzZXRDbGF1ZGVBSUNsZWFyQXV0aEJyb3dzZXJPcGVuZWQoZmFsc2UpXG4gIH0sIFtzZXJ2ZXIubmFtZSwgc2VydmVyLmNvbmZpZywgc2VydmVyLnNjb3BlLCBzZXRBcHBTdGF0ZSwgb25Db21wbGV0ZV0pXG5cbiAgLy8gRXNjYXBlIHRvIGNhbmNlbCBhdXRoZW50aWNhdGlvbiBmbG93XG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2NvbmZpcm06bm8nLFxuICAgICgpID0+IHtcbiAgICAgIGF1dGhBYm9ydENvbnRyb2xsZXJSZWYuY3VycmVudD8uYWJvcnQoKVxuICAgICAgYXV0aEFib3J0Q29udHJvbGxlclJlZi5jdXJyZW50ID0gbnVsbFxuICAgICAgc2V0SXNBdXRoZW50aWNhdGluZyhmYWxzZSlcbiAgICAgIHNldEF1dGhvcml6YXRpb25VcmwobnVsbClcbiAgICB9LFxuICAgIHtcbiAgICAgIGNvbnRleHQ6ICdDb25maXJtYXRpb24nLFxuICAgICAgaXNBY3RpdmU6IGlzQXV0aGVudGljYXRpbmcsXG4gICAgfSxcbiAgKVxuXG4gIC8vIEVzY2FwZSB0byBjYW5jZWwgQ2xhdWRlIEFJIGF1dGhlbnRpY2F0aW9uXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2NvbmZpcm06bm8nLFxuICAgICgpID0+IHtcbiAgICAgIHNldElzQ2xhdWRlQUlBdXRoZW50aWNhdGluZyhmYWxzZSlcbiAgICAgIHNldENsYXVkZUFJQXV0aFVybChudWxsKVxuICAgIH0sXG4gICAge1xuICAgICAgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsXG4gICAgICBpc0FjdGl2ZTogaXNDbGF1ZGVBSUF1dGhlbnRpY2F0aW5nLFxuICAgIH0sXG4gIClcblxuICAvLyBFc2NhcGUgdG8gY2FuY2VsIENsYXVkZSBBSSBjbGVhciBhdXRoXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2NvbmZpcm06bm8nLFxuICAgICgpID0+IHtcbiAgICAgIHNldElzQ2xhdWRlQUlDbGVhcmluZ0F1dGgoZmFsc2UpXG4gICAgICBzZXRDbGF1ZGVBSUNsZWFyQXV0aFVybChudWxsKVxuICAgICAgc2V0Q2xhdWRlQUlDbGVhckF1dGhCcm93c2VyT3BlbmVkKGZhbHNlKVxuICAgIH0sXG4gICAge1xuICAgICAgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsXG4gICAgICBpc0FjdGl2ZTogaXNDbGF1ZGVBSUNsZWFyaW5nQXV0aCxcbiAgICB9LFxuICApXG5cbiAgLy8gUmV0dXJuIGtleSBoYW5kbGluZyBmb3IgYXV0aGVudGljYXRpb24gZmxvd3MgYW5kICdjJyB0byBjb3B5IFVSTFxuICB1c2VJbnB1dCgoaW5wdXQsIGtleSkgPT4ge1xuICAgIGlmIChrZXkucmV0dXJuICYmIGlzQ2xhdWRlQUlBdXRoZW50aWNhdGluZykge1xuICAgICAgdm9pZCBoYW5kbGVDbGF1ZGVBSUF1dGhDb21wbGV0ZSgpXG4gICAgfVxuICAgIGlmIChrZXkucmV0dXJuICYmIGlzQ2xhdWRlQUlDbGVhcmluZ0F1dGgpIHtcbiAgICAgIGlmIChjbGF1ZGVBSUNsZWFyQXV0aEJyb3dzZXJPcGVuZWQpIHtcbiAgICAgICAgdm9pZCBoYW5kbGVDbGF1ZGVBSUNsZWFyQXV0aENvbXBsZXRlKClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEZpcnN0IEVudGVyOiBvcGVuIHRoZSBicm93c2VyXG4gICAgICAgIGNvbnN0IGNvbm5lY3RvcnNVcmwgPSBgJHtnZXRPYXV0aENvbmZpZygpLkNMQVVERV9BSV9PUklHSU59L3NldHRpbmdzL2Nvbm5lY3RvcnNgXG4gICAgICAgIHNldENsYXVkZUFJQ2xlYXJBdXRoVXJsKGNvbm5lY3RvcnNVcmwpXG4gICAgICAgIHNldENsYXVkZUFJQ2xlYXJBdXRoQnJvd3Nlck9wZW5lZCh0cnVlKVxuICAgICAgICB2b2lkIG9wZW5Ccm93c2VyKGNvbm5lY3RvcnNVcmwpXG4gICAgICB9XG4gICAgfVxuICAgIGlmIChpbnB1dCA9PT0gJ2MnICYmICF1cmxDb3BpZWQpIHtcbiAgICAgIGNvbnN0IHVybFRvQ29weSA9XG4gICAgICAgIGF1dGhvcml6YXRpb25VcmwgfHwgY2xhdWRlQUlBdXRoVXJsIHx8IGNsYXVkZUFJQ2xlYXJBdXRoVXJsXG4gICAgICBpZiAodXJsVG9Db3B5KSB7XG4gICAgICAgIHZvaWQgc2V0Q2xpcGJvYXJkKHVybFRvQ29weSkudGhlbihyYXcgPT4ge1xuICAgICAgICAgIGlmICh1bm1vdW50ZWRSZWYuY3VycmVudCkgcmV0dXJuXG4gICAgICAgICAgaWYgKHJhdykgcHJvY2Vzcy5zdGRvdXQud3JpdGUocmF3KVxuICAgICAgICAgIHNldFVybENvcGllZCh0cnVlKVxuICAgICAgICAgIGlmIChjb3B5VGltZW91dFJlZi5jdXJyZW50ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIGNsZWFyVGltZW91dChjb3B5VGltZW91dFJlZi5jdXJyZW50KVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb3B5VGltZW91dFJlZi5jdXJyZW50ID0gc2V0VGltZW91dChzZXRVcmxDb3BpZWQsIDIwMDAsIGZhbHNlKVxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH1cbiAgfSlcblxuICBjb25zdCBjYXBpdGFsaXplZFNlcnZlck5hbWUgPSBjYXBpdGFsaXplKFN0cmluZyhzZXJ2ZXIubmFtZSkpXG5cbiAgLy8gQ291bnQgTUNQIHByb21wdHMgZm9yIHRoaXMgc2VydmVyIChza2lsbHMgYXJlIHNob3duIGluIC9za2lsbHMsIG5vdCBoZXJlKVxuICBjb25zdCBzZXJ2ZXJDb21tYW5kc0NvdW50ID0gZmlsdGVyTWNwUHJvbXB0c0J5U2VydmVyKFxuICAgIG1jcC5jb21tYW5kcyxcbiAgICBzZXJ2ZXIubmFtZSxcbiAgKS5sZW5ndGhcblxuICBjb25zdCB0b2dnbGVNY3BTZXJ2ZXIgPSB1c2VNY3BUb2dnbGVFbmFibGVkKClcblxuICBjb25zdCBoYW5kbGVDbGF1ZGVBSUF1dGggPSBSZWFjdC51c2VDYWxsYmFjayhhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgY2xhdWRlQWlCYXNlVXJsID0gZ2V0T2F1dGhDb25maWcoKS5DTEFVREVfQUlfT1JJR0lOXG4gICAgY29uc3QgYWNjb3VudEluZm8gPSBnZXRPYXV0aEFjY291bnRJbmZvKClcbiAgICBjb25zdCBvcmdVdWlkID0gYWNjb3VudEluZm8/Lm9yZ2FuaXphdGlvblV1aWRcblxuICAgIGxldCBhdXRoVXJsOiBzdHJpbmdcbiAgICBpZiAoXG4gICAgICBvcmdVdWlkICYmXG4gICAgICBzZXJ2ZXIuY29uZmlnLnR5cGUgPT09ICdjbGF1ZGVhaS1wcm94eScgJiZcbiAgICAgIHNlcnZlci5jb25maWcuaWRcbiAgICApIHtcbiAgICAgIC8vIFVzZSB0aGUgZGlyZWN0IGF1dGggVVJMIHdpdGggb3JnIGFuZCBzZXJ2ZXIgSURzXG4gICAgICAvLyBSZXBsYWNlICdtY3BycycgcHJlZml4IHdpdGggJ21jcHNydicgaWYgcHJlc2VudFxuICAgICAgY29uc3Qgc2VydmVySWQgPSBzZXJ2ZXIuY29uZmlnLmlkLnN0YXJ0c1dpdGgoJ21jcHJzJylcbiAgICAgICAgPyAnbWNwc3J2JyArIHNlcnZlci5jb25maWcuaWQuc2xpY2UoNSlcbiAgICAgICAgOiBzZXJ2ZXIuY29uZmlnLmlkXG4gICAgICBjb25zdCBwcm9kdWN0U3VyZmFjZSA9IGVuY29kZVVSSUNvbXBvbmVudChcbiAgICAgICAgcHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfRU5UUllQT0lOVCB8fCAnY2xpJyxcbiAgICAgIClcbiAgICAgIGF1dGhVcmwgPSBgJHtjbGF1ZGVBaUJhc2VVcmx9L2FwaS9vcmdhbml6YXRpb25zLyR7b3JnVXVpZH0vbWNwL3N0YXJ0LWF1dGgvJHtzZXJ2ZXJJZH0/cHJvZHVjdF9zdXJmYWNlPSR7cHJvZHVjdFN1cmZhY2V9YFxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBGYWxsIGJhY2sgdG8gc2V0dGluZ3MvY29ubmVjdG9ycyBpZiB3ZSBkb24ndCBoYXZlIHRoZSByZXF1aXJlZCBJRHNcbiAgICAgIGF1dGhVcmwgPSBgJHtjbGF1ZGVBaUJhc2VVcmx9L3NldHRpbmdzL2Nvbm5lY3RvcnNgXG4gICAgfVxuXG4gICAgc2V0Q2xhdWRlQUlBdXRoVXJsKGF1dGhVcmwpXG4gICAgc2V0SXNDbGF1ZGVBSUF1dGhlbnRpY2F0aW5nKHRydWUpXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X2NsYXVkZWFpX21jcF9hdXRoX3N0YXJ0ZWQnLCB7fSlcbiAgICBhd2FpdCBvcGVuQnJvd3NlcihhdXRoVXJsKVxuICB9LCBbc2VydmVyLmNvbmZpZ10pXG5cbiAgY29uc3QgaGFuZGxlQ2xhdWRlQUlDbGVhckF1dGggPSBSZWFjdC51c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgc2V0SXNDbGF1ZGVBSUNsZWFyaW5nQXV0aCh0cnVlKVxuICAgIGxvZ0V2ZW50KCd0ZW5ndV9jbGF1ZGVhaV9tY3BfY2xlYXJfYXV0aF9zdGFydGVkJywge30pXG4gIH0sIFtdKVxuXG4gIGNvbnN0IGhhbmRsZVRvZ2dsZUVuYWJsZWQgPSBSZWFjdC51c2VDYWxsYmFjayhhc3luYyAoKSA9PiB7XG4gICAgY29uc3Qgd2FzRW5hYmxlZCA9IHNlcnZlci5jbGllbnQudHlwZSAhPT0gJ2Rpc2FibGVkJ1xuXG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IHRvZ2dsZU1jcFNlcnZlcihzZXJ2ZXIubmFtZSlcblxuICAgICAgaWYgKHNlcnZlci5jb25maWcudHlwZSA9PT0gJ2NsYXVkZWFpLXByb3h5Jykge1xuICAgICAgICBsb2dFdmVudCgndGVuZ3VfY2xhdWRlYWlfbWNwX3RvZ2dsZScsIHtcbiAgICAgICAgICBuZXdfc3RhdGU6ICh3YXNFbmFibGVkXG4gICAgICAgICAgICA/ICdkaXNhYmxlZCdcbiAgICAgICAgICAgIDogJ2VuYWJsZWQnKSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICAvLyBSZXR1cm4gdG8gdGhlIHNlcnZlciBsaXN0IHNvIHVzZXIgY2FuIGNvbnRpbnVlIG1hbmFnaW5nIG90aGVyIHNlcnZlcnNcbiAgICAgIG9uQ2FuY2VsKClcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGNvbnN0IGFjdGlvbiA9IHdhc0VuYWJsZWQgPyAnZGlzYWJsZScgOiAnZW5hYmxlJ1xuICAgICAgb25Db21wbGV0ZT8uKFxuICAgICAgICBgRmFpbGVkIHRvICR7YWN0aW9ufSBNQ1Agc2VydmVyICcke3NlcnZlci5uYW1lfSc6ICR7ZXJyb3JNZXNzYWdlKGVycil9YCxcbiAgICAgIClcbiAgICB9XG4gIH0sIFtcbiAgICBzZXJ2ZXIuY2xpZW50LnR5cGUsXG4gICAgc2VydmVyLmNvbmZpZy50eXBlLFxuICAgIHNlcnZlci5uYW1lLFxuICAgIHRvZ2dsZU1jcFNlcnZlcixcbiAgICBvbkNhbmNlbCxcbiAgICBvbkNvbXBsZXRlLFxuICBdKVxuXG4gIGNvbnN0IGhhbmRsZUF1dGhlbnRpY2F0ZSA9IFJlYWN0LnVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBpZiAoc2VydmVyLmNvbmZpZy50eXBlID09PSAnY2xhdWRlYWktcHJveHknKSByZXR1cm5cblxuICAgIHNldElzQXV0aGVudGljYXRpbmcodHJ1ZSlcbiAgICBzZXRFcnJvcihudWxsKVxuXG4gICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKVxuICAgIGF1dGhBYm9ydENvbnRyb2xsZXJSZWYuY3VycmVudCA9IGNvbnRyb2xsZXJcblxuICAgIHRyeSB7XG4gICAgICAvLyBSZXZva2UgZXhpc3RpbmcgdG9rZW5zIGlmIHJlLWF1dGhlbnRpY2F0aW5nLCBidXQgcHJlc2VydmUgc3RlcC11cFxuICAgICAgLy8gYXV0aCBzdGF0ZSBzbyB0aGUgbmV4dCBPQXV0aCBmbG93IGNhbiByZXVzZSBjYWNoZWQgc2NvcGUvZGlzY292ZXJ5LlxuICAgICAgaWYgKHNlcnZlci5pc0F1dGhlbnRpY2F0ZWQgJiYgc2VydmVyLmNvbmZpZykge1xuICAgICAgICBhd2FpdCByZXZva2VTZXJ2ZXJUb2tlbnMoc2VydmVyLm5hbWUsIHNlcnZlci5jb25maWcsIHtcbiAgICAgICAgICBwcmVzZXJ2ZVN0ZXBVcFN0YXRlOiB0cnVlLFxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICBpZiAoc2VydmVyLmNvbmZpZykge1xuICAgICAgICBhd2FpdCBwZXJmb3JtTUNQT0F1dGhGbG93KFxuICAgICAgICAgIHNlcnZlci5uYW1lLFxuICAgICAgICAgIHNlcnZlci5jb25maWcsXG4gICAgICAgICAgc2V0QXV0aG9yaXphdGlvblVybCxcbiAgICAgICAgICBjb250cm9sbGVyLnNpZ25hbCxcbiAgICAgICAgICB7XG4gICAgICAgICAgICBvbldhaXRpbmdGb3JDYWxsYmFjazogc3VibWl0ID0+IHtcbiAgICAgICAgICAgICAgc2V0TWFudWFsQ2FsbGJhY2tTdWJtaXQoKCkgPT4gc3VibWl0KVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICApXG5cbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X21jcF9hdXRoX2NvbmZpZ19hdXRoZW50aWNhdGUnLCB7XG4gICAgICAgICAgd2FzQXV0aGVudGljYXRlZDogc2VydmVyLmlzQXV0aGVudGljYXRlZCxcbiAgICAgICAgfSlcblxuICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCByZWNvbm5lY3RNY3BTZXJ2ZXIoc2VydmVyLm5hbWUpXG5cbiAgICAgICAgaWYgKHJlc3VsdC5jbGllbnQudHlwZSA9PT0gJ2Nvbm5lY3RlZCcpIHtcbiAgICAgICAgICBjb25zdCBtZXNzYWdlID0gaXNFZmZlY3RpdmVseUF1dGhlbnRpY2F0ZWRcbiAgICAgICAgICAgID8gYEF1dGhlbnRpY2F0aW9uIHN1Y2Nlc3NmdWwuIFJlY29ubmVjdGVkIHRvICR7c2VydmVyLm5hbWV9LmBcbiAgICAgICAgICAgIDogYEF1dGhlbnRpY2F0aW9uIHN1Y2Nlc3NmdWwuIENvbm5lY3RlZCB0byAke3NlcnZlci5uYW1lfS5gXG4gICAgICAgICAgb25Db21wbGV0ZT8uKG1lc3NhZ2UpXG4gICAgICAgIH0gZWxzZSBpZiAocmVzdWx0LmNsaWVudC50eXBlID09PSAnbmVlZHMtYXV0aCcpIHtcbiAgICAgICAgICBvbkNvbXBsZXRlPy4oXG4gICAgICAgICAgICAnQXV0aGVudGljYXRpb24gc3VjY2Vzc2Z1bCwgYnV0IHNlcnZlciBzdGlsbCByZXF1aXJlcyBhdXRoZW50aWNhdGlvbi4gWW91IG1heSBuZWVkIHRvIG1hbnVhbGx5IHJlc3RhcnQgQ2xhdWRlIENvZGUuJyxcbiAgICAgICAgICApXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gcmVzdWx0LmNsaWVudC50eXBlID09PSAnZmFpbGVkJ1xuICAgICAgICAgIGxvZ01DUERlYnVnKHNlcnZlci5uYW1lLCBgUmVjb25uZWN0aW9uIGZhaWxlZCBhZnRlciBhdXRoZW50aWNhdGlvbmApXG4gICAgICAgICAgb25Db21wbGV0ZT8uKFxuICAgICAgICAgICAgJ0F1dGhlbnRpY2F0aW9uIHN1Y2Nlc3NmdWwsIGJ1dCBzZXJ2ZXIgcmVjb25uZWN0aW9uIGZhaWxlZC4gWW91IG1heSBuZWVkIHRvIG1hbnVhbGx5IHJlc3RhcnQgQ2xhdWRlIENvZGUgZm9yIHRoZSBjaGFuZ2VzIHRvIHRha2UgZWZmZWN0LicsXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAvLyBEb24ndCBzaG93IGVycm9yIGlmIGl0IHdhcyBhIGNhbmNlbGxhdGlvblxuICAgICAgaWYgKFxuICAgICAgICBlcnIgaW5zdGFuY2VvZiBFcnJvciAmJlxuICAgICAgICAhKGVyciBpbnN0YW5jZW9mIEF1dGhlbnRpY2F0aW9uQ2FuY2VsbGVkRXJyb3IpXG4gICAgICApIHtcbiAgICAgICAgc2V0RXJyb3IoZXJyLm1lc3NhZ2UpXG4gICAgICB9XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIHNldElzQXV0aGVudGljYXRpbmcoZmFsc2UpXG4gICAgICBhdXRoQWJvcnRDb250cm9sbGVyUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICBzZXRNYW51YWxDYWxsYmFja1N1Ym1pdChudWxsKVxuICAgICAgc2V0Q2FsbGJhY2tVcmxJbnB1dCgnJylcbiAgICB9XG4gIH0sIFtcbiAgICBzZXJ2ZXIuaXNBdXRoZW50aWNhdGVkLFxuICAgIHNlcnZlci5jb25maWcsXG4gICAgc2VydmVyLm5hbWUsXG4gICAgb25Db21wbGV0ZSxcbiAgICByZWNvbm5lY3RNY3BTZXJ2ZXIsXG4gICAgaXNFZmZlY3RpdmVseUF1dGhlbnRpY2F0ZWQsXG4gIF0pXG5cbiAgY29uc3QgaGFuZGxlQ2xlYXJBdXRoID0gYXN5bmMgKCkgPT4ge1xuICAgIGlmIChzZXJ2ZXIuY29uZmlnLnR5cGUgPT09ICdjbGF1ZGVhaS1wcm94eScpIHJldHVyblxuXG4gICAgaWYgKHNlcnZlci5jb25maWcpIHtcbiAgICAgIC8vIEZpcnN0IHJldm9rZSB0aGUgYXV0aGVudGljYXRpb24gdG9rZW5zIGFuZCBjbGVhciBhbGwgYXV0aCBzdGF0ZVxuICAgICAgYXdhaXQgcmV2b2tlU2VydmVyVG9rZW5zKHNlcnZlci5uYW1lLCBzZXJ2ZXIuY29uZmlnKVxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X21jcF9hdXRoX2NvbmZpZ19jbGVhcicsIHt9KVxuXG4gICAgICAvLyBEaXNjb25uZWN0IHRoZSBjbGllbnQgYW5kIGNsZWFyIHRoZSBjYWNoZVxuICAgICAgYXdhaXQgY2xlYXJTZXJ2ZXJDYWNoZShzZXJ2ZXIubmFtZSwge1xuICAgICAgICAuLi5zZXJ2ZXIuY29uZmlnLFxuICAgICAgICBzY29wZTogc2VydmVyLnNjb3BlLFxuICAgICAgfSlcblxuICAgICAgLy8gVXBkYXRlIGFwcCBzdGF0ZSB0byByZW1vdmUgdGhlIGRpc2Nvbm5lY3RlZCBzZXJ2ZXIncyB0b29scywgY29tbWFuZHMsIGFuZCByZXNvdXJjZXNcbiAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICBjb25zdCBuZXdDbGllbnRzID0gcHJldi5tY3AuY2xpZW50cy5tYXAoYyA9PlxuICAgICAgICAgIC8vICdmYWlsZWQnIGlzIGEgbWlzbm9tZXIgaGVyZSwgYnV0IHdlIGRvbid0IHJlYWxseSBkaWZmZXJlbnRpYXRlIGJldHdlZW4gXCJub3QgY29ubmVjdGVkXCIgYW5kIFwiZmFpbGVkXCIgYXQgdGhlIG1vbWVudFxuICAgICAgICAgIGMubmFtZSA9PT0gc2VydmVyLm5hbWUgPyB7IC4uLmMsIHR5cGU6ICdmYWlsZWQnIGFzIGNvbnN0IH0gOiBjLFxuICAgICAgICApXG4gICAgICAgIGNvbnN0IG5ld1Rvb2xzID0gZXhjbHVkZVRvb2xzQnlTZXJ2ZXIocHJldi5tY3AudG9vbHMsIHNlcnZlci5uYW1lKVxuICAgICAgICBjb25zdCBuZXdDb21tYW5kcyA9IGV4Y2x1ZGVDb21tYW5kc0J5U2VydmVyKFxuICAgICAgICAgIHByZXYubWNwLmNvbW1hbmRzLFxuICAgICAgICAgIHNlcnZlci5uYW1lLFxuICAgICAgICApXG4gICAgICAgIGNvbnN0IG5ld1Jlc291cmNlcyA9IGV4Y2x1ZGVSZXNvdXJjZXNCeVNlcnZlcihcbiAgICAgICAgICBwcmV2Lm1jcC5yZXNvdXJjZXMsXG4gICAgICAgICAgc2VydmVyLm5hbWUsXG4gICAgICAgIClcblxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgbWNwOiB7XG4gICAgICAgICAgICAuLi5wcmV2Lm1jcCxcbiAgICAgICAgICAgIGNsaWVudHM6IG5ld0NsaWVudHMsXG4gICAgICAgICAgICB0b29sczogbmV3VG9vbHMsXG4gICAgICAgICAgICBjb21tYW5kczogbmV3Q29tbWFuZHMsXG4gICAgICAgICAgICByZXNvdXJjZXM6IG5ld1Jlc291cmNlcyxcbiAgICAgICAgICB9LFxuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICBvbkNvbXBsZXRlPy4oYEF1dGhlbnRpY2F0aW9uIGNsZWFyZWQgZm9yICR7c2VydmVyLm5hbWV9LmApXG4gICAgfVxuICB9XG5cbiAgaWYgKGlzQXV0aGVudGljYXRpbmcpIHtcbiAgICAvLyBYQUE6IHNpbGVudCBleGNoYW5nZSAoY2FjaGVkIGlkX3Rva2VuIOKGkiBubyBicm93c2VyKSwgc28gZG9uJ3QgY2xhaW1cbiAgICAvLyBvbmUgd2lsbCBvcGVuLiBJZiBJZFAgbG9naW4gSVMgbmVlZGVkLCBhdXRob3JpemF0aW9uVXJsIHBvcHVsYXRlcyBhbmRcbiAgICAvLyB0aGUgVVJMIGZhbGxiYWNrIGJsb2NrIGJlbG93IHN0aWxsIHJlbmRlcnMuXG4gICAgY29uc3QgYXV0aENvcHkgPVxuICAgICAgc2VydmVyLmNvbmZpZy50eXBlICE9PSAnY2xhdWRlYWktcHJveHknICYmIHNlcnZlci5jb25maWcub2F1dGg/LnhhYVxuICAgICAgICA/ICcgQXV0aGVudGljYXRpbmcgdmlhIHlvdXIgaWRlbnRpdHkgcHJvdmlkZXInXG4gICAgICAgIDogJyBBIGJyb3dzZXIgd2luZG93IHdpbGwgb3BlbiBmb3IgYXV0aGVudGljYXRpb24nXG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0gcGFkZGluZz17MX0+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+QXV0aGVudGljYXRpbmcgd2l0aCB7c2VydmVyLm5hbWV94oCmPC9UZXh0PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxTcGlubmVyIC8+XG4gICAgICAgICAgPFRleHQ+e2F1dGhDb3B5fTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHthdXRob3JpemF0aW9uVXJsICYmIChcbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIDxCb3g+XG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgIElmIHlvdXIgYnJvd3NlciBkb2VzbiZhcG9zO3Qgb3BlbiBhdXRvbWF0aWNhbGx5LCBjb3B5IHRoaXMgVVJMXG4gICAgICAgICAgICAgICAgbWFudWFsbHl7JyAnfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIHt1cmxDb3BpZWQgPyAoXG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+KENvcGllZCEpPC9UZXh0PlxuICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiY1wiIGFjdGlvbj1cImNvcHlcIiBwYXJlbnMgLz5cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICl9XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgIDxMaW5rIHVybD17YXV0aG9yaXphdGlvblVybH0gLz5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgICAge2lzQXV0aGVudGljYXRpbmcgJiYgYXV0aG9yaXphdGlvblVybCAmJiBtYW51YWxDYWxsYmFja1N1Ym1pdCAmJiAoXG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICBJZiB0aGUgcmVkaXJlY3QgcGFnZSBzaG93cyBhIGNvbm5lY3Rpb24gZXJyb3IsIHBhc3RlIHRoZSBVUkwgZnJvbVxuICAgICAgICAgICAgICB5b3VyIGJyb3dzZXImYXBvcztzIGFkZHJlc3MgYmFyOlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPEJveD5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+VVJMIHsnPid9IDwvVGV4dD5cbiAgICAgICAgICAgICAgPFRleHRJbnB1dFxuICAgICAgICAgICAgICAgIHZhbHVlPXtjYWxsYmFja1VybElucHV0fVxuICAgICAgICAgICAgICAgIG9uQ2hhbmdlPXtzZXRDYWxsYmFja1VybElucHV0fVxuICAgICAgICAgICAgICAgIG9uU3VibWl0PXsodmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgICAgICAgICAgICAgbWFudWFsQ2FsbGJhY2tTdWJtaXQodmFsdWUudHJpbSgpKVxuICAgICAgICAgICAgICAgICAgc2V0Q2FsbGJhY2tVcmxJbnB1dCgnJylcbiAgICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgICAgIGN1cnNvck9mZnNldD17Y2FsbGJhY2tVcmxDdXJzb3JPZmZzZXR9XG4gICAgICAgICAgICAgICAgb25DaGFuZ2VDdXJzb3JPZmZzZXQ9e3NldENhbGxiYWNrVXJsQ3Vyc29yT2Zmc2V0fVxuICAgICAgICAgICAgICAgIGNvbHVtbnM9e3Rlcm1pbmFsQ29sdW1ucyAtIDh9XG4gICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgICAgPEJveCBtYXJnaW5MZWZ0PXszfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIFJldHVybiBoZXJlIGFmdGVyIGF1dGhlbnRpY2F0aW5nIGluIHlvdXIgYnJvd3Nlci4gUHJlc3MgRXNjIHRvIGdvXG4gICAgICAgICAgICBiYWNrLlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBpZiAoaXNDbGF1ZGVBSUF1dGhlbnRpY2F0aW5nKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0gcGFkZGluZz17MX0+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiY2xhdWRlXCI+QXV0aGVudGljYXRpbmcgd2l0aCB7c2VydmVyLm5hbWV94oCmPC9UZXh0PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxTcGlubmVyIC8+XG4gICAgICAgICAgPFRleHQ+IEEgYnJvd3NlciB3aW5kb3cgd2lsbCBvcGVuIGZvciBhdXRoZW50aWNhdGlvbjwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHtjbGF1ZGVBSUF1dGhVcmwgJiYgKFxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPEJveD5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgSWYgeW91ciBicm93c2VyIGRvZXNuJmFwb3M7dCBvcGVuIGF1dG9tYXRpY2FsbHksIGNvcHkgdGhpcyBVUkxcbiAgICAgICAgICAgICAgICBtYW51YWxseXsnICd9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAge3VybENvcGllZCA/IChcbiAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj4oQ29waWVkISk8L1RleHQ+XG4gICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJjXCIgYWN0aW9uPVwiY29weVwiIHBhcmVucyAvPlxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgPExpbmsgdXJsPXtjbGF1ZGVBSUF1dGhVcmx9IC8+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICAgIDxCb3ggbWFyZ2luTGVmdD17M30gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwicGVybWlzc2lvblwiPlxuICAgICAgICAgICAgUHJlc3MgPFRleHQgYm9sZD5FbnRlcjwvVGV4dD4gYWZ0ZXIgYXV0aGVudGljYXRpbmcgaW4geW91ciBicm93c2VyLlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgICBkZXNjcmlwdGlvbj1cImJhY2tcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgaWYgKGlzQ2xhdWRlQUlDbGVhcmluZ0F1dGgpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfSBwYWRkaW5nPXsxfT5cbiAgICAgICAgPFRleHQgY29sb3I9XCJjbGF1ZGVcIj5DbGVhciBhdXRoZW50aWNhdGlvbiBmb3Ige3NlcnZlci5uYW1lfTwvVGV4dD5cbiAgICAgICAge2NsYXVkZUFJQ2xlYXJBdXRoQnJvd3Nlck9wZW5lZCA/IChcbiAgICAgICAgICA8PlxuICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgIEZpbmQgdGhlIE1DUCBzZXJ2ZXIgaW4gdGhlIGJyb3dzZXIgYW5kIGNsaWNrXG4gICAgICAgICAgICAgICZxdW90O0Rpc2Nvbm5lY3QmcXVvdDsuXG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICB7Y2xhdWRlQUlDbGVhckF1dGhVcmwgJiYgKFxuICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICAgIElmIHlvdXIgYnJvd3NlciBkaWRuJmFwb3M7dCBvcGVuIGF1dG9tYXRpY2FsbHksIGNvcHkgdGhpc1xuICAgICAgICAgICAgICAgICAgICBVUkwgbWFudWFsbHl7JyAnfVxuICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAge3VybENvcGllZCA/IChcbiAgICAgICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+KENvcGllZCEpPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiY1wiIGFjdGlvbj1cImNvcHlcIiBwYXJlbnMgLz5cbiAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgICA8TGluayB1cmw9e2NsYXVkZUFJQ2xlYXJBdXRoVXJsfSAvPlxuICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgICA8Qm94IG1hcmdpbkxlZnQ9ezN9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJwZXJtaXNzaW9uXCI+XG4gICAgICAgICAgICAgICAgUHJlc3MgPFRleHQgYm9sZD5FbnRlcjwvVGV4dD4gd2hlbiBkb25lLlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGl0YWxpYz5cbiAgICAgICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgICAgIGNvbnRleHQ9XCJDb25maXJtYXRpb25cIlxuICAgICAgICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJiYWNrXCJcbiAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8Lz5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8PlxuICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgIFRoaXMgd2lsbCBvcGVuIGNsYXVkZS5haSBpbiB0aGUgYnJvd3Nlci4gRmluZCB0aGUgTUNQIHNlcnZlciBpblxuICAgICAgICAgICAgICB0aGUgbGlzdCBhbmQgY2xpY2sgJnF1b3Q7RGlzY29ubmVjdCZxdW90Oy5cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxCb3ggbWFyZ2luTGVmdD17M30gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cInBlcm1pc3Npb25cIj5cbiAgICAgICAgICAgICAgICBQcmVzcyA8VGV4dCBib2xkPkVudGVyPC9UZXh0PiB0byBvcGVuIHRoZSBicm93c2VyLlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIGl0YWxpYz5cbiAgICAgICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgICAgIGNvbnRleHQ9XCJDb25maXJtYXRpb25cIlxuICAgICAgICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJiYWNrXCJcbiAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8Lz5cbiAgICAgICAgKX1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGlmIChpc1JlY29ubmVjdGluZykge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9IHBhZGRpbmc9ezF9PlxuICAgICAgICA8VGV4dCBjb2xvcj1cInRleHRcIj5cbiAgICAgICAgICBDb25uZWN0aW5nIHRvIDxUZXh0IGJvbGQ+e3NlcnZlci5uYW1lfTwvVGV4dD7igKZcbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxTcGlubmVyIC8+XG4gICAgICAgICAgPFRleHQ+IEVzdGFibGlzaGluZyBjb25uZWN0aW9uIHRvIE1DUCBzZXJ2ZXI8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5UaGlzIG1heSB0YWtlIGEgZmV3IG1vbWVudHMuPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgY29uc3QgbWVudU9wdGlvbnMgPSBbXVxuXG4gIC8vIElmIHNlcnZlciBpcyBkaXNhYmxlZCwgc2hvdyBFbmFibGUgZmlyc3QgYXMgdGhlIHByaW1hcnkgYWN0aW9uXG4gIGlmIChzZXJ2ZXIuY2xpZW50LnR5cGUgPT09ICdkaXNhYmxlZCcpIHtcbiAgICBtZW51T3B0aW9ucy5wdXNoKHtcbiAgICAgIGxhYmVsOiAnRW5hYmxlJyxcbiAgICAgIHZhbHVlOiAndG9nZ2xlLWVuYWJsZWQnLFxuICAgIH0pXG4gIH1cblxuICBpZiAoc2VydmVyLmNsaWVudC50eXBlID09PSAnY29ubmVjdGVkJyAmJiBzZXJ2ZXJUb29sc0NvdW50ID4gMCkge1xuICAgIG1lbnVPcHRpb25zLnB1c2goe1xuICAgICAgbGFiZWw6ICdWaWV3IHRvb2xzJyxcbiAgICAgIHZhbHVlOiAndG9vbHMnLFxuICAgIH0pXG4gIH1cblxuICBpZiAoc2VydmVyLmNvbmZpZy50eXBlID09PSAnY2xhdWRlYWktcHJveHknKSB7XG4gICAgaWYgKHNlcnZlci5jbGllbnQudHlwZSA9PT0gJ2Nvbm5lY3RlZCcpIHtcbiAgICAgIG1lbnVPcHRpb25zLnB1c2goe1xuICAgICAgICBsYWJlbDogJ0NsZWFyIGF1dGhlbnRpY2F0aW9uJyxcbiAgICAgICAgdmFsdWU6ICdjbGF1ZGVhaS1jbGVhci1hdXRoJyxcbiAgICAgIH0pXG4gICAgfSBlbHNlIGlmIChzZXJ2ZXIuY2xpZW50LnR5cGUgIT09ICdkaXNhYmxlZCcpIHtcbiAgICAgIG1lbnVPcHRpb25zLnB1c2goe1xuICAgICAgICBsYWJlbDogJ0F1dGhlbnRpY2F0ZScsXG4gICAgICAgIHZhbHVlOiAnY2xhdWRlYWktYXV0aCcsXG4gICAgICB9KVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICBpZiAoaXNFZmZlY3RpdmVseUF1dGhlbnRpY2F0ZWQpIHtcbiAgICAgIG1lbnVPcHRpb25zLnB1c2goe1xuICAgICAgICBsYWJlbDogJ1JlLWF1dGhlbnRpY2F0ZScsXG4gICAgICAgIHZhbHVlOiAncmVhdXRoJyxcbiAgICAgIH0pXG4gICAgICBtZW51T3B0aW9ucy5wdXNoKHtcbiAgICAgICAgbGFiZWw6ICdDbGVhciBhdXRoZW50aWNhdGlvbicsXG4gICAgICAgIHZhbHVlOiAnY2xlYXItYXV0aCcsXG4gICAgICB9KVxuICAgIH1cblxuICAgIGlmICghaXNFZmZlY3RpdmVseUF1dGhlbnRpY2F0ZWQpIHtcbiAgICAgIG1lbnVPcHRpb25zLnB1c2goe1xuICAgICAgICBsYWJlbDogJ0F1dGhlbnRpY2F0ZScsXG4gICAgICAgIHZhbHVlOiAnYXV0aCcsXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIGlmIChzZXJ2ZXIuY2xpZW50LnR5cGUgIT09ICdkaXNhYmxlZCcpIHtcbiAgICBpZiAoc2VydmVyLmNsaWVudC50eXBlICE9PSAnbmVlZHMtYXV0aCcpIHtcbiAgICAgIG1lbnVPcHRpb25zLnB1c2goe1xuICAgICAgICBsYWJlbDogJ1JlY29ubmVjdCcsXG4gICAgICAgIHZhbHVlOiAncmVjb25uZWN0TWNwU2VydmVyJyxcbiAgICAgIH0pXG4gICAgfVxuICAgIG1lbnVPcHRpb25zLnB1c2goe1xuICAgICAgbGFiZWw6ICdEaXNhYmxlJyxcbiAgICAgIHZhbHVlOiAndG9nZ2xlLWVuYWJsZWQnLFxuICAgIH0pXG4gIH1cblxuICAvLyBJZiB0aGVyZSBhcmUgbm8gb3RoZXIgb3B0aW9ucywgYWRkIGEgYmFjayBvcHRpb24gc28gU2VsZWN0IGhhbmRsZXMgZXNjYXBlXG4gIGlmIChtZW51T3B0aW9ucy5sZW5ndGggPT09IDApIHtcbiAgICBtZW51T3B0aW9ucy5wdXNoKHtcbiAgICAgIGxhYmVsOiAnQmFjaycsXG4gICAgICB2YWx1ZTogJ2JhY2snLFxuICAgIH0pXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPEJveFxuICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgcGFkZGluZ1g9ezF9XG4gICAgICAgIGJvcmRlclN0eWxlPXtib3JkZXJsZXNzID8gdW5kZWZpbmVkIDogJ3JvdW5kJ31cbiAgICAgID5cbiAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgIDxUZXh0IGJvbGQ+e2NhcGl0YWxpemVkU2VydmVyTmFtZX0gTUNQIFNlcnZlcjwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXswfT5cbiAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgPFRleHQgYm9sZD5TdGF0dXM6IDwvVGV4dD5cbiAgICAgICAgICAgIHtzZXJ2ZXIuY2xpZW50LnR5cGUgPT09ICdkaXNhYmxlZCcgPyAoXG4gICAgICAgICAgICAgIDxUZXh0Pntjb2xvcignaW5hY3RpdmUnLCB0aGVtZSkoZmlndXJlcy5yYWRpb09mZil9IGRpc2FibGVkPC9UZXh0PlxuICAgICAgICAgICAgKSA6IHNlcnZlci5jbGllbnQudHlwZSA9PT0gJ2Nvbm5lY3RlZCcgPyAoXG4gICAgICAgICAgICAgIDxUZXh0Pntjb2xvcignc3VjY2VzcycsIHRoZW1lKShmaWd1cmVzLnRpY2spfSBjb25uZWN0ZWQ8L1RleHQ+XG4gICAgICAgICAgICApIDogc2VydmVyLmNsaWVudC50eXBlID09PSAncGVuZGluZycgPyAoXG4gICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+e2ZpZ3VyZXMucmFkaW9PZmZ9PC9UZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0PiBjb25uZWN0aW5n4oCmPC9UZXh0PlxuICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICkgOiBzZXJ2ZXIuY2xpZW50LnR5cGUgPT09ICduZWVkcy1hdXRoJyA/IChcbiAgICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgICAge2NvbG9yKCd3YXJuaW5nJywgdGhlbWUpKGZpZ3VyZXMudHJpYW5nbGVVcE91dGxpbmUpfSBuZWVkc1xuICAgICAgICAgICAgICAgIGF1dGhlbnRpY2F0aW9uXG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgIDxUZXh0Pntjb2xvcignZXJyb3InLCB0aGVtZSkoZmlndXJlcy5jcm9zcyl9IGZhaWxlZDwvVGV4dD5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgICB7c2VydmVyLnRyYW5zcG9ydCAhPT0gJ2NsYXVkZWFpLXByb3h5JyAmJiAoXG4gICAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgICA8VGV4dCBib2xkPkF1dGg6IDwvVGV4dD5cbiAgICAgICAgICAgICAge2lzRWZmZWN0aXZlbHlBdXRoZW50aWNhdGVkID8gKFxuICAgICAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAgICAge2NvbG9yKCdzdWNjZXNzJywgdGhlbWUpKGZpZ3VyZXMudGljayl9IGF1dGhlbnRpY2F0ZWRcbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgICAgICB7Y29sb3IoJ2Vycm9yJywgdGhlbWUpKGZpZ3VyZXMuY3Jvc3MpfSBub3QgYXV0aGVudGljYXRlZFxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICl9XG5cbiAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgPFRleHQgYm9sZD5VUkw6IDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntzZXJ2ZXIuY29uZmlnLnVybH08L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgICA8Qm94PlxuICAgICAgICAgICAgPFRleHQgYm9sZD5Db25maWcgbG9jYXRpb246IDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntkZXNjcmliZU1jcENvbmZpZ0ZpbGVQYXRoKHNlcnZlci5zY29wZSl9PC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuXG4gICAgICAgICAge3NlcnZlci5jbGllbnQudHlwZSA9PT0gJ2Nvbm5lY3RlZCcgJiYgKFxuICAgICAgICAgICAgPENhcGFiaWxpdGllc1NlY3Rpb25cbiAgICAgICAgICAgICAgc2VydmVyVG9vbHNDb3VudD17c2VydmVyVG9vbHNDb3VudH1cbiAgICAgICAgICAgICAgc2VydmVyUHJvbXB0c0NvdW50PXtzZXJ2ZXJDb21tYW5kc0NvdW50fVxuICAgICAgICAgICAgICBzZXJ2ZXJSZXNvdXJjZXNDb3VudD17bWNwLnJlc291cmNlc1tzZXJ2ZXIubmFtZV0/Lmxlbmd0aCB8fCAwfVxuICAgICAgICAgICAgLz5cbiAgICAgICAgICApfVxuXG4gICAgICAgICAge3NlcnZlci5jbGllbnQudHlwZSA9PT0gJ2Nvbm5lY3RlZCcgJiYgc2VydmVyVG9vbHNDb3VudCA+IDAgJiYgKFxuICAgICAgICAgICAgPEJveD5cbiAgICAgICAgICAgICAgPFRleHQgYm9sZD5Ub29sczogPC9UZXh0PlxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57c2VydmVyVG9vbHNDb3VudH0gdG9vbHM8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cblxuICAgICAgICB7ZXJyb3IgJiYgKFxuICAgICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5FcnJvcjoge2Vycm9yfTwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cblxuICAgICAgICB7bWVudU9wdGlvbnMubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgICBvcHRpb25zPXttZW51T3B0aW9uc31cbiAgICAgICAgICAgICAgb25DaGFuZ2U9e2FzeW5jIHZhbHVlID0+IHtcbiAgICAgICAgICAgICAgICBzd2l0Y2ggKHZhbHVlKSB7XG4gICAgICAgICAgICAgICAgICBjYXNlICd0b29scyc6XG4gICAgICAgICAgICAgICAgICAgIG9uVmlld1Rvb2xzKClcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICAgIGNhc2UgJ2F1dGgnOlxuICAgICAgICAgICAgICAgICAgY2FzZSAncmVhdXRoJzpcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgaGFuZGxlQXV0aGVudGljYXRlKClcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICAgIGNhc2UgJ2NsZWFyLWF1dGgnOlxuICAgICAgICAgICAgICAgICAgICBhd2FpdCBoYW5kbGVDbGVhckF1dGgoKVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgICAgY2FzZSAnY2xhdWRlYWktYXV0aCc6XG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IGhhbmRsZUNsYXVkZUFJQXV0aCgpXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgICBjYXNlICdjbGF1ZGVhaS1jbGVhci1hdXRoJzpcbiAgICAgICAgICAgICAgICAgICAgaGFuZGxlQ2xhdWRlQUlDbGVhckF1dGgoKVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgICAgY2FzZSAncmVjb25uZWN0TWNwU2VydmVyJzpcbiAgICAgICAgICAgICAgICAgICAgc2V0SXNSZWNvbm5lY3RpbmcodHJ1ZSlcbiAgICAgICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCByZWNvbm5lY3RNY3BTZXJ2ZXIoc2VydmVyLm5hbWUpXG4gICAgICAgICAgICAgICAgICAgICAgaWYgKHNlcnZlci5jb25maWcudHlwZSA9PT0gJ2NsYXVkZWFpLXByb3h5Jykge1xuICAgICAgICAgICAgICAgICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2NsYXVkZWFpX21jcF9yZWNvbm5lY3QnLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHJlc3VsdC5jbGllbnQudHlwZSA9PT0gJ2Nvbm5lY3RlZCcsXG4gICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCB7IG1lc3NhZ2UgfSA9IGhhbmRsZVJlY29ubmVjdFJlc3VsdChcbiAgICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlcnZlci5uYW1lLFxuICAgICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgICBvbkNvbXBsZXRlPy4obWVzc2FnZSlcbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgaWYgKHNlcnZlci5jb25maWcudHlwZSA9PT0gJ2NsYXVkZWFpLXByb3h5Jykge1xuICAgICAgICAgICAgICAgICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2NsYXVkZWFpX21jcF9yZWNvbm5lY3QnLCB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgb25Db21wbGV0ZT8uKGhhbmRsZVJlY29ubmVjdEVycm9yKGVyciwgc2VydmVyLm5hbWUpKVxuICAgICAgICAgICAgICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgICAgICAgICAgICAgIHNldElzUmVjb25uZWN0aW5nKGZhbHNlKVxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgICAgICBjYXNlICd0b2dnbGUtZW5hYmxlZCc6XG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IGhhbmRsZVRvZ2dsZUVuYWJsZWQoKVxuICAgICAgICAgICAgICAgICAgICBicmVha1xuICAgICAgICAgICAgICAgICAgY2FzZSAnYmFjayc6XG4gICAgICAgICAgICAgICAgICAgIG9uQ2FuY2VsKClcbiAgICAgICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAgIG9uQ2FuY2VsPXtvbkNhbmNlbH1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICA8L0JveD5cblxuICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAge2V4aXRTdGF0ZS5wZW5kaW5nID8gKFxuICAgICAgICAgICAgPD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8Lz5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwi4oaR4oaTXCIgYWN0aW9uPVwibmF2aWdhdGVcIiAvPlxuICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cInNlbGVjdFwiIC8+XG4gICAgICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgICAgICAgICBmYWxsYmFjaz1cIkVzY1wiXG4gICAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJiYWNrXCJcbiAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgIDwvQnlsaW5lPlxuICAgICAgICAgICl9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLE9BQU8sTUFBTSxTQUFTO0FB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 \ No newline at end of file diff --git a/components/mcp/MCPSettings.tsx b/components/mcp/MCPSettings.tsx new file mode 100644 index 0000000..95562c7 --- /dev/null +++ b/components/mcp/MCPSettings.tsx @@ -0,0 +1,398 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useEffect, useMemo } from 'react'; +import type { CommandResultDisplay } from '../../commands.js'; +import { ClaudeAuthProvider } from '../../services/mcp/auth.js'; +import type { McpClaudeAIProxyServerConfig, McpHTTPServerConfig, McpSSEServerConfig, McpStdioServerConfig } from '../../services/mcp/types.js'; +import { extractAgentMcpServers, filterToolsByServer } from '../../services/mcp/utils.js'; +import { useAppState } from '../../state/AppState.js'; +import { getSessionIngressAuthToken } from '../../utils/sessionIngressAuth.js'; +import { MCPAgentServerMenu } from './MCPAgentServerMenu.js'; +import { MCPListPanel } from './MCPListPanel.js'; +import { MCPRemoteServerMenu } from './MCPRemoteServerMenu.js'; +import { MCPStdioServerMenu } from './MCPStdioServerMenu.js'; +import { MCPToolDetailView } from './MCPToolDetailView.js'; +import { MCPToolListView } from './MCPToolListView.js'; +import type { AgentMcpServerInfo, MCPViewState, ServerInfo } from './types.js'; +type Props = { + onComplete: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; +}; +export function MCPSettings(t0) { + const $ = _c(66); + const { + onComplete + } = t0; + const mcp = useAppState(_temp); + const agentDefinitions = useAppState(_temp2); + const mcpClients = mcp.clients; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + type: "list" + }; + $[0] = t1; + } else { + t1 = $[0]; + } + const [viewState, setViewState] = React.useState(t1); + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = []; + $[1] = t2; + } else { + t2 = $[1]; + } + const [servers, setServers] = React.useState(t2); + let t3; + if ($[2] !== agentDefinitions.allAgents) { + t3 = extractAgentMcpServers(agentDefinitions.allAgents); + $[2] = agentDefinitions.allAgents; + $[3] = t3; + } else { + t3 = $[3]; + } + const agentMcpServers = t3; + let t4; + if ($[4] !== mcpClients) { + t4 = mcpClients.filter(_temp3).sort(_temp4); + $[4] = mcpClients; + $[5] = t4; + } else { + t4 = $[5]; + } + const filteredClients = t4; + let t5; + let t6; + if ($[6] !== filteredClients || $[7] !== mcp.tools) { + t5 = () => { + let cancelled = false; + const prepareServers = async function prepareServers() { + const serverInfos = await Promise.all(filteredClients.map(async client_0 => { + const scope = client_0.config.scope; + const isSSE = client_0.config.type === "sse"; + const isHTTP = client_0.config.type === "http"; + const isClaudeAIProxy = client_0.config.type === "claudeai-proxy"; + let isAuthenticated = undefined; + if (isSSE || isHTTP) { + const authProvider = new ClaudeAuthProvider(client_0.name, client_0.config as McpSSEServerConfig | McpHTTPServerConfig); + const tokens = await authProvider.tokens(); + const hasSessionAuth = getSessionIngressAuthToken() !== null && client_0.type === "connected"; + const hasToolsAndConnected = client_0.type === "connected" && filterToolsByServer(mcp.tools, client_0.name).length > 0; + isAuthenticated = Boolean(tokens) || hasSessionAuth || hasToolsAndConnected; + } + const baseInfo = { + name: client_0.name, + client: client_0, + scope + }; + if (isClaudeAIProxy) { + return { + ...baseInfo, + transport: "claudeai-proxy" as const, + isAuthenticated: false, + config: client_0.config as McpClaudeAIProxyServerConfig + }; + } else { + if (isSSE) { + return { + ...baseInfo, + transport: "sse" as const, + isAuthenticated, + config: client_0.config as McpSSEServerConfig + }; + } else { + if (isHTTP) { + return { + ...baseInfo, + transport: "http" as const, + isAuthenticated, + config: client_0.config as McpHTTPServerConfig + }; + } else { + return { + ...baseInfo, + transport: "stdio" as const, + config: client_0.config as McpStdioServerConfig + }; + } + } + } + })); + if (cancelled) { + return; + } + setServers(serverInfos); + }; + prepareServers(); + return () => { + cancelled = true; + }; + }; + t6 = [filteredClients, mcp.tools]; + $[6] = filteredClients; + $[7] = mcp.tools; + $[8] = t5; + $[9] = t6; + } else { + t5 = $[8]; + t6 = $[9]; + } + React.useEffect(t5, t6); + let t7; + let t8; + if ($[10] !== agentMcpServers.length || $[11] !== filteredClients.length || $[12] !== onComplete || $[13] !== servers.length) { + t7 = () => { + if (servers.length === 0 && filteredClients.length > 0) { + return; + } + if (servers.length === 0 && agentMcpServers.length === 0) { + onComplete("No MCP servers configured. Please run /doctor if this is unexpected. Otherwise, run `claude mcp --help` or visit https://code.claude.com/docs/en/mcp to learn more."); + } + }; + t8 = [servers.length, filteredClients.length, agentMcpServers.length, onComplete]; + $[10] = agentMcpServers.length; + $[11] = filteredClients.length; + $[12] = onComplete; + $[13] = servers.length; + $[14] = t7; + $[15] = t8; + } else { + t7 = $[14]; + t8 = $[15]; + } + useEffect(t7, t8); + switch (viewState.type) { + case "list": + { + let t10; + let t9; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t9 = server => setViewState({ + type: "server-menu", + server + }); + t10 = agentServer => setViewState({ + type: "agent-server-menu", + agentServer + }); + $[16] = t10; + $[17] = t9; + } else { + t10 = $[16]; + t9 = $[17]; + } + let t11; + if ($[18] !== agentMcpServers || $[19] !== onComplete || $[20] !== servers || $[21] !== viewState.defaultTab) { + t11 = ; + $[18] = agentMcpServers; + $[19] = onComplete; + $[20] = servers; + $[21] = viewState.defaultTab; + $[22] = t11; + } else { + t11 = $[22]; + } + return t11; + } + case "server-menu": + { + let t9; + if ($[23] !== mcp.tools || $[24] !== viewState.server.name) { + t9 = filterToolsByServer(mcp.tools, viewState.server.name); + $[23] = mcp.tools; + $[24] = viewState.server.name; + $[25] = t9; + } else { + t9 = $[25]; + } + const serverTools_0 = t9; + const defaultTab = viewState.server.transport === "claudeai-proxy" ? "claude.ai" : "Claude Code"; + if (viewState.server.transport === "stdio") { + let t10; + if ($[26] !== viewState.server) { + t10 = () => setViewState({ + type: "server-tools", + server: viewState.server + }); + $[26] = viewState.server; + $[27] = t10; + } else { + t10 = $[27]; + } + let t11; + if ($[28] !== defaultTab) { + t11 = () => setViewState({ + type: "list", + defaultTab + }); + $[28] = defaultTab; + $[29] = t11; + } else { + t11 = $[29]; + } + let t12; + if ($[30] !== onComplete || $[31] !== serverTools_0.length || $[32] !== t10 || $[33] !== t11 || $[34] !== viewState.server) { + t12 = ; + $[30] = onComplete; + $[31] = serverTools_0.length; + $[32] = t10; + $[33] = t11; + $[34] = viewState.server; + $[35] = t12; + } else { + t12 = $[35]; + } + return t12; + } else { + let t10; + if ($[36] !== viewState.server) { + t10 = () => setViewState({ + type: "server-tools", + server: viewState.server + }); + $[36] = viewState.server; + $[37] = t10; + } else { + t10 = $[37]; + } + let t11; + if ($[38] !== defaultTab) { + t11 = () => setViewState({ + type: "list", + defaultTab + }); + $[38] = defaultTab; + $[39] = t11; + } else { + t11 = $[39]; + } + let t12; + if ($[40] !== onComplete || $[41] !== serverTools_0.length || $[42] !== t10 || $[43] !== t11 || $[44] !== viewState.server) { + t12 = ; + $[40] = onComplete; + $[41] = serverTools_0.length; + $[42] = t10; + $[43] = t11; + $[44] = viewState.server; + $[45] = t12; + } else { + t12 = $[45]; + } + return t12; + } + } + case "server-tools": + { + let t10; + let t9; + if ($[46] !== viewState.server) { + t9 = (_, index) => setViewState({ + type: "server-tool-detail", + server: viewState.server, + toolIndex: index + }); + t10 = () => setViewState({ + type: "server-menu", + server: viewState.server + }); + $[46] = viewState.server; + $[47] = t10; + $[48] = t9; + } else { + t10 = $[47]; + t9 = $[48]; + } + let t11; + if ($[49] !== t10 || $[50] !== t9 || $[51] !== viewState.server) { + t11 = ; + $[49] = t10; + $[50] = t9; + $[51] = viewState.server; + $[52] = t11; + } else { + t11 = $[52]; + } + return t11; + } + case "server-tool-detail": + { + let t9; + if ($[53] !== mcp.tools || $[54] !== viewState.server.name) { + t9 = filterToolsByServer(mcp.tools, viewState.server.name); + $[53] = mcp.tools; + $[54] = viewState.server.name; + $[55] = t9; + } else { + t9 = $[55]; + } + const serverTools = t9; + const tool = serverTools[viewState.toolIndex]; + if (!tool) { + setViewState({ + type: "server-tools", + server: viewState.server + }); + return null; + } + let t10; + if ($[56] !== viewState.server) { + t10 = () => setViewState({ + type: "server-tools", + server: viewState.server + }); + $[56] = viewState.server; + $[57] = t10; + } else { + t10 = $[57]; + } + let t11; + if ($[58] !== t10 || $[59] !== tool || $[60] !== viewState.server) { + t11 = ; + $[58] = t10; + $[59] = tool; + $[60] = viewState.server; + $[61] = t11; + } else { + t11 = $[61]; + } + return t11; + } + case "agent-server-menu": + { + let t9; + if ($[62] === Symbol.for("react.memo_cache_sentinel")) { + t9 = () => setViewState({ + type: "list", + defaultTab: "Agents" + }); + $[62] = t9; + } else { + t9 = $[62]; + } + let t10; + if ($[63] !== onComplete || $[64] !== viewState.agentServer) { + t10 = ; + $[63] = onComplete; + $[64] = viewState.agentServer; + $[65] = t10; + } else { + t10 = $[65]; + } + return t10; + } + } +} +function _temp4(a, b) { + return a.name.localeCompare(b.name); +} +function _temp3(client) { + return client.name !== "ide"; +} +function _temp2(s_0) { + return s_0.agentDefinitions; +} +function _temp(s) { + return s.mcp; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJDb21tYW5kUmVzdWx0RGlzcGxheSIsIkNsYXVkZUF1dGhQcm92aWRlciIsIk1jcENsYXVkZUFJUHJveHlTZXJ2ZXJDb25maWciLCJNY3BIVFRQU2VydmVyQ29uZmlnIiwiTWNwU1NFU2VydmVyQ29uZmlnIiwiTWNwU3RkaW9TZXJ2ZXJDb25maWciLCJleHRyYWN0QWdlbnRNY3BTZXJ2ZXJzIiwiZmlsdGVyVG9vbHNCeVNlcnZlciIsInVzZUFwcFN0YXRlIiwiZ2V0U2Vzc2lvbkluZ3Jlc3NBdXRoVG9rZW4iLCJNQ1BBZ2VudFNlcnZlck1lbnUiLCJNQ1BMaXN0UGFuZWwiLCJNQ1BSZW1vdGVTZXJ2ZXJNZW51IiwiTUNQU3RkaW9TZXJ2ZXJNZW51IiwiTUNQVG9vbERldGFpbFZpZXciLCJNQ1BUb29sTGlzdFZpZXciLCJBZ2VudE1jcFNlcnZlckluZm8iLCJNQ1BWaWV3U3RhdGUiLCJTZXJ2ZXJJbmZvIiwiUHJvcHMiLCJvbkNvbXBsZXRlIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJNQ1BTZXR0aW5ncyIsInQwIiwiJCIsIl9jIiwibWNwIiwiX3RlbXAiLCJhZ2VudERlZmluaXRpb25zIiwiX3RlbXAyIiwibWNwQ2xpZW50cyIsImNsaWVudHMiLCJ0MSIsIlN5bWJvbCIsImZvciIsInR5cGUiLCJ2aWV3U3RhdGUiLCJzZXRWaWV3U3RhdGUiLCJ1c2VTdGF0ZSIsInQyIiwic2VydmVycyIsInNldFNlcnZlcnMiLCJ0MyIsImFsbEFnZW50cyIsImFnZW50TWNwU2VydmVycyIsInQ0IiwiZmlsdGVyIiwiX3RlbXAzIiwic29ydCIsIl90ZW1wNCIsImZpbHRlcmVkQ2xpZW50cyIsInQ1IiwidDYiLCJ0b29scyIsImNhbmNlbGxlZCIsInByZXBhcmVTZXJ2ZXJzIiwic2VydmVySW5mb3MiLCJQcm9taXNlIiwiYWxsIiwibWFwIiwiY2xpZW50XzAiLCJzY29wZSIsImNsaWVudCIsImNvbmZpZyIsImlzU1NFIiwiaXNIVFRQIiwiaXNDbGF1ZGVBSVByb3h5IiwiaXNBdXRoZW50aWNhdGVkIiwidW5kZWZpbmVkIiwiYXV0aFByb3ZpZGVyIiwibmFtZSIsInRva2VucyIsImhhc1Nlc3Npb25BdXRoIiwiaGFzVG9vbHNBbmRDb25uZWN0ZWQiLCJsZW5ndGgiLCJCb29sZWFuIiwiYmFzZUluZm8iLCJ0cmFuc3BvcnQiLCJjb25zdCIsInQ3IiwidDgiLCJ0MTAiLCJ0OSIsInNlcnZlciIsImFnZW50U2VydmVyIiwidDExIiwiZGVmYXVsdFRhYiIsInNlcnZlclRvb2xzXzAiLCJ0MTIiLCJzZXJ2ZXJUb29scyIsIl8iLCJpbmRleCIsInRvb2xJbmRleCIsInRvb2wiLCJhIiwiYiIsImxvY2FsZUNvbXBhcmUiLCJzXzAiLCJzIl0sInNvdXJjZXMiOlsiTUNQU2V0dGluZ3MudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VFZmZlY3QsIHVzZU1lbW8gfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgQ29tbWFuZFJlc3VsdERpc3BsYXkgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IENsYXVkZUF1dGhQcm92aWRlciB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL21jcC9hdXRoLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBNY3BDbGF1ZGVBSVByb3h5U2VydmVyQ29uZmlnLFxuICBNY3BIVFRQU2VydmVyQ29uZmlnLFxuICBNY3BTU0VTZXJ2ZXJDb25maWcsXG4gIE1jcFN0ZGlvU2VydmVyQ29uZmlnLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvdHlwZXMuanMnXG5pbXBvcnQge1xuICBleHRyYWN0QWdlbnRNY3BTZXJ2ZXJzLFxuICBmaWx0ZXJUb29sc0J5U2VydmVyLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvdXRpbHMuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSB9IGZyb20gJy4uLy4uL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHsgZ2V0U2Vzc2lvbkluZ3Jlc3NBdXRoVG9rZW4gfSBmcm9tICcuLi8uLi91dGlscy9zZXNzaW9uSW5ncmVzc0F1dGguanMnXG5pbXBvcnQgeyBNQ1BBZ2VudFNlcnZlck1lbnUgfSBmcm9tICcuL01DUEFnZW50U2VydmVyTWVudS5qcydcbmltcG9ydCB7IE1DUExpc3RQYW5lbCB9IGZyb20gJy4vTUNQTGlzdFBhbmVsLmpzJ1xuaW1wb3J0IHsgTUNQUmVtb3RlU2VydmVyTWVudSB9IGZyb20gJy4vTUNQUmVtb3RlU2VydmVyTWVudS5qcydcbmltcG9ydCB7IE1DUFN0ZGlvU2VydmVyTWVudSB9IGZyb20gJy4vTUNQU3RkaW9TZXJ2ZXJNZW51LmpzJ1xuaW1wb3J0IHsgTUNQVG9vbERldGFpbFZpZXcgfSBmcm9tICcuL01DUFRvb2xEZXRhaWxWaWV3LmpzJ1xuaW1wb3J0IHsgTUNQVG9vbExpc3RWaWV3IH0gZnJvbSAnLi9NQ1BUb29sTGlzdFZpZXcuanMnXG5pbXBvcnQgdHlwZSB7IEFnZW50TWNwU2VydmVySW5mbywgTUNQVmlld1N0YXRlLCBTZXJ2ZXJJbmZvIH0gZnJvbSAnLi90eXBlcy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgb25Db21wbGV0ZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBNQ1BTZXR0aW5ncyh7IG9uQ29tcGxldGUgfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBtY3AgPSB1c2VBcHBTdGF0ZShzID0+IHMubWNwKVxuICBjb25zdCBhZ2VudERlZmluaXRpb25zID0gdXNlQXBwU3RhdGUocyA9PiBzLmFnZW50RGVmaW5pdGlvbnMpXG4gIGNvbnN0IG1jcENsaWVudHMgPSBtY3AuY2xpZW50c1xuICBjb25zdCBbdmlld1N0YXRlLCBzZXRWaWV3U3RhdGVdID0gUmVhY3QudXNlU3RhdGU8TUNQVmlld1N0YXRlPih7XG4gICAgdHlwZTogJ2xpc3QnLFxuICB9KVxuICBjb25zdCBbc2VydmVycywgc2V0U2VydmVyc10gPSBSZWFjdC51c2VTdGF0ZTxTZXJ2ZXJJbmZvW10+KFtdKVxuXG4gIC8vIEV4dHJhY3QgYWdlbnQtc3BlY2lmaWMgTUNQIHNlcnZlcnMgZnJvbSBhZ2VudCBkZWZpbml0aW9uc1xuICBjb25zdCBhZ2VudE1jcFNlcnZlcnMgPSB1c2VNZW1vKFxuICAgICgpID0+IGV4dHJhY3RBZ2VudE1jcFNlcnZlcnMoYWdlbnREZWZpbml0aW9ucy5hbGxBZ2VudHMpLFxuICAgIFthZ2VudERlZmluaXRpb25zLmFsbEFnZW50c10sXG4gIClcblxuICBjb25zdCBmaWx0ZXJlZENsaWVudHMgPSBSZWFjdC51c2VNZW1vKFxuICAgICgpID0+XG4gICAgICBtY3BDbGllbnRzXG4gICAgICAgIC5maWx0ZXIoY2xpZW50ID0+IGNsaWVudC5uYW1lICE9PSAnaWRlJylcbiAgICAgICAgLnNvcnQoKGEsIGIpID0+IGEubmFtZS5sb2NhbGVDb21wYXJlKGIubmFtZSkpLFxuICAgIFttY3BDbGllbnRzXSxcbiAgKVxuXG4gIFJlYWN0LnVzZUVmZmVjdCgoKSA9PiB7XG4gICAgbGV0IGNhbmNlbGxlZCA9IGZhbHNlXG4gICAgYXN5bmMgZnVuY3Rpb24gcHJlcGFyZVNlcnZlcnMoKSB7XG4gICAgICBjb25zdCBzZXJ2ZXJJbmZvcyA9IGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgICBmaWx0ZXJlZENsaWVudHMubWFwKGFzeW5jIGNsaWVudCA9PiB7XG4gICAgICAgICAgY29uc3Qgc2NvcGUgPSBjbGllbnQuY29uZmlnLnNjb3BlXG4gICAgICAgICAgY29uc3QgaXNTU0UgPSBjbGllbnQuY29uZmlnLnR5cGUgPT09ICdzc2UnXG4gICAgICAgICAgY29uc3QgaXNIVFRQID0gY2xpZW50LmNvbmZpZy50eXBlID09PSAnaHR0cCdcbiAgICAgICAgICBjb25zdCBpc0NsYXVkZUFJUHJveHkgPSBjbGllbnQuY29uZmlnLnR5cGUgPT09ICdjbGF1ZGVhaS1wcm94eSdcbiAgICAgICAgICBsZXQgaXNBdXRoZW50aWNhdGVkOiBib29sZWFuIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkXG5cbiAgICAgICAgICBpZiAoaXNTU0UgfHwgaXNIVFRQKSB7XG4gICAgICAgICAgICBjb25zdCBhdXRoUHJvdmlkZXIgPSBuZXcgQ2xhdWRlQXV0aFByb3ZpZGVyKFxuICAgICAgICAgICAgICBjbGllbnQubmFtZSxcbiAgICAgICAgICAgICAgY2xpZW50LmNvbmZpZyBhcyBNY3BTU0VTZXJ2ZXJDb25maWcgfCBNY3BIVFRQU2VydmVyQ29uZmlnLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgY29uc3QgdG9rZW5zID0gYXdhaXQgYXV0aFByb3ZpZGVyLnRva2VucygpXG4gICAgICAgICAgICAvLyBTZXJ2ZXIgaXMgYXV0aGVudGljYXRlZCBpZjpcbiAgICAgICAgICAgIC8vIDEuIEl0IGhhcyBPQXV0aCB0b2tlbnMsIE9SXG4gICAgICAgICAgICAvLyAyLiBJdCdzIGNvbm5lY3RlZCB2aWEgc2Vzc2lvbiBhdXRoIChoYXMgc2Vzc2lvbiB0b2tlbiBhbmQgaXMgY29ubmVjdGVkKSwgT1JcbiAgICAgICAgICAgIC8vIDMuIEl0J3MgY29ubmVjdGVkIGFuZCBoYXMgdG9vbHMgKG1lYW5pbmcgaXQncyB3b3JraW5nLCByZWdhcmRsZXNzIG9mIGF1dGggbWV0aG9kKVxuICAgICAgICAgICAgY29uc3QgaGFzU2Vzc2lvbkF1dGggPVxuICAgICAgICAgICAgICBnZXRTZXNzaW9uSW5ncmVzc0F1dGhUb2tlbigpICE9PSBudWxsICYmXG4gICAgICAgICAgICAgIGNsaWVudC50eXBlID09PSAnY29ubmVjdGVkJ1xuICAgICAgICAgICAgY29uc3QgaGFzVG9vbHNBbmRDb25uZWN0ZWQgPVxuICAgICAgICAgICAgICBjbGllbnQudHlwZSA9PT0gJ2Nvbm5lY3RlZCcgJiZcbiAgICAgICAgICAgICAgZmlsdGVyVG9vbHNCeVNlcnZlcihtY3AudG9vbHMsIGNsaWVudC5uYW1lKS5sZW5ndGggPiAwXG4gICAgICAgICAgICBpc0F1dGhlbnRpY2F0ZWQgPVxuICAgICAgICAgICAgICBCb29sZWFuKHRva2VucykgfHwgaGFzU2Vzc2lvbkF1dGggfHwgaGFzVG9vbHNBbmRDb25uZWN0ZWRcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBiYXNlSW5mbyA9IHtcbiAgICAgICAgICAgIG5hbWU6IGNsaWVudC5uYW1lLFxuICAgICAgICAgICAgY2xpZW50LFxuICAgICAgICAgICAgc2NvcGUsXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgaWYgKGlzQ2xhdWRlQUlQcm94eSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgLi4uYmFzZUluZm8sXG4gICAgICAgICAgICAgIHRyYW5zcG9ydDogJ2NsYXVkZWFpLXByb3h5JyBhcyBjb25zdCxcbiAgICAgICAgICAgICAgaXNBdXRoZW50aWNhdGVkOiBmYWxzZSxcbiAgICAgICAgICAgICAgY29uZmlnOiBjbGllbnQuY29uZmlnIGFzIE1jcENsYXVkZUFJUHJveHlTZXJ2ZXJDb25maWcsXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIGlmIChpc1NTRSkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgLi4uYmFzZUluZm8sXG4gICAgICAgICAgICAgIHRyYW5zcG9ydDogJ3NzZScgYXMgY29uc3QsXG4gICAgICAgICAgICAgIGlzQXV0aGVudGljYXRlZCxcbiAgICAgICAgICAgICAgY29uZmlnOiBjbGllbnQuY29uZmlnIGFzIE1jcFNTRVNlcnZlckNvbmZpZyxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2UgaWYgKGlzSFRUUCkge1xuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgLi4uYmFzZUluZm8sXG4gICAgICAgICAgICAgIHRyYW5zcG9ydDogJ2h0dHAnIGFzIGNvbnN0LFxuICAgICAgICAgICAgICBpc0F1dGhlbnRpY2F0ZWQsXG4gICAgICAgICAgICAgIGNvbmZpZzogY2xpZW50LmNvbmZpZyBhcyBNY3BIVFRQU2VydmVyQ29uZmlnLFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAuLi5iYXNlSW5mbyxcbiAgICAgICAgICAgICAgdHJhbnNwb3J0OiAnc3RkaW8nIGFzIGNvbnN0LFxuICAgICAgICAgICAgICBjb25maWc6IGNsaWVudC5jb25maWcgYXMgTWNwU3RkaW9TZXJ2ZXJDb25maWcsXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9KSxcbiAgICAgIClcblxuICAgICAgaWYgKGNhbmNlbGxlZCkgcmV0dXJuXG4gICAgICBzZXRTZXJ2ZXJzKHNlcnZlckluZm9zKVxuICAgIH1cblxuICAgIHZvaWQgcHJlcGFyZVNlcnZlcnMoKVxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBjYW5jZWxsZWQgPSB0cnVlXG4gICAgfVxuICB9LCBbZmlsdGVyZWRDbGllbnRzLCBtY3AudG9vbHNdKVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKHNlcnZlcnMubGVuZ3RoID09PSAwICYmIGZpbHRlcmVkQ2xpZW50cy5sZW5ndGggPiAwKSB7XG4gICAgICAvLyBTdGlsbCBsb2FkaW5nXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBPbmx5IHNob3cgXCJubyBzZXJ2ZXJzXCIgbWVzc2FnZSBpZiBubyByZWd1bGFyIHNlcnZlcnMgQU5EIG5vIGFnZW50IHNlcnZlcnNcbiAgICBpZiAoc2VydmVycy5sZW5ndGggPT09IDAgJiYgYWdlbnRNY3BTZXJ2ZXJzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgb25Db21wbGV0ZShcbiAgICAgICAgJ05vIE1DUCBzZXJ2ZXJzIGNvbmZpZ3VyZWQuIFBsZWFzZSBydW4gL2RvY3RvciBpZiB0aGlzIGlzIHVuZXhwZWN0ZWQuIE90aGVyd2lzZSwgcnVuIGBjbGF1ZGUgbWNwIC0taGVscGAgb3IgdmlzaXQgaHR0cHM6Ly9jb2RlLmNsYXVkZS5jb20vZG9jcy9lbi9tY3AgdG8gbGVhcm4gbW9yZS4nLFxuICAgICAgKVxuICAgIH1cbiAgfSwgW1xuICAgIHNlcnZlcnMubGVuZ3RoLFxuICAgIGZpbHRlcmVkQ2xpZW50cy5sZW5ndGgsXG4gICAgYWdlbnRNY3BTZXJ2ZXJzLmxlbmd0aCxcbiAgICBvbkNvbXBsZXRlLFxuICBdKVxuXG4gIHN3aXRjaCAodmlld1N0YXRlLnR5cGUpIHtcbiAgICBjYXNlICdsaXN0JzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxNQ1BMaXN0UGFuZWxcbiAgICAgICAgICBzZXJ2ZXJzPXtzZXJ2ZXJzfVxuICAgICAgICAgIGFnZW50U2VydmVycz17YWdlbnRNY3BTZXJ2ZXJzfVxuICAgICAgICAgIG9uU2VsZWN0U2VydmVyPXtzZXJ2ZXIgPT5cbiAgICAgICAgICAgIHNldFZpZXdTdGF0ZSh7IHR5cGU6ICdzZXJ2ZXItbWVudScsIHNlcnZlciB9KVxuICAgICAgICAgIH1cbiAgICAgICAgICBvblNlbGVjdEFnZW50U2VydmVyPXsoYWdlbnRTZXJ2ZXI6IEFnZW50TWNwU2VydmVySW5mbykgPT5cbiAgICAgICAgICAgIHNldFZpZXdTdGF0ZSh7IHR5cGU6ICdhZ2VudC1zZXJ2ZXItbWVudScsIGFnZW50U2VydmVyIH0pXG4gICAgICAgICAgfVxuICAgICAgICAgIG9uQ29tcGxldGU9e29uQ29tcGxldGV9XG4gICAgICAgICAgZGVmYXVsdFRhYj17dmlld1N0YXRlLmRlZmF1bHRUYWJ9XG4gICAgICAgIC8+XG4gICAgICApXG5cbiAgICBjYXNlICdzZXJ2ZXItbWVudSc6IHtcbiAgICAgIGNvbnN0IHNlcnZlclRvb2xzID0gZmlsdGVyVG9vbHNCeVNlcnZlcihtY3AudG9vbHMsIHZpZXdTdGF0ZS5zZXJ2ZXIubmFtZSlcblxuICAgICAgY29uc3QgZGVmYXVsdFRhYiA9XG4gICAgICAgIHZpZXdTdGF0ZS5zZXJ2ZXIudHJhbnNwb3J0ID09PSAnY2xhdWRlYWktcHJveHknXG4gICAgICAgICAgPyAnY2xhdWRlLmFpJ1xuICAgICAgICAgIDogJ0NsYXVkZSBDb2RlJ1xuXG4gICAgICBpZiAodmlld1N0YXRlLnNlcnZlci50cmFuc3BvcnQgPT09ICdzdGRpbycpIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICA8TUNQU3RkaW9TZXJ2ZXJNZW51XG4gICAgICAgICAgICBzZXJ2ZXI9e3ZpZXdTdGF0ZS5zZXJ2ZXJ9XG4gICAgICAgICAgICBzZXJ2ZXJUb29sc0NvdW50PXtzZXJ2ZXJUb29scy5sZW5ndGh9XG4gICAgICAgICAgICBvblZpZXdUb29scz17KCkgPT5cbiAgICAgICAgICAgICAgc2V0Vmlld1N0YXRlKHsgdHlwZTogJ3NlcnZlci10b29scycsIHNlcnZlcjogdmlld1N0YXRlLnNlcnZlciB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgb25DYW5jZWw9eygpID0+IHNldFZpZXdTdGF0ZSh7IHR5cGU6ICdsaXN0JywgZGVmYXVsdFRhYiB9KX1cbiAgICAgICAgICAgIG9uQ29tcGxldGU9e29uQ29tcGxldGV9XG4gICAgICAgICAgLz5cbiAgICAgICAgKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICA8TUNQUmVtb3RlU2VydmVyTWVudVxuICAgICAgICAgICAgc2VydmVyPXt2aWV3U3RhdGUuc2VydmVyfVxuICAgICAgICAgICAgc2VydmVyVG9vbHNDb3VudD17c2VydmVyVG9vbHMubGVuZ3RofVxuICAgICAgICAgICAgb25WaWV3VG9vbHM9eygpID0+XG4gICAgICAgICAgICAgIHNldFZpZXdTdGF0ZSh7IHR5cGU6ICdzZXJ2ZXItdG9vbHMnLCBzZXJ2ZXI6IHZpZXdTdGF0ZS5zZXJ2ZXIgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBzZXRWaWV3U3RhdGUoeyB0eXBlOiAnbGlzdCcsIGRlZmF1bHRUYWIgfSl9XG4gICAgICAgICAgICBvbkNvbXBsZXRlPXtvbkNvbXBsZXRlfVxuICAgICAgICAgIC8+XG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjYXNlICdzZXJ2ZXItdG9vbHMnOlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPE1DUFRvb2xMaXN0Vmlld1xuICAgICAgICAgIHNlcnZlcj17dmlld1N0YXRlLnNlcnZlcn1cbiAgICAgICAgICBvblNlbGVjdFRvb2w9eyhfLCBpbmRleCkgPT5cbiAgICAgICAgICAgIHNldFZpZXdTdGF0ZSh7XG4gICAgICAgICAgICAgIHR5cGU6ICdzZXJ2ZXItdG9vbC1kZXRhaWwnLFxuICAgICAgICAgICAgICBzZXJ2ZXI6IHZpZXdTdGF0ZS5zZXJ2ZXIsXG4gICAgICAgICAgICAgIHRvb2xJbmRleDogaW5kZXgsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH1cbiAgICAgICAgICBvbkJhY2s9eygpID0+XG4gICAgICAgICAgICBzZXRWaWV3U3RhdGUoeyB0eXBlOiAnc2VydmVyLW1lbnUnLCBzZXJ2ZXI6IHZpZXdTdGF0ZS5zZXJ2ZXIgfSlcbiAgICAgICAgICB9XG4gICAgICAgIC8+XG4gICAgICApXG5cbiAgICBjYXNlICdzZXJ2ZXItdG9vbC1kZXRhaWwnOiB7XG4gICAgICBjb25zdCBzZXJ2ZXJUb29scyA9IGZpbHRlclRvb2xzQnlTZXJ2ZXIobWNwLnRvb2xzLCB2aWV3U3RhdGUuc2VydmVyLm5hbWUpXG4gICAgICBjb25zdCB0b29sID0gc2VydmVyVG9vbHNbdmlld1N0YXRlLnRvb2xJbmRleF1cbiAgICAgIGlmICghdG9vbCkge1xuICAgICAgICBzZXRWaWV3U3RhdGUoeyB0eXBlOiAnc2VydmVyLXRvb2xzJywgc2VydmVyOiB2aWV3U3RhdGUuc2VydmVyIH0pXG4gICAgICAgIHJldHVybiBudWxsXG4gICAgICB9XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8TUNQVG9vbERldGFpbFZpZXdcbiAgICAgICAgICB0b29sPXt0b29sfVxuICAgICAgICAgIHNlcnZlcj17dmlld1N0YXRlLnNlcnZlcn1cbiAgICAgICAgICBvbkJhY2s9eygpID0+XG4gICAgICAgICAgICBzZXRWaWV3U3RhdGUoeyB0eXBlOiAnc2VydmVyLXRvb2xzJywgc2VydmVyOiB2aWV3U3RhdGUuc2VydmVyIH0pXG4gICAgICAgICAgfVxuICAgICAgICAvPlxuICAgICAgKVxuICAgIH1cblxuICAgIGNhc2UgJ2FnZW50LXNlcnZlci1tZW51JzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxNQ1BBZ2VudFNlcnZlck1lbnVcbiAgICAgICAgICBhZ2VudFNlcnZlcj17dmlld1N0YXRlLmFnZW50U2VydmVyfVxuICAgICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBzZXRWaWV3U3RhdGUoeyB0eXBlOiAnbGlzdCcsIGRlZmF1bHRUYWI6ICdBZ2VudHMnIH0pfVxuICAgICAgICAgIG9uQ29tcGxldGU9e29uQ29tcGxldGV9XG4gICAgICAgIC8+XG4gICAgICApXG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFBSUMsU0FBUyxFQUFFQyxPQUFPLFFBQVEsT0FBTztBQUNqRCxjQUFjQyxvQkFBb0IsUUFBUSxtQkFBbUI7QUFDN0QsU0FBU0Msa0JBQWtCLFFBQVEsNEJBQTRCO0FBQy9ELGNBQ0VDLDRCQUE0QixFQUM1QkMsbUJBQW1CLEVBQ25CQyxrQkFBa0IsRUFDbEJDLG9CQUFvQixRQUNmLDZCQUE2QjtBQUNwQyxTQUNFQyxzQkFBc0IsRUFDdEJDLG1CQUFtQixRQUNkLDZCQUE2QjtBQUNwQyxTQUFTQyxXQUFXLFFBQVEseUJBQXlCO0FBQ3JELFNBQVNDLDBCQUEwQixRQUFRLG1DQUFtQztBQUM5RSxTQUFTQyxrQkFBa0IsUUFBUSx5QkFBeUI7QUFDNUQsU0FBU0MsWUFBWSxRQUFRLG1CQUFtQjtBQUNoRCxTQUFTQyxtQkFBbUIsUUFBUSwwQkFBMEI7QUFDOUQsU0FBU0Msa0JBQWtCLFFBQVEseUJBQXlCO0FBQzVELFNBQVNDLGlCQUFpQixRQUFRLHdCQUF3QjtBQUMxRCxTQUFTQyxlQUFlLFFB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 \ No newline at end of file diff --git a/components/mcp/MCPStdioServerMenu.tsx b/components/mcp/MCPStdioServerMenu.tsx new file mode 100644 index 0000000..b595103 --- /dev/null +++ b/components/mcp/MCPStdioServerMenu.tsx @@ -0,0 +1,177 @@ +import figures from 'figures'; +import React, { useState } from 'react'; +import type { CommandResultDisplay } from '../../commands.js'; +import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { Box, color, Text, useTheme } from '../../ink.js'; +import { getMcpConfigByName } from '../../services/mcp/config.js'; +import { useMcpReconnect, useMcpToggleEnabled } from '../../services/mcp/MCPConnectionManager.js'; +import { describeMcpConfigFilePath, filterMcpPromptsByServer } from '../../services/mcp/utils.js'; +import { useAppState } from '../../state/AppState.js'; +import { errorMessage } from '../../utils/errors.js'; +import { capitalize } from '../../utils/stringUtils.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { Select } from '../CustomSelect/index.js'; +import { Byline } from '../design-system/Byline.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { Spinner } from '../Spinner.js'; +import { CapabilitiesSection } from './CapabilitiesSection.js'; +import type { StdioServerInfo } from './types.js'; +import { handleReconnectError, handleReconnectResult } from './utils/reconnectHelpers.js'; +type Props = { + server: StdioServerInfo; + serverToolsCount: number; + onViewTools: () => void; + onCancel: () => void; + onComplete: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; + borderless?: boolean; +}; +export function MCPStdioServerMenu({ + server, + serverToolsCount, + onViewTools, + onCancel, + onComplete, + borderless = false +}: Props): React.ReactNode { + const [theme] = useTheme(); + const exitState = useExitOnCtrlCDWithKeybindings(); + const mcp = useAppState(s => s.mcp); + const reconnectMcpServer = useMcpReconnect(); + const toggleMcpServer = useMcpToggleEnabled(); + const [isReconnecting, setIsReconnecting] = useState(false); + const handleToggleEnabled = React.useCallback(async () => { + const wasEnabled = server.client.type !== 'disabled'; + try { + await toggleMcpServer(server.name); + // Return to the server list so user can continue managing other servers + onCancel(); + } catch (err) { + const action = wasEnabled ? 'disable' : 'enable'; + onComplete(`Failed to ${action} MCP server '${server.name}': ${errorMessage(err)}`); + } + }, [server.client.type, server.name, toggleMcpServer, onCancel, onComplete]); + const capitalizedServerName = capitalize(String(server.name)); + + // Count MCP prompts for this server (skills are shown in /skills, not here) + const serverCommandsCount = filterMcpPromptsByServer(mcp.commands, server.name).length; + const menuOptions = []; + + // Only show "View tools" if server is not disabled and has tools + if (server.client.type !== 'disabled' && serverToolsCount > 0) { + menuOptions.push({ + label: 'View tools', + value: 'tools' + }); + } + + // Only show reconnect option if the server is not disabled + if (server.client.type !== 'disabled') { + menuOptions.push({ + label: 'Reconnect', + value: 'reconnectMcpServer' + }); + } + menuOptions.push({ + label: server.client.type !== 'disabled' ? 'Disable' : 'Enable', + value: 'toggle-enabled' + }); + + // If there are no other options, add a back option so Select handles escape + if (menuOptions.length === 0) { + menuOptions.push({ + label: 'Back', + value: 'back' + }); + } + if (isReconnecting) { + return + + Reconnecting to {server.name} + + + + Restarting MCP server process + + This may take a few moments. + ; + } + return + + + {capitalizedServerName} MCP Server + + + + + Status: + {server.client.type === 'disabled' ? {color('inactive', theme)(figures.radioOff)} disabled : server.client.type === 'connected' ? {color('success', theme)(figures.tick)} connected : server.client.type === 'pending' ? <> + {figures.radioOff} + connecting… + : {color('error', theme)(figures.cross)} failed} + + + + Command: + {server.config.command} + + + {server.config.args && server.config.args.length > 0 && + Args: + {server.config.args.join(' ')} + } + + + Config location: + + {describeMcpConfigFilePath(getMcpConfigByName(server.name)?.scope ?? 'dynamic')} + + + + {server.client.type === 'connected' && } + + {server.client.type === 'connected' && serverToolsCount > 0 && + Tools: + {serverToolsCount} tools + } + + + {menuOptions.length > 0 && + { + const index_0 = parseInt(value); + const tool_0 = serverTools[index_0]; + if (tool_0) { + onSelectTool(tool_0, index_0); + } + }} onCancel={onBack} />; + $[11] = onBack; + $[12] = onSelectTool; + $[13] = serverTools; + $[14] = toolOptions; + $[15] = t7; + } else { + t7 = $[15]; + } + let t8; + if ($[16] !== onBack || $[17] !== t3 || $[18] !== t6 || $[19] !== t7) { + t8 = {t7}; + $[16] = onBack; + $[17] = t3; + $[18] = t6; + $[19] = t7; + $[20] = t8; + } else { + t8 = $[20]; + } + return t8; +} +function _temp2(exitState) { + return exitState.pending ? Press {exitState.keyName} again to exit : ; +} +function _temp(s) { + return s.mcp.tools; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJleHRyYWN0TWNwVG9vbERpc3BsYXlOYW1lIiwiZ2V0TWNwRGlzcGxheU5hbWUiLCJmaWx0ZXJUb29sc0J5U2VydmVyIiwidXNlQXBwU3RhdGUiLCJUb29sIiwicGx1cmFsIiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiU2VsZWN0IiwiQnlsaW5lIiwiRGlhbG9nIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJTZXJ2ZXJJbmZvIiwiUHJvcHMiLCJzZXJ2ZXIiLCJvblNlbGVjdFRvb2wiLCJ0b29sIiwiaW5kZXgiLCJvbkJhY2siLCJNQ1BUb29sTGlzdFZpZXciLCJ0MCIsIiQiLCJfYyIsIm1jcFRvb2xzIiwiX3RlbXAiLCJ0MSIsImJiMCIsImNsaWVudCIsInR5cGUiLCJ0MiIsIlN5bWJvbCIsImZvciIsIm5hbWUiLCJzZXJ2ZXJUb29scyIsInQzIiwidG9vbE5hbWUiLCJmdWxsRGlzcGxheU5hbWUiLCJ1c2VyRmFjaW5nTmFtZSIsImRpc3BsYXlOYW1lIiwiaXNSZWFkT25seSIsImlzRGVzdHJ1Y3RpdmUiLCJpc09wZW5Xb3JsZCIsImFubm90YXRpb25zIiwicHVzaCIsImxhYmVsIiwidmFsdWUiLCJ0b1N0cmluZyIsImRlc2NyaXB0aW9uIiwibGVuZ3RoIiwiam9pbiIsInVuZGVmaW5lZCIsImRlc2NyaXB0aW9uQ29sb3IiLCJtYXAiLCJ0b29sT3B0aW9ucyIsInQ0IiwidDUiLCJ0NiIsInQ3IiwiaW5kZXhfMCIsInBhcnNlSW50IiwidG9vbF8wIiwidDgiLCJfdGVtcDIiLCJleGl0U3RhdGUiLCJwZW5kaW5nIiwia2V5TmFtZSIsInMiLCJtY3AiLCJ0b29scyJdLCJzb3VyY2VzIjpbIk1DUFRvb2xMaXN0Vmlldy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7XG4gIGV4dHJhY3RNY3BUb29sRGlzcGxheU5hbWUsXG4gIGdldE1jcERpc3BsYXlOYW1lLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvbWNwU3RyaW5nVXRpbHMuanMnXG5pbXBvcnQgeyBmaWx0ZXJUb29sc0J5U2VydmVyIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvbWNwL3V0aWxzLmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgVG9vbCB9IGZyb20gJy4uLy4uL1Rvb2wuanMnXG5pbXBvcnQgeyBwbHVyYWwgfSBmcm9tICcuLi8uLi91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB7IENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCB9IGZyb20gJy4uL0NvbmZpZ3VyYWJsZVNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4uL0N1c3RvbVNlbGVjdC9pbmRleC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vQnlsaW5lLmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgdHlwZSB7IFNlcnZlckluZm8gfSBmcm9tICcuL3R5cGVzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBzZXJ2ZXI6IFNlcnZlckluZm9cbiAgb25TZWxlY3RUb29sOiAodG9vbDogVG9vbCwgaW5kZXg6IG51bWJlcikgPT4gdm9pZFxuICBvbkJhY2s6ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIE1DUFRvb2xMaXN0Vmlldyh7XG4gIHNlcnZlcixcbiAgb25TZWxlY3RUb29sLFxuICBvbkJhY2ssXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IG1jcFRvb2xzID0gdXNlQXBwU3RhdGUocyA9PiBzLm1jcC50b29scylcblxuICBjb25zdCBzZXJ2ZXJUb29scyA9IFJlYWN0LnVzZU1lbW8oKCkgPT4ge1xuICAgIGlmIChzZXJ2ZXIuY2xpZW50LnR5cGUgIT09ICdjb25uZWN0ZWQnKSByZXR1cm4gW11cbiAgICByZXR1cm4gZmlsdGVyVG9vbHNCeVNlcnZlcihtY3BUb29scywgc2VydmVyLm5hbWUpXG4gIH0sIFtzZXJ2ZXIsIG1jcFRvb2xzXSlcblxuICBjb25zdCB0b29sT3B0aW9ucyA9IHNlcnZlclRvb2xzLm1hcCgodG9vbCwgaW5kZXgpID0+IHtcbiAgICBjb25zdCB0b29sTmFtZSA9IGdldE1jcERpc3BsYXlOYW1lKHRvb2wubmFtZSwgc2VydmVyLm5hbWUpXG4gICAgY29uc3QgZnVsbERpc3BsYXlOYW1lID0gdG9vbC51c2VyRmFjaW5nTmFtZVxuICAgICAgPyB0b29sLnVzZXJGYWNpbmdOYW1lKHt9KVxuICAgICAgOiB0b29sTmFtZVxuICAgIC8vIEV4dHJhY3QganVzdCB0aGUgdG9vbCBkaXNwbGF5IG5hbWUgd2l0aG91dCBzZXJ2ZXIgcHJlZml4XG4gICAgY29uc3QgZGlzcGxheU5hbWUgPSBleHRyYWN0TWNwVG9vbERpc3BsYXlOYW1lKGZ1bGxEaXNwbGF5TmFtZSlcblxuICAgIGNvbnN0IGlzUmVhZE9ubHkgPSB0b29sLmlzUmVhZE9ubHk/Lih7fSkgPz8gZmFsc2VcbiAgICBjb25zdCBpc0Rlc3RydWN0aXZlID0gdG9vbC5pc0Rlc3RydWN0aXZlPy4oe30pID8/IGZhbHNlXG4gICAgY29uc3QgaXNPcGVuV29ybGQgPSB0b29sLmlzT3BlbldvcmxkPy4oe30pID8/IGZhbHNlXG5cbiAgICBjb25zdCBhbm5vdGF0aW9ucyA9IFtdXG4gICAgaWYgKGlzUmVhZE9ubHkpIGFubm90YXRpb25zLnB1c2goJ3JlYWQtb25seScpXG4gICAgaWYgKGlzRGVzdHJ1Y3RpdmUpIGFubm90YXRpb25zLnB1c2goJ2Rlc3RydWN0aXZlJylcbiAgICBpZiAoaXNPcGVuV29ybGQpIGFubm90YXRpb25zLnB1c2goJ29wZW4td29ybGQnKVxuXG4gICAgcmV0dXJuIHtcbiAgICAgIGxhYmVsOiBkaXNwbGF5TmFtZSxcbiAgICAgIHZhbHVlOiBpbmRleC50b1N0cmluZygpLFxuICAgICAgZGVzY3JpcHRpb246IGFubm90YXRpb25zLmxlbmd0aCA+IDAgPyBhbm5vdGF0aW9ucy5qb2luKCcsICcpIDogdW5kZWZpbmVkLFxuICAgICAgZGVzY3JpcHRpb25Db2xvcjogaXNEZXN0cnVjdGl2ZVxuICAgICAgICA/ICdlcnJvcidcbiAgICAgICAgOiBpc1JlYWRPbmx5XG4gICAgICAgICAgPyAnc3VjY2VzcydcbiAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICB9XG4gIH0pXG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT17YFRvb2xzIGZvciAke3NlcnZlci5uYW1lfWB9XG4gICAgICBzdWJ0aXRsZT17YCR7c2VydmVyVG9vbHMubGVuZ3RofSAke3BsdXJhbChzZXJ2ZXJUb29scy5sZW5ndGgsICd0b29sJyl9YH1cbiAgICAgIG9uQ2FuY2VsPXtvbkJhY2t9XG4gICAgICBpbnB1dEd1aWRlPXtleGl0U3RhdGUgPT5cbiAgICAgICAgZXhpdFN0YXRlLnBlbmRpbmcgPyAoXG4gICAgICAgICAgPFRleHQ+UHJlc3Mge2V4aXRTdGF0ZS5rZXlOYW1lfSBhZ2FpbiB0byBleGl0PC9UZXh0PlxuICAgICAgICApIDogKFxuICAgICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCLihpHihpNcIiBhY3Rpb249XCJuYXZpZ2F0ZVwiIC8+XG4gICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cInNlbGVjdFwiIC8+XG4gICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgICBkZXNjcmlwdGlvbj1cImJhY2tcIlxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgKVxuICAgICAgfVxuICAgID5cbiAgICAgIHtzZXJ2ZXJUb29scy5sZW5ndGggPT09IDAgPyAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPk5vIHRvb2xzIGF2YWlsYWJsZTwvVGV4dD5cbiAgICAgICkgOiAoXG4gICAgICAgIDxTZWxlY3RcbiAgICAgICAgICBvcHRpb25zPXt0b29sT3B0aW9uc31cbiAgICAgICAgICBvbkNoYW5nZT17dmFsdWUgPT4ge1xuICAgICAgICAgICAgY29uc3QgaW5kZXggPSBwYXJzZUludCh2YWx1ZSlcbiAgICAgICAgICAgIGNvbnN0IHRvb2wgPSBzZXJ2ZXJUb29sc1tpbmRleF1cbiAgICAgICAgICAgIGlmICh0b29sKSB7XG4gICAgICAgICAgICAgIG9uU2VsZWN0VG9vbCh0b29sLCBpbmRleClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9fVxuICAgICAgICAgIG9uQ2FuY2VsPXtvbkJhY2t9XG4gICAgICAgIC8+XG4gICAgICApfVxuICAgIDwvRGlhbG9nPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyxJQUFJLFFBQVEsY0FBYztBQUNuQyxTQUNFQyx5QkFBeUIsRUFDekJDLGlCQUFpQixRQUNaLHNDQUFzQztBQUM3QyxTQUFTQyxtQkFBbUIsUUFBUSw2QkFBNkI7QUFDakUsU0FBU0MsV0FBVyxRQUFRLHlCQUF5QjtBQUNyRCxjQUFjQyxJQUFJLFFBQVEsZUFBZTtBQUN6QyxTQUFTQyxNQUFNLFFBQVEsNEJBQTRCO0FBQ25ELFNBQVNDLHdCQUF3QixRQUFRLGdDQUFnQztBQUN6RSxTQUFTQyxNQUFNLFFBQVEsMEJBQTBCO0FB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 \ No newline at end of file diff --git a/components/mcp/McpParsingWarnings.tsx b/components/mcp/McpParsingWarnings.tsx new file mode 100644 index 0000000..db13014 --- /dev/null +++ b/components/mcp/McpParsingWarnings.tsx @@ -0,0 +1,213 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useMemo } from 'react'; +import { getMcpConfigsByScope } from 'src/services/mcp/config.js'; +import type { ConfigScope } from 'src/services/mcp/types.js'; +import { describeMcpConfigFilePath, getScopeLabel } from 'src/services/mcp/utils.js'; +import type { ValidationError } from 'src/utils/settings/validation.js'; +import { Box, Link, Text } from '../../ink.js'; +function McpConfigErrorSection(t0) { + const $ = _c(26); + const { + scope, + parsingErrors, + warnings + } = t0; + const hasErrors = parsingErrors.length > 0; + const hasWarnings = warnings.length > 0; + if (!hasErrors && !hasWarnings) { + return null; + } + let t1; + if ($[0] !== hasErrors || $[1] !== hasWarnings) { + t1 = (hasErrors || hasWarnings) && [{hasErrors ? "Failed to parse" : "Contains warnings"}]{" "}; + $[0] = hasErrors; + $[1] = hasWarnings; + $[2] = t1; + } else { + t1 = $[2]; + } + let t2; + if ($[3] !== scope) { + t2 = getScopeLabel(scope); + $[3] = scope; + $[4] = t2; + } else { + t2 = $[4]; + } + let t3; + if ($[5] !== t2) { + t3 = {t2}; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] !== t1 || $[8] !== t3) { + t4 = {t1}{t3}; + $[7] = t1; + $[8] = t3; + $[9] = t4; + } else { + t4 = $[9]; + } + let t5; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t5 = Location: ; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] !== scope) { + t6 = describeMcpConfigFilePath(scope); + $[11] = scope; + $[12] = t6; + } else { + t6 = $[12]; + } + let t7; + if ($[13] !== t6) { + t7 = {t5}{t6}; + $[13] = t6; + $[14] = t7; + } else { + t7 = $[14]; + } + let t8; + if ($[15] !== parsingErrors) { + t8 = parsingErrors.map(_temp); + $[15] = parsingErrors; + $[16] = t8; + } else { + t8 = $[16]; + } + let t9; + if ($[17] !== warnings) { + t9 = warnings.map(_temp2); + $[17] = warnings; + $[18] = t9; + } else { + t9 = $[18]; + } + let t10; + if ($[19] !== t8 || $[20] !== t9) { + t10 = {t8}{t9}; + $[19] = t8; + $[20] = t9; + $[21] = t10; + } else { + t10 = $[21]; + } + let t11; + if ($[22] !== t10 || $[23] !== t4 || $[24] !== t7) { + t11 = {t4}{t7}{t10}; + $[22] = t10; + $[23] = t4; + $[24] = t7; + $[25] = t11; + } else { + t11 = $[25]; + } + return t11; +} +function _temp2(warning, i_0) { + const serverName_0 = warning.mcpErrorMetadata?.serverName; + return [Warning]{" "}{serverName_0 && `[${serverName_0}] `}{warning.path && warning.path !== "" ? `${warning.path}: ` : ""}{warning.message}; +} +function _temp(error, i) { + const serverName = error.mcpErrorMetadata?.serverName; + return [Error]{" "}{serverName && `[${serverName}] `}{error.path && error.path !== "" ? `${error.path}: ` : ""}{error.message}; +} +export function McpParsingWarnings() { + const $ = _c(6); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = { + scope: "user", + config: getMcpConfigsByScope("user") + }; + $[0] = t0; + } else { + t0 = $[0]; + } + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + scope: "project", + config: getMcpConfigsByScope("project") + }; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = { + scope: "local", + config: getMcpConfigsByScope("local") + }; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = [t0, t1, t2, { + scope: "enterprise", + config: getMcpConfigsByScope("enterprise") + }]; + $[3] = t3; + } else { + t3 = $[3]; + } + const scopes = t3 satisfies Array<{ + scope: ConfigScope; + config: { + errors: ValidationError[]; + }; + }>; + const hasParsingErrors = scopes.some(_temp3); + const hasWarnings = scopes.some(_temp4); + if (!hasParsingErrors && !hasWarnings) { + return null; + } + let t4; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t4 = MCP Config Diagnostics; + $[4] = t4; + } else { + t4 = $[4]; + } + let t5; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t5 = {t4}For help configuring MCP servers, see:{" "}https://code.claude.com/docs/en/mcp{scopes.map(_temp5)}; + $[5] = t5; + } else { + t5 = $[5]; + } + return t5; +} +function _temp5(t0) { + const { + scope, + config: config_1 + } = t0; + return ; +} +function _temp4(t0) { + const { + config: config_0 + } = t0; + return filterErrors(config_0.errors, "warning").length > 0; +} +function _temp3(t0) { + const { + config + } = t0; + return filterErrors(config.errors, "fatal").length > 0; +} +function filterErrors(errors: ValidationError[], severity: 'fatal' | 'warning'): ValidationError[] { + return errors.filter(e => e.mcpErrorMetadata?.severity === severity); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZU1lbW8iLCJnZXRNY3BDb25maWdzQnlTY29wZSIsIkNvbmZpZ1Njb3BlIiwiZGVzY3JpYmVNY3BDb25maWdGaWxlUGF0aCIsImdldFNjb3BlTGFiZWwiLCJWYWxpZGF0aW9uRXJyb3IiLCJCb3giLCJMaW5rIiwiVGV4dCIsIk1jcENvbmZpZ0Vycm9yU2VjdGlvbiIsInQwIiwiJCIsIl9jIiwic2NvcGUiLCJwYXJzaW5nRXJyb3JzIiwid2FybmluZ3MiLCJoYXNFcnJvcnMiLCJsZW5ndGgiLCJoYXNXYXJuaW5ncyIsInQxIiwidDIiLCJ0MyIsInQ0IiwidDUiLCJTeW1ib2wiLCJmb3IiLCJ0NiIsInQ3IiwidDgiLCJtYXAiLCJfdGVtcCIsInQ5IiwiX3RlbXAyIiwidDEwIiwidDExIiwid2FybmluZyIsImlfMCIsInNlcnZlck5hbWVfMCIsIm1jcEVycm9yTWV0YWRhdGEiLCJzZXJ2ZXJOYW1lIiwiaSIsInBhdGgiLCJtZXNzYWdlIiwiZXJyb3IiLCJNY3BQYXJzaW5nV2FybmluZ3MiLCJjb25maWciLCJzY29wZXMiLCJBcnJheSIsImVycm9ycyIsImhhc1BhcnNpbmdFcnJvcnMiLCJzb21lIiwiX3RlbXAzIiwiX3RlbXA0IiwiX3RlbXA1IiwiY29uZmlnXzEiLCJmaWx0ZXJFcnJvcnMiLCJjb25maWdfMCIsInNldmVyaXR5IiwiZmlsdGVyIiwiZSJdLCJzb3VyY2VzIjpbIk1jcFBhcnNpbmdXYXJuaW5ncy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHVzZU1lbW8gfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGdldE1jcENvbmZpZ3NCeVNjb3BlIH0gZnJvbSAnc3JjL3NlcnZpY2VzL21jcC9jb25maWcuanMnXG5pbXBvcnQgdHlwZSB7IENvbmZpZ1Njb3BlIH0gZnJvbSAnc3JjL3NlcnZpY2VzL21jcC90eXBlcy5qcydcbmltcG9ydCB7XG4gIGRlc2NyaWJlTWNwQ29uZmlnRmlsZVBhdGgsXG4gIGdldFNjb3BlTGFiZWwsXG59IGZyb20gJ3NyYy9zZXJ2aWNlcy9tY3AvdXRpbHMuanMnXG5pbXBvcnQgdHlwZSB7IFZhbGlkYXRpb25FcnJvciB9IGZyb20gJ3NyYy91dGlscy9zZXR0aW5ncy92YWxpZGF0aW9uLmpzJ1xuaW1wb3J0IHsgQm94LCBMaW5rLCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuXG5mdW5jdGlvbiBNY3BDb25maWdFcnJvclNlY3Rpb24oe1xuICBzY29wZSxcbiAgcGFyc2luZ0Vycm9ycyxcbiAgd2FybmluZ3MsXG59OiB7XG4gIHNjb3BlOiBDb25maWdTY29wZVxuICBwYXJzaW5nRXJyb3JzOiBWYWxpZGF0aW9uRXJyb3JbXVxuICB3YXJuaW5nczogVmFsaWRhdGlvbkVycm9yW11cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBoYXNFcnJvcnMgPSBwYXJzaW5nRXJyb3JzLmxlbmd0aCA+IDBcbiAgY29uc3QgaGFzV2FybmluZ3MgPSB3YXJuaW5ncy5sZW5ndGggPiAwXG5cbiAgaWYgKCFoYXNFcnJvcnMgJiYgIWhhc1dhcm5pbmdzKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgIDxCb3g+XG4gICAgICAgIHsoaGFzRXJyb3JzIHx8IGhhc1dhcm5pbmdzKSAmJiAoXG4gICAgICAgICAgPFRleHQgY29sb3I9e2hhc0Vycm9ycyA/ICdlcnJvcicgOiAnd2FybmluZyd9PlxuICAgICAgICAgICAgW3toYXNFcnJvcnMgPyAnRmFpbGVkIHRvIHBhcnNlJyA6ICdDb250YWlucyB3YXJuaW5ncyd9XXsnICd9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApfVxuICAgICAgICA8VGV4dD57Z2V0U2NvcGVMYWJlbChzY29wZSl9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgICA8Qm94PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5Mb2NhdGlvbjogPC9UZXh0PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj57ZGVzY3JpYmVNY3BDb25maWdGaWxlUGF0aChzY29wZSl9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgICA8Qm94IG1hcmdpbkxlZnQ9ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAge3BhcnNpbmdFcnJvcnMubWFwKChlcnJvciwgaSkgPT4ge1xuICAgICAgICAgIGNvbnN0IHNlcnZlck5hbWUgPSBlcnJvci5tY3BFcnJvck1ldGFkYXRhPy5zZXJ2ZXJOYW1lXG4gICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIDxCb3gga2V5PXtgZXJyb3ItJHtpfWB9PlxuICAgICAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj7ilJQgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5bRXJyb3JdPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgeycgJ31cbiAgICAgICAgICAgICAgICAgIHtzZXJ2ZXJOYW1lICYmIGBbJHtzZXJ2ZXJOYW1lfV0gYH1cbiAgICAgICAgICAgICAgICAgIHtlcnJvci5wYXRoICYmIGVycm9yLnBhdGggIT09ICcnID8gYCR7ZXJyb3IucGF0aH06IGAgOiAnJ31cbiAgICAgICAgICAgICAgICAgIHtlcnJvci5tZXNzYWdlfVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKVxuICAgICAgICB9KX1cbiAgICAgICAge3dhcm5pbmdzLm1hcCgod2FybmluZywgaSkgPT4ge1xuICAgICAgICAgIGNvbnN0IHNlcnZlck5hbWUgPSB3YXJuaW5nLm1jcEVycm9yTWV0YWRhdGE/LnNlcnZlck5hbWVcblxuICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8Qm94IGtleT17YHdhcm5pbmctJHtpfWB9PlxuICAgICAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj7ilJQgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPltXYXJuaW5nXTwvVGV4dD5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICAgIHsnICd9XG4gICAgICAgICAgICAgICAgICB7c2VydmVyTmFtZSAmJiBgWyR7c2VydmVyTmFtZX1dIGB9XG4gICAgICAgICAgICAgICAgICB7d2FybmluZy5wYXRoICYmIHdhcm5pbmcucGF0aCAhPT0gJydcbiAgICAgICAgICAgICAgICAgICAgPyBgJHt3YXJuaW5nLnBhdGh9OiBgXG4gICAgICAgICAgICAgICAgICAgIDogJyd9XG4gICAgICAgICAgICAgICAgICB7d2FybmluZy5tZXNzYWdlfVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKVxuICAgICAgICB9KX1cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBNY3BQYXJzaW5nV2FybmluZ3MoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgLy8gQ29uZmlnIGZpbGVzIGRvbid0IGNoYW5nZSBkdXJpbmcgZGlhbG9nIGxpZmV0aW1lOyByZWFkIG9uY2Ugb24gbW91bnRcbiAgLy8gdG8gYXZvaWQgYmxvY2tpbmcgZmlsZSBJTyBvbiBldmVyeSByZS1yZW5kZXIuXG4gIGNvbnN0IHNjb3BlcyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIFtcbiAgICAgICAgeyBzY29wZTogJ3VzZXInLCBjb25maWc6IGdldE1jcENvbmZpZ3NCeVNjb3BlKCd1c2VyJykgfSxcbiAgICAgICAgeyBzY29wZTogJ3Byb2plY3QnLCBjb25maWc6IGdldE1jcENvbmZpZ3NCeVNjb3BlKCdwcm9qZWN0JykgfSxcbiAgICAgICAgeyBzY29wZTogJ2xvY2FsJywgY29uZmlnOiBnZXRNY3BDb25maWdzQnlTY29wZSgnbG9jYWwnKSB9LFxuICAgICAgICB7IHNjb3BlOiAnZW50ZXJwcmlzZScsIGNvbmZpZzogZ2V0TWNwQ29uZmlnc0J5U2NvcGUoJ2VudGVycHJpc2UnKSB9LFxuICAgICAgXSBzYXRpc2ZpZXMgQXJyYXk8e1xuICAgICAgICBzY29wZTogQ29uZmlnU2NvcGVcbiAgICAgICAgY29uZmlnOiB7IGVycm9yczogVmFsaWRhdGlvbkVycm9yW10gfVxuICAgICAgfT4sXG4gICAgW10sXG4gIClcblxuICBjb25zdCBoYXNQYXJzaW5nRXJyb3JzID0gc2NvcGVzLnNvbWUoXG4gICAgKHsgY29uZmlnIH0pID0+IGZpbHRlckVycm9ycyhjb25maWcuZXJyb3JzLCAnZmF0YWwnKS5sZW5ndGggPiAwLFxuICApXG4gIGNvbnN0IGhhc1dhcm5pbmdzID0gc2NvcGVzLnNvbWUoXG4gICAgKHsgY29uZmlnIH0pID0+IGZpbHRlckVycm9ycyhjb25maWcuZXJyb3JzLCAnd2FybmluZycpLmxlbmd0aCA+IDAsXG4gIClcblxuICBpZiAoIWhhc1BhcnNpbmdFcnJvcnMgJiYgIWhhc1dhcm5pbmdzKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfSBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgPFRleHQgYm9sZD5NQ1AgQ29uZmlnIERpYWdub3N0aWNzPC9UZXh0PlxuICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICBGb3IgaGVscCBjb25maWd1cmluZyBNQ1Agc2VydmVycywgc2VlOnsnICd9XG4gICAgICAgICAgPExpbmsgdXJsPVwiaHR0cHM6Ly9jb2RlLmNsYXVkZS5jb20vZG9jcy9lbi9tY3BcIj5cbiAgICAgICAgICAgIGh0dHBzOi8vY29kZS5jbGF1ZGUuY29tL2RvY3MvZW4vbWNwXG4gICAgICAgICAgPC9MaW5rPlxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICAgIHtzY29wZXMubWFwKCh7IHNjb3BlLCBjb25maWcgfSkgPT4gKFxuICAgICAgICA8TWNwQ29uZmlnRXJyb3JTZWN0aW9uXG4gICAgICAgICAga2V5PXtzY29wZX1cbiAgICAgICAgICBzY29wZT17c2NvcGV9XG4gICAgICAgICAgcGFyc2luZ0Vycm9ycz17ZmlsdGVyRXJyb3JzKGNvbmZpZy5lcnJvcnMsICdmYXRhbCcpfVxuICAgICAgICAgIHdhcm5pbmdzPXtmaWx0ZXJFcnJvcnMoY29uZmlnLmVycm9ycywgJ3dhcm5pbmcnKX1cbiAgICAgICAgLz5cbiAgICAgICkpfVxuICAgICAgey8qIFRPRE86IEFkZCBhZGRpdGlvbmFsIGRpYWdub3N0aWMgc2VjdGlvbnM6XG4gICAgICAgKiAtIER1cGxpY2F0ZSBTZXJ2ZXIgTmFtZXMgKGNoZWNrIGZvciBzZXJ2ZXJzIHdpdGggc2FtZSBuYW1lIGFjcm9zcyBzY29wZXMpXG4gICAgICAgKiBUaGlzIHNlY3Rpb24gc2hvdWxkIGluY2x1ZGU6XG4gICAgICAgKiAtIEZpbGUgcGF0aHMgd2hlcmUgZWFjaCBzZXJ2ZXIgaXMgZGVmaW5lZFxuICAgICAgICogLSBNb3JlIGRldGFpbGVkIGxvY2F0aW9uIGluZm8gZm9yIHVzZXIvbG9jYWwgc2NvcGVzXG4gICAgICAgKiAtIEFwcHJvdmVkIC8gZGlzYWJsZWQgc3RhdHVzIG9mIHNlcnZlcnNcbiAgICAgICAqL31cbiAgICA8L0JveD5cbiAgKVxufVxuXG5mdW5jdGlvbiBmaWx0ZXJFcnJvcnMoXG4gIGVycm9yczogVmFsaWRhdGlvbkVycm9yW10sXG4gIHNldmVyaXR5OiAnZmF0YWwnIHwgJ3dhcm5pbmcnLFxuKTogVmFsaWRhdGlvbkVycm9yW10ge1xuICByZXR1cm4gZXJyb3JzLmZpbHRlcihlID0+IGUubWNwRXJyb3JNZXRhZGF0YT8uc2V2ZXJpdHkgPT09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 \ No newline at end of file diff --git a/components/mcp/index.ts b/components/mcp/index.ts new file mode 100644 index 0000000..1cca323 --- /dev/null +++ b/components/mcp/index.ts @@ -0,0 +1,9 @@ +export { MCPAgentServerMenu } from './MCPAgentServerMenu.js' +export { MCPListPanel } from './MCPListPanel.js' +export { MCPReconnect } from './MCPReconnect.js' +export { MCPRemoteServerMenu } from './MCPRemoteServerMenu.js' +export { MCPSettings } from './MCPSettings.js' +export { MCPStdioServerMenu } from './MCPStdioServerMenu.js' +export { MCPToolDetailView } from './MCPToolDetailView.js' +export { MCPToolListView } from './MCPToolListView.js' +export type { AgentMcpServerInfo, MCPViewState, ServerInfo } from './types.js' diff --git a/components/mcp/utils/reconnectHelpers.tsx b/components/mcp/utils/reconnectHelpers.tsx new file mode 100644 index 0000000..f9931fb --- /dev/null +++ b/components/mcp/utils/reconnectHelpers.tsx @@ -0,0 +1,49 @@ +import type { Command } from '../../../commands.js'; +import type { MCPServerConnection, ServerResource } from '../../../services/mcp/types.js'; +import type { Tool } from '../../../Tool.js'; +export interface ReconnectResult { + message: string; + success: boolean; +} + +/** + * Handles the result of a reconnect attempt and returns an appropriate user message + */ +export function handleReconnectResult(result: { + client: MCPServerConnection; + tools: Tool[]; + commands: Command[]; + resources?: ServerResource[]; +}, serverName: string): ReconnectResult { + switch (result.client.type) { + case 'connected': + return { + message: `Reconnected to ${serverName}.`, + success: true + }; + case 'needs-auth': + return { + message: `${serverName} requires authentication. Use the 'Authenticate' option.`, + success: false + }; + case 'failed': + return { + message: `Failed to reconnect to ${serverName}.`, + success: false + }; + default: + return { + message: `Unknown result when reconnecting to ${serverName}.`, + success: false + }; + } +} + +/** + * Handles errors from reconnect attempts + */ +export function handleReconnectError(error: unknown, serverName: string): string { + const errorMessage = error instanceof Error ? error.message : String(error); + return `Error reconnecting to ${serverName}: ${errorMessage}`; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJDb21tYW5kIiwiTUNQU2VydmVyQ29ubmVjdGlvbiIsIlNlcnZlclJlc291cmNlIiwiVG9vbCIsIlJlY29ubmVjdFJlc3VsdCIsIm1lc3NhZ2UiLCJzdWNjZXNzIiwiaGFuZGxlUmVjb25uZWN0UmVzdWx0IiwicmVzdWx0IiwiY2xpZW50IiwidG9vbHMiLCJjb21tYW5kcyIsInJlc291cmNlcyIsInNlcnZlck5hbWUiLCJ0eXBlIiwiaGFuZGxlUmVjb25uZWN0RXJyb3IiLCJlcnJvciIsImVycm9yTWVzc2FnZSIsIkVycm9yIiwiU3RyaW5nIl0sInNvdXJjZXMiOlsicmVjb25uZWN0SGVscGVycy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBDb21tYW5kIH0gZnJvbSAnLi4vLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgdHlwZSB7XG4gIE1DUFNlcnZlckNvbm5lY3Rpb24sXG4gIFNlcnZlclJlc291cmNlLFxufSBmcm9tICcuLi8uLi8uLi9zZXJ2aWNlcy9tY3AvdHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2wgfSBmcm9tICcuLi8uLi8uLi9Ub29sLmpzJ1xuXG5leHBvcnQgaW50ZXJmYWNlIFJlY29ubmVjdFJlc3VsdCB7XG4gIG1lc3NhZ2U6IHN0cmluZ1xuICBzdWNjZXNzOiBib29sZWFuXG59XG5cbi8qKlxuICogSGFuZGxlcyB0aGUgcmVzdWx0IG9mIGEgcmVjb25uZWN0IGF0dGVtcHQgYW5kIHJldHVybnMgYW4gYXBwcm9wcmlhdGUgdXNlciBtZXNzYWdlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBoYW5kbGVSZWNvbm5lY3RSZXN1bHQoXG4gIHJlc3VsdDoge1xuICAgIGNsaWVudDogTUNQU2VydmVyQ29ubmVjdGlvblxuICAgIHRvb2xzOiBUb29sW11cbiAgICBjb21tYW5kczogQ29tbWFuZFtdXG4gICAgcmVzb3VyY2VzPzogU2VydmVyUmVzb3VyY2VbXVxuICB9LFxuICBzZXJ2ZXJOYW1lOiBzdHJpbmcsXG4pOiBSZWNvbm5lY3RSZXN1bHQge1xuICBzd2l0Y2ggKHJlc3VsdC5jbGllbnQudHlwZSkge1xuICAgIGNhc2UgJ2Nvbm5lY3RlZCc6XG4gICAgICByZXR1cm4ge1xuICAgICAgICBtZXNzYWdlOiBgUmVjb25uZWN0ZWQgdG8gJHtzZXJ2ZXJOYW1lfS5gLFxuICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgfVxuXG4gICAgY2FzZSAnbmVlZHMtYXV0aCc6XG4gICAgICByZXR1cm4ge1xuICAgICAgICBtZXNzYWdlOiBgJHtzZXJ2ZXJOYW1lfSByZXF1aXJlcyBhdXRoZW50aWNhdGlvbi4gVXNlIHRoZSAnQXV0aGVudGljYXRlJyBvcHRpb24uYCxcbiAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICB9XG5cbiAgICBjYXNlICdmYWlsZWQnOlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbWVzc2FnZTogYEZhaWxlZCB0byByZWNvbm5lY3QgdG8gJHtzZXJ2ZXJOYW1lfS5gLFxuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgIH1cblxuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4ge1xuICAgICAgICBtZXNzYWdlOiBgVW5rbm93biByZXN1bHQgd2hlbiByZWNvbm5lY3RpbmcgdG8gJHtzZXJ2ZXJOYW1lfS5gLFxuICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIEhhbmRsZXMgZXJyb3JzIGZyb20gcmVjb25uZWN0IGF0dGVtcHRzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBoYW5kbGVSZWNvbm5lY3RFcnJvcihcbiAgZXJyb3I6IHVua25vd24sXG4gIHNlcnZlck5hbWU6IHN0cmluZyxcbik6IHN0cmluZyB7XG4gIGNvbnN0IGVycm9yTWVzc2FnZSA9IGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogU3RyaW5nKGVycm9yKVxuICByZXR1cm4gYEVycm9yIHJlY29ubmVjdGluZyB0byAke3NlcnZlck5hbWV9OiAke2Vycm9yTWVzc2FnZX1gXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLGNBQWNBLE9BQU8sUUFBUSxzQkFBc0I7QUFDbkQsY0FDRUMsbUJBQW1CLEVBQ25CQyxjQUFjLFFBQ1QsZ0NBQWdDO0FBQ3ZDLGNBQWNDLElBQUksUUFBUSxrQkFBa0I7QUFFNUMsT0FBTyxVQUFVQyxlQUFlLENBQUM7RUFDL0JDLE9BQU8sRUFBRSxNQUFNO0VBQ2ZDLE9BQU8sRUFBRSxPQUFPO0FBQ2xCOztBQUVBO0FBQ0E7QUFDQTtBQUNBLE9BQU8sU0FBU0MscUJBQXFCQSxDQUNuQ0MsTUFBTSxFQUFFO0VBQ05DLE1BQU0sRUFBRVIsbUJBQW1CO0VBQzNCUyxLQUFLLEVBQUVQLElBQUksRUFBRTtFQUNiUSxRQUFRLEVBQUVYLE9BQU8sRUFBRTtFQUNuQlksU0FBUyxDQUFDLEVBQUVWLGNBQWMsRUFBRTtBQUM5QixDQUFDLEVBQ0RXLFVBQVUsRUFBRSxNQUFNLENBQ25CLEVBQUVULGVBQWUsQ0FBQztFQUNqQixRQUFRSSxNQUFNLENBQUNDLE1BQU0sQ0FBQ0ssSUFBSTtJQUN4QixLQUFLLFdBQVc7TUFDZCxPQUFPO1FBQ0xULE9BQU8sRUFBRSxrQkFBa0JRLFVBQVUsR0FBRztRQUN4Q1AsT0FBTyxFQUFFO01BQ1gsQ0FBQztJQUVILEtBQUssWUFBWTtNQUNmLE9BQU87UUFDTEQsT0FBTyxFQUFFLEdBQUdRLFVBQVUsMERBQTBEO1FBQ2hGUCxPQUFPLEVBQUU7TUFDWCxDQUFDO0lBRUgsS0FBSyxRQUFRO01BQ1gsT0FBTztRQUNMRCxPQUFPLEVBQUUsMEJBQTBCUSxVQUFVLEdBQUc7UUFDaERQLE9BQU8sRUFBRTtNQUNYLENBQUM7SUFFSDtNQUNFLE9BQU87UUFDTEQsT0FBTyxFQUFFLHVDQUF1Q1EsVUFBVSxHQUFHO1FBQzdEUCxPQUFPLEVBQUU7TUFDWCxDQUFDO0VBQ0w7QUFDRjs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQVNTLG9CQUFvQkEsQ0FDbENDLEtBQUssRUFBRSxPQUFPLEVBQ2RILFVBQVUsRUFBRSxNQUFNLENBQ25CLEVBQUUsTUFBTSxDQUFDO0VBQ1IsTUFBTUksWUFBWSxHQUFHRCxLQUFLLFlBQVlFLEtBQUssR0FBR0YsS0FBSyxDQUFDWCxPQUFPLEdBQUdjLE1BQU0sQ0FBQ0gsS0FBSyxDQUFDO0VBQzNFLE9BQU8seUJBQXlCSCxVQUFVLEtBQUtJLFlBQVksRUFBRTtBQUMvRCIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/memory/MemoryFileSelector.tsx b/components/memory/MemoryFileSelector.tsx new file mode 100644 index 0000000..0c207ef --- /dev/null +++ b/components/memory/MemoryFileSelector.tsx @@ -0,0 +1,438 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import chalk from 'chalk'; +import { mkdir } from 'fs/promises'; +import { join } from 'path'; +import * as React from 'react'; +import { use, useEffect, useState } from 'react'; +import { getOriginalCwd } from '../../bootstrap/state.js'; +import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { getAutoMemPath, isAutoMemoryEnabled } from '../../memdir/paths.js'; +import { logEvent } from '../../services/analytics/index.js'; +import { isAutoDreamEnabled } from '../../services/autoDream/config.js'; +import { readLastConsolidatedAt } from '../../services/autoDream/consolidationLock.js'; +import { useAppState } from '../../state/AppState.js'; +import { getAgentMemoryDir } from '../../tools/AgentTool/agentMemory.js'; +import { openPath } from '../../utils/browser.js'; +import { getMemoryFiles, type MemoryFileInfo } from '../../utils/claudemd.js'; +import { getClaudeConfigHomeDir } from '../../utils/envUtils.js'; +import { getDisplayPath } from '../../utils/file.js'; +import { formatRelativeTimeAgo } from '../../utils/format.js'; +import { projectIsInGitRepo } from '../../utils/memory/versions.js'; +import { updateSettingsForSource } from '../../utils/settings/settings.js'; +import { Select } from '../CustomSelect/index.js'; +import { ListItem } from '../design-system/ListItem.js'; + +/* eslint-disable @typescript-eslint/no-require-imports */ +const teamMemPaths = feature('TEAMMEM') ? require('../../memdir/teamMemPaths.js') as typeof import('../../memdir/teamMemPaths.js') : null; +/* eslint-enable @typescript-eslint/no-require-imports */ + +interface ExtendedMemoryFileInfo extends MemoryFileInfo { + isNested?: boolean; + exists: boolean; +} + +// Remember last selected path +let lastSelectedPath: string | undefined; +const OPEN_FOLDER_PREFIX = '__open_folder__'; +type Props = { + onSelect: (path: string) => void; + onCancel: () => void; +}; +export function MemoryFileSelector(t0) { + const $ = _c(58); + const { + onSelect, + onCancel + } = t0; + const existingMemoryFiles = use(getMemoryFiles()); + const userMemoryPath = join(getClaudeConfigHomeDir(), "CLAUDE.md"); + const projectMemoryPath = join(getOriginalCwd(), "CLAUDE.md"); + const hasUserMemory = existingMemoryFiles.some(f => f.path === userMemoryPath); + const hasProjectMemory = existingMemoryFiles.some(f_0 => f_0.path === projectMemoryPath); + const allMemoryFiles = [...existingMemoryFiles.filter(_temp).map(_temp2), ...(hasUserMemory ? [] : [{ + path: userMemoryPath, + type: "User" as const, + content: "", + exists: false + }]), ...(hasProjectMemory ? [] : [{ + path: projectMemoryPath, + type: "Project" as const, + content: "", + exists: false + }])]; + const depths = new Map(); + const memoryOptions = allMemoryFiles.map(file => { + const displayPath = getDisplayPath(file.path); + const existsLabel = file.exists ? "" : " (new)"; + const depth = file.parent ? (depths.get(file.parent) ?? 0) + 1 : 0; + depths.set(file.path, depth); + const indent = depth > 0 ? " ".repeat(depth - 1) : ""; + let label; + if (file.type === "User" && !file.isNested && file.path === userMemoryPath) { + label = "User memory"; + } else { + if (file.type === "Project" && !file.isNested && file.path === projectMemoryPath) { + label = "Project memory"; + } else { + if (depth > 0) { + label = `${indent}L ${displayPath}${existsLabel}`; + } else { + label = `${displayPath}`; + } + } + } + let description; + const isGit = projectIsInGitRepo(getOriginalCwd()); + if (file.type === "User" && !file.isNested) { + description = "Saved in ~/.claude/CLAUDE.md"; + } else { + if (file.type === "Project" && !file.isNested && file.path === projectMemoryPath) { + description = `${isGit ? "Checked in at" : "Saved in"} ./CLAUDE.md`; + } else { + if (file.parent) { + description = "@-imported"; + } else { + if (file.isNested) { + description = "dynamically loaded"; + } else { + description = ""; + } + } + } + } + return { + label, + value: file.path, + description + }; + }); + const folderOptions = []; + const agentDefinitions = useAppState(_temp3); + if (isAutoMemoryEnabled()) { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + label: "Open auto-memory folder", + value: `${OPEN_FOLDER_PREFIX}${getAutoMemPath()}`, + description: "" + }; + $[0] = t1; + } else { + t1 = $[0]; + } + folderOptions.push(t1); + if (feature("TEAMMEM") && teamMemPaths.isTeamMemoryEnabled()) { + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = { + label: "Open team memory folder", + value: `${OPEN_FOLDER_PREFIX}${teamMemPaths.getTeamMemPath()}`, + description: "" + }; + $[1] = t2; + } else { + t2 = $[1]; + } + folderOptions.push(t2); + } + for (const agent of agentDefinitions.activeAgents) { + if (agent.memory) { + const agentDir = getAgentMemoryDir(agent.agentType, agent.memory); + folderOptions.push({ + label: `Open ${chalk.bold(agent.agentType)} agent memory`, + value: `${OPEN_FOLDER_PREFIX}${agentDir}`, + description: `${agent.memory} scope` + }); + } + } + } + memoryOptions.push(...folderOptions); + let t1; + if ($[2] !== memoryOptions) { + t1 = lastSelectedPath && memoryOptions.some(_temp4) ? lastSelectedPath : memoryOptions[0]?.value || ""; + $[2] = memoryOptions; + $[3] = t1; + } else { + t1 = $[3]; + } + const initialPath = t1; + const [autoMemoryOn, setAutoMemoryOn] = useState(isAutoMemoryEnabled); + const [autoDreamOn, setAutoDreamOn] = useState(isAutoDreamEnabled); + const [showDreamRow] = useState(isAutoMemoryEnabled); + const isDreamRunning = useAppState(_temp6); + const [lastDreamAt, setLastDreamAt] = useState(null); + let t2; + if ($[4] !== showDreamRow) { + t2 = () => { + if (!showDreamRow) { + return; + } + readLastConsolidatedAt().then(setLastDreamAt); + }; + $[4] = showDreamRow; + $[5] = t2; + } else { + t2 = $[5]; + } + let t3; + if ($[6] !== isDreamRunning || $[7] !== showDreamRow) { + t3 = [showDreamRow, isDreamRunning]; + $[6] = isDreamRunning; + $[7] = showDreamRow; + $[8] = t3; + } else { + t3 = $[8]; + } + useEffect(t2, t3); + let t4; + if ($[9] !== isDreamRunning || $[10] !== lastDreamAt) { + t4 = isDreamRunning ? "running" : lastDreamAt === null ? "" : lastDreamAt === 0 ? "never" : `last ran ${formatRelativeTimeAgo(new Date(lastDreamAt))}`; + $[9] = isDreamRunning; + $[10] = lastDreamAt; + $[11] = t4; + } else { + t4 = $[11]; + } + const dreamStatus = t4; + const [focusedToggle, setFocusedToggle] = useState(null); + const toggleFocused = focusedToggle !== null; + const lastToggleIndex = showDreamRow ? 1 : 0; + let t5; + if ($[12] !== autoMemoryOn) { + t5 = function handleToggleAutoMemory() { + const newValue = !autoMemoryOn; + updateSettingsForSource("userSettings", { + autoMemoryEnabled: newValue + }); + setAutoMemoryOn(newValue); + logEvent("tengu_auto_memory_toggled", { + enabled: newValue + }); + }; + $[12] = autoMemoryOn; + $[13] = t5; + } else { + t5 = $[13]; + } + const handleToggleAutoMemory = t5; + let t6; + if ($[14] !== autoDreamOn) { + t6 = function handleToggleAutoDream() { + const newValue_0 = !autoDreamOn; + updateSettingsForSource("userSettings", { + autoDreamEnabled: newValue_0 + }); + setAutoDreamOn(newValue_0); + logEvent("tengu_auto_dream_toggled", { + enabled: newValue_0 + }); + }; + $[14] = autoDreamOn; + $[15] = t6; + } else { + t6 = $[15]; + } + const handleToggleAutoDream = t6; + useExitOnCtrlCDWithKeybindings(); + let t7; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t7 = { + context: "Confirmation" + }; + $[16] = t7; + } else { + t7 = $[16]; + } + useKeybinding("confirm:no", onCancel, t7); + let t8; + if ($[17] !== focusedToggle || $[18] !== handleToggleAutoDream || $[19] !== handleToggleAutoMemory) { + t8 = () => { + if (focusedToggle === 0) { + handleToggleAutoMemory(); + } else { + if (focusedToggle === 1) { + handleToggleAutoDream(); + } + } + }; + $[17] = focusedToggle; + $[18] = handleToggleAutoDream; + $[19] = handleToggleAutoMemory; + $[20] = t8; + } else { + t8 = $[20]; + } + let t9; + if ($[21] !== toggleFocused) { + t9 = { + context: "Confirmation", + isActive: toggleFocused + }; + $[21] = toggleFocused; + $[22] = t9; + } else { + t9 = $[22]; + } + useKeybinding("confirm:yes", t8, t9); + let t10; + if ($[23] !== lastToggleIndex) { + t10 = () => { + setFocusedToggle(prev => prev !== null && prev < lastToggleIndex ? prev + 1 : null); + }; + $[23] = lastToggleIndex; + $[24] = t10; + } else { + t10 = $[24]; + } + let t11; + if ($[25] !== toggleFocused) { + t11 = { + context: "Select", + isActive: toggleFocused + }; + $[25] = toggleFocused; + $[26] = t11; + } else { + t11 = $[26]; + } + useKeybinding("select:next", t10, t11); + let t12; + if ($[27] === Symbol.for("react.memo_cache_sentinel")) { + t12 = () => { + setFocusedToggle(_temp7); + }; + $[27] = t12; + } else { + t12 = $[27]; + } + let t13; + if ($[28] !== toggleFocused) { + t13 = { + context: "Select", + isActive: toggleFocused + }; + $[28] = toggleFocused; + $[29] = t13; + } else { + t13 = $[29]; + } + useKeybinding("select:previous", t12, t13); + const t14 = focusedToggle === 0; + const t15 = autoMemoryOn ? "on" : "off"; + let t16; + if ($[30] !== t15) { + t16 = Auto-memory: {t15}; + $[30] = t15; + $[31] = t16; + } else { + t16 = $[31]; + } + let t17; + if ($[32] !== t14 || $[33] !== t16) { + t17 = {t16}; + $[32] = t14; + $[33] = t16; + $[34] = t17; + } else { + t17 = $[34]; + } + let t18; + if ($[35] !== autoDreamOn || $[36] !== dreamStatus || $[37] !== focusedToggle || $[38] !== isDreamRunning || $[39] !== showDreamRow) { + t18 = showDreamRow && Auto-dream: {autoDreamOn ? "on" : "off"}{dreamStatus && · {dreamStatus}}{!isDreamRunning && autoDreamOn && · /dream to run}; + $[35] = autoDreamOn; + $[36] = dreamStatus; + $[37] = focusedToggle; + $[38] = isDreamRunning; + $[39] = showDreamRow; + $[40] = t18; + } else { + t18 = $[40]; + } + let t19; + if ($[41] !== t17 || $[42] !== t18) { + t19 = {t17}{t18}; + $[41] = t17; + $[42] = t18; + $[43] = t19; + } else { + t19 = $[43]; + } + let t20; + if ($[44] !== onSelect) { + t20 = value => { + if (value.startsWith(OPEN_FOLDER_PREFIX)) { + const folderPath = value.slice(OPEN_FOLDER_PREFIX.length); + mkdir(folderPath, { + recursive: true + }).catch(_temp8).then(() => openPath(folderPath)); + return; + } + lastSelectedPath = value; + onSelect(value); + }; + $[44] = onSelect; + $[45] = t20; + } else { + t20 = $[45]; + } + let t21; + if ($[46] !== lastToggleIndex) { + t21 = () => setFocusedToggle(lastToggleIndex); + $[46] = lastToggleIndex; + $[47] = t21; + } else { + t21 = $[47]; + } + let t22; + if ($[48] !== initialPath || $[49] !== memoryOptions || $[50] !== onCancel || $[51] !== t20 || $[52] !== t21 || $[53] !== toggleFocused) { + t22 = { + onUpdateQuestionState(questionText, { + selectedValue: value_1 + }, false); + const textInput_0 = value_1 === "__other__" ? questionStates[questionText]?.textInputValue : undefined; + onAnswer(questionText, value_1, textInput_0); + }} onFocus={handleFocus} onCancel={onCancel} onDownFromLastItem={handleDownFromLastItem} isDisabled={isFooterFocused} layout="compact-vertical" onOpenEditor={handleOpenEditor} onImagePaste={onImagePaste} pastedContents={pastedContents} onRemoveImage={onRemoveImage} />}; + $[58] = currentQuestionIndex; + $[59] = handleFocus; + $[60] = handleOpenEditor; + $[61] = isFooterFocused; + $[62] = onAnswer; + $[63] = onCancel; + $[64] = onImagePaste; + $[65] = onRemoveImage; + $[66] = onSubmit; + $[67] = onUpdateQuestionState; + $[68] = options; + $[69] = pastedContents; + $[70] = question.multiSelect; + $[71] = question.question; + $[72] = questionStates; + $[73] = questionText; + $[74] = questions.length; + $[75] = t12; + } else { + t12 = $[75]; + } + let t13; + if ($[76] === Symbol.for("react.memo_cache_sentinel")) { + t13 = ; + $[76] = t13; + } else { + t13 = $[76]; + } + let t14; + if ($[77] !== footerIndex || $[78] !== isFooterFocused) { + t14 = isFooterFocused && footerIndex === 0 ? {figures.pointer} : ; + $[77] = footerIndex; + $[78] = isFooterFocused; + $[79] = t14; + } else { + t14 = $[79]; + } + const t15 = isFooterFocused && footerIndex === 0 ? "suggestion" : undefined; + const t16 = options.length + 1; + let t17; + if ($[80] !== t15 || $[81] !== t16) { + t17 = {t16}. Chat about this; + $[80] = t15; + $[81] = t16; + $[82] = t17; + } else { + t17 = $[82]; + } + let t18; + if ($[83] !== t14 || $[84] !== t17) { + t18 = {t14}{t17}; + $[83] = t14; + $[84] = t17; + $[85] = t18; + } else { + t18 = $[85]; + } + let t19; + if ($[86] !== footerIndex || $[87] !== isFooterFocused || $[88] !== isInPlanMode || $[89] !== options.length) { + t19 = isInPlanMode && {isFooterFocused && footerIndex === 1 ? {figures.pointer} : }{options.length + 2}. Skip interview and plan immediately; + $[86] = footerIndex; + $[87] = isFooterFocused; + $[88] = isInPlanMode; + $[89] = options.length; + $[90] = t19; + } else { + t19 = $[90]; + } + let t20; + if ($[91] !== t18 || $[92] !== t19) { + t20 = {t13}{t18}{t19}; + $[91] = t18; + $[92] = t19; + $[93] = t20; + } else { + t20 = $[93]; + } + let t21; + if ($[94] !== questions.length) { + t21 = questions.length === 1 ? <>{figures.arrowUp}/{figures.arrowDown} to navigate : "Tab/Arrow keys to navigate"; + $[94] = questions.length; + $[95] = t21; + } else { + t21 = $[95]; + } + let t22; + if ($[96] !== isOtherFocused) { + t22 = isOtherFocused && editorName && <> · ctrl+g to edit in {editorName}; + $[96] = isOtherFocused; + $[97] = t22; + } else { + t22 = $[97]; + } + let t23; + if ($[98] !== t21 || $[99] !== t22) { + t23 = Enter to select ·{" "}{t21}{t22}{" "}· Esc to cancel; + $[98] = t21; + $[99] = t22; + $[100] = t23; + } else { + t23 = $[100]; + } + let t24; + if ($[101] !== minContentHeight || $[102] !== t12 || $[103] !== t20 || $[104] !== t23) { + t24 = {t12}{t20}{t23}; + $[101] = minContentHeight; + $[102] = t12; + $[103] = t20; + $[104] = t23; + $[105] = t24; + } else { + t24 = $[105]; + } + let t25; + if ($[106] !== t10 || $[107] !== t11 || $[108] !== t24) { + t25 = {t10}{t11}{t24}; + $[106] = t10; + $[107] = t11; + $[108] = t24; + $[109] = t25; + } else { + t25 = $[109]; + } + let t26; + if ($[110] !== handleKeyDown || $[111] !== t25 || $[112] !== t8) { + t26 = {t8}{t9}{t25}; + $[110] = handleKeyDown; + $[111] = t25; + $[112] = t8; + $[113] = t26; + } else { + t26 = $[113]; + } + return t26; +} +function _temp4(v) { + return v !== "__other__"; +} +function _temp3(opt_0) { + return opt_0.preview; +} +function _temp2(opt) { + return { + type: "text" as const, + value: opt.label, + label: opt.label, + description: opt.description + }; +} +function _temp(s) { + return s.toolPermissionContext.mode; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZVN0YXRlIiwiS2V5Ym9hcmRFdmVudCIsIkJveCIsIlRleHQiLCJ1c2VBcHBTdGF0ZSIsIlF1ZXN0aW9uIiwiUXVlc3Rpb25PcHRpb24iLCJQYXN0ZWRDb250ZW50IiwiZ2V0RXh0ZXJuYWxFZGl0b3IiLCJ0b0lERURpc3BsYXlOYW1lIiwiSW1hZ2VEaW1lbnNpb25zIiwiZWRpdFByb21wdEluRWRpdG9yIiwiT3B0aW9uV2l0aERlc2NyaXB0aW9uIiwiU2VsZWN0IiwiU2VsZWN0TXVsdGkiLCJEaXZpZGVyIiwiRmlsZVBhdGhMaW5rIiwiUGVybWlzc2lvblJlcXVlc3RUaXRsZSIsIlByZXZpZXdRdWVzdGlvblZpZXciLCJRdWVzdGlvbk5hdmlnYXRpb25CYXIiLCJRdWVzdGlvblN0YXRlIiwiUHJvcHMiLCJxdWVzdGlvbiIsInF1ZXN0aW9ucyIsImN1cnJlbnRRdWVzdGlvbkluZGV4IiwiYW5zd2VycyIsIlJlY29yZCIsInF1ZXN0aW9uU3RhdGVzIiwiaGlkZVN1Ym1pdFRhYiIsInBsYW5GaWxlUGF0aCIsInBhc3RlZENvbnRlbnRzIiwibWluQ29udGVudEhlaWdodCIsIm1pbkNvbnRlbnRXaWR0aCIsIm9uVXBkYXRlUXVlc3Rpb25TdGF0ZSIsInF1ZXN0aW9uVGV4dCIsInVwZGF0ZXMiLCJQYXJ0aWFsIiwiaXNNdWx0aVNlbGVjdCIsIm9uQW5zd2VyIiwibGFiZWwiLCJ0ZXh0SW5wdXQiLCJzaG91bGRBZHZhbmNlIiwib25UZXh0SW5wdXRGb2N1cyIsImlzSW5JbnB1dCIsIm9uQ2FuY2VsIiwib25TdWJtaXQiLCJvblRhYlByZXYiLCJvblRhYk5leHQiLCJvblJlc3BvbmRUb0NsYXVkZSIsIm9uRmluaXNoUGxhbkludGVydmlldyIsIm9uSW1hZ2VQYXN0ZSIsImJhc2U2NEltYWdlIiwibWVkaWFUeXBlIiwiZmlsZW5hbWUiLCJkaW1lbnNpb25zIiwic291cmNlUGF0aCIsIm9uUmVtb3ZlSW1hZ2UiLCJpZCIsIlF1ZXN0aW9uVmlldyIsInQwIiwiJCIsIl9jIiwidDEiLCJ1bmRlZmluZWQiLCJpc0luUGxhbk1vZGUiLCJfdGVtcCIsImlzRm9vdGVyRm9jdXNlZCIsInNldElzRm9vdGVyRm9jdXNlZCIsImZvb3RlckluZGV4Iiwic2V0Rm9vdGVySW5kZXgiLCJpc090aGVyRm9jdXNlZCIsInNldElzT3RoZXJGb2N1c2VkIiwidDIiLCJTeW1ib2wiLCJmb3IiLCJlZGl0b3IiLCJlZGl0b3JOYW1lIiwidDMiLCJ2YWx1ZSIsImlzT3RoZXIiLCJoYW5kbGVGb2N1cyIsInQ0IiwiaGFuZGxlRG93bkZyb21MYXN0SXRlbSIsInQ1IiwiaGFuZGxlVXBGcm9tRm9vdGVyIiwidDYiLCJlIiwia2V5IiwiY3RybCIsInByZXZlbnREZWZhdWx0IiwiaGFuZGxlS2V5RG93biIsImhhbmRsZU9wZW5FZGl0b3IiLCJ0NyIsInRleHRPcHRpb25zIiwib3B0aW9ucyIsIm1hcCIsIl90ZW1wMiIsInF1ZXN0aW9uU3RhdGUiLCJ0OCIsIm11bHRpU2VsZWN0IiwiY3VycmVudFZhbHVlIiwic2V0VmFsdWUiLCJyZXN1bHQiLCJjb250ZW50IiwidGV4dElucHV0VmFsdWUiLCJ0OSIsInQxMCIsInQxMSIsInZhbHVlXzAiLCJ0MTIiLCJ0eXBlIiwiY29uc3QiLCJwbGFjZWhvbGRlciIsImluaXRpYWxWYWx1ZSIsIm9uQ2hhbmdlIiwib3RoZXJPcHRpb24iLCJoYXNBbnlQcmV2aWV3Iiwic29tZSIsIl90ZW1wMyIsImxlbmd0aCIsInNlbGVjdGVkVmFsdWUiLCJ2YWx1ZXMiLCJpbmNsdWRlcyIsImZpbmFsVmFsdWVzIiwiZmlsdGVyIiwiX3RlbXA0IiwiY29uY2F0IiwidmFsdWVfMSIsInRleHRJbnB1dF8wIiwidDEzIiwidDE0IiwicG9pbnRlciIsInQxNSIsInQxNiIsInQxNyIsInQxOCIsInQxOSIsInQyMCIsInQyMSIsImFycm93VXAiLCJhcnJvd0Rvd24iLCJ0MjIiLCJ0MjMiLCJ0MjQiLCJ0MjUiLCJ0MjYiLCJ2Iiwib3B0XzAiLCJvcHQiLCJwcmV2aWV3IiwiZGVzY3JpcHRpb24iLCJzIiwidG9vbFBlcm1pc3Npb25Db250ZXh0IiwibW9kZSJdLCJzb3VyY2VzIjpbIlF1ZXN0aW9uVmlldy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCBSZWFjdCwgeyB1c2VDYWxsYmFjaywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uLy4uLy4uL2luay9ldmVudHMva2V5Ym9hcmQtZXZlbnQuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSB9IGZyb20gJy4uLy4uLy4uL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBRdWVzdGlvbixcbiAgUXVlc3Rpb25PcHRpb24sXG59IGZyb20gJy4uLy4uLy4uL3Rvb2xzL0Fza1VzZXJRdWVzdGlvblRvb2wvQXNrVXNlclF1ZXN0aW9uVG9vbC5qcydcbmltcG9ydCB0eXBlIHsgUGFzdGVkQ29udGVudCB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGdldEV4dGVybmFsRWRpdG9yIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvZWRpdG9yLmpzJ1xuaW1wb3J0IHsgdG9JREVEaXNwbGF5TmFtZSB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL2lkZS5qcydcbmltcG9ydCB0eXBlIHsgSW1hZ2VEaW1lbnNpb25zIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvaW1hZ2VSZXNpemVyLmpzJ1xuaW1wb3J0IHsgZWRpdFByb21wdEluRWRpdG9yIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcHJvbXB0RWRpdG9yLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBPcHRpb25XaXRoRGVzY3JpcHRpb24sXG4gIFNlbGVjdCxcbiAgU2VsZWN0TXVsdGksXG59IGZyb20gJy4uLy4uL0N1c3RvbVNlbGVjdC9pbmRleC5qcydcbmltcG9ydCB7IERpdmlkZXIgfSBmcm9tICcuLi8uLi9kZXNpZ24tc3lzdGVtL0RpdmlkZXIuanMnXG5pbXBvcnQgeyBGaWxlUGF0aExpbmsgfSBmcm9tICcuLi8uLi9GaWxlUGF0aExpbmsuanMnXG5pbXBvcnQgeyBQZXJtaXNzaW9uUmVxdWVzdFRpdGxlIH0gZnJvbSAnLi4vUGVybWlzc2lvblJlcXVlc3RUaXRsZS5qcydcbmltcG9ydCB7IFByZXZpZXdRdWVzdGlvblZpZXcgfSBmcm9tICcuL1ByZXZpZXdRdWVzdGlvblZpZXcuanMnXG5pbXBvcnQgeyBRdWVzdGlvbk5hdmlnYXRpb25CYXIgfSBmcm9tICcuL1F1ZXN0aW9uTmF2aWdhdGlvbkJhci5qcydcbmltcG9ydCB0eXBlIHsgUXVlc3Rpb25TdGF0ZSB9IGZyb20gJy4vdXNlLW11bHRpcGxlLWNob2ljZS1zdGF0ZS5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgcXVlc3Rpb246IFF1ZXN0aW9uXG4gIHF1ZXN0aW9uczogUXVlc3Rpb25bXVxuICBjdXJyZW50UXVlc3Rpb25JbmRleDogbnVtYmVyXG4gIGFuc3dlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz5cbiAgcXVlc3Rpb25TdGF0ZXM6IFJlY29yZDxzdHJpbmcsIFF1ZXN0aW9uU3RhdGU+XG4gIGhpZGVTdWJtaXRUYWI/OiBib29sZWFuXG4gIHBsYW5GaWxlUGF0aD86IHN0cmluZ1xuICBwYXN0ZWRDb250ZW50cz86IFJlY29yZDxudW1iZXIsIFBhc3RlZENvbnRlbnQ+XG4gIG1pbkNvbnRlbnRIZWlnaHQ/OiBudW1iZXJcbiAgbWluQ29udGVudFdpZHRoPzogbnVtYmVyXG4gIG9uVXBkYXRlUXVlc3Rpb25TdGF0ZTogKFxuICAgIHF1ZXN0aW9uVGV4dDogc3RyaW5nLFxuICAgIHVwZGF0ZXM6IFBhcnRpYWw8UXVlc3Rpb25TdGF0ZT4sXG4gICAgaXNNdWx0aVNlbGVjdDogYm9vbGVhbixcbiAgKSA9PiB2b2lkXG4gIG9uQW5zd2VyOiAoXG4gICAgcXVlc3Rpb25UZXh0OiBzdHJpbmcsXG4gICAgbGFiZWw6IHN0cmluZyB8IHN0cmluZ1tdLFxuICAgIHRleHRJbnB1dD86IHN0cmluZyxcbiAgICBzaG91bGRBZHZhbmNlPzogYm9vbGVhbixcbiAgKSA9PiB2b2lkXG4gIG9uVGV4dElucHV0Rm9jdXM6IChpc0luSW5wdXQ6IGJvb2xlYW4pID0+IHZvaWRcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbiAgb25TdWJtaXQ6ICgpID0+IHZvaWRcbiAgb25UYWJQcmV2PzogKCkgPT4gdm9pZFxuICBvblRhYk5leHQ/OiAoKSA9PiB2b2lkXG4gIG9uUmVzcG9uZFRvQ2xhdWRlOiAoKSA9PiB2b2lkXG4gIG9uRmluaXNoUGxhbkludGVydmlldzogKCkgPT4gdm9pZFxuICBvbkltYWdlUGFzdGU/OiAoXG4gICAgYmFzZTY0SW1hZ2U6IHN0cmluZyxcbiAgICBtZWRpYVR5cGU/OiBzdHJpbmcsXG4gICAgZmlsZW5hbWU/OiBzdHJpbmcsXG4gICAgZGltZW5zaW9ucz86IEltYWdlRGltZW5zaW9ucyxcbiAgICBzb3VyY2VQYXRoPzogc3RyaW5nLFxuICApID0+IHZvaWRcbiAgb25SZW1vdmVJbWFnZT86IChpZDogbnVtYmVyKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBRdWVzdGlvblZpZXcoe1xuICBxdWVzdGlvbixcbiAgcXVlc3Rpb25zLFxuICBjdXJyZW50UXVlc3Rpb25JbmRleCxcbiAgYW5zd2VycyxcbiAgcXVlc3Rpb25TdGF0ZXMsXG4gIGhpZGVTdWJtaXRUYWIgPSBmYWxzZSxcbiAgcGxhbkZpbGVQYXRoLFxuICBtaW5Db250ZW50SGVpZ2h0LFxuICBtaW5Db250ZW50V2lkdGgsXG4gIG9uVXBkYXRlUXVlc3Rpb25TdGF0ZSxcbiAgb25BbnN3ZXIsXG4gIG9uVGV4dElucHV0Rm9jdXMsXG4gIG9uQ2FuY2VsLFxuICBvblN1Ym1pdCxcbiAgb25UYWJQcmV2LFxuICBvblRhYk5leHQsXG4gIG9uUmVzcG9uZFRvQ2xhdWRlLFxuICBvbkZpbmlzaFBsYW5JbnRlcnZpZXcsXG4gIG9uSW1hZ2VQYXN0ZSxcbiAgcGFzdGVkQ29udGVudHMsXG4gIG9uUmVtb3ZlSW1hZ2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGlzSW5QbGFuTW9kZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy50b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSkgPT09ICdwbGFuJ1xuICBjb25zdCBbaXNGb290ZXJGb2N1c2VkLCBzZXRJc0Zvb3RlckZvY3VzZWRdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtmb290ZXJJbmRleCwgc2V0Rm9vdGVySW5kZXhdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgW2lzT3RoZXJGb2N1c2VkLCBzZXRJc090aGVyRm9jdXNlZF0gPSB1c2VTdGF0ZShmYWxzZSlcblxuICBjb25zdCBlZGl0b3IgPSBnZXRFeHRlcm5hbEVkaXRvcigpXG4gIGNvbnN0IGVkaXRvck5hbWUgPSBlZGl0b3IgPyB0b0lERURpc3BsYXlOYW1lKGVkaXRvcikgOiBudWxsXG5cbiAgY29uc3QgaGFuZGxlRm9jdXMgPSB1c2VDYWxsYmFjayhcbiAgICAodmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgY29uc3QgaXNPdGhlciA9IHZhbHVlID09PSAnX19vdGhlcl9fJ1xuICAgICAgc2V0SXNPdGhlckZvY3VzZWQoaXNPdGhlcilcbiAgICAgIG9uVGV4dElucHV0Rm9jdXMoaXNPdGhlcilcbiAgICB9LFxuICAgIFtvblRleHRJbnB1dEZvY3VzXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZURvd25Gcm9tTGFzdEl0ZW0gPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgc2V0SXNGb290ZXJGb2N1c2VkKHRydWUpXG4gIH0sIFtdKVxuXG4gIGNvbnN0IGhhbmRsZVVwRnJvbUZvb3RlciA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRJc0Zvb3RlckZvY3VzZWQoZmFsc2UpXG4gIH0sIFtdKVxuXG4gIC8vIEhhbmRsZSBrZXlib2FyZCBpbnB1dCB3aGVuIGZvb3RlciBpcyBmb2N1c2VkXG4gIGNvbnN0IGhhbmRsZUtleURvd24gPSB1c2VDYWxsYmFjayhcbiAgICAoZTogS2V5Ym9hcmRFdmVudCkgPT4ge1xuICAgICAgaWYgKCFpc0Zvb3RlckZvY3VzZWQpIHJldHVyblxuXG4gICAgICBpZiAoZS5rZXkgPT09ICd1cCcgfHwgKGUuY3RybCAmJiBlLmtleSA9PT0gJ3AnKSkge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgaWYgKGZvb3RlckluZGV4ID09PSAwKSB7XG4gICAgICAgICAgaGFuZGxlVXBGcm9tRm9vdGVyKClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzZXRGb290ZXJJbmRleCgwKVxuICAgICAgICB9XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICBpZiAoZS5rZXkgPT09ICdkb3duJyB8fCAoZS5jdHJsICYmIGUua2V5ID09PSAnbicpKSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgICBpZiAoaXNJblBsYW5Nb2RlICYmIGZvb3RlckluZGV4ID09PSAwKSB7XG4gICAgICAgICAgc2V0Rm9vdGVySW5kZXgoMSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgaWYgKGUua2V5ID09PSAncmV0dXJuJykge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgaWYgKGZvb3RlckluZGV4ID09PSAwKSB7XG4gICAgICAgICAgb25SZXNwb25kVG9DbGF1ZGUoKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG9uRmluaXNoUGxhbkludGVydmlldygpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGlmIChlLmtleSA9PT0gJ2VzY2FwZScpIHtcbiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIG9uQ2FuY2VsKClcbiAgICAgIH1cbiAgICB9LFxuICAgIFtcbiAgICAgIGlzRm9vdGVyRm9jdXNlZCxcbiAgICAgIGZvb3RlckluZGV4LFxuICAgICAgaXNJblBsYW5Nb2RlLFxuICAgICAgaGFuZGxlVXBGcm9tRm9vdGVyLFxuICAgICAgb25SZXNwb25kVG9DbGF1ZGUsXG4gICAgICBvbkZpbmlzaFBsYW5JbnRlcnZpZXcsXG4gICAgICBvbkNhbmNlbCxcbiAgICBdLFxuICApXG5cbiAgY29uc3QgdGV4dE9wdGlvbnM6IE9wdGlvbldpdGhEZXNjcmlwdGlvbjxzdHJpbmc+W10gPSBxdWVzdGlvbi5vcHRpb25zLm1hcChcbiAgICAob3B0OiBRdWVzdGlvbk9wdGlvbikgPT4gKHtcbiAgICAgIHR5cGU6ICd0ZXh0JyBhcyBjb25zdCxcbiAgICAgIHZhbHVlOiBvcHQubGFiZWwsXG4gICAgICBsYWJlbDogb3B0LmxhYmVsLFxuICAgICAgZGVzY3JpcHRpb246IG9wdC5kZXNjcmlwdGlvbixcbiAgICB9KSxcbiAgKVxuXG4gIGNvbnN0IHF1ZXN0aW9uVGV4dCA9IHF1ZXN0aW9uLnF1ZXN0aW9uXG4gIGNvbnN0IHF1ZXN0aW9uU3RhdGUgPSBxdWVzdGlvblN0YXRlc1txdWVzdGlvblRleHRdXG5cbiAgY29uc3QgaGFuZGxlT3BlbkVkaXRvciA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jIChjdXJyZW50VmFsdWU6IHN0cmluZywgc2V0VmFsdWU6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkKSA9PiB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBlZGl0UHJvbXB0SW5FZGl0b3IoY3VycmVudFZhbHVlKVxuXG4gICAgICBpZiAocmVzdWx0LmNvbnRlbnQgIT09IG51bGwgJiYgcmVzdWx0LmNvbnRlbnQgIT09IGN1cnJlbnRWYWx1ZSkge1xuICAgICAgICAvLyBVcGRhdGUgdGhlIFNlbGVjdCdzIGludGVybmFsIHN0YXRlIGZvciBpbW1lZGlhdGUgVUkgdXBkYXRlXG4gICAgICAgIHNldFZhbHVlKHJlc3VsdC5jb250ZW50KVxuICAgICAgICAvLyBBbHNvIHVwZGF0ZSB0aGUgcXVlc3Rpb24gc3RhdGUgZm9yIHBlcnNpc3RlbmNlXG4gICAgICAgIG9uVXBkYXRlUXVlc3Rpb25TdGF0ZShcbiAgICAgICAgICBxdWVzdGlvblRleHQsXG4gICAgICAgICAgeyB0ZXh0SW5wdXRWYWx1ZTogcmVzdWx0LmNvbnRlbnQgfSxcbiAgICAgICAgICBxdWVzdGlvbi5tdWx0aVNlbGVjdCA/PyBmYWxzZSxcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH0sXG4gICAgW3F1ZXN0aW9uVGV4dCwgb25VcGRhdGVRdWVzdGlvblN0YXRlLCBxdWVzdGlvbi5tdWx0aVNlbGVjdF0sXG4gIClcblxuICBjb25zdCBvdGhlck9wdGlvbjogT3B0aW9uV2l0aERlc2NyaXB0aW9uPHN0cmluZz4gPSB7XG4gICAgdHlwZTogJ2lucHV0JyBhcyBjb25zdCxcbiAgICB2YWx1ZTogJ19fb3RoZXJfXycsXG4gICAgbGFiZWw6ICdPdGhlcicsXG4gICAgcGxhY2Vob2xkZXI6IHF1ZXN0aW9uLm11bHRpU2VsZWN0ID8gJ1R5cGUgc29tZXRoaW5nJyA6ICdUeXBlIHNvbWV0aGluZy4nLFxuICAgIGluaXRpYWxWYWx1ZTogcXVlc3Rpb25TdGF0ZT8udGV4dElucHV0VmFsdWUgPz8gJycsXG4gICAgb25DaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB7XG4gICAgICBvblVwZGF0ZVF1ZXN0aW9uU3RhdGUoXG4gICAgICAgIHF1ZXN0aW9uVGV4dCxcbiAgICAgICAgeyB0ZXh0SW5wdXRWYWx1ZTogdmFsdWUgfSxcbiAgICAgICAgcXVlc3Rpb24ubXVsdGlTZWxlY3QgPz8gZmFsc2UsXG4gICAgICApXG4gICAgfSxcbiAgfVxuXG4gIGNvbnN0IG9wdGlvbnMgPSBbLi4udGV4dE9wdGlvbnMsIG90aGVyT3B0aW9uXVxuXG4gIC8vIENoZWNrIGlmIGFueSBvcHRpb24gaGFzIGEgcHJldmlldyBhbmQgaXQncyBub3QgbXVsdGktc2VsZWN0XG4gIC8vIFByZXZpZXdzIG9ubHkgc3VwcG9ydGVkIGZvciBzaW5nbGUtc2VsZWN0IHF1ZXN0aW9uc1xuICBjb25zdCBoYXNBbnlQcmV2aWV3ID1cbiAgICAhcXVlc3Rpb24ubXVsdGlTZWxlY3QgJiYgcXVlc3Rpb24ub3B0aW9ucy5zb21lKG9wdCA9PiBvcHQucHJldmlldylcblxuICAvLyBEZWxlZ2F0ZSB0byBQcmV2aWV3UXVlc3Rpb25WaWV3IGZvciBjYXJvdXNlbC1zdHlsZSBwcmV2aWV3IG1vZGVcbiAgaWYgKGhhc0FueVByZXZpZXcpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPFByZXZpZXdRdWVzdGlvblZpZXdcbiAgICAgICAgcXVlc3Rpb249e3F1ZXN0aW9ufVxuICAgICAgICBxdWVzdGlvbnM9e3F1ZXN0aW9uc31cbiAgICAgICAgY3VycmVudFF1ZXN0aW9uSW5kZXg9e2N1cnJlbnRRdWVzdGlvbkluZGV4fVxuICAgICAgICBhbnN3ZXJzPXthbnN3ZXJzfVxuICAgICAgICBxdWVzdGlvblN0YXRlcz17cXVlc3Rpb25TdGF0ZXN9XG4gICAgICAgIGhpZGVTdWJtaXRUYWI9e2hpZGVTdWJtaXRUYWJ9XG4gICAgICAgIG1pbkNvbnRlbnRIZWlnaHQ9e21pbkNvbnRlbnRIZWlnaHR9XG4gICAgICAgIG1pbkNvbnRlbnRXaWR0aD17bWluQ29udGVudFdpZHRofVxuICAgICAgICBvblVwZGF0ZVF1ZXN0aW9uU3RhdGU9e29uVXBkYXRlUXVlc3Rpb25TdGF0ZX1cbiAgICAgICAgb25BbnN3ZXI9e29uQW5zd2VyfVxuICAgICAgICBvblRleHRJbnB1dEZvY3VzPXtvblRleHRJbnB1dEZvY3VzfVxuICAgICAgICBvbkNhbmNlbD17b25DYW5jZWx9XG4gICAgICAgIG9uVGFiUHJldj17b25UYWJQcmV2fVxuICAgICAgICBvblRhYk5leHQ9e29uVGFiTmV4dH1cbiAgICAgICAgb25SZXNwb25kVG9DbGF1ZGU9e29uUmVzcG9uZFRvQ2xhdWRlfVxuICAgICAgICBvbkZpbmlzaFBsYW5JbnRlcnZpZXc9e29uRmluaXNoUGxhbkludGVydmlld31cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgIG1hcmdpblRvcD17MH1cbiAgICAgIHRhYkluZGV4PXswfVxuICAgICAgYXV0b0ZvY3VzXG4gICAgICBvbktleURvd249e2hhbmRsZUtleURvd259XG4gICAgPlxuICAgICAge2lzSW5QbGFuTW9kZSAmJiBwbGFuRmlsZVBhdGggJiYgKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezB9PlxuICAgICAgICAgIDxEaXZpZGVyIGNvbG9yPVwiaW5hY3RpdmVcIiAvPlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiaW5hY3RpdmVcIj5cbiAgICAgICAgICAgIFBsYW5uaW5nOiA8RmlsZVBhdGhMaW5rIGZpbGVQYXRoPXtwbGFuRmlsZVBhdGh9IC8+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgICA8Qm94IG1hcmdpblRvcD17LTF9PlxuICAgICAgICA8RGl2aWRlciBjb2xvcj1cImluYWN0aXZlXCIgLz5cbiAgICAgIDwvQm94PlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgcGFkZGluZ1RvcD17MH0+XG4gICAgICAgIDxRdWVzdGlvbk5hdmlnYXRpb25CYXJcbiAgICAgICAgICBxdWVzdGlvbnM9e3F1ZXN0aW9uc31cbiAgICAgICAgICBjdXJyZW50UXVlc3Rpb25JbmRleD17Y3VycmVudFF1ZXN0aW9uSW5kZXh9XG4gICAgICAgICAgYW5zd2Vycz17YW5zd2Vyc31cbiAgICAgICAgICBoaWRlU3VibWl0VGFiPXtoaWRlU3VibWl0VGFifVxuICAgICAgICAvPlxuICAgICAgICA8UGVybWlzc2lvblJlcXVlc3RUaXRsZSB0aXRsZT17cXVlc3Rpb24ucXVlc3Rpb259IGNvbG9yPXsndGV4dCd9IC8+XG5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWluSGVpZ2h0PXttaW5Db250ZW50SGVpZ2h0fT5cbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICB7cXVlc3Rpb24ubXVsdGlTZWxlY3QgPyAoXG4gICAgICAgICAgICAgIDxTZWxlY3RNdWx0aVxuICAgICAgICAgICAgICAgIGtleT17cXVlc3Rpb24ucXVlc3Rpb259XG4gICAgICAgICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU9e1xuICAgICAgICAgICAgICAgICAgcXVlc3Rpb25TdGF0ZXNbcXVlc3Rpb24ucXVlc3Rpb25dPy5zZWxlY3RlZFZhbHVlIGFzXG4gICAgICAgICAgICAgICAgICAgIHwgc3RyaW5nW11cbiAgICAgICAgICAgICAgICAgICAgfCB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgb25DaGFuZ2U9eyh2YWx1ZXM6IHN0cmluZ1tdKSA9PiB7XG4gICAgICAgICAgICAgICAgICBvblVwZGF0ZVF1ZXN0aW9uU3RhdGUoXG4gICAgICAgICAgICAgICAgICAgIHF1ZXN0aW9uVGV4dCxcbiAgICAgICAgICAgICAgICAgICAgeyBzZWxlY3RlZFZhbHVlOiB2YWx1ZXMgfSxcbiAgICAgICAgICAgICAgICAgICAgdHJ1ZSxcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHRleHRJbnB1dCA9IHZhbHVlcy5pbmNsdWRlcygnX19vdGhlcl9fJylcbiAgICAgICAgICAgICAgICAgICAgPyBxdWVzdGlvblN0YXRlc1txdWVzdGlvblRleHRdPy50ZXh0SW5wdXRWYWx1ZVxuICAgICAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgY29uc3QgZmluYWxWYWx1ZXMgPSB2YWx1ZXNcbiAgICAgICAgICAgICAgICAgICAgLmZpbHRlcih2ID0+IHYgIT09ICdfX290aGVyX18nKVxuICAgICAgICAgICAgICAgICAgICAuY29uY2F0KHRleHRJbnB1dCA/IFt0ZXh0SW5wdXRdIDogW10pXG4gICAgICAgICAgICAgICAgICBvbkFuc3dlcihxdWVzdGlvblRleHQsIGZpbmFsVmFsdWVzLCB1bmRlZmluZWQsIGZhbHNlKVxuICAgICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAgICAgb25Gb2N1cz17aGFuZGxlRm9jdXN9XG4gICAgICAgICAgICAgICAgb25DYW5jZWw9e29uQ2FuY2VsfVxuICAgICAgICAgICAgICAgIHN1Ym1pdEJ1dHRvblRleHQ9e1xuICAgICAgICAgICAgICAgICAgY3VycmVudFF1ZXN0aW9uSW5kZXggPT09IHF1ZXN0aW9ucy5sZW5ndGggLSAxXG4gICAgICAgICAgICAgICAgICAgID8gJ1N1Ym1pdCdcbiAgICAgICAgICAgICAgICAgICAgOiAnTmV4dCdcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgb25TdWJtaXQ9e29uU3VibWl0fVxuICAgICAgICAgICAgICAgIG9uRG93bkZyb21MYXN0SXRlbT17aGFuZGxlRG93bkZyb21MYXN0SXRlbX1cbiAgICAgICAgICAgICAgICBpc0Rpc2FibGVkPXtpc0Zvb3RlckZvY3VzZWR9XG4gICAgICAgICAgICAgICAgb25PcGVuRWRpdG9yPXtoYW5kbGVPcGVuRWRpdG9yfVxuICAgICAgICAgICAgICAgIG9uSW1hZ2VQYXN0ZT17b25JbWFnZVBhc3RlfVxuICAgICAgICAgICAgICAgIHBhc3RlZENvbnRlbnRzPXtwYXN0ZWRDb250ZW50c31cbiAgICAgICAgICAgICAgICBvblJlbW92ZUltYWdlPXtvblJlbW92ZUltYWdlfVxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgICAgIGtleT17cXVlc3Rpb24ucXVlc3Rpb259XG4gICAgICAgICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU9e1xuICAgICAgICAgICAgICAgICAgcXVlc3Rpb25TdGF0ZXNbcXVlc3Rpb24ucXVlc3Rpb25dPy5zZWxlY3RlZFZhbHVlIGFzXG4gICAgICAgICAgICAgICAgICAgIHwgc3RyaW5nXG4gICAgICAgICAgICAgICAgICAgIHwgdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG9uQ2hhbmdlPXsodmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgICAgICAgICAgICAgb25VcGRhdGVRdWVzdGlvblN0YXRlKFxuICAgICAgICAgICAgICAgICAgICBxdWVzdGlvblRleHQsXG4gICAgICAgICAgICAgICAgICAgIHsgc2VsZWN0ZWRWYWx1ZTogdmFsdWUgfSxcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICBjb25zdCB0ZXh0SW5wdXQgPVxuICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9PT0gJ19fb3RoZXJfXydcbiAgICAgICAgICAgICAgICAgICAgICA/IHF1ZXN0aW9uU3RhdGVzW3F1ZXN0aW9uVGV4dF0/LnRleHRJbnB1dFZhbHVlXG4gICAgICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICAgIG9uQW5zd2VyKHF1ZXN0aW9uVGV4dCwgdmFsdWUsIHRleHRJbnB1dClcbiAgICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgICAgIG9uRm9jdXM9e2hhbmRsZUZvY3VzfVxuICAgICAgICAgICAgICAgIG9uQ2FuY2VsPXtvbkNhbmNlbH1cbiAgICAgICAgICAgICAgICBvbkRvd25Gcm9tTGFzdEl0ZW09e2hhbmRsZURvd25Gcm9tTGFzdEl0ZW19XG4gICAgICAgICAgICAgICAgaXNEaXNhYmxlZD17aXNGb290ZXJGb2N1c2VkfVxuICAgICAgICAgICAgICAgIGxheW91dD1cImNvbXBhY3QtdmVydGljYWxcIlxuICAgICAgICAgICAgICAgIG9uT3BlbkVkaXRvcj17aGFuZGxlT3BlbkVkaXRvcn1cbiAgICAgICAgICAgICAgICBvbkltYWdlUGFzdGU9e29uSW1hZ2VQYXN0ZX1cbiAgICAgICAgICAgICAgICBwYXN0ZWRDb250ZW50cz17cGFzdGVkQ29udGVudHN9XG4gICAgICAgICAgICAgICAgb25SZW1vdmVJbWFnZT17b25SZW1vdmVJbWFnZX1cbiAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgey8qIEZvb3RlciBzZWN0aW9uIC0gYWx3YXlzIHZpc2libGUsIHNlcGFyYXRlIGZyb20gU2VsZWN0ICovfVxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPERpdmlkZXIgY29sb3I9XCJpbmFjdGl2ZVwiIC8+XG4gICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9PlxuICAgICAgICAgICAgICB7aXNGb290ZXJGb2N1c2VkICYmIGZvb3RlckluZGV4ID09PSAwID8gKFxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPntmaWd1cmVzLnBvaW50ZXJ9PC9UZXh0PlxuICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgIDxUZXh0PiA8L1RleHQ+XG4gICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgY29sb3I9e1xuICAgICAgICAgICAgICAgICAgaXNGb290ZXJGb2N1c2VkICYmIGZvb3RlckluZGV4ID09PSAwXG4gICAgICAgICAgICAgICAgICAgID8gJ3N1Z2dlc3Rpb24nXG4gICAgICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAge29wdGlvbnMubGVuZ3RoICsgMX0uIENoYXQgYWJvdXQgdGhpc1xuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgIHtpc0luUGxhbk1vZGUgJiYgKFxuICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9PlxuICAgICAgICAgICAgICAgIHtpc0Zvb3RlckZvY3VzZWQgJiYgZm9vdGVySW5kZXggPT09IDEgPyAoXG4gICAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Z2dlc3Rpb25cIj57ZmlndXJlcy5wb2ludGVyfTwvVGV4dD5cbiAgICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgICAgPFRleHQ+IDwvVGV4dD5cbiAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgICBjb2xvcj17XG4gICAgICAgICAgICAgICAgICAgIGlzRm9vdGVyRm9jdXNlZCAmJiBmb290ZXJJbmRleCA9PT0gMVxuICAgICAgICAgICAgICAgICAgICAgID8gJ3N1Z2dlc3Rpb24nXG4gICAgICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICB7b3B0aW9ucy5sZW5ndGggKyAyfS4gU2tpcCBpbnRlcnZpZXcgYW5kIHBsYW4gaW1tZWRpYXRlbHlcbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImluYWN0aXZlXCIgZGltQ29sb3I+XG4gICAgICAgICAgICAgIEVudGVyIHRvIHNlbGVjdCDCt3snICd9XG4gICAgICAgICAgICAgIHtxdWVzdGlvbnMubGVuZ3RoID09PSAxID8gKFxuICAgICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgICB7ZmlndXJlcy5hcnJvd1VwfS97ZmlndXJlcy5hcnJvd0Rvd259IHRvIG5hdmlnYXRlXG4gICAgICAgICAgICAgICAgPC8+XG4gICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgJ1RhYi9BcnJvdyBrZXlzIHRvIG5hdmlnYXRlJ1xuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICB7aXNPdGhlckZvY3VzZWQgJiYgZWRpdG9yTmFtZSAmJiAoXG4gICAgICAgICAgICAgICAgPD4gwrcgY3RybCtnIHRvIGVkaXQgaW4ge2VkaXRvck5hbWV9PC8+XG4gICAgICAgICAgICAgICl9eycgJ31cbiAgICAgICAgICAgICAgwrcgRXNjIHRvIGNhbmNlbFxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPQyxLQUFLLElBQUlDLFdBQVcsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDcEQsY0FBY0MsYUFBYSxRQUFRLHVDQUF1QztBQUMxRSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxpQkFBaUI7QUFDM0MsU0FBU0MsV0FBVyxRQUFRLDRCQUE0QjtBQUN4RCxjQUNFQyxRQUFRLEVBQ1JDLGNBQWMsUUFDVCwyREFBMkQ7QUFDbEUsY0FBY0MsYUFBYSxRQUFRLDBCQUEwQjtBQUM3RCxTQUFTQyxpQkFBaUIsUUFBUSwwQkFBMEI7QUFDNUQsU0FBU0MsZ0JBQWdCLFFBQVEsdUJBQXVCO0FBQ3hELGNBQWNDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDckUsU0FBU0Msa0JBQWtCLFFBQVEsZ0NBQWdDO0FBQ25FLFNBQ0UsS0FBS0MscUJBQXFCLEVBQzFCQyxNQUFNLEVBQ05DLFdBQVcsUUFDTiw2QkFBNkI7QUFDcEMsU0FBU0MsT0FBTyxRQUFRLGdDQUFnQztBQUN4RCxTQUFTQyxZQUFZLFFBQVEsdUJBQXVCO0FBQ3BELFNBQVNDLHNCQUFzQixRQUFRLDhCQUE4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 \ No newline at end of file diff --git a/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx b/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx new file mode 100644 index 0000000..d0b5fb3 --- /dev/null +++ b/components/permissions/AskUserQuestionPermissionRequest/SubmitQuestionsView.tsx @@ -0,0 +1,144 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import React from 'react'; +import { Box, Text } from '../../../ink.js'; +import type { Question } from '../../../tools/AskUserQuestionTool/AskUserQuestionTool.js'; +import type { PermissionDecision } from '../../../utils/permissions/PermissionResult.js'; +import { Select } from '../../CustomSelect/index.js'; +import { Divider } from '../../design-system/Divider.js'; +import { PermissionRequestTitle } from '../PermissionRequestTitle.js'; +import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js'; +import { QuestionNavigationBar } from './QuestionNavigationBar.js'; +type Props = { + questions: Question[]; + currentQuestionIndex: number; + answers: Record; + allQuestionsAnswered: boolean; + permissionResult: PermissionDecision; + minContentHeight?: number; + onFinalResponse: (value: 'submit' | 'cancel') => void; +}; +export function SubmitQuestionsView(t0) { + const $ = _c(27); + const { + questions, + currentQuestionIndex, + answers, + allQuestionsAnswered, + permissionResult, + minContentHeight, + onFinalResponse + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== answers || $[2] !== currentQuestionIndex || $[3] !== questions) { + t2 = ; + $[1] = answers; + $[2] = currentQuestionIndex; + $[3] = questions; + $[4] = t2; + } else { + t2 = $[4]; + } + let t3; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t3 = ; + $[5] = t3; + } else { + t3 = $[5]; + } + let t4; + if ($[6] !== allQuestionsAnswered) { + t4 = !allQuestionsAnswered && {figures.warning} You have not answered all questions; + $[6] = allQuestionsAnswered; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] !== answers || $[9] !== questions) { + t5 = Object.keys(answers).length > 0 && {questions.filter(q => q?.question && answers[q.question]).map(q_0 => { + const answer = answers[q_0?.question]; + return {figures.bullet} {q_0?.question || "Question"}{figures.arrowRight} {answer}; + })}; + $[8] = answers; + $[9] = questions; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] !== permissionResult) { + t6 = ; + $[11] = permissionResult; + $[12] = t6; + } else { + t6 = $[12]; + } + let t7; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t7 = Ready to submit your answers?; + $[13] = t7; + } else { + t7 = $[13]; + } + let t8; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t8 = { + type: "text" as const, + label: "Submit answers", + value: "submit" + }; + $[14] = t8; + } else { + t8 = $[14]; + } + let t9; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t9 = [t8, { + type: "text" as const, + label: "Cancel", + value: "cancel" + }]; + $[15] = t9; + } else { + t9 = $[15]; + } + let t10; + if ($[16] !== onFinalResponse) { + t10 = ({ + ...o, + disabled: true + })) : options : options} isDisabled={feature('BASH_CLASSIFIER') ? toolUseConfirm.classifierAutoApproved : false} inlineDescriptions onChange={onSelect} onCancel={() => handleReject()} onFocus={handleFocus} onInputModeToggle={handleInputModeToggle} /> + + + + Esc to cancel + {(focusedOption === 'yes' && !yesInputMode || focusedOption === 'no' && !noInputMode) && ' · Tab to amend'} + {explainerState.enabled && ` · ctrl+e to ${explainerState.visible ? 'hide' : 'explain'}`} + + {toolUseContext.options.debug && Ctrl+d to show debug info} + + } + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiZmlndXJlcyIsIlJlYWN0IiwidXNlQ2FsbGJhY2siLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwidXNlUmVmIiwidXNlU3RhdGUiLCJCb3giLCJUZXh0IiwidXNlVGhlbWUiLCJ1c2VLZXliaW5kaW5nIiwiZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJzYW5pdGl6ZVRvb2xOYW1lRm9yQW5hbHl0aWNzIiwidXNlQXBwU3RhdGUiLCJCYXNoVG9vbCIsImdldEZpcnN0V29yZFByZWZpeCIsImdldFNpbXBsZUNvbW1hbmRQcmVmaXgiLCJnZXREZXN0cnVjdGl2ZUNvbW1hbmRXYXJuaW5nIiwicGFyc2VTZWRFZGl0Q29tbWFuZCIsInNob3VsZFVzZVNhbmRib3giLCJnZXRDb21wb3VuZENvbW1hbmRQcmVmaXhlc1N0YXRpYyIsImNyZWF0ZVByb21wdFJ1bGVDb250ZW50IiwiZ2VuZXJhdGVHZW5lcmljRGVzY3JpcHRpb24iLCJnZXRCYXNoUHJvbXB0QWxsb3dEZXNjcmlwdGlvbnMiLCJpc0NsYXNzaWZpZXJQZXJtaXNzaW9uc0VuYWJsZWQiLCJleHRyYWN0UnVsZXMiLCJQZXJtaXNzaW9uVXBkYXRlIiwiU2FuZGJveE1hbmFnZXIiLCJTZWxlY3QiLCJTaGltbWVyQ2hhciIsInVzZVNoaW1tZXJBbmltYXRpb24iLCJVbmFyeUV2ZW50IiwidXNlUGVybWlzc2lvblJlcXVlc3RMb2dnaW5nIiwiUGVybWlzc2lvbkRlY2lzaW9uRGVidWdJbmZvIiwiUGVybWlzc2lvbkRpYWxvZyIsIlBlcm1pc3Npb25FeHBsYWluZXJDb250ZW50IiwidXNlUGVybWlzc2lvbkV4cGxhaW5lclVJIiwiUGVybWlzc2lvblJlcXVlc3RQcm9wcyIsIlBlcm1pc3Npb25SdWxlRXhwbGFuYXRpb24iLCJTZWRFZGl0UGVybWlzc2lvblJlcXVlc3QiLCJ1c2VTaGVsbFBlcm1pc3Npb25GZWVkYmFjayIsImxvZ1VuYXJ5UGVybWlzc2lvbkV2ZW50IiwiYmFzaFRvb2xVc2VPcHRpb25zIiwiQ0hFQ0tJTkdfVEVYVCIsIkNsYXNzaWZpZXJDaGVja2luZ1N1YnRpdGxlIiwiJCIsIl9jIiwicmVmIiwiZ2xpbW1lckluZGV4IiwidDAiLCJTeW1ib2wiLCJmb3IiLCJ0MSIsIm1hcCIsImNoYXIiLCJpIiwidDIiLCJCYXNoUGVybWlzc2lvblJlcXVlc3QiLCJwcm9wcyIsInRvb2xVc2VDb25maXJtIiwidG9vbFVzZUNvbnRleHQiLCJvbkRvbmUiLCJvblJlamVjdCIsInZlcmJvc2UiLCJ3b3JrZXJCYWRnZSIsImNvbW1hbmQiLCJkZXNjcmlwdGlvbiIsImlucHV0IiwiaW5wdXRTY2hlbWEiLCJwYXJzZSIsInNlZEluZm8iLCJCYXNoUGVybWlzc2lvblJlcXVlc3RJbm5lciIsIl92ZXJib3NlIiwiUmVhY3ROb2RlIiwidGhlbWUiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJzIiwiZXhwbGFpbmVyU3RhdGUiLCJ0b29sTmFtZSIsInRvb2wiLCJuYW1lIiwidG9vbElucHV0IiwidG9vbERlc2NyaXB0aW9uIiwibWVzc2FnZXMiLCJ5ZXNJbnB1dE1vZGUiLCJub0lucHV0TW9kZSIsInllc0ZlZWRiYWNrTW9kZUVudGVyZWQiLCJub0ZlZWRiYWNrTW9kZUVudGVyZWQiLCJhY2NlcHRGZWVkYmFjayIsInJlamVjdEZlZWRiYWNrIiwic2V0QWNjZXB0RmVlZGJhY2siLCJzZXRSZWplY3RGZWVkYmFjayIsImZvY3VzZWRPcHRpb24iLCJoYW5kbGVJbnB1dE1vZGVUb2dnbGUiLCJoYW5kbGVSZWplY3QiLCJoYW5kbGVGb2N1cyIsImV4cGxhaW5lclZpc2libGUiLCJ2aXNpYmxlIiwic2hvd1Blcm1pc3Npb25EZWJ1ZyIsInNldFNob3dQZXJtaXNzaW9uRGVidWciLCJjbGFzc2lmaWVyRGVzY3JpcHRpb24iLCJzZXRDbGFzc2lmaWVyRGVzY3JpcHRpb24iLCJpbml0aWFsQ2xhc3NpZmllckRlc2NyaXB0aW9uRW1wdHkiLCJzZXRJbml0aWFsQ2xhc3NpZmllckRlc2NyaXB0aW9uRW1wdHkiLCJ0cmltIiwiYWJvcnRDb250cm9sbGVyIiwiQWJvcnRDb250cm9sbGVyIiwic2lnbmFsIiwidGhlbiIsImdlbmVyaWMiLCJhYm9ydGVkIiwiY2F0Y2giLCJhYm9ydCIsImlzQ29tcG91bmQiLCJwZXJtaXNzaW9uUmVzdWx0IiwiZGVjaXNpb25SZWFzb24iLCJ0eXBlIiwiZWRpdGFibGVQcmVmaXgiLCJzZXRFZGl0YWJsZVByZWZpeCIsImJhY2tlbmRCYXNoUnVsZXMiLCJzdWdnZXN0aW9ucyIsInVuZGVmaW5lZCIsImZpbHRlciIsInIiLCJydWxlQ29udGVudCIsImxlbmd0aCIsInR3byIsIm9uZSIsImhhc1VzZXJFZGl0ZWRQcmVmaXgiLCJvbkVkaXRhYmxlUHJlZml4Q2hhbmdlIiwidmFsdWUiLCJjdXJyZW50IiwiY2FuY2VsbGVkIiwic3ViY21kIiwiaXNSZWFkT25seSIsInByZWZpeGVzIiwiY2xhc3NpZmllcldhc0NoZWNraW5nIiwiY2xhc3NpZmllckNoZWNrSW5Qcm9ncmVzcyIsImRlc3RydWN0aXZlV2FybmluZyIsInNhbmRib3hpbmdFbmFibGVkIiwiaXNTYW5kYm94ZWQiLCJpc1NhbmRib3hpbmdFbmFibGVkIiwidW5hcnlFdmVudCIsImNvbXBsZXRpb25fdHlwZSIsImxhbmd1YWdlX25hbWUiLCJleGlzdGluZ0FsbG93RGVzY3JpcHRpb25zIiwib3B0aW9ucyIsImJlaGF2aW9yIiwib25SZWplY3RGZWVkYmFja0NoYW5nZSIsIm9uQWNjZXB0RmVlZGJhY2tDaGFuZ2UiLCJvbkNsYXNzaWZpZXJEZXNjcmlwdGlvbkNoYW5nZSIsImhhbmRsZVRvZ2dsZURlYnVnIiwicHJldiIsImNvbnRleHQiLCJoYW5kbGVEaXNtaXNzQ2hlY2ttYXJrIiwib25EaXNtaXNzQ2hlY2ttYXJrIiwiaXNBY3RpdmUiLCJjbGFzc2lmaWVyQXV0b0FwcHJvdmVkIiwib25TZWxlY3QiLCJvcHRpb25JbmRleCIsIlJlY29yZCIsInllcyIsIm5vIiwib3B0aW9uX2luZGV4IiwiZXhwbGFpbmVyX3Zpc2libGUiLCJ0b29sTmFtZUZvckFuYWx5dGljcyIsInRyaW1tZWRQcmVmaXgiLCJvbkFsbG93IiwicHJlZml4VXBkYXRlcyIsInJ1bGVzIiwiZGVzdGluYXRpb24iLCJ0cmltbWVkRGVzY3JpcHRpb24iLCJwZXJtaXNzaW9uVXBkYXRlcyIsInRyaW1tZWRGZWVkYmFjayIsImlzTWNwIiwiaGFzX2luc3RydWN0aW9ucyIsImluc3RydWN0aW9uc19sZW5ndGgiLCJlbnRlcmVkX2ZlZWRiYWNrX21vZGUiLCJjbGFzc2lmaWVyU3VidGl0bGUiLCJ0aWNrIiwiY2xhc3NpZmllck1hdGNoZWRSdWxlIiwicmVuZGVyVG9vbFVzZU1lc3NhZ2UiLCJwcm9taXNlIiwiZGVidWciLCJvIiwiZGlzYWJsZWQiLCJlbmFibGVkIl0sInNvdXJjZXMiOlsiQmFzaFBlcm1pc3Npb25SZXF1ZXN0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgUmVhY3QsIHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlTWVtbywgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0LCB1c2VUaGVtZSB9IGZyb20gJy4uLy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi8uLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUgfSBmcm9tICcuLi8uLi8uLi9zZXJ2aWNlcy9hbmFseXRpY3MvZ3Jvd3RoYm9vay5qcydcbmltcG9ydCB7XG4gIHR5cGUgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgbG9nRXZlbnQsXG59IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IHNhbml0aXplVG9vbE5hbWVGb3JBbmFseXRpY3MgfSBmcm9tICcuLi8uLi8uLi9zZXJ2aWNlcy9hbmFseXRpY3MvbWV0YWRhdGEuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSB9IGZyb20gJy4uLy4uLy4uL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHsgQmFzaFRvb2wgfSBmcm9tICcuLi8uLi8uLi90b29scy9CYXNoVG9vbC9CYXNoVG9vbC5qcydcbmltcG9ydCB7XG4gIGdldEZpcnN0V29yZFByZWZpeCxcbiAgZ2V0U2ltcGxlQ29tbWFuZFByZWZpeCxcbn0gZnJvbSAnLi4vLi4vLi4vdG9vbHMvQmFzaFRvb2wvYmFzaFBlcm1pc3Npb25zLmpzJ1xuaW1wb3J0IHsgZ2V0RGVzdHJ1Y3RpdmVDb21tYW5kV2FybmluZyB9IGZyb20gJy4uLy4uLy4uL3Rvb2xzL0Jhc2hUb29sL2Rlc3RydWN0aXZlQ29tbWFuZFdhcm5pbmcuanMnXG5pbXBvcnQgeyBwYXJzZVNlZEVkaXRDb21tYW5kIH0gZnJvbSAnLi4vLi4vLi4vdG9vbHMvQmFzaFRvb2wvc2VkRWRpdFBhcnNlci5qcydcbmltcG9ydCB7IHNob3VsZFVzZVNhbmRib3ggfSBmcm9tICcuLi8uLi8uLi90b29scy9CYXNoVG9vbC9zaG91bGRVc2VTYW5kYm94LmpzJ1xuaW1wb3J0IHsgZ2V0Q29tcG91bmRDb21tYW5kUHJlZml4ZXNTdGF0aWMgfSBmcm9tICcuLi8uLi8uLi91dGlscy9iYXNoL3ByZWZpeC5qcydcbmltcG9ydCB7XG4gIGNyZWF0ZVByb21wdFJ1bGVDb250ZW50LFxuICBnZW5lcmF0ZUdlbmVyaWNEZXNjcmlwdGlvbixcbiAgZ2V0QmFzaFByb21wdEFsbG93RGVzY3JpcHRpb25zLFxuICBpc0NsYXNzaWZpZXJQZXJtaXNzaW9uc0VuYWJsZWQsXG59IGZyb20gJy4uLy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL2Jhc2hDbGFzc2lmaWVyLmpzJ1xuaW1wb3J0IHsgZXh0cmFjdFJ1bGVzIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvblVwZGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvblVwZGF0ZSB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25VcGRhdGVTY2hlbWEuanMnXG5pbXBvcnQgeyBTYW5kYm94TWFuYWdlciB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL3NhbmRib3gvc2FuZGJveC1hZGFwdGVyLmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi4vLi4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IFNoaW1tZXJDaGFyIH0gZnJvbSAnLi4vLi4vU3Bpbm5lci9TaGltbWVyQ2hhci5qcydcbmltcG9ydCB7IHVzZVNoaW1tZXJBbmltYXRpb24gfSBmcm9tICcuLi8uLi9TcGlubmVyL3VzZVNoaW1tZXJBbmltYXRpb24uanMnXG5pbXBvcnQgeyB0eXBlIFVuYXJ5RXZlbnQsIHVzZVBlcm1pc3Npb25SZXF1ZXN0TG9nZ2luZyB9IGZyb20gJy4uL2hvb2tzLmpzJ1xuaW1wb3J0IHsgUGVybWlzc2lvbkRlY2lzaW9uRGVidWdJbmZvIH0gZnJvbSAnLi4vUGVybWlzc2lvbkRlY2lzaW9uRGVidWdJbmZvLmpzJ1xuaW1wb3J0IHsgUGVybWlzc2lvbkRpYWxvZyB9IGZyb20gJy4uL1Blcm1pc3Npb25EaWFsb2cuanMnXG5pbXBvcnQge1xuICBQZXJtaXNzaW9uRXhwbGFpbmVyQ29udGVudCxcbiAgdXNlUGVybWlzc2lvbkV4cGxhaW5lclVJLFxufSBmcm9tICcuLi9QZXJtaXNzaW9uRXhwbGFuYXRpb24uanMnXG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMgfSBmcm9tICcuLi9QZXJtaXNzaW9uUmVxdWVzdC5qcydcbmltcG9ydCB7IFBlcm1pc3Npb25SdWxlRXhwbGFuYXRpb24gfSBmcm9tICcuLi9QZXJtaXNzaW9uUnVsZUV4cGxhbmF0aW9uLmpzJ1xuaW1wb3J0IHsgU2VkRWRpdFBlcm1pc3Npb25SZXF1ZXN0IH0gZnJvbSAnLi4vU2VkRWRpdFBlcm1pc3Npb25SZXF1ZXN0L1NlZEVkaXRQZXJtaXNzaW9uUmVxdWVzdC5qcydcbmltcG9ydCB7IHVzZVNoZWxsUGVybWlzc2lvbkZlZWRiYWNrIH0gZnJvbSAnLi4vdXNlU2hlbGxQZXJtaXNzaW9uRmVlZGJhY2suanMnXG5pbXBvcnQgeyBsb2dVbmFyeVBlcm1pc3Npb25FdmVudCB9IGZyb20gJy4uL3V0aWxzLmpzJ1xuaW1wb3J0IHsgYmFzaFRvb2xVc2VPcHRpb25zIH0gZnJvbSAnLi9iYXNoVG9vbFVzZU9wdGlvbnMuanMnXG5cbmNvbnN0IENIRUNLSU5HX1RFWFQgPSAnQXR0ZW1wdGluZyB0byBhdXRvLWFwcHJvdmVcXHUyMDI2J1xuXG4vLyBJc29sYXRlcyB0aGUgMjBmcHMgc2hpbW1lciBjbG9jayBmcm9tIEJhc2hQZXJtaXNzaW9uUmVxdWVzdElubmVyLiBCZWZvcmUgdGhpc1xuLy8gZXh0cmFjdGlvbiwgdXNlU2hpbW1lckFuaW1hdGlvbiBsaXZlZCBpbnNpZGUgdGhlIDUzNS1saW5lIElubmVyIGJvZHksIHNvIGV2ZXJ5XG4vLyA1MG1zIGNsb2NrIHRpY2sgcmUtcmVuZGVyZWQgdGhlIGVudGlyZSBkaWFsb2cgKFBlcm1pc3Npb25EaWFsb2cgKyBTZWxlY3QgK1xuLy8gYWxsIGNoaWxkcmVuKSBmb3IgdGhlIH4xLTMgc2Vjb25kcyB0aGUgY2xhc3NpZmllciB0eXBpY2FsbHkgdGFrZXMuIElubmVyIGFsc29cbi8vIGhhcyBhIENvbXBpbGVyIGJhaWxvdXQgKHNlZSBiZWxvdyksIHNvIG5vdGhpbmcgd2FzIGF1dG8tbWVtb2l6ZWQg4oCUIHRoZSBmdWxsXG4vLyBKU1ggdHJlZSB3YXMgcmVjb25zdHJ1Y3RlZCAyMC02MCB0aW1lcyBwZXIgY2xhc3NpZmllciBjaGVjay5cbmZ1bmN0aW9uIENsYXNzaWZpZXJDaGVja2luZ1N1YnRpdGxlKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtyZWYsIGdsaW1tZXJJbmRleF0gPSB1c2VTaGltbWVyQW5pbWF0aW9uKFxuICAgICdyZXF1ZXN0aW5nJyxcbiAgICBDSEVDS0lOR19URVhULFxuICAgIGZhbHNlLFxuICApXG4gIHJldHVybiAoXG4gICAgPEJveCByZWY9e3JlZn0+XG4gICAgICA8VGV4dD5cbiAgICAgICAge1suLi5DSEVDS0lOR19URVhUXS5tYXAoKGNoYXIsIGkpID0+IChcbiAgICAgICAgICA8U2hpbW1lckNoYXJcbiAgICAgICAgICAgIGtleT17aX1cbiAgICAgICAgICAgIGNoYXI9e2NoYXJ9XG4gICAgICAgICAgICBpbmRleD17aX1cbiAgICAgICAgICAgIGdsaW1tZXJJbmRleD17Z2xpbW1lckluZGV4fVxuICAgICAgICAgICAgbWVzc2FnZUNvbG9yPVwiaW5hY3RpdmVcIlxuICAgICAgICAgICAgc2hpbW1lckNvbG9yPVwic3VidGxlXCJcbiAgICAgICAgICAvPlxuICAgICAgICApKX1cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gQmFzaFBlcm1pc3Npb25SZXF1ZXN0KFxuICBwcm9wczogUGVybWlzc2lvblJlcXVlc3RQcm9wcyxcbik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHtcbiAgICB0b29sVXNlQ29uZmlybSxcbiAgICB0b29sVXNlQ29udGV4dCxcbiAgICBvbkRvbmUsXG4gICAgb25SZWplY3QsXG4gICAgdmVyYm9zZSxcbiAgICB3b3JrZXJCYWRnZSxcbiAgfSA9IHByb3BzXG5cbiAgY29uc3QgeyBjb21tYW5kLCBkZXNjcmlwdGlvbiB9ID0gQmFzaFRvb2wuaW5wdXRTY2hlbWEucGFyc2UoXG4gICAgdG9vbFVzZUNvbmZpcm0uaW5wdXQsXG4gIClcblxuICAvLyBEZXRlY3Qgc2VkIGluLXBsYWNlIGVkaXQgY29tbWFuZHMgYW5kIGRlbGVnYXRlIHRvIFNlZEVkaXRQZXJtaXNzaW9uUmVxdWVzdFxuICAvLyBUaGlzIHJlbmRlcnMgc2VkIGVkaXRzIGxpa2UgZmlsZSBlZGl0cyB3aXRoIGEgZGlmZiB2aWV3XG4gIGNvbnN0IHNlZEluZm8gPSBwYXJzZVNlZEVkaXRDb21tYW5kKGNvbW1hbmQpXG5cbiAgaWYgKHNlZEluZm8pIHtcbiAgICByZXR1cm4gKFxuICAgICAgPFNlZEVkaXRQZXJtaXNzaW9uUmVxdWVzdFxuICAgICAgICB0b29sVXNlQ29uZmlybT17dG9vbFVzZUNvbmZpcm19XG4gICAgICAgIHRvb2xVc2VDb250ZXh0PXt0b29sVXNlQ29udGV4dH1cbiAgICAgICAgb25Eb25lPXtvbkRvbmV9XG4gICAgICAgIG9uUmVqZWN0PXtvblJlamVjdH1cbiAgICAgICAgdmVyYm9zZT17dmVyYm9zZX1cbiAgICAgICAgd29ya2VyQmFkZ2U9e3dvcmtlckJhZGdlfVxuICAgICAgICBzZWRJbmZvPXtzZWRJbmZvfVxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICAvLyBSZWd1bGFyIGJhc2ggY29tbWFuZCAtIHJlbmRlciB3aXRoIGhvb2tzXG4gIHJldHVybiAoXG4gICAgPEJhc2hQZXJtaXNzaW9uUmVxdWVzdElubmVyXG4gICAgICB0b29sVXNlQ29uZmlybT17dG9vbFVzZUNvbmZpcm19XG4gICAgICB0b29sVXNlQ29udGV4dD17dG9vbFVzZUNvbnRleHR9XG4gICAgICBvbkRvbmU9e29uRG9uZX1cbiAgICAgIG9uUmVqZWN0PXtvblJlamVjdH1cbiAgICAgIHZlcmJvc2U9e3ZlcmJvc2V9XG4gICAgICB3b3JrZXJCYWRnZT17d29ya2VyQmFkZ2V9XG4gICAgICBjb21tYW5kPXtjb21tYW5kfVxuICAgICAgZGVzY3JpcHRpb249e2Rlc2NyaXB0aW9ufVxuICAgIC8+XG4gIClcbn1cblxuLy8gSW5uZXIgY29tcG9uZW50IHRoYXQgdXNlcyBob29rcyAtIG9ubHkgY2FsbGVkIGZvciBub24tTUNQIENMSSBjb21tYW5kc1xuZnVuY3Rpb24gQmFzaFBlcm1pc3Npb25SZXF1ZXN0SW5uZXIoe1xuICB0b29sVXNlQ29uZmlybSxcbiAgdG9vbFVzZUNvbnRleHQsXG4gIG9uRG9uZSxcbiAgb25SZWplY3QsXG4gIHZlcmJvc2U6IF92ZXJib3NlLFxuICB3b3JrZXJCYWRnZSxcbiAgY29tbWFuZCxcbiAgZGVzY3JpcHRpb24sXG59OiBQZXJtaXNzaW9uUmVxdWVzdFByb3BzICYge1xuICBjb21tYW5kOiBzdHJpbmdcbiAgZGVzY3JpcHRpb24/OiBzdHJpbmdcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbdGhlbWVdID0gdXNlVGhlbWUoKVxuICBjb25zdCB0b29sUGVybWlzc2lvbkNvbnRleHQgPSB1c2VBcHBTdGF0ZShzID0+IHMudG9vbFBlcm1pc3Npb25Db250ZXh0KVxuICBjb25zdCBleHBsYWluZXJTdGF0ZSA9IHVzZVBlcm1pc3Npb25FeHBsYWluZXJVSSh7XG4gICAgdG9vbE5hbWU6IHRvb2xVc2VDb25maXJtLnRvb2wubmFtZSxcbiAgICB0b29sSW5wdXQ6IHRvb2xVc2VDb25maXJtLmlucHV0LFxuICAgIHRvb2xEZXNjcmlwdGlvbjogdG9vbFVzZUNvbmZpcm0uZGVzY3JpcHRpb24sXG4gICAgbWVzc2FnZXM6IHRvb2xVc2VDb250ZXh0Lm1lc3NhZ2VzLFxuICB9KVxuICBjb25zdCB7XG4gICAgeWVzSW5wdXRNb2RlLFxuICAgIG5vSW5wdXRNb2RlLFxuICAgIHllc0ZlZWRiYWNrTW9kZUVudGVyZWQsXG4gICAgbm9GZWVkYmFja01vZGVFbnRlcmVkLFxuICAgIGFjY2VwdEZlZWRiYWNrLFxuICAgIHJlamVjdEZlZWRiYWNrLFxuICAgIHNldEFjY2VwdEZlZWRiYWNrLFxuICAgIHNldFJlamVjdEZlZWRiYWNrLFxuICAgIGZvY3VzZWRPcHRpb24sXG4gICAgaGFuZGxlSW5wdXRNb2RlVG9nZ2xlLFxuICAgIGhhbmRsZVJlamVjdCxcbiAgICBoYW5kbGVGb2N1cyxcbiAgfSA9IHVzZVNoZWxsUGVybWlzc2lvbkZlZWRiYWNrKHtcbiAgICB0b29sVXNlQ29uZmlybSxcbiAgICBvbkRvbmUsXG4gICAgb25SZWplY3QsXG4gICAgZXhwbGFpbmVyVmlzaWJsZTogZXhwbGFpbmVyU3RhdGUudmlzaWJsZSxcbiAgfSlcbiAgY29uc3QgW3Nob3dQZXJtaXNzaW9uRGVidWcsIHNldFNob3dQZXJtaXNzaW9uRGVidWddID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtjbGFzc2lmaWVyRGVzY3JpcHRpb24sIHNldENsYXNzaWZpZXJEZXNjcmlwdGlvbl0gPSB1c2VTdGF0ZShcbiAgICBkZXNjcmlwdGlvbiB8fCAnJyxcbiAgKVxuICAvLyBUcmFjayB3aGV0aGVyIHRoZSBpbml0aWFsIGRlc2NyaXB0aW9uIChmcm9tIHByb3Agb3IgYXN5bmMgZ2VuZXJhdGlvbikgd2FzIGVtcHR5LlxuICAvLyBPbmNlIHdlIHJlY2VpdmUgYSBub24tZW1wdHkgZGVzY3JpcHRpb24sIHRoaXMgc3RheXMgZmFsc2UuXG4gIGNvbnN0IFtcbiAgICBpbml0aWFsQ2xhc3NpZmllckRlc2NyaXB0aW9uRW1wdHksXG4gICAgc2V0SW5pdGlhbENsYXNzaWZpZXJEZXNjcmlwdGlvbkVtcHR5LFxuICBdID0gdXNlU3RhdGUoIWRlc2NyaXB0aW9uPy50cmltKCkpXG5cbiAgLy8gQXN5bmNocm9ub3VzbHkgZ2VuZXJhdGUgYSBnZW5lcmljIGRlc2NyaXB0aW9uIGZvciB0aGUgY2xhc3NpZmllclxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghaXNDbGFzc2lmaWVyUGVybWlzc2lvbnNFbmFibGVkKCkpIHJldHVyblxuXG4gICAgY29uc3QgYWJvcnRDb250cm9sbGVyID0gbmV3IEFib3J0Q29udHJvbGxlcigpXG4gICAgZ2VuZXJhdGVHZW5lcmljRGVzY3JpcHRpb24oY29tbWFuZCwgZGVzY3JpcHRpb24sIGFib3J0Q29udHJvbGxlci5zaWduYWwpXG4gICAgICAudGhlbihnZW5lcmljID0+IHtcbiAgICAgICAgaWYgKGdlbmVyaWMgJiYgIWFib3J0Q29udHJvbGxlci5zaWduYWwuYWJvcnRlZCkge1xuICAgICAgICAgIHNldENsYXNzaWZpZXJEZXNjcmlwdGlvbihnZW5lcmljKVxuICAgICAgICAgIHNldEluaXRpYWxDbGFzc2lmaWVyRGVzY3JpcHRpb25FbXB0eShmYWxzZSlcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICAgIC5jYXRjaCgoKSA9PiB7fSkgLy8gS2VlcCBvcmlnaW5hbCBvbiBlcnJvclxuICAgIHJldHVybiAoKSA9PiBhYm9ydENvbnRyb2xsZXIuYWJvcnQoKVxuICB9LCBbY29tbWFuZCwgZGVzY3JpcHRpb25dKVxuXG4gIC8vIEdIIzExMzgwOiBGb3IgY29tcG91bmQgY29tbWFuZHMgKGNkIHNyYyAmJiBnaXQgc3RhdHVzICYmIG5wbSB0ZXN0KSwgdGhlXG4gIC8vIGJhY2tlbmQgYWxyZWFkeSBjb21wdXRlZCBjb3JyZWN0IHBlci1zdWJjb21tYW5kIHN1Z2dlc3Rpb25zIHZpYSB0cmVlLXNpdHRlclxuICAvLyBzcGxpdCArIHBlci1zdWJjb21tYW5kIHBlcm1pc3Npb24gY2hlY2tzLiBkZWNpc2lvblJlYXNvbi50eXBlID09PVxuICAvLyAnc3ViY29tbWFuZFJlc3VsdHMnIG1hcmtzIHRoaXMgcGF0aC4gVGhlIHN5bmMgcHJlZml4IGhldXJpc3RpY3MgYmVsb3dcbiAgLy8gKGdldFNpbXBsZUNvbW1hbmRQcmVmaXgvZ2V0Rmlyc3RXb3JkUHJlZml4KSBvcGVyYXRlIG9uIHRoZSBGVUxMIGNvbXBvdW5kXG4gIC8vIHN0cmluZyBhbmQgcGljayB0aGUgZmlyc3QgdHdvIHdvcmRzIOKAlCBwcm9kdWNpbmcgZGVhZCBydWxlcyBsaWtlXG4gIC8vIGBCYXNoKGNkIHNyYzoqKWAgb3IgYEJhc2goLi9zY3JpcHQuc2ggJiYgbnBtIHRlc3QpYCB0aGF0IG5ldmVyIG1hdGNoIGFnYWluLlxuICAvLyBVc2VycyBhY2N1bXVsYXRlIDE1MCsgb2YgdGhlc2UgaW4gc2V0dGluZ3MubG9jYWwuanNvbi5cbiAgLy9cbiAgLy8gV2hlbiBjb21wb3VuZCB3aXRoIGV4YWN0bHkgb25lIEJhc2ggcnVsZSAoZS5nLiBgY2Qgc3JjICYmIG5wbSB0ZXN0YCB3aGVyZVxuICAvLyBjZCBpcyByZWFkLW9ubHkg4oaSIG9ubHkgbnBtIHRlc3QgbmVlZHMgYXBwcm92YWwpLCBzZWVkIHRoZSBlZGl0YWJsZSBpbnB1dFxuICAvLyBmcm9tIHRoZSBiYWNrZW5kIHJ1bGUuIFdoZW4gY29tcG91bmQgd2l0aCAyKyBydWxlcywgZWRpdGFibGVQcmVmaXggc3RheXNcbiAgLy8gdW5kZWZpbmVkIHNvIGJhc2hUb29sVXNlT3B0aW9ucyBmYWxscyB0aHJvdWdoIHRvIHllcy1hcHBseS1zdWdnZXN0aW9ucyxcbiAgLy8gd2hpY2ggc2F2ZXMgYWxsIHBlci1zdWJjb21tYW5kIHJ1bGVzIGF0b21pY2FsbHkuXG4gIGNvbnN0IGlzQ29tcG91bmQgPVxuICAgIHRvb2xVc2VDb25maXJtLnBlcm1pc3Npb25SZXN1bHQuZGVjaXNpb25SZWFzb24/LnR5cGUgPT09ICdzdWJjb21tYW5kUmVzdWx0cydcblxuICAvLyBFZGl0YWJsZSBwcmVmaXgg4oCUIGluaXRpYWxpemUgc3luY2hyb25vdXNseSB3aXRoIHRoZSBiZXN0IHByZWZpeCB3ZSBjYW5cbiAgLy8gZXh0cmFjdCB3aXRob3V0IHRyZWUtc2l0dGVyLCB0aGVuIHJlZmluZSB2aWEgdHJlZS1zaXR0ZXIgZm9yIGNvbXBvdW5kXG4gIC8vIGNvbW1hbmRzLiBUaGUgc3luYyBwYXRoIG1hdHRlcnMgYmVjYXVzZSBUUkVFX1NJVFRFUl9CQVNIIGlzIGdhdGVkXG4gIC8vIGFudC1vbmx5OiBpbiBleHRlcm5hbCBidWlsZHMgdGhlIGFzeW5jIHJlZmluZW1lbnQgYmVsb3cgYWx3YXlzIHJlc29sdmVzXG4gIC8vIHRvIFtdIGFuZCB0aGlzIGluaXRpYWwgdmFsdWUgaXMgd2hhdCB0aGUgdXNlciBzZWVzLlxuICAvL1xuICAvLyBMYXp5IGluaXRpYWxpemVyOiB0aGlzIHJ1bnMgcmVnZXggKyBzcGxpdCBvbiBldmVyeSByZW5kZXIgaWYgbGVmdCBpblxuICAvLyB0aGUgcmVuZGVyIGJvZHk7IGl0J3Mgb25seSBuZWVkZWQgZm9yIGluaXRpYWwgc3RhdGUuXG4gIGNvbnN0IFtlZGl0YWJsZVByZWZpeCwgc2V0RWRpdGFibGVQcmVmaXhdID0gdXNlU3RhdGU8c3RyaW5nIHwgdW5kZWZpbmVkPihcbiAgICAoKSA9PiB7XG4gICAgICBpZiAoaXNDb21wb3VuZCkge1xuICAgICAgICAvLyBCYWNrZW5kIHN1Z2dlc3Rpb24gaXMgdGhlIHNvdXJjZSBvZiB0cnV0aCBmb3IgY29tcG91bmQgY29tbWFuZHMuXG4gICAgICAgIC8vIFNpbmdsZSBydWxlIOKGkiBzZWVkIHRoZSBlZGl0YWJsZSBpbnB1dCBzbyB0aGUgdXNlciBjYW4gcmVmaW5lIGl0LlxuICAgICAgICAvLyBNdWx0aXBsZS96ZXJvIHJ1bGVzIOKGkiB1bmRlZmluZWQg4oaSIHllcy1hcHBseS1zdWdnZXN0aW9ucyBoYW5kbGVzIGl0LlxuICAgICAgICBjb25zdCBiYWNrZW5kQmFzaFJ1bGVzID0gZXh0cmFjdFJ1bGVzKFxuICAgICAgICAgICdzdWdnZXN0aW9ucycgaW4gdG9vbFVzZUNvbmZpcm0ucGVybWlzc2lvblJlc3VsdFxuICAgICAgICAgICAgPyB0b29sVXNlQ29uZmlybS5wZXJtaXNzaW9uUmVzdWx0LnN1Z2dlc3Rpb25zXG4gICAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICAgICAgKS5maWx0ZXIociA9PiByLnRvb2xOYW1lID09PSBCYXNoVG9vbC5uYW1lICYmIHIucnVsZUNvbnRlbnQpXG4gICAgICAgIHJldHVybiBiYWNrZW5kQmFzaFJ1bGVzLmxlbmd0aCA9PT0gMVxuICAgICAgICAgID8gYmFja2VuZEJhc2hSdWxlc1swXSEucnVsZUNvbnRlbnRcbiAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgfVxuICAgICAgY29uc3QgdHdvID0gZ2V0U2ltcGxlQ29tbWFuZFByZWZpeChjb21tYW5kKVxuICAgICAgaWYgKHR3bykgcmV0dXJuIGAke3R3b306KmBcbiAgICAgIGNvbnN0IG9uZSA9IGdldEZpcnN0V29yZFByZWZpeChjb21tYW5kKVxuICAgICAgaWYgKG9uZSkgcmV0dXJuIGAke29uZX06KmBcbiAgICAgIHJldHVybiBjb21tYW5kXG4gICAgfSxcbiAgKVxuICBjb25zdCBoYXNVc2VyRWRpdGVkUHJlZml4ID0gdXNlUmVmKGZhbHNlKVxuICBjb25zdCBvbkVkaXRhYmxlUHJlZml4Q2hhbmdlID0gdXNlQ2FsbGJhY2soKHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICBoYXNVc2VyRWRpdGVkUHJlZml4LmN1cnJlbnQgPSB0cnVlXG4gICAgc2V0RWRpdGFibGVQcmVmaXgodmFsdWUpXG4gIH0sIFtdKVxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIC8vIFNraXAgYXN5bmMgcmVmaW5lbWVudCBmb3IgY29tcG91bmQgY29tbWFuZHMg4oCUIHRoZSBiYWNrZW5kIGFscmVhZHkgcmFuXG4gICAgLy8gdGhlIGZ1bGwgcGVyLXN1YmNvbW1hbmQgYW5hbHlzaXMgYW5kIGl0cyBzdWdnZXN0aW9uIGlzIGNvcnJlY3QuXG4gICAgaWYgKGlzQ29tcG91bmQpIHJldHVyblxuICAgIGxldCBjYW5jZWxsZWQgPSBmYWxzZVxuICAgIGdldENvbXBvdW5kQ29tbWFuZFByZWZpeGVzU3RhdGljKGNvbW1hbmQsIHN1YmNtZCA9PlxuICAgICAgQmFzaFRvb2wuaXNSZWFkT25seSh7IGNvbW1hbmQ6IHN1YmNtZCB9KSxcbiAgICApXG4gICAgICAudGhlbihwcmVmaXhlcyA9PiB7XG4gICAgICAgIGlmIChjYW5jZWxsZWQgfHwgaGFzVXNlckVkaXRlZFByZWZpeC5jdXJyZW50KSByZXR1cm5cbiAgICAgICAgaWYgKHByZWZpeGVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBzZXRFZGl0YWJsZVByZWZpeChgJHtwcmVmaXhlc1swXX06KmApXG4gICAgICAgIH1cbiAgICAgIH0pXG4gICAgICAuY2F0Y2goKCkgPT4ge30pIC8vIEtlZXAgc3luYyBwcmVmaXggb24gdHJlZS1zaXR0ZXIgZmFpbHVyZVxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBjYW5jZWxsZWQgPSB0cnVlXG4gICAgfVxuICB9LCBbY29tbWFuZCwgaXNDb21wb3VuZF0pXG5cbiAgLy8gVHJhY2sgd2hldGhlciBjbGFzc2lmaWVyIGNoZWNrIHdhcyBldmVyIGluIHByb2dyZXNzIChwZXJzaXN0cyBhZnRlciBjb21wbGV0aW9uKS5cbiAgLy8gY2xhc3NpZmllckNoZWNrSW5Qcm9ncmVzcyBpcyBzZXQgb25jZSBhdCBxdWV1ZS1wdXNoIHRpbWUgKGludGVyYWN0aXZlSGFuZGxlcilcbiAgLy8gYW5kIG9ubHkgZXZlciB0cmFuc2l0aW9ucyB0cnVl4oaSZmFsc2UsIHNvIGNhcHR1cmluZyB0aGUgbW91bnQtdGltZSB2YWx1ZSBpc1xuICAvLyBzdWZmaWNpZW50IOKAlCBubyBsYXRjaC9yZWYgbmVlZGVkLiBUaGUgZmVhdHVyZSgpIHRlcm5hcnkga2VlcHMgdGhlIHByb3BlcnR5XG4gIC8vIHJlYWQgb3V0IG9mIGV4dGVybmFsIGJ1aWxkcyAoZm9yYmlkZGVuLXN0cmluZyBjaGVjaykuXG4gIGNvbnN0IFtjbGFzc2lmaWVyV2FzQ2hlY2tpbmddID0gdXNlU3RhdGUoXG4gICAgZmVhdHVyZSgnQkFTSF9DTEFTU0lGSUVSJylcbiAgICAgID8gISF0b29sVXNlQ29uZmlybS5jbGFzc2lmaWVyQ2hlY2tJblByb2dyZXNzXG4gICAgICA6IGZhbHNlLFxuICApXG5cbiAgLy8gVGhlc2UgZGVyaXZlIHNvbGVseSBmcm9tIHRoZSB0b29sIGlucHV0IChmaXhlZCBmb3IgdGhlIGRpYWxvZyBsaWZldGltZSkuXG4gIC8vIFRoZSBzaGltbWVyIGNsb2NrIHVzZWQgdG8gbGl2ZSBpbiB0aGlzIGNvbXBvbmVudCBhbmQgcmUtcmVuZGVyIGl0IGF0IDIwZnBzXG4gIC8vIHdoaWxlIHRoZSBjbGFzc2lmaWVyIHJhbiAoc2VlIENsYXNzaWZpZXJDaGVja2luZ1N1YnRpdGxlIGFib3ZlIGZvciB0aGVcbiAgLy8gZXh0cmFjdGlvbikuIFJlYWN0IENvbXBpbGVyIGNhbid0IGF1dG8tbWVtb2l6ZSBpbXBvcnRlZCBmdW5jdGlvbnMgKGNhbid0XG4gIC8vIHByb3ZlIHNpZGUtZWZmZWN0IGZyZWVkb20pLCBzbyB0aGlzIHVzZU1lbW8gc3RpbGwgZ3VhcmRzIGFnYWluc3QgYW55XG4gIC8vIHJlLXJlbmRlciBzb3VyY2UgKGUuZy4gSW5uZXIgc3RhdGUgdXBkYXRlcykuIFNhbWUgcGF0dGVybiBhcyBQUiMyMDczMC5cbiAgY29uc3QgeyBkZXN0cnVjdGl2ZVdhcm5pbmcsIHNhbmRib3hpbmdFbmFibGVkLCBpc1NhbmRib3hlZCB9ID0gdXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3QgZGVzdHJ1Y3RpdmVXYXJuaW5nID0gZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUoXG4gICAgICAndGVuZ3VfZGVzdHJ1Y3RpdmVfY29tbWFuZF93YXJuaW5nJyxcbiAgICAgIGZhbHNlLFxuICAgIClcbiAgICAgID8gZ2V0RGVzdHJ1Y3RpdmVDb21tYW5kV2FybmluZyhjb21tYW5kKVxuICAgICAgOiBudWxsXG5cbiAgICBjb25zdCBzYW5kYm94aW5nRW5hYmxlZCA9IFNhbmRib3hNYW5hZ2VyLmlzU2FuZGJveGluZ0VuYWJsZWQoKVxuICAgIGNvbnN0IGlzU2FuZGJveGVkID1cbiAgICAgIHNhbmRib3hpbmdFbmFibGVkICYmIHNob3VsZFVzZVNhbmRib3godG9vbFVzZUNvbmZpcm0uaW5wdXQpXG5cbiAgICByZXR1cm4geyBkZXN0cnVjdGl2ZVdhcm5pbmcsIHNhbmRib3hpbmdFbmFibGVkLCBpc1NhbmRib3hlZCB9XG4gIH0sIFtjb21tYW5kLCB0b29sVXNlQ29uZmlybS5pbnB1dF0pXG5cbiAgY29uc3QgdW5hcnlFdmVudCA9IHVzZU1lbW88VW5hcnlFdmVudD4oXG4gICAgKCkgPT4gKHsgY29tcGxldGlvbl90eXBlOiAndG9vbF91c2Vfc2luZ2xlJywgbGFuZ3VhZ2VfbmFtZTogJ25vbmUnIH0pLFxuICAgIFtdLFxuICApXG5cbiAgdXNlUGVybWlzc2lvblJlcXVlc3RMb2dnaW5nKHRvb2xVc2VDb25maXJtLCB1bmFyeUV2ZW50KVxuXG4gIGNvbnN0IGV4aXN0aW5nQWxsb3dEZXNjcmlwdGlvbnMgPSB1c2VNZW1vKFxuICAgICgpID0+IGdldEJhc2hQcm9tcHRBbGxvd0Rlc2NyaXB0aW9ucyh0b29sUGVybWlzc2lvbkNvbnRleHQpLFxuICAgIFt0b29sUGVybWlzc2lvbkNvbnRleHRdLFxuICApXG5cbiAgY29uc3Qgb3B0aW9ucyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIGJhc2hUb29sVXNlT3B0aW9ucyh7XG4gICAgICAgIHN1Z2dlc3Rpb25zOlxuICAgICAgICAgIHRvb2xVc2VDb25maXJtLnBlcm1pc3Npb25SZXN1bHQuYmVoYXZpb3IgPT09ICdhc2snXG4gICAgICAgICAgICA/IHRvb2xVc2VDb25maXJtLnBlcm1pc3Npb25SZXN1bHQuc3VnZ2VzdGlvbnNcbiAgICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgICAgICBkZWNpc2lvblJlYXNvbjogdG9vbFVzZUNvbmZpcm0ucGVybWlzc2lvblJlc3VsdC5kZWNpc2lvblJlYXNvbixcbiAgICAgICAgb25SZWplY3RGZWVkYmFja0NoYW5nZTogc2V0UmVqZWN0RmVlZGJhY2ssXG4gICAgICAgIG9uQWNjZXB0RmVlZGJhY2tDaGFuZ2U6IHNldEFjY2VwdEZlZWRiYWNrLFxuICAgICAgICBvbkNsYXNzaWZpZXJEZXNjcmlwdGlvbkNoYW5nZTogc2V0Q2xhc3NpZmllckRlc2NyaXB0aW9uLFxuICAgICAgICBjbGFzc2lmaWVyRGVzY3JpcHRpb24sXG4gICAgICAgIGluaXRpYWxDbGFzc2lmaWVyRGVzY3JpcHRpb25FbXB0eSxcbiAgICAgICAgZXhpc3RpbmdBbGxvd0Rlc2NyaXB0aW9ucyxcbiAgICAgICAgeWVzSW5wdXRNb2RlLFxuICAgICAgICBub0lucHV0TW9kZSxcbiAgICAgICAgZWRpdGFibGVQcmVmaXgsXG4gICAgICAgIG9uRWRpdGFibGVQcmVmaXhDaGFuZ2UsXG4gICAgICB9KSxcbiAgICBbXG4gICAgICB0b29sVXNlQ29uZmlybSxcbiAgICAgIGNsYXNzaWZpZXJEZXNjcmlwdGlvbixcbiAgICAgIGluaXRpYWxDbGFzc2lmaWVyRGVzY3JpcHRpb25FbXB0eSxcbiAgICAgIGV4aXN0aW5nQWxsb3dEZXNjcmlwdGlvbnMsXG4gICAgICB5ZXNJbnB1dE1vZGUsXG4gICAgICBub0lucHV0TW9kZSxcbiAgICAgIGVkaXRhYmxlUHJlZml4LFxuICAgICAgb25FZGl0YWJsZVByZWZpeENoYW5nZSxcbiAgICBdLFxuICApXG5cbiAgLy8gVG9nZ2xlIHBlcm1pc3Npb24gZGVidWcgaW5mbyB3aXRoIGtleWJpbmRpbmdcbiAgY29uc3QgaGFuZGxlVG9nZ2xlRGVidWcgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgc2V0U2hvd1Blcm1pc3Npb25EZWJ1ZyhwcmV2ID0+ICFwcmV2KVxuICB9LCBbXSlcbiAgdXNlS2V5YmluZGluZygncGVybWlzc2lvbjp0b2dnbGVEZWJ1ZycsIGhhbmRsZVRvZ2dsZURlYnVnLCB7XG4gICAgY29udGV4dDogJ0NvbmZpcm1hdGlvbicsXG4gIH0pXG5cbiAgLy8gQWxsb3cgRXNjIHRvIGRpc21pc3MgdGhlIGNoZWNrbWFyayBhZnRlciBhdXRvLWFwcHJvdmFsXG4gIGNvbnN0IGhhbmRsZURpc21pc3NDaGVja21hcmsgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgdG9vbFVzZUNvbmZpcm0ub25EaXNtaXNzQ2hlY2ttYXJrPy4oKVxuICB9LCBbdG9vbFVzZUNvbmZpcm1dKVxuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOm5vJywgaGFuZGxlRGlzbWlzc0NoZWNrbWFyaywge1xuICAgIGNvbnRleHQ6ICdDb25maXJtYXRpb24nLFxuICAgIGlzQWN0aXZlOiBmZWF0dXJlKCdCQVNIX0NMQVNTSUZJRVInKVxuICAgICAgPyAhIXRvb2xVc2VDb25maXJtLmNsYXNzaWZpZXJBdXRvQXBwcm92ZWRcbiAgICAgIDogZmFsc2UsXG4gIH0pXG5cbiAgZnVuY3Rpb24gb25TZWxlY3QodmFsdWU6IHN0cmluZykge1xuICAgIC8vIE1hcCBvcHRpb25zIHRvIG51bWVyaWMgdmFsdWVzIGZvciBhbmFseXRpY3MgKHN0cmluZ3Mgbm90IGFsbG93ZWQgaW4gbG9nRXZlbnQpXG4gICAgbGV0IG9wdGlvbkluZGV4OiBSZWNvcmQ8c3RyaW5nLCBudW1iZXI+ID0ge1xuICAgICAgeWVzOiAxLFxuICAgICAgJ3llcy1hcHBseS1zdWdnZXN0aW9ucyc6IDIsXG4gICAgICAneWVzLXByZWZpeC1lZGl0ZWQnOiAyLFxuICAgICAgbm86IDMsXG4gICAgfVxuICAgIGlmIChmZWF0dXJlKCdCQVNIX0NMQVNTSUZJRVInKSkge1xuICAgICAgb3B0aW9uSW5kZXggPSB7XG4gICAgICAgIHllczogMSxcbiAgICAgICAgJ3llcy1hcHBseS1zdWdnZXN0aW9ucyc6IDIsXG4gICAgICAgICd5ZXMtcHJlZml4LWVkaXRlZCc6IDIsXG4gICAgICAgICd5ZXMtY2xhc3NpZmllci1yZXZpZXdlZCc6IDMsXG4gICAgICAgIG5vOiA0LFxuICAgICAgfVxuICAgIH1cbiAgICBsb2dFdmVudCgndGVuZ3VfcGVybWlzc2lvbl9yZXF1ZXN0X29wdGlvbl9zZWxlY3RlZCcsIHtcbiAgICAgIG9wdGlvbl9pbmRleDogb3B0aW9uSW5kZXhbdmFsdWVdLFxuICAgICAgZXhwbGFpbmVyX3Zpc2libGU6IGV4cGxhaW5lclN0YXRlLnZpc2libGUsXG4gICAgfSlcblxuICAgIGNvbnN0IHRvb2xOYW1lRm9yQW5hbHl0aWNzID0gc2FuaXRpemVUb29sTmFtZUZvckFuYWx5dGljcyhcbiAgICAgIHRvb2xVc2VDb25maXJtLnRvb2wubmFtZSxcbiAgICApIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFNcblxuICAgIGlmICh2YWx1ZSA9PT0gJ3llcy1wcmVmaXgtZWRpdGVkJykge1xuICAgICAgY29uc3QgdHJpbW1lZFByZWZpeCA9IChlZGl0YWJsZVByZWZpeCA/PyAnJykudHJpbSgpXG4gICAgICBsb2dVbmFyeVBlcm1pc3Npb25FdmVudCgndG9vbF91c2Vfc2luZ2xlJywgdG9vbFVzZUNvbmZpcm0sICdhY2NlcHQnKVxuICAgICAgaWYgKCF0cmltbWVkUHJlZml4KSB7XG4gICAgICAgIHRvb2xVc2VDb25maXJtLm9uQWxsb3codG9vbFVzZUNvbmZpcm0uaW5wdXQsIFtdKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgcHJlZml4VXBkYXRlczogUGVybWlzc2lvblVwZGF0ZVtdID0gW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIHR5cGU6ICdhZGRSdWxlcycsXG4gICAgICAgICAgICBydWxlczogW1xuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdG9vbE5hbWU6IEJhc2hUb29sLm5hbWUsXG4gICAgICAgICAgICAgICAgcnVsZUNvbnRlbnQ6IHRyaW1tZWRQcmVmaXgsXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBdLFxuICAgICAgICAgICAgYmVoYXZpb3I6ICdhbGxvdycsXG4gICAgICAgICAgICBkZXN0aW5hdGlvbjogJ2xvY2FsU2V0dGluZ3MnLFxuICAgICAgICAgIH0sXG4gICAgICAgIF1cbiAgICAgICAgdG9vbFVzZUNvbmZpcm0ub25BbGxvdyh0b29sVXNlQ29uZmlybS5pbnB1dCwgcHJlZml4VXBkYXRlcylcbiAgICAgIH1cbiAgICAgIG9uRG9uZSgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoZmVhdHVyZSgnQkFTSF9DTEFTU0lGSUVSJykgJiYgdmFsdWUgPT09ICd5ZXMtY2xhc3NpZmllci1yZXZpZXdlZCcpIHtcbiAgICAgIGNvbnN0IHRyaW1tZWREZXNjcmlwdGlvbiA9IGNsYXNzaWZpZXJEZXNjcmlwdGlvbi50cmltKClcbiAgICAgIGxvZ1VuYXJ5UGVybWlzc2lvbkV2ZW50KCd0b29sX3VzZV9zaW5nbGUnLCB0b29sVXNlQ29uZmlybSwgJ2FjY2VwdCcpXG4gICAgICBpZiAoIXRyaW1tZWREZXNjcmlwdGlvbikge1xuICAgICAgICB0b29sVXNlQ29uZmlybS5vbkFsbG93KHRvb2xVc2VDb25maXJtLmlucHV0LCBbXSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IHBlcm1pc3Npb25VcGRhdGVzOiBQZXJtaXNzaW9uVXBkYXRlW10gPSBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgdHlwZTogJ2FkZFJ1bGVzJyxcbiAgICAgICAgICAgIHJ1bGVzOiBbXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB0b29sTmFtZTogQmFzaFRvb2wubmFtZSxcbiAgICAgICAgICAgICAgICBydWxlQ29udGVudDogY3JlYXRlUHJvbXB0UnVsZUNvbnRlbnQodHJpbW1lZERlc2NyaXB0aW9uKSxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIF0sXG4gICAgICAgICAgICBiZWhhdmlvcjogJ2FsbG93JyxcbiAgICAgICAgICAgIGRlc3RpbmF0aW9uOiAnc2Vzc2lvbicsXG4gICAgICAgICAgfSxcbiAgICAgICAgXVxuICAgICAgICB0b29sVXNlQ29uZmlybS5vbkFsbG93KHRvb2xVc2VDb25maXJtLmlucHV0LCBwZXJtaXNzaW9uVXBkYXRlcylcbiAgICAgIH1cbiAgICAgIG9uRG9uZSgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBzd2l0Y2ggKHZhbHVlKSB7XG4gICAgICBjYXNlICd5ZXMnOiB7XG4gICAgICAgIGNvbnN0IHRyaW1tZWRGZWVkYmFjayA9IGFjY2VwdEZlZWRiYWNrLnRyaW0oKVxuICAgICAgICBsb2dVbmFyeVBlcm1pc3Npb25FdmVudCgndG9vbF91c2Vfc2luZ2xlJywgdG9vbFVzZUNvbmZpcm0sICdhY2NlcHQnKVxuICAgICAgICAvLyBMb2cgYWNjZXB0IHN1Ym1pc3Npb24gd2l0aCBmZWVkYmFjayBjb250ZXh0XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9hY2NlcHRfc3VibWl0dGVkJywge1xuICAgICAgICAgIHRvb2xOYW1lOiB0b29sTmFtZUZvckFuYWx5dGljcyxcbiAgICAgICAgICBpc01jcDogdG9vbFVzZUNvbmZpcm0udG9vbC5pc01jcCA/PyBmYWxzZSxcbiAgICAgICAgICBoYXNfaW5zdHJ1Y3Rpb25zOiAhIXRyaW1tZWRGZWVkYmFjayxcbiAgICAgICAgICBpbnN0cnVjdGlvbnNfbGVuZ3RoOiB0cmltbWVkRmVlZGJhY2subGVuZ3RoLFxuICAgICAgICAgIGVudGVyZWRfZmVlZGJhY2tfbW9kZTogeWVzRmVlZGJhY2tNb2RlRW50ZXJlZCxcbiAgICAgICAgfSlcbiAgICAgICAgdG9vbFVzZUNvbmZpcm0ub25BbGxvdyhcbiAgICAgICAgICB0b29sVXNlQ29uZmlybS5pbnB1dCxcbiAgICAgICAgICBbXSxcbiAgICAgICAgICB0cmltbWVkRmVlZGJhY2sgfHwgdW5kZWZpbmVkLFxuICAgICAgICApXG4gICAgICAgIG9uRG9uZSgpXG4gICAgICAgIGJyZWFrXG4gICAgICB9XG4gICAgICBjYXNlICd5ZXMtYXBwbHktc3VnZ2VzdGlvbnMnOiB7XG4gICAgICAgIGxvZ1VuYXJ5UGVybWlzc2lvbkV2ZW50KCd0b29sX3VzZV9zaW5nbGUnLCB0b29sVXNlQ29uZmlybSwgJ2FjY2VwdCcpXG4gICAgICAgIC8vIEV4dHJhY3Qgc3VnZ2VzdGlvbnMgaWYgcHJlc2VudCAod29ya3MgZm9yIGJvdGggJ2FzaycgYW5kICdwYXNzdGhyb3VnaCcgYmVoYXZpb3JzKVxuICAgICAgICBjb25zdCBwZXJtaXNzaW9uVXBkYXRlcyA9XG4gICAgICAgICAgJ3N1Z2dlc3Rpb25zJyBpbiB0b29sVXNlQ29uZmlybS5wZXJtaXNzaW9uUmVzdWx0XG4gICAgICAgICAgICA/IHRvb2xVc2VDb25maXJtLnBlcm1pc3Npb25SZXN1bHQuc3VnZ2VzdGlvbnMgfHwgW11cbiAgICAgICAgICAgIDogW11cbiAgICAgICAgdG9vbFVzZUNvbmZpcm0ub25BbGxvdyh0b29sVXNlQ29uZmlybS5pbnB1dCwgcGVybWlzc2lvblVwZGF0ZXMpXG4gICAgICAgIG9uRG9uZSgpXG4gICAgICAgIGJyZWFrXG4gICAgICB9XG4gICAgICBjYXNlICdubyc6IHtcbiAgICAgICAgY29uc3QgdHJpbW1lZEZlZWRiYWNrID0gcmVqZWN0RmVlZGJhY2sudHJpbSgpXG5cbiAgICAgICAgLy8gTG9nIHJlamVjdCBzdWJtaXNzaW9uIHdpdGggZmVlZGJhY2sgY29udGV4dFxuICAgICAgICBsb2dFdmVudCgndGVuZ3VfcmVqZWN0X3N1Ym1pdHRlZCcsIHtcbiAgICAgICAgICB0b29sTmFtZTogdG9vbE5hbWVGb3JBbmFseXRpY3MsXG4gICAgICAgICAgaXNNY3A6IHRvb2xVc2VDb25maXJtLnRvb2wuaXNNY3AgPz8gZmFsc2UsXG4gICAgICAgICAgaGFzX2luc3RydWN0aW9uczogISF0cmltbWVkRmVlZGJhY2ssXG4gICAgICAgICAgaW5zdHJ1Y3Rpb25zX2xlbmd0aDogdHJpbW1lZEZlZWRiYWNrLmxlbmd0aCxcbiAgICAgICAgICBlbnRlcmVkX2ZlZWRiYWNrX21vZGU6IG5vRmVlZGJhY2tNb2RlRW50ZXJlZCxcbiAgICAgICAgfSlcblxuICAgICAgICAvLyBQcm9jZXNzIHJlamVjdGlvbiAod2l0aCBvciB3aXRob3V0IGZlZWRiYWNrKVxuICAgICAgICBoYW5kbGVSZWplY3QodHJpbW1lZEZlZWRiYWNrIHx8IHVuZGVmaW5lZClcbiAgICAgICAgYnJlYWtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBjb25zdCBjbGFzc2lmaWVyU3VidGl0bGUgPSBmZWF0dXJlKCdCQVNIX0NMQVNTSUZJRVInKSA/IChcbiAgICB0b29sVXNlQ29uZmlybS5jbGFzc2lmaWVyQXV0b0FwcHJvdmVkID8gKFxuICAgICAgPFRleHQ+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwic3VjY2Vzc1wiPntmaWd1cmVzLnRpY2t9IEF1dG8tYXBwcm92ZWQ8L1RleHQ+XG4gICAgICAgIHt0b29sVXNlQ29uZmlybS5jbGFzc2lmaWVyTWF0Y2hlZFJ1bGUgJiYgKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgeycgXFx1MDBiNyBtYXRjaGVkIFwiJ31cbiAgICAgICAgICAgIHt0b29sVXNlQ29uZmlybS5jbGFzc2lmaWVyTWF0Y2hlZFJ1bGV9XG4gICAgICAgICAgICB7J1wiJ31cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICA8L1RleHQ+XG4gICAgKSA6IHRvb2xVc2VDb25maXJtLmNsYXNzaWZpZXJDaGVja0luUHJvZ3Jlc3MgPyAoXG4gICAgICA8Q2xhc3NpZmllckNoZWNraW5nU3VidGl0bGUgLz5cbiAgICApIDogY2xhc3NpZmllcldhc0NoZWNraW5nID8gKFxuICAgICAgPFRleHQgZGltQ29sb3I+UmVxdWlyZXMgbWFudWFsIGFwcHJvdmFsPC9UZXh0PlxuICAgICkgOiB1bmRlZmluZWRcbiAgKSA6IHVuZGVmaW5lZFxuXG4gIHJldHVybiAoXG4gICAgPFBlcm1pc3Npb25EaWFsb2dcbiAgICAgIHdvcmtlckJhZGdlPXt3b3JrZXJCYWRnZX1cbiAgICAgIHRpdGxlPXtcbiAgICAgICAgc2FuZGJveGluZ0VuYWJsZWQgJiYgIWlzU2FuZGJveGVkXG4gICAgICAgICAgPyAnQmFzaCBjb21tYW5kICh1bnNhbmRib3hlZCknXG4gICAgICAgICAgOiAnQmFzaCBjb21tYW5kJ1xuICAgICAgfVxuICAgICAgc3VidGl0bGU9e2NsYXNzaWZpZXJTdWJ0aXRsZX1cbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nWD17Mn0gcGFkZGluZ1k9ezF9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj17ZXhwbGFpbmVyU3RhdGUudmlzaWJsZX0+XG4gICAgICAgICAge0Jhc2hUb29sLnJlbmRlclRvb2xVc2VNZXNzYWdlKFxuICAgICAgICAgICAgeyBjb21tYW5kLCBkZXNjcmlwdGlvbiB9LFxuICAgICAgICAgICAgeyB0aGVtZSwgdmVyYm9zZTogdHJ1ZSB9LCAvLyBhbHdheXMgc2hvdyB0aGUgZnVsbCBjb21tYW5kXG4gICAgICAgICAgKX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICB7IWV4cGxhaW5lclN0YXRlLnZpc2libGUgJiYgKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPnt0b29sVXNlQ29uZmlybS5kZXNjcmlwdGlvbn08L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIDxQZXJtaXNzaW9uRXhwbGFpbmVyQ29udGVudFxuICAgICAgICAgIHZpc2libGU9e2V4cGxhaW5lclN0YXRlLnZpc2libGV9XG4gICAgICAgICAgcHJvbWlzZT17ZXhwbGFpbmVyU3RhdGUucHJvbWlzZX1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgICAge3Nob3dQZXJtaXNzaW9uRGVidWcgPyAoXG4gICAgICAgIDw+XG4gICAgICAgICAgPFBlcm1pc3Npb25EZWNpc2lvbkRlYnVnSW5mb1xuICAgICAgICAgICAgcGVybWlzc2lvblJlc3VsdD17dG9vbFVzZUNvbmZpcm0ucGVybWlzc2lvblJlc3VsdH1cbiAgICAgICAgICAgIHRvb2xOYW1lPVwiQmFzaFwiXG4gICAgICAgICAgLz5cbiAgICAgICAgICB7dG9vbFVzZUNvbnRleHQub3B0aW9ucy5kZWJ1ZyAmJiAoXG4gICAgICAgICAgICA8Qm94IGp1c3RpZnlDb250ZW50PVwiZmxleC1lbmRcIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5DdHJsLUQgdG8gaGlkZSBkZWJ1ZyBpbmZvPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC8+XG4gICAgICApIDogKFxuICAgICAgICA8PlxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPFBlcm1pc3Npb25SdWxlRXhwbGFuYXRpb25cbiAgICAgICAgICAgICAgcGVybWlzc2lvblJlc3VsdD17dG9vbFVzZUNvbmZpcm0ucGVybWlzc2lvblJlc3VsdH1cbiAgICAgICAgICAgICAgdG9vbFR5cGU9XCJjb21tYW5kXCJcbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgICB7ZGVzdHJ1Y3RpdmVXYXJuaW5nICYmIChcbiAgICAgICAgICAgICAgPEJveCBtYXJnaW5Cb3R0b209ezF9PlxuICAgICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgICBjb2xvcj1cIndhcm5pbmdcIlxuICAgICAgICAgICAgICAgICAgZGltQ29sb3I9e1xuICAgICAgICAgICAgICAgICAgICBmZWF0dXJlKCdCQVNIX0NMQVNTSUZJRVInKVxuICAgICAgICAgICAgICAgICAgICAgID8gdG9vbFVzZUNvbmZpcm0uY2xhc3NpZmllckF1dG9BcHByb3ZlZFxuICAgICAgICAgICAgICAgICAgICAgIDogZmFsc2VcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICB7ZGVzdHJ1Y3RpdmVXYXJuaW5nfVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICApfVxuICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgZGltQ29sb3I9e1xuICAgICAgICAgICAgICAgIGZlYXR1cmUoJ0JBU0hfQ0xBU1NJRklFUicpXG4gICAgICAgICAgICAgICAgICA/IHRvb2xVc2VDb25maXJtLmNsYXNzaWZpZXJBdXRvQXBwcm92ZWRcbiAgICAgICAgICAgICAgICAgIDogZmFsc2VcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICBEbyB5b3Ugd2FudCB0byBwcm9jZWVkP1xuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgICBvcHRpb25zPXtcbiAgICAgICAgICAgICAgICBmZWF0dXJlKCdCQVNIX0NMQVNTSUZJRVInKVxuICAgICAgICAgICAgICAgICAgPyB0b29sVXNlQ29uZmlybS5jbGFzc2lmaWVyQXV0b0FwcHJvdmVkXG4gICAgICAgICAgICAgICAgICAgID8gb3B0aW9ucy5tYXAobyA9PiAoeyAuLi5vLCBkaXNhYmxlZDogdHJ1ZSB9KSlcbiAgICAgICAgICAgICAgICAgICAgOiBvcHRpb25zXG4gICAgICAgICAgICAgICAgICA6IG9wdGlvbnNcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBpc0Rpc2FibGVkPXtcbiAgICAgICAgICAgICAgICBmZWF0dXJlKCdCQVNIX0NMQVNTSUZJRVInKVxuICAgICAgICAgICAgICAgICAgPyB0b29sVXNlQ29uZmlybS5jbGFzc2lmaWVyQXV0b0FwcHJvdmVkXG4gICAgICAgICAgICAgICAgICA6IGZhbHNlXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaW5saW5lRGVzY3JpcHRpb25zXG4gICAgICAgICAgICAgIG9uQ2hhbmdlPXtvblNlbGVjdH1cbiAgICAgICAgICAgICAgb25DYW5jZWw9eygpID0+IGhhbmRsZVJlamVjdCgpfVxuICAgICAgICAgICAgICBvbkZvY3VzPXtoYW5kbGVGb2N1c31cbiAgICAgICAgICAgICAgb25JbnB1dE1vZGVUb2dnbGU9e2hhbmRsZUlucHV0TW9kZVRvZ2dsZX1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPEJveCBqdXN0aWZ5Q29udGVudD1cInNwYWNlLWJldHdlZW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIEVzYyB0byBjYW5jZWxcbiAgICAgICAgICAgICAgeygoZm9jdXNlZE9wdGlvbiA9PT0gJ3llcycgJiYgIXllc0lucHV0TW9kZSkgfHxcbiAgICAgICAgICAgICAgICAoZm9jdXNlZE9wdGlvbiA9PT0gJ25vJyAmJiAhbm9JbnB1dE1vZGUpKSAmJlxuICAgICAgICAgICAgICAgICcgwrcgVGFiIHRvIGFtZW5kJ31cbiAgICAgICAgICAgICAge2V4cGxhaW5lclN0YXRlLmVuYWJsZWQgJiZcbiAgICAgICAgICAgICAgICBgIMK3IGN0cmwrZSB0byAke2V4cGxhaW5lclN0YXRlLnZpc2libGUgPyAnaGlkZScgOiAnZXhwbGFpbid9YH1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIHt0b29sVXNlQ29udGV4dC5vcHRpb25zLmRlYnVnICYmIChcbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+Q3RybCtkIHRvIHNob3cgZGVidWcgaW5mbzwvVGV4dD5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvPlxuICAgICAgKX1cbiAgICA8L1Blcm1pc3Npb25EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLE9BQU8sUUFBUSxZQUFZO0FBQ3BDLE9BQU9DLE9BQU8sTUFBTSxTQUFTO0FBQzdCLE9BQU9DLEtBQUssSUFBSUMsV0FBVyxFQUFFQyxTQUFTLEVBQUVDLE9BQU8sRUFBRUMsTUFBTSxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUNoRixTQUFTQyxHQUFHLEVBQUVDLElBQUksRUFBRUMsUUFBUSxRQUFRLGlCQUFpQjtBQUNyRCxTQUFTQyxhQUFhLFFBQVEsdUNBQXVDO0FBQ3JFLFNBQVNDLG1DQUFtQyxRQUFRLDJDQUEyQztBQUMvRixTQUNFLEtBQUtDLDBEQUEwRCxFQUMvREMsUUFBUSxRQUNILHNDQUFzQztBQUM3QyxTQUFTQyw0QkFBNEIsUUFBUSx5Q0FBeUM7QUFDdEYsU0FBU0MsV0FBVyxRQUFRLDRCQUE0QjtBQUN4RCxTQUFTQyxRQUFRLFFBQVEscUNBQXFDO0FBQzlELFNBQ0VDLGtCQUFrQixFQUNsQkMsc0JBQXNCLFFBQ2pCLDRDQUE0QztBQUNuRCxTQUFTQyw0QkFBNEIsUUFBUSxzREFBc0Q7QUFDbkcsU0FBU0MsbUJBQW1CLFFBQVEsMENBQTBDO0FBQzlFLFNBQVNDLGdCQUFnQixRQUFRLDZDQUE2QztBQUM5RSxTQUFTQyxnQ0FBZ0MsUUFBUSwrQkFBK0I7QUFDaEYsU0FDRUMsdUJBQXVCLEVBQ3ZCQywwQkFBMEIsRUFDMUJDLDhCQUE4QixFQUM5QkMsOEJBQThCLFFBQ3pCLDhDQUE4QztBQUNy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QUMsaUJBQWlCLENBQUMsR0FDdEI4RCxjQUFjLENBQUNrRyxzQkFBc0IsR0FDckMsS0FDTixDQUFDO0FBRWY7QUFDQSxZQUFZLEVBQUUsSUFBSTtBQUNsQixZQUFZLENBQUMsTUFBTSxDQUNMLE9BQU8sQ0FBQyxDQUNOaEssT0FBTyxDQUFDLGlCQUFpQixDQUFDLEdBQ3RCOEQsY0FBYyxDQUFDa0csc0JBQXNCLEdBQ25DWCxPQUFPLENBQUM3RixHQUFHLENBQUNtSSxDQUFDLEtBQUs7VUFBRSxHQUFHQSxDQUFDO1VBQUVDLFFBQVEsRUFBRTtRQUFLLENBQUMsQ0FBQyxDQUFDLEdBQzVDdkMsT0FBTyxHQUNUQSxPQUNOLENBQUMsQ0FDRCxVQUFVLENBQUMsQ0FDVHJKLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxHQUN0QjhELGNBQWMsQ0FBQ2tHLHNCQUFzQixHQUNyQyxLQUNOLENBQUMsQ0FDRCxrQkFBa0IsQ0FDbEIsUUFBUSxDQUFDLENBQUNDLFFBQVEsQ0FBQyxDQUNuQixRQUFRLENBQUMsQ0FBQyxNQUFNaEUsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUMvQixPQUFPLENBQUMsQ0FBQ0MsV0FBVyxDQUFDLENBQ3JCLGlCQUFpQixDQUFDLENBQUNGLHFCQUFxQixDQUFDO0FBRXZELFVBQVUsRUFBRSxHQUFHO0FBQ2YsVUFBVSxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsZUFBZSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUMzRCxZQUFZLENBQUMsSUFBSSxDQUFDLFFBQVE7QUFDMUI7QUFDQSxjQUFjLENBQUMsQ0FBRUQsYUFBYSxLQUFLLEtBQUssSUFBSSxDQUFDUixZQUFZLElBQ3hDUSxhQUFhLEtBQUssSUFBSSxJQUFJLENBQUNQLFdBQVksS0FDeEMsaUJBQWlCO0FBQ2pDLGNBQWMsQ0FBQ1IsY0FBYyxDQUFDNkcsT0FBTyxJQUNyQixnQkFBZ0I3RyxjQUFjLENBQUNvQixPQUFPLEdBQUcsTUFBTSxHQUFHLFNBQVMsRUFBRTtBQUM3RSxZQUFZLEVBQUUsSUFBSTtBQUNsQixZQUFZLENBQUNyQyxjQUFjLENBQUNzRixPQUFPLENBQUNxQyxLQUFLLElBQzNCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyx5QkFBeUIsRUFBRSxJQUFJLENBQy9DO0FBQ2IsVUFBVSxFQUFFLEdBQUc7QUFDZixRQUFRLEdBQ0Q7QUFDUCxJQUFJLEVBQUUsZ0JBQWdCLENBQUM7QUFFdkIiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/permissions/BashPermissionRequest/bashToolUseOptions.tsx b/components/permissions/BashPermissionRequest/bashToolUseOptions.tsx new file mode 100644 index 0000000..649e92e --- /dev/null +++ b/components/permissions/BashPermissionRequest/bashToolUseOptions.tsx @@ -0,0 +1,147 @@ +import { BASH_TOOL_NAME } from '../../../tools/BashTool/toolName.js'; +import { extractOutputRedirections } from '../../../utils/bash/commands.js'; +import { isClassifierPermissionsEnabled } from '../../../utils/permissions/bashClassifier.js'; +import type { PermissionDecisionReason } from '../../../utils/permissions/PermissionResult.js'; +import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js'; +import { shouldShowAlwaysAllowOptions } from '../../../utils/permissions/permissionsLoader.js'; +import type { OptionWithDescription } from '../../CustomSelect/select.js'; +import { generateShellSuggestionsLabel } from '../shellPermissionHelpers.js'; +export type BashToolUseOption = 'yes' | 'yes-apply-suggestions' | 'yes-prefix-edited' | 'yes-classifier-reviewed' | 'no'; + +/** + * Check if a description already exists in the allow list. + * Compares lowercase and trailing-whitespace-trimmed versions. + */ +function descriptionAlreadyExists(description: string, existingDescriptions: string[]): boolean { + const normalized = description.toLowerCase().trimEnd(); + return existingDescriptions.some(existing => existing.toLowerCase().trimEnd() === normalized); +} + +/** + * Strip output redirections so filenames don't show as commands in the label. + */ +function stripBashRedirections(command: string): string { + const { + commandWithoutRedirections, + redirections + } = extractOutputRedirections(command); + // Only use stripped version if there were actual redirections + return redirections.length > 0 ? commandWithoutRedirections : command; +} +export function bashToolUseOptions({ + suggestions = [], + decisionReason, + onRejectFeedbackChange, + onAcceptFeedbackChange, + onClassifierDescriptionChange, + classifierDescription, + initialClassifierDescriptionEmpty = false, + existingAllowDescriptions = [], + yesInputMode = false, + noInputMode = false, + editablePrefix, + onEditablePrefixChange +}: { + suggestions?: PermissionUpdate[]; + decisionReason?: PermissionDecisionReason; + onRejectFeedbackChange: (value: string) => void; + onAcceptFeedbackChange: (value: string) => void; + onClassifierDescriptionChange?: (value: string) => void; + classifierDescription?: string; + /** Whether the initial classifier description was empty. When true, hides the option. */ + initialClassifierDescriptionEmpty?: boolean; + existingAllowDescriptions?: string[]; + yesInputMode?: boolean; + noInputMode?: boolean; + /** Editable prefix rule content (e.g., "npm run:*"). When set, replaces Haiku-based suggestions. */ + editablePrefix?: string; + /** Callback when the user edits the prefix value. */ + onEditablePrefixChange?: (value: string) => void; +}): OptionWithDescription[] { + const options: OptionWithDescription[] = []; + if (yesInputMode) { + options.push({ + type: 'input', + label: 'Yes', + value: 'yes', + placeholder: 'and tell Claude what to do next', + onChange: onAcceptFeedbackChange, + allowEmptySubmitToCancel: true + }); + } else { + options.push({ + label: 'Yes', + value: 'yes' + }); + } + + // Only show "always allow" options when not restricted by allowManagedPermissionRulesOnly + if (shouldShowAlwaysAllowOptions()) { + // Show an editable input for the prefix rule instead of the + // Haiku-generated suggestion label — but only when the suggestions + // don't contain non-Bash items (addDirectories, Read rules) that + // the editable prefix can't represent. + const hasNonBashSuggestions = suggestions.some(s => s.type === 'addDirectories' || s.type === 'addRules' && s.rules?.some(r => r.toolName !== BASH_TOOL_NAME)); + if (editablePrefix !== undefined && onEditablePrefixChange && !hasNonBashSuggestions && suggestions.length > 0) { + options.push({ + type: 'input', + label: 'Yes, and don\u2019t ask again for', + value: 'yes-prefix-edited', + placeholder: 'command prefix (e.g., npm run:*)', + initialValue: editablePrefix, + onChange: onEditablePrefixChange, + allowEmptySubmitToCancel: true, + showLabelWithValue: true, + labelValueSeparator: ': ', + resetCursorOnUpdate: true + }); + } else if (suggestions.length > 0) { + const label = generateShellSuggestionsLabel(suggestions, BASH_TOOL_NAME, stripBashRedirections); + if (label) { + options.push({ + label, + value: 'yes-apply-suggestions' + }); + } + } + + // Add classifier-reviewed option if enabled, the initial description was + // non-empty, the description doesn't already exist in the allow list, + // and the decision reason is NOT a server-side classifier block + // (prompt-based rules don't help when the server-side classifier triggers first). + // Skip when the editable prefix option is already shown — they serve the + // same role and having two identical-looking "don't ask again" inputs is confusing. + const editablePrefixShown = options.some(o => o.value === 'yes-prefix-edited'); + if ("external" === 'ant' && !editablePrefixShown && isClassifierPermissionsEnabled() && onClassifierDescriptionChange && !initialClassifierDescriptionEmpty && !descriptionAlreadyExists(classifierDescription ?? '', existingAllowDescriptions) && decisionReason?.type !== 'classifier') { + options.push({ + type: 'input', + label: 'Yes, and don\u2019t ask again for', + value: 'yes-classifier-reviewed', + placeholder: 'describe what to allow...', + initialValue: classifierDescription ?? '', + onChange: onClassifierDescriptionChange, + allowEmptySubmitToCancel: true, + showLabelWithValue: true, + labelValueSeparator: ': ', + resetCursorOnUpdate: true + }); + } + } + if (noInputMode) { + options.push({ + type: 'input', + label: 'No', + value: 'no', + placeholder: 'and tell Claude what to do differently', + onChange: onRejectFeedbackChange, + allowEmptySubmitToCancel: true + }); + } else { + options.push({ + label: 'No', + value: 'no' + }); + } + return options; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJCQVNIX1RPT0xfTkFNRSIsImV4dHJhY3RPdXRwdXRSZWRpcmVjdGlvbnMiLCJpc0NsYXNzaWZpZXJQZXJtaXNzaW9uc0VuYWJsZWQiLCJQZXJtaXNzaW9uRGVjaXNpb25SZWFzb24iLCJQZXJtaXNzaW9uVXBkYXRlIiwic2hvdWxkU2hvd0Fsd2F5c0FsbG93T3B0aW9ucyIsIk9wdGlvbldpdGhEZXNjcmlwdGlvbiIsImdlbmVyYXRlU2hlbGxTdWdnZXN0aW9uc0xhYmVsIiwiQmFzaFRvb2xVc2VPcHRpb24iLCJkZXNjcmlwdGlvbkFscmVhZHlFeGlzdHMiLCJkZXNjcmlwdGlvbiIsImV4aXN0aW5nRGVzY3JpcHRpb25zIiwibm9ybWFsaXplZCIsInRvTG93ZXJDYXNlIiwidHJpbUVuZCIsInNvbWUiLCJleGlzdGluZyIsInN0cmlwQmFzaFJlZGlyZWN0aW9ucyIsImNvbW1hbmQiLCJjb21tYW5kV2l0aG91dFJlZGlyZWN0aW9ucyIsInJlZGlyZWN0aW9ucyIsImxlbmd0aCIsImJhc2hUb29sVXNlT3B0aW9ucyIsInN1Z2dlc3Rpb25zIiwiZGVjaXNpb25SZWFzb24iLCJvblJlamVjdEZlZWRiYWNrQ2hhbmdlIiwib25BY2NlcHRGZWVkYmFja0NoYW5nZSIsIm9uQ2xhc3NpZmllckRlc2NyaXB0aW9uQ2hhbmdlIiwiY2xhc3NpZmllckRlc2NyaXB0aW9uIiwiaW5pdGlhbENsYXNzaWZpZXJEZXNjcmlwdGlvbkVtcHR5IiwiZXhpc3RpbmdBbGxvd0Rlc2NyaXB0aW9ucyIsInllc0lucHV0TW9kZSIsIm5vSW5wdXRNb2RlIiwiZWRpdGFibGVQcmVmaXgiLCJvbkVkaXRhYmxlUHJlZml4Q2hhbmdlIiwidmFsdWUiLCJvcHRpb25zIiwicHVzaCIsInR5cGUiLCJsYWJlbCIsInBsYWNlaG9sZGVyIiwib25DaGFuZ2UiLCJhbGxvd0VtcHR5U3VibWl0VG9DYW5jZWwiLCJoYXNOb25CYXNoU3VnZ2VzdGlvbnMiLCJzIiwicnVsZXMiLCJyIiwidG9vbE5hbWUiLCJ1bmRlZmluZWQiLCJpbml0aWFsVmFsdWUiLCJzaG93TGFiZWxXaXRoVmFsdWUiLCJsYWJlbFZhbHVlU2VwYXJhdG9yIiwicmVzZXRDdXJzb3JPblVwZGF0ZSIsImVkaXRhYmxlUHJlZml4U2hvd24iLCJvIl0sInNvdXJjZXMiOlsiYmFzaFRvb2xVc2VPcHRpb25zLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBCQVNIX1RPT0xfTkFNRSB9IGZyb20gJy4uLy4uLy4uL3Rvb2xzL0Jhc2hUb29sL3Rvb2xOYW1lLmpzJ1xuaW1wb3J0IHsgZXh0cmFjdE91dHB1dFJlZGlyZWN0aW9ucyB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL2Jhc2gvY29tbWFuZHMuanMnXG5pbXBvcnQgeyBpc0NsYXNzaWZpZXJQZXJtaXNzaW9uc0VuYWJsZWQgfSBmcm9tICcuLi8uLi8uLi91dGlscy9wZXJtaXNzaW9ucy9iYXNoQ2xhc3NpZmllci5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvbkRlY2lzaW9uUmVhc29uIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvblJlc3VsdC5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvblVwZGF0ZSB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25VcGRhdGVTY2hlbWEuanMnXG5pbXBvcnQgeyBzaG91bGRTaG93QWx3YXlzQWxsb3dPcHRpb25zIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvcGVybWlzc2lvbnNMb2FkZXIuanMnXG5pbXBvcnQgdHlwZSB7IE9wdGlvbldpdGhEZXNjcmlwdGlvbiB9IGZyb20gJy4uLy4uL0N1c3RvbVNlbGVjdC9zZWxlY3QuanMnXG5pbXBvcnQgeyBnZW5lcmF0ZVNoZWxsU3VnZ2VzdGlvbnNMYWJlbCB9IGZyb20gJy4uL3NoZWxsUGVybWlzc2lvbkhlbHBlcnMuanMnXG5cbmV4cG9ydCB0eXBlIEJhc2hUb29sVXNlT3B0aW9uID1cbiAgfCAneWVzJ1xuICB8ICd5ZXMtYXBwbHktc3VnZ2VzdGlvbnMnXG4gIHwgJ3llcy1wcmVmaXgtZWRpdGVkJ1xuICB8ICd5ZXMtY2xhc3NpZmllci1yZXZpZXdlZCdcbiAgfCAnbm8nXG5cbi8qKlxuICogQ2hlY2sgaWYgYSBkZXNjcmlwdGlvbiBhbHJlYWR5IGV4aXN0cyBpbiB0aGUgYWxsb3cgbGlzdC5cbiAqIENvbXBhcmVzIGxvd2VyY2FzZSBhbmQgdHJhaWxpbmctd2hpdGVzcGFjZS10cmltbWVkIHZlcnNpb25zLlxuICovXG5mdW5jdGlvbiBkZXNjcmlwdGlvbkFscmVhZHlFeGlzdHMoXG4gIGRlc2NyaXB0aW9uOiBzdHJpbmcsXG4gIGV4aXN0aW5nRGVzY3JpcHRpb25zOiBzdHJpbmdbXSxcbik6IGJvb2xlYW4ge1xuICBjb25zdCBub3JtYWxpemVkID0gZGVzY3JpcHRpb24udG9Mb3dlckNhc2UoKS50cmltRW5kKClcbiAgcmV0dXJuIGV4aXN0aW5nRGVzY3JpcHRpb25zLnNvbWUoXG4gICAgZXhpc3RpbmcgPT4gZXhpc3RpbmcudG9Mb3dlckNhc2UoKS50cmltRW5kKCkgPT09IG5vcm1hbGl6ZWQsXG4gIClcbn1cblxuLyoqXG4gKiBTdHJpcCBvdXRwdXQgcmVkaXJlY3Rpb25zIHNvIGZpbGVuYW1lcyBkb24ndCBzaG93IGFzIGNvbW1hbmRzIGluIHRoZSBsYWJlbC5cbiAqL1xuZnVuY3Rpb24gc3RyaXBCYXNoUmVkaXJlY3Rpb25zKGNvbW1hbmQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHsgY29tbWFuZFdpdGhvdXRSZWRpcmVjdGlvbnMsIHJlZGlyZWN0aW9ucyB9ID1cbiAgICBleHRyYWN0T3V0cHV0UmVkaXJlY3Rpb25zKGNvbW1hbmQpXG4gIC8vIE9ubHkgdXNlIHN0cmlwcGVkIHZlcnNpb24gaWYgdGhlcmUgd2VyZSBhY3R1YWwgcmVkaXJlY3Rpb25zXG4gIHJldHVybiByZWRpcmVjdGlvbnMubGVuZ3RoID4gMCA/IGNvbW1hbmRXaXRob3V0UmVkaXJlY3Rpb25zIDogY29tbWFuZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gYmFzaFRvb2xVc2VPcHRpb25zKHtcbiAgc3VnZ2VzdGlvbnMgPSBbXSxcbiAgZGVjaXNpb25SZWFzb24sXG4gIG9uUmVqZWN0RmVlZGJhY2tDaGFuZ2UsXG4gIG9uQWNjZXB0RmVlZGJhY2tDaGFuZ2UsXG4gIG9uQ2xhc3NpZmllckRlc2NyaXB0aW9uQ2hhbmdlLFxuICBjbGFzc2lmaWVyRGVzY3JpcHRpb24sXG4gIGluaXRpYWxDbGFzc2lmaWVyRGVzY3JpcHRpb25FbXB0eSA9IGZhbHNlLFxuICBleGlzdGluZ0FsbG93RGVzY3JpcHRpb25zID0gW10sXG4gIHllc0lucHV0TW9kZSA9IGZhbHNlLFxuICBub0lucHV0TW9kZSA9IGZhbHNlLFxuICBlZGl0YWJsZVByZWZpeCxcbiAgb25FZGl0YWJsZVByZWZpeENoYW5nZSxcbn06IHtcbiAgc3VnZ2VzdGlvbnM/OiBQZXJtaXNzaW9uVXBkYXRlW11cbiAgZGVjaXNpb25SZWFzb24/OiBQZXJtaXNzaW9uRGVjaXNpb25SZWFzb25cbiAgb25SZWplY3RGZWVkYmFja0NoYW5nZTogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWRcbiAgb25BY2NlcHRGZWVkYmFja0NoYW5nZTogKHZhbHVlOiBzdHJpbmcpID0+IHZvaWRcbiAgb25DbGFzc2lmaWVyRGVzY3JpcHRpb25DaGFuZ2U/OiAodmFsdWU6IHN0cmluZykgPT4gdm9pZFxuICBjbGFzc2lmaWVyRGVzY3JpcHRpb24/OiBzdHJpbmdcbiAgLyoqIFdoZXRoZXIgdGhlIGluaXRpYWwgY2xhc3NpZmllciBkZXNjcmlwdGlvbiB3YXMgZW1wdHkuIFdoZW4gdHJ1ZSwgaGlkZXMgdGhlIG9wdGlvbi4gKi9cbiAgaW5pdGlhbENsYXNzaWZpZXJEZXNjcmlwdGlvbkVtcHR5PzogYm9vbGVhblxuICBleGlzdGluZ0FsbG93RGVzY3JpcHRpb25zPzogc3RyaW5nW11cbiAgeWVzSW5wdXRNb2RlPzogYm9vbGVhblxuICBub0lucHV0TW9kZT86IGJvb2xlYW5cbiAgLyoqIEVkaXRhYmxlIHByZWZpeCBydWxlIGNvbnRlbnQgKGUuZy4sIFwibnBtIHJ1bjoqXCIpLiBXaGVuIHNldCwgcmVwbGFjZXMgSGFpa3UtYmFzZWQgc3VnZ2VzdGlvbnMuICovXG4gIGVkaXRhYmxlUHJlZml4Pzogc3RyaW5nXG4gIC8qKiBDYWxsYmFjayB3aGVuIHRoZSB1c2VyIGVkaXRzIHRoZSBwcmVmaXggdmFsdWUuICovXG4gIG9uRWRpdGFibGVQcmVmaXhDaGFuZ2U/OiAodmFsdWU6IHN0cmluZykgPT4gdm9pZFxufSk6IE9wdGlvbldpdGhEZXNjcmlwdGlvbjxCYXNoVG9vbFVzZU9wdGlvbj5bXSB7XG4gIGNvbnN0IG9wdGlvbnM6IE9wdGlvbldpdGhEZXNjcmlwdGlvbjxCYXNoVG9vbFVzZU9wdGlvbj5bXSA9IFtdXG5cbiAgaWYgKHllc0lucHV0TW9kZSkge1xuICAgIG9wdGlvbnMucHVzaCh7XG4gICAgICB0eXBlOiAnaW5wdXQnLFxuICAgICAgbGFiZWw6ICdZZXMnLFxuICAgICAgdmFsdWU6ICd5ZXMnLFxuICAgICAgcGxhY2Vob2xkZXI6ICdhbmQgdGVsbCBDbGF1ZGUgd2hhdCB0byBkbyBuZXh0JyxcbiAgICAgIG9uQ2hhbmdlOiBvbkFjY2VwdEZlZWRiYWNrQ2hhbmdlLFxuICAgICAgYWxsb3dFbXB0eVN1Ym1pdFRvQ2FuY2VsOiB0cnVlLFxuICAgIH0pXG4gIH0gZWxzZSB7XG4gICAgb3B0aW9ucy5wdXNoKHtcbiAgICAgIGxhYmVsOiAnWWVzJyxcbiAgICAgIHZhbHVlOiAneWVzJyxcbiAgICB9KVxuICB9XG5cbiAgLy8gT25seSBzaG93IFwiYWx3YXlzIGFsbG93XCIgb3B0aW9ucyB3aGVuIG5vdCByZXN0cmljdGVkIGJ5IGFsbG93TWFuYWdlZFBlcm1pc3Npb25SdWxlc09ubHlcbiAgaWYgKHNob3VsZFNob3dBbHdheXNBbGxvd09wdGlvbnMoKSkge1xuICAgIC8vIFNob3cgYW4gZWRpdGFibGUgaW5wdXQgZm9yIHRoZSBwcmVmaXggcnVsZSBpbnN0ZWFkIG9mIHRoZVxuICAgIC8vIEhhaWt1LWdlbmVyYXRlZCBzdWdnZXN0aW9uIGxhYmVsIOKAlCBidXQgb25seSB3aGVuIHRoZSBzdWdnZXN0aW9uc1xuICAgIC8vIGRvbid0IGNvbnRhaW4gbm9uLUJhc2ggaXRlbXMgKGFkZERpcmVjdG9yaWVzLCBSZWFkIHJ1bGVzKSB0aGF0XG4gICAgLy8gdGhlIGVkaXRhYmxlIHByZWZpeCBjYW4ndCByZXByZXNlbnQuXG4gICAgY29uc3QgaGFzTm9uQmFzaFN1Z2dlc3Rpb25zID0gc3VnZ2VzdGlvbnMuc29tZShcbiAgICAgIHMgPT5cbiAgICAgICAgcy50eXBlID09PSAnYWRkRGlyZWN0b3JpZXMnIHx8XG4gICAgICAgIChzLnR5cGUgPT09ICdhZGRSdWxlcycgJiZcbiAgICAgICAgICBzLnJ1bGVzPy5zb21lKHIgPT4gci50b29sTmFtZSAhPT0gQkFTSF9UT09MX05BTUUpKSxcbiAgICApXG4gICAgaWYgKFxuICAgICAgZWRpdGFibGVQcmVmaXggIT09IHVuZGVmaW5lZCAmJlxuICAgICAgb25FZGl0YWJsZVByZWZpeENoYW5nZSAmJlxuICAgICAgIWhhc05vbkJhc2hTdWdnZXN0aW9ucyAmJlxuICAgICAgc3VnZ2VzdGlvbnMubGVuZ3RoID4gMFxuICAgICkge1xuICAgICAgb3B0aW9ucy5wdXNoKHtcbiAgICAgICAgdHlwZTogJ2lucHV0JyxcbiAgICAgICAgbGFiZWw6ICdZZXMsIGFuZCBkb25cXHUyMDE5dCBhc2sgYWdhaW4gZm9yJyxcbiAgICAgICAgdmFsdWU6ICd5ZXMtcHJlZml4LWVkaXRlZCcsXG4gICAgICAgIHBsYWNlaG9sZGVyOiAnY29tbWFuZCBwcmVmaXggKGUuZy4sIG5wbSBydW46KiknLFxuICAgICAgICBpbml0aWFsVmFsdWU6IGVkaXRhYmxlUHJlZml4LFxuICAgICAgICBvbkNoYW5nZTogb25FZGl0YWJsZVByZWZpeENoYW5nZSxcbiAgICAgICAgYWxsb3dFbXB0eVN1Ym1pdFRvQ2FuY2VsOiB0cnVlLFxuICAgICAgICBzaG93TGFiZWxXaXRoVmFsdWU6IHRydWUsXG4gICAgICAgIGxhYmVsVmFsdWVTZXBhcmF0b3I6ICc6ICcsXG4gICAgICAgIHJlc2V0Q3Vyc29yT25VcGRhdGU6IHRydWUsXG4gICAgICB9KVxuICAgIH0gZWxzZSBpZiAoc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3QgbGFiZWwgPSBnZW5lcmF0ZVNoZWxsU3VnZ2VzdGlvbnNMYWJlbChcbiAgICAgICAgc3VnZ2VzdGlvbnMsXG4gICAgICAgIEJBU0hfVE9PTF9OQU1FLFxuICAgICAgICBzdHJpcEJhc2hSZWRpcmVjdGlvbnMsXG4gICAgICApXG5cbiAgICAgIGlmIChsYWJlbCkge1xuICAgICAgICBvcHRpb25zLnB1c2goe1xuICAgICAgICAgIGxhYmVsLFxuICAgICAgICAgIHZhbHVlOiAneWVzLWFwcGx5LXN1Z2dlc3Rpb25zJyxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBBZGQgY2xhc3NpZmllci1yZXZpZXdlZCBvcHRpb24gaWYgZW5hYmxlZCwgdGhlIGluaXRpYWwgZGVzY3JpcHRpb24gd2FzXG4gICAgLy8gbm9uLWVtcHR5LCB0aGUgZGVzY3JpcHRpb24gZG9lc24ndCBhbHJlYWR5IGV4aXN0IGluIHRoZSBhbGxvdyBsaXN0LFxuICAgIC8vIGFuZCB0aGUgZGVjaXNpb24gcmVhc29uIGlzIE5PVCBhIHNlcnZlci1zaWRlIGNsYXNzaWZpZXIgYmxvY2tcbiAgICAvLyAocHJvbXB0LWJhc2VkIHJ1bGVzIGRvbid0IGhlbHAgd2hlbiB0aGUgc2VydmVyLXNpZGUgY2xhc3NpZmllciB0cmlnZ2VycyBmaXJzdCkuXG4gICAgLy8gU2tpcCB3aGVuIHRoZSBlZGl0YWJsZSBwcmVmaXggb3B0aW9uIGlzIGFscmVhZHkgc2hvd24g4oCUIHRoZXkgc2VydmUgdGhlXG4gICAgLy8gc2FtZSByb2xlIGFuZCBoYXZpbmcgdHdvIGlkZW50aWNhbC1sb29raW5nIFwiZG9uJ3QgYXNrIGFnYWluXCIgaW5wdXRzIGlzIGNvbmZ1c2luZy5cbiAgICBjb25zdCBlZGl0YWJsZVByZWZpeFNob3duID0gb3B0aW9ucy5zb21lKFxuICAgICAgbyA9PiBvLnZhbHVlID09PSAneWVzLXByZWZpeC1lZGl0ZWQnLFxuICAgIClcbiAgICBpZiAoXG4gICAgICBcImV4dGVybmFsXCIgPT09ICdhbnQnICYmXG4gICAgICAhZWRpdGFibGVQcmVmaXhTaG93biAmJlxuICAgICAgaXNDbGFzc2lmaWVyUGVybWlzc2lvbnNFbmFibGVkKCkgJiZcbiAgICAgIG9uQ2xhc3NpZmllckRlc2NyaXB0aW9uQ2hhbmdlICYmXG4gICAgICAhaW5pdGlhbENsYXNzaWZpZXJEZXNjcmlwdGlvbkVtcHR5ICYmXG4gICAgICAhZGVzY3JpcHRpb25BbHJlYWR5RXhpc3RzKFxuICAgICAgICBjbGFzc2lmaWVyRGVzY3JpcHRpb24gPz8gJycsXG4gICAgICAgIGV4aXN0aW5nQWxsb3dEZXNjcmlwdGlvbnMsXG4gICAgICApICYmXG4gICAgICBkZWNpc2lvblJlYXNvbj8udHlwZSAhPT0gJ2NsYXNzaWZpZXInXG4gICAgKSB7XG4gICAgICBvcHRpb25zLnB1c2goe1xuICAgICAgICB0eXBlOiAnaW5wdXQnLFxuICAgICAgICBsYWJlbDogJ1llcywgYW5kIGRvblxcdTIwMTl0IGFzayBhZ2FpbiBmb3InLFxuICAgICAgICB2YWx1ZTogJ3llcy1jbGFzc2lmaWVyLXJldmlld2VkJyxcbiAgICAgICAgcGxhY2Vob2xkZXI6ICdkZXNjcmliZSB3aGF0IHRvIGFsbG93Li4uJyxcbiAgICAgICAgaW5pdGlhbFZhbHVlOiBjbGFzc2lmaWVyRGVzY3JpcHRpb24gPz8gJycsXG4gICAgICAgIG9uQ2hhbmdlOiBvbkNsYXNzaWZpZXJEZXNjcmlwdGlvbkNoYW5nZSxcbiAgICAgICAgYWxsb3dFbXB0eVN1Ym1pdFRvQ2FuY2VsOiB0cnVlLFxuICAgICAgICBzaG93TGFiZWxXaXRoVmFsdWU6IHRydWUsXG4gICAgICAgIGxhYmVsVmFsdWVTZXBhcmF0b3I6ICc6ICcsXG4gICAgICAgIHJlc2V0Q3Vyc29yT25VcGRhdGU6IHRydWUsXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIGlmIChub0lucHV0TW9kZSkge1xuICAgIG9wdGlvbnMucHVzaCh7XG4gICAgICB0eXBlOiAnaW5wdXQnLFxuICAgICAgbGFiZWw6ICdObycsXG4gICAgICB2YWx1ZTogJ25vJyxcbiAgICAgIHBsYWNlaG9sZGVyOiAnYW5kIHRlbGwgQ2xhdWRlIHdoYXQgdG8gZG8gZGlmZmVyZW50bHknLFxuICAgICAgb25DaGFuZ2U6IG9uUmVqZWN0RmVlZGJhY2tDaGFuZ2UsXG4gICAgICBhbGxvd0VtcHR5U3VibWl0VG9DYW5jZWw6IHRydWUsXG4gICAgfSlcbiAgfSBlbHNlIHtcbiAgICBvcHRpb25zLnB1c2goe1xuICAgICAgbGFiZWw6ICdObycsXG4gICAgICB2YWx1ZTogJ25vJyxcbiAgICB9KVxuICB9XG5cbiAgcmV0dXJuIG9wdGlvbnNcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsY0FBYyxRQUFRLHFDQUFxQztBQUNwRSxTQUFTQyx5QkFBeUIsUUFBUSxpQ0FBaUM7QUFDM0UsU0FBU0MsOEJBQThCLFFBQVEsOENBQThDO0FBQzdGLGNBQWNDLHdCQUF3QixRQUFRLGdEQUFnRDtBQUM5RixjQUFjQyxnQkFBZ0IsUUFBUSxzREFBc0Q7QUFDNUYsU0FBU0MsNEJBQTRCLFFBQVEsaURBQWlEO0FBQzlGLGNBQWNDLHFCQUFxQixRQUFRLDhCQUE4QjtBQUN6RSxTQUFTQyw2QkFBNkIsUUFBUSw4QkFBOEI7QUFFNUUsT0FBTyxLQUFLQyxpQkFBaUIsR0FDekIsS0FBSyxHQUNMLHVCQUF1QixHQUN2QixtQkFBbUIsR0FDbkIseUJBQXlCLEdBQ3pCLElBQUk7O0FBRVI7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTQyx3QkFBd0JBLENBQy9CQyxXQUFXLEVBQUUsTUFBTSxFQUNuQkMsb0JBQW9CLEVBQUUsTUFBTSxFQUFFLENBQy9CLEVBQUUsT0FBTyxDQUFDO0VBQ1QsTUFBTUMsVUFBVSxHQUFHRixXQUFXLENBQUNHLFdBQVcsQ0FBQyxDQUFDLENBQUNDLE9BQU8sQ0FBQyxDQUFDO0VBQ3RELE9BQU9ILG9CQUFvQixDQUFDSSxJQUFJLENBQzlCQyxRQUFRLElBQUlBLFFBQVEsQ0FBQ0gsV0FBVyxDQUFDLENBQUMsQ0FBQ0MsT0FBTyxDQUFDLENBQUMsS0FBS0YsVUFDbkQsQ0FBQztBQUNIOztBQUVBO0FBQ0E7QUFDQTtBQUNBLFNBQVNLLHFCQUFxQkEsQ0FBQ0MsT0FBTyxFQUFFLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQztFQUN0RCxNQUFNO0lBQUVDLDBCQUEwQjtJQUFFQztFQUFhLENBQUMsR0FDaERuQix5QkFBeUIsQ0FBQ2lCLE9BQU8sQ0FBQztFQUNwQztFQUNBLE9BQU9FLFlBQVksQ0FBQ0MsTUFBTSxHQUFHLENBQUMsR0FBR0YsMEJBQTBCLEdBQUdELE9BQU87QUFDdkU7QUFFQSxPQUFPLFNBQVNJLGtCQUFrQkEsQ0FBQztFQUNqQ0MsV0FBVyxHQUFHLEVBQUU7RUFDaEJDLGNBQWM7RUFDZEMsc0JBQXNCO0VBQ3RCQyxzQkFBc0I7RUFDdEJDLDZCQUE2QjtFQUM3QkMscUJBQXFCO0VBQ3JCQyxpQ0FBaUMsR0FBRyxLQUFLO0VBQ3pDQyx5QkFBeUIsR0FBRyxFQUFFO0VBQzlCQyxZQUFZLEdBQUcsS0FBSztFQUNwQkMsV0FBVyxHQUFHLEtBQUs7RUFDbkJDLGNBQWM7RUFDZEM7QUFpQkYsQ0FoQkMsRUFBRTtFQUNEWCxXQUFXLENBQUMsRUFBRW5CLGdCQUFnQixFQUFFO0VBQ2hDb0IsY0FBYyxDQUFDLEVBQUVyQix3QkFBd0I7RUFDekNzQixzQkFBc0IsRUFBRSxDQUFDVSxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSTtFQUMvQ1Qsc0JBQXNCLEVBQUUsQ0FBQ1MsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUk7RUFDL0NSLDZCQUE2QixDQUFDLEVBQUUsQ0FBQ1EsS0FBSyxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUk7RUFDdkRQLHFCQUFxQixDQUFDLEVBQUUsTUFBTTtFQUM5QjtFQUNBQyxpQ0FBaUMsQ0FBQyxFQUFFLE9BQU87RUFDM0NDLHlCQUF5QixDQUFDLEVBQUUsTUFBTSxFQUFFO0VBQ3BDQyxZQUFZLENBQUMsRUFBRSxPQUFPO0VBQ3RCQyxXQUFXLENBQUMsRUFBRSxPQUFPO0VBQ3JCO0VBQ0FDLGNBQWMsQ0FBQyxFQUFFLE1BQU07RUFDdkI7RUFDQUMsc0JBQXNCLENBQUMsRUFBRSxDQUFDQyxLQUFLLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSTtBQUNsRCxDQUFDLENBQUMsRUFBRTdCLHFCQUFxQixDQUFDRSxpQkFBaUIsQ0FBQyxFQUFFLENBQUM7RUFDN0MsTUFBTTRCLE9BQU8sRUFBRTlCLHFCQUFxQixDQUFDRSxpQkFBaUIsQ0FBQyxFQUFFLEdBQUcsRUFBRTtFQUU5RCxJQUFJdUIsWUFBWSxFQUFFO0lBQ2hCSyxPQUFPLENBQUNDLElBQUksQ0FBQztNQUNYQyxJQUFJLEVBQUUsT0FBTztNQUNiQyxLQUFLLEVBQUUsS0FBSztNQUNaSixLQUFLLEVBQUUsS0FBSztNQUNaSyxXQUFXLEVBQUUsaUNBQWlDO01BQzlDQyxRQUFRLEVBQUVmLHNCQUFzQjtNQUNoQ2dCLHdCQUF3QixFQUFFO0lBQzVCLENBQUMsQ0FBQztFQUNKLENBQUMsTUFBTTtJQUNMTixPQUFPLENBQUNDLElBQUksQ0FBQztNQUNYRSxLQUFLLEVBQUUsS0FBSztNQUNaSixLQUFLLEVBQUU7SUFDVCxDQUFDLENBQUM7RUFDSjs7RUFFQTtFQUNBLElBQUk5Qiw0QkFBNEIsQ0FBQyxDQUFDLEVBQUU7SUFDbEM7SUFDQTtJQUNBO0lBQ0E7SUFDQSxNQUFNc0MscUJBQXFCLEdBQUdwQixXQUFXLENBQUNSLElBQUksQ0FDNUM2QixDQUFDLElBQ0NBLENBQUMsQ0FBQ04sSUFBSSxLQUFLLGdCQUFnQixJQUMxQk0sQ0FBQyxDQUFDTixJQUFJLEtBQUssVUFBVSxJQUNwQk0sQ0FBQyxDQUFDQyxLQUFLLEVBQUU5QixJQUFJLENBQUMrQixDQUFDLElBQUlBLENBQUMsQ0FBQ0MsUUFBUSxLQUFLL0MsY0FBYyxDQUN0RCxDQUFDO0lBQ0QsSUFDRWlDLGNBQWMsS0FBS2UsU0FBUyxJQUM1QmQsc0JBQXNCLElBQ3RCLENBQUNTLHFCQUFxQixJQUN0QnBCLFdBQVcsQ0FBQ0YsTUFBTSxHQUFHLENBQUMsRUFDdEI7TUFDQWUsT0FBTyxDQUFDQyxJQUFJLENBQUM7UUFDWEMsSUFBSSxFQUFFLE9BQU87UUFDYkMsS0FBSyxFQUFFLG1DQUFtQztRQUMxQ0osS0FBSyxFQUFFLG1CQUFtQjtRQUMxQkssV0FBVyxFQUFFLGtDQUFrQztRQUMvQ1MsWUFBWSxFQUFFaEIsY0FBYztRQUM1QlEsUUFBUSxFQUFFUCxzQkFBc0I7UUFDaENRLHdCQUF3QixFQUFFLElBQUk7UUFDOUJRLGtCQUFrQixFQUFFLElBQUk7UUFDeEJDLG1CQUFtQixFQUFFLElBQUk7UUFDekJDLG1CQUFtQixFQUFFO01BQ3ZCLENBQUMsQ0FBQztJQUNKLENBQUMsTUFBTSxJQUFJN0IsV0FBVyxDQUFDRixNQUFNLEdBQUcsQ0FBQyxFQUFFO01BQ2pDLE1BQU1rQixLQUFLLEdBQUdoQyw2QkFBNkIsQ0FDekNnQixXQUFXLEVBQ1h2QixjQUFjLEVBQ2RpQixxQkFDRixDQUFDO01BRUQsSUFBSXNCLEtBQUssRUFBRTtRQUNUSCxPQUFPLENBQUNDLElBQUksQ0FBQztVQUNYRSxLQUFLO1VBQ0xKLEtBQUssRUFBRTtRQUNULENBQUMsQ0FBQztNQUNKO0lBQ0Y7O0lBRUE7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0EsTUFBTWtCLG1CQUFtQixHQUFHakIsT0FBTyxDQUFDckIsSUFBSSxDQUN0Q3VDLENBQUMsSUFBSUEsQ0FBQyxDQUFDbkIsS0FBSyxLQUFLLG1CQUNuQixDQUFDO0lBQ0QsSUFDRSxVQUFVLEtBQUssS0FBSyxJQUNwQixDQUFDa0IsbUJBQW1CLElBQ3BCbkQsOEJBQThCLENBQUMsQ0FBQyxJQUNoQ3lCLDZCQUE2QixJQUM3QixDQUFDRSxpQ0FBaUMsSUFDbEMsQ0FBQ3BCLHdCQUF3QixDQUN2Qm1CLHFCQUFxQixJQUFJLEVBQUUsRUFDM0JFLHlCQUNGLENBQUMsSUFDRE4sY0FBYyxFQUFFYyxJQUFJLEtBQUssWUFBWSxFQUNyQztNQUNBRixPQUFPLENBQUNDLElBQUksQ0FBQztRQUNYQyxJQUFJLEVBQUUsT0FBTztRQUNiQyxLQUFLLEVBQUUsbUNBQW1DO1FBQzFDSixLQUFLLEVBQUUseUJBQXlCO1FBQ2hDSyxXQUFXLEVBQUUsMkJBQTJCO1FBQ3hDUyxZQUFZLEVBQUVyQixxQkFBcUIsSUFBSSxFQUFFO1FBQ3pDYSxRQUFRLEVBQUVkLDZCQUE2QjtRQUN2Q2Usd0JBQXdCLEVBQUUsSUFBSTtRQUM5QlEsa0JBQWtCLEVBQUUsSUFBSTtRQUN4QkMsbUJBQW1CLEVBQUUsSUFBSTtRQUN6QkMsbUJBQW1CLEVBQUU7TUFDdkIsQ0FBQyxDQUFDO0lBQ0o7RUFDRjtFQUVBLElBQUlwQixXQUFXLEVBQUU7SUFDZkksT0FBTyxDQUFDQyxJQUFJLENBQUM7TUFDWEMsSUFBSSxFQUFFLE9BQU87TUFDYkMsS0FBSyxFQUFFLElBQUk7TUFDWEosS0FBSyxFQUFFLElBQUk7TUFDWEssV0FBVyxFQUFFLHdDQUF3QztNQUNyREMsUUFBUSxFQUFFaEIsc0JBQXNCO01BQ2hDaUIsd0JBQXdCLEVBQUU7SUFDNUIsQ0FBQyxDQUFDO0VBQ0osQ0FBQyxNQUFNO0lBQ0xOLE9BQU8sQ0FBQ0MsSUFBSSxDQUFDO01BQ1hFLEtBQUssRUFBRSxJQUFJO01BQ1hKLEtBQUssRUFBRTtJQUNULENBQUMsQ0FBQztFQUNKO0VBRUEsT0FBT0MsT0FBTztBQUNoQiIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx b/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx new file mode 100644 index 0000000..9d85595 --- /dev/null +++ b/components/permissions/ComputerUseApproval/ComputerUseApproval.tsx @@ -0,0 +1,441 @@ +import { c as _c } from "react/compiler-runtime"; +import { getSentinelCategory } from '@ant/computer-use-mcp/sentinelApps'; +import type { CuPermissionRequest, CuPermissionResponse } from '@ant/computer-use-mcp/types'; +import { DEFAULT_GRANT_FLAGS } from '@ant/computer-use-mcp/types'; +import figures from 'figures'; +import * as React from 'react'; +import { useMemo, useState } from 'react'; +import { Box, Text } from '../../../ink.js'; +import { execFileNoThrow } from '../../../utils/execFileNoThrow.js'; +import { plural } from '../../../utils/stringUtils.js'; +import type { OptionWithDescription } from '../../CustomSelect/select.js'; +import { Select } from '../../CustomSelect/select.js'; +import { Dialog } from '../../design-system/Dialog.js'; +type ComputerUseApprovalProps = { + request: CuPermissionRequest; + onDone: (response: CuPermissionResponse) => void; +}; +const DENY_ALL_RESPONSE: CuPermissionResponse = { + granted: [], + denied: [], + flags: DEFAULT_GRANT_FLAGS +}; + +/** + * Two-panel dispatcher. When `request.tccState` is present, macOS permissions + * (Accessibility / Screen Recording) are missing and the app list is + * irrelevant — show a TCC panel that opens System Settings. Otherwise show the + * app allowlist + grant-flags panel. + */ +export function ComputerUseApproval(t0) { + const $ = _c(3); + const { + request, + onDone + } = t0; + let t1; + if ($[0] !== onDone || $[1] !== request) { + t1 = request.tccState ? onDone(DENY_ALL_RESPONSE)} /> : ; + $[0] = onDone; + $[1] = request; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} + +// ── TCC panel ───────────────────────────────────────────────────────────── + +type TccOption = 'open_accessibility' | 'open_screen_recording' | 'retry'; +function ComputerUseTccPanel(t0) { + const $ = _c(26); + const { + tccState, + onDone + } = t0; + let opts; + if ($[0] !== tccState.accessibility || $[1] !== tccState.screenRecording) { + opts = []; + if (!tccState.accessibility) { + let t1; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + label: "Open System Settings \u2192 Accessibility", + value: "open_accessibility" + }; + $[3] = t1; + } else { + t1 = $[3]; + } + opts.push(t1); + } + if (!tccState.screenRecording) { + let t1; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + label: "Open System Settings \u2192 Screen Recording", + value: "open_screen_recording" + }; + $[4] = t1; + } else { + t1 = $[4]; + } + opts.push(t1); + } + let t1; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + label: "Try again", + value: "retry" + }; + $[5] = t1; + } else { + t1 = $[5]; + } + opts.push(t1); + $[0] = tccState.accessibility; + $[1] = tccState.screenRecording; + $[2] = opts; + } else { + opts = $[2]; + } + const options = opts; + let t1; + if ($[6] !== onDone) { + t1 = function onChange(value) { + switch (value) { + case "open_accessibility": + { + execFileNoThrow("open", ["x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"], { + useCwd: false + }); + return; + } + case "open_screen_recording": + { + execFileNoThrow("open", ["x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture"], { + useCwd: false + }); + return; + } + case "retry": + { + onDone(); + return; + } + } + }; + $[6] = onDone; + $[7] = t1; + } else { + t1 = $[7]; + } + const onChange = t1; + const t2 = tccState.accessibility ? `${figures.tick} granted` : `${figures.cross} not granted`; + let t3; + if ($[8] !== t2) { + t3 = Accessibility:{" "}{t2}; + $[8] = t2; + $[9] = t3; + } else { + t3 = $[9]; + } + const t4 = tccState.screenRecording ? `${figures.tick} granted` : `${figures.cross} not granted`; + let t5; + if ($[10] !== t4) { + t5 = Screen Recording:{" "}{t4}; + $[10] = t4; + $[11] = t5; + } else { + t5 = $[11]; + } + let t6; + if ($[12] !== t3 || $[13] !== t5) { + t6 = {t3}{t5}; + $[12] = t3; + $[13] = t5; + $[14] = t6; + } else { + t6 = $[14]; + } + let t7; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t7 = Grant the missing permissions in System Settings, then select "Try again". macOS may require you to restart Claude Code after granting Screen Recording.; + $[15] = t7; + } else { + t7 = $[15]; + } + let t8; + if ($[16] !== onChange || $[17] !== onDone || $[18] !== options) { + t8 = ; + $[35] = options; + $[36] = t17; + $[37] = t18; + $[38] = t19; + } else { + t19 = $[38]; + } + let t20; + if ($[39] !== t12 || $[40] !== t14 || $[41] !== t15 || $[42] !== t16 || $[43] !== t19) { + t20 = {t12}{t14}{t15}{t16}{t19}; + $[39] = t12; + $[40] = t14; + $[41] = t15; + $[42] = t16; + $[43] = t19; + $[44] = t20; + } else { + t20 = $[44]; + } + let t21; + if ($[45] !== t11 || $[46] !== t20) { + t21 = {t20}; + $[45] = t11; + $[46] = t20; + $[47] = t21; + } else { + t21 = $[47]; + } + return t21; +} +function _temp4(flag) { + return {" "}· {flag}; +} +function _temp3(k_0) { + return [k_0, true] as const; +} +function _temp2(a_2) { + return { + bundleId: a_2.resolved?.bundleId ?? a_2.requestedName, + reason: a_2.resolved ? "user_denied" as const : "not_installed" as const + }; +} +function _temp(a) { + return a.resolved && !a.alreadyGranted ? [a.resolved.bundleId] : []; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJnZXRTZW50aW5lbENhdGVnb3J5IiwiQ3VQZXJtaXNzaW9uUmVxdWVzdCIsIkN1UGVybWlzc2lvblJlc3BvbnNlIiwiREVGQVVMVF9HUkFOVF9GTEFHUyIsImZpZ3VyZXMiLCJSZWFjdCIsInVzZU1lbW8iLCJ1c2VTdGF0ZSIsIkJveCIsIlRleHQiLCJleGVjRmlsZU5vVGhyb3ciLCJwbHVyYWwiLCJPcHRpb25XaXRoRGVzY3JpcHRpb24iLCJTZWxlY3QiLCJEaWFsb2ciLCJDb21wdXRlclVzZUFwcHJvdmFsUHJvcHMiLCJyZXF1ZXN0Iiwib25Eb25lIiwicmVzcG9uc2UiLCJERU5ZX0FMTF9SRVNQT05TRSIsImdyYW50ZWQiLCJkZW5pZWQiLCJmbGFncyIsIkNvbXB1dGVyVXNlQXBwcm92YWwiLCJ0MCIsIiQiLCJfYyIsInQxIiwidGNjU3RhdGUiLCJUY2NPcHRpb24iLCJDb21wdXRlclVzZVRjY1BhbmVsIiwib3B0cyIsImFjY2Vzc2liaWxpdHkiLCJzY3JlZW5SZWNvcmRpbmciLCJTeW1ib2wiLCJmb3IiLCJsYWJlbCIsInZhbHVlIiwicHVzaCIsIm9wdGlvbnMiLCJvbkNoYW5nZSIsInVzZUN3ZCIsInQyIiwidGljayIsImNyb3NzIiwidDMiLCJ0NCIsInQ1IiwidDYiLCJ0NyIsInQ4IiwidDkiLCJ0MTAiLCJBcHBMaXN0T3B0aW9uIiwiU0VOVElORUxfV0FSTklORyIsIlJlY29yZCIsIk5vbk51bGxhYmxlIiwiUmV0dXJuVHlwZSIsInNoZWxsIiwiZmlsZXN5c3RlbSIsInN5c3RlbV9zZXR0aW5ncyIsIkNvbXB1dGVyVXNlQXBwTGlzdFBhbmVsIiwiYXBwcyIsIlNldCIsImZsYXRNYXAiLCJfdGVtcCIsImNoZWNrZWQiLCJBTExfRkxBR19LRVlTIiwicmVxdWVzdGVkRmxhZ3MiLCJmaWx0ZXIiLCJrIiwicmVxdWVzdGVkRmxhZ0tleXMiLCJzaXplIiwicmVzcG9uZCIsImFsbG93Iiwibm93IiwiRGF0ZSIsImFfMCIsImEiLCJyZXNvbHZlZCIsImhhcyIsImJ1bmRsZUlkIiwiZGlzcGxheU5hbWUiLCJncmFudGVkQXQiLCJhXzEiLCJtYXAiLCJfdGVtcDIiLCJPYmplY3QiLCJmcm9tRW50cmllcyIsIl90ZW1wMyIsInQxMSIsInQxMiIsInJlYXNvbiIsInQxMyIsInQxNCIsImFfMyIsInJlcXVlc3RlZE5hbWUiLCJjaXJjbGUiLCJhbHJlYWR5R3JhbnRlZCIsInNlbnRpbmVsIiwiaXNDaGVja2VkIiwiY2lyY2xlRmlsbGVkIiwid2FybmluZyIsInQxNSIsImxlbmd0aCIsIl90ZW1wNCIsInQxNiIsIndpbGxIaWRlIiwidDE3IiwidDE4IiwidiIsInQxOSIsInQyMCIsInQyMSIsImZsYWciLCJrXzAiLCJjb25zdCIsImFfMiJdLCJzb3VyY2VzIjpbIkNvbXB1dGVyVXNlQXBwcm92YWwudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGdldFNlbnRpbmVsQ2F0ZWdvcnkgfSBmcm9tICdAYW50L2NvbXB1dGVyLXVzZS1tY3Avc2VudGluZWxBcHBzJ1xuaW1wb3J0IHR5cGUge1xuICBDdVBlcm1pc3Npb25SZXF1ZXN0LFxuICBDdVBlcm1pc3Npb25SZXNwb25zZSxcbn0gZnJvbSAnQGFudC9jb21wdXRlci11c2UtbWNwL3R5cGVzJ1xuaW1wb3J0IHsgREVGQVVMVF9HUkFOVF9GTEFHUyB9IGZyb20gJ0BhbnQvY29tcHV0ZXItdXNlLW1jcC90eXBlcydcbmltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZU1lbW8sIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBleGVjRmlsZU5vVGhyb3cgfSBmcm9tICcuLi8uLi8uLi91dGlscy9leGVjRmlsZU5vVGhyb3cuanMnXG5pbXBvcnQgeyBwbHVyYWwgfSBmcm9tICcuLi8uLi8uLi91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB0eXBlIHsgT3B0aW9uV2l0aERlc2NyaXB0aW9uIH0gZnJvbSAnLi4vLi4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4uLy4uL0N1c3RvbVNlbGVjdC9zZWxlY3QuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi8uLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcblxudHlwZSBDb21wdXRlclVzZUFwcHJvdmFsUHJvcHMgPSB7XG4gIHJlcXVlc3Q6IEN1UGVybWlzc2lvblJlcXVlc3RcbiAgb25Eb25lOiAocmVzcG9uc2U6IEN1UGVybWlzc2lvblJlc3BvbnNlKSA9PiB2b2lkXG59XG5cbmNvbnN0IERFTllfQUxMX1JFU1BPTlNFOiBDdVBlcm1pc3Npb25SZXNwb25zZSA9IHtcbiAgZ3JhbnRlZDogW10sXG4gIGRlbmllZDogW10sXG4gIGZsYWdzOiBERUZBVUxUX0dSQU5UX0ZMQUdTLFxufVxuXG4vKipcbiAqIFR3by1wYW5lbCBkaXNwYXRjaGVyLiBXaGVuIGByZXF1ZXN0LnRjY1N0YXRlYCBpcyBwcmVzZW50LCBtYWNPUyBwZXJtaXNzaW9uc1xuICogKEFjY2Vzc2liaWxpdHkgLyBTY3JlZW4gUmVjb3JkaW5nKSBhcmUgbWlzc2luZyBhbmQgdGhlIGFwcCBsaXN0IGlzXG4gKiBpcnJlbGV2YW50IOKAlCBzaG93IGEgVENDIHBhbmVsIHRoYXQgb3BlbnMgU3lzdGVtIFNldHRpbmdzLiBPdGhlcndpc2Ugc2hvdyB0aGVcbiAqIGFwcCBhbGxvd2xpc3QgKyBncmFudC1mbGFncyBwYW5lbC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIENvbXB1dGVyVXNlQXBwcm92YWwoe1xuICByZXF1ZXN0LFxuICBvbkRvbmUsXG59OiBDb21wdXRlclVzZUFwcHJvdmFsUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gcmVxdWVzdC50Y2NTdGF0ZSA/IChcbiAgICA8Q29tcHV0ZXJVc2VUY2NQYW5lbFxuICAgICAgdGNjU3RhdGU9e3JlcXVlc3QudGNjU3RhdGV9XG4gICAgICBvbkRvbmU9eygpID0+IG9uRG9uZShERU5ZX0FMTF9SRVNQT05TRSl9XG4gICAgLz5cbiAgKSA6IChcbiAgICA8Q29tcHV0ZXJVc2VBcHBMaXN0UGFuZWwgcmVxdWVzdD17cmVxdWVzdH0gb25Eb25lPXtvbkRvbmV9IC8+XG4gIClcbn1cblxuLy8g4pSA4pSAIFRDQyBwYW5lbCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIBcblxudHlwZSBUY2NPcHRpb24gPSAnb3Blbl9hY2Nlc3NpYmlsaXR5JyB8ICdvcGVuX3NjcmVlbl9yZWNvcmRpbmcnIHwgJ3JldHJ5J1xuXG5mdW5jdGlvbiBDb21wdXRlclVzZVRjY1BhbmVsKHtcbiAgdGNjU3RhdGUsXG4gIG9uRG9uZSxcbn06IHtcbiAgdGNjU3RhdGU6IE5vbk51bGxhYmxlPEN1UGVybWlzc2lvblJlcXVlc3RbJ3RjY1N0YXRlJ10+XG4gIG9uRG9uZTogKCkgPT4gdm9pZFxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IG9wdGlvbnMgPSB1c2VNZW1vPE9wdGlvbldpdGhEZXNjcmlwdGlvbjxUY2NPcHRpb24+W10+KCgpID0+IHtcbiAgICBjb25zdCBvcHRzOiBPcHRpb25XaXRoRGVzY3JpcHRpb248VGNjT3B0aW9uPltdID0gW11cbiAgICBpZiAoIXRjY1N0YXRlLmFjY2Vzc2liaWxpdHkpIHtcbiAgICAgIG9wdHMucHVzaCh7XG4gICAgICAgIGxhYmVsOiAnT3BlbiBTeXN0ZW0gU2V0dGluZ3Mg4oaSIEFjY2Vzc2liaWxpdHknLFxuICAgICAgICB2YWx1ZTogJ29wZW5fYWNjZXNzaWJpbGl0eScsXG4gICAgICB9KVxuICAgIH1cbiAgICBpZiAoIXRjY1N0YXRlLnNjcmVlblJlY29yZGluZykge1xuICAgICAgb3B0cy5wdXNoKHtcbiAgICAgICAgbGFiZWw6ICdPcGVuIFN5c3RlbSBTZXR0aW5ncyDihpIgU2NyZWVuIFJlY29yZGluZycsXG4gICAgICAgIHZhbHVlOiAnb3Blbl9zY3JlZW5fcmVjb3JkaW5nJyxcbiAgICAgIH0pXG4gICAgfVxuICAgIG9wdHMucHVzaCh7IGxhYmVsOiAnVHJ5IGFnYWluJywgdmFsdWU6ICdyZXRyeScgfSlcbiAgICByZXR1cm4gb3B0c1xuICB9LCBbdGNjU3RhdGUuYWNjZXNzaWJpbGl0eSwgdGNjU3RhdGUuc2NyZWVuUmVjb3JkaW5nXSlcblxuICBmdW5jdGlvbiBvbkNoYW5nZSh2YWx1ZTogVGNjT3B0aW9uKTogdm9pZCB7XG4gICAgc3dpdGNoICh2YWx1ZSkge1xuICAgICAgY2FzZSAnb3Blbl9hY2Nlc3NpYmlsaXR5JzpcbiAgICAgICAgdm9pZCBleGVjRmlsZU5vVGhyb3coXG4gICAgICAgICAgJ29wZW4nLFxuICAgICAgICAgIFtcbiAgICAgICAgICAgICd4LWFwcGxlLnN5c3RlbXByZWZlcmVuY2VzOmNvbS5hcHBsZS5wcmVmZXJlbmNlLnNlY3VyaXR5P1ByaXZhY3lfQWNjZXNzaWJpbGl0eScsXG4gICAgICAgICAgXSxcbiAgICAgICAgICB7IHVzZUN3ZDogZmFsc2UgfSxcbiAgICAgICAgKVxuICAgICAgICByZXR1cm5cbiAgICAgIGNhc2UgJ29wZW5fc2NyZWVuX3JlY29yZGluZyc6XG4gICAgICAgIHZvaWQgZXhlY0ZpbGVOb1Rocm93KFxuICAgICAgICAgICdvcGVuJyxcbiAgICAgICAgICBbXG4gICAgICAgICAgICAneC1hcHBsZS5zeXN0ZW1wcmVmZXJlbmNlczpjb20uYXBwbGUucHJlZmVyZW5jZS5zZWN1cml0eT9Qcml2YWN5X1NjcmVlbkNhcHR1cmUnLFxuICAgICAgICAgIF0sXG4gICAgICAgICAgeyB1c2VDd2Q6IGZhbHNlIH0sXG4gICAgICAgIClcbiAgICAgICAgcmV0dXJuXG4gICAgICBjYXNlICdyZXRyeSc6XG4gICAgICAgIC8vIFJlc29sdmUgd2l0aCBkZW55LWFsbCDigJQgdGhlIG1vZGVsIHJlLWNhbGxzIHJlcXVlc3RfYWNjZXNzLCB3aGljaFxuICAgICAgICAvLyByZS1jaGVja3MgVENDIGFuZCByZW5kZXJzIHRoZSBhcHAgbGlzdCBpZiBub3cgZ3JhbnRlZC5cbiAgICAgICAgb25Eb25lKClcbiAgICAgICAgcmV0dXJuXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nIHRpdGxlPVwiQ29tcHV0ZXIgVXNlIG5lZWRzIG1hY09TIHBlcm1pc3Npb25zXCIgb25DYW5jZWw9e29uRG9uZX0+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nWD17MX0gcGFkZGluZ1k9ezF9IGdhcD17MX0+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgQWNjZXNzaWJpbGl0eTp7JyAnfVxuICAgICAgICAgICAge3RjY1N0YXRlLmFjY2Vzc2liaWxpdHlcbiAgICAgICAgICAgICAgPyBgJHtmaWd1cmVzLnRpY2t9IGdyYW50ZWRgXG4gICAgICAgICAgICAgIDogYCR7ZmlndXJlcy5jcm9zc30gbm90IGdyYW50ZWRgfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIFNjcmVlbiBSZWNvcmRpbmc6eycgJ31cbiAgICAgICAgICAgIHt0Y2NTdGF0ZS5zY3JlZW5SZWNvcmRpbmdcbiAgICAgICAgICAgICAgPyBgJHtmaWd1cmVzLnRpY2t9IGdyYW50ZWRgXG4gICAgICAgICAgICAgIDogYCR7ZmlndXJlcy5jcm9zc30gbm90IGdyYW50ZWRgfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgIEdyYW50IHRoZSBtaXNzaW5nIHBlcm1pc3Npb25zIGluIFN5c3RlbSBTZXR0aW5ncywgdGhlbiBzZWxlY3RcbiAgICAgICAgICAmcXVvdDtUcnkgYWdhaW4mcXVvdDsuIG1hY09TIG1heSByZXF1aXJlIHlvdSB0byByZXN0YXJ0IENsYXVkZSBDb2RlXG4gICAgICAgICAgYWZ0ZXIgZ3JhbnRpbmcgU2NyZWVuIFJlY29yZGluZy5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8U2VsZWN0IG9wdGlvbnM9e29wdGlvbnN9IG9uQ2hhbmdlPXtvbkNoYW5nZX0gb25DYW5jZWw9e29uRG9uZX0gLz5cbiAgICAgIDwvQm94PlxuICAgIDwvRGlhbG9nPlxuICApXG59XG5cbi8vIOKUgOKUgCBBcHAgYWxsb3dsaXN0IHBhbmVsIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuXG50eXBlIEFwcExpc3RPcHRpb24gPSAnYWxsb3dfYWxsJyB8ICdkZW55J1xuXG5jb25zdCBTRU5USU5FTF9XQVJOSU5HOiBSZWNvcmQ8XG4gIE5vbk51bGxhYmxlPFJldHVyblR5cGU8dHlwZW9mIGdldFNlbnRpbmVsQ2F0ZWdvcnk+PixcbiAgc3RyaW5nXG4+ID0ge1xuICBzaGVsbDogJ2VxdWl2YWxlbnQgdG8gc2hlbGwgYWNjZXNzJyxcbiAgZmlsZXN5c3RlbTogJ2NhbiByZWFkL3dyaXRlIGFueSBmaWxlJyxcbiAgc3lzdGVtX3NldHRpbmdzOiAnY2FuIGNoYW5nZSBzeXN0ZW0gc2V0dGluZ3MnLFxufVxuXG5mdW5jdGlvbiBDb21wdXRlclVzZUFwcExpc3RQYW5lbCh7XG4gIHJlcXVlc3QsXG4gIG9uRG9uZSxcbn06IENvbXB1dGVyVXNlQXBwcm92YWxQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIFByZS1jaGVjayBldmVyeSByZXNvbHZlZCwgbm90LXlldC1ncmFudGVkIGFwcC4gU2VudGluZWxzIHN0YXkgY2hlY2tlZFxuICAvLyB0b28g4oCUIHRoZSB3YXJuaW5nIHRleHQgaXMgdGhlIHNpZ25hbCwgbm90IGFuIHVuY2hlY2tlZCBib3guXG4gIC8vIFBlci1pdGVtIHRvZ2dsZXMgYXJlIGEgZm9sbG93LXVwOyBmb3Igbm93IGV2ZXJ5IHJlc29sdmVkIGFwcCBpcyBncmFudGVkXG4gIC8vIHdoZW4gdGhlIHVzZXIgYWNjZXB0cy4gYHNldENoZWNrZWRgIGlzIHVudXNlZCB1bnRpbCB0aGVuLlxuICBjb25zdCBbY2hlY2tlZF0gPSB1c2VTdGF0ZTxSZWFkb25seVNldDxzdHJpbmc+PihcbiAgICAoKSA9PlxuICAgICAgbmV3IFNldChcbiAgICAgICAgcmVxdWVzdC5hcHBzLmZsYXRNYXAoYSA9PlxuICAgICAgICAgIGEucmVzb2x2ZWQgJiYgIWEuYWxyZWFkeUdyYW50ZWQgPyBbYS5yZXNvbHZlZC5idW5kbGVJZF0gOiBbXSxcbiAgICAgICAgKSxcbiAgICAgICksXG4gIClcblxuICB0eXBlIEZsYWdLZXkgPSBrZXlvZiB0eXBlb2YgREVGQVVMVF9HUkFOVF9GTEFHU1xuICBjb25zdCBBTExfRkxBR19LRVlTOiBGbGFnS2V5W10gPSBbXG4gICAgJ2NsaXBib2FyZFJlYWQnLFxuICAgICdjbGlwYm9hcmRXcml0ZScsXG4gICAgJ3N5c3RlbUtleUNvbWJvcycsXG4gIF1cbiAgY29uc3QgcmVxdWVzdGVkRmxhZ0tleXMgPSB1c2VNZW1vKFxuICAgICgpOiBGbGFnS2V5W10gPT4gQUxMX0ZMQUdfS0VZUy5maWx0ZXIoayA9PiByZXF1ZXN0LnJlcXVlc3RlZEZsYWdzW2tdKSxcbiAgICBbcmVxdWVzdC5yZXF1ZXN0ZWRGbGFnc10sXG4gIClcblxuICBjb25zdCBvcHRpb25zID0gdXNlTWVtbzxPcHRpb25XaXRoRGVzY3JpcHRpb248QXBwTGlzdE9wdGlvbj5bXT4oXG4gICAgKCkgPT4gW1xuICAgICAge1xuICAgICAgICBsYWJlbDogYEFsbG93IGZvciB0aGlzIHNlc3Npb24gKCR7Y2hlY2tlZC5zaXplfSAke3BsdXJhbChjaGVja2VkLnNpemUsICdhcHAnKX0pYCxcbiAgICAgICAgdmFsdWU6ICdhbGxvd19hbGwnLFxuICAgICAgfSxcbiAgICAgIHtcbiAgICAgICAgbGFiZWw6IChcbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIERlbnksIGFuZCB0ZWxsIENsYXVkZSB3aGF0IHRvIGRvIGRpZmZlcmVudGx5IDxUZXh0IGJvbGQ+KGVzYyk8L1RleHQ+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApLFxuICAgICAgICB2YWx1ZTogJ2RlbnknLFxuICAgICAgfSxcbiAgICBdLFxuICAgIFtjaGVja2VkLnNpemVdLFxuICApXG5cbiAgZnVuY3Rpb24gcmVzcG9uZChhbGxvdzogYm9vbGVhbik6IHZvaWQge1xuICAgIGlmICghYWxsb3cpIHtcbiAgICAgIG9uRG9uZShERU5ZX0FMTF9SRVNQT05TRSlcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpXG4gICAgY29uc3QgZ3JhbnRlZCA9IHJlcXVlc3QuYXBwcy5mbGF0TWFwKGEgPT5cbiAgICAgIGEucmVzb2x2ZWQgJiYgY2hlY2tlZC5oYXMoYS5yZXNvbHZlZC5idW5kbGVJZClcbiAgICAgICAgPyBbXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGJ1bmRsZUlkOiBhLnJlc29sdmVkLmJ1bmRsZUlkLFxuICAgICAgICAgICAgICBkaXNwbGF5TmFtZTogYS5yZXNvbHZlZC5kaXNwbGF5TmFtZSxcbiAgICAgICAgICAgICAgZ3JhbnRlZEF0OiBub3csXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIF1cbiAgICAgICAgOiBbXSxcbiAgICApXG4gICAgY29uc3QgZGVuaWVkID0gcmVxdWVzdC5hcHBzXG4gICAgICAuZmlsdGVyKGEgPT4gIWEucmVzb2x2ZWQgfHwgIWNoZWNrZWQuaGFzKGEucmVzb2x2ZWQuYnVuZGxlSWQpKVxuICAgICAgLm1hcChhID0+ICh7XG4gICAgICAgIGJ1bmRsZUlkOiBhLnJlc29sdmVkPy5idW5kbGVJZCA/PyBhLnJlcXVlc3RlZE5hbWUsXG4gICAgICAgIHJlYXNvbjogYS5yZXNvbHZlZFxuICAgICAgICAgID8gKCd1c2VyX2RlbmllZCcgYXMgY29uc3QpXG4gICAgICAgICAgOiAoJ25vdF9pbnN0YWxsZWQnIGFzIGNvbnN0KSxcbiAgICAgIH0pKVxuICAgIC8vIEdyYW50IGFsbCByZXF1ZXN0ZWQgZmxhZ3Mgb24gYWxsb3cg4oCUIHBlci1mbGFnIHRvZ2dsZXMgYXJlIGEgZm9sbG93LXVwLlxuICAgIGNvbnN0IGZsYWdzID0ge1xuICAgICAgLi4uREVGQVVMVF9HUkFOVF9GTEFHUyxcbiAgICAgIC4uLk9iamVjdC5mcm9tRW50cmllcyhyZXF1ZXN0ZWRGbGFnS2V5cy5tYXAoayA9PiBbaywgdHJ1ZV0gYXMgY29uc3QpKSxcbiAgICB9XG4gICAgb25Eb25lKHsgZ3JhbnRlZCwgZGVuaWVkLCBmbGFncyB9KVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT1cIkNvbXB1dGVyIFVzZSB3YW50cyB0byBjb250cm9sIHRoZXNlIGFwcHNcIlxuICAgICAgb25DYW5jZWw9eygpID0+IHJlc3BvbmQoZmFsc2UpfVxuICAgID5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdYPXsxfSBwYWRkaW5nWT17MX0gZ2FwPXsxfT5cbiAgICAgICAge3JlcXVlc3QucmVhc29uID8gPFRleHQgZGltQ29sb3I+e3JlcXVlc3QucmVhc29ufTwvVGV4dD4gOiBudWxsfVxuXG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIHtyZXF1ZXN0LmFwcHMubWFwKGEgPT4ge1xuICAgICAgICAgICAgY29uc3QgcmVzb2x2ZWQgPSBhLnJlc29sdmVkXG4gICAgICAgICAgICBpZiAoIXJlc29sdmVkKSB7XG4gICAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgPFRleHQga2V5PXthLnJlcXVlc3RlZE5hbWV9IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgeycgICd9XG4gICAgICAgICAgICAgICAgICB7ZmlndXJlcy5jaXJjbGV9IHthLnJlcXVlc3RlZE5hbWV9eycgJ31cbiAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPihub3QgaW5zdGFsbGVkKTwvVGV4dD5cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChhLmFscmVhZHlHcmFudGVkKSB7XG4gICAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgPFRleHQga2V5PXtyZXNvbHZlZC5idW5kbGVJZH0gZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICB7JyAgJ31cbiAgICAgICAgICAgICAgICAgIHtmaWd1cmVzLnRpY2t9IHtyZXNvbHZlZC5kaXNwbGF5TmFtZX17JyAnfVxuICAgICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+KGFscmVhZHkgZ3JhbnRlZCk8L1RleHQ+XG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBzZW50aW5lbCA9IGdldFNlbnRpbmVsQ2F0ZWdvcnkocmVzb2x2ZWQuYnVuZGxlSWQpXG4gICAgICAgICAgICBjb25zdCBpc0NoZWNrZWQgPSBjaGVja2VkLmhhcyhyZXNvbHZlZC5idW5kbGVJZClcbiAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgIDxCb3gga2V5PXtyZXNvbHZlZC5idW5kbGVJZH0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICAgICAgeycgICd9XG4gICAgICAgICAgICAgICAgICB7aXNDaGVja2VkID8gZmlndXJlcy5jaXJjbGVGaWxsZWQgOiBmaWd1cmVzLmNpcmNsZX17JyAnfVxuICAgICAgICAgICAgICAgICAge3Jlc29sdmVkLmRpc3BsYXlOYW1lfVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICB7c2VudGluZWwgPyAoXG4gICAgICAgICAgICAgICAgICA8VGV4dCBib2xkPlxuICAgICAgICAgICAgICAgICAgICB7JyAgICAnfVxuICAgICAgICAgICAgICAgICAgICB7ZmlndXJlcy53YXJuaW5nfSB7U0VOVElORUxfV0FSTklOR1tzZW50aW5lbF19XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgKSA6IG51bGx9XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKVxuICAgICAgICAgIH0pfVxuICAgICAgICA8L0JveD5cblxuICAgICAgICB7cmVxdWVzdGVkRmxhZ0tleXMubGVuZ3RoID4gMCA/IChcbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPkFsc28gcmVxdWVzdGVkOjwvVGV4dD5cbiAgICAgICAgICAgIHtyZXF1ZXN0ZWRGbGFnS2V5cy5tYXAoZmxhZyA9PiAoXG4gICAgICAgICAgICAgIDxUZXh0IGtleT17ZmxhZ30gZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgeycgICd9wrcge2ZsYWd9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICkpfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApIDogbnVsbH1cblxuICAgICAgICB7cmVxdWVzdC53aWxsSGlkZSAmJiByZXF1ZXN0LndpbGxIaWRlLmxlbmd0aCA+IDAgPyAoXG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7cmVxdWVzdC53aWxsSGlkZS5sZW5ndGh9IG90aGVyeycgJ31cbiAgICAgICAgICAgIHtwbHVyYWwocmVxdWVzdC53aWxsSGlkZS5sZW5ndGgsICdhcHAnKX0gd2lsbCBiZSBoaWRkZW4gd2hpbGUgQ2xhdWRlXG4gICAgICAgICAgICB3b3Jrcy5cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICkgOiBudWxsfVxuXG4gICAgICAgIDxTZWxlY3RcbiAgICAgICAgICBvcHRpb25zPXtvcHRpb25zfVxuICAgICAgICAgIG9uQ2hhbmdlPXt2ID0+IHJlc3BvbmQodiA9PT0gJ2FsbG93X2FsbCcpfVxuICAgICAgICAgIG9uQ2FuY2VsPXsoKSA9PiByZXNwb25kKGZhbHNlKX1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIDwvRGlhbG9nPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxtQkFBbUIsUUFBUSxvQ0FBb0M7QUFDeEUsY0FDRUMsbUJBQW1CLEVBQ25CQyxvQkFBb0IsUUFDZiw2QkFBNkI7QUFDcEMsU0FBU0MsbUJBQW1CLFFBQVEsNkJBQTZCO0FBQ2pFLE9BQU9DLE9BQU8sTUFBTSxTQUFTO0FBQzdCLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsT0FBTyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUN6QyxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxpQkFBaUI7QUFDM0MsU0FBU0MsZUFBZSxRQUFRLG1DQUFtQztBQUNuRSxTQUFTQyxNQUFNLFFBQVEsK0JBQStCO0FBQ3RELGNBQWNDLHFCQUFxQixRQUFRLDhCQUE4QjtBQUN6RSxTQUFTQyxNQUFNLFFBQVEsOEJBQThCO0FBQ3JELFNBQVNDLE1BQU0sUUFBUSwrQkFBK0I7QUFFdEQsS0FBS0Msd0JBQXdCLEdBQUc7RUFDOUJDLE9BQU8sRUFBRWYsbUJBQW1CO0VBQzVCZ0IsTUFBTSxFQUFFLENBQUNDLFFBQVEsRUFBRWhCLG9CQUFvQixFQUFFLEdBQUcsSUFBSTtBQUNsRCxDQUFDO0FBRUQsTUFBTWlCLGlCQUFpQixFQUFFakIsb0JBQW9CLEdBQUc7RUFDOUNrQixPQUFPLEVBQUUsRUFBRTtFQUNYQyxNQUFNLEVBQUUsRUFB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 \ No newline at end of file diff --git a/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx b/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx new file mode 100644 index 0000000..d680f0c --- /dev/null +++ b/components/permissions/EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.tsx @@ -0,0 +1,122 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { handlePlanModeTransition } from '../../../bootstrap/state.js'; +import { Box, Text } from '../../../ink.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../../services/analytics/index.js'; +import { useAppState } from '../../../state/AppState.js'; +import { isPlanModeInterviewPhaseEnabled } from '../../../utils/planModeV2.js'; +import { Select } from '../../CustomSelect/index.js'; +import { PermissionDialog } from '../PermissionDialog.js'; +import type { PermissionRequestProps } from '../PermissionRequest.js'; +export function EnterPlanModePermissionRequest(t0) { + const $ = _c(18); + const { + toolUseConfirm, + onDone, + onReject, + workerBadge + } = t0; + const toolPermissionContextMode = useAppState(_temp); + let t1; + if ($[0] !== onDone || $[1] !== onReject || $[2] !== toolPermissionContextMode || $[3] !== toolUseConfirm) { + t1 = function handleResponse(value) { + if (value === "yes") { + logEvent("tengu_plan_enter", { + interviewPhaseEnabled: isPlanModeInterviewPhaseEnabled(), + entryMethod: "tool" as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + handlePlanModeTransition(toolPermissionContextMode, "plan"); + onDone(); + toolUseConfirm.onAllow({}, [{ + type: "setMode", + mode: "plan", + destination: "session" + }]); + } else { + onDone(); + onReject(); + toolUseConfirm.onReject(); + } + }; + $[0] = onDone; + $[1] = onReject; + $[2] = toolPermissionContextMode; + $[3] = toolUseConfirm; + $[4] = t1; + } else { + t1 = $[4]; + } + const handleResponse = t1; + let t2; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t2 = Claude wants to enter plan mode to explore and design an implementation approach.; + $[5] = t2; + } else { + t2 = $[5]; + } + let t3; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t3 = In plan mode, Claude will: · Explore the codebase thoroughly · Identify existing patterns · Design an implementation strategy · Present a plan for your approval; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t4 = No code changes will be made until you approve the plan.; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + label: "Yes, enter plan mode", + value: "yes" as const + }; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t6 = [t5, { + label: "No, start implementing now", + value: "no" as const + }]; + $[9] = t6; + } else { + t6 = $[9]; + } + let t7; + if ($[10] !== handleResponse) { + t7 = () => handleResponse("no"); + $[10] = handleResponse; + $[11] = t7; + } else { + t7 = $[11]; + } + let t8; + if ($[12] !== handleResponse || $[13] !== t7) { + t8 = {t2}{t3}{t4} void handleResponseRef.current(v)} onCancel={() => handleCancelRef.current?.()} onImagePaste={onImagePaste} pastedContents={pastedContents} onRemoveImage={onRemoveImage} /> + + {editorName && + ctrl-g to edit in + + {editorName} + + {isV2 && planFilePath && · {getDisplayPath(planFilePath)}} + {showSaveMessage && <> + {' · '} + {figures.tick}Plan saved! + } + } + ); + return () => setStickyFooter(null); + // onImagePaste/onRemoveImage are stable (useCallback/useRef-backed above) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [useStickyFooter, setStickyFooter, options, pastedContents, editorName, isV2, planFilePath, showSaveMessage]); + + // Simplified UI for empty plans + if (isEmpty) { + function handleEmptyPlanResponse(value: 'yes' | 'no'): void { + if (value === 'yes') { + logEvent('tengu_plan_exit', { + planLengthChars: 0, + outcome: 'yes-default' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + interviewPhaseEnabled: isPlanModeInterviewPhaseEnabled(), + planStructureVariant + }); + if (feature('TRANSCRIPT_CLASSIFIER')) { + const autoWasUsedDuringPlan = autoModeStateModule?.isAutoModeActive() ?? false; + if (autoWasUsedDuringPlan) { + autoModeStateModule?.setAutoModeActive(false); + setNeedsAutoModeExitAttachment(true); + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...restoreDangerousPermissions(prev.toolPermissionContext), + prePlanMode: undefined + } + })); + } + } + setHasExitedPlanMode(true); + setNeedsPlanModeExitAttachment(true); + onDone(); + toolUseConfirm.onAllow({}, [{ + type: 'setMode', + mode: 'default', + destination: 'session' + }]); + } else { + logEvent('tengu_plan_exit', { + planLengthChars: 0, + outcome: 'no' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + interviewPhaseEnabled: isPlanModeInterviewPhaseEnabled(), + planStructureVariant + }); + onDone(); + onReject(); + toolUseConfirm.onReject(); + } + } + return + + Claude wants to exit plan mode + + handleCancelRef.current?.()} onImagePaste={onImagePaste} pastedContents={pastedContents} onRemoveImage={onRemoveImage} /> + + } + + + + {!useStickyFooter && editorName && + + ctrl-g to edit in + + {editorName} + + {isV2 && planFilePath && · {getDisplayPath(planFilePath)}} + + {showSaveMessage && + {' · '} + {figures.tick}Plan saved! + } + } + ; +} + +/** @internal Exported for testing. */ +export function buildPlanApprovalOptions({ + showClearContext, + showUltraplan, + usedPercent, + isAutoModeAvailable, + isBypassPermissionsModeAvailable, + onFeedbackChange +}: { + showClearContext: boolean; + showUltraplan: boolean; + usedPercent: number | null; + isAutoModeAvailable: boolean | undefined; + isBypassPermissionsModeAvailable: boolean | undefined; + onFeedbackChange: (v: string) => void; +}): OptionWithDescription[] { + const options: OptionWithDescription[] = []; + const usedLabel = usedPercent !== null ? ` (${usedPercent}% used)` : ''; + if (showClearContext) { + if (feature('TRANSCRIPT_CLASSIFIER') && isAutoModeAvailable) { + options.push({ + label: `Yes, clear context${usedLabel} and use auto mode`, + value: 'yes-auto-clear-context' + }); + } else if (isBypassPermissionsModeAvailable) { + options.push({ + label: `Yes, clear context${usedLabel} and bypass permissions`, + value: 'yes-bypass-permissions' + }); + } else { + options.push({ + label: `Yes, clear context${usedLabel} and auto-accept edits`, + value: 'yes-accept-edits' + }); + } + } + + // Slot 2: keep-context with elevated mode (same priority: auto > bypass > edits). + if (feature('TRANSCRIPT_CLASSIFIER') && isAutoModeAvailable) { + options.push({ + label: 'Yes, and use auto mode', + value: 'yes-resume-auto-mode' + }); + } else if (isBypassPermissionsModeAvailable) { + options.push({ + label: 'Yes, and bypass permissions', + value: 'yes-accept-edits-keep-context' + }); + } else { + options.push({ + label: 'Yes, auto-accept edits', + value: 'yes-accept-edits-keep-context' + }); + } + options.push({ + label: 'Yes, manually approve edits', + value: 'yes-default-keep-context' + }); + if (showUltraplan) { + options.push({ + label: 'No, refine with Ultraplan on Claude Code on the web', + value: 'ultraplan' + }); + } + options.push({ + type: 'input', + label: 'No, keep planning', + value: 'no', + placeholder: 'Tell Claude what to change', + description: 'shift+tab to approve with this feedback', + onChange: onFeedbackChange + }); + return options; +} +function getContextUsedPercent(usage: { + input_tokens: number; + cache_creation_input_tokens?: number | null; + cache_read_input_tokens?: number | null; +} | undefined, permissionMode: PermissionMode): number | null { + if (!usage) return null; + const runtimeModel = getRuntimeMainLoopModel({ + permissionMode, + mainLoopModel: getMainLoopModel(), + exceeds200kTokens: false + }); + const contextWindowSize = getContextWindowForModel(runtimeModel, getSdkBetas()); + const { + used + } = calculateContextPercentages({ + input_tokens: usage.input_tokens, + cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0, + cache_read_input_tokens: usage.cache_read_input_tokens ?? 0 + }, contextWindowSize); + return used; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiVVVJRCIsImZpZ3VyZXMiLCJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlRWZmZWN0IiwidXNlTGF5b3V0RWZmZWN0IiwidXNlTWVtbyIsInVzZVJlZiIsInVzZVN0YXRlIiwidXNlTm90aWZpY2F0aW9ucyIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsInVzZUFwcFN0YXRlIiwidXNlQXBwU3RhdGVTdG9yZSIsInVzZVNldEFwcFN0YXRlIiwiZ2V0U2RrQmV0YXMiLCJnZXRTZXNzaW9uSWQiLCJpc1Nlc3Npb25QZXJzaXN0ZW5jZURpc2FibGVkIiwic2V0SGFzRXhpdGVkUGxhbk1vZGUiLCJzZXROZWVkc0F1dG9Nb2RlRXhpdEF0dGFjaG1lbnQiLCJzZXROZWVkc1BsYW5Nb2RlRXhpdEF0dGFjaG1lbnQiLCJnZW5lcmF0ZVNlc3Npb25OYW1lIiwibGF1bmNoVWx0cmFwbGFuIiwiS2V5Ym9hcmRFdmVudCIsIkJveCIsIlRleHQiLCJBcHBTdGF0ZSIsIkFHRU5UX1RPT0xfTkFNRSIsIkVYSVRfUExBTl9NT0RFX1YyX1RPT0xfTkFNRSIsIkFsbG93ZWRQcm9tcHQiLCJURUFNX0NSRUFURV9UT09MX05BTUUiLCJpc0FnZW50U3dhcm1zRW5hYmxlZCIsImNhbGN1bGF0ZUNvbnRleHRQZXJjZW50YWdlcyIsImdldENvbnRleHRXaW5kb3dGb3JNb2RlbCIsImdldEV4dGVybmFsRWRpdG9yIiwiZ2V0RGlzcGxheVBhdGgiLCJ0b0lERURpc3BsYXlOYW1lIiwibG9nRXJyb3IiLCJlbnF1ZXVlUGVuZGluZ05vdGlmaWNhdGlvbiIsImNyZWF0ZVVzZXJNZXNzYWdlIiwiZ2V0TWFpbkxvb3BNb2RlbCIsImdldFJ1bnRpbWVNYWluTG9vcE1vZGVsIiwiY3JlYXRlUHJvbXB0UnVsZUNvbnRlbnQiLCJpc0NsYXNzaWZpZXJQZXJtaXNzaW9uc0VuYWJsZWQiLCJQUk9NUFRfUFJFRklYIiwiUGVybWlzc2lvbk1vZGUiLCJ0b0V4dGVybmFsUGVybWlzc2lvbk1vZGUiLCJQZXJtaXNzaW9uVXBkYXRlIiwiaXNBdXRvTW9kZUdhdGVFbmFibGVkIiwicmVzdG9yZURhbmdlcm91c1Blcm1pc3Npb25zIiwic3RyaXBEYW5nZXJvdXNQZXJtaXNzaW9uc0ZvckF1dG9Nb2RlIiwiZ2V0UGV3dGVyTGVkZ2VyVmFyaWFudCIsImlzUGxhbk1vZGVJbnRlcnZpZXdQaGFzZUVuYWJsZWQiLCJnZXRQbGFuIiwiZ2V0UGxhbkZpbGVQYXRoIiwiZWRpdEZpbGVJbkVkaXRvciIsImVkaXRQcm9tcHRJbkVkaXRvciIsImdldEN1cnJlbnRTZXNzaW9uVGl0bGUiLCJnZXRUcmFuc2NyaXB0UGF0aCIsInNhdmVBZ2VudE5hbWUiLCJzYXZlQ3VzdG9tVGl0bGUiLCJnZXRTZXR0aW5nc19ERVBSRUNBVEVEIiwiT3B0aW9uV2l0aERlc2NyaXB0aW9uIiwiU2VsZWN0IiwiTWFya2Rvd24iLCJQZXJtaXNzaW9uRGlhbG9nIiwiUGVybWlzc2lvblJlcXVlc3RQcm9wcyIsIlBlcm1pc3Npb25SdWxlRXhwbGFuYXRpb24iLCJhdXRvTW9kZVN0YXRlTW9kdWxlIiwicmVxdWlyZSIsIkJhc2U2NEltYWdlU291cmNlIiwiSW1hZ2VCbG9ja1BhcmFtIiwiUGFzdGVkQ29udGVudCIsIkltYWdlRGltZW5zaW9ucyIsIm1heWJlUmVzaXplQW5kRG93bnNhbXBsZUltYWdlQmxvY2siLCJjYWNoZUltYWdlUGF0aCIsInN0b3JlSW1hZ2UiLCJSZXNwb25zZVZhbHVlIiwiYnVpbGRQZXJtaXNzaW9uVXBkYXRlcyIsIm1vZGUiLCJhbGxvd2VkUHJvbXB0cyIsInVwZGF0ZXMiLCJ0eXBlIiwiZGVzdGluYXRpb24iLCJsZW5ndGgiLCJwdXNoIiwicnVsZXMiLCJtYXAiLCJwIiwidG9vbE5hbWUiLCJ0b29sIiwicnVsZUNvbnRlbnQiLCJwcm9tcHQiLCJiZWhhdmlvciIsImF1dG9OYW1lU2Vzc2lvbkZyb21QbGFuIiwicGxhbiIsInNldEFwcFN0YXRlIiwidXBkYXRlciIsInByZXYiLCJpc0NsZWFyQ29udGV4dCIsImNsZWFudXBQZXJpb2REYXlzIiwiY29udGVudCIsInNsaWNlIiwiQWJvcnRDb250cm9sbGVyIiwic2lnbmFsIiwidGhlbiIsIm5hbWUiLCJzZXNzaW9uSWQiLCJmdWxsUGF0aCIsInN0YW5kYWxvbmVBZ2VudENvbnRleHQiLCJjYXRjaCIsIkV4aXRQbGFuTW9kZVBlcm1pc3Npb25SZXF1ZXN0IiwidG9vbFVzZUNvbmZpcm0iLCJvbkRvbmUiLCJvblJlamVjdCIsIndvcmtlckJhZGdlIiwic2V0U3RpY2t5Rm9vdGVyIiwiUmVhY3ROb2RlIiwidG9vbFBlcm1pc3Npb25Db250ZXh0IiwicyIsInN0b3JlIiwiYWRkTm90aWZpY2F0aW9uIiwicGxhbkZlZWRiYWNrIiwic2V0UGxhbkZlZWRiYWNrIiwicGFzdGVkQ29udGVudHMiLCJzZXRQYXN0ZWRDb250ZW50cyIsIlJlY29yZCIsIm5leHRQYXN0ZUlkUmVmIiwic2hvd0NsZWFyQ29udGV4dCIsInNldHRpbmdzIiwic2hvd0NsZWFyQ29udGV4dE9uUGxhbkFjY2VwdCIsInVsdHJhcGxhblNlc3Npb25VcmwiLCJ1bHRyYXBsYW5MYXVuY2hpbmciLCJzaG93VWx0cmFwbGFuIiwidXNhZ2UiLCJhc3Npc3RhbnRNZXNzYWdlIiwibWVzc2FnZSIsImlzQXV0b01vZGVBdmFpbGFibGUiLCJpc0J5cGFzc1Blcm1pc3Npb25zTW9kZUF2YWlsYWJsZSIsIm9wdGlvbnMiLCJidWlsZFBsYW5BcHByb3ZhbE9wdGlvbnMiLCJ1c2VkUGVyY2VudCIsImdldENvbnRleHRVc2VkUGVyY2VudCIsIm9uRmVlZGJhY2tDaGFuZ2UiLCJvbkltYWdlUGFzdGUiLCJiYXNlNjRJbWFnZSIsIm1lZGlhVHlwZSIsImZpbGVuYW1lIiwiZGltZW5zaW9ucyIsIl9zb3VyY2VQYXRoIiwicGFzdGVJZCIsImN1cnJlbnQiLCJuZXdDb250ZW50IiwiaWQiLCJvblJlbW92ZUltYWdlIiwibmV4dCIsImltYWdlQXR0YWNobWVudHMiLCJPYmplY3QiLCJ2YWx1ZXMiLCJmaWx0ZXIiLCJjIiwiaGFzSW1hZ2VzIiwiaXNWMiIsImlucHV0UGxhbiIsInVuZGVmaW5lZCIsImlucHV0IiwicGxhbkZpbGVQYXRoIiwicmF3UGxhbiIsImlzRW1wdHkiLCJ0cmltIiwicGxhblN0cnVjdHVyZVZhcmlhbnQiLCJjdXJyZW50UGxhbiIsInNldEN1cnJlbnRQbGFuIiwic2hvd1NhdmVNZXNzYWdlIiwic2V0U2hvd1NhdmVNZXNzYWdlIiwicGxhbkVkaXRlZExvY2FsbHkiLCJzZXRQbGFuRWRpdGVkTG9jYWxseSIsInRpbWVyIiwic2V0VGltZW91dCIsImNsZWFyVGltZW91dCIsImhhbmRsZUtleURvd24iLCJlIiwiY3RybCIsImtleSIsInByZXZlbnREZWZhdWx0IiwicmVzdWx0IiwiZXJyb3IiLCJ0ZXh0IiwiY29sb3IiLCJwcmlvcml0eSIsInNoaWZ0IiwiaGFuZGxlUmVzcG9uc2UiLCJ2YWx1ZSIsIlByb21pc2UiLCJ0cmltbWVkRmVlZGJhY2siLCJhY2NlcHRGZWVkYmFjayIsInBsYW5MZW5ndGhDaGFycyIsIm91dGNvbWUiLCJpbnRlcnZpZXdQaGFzZUVuYWJsZWQiLCJibHVyYiIsInNlZWRQbGFuIiwiZ2V0QXBwU3RhdGUiLCJnZXRTdGF0ZSIsInNldFN0YXRlIiwibXNnIiwidXBkYXRlZElucHV0IiwiZ29pbmdUb0F1dG8iLCJhdXRvV2FzVXNlZER1cmluZ1BsYW4iLCJpc0F1dG9Nb2RlQWN0aXZlIiwic2V0QXV0b01vZGVBY3RpdmUiLCJwcmVQbGFuTW9kZSIsImlzUmVzdW1lQXV0b09wdGlvbiIsImlzS2VlcENvbnRleHRPcHRpb24iLCJjbGVhckNvbnRleHQiLCJoYXNGZWVkYmFjayIsInZlcmlmaWNhdGlvbkluc3RydWN0aW9uIiwidHJhbnNjcmlwdFBhdGgiLCJ0cmFuc2NyaXB0SGludCIsInRlYW1IaW50IiwiZmVlZGJhY2tTdWZmaXgiLCJpbml0aWFsTWVzc2FnZSIsInBsYW5Db250ZW50Iiwib25BbGxvdyIsImtlZXBDb250ZXh0TW9kZXMiLCJjb25zdCIsImtlZXBDb250ZXh0TW9kZSIsInN0YW5kYXJkTW9kZXMiLCJzdGFuZGFyZE1vZGUiLCJpbWFnZUJsb2NrcyIsImFsbCIsImltZyIsImJsb2NrIiwic291cmNlIiwibWVkaWFfdHlwZSIsImRhdGEiLCJyZXNpemVkIiwiZWRpdG9yIiwiZWRpdG9yTmFtZSIsImhhbmRsZVJlc3BvbnNlUmVmIiwiaGFuZGxlQ2FuY2VsUmVmIiwidXNlU3RpY2t5Rm9vdGVyIiwidiIsInRpY2siLCJoYW5kbGVFbXB0eVBsYW5SZXNwb25zZSIsImxhYmVsIiwicGVybWlzc2lvblJlc3VsdCIsImkiLCJ1c2VkTGFiZWwiLCJwbGFjZWhvbGRlciIsImRlc2NyaXB0aW9uIiwib25DaGFuZ2UiLCJpbnB1dF90b2tlbnMiLCJjYWNoZV9jcmVhdGlvbl9pbnB1dF90b2tlbnMiLCJjYWNoZV9yZWFkX2lucHV0X3Rva2VucyIsInBlcm1pc3Npb25Nb2RlIiwicnVudGltZU1vZGVsIiwibWFpbkxvb3BNb2RlbCIsImV4Y2VlZHMyMDBrVG9rZW5zIiwiY29udGV4dFdpbmRvd1NpemUiLCJ1c2VkIl0sInNvdXJjZXMiOlsiRXhpdFBsYW5Nb2RlUGVybWlzc2lvblJlcXVlc3QudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0IHR5cGUgeyBVVUlEIH0gZnJvbSAnY3J5cHRvJ1xuaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCBSZWFjdCwge1xuICB1c2VDYWxsYmFjayxcbiAgdXNlRWZmZWN0LFxuICB1c2VMYXlvdXRFZmZlY3QsXG4gIHVzZU1lbW8sXG4gIHVzZVJlZixcbiAgdXNlU3RhdGUsXG59IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJ3NyYy9jb250ZXh0L25vdGlmaWNhdGlvbnMuanMnXG5pbXBvcnQge1xuICB0eXBlIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIGxvZ0V2ZW50LFxufSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHtcbiAgdXNlQXBwU3RhdGUsXG4gIHVzZUFwcFN0YXRlU3RvcmUsXG4gIHVzZVNldEFwcFN0YXRlLFxufSBmcm9tICdzcmMvc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQge1xuICBnZXRTZGtCZXRhcyxcbiAgZ2V0U2Vzc2lvbklkLFxuICBpc1Nlc3Npb25QZXJzaXN0ZW5jZURpc2FibGVkLFxuICBzZXRIYXNFeGl0ZWRQbGFuTW9kZSxcbiAgc2V0TmVlZHNBdXRvTW9kZUV4aXRBdHRhY2htZW50LFxuICBzZXROZWVkc1BsYW5Nb2RlRXhpdEF0dGFjaG1lbnQsXG59IGZyb20gJy4uLy4uLy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7IGdlbmVyYXRlU2Vzc2lvbk5hbWUgfSBmcm9tICcuLi8uLi8uLi9jb21tYW5kcy9yZW5hbWUvZ2VuZXJhdGVTZXNzaW9uTmFtZS5qcydcbmltcG9ydCB7IGxhdW5jaFVsdHJhcGxhbiB9IGZyb20gJy4uLy4uLy4uL2NvbW1hbmRzL3VsdHJhcGxhbi5qcydcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uLy4uLy4uL2luay9ldmVudHMva2V5Ym9hcmQtZXZlbnQuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi8uLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IEFwcFN0YXRlIH0gZnJvbSAnLi4vLi4vLi4vc3RhdGUvQXBwU3RhdGVTdG9yZS5qcydcbmltcG9ydCB7IEFHRU5UX1RPT0xfTkFNRSB9IGZyb20gJy4uLy4uLy4uL3Rvb2xzL0FnZW50VG9vbC9jb25zdGFudHMuanMnXG5pbXBvcnQgeyBFWElUX1BMQU5fTU9ERV9WMl9UT09MX05BTUUgfSBmcm9tICcuLi8uLi8uLi90b29scy9FeGl0UGxhbk1vZGVUb29sL2NvbnN0YW50cy5qcydcbmltcG9ydCB0eXBlIHsgQWxsb3dlZFByb21wdCB9IGZyb20gJy4uLy4uLy4uL3Rvb2xzL0V4aXRQbGFuTW9kZVRvb2wvRXhpdFBsYW5Nb2RlVjJUb29sLmpzJ1xuaW1wb3J0IHsgVEVBTV9DUkVBVEVfVE9PTF9OQU1FIH0gZnJvbSAnLi4vLi4vLi4vdG9vbHMvVGVhbUNyZWF0ZVRvb2wvY29uc3RhbnRzLmpzJ1xuaW1wb3J0IHsgaXNBZ2VudFN3YXJtc0VuYWJsZWQgfSBmcm9tICcuLi8uLi8uLi91dGlscy9hZ2VudFN3YXJtc0VuYWJsZWQuanMnXG5pbXBvcnQge1xuICBjYWxjdWxhdGVDb250ZXh0UGVyY2VudGFnZXMsXG4gIGdldENvbnRleHRXaW5kb3dGb3JNb2RlbCxcbn0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvY29udGV4dC5qcydcbmltcG9ydCB7IGdldEV4dGVybmFsRWRpdG9yIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvZWRpdG9yLmpzJ1xuaW1wb3J0IHsgZ2V0RGlzcGxheVBhdGggfSBmcm9tICcuLi8uLi8uLi91dGlscy9maWxlLmpzJ1xuaW1wb3J0IHsgdG9JREVEaXNwbGF5TmFtZSB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL2lkZS5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvbG9nLmpzJ1xuaW1wb3J0IHsgZW5xdWV1ZVBlbmRpbmdOb3RpZmljYXRpb24gfSBmcm9tICcuLi8uLi8uLi91dGlscy9tZXNzYWdlUXVldWVNYW5hZ2VyLmpzJ1xuaW1wb3J0IHsgY3JlYXRlVXNlck1lc3NhZ2UgfSBmcm9tICcuLi8uLi8uLi91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB7XG4gIGdldE1haW5Mb29wTW9kZWwsXG4gIGdldFJ1bnRpbWVNYWluTG9vcE1vZGVsLFxufSBmcm9tICcuLi8uLi8uLi91dGlscy9tb2RlbC9tb2RlbC5qcydcbmltcG9ydCB7XG4gIGNyZWF0ZVByb21wdFJ1bGVDb250ZW50LFxuICBpc0NsYXNzaWZpZXJQZXJtaXNzaW9uc0VuYWJsZWQsXG4gIFBST01QVF9QUkVGSVgsXG59IGZyb20gJy4uLy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL2Jhc2hDbGFzc2lmaWVyLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBQZXJtaXNzaW9uTW9kZSxcbiAgdG9FeHRlcm5hbFBlcm1pc3Npb25Nb2RlLFxufSBmcm9tICcuLi8uLi8uLi91dGlscy9wZXJtaXNzaW9ucy9QZXJtaXNzaW9uTW9kZS5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvblVwZGF0ZSB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25VcGRhdGVTY2hlbWEuanMnXG5pbXBvcnQge1xuICBpc0F1dG9Nb2RlR2F0ZUVuYWJsZWQsXG4gIHJlc3RvcmVEYW5nZXJvdXNQZXJtaXNzaW9ucyxcbiAgc3RyaXBEYW5nZXJvdXNQZXJtaXNzaW9uc0ZvckF1dG9Nb2RlLFxufSBmcm9tICcuLi8uLi8uLi91dGlscy9wZXJtaXNzaW9ucy9wZXJtaXNzaW9uU2V0dXAuanMnXG5pbXBvcnQge1xuICBnZXRQZXd0ZXJMZWRnZXJWYXJpYW50LFxuICBpc1BsYW5Nb2RlSW50ZXJ2aWV3UGhhc2VFbmFibGVkLFxufSBmcm9tICcuLi8uLi8uLi91dGlscy9wbGFuTW9kZVYyLmpzJ1xuaW1wb3J0IHsgZ2V0UGxhbiwgZ2V0UGxhbkZpbGVQYXRoIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcGxhbnMuanMnXG5pbXBvcnQge1xuICBlZGl0RmlsZUluRWRpdG9yLFxuICBlZGl0UHJvbXB0SW5FZGl0b3IsXG59IGZyb20gJy4uLy4uLy4uL3V0aWxzL3Byb21wdEVkaXRvci5qcydcbmltcG9ydCB7XG4gIGdldEN1cnJlbnRTZXNzaW9uVGl0bGUsXG4gIGdldFRyYW5zY3JpcHRQYXRoLFxuICBzYXZlQWdlbnROYW1lLFxuICBzYXZlQ3VzdG9tVGl0bGUsXG59IGZyb20gJy4uLy4uLy4uL3V0aWxzL3Nlc3Npb25TdG9yYWdlLmpzJ1xuaW1wb3J0IHsgZ2V0U2V0dGluZ3NfREVQUkVDQVRFRCB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuaW1wb3J0IHsgdHlwZSBPcHRpb25XaXRoRGVzY3JpcHRpb24sIFNlbGVjdCB9IGZyb20gJy4uLy4uL0N1c3RvbVNlbGVjdC9pbmRleC5qcydcbmltcG9ydCB7IE1hcmtkb3duIH0gZnJvbSAnLi4vLi4vTWFya2Rvd24uanMnXG5pbXBvcnQgeyBQZXJtaXNzaW9uRGlhbG9nIH0gZnJvbSAnLi4vUGVybWlzc2lvbkRpYWxvZy5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvblJlcXVlc3RQcm9wcyB9IGZyb20gJy4uL1Blcm1pc3Npb25SZXF1ZXN0LmpzJ1xuaW1wb3J0IHsgUGVybWlzc2lvblJ1bGVFeHBsYW5hdGlvbiB9IGZyb20gJy4uL1Blcm1pc3Npb25SdWxlRXhwbGFuYXRpb24uanMnXG5cbi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbmNvbnN0IGF1dG9Nb2RlU3RhdGVNb2R1bGUgPSBmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKVxuICA/IChyZXF1aXJlKCcuLi8uLi8uLi91dGlscy9wZXJtaXNzaW9ucy9hdXRvTW9kZVN0YXRlLmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvYXV0b01vZGVTdGF0ZS5qcycpKVxuICA6IG51bGxcblxuaW1wb3J0IHR5cGUge1xuICBCYXNlNjRJbWFnZVNvdXJjZSxcbiAgSW1hZ2VCbG9ja1BhcmFtLFxufSBmcm9tICdAYW50aHJvcGljLWFpL3Nkay9yZXNvdXJjZXMvbWVzc2FnZXMubWpzJ1xuLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG5pbXBvcnQgdHlwZSB7IFBhc3RlZENvbnRlbnQgfSBmcm9tICcuLi8uLi8uLi91dGlscy9jb25maWcuanMnXG5pbXBvcnQgdHlwZSB7IEltYWdlRGltZW5zaW9ucyB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL2ltYWdlUmVzaXplci5qcydcbmltcG9ydCB7IG1heWJlUmVzaXplQW5kRG93bnNhbXBsZUltYWdlQmxvY2sgfSBmcm9tICcuLi8uLi8uLi91dGlscy9pbWFnZVJlc2l6ZXIuanMnXG5pbXBvcnQgeyBjYWNoZUltYWdlUGF0aCwgc3RvcmVJbWFnZSB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL2ltYWdlU3RvcmUuanMnXG5cbnR5cGUgUmVzcG9uc2VWYWx1ZSA9XG4gIHwgJ3llcy1ieXBhc3MtcGVybWlzc2lvbnMnXG4gIHwgJ3llcy1hY2NlcHQtZWRpdHMnXG4gIHwgJ3llcy1hY2NlcHQtZWRpdHMta2VlcC1jb250ZXh0J1xuICB8ICd5ZXMtZGVmYXVsdC1rZWVwLWNvbnRleHQnXG4gIHwgJ3llcy1yZXN1bWUtYXV0by1tb2RlJ1xuICB8ICd5ZXMtYXV0by1jbGVhci1jb250ZXh0J1xuICB8ICd1bHRyYXBsYW4nXG4gIHwgJ25vJ1xuXG4vKipcbiAqIEJ1aWxkIHBlcm1pc3Npb24gdXBkYXRlcyBmb3IgcGxhbiBhcHByb3ZhbCwgaW5jbHVkaW5nIHByb21wdC1iYXNlZCBydWxlcyBpZiBwcm92aWRlZC5cbiAqIFByb21wdC1iYXNlZCBydWxlcyBhcmUgb25seSBhZGRlZCB3aGVuIGNsYXNzaWZpZXIgcGVybWlzc2lvbnMgYXJlIGVuYWJsZWQgKEFudC1vbmx5KS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGJ1aWxkUGVybWlzc2lvblVwZGF0ZXMoXG4gIG1vZGU6IFBlcm1pc3Npb25Nb2RlLFxuICBhbGxvd2VkUHJvbXB0cz86IEFsbG93ZWRQcm9tcHRbXSxcbik6IFBlcm1pc3Npb25VcGRhdGVbXSB7XG4gIGNvbnN0IHVwZGF0ZXM6IFBlcm1pc3Npb25VcGRhdGVbXSA9IFtcbiAgICB7XG4gICAgICB0eXBlOiAnc2V0TW9kZScsXG4gICAgICBtb2RlOiB0b0V4dGVybmFsUGVybWlzc2lvbk1vZGUobW9kZSksXG4gICAgICBkZXN0aW5hdGlvbjogJ3Nlc3Npb24nLFxuICAgIH0sXG4gIF1cblxuICAvLyBBZGQgcHJvbXB0LWJhc2VkIHBlcm1pc3Npb24gcnVsZXMgaWYgcHJvdmlkZWQgKEFudC1vbmx5IGZlYXR1cmUpXG4gIGlmIChcbiAgICBpc0NsYXNzaWZpZXJQZXJtaXNzaW9uc0VuYWJsZWQoKSAmJlxuICAgIGFsbG93ZWRQcm9tcHRzICYmXG4gICAgYWxsb3dlZFByb21wdHMubGVuZ3RoID4gMFxuICApIHtcbiAgICB1cGRhdGVzLnB1c2goe1xuICAgICAgdHlwZTogJ2FkZFJ1bGVzJyxcbiAgICAgIHJ1bGVzOiBhbGxvd2VkUHJvbXB0cy5tYXAocCA9PiAoe1xuICAgICAgICB0b29sTmFtZTogcC50b29sLFxuICAgICAgICBydWxlQ29udGVudDogY3JlYXRlUHJvbXB0UnVsZUNvbnRlbnQocC5wcm9tcHQpLFxuICAgICAgfSkpLFxuICAgICAgYmVoYXZpb3I6ICdhbGxvdycsXG4gICAgICBkZXN0aW5hdGlvbjogJ3Nlc3Npb24nLFxuICAgIH0pXG4gIH1cblxuICByZXR1cm4gdXBkYXRlc1xufVxuXG4vKipcbiAqIEF1dG8tbmFtZSB0aGUgc2Vzc2lvbiBmcm9tIHRoZSBwbGFuIGNvbnRlbnQgd2hlbiB0aGUgdXNlciBhY2NlcHRzIGEgcGxhbixcbiAqIGlmIHRoZXkgaGF2ZW4ndCBhbHJlYWR5IG5hbWVkIGl0IHZpYSAvcmVuYW1lIG9yIC0tbmFtZS4gRmlyZS1hbmQtZm9yZ2V0LlxuICogTWlycm9ycyAvcmVuYW1lOiBrZWJhYi1jYXNlIG5hbWUsIHVwZGF0ZXMgdGhlIHByb21wdC1ib3JkZXIgYmFkZ2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhdXRvTmFtZVNlc3Npb25Gcm9tUGxhbihcbiAgcGxhbjogc3RyaW5nLFxuICBzZXRBcHBTdGF0ZTogKHVwZGF0ZXI6IChwcmV2OiBBcHBTdGF0ZSkgPT4gQXBwU3RhdGUpID0+IHZvaWQsXG4gIGlzQ2xlYXJDb250ZXh0OiBib29sZWFuLFxuKTogdm9pZCB7XG4gIGlmIChcbiAgICBpc1Nlc3Npb25QZXJzaXN0ZW5jZURpc2FibGVkKCkgfHxcbiAgICBnZXRTZXR0aW5nc19ERVBSRUNBVEVEKCk/LmNsZWFudXBQZXJpb2REYXlzID09PSAwXG4gICkge1xuICAgIHJldHVyblxuICB9XG4gIC8vIE9uIGNsZWFyLWNvbnRleHQsIHRoZSBjdXJyZW50IHNlc3Npb24gaXMgYWJvdXQgdG8gYmUgYWJhbmRvbmVkIOKAlCBpdHNcbiAgLy8gdGl0bGUgKHdoaWNoIG1heSBoYXZlIGJlZW4gc2V0IGJ5IGEgUFJJT1IgYXV0by1uYW1lKSBpcyBpcnJlbGV2YW50LlxuICAvLyBDaGVja2luZyBpdCB3b3VsZCBtYWtlIHRoZSBmZWF0dXJlIHNlbGYtZGVmZWF0aW5nIGFmdGVyIGZpcnN0IHVzZS5cbiAgaWYgKCFpc0NsZWFyQ29udGV4dCAmJiBnZXRDdXJyZW50U2Vzc2lvblRpdGxlKGdldFNlc3Npb25JZCgpKSkgcmV0dXJuXG4gIHZvaWQgZ2VuZXJhdGVTZXNzaW9uTmFtZShcbiAgICAvLyBnZW5lcmF0ZVNlc3Npb25OYW1lIHRhaWwtc2xpY2VzIHRvIHRoZSBsYXN0IDEwMDAgY2hhcnMgKGNvcnJlY3QgZm9yXG4gICAgLy8gY29udmVyc2F0aW9ucywgd2hlcmUgcmVjZW5jeSBtYXR0ZXJzKS4gUGxhbnMgZnJvbnQtbG9hZCB0aGUgZ29hbCBhbmRcbiAgICAvLyBlbmQgd2l0aCB0ZXN0aW5nIHN0ZXBzIOKAlCBoZWFkLXNsaWNlIHNvIEhhaWt1IHNlZXMgdGhlIHN1bW1hcnkuXG4gICAgW2NyZWF0ZVVzZXJNZXNzYWdlKHsgY29udGVudDogcGxhbi5zbGljZSgwLCAxMDAwKSB9KV0sXG4gICAgbmV3IEFib3J0Q29udHJvbGxlcigpLnNpZ25hbCxcbiAgKVxuICAgIC50aGVuKGFzeW5jIG5hbWUgPT4ge1xuICAgICAgLy8gT24gY2xlYXItY29udGV4dCBhY2NlcHRhbmNlLCByZWdlbmVyYXRlU2Vzc2lvbklkKCkgaGFzIHJ1biBieSBub3cg4oCUXG4gICAgICAvLyB0aGlzIGludGVudGlvbmFsbHkgbmFtZXMgdGhlIE5FVyBleGVjdXRpb24gc2Vzc2lvbi4gRG8gbm90IFwiZml4XCIgYnlcbiAgICAgIC8vIGNhcHR1cmluZyBzZXNzaW9uSWQgb25jZTsgdGhhdCB3b3VsZCBuYW1lIHRoZSBhYmFuZG9uZWQgcGxhbm5pbmcgc2Vzc2lvbi5cbiAgICAgIGlmICghbmFtZSB8fCBnZXRDdXJyZW50U2Vzc2lvblRpdGxlKGdldFNlc3Npb25JZCgpKSkgcmV0dXJuXG4gICAgICBjb25zdCBzZXNzaW9uSWQgPSBnZXRTZXNzaW9uSWQoKSBhcyBVVUlEXG4gICAgICBjb25zdCBmdWxsUGF0aCA9IGdldFRyYW5zY3JpcHRQYXRoKClcbiAgICAgIGF3YWl0IHNhdmVDdXN0b21UaXRsZShzZXNzaW9uSWQsIG5hbWUsIGZ1bGxQYXRoLCAnYXV0bycpXG4gICAgICBhd2FpdCBzYXZlQWdlbnROYW1lKHNlc3Npb25JZCwgbmFtZSwgZnVsbFBhdGgsICdhdXRvJylcbiAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICBpZiAocHJldi5zdGFuZGFsb25lQWdlbnRDb250ZXh0Py5uYW1lID09PSBuYW1lKSByZXR1cm4gcHJldlxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgc3RhbmRhbG9uZUFnZW50Q29udGV4dDogeyAuLi5wcmV2LnN0YW5kYWxvbmVBZ2VudENvbnRleHQsIG5hbWUgfSxcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9KVxuICAgIC5jYXRjaChsb2dFcnJvcilcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIEV4aXRQbGFuTW9kZVBlcm1pc3Npb25SZXF1ZXN0KHtcbiAgdG9vbFVzZUNvbmZpcm0sXG4gIG9uRG9uZSxcbiAgb25SZWplY3QsXG4gIHdvcmtlckJhZGdlLFxuICBzZXRTdGlja3lGb290ZXIsXG59OiBQZXJtaXNzaW9uUmVxdWVzdFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgdG9vbFBlcm1pc3Npb25Db250ZXh0ID0gdXNlQXBwU3RhdGUocyA9PiBzLnRvb2xQZXJtaXNzaW9uQ29udGV4dClcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IHN0b3JlID0gdXNlQXBwU3RhdGVTdG9yZSgpXG4gIGNvbnN0IHsgYWRkTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcbiAgLy8gRmVlZGJhY2sgdGV4dCBmcm9tIHRoZSAnTm8nIG9wdGlvbidzIGlucHV0LiBUaHJlYWRlZCB0aHJvdWdoIG9uQWxsb3cgYXNcbiAgLy8gYWNjZXB0RmVlZGJhY2sgd2hlbiB0aGUgdXNlciBhcHByb3ZlcyDigJQgbGV0cyB1c2VycyBhbm5vdGF0ZSB0aGUgcGxhblxuICAvLyAoXCJhbHNvIHVwZGF0ZSB0aGUgUkVBRE1FXCIpIHdpdGhvdXQgYSByZWplY3QrcmUtcGxhbiByb3VuZC10cmlwLlxuICBjb25zdCBbcGxhbkZlZWRiYWNrLCBzZXRQbGFuRmVlZGJhY2tdID0gdXNlU3RhdGUoJycpXG4gIGNvbnN0IFtwYXN0ZWRDb250ZW50cywgc2V0UGFzdGVkQ29udGVudHNdID0gdXNlU3RhdGU8XG4gICAgUmVjb3JkPG51bWJlciwgUGFzdGVkQ29udGVudD5cbiAgPih7fSlcbiAgY29uc3QgbmV4dFBhc3RlSWRSZWYgPSB1c2VSZWYoMClcblxuICBjb25zdCBzaG93Q2xlYXJDb250ZXh0ID1cbiAgICB1c2VBcHBTdGF0ZShzID0+IHMuc2V0dGluZ3Muc2hvd0NsZWFyQ29udGV4dE9uUGxhbkFjY2VwdCkgPz8gZmFsc2VcbiAgY29uc3QgdWx0cmFwbGFuU2Vzc2lvblVybCA9IHVzZUFwcFN0YXRlKHMgPT4gcy51bHRyYXBsYW5TZXNzaW9uVXJsKVxuICBjb25zdCB1bHRyYXBsYW5MYXVuY2hpbmcgPSB1c2VBcHBTdGF0ZShzID0+IHMudWx0cmFwbGFuTGF1bmNoaW5nKVxuICAvLyBIaWRlIHRoZSBVbHRyYXBsYW4gYnV0dG9uIHdoaWxlIGEgc2Vzc2lvbiBpcyBhY3RpdmUgb3IgbGF1bmNoaW5nIOKAlFxuICAvLyBzZWxlY3RpbmcgaXQgd291bGQgZGlzbWlzcyB0aGUgZGlhbG9nIGFuZCByZWplY3QgbG9jYWxseSBiZWZvcmVcbiAgLy8gbGF1bmNoVWx0cmFwbGFuIGNhbiBub3RpY2UgdGhlIHNlc3Npb24gZXhpc3RzIGFuZCByZXR1cm4gXCJhbHJlYWR5IHBvbGxpbmdcIi5cbiAgLy8gZmVhdHVyZSgpIG11c3Qgc2l0IGRpcmVjdGx5IGluIGFuIGlmL3Rlcm5hcnkgKGJ1bjpidW5kbGUgRENFIGNvbnN0cmFpbnQpLlxuICBjb25zdCBzaG93VWx0cmFwbGFuID0gZmVhdHVyZSgnVUxUUkFQTEFOJylcbiAgICA/ICF1bHRyYXBsYW5TZXNzaW9uVXJsICYmICF1bHRyYXBsYW5MYXVuY2hpbmdcbiAgICA6IGZhbHNlXG4gIGNvbnN0IHVzYWdlID0gdG9vbFVzZUNvbmZpcm0uYXNzaXN0YW50TWVzc2FnZS5tZXNzYWdlLnVzYWdlXG4gIGNvbnN0IHsgbW9kZSwgaXNBdXRvTW9kZUF2YWlsYWJsZSwgaXNCeXBhc3NQZXJtaXNzaW9uc01vZGVBdmFpbGFibGUgfSA9XG4gICAgdG9vbFBlcm1pc3Npb25Db250ZXh0XG4gIGNvbnN0IG9wdGlvbnMgPSB1c2VNZW1vKFxuICAgICgpID0+XG4gICAgICBidWlsZFBsYW5BcHByb3ZhbE9wdGlvbnMoe1xuICAgICAgICBzaG93Q2xlYXJDb250ZXh0LFxuICAgICAgICBzaG93VWx0cmFwbGFuLFxuICAgICAgICB1c2VkUGVyY2VudDogc2hvd0NsZWFyQ29udGV4dFxuICAgICAgICAgID8gZ2V0Q29udGV4dFVzZWRQZXJjZW50KHVzYWdlLCBtb2RlKVxuICAgICAgICAgIDogbnVsbCxcbiAgICAgICAgaXNBdXRvTW9kZUF2YWlsYWJsZSxcbiAgICAgICAgaXNCeXBhc3NQZXJtaXNzaW9uc01vZGVBdmFpbGFibGUsXG4gICAgICAgIG9uRmVlZGJhY2tDaGFuZ2U6IHNldFBsYW5GZWVkYmFjayxcbiAgICAgIH0pLFxuICAgIFtcbiAgICAgIHNob3dDbGVhckNvbnRleHQsXG4gICAgICBzaG93VWx0cmFwbGFuLFxuICAgICAgdXNhZ2UsXG4gICAgICBtb2RlLFxuICAgICAgaXNBdXRvTW9kZUF2YWlsYWJsZSxcbiAgICAgIGlzQnlwYXNzUGVybWlzc2lvbnNNb2RlQXZhaWxhYmxlLFxuICAgIF0sXG4gIClcblxuICBmdW5jdGlvbiBvbkltYWdlUGFzdGUoXG4gICAgYmFzZTY0SW1hZ2U6IHN0cmluZyxcbiAgICBtZWRpYVR5cGU/OiBzdHJpbmcsXG4gICAgZmlsZW5hbWU/OiBzdHJpbmcsXG4gICAgZGltZW5zaW9ucz86IEltYWdlRGltZW5zaW9ucyxcbiAgICBfc291cmNlUGF0aD86IHN0cmluZyxcbiAgKSB7XG4gICAgY29uc3QgcGFzdGVJZCA9IG5leHRQYXN0ZUlkUmVmLmN1cnJlbnQrK1xuICAgIGNvbnN0IG5ld0NvbnRlbnQ6IFBhc3RlZENvbnRlbnQgPSB7XG4gICAgICBpZDogcGFzdGVJZCxcbiAgICAgIHR5cGU6ICdpbWFnZScsXG4gICAgICBjb250ZW50OiBiYXNlNjRJbWFnZSxcbiAgICAgIG1lZGlhVHlwZTogbWVkaWFUeXBlIHx8ICdpbWFnZS9wbmcnLFxuICAgICAgZmlsZW5hbWU6IGZpbGVuYW1lIHx8ICdQYXN0ZWQgaW1hZ2UnLFxuICAgICAgZGltZW5zaW9ucyxcbiAgICB9XG4gICAgY2FjaGVJbWFnZVBhdGgobmV3Q29udGVudClcbiAgICB2b2lkIHN0b3JlSW1hZ2UobmV3Q29udGVudClcbiAgICBzZXRQYXN0ZWRDb250ZW50cyhwcmV2ID0+ICh7IC4uLnByZXYsIFtwYXN0ZUlkXTogbmV3Q29udGVudCB9KSlcbiAgfVxuXG4gIGNvbnN0IG9uUmVtb3ZlSW1hZ2UgPSB1c2VDYWxsYmFjaygoaWQ6IG51bWJlcikgPT4ge1xuICAgIHNldFBhc3RlZENvbnRlbnRzKHByZXYgPT4ge1xuICAgICAgY29uc3QgbmV4dCA9IHsgLi4ucHJldiB9XG4gICAgICBkZWxldGUgbmV4dFtpZF1cbiAgICAgIHJldHVybiBuZXh0XG4gICAgfSlcbiAgfSwgW10pXG5cbiAgY29uc3QgaW1hZ2VBdHRhY2htZW50cyA9IE9iamVjdC52YWx1ZXMocGFzdGVkQ29udGVudHMpLmZpbHRlcihcbiAgICBjID0+IGMudHlwZSA9PT0gJ2ltYWdlJyxcbiAgKVxuICBjb25zdCBoYXNJbWFnZXMgPSBpbWFnZUF0dGFjaG1lbnRzLmxlbmd0aCA+IDBcblxuICAvLyBUT0RPOiBEZWxldGUgdGhlIGJyYW5jaCBhZnRlciBtb3ZpbmcgdG8gVjJcbiAgLy8gVXNlIHRvb2wgbmFtZSB0byBkZXRlY3QgVjIgaW5zdGVhZCBvZiBjaGVja2luZyBpbnB1dC5wbGFuLCBiZWNhdXNlIFBSICMxMDM5NFxuICAvLyBpbmplY3RzIHBsYW4gY29udGVudCBpbnRvIGlucHV0LnBsYW4gZm9yIGhvb2tzL1NESywgd2hpY2ggYnJva2UgdGhlIG9sZCBkZXRlY3Rpb25cbiAgLy8gKHNlZSBpc3N1ZSAjMTA4NzgpXG4gIGNvbnN0IGlzVjIgPSB0b29sVXNlQ29uZmlybS50b29sLm5hbWUgPT09IEVYSVRfUExBTl9NT0RFX1YyX1RPT0xfTkFNRVxuICBjb25zdCBpbnB1dFBsYW4gPSBpc1YyXG4gICAgPyB1bmRlZmluZWRcbiAgICA6ICh0b29sVXNlQ29uZmlybS5pbnB1dC5wbGFuIGFzIHN0cmluZyB8IHVuZGVmaW5lZClcbiAgY29uc3QgcGxhbkZpbGVQYXRoID0gaXNWMiA/IGdldFBsYW5GaWxlUGF0aCgpIDogdW5kZWZpbmVkXG5cbiAgLy8gRXh0cmFjdCBhbGxvd2VkIHByb21wdHMgcmVxdWVzdGVkIGJ5IHRoZSBwbGFuIChBbnQtb25seSBmZWF0dXJlKVxuICBjb25zdCBhbGxvd2VkUHJvbXB0cyA9IHRvb2xVc2VDb25maXJtLmlucHV0LmFsbG93ZWRQcm9tcHRzIGFzXG4gICAgfCBBbGxvd2VkUHJvbXB0W11cbiAgICB8IHVuZGVmaW5lZFxuXG4gIC8vIEdldCB0aGUgcmF3IHBsYW4gdG8gY2hlY2sgaWYgaXQncyBlbXB0eVxuICBjb25zdCByYXdQbGFuID0gaW5wdXRQbGFuID8/IGdldFBsYW4oKVxuICBjb25zdCBpc0VtcHR5ID0gIXJhd1BsYW4gfHwgcmF3UGxhbi50cmltKCkgPT09ICcnXG5cbiAgLy8gQ2FwdHVyZSB0aGUgdmFyaWFudCBvbmNlIG9uIG1vdW50LiBHcm93dGhCb29rIHJlYWRzIGZyb20gYSBkaXNrIGNhY2hlXG4gIC8vIHNvIHRoZSB2YWx1ZSBpcyBzdGFibGUgYWNyb3NzIGEgc2luZ2xlIHBsYW5uaW5nIHNlc3Npb24uIHVuZGVmaW5lZCA9XG4gIC8vIGNvbnRyb2wgYXJtLiBUaGUgdmFyaWFudCBpcyBhIGZpeGVkIDMtdmFsdWUgZW51bSBvZiBzaG9ydCBsaXRlcmFscyxcbiAgLy8gbm90IHVzZXIgaW5wdXQuXG4gIGNvbnN0IFtwbGFuU3RydWN0dXJlVmFyaWFudF0gPSB1c2VTdGF0ZShcbiAgICAoKSA9PlxuICAgICAgKGdldFBld3RlckxlZGdlclZhcmlhbnQoKSA/P1xuICAgICAgICB1bmRlZmluZWQpIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIClcblxuICBjb25zdCBbY3VycmVudFBsYW4sIHNldEN1cnJlbnRQbGFuXSA9IHVzZVN0YXRlKCgpID0+IHtcbiAgICBpZiAoaW5wdXRQbGFuKSByZXR1cm4gaW5wdXRQbGFuXG4gICAgY29uc3QgcGxhbiA9IGdldFBsYW4oKVxuICAgIHJldHVybiAoXG4gICAgICBwbGFuID8/ICdObyBwbGFuIGZvdW5kLiBQbGVhc2Ugd3JpdGUgeW91ciBwbGFuIHRvIHRoZSBwbGFuIGZpbGUgZmlyc3QuJ1xuICAgIClcbiAgfSlcbiAgY29uc3QgW3Nob3dTYXZlTWVzc2FnZSwgc2V0U2hvd1NhdmVNZXNzYWdlXSA9IHVzZVN0YXRlKGZhbHNlKVxuICAvLyBUcmFjayBDdHJsK0cgbG9jYWwgZWRpdHMgc28gdXBkYXRlZElucHV0IGNhbiBpbmNsdWRlIHRoZSBwbGFuICh0aGUgdG9vbFxuICAvLyBvbmx5IGVjaG9lcyB0aGUgcGxhbiBpbiB0b29sX3Jlc3VsdCB3aGVuIGlucHV0LnBsYW4gaXMgc2V0IOKAlCBvdGhlcndpc2VcbiAgLy8gdGhlIG1vZGVsIGFscmVhZHkgaGFzIGl0IGluIGNvbnRleHQgZnJvbSB3cml0aW5nIHRoZSBwbGFuIGZpbGUpLlxuICBjb25zdCBbcGxhbkVkaXRlZExvY2FsbHksIHNldFBsYW5FZGl0ZWRMb2NhbGx5XSA9IHVzZVN0YXRlKGZhbHNlKVxuXG4gIC8vIEF1dG8taGlkZSBzYXZlIG1lc3NhZ2UgYWZ0ZXIgNSBzZWNvbmRzXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKHNob3dTYXZlTWVzc2FnZSkge1xuICAgICAgY29uc3QgdGltZXIgPSBzZXRUaW1lb3V0KHNldFNob3dTYXZlTWVzc2FnZSwgNTAwMCwgZmFsc2UpXG4gICAgICByZXR1cm4gKCkgPT4gY2xlYXJUaW1lb3V0KHRpbWVyKVxuICAgIH1cbiAgfSwgW3Nob3dTYXZlTWVzc2FnZV0pXG5cbiAgLy8gSGFuZGxlIEN0cmwrRyB0byBlZGl0IHBsYW4gaW4gJEVESVRPUiwgU2hpZnQrVGFiIGZvciBhdXRvLWFjY2VwdCBlZGl0c1xuICBjb25zdCBoYW5kbGVLZXlEb3duID0gKGU6IEtleWJvYXJkRXZlbnQpOiB2b2lkID0+IHtcbiAgICBpZiAoZS5jdHJsICYmIGUua2V5ID09PSAnZycpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3BsYW5fZXh0ZXJuYWxfZWRpdG9yX3VzZWQnLCB7fSlcblxuICAgICAgdm9pZCAoYXN5bmMgKCkgPT4ge1xuICAgICAgICBpZiAoaXNWMiAmJiBwbGFuRmlsZVBhdGgpIHtcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBlZGl0RmlsZUluRWRpdG9yKHBsYW5GaWxlUGF0aClcbiAgICAgICAgICBpZiAocmVzdWx0LmVycm9yKSB7XG4gICAgICAgICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICAgICAgICBrZXk6ICdleHRlcm5hbC1lZGl0b3ItZXJyb3InLFxuICAgICAgICAgICAgICB0ZXh0OiByZXN1bHQuZXJyb3IsXG4gICAgICAgICAgICAgIGNvbG9yOiAnd2FybmluZycsXG4gICAgICAgICAgICAgIHByaW9yaXR5OiAnaGlnaCcsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAocmVzdWx0LmNvbnRlbnQgIT09IG51bGwpIHtcbiAgICAgICAgICAgIGlmIChyZXN1bHQuY29udGVudCAhPT0gY3VycmVudFBsYW4pIHNldFBsYW5FZGl0ZWRMb2NhbGx5KHRydWUpXG4gICAgICAgICAgICBzZXRDdXJyZW50UGxhbihyZXN1bHQuY29udGVudClcbiAgICAgICAgICAgIHNldFNob3dTYXZlTWVzc2FnZSh0cnVlKVxuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBlZGl0UHJvbXB0SW5FZGl0b3IoY3VycmVudFBsYW4pXG4gICAgICAgICAgaWYgKHJlc3VsdC5lcnJvcikge1xuICAgICAgICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAgICAgICAga2V5OiAnZXh0ZXJuYWwtZWRpdG9yLWVycm9yJyxcbiAgICAgICAgICAgICAgdGV4dDogcmVzdWx0LmVycm9yLFxuICAgICAgICAgICAgICBjb2xvcjogJ3dhcm5pbmcnLFxuICAgICAgICAgICAgICBwcmlvcml0eTogJ2hpZ2gnLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHJlc3VsdC5jb250ZW50ICE9PSBudWxsICYmIHJlc3VsdC5jb250ZW50ICE9PSBjdXJyZW50UGxhbikge1xuICAgICAgICAgICAgc2V0Q3VycmVudFBsYW4ocmVzdWx0LmNvbnRlbnQpXG4gICAgICAgICAgICBzZXRTaG93U2F2ZU1lc3NhZ2UodHJ1ZSlcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pKClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIFNoaWZ0K1RhYiBpbW1lZGlhdGVseSBzZWxlY3RzIFwiYXV0by1hY2NlcHQgZWRpdHNcIlxuICAgIGlmIChlLnNoaWZ0ICYmIGUua2V5ID09PSAndGFiJykge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICB2b2lkIGhhbmRsZVJlc3BvbnNlKFxuICAgICAgICBzaG93Q2xlYXJDb250ZXh0ID8gJ3llcy1hY2NlcHQtZWRpdHMnIDogJ3llcy1hY2NlcHQtZWRpdHMta2VlcC1jb250ZXh0JyxcbiAgICAgIClcbiAgICAgIHJldHVyblxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGZ1bmN0aW9uIGhhbmRsZVJlc3BvbnNlKHZhbHVlOiBSZXNwb25zZVZhbHVlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgdHJpbW1lZEZlZWRiYWNrID0gcGxhbkZlZWRiYWNrLnRyaW0oKVxuICAgIGNvbnN0IGFjY2VwdEZlZWRiYWNrID0gdHJpbW1lZEZlZWRiYWNrIHx8IHVuZGVmaW5lZFxuXG4gICAgLy8gVWx0cmFwbGFuOiByZWplY3QgbG9jYWxseSwgdGVsZXBvcnQgdGhlIHBsYW4gdG8gQ0NSIGFzIGEgc2VlZCBkcmFmdC5cbiAgICAvLyBEaWFsb2cgZGlzbWlzc2VzIGltbWVkaWF0ZWx5IHNvIHRoZSBxdWVyeSBsb29wIHVuYmxvY2tzOyB0aGUgdGVsZXBvcnRcbiAgICAvLyBydW5zIGRldGFjaGVkIGFuZCBpdHMgbGF1bmNoIG1lc3NhZ2UgbGFuZHMgdmlhIHRoZSBjb21tYW5kIHF1ZXVlLlxuICAgIGlmICh2YWx1ZSA9PT0gJ3VsdHJhcGxhbicpIHtcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9wbGFuX2V4aXQnLCB7XG4gICAgICAgIHBsYW5MZW5ndGhDaGFyczogY3VycmVudFBsYW4ubGVuZ3RoLFxuICAgICAgICBvdXRjb21lOlxuICAgICAgICAgICd1bHRyYXBsYW4nIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGludGVydmlld1BoYXNlRW5hYmxlZDogaXNQbGFuTW9kZUludGVydmlld1BoYXNlRW5hYmxlZCgpLFxuICAgICAgICBwbGFuU3RydWN0dXJlVmFyaWFudCxcbiAgICAgIH0pXG4gICAgICBvbkRvbmUoKVxuICAgICAgb25SZWplY3QoKVxuICAgICAgdG9vbFVzZUNvbmZpcm0ub25SZWplY3QoXG4gICAgICAgICdQbGFuIGJlaW5nIHJlZmluZWQgdmlhIFVsdHJhcGxhbiDigJQgcGxlYXNlIHdhaXQgZm9yIHRoZSByZXN1bHQuJyxcbiAgICAgIClcbiAgICAgIHZvaWQgbGF1bmNoVWx0cmFwbGFuKHtcbiAgICAgICAgYmx1cmI6ICcnLFxuICAgICAgICBzZWVkUGxhbjogY3VycmVudFBsYW4sXG4gICAgICAgIGdldEFwcFN0YXRlOiBzdG9yZS5nZXRTdGF0ZSxcbiAgICAgICAgc2V0QXBwU3RhdGU6IHN0b3JlLnNldFN0YXRlLFxuICAgICAgICBzaWduYWw6IG5ldyBBYm9ydENvbnRyb2xsZXIoKS5zaWduYWwsXG4gICAgICB9KVxuICAgICAgICAudGhlbihtc2cgPT5cbiAgICAgICAgICBlbnF1ZXVlUGVuZGluZ05vdGlmaWNhdGlvbih7IHZhbHVlOiBtc2csIG1vZGU6ICd0YXNrLW5vdGlmaWNhdGlvbicgfSksXG4gICAgICAgIClcbiAgICAgICAgLmNhdGNoKGxvZ0Vycm9yKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gVjE6IHBhc3MgcGxhbiBpbiBpbnB1dC4gVjI6IHBsYW4gaXMgb24gZGlzaywgYnV0IGlmIHRoZSB1c2VyIGVkaXRlZCBpdFxuICAgIC8vIHZpYSBDdHJsK0cgd2UgcGFzcyBpdCB0aHJvdWdoIHNvIHRoZSB0b29sIGVjaG9lcyB0aGUgZWRpdCBpbiB0b29sX3Jlc3VsdFxuICAgIC8vIChvdGhlcndpc2UgdGhlIG1vZGVsIG5ldmVyIHNlZXMgdGhlIHVzZXIncyBjaGFuZ2VzKS5cbiAgICBjb25zdCB1cGRhdGVkSW5wdXQgPSBpc1YyICYmICFwbGFuRWRpdGVkTG9jYWxseSA/IHt9IDogeyBwbGFuOiBjdXJyZW50UGxhbiB9XG5cbiAgICAvLyBJZiBhdXRvIHdhcyBhY3RpdmUgZHVyaW5nIHBsYW4gKGZyb20gYXV0byBtb2RlIG9yIG9wdC1pbikgYW5kIE5PVCBnb2luZ1xuICAgIC8vIHRvIGF1dG8sIGRlYWN0aXZhdGUgYXV0byArIHJlc3RvcmUgcGVybWlzc2lvbnMgKyBmaXJlIGV4aXQgYXR0YWNobWVudC5cbiAgICBpZiAoZmVhdHVyZSgnVFJBTlNDUklQVF9DTEFTU0lGSUVSJykpIHtcbiAgICAgIGNvbnN0IGdvaW5nVG9BdXRvID1cbiAgICAgICAgKHZhbHVlID09PSAneWVzLXJlc3VtZS1hdXRvLW1vZGUnIHx8XG4gICAgICAgICAgdmFsdWUgPT09ICd5ZXMtYXV0by1jbGVhci1jb250ZXh0JykgJiZcbiAgICAgICAgaXNBdXRvTW9kZUdhdGVFbmFibGVkKClcbiAgICAgIC8vIGlzQXV0b01vZGVBY3RpdmUoKSBpcyB0aGUgYXV0aG9yaXRhdGl2ZSBzaWduYWwg4oCUIHByZVBsYW5Nb2RlL1xuICAgICAgLy8gc3RyaXBwZWREYW5nZXJvdXNSdWxlcyBhcmUgc3RhbGUgYWZ0ZXIgdHJhbnNpdGlvblBsYW5BdXRvTW9kZVxuICAgICAgLy8gZGVhY3RpdmF0ZXMgbWlkLXBsYW4gKHdvdWxkIGNhdXNlIGR1cGxpY2F0ZSBleGl0IGF0dGFjaG1lbnQpLlxuICAgICAgY29uc3QgYXV0b1dhc1VzZWREdXJpbmdQbGFuID1cbiAgICAgICAgYXV0b01vZGVTdGF0ZU1vZHVsZT8uaXNBdXRvTW9kZUFjdGl2ZSgpID8/IGZhbHNlXG4gICAgICBpZiAodmFsdWUgIT09ICdubycgJiYgIWdvaW5nVG9BdXRvICYmIGF1dG9XYXNVc2VkRHVyaW5nUGxhbikge1xuICAgICAgICBhdXRvTW9kZVN0YXRlTW9kdWxlPy5zZXRBdXRvTW9kZUFjdGl2ZShmYWxzZSlcbiAgICAgICAgc2V0TmVlZHNBdXRvTW9kZUV4aXRBdHRhY2htZW50KHRydWUpXG4gICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4gKHtcbiAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dDoge1xuICAgICAgICAgICAgLi4ucmVzdG9yZURhbmdlcm91c1Blcm1pc3Npb25zKHByZXYudG9vbFBlcm1pc3Npb25Db250ZXh0KSxcbiAgICAgICAgICAgIHByZVBsYW5Nb2RlOiB1bmRlZmluZWQsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSkpXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQ2xlYXItY29udGV4dCBvcHRpb25zOiBzZXQgcGVuZGluZyBwbGFuIGltcGxlbWVudGF0aW9uIGFuZCByZWplY3QgdGhlIGRpYWxvZ1xuICAgIC8vIFRoZSBSRVBMIHdpbGwgaGFuZGxlIGNvbnRleHQgY2xlYXIgYW5kIHRyaWdnZXIgYSBmcmVzaCBxdWVyeVxuICAgIC8vIEtlZXAtY29udGV4dCBvcHRpb25zIHNraXAgdGhpcyBibG9jayBhbmQgZ28gdGhyb3VnaCB0aGUgbm9ybWFsIGZsb3cgYmVsb3dcbiAgICBjb25zdCBpc1Jlc3VtZUF1dG9PcHRpb24gPSBmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKVxuICAgICAgPyB2YWx1ZSA9PT0gJ3llcy1yZXN1bWUtYXV0by1tb2RlJ1xuICAgICAgOiBmYWxzZVxuICAgIGNvbnN0IGlzS2VlcENvbnRleHRPcHRpb24gPVxuICAgICAgdmFsdWUgPT09ICd5ZXMtYWNjZXB0LWVkaXRzLWtlZXAtY29udGV4dCcgfHxcbiAgICAgIHZhbHVlID09PSAneWVzLWRlZmF1bHQta2VlcC1jb250ZXh0JyB8fFxuICAgICAgaXNSZXN1bWVBdXRvT3B0aW9uXG5cbiAgICBpZiAodmFsdWUgIT09ICdubycpIHtcbiAgICAgIGF1dG9OYW1lU2Vzc2lvbkZyb21QbGFuKGN1cnJlbnRQbGFuLCBzZXRBcHBTdGF0ZSwgIWlzS2VlcENvbnRleHRPcHRpb24pXG4gICAgfVxuXG4gICAgaWYgKHZhbHVlICE9PSAnbm8nICYmICFpc0tlZXBDb250ZXh0T3B0aW9uKSB7XG4gICAgICAvLyBEZXRlcm1pbmUgdGhlIHBlcm1pc3Npb24gbW9kZSBiYXNlZCBvbiB0aGUgc2VsZWN0ZWQgb3B0aW9uXG4gICAgICBsZXQgbW9kZTogUGVybWlzc2lvbk1vZGUgPSAnZGVmYXVsdCdcbiAgICAgIGlmICh2YWx1ZSA9PT0gJ3llcy1ieXBhc3MtcGVybWlzc2lvbnMnKSB7XG4gICAgICAgIG1vZGUgPSAnYnlwYXNzUGVybWlzc2lvbnMnXG4gICAgICB9IGVsc2UgaWYgKHZhbHVlID09PSAneWVzLWFjY2VwdC1lZGl0cycpIHtcbiAgICAgICAgbW9kZSA9ICdhY2NlcHRFZGl0cydcbiAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgIGZlYXR1cmUoJ1RSQU5TQ1JJUFRfQ0xBU1NJRklFUicpICYmXG4gICAgICAgIHZhbHVlID09PSAneWVzLWF1dG8tY2xlYXItY29udGV4dCcgJiZcbiAgICAgICAgaXNBdXRvTW9kZUdhdGVFbmFibGVkKClcbiAgICAgICkge1xuICAgICAgICAvLyBSRVBMJ3MgcHJvY2Vzc0luaXRpYWxNZXNzYWdlIGhhbmRsZXMgc3RyaXBEYW5nZXJvdXNQZXJtaXNzaW9ucyArIG1vZGUsXG4gICAgICAgIC8vIGJ1dCBkb2VzIE5PVCBzZXQgYXV0b01vZGVBY3RpdmUuIEdhdGUtb2ZmIGZhbGxzIHRocm91Z2ggdG8gJ2RlZmF1bHQnLlxuICAgICAgICBtb2RlID0gJ2F1dG8nXG4gICAgICAgIGF1dG9Nb2RlU3RhdGVNb2R1bGU/LnNldEF1dG9Nb2RlQWN0aXZlKHRydWUpXG4gICAgICB9XG5cbiAgICAgIC8vIExvZyBwbGFuIGV4aXQgZXZlbnRcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9wbGFuX2V4aXQnLCB7XG4gICAgICAgIHBsYW5MZW5ndGhDaGFyczogY3VycmVudFBsYW4ubGVuZ3RoLFxuICAgICAgICBvdXRjb21lOlxuICAgICAgICAgIHZhbHVlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGNsZWFyQ29udGV4dDogdHJ1ZSxcbiAgICAgICAgaW50ZXJ2aWV3UGhhc2VFbmFibGVkOiBpc1BsYW5Nb2RlSW50ZXJ2aWV3UGhhc2VFbmFibGVkKCksXG4gICAgICAgIHBsYW5TdHJ1Y3R1cmVWYXJpYW50LFxuICAgICAgICBoYXNGZWVkYmFjazogISFhY2NlcHRGZWVkYmFjayxcbiAgICAgIH0pXG5cbiAgICAgIC8vIFNldCBpbml0aWFsIG1lc3NhZ2UgLSBSRVBMIHdpbGwgaGFuZGxlIGNvbnRleHQgY2xlYXIgYW5kIGZyZXNoIHF1ZXJ5XG4gICAgICAvLyBBZGQgdmVyaWZpY2F0aW9uIGluc3RydWN0aW9uIGlmIHRoZSBmZWF0dXJlIGlzIGVuYWJsZWRcbiAgICAgIC8vIERlYWQgY29kZSBlbGltaW5hdGlvbjogQ0xBVURFX0NPREVfVkVSSUZZX1BMQU49J2ZhbHNlJyBpbiBleHRlcm5hbCBidWlsZHMsIHNvID09PSAndHJ1ZScgY2hlY2sgYWxsb3dzIEJ1biB0byBlbGltaW5hdGUgdGhlIHN0cmluZ1xuICAgICAgY29uc3QgdmVyaWZpY2F0aW9uSW5zdHJ1Y3Rpb24gPVxuICAgICAgICB1bmRlZmluZWQgPT09ICd0cnVlJ1xuICAgICAgICAgID8gYFxcblxcbklNUE9SVEFOVDogV2hlbiB5b3UgaGF2ZSBmaW5pc2hlZCBpbXBsZW1lbnRpbmcgdGhlIHBsYW4sIHlvdSBNVVNUIGNhbGwgdGhlIFwiVmVyaWZ5UGxhbkV4ZWN1dGlvblwiIHRvb2wgZGlyZWN0bHkgKE5PVCB0aGUgJHtBR0VOVF9UT09MX05BTUV9IHRvb2wgb3IgYW4gYWdlbnQpIHRvIHRyaWdnZXIgYmFja2dyb3VuZCB2ZXJpZmljYXRpb24uYFxuICAgICAgICAgIDogJydcblxuICAgICAgLy8gQ2FwdHVyZSB0aGUgdHJhbnNjcmlwdCBwYXRoIGJlZm9yZSBjb250ZXh0IGlzIGNsZWFyZWQgKHNlc3Npb24gSUQgd2lsbCBiZSByZWdlbmVyYXRlZClcbiAgICAgIGNvbnN0IHRyYW5zY3JpcHRQYXRoID0gZ2V0VHJhbnNjcmlwdFBhdGgoKVxuICAgICAgY29uc3QgdHJhbnNjcmlwdEhpbnQgPSBgXFxuXFxuSWYgeW91IG5lZWQgc3BlY2lmaWMgZGV0YWlscyBmcm9tIGJlZm9yZSBleGl0aW5nIHBsYW4gbW9kZSAobGlrZSBleGFjdCBjb2RlIHNuaXBwZXRzLCBlcnJvciBtZXNzYWdlcywgb3IgY29udGVudCB5b3UgZ2VuZXJhdGVkKSwgcmVhZCB0aGUgZnVsbCB0cmFuc2NyaXB0IGF0OiAke3RyYW5zY3JpcHRQYXRofWBcblxuICAgICAgY29uc3QgdGVhbUhpbnQgPSBpc0FnZW50U3dhcm1zRW5hYmxlZCgpXG4gICAgICAgID8gYFxcblxcbklmIHRoaXMgcGxhbiBjYW4gYmUgYnJva2VuIGRvd24gaW50byBtdWx0aXBsZSBpbmRlcGVuZGVudCB0YXNrcywgY29uc2lkZXIgdXNpbmcgdGhlICR7VEVBTV9DUkVBVEVfVE9PTF9OQU1FfSB0b29sIHRvIGNyZWF0ZSBhIHRlYW0gYW5kIHBhcmFsbGVsaXplIHRoZSB3b3JrLmBcbiAgICAgICAgOiAnJ1xuXG4gICAgICBjb25zdCBmZWVkYmFja1N1ZmZpeCA9IGFjY2VwdEZlZWRiYWNrXG4gICAgICAgID8gYFxcblxcblVzZXIgZmVlZGJhY2sgb24gdGhpcyBwbGFuOiAke2FjY2VwdEZlZWRiYWNrfWBcbiAgICAgICAgOiAnJ1xuXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgIC4uLnByZXYsXG4gICAgICAgIGluaXRpYWxNZXNzYWdlOiB7XG4gICAgICAgICAgbWVzc2FnZToge1xuICAgICAgICAgICAgLi4uY3JlYXRlVXNlck1lc3NhZ2Uoe1xuICAgICAgICAgICAgICBjb250ZW50OiBgSW1wbGVtZW50IHRoZSBmb2xsb3dpbmcgcGxhbjpcXG5cXG4ke2N1cnJlbnRQbGFufSR7dmVyaWZpY2F0aW9uSW5zdHJ1Y3Rpb259JHt0cmFuc2NyaXB0SGludH0ke3RlYW1IaW50fSR7ZmVlZGJhY2tTdWZmaXh9YCxcbiAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgcGxhbkNvbnRlbnQ6IGN1cnJlbnRQbGFuLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgY2xlYXJDb250ZXh0OiB0cnVlLFxuICAgICAgICAgIG1vZGUsXG4gICAgICAgICAgYWxsb3dlZFByb21wdHMsXG4gICAgICAgIH0sXG4gICAgICB9KSlcblxuICAgICAgc2V0SGFzRXhpdGVkUGxhbk1vZGUodHJ1ZSlcbiAgICAgIG9uRG9uZSgpXG4gICAgICBvblJlamVjdCgpXG4gICAgICAvLyBSZWplY3QgdGhlIHRvb2wgdXNlIHRvIHVuYmxvY2sgdGhlIHF1ZXJ5IGxvb3BcbiAgICAgIC8vIFRoZSBSRVBMIHdpbGwgc2VlIHBlbmRpbmdJbml0aWFsUXVlcnkgYW5kIHRyaWdnZXIgZnJlc2ggcXVlcnlcbiAgICAgIHRvb2xVc2VDb25maXJtLm9uUmVqZWN0KClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIEhhbmRsZSBhdXRvIGtlZXAtY29udGV4dCBvcHRpb24g4oCUIG5lZWRzIHNwZWNpYWwgaGFuZGxpbmcgYmVjYXVzZVxuICAgIC8vIGJ1aWxkUGVybWlzc2lvblVwZGF0ZXMgbWFwcyBhdXRvIHRvICdkZWZhdWx0JyB2aWEgdG9FeHRlcm5hbFBlcm1pc3Npb25Nb2RlLlxuICAgIC8vIFdlIHNldCB0aGUgbW9kZSBkaXJlY3RseSB2aWEgc2V0QXBwU3RhdGUgYW5kIHN5bmMgdGhlIGJvb3RzdHJhcCBzdGF0ZS5cbiAgICBpZiAoXG4gICAgICBmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSAmJlxuICAgICAgdmFsdWUgPT09ICd5ZXMtcmVzdW1lLWF1dG8tbW9kZScgJiZcbiAgICAgIGlzQXV0b01vZGVHYXRlRW5hYmxlZCgpXG4gICAgKSB7XG4gICAgICBsb2dFdmVudCgndGVuZ3VfcGxhbl9leGl0Jywge1xuICAgICAgICBwbGFuTGVuZ3RoQ2hhcnM6IGN1cnJlbnRQbGFuLmxlbmd0aCxcbiAgICAgICAgb3V0Y29tZTpcbiAgICAgICAgICB2YWx1ZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBjbGVhckNvbnRleHQ6IGZhbHNlLFxuICAgICAgICBpbnRlcnZpZXdQaGFzZUVuYWJsZWQ6IGlzUGxhbk1vZGVJbnRlcnZpZXdQaGFzZUVuYWJsZWQoKSxcbiAgICAgICAgcGxhblN0cnVjdHVyZVZhcmlhbnQsXG4gICAgICAgIGhhc0ZlZWRiYWNrOiAhIWFjY2VwdEZlZWRiYWNrLFxuICAgICAgfSlcbiAgICAgIHNldEhhc0V4aXRlZFBsYW5Nb2RlKHRydWUpXG4gICAgICBzZXROZWVkc1BsYW5Nb2RlRXhpdEF0dGFjaG1lbnQodHJ1ZSlcbiAgICAgIGF1dG9Nb2RlU3RhdGVNb2R1bGU/LnNldEF1dG9Nb2RlQWN0aXZlKHRydWUpXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgIC4uLnByZXYsXG4gICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dDogc3RyaXBEYW5nZXJvdXNQZXJtaXNzaW9uc0ZvckF1dG9Nb2RlKHtcbiAgICAgICAgICAuLi5wcmV2LnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgICBtb2RlOiAnYXV0bycsXG4gICAgICAgICAgcHJlUGxhbk1vZGU6IHVuZGVmaW5lZCxcbiAgICAgICAgfSksXG4gICAgICB9KSlcbiAgICAgIG9uRG9uZSgpXG4gICAgICB0b29sVXNlQ29uZmlybS5vbkFsbG93KHVwZGF0ZWRJbnB1dCwgW10sIGFjY2VwdEZlZWRiYWNrKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIGtlZXAtY29udGV4dCBvcHRpb25zIChnb2VzIHRocm91Z2ggbm9ybWFsIG9uQWxsb3cgZmxvdylcbiAgICAvLyB5ZXMtcmVzdW1lLWF1dG8tbW9kZSBmYWxscyB0aHJvdWdoIGhlcmUgd2hlbiB0aGUgYXV0byBtb2RlIGdhdGUgaXNcbiAgICAvLyBkaXNhYmxlZCAoZS5nLiBjaXJjdWl0IGJyZWFrZXIgZmlyZWQgYWZ0ZXIgdGhlIGRpYWxvZyByZW5kZXJlZCkuXG4gICAgLy8gV2l0aG91dCB0aGlzIGZhbGxiYWNrIHRoZSBmdW5jdGlvbiB3b3VsZCByZXR1cm4gd2l0aG91dCByZXNvbHZpbmcgdGhlXG4gICAgLy8gZGlhbG9nLCBsZWF2aW5nIHRoZSBxdWVyeSBsb29wIGJsb2NrZWQgYW5kIHNhZmV0eSBzdGF0ZSBjb3JydXB0ZWQuXG4gICAgY29uc3Qga2VlcENvbnRleHRNb2RlczogUmVjb3JkPHN0cmluZywgUGVybWlzc2lvbk1vZGU+ID0ge1xuICAgICAgJ3llcy1hY2NlcHQtZWRpdHMta2VlcC1jb250ZXh0JzpcbiAgICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0LmlzQnlwYXNzUGVybWlzc2lvbnNNb2RlQXZhaWxhYmxlXG4gICAgICAgICAgPyAnYnlwYXNzUGVybWlzc2lvbnMnXG4gICAgICAgICAgOiAnYWNjZXB0RWRpdHMnLFxuICAgICAgJ3llcy1kZWZhdWx0LWtlZXAtY29udGV4dCc6ICdkZWZhdWx0JyxcbiAgICAgIC4uLihmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKVxuICAgICAgICA/IHsgJ3llcy1yZXN1bWUtYXV0by1tb2RlJzogJ2RlZmF1bHQnIGFzIGNvbnN0IH1cbiAgICAgICAgOiB7fSksXG4gICAgfVxuICAgIGNvbnN0IGtlZXBDb250ZXh0TW9kZSA9IGtlZXBDb250ZXh0TW9kZXNbdmFsdWVdXG4gICAgaWYgKGtlZXBDb250ZXh0TW9kZSkge1xuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3BsYW5fZXhpdCcsIHtcbiAgICAgICAgcGxhbkxlbmd0aENoYXJzOiBjdXJyZW50UGxhbi5sZW5ndGgsXG4gICAgICAgIG91dGNvbWU6XG4gICAgICAgICAgdmFsdWUgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgY2xlYXJDb250ZXh0OiBmYWxzZSxcbiAgICAgICAgaW50ZXJ2aWV3UGhhc2VFbmFibGVkOiBpc1BsYW5Nb2RlSW50ZXJ2aWV3UGhhc2VFbmFibGVkKCksXG4gICAgICAgIHBsYW5TdHJ1Y3R1cmVWYXJpYW50LFxuICAgICAgICBoYXNGZWVkYmFjazogISFhY2NlcHRGZWVkYmFjayxcbiAgICAgIH0pXG4gICAgICBzZXRIYXNFeGl0ZWRQbGFuTW9kZSh0cnVlKVxuICAgICAgc2V0TmVlZHNQbGFuTW9kZUV4aXRBdHRhY2htZW50KHRydWUpXG4gICAgICBvbkRvbmUoKVxuICAgICAgdG9vbFVzZUNvbmZpcm0ub25BbGxvdyhcbiAgICAgICAgdXBkYXRlZElucHV0LFxuICAgICAgICBidWlsZFBlcm1pc3Npb25VcGRhdGVzKGtlZXBDb250ZXh0TW9kZSwgYWxsb3dlZFByb21wdHMpLFxuICAgICAgICBhY2NlcHRGZWVkYmFjayxcbiAgICAgIClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIEhhbmRsZSBzdGFuZGFyZCBhcHByb3ZhbCBvcHRpb25zXG4gICAgY29uc3Qgc3RhbmRhcmRNb2RlczogUmVjb3JkPHN0cmluZywgUGVybWlzc2lvbk1vZGU+ID0ge1xuICAgICAgJ3llcy1ieXBhc3MtcGVybWlzc2lvbnMnOiAnYnlwYXNzUGVybWlzc2lvbnMnLFxuICAgICAgJ3llcy1hY2NlcHQtZWRpdHMnOiAnYWNjZXB0RWRpdHMnLFxuICAgIH1cbiAgICBjb25zdCBzdGFuZGFyZE1vZGUgPSBzdGFuZGFyZE1vZGVzW3ZhbHVlXVxuICAgIGlmIChzdGFuZGFyZE1vZGUpIHtcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9wbGFuX2V4aXQnLCB7XG4gICAgICAgIHBsYW5MZW5ndGhDaGFyczogY3VycmVudFBsYW4ubGVuZ3RoLFxuICAgICAgICBvdXRjb21lOlxuICAgICAgICAgIHZhbHVlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGludGVydmlld1BoYXNlRW5hYmxlZDogaXNQbGFuTW9kZUludGVydmlld1BoYXNlRW5hYmxlZCgpLFxuICAgICAgICBwbGFuU3RydWN0dXJlVmFyaWFudCxcbiAgICAgICAgaGFzRmVlZGJhY2s6ICEhYWNjZXB0RmVlZGJhY2ssXG4gICAgICB9KVxuICAgICAgc2V0SGFzRXhpdGVkUGxhbk1vZGUodHJ1ZSlcbiAgICAgIHNldE5lZWRzUGxhbk1vZGVFeGl0QXR0YWNobWVudCh0cnVlKVxuICAgICAgb25Eb25lKClcbiAgICAgIHRvb2xVc2VDb25maXJtLm9uQWxsb3coXG4gICAgICAgIHVwZGF0ZWRJbnB1dCxcbiAgICAgICAgYnVpbGRQZXJtaXNzaW9uVXBkYXRlcyhzdGFuZGFyZE1vZGUsIGFsbG93ZWRQcm9tcHRzKSxcbiAgICAgICAgYWNjZXB0RmVlZGJhY2ssXG4gICAgICApXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgJ25vJyAtIHN0YXkgaW4gcGxhbiBtb2RlXG4gICAgaWYgKHZhbHVlID09PSAnbm8nKSB7XG4gICAgICBpZiAoIXRyaW1tZWRGZWVkYmFjayAmJiAhaGFzSW1hZ2VzKSB7XG4gICAgICAgIC8vIE5vIGZlZWRiYWNrIHlldCAtIHVzZXIgaXMgc3RpbGwgb24gdGhlIGlucHV0IGZpZWxkXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICBsb2dFdmVudCgndGVuZ3VfcGxhbl9leGl0Jywge1xuICAgICAgICBwbGFuTGVuZ3RoQ2hhcnM6IGN1cnJlbnRQbGFuLmxlbmd0aCxcbiAgICAgICAgb3V0Y29tZTpcbiAgICAgICAgICAnbm8nIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGludGVydmlld1BoYXNlRW5hYmxlZDogaXNQbGFuTW9kZUludGVydmlld1BoYXNlRW5hYmxlZCgpLFxuICAgICAgICBwbGFuU3RydWN0dXJlVmFyaWFudCxcbiAgICAgIH0pXG5cbiAgICAgIC8vIENvbnZlcnQgcGFzdGVkIGltYWdlcyB0byBJbWFnZUJsb2NrUGFyYW1bXSB3aXRoIHJlc2l6aW5nXG4gICAgICBsZXQgaW1hZ2VCbG9ja3M6IEltYWdlQmxvY2tQYXJhbVtdIHwgdW5kZWZpbmVkXG4gICAgICBpZiAoaGFzSW1hZ2VzKSB7XG4gICAgICAgIGltYWdlQmxvY2tzID0gYXdhaXQgUHJvbWlzZS5hbGwoXG4gICAgICAgICAgaW1hZ2VBdHRhY2htZW50cy5tYXAoYXN5bmMgaW1nID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGJsb2NrOiBJbWFnZUJsb2NrUGFyYW0gPSB7XG4gICAgICAgICAgICAgIHR5cGU6ICdpbWFnZScsXG4gICAgICAgICAgICAgIHNvdXJjZToge1xuICAgICAgICAgICAgICAgIHR5cGU6ICdiYXNlNjQnLFxuICAgICAgICAgICAgICAgIG1lZGlhX3R5cGU6IChpbWcubWVkaWFUeXBlIHx8XG4gICAgICAgICAgICAgICAgICAnaW1hZ2UvcG5nJykgYXMgQmFzZTY0SW1hZ2VTb3VyY2VbJ21lZGlhX3R5cGUnXSxcbiAgICAgICAgICAgICAgICBkYXRhOiBpbWcuY29udGVudCxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHJlc2l6ZWQgPSBhd2FpdCBtYXliZVJlc2l6ZUFuZERvd25zYW1wbGVJbWFnZUJsb2NrKGJsb2NrKVxuICAgICAgICAgICAgcmV0dXJuIHJlc2l6ZWQuYmxvY2tcbiAgICAgICAgICB9KSxcbiAgICAgICAgKVxuICAgICAgfVxuXG4gICAgICBvbkRvbmUoKVxuICAgICAgb25SZWplY3QoKVxuICAgICAgdG9vbFVzZUNvbmZpcm0ub25SZWplY3QoXG4gICAgICAgIHRyaW1tZWRGZWVkYmFjayB8fCAoaGFzSW1hZ2VzID8gJyhTZWUgYXR0YWNoZWQgaW1hZ2UpJyA6IHVuZGVmaW5lZCksXG4gICAgICAgIGltYWdlQmxvY2tzICYmIGltYWdlQmxvY2tzLmxlbmd0aCA+IDAgPyBpbWFnZUJsb2NrcyA6IHVuZGVmaW5lZCxcbiAgICAgIClcbiAgICB9XG4gIH1cblxuICBjb25zdCBlZGl0b3IgPSBnZXRFeHRlcm5hbEVkaXRvcigpXG4gIGNvbnN0IGVkaXRvck5hbWUgPSBlZGl0b3IgPyB0b0lERURpc3BsYXlOYW1lKGVkaXRvcikgOiBudWxsXG5cbiAgLy8gU3RpY2t5IGZvb3Rlcjogd2hlbiBzZXRTdGlja3lGb290ZXIgaXMgcHJvdmlkZWQgKGZ1bGxzY3JlZW4gbW9kZSksIHRoZVxuICAvLyBTZWxlY3Qgb3B0aW9ucyByZW5kZXIgaW4gRnVsbHNjcmVlbkxheW91dCdzIGBib3R0b21gIHNsb3Qgc28gdGhleSBzdGF5XG4gIC8vIHZpc2libGUgd2hpbGUgdGhlIHVzZXIgc2Nyb2xscyB0aHJvdWdoIGEgbG9uZyBwbGFuLiBoYW5kbGVSZXNwb25zZSBpc1xuICAvLyB3cmFwcGVkIGluIGEgcmVmIHNvIHRoZSBKU1ggKHNldCBvbmNlIHBlciBvcHRpb25zL2ltYWdlcyBjaGFuZ2UpIGNhbiBjYWxsXG4gIC8vIHRoZSBsYXRlc3QgY2xvc3VyZSB3aXRob3V0IHJlLXJlZ2lzdGVyaW5nIG9uIGV2ZXJ5IGtleXN0cm9rZS4gUmVhY3RcbiAgLy8gcmVjb25jaWxlcyB0aGUgc3RpY2t5LWZvb3RlciBTZWxlY3QgYnkgdHlwZSwgcHJlc2VydmluZyBmb2N1cy9pbnB1dCBzdGF0ZS5cbiAgY29uc3QgaGFuZGxlUmVzcG9uc2VSZWYgPSB1c2VSZWYoaGFuZGxlUmVzcG9uc2UpXG4gIGhhbmRsZVJlc3BvbnNlUmVmLmN1cnJlbnQgPSBoYW5kbGVSZXNwb25zZVxuICBjb25zdCBoYW5kbGVDYW5jZWxSZWYgPSB1c2VSZWY8KCkgPT4gdm9pZD4odW5kZWZpbmVkKVxuICBoYW5kbGVDYW5jZWxSZWYuY3VycmVudCA9ICgpID0+IHtcbiAgICBsb2dFdmVudCgndGVuZ3VfcGxhbl9leGl0Jywge1xuICAgICAgcGxhbkxlbmd0aENoYXJzOiBjdXJyZW50UGxhbi5sZW5ndGgsXG4gICAgICBvdXRjb21lOlxuICAgICAgICAnbm8nIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICBpbnRlcnZpZXdQaGFzZUVuYWJsZWQ6IGlzUGxhbk1vZGVJbnRlcnZpZXdQaGFzZUVuYWJsZWQoKSxcbiAgICAgIHBsYW5TdHJ1Y3R1cmVWYXJpYW50LFxuICAgIH0pXG4gICAgb25Eb25lKClcbiAgICBvblJlamVjdCgpXG4gICAgdG9vbFVzZUNvbmZpcm0ub25SZWplY3QoKVxuICB9XG4gIGNvbnN0IHVzZVN0aWNreUZvb3RlciA9ICFpc0VtcHR5ICYmICEhc2V0U3RpY2t5Rm9vdGVyXG4gIHVzZUxheW91dEVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKCF1c2VTdGlja3lGb290ZXIpIHJldHVyblxuICAgIHNldFN0aWNreUZvb3RlcihcbiAgICAgIDxCb3hcbiAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgIGJvcmRlclN0eWxlPVwicm91bmRcIlxuICAgICAgICBib3JkZXJDb2xvcj1cInBsYW5Nb2RlXCJcbiAgICAgICAgYm9yZGVyTGVmdD17ZmFsc2V9XG4gICAgICAgIGJvcmRlclJpZ2h0PXtmYWxzZX1cbiAgICAgICAgYm9yZGVyQm90dG9tPXtmYWxzZX1cbiAgICAgICAgcGFkZGluZ1g9ezF9XG4gICAgICA+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPldvdWxkIHlvdSBsaWtlIHRvIHByb2NlZWQ/PC9UZXh0PlxuICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICAgIG9uQ2hhbmdlPXt2ID0+IHZvaWQgaGFuZGxlUmVzcG9uc2VSZWYuY3VycmVudCh2KX1cbiAgICAgICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBoYW5kbGVDYW5jZWxSZWYuY3VycmVudD8uKCl9XG4gICAgICAgICAgICBvbkltYWdlUGFzdGU9e29uSW1hZ2VQYXN0ZX1cbiAgICAgICAgICAgIHBhc3RlZENvbnRlbnRzPXtwYXN0ZWRDb250ZW50c31cbiAgICAgICAgICAgIG9uUmVtb3ZlSW1hZ2U9e29uUmVtb3ZlSW1hZ2V9XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHtlZGl0b3JOYW1lICYmIChcbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5jdHJsLWcgdG8gZWRpdCBpbiA8L1RleHQ+XG4gICAgICAgICAgICA8VGV4dCBib2xkIGRpbUNvbG9yPlxuICAgICAgICAgICAgICB7ZWRpdG9yTmFtZX1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIHtpc1YyICYmIHBsYW5GaWxlUGF0aCAmJiAoXG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiDCtyB7Z2V0RGlzcGxheVBhdGgocGxhbkZpbGVQYXRoKX08L1RleHQ+XG4gICAgICAgICAgICApfVxuICAgICAgICAgICAge3Nob3dTYXZlTWVzc2FnZSAmJiAoXG4gICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+eycgwrcgJ308L1RleHQ+XG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+e2ZpZ3VyZXMudGlja31QbGFuIHNhdmVkITwvVGV4dD5cbiAgICAgICAgICAgICAgPC8+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApfVxuICAgICAgPC9Cb3g+LFxuICAgIClcbiAgICByZXR1cm4gKCkgPT4gc2V0U3RpY2t5Rm9vdGVyKG51bGwpXG4gICAgLy8gb25JbWFnZVBhc3RlL29uUmVtb3ZlSW1hZ2UgYXJlIHN0YWJsZSAodXNlQ2FsbGJhY2svdXNlUmVmLWJhY2tlZCBhYm92ZSlcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcmVhY3QtaG9va3MvZXhoYXVzdGl2ZS1kZXBzXG4gIH0sIFtcbiAgICB1c2VTdGlja3lGb290ZXIsXG4gICAgc2V0U3RpY2t5Rm9vdGVyLFxuICAgIG9wdGlvbnMsXG4gICAgcGFzdGVkQ29udGVudHMsXG4gICAgZWRpdG9yTmFtZSxcbiAgICBpc1YyLFxuICAgIHBsYW5GaWxlUGF0aCxcbiAgICBzaG93U2F2ZU1lc3NhZ2UsXG4gIF0pXG5cbiAgLy8gU2ltcGxpZmllZCBVSSBmb3IgZW1wdHkgcGxhbnNcbiAgaWYgKGlzRW1wdHkpIHtcbiAgICBmdW5jdGlvbiBoYW5kbGVFbXB0eVBsYW5SZXNwb25zZSh2YWx1ZTogJ3llcycgfCAnbm8nKTogdm9pZCB7XG4gICAgICBpZiAodmFsdWUgPT09ICd5ZXMnKSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9wbGFuX2V4aXQnLCB7XG4gICAgICAgICAgcGxhbkxlbmd0aENoYXJzOiAwLFxuICAgICAgICAgIG91dGNvbWU6XG4gICAgICAgICAgICAneWVzLWRlZmF1bHQnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgaW50ZXJ2aWV3UGhhc2VFbmFibGVkOiBpc1BsYW5Nb2RlSW50ZXJ2aWV3UGhhc2VFbmFibGVkKCksXG4gICAgICAgICAgcGxhblN0cnVjdHVyZVZhcmlhbnQsXG4gICAgICAgIH0pXG4gICAgICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgICAgICAgIGNvbnN0IGF1dG9XYXNVc2VkRHVyaW5nUGxhbiA9XG4gICAgICAgICAgICBhdXRvTW9kZVN0YXRlTW9kdWxlPy5pc0F1dG9Nb2RlQWN0aXZlKCkgPz8gZmFsc2VcbiAgICAgICAgICBpZiAoYXV0b1dhc1VzZWREdXJpbmdQbGFuKSB7XG4gICAgICAgICAgICBhdXRvTW9kZVN0YXRlTW9kdWxlPy5zZXRBdXRvTW9kZUFjdGl2ZShmYWxzZSlcbiAgICAgICAgICAgIHNldE5lZWRzQXV0b01vZGVFeGl0QXR0YWNobWVudCh0cnVlKVxuICAgICAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiAoe1xuICAgICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQ6IHtcbiAgICAgICAgICAgICAgICAuLi5yZXN0b3JlRGFuZ2Vyb3VzUGVybWlzc2lvbnMocHJldi50b29sUGVybWlzc2lvbkNvbnRleHQpLFxuICAgICAgICAgICAgICAgIHByZVBsYW5Nb2RlOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB9KSlcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgc2V0SGFzRXhpdGVkUGxhbk1vZGUodHJ1ZSlcbiAgICAgICAgc2V0TmVlZHNQbGFuTW9kZUV4aXRBdHRhY2htZW50KHRydWUpXG4gICAgICAgIG9uRG9uZSgpXG4gICAgICAgIHRvb2xVc2VDb25maXJtLm9uQWxsb3coe30sIFtcbiAgICAgICAgICB7IHR5cGU6ICdzZXRNb2RlJywgbW9kZTogJ2RlZmF1bHQnLCBkZXN0aW5hdGlvbjogJ3Nlc3Npb24nIH0sXG4gICAgICAgIF0pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsb2dFdmVudCgndGVuZ3VfcGxhbl9leGl0Jywge1xuICAgICAgICAgIHBsYW5MZW5ndGhDaGFyczogMCxcbiAgICAgICAgICBvdXRjb21lOlxuICAgICAgICAgICAgJ25vJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIGludGVydmlld1BoYXNlRW5hYmxlZDogaXNQbGFuTW9kZUludGVydmlld1BoYXNlRW5hYmxlZCgpLFxuICAgICAgICAgIHBsYW5TdHJ1Y3R1cmVWYXJpYW50LFxuICAgICAgICB9KVxuICAgICAgICBvbkRvbmUoKVxuICAgICAgICBvblJlamVjdCgpXG4gICAgICAgIHRvb2xVc2VDb25maXJtLm9uUmVqZWN0KClcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gKFxuICAgICAgPFBlcm1pc3Npb25EaWFsb2dcbiAgICAgICAgY29sb3I9XCJwbGFuTW9kZVwiXG4gICAgICAgIHRpdGxlPVwiRXhpdCBwbGFuIG1vZGU/XCJcbiAgICAgICAgd29ya2VyQmFkZ2U9e3dvcmtlckJhZGdlfVxuICAgICAgPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nWD17MX0gbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dD5DbGF1ZGUgd2FudHMgdG8gZXhpdCBwbGFuIG1vZGU8L1RleHQ+XG4gICAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgICBvcHRpb25zPXtbXG4gICAgICAgICAgICAgICAgeyBsYWJlbDogJ1llcycsIHZhbHVlOiAneWVzJyBhcyBjb25zdCB9LFxuICAgICAgICAgICAgICAgIHsgbGFiZWw6ICdObycsIHZhbHVlOiAnbm8nIGFzIGNvbnN0IH0sXG4gICAgICAgICAgICAgIF19XG4gICAgICAgICAgICAgIG9uQ2hhbmdlPXtoYW5kbGVFbXB0eVBsYW5SZXNwb25zZX1cbiAgICAgICAgICAgICAgb25DYW5jZWw9eygpID0+IHtcbiAgICAgICAgICAgICAgICBsb2dFdmVudCgndGVuZ3VfcGxhbl9leGl0Jywge1xuICAgICAgICAgICAgICAgICAgcGxhbkxlbmd0aENoYXJzOiAwLFxuICAgICAgICAgICAgICAgICAgb3V0Y29tZTpcbiAgICAgICAgICAgICAgICAgICAgJ25vJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgICAgICAgaW50ZXJ2aWV3UGhhc2VFbmFibGVkOiBpc1BsYW5Nb2RlSW50ZXJ2aWV3UGhhc2VFbmFibGVkKCksXG4gICAgICAgICAgICAgICAgICBwbGFuU3RydWN0dXJlVmFyaWFudCxcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIG9uRG9uZSgpXG4gICAgICAgICAgICAgICAgb25SZWplY3QoKVxuICAgICAgICAgICAgICAgIHRvb2xVc2VDb25maXJtLm9uUmVqZWN0KClcbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9QZXJtaXNzaW9uRGlhbG9nPlxuICAgIClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveFxuICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICB0YWJJbmRleD17MH1cbiAgICAgIGF1dG9Gb2N1c1xuICAgICAgb25LZXlEb3duPXtoYW5kbGVLZXlEb3dufVxuICAgID5cbiAgICAgIDxQZXJtaXNzaW9uRGlhbG9nXG4gICAgICAgIGNvbG9yPVwicGxhbk1vZGVcIlxuICAgICAgICB0aXRsZT1cIlJlYWR5IHRvIGNvZGU/XCJcbiAgICAgICAgaW5uZXJQYWRkaW5nWD17MH1cbiAgICAgICAgd29ya2VyQmFkZ2U9e3dvcmtlckJhZGdlfVxuICAgICAgPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxCb3ggcGFkZGluZ1g9ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIDxUZXh0PkhlcmUgaXMgQ2xhdWRlJmFwb3M7cyBwbGFuOjwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8Qm94XG4gICAgICAgICAgICBib3JkZXJDb2xvcj1cInN1YnRsZVwiXG4gICAgICAgICAgICBib3JkZXJTdHlsZT1cImRhc2hlZFwiXG4gICAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICAgIGJvcmRlckxlZnQ9e2ZhbHNlfVxuICAgICAgICAgICAgYm9yZGVyUmlnaHQ9e2ZhbHNlfVxuICAgICAgICAgICAgcGFkZGluZ1g9ezF9XG4gICAgICAgICAgICBtYXJnaW5Cb3R0b209ezF9XG4gICAgICAgICAgICAvLyBOZWNlc3NhcnkgZm9yIFdpbmRvd3MgVGVybWluYWwgdG8gcmVuZGVyIHByb3Blcmx5XG4gICAgICAgICAgICBvdmVyZmxvdz1cImhpZGRlblwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAgPE1hcmtkb3duPntjdXJyZW50UGxhbn08L01hcmtkb3duPlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdYPXsxfT5cbiAgICAgICAgICAgIDxQZXJtaXNzaW9uUnVsZUV4cGxhbmF0aW9uXG4gICAgICAgICAgICAgIHBlcm1pc3Npb25SZXN1bHQ9e3Rvb2xVc2VDb25maXJtLnBlcm1pc3Npb25SZXN1bHR9XG4gICAgICAgICAgICAgIHRvb2xUeXBlPVwidG9vbFwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAge2lzQ2xhc3NpZmllclBlcm1pc3Npb25zRW5hYmxlZCgpICYmXG4gICAgICAgICAgICAgIGFsbG93ZWRQcm9tcHRzICYmXG4gICAgICAgICAgICAgIGFsbG93ZWRQcm9tcHRzLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgICAgICAgICAgICA8VGV4dCBib2xkPlJlcXVlc3RlZCBwZXJtaXNzaW9uczo8L1RleHQ+XG4gICAgICAgICAgICAgICAgICB7YWxsb3dlZFByb21wdHMubWFwKChwLCBpKSA9PiAoXG4gICAgICAgICAgICAgICAgICAgIDxUZXh0IGtleT17aX0gZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICAgICAgeycgICd9wrcge3AudG9vbH0oe1BST01QVF9QUkVGSVh9IHtwLnByb21wdH0pXG4gICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICkpfVxuICAgICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgeyF1c2VTdGlja3lGb290ZXIgJiYgKFxuICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgQ2xhdWRlIGhhcyB3cml0dGVuIHVwIGEgcGxhbiBhbmQgaXMgcmVhZHkgdG8gZXhlY3V0ZS4gV291bGRcbiAgICAgICAgICAgICAgICAgIHlvdSBsaWtlIHRvIHByb2NlZWQ/XG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICAgICAgICAgIDxTZWxlY3RcbiAgICAgICAgICAgICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICAgICAgICAgICAgb25DaGFuZ2U9e2hhbmRsZVJlc3BvbnNlfVxuICAgICAgICAgICAgICAgICAgICBvbkNhbmNlbD17KCkgPT4gaGFuZGxlQ2FuY2VsUmVmLmN1cnJlbnQ/LigpfVxuICAgICAgICAgICAgICAgICAgICBvbkltYWdlUGFzdGU9e29uSW1hZ2VQYXN0ZX1cbiAgICAgICAgICAgICAgICAgICAgcGFzdGVkQ29udGVudHM9e3Bhc3RlZENvbnRlbnRzfVxuICAgICAgICAgICAgICAgICAgICBvblJlbW92ZUltYWdlPXtvblJlbW92ZUltYWdlfVxuICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgPC8+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvUGVybWlzc2lvbkRpYWxvZz5cbiAgICAgIHshdXNlU3RpY2t5Rm9vdGVyICYmIGVkaXRvck5hbWUgJiYgKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9IHBhZGRpbmdYPXsxfSBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxCb3g+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5jdHJsLWcgdG8gZWRpdCBpbiA8L1RleHQ+XG4gICAgICAgICAgICA8VGV4dCBib2xkIGRpbUNvbG9yPlxuICAgICAgICAgICAgICB7ZWRpdG9yTmFtZX1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIHtpc1YyICYmIHBsYW5GaWxlUGF0aCAmJiAoXG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiDCtyB7Z2V0RGlzcGxheVBhdGgocGxhbkZpbGVQYXRoKX08L1RleHQ+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICAgIHtzaG93U2F2ZU1lc3NhZ2UgJiYgKFxuICAgICAgICAgICAgPEJveD5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+eycgwrcgJ308L1RleHQ+XG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VjY2Vzc1wiPntmaWd1cmVzLnRpY2t9UGxhbiBzYXZlZCE8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuLyoqIEBpbnRlcm5hbCBFeHBvcnRlZCBmb3IgdGVzdGluZy4gKi9cbmV4cG9ydCBmdW5jdGlvbiBidWlsZFBsYW5BcHByb3ZhbE9wdGlvbnMoe1xuICBzaG93Q2xlYXJDb250ZXh0LFxuICBzaG93VWx0cmFwbGFuLFxuICB1c2VkUGVyY2VudCxcbiAgaXNBdXRvTW9kZUF2YWlsYWJsZSxcbiAgaXNCeXBhc3NQZXJtaXNzaW9uc01vZGVBdmFpbGFibGUsXG4gIG9uRmVlZGJhY2tDaGFuZ2UsXG59OiB7XG4gIHNob3dDbGVhckNvbnRleHQ6IGJvb2xlYW5cbiAgc2hvd1VsdHJhcGxhbjogYm9vbGVhblxuICB1c2VkUGVyY2VudDogbnVtYmVyIHwgbnVsbFxuICBpc0F1dG9Nb2RlQXZhaWxhYmxlOiBib29sZWFuIHwgdW5kZWZpbmVkXG4gIGlzQnlwYXNzUGVybWlzc2lvbnNNb2RlQXZhaWxhYmxlOiBib29sZWFuIHwgdW5kZWZpbmVkXG4gIG9uRmVlZGJhY2tDaGFuZ2U6ICh2OiBzdHJpbmcpID0+IHZvaWRcbn0pOiBPcHRpb25XaXRoRGVzY3JpcHRpb248UmVzcG9uc2VWYWx1ZT5bXSB7XG4gIGNvbnN0IG9wdGlvbnM6IE9wdGlvbldpdGhEZXNjcmlwdGlvbjxSZXNwb25zZVZhbHVlPltdID0gW11cbiAgY29uc3QgdXNlZExhYmVsID0gdXNlZFBlcmNlbnQgIT09IG51bGwgPyBgICgke3VzZWRQZXJjZW50fSUgdXNlZClgIDogJydcblxuICBpZiAoc2hvd0NsZWFyQ29udGV4dCkge1xuICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSAmJiBpc0F1dG9Nb2RlQXZhaWxhYmxlKSB7XG4gICAgICBvcHRpb25zLnB1c2goe1xuICAgICAgICBsYWJlbDogYFllcywgY2xlYXIgY29udGV4dCR7dXNlZExhYmVsfSBhbmQgdXNlIGF1dG8gbW9kZWAsXG4gICAgICAgIHZhbHVlOiAneWVzLWF1dG8tY2xlYXItY29udGV4dCcsXG4gICAgICB9KVxuICAgIH0gZWxzZSBpZiAoaXNCeXBhc3NQZXJtaXNzaW9uc01vZGVBdmFpbGFibGUpIHtcbiAgICAgIG9wdGlvbnMucHVzaCh7XG4gICAgICAgIGxhYmVsOiBgWWVzLCBjbGVhciBjb250ZXh0JHt1c2VkTGFiZWx9IGFuZCBieXBhc3MgcGVybWlzc2lvbnNgLFxuICAgICAgICB2YWx1ZTogJ3llcy1ieXBhc3MtcGVybWlzc2lvbnMnLFxuICAgICAgfSlcbiAgICB9IGVsc2Uge1xuICAgICAgb3B0aW9ucy5wdXNoKHtcbiAgICAgICAgbGFiZWw6IGBZZXMsIGNsZWFyIGNvbnRleHQke3VzZWRMYWJlbH0gYW5kIGF1dG8tYWNjZXB0IGVkaXRzYCxcbiAgICAgICAgdmFsdWU6ICd5ZXMtYWNjZXB0LWVkaXRzJyxcbiAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgLy8gU2xvdCAyOiBrZWVwLWNvbnRleHQgd2l0aCBlbGV2YXRlZCBtb2RlIChzYW1lIHByaW9yaXR5OiBhdXRvID4gYnlwYXNzID4gZWRpdHMpLlxuICBpZiAoZmVhdHVyZSgnVFJBTlNDUklQVF9DTEFTU0lGSUVSJykgJiYgaXNBdXRvTW9kZUF2YWlsYWJsZSkge1xuICAgIG9wdGlvbnMucHVzaCh7XG4gICAgICBsYWJlbDogJ1llcywgYW5kIHVzZSBhdXRvIG1vZGUnLFxuICAgICAgdmFsdWU6ICd5ZXMtcmVzdW1lLWF1dG8tbW9kZScsXG4gICAgfSlcbiAgfSBlbHNlIGlmIChpc0J5cGFzc1Blcm1pc3Npb25zTW9kZUF2YWlsYWJsZSkge1xuICAgIG9wdGlvbnMucHVzaCh7XG4gICAgICBsYWJlbDogJ1llcywgYW5kIGJ5cGFzcyBwZXJtaXNzaW9ucycsXG4gICAgICB2YWx1ZTogJ3llcy1hY2NlcHQtZWRpdHMta2VlcC1jb250ZXh0JyxcbiAgICB9KVxuICB9IGVsc2Uge1xuICAgIG9wdGlvbnMucHVzaCh7XG4gICAgICBsYWJlbDogJ1llcywgYXV0by1hY2NlcHQgZWRpdHMnLFxuICAgICAgdmFsdWU6ICd5ZXMtYWNjZXB0LWVkaXRzLWtlZXAtY29udGV4dCcsXG4gICAgfSlcbiAgfVxuXG4gIG9wdGlvbnMucHVzaCh7XG4gICAgbGFiZWw6ICdZZXMsIG1hbnVhbGx5IGFwcHJvdmUgZWRpdHMnLFxuICAgIHZhbHVlOiAneWVzLWRlZmF1bHQta2VlcC1jb250ZXh0JyxcbiAgfSlcblxuICBpZiAoc2hvd1VsdHJhcGxhbikge1xuICAgIG9wdGlvbnMucHVzaCh7XG4gICAgICBsYWJlbDogJ05vLCByZWZpbmUgd2l0aCBVbHRyYXBsYW4gb24gQ2xhdWRlIENvZGUgb24gdGhlIHdlYicsXG4gICAgICB2YWx1ZTogJ3VsdHJhcGxhbicsXG4gICAgfSlcbiAgfVxuXG4gIG9wdGlvbnMucHVzaCh7XG4gICAgdHlwZTogJ2lucHV0JyxcbiAgICBsYWJlbDogJ05vLCBrZWVwIHBsYW5uaW5nJyxcbiAgICB2YWx1ZTogJ25vJyxcbiAgICBwbGFjZWhvbGRlcjogJ1RlbGwgQ2xhdWRlIHdoYXQgdG8gY2hhbmdlJyxcbiAgICBkZXNjcmlwdGlvbjogJ3NoaWZ0K3RhYiB0byBhcHByb3ZlIHdpdGggdGhpcyBmZWVkYmFjaycsXG4gICAgb25DaGFuZ2U6IG9uRmVlZGJhY2tDaGFuZ2UsXG4gIH0pXG5cbiAgcmV0dXJuIG9wdGlvbnNcbn1cblxuZnVuY3Rpb24gZ2V0Q29udGV4dFVzZWRQZXJjZW50KFxuICB1c2FnZTpcbiAgICB8IHtcbiAgICAgICAgaW5wdXRfdG9rZW5zOiBudW1iZXJcbiAgICAgICAgY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zPzogbnVtYmVyIHwgbnVsbFxuICAgICAgICBjYWNoZV9yZWFkX2lucHV0X3Rva2Vucz86IG51bWJlciB8IG51bGxcbiAgICAgIH1cbiAgICB8IHVuZGVmaW5lZCxcbiAgcGVybWlzc2lvbk1vZGU6IFBlcm1pc3Npb25Nb2RlLFxuKTogbnVtYmVyIHwgbnVsbCB7XG4gIGlmICghdXNhZ2UpIHJldHVybiBudWxsXG4gIGNvbnN0IHJ1bnRpbWVNb2RlbCA9IGdldFJ1bnRpbWVNYWluTG9vcE1vZGVsKHtcbiAgICBwZXJtaXNzaW9uTW9kZSxcbiAgICBtYWluTG9vcE1vZGVsOiBnZXRNYWluTG9vcE1vZGVsKCksXG4gICAgZXhjZWVkczIwMGtUb2tlbnM6IGZhbHNlLFxuICB9KVxuICBjb25zdCBjb250ZXh0V2luZG93U2l6ZSA9IGdldENvbnRleHRXaW5kb3dGb3JNb2RlbChcbiAgICBydW50aW1lTW9kZWwsXG4gICAgZ2V0U2RrQmV0YXMoKSxcbiAgKVxuICBjb25zdCB7IHVzZWQgfSA9IGNhbGN1bGF0ZUNvbnRleHRQZXJjZW50YWdlcyhcbiAgICB7XG4gICAgICBpbnB1dF90b2tlbnM6IHVzYWdlLmlucHV0X3Rva2VucyxcbiAgICAgIGNhY2hlX2NyZWF0aW9uX2lucHV0X3Rva2VuczogdXNhZ2UuY2FjaGVfY3JlYXRpb25faW5wdXRfdG9rZW5zID8/IDAsXG4gICAgICBjYWNoZV9yZWFkX2lucHV0X3Rva2VuczogdXNhZ2UuY2FjaGVfcmVhZF9pbnB1dF90b2tlbnMgPz8gMCxcbiAgICB9LFxuICAgIGNvbnRleHRXaW5kb3dTaXplLFxuICApXG4gIHJldHVybiB1c2VkXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLE9BQU8sUUFBUSxZQUFZO0FBQ3BDLGNBQWNDLElBQUksUUFBUSxRQUFRO0FBQ2xDLE9BQU9DLE9BQU8sTUFBTSxTQUFTO0FBQzdCLE9BQU9DLEtBQUssSUFDVkMsV0FBVyxFQUNYQyxTQUFTLEVBQ1RDLGVBQWUsRUFDZkMsT0FBTyxFQUNQQyxNQUFNLEVBQ05DLFFBQVEsUUFDSCxPQUFPO0FBQ2QsU0FBU0MsZ0JBQWdCLFFBQVEsOEJBQThCO0FBQy9ELFNBQ0UsS0FBS0MsMERBQTBELEVBQy9EQyxRQUFRLFFBQ0gsaUNBQWlDO0FBQ3hDLFNBQ0VDLFdBQVcsRUFDWEMsZ0JBQWdCLEVBQ2hCQyxjQUFjLFFBQ1QsdUJBQXVCO0FBQzlCLFNBQ0VDLFdBQVcsRUFDWEMsWUFBWSxFQUNaQyw0QkFBNEIsRUFDNUJDLG9CQUFvQixFQUNwQkMsOEJBQThCLEVBQzlCQyw4QkFBOEIsUUFDekIsNkJBQTZCO0FBQ3BDLFNBQVNDLG1CQUFtQixRQUFRLGlEQUFpRDtBQUNyRixTQUFTQyxlQUFlLFFBQVEsZ0NBQWdDO0FBQ2hFLGNBQWNDLGFBQWEsUUFBUSx1Q0FBdUM7QUFDMUUsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsaUJBQWlCO0FBQzNDLGNBQWNDLFFBQVEsUUFBUSxpQ0FBaUM7QUFDL0QsU0FBU0MsZUFBZSxRQUFRLHVDQUF1QztBQUN2RSxTQUFTQywyQkFBMkIsUUFBUSw4Q0FBOEM7QUFDMUYsY0FBY0MsYUFBYSxRQUFRLHVEQUF1RDtBQUMxRixTQUFTQyxxQkFBcUIsUUFBUSw0Q0FBNEM7QUFDbEYsU0FBU0Msb0JBQW9CLFFBQVEsc0NBQXNDO0FBQzNFLFNBQ0VDLDJCQUEyQixFQUMzQkMsd0JBQXdCLFFBQ25CLDJCQUEyQjtBQUNsQyxTQUFTQyxpQkFBaUIsUUFBUSwwQkFBMEI7QUFDNUQsU0FBU0MsY0FBYyxRQUFRLHdCQUF3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 \ No newline at end of file diff --git a/components/permissions/FallbackPermissionRequest.tsx b/components/permissions/FallbackPermissionRequest.tsx new file mode 100644 index 0000000..2e88978 --- /dev/null +++ b/components/permissions/FallbackPermissionRequest.tsx @@ -0,0 +1,333 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useMemo } from 'react'; +import { getOriginalCwd } from '../../bootstrap/state.js'; +import { Box, Text, useTheme } from '../../ink.js'; +import { sanitizeToolNameForAnalytics } from '../../services/analytics/metadata.js'; +import { env } from '../../utils/env.js'; +import { shouldShowAlwaysAllowOptions } from '../../utils/permissions/permissionsLoader.js'; +import { truncateToLines } from '../../utils/stringUtils.js'; +import { logUnaryEvent } from '../../utils/unaryLogging.js'; +import { type UnaryEvent, usePermissionRequestLogging } from './hooks.js'; +import { PermissionDialog } from './PermissionDialog.js'; +import { PermissionPrompt, type PermissionPromptOption, type ToolAnalyticsContext } from './PermissionPrompt.js'; +import type { PermissionRequestProps } from './PermissionRequest.js'; +import { PermissionRuleExplanation } from './PermissionRuleExplanation.js'; +type FallbackOptionValue = 'yes' | 'yes-dont-ask-again' | 'no'; +export function FallbackPermissionRequest(t0) { + const $ = _c(58); + const { + toolUseConfirm, + onDone, + onReject, + workerBadge + } = t0; + const [theme] = useTheme(); + let originalUserFacingName; + let t1; + if ($[0] !== toolUseConfirm.input || $[1] !== toolUseConfirm.tool) { + originalUserFacingName = toolUseConfirm.tool.userFacingName(toolUseConfirm.input as never); + t1 = originalUserFacingName.endsWith(" (MCP)") ? originalUserFacingName.slice(0, -6) : originalUserFacingName; + $[0] = toolUseConfirm.input; + $[1] = toolUseConfirm.tool; + $[2] = originalUserFacingName; + $[3] = t1; + } else { + originalUserFacingName = $[2]; + t1 = $[3]; + } + const userFacingName = t1; + let t2; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t2 = { + completion_type: "tool_use_single", + language_name: "none" + }; + $[4] = t2; + } else { + t2 = $[4]; + } + const unaryEvent = t2; + usePermissionRequestLogging(toolUseConfirm, unaryEvent); + let t3; + if ($[5] !== onDone || $[6] !== onReject || $[7] !== toolUseConfirm) { + t3 = (value, feedback) => { + bb8: switch (value) { + case "yes": + { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "accept", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + toolUseConfirm.onAllow(toolUseConfirm.input, [], feedback); + onDone(); + break bb8; + } + case "yes-dont-ask-again": + { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "accept", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + toolUseConfirm.onAllow(toolUseConfirm.input, [{ + type: "addRules", + rules: [{ + toolName: toolUseConfirm.tool.name + }], + behavior: "allow", + destination: "localSettings" + }]); + onDone(); + break bb8; + } + case "no": + { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "reject", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + toolUseConfirm.onReject(feedback); + onReject(); + onDone(); + } + } + }; + $[5] = onDone; + $[6] = onReject; + $[7] = toolUseConfirm; + $[8] = t3; + } else { + t3 = $[8]; + } + const handleSelect = t3; + let t4; + if ($[9] !== onDone || $[10] !== onReject || $[11] !== toolUseConfirm) { + t4 = () => { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "reject", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + toolUseConfirm.onReject(); + onReject(); + onDone(); + }; + $[9] = onDone; + $[10] = onReject; + $[11] = toolUseConfirm; + $[12] = t4; + } else { + t4 = $[12]; + } + const handleCancel = t4; + let t5; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t5 = getOriginalCwd(); + $[13] = t5; + } else { + t5 = $[13]; + } + const originalCwd = t5; + let t6; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t6 = shouldShowAlwaysAllowOptions(); + $[14] = t6; + } else { + t6 = $[14]; + } + const showAlwaysAllowOptions = t6; + let t7; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t7 = { + label: "Yes", + value: "yes", + feedbackConfig: { + type: "accept" + } + }; + $[15] = t7; + } else { + t7 = $[15]; + } + let result; + if ($[16] !== userFacingName) { + result = [t7]; + if (showAlwaysAllowOptions) { + const t8 = {userFacingName}; + let t9; + if ($[18] === Symbol.for("react.memo_cache_sentinel")) { + t9 = {originalCwd}; + $[18] = t9; + } else { + t9 = $[18]; + } + let t10; + if ($[19] !== t8) { + t10 = { + label: Yes, and don't ask again for {t8}{" "}commands in {t9}, + value: "yes-dont-ask-again" + }; + $[19] = t8; + $[20] = t10; + } else { + t10 = $[20]; + } + result.push(t10); + } + let t8; + if ($[21] === Symbol.for("react.memo_cache_sentinel")) { + t8 = { + label: "No", + value: "no", + feedbackConfig: { + type: "reject" + } + }; + $[21] = t8; + } else { + t8 = $[21]; + } + result.push(t8); + $[16] = userFacingName; + $[17] = result; + } else { + result = $[17]; + } + const options = result; + let t8; + if ($[22] !== toolUseConfirm.tool.name) { + t8 = sanitizeToolNameForAnalytics(toolUseConfirm.tool.name); + $[22] = toolUseConfirm.tool.name; + $[23] = t8; + } else { + t8 = $[23]; + } + const t9 = toolUseConfirm.tool.isMcp ?? false; + let t10; + if ($[24] !== t8 || $[25] !== t9) { + t10 = { + toolName: t8, + isMcp: t9 + }; + $[24] = t8; + $[25] = t9; + $[26] = t10; + } else { + t10 = $[26]; + } + const toolAnalyticsContext = t10; + let t11; + if ($[27] !== theme || $[28] !== toolUseConfirm.input || $[29] !== toolUseConfirm.tool) { + t11 = toolUseConfirm.tool.renderToolUseMessage(toolUseConfirm.input as never, { + theme, + verbose: true + }); + $[27] = theme; + $[28] = toolUseConfirm.input; + $[29] = toolUseConfirm.tool; + $[30] = t11; + } else { + t11 = $[30]; + } + let t12; + if ($[31] !== originalUserFacingName) { + t12 = originalUserFacingName.endsWith(" (MCP)") ? (MCP) : ""; + $[31] = originalUserFacingName; + $[32] = t12; + } else { + t12 = $[32]; + } + let t13; + if ($[33] !== t11 || $[34] !== t12 || $[35] !== userFacingName) { + t13 = {userFacingName}({t11}){t12}; + $[33] = t11; + $[34] = t12; + $[35] = userFacingName; + $[36] = t13; + } else { + t13 = $[36]; + } + let t14; + if ($[37] !== toolUseConfirm.description) { + t14 = truncateToLines(toolUseConfirm.description, 3); + $[37] = toolUseConfirm.description; + $[38] = t14; + } else { + t14 = $[38]; + } + let t15; + if ($[39] !== t14) { + t15 = {t14}; + $[39] = t14; + $[40] = t15; + } else { + t15 = $[40]; + } + let t16; + if ($[41] !== t13 || $[42] !== t15) { + t16 = {t13}{t15}; + $[41] = t13; + $[42] = t15; + $[43] = t16; + } else { + t16 = $[43]; + } + let t17; + if ($[44] !== toolUseConfirm.permissionResult) { + t17 = ; + $[44] = toolUseConfirm.permissionResult; + $[45] = t17; + } else { + t17 = $[45]; + } + let t18; + if ($[46] !== handleCancel || $[47] !== handleSelect || $[48] !== options || $[49] !== toolAnalyticsContext) { + t18 = ; + $[46] = handleCancel; + $[47] = handleSelect; + $[48] = options; + $[49] = toolAnalyticsContext; + $[50] = t18; + } else { + t18 = $[50]; + } + let t19; + if ($[51] !== t17 || $[52] !== t18) { + t19 = {t17}{t18}; + $[51] = t17; + $[52] = t18; + $[53] = t19; + } else { + t19 = $[53]; + } + let t20; + if ($[54] !== t16 || $[55] !== t19 || $[56] !== workerBadge) { + t20 = {t16}{t19}; + $[54] = t16; + $[55] = t19; + $[56] = workerBadge; + $[57] = t20; + } else { + t20 = $[57]; + } + return t20; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlTWVtbyIsImdldE9yaWdpbmFsQ3dkIiwiQm94IiwiVGV4dCIsInVzZVRoZW1lIiwic2FuaXRpemVUb29sTmFtZUZvckFuYWx5dGljcyIsImVudiIsInNob3VsZFNob3dBbHdheXNBbGxvd09wdGlvbnMiLCJ0cnVuY2F0ZVRvTGluZXMiLCJsb2dVbmFyeUV2ZW50IiwiVW5hcnlFdmVudCIsInVzZVBlcm1pc3Npb25SZXF1ZXN0TG9nZ2luZyIsIlBlcm1pc3Npb25EaWFsb2ciLCJQZXJtaXNzaW9uUHJvbXB0IiwiUGVybWlzc2lvblByb21wdE9wdGlvbiIsIlRvb2xBbmFseXRpY3NDb250ZXh0IiwiUGVybWlzc2lvblJlcXVlc3RQcm9wcyIsIlBlcm1pc3Npb25SdWxlRXhwbGFuYXRpb24iLCJGYWxsYmFja09wdGlvblZhbHVlIiwiRmFsbGJhY2tQZXJtaXNzaW9uUmVxdWVzdCIsInQwIiwiJCIsIl9jIiwidG9vbFVzZUNvbmZpcm0iLCJvbkRvbmUiLCJvblJlamVjdCIsIndvcmtlckJhZGdlIiwidGhlbWUiLCJvcmlnaW5hbFVzZXJGYWNpbmdOYW1lIiwidDEiLCJpbnB1dCIsInRvb2wiLCJ1c2VyRmFjaW5nTmFtZSIsImVuZHNXaXRoIiwic2xpY2UiLCJ0MiIsIlN5bWJvbCIsImZvciIsImNvbXBsZXRpb25fdHlwZSIsImxhbmd1YWdlX25hbWUiLCJ1bmFyeUV2ZW50IiwidDMiLCJ2YWx1ZSIsImZlZWRiYWNrIiwiYmI4IiwiZXZlbnQiLCJtZXRhZGF0YSIsIm1lc3NhZ2VfaWQiLCJhc3Npc3RhbnRNZXNzYWdlIiwibWVzc2FnZSIsImlkIiwicGxhdGZvcm0iLCJvbkFsbG93IiwidHlwZSIsInJ1bGVzIiwidG9vbE5hbWUiLCJuYW1lIiwiYmVoYXZpb3IiLCJkZXN0aW5hdGlvbiIsImhhbmRsZVNlbGVjdCIsInQ0IiwiaGFuZGxlQ2FuY2VsIiwidDUiLCJvcmlnaW5hbEN3ZCIsInQ2Iiwic2hvd0Fsd2F5c0FsbG93T3B0aW9ucyIsInQ3IiwibGFiZWwiLCJmZWVkYmFja0NvbmZpZyIsInJlc3VsdCIsInQ4IiwidDkiLCJ0MTAiLCJwdXNoIiwib3B0aW9ucyIsImlzTWNwIiwidG9vbEFuYWx5dGljc0NvbnRleHQiLCJ0MTEiLCJyZW5kZXJUb29sVXNlTWVzc2FnZSIsInZlcmJvc2UiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJkZXNjcmlwdGlvbiIsInQxNSIsInQxNiIsInQxNyIsInBlcm1pc3Npb25SZXN1bHQiLCJ0MTgiLCJ0MTkiLCJ0MjAiXSwic291cmNlcyI6WyJGYWxsYmFja1Blcm1pc3Npb25SZXF1ZXN0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlQ2FsbGJhY2ssIHVzZU1lbW8gfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGdldE9yaWdpbmFsQ3dkIH0gZnJvbSAnLi4vLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0LCB1c2VUaGVtZSB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHNhbml0aXplVG9vbE5hbWVGb3JBbmFseXRpY3MgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hbmFseXRpY3MvbWV0YWRhdGEuanMnXG5pbXBvcnQgeyBlbnYgfSBmcm9tICcuLi8uLi91dGlscy9lbnYuanMnXG5pbXBvcnQgeyBzaG91bGRTaG93QWx3YXlzQWxsb3dPcHRpb25zIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvcGVybWlzc2lvbnNMb2FkZXIuanMnXG5pbXBvcnQgeyB0cnVuY2F0ZVRvTGluZXMgfSBmcm9tICcuLi8uLi91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB7IGxvZ1VuYXJ5RXZlbnQgfSBmcm9tICcuLi8uLi91dGlscy91bmFyeUxvZ2dpbmcuanMnXG5pbXBvcnQgeyB0eXBlIFVuYXJ5RXZlbnQsIHVzZVBlcm1pc3Npb25SZXF1ZXN0TG9nZ2luZyB9IGZyb20gJy4vaG9va3MuanMnXG5pbXBvcnQgeyBQZXJtaXNzaW9uRGlhbG9nIH0gZnJvbSAnLi9QZXJtaXNzaW9uRGlhbG9nLmpzJ1xuaW1wb3J0IHtcbiAgUGVybWlzc2lvblByb21wdCxcbiAgdHlwZSBQZXJtaXNzaW9uUHJvbXB0T3B0aW9uLFxuICB0eXBlIFRvb2xBbmFseXRpY3NDb250ZXh0LFxufSBmcm9tICcuL1Blcm1pc3Npb25Qcm9tcHQuanMnXG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMgfSBmcm9tICcuL1Blcm1pc3Npb25SZXF1ZXN0LmpzJ1xuaW1wb3J0IHsgUGVybWlzc2lvblJ1bGVFeHBsYW5hdGlvbiB9IGZyb20gJy4vUGVybWlzc2lvblJ1bGVFeHBsYW5hdGlvbi5qcydcblxudHlwZSBGYWxsYmFja09wdGlvblZhbHVlID0gJ3llcycgfCAneWVzLWRvbnQtYXNrLWFnYWluJyB8ICdubydcblxuZXhwb3J0IGZ1bmN0aW9uIEZhbGxiYWNrUGVybWlzc2lvblJlcXVlc3Qoe1xuICB0b29sVXNlQ29uZmlybSxcbiAgb25Eb25lLFxuICBvblJlamVjdCxcbiAgdmVyYm9zZTogX3ZlcmJvc2UsXG4gIHdvcmtlckJhZGdlLFxufTogUGVybWlzc2lvblJlcXVlc3RQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZV0gPSB1c2VUaGVtZSgpXG4gIC8vIFRPRE86IEF2b2lkIHRoZXNlIHNwZWNpYWwgY2FzZXNcbiAgY29uc3Qgb3JpZ2luYWxVc2VyRmFjaW5nTmFtZSA9IHRvb2xVc2VDb25maXJtLnRvb2wudXNlckZhY2luZ05hbWUoXG4gICAgdG9vbFVzZUNvbmZpcm0uaW5wdXQgYXMgbmV2ZXIsXG4gIClcbiAgY29uc3QgdXNlckZhY2luZ05hbWUgPSBvcmlnaW5hbFVzZXJGYWNpbmdOYW1lLmVuZHNXaXRoKCcgKE1DUCknKVxuICAgID8gb3JpZ2luYWxVc2VyRmFjaW5nTmFtZS5zbGljZSgwLCAtNilcbiAgICA6IG9yaWdpbmFsVXNlckZhY2luZ05hbWVcblxuICBjb25zdCB1bmFyeUV2ZW50ID0gdXNlTWVtbzxVbmFyeUV2ZW50PihcbiAgICAoKSA9PiAoe1xuICAgICAgY29tcGxldGlvbl90eXBlOiAndG9vbF91c2Vfc2luZ2xlJyxcbiAgICAgIGxhbmd1YWdlX25hbWU6ICdub25lJyxcbiAgICB9KSxcbiAgICBbXSxcbiAgKVxuXG4gIHVzZVBlcm1pc3Npb25SZXF1ZXN0TG9nZ2luZyh0b29sVXNlQ29uZmlybSwgdW5hcnlFdmVudClcblxuICBjb25zdCBoYW5kbGVTZWxlY3QgPSB1c2VDYWxsYmFjayhcbiAgICAodmFsdWU6IEZhbGxiYWNrT3B0aW9uVmFsdWUsIGZlZWRiYWNrPzogc3RyaW5nKSA9PiB7XG4gICAgICBzd2l0Y2ggKHZhbHVlKSB7XG4gICAgICAgIGNhc2UgJ3llcyc6XG4gICAgICAgICAgdm9pZCBsb2dVbmFyeUV2ZW50KHtcbiAgICAgICAgICAgIGNvbXBsZXRpb25fdHlwZTogJ3Rvb2xfdXNlX3NpbmdsZScsXG4gICAgICAgICAgICBldmVudDogJ2FjY2VwdCcsXG4gICAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAgICBsYW5ndWFnZV9uYW1lOiAnbm9uZScsXG4gICAgICAgICAgICAgIG1lc3NhZ2VfaWQ6IHRvb2xVc2VDb25maXJtLmFzc2lzdGFudE1lc3NhZ2UubWVzc2FnZS5pZCxcbiAgICAgICAgICAgICAgcGxhdGZvcm06IGVudi5wbGF0Zm9ybSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSlcbiAgICAgICAgICB0b29sVXNlQ29uZmlybS5vbkFsbG93KHRvb2xVc2VDb25maXJtLmlucHV0LCBbXSwgZmVlZGJhY2spXG4gICAgICAgICAgb25Eb25lKClcbiAgICAgICAgICBicmVha1xuICAgICAgICBjYXNlICd5ZXMtZG9udC1hc2stYWdhaW4nOiB7XG4gICAgICAgICAgdm9pZCBsb2dVbmFyeUV2ZW50KHtcbiAgICAgICAgICAgIGNvbXBsZXRpb25fdHlwZTogJ3Rvb2xfdXNlX3NpbmdsZScsXG4gICAgICAgICAgICBldmVudDogJ2FjY2VwdCcsXG4gICAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAgICBsYW5ndWFnZV9uYW1lOiAnbm9uZScsXG4gICAgICAgICAgICAgIG1lc3NhZ2VfaWQ6IHRvb2xVc2VDb25maXJtLmFzc2lzdGFudE1lc3NhZ2UubWVzc2FnZS5pZCxcbiAgICAgICAgICAgICAgcGxhdGZvcm06IGVudi5wbGF0Zm9ybSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSlcblxuICAgICAgICAgIHRvb2xVc2VDb25maXJtLm9uQWxsb3codG9vbFVzZUNvbmZpcm0uaW5wdXQsIFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgdHlwZTogJ2FkZFJ1bGVzJyxcbiAgICAgICAgICAgICAgcnVsZXM6IFtcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICB0b29sTmFtZTogdG9vbFVzZUNvbmZpcm0udG9vbC5uYW1lLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIF0sXG4gICAgICAgICAgICAgIGJlaGF2aW9yOiAnYWxsb3cnLFxuICAgICAgICAgICAgICBkZXN0aW5hdGlvbjogJ2xvY2FsU2V0dGluZ3MnLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICBdKVxuICAgICAgICAgIG9uRG9uZSgpXG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgICBjYXNlICdubyc6XG4gICAgICAgICAgdm9pZCBsb2dVbmFyeUV2ZW50KHtcbiAgICAgICAgICAgIGNvbXBsZXRpb25fdHlwZTogJ3Rvb2xfdXNlX3NpbmdsZScsXG4gICAgICAgICAgICBldmVudDogJ3JlamVjdCcsXG4gICAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAgICBsYW5ndWFnZV9uYW1lOiAnbm9uZScsXG4gICAgICAgICAgICAgIG1lc3NhZ2VfaWQ6IHRvb2xVc2VDb25maXJtLmFzc2lzdGFudE1lc3NhZ2UubWVzc2FnZS5pZCxcbiAgICAgICAgICAgICAgcGxhdGZvcm06IGVudi5wbGF0Zm9ybSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSlcbiAgICAgICAgICB0b29sVXNlQ29uZmlybS5vblJlamVjdChmZWVkYmFjaylcbiAgICAgICAgICBvblJlamVjdCgpXG4gICAgICAgICAgb25Eb25lKClcbiAgICAgICAgICBicmVha1xuICAgICAgfVxuICAgIH0sXG4gICAgW3Rvb2xVc2VDb25maXJtLCBvbkRvbmUsIG9uUmVqZWN0XSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZUNhbmNlbCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICB2b2lkIGxvZ1VuYXJ5RXZlbnQoe1xuICAgICAgY29tcGxldGlvbl90eXBlOiAndG9vbF91c2Vfc2luZ2xlJyxcbiAgICAgIGV2ZW50OiAncmVqZWN0JyxcbiAgICAgIG1ldGFkYXRhOiB7XG4gICAgICAgIGxhbmd1YWdlX25hbWU6ICdub25lJyxcbiAgICAgICAgbWVzc2FnZV9pZDogdG9vbFVzZUNvbmZpcm0uYXNzaXN0YW50TWVzc2FnZS5tZXNzYWdlLmlkLFxuICAgICAgICBwbGF0Zm9ybTogZW52LnBsYXRmb3JtLFxuICAgICAgfSxcbiAgICB9KVxuICAgIHRvb2xVc2VDb25maXJtLm9uUmVqZWN0KClcbiAgICBvblJlamVjdCgpXG4gICAgb25Eb25lKClcbiAgfSwgW3Rvb2xVc2VDb25maXJtLCBvbkRvbmUsIG9uUmVqZWN0XSlcblxuICBjb25zdCBvcmlnaW5hbEN3ZCA9IGdldE9yaWdpbmFsQ3dkKClcbiAgY29uc3Qgc2hvd0Fsd2F5c0FsbG93T3B0aW9ucyA9IHNob3VsZFNob3dBbHdheXNBbGxvd09wdGlvbnMoKVxuICBjb25zdCBvcHRpb25zID0gdXNlTWVtbygoKTogUGVybWlzc2lvblByb21wdE9wdGlvbjxGYWxsYmFja09wdGlvblZhbHVlPltdID0+IHtcbiAgICBjb25zdCByZXN1bHQ6IFBlcm1pc3Npb25Qcm9tcHRPcHRpb248RmFsbGJhY2tPcHRpb25WYWx1ZT5bXSA9IFtcbiAgICAgIHtcbiAgICAgICAgbGFiZWw6ICdZZXMnLFxuICAgICAgICB2YWx1ZTogJ3llcycsXG4gICAgICAgIGZlZWRiYWNrQ29uZmlnOiB7IHR5cGU6ICdhY2NlcHQnIH0sXG4gICAgICB9LFxuICAgIF1cblxuICAgIGlmIChzaG93QWx3YXlzQWxsb3dPcHRpb25zKSB7XG4gICAgICByZXN1bHQucHVzaCh7XG4gICAgICAgIGxhYmVsOiAoXG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICBZZXMsIGFuZCBkb24mYXBvczt0IGFzayBhZ2FpbiBmb3IgPFRleHQgYm9sZD57dXNlckZhY2luZ05hbWV9PC9UZXh0PnsnICd9XG4gICAgICAgICAgICBjb21tYW5kcyBpbiA8VGV4dCBib2xkPntvcmlnaW5hbEN3ZH08L1RleHQ+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApLFxuICAgICAgICB2YWx1ZTogJ3llcy1kb250LWFzay1hZ2FpbicsXG4gICAgICB9KVxuICAgIH1cblxuICAgIHJlc3VsdC5wdXNoKHtcbiAgICAgIGxhYmVsOiAnTm8nLFxuICAgICAgdmFsdWU6ICdubycsXG4gICAgICBmZWVkYmFja0NvbmZpZzogeyB0eXBlOiAncmVqZWN0JyB9LFxuICAgIH0pXG5cbiAgICByZXR1cm4gcmVzdWx0XG4gIH0sIFt1c2VyRmFjaW5nTmFtZSwgb3JpZ2luYWxDd2QsIHNob3dBbHdheXNBbGxvd09wdGlvbnNdKVxuXG4gIGNvbnN0IHRvb2xBbmFseXRpY3NDb250ZXh0ID0gdXNlTWVtbyhcbiAgICAoKTogVG9vbEFuYWx5dGljc0NvbnRleHQgPT4gKHtcbiAgICAgIHRvb2xOYW1lOiBzYW5pdGl6ZVRvb2xOYW1lRm9yQW5hbHl0aWNzKHRvb2xVc2VDb25maXJtLnRvb2wubmFtZSksXG4gICAgICBpc01jcDogdG9vbFVzZUNvbmZpcm0udG9vbC5pc01jcCA/PyBmYWxzZSxcbiAgICB9KSxcbiAgICBbdG9vbFVzZUNvbmZpcm0udG9vbC5uYW1lLCB0b29sVXNlQ29uZmlybS50b29sLmlzTWNwXSxcbiAgKVxuXG4gIHJldHVybiAoXG4gICAgPFBlcm1pc3Npb25EaWFsb2cgdGl0bGU9XCJUb29sIHVzZVwiIHdvcmtlckJhZGdlPXt3b3JrZXJCYWRnZX0+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nWD17Mn0gcGFkZGluZ1k9ezF9PlxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICB7dXNlckZhY2luZ05hbWV9KFxuICAgICAgICAgIHt0b29sVXNlQ29uZmlybS50b29sLnJlbmRlclRvb2xVc2VNZXNzYWdlKFxuICAgICAgICAgICAgdG9vbFVzZUNvbmZpcm0uaW5wdXQgYXMgbmV2ZXIsXG4gICAgICAgICAgICB7IHRoZW1lLCB2ZXJib3NlOiB0cnVlIH0sXG4gICAgICAgICAgKX1cbiAgICAgICAgICApXG4gICAgICAgICAge29yaWdpbmFsVXNlckZhY2luZ05hbWUuZW5kc1dpdGgoJyAoTUNQKScpID8gKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+IChNQ1ApPC9UZXh0PlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAnJ1xuICAgICAgICAgICl9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+e3RydW5jYXRlVG9MaW5lcyh0b29sVXNlQ29uZmlybS5kZXNjcmlwdGlvbiwgMyl9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8UGVybWlzc2lvblJ1bGVFeHBsYW5hdGlvblxuICAgICAgICAgIHBlcm1pc3Npb25SZXN1bHQ9e3Rvb2xVc2VDb25maXJtLnBlcm1pc3Npb25SZXN1bHR9XG4gICAgICAgICAgdG9vbFR5cGU9XCJ0b29sXCJcbiAgICAgICAgLz5cbiAgICAgICAgPFBlcm1pc3Npb25Qcm9tcHRcbiAgICAgICAgICBvcHRpb25zPXtvcHRpb25zfVxuICAgICAgICAgIG9uU2VsZWN0PXtoYW5kbGVTZWxlY3R9XG4gICAgICAgICAgb25DYW5jZWw9e2hhbmRsZUNhbmNlbH1cbiAgICAgICAgICB0b29sQW5hbHl0aWNzQ29udGV4dD17dG9vbEFuYWx5dGljc0NvbnRleHR9XG4gICAgICAgIC8+XG4gICAgICA8L0JveD5cbiAgICA8L1Blcm1pc3Npb25EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFBSUMsV0FBVyxFQUFFQyxPQUFPLFFBQVEsT0FBTztBQUNuRCxTQUFTQyxjQUFjLFFBQVEsMEJBQTBCO0FBQ3pELFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxFQUFFQyxRQUFRLFFBQVEsY0FBYztBQUNsRCxTQUFTQyw0QkFBNEIsUUFBUSxzQ0FBc0M7QUFDbkYsU0FBU0MsR0FBRyxRQUFRLG9CQUFvQjtBQUN4QyxTQUFTQyw0QkFBNEIsUUFBUSw4Q0FBOEM7QUFDM0YsU0FBU0MsZUFBZSxRQUFRLDRCQUE0QjtBQUM1RCxTQUFTQyxhQUFhLFFBQVEsNkJBQTZCO0FBQzNELFNBQVMsS0FBS0MsVUFBVSxFQUFFQywyQkFBMkIsUUFBUSxZQUFZO0FBQ3pFLFNBQVNDLGdCQUFnQixRQUFRLHVCQUF1QjtBQUN4RCxTQUNFQyxnQkFBZ0IsRUFDaEIsS0FBS0Msc0JBQXNCLEVBQzNCLEtBQUtDLG9CQUFvQixRQUNwQix1QkFBdUI7QUFDOUIsY0FBY0Msc0JBQXNCLFFBQVEsd0JBQXdCO0FBQ3BFLFNBQVNDLHlCQUF5QixRQUFRLGdDQUFnQztBQUUxRSxLQUFLQyxtQkFBbUIsR0FBRyxLQUFLLEdBQUcsb0JBQW9CLEdBQUcsSUFBSTtBQUU5RCxPQUFPLFNBQUFDLDBCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQW1DO0lBQUFDLGNBQUE7SUFBQUMsTUFBQTtJQUFBQyxRQUFBO0lBQUFDO0VBQUEsSUFBQU4sRUFNakI7RUFDdkIsT0FBQU8sS0FBQSxJQUFnQnZCLFFBQVEsQ0FBQyxDQUFDO0VBQUEsSUFBQXdCLHNCQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFSLENBQUEsUUFBQUUsY0FBQSxDQUFBTyxLQUFBLElB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 \ No newline at end of file diff --git a/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx b/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx new file mode 100644 index 0000000..65a9fb9 --- /dev/null +++ b/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx @@ -0,0 +1,182 @@ +import { c as _c } from "react/compiler-runtime"; +import { basename, relative } from 'path'; +import React from 'react'; +import { FileEditToolDiff } from 'src/components/FileEditToolDiff.js'; +import { getCwd } from 'src/utils/cwd.js'; +import type { z } from 'zod/v4'; +import { Text } from '../../../ink.js'; +import { FileEditTool } from '../../../tools/FileEditTool/FileEditTool.js'; +import { FilePermissionDialog } from '../FilePermissionDialog/FilePermissionDialog.js'; +import { createSingleEditDiffConfig, type FileEdit, type IDEDiffSupport } from '../FilePermissionDialog/ideDiffConfig.js'; +import type { PermissionRequestProps } from '../PermissionRequest.js'; +type FileEditInput = z.infer; +const ideDiffSupport: IDEDiffSupport = { + getConfig: (input: FileEditInput) => createSingleEditDiffConfig(input.file_path, input.old_string, input.new_string, input.replace_all), + applyChanges: (input: FileEditInput, modifiedEdits: FileEdit[]) => { + const firstEdit = modifiedEdits[0]; + if (firstEdit) { + return { + ...input, + old_string: firstEdit.old_string, + new_string: firstEdit.new_string, + replace_all: firstEdit.replace_all + }; + } + return input; + } +}; +export function FileEditPermissionRequest(props) { + const $ = _c(51); + const parseInput = _temp; + let T0; + let T1; + let T2; + let file_path; + let new_string; + let old_string; + let replace_all; + let t0; + let t1; + let t10; + let t2; + let t3; + let t4; + let t5; + let t6; + let t7; + let t8; + let t9; + if ($[0] !== props.onDone || $[1] !== props.onReject || $[2] !== props.toolUseConfirm || $[3] !== props.toolUseContext || $[4] !== props.workerBadge) { + const parsed = parseInput(props.toolUseConfirm.input); + ({ + file_path, + old_string, + new_string, + replace_all + } = parsed); + T2 = FilePermissionDialog; + t4 = props.toolUseConfirm; + t5 = props.toolUseContext; + t6 = props.onDone; + t7 = props.onReject; + t8 = props.workerBadge; + t9 = "Edit file"; + t10 = relative(getCwd(), file_path); + T1 = Text; + t2 = "Do you want to make this edit to"; + t3 = " "; + T0 = Text; + t0 = true; + t1 = basename(file_path); + $[0] = props.onDone; + $[1] = props.onReject; + $[2] = props.toolUseConfirm; + $[3] = props.toolUseContext; + $[4] = props.workerBadge; + $[5] = T0; + $[6] = T1; + $[7] = T2; + $[8] = file_path; + $[9] = new_string; + $[10] = old_string; + $[11] = replace_all; + $[12] = t0; + $[13] = t1; + $[14] = t10; + $[15] = t2; + $[16] = t3; + $[17] = t4; + $[18] = t5; + $[19] = t6; + $[20] = t7; + $[21] = t8; + $[22] = t9; + } else { + T0 = $[5]; + T1 = $[6]; + T2 = $[7]; + file_path = $[8]; + new_string = $[9]; + old_string = $[10]; + replace_all = $[11]; + t0 = $[12]; + t1 = $[13]; + t10 = $[14]; + t2 = $[15]; + t3 = $[16]; + t4 = $[17]; + t5 = $[18]; + t6 = $[19]; + t7 = $[20]; + t8 = $[21]; + t9 = $[22]; + } + let t11; + if ($[23] !== T0 || $[24] !== t0 || $[25] !== t1) { + t11 = {t1}; + $[23] = T0; + $[24] = t0; + $[25] = t1; + $[26] = t11; + } else { + t11 = $[26]; + } + let t12; + if ($[27] !== T1 || $[28] !== t11 || $[29] !== t2 || $[30] !== t3) { + t12 = {t2}{t3}{t11}?; + $[27] = T1; + $[28] = t11; + $[29] = t2; + $[30] = t3; + $[31] = t12; + } else { + t12 = $[31]; + } + const t13 = replace_all || false; + let t14; + if ($[32] !== new_string || $[33] !== old_string || $[34] !== t13) { + t14 = [{ + old_string, + new_string, + replace_all: t13 + }]; + $[32] = new_string; + $[33] = old_string; + $[34] = t13; + $[35] = t14; + } else { + t14 = $[35]; + } + let t15; + if ($[36] !== file_path || $[37] !== t14) { + t15 = ; + $[36] = file_path; + $[37] = t14; + $[38] = t15; + } else { + t15 = $[38]; + } + let t16; + if ($[39] !== T2 || $[40] !== file_path || $[41] !== t10 || $[42] !== t12 || $[43] !== t15 || $[44] !== t4 || $[45] !== t5 || $[46] !== t6 || $[47] !== t7 || $[48] !== t8 || $[49] !== t9) { + t16 = ; + $[39] = T2; + $[40] = file_path; + $[41] = t10; + $[42] = t12; + $[43] = t15; + $[44] = t4; + $[45] = t5; + $[46] = t6; + $[47] = t7; + $[48] = t8; + $[49] = t9; + $[50] = t16; + } else { + t16 = $[50]; + } + return t16; +} +function _temp(input) { + return FileEditTool.inputSchema.parse(input); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJiYXNlbmFtZSIsInJlbGF0aXZlIiwiUmVhY3QiLCJGaWxlRWRpdFRvb2xEaWZmIiwiZ2V0Q3dkIiwieiIsIlRleHQiLCJGaWxlRWRpdFRvb2wiLCJGaWxlUGVybWlzc2lvbkRpYWxvZyIsImNyZWF0ZVNpbmdsZUVkaXREaWZmQ29uZmlnIiwiRmlsZUVkaXQiLCJJREVEaWZmU3VwcG9ydCIsIlBlcm1pc3Npb25SZXF1ZXN0UHJvcHMiLCJGaWxlRWRpdElucHV0IiwiaW5mZXIiLCJpbnB1dFNjaGVtYSIsImlkZURpZmZTdXBwb3J0IiwiZ2V0Q29uZmlnIiwiaW5wdXQiLCJmaWxlX3BhdGgiLCJvbGRfc3RyaW5nIiwibmV3X3N0cmluZyIsInJlcGxhY2VfYWxsIiwiYXBwbHlDaGFuZ2VzIiwibW9kaWZpZWRFZGl0cyIsImZpcnN0RWRpdCIsIkZpbGVFZGl0UGVybWlzc2lvblJlcXVlc3QiLCJwcm9wcyIsIiQiLCJfYyIsInBhcnNlSW5wdXQiLCJfdGVtcCIsIlQwIiwiVDEiLCJUMiIsInQwIiwidDEiLCJ0MTAiLCJ0MiIsInQzIiwidDQiLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5Iiwib25Eb25lIiwib25SZWplY3QiLCJ0b29sVXNlQ29uZmlybSIsInRvb2xVc2VDb250ZXh0Iiwid29ya2VyQmFkZ2UiLCJwYXJzZWQiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJ0MTYiLCJwYXJzZSJdLCJzb3VyY2VzIjpbIkZpbGVFZGl0UGVybWlzc2lvblJlcXVlc3QudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGJhc2VuYW1lLCByZWxhdGl2ZSB9IGZyb20gJ3BhdGgnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBGaWxlRWRpdFRvb2xEaWZmIH0gZnJvbSAnc3JjL2NvbXBvbmVudHMvRmlsZUVkaXRUb29sRGlmZi5qcydcbmltcG9ydCB7IGdldEN3ZCB9IGZyb20gJ3NyYy91dGlscy9jd2QuanMnXG5pbXBvcnQgdHlwZSB7IHogfSBmcm9tICd6b2QvdjQnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgRmlsZUVkaXRUb29sIH0gZnJvbSAnLi4vLi4vLi4vdG9vbHMvRmlsZUVkaXRUb29sL0ZpbGVFZGl0VG9vbC5qcydcbmltcG9ydCB7IEZpbGVQZXJtaXNzaW9uRGlhbG9nIH0gZnJvbSAnLi4vRmlsZVBlcm1pc3Npb25EaWFsb2cvRmlsZVBlcm1pc3Npb25EaWFsb2cuanMnXG5pbXBvcnQge1xuICBjcmVhdGVTaW5nbGVFZGl0RGlmZkNvbmZpZyxcbiAgdHlwZSBGaWxlRWRpdCxcbiAgdHlwZSBJREVEaWZmU3VwcG9ydCxcbn0gZnJvbSAnLi4vRmlsZVBlcm1pc3Npb25EaWFsb2cvaWRlRGlmZkNvbmZpZy5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvblJlcXVlc3RQcm9wcyB9IGZyb20gJy4uL1Blcm1pc3Npb25SZXF1ZXN0LmpzJ1xuXG50eXBlIEZpbGVFZGl0SW5wdXQgPSB6LmluZmVyPHR5cGVvZiBGaWxlRWRpdFRvb2wuaW5wdXRTY2hlbWE+XG5cbmNvbnN0IGlkZURpZmZTdXBwb3J0OiBJREVEaWZmU3VwcG9ydDxGaWxlRWRpdElucHV0PiA9IHtcbiAgZ2V0Q29uZmlnOiAoaW5wdXQ6IEZpbGVFZGl0SW5wdXQpID0+XG4gICAgY3JlYXRlU2luZ2xlRWRpdERpZmZDb25maWcoXG4gICAgICBpbnB1dC5maWxlX3BhdGgsXG4gICAgICBpbnB1dC5vbGRfc3RyaW5nLFxuICAgICAgaW5wdXQubmV3X3N0cmluZyxcbiAgICAgIGlucHV0LnJlcGxhY2VfYWxsLFxuICAgICksXG4gIGFwcGx5Q2hhbmdlczogKGlucHV0OiBGaWxlRWRpdElucHV0LCBtb2RpZmllZEVkaXRzOiBGaWxlRWRpdFtdKSA9PiB7XG4gICAgY29uc3QgZmlyc3RFZGl0ID0gbW9kaWZpZWRFZGl0c1swXVxuICAgIGlmIChmaXJzdEVkaXQpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLmlucHV0LFxuICAgICAgICBvbGRfc3RyaW5nOiBmaXJzdEVkaXQub2xkX3N0cmluZyxcbiAgICAgICAgbmV3X3N0cmluZzogZmlyc3RFZGl0Lm5ld19zdHJpbmcsXG4gICAgICAgIHJlcGxhY2VfYWxsOiBmaXJzdEVkaXQucmVwbGFjZV9hbGwsXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBpbnB1dFxuICB9LFxufVxuXG5leHBvcnQgZnVuY3Rpb24gRmlsZUVkaXRQZXJtaXNzaW9uUmVxdWVzdChcbiAgcHJvcHM6IFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMsXG4pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBwYXJzZUlucHV0ID0gKGlucHV0OiB1bmtub3duKTogRmlsZUVkaXRJbnB1dCA9PiB7XG4gICAgcmV0dXJuIEZpbGVFZGl0VG9vbC5pbnB1dFNjaGVtYS5wYXJzZShpbnB1dClcbiAgfVxuXG4gIGNvbnN0IHBhcnNlZCA9IHBhcnNlSW5wdXQocHJvcHMudG9vbFVzZUNvbmZpcm0uaW5wdXQpXG4gIGNvbnN0IHsgZmlsZV9wYXRoLCBvbGRfc3RyaW5nLCBuZXdfc3RyaW5nLCByZXBsYWNlX2FsbCB9ID0gcGFyc2VkXG5cbiAgcmV0dXJuIChcbiAgICA8RmlsZVBlcm1pc3Npb25EaWFsb2dcbiAgICAgIHRvb2xVc2VDb25maXJtPXtwcm9wcy50b29sVXNlQ29uZmlybX1cbiAgICAgIHRvb2xVc2VDb250ZXh0PXtwcm9wcy50b29sVXNlQ29udGV4dH1cbiAgICAgIG9uRG9uZT17cHJvcHMub25Eb25lfVxuICAgICAgb25SZWplY3Q9e3Byb3BzLm9uUmVqZWN0fVxuICAgICAgd29ya2VyQmFkZ2U9e3Byb3BzLndvcmtlckJhZGdlfVxuICAgICAgdGl0bGU9XCJFZGl0IGZpbGVcIlxuICAgICAgc3VidGl0bGU9e3JlbGF0aXZlKGdldEN3ZCgpLCBmaWxlX3BhdGgpfVxuICAgICAgcXVlc3Rpb249e1xuICAgICAgICA8VGV4dD5cbiAgICAgICAgICBEbyB5b3Ugd2FudCB0byBtYWtlIHRoaXMgZWRpdCB0b3snICd9XG4gICAgICAgICAgPFRleHQgYm9sZD57YmFzZW5hbWUoZmlsZV9wYXRoKX08L1RleHQ+P1xuICAgICAgICA8L1RleHQ+XG4gICAgICB9XG4gICAgICBjb250ZW50PXtcbiAgICAgICAgPEZpbGVFZGl0VG9vbERpZmZcbiAgICAgICAgICBmaWxlX3BhdGg9e2ZpbGVfcGF0aH1cbiAgICAgICAgICBlZGl0cz17W1xuICAgICAgICAgICAgeyBvbGRfc3RyaW5nLCBuZXdfc3RyaW5nLCByZXBsYWNlX2FsbDogcmVwbGFjZV9hbGwgfHwgZmFsc2UgfSxcbiAgICAgICAgICBdfVxuICAgICAgICAvPlxuICAgICAgfVxuICAgICAgcGF0aD17ZmlsZV9wYXRofVxuICAgICAgY29tcGxldGlvblR5cGU9XCJzdHJfcmVwbGFjZV9zaW5nbGVcIlxuICAgICAgcGFyc2VJbnB1dD17cGFyc2VJbnB1dH1cbiAgICAgIGlkZURpZmZTdXBwb3J0PXtpZGVEaWZmU3VwcG9ydH1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxRQUFRLEVBQUVDLFFBQVEsUUFBUSxNQUFNO0FBQ3pDLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLGdCQUFnQixRQUFRLG9DQUFvQztBQUNyRSxTQUFTQyxNQUFNLFFBQVEsa0JBQWtCO0FBQ3pDLGNBQWNDLENBQUMsUUFBUSxRQUFRO0FBQy9CLFNBQVNDLElBQUksUUFBUSxpQkFBaUI7QUFDdEMsU0FBU0MsWUFBWSxRQUFRLDZDQUE2QztBQUMxRSxTQUFTQyxvQkFBb0IsUUFBUSxpREFBaUQ7QUFDdEYsU0FDRUMsMEJBQTBCLEVBQzFCLEtBQUtDLFFBQVEsRUFDYixLQUFLQyxjQUFjLFFBQ2QsMENBQTBDO0FBQ2pELGNBQWNDLHNCQUFzQixRQUFRLHlCQUF5QjtBQUVyRSxLQUFLQyxhQUFhLEdBQUdSLENBQUMsQ0FBQ1MsS0FBSyxDQUFDLE9BQU9QLFlBQVksQ0FBQ1EsV0FBVyxDQUFDO0FBRTdELE1BQU1DLGNBQWMsRUFBRUwsY0FB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 \ No newline at end of file diff --git a/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx b/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx new file mode 100644 index 0000000..e465c29 --- /dev/null +++ b/components/permissions/FilePermissionDialog/FilePermissionDialog.tsx @@ -0,0 +1,204 @@ +import { relative } from 'path'; +import React, { useMemo } from 'react'; +import { useDiffInIDE } from '../../../hooks/useDiffInIDE.js'; +import { Box, Text } from '../../../ink.js'; +import type { ToolUseContext } from '../../../Tool.js'; +import { getLanguageName } from '../../../utils/cliHighlight.js'; +import { getCwd } from '../../../utils/cwd.js'; +import { getFsImplementation, safeResolvePath } from '../../../utils/fsOperations.js'; +import { expandPath } from '../../../utils/path.js'; +import type { CompletionType } from '../../../utils/unaryLogging.js'; +import { Select } from '../../CustomSelect/index.js'; +import { ShowInIDEPrompt } from '../../ShowInIDEPrompt.js'; +import { usePermissionRequestLogging } from '../hooks.js'; +import { PermissionDialog } from '../PermissionDialog.js'; +import type { ToolUseConfirm } from '../PermissionRequest.js'; +import type { WorkerBadgeProps } from '../WorkerBadge.js'; +import type { IDEDiffSupport } from './ideDiffConfig.js'; +import type { FileOperationType, PermissionOption } from './permissionOptions.js'; +import { type ToolInput, useFilePermissionDialog } from './useFilePermissionDialog.js'; +export type FilePermissionDialogProps = { + // Required props from PermissionRequestProps + toolUseConfirm: ToolUseConfirm; + toolUseContext: ToolUseContext; + onDone: () => void; + onReject: () => void; + + // Dialog customization + title: string; + subtitle?: React.ReactNode; + question?: string | React.ReactNode; + content?: React.ReactNode; // Can be general content or diff component + + // Logging + completionType?: CompletionType; + languageName?: string; // override — derived from path when omitted + + // File/directory operations + path: string | null; + parseInput: (input: unknown) => T; + operationType?: FileOperationType; + + // IDE diff support + ideDiffSupport?: IDEDiffSupport; + + // Worker badge for teammate permission requests + workerBadge: WorkerBadgeProps | undefined; +}; +export function FilePermissionDialog({ + toolUseConfirm, + toolUseContext, + onDone, + onReject, + title, + subtitle, + question = 'Do you want to proceed?', + content, + completionType = 'tool_use_single', + path, + parseInput, + operationType = 'write', + ideDiffSupport, + workerBadge, + languageName: languageNameOverride +}: FilePermissionDialogProps): React.ReactNode { + // Derive from path unless caller provided an explicit override (NotebookEdit + // passes 'python'/'markdown' from cell_type). getLanguageName is async; + // downstream UnaryEvent.language_name and logPermissionEvent already accept + // Promise. useMemo keeps the promise stable across renders. + const languageName = useMemo(() => languageNameOverride ?? (path ? getLanguageName(path) : 'none'), [languageNameOverride, path]); + const unaryEvent = useMemo(() => ({ + completion_type: completionType, + language_name: languageName + }), [completionType, languageName]); + usePermissionRequestLogging(toolUseConfirm, unaryEvent); + const symlinkTarget = useMemo(() => { + if (!path || operationType === 'read') { + return null; + } + const expandedPath = expandPath(path); + const fs = getFsImplementation(); + const { + resolvedPath, + isSymlink + } = safeResolvePath(fs, expandedPath); + if (isSymlink) { + return resolvedPath; + } + return null; + }, [path, operationType]); + const fileDialogResult = useFilePermissionDialog({ + filePath: path || '', + completionType, + languageName, + toolUseConfirm, + onDone, + onReject, + parseInput, + operationType + }); + + // Use file dialog results for options + const { + options, + acceptFeedback, + rejectFeedback, + setFocusedOption, + handleInputModeToggle, + focusedOption, + yesInputMode, + noInputMode + } = fileDialogResult; + + // Parse input using the provided parser + const parsedInput = parseInput(toolUseConfirm.input); + + // Set up IDE diff support if enabled. Memoized: getConfig may do disk I/O + // (FileWrite's getConfig calls readFileSync for the old-content diff). + // Keyed on the raw input — parseInput is a pure Zod parse whose result + // depends only on toolUseConfirm.input. + const ideDiffConfig = useMemo(() => ideDiffSupport ? ideDiffSupport.getConfig(parseInput(toolUseConfirm.input)) : null, [ideDiffSupport, toolUseConfirm.input]); + + // Create diff params based on whether IDE diff is available + const diffParams = ideDiffConfig ? { + onChange: (option: PermissionOption, input: { + file_path: string; + edits: Array<{ + old_string: string; + new_string: string; + replace_all?: boolean; + }>; + }) => { + const transformedInput = ideDiffSupport!.applyChanges(parsedInput, input.edits); + fileDialogResult.onChange(option, transformedInput); + }, + toolUseContext, + filePath: ideDiffConfig.filePath, + edits: (ideDiffConfig.edits || []).map(e => ({ + old_string: e.old_string, + new_string: e.new_string, + replace_all: e.replace_all || false + })), + editMode: ideDiffConfig.editMode || 'single' + } : { + onChange: () => {}, + toolUseContext, + filePath: '', + edits: [], + editMode: 'single' as const + }; + const { + closeTabInIDE, + showingDiffInIDE, + ideName + } = useDiffInIDE(diffParams); + const onChange = (option_0: PermissionOption, feedback?: string) => { + closeTabInIDE?.(); + fileDialogResult.onChange(option_0, parsedInput, feedback?.trim()); + }; + if (showingDiffInIDE && ideDiffConfig && path) { + return onChange(option_1, feedback_0)} options={options} filePath={path} input={parsedInput} ideName={ideName} symlinkTarget={symlinkTarget} rejectFeedback={rejectFeedback} acceptFeedback={acceptFeedback} setFocusedOption={setFocusedOption} onInputModeToggle={handleInputModeToggle} focusedOption={focusedOption} yesInputMode={yesInputMode} noInputMode={noInputMode} />; + } + const isSymlinkOutsideCwd = symlinkTarget != null && relative(getCwd(), symlinkTarget).startsWith('..'); + const symlinkWarning = symlinkTarget ? + + {isSymlinkOutsideCwd ? `This will modify ${symlinkTarget} (outside working directory) via a symlink` : `Symlink target: ${symlinkTarget}`} + + : null; + return <> + + {symlinkWarning} + {content} + + {typeof question === 'string' ? {question} : question} + ; + $[42] = handleCancel; + $[43] = handleInputModeToggle; + $[44] = handleSelect; + $[45] = selectOptions; + $[46] = t9; + $[47] = t10; + } else { + t10 = $[47]; + } + const t11 = showTabHint && " \xB7 Tab to amend"; + let t12; + if ($[48] !== t11) { + t12 = Esc to cancel{t11}; + $[48] = t11; + $[49] = t12; + } else { + t12 = $[49]; + } + let t13; + if ($[50] !== t10 || $[51] !== t12 || $[52] !== t8) { + t13 = {t8}{t10}{t12}; + $[50] = t10; + $[51] = t12; + $[52] = t8; + $[53] = t13; + } else { + t13 = $[53]; + } + return t13; +} +function _temp(prev) { + return { + ...prev, + attribution: { + ...prev.attribution, + escapeCount: prev.attribution.escapeCount + 1 + } + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlYWN0Tm9kZSIsInVzZUNhbGxiYWNrIiwidXNlTWVtbyIsInVzZVN0YXRlIiwiQm94IiwiVGV4dCIsIktleWJpbmRpbmdBY3Rpb24iLCJ1c2VLZXliaW5kaW5ncyIsIkFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMiLCJsb2dFdmVudCIsInVzZVNldEFwcFN0YXRlIiwiT3B0aW9uV2l0aERlc2NyaXB0aW9uIiwiU2VsZWN0IiwiRmVlZGJhY2tUeXBlIiwiUGVybWlzc2lvblByb21wdE9wdGlvbiIsInZhbHVlIiwiVCIsImxhYmVsIiwiZmVlZGJhY2tDb25maWciLCJ0eXBlIiwicGxhY2Vob2xkZXIiLCJrZXliaW5kaW5nIiwiVG9vbEFuYWx5dGljc0NvbnRleHQiLCJ0b29sTmFtZSIsImlzTWNwIiwiUGVybWlzc2lvblByb21wdFByb3BzIiwib3B0aW9ucyIsIm9uU2VsZWN0IiwiZmVlZGJhY2siLCJvbkNhbmNlbCIsInF1ZXN0aW9uIiwidG9vbEFuYWx5dGljc0NvbnRleHQiLCJERUZBVUxUX1BMQUNFSE9MREVSUyIsIlJlY29yZCIsImFjY2VwdCIsInJlamVjdCIsIlBlcm1pc3Npb25Qcm9tcHQiLCJ0MCIsIiQiLCJfYyIsInQxIiwidW5kZWZpbmVkIiwic2V0QXBwU3RhdGUiLCJhY2NlcHRGZWVkYmFjayIsInNldEFjY2VwdEZlZWRiYWNrIiwicmVqZWN0RmVlZGJhY2siLCJzZXRSZWplY3RGZWVkYmFjayIsImFjY2VwdElucHV0TW9kZSIsInNldEFjY2VwdElucHV0TW9kZSIsInJlamVjdElucHV0TW9kZSIsInNldFJlamVjdElucHV0TW9kZSIsImZvY3VzZWRWYWx1ZSIsInNldEZvY3VzZWRWYWx1ZSIsImFjY2VwdEZlZWRiYWNrTW9kZUVudGVyZWQiLCJzZXRBY2NlcHRGZWVkYmFja01vZGVFbnRlcmVkIiwicmVqZWN0RmVlZGJhY2tNb2RlRW50ZXJlZCIsInNldFJlamVjdEZlZWRiYWNrTW9kZUVudGVyZWQiLCJ0MiIsInQzIiwib3B0IiwiZmluZCIsImZvY3VzZWRPcHRpb24iLCJmb2N1c2VkRmVlZGJhY2tUeXBlIiwic2hvd1RhYkhpbnQiLCJ0NCIsIm9wdF8wIiwiaXNJbnB1dE1vZGUiLCJvbkNoYW5nZSIsImRlZmF1bHRQbGFjZWhvbGRlciIsImNvbnN0IiwiYWxsb3dFbXB0eVN1Ym1pdFRvQ2FuY2VsIiwibWFwIiwic2VsZWN0T3B0aW9ucyIsInZhbHVlXzAiLCJvcHRpb24iLCJvcHRfMSIsInR5cGVfMCIsImFuYWx5dGljc1Byb3BzIiwiaGFuZGxlSW5wdXRNb2RlVG9nZ2xlIiwidDUiLCJ2YWx1ZV8xIiwib3B0aW9uXzAiLCJvcHRfMiIsInJhd0ZlZWRiYWNrIiwidHJpbW1lZEZlZWRiYWNrIiwidHJpbSIsImFuYWx5dGljc1Byb3BzXzAiLCJoYXNfaW5zdHJ1Y3Rpb25zIiwiaW5zdHJ1Y3Rpb25zX2xlbmd0aCIsImxlbmd0aCIsImVudGVyZWRfZmVlZGJhY2tfbW9kZSIsImhhbmRsZVNlbGVjdCIsImhhbmRsZXJzIiwib3B0XzMiLCJrZXliaW5kaW5nSGFuZGxlcnMiLCJ0NiIsIlN5bWJvbCIsImZvciIsImNvbnRleHQiLCJ0NyIsIl90ZW1wIiwiaGFuZGxlQ2FuY2VsIiwidDgiLCJ0OSIsInZhbHVlXzIiLCJuZXdPcHRpb24iLCJvcHRfNCIsInQxMCIsInQxMSIsInQxMiIsInQxMyIsInByZXYiLCJhdHRyaWJ1dGlvbiIsImVzY2FwZUNvdW50Il0sInNvdXJjZXMiOlsiUGVybWlzc2lvblByb21wdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHR5cGUgUmVhY3ROb2RlLCB1c2VDYWxsYmFjaywgdXNlTWVtbywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgS2V5YmluZGluZ0FjdGlvbiB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3R5cGVzLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZ3MgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnLi4vLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgdXNlU2V0QXBwU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IHR5cGUgT3B0aW9uV2l0aERlc2NyaXB0aW9uLCBTZWxlY3QgfSBmcm9tICcuLi9DdXN0b21TZWxlY3Qvc2VsZWN0LmpzJ1xuXG5leHBvcnQgdHlwZSBGZWVkYmFja1R5cGUgPSAnYWNjZXB0JyB8ICdyZWplY3QnXG5cbmV4cG9ydCB0eXBlIFBlcm1pc3Npb25Qcm9tcHRPcHRpb248VCBleHRlbmRzIHN0cmluZz4gPSB7XG4gIHZhbHVlOiBUXG4gIGxhYmVsOiBSZWFjdE5vZGVcbiAgZmVlZGJhY2tDb25maWc/OiB7XG4gICAgdHlwZTogRmVlZGJhY2tUeXBlXG4gICAgcGxhY2Vob2xkZXI/OiBzdHJpbmdcbiAgfVxuICBrZXliaW5kaW5nPzogS2V5YmluZGluZ0FjdGlvblxufVxuXG5leHBvcnQgdHlwZSBUb29sQW5hbHl0aWNzQ29udGV4dCA9IHtcbiAgdG9vbE5hbWU6IHN0cmluZ1xuICBpc01jcDogYm9vbGVhblxufVxuXG5leHBvcnQgdHlwZSBQZXJtaXNzaW9uUHJvbXB0UHJvcHM8VCBleHRlbmRzIHN0cmluZz4gPSB7XG4gIG9wdGlvbnM6IFBlcm1pc3Npb25Qcm9tcHRPcHRpb248VD5bXVxuICBvblNlbGVjdDogKHZhbHVlOiBULCBmZWVkYmFjaz86IHN0cmluZykgPT4gdm9pZFxuICBvbkNhbmNlbD86ICgpID0+IHZvaWRcbiAgcXVlc3Rpb24/OiBzdHJpbmcgfCBSZWFjdE5vZGVcbiAgdG9vbEFuYWx5dGljc0NvbnRleHQ/OiBUb29sQW5hbHl0aWNzQ29udGV4dFxufVxuXG5jb25zdCBERUZBVUxUX1BMQUNFSE9MREVSUzogUmVjb3JkPEZlZWRiYWNrVHlwZSwgc3RyaW5nPiA9IHtcbiAgYWNjZXB0OiAndGVsbCBDbGF1ZGUgd2hhdCB0byBkbyBuZXh0JyxcbiAgcmVqZWN0OiAndGVsbCBDbGF1ZGUgd2hhdCB0byBkbyBkaWZmZXJlbnRseScsXG59XG5cbi8qKlxuICogU2hhcmVkIGNvbXBvbmVudCBmb3IgcGVybWlzc2lvbiBwcm9tcHRzIHdpdGggb3B0aW9uYWwgZmVlZGJhY2sgaW5wdXQuXG4gKlxuICogSGFuZGxlczpcbiAqIC0gXCJEbyB5b3Ugd2FudCB0byBwcm9jZWVkP1wiIHF1ZXN0aW9uIHdpdGggb3B0aW9uYWwgVGFiIGhpbnRcbiAqIC0gRmVhdHVyZSBmbGFnIGNoZWNrIGZvciBmZWVkYmFjayBjYXBhYmlsaXR5XG4gKiAtIElucHV0IG1vZGUgdG9nZ2xpbmcgKFRhYiB0byBleHBhbmQgZmVlZGJhY2sgaW5wdXQpXG4gKiAtIEFuYWx5dGljcyBldmVudHMgZm9yIGZlZWRiYWNrIGludGVyYWN0aW9uc1xuICogLSBUcmFuc2Zvcm1pbmcgb3B0aW9ucyB0byBTZWxlY3QtY29tcGF0aWJsZSBmb3JtYXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFBlcm1pc3Npb25Qcm9tcHQ8VCBleHRlbmRzIHN0cmluZz4oe1xuICBvcHRpb25zLFxuICBvblNlbGVjdCxcbiAgb25DYW5jZWwsXG4gIHF1ZXN0aW9uID0gJ0RvIHlvdSB3YW50IHRvIHByb2NlZWQ/JyxcbiAgdG9vbEFuYWx5dGljc0NvbnRleHQsXG59OiBQZXJtaXNzaW9uUHJvbXB0UHJvcHM8VD4pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBzZXRBcHBTdGF0ZSA9IHVzZVNldEFwcFN0YXRlKClcbiAgY29uc3QgW2FjY2VwdEZlZWRiYWNrLCBzZXRBY2NlcHRGZWVkYmFja10gPSB1c2VTdGF0ZSgnJylcbiAgY29uc3QgW3JlamVjdEZlZWRiYWNrLCBzZXRSZWplY3RGZWVkYmFja10gPSB1c2VTdGF0ZSgnJylcbiAgY29uc3QgW2FjY2VwdElucHV0TW9kZSwgc2V0QWNjZXB0SW5wdXRNb2RlXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbcmVqZWN0SW5wdXRNb2RlLCBzZXRSZWplY3RJbnB1dE1vZGVdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtmb2N1c2VkVmFsdWUsIHNldEZvY3VzZWRWYWx1ZV0gPSB1c2VTdGF0ZTxUIHwgbnVsbD4obnVsbClcbiAgLy8gVHJhY2sgd2hldGhlciB1c2VyIGV2ZXIgZW50ZXJlZCBmZWVkYmFjayBtb2RlIChwZXJzaXN0cyBhZnRlciBjb2xsYXBzZSlcbiAgY29uc3QgW2FjY2VwdEZlZWRiYWNrTW9kZUVudGVyZWQsIHNldEFjY2VwdEZlZWRiYWNrTW9kZUVudGVyZWRdID1cbiAgICB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW3JlamVjdEZlZWRiYWNrTW9kZUVudGVyZWQsIHNldFJlamVjdEZlZWRiYWNrTW9kZUVudGVyZWRdID1cbiAgICB1c2VTdGF0ZShmYWxzZSlcblxuICAvLyBGaW5kIHdoaWNoIG9wdGlvbiBpcyBmb2N1c2VkIGFuZCB3aGV0aGVyIGl0IGhhcyBmZWVkYmFjayBjb25maWdcbiAgY29uc3QgZm9jdXNlZE9wdGlvbiA9IG9wdGlvbnMuZmluZChvcHQgPT4gb3B0LnZhbHVlID09PSBmb2N1c2VkVmFsdWUpXG4gIGNvbnN0IGZvY3VzZWRGZWVkYmFja1R5cGUgPSBmb2N1c2VkT3B0aW9uPy5mZWVkYmFja0NvbmZpZz8udHlwZVxuXG4gIC8vIFNob3cgVGFiIGhpbnQgd2hlbiBmb2N1c2VkIG9uIGEgZmVlZGJhY2stZW5hYmxlZCBvcHRpb24gdGhhdCdzIG5vdCBhbHJlYWR5IGluIGlucHV0IG1vZGVcbiAgY29uc3Qgc2hvd1RhYkhpbnQgPVxuICAgIChmb2N1c2VkRmVlZGJhY2tUeXBlID09PSAnYWNjZXB0JyAmJiAhYWNjZXB0SW5wdXRNb2RlKSB8fFxuICAgIChmb2N1c2VkRmVlZGJhY2tUeXBlID09PSAncmVqZWN0JyAmJiAhcmVqZWN0SW5wdXRNb2RlKVxuXG4gIC8vIFRyYW5zZm9ybSBvcHRpb25zIHRvIFNlbGVjdC1jb21wYXRpYmxlIGZvcm1hdFxuICBjb25zdCBzZWxlY3RPcHRpb25zID0gdXNlTWVtbygoKTogT3B0aW9uV2l0aERlc2NyaXB0aW9uPFQ+W10gPT4ge1xuICAgIHJldHVybiBvcHRpb25zLm1hcChvcHQgPT4ge1xuICAgICAgY29uc3QgeyB2YWx1ZSwgbGFiZWwsIGZlZWRiYWNrQ29uZmlnIH0gPSBvcHRcblxuICAgICAgLy8gTm8gZmVlZGJhY2sgY29uZmlnID0gc2ltcGxlIG9wdGlvblxuICAgICAgaWYgKCFmZWVkYmFja0NvbmZpZykge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGxhYmVsLFxuICAgICAgICAgIHZhbHVlLFxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHsgdHlwZSwgcGxhY2Vob2xkZXIgfSA9IGZlZWRiYWNrQ29uZmlnXG4gICAgICBjb25zdCBpc0lucHV0TW9kZSA9IHR5cGUgPT09ICdhY2NlcHQnID8gYWNjZXB0SW5wdXRNb2RlIDogcmVqZWN0SW5wdXRNb2RlXG4gICAgICBjb25zdCBvbkNoYW5nZSA9IHR5cGUgPT09ICdhY2NlcHQnID8gc2V0QWNjZXB0RmVlZGJhY2sgOiBzZXRSZWplY3RGZWVkYmFja1xuICAgICAgY29uc3QgZGVmYXVsdFBsYWNlaG9sZGVyID0gREVGQVVMVF9QTEFDRUhPTERFUlNbdHlwZV1cblxuICAgICAgLy8gV2hlbiBpbiBpbnB1dCBtb2RlLCBzaG93IGlucHV0IGZpZWxkXG4gICAgICBpZiAoaXNJbnB1dE1vZGUpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICB0eXBlOiAnaW5wdXQnIGFzIGNvbnN0LFxuICAgICAgICAgIGxhYmVsLFxuICAgICAgICAgIHZhbHVlLFxuICAgICAgICAgIHBsYWNlaG9sZGVyOiBwbGFjZWhvbGRlciA/PyBkZWZhdWx0UGxhY2Vob2xkZXIsXG4gICAgICAgICAgb25DaGFuZ2UsXG4gICAgICAgICAgYWxsb3dFbXB0eVN1Ym1pdFRvQ2FuY2VsOiB0cnVlLFxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIE5vdCBpbiBpbnB1dCBtb2RlIC0gc2hvdyBzaW1wbGUgb3B0aW9uXG4gICAgICByZXR1cm4ge1xuICAgICAgICBsYWJlbCxcbiAgICAgICAgdmFsdWUsXG4gICAgICB9XG4gICAgfSlcbiAgfSwgW29wdGlvbnMsIGFjY2VwdElucHV0TW9kZSwgcmVqZWN0SW5wdXRNb2RlXSlcblxuICAvLyBIYW5kbGUgVGFiIGtleSB0byB0b2dnbGUgaW5wdXQgbW9kZVxuICBjb25zdCBoYW5kbGVJbnB1dE1vZGVUb2dnbGUgPSB1c2VDYWxsYmFjayhcbiAgICAodmFsdWU6IFQpID0+IHtcbiAgICAgIGNvbnN0IG9wdGlvbiA9IG9wdGlvbnMuZmluZChvcHQgPT4gb3B0LnZhbHVlID09PSB2YWx1ZSlcbiAgICAgIGlmICghb3B0aW9uPy5mZWVkYmFja0NvbmZpZykgcmV0dXJuXG5cbiAgICAgIGNvbnN0IHsgdHlwZSB9ID0gb3B0aW9uLmZlZWRiYWNrQ29uZmlnXG4gICAgICBjb25zdCBhbmFseXRpY3NQcm9wcyA9IHtcbiAgICAgICAgdG9vbE5hbWU6XG4gICAgICAgICAgdG9vbEFuYWx5dGljc0NvbnRleHQ/LnRvb2xOYW1lIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGlzTWNwOiB0b29sQW5hbHl0aWNzQ29udGV4dD8uaXNNY3AgPz8gZmFsc2UsXG4gICAgICB9XG5cbiAgICAgIGlmICh0eXBlID09PSAnYWNjZXB0Jykge1xuICAgICAgICBpZiAoYWNjZXB0SW5wdXRNb2RlKSB7XG4gICAgICAgICAgc2V0QWNjZXB0SW5wdXRNb2RlKGZhbHNlKVxuICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9hY2NlcHRfZmVlZGJhY2tfbW9kZV9jb2xsYXBzZWQnLCBhbmFseXRpY3NQcm9wcylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzZXRBY2NlcHRJbnB1dE1vZGUodHJ1ZSlcbiAgICAgICAgICBzZXRBY2NlcHRGZWVkYmFja01vZGVFbnRlcmVkKHRydWUpXG4gICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2FjY2VwdF9mZWVkYmFja19tb2RlX2VudGVyZWQnLCBhbmFseXRpY3NQcm9wcylcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmICh0eXBlID09PSAncmVqZWN0Jykge1xuICAgICAgICBpZiAocmVqZWN0SW5wdXRNb2RlKSB7XG4gICAgICAgICAgc2V0UmVqZWN0SW5wdXRNb2RlKGZhbHNlKVxuICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9yZWplY3RfZmVlZGJhY2tfbW9kZV9jb2xsYXBzZWQnLCBhbmFseXRpY3NQcm9wcylcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzZXRSZWplY3RJbnB1dE1vZGUodHJ1ZSlcbiAgICAgICAgICBzZXRSZWplY3RGZWVkYmFja01vZGVFbnRlcmVkKHRydWUpXG4gICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3JlamVjdF9mZWVkYmFja19tb2RlX2VudGVyZWQnLCBhbmFseXRpY3NQcm9wcylcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0sXG4gICAgW29wdGlvbnMsIGFjY2VwdElucHV0TW9kZSwgcmVqZWN0SW5wdXRNb2RlLCB0b29sQW5hbHl0aWNzQ29udGV4dF0sXG4gIClcblxuICAvLyBIYW5kbGUgc2VsZWN0aW9uXG4gIGNvbnN0IGhhbmRsZVNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgICh2YWx1ZTogVCkgPT4ge1xuICAgICAgY29uc3Qgb3B0aW9uID0gb3B0aW9ucy5maW5kKG9wdCA9PiBvcHQudmFsdWUgPT09IHZhbHVlKVxuICAgICAgaWYgKCFvcHRpb24pIHJldHVyblxuXG4gICAgICAvLyBHZXQgZmVlZGJhY2sgaWYgYXBwbGljYWJsZVxuICAgICAgbGV0IGZlZWRiYWNrOiBzdHJpbmcgfCB1bmRlZmluZWRcbiAgICAgIGlmIChvcHRpb24uZmVlZGJhY2tDb25maWcpIHtcbiAgICAgICAgY29uc3QgcmF3RmVlZGJhY2sgPVxuICAgICAgICAgIG9wdGlvbi5mZWVkYmFja0NvbmZpZy50eXBlID09PSAnYWNjZXB0J1xuICAgICAgICAgICAgPyBhY2NlcHRGZWVkYmFja1xuICAgICAgICAgICAgOiByZWplY3RGZWVkYmFja1xuICAgICAgICBjb25zdCB0cmltbWVkRmVlZGJhY2sgPSByYXdGZWVkYmFjay50cmltKClcblxuICAgICAgICBpZiAodHJpbW1lZEZlZWRiYWNrKSB7XG4gICAgICAgICAgZmVlZGJhY2sgPSB0cmltbWVkRmVlZGJhY2tcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIExvZyBhY2NlcHQvcmVqZWN0IHN1Ym1pc3Npb24gd2l0aCBmZWVkYmFjayBjb250ZXh0XG4gICAgICAgIGNvbnN0IGFuYWx5dGljc1Byb3BzID0ge1xuICAgICAgICAgIHRvb2xOYW1lOlxuICAgICAgICAgICAgdG9vbEFuYWx5dGljc0NvbnRleHQ/LnRvb2xOYW1lIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgaXNNY3A6IHRvb2xBbmFseXRpY3NDb250ZXh0Py5pc01jcCA/PyBmYWxzZSxcbiAgICAgICAgICBoYXNfaW5zdHJ1Y3Rpb25zOiAhIXRyaW1tZWRGZWVkYmFjayxcbiAgICAgICAgICBpbnN0cnVjdGlvbnNfbGVuZ3RoOiB0cmltbWVkRmVlZGJhY2s/Lmxlbmd0aCA/PyAwLFxuICAgICAgICAgIGVudGVyZWRfZmVlZGJhY2tfbW9kZTpcbiAgICAgICAgICAgIG9wdGlvbi5mZWVkYmFja0NvbmZpZy50eXBlID09PSAnYWNjZXB0J1xuICAgICAgICAgICAgICA/IGFjY2VwdEZlZWRiYWNrTW9kZUVudGVyZWRcbiAgICAgICAgICAgICAgOiByZWplY3RGZWVkYmFja01vZGVFbnRlcmVkLFxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG9wdGlvbi5mZWVkYmFja0NvbmZpZy50eXBlID09PSAnYWNjZXB0Jykge1xuICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9hY2NlcHRfc3VibWl0dGVkJywgYW5hbHl0aWNzUHJvcHMpXG4gICAgICAgIH0gZWxzZSBpZiAob3B0aW9uLmZlZWRiYWNrQ29uZmlnLnR5cGUgPT09ICdyZWplY3QnKSB7XG4gICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3JlamVjdF9zdWJtaXR0ZWQnLCBhbmFseXRpY3NQcm9wcylcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBvblNlbGVjdCh2YWx1ZSwgZmVlZGJhY2spXG4gICAgfSxcbiAgICBbXG4gICAgICBvcHRpb25zLFxuICAgICAgYWNjZXB0RmVlZGJhY2ssXG4gICAgICByZWplY3RGZWVkYmFjayxcbiAgICAgIG9uU2VsZWN0LFxuICAgICAgdG9vbEFuYWx5dGljc0NvbnRleHQsXG4gICAgICBhY2NlcHRGZWVkYmFja01vZGVFbnRlcmVkLFxuICAgICAgcmVqZWN0RmVlZGJhY2tNb2RlRW50ZXJlZCxcbiAgICBdLFxuICApXG5cbiAgLy8gUmVnaXN0ZXIga2V5YmluZGluZyBoYW5kbGVycyBmb3Igb3B0aW9ucyB0aGF0IGhhdmUgYSBrZXliaW5kaW5nIHNldFxuICBjb25zdCBrZXliaW5kaW5nSGFuZGxlcnMgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBjb25zdCBoYW5kbGVyczogUmVjb3JkPHN0cmluZywgKCkgPT4gdm9pZD4gPSB7fVxuICAgIGZvciAoY29uc3Qgb3B0IG9mIG9wdGlvbnMpIHtcbiAgICAgIGlmIChvcHQua2V5YmluZGluZykge1xuICAgICAgICBoYW5kbGVyc1tvcHQua2V5YmluZGluZ10gPSAoKSA9PiBoYW5kbGVTZWxlY3Qob3B0LnZhbHVlKVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gaGFuZGxlcnNcbiAgfSwgW29wdGlvbnMsIGhhbmRsZVNlbGVjdF0pXG5cbiAgdXNlS2V5YmluZGluZ3Moa2V5YmluZGluZ0hhbmRsZXJzLCB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nIH0pXG5cbiAgLy8gSGFuZGxlIGNhbmNlbCAoRXNjKVxuICBjb25zdCBoYW5kbGVDYW5jZWwgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgbG9nRXZlbnQoJ3Rlbmd1X3Blcm1pc3Npb25fcmVxdWVzdF9lc2NhcGUnLCB7fSlcbiAgICAvLyBJbmNyZW1lbnQgZXNjYXBlIGNvdW50IGZvciBhdHRyaWJ1dGlvbiB0cmFja2luZ1xuICAgIHNldEFwcFN0YXRlKHByZXYgPT4gKHtcbiAgICAgIC4uLnByZXYsXG4gICAgICBhdHRyaWJ1dGlvbjoge1xuICAgICAgICAuLi5wcmV2LmF0dHJpYnV0aW9uLFxuICAgICAgICBlc2NhcGVDb3VudDogcHJldi5hdHRyaWJ1dGlvbi5lc2NhcGVDb3VudCArIDEsXG4gICAgICB9LFxuICAgIH0pKVxuICAgIG9uQ2FuY2VsPy4oKVxuICB9LCBbb25DYW5jZWwsIHNldEFwcFN0YXRlXSlcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAge3R5cGVvZiBxdWVzdGlvbiA9PT0gJ3N0cmluZycgPyA8VGV4dD57cXVlc3Rpb259PC9UZXh0PiA6IHF1ZXN0aW9ufVxuICAgICAgPFNlbGVjdFxuICAgICAgICBvcHRpb25zPXtzZWxlY3RPcHRpb25zfVxuICAgICAgICBpbmxpbmVEZXNjcmlwdGlvbnNcbiAgICAgICAgb25DaGFuZ2U9e2hhbmRsZVNlbGVjdH1cbiAgICAgICAgb25DYW5jZWw9e2hhbmRsZUNhbmNlbH1cbiAgICAgICAgb25Gb2N1cz17dmFsdWUgPT4ge1xuICAgICAgICAgIC8vIFJlc2V0IGlucHV0IG1vZGUgd2hlbiBuYXZpZ2F0aW5nIGF3YXksIGJ1dCBvbmx5IGlmIG5vIHRleHQgdHlwZWRcbiAgICAgICAgICBjb25zdCBuZXdPcHRpb24gPSBvcHRpb25zLmZpbmQob3B0ID0+IG9wdC52YWx1ZSA9PT0gdmFsdWUpXG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgbmV3T3B0aW9uPy5mZWVkYmFja0NvbmZpZz8udHlwZSAhPT0gJ2FjY2VwdCcgJiZcbiAgICAgICAgICAgIGFjY2VwdElucHV0TW9kZSAmJlxuICAgICAgICAgICAgIWFjY2VwdEZlZWRiYWNrLnRyaW0oKVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgc2V0QWNjZXB0SW5wdXRNb2RlKGZhbHNlKVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICBuZXdPcHRpb24/LmZlZWRiYWNrQ29uZmlnPy50eXBlICE9PSAncmVqZWN0JyAmJlxuICAgICAgICAgICAgcmVqZWN0SW5wdXRNb2RlICYmXG4gICAgICAgICAgICAhcmVqZWN0RmVlZGJhY2sudHJpbSgpXG4gICAgICAgICAgKSB7XG4gICAgICAgICAgICBzZXRSZWplY3RJbnB1dE1vZGUoZmFsc2UpXG4gICAgICAgICAgfVxuICAgICAgICAgIHNldEZvY3VzZWRWYWx1ZSh2YWx1ZSlcbiAgICAgICAgfX1cbiAgICAgICAgb25JbnB1dE1vZGVUb2dnbGU9e2hhbmRsZUlucHV0TW9kZVRvZ2dsZX1cbiAgICAgIC8+XG4gICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPkVzYyB0byBjYW5jZWx7c2hvd1RhYkhpbnQgJiYgJyDCtyBUYWIgdG8gYW1lbmQnfTwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUksS0FBS0MsU0FBUyxFQUFFQyxXQUFXLEVBQUVDLE9BQU8sRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDN0UsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxjQUFjQyxnQkFBZ0IsUUFBUSw0QkFBNEI7QUFDbEUsU0FBU0MsY0FBYyxRQUFRLG9DQUFvQztBQUNuRSxTQUNFLEtBQUtDLDBEQUEwRCxF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 \ No newline at end of file diff --git a/components/permissions/PermissionRequest.tsx b/components/permissions/PermissionRequest.tsx new file mode 100644 index 0000000..2def623 --- /dev/null +++ b/components/permissions/PermissionRequest.tsx @@ -0,0 +1,217 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import { EnterPlanModeTool } from 'src/tools/EnterPlanModeTool/EnterPlanModeTool.js'; +import { ExitPlanModeV2Tool } from 'src/tools/ExitPlanModeTool/ExitPlanModeV2Tool.js'; +import { useNotifyAfterTimeout } from '../../hooks/useNotifyAfterTimeout.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import type { AnyObject, Tool, ToolUseContext } from '../../Tool.js'; +import { AskUserQuestionTool } from '../../tools/AskUserQuestionTool/AskUserQuestionTool.js'; +import { BashTool } from '../../tools/BashTool/BashTool.js'; +import { FileEditTool } from '../../tools/FileEditTool/FileEditTool.js'; +import { FileReadTool } from '../../tools/FileReadTool/FileReadTool.js'; +import { FileWriteTool } from '../../tools/FileWriteTool/FileWriteTool.js'; +import { GlobTool } from '../../tools/GlobTool/GlobTool.js'; +import { GrepTool } from '../../tools/GrepTool/GrepTool.js'; +import { NotebookEditTool } from '../../tools/NotebookEditTool/NotebookEditTool.js'; +import { PowerShellTool } from '../../tools/PowerShellTool/PowerShellTool.js'; +import { SkillTool } from '../../tools/SkillTool/SkillTool.js'; +import { WebFetchTool } from '../../tools/WebFetchTool/WebFetchTool.js'; +import type { AssistantMessage } from '../../types/message.js'; +import type { PermissionDecision } from '../../utils/permissions/PermissionResult.js'; +import { AskUserQuestionPermissionRequest } from './AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.js'; +import { BashPermissionRequest } from './BashPermissionRequest/BashPermissionRequest.js'; +import { EnterPlanModePermissionRequest } from './EnterPlanModePermissionRequest/EnterPlanModePermissionRequest.js'; +import { ExitPlanModePermissionRequest } from './ExitPlanModePermissionRequest/ExitPlanModePermissionRequest.js'; +import { FallbackPermissionRequest } from './FallbackPermissionRequest.js'; +import { FileEditPermissionRequest } from './FileEditPermissionRequest/FileEditPermissionRequest.js'; +import { FilesystemPermissionRequest } from './FilesystemPermissionRequest/FilesystemPermissionRequest.js'; +import { FileWritePermissionRequest } from './FileWritePermissionRequest/FileWritePermissionRequest.js'; +import { NotebookEditPermissionRequest } from './NotebookEditPermissionRequest/NotebookEditPermissionRequest.js'; +import { PowerShellPermissionRequest } from './PowerShellPermissionRequest/PowerShellPermissionRequest.js'; +import { SkillPermissionRequest } from './SkillPermissionRequest/SkillPermissionRequest.js'; +import { WebFetchPermissionRequest } from './WebFetchPermissionRequest/WebFetchPermissionRequest.js'; + +/* eslint-disable @typescript-eslint/no-require-imports */ +const ReviewArtifactTool = feature('REVIEW_ARTIFACT') ? (require('../../tools/ReviewArtifactTool/ReviewArtifactTool.js') as typeof import('../../tools/ReviewArtifactTool/ReviewArtifactTool.js')).ReviewArtifactTool : null; +const ReviewArtifactPermissionRequest = feature('REVIEW_ARTIFACT') ? (require('./ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.js') as typeof import('./ReviewArtifactPermissionRequest/ReviewArtifactPermissionRequest.js')).ReviewArtifactPermissionRequest : null; +const WorkflowTool = feature('WORKFLOW_SCRIPTS') ? (require('../../tools/WorkflowTool/WorkflowTool.js') as typeof import('../../tools/WorkflowTool/WorkflowTool.js')).WorkflowTool : null; +const WorkflowPermissionRequest = feature('WORKFLOW_SCRIPTS') ? (require('../../tools/WorkflowTool/WorkflowPermissionRequest.js') as typeof import('../../tools/WorkflowTool/WorkflowPermissionRequest.js')).WorkflowPermissionRequest : null; +const MonitorTool = feature('MONITOR_TOOL') ? (require('../../tools/MonitorTool/MonitorTool.js') as typeof import('../../tools/MonitorTool/MonitorTool.js')).MonitorTool : null; +const MonitorPermissionRequest = feature('MONITOR_TOOL') ? (require('./MonitorPermissionRequest/MonitorPermissionRequest.js') as typeof import('./MonitorPermissionRequest/MonitorPermissionRequest.js')).MonitorPermissionRequest : null; +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'; +/* eslint-enable @typescript-eslint/no-require-imports */ +import type { z } from 'zod/v4'; +import type { PermissionUpdate } from '../../utils/permissions/PermissionUpdateSchema.js'; +import type { WorkerBadgeProps } from './WorkerBadge.js'; +function permissionComponentForTool(tool: Tool): React.ComponentType { + switch (tool) { + case FileEditTool: + return FileEditPermissionRequest; + case FileWriteTool: + return FileWritePermissionRequest; + case BashTool: + return BashPermissionRequest; + case PowerShellTool: + return PowerShellPermissionRequest; + case ReviewArtifactTool: + return ReviewArtifactPermissionRequest ?? FallbackPermissionRequest; + case WebFetchTool: + return WebFetchPermissionRequest; + case NotebookEditTool: + return NotebookEditPermissionRequest; + case ExitPlanModeV2Tool: + return ExitPlanModePermissionRequest; + case EnterPlanModeTool: + return EnterPlanModePermissionRequest; + case SkillTool: + return SkillPermissionRequest; + case AskUserQuestionTool: + return AskUserQuestionPermissionRequest; + case WorkflowTool: + return WorkflowPermissionRequest ?? FallbackPermissionRequest; + case MonitorTool: + return MonitorPermissionRequest ?? FallbackPermissionRequest; + case GlobTool: + case GrepTool: + case FileReadTool: + return FilesystemPermissionRequest; + default: + return FallbackPermissionRequest; + } +} +export type PermissionRequestProps = { + toolUseConfirm: ToolUseConfirm; + toolUseContext: ToolUseContext; + onDone(): void; + onReject(): void; + verbose: boolean; + workerBadge: WorkerBadgeProps | undefined; + /** + * Register JSX to render in a sticky footer below the scrollable area. + * Fullscreen mode only (non-fullscreen has no sticky area — terminal + * scrollback moves everything together). Call with null to clear. + * + * Used by ExitPlanModePermissionRequest to keep response options visible + * while the user scrolls through a long plan. The callback is stable — + * JSX passed should use refs for callbacks that close over component state + * to avoid stale closures (React reconciles the JSX, preserving Select's + * internal focus/input state). + */ + setStickyFooter?: (jsx: React.ReactNode | null) => void; +}; +export type ToolUseConfirm = { + assistantMessage: AssistantMessage; + tool: Tool; + description: string; + input: z.infer; + toolUseContext: ToolUseContext; + toolUseID: string; + permissionResult: PermissionDecision; + permissionPromptStartTimeMs: number; + /** + * Called when user interacts with the permission dialog (e.g., arrow keys, tab, typing). + * This prevents async auto-approval mechanisms (like the bash classifier) from + * dismissing the dialog while the user is actively engaging with it. + */ + classifierCheckInProgress?: boolean; + classifierAutoApproved?: boolean; + classifierMatchedRule?: string; + workerBadge?: WorkerBadgeProps; + onUserInteraction(): void; + onAbort(): void; + onDismissCheckmark?(): void; + onAllow(updatedInput: z.infer, permissionUpdates: PermissionUpdate[], feedback?: string, contentBlocks?: ContentBlockParam[]): void; + onReject(feedback?: string, contentBlocks?: ContentBlockParam[]): void; + recheckPermission(): Promise; +}; +function getNotificationMessage(toolUseConfirm: ToolUseConfirm): string { + const toolName = toolUseConfirm.tool.userFacingName(toolUseConfirm.input as never); + if (toolUseConfirm.tool === ExitPlanModeV2Tool) { + return 'Claude Code needs your approval for the plan'; + } + if (toolUseConfirm.tool === EnterPlanModeTool) { + return 'Claude Code wants to enter plan mode'; + } + if (feature('REVIEW_ARTIFACT') && toolUseConfirm.tool === ReviewArtifactTool) { + return 'Claude needs your approval for a review artifact'; + } + if (!toolName || toolName.trim() === '') { + return 'Claude Code needs your attention'; + } + return `Claude needs your permission to use ${toolName}`; +} + +// TODO: Move this to Tool.renderPermissionRequest +export function PermissionRequest(t0) { + const $ = _c(18); + const { + toolUseConfirm, + toolUseContext, + onDone, + onReject, + verbose, + workerBadge, + setStickyFooter + } = t0; + let t1; + if ($[0] !== onDone || $[1] !== onReject || $[2] !== toolUseConfirm) { + t1 = () => { + onDone(); + onReject(); + toolUseConfirm.onReject(); + }; + $[0] = onDone; + $[1] = onReject; + $[2] = toolUseConfirm; + $[3] = t1; + } else { + t1 = $[3]; + } + let t2; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t2 = { + context: "Confirmation" + }; + $[4] = t2; + } else { + t2 = $[4]; + } + useKeybinding("app:interrupt", t1, t2); + let t3; + if ($[5] !== toolUseConfirm) { + t3 = getNotificationMessage(toolUseConfirm); + $[5] = toolUseConfirm; + $[6] = t3; + } else { + t3 = $[6]; + } + const notificationMessage = t3; + useNotifyAfterTimeout(notificationMessage, "permission_prompt"); + let t4; + if ($[7] !== toolUseConfirm.tool) { + t4 = permissionComponentForTool(toolUseConfirm.tool); + $[7] = toolUseConfirm.tool; + $[8] = t4; + } else { + t4 = $[8]; + } + const PermissionComponent = t4; + let t5; + if ($[9] !== PermissionComponent || $[10] !== onDone || $[11] !== onReject || $[12] !== setStickyFooter || $[13] !== toolUseConfirm || $[14] !== toolUseContext || $[15] !== verbose || $[16] !== workerBadge) { + t5 = ; + $[9] = PermissionComponent; + $[10] = onDone; + $[11] = onReject; + $[12] = setStickyFooter; + $[13] = toolUseConfirm; + $[14] = toolUseContext; + $[15] = verbose; + $[16] = workerBadge; + $[17] = t5; + } else { + t5 = $[17]; + } + return t5; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJFbnRlclBsYW5Nb2RlVG9vbCIsIkV4aXRQbGFuTW9kZVYyVG9vbCIsInVzZU5vdGlmeUFmdGVyVGltZW91dCIsInVzZUtleWJpbmRpbmciLCJBbnlPYmplY3QiLCJUb29sIiwiVG9vbFVzZUNvbnRleHQiLCJBc2tVc2VyUXVlc3Rpb25Ub29sIiwiQmFzaFRvb2wiLCJGaWxlRWRpdFRvb2wiLCJGaWxlUmVhZFRvb2wiLCJGaWxlV3JpdGVUb29sIiwiR2xvYlRvb2wiLCJHcmVwVG9vbCIsIk5vdGVib29rRWRpdFRvb2wiLCJQb3dlclNoZWxsVG9vbCIsIlNraWxsVG9vbCIsIldlYkZldGNoVG9vbCIsIkFzc2lzdGFudE1lc3NhZ2UiLCJQZXJtaXNzaW9uRGVjaXNpb24iLCJBc2tVc2VyUXVlc3Rpb25QZXJtaXNzaW9uUmVxdWVzdCIsIkJhc2hQZXJtaXNzaW9uUmVxdWVzdCIsIkVudGVyUGxhbk1vZGVQZXJtaXNzaW9uUmVxdWVzdCIsIkV4aXRQbGFuTW9kZVBlcm1pc3Npb25SZXF1ZXN0IiwiRmFsbGJhY2tQZXJtaXNzaW9uUmVxdWVzdCIsIkZpbGVFZGl0UGVybWlzc2lvblJlcXVlc3QiLCJGaWxlc3lzdGVtUGVybWlzc2lvblJlcXVlc3QiLCJGaWxlV3JpdGVQZXJtaXNzaW9uUmVxdWVzdCIsIk5vdGVib29rRWRpdFBlcm1pc3Npb25SZXF1ZXN0IiwiUG93ZXJTaGVsbFBlcm1pc3Npb25SZXF1ZXN0IiwiU2tpbGxQZXJtaXNzaW9uUmVxdWVzdCIsIldlYkZldGNoUGVybWlzc2lvblJlcXVlc3QiLCJSZXZpZXdBcnRpZmFjdFRvb2wiLCJyZXF1aXJlIiwiUmV2aWV3QXJ0aWZhY3RQZXJtaXNzaW9uUmVxdWVzdCIsIldvcmtmbG93VG9vbCIsIldvcmtmbG93UGVybWlzc2lvblJlcXVlc3QiLCJNb25pdG9yVG9vbCIsIk1vbml0b3JQZXJtaXNzaW9uUmVxdWVzdCIsIkNvbnRlbnRCbG9ja1BhcmFtIiwieiIsIlBlcm1pc3Npb25VcGRhdGUiLCJXb3JrZXJCYWRnZVByb3BzIiwicGVybWlzc2lvbkNvbXBvbmVudEZvclRvb2wiLCJ0b29sIiwiQ29tcG9uZW50VHlwZSIsIlBlcm1pc3Npb25SZXF1ZXN0UHJvcHMiLCJ0b29sVXNlQ29uZmlybSIsIlRvb2xVc2VDb25maXJtIiwiSW5wdXQiLCJ0b29sVXNlQ29udGV4dCIsIm9uRG9uZSIsIm9uUmVqZWN0IiwidmVyYm9zZSIsIndvcmtlckJhZGdlIiwic2V0U3RpY2t5Rm9vdGVyIiwianN4IiwiUmVhY3ROb2RlIiwiYXNzaXN0YW50TWVzc2FnZSIsImRlc2NyaXB0aW9uIiwiaW5wdXQiLCJpbmZlciIsInRvb2xVc2VJRCIsInBlcm1pc3Npb25SZXN1bHQiLCJwZXJtaXNzaW9uUHJvbXB0U3RhcnRUaW1lTXMiLCJjbGFzc2lmaWVyQ2hlY2tJblByb2dyZXNzIiwiY2xhc3NpZmllckF1dG9BcHByb3ZlZCIsImNsYXNzaWZpZXJNYXRjaGVkUnVsZSIsIm9uVXNlckludGVyYWN0aW9uIiwib25BYm9ydCIsIm9uRGlzbWlzc0NoZWNrbWFyayIsIm9uQWxsb3ciLCJ1cGRhdGVkSW5wdXQiLCJwZXJtaXNzaW9uVXBkYXRlcyIsImZlZWRiYWNrIiwiY29udGVudEJsb2NrcyIsInJlY2hlY2tQZXJtaXNzaW9uIiwiUHJvbWlzZSIsImdldE5vdGlmaWNhdGlvbk1lc3NhZ2UiLCJ0b29sTmFtZSIsInVzZXJGYWNpbmdOYW1lIiwidHJpbSIsIlBlcm1pc3Npb25SZXF1ZXN0IiwidDAiLCIkIiwiX2MiLCJ0MSIsInQyIiwiU3ltYm9sIiwiZm9yIiwiY29udGV4dCIsInQzIiwibm90aWZpY2F0aW9uTWVzc2FnZSIsInQ0IiwiUGVybWlzc2lvbkNvbXBvbmVudCIsInQ1Il0sInNvdXJjZXMiOlsiUGVybWlzc2lvblJlcXVlc3QudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBFbnRlclBsYW5Nb2RlVG9vbCB9IGZyb20gJ3NyYy90b29scy9FbnRlclBsYW5Nb2RlVG9vbC9FbnRlclBsYW5Nb2RlVG9vbC5qcydcbmltcG9ydCB7IEV4aXRQbGFuTW9kZVYyVG9vbCB9IGZyb20gJ3NyYy90b29scy9FeGl0UGxhbk1vZGVUb29sL0V4aXRQbGFuTW9kZVYyVG9vbC5qcydcbmltcG9ydCB7IHVzZU5vdGlmeUFmdGVyVGltZW91dCB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZU5vdGlmeUFmdGVyVGltZW91dC5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHR5cGUgeyBBbnlPYmplY3QsIFRvb2wsIFRvb2xVc2VDb250ZXh0IH0gZnJvbSAnLi4vLi4vVG9vbC5qcydcbmltcG9ydCB7IEFza1VzZXJRdWVzdGlvblRvb2wgfSBmcm9tICcuLi8uLi90b29scy9Bc2tVc2VyUXVlc3Rpb25Ub29sL0Fza1VzZXJRdWVzdGlvblRvb2wuanMnXG5pbXBvcnQgeyBCYXNoVG9vbCB9IGZyb20gJy4uLy4uL3Rvb2xzL0Jhc2hUb29sL0Jhc2hUb29sLmpzJ1xuaW1wb3J0IHsgRmlsZUVkaXRUb29sIH0gZnJvbSAnLi4vLi4vdG9vbHMvRmlsZUVkaXRUb29sL0ZpbGVFZGl0VG9vbC5qcydcbmltcG9ydCB7IEZpbGVSZWFkVG9vbCB9IGZyb20gJy4uLy4uL3Rvb2xzL0ZpbGVSZWFkVG9vbC9GaWxlUmVhZFRvb2wuanMnXG5pbXBvcnQgeyBGaWxlV3JpdGVUb29sIH0gZnJvbSAnLi4vLi4vdG9vbHMvRmlsZVdyaXRlVG9vbC9GaWxlV3JpdGVUb29sLmpzJ1xuaW1wb3J0IHsgR2xvYlRvb2wgfSBmcm9tICcuLi8uLi90b29scy9HbG9iVG9vbC9HbG9iVG9vbC5qcydcbmltcG9ydCB7IEdyZXBUb29sIH0gZnJvbSAnLi4vLi4vdG9vbHMvR3JlcFRvb2wvR3JlcFRvb2wuanMnXG5pbXBvcnQgeyBOb3RlYm9va0VkaXRUb29sIH0gZnJvbSAnLi4vLi4vdG9vbHMvTm90ZWJvb2tFZGl0VG9vbC9Ob3RlYm9va0VkaXRUb29sLmpzJ1xuaW1wb3J0IHsgUG93ZXJTaGVsbFRvb2wgfSBmcm9tICcuLi8uLi90b29scy9Qb3dlclNoZWxsVG9vbC9Qb3dlclNoZWxsVG9vbC5qcydcbmltcG9ydCB7IFNraWxsVG9vbCB9IGZyb20gJy4uLy4uL3Rvb2xzL1NraWxsVG9vbC9Ta2lsbFRvb2wuanMnXG5pbXBvcnQgeyBXZWJGZXRjaFRvb2wgfSBmcm9tICcuLi8uLi90b29scy9XZWJGZXRjaFRvb2wvV2ViRmV0Y2hUb29sLmpzJ1xuaW1wb3J0IHR5cGUgeyBBc3Npc3RhbnRNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvbkRlY2lzaW9uIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvblJlc3VsdC5qcydcbmltcG9ydCB7IEFza1VzZXJRdWVzdGlvblBlcm1pc3Npb25SZXF1ZXN0IH0gZnJvbSAnLi9Bc2tVc2VyUXVlc3Rpb25QZXJtaXNzaW9uUmVxdWVzdC9Bc2tVc2VyUXVlc3Rpb25QZXJtaXNzaW9uUmVxdWVzdC5qcydcbmltcG9ydCB7IEJhc2hQZXJtaXNzaW9uUmVxdWVzdCB9IGZyb20gJy4vQmFzaFBlcm1pc3Npb25SZXF1ZXN0L0Jhc2hQZXJtaXNzaW9uUmVxdWVzdC5qcydcbmltcG9ydCB7IEVudGVyUGxhbk1vZGVQZXJtaXNzaW9uUmVxdWVzdCB9IGZyb20gJy4vRW50ZXJQbGFuTW9kZVBlcm1pc3Npb25SZXF1ZXN0L0VudGVyUGxhbk1vZGVQZXJtaXNzaW9uUmVxdWVzdC5qcydcbmltcG9ydCB7IEV4aXRQbGFuTW9kZVBlcm1pc3Npb25SZXF1ZXN0IH0gZnJvbSAnLi9FeGl0UGxhbk1vZGVQZXJtaXNzaW9uUmVxdWVzdC9FeGl0UGxhbk1vZGVQZXJtaXNzaW9uUmVxdWVzdC5qcydcbmltcG9ydCB7IEZhbGxiYWNrUGVybWlzc2lvblJlcXVlc3QgfSBmcm9tICcuL0ZhbGxiYWNrUGVybWlzc2lvblJlcXVlc3QuanMnXG5pbXBvcnQgeyBGaWxlRWRpdFBlcm1pc3Npb25SZXF1ZXN0IH0gZnJvbSAnLi9GaWxlRWRpdFBlcm1pc3Npb25SZXF1ZXN0L0ZpbGVFZGl0UGVybWlzc2lvblJlcXVlc3QuanMnXG5pbXBvcnQgeyBGaWxlc3lzdGVtUGVybWlzc2lvblJlcXVlc3QgfSBmcm9tICcuL0ZpbGVzeXN0ZW1QZXJtaXNzaW9uUmVxdWVzdC9GaWxlc3lzdGVtUGVybWlzc2lvblJlcXVlc3QuanMnXG5pbXBvcnQgeyBGaWxlV3JpdGVQZXJtaXNzaW9uUmVxdWVzdCB9IGZyb20gJy4vRmlsZVdyaXRlUGVybWlzc2lvblJlcXVlc3QvRmlsZVdyaXRlUGVybWlzc2lvblJlcXVlc3QuanMnXG5pbXBvcnQgeyBOb3RlYm9va0VkaXRQZXJtaXNzaW9uUmVxdWVzdCB9IGZyb20gJy4vTm90ZWJvb2tFZGl0UGVybWlzc2lvblJlcXVlc3QvTm90ZWJvb2tFZGl0UGVybWlzc2lvblJlcXVlc3QuanMnXG5pbXBvcnQgeyBQb3dlclNoZWxsUGVybWlzc2lvblJlcXVlc3QgfSBmcm9tICcuL1Bvd2VyU2hlbGxQZXJtaXNzaW9uUmVxdWVzdC9Qb3dlclNoZWxsUGVybWlzc2lvblJlcXVlc3QuanMnXG5pbXBvcnQgeyBTa2lsbFBlcm1pc3Npb25SZXF1ZXN0IH0gZnJvbSAnLi9Ta2lsbFBlcm1pc3Npb25SZXF1ZXN0L1NraWxsUGVybWlzc2lvblJlcXVlc3QuanMnXG5pbXBvcnQgeyBXZWJGZXRjaFBlcm1pc3Npb25SZXF1ZXN0IH0gZnJvbSAnLi9XZWJGZXRjaFBlcm1pc3Npb25SZXF1ZXN0L1dlYkZldGNoUGVybWlzc2lvblJlcXVlc3QuanMnXG5cbi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbmNvbnN0IFJldmlld0FydGlmYWN0VG9vbCA9IGZlYXR1cmUoJ1JFVklFV19BUlRJRkFDVCcpXG4gID8gKFxuICAgICAgcmVxdWlyZSgnLi4vLi4vdG9vbHMvUmV2aWV3QXJ0aWZhY3RUb29sL1Jldmlld0FydGlmYWN0VG9vbC5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4uLy4uL3Rvb2xzL1Jldmlld0FydGlmYWN0VG9vbC9SZXZpZXdBcnRpZmFjdFRvb2wuanMnKVxuICAgICkuUmV2aWV3QXJ0aWZhY3RUb29sXG4gIDogbnVsbFxuXG5jb25zdCBSZXZpZXdBcnRpZmFjdFBlcm1pc3Npb25SZXF1ZXN0ID0gZmVhdHVyZSgnUkVWSUVXX0FSVElGQUNUJylcbiAgPyAoXG4gICAgICByZXF1aXJlKCcuL1Jldmlld0FydGlmYWN0UGVybWlzc2lvblJlcXVlc3QvUmV2aWV3QXJ0aWZhY3RQZXJtaXNzaW9uUmVxdWVzdC5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vUmV2aWV3QXJ0aWZhY3RQZXJtaXNzaW9uUmVxdWVzdC9SZXZpZXdBcnRpZmFjdFBlcm1pc3Npb25SZXF1ZXN0LmpzJylcbiAgICApLlJldmlld0FydGlmYWN0UGVybWlzc2lvblJlcXVlc3RcbiAgOiBudWxsXG5cbmNvbnN0IFdvcmtmbG93VG9vbCA9IGZlYXR1cmUoJ1dPUktGTE9XX1NDUklQVFMnKVxuICA/IChcbiAgICAgIHJlcXVpcmUoJy4uLy4uL3Rvb2xzL1dvcmtmbG93VG9vbC9Xb3JrZmxvd1Rvb2wuanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuLi8uLi90b29scy9Xb3JrZmxvd1Rvb2wvV29ya2Zsb3dUb29sLmpzJylcbiAgICApLldvcmtmbG93VG9vbFxuICA6IG51bGxcblxuY29uc3QgV29ya2Zsb3dQZXJtaXNzaW9uUmVxdWVzdCA9IGZlYXR1cmUoJ1dPUktGTE9XX1NDUklQVFMnKVxuICA/IChcbiAgICAgIHJlcXVpcmUoJy4uLy4uL3Rvb2xzL1dvcmtmbG93VG9vbC9Xb3JrZmxvd1Blcm1pc3Npb25SZXF1ZXN0LmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vdG9vbHMvV29ya2Zsb3dUb29sL1dvcmtmbG93UGVybWlzc2lvblJlcXVlc3QuanMnKVxuICAgICkuV29ya2Zsb3dQZXJtaXNzaW9uUmVxdWVzdFxuICA6IG51bGxcblxuY29uc3QgTW9uaXRvclRvb2wgPSBmZWF0dXJlKCdNT05JVE9SX1RPT0wnKVxuICA/IChcbiAgICAgIHJlcXVpcmUoJy4uLy4uL3Rvb2xzL01vbml0b3JUb29sL01vbml0b3JUb29sLmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi4vLi4vdG9vbHMvTW9uaXRvclRvb2wvTW9uaXRvclRvb2wuanMnKVxuICAgICkuTW9uaXRvclRvb2xcbiAgOiBudWxsXG5cbmNvbnN0IE1vbml0b3JQZXJtaXNzaW9uUmVxdWVzdCA9IGZlYXR1cmUoJ01PTklUT1JfVE9PTCcpXG4gID8gKFxuICAgICAgcmVxdWlyZSgnLi9Nb25pdG9yUGVybWlzc2lvblJlcXVlc3QvTW9uaXRvclBlcm1pc3Npb25SZXF1ZXN0LmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi9Nb25pdG9yUGVybWlzc2lvblJlcXVlc3QvTW9uaXRvclBlcm1pc3Npb25SZXF1ZXN0LmpzJylcbiAgICApLk1vbml0b3JQZXJtaXNzaW9uUmVxdWVzdFxuICA6IG51bGxcblxuaW1wb3J0IHR5cGUgeyBDb250ZW50QmxvY2tQYXJhbSB9IGZyb20gJ0BhbnRocm9waWMtYWkvc2RrL3Jlc291cmNlcy9tZXNzYWdlcy5tanMnXG4vKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbmltcG9ydCB0eXBlIHsgeiB9IGZyb20gJ3pvZC92NCdcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvblVwZGF0ZSB9IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25VcGRhdGVTY2hlbWEuanMnXG5pbXBvcnQgdHlwZSB7IFdvcmtlckJhZGdlUHJvcHMgfSBmcm9tICcuL1dvcmtlckJhZGdlLmpzJ1xuXG5mdW5jdGlvbiBwZXJtaXNzaW9uQ29tcG9uZW50Rm9yVG9vbChcbiAgdG9vbDogVG9vbCxcbik6IFJlYWN0LkNvbXBvbmVudFR5cGU8UGVybWlzc2lvblJlcXVlc3RQcm9wcz4ge1xuICBzd2l0Y2ggKHRvb2wpIHtcbiAgICBjYXNlIEZpbGVFZGl0VG9vbDpcbiAgICAgIHJldHVybiBGaWxlRWRpdFBlcm1pc3Npb25SZXF1ZXN0XG4gICAgY2FzZSBGaWxlV3JpdGVUb29sOlxuICAgICAgcmV0dXJuIEZpbGVXcml0ZVBlcm1pc3Npb25SZXF1ZXN0XG4gICAgY2FzZSBCYXNoVG9vbDpcbiAgICAgIHJldHVybiBCYXNoUGVybWlzc2lvblJlcXVlc3RcbiAgICBjYXNlIFBvd2VyU2hlbGxUb29sOlxuICAgICAgcmV0dXJuIFBvd2VyU2hlbGxQZXJtaXNzaW9uUmVxdWVzdFxuICAgIGNhc2UgUmV2aWV3QXJ0aWZhY3RUb29sOlxuICAgICAgcmV0dXJuIFJldmlld0FydGlmYWN0UGVybWlzc2lvblJlcXVlc3QgPz8gRmFsbGJhY2tQZXJtaXNzaW9uUmVxdWVzdFxuICAgIGNhc2UgV2ViRmV0Y2hUb29sOlxuICAgICAgcmV0dXJuIFdlYkZldGNoUGVybWlzc2lvblJlcXVlc3RcbiAgICBjYXNlIE5vdGVib29rRWRpdFRvb2w6XG4gICAgICByZXR1cm4gTm90ZWJvb2tFZGl0UGVybWlzc2lvblJlcXVlc3RcbiAgICBjYXNlIEV4aXRQbGFuTW9kZVYyVG9vbDpcbiAgICAgIHJldHVybiBFeGl0UGxhbk1vZGVQZXJtaXNzaW9uUmVxdWVzdFxuICAgIGNhc2UgRW50ZXJQbGFuTW9kZVRvb2w6XG4gICAgICByZXR1cm4gRW50ZXJQbGFuTW9kZVBlcm1pc3Npb25SZXF1ZXN0XG4gICAgY2FzZSBTa2lsbFRvb2w6XG4gICAgICByZXR1cm4gU2tpbGxQZXJtaXNzaW9uUmVxdWVzdFxuICAgIGNhc2UgQXNrVXNlclF1ZXN0aW9uVG9vbDpcbiAgICAgIHJldHVybiBBc2tVc2VyUXVlc3Rpb25QZXJtaXNzaW9uUmVxdWVzdFxuICAgIGNhc2UgV29ya2Zsb3dUb29sOlxuICAgICAgcmV0dXJuIFdvcmtmbG93UGVybWlzc2lvblJlcXVlc3QgPz8gRmFsbGJhY2tQZXJtaXNzaW9uUmVxdWVzdFxuICAgIGNhc2UgTW9uaXRvclRvb2w6XG4gICAgICByZXR1cm4gTW9uaXRvclBlcm1pc3Npb25SZXF1ZXN0ID8/IEZhbGxiYWNrUGVybWlzc2lvblJlcXVlc3RcbiAgICBjYXNlIEdsb2JUb29sOlxuICAgIGNhc2UgR3JlcFRvb2w6XG4gICAgY2FzZSBGaWxlUmVhZFRvb2w6XG4gICAgICByZXR1cm4gRmlsZXN5c3RlbVBlcm1pc3Npb25SZXF1ZXN0XG4gICAgZGVmYXVsdDpcbiAgICAgIHJldHVybiBGYWxsYmFja1Blcm1pc3Npb25SZXF1ZXN0XG4gIH1cbn1cblxuZXhwb3J0IHR5cGUgUGVybWlzc2lvblJlcXVlc3RQcm9wczxJbnB1dCBleHRlbmRzIEFueU9iamVjdCA9IEFueU9iamVjdD4gPSB7XG4gIHRvb2xVc2VDb25maXJtOiBUb29sVXNlQ29uZmlybTxJbnB1dD5cbiAgdG9vbFVzZUNvbnRleHQ6IFRvb2xVc2VDb250ZXh0XG4gIG9uRG9uZSgpOiB2b2lkXG4gIG9uUmVqZWN0KCk6IHZvaWRcbiAgdmVyYm9zZTogYm9vbGVhblxuICB3b3JrZXJCYWRnZTogV29ya2VyQmFkZ2VQcm9wcyB8IHVuZGVmaW5lZFxuICAvKipcbiAgICogUmVnaXN0ZXIgSlNYIHRvIHJlbmRlciBpbiBhIHN0aWNreSBmb290ZXIgYmVsb3cgdGhlIHNjcm9sbGFibGUgYXJlYS5cbiAgICogRnVsbHNjcmVlbiBtb2RlIG9ubHkgKG5vbi1mdWxsc2NyZWVuIGhhcyBubyBzdGlja3kgYXJlYSDigJQgdGVybWluYWxcbiAgICogc2Nyb2xsYmFjayBtb3ZlcyBldmVyeXRoaW5nIHRvZ2V0aGVyKS4gQ2FsbCB3aXRoIG51bGwgdG8gY2xlYXIuXG4gICAqXG4gICAqIFVzZWQgYnkgRXhpdFBsYW5Nb2RlUGVybWlzc2lvblJlcXVlc3QgdG8ga2VlcCByZXNwb25zZSBvcHRpb25zIHZpc2libGVcbiAgICogd2hpbGUgdGhlIHVzZXIgc2Nyb2xscyB0aHJvdWdoIGEgbG9uZyBwbGFuLiBUaGUgY2FsbGJhY2sgaXMgc3RhYmxlIOKAlFxuICAgKiBKU1ggcGFzc2VkIHNob3VsZCB1c2UgcmVmcyBmb3IgY2FsbGJhY2tzIHRoYXQgY2xvc2Ugb3ZlciBjb21wb25lbnQgc3RhdGVcbiAgICogdG8gYXZvaWQgc3RhbGUgY2xvc3VyZXMgKFJlYWN0IHJlY29uY2lsZXMgdGhlIEpTWCwgcHJlc2VydmluZyBTZWxlY3Qnc1xuICAgKiBpbnRlcm5hbCBmb2N1cy9pbnB1dCBzdGF0ZSkuXG4gICAqL1xuICBzZXRTdGlja3lGb290ZXI/OiAoanN4OiBSZWFjdC5SZWFjdE5vZGUgfCBudWxsKSA9PiB2b2lkXG59XG5cbmV4cG9ydCB0eXBlIFRvb2xVc2VDb25maXJtPElucHV0IGV4dGVuZHMgQW55T2JqZWN0ID0gQW55T2JqZWN0PiA9IHtcbiAgYXNzaXN0YW50TWVzc2FnZTogQXNzaXN0YW50TWVzc2FnZVxuICB0b29sOiBUb29sPElucHV0PlxuICBkZXNjcmlwdGlvbjogc3RyaW5nXG4gIGlucHV0OiB6LmluZmVyPElucHV0PlxuICB0b29sVXNlQ29udGV4dDogVG9vbFVzZUNvbnRleHRcbiAgdG9vbFVzZUlEOiBzdHJpbmdcbiAgcGVybWlzc2lvblJlc3VsdDogUGVybWlzc2lvbkRlY2lzaW9uXG4gIHBlcm1pc3Npb25Qcm9tcHRTdGFydFRpbWVNczogbnVtYmVyXG4gIC8qKlxuICAgKiBDYWxsZWQgd2hlbiB1c2VyIGludGVyYWN0cyB3aXRoIHRoZSBwZXJtaXNzaW9uIGRpYWxvZyAoZS5nLiwgYXJyb3cga2V5cywgdGFiLCB0eXBpbmcpLlxuICAgKiBUaGlzIHByZXZlbnRzIGFzeW5jIGF1dG8tYXBwcm92YWwgbWVjaGFuaXNtcyAobGlrZSB0aGUgYmFzaCBjbGFzc2lmaWVyKSBmcm9tXG4gICAqIGRpc21pc3NpbmcgdGhlIGRpYWxvZyB3aGlsZSB0aGUgdXNlciBpcyBhY3RpdmVseSBlbmdhZ2luZyB3aXRoIGl0LlxuICAgKi9cbiAgY2xhc3NpZmllckNoZWNrSW5Qcm9ncmVzcz86IGJvb2xlYW5cbiAgY2xhc3NpZmllckF1dG9BcHByb3ZlZD86IGJvb2xlYW5cbiAgY2xhc3NpZmllck1hdGNoZWRSdWxlPzogc3RyaW5nXG4gIHdvcmtlckJhZGdlPzogV29ya2VyQmFkZ2VQcm9wc1xuICBvblVzZXJJbnRlcmFjdGlvbigpOiB2b2lkXG4gIG9uQWJvcnQoKTogdm9pZFxuICBvbkRpc21pc3NDaGVja21hcms/KCk6IHZvaWRcbiAgb25BbGxvdyhcbiAgICB1cGRhdGVkSW5wdXQ6IHouaW5mZXI8SW5wdXQ+LFxuICAgIHBlcm1pc3Npb25VcGRhdGVzOiBQZXJtaXNzaW9uVXBkYXRlW10sXG4gICAgZmVlZGJhY2s/OiBzdHJpbmcsXG4gICAgY29udGVudEJsb2Nrcz86IENvbnRlbnRCbG9ja1BhcmFtW10sXG4gICk6IHZvaWRcbiAgb25SZWplY3QoZmVlZGJhY2s/OiBzdHJpbmcsIGNvbnRlbnRCbG9ja3M/OiBDb250ZW50QmxvY2tQYXJhbVtdKTogdm9pZFxuICByZWNoZWNrUGVybWlzc2lvbigpOiBQcm9taXNlPHZvaWQ+XG59XG5cbmZ1bmN0aW9uIGdldE5vdGlmaWNhdGlvbk1lc3NhZ2UodG9vbFVzZUNvbmZpcm06IFRvb2xVc2VDb25maXJtKTogc3RyaW5nIHtcbiAgY29uc3QgdG9vbE5hbWUgPSB0b29sVXNlQ29uZmlybS50b29sLnVzZXJGYWNpbmdOYW1lKFxuICAgIHRvb2xVc2VDb25maXJtLmlucHV0IGFzIG5ldmVyLFxuICApXG5cbiAgaWYgKHRvb2xVc2VDb25maXJtLnRvb2wgPT09IEV4aXRQbGFuTW9kZVYyVG9vbCkge1xuICAgIHJldHVybiAnQ2xhdWRlIENvZGUgbmVlZHMgeW91ciBhcHByb3ZhbCBmb3IgdGhlIHBsYW4nXG4gIH1cblxuICBpZiAodG9vbFVzZUNvbmZpcm0udG9vbCA9PT0gRW50ZXJQbGFuTW9kZVRvb2wpIHtcbiAgICByZXR1cm4gJ0NsYXVkZSBDb2RlIHdhbnRzIHRvIGVudGVyIHBsYW4gbW9kZSdcbiAgfVxuXG4gIGlmIChcbiAgICBmZWF0dXJlKCdSRVZJRVdfQVJUSUZBQ1QnKSAmJlxuICAgIHRvb2xVc2VDb25maXJtLnRvb2wgPT09IFJldmlld0FydGlmYWN0VG9vbFxuICApIHtcbiAgICByZXR1cm4gJ0NsYXVkZSBuZWVkcyB5b3VyIGFwcHJvdmFsIGZvciBhIHJldmlldyBhcnRpZmFjdCdcbiAgfVxuXG4gIGlmICghdG9vbE5hbWUgfHwgdG9vbE5hbWUudHJpbSgpID09PSAnJykge1xuICAgIHJldHVybiAnQ2xhdWRlIENvZGUgbmVlZHMgeW91ciBhdHRlbnRpb24nXG4gIH1cblxuICByZXR1cm4gYENsYXVkZSBuZWVkcyB5b3VyIHBlcm1pc3Npb24gdG8gdXNlICR7dG9vbE5hbWV9YFxufVxuXG4vLyBUT0RPOiBNb3ZlIHRoaXMgdG8gVG9vbC5yZW5kZXJQZXJtaXNzaW9uUmVxdWVzdFxuZXhwb3J0IGZ1bmN0aW9uIFBlcm1pc3Npb25SZXF1ZXN0KHtcbiAgdG9vbFVzZUNvbmZpcm0sXG4gIHRvb2xVc2VDb250ZXh0LFxuICBvbkRvbmUsXG4gIG9uUmVqZWN0LFxuICB2ZXJib3NlLFxuICB3b3JrZXJCYWRnZSxcbiAgc2V0U3RpY2t5Rm9vdGVyLFxufTogUGVybWlzc2lvblJlcXVlc3RQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIEhhbmRsZSBDdHJsK0MgKGFwcDppbnRlcnJ1cHQpIHRvIHJlamVjdFxuICB1c2VLZXliaW5kaW5nKFxuICAgICdhcHA6aW50ZXJydXB0JyxcbiAgICAoKSA9PiB7XG4gICAgICBvbkRvbmUoKVxuICAgICAgb25SZWplY3QoKVxuICAgICAgdG9vbFVzZUNvbmZpcm0ub25SZWplY3QoKVxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJyB9LFxuICApXG5cbiAgY29uc3Qgbm90aWZpY2F0aW9uTWVzc2FnZSA9IGdldE5vdGlmaWNhdGlvbk1lc3NhZ2UodG9vbFVzZUNvbmZpcm0pXG4gIHVzZU5vdGlmeUFmdGVyVGltZW91dChub3RpZmljYXRpb25NZXNzYWdlLCAncGVybWlzc2lvbl9wcm9tcHQnKVxuXG4gIGNvbnN0IFBlcm1pc3Npb25Db21wb25lbnQgPSBwZXJtaXNzaW9uQ29tcG9uZW50Rm9yVG9vbCh0b29sVXNlQ29uZmlybS50b29sKVxuXG4gIHJldHVybiAoXG4gICAgPFBlcm1pc3Npb25Db21wb25lbnRcbiAgICAgIHRvb2xVc2VDb250ZXh0PXt0b29sVXNlQ29udGV4dH1cbiAgICAgIHRvb2xVc2VDb25maXJtPXt0b29sVXNlQ29uZmlybX1cbiAgICAgIG9uRG9uZT17b25Eb25lfVxuICAgICAgb25SZWplY3Q9e29uUmVqZWN0fVxuICAgICAgdmVyYm9zZT17dmVyYm9zZX1cbiAgICAgIHdvcmtlckJhZGdlPXt3b3JrZXJCYWRnZX1cbiAgICAgIHNldFN0aWNreUZvb3Rlcj17c2V0U3RpY2t5Rm9vdGVyfVxuICAgIC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLE9BQU8sUUFBUSxZQUFZO0FBQ3BDLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsaUJBQWlCLFFBQVEsa0RBQWtEO0FBQ3BGLFNBQVNDLGtCQUFrQixRQUFRLGtEQUFrRDtBQUNyRixTQUFTQyxxQkFBcUIsUUFBUSxzQ0FBc0M7QUFDNUUsU0FBU0MsYUFBYSxRQUFRLG9DQUFvQztBQUNsRSxjQUFjQyxTQUFTLEVBQUVDLElBQUksRUFBRUMsY0FBYyxRQUFRLGVBQWU7QUFDcEUsU0FBU0MsbUJBQW1CLFFBQVEsd0RBQXdEO0FBQzVGLFNBQVNDLFFBQVEsUUFBUSxrQ0FBa0M7QUFDM0QsU0FBU0MsWUFBWSxRQUFRLDBDQUEwQztBQUN2RSxTQUFTQyxZQUFZLFFBQVEsMENBQTBDO0FBQ3ZFLFNBQVNDLGFBQWEsUUFBUSw0Q0FBNEM7QUFDMUUsU0FBU0MsUUFBUSxRQUFRLGtDQUFrQztBQUMzRCxTQUFTQyxRQUFRLFFBQVEsa0NBQWtDO0FBQzNELFNBQVNDLGdCQUFnQixRQUFRLGtEQUFrRDtBQUNuRixTQUFTQyxjQUFjLFFBQVEsOENBQThDO0FBQzdF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 \ No newline at end of file diff --git a/components/permissions/PermissionRequestTitle.tsx b/components/permissions/PermissionRequestTitle.tsx new file mode 100644 index 0000000..f93b6ff --- /dev/null +++ b/components/permissions/PermissionRequestTitle.tsx @@ -0,0 +1,66 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Box, Text } from '../../ink.js'; +import type { Theme } from '../../utils/theme.js'; +import type { WorkerBadgeProps } from './WorkerBadge.js'; +type Props = { + title: string; + subtitle?: React.ReactNode; + color?: keyof Theme; + workerBadge?: WorkerBadgeProps; +}; +export function PermissionRequestTitle(t0) { + const $ = _c(13); + const { + title, + subtitle, + color: t1, + workerBadge + } = t0; + const color = t1 === undefined ? "permission" : t1; + let t2; + if ($[0] !== color || $[1] !== title) { + t2 = {title}; + $[0] = color; + $[1] = title; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] !== workerBadge) { + t3 = workerBadge && {"\xB7 "}@{workerBadge.name}; + $[3] = workerBadge; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== t2 || $[6] !== t3) { + t4 = {t2}{t3}; + $[5] = t2; + $[6] = t3; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] !== subtitle) { + t5 = subtitle != null && (typeof subtitle === "string" ? {subtitle} : subtitle); + $[8] = subtitle; + $[9] = t5; + } else { + t5 = $[9]; + } + let t6; + if ($[10] !== t4 || $[11] !== t5) { + t6 = {t4}{t5}; + $[10] = t4; + $[11] = t5; + $[12] = t6; + } else { + t6 = $[12]; + } + return t6; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJUaGVtZSIsIldvcmtlckJhZGdlUHJvcHMiLCJQcm9wcyIsInRpdGxlIiwic3VidGl0bGUiLCJSZWFjdE5vZGUiLCJjb2xvciIsIndvcmtlckJhZGdlIiwiUGVybWlzc2lvblJlcXVlc3RUaXRsZSIsInQwIiwiJCIsIl9jIiwidDEiLCJ1bmRlZmluZWQiLCJ0MiIsInQzIiwibmFtZSIsInQ0IiwidDUiLCJ0NiJdLCJzb3VyY2VzIjpbIlBlcm1pc3Npb25SZXF1ZXN0VGl0bGUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJy4uLy4uL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHR5cGUgeyBXb3JrZXJCYWRnZVByb3BzIH0gZnJvbSAnLi9Xb3JrZXJCYWRnZS5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgdGl0bGU6IHN0cmluZ1xuICBzdWJ0aXRsZT86IFJlYWN0LlJlYWN0Tm9kZVxuICBjb2xvcj86IGtleW9mIFRoZW1lXG4gIHdvcmtlckJhZGdlPzogV29ya2VyQmFkZ2VQcm9wc1xufVxuXG5leHBvcnQgZnVuY3Rpb24gUGVybWlzc2lvblJlcXVlc3RUaXRsZSh7XG4gIHRpdGxlLFxuICBzdWJ0aXRsZSxcbiAgY29sb3IgPSAncGVybWlzc2lvbicsXG4gIHdvcmtlckJhZGdlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgZ2FwPXsxfT5cbiAgICAgICAgPFRleHQgYm9sZCBjb2xvcj17Y29sb3J9PlxuICAgICAgICAgIHt0aXRsZX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICB7d29ya2VyQmFkZ2UgJiYgKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgeyfCtyAnfUB7d29ya2VyQmFkZ2UubmFtZX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICA8L0JveD5cbiAgICAgIHtzdWJ0aXRsZSAhPSBudWxsICYmXG4gICAgICAgICh0eXBlb2Ygc3VidGl0bGUgPT09ICdzdHJpbmcnID8gKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yIHdyYXA9XCJ0cnVuY2F0ZS1zdGFydFwiPlxuICAgICAgICAgICAge3N1YnRpdGxlfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICBzdWJ0aXRsZVxuICAgICAgICApKX1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLGNBQWNDLEtBQUssUUFBUSxzQkFBc0I7QUFDakQsY0FBY0MsZ0JBQWdCLFFBQVEsa0JBQWtCO0FBRXhELEtBQUtDLEtBQUssR0FBRztFQUNYQyxLQUFLLEVBQUUsTUFBTTtFQUNiQyxRQUFRLENBQUMsRUFBRVAsS0FBSyxDQUFDUSxTQUFTO0VBQzFCQyxLQUFLLENBQUMsRUFBRSxNQUFNTixLQUFLO0VBQ25CTyxXQUFXLENBQUMsRUFBRU4sZ0JBQWdCO0FBQ2hDLENBQUM7QUFFRCxPQUFPLFNBQUFPLHVCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWdDO0lBQUFSLEtBQUE7SUFBQUMsUUFBQTtJQUFBRSxLQUFBLEVBQUFNLEVBQUE7SUFBQUw7RUFBQSxJQUFBRSxFQUsvQjtFQUZOLE1BQUFILEtBQUEsR0FBQU0sRUFBb0IsS0FBcEJDLFNBQW9CLEdBQXBCLFlBQW9CLEdBQXBCRCxFQUFvQjtFQUFBLElBQUFFLEVBQUE7RUFBQSxJQUFBSixDQUFBLFFBQUFKLEtBQUEsSUFBQUksQ0FBQSxRQUFBUCxLQUFBO0lBTWRXLEVBQUEsSUFBQyxJQUFJLENBQUMsSUFBSSxDQUFKLEtBQUcsQ0FBQyxDQUFRUixLQUFLLENBQUxBLE1BQUksQ0FBQyxDQUNwQkgsTUFBSSxDQUNQLEVBRkMsSUFBSSxDQUVFO0lBQUFPLENBQUEsTUFBQUosS0FBQTtJQUFBSSxDQUFBLE1BQUFQLEtBQUE7SUFBQU8sQ0FBQSxNQUFBSSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSixDQUFBO0VBQUE7RUFBQSxJQUFBSyxFQUFBO0VBQUEsSUFBQUwsQ0FBQSxRQUFBSCxXQUFBO0lBQ05RLEVBQUEsR0FBQVIsV0FJQSxJQUhDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FDWCxRQUFHLENBQUUsQ0FBRSxDQUFBQSxXQUFXLENBQUFTLElBQUksQ0FDekIsRUFGQyxJQUFJLENBR047SUFBQU4sQ0FBQSxNQUFBSCxXQUFBO0lBQUFHLENBQUEsTUFBQUssRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUwsQ0FBQTtFQUFBO0VBQUEsSUFBQU8sRUFBQTtFQUFBLElBQUFQLENBQUEsUUFBQUksRUFBQSxJQUFBSixDQUFBLFFBQUFLLEVBQUE7SUFSSEUsRUFBQSxJQUFDLEdBQUcsQ0FBZSxhQUFLLENBQUwsS0FBSyxDQUFNLEdBQUMsQ0FBRCxHQUFDLENBQzdCLENBQUFILEVBRU0sQ0FDTCxDQUFBQyxFQUlELENBQ0YsRUFUQyxHQUFHLENBU0U7SUFBQUwsQ0FBQSxNQUFBSSxFQUFBO0lBQUFKLENBQUEsTUFBQUssRUFBQTtJQUFBTCxDQUFBLE1BQUFPLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFQLENBQUE7RUFBQTtFQUFBLElBQUFRLEVBQUE7RUFBQSxJQUFBUixDQUFBLFFBQUFOLFFBQUE7SUFDTGMsRUFBQSxHQUFBZCxRQUFRLElBQUksSUFPVCxLQU5ELE9BQU9BLFFBQVEsS0FBSyxRQU1wQixHQUxDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBTSxJQUFnQixDQUFoQixnQkFBZ0IsQ0FDakNBLFNBQU8sQ0FDVixFQUZDLElBQUksQ0FLTixHQU5BQSxRQU1DO0lBQUFNLENBQUEsTUFBQU4sUUFBQTtJQUFBTSxDQUFBLE1BQUFRLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFSLENBQUE7RUFBQTtFQUFBLElBQUFTLEVBQUE7RUFBQSxJQUFBVCxDQUFBLFNBQUFPLEVBQUEsSUFBQVAsQ0FBQSxTQUFBUSxFQUFBO0lBbEJOQyxFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQ3pCLENBQUFGLEVBU0ssQ0FDSixDQUFBQyxFQU9FLENBQ0wsRUFuQkMsR0FBRyxDQW1CRTtJQUFBUixDQUFBLE9BQUFPLEVBQUE7SUFBQVAsQ0FBQSxPQUFBUSxFQUFBO0lBQUFSLENBQUEsT0FBQVMsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVQsQ0FBQTtFQUFBO0VBQUEsT0FuQk5TLEVBbUJNO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/permissions/PermissionRuleExplanation.tsx b/components/permissions/PermissionRuleExplanation.tsx new file mode 100644 index 0000000..97ff194 --- /dev/null +++ b/components/permissions/PermissionRuleExplanation.tsx @@ -0,0 +1,121 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import chalk from 'chalk'; +import React from 'react'; +import { Ansi, Box, Text } from '../../ink.js'; +import { useAppState } from '../../state/AppState.js'; +import type { PermissionDecision, PermissionDecisionReason } from '../../utils/permissions/PermissionResult.js'; +import { permissionRuleValueToString } from '../../utils/permissions/permissionRuleParser.js'; +import type { Theme } from '../../utils/theme.js'; +import ThemedText from '../design-system/ThemedText.js'; +export type PermissionRuleExplanationProps = { + permissionResult: PermissionDecision; + toolType: 'tool' | 'command' | 'edit' | 'read'; +}; +type DecisionReasonStrings = { + reasonString: string; + configString?: string; + /** When set, reasonString is plain text rendered with this theme color instead of . */ + themeColor?: keyof Theme; +}; +function stringsForDecisionReason(reason: PermissionDecisionReason | undefined, toolType: 'tool' | 'command' | 'edit' | 'read'): DecisionReasonStrings | null { + if (!reason) { + return null; + } + if ((feature('BASH_CLASSIFIER') || feature('TRANSCRIPT_CLASSIFIER')) && reason.type === 'classifier') { + if (reason.classifier === 'auto-mode') { + return { + reasonString: `Auto mode classifier requires confirmation for this ${toolType}.\n${reason.reason}`, + configString: undefined, + themeColor: 'error' + }; + } + return { + reasonString: `Classifier ${chalk.bold(reason.classifier)} requires confirmation for this ${toolType}.\n${reason.reason}`, + configString: undefined + }; + } + switch (reason.type) { + case 'rule': + return { + reasonString: `Permission rule ${chalk.bold(permissionRuleValueToString(reason.rule.ruleValue))} requires confirmation for this ${toolType}.`, + configString: reason.rule.source === 'policySettings' ? undefined : '/permissions to update rules' + }; + case 'hook': + { + const hookReasonString = reason.reason ? `:\n${reason.reason}` : '.'; + const sourceLabel = reason.hookSource ? ` ${chalk.dim(`[${reason.hookSource}]`)}` : ''; + return { + reasonString: `Hook ${chalk.bold(reason.hookName)} requires confirmation for this ${toolType}${hookReasonString}${sourceLabel}`, + configString: '/hooks to update' + }; + } + case 'safetyCheck': + case 'other': + return { + reasonString: reason.reason, + configString: undefined + }; + case 'workingDir': + return { + reasonString: reason.reason, + configString: '/permissions to update rules' + }; + default: + return null; + } +} +export function PermissionRuleExplanation(t0) { + const $ = _c(11); + const { + permissionResult, + toolType + } = t0; + const permissionMode = useAppState(_temp); + const t1 = permissionResult?.decisionReason; + let t2; + if ($[0] !== t1 || $[1] !== toolType) { + t2 = stringsForDecisionReason(t1, toolType); + $[0] = t1; + $[1] = toolType; + $[2] = t2; + } else { + t2 = $[2]; + } + const strings = t2; + if (!strings) { + return null; + } + const themeColor = strings.themeColor ?? (permissionResult?.decisionReason?.type === "hook" && permissionMode === "auto" ? "warning" : undefined); + let t3; + if ($[3] !== strings.reasonString || $[4] !== themeColor) { + t3 = themeColor ? {strings.reasonString} : {strings.reasonString}; + $[3] = strings.reasonString; + $[4] = themeColor; + $[5] = t3; + } else { + t3 = $[5]; + } + let t4; + if ($[6] !== strings.configString) { + t4 = strings.configString && {strings.configString}; + $[6] = strings.configString; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] !== t3 || $[9] !== t4) { + t5 = {t3}{t4}; + $[8] = t3; + $[9] = t4; + $[10] = t5; + } else { + t5 = $[10]; + } + return t5; +} +function _temp(s) { + return s.toolPermissionContext.mode; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiY2hhbGsiLCJSZWFjdCIsIkFuc2kiLCJCb3giLCJUZXh0IiwidXNlQXBwU3RhdGUiLCJQZXJtaXNzaW9uRGVjaXNpb24iLCJQZXJtaXNzaW9uRGVjaXNpb25SZWFzb24iLCJwZXJtaXNzaW9uUnVsZVZhbHVlVG9TdHJpbmciLCJUaGVtZSIsIlRoZW1lZFRleHQiLCJQZXJtaXNzaW9uUnVsZUV4cGxhbmF0aW9uUHJvcHMiLCJwZXJtaXNzaW9uUmVzdWx0IiwidG9vbFR5cGUiLCJEZWNpc2lvblJlYXNvblN0cmluZ3MiLCJyZWFzb25TdHJpbmciLCJjb25maWdTdHJpbmciLCJ0aGVtZUNvbG9yIiwic3RyaW5nc0ZvckRlY2lzaW9uUmVhc29uIiwicmVhc29uIiwidHlwZSIsImNsYXNzaWZpZXIiLCJ1bmRlZmluZWQiLCJib2xkIiwicnVsZSIsInJ1bGVWYWx1ZSIsInNvdXJjZSIsImhvb2tSZWFzb25TdHJpbmciLCJzb3VyY2VMYWJlbCIsImhvb2tTb3VyY2UiLCJkaW0iLCJob29rTmFtZSIsIlBlcm1pc3Npb25SdWxlRXhwbGFuYXRpb24iLCJ0MCIsIiQiLCJfYyIsInBlcm1pc3Npb25Nb2RlIiwiX3RlbXAiLCJ0MSIsImRlY2lzaW9uUmVhc29uIiwidDIiLCJzdHJpbmdzIiwidDMiLCJ0NCIsInQ1IiwicyIsInRvb2xQZXJtaXNzaW9uQ29udGV4dCIsIm1vZGUiXSwic291cmNlcyI6WyJQZXJtaXNzaW9uUnVsZUV4cGxhbmF0aW9uLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCBjaGFsayBmcm9tICdjaGFsaydcbmltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEFuc2ksIEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlIH0gZnJvbSAnLi4vLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7XG4gIFBlcm1pc3Npb25EZWNpc2lvbixcbiAgUGVybWlzc2lvbkRlY2lzaW9uUmVhc29uLFxufSBmcm9tICcuLi8uLi91dGlscy9wZXJtaXNzaW9ucy9QZXJtaXNzaW9uUmVzdWx0LmpzJ1xuaW1wb3J0IHsgcGVybWlzc2lvblJ1bGVWYWx1ZVRvU3RyaW5nIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvcGVybWlzc2lvblJ1bGVQYXJzZXIuanMnXG5pbXBvcnQgdHlwZSB7IFRoZW1lIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGhlbWUuanMnXG5pbXBvcnQgVGhlbWVkVGV4dCBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL1RoZW1lZFRleHQuanMnXG5cbmV4cG9ydCB0eXBlIFBlcm1pc3Npb25SdWxlRXhwbGFuYXRpb25Qcm9wcyA9IHtcbiAgcGVybWlzc2lvblJlc3VsdDogUGVybWlzc2lvbkRlY2lzaW9uXG4gIHRvb2xUeXBlOiAndG9vbCcgfCAnY29tbWFuZCcgfCAnZWRpdCcgfCAncmVhZCdcbn1cblxudHlwZSBEZWNpc2lvblJlYXNvblN0cmluZ3MgPSB7XG4gIHJlYXNvblN0cmluZzogc3RyaW5nXG4gIGNvbmZpZ1N0cmluZz86IHN0cmluZ1xuICAvKiogV2hlbiBzZXQsIHJlYXNvblN0cmluZyBpcyBwbGFpbiB0ZXh0IHJlbmRlcmVkIHdpdGggdGhpcyB0aGVtZSBjb2xvciBpbnN0ZWFkIG9mIDxBbnNpPi4gKi9cbiAgdGhlbWVDb2xvcj86IGtleW9mIFRoZW1lXG59XG5cbmZ1bmN0aW9uIHN0cmluZ3NGb3JEZWNpc2lvblJlYXNvbihcbiAgcmVhc29uOiBQZXJtaXNzaW9uRGVjaXNpb25SZWFzb24gfCB1bmRlZmluZWQsXG4gIHRvb2xUeXBlOiAndG9vbCcgfCAnY29tbWFuZCcgfCAnZWRpdCcgfCAncmVhZCcsXG4pOiBEZWNpc2lvblJlYXNvblN0cmluZ3MgfCBudWxsIHtcbiAgaWYgKCFyZWFzb24pIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG4gIGlmIChcbiAgICAoZmVhdHVyZSgnQkFTSF9DTEFTU0lGSUVSJykgfHwgZmVhdHVyZSgnVFJBTlNDUklQVF9DTEFTU0lGSUVSJykpICYmXG4gICAgcmVhc29uLnR5cGUgPT09ICdjbGFzc2lmaWVyJ1xuICApIHtcbiAgICBpZiAocmVhc29uLmNsYXNzaWZpZXIgPT09ICdhdXRvLW1vZGUnKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICByZWFzb25TdHJpbmc6IGBBdXRvIG1vZGUgY2xhc3NpZmllciByZXF1aXJlcyBjb25maXJtYXRpb24gZm9yIHRoaXMgJHt0b29sVHlwZX0uXFxuJHtyZWFzb24ucmVhc29ufWAsXG4gICAgICAgIGNvbmZpZ1N0cmluZzogdW5kZWZpbmVkLFxuICAgICAgICB0aGVtZUNvbG9yOiAnZXJyb3InLFxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4ge1xuICAgICAgcmVhc29uU3RyaW5nOiBgQ2xhc3NpZmllciAke2NoYWxrLmJvbGQocmVhc29uLmNsYXNzaWZpZXIpfSByZXF1aXJlcyBjb25maXJtYXRpb24gZm9yIHRoaXMgJHt0b29sVHlwZX0uXFxuJHtyZWFzb24ucmVhc29ufWAsXG4gICAgICBjb25maWdTdHJpbmc6IHVuZGVmaW5lZCxcbiAgICB9XG4gIH1cbiAgc3dpdGNoIChyZWFzb24udHlwZSkge1xuICAgIGNhc2UgJ3J1bGUnOlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcmVhc29uU3RyaW5nOiBgUGVybWlzc2lvbiBydWxlICR7Y2hhbGsuYm9sZChcbiAgICAgICAgICBwZXJtaXNzaW9uUnVsZVZhbHVlVG9TdHJpbmcocmVhc29uLnJ1bGUucnVsZVZhbHVlKSxcbiAgICAgICAgKX0gcmVxdWlyZXMgY29uZmlybWF0aW9uIGZvciB0aGlzICR7dG9vbFR5cGV9LmAsXG4gICAgICAgIGNvbmZpZ1N0cmluZzpcbiAgICAgICAgICByZWFzb24ucnVsZS5zb3VyY2UgPT09ICdwb2xpY3lTZXR0aW5ncydcbiAgICAgICAgICAgID8gdW5kZWZpbmVkXG4gICAgICAgICAgICA6ICcvcGVybWlzc2lvbnMgdG8gdXBkYXRlIHJ1bGVzJyxcbiAgICAgIH1cbiAgICBjYXNlICdob29rJzoge1xuICAgICAgY29uc3QgaG9va1JlYXNvblN0cmluZyA9IHJlYXNvbi5yZWFzb24gPyBgOlxcbiR7cmVhc29uLnJlYXNvbn1gIDogJy4nXG4gICAgICBjb25zdCBzb3VyY2VMYWJlbCA9IHJlYXNvbi5ob29rU291cmNlXG4gICAgICAgID8gYCAke2NoYWxrLmRpbShgWyR7cmVhc29uLmhvb2tTb3VyY2V9XWApfWBcbiAgICAgICAgOiAnJ1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcmVhc29uU3RyaW5nOiBgSG9vayAke2NoYWxrLmJvbGQocmVhc29uLmhvb2tOYW1lKX0gcmVxdWlyZXMgY29uZmlybWF0aW9uIGZvciB0aGlzICR7dG9vbFR5cGV9JHtob29rUmVhc29uU3RyaW5nfSR7c291cmNlTGFiZWx9YCxcbiAgICAgICAgY29uZmlnU3RyaW5nOiAnL2hvb2tzIHRvIHVwZGF0ZScsXG4gICAgICB9XG4gICAgfVxuICAgIGNhc2UgJ3NhZmV0eUNoZWNrJzpcbiAgICBjYXNlICdvdGhlcic6XG4gICAgICByZXR1cm4ge1xuICAgICAgICByZWFzb25TdHJpbmc6IHJlYXNvbi5yZWFzb24sXG4gICAgICAgIGNvbmZpZ1N0cmluZzogdW5kZWZpbmVkLFxuICAgICAgfVxuICAgIGNhc2UgJ3dvcmtpbmdEaXInOlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgcmVhc29uU3RyaW5nOiByZWFzb24ucmVhc29uLFxuICAgICAgICBjb25maWdTdHJpbmc6ICcvcGVybWlzc2lvbnMgdG8gdXBkYXRlIHJ1bGVzJyxcbiAgICAgIH1cbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuIG51bGxcbiAgfVxufVxuXG5leHBvcnQgZnVuY3Rpb24gUGVybWlzc2lvblJ1bGVFeHBsYW5hdGlvbih7XG4gIHBlcm1pc3Npb25SZXN1bHQsXG4gIHRvb2xUeXBlLFxufTogUGVybWlzc2lvblJ1bGVFeHBsYW5hdGlvblByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgcGVybWlzc2lvbk1vZGUgPSB1c2VBcHBTdGF0ZShzID0+IHMudG9vbFBlcm1pc3Npb25Db250ZXh0Lm1vZGUpXG4gIGNvbnN0IHN0cmluZ3MgPSBzdHJpbmdzRm9yRGVjaXNpb25SZWFzb24oXG4gICAgcGVybWlzc2lvblJlc3VsdD8uZGVjaXNpb25SZWFzb24sXG4gICAgdG9vbFR5cGUsXG4gIClcbiAgaWYgKCFzdHJpbmdzKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IHRoZW1lQ29sb3IgPVxuICAgIHN0cmluZ3MudGhlbWVDb2xvciA/P1xuICAgIChwZXJtaXNzaW9uUmVzdWx0Py5kZWNpc2lvblJlYXNvbj8udHlwZSA9PT0gJ2hvb2snICYmXG4gICAgcGVybWlzc2lvbk1vZGUgPT09ICdhdXRvJ1xuICAgICAgPyAnd2FybmluZydcbiAgICAgIDogdW5kZWZpbmVkKVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBtYXJnaW5Cb3R0b209ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgIHt0aGVtZUNvbG9yID8gKFxuICAgICAgICA8VGhlbWVkVGV4dCBjb2xvcj17dGhlbWVDb2xvcn0+e3N0cmluZ3MucmVhc29uU3RyaW5nfTwvVGhlbWVkVGV4dD5cbiAgICAgICkgOiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIDxBbnNpPntzdHJpbmdzLnJlYXNvblN0cmluZ308L0Fuc2k+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICl9XG4gICAgICB7c3RyaW5ncy5jb25maWdTdHJpbmcgJiYgPFRleHQgZGltQ29sb3I+e3N0cmluZ3MuY29uZmlnU3RyaW5nfTwvVGV4dD59XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLE9BQU8sUUFBUSxZQUFZO0FBQ3BDLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLElBQUksRUFBRUMs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 \ No newline at end of file diff --git a/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx b/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx new file mode 100644 index 0000000..2a7cd38 --- /dev/null +++ b/components/permissions/PowerShellPermissionRequest/PowerShellPermissionRequest.tsx @@ -0,0 +1,235 @@ +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { Box, Text, useTheme } from '../../../ink.js'; +import { useKeybinding } from '../../../keybindings/useKeybinding.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../../services/analytics/growthbook.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../../services/analytics/index.js'; +import { sanitizeToolNameForAnalytics } from '../../../services/analytics/metadata.js'; +import { getDestructiveCommandWarning } from '../../../tools/PowerShellTool/destructiveCommandWarning.js'; +import { PowerShellTool } from '../../../tools/PowerShellTool/PowerShellTool.js'; +import { isAllowlistedCommand } from '../../../tools/PowerShellTool/readOnlyValidation.js'; +import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js'; +import { getCompoundCommandPrefixesStatic } from '../../../utils/powershell/staticPrefix.js'; +import { Select } from '../../CustomSelect/select.js'; +import { type UnaryEvent, usePermissionRequestLogging } from '../hooks.js'; +import { PermissionDecisionDebugInfo } from '../PermissionDecisionDebugInfo.js'; +import { PermissionDialog } from '../PermissionDialog.js'; +import { PermissionExplainerContent, usePermissionExplainerUI } from '../PermissionExplanation.js'; +import type { PermissionRequestProps } from '../PermissionRequest.js'; +import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js'; +import { useShellPermissionFeedback } from '../useShellPermissionFeedback.js'; +import { logUnaryPermissionEvent } from '../utils.js'; +import { powershellToolUseOptions } from './powershellToolUseOptions.js'; +export function PowerShellPermissionRequest(props: PermissionRequestProps): React.ReactNode { + const { + toolUseConfirm, + toolUseContext, + onDone, + onReject, + workerBadge + } = props; + const { + command, + description + } = PowerShellTool.inputSchema.parse(toolUseConfirm.input); + const [theme] = useTheme(); + const explainerState = usePermissionExplainerUI({ + toolName: toolUseConfirm.tool.name, + toolInput: toolUseConfirm.input, + toolDescription: toolUseConfirm.description, + messages: toolUseContext.messages + }); + const { + yesInputMode, + noInputMode, + yesFeedbackModeEntered, + noFeedbackModeEntered, + acceptFeedback, + rejectFeedback, + setAcceptFeedback, + setRejectFeedback, + focusedOption, + handleInputModeToggle, + handleReject, + handleFocus + } = useShellPermissionFeedback({ + toolUseConfirm, + onDone, + onReject, + explainerVisible: explainerState.visible + }); + const destructiveWarning = getFeatureValue_CACHED_MAY_BE_STALE('tengu_destructive_command_warning', false) ? getDestructiveCommandWarning(command) : null; + const [showPermissionDebug, setShowPermissionDebug] = useState(false); + + // Editable prefix — compute static prefix locally (no LLM call). + // Initialize synchronously to the raw command for single-line commands so + // the editable input renders immediately, then refine to the extracted prefix + // once the AST parser resolves. Multiline commands (`# comment\n...`, + // foreach loops) get undefined → powershellToolUseOptions:64 hides the + // "don't ask again" option — those literals are one-time-use (settings + // corpus shows 14 multiline rules, zero match twice). For compound commands, + // computes a prefix per subcommand, excluding subcommands that are already + // auto-allowed (read-only). + const [editablePrefix, setEditablePrefix] = useState(command.includes('\n') ? undefined : command); + const hasUserEditedPrefix = useRef(false); + useEffect(() => { + let cancelled = false; + // Filter receives ParsedCommandElement — isAllowlistedCommand works from + // element.name/nameType/args directly. isReadOnlyCommand(text) would need + // to reparse (pwsh.exe spawn per subcommand) and returns false without the + // full parsed AST, making the filter a no-op. + getCompoundCommandPrefixesStatic(command, element => isAllowlistedCommand(element, element.text)).then(prefixes => { + if (cancelled || hasUserEditedPrefix.current) return; + if (prefixes.length > 0) { + setEditablePrefix(`${prefixes[0]}:*`); + } + }).catch(() => {}); + return () => { + cancelled = true; + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [command]); + const onEditablePrefixChange = useCallback((value: string) => { + hasUserEditedPrefix.current = true; + setEditablePrefix(value); + }, []); + const unaryEvent = useMemo(() => ({ + completion_type: 'tool_use_single', + language_name: 'none' + }), []); + usePermissionRequestLogging(toolUseConfirm, unaryEvent); + const options = useMemo(() => powershellToolUseOptions({ + suggestions: toolUseConfirm.permissionResult.behavior === 'ask' ? toolUseConfirm.permissionResult.suggestions : undefined, + onRejectFeedbackChange: setRejectFeedback, + onAcceptFeedbackChange: setAcceptFeedback, + yesInputMode, + noInputMode, + editablePrefix, + onEditablePrefixChange + }), [toolUseConfirm, yesInputMode, noInputMode, editablePrefix, onEditablePrefixChange]); + + // Toggle permission debug info with keybinding + const handleToggleDebug = useCallback(() => { + setShowPermissionDebug(prev => !prev); + }, []); + useKeybinding('permission:toggleDebug', handleToggleDebug, { + context: 'Confirmation' + }); + function onSelect(value: string) { + // Map options to numeric values for analytics (strings not allowed in logEvent) + const optionIndex: Record = { + yes: 1, + 'yes-apply-suggestions': 2, + 'yes-prefix-edited': 2, + no: 3 + }; + logEvent('tengu_permission_request_option_selected', { + option_index: optionIndex[value], + explainer_visible: explainerState.visible + }); + const toolNameForAnalytics = sanitizeToolNameForAnalytics(toolUseConfirm.tool.name) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS; + if (value === 'yes-prefix-edited') { + const trimmedPrefix = (editablePrefix ?? '').trim(); + logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept'); + if (!trimmedPrefix) { + toolUseConfirm.onAllow(toolUseConfirm.input, []); + } else { + const prefixUpdates: PermissionUpdate[] = [{ + type: 'addRules', + rules: [{ + toolName: PowerShellTool.name, + ruleContent: trimmedPrefix + }], + behavior: 'allow', + destination: 'localSettings' + }]; + toolUseConfirm.onAllow(toolUseConfirm.input, prefixUpdates); + } + onDone(); + return; + } + switch (value) { + case 'yes': + { + const trimmedFeedback = acceptFeedback.trim(); + logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept'); + // Log accept submission with feedback context + logEvent('tengu_accept_submitted', { + toolName: toolNameForAnalytics, + isMcp: toolUseConfirm.tool.isMcp ?? false, + has_instructions: !!trimmedFeedback, + instructions_length: trimmedFeedback.length, + entered_feedback_mode: yesFeedbackModeEntered + }); + toolUseConfirm.onAllow(toolUseConfirm.input, [], trimmedFeedback || undefined); + onDone(); + break; + } + case 'yes-apply-suggestions': + { + logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept'); + // Extract suggestions if present (works for both 'ask' and 'passthrough' behaviors) + const permissionUpdates = 'suggestions' in toolUseConfirm.permissionResult ? toolUseConfirm.permissionResult.suggestions || [] : []; + toolUseConfirm.onAllow(toolUseConfirm.input, permissionUpdates); + onDone(); + break; + } + case 'no': + { + const trimmedFeedback = rejectFeedback.trim(); + + // Log reject submission with feedback context + logEvent('tengu_reject_submitted', { + toolName: toolNameForAnalytics, + isMcp: toolUseConfirm.tool.isMcp ?? false, + has_instructions: !!trimmedFeedback, + instructions_length: trimmedFeedback.length, + entered_feedback_mode: noFeedbackModeEntered + }); + + // Process rejection (with or without feedback) + handleReject(trimmedFeedback || undefined); + break; + } + } + } + return + + + {PowerShellTool.renderToolUseMessage({ + command, + description + }, { + theme, + verbose: true + } // always show the full command + )} + + {!explainerState.visible && {toolUseConfirm.description}} + + + {showPermissionDebug ? <> + + {toolUseContext.options.debug && + Ctrl-D to hide debug info + } + : <> + + + {destructiveWarning && + {destructiveWarning} + } + Do you want to proceed? + ; + $[15] = onSelect; + $[16] = options; + $[17] = t11; + $[18] = t12; + } else { + t12 = $[18]; + } + let t13; + if ($[19] !== t12 || $[20] !== t9) { + t13 = {t9}{t10}{t12}; + $[19] = t12; + $[20] = t9; + $[21] = t13; + } else { + t13 = $[21]; + } + return t13; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJOZXR3b3JrSG9zdFBhdHRlcm4iLCJzaG91bGRBbGxvd01hbmFnZWRTYW5kYm94RG9tYWluc09ubHkiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJTZWxlY3QiLCJQZXJtaXNzaW9uRGlhbG9nIiwiU2FuZGJveFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMiLCJob3N0UGF0dGVybiIsIm9uVXNlclJlc3BvbnNlIiwicmVzcG9uc2UiLCJhbGxvdyIsInBlcnNpc3RUb1NldHRpbmdzIiwiU2FuZGJveFBlcm1pc3Npb25SZXF1ZXN0IiwidDAiLCIkIiwiX2MiLCJ0MSIsImhvc3QiLCJ0MiIsIm9uU2VsZWN0IiwidmFsdWUiLCJiYjQiLCJ0MyIsIlN5bWJvbCIsImZvciIsIm1hbmFnZWREb21haW5zT25seSIsInQ0IiwibGFiZWwiLCJ0NSIsInQ2IiwidDciLCJvcHRpb25zIiwidDgiLCJ0OSIsInQxMCIsInQxMSIsInQxMiIsInQxMyJdLCJzb3VyY2VzIjpbIlNhbmRib3hQZXJtaXNzaW9uUmVxdWVzdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICdzcmMvaW5rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBOZXR3b3JrSG9zdFBhdHRlcm4sXG4gIHNob3VsZEFsbG93TWFuYWdlZFNhbmRib3hEb21haW5zT25seSxcbn0gZnJvbSAnc3JjL3V0aWxzL3NhbmRib3gvc2FuZGJveC1hZGFwdGVyLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnLi4vLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IFBlcm1pc3Npb25EaWFsb2cgfSBmcm9tICcuL1Blcm1pc3Npb25EaWFsb2cuanMnXG5cbmV4cG9ydCB0eXBlIFNhbmRib3hQZXJtaXNzaW9uUmVxdWVzdFByb3BzID0ge1xuICBob3N0UGF0dGVybjogTmV0d29ya0hvc3RQYXR0ZXJuXG4gIG9uVXNlclJlc3BvbnNlOiAocmVzcG9uc2U6IHtcbiAgICBhbGxvdzogYm9vbGVhblxuICAgIHBlcnNpc3RUb1NldHRpbmdzOiBib29sZWFuXG4gIH0pID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFNhbmRib3hQZXJtaXNzaW9uUmVxdWVzdCh7XG4gIGhvc3RQYXR0ZXJuOiB7IGhvc3QgfSxcbiAgb25Vc2VyUmVzcG9uc2UsXG59OiBTYW5kYm94UGVybWlzc2lvblJlcXVlc3RQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGZ1bmN0aW9uIG9uU2VsZWN0KHZhbHVlOiBzdHJpbmcpIHtcbiAgICAvLyBXZSBtYXkgd2FudCB0byBiZXR0ZXIgdW5pZnkgdGhpcyBkaWFsb2cgd2l0aCBvdGhlciBwZXJtaXNzaW9uIGRpYWxvZ3NcbiAgICAvLyBhbmQgdXNlIHRoZWlyIGxvZ2dpbmcsIGJ1dCB0aGlzIGlzIHNsaWdodGx5IGRpZmZlcmVudCBhbmQgd2UgZG9uJ3QgaGF2ZVxuICAgIC8vIHRoZSB0b29sIGNvbnRleHQgaGVyZS4gRm9yIG5vdywganVzdCB1c2UgYmFzaWMgbG9nZ2luZyBmb3IgYmFzaWMgZGF0YS5cbiAgICBpZiAoXCJleHRlcm5hbFwiID09PSAnYW50Jykge1xuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3NhbmRib3hfbmV0d29ya19kaWFsb2dfcmVzdWx0Jywge1xuICAgICAgICBob3N0OiBob3N0IGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHJlc3VsdDpcbiAgICAgICAgICB2YWx1ZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcbiAgICB9XG5cbiAgICBzd2l0Y2ggKHZhbHVlKSB7XG4gICAgICBjYXNlICd5ZXMnOlxuICAgICAgICBvblVzZXJSZXNwb25zZSh7IGFsbG93OiB0cnVlLCBwZXJzaXN0VG9TZXR0aW5nczogZmFsc2UgfSlcbiAgICAgICAgYnJlYWtcbiAgICAgIGNhc2UgJ3llcy1kb250LWFzay1hZ2Fpbic6XG4gICAgICAgIG9uVXNlclJlc3BvbnNlKHsgYWxsb3c6IHRydWUsIHBlcnNpc3RUb1NldHRpbmdzOiB0cnVlIH0pXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdubyc6XG4gICAgICAgIG9uVXNlclJlc3BvbnNlKHsgYWxsb3c6IGZhbHNlLCBwZXJzaXN0VG9TZXR0aW5nczogZmFsc2UgfSlcbiAgICAgICAgYnJlYWtcbiAgICB9XG4gIH1cblxuICBjb25zdCBtYW5hZ2VkRG9tYWluc09ubHkgPSBzaG91bGRBbGxvd01hbmFnZWRTYW5kYm94RG9tYWluc09ubHkoKVxuXG4gIGNvbnN0IG9wdGlvbnMgPSBbXG4gICAgeyBsYWJlbDogJ1llcycsIHZhbHVlOiAneWVzJyB9LFxuICAgIC4uLighbWFuYWdlZERvbWFpbnNPbmx5XG4gICAgICA/IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBsYWJlbDogKFxuICAgICAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgICAgICBZZXMsIGFuZCBkb24mYXBvczt0IGFzayBhZ2FpbiBmb3IgPFRleHQgYm9sZD57aG9zdH08L1RleHQ+XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICksXG4gICAgICAgICAgICB2YWx1ZTogJ3llcy1kb250LWFzay1hZ2FpbicsXG4gICAgICAgICAgfSxcbiAgICAgICAgXVxuICAgICAgOiBbXSksXG4gICAge1xuICAgICAgbGFiZWw6IChcbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgTm8sIGFuZCB0ZWxsIENsYXVkZSB3aGF0IHRvIGRvIGRpZmZlcmVudGx5IDxUZXh0IGJvbGQ+KGVzYyk8L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICksXG4gICAgICB2YWx1ZTogJ25vJyxcbiAgICB9LFxuICBdXG5cbiAgcmV0dXJuIChcbiAgICA8UGVybWlzc2lvbkRpYWxvZyB0aXRsZT1cIk5ldHdvcmsgcmVxdWVzdCBvdXRzaWRlIG9mIHNhbmRib3hcIj5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdYPXsyfSBwYWRkaW5nWT17MX0+XG4gICAgICAgIDxCb3g+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+SG9zdDo8L1RleHQ+XG4gICAgICAgICAgPFRleHQ+IHtob3N0fTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgICA8VGV4dD5EbyB5b3Ugd2FudCB0byBhbGxvdyB0aGlzIGNvbm5lY3Rpb24/PC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICAgPEJveD5cbiAgICAgICAgICA8U2VsZWN0XG4gICAgICAgICAgICBvcHRpb25zPXtvcHRpb25zfVxuICAgICAgICAgICAgb25DaGFuZ2U9e29uU2VsZWN0fVxuICAgICAgICAgICAgb25DYW5jZWw9eygpID0+IHtcbiAgICAgICAgICAgICAgaWYgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcpIHtcbiAgICAgICAgICAgICAgICBsb2dFdmVudCgndGVuZ3Vfc2FuZGJveF9uZXR3b3JrX2RpYWxvZ19yZXN1bHQnLCB7XG4gICAgICAgICAgICAgICAgICBob3N0OiBob3N0IGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgICAgICAgICByZXN1bHQ6XG4gICAgICAgICAgICAgICAgICAgICdjYW5jZWwnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBvblVzZXJSZXNwb25zZSh7IGFsbG93OiBmYWxzZSwgcGVyc2lzdFRvU2V0dGluZ3M6IGZhbHNlIH0pXG4gICAgICAgICAgICB9fVxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9Cb3g+XG4gICAgPC9QZXJtaXNzaW9uRGlhbG9nPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLFlBQVk7QUFDdEMsU0FDRSxLQUFLQyxrQkFBa0IsRUFDdkJDLG9DQUFvQyxRQUMvQixzQ0FBc0M7QUFDN0MsU0FDRSxLQUFLQywwREFBMEQsRUFDL0RDLFFBQVEsUUFDSCxtQ0FBbUM7QUFDMUMsU0FBU0MsTUFBTSxRQUFRLDJCQUEyQjtBQUNsRCxTQUFTQyxnQkFBZ0IsUUFBUSx1QkFBdUI7QUFFeEQsT0FBTyxLQUFLQyw2QkFBNkIsR0FBRztFQUMxQ0MsV0FBVyxFQUFFUCxrQkFBa0I7RUFDL0JRLGNBQWMsRUFBRSxDQUFDQyxRQUFRLEVBQUU7SUFDekJDLEtBQUssRUFBRSxPQUFPO0lBQ2RDLGlCQUFpQixFQUFFLE9BQU87RUFDNUIsQ0FBQyxFQUFFLEdBQUcsSUFBSTtBQUNaLENBQUM7QUFFRCxPQUFPLFNBQUFDLHlCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWtDO0lBQUFSLFdBQUEsRUFBQVMsRUFBQTtJQUFBUjtFQUFBLElBQUFLLEVBR1Q7RUFGakI7SUFBQUk7RUFBQSxJQUFBRCxFQUFRO0VBQUEsSUFBQUUsRUFBQTtFQUFBLElBQUFKLENBQUEsUUFBQU4sY0FBQTtJQUdyQlUsRUFBQSxZQUFBQyxTQUFBQyxLQUFBO01BQUFDLEdBQUEsRUFZ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 \ No newline at end of file diff --git a/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx b/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx new file mode 100644 index 0000000..be81a18 --- /dev/null +++ b/components/permissions/SedEditPermissionRequest/SedEditPermissionRequest.tsx @@ -0,0 +1,230 @@ +import { c as _c } from "react/compiler-runtime"; +import { basename, relative } from 'path'; +import React, { Suspense, use, useMemo } from 'react'; +import { FileEditToolDiff } from 'src/components/FileEditToolDiff.js'; +import { getCwd } from 'src/utils/cwd.js'; +import { isENOENT } from 'src/utils/errors.js'; +import { detectEncodingForResolvedPath } from 'src/utils/fileRead.js'; +import { getFsImplementation } from 'src/utils/fsOperations.js'; +import { Text } from '../../../ink.js'; +import { BashTool } from '../../../tools/BashTool/BashTool.js'; +import { applySedSubstitution, type SedEditInfo } from '../../../tools/BashTool/sedEditParser.js'; +import { FilePermissionDialog } from '../FilePermissionDialog/FilePermissionDialog.js'; +import type { PermissionRequestProps } from '../PermissionRequest.js'; +type SedEditPermissionRequestProps = PermissionRequestProps & { + sedInfo: SedEditInfo; +}; +type FileReadResult = { + oldContent: string; + fileExists: boolean; +}; +export function SedEditPermissionRequest(t0) { + const $ = _c(9); + let props; + let sedInfo; + if ($[0] !== t0) { + ({ + sedInfo, + ...props + } = t0); + $[0] = t0; + $[1] = props; + $[2] = sedInfo; + } else { + props = $[1]; + sedInfo = $[2]; + } + const { + filePath + } = sedInfo; + let t1; + if ($[3] !== filePath) { + t1 = (async () => { + const encoding = detectEncodingForResolvedPath(filePath); + const raw = await getFsImplementation().readFile(filePath, { + encoding + }); + return { + oldContent: raw.replaceAll("\r\n", "\n"), + fileExists: true + }; + })().catch(_temp); + $[3] = filePath; + $[4] = t1; + } else { + t1 = $[4]; + } + const contentPromise = t1; + let t2; + if ($[5] !== contentPromise || $[6] !== props || $[7] !== sedInfo) { + t2 = ; + $[5] = contentPromise; + $[6] = props; + $[7] = sedInfo; + $[8] = t2; + } else { + t2 = $[8]; + } + return t2; +} +function _temp(e) { + if (!isENOENT(e)) { + throw e; + } + return { + oldContent: "", + fileExists: false + }; +} +function SedEditPermissionRequestInner(t0) { + const $ = _c(35); + let contentPromise; + let props; + let sedInfo; + if ($[0] !== t0) { + ({ + sedInfo, + contentPromise, + ...props + } = t0); + $[0] = t0; + $[1] = contentPromise; + $[2] = props; + $[3] = sedInfo; + } else { + contentPromise = $[1]; + props = $[2]; + sedInfo = $[3]; + } + const { + filePath + } = sedInfo; + const { + oldContent, + fileExists + } = use(contentPromise); + let t1; + if ($[4] !== oldContent || $[5] !== sedInfo) { + t1 = applySedSubstitution(oldContent, sedInfo); + $[4] = oldContent; + $[5] = sedInfo; + $[6] = t1; + } else { + t1 = $[6]; + } + const newContent = t1; + let t2; + bb0: { + if (oldContent === newContent) { + let t3; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t3 = []; + $[7] = t3; + } else { + t3 = $[7]; + } + t2 = t3; + break bb0; + } + let t3; + if ($[8] !== newContent || $[9] !== oldContent) { + t3 = [{ + old_string: oldContent, + new_string: newContent, + replace_all: false + }]; + $[8] = newContent; + $[9] = oldContent; + $[10] = t3; + } else { + t3 = $[10]; + } + t2 = t3; + } + const edits = t2; + let t3; + bb1: { + if (!fileExists) { + t3 = "File does not exist"; + break bb1; + } + t3 = "Pattern did not match any content"; + } + const noChangesMessage = t3; + let t4; + if ($[11] !== filePath || $[12] !== newContent) { + t4 = input => { + const parsed = BashTool.inputSchema.parse(input); + return { + ...parsed, + _simulatedSedEdit: { + filePath, + newContent + } + }; + }; + $[11] = filePath; + $[12] = newContent; + $[13] = t4; + } else { + t4 = $[13]; + } + const parseInput = t4; + const t5 = props.toolUseConfirm; + const t6 = props.toolUseContext; + const t7 = props.onDone; + const t8 = props.onReject; + let t9; + if ($[14] !== filePath) { + t9 = relative(getCwd(), filePath); + $[14] = filePath; + $[15] = t9; + } else { + t9 = $[15]; + } + let t10; + if ($[16] !== filePath) { + t10 = basename(filePath); + $[16] = filePath; + $[17] = t10; + } else { + t10 = $[17]; + } + let t11; + if ($[18] !== t10) { + t11 = Do you want to make this edit to{" "}{t10}?; + $[18] = t10; + $[19] = t11; + } else { + t11 = $[19]; + } + let t12; + if ($[20] !== edits || $[21] !== filePath || $[22] !== noChangesMessage) { + t12 = edits.length > 0 ? : {noChangesMessage}; + $[20] = edits; + $[21] = filePath; + $[22] = noChangesMessage; + $[23] = t12; + } else { + t12 = $[23]; + } + let t13; + if ($[24] !== filePath || $[25] !== parseInput || $[26] !== props.onDone || $[27] !== props.onReject || $[28] !== props.toolUseConfirm || $[29] !== props.toolUseContext || $[30] !== props.workerBadge || $[31] !== t11 || $[32] !== t12 || $[33] !== t9) { + t13 = ; + $[24] = filePath; + $[25] = parseInput; + $[26] = props.onDone; + $[27] = props.onReject; + $[28] = props.toolUseConfirm; + $[29] = props.toolUseContext; + $[30] = props.workerBadge; + $[31] = t11; + $[32] = t12; + $[33] = t9; + $[34] = t13; + } else { + t13 = $[34]; + } + return t13; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJiYXNlbmFtZSIsInJlbGF0aXZlIiwiUmVhY3QiLCJTdXNwZW5zZSIsInVzZSIsInVzZU1lbW8iLCJGaWxlRWRpdFRvb2xEaWZmIiwiZ2V0Q3dkIiwiaXNFTk9FTlQiLCJkZXRlY3RFbmNvZGluZ0ZvclJlc29sdmVkUGF0aCIsImdldEZzSW1wbGVtZW50YXRpb24iLCJUZXh0IiwiQmFzaFRvb2wiLCJhcHBseVNlZFN1YnN0aXR1dGlvbiIsIlNlZEVkaXRJbmZvIiwiRmlsZVBlcm1pc3Npb25EaWFsb2ciLCJQZXJtaXNzaW9uUmVxdWVzdFByb3BzIiwiU2VkRWRpdFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMiLCJzZWRJbmZvIiwiRmlsZVJlYWRSZXN1bHQiLCJvbGRDb250ZW50IiwiZmlsZUV4aXN0cyIsIlNlZEVkaXRQZXJtaXNzaW9uUmVxdWVzdCIsInQwIiwiJCIsIl9jIiwicHJvcHMiLCJmaWxlUGF0aCIsInQxIiwiZW5jb2RpbmciLCJyYXciLCJyZWFkRmlsZSIsInJlcGxhY2VBbGwiLCJjYXRjaCIsIl90ZW1wIiwiY29udGVudFByb21pc2UiLCJ0MiIsImUiLCJTZWRFZGl0UGVybWlzc2lvblJlcXVlc3RJbm5lciIsIm5ld0NvbnRlbnQiLCJiYjAiLCJ0MyIsIlN5bWJvbCIsImZvciIsIm9sZF9zdHJpbmciLCJuZXdfc3RyaW5nIiwicmVwbGFjZV9hbGwiLCJlZGl0cyIsImJiMSIsIm5vQ2hhbmdlc01lc3NhZ2UiLCJ0NCIsImlucHV0IiwicGFyc2VkIiwiaW5wdXRTY2hlbWEiLCJwYXJzZSIsIl9zaW11bGF0ZWRTZWRFZGl0IiwicGFyc2VJbnB1dCIsInQ1IiwidG9vbFVzZUNvbmZpcm0iLCJ0NiIsInRvb2xVc2VDb250ZXh0IiwidDciLCJvbkRvbmUiLCJ0OCIsIm9uUmVqZWN0IiwidDkiLCJ0MTAiLCJ0MTEiLCJ0MTIiLCJsZW5ndGgiLCJ0MTMiLCJ3b3JrZXJCYWRnZSJdLCJzb3VyY2VzIjpbIlNlZEVkaXRQZXJtaXNzaW9uUmVxdWVzdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgYmFzZW5hbWUsIHJlbGF0aXZlIH0gZnJvbSAncGF0aCdcbmltcG9ydCBSZWFjdCwgeyBTdXNwZW5zZSwgdXNlLCB1c2VNZW1vIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBGaWxlRWRpdFRvb2xEaWZmIH0gZnJvbSAnc3JjL2NvbXBvbmVudHMvRmlsZUVkaXRUb29sRGlmZi5qcydcbmltcG9ydCB7IGdldEN3ZCB9IGZyb20gJ3NyYy91dGlscy9jd2QuanMnXG5pbXBvcnQgeyBpc0VOT0VOVCB9IGZyb20gJ3NyYy91dGlscy9lcnJvcnMuanMnXG5pbXBvcnQgeyBkZXRlY3RFbmNvZGluZ0ZvclJlc29sdmVkUGF0aCB9IGZyb20gJ3NyYy91dGlscy9maWxlUmVhZC5qcydcbmltcG9ydCB7IGdldEZzSW1wbGVtZW50YXRpb24gfSBmcm9tICdzcmMvdXRpbHMvZnNPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uLy4uLy4uL2luay5qcydcbmltcG9ydCB7IEJhc2hUb29sIH0gZnJvbSAnLi4vLi4vLi4vdG9vbHMvQmFzaFRvb2wvQmFzaFRvb2wuanMnXG5pbXBvcnQge1xuICBhcHBseVNlZFN1YnN0aXR1dGlvbixcbiAgdHlwZSBTZWRFZGl0SW5mbyxcbn0gZnJvbSAnLi4vLi4vLi4vdG9vbHMvQmFzaFRvb2wvc2VkRWRpdFBhcnNlci5qcydcbmltcG9ydCB7IEZpbGVQZXJtaXNzaW9uRGlhbG9nIH0gZnJvbSAnLi4vRmlsZVBlcm1pc3Npb25EaWFsb2cvRmlsZVBlcm1pc3Npb25EaWFsb2cuanMnXG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMgfSBmcm9tICcuLi9QZXJtaXNzaW9uUmVxdWVzdC5qcydcblxudHlwZSBTZWRFZGl0UGVybWlzc2lvblJlcXVlc3RQcm9wcyA9IFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMgJiB7XG4gIHNlZEluZm86IFNlZEVkaXRJbmZvXG59XG5cbnR5cGUgRmlsZVJlYWRSZXN1bHQgPSB7IG9sZENvbnRlbnQ6IHN0cmluZzsgZmlsZUV4aXN0czogYm9vbGVhbiB9XG5cbmV4cG9ydCBmdW5jdGlvbiBTZWRFZGl0UGVybWlzc2lvblJlcXVlc3Qoe1xuICBzZWRJbmZvLFxuICAuLi5wcm9wc1xufTogU2VkRWRpdFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB7IGZpbGVQYXRoIH0gPSBzZWRJbmZvXG5cbiAgLy8gUmVhZCBmaWxlIGNvbnRlbnQgYXN5bmMgc28gbW91bnQgZG9lc24ndCBibG9jayBSZWFjdCBjb21taXQgb24gZGlzayBJL08uXG4gIC8vIExhcmdlIGZpbGVzIHdvdWxkIG90aGVyd2lzZSBoYW5nIHRoZSBkaWFsb2cgYmVmb3JlIGl0IHJlbmRlcnMuXG4gIC8vIE1lbW9pemVkIG9uIGZpbGVQYXRoIHNvIHdlIGRvbid0IHJlLXJlYWQgb24gZXZlcnkgcmVuZGVyLlxuICBjb25zdCBjb250ZW50UHJvbWlzZSA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIChhc3luYyAoKTogUHJvbWlzZTxGaWxlUmVhZFJlc3VsdD4gPT4ge1xuICAgICAgICAvLyBEZXRlY3QgZW5jb2RpbmcgZmlyc3QgKHN5bmMgNEtCIHJlYWQg4oCUIG5lZ2xpZ2libGUpIHNvIFVURi0xNkxFIEJPTXNcbiAgICAgICAgLy8gcmVuZGVyIGNvcnJlY3RseS4gVGhpcyBtYXRjaGVzIHdoYXQgcmVhZEZpbGVTeW5jIGRpZCBiZWZvcmUgdGhlXG4gICAgICAgIC8vIGFzeW5jIGNvbnZlcnNpb24uXG4gICAgICAgIGNvbnN0IGVuY29kaW5nID0gZGV0ZWN0RW5jb2RpbmdGb3JSZXNvbHZlZFBhdGgoZmlsZVBhdGgpXG4gICAgICAgIGNvbnN0IHJhdyA9IGF3YWl0IGdldEZzSW1wbGVtZW50YXRpb24oKS5yZWFkRmlsZShmaWxlUGF0aCwgeyBlbmNvZGluZyB9KVxuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIG9sZENvbnRlbnQ6IHJhdy5yZXBsYWNlQWxsKCdcXHJcXG4nLCAnXFxuJyksXG4gICAgICAgICAgZmlsZUV4aXN0czogdHJ1ZSxcbiAgICAgICAgfVxuICAgICAgfSkoKS5jYXRjaCgoZTogdW5rbm93bik6IEZpbGVSZWFkUmVzdWx0ID0+IHtcbiAgICAgICAgaWYgKCFpc0VOT0VOVChlKSkgdGhyb3cgZVxuICAgICAgICByZXR1cm4geyBvbGRDb250ZW50OiAnJywgZmlsZUV4aXN0czogZmFsc2UgfVxuICAgICAgfSksXG4gICAgW2ZpbGVQYXRoXSxcbiAgKVxuXG4gIHJldHVybiAoXG4gICAgPFN1c3BlbnNlIGZhbGxiYWNrPXtudWxsfT5cbiAgICAgIDxTZWRFZGl0UGVybWlzc2lvblJlcXVlc3RJbm5lclxuICAgICAgICBzZWRJbmZvPXtzZWRJbmZvfVxuICAgICAgICBjb250ZW50UHJvbWlzZT17Y29udGVudFByb21pc2V9XG4gICAgICAgIHsuLi5wcm9wc31cbiAgICAgIC8+XG4gICAgPC9TdXNwZW5zZT5cbiAgKVxufVxuXG5mdW5jdGlvbiBTZWRFZGl0UGVybWlzc2lvblJlcXVlc3RJbm5lcih7XG4gIHNlZEluZm8sXG4gIGNvbnRlbnRQcm9taXNlLFxuICAuLi5wcm9wc1xufTogU2VkRWRpdFBlcm1pc3Npb25SZXF1ZXN0UHJvcHMgJiB7XG4gIGNvbnRlbnRQcm9taXNlOiBQcm9taXNlPEZpbGVSZWFkUmVzdWx0PlxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgZmlsZVBhdGggfSA9IHNlZEluZm9cbiAgY29uc3QgeyBvbGRDb250ZW50LCBmaWxlRXhpc3RzIH0gPSB1c2UoY29udGVudFByb21pc2UpXG5cbiAgLy8gQ29tcHV0ZSB0aGUgbmV3IGNvbnRlbnQgYnkgYXBwbHlpbmcgdGhlIHNlZCBzdWJzdGl0dXRpb25cbiAgY29uc3QgbmV3Q29udGVudCA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIHJldHVybiBhcHBseVNlZFN1YnN0aXR1dGlvbihvbGRDb250ZW50LCBzZWRJbmZvKVxuICB9LCBbb2xkQ29udGVudCwgc2VkSW5mb10pXG5cbiAgLy8gQ3JlYXRlIHRoZSBlZGl0IHJlcHJlc2VudGF0aW9uIGZvciB0aGUgZGlmZlxuICBjb25zdCBlZGl0cyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmIChvbGRDb250ZW50ID09PSBuZXdDb250ZW50KSB7XG4gICAgICByZXR1cm4gW11cbiAgICB9XG4gICAgcmV0dXJuIFtcbiAgICAgIHtcbiAgICAgICAgb2xkX3N0cmluZzogb2xkQ29udGVudCxcbiAgICAgICAgbmV3X3N0cmluZzogbmV3Q29udGVudCxcbiAgICAgICAgcmVwbGFjZV9hbGw6IGZhbHNlLFxuICAgICAgfSxcbiAgICBdXG4gIH0sIFtvbGRDb250ZW50LCBuZXdDb250ZW50XSlcblxuICAvLyBEZXRlcm1pbmUgYXBwcm9wcmlhdGUgbWVzc2FnZSB3aGVuIG5vIGNoYW5nZXNcbiAgY29uc3Qgbm9DaGFuZ2VzTWVzc2FnZSA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmICghZmlsZUV4aXN0cykge1xuICAgICAgcmV0dXJuICdGaWxlIGRvZXMgbm90IGV4aXN0J1xuICAgIH1cbiAgICByZXR1cm4gJ1BhdHRlcm4gZGlkIG5vdCBtYXRjaCBhbnkgY29udGVudCdcbiAgfSwgW2ZpbGVFeGlzdHNdKVxuXG4gIC8vIFBhcnNlIGlucHV0IGFuZCBhZGQgX3NpbXVsYXRlZFNlZEVkaXQgdG8gZW5zdXJlIHdoYXQgdXNlciBwcmV2aWV3ZWRcbiAgLy8gaXMgZXhhY3RseSB3aGF0IGdldHMgd3JpdHRlbiAocHJldmVudHMgc2VkL0pTIHJlZ2V4IGRpZmZlcmVuY2VzKVxuICBjb25zdCBwYXJzZUlucHV0ID0gKGlucHV0OiB1bmtub3duKSA9PiB7XG4gICAgY29uc3QgcGFyc2VkID0gQmFzaFRvb2wuaW5wdXRTY2hlbWEucGFyc2UoaW5wdXQpXG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLnBhcnNlZCxcbiAgICAgIF9zaW11bGF0ZWRTZWRFZGl0OiB7XG4gICAgICAgIGZpbGVQYXRoLFxuICAgICAgICBuZXdDb250ZW50LFxuICAgICAgfSxcbiAgICB9XG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxGaWxlUGVybWlzc2lvbkRpYWxvZ1xuICAgICAgdG9vbFVzZUNvbmZpcm09e3Byb3BzLnRvb2xVc2VDb25maXJtfVxuICAgICAgdG9vbFVzZUNvbnRleHQ9e3Byb3BzLnRvb2xVc2VDb250ZXh0fVxuICAgICAgb25Eb25lPXtwcm9wcy5vbkRvbmV9XG4gICAgICBvblJlamVjdD17cHJvcHMub25SZWplY3R9XG4gICAgICB0aXRsZT1cIkVkaXQgZmlsZVwiXG4gICAgICBzdWJ0aXRsZT17cmVsYXRpdmUoZ2V0Q3dkKCksIGZpbGVQYXRoKX1cbiAgICAgIHF1ZXN0aW9uPXtcbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgRG8geW91IHdhbnQgdG8gbWFrZSB0aGlzIGVkaXQgdG97JyAnfVxuICAgICAgICAgIDxUZXh0IGJvbGQ+e2Jhc2VuYW1lKGZpbGVQYXRoKX08L1RleHQ+P1xuICAgICAgICA8L1RleHQ+XG4gICAgICB9XG4gICAgICBjb250ZW50PXtcbiAgICAgICAgZWRpdHMubGVuZ3RoID4gMCA/IChcbiAgICAgICAgICA8RmlsZUVkaXRUb29sRGlmZiBmaWxlX3BhdGg9e2ZpbGVQYXRofSBlZGl0cz17ZWRpdHN9IC8+XG4gICAgICAgICkgOiAoXG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+e25vQ2hhbmdlc01lc3NhZ2V9PC9UZXh0PlxuICAgICAgICApXG4gICAgICB9XG4gICAgICBwYXRoPXtmaWxlUGF0aH1cbiAgICAgIGNvbXBsZXRpb25UeXBlPVwic3RyX3JlcGxhY2Vfc2luZ2xlXCJcbiAgICAgIHBhcnNlSW5wdXQ9e3BhcnNlSW5wdXR9XG4gICAgICB3b3JrZXJCYWRnZT17cHJvcHMud29ya2VyQmFkZ2V9XG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsUUFBUSxFQUFFQyxRQUFRLFFBQVEsTUFBTTtBQUN6QyxPQUFPQyxLQUFLLElBQUlDLFFBQVEsRUFBRUMsR0FBRyxFQUFF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 \ No newline at end of file diff --git a/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx b/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx new file mode 100644 index 0000000..346f846 --- /dev/null +++ b/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx @@ -0,0 +1,369 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useCallback, useMemo } from 'react'; +import { logError } from 'src/utils/log.js'; +import { getOriginalCwd } from '../../../bootstrap/state.js'; +import { Box, Text } from '../../../ink.js'; +import { sanitizeToolNameForAnalytics } from '../../../services/analytics/metadata.js'; +import { SKILL_TOOL_NAME } from '../../../tools/SkillTool/constants.js'; +import { SkillTool } from '../../../tools/SkillTool/SkillTool.js'; +import { env } from '../../../utils/env.js'; +import { shouldShowAlwaysAllowOptions } from '../../../utils/permissions/permissionsLoader.js'; +import { logUnaryEvent } from '../../../utils/unaryLogging.js'; +import { type UnaryEvent, usePermissionRequestLogging } from '../hooks.js'; +import { PermissionDialog } from '../PermissionDialog.js'; +import { PermissionPrompt, type PermissionPromptOption, type ToolAnalyticsContext } from '../PermissionPrompt.js'; +import type { PermissionRequestProps } from '../PermissionRequest.js'; +import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js'; +type SkillOptionValue = 'yes' | 'yes-exact' | 'yes-prefix' | 'no'; +export function SkillPermissionRequest(props) { + const $ = _c(51); + const { + toolUseConfirm, + onDone, + onReject, + workerBadge + } = props; + const parseInput = _temp; + let t0; + if ($[0] !== toolUseConfirm.input) { + t0 = parseInput(toolUseConfirm.input); + $[0] = toolUseConfirm.input; + $[1] = t0; + } else { + t0 = $[1]; + } + const skill = t0; + const commandObj = toolUseConfirm.permissionResult.behavior === "ask" && toolUseConfirm.permissionResult.metadata && "command" in toolUseConfirm.permissionResult.metadata ? toolUseConfirm.permissionResult.metadata.command : undefined; + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + completion_type: "tool_use_single", + language_name: "none" + }; + $[2] = t1; + } else { + t1 = $[2]; + } + const unaryEvent = t1; + usePermissionRequestLogging(toolUseConfirm, unaryEvent); + let t2; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t2 = getOriginalCwd(); + $[3] = t2; + } else { + t2 = $[3]; + } + const originalCwd = t2; + let t3; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t3 = shouldShowAlwaysAllowOptions(); + $[4] = t3; + } else { + t3 = $[4]; + } + const showAlwaysAllowOptions = t3; + let t4; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t4 = [{ + label: "Yes", + value: "yes", + feedbackConfig: { + type: "accept" + } + }]; + $[5] = t4; + } else { + t4 = $[5]; + } + const baseOptions = t4; + let alwaysAllowOptions; + if ($[6] !== skill) { + alwaysAllowOptions = []; + if (showAlwaysAllowOptions) { + const t5 = {skill}; + let t6; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t6 = {originalCwd}; + $[8] = t6; + } else { + t6 = $[8]; + } + let t7; + if ($[9] !== t5) { + t7 = { + label: Yes, and don't ask again for {t5} in{" "}{t6}, + value: "yes-exact" + }; + $[9] = t5; + $[10] = t7; + } else { + t7 = $[10]; + } + alwaysAllowOptions.push(t7); + const spaceIndex = skill.indexOf(" "); + if (spaceIndex > 0) { + const commandPrefix = skill.substring(0, spaceIndex); + const t8 = commandPrefix + ":*"; + let t9; + if ($[11] !== t8) { + t9 = {t8}; + $[11] = t8; + $[12] = t9; + } else { + t9 = $[12]; + } + let t10; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t10 = {originalCwd}; + $[13] = t10; + } else { + t10 = $[13]; + } + let t11; + if ($[14] !== t9) { + t11 = { + label: Yes, and don't ask again for{" "}{t9} commands in{" "}{t10}, + value: "yes-prefix" + }; + $[14] = t9; + $[15] = t11; + } else { + t11 = $[15]; + } + alwaysAllowOptions.push(t11); + } + } + $[6] = skill; + $[7] = alwaysAllowOptions; + } else { + alwaysAllowOptions = $[7]; + } + let t5; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + label: "No", + value: "no", + feedbackConfig: { + type: "reject" + } + }; + $[16] = t5; + } else { + t5 = $[16]; + } + const noOption = t5; + let t6; + if ($[17] !== alwaysAllowOptions) { + t6 = [...baseOptions, ...alwaysAllowOptions, noOption]; + $[17] = alwaysAllowOptions; + $[18] = t6; + } else { + t6 = $[18]; + } + const options = t6; + let t7; + if ($[19] !== toolUseConfirm.tool.name) { + t7 = sanitizeToolNameForAnalytics(toolUseConfirm.tool.name); + $[19] = toolUseConfirm.tool.name; + $[20] = t7; + } else { + t7 = $[20]; + } + const t8 = toolUseConfirm.tool.isMcp ?? false; + let t9; + if ($[21] !== t7 || $[22] !== t8) { + t9 = { + toolName: t7, + isMcp: t8 + }; + $[21] = t7; + $[22] = t8; + $[23] = t9; + } else { + t9 = $[23]; + } + const toolAnalyticsContext = t9; + let t10; + if ($[24] !== onDone || $[25] !== onReject || $[26] !== skill || $[27] !== toolUseConfirm) { + t10 = (value, feedback) => { + bb33: switch (value) { + case "yes": + { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "accept", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + toolUseConfirm.onAllow(toolUseConfirm.input, [], feedback); + onDone(); + break bb33; + } + case "yes-exact": + { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "accept", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + toolUseConfirm.onAllow(toolUseConfirm.input, [{ + type: "addRules", + rules: [{ + toolName: SKILL_TOOL_NAME, + ruleContent: skill + }], + behavior: "allow", + destination: "localSettings" + }]); + onDone(); + break bb33; + } + case "yes-prefix": + { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "accept", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + const spaceIndex_0 = skill.indexOf(" "); + const commandPrefix_0 = spaceIndex_0 > 0 ? skill.substring(0, spaceIndex_0) : skill; + toolUseConfirm.onAllow(toolUseConfirm.input, [{ + type: "addRules", + rules: [{ + toolName: SKILL_TOOL_NAME, + ruleContent: `${commandPrefix_0}:*` + }], + behavior: "allow", + destination: "localSettings" + }]); + onDone(); + break bb33; + } + case "no": + { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "reject", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + toolUseConfirm.onReject(feedback); + onReject(); + onDone(); + } + } + }; + $[24] = onDone; + $[25] = onReject; + $[26] = skill; + $[27] = toolUseConfirm; + $[28] = t10; + } else { + t10 = $[28]; + } + const handleSelect = t10; + let t11; + if ($[29] !== onDone || $[30] !== onReject || $[31] !== toolUseConfirm) { + t11 = () => { + logUnaryEvent({ + completion_type: "tool_use_single", + event: "reject", + metadata: { + language_name: "none", + message_id: toolUseConfirm.assistantMessage.message.id, + platform: env.platform + } + }); + toolUseConfirm.onReject(); + onReject(); + onDone(); + }; + $[29] = onDone; + $[30] = onReject; + $[31] = toolUseConfirm; + $[32] = t11; + } else { + t11 = $[32]; + } + const handleCancel = t11; + const t12 = `Use skill "${skill}"?`; + let t13; + if ($[33] === Symbol.for("react.memo_cache_sentinel")) { + t13 = Claude may use instructions, code, or files from this Skill.; + $[33] = t13; + } else { + t13 = $[33]; + } + const t14 = commandObj?.description; + let t15; + if ($[34] !== t14) { + t15 = {t14}; + $[34] = t14; + $[35] = t15; + } else { + t15 = $[35]; + } + let t16; + if ($[36] !== toolUseConfirm.permissionResult) { + t16 = ; + $[36] = toolUseConfirm.permissionResult; + $[37] = t16; + } else { + t16 = $[37]; + } + let t17; + if ($[38] !== handleCancel || $[39] !== handleSelect || $[40] !== options || $[41] !== toolAnalyticsContext) { + t17 = ; + $[38] = handleCancel; + $[39] = handleSelect; + $[40] = options; + $[41] = toolAnalyticsContext; + $[42] = t17; + } else { + t17 = $[42]; + } + let t18; + if ($[43] !== t16 || $[44] !== t17) { + t18 = {t16}{t17}; + $[43] = t16; + $[44] = t17; + $[45] = t18; + } else { + t18 = $[45]; + } + let t19; + if ($[46] !== t12 || $[47] !== t15 || $[48] !== t18 || $[49] !== workerBadge) { + t19 = {t13}{t15}{t18}; + $[46] = t12; + $[47] = t15; + $[48] = t18; + $[49] = workerBadge; + $[50] = t19; + } else { + t19 = $[50]; + } + return t19; +} +function _temp(input) { + const result = SkillTool.inputSchema.safeParse(input); + if (!result.success) { + logError(new Error(`Failed to parse skill tool input: ${result.error.message}`)); + return ""; + } + return result.data.skill; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlTWVtbyIsImxvZ0Vycm9yIiwiZ2V0T3JpZ2luYWxDd2QiLCJCb3giLCJUZXh0Iiwic2FuaXRpemVUb29sTmFtZUZvckFuYWx5dGljcyIsIlNLSUxMX1RPT0xfTkFNRSIsIlNraWxsVG9vbCIsImVudiIsInNob3VsZFNob3dBbHdheXNBbGxvd09wdGlvbnMiLCJsb2dVbmFyeUV2ZW50IiwiVW5hcnlFdmVudCIsInVzZVBlcm1pc3Npb25SZXF1ZXN0TG9nZ2luZyIsIlBlcm1pc3Npb25EaWFsb2ciLCJQZXJtaXNzaW9uUHJvbXB0IiwiUGVybWlzc2lvblByb21wdE9wdGlvbiIsIlRvb2xBbmFseXRpY3NDb250ZXh0IiwiUGVybWlzc2lvblJlcXVlc3RQcm9wcyIsIlBlcm1pc3Npb25SdWxlRXhwbGFuYXRpb24iLCJTa2lsbE9wdGlvblZhbHVlIiwiU2tpbGxQZXJtaXNzaW9uUmVxdWVzdCIsInByb3BzIiwiJCIsIl9jIiwidG9vbFVzZUNvbmZpcm0iLCJvbkRvbmUiLCJvblJlamVjdCIsIndvcmtlckJhZGdlIiwicGFyc2VJbnB1dCIsIl90ZW1wIiwidDAiLCJpbnB1dCIsInNraWxsIiwiY29tbWFuZE9iaiIsInBlcm1pc3Npb25SZXN1bHQiLCJiZWhhdmlvciIsIm1ldGFkYXRhIiwiY29tbWFuZCIsInVuZGVmaW5lZCIsInQxIiwiU3ltYm9sIiwiZm9yIiwiY29tcGxldGlvbl90eXBlIiwibGFuZ3VhZ2VfbmFtZSIsInVuYXJ5RXZlbnQiLCJ0MiIsIm9yaWdpbmFsQ3dkIiwidDMiLCJzaG93QWx3YXlzQWxsb3dPcHRpb25zIiwidDQiLCJsYWJlbCIsInZhbHVlIiwiZmVlZGJhY2tDb25maWciLCJ0eXBlIiwiYmFzZU9wdGlvbnMiLCJhbHdheXNBbGxvd09wdGlvbnMiLCJ0NSIsInQ2IiwidDciLCJwdXNoIiwic3BhY2VJbmRleCIsImluZGV4T2YiLCJjb21tYW5kUHJlZml4Iiwic3Vic3RyaW5nIiwidDgiLCJ0OSIsInQxMCIsInQxMSIsIm5vT3B0aW9uIiwib3B0aW9ucyIsInRvb2wiLCJuYW1lIiwiaXNNY3AiLCJ0b29sTmFtZSIsInRvb2xBbmFseXRpY3NDb250ZXh0IiwiZmVlZGJhY2siLCJiYjMzIiwiZXZlbnQiLCJtZXNzYWdlX2lkIiwiYXNzaXN0YW50TWVzc2FnZSIsIm1lc3NhZ2UiLCJpZCIsInBsYXRmb3JtIiwib25BbGxvdyIsInJ1bGVzIiwicnVsZUNvbnRlbnQiLCJkZXN0aW5hdGlvbiIsInNwYWNlSW5kZXhfMCIsImNvbW1hbmRQcmVmaXhfMCIsImhhbmRsZVNlbGVjdCIsImhhbmRsZUNhbmNlbCIsInQxMiIsInQxMyIsInQxNCIsImRlc2NyaXB0aW9uIiwidDE1IiwidDE2IiwidDE3IiwidDE4IiwidDE5IiwicmVzdWx0IiwiaW5wdXRTY2hlbWEiLCJzYWZlUGFyc2UiLCJzdWNjZXNzIiwiRXJyb3IiLCJlcnJvciIsImRhdGEiXSwic291cmNlcyI6WyJTa2lsbFBlcm1pc3Npb25SZXF1ZXN0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgdXNlQ2FsbGJhY2ssIHVzZU1lbW8gfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnc3JjL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7IGdldE9yaWdpbmFsQ3dkIH0gZnJvbSAnLi4vLi4vLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgc2FuaXRpemVUb29sTmFtZUZvckFuYWx5dGljcyB9IGZyb20gJy4uLy4uLy4uL3NlcnZpY2VzL2FuYWx5dGljcy9tZXRhZGF0YS5qcydcbmltcG9ydCB7IFNLSUxMX1RPT0xfTkFNRSB9IGZyb20gJy4uLy4uLy4uL3Rvb2xzL1NraWxsVG9vbC9jb25zdGFudHMuanMnXG5pbXBvcnQgeyBTa2lsbFRvb2wgfSBmcm9tICcuLi8uLi8uLi90b29scy9Ta2lsbFRvb2wvU2tpbGxUb29sLmpzJ1xuaW1wb3J0IHsgZW52IH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvZW52LmpzJ1xuaW1wb3J0IHsgc2hvdWxkU2hvd0Fsd2F5c0FsbG93T3B0aW9ucyB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL3Blcm1pc3Npb25zTG9hZGVyLmpzJ1xuaW1wb3J0IHsgbG9nVW5hcnlFdmVudCB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL3VuYXJ5TG9nZ2luZy5qcydcbmltcG9ydCB7IHR5cGUgVW5hcnlFdmVudCwgdXNlUGVybWlzc2lvblJlcXVlc3RMb2dnaW5nIH0gZnJvbSAnLi4vaG9va3MuanMnXG5pbXBvcnQgeyBQZXJtaXNzaW9uRGlhbG9nIH0gZnJvbSAnLi4vUGVybWlzc2lvbkRpYWxvZy5qcydcbmltcG9ydCB7XG4gIFBlcm1pc3Npb25Qcm9tcHQsXG4gIHR5cGUgUGVybWlzc2lvblByb21wdE9wdGlvbixcbiAgdHlwZSBUb29sQW5hbHl0aWNzQ29udGV4dCxcbn0gZnJvbSAnLi4vUGVybWlzc2lvblByb21wdC5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvblJlcXVlc3RQcm9wcyB9IGZyb20gJy4uL1Blcm1pc3Npb25SZXF1ZXN0LmpzJ1xuaW1wb3J0IHsgUGVybWlzc2lvblJ1bGVFeHBsYW5hdGlvbiB9IGZyb20gJy4uL1Blcm1pc3Npb25SdWxlRXhwbGFuYXRpb24uanMnXG5cbnR5cGUgU2tpbGxPcHRpb25WYWx1ZSA9ICd5ZXMnIHwgJ3llcy1leGFjdCcgfCAneWVzLXByZWZpeCcgfCAnbm8nXG5cbmV4cG9ydCBmdW5jdGlvbiBTa2lsbFBlcm1pc3Npb25SZXF1ZXN0KFxuICBwcm9wczogUGVybWlzc2lvblJlcXVlc3RQcm9wcyxcbik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHtcbiAgICB0b29sVXNlQ29uZmlybSxcbiAgICBvbkRvbmUsXG4gICAgb25SZWplY3QsXG4gICAgdmVyYm9zZTogX3ZlcmJvc2UsXG4gICAgd29ya2VyQmFkZ2UsXG4gIH0gPSBwcm9wc1xuICBjb25zdCBwYXJzZUlucHV0ID0gKGlucHV0OiB1bmtub3duKTogc3RyaW5nID0+IHtcbiAgICBjb25zdCByZXN1bHQgPSBTa2lsbFRvb2wuaW5wdXRTY2hlbWEuc2FmZVBhcnNlKGlucHV0KVxuICAgIGlmICghcmVzdWx0LnN1Y2Nlc3MpIHtcbiAgICAgIGxvZ0Vycm9yKFxuICAgICAgICBuZXcgRXJyb3IoYEZhaWxlZCB0byBwYXJzZSBza2lsbCB0b29sIGlucHV0OiAke3Jlc3VsdC5lcnJvci5tZXNzYWdlfWApLFxuICAgICAgKVxuICAgICAgcmV0dXJuICcnXG4gICAgfVxuICAgIHJldHVybiByZXN1bHQuZGF0YS5za2lsbFxuICB9XG5cbiAgY29uc3Qgc2tpbGwgPSBwYXJzZUlucHV0KHRvb2xVc2VDb25maXJtLmlucHV0KVxuXG4gIC8vIENoZWNrIGlmIHRoaXMgaXMgYSBjb21tYW5kIHVzaW5nIG1ldGFkYXRhIGZyb20gY2hlY2tQZXJtaXNzaW9uc1xuICBjb25zdCBjb21tYW5kT2JqID1cbiAgICB0b29sVXNlQ29uZmlybS5wZXJtaXNzaW9uUmVzdWx0LmJlaGF2aW9yID09PSAnYXNrJyAmJlxuICAgIHRvb2xVc2VDb25maXJtLnBlcm1pc3Npb25SZXN1bHQubWV0YWRhdGEgJiZcbiAgICAnY29tbWFuZCcgaW4gdG9vbFVzZUNvbmZpcm0ucGVybWlzc2lvblJlc3VsdC5tZXRhZGF0YVxuICAgICAgPyB0b29sVXNlQ29uZmlybS5wZXJtaXNzaW9uUmVzdWx0Lm1ldGFkYXRhLmNvbW1hbmRcbiAgICAgIDogdW5kZWZpbmVkXG5cbiAgY29uc3QgdW5hcnlFdmVudCA9IHVzZU1lbW88VW5hcnlFdmVudD4oXG4gICAgKCkgPT4gKHtcbiAgICAgIGNvbXBsZXRpb25fdHlwZTogJ3Rvb2xfdXNlX3NpbmdsZScsXG4gICAgICBsYW5ndWFnZV9uYW1lOiAnbm9uZScsXG4gICAgfSksXG4gICAgW10sXG4gIClcblxuICB1c2VQZXJtaXNzaW9uUmVxdWVzdExvZ2dpbmcodG9vbFVzZUNvbmZpcm0sIHVuYXJ5RXZlbnQpXG5cbiAgY29uc3Qgb3JpZ2luYWxDd2QgPSBnZXRPcmlnaW5hbEN3ZCgpXG4gIGNvbnN0IHNob3dBbHdheXNBbGxvd09wdGlvbnMgPSBzaG91bGRTaG93QWx3YXlzQWxsb3dPcHRpb25zKClcbiAgY29uc3Qgb3B0aW9ucyA9IHVzZU1lbW8oKCk6IFBlcm1pc3Npb25Qcm9tcHRPcHRpb248U2tpbGxPcHRpb25WYWx1ZT5bXSA9PiB7XG4gICAgY29uc3QgYmFzZU9wdGlvbnM6IFBlcm1pc3Npb25Qcm9tcHRPcHRpb248U2tpbGxPcHRpb25WYWx1ZT5bXSA9IFtcbiAgICAgIHtcbiAgICAgICAgbGFiZWw6ICdZZXMnLFxuICAgICAgICB2YWx1ZTogJ3llcycsXG4gICAgICAgIGZlZWRiYWNrQ29uZmlnOiB7IHR5cGU6ICdhY2NlcHQnIH0sXG4gICAgICB9LFxuICAgIF1cblxuICAgIC8vIE9ubHkgYWRkIFwiYWx3YXlzIGFsbG93XCIgb3B0aW9ucyB3aGVuIG5vdCByZXN0cmljdGVkIGJ5IGFsbG93TWFuYWdlZFBlcm1pc3Npb25SdWxlc09ubHlcbiAgICBjb25zdCBhbHdheXNBbGxvd09wdGlvbnM6IFBlcm1pc3Npb25Qcm9tcHRPcHRpb248U2tpbGxPcHRpb25WYWx1ZT5bXSA9IFtdXG4gICAgaWYgKHNob3dBbHdheXNBbGxvd09wdGlvbnMpIHtcbiAgICAgIC8vIEFkZCBleGFjdCBtYXRjaCBvcHRpb25cbiAgICAgIGFsd2F5c0FsbG93T3B0aW9ucy5wdXNoKHtcbiAgICAgICAgbGFiZWw6IChcbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIFllcywgYW5kIGRvbiZhcG9zO3QgYXNrIGFnYWluIGZvciA8VGV4dCBib2xkPntza2lsbH08L1RleHQ+IGlueycgJ31cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+e29yaWdpbmFsQ3dkfTwvVGV4dD5cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICksXG4gICAgICAgIHZhbHVlOiAneWVzLWV4YWN0JyxcbiAgICAgIH0pXG5cbiAgICAgIC8vIEFkZCBwcmVmaXggb3B0aW9uIGlmIHRoZSBza2lsbCBoYXMgYXJndW1lbnRzXG4gICAgICBjb25zdCBzcGFjZUluZGV4ID0gc2tpbGwuaW5kZXhPZignICcpXG4gICAgICBpZiAoc3BhY2VJbmRleCA+IDApIHtcbiAgICAgICAgY29uc3QgY29tbWFuZFByZWZpeCA9IHNraWxsLnN1YnN0cmluZygwLCBzcGFjZUluZGV4KVxuICAgICAgICBhbHdheXNBbGxvd09wdGlvbnMucHVzaCh7XG4gICAgICAgICAgbGFiZWw6IChcbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICBZZXMsIGFuZCBkb24mYXBvczt0IGFzayBhZ2FpbiBmb3J7JyAnfVxuICAgICAgICAgICAgICA8VGV4dCBib2xkPntjb21tYW5kUHJlZml4ICsgJzoqJ308L1RleHQ+IGNvbW1hbmRzIGlueycgJ31cbiAgICAgICAgICAgICAgPFRleHQgYm9sZD57b3JpZ2luYWxDd2R9PC9UZXh0PlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICksXG4gICAgICAgICAgdmFsdWU6ICd5ZXMtcHJlZml4JyxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBub09wdGlvbjogUGVybWlzc2lvblByb21wdE9wdGlvbjxTa2lsbE9wdGlvblZhbHVlPiA9IHtcbiAgICAgIGxhYmVsOiAnTm8nLFxuICAgICAgdmFsdWU6ICdubycsXG4gICAgICBmZWVkYmFja0NvbmZpZzogeyB0eXBlOiAncmVqZWN0JyB9LFxuICAgIH1cblxuICAgIHJldHVybiBbLi4uYmFzZU9wdGlvbnMsIC4uLmFsd2F5c0FsbG93T3B0aW9ucywgbm9PcHRpb25dXG4gIH0sIFtza2lsbCwgb3JpZ2luYWxDd2QsIHNob3dBbHdheXNBbGxvd09wdGlvbnNdKVxuXG4gIGNvbnN0IHRvb2xBbmFseXRpY3NDb250ZXh0ID0gdXNlTWVtbyhcbiAgICAoKTogVG9vbEFuYWx5dGljc0NvbnRleHQgPT4gKHtcbiAgICAgIHRvb2xOYW1lOiBzYW5pdGl6ZVRvb2xOYW1lRm9yQW5hbHl0aWNzKHRvb2xVc2VDb25maXJtLnRvb2wubmFtZSksXG4gICAgICBpc01jcDogdG9vbFVzZUNvbmZpcm0udG9vbC5pc01jcCA/PyBmYWxzZSxcbiAgICB9KSxcbiAgICBbdG9vbFVzZUNvbmZpcm0udG9vbC5uYW1lLCB0b29sVXNlQ29uZmlybS50b29sLmlzTWNwXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZVNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgICh2YWx1ZTogU2tpbGxPcHRpb25WYWx1ZSwgZmVlZGJhY2s/OiBzdHJpbmcpID0+IHtcbiAgICAgIHN3aXRjaCAodmFsdWUpIHtcbiAgICAgICAgY2FzZSAneWVzJzpcbiAgICAgICAgICB2b2lkIGxvZ1VuYXJ5RXZlbnQoe1xuICAgICAgICAgICAgY29tcGxldGlvbl90eXBlOiAndG9vbF91c2Vfc2luZ2xlJyxcbiAgICAgICAgICAgIGV2ZW50OiAnYWNjZXB0JyxcbiAgICAgICAgICAgIG1ldGFkYXRhOiB7XG4gICAgICAgICAgICAgIGxhbmd1YWdlX25hbWU6ICdub25lJyxcbiAgICAgICAgICAgICAgbWVzc2FnZV9pZDogdG9vbFVzZUNvbmZpcm0uYXNzaXN0YW50TWVzc2FnZS5tZXNzYWdlLmlkLFxuICAgICAgICAgICAgICBwbGF0Zm9ybTogZW52LnBsYXRmb3JtLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KVxuICAgICAgICAgIHRvb2xVc2VDb25maXJtLm9uQWxsb3codG9vbFVzZUNvbmZpcm0uaW5wdXQsIFtdLCBmZWVkYmFjaylcbiAgICAgICAgICBvbkRvbmUoKVxuICAgICAgICAgIGJyZWFrXG4gICAgICAgIGNhc2UgJ3llcy1leGFjdCc6IHtcbiAgICAgICAgICB2b2lkIGxvZ1VuYXJ5RXZlbnQoe1xuICAgICAgICAgICAgY29tcGxldGlvbl90eXBlOiAndG9vbF91c2Vfc2luZ2xlJyxcbiAgICAgICAgICAgIGV2ZW50OiAnYWNjZXB0JyxcbiAgICAgICAgICAgIG1ldGFkYXRhOiB7XG4gICAgICAgICAgICAgIGxhbmd1YWdlX25hbWU6ICdub25lJyxcbiAgICAgICAgICAgICAgbWVzc2FnZV9pZDogdG9vbFVzZUNvbmZpcm0uYXNzaXN0YW50TWVzc2FnZS5tZXNzYWdlLmlkLFxuICAgICAgICAgICAgICBwbGF0Zm9ybTogZW52LnBsYXRmb3JtLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9KVxuXG4gICAgICAgICAgdG9vbFVzZUNvbmZpcm0ub25BbGxvdyh0b29sVXNlQ29uZmlybS5pbnB1dCwgW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICB0eXBlOiAnYWRkUnVsZXMnLFxuICAgICAgICAgICAgICBydWxlczogW1xuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIHRvb2xOYW1lOiBTS0lMTF9UT09MX05BTUUsXG4gICAgICAgICAgICAgICAgICBydWxlQ29udGVudDogc2tpbGwsXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgXSxcbiAgICAgICAgICAgICAgYmVoYXZpb3I6ICdhbGxvdycsXG4gICAgICAgICAgICAgIGRlc3RpbmF0aW9uOiAnbG9jYWxTZXR0aW5ncycsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIF0pXG4gICAgICAgICAgb25Eb25lKClcbiAgICAgICAgICBicmVha1xuICAgICAgICB9XG4gICAgICAgIGNhc2UgJ3llcy1wcmVmaXgnOiB7XG4gICAgICAgICAgdm9pZCBsb2dVbmFyeUV2ZW50KHtcbiAgICAgICAgICAgIGNvbXBsZXRpb25fdHlwZTogJ3Rvb2xfdXNlX3NpbmdsZScsXG4gICAgICAgICAgICBldmVudDogJ2FjY2VwdCcsXG4gICAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAgICBsYW5ndWFnZV9uYW1lOiAnbm9uZScsXG4gICAgICAgICAgICAgIG1lc3NhZ2VfaWQ6IHRvb2xVc2VDb25maXJtLmFzc2lzdGFudE1lc3NhZ2UubWVzc2FnZS5pZCxcbiAgICAgICAgICAgICAgcGxhdGZvcm06IGVudi5wbGF0Zm9ybSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSlcblxuICAgICAgICAgIC8vIEV4dHJhY3QgdGhlIHNraWxsIHByZWZpeCAoZXZlcnl0aGluZyBiZWZvcmUgdGhlIGZpcnN0IHNwYWNlKVxuICAgICAgICAgIGNvbnN0IHNwYWNlSW5kZXggPSBza2lsbC5pbmRleE9mKCcgJylcbiAgICAgICAgICBjb25zdCBjb21tYW5kUHJlZml4ID1cbiAgICAgICAgICAgIHNwYWNlSW5kZXggPiAwID8gc2tpbGwuc3Vic3RyaW5nKDAsIHNwYWNlSW5kZXgpIDogc2tpbGxcblxuICAgICAgICAgIHRvb2xVc2VDb25maXJtLm9uQWxsb3codG9vbFVzZUNvbmZpcm0uaW5wdXQsIFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgdHlwZTogJ2FkZFJ1bGVzJyxcbiAgICAgICAgICAgICAgcnVsZXM6IFtcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICB0b29sTmFtZTogU0tJTExfVE9PTF9OQU1FLFxuICAgICAgICAgICAgICAgICAgcnVsZUNvbnRlbnQ6IGAke2NvbW1hbmRQcmVmaXh9OipgLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIF0sXG4gICAgICAgICAgICAgIGJlaGF2aW9yOiAnYWxsb3cnLFxuICAgICAgICAgICAgICBkZXN0aW5hdGlvbjogJ2xvY2FsU2V0dGluZ3MnLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICBdKVxuICAgICAgICAgIG9uRG9uZSgpXG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgICBjYXNlICdubyc6XG4gICAgICAgICAgdm9pZCBsb2dVbmFyeUV2ZW50KHtcbiAgICAgICAgICAgIGNvbXBsZXRpb25fdHlwZTogJ3Rvb2xfdXNlX3NpbmdsZScsXG4gICAgICAgICAgICBldmVudDogJ3JlamVjdCcsXG4gICAgICAgICAgICBtZXRhZGF0YToge1xuICAgICAgICAgICAgICBsYW5ndWFnZV9uYW1lOiAnbm9uZScsXG4gICAgICAgICAgICAgIG1lc3NhZ2VfaWQ6IHRvb2xVc2VDb25maXJtLmFzc2lzdGFudE1lc3NhZ2UubWVzc2FnZS5pZCxcbiAgICAgICAgICAgICAgcGxhdGZvcm06IGVudi5wbGF0Zm9ybSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSlcbiAgICAgICAgICB0b29sVXNlQ29uZmlybS5vblJlamVjdChmZWVkYmFjaylcbiAgICAgICAgICBvblJlamVjdCgpXG4gICAgICAgICAgb25Eb25lKClcbiAgICAgICAgICBicmVha1xuICAgICAgfVxuICAgIH0sXG4gICAgW3Rvb2xVc2VDb25maXJtLCBvbkRvbmUsIG9uUmVqZWN0LCBza2lsbF0sXG4gIClcblxuICBjb25zdCBoYW5kbGVDYW5jZWwgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgdm9pZCBsb2dVbmFyeUV2ZW50KHtcbiAgICAgIGNvbXBsZXRpb25fdHlwZTogJ3Rvb2xfdXNlX3NpbmdsZScsXG4gICAgICBldmVudDogJ3JlamVjdCcsXG4gICAgICBtZXRhZGF0YToge1xuICAgICAgICBsYW5ndWFnZV9uYW1lOiAnbm9uZScsXG4gICAgICAgIG1lc3NhZ2VfaWQ6IHRvb2xVc2VDb25maXJtLmFzc2lzdGFudE1lc3NhZ2UubWVzc2FnZS5pZCxcbiAgICAgICAgcGxhdGZvcm06IGVudi5wbGF0Zm9ybSxcbiAgICAgIH0sXG4gICAgfSlcbiAgICB0b29sVXNlQ29uZmlybS5vblJlamVjdCgpXG4gICAgb25SZWplY3QoKVxuICAgIG9uRG9uZSgpXG4gIH0sIFt0b29sVXNlQ29uZmlybSwgb25Eb25lLCBvblJlamVjdF0pXG5cbiAgcmV0dXJuIChcbiAgICA8UGVybWlzc2lvbkRpYWxvZyB0aXRsZT17YFVzZSBza2lsbCBcIiR7c2tpbGx9XCI/YH0gd29ya2VyQmFkZ2U9e3dvcmtlckJhZGdlfT5cbiAgICAgIDxUZXh0PkNsYXVkZSBtYXkgdXNlIGluc3RydWN0aW9ucywgY29kZSwgb3IgZmlsZXMgZnJvbSB0aGlzIFNraWxsLjwvVGV4dD5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdYPXsyfSBwYWRkaW5nWT17MX0+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPntjb21tYW5kT2JqPy5kZXNjcmlwdGlvbn08L1RleHQ+XG4gICAgICA8L0JveD5cblxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIDxQZXJtaXNzaW9uUnVsZUV4cGxhbmF0aW9uXG4gICAgICAgICAgcGVybWlzc2lvblJlc3VsdD17dG9vbFVzZUNvbmZpcm0ucGVybWlzc2lvblJlc3VsdH1cbiAgICAgICAgICB0b29sVHlwZT1cInRvb2xcIlxuICAgICAgICAvPlxuICAgICAgICA8UGVybWlzc2lvblByb21wdFxuICAgICAgICAgIG9wdGlvbnM9e29wdGlvbnN9XG4gICAgICAgICAgb25TZWxlY3Q9e2hhbmRsZVNlbGVjdH1cbiAgICAgICAgICBvbkNhbmNlbD17aGFuZGxlQ2FuY2VsfVxuICAgICAgICAgIHRvb2xBbmFseXRpY3NDb250ZXh0PXt0b29sQW5hbHl0aWNzQ29udGV4dH1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIDwvUGVybWlzc2lvbkRpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJQyxXQUFXLEVBQUVDLE9BQU8sUUFBUSxPQUFPO0FBQ25ELFNBQVNDLFFBQVEsUUFBUSxrQkFBa0I7QUFDM0MsU0FBU0MsY0FBYyxRQUFRLDZCQUE2QjtBQUM1RCxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxpQkFBaUI7QUFDM0MsU0FBU0MsNEJBQTRCLFFBQVEseUNBQXlDO0FBQ3RGLFNBQVNDLGVBQWUsUUFBUSx1Q0FBdUM7QUFDdkUsU0FBU0MsU0FBUyxRQUFRLHVDQUF1QztBQUNqRSxTQUFTQyxHQUFHLFFBQVEsdUJBQXVCO0FBQzNDLFNBQVNDLDRCQUE0QixRQUFRLGlEQUFpRDtB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 \ No newline at end of file diff --git a/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx b/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx new file mode 100644 index 0000000..28a548c --- /dev/null +++ b/components/permissions/WebFetchPermissionRequest/WebFetchPermissionRequest.tsx @@ -0,0 +1,258 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useMemo } from 'react'; +import { Box, Text, useTheme } from '../../../ink.js'; +import { WebFetchTool } from '../../../tools/WebFetchTool/WebFetchTool.js'; +import { shouldShowAlwaysAllowOptions } from '../../../utils/permissions/permissionsLoader.js'; +import { type OptionWithDescription, Select } from '../../CustomSelect/select.js'; +import { type UnaryEvent, usePermissionRequestLogging } from '../hooks.js'; +import { PermissionDialog } from '../PermissionDialog.js'; +import type { PermissionRequestProps } from '../PermissionRequest.js'; +import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js'; +import { logUnaryPermissionEvent } from '../utils.js'; +function inputToPermissionRuleContent(input: { + [k: string]: unknown; +}): string { + try { + const parsedInput = WebFetchTool.inputSchema.safeParse(input); + if (!parsedInput.success) { + return `input:${input.toString()}`; + } + const { + url + } = parsedInput.data; + const hostname = new URL(url).hostname; + return `domain:${hostname}`; + } catch { + return `input:${input.toString()}`; + } +} +export function WebFetchPermissionRequest(t0) { + const $ = _c(41); + const { + toolUseConfirm, + onDone, + onReject, + verbose, + workerBadge + } = t0; + const [theme] = useTheme(); + const { + url + } = toolUseConfirm.input as { + url: string; + }; + let t1; + if ($[0] !== url) { + t1 = new URL(url); + $[0] = url; + $[1] = t1; + } else { + t1 = $[1]; + } + const hostname = t1.hostname; + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = { + completion_type: "tool_use_single", + language_name: "none" + }; + $[2] = t2; + } else { + t2 = $[2]; + } + const unaryEvent = t2; + usePermissionRequestLogging(toolUseConfirm, unaryEvent); + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = shouldShowAlwaysAllowOptions(); + $[3] = t3; + } else { + t3 = $[3]; + } + const showAlwaysAllowOptions = t3; + let t4; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t4 = { + label: "Yes", + value: "yes" + }; + $[4] = t4; + } else { + t4 = $[4]; + } + let result; + if ($[5] !== hostname) { + result = [t4]; + if (showAlwaysAllowOptions) { + const t5 = {hostname}; + let t6; + if ($[7] !== t5) { + t6 = { + label: Yes, and don't ask again for {t5}, + value: "yes-dont-ask-again-domain" + }; + $[7] = t5; + $[8] = t6; + } else { + t6 = $[8]; + } + result.push(t6); + } + let t5; + if ($[9] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + label: No, and tell Claude what to do differently (esc), + value: "no" + }; + $[9] = t5; + } else { + t5 = $[9]; + } + result.push(t5); + $[5] = hostname; + $[6] = result; + } else { + result = $[6]; + } + const options = result; + let t5; + if ($[10] !== onDone || $[11] !== onReject || $[12] !== toolUseConfirm) { + t5 = function onChange(newValue) { + bb8: switch (newValue) { + case "yes": + { + logUnaryPermissionEvent("tool_use_single", toolUseConfirm, "accept"); + toolUseConfirm.onAllow(toolUseConfirm.input, []); + onDone(); + break bb8; + } + case "yes-dont-ask-again-domain": + { + logUnaryPermissionEvent("tool_use_single", toolUseConfirm, "accept"); + const ruleContent = inputToPermissionRuleContent(toolUseConfirm.input); + const ruleValue = { + toolName: toolUseConfirm.tool.name, + ruleContent + }; + toolUseConfirm.onAllow(toolUseConfirm.input, [{ + type: "addRules", + rules: [ruleValue], + behavior: "allow", + destination: "localSettings" + }]); + onDone(); + break bb8; + } + case "no": + { + logUnaryPermissionEvent("tool_use_single", toolUseConfirm, "reject"); + toolUseConfirm.onReject(); + onReject(); + onDone(); + } + } + }; + $[10] = onDone; + $[11] = onReject; + $[12] = toolUseConfirm; + $[13] = t5; + } else { + t5 = $[13]; + } + const onChange = t5; + let t6; + if ($[14] !== theme || $[15] !== toolUseConfirm.input || $[16] !== verbose) { + t6 = WebFetchTool.renderToolUseMessage(toolUseConfirm.input as { + url: string; + prompt: string; + }, { + theme, + verbose + }); + $[14] = theme; + $[15] = toolUseConfirm.input; + $[16] = verbose; + $[17] = t6; + } else { + t6 = $[17]; + } + let t7; + if ($[18] !== t6) { + t7 = {t6}; + $[18] = t6; + $[19] = t7; + } else { + t7 = $[19]; + } + let t8; + if ($[20] !== toolUseConfirm.description) { + t8 = {toolUseConfirm.description}; + $[20] = toolUseConfirm.description; + $[21] = t8; + } else { + t8 = $[21]; + } + let t9; + if ($[22] !== t7 || $[23] !== t8) { + t9 = {t7}{t8}; + $[22] = t7; + $[23] = t8; + $[24] = t9; + } else { + t9 = $[24]; + } + let t10; + if ($[25] !== toolUseConfirm.permissionResult) { + t10 = ; + $[25] = toolUseConfirm.permissionResult; + $[26] = t10; + } else { + t10 = $[26]; + } + let t11; + if ($[27] === Symbol.for("react.memo_cache_sentinel")) { + t11 = Do you want to allow Claude to fetch this content?; + $[27] = t11; + } else { + t11 = $[27]; + } + let t12; + if ($[28] !== onChange) { + t12 = () => onChange("no"); + $[28] = onChange; + $[29] = t12; + } else { + t12 = $[29]; + } + let t13; + if ($[30] !== onChange || $[31] !== options || $[32] !== t12) { + t13 = ; + $[16] = onSelect; + $[17] = t8; + } else { + t8 = $[17]; + } + let t9; + if ($[18] !== t7 || $[19] !== t8) { + t9 = {t7}{t8}; + $[18] = t7; + $[19] = t8; + $[20] = t9; + } else { + t9 = $[20]; + } + let t10; + if ($[21] !== onCancel || $[22] !== t5 || $[23] !== t9 || $[24] !== title) { + t10 = {t5}{t9}; + $[21] = onCancel; + $[22] = t5; + $[23] = t9; + $[24] = title; + $[25] = t10; + } else { + t10 = $[25]; + } + return t10; +} +function _temp(ruleValue_0) { + return {permissionRuleValueToString(ruleValue_0)}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwiU2VsZWN0IiwiQm94IiwiVGV4dCIsIlRvb2xQZXJtaXNzaW9uQ29udGV4dCIsIlBlcm1pc3Npb25CZWhhdmlvciIsIlBlcm1pc3Npb25SdWxlIiwiUGVybWlzc2lvblJ1bGVWYWx1ZSIsImFwcGx5UGVybWlzc2lvblVwZGF0ZSIsInBlcnNpc3RQZXJtaXNzaW9uVXBkYXRlIiwicGVybWlzc2lvblJ1bGVWYWx1ZVRvU3RyaW5nIiwiZGV0ZWN0VW5yZWFjaGFibGVSdWxlcyIsIlVucmVhY2hhYmxlUnVsZSIsIlNhbmRib3hNYW5hZ2VyIiwiRWRpdGFibGVTZXR0aW5nU291cmNlIiwiU09VUkNFUyIsImdldFJlbGF0aXZlU2V0dGluZ3NGaWxlUGF0aEZvclNvdXJjZSIsInBsdXJhbCIsIk9wdGlvbldpdGhEZXNjcmlwdGlvbiIsIkRpYWxvZyIsIlBlcm1pc3Npb25SdWxlRGVzY3JpcHRpb24iLCJvcHRpb25Gb3JQZXJtaXNzaW9uU2F2ZURlc3RpbmF0aW9uIiwic2F2ZURlc3RpbmF0aW9uIiwibGFiZWwiLCJkZXNjcmlwdGlvbiIsInZhbHVlIiwiUHJvcHMiLCJvbkFkZFJ1bGVzIiwicnVsZXMiLCJ1bnJlYWNoYWJsZSIsIm9uQ2FuY2VsIiwicnVsZVZhbHVlcyIsInJ1bGVCZWhhdmlvciIsImluaXRpYWxDb250ZXh0Iiwic2V0VG9vbFBlcm1pc3Npb25Db250ZXh0IiwibmV3Q29udGV4dCIsIkFkZFBlcm1pc3Npb25SdWxlcyIsInQwIiwiJCIsIl9jIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJtYXAiLCJhbGxPcHRpb25zIiwidDIiLCJzZWxlY3RlZFZhbHVlIiwiaW5jbHVkZXMiLCJkZXN0aW5hdGlvbiIsInVwZGF0ZWRDb250ZXh0IiwidHlwZSIsImJlaGF2aW9yIiwicnVsZVZhbHVlIiwic291cmNlIiwic2FuZGJveEF1dG9BbGxvd0VuYWJsZWQiLCJpc1NhbmRib3hpbmdFbmFibGVkIiwiaXNBdXRvQWxsb3dCYXNoSWZTYW5kYm94ZWRFbmFibGVkIiwiYWxsVW5yZWFjaGFibGUiLCJuZXdVbnJlYWNoYWJsZSIsImZpbHRlciIsInUiLCJzb21lIiwicnYiLCJ0b29sTmFtZSIsInJ1bGUiLCJydWxlQ29udGVudCIsImxlbmd0aCIsInVuZGVmaW5lZCIsIm9uU2VsZWN0IiwidDMiLCJ0aXRsZSIsInQ0IiwiX3RlbXAiLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5IiwidDEwIiwicnVsZVZhbHVlXzAiXSwic291cmNlcyI6WyJBZGRQZXJtaXNzaW9uUnVsZXMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2sgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4uLy4uLy4uL2NvbXBvbmVudHMvQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uLy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgVG9vbFBlcm1pc3Npb25Db250ZXh0IH0gZnJvbSAnLi4vLi4vLi4vVG9vbC5qcydcbmltcG9ydCB0eXBlIHtcbiAgUGVybWlzc2lvbkJlaGF2aW9yLFxuICBQZXJtaXNzaW9uUnVsZSxcbiAgUGVybWlzc2lvblJ1bGVWYWx1ZSxcbn0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvblJ1bGUuanMnXG5pbXBvcnQge1xuICBhcHBseVBlcm1pc3Npb25VcGRhdGUsXG4gIHBlcnNpc3RQZXJtaXNzaW9uVXBkYXRlLFxufSBmcm9tICcuLi8uLi8uLi91dGlscy9wZXJtaXNzaW9ucy9QZXJtaXNzaW9uVXBkYXRlLmpzJ1xuaW1wb3J0IHsgcGVybWlzc2lvblJ1bGVWYWx1ZVRvU3RyaW5nIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvcGVybWlzc2lvblJ1bGVQYXJzZXIuanMnXG5pbXBvcnQge1xuICBkZXRlY3RVbnJlYWNoYWJsZVJ1bGVzLFxuICB0eXBlIFVucmVhY2hhYmxlUnVsZSxcbn0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvc2hhZG93ZWRSdWxlRGV0ZWN0aW9uLmpzJ1xuaW1wb3J0IHsgU2FuZGJveE1hbmFnZXIgfSBmcm9tICcuLi8uLi8uLi91dGlscy9zYW5kYm94L3NhbmRib3gtYWRhcHRlci5qcydcbmltcG9ydCB7XG4gIHR5cGUgRWRpdGFibGVTZXR0aW5nU291cmNlLFxuICBTT1VSQ0VTLFxufSBmcm9tICcuLi8uLi8uLi91dGlscy9zZXR0aW5ncy9jb25zdGFudHMuanMnXG5pbXBvcnQgeyBnZXRSZWxhdGl2ZVNldHRpbmdzRmlsZVBhdGhGb3JTb3VyY2UgfSBmcm9tICcuLi8uLi8uLi91dGlscy9zZXR0aW5ncy9zZXR0aW5ncy5qcydcbmltcG9ydCB7IHBsdXJhbCB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuaW1wb3J0IHR5cGUgeyBPcHRpb25XaXRoRGVzY3JpcHRpb24gfSBmcm9tICcuLi8uLi9DdXN0b21TZWxlY3Qvc2VsZWN0LmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi4vLi4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5pbXBvcnQgeyBQZXJtaXNzaW9uUnVsZURlc2NyaXB0aW9uIH0gZnJvbSAnLi9QZXJtaXNzaW9uUnVsZURlc2NyaXB0aW9uLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gb3B0aW9uRm9yUGVybWlzc2lvblNhdmVEZXN0aW5hdGlvbihcbiAgc2F2ZURlc3RpbmF0aW9uOiBFZGl0YWJsZVNldHRpbmdTb3VyY2UsXG4pOiBPcHRpb25XaXRoRGVzY3JpcHRpb24ge1xuICBzd2l0Y2ggKHNhdmVEZXN0aW5hdGlvbikge1xuICAgIGNhc2UgJ2xvY2FsU2V0dGluZ3MnOlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbGFiZWw6ICdQcm9qZWN0IHNldHRpbmdzIChsb2NhbCknLFxuICAgICAgICBkZXNjcmlwdGlvbjogYFNhdmVkIGluICR7Z2V0UmVsYXRpdmVTZXR0aW5nc0ZpbGVQYXRoRm9yU291cmNlKCdsb2NhbFNldHRpbmdzJyl9YCxcbiAgICAgICAgdmFsdWU6IHNhdmVEZXN0aW5hdGlvbixcbiAgICAgIH1cbiAgICBjYXNlICdwcm9qZWN0U2V0dGluZ3MnOlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgbGFiZWw6ICdQcm9qZWN0IHNldHRpbmdzJyxcbiAgICAgICAgZGVzY3JpcHRpb246IGBDaGVja2VkIGluIGF0ICR7Z2V0UmVsYXRpdmVTZXR0aW5nc0ZpbGVQYXRoRm9yU291cmNlKCdwcm9qZWN0U2V0dGluZ3MnKX1gLFxuICAgICAgICB2YWx1ZTogc2F2ZURlc3RpbmF0aW9uLFxuICAgICAgfVxuICAgIGNhc2UgJ3VzZXJTZXR0aW5ncyc6XG4gICAgICByZXR1cm4ge1xuICAgICAgICBsYWJlbDogJ1VzZXIgc2V0dGluZ3MnLFxuICAgICAgICBkZXNjcmlwdGlvbjogYFNhdmVkIGluIGF0IH4vLmNsYXVkZS9zZXR0aW5ncy5qc29uYCxcbiAgICAgICAgdmFsdWU6IHNhdmVEZXN0aW5hdGlvbixcbiAgICAgIH1cbiAgfVxufVxuXG50eXBlIFByb3BzID0ge1xuICBvbkFkZFJ1bGVzOiAocnVsZXM6IFBlcm1pc3Npb25SdWxlW10sIHVucmVhY2hhYmxlPzogVW5yZWFjaGFibGVSdWxlW10pID0+IHZvaWRcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbiAgcnVsZVZhbHVlczogUGVybWlzc2lvblJ1bGVWYWx1ZVtdXG4gIHJ1bGVCZWhhdmlvcjogUGVybWlzc2lvbkJlaGF2aW9yXG4gIGluaXRpYWxDb250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHRcbiAgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0OiAobmV3Q29udGV4dDogVG9vbFBlcm1pc3Npb25Db250ZXh0KSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBBZGRQZXJtaXNzaW9uUnVsZXMoe1xuICBvbkFkZFJ1bGVzLFxuICBvbkNhbmNlbCxcbiAgcnVsZVZhbHVlcyxcbiAgcnVsZUJlaGF2aW9yLFxuICBpbml0aWFsQ29udGV4dCxcbiAgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0LFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBhbGxPcHRpb25zID0gU09VUkNFUy5tYXAob3B0aW9uRm9yUGVybWlzc2lvblNhdmVEZXN0aW5hdGlvbilcblxuICBjb25zdCBvblNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgIChzZWxlY3RlZFZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICAgIGlmIChzZWxlY3RlZFZhbHVlID09PSAnY2FuY2VsJykge1xuICAgICAgICBvbkNhbmNlbCgpXG4gICAgICAgIHJldHVyblxuICAgICAgfSBlbHNlIGlmICgoU09VUkNFUyBhcyByZWFkb25seSBzdHJpbmdbXSkuaW5jbHVkZXMoc2VsZWN0ZWRWYWx1ZSkpIHtcbiAgICAgICAgY29uc3QgZGVzdGluYXRpb24gPSBzZWxlY3RlZFZhbHVlIGFzIEVkaXRhYmxlU2V0dGluZ1NvdXJjZVxuXG4gICAgICAgIGNvbnN0IHVwZGF0ZWRDb250ZXh0ID0gYXBwbHlQZXJtaXNzaW9uVXBkYXRlKGluaXRpYWxDb250ZXh0LCB7XG4gICAgICAgICAgdHlwZTogJ2FkZFJ1bGVzJyxcbiAgICAgICAgICBydWxlczogcnVsZVZhbHVlcyxcbiAgICAgICAgICBiZWhhdmlvcjogcnVsZUJlaGF2aW9yLFxuICAgICAgICAgIGRlc3RpbmF0aW9uLFxuICAgICAgICB9KVxuXG4gICAgICAgIC8vIFBlcnNpc3QgdG8gc2V0dGluZ3NcbiAgICAgICAgcGVyc2lzdFBlcm1pc3Npb25VcGRhdGUoe1xuICAgICAgICAgIHR5cGU6ICdhZGRSdWxlcycsXG4gICAgICAgICAgcnVsZXM6IHJ1bGVWYWx1ZXMsXG4gICAgICAgICAgYmVoYXZpb3I6IHJ1bGVCZWhhdmlvcixcbiAgICAgICAgICBkZXN0aW5hdGlvbixcbiAgICAgICAgfSlcblxuICAgICAgICBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQodXBkYXRlZENvbnRleHQpXG5cbiAgICAgICAgY29uc3QgcnVsZXM6IFBlcm1pc3Npb25SdWxlW10gPSBydWxlVmFsdWVzLm1hcChydWxlVmFsdWUgPT4gKHtcbiAgICAgICAgICBydWxlVmFsdWUsXG4gICAgICAgICAgcnVsZUJlaGF2aW9yLFxuICAgICAgICAgIHNvdXJjZTogZGVzdGluYXRpb24sXG4gICAgICAgIH0pKVxuXG4gICAgICAgIC8vIENoZWNrIGZvciB1bnJlYWNoYWJsZSBydWxlcyBhbW9uZyB0aGUgb25lcyB3ZSBqdXN0IGFkZGVkXG4gICAgICAgIGNvbnN0IHNhbmRib3hBdXRvQWxsb3dFbmFibGVkID1cbiAgICAgICAgICBTYW5kYm94TWFuYWdlci5pc1NhbmRib3hpbmdFbmFibGVkKCkgJiZcbiAgICAgICAgICBTYW5kYm94TWFuYWdlci5pc0F1dG9BbGxvd0Jhc2hJZlNhbmRib3hlZEVuYWJsZWQoKVxuICAgICAgICBjb25zdCBhbGxVbnJlYWNoYWJsZSA9IGRldGVjdFVucmVhY2hhYmxlUnVsZXModXBkYXRlZENvbnRleHQsIHtcbiAgICAgICAgICBzYW5kYm94QXV0b0FsbG93RW5hYmxlZCxcbiAgICAgICAgfSlcblxuICAgICAgICAvLyBGaWx0ZXIgdG8gb25seSBydWxlcyB3ZSBqdXN0IGFkZGVkXG4gICAgICAgIGNvbnN0IG5ld1VucmVhY2hhYmxlID0gYWxsVW5yZWFjaGFibGUuZmlsdGVyKHUgPT5cbiAgICAgICAgICBydWxlVmFsdWVzLnNvbWUoXG4gICAgICAgICAgICBydiA9PlxuICAgICAgICAgICAgICBydi50b29sTmFtZSA9PT0gdS5ydWxlLnJ1bGVWYWx1ZS50b29sTmFtZSAmJlxuICAgICAgICAgICAgICBydi5ydWxlQ29udGVudCA9PT0gdS5ydWxlLnJ1bGVWYWx1ZS5ydWxlQ29udGVudCxcbiAgICAgICAgICApLFxuICAgICAgICApXG5cbiAgICAgICAgb25BZGRSdWxlcyhcbiAgICAgICAgICBydWxlcyxcbiAgICAgICAgICBuZXdVbnJlYWNoYWJsZS5sZW5ndGggPiAwID8gbmV3VW5yZWFjaGFibGUgOiB1bmRlZmluZWQsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9LFxuICAgIFtcbiAgICAgIG9uQWRkUnVsZXMsXG4gICAgICBvbkNhbmNlbCxcbiAgICAgIHJ1bGVWYWx1ZXMsXG4gICAgICBydWxlQmVoYXZpb3IsXG4gICAgICBpbml0aWFsQ29udGV4dCxcbiAgICAgIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICBdLFxuICApXG5cbiAgY29uc3QgdGl0bGUgPSBgQWRkICR7cnVsZUJlaGF2aW9yfSBwZXJtaXNzaW9uICR7cGx1cmFsKHJ1bGVWYWx1ZXMubGVuZ3RoLCAncnVsZScpfWBcblxuICByZXR1cm4gKFxuICAgIDxEaWFsb2cgdGl0bGU9e3RpdGxlfSBvbkNhbmNlbD17b25DYW5jZWx9IGNvbG9yPVwicGVybWlzc2lvblwiPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgcGFkZGluZ1g9ezJ9PlxuICAgICAgICB7cnVsZVZhbHVlcy5tYXAocnVsZVZhbHVlID0+IChcbiAgICAgICAgICA8Qm94XG4gICAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICAgIGtleT17cGVybWlzc2lvblJ1bGVWYWx1ZVRvU3RyaW5nKHJ1bGVWYWx1ZSl9XG4gICAgICAgICAgPlxuICAgICAgICAgICAgPFRleHQgYm9sZD57cGVybWlzc2lvblJ1bGVWYWx1ZVRvU3RyaW5nKHJ1bGVWYWx1ZSl9PC9UZXh0PlxuICAgICAgICAgICAgPFBlcm1pc3Npb25SdWxlRGVzY3JpcHRpb24gcnVsZVZhbHVlPXtydWxlVmFsdWV9IC8+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICkpfVxuICAgICAgPC9Cb3g+XG5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpblk9ezF9PlxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICB7cnVsZVZhbHVlcy5sZW5ndGggPT09IDFcbiAgICAgICAgICAgID8gJ1doZXJlIHNob3VsZCB0aGlzIHJ1bGUgYmUgc2F2ZWQ/J1xuICAgICAgICAgICAgOiAnV2hlcmUgc2hvdWxkIHRoZXNlIHJ1bGVzIGJlIHNhdmVkPyd9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAgPFNlbGVjdCBvcHRpb25zPXthbGxPcHRpb25zfSBvbkNoYW5nZT17b25TZWxlY3R9IC8+XG4gICAgICA8L0JveD5cbiAgICA8L0RpYWxvZz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxXQUFXLFFBQVEsT0FBTztBQUNuQyxTQUFTQyxNQUFNLFFBQVEsNENBQTRDO0FBQ25FLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGlCQUFpQjtBQUMzQyxjQUFjQyxxQkFBcUIsUUFBUSxrQkFBa0I7QUFDN0QsY0FDRUMsa0JBQWtCLEVBQ2xCQyxjQUFjLEVBQ2RDLG1CQUFtQixRQUNkLDhDQUE4QztBQUNyRCxTQUNFQyxxQkFBcUIsRUFDckJDLHVCQUF1QixRQUNsQixnREFBZ0Q7QUFDdkQsU0FBU0MsMkJBQTJCLFFBQVEsb0RBQW9EO0FBQ2hHLFNBQ0VDLHNCQUFzQixFQUN0QixLQUFLQyxlQUFlLFFBQ2YscURBQXFEO0FBQzVELFNBQVNDLGNBQWMsUUFBUSwyQ0FBMkM7QUFDMUUsU0FDRSxLQUFLQyxxQkFBcUIsRUFDMUJDLE9BQU8sUUFDRixzQ0FBc0M7QUFDN0MsU0FBU0Msb0NBQW9DLFFBQVEscUNBQXFDO0FBQzFGLFNBQVNDLE1BQU0sUUFBUSwrQkFBK0I7QUFDdEQsY0FBY0MscUJBQXFCLFFBQVEsOEJBQThCO0FBQ3pFLFNBQVNDLE1BQU0sUUFBUSwrQkFBK0I7QUFDdEQsU0FBU0MseUJBQXlCLFFBQVEsZ0NBQWdDO0FBRTFFLE9BQU8sU0FBU0Msa0NBQWtDQSxDQUNoREMsZUFBZSxFQUFFUixxQkFBcUIsQ0FDdkMsRUFBRUkscUJBQXFCLENBQUM7RUFDdkIsUUFBUUksZUFBZTtJQUNyQixLQUFLLGVBQWU7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 \ No newline at end of file diff --git a/components/permissions/rules/AddWorkspaceDirectory.tsx b/components/permissions/rules/AddWorkspaceDirectory.tsx new file mode 100644 index 0000000..7ff97fa --- /dev/null +++ b/components/permissions/rules/AddWorkspaceDirectory.tsx @@ -0,0 +1,340 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import * as React from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDebounceCallback } from 'usehooks-ts'; +import { addDirHelpMessage, validateDirectoryForWorkspace } from '../../../commands/add-dir/validation.js'; +import TextInput from '../../../components/TextInput.js'; +import type { KeyboardEvent } from '../../../ink/events/keyboard-event.js'; +import { Box, Text } from '../../../ink.js'; +import { useKeybinding } from '../../../keybindings/useKeybinding.js'; +import type { ToolPermissionContext } from '../../../Tool.js'; +import { getDirectoryCompletions } from '../../../utils/suggestions/directoryCompletion.js'; +import { ConfigurableShortcutHint } from '../../ConfigurableShortcutHint.js'; +import { Select } from '../../CustomSelect/select.js'; +import { Byline } from '../../design-system/Byline.js'; +import { Dialog } from '../../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../../design-system/KeyboardShortcutHint.js'; +import { PromptInputFooterSuggestions, type SuggestionItem } from '../../PromptInput/PromptInputFooterSuggestions.js'; +type Props = { + onAddDirectory: (path: string, remember?: boolean) => void; + onCancel: () => void; + permissionContext: ToolPermissionContext; + directoryPath?: string; // When directoryPath is provided, show selection options instead of input +}; +type RememberDirectoryOption = 'yes-session' | 'yes-remember' | 'no'; +const REMEMBER_DIRECTORY_OPTIONS: Array<{ + value: RememberDirectoryOption; + label: string; +}> = [{ + value: 'yes-session', + label: 'Yes, for this session' +}, { + value: 'yes-remember', + label: 'Yes, and remember this directory' +}, { + value: 'no', + label: 'No' +}]; +function PermissionDescription() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = Claude Code will be able to read files in this directory and make edits when auto-accept edits is on.; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +function DirectoryDisplay(t0) { + const $ = _c(5); + const { + path + } = t0; + let t1; + if ($[0] !== path) { + t1 = {path}; + $[0] = path; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = ; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] !== t1) { + t3 = {t1}{t2}; + $[3] = t1; + $[4] = t3; + } else { + t3 = $[4]; + } + return t3; +} +function DirectoryInput(t0) { + const $ = _c(14); + const { + value, + onChange, + onSubmit, + error, + suggestions, + selectedSuggestion + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Enter the path to the directory:; + $[0] = t1; + } else { + t1 = $[0]; + } + let t2; + if ($[1] !== onChange || $[2] !== onSubmit || $[3] !== value) { + t2 = ; + $[1] = onChange; + $[2] = onSubmit; + $[3] = value; + $[4] = t2; + } else { + t2 = $[4]; + } + let t3; + if ($[5] !== selectedSuggestion || $[6] !== suggestions) { + t3 = suggestions.length > 0 && ; + $[5] = selectedSuggestion; + $[6] = suggestions; + $[7] = t3; + } else { + t3 = $[7]; + } + let t4; + if ($[8] !== error) { + t4 = error && {error}; + $[8] = error; + $[9] = t4; + } else { + t4 = $[9]; + } + let t5; + if ($[10] !== t2 || $[11] !== t3 || $[12] !== t4) { + t5 = {t1}{t2}{t3}{t4}; + $[10] = t2; + $[11] = t3; + $[12] = t4; + $[13] = t5; + } else { + t5 = $[13]; + } + return t5; +} +function _temp() {} +export function AddWorkspaceDirectory(t0) { + const $ = _c(34); + const { + onAddDirectory, + onCancel, + permissionContext, + directoryPath + } = t0; + const [directoryInput, setDirectoryInput] = useState(""); + const [error, setError] = useState(null); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = []; + $[0] = t1; + } else { + t1 = $[0]; + } + const [suggestions, setSuggestions] = useState(t1); + const [selectedSuggestion, setSelectedSuggestion] = useState(0); + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = async path => { + if (!path) { + setSuggestions([]); + setSelectedSuggestion(0); + return; + } + const completions = await getDirectoryCompletions(path); + setSuggestions(completions); + setSelectedSuggestion(0); + }; + $[1] = t2; + } else { + t2 = $[1]; + } + const fetchSuggestions = t2; + const debouncedFetchSuggestions = useDebounceCallback(fetchSuggestions, 100); + let t3; + let t4; + if ($[2] !== debouncedFetchSuggestions || $[3] !== directoryInput) { + t3 = () => { + debouncedFetchSuggestions(directoryInput); + }; + t4 = [directoryInput, debouncedFetchSuggestions]; + $[2] = debouncedFetchSuggestions; + $[3] = directoryInput; + $[4] = t3; + $[5] = t4; + } else { + t3 = $[4]; + t4 = $[5]; + } + useEffect(t3, t4); + let t5; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t5 = suggestion => { + const newPath = suggestion.id + "/"; + setDirectoryInput(newPath); + setError(null); + }; + $[6] = t5; + } else { + t5 = $[6]; + } + const applySuggestion = t5; + let t6; + if ($[7] !== onAddDirectory || $[8] !== permissionContext) { + t6 = async newPath_0 => { + const result = await validateDirectoryForWorkspace(newPath_0, permissionContext); + if (result.resultType === "success") { + onAddDirectory(result.absolutePath, false); + } else { + setError(addDirHelpMessage(result)); + } + }; + $[7] = onAddDirectory; + $[8] = permissionContext; + $[9] = t6; + } else { + t6 = $[9]; + } + const handleSubmit = t6; + let t7; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t7 = { + context: "Settings" + }; + $[10] = t7; + } else { + t7 = $[10]; + } + useKeybinding("confirm:no", onCancel, t7); + let t8; + if ($[11] !== handleSubmit || $[12] !== selectedSuggestion || $[13] !== suggestions) { + t8 = e => { + if (suggestions.length > 0) { + if (e.key === "tab") { + e.preventDefault(); + const suggestion_0 = suggestions[selectedSuggestion]; + if (suggestion_0) { + applySuggestion(suggestion_0); + } + return; + } + if (e.key === "return") { + e.preventDefault(); + const suggestion_1 = suggestions[selectedSuggestion]; + if (suggestion_1) { + handleSubmit(suggestion_1.id + "/"); + } + return; + } + if (e.key === "up" || e.ctrl && e.key === "p") { + e.preventDefault(); + setSelectedSuggestion(prev => prev <= 0 ? suggestions.length - 1 : prev - 1); + return; + } + if (e.key === "down" || e.ctrl && e.key === "n") { + e.preventDefault(); + setSelectedSuggestion(prev_0 => prev_0 >= suggestions.length - 1 ? 0 : prev_0 + 1); + return; + } + } + }; + $[11] = handleSubmit; + $[12] = selectedSuggestion; + $[13] = suggestions; + $[14] = t8; + } else { + t8 = $[14]; + } + const handleKeyDown = t8; + let t9; + if ($[15] !== directoryPath || $[16] !== onAddDirectory || $[17] !== onCancel) { + t9 = value => { + if (!directoryPath) { + return; + } + const selectionValue = value as RememberDirectoryOption; + bb64: switch (selectionValue) { + case "yes-session": + { + onAddDirectory(directoryPath, false); + break bb64; + } + case "yes-remember": + { + onAddDirectory(directoryPath, true); + break bb64; + } + case "no": + { + onCancel(); + } + } + }; + $[15] = directoryPath; + $[16] = onAddDirectory; + $[17] = onCancel; + $[18] = t9; + } else { + t9 = $[18]; + } + const handleSelect = t9; + const t10 = directoryPath ? undefined : _temp2; + let t11; + if ($[19] !== directoryInput || $[20] !== directoryPath || $[21] !== error || $[22] !== handleSelect || $[23] !== handleSubmit || $[24] !== selectedSuggestion || $[25] !== suggestions) { + t11 = directoryPath ? ; + $[32] = onCancel; + $[33] = t11; + $[34] = t13; + } else { + t13 = $[34]; + } + let t14; + if ($[35] !== ruleDescription || $[36] !== t13 || $[37] !== t9) { + t14 = {t9}{ruleDescription}{t10}{t13}; + $[35] = ruleDescription; + $[36] = t13; + $[37] = t9; + $[38] = t14; + } else { + t14 = $[38]; + } + let t15; + if ($[39] !== footer || $[40] !== t14) { + t15 = <>{t14}{footer}; + $[39] = footer; + $[40] = t14; + $[41] = t15; + } else { + t15 = $[41]; + } + return t15; +} +type RulesTabContentProps = { + options: Option[]; + searchQuery: string; + isSearchMode: boolean; + isFocused: boolean; + onSelect: (value: string) => void; + onCancel: () => void; + lastFocusedRuleKey: string | undefined; + cursorOffset?: number; + onHeaderFocusChange?: (focused: boolean) => void; +}; + +// Component for rendering rules tab content with full width support +function RulesTabContent(props) { + const $ = _c(26); + const { + options, + searchQuery, + isSearchMode, + isFocused, + onSelect, + onCancel, + lastFocusedRuleKey, + cursorOffset, + onHeaderFocusChange + } = props; + const tabWidth = useTabsWidth(); + const { + headerFocused, + focusHeader, + blurHeader + } = useTabHeaderFocus(); + let t0; + let t1; + if ($[0] !== blurHeader || $[1] !== headerFocused || $[2] !== isSearchMode) { + t0 = () => { + if (isSearchMode && headerFocused) { + blurHeader(); + } + }; + t1 = [isSearchMode, headerFocused, blurHeader]; + $[0] = blurHeader; + $[1] = headerFocused; + $[2] = isSearchMode; + $[3] = t0; + $[4] = t1; + } else { + t0 = $[3]; + t1 = $[4]; + } + useEffect(t0, t1); + let t2; + let t3; + if ($[5] !== headerFocused || $[6] !== onHeaderFocusChange) { + t2 = () => { + onHeaderFocusChange?.(headerFocused); + }; + t3 = [headerFocused, onHeaderFocusChange]; + $[5] = headerFocused; + $[6] = onHeaderFocusChange; + $[7] = t2; + $[8] = t3; + } else { + t2 = $[7]; + t3 = $[8]; + } + useEffect(t2, t3); + const t4 = isSearchMode && !headerFocused; + let t5; + if ($[9] !== cursorOffset || $[10] !== isFocused || $[11] !== searchQuery || $[12] !== t4 || $[13] !== tabWidth) { + t5 = ; + $[9] = cursorOffset; + $[10] = isFocused; + $[11] = searchQuery; + $[12] = t4; + $[13] = tabWidth; + $[14] = t5; + } else { + t5 = $[14]; + } + const t6 = Math.min(10, options.length); + const t7 = isSearchMode || headerFocused; + let t8; + if ($[15] !== focusHeader || $[16] !== lastFocusedRuleKey || $[17] !== onCancel || $[18] !== onSelect || $[19] !== options || $[20] !== t6 || $[21] !== t7) { + t8 = ; + $[25] = focusHeader; + $[26] = headerFocused; + $[27] = options; + $[28] = t12; + $[29] = t13; + } else { + t13 = $[29]; + } + return t13; +} +function _temp3() { + return new Set(); +} +function _temp2() { + return new Set(); +} +function _temp() { + return getAutoModeDenials(); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlRWZmZWN0IiwidXNlU3RhdGUiLCJCb3giLCJUZXh0IiwidXNlSW5wdXQiLCJBdXRvTW9kZURlbmlhbCIsImdldEF1dG9Nb2RlRGVuaWFscyIsIlNlbGVjdCIsIlN0YXR1c0ljb24iLCJ1c2VUYWJIZWFkZXJGb2N1cyIsIlByb3BzIiwib25IZWFkZXJGb2N1c0NoYW5nZSIsImZvY3VzZWQiLCJvblN0YXRlQ2hhbmdlIiwic3RhdGUiLCJhcHByb3ZlZCIsIlNldCIsInJldHJ5IiwiZGVuaWFscyIsIlJlY2VudERlbmlhbHNUYWIiLCJ0MCIsIiQiLCJfYyIsImhlYWRlckZvY3VzZWQiLCJmb2N1c0hlYWRlciIsInQxIiwidDIiLCJfdGVtcCIsInNldEFwcHJvdmVkIiwiX3RlbXAyIiwic2V0UmV0cnkiLCJfdGVtcDMiLCJmb2N1c2VkSWR4Iiwic2V0Rm9jdXNlZElkeCIsInQzIiwidDQiLCJ0NSIsIlN5bWJvbCIsImZvciIsInZhbHVlIiwiaWR4IiwiTnVtYmVyIiwicHJldiIsIm5leHQiLCJoYXMiLCJkZWxldGUiLCJhZGQiLCJoYW5kbGVTZWxlY3QiLCJ0NiIsInZhbHVlXzAiLCJoYW5kbGVGb2N1cyIsInQ3IiwiaW5wdXQiLCJfa2V5IiwicHJldl8wIiwibmV4dF8wIiwicHJldl8xIiwibmV4dF8xIiwidDgiLCJsZW5ndGgiLCJ0OSIsImlzQWN0aXZlIiwidDEwIiwidDExIiwiZCIsImlkeF8wIiwiaXNBcHByb3ZlZCIsInN1ZmZpeCIsImxhYmVsIiwiZGlzcGxheSIsIlN0cmluZyIsIm1hcCIsIm9wdGlvbnMiLCJ0MTIiLCJNYXRoIiwibWluIiwidDEzIl0sInNvdXJjZXMiOlsiUmVjZW50RGVuaWFsc1RhYi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VDYWxsYmFjaywgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9wcmVmZXItdXNlLWtleWJpbmRpbmdzIC0tICdyJyBpcyBhIHZpZXctc3BlY2lmaWMga2V5LCBub3QgYSBnbG9iYWwga2V5YmluZGluZ1xuaW1wb3J0IHsgQm94LCBUZXh0LCB1c2VJbnB1dCB9IGZyb20gJy4uLy4uLy4uL2luay5qcydcbmltcG9ydCB7XG4gIHR5cGUgQXV0b01vZGVEZW5pYWwsXG4gIGdldEF1dG9Nb2RlRGVuaWFscyxcbn0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvYXV0b01vZGVEZW5pYWxzLmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi4vLi4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IFN0YXR1c0ljb24gfSBmcm9tICcuLi8uLi9kZXNpZ24tc3lzdGVtL1N0YXR1c0ljb24uanMnXG5pbXBvcnQgeyB1c2VUYWJIZWFkZXJGb2N1cyB9IGZyb20gJy4uLy4uL2Rlc2lnbi1zeXN0ZW0vVGFicy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgb25IZWFkZXJGb2N1c0NoYW5nZT86IChmb2N1c2VkOiBib29sZWFuKSA9PiB2b2lkXG4gIC8qKiBDYWxsZWQgd2hlbiBhcHByb3ZlZC9yZXRyeSBzdGF0ZSBjaGFuZ2VzIHNvIHBhcmVudCBjYW4gYWN0IG9uIGV4aXQgKi9cbiAgb25TdGF0ZUNoYW5nZTogKHN0YXRlOiB7XG4gICAgYXBwcm92ZWQ6IFNldDxudW1iZXI+XG4gICAgcmV0cnk6IFNldDxudW1iZXI+XG4gICAgZGVuaWFsczogcmVhZG9ubHkgQXV0b01vZGVEZW5pYWxbXVxuICB9KSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBSZWNlbnREZW5pYWxzVGFiKHtcbiAgb25IZWFkZXJGb2N1c0NoYW5nZSxcbiAgb25TdGF0ZUNoYW5nZSxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBoZWFkZXJGb2N1c2VkLCBmb2N1c0hlYWRlciB9ID0gdXNlVGFiSGVhZGVyRm9jdXMoKVxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIG9uSGVhZGVyRm9jdXNDaGFuZ2U/LihoZWFkZXJGb2N1c2VkKVxuICB9LCBbaGVhZGVyRm9jdXNlZCwgb25IZWFkZXJGb2N1c0NoYW5nZV0pXG5cbiAgLy8gU25hcHNob3Qgb24gbW91bnQg4oCUIGFwcHJvdmVkL3JldHJ5IFNldHMga2V5IGJ5IGluZGV4LCBhbmQgdGhlIGxpdmUgc3RvcmVcbiAgLy8gcHJlcGVuZHMuIEEgY29uY3VycmVudCBkZW5pYWwgd291bGQgc2hpZnQgYWxsIGluZGljZXMgbWlkLWVkaXQuXG4gIGNvbnN0IFtkZW5pYWxzXSA9IHVzZVN0YXRlKCgpID0+IGdldEF1dG9Nb2RlRGVuaWFscygpKVxuXG4gIGNvbnN0IFthcHByb3ZlZCwgc2V0QXBwcm92ZWRdID0gdXNlU3RhdGU8U2V0PG51bWJlcj4+KCgpID0+IG5ldyBTZXQoKSlcbiAgY29uc3QgW3JldHJ5LCBzZXRSZXRyeV0gPSB1c2VTdGF0ZTxTZXQ8bnVtYmVyPj4oKCkgPT4gbmV3IFNldCgpKVxuICBjb25zdCBbZm9jdXNlZElkeCwgc2V0Rm9jdXNlZElkeF0gPSB1c2VTdGF0ZSgwKVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgb25TdGF0ZUNoYW5nZSh7IGFwcHJvdmVkLCByZXRyeSwgZGVuaWFscyB9KVxuICB9LCBbYXBwcm92ZWQsIHJldHJ5LCBkZW5pYWxzLCBvblN0YXRlQ2hhbmdlXSlcblxuICBjb25zdCBoYW5kbGVTZWxlY3QgPSB1c2VDYWxsYmFjaygodmFsdWU6IHN0cmluZykgPT4ge1xuICAgIGNvbnN0IGlkeCA9IE51bWJlcih2YWx1ZSlcbiAgICBzZXRBcHByb3ZlZChwcmV2ID0+IHtcbiAgICAgIGNvbnN0IG5leHQgPSBuZXcgU2V0KHByZXYpXG4gICAgICBpZiAobmV4dC5oYXMoaWR4KSkgbmV4dC5kZWxldGUoaWR4KVxuICAgICAgZWxzZSBuZXh0LmFkZChpZHgpXG4gICAgICByZXR1cm4gbmV4dFxuICAgIH0pXG4gIH0sIFtdKVxuXG4gIGNvbnN0IGhhbmRsZUZvY3VzID0gdXNlQ2FsbGJhY2soKHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICBzZXRGb2N1c2VkSWR4KE51bWJlcih2YWx1ZSkpXG4gIH0sIFtdKVxuXG4gIHVzZUlucHV0KFxuICAgIChpbnB1dCwgX2tleSkgPT4ge1xuICAgICAgaWYgKGlucHV0ID09PSAncicpIHtcbiAgICAgICAgc2V0UmV0cnkocHJldiA9PiB7XG4gICAgICAgICAgY29uc3QgbmV4dCA9IG5ldyBTZXQocHJldilcbiAgICAgICAgICBpZiAobmV4dC5oYXMoZm9jdXNlZElkeCkpIG5leHQuZGVsZXRlKGZvY3VzZWRJZHgpXG4gICAgICAgICAgZWxzZSBuZXh0LmFkZChmb2N1c2VkSWR4KVxuICAgICAgICAgIHJldHVybiBuZXh0XG4gICAgICAgIH0pXG4gICAgICAgIC8vIFJldHJ5IGltcGxpZXMgYXBwcm92ZVxuICAgICAgICBzZXRBcHByb3ZlZChwcmV2ID0+IHtcbiAgICAgICAgICBpZiAocHJldi5oYXMoZm9jdXNlZElkeCkpIHJldHVybiBwcmV2XG4gICAgICAgICAgY29uc3QgbmV4dCA9IG5ldyBTZXQocHJldilcbiAgICAgICAgICBuZXh0LmFkZChmb2N1c2VkSWR4KVxuICAgICAgICAgIHJldHVybiBuZXh0XG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfSxcbiAgICB7IGlzQWN0aXZlOiBkZW5pYWxzLmxlbmd0aCA+IDAgfSxcbiAgKVxuXG4gIGlmIChkZW5pYWxzLmxlbmd0aCA9PT0gMCkge1xuICAgIHJldHVybiAoXG4gICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgTm8gcmVjZW50IGRlbmlhbHMuIENvbW1hbmRzIGRlbmllZCBieSB0aGUgYXV0byBtb2RlIGNsYXNzaWZpZXIgd2lsbFxuICAgICAgICBhcHBlYXIgaGVyZS5cbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH1cblxuICBjb25zdCBvcHRpb25zID0gZGVuaWFscy5tYXAoKGQsIGlkeCkgPT4ge1xuICAgIGNvbnN0IGlzQXBwcm92ZWQgPSBhcHByb3ZlZC5oYXMoaWR4KVxuICAgIGNvbnN0IHN1ZmZpeCA9IHJldHJ5LmhhcyhpZHgpID8gJyAocmV0cnkpJyA6ICcnXG4gICAgcmV0dXJuIHtcbiAgICAgIGxhYmVsOiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIDxTdGF0dXNJY29uIHN0YXR1cz17aXNBcHByb3ZlZCA/ICdzdWNjZXNzJyA6ICdlcnJvcid9IHdpdGhTcGFjZSAvPlxuICAgICAgICAgIHtkLmRpc3BsYXl9XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+e3N1ZmZpeH08L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICksXG4gICAgICB2YWx1ZTogU3RyaW5nKGlkeCksXG4gICAgfVxuICB9KVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8VGV4dD5Db21tYW5kcyByZWNlbnRseSBkZW5pZWQgYnkgdGhlIGF1dG8gbW9kZSBjbGFzc2lmaWVyLjwvVGV4dD5cbiAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfT5cbiAgICAgICAgPFNlbGVjdFxuICAgICAgICAgIG9wdGlvbnM9e29wdGlvbnN9XG4gICAgICAgICAgb25DaGFuZ2U9e2hhbmRsZVNlbGVjdH1cbiAgICAgICAgICBvbkZvY3VzPXtoYW5kbGVGb2N1c31cbiAgICAgICAgICB2aXNpYmxlT3B0aW9uQ291bnQ9e01hdGgubWluKDEwLCBvcHRpb25zLmxlbmd0aCl9XG4gICAgICAgICAgaXNEaXNhYmxlZD17aGVhZGVyRm9jdXNlZH1cbiAgICAgICAgICBvblVwRnJvbUZpcnN0SXRlbT17Zm9jdXNIZWFkZXJ9XG4gICAgICAgIC8+XG4gICAgICA8L0JveD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxXQUFXLEVBQUVDLFNBQVMsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFD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 \ No newline at end of file diff --git a/components/permissions/rules/RemoveWorkspaceDirectory.tsx b/components/permissions/rules/RemoveWorkspaceDirectory.tsx new file mode 100644 index 0000000..7174df1 --- /dev/null +++ b/components/permissions/rules/RemoveWorkspaceDirectory.tsx @@ -0,0 +1,110 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useCallback } from 'react'; +import { Select } from '../../../components/CustomSelect/select.js'; +import { Box, Text } from '../../../ink.js'; +import type { ToolPermissionContext } from '../../../Tool.js'; +import { applyPermissionUpdate } from '../../../utils/permissions/PermissionUpdate.js'; +import { Dialog } from '../../design-system/Dialog.js'; +type Props = { + directoryPath: string; + onRemove: () => void; + onCancel: () => void; + permissionContext: ToolPermissionContext; + setPermissionContext: (context: ToolPermissionContext) => void; +}; +export function RemoveWorkspaceDirectory(t0) { + const $ = _c(19); + const { + directoryPath, + onRemove, + onCancel, + permissionContext, + setPermissionContext + } = t0; + let t1; + if ($[0] !== directoryPath || $[1] !== onRemove || $[2] !== permissionContext || $[3] !== setPermissionContext) { + t1 = () => { + const updatedContext = applyPermissionUpdate(permissionContext, { + type: "removeDirectories", + directories: [directoryPath], + destination: "session" + }); + setPermissionContext(updatedContext); + onRemove(); + }; + $[0] = directoryPath; + $[1] = onRemove; + $[2] = permissionContext; + $[3] = setPermissionContext; + $[4] = t1; + } else { + t1 = $[4]; + } + const handleRemove = t1; + let t2; + if ($[5] !== handleRemove || $[6] !== onCancel) { + t2 = value => { + if (value === "yes") { + handleRemove(); + } else { + onCancel(); + } + }; + $[5] = handleRemove; + $[6] = onCancel; + $[7] = t2; + } else { + t2 = $[7]; + } + const handleSelect = t2; + let t3; + if ($[8] !== directoryPath) { + t3 = {directoryPath}; + $[8] = directoryPath; + $[9] = t3; + } else { + t3 = $[9]; + } + let t4; + if ($[10] === Symbol.for("react.memo_cache_sentinel")) { + t4 = Claude Code will no longer have access to files in this directory.; + $[10] = t4; + } else { + t4 = $[10]; + } + let t5; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t5 = [{ + label: "Yes", + value: "yes" + }, { + label: "No", + value: "no" + }]; + $[11] = t5; + } else { + t5 = $[11]; + } + let t6; + if ($[12] !== handleSelect || $[13] !== onCancel) { + t6 = ; + $[16] = focusHeader; + $[17] = handleCancel; + $[18] = handleDirectorySelect; + $[19] = headerFocused; + $[20] = options; + $[21] = t7; + $[22] = t8; + } else { + t8 = $[22]; + } + return t8; +} +function _temp2(dir) { + return { + label: dir.path, + value: dir.path + }; +} +function _temp(path) { + return { + path, + isCurrent: false, + isDeletable: true + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZUVmZmVjdCIsImdldE9yaWdpbmFsQ3dkIiwiQ29tbWFuZFJlc3VsdERpc3BsYXkiLCJTZWxlY3QiLCJCb3giLCJUZXh0IiwiVG9vbFBlcm1pc3Npb25Db250ZXh0IiwidXNlVGFiSGVhZGVyRm9jdXMiLCJQcm9wcyIsIm9uRXhpdCIsInJlc3VsdCIsIm9wdGlvbnMiLCJkaXNwbGF5IiwidG9vbFBlcm1pc3Npb25Db250ZXh0Iiwib25SZXF1ZXN0QWRkRGlyZWN0b3J5Iiwib25SZXF1ZXN0UmVtb3ZlRGlyZWN0b3J5IiwicGF0aCIsIm9uSGVhZGVyRm9jdXNDaGFuZ2UiLCJmb2N1c2VkIiwiRGlyZWN0b3J5SXRlbSIsImlzQ3VycmVudCIsImlzRGVsZXRhYmxlIiwiV29ya3NwYWNlVGFiIiwidDAiLCIkIiwiX2MiLCJoZWFkZXJGb2N1c2VkIiwiZm9jdXNIZWFkZXIiLCJ0MSIsInQyIiwidDMiLCJhZGRpdGlvbmFsV29ya2luZ0RpcmVjdG9yaWVzIiwiQXJyYXkiLCJmcm9tIiwia2V5cyIsIm1hcCIsIl90ZW1wIiwiYWRkaXRpb25hbERpcmVjdG9yaWVzIiwidDQiLCJzZWxlY3RlZFZhbHVlIiwiZGlyZWN0b3J5IiwiZmluZCIsImQiLCJoYW5kbGVEaXJlY3RvcnlTZWxlY3QiLCJ0NSIsImhhbmRsZUNhbmNlbCIsIm9wdHMiLCJfdGVtcDIiLCJ0NiIsIlN5bWJvbCIsImZvciIsImxhYmVsIiwiZWxsaXBzaXMiLCJ2YWx1ZSIsInB1c2giLCJ0NyIsIk1hdGgiLCJtaW4iLCJsZW5ndGgiLCJ0OCIsImRpciJdLCJzb3VyY2VzIjpbIldvcmtzcGFjZVRhYi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgZ2V0T3JpZ2luYWxDd2QgfSBmcm9tICcuLi8uLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IENvbW1hbmRSZXN1bHREaXNwbGF5IH0gZnJvbSAnLi4vLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi8uLi8uLi9jb21wb25lbnRzL0N1c3RvbVNlbGVjdC9zZWxlY3QuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi8uLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xQZXJtaXNzaW9uQ29udGV4dCB9IGZyb20gJy4uLy4uLy4uL1Rvb2wuanMnXG5pbXBvcnQgeyB1c2VUYWJIZWFkZXJGb2N1cyB9IGZyb20gJy4uLy4uL2Rlc2lnbi1zeXN0ZW0vVGFicy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgb25FeGl0OiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWRcbiAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHRcbiAgb25SZXF1ZXN0QWRkRGlyZWN0b3J5OiAoKSA9PiB2b2lkXG4gIG9uUmVxdWVzdFJlbW92ZURpcmVjdG9yeTogKHBhdGg6IHN0cmluZykgPT4gdm9pZFxuICBvbkhlYWRlckZvY3VzQ2hhbmdlPzogKGZvY3VzZWQ6IGJvb2xlYW4pID0+IHZvaWRcbn1cblxudHlwZSBEaXJlY3RvcnlJdGVtID0ge1xuICBwYXRoOiBzdHJpbmdcbiAgaXNDdXJyZW50OiBib29sZWFuXG4gIGlzRGVsZXRhYmxlOiBib29sZWFuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBXb3Jrc3BhY2VUYWIoe1xuICBvbkV4aXQsXG4gIHRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgb25SZXF1ZXN0QWRkRGlyZWN0b3J5LFxuICBvblJlcXVlc3RSZW1vdmVEaXJlY3RvcnksXG4gIG9uSGVhZGVyRm9jdXNDaGFuZ2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgaGVhZGVyRm9jdXNlZCwgZm9jdXNIZWFkZXIgfSA9IHVzZVRhYkhlYWRlckZvY3VzKClcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBvbkhlYWRlckZvY3VzQ2hhbmdlPy4oaGVhZGVyRm9jdXNlZClcbiAgfSwgW2hlYWRlckZvY3VzZWQsIG9uSGVhZGVyRm9jdXNDaGFuZ2VdKVxuICAvLyBHZXQgb25seSBhZGRpdGlvbmFsIHdvcmtzcGFjZSBkaXJlY3RvcmllcyAobm90IHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5KVxuICBjb25zdCBhZGRpdGlvbmFsRGlyZWN0b3JpZXMgPSBSZWFjdC51c2VNZW1vKCgpOiBEaXJlY3RvcnlJdGVtW10gPT4ge1xuICAgIHJldHVybiBBcnJheS5mcm9tKFxuICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0LmFkZGl0aW9uYWxXb3JraW5nRGlyZWN0b3JpZXMua2V5cygpLFxuICAgICkubWFwKHBhdGggPT4gKHtcbiAgICAgIHBhdGgsXG4gICAgICBpc0N1cnJlbnQ6IGZhbHNlLFxuICAgICAgaXNEZWxldGFibGU6IHRydWUsXG4gICAgfSkpXG4gIH0sIFt0b29sUGVybWlzc2lvbkNvbnRleHQuYWRkaXRpb25hbFdvcmtpbmdEaXJlY3Rvcmllc10pXG5cbiAgY29uc3QgaGFuZGxlRGlyZWN0b3J5U2VsZWN0ID0gdXNlQ2FsbGJhY2soXG4gICAgKHNlbGVjdGVkVmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgaWYgKHNlbGVjdGVkVmFsdWUgPT09ICdhZGQtZGlyZWN0b3J5Jykge1xuICAgICAgICBvblJlcXVlc3RBZGREaXJlY3RvcnkoKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgY29uc3QgZGlyZWN0b3J5ID0gYWRkaXRpb25hbERpcmVjdG9yaWVzLmZpbmQoXG4gICAgICAgIGQgPT4gZC5wYXRoID09PSBzZWxlY3RlZFZhbHVlLFxuICAgICAgKVxuICAgICAgaWYgKGRpcmVjdG9yeSAmJiBkaXJlY3RvcnkuaXNEZWxldGFibGUpIHtcbiAgICAgICAgb25SZXF1ZXN0UmVtb3ZlRGlyZWN0b3J5KGRpcmVjdG9yeS5wYXRoKVxuICAgICAgfVxuICAgIH0sXG4gICAgW2FkZGl0aW9uYWxEaXJlY3Rvcmllcywgb25SZXF1ZXN0QWRkRGlyZWN0b3J5LCBvblJlcXVlc3RSZW1vdmVEaXJlY3RvcnldLFxuICApXG5cbiAgY29uc3QgaGFuZGxlQ2FuY2VsID0gdXNlQ2FsbGJhY2soXG4gICAgKCkgPT4gb25FeGl0KCdXb3Jrc3BhY2UgZGlhbG9nIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSksXG4gICAgW29uRXhpdF0sXG4gIClcblxuICAvLyBNYWluIGxpc3QgdmlldyBvcHRpb25zXG4gIGNvbnN0IG9wdGlvbnMgPSBSZWFjdC51c2VNZW1vKCgpID0+IHtcbiAgICBjb25zdCBvcHRzID0gYWRkaXRpb25hbERpcmVjdG9yaWVzLm1hcChkaXIgPT4gKHtcbiAgICAgIGxhYmVsOiBkaXIucGF0aCxcbiAgICAgIHZhbHVlOiBkaXIucGF0aCxcbiAgICB9KSlcblxuICAgIG9wdHMucHVzaCh7XG4gICAgICBsYWJlbDogYEFkZCBkaXJlY3Rvcnkke2ZpZ3VyZXMuZWxsaXBzaXN9YCxcbiAgICAgIHZhbHVlOiAnYWRkLWRpcmVjdG9yeScsXG4gICAgfSlcblxuICAgIHJldHVybiBvcHRzXG4gIH0sIFthZGRpdGlvbmFsRGlyZWN0b3JpZXNdKVxuXG4gIC8vIE1haW4gbGlzdCB2aWV3XG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgIHsvKiBDdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IHNlY3Rpb24gKi99XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBtYXJnaW5Ub3A9ezF9IG1hcmdpbkxlZnQ9ezJ9IGdhcD17MX0+XG4gICAgICAgIDxUZXh0PntgLSAgJHtnZXRPcmlnaW5hbEN3ZCgpfWB9PC9UZXh0PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj4oT3JpZ2luYWwgd29ya2luZyBkaXJlY3RvcnkpPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgICA8U2VsZWN0XG4gICAgICAgIG9wdGlvbnM9e29wdGlvbnN9XG4gICAgICAgIG9uQ2hhbmdlPXtoYW5kbGVEaXJlY3RvcnlTZWxlY3R9XG4gICAgICAgIG9uQ2FuY2VsPXtoYW5kbGVDYW5jZWx9XG4gICAgICAgIHZpc2libGVPcHRpb25Db3VudD17TWF0aC5taW4oMTAsIG9wdGlvbnMubGVuZ3RoKX1cbiAgICAgICAgb25VcEZyb21GaXJzdEl0ZW09e2ZvY3VzSGVhZGVyfVxuICAgICAgICBpc0Rpc2FibGVkPXtoZWFkZXJGb2N1c2VkfVxuICAgICAgLz5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxXQUFXLEVBQUVDLFNBQVMsUUFBUSxPQUFPO0FBQzlDLFNBQVNDLGNBQWMsUUFBUSw2QkFBNkI7QUFDNUQsY0FBY0Msb0JBQW9CLFFBQVEsc0JBQXNCO0FBQ2hFLFNBQVNDLE1BQU0sUUFBUSw0Q0FBNEM7QUFDbkUsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsaUJBQWlCO0FBQzNDLGNBQWNDLHFCQUFxQixRQUFRLGtCQUFrQjtBQUM3RCxTQUFTQyxpQkFBaUIsUUFBUSw2QkFBNkI7QUFFL0QsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLE1BQU0sRUFBRSxDQUNOQyxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQ2ZDLE9BQTRDLENBQXBDLEVBQUU7SUFBRUMsT0FBTyxDQUFDLEVBQUVWLG9CQUFvQjtFQUFDLENBQUMsRUFDNUMsR0FBRyxJQUFJO0VBQ1RXLHFCQUFxQixFQUFFUCxxQkFBcUI7RUFDNUNRLHFCQUFxQixFQUFFLEdBQUcsR0FBRyxJQUFJ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 \ No newline at end of file diff --git a/components/permissions/shellPermissionHelpers.tsx b/components/permissions/shellPermissionHelpers.tsx new file mode 100644 index 0000000..e4408a6 --- /dev/null +++ b/components/permissions/shellPermissionHelpers.tsx @@ -0,0 +1,164 @@ +import { basename, sep } from 'path'; +import React, { type ReactNode } from 'react'; +import { getOriginalCwd } from '../../bootstrap/state.js'; +import { Text } from '../../ink.js'; +import type { PermissionUpdate } from '../../utils/permissions/PermissionUpdateSchema.js'; +import { permissionRuleExtractPrefix } from '../../utils/permissions/shellRuleMatching.js'; +function commandListDisplay(commands: string[]): ReactNode { + switch (commands.length) { + case 0: + return ''; + case 1: + return {commands[0]}; + case 2: + return + {commands[0]} and {commands[1]} + ; + default: + return + {commands.slice(0, -1).join(', ')}, and{' '} + {commands.slice(-1)[0]} + ; + } +} +function commandListDisplayTruncated(commands: string[]): ReactNode { + // Check if the plain text representation would be too long + const plainText = commands.join(', '); + if (plainText.length > 50) { + return 'similar'; + } + return commandListDisplay(commands); +} +function formatPathList(paths: string[]): ReactNode { + if (paths.length === 0) return ''; + + // Extract directory names from paths + const names = paths.map(p => basename(p) || p); + if (names.length === 1) { + return + {names[0]} + {sep} + ; + } + if (names.length === 2) { + return + {names[0]} + {sep} and {names[1]} + {sep} + ; + } + + // For 3+, show first two with "and N more" + return + {names[0]} + {sep}, {names[1]} + {sep} and {paths.length - 2} more + ; +} + +/** + * Generate the label for the "Yes, and apply suggestions" option in shell + * permission dialogs (Bash, PowerShell). Parametrized by the shell tool name + * and an optional command transform (e.g., Bash strips output redirections so + * filenames don't show as commands). + */ +export function generateShellSuggestionsLabel(suggestions: PermissionUpdate[], shellToolName: string, commandTransform?: (command: string) => string): ReactNode | null { + // Collect all rules for display + const allRules = suggestions.filter(s => s.type === 'addRules').flatMap(s => s.rules || []); + + // Separate Read rules from shell rules + const readRules = allRules.filter(r => r.toolName === 'Read'); + const shellRules = allRules.filter(r => r.toolName === shellToolName); + + // Get directory info + const directories = suggestions.filter(s => s.type === 'addDirectories').flatMap(s => s.directories || []); + + // Extract paths from Read rules (keep separate from directories) + const readPaths = readRules.map(r => r.ruleContent?.replace('/**', '') || '').filter(p => p); + + // Extract shell command prefixes, optionally transforming for display + const shellCommands = [...new Set(shellRules.flatMap(rule => { + if (!rule.ruleContent) return []; + const command = permissionRuleExtractPrefix(rule.ruleContent) ?? rule.ruleContent; + return commandTransform ? commandTransform(command) : command; + }))]; + + // Check what we have + const hasDirectories = directories.length > 0; + const hasReadPaths = readPaths.length > 0; + const hasCommands = shellCommands.length > 0; + + // Handle single type cases + if (hasReadPaths && !hasDirectories && !hasCommands) { + // Only Read rules - use "reading from" language + if (readPaths.length === 1) { + const firstPath = readPaths[0]!; + const dirName = basename(firstPath) || firstPath; + return + Yes, allow reading from {dirName} + {sep} from this project + ; + } + + // Multiple read paths + return + Yes, allow reading from {formatPathList(readPaths)} from this project + ; + } + if (hasDirectories && !hasReadPaths && !hasCommands) { + // Only directory permissions - use "access to" language + if (directories.length === 1) { + const firstDir = directories[0]!; + const dirName = basename(firstDir) || firstDir; + return + Yes, and always allow access to {dirName} + {sep} from this project + ; + } + + // Multiple directories + return + Yes, and always allow access to {formatPathList(directories)} from this + project + ; + } + if (hasCommands && !hasDirectories && !hasReadPaths) { + // Only shell command permissions + return + {"Yes, and don't ask again for "} + {commandListDisplayTruncated(shellCommands)} commands in{' '} + {getOriginalCwd()} + ; + } + + // Handle mixed cases + if ((hasDirectories || hasReadPaths) && !hasCommands) { + // Combine directories and read paths since they're both path access + const allPaths = [...directories, ...readPaths]; + if (hasDirectories && hasReadPaths) { + // Mixed - use generic "access to" + return + Yes, and always allow access to {formatPathList(allPaths)} from this + project + ; + } + } + if ((hasDirectories || hasReadPaths) && hasCommands) { + // Build descriptive message for both types + const allPaths = [...directories, ...readPaths]; + + // Keep it concise but informative + if (allPaths.length === 1 && shellCommands.length === 1) { + return + Yes, and allow access to {formatPathList(allPaths)} and{' '} + {commandListDisplayTruncated(shellCommands)} commands + ; + } + return + Yes, and allow {formatPathList(allPaths)} access and{' '} + {commandListDisplayTruncated(shellCommands)} commands + ; + } + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJiYXNlbmFtZSIsInNlcCIsIlJlYWN0IiwiUmVhY3ROb2RlIiwiZ2V0T3JpZ2luYWxDd2QiLCJUZXh0IiwiUGVybWlzc2lvblVwZGF0ZSIsInBlcm1pc3Npb25SdWxlRXh0cmFjdFByZWZpeCIsImNvbW1hbmRMaXN0RGlzcGxheSIsImNvbW1hbmRzIiwibGVuZ3RoIiwic2xpY2UiLCJqb2luIiwiY29tbWFuZExpc3REaXNwbGF5VHJ1bmNhdGVkIiwicGxhaW5UZXh0IiwiZm9ybWF0UGF0aExpc3QiLCJwYXRocyIsIm5hbWVzIiwibWFwIiwicCIsImdlbmVyYXRlU2hlbGxTdWdnZXN0aW9uc0xhYmVsIiwic3VnZ2VzdGlvbnMiLCJzaGVsbFRvb2xOYW1lIiwiY29tbWFuZFRyYW5zZm9ybSIsImNvbW1hbmQiLCJhbGxSdWxlcyIsImZpbHRlciIsInMiLCJ0eXBlIiwiZmxhdE1hcCIsInJ1bGVzIiwicmVhZFJ1bGVzIiwiciIsInRvb2xOYW1lIiwic2hlbGxSdWxlcyIsImRpcmVjdG9yaWVzIiwicmVhZFBhdGhzIiwicnVsZUNvbnRlbnQiLCJyZXBsYWNlIiwic2hlbGxDb21tYW5kcyIsIlNldCIsInJ1bGUiLCJoYXNEaXJlY3RvcmllcyIsImhhc1JlYWRQYXRocyIsImhhc0NvbW1hbmRzIiwiZmlyc3RQYXRoIiwiZGlyTmFtZSIsImZpcnN0RGlyIiwiYWxsUGF0aHMiXSwic291cmNlcyI6WyJzaGVsbFBlcm1pc3Npb25IZWxwZXJzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBiYXNlbmFtZSwgc2VwIH0gZnJvbSAncGF0aCdcbmltcG9ydCBSZWFjdCwgeyB0eXBlIFJlYWN0Tm9kZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgZ2V0T3JpZ2luYWxDd2QgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBQZXJtaXNzaW9uVXBkYXRlIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvblVwZGF0ZVNjaGVtYS5qcydcbmltcG9ydCB7IHBlcm1pc3Npb25SdWxlRXh0cmFjdFByZWZpeCB9IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL3NoZWxsUnVsZU1hdGNoaW5nLmpzJ1xuXG5mdW5jdGlvbiBjb21tYW5kTGlzdERpc3BsYXkoY29tbWFuZHM6IHN0cmluZ1tdKTogUmVhY3ROb2RlIHtcbiAgc3dpdGNoIChjb21tYW5kcy5sZW5ndGgpIHtcbiAgICBjYXNlIDA6XG4gICAgICByZXR1cm4gJydcbiAgICBjYXNlIDE6XG4gICAgICByZXR1cm4gPFRleHQgYm9sZD57Y29tbWFuZHNbMF19PC9UZXh0PlxuICAgIGNhc2UgMjpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIDxUZXh0IGJvbGQ+e2NvbW1hbmRzWzBdfTwvVGV4dD4gYW5kIDxUZXh0IGJvbGQ+e2NvbW1hbmRzWzFdfTwvVGV4dD5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKVxuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICA8VGV4dCBib2xkPntjb21tYW5kcy5zbGljZSgwLCAtMSkuam9pbignLCAnKX08L1RleHQ+LCBhbmR7JyAnfVxuICAgICAgICAgIDxUZXh0IGJvbGQ+e2NvbW1hbmRzLnNsaWNlKC0xKVswXX08L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIClcbiAgfVxufVxuXG5mdW5jdGlvbiBjb21tYW5kTGlzdERpc3BsYXlUcnVuY2F0ZWQoY29tbWFuZHM6IHN0cmluZ1tdKTogUmVhY3ROb2RlIHtcbiAgLy8gQ2hlY2sgaWYgdGhlIHBsYWluIHRleHQgcmVwcmVzZW50YXRpb24gd291bGQgYmUgdG9vIGxvbmdcbiAgY29uc3QgcGxhaW5UZXh0ID0gY29tbWFuZHMuam9pbignLCAnKVxuICBpZiAocGxhaW5UZXh0Lmxlbmd0aCA+IDUwKSB7XG4gICAgcmV0dXJuICdzaW1pbGFyJ1xuICB9XG4gIHJldHVybiBjb21tYW5kTGlzdERpc3BsYXkoY29tbWFuZHMpXG59XG5cbmZ1bmN0aW9uIGZvcm1hdFBhdGhMaXN0KHBhdGhzOiBzdHJpbmdbXSk6IFJlYWN0Tm9kZSB7XG4gIGlmIChwYXRocy5sZW5ndGggPT09IDApIHJldHVybiAnJ1xuXG4gIC8vIEV4dHJhY3QgZGlyZWN0b3J5IG5hbWVzIGZyb20gcGF0aHNcbiAgY29uc3QgbmFtZXMgPSBwYXRocy5tYXAocCA9PiBiYXNlbmFtZShwKSB8fCBwKVxuXG4gIGlmIChuYW1lcy5sZW5ndGggPT09IDEpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPFRleHQ+XG4gICAgICAgIDxUZXh0IGJvbGQ+e25hbWVzWzBdfTwvVGV4dD5cbiAgICAgICAge3NlcH1cbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH1cbiAgaWYgKG5hbWVzLmxlbmd0aCA9PT0gMikge1xuICAgIHJldHVybiAoXG4gICAgICA8VGV4dD5cbiAgICAgICAgPFRleHQgYm9sZD57bmFtZXNbMF19PC9UZXh0PlxuICAgICAgICB7c2VwfSBhbmQgPFRleHQgYm9sZD57bmFtZXNbMV19PC9UZXh0PlxuICAgICAgICB7c2VwfVxuICAgICAgPC9UZXh0PlxuICAgIClcbiAgfVxuXG4gIC8vIEZvciAzKywgc2hvdyBmaXJzdCB0d28gd2l0aCBcImFuZCBOIG1vcmVcIlxuICByZXR1cm4gKFxuICAgIDxUZXh0PlxuICAgICAgPFRleHQgYm9sZD57bmFtZXNbMF19PC9UZXh0PlxuICAgICAge3NlcH0sIDxUZXh0IGJvbGQ+e25hbWVzWzFdfTwvVGV4dD5cbiAgICAgIHtzZXB9IGFuZCB7cGF0aHMubGVuZ3RoIC0gMn0gbW9yZVxuICAgIDwvVGV4dD5cbiAgKVxufVxuXG4vKipcbiAqIEdlbmVyYXRlIHRoZSBsYWJlbCBmb3IgdGhlIFwiWWVzLCBhbmQgYXBwbHkgc3VnZ2VzdGlvbnNcIiBvcHRpb24gaW4gc2hlbGxcbiAqIHBlcm1pc3Npb24gZGlhbG9ncyAoQmFzaCwgUG93ZXJTaGVsbCkuIFBhcmFtZXRyaXplZCBieSB0aGUgc2hlbGwgdG9vbCBuYW1lXG4gKiBhbmQgYW4gb3B0aW9uYWwgY29tbWFuZCB0cmFuc2Zvcm0gKGUuZy4sIEJhc2ggc3RyaXBzIG91dHB1dCByZWRpcmVjdGlvbnMgc29cbiAqIGZpbGVuYW1lcyBkb24ndCBzaG93IGFzIGNvbW1hbmRzKS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlU2hlbGxTdWdnZXN0aW9uc0xhYmVsKFxuICBzdWdnZXN0aW9uczogUGVybWlzc2lvblVwZGF0ZVtdLFxuICBzaGVsbFRvb2xOYW1lOiBzdHJpbmcsXG4gIGNvbW1hbmRUcmFuc2Zvcm0/OiAoY29tbWFuZDogc3RyaW5nKSA9PiBzdHJpbmcsXG4pOiBSZWFjdE5vZGUgfCBudWxsIHtcbiAgLy8gQ29sbGVjdCBhbGwgcnVsZXMgZm9yIGRpc3BsYXlcbiAgY29uc3QgYWxsUnVsZXMgPSBzdWdnZXN0aW9uc1xuICAgIC5maWx0ZXIocyA9PiBzLnR5cGUgPT09ICdhZGRSdWxlcycpXG4gICAgLmZsYXRNYXAocyA9PiBzLnJ1bGVzIHx8IFtdKVxuXG4gIC8vIFNlcGFyYXRlIFJlYWQgcnVsZXMgZnJvbSBzaGVsbCBydWxlc1xuICBjb25zdCByZWFkUnVsZXMgPSBhbGxSdWxlcy5maWx0ZXIociA9PiByLnRvb2xOYW1lID09PSAnUmVhZCcpXG4gIGNvbnN0IHNoZWxsUnVsZXMgPSBhbGxSdWxlcy5maWx0ZXIociA9PiByLnRvb2xOYW1lID09PSBzaGVsbFRvb2xOYW1lKVxuXG4gIC8vIEdldCBkaXJlY3RvcnkgaW5mb1xuICBjb25zdCBkaXJlY3RvcmllcyA9IHN1Z2dlc3Rpb25zXG4gICAgLmZpbHRlcihzID0+IHMudHlwZSA9PT0gJ2FkZERpcmVjdG9yaWVzJylcbiAgICAuZmxhdE1hcChzID0+IHMuZGlyZWN0b3JpZXMgfHwgW10pXG5cbiAgLy8gRXh0cmFjdCBwYXRocyBmcm9tIFJlYWQgcnVsZXMgKGtlZXAgc2VwYXJhdGUgZnJvbSBkaXJlY3RvcmllcylcbiAgY29uc3QgcmVhZFBhdGhzID0gcmVhZFJ1bGVzXG4gICAgLm1hcChyID0+IHIucnVsZUNvbnRlbnQ/LnJlcGxhY2UoJy8qKicsICcnKSB8fCAnJylcbiAgICAuZmlsdGVyKHAgPT4gcClcblxuICAvLyBFeHRyYWN0IHNoZWxsIGNvbW1hbmQgcHJlZml4ZXMsIG9wdGlvbmFsbHkgdHJhbnNmb3JtaW5nIGZvciBkaXNwbGF5XG4gIGNvbnN0IHNoZWxsQ29tbWFuZHMgPSBbXG4gICAgLi4ubmV3IFNldChcbiAgICAgIHNoZWxsUnVsZXMuZmxhdE1hcChydWxlID0+IHtcbiAgICAgICAgaWYgKCFydWxlLnJ1bGVDb250ZW50KSByZXR1cm4gW11cbiAgICAgICAgY29uc3QgY29tbWFuZCA9XG4gICAgICAgICAgcGVybWlzc2lvblJ1bGVFeHRyYWN0UHJlZml4KHJ1bGUucnVsZUNvbnRlbnQpID8/IHJ1bGUucnVsZUNvbnRlbnRcbiAgICAgICAgcmV0dXJuIGNvbW1hbmRUcmFuc2Zvcm0gPyBjb21tYW5kVHJhbnNmb3JtKGNvbW1hbmQpIDogY29tbWFuZFxuICAgICAgfSksXG4gICAgKSxcbiAgXVxuXG4gIC8vIENoZWNrIHdoYXQgd2UgaGF2ZVxuICBjb25zdCBoYXNEaXJlY3RvcmllcyA9IGRpcmVjdG9yaWVzLmxlbmd0aCA+IDBcbiAgY29uc3QgaGFzUmVhZFBhdGhzID0gcmVhZFBhdGhzLmxlbmd0aCA+IDBcbiAgY29uc3QgaGFzQ29tbWFuZHMgPSBzaGVsbENvbW1hbmRzLmxlbmd0aCA+IDBcblxuICAvLyBIYW5kbGUgc2luZ2xlIHR5cGUgY2FzZXNcbiAgaWYgKGhhc1JlYWRQYXRocyAmJiAhaGFzRGlyZWN0b3JpZXMgJiYgIWhhc0NvbW1hbmRzKSB7XG4gICAgLy8gT25seSBSZWFkIHJ1bGVzIC0gdXNlIFwicmVhZGluZyBmcm9tXCIgbGFuZ3VhZ2VcbiAgICBpZiAocmVhZFBhdGhzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgY29uc3QgZmlyc3RQYXRoID0gcmVhZFBhdGhzWzBdIVxuICAgICAgY29uc3QgZGlyTmFtZSA9IGJhc2VuYW1lKGZpcnN0UGF0aCkgfHwgZmlyc3RQYXRoXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICBZZXMsIGFsbG93IHJlYWRpbmcgZnJvbSA8VGV4dCBib2xkPntkaXJOYW1lfTwvVGV4dD5cbiAgICAgICAgICB7c2VwfSBmcm9tIHRoaXMgcHJvamVjdFxuICAgICAgICA8L1RleHQ+XG4gICAgICApXG4gICAgfVxuXG4gICAgLy8gTXVsdGlwbGUgcmVhZCBwYXRoc1xuICAgIHJldHVybiAoXG4gICAgICA8VGV4dD5cbiAgICAgICAgWWVzLCBhbGxvdyByZWFkaW5nIGZyb20ge2Zvcm1hdFBhdGhMaXN0KHJlYWRQYXRocyl9IGZyb20gdGhpcyBwcm9qZWN0XG4gICAgICA8L1RleHQ+XG4gICAgKVxuICB9XG5cbiAgaWYgKGhhc0RpcmVjdG9yaWVzICYmICFoYXNSZWFkUGF0aHMgJiYgIWhhc0NvbW1hbmRzKSB7XG4gICAgLy8gT25seSBkaXJlY3RvcnkgcGVybWlzc2lvbnMgLSB1c2UgXCJhY2Nlc3MgdG9cIiBsYW5ndWFnZVxuICAgIGlmIChkaXJlY3Rvcmllcy5sZW5ndGggPT09IDEpIHtcbiAgICAgIGNvbnN0IGZpcnN0RGlyID0gZGlyZWN0b3JpZXNbMF0hXG4gICAgICBjb25zdCBkaXJOYW1lID0gYmFzZW5hbWUoZmlyc3REaXIpIHx8IGZpcnN0RGlyXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICBZZXMsIGFuZCBhbHdheXMgYWxsb3cgYWNjZXNzIHRvIDxUZXh0IGJvbGQ+e2Rpck5hbWV9PC9UZXh0PlxuICAgICAgICAgIHtzZXB9IGZyb20gdGhpcyBwcm9qZWN0XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIClcbiAgICB9XG5cbiAgICAvLyBNdWx0aXBsZSBkaXJlY3Rvcmllc1xuICAgIHJldHVybiAoXG4gICAgICA8VGV4dD5cbiAgICAgICAgWWVzLCBhbmQgYWx3YXlzIGFsbG93IGFjY2VzcyB0byB7Zm9ybWF0UGF0aExpc3QoZGlyZWN0b3JpZXMpfSBmcm9tIHRoaXNcbiAgICAgICAgcHJvamVjdFxuICAgICAgPC9UZXh0PlxuICAgIClcbiAgfVxuXG4gIGlmIChoYXNDb21tYW5kcyAmJiAhaGFzRGlyZWN0b3JpZXMgJiYgIWhhc1JlYWRQYXRocykge1xuICAgIC8vIE9ubHkgc2hlbGwgY29tbWFuZCBwZXJtaXNzaW9uc1xuICAgIHJldHVybiAoXG4gICAgICA8VGV4dD5cbiAgICAgICAge1wiWWVzLCBhbmQgZG9uJ3QgYXNrIGFnYWluIGZvciBcIn1cbiAgICAgICAge2NvbW1hbmRMaXN0RGlzcGxheVRydW5jYXRlZChzaGVsbENvbW1hbmRzKX0gY29tbWFuZHMgaW57JyAnfVxuICAgICAgICA8VGV4dCBib2xkPntnZXRPcmlnaW5hbEN3ZCgpfTwvVGV4dD5cbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH1cblxuICAvLyBIYW5kbGUgbWl4ZWQgY2FzZXNcbiAgaWYgKChoYXNEaXJlY3RvcmllcyB8fCBoYXNSZWFkUGF0aHMpICYmICFoYXNDb21tYW5kcykge1xuICAgIC8vIENvbWJpbmUgZGlyZWN0b3JpZXMgYW5kIHJlYWQgcGF0aHMgc2luY2UgdGhleSdyZSBib3RoIHBhdGggYWNjZXNzXG4gICAgY29uc3QgYWxsUGF0aHMgPSBbLi4uZGlyZWN0b3JpZXMsIC4uLnJlYWRQYXRoc11cbiAgICBpZiAoaGFzRGlyZWN0b3JpZXMgJiYgaGFzUmVhZFBhdGhzKSB7XG4gICAgICAvLyBNaXhlZCAtIHVzZSBnZW5lcmljIFwiYWNjZXNzIHRvXCJcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIFllcywgYW5kIGFsd2F5cyBhbGxvdyBhY2Nlc3MgdG8ge2Zvcm1hdFBhdGhMaXN0KGFsbFBhdGhzKX0gZnJvbSB0aGlzXG4gICAgICAgICAgcHJvamVjdFxuICAgICAgICA8L1RleHQ+XG4gICAgICApXG4gICAgfVxuICB9XG5cbiAgaWYgKChoYXNEaXJlY3RvcmllcyB8fCBoYXNSZWFkUGF0aHMpICYmIGhhc0NvbW1hbmRzKSB7XG4gICAgLy8gQnVpbGQgZGVzY3JpcHRpdmUgbWVzc2FnZSBmb3IgYm90aCB0eXBlc1xuICAgIGNvbnN0IGFsbFBhdGhzID0gWy4uLmRpcmVjdG9yaWVzLCAuLi5yZWFkUGF0aHNdXG5cbiAgICAvLyBLZWVwIGl0IGNvbmNpc2UgYnV0IGluZm9ybWF0aXZlXG4gICAgaWYgKGFsbFBhdGhzLmxlbmd0aCA9PT0gMSAmJiBzaGVsbENvbW1hbmRzLmxlbmd0aCA9PT0gMSkge1xuICAgICAgcmV0dXJuIChcbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgWWVzLCBhbmQgYWxsb3cgYWNjZXNzIHRvIHtmb3JtYXRQYXRoTGlzdChhbGxQYXRocyl9IGFuZHsnICd9XG4gICAgICAgICAge2NvbW1hbmRMaXN0RGlzcGxheVRydW5jYXRlZChzaGVsbENvbW1hbmRzKX0gY29tbWFuZHNcbiAgICAgICAgPC9UZXh0PlxuICAgICAgKVxuICAgIH1cblxuICAgIHJldHVybiAoXG4gICAgICA8VGV4dD5cbiAgICAgICAgWWVzLCBhbmQgYWxsb3cge2Zvcm1hdFBhdGhMaXN0KGFsbFBhdGhzKX0gYWNjZXNzIGFuZHsnICd9XG4gICAgICAgIHtjb21tYW5kTGlzdERpc3BsYXlUcnVuY2F0ZWQoc2hlbGxDb21tYW5kcyl9IGNvbW1hbmRzXG4gICAgICA8L1RleHQ+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsUUFBUSxFQUFFQyxHQUFHLFFBQVEsTUFBTTtBQUNwQyxPQUFPQyxLQUFLLElBQUksS0FBS0MsU0FBUyxRQUFRLE9BQU87QUFDN0MsU0FBU0MsY0FBYyxRQUFRLDBCQUEwQjtBQUN6RCxTQUFTQyxJQUFJLFFBQVEsY0FBYztBQUNuQyxjQUFjQyxnQkFBZ0IsUUFBUSxtREFBbUQ7QUFDekYsU0FBU0MsMkJBQTJCLFFBQVEsOENBQThDO0FBRTFGLFNBQVNDLGtCQUFrQkEsQ0FBQ0MsUUFB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 \ No newline at end of file diff --git a/components/permissions/useShellPermissionFeedback.ts b/components/permissions/useShellPermissionFeedback.ts new file mode 100644 index 0000000..58abbbd --- /dev/null +++ b/components/permissions/useShellPermissionFeedback.ts @@ -0,0 +1,148 @@ +import { useState } from 'react' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../../services/analytics/index.js' +import { sanitizeToolNameForAnalytics } from '../../services/analytics/metadata.js' +import { useSetAppState } from '../../state/AppState.js' +import type { ToolUseConfirm } from './PermissionRequest.js' +import { logUnaryPermissionEvent } from './utils.js' + +/** + * Shared feedback-mode state + handlers for shell permission dialogs (Bash, + * PowerShell). Encapsulates the yes/no input-mode toggle, feedback text state, + * focus tracking, and reject handling. + */ +export function useShellPermissionFeedback({ + toolUseConfirm, + onDone, + onReject, + explainerVisible, +}: { + toolUseConfirm: ToolUseConfirm + onDone: () => void + onReject: () => void + explainerVisible: boolean +}): { + yesInputMode: boolean + noInputMode: boolean + yesFeedbackModeEntered: boolean + noFeedbackModeEntered: boolean + acceptFeedback: string + rejectFeedback: string + setAcceptFeedback: (v: string) => void + setRejectFeedback: (v: string) => void + focusedOption: string + handleInputModeToggle: (option: string) => void + handleReject: (feedback?: string) => void + handleFocus: (value: string) => void +} { + const setAppState = useSetAppState() + const [rejectFeedback, setRejectFeedback] = useState('') + const [acceptFeedback, setAcceptFeedback] = useState('') + const [yesInputMode, setYesInputMode] = useState(false) + const [noInputMode, setNoInputMode] = useState(false) + const [focusedOption, setFocusedOption] = useState('yes') + // Track whether user ever entered feedback mode (persists after collapse) + const [yesFeedbackModeEntered, setYesFeedbackModeEntered] = useState(false) + const [noFeedbackModeEntered, setNoFeedbackModeEntered] = useState(false) + + // Handle Tab key toggling input mode for Yes/No options + function handleInputModeToggle(option: string) { + // Notify that user is interacting with the dialog + toolUseConfirm.onUserInteraction() + const analyticsProps = { + toolName: sanitizeToolNameForAnalytics( + toolUseConfirm.tool.name, + ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + isMcp: toolUseConfirm.tool.isMcp ?? false, + } + + if (option === 'yes') { + if (yesInputMode) { + setYesInputMode(false) + logEvent('tengu_accept_feedback_mode_collapsed', analyticsProps) + } else { + setYesInputMode(true) + setYesFeedbackModeEntered(true) + logEvent('tengu_accept_feedback_mode_entered', analyticsProps) + } + } else if (option === 'no') { + if (noInputMode) { + setNoInputMode(false) + logEvent('tengu_reject_feedback_mode_collapsed', analyticsProps) + } else { + setNoInputMode(true) + setNoFeedbackModeEntered(true) + logEvent('tengu_reject_feedback_mode_entered', analyticsProps) + } + } + } + + function handleReject(feedback?: string) { + const trimmedFeedback = feedback?.trim() + const hasFeedback = !!trimmedFeedback + + // Log escape if no feedback was provided (user pressed ESC) + if (!hasFeedback) { + logEvent('tengu_permission_request_escape', { + explainer_visible: explainerVisible, + }) + // Increment escape count for attribution tracking + setAppState(prev => ({ + ...prev, + attribution: { + ...prev.attribution, + escapeCount: prev.attribution.escapeCount + 1, + }, + })) + } + + logUnaryPermissionEvent( + 'tool_use_single', + toolUseConfirm, + 'reject', + hasFeedback, + ) + + if (trimmedFeedback) { + toolUseConfirm.onReject(trimmedFeedback) + } else { + toolUseConfirm.onReject() + } + + onReject() + onDone() + } + + function handleFocus(value: string) { + // Notify that user is interacting with the dialog (only if focus changed) + // This prevents triggering on the initial mount/render + if (value !== focusedOption) { + toolUseConfirm.onUserInteraction() + } + // Reset input mode when navigating away, but only if no text typed + if (value !== 'yes' && yesInputMode && !acceptFeedback.trim()) { + setYesInputMode(false) + } + if (value !== 'no' && noInputMode && !rejectFeedback.trim()) { + setNoInputMode(false) + } + setFocusedOption(value) + } + + return { + yesInputMode, + noInputMode, + yesFeedbackModeEntered, + noFeedbackModeEntered, + acceptFeedback, + rejectFeedback, + setAcceptFeedback, + setRejectFeedback, + focusedOption, + handleInputModeToggle, + handleReject, + handleFocus, + } +} diff --git a/components/permissions/utils.ts b/components/permissions/utils.ts new file mode 100644 index 0000000..90b7b0b --- /dev/null +++ b/components/permissions/utils.ts @@ -0,0 +1,25 @@ +import { getHostPlatformForAnalytics } from '../../utils/env.js' +import { type CompletionType, logUnaryEvent } from '../../utils/unaryLogging.js' +import type { ToolUseConfirm } from './PermissionRequest.js' + +export function logUnaryPermissionEvent( + completion_type: CompletionType, + { + assistantMessage: { + message: { id: message_id }, + }, + }: ToolUseConfirm, + event: 'accept' | 'reject', + hasFeedback?: boolean, +): void { + void logUnaryEvent({ + completion_type, + event, + metadata: { + language_name: 'none', + message_id, + platform: getHostPlatformForAnalytics(), + hasFeedback: hasFeedback ?? false, + }, + }) +} diff --git a/components/sandbox/SandboxConfigTab.tsx b/components/sandbox/SandboxConfigTab.tsx new file mode 100644 index 0000000..dc62f88 --- /dev/null +++ b/components/sandbox/SandboxConfigTab.tsx @@ -0,0 +1,45 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Box, Text } from '../../ink.js'; +import { SandboxManager, shouldAllowManagedSandboxDomainsOnly } from '../../utils/sandbox/sandbox-adapter.js'; +export function SandboxConfigTab() { + const $ = _c(3); + const isEnabled = SandboxManager.isSandboxingEnabled(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + const depCheck = SandboxManager.checkDependencies(); + t0 = depCheck.warnings.length > 0 ? {depCheck.warnings.map(_temp)} : null; + $[0] = t0; + } else { + t0 = $[0]; + } + const warningsNote = t0; + if (!isEnabled) { + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Sandbox is not enabled{warningsNote}; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + const fsReadConfig = SandboxManager.getFsReadConfig(); + const fsWriteConfig = SandboxManager.getFsWriteConfig(); + const networkConfig = SandboxManager.getNetworkRestrictionConfig(); + const allowUnixSockets = SandboxManager.getAllowUnixSockets(); + const excludedCommands = SandboxManager.getExcludedCommands(); + const globPatternWarnings = SandboxManager.getLinuxGlobPatternWarnings(); + t1 = Excluded Commands:{excludedCommands.length > 0 ? excludedCommands.join(", ") : "None"}{fsReadConfig.denyOnly.length > 0 && Filesystem Read Restrictions:Denied: {fsReadConfig.denyOnly.join(", ")}{fsReadConfig.allowWithinDeny && fsReadConfig.allowWithinDeny.length > 0 && Allowed within denied: {fsReadConfig.allowWithinDeny.join(", ")}}}{fsWriteConfig.allowOnly.length > 0 && Filesystem Write Restrictions:Allowed: {fsWriteConfig.allowOnly.join(", ")}{fsWriteConfig.denyWithinAllow.length > 0 && Denied within allowed: {fsWriteConfig.denyWithinAllow.join(", ")}}}{(networkConfig.allowedHosts && networkConfig.allowedHosts.length > 0 || networkConfig.deniedHosts && networkConfig.deniedHosts.length > 0) && Network Restrictions{shouldAllowManagedSandboxDomainsOnly() ? " (Managed)" : ""}:{networkConfig.allowedHosts && networkConfig.allowedHosts.length > 0 && Allowed: {networkConfig.allowedHosts.join(", ")}}{networkConfig.deniedHosts && networkConfig.deniedHosts.length > 0 && Denied: {networkConfig.deniedHosts.join(", ")}}}{allowUnixSockets && allowUnixSockets.length > 0 && Allowed Unix Sockets:{allowUnixSockets.join(", ")}}{globPatternWarnings.length > 0 && ⚠ Warning: Glob patterns not fully supported on LinuxThe following patterns will be ignored:{" "}{globPatternWarnings.slice(0, 3).join(", ")}{globPatternWarnings.length > 3 && ` (${globPatternWarnings.length - 3} more)`}}{warningsNote}; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} +function _temp(w, i) { + return {w}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJTYW5kYm94TWFuYWdlciIsInNob3VsZEFsbG93TWFuYWdlZFNhbmRib3hEb21haW5zT25seSIsIlNhbmRib3hDb25maWdUYWIiLCIkIiwiX2MiLCJpc0VuYWJsZWQiLCJpc1NhbmRib3hpbmdFbmFibGVkIiwidDAiLCJTeW1ib2wiLCJmb3IiLCJkZXBDaGVjayIsImNoZWNrRGVwZW5kZW5jaWVzIiwid2FybmluZ3MiLCJsZW5ndGgiLCJtYXAiLCJfdGVtcCIsIndhcm5pbmdzTm90ZSIsInQxIiwiZnNSZWFkQ29uZmlnIiwiZ2V0RnNSZWFkQ29uZmlnIiwiZnNXcml0ZUNvbmZpZyIsImdldEZzV3JpdGVDb25maWciLCJuZXR3b3JrQ29uZmlnIiwiZ2V0TmV0d29ya1Jlc3RyaWN0aW9uQ29uZmlnIiwiYWxsb3dVbml4U29ja2V0cyIsImdldEFsbG93VW5peFNvY2tldHMiLCJleGNsdWRlZENvbW1hbmRzIiwiZ2V0RXhjbHVkZWRDb21tYW5kcyIsImdsb2JQYXR0ZXJuV2FybmluZ3MiLCJnZXRMaW51eEdsb2JQYXR0ZXJuV2FybmluZ3MiLCJqb2luIiwiZGVueU9ubHkiLCJhbGxvd1dpdGhpbkRlbnkiLCJhbGxvd09ubHkiLCJkZW55V2l0aGluQWxsb3ciLCJhbGxvd2VkSG9zdHMiLCJkZW5pZWRIb3N0cyIsInNsaWNlIiwidyIsImkiXSwic291cmNlcyI6WyJTYW5kYm94Q29uZmlnVGFiLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7XG4gIFNhbmRib3hNYW5hZ2VyLFxuICBzaG91bGRBbGxvd01hbmFnZWRTYW5kYm94RG9tYWluc09ubHksXG59IGZyb20gJy4uLy4uL3V0aWxzL3NhbmRib3gvc2FuZGJveC1hZGFwdGVyLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gU2FuZGJveENvbmZpZ1RhYigpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBpc0VuYWJsZWQgPSBTYW5kYm94TWFuYWdlci5pc1NhbmRib3hpbmdFbmFibGVkKClcblxuICAvLyBTaG93IHdhcm5pbmdzIChlLmcuLCBzZWNjb21wIG5vdCBhdmFpbGFibGUgb24gTGludXgpXG4gIGNvbnN0IGRlcENoZWNrID0gU2FuZGJveE1hbmFnZXIuY2hlY2tEZXBlbmRlbmNpZXMoKVxuICBjb25zdCB3YXJuaW5nc05vdGUgPVxuICAgIGRlcENoZWNrLndhcm5pbmdzLmxlbmd0aCA+IDAgPyAoXG4gICAgICA8Qm94IG1hcmdpblRvcD17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICB7ZGVwQ2hlY2sud2FybmluZ3MubWFwKCh3LCBpKSA9PiAoXG4gICAgICAgICAgPFRleHQga2V5PXtpfSBkaW1Db2xvcj5cbiAgICAgICAgICAgIHt3fVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKSl9XG4gICAgICA8L0JveD5cbiAgICApIDogbnVsbFxuXG4gIGlmICghaXNFbmFibGVkKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdZPXsxfT5cbiAgICAgICAgPFRleHQgY29sb3I9XCJzdWJ0bGVcIj5TYW5kYm94IGlzIG5vdCBlbmFibGVkPC9UZXh0PlxuICAgICAgICB7d2FybmluZ3NOb3RlfVxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgY29uc3QgZnNSZWFkQ29uZmlnID0gU2FuZGJveE1hbmFnZXIuZ2V0RnNSZWFkQ29uZmlnKClcbiAgY29uc3QgZnNXcml0ZUNvbmZpZyA9IFNhbmRib3hNYW5hZ2VyLmdldEZzV3JpdGVDb25maWcoKVxuICBjb25zdCBuZXR3b3JrQ29uZmlnID0gU2FuZGJveE1hbmFnZXIuZ2V0TmV0d29ya1Jlc3RyaWN0aW9uQ29uZmlnKClcbiAgY29uc3QgYWxsb3dVbml4U29ja2V0cyA9IFNhbmRib3hNYW5hZ2VyLmdldEFsbG93VW5peFNvY2tldHMoKVxuICBjb25zdCBleGNsdWRlZENvbW1hbmRzID0gU2FuZGJveE1hbmFnZXIuZ2V0RXhjbHVkZWRDb21tYW5kcygpXG4gIGNvbnN0IGdsb2JQYXR0ZXJuV2FybmluZ3MgPSBTYW5kYm94TWFuYWdlci5nZXRMaW51eEdsb2JQYXR0ZXJuV2FybmluZ3MoKVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgcGFkZGluZ1k9ezF9PlxuICAgICAgey8qIEV4Y2x1ZGVkIENvbW1hbmRzICovfVxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgIDxUZXh0IGJvbGQgY29sb3I9XCJwZXJtaXNzaW9uXCI+XG4gICAgICAgICAgRXhjbHVkZWQgQ29tbWFuZHM6XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAge2V4Y2x1ZGVkQ29tbWFuZHMubGVuZ3RoID4gMCA/IGV4Y2x1ZGVkQ29tbWFuZHMuam9pbignLCAnKSA6ICdOb25lJ31cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG5cbiAgICAgIHsvKiBGaWxlc3lzdGVtIFJlYWQgUmVzdHJpY3Rpb25zICovfVxuICAgICAge2ZzUmVhZENvbmZpZy5kZW55T25seS5sZW5ndGggPiAwICYmIChcbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBib2xkIGNvbG9yPVwicGVybWlzc2lvblwiPlxuICAgICAgICAgICAgRmlsZXN5c3RlbSBSZWFkIFJlc3RyaWN0aW9uczpcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+RGVuaWVkOiB7ZnNSZWFkQ29uZmlnLmRlbnlPbmx5LmpvaW4oJywgJyl9PC9UZXh0PlxuICAgICAgICAgIHtmc1JlYWRDb25maWcuYWxsb3dXaXRoaW5EZW55ICYmXG4gICAgICAgICAgICBmc1JlYWRDb25maWcuYWxsb3dXaXRoaW5EZW55Lmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICBBbGxvd2VkIHdpdGhpbiBkZW5pZWQ6IHtmc1JlYWRDb25maWcuYWxsb3dXaXRoaW5EZW55LmpvaW4oJywgJyl9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICl9XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cblxuICAgICAgey8qIEZpbGVzeXN0ZW0gV3JpdGUgUmVzdHJpY3Rpb25zICovfVxuICAgICAge2ZzV3JpdGVDb25maWcuYWxsb3dPbmx5Lmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0IGJvbGQgY29sb3I9XCJwZXJtaXNzaW9uXCI+XG4gICAgICAgICAgICBGaWxlc3lzdGVtIFdyaXRlIFJlc3RyaWN0aW9uczpcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+QWxsb3dlZDoge2ZzV3JpdGVDb25maWcuYWxsb3dPbmx5LmpvaW4oJywgJyl9PC9UZXh0PlxuICAgICAgICAgIHtmc1dyaXRlQ29uZmlnLmRlbnlXaXRoaW5BbGxvdy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICBEZW5pZWQgd2l0aGluIGFsbG93ZWQ6IHtmc1dyaXRlQ29uZmlnLmRlbnlXaXRoaW5BbGxvdy5qb2luKCcsICcpfVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICl9XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cblxuICAgICAgey8qIE5ldHdvcmsgUmVzdHJpY3Rpb25zICovfVxuICAgICAgeygobmV0d29ya0NvbmZpZy5hbGxvd2VkSG9zdHMgJiYgbmV0d29ya0NvbmZpZy5hbGxvd2VkSG9zdHMubGVuZ3RoID4gMCkgfHxcbiAgICAgICAgKG5ldHdvcmtDb25maWcuZGVuaWVkSG9zdHMgJiZcbiAgICAgICAgICBuZXR3b3JrQ29uZmlnLmRlbmllZEhvc3RzLmxlbmd0aCA+IDApKSAmJiAoXG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPFRleHQgYm9sZCBjb2xvcj1cInBlcm1pc3Npb25cIj5cbiAgICAgICAgICAgIE5ldHdvcmsgUmVzdHJpY3Rpb25zXG4gICAgICAgICAgICB7c2hvdWxkQWxsb3dNYW5hZ2VkU2FuZGJveERvbWFpbnNPbmx5KCkgPyAnIChNYW5hZ2VkKScgOiAnJ306XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIHtuZXR3b3JrQ29uZmlnLmFsbG93ZWRIb3N0cyAmJlxuICAgICAgICAgICAgbmV0d29ya0NvbmZpZy5hbGxvd2VkSG9zdHMubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgIEFsbG93ZWQ6IHtuZXR3b3JrQ29uZmlnLmFsbG93ZWRIb3N0cy5qb2luKCcsICcpfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIHtuZXR3b3JrQ29uZmlnLmRlbmllZEhvc3RzICYmXG4gICAgICAgICAgICBuZXR3b3JrQ29uZmlnLmRlbmllZEhvc3RzLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICBEZW5pZWQ6IHtuZXR3b3JrQ29uZmlnLmRlbmllZEhvc3RzLmpvaW4oJywgJyl9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICl9XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cblxuICAgICAgey8qIFVuaXggU29ja2V0cyAqL31cbiAgICAgIHthbGxvd1VuaXhTb2NrZXRzICYmIGFsbG93VW5peFNvY2tldHMubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgIDxCb3ggbWFyZ2luVG9wPXsxfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPFRleHQgYm9sZCBjb2xvcj1cInBlcm1pc3Npb25cIj5cbiAgICAgICAgICAgIEFsbG93ZWQgVW5peCBTb2NrZXRzOlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57YWxsb3dVbml4U29ja2V0cy5qb2luKCcsICcpfTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuXG4gICAgICB7LyogTGludXggR2xvYiBQYXR0ZXJuIFdhcm5pbmcgKi99XG4gICAgICB7Z2xvYlBhdHRlcm5XYXJuaW5ncy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBib2xkIGNvbG9yPVwid2FybmluZ1wiPlxuICAgICAgICAgICAg4pqgIFdhcm5pbmc6IEdsb2IgcGF0dGVybnMgbm90IGZ1bGx5IHN1cHBvcnRlZCBvbiBMaW51eFxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIFRoZSBmb2xsb3dpbmcgcGF0dGVybnMgd2lsbCBiZSBpZ25vcmVkOnsnICd9XG4gICAgICAgICAgICB7Z2xvYlBhdHRlcm5XYXJuaW5ncy5zbGljZSgwLCAzKS5qb2luKCcsICcpfVxuICAgICAgICAgICAge2dsb2JQYXR0ZXJuV2FybmluZ3MubGVuZ3RoID4gMyAmJlxuICAgICAgICAgICAgICBgICgke2dsb2JQYXR0ZXJuV2FybmluZ3MubGVuZ3RoIC0gM30gbW9yZSlgfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuXG4gICAgICB7d2FybmluZ3NOb3RlfVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsU0FDRUMsY0FBYyxFQUNkQyxvQ0FBb0MsUUFDL0Isd0NBQXdDO0FBRS9DLE9BQU8sU0FBQUMsaUJBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFDTCxNQUFBQyxTQUFBLEdBQWtCTCxjQUFjLENBQUFNLG1CQUFvQixDQUFDLENBQUM7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQUosQ0FBQSxRQUFBSyxNQUFBLENBQUFDLEdBQUE7SUFHdEQsTUFBQUMsUUFBQSxHQUFpQlYsY0FBYyxDQUFBVyxpQkFBa0IsQ0FBQyxDQUFDO0lBRWpESixFQUFBLEdBQUFHLFFBQVEsQ0FBQUUsUUFBUyxDQUFBQyxNQUFPLEdBQUcsQ0FRbkIsR0FQTixDQUFDLEdBQUcsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUFnQixhQUFRLENBQVIsUUFBUSxDQUN0QyxDQUFBSCxRQUFRLENBQUFFLFFBQVMsQ0FBQUUsR0FBSSxDQUFDQyxLQUl0QixFQUNILEVBTkMsR0FBRyxDQU9FLEdBUlIsSUFRUTtJQUFBWixDQUFBLE1BQUFJLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFKLENBQUE7RUFBQTtFQVRWLE1BQUFhLFlBQUEsR0FDRVQsRUFRUTtFQUVWLElBQUksQ0FBQ0YsU0FBUztJQUFBLElBQUFZLEVBQUE7SUFBQSxJQUFBZCxDQUFBLFFBQUFLLE1BQUEsQ0FBQUMsR0FBQTtNQUVWUSxFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQVcsUUFBQyxDQUFELEdBQUMsQ0FDckMsQ0FBQyxJQUFJLENBQU8sS0FBUSxDQUFSLFFBQVEsQ0FBQyxzQkFBc0IsRUFBMUMsSUFBSSxDQUNKRCxhQUFXLENBQ2QsRUFIQyxHQUFHLENBR0U7TUFBQWIsQ0FBQSxNQUFBYyxFQUFBO0lBQUE7TUFBQUEsRUFBQSxHQUFBZCxDQUFBO0lBQUE7SUFBQSxPQUhOYyxFQUdNO0VBQUE7RUFFVCxJQUFBQSxFQUFBO0VBQUEsSUFBQWQsQ0FBQSxRQUFBSyxNQUFBLENBQUFDLEdBQUE7SUFFRCxNQUFBUyxZQUFBLEdBQXFCbEIsY0FBYyxDQUFBbUIsZUFBZ0IsQ0FBQyxDQUFDO0lBQ3JELE1BQUFDLGFBQUEsR0FBc0JwQixjQUFjLENBQUFxQixnQkFBaUIsQ0FBQyxDQUFDO0lBQ3ZELE1BQUFDLGFBQUEsR0FBc0J0QixjQUFjLENBQUF1QiwyQkFBNEIsQ0FBQyxDQUFDO0lBQ2xFLE1BQUFDLGdCQUFBLEdBQXlCeEIsY0FBYyxDQUFBeUIsbUJBQW9CLENBQUMsQ0FBQztJQUM3RCxNQUFBQyxnQkFBQSxHQUF5QjFCLGNBQWMsQ0FBQTJCLG1CQUFvQixDQUFDLENBQUM7SUFDN0QsTUFBQUMsbUJBQUEsR0FBNEI1QixjQUFjLENBQUE2QiwyQkFBNEIsQ0FBQyxDQUFDO0lBR3RFWixFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQVcsUUFBQyxDQUFELEdBQUMsQ0FFckMsQ0FBQyxHQUFHLENBQWUsYUFBUSxDQUFSLFFBQVEsQ0FDekIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFKLEtBQUcsQ0FBQyxDQUFPLEtBQVksQ0FBWixZQUFZLENBQUMsa0JBRTlCLEVBRkMsSUFBSSxDQUdMLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FDWCxDQUFBUyxnQkFBZ0IsQ0FBQWIsTUFBTyxHQUFHLENBQXdDLEdBQXBDYSxnQkFBZ0IsQ0FBQUksSUFBSyxDQUFDLElBQWEsQ0FBQyxHQUFsRSxNQUFpRSxDQUNwRSxFQUZDLElBQUksQ0FHUCxFQVBDLEdBQUcsQ0FVSCxDQUFBWixZQUFZLENBQUFhLFFBQVMsQ0FBQWxCLE1BQU8sR0FBRyxDQWEvQixJQVpDLENBQUMsR0FBRyxDQUFZLFNBQUMsQ0FBRCxHQUFDLENBQWdCLGFBQVEsQ0FBUixRQUFRLENBQ3ZDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBSixLQUFHLENBQUMsQ0FBTyxLQUFZLENBQVosWUFBWSxDQUFDLDZCQUU5QixFQUZDLElBQUksQ0FHTCxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsUUFBUyxDQUFBSyxZQUFZLENBQUFhLFFBQVMsQ0FBQUQsSUFBSyxDQUFDLElBQUksRUFBRSxFQUF4RCxJQUFJLENBQ0osQ0FBQVosWUFBWSxDQUFBYyxlQUM0QixJQUF2Q2QsWUFBWSxDQUFBYyxlQUFnQixDQUFBbkIsTUFBTyxHQUFHLENBSXJDLElBSEMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLHVCQUNXLENBQUFLLFlBQVksQ0FBQWMsZUFBZ0IsQ0FBQUYsSUFBSyxDQUFDLElBQUksRUFDaEUsRUFGQyxJQUFJLENBR1AsQ0FDSixFQVhDLEdBQUcsQ0FZTixDQUdDLENBQUFWLGFBQWEsQ0FBQWEsU0FBVSxDQUFBcEIsTUFBTyxHQUFHLENBWWpDLElBWEMsQ0FBQyxHQUFHLENBQVksU0FBQyxDQUFELEdBQUMsQ0FBZ0IsYUFBUSxDQUFSLFFBQVEsQ0FDdkMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFKLEtBQUcsQ0FBQyxDQUFPLEtBQVksQ0FBWixZQUFZLENBQUMsOEJBRTlCLEVBRkMsSUFBSSxDQUdMLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxTQUFVLENBQUFPLGFBQWEsQ0FBQWEsU0FBVSxDQUFBSCxJQUFLLENBQUMsSUFBSSxFQUFFLEVBQTNELElBQUksQ0FDSixDQUFBVixhQUFhLENBQUFjLGVBQWdCLENBQUFyQixNQUFPLEdBQUcsQ0FJdkMsSUFIQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsdUJBQ1csQ0FBQU8sYUFBYSxDQUFBYyxlQUFnQixDQUFBSixJQUFLLENBQUMsSUFBSSxFQUNqRSxFQUZDLElBQUksQ0FHUCxDQUNGLEVBVkMsR0FBRyxDQVdOLENBR0MsRUFBRVIsYUFBYSxDQUFBYSxZQUFzRCxJQUFyQ2IsYUFBYSxDQUFBYSxZQUFhLENBQUF0QixNQUFPLEdBQUcsQ0FFNUIsSUFEdENTLGFBQWEsQ0FBQWMsV0FDd0IsSUFBcENkLGFBQWEsQ0FBQWMsV0FBWSxDQUFBdkIsTUFBTyxHQUFHLENBbUJ0QyxLQWxCQyxDQUFDLEdBQUcsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUFnQixhQUFRLENBQVIsUUFBUSxDQUN2QyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUosS0FBRyxDQUFDLENBQU8sS0FBWSxDQUFaLFlBQVksQ0FBQyxvQkFFM0IsQ0FBQVosb0NBQW9DLENBQXFCLENBQUMsR0FBMUQsWUFBMEQsR0FBMUQsRUFBeUQsQ0FBRSxDQUM5RCxFQUhDLElBQUksQ0FJSixDQUFBcUIsYUFBYSxDQUFBYSxZQUN5QixJQUFyQ2IsYUFBYSxDQUFBYSxZQUFhLENBQUF0QixNQUFPLEdBQUcsQ0FJbkMsSUFIQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsU0FDSCxDQUFBUyxhQUFhLENBQUFhLFlBQWEsQ0FBQUwsSUFBSyxDQUFDLElBQUksRUFDaEQsRUFGQyxJQUFJLENBR1AsQ0FDRCxDQUFBUixhQUFhLENBQUFjLFdBQ3dCLElBQXBDZCxhQUFhLENBQUFjLFdBQVksQ0FBQXZCLE1BQU8sR0FBRyxDQUlsQyxJQUhDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxRQUNKLENBQUFTLGFBQWEsQ0FBQWMsV0FBWSxDQUFBTixJQUFLLENBQUMsSUFBSSxFQUM5QyxFQUZDLElBQUksQ0FHUCxDQUNKLEVBakJDLEdBQUcsQ0FrQk4sQ0FHQyxDQUFBTixnQkFBK0MsSUFBM0JBLGdCQUFnQixDQUFBWCxNQUFPLEdBQUcsQ0FPOUMsSUFOQyxDQUFDLEdBQUcsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUFnQixhQUFRLENBQVIsUUFBUSxDQUN2QyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUosS0FBRyxDQUFDLENBQU8sS0FBWSxDQUFaLFlBQVksQ0FBQyxxQkFFOUIsRUFGQyxJQUFJLENBR0wsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFFLENBQUFXLGdCQUFnQixDQUFBTSxJQUFLLENBQUMsSUFBSSxFQUFFLEVBQTNDLElBQUksQ0FDUCxFQUxDLEdBQUcsQ0FNTixDQUdDLENBQUFGLG1CQUFtQixDQUFBZixNQUFPLEdBQUcsQ0FZN0IsSUFYQyxDQUFDLEdBQUcsQ0FBWSxTQUFDLENBQUQsR0FBQyxDQUFnQixhQUFRLENBQVIsUUFBUSxDQUN2QyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUosS0FBRyxDQUFDLENBQU8sS0FBUyxDQUFULFNBQVMsQ0FBQyxxREFFM0IsRUFGQyxJQUFJLENBR0wsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLHVDQUMyQixJQUFFLENBQ3pDLENBQUFlLG1CQUFtQixDQUFBUyxLQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFBUCxJQUFLLENBQUMsSUFBSSxFQUN6QyxDQUFBRixtQkFBbUIsQ0FBQWYsTUFBTyxHQUFHLENBQ2UsSUFENUMsS0FDTWUsbUJBQW1CLENBQUFmLE1BQU8sR0FBRyxDQUFDLFFBQU8sQ0FDOUMsRUFMQyxJQUFJLENBTVAsRUFWQyxHQUFHLENBV04sQ0FFQ0csYUFBVyxDQUNkLEVBNUZDLEdBQUcsQ0E0RkU7SUFBQWIsQ0FBQSxNQUFBYyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBZCxDQUFBO0VBQUE7RUFBQSxPQTVGTmMsRUE0Rk07QUFBQTtBQTdISCxTQUFBRixNQUFBdUIsQ0FBQSxFQUFBQyxDQUFBO0VBQUEsT0FTRyxDQUFDLElBQUksQ0FBTUEsR0FBQyxDQUFEQSxFQUFBLENBQUMsQ0FBRSxRQUFRLENBQVIsS0FBTyxDQUFDLENBQ25CRCxFQUFBLENBQ0gsRUFGQyxJQUFJLENBRUU7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/sandbox/SandboxDependenciesTab.tsx b/components/sandbox/SandboxDependenciesTab.tsx new file mode 100644 index 0000000..c9ecbc5 --- /dev/null +++ b/components/sandbox/SandboxDependenciesTab.tsx @@ -0,0 +1,120 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, Text } from '../../ink.js'; +import { getPlatform } from '../../utils/platform.js'; +import type { SandboxDependencyCheck } from '../../utils/sandbox/sandbox-adapter.js'; +type Props = { + depCheck: SandboxDependencyCheck; +}; +export function SandboxDependenciesTab(t0) { + const $ = _c(24); + const { + depCheck + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = getPlatform(); + $[0] = t1; + } else { + t1 = $[0]; + } + const platform = t1; + const isMac = platform === "macos"; + let t2; + if ($[1] !== depCheck.errors) { + t2 = depCheck.errors.some(_temp); + $[1] = depCheck.errors; + $[2] = t2; + } else { + t2 = $[2]; + } + const rgMissing = t2; + let t3; + if ($[3] !== depCheck.errors) { + t3 = depCheck.errors.some(_temp2); + $[3] = depCheck.errors; + $[4] = t3; + } else { + t3 = $[4]; + } + const bwrapMissing = t3; + let t4; + if ($[5] !== depCheck.errors) { + t4 = depCheck.errors.some(_temp3); + $[5] = depCheck.errors; + $[6] = t4; + } else { + t4 = $[6]; + } + const socatMissing = t4; + const seccompMissing = depCheck.warnings.length > 0; + let t5; + if ($[7] !== bwrapMissing || $[8] !== depCheck.errors || $[9] !== rgMissing || $[10] !== seccompMissing || $[11] !== socatMissing) { + const otherErrors = depCheck.errors.filter(_temp4); + const rgInstallHint = isMac ? "brew install ripgrep" : "apt install ripgrep"; + let t6; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t6 = isMac && seatbelt: built-in (macOS); + $[13] = t6; + } else { + t6 = $[13]; + } + let t7; + let t8; + if ($[14] !== rgMissing) { + t7 = ripgrep (rg):{" "}{rgMissing ? not found : found}; + t8 = rgMissing && {" "}· {rgInstallHint}; + $[14] = rgMissing; + $[15] = t7; + $[16] = t8; + } else { + t7 = $[15]; + t8 = $[16]; + } + let t9; + if ($[17] !== t7 || $[18] !== t8) { + t9 = {t7}{t8}; + $[17] = t7; + $[18] = t8; + $[19] = t9; + } else { + t9 = $[19]; + } + let t10; + if ($[20] !== bwrapMissing || $[21] !== seccompMissing || $[22] !== socatMissing) { + t10 = !isMac && <>bubblewrap (bwrap):{" "}{bwrapMissing ? not installed : installed}{bwrapMissing && {" "}· apt install bubblewrap}socat:{" "}{socatMissing ? not installed : installed}{socatMissing && {" "}· apt install socat}seccomp filter:{" "}{seccompMissing ? not installed : installed}{seccompMissing && (required to block unix domain sockets)}{seccompMissing && {" "}· npm install -g @anthropic-ai/sandbox-runtime{" "}· or copy vendor/seccomp/* from sandbox-runtime and set{" "}sandbox.seccomp.bpfPath and applyPath in settings.json}; + $[20] = bwrapMissing; + $[21] = seccompMissing; + $[22] = socatMissing; + $[23] = t10; + } else { + t10 = $[23]; + } + t5 = {t6}{t9}{t10}{otherErrors.map(_temp5)}; + $[7] = bwrapMissing; + $[8] = depCheck.errors; + $[9] = rgMissing; + $[10] = seccompMissing; + $[11] = socatMissing; + $[12] = t5; + } else { + t5 = $[12]; + } + return t5; +} +function _temp5(err) { + return {err}; +} +function _temp4(e_2) { + return !e_2.includes("ripgrep") && !e_2.includes("bwrap") && !e_2.includes("socat"); +} +function _temp3(e_1) { + return e_1.includes("socat"); +} +function _temp2(e_0) { + return e_0.includes("bwrap"); +} +function _temp(e) { + return e.includes("ripgrep"); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJnZXRQbGF0Zm9ybSIsIlNhbmRib3hEZXBlbmRlbmN5Q2hlY2siLCJQcm9wcyIsImRlcENoZWNrIiwiU2FuZGJveERlcGVuZGVuY2llc1RhYiIsInQwIiwiJCIsIl9jIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJwbGF0Zm9ybSIsImlzTWFjIiwidDIiLCJlcnJvcnMiLCJzb21lIiwiX3RlbXAiLCJyZ01pc3NpbmciLCJ0MyIsIl90ZW1wMiIsImJ3cmFwTWlzc2luZyIsInQ0IiwiX3RlbXAzIiwic29jYXRNaXNzaW5nIiwic2VjY29tcE1pc3NpbmciLCJ3YXJuaW5ncyIsImxlbmd0aCIsInQ1Iiwib3RoZXJFcnJvcnMiLCJmaWx0ZXIiLCJfdGVtcDQiLCJyZ0luc3RhbGxIaW50IiwidDYiLCJ0NyIsInQ4IiwidDkiLCJ0MTAiLCJtYXAiLCJfdGVtcDUiLCJlcnIiLCJlXzIiLCJlIiwiaW5jbHVkZXMiLCJlXzEiLCJlXzAiXSwic291cmNlcyI6WyJTYW5kYm94RGVwZW5kZW5jaWVzVGFiLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBnZXRQbGF0Zm9ybSB9IGZyb20gJy4uLy4uL3V0aWxzL3BsYXRmb3JtLmpzJ1xuaW1wb3J0IHR5cGUgeyBTYW5kYm94RGVwZW5kZW5jeUNoZWNrIH0gZnJvbSAnLi4vLi4vdXRpbHMvc2FuZGJveC9zYW5kYm94LWFkYXB0ZXIuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGRlcENoZWNrOiBTYW5kYm94RGVwZW5kZW5jeUNoZWNrXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTYW5kYm94RGVwZW5kZW5jaWVzVGFiKHsgZGVwQ2hlY2sgfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBwbGF0Zm9ybSA9IGdldFBsYXRmb3JtKClcbiAgY29uc3QgaXNNYWMgPSBwbGF0Zm9ybSA9PT0gJ21hY29zJ1xuXG4gIC8vIHJpcGdyZXAgaXMgcmVxdWlyZWQgb24gYWxsIHBsYXRmb3JtcyAodXNlZCB0byBzY2FuIGZvciBkYW5nZXJvdXMgZGlycykuXG4gIC8vIE9uIG1hY09TLCBzZWF0YmVsdCBpcyBidWlsdCBpbnRvIHRoZSBPUyDigJQgcmlwZ3JlcCBpcyB0aGUgb25seSBydW50aW1lIGRlcC5cbiAgLy8gT24gTGludXgvV1NMLCBid3JhcCArIHNvY2F0IGFyZSByZXF1aXJlZCwgc2VjY29tcCBpcyBvcHRpb25hbC5cbiAgLy9cbiAgLy8gIzMxODA0OiBwcmV2aW91c2x5IHRoaXMgdGFiIHVuY29uZGl0aW9uYWxseSByZW5kZXJlZCBMaW51eCBkZXBzIChid3JhcCxcbiAgLy8gc29jYXQsIHNlY2NvbXApLiBXaGVuIHJpcGdyZXAgd2FzIG1pc3Npbmcgb24gbWFjT1MsIHVzZXJzIHNhdyBjb25mdXNpbmdcbiAgLy8gTGludXggaW5zdGFsbCBpbnN0cnVjdGlvbnMgYW5kIG5vIG1lbnRpb24gb2YgdGhlIGFjdHVhbCBwcm9ibGVtLlxuICBjb25zdCByZ01pc3NpbmcgPSBkZXBDaGVjay5lcnJvcnMuc29tZShlID0+IGUuaW5jbHVkZXMoJ3JpcGdyZXAnKSlcbiAgY29uc3QgYndyYXBNaXNzaW5nID0gZGVwQ2hlY2suZXJyb3JzLnNvbWUoZSA9PiBlLmluY2x1ZGVzKCdid3JhcCcpKVxuICBjb25zdCBzb2NhdE1pc3NpbmcgPSBkZXBDaGVjay5lcnJvcnMuc29tZShlID0+IGUuaW5jbHVkZXMoJ3NvY2F0JykpXG4gIGNvbnN0IHNlY2NvbXBNaXNzaW5nID0gZGVwQ2hlY2sud2FybmluZ3MubGVuZ3RoID4gMFxuXG4gIC8vIEFueSBlcnJvcnMgd2UgZG9uJ3QgaGF2ZSBhIGRlZGljYXRlZCByb3cgZm9yIOKAlCByZW5kZXIgdmVyYmF0aW0gc28gdGhleVxuICAvLyBhcmVuJ3Qgc2lsZW50bHkgc3dhbGxvd2VkIChlLmcuIFwiVW5zdXBwb3J0ZWQgcGxhdGZvcm1cIiBvciBmdXR1cmUgZGVwcykuXG4gIGNvbnN0IG90aGVyRXJyb3JzID0gZGVwQ2hlY2suZXJyb3JzLmZpbHRlcihcbiAgICBlID0+ICFlLmluY2x1ZGVzKCdyaXBncmVwJykgJiYgIWUuaW5jbHVkZXMoJ2J3cmFwJykgJiYgIWUuaW5jbHVkZXMoJ3NvY2F0JyksXG4gIClcblxuICBjb25zdCByZ0luc3RhbGxIaW50ID0gaXNNYWMgPyAnYnJldyBpbnN0YWxsIHJpcGdyZXAnIDogJ2FwdCBpbnN0YWxsIHJpcGdyZXAnXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nWT17MX0gZ2FwPXsxfT5cbiAgICAgIHtpc01hYyAmJiAoXG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgc2VhdGJlbHQ6IDxUZXh0IGNvbG9yPVwic3VjY2Vzc1wiPmJ1aWx0LWluIChtYWNPUyk8L1RleHQ+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG5cbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICByaXBncmVwIChyZyk6eycgJ31cbiAgICAgICAgICB7cmdNaXNzaW5nID8gKFxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPm5vdCBmb3VuZDwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+Zm91bmQ8L1RleHQ+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICB7cmdNaXNzaW5nICYmIChcbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIHsnICAnfcK3IHtyZ0luc3RhbGxIaW50fVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKX1cbiAgICAgIDwvQm94PlxuXG4gICAgICB7IWlzTWFjICYmIChcbiAgICAgICAgPD5cbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICBidWJibGV3cmFwIChid3JhcCk6eycgJ31cbiAgICAgICAgICAgICAge2J3cmFwTWlzc2luZyA/IChcbiAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+bm90IGluc3RhbGxlZDwvVGV4dD5cbiAgICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj5pbnN0YWxsZWQ8L1RleHQ+XG4gICAgICAgICAgICAgICl9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICB7YndyYXBNaXNzaW5nICYmIChcbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+eycgICd9wrcgYXB0IGluc3RhbGwgYnViYmxld3JhcDwvVGV4dD5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgICBzb2NhdDp7JyAnfVxuICAgICAgICAgICAgICB7c29jYXRNaXNzaW5nID8gKFxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5ub3QgaW5zdGFsbGVkPC9UZXh0PlxuICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VjY2Vzc1wiPmluc3RhbGxlZDwvVGV4dD5cbiAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIHtzb2NhdE1pc3NpbmcgJiYgPFRleHQgZGltQ29sb3I+eycgICd9wrcgYXB0IGluc3RhbGwgc29jYXQ8L1RleHQ+fVxuICAgICAgICAgIDwvQm94PlxuXG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgICAgc2VjY29tcCBmaWx0ZXI6eycgJ31cbiAgICAgICAgICAgICAge3NlY2NvbXBNaXNzaW5nID8gKFxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPm5vdCBpbnN0YWxsZWQ8L1RleHQ+XG4gICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+aW5zdGFsbGVkPC9UZXh0PlxuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICB7c2VjY29tcE1pc3NpbmcgJiYgKFxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiAocmVxdWlyZWQgdG8gYmxvY2sgdW5peCBkb21haW4gc29ja2V0cyk8L1RleHQ+XG4gICAgICAgICAgICAgICl9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICB7c2VjY29tcE1pc3NpbmcgJiYgKFxuICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICAgIHsnICAnfcK3IG5wbSBpbnN0YWxsIC1nIEBhbnRocm9waWMtYWkvc2FuZGJveC1ydW50aW1lXG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgeycgICd9wrcgb3IgY29weSB2ZW5kb3Ivc2VjY29tcC8qIGZyb20gc2FuZGJveC1ydW50aW1lIGFuZCBzZXRcbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICB7JyAgICAnfXNhbmRib3guc2VjY29tcC5icGZQYXRoIGFuZCBhcHBseVBhdGggaW4gc2V0dGluZ3MuanNvblxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8Lz5cbiAgICAgICl9XG5cbiAgICAgIHtvdGhlckVycm9ycy5tYXAoZXJyID0+IChcbiAgICAgICAgPFRleHQga2V5PXtlcnJ9IGNvbG9yPVwiZXJyb3JcIj5cbiAgICAgICAgICB7ZXJyfVxuICAgICAgICA8L1RleHQ+XG4gICAgICApKX1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxXQUFXLFFBQVEseUJBQXlCO0FBQ3JELGNBQWNDLHNCQUFzQixRQUFRLHdDQUF3QztBQUVwRixLQUFLQyxLQUFLLEdBQUc7RUFDWEMsUUFBUSxFQUFFRixzQkFBc0I7QUFDbEMsQ0FBQztBQUVELE9BQU8sU0FBQUcsdUJBQUFDLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBZ0M7SUFBQUo7RUFBQSxJQUFBRSxFQUFtQjtFQUFBLElBQUFHLEVBQUE7RUFBQSxJQUFBRixDQUFBLFFBQUFHLE1BQUEsQ0FBQUMsR0FBQTtJQUN2Q0YsRUFBQSxHQUFBUixXQUFXLENBQUMsQ0FBQztJQUFBTSxDQUFBLE1BQUFFLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFGLENBQUE7RUFBQTtFQUE5QixNQUFBSyxRQUFBLEdBQWlCSCxFQUFhO0VBQzlCLE1BQUFJLEtBQUEsR0FBY0QsUUFBUSxLQUFLLE9BQU87RUFBQSxJQUFBRSxFQUFBO0VBQUEsSUFBQVAsQ0FBQSxRQUFBSCxRQUFBLENBQUFXLE1BQUE7SUFTaEJELEVBQUEsR0FBQVYsUUFBUSxDQUFBVyxNQUFPLENBQUFDLElBQUssQ0FBQ0MsS0FBMEIsQ0FBQztJQUFBVixDQUFBLE1BQUFILFFBQUEsQ0FBQVcsTUFBQTtJQUFBUixDQUFBLE1BQUFPLEVB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 \ No newline at end of file diff --git a/components/sandbox/SandboxDoctorSection.tsx b/components/sandbox/SandboxDoctorSection.tsx new file mode 100644 index 0000000..5d899db --- /dev/null +++ b/components/sandbox/SandboxDoctorSection.tsx @@ -0,0 +1,46 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, Text } from '../../ink.js'; +import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js'; +export function SandboxDoctorSection() { + const $ = _c(2); + if (!SandboxManager.isSupportedPlatform()) { + return null; + } + if (!SandboxManager.isSandboxEnabledInSettings()) { + return null; + } + let t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Symbol.for("react.early_return_sentinel"); + bb0: { + const depCheck = SandboxManager.checkDependencies(); + const hasErrors = depCheck.errors.length > 0; + const hasWarnings = depCheck.warnings.length > 0; + if (!hasErrors && !hasWarnings) { + t1 = null; + break bb0; + } + const statusColor = hasErrors ? "error" as const : "warning" as const; + const statusText = hasErrors ? "Missing dependencies" : "Available (with warnings)"; + t0 = Sandbox└ Status: {statusText}{depCheck.errors.map(_temp)}{depCheck.warnings.map(_temp2)}{hasErrors && └ Run /sandbox for install instructions}; + } + $[0] = t0; + $[1] = t1; + } else { + t0 = $[0]; + t1 = $[1]; + } + if (t1 !== Symbol.for("react.early_return_sentinel")) { + return t1; + } + return t0; +} +function _temp2(w, i_0) { + return └ {w}; +} +function _temp(e, i) { + return └ {e}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJTYW5kYm94TWFuYWdlciIsIlNhbmRib3hEb2N0b3JTZWN0aW9uIiwiJCIsIl9jIiwiaXNTdXBwb3J0ZWRQbGF0Zm9ybSIsImlzU2FuZGJveEVuYWJsZWRJblNldHRpbmdzIiwidDAiLCJ0MSIsIlN5bWJvbCIsImZvciIsImJiMCIsImRlcENoZWNrIiwiY2hlY2tEZXBlbmRlbmNpZXMiLCJoYXNFcnJvcnMiLCJlcnJvcnMiLCJsZW5ndGgiLCJoYXNXYXJuaW5ncyIsIndhcm5pbmdzIiwic3RhdHVzQ29sb3IiLCJjb25zdCIsInN0YXR1c1RleHQiLCJtYXAiLCJfdGVtcCIsIl90ZW1wMiIsInciLCJpXzAiLCJpIiwiZSJdLCJzb3VyY2VzIjpbIlNhbmRib3hEb2N0b3JTZWN0aW9uLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBTYW5kYm94TWFuYWdlciB9IGZyb20gJy4uLy4uL3V0aWxzL3NhbmRib3gvc2FuZGJveC1hZGFwdGVyLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gU2FuZGJveERvY3RvclNlY3Rpb24oKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKCFTYW5kYm94TWFuYWdlci5pc1N1cHBvcnRlZFBsYXRmb3JtKCkpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKCFTYW5kYm94TWFuYWdlci5pc1NhbmRib3hFbmFibGVkSW5TZXR0aW5ncygpKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IGRlcENoZWNrID0gU2FuZGJveE1hbmFnZXIuY2hlY2tEZXBlbmRlbmNpZXMoKVxuICBjb25zdCBoYXNFcnJvcnMgPSBkZXBDaGVjay5lcnJvcnMubGVuZ3RoID4gMFxuICBjb25zdCBoYXNXYXJuaW5ncyA9IGRlcENoZWNrLndhcm5pbmdzLmxlbmd0aCA+IDBcblxuICBpZiAoIWhhc0Vycm9ycyAmJiAhaGFzV2FybmluZ3MpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgY29uc3Qgc3RhdHVzQ29sb3IgPSBoYXNFcnJvcnMgPyAoJ2Vycm9yJyBhcyBjb25zdCkgOiAoJ3dhcm5pbmcnIGFzIGNvbnN0KVxuICBjb25zdCBzdGF0dXNUZXh0ID0gaGFzRXJyb3JzXG4gICAgPyAnTWlzc2luZyBkZXBlbmRlbmNpZXMnXG4gICAgOiAnQXZhaWxhYmxlICh3aXRoIHdhcm5pbmdzKSdcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPFRleHQgYm9sZD5TYW5kYm94PC9UZXh0PlxuICAgICAgPFRleHQ+XG4gICAgICAgIOKUlCBTdGF0dXM6IDxUZXh0IGNvbG9yPXtzdGF0dXNDb2xvcn0+e3N0YXR1c1RleHR9PC9UZXh0PlxuICAgICAgPC9UZXh0PlxuICAgICAge2RlcENoZWNrLmVycm9ycy5tYXAoKGUsIGkpID0+IChcbiAgICAgICAgPFRleHQga2V5PXtpfSBjb2xvcj1cImVycm9yXCI+XG4gICAgICAgICAg4pSUIHtlfVxuICAgICAgICA8L1RleHQ+XG4gICAgICApKX1cbiAgICAgIHtkZXBDaGVjay53YXJuaW5ncy5tYXAoKHcsIGkpID0+IChcbiAgICAgICAgPFRleHQga2V5PXtpfSBjb2xvcj1cIndhcm5pbmdcIj5cbiAgICAgICAgICDilJQge3d9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICkpfVxuICAgICAge2hhc0Vycm9ycyAmJiAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPuKUlCBSdW4gL3NhbmRib3ggZm9yIGluc3RhbGwgaW5zdHJ1Y3Rpb25zPC9UZXh0PlxuICAgICAgKX1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxjQUFjLFFBQVEsd0NBQXdDO0FBRXZFLE9BQU8sU0FBQUMscUJBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFDTCxJQUFJLENBQUNILGNBQWMsQ0FBQUksbUJBQW9CLENBQUMsQ0FBQztJQUFBLE9BQ2hDLElBQUk7RUFBQTtFQUdiLElBQUksQ0FBQ0osY0FBYyxDQUFBSywwQkFBMkIsQ0FBQyxDQUFDO0lBQUEsT0FDdkMsSUFBSTtFQUFBO0VBQ1osSUFBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBTCxDQUFBLFFBQUFNLE1BQUEsQ0FBQUMsR0FBQTtJQU9RRixFQUFBLEdBQUFDLE1BQUksQ0FBQUMsR0FBQSxDQUFKLDZCQUFHLENBQUM7SUFBQUMsR0FBQTtNQUxiLE1BQUFDLFFBQUEsR0FBaUJYLGNBQWMsQ0FBQVksaUJBQWtCLENBQUMsQ0FBQztNQUNuRCxNQUFBQyxTQUFBLEdBQWtCRixRQUFRLENBQUFHLE1BQU8sQ0FBQUMsTUFBTyxHQUFHLENBQUM7TUFDNUMsTUFBQUMsV0FBQSxHQUFvQkwsUUFBUSxDQUFBTSxRQUFTLENBQUFGLE1BQU8sR0FBRyxDQUFDO01BRWhELElBQUksQ0FBQ0YsU0FBeUIsSUFBMUIsQ0FBZUcsV0FBVztRQUNyQlQsRUFBQSxPQUFJO1FBQUosTUFBQUcsR0FBQTtNQUFJO01BR2IsTUFBQVEsV0FBQSxHQUFvQkwsU0FBUyxHQUFJLE9BQU8sSUFBSU0sS0FBNkIsR0FBbkIsU0FBUyxJQUFJQSxLQUFNO01BQ3pFLE1BQUFDLFVBQUEsR0FBbUJQLFNBQVMsR0FBVCxzQkFFWSxHQUZaLDJCQUVZO01BRzdCUCxFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQ3pCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBSixLQUFHLENBQUMsQ0FBQyxPQUFPLEVBQWpCLElBQUksQ0FDTCxDQUFDLElBQUksQ0FBQyxVQUNNLENBQUMsSUFBSSxDQUFRWSxLQUFXLENBQVhBLFlBQVUsQ0FBQyxDQUFHRSxXQUFTLENBQUUsRUFBckMsSUFBSSxDQUNqQixFQUZDLElBQUksQ0FHSixDQUFBVCxRQUFRLENBQUFHLE1BQU8sQ0FBQU8sR0FBSSxDQUFDQyxLQUlwQixFQUNBLENBQUFYLFFBQVEsQ0FBQU0sUUFBUyxDQUFBSSxHQUFJLENBQUNFLE1BSXRCLEVBQ0EsQ0FBQVYsU0FFQSxJQURDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyx1Q0FBdUMsRUFBckQsSUFBSSxDQUNQLENBQ0YsRUFsQkMsR0FBRyxDQWtCRTtJQUFBO0lBQUFYLENBQUEsTUFBQUksRUFBQTtJQUFBSixDQUFBLE1BQUFLLEVBQUE7RUFBQTtJQUFBRCxFQUFBLEdBQUFKLENBQUE7SUFBQUssRUFBQSxHQUFBTCxDQUFBO0VBQUE7RUFBQSxJQUFBSyxFQUFBLEtBQUFDLE1BQUEsQ0FBQUMsR0FBQTtJQUFBLE9BQUFGLEVBQUE7RUFBQTtFQUFBLE9BbEJORCxFQWtCTTtBQUFBO0FBekNILFNBQUFpQixPQUFBQyxDQUFBLEVBQUFDLEdBQUE7RUFBQSxPQWtDQyxDQUFDLElBQUksQ0FBTUMsR0FBQyxDQUFEQSxJQUFBLENBQUMsQ0FBUSxLQUFTLENBQVQsU0FBUyxDQUFDLEVBQ3pCRixFQUFBLENBQ0wsRUFGQyxJQUFJLENBRUU7QUFBQTtBQXBDUixTQUFBRixNQUFBSyxDQUFBLEVBQUFELENBQUE7RUFBQSxPQTZCQyxDQUFDLElBQUksQ0FBTUEsR0FBQyxDQUFEQSxFQUFBLENBQUMsQ0FBUSxLQUFPLENBQVAsT0FBTyxDQUFDLEVBQ3ZCQyxFQUFBLENBQ0wsRUFGQyxJQUFJLENBRUU7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/sandbox/SandboxOverridesTab.tsx b/components/sandbox/SandboxOverridesTab.tsx new file mode 100644 index 0000000..5990b15 --- /dev/null +++ b/components/sandbox/SandboxOverridesTab.tsx @@ -0,0 +1,193 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Box, color, Link, Text, useTheme } from '../../ink.js'; +import type { CommandResultDisplay } from '../../types/command.js'; +import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js'; +import { Select } from '../CustomSelect/select.js'; +import { useTabHeaderFocus } from '../design-system/Tabs.js'; +type Props = { + onComplete: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; +}; +type OverrideMode = 'open' | 'closed'; +export function SandboxOverridesTab(t0) { + const $ = _c(5); + const { + onComplete + } = t0; + const isEnabled = SandboxManager.isSandboxingEnabled(); + const isLocked = SandboxManager.areSandboxSettingsLockedByPolicy(); + const currentAllowUnsandboxed = SandboxManager.areUnsandboxedCommandsAllowed(); + if (!isEnabled) { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Sandbox is not enabled. Enable sandbox to configure override settings.; + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; + } + if (isLocked) { + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = Override settings are managed by a higher-priority configuration and cannot be changed locally.; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = {t1}Current setting:{" "}{currentAllowUnsandboxed ? "Allow unsandboxed fallback" : "Strict sandbox mode"}; + $[2] = t2; + } else { + t2 = $[2]; + } + return t2; + } + let t1; + if ($[3] !== onComplete) { + t1 = ; + $[3] = onComplete; + $[4] = t1; + } else { + t1 = $[4]; + } + return t1; +} + +// Split so useTabHeaderFocus() only runs when the Select renders. Calling it +// above the early returns registers a down-arrow opt-in even when we return +// static text — pressing ↓ then blurs the header with no way back. +function OverridesSelect(t0) { + const $ = _c(25); + const { + onComplete, + currentMode + } = t0; + const [theme] = useTheme(); + const { + headerFocused, + focusHeader + } = useTabHeaderFocus(); + let t1; + if ($[0] !== theme) { + t1 = color("success", theme)("(current)"); + $[0] = theme; + $[1] = t1; + } else { + t1 = $[1]; + } + const currentIndicator = t1; + const t2 = currentMode === "open" ? `Allow unsandboxed fallback ${currentIndicator}` : "Allow unsandboxed fallback"; + let t3; + if ($[2] !== t2) { + t3 = { + label: t2, + value: "open" + }; + $[2] = t2; + $[3] = t3; + } else { + t3 = $[3]; + } + const t4 = currentMode === "closed" ? `Strict sandbox mode ${currentIndicator}` : "Strict sandbox mode"; + let t5; + if ($[4] !== t4) { + t5 = { + label: t4, + value: "closed" + }; + $[4] = t4; + $[5] = t5; + } else { + t5 = $[5]; + } + let t6; + if ($[6] !== t3 || $[7] !== t5) { + t6 = [t3, t5]; + $[6] = t3; + $[7] = t5; + $[8] = t6; + } else { + t6 = $[8]; + } + const options = t6; + let t7; + if ($[9] !== onComplete) { + t7 = async function handleSelect(value) { + const mode = value as OverrideMode; + await SandboxManager.setSandboxSettings({ + allowUnsandboxedCommands: mode === "open" + }); + const message = mode === "open" ? "\u2713 Unsandboxed fallback allowed - commands can run outside sandbox when necessary" : "\u2713 Strict sandbox mode - all commands must run in sandbox or be excluded via the `excludedCommands` option"; + onComplete(message); + }; + $[9] = onComplete; + $[10] = t7; + } else { + t7 = $[10]; + } + const handleSelect = t7; + let t8; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t8 = Configure Overrides:; + $[11] = t8; + } else { + t8 = $[11]; + } + let t9; + if ($[12] !== onComplete) { + t9 = () => onComplete(undefined, { + display: "skip" + }); + $[12] = onComplete; + $[13] = t9; + } else { + t9 = $[13]; + } + let t10; + if ($[14] !== focusHeader || $[15] !== handleSelect || $[16] !== headerFocused || $[17] !== options || $[18] !== t9) { + t10 = ; + $[5] = focusHeader; + $[6] = headerFocused; + $[7] = onSelect; + $[8] = options; + $[9] = t3; + $[10] = t4; + } else { + t4 = $[10]; + } + let t5; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t5 = Auto-allow mode:{" "}Commands will try to run in the sandbox automatically, and attempts to run outside of the sandbox fallback to regular permissions. Explicit ask/deny rules are always respected.; + $[11] = t5; + } else { + t5 = $[11]; + } + let t6; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t6 = {t5}Learn more:{" "}code.claude.com/docs/en/sandboxing; + $[12] = t6; + } else { + t6 = $[12]; + } + let t7; + if ($[13] !== t1 || $[14] !== t4) { + t7 = {t1}{t2}{t4}{t6}; + $[13] = t1; + $[14] = t4; + $[15] = t7; + } else { + t7 = $[15]; + } + return t7; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsImNvbG9yIiwiTGluayIsIlRleHQiLCJ1c2VUaGVtZSIsInVzZUtleWJpbmRpbmdzIiwiQ29tbWFuZFJlc3VsdERpc3BsYXkiLCJTYW5kYm94RGVwZW5kZW5jeUNoZWNrIiwiU2FuZGJveE1hbmFnZXIiLCJnZXRTZXR0aW5nc19ERVBSRUNBVEVEIiwiU2VsZWN0IiwiUGFuZSIsIlRhYiIsIlRhYnMiLCJ1c2VUYWJIZWFkZXJGb2N1cyIsIlNhbmRib3hDb25maWdUYWIiLCJTYW5kYm94RGVwZW5kZW5jaWVzVGFiIiwiU2FuZGJveE92ZXJyaWRlc1RhYiIsIlByb3BzIiwib25Db21wbGV0ZSIsInJlc3VsdCIsIm9wdGlvbnMiLCJkaXNwbGF5IiwiZGVwQ2hlY2siLCJTYW5kYm94TW9kZSIsIlNhbmRib3hTZXR0aW5ncyIsInQwIiwiJCIsIl9jIiwidGhlbWUiLCJjdXJyZW50RW5hYmxlZCIsImlzU2FuZGJveGluZ0VuYWJsZWQiLCJjdXJyZW50QXV0b0FsbG93IiwiaXNBdXRvQWxsb3dCYXNoSWZTYW5kYm94ZWRFbmFibGVkIiwiaGFzV2FybmluZ3MiLCJ3YXJuaW5ncyIsImxlbmd0aCIsInQxIiwiU3ltYm9sIiwiZm9yIiwic2V0dGluZ3MiLCJhbGxvd0FsbFVuaXhTb2NrZXRzIiwic2FuZGJveCIsIm5ldHdvcmsiLCJzaG93U29ja2V0V2FybmluZyIsImdldEN1cnJlbnRNb2RlIiwiY3VycmVudE1vZGUiLCJ0MiIsImN1cnJlbnRJbmRpY2F0b3IiLCJ0MyIsInQ0IiwibGFiZWwiLCJ2YWx1ZSIsInQ1IiwidDYiLCJ0NyIsInQ4IiwidDkiLCJ0MTAiLCJoYW5kbGVTZWxlY3QiLCJtb2RlIiwiYmIzMyIsInNldFNhbmRib3hTZXR0aW5ncyIsImVuYWJsZWQiLCJhdXRvQWxsb3dCYXNoSWZTYW5kYm94ZWQiLCJ0MTEiLCJjb25maXJtOm5vIiwidW5kZWZpbmVkIiwidDEyIiwiY29udGV4dCIsInQxMyIsIm1vZGVUYWIiLCJ0MTQiLCJvdmVycmlkZXNUYWIiLCJ0MTUiLCJjb25maWdUYWIiLCJoYXNFcnJvcnMiLCJlcnJvcnMiLCJ0MTYiLCJ0YWJzIiwidDE3IiwiU2FuZGJveE1vZGVUYWIiLCJvblNlbGVjdCIsImhlYWRlckZvY3VzZWQiLCJmb2N1c0hlYWRlciJdLCJzb3VyY2VzIjpbIlNhbmRib3hTZXR0aW5ncy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBjb2xvciwgTGluaywgVGV4dCwgdXNlVGhlbWUgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5ncyB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgdHlwZSB7IENvbW1hbmRSZXN1bHREaXNwbGF5IH0gZnJvbSAnLi4vLi4vdHlwZXMvY29tbWFuZC5qcydcbmltcG9ydCB0eXBlIHsgU2FuZGJveERlcGVuZGVuY3lDaGVjayB9IGZyb20gJy4uLy4uL3V0aWxzL3NhbmRib3gvc2FuZGJveC1hZGFwdGVyLmpzJ1xuaW1wb3J0IHsgU2FuZGJveE1hbmFnZXIgfSBmcm9tICcuLi8uLi91dGlscy9zYW5kYm94L3NhbmRib3gtYWRhcHRlci5qcydcbmltcG9ydCB7IGdldFNldHRpbmdzX0RFUFJFQ0FURUQgfSBmcm9tICcuLi8uLi91dGlscy9zZXR0aW5ncy9zZXR0aW5ncy5qcydcbmltcG9ydCB7IFNlbGVjdCB9IGZyb20gJy4uL0N1c3RvbVNlbGVjdC9zZWxlY3QuanMnXG5pbXBvcnQgeyBQYW5lIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9QYW5lLmpzJ1xuaW1wb3J0IHsgVGFiLCBUYWJzLCB1c2VUYWJIZWFkZXJGb2N1cyB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vVGFicy5qcydcbmltcG9ydCB7IFNhbmRib3hDb25maWdUYWIgfSBmcm9tICcuL1NhbmRib3hDb25maWdUYWIuanMnXG5pbXBvcnQgeyBTYW5kYm94RGVwZW5kZW5jaWVzVGFiIH0gZnJvbSAnLi9TYW5kYm94RGVwZW5kZW5jaWVzVGFiLmpzJ1xuaW1wb3J0IHsgU2FuZGJveE92ZXJyaWRlc1RhYiB9IGZyb20gJy4vU2FuZGJveE92ZXJyaWRlc1RhYi5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgb25Db21wbGV0ZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkXG4gIGRlcENoZWNrOiBTYW5kYm94RGVwZW5kZW5jeUNoZWNrXG59XG5cbnR5cGUgU2FuZGJveE1vZGUgPSAnYXV0by1hbGxvdycgfCAncmVndWxhcicgfCAnZGlzYWJsZWQnXG5cbmV4cG9ydCBmdW5jdGlvbiBTYW5kYm94U2V0dGluZ3Moe1xuICBvbkNvbXBsZXRlLFxuICBkZXBDaGVjayxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3RoZW1lXSA9IHVzZVRoZW1lKClcbiAgY29uc3QgY3VycmVudEVuYWJsZWQgPSBTYW5kYm94TWFuYWdlci5pc1NhbmRib3hpbmdFbmFibGVkKClcbiAgY29uc3QgY3VycmVudEF1dG9BbGxvdyA9IFNhbmRib3hNYW5hZ2VyLmlzQXV0b0FsbG93QmFzaElmU2FuZGJveGVkRW5hYmxlZCgpXG4gIGNvbnN0IGhhc1dhcm5pbmdzID0gZGVwQ2hlY2sud2FybmluZ3MubGVuZ3RoID4gMFxuICBjb25zdCBzZXR0aW5ncyA9IGdldFNldHRpbmdzX0RFUFJFQ0FURUQoKVxuICBjb25zdCBhbGxvd0FsbFVuaXhTb2NrZXRzID0gc2V0dGluZ3Muc2FuZGJveD8ubmV0d29yaz8uYWxsb3dBbGxVbml4U29ja2V0c1xuICAvLyBTaG93IHdhcm5pbmcgaWYgc2VjY29tcCBtaXNzaW5nIEFORCB1c2VyIGhhc24ndCBhbGxvd2VkIGFsbCB1bml4IHNvY2tldHNcbiAgY29uc3Qgc2hvd1NvY2tldFdhcm5pbmcgPSBoYXNXYXJuaW5ncyAmJiAhYWxsb3dBbGxVbml4U29ja2V0c1xuXG4gIC8vIERldGVybWluZSBjdXJyZW50IG1vZGVcbiAgY29uc3QgZ2V0Q3VycmVudE1vZGUgPSAoKTogU2FuZGJveE1vZGUgPT4ge1xuICAgIGlmICghY3VycmVudEVuYWJsZWQpIHJldHVybiAnZGlzYWJsZWQnXG4gICAgaWYgKGN1cnJlbnRBdXRvQWxsb3cpIHJldHVybiAnYXV0by1hbGxvdydcbiAgICByZXR1cm4gJ3JlZ3VsYXInXG4gIH1cblxuICBjb25zdCBjdXJyZW50TW9kZSA9IGdldEN1cnJlbnRNb2RlKClcbiAgY29uc3QgY3VycmVudEluZGljYXRvciA9IGNvbG9yKCdzdWNjZXNzJywgdGhlbWUpKGAoY3VycmVudClgKVxuXG4gIGNvbnN0IG9wdGlvbnMgPSBbXG4gICAge1xuICAgICAgbGFiZWw6XG4gICAgICAgIGN1cnJlbnRNb2RlID09PSAnYXV0by1hbGxvdydcbiAgICAgICAgICA/IGBTYW5kYm94IEJhc2hUb29sLCB3aXRoIGF1dG8tYWxsb3cgJHtjdXJyZW50SW5kaWNhdG9yfWBcbiAgICAgICAgICA6ICdTYW5kYm94IEJhc2hUb29sLCB3aXRoIGF1dG8tYWxsb3cnLFxuICAgICAgdmFsdWU6ICdhdXRvLWFsbG93JyxcbiAgICB9LFxuICAgIHtcbiAgICAgIGxhYmVsOlxuICAgICAgICBjdXJyZW50TW9kZSA9PT0gJ3JlZ3VsYXInXG4gICAgICAgICAgPyBgU2FuZGJveCBCYXNoVG9vbCwgd2l0aCByZWd1bGFyIHBlcm1pc3Npb25zICR7Y3VycmVudEluZGljYXRvcn1gXG4gICAgICAgICAgOiAnU2FuZGJveCBCYXNoVG9vbCwgd2l0aCByZWd1bGFyIHBlcm1pc3Npb25zJyxcbiAgICAgIHZhbHVlOiAncmVndWxhcicsXG4gICAgfSxcbiAgICB7XG4gICAgICBsYWJlbDpcbiAgICAgICAgY3VycmVudE1vZGUgPT09ICdkaXNhYmxlZCdcbiAgICAgICAgICA/IGBObyBTYW5kYm94ICR7Y3VycmVudEluZGljYXRvcn1gXG4gICAgICAgICAgOiAnTm8gU2FuZGJveCcsXG4gICAgICB2YWx1ZTogJ2Rpc2FibGVkJyxcbiAgICB9LFxuICBdXG5cbiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlU2VsZWN0KHZhbHVlOiBzdHJpbmcpIHtcbiAgICBjb25zdCBtb2RlID0gdmFsdWUgYXMgU2FuZGJveE1vZGVcblxuICAgIHN3aXRjaCAobW9kZSkge1xuICAgICAgY2FzZSAnYXV0by1hbGxvdyc6XG4gICAgICAgIGF3YWl0IFNhbmRib3hNYW5hZ2VyLnNldFNhbmRib3hTZXR0aW5ncyh7XG4gICAgICAgICAgZW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgICBhdXRvQWxsb3dCYXNoSWZTYW5kYm94ZWQ6IHRydWUsXG4gICAgICAgIH0pXG4gICAgICAgIG9uQ29tcGxldGUoJ+KckyBTYW5kYm94IGVuYWJsZWQgd2l0aCBhdXRvLWFsbG93IGZvciBiYXNoIGNvbW1hbmRzJylcbiAgICAgICAgYnJlYWtcbiAgICAgIGNhc2UgJ3JlZ3VsYXInOlxuICAgICAgICBhd2FpdCBTYW5kYm94TWFuYWdlci5zZXRTYW5kYm94U2V0dGluZ3Moe1xuICAgICAgICAgIGVuYWJsZWQ6IHRydWUsXG4gICAgICAgICAgYXV0b0FsbG93QmFzaElmU2FuZGJveGVkOiBmYWxzZSxcbiAgICAgICAgfSlcbiAgICAgICAgb25Db21wbGV0ZSgn4pyTIFNhbmRib3ggZW5hYmxlZCB3aXRoIHJlZ3VsYXIgYmFzaCBwZXJtaXNzaW9ucycpXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdkaXNhYmxlZCc6XG4gICAgICAgIGF3YWl0IFNhbmRib3hNYW5hZ2VyLnNldFNhbmRib3hTZXR0aW5ncyh7XG4gICAgICAgICAgZW5hYmxlZDogZmFsc2UsXG4gICAgICAgICAgYXV0b0FsbG93QmFzaElmU2FuZGJveGVkOiBmYWxzZSxcbiAgICAgICAgfSlcbiAgICAgICAgb25Db21wbGV0ZSgn4peLIFNhbmRib3ggZGlzYWJsZWQnKVxuICAgICAgICBicmVha1xuICAgIH1cbiAgfVxuXG4gIHVzZUtleWJpbmRpbmdzKFxuICAgIHtcbiAgICAgICdjb25maXJtOm5vJzogKCkgPT4gb25Db21wbGV0ZSh1bmRlZmluZWQsIHsgZGlzcGxheTogJ3NraXAnIH0pLFxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnU2V0dGluZ3MnIH0sXG4gIClcblxuICBjb25zdCBtb2RlVGFiID0gKFxuICAgIDxUYWIga2V5PVwibW9kZVwiIHRpdGxlPVwiTW9kZVwiPlxuICAgICAgPFNhbmRib3hNb2RlVGFiXG4gICAgICAgIHNob3dTb2NrZXRXYXJuaW5nPXtzaG93U29ja2V0V2FybmluZ31cbiAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgb25TZWxlY3Q9e2hhbmRsZVNlbGVjdH1cbiAgICAgICAgb25Db21wbGV0ZT17b25Db21wbGV0ZX1cbiAgICAgIC8+XG4gICAgPC9UYWI+XG4gIClcblxuICBjb25zdCBvdmVycmlkZXNUYWIgPSAoXG4gICAgPFRhYiBrZXk9XCJvdmVycmlkZXNcIiB0aXRsZT1cIk92ZXJyaWRlc1wiPlxuICAgICAgPFNhbmRib3hPdmVycmlkZXNUYWIgb25Db21wbGV0ZT17b25Db21wbGV0ZX0gLz5cbiAgICA8L1RhYj5cbiAgKVxuXG4gIGNvbnN0IGNvbmZpZ1RhYiA9IChcbiAgICA8VGFiIGtleT1cImNvbmZpZ1wiIHRpdGxlPVwiQ29uZmlnXCI+XG4gICAgICA8U2FuZGJveENvbmZpZ1RhYiAvPlxuICAgIDwvVGFiPlxuICApXG5cbiAgY29uc3QgaGFzRXJyb3JzID0gZGVwQ2hlY2suZXJyb3JzLmxlbmd0aCA+IDBcblxuICAvLyBJZiByZXF1aXJlZCBkZXBzIG1pc3NpbmcsIG9ubHkgc2hvdyBEZXBlbmRlbmNpZXMgdGFiXG4gIC8vIElmIG9ubHkgb3B0aW9uYWwgZGVwcyBtaXNzaW5nLCBzaG93IGFsbCB0YWJzXG4gIGNvbnN0IHRhYnMgPSBoYXNFcnJvcnNcbiAgICA/IFtcbiAgICAgICAgPFRhYiBrZXk9XCJkZXBlbmRlbmNpZXNcIiB0aXRsZT1cIkRlcGVuZGVuY2llc1wiPlxuICAgICAgICAgIDxTYW5kYm94RGVwZW5kZW5jaWVzVGFiIGRlcENoZWNrPXtkZXBDaGVja30gLz5cbiAgICAgICAgPC9UYWI+LFxuICAgICAgXVxuICAgIDogW1xuICAgICAgICBtb2RlVGFiLFxuICAgICAgICAuLi4oaGFzV2FybmluZ3NcbiAgICAgICAgICA/IFtcbiAgICAgICAgICAgICAgPFRhYiBrZXk9XCJkZXBlbmRlbmNpZXNcIiB0aXRsZT1cIkRlcGVuZGVuY2llc1wiPlxuICAgICAgICAgICAgICAgIDxTYW5kYm94RGVwZW5kZW5jaWVzVGFiIGRlcENoZWNrPXtkZXBDaGVja30gLz5cbiAgICAgICAgICAgICAgPC9UYWI+LFxuICAgICAgICAgICAgXVxuICAgICAgICAgIDogW10pLFxuICAgICAgICBvdmVycmlkZXNUYWIsXG4gICAgICAgIGNvbmZpZ1RhYixcbiAgICAgIF1cblxuICByZXR1cm4gKFxuICAgIDxQYW5lIGNvbG9yPVwicGVybWlzc2lvblwiPlxuICAgICAgPFRhYnMgdGl0bGU9XCJTYW5kYm94OlwiIGNvbG9yPVwicGVybWlzc2lvblwiIGRlZmF1bHRUYWI9XCJNb2RlXCI+XG4gICAgICAgIHt0YWJzfVxuICAgICAgPC9UYWJzPlxuICAgIDwvUGFuZT5cbiAgKVxufVxuXG5mdW5jdGlvbiBTYW5kYm94TW9kZVRhYih7XG4gIHNob3dTb2NrZXRXYXJuaW5nLFxuICBvcHRpb25zLFxuICBvblNlbGVjdCxcbiAgb25Db21wbGV0ZSxcbn06IHtcbiAgc2hvd1NvY2tldFdhcm5pbmc6IGJvb2xlYW5cbiAgb3B0aW9uczogQXJyYXk8eyBsYWJlbDogc3RyaW5nOyB2YWx1ZTogc3RyaW5nIH0+XG4gIG9uU2VsZWN0OiAodmFsdWU6IHN0cmluZykgPT4gdm9pZFxuICBvbkNvbXBsZXRlOiBQcm9wc1snb25Db21wbGV0ZSddXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBoZWFkZXJGb2N1c2VkLCBmb2N1c0hlYWRlciB9ID0gdXNlVGFiSGVhZGVyRm9jdXMoKVxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIHBhZGRpbmdZPXsxfT5cbiAgICAgIHtzaG93U29ja2V0V2FybmluZyAmJiAoXG4gICAgICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj5cbiAgICAgICAgICAgIENhbm5vdCBibG9jayB1bml4IGRvbWFpbiBzb2NrZXRzIChzZWUgRGVwZW5kZW5jaWVzIHRhYilcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICAgIDxCb3ggbWFyZ2luQm90dG9tPXsxfT5cbiAgICAgICAgPFRleHQgYm9sZD5Db25maWd1cmUgTW9kZTo8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICAgIDxTZWxlY3RcbiAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgb25DaGFuZ2U9e29uU2VsZWN0fVxuICAgICAgICBvbkNhbmNlbD17KCkgPT4gb25Db21wbGV0ZSh1bmRlZmluZWQsIHsgZGlzcGxheTogJ3NraXAnIH0pfVxuICAgICAgICBvblVwRnJvbUZpcnN0SXRlbT17Zm9jdXNIZWFkZXJ9XG4gICAgICAgIGlzRGlzYWJsZWQ9e2hlYWRlckZvY3VzZWR9XG4gICAgICAvPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfSBnYXA9ezF9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICA8VGV4dCBib2xkIGRpbUNvbG9yPlxuICAgICAgICAgICAgQXV0by1hbGxvdyBtb2RlOlxuICAgICAgICAgIDwvVGV4dD57JyAnfVxuICAgICAgICAgIENvbW1hbmRzIHdpbGwgdHJ5IHRvIHJ1biBpbiB0aGUgc2FuZGJveCBhdXRvbWF0aWNhbGx5LCBhbmQgYXR0ZW1wdHMgdG9cbiAgICAgICAgICBydW4gb3V0c2lkZSBvZiB0aGUgc2FuZGJveCBmYWxsYmFjayB0byByZWd1bGFyIHBlcm1pc3Npb25zLiBFeHBsaWNpdFxuICAgICAgICAgIGFzay9kZW55IHJ1bGVzIGFyZSBhbHdheXMgcmVzcGVjdGVkLlxuICAgICAgICA8L1RleHQ+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgIExlYXJuIG1vcmU6eycgJ31cbiAgICAgICAgICA8TGluayB1cmw9XCJodHRwczovL2NvZGUuY2xhdWRlLmNvbS9kb2NzL2VuL3NhbmRib3hpbmdcIj5cbiAgICAgICAgICAgIGNvZGUuY2xhdWRlLmNvbS9kb2NzL2VuL3NhbmRib3hpbmdcbiAgICAgICAgICA8L0xpbms+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixTQUFTQyxHQUFHLEVBQUVDLEtBQUssRUFBRUMsSUFBSSxFQUFFQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxjQUFjO0FBQy9ELFNBQVNDLGNBQWMsUUFBUSxvQ0FBb0M7QUFDbkUsY0FBY0Msb0JBQW9CLFFBQVEsd0JBQXdCO0FBQ2xFLGNBQWNDLHNCQUFzQixRQUFRLHdDQUF3QztBQUNwRixTQUFTQyxjQUFjLFFBQVEsd0NBQXdDO0FBQ3ZFLFNBQVNDLHNCQUFzQixRQUFRLGtDQUFrQztBQUN6RSxTQUFTQyxNQUFNLFFBQVEsMkJBQTJCO0FBQ2xELFNBQVNDLElBQUksUUFBUSwwQkFBMEI7QUFDL0MsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLGlCQUFpQixRQUFRLDBCQUEwQjtBQUN2RSxTQUFTQyxnQkFBZ0IsUUFBUSx1QkFBdUI7QUFDeEQsU0FBU0Msc0JBQXNCLFFBQVEsNkJBQTZCO0FBQ3BFLFNBQVNDLG1CQUFtQixRQUFRLDBCQUEwQjtBQUU5RCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsVUFBVSxFQUFFLENBQ1ZDLE1BQWUsQ0FBUixFQUFFLE1BQU0sRUFDZkMsT0FBNEMsQ0FBcEMsRUFBRTtJQUFFQyxPQUFPLENBQUMsRUFBRWhCLG9C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 \ No newline at end of file diff --git a/components/shell/ExpandShellOutputContext.tsx b/components/shell/ExpandShellOutputContext.tsx new file mode 100644 index 0000000..2452208 --- /dev/null +++ b/components/shell/ExpandShellOutputContext.tsx @@ -0,0 +1,36 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useContext } from 'react'; + +/** + * Context to indicate that shell output should be shown in full (not truncated). + * Used to auto-expand the most recent user `!` command output. + * + * This follows the same pattern as MessageResponseContext and SubAgentContext - + * a boolean context that child components can check to modify their behavior. + */ +const ExpandShellOutputContext = React.createContext(false); +export function ExpandShellOutputProvider(t0) { + const $ = _c(2); + const { + children + } = t0; + let t1; + if ($[0] !== children) { + t1 = {children}; + $[0] = children; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; +} + +/** + * Returns true if this component is rendered inside an ExpandShellOutputProvider, + * indicating the shell output should be shown in full rather than truncated. + */ +export function useExpandShellOutput() { + return useContext(ExpandShellOutputContext); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNvbnRleHQiLCJFeHBhbmRTaGVsbE91dHB1dENvbnRleHQiLCJjcmVhdGVDb250ZXh0IiwiRXhwYW5kU2hlbGxPdXRwdXRQcm92aWRlciIsInQwIiwiJCIsIl9jIiwiY2hpbGRyZW4iLCJ0MSIsInVzZUV4cGFuZFNoZWxsT3V0cHV0Il0sInNvdXJjZXMiOlsiRXhwYW5kU2hlbGxPdXRwdXRDb250ZXh0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUNvbnRleHQgfSBmcm9tICdyZWFjdCdcblxuLyoqXG4gKiBDb250ZXh0IHRvIGluZGljYXRlIHRoYXQgc2hlbGwgb3V0cHV0IHNob3VsZCBiZSBzaG93biBpbiBmdWxsIChub3QgdHJ1bmNhdGVkKS5cbiAqIFVzZWQgdG8gYXV0by1leHBhbmQgdGhlIG1vc3QgcmVjZW50IHVzZXIgYCFgIGNvbW1hbmQgb3V0cHV0LlxuICpcbiAqIFRoaXMgZm9sbG93cyB0aGUgc2FtZSBwYXR0ZXJuIGFzIE1lc3NhZ2VSZXNwb25zZUNvbnRleHQgYW5kIFN1YkFnZW50Q29udGV4dCAtXG4gKiBhIGJvb2xlYW4gY29udGV4dCB0aGF0IGNoaWxkIGNvbXBvbmVudHMgY2FuIGNoZWNrIHRvIG1vZGlmeSB0aGVpciBiZWhhdmlvci5cbiAqL1xuY29uc3QgRXhwYW5kU2hlbGxPdXRwdXRDb250ZXh0ID0gUmVhY3QuY3JlYXRlQ29udGV4dChmYWxzZSlcblxuZXhwb3J0IGZ1bmN0aW9uIEV4cGFuZFNoZWxsT3V0cHV0UHJvdmlkZXIoe1xuICBjaGlsZHJlbixcbn06IHtcbiAgY2hpbGRyZW46IFJlYWN0LlJlYWN0Tm9kZVxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPEV4cGFuZFNoZWxsT3V0cHV0Q29udGV4dC5Qcm92aWRlciB2YWx1ZT17dHJ1ZX0+XG4gICAgICB7Y2hpbGRyZW59XG4gICAgPC9FeHBhbmRTaGVsbE91dHB1dENvbnRleHQuUHJvdmlkZXI+XG4gIClcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgdGhpcyBjb21wb25lbnQgaXMgcmVuZGVyZWQgaW5zaWRlIGFuIEV4cGFuZFNoZWxsT3V0cHV0UHJvdmlkZXIsXG4gKiBpbmRpY2F0aW5nIHRoZSBzaGVsbCBvdXRwdXQgc2hvdWxkIGJlIHNob3duIGluIGZ1bGwgcmF0aGVyIHRoYW4gdHJ1bmNhdGVkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlRXhwYW5kU2hlbGxPdXRwdXQoKTogYm9vbGVhbiB7XG4gIHJldHVybiB1c2VDb250ZXh0KEV4cGFuZFNoZWxsT3V0cHV0Q29udGV4dClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsVUFBVSxRQUFRLE9BQU87O0FBRWxDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsTUFBTUMsd0JBQXdCLEdBQUdGLEtBQUssQ0FBQ0csYUFBYSxDQUFDLEtBQUssQ0FBQztBQUUzRCxPQUFPLFNBQUFDLDBCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQW1DO0lBQUFDO0VBQUEsSUFBQUgsRUFJekM7RUFBQSxJQUFBSSxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBRSxRQUFBO0lBRUdDLEVBQUEsc0NBQTBDLEtBQUksQ0FBSixLQUFHLENBQUMsQ0FDM0NELFNBQU8sQ0FDVixvQ0FBb0M7SUFBQUYsQ0FBQSxNQUFBRSxRQUFBO0lBQUFGLENBQUEsTUFBQUcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBQUEsT0FGcENHLEVBRW9DO0FBQUE7O0FBSXhDO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTyxTQUFBQyxxQkFBQTtFQUFBLE9BQ0VULFVBQVUsQ0FBQ0Msd0JBQXdCLENBQUM7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/components/shell/OutputLine.tsx b/components/shell/OutputLine.tsx new file mode 100644 index 0000000..a6c5597 --- /dev/null +++ b/components/shell/OutputLine.tsx @@ -0,0 +1,118 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useMemo } from 'react'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { Ansi, Text } from '../../ink.js'; +import { createHyperlink } from '../../utils/hyperlink.js'; +import { jsonParse, jsonStringify } from '../../utils/slowOperations.js'; +import { renderTruncatedContent } from '../../utils/terminal.js'; +import { MessageResponse } from '../MessageResponse.js'; +import { InVirtualListContext } from '../messageActions.js'; +import { useExpandShellOutput } from './ExpandShellOutputContext.js'; +export function tryFormatJson(line: string): string { + try { + const parsed = jsonParse(line); + const stringified = jsonStringify(parsed); + + // Check if precision was lost during JSON round-trip + // This happens when large integers exceed Number.MAX_SAFE_INTEGER + // We normalize both strings by removing whitespace and unnecessary + // escapes (\/ is valid but optional in JSON) for comparison + const normalizedOriginal = line.replace(/\\\//g, '/').replace(/\s+/g, ''); + const normalizedStringified = stringified.replace(/\s+/g, ''); + if (normalizedOriginal !== normalizedStringified) { + // Precision loss detected - return original line unformatted + return line; + } + return jsonStringify(parsed, null, 2); + } catch { + return line; + } +} +const MAX_JSON_FORMAT_LENGTH = 10_000; +export function tryJsonFormatContent(content: string): string { + if (content.length > MAX_JSON_FORMAT_LENGTH) { + return content; + } + const allLines = content.split('\n'); + return allLines.map(tryFormatJson).join('\n'); +} + +// Match http(s) URLs inside JSON string values. Conservative: no quotes, +// no whitespace, no trailing comma/brace that'd be JSON structure. +const URL_IN_JSON = /https?:\/\/[^\s"'<>\\]+/g; +export function linkifyUrlsInText(content: string): string { + return content.replace(URL_IN_JSON, url => createHyperlink(url)); +} +export function OutputLine(t0) { + const $ = _c(11); + const { + content, + verbose, + isError, + isWarning, + linkifyUrls + } = t0; + const { + columns + } = useTerminalSize(); + const expandShellOutput = useExpandShellOutput(); + const inVirtualList = React.useContext(InVirtualListContext); + const shouldShowFull = verbose || expandShellOutput; + let t1; + if ($[0] !== columns || $[1] !== content || $[2] !== inVirtualList || $[3] !== linkifyUrls || $[4] !== shouldShowFull) { + bb0: { + let formatted = tryJsonFormatContent(content); + if (linkifyUrls) { + formatted = linkifyUrlsInText(formatted); + } + if (shouldShowFull) { + t1 = stripUnderlineAnsi(formatted); + break bb0; + } + t1 = stripUnderlineAnsi(renderTruncatedContent(formatted, columns, inVirtualList)); + } + $[0] = columns; + $[1] = content; + $[2] = inVirtualList; + $[3] = linkifyUrls; + $[4] = shouldShowFull; + $[5] = t1; + } else { + t1 = $[5]; + } + const formattedContent = t1; + const color = isError ? "error" : isWarning ? "warning" : undefined; + let t2; + if ($[6] !== formattedContent) { + t2 = {formattedContent}; + $[6] = formattedContent; + $[7] = t2; + } else { + t2 = $[7]; + } + let t3; + if ($[8] !== color || $[9] !== t2) { + t3 = {t2}; + $[8] = color; + $[9] = t2; + $[10] = t3; + } else { + t3 = $[10]; + } + return t3; +} + +/** + * Underline ANSI codes in particular tend to leak out for some reason. I wasn't + * able to figure out why, or why emitting a reset ANSI code wasn't enough to + * prevent them from leaking. I also didn't want to strip all ANSI codes with + * stripAnsi(), because we used to do that and people complained about losing + * all formatting. So we just strip the underline ANSI codes specifically. + */ +export function stripUnderlineAnsi(content: string): string { + return content.replace( + // eslint-disable-next-line no-control-regex + /\u001b\[([0-9]+;)*4(;[0-9]+)*m|\u001b\[4(;[0-9]+)*m|\u001b\[([0-9]+;)*4m/g, ''); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZU1lbW8iLCJ1c2VUZXJtaW5hbFNpemUiLCJBbnNpIiwiVGV4dCIsImNyZWF0ZUh5cGVybGluayIsImpzb25QYXJzZSIsImpzb25TdHJpbmdpZnkiLCJyZW5kZXJUcnVuY2F0ZWRDb250ZW50IiwiTWVzc2FnZVJlc3BvbnNlIiwiSW5WaXJ0dWFsTGlzdENvbnRleHQiLCJ1c2VFeHBhbmRTaGVsbE91dHB1dCIsInRyeUZvcm1hdEpzb24iLCJsaW5lIiwicGFyc2VkIiwic3RyaW5naWZpZWQiLCJub3JtYWxpemVkT3JpZ2luYWwiLCJyZXBsYWNlIiwibm9ybWFsaXplZFN0cmluZ2lmaWVkIiwiTUFYX0pTT05fRk9STUFUX0xFTkdUSCIsInRyeUpzb25Gb3JtYXRDb250ZW50IiwiY29udGVudCIsImxlbmd0aCIsImFsbExpbmVzIiwic3BsaXQiLCJtYXAiLCJqb2luIiwiVVJMX0lOX0pTT04iLCJsaW5raWZ5VXJsc0luVGV4dCIsInVybCIsIk91dHB1dExpbmUiLCJ0MCIsIiQiLCJfYyIsInZlcmJvc2UiLCJpc0Vycm9yIiwiaXNXYXJuaW5nIiwibGlua2lmeVVybHMiLCJjb2x1bW5zIiwiZXhwYW5kU2hlbGxPdXRwdXQiLCJpblZpcnR1YWxMaXN0IiwidXNlQ29udGV4dCIsInNob3VsZFNob3dGdWxsIiwidDEiLCJiYjAiLCJmb3JtYXR0ZWQiLCJzdHJpcFVuZGVybGluZUFuc2kiLCJmb3JtYXR0ZWRDb250ZW50IiwiY29sb3IiLCJ1bmRlZmluZWQiLCJ0MiIsInQzIl0sInNvdXJjZXMiOlsiT3V0cHV0TGluZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VNZW1vIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyBBbnNpLCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgY3JlYXRlSHlwZXJsaW5rIH0gZnJvbSAnLi4vLi4vdXRpbHMvaHlwZXJsaW5rLmpzJ1xuaW1wb3J0IHsganNvblBhcnNlLCBqc29uU3RyaW5naWZ5IH0gZnJvbSAnLi4vLi4vdXRpbHMvc2xvd09wZXJhdGlvbnMuanMnXG5pbXBvcnQgeyByZW5kZXJUcnVuY2F0ZWRDb250ZW50IH0gZnJvbSAnLi4vLi4vdXRpbHMvdGVybWluYWwuanMnXG5pbXBvcnQgeyBNZXNzYWdlUmVzcG9uc2UgfSBmcm9tICcuLi9NZXNzYWdlUmVzcG9uc2UuanMnXG5pbXBvcnQgeyBJblZpcnR1YWxMaXN0Q29udGV4dCB9IGZyb20gJy4uL21lc3NhZ2VBY3Rpb25zLmpzJ1xuaW1wb3J0IHsgdXNlRXhwYW5kU2hlbGxPdXRwdXQgfSBmcm9tICcuL0V4cGFuZFNoZWxsT3V0cHV0Q29udGV4dC5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIHRyeUZvcm1hdEpzb24obGluZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgdHJ5IHtcbiAgICBjb25zdCBwYXJzZWQgPSBqc29uUGFyc2UobGluZSlcbiAgICBjb25zdCBzdHJpbmdpZmllZCA9IGpzb25TdHJpbmdpZnkocGFyc2VkKVxuXG4gICAgLy8gQ2hlY2sgaWYgcHJlY2lzaW9uIHdhcyBsb3N0IGR1cmluZyBKU09OIHJvdW5kLXRyaXBcbiAgICAvLyBUaGlzIGhhcHBlbnMgd2hlbiBsYXJnZSBpbnRlZ2VycyBleGNlZWQgTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVJcbiAgICAvLyBXZSBub3JtYWxpemUgYm90aCBzdHJpbmdzIGJ5IHJlbW92aW5nIHdoaXRlc3BhY2UgYW5kIHVubmVjZXNzYXJ5XG4gICAgLy8gZXNjYXBlcyAoXFwvIGlzIHZhbGlkIGJ1dCBvcHRpb25hbCBpbiBKU09OKSBmb3IgY29tcGFyaXNvblxuICAgIGNvbnN0IG5vcm1hbGl6ZWRPcmlnaW5hbCA9IGxpbmUucmVwbGFjZSgvXFxcXFxcLy9nLCAnLycpLnJlcGxhY2UoL1xccysvZywgJycpXG4gICAgY29uc3Qgbm9ybWFsaXplZFN0cmluZ2lmaWVkID0gc3RyaW5naWZpZWQucmVwbGFjZSgvXFxzKy9nLCAnJylcblxuICAgIGlmIChub3JtYWxpemVkT3JpZ2luYWwgIT09IG5vcm1hbGl6ZWRTdHJpbmdpZmllZCkge1xuICAgICAgLy8gUHJlY2lzaW9uIGxvc3MgZGV0ZWN0ZWQgLSByZXR1cm4gb3JpZ2luYWwgbGluZSB1bmZvcm1hdHRlZFxuICAgICAgcmV0dXJuIGxpbmVcbiAgICB9XG5cbiAgICByZXR1cm4ganNvblN0cmluZ2lmeShwYXJzZWQsIG51bGwsIDIpXG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBsaW5lXG4gIH1cbn1cblxuY29uc3QgTUFYX0pTT05fRk9STUFUX0xFTkdUSCA9IDEwXzAwMFxuXG5leHBvcnQgZnVuY3Rpb24gdHJ5SnNvbkZvcm1hdENvbnRlbnQoY29udGVudDogc3RyaW5nKTogc3RyaW5nIHtcbiAgaWYgKGNvbnRlbnQubGVuZ3RoID4gTUFYX0pTT05fRk9STUFUX0xFTkdUSCkge1xuICAgIHJldHVybiBjb250ZW50XG4gIH1cbiAgY29uc3QgYWxsTGluZXMgPSBjb250ZW50LnNwbGl0KCdcXG4nKVxuICByZXR1cm4gYWxsTGluZXMubWFwKHRyeUZvcm1hdEpzb24pLmpvaW4oJ1xcbicpXG59XG5cbi8vIE1hdGNoIGh0dHAocykgVVJMcyBpbnNpZGUgSlNPTiBzdHJpbmcgdmFsdWVzLiBDb25zZXJ2YXRpdmU6IG5vIHF1b3Rlcyxcbi8vIG5vIHdoaXRlc3BhY2UsIG5vIHRyYWlsaW5nIGNvbW1hL2JyYWNlIHRoYXQnZCBiZSBKU09OIHN0cnVjdHVyZS5cbmNvbnN0IFVSTF9JTl9KU09OID0gL2h0dHBzPzpcXC9cXC9bXlxcc1wiJzw+XFxcXF0rL2dcblxuZXhwb3J0IGZ1bmN0aW9uIGxpbmtpZnlVcmxzSW5UZXh0KGNvbnRlbnQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIHJldHVybiBjb250ZW50LnJlcGxhY2UoVVJMX0lOX0pTT04sIHVybCA9PiBjcmVhdGVIeXBlcmxpbmsodXJsKSlcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIE91dHB1dExpbmUoe1xuICBjb250ZW50LFxuICB2ZXJib3NlLFxuICBpc0Vycm9yLFxuICBpc1dhcm5pbmcsXG4gIGxpbmtpZnlVcmxzLFxufToge1xuICBjb250ZW50OiBzdHJpbmdcbiAgdmVyYm9zZTogYm9vbGVhblxuICBpc0Vycm9yPzogYm9vbGVhblxuICBpc1dhcm5pbmc/OiBib29sZWFuXG4gIGxpbmtpZnlVcmxzPzogYm9vbGVhblxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcbiAgLy8gQ29udGV4dC1iYXNlZCBleHBhbnNpb24gZm9yIGxhdGVzdCB1c2VyIHNoZWxsIG91dHB1dCAoZnJvbSAhIGNvbW1hbmRzKVxuICBjb25zdCBleHBhbmRTaGVsbE91dHB1dCA9IHVzZUV4cGFuZFNoZWxsT3V0cHV0KClcbiAgY29uc3QgaW5WaXJ0dWFsTGlzdCA9IFJlYWN0LnVzZUNvbnRleHQoSW5WaXJ0dWFsTGlzdENvbnRleHQpXG5cbiAgLy8gU2hvdyBmdWxsIG91dHB1dCBpZiB2ZXJib3NlIG1vZGUgT1IgaWYgdGhpcyBpcyB0aGUgbGF0ZXN0IHVzZXIgc2hlbGwgb3V0cHV0XG4gIGNvbnN0IHNob3VsZFNob3dGdWxsID0gdmVyYm9zZSB8fCBleHBhbmRTaGVsbE91dHB1dFxuXG4gIGNvbnN0IGZvcm1hdHRlZENvbnRlbnQgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBsZXQgZm9ybWF0dGVkID0gdHJ5SnNvbkZvcm1hdENvbnRlbnQoY29udGVudClcbiAgICBpZiAobGlua2lmeVVybHMpIHtcbiAgICAgIGZvcm1hdHRlZCA9IGxpbmtpZnlVcmxzSW5UZXh0KGZvcm1hdHRlZClcbiAgICB9XG4gICAgaWYgKHNob3VsZFNob3dGdWxsKSB7XG4gICAgICByZXR1cm4gc3RyaXBVbmRlcmxpbmVBbnNpKGZvcm1hdHRlZClcbiAgICB9XG4gICAgcmV0dXJuIHN0cmlwVW5kZXJsaW5lQW5zaShcbiAgICAgIHJlbmRlclRydW5jYXRlZENvbnRlbnQoZm9ybWF0dGVkLCBjb2x1bW5zLCBpblZpcnR1YWxMaXN0KSxcbiAgICApXG4gIH0sIFtjb250ZW50LCBzaG91bGRTaG93RnVsbCwgY29sdW1ucywgbGlua2lmeVVybHMsIGluVmlydHVhbExpc3RdKVxuXG4gIGNvbnN0IGNvbG9yID0gaXNFcnJvciA/ICdlcnJvcicgOiBpc1dhcm5pbmcgPyAnd2FybmluZycgOiB1bmRlZmluZWRcblxuICByZXR1cm4gKFxuICAgIDxNZXNzYWdlUmVzcG9uc2U+XG4gICAgICA8VGV4dCBjb2xvcj17Y29sb3J9PlxuICAgICAgICA8QW5zaT57Zm9ybWF0dGVkQ29udGVudH08L0Fuc2k+XG4gICAgICA8L1RleHQ+XG4gICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gIClcbn1cblxuLyoqXG4gKiBVbmRlcmxpbmUgQU5TSSBjb2RlcyBpbiBwYXJ0aWN1bGFyIHRlbmQgdG8gbGVhayBvdXQgZm9yIHNvbWUgcmVhc29uLiBJIHdhc24ndFxuICogYWJsZSB0byBmaWd1cmUgb3V0IHdoeSwgb3Igd2h5IGVtaXR0aW5nIGEgcmVzZXQgQU5TSSBjb2RlIHdhc24ndCBlbm91Z2ggdG9cbiAqIHByZXZlbnQgdGhlbSBmcm9tIGxlYWtpbmcuIEkgYWxzbyBkaWRuJ3Qgd2FudCB0byBzdHJpcCBhbGwgQU5TSSBjb2RlcyB3aXRoXG4gKiBzdHJpcEFuc2koKSwgYmVjYXVzZSB3ZSB1c2VkIHRvIGRvIHRoYXQgYW5kIHBlb3BsZSBjb21wbGFpbmVkIGFib3V0IGxvc2luZ1xuICogYWxsIGZvcm1hdHRpbmcuIFNvIHdlIGp1c3Qgc3RyaXAgdGhlIHVuZGVybGluZSBBTlNJIGNvZGVzIHNwZWNpZmljYWxseS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN0cmlwVW5kZXJsaW5lQW5zaShjb250ZW50OiBzdHJpbmcpOiBzdHJpbmcge1xuICByZXR1cm4gY29udGVudC5yZXBsYWNlKFxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb250cm9sLXJlZ2V4XG4gICAgL1xcdTAwMWJcXFsoWzAtOV0rOykqNCg7WzAtOV0rKSptfFxcdTAwMWJcXFs0KDtbMC05XSspKm18XFx1MDAxYlxcWyhbMC05XSs7KSo0bS9nLFxuICAgICcnLFxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLE9BQU8sUUFBUSxPQUFPO0FBQy9CLFNBQVNDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDaEUsU0FBU0MsSUFBSSxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN6QyxTQUFTQyxlQUFlLFFBQVEsMEJBQTBCO0FBQzFELFNBQVNDLFNBQVMsRUFBRUMsYUFBYSxRQUFRLCtCQUErQjtBQUN4RSxTQUFTQyxzQkFBc0IsUUFBUSx5QkFBeUI7QUFDaEUsU0FBU0MsZUFBZSxRQUFRLHVCQUF1QjtBQUN2RCxTQUFTQyxvQkFBb0IsUUFBUSxzQkFBc0I7QUFDM0QsU0FBU0Msb0JBQW9CLFFBQVEsK0JBQStCO0FBRXBFLE9BQU8sU0FBU0MsYUFBYUEsQ0FBQ0MsSUFBSSxFQUFFLE1BQU0sQ0FBQyxFQUFFLE1BQU0sQ0FBQztFQUNsRCxJQUFJO0lBQ0YsTUFBTUMsTUFBTSxHQUFHUixTQUFTLENBQUNPLElBQUksQ0FBQztJQUM5QixNQUFNRSxXQUFXLEdBQUdSLGFBQWEsQ0FBQ08sTUFBTSxDQUFDOztJQUV6QztJQUNBO0lBQ0E7SUFDQTtJQUNBLE1BQU1FLGtCQUFrQixHQUFHSCxJQUFJLENBQUNJLE9BQU8sQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDLENBQUNBLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDO0lBQ3pFLE1BQU1DLHFCQUFxQixHQUFHSCxXQUFXLENBQUNFLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDO0lBRTdELElBQUlELGtCQUFrQixLQUFLRSxxQkFBcUIsRUFBRTtNQUNoRDtNQUNBLE9BQU9MLElBQUk7SUFDYjtJQUVBLE9BQU9OLGFBQWEs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 \ No newline at end of file diff --git a/components/shell/ShellProgressMessage.tsx b/components/shell/ShellProgressMessage.tsx new file mode 100644 index 0000000..962cce1 --- /dev/null +++ b/components/shell/ShellProgressMessage.tsx @@ -0,0 +1,150 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import stripAnsi from 'strip-ansi'; +import { Box, Text } from '../../ink.js'; +import { formatFileSize } from '../../utils/format.js'; +import { MessageResponse } from '../MessageResponse.js'; +import { OffscreenFreeze } from '../OffscreenFreeze.js'; +import { ShellTimeDisplay } from './ShellTimeDisplay.js'; +type Props = { + output: string; + fullOutput: string; + elapsedTimeSeconds?: number; + totalLines?: number; + totalBytes?: number; + timeoutMs?: number; + taskId?: string; + verbose: boolean; +}; +export function ShellProgressMessage(t0) { + const $ = _c(30); + const { + output, + fullOutput, + elapsedTimeSeconds, + totalLines, + totalBytes, + timeoutMs, + verbose + } = t0; + let t1; + if ($[0] !== fullOutput) { + t1 = stripAnsi(fullOutput.trim()); + $[0] = fullOutput; + $[1] = t1; + } else { + t1 = $[1]; + } + const strippedFullOutput = t1; + let lines; + let t2; + if ($[2] !== output || $[3] !== strippedFullOutput || $[4] !== verbose) { + const strippedOutput = stripAnsi(output.trim()); + lines = strippedOutput.split("\n").filter(_temp); + t2 = verbose ? strippedFullOutput : lines.slice(-5).join("\n"); + $[2] = output; + $[3] = strippedFullOutput; + $[4] = verbose; + $[5] = lines; + $[6] = t2; + } else { + lines = $[5]; + t2 = $[6]; + } + const displayLines = t2; + if (!lines.length) { + let t3; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t3 = Running… ; + $[7] = t3; + } else { + t3 = $[7]; + } + let t4; + if ($[8] !== elapsedTimeSeconds || $[9] !== timeoutMs) { + t4 = {t3}; + $[8] = elapsedTimeSeconds; + $[9] = timeoutMs; + $[10] = t4; + } else { + t4 = $[10]; + } + return t4; + } + const extraLines = totalLines ? Math.max(0, totalLines - 5) : 0; + let lineStatus = ""; + if (!verbose && totalBytes && totalLines) { + lineStatus = `~${totalLines} lines`; + } else { + if (!verbose && extraLines > 0) { + lineStatus = `+${extraLines} lines`; + } + } + const t3 = verbose ? undefined : Math.min(5, lines.length); + let t4; + if ($[11] !== displayLines) { + t4 = {displayLines}; + $[11] = displayLines; + $[12] = t4; + } else { + t4 = $[12]; + } + let t5; + if ($[13] !== t3 || $[14] !== t4) { + t5 = {t4}; + $[13] = t3; + $[14] = t4; + $[15] = t5; + } else { + t5 = $[15]; + } + let t6; + if ($[16] !== lineStatus) { + t6 = lineStatus ? {lineStatus} : null; + $[16] = lineStatus; + $[17] = t6; + } else { + t6 = $[17]; + } + let t7; + if ($[18] !== elapsedTimeSeconds || $[19] !== timeoutMs) { + t7 = ; + $[18] = elapsedTimeSeconds; + $[19] = timeoutMs; + $[20] = t7; + } else { + t7 = $[20]; + } + let t8; + if ($[21] !== totalBytes) { + t8 = totalBytes ? {formatFileSize(totalBytes)} : null; + $[21] = totalBytes; + $[22] = t8; + } else { + t8 = $[22]; + } + let t9; + if ($[23] !== t6 || $[24] !== t7 || $[25] !== t8) { + t9 = {t6}{t7}{t8}; + $[23] = t6; + $[24] = t7; + $[25] = t8; + $[26] = t9; + } else { + t9 = $[26]; + } + let t10; + if ($[27] !== t5 || $[28] !== t9) { + t10 = {t5}{t9}; + $[27] = t5; + $[28] = t9; + $[29] = t10; + } else { + t10 = $[29]; + } + return t10; +} +function _temp(line) { + return line; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInN0cmlwQW5zaSIsIkJveCIsIlRleHQiLCJmb3JtYXRGaWxlU2l6ZSIsIk1lc3NhZ2VSZXNwb25zZSIsIk9mZnNjcmVlbkZyZWV6ZSIsIlNoZWxsVGltZURpc3BsYXkiLCJQcm9wcyIsIm91dHB1dCIsImZ1bGxPdXRwdXQiLCJlbGFwc2VkVGltZVNlY29uZHMiLCJ0b3RhbExpbmVzIiwidG90YWxCeXRlcyIsInRpbWVvdXRNcyIsInRhc2tJZCIsInZlcmJvc2UiLCJTaGVsbFByb2dyZXNzTWVzc2FnZSIsInQwIiwiJCIsIl9jIiwidDEiLCJ0cmltIiwic3RyaXBwZWRGdWxsT3V0cHV0IiwibGluZXMiLCJ0MiIsInN0cmlwcGVkT3V0cHV0Iiwic3BsaXQiLCJmaWx0ZXIiLCJfdGVtcCIsInNsaWNlIiwiam9pbiIsImRpc3BsYXlMaW5lcyIsImxlbmd0aCIsInQzIiwiU3ltYm9sIiwiZm9yIiwidDQiLCJleHRyYUxpbmVzIiwiTWF0aCIsIm1heCIsImxpbmVTdGF0dXMiLCJ1bmRlZmluZWQiLCJtaW4iLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5IiwidDEwIiwibGluZSJdLCJzb3VyY2VzIjpbIlNoZWxsUHJvZ3Jlc3NNZXNzYWdlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgc3RyaXBBbnNpIGZyb20gJ3N0cmlwLWFuc2knXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBmb3JtYXRGaWxlU2l6ZSB9IGZyb20gJy4uLy4uL3V0aWxzL2Zvcm1hdC5qcydcbmltcG9ydCB7IE1lc3NhZ2VSZXNwb25zZSB9IGZyb20gJy4uL01lc3NhZ2VSZXNwb25zZS5qcydcbmltcG9ydCB7IE9mZnNjcmVlbkZyZWV6ZSB9IGZyb20gJy4uL09mZnNjcmVlbkZyZWV6ZS5qcydcbmltcG9ydCB7IFNoZWxsVGltZURpc3BsYXkgfSBmcm9tICcuL1NoZWxsVGltZURpc3BsYXkuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG91dHB1dDogc3RyaW5nXG4gIGZ1bGxPdXRwdXQ6IHN0cmluZ1xuICBlbGFwc2VkVGltZVNlY29uZHM/OiBudW1iZXJcbiAgdG90YWxMaW5lcz86IG51bWJlclxuICB0b3RhbEJ5dGVzPzogbnVtYmVyXG4gIHRpbWVvdXRNcz86IG51bWJlclxuICB0YXNrSWQ/OiBzdHJpbmdcbiAgdmVyYm9zZTogYm9vbGVhblxufVxuXG5leHBvcnQgZnVuY3Rpb24gU2hlbGxQcm9ncmVzc01lc3NhZ2Uoe1xuICBvdXRwdXQsXG4gIGZ1bGxPdXRwdXQsXG4gIGVsYXBzZWRUaW1lU2Vjb25kcyxcbiAgdG90YWxMaW5lcyxcbiAgdG90YWxCeXRlcyxcbiAgdGltZW91dE1zLFxuICB2ZXJib3NlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBzdHJpcHBlZEZ1bGxPdXRwdXQgPSBzdHJpcEFuc2koZnVsbE91dHB1dC50cmltKCkpXG4gIGNvbnN0IHN0cmlwcGVkT3V0cHV0ID0gc3RyaXBBbnNpKG91dHB1dC50cmltKCkpXG4gIGNvbnN0IGxpbmVzID0gc3RyaXBwZWRPdXRwdXQuc3BsaXQoJ1xcbicpLmZpbHRlcihsaW5lID0+IGxpbmUpXG4gIGNvbnN0IGRpc3BsYXlMaW5lcyA9IHZlcmJvc2UgPyBzdHJpcHBlZEZ1bGxPdXRwdXQgOiBsaW5lcy5zbGljZSgtNSkuam9pbignXFxuJylcblxuICAvLyBPZmZzY3JlZW5GcmVlemU6IEJhc2hUb29sIHlpZWxkcyBwcm9ncmVzcyAoZWxhcHNlZFRpbWVTZWNvbmRzKSBldmVyeSBzZWNvbmQuXG4gIC8vIElmIHRoaXMgbGluZSBzY3JvbGxzIGludG8gc2Nyb2xsYmFjaywgZWFjaCB0aWNrIGZvcmNlcyBhIGZ1bGwgdGVybWluYWwgcmVzZXQuXG4gIC8vIEEgZm9yZWdyb3VuZCBgc2xlZXAgNjAwYCBvbiBhIDI5LXJvdyB0ZXJtaW5hbCB3aXRoIDQwMDAgcm93cyBvZiBoaXN0b3J5XG4gIC8vIHByb2R1Y2VkIDUwNyByZXNldHMgb3ZlciAxMCBtaW51dGVzIChnby9jY3NoYXJlL21heGstMjAyNjAyMjYtMTkwMzQ4KS5cbiAgaWYgKCFsaW5lcy5sZW5ndGgpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPE1lc3NhZ2VSZXNwb25zZT5cbiAgICAgICAgPE9mZnNjcmVlbkZyZWV6ZT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5SdW5uaW5n4oCmIDwvVGV4dD5cbiAgICAgICAgICA8U2hlbGxUaW1lRGlzcGxheVxuICAgICAgICAgICAgZWxhcHNlZFRpbWVTZWNvbmRzPXtlbGFwc2VkVGltZVNlY29uZHN9XG4gICAgICAgICAgICB0aW1lb3V0TXM9e3RpbWVvdXRNc31cbiAgICAgICAgICAvPlxuICAgICAgICA8L09mZnNjcmVlbkZyZWV6ZT5cbiAgICAgIDwvTWVzc2FnZVJlc3BvbnNlPlxuICAgIClcbiAgfVxuXG4gIC8vIE5vdCB0cnVuY2F0ZWQ6IFwiKzIgbGluZXNcIiAodG90YWwgZXhjZWVkcyBkaXNwbGF5ZWQgNSlcbiAgLy8gVHJ1bmNhdGVkOiAgICAgXCJ+MjAwMCBsaW5lc1wiIChleHRyYXBvbGF0ZWQgZXN0aW1hdGUgZnJvbSB0YWlsIHNhbXBsZSlcbiAgY29uc3QgZXh0cmFMaW5lcyA9IHRvdGFsTGluZXMgPyBNYXRoLm1heCgwLCB0b3RhbExpbmVzIC0gNSkgOiAwXG4gIGxldCBsaW5lU3RhdHVzID0gJydcbiAgaWYgKCF2ZXJib3NlICYmIHRvdGFsQnl0ZXMgJiYgdG90YWxMaW5lcykge1xuICAgIGxpbmVTdGF0dXMgPSBgfiR7dG90YWxMaW5lc30gbGluZXNgXG4gIH0gZWxzZSBpZiAoIXZlcmJvc2UgJiYgZXh0cmFMaW5lcyA+IDApIHtcbiAgICBsaW5lU3RhdHVzID0gYCske2V4dHJhTGluZXN9IGxpbmVzYFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8TWVzc2FnZVJlc3BvbnNlPlxuICAgICAgPE9mZnNjcmVlbkZyZWV6ZT5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgPEJveFxuICAgICAgICAgICAgaGVpZ2h0PXt2ZXJib3NlID8gdW5kZWZpbmVkIDogTWF0aC5taW4oNSwgbGluZXMubGVuZ3RoKX1cbiAgICAgICAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgICAgICAgb3ZlcmZsb3c9XCJoaWRkZW5cIlxuICAgICAgICAgID5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntkaXNwbGF5TGluZXN9PC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiIGdhcD17MX0+XG4gICAgICAgICAgICB7bGluZVN0YXR1cyA/IDxUZXh0IGRpbUNvbG9yPntsaW5lU3RhdHVzfTwvVGV4dD4gOiBudWxsfVxuICAgICAgICAgICAgPFNoZWxsVGltZURpc3BsYXlcbiAgICAgICAgICAgICAgZWxhcHNlZFRpbWVTZWNvbmRzPXtlbGFwc2VkVGltZVNlY29uZHN9XG4gICAgICAgICAgICAgIHRpbWVvdXRNcz17dGltZW91dE1zfVxuICAgICAgICAgICAgLz5cbiAgICAgICAgICAgIHt0b3RhbEJ5dGVzID8gKFxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57Zm9ybWF0RmlsZVNpemUodG90YWxCeXRlcyl9PC9UZXh0PlxuICAgICAgICAgICAgKSA6IG51bGx9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9PZmZzY3JlZW5GcmVlemU+XG4gICAgPC9NZXNzYWdlUmVzcG9uc2U+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLE9BQU9DLFNBQVMsTUFBTSxZQUFZO0FBQ2xDLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsU0FBU0MsY0FBYyxRQUFRLHVCQUF1QjtBQUN0RCxTQUFTQyxlQUFlLFFBQVEsdUJBQXVCO0FBQ3ZELFNBQVNDLGVBQWUs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 \ No newline at end of file diff --git a/components/shell/ShellTimeDisplay.tsx b/components/shell/ShellTimeDisplay.tsx new file mode 100644 index 0000000..d541ede --- /dev/null +++ b/components/shell/ShellTimeDisplay.tsx @@ -0,0 +1,74 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import { Text } from '../../ink.js'; +import { formatDuration } from '../../utils/format.js'; +type Props = { + elapsedTimeSeconds?: number; + timeoutMs?: number; +}; +export function ShellTimeDisplay(t0) { + const $ = _c(10); + const { + elapsedTimeSeconds, + timeoutMs + } = t0; + if (elapsedTimeSeconds === undefined && !timeoutMs) { + return null; + } + let t1; + if ($[0] !== timeoutMs) { + t1 = timeoutMs ? formatDuration(timeoutMs, { + hideTrailingZeros: true + }) : undefined; + $[0] = timeoutMs; + $[1] = t1; + } else { + t1 = $[1]; + } + const timeout = t1; + if (elapsedTimeSeconds === undefined) { + const t2 = `(timeout ${timeout})`; + let t3; + if ($[2] !== t2) { + t3 = {t2}; + $[2] = t2; + $[3] = t3; + } else { + t3 = $[3]; + } + return t3; + } + const t2 = elapsedTimeSeconds * 1000; + let t3; + if ($[4] !== t2) { + t3 = formatDuration(t2); + $[4] = t2; + $[5] = t3; + } else { + t3 = $[5]; + } + const elapsed = t3; + if (timeout) { + const t4 = `(${elapsed} · timeout ${timeout})`; + let t5; + if ($[6] !== t4) { + t5 = {t4}; + $[6] = t4; + $[7] = t5; + } else { + t5 = $[7]; + } + return t5; + } + const t4 = `(${elapsed})`; + let t5; + if ($[8] !== t4) { + t5 = {t4}; + $[8] = t4; + $[9] = t5; + } else { + t5 = $[9]; + } + return t5; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJmb3JtYXREdXJhdGlvbiIsIlByb3BzIiwiZWxhcHNlZFRpbWVTZWNvbmRzIiwidGltZW91dE1zIiwiU2hlbGxUaW1lRGlzcGxheSIsInQwIiwiJCIsIl9jIiwidW5kZWZpbmVkIiwidDEiLCJoaWRlVHJhaWxpbmdaZXJvcyIsInRpbWVvdXQiLCJ0MiIsInQzIiwiZWxhcHNlZCIsInQ0IiwidDUiXSwic291cmNlcyI6WyJTaGVsbFRpbWVEaXNwbGF5LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgZm9ybWF0RHVyYXRpb24gfSBmcm9tICcuLi8uLi91dGlscy9mb3JtYXQuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGVsYXBzZWRUaW1lU2Vjb25kcz86IG51bWJlclxuICB0aW1lb3V0TXM/OiBudW1iZXJcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFNoZWxsVGltZURpc3BsYXkoe1xuICBlbGFwc2VkVGltZVNlY29uZHMsXG4gIHRpbWVvdXRNcyxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKGVsYXBzZWRUaW1lU2Vjb25kcyA9PT0gdW5kZWZpbmVkICYmICF0aW1lb3V0TXMpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG4gIGNvbnN0IHRpbWVvdXQgPSB0aW1lb3V0TXNcbiAgICA/IGZvcm1hdER1cmF0aW9uKHRpbWVvdXRNcywgeyBoaWRlVHJhaWxpbmdaZXJvczogdHJ1ZSB9KVxuICAgIDogdW5kZWZpbmVkXG4gIGlmIChlbGFwc2VkVGltZVNlY29uZHMgPT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybiA8VGV4dCBkaW1Db2xvcj57YCh0aW1lb3V0ICR7dGltZW91dH0pYH08L1RleHQ+XG4gIH1cbiAgY29uc3QgZWxhcHNlZCA9IGZvcm1hdER1cmF0aW9uKGVsYXBzZWRUaW1lU2Vjb25kcyAqIDEwMDApXG4gIGlmICh0aW1lb3V0KSB7XG4gICAgcmV0dXJuIDxUZXh0IGRpbUNvbG9yPntgKCR7ZWxhcHNlZH0gwrcgdGltZW91dCAke3RpbWVvdXR9KWB9PC9UZXh0PlxuICB9XG4gIHJldHVybiA8VGV4dCBkaW1Db2xvcj57YCgke2VsYXBzZWR9KWB9PC9UZXh0PlxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsSUFBSSxRQUFRLGNBQWM7QUFDbkMsU0FBU0MsY0FBYyxRQUFRLHVCQUF1QjtBQUV0RCxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsa0JBQWtCLENBQUMsRUFBRSxNQUFNO0VBQzNCQyxTQUFTLENBQUMsRUFBRSxNQUFNO0FBQ3BCLENBQUM7QUFFRCxPQUFPLFNBQUFDLGlCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQTBCO0lBQUFMLGtCQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFHekI7RUFDTixJQUFJSCxrQkFBa0IsS0FBS00sU0FBdUIsSUFBOUMsQ0FBcUNMLFNBQVM7SUFBQSxPQUN6QyxJQUFJO0VBQUE7RUFDWixJQUFBTSxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBSCxTQUFBO0lBQ2VNLEVBQUEsR0FBQU4sU0FBUyxHQUNyQkgsY0FBYyxDQUFDRyxTQUFTLEVBQUU7TUFBQU8saUJBQUEsRUFBcUI7SUFBSyxDQUM1QyxDQUFDLEdBRkdGLFNBRUg7SUFBQUYsQ0FBQSxNQUFBSCxTQUFBO0lBQUFHLENBQUEsTUFBQUcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBRmIsTUFBQUssT0FBQSxHQUFnQkYsRUFFSDtFQUNiLElBQUlQLGtCQUFrQixLQUFLTSxTQUFTO0lBQ1gsTUFBQUksRUFBQSxlQUFZRCxPQUFPLEdBQUc7SUFBQSxJQUFBRSxFQUFBO0lBQUEsSUFBQVAsQ0FBQSxRQUFBTSxFQUFBO01BQXRDQyxFQUFBLElBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBRSxDQUFBRCxFQUFxQixDQUFFLEVBQXRDLElBQUksQ0FBeUM7TUFBQU4sQ0FBQSxNQUFBTSxFQUFBO01BQUFOLENBQUEsTUFBQU8sRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQVAsQ0FBQTtJQUFBO0lBQUEsT0FBOUNPLEVBQThDO0VBQUE7RUFFeEIsTUFBQUQsRUFBQSxHQUFBVixrQkFBa0IsR0FBRyxJQUFJO0VBQUEsSUFBQVcsRUFBQTtFQUFBLElBQUFQLENBQUEsUUFBQU0sRUFBQTtJQUF4Q0MsRUFBQSxHQUFBYixjQUFjLENBQUNZLEVBQXlCLENBQUM7SUFBQU4sQ0FBQSxNQUFBTSxFQUFBO0lBQUFOLENBQUEsTUFBQU8sRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVAsQ0FBQTtFQUFBO0VBQXpELE1BQUFRLE9BQUEsR0FBZ0JELEVBQXlDO0VBQ3pELElBQUlGLE9BQU87SUFDYyxNQUFBSSxFQUFBLE9BQUlELE9BQU8sY0FBY0gsT0FBTyxHQUFHO0lBQUEsSUFBQUssRUFBQTtJQUFBLElBQUFWLENBQUEsUUFBQVMsRUFBQTtNQUFuREMsRUFBQSxJQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUUsQ0FBQUQsRUFBa0MsQ0FBRSxFQUFuRCxJQUFJLENBQXNEO01BQUFULENBQUEsTUFBQVMsRUFBQTtNQUFBVCxDQUFBLE1BQUFVLEVBQUE7SUFBQTtNQUFBQSxFQUFBLEdBQUFWLENBQUE7SUFBQTtJQUFBLE9BQTNEVSxFQUEyRDtFQUFBO0VBRTdDLE1BQUFELEVBQUEsT0FBSUQsT0FBTyxHQUFHO0VBQUEsSUFBQUUsRUFBQTtFQUFBLElBQUFWLENBQUEsUUFBQVMsRUFBQTtJQUE5QkMsRUFBQSxJQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUUsQ0FBQUQsRUFBYSxDQUFFLEVBQTlCLElBQUksQ0FBaUM7SUFBQVQsQ0FBQSxNQUFBUyxFQUFBO0lBQUFULENBQUEsTUFBQVUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVYsQ0FBQTtFQUFBO0VBQUEsT0FBdENVLEVBQXNDO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/skills/SkillsMenu.tsx b/components/skills/SkillsMenu.tsx new file mode 100644 index 0000000..9c7facb --- /dev/null +++ b/components/skills/SkillsMenu.tsx @@ -0,0 +1,237 @@ +import { c as _c } from "react/compiler-runtime"; +import capitalize from 'lodash-es/capitalize.js'; +import * as React from 'react'; +import { useMemo } from 'react'; +import { type Command, type CommandBase, type CommandResultDisplay, getCommandName, type PromptCommand } from '../../commands.js'; +import { Box, Text } from '../../ink.js'; +import { estimateSkillFrontmatterTokens, getSkillsPath } from '../../skills/loadSkillsDir.js'; +import { getDisplayPath } from '../../utils/file.js'; +import { formatTokens } from '../../utils/format.js'; +import { getSettingSourceName, type SettingSource } from '../../utils/settings/constants.js'; +import { plural } from '../../utils/stringUtils.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { Dialog } from '../design-system/Dialog.js'; + +// Skills are always PromptCommands with CommandBase properties +type SkillCommand = CommandBase & PromptCommand; +type SkillSource = SettingSource | 'plugin' | 'mcp'; +type Props = { + onExit: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; + commands: Command[]; +}; +function getSourceTitle(source: SkillSource): string { + if (source === 'plugin') { + return 'Plugin skills'; + } + if (source === 'mcp') { + return 'MCP skills'; + } + return `${capitalize(getSettingSourceName(source))} skills`; +} +function getSourceSubtitle(source: SkillSource, skills: SkillCommand[]): string | undefined { + // MCP skills show server names; file-based skills show filesystem paths. + // Skill names are `:`, not `mcp____…`. + if (source === 'mcp') { + const servers = [...new Set(skills.map(s => { + const idx = s.name.indexOf(':'); + return idx > 0 ? s.name.slice(0, idx) : null; + }).filter((n): n is string => n != null))]; + return servers.length > 0 ? servers.join(', ') : undefined; + } + const skillsPath = getDisplayPath(getSkillsPath(source, 'skills')); + const hasCommandsSkills = skills.some(s => s.loadedFrom === 'commands_DEPRECATED'); + return hasCommandsSkills ? `${skillsPath}, ${getDisplayPath(getSkillsPath(source, 'commands'))}` : skillsPath; +} +export function SkillsMenu(t0) { + const $ = _c(35); + const { + onExit, + commands + } = t0; + let t1; + if ($[0] !== commands) { + t1 = commands.filter(_temp); + $[0] = commands; + $[1] = t1; + } else { + t1 = $[1]; + } + const skills = t1; + let groups; + if ($[2] !== skills) { + groups = { + policySettings: [], + userSettings: [], + projectSettings: [], + localSettings: [], + flagSettings: [], + plugin: [], + mcp: [] + }; + for (const skill of skills) { + const source = skill.source as SkillSource; + if (source in groups) { + groups[source].push(skill); + } + } + for (const group of Object.values(groups)) { + group.sort(_temp2); + } + $[2] = skills; + $[3] = groups; + } else { + groups = $[3]; + } + const skillsBySource = groups; + let t2; + if ($[4] !== onExit) { + t2 = () => { + onExit("Skills dialog dismissed", { + display: "system" + }); + }; + $[4] = onExit; + $[5] = t2; + } else { + t2 = $[5]; + } + const handleCancel = t2; + if (skills.length === 0) { + let t3; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t3 = Create skills in .claude/skills/ or ~/.claude/skills/; + $[6] = t3; + } else { + t3 = $[6]; + } + let t4; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t4 = ; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] !== handleCancel) { + t5 = {t3}{t4}; + $[8] = handleCancel; + $[9] = t5; + } else { + t5 = $[9]; + } + return t5; + } + const renderSkill = _temp3; + let t3; + if ($[10] !== skillsBySource) { + t3 = source_0 => { + const groupSkills = skillsBySource[source_0]; + if (groupSkills.length === 0) { + return null; + } + const title = getSourceTitle(source_0); + const subtitle = getSourceSubtitle(source_0, groupSkills); + return {title}{subtitle && ({subtitle})}{groupSkills.map(skill_1 => renderSkill(skill_1))}; + }; + $[10] = skillsBySource; + $[11] = t3; + } else { + t3 = $[11]; + } + const renderSkillGroup = t3; + const t4 = skills.length; + let t5; + if ($[12] !== skills.length) { + t5 = plural(skills.length, "skill"); + $[12] = skills.length; + $[13] = t5; + } else { + t5 = $[13]; + } + const t6 = `${t4} ${t5}`; + let t7; + if ($[14] !== renderSkillGroup) { + t7 = renderSkillGroup("projectSettings"); + $[14] = renderSkillGroup; + $[15] = t7; + } else { + t7 = $[15]; + } + let t8; + if ($[16] !== renderSkillGroup) { + t8 = renderSkillGroup("userSettings"); + $[16] = renderSkillGroup; + $[17] = t8; + } else { + t8 = $[17]; + } + let t9; + if ($[18] !== renderSkillGroup) { + t9 = renderSkillGroup("policySettings"); + $[18] = renderSkillGroup; + $[19] = t9; + } else { + t9 = $[19]; + } + let t10; + if ($[20] !== renderSkillGroup) { + t10 = renderSkillGroup("plugin"); + $[20] = renderSkillGroup; + $[21] = t10; + } else { + t10 = $[21]; + } + let t11; + if ($[22] !== renderSkillGroup) { + t11 = renderSkillGroup("mcp"); + $[22] = renderSkillGroup; + $[23] = t11; + } else { + t11 = $[23]; + } + let t12; + if ($[24] !== t10 || $[25] !== t11 || $[26] !== t7 || $[27] !== t8 || $[28] !== t9) { + t12 = {t7}{t8}{t9}{t10}{t11}; + $[24] = t10; + $[25] = t11; + $[26] = t7; + $[27] = t8; + $[28] = t9; + $[29] = t12; + } else { + t12 = $[29]; + } + let t13; + if ($[30] === Symbol.for("react.memo_cache_sentinel")) { + t13 = ; + $[30] = t13; + } else { + t13 = $[30]; + } + let t14; + if ($[31] !== handleCancel || $[32] !== t12 || $[33] !== t6) { + t14 = {t12}{t13}; + $[31] = handleCancel; + $[32] = t12; + $[33] = t6; + $[34] = t14; + } else { + t14 = $[34]; + } + return t14; +} +function _temp3(skill_0) { + const estimatedTokens = estimateSkillFrontmatterTokens(skill_0); + const tokenDisplay = `~${formatTokens(estimatedTokens)}`; + const pluginName = skill_0.source === "plugin" ? skill_0.pluginInfo?.pluginManifest.name : undefined; + return {getCommandName(skill_0)}{pluginName ? ` · ${pluginName}` : ""} · {tokenDisplay} description tokens; +} +function _temp2(a, b) { + return getCommandName(a).localeCompare(getCommandName(b)); +} +function _temp(cmd) { + return cmd.type === "prompt" && (cmd.loadedFrom === "skills" || cmd.loadedFrom === "commands_DEPRECATED" || cmd.loadedFrom === "plugin" || cmd.loadedFrom === "mcp"); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjYXBpdGFsaXplIiwiUmVhY3QiLCJ1c2VNZW1vIiwiQ29tbWFuZCIsIkNvbW1hbmRCYXNlIiwiQ29tbWFuZFJlc3VsdERpc3BsYXkiLCJnZXRDb21tYW5kTmFtZSIsIlByb21wdENvbW1hbmQiLCJCb3giLCJUZXh0IiwiZXN0aW1hdGVTa2lsbEZyb250bWF0dGVyVG9rZW5zIiwiZ2V0U2tpbGxzUGF0aCIsImdldERpc3BsYXlQYXRoIiwiZm9ybWF0VG9rZW5zIiwiZ2V0U2V0dGluZ1NvdXJjZU5hbWUiLCJTZXR0aW5nU291cmNlIiwicGx1cmFsIiwiQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IiwiRGlhbG9nIiwiU2tpbGxDb21tYW5kIiwiU2tpbGxTb3VyY2UiLCJQcm9wcyIsIm9uRXhpdCIsInJlc3VsdCIsIm9wdGlvbnMiLCJkaXNwbGF5IiwiY29tbWFuZHMiLCJnZXRTb3VyY2VUaXRsZSIsInNvdXJjZSIsImdldFNvdXJjZVN1YnRpdGxlIiwic2tpbGxzIiwic2VydmVycyIsIlNldCIsIm1hcCIsInMiLCJpZHgiLCJuYW1lIiwiaW5kZXhPZiIsInNsaWNlIiwiZmlsdGVyIiwibiIsImxlbmd0aCIsImpvaW4iLCJ1bmRlZmluZWQiLCJza2lsbHNQYXRoIiwiaGFzQ29tbWFuZHNTa2lsbHMiLCJzb21lIiwibG9hZGVkRnJvbSIsIlNraWxsc01lbnUiLCJ0MCIsIiQiLCJfYyIsInQxIiwiX3RlbXAiLCJncm91cHMiLCJwb2xpY3lTZXR0aW5ncyIsInVzZXJTZXR0aW5ncyIsInByb2plY3RTZXR0aW5ncyIsImxvY2FsU2V0dGluZ3MiLCJmbGFnU2V0dGluZ3MiLCJwbHVnaW4iLCJtY3AiLCJza2lsbCIsInB1c2giLCJncm91cCIsIk9iamVjdCIsInZhbHVlcyIsInNvcnQiLCJfdGVtcDIiLCJza2lsbHNCeVNvdXJjZSIsInQyIiwiaGFuZGxlQ2FuY2VsIiwidDMiLCJTeW1ib2wiLCJmb3IiLCJ0NCIsInQ1IiwicmVuZGVyU2tpbGwiLCJfdGVtcDMiLCJzb3VyY2VfMCIsImdyb3VwU2tpbGxzIiwidGl0bGUiLCJzdWJ0aXRsZSIsInNraWxsXzEiLCJyZW5kZXJTa2lsbEdyb3VwIiwidDYiLCJ0NyIsInQ4IiwidDkiLCJ0MTAiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJza2lsbF8wIiwiZXN0aW1hdGVkVG9rZW5zIiwidG9rZW5EaXNwbGF5IiwicGx1Z2luTmFtZSIsInBsdWdpbkluZm8iLCJwbHVnaW5NYW5pZmVzdCIsImEiLCJiIiwibG9jYWxlQ29tcGFyZSIsImNtZCIsInR5cGUiXSwic291cmNlcyI6WyJTa2lsbHNNZW51LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgY2FwaXRhbGl6ZSBmcm9tICdsb2Rhc2gtZXMvY2FwaXRhbGl6ZS5qcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTWVtbyB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHtcbiAgdHlwZSBDb21tYW5kLFxuICB0eXBlIENvbW1hbmRCYXNlLFxuICB0eXBlIENvbW1hbmRSZXN1bHREaXNwbGF5LFxuICBnZXRDb21tYW5kTmFtZSxcbiAgdHlwZSBQcm9tcHRDb21tYW5kLFxufSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7XG4gIGVzdGltYXRlU2tpbGxGcm9udG1hdHRlclRva2VucyxcbiAgZ2V0U2tpbGxzUGF0aCxcbn0gZnJvbSAnLi4vLi4vc2tpbGxzL2xvYWRTa2lsbHNEaXIuanMnXG5pbXBvcnQgeyBnZXREaXNwbGF5UGF0aCB9IGZyb20gJy4uLy4uL3V0aWxzL2ZpbGUuanMnXG5pbXBvcnQgeyBmb3JtYXRUb2tlbnMgfSBmcm9tICcuLi8uLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQge1xuICBnZXRTZXR0aW5nU291cmNlTmFtZSxcbiAgdHlwZSBTZXR0aW5nU291cmNlLFxufSBmcm9tICcuLi8uLi91dGlscy9zZXR0aW5ncy9jb25zdGFudHMuanMnXG5pbXBvcnQgeyBwbHVyYWwgfSBmcm9tICcuLi8uLi91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB7IENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludCB9IGZyb20gJy4uL0NvbmZpZ3VyYWJsZVNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuXG4vLyBTa2lsbHMgYXJlIGFsd2F5cyBQcm9tcHRDb21tYW5kcyB3aXRoIENvbW1hbmRCYXNlIHByb3BlcnRpZXNcbnR5cGUgU2tpbGxDb21tYW5kID0gQ29tbWFuZEJhc2UgJiBQcm9tcHRDb21tYW5kXG5cbnR5cGUgU2tpbGxTb3VyY2UgPSBTZXR0aW5nU291cmNlIHwgJ3BsdWdpbicgfCAnbWNwJ1xuXG50eXBlIFByb3BzID0ge1xuICBvbkV4aXQ6IChcbiAgICByZXN1bHQ/OiBzdHJpbmcsXG4gICAgb3B0aW9ucz86IHsgZGlzcGxheT86IENvbW1hbmRSZXN1bHREaXNwbGF5IH0sXG4gICkgPT4gdm9pZFxuICBjb21tYW5kczogQ29tbWFuZFtdXG59XG5cbmZ1bmN0aW9uIGdldFNvdXJjZVRpdGxlKHNvdXJjZTogU2tpbGxTb3VyY2UpOiBzdHJpbmcge1xuICBpZiAoc291cmNlID09PSAncGx1Z2luJykge1xuICAgIHJldHVybiAnUGx1Z2luIHNraWxscydcbiAgfVxuICBpZiAoc291cmNlID09PSAnbWNwJykge1xuICAgIHJldHVybiAnTUNQIHNraWxscydcbiAgfVxuICByZXR1cm4gYCR7Y2FwaXRhbGl6ZShnZXRTZXR0aW5nU291cmNlTmFtZShzb3VyY2UpKX0gc2tpbGxzYFxufVxuXG5mdW5jdGlvbiBnZXRTb3VyY2VTdWJ0aXRsZShcbiAgc291cmNlOiBTa2lsbFNvdXJjZSxcbiAgc2tpbGxzOiBTa2lsbENvbW1hbmRbXSxcbik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gIC8vIE1DUCBza2lsbHMgc2hvdyBzZXJ2ZXIgbmFtZXM7IGZpbGUtYmFzZWQgc2tpbGxzIHNob3cgZmlsZXN5c3RlbSBwYXRocy5cbiAgLy8gU2tpbGwgbmFtZXMgYXJlIGA8c2VydmVyPjo8c2tpbGw+YCwgbm90IGBtY3BfXzxzZXJ2ZXI+X1/igKZgLlxuICBpZiAoc291cmNlID09PSAnbWNwJykge1xuICAgIGNvbnN0IHNlcnZlcnMgPSBbXG4gICAgICAuLi5uZXcgU2V0KFxuICAgICAgICBza2lsbHNcbiAgICAgICAgICAubWFwKHMgPT4ge1xuICAgICAgICAgICAgY29uc3QgaWR4ID0gcy5uYW1lLmluZGV4T2YoJzonKVxuICAgICAgICAgICAgcmV0dXJuIGlkeCA+IDAgPyBzLm5hbWUuc2xpY2UoMCwgaWR4KSA6IG51bGxcbiAgICAgICAgICB9KVxuICAgICAgICAgIC5maWx0ZXIoKG4pOiBuIGlzIHN0cmluZyA9PiBuICE9IG51bGwpLFxuICAgICAgKSxcbiAgICBdXG4gICAgcmV0dXJuIHNlcnZlcnMubGVuZ3RoID4gMCA/IHNlcnZlcnMuam9pbignLCAnKSA6IHVuZGVmaW5lZFxuICB9XG4gIGNvbnN0IHNraWxsc1BhdGggPSBnZXREaXNwbGF5UGF0aChnZXRTa2lsbHNQYXRoKHNvdXJjZSwgJ3NraWxscycpKVxuICBjb25zdCBoYXNDb21tYW5kc1NraWxscyA9IHNraWxscy5zb21lKFxuICAgIHMgPT4gcy5sb2FkZWRGcm9tID09PSAnY29tbWFuZHNfREVQUkVDQVRFRCcsXG4gIClcbiAgcmV0dXJuIGhhc0NvbW1hbmRzU2tpbGxzXG4gICAgPyBgJHtza2lsbHNQYXRofSwgJHtnZXREaXNwbGF5UGF0aChnZXRTa2lsbHNQYXRoKHNvdXJjZSwgJ2NvbW1hbmRzJykpfWBcbiAgICA6IHNraWxsc1BhdGhcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFNraWxsc01lbnUoeyBvbkV4aXQsIGNvbW1hbmRzIH06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgLy8gRmlsdGVyIGNvbW1hbmRzIGZvciBza2lsbHMgYW5kIGNhc3QgdG8gU2tpbGxDb21tYW5kXG4gIGNvbnN0IHNraWxscyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIHJldHVybiBjb21tYW5kcy5maWx0ZXIoXG4gICAgICAoY21kKTogY21kIGlzIFNraWxsQ29tbWFuZCA9PlxuICAgICAgICBjbWQudHlwZSA9PT0gJ3Byb21wdCcgJiZcbiAgICAgICAgKGNtZC5sb2FkZWRGcm9tID09PSAnc2tpbGxzJyB8fFxuICAgICAgICAgIGNtZC5sb2FkZWRGcm9tID09PSAnY29tbWFuZHNfREVQUkVDQVRFRCcgfHxcbiAgICAgICAgICBjbWQubG9hZGVkRnJvbSA9PT0gJ3BsdWdpbicgfHxcbiAgICAgICAgICBjbWQubG9hZGVkRnJvbSA9PT0gJ21jcCcpLFxuICAgIClcbiAgfSwgW2NvbW1hbmRzXSlcblxuICBjb25zdCBza2lsbHNCeVNvdXJjZSA9IHVzZU1lbW8oKCk6IFJlY29yZDxTa2lsbFNvdXJjZSwgU2tpbGxDb21tYW5kW10+ID0+IHtcbiAgICBjb25zdCBncm91cHM6IFJlY29yZDxTa2lsbFNvdXJjZSwgU2tpbGxDb21tYW5kW10+ID0ge1xuICAgICAgcG9saWN5U2V0dGluZ3M6IFtdLFxuICAgICAgdXNlclNldHRpbmdzOiBbXSxcbiAgICAgIHByb2plY3RTZXR0aW5nczogW10sXG4gICAgICBsb2NhbFNldHRpbmdzOiBbXSxcbiAgICAgIGZsYWdTZXR0aW5nczogW10sXG4gICAgICBwbHVnaW46IFtdLFxuICAgICAgbWNwOiBbXSxcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IHNraWxsIG9mIHNraWxscykge1xuICAgICAgY29uc3Qgc291cmNlID0gc2tpbGwuc291cmNlIGFzIFNraWxsU291cmNlXG4gICAgICBpZiAoc291cmNlIGluIGdyb3Vwcykge1xuICAgICAgICBncm91cHNbc291cmNlXS5wdXNoKHNraWxsKVxuICAgICAgfVxuICAgIH1cblxuICAgIGZvciAoY29uc3QgZ3JvdXAgb2YgT2JqZWN0LnZhbHVlcyhncm91cHMpKSB7XG4gICAgICBncm91cC5zb3J0KChhLCBiKSA9PiBnZXRDb21tYW5kTmFtZShhKS5sb2NhbGVDb21wYXJlKGdldENvbW1hbmROYW1lKGIpKSlcbiAgICB9XG5cbiAgICByZXR1cm4gZ3JvdXBzXG4gIH0sIFtza2lsbHNdKVxuXG4gIGNvbnN0IGhhbmRsZUNhbmNlbCA9ICgpOiB2b2lkID0+IHtcbiAgICBvbkV4aXQoJ1NraWxscyBkaWFsb2cgZGlzbWlzc2VkJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICB9XG5cbiAgaWYgKHNraWxscy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gKFxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT1cIlNraWxsc1wiXG4gICAgICAgIHN1YnRpdGxlPVwiTm8gc2tpbGxzIGZvdW5kXCJcbiAgICAgICAgb25DYW5jZWw9e2hhbmRsZUNhbmNlbH1cbiAgICAgICAgaGlkZUlucHV0R3VpZGVcbiAgICAgID5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgQ3JlYXRlIHNraWxscyBpbiAuY2xhdWRlL3NraWxscy8gb3Igfi8uY2xhdWRlL3NraWxscy9cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgYWN0aW9uPVwiY29uZmlybTpub1wiXG4gICAgICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgICAgIGZhbGxiYWNrPVwiRXNjXCJcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwiY2xvc2VcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvRGlhbG9nPlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IHJlbmRlclNraWxsID0gKHNraWxsOiBTa2lsbENvbW1hbmQpID0+IHtcbiAgICBjb25zdCBlc3RpbWF0ZWRUb2tlbnMgPSBlc3RpbWF0ZVNraWxsRnJvbnRtYXR0ZXJUb2tlbnMoc2tpbGwpXG4gICAgY29uc3QgdG9rZW5EaXNwbGF5ID0gYH4ke2Zvcm1hdFRva2Vucyhlc3RpbWF0ZWRUb2tlbnMpfWBcbiAgICBjb25zdCBwbHVnaW5OYW1lID1cbiAgICAgIHNraWxsLnNvdXJjZSA9PT0gJ3BsdWdpbidcbiAgICAgICAgPyBza2lsbC5wbHVnaW5JbmZvPy5wbHVnaW5NYW5pZmVzdC5uYW1lXG4gICAgICAgIDogdW5kZWZpbmVkXG5cbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBrZXk9e2Ake3NraWxsLm5hbWV9LSR7c2tpbGwuc291cmNlfWB9PlxuICAgICAgICA8VGV4dD57Z2V0Q29tbWFuZE5hbWUoc2tpbGwpfTwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAge3BsdWdpbk5hbWUgPyBgIMK3ICR7cGx1Z2luTmFtZX1gIDogJyd9IMK3IHt0b2tlbkRpc3BsYXl9IGRlc2NyaXB0aW9uXG4gICAgICAgICAgdG9rZW5zXG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IHJlbmRlclNraWxsR3JvdXAgPSAoc291cmNlOiBTa2lsbFNvdXJjZSkgPT4ge1xuICAgIGNvbnN0IGdyb3VwU2tpbGxzID0gc2tpbGxzQnlTb3VyY2Vbc291cmNlXVxuICAgIGlmIChncm91cFNraWxscy5sZW5ndGggPT09IDApIHJldHVybiBudWxsXG5cbiAgICBjb25zdCB0aXRsZSA9IGdldFNvdXJjZVRpdGxlKHNvdXJjZSlcbiAgICBjb25zdCBzdWJ0aXRsZSA9IGdldFNvdXJjZVN1YnRpdGxlKHNvdXJjZSwgZ3JvdXBTa2lsbHMpXG5cbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIga2V5PXtzb3VyY2V9PlxuICAgICAgICA8Qm94PlxuICAgICAgICAgIDxUZXh0IGJvbGQgZGltQ29sb3I+XG4gICAgICAgICAgICB7dGl0bGV9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIHtzdWJ0aXRsZSAmJiA8VGV4dCBkaW1Db2xvcj4gKHtzdWJ0aXRsZX0pPC9UZXh0Pn1cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHtncm91cFNraWxscy5tYXAoc2tpbGwgPT4gcmVuZGVyU2tpbGwoc2tpbGwpKX1cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPERpYWxvZ1xuICAgICAgdGl0bGU9XCJTa2lsbHNcIlxuICAgICAgc3VidGl0bGU9e2Ake3NraWxscy5sZW5ndGh9ICR7cGx1cmFsKHNraWxscy5sZW5ndGgsICdza2lsbCcpfWB9XG4gICAgICBvbkNhbmNlbD17aGFuZGxlQ2FuY2VsfVxuICAgICAgaGlkZUlucHV0R3VpZGVcbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICB7cmVuZGVyU2tpbGxHcm91cCgncHJvamVjdFNldHRpbmdzJyl9XG4gICAgICAgIHtyZW5kZXJTa2lsbEdyb3VwKCd1c2VyU2V0dGluZ3MnKX1cbiAgICAgICAge3JlbmRlclNraWxsR3JvdXAoJ3BvbGljeVNldHRpbmdzJyl9XG4gICAgICAgIHtyZW5kZXJTa2lsbEdyb3VwKCdwbHVnaW4nKX1cbiAgICAgICAge3JlbmRlclNraWxsR3JvdXAoJ21jcCcpfVxuICAgICAgPC9Cb3g+XG4gICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICBjb250ZXh0PVwiQ29uZmlybWF0aW9uXCJcbiAgICAgICAgICBmYWxsYmFjaz1cIkVzY1wiXG4gICAgICAgICAgZGVzY3JpcHRpb249XCJjbG9zZVwiXG4gICAgICAgIC8+XG4gICAgICA8L1RleHQ+XG4gICAgPC9EaWFsb2c+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLFVBQVUsTUFBTSx5QkFBeUI7QUFDaEQsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxP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 \ No newline at end of file diff --git a/components/tasks/AsyncAgentDetailDialog.tsx b/components/tasks/AsyncAgentDetailDialog.tsx new file mode 100644 index 0000000..ee18f3b --- /dev/null +++ b/components/tasks/AsyncAgentDetailDialog.tsx @@ -0,0 +1,229 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useMemo } from 'react'; +import type { DeepImmutable } from 'src/types/utils.js'; +import { useElapsedTime } from '../../hooks/useElapsedTime.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box, Text, useTheme } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +import { getEmptyToolPermissionContext } from '../../Tool.js'; +import type { LocalAgentTaskState } from '../../tasks/LocalAgentTask/LocalAgentTask.js'; +import { getTools } from '../../tools.js'; +import { formatNumber } from '../../utils/format.js'; +import { extractTag } from '../../utils/messages.js'; +import { Byline } from '../design-system/Byline.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { UserPlanMessage } from '../messages/UserPlanMessage.js'; +import { renderToolActivity } from './renderToolActivity.js'; +import { getTaskStatusColor, getTaskStatusIcon } from './taskStatusUtils.js'; +type Props = { + agent: DeepImmutable; + onDone: () => void; + onKillAgent?: () => void; + onBack?: () => void; +}; +export function AsyncAgentDetailDialog(t0) { + const $ = _c(54); + const { + agent, + onDone, + onKillAgent, + onBack + } = t0; + const [theme] = useTheme(); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = getTools(getEmptyToolPermissionContext()); + $[0] = t1; + } else { + t1 = $[0]; + } + const tools = t1; + const elapsedTime = useElapsedTime(agent.startTime, agent.status === "running", 1000, agent.totalPausedMs ?? 0); + let t2; + if ($[1] !== onDone) { + t2 = { + "confirm:yes": onDone + }; + $[1] = onDone; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = { + context: "Confirmation" + }; + $[3] = t3; + } else { + t3 = $[3]; + } + useKeybindings(t2, t3); + let t4; + if ($[4] !== agent.status || $[5] !== onBack || $[6] !== onDone || $[7] !== onKillAgent) { + t4 = e => { + if (e.key === " ") { + e.preventDefault(); + onDone(); + } else { + if (e.key === "left" && onBack) { + e.preventDefault(); + onBack(); + } else { + if (e.key === "x" && agent.status === "running" && onKillAgent) { + e.preventDefault(); + onKillAgent(); + } + } + } + }; + $[4] = agent.status; + $[5] = onBack; + $[6] = onDone; + $[7] = onKillAgent; + $[8] = t4; + } else { + t4 = $[8]; + } + const handleKeyDown = t4; + let t5; + if ($[9] !== agent.prompt) { + t5 = extractTag(agent.prompt, "plan"); + $[9] = agent.prompt; + $[10] = t5; + } else { + t5 = $[10]; + } + const planContent = t5; + const displayPrompt = agent.prompt.length > 300 ? agent.prompt.substring(0, 297) + "\u2026" : agent.prompt; + const tokenCount = agent.result?.totalTokens ?? agent.progress?.tokenCount; + const toolUseCount = agent.result?.totalToolUseCount ?? agent.progress?.toolUseCount; + const t6 = agent.selectedAgent?.agentType ?? "agent"; + const t7 = agent.description || "Async agent"; + let t8; + if ($[11] !== t6 || $[12] !== t7) { + t8 = {t6} ›{" "}{t7}; + $[11] = t6; + $[12] = t7; + $[13] = t8; + } else { + t8 = $[13]; + } + const title = t8; + let t9; + if ($[14] !== agent.status) { + t9 = agent.status !== "running" && {getTaskStatusIcon(agent.status)}{" "}{agent.status === "completed" ? "Completed" : agent.status === "failed" ? "Failed" : "Stopped"}{" \xB7 "}; + $[14] = agent.status; + $[15] = t9; + } else { + t9 = $[15]; + } + let t10; + if ($[16] !== tokenCount) { + t10 = tokenCount !== undefined && tokenCount > 0 && <> · {formatNumber(tokenCount)} tokens; + $[16] = tokenCount; + $[17] = t10; + } else { + t10 = $[17]; + } + let t11; + if ($[18] !== toolUseCount) { + t11 = toolUseCount !== undefined && toolUseCount > 0 && <>{" "}· {toolUseCount} {toolUseCount === 1 ? "tool" : "tools"}; + $[18] = toolUseCount; + $[19] = t11; + } else { + t11 = $[19]; + } + let t12; + if ($[20] !== elapsedTime || $[21] !== t10 || $[22] !== t11) { + t12 = {elapsedTime}{t10}{t11}; + $[20] = elapsedTime; + $[21] = t10; + $[22] = t11; + $[23] = t12; + } else { + t12 = $[23]; + } + let t13; + if ($[24] !== t12 || $[25] !== t9) { + t13 = {t9}{t12}; + $[24] = t12; + $[25] = t9; + $[26] = t13; + } else { + t13 = $[26]; + } + const subtitle = t13; + let t14; + if ($[27] !== agent.status || $[28] !== onBack || $[29] !== onKillAgent) { + t14 = exitState => exitState.pending ? Press {exitState.keyName} again to exit : {onBack && }{agent.status === "running" && onKillAgent && }; + $[27] = agent.status; + $[28] = onBack; + $[29] = onKillAgent; + $[30] = t14; + } else { + t14 = $[30]; + } + let t15; + if ($[31] !== agent.progress || $[32] !== agent.status || $[33] !== theme) { + t15 = agent.status === "running" && agent.progress?.recentActivities && agent.progress.recentActivities.length > 0 && Progress{agent.progress.recentActivities.map((activity, i) => {i === agent.progress.recentActivities.length - 1 ? "\u203A " : " "}{renderToolActivity(activity, tools, theme)})}; + $[31] = agent.progress; + $[32] = agent.status; + $[33] = theme; + $[34] = t15; + } else { + t15 = $[34]; + } + let t16; + if ($[35] !== displayPrompt || $[36] !== planContent) { + t16 = planContent ? : Prompt{displayPrompt}; + $[35] = displayPrompt; + $[36] = planContent; + $[37] = t16; + } else { + t16 = $[37]; + } + let t17; + if ($[38] !== agent.error || $[39] !== agent.status) { + t17 = agent.status === "failed" && agent.error && Error{agent.error}; + $[38] = agent.error; + $[39] = agent.status; + $[40] = t17; + } else { + t17 = $[40]; + } + let t18; + if ($[41] !== t15 || $[42] !== t16 || $[43] !== t17) { + t18 = {t15}{t16}{t17}; + $[41] = t15; + $[42] = t16; + $[43] = t17; + $[44] = t18; + } else { + t18 = $[44]; + } + let t19; + if ($[45] !== onDone || $[46] !== subtitle || $[47] !== t14 || $[48] !== t18 || $[49] !== title) { + t19 = {t18}; + $[45] = onDone; + $[46] = subtitle; + $[47] = t14; + $[48] = t18; + $[49] = title; + $[50] = t19; + } else { + t19 = $[50]; + } + let t20; + if ($[51] !== handleKeyDown || $[52] !== t19) { + t20 = {t19}; + $[51] = handleKeyDown; + $[52] = t19; + $[53] = t20; + } else { + t20 = $[53]; + } + return t20; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZU1lbW8iLCJEZWVwSW1tdXRhYmxlIiwidXNlRWxhcHNlZFRpbWUiLCJLZXlib2FyZEV2ZW50IiwiQm94IiwiVGV4dCIsInVzZVRoZW1lIiwidXNlS2V5YmluZGluZ3MiLCJnZXRFbXB0eVRvb2xQZXJtaXNzaW9uQ29udGV4dCIsIkxvY2FsQWdlbnRUYXNrU3RhdGUiLCJnZXRUb29scyIsImZvcm1hdE51bWJlciIsImV4dHJhY3RUYWciLCJCeWxpbmUiLCJEaWFsb2ciLCJLZXlib2FyZFNob3J0Y3V0SGludCIsIlVzZXJQbGFuTWVzc2FnZSIsInJlbmRlclRvb2xBY3Rpdml0eSIsImdldFRhc2tTdGF0dXNDb2xvciIsImdldFRhc2tTdGF0dXNJY29uIiwiUHJvcHMiLCJhZ2VudCIsIm9uRG9uZSIsIm9uS2lsbEFnZW50Iiwib25CYWNrIiwiQXN5bmNBZ2VudERldGFpbERpYWxvZyIsInQwIiwiJCIsIl9jIiwidGhlbWUiLCJ0MSIsIlN5bWJvbCIsImZvciIsInRvb2xzIiwiZWxhcHNlZFRpbWUiLCJzdGFydFRpbWUiLCJzdGF0dXMiLCJ0b3RhbFBhdXNlZE1zIiwidDIiLCJ0MyIsImNvbnRleHQiLCJ0NCIsImUiLCJrZXkiLCJwcmV2ZW50RGVmYXVsdCIsImhhbmRsZUtleURvd24iLCJ0NSIsInByb21wdCIsInBsYW5Db250ZW50IiwiZGlzcGxheVByb21wdCIsImxlbmd0aCIsInN1YnN0cmluZyIsInRva2VuQ291bnQiLCJyZXN1bHQiLCJ0b3RhbFRva2VucyIsInByb2dyZXNzIiwidG9vbFVzZUNvdW50IiwidG90YWxUb29sVXNlQ291bnQiLCJ0NiIsInNlbGVjdGVkQWdlbnQiLCJhZ2VudFR5cGUiLCJ0NyIsImRlc2NyaXB0aW9uIiwidDgiLCJ0aXRsZSIsInQ5IiwidDEwIiwidW5kZWZpbmVkIiwidDExIiwidDEyIiwidDEzIiwic3VidGl0bGUiLCJ0MTQiLCJleGl0U3RhdGUiLCJwZW5kaW5nIiwia2V5TmFtZSIsInQxNSIsInJlY2VudEFjdGl2aXRpZXMiLCJtYXAiLCJhY3Rpdml0eSIsImkiLCJ0MTYiLCJ0MTciLCJlcnJvciIsInQxOCIsInQxOSIsInQyMCJdLCJzb3VyY2VzIjpbIkFzeW5jQWdlbnREZXRhaWxEaWFsb2cudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VNZW1vIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IERlZXBJbW11dGFibGUgfSBmcm9tICdzcmMvdHlwZXMvdXRpbHMuanMnXG5pbXBvcnQgeyB1c2VFbGFwc2VkVGltZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUVsYXBzZWRUaW1lLmpzJ1xuaW1wb3J0IHR5cGUgeyBLZXlib2FyZEV2ZW50IH0gZnJvbSAnLi4vLi4vaW5rL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB7IEJveCwgVGV4dCwgdXNlVGhlbWUgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5ncyB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyBnZXRFbXB0eVRvb2xQZXJtaXNzaW9uQ29udGV4dCB9IGZyb20gJy4uLy4uL1Rvb2wuanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsQWdlbnRUYXNrU3RhdGUgfSBmcm9tICcuLi8uLi90YXNrcy9Mb2NhbEFnZW50VGFzay9Mb2NhbEFnZW50VGFzay5qcydcbmltcG9ydCB7IGdldFRvb2xzIH0gZnJvbSAnLi4vLi4vdG9vbHMuanMnXG5pbXBvcnQgeyBmb3JtYXROdW1iZXIgfSBmcm9tICcuLi8uLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQgeyBleHRyYWN0VGFnIH0gZnJvbSAnLi4vLi4vdXRpbHMvbWVzc2FnZXMuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgVXNlclBsYW5NZXNzYWdlIH0gZnJvbSAnLi4vbWVzc2FnZXMvVXNlclBsYW5NZXNzYWdlLmpzJ1xuaW1wb3J0IHsgcmVuZGVyVG9vbEFjdGl2aXR5IH0gZnJvbSAnLi9yZW5kZXJUb29sQWN0aXZpdHkuanMnXG5pbXBvcnQgeyBnZXRUYXNrU3RhdHVzQ29sb3IsIGdldFRhc2tTdGF0dXNJY29uIH0gZnJvbSAnLi90YXNrU3RhdHVzVXRpbHMuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIGFnZW50OiBEZWVwSW1tdXRhYmxlPExvY2FsQWdlbnRUYXNrU3RhdGU+XG4gIG9uRG9uZTogKCkgPT4gdm9pZFxuICBvbktpbGxBZ2VudD86ICgpID0+IHZvaWRcbiAgb25CYWNrPzogKCkgPT4gdm9pZFxufVxuXG5leHBvcnQgZnVuY3Rpb24gQXN5bmNBZ2VudERldGFpbERpYWxvZyh7XG4gIGFnZW50LFxuICBvbkRvbmUsXG4gIG9uS2lsbEFnZW50LFxuICBvbkJhY2ssXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZV0gPSB1c2VUaGVtZSgpXG5cbiAgLy8gR2V0IHRvb2xzIGZvciByZW5kZXJpbmcgYWN0aXZpdHkgbWVzc2FnZXNcbiAgY29uc3QgdG9vbHMgPSB1c2VNZW1vKCgpID0+IGdldFRvb2xzKGdldEVtcHR5VG9vbFBlcm1pc3Npb25Db250ZXh0KCkpLCBbXSlcblxuICBjb25zdCBlbGFwc2VkVGltZSA9IHVzZUVsYXBzZWRUaW1lKFxuICAgIGFnZW50LnN0YXJ0VGltZSxcbiAgICBhZ2VudC5zdGF0dXMgPT09ICdydW5uaW5nJyxcbiAgICAxMDAwLFxuICAgIGFnZW50LnRvdGFsUGF1c2VkTXMgPz8gMCxcbiAgKVxuXG4gIC8vIFJlc3RvcmUgY29uZmlybTp5ZXMgKEVudGVyL3kpIGRpc21pc3NhbCDigJQgRGlhbG9nIGhhbmRsZXMgY29uZmlybTpubyAoRXNjKVxuICAvLyBpbnRlcm5hbGx5IGJ1dCBkb2VzIE5PVCBhdXRvLXdpcmUgY29uZmlybTp5ZXMuXG4gIHVzZUtleWJpbmRpbmdzKFxuICAgIHtcbiAgICAgICdjb25maXJtOnllcyc6IG9uRG9uZSxcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicgfSxcbiAgKVxuXG4gIC8vIENvbXBvbmVudC1zcGVjaWZpYyBzaG9ydGN1dHMgc2hvd24gaW4gVUkgaGludHMgKHg9c3RvcCkgYW5kXG4gIC8vIG5hdmlnYXRpb24ga2V5cyAoc3BhY2U9ZGlzbWlzcywgbGVmdD1iYWNrKS4gVGhlc2UgYXJlIGNvbnRleHQtZGVwZW5kZW50XG4gIC8vIGFjdGlvbnMgdGllZCB0byBhZ2VudCBzdGF0ZSwgbm90IHN0YW5kYXJkIGRpYWxvZyBrZXliaW5kaW5ncy5cbiAgLy8gTm90ZTogRGlhbG9nIGNvbXBvbmVudCBhbHJlYWR5IGhhbmRsZXMgRVNDIHZpYSBjb25maXJtOm5vIGtleWJpbmRpbmc7XG4gIC8vIGNvbmZpcm06eWVzIChFbnRlci95KSBpcyBoYW5kbGVkIGJ5IHVzZUtleWJpbmRpbmdzIGFib3ZlLlxuICBjb25zdCBoYW5kbGVLZXlEb3duID0gKGU6IEtleWJvYXJkRXZlbnQpID0+IHtcbiAgICBpZiAoZS5rZXkgPT09ICcgJykge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBvbkRvbmUoKVxuICAgIH0gZWxzZSBpZiAoZS5rZXkgPT09ICdsZWZ0JyAmJiBvbkJhY2spIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25CYWNrKClcbiAgICB9IGVsc2UgaWYgKGUua2V5ID09PSAneCcgJiYgYWdlbnQuc3RhdHVzID09PSAncnVubmluZycgJiYgb25LaWxsQWdlbnQpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25LaWxsQWdlbnQoKVxuICAgIH1cbiAgfVxuXG4gIC8vIEV4dHJhY3QgcGxhbiBmcm9tIHByb21wdCAtIGlmIHByZXNlbnQsIHdlIHNob3cgdGhlIHBsYW4gaW5zdGVhZCBvZiB0aGUgcHJvbXB0XG4gIGNvbnN0IHBsYW5Db250ZW50ID0gZXh0cmFjdFRhZyhhZ2VudC5wcm9tcHQsICdwbGFuJylcblxuICBjb25zdCBkaXNwbGF5UHJvbXB0ID1cbiAgICBhZ2VudC5wcm9tcHQubGVuZ3RoID4gMzAwXG4gICAgICA/IGFnZW50LnByb21wdC5zdWJzdHJpbmcoMCwgMjk3KSArICfigKYnXG4gICAgICA6IGFnZW50LnByb21wdFxuXG4gIC8vIEdldCB0b2tlbnMgYW5kIHRvb2wgdXNlcyAoZnJvbSByZXN1bHQgaWYgY29tcGxldGVkLCBvdGhlcndpc2UgZnJvbSBwcm9ncmVzcylcbiAgY29uc3QgdG9rZW5Db3VudCA9IGFnZW50LnJlc3VsdD8udG90YWxUb2tlbnMgPz8gYWdlbnQucHJvZ3Jlc3M/LnRva2VuQ291bnRcbiAgY29uc3QgdG9vbFVzZUNvdW50ID1cbiAgICBhZ2VudC5yZXN1bHQ/LnRvdGFsVG9vbFVzZUNvdW50ID8/IGFnZW50LnByb2dyZXNzPy50b29sVXNlQ291bnRcblxuICBjb25zdCB0aXRsZSA9IChcbiAgICA8VGV4dD5cbiAgICAgIHthZ2VudC5zZWxlY3RlZEFnZW50Py5hZ2VudFR5cGUgPz8gJ2FnZW50J30g4oC6eycgJ31cbiAgICAgIHthZ2VudC5kZXNjcmlwdGlvbiB8fCAnQXN5bmMgYWdlbnQnfVxuICAgIDwvVGV4dD5cbiAgKVxuXG4gIC8vIEJ1aWxkIHN1YnRpdGxlIHdpdGggc3RhdHVzIGFuZCBzdGF0c1xuICBjb25zdCBzdWJ0aXRsZSA9IChcbiAgICA8VGV4dD5cbiAgICAgIHthZ2VudC5zdGF0dXMgIT09ICdydW5uaW5nJyAmJiAoXG4gICAgICAgIDxUZXh0IGNvbG9yPXtnZXRUYXNrU3RhdHVzQ29sb3IoYWdlbnQuc3RhdHVzKX0+XG4gICAgICAgICAge2dldFRhc2tTdGF0dXNJY29uKGFnZW50LnN0YXR1cyl9eycgJ31cbiAgICAgICAgICB7YWdlbnQuc3RhdHVzID09PSAnY29tcGxldGVkJ1xuICAgICAgICAgICAgPyAnQ29tcGxldGVkJ1xuICAgICAgICAgICAgOiBhZ2VudC5zdGF0dXMgPT09ICdmYWlsZWQnXG4gICAgICAgICAgICAgID8gJ0ZhaWxlZCdcbiAgICAgICAgICAgICAgOiAnU3RvcHBlZCd9XG4gICAgICAgICAgeycgwrcgJ31cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKX1cbiAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICB7ZWxhcHNlZFRpbWV9XG4gICAgICAgIHt0b2tlbkNvdW50ICE9PSB1bmRlZmluZWQgJiYgdG9rZW5Db3VudCA+IDAgJiYgKFxuICAgICAgICAgIDw+IMK3IHtmb3JtYXROdW1iZXIodG9rZW5Db3VudCl9IHRva2VuczwvPlxuICAgICAgICApfVxuICAgICAgICB7dG9vbFVzZUNvdW50ICE9PSB1bmRlZmluZWQgJiYgdG9vbFVzZUNvdW50ID4gMCAmJiAoXG4gICAgICAgICAgPD5cbiAgICAgICAgICAgIHsnICd9XG4gICAgICAgICAgICDCtyB7dG9vbFVzZUNvdW50fSB7dG9vbFVzZUNvdW50ID09PSAxID8gJ3Rvb2wnIDogJ3Rvb2xzJ31cbiAgICAgICAgICA8Lz5cbiAgICAgICAgKX1cbiAgICAgIDwvVGV4dD5cbiAgICA8L1RleHQ+XG4gIClcblxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgdGFiSW5kZXg9ezB9XG4gICAgICBhdXRvRm9jdXNcbiAgICAgIG9uS2V5RG93bj17aGFuZGxlS2V5RG93bn1cbiAgICA+XG4gICAgICA8RGlhbG9nXG4gICAgICAgIHRpdGxlPXt0aXRsZX1cbiAgICAgICAgc3VidGl0bGU9e3N1YnRpdGxlfVxuICAgICAgICBvbkNhbmNlbD17b25Eb25lfVxuICAgICAgICBjb2xvcj1cImJhY2tncm91bmRcIlxuICAgICAgICBpbnB1dEd1aWRlPXtleGl0U3RhdGUgPT5cbiAgICAgICAgICBleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICAgIDxUZXh0PlByZXNzIHtleGl0U3RhdGUua2V5TmFtZX0gYWdhaW4gdG8gZXhpdDwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgICAge29uQmFjayAmJiA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCLihpBcIiBhY3Rpb249XCJnbyBiYWNrXCIgLz59XG4gICAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVzYy9FbnRlci9TcGFjZVwiIGFjdGlvbj1cImNsb3NlXCIgLz5cbiAgICAgICAgICAgICAge2FnZW50LnN0YXR1cyA9PT0gJ3J1bm5pbmcnICYmIG9uS2lsbEFnZW50ICYmIChcbiAgICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJ4XCIgYWN0aW9uPVwic3RvcFwiIC8+XG4gICAgICAgICAgICAgICl9XG4gICAgICAgICAgICA8L0J5bGluZT5cbiAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgID5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgey8qIFJlY2VudCBhY3Rpdml0aWVzIGZvciBydW5uaW5nIGFnZW50cyAqL31cbiAgICAgICAgICB7YWdlbnQuc3RhdHVzID09PSAncnVubmluZycgJiZcbiAgICAgICAgICAgIGFnZW50LnByb2dyZXNzPy5yZWNlbnRBY3Rpdml0aWVzICYmXG4gICAgICAgICAgICBhZ2VudC5wcm9ncmVzcy5yZWNlbnRBY3Rpdml0aWVzLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICA8VGV4dCBib2xkIGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgUHJvZ3Jlc3NcbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAge2FnZW50LnByb2dyZXNzLnJlY2VudEFjdGl2aXRpZXMubWFwKChhY3Rpdml0eSwgaSkgPT4gKFxuICAgICAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICAgICAga2V5PXtpfVxuICAgICAgICAgICAgICAgICAgICBkaW1Db2xvcj17aSA8IGFnZW50LnByb2dyZXNzIS5yZWNlbnRBY3Rpdml0aWVzIS5sZW5ndGggLSAxfVxuICAgICAgICAgICAgICAgICAgICB3cmFwPVwidHJ1bmNhdGUtZW5kXCJcbiAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAge2kgPT09IGFnZW50LnByb2dyZXNzIS5yZWNlbnRBY3Rpdml0aWVzIS5sZW5ndGggLSAxXG4gICAgICAgICAgICAgICAgICAgICAgPyAn4oC6ICdcbiAgICAgICAgICAgICAgICAgICAgICA6ICcgICd9XG4gICAgICAgICAgICAgICAgICAgIHtyZW5kZXJUb29sQWN0aXZpdHkoYWN0aXZpdHksIHRvb2xzLCB0aGVtZSl9XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cblxuICAgICAgICAgIHsvKiBQbGFuIHNlY3Rpb24gKGlmIHByZXNlbnQpIC0gc2hvd24gaW5zdGVhZCBvZiBwcm9tcHQgKi99XG4gICAgICAgICAge3BsYW5Db250ZW50ID8gKFxuICAgICAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgICA8VXNlclBsYW5NZXNzYWdlIGFkZE1hcmdpbj17ZmFsc2V9IHBsYW5Db250ZW50PXtwbGFuQ29udGVudH0gLz5cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAvKiBQcm9tcHQgc2VjdGlvbiAtIG9ubHkgc2hvd24gd2hlbiBubyBwbGFuICovXG4gICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgICA8VGV4dCBib2xkIGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgIFByb21wdFxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIDxUZXh0IHdyYXA9XCJ3cmFwXCI+e2Rpc3BsYXlQcm9tcHR9PC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKX1cblxuICAgICAgICAgIHsvKiBFcnJvciBkZXRhaWxzIGlmIGZhaWxlZCAqL31cbiAgICAgICAgICB7YWdlbnQuc3RhdHVzID09PSAnZmFpbGVkJyAmJiBhZ2VudC5lcnJvciAmJiAoXG4gICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgICA8VGV4dCBib2xkIGNvbG9yPVwiZXJyb3JcIj5cbiAgICAgICAgICAgICAgICBFcnJvclxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIiB3cmFwPVwid3JhcFwiPlxuICAgICAgICAgICAgICAgIHthZ2VudC5lcnJvcn1cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0RpYWxvZz5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJQyxPQUFPLFFBQVEsT0FBTztBQUN0QyxjQUFjQyxhQUFhLFFBQVEsb0JBQW9CO0FBQ3ZELFNBQVNDLGNBQWMsUUFBUSwrQkFBK0I7QUFDOUQsY0FBY0MsYUFBYSxRQUFRLG9DQUFvQztBQUN2RSxTQUFTQyxHQUFHLEVBQUVDLElBQUksRUFBRUMsUUFBUSxRQUFRLGNBQWM7QUFDbEQsU0FBU0MsY0FBYyxRQUFRLG9DQUFvQztBQUNuRSxTQUFTQyw2QkFBNkIsUUFBUSxlQUFlO0FBQzdELGNBQWNDLG1C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 \ No newline at end of file diff --git a/components/tasks/BackgroundTask.tsx b/components/tasks/BackgroundTask.tsx new file mode 100644 index 0000000..4084db3 --- /dev/null +++ b/components/tasks/BackgroundTask.tsx @@ -0,0 +1,345 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Text } from 'src/ink.js'; +import type { BackgroundTaskState } from 'src/tasks/types.js'; +import type { DeepImmutable } from 'src/types/utils.js'; +import { truncate } from 'src/utils/format.js'; +import { toInkColor } from 'src/utils/ink.js'; +import { plural } from 'src/utils/stringUtils.js'; +import { DIAMOND_FILLED, DIAMOND_OPEN } from '../../constants/figures.js'; +import { RemoteSessionProgress } from './RemoteSessionProgress.js'; +import { ShellProgress, TaskStatusText } from './ShellProgress.js'; +import { describeTeammateActivity } from './taskStatusUtils.js'; +type Props = { + task: DeepImmutable; + maxActivityWidth?: number; +}; +export function BackgroundTask(t0) { + const $ = _c(92); + const { + task, + maxActivityWidth + } = t0; + const activityLimit = maxActivityWidth ?? 40; + switch (task.type) { + case "local_bash": + { + const t1 = task.kind === "monitor" ? task.description : task.command; + let t2; + if ($[0] !== activityLimit || $[1] !== t1) { + t2 = truncate(t1, activityLimit, true); + $[0] = activityLimit; + $[1] = t1; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] !== task) { + t3 = ; + $[3] = task; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== t2 || $[6] !== t3) { + t4 = {t2}{" "}{t3}; + $[5] = t2; + $[6] = t3; + $[7] = t4; + } else { + t4 = $[7]; + } + return t4; + } + case "remote_agent": + { + if (task.isRemoteReview) { + let t1; + if ($[8] !== task) { + t1 = ; + $[8] = task; + $[9] = t1; + } else { + t1 = $[9]; + } + return t1; + } + const running = task.status === "running" || task.status === "pending"; + const t1 = running ? DIAMOND_OPEN : DIAMOND_FILLED; + let t2; + if ($[10] !== t1) { + t2 = {t1} ; + $[10] = t1; + $[11] = t2; + } else { + t2 = $[11]; + } + let t3; + if ($[12] !== activityLimit || $[13] !== task.title) { + t3 = truncate(task.title, activityLimit, true); + $[12] = activityLimit; + $[13] = task.title; + $[14] = t3; + } else { + t3 = $[14]; + } + let t4; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t4 = · ; + $[15] = t4; + } else { + t4 = $[15]; + } + let t5; + if ($[16] !== task) { + t5 = ; + $[16] = task; + $[17] = t5; + } else { + t5 = $[17]; + } + let t6; + if ($[18] !== t2 || $[19] !== t3 || $[20] !== t5) { + t6 = {t2}{t3}{t4}{t5}; + $[18] = t2; + $[19] = t3; + $[20] = t5; + $[21] = t6; + } else { + t6 = $[21]; + } + return t6; + } + case "local_agent": + { + let t1; + if ($[22] !== activityLimit || $[23] !== task.description) { + t1 = truncate(task.description, activityLimit, true); + $[22] = activityLimit; + $[23] = task.description; + $[24] = t1; + } else { + t1 = $[24]; + } + const t2 = task.status === "completed" ? "done" : undefined; + const t3 = task.status === "completed" && !task.notified ? ", unread" : undefined; + let t4; + if ($[25] !== t2 || $[26] !== t3 || $[27] !== task.status) { + t4 = ; + $[25] = t2; + $[26] = t3; + $[27] = task.status; + $[28] = t4; + } else { + t4 = $[28]; + } + let t5; + if ($[29] !== t1 || $[30] !== t4) { + t5 = {t1}{" "}{t4}; + $[29] = t1; + $[30] = t4; + $[31] = t5; + } else { + t5 = $[31]; + } + return t5; + } + case "in_process_teammate": + { + let T0; + let T1; + let t1; + let t2; + let t3; + let t4; + if ($[32] !== activityLimit || $[33] !== task) { + const activity = describeTeammateActivity(task); + T1 = Text; + let t5; + if ($[40] !== task.identity.color) { + t5 = toInkColor(task.identity.color); + $[40] = task.identity.color; + $[41] = t5; + } else { + t5 = $[41]; + } + if ($[42] !== t5 || $[43] !== task.identity.agentName) { + t4 = @{task.identity.agentName}; + $[42] = t5; + $[43] = task.identity.agentName; + $[44] = t4; + } else { + t4 = $[44]; + } + T0 = Text; + t1 = true; + t2 = ": "; + t3 = truncate(activity, activityLimit, true); + $[32] = activityLimit; + $[33] = task; + $[34] = T0; + $[35] = T1; + $[36] = t1; + $[37] = t2; + $[38] = t3; + $[39] = t4; + } else { + T0 = $[34]; + T1 = $[35]; + t1 = $[36]; + t2 = $[37]; + t3 = $[38]; + t4 = $[39]; + } + let t5; + if ($[45] !== T0 || $[46] !== t1 || $[47] !== t2 || $[48] !== t3) { + t5 = {t2}{t3}; + $[45] = T0; + $[46] = t1; + $[47] = t2; + $[48] = t3; + $[49] = t5; + } else { + t5 = $[49]; + } + let t6; + if ($[50] !== T1 || $[51] !== t4 || $[52] !== t5) { + t6 = {t4}{t5}; + $[50] = T1; + $[51] = t4; + $[52] = t5; + $[53] = t6; + } else { + t6 = $[53]; + } + return t6; + } + case "local_workflow": + { + const t1 = task.workflowName ?? task.summary ?? task.description; + let t2; + if ($[54] !== activityLimit || $[55] !== t1) { + t2 = truncate(t1, activityLimit, true); + $[54] = activityLimit; + $[55] = t1; + $[56] = t2; + } else { + t2 = $[56]; + } + let t3; + if ($[57] !== task.agentCount || $[58] !== task.status) { + t3 = task.status === "running" ? `${task.agentCount} ${plural(task.agentCount, "agent")}` : task.status === "completed" ? "done" : undefined; + $[57] = task.agentCount; + $[58] = task.status; + $[59] = t3; + } else { + t3 = $[59]; + } + const t4 = task.status === "completed" && !task.notified ? ", unread" : undefined; + let t5; + if ($[60] !== t3 || $[61] !== t4 || $[62] !== task.status) { + t5 = ; + $[60] = t3; + $[61] = t4; + $[62] = task.status; + $[63] = t5; + } else { + t5 = $[63]; + } + let t6; + if ($[64] !== t2 || $[65] !== t5) { + t6 = {t2}{" "}{t5}; + $[64] = t2; + $[65] = t5; + $[66] = t6; + } else { + t6 = $[66]; + } + return t6; + } + case "monitor_mcp": + { + let t1; + if ($[67] !== activityLimit || $[68] !== task.description) { + t1 = truncate(task.description, activityLimit, true); + $[67] = activityLimit; + $[68] = task.description; + $[69] = t1; + } else { + t1 = $[69]; + } + const t2 = task.status === "completed" ? "done" : undefined; + const t3 = task.status === "completed" && !task.notified ? ", unread" : undefined; + let t4; + if ($[70] !== t2 || $[71] !== t3 || $[72] !== task.status) { + t4 = ; + $[70] = t2; + $[71] = t3; + $[72] = task.status; + $[73] = t4; + } else { + t4 = $[73]; + } + let t5; + if ($[74] !== t1 || $[75] !== t4) { + t5 = {t1}{" "}{t4}; + $[74] = t1; + $[75] = t4; + $[76] = t5; + } else { + t5 = $[76]; + } + return t5; + } + case "dream": + { + const n = task.filesTouched.length; + let t1; + if ($[77] !== n || $[78] !== task.phase || $[79] !== task.sessionsReviewing) { + t1 = task.phase === "updating" && n > 0 ? `${n} ${plural(n, "file")}` : `${task.sessionsReviewing} ${plural(task.sessionsReviewing, "session")}`; + $[77] = n; + $[78] = task.phase; + $[79] = task.sessionsReviewing; + $[80] = t1; + } else { + t1 = $[80]; + } + const detail = t1; + let t2; + if ($[81] !== detail || $[82] !== task.phase) { + t2 = · {task.phase} · {detail}; + $[81] = detail; + $[82] = task.phase; + $[83] = t2; + } else { + t2 = $[83]; + } + const t3 = task.status === "completed" ? "done" : undefined; + const t4 = task.status === "completed" && !task.notified ? ", unread" : undefined; + let t5; + if ($[84] !== t3 || $[85] !== t4 || $[86] !== task.status) { + t5 = ; + $[84] = t3; + $[85] = t4; + $[86] = task.status; + $[87] = t5; + } else { + t5 = $[87]; + } + let t6; + if ($[88] !== t2 || $[89] !== t5 || $[90] !== task.description) { + t6 = {task.description}{" "}{t2}{" "}{t5}; + $[88] = t2; + $[89] = t5; + $[90] = task.description; + $[91] = t6; + } else { + t6 = $[91]; + } + return t6; + } + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJCYWNrZ3JvdW5kVGFza1N0YXRlIiwiRGVlcEltbXV0YWJsZSIsInRydW5jYXRlIiwidG9JbmtDb2xvciIsInBsdXJhbCIsIkRJQU1PTkRfRklMTEVEIiwiRElBTU9ORF9PUEVOIiwiUmVtb3RlU2Vzc2lvblByb2dyZXNzIiwiU2hlbGxQcm9ncmVzcyIsIlRhc2tTdGF0dXNUZXh0IiwiZGVzY3JpYmVUZWFtbWF0ZUFjdGl2aXR5IiwiUHJvcHMiLCJ0YXNrIiwibWF4QWN0aXZpdHlXaWR0aCIsIkJhY2tncm91bmRUYXNrIiwidDAiLCIkIiwiX2MiLCJhY3Rpdml0eUxpbWl0IiwidHlwZSIsInQxIiwia2luZCIsImRlc2NyaXB0aW9uIiwiY29tbWFuZCIsInQyIiwidDMiLCJ0NCIsImlzUmVtb3RlUmV2aWV3IiwicnVubmluZyIsInN0YXR1cyIsInRpdGxlIiwiU3ltYm9sIiwiZm9yIiwidDUiLCJ0NiIsInVuZGVmaW5lZCIsIm5vdGlmaWVkIiwiVDAiLCJUMSIsImFjdGl2aXR5IiwiaWRlbnRpdHkiLCJjb2xvciIsImFnZW50TmFtZSIsIndvcmtmbG93TmFtZSIsInN1bW1hcnkiLCJhZ2VudENvdW50IiwibiIsImZpbGVzVG91Y2hlZCIsImxlbmd0aCIsInBoYXNlIiwic2Vzc2lvbnNSZXZpZXdpbmciLCJkZXRhaWwiXSwic291cmNlcyI6WyJCYWNrZ3JvdW5kVGFzay50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnc3JjL2luay5qcydcbmltcG9ydCB0eXBlIHsgQmFja2dyb3VuZFRhc2tTdGF0ZSB9IGZyb20gJ3NyYy90YXNrcy90eXBlcy5qcydcbmltcG9ydCB0eXBlIHsgRGVlcEltbXV0YWJsZSB9IGZyb20gJ3NyYy90eXBlcy91dGlscy5qcydcbmltcG9ydCB7IHRydW5jYXRlIH0gZnJvbSAnc3JjL3V0aWxzL2Zvcm1hdC5qcydcbmltcG9ydCB7IHRvSW5rQ29sb3IgfSBmcm9tICdzcmMvdXRpbHMvaW5rLmpzJ1xuaW1wb3J0IHsgcGx1cmFsIH0gZnJvbSAnc3JjL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuaW1wb3J0IHsgRElBTU9ORF9GSUxMRUQsIERJQU1PTkRfT1BFTiB9IGZyb20gJy4uLy4uL2NvbnN0YW50cy9maWd1cmVzLmpzJ1xuaW1wb3J0IHsgUmVtb3RlU2Vzc2lvblByb2dyZXNzIH0gZnJvbSAnLi9SZW1vdGVTZXNzaW9uUHJvZ3Jlc3MuanMnXG5pbXBvcnQgeyBTaGVsbFByb2dyZXNzLCBUYXNrU3RhdHVzVGV4dCB9IGZyb20gJy4vU2hlbGxQcm9ncmVzcy5qcydcbmltcG9ydCB7IGRlc2NyaWJlVGVhbW1hdGVBY3Rpdml0eSB9IGZyb20gJy4vdGFza1N0YXR1c1V0aWxzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0YXNrOiBEZWVwSW1tdXRhYmxlPEJhY2tncm91bmRUYXNrU3RhdGU+XG4gIG1heEFjdGl2aXR5V2lkdGg/OiBudW1iZXJcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIEJhY2tncm91bmRUYXNrKHtcbiAgdGFzayxcbiAgbWF4QWN0aXZpdHlXaWR0aCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgYWN0aXZpdHlMaW1pdCA9IG1heEFjdGl2aXR5V2lkdGggPz8gNDBcbiAgc3dpdGNoICh0YXNrLnR5cGUpIHtcbiAgICBjYXNlICdsb2NhbF9iYXNoJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIHt0cnVuY2F0ZShcbiAgICAgICAgICAgIHRhc2sua2luZCA9PT0gJ21vbml0b3InID8gdGFzay5kZXNjcmlwdGlvbiA6IHRhc2suY29tbWFuZCxcbiAgICAgICAgICAgIGFjdGl2aXR5TGltaXQsXG4gICAgICAgICAgICB0cnVlLFxuICAgICAgICAgICl9eycgJ31cbiAgICAgICAgICA8U2hlbGxQcm9ncmVzcyBzaGVsbD17dGFza30gLz5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKVxuICAgIGNhc2UgJ3JlbW90ZV9hZ2VudCc6IHtcbiAgICAgIC8vIExpdGUtcmV2aWV3IHJlbmRlcnMgaXRzIG93biByYWluYm93IGxpbmUgKHRpdGxlICsgbGl2ZSBjb3VudHMpLFxuICAgICAgLy8gc28gd2UgZG9uJ3QgcHJlZml4IHRoZSB0aXRsZSDigJQgdGhlIHJhaW5ib3cgYWxyZWFkeSBpbmNsdWRlcyBpdC5cbiAgICAgIGlmICh0YXNrLmlzUmVtb3RlUmV2aWV3KSB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICA8UmVtb3RlU2Vzc2lvblByb2dyZXNzIHNlc3Npb249e3Rhc2t9IC8+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApXG4gICAgICB9XG4gICAgICBjb25zdCBydW5uaW5nID0gdGFzay5zdGF0dXMgPT09ICdydW5uaW5nJyB8fCB0YXNrLnN0YXR1cyA9PT0gJ3BlbmRpbmcnXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57cnVubmluZyA/IERJQU1PTkRfT1BFTiA6IERJQU1PTkRfRklMTEVEfSA8L1RleHQ+XG4gICAgICAgICAge3RydW5jYXRlKHRhc2sudGl0bGUsIGFjdGl2aXR5TGltaXQsIHRydWUpfVxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiDCtyA8L1RleHQ+XG4gICAgICAgICAgPFJlbW90ZVNlc3Npb25Qcm9ncmVzcyBzZXNzaW9uPXt0YXNrfSAvPlxuICAgICAgICA8L1RleHQ+XG4gICAgICApXG4gICAgfVxuICAgIGNhc2UgJ2xvY2FsX2FnZW50JzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIHt0cnVuY2F0ZSh0YXNrLmRlc2NyaXB0aW9uLCBhY3Rpdml0eUxpbWl0LCB0cnVlKX17JyAnfVxuICAgICAgICAgIDxUYXNrU3RhdHVzVGV4dFxuICAgICAgICAgICAgc3RhdHVzPXt0YXNrLnN0YXR1c31cbiAgICAgICAgICAgIGxhYmVsPXt0YXNrLnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcgPyAnZG9uZScgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICBzdWZmaXg9e1xuICAgICAgICAgICAgICB0YXNrLnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcgJiYgIXRhc2subm90aWZpZWRcbiAgICAgICAgICAgICAgICA/ICcsIHVucmVhZCdcbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgfVxuICAgICAgICAgIC8+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIClcbiAgICBjYXNlICdpbl9wcm9jZXNzX3RlYW1tYXRlJzoge1xuICAgICAgY29uc3QgYWN0aXZpdHkgPSBkZXNjcmliZVRlYW1tYXRlQWN0aXZpdHkodGFzaylcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPXt0b0lua0NvbG9yKHRhc2suaWRlbnRpdHkuY29sb3IpfT5cbiAgICAgICAgICAgIEB7dGFzay5pZGVudGl0eS5hZ2VudE5hbWV9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPjoge3RydW5jYXRlKGFjdGl2aXR5LCBhY3Rpdml0eUxpbWl0LCB0cnVlKX08L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIClcbiAgICB9XG4gICAgY2FzZSAnbG9jYWxfd29ya2Zsb3cnOlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAge3RydW5jYXRlKFxuICAgICAgICAgICAgdGFzay53b3JrZmxvd05hbWUgPz8gdGFzay5zdW1tYXJ5ID8/IHRhc2suZGVzY3JpcHRpb24sXG4gICAgICAgICAgICBhY3Rpdml0eUxpbWl0LFxuICAgICAgICAgICAgdHJ1ZSxcbiAgICAgICAgICApfXsnICd9XG4gICAgICAgICAgPFRhc2tTdGF0dXNUZXh0XG4gICAgICAgICAgICBzdGF0dXM9e3Rhc2suc3RhdHVzfVxuICAgICAgICAgICAgbGFiZWw9e1xuICAgICAgICAgICAgICB0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnXG4gICAgICAgICAgICAgICAgPyBgJHt0YXNrLmFnZW50Q291bnR9ICR7cGx1cmFsKHRhc2suYWdlbnRDb3VudCwgJ2FnZW50Jyl9YFxuICAgICAgICAgICAgICAgIDogdGFzay5zdGF0dXMgPT09ICdjb21wbGV0ZWQnXG4gICAgICAgICAgICAgICAgICA/ICdkb25lJ1xuICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHN1ZmZpeD17XG4gICAgICAgICAgICAgIHRhc2suc3RhdHVzID09PSAnY29tcGxldGVkJyAmJiAhdGFzay5ub3RpZmllZFxuICAgICAgICAgICAgICAgID8gJywgdW5yZWFkJ1xuICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICB9XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKVxuICAgIGNhc2UgJ21vbml0b3JfbWNwJzpcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIHt0cnVuY2F0ZSh0YXNrLmRlc2NyaXB0aW9uLCBhY3Rpdml0eUxpbWl0LCB0cnVlKX17JyAnfVxuICAgICAgICAgIDxUYXNrU3RhdHVzVGV4dFxuICAgICAgICAgICAgc3RhdHVzPXt0YXNrLnN0YXR1c31cbiAgICAgICAgICAgIGxhYmVsPXt0YXNrLnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcgPyAnZG9uZScgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICBzdWZmaXg9e1xuICAgICAgICAgICAgICB0YXNrLnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcgJiYgIXRhc2subm90aWZpZWRcbiAgICAgICAgICAgICAgICA/ICcsIHVucmVhZCdcbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgfVxuICAgICAgICAgIC8+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIClcbiAgICBjYXNlICdkcmVhbSc6IHtcbiAgICAgIGNvbnN0IG4gPSB0YXNrLmZpbGVzVG91Y2hlZC5sZW5ndGhcbiAgICAgIGNvbnN0IGRldGFpbCA9XG4gICAgICAgIHRhc2sucGhhc2UgPT09ICd1cGRhdGluZycgJiYgbiA+IDBcbiAgICAgICAgICA/IGAke259ICR7cGx1cmFsKG4sICdmaWxlJyl9YFxuICAgICAgICAgIDogYCR7dGFzay5zZXNzaW9uc1Jldmlld2luZ30gJHtwbHVyYWwodGFzay5zZXNzaW9uc1Jldmlld2luZywgJ3Nlc3Npb24nKX1gXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICB7dGFzay5kZXNjcmlwdGlvbn17JyAnfVxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgwrcge3Rhc2sucGhhc2V9IMK3IHtkZXRhaWx9XG4gICAgICAgICAgPC9UZXh0PnsnICd9XG4gICAgICAgICAgPFRhc2tTdGF0dXNUZXh0XG4gICAgICAgICAgICBzdGF0dXM9e3Rhc2suc3RhdHVzfVxuICAgICAgICAgICAgbGFiZWw9e3Rhc2suc3RhdHVzID09PSAnY29tcGxldGVkJyA/ICdkb25lJyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICAgIHN1ZmZpeD17XG4gICAgICAgICAgICAgIHRhc2suc3RhdHVzID09PSAnY29tcGxldGVkJyAmJiAhdGFzay5ub3RpZmllZFxuICAgICAgICAgICAgICAgID8gJywgdW5yZWFkJ1xuICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICB9XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgKVxuICAgIH1cbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxJQUFJLFFBQVEsWUFBWTtBQUNqQyxjQUFjQyxtQkFBbUIsUUFBUSxvQkFBb0I7QUFDN0QsY0FBY0MsYUFBYSxRQUFRLG9CQUFvQjtB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 \ No newline at end of file diff --git a/components/tasks/BackgroundTaskStatus.tsx b/components/tasks/BackgroundTaskStatus.tsx new file mode 100644 index 0000000..7476608 --- /dev/null +++ b/components/tasks/BackgroundTaskStatus.tsx @@ -0,0 +1,429 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import * as React from 'react'; +import { useMemo, useState } from 'react'; +import { useTerminalSize } from 'src/hooks/useTerminalSize.js'; +import { stringWidth } from 'src/ink/stringWidth.js'; +import { useAppState, useSetAppState } from 'src/state/AppState.js'; +import { enterTeammateView, exitTeammateView } from 'src/state/teammateViewHelpers.js'; +import { isPanelAgentTask } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'; +import { getPillLabel, pillNeedsCta } from 'src/tasks/pillLabel.js'; +import { type BackgroundTaskState, isBackgroundTask, type TaskState } from 'src/tasks/types.js'; +import { calculateHorizontalScrollWindow } from 'src/utils/horizontalScroll.js'; +import { Box, Text } from '../../ink.js'; +import { AGENT_COLOR_TO_THEME_COLOR, AGENT_COLORS, type AgentColorName } from '../../tools/AgentTool/agentColorManager.js'; +import type { Theme } from '../../utils/theme.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { shouldHideTasksFooter } from './taskStatusUtils.js'; +type Props = { + tasksSelected: boolean; + isViewingTeammate?: boolean; + teammateFooterIndex?: number; + isLeaderIdle?: boolean; + onOpenDialog?: (taskId?: string) => void; +}; +export function BackgroundTaskStatus(t0) { + const $ = _c(48); + const { + tasksSelected, + isViewingTeammate, + teammateFooterIndex: t1, + isLeaderIdle: t2, + onOpenDialog + } = t0; + const teammateFooterIndex = t1 === undefined ? 0 : t1; + const isLeaderIdle = t2 === undefined ? false : t2; + const setAppState = useSetAppState(); + const { + columns + } = useTerminalSize(); + const tasks = useAppState(_temp); + const viewingAgentTaskId = useAppState(_temp2); + let t3; + if ($[0] !== tasks) { + t3 = (Object.values(tasks ?? {}) as TaskState[]).filter(_temp3); + $[0] = tasks; + $[1] = t3; + } else { + t3 = $[1]; + } + const runningTasks = t3; + const expandedView = useAppState(_temp4); + const showSpinnerTree = expandedView === "teammates"; + const allTeammates = !showSpinnerTree && runningTasks.length > 0 && runningTasks.every(_temp5); + let t4; + if ($[2] !== runningTasks) { + t4 = runningTasks.filter(_temp6).sort(_temp7); + $[2] = runningTasks; + $[3] = t4; + } else { + t4 = $[3]; + } + const teammateEntries = t4; + let t5; + if ($[4] !== isLeaderIdle) { + t5 = { + name: "main", + color: undefined as keyof Theme | undefined, + isIdle: isLeaderIdle, + taskId: undefined as string | undefined + }; + $[4] = isLeaderIdle; + $[5] = t5; + } else { + t5 = $[5]; + } + const mainPill = t5; + let t6; + if ($[6] !== mainPill || $[7] !== tasksSelected || $[8] !== teammateEntries) { + const teammatePills = teammateEntries.map(_temp8); + if (!tasksSelected) { + teammatePills.sort(_temp9); + } + const pills = [mainPill, ...teammatePills]; + t6 = pills.map(_temp0); + $[6] = mainPill; + $[7] = tasksSelected; + $[8] = teammateEntries; + $[9] = t6; + } else { + t6 = $[9]; + } + const allPills = t6; + let t7; + if ($[10] !== allPills) { + t7 = allPills.map(_temp1); + $[10] = allPills; + $[11] = t7; + } else { + t7 = $[11]; + } + const pillWidths = t7; + if (allTeammates || !showSpinnerTree && isViewingTeammate) { + const selectedIdx = tasksSelected ? teammateFooterIndex : -1; + let t8; + if ($[12] !== teammateEntries || $[13] !== viewingAgentTaskId) { + t8 = viewingAgentTaskId ? teammateEntries.findIndex(t_3 => t_3.id === viewingAgentTaskId) + 1 : 0; + $[12] = teammateEntries; + $[13] = viewingAgentTaskId; + $[14] = t8; + } else { + t8 = $[14]; + } + const viewedIdx = t8; + const availableWidth = Math.max(20, columns - 20 - 4); + const t9 = selectedIdx >= 0 ? selectedIdx : 0; + let t10; + if ($[15] !== availableWidth || $[16] !== pillWidths || $[17] !== t9) { + t10 = calculateHorizontalScrollWindow(pillWidths, availableWidth, 2, t9); + $[15] = availableWidth; + $[16] = pillWidths; + $[17] = t9; + $[18] = t10; + } else { + t10 = $[18]; + } + const { + startIndex, + endIndex, + showLeftArrow, + showRightArrow + } = t10; + let t11; + if ($[19] !== allPills || $[20] !== endIndex || $[21] !== startIndex) { + t11 = allPills.slice(startIndex, endIndex); + $[19] = allPills; + $[20] = endIndex; + $[21] = startIndex; + $[22] = t11; + } else { + t11 = $[22]; + } + const visiblePills = t11; + let t12; + if ($[23] !== showLeftArrow) { + t12 = showLeftArrow && {figures.arrowLeft} ; + $[23] = showLeftArrow; + $[24] = t12; + } else { + t12 = $[24]; + } + let t13; + if ($[25] !== selectedIdx || $[26] !== setAppState || $[27] !== viewedIdx || $[28] !== visiblePills) { + t13 = visiblePills.map((pill_1, i_1) => { + const needsSeparator = i_1 > 0; + return {needsSeparator && } pill_1.taskId ? enterTeammateView(pill_1.taskId, setAppState) : exitTeammateView(setAppState)} />; + }); + $[25] = selectedIdx; + $[26] = setAppState; + $[27] = viewedIdx; + $[28] = visiblePills; + $[29] = t13; + } else { + t13 = $[29]; + } + let t14; + if ($[30] !== showRightArrow) { + t14 = showRightArrow && {figures.arrowRight}; + $[30] = showRightArrow; + $[31] = t14; + } else { + t14 = $[31]; + } + let t15; + if ($[32] === Symbol.for("react.memo_cache_sentinel")) { + t15 = {" \xB7 "}; + $[32] = t15; + } else { + t15 = $[32]; + } + let t16; + if ($[33] !== t12 || $[34] !== t13 || $[35] !== t14) { + t16 = <>{t12}{t13}{t14}{t15}; + $[33] = t12; + $[34] = t13; + $[35] = t14; + $[36] = t16; + } else { + t16 = $[36]; + } + return t16; + } + if (shouldHideTasksFooter(tasks ?? {}, showSpinnerTree)) { + return null; + } + if (runningTasks.length === 0) { + return null; + } + let t8; + if ($[37] !== runningTasks) { + t8 = getPillLabel(runningTasks); + $[37] = runningTasks; + $[38] = t8; + } else { + t8 = $[38]; + } + let t9; + if ($[39] !== onOpenDialog || $[40] !== t8 || $[41] !== tasksSelected) { + t9 = {t8}; + $[39] = onOpenDialog; + $[40] = t8; + $[41] = tasksSelected; + $[42] = t9; + } else { + t9 = $[42]; + } + let t10; + if ($[43] !== runningTasks) { + t10 = pillNeedsCta(runningTasks) && · {figures.arrowDown} to view; + $[43] = runningTasks; + $[44] = t10; + } else { + t10 = $[44]; + } + let t11; + if ($[45] !== t10 || $[46] !== t9) { + t11 = <>{t9}{t10}; + $[45] = t10; + $[46] = t9; + $[47] = t11; + } else { + t11 = $[47]; + } + return t11; +} +function _temp1(pill_0, i_0) { + const pillText = `@${pill_0.name}`; + return stringWidth(pillText) + (i_0 > 0 ? 1 : 0); +} +function _temp0(pill, i) { + return { + ...pill, + idx: i + }; +} +function _temp9(a_0, b_0) { + if (a_0.isIdle !== b_0.isIdle) { + return a_0.isIdle ? 1 : -1; + } + return 0; +} +function _temp8(t_2) { + return { + name: t_2.identity.agentName, + color: getAgentThemeColor(t_2.identity.color), + isIdle: t_2.isIdle, + taskId: t_2.id + }; +} +function _temp7(a, b) { + return a.identity.agentName.localeCompare(b.identity.agentName); +} +function _temp6(t_1) { + return t_1.type === "in_process_teammate"; +} +function _temp5(t_0) { + return t_0.type === "in_process_teammate"; +} +function _temp4(s_1) { + return s_1.expandedView; +} +function _temp3(t) { + return isBackgroundTask(t) && !(false && isPanelAgentTask(t)); +} +function _temp2(s_0) { + return s_0.viewingAgentTaskId; +} +function _temp(s) { + return s.tasks; +} +type AgentPillProps = { + name: string; + color?: keyof Theme; + isSelected: boolean; + isViewed: boolean; + isIdle: boolean; + onClick?: () => void; +}; +function AgentPill(t0) { + const $ = _c(19); + const { + name, + color, + isSelected, + isViewed, + isIdle, + onClick + } = t0; + const [hover, setHover] = useState(false); + const highlighted = isSelected || hover; + let label; + if (highlighted) { + let t1; + if ($[0] !== color || $[1] !== isViewed || $[2] !== name) { + t1 = color ? @{name} : @{name}; + $[0] = color; + $[1] = isViewed; + $[2] = name; + $[3] = t1; + } else { + t1 = $[3]; + } + label = t1; + } else { + if (isIdle) { + let t1; + if ($[4] !== isViewed || $[5] !== name) { + t1 = @{name}; + $[4] = isViewed; + $[5] = name; + $[6] = t1; + } else { + t1 = $[6]; + } + label = t1; + } else { + if (isViewed) { + let t1; + if ($[7] !== color || $[8] !== name) { + t1 = @{name}; + $[7] = color; + $[8] = name; + $[9] = t1; + } else { + t1 = $[9]; + } + label = t1; + } else { + const t1 = !color; + let t2; + if ($[10] !== color || $[11] !== name || $[12] !== t1) { + t2 = @{name}; + $[10] = color; + $[11] = name; + $[12] = t1; + $[13] = t2; + } else { + t2 = $[13]; + } + label = t2; + } + } + } + if (!onClick) { + return label; + } + let t1; + let t2; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => setHover(true); + t2 = () => setHover(false); + $[14] = t1; + $[15] = t2; + } else { + t1 = $[14]; + t2 = $[15]; + } + let t3; + if ($[16] !== label || $[17] !== onClick) { + t3 = {label}; + $[16] = label; + $[17] = onClick; + $[18] = t3; + } else { + t3 = $[18]; + } + return t3; +} +function SummaryPill(t0) { + const $ = _c(8); + const { + selected, + onClick, + children + } = t0; + const [hover, setHover] = useState(false); + const t1 = selected || hover; + let t2; + if ($[0] !== children || $[1] !== t1) { + t2 = {children}; + $[0] = children; + $[1] = t1; + $[2] = t2; + } else { + t2 = $[2]; + } + const label = t2; + if (!onClick) { + return label; + } + let t3; + let t4; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = () => setHover(true); + t4 = () => setHover(false); + $[3] = t3; + $[4] = t4; + } else { + t3 = $[3]; + t4 = $[4]; + } + let t5; + if ($[5] !== label || $[6] !== onClick) { + t5 = {label}; + $[5] = label; + $[6] = onClick; + $[7] = t5; + } else { + t5 = $[7]; + } + return t5; +} +function getAgentThemeColor(colorName: string | undefined): keyof Theme | undefined { + if (!colorName) return undefined; + if (AGENT_COLORS.includes(colorName as AgentColorName)) { + return AGENT_COLOR_TO_THEME_COLOR[colorName as AgentColorName]; + } + return undefined; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VNZW1vIiwidXNlU3RhdGUiLCJ1c2VUZXJtaW5hbFNpemUiLCJzdHJpbmdXaWR0aCIsInVzZUFwcFN0YXRlIiwidXNlU2V0QXBwU3RhdGUiLCJlbnRlclRlYW1tYXRlVmlldyIsImV4aXRUZWFtbWF0ZVZpZXciLCJpc1BhbmVsQWdlbnRUYXNrIiwiZ2V0UGlsbExhYmVsIiwicGlsbE5lZWRzQ3RhIiwiQmFja2dyb3VuZFRhc2tTdGF0ZSIsImlzQmFja2dyb3VuZFRhc2siLCJUYXNrU3RhdGUiLCJjYWxjdWxhdGVIb3Jpem9udGFsU2Nyb2xsV2luZG93IiwiQm94IiwiVGV4dCIsIkFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SIiwiQUdFTlRfQ09MT1JTIiwiQWdlbnRDb2xvck5hbWUiLCJUaGVtZSIsIktleWJvYXJkU2hvcnRjdXRIaW50Iiwic2hvdWxkSGlkZVRhc2tzRm9vdGVyIiwiUHJvcHMiLCJ0YXNrc1NlbGVjdGVkIiwiaXNWaWV3aW5nVGVhbW1hdGUiLCJ0ZWFtbWF0ZUZvb3RlckluZGV4IiwiaXNMZWFkZXJJZGxlIiwib25PcGVuRGlhbG9nIiwidGFza0lkIiwiQmFja2dyb3VuZFRhc2tTdGF0dXMiLCJ0MCIsIiQiLCJfYyIsInQxIiwidDIiLCJ1bmRlZmluZWQiLCJzZXRBcHBTdGF0ZSIsImNvbHVtbnMiLCJ0YXNrcyIsIl90ZW1wIiwidmlld2luZ0FnZW50VGFza0lkIiwiX3RlbXAyIiwidDMiLCJPYmplY3QiLCJ2YWx1ZXMiLCJmaWx0ZXIiLCJfdGVtcDMiLCJydW5uaW5nVGFza3MiLCJleHBhbmRlZFZpZXciLCJfdGVtcDQiLCJzaG93U3Bpbm5lclRyZWUiLCJhbGxUZWFtbWF0ZXMiLCJsZW5ndGgiLCJldmVyeSIsIl90ZW1wNSIsInQ0IiwiX3RlbXA2Iiwic29ydCIsIl90ZW1wNyIsInRlYW1tYXRlRW50cmllcyIsInQ1IiwibmFtZSIsImNvbG9yIiwiaXNJZGxlIiwibWFpblBpbGwiLCJ0NiIsInRlYW1tYXRlUGlsbHMiLCJtYXAiLCJfdGVtcDgiLCJfdGVtcDkiLCJwaWxscyIsIl90ZW1wMCIsImFsbFBpbGxzIiwidDciLCJfdGVtcDEiLCJwaWxsV2lkdGhzIiwic2VsZWN0ZWRJZHgiLCJ0OCIsImZpbmRJbmRleCIsInRfMyIsInQiLCJpZCIsInZpZXdlZElkeCIsImF2YWlsYWJsZVdpZHRoIiwiTWF0aCIsIm1heCIsInQ5IiwidDEwIiwic3RhcnRJbmRleCIsImVuZEluZGV4Iiwic2hvd0xlZnRBcnJvdyIsInNob3dSaWdodEFycm93IiwidDExIiwic2xpY2UiLCJ2aXNpYmxlUGlsbHMiLCJ0MTIiLCJhcnJvd0xlZnQiLCJ0MTMiLCJwaWxsXzEiLCJpXzEiLCJuZWVkc1NlcGFyYXRvciIsImkiLCJwaWxsIiwiaWR4IiwidDE0IiwiYXJyb3dSaWdodCIsInQxNSIsIlN5bWJvbCIsImZvciIsInQxNiIsImFycm93RG93biIsInBpbGxfMCIsImlfMCIsInBpbGxUZXh0IiwiYV8wIiwiYl8wIiwiYSIsImIiLCJ0XzIiLCJpZGVudGl0eSIsImFnZW50TmFtZSIsImdldEFnZW50VGhlbWVDb2xvciIsImxvY2FsZUNvbXBhcmUiLCJ0XzEiLCJ0eXBlIiwidF8wIiwic18xIiwicyIsInNfMCIsIkFnZW50UGlsbFByb3BzIiwiaXNTZWxlY3RlZCIsImlzVmlld2VkIiwib25DbGljayIsIkFnZW50UGlsbCIsImhvdmVyIiwic2V0SG92ZXIiLCJoaWdobGlnaHRlZCIsImxhYmVsIiwiU3VtbWFyeVBpbGwiLCJzZWxlY3RlZCIsImNoaWxkcmVuIiwiY29sb3JOYW1lIiwiaW5jbHVkZXMiXSwic291cmNlcyI6WyJCYWNrZ3JvdW5kVGFza1N0YXR1cy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTWVtbywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZVRlcm1pbmFsU2l6ZSB9IGZyb20gJ3NyYy9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyBzdHJpbmdXaWR0aCB9IGZyb20gJ3NyYy9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSwgdXNlU2V0QXBwU3RhdGUgfSBmcm9tICdzcmMvc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQge1xuICBlbnRlclRlYW1tYXRlVmlldyxcbiAgZXhpdFRlYW1tYXRlVmlldyxcbn0gZnJvbSAnc3JjL3N0YXRlL3RlYW1tYXRlVmlld0hlbHBlcnMuanMnXG5pbXBvcnQgeyBpc1BhbmVsQWdlbnRUYXNrIH0gZnJvbSAnc3JjL3Rhc2tzL0xvY2FsQWdlbnRUYXNrL0xvY2FsQWdlbnRUYXNrLmpzJ1xuaW1wb3J0IHsgZ2V0UGlsbExhYmVsLCBwaWxsTmVlZHNDdGEgfSBmcm9tICdzcmMvdGFza3MvcGlsbExhYmVsLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBCYWNrZ3JvdW5kVGFza1N0YXRlLFxuICBpc0JhY2tncm91bmRUYXNrLFxuICB0eXBlIFRhc2tTdGF0ZSxcbn0gZnJvbSAnc3JjL3Rhc2tzL3R5cGVzLmpzJ1xuaW1wb3J0IHsgY2FsY3VsYXRlSG9yaXpvbnRhbFNjcm9sbFdpbmRvdyB9IGZyb20gJ3NyYy91dGlscy9ob3Jpem9udGFsU2Nyb2xsLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHtcbiAgQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1IsXG4gIEFHRU5UX0NPTE9SUyxcbiAgdHlwZSBBZ2VudENvbG9yTmFtZSxcbn0gZnJvbSAnLi4vLi4vdG9vbHMvQWdlbnRUb29sL2FnZW50Q29sb3JNYW5hZ2VyLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJy4uLy4uL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgc2hvdWxkSGlkZVRhc2tzRm9vdGVyIH0gZnJvbSAnLi90YXNrU3RhdHVzVXRpbHMuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHRhc2tzU2VsZWN0ZWQ6IGJvb2xlYW5cbiAgaXNWaWV3aW5nVGVhbW1hdGU/OiBib29sZWFuXG4gIHRlYW1tYXRlRm9vdGVySW5kZXg/OiBudW1iZXJcbiAgaXNMZWFkZXJJZGxlPzogYm9vbGVhblxuICBvbk9wZW5EaWFsb2c/OiAodGFza0lkPzogc3RyaW5nKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBCYWNrZ3JvdW5kVGFza1N0YXR1cyh7XG4gIHRhc2tzU2VsZWN0ZWQsXG4gIGlzVmlld2luZ1RlYW1tYXRlLFxuICB0ZWFtbWF0ZUZvb3RlckluZGV4ID0gMCxcbiAgaXNMZWFkZXJJZGxlID0gZmFsc2UsXG4gIG9uT3BlbkRpYWxvZyxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IHsgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcbiAgY29uc3QgdGFza3MgPSB1c2VBcHBTdGF0ZShzID0+IHMudGFza3MpXG4gIGNvbnN0IHZpZXdpbmdBZ2VudFRhc2tJZCA9IHVzZUFwcFN0YXRlKHMgPT4gcy52aWV3aW5nQWdlbnRUYXNrSWQpXG5cbiAgY29uc3QgcnVubmluZ1Rhc2tzID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgKE9iamVjdC52YWx1ZXModGFza3MgPz8ge30pIGFzIFRhc2tTdGF0ZVtdKS5maWx0ZXIoXG4gICAgICAgIHQgPT5cbiAgICAgICAgICBpc0JhY2tncm91bmRUYXNrKHQpICYmXG4gICAgICAgICAgIShcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIGlzUGFuZWxBZ2VudFRhc2sodCkpLFxuICAgICAgKSxcbiAgICBbdGFza3NdLFxuICApXG5cbiAgLy8gQ2hlY2sgaWYgYWxsIHRhc2tzIGFyZSBpbi1wcm9jZXNzIHRlYW1tYXRlcyAodGVhbSBtb2RlKVxuICAvLyBJbiBzcGlubmVyLXRyZWUgbW9kZSwgZG9uJ3Qgc2hvdyB0ZWFtbWF0ZSBwaWxscyAodGVhbW1hdGVzIGFwcGVhciBpbiB0aGUgc3Bpbm5lciB0cmVlKVxuICBjb25zdCBleHBhbmRlZFZpZXcgPSB1c2VBcHBTdGF0ZShzID0+IHMuZXhwYW5kZWRWaWV3KVxuICBjb25zdCBzaG93U3Bpbm5lclRyZWUgPSBleHBhbmRlZFZpZXcgPT09ICd0ZWFtbWF0ZXMnXG4gIGNvbnN0IGFsbFRlYW1tYXRlcyA9XG4gICAgIXNob3dTcGlubmVyVHJlZSAmJlxuICAgIHJ1bm5pbmdUYXNrcy5sZW5ndGggPiAwICYmXG4gICAgcnVubmluZ1Rhc2tzLmV2ZXJ5KHQgPT4gdC50eXBlID09PSAnaW5fcHJvY2Vzc190ZWFtbWF0ZScpXG5cbiAgLy8gTWVtb2l6ZSB0ZWFtbWF0ZS1yZWxhdGVkIGNvbXB1dGF0aW9ucyBhdCB0aGUgdG9wIGxldmVsIChydWxlcyBvZiBob29rcylcbiAgY29uc3QgdGVhbW1hdGVFbnRyaWVzID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgcnVubmluZ1Rhc2tzXG4gICAgICAgIC5maWx0ZXIoXG4gICAgICAgICAgKHQpOiB0IGlzIEJhY2tncm91bmRUYXNrU3RhdGUgJiB7IHR5cGU6ICdpbl9wcm9jZXNzX3RlYW1tYXRlJyB9ID0+XG4gICAgICAgICAgICB0LnR5cGUgPT09ICdpbl9wcm9jZXNzX3RlYW1tYXRlJyxcbiAgICAgICAgKVxuICAgICAgICAuc29ydCgoYSwgYikgPT5cbiAgICAgICAgICBhLmlkZW50aXR5LmFnZW50TmFtZS5sb2NhbGVDb21wYXJlKGIuaWRlbnRpdHkuYWdlbnROYW1lKSxcbiAgICAgICAgKSxcbiAgICBbcnVubmluZ1Rhc2tzXSxcbiAgKVxuXG4gIC8vIEJ1aWxkIGFycmF5IG9mIGFsbCBwaWxscyB3aXRoIHRoZWlyIGFjdGl2aXR5IHN0YXRlXG4gIC8vIEVhY2ggcGlsbCBpcyBcIkB7bmFtZX1cIiBhbmQgc2VwYXJhdG9yIGlzIFwiIFwiICgxIGNoYXIpXG4gIC8vIFNvcnQgaWRsZSBhZ2VudHMgdG8gdGhlIGVuZCwgYnV0IG9ubHkgd2hlbiBub3QgaW4gc2VsZWN0aW9uIG1vZGVcbiAgLy8gdG8gYXZvaWQgcmVvcmRlcmluZyB3aGlsZSB1c2VyIGlzIGFycm93aW5nIHRocm91Z2ggdGhlIGxpc3RcbiAgLy8gXCJtYWluXCIgYWx3YXlzIHN0YXlzIGZpcnN0IHJlZ2FyZGxlc3Mgb2YgaWRsZSBzdGF0ZVxuICBjb25zdCBhbGxQaWxscyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGNvbnN0IG1haW5QaWxsID0ge1xuICAgICAgbmFtZTogJ21haW4nLFxuICAgICAgY29sb3I6IHVuZGVmaW5lZCBhcyBrZXlvZiBUaGVtZSB8IHVuZGVmaW5lZCxcbiAgICAgIGlzSWRsZTogaXNMZWFkZXJJZGxlLFxuICAgICAgdGFza0lkOiB1bmRlZmluZWQgYXMgc3RyaW5nIHwgdW5kZWZpbmVkLFxuICAgIH1cblxuICAgIGNvbnN0IHRlYW1tYXRlUGlsbHMgPSB0ZWFtbWF0ZUVudHJpZXMubWFwKHQgPT4gKHtcbiAgICAgIG5hbWU6IHQuaWRlbnRpdHkuYWdlbnROYW1lLFxuICAgICAgY29sb3I6IGdldEFnZW50VGhlbWVDb2xvcih0LmlkZW50aXR5LmNvbG9yKSxcbiAgICAgIGlzSWRsZTogdC5pc0lkbGUsXG4gICAgICB0YXNrSWQ6IHQuaWQsXG4gICAgfSkpXG5cbiAgICAvLyBPbmx5IHNvcnQgdGVhbW1hdGVzIHdoZW4gbm90IHNlbGVjdGluZyB0byBhdm9pZCByZW9yZGVyaW5nIGR1cmluZyBuYXZpZ2F0aW9uXG4gICAgaWYgKCF0YXNrc1NlbGVjdGVkKSB7XG4gICAgICB0ZWFtbWF0ZVBpbGxzLnNvcnQoKGEsIGIpID0+IHtcbiAgICAgICAgLy8gQWN0aXZlIGFnZW50cyBmaXJzdCwgaWRsZSBhZ2VudHMgbGFzdFxuICAgICAgICBpZiAoYS5pc0lkbGUgIT09IGIuaXNJZGxlKSByZXR1cm4gYS5pc0lkbGUgPyAxIDogLTFcbiAgICAgICAgcmV0dXJuIDAgLy8gS2VlcCBvcmlnaW5hbCBvcmRlciB3aXRoaW4gZWFjaCBncm91cFxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBtYWluIGFsd2F5cyBmaXJzdCwgdGhlbiBzb3J0ZWQgdGVhbW1hdGVzXG4gICAgY29uc3QgcGlsbHMgPSBbbWFpblBpbGwsIC4uLnRlYW1tYXRlUGlsbHNdXG5cbiAgICAvLyBBZGQgaWR4IGFmdGVyIHNvcnRpbmdcbiAgICByZXR1cm4gcGlsbHMubWFwKChwaWxsLCBpKSA9PiAoeyAuLi5waWxsLCBpZHg6IGkgfSkpXG4gIH0sIFt0ZWFtbWF0ZUVudHJpZXMsIGlzTGVhZGVySWRsZSwgdGFza3NTZWxlY3RlZF0pXG5cbiAgLy8gQ2FsY3VsYXRlIHBpbGwgd2lkdGhzIChpbmNsdWRpbmcgc2VwYXJhdG9yIHNwYWNlLCBleGNlcHQgZmlyc3QpXG4gIGNvbnN0IHBpbGxXaWR0aHMgPSB1c2VNZW1vKFxuICAgICgpID0+XG4gICAgICBhbGxQaWxscy5tYXAoKHBpbGwsIGkpID0+IHtcbiAgICAgICAgY29uc3QgcGlsbFRleHQgPSBgQCR7cGlsbC5uYW1lfWBcbiAgICAgICAgLy8gRmlyc3QgcGlsbCBoYXMgbm8gbGVhZGluZyBzcGFjZSwgb3RoZXJzIGhhdmUgMSBzcGFjZSBzZXBhcmF0b3JcbiAgICAgICAgcmV0dXJuIHN0cmluZ1dpZHRoKHBpbGxUZXh0KSArIChpID4gMCA/IDEgOiAwKVxuICAgICAgfSksXG4gICAgW2FsbFBpbGxzXSxcbiAgKVxuXG4gIGlmIChhbGxUZWFtbWF0ZXMgfHwgKCFzaG93U3Bpbm5lclRyZWUgJiYgaXNWaWV3aW5nVGVhbW1hdGUpKSB7XG4gICAgY29uc3Qgc2VsZWN0ZWRJZHggPSB0YXNrc1NlbGVjdGVkID8gdGVhbW1hdGVGb290ZXJJbmRleCA6IC0xXG4gICAgLy8gV2hpY2ggYWdlbnQgaXMgY3VycmVudGx5IGZvcmVncm91bmRlZCAoYm9sZClcbiAgICBjb25zdCB2aWV3ZWRJZHggPSB2aWV3aW5nQWdlbnRUYXNrSWRcbiAgICAgID8gdGVhbW1hdGVFbnRyaWVzLmZpbmRJbmRleCh0ID0+IHQuaWQgPT09IHZpZXdpbmdBZ2VudFRhc2tJZCkgKyAxXG4gICAgICA6IDAgLy8gMCA9IG1haW4vbGVhZGVyXG5cbiAgICAvLyBDYWxjdWxhdGUgYXZhaWxhYmxlIHdpZHRoIGZvciBwaWxsc1xuICAgIC8vIFJlc2VydmUgc3BhY2UgZm9yOiBhcnJvd3MsIGhpbnQsIGFuZCBtaW5pbWFsIHBhZGRpbmdcbiAgICAvLyBQaWxscyBhcmUgcmVuZGVyZWQgb24gdGhlaXIgb3duIGxpbmUgd2hlbiBpbiB0ZWFtIG1vZGVcbiAgICBjb25zdCBBUlJPV19XSURUSCA9IDIgLy8gYXJyb3cgY2hhciArIHNwYWNlXG4gICAgY29uc3QgSElOVF9XSURUSCA9IDIwIC8vIHNoaWZ0K+KGkyB0byBleHBhbmRcbiAgICBjb25zdCBQQURESU5HID0gNCAvLyBtaW5pbWFsIHNhZmV0eSBtYXJnaW5cbiAgICBjb25zdCBhdmFpbGFibGVXaWR0aCA9IE1hdGgubWF4KDIwLCBjb2x1bW5zIC0gSElOVF9XSURUSCAtIFBBRERJTkcpXG5cbiAgICAvLyBDYWxjdWxhdGUgdmlzaWJsZSB3aW5kb3cgb2YgcGlsbHNcbiAgICBjb25zdCB7IHN0YXJ0SW5kZXgsIGVuZEluZGV4LCBzaG93TGVmdEFycm93LCBzaG93UmlnaHRBcnJvdyB9ID1cbiAgICAgIGNhbGN1bGF0ZUhvcml6b250YWxTY3JvbGxXaW5kb3coXG4gICAgICAgIHBpbGxXaWR0aHMsXG4gICAgICAgIGF2YWlsYWJsZVdpZHRoLFxuICAgICAgICBBUlJPV19XSURUSCxcbiAgICAgICAgc2VsZWN0ZWRJZHggPj0gMCA/IHNlbGVjdGVkSWR4IDogMCxcbiAgICAgIClcblxuICAgIGNvbnN0IHZpc2libGVQaWxscyA9IGFsbFBpbGxzLnNsaWNlKHN0YXJ0SW5kZXgsIGVuZEluZGV4KVxuXG4gICAgcmV0dXJuIChcbiAgICAgIDw+XG4gICAgICAgIHtzaG93TGVmdEFycm93ICYmIDxUZXh0IGRpbUNvbG9yPntmaWd1cmVzLmFycm93TGVmdH0gPC9UZXh0Pn1cbiAgICAgICAge3Zpc2libGVQaWxscy5tYXAoKHBpbGwsIGkpID0+IHtcbiAgICAgICAgICAvLyBGaXJzdCB2aXNpYmxlIHBpbGwgaGFzIG5vIGxlYWRpbmcgc2VwYXJhdG9yXG4gICAgICAgICAgLy8gKGxlZnQgYXJyb3cgYWxyZWFkeSBwcm92aWRlcyBzcGFjaW5nIGlmIHByZXNlbnQpXG4gICAgICAgICAgY29uc3QgbmVlZHNTZXBhcmF0b3IgPSBpID4gMFxuICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8UmVhY3QuRnJhZ21lbnQga2V5PXtwaWxsLm5hbWV9PlxuICAgICAgICAgICAgICB7bmVlZHNTZXBhcmF0b3IgJiYgPFRleHQ+IDwvVGV4dD59XG4gICAgICAgICAgICAgIDxBZ2VudFBpbGxcbiAgICAgICAgICAgICAgICBuYW1lPXtwaWxsLm5hbWV9XG4gICAgICAgICAgICAgICAgY29sb3I9e3BpbGwuY29sb3J9XG4gICAgICAgICAgICAgICAgaXNTZWxlY3RlZD17c2VsZWN0ZWRJZHggPT09IHBpbGwuaWR4fVxuICAgICAgICAgICAgICAgIGlzVmlld2VkPXt2aWV3ZWRJZHggPT09IHBpbGwuaWR4fVxuICAgICAgICAgICAgICAgIGlzSWRsZT17cGlsbC5pc0lkbGV9XG4gICAgICAgICAgICAgICAgb25DbGljaz17KCkgPT5cbiAgICAgICAgICAgICAgICAgIHBpbGwudGFza0lkXG4gICAgICAgICAgICAgICAgICAgID8gZW50ZXJUZWFtbWF0ZVZpZXcocGlsbC50YXNrSWQsIHNldEFwcFN0YXRlKVxuICAgICAgICAgICAgICAgICAgICA6IGV4aXRUZWFtbWF0ZVZpZXcoc2V0QXBwU3RhdGUpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgPC9SZWFjdC5GcmFnbWVudD5cbiAgICAgICAgICApXG4gICAgICAgIH0pfVxuICAgICAgICB7c2hvd1JpZ2h0QXJyb3cgJiYgPFRleHQgZGltQ29sb3I+IHtmaWd1cmVzLmFycm93UmlnaHR9PC9UZXh0Pn1cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgeycgwrcgJ31cbiAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJzaGlmdCArIOKGk1wiIGFjdGlvbj1cImV4cGFuZFwiIC8+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvPlxuICAgIClcbiAgfVxuXG4gIC8vIEluIHNwaW5uZXItdHJlZSBtb2RlLCBkb24ndCBzaG93IGFueSBmb290ZXIgc3RhdHVzIGZvciB0ZWFtbWF0ZXNcbiAgLy8gKHRoZXkgYXBwZWFyIGluIHRoZSBzcGlubmVyIHRyZWUgYWJvdmUpXG4gIGlmIChzaG91bGRIaWRlVGFza3NGb290ZXIodGFza3MgPz8ge30sIHNob3dTcGlubmVyVHJlZSkpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKHJ1bm5pbmdUYXNrcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPFN1bW1hcnlQaWxsIHNlbGVjdGVkPXt0YXNrc1NlbGVjdGVkfSBvbkNsaWNrPXtvbk9wZW5EaWFsb2d9PlxuICAgICAgICB7Z2V0UGlsbExhYmVsKHJ1bm5pbmdUYXNrcyl9XG4gICAgICA8L1N1bW1hcnlQaWxsPlxuICAgICAge3BpbGxOZWVkc0N0YShydW5uaW5nVGFza3MpICYmIChcbiAgICAgICAgPFRleHQgZGltQ29sb3I+IMK3IHtmaWd1cmVzLmFycm93RG93bn0gdG8gdmlldzwvVGV4dD5cbiAgICAgICl9XG4gICAgPC8+XG4gIClcbn1cblxudHlwZSBBZ2VudFBpbGxQcm9wcyA9IHtcbiAgbmFtZTogc3RyaW5nXG4gIGNvbG9yPzoga2V5b2YgVGhlbWVcbiAgaXNTZWxlY3RlZDogYm9vbGVhblxuICBpc1ZpZXdlZDogYm9vbGVhblxuICBpc0lkbGU6IGJvb2xlYW5cbiAgb25DbGljaz86ICgpID0+IHZvaWRcbn1cblxuZnVuY3Rpb24gQWdlbnRQaWxsKHtcbiAgbmFtZSxcbiAgY29sb3IsXG4gIGlzU2VsZWN0ZWQsXG4gIGlzVmlld2VkLFxuICBpc0lkbGUsXG4gIG9uQ2xpY2ssXG59OiBBZ2VudFBpbGxQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtob3Zlciwgc2V0SG92ZXJdID0gdXNlU3RhdGUoZmFsc2UpXG4gIC8vIEhvdmVyIG1pcnJvcnMgdGhlIGtleWJvYXJkLXNlbGVjdGVkIGxvb2sgc28gdGhlIGFmZm9yZGFuY2UgaXMgZmFtaWxpYXIuXG4gIGNvbnN0IGhpZ2hsaWdodGVkID0gaXNTZWxlY3RlZCB8fCBob3ZlclxuXG4gIGxldCBsYWJlbDogUmVhY3QuUmVhY3ROb2RlXG4gIGlmIChoaWdobGlnaHRlZCkge1xuICAgIGxhYmVsID0gY29sb3IgPyAoXG4gICAgICA8VGV4dCBiYWNrZ3JvdW5kQ29sb3I9e2NvbG9yfSBjb2xvcj1cImludmVyc2VUZXh0XCIgYm9sZD17aXNWaWV3ZWR9PlxuICAgICAgICBAe25hbWV9XG4gICAgICA8L1RleHQ+XG4gICAgKSA6IChcbiAgICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiIGludmVyc2UgYm9sZD17aXNWaWV3ZWR9PlxuICAgICAgICBAe25hbWV9XG4gICAgICA8L1RleHQ+XG4gICAgKVxuICB9IGVsc2UgaWYgKGlzSWRsZSkge1xuICAgIGxhYmVsID0gKFxuICAgICAgPFRleHQgZGltQ29sb3IgYm9sZD17aXNWaWV3ZWR9PlxuICAgICAgICBAe25hbWV9XG4gICAgICA8L1RleHQ+XG4gICAgKVxuICB9IGVsc2UgaWYgKGlzVmlld2VkKSB7XG4gICAgbGFiZWwgPSAoXG4gICAgICA8VGV4dCBjb2xvcj17Y29sb3J9IGJvbGQ+XG4gICAgICAgIEB7bmFtZX1cbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH0gZWxzZSB7XG4gICAgbGFiZWwgPSAoXG4gICAgICA8VGV4dCBjb2xvcj17Y29sb3J9IGRpbUNvbG9yPXshY29sb3J9PlxuICAgICAgICBAe25hbWV9XG4gICAgICA8L1RleHQ+XG4gICAgKVxuICB9XG5cbiAgaWYgKCFvbkNsaWNrKSByZXR1cm4gbGFiZWxcbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBvbkNsaWNrPXtvbkNsaWNrfVxuICAgICAgb25Nb3VzZUVudGVyPXsoKSA9PiBzZXRIb3Zlcih0cnVlKX1cbiAgICAgIG9uTW91c2VMZWF2ZT17KCkgPT4gc2V0SG92ZXIoZmFsc2UpfVxuICAgID5cbiAgICAgIHtsYWJlbH1cbiAgICA8L0JveD5cbiAgKVxufVxuXG5mdW5jdGlvbiBTdW1tYXJ5UGlsbCh7XG4gIHNlbGVjdGVkLFxuICBvbkNsaWNrLFxuICBjaGlsZHJlbixcbn06IHtcbiAgc2VsZWN0ZWQ6IGJvb2xlYW5cbiAgb25DbGljaz86ICgpID0+IHZvaWRcbiAgY2hpbGRyZW46IFJlYWN0LlJlYWN0Tm9kZVxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtob3Zlciwgc2V0SG92ZXJdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IGxhYmVsID0gKFxuICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiIGludmVyc2U9e3NlbGVjdGVkIHx8IGhvdmVyfT5cbiAgICAgIHtjaGlsZHJlbn1cbiAgICA8L1RleHQ+XG4gIClcbiAgaWYgKCFvbkNsaWNrKSByZXR1cm4gbGFiZWxcbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBvbkNsaWNrPXtvbkNsaWNrfVxuICAgICAgb25Nb3VzZUVudGVyPXsoKSA9PiBzZXRIb3Zlcih0cnVlKX1cbiAgICAgIG9uTW91c2VMZWF2ZT17KCkgPT4gc2V0SG92ZXIoZmFsc2UpfVxuICAgID5cbiAgICAgIHtsYWJlbH1cbiAgICA8L0JveD5cbiAgKVxufVxuXG5mdW5jdGlvbiBnZXRBZ2VudFRoZW1lQ29sb3IoXG4gIGNvbG9yTmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkLFxuKToga2V5b2YgVGhlbWUgfCB1bmRlZmluZWQge1xuICBpZiAoIWNvbG9yTmFtZSkgcmV0dXJuIHVuZGVmaW5lZFxuICBpZiAoQUdFTlRfQ09MT1JTLmluY2x1ZGVzKGNvbG9yTmFtZSBhcyBBZ2VudENvbG9yTmFtZSkpIHtcbiAgICByZXR1cm4gQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1JbY29sb3JOYW1lIGFzIEFnZW50Q29sb3JOYW1lXVxuICB9XG4gIHJldHVybiB1bmRlZmluZWRcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLE9BQU8sTUFBTSxTQUFTO0FBQzdCLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsT0FBTyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUN6QyxTQUFTQyxlQUFlLFFBQVEsOEJBQThCO0FBQzlELFNBQVNDLFdBQVcsUUFBUSx3QkFBd0I7QUFDcEQsU0FBU0MsV0FBVyxFQUFFQyxjQUFjLFFBQVEsdUJBQXVCO0FBQ25FLFNBQ0VDLGlCQUFpQixFQUNqQkMsZ0JBQWdCLFFBQ1gsa0NBQWtDO0FBQ3pDLFNBQVNDLGdCQUFnQixRQUFRLDRDQUE0QztBQUM3RSxTQUFTQyxZQUFZLEVBQUVDLFlBQVksUUFBUSx3QkFBd0I7QUFDbkUsU0FDRSxLQUFLQyxtQkFBbUIsRUFDeEJDLGdCQUFnQixFQUNoQixLQUFLQyxTQUFTLFFBQ1Qsb0JBQW9CO0FBQzNCLFNBQVNDLCtCQUErQixRQUFRLCtCQUErQjtBQUMvRSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQ0VDLDBCQUEwQixFQUMxQkMsWUFBWSxFQUNaLEtBQUtDLGNBQWMsUUFDZCw0Q0FBNEM7QUFDbkQsY0FBY0MsS0FBSyxRQUFRLHNCQUFzQjtBQUNqRCxTQUFTQyxvQkFBb0IsUUFBUSwwQ0FBMEM7QUFDL0UsU0FBU0MscUJBQXFCLFFBQVEsc0JBQXNCO0FBRTVELEtBQUtDLEtBQUssR0FBRztFQUNYQyxhQUFhLEVBQUUsT0FBTztFQUN0QkMsaUJBQWlCLENBQUMsRUFBRSxPQUFPO0VBQzNCQyxtQkFBbUIsQ0FBQyxFQUFFLE1B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 \ No newline at end of file diff --git a/components/tasks/BackgroundTasksDialog.tsx b/components/tasks/BackgroundTasksDialog.tsx new file mode 100644 index 0000000..7abd9c0 --- /dev/null +++ b/components/tasks/BackgroundTasksDialog.tsx @@ -0,0 +1,652 @@ +import { c as _c } from "react/compiler-runtime"; +import { feature } from 'bun:bundle'; +import figures from 'figures'; +import React, { type ReactNode, useEffect, useEffectEvent, useMemo, useRef, useState } from 'react'; +import { isCoordinatorMode } from 'src/coordinator/coordinatorMode.js'; +import { useTerminalSize } from 'src/hooks/useTerminalSize.js'; +import { useAppState, useSetAppState } from 'src/state/AppState.js'; +import { enterTeammateView, exitTeammateView } from 'src/state/teammateViewHelpers.js'; +import type { ToolUseContext } from 'src/Tool.js'; +import { DreamTask, type DreamTaskState } from 'src/tasks/DreamTask/DreamTask.js'; +import { InProcessTeammateTask } from 'src/tasks/InProcessTeammateTask/InProcessTeammateTask.js'; +import type { InProcessTeammateTaskState } from 'src/tasks/InProcessTeammateTask/types.js'; +import type { LocalAgentTaskState } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'; +import { LocalAgentTask } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'; +import type { LocalShellTaskState } from 'src/tasks/LocalShellTask/guards.js'; +import { LocalShellTask } from 'src/tasks/LocalShellTask/LocalShellTask.js'; +// Type import is erased at build time — safe even though module is ant-gated. +import type { LocalWorkflowTaskState } from 'src/tasks/LocalWorkflowTask/LocalWorkflowTask.js'; +import type { MonitorMcpTaskState } from 'src/tasks/MonitorMcpTask/MonitorMcpTask.js'; +import { RemoteAgentTask, type RemoteAgentTaskState } from 'src/tasks/RemoteAgentTask/RemoteAgentTask.js'; +import { type BackgroundTaskState, isBackgroundTask, type TaskState } from 'src/tasks/types.js'; +import type { DeepImmutable } from 'src/types/utils.js'; +import { intersperse } from 'src/utils/array.js'; +import { TEAM_LEAD_NAME } from 'src/utils/swarm/constants.js'; +import { stopUltraplan } from '../../commands/ultraplan.js'; +import type { CommandResultDisplay } from '../../commands.js'; +import { useRegisterOverlay } from '../../context/overlayContext.js'; +import type { ExitState } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'; +import { count } from '../../utils/array.js'; +import { Byline } from '../design-system/Byline.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { AsyncAgentDetailDialog } from './AsyncAgentDetailDialog.js'; +import { BackgroundTask as BackgroundTaskComponent } from './BackgroundTask.js'; +import { DreamDetailDialog } from './DreamDetailDialog.js'; +import { InProcessTeammateDetailDialog } from './InProcessTeammateDetailDialog.js'; +import { RemoteSessionDetailDialog } from './RemoteSessionDetailDialog.js'; +import { ShellDetailDialog } from './ShellDetailDialog.js'; +type ViewState = { + mode: 'list'; +} | { + mode: 'detail'; + itemId: string; +}; +type Props = { + onDone: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; + toolUseContext: ToolUseContext; + initialDetailTaskId?: string; +}; +type ListItem = { + id: string; + type: 'local_bash'; + label: string; + status: string; + task: DeepImmutable; +} | { + id: string; + type: 'remote_agent'; + label: string; + status: string; + task: DeepImmutable; +} | { + id: string; + type: 'local_agent'; + label: string; + status: string; + task: DeepImmutable; +} | { + id: string; + type: 'in_process_teammate'; + label: string; + status: string; + task: DeepImmutable; +} | { + id: string; + type: 'local_workflow'; + label: string; + status: string; + task: DeepImmutable; +} | { + id: string; + type: 'monitor_mcp'; + label: string; + status: string; + task: DeepImmutable; +} | { + id: string; + type: 'dream'; + label: string; + status: string; + task: DeepImmutable; +} | { + id: string; + type: 'leader'; + label: string; + status: 'running'; +}; + +// WORKFLOW_SCRIPTS is ant-only (build_flags.yaml). Static imports would leak +// ~1.3K lines into external builds. Gate with feature() + require so the +// bundler can dead-code-eliminate the branch. +/* eslint-disable @typescript-eslint/no-require-imports */ +const WorkflowDetailDialog = feature('WORKFLOW_SCRIPTS') ? (require('./WorkflowDetailDialog.js') as typeof import('./WorkflowDetailDialog.js')).WorkflowDetailDialog : null; +const workflowTaskModule = feature('WORKFLOW_SCRIPTS') ? require('src/tasks/LocalWorkflowTask/LocalWorkflowTask.js') as typeof import('src/tasks/LocalWorkflowTask/LocalWorkflowTask.js') : null; +const killWorkflowTask = workflowTaskModule?.killWorkflowTask ?? null; +const skipWorkflowAgent = workflowTaskModule?.skipWorkflowAgent ?? null; +const retryWorkflowAgent = workflowTaskModule?.retryWorkflowAgent ?? null; +// Relative path, not `src/...` path-mapping — Bun's DCE can statically +// resolve + eliminate `./` requires, but path-mapped strings stay opaque +// and survive as dead literals in the bundle. Matches tasks.ts pattern. +const monitorMcpModule = feature('MONITOR_TOOL') ? require('../../tasks/MonitorMcpTask/MonitorMcpTask.js') as typeof import('../../tasks/MonitorMcpTask/MonitorMcpTask.js') : null; +const killMonitorMcp = monitorMcpModule?.killMonitorMcp ?? null; +const MonitorMcpDetailDialog = feature('MONITOR_TOOL') ? (require('./MonitorMcpDetailDialog.js') as typeof import('./MonitorMcpDetailDialog.js')).MonitorMcpDetailDialog : null; +/* eslint-enable @typescript-eslint/no-require-imports */ + +// Helper to get filtered background tasks (excludes foregrounded local_agent) +function getSelectableBackgroundTasks(tasks: Record | undefined, foregroundedTaskId: string | undefined): TaskState[] { + const backgroundTasks = Object.values(tasks ?? {}).filter(isBackgroundTask); + return backgroundTasks.filter(task => !(task.type === 'local_agent' && task.id === foregroundedTaskId)); +} +export function BackgroundTasksDialog({ + onDone, + toolUseContext, + initialDetailTaskId +}: Props): React.ReactNode { + const tasks = useAppState(s => s.tasks); + const foregroundedTaskId = useAppState(s_0 => s_0.foregroundedTaskId); + const showSpinnerTree = useAppState(s_1 => s_1.expandedView) === 'teammates'; + const setAppState = useSetAppState(); + const killAgentsShortcut = useShortcutDisplay('chat:killAgents', 'Chat', 'ctrl+x ctrl+k'); + const typedTasks = tasks as Record | undefined; + + // Track if we skipped list view on mount (for back button behavior) + const skippedListOnMount = useRef(false); + + // Compute initial view state - skip list if caller provided a specific task, + // or if there's exactly one task + const [viewState, setViewState] = useState(() => { + if (initialDetailTaskId) { + skippedListOnMount.current = true; + return { + mode: 'detail', + itemId: initialDetailTaskId + }; + } + const allItems = getSelectableBackgroundTasks(typedTasks, foregroundedTaskId); + if (allItems.length === 1) { + skippedListOnMount.current = true; + return { + mode: 'detail', + itemId: allItems[0]!.id + }; + } + return { + mode: 'list' + }; + }); + const [selectedIndex, setSelectedIndex] = useState(0); + + // Register as modal overlay so parent Chat keybindings (up/down for history) + // are deactivated while this dialog is open + useRegisterOverlay('background-tasks-dialog'); + + // Memoize the sorted and categorized items together to ensure stable references + const { + bashTasks, + remoteSessions, + agentTasks, + teammateTasks, + workflowTasks, + mcpMonitors, + dreamTasks: dreamTasks_0, + allSelectableItems + } = useMemo(() => { + // Filter to only show running/pending background tasks, matching the status bar count + const backgroundTasks = Object.values(typedTasks ?? {}).filter(isBackgroundTask); + const allItems_0 = backgroundTasks.map(toListItem); + const sorted = allItems_0.sort((a, b) => { + const aStatus = a.status; + const bStatus = b.status; + if (aStatus === 'running' && bStatus !== 'running') return -1; + if (aStatus !== 'running' && bStatus === 'running') return 1; + const aTime = 'task' in a ? a.task.startTime : 0; + const bTime = 'task' in b ? b.task.startTime : 0; + return bTime - aTime; + }); + const bash = sorted.filter(item => item.type === 'local_bash'); + const remote = sorted.filter(item_0 => item_0.type === 'remote_agent'); + // Exclude foregrounded task - it's being viewed in the main UI, not a background task + const agent = sorted.filter(item_1 => item_1.type === 'local_agent' && item_1.id !== foregroundedTaskId); + const workflows = sorted.filter(item_2 => item_2.type === 'local_workflow'); + const monitorMcp = sorted.filter(item_3 => item_3.type === 'monitor_mcp'); + const dreamTasks = sorted.filter(item_4 => item_4.type === 'dream'); + // In spinner-tree mode, exclude teammates from the dialog (they appear in the tree) + const teammates = showSpinnerTree ? [] : sorted.filter(item_5 => item_5.type === 'in_process_teammate'); + // Add leader entry when there are teammates, so users can foreground back to leader + const leaderItem: ListItem[] = teammates.length > 0 ? [{ + id: '__leader__', + type: 'leader', + label: `@${TEAM_LEAD_NAME}`, + status: 'running' + }] : []; + return { + bashTasks: bash, + remoteSessions: remote, + agentTasks: agent, + workflowTasks: workflows, + mcpMonitors: monitorMcp, + dreamTasks, + teammateTasks: [...leaderItem, ...teammates], + // Order MUST match JSX render order (teammates \u2192 bash \u2192 monitorMcp \u2192 + // remote \u2192 agent \u2192 workflows \u2192 dream) so \u2193/\u2191 navigation moves the cursor + // visually downward. + allSelectableItems: [...leaderItem, ...teammates, ...bash, ...monitorMcp, ...remote, ...agent, ...workflows, ...dreamTasks] + }; + }, [typedTasks, foregroundedTaskId, showSpinnerTree]); + const currentSelection = allSelectableItems[selectedIndex] ?? null; + + // Use configurable keybindings for standard navigation and confirm/cancel. + // confirm:no is handled by Dialog's onCancel prop. + useKeybindings({ + 'confirm:previous': () => setSelectedIndex(prev => Math.max(0, prev - 1)), + 'confirm:next': () => setSelectedIndex(prev_0 => Math.min(allSelectableItems.length - 1, prev_0 + 1)), + 'confirm:yes': () => { + const current = allSelectableItems[selectedIndex]; + if (current) { + if (current.type === 'leader') { + exitTeammateView(setAppState); + onDone('Viewing leader', { + display: 'system' + }); + } else { + setViewState({ + mode: 'detail', + itemId: current.id + }); + } + } + } + }, { + context: 'Confirmation', + isActive: viewState.mode === 'list' + }); + + // Component-specific shortcuts (x=stop, f=foreground, right=zoom) shown in UI. + // These are task-type and status dependent, not standard dialog keybindings. + const handleKeyDown = (e: KeyboardEvent) => { + // Only handle input when in list mode + if (viewState.mode !== 'list') return; + if (e.key === 'left') { + e.preventDefault(); + onDone('Background tasks dialog dismissed', { + display: 'system' + }); + return; + } + + // Compute current selection at the time of the key press + const currentSelection_0 = allSelectableItems[selectedIndex]; + if (!currentSelection_0) return; // everything below requires a selection + + if (e.key === 'x') { + e.preventDefault(); + if (currentSelection_0.type === 'local_bash' && currentSelection_0.status === 'running') { + void killShellTask(currentSelection_0.id); + } else if (currentSelection_0.type === 'local_agent' && currentSelection_0.status === 'running') { + void killAgentTask(currentSelection_0.id); + } else if (currentSelection_0.type === 'in_process_teammate' && currentSelection_0.status === 'running') { + void killTeammateTask(currentSelection_0.id); + } else if (currentSelection_0.type === 'local_workflow' && currentSelection_0.status === 'running' && killWorkflowTask) { + killWorkflowTask(currentSelection_0.id, setAppState); + } else if (currentSelection_0.type === 'monitor_mcp' && currentSelection_0.status === 'running' && killMonitorMcp) { + killMonitorMcp(currentSelection_0.id, setAppState); + } else if (currentSelection_0.type === 'dream' && currentSelection_0.status === 'running') { + void killDreamTask(currentSelection_0.id); + } else if (currentSelection_0.type === 'remote_agent' && currentSelection_0.status === 'running') { + if (currentSelection_0.task.isUltraplan) { + void stopUltraplan(currentSelection_0.id, currentSelection_0.task.sessionId, setAppState); + } else { + void killRemoteAgentTask(currentSelection_0.id); + } + } + } + if (e.key === 'f') { + if (currentSelection_0.type === 'in_process_teammate' && currentSelection_0.status === 'running') { + e.preventDefault(); + enterTeammateView(currentSelection_0.id, setAppState); + onDone('Viewing teammate', { + display: 'system' + }); + } else if (currentSelection_0.type === 'leader') { + e.preventDefault(); + exitTeammateView(setAppState); + onDone('Viewing leader', { + display: 'system' + }); + } + } + }; + async function killShellTask(taskId: string): Promise { + await LocalShellTask.kill(taskId, setAppState); + } + async function killAgentTask(taskId_0: string): Promise { + await LocalAgentTask.kill(taskId_0, setAppState); + } + async function killTeammateTask(taskId_1: string): Promise { + await InProcessTeammateTask.kill(taskId_1, setAppState); + } + async function killDreamTask(taskId_2: string): Promise { + await DreamTask.kill(taskId_2, setAppState); + } + async function killRemoteAgentTask(taskId_3: string): Promise { + await RemoteAgentTask.kill(taskId_3, setAppState); + } + + // Wrap onDone in useEffectEvent to get a stable reference that always calls + // the current onDone callback without causing the effect to re-fire. + const onDoneEvent = useEffectEvent(onDone); + useEffect(() => { + if (viewState.mode !== 'list') { + const task = (typedTasks ?? {})[viewState.itemId]; + // Workflow tasks get a grace: their detail view stays open through + // completion so the user sees the final state before eviction. + if (!task || task.type !== 'local_workflow' && !isBackgroundTask(task)) { + // Task was removed or is no longer a background task (e.g. killed). + // If we skipped the list on mount, close the dialog entirely. + if (skippedListOnMount.current) { + onDoneEvent('Background tasks dialog dismissed', { + display: 'system' + }); + } else { + setViewState({ + mode: 'list' + }); + } + } + } + const totalItems = allSelectableItems.length; + if (selectedIndex >= totalItems && totalItems > 0) { + setSelectedIndex(totalItems - 1); + } + }, [viewState, typedTasks, selectedIndex, allSelectableItems, onDoneEvent]); + + // Helper to go back to list view (or close dialog if we skipped list on + // mount AND there's still only ≤1 item). Checking current count prevents + // the stale-state trap: if you opened with 1 task (auto-skipped to detail), + // then a second task started, 'back' should show the list — not close. + const goBackToList = () => { + if (skippedListOnMount.current && allSelectableItems.length <= 1) { + onDone('Background tasks dialog dismissed', { + display: 'system' + }); + } else { + skippedListOnMount.current = false; + setViewState({ + mode: 'list' + }); + } + }; + + // If an item is selected, show the appropriate view + if (viewState.mode !== 'list' && typedTasks) { + const task_0 = typedTasks[viewState.itemId]; + if (!task_0) { + return null; + } + + // Detail mode - show appropriate detail dialog + switch (task_0.type) { + case 'local_bash': + return void killShellTask(task_0.id)} onBack={goBackToList} key={`shell-${task_0.id}`} />; + case 'local_agent': + return void killAgentTask(task_0.id)} onBack={goBackToList} key={`agent-${task_0.id}`} />; + case 'remote_agent': + return void stopUltraplan(task_0.id, task_0.sessionId, setAppState) : () => void killRemoteAgentTask(task_0.id)} key={`session-${task_0.id}`} />; + case 'in_process_teammate': + return void killTeammateTask(task_0.id) : undefined} onBack={goBackToList} onForeground={task_0.status === 'running' ? () => { + enterTeammateView(task_0.id, setAppState); + onDone('Viewing teammate', { + display: 'system' + }); + } : undefined} key={`teammate-${task_0.id}`} />; + case 'local_workflow': + if (!WorkflowDetailDialog) return null; + return killWorkflowTask(task_0.id, setAppState) : undefined} onSkipAgent={task_0.status === 'running' && skipWorkflowAgent ? agentId => skipWorkflowAgent(task_0.id, agentId, setAppState) : undefined} onRetryAgent={task_0.status === 'running' && retryWorkflowAgent ? agentId_0 => retryWorkflowAgent(task_0.id, agentId_0, setAppState) : undefined} onBack={goBackToList} key={`workflow-${task_0.id}`} />; + case 'monitor_mcp': + if (!MonitorMcpDetailDialog) return null; + return killMonitorMcp(task_0.id, setAppState) : undefined} onBack={goBackToList} key={`monitor-mcp-${task_0.id}`} />; + case 'dream': + return onDone('Background tasks dialog dismissed', { + display: 'system' + })} onBack={goBackToList} onKill={task_0.status === 'running' ? () => void killDreamTask(task_0.id) : undefined} key={`dream-${task_0.id}`} />; + } + } + const runningBashCount = count(bashTasks, _ => _.status === 'running'); + const runningAgentCount = count(remoteSessions, __0 => __0.status === 'running' || __0.status === 'pending') + count(agentTasks, __1 => __1.status === 'running'); + const runningTeammateCount = count(teammateTasks, __2 => __2.status === 'running'); + const subtitle = intersperse([...(runningTeammateCount > 0 ? [ + {runningTeammateCount}{' '} + {runningTeammateCount !== 1 ? 'agents' : 'agent'} + ] : []), ...(runningBashCount > 0 ? [ + {runningBashCount}{' '} + {runningBashCount !== 1 ? 'active shells' : 'active shell'} + ] : []), ...(runningAgentCount > 0 ? [ + {runningAgentCount}{' '} + {runningAgentCount !== 1 ? 'active agents' : 'active agent'} + ] : [])], index => · ); + const actions = [, , ...(currentSelection?.type === 'in_process_teammate' && currentSelection.status === 'running' ? [] : []), ...((currentSelection?.type === 'local_bash' || currentSelection?.type === 'local_agent' || currentSelection?.type === 'in_process_teammate' || currentSelection?.type === 'local_workflow' || currentSelection?.type === 'monitor_mcp' || currentSelection?.type === 'dream' || currentSelection?.type === 'remote_agent') && currentSelection.status === 'running' ? [] : []), ...(agentTasks.some(t => t.status === 'running') ? [] : []), ]; + const handleCancel = () => onDone('Background tasks dialog dismissed', { + display: 'system' + }); + function renderInputGuide(exitState: ExitState): React.ReactNode { + if (exitState.pending) { + return Press {exitState.keyName} again to exit; + } + return {actions}; + } + return + {subtitle}} onCancel={handleCancel} color="background" inputGuide={renderInputGuide}> + {allSelectableItems.length === 0 ? No tasks currently running : + {teammateTasks.length > 0 && + {(bashTasks.length > 0 || remoteSessions.length > 0 || agentTasks.length > 0) && + {' '}Agents ( + {count(teammateTasks, i => i.type !== 'leader')}) + } + + + + } + + {bashTasks.length > 0 && 0 ? 1 : 0}> + {(teammateTasks.length > 0 || remoteSessions.length > 0 || agentTasks.length > 0) && + {' '}Shells ({bashTasks.length}) + } + + {bashTasks.map(item_6 => )} + + } + + {mcpMonitors.length > 0 && 0 || bashTasks.length > 0 ? 1 : 0}> + + {' '}Monitors ({mcpMonitors.length}) + + + {mcpMonitors.map(item_7 => )} + + } + + {remoteSessions.length > 0 && 0 || bashTasks.length > 0 || mcpMonitors.length > 0 ? 1 : 0}> + + {' '}Remote agents ({remoteSessions.length} + ) + + + {remoteSessions.map(item_8 => )} + + } + + {agentTasks.length > 0 && 0 || bashTasks.length > 0 || mcpMonitors.length > 0 || remoteSessions.length > 0 ? 1 : 0}> + + {' '}Local agents ({agentTasks.length}) + + + {agentTasks.map(item_9 => )} + + } + + {workflowTasks.length > 0 && 0 || bashTasks.length > 0 || mcpMonitors.length > 0 || remoteSessions.length > 0 || agentTasks.length > 0 ? 1 : 0}> + + {' '}Workflows ({workflowTasks.length}) + + + {workflowTasks.map(item_10 => )} + + } + + {dreamTasks_0.length > 0 && 0 || bashTasks.length > 0 || mcpMonitors.length > 0 || remoteSessions.length > 0 || agentTasks.length > 0 || workflowTasks.length > 0 ? 1 : 0}> + + {dreamTasks_0.map(item_11 => )} + + } + } + + ; +} +function toListItem(task: BackgroundTaskState): ListItem { + switch (task.type) { + case 'local_bash': + return { + id: task.id, + type: 'local_bash', + label: task.kind === 'monitor' ? task.description : task.command, + status: task.status, + task + }; + case 'remote_agent': + return { + id: task.id, + type: 'remote_agent', + label: task.title, + status: task.status, + task + }; + case 'local_agent': + return { + id: task.id, + type: 'local_agent', + label: task.description, + status: task.status, + task + }; + case 'in_process_teammate': + return { + id: task.id, + type: 'in_process_teammate', + label: `@${task.identity.agentName}`, + status: task.status, + task + }; + case 'local_workflow': + return { + id: task.id, + type: 'local_workflow', + label: task.summary ?? task.description, + status: task.status, + task + }; + case 'monitor_mcp': + return { + id: task.id, + type: 'monitor_mcp', + label: task.description, + status: task.status, + task + }; + case 'dream': + return { + id: task.id, + type: 'dream', + label: task.description, + status: task.status, + task + }; + } +} +function Item(t0) { + const $ = _c(14); + const { + item, + isSelected + } = t0; + const { + columns + } = useTerminalSize(); + const maxActivityWidth = Math.max(30, columns - 26); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = isCoordinatorMode(); + $[0] = t1; + } else { + t1 = $[0]; + } + const useGreyPointer = t1; + const t2 = useGreyPointer && isSelected; + const t3 = isSelected ? figures.pointer + " " : " "; + let t4; + if ($[1] !== t2 || $[2] !== t3) { + t4 = {t3}; + $[1] = t2; + $[2] = t3; + $[3] = t4; + } else { + t4 = $[3]; + } + const t5 = isSelected && !useGreyPointer ? "suggestion" : undefined; + let t6; + if ($[4] !== item.task || $[5] !== item.type || $[6] !== maxActivityWidth) { + t6 = item.type === "leader" ? @{TEAM_LEAD_NAME} : ; + $[4] = item.task; + $[5] = item.type; + $[6] = maxActivityWidth; + $[7] = t6; + } else { + t6 = $[7]; + } + let t7; + if ($[8] !== t5 || $[9] !== t6) { + t7 = {t6}; + $[8] = t5; + $[9] = t6; + $[10] = t7; + } else { + t7 = $[10]; + } + let t8; + if ($[11] !== t4 || $[12] !== t7) { + t8 = {t4}{t7}; + $[11] = t4; + $[12] = t7; + $[13] = t8; + } else { + t8 = $[13]; + } + return t8; +} +function TeammateTaskGroups(t0) { + const $ = _c(3); + const { + teammateTasks, + currentSelectionId + } = t0; + let t1; + if ($[0] !== currentSelectionId || $[1] !== teammateTasks) { + const leaderItems = teammateTasks.filter(_temp); + const teammateItems = teammateTasks.filter(_temp2); + const teams = new Map(); + for (const item of teammateItems) { + const teamName = item.task.identity.teamName; + const group = teams.get(teamName); + if (group) { + group.push(item); + } else { + teams.set(teamName, [item]); + } + } + const teamEntries = [...teams.entries()]; + t1 = <>{teamEntries.map(t2 => { + const [teamName_0, items] = t2; + const memberCount = items.length + leaderItems.length; + return {" "}Team: {teamName_0} ({memberCount}){leaderItems.map(item_0 => )}{items.map(item_1 => )}; + })}; + $[0] = currentSelectionId; + $[1] = teammateTasks; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; +} +function _temp2(i_0) { + return i_0.type === "in_process_teammate"; +} +function _temp(i) { + return i.type === "leader"; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiZmlndXJlcyIsIlJlYWN0IiwiUmVhY3ROb2RlIiwidXNlRWZmZWN0IiwidXNlRWZmZWN0RXZlbnQiLCJ1c2VNZW1vIiwidXNlUmVmIiwidXNlU3RhdGUiLCJpc0Nvb3JkaW5hdG9yTW9kZSIsInVzZVRlcm1pbmFsU2l6ZSIsInVzZUFwcFN0YXRlIiwidXNlU2V0QXBwU3RhdGUiLCJlbnRlclRlYW1tYXRlVmlldyIsImV4aXRUZWFtbWF0ZVZpZXciLCJUb29sVXNlQ29udGV4dCIsIkRyZWFtVGFzayIsIkRyZWFtVGFza1N0YXRlIiwiSW5Qcm9jZXNzVGVhbW1hdGVUYXNrIiwiSW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGUiLCJMb2NhbEFnZW50VGFza1N0YXRlIiwiTG9jYWxBZ2VudFRhc2siLCJMb2NhbFNoZWxsVGFza1N0YXRlIiwiTG9jYWxTaGVsbFRhc2siLCJMb2NhbFdvcmtmbG93VGFza1N0YXRlIiwiTW9uaXRvck1jcFRhc2tTdGF0ZSIsIlJlbW90ZUFnZW50VGFzayIsIlJlbW90ZUFnZW50VGFza1N0YXRlIiwiQmFja2dyb3VuZFRhc2tTdGF0ZSIsImlzQmFja2dyb3VuZFRhc2siLCJUYXNrU3RhdGUiLCJEZWVwSW1tdXRhYmxlIiwiaW50ZXJzcGVyc2UiLCJURUFNX0xFQURfTkFNRSIsInN0b3BVbHRyYXBsYW4iLCJDb21tYW5kUmVzdWx0RGlzcGxheSIsInVzZVJlZ2lzdGVyT3ZlcmxheSIsIkV4aXRTdGF0ZSIsIktleWJvYXJkRXZlbnQiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZ3MiLCJ1c2VTaG9ydGN1dERpc3BsYXkiLCJjb3VudCIsIkJ5bGluZSIsIkRpYWxvZyIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwiQXN5bmNBZ2VudERldGFpbERpYWxvZyIsIkJhY2tncm91bmRUYXNrIiwiQmFja2dyb3VuZFRhc2tDb21wb25lbnQiLCJEcmVhbURldGFpbERpYWxvZyIsIkluUHJvY2Vzc1RlYW1tYXRlRGV0YWlsRGlhbG9nIiwiUmVtb3RlU2Vzc2lvbkRldGFpbERpYWxvZyIsIlNoZWxsRGV0YWlsRGlhbG9nIiwiVmlld1N0YXRlIiwibW9kZSIsIml0ZW1JZCIsIlByb3BzIiwib25Eb25lIiwicmVzdWx0Iiwib3B0aW9ucyIsImRpc3BsYXkiLCJ0b29sVXNlQ29udGV4dCIsImluaXRpYWxEZXRhaWxUYXNrSWQiLCJMaXN0SXRlbSIsImlkIiwidHlwZSIsImxhYmVsIiwic3RhdHVzIiwidGFzayIsIldvcmtmbG93RGV0YWlsRGlhbG9nIiwicmVxdWlyZSIsIndvcmtmbG93VGFza01vZHVsZSIsImtpbGxXb3JrZmxvd1Rhc2siLCJza2lwV29ya2Zsb3dBZ2VudCIsInJldHJ5V29ya2Zsb3dBZ2VudCIsIm1vbml0b3JNY3BNb2R1bGUiLCJraWxsTW9uaXRvck1jcCIsIk1vbml0b3JNY3BEZXRhaWxEaWFsb2ciLCJnZXRTZWxlY3RhYmxlQmFja2dyb3VuZFRhc2tzIiwidGFza3MiLCJSZWNvcmQiLCJmb3JlZ3JvdW5kZWRUYXNrSWQiLCJiYWNrZ3JvdW5kVGFza3MiLCJPYmplY3QiLCJ2YWx1ZXMiLCJmaWx0ZXIiLCJCYWNrZ3JvdW5kVGFza3NEaWFsb2ciLCJzIiwic2hvd1NwaW5uZXJUcmVlIiwiZXhwYW5kZWRWaWV3Iiwic2V0QXBwU3RhdGUiLCJraWxsQWdlbnRzU2hvcnRjdXQiLCJ0eXBlZFRhc2tzIiwic2tpcHBlZExpc3RPbk1vdW50Iiwidmlld1N0YXRlIiwic2V0Vmlld1N0YXRlIiwiY3VycmVudCIsImFsbEl0ZW1zIiwibGVuZ3RoIiwic2VsZWN0ZWRJbmRleCIsInNldFNlbGVjdGVkSW5kZXgiLCJiYXNoVGFza3MiLCJyZW1vdGVTZXNzaW9ucyIsImFnZW50VGFza3MiLCJ0ZWFtbWF0ZVRhc2tzIiwid29ya2Zsb3dUYXNrcyIsIm1jcE1vbml0b3JzIiwiZHJlYW1UYXNrcyIsImFsbFNlbGVjdGFibGVJdGVtcyIsIm1hcCIsInRvTGlzdEl0ZW0iLCJzb3J0ZWQiLCJzb3J0IiwiYSIsImIiLCJhU3RhdHVzIiwiYlN0YXR1cyIsImFUaW1lIiwic3RhcnRUaW1lIiwiYlRpbWUiLCJiYXNoIiwiaXRlbSIsInJlbW90ZSIsImFnZW50Iiwid29ya2Zsb3dzIiwibW9uaXRvck1jcCIsInRlYW1tYXRlcyIsImxlYWRlckl0ZW0iLCJjdXJyZW50U2VsZWN0aW9uIiwiY29uZmlybTpwcmV2aW91cyIsInByZXYiLCJNYXRoIiwibWF4IiwiY29uZmlybTpuZXh0IiwibWluIiwiY29uZmlybTp5ZXMiLCJjb250ZXh0IiwiaXNBY3RpdmUiLCJoYW5kbGVLZXlEb3duIiwiZSIsImtleSIsInByZXZlbnREZWZhdWx0Iiwia2lsbFNoZWxsVGFzayIsImtpbGxBZ2VudFRhc2siLCJraWxsVGVhbW1hdGVUYXNrIiwia2lsbERyZWFtVGFzayIsImlzVWx0cmFwbGFuIiwic2Vzc2lvbklkIiwia2lsbFJlbW90ZUFnZW50VGFzayIsInRhc2tJZCIsIlByb21pc2UiLCJraWxsIiwib25Eb25lRXZlbnQiLCJ0b3RhbEl0ZW1zIiwiZ29CYWNrVG9MaXN0IiwidW5kZWZpbmVkIiwiYWdlbnRJZCIsInJ1bm5pbmdCYXNoQ291bnQiLCJfIiwicnVubmluZ0FnZW50Q291bnQiLCJydW5uaW5nVGVhbW1hdGVDb3VudCIsInN1YnRpdGxlIiwiaW5kZXgiLCJhY3Rpb25zIiwic29tZSIsInQiLCJoYW5kbGVDYW5jZWwiLCJyZW5kZXJJbnB1dEd1aWRlIiwiZXhpdFN0YXRlIiwicGVuZGluZyIsImtleU5hbWUiLCJpIiwia2luZCIsImRlc2NyaXB0aW9uIiwiY29tbWFuZCIsInRpdGxlIiwiaWRlbnRpdHkiLCJhZ2VudE5hbWUiLCJzdW1tYXJ5IiwiSXRlbSIsInQwIiwiJCIsIl9jIiwiaXNTZWxlY3RlZCIsImNvbHVtbnMiLCJtYXhBY3Rpdml0eVdpZHRoIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJ1c2VHcmV5UG9pbnRlciIsInQyIiwidDMiLCJwb2ludGVyIiwidDQiLCJ0NSIsInQ2IiwidDciLCJ0OCIsIlRlYW1tYXRlVGFza0dyb3VwcyIsImN1cnJlbnRTZWxlY3Rpb25JZCIsImxlYWRlckl0ZW1zIiwiX3RlbXAiLCJ0ZWFtbWF0ZUl0ZW1zIiwiX3RlbXAyIiwidGVhbXMiLCJNYXAiLCJ0ZWFtTmFtZSIsImdyb3VwIiwiZ2V0IiwicHVzaCIsInNldCIsInRlYW1FbnRyaWVzIiwiZW50cmllcyIsInRlYW1OYW1lXzAiLCJpdGVtcyIsIm1lbWJlckNvdW50IiwiaXRlbV8wIiwiaXRlbV8xIiwiaV8wIl0sInNvdXJjZXMiOlsiQmFja2dyb3VuZFRhc2tzRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCBmaWd1cmVzIGZyb20gJ2ZpZ3VyZXMnXG5pbXBvcnQgUmVhY3QsIHtcbiAgdHlwZSBSZWFjdE5vZGUsXG4gIHVzZUVmZmVjdCxcbiAgdXNlRWZmZWN0RXZlbnQsXG4gIHVzZU1lbW8sXG4gIHVzZVJlZixcbiAgdXNlU3RhdGUsXG59IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgaXNDb29yZGluYXRvck1vZGUgfSBmcm9tICdzcmMvY29vcmRpbmF0b3IvY29vcmRpbmF0b3JNb2RlLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnc3JjL2hvb2tzL3VzZVRlcm1pbmFsU2l6ZS5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlLCB1c2VTZXRBcHBTdGF0ZSB9IGZyb20gJ3NyYy9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7XG4gIGVudGVyVGVhbW1hdGVWaWV3LFxuICBleGl0VGVhbW1hdGVWaWV3LFxufSBmcm9tICdzcmMvc3RhdGUvdGVhbW1hdGVWaWV3SGVscGVycy5qcydcbmltcG9ydCB0eXBlIHsgVG9vbFVzZUNvbnRleHQgfSBmcm9tICdzcmMvVG9vbC5qcydcbmltcG9ydCB7XG4gIERyZWFtVGFzayxcbiAgdHlwZSBEcmVhbVRhc2tTdGF0ZSxcbn0gZnJvbSAnc3JjL3Rhc2tzL0RyZWFtVGFzay9EcmVhbVRhc2suanMnXG5pbXBvcnQgeyBJblByb2Nlc3NUZWFtbWF0ZVRhc2sgfSBmcm9tICdzcmMvdGFza3MvSW5Qcm9jZXNzVGVhbW1hdGVUYXNrL0luUHJvY2Vzc1RlYW1tYXRlVGFzay5qcydcbmltcG9ydCB0eXBlIHsgSW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGUgfSBmcm9tICdzcmMvdGFza3MvSW5Qcm9jZXNzVGVhbW1hdGVUYXNrL3R5cGVzLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbEFnZW50VGFza1N0YXRlIH0gZnJvbSAnc3JjL3Rhc2tzL0xvY2FsQWdlbnRUYXNrL0xvY2FsQWdlbnRUYXNrLmpzJ1xuaW1wb3J0IHsgTG9jYWxBZ2VudFRhc2sgfSBmcm9tICdzcmMvdGFza3MvTG9jYWxBZ2VudFRhc2svTG9jYWxBZ2VudFRhc2suanMnXG5pbXBvcnQgdHlwZSB7IExvY2FsU2hlbGxUYXNrU3RhdGUgfSBmcm9tICdzcmMvdGFza3MvTG9jYWxTaGVsbFRhc2svZ3VhcmRzLmpzJ1xuaW1wb3J0IHsgTG9jYWxTaGVsbFRhc2sgfSBmcm9tICdzcmMvdGFza3MvTG9jYWxTaGVsbFRhc2svTG9jYWxTaGVsbFRhc2suanMnXG4vLyBUeXBlIGltcG9ydCBpcyBlcmFzZWQgYXQgYnVpbGQgdGltZSDigJQgc2FmZSBldmVuIHRob3VnaCBtb2R1bGUgaXMgYW50LWdhdGVkLlxuaW1wb3J0IHR5cGUgeyBMb2NhbFdvcmtmbG93VGFza1N0YXRlIH0gZnJvbSAnc3JjL3Rhc2tzL0xvY2FsV29ya2Zsb3dUYXNrL0xvY2FsV29ya2Zsb3dUYXNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBNb25pdG9yTWNwVGFza1N0YXRlIH0gZnJvbSAnc3JjL3Rhc2tzL01vbml0b3JNY3BUYXNrL01vbml0b3JNY3BUYXNrLmpzJ1xuaW1wb3J0IHtcbiAgUmVtb3RlQWdlbnRUYXNrLFxuICB0eXBlIFJlbW90ZUFnZW50VGFza1N0YXRlLFxufSBmcm9tICdzcmMvdGFza3MvUmVtb3RlQWdlbnRUYXNrL1JlbW90ZUFnZW50VGFzay5qcydcbmltcG9ydCB7XG4gIHR5cGUgQmFja2dyb3VuZFRhc2tTdGF0ZSxcbiAgaXNCYWNrZ3JvdW5kVGFzayxcbiAgdHlwZSBUYXNrU3RhdGUsXG59IGZyb20gJ3NyYy90YXNrcy90eXBlcy5qcydcbmltcG9ydCB0eXBlIHsgRGVlcEltbXV0YWJsZSB9IGZyb20gJ3NyYy90eXBlcy91dGlscy5qcydcbmltcG9ydCB7IGludGVyc3BlcnNlIH0gZnJvbSAnc3JjL3V0aWxzL2FycmF5LmpzJ1xuaW1wb3J0IHsgVEVBTV9MRUFEX05BTUUgfSBmcm9tICdzcmMvdXRpbHMvc3dhcm0vY29uc3RhbnRzLmpzJ1xuaW1wb3J0IHsgc3RvcFVsdHJhcGxhbiB9IGZyb20gJy4uLy4uL2NvbW1hbmRzL3VsdHJhcGxhbi5qcydcbmltcG9ydCB0eXBlIHsgQ29tbWFuZFJlc3VsdERpc3BsYXkgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IHVzZVJlZ2lzdGVyT3ZlcmxheSB9IGZyb20gJy4uLy4uL2NvbnRleHQvb3ZlcmxheUNvbnRleHQuanMnXG5pbXBvcnQgdHlwZSB7IEV4aXRTdGF0ZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncy5qcydcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uLy4uL2luay9ldmVudHMva2V5Ym9hcmQtZXZlbnQuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5ncyB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyB1c2VTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VTaG9ydGN1dERpc3BsYXkuanMnXG5pbXBvcnQgeyBjb3VudCB9IGZyb20gJy4uLy4uL3V0aWxzL2FycmF5LmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IEFzeW5jQWdlbnREZXRhaWxEaWFsb2cgfSBmcm9tICcuL0FzeW5jQWdlbnREZXRhaWxEaWFsb2cuanMnXG5pbXBvcnQgeyBCYWNrZ3JvdW5kVGFzayBhcyBCYWNrZ3JvdW5kVGFza0NvbXBvbmVudCB9IGZyb20gJy4vQmFja2dyb3VuZFRhc2suanMnXG5pbXBvcnQgeyBEcmVhbURldGFpbERpYWxvZyB9IGZyb20gJy4vRHJlYW1EZXRhaWxEaWFsb2cuanMnXG5pbXBvcnQgeyBJblByb2Nlc3NUZWFtbWF0ZURldGFpbERpYWxvZyB9IGZyb20gJy4vSW5Qcm9jZXNzVGVhbW1hdGVEZXRhaWxEaWFsb2cuanMnXG5pbXBvcnQgeyBSZW1vdGVTZXNzaW9uRGV0YWlsRGlhbG9nIH0gZnJvbSAnLi9SZW1vdGVTZXNzaW9uRGV0YWlsRGlhbG9nLmpzJ1xuaW1wb3J0IHsgU2hlbGxEZXRhaWxEaWFsb2cgfSBmcm9tICcuL1NoZWxsRGV0YWlsRGlhbG9nLmpzJ1xuXG50eXBlIFZpZXdTdGF0ZSA9IHsgbW9kZTogJ2xpc3QnIH0gfCB7IG1vZGU6ICdkZXRhaWwnOyBpdGVtSWQ6IHN0cmluZyB9XG5cbnR5cGUgUHJvcHMgPSB7XG4gIG9uRG9uZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkXG4gIHRvb2xVc2VDb250ZXh0OiBUb29sVXNlQ29udGV4dFxuICBpbml0aWFsRGV0YWlsVGFza0lkPzogc3RyaW5nXG59XG5cbnR5cGUgTGlzdEl0ZW0gPVxuICB8IHtcbiAgICAgIGlkOiBzdHJpbmdcbiAgICAgIHR5cGU6ICdsb2NhbF9iYXNoJ1xuICAgICAgbGFiZWw6IHN0cmluZ1xuICAgICAgc3RhdHVzOiBzdHJpbmdcbiAgICAgIHRhc2s6IERlZXBJbW11dGFibGU8TG9jYWxTaGVsbFRhc2tTdGF0ZT5cbiAgICB9XG4gIHwge1xuICAgICAgaWQ6IHN0cmluZ1xuICAgICAgdHlwZTogJ3JlbW90ZV9hZ2VudCdcbiAgICAgIGxhYmVsOiBzdHJpbmdcbiAgICAgIHN0YXR1czogc3RyaW5nXG4gICAgICB0YXNrOiBEZWVwSW1tdXRhYmxlPFJlbW90ZUFnZW50VGFza1N0YXRlPlxuICAgIH1cbiAgfCB7XG4gICAgICBpZDogc3RyaW5nXG4gICAgICB0eXBlOiAnbG9jYWxfYWdlbnQnXG4gICAgICBsYWJlbDogc3RyaW5nXG4gICAgICBzdGF0dXM6IHN0cmluZ1xuICAgICAgdGFzazogRGVlcEltbXV0YWJsZTxMb2NhbEFnZW50VGFza1N0YXRlPlxuICAgIH1cbiAgfCB7XG4gICAgICBpZDogc3RyaW5nXG4gICAgICB0eXBlOiAnaW5fcHJvY2Vzc190ZWFtbWF0ZSdcbiAgICAgIGxhYmVsOiBzdHJpbmdcbiAgICAgIHN0YXR1czogc3RyaW5nXG4gICAgICB0YXNrOiBEZWVwSW1tdXRhYmxlPEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlPlxuICAgIH1cbiAgfCB7XG4gICAgICBpZDogc3RyaW5nXG4gICAgICB0eXBlOiAnbG9jYWxfd29ya2Zsb3cnXG4gICAgICBsYWJlbDogc3RyaW5nXG4gICAgICBzdGF0dXM6IHN0cmluZ1xuICAgICAgdGFzazogRGVlcEltbXV0YWJsZTxMb2NhbFdvcmtmbG93VGFza1N0YXRlPlxuICAgIH1cbiAgfCB7XG4gICAgICBpZDogc3RyaW5nXG4gICAgICB0eXBlOiAnbW9uaXRvcl9tY3AnXG4gICAgICBsYWJlbDogc3RyaW5nXG4gICAgICBzdGF0dXM6IHN0cmluZ1xuICAgICAgdGFzazogRGVlcEltbXV0YWJsZTxNb25pdG9yTWNwVGFza1N0YXRlPlxuICAgIH1cbiAgfCB7XG4gICAgICBpZDogc3RyaW5nXG4gICAgICB0eXBlOiAnZHJlYW0nXG4gICAgICBsYWJlbDogc3RyaW5nXG4gICAgICBzdGF0dXM6IHN0cmluZ1xuICAgICAgdGFzazogRGVlcEltbXV0YWJsZTxEcmVhbVRhc2tTdGF0ZT5cbiAgICB9XG4gIHwge1xuICAgICAgaWQ6IHN0cmluZ1xuICAgICAgdHlwZTogJ2xlYWRlcidcbiAgICAgIGxhYmVsOiBzdHJpbmdcbiAgICAgIHN0YXR1czogJ3J1bm5pbmcnXG4gICAgfVxuXG4vLyBXT1JLRkxPV19TQ1JJUFRTIGlzIGFudC1vbmx5IChidWlsZF9mbGFncy55YW1sKS4gU3RhdGljIGltcG9ydHMgd291bGQgbGVha1xuLy8gfjEuM0sgbGluZXMgaW50byBleHRlcm5hbCBidWlsZHMuIEdhdGUgd2l0aCBmZWF0dXJlKCkgKyByZXF1aXJlIHNvIHRoZVxuLy8gYnVuZGxlciBjYW4gZGVhZC1jb2RlLWVsaW1pbmF0ZSB0aGUgYnJhbmNoLlxuLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuY29uc3QgV29ya2Zsb3dEZXRhaWxEaWFsb2cgPSBmZWF0dXJlKCdXT1JLRkxPV19TQ1JJUFRTJylcbiAgPyAoXG4gICAgICByZXF1aXJlKCcuL1dvcmtmbG93RGV0YWlsRGlhbG9nLmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi9Xb3JrZmxvd0RldGFpbERpYWxvZy5qcycpXG4gICAgKS5Xb3JrZmxvd0RldGFpbERpYWxvZ1xuICA6IG51bGxcbmNvbnN0IHdvcmtmbG93VGFza01vZHVsZSA9IGZlYXR1cmUoJ1dPUktGTE9XX1NDUklQVFMnKVxuICA/IChyZXF1aXJlKCdzcmMvdGFza3MvTG9jYWxXb3JrZmxvd1Rhc2svTG9jYWxXb3JrZmxvd1Rhc2suanMnKSBhcyB0eXBlb2YgaW1wb3J0KCdzcmMvdGFza3MvTG9jYWxXb3JrZmxvd1Rhc2svTG9jYWxXb3JrZmxvd1Rhc2suanMnKSlcbiAgOiBudWxsXG5jb25zdCBraWxsV29ya2Zsb3dUYXNrID0gd29ya2Zsb3dUYXNrTW9kdWxlPy5raWxsV29ya2Zsb3dUYXNrID8/IG51bGxcbmNvbnN0IHNraXBXb3JrZmxvd0FnZW50ID0gd29ya2Zsb3dUYXNrTW9kdWxlPy5za2lwV29ya2Zsb3dBZ2VudCA/PyBudWxsXG5jb25zdCByZXRyeVdvcmtmbG93QWdlbnQgPSB3b3JrZmxvd1Rhc2tNb2R1bGU/LnJldHJ5V29ya2Zsb3dBZ2VudCA/PyBudWxsXG4vLyBSZWxhdGl2ZSBwYXRoLCBub3QgYHNyYy8uLi5gIHBhdGgtbWFwcGluZyDigJQgQnVuJ3MgRENFIGNhbiBzdGF0aWNhbGx5XG4vLyByZXNvbHZlICsgZWxpbWluYXRlIGAuL2AgcmVxdWlyZXMsIGJ1dCBwYXRoLW1hcHBlZCBzdHJpbmdzIHN0YXkgb3BhcXVlXG4vLyBhbmQgc3Vydml2ZSBhcyBkZWFkIGxpdGVyYWxzIGluIHRoZSBidW5kbGUuIE1hdGNoZXMgdGFza3MudHMgcGF0dGVybi5cbmNvbnN0IG1vbml0b3JNY3BNb2R1bGUgPSBmZWF0dXJlKCdNT05JVE9SX1RPT0wnKVxuICA/IChyZXF1aXJlKCcuLi8uLi90YXNrcy9Nb25pdG9yTWNwVGFzay9Nb25pdG9yTWNwVGFzay5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4uLy4uL3Rhc2tzL01vbml0b3JNY3BUYXNrL01vbml0b3JNY3BUYXNrLmpzJykpXG4gIDogbnVsbFxuY29uc3Qga2lsbE1vbml0b3JNY3AgPSBtb25pdG9yTWNwTW9kdWxlPy5raWxsTW9uaXRvck1jcCA/PyBudWxsXG5jb25zdCBNb25pdG9yTWNwRGV0YWlsRGlhbG9nID0gZmVhdHVyZSgnTU9OSVRPUl9UT09MJylcbiAgPyAoXG4gICAgICByZXF1aXJlKCcuL01vbml0b3JNY3BEZXRhaWxEaWFsb2cuanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuL01vbml0b3JNY3BEZXRhaWxEaWFsb2cuanMnKVxuICAgICkuTW9uaXRvck1jcERldGFpbERpYWxvZ1xuICA6IG51bGxcbi8qIGVzbGludC1lbmFibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuXG4vLyBIZWxwZXIgdG8gZ2V0IGZpbHRlcmVkIGJhY2tncm91bmQgdGFza3MgKGV4Y2x1ZGVzIGZvcmVncm91bmRlZCBsb2NhbF9hZ2VudClcbmZ1bmN0aW9uIGdldFNlbGVjdGFibGVCYWNrZ3JvdW5kVGFza3MoXG4gIHRhc2tzOiBSZWNvcmQ8c3RyaW5nLCBUYXNrU3RhdGU+IHwgdW5kZWZpbmVkLFxuICBmb3JlZ3JvdW5kZWRUYXNrSWQ6IHN0cmluZyB8IHVuZGVmaW5lZCxcbik6IFRhc2tTdGF0ZVtdIHtcbiAgY29uc3QgYmFja2dyb3VuZFRhc2tzID0gT2JqZWN0LnZhbHVlcyh0YXNrcyA/PyB7fSkuZmlsdGVyKGlzQmFja2dyb3VuZFRhc2spXG4gIHJldHVybiBiYWNrZ3JvdW5kVGFza3MuZmlsdGVyKFxuICAgIHRhc2sgPT4gISh0YXNrLnR5cGUgPT09ICdsb2NhbF9hZ2VudCcgJiYgdGFzay5pZCA9PT0gZm9yZWdyb3VuZGVkVGFza0lkKSxcbiAgKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gQmFja2dyb3VuZFRhc2tzRGlhbG9nKHtcbiAgb25Eb25lLFxuICB0b29sVXNlQ29udGV4dCxcbiAgaW5pdGlhbERldGFpbFRhc2tJZCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgdGFza3MgPSB1c2VBcHBTdGF0ZShzID0+IHMudGFza3MpXG4gIGNvbnN0IGZvcmVncm91bmRlZFRhc2tJZCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5mb3JlZ3JvdW5kZWRUYXNrSWQpXG4gIGNvbnN0IHNob3dTcGlubmVyVHJlZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5leHBhbmRlZFZpZXcpID09PSAndGVhbW1hdGVzJ1xuICBjb25zdCBzZXRBcHBTdGF0ZSA9IHVzZVNldEFwcFN0YXRlKClcbiAgY29uc3Qga2lsbEFnZW50c1Nob3J0Y3V0ID0gdXNlU2hvcnRjdXREaXNwbGF5KFxuICAgICdjaGF0OmtpbGxBZ2VudHMnLFxuICAgICdDaGF0JyxcbiAgICAnY3RybCt4IGN0cmwraycsXG4gIClcbiAgY29uc3QgdHlwZWRUYXNrcyA9IHRhc2tzIGFzIFJlY29yZDxzdHJpbmcsIFRhc2tTdGF0ZT4gfCB1bmRlZmluZWRcblxuICAvLyBUcmFjayBpZiB3ZSBza2lwcGVkIGxpc3QgdmlldyBvbiBtb3VudCAoZm9yIGJhY2sgYnV0dG9uIGJlaGF2aW9yKVxuICBjb25zdCBza2lwcGVkTGlzdE9uTW91bnQgPSB1c2VSZWYoZmFsc2UpXG5cbiAgLy8gQ29tcHV0ZSBpbml0aWFsIHZpZXcgc3RhdGUgLSBza2lwIGxpc3QgaWYgY2FsbGVyIHByb3ZpZGVkIGEgc3BlY2lmaWMgdGFzayxcbiAgLy8gb3IgaWYgdGhlcmUncyBleGFjdGx5IG9uZSB0YXNrXG4gIGNvbnN0IFt2aWV3U3RhdGUsIHNldFZpZXdTdGF0ZV0gPSB1c2VTdGF0ZTxWaWV3U3RhdGU+KCgpID0+IHtcbiAgICBpZiAoaW5pdGlhbERldGFpbFRhc2tJZCkge1xuICAgICAgc2tpcHBlZExpc3RPbk1vdW50LmN1cnJlbnQgPSB0cnVlXG4gICAgICByZXR1cm4geyBtb2RlOiAnZGV0YWlsJywgaXRlbUlkOiBpbml0aWFsRGV0YWlsVGFza0lkIH1cbiAgICB9XG4gICAgY29uc3QgYWxsSXRlbXMgPSBnZXRTZWxlY3RhYmxlQmFja2dyb3VuZFRhc2tzKFxuICAgICAgdHlwZWRUYXNrcyxcbiAgICAgIGZvcmVncm91bmRlZFRhc2tJZCxcbiAgICApXG4gICAgaWYgKGFsbEl0ZW1zLmxlbmd0aCA9PT0gMSkge1xuICAgICAgc2tpcHBlZExpc3RPbk1vdW50LmN1cnJlbnQgPSB0cnVlXG4gICAgICByZXR1cm4geyBtb2RlOiAnZGV0YWlsJywgaXRlbUlkOiBhbGxJdGVtc1swXSEuaWQgfVxuICAgIH1cbiAgICByZXR1cm4geyBtb2RlOiAnbGlzdCcgfVxuICB9KVxuICBjb25zdCBbc2VsZWN0ZWRJbmRleCwgc2V0U2VsZWN0ZWRJbmRleF0gPSB1c2VTdGF0ZTxudW1iZXI+KDApXG5cbiAgLy8gUmVnaXN0ZXIgYXMgbW9kYWwgb3ZlcmxheSBzbyBwYXJlbnQgQ2hhdCBrZXliaW5kaW5ncyAodXAvZG93biBmb3IgaGlzdG9yeSlcbiAgLy8gYXJlIGRlYWN0aXZhdGVkIHdoaWxlIHRoaXMgZGlhbG9nIGlzIG9wZW5cbiAgdXNlUmVnaXN0ZXJPdmVybGF5KCdiYWNrZ3JvdW5kLXRhc2tzLWRpYWxvZycpXG5cbiAgLy8gTWVtb2l6ZSB0aGUgc29ydGVkIGFuZCBjYXRlZ29yaXplZCBpdGVtcyB0b2dldGhlciB0byBlbnN1cmUgc3RhYmxlIHJlZmVyZW5jZXNcbiAgY29uc3Qge1xuICAgIGJhc2hUYXNrcyxcbiAgICByZW1vdGVTZXNzaW9ucyxcbiAgICBhZ2VudFRhc2tzLFxuICAgIHRlYW1tYXRlVGFza3MsXG4gICAgd29ya2Zsb3dUYXNrcyxcbiAgICBtY3BNb25pdG9ycyxcbiAgICBkcmVhbVRhc2tzLFxuICAgIGFsbFNlbGVjdGFibGVJdGVtcyxcbiAgfSA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIC8vIEZpbHRlciB0byBvbmx5IHNob3cgcnVubmluZy9wZW5kaW5nIGJhY2tncm91bmQgdGFza3MsIG1hdGNoaW5nIHRoZSBzdGF0dXMgYmFyIGNvdW50XG4gICAgY29uc3QgYmFja2dyb3VuZFRhc2tzID0gT2JqZWN0LnZhbHVlcyh0eXBlZFRhc2tzID8/IHt9KS5maWx0ZXIoXG4gICAgICBpc0JhY2tncm91bmRUYXNrLFxuICAgIClcbiAgICBjb25zdCBhbGxJdGVtcyA9IGJhY2tncm91bmRUYXNrcy5tYXAodG9MaXN0SXRlbSlcbiAgICBjb25zdCBzb3J0ZWQgPSBhbGxJdGVtcy5zb3J0KChhLCBiKSA9PiB7XG4gICAgICBjb25zdCBhU3RhdHVzID0gYS5zdGF0dXNcbiAgICAgIGNvbnN0IGJTdGF0dXMgPSBiLnN0YXR1c1xuICAgICAgaWYgKGFTdGF0dXMgPT09ICdydW5uaW5nJyAmJiBiU3RhdHVzICE9PSAncnVubmluZycpIHJldHVybiAtMVxuICAgICAgaWYgKGFTdGF0dXMgIT09ICdydW5uaW5nJyAmJiBiU3RhdHVzID09PSAncnVubmluZycpIHJldHVybiAxXG4gICAgICBjb25zdCBhVGltZSA9ICd0YXNrJyBpbiBhID8gYS50YXNrLnN0YXJ0VGltZSA6IDBcbiAgICAgIGNvbnN0IGJUaW1lID0gJ3Rhc2snIGluIGIgPyBiLnRhc2suc3RhcnRUaW1lIDogMFxuICAgICAgcmV0dXJuIGJUaW1lIC0gYVRpbWVcbiAgICB9KVxuICAgIGNvbnN0IGJhc2ggPSBzb3J0ZWQuZmlsdGVyKGl0ZW0gPT4gaXRlbS50eXBlID09PSAnbG9jYWxfYmFzaCcpXG4gICAgY29uc3QgcmVtb3RlID0gc29ydGVkLmZpbHRlcihpdGVtID0+IGl0ZW0udHlwZSA9PT0gJ3JlbW90ZV9hZ2VudCcpXG4gICAgLy8gRXhjbHVkZSBmb3JlZ3JvdW5kZWQgdGFzayAtIGl0J3MgYmVpbmcgdmlld2VkIGluIHRoZSBtYWluIFVJLCBub3QgYSBiYWNrZ3JvdW5kIHRhc2tcbiAgICBjb25zdCBhZ2VudCA9IHNvcnRlZC5maWx0ZXIoXG4gICAgICBpdGVtID0+IGl0ZW0udHlwZSA9PT0gJ2xvY2FsX2FnZW50JyAmJiBpdGVtLmlkICE9PSBmb3JlZ3JvdW5kZWRUYXNrSWQsXG4gICAgKVxuICAgIGNvbnN0IHdvcmtmbG93cyA9IHNvcnRlZC5maWx0ZXIoaXRlbSA9PiBpdGVtLnR5cGUgPT09ICdsb2NhbF93b3JrZmxvdycpXG4gICAgY29uc3QgbW9uaXRvck1jcCA9IHNvcnRlZC5maWx0ZXIoaXRlbSA9PiBpdGVtLnR5cGUgPT09ICdtb25pdG9yX21jcCcpXG4gICAgY29uc3QgZHJlYW1UYXNrcyA9IHNvcnRlZC5maWx0ZXIoaXRlbSA9PiBpdGVtLnR5cGUgPT09ICdkcmVhbScpXG4gICAgLy8gSW4gc3Bpbm5lci10cmVlIG1vZGUsIGV4Y2x1ZGUgdGVhbW1hdGVzIGZyb20gdGhlIGRpYWxvZyAodGhleSBhcHBlYXIgaW4gdGhlIHRyZWUpXG4gICAgY29uc3QgdGVhbW1hdGVzID0gc2hvd1NwaW5uZXJUcmVlXG4gICAgICA/IFtdXG4gICAgICA6IHNvcnRlZC5maWx0ZXIoaXRlbSA9PiBpdGVtLnR5cGUgPT09ICdpbl9wcm9jZXNzX3RlYW1tYXRlJylcbiAgICAvLyBBZGQgbGVhZGVyIGVudHJ5IHdoZW4gdGhlcmUgYXJlIHRlYW1tYXRlcywgc28gdXNlcnMgY2FuIGZvcmVncm91bmQgYmFjayB0byBsZWFkZXJcbiAgICBjb25zdCBsZWFkZXJJdGVtOiBMaXN0SXRlbVtdID1cbiAgICAgIHRlYW1tYXRlcy5sZW5ndGggPiAwXG4gICAgICAgID8gW1xuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBpZDogJ19fbGVhZGVyX18nLFxuICAgICAgICAgICAgICB0eXBlOiAnbGVhZGVyJyxcbiAgICAgICAgICAgICAgbGFiZWw6IGBAJHtURUFNX0xFQURfTkFNRX1gLFxuICAgICAgICAgICAgICBzdGF0dXM6ICdydW5uaW5nJyxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgXVxuICAgICAgICA6IFtdXG4gICAgcmV0dXJuIHtcbiAgICAgIGJhc2hUYXNrczogYmFzaCxcbiAgICAgIHJlbW90ZVNlc3Npb25zOiByZW1vdGUsXG4gICAgICBhZ2VudFRhc2tzOiBhZ2VudCxcbiAgICAgIHdvcmtmbG93VGFza3M6IHdvcmtmbG93cyxcbiAgICAgIG1jcE1vbml0b3JzOiBtb25pdG9yTWNwLFxuICAgICAgZHJlYW1UYXNrcyxcbiAgICAgIHRlYW1tYXRlVGFza3M6IFsuLi5sZWFkZXJJdGVtLCAuLi50ZWFtbWF0ZXNdLFxuICAgICAgLy8gT3JkZXIgTVVTVCBtYXRjaCBKU1ggcmVuZGVyIG9yZGVyICh0ZWFtbWF0ZXMgXFx1MjE5MiBiYXNoIFxcdTIxOTIgbW9uaXRvck1jcCBcXHUyMTkyXG4gICAgICAvLyByZW1vdGUgXFx1MjE5MiBhZ2VudCBcXHUyMTkyIHdvcmtmbG93cyBcXHUyMTkyIGRyZWFtKSBzbyBcXHUyMTkzL1xcdTIxOTEgbmF2aWdhdGlvbiBtb3ZlcyB0aGUgY3Vyc29yXG4gICAgICAvLyB2aXN1YWxseSBkb3dud2FyZC5cbiAgICAgIGFsbFNlbGVjdGFibGVJdGVtczogW1xuICAgICAgICAuLi5sZWFkZXJJdGVtLFxuICAgICAgICAuLi50ZWFtbWF0ZXMsXG4gICAgICAgIC4uLmJhc2gsXG4gICAgICAgIC4uLm1vbml0b3JNY3AsXG4gICAgICAgIC4uLnJlbW90ZSxcbiAgICAgICAgLi4uYWdlbnQsXG4gICAgICAgIC4uLndvcmtmbG93cyxcbiAgICAgICAgLi4uZHJlYW1UYXNrcyxcbiAgICAgIF0sXG4gICAgfVxuICB9LCBbdHlwZWRUYXNrcywgZm9yZWdyb3VuZGVkVGFza0lkLCBzaG93U3Bpbm5lclRyZWVdKVxuXG4gIGNvbnN0IGN1cnJlbnRTZWxlY3Rpb24gPSBhbGxTZWxlY3RhYmxlSXRlbXNbc2VsZWN0ZWRJbmRleF0gPz8gbnVsbFxuXG4gIC8vIFVzZSBjb25maWd1cmFibGUga2V5YmluZGluZ3MgZm9yIHN0YW5kYXJkIG5hdmlnYXRpb24gYW5kIGNvbmZpcm0vY2FuY2VsLlxuICAvLyBjb25maXJtOm5vIGlzIGhhbmRsZWQgYnkgRGlhbG9nJ3Mgb25DYW5jZWwgcHJvcC5cbiAgdXNlS2V5YmluZGluZ3MoXG4gICAge1xuICAgICAgJ2NvbmZpcm06cHJldmlvdXMnOiAoKSA9PiBzZXRTZWxlY3RlZEluZGV4KHByZXYgPT4gTWF0aC5tYXgoMCwgcHJldiAtIDEpKSxcbiAgICAgICdjb25maXJtOm5leHQnOiAoKSA9PlxuICAgICAgICBzZXRTZWxlY3RlZEluZGV4KHByZXYgPT5cbiAgICAgICAgICBNYXRoLm1pbihhbGxTZWxlY3RhYmxlSXRlbXMubGVuZ3RoIC0gMSwgcHJldiArIDEpLFxuICAgICAgICApLFxuICAgICAgJ2NvbmZpcm06eWVzJzogKCkgPT4ge1xuICAgICAgICBjb25zdCBjdXJyZW50ID0gYWxsU2VsZWN0YWJsZUl0ZW1zW3NlbGVjdGVkSW5kZXhdXG4gICAgICAgIGlmIChjdXJyZW50KSB7XG4gICAgICAgICAgaWYgKGN1cnJlbnQudHlwZSA9PT0gJ2xlYWRlcicpIHtcbiAgICAgICAgICAgIGV4aXRUZWFtbWF0ZVZpZXcoc2V0QXBwU3RhdGUpXG4gICAgICAgICAgICBvbkRvbmUoJ1ZpZXdpbmcgbGVhZGVyJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBzZXRWaWV3U3RhdGUoeyBtb2RlOiAnZGV0YWlsJywgaXRlbUlkOiBjdXJyZW50LmlkIH0pXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9LFxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJywgaXNBY3RpdmU6IHZpZXdTdGF0ZS5tb2RlID09PSAnbGlzdCcgfSxcbiAgKVxuXG4gIC8vIENvbXBvbmVudC1zcGVjaWZpYyBzaG9ydGN1dHMgKHg9c3RvcCwgZj1mb3JlZ3JvdW5kLCByaWdodD16b29tKSBzaG93biBpbiBVSS5cbiAgLy8gVGhlc2UgYXJlIHRhc2stdHlwZSBhbmQgc3RhdHVzIGRlcGVuZGVudCwgbm90IHN0YW5kYXJkIGRpYWxvZyBrZXliaW5kaW5ncy5cbiAgY29uc3QgaGFuZGxlS2V5RG93biA9IChlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgLy8gT25seSBoYW5kbGUgaW5wdXQgd2hlbiBpbiBsaXN0IG1vZGVcbiAgICBpZiAodmlld1N0YXRlLm1vZGUgIT09ICdsaXN0JykgcmV0dXJuXG5cbiAgICBpZiAoZS5rZXkgPT09ICdsZWZ0Jykge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBvbkRvbmUoJ0JhY2tncm91bmQgdGFza3MgZGlhbG9nIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIENvbXB1dGUgY3VycmVudCBzZWxlY3Rpb24gYXQgdGhlIHRpbWUgb2YgdGhlIGtleSBwcmVzc1xuICAgIGNvbnN0IGN1cnJlbnRTZWxlY3Rpb24gPSBhbGxTZWxlY3RhYmxlSXRlbXNbc2VsZWN0ZWRJbmRleF1cbiAgICBpZiAoIWN1cnJlbnRTZWxlY3Rpb24pIHJldHVybiAvLyBldmVyeXRoaW5nIGJlbG93IHJlcXVpcmVzIGEgc2VsZWN0aW9uXG5cbiAgICBpZiAoZS5rZXkgPT09ICd4Jykge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBpZiAoXG4gICAgICAgIGN1cnJlbnRTZWxlY3Rpb24udHlwZSA9PT0gJ2xvY2FsX2Jhc2gnICYmXG4gICAgICAgIGN1cnJlbnRTZWxlY3Rpb24uc3RhdHVzID09PSAncnVubmluZydcbiAgICAgICkge1xuICAgICAgICB2b2lkIGtpbGxTaGVsbFRhc2soY3VycmVudFNlbGVjdGlvbi5pZClcbiAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgIGN1cnJlbnRTZWxlY3Rpb24udHlwZSA9PT0gJ2xvY2FsX2FnZW50JyAmJlxuICAgICAgICBjdXJyZW50U2VsZWN0aW9uLnN0YXR1cyA9PT0gJ3J1bm5pbmcnXG4gICAgICApIHtcbiAgICAgICAgdm9pZCBraWxsQWdlbnRUYXNrKGN1cnJlbnRTZWxlY3Rpb24uaWQpXG4gICAgICB9IGVsc2UgaWYgKFxuICAgICAgICBjdXJyZW50U2VsZWN0aW9uLnR5cGUgPT09ICdpbl9wcm9jZXNzX3RlYW1tYXRlJyAmJlxuICAgICAgICBjdXJyZW50U2VsZWN0aW9uLnN0YXR1cyA9PT0gJ3J1bm5pbmcnXG4gICAgICApIHtcbiAgICAgICAgdm9pZCBraWxsVGVhbW1hdGVUYXNrKGN1cnJlbnRTZWxlY3Rpb24uaWQpXG4gICAgICB9IGVsc2UgaWYgKFxuICAgICAgICBjdXJyZW50U2VsZWN0aW9uLnR5cGUgPT09ICdsb2NhbF93b3JrZmxvdycgJiZcbiAgICAgICAgY3VycmVudFNlbGVjdGlvbi5zdGF0dXMgPT09ICdydW5uaW5nJyAmJlxuICAgICAgICBraWxsV29ya2Zsb3dUYXNrXG4gICAgICApIHtcbiAgICAgICAga2lsbFdvcmtmbG93VGFzayhjdXJyZW50U2VsZWN0aW9uLmlkLCBzZXRBcHBTdGF0ZSlcbiAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgIGN1cnJlbnRTZWxlY3Rpb24udHlwZSA9PT0gJ21vbml0b3JfbWNwJyAmJlxuICAgICAgICBjdXJyZW50U2VsZWN0aW9uLnN0YXR1cyA9PT0gJ3J1bm5pbmcnICYmXG4gICAgICAgIGtpbGxNb25pdG9yTWNwXG4gICAgICApIHtcbiAgICAgICAga2lsbE1vbml0b3JNY3AoY3VycmVudFNlbGVjdGlvbi5pZCwgc2V0QXBwU3RhdGUpXG4gICAgICB9IGVsc2UgaWYgKFxuICAgICAgICBjdXJyZW50U2VsZWN0aW9uLnR5cGUgPT09ICdkcmVhbScgJiZcbiAgICAgICAgY3VycmVudFNlbGVjdGlvbi5zdGF0dXMgPT09ICdydW5uaW5nJ1xuICAgICAgKSB7XG4gICAgICAgIHZvaWQga2lsbERyZWFtVGFzayhjdXJyZW50U2VsZWN0aW9uLmlkKVxuICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgY3VycmVudFNlbGVjdGlvbi50eXBlID09PSAncmVtb3RlX2FnZW50JyAmJlxuICAgICAgICBjdXJyZW50U2VsZWN0aW9uLnN0YXR1cyA9PT0gJ3J1bm5pbmcnXG4gICAgICApIHtcbiAgICAgICAgaWYgKGN1cnJlbnRTZWxlY3Rpb24udGFzay5pc1VsdHJhcGxhbikge1xuICAgICAgICAgIHZvaWQgc3RvcFVsdHJhcGxhbihcbiAgICAgICAgICAgIGN1cnJlbnRTZWxlY3Rpb24uaWQsXG4gICAgICAgICAgICBjdXJyZW50U2VsZWN0aW9uLnRhc2suc2Vzc2lvbklkLFxuICAgICAgICAgICAgc2V0QXBwU3RhdGUsXG4gICAgICAgICAgKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHZvaWQga2lsbFJlbW90ZUFnZW50VGFzayhjdXJyZW50U2VsZWN0aW9uLmlkKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKGUua2V5ID09PSAnZicpIHtcbiAgICAgIGlmIChcbiAgICAgICAgY3VycmVudFNlbGVjdGlvbi50eXBlID09PSAnaW5fcHJvY2Vzc190ZWFtbWF0ZScgJiZcbiAgICAgICAgY3VycmVudFNlbGVjdGlvbi5zdGF0dXMgPT09ICdydW5uaW5nJ1xuICAgICAgKSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgICBlbnRlclRlYW1tYXRlVmlldyhjdXJyZW50U2VsZWN0aW9uLmlkLCBzZXRBcHBTdGF0ZSlcbiAgICAgICAgb25Eb25lKCdWaWV3aW5nIHRlYW1tYXRlJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgICAgfSBlbHNlIGlmIChjdXJyZW50U2VsZWN0aW9uLnR5cGUgPT09ICdsZWFkZXInKSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgICBleGl0VGVhbW1hdGVWaWV3KHNldEFwcFN0YXRlKVxuICAgICAgICBvbkRvbmUoJ1ZpZXdpbmcgbGVhZGVyJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGZ1bmN0aW9uIGtpbGxTaGVsbFRhc2sodGFza0lkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCBMb2NhbFNoZWxsVGFzay5raWxsKHRhc2tJZCwgc2V0QXBwU3RhdGUpXG4gIH1cblxuICBhc3luYyBmdW5jdGlvbiBraWxsQWdlbnRUYXNrKHRhc2tJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgTG9jYWxBZ2VudFRhc2sua2lsbCh0YXNrSWQsIHNldEFwcFN0YXRlKVxuICB9XG5cbiAgYXN5bmMgZnVuY3Rpb24ga2lsbFRlYW1tYXRlVGFzayh0YXNrSWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IEluUHJvY2Vzc1RlYW1tYXRlVGFzay5raWxsKHRhc2tJZCwgc2V0QXBwU3RhdGUpXG4gIH1cblxuICBhc3luYyBmdW5jdGlvbiBraWxsRHJlYW1UYXNrKHRhc2tJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYXdhaXQgRHJlYW1UYXNrLmtpbGwodGFza0lkLCBzZXRBcHBTdGF0ZSlcbiAgfVxuXG4gIGFzeW5jIGZ1bmN0aW9uIGtpbGxSZW1vdGVBZ2VudFRhc2sodGFza0lkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBhd2FpdCBSZW1vdGVBZ2VudFRhc2sua2lsbCh0YXNrSWQsIHNldEFwcFN0YXRlKVxuICB9XG5cbiAgLy8gV3JhcCBvbkRvbmUgaW4gdXNlRWZmZWN0RXZlbnQgdG8gZ2V0IGEgc3RhYmxlIHJlZmVyZW5jZSB0aGF0IGFsd2F5cyBjYWxsc1xuICAvLyB0aGUgY3VycmVudCBvbkRvbmUgY2FsbGJhY2sgd2l0aG91dCBjYXVzaW5nIHRoZSBlZmZlY3QgdG8gcmUtZmlyZS5cbiAgY29uc3Qgb25Eb25lRXZlbnQgPSB1c2VFZmZlY3RFdmVudChvbkRvbmUpXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAodmlld1N0YXRlLm1vZGUgIT09ICdsaXN0Jykge1xuICAgICAgY29uc3QgdGFzayA9ICh0eXBlZFRhc2tzID8/IHt9KVt2aWV3U3RhdGUuaXRlbUlkXVxuICAgICAgLy8gV29ya2Zsb3cgdGFza3MgZ2V0IGEgZ3JhY2U6IHRoZWlyIGRldGFpbCB2aWV3IHN0YXlzIG9wZW4gdGhyb3VnaFxuICAgICAgLy8gY29tcGxldGlvbiBzbyB0aGUgdXNlciBzZWVzIHRoZSBmaW5hbCBzdGF0ZSBiZWZvcmUgZXZpY3Rpb24uXG4gICAgICBpZiAoXG4gICAgICAgICF0YXNrIHx8XG4gICAgICAgICh0YXNrLnR5cGUgIT09ICdsb2NhbF93b3JrZmxvdycgJiYgIWlzQmFja2dyb3VuZFRhc2sodGFzaykpXG4gICAgICApIHtcbiAgICAgICAgLy8gVGFzayB3YXMgcmVtb3ZlZCBvciBpcyBubyBsb25nZXIgYSBiYWNrZ3JvdW5kIHRhc2sgKGUuZy4ga2lsbGVkKS5cbiAgICAgICAgLy8gSWYgd2Ugc2tpcHBlZCB0aGUgbGlzdCBvbiBtb3VudCwgY2xvc2UgdGhlIGRpYWxvZyBlbnRpcmVseS5cbiAgICAgICAgaWYgKHNraXBwZWRMaXN0T25Nb3VudC5jdXJyZW50KSB7XG4gICAgICAgICAgb25Eb25lRXZlbnQoJ0JhY2tncm91bmQgdGFza3MgZGlhbG9nIGRpc21pc3NlZCcsIHtcbiAgICAgICAgICAgIGRpc3BsYXk6ICdzeXN0ZW0nLFxuICAgICAgICAgIH0pXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgc2V0Vmlld1N0YXRlKHsgbW9kZTogJ2xpc3QnIH0pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCB0b3RhbEl0ZW1zID0gYWxsU2VsZWN0YWJsZUl0ZW1zLmxlbmd0aFxuICAgIGlmIChzZWxlY3RlZEluZGV4ID49IHRvdGFsSXRlbXMgJiYgdG90YWxJdGVtcyA+IDApIHtcbiAgICAgIHNldFNlbGVjdGVkSW5kZXgodG90YWxJdGVtcyAtIDEpXG4gICAgfVxuICB9LCBbdmlld1N0YXRlLCB0eXBlZFRhc2tzLCBzZWxlY3RlZEluZGV4LCBhbGxTZWxlY3RhYmxlSXRlbXMsIG9uRG9uZUV2ZW50XSlcblxuICAvLyBIZWxwZXIgdG8gZ28gYmFjayB0byBsaXN0IHZpZXcgKG9yIGNsb3NlIGRpYWxvZyBpZiB3ZSBza2lwcGVkIGxpc3Qgb25cbiAgLy8gbW91bnQgQU5EIHRoZXJlJ3Mgc3RpbGwgb25seSDiiaQxIGl0ZW0pLiBDaGVja2luZyBjdXJyZW50IGNvdW50IHByZXZlbnRzXG4gIC8vIHRoZSBzdGFsZS1zdGF0ZSB0cmFwOiBpZiB5b3Ugb3BlbmVkIHdpdGggMSB0YXNrIChhdXRvLXNraXBwZWQgdG8gZGV0YWlsKSxcbiAgLy8gdGhlbiBhIHNlY29uZCB0YXNrIHN0YXJ0ZWQsICdiYWNrJyBzaG91bGQgc2hvdyB0aGUgbGlzdCDigJQgbm90IGNsb3NlLlxuICBjb25zdCBnb0JhY2tUb0xpc3QgPSAoKSA9PiB7XG4gICAgaWYgKHNraXBwZWRMaXN0T25Nb3VudC5jdXJyZW50ICYmIGFsbFNlbGVjdGFibGVJdGVtcy5sZW5ndGggPD0gMSkge1xuICAgICAgb25Eb25lKCdCYWNrZ3JvdW5kIHRhc2tzIGRpYWxvZyBkaXNtaXNzZWQnLCB7IGRpc3BsYXk6ICdzeXN0ZW0nIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHNraXBwZWRMaXN0T25Nb3VudC5jdXJyZW50ID0gZmFsc2VcbiAgICAgIHNldFZpZXdTdGF0ZSh7IG1vZGU6ICdsaXN0JyB9KVxuICAgIH1cbiAgfVxuXG4gIC8vIElmIGFuIGl0ZW0gaXMgc2VsZWN0ZWQsIHNob3cgdGhlIGFwcHJvcHJpYXRlIHZpZXdcbiAgaWYgKHZpZXdTdGF0ZS5tb2RlICE9PSAnbGlzdCcgJiYgdHlwZWRUYXNrcykge1xuICAgIGNvbnN0IHRhc2sgPSB0eXBlZFRhc2tzW3ZpZXdTdGF0ZS5pdGVtSWRdXG4gICAgaWYgKCF0YXNrKSB7XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cblxuICAgIC8vIERldGFpbCBtb2RlIC0gc2hvdyBhcHByb3ByaWF0ZSBkZXRhaWwgZGlhbG9nXG4gICAgc3dpdGNoICh0YXNrLnR5cGUpIHtcbiAgICAgIGNhc2UgJ2xvY2FsX2Jhc2gnOlxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIDxTaGVsbERldGFpbERpYWxvZ1xuICAgICAgICAgICAgc2hlbGw9e3Rhc2t9XG4gICAgICAgICAgICBvbkRvbmU9e29uRG9uZX1cbiAgICAgICAgICAgIG9uS2lsbFNoZWxsPXsoKSA9PiB2b2lkIGtpbGxTaGVsbFRhc2sodGFzay5pZCl9XG4gICAgICAgICAgICBvbkJhY2s9e2dvQmFja1RvTGlzdH1cbiAgICAgICAgICAgIGtleT17YHNoZWxsLSR7dGFzay5pZH1gfVxuICAgICAgICAgIC8+XG4gICAgICAgIClcbiAgICAgIGNhc2UgJ2xvY2FsX2FnZW50JzpcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICA8QXN5bmNBZ2VudERldGFpbERpYWxvZ1xuICAgICAgICAgICAgYWdlbnQ9e3Rhc2t9XG4gICAgICAgICAgICBvbkRvbmU9e29uRG9uZX1cbiAgICAgICAgICAgIG9uS2lsbEFnZW50PXsoKSA9PiB2b2lkIGtpbGxBZ2VudFRhc2sodGFzay5pZCl9XG4gICAgICAgICAgICBvbkJhY2s9e2dvQmFja1RvTGlzdH1cbiAgICAgICAgICAgIGtleT17YGFnZW50LSR7dGFzay5pZH1gfVxuICAgICAgICAgIC8+XG4gICAgICAgIClcbiAgICAgIGNhc2UgJ3JlbW90ZV9hZ2VudCc6XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgPFJlbW90ZVNlc3Npb25EZXRhaWxEaWFsb2dcbiAgICAgICAgICAgIHNlc3Npb249e3Rhc2t9XG4gICAgICAgICAgICBvbkRvbmU9e29uRG9uZX1cbiAgICAgICAgICAgIHRvb2xVc2VDb250ZXh0PXt0b29sVXNlQ29udGV4dH1cbiAgICAgICAgICAgIG9uQmFjaz17Z29CYWNrVG9MaXN0fVxuICAgICAgICAgICAgb25LaWxsPXtcbiAgICAgICAgICAgICAgdGFzay5zdGF0dXMgIT09ICdydW5uaW5nJ1xuICAgICAgICAgICAgICAgID8gdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgOiB0YXNrLmlzVWx0cmFwbGFuXG4gICAgICAgICAgICAgICAgICA/ICgpID0+XG4gICAgICAgICAgICAgICAgICAgICAgdm9pZCBzdG9wVWx0cmFwbGFuKHRhc2suaWQsIHRhc2suc2Vzc2lvbklkLCBzZXRBcHBTdGF0ZSlcbiAgICAgICAgICAgICAgICAgIDogKCkgPT4gdm9pZCBraWxsUmVtb3RlQWdlbnRUYXNrKHRhc2suaWQpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBrZXk9e2BzZXNzaW9uLSR7dGFzay5pZH1gfVxuICAgICAgICAgIC8+XG4gICAgICAgIClcbiAgICAgIGNhc2UgJ2luX3Byb2Nlc3NfdGVhbW1hdGUnOlxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIDxJblByb2Nlc3NUZWFtbWF0ZURldGFpbERpYWxvZ1xuICAgICAgICAgICAgdGVhbW1hdGU9e3Rhc2t9XG4gICAgICAgICAgICBvbkRvbmU9e29uRG9uZX1cbiAgICAgICAgICAgIG9uS2lsbD17XG4gICAgICAgICAgICAgIHRhc2suc3RhdHVzID09PSAncnVubmluZydcbiAgICAgICAgICAgICAgICA/ICgpID0+IHZvaWQga2lsbFRlYW1tYXRlVGFzayh0YXNrLmlkKVxuICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBvbkJhY2s9e2dvQmFja1RvTGlzdH1cbiAgICAgICAgICAgIG9uRm9yZWdyb3VuZD17XG4gICAgICAgICAgICAgIHRhc2suc3RhdHVzID09PSAncnVubmluZydcbiAgICAgICAgICAgICAgICA/ICgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgZW50ZXJUZWFtbWF0ZVZpZXcodGFzay5pZCwgc2V0QXBwU3RhdGUpXG4gICAgICAgICAgICAgICAgICAgIG9uRG9uZSgnVmlld2luZyB0ZWFtbWF0ZScsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAga2V5PXtgdGVhbW1hdGUtJHt0YXNrLmlkfWB9XG4gICAgICAgICAgLz5cbiAgICAgICAgKVxuICAgICAgY2FzZSAnbG9jYWxfd29ya2Zsb3cnOlxuICAgICAgICBpZiAoIVdvcmtmbG93RGV0YWlsRGlhbG9nKSByZXR1cm4gbnVsbFxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIDxXb3JrZmxvd0RldGFpbERpYWxvZ1xuICAgICAgICAgICAgd29ya2Zsb3c9e3Rhc2t9XG4gICAgICAgICAgICBvbkRvbmU9e29uRG9uZX1cbiAgICAgICAgICAgIG9uS2lsbD17XG4gICAgICAgICAgICAgIHRhc2suc3RhdHVzID09PSAncnVubmluZycgJiYga2lsbFdvcmtmbG93VGFza1xuICAgICAgICAgICAgICAgID8gKCkgPT4ga2lsbFdvcmtmbG93VGFzayh0YXNrLmlkLCBzZXRBcHBTdGF0ZSlcbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgb25Ta2lwQWdlbnQ9e1xuICAgICAgICAgICAgICB0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnICYmIHNraXBXb3JrZmxvd0FnZW50XG4gICAgICAgICAgICAgICAgPyBhZ2VudElkID0+IHNraXBXb3JrZmxvd0FnZW50KHRhc2suaWQsIGFnZW50SWQsIHNldEFwcFN0YXRlKVxuICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBvblJldHJ5QWdlbnQ9e1xuICAgICAgICAgICAgICB0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnICYmIHJldHJ5V29ya2Zsb3dBZ2VudFxuICAgICAgICAgICAgICAgID8gYWdlbnRJZCA9PiByZXRyeVdvcmtmbG93QWdlbnQodGFzay5pZCwgYWdlbnRJZCwgc2V0QXBwU3RhdGUpXG4gICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG9uQmFjaz17Z29CYWNrVG9MaXN0fVxuICAgICAgICAgICAga2V5PXtgd29ya2Zsb3ctJHt0YXNrLmlkfWB9XG4gICAgICAgICAgLz5cbiAgICAgICAgKVxuICAgICAgY2FzZSAnbW9uaXRvcl9tY3AnOlxuICAgICAgICBpZiAoIU1vbml0b3JNY3BEZXRhaWxEaWFsb2cpIHJldHVybiBudWxsXG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgPE1vbml0b3JNY3BEZXRhaWxEaWFsb2dcbiAgICAgICAgICAgIHRhc2s9e3Rhc2t9XG4gICAgICAgICAgICBvbktpbGw9e1xuICAgICAgICAgICAgICB0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnICYmIGtpbGxNb25pdG9yTWNwXG4gICAgICAgICAgICAgICAgPyAoKSA9PiBraWxsTW9uaXRvck1jcCh0YXNrLmlkLCBzZXRBcHBTdGF0ZSlcbiAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgb25CYWNrPXtnb0JhY2tUb0xpc3R9XG4gICAgICAgICAgICBrZXk9e2Btb25pdG9yLW1jcC0ke3Rhc2suaWR9YH1cbiAgICAgICAgICAvPlxuICAgICAgICApXG4gICAgICBjYXNlICdkcmVhbSc6XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgPERyZWFtRGV0YWlsRGlhbG9nXG4gICAgICAgICAgICB0YXNrPXt0YXNrfVxuICAgICAgICAgICAgb25Eb25lPXsoKSA9PlxuICAgICAgICAgICAgICBvbkRvbmUoJ0JhY2tncm91bmQgdGFza3MgZGlhbG9nIGRpc21pc3NlZCcsIHtcbiAgICAgICAgICAgICAgICBkaXNwbGF5OiAnc3lzdGVtJyxcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIG9uQmFjaz17Z29CYWNrVG9MaXN0fVxuICAgICAgICAgICAgb25LaWxsPXtcbiAgICAgICAgICAgICAgdGFzay5zdGF0dXMgPT09ICdydW5uaW5nJ1xuICAgICAgICAgICAgICAgID8gKCkgPT4gdm9pZCBraWxsRHJlYW1UYXNrKHRhc2suaWQpXG4gICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGtleT17YGRyZWFtLSR7dGFzay5pZH1gfVxuICAgICAgICAgIC8+XG4gICAgICAgIClcbiAgICB9XG4gIH1cblxuICBjb25zdCBydW5uaW5nQmFzaENvdW50ID0gY291bnQoYmFzaFRhc2tzLCBfID0+IF8uc3RhdHVzID09PSAncnVubmluZycpXG4gIGNvbnN0IHJ1bm5pbmdBZ2VudENvdW50ID1cbiAgICBjb3VudChcbiAgICAgIHJlbW90ZVNlc3Npb25zLFxuICAgICAgXyA9PiBfLnN0YXR1cyA9PT0gJ3J1bm5pbmcnIHx8IF8uc3RhdHVzID09PSAncGVuZGluZycsXG4gICAgKSArIGNvdW50KGFnZW50VGFza3MsIF8gPT4gXy5zdGF0dXMgPT09ICdydW5uaW5nJylcbiAgY29uc3QgcnVubmluZ1RlYW1tYXRlQ291bnQgPSBjb3VudCh0ZWFtbWF0ZVRhc2tzLCBfID0+IF8uc3RhdHVzID09PSAncnVubmluZycpXG4gIGNvbnN0IHN1YnRpdGxlID0gaW50ZXJzcGVyc2UoXG4gICAgW1xuICAgICAgLi4uKHJ1bm5pbmdUZWFtbWF0ZUNvdW50ID4gMFxuICAgICAgICA/IFtcbiAgICAgICAgICAgIDxUZXh0IGtleT1cInRlYW1tYXRlc1wiPlxuICAgICAgICAgICAgICB7cnVubmluZ1RlYW1tYXRlQ291bnR9eycgJ31cbiAgICAgICAgICAgICAge3J1bm5pbmdUZWFtbWF0ZUNvdW50ICE9PSAxID8gJ2FnZW50cycgOiAnYWdlbnQnfVxuICAgICAgICAgICAgPC9UZXh0PixcbiAgICAgICAgICBdXG4gICAgICAgIDogW10pLFxuICAgICAgLi4uKHJ1bm5pbmdCYXNoQ291bnQgPiAwXG4gICAgICAgID8gW1xuICAgICAgICAgICAgPFRleHQga2V5PVwic2hlbGxzXCI+XG4gICAgICAgICAgICAgIHtydW5uaW5nQmFzaENvdW50fXsnICd9XG4gICAgICAgICAgICAgIHtydW5uaW5nQmFzaENvdW50ICE9PSAxID8gJ2FjdGl2ZSBzaGVsbHMnIDogJ2FjdGl2ZSBzaGVsbCd9XG4gICAgICAgICAgICA8L1RleHQ+LFxuICAgICAgICAgIF1cbiAgICAgICAgOiBbXSksXG4gICAgICAuLi4ocnVubmluZ0FnZW50Q291bnQgPiAwXG4gICAgICAgID8gW1xuICAgICAgICAgICAgPFRleHQga2V5PVwiYWdlbnRzXCI+XG4gICAgICAgICAgICAgIHtydW5uaW5nQWdlbnRDb3VudH17JyAnfVxuICAgICAgICAgICAgICB7cnVubmluZ0FnZW50Q291bnQgIT09IDEgPyAnYWN0aXZlIGFnZW50cycgOiAnYWN0aXZlIGFnZW50J31cbiAgICAgICAgICAgIDwvVGV4dD4sXG4gICAgICAgICAgXVxuICAgICAgICA6IFtdKSxcbiAgICBdLFxuICAgIGluZGV4ID0+IDxUZXh0IGtleT17YHNlcGFyYXRvci0ke2luZGV4fWB9PiDCtyA8L1RleHQ+LFxuICApXG5cbiAgY29uc3QgYWN0aW9ucyA9IFtcbiAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQga2V5PVwidXBEb3duXCIgc2hvcnRjdXQ9XCLihpEv4oaTXCIgYWN0aW9uPVwic2VsZWN0XCIgLz4sXG4gICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IGtleT1cImVudGVyXCIgc2hvcnRjdXQ9XCJFbnRlclwiIGFjdGlvbj1cInZpZXdcIiAvPixcbiAgICAuLi4oY3VycmVudFNlbGVjdGlvbj8udHlwZSA9PT0gJ2luX3Byb2Nlc3NfdGVhbW1hdGUnICYmXG4gICAgY3VycmVudFNlbGVjdGlvbi5zdGF0dXMgPT09ICdydW5uaW5nJ1xuICAgICAgPyBbXG4gICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50XG4gICAgICAgICAgICBrZXk9XCJmb3JlZ3JvdW5kXCJcbiAgICAgICAgICAgIHNob3J0Y3V0PVwiZlwiXG4gICAgICAgICAgICBhY3Rpb249XCJmb3JlZ3JvdW5kXCJcbiAgICAgICAgICAvPixcbiAgICAgICAgXVxuICAgICAgOiBbXSksXG4gICAgLi4uKChjdXJyZW50U2VsZWN0aW9uPy50eXBlID09PSAnbG9jYWxfYmFzaCcgfHxcbiAgICAgIGN1cnJlbnRTZWxlY3Rpb24/LnR5cGUgPT09ICdsb2NhbF9hZ2VudCcgfHxcbiAgICAgIGN1cnJlbnRTZWxlY3Rpb24/LnR5cGUgPT09ICdpbl9wcm9jZXNzX3RlYW1tYXRlJyB8fFxuICAgICAgY3VycmVudFNlbGVjdGlvbj8udHlwZSA9PT0gJ2xvY2FsX3dvcmtmbG93JyB8fFxuICAgICAgY3VycmVudFNlbGVjdGlvbj8udHlwZSA9PT0gJ21vbml0b3JfbWNwJyB8fFxuICAgICAgY3VycmVudFNlbGVjdGlvbj8udHlwZSA9PT0gJ2RyZWFtJyB8fFxuICAgICAgY3VycmVudFNlbGVjdGlvbj8udHlwZSA9PT0gJ3JlbW90ZV9hZ2VudCcpICYmXG4gICAgY3VycmVudFNlbGVjdGlvbi5zdGF0dXMgPT09ICdydW5uaW5nJ1xuICAgICAgPyBbPEtleWJvYXJkU2hvcnRjdXRIaW50IGtleT1cImtpbGxcIiBzaG9ydGN1dD1cInhcIiBhY3Rpb249XCJzdG9wXCIgLz5dXG4gICAgICA6IFtdKSxcbiAgICAuLi4oYWdlbnRUYXNrcy5zb21lKHQgPT4gdC5zdGF0dXMgPT09ICdydW5uaW5nJylcbiAgICAgID8gW1xuICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludFxuICAgICAgICAgICAga2V5PVwia2lsbC1hbGxcIlxuICAgICAgICAgICAgc2hvcnRjdXQ9e2tpbGxBZ2VudHNTaG9ydGN1dH1cbiAgICAgICAgICAgIGFjdGlvbj1cInN0b3AgYWxsIGFnZW50c1wiXG4gICAgICAgICAgLz4sXG4gICAgICAgIF1cbiAgICAgIDogW10pLFxuICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBrZXk9XCJlc2NcIiBzaG9ydGN1dD1cIuKGkC9Fc2NcIiBhY3Rpb249XCJjbG9zZVwiIC8+LFxuICBdXG5cbiAgY29uc3QgaGFuZGxlQ2FuY2VsID0gKCkgPT5cbiAgICBvbkRvbmUoJ0JhY2tncm91bmQgdGFza3MgZGlhbG9nIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcblxuICBmdW5jdGlvbiByZW5kZXJJbnB1dEd1aWRlKGV4aXRTdGF0ZTogRXhpdFN0YXRlKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgICBpZiAoZXhpdFN0YXRlLnBlbmRpbmcpIHtcbiAgICAgIHJldHVybiA8VGV4dD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8L1RleHQ+XG4gICAgfVxuICAgIHJldHVybiA8QnlsaW5lPnthY3Rpb25zfTwvQnlsaW5lPlxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgIHRhYkluZGV4PXswfVxuICAgICAgYXV0b0ZvY3VzXG4gICAgICBvbktleURvd249e2hhbmRsZUtleURvd259XG4gICAgPlxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT1cIkJhY2tncm91bmQgdGFza3NcIlxuICAgICAgICBzdWJ0aXRsZT17PD57c3VidGl0bGV9PC8+fVxuICAgICAgICBvbkNhbmNlbD17aGFuZGxlQ2FuY2VsfVxuICAgICAgICBjb2xvcj1cImJhY2tncm91bmRcIlxuICAgICAgICBpbnB1dEd1aWRlPXtyZW5kZXJJbnB1dEd1aWRlfVxuICAgICAgPlxuICAgICAgICB7YWxsU2VsZWN0YWJsZUl0ZW1zLmxlbmd0aCA9PT0gMCA/IChcbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5ObyB0YXNrcyBjdXJyZW50bHkgcnVubmluZzwvVGV4dD5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIHt0ZWFtbWF0ZVRhc2tzLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICB7KGJhc2hUYXNrcy5sZW5ndGggPiAwIHx8XG4gICAgICAgICAgICAgICAgICByZW1vdGVTZXNzaW9ucy5sZW5ndGggPiAwIHx8XG4gICAgICAgICAgICAgICAgICBhZ2VudFRhc2tzLmxlbmd0aCA+IDApICYmIChcbiAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgICA8VGV4dCBib2xkPnsnICAnfUFnZW50czwvVGV4dD4gKFxuICAgICAgICAgICAgICAgICAgICB7Y291bnQodGVhbW1hdGVUYXNrcywgaSA9PiBpLnR5cGUgIT09ICdsZWFkZXInKX0pXG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICAgIDxUZWFtbWF0ZVRhc2tHcm91cHNcbiAgICAgICAgICAgICAgICAgICAgdGVhbW1hdGVUYXNrcz17dGVhbW1hdGVUYXNrc31cbiAgICAgICAgICAgICAgICAgICAgY3VycmVudFNlbGVjdGlvbklkPXtjdXJyZW50U2VsZWN0aW9uPy5pZH1cbiAgICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cblxuICAgICAgICAgICAge2Jhc2hUYXNrcy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICAgICAgPEJveFxuICAgICAgICAgICAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgICAgICAgICAgIG1hcmdpblRvcD17dGVhbW1hdGVUYXNrcy5sZW5ndGggPiAwID8gMSA6IDB9XG4gICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICB7KHRlYW1tYXRlVGFza3MubGVuZ3RoID4gMCB8fFxuICAgICAgICAgICAgICAgICAgcmVtb3RlU2Vzc2lvbnMubGVuZ3RoID4gMCB8fFxuICAgICAgICAgICAgICAgICAgYWdlbnRUYXNrcy5sZW5ndGggPiAwKSAmJiAoXG4gICAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICAgICAgPFRleHQgYm9sZD57JyAgJ31TaGVsbHM8L1RleHQ+ICh7YmFzaFRhc2tzLmxlbmd0aH0pXG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICAgIHtiYXNoVGFza3MubWFwKGl0ZW0gPT4gKFxuICAgICAgICAgICAgICAgICAgICA8SXRlbVxuICAgICAgICAgICAgICAgICAgICAgIGtleT17aXRlbS5pZH1cbiAgICAgICAgICAgICAgICAgICAgICBpdGVtPXtpdGVtfVxuICAgICAgICAgICAgICAgICAgICAgIGlzU2VsZWN0ZWQ9e2l0ZW0uaWQgPT09IGN1cnJlbnRTZWxlY3Rpb24/LmlkfVxuICAgICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cblxuICAgICAgICAgICAge21jcE1vbml0b3JzLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICA8Qm94XG4gICAgICAgICAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgICAgICAgICAgbWFyZ2luVG9wPXtcbiAgICAgICAgICAgICAgICAgIHRlYW1tYXRlVGFza3MubGVuZ3RoID4gMCB8fCBiYXNoVGFza3MubGVuZ3RoID4gMCA/IDEgOiAwXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICA8VGV4dCBib2xkPnsnICAnfU1vbml0b3JzPC9UZXh0PiAoe21jcE1vbml0b3JzLmxlbmd0aH0pXG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgICAgICAge21jcE1vbml0b3JzLm1hcChpdGVtID0+IChcbiAgICAgICAgICAgICAgICAgICAgPEl0ZW1cbiAgICAgICAgICAgICAgICAgICAgICBrZXk9e2l0ZW0uaWR9XG4gICAgICAgICAgICAgICAgICAgICAgaXRlbT17aXRlbX1cbiAgICAgICAgICAgICAgICAgICAgICBpc1NlbGVjdGVkPXtpdGVtLmlkID09PSBjdXJyZW50U2VsZWN0aW9uPy5pZH1cbiAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICkpfVxuICAgICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICl9XG5cbiAgICAgICAgICAgIHtyZW1vdGVTZXNzaW9ucy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICAgICAgPEJveFxuICAgICAgICAgICAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgICAgICAgICAgIG1hcmdpblRvcD17XG4gICAgICAgICAgICAgICAgICB0ZWFtbWF0ZVRhc2tzLmxlbmd0aCA+IDAgfHxcbiAgICAgICAgICAgICAgICAgIGJhc2hUYXNrcy5sZW5ndGggPiAwIHx8XG4gICAgICAgICAgICAgICAgICBtY3BNb25pdG9ycy5sZW5ndGggPiAwXG4gICAgICAgICAgICAgICAgICAgID8gMVxuICAgICAgICAgICAgICAgICAgICA6IDBcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICAgIDxUZXh0IGJvbGQ+eycgICd9UmVtb3RlIGFnZW50czwvVGV4dD4gKHtyZW1vdGVTZXNzaW9ucy5sZW5ndGh9XG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgICAgICAge3JlbW90ZVNlc3Npb25zLm1hcChpdGVtID0+IChcbiAgICAgICAgICAgICAgICAgICAgPEl0ZW1cbiAgICAgICAgICAgICAgICAgICAgICBrZXk9e2l0ZW0uaWR9XG4gICAgICAgICAgICAgICAgICAgICAgaXRlbT17aXRlbX1cbiAgICAgICAgICAgICAgICAgICAgICBpc1NlbGVjdGVkPXtpdGVtLmlkID09PSBjdXJyZW50U2VsZWN0aW9uPy5pZH1cbiAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICkpfVxuICAgICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICl9XG5cbiAgICAgICAgICAgIHthZ2VudFRhc2tzLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICA8Qm94XG4gICAgICAgICAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgICAgICAgICAgbWFyZ2luVG9wPXtcbiAgICAgICAgICAgICAgICAgIHRlYW1tYXRlVGFza3MubGVuZ3RoID4gMCB8fFxuICAgICAgICAgICAgICAgICAgYmFzaFRhc2tzLmxlbmd0aCA+IDAgfHxcbiAgICAgICAgICAgICAgICAgIG1jcE1vbml0b3JzLmxlbmd0aCA+IDAgfHxcbiAgICAgICAgICAgICAgICAgIHJlbW90ZVNlc3Npb25zLmxlbmd0aCA+IDBcbiAgICAgICAgICAgICAgICAgICAgPyAxXG4gICAgICAgICAgICAgICAgICAgIDogMFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgPFRleHQgYm9sZD57JyAgJ31Mb2NhbCBhZ2VudHM8L1RleHQ+ICh7YWdlbnRUYXNrcy5sZW5ndGh9KVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgICAgICAgIHthZ2VudFRhc2tzLm1hcChpdGVtID0+IChcbiAgICAgICAgICAgICAgICAgICAgPEl0ZW1cbiAgICAgICAgICAgICAgICAgICAgICBrZXk9e2l0ZW0uaWR9XG4gICAgICAgICAgICAgICAgICAgICAgaXRlbT17aXRlbX1cbiAgICAgICAgICAgICAgICAgICAgICBpc1NlbGVjdGVkPXtpdGVtLmlkID09PSBjdXJyZW50U2VsZWN0aW9uPy5pZH1cbiAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICkpfVxuICAgICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICl9XG5cbiAgICAgICAgICAgIHt3b3JrZmxvd1Rhc2tzLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICA8Qm94XG4gICAgICAgICAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgICAgICAgICAgbWFyZ2luVG9wPXtcbiAgICAgICAgICAgICAgICAgIHRlYW1tYXRlVGFza3MubGVuZ3RoID4gMCB8fFxuICAgICAgICAgICAgICAgICAgYmFzaFRhc2tzLmxlbmd0aCA+IDAgfHxcbiAgICAgICAgICAgICAgICAgIG1jcE1vbml0b3JzLmxlbmd0aCA+IDAgfHxcbiAgICAgICAgICAgICAgICAgIHJlbW90ZVNlc3Npb25zLmxlbmd0aCA+IDAgfHxcbiAgICAgICAgICAgICAgICAgIGFnZW50VGFza3MubGVuZ3RoID4gMFxuICAgICAgICAgICAgICAgICAgICA/IDFcbiAgICAgICAgICAgICAgICAgICAgOiAwXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgICAgICA8VGV4dCBib2xkPnsnICAnfVdvcmtmbG93czwvVGV4dD4gKHt3b3JrZmxvd1Rhc2tzLmxlbmd0aH0pXG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgICAgICAge3dvcmtmbG93VGFza3MubWFwKGl0ZW0gPT4gKFxuICAgICAgICAgICAgICAgICAgICA8SXRlbVxuICAgICAgICAgICAgICAgICAgICAgIGtleT17aXRlbS5pZH1cbiAgICAgICAgICAgICAgICAgICAgICBpdGVtPXtpdGVtfVxuICAgICAgICAgICAgICAgICAgICAgIGlzU2VsZWN0ZWQ9e2l0ZW0uaWQgPT09IGN1cnJlbnRTZWxlY3Rpb24/LmlkfVxuICAgICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cblxuICAgICAgICAgICAge2RyZWFtVGFza3MubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgICAgICAgIDxCb3hcbiAgICAgICAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICAgICAgICBtYXJnaW5Ub3A9e1xuICAgICAgICAgICAgICAgICAgdGVhbW1hdGVUYXNrcy5sZW5ndGggPiAwIHx8XG4gICAgICAgICAgICAgICAgICBiYXNoVGFza3MubGVuZ3RoID4gMCB8fFxuICAgICAgICAgICAgICAgICAgbWNwTW9uaXRvcnMubGVuZ3RoID4gMCB8fFxuICAgICAgICAgICAgICAgICAgcmVtb3RlU2Vzc2lvbnMubGVuZ3RoID4gMCB8fFxuICAgICAgICAgICAgICAgICAgYWdlbnRUYXNrcy5sZW5ndGggPiAwIHx8XG4gICAgICAgICAgICAgICAgICB3b3JrZmxvd1Rhc2tzLmxlbmd0aCA+IDBcbiAgICAgICAgICAgICAgICAgICAgPyAxXG4gICAgICAgICAgICAgICAgICAgIDogMFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgICAgICAge2RyZWFtVGFza3MubWFwKGl0ZW0gPT4gKFxuICAgICAgICAgICAgICAgICAgICA8SXRlbVxuICAgICAgICAgICAgICAgICAgICAgIGtleT17aXRlbS5pZH1cbiAgICAgICAgICAgICAgICAgICAgICBpdGVtPXtpdGVtfVxuICAgICAgICAgICAgICAgICAgICAgIGlzU2VsZWN0ZWQ9e2l0ZW0uaWQgPT09IGN1cnJlbnRTZWxlY3Rpb24/LmlkfVxuICAgICAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgIDwvRGlhbG9nPlxuICAgIDwvQm94PlxuICApXG59XG5cbmZ1bmN0aW9uIHRvTGlzdEl0ZW0odGFzazogQmFja2dyb3VuZFRhc2tTdGF0ZSk6IExpc3RJdGVtIHtcbiAgc3dpdGNoICh0YXNrLnR5cGUpIHtcbiAgICBjYXNlICdsb2NhbF9iYXNoJzpcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGlkOiB0YXNrLmlkLFxuICAgICAgICB0eXBlOiAnbG9jYWxfYmFzaCcsXG4gICAgICAgIGxhYmVsOiB0YXNrLmtpbmQgPT09ICdtb25pdG9yJyA/IHRhc2suZGVzY3JpcHRpb24gOiB0YXNrLmNvbW1hbmQsXG4gICAgICAgIHN0YXR1czogdGFzay5zdGF0dXMsXG4gICAgICAgIHRhc2ssXG4gICAgICB9XG4gICAgY2FzZSAncmVtb3RlX2FnZW50JzpcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGlkOiB0YXNrLmlkLFxuICAgICAgICB0eXBlOiAncmVtb3RlX2FnZW50JyxcbiAgICAgICAgbGFiZWw6IHRhc2sudGl0bGUsXG4gICAgICAgIHN0YXR1czogdGFzay5zdGF0dXMsXG4gICAgICAgIHRhc2ssXG4gICAgICB9XG4gICAgY2FzZSAnbG9jYWxfYWdlbnQnOlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaWQ6IHRhc2suaWQsXG4gICAgICAgIHR5cGU6ICdsb2NhbF9hZ2VudCcsXG4gICAgICAgIGxhYmVsOiB0YXNrLmRlc2NyaXB0aW9uLFxuICAgICAgICBzdGF0dXM6IHRhc2suc3RhdHVzLFxuICAgICAgICB0YXNrLFxuICAgICAgfVxuICAgIGNhc2UgJ2luX3Byb2Nlc3NfdGVhbW1hdGUnOlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaWQ6IHRhc2suaWQsXG4gICAgICAgIHR5cGU6ICdpbl9wcm9jZXNzX3RlYW1tYXRlJyxcbiAgICAgICAgbGFiZWw6IGBAJHt0YXNrLmlkZW50aXR5LmFnZW50TmFtZX1gLFxuICAgICAgICBzdGF0dXM6IHRhc2suc3RhdHVzLFxuICAgICAgICB0YXNrLFxuICAgICAgfVxuICAgIGNhc2UgJ2xvY2FsX3dvcmtmbG93JzpcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGlkOiB0YXNrLmlkLFxuICAgICAgICB0eXBlOiAnbG9jYWxfd29ya2Zsb3cnLFxuICAgICAgICBsYWJlbDogdGFzay5zdW1tYXJ5ID8/IHRhc2suZGVzY3JpcHRpb24sXG4gICAgICAgIHN0YXR1czogdGFzay5zdGF0dXMsXG4gICAgICAgIHRhc2ssXG4gICAgICB9XG4gICAgY2FzZSAnbW9uaXRvcl9tY3AnOlxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaWQ6IHRhc2suaWQsXG4gICAgICAgIHR5cGU6ICdtb25pdG9yX21jcCcsXG4gICAgICAgIGxhYmVsOiB0YXNrLmRlc2NyaXB0aW9uLFxuICAgICAgICBzdGF0dXM6IHRhc2suc3RhdHVzLFxuICAgICAgICB0YXNrLFxuICAgICAgfVxuICAgIGNhc2UgJ2RyZWFtJzpcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGlkOiB0YXNrLmlkLFxuICAgICAgICB0eXBlOiAnZHJlYW0nLFxuICAgICAgICBsYWJlbDogdGFzay5kZXNjcmlwdGlvbixcbiAgICAgICAgc3RhdHVzOiB0YXNrLnN0YXR1cyxcbiAgICAgICAgdGFzayxcbiAgICAgIH1cbiAgfVxufVxuXG5mdW5jdGlvbiBJdGVtKHtcbiAgaXRlbSxcbiAgaXNTZWxlY3RlZCxcbn06IHtcbiAgaXRlbTogTGlzdEl0ZW1cbiAgaXNTZWxlY3RlZDogYm9vbGVhblxufSk6IFJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcbiAgLy8gRGlhbG9nIGJvcmRlciAoMikgKyBwYWRkaW5nICgyKSArIHBvaW50ZXIgcHJlZml4ICgyKSArIG5hbWUvc3RhdHVzIG92ZXJoZWFkICh+MjApXG4gIGNvbnN0IG1heEFjdGl2aXR5V2lkdGggPSBNYXRoLm1heCgzMCwgY29sdW1ucyAtIDI2KVxuICAvLyBJbiBjb29yZGluYXRvciBtb2RlLCB1c2UgZ3JleSBwb2ludGVyIGluc3RlYWQgb2YgYmx1ZVxuICBjb25zdCB1c2VHcmV5UG9pbnRlciA9IGlzQ29vcmRpbmF0b3JNb2RlKClcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgPFRleHQgZGltQ29sb3I9e3VzZUdyZXlQb2ludGVyICYmIGlzU2VsZWN0ZWR9PlxuICAgICAgICB7aXNTZWxlY3RlZCA/IGZpZ3VyZXMucG9pbnRlciArICcgJyA6ICcgICd9XG4gICAgICA8L1RleHQ+XG4gICAgICA8VGV4dCBjb2xvcj17aXNTZWxlY3RlZCAmJiAhdXNlR3JleVBvaW50ZXIgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9PlxuICAgICAgICB7aXRlbS50eXBlID09PSAnbGVhZGVyJyA/IChcbiAgICAgICAgICA8VGV4dD5Ae1RFQU1fTEVBRF9OQU1FfTwvVGV4dD5cbiAgICAgICAgKSA6IChcbiAgICAgICAgICA8QmFja2dyb3VuZFRhc2tDb21wb25lbnRcbiAgICAgICAgICAgIHRhc2s9e2l0ZW0udGFza31cbiAgICAgICAgICAgIG1heEFjdGl2aXR5V2lkdGg9e21heEFjdGl2aXR5V2lkdGh9XG4gICAgICAgICAgLz5cbiAgICAgICAgKX1cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuXG5mdW5jdGlvbiBUZWFtbWF0ZVRhc2tHcm91cHMoe1xuICB0ZWFtbWF0ZVRhc2tzLFxuICBjdXJyZW50U2VsZWN0aW9uSWQsXG59OiB7XG4gIHRlYW1tYXRlVGFza3M6IExpc3RJdGVtW11cbiAgY3VycmVudFNlbGVjdGlvbklkOiBzdHJpbmcgfCB1bmRlZmluZWRcbn0pOiBSZWFjdE5vZGUge1xuICAvLyBTZXBhcmF0ZSBsZWFkZXIgZnJvbSB0ZWFtbWF0ZXMsIGdyb3VwIHRlYW1tYXRlcyBieSB0ZWFtXG4gIGNvbnN0IGxlYWRlckl0ZW1zID0gdGVhbW1hdGVUYXNrcy5maWx0ZXIoaSA9PiBpLnR5cGUgPT09ICdsZWFkZXInKVxuICBjb25zdCB0ZWFtbWF0ZUl0ZW1zID0gdGVhbW1hdGVUYXNrcy5maWx0ZXIoXG4gICAgaSA9PiBpLnR5cGUgPT09ICdpbl9wcm9jZXNzX3RlYW1tYXRlJyxcbiAgKVxuICBjb25zdCB0ZWFtcyA9IG5ldyBNYXA8c3RyaW5nLCB0eXBlb2YgdGVhbW1hdGVJdGVtcz4oKVxuICBmb3IgKGNvbnN0IGl0ZW0gb2YgdGVhbW1hdGVJdGVtcykge1xuICAgIGNvbnN0IHRlYW1OYW1lID0gaXRlbS50YXNrLmlkZW50aXR5LnRlYW1OYW1lXG4gICAgY29uc3QgZ3JvdXAgPSB0ZWFtcy5nZXQodGVhbU5hbWUpXG4gICAgaWYgKGdyb3VwKSB7XG4gICAgICBncm91cC5wdXNoKGl0ZW0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHRlYW1zLnNldCh0ZWFtTmFtZSwgW2l0ZW1dKVxuICAgIH1cbiAgfVxuICBjb25zdCB0ZWFtRW50cmllcyA9IFsuLi50ZWFtcy5lbnRyaWVzKCldXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIHt0ZWFtRW50cmllcy5tYXAoKFt0ZWFtTmFtZSwgaXRlbXNdKSA9PiB7XG4gICAgICAgIGNvbnN0IG1lbWJlckNvdW50ID0gaXRlbXMubGVuZ3RoICsgbGVhZGVySXRlbXMubGVuZ3RoXG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgPEJveCBrZXk9e3RlYW1OYW1lfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgeycgICd9VGVhbToge3RlYW1OYW1lfSAoe21lbWJlckNvdW50fSlcbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIHsvKiBSZW5kZXIgbGVhZGVyIGZpcnN0IHdpdGhpbiBlYWNoIHRlYW0gKi99XG4gICAgICAgICAgICB7bGVhZGVySXRlbXMubWFwKGl0ZW0gPT4gKFxuICAgICAgICAgICAgICA8SXRlbVxuICAgICAgICAgICAgICAgIGtleT17YCR7aXRlbS5pZH0tJHt0ZWFtTmFtZX1gfVxuICAgICAgICAgICAgICAgIGl0ZW09e2l0ZW19XG4gICAgICAgICAgICAgICAgaXNTZWxlY3RlZD17aXRlbS5pZCA9PT0gY3VycmVudFNlbGVjdGlvbklkfVxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICB7aXRlbXMubWFwKGl0ZW0gPT4gKFxuICAgICAgICAgICAgICA8SXRlbVxuICAgICAgICAgICAgICAgIGtleT17aXRlbS5pZH1cbiAgICAgICAgICAgICAgICBpdGVtPXtpdGVtfVxuICAgICAgICAgICAgICAgIGlzU2VsZWN0ZWQ9e2l0ZW0uaWQgPT09IGN1cnJlbnRTZWxlY3Rpb25JZH1cbiAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICkpfVxuICAgICAgICAgIDwvQm94PlxuICAgICAgICApXG4gICAgICB9KX1cbiAgICA8Lz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsT0FBT0MsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBT0MsS0FBSyxJQUNWLEtBQUtDLFNBQVMsRUFDZEMsU0FBUyxFQUNUQyxjQUFjLEVBQ2RDLE9BQU8sRUFDUEMsTUFBTSxFQUNOQyxRQUFRLFFBQ0gsT0FBTztBQUNkLFNBQVNDLGlCQUFpQixRQUFRLG9DQUFvQztBQUN0RSxTQUFTQyxlQUFlLFFBQVEsOEJBQThCO0FBQzlELFNBQVNDLFdBQVcsRUFBRUMsY0FBYyxRQUFRLHVCQUF1QjtBQUNuRSxTQUNFQyxpQkFBaUIsRUFDakJDLGdCQUFnQixRQUNYLGtDQUFrQztBQUN6QyxjQUFjQyxjQUFjLFFBQVEsYUFBYTtBQUNqRCxTQUNFQyxTQUFTLEVBQ1QsS0FBS0MsY0FBYyxRQUNkLGtDQUFrQztBQUN6QyxTQUFTQyxxQkFBcUIsUUFBUSwwREFBMEQ7QUFDaEcsY0FBY0MsMEJBQTBCLFFBQVEsMENBQTBDO0FBQzFGLGNBQWNDLG1CQUFtQixRQUFRLDRDQUE0QztBQUNyRixTQUFTQyxjQUFjLFFBQVEsNENBQTRDO0FBQzNFLGNBQWNDLG1CQUFtQixRQUFRLG9DQUFvQztBQUM3RSxTQUFTQyxjQUFjLFFBQVEsNENBQTRDO0FBQzNFO0FBQ0EsY0FBY0Msc0JBQXNC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 \ No newline at end of file diff --git a/components/tasks/DreamDetailDialog.tsx b/components/tasks/DreamDetailDialog.tsx new file mode 100644 index 0000000..74fdee5 --- /dev/null +++ b/components/tasks/DreamDetailDialog.tsx @@ -0,0 +1,251 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import type { DeepImmutable } from 'src/types/utils.js'; +import { useElapsedTime } from '../../hooks/useElapsedTime.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +import type { DreamTaskState } from '../../tasks/DreamTask/DreamTask.js'; +import { plural } from '../../utils/stringUtils.js'; +import { Byline } from '../design-system/Byline.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +type Props = { + task: DeepImmutable; + onDone: () => void; + onBack?: () => void; + onKill?: () => void; +}; + +// How many recent turns to render. Earlier turns collapse to a count. +const VISIBLE_TURNS = 6; +export function DreamDetailDialog(t0) { + const $ = _c(70); + const { + task, + onDone, + onBack, + onKill + } = t0; + const elapsedTime = useElapsedTime(task.startTime, task.status === "running", 1000, 0); + let t1; + if ($[0] !== onDone) { + t1 = { + "confirm:yes": onDone + }; + $[0] = onDone; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t2 = { + context: "Confirmation" + }; + $[2] = t2; + } else { + t2 = $[2]; + } + useKeybindings(t1, t2); + let t3; + if ($[3] !== onBack || $[4] !== onDone || $[5] !== onKill || $[6] !== task.status) { + t3 = e => { + if (e.key === " ") { + e.preventDefault(); + onDone(); + } else { + if (e.key === "left" && onBack) { + e.preventDefault(); + onBack(); + } else { + if (e.key === "x" && task.status === "running" && onKill) { + e.preventDefault(); + onKill(); + } + } + } + }; + $[3] = onBack; + $[4] = onDone; + $[5] = onKill; + $[6] = task.status; + $[7] = t3; + } else { + t3 = $[7]; + } + const handleKeyDown = t3; + let T0; + let T1; + let T2; + let t10; + let t11; + let t12; + let t13; + let t14; + let t15; + let t16; + let t4; + let t5; + let t6; + let t7; + let t8; + let t9; + if ($[8] !== elapsedTime || $[9] !== handleKeyDown || $[10] !== onBack || $[11] !== onDone || $[12] !== onKill || $[13] !== task.filesTouched.length || $[14] !== task.sessionsReviewing || $[15] !== task.status || $[16] !== task.turns) { + const visibleTurns = task.turns.filter(_temp); + const shown = visibleTurns.slice(-VISIBLE_TURNS); + const hidden = visibleTurns.length - shown.length; + T2 = Box; + t13 = "column"; + t14 = 0; + t15 = true; + t16 = handleKeyDown; + T1 = Dialog; + t8 = "Memory consolidation"; + const t17 = task.sessionsReviewing; + let t18; + if ($[33] !== task.sessionsReviewing) { + t18 = plural(task.sessionsReviewing, "session"); + $[33] = task.sessionsReviewing; + $[34] = t18; + } else { + t18 = $[34]; + } + let t19; + if ($[35] !== task.filesTouched.length) { + t19 = task.filesTouched.length > 0 && <>{" "}· {task.filesTouched.length}{" "}{plural(task.filesTouched.length, "file")} touched; + $[35] = task.filesTouched.length; + $[36] = t19; + } else { + t19 = $[36]; + } + if ($[37] !== elapsedTime || $[38] !== t18 || $[39] !== t19 || $[40] !== task.sessionsReviewing) { + t9 = {elapsedTime} · reviewing {t17}{" "}{t18}{t19}; + $[37] = elapsedTime; + $[38] = t18; + $[39] = t19; + $[40] = task.sessionsReviewing; + $[41] = t9; + } else { + t9 = $[41]; + } + t10 = onDone; + t11 = "background"; + if ($[42] !== onBack || $[43] !== onKill || $[44] !== task.status) { + t12 = exitState => exitState.pending ? Press {exitState.keyName} again to exit : {onBack && }{task.status === "running" && onKill && }; + $[42] = onBack; + $[43] = onKill; + $[44] = task.status; + $[45] = t12; + } else { + t12 = $[45]; + } + T0 = Box; + t4 = "column"; + t5 = 1; + let t20; + if ($[46] === Symbol.for("react.memo_cache_sentinel")) { + t20 = Status:; + $[46] = t20; + } else { + t20 = $[46]; + } + if ($[47] !== task.status) { + t6 = {t20}{" "}{task.status === "running" ? running : task.status === "completed" ? {task.status} : {task.status}}; + $[47] = task.status; + $[48] = t6; + } else { + t6 = $[48]; + } + t7 = shown.length === 0 ? {task.status === "running" ? "Starting\u2026" : "(no text output)"} : <>{hidden > 0 && ({hidden} earlier {plural(hidden, "turn")})}{shown.map(_temp2)}; + $[8] = elapsedTime; + $[9] = handleKeyDown; + $[10] = onBack; + $[11] = onDone; + $[12] = onKill; + $[13] = task.filesTouched.length; + $[14] = task.sessionsReviewing; + $[15] = task.status; + $[16] = task.turns; + $[17] = T0; + $[18] = T1; + $[19] = T2; + $[20] = t10; + $[21] = t11; + $[22] = t12; + $[23] = t13; + $[24] = t14; + $[25] = t15; + $[26] = t16; + $[27] = t4; + $[28] = t5; + $[29] = t6; + $[30] = t7; + $[31] = t8; + $[32] = t9; + } else { + T0 = $[17]; + T1 = $[18]; + T2 = $[19]; + t10 = $[20]; + t11 = $[21]; + t12 = $[22]; + t13 = $[23]; + t14 = $[24]; + t15 = $[25]; + t16 = $[26]; + t4 = $[27]; + t5 = $[28]; + t6 = $[29]; + t7 = $[30]; + t8 = $[31]; + t9 = $[32]; + } + let t17; + if ($[49] !== T0 || $[50] !== t4 || $[51] !== t5 || $[52] !== t6 || $[53] !== t7) { + t17 = {t6}{t7}; + $[49] = T0; + $[50] = t4; + $[51] = t5; + $[52] = t6; + $[53] = t7; + $[54] = t17; + } else { + t17 = $[54]; + } + let t18; + if ($[55] !== T1 || $[56] !== t10 || $[57] !== t11 || $[58] !== t12 || $[59] !== t17 || $[60] !== t8 || $[61] !== t9) { + t18 = {t17}; + $[55] = T1; + $[56] = t10; + $[57] = t11; + $[58] = t12; + $[59] = t17; + $[60] = t8; + $[61] = t9; + $[62] = t18; + } else { + t18 = $[62]; + } + let t19; + if ($[63] !== T2 || $[64] !== t13 || $[65] !== t14 || $[66] !== t15 || $[67] !== t16 || $[68] !== t18) { + t19 = {t18}; + $[63] = T2; + $[64] = t13; + $[65] = t14; + $[66] = t15; + $[67] = t16; + $[68] = t18; + $[69] = t19; + } else { + t19 = $[69]; + } + return t19; +} +function _temp2(turn, i) { + return {turn.text}{turn.toolUseCount > 0 && {" "}({turn.toolUseCount}{" "}{plural(turn.toolUseCount, "tool")})}; +} +function _temp(t) { + return t.text !== ""; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkRlZXBJbW11dGFibGUiLCJ1c2VFbGFwc2VkVGltZSIsIktleWJvYXJkRXZlbnQiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZ3MiLCJEcmVhbVRhc2tTdGF0ZSIsInBsdXJhbCIsIkJ5bGluZSIsIkRpYWxvZyIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwiUHJvcHMiLCJ0YXNrIiwib25Eb25lIiwib25CYWNrIiwib25LaWxsIiwiVklTSUJMRV9UVVJOUyIsIkRyZWFtRGV0YWlsRGlhbG9nIiwidDAiLCIkIiwiX2MiLCJlbGFwc2VkVGltZSIsInN0YXJ0VGltZSIsInN0YXR1cyIsInQxIiwidDIiLCJTeW1ib2wiLCJmb3IiLCJjb250ZXh0IiwidDMiLCJlIiwia2V5IiwicHJldmVudERlZmF1bHQiLCJoYW5kbGVLZXlEb3duIiwiVDAiLCJUMSIsIlQyIiwidDEwIiwidDExIiwidDEyIiwidDEzIiwidDE0IiwidDE1IiwidDE2IiwidDQiLCJ0NSIsInQ2IiwidDciLCJ0OCIsInQ5IiwiZmlsZXNUb3VjaGVkIiwibGVuZ3RoIiwic2Vzc2lvbnNSZXZpZXdpbmciLCJ0dXJucyIsInZpc2libGVUdXJucyIsImZpbHRlciIsIl90ZW1wIiwic2hvd24iLCJzbGljZSIsImhpZGRlbiIsInQxNyIsInQxOCIsInQxOSIsImV4aXRTdGF0ZSIsInBlbmRpbmciLCJrZXlOYW1lIiwidDIwIiwibWFwIiwiX3RlbXAyIiwidHVybiIsImkiLCJ0ZXh0IiwidG9vbFVzZUNvdW50IiwidCJdLCJzb3VyY2VzIjpbIkRyZWFtRGV0YWlsRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IERlZXBJbW11dGFibGUgfSBmcm9tICdzcmMvdHlwZXMvdXRpbHMuanMnXG5pbXBvcnQgeyB1c2VFbGFwc2VkVGltZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUVsYXBzZWRUaW1lLmpzJ1xuaW1wb3J0IHR5cGUgeyBLZXlib2FyZEV2ZW50IH0gZnJvbSAnLi4vLi4vaW5rL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmdzIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB0eXBlIHsgRHJlYW1UYXNrU3RhdGUgfSBmcm9tICcuLi8uLi90YXNrcy9EcmVhbVRhc2svRHJlYW1UYXNrLmpzJ1xuaW1wb3J0IHsgcGx1cmFsIH0gZnJvbSAnLi4vLi4vdXRpbHMvc3RyaW5nVXRpbHMuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0YXNrOiBEZWVwSW1tdXRhYmxlPERyZWFtVGFza1N0YXRlPlxuICBvbkRvbmU6ICgpID0+IHZvaWRcbiAgb25CYWNrPzogKCkgPT4gdm9pZFxuICBvbktpbGw/OiAoKSA9PiB2b2lkXG59XG5cbi8vIEhvdyBtYW55IHJlY2VudCB0dXJucyB0byByZW5kZXIuIEVhcmxpZXIgdHVybnMgY29sbGFwc2UgdG8gYSBjb3VudC5cbmNvbnN0IFZJU0lCTEVfVFVSTlMgPSA2XG5cbmV4cG9ydCBmdW5jdGlvbiBEcmVhbURldGFpbERpYWxvZyh7XG4gIHRhc2ssXG4gIG9uRG9uZSxcbiAgb25CYWNrLFxuICBvbktpbGwsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGVsYXBzZWRUaW1lID0gdXNlRWxhcHNlZFRpbWUoXG4gICAgdGFzay5zdGFydFRpbWUsXG4gICAgdGFzay5zdGF0dXMgPT09ICdydW5uaW5nJyxcbiAgICAxMDAwLFxuICAgIDAsXG4gIClcblxuICAvLyBEaWFsb2cgaGFuZGxlcyBjb25maXJtOm5vIChFc2MpIOKGkiBvbkNhbmNlbC4gV2lyZSBjb25maXJtOnllcyAoRW50ZXIveSkgdG9vLlxuICB1c2VLZXliaW5kaW5ncyh7ICdjb25maXJtOnllcyc6IG9uRG9uZSB9LCB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nIH0pXG5cbiAgY29uc3QgaGFuZGxlS2V5RG93biA9IChlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgaWYgKGUua2V5ID09PSAnICcpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25Eb25lKClcbiAgICB9IGVsc2UgaWYgKGUua2V5ID09PSAnbGVmdCcgJiYgb25CYWNrKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIG9uQmFjaygpXG4gICAgfSBlbHNlIGlmIChlLmtleSA9PT0gJ3gnICYmIHRhc2suc3RhdHVzID09PSAncnVubmluZycgJiYgb25LaWxsKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIG9uS2lsbCgpXG4gICAgfVxuICB9XG5cbiAgLy8gVHVybnMgd2l0aCB0ZXh0IHRvIHNob3cuIFRvb2wtb25seSB0dXJucyAodGV4dD0nJykgYXJlIGRyb3BwZWQgZW50aXJlbHkg4oCUXG4gIC8vIHRoZSBwZXItdHVybiB0b29sVXNlQ291bnQgYWxyZWFkeSBjYXB0dXJlcyB0aGF0IHdvcmsuXG4gIGNvbnN0IHZpc2libGVUdXJucyA9IHRhc2sudHVybnMuZmlsdGVyKHQgPT4gdC50ZXh0ICE9PSAnJylcbiAgY29uc3Qgc2hvd24gPSB2aXNpYmxlVHVybnMuc2xpY2UoLVZJU0lCTEVfVFVSTlMpXG4gIGNvbnN0IGhpZGRlbiA9IHZpc2libGVUdXJucy5sZW5ndGggLSBzaG93bi5sZW5ndGhcblxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgdGFiSW5kZXg9ezB9XG4gICAgICBhdXRvRm9jdXNcbiAgICAgIG9uS2V5RG93bj17aGFuZGxlS2V5RG93bn1cbiAgICA+XG4gICAgICA8RGlhbG9nXG4gICAgICAgIHRpdGxlPVwiTWVtb3J5IGNvbnNvbGlkYXRpb25cIlxuICAgICAgICBzdWJ0aXRsZT17XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7ZWxhcHNlZFRpbWV9IMK3IHJldmlld2luZyB7dGFzay5zZXNzaW9uc1Jldmlld2luZ317JyAnfVxuICAgICAgICAgICAge3BsdXJhbCh0YXNrLnNlc3Npb25zUmV2aWV3aW5nLCAnc2Vzc2lvbicpfVxuICAgICAgICAgICAge3Rhc2suZmlsZXNUb3VjaGVkLmxlbmd0aCA+IDAgJiYgKFxuICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgIHsnICd9XG4gICAgICAgICAgICAgICAgwrcge3Rhc2suZmlsZXNUb3VjaGVkLmxlbmd0aH17JyAnfVxuICAgICAgICAgICAgICAgIHtwbHVyYWwodGFzay5maWxlc1RvdWNoZWQubGVuZ3RoLCAnZmlsZScpfSB0b3VjaGVkXG4gICAgICAgICAgICAgIDwvPlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIH1cbiAgICAgICAgb25DYW5jZWw9e29uRG9uZX1cbiAgICAgICAgY29sb3I9XCJiYWNrZ3JvdW5kXCJcbiAgICAgICAgaW5wdXRHdWlkZT17ZXhpdFN0YXRlID0+XG4gICAgICAgICAgZXhpdFN0YXRlLnBlbmRpbmcgPyAoXG4gICAgICAgICAgICA8VGV4dD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8L1RleHQ+XG4gICAgICAgICAgKSA6IChcbiAgICAgICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgICAgIHtvbkJhY2sgJiYgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwi4oaQXCIgYWN0aW9uPVwiZ28gYmFja1wiIC8+fVxuICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFc2MvRW50ZXIvU3BhY2VcIiBhY3Rpb249XCJjbG9zZVwiIC8+XG4gICAgICAgICAgICAgIHt0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnICYmIG9uS2lsbCAmJiAoXG4gICAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwieFwiIGFjdGlvbj1cInN0b3BcIiAvPlxuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgPC9CeWxpbmU+XG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICA+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICA8VGV4dCBib2xkPlN0YXR1czo8L1RleHQ+eycgJ31cbiAgICAgICAgICAgIHt0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnID8gKFxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cImJhY2tncm91bmRcIj5ydW5uaW5nPC9UZXh0PlxuICAgICAgICAgICAgKSA6IHRhc2suc3RhdHVzID09PSAnY29tcGxldGVkJyA/IChcbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+e3Rhc2suc3RhdHVzfTwvVGV4dD5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj57dGFzay5zdGF0dXN9PC9UZXh0PlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L1RleHQ+XG5cbiAgICAgICAgICB7c2hvd24ubGVuZ3RoID09PSAwID8gKFxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICAgIHt0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnID8gJ1N0YXJ0aW5n4oCmJyA6ICcobm8gdGV4dCBvdXRwdXQpJ31cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPD5cbiAgICAgICAgICAgICAge2hpZGRlbiA+IDAgJiYgKFxuICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgKHtoaWRkZW59IGVhcmxpZXIge3BsdXJhbChoaWRkZW4sICd0dXJuJyl9KVxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAge3Nob3duLm1hcCgodHVybiwgaSkgPT4gKFxuICAgICAgICAgICAgICAgIDxCb3gga2V5PXtpfSBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICAgICAgICA8VGV4dCB3cmFwPVwid3JhcFwiPnt0dXJuLnRleHR9PC9UZXh0PlxuICAgICAgICAgICAgICAgICAge3R1cm4udG9vbFVzZUNvdW50ID4gMCAmJiAoXG4gICAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgICAgICAgIHsnICAnfSh7dHVybi50b29sVXNlQ291bnR9eycgJ31cbiAgICAgICAgICAgICAgICAgICAgICB7cGx1cmFsKHR1cm4udG9vbFVzZUNvdW50LCAndG9vbCcpfSlcbiAgICAgICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICA8Lz5cbiAgICAgICAgICApfVxuICAgICAgICA8L0JveD5cbiAgICAgIDwvRGlhbG9nPlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixjQUFjQyxhQUFhLFFBQVEsb0JBQW9CO0FBQ3ZELFNBQVNDLGNBQWMsUUFBUSwrQkFBK0I7QUFDOUQsY0FBY0MsYUFBYSxRQUFRLG9DQUFvQztBQUN2RSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxjQUFjO0FBQ3hDLFNBQVNDLGNBQWMsUUFBUSxvQ0FBb0M7QUFDbkUsY0FBY0MsY0FBYyxRQUFRLG9DQUFvQztBQUN4RSxTQUFTQyxNQUFNLFFBQVEsNEJBQTRCO0FBQ25ELFNBQVNDLE1BQU0sUUFBUSw0QkFBNEI7QUFDbkQsU0FBU0MsTUFBTSxRQUFRLDRCQUE0QjtBQUNuRCxTQUFTQyxvQkFBb0IsUUFBUSwwQ0FBMEM7QUFFL0UsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLElBQUksRUFBRVosYUFBYSxDQUFDTSxjQUFjLENBQUM7RUFDbkNPLE1BQU0sRUFBRSxHQUFHLEdBQUcsSUFBSTtFQUNsQkMsTUFBTSxDQUFDLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDbkJDLE1BQU0sQ0FBQyxFQUFFLEdBQUcsR0FBRyxJQUFJO0FBQ3JCLENBQUM7O0FBRUQ7QUFDQSxNQUFNQyxhQUFhLEdBQUcsQ0FBQztBQUV2QixPQUFPLFNBQUFDLGtCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQTJCO0lBQUFSLElBQUE7SUFBQUMsTUFBQTtJQUFBQyxNQUFBO0lBQUFDO0VBQUEsSUFBQUcsRUFLMUI7RUFDTixNQUFBRyxXQUFBLEdBQW9CcEIsY0FBYyxDQUNoQ1csSUFBSSxDQUFBVSxTQUFVLEVBQ2RWLElBQUksQ0FBQVcsTUFBTyxLQUFLLFNBQVMsRUFDekIsSUFBSSxFQUNKLENBQ0YsQ0FBQztFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBTCxDQUFBLFFBQUFOLE1BQUE7SUFHY1csRUFBQTtNQUFBLGVBQWlCWDtJQUFPLENBQUM7SUFBQU0sQ0FBQSxNQUFBTixNQUFBO0lBQUFNLENBQUEsTUFBQUssRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUwsQ0FBQTtFQUFBO0VBQUEsSUFBQU0sRUFBQTtFQUFBLElBQUFOLENBQUEsUUFBQU8sTUFBQSxDQUFBQyxHQUFBO0lBQUVGLEVBQUE7TUFBQUcsT0FBQSxFQUFXO0lBQWUsQ0FBQztJQUFBVCxDQUFBLE1BQUFNLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFOLENBQUE7RUFBQTtFQUFyRWQsY0FBYyxDQUFDbUIsRUFBeUIsRUFBRUMsRUFBMkIsQ0FBQztFQUFBLElBQUFJLEVBQUE7RUFBQSxJQUFBVixDQUFBLFFBQUFMLE1BQUEsSUFBQUssQ0FBQSxRQUFBTixNQUFBLElBQUFNLENBQUEsUUFBQUosTUFBQSxJQUFBSSxDQUFBLFFBQUFQLElBQUEsQ0FBQVcsTUFBQTtJQUVoRE0sRUFBQSxHQUFBQyxDQUFBO01BQ3BCLElBQUlBLENBQUMsQ0FBQUMsR0FBSSxLQUFLLEdBQUc7UUFDZkQsQ0FBQyxDQUFBRSxjQUFlLENBQUMsQ0FBQztRQUNsQm5CLE1BQU0sQ0FBQyxDQUFDO01BQUE7UUFDSCxJQUFJaUIsQ0FBQyxDQUFBQyxHQUFJLEtBQUssTUFBZ0IsSUFBMUJqQixNQUEwQjtVQUNuQ2dCLENBQUMsQ0FBQUUsY0FBZSxDQUFDLENBQUM7VUFDbEJsQixNQUFNLENBQUMsQ0FBQztRQUFBO1VBQ0gsSUFBSWdCLENBQUMsQ0FBQUMsR0FBSSxLQUFLLEdBQWdDLElBQXpCbkIsSUFBSSxDQUFBVyxNQUFPLEtBQUssU0FBbUIsSUFBcERSLE1BQW9EO1lBQzdEZSxDQUFDLENBQUFFLGNBQWUsQ0FBQyxDQUFDO1lBQ2xCakIsTUFBTSxDQUFDLENBQUM7VUFBQTtRQUNUO01BQUE7SUFBQSxDQUNGO0lBQUFJLENBQUEsTUFBQUwsTUFBQTtJQUFBSyxDQUFBLE1BQUFOLE1BQUE7SUFBQU0sQ0FBQSxNQUFBSixNQUFBO0lBQUFJLENBQUEsTUFBQVAsSUFBQSxDQUFBVyxNQUFBO0lBQUFKLENBQUEsTUFBQVUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVYsQ0FBQTtFQUFBO0VBWEQsTUFBQWMsYUFBQSxHQUFzQkosRUFXckI7RUFBQSxJQUFBSyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBQyxHQUFBO0VBQUEsSUFBQUMsR0FBQTtFQUFBLElBQUFDLEdBQUE7RUFBQSxJQUFBQyxHQUFBO0VBQUEsSUFBQUMsR0FBQTtFQUFBLElBQUFDLEdBQUE7RUFBQSxJQUFBQyxHQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQTlCLENBQUEsUUFBQUUsV0FBQSxJQUFBRixDQUFBLFFBQUFjLGFBQUEsSUFBQWQsQ0FBQSxTQUFBTCxNQUFBLElBQUFLLENBQUEsU0FBQU4sTUFBQSxJQUFBTSxDQUFBLFNBQUFKLE1BQUEsSUFBQUksQ0FBQSxTQUFBUCxJQUFBLENBQUFzQyxZQUFBLENBQUFDLE1BQUEsSUFBQWhDLENBQUEsU0FBQVAsSUFBQSxDQUFBd0MsaUJBQUEsSUFBQWpDLENBQUEsU0FBQVAsSUFBQSxDQUFBVyxNQUFBLElBQUFKLENBQUEsU0FBQVAsSUFBQSxDQUFBeUMsS0FBQTtJQUlELE1BQUFDLFlBQUEsR0FBcUIxQyxJQUFJLENBQUF5QyxLQUFNLENBQUFFLE1BQU8sQ0FBQ0MsS0FBa0IsQ0FBQztJQUMxRCxNQUFBQyxLQUFBLEdBQWNILFlBQVksQ0FBQUksS0FBTSxDQUFDLENBQUMxQyxhQUFhLENBQUM7SUFDaEQsTUFBQTJDLE1BQUEsR0FBZUwsWUFBWSxDQUFBSCxNQUFPLEdBQUdNLEtBQUssQ0FBQU4sTUFBTztJQUc5Q2YsRUFBQSxHQUFBakMsR0FBRztJQUNZcUMsR0FBQSxXQUFRO0lBQ1pDLEdBQUEsSUFBQztJQUNYQyxHQUFBLE9BQVM7SUFDRVQsR0FBQSxDQUFBQSxDQUFBLENBQUFBLGFBQWE7SUFFdkJFLEVBQUEsR0FBQTFCLE1BQU07SUFDQ3VDLEVBQUEseUJBQXNCO0lBR0csTUFBQVksR0FBQSxHQUFBaEQsSUFBSSxDQUFBd0MsaUJBQWtCO0lBQUEsSUFBQVMsR0FBQTtJQUFBLElBQUExQyxDQUFBLFNBQUFQLElBQUEsQ0FBQXdDLGlCQUFBO01BQ2hEUyxHQUFBLEdBQUF0RCxNQUFNLENBQUNLLElBQUksQ0FBQXdDLGlCQUFrQixFQUFFLFNBQVMsQ0FBQztNQUFBakMsQ0FBQSxPQUFBUCxJQUFBLENBQUF3QyxpQkFBQTtNQUFBakMsQ0FBQSxPQUFBMEMsR0FBQTtJQUFBO01BQUFBLEdBQUEsR0FBQTFDLENBQUE7SUFBQTtJQUFBLElBQUEyQyxHQUFBO0lBQUEsSUFBQTNDLENBQUEsU0FBQVAsSUFBQSxDQUFBc0MsWUFBQSxDQUFBQyxNQUFBO01BQ3pDVyxHQUFBLEdBQUFsRCxJQUFJLENBQUFzQyxZQUFhLENBQUFDLE1BQU8sR0FBRyxDQU0zQixJQU5BLEVBRUksSUFBRSxDQUFFLEVBQ0YsQ0FBQXZDLElBQUksQ0FBQXNDLFlBQWEsQ0FBQUMsTUFBTSxDQUFHLElBQUUsQ0FDOUIsQ0FBQTVDLE1BQU0sQ0FBQ0ssSUFBSSxDQUFBc0MsWUFBYSxDQUFBQyxNQUFPLEVBQUUsTUFBTSxFQUFFLFFBQzVDLEdBQ0Q7TUFBQWhDLENBQUEsT0FBQVAsSUFBQSxDQUFBc0MsWUFBQSxDQUFBQyxNQUFBO01BQUFoQyxDQUFBLE9BQUEyQyxHQUFBO0lBQUE7TUFBQUEsR0FBQSxHQUFBM0MsQ0FBQTtJQUFBO0lBQUEsSUFBQUEsQ0FBQSxTQUFBRSxXQUFBLElBQUFGLENBQUEsU0FBQTBDLEdBQUEsSUFBQTFDLENBQUEsU0FBQTJDLEdBQUEsSUFBQTNDLENBQUEsU0FBQVAsSUFBQSxDQUFBd0MsaUJBQUE7TUFUSEgsRUFBQSxJQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQ1g1QixZQUFVLENBQUUsYUFBYyxDQUFBdUMsR0FBcUIsQ0FBRyxJQUFFLENBQ3BELENBQUFDLEdBQXdDLENBQ3hDLENBQUFDLEdBTUQsQ0FDRixFQVZDLElBQUksQ0FVRTtNQUFBM0MsQ0FBQSxPQUFBRSxXQUFBO01BQUFGLENBQUEsT0FBQTBDLEdBQUE7TUFBQTFDLENBQUEsT0FBQTJDLEdBQUE7TUFBQTNDLENBQUEsT0FBQVAsSUFBQSxDQUFBd0MsaUJBQUE7TUFBQWpDLENBQUEsT0FBQThCLEVBQUE7SUFBQTtNQUFBQSxFQUFBLEdBQUE5QixDQUFBO0lBQUE7SUFFQ04sR0FBQSxDQUFBQSxDQUFBLENBQUFBLE1BQU07SUFDVnlCLEdBQUEsZUFBWTtJQUFBLElBQUFuQixDQUFBLFNBQUFMLE1BQUEsSUFBQUssQ0FBQSxTQUFBSixNQUFBLElBQUFJLENBQUEsU0FBQVAsSUFBQSxDQUFBVyxNQUFBO01BQ05nQixHQUFBLEdBQUF3QixTQUFBLElBQ1ZBLFNBQVMsQ0FBQUMsT0FVUixHQVRDLENBQUMsSUFBSSxDQUFDLE1BQU8sQ0FBQUQsU0FBUyxDQUFBRSxPQUFPLENBQUUsY0FBYyxFQUE1QyxJQUFJLENBU04sR0FQQyxDQUFDLE1BQU0sQ0FDSixDQUFBbkQsTUFBZ0UsSUFBdEQsQ0FBQyxvQkFBb0IsQ0FBVSxRQUFHLENBQUgsU0FBRSxDQUFDLENBQVEsTUFBUyxDQUFULFNBQVMsR0FBRSxDQUNoRSxDQUFDLG9CQUFvQixDQUFVLFFBQWlCLENBQWpCLGlCQUFpQixDQUFRLE1BQU8sQ0FBUCxPQUFPLEdBQzlELENBQUFGLElBQUksQ0FBQVcsTUFBTyxLQUFLLFNBQW1CLElBQW5DUixNQUVBLElBREMsQ0FBQyxvQkFBb0IsQ0FBVSxRQUFHLENBQUgsR0FBRyxDQUFRLE1BQU0sQ0FBTixNQUFNLEdBQ2xELENBQ0YsRUFOQyxNQUFNLENBT1I7TUFBQUksQ0FBQSxPQUFBTCxNQUFBO01BQUFLLENBQUEsT0FBQUosTUFBQTtNQUFBSSxDQUFBLE9BQUFQLElBQUEsQ0FBQVcsTUFBQTtNQUFBSixDQUFBLE9BQUFvQixHQUFBO0lBQUE7TUFBQUEsR0FBQSxHQUFBcEIsQ0FBQTtJQUFBO0lBR0ZlLEVBQUEsR0FBQS9CLEdBQUc7SUFBZXlDLEVBQUEsV0FBUTtJQUFNQyxFQUFBLElBQUM7SUFBQSxJQUFBcUIsR0FBQTtJQUFBLElBQUEvQyxDQUFBLFNBQUFPLE1BQUEsQ0FBQUMsR0FBQTtNQUU5QnVDLEdBQUEsSUFBQyxJQUFJLENBQUMsSUFBSSxDQUFKLEtBQUcsQ0FBQyxDQUFDLE9BQU8sRUFBakIsSUFBSSxDQUFvQjtNQUFBL0MsQ0FBQSxPQUFBK0MsR0FBQTtJQUFBO01BQUFBLEdBQUEsR0FBQS9DLENBQUE7SUFBQTtJQUFBLElBQUFBLENBQUEsU0FBQVAsSUFBQSxDQUFBVyxNQUFBO01BRDNCdUIsRUFBQSxJQUFDLElBQUksQ0FDSCxDQUFBb0IsR0FBd0IsQ0FBRSxJQUFFLENBQzNCLENBQUF0RCxJQUFJLENBQUFXLE1BQU8sS0FBSyxTQU1oQixHQUxDLENBQUMsSUFBSSxDQUFPLEtBQVksQ0FBWixZQUFZLENBQUMsT0FBTyxFQUEvQixJQUFJLENBS04sR0FKR1gsSUFBSSxDQUFBVyxNQUFPLEtBQUssV0FJbkIsR0FIQyxDQUFDLElBQUksQ0FBTyxLQUFTLENBQVQsU0FBUyxDQUFFLENBQUFYLElBQUksQ0FBQVcsTUFBTSxDQUFFLEVBQWxDLElBQUksQ0FHTixHQURDLENBQUMsSUFBSSxDQUFPLEtBQU8sQ0FBUCxPQUFPLENBQUUsQ0FBQVgsSUFBSSxDQUFBVyxNQUFNLENBQUUsRUFBaEMsSUFBSSxDQUNQLENBQ0YsRUFUQyxJQUFJLENBU0U7TUFBQUosQ0FBQSxPQUFBUCxJQUFBLENBQUFXLE1BQUE7TUFBQUosQ0FBQSxPQUFBMkIsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQTNCLENBQUE7SUFBQTtJQUVONEIsRUFBQSxHQUFBVSxLQUFLLENBQUFOLE1BQU8sS0FBSyxDQXVCakIsR0F0QkMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUNYLENBQUF2QyxJQUFJLENBQUFXLE1BQU8sS0FBSyxTQUE0QyxHQUE1RCxnQkFBNEQsR0FBNUQsa0JBQTJELENBQzlELEVBRkMsSUFBSSxDQXNCTixHQXZCQSxFQU1JLENBQUFvQyxNQUFNLEdBQUcsQ0FJVCxJQUhDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyxDQUNYQSxPQUFLLENBQUUsU0FBVSxDQUFBcEQsTUFBTSxDQUFDb0QsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUM1QyxFQUZDLElBQUksQ0FHUCxDQUNDLENBQUFGLEtBQUssQ0FBQVUsR0FBSSxDQUFDQyxNQVVWLEVBQUMsR0FFTDtJQUFBakQsQ0FBQSxNQUFBRSxXQUFBO0lBQUFGLENBQUEsTUFBQWMsYUFBQTtJQUFBZCxDQUFBLE9BQUFMLE1BQUE7SUFBQUssQ0FBQSxPQUFBTixNQUFBO0lBQUFNLENBQUEsT0FBQUosTUFBQTtJQUFBSSxDQUFBLE9BQUFQLElBQUEsQ0FBQXNDLFlBQUEsQ0FBQUMsTUFBQTtJQUFBaEMsQ0FBQSxPQUFBUCxJQUFBLENBQUF3QyxpQkFBQTtJQUFBakMsQ0FBQSxPQUFBUCxJQUFBLENBQUFXLE1BQUE7SUFBQUosQ0FBQSxPQUFBUCxJQUFBLENBQUF5QyxLQUFBO0lBQUFsQyxDQUFBLE9BQUFlLEVBQUE7SUFBQWYsQ0FBQSxPQUFBZ0IsRUFBQTtJQUFBaEIsQ0FBQSxPQUFBaUIsRUFBQTtJQUFBakIsQ0FBQSxPQUFBa0IsR0FBQTtJQUFBbEIsQ0FBQSxPQUFBbUIsR0FBQTtJQUFBbkIsQ0FBQSxPQUFBb0IsR0FBQTtJQUFBcEIsQ0FBQSxPQUFBcUIsR0FBQTtJQUFBckIsQ0FBQSxPQUFBc0IsR0FBQTtJQUFBdEIsQ0FBQSxPQUFBdUIsR0FBQTtJQUFBdkIsQ0FBQSxPQUFBd0IsR0FBQTtJQUFBeEIsQ0FBQSxPQUFBeUIsRUFBQTtJQUFBekIsQ0FBQSxPQUFBMEIsRUFBQTtJQUFBMUIsQ0FBQSxPQUFBMkIsRUFBQTtJQUFBM0IsQ0FBQSxPQUFBNEIsRUFBQTtJQUFBNUIsQ0FBQSxPQUFBNkIsRUFBQTtJQUFBN0IsQ0FBQSxPQUFBOEIsRUFBQTtFQUFBO0lBQUFmLEVBQUEsR0FBQWYsQ0FBQTtJQUFBZ0IsRUFBQSxHQUFBaEIsQ0FBQTtJQUFBaUIsRUFBQSxHQUFBakIsQ0FBQTtJQUFBa0IsR0FBQSxHQUFBbEIsQ0FBQTtJQUFBbUIsR0FBQSxHQUFBbkIsQ0FBQTtJQUFBb0IsR0FBQSxHQUFBcEIsQ0FBQTtJQUFBcUIsR0FBQSxHQUFBckIsQ0FBQTtJQUFBc0IsR0FBQSxHQUFBdEIsQ0FBQTtJQUFBdUIsR0FBQSxHQUFBdkIsQ0FBQTtJQUFBd0IsR0FBQSxHQUFBeEIsQ0FBQTtJQUFBeUIsRUFBQSxHQUFBekIsQ0FBQTtJQUFBMEIsRUFBQSxHQUFBMUIsQ0FBQTtJQUFBMkIsRUFBQSxHQUFBM0IsQ0FBQTtJQUFBNEIsRUFBQSxHQUFBNUIsQ0FBQTtJQUFBNkIsRUFBQSxHQUFBN0IsQ0FBQTtJQUFBOEIsRUFBQSxHQUFBOUIsQ0FBQTtFQUFBO0VBQUEsSUFBQXlDLEdBQUE7RUFBQSxJQUFBekMsQ0FBQSxTQUFBZSxFQUFBLElBQUFmLENBQUEsU0FBQXlCLEVBQUEsSUFBQXpCLENBQUEsU0FBQTBCLEVBQUEsSUFBQTFCLENBQUEsU0FBQTJCLEVBQUEsSUFBQTNCLENBQUEsU0FBQTRCLEVBQUE7SUFuQ0hhLEdBQUEsSUFBQyxFQUFHLENBQWUsYUFBUSxDQUFSLENBQUFoQixFQUFPLENBQUMsQ0FBTSxHQUFDLENBQUQsQ0FBQUMsRUFBQSxDQUFDLENBQ2hDLENBQUFDLEVBU00sQ0FFTCxDQUFBQyxFQXVCRCxDQUNGLEVBcENDLEVBQUcsQ0FvQ0U7SUFBQTVCLENBQUEsT0FBQWUsRUFBQTtJQUFBZixDQUFBLE9BQUF5QixFQUFBO0lBQUF6QixDQUFBLE9BQUEwQixFQUFBO0lBQUExQixDQUFBLE9BQUEyQixFQUFBO0lBQUEzQixDQUFBLE9BQUE0QixFQUFBO0lBQUE1QixDQUFBLE9BQUF5QyxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBekMsQ0FBQTtFQUFBO0VBQUEsSUFBQTBDLEdBQUE7RUFBQSxJQUFBMUMsQ0FBQSxTQUFBZ0IsRUFBQSxJQUFBaEIsQ0FBQSxTQUFBa0IsR0FBQSxJQUFBbEIsQ0FBQSxTQUFBbUIsR0FBQSxJQUFBbkIsQ0FBQSxTQUFBb0IsR0FBQSxJQUFBcEIsQ0FBQSxTQUFBeUMsR0FBQSxJQUFBekMsQ0FBQSxTQUFBNkIsRUFBQSxJQUFBN0IsQ0FBQSxTQUFBOEIsRUFBQTtJQW5FUlksR0FBQSxJQUFDLEVBQU0sQ0FDQyxLQUFzQixDQUF0QixDQUFBYixFQUFxQixDQUFDLENBRTFCLFFBVU8sQ0FWUCxDQUFBQyxFQVVNLENBQUMsQ0FFQ3BDLFFBQU0sQ0FBTkEsSUFBSyxDQUFDLENBQ1YsS0FBWSxDQUFaLENBQUF5QixHQUFXLENBQUMsQ0FDTixVQVdULENBWFMsQ0FBQUMsR0FXVixDQUFDLENBR0gsQ0FBQXFCLEdBb0NLLENBQ1AsRUFwRUMsRUFBTSxDQW9FRTtJQUFBekMsQ0FBQSxPQUFBZ0IsRUFBQTtJQUFBaEIsQ0FBQSxPQUFBa0IsR0FBQTtJQUFBbEIsQ0FBQSxPQUFBbUIsR0FBQTtJQUFBbkIsQ0FBQSxPQUFBb0IsR0FBQTtJQUFBcEIsQ0FBQSxPQUFBeUMsR0FBQTtJQUFBekMsQ0FBQSxPQUFBNkIsRUFBQTtJQUFBN0IsQ0FBQSxPQUFBOEIsRUFBQTtJQUFBOUIsQ0FBQSxPQUFBMEMsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQTFDLENBQUE7RUFBQTtFQUFBLElBQUEyQyxHQUFBO0VBQUEsSUFBQTNDLENBQUEsU0FBQWlCLEVBQUEsSUFBQWpCLENBQUEsU0FBQXFCLEdBQUEsSUFBQXJCLENBQUEsU0FBQXNCLEdBQUEsSUFBQXRCLENBQUEsU0FBQXVCLEdBQUEsSUFBQXZCLENBQUEsU0FBQXdCLEdBQUEsSUFBQXhCLENBQUEsU0FBQTBDLEdBQUE7SUExRVhDLEdBQUEsSUFBQyxFQUFHLENBQ1ksYUFBUSxDQUFSLENBQUF0QixHQUFPLENBQUMsQ0FDWixRQUFDLENBQUQsQ0FBQUMsR0FBQSxDQUFDLENBQ1gsU0FBUyxDQUFULENBQUFDLEdBQVEsQ0FBQyxDQUNFVCxTQUFhLENBQWJBLElBQVksQ0FBQyxDQUV4QixDQUFBNEIsR0FvRVEsQ0FDVixFQTNFQyxFQUFHLENBMkVFO0lBQUExQyxDQUFBLE9BQUFpQixFQUFBO0lBQUFqQixDQUFBLE9BQUFxQixHQUFBO0lBQUFyQixDQUFBLE9BQUFzQixHQUFBO0lBQUF0QixDQUFBLE9BQUF1QixHQUFBO0lBQUF2QixDQUFBLE9BQUF3QixHQUFBO0lBQUF4QixDQUFBLE9BQUEwQyxHQUFBO0lBQUExQyxDQUFBLE9BQUEyQyxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBM0MsQ0FBQTtFQUFBO0VBQUEsT0EzRU4yQyxHQTJFTTtBQUFBO0FBL0dILFNBQUFNLE9BQUFDLElBQUEsRUFBQUMsQ0FBQTtFQUFBLE9BaUdTLENBQUMsR0FBRyxDQUFNQSxHQUFDLENBQURBLEVBQUEsQ0FBQyxDQUFnQixhQUFRLENBQVIsUUFBUSxDQUNqQyxDQUFDLElBQUksQ0FBTSxJQUFNLENBQU4sTUFBTSxDQUFFLENBQUFELElBQUksQ0FBQUUsSUFBSSxDQUFFLEVBQTVCLElBQUksQ0FDSixDQUFBRixJQUFJLENBQUFHLFlBQWEsR0FBRyxDQUtwQixJQUpDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FDWCxLQUFHLENBQUUsQ0FBRSxDQUFBSCxJQUFJLENBQUFHLFlBQVksQ0FBRyxJQUFFLENBQzVCLENBQUFqRSxNQUFNLENBQUM4RCxJQUFJLENBQUFHLFlBQWEsRUFBRSxNQUFNLEVBQUUsQ0FDckMsRUFIQyxJQUFJLENBSVAsQ0FDRixFQVJDLEdBQUcsQ0FRRTtBQUFBO0FBekdmLFNBQUFoQixNQUFBaUIsQ0FBQTtFQUFBLE9BK0J1Q0EsQ0FBQyxDQUFBRixJQUFLLEtBQUssRUFBRTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/tasks/InProcessTeammateDetailDialog.tsx b/components/tasks/InProcessTeammateDetailDialog.tsx new file mode 100644 index 0000000..3f71c60 --- /dev/null +++ b/components/tasks/InProcessTeammateDetailDialog.tsx @@ -0,0 +1,266 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useMemo } from 'react'; +import type { DeepImmutable } from 'src/types/utils.js'; +import { useElapsedTime } from '../../hooks/useElapsedTime.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box, Text, useTheme } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +import { getEmptyToolPermissionContext } from '../../Tool.js'; +import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js'; +import { getTools } from '../../tools.js'; +import { formatNumber, truncateToWidth } from '../../utils/format.js'; +import { toInkColor } from '../../utils/ink.js'; +import { Byline } from '../design-system/Byline.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { renderToolActivity } from './renderToolActivity.js'; +import { describeTeammateActivity } from './taskStatusUtils.js'; +type Props = { + teammate: DeepImmutable; + onDone: () => void; + onKill?: () => void; + onBack?: () => void; + onForeground?: () => void; +}; +export function InProcessTeammateDetailDialog(t0) { + const $ = _c(63); + const { + teammate, + onDone, + onKill, + onBack, + onForeground + } = t0; + const [theme] = useTheme(); + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = getTools(getEmptyToolPermissionContext()); + $[0] = t1; + } else { + t1 = $[0]; + } + const tools = t1; + const elapsedTime = useElapsedTime(teammate.startTime, teammate.status === "running", 1000, teammate.totalPausedMs ?? 0); + let t2; + if ($[1] !== onDone) { + t2 = { + "confirm:yes": onDone + }; + $[1] = onDone; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t3 = { + context: "Confirmation" + }; + $[3] = t3; + } else { + t3 = $[3]; + } + useKeybindings(t2, t3); + let t4; + if ($[4] !== onBack || $[5] !== onDone || $[6] !== onForeground || $[7] !== onKill || $[8] !== teammate.status) { + t4 = e => { + if (e.key === " ") { + e.preventDefault(); + onDone(); + } else { + if (e.key === "left" && onBack) { + e.preventDefault(); + onBack(); + } else { + if (e.key === "x" && teammate.status === "running" && onKill) { + e.preventDefault(); + onKill(); + } else { + if (e.key === "f" && teammate.status === "running" && onForeground) { + e.preventDefault(); + onForeground(); + } + } + } + } + }; + $[4] = onBack; + $[5] = onDone; + $[6] = onForeground; + $[7] = onKill; + $[8] = teammate.status; + $[9] = t4; + } else { + t4 = $[9]; + } + const handleKeyDown = t4; + let t5; + if ($[10] !== teammate) { + t5 = describeTeammateActivity(teammate); + $[10] = teammate; + $[11] = t5; + } else { + t5 = $[11]; + } + const activity = t5; + const tokenCount = teammate.result?.totalTokens ?? teammate.progress?.tokenCount; + const toolUseCount = teammate.result?.totalToolUseCount ?? teammate.progress?.toolUseCount; + let t6; + if ($[12] !== teammate.prompt) { + t6 = truncateToWidth(teammate.prompt, 300); + $[12] = teammate.prompt; + $[13] = t6; + } else { + t6 = $[13]; + } + const displayPrompt = t6; + let t7; + if ($[14] !== teammate.identity.color) { + t7 = toInkColor(teammate.identity.color); + $[14] = teammate.identity.color; + $[15] = t7; + } else { + t7 = $[15]; + } + let t8; + if ($[16] !== t7 || $[17] !== teammate.identity.agentName) { + t8 = @{teammate.identity.agentName}; + $[16] = t7; + $[17] = teammate.identity.agentName; + $[18] = t8; + } else { + t8 = $[18]; + } + let t9; + if ($[19] !== activity) { + t9 = activity && ({activity}); + $[19] = activity; + $[20] = t9; + } else { + t9 = $[20]; + } + let t10; + if ($[21] !== t8 || $[22] !== t9) { + t10 = {t8}{t9}; + $[21] = t8; + $[22] = t9; + $[23] = t10; + } else { + t10 = $[23]; + } + const title = t10; + let t11; + if ($[24] !== teammate.status) { + t11 = teammate.status !== "running" && {teammate.status === "completed" ? "Completed" : teammate.status === "failed" ? "Failed" : "Stopped"}{" \xB7 "}; + $[24] = teammate.status; + $[25] = t11; + } else { + t11 = $[25]; + } + let t12; + if ($[26] !== tokenCount) { + t12 = tokenCount !== undefined && tokenCount > 0 && <> · {formatNumber(tokenCount)} tokens; + $[26] = tokenCount; + $[27] = t12; + } else { + t12 = $[27]; + } + let t13; + if ($[28] !== toolUseCount) { + t13 = toolUseCount !== undefined && toolUseCount > 0 && <>{" "}· {toolUseCount} {toolUseCount === 1 ? "tool" : "tools"}; + $[28] = toolUseCount; + $[29] = t13; + } else { + t13 = $[29]; + } + let t14; + if ($[30] !== elapsedTime || $[31] !== t12 || $[32] !== t13) { + t14 = {elapsedTime}{t12}{t13}; + $[30] = elapsedTime; + $[31] = t12; + $[32] = t13; + $[33] = t14; + } else { + t14 = $[33]; + } + let t15; + if ($[34] !== t11 || $[35] !== t14) { + t15 = {t11}{t14}; + $[34] = t11; + $[35] = t14; + $[36] = t15; + } else { + t15 = $[36]; + } + const subtitle = t15; + let t16; + if ($[37] !== onBack || $[38] !== onForeground || $[39] !== onKill || $[40] !== teammate.status) { + t16 = exitState => exitState.pending ? Press {exitState.keyName} again to exit : {onBack && }{teammate.status === "running" && onKill && }{teammate.status === "running" && onForeground && }; + $[37] = onBack; + $[38] = onForeground; + $[39] = onKill; + $[40] = teammate.status; + $[41] = t16; + } else { + t16 = $[41]; + } + let t17; + if ($[42] !== teammate.progress || $[43] !== teammate.status || $[44] !== theme) { + t17 = teammate.status === "running" && teammate.progress?.recentActivities && teammate.progress.recentActivities.length > 0 && Progress{teammate.progress.recentActivities.map((activity_0, i) => {i === teammate.progress.recentActivities.length - 1 ? "\u203A " : " "}{renderToolActivity(activity_0, tools, theme)})}; + $[42] = teammate.progress; + $[43] = teammate.status; + $[44] = theme; + $[45] = t17; + } else { + t17 = $[45]; + } + let t18; + if ($[46] === Symbol.for("react.memo_cache_sentinel")) { + t18 = Prompt; + $[46] = t18; + } else { + t18 = $[46]; + } + let t19; + if ($[47] !== displayPrompt) { + t19 = {t18}{displayPrompt}; + $[47] = displayPrompt; + $[48] = t19; + } else { + t19 = $[48]; + } + let t20; + if ($[49] !== teammate.error || $[50] !== teammate.status) { + t20 = teammate.status === "failed" && teammate.error && Error{teammate.error}; + $[49] = teammate.error; + $[50] = teammate.status; + $[51] = t20; + } else { + t20 = $[51]; + } + let t21; + if ($[52] !== onDone || $[53] !== subtitle || $[54] !== t16 || $[55] !== t17 || $[56] !== t19 || $[57] !== t20 || $[58] !== title) { + t21 = {t17}{t19}{t20}; + $[52] = onDone; + $[53] = subtitle; + $[54] = t16; + $[55] = t17; + $[56] = t19; + $[57] = t20; + $[58] = title; + $[59] = t21; + } else { + t21 = $[59]; + } + let t22; + if ($[60] !== handleKeyDown || $[61] !== t21) { + t22 = {t21}; + $[60] = handleKeyDown; + $[61] = t21; + $[62] = t22; + } else { + t22 = $[62]; + } + return t22; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZU1lbW8iLCJEZWVwSW1tdXRhYmxlIiwidXNlRWxhcHNlZFRpbWUiLCJLZXlib2FyZEV2ZW50IiwiQm94IiwiVGV4dCIsInVzZVRoZW1lIiwidXNlS2V5YmluZGluZ3MiLCJnZXRFbXB0eVRvb2xQZXJtaXNzaW9uQ29udGV4dCIsIkluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlIiwiZ2V0VG9vbHMiLCJmb3JtYXROdW1iZXIiLCJ0cnVuY2F0ZVRvV2lkdGgiLCJ0b0lua0NvbG9yIiwiQnlsaW5lIiwiRGlhbG9nIiwiS2V5Ym9hcmRTaG9ydGN1dEhpbnQiLCJyZW5kZXJUb29sQWN0aXZpdHkiLCJkZXNjcmliZVRlYW1tYXRlQWN0aXZpdHkiLCJQcm9wcyIsInRlYW1tYXRlIiwib25Eb25lIiwib25LaWxsIiwib25CYWNrIiwib25Gb3JlZ3JvdW5kIiwiSW5Qcm9jZXNzVGVhbW1hdGVEZXRhaWxEaWFsb2ciLCJ0MCIsIiQiLCJfYyIsInRoZW1lIiwidDEiLCJTeW1ib2wiLCJmb3IiLCJ0b29scyIsImVsYXBzZWRUaW1lIiwic3RhcnRUaW1lIiwic3RhdHVzIiwidG90YWxQYXVzZWRNcyIsInQyIiwidDMiLCJjb250ZXh0IiwidDQiLCJlIiwia2V5IiwicHJldmVudERlZmF1bHQiLCJoYW5kbGVLZXlEb3duIiwidDUiLCJhY3Rpdml0eSIsInRva2VuQ291bnQiLCJyZXN1bHQiLCJ0b3RhbFRva2VucyIsInByb2dyZXNzIiwidG9vbFVzZUNvdW50IiwidG90YWxUb29sVXNlQ291bnQiLCJ0NiIsInByb21wdCIsImRpc3BsYXlQcm9tcHQiLCJ0NyIsImlkZW50aXR5IiwiY29sb3IiLCJ0OCIsImFnZW50TmFtZSIsInQ5IiwidDEwIiwidGl0bGUiLCJ0MTEiLCJ0MTIiLCJ1bmRlZmluZWQiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJzdWJ0aXRsZSIsInQxNiIsImV4aXRTdGF0ZSIsInBlbmRpbmciLCJrZXlOYW1lIiwidDE3IiwicmVjZW50QWN0aXZpdGllcyIsImxlbmd0aCIsIm1hcCIsImFjdGl2aXR5XzAiLCJpIiwidDE4IiwidDE5IiwidDIwIiwiZXJyb3IiLCJ0MjEiLCJ0MjIiXSwic291cmNlcyI6WyJJblByb2Nlc3NUZWFtbWF0ZURldGFpbERpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHVzZU1lbW8gfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgRGVlcEltbXV0YWJsZSB9IGZyb20gJ3NyYy90eXBlcy91dGlscy5qcydcbmltcG9ydCB7IHVzZUVsYXBzZWRUaW1lIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlRWxhcHNlZFRpbWUuanMnXG5pbXBvcnQgdHlwZSB7IEtleWJvYXJkRXZlbnQgfSBmcm9tICcuLi8uLi9pbmsvZXZlbnRzL2tleWJvYXJkLWV2ZW50LmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0LCB1c2VUaGVtZSB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmdzIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7IGdldEVtcHR5VG9vbFBlcm1pc3Npb25Db250ZXh0IH0gZnJvbSAnLi4vLi4vVG9vbC5qcydcbmltcG9ydCB0eXBlIHsgSW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGUgfSBmcm9tICcuLi8uLi90YXNrcy9JblByb2Nlc3NUZWFtbWF0ZVRhc2svdHlwZXMuanMnXG5pbXBvcnQgeyBnZXRUb29scyB9IGZyb20gJy4uLy4uL3Rvb2xzLmpzJ1xuaW1wb3J0IHsgZm9ybWF0TnVtYmVyLCB0cnVuY2F0ZVRvV2lkdGggfSBmcm9tICcuLi8uLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQgeyB0b0lua0NvbG9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvaW5rLmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcbmltcG9ydCB7IEtleWJvYXJkU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9LZXlib2FyZFNob3J0Y3V0SGludC5qcydcbmltcG9ydCB7IHJlbmRlclRvb2xBY3Rpdml0eSB9IGZyb20gJy4vcmVuZGVyVG9vbEFjdGl2aXR5LmpzJ1xuaW1wb3J0IHsgZGVzY3JpYmVUZWFtbWF0ZUFjdGl2aXR5IH0gZnJvbSAnLi90YXNrU3RhdHVzVXRpbHMuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHRlYW1tYXRlOiBEZWVwSW1tdXRhYmxlPEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlPlxuICBvbkRvbmU6ICgpID0+IHZvaWRcbiAgb25LaWxsPzogKCkgPT4gdm9pZFxuICBvbkJhY2s/OiAoKSA9PiB2b2lkXG4gIG9uRm9yZWdyb3VuZD86ICgpID0+IHZvaWRcbn1cbmV4cG9ydCBmdW5jdGlvbiBJblByb2Nlc3NUZWFtbWF0ZURldGFpbERpYWxvZyh7XG4gIHRlYW1tYXRlLFxuICBvbkRvbmUsXG4gIG9uS2lsbCxcbiAgb25CYWNrLFxuICBvbkZvcmVncm91bmQsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZV0gPSB1c2VUaGVtZSgpXG4gIGNvbnN0IHRvb2xzID0gdXNlTWVtbygoKSA9PiBnZXRUb29scyhnZXRFbXB0eVRvb2xQZXJtaXNzaW9uQ29udGV4dCgpKSwgW10pXG5cbiAgY29uc3QgZWxhcHNlZFRpbWUgPSB1c2VFbGFwc2VkVGltZShcbiAgICB0ZWFtbWF0ZS5zdGFydFRpbWUsXG4gICAgdGVhbW1hdGUuc3RhdHVzID09PSAncnVubmluZycsXG4gICAgMTAwMCxcbiAgICB0ZWFtbWF0ZS50b3RhbFBhdXNlZE1zID8/IDAsXG4gIClcblxuICAvLyBSZXN0b3JlIGNvbmZpcm06eWVzIChFbnRlci95KSBkaXNtaXNzYWwg4oCUIERpYWxvZyBoYW5kbGVzIGNvbmZpcm06bm8gKEVzYylcbiAgdXNlS2V5YmluZGluZ3MoXG4gICAge1xuICAgICAgJ2NvbmZpcm06eWVzJzogb25Eb25lLFxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnQ29uZmlybWF0aW9uJyB9LFxuICApXG5cbiAgY29uc3QgaGFuZGxlS2V5RG93biA9IChlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgaWYgKGUua2V5ID09PSAnICcpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25Eb25lKClcbiAgICB9IGVsc2UgaWYgKGUua2V5ID09PSAnbGVmdCcgJiYgb25CYWNrKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIG9uQmFjaygpXG4gICAgfSBlbHNlIGlmIChlLmtleSA9PT0gJ3gnICYmIHRlYW1tYXRlLnN0YXR1cyA9PT0gJ3J1bm5pbmcnICYmIG9uS2lsbCkge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBvbktpbGwoKVxuICAgIH0gZWxzZSBpZiAoZS5rZXkgPT09ICdmJyAmJiB0ZWFtbWF0ZS5zdGF0dXMgPT09ICdydW5uaW5nJyAmJiBvbkZvcmVncm91bmQpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25Gb3JlZ3JvdW5kKClcbiAgICB9XG4gIH1cblxuICBjb25zdCBhY3Rpdml0eSA9IGRlc2NyaWJlVGVhbW1hdGVBY3Rpdml0eSh0ZWFtbWF0ZSlcblxuICBjb25zdCB0b2tlbkNvdW50ID1cbiAgICB0ZWFtbWF0ZS5yZXN1bHQ/LnRvdGFsVG9rZW5zID8/IHRlYW1tYXRlLnByb2dyZXNzPy50b2tlbkNvdW50XG4gIGNvbnN0IHRvb2xVc2VDb3VudCA9XG4gICAgdGVhbW1hdGUucmVzdWx0Py50b3RhbFRvb2xVc2VDb3VudCA/PyB0ZWFtbWF0ZS5wcm9ncmVzcz8udG9vbFVzZUNvdW50XG5cbiAgY29uc3QgZGlzcGxheVByb21wdCA9IHRydW5jYXRlVG9XaWR0aCh0ZWFtbWF0ZS5wcm9tcHQsIDMwMClcblxuICBjb25zdCB0aXRsZSA9IChcbiAgICA8VGV4dD5cbiAgICAgIDxUZXh0IGNvbG9yPXt0b0lua0NvbG9yKHRlYW1tYXRlLmlkZW50aXR5LmNvbG9yKX0+XG4gICAgICAgIEB7dGVhbW1hdGUuaWRlbnRpdHkuYWdlbnROYW1lfVxuICAgICAgPC9UZXh0PlxuICAgICAge2FjdGl2aXR5ICYmIDxUZXh0IGRpbUNvbG9yPiAoe2FjdGl2aXR5fSk8L1RleHQ+fVxuICAgIDwvVGV4dD5cbiAgKVxuXG4gIGNvbnN0IHN1YnRpdGxlID0gKFxuICAgIDxUZXh0PlxuICAgICAge3RlYW1tYXRlLnN0YXR1cyAhPT0gJ3J1bm5pbmcnICYmIChcbiAgICAgICAgPFRleHRcbiAgICAgICAgICBjb2xvcj17XG4gICAgICAgICAgICB0ZWFtbWF0ZS5zdGF0dXMgPT09ICdjb21wbGV0ZWQnXG4gICAgICAgICAgICAgID8gJ3N1Y2Nlc3MnXG4gICAgICAgICAgICAgIDogdGVhbW1hdGUuc3RhdHVzID09PSAna2lsbGVkJ1xuICAgICAgICAgICAgICAgID8gJ3dhcm5pbmcnXG4gICAgICAgICAgICAgICAgOiAnZXJyb3InXG4gICAgICAgICAgfVxuICAgICAgICA+XG4gICAgICAgICAge3RlYW1tYXRlLnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCdcbiAgICAgICAgICAgID8gJ0NvbXBsZXRlZCdcbiAgICAgICAgICAgIDogdGVhbW1hdGUuc3RhdHVzID09PSAnZmFpbGVkJ1xuICAgICAgICAgICAgICA/ICdGYWlsZWQnXG4gICAgICAgICAgICAgIDogJ1N0b3BwZWQnfVxuICAgICAgICAgIHsnIMK3ICd9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICl9XG4gICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAge2VsYXBzZWRUaW1lfVxuICAgICAgICB7dG9rZW5Db3VudCAhPT0gdW5kZWZpbmVkICYmIHRva2VuQ291bnQgPiAwICYmIChcbiAgICAgICAgICA8PiDCtyB7Zm9ybWF0TnVtYmVyKHRva2VuQ291bnQpfSB0b2tlbnM8Lz5cbiAgICAgICAgKX1cbiAgICAgICAge3Rvb2xVc2VDb3VudCAhPT0gdW5kZWZpbmVkICYmIHRvb2xVc2VDb3VudCA+IDAgJiYgKFxuICAgICAgICAgIDw+XG4gICAgICAgICAgICB7JyAnfVxuICAgICAgICAgICAgwrcge3Rvb2xVc2VDb3VudH0ge3Rvb2xVc2VDb3VudCA9PT0gMSA/ICd0b29sJyA6ICd0b29scyd9XG4gICAgICAgICAgPC8+XG4gICAgICAgICl9XG4gICAgICA8L1RleHQ+XG4gICAgPC9UZXh0PlxuICApXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgIHRhYkluZGV4PXswfVxuICAgICAgYXV0b0ZvY3VzXG4gICAgICBvbktleURvd249e2hhbmRsZUtleURvd259XG4gICAgPlxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT17dGl0bGV9XG4gICAgICAgIHN1YnRpdGxlPXtzdWJ0aXRsZX1cbiAgICAgICAgb25DYW5jZWw9e29uRG9uZX1cbiAgICAgICAgY29sb3I9XCJiYWNrZ3JvdW5kXCJcbiAgICAgICAgaW5wdXRHdWlkZT17ZXhpdFN0YXRlID0+XG4gICAgICAgICAgZXhpdFN0YXRlLnBlbmRpbmcgPyAoXG4gICAgICAgICAgICA8VGV4dD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8L1RleHQ+XG4gICAgICAgICAgKSA6IChcbiAgICAgICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgICAgIHtvbkJhY2sgJiYgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwi4oaQXCIgYWN0aW9uPVwiZ28gYmFja1wiIC8+fVxuICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJFc2MvRW50ZXIvU3BhY2VcIiBhY3Rpb249XCJjbG9zZVwiIC8+XG4gICAgICAgICAgICAgIHt0ZWFtbWF0ZS5zdGF0dXMgPT09ICdydW5uaW5nJyAmJiBvbktpbGwgJiYgKFxuICAgICAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cInhcIiBhY3Rpb249XCJzdG9wXCIgLz5cbiAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAge3RlYW1tYXRlLnN0YXR1cyA9PT0gJ3J1bm5pbmcnICYmIG9uRm9yZWdyb3VuZCAmJiAoXG4gICAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiZlwiIGFjdGlvbj1cImZvcmVncm91bmRcIiAvPlxuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgPC9CeWxpbmU+XG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICA+XG4gICAgICAgIHsvKiBSZWNlbnQgYWN0aXZpdGllcyBmb3IgcnVubmluZyB0ZWFtbWF0ZXMgKi99XG4gICAgICAgIHt0ZWFtbWF0ZS5zdGF0dXMgPT09ICdydW5uaW5nJyAmJlxuICAgICAgICAgIHRlYW1tYXRlLnByb2dyZXNzPy5yZWNlbnRBY3Rpdml0aWVzICYmXG4gICAgICAgICAgdGVhbW1hdGUucHJvZ3Jlc3MucmVjZW50QWN0aXZpdGllcy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgICA8VGV4dCBib2xkIGRpbUNvbG9yPlxuICAgICAgICAgICAgICAgIFByb2dyZXNzXG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAge3RlYW1tYXRlLnByb2dyZXNzLnJlY2VudEFjdGl2aXRpZXMubWFwKChhY3Rpdml0eSwgaSkgPT4gKFxuICAgICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgICBrZXk9e2l9XG4gICAgICAgICAgICAgICAgICBkaW1Db2xvcj17aSA8IHRlYW1tYXRlLnByb2dyZXNzIS5yZWNlbnRBY3Rpdml0aWVzIS5sZW5ndGggLSAxfVxuICAgICAgICAgICAgICAgICAgd3JhcD1cInRydW5jYXRlLWVuZFwiXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAge2kgPT09IHRlYW1tYXRlLnByb2dyZXNzIS5yZWNlbnRBY3Rpdml0aWVzIS5sZW5ndGggLSAxXG4gICAgICAgICAgICAgICAgICAgID8gJ+KAuiAnXG4gICAgICAgICAgICAgICAgICAgIDogJyAgJ31cbiAgICAgICAgICAgICAgICAgIHtyZW5kZXJUb29sQWN0aXZpdHkoYWN0aXZpdHksIHRvb2xzLCB0aGVtZSl9XG4gICAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgICApKX1cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICl9XG5cbiAgICAgICAgey8qIFByb21wdCBzZWN0aW9uICovfVxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxUZXh0IGJvbGQgZGltQ29sb3I+XG4gICAgICAgICAgICBQcm9tcHRcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgd3JhcD1cIndyYXBcIj57ZGlzcGxheVByb21wdH08L1RleHQ+XG4gICAgICAgIDwvQm94PlxuXG4gICAgICAgIHsvKiBFcnJvciBkZXRhaWxzIGlmIGZhaWxlZCAqL31cbiAgICAgICAge3RlYW1tYXRlLnN0YXR1cyA9PT0gJ2ZhaWxlZCcgJiYgdGVhbW1hdGUuZXJyb3IgJiYgKFxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCBib2xkIGNvbG9yPVwiZXJyb3JcIj5cbiAgICAgICAgICAgICAgRXJyb3JcbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIiB3cmFwPVwid3JhcFwiPlxuICAgICAgICAgICAgICB7dGVhbW1hdGUuZXJyb3J9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG4gICAgICA8L0RpYWxvZz5cbiAgICA8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 \ No newline at end of file diff --git a/components/tasks/RemoteSessionDetailDialog.tsx b/components/tasks/RemoteSessionDetailDialog.tsx new file mode 100644 index 0000000..153cd7a --- /dev/null +++ b/components/tasks/RemoteSessionDetailDialog.tsx @@ -0,0 +1,904 @@ +import { c as _c } from "react/compiler-runtime"; +import figures from 'figures'; +import React, { useMemo, useState } from 'react'; +import type { SDKMessage } from 'src/entrypoints/agentSdkTypes.js'; +import type { ToolUseContext } from 'src/Tool.js'; +import type { DeepImmutable } from 'src/types/utils.js'; +import type { CommandResultDisplay } from '../../commands.js'; +import { DIAMOND_FILLED, DIAMOND_OPEN } from '../../constants/figures.js'; +import { useElapsedTime } from '../../hooks/useElapsedTime.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box, Link, Text } from '../../ink.js'; +import type { RemoteAgentTaskState } from '../../tasks/RemoteAgentTask/RemoteAgentTask.js'; +import { getRemoteTaskSessionUrl } from '../../tasks/RemoteAgentTask/RemoteAgentTask.js'; +import { AGENT_TOOL_NAME, LEGACY_AGENT_TOOL_NAME } from '../../tools/AgentTool/constants.js'; +import { ASK_USER_QUESTION_TOOL_NAME } from '../../tools/AskUserQuestionTool/prompt.js'; +import { EXIT_PLAN_MODE_V2_TOOL_NAME } from '../../tools/ExitPlanModeTool/constants.js'; +import { openBrowser } from '../../utils/browser.js'; +import { errorMessage } from '../../utils/errors.js'; +import { formatDuration, truncateToWidth } from '../../utils/format.js'; +import { toInternalMessages } from '../../utils/messages/mappers.js'; +import { EMPTY_LOOKUPS, normalizeMessages } from '../../utils/messages.js'; +import { plural } from '../../utils/stringUtils.js'; +import { teleportResumeCodeSession } from '../../utils/teleport.js'; +import { Select } from '../CustomSelect/select.js'; +import { Byline } from '../design-system/Byline.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +import { Message } from '../Message.js'; +import { formatReviewStageCounts, RemoteSessionProgress } from './RemoteSessionProgress.js'; +type Props = { + session: DeepImmutable; + toolUseContext: ToolUseContext; + onDone: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; + onBack?: () => void; + onKill?: () => void; +}; + +// Compact one-line summary: tool name + first meaningful string arg. +// Lighter than tool.renderToolUseMessage (no registry lookup / schema parse). +// Collapses whitespace so multi-line inputs (e.g. Bash command text) +// render on one line. +export function formatToolUseSummary(name: string, input: unknown): string { + // plan_ready phase is only reached via ExitPlanMode tool + if (name === EXIT_PLAN_MODE_V2_TOOL_NAME) { + return 'Review the plan in Claude Code on the web'; + } + if (!input || typeof input !== 'object') return name; + // AskUserQuestion: show the question text as a CTA, not the tool name. + // Input shape is {questions: [{question, header, options}]}. + if (name === ASK_USER_QUESTION_TOOL_NAME && 'questions' in input) { + const qs = input.questions; + if (Array.isArray(qs) && qs[0] && typeof qs[0] === 'object') { + // Prefer question (full text) over header (max-12-char tag). header + // is a required schema field so checking it first would make the + // question fallback dead code. + const q = 'question' in qs[0] && typeof qs[0].question === 'string' && qs[0].question ? qs[0].question : 'header' in qs[0] && typeof qs[0].header === 'string' ? qs[0].header : null; + if (q) { + const oneLine = q.replace(/\s+/g, ' ').trim(); + return `Answer in browser: ${truncateToWidth(oneLine, 50)}`; + } + } + } + for (const v of Object.values(input)) { + if (typeof v === 'string' && v.trim()) { + const oneLine = v.replace(/\s+/g, ' ').trim(); + return `${name} ${truncateToWidth(oneLine, 60)}`; + } + } + return name; +} +const PHASE_LABEL = { + needs_input: 'input required', + plan_ready: 'ready' +} as const; +const AGENT_VERB = { + needs_input: 'waiting', + plan_ready: 'done' +} as const; +function UltraplanSessionDetail(t0) { + const $ = _c(70); + const { + session, + onDone, + onBack, + onKill + } = t0; + const running = session.status === "running" || session.status === "pending"; + const phase = session.ultraplanPhase; + const statusText = running ? phase ? PHASE_LABEL[phase] : "running" : session.status; + const elapsedTime = useElapsedTime(session.startTime, running, 1000, 0, session.endTime); + let spawns = 0; + let calls = 0; + let lastBlock = null; + for (const msg of session.log) { + if (msg.type !== "assistant") { + continue; + } + for (const block of msg.message.content) { + if (block.type !== "tool_use") { + continue; + } + calls++; + lastBlock = block; + if (block.name === AGENT_TOOL_NAME || block.name === LEGACY_AGENT_TOOL_NAME) { + spawns++; + } + } + } + const t1 = 1 + spawns; + let t2; + if ($[0] !== lastBlock) { + t2 = lastBlock ? formatToolUseSummary(lastBlock.name, lastBlock.input) : null; + $[0] = lastBlock; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== calls || $[3] !== t1 || $[4] !== t2) { + t3 = { + agentsWorking: t1, + toolCalls: calls, + lastToolCall: t2 + }; + $[2] = calls; + $[3] = t1; + $[4] = t2; + $[5] = t3; + } else { + t3 = $[5]; + } + const { + agentsWorking, + toolCalls, + lastToolCall + } = t3; + let t4; + if ($[6] !== session.sessionId) { + t4 = getRemoteTaskSessionUrl(session.sessionId); + $[6] = session.sessionId; + $[7] = t4; + } else { + t4 = $[7]; + } + const sessionUrl = t4; + let t5; + if ($[8] !== onBack || $[9] !== onDone) { + t5 = onBack ?? (() => onDone("Remote session details dismissed", { + display: "system" + })); + $[8] = onBack; + $[9] = onDone; + $[10] = t5; + } else { + t5 = $[10]; + } + const goBackOrClose = t5; + const [confirmingStop, setConfirmingStop] = useState(false); + if (confirmingStop) { + let t6; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t6 = () => setConfirmingStop(false); + $[11] = t6; + } else { + t6 = $[11]; + } + let t7; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t7 = This will terminate the Claude Code on the web session.; + $[12] = t7; + } else { + t7 = $[12]; + } + let t8; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t8 = { + label: "Terminate session", + value: "stop" as const + }; + $[13] = t8; + } else { + t8 = $[13]; + } + let t9; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t9 = [t8, { + label: "Back", + value: "back" as const + }]; + $[14] = t9; + } else { + t9 = $[14]; + } + let t10; + if ($[15] !== goBackOrClose || $[16] !== onKill) { + t10 = {t7}; + $[58] = t22; + $[59] = t23; + $[60] = t24; + } else { + t24 = $[60]; + } + let t25; + if ($[61] !== t15 || $[62] !== t16 || $[63] !== t18 || $[64] !== t24) { + t25 = {t15}{t16}{t18}{t24}; + $[61] = t15; + $[62] = t16; + $[63] = t18; + $[64] = t24; + $[65] = t25; + } else { + t25 = $[65]; + } + let t26; + if ($[66] !== goBackOrClose || $[67] !== t10 || $[68] !== t25) { + t26 = {t25}; + $[66] = goBackOrClose; + $[67] = t10; + $[68] = t25; + $[69] = t26; + } else { + t26 = $[69]; + } + return t26; +} +const STAGES = ['finding', 'verifying', 'synthesizing'] as const; +const STAGE_LABELS: Record<(typeof STAGES)[number], string> = { + finding: 'Find', + verifying: 'Verify', + synthesizing: 'Dedupe' +}; + +// Setup → Find → Verify → Dedupe pipeline. Current stage in cloud teal, +// rest dim. When completed, all stages dim with a trailing green ✓. The +// "Setup" label shows before the orchestrator writes its first progress +// snapshot (container boot + repo clone), so the 0-found display doesn't +// look like a hung finder. +function StagePipeline(t0) { + const $ = _c(15); + const { + stage, + completed, + hasProgress + } = t0; + let t1; + if ($[0] !== stage) { + t1 = stage ? STAGES.indexOf(stage) : -1; + $[0] = stage; + $[1] = t1; + } else { + t1 = $[1]; + } + const currentIdx = t1; + const inSetup = !completed && !hasProgress; + let t2; + if ($[2] !== inSetup) { + t2 = inSetup ? Setup : Setup; + $[2] = inSetup; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t3 = ; + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] !== completed || $[6] !== currentIdx || $[7] !== inSetup) { + t4 = STAGES.map((s, i) => { + const isCurrent = !completed && !inSetup && i === currentIdx; + return {i > 0 && }{isCurrent ? {STAGE_LABELS[s]} : {STAGE_LABELS[s]}}; + }); + $[5] = completed; + $[6] = currentIdx; + $[7] = inSetup; + $[8] = t4; + } else { + t4 = $[8]; + } + let t5; + if ($[9] !== completed) { + t5 = completed && ; + $[9] = completed; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] !== t2 || $[12] !== t4 || $[13] !== t5) { + t6 = {t2}{t3}{t4}{t5}; + $[11] = t2; + $[12] = t4; + $[13] = t5; + $[14] = t6; + } else { + t6 = $[14]; + } + return t6; +} + +// Stage-appropriate counts line. Running-state formatting delegates to +// formatReviewStageCounts (shared with the pill) so the two views can't +// drift; completed state is dialog-specific (findings summary). +function reviewCountsLine(session: DeepImmutable): string { + const p = session.reviewProgress; + // No progress data — the orchestrator never wrote a snapshot. Don't + // claim "0 findings" when completed; we just don't know. + if (!p) return session.status === 'completed' ? 'done' : 'setting up'; + const verified = p.bugsVerified; + const refuted = p.bugsRefuted ?? 0; + if (session.status === 'completed') { + const parts = [`${verified} ${plural(verified, 'finding')}`]; + if (refuted > 0) parts.push(`${refuted} refuted`); + return parts.join(' · '); + } + return formatReviewStageCounts(p.stage, p.bugsFound, verified, refuted); +} +type MenuAction = 'open' | 'stop' | 'back' | 'dismiss'; +function ReviewSessionDetail(t0) { + const $ = _c(56); + const { + session, + onDone, + onBack, + onKill + } = t0; + const completed = session.status === "completed"; + const running = session.status === "running" || session.status === "pending"; + const [confirmingStop, setConfirmingStop] = useState(false); + const elapsedTime = useElapsedTime(session.startTime, running, 1000, 0, session.endTime); + let t1; + if ($[0] !== onDone) { + t1 = () => onDone("Remote session details dismissed", { + display: "system" + }); + $[0] = onDone; + $[1] = t1; + } else { + t1 = $[1]; + } + const handleClose = t1; + const goBackOrClose = onBack ?? handleClose; + let t2; + if ($[2] !== session.sessionId) { + t2 = getRemoteTaskSessionUrl(session.sessionId); + $[2] = session.sessionId; + $[3] = t2; + } else { + t2 = $[3]; + } + const sessionUrl = t2; + const statusLabel = completed ? "ready" : running ? "running" : session.status; + if (confirmingStop) { + let t3; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t3 = () => setConfirmingStop(false); + $[4] = t3; + } else { + t3 = $[4]; + } + let t4; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t4 = This archives the remote session and stops local tracking. The review will not complete and any findings so far are discarded.; + $[5] = t4; + } else { + t4 = $[5]; + } + let t5; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t5 = { + label: "Stop ultrareview", + value: "stop" as const + }; + $[6] = t5; + } else { + t5 = $[6]; + } + let t6; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t6 = [t5, { + label: "Back", + value: "back" as const + }]; + $[7] = t6; + } else { + t6 = $[7]; + } + let t7; + if ($[8] !== goBackOrClose || $[9] !== onKill) { + t7 = {t4}; + $[45] = handleSelect; + $[46] = options; + $[47] = t18; + } else { + t18 = $[47]; + } + let t19; + if ($[48] !== t12 || $[49] !== t17 || $[50] !== t18) { + t19 = {t12}{t17}{t18}; + $[48] = t12; + $[49] = t17; + $[50] = t18; + $[51] = t19; + } else { + t19 = $[51]; + } + let t20; + if ($[52] !== goBackOrClose || $[53] !== t19 || $[54] !== t9) { + t20 = {t19}; + $[52] = goBackOrClose; + $[53] = t19; + $[54] = t9; + $[55] = t20; + } else { + t20 = $[55]; + } + return t20; +} +function _temp(exitState) { + return exitState.pending ? Press {exitState.keyName} again to exit : ; +} +export function RemoteSessionDetailDialog({ + session, + toolUseContext, + onDone, + onBack, + onKill +}: Props): React.ReactNode { + const [isTeleporting, setIsTeleporting] = useState(false); + const [teleportError, setTeleportError] = useState(null); + + // Get last few messages from remote session for display. + // Scan all messages (not just the last 3 raw entries) because the tail of + // the log is often thinking-only blocks that normalise to 'progress' type. + // Placed before the early returns so hook call order is stable (Rules of Hooks). + // Ultraplan/review sessions never read this — skip the normalize work for them. + const lastMessages = useMemo(() => { + if (session.isUltraplan || session.isRemoteReview) return []; + return normalizeMessages(toInternalMessages(session.log as SDKMessage[])).filter(_ => _.type !== 'progress').slice(-3); + }, [session]); + if (session.isUltraplan) { + return ; + } + + // Review sessions get the stage-pipeline view; everything else keeps the + // generic label/value + recent-messages dialog below. + if (session.isRemoteReview) { + return ; + } + const handleClose = () => onDone('Remote session details dismissed', { + display: 'system' + }); + + // Component-specific shortcuts shown in UI hints (t=teleport, space=dismiss, + // left=back). These are state-dependent actions, not standard dialog keybindings. + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === ' ') { + e.preventDefault(); + onDone('Remote session details dismissed', { + display: 'system' + }); + } else if (e.key === 'left' && onBack) { + e.preventDefault(); + onBack(); + } else if (e.key === 't' && !isTeleporting) { + e.preventDefault(); + void handleTeleport(); + } else if (e.key === 'return') { + e.preventDefault(); + handleClose(); + } + }; + + // Handle teleporting to remote session + async function handleTeleport(): Promise { + setIsTeleporting(true); + setTeleportError(null); + try { + await teleportResumeCodeSession(session.sessionId); + } catch (err) { + setTeleportError(errorMessage(err)); + } finally { + setIsTeleporting(false); + } + } + + // Truncate title if too long (for display purposes) + const displayTitle = truncateToWidth(session.title, 50); + + // Map TaskStatus to display status (handle 'pending') + const displayStatus = session.status === 'pending' ? 'starting' : session.status; + return + exitState.pending ? Press {exitState.keyName} again to exit : + {onBack && } + + {!isTeleporting && } + }> + + + Status:{' '} + {displayStatus === 'running' || displayStatus === 'starting' ? {displayStatus} : displayStatus === 'completed' ? {displayStatus} : {displayStatus}} + + + Runtime:{' '} + {formatDuration((session.endTime ?? Date.now()) - session.startTime)} + + + Title: {displayTitle} + + + Progress:{' '} + + + + Session URL:{' '} + + {getRemoteTaskSessionUrl(session.sessionId)} + + + + + {/* Remote session messages section */} + {session.log.length > 0 && + + Recent messages: + + + {lastMessages.map((msg, i) => 0} tools={toolUseContext.options.tools} commands={toolUseContext.options.commands} verbose={toolUseContext.options.verbose} inProgressToolUseIDs={new Set()} progressMessagesForMessage={[]} shouldAnimate={false} shouldShowDot={false} style="condensed" isTranscriptMode={false} isStatic={true} />)} + + + + Showing last {lastMessages.length} of {session.log.length}{' '} + messages + + + } + + {/* Teleport error message */} + {teleportError && + Teleport failed: {teleportError} + } + + {/* Teleporting status */} + {isTeleporting && Teleporting to session…} + + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VNZW1vIiwidXNlU3RhdGUiLCJTREtNZXNzYWdlIiwiVG9vbFVzZUNvbnRleHQiLCJEZWVwSW1tdXRhYmxlIiwiQ29tbWFuZFJlc3VsdERpc3BsYXkiLCJESUFNT05EX0ZJTExFRCIsIkRJQU1PTkRfT1BFTiIsInVzZUVsYXBzZWRUaW1lIiwiS2V5Ym9hcmRFdmVudCIsIkJveCIsIkxpbmsiLCJUZXh0IiwiUmVtb3RlQWdlbnRUYXNrU3RhdGUiLCJnZXRSZW1vdGVUYXNrU2Vzc2lvblVybCIsIkFHRU5UX1RPT0xfTkFNRSIsIkxFR0FDWV9BR0VOVF9UT09MX05BTUUiLCJBU0tfVVNFUl9RVUVTVElPTl9UT09MX05BTUUiLCJFWElUX1BMQU5fTU9ERV9WMl9UT09MX05BTUUiLCJvcGVuQnJvd3NlciIsImVycm9yTWVzc2FnZSIsImZvcm1hdER1cmF0aW9uIiwidHJ1bmNhdGVUb1dpZHRoIiwidG9JbnRlcm5hbE1lc3NhZ2VzIiwiRU1QVFlfTE9PS1VQUyIsIm5vcm1hbGl6ZU1lc3NhZ2VzIiwicGx1cmFsIiwidGVsZXBvcnRSZXN1bWVDb2RlU2Vzc2lvbiIsIlNlbGVjdCIsIkJ5bGluZSIsIkRpYWxvZyIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwiTWVzc2FnZSIsImZvcm1hdFJldmlld1N0YWdlQ291bnRzIiwiUmVtb3RlU2Vzc2lvblByb2dyZXNzIiwiUHJvcHMiLCJzZXNzaW9uIiwidG9vbFVzZUNvbnRleHQiLCJvbkRvbmUiLCJyZXN1bHQiLCJvcHRpb25zIiwiZGlzcGxheSIsIm9uQmFjayIsIm9uS2lsbCIsImZvcm1hdFRvb2xVc2VTdW1tYXJ5IiwibmFtZSIsImlucHV0IiwicXMiLCJxdWVzdGlvbnMiLCJBcnJheSIsImlzQXJyYXkiLCJxIiwicXVlc3Rpb24iLCJoZWFkZXIiLCJvbmVMaW5lIiwicmVwbGFjZSIsInRyaW0iLCJ2IiwiT2JqZWN0IiwidmFsdWVzIiwiUEhBU0VfTEFCRUwiLCJuZWVkc19pbnB1dCIsInBsYW5fcmVhZHkiLCJjb25zdCIsIkFHRU5UX1ZFUkIiLCJVbHRyYXBsYW5TZXNzaW9uRGV0YWlsIiwidDAiLCIkIiwiX2MiLCJydW5uaW5nIiwic3RhdHVzIiwicGhhc2UiLCJ1bHRyYXBsYW5QaGFzZSIsInN0YXR1c1RleHQiLCJlbGFwc2VkVGltZSIsInN0YXJ0VGltZSIsImVuZFRpbWUiLCJzcGF3bnMiLCJjYWxscyIsImxhc3RCbG9jayIsIm1zZyIsImxvZyIsInR5cGUiLCJibG9jayIsIm1lc3NhZ2UiLCJjb250ZW50IiwidDEiLCJ0MiIsInQzIiwiYWdlbnRzV29ya2luZyIsInRvb2xDYWxscyIsImxhc3RUb29sQ2FsbCIsInQ0Iiwic2Vzc2lvbklkIiwic2Vzc2lvblVybCIsInQ1IiwiZ29CYWNrT3JDbG9zZSIsImNvbmZpcm1pbmdTdG9wIiwic2V0Q29uZmlybWluZ1N0b3AiLCJ0NiIsIlN5bWJvbCIsImZvciIsInQ3IiwidDgiLCJsYWJlbCIsInZhbHVlIiwidDkiLCJ0MTAiLCJ0MTEiLCJ0aWNrIiwidDEyIiwidDEzIiwidDE0IiwidDE1IiwidDE2IiwidDE3IiwidDE4IiwidDE5IiwidDIwIiwidDIxIiwidDIyIiwidDIzIiwidl8wIiwidDI0IiwidDI1IiwidDI2IiwiU1RBR0VTIiwiU1RBR0VfTEFCRUxTIiwiUmVjb3JkIiwiZmluZGluZyIsInZlcmlmeWluZyIsInN5bnRoZXNpemluZyIsIlN0YWdlUGlwZWxpbmUiLCJzdGFnZSIsImNvbXBsZXRlZCIsImhhc1Byb2dyZXNzIiwiaW5kZXhPZiIsImN1cnJlbnRJZHgiLCJpblNldHVwIiwibWFwIiwicyIsImkiLCJpc0N1cnJlbnQiLCJyZXZpZXdDb3VudHNMaW5lIiwicCIsInJldmlld1Byb2dyZXNzIiwidmVyaWZpZWQiLCJidWdzVmVyaWZpZWQiLCJyZWZ1dGVkIiwiYnVnc1JlZnV0ZWQiLCJwYXJ0cyIsInB1c2giLCJqb2luIiwiYnVnc0ZvdW5kIiwiTWVudUFjdGlvbiIsIlJldmlld1Nlc3Npb25EZXRhaWwiLCJoYW5kbGVDbG9zZSIsInN0YXR1c0xhYmVsIiwiYWN0aW9uIiwiYmI0NSIsImhhbmRsZVNlbGVjdCIsIl90ZW1wIiwiZXhpdFN0YXRlIiwicGVuZGluZyIsImtleU5hbWUiLCJSZW1vdGVTZXNzaW9uRGV0YWlsRGlhbG9nIiwiUmVhY3ROb2RlIiwiaXNUZWxlcG9ydGluZyIsInNldElzVGVsZXBvcnRpbmciLCJ0ZWxlcG9ydEVycm9yIiwic2V0VGVsZXBvcnRFcnJvciIsImxhc3RNZXNzYWdlcyIsImlzVWx0cmFwbGFuIiwiaXNSZW1vdGVSZXZpZXciLCJmaWx0ZXIiLCJfIiwic2xpY2UiLCJoYW5kbGVLZXlEb3duIiwiZSIsImtleSIsInByZXZlbnREZWZhdWx0IiwiaGFuZGxlVGVsZXBvcnQiLCJQcm9taXNlIiwiZXJyIiwiZGlzcGxheVRpdGxlIiwidGl0bGUiLCJkaXNwbGF5U3RhdHVzIiwiRGF0ZSIsIm5vdyIsImxlbmd0aCIsInRvb2xzIiwiY29tbWFuZHMiLCJ2ZXJib3NlIiwiU2V0Il0sInNvdXJjZXMiOlsiUmVtb3RlU2Vzc2lvbkRldGFpbERpYWxvZy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCBSZWFjdCwgeyB1c2VNZW1vLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBTREtNZXNzYWdlIH0gZnJvbSAnc3JjL2VudHJ5cG9pbnRzL2FnZW50U2RrVHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xVc2VDb250ZXh0IH0gZnJvbSAnc3JjL1Rvb2wuanMnXG5pbXBvcnQgdHlwZSB7IERlZXBJbW11dGFibGUgfSBmcm9tICdzcmMvdHlwZXMvdXRpbHMuanMnXG5pbXBvcnQgdHlwZSB7IENvbW1hbmRSZXN1bHREaXNwbGF5IH0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyBESUFNT05EX0ZJTExFRCwgRElBTU9ORF9PUEVOIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL2ZpZ3VyZXMuanMnXG5pbXBvcnQgeyB1c2VFbGFwc2VkVGltZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUVsYXBzZWRUaW1lLmpzJ1xuaW1wb3J0IHR5cGUgeyBLZXlib2FyZEV2ZW50IH0gZnJvbSAnLi4vLi4vaW5rL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB7IEJveCwgTGluaywgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB0eXBlIHsgUmVtb3RlQWdlbnRUYXNrU3RhdGUgfSBmcm9tICcuLi8uLi90YXNrcy9SZW1vdGVBZ2VudFRhc2svUmVtb3RlQWdlbnRUYXNrLmpzJ1xuaW1wb3J0IHsgZ2V0UmVtb3RlVGFza1Nlc3Npb25VcmwgfSBmcm9tICcuLi8uLi90YXNrcy9SZW1vdGVBZ2VudFRhc2svUmVtb3RlQWdlbnRUYXNrLmpzJ1xuaW1wb3J0IHtcbiAgQUdFTlRfVE9PTF9OQU1FLFxuICBMRUdBQ1lfQUdFTlRfVE9PTF9OQU1FLFxufSBmcm9tICcuLi8uLi90b29scy9BZ2VudFRvb2wvY29uc3RhbnRzLmpzJ1xuaW1wb3J0IHsgQVNLX1VTRVJfUVVFU1RJT05fVE9PTF9OQU1FIH0gZnJvbSAnLi4vLi4vdG9vbHMvQXNrVXNlclF1ZXN0aW9uVG9vbC9wcm9tcHQuanMnXG5pbXBvcnQgeyBFWElUX1BMQU5fTU9ERV9WMl9UT09MX05BTUUgfSBmcm9tICcuLi8uLi90b29scy9FeGl0UGxhbk1vZGVUb29sL2NvbnN0YW50cy5qcydcbmltcG9ydCB7IG9wZW5Ccm93c2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvYnJvd3Nlci5qcydcbmltcG9ydCB7IGVycm9yTWVzc2FnZSB9IGZyb20gJy4uLy4uL3V0aWxzL2Vycm9ycy5qcydcbmltcG9ydCB7IGZvcm1hdER1cmF0aW9uLCB0cnVuY2F0ZVRvV2lkdGggfSBmcm9tICcuLi8uLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQgeyB0b0ludGVybmFsTWVzc2FnZXMgfSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlcy9tYXBwZXJzLmpzJ1xuaW1wb3J0IHsgRU1QVFlfTE9PS1VQUywgbm9ybWFsaXplTWVzc2FnZXMgfSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB7IHBsdXJhbCB9IGZyb20gJy4uLy4uL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuaW1wb3J0IHsgdGVsZXBvcnRSZXN1bWVDb2RlU2Vzc2lvbiB9IGZyb20gJy4uLy4uL3V0aWxzL3RlbGVwb3J0LmpzJ1xuaW1wb3J0IHsgU2VsZWN0IH0gZnJvbSAnLi4vQ3VzdG9tU2VsZWN0L3NlbGVjdC5qcydcbmltcG9ydCB7IEJ5bGluZSB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vQnlsaW5lLmpzJ1xuaW1wb3J0IHsgRGlhbG9nIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9EaWFsb2cuanMnXG5pbXBvcnQgeyBLZXlib2FyZFNob3J0Y3V0SGludCB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vS2V5Ym9hcmRTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQgeyBNZXNzYWdlIH0gZnJvbSAnLi4vTWVzc2FnZS5qcydcbmltcG9ydCB7XG4gIGZvcm1hdFJldmlld1N0YWdlQ291bnRzLFxuICBSZW1vdGVTZXNzaW9uUHJvZ3Jlc3MsXG59IGZyb20gJy4vUmVtb3RlU2Vzc2lvblByb2dyZXNzLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBzZXNzaW9uOiBEZWVwSW1tdXRhYmxlPFJlbW90ZUFnZW50VGFza1N0YXRlPlxuICB0b29sVXNlQ29udGV4dDogVG9vbFVzZUNvbnRleHRcbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWRcbiAgb25CYWNrPzogKCkgPT4gdm9pZFxuICBvbktpbGw/OiAoKSA9PiB2b2lkXG59XG5cbi8vIENvbXBhY3Qgb25lLWxpbmUgc3VtbWFyeTogdG9vbCBuYW1lICsgZmlyc3QgbWVhbmluZ2Z1bCBzdHJpbmcgYXJnLlxuLy8gTGlnaHRlciB0aGFuIHRvb2wucmVuZGVyVG9vbFVzZU1lc3NhZ2UgKG5vIHJlZ2lzdHJ5IGxvb2t1cCAvIHNjaGVtYSBwYXJzZSkuXG4vLyBDb2xsYXBzZXMgd2hpdGVzcGFjZSBzbyBtdWx0aS1saW5lIGlucHV0cyAoZS5nLiBCYXNoIGNvbW1hbmQgdGV4dClcbi8vIHJlbmRlciBvbiBvbmUgbGluZS5cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRUb29sVXNlU3VtbWFyeShuYW1lOiBzdHJpbmcsIGlucHV0OiB1bmtub3duKTogc3RyaW5nIHtcbiAgLy8gcGxhbl9yZWFkeSBwaGFzZSBpcyBvbmx5IHJlYWNoZWQgdmlhIEV4aXRQbGFuTW9kZSB0b29sXG4gIGlmIChuYW1lID09PSBFWElUX1BMQU5fTU9ERV9WMl9UT09MX05BTUUpIHtcbiAgICByZXR1cm4gJ1JldmlldyB0aGUgcGxhbiBpbiBDbGF1ZGUgQ29kZSBvbiB0aGUgd2ViJ1xuICB9XG4gIGlmICghaW5wdXQgfHwgdHlwZW9mIGlucHV0ICE9PSAnb2JqZWN0JykgcmV0dXJuIG5hbWVcbiAgLy8gQXNrVXNlclF1ZXN0aW9uOiBzaG93IHRoZSBxdWVzdGlvbiB0ZXh0IGFzIGEgQ1RBLCBub3QgdGhlIHRvb2wgbmFtZS5cbiAgLy8gSW5wdXQgc2hhcGUgaXMge3F1ZXN0aW9uczogW3txdWVzdGlvbiwgaGVhZGVyLCBvcHRpb25zfV19LlxuICBpZiAobmFtZSA9PT0gQVNLX1VTRVJfUVVFU1RJT05fVE9PTF9OQU1FICYmICdxdWVzdGlvbnMnIGluIGlucHV0KSB7XG4gICAgY29uc3QgcXMgPSBpbnB1dC5xdWVzdGlvbnNcbiAgICBpZiAoQXJyYXkuaXNBcnJheShxcykgJiYgcXNbMF0gJiYgdHlwZW9mIHFzWzBdID09PSAnb2JqZWN0Jykge1xuICAgICAgLy8gUHJlZmVyIHF1ZXN0aW9uIChmdWxsIHRleHQpIG92ZXIgaGVhZGVyIChtYXgtMTItY2hhciB0YWcpLiBoZWFkZXJcbiAgICAgIC8vIGlzIGEgcmVxdWlyZWQgc2NoZW1hIGZpZWxkIHNvIGNoZWNraW5nIGl0IGZpcnN0IHdvdWxkIG1ha2UgdGhlXG4gICAgICAvLyBxdWVzdGlvbiBmYWxsYmFjayBkZWFkIGNvZGUuXG4gICAgICBjb25zdCBxID1cbiAgICAgICAgJ3F1ZXN0aW9uJyBpbiBxc1swXSAmJlxuICAgICAgICB0eXBlb2YgcXNbMF0ucXVlc3Rpb24gPT09ICdzdHJpbmcnICYmXG4gICAgICAgIHFzWzBdLnF1ZXN0aW9uXG4gICAgICAgICAgPyBxc1swXS5xdWVzdGlvblxuICAgICAgICAgIDogJ2hlYWRlcicgaW4gcXNbMF0gJiYgdHlwZW9mIHFzWzBdLmhlYWRlciA9PT0gJ3N0cmluZydcbiAgICAgICAgICAgID8gcXNbMF0uaGVhZGVyXG4gICAgICAgICAgICA6IG51bGxcbiAgICAgIGlmIChxKSB7XG4gICAgICAgIGNvbnN0IG9uZUxpbmUgPSBxLnJlcGxhY2UoL1xccysvZywgJyAnKS50cmltKClcbiAgICAgICAgcmV0dXJuIGBBbnN3ZXIgaW4gYnJvd3NlcjogJHt0cnVuY2F0ZVRvV2lkdGgob25lTGluZSwgNTApfWBcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgZm9yIChjb25zdCB2IG9mIE9iamVjdC52YWx1ZXMoaW5wdXQpKSB7XG4gICAgaWYgKHR5cGVvZiB2ID09PSAnc3RyaW5nJyAmJiB2LnRyaW0oKSkge1xuICAgICAgY29uc3Qgb25lTGluZSA9IHYucmVwbGFjZSgvXFxzKy9nLCAnICcpLnRyaW0oKVxuICAgICAgcmV0dXJuIGAke25hbWV9ICR7dHJ1bmNhdGVUb1dpZHRoKG9uZUxpbmUsIDYwKX1gXG4gICAgfVxuICB9XG4gIHJldHVybiBuYW1lXG59XG5cbmNvbnN0IFBIQVNFX0xBQkVMID0ge1xuICBuZWVkc19pbnB1dDogJ2lucHV0IHJlcXVpcmVkJyxcbiAgcGxhbl9yZWFkeTogJ3JlYWR5Jyxcbn0gYXMgY29uc3RcblxuY29uc3QgQUdFTlRfVkVSQiA9IHtcbiAgbmVlZHNfaW5wdXQ6ICd3YWl0aW5nJyxcbiAgcGxhbl9yZWFkeTogJ2RvbmUnLFxufSBhcyBjb25zdFxuXG5mdW5jdGlvbiBVbHRyYXBsYW5TZXNzaW9uRGV0YWlsKHtcbiAgc2Vzc2lvbixcbiAgb25Eb25lLFxuICBvbkJhY2ssXG4gIG9uS2lsbCxcbn06IE9taXQ8UHJvcHMsICd0b29sVXNlQ29udGV4dCc+KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgcnVubmluZyA9IHNlc3Npb24uc3RhdHVzID09PSAncnVubmluZycgfHwgc2Vzc2lvbi5zdGF0dXMgPT09ICdwZW5kaW5nJ1xuICBjb25zdCBwaGFzZSA9IHNlc3Npb24udWx0cmFwbGFuUGhhc2VcbiAgY29uc3Qgc3RhdHVzVGV4dCA9IHJ1bm5pbmdcbiAgICA/IHBoYXNlXG4gICAgICA/IFBIQVNFX0xBQkVMW3BoYXNlXVxuICAgICAgOiAncnVubmluZydcbiAgICA6IHNlc3Npb24uc3RhdHVzXG4gIGNvbnN0IGVsYXBzZWRUaW1lID0gdXNlRWxhcHNlZFRpbWUoXG4gICAgc2Vzc2lvbi5zdGFydFRpbWUsXG4gICAgcnVubmluZyxcbiAgICAxMDAwLFxuICAgIDAsXG4gICAgc2Vzc2lvbi5lbmRUaW1lLFxuICApXG5cbiAgLy8gQ291bnRzIGFyZSBldmVudHVhbGx5IGNvcnJlY3QgKGxhZyDiiaQgcG9sbCBpbnRlcnZhbCkuIGFnZW50c1dvcmtpbmcgc3RhcnRzXG4gIC8vIGF0IDEgKHRoZSBtYWluIHNlc3Npb24gYWdlbnQpIGFuZCBpbmNyZW1lbnRzIHBlciBzdWJhZ2VudCBzcGF3bi4gdG9vbENhbGxzXG4gIC8vIGlzIG1haW4tc2Vzc2lvbiBvbmx5IOKAlCBzdWJhZ2VudCBjYWxscyBtYXkgbm90IHN1cmZhY2UgaW4gdGhpcyBzdHJlYW0uXG4gIGNvbnN0IHsgYWdlbnRzV29ya2luZywgdG9vbENhbGxzLCBsYXN0VG9vbENhbGwgfSA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGxldCBzcGF3bnMgPSAwXG4gICAgbGV0IGNhbGxzID0gMFxuICAgIGxldCBsYXN0QmxvY2s6IHsgbmFtZTogc3RyaW5nOyBpbnB1dDogdW5rbm93biB9IHwgbnVsbCA9IG51bGxcbiAgICBmb3IgKGNvbnN0IG1zZyBvZiBzZXNzaW9uLmxvZykge1xuICAgICAgaWYgKG1zZy50eXBlICE9PSAnYXNzaXN0YW50JykgY29udGludWVcbiAgICAgIGZvciAoY29uc3QgYmxvY2sgb2YgbXNnLm1lc3NhZ2UuY29udGVudCkge1xuICAgICAgICBpZiAoYmxvY2sudHlwZSAhPT0gJ3Rvb2xfdXNlJykgY29udGludWVcbiAgICAgICAgY2FsbHMrK1xuICAgICAgICBsYXN0QmxvY2sgPSBibG9ja1xuICAgICAgICBpZiAoXG4gICAgICAgICAgYmxvY2submFtZSA9PT0gQUdFTlRfVE9PTF9OQU1FIHx8XG4gICAgICAgICAgYmxvY2submFtZSA9PT0gTEVHQUNZX0FHRU5UX1RPT0xfTkFNRVxuICAgICAgICApIHtcbiAgICAgICAgICBzcGF3bnMrK1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBhZ2VudHNXb3JraW5nOiAxICsgc3Bhd25zLFxuICAgICAgdG9vbENhbGxzOiBjYWxscyxcbiAgICAgIGxhc3RUb29sQ2FsbDogbGFzdEJsb2NrXG4gICAgICAgID8gZm9ybWF0VG9vbFVzZVN1bW1hcnkobGFzdEJsb2NrLm5hbWUsIGxhc3RCbG9jay5pbnB1dClcbiAgICAgICAgOiBudWxsLFxuICAgIH1cbiAgfSwgW3Nlc3Npb24ubG9nXSlcblxuICBjb25zdCBzZXNzaW9uVXJsID0gZ2V0UmVtb3RlVGFza1Nlc3Npb25Vcmwoc2Vzc2lvbi5zZXNzaW9uSWQpXG4gIGNvbnN0IGdvQmFja09yQ2xvc2UgPVxuICAgIG9uQmFjayA/P1xuICAgICgoKSA9PiBvbkRvbmUoJ1JlbW90ZSBzZXNzaW9uIGRldGFpbHMgZGlzbWlzc2VkJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KSlcbiAgY29uc3QgW2NvbmZpcm1pbmdTdG9wLCBzZXRDb25maXJtaW5nU3RvcF0gPSB1c2VTdGF0ZShmYWxzZSlcblxuICBpZiAoY29uZmlybWluZ1N0b3ApIHtcbiAgICByZXR1cm4gKFxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT1cIlN0b3AgdWx0cmFwbGFuP1wiXG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBzZXRDb25maXJtaW5nU3RvcChmYWxzZSl9XG4gICAgICAgIGNvbG9yPVwiYmFja2dyb3VuZFwiXG4gICAgICA+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICBUaGlzIHdpbGwgdGVybWluYXRlIHRoZSBDbGF1ZGUgQ29kZSBvbiB0aGUgd2ViIHNlc3Npb24uXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxTZWxlY3RcbiAgICAgICAgICAgIG9wdGlvbnM9e1tcbiAgICAgICAgICAgICAgeyBsYWJlbDogJ1Rlcm1pbmF0ZSBzZXNzaW9uJywgdmFsdWU6ICdzdG9wJyBhcyBjb25zdCB9LFxuICAgICAgICAgICAgICB7IGxhYmVsOiAnQmFjaycsIHZhbHVlOiAnYmFjaycgYXMgY29uc3QgfSxcbiAgICAgICAgICAgIF19XG4gICAgICAgICAgICBvbkNoYW5nZT17diA9PiB7XG4gICAgICAgICAgICAgIGlmICh2ID09PSAnc3RvcCcpIHtcbiAgICAgICAgICAgICAgICBvbktpbGw/LigpXG4gICAgICAgICAgICAgICAgZ29CYWNrT3JDbG9zZSgpXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc2V0Q29uZmlybWluZ1N0b3AoZmFsc2UpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH19XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9Cb3g+XG4gICAgICA8L0RpYWxvZz5cbiAgICApXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxEaWFsb2dcbiAgICAgIHRpdGxlPXtcbiAgICAgICAgPFRleHQ+XG4gICAgICAgICAgPFRleHQgY29sb3I9XCJiYWNrZ3JvdW5kXCI+XG4gICAgICAgICAgICB7cGhhc2UgPT09ICdwbGFuX3JlYWR5JyA/IERJQU1PTkRfRklMTEVEIDogRElBTU9ORF9PUEVOfXsnICd9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGJvbGQ+dWx0cmFwbGFuPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgeycgwrcgJ31cbiAgICAgICAgICAgIHtlbGFwc2VkVGltZX1cbiAgICAgICAgICAgIHsnIMK3ICd9XG4gICAgICAgICAgICB7c3RhdHVzVGV4dH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIH1cbiAgICAgIG9uQ2FuY2VsPXtnb0JhY2tPckNsb3NlfVxuICAgICAgY29sb3I9XCJiYWNrZ3JvdW5kXCJcbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICB7cGhhc2UgPT09ICdwbGFuX3JlYWR5JyAmJiAoXG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj57ZmlndXJlcy50aWNrfSA8L1RleHQ+XG4gICAgICAgICAgKX1cbiAgICAgICAgICB7YWdlbnRzV29ya2luZ30ge3BsdXJhbChhZ2VudHNXb3JraW5nLCAnYWdlbnQnKX17JyAnfVxuICAgICAgICAgIHtwaGFzZSA/IEFHRU5UX1ZFUkJbcGhhc2VdIDogJ3dvcmtpbmcnfSDCtyB7dG9vbENhbGxzfSB0b29seycgJ31cbiAgICAgICAgICB7cGx1cmFsKHRvb2xDYWxscywgJ2NhbGwnKX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICB7bGFzdFRvb2xDYWxsICYmIDxUZXh0IGRpbUNvbG9yPntsYXN0VG9vbENhbGx9PC9UZXh0Pn1cbiAgICAgICAgPExpbmsgdXJsPXtzZXNzaW9uVXJsfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj57c2Vzc2lvblVybH08L1RleHQ+XG4gICAgICAgIDwvTGluaz5cbiAgICAgICAgPFNlbGVjdFxuICAgICAgICAgIG9wdGlvbnM9e1tcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6ICdSZXZpZXcgaW4gQ2xhdWRlIENvZGUgb24gdGhlIHdlYicsXG4gICAgICAgICAgICAgIHZhbHVlOiAnb3BlbicgYXMgY29uc3QsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgLi4uKG9uS2lsbCAmJiBydW5uaW5nXG4gICAgICAgICAgICAgID8gW3sgbGFiZWw6ICdTdG9wIHVsdHJhcGxhbicsIHZhbHVlOiAnc3RvcCcgYXMgY29uc3QgfV1cbiAgICAgICAgICAgICAgOiBbXSksXG4gICAgICAgICAgICB7IGxhYmVsOiAnQmFjaycsIHZhbHVlOiAnYmFjaycgYXMgY29uc3QgfSxcbiAgICAgICAgICBdfVxuICAgICAgICAgIG9uQ2hhbmdlPXt2ID0+IHtcbiAgICAgICAgICAgIHN3aXRjaCAodikge1xuICAgICAgICAgICAgICBjYXNlICdvcGVuJzpcbiAgICAgICAgICAgICAgICB2b2lkIG9wZW5Ccm93c2VyKHNlc3Npb25VcmwpXG4gICAgICAgICAgICAgICAgLy8gQ2xvc2UgdGhlIGRpYWxvZyBzbyB0aGUgdXNlciBsYW5kcyBiYWNrIGF0IHRoZSBwcm9tcHQgd2l0aFxuICAgICAgICAgICAgICAgIC8vIGFueSBoYWxmLXdyaXR0ZW4gaW5wdXQgaW50YWN0IChpbnB1dFZhbHVlIHBlcnNpc3RzIGFjcm9zc1xuICAgICAgICAgICAgICAgIC8vIHRoZSBzaG93QmFzaGVzRGlhbG9nIHRvZ2dsZSkuXG4gICAgICAgICAgICAgICAgb25Eb25lKClcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgICAgY2FzZSAnc3RvcCc6XG4gICAgICAgICAgICAgICAgc2V0Q29uZmlybWluZ1N0b3AodHJ1ZSlcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgICAgY2FzZSAnYmFjayc6XG4gICAgICAgICAgICAgICAgZ29CYWNrT3JDbG9zZSgpXG4gICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfX1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIDwvRGlhbG9nPlxuICApXG59XG5cbmNvbnN0IFNUQUdFUyA9IFsnZmluZGluZycsICd2ZXJpZnlpbmcnLCAnc3ludGhlc2l6aW5nJ10gYXMgY29uc3RcbmNvbnN0IFNUQUdFX0xBQkVMUzogUmVjb3JkPCh0eXBlb2YgU1RBR0VTKVtudW1iZXJdLCBzdHJpbmc+ID0ge1xuICBmaW5kaW5nOiAnRmluZCcsXG4gIHZlcmlmeWluZzogJ1ZlcmlmeScsXG4gIHN5bnRoZXNpemluZzogJ0RlZHVwZScsXG59XG5cbi8vIFNldHVwIOKGkiBGaW5kIOKGkiBWZXJpZnkg4oaSIERlZHVwZSBwaXBlbGluZS4gQ3VycmVudCBzdGFnZSBpbiBjbG91ZCB0ZWFsLFxuLy8gcmVzdCBkaW0uIFdoZW4gY29tcGxldGVkLCBhbGwgc3RhZ2VzIGRpbSB3aXRoIGEgdHJhaWxpbmcgZ3JlZW4g4pyTLiBUaGVcbi8vIFwiU2V0dXBcIiBsYWJlbCBzaG93cyBiZWZvcmUgdGhlIG9yY2hlc3RyYXRvciB3cml0ZXMgaXRzIGZpcnN0IHByb2dyZXNzXG4vLyBzbmFwc2hvdCAoY29udGFpbmVyIGJvb3QgKyByZXBvIGNsb25lKSwgc28gdGhlIDAtZm91bmQgZGlzcGxheSBkb2Vzbid0XG4vLyBsb29rIGxpa2UgYSBodW5nIGZpbmRlci5cbmZ1bmN0aW9uIFN0YWdlUGlwZWxpbmUoe1xuICBzdGFnZSxcbiAgY29tcGxldGVkLFxuICBoYXNQcm9ncmVzcyxcbn06IHtcbiAgc3RhZ2U6ICdmaW5kaW5nJyB8ICd2ZXJpZnlpbmcnIHwgJ3N5bnRoZXNpemluZycgfCB1bmRlZmluZWRcbiAgY29tcGxldGVkOiBib29sZWFuXG4gIGhhc1Byb2dyZXNzOiBib29sZWFuXG59KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgY3VycmVudElkeCA9IHN0YWdlID8gU1RBR0VTLmluZGV4T2Yoc3RhZ2UpIDogLTFcbiAgY29uc3QgaW5TZXR1cCA9ICFjb21wbGV0ZWQgJiYgIWhhc1Byb2dyZXNzXG4gIHJldHVybiAoXG4gICAgPFRleHQ+XG4gICAgICB7aW5TZXR1cCA/IChcbiAgICAgICAgPFRleHQgY29sb3I9XCJiYWNrZ3JvdW5kXCI+U2V0dXA8L1RleHQ+XG4gICAgICApIDogKFxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5TZXR1cDwvVGV4dD5cbiAgICAgICl9XG4gICAgICA8VGV4dCBkaW1Db2xvcj4g4oaSIDwvVGV4dD5cbiAgICAgIHtTVEFHRVMubWFwKChzLCBpKSA9PiB7XG4gICAgICAgIGNvbnN0IGlzQ3VycmVudCA9ICFjb21wbGV0ZWQgJiYgIWluU2V0dXAgJiYgaSA9PT0gY3VycmVudElkeFxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIDxSZWFjdC5GcmFnbWVudCBrZXk9e3N9PlxuICAgICAgICAgICAge2kgPiAwICYmIDxUZXh0IGRpbUNvbG9yPiDihpIgPC9UZXh0Pn1cbiAgICAgICAgICAgIHtpc0N1cnJlbnQgPyAoXG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiPntTVEFHRV9MQUJFTFNbc119PC9UZXh0PlxuICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+e1NUQUdFX0xBQkVMU1tzXX08L1RleHQ+XG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvUmVhY3QuRnJhZ21lbnQ+XG4gICAgICAgIClcbiAgICAgIH0pfVxuICAgICAge2NvbXBsZXRlZCAmJiA8VGV4dCBjb2xvcj1cInN1Y2Nlc3NcIj4g4pyTPC9UZXh0Pn1cbiAgICA8L1RleHQ+XG4gIClcbn1cblxuLy8gU3RhZ2UtYXBwcm9wcmlhdGUgY291bnRzIGxpbmUuIFJ1bm5pbmctc3RhdGUgZm9ybWF0dGluZyBkZWxlZ2F0ZXMgdG9cbi8vIGZvcm1hdFJldmlld1N0YWdlQ291bnRzIChzaGFyZWQgd2l0aCB0aGUgcGlsbCkgc28gdGhlIHR3byB2aWV3cyBjYW4ndFxuLy8gZHJpZnQ7IGNvbXBsZXRlZCBzdGF0ZSBpcyBkaWFsb2ctc3BlY2lmaWMgKGZpbmRpbmdzIHN1bW1hcnkpLlxuZnVuY3Rpb24gcmV2aWV3Q291bnRzTGluZShcbiAgc2Vzc2lvbjogRGVlcEltbXV0YWJsZTxSZW1vdGVBZ2VudFRhc2tTdGF0ZT4sXG4pOiBzdHJpbmcge1xuICBjb25zdCBwID0gc2Vzc2lvbi5yZXZpZXdQcm9ncmVzc1xuICAvLyBObyBwcm9ncmVzcyBkYXRhIOKAlCB0aGUgb3JjaGVzdHJhdG9yIG5ldmVyIHdyb3RlIGEgc25hcHNob3QuIERvbid0XG4gIC8vIGNsYWltIFwiMCBmaW5kaW5nc1wiIHdoZW4gY29tcGxldGVkOyB3ZSBqdXN0IGRvbid0IGtub3cuXG4gIGlmICghcCkgcmV0dXJuIHNlc3Npb24uc3RhdHVzID09PSAnY29tcGxldGVkJyA/ICdkb25lJyA6ICdzZXR0aW5nIHVwJ1xuICBjb25zdCB2ZXJpZmllZCA9IHAuYnVnc1ZlcmlmaWVkXG4gIGNvbnN0IHJlZnV0ZWQgPSBwLmJ1Z3NSZWZ1dGVkID8/IDBcbiAgaWYgKHNlc3Npb24uc3RhdHVzID09PSAnY29tcGxldGVkJykge1xuICAgIGNvbnN0IHBhcnRzID0gW2Ake3ZlcmlmaWVkfSAke3BsdXJhbCh2ZXJpZmllZCwgJ2ZpbmRpbmcnKX1gXVxuICAgIGlmIChyZWZ1dGVkID4gMCkgcGFydHMucHVzaChgJHtyZWZ1dGVkfSByZWZ1dGVkYClcbiAgICByZXR1cm4gcGFydHMuam9pbignIMK3ICcpXG4gIH1cbiAgcmV0dXJuIGZvcm1hdFJldmlld1N0YWdlQ291bnRzKHAuc3RhZ2UsIHAuYnVnc0ZvdW5kLCB2ZXJpZmllZCwgcmVmdXRlZClcbn1cblxudHlwZSBNZW51QWN0aW9uID0gJ29wZW4nIHwgJ3N0b3AnIHwgJ2JhY2snIHwgJ2Rpc21pc3MnXG5cbmZ1bmN0aW9uIFJldmlld1Nlc3Npb25EZXRhaWwoe1xuICBzZXNzaW9uLFxuICBvbkRvbmUsXG4gIG9uQmFjayxcbiAgb25LaWxsLFxufTogT21pdDxQcm9wcywgJ3Rvb2xVc2VDb250ZXh0Jz4pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBjb21wbGV0ZWQgPSBzZXNzaW9uLnN0YXR1cyA9PT0gJ2NvbXBsZXRlZCdcbiAgY29uc3QgcnVubmluZyA9IHNlc3Npb24uc3RhdHVzID09PSAncnVubmluZycgfHwgc2Vzc2lvbi5zdGF0dXMgPT09ICdwZW5kaW5nJ1xuICBjb25zdCBbY29uZmlybWluZ1N0b3AsIHNldENvbmZpcm1pbmdTdG9wXSA9IHVzZVN0YXRlKGZhbHNlKVxuXG4gIC8vIHVzZUVsYXBzZWRUaW1lIGRyaXZlcyB0aGUgMUh6IHRpY2sgc28gdGhlIHRpbWVyIGFkdmFuY2VzIHdoaWxlIHRoZVxuICAvLyBkaWFsb2cgaXMgb3BlbiDigJQgdGhlIHByZXZpb3VzIGlubGluZSBlbGFwc2VkLXRpbWUgY2FsY3VsYXRpb24gb25seVxuICAvLyByZS1yZW5kZXJlZCBvbiBzZXNzaW9uIHN0YXRlIGNoYW5nZXMgKHBvbGwgaW50ZXJ2YWwpLCB3aGljaCBsb29rZWRcbiAgLy8gbGlrZSB0aGUgY2xvY2sgd2FzIHN0dWNrLlxuICBjb25zdCBlbGFwc2VkVGltZSA9IHVzZUVsYXBzZWRUaW1lKFxuICAgIHNlc3Npb24uc3RhcnRUaW1lLFxuICAgIHJ1bm5pbmcsXG4gICAgMTAwMCxcbiAgICAwLFxuICAgIHNlc3Npb24uZW5kVGltZSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZUNsb3NlID0gKCkgPT5cbiAgICBvbkRvbmUoJ1JlbW90ZSBzZXNzaW9uIGRldGFpbHMgZGlzbWlzc2VkJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICBjb25zdCBnb0JhY2tPckNsb3NlID0gb25CYWNrID8/IGhhbmRsZUNsb3NlXG5cbiAgY29uc3Qgc2Vzc2lvblVybCA9IGdldFJlbW90ZVRhc2tTZXNzaW9uVXJsKHNlc3Npb24uc2Vzc2lvbklkKVxuICBjb25zdCBzdGF0dXNMYWJlbCA9IGNvbXBsZXRlZCA/ICdyZWFkeScgOiBydW5uaW5nID8gJ3J1bm5pbmcnIDogc2Vzc2lvbi5zdGF0dXNcblxuICBpZiAoY29uZmlybWluZ1N0b3ApIHtcbiAgICByZXR1cm4gKFxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT1cIlN0b3AgdWx0cmFyZXZpZXc/XCJcbiAgICAgICAgb25DYW5jZWw9eygpID0+IHNldENvbmZpcm1pbmdTdG9wKGZhbHNlKX1cbiAgICAgICAgY29sb3I9XCJiYWNrZ3JvdW5kXCJcbiAgICAgID5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgIFRoaXMgYXJjaGl2ZXMgdGhlIHJlbW90ZSBzZXNzaW9uIGFuZCBzdG9wcyBsb2NhbCB0cmFja2luZy4gVGhlXG4gICAgICAgICAgICByZXZpZXcgd2lsbCBub3QgY29tcGxldGUgYW5kIGFueSBmaW5kaW5ncyBzbyBmYXIgYXJlIGRpc2NhcmRlZC5cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgb3B0aW9ucz17W1xuICAgICAgICAgICAgICB7IGxhYmVsOiAnU3RvcCB1bHRyYXJldmlldycsIHZhbHVlOiAnc3RvcCcgYXMgY29uc3QgfSxcbiAgICAgICAgICAgICAgeyBsYWJlbDogJ0JhY2snLCB2YWx1ZTogJ2JhY2snIGFzIGNvbnN0IH0sXG4gICAgICAgICAgICBdfVxuICAgICAgICAgICAgb25DaGFuZ2U9e3YgPT4ge1xuICAgICAgICAgICAgICBpZiAodiA9PT0gJ3N0b3AnKSB7XG4gICAgICAgICAgICAgICAgb25LaWxsPy4oKVxuICAgICAgICAgICAgICAgIGdvQmFja09yQ2xvc2UoKVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHNldENvbmZpcm1pbmdTdG9wKGZhbHNlKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9fVxuICAgICAgICAgIC8+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9EaWFsb2c+XG4gICAgKVxuICB9XG5cbiAgY29uc3Qgb3B0aW9uczogeyBsYWJlbDogc3RyaW5nOyB2YWx1ZTogTWVudUFjdGlvbiB9W10gPSBjb21wbGV0ZWRcbiAgICA/IFtcbiAgICAgICAgeyBsYWJlbDogJ09wZW4gaW4gQ2xhdWRlIENvZGUgb24gdGhlIHdlYicsIHZhbHVlOiAnb3BlbicgfSxcbiAgICAgICAgeyBsYWJlbDogJ0Rpc21pc3MnLCB2YWx1ZTogJ2Rpc21pc3MnIH0sXG4gICAgICBdXG4gICAgOiBbXG4gICAgICAgIHsgbGFiZWw6ICdPcGVuIGluIENsYXVkZSBDb2RlIG9uIHRoZSB3ZWInLCB2YWx1ZTogJ29wZW4nIH0sXG4gICAgICAgIC4uLihvbktpbGwgJiYgcnVubmluZ1xuICAgICAgICAgID8gW3sgbGFiZWw6ICdTdG9wIHVsdHJhcmV2aWV3JywgdmFsdWU6ICdzdG9wJyBhcyBjb25zdCB9XVxuICAgICAgICAgIDogW10pLFxuICAgICAgICB7IGxhYmVsOiAnQmFjaycsIHZhbHVlOiAnYmFjaycgfSxcbiAgICAgIF1cblxuICBjb25zdCBoYW5kbGVTZWxlY3QgPSAoYWN0aW9uOiBNZW51QWN0aW9uKSA9PiB7XG4gICAgc3dpdGNoIChhY3Rpb24pIHtcbiAgICAgIGNhc2UgJ29wZW4nOlxuICAgICAgICB2b2lkIG9wZW5Ccm93c2VyKHNlc3Npb25VcmwpXG4gICAgICAgIG9uRG9uZSgpXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdzdG9wJzpcbiAgICAgICAgc2V0Q29uZmlybWluZ1N0b3AodHJ1ZSlcbiAgICAgICAgYnJlYWtcbiAgICAgIGNhc2UgJ2JhY2snOlxuICAgICAgICBnb0JhY2tPckNsb3NlKClcbiAgICAgICAgYnJlYWtcbiAgICAgIGNhc2UgJ2Rpc21pc3MnOlxuICAgICAgICBoYW5kbGVDbG9zZSgpXG4gICAgICAgIGJyZWFrXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8RGlhbG9nXG4gICAgICB0aXRsZT17XG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiPlxuICAgICAgICAgICAge2NvbXBsZXRlZCA/IERJQU1PTkRfRklMTEVEIDogRElBTU9ORF9PUEVOfXsnICd9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGJvbGQ+dWx0cmFyZXZpZXc8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7JyDCtyAnfVxuICAgICAgICAgICAge2VsYXBzZWRUaW1lfVxuICAgICAgICAgICAgeycgwrcgJ31cbiAgICAgICAgICAgIHtzdGF0dXNMYWJlbH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIH1cbiAgICAgIG9uQ2FuY2VsPXtnb0JhY2tPckNsb3NlfVxuICAgICAgY29sb3I9XCJiYWNrZ3JvdW5kXCJcbiAgICAgIGlucHV0R3VpZGU9e2V4aXRTdGF0ZSA9PlxuICAgICAgICBleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICA8VGV4dD5QcmVzcyB7ZXhpdFN0YXRlLmtleU5hbWV9IGFnYWluIHRvIGV4aXQ8L1RleHQ+XG4gICAgICAgICkgOiAoXG4gICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVudGVyXCIgYWN0aW9uPVwic2VsZWN0XCIgLz5cbiAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVzY1wiIGFjdGlvbj1cImdvIGJhY2tcIiAvPlxuICAgICAgICAgIDwvQnlsaW5lPlxuICAgICAgICApXG4gICAgICB9XG4gICAgPlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgZ2FwPXsxfT5cbiAgICAgICAgPFN0YWdlUGlwZWxpbmVcbiAgICAgICAgICBzdGFnZT17c2Vzc2lvbi5yZXZpZXdQcm9ncmVzcz8uc3RhZ2V9XG4gICAgICAgICAgY29tcGxldGVkPXtjb21wbGV0ZWR9XG4gICAgICAgICAgaGFzUHJvZ3Jlc3M9eyEhc2Vzc2lvbi5yZXZpZXdQcm9ncmVzc31cbiAgICAgICAgLz5cblxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dD57cmV2aWV3Q291bnRzTGluZShzZXNzaW9uKX08L1RleHQ+XG4gICAgICAgICAgPExpbmsgdXJsPXtzZXNzaW9uVXJsfT5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntzZXNzaW9uVXJsfTwvVGV4dD5cbiAgICAgICAgICA8L0xpbms+XG4gICAgICAgIDwvQm94PlxuXG4gICAgICAgIDxTZWxlY3Qgb3B0aW9ucz17b3B0aW9uc30gb25DaGFuZ2U9e2hhbmRsZVNlbGVjdH0gLz5cbiAgICAgIDwvQm94PlxuICAgIDwvRGlhbG9nPlxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBSZW1vdGVTZXNzaW9uRGV0YWlsRGlhbG9nKHtcbiAgc2Vzc2lvbixcbiAgdG9vbFVzZUNvbnRleHQsXG4gIG9uRG9uZSxcbiAgb25CYWNrLFxuICBvbktpbGwsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtpc1RlbGVwb3J0aW5nLCBzZXRJc1RlbGVwb3J0aW5nXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbdGVsZXBvcnRFcnJvciwgc2V0VGVsZXBvcnRFcnJvcl0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuXG4gIC8vIEdldCBsYXN0IGZldyBtZXNzYWdlcyBmcm9tIHJlbW90ZSBzZXNzaW9uIGZvciBkaXNwbGF5LlxuICAvLyBTY2FuIGFsbCBtZXNzYWdlcyAobm90IGp1c3QgdGhlIGxhc3QgMyByYXcgZW50cmllcykgYmVjYXVzZSB0aGUgdGFpbCBvZlxuICAvLyB0aGUgbG9nIGlzIG9mdGVuIHRoaW5raW5nLW9ubHkgYmxvY2tzIHRoYXQgbm9ybWFsaXNlIHRvICdwcm9ncmVzcycgdHlwZS5cbiAgLy8gUGxhY2VkIGJlZm9yZSB0aGUgZWFybHkgcmV0dXJucyBzbyBob29rIGNhbGwgb3JkZXIgaXMgc3RhYmxlIChSdWxlcyBvZiBIb29rcykuXG4gIC8vIFVsdHJhcGxhbi9yZXZpZXcgc2Vzc2lvbnMgbmV2ZXIgcmVhZCB0aGlzIOKAlCBza2lwIHRoZSBub3JtYWxpemUgd29yayBmb3IgdGhlbS5cbiAgY29uc3QgbGFzdE1lc3NhZ2VzID0gdXNlTWVtbygoKSA9PiB7XG4gICAgaWYgKHNlc3Npb24uaXNVbHRyYXBsYW4gfHwgc2Vzc2lvbi5pc1JlbW90ZVJldmlldykgcmV0dXJuIFtdXG4gICAgcmV0dXJuIG5vcm1hbGl6ZU1lc3NhZ2VzKHRvSW50ZXJuYWxNZXNzYWdlcyhzZXNzaW9uLmxvZyBhcyBTREtNZXNzYWdlW10pKVxuICAgICAgLmZpbHRlcihfID0+IF8udHlwZSAhPT0gJ3Byb2dyZXNzJylcbiAgICAgIC5zbGljZSgtMylcbiAgfSwgW3Nlc3Npb25dKVxuXG4gIGlmIChzZXNzaW9uLmlzVWx0cmFwbGFuKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxVbHRyYXBsYW5TZXNzaW9uRGV0YWlsXG4gICAgICAgIHNlc3Npb249e3Nlc3Npb259XG4gICAgICAgIG9uRG9uZT17b25Eb25lfVxuICAgICAgICBvbkJhY2s9e29uQmFja31cbiAgICAgICAgb25LaWxsPXtvbktpbGx9XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIC8vIFJldmlldyBzZXNzaW9ucyBnZXQgdGhlIHN0YWdlLXBpcGVsaW5lIHZpZXc7IGV2ZXJ5dGhpbmcgZWxzZSBrZWVwcyB0aGVcbiAgLy8gZ2VuZXJpYyBsYWJlbC92YWx1ZSArIHJlY2VudC1tZXNzYWdlcyBkaWFsb2cgYmVsb3cuXG4gIGlmIChzZXNzaW9uLmlzUmVtb3RlUmV2aWV3KSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxSZXZpZXdTZXNzaW9uRGV0YWlsXG4gICAgICAgIHNlc3Npb249e3Nlc3Npb259XG4gICAgICAgIG9uRG9uZT17b25Eb25lfVxuICAgICAgICBvbkJhY2s9e29uQmFja31cbiAgICAgICAgb25LaWxsPXtvbktpbGx9XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IGhhbmRsZUNsb3NlID0gKCkgPT5cbiAgICBvbkRvbmUoJ1JlbW90ZSBzZXNzaW9uIGRldGFpbHMgZGlzbWlzc2VkJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuXG4gIC8vIENvbXBvbmVudC1zcGVjaWZpYyBzaG9ydGN1dHMgc2hvd24gaW4gVUkgaGludHMgKHQ9dGVsZXBvcnQsIHNwYWNlPWRpc21pc3MsXG4gIC8vIGxlZnQ9YmFjaykuIFRoZXNlIGFyZSBzdGF0ZS1kZXBlbmRlbnQgYWN0aW9ucywgbm90IHN0YW5kYXJkIGRpYWxvZyBrZXliaW5kaW5ncy5cbiAgY29uc3QgaGFuZGxlS2V5RG93biA9IChlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgaWYgKGUua2V5ID09PSAnICcpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25Eb25lKCdSZW1vdGUgc2Vzc2lvbiBkZXRhaWxzIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgICB9IGVsc2UgaWYgKGUua2V5ID09PSAnbGVmdCcgJiYgb25CYWNrKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIG9uQmFjaygpXG4gICAgfSBlbHNlIGlmIChlLmtleSA9PT0gJ3QnICYmICFpc1RlbGVwb3J0aW5nKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIHZvaWQgaGFuZGxlVGVsZXBvcnQoKVxuICAgIH0gZWxzZSBpZiAoZS5rZXkgPT09ICdyZXR1cm4nKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIGhhbmRsZUNsb3NlKClcbiAgICB9XG4gIH1cblxuICAvLyBIYW5kbGUgdGVsZXBvcnRpbmcgdG8gcmVtb3RlIHNlc3Npb25cbiAgYXN5bmMgZnVuY3Rpb24gaGFuZGxlVGVsZXBvcnQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgc2V0SXNUZWxlcG9ydGluZyh0cnVlKVxuICAgIHNldFRlbGVwb3J0RXJyb3IobnVsbClcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0ZWxlcG9ydFJlc3VtZUNvZGVTZXNzaW9uKHNlc3Npb24uc2Vzc2lvbklkKVxuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgc2V0VGVsZXBvcnRFcnJvcihlcnJvck1lc3NhZ2UoZXJyKSlcbiAgICB9IGZpbmFsbHkge1xuICAgICAgc2V0SXNUZWxlcG9ydGluZyhmYWxzZSlcbiAgICB9XG4gIH1cblxuICAvLyBUcnVuY2F0ZSB0aXRsZSBpZiB0b28gbG9uZyAoZm9yIGRpc3BsYXkgcHVycG9zZXMpXG4gIGNvbnN0IGRpc3BsYXlUaXRsZSA9IHRydW5jYXRlVG9XaWR0aChzZXNzaW9uLnRpdGxlLCA1MClcblxuICAvLyBNYXAgVGFza1N0YXR1cyB0byBkaXNwbGF5IHN0YXR1cyAoaGFuZGxlICdwZW5kaW5nJylcbiAgY29uc3QgZGlzcGxheVN0YXR1cyA9XG4gICAgc2Vzc2lvbi5zdGF0dXMgPT09ICdwZW5kaW5nJyA/ICdzdGFydGluZycgOiBzZXNzaW9uLnN0YXR1c1xuXG4gIHJldHVybiAoXG4gICAgPEJveFxuICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICB0YWJJbmRleD17MH1cbiAgICAgIGF1dG9Gb2N1c1xuICAgICAgb25LZXlEb3duPXtoYW5kbGVLZXlEb3dufVxuICAgID5cbiAgICAgIDxEaWFsb2dcbiAgICAgICAgdGl0bGU9XCJSZW1vdGUgc2Vzc2lvbiBkZXRhaWxzXCJcbiAgICAgICAgb25DYW5jZWw9e2hhbmRsZUNsb3NlfVxuICAgICAgICBjb2xvcj1cImJhY2tncm91bmRcIlxuICAgICAgICBpbnB1dEd1aWRlPXtleGl0U3RhdGUgPT5cbiAgICAgICAgICBleGl0U3RhdGUucGVuZGluZyA/IChcbiAgICAgICAgICAgIDxUZXh0PlByZXNzIHtleGl0U3RhdGUua2V5TmFtZX0gYWdhaW4gdG8gZXhpdDwvVGV4dD5cbiAgICAgICAgICApIDogKFxuICAgICAgICAgICAgPEJ5bGluZT5cbiAgICAgICAgICAgICAge29uQmFjayAmJiA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCLihpBcIiBhY3Rpb249XCJnbyBiYWNrXCIgLz59XG4gICAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIkVzYy9FbnRlci9TcGFjZVwiIGFjdGlvbj1cImNsb3NlXCIgLz5cbiAgICAgICAgICAgICAgeyFpc1RlbGVwb3J0aW5nICYmIChcbiAgICAgICAgICAgICAgICA8S2V5Ym9hcmRTaG9ydGN1dEhpbnQgc2hvcnRjdXQ9XCJ0XCIgYWN0aW9uPVwidGVsZXBvcnRcIiAvPlxuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgPC9CeWxpbmU+XG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICA+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgYm9sZD5TdGF0dXM8L1RleHQ+OnsnICd9XG4gICAgICAgICAgICB7ZGlzcGxheVN0YXR1cyA9PT0gJ3J1bm5pbmcnIHx8IGRpc3BsYXlTdGF0dXMgPT09ICdzdGFydGluZycgPyAoXG4gICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiPntkaXNwbGF5U3RhdHVzfTwvVGV4dD5cbiAgICAgICAgICAgICkgOiBkaXNwbGF5U3RhdHVzID09PSAnY29tcGxldGVkJyA/IChcbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+e2Rpc3BsYXlTdGF0dXN9PC9UZXh0PlxuICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPntkaXNwbGF5U3RhdHVzfTwvVGV4dD5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgYm9sZD5SdW50aW1lPC9UZXh0Pjp7JyAnfVxuICAgICAgICAgICAge2Zvcm1hdER1cmF0aW9uKFxuICAgICAgICAgICAgICAoc2Vzc2lvbi5lbmRUaW1lID8/IERhdGUubm93KCkpIC0gc2Vzc2lvbi5zdGFydFRpbWUsXG4gICAgICAgICAgICApfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCB3cmFwPVwidHJ1bmNhdGUtZW5kXCI+XG4gICAgICAgICAgICA8VGV4dCBib2xkPlRpdGxlPC9UZXh0Pjoge2Rpc3BsYXlUaXRsZX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICA8VGV4dCBib2xkPlByb2dyZXNzPC9UZXh0Pjp7JyAnfVxuICAgICAgICAgICAgPFJlbW90ZVNlc3Npb25Qcm9ncmVzcyBzZXNzaW9uPXtzZXNzaW9ufSAvPlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+U2Vzc2lvbiBVUkw8L1RleHQ+OnsnICd9XG4gICAgICAgICAgICA8TGluayB1cmw9e2dldFJlbW90ZVRhc2tTZXNzaW9uVXJsKHNlc3Npb24uc2Vzc2lvbklkKX0+XG4gICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntnZXRSZW1vdGVUYXNrU2Vzc2lvblVybChzZXNzaW9uLnNlc3Npb25JZCl9PC9UZXh0PlxuICAgICAgICAgICAgPC9MaW5rPlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgey8qIFJlbW90ZSBzZXNzaW9uIG1lc3NhZ2VzIHNlY3Rpb24gKi99XG4gICAgICAgIHtzZXNzaW9uLmxvZy5sZW5ndGggPiAwICYmIChcbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgIDxUZXh0IGJvbGQ+UmVjZW50IG1lc3NhZ2VzPC9UZXh0PjpcbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGhlaWdodD17MTB9IG92ZXJmbG93WT1cImhpZGRlblwiPlxuICAgICAgICAgICAgICB7bGFzdE1lc3NhZ2VzLm1hcCgobXNnLCBpKSA9PiAoXG4gICAgICAgICAgICAgICAgPE1lc3NhZ2VcbiAgICAgICAgICAgICAgICAgIGtleT17aX1cbiAgICAgICAgICAgICAgICAgIG1lc3NhZ2U9e21zZ31cbiAgICAgICAgICAgICAgICAgIGxvb2t1cHM9e0VNUFRZX0xPT0tVUFN9XG4gICAgICAgICAgICAgICAgICBhZGRNYXJnaW49e2kgPiAwfVxuICAgICAgICAgICAgICAgICAgdG9vbHM9e3Rvb2xVc2VDb250ZXh0Lm9wdGlvbnMudG9vbHN9XG4gICAgICAgICAgICAgICAgICBjb21tYW5kcz17dG9vbFVzZUNvbnRleHQub3B0aW9ucy5jb21tYW5kc31cbiAgICAgICAgICAgICAgICAgIHZlcmJvc2U9e3Rvb2xVc2VDb250ZXh0Lm9wdGlvbnMudmVyYm9zZX1cbiAgICAgICAgICAgICAgICAgIGluUHJvZ3Jlc3NUb29sVXNlSURzPXtuZXcgU2V0KCl9XG4gICAgICAgICAgICAgICAgICBwcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZT17W119XG4gICAgICAgICAgICAgICAgICBzaG91bGRBbmltYXRlPXtmYWxzZX1cbiAgICAgICAgICAgICAgICAgIHNob3VsZFNob3dEb3Q9e2ZhbHNlfVxuICAgICAgICAgICAgICAgICAgc3R5bGU9XCJjb25kZW5zZWRcIlxuICAgICAgICAgICAgICAgICAgaXNUcmFuc2NyaXB0TW9kZT17ZmFsc2V9XG4gICAgICAgICAgICAgICAgICBpc1N0YXRpYz17dHJ1ZX1cbiAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICApKX1cbiAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAgICAgICAgU2hvd2luZyBsYXN0IHtsYXN0TWVzc2FnZXMubGVuZ3RofSBvZiB7c2Vzc2lvbi5sb2cubGVuZ3RofXsnICd9XG4gICAgICAgICAgICAgICAgbWVzc2FnZXNcbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG5cbiAgICAgICAgey8qIFRlbGVwb3J0IGVycm9yIG1lc3NhZ2UgKi99XG4gICAgICAgIHt0ZWxlcG9ydEVycm9yICYmIChcbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+VGVsZXBvcnQgZmFpbGVkOiB7dGVsZXBvcnRFcnJvcn08L1RleHQ+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG5cbiAgICAgICAgey8qIFRlbGVwb3J0aW5nIHN0YXR1cyAqL31cbiAgICAgICAge2lzVGVsZXBvcnRpbmcgJiYgKFxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiPlRlbGVwb3J0aW5nIHRvIHNlc3Npb27igKY8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICA8L0RpYWxvZz5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsT0FBTyxNQUFNLFNBQVM7QUFDN0IsT0FBT0MsS0FBSyxJQUFJQyxPQUFPLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ2hELGNBQWNDLFVBQVUsUUFBUSxrQ0FBa0M7QUFDbEUsY0FBY0MsY0FBYyxRQUFRLGFBQWE7QUFDakQsY0FBY0MsYUFBYSxRQUFRLG9CQUFvQjtBQUN2RCxjQUFjQyxvQkFBb0IsUUFBUSxtQkFBbUI7QUFDN0QsU0FBU0MsY0FBYyxFQUFFQyxZQUFZLFFBQVEsNEJBQTRCO0FBQ3pFLFNBQVNDLGNBQWMsUUFBUSwrQkFBK0I7QUFDOUQsY0FBY0MsYUFBYSxRQUFRLG9DQUFvQztBQUN2RSxTQUFTQyxHQUFHLEVBQUVDLElBQUksRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDOUMsY0FBY0Msb0JBQW9CLFFBQVEsZ0RBQWdEO0FBQzFGLFNBQVNDLHVCQUF1QixRQUFRLGdEQUFnRDtBQUN4RixTQUNFQyxlQUFlLEVBQ2ZDLHNCQUFzQixRQUNqQixvQ0FBb0M7QUFDM0MsU0FBU0MsMkJBQTJCLFFBQVEsMkNBQTJDO0FBQ3ZGLFNBQVNDLDJCQUEyQixRQUFRLDJDQUEyQztBQUN2RixTQUFTQyxXQUFXLFFBQVEsd0JBQXdCO0FBQ3BELFNBQVNDLFlBQVksUUFBUSx1QkFBdUI7QUFDcEQsU0FBU0MsY0FBYyxFQUFFQyxlQUFlLFFBQVEsdUJBQXVCO0FBQ3ZFLFNBQVNDLGtCQUFrQixRQUFRLGlDQUFpQztBQUNwRSxTQUFTQyxhQUFhLEVBQUVDLGlCQUFpQixRQUFRLHlCQUF5QjtBQUMxRSxTQUFTQyxNQUFNLFFBQVEsNEJBQTRCO0FBQ25ELFNBQVNDLHlCQUF5QixRQUFRLHlCQUF5QjtBQUNuRSxTQUFTQyxNQUFNLFFBQVEsMkJBQTJCO0FBQ2xELFNBQVNDLE1BQU0sUUFBUSw0QkFBNEI7QUFDbkQsU0FBU0MsTUFBTSxRQUFRLDRCQUE0QjtBQUNuRCxTQUFTQyxvQkFBb0IsUUFBUSwwQ0FBMEM7QUFDL0UsU0FBU0MsT0FBTyxRQUFRLGVBQWU7QUFDdkMsU0FDRUMsdUJBQXVCLEVBQ3ZCQyxxQkFBcUIsUUFDaEIsNEJBQTRCO0FBRW5DLEtBQUtDLEtBQUssR0FBRztFQUNYQyxPQUFPLEVBQUVoQyxhQUFhLENBQUNTLG9CQUFvQixDQUFDO0VBQzVDd0IsY0FBYyxFQUFFbEMsY0FBYztFQUM5Qm1DLE1BQU0sRUFBRSxDQUNOQyxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQ2ZDLE9BQTRDLENBQXBDLEVBQUU7SUFBRUMsT0FBTyxDQUFDLEVBQUVwQyxvQkFBb0I7RUFBQyxDQUFDLEVBQzVDLEdBQUcsSUFBSTtFQUNUcUMsTUFBTSxDQUFDLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDbkJDLE1BQU0sQ0FBQyxFQUFFLEdBQUcsR0FBRyxJQUFJO0FBQ3JCLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQVNDLG9CQUFvQkEsQ0FBQ0MsSUFBSSxFQUFFLE1BQU0sRUFBRUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxFQUFFLE1BQU0sQ0FBQztFQUN6RTtFQUNBLElBQUlELElBQUksS0FBSzNCLDJCQUEyQixFQUFFO0lBQ3hDLE9BQU8sMkNBQTJDO0VBQ3BEO0VBQ0EsSUFBSSxDQUFDNEIsS0FBSyxJQUFJLE9BQU9BLEtBQUssS0FBSyxRQUFRLEVBQUUsT0FBT0QsSUFBSTtFQUNwRDtFQUNBO0VBQ0EsSUFBSUEsSUFBSSxLQUFLNUIsMkJBQTJCLElBQUksV0FBVyxJQUFJNkIsS0FBSyxFQUFFO0lBQ2hFLE1BQU1DLEVBQUUsR0FBR0QsS0FBSyxDQUFDRSxTQUFTO0lBQzFCLElBQUlDLEtBQUssQ0FBQ0MsT0FBTyxDQUFDSCxFQUFFLENBQUMsSUFBSUEsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLE9BQU9BLEVBQUUsQ0FBQyxDQUFDLENBQUMsS0FBSyxRQUFRLEVBQUU7TUFDM0Q7TUFDQTtNQUNBO01BQ0EsTUFBTUksQ0FBQyxHQUNMLFVBQVUsSUFBSUosRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUNuQixPQUFPQSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUNLLFFBQVEsS0FBSyxRQUFRLElBQ2xDTCxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUNLLFFBQVEsR0FDVkwsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDSyxRQUFRLEdBQ2QsUUFBUSxJQUFJTCxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksT0FBT0EsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDTSxNQUFNLEtBQUssUUFBUSxHQUNuRE4sRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDTSxNQUFNLEdBQ1osSUFBSTtNQUNaLElBQUlGLENBQUMsRUFBRTtRQUNMLE1BQU1HLE9BQU8sR0FBR0gsQ0FBQyxDQUFDSSxPQUFPLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDQyxJQUFJLENBQUMsQ0FBQztRQUM3QyxPQUFPLHNCQUFzQmxDLGVBQWUsQ0FBQ2dDLE9BQU8sRUFBRSxFQUFFLENBQUMsRUFBRTtNQUM3RDtJQUNGO0VBQ0Y7RUFDQSxLQUFLLE1BQU1HLENBQUMsSUFBSUMsTUFBTSxDQUFDQyxNQUFNLENBQUNiLEtBQUssQ0FBQyxFQUFFO0lBQ3BDLElBQUksT0FBT1csQ0FBQyxLQUFLLFFBQVEsSUFBSUEsQ0FBQyxDQUFDRCxJQUFJLENBQUMsQ0FBQyxFQUFFO01BQ3JDLE1BQU1GLE9BQU8sR0FBR0csQ0FBQyxDQUFDRixPQUFPLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDQyxJQUFJLENBQUMsQ0FBQztNQUM3QyxPQUFPLEdBQUdYLElBQUksSUFBSXZCLGVBQWUsQ0FBQ2dDLE9BQU8sRUFBRSxFQUFFLENBQUMsRUFBRTtJQUNsRDtFQUNGO0VBQ0EsT0FBT1QsSUFBSTtBQUNiO0FBRUEsTUFBTWUsV0FBVyxHQUFHO0VBQ2xCQyxXQUFXLEVBQUUsZ0JBQWdCO0VBQzdCQyxVQUFVLEVBQUU7QUFDZCxDQUFDLElBQUlDLEtBQUs7QUFFVixNQUFNQyxVQUFVLEdBQUc7RUFDakJILFdBQVcsRUFBRSxTQUFTO0VBQ3RCQyxVQUFVLEVBQUU7QUFDZCxDQUFDLElBQUlDLEtBQUs7QUFFVixTQUFBRSx1QkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFnQztJQUFBaEMsT0FBQTtJQUFBRSxNQUFBO0lBQUFJLE1BQUE7SUFBQUM7RUFBQSxJQUFBdUIsRUFLQTtFQUM5QixNQUFBRyxPQUFBLEdBQWdCakMsT0FBTyxDQUFBa0MsTUFBTyxLQUFLLFNBQXlDLElBQTVCbEMsT0FBTyxDQUFBa0MsTUFBTyxLQUFLLFNBQVM7RUFDNUUsTUFBQUMsS0FBQSxHQUFjbkMsT0FBTyxDQUFBb0MsY0FBZTtFQUNwQyxNQUFBQyxVQUFBLEdBQW1CSixPQUFPLEdBQ3RCRSxLQUFLLEdBQ0hYLFdBQVcsQ0FBQ1csS0FBSyxDQUNSLEdBRlgsU0FHYyxHQUFkbkMsT0FBTyxDQUFBa0MsTUFBTztFQUNsQixNQUFBSSxXQUFBLEdBQW9CbEUsY0FBYyxDQUNoQzRCLE9BQU8sQ0FBQXVDLFNBQVUsRUFDakJOLE9BQU8sRUFDUCxJQUFJLEVBQ0osQ0FBQyxFQUNEakMsT0FBTyxDQUFBd0MsT0FDVCxDQUFDO0VBTUMsSUFBQUMsTUFBQSxHQUFhLENBQUM7RUFDZCxJQUFBQyxLQUFBLEdBQVksQ0FBQztFQUNiLElBQUFDLFNBQUEsR0FBeUQsSUFBSTtFQUM3RCxLQUFLLE1BQUFDLEdBQVMsSUFBSTVDLE9BQU8sQ0FBQTZDLEdBQUk7SUFDM0IsSUFBSUQsR0FBRyxDQUFBRSxJQUFLLEtBQUssV0FBVztNQUFFO0lBQVE7SUFDdEMsS0FBSyxNQUFBQyxLQUFXLElBQUlILEdBQUcsQ0FBQUksT0FBUSxDQUFBQyxPQUFRO01BQ3JDLElBQUlGLEtBQUssQ0FBQUQsSUFBSyxLQUFLLFVBQVU7UUFBRTtNQUFRO01BQ3ZDSixLQUFLLEVBQUU7TUFDUEMsU0FBQSxDQUFBQSxDQUFBLENBQVlJLEtBQUs7TUFDakIsSUFDRUEsS0FBSyxDQUFBdEMsSUFBSyxLQUFLOUIsZUFDc0IsSUFBckNvRSxLQUFLLENBQUF0QyxJQUFLLEtBQUs3QixzQkFBc0I7UUFFckM2RCxNQUFNLEVBQUU7TUFBQTtJQUNUO0VBQ0Y7RUFHYyxNQUFBUyxFQUFBLElBQUMsR0FBR1QsTUFBTTtFQUFBLElBQUFVLEVBQUE7RUFBQSxJQUFBcEIsQ0FBQSxRQUFBWSxTQUFBO0lBRVhRLEVBQUEsR0FBQVIsU0FBUyxHQUNuQm5DLG9CQUFvQixDQUFDbUMsU0FBUyxDQUFBbEMsSUFBSyxFQUFFa0MsU0FBUyxDQUFBakMsS0FDM0MsQ0FBQyxHQUZNLElBRU47SUFBQXFCLENBQUEsTUFBQVksU0FBQTtJQUFBWixDQUFBLE1BQUFvQixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBcEIsQ0FBQTtFQUFBO0VBQUEsSUFBQXFCLEVBQUE7RUFBQSxJQUFBckIsQ0FBQSxRQUFBVyxLQUFBLElBQUFYLENBQUEsUUFBQW1CLEVBQUEsSUFBQW5CLENBQUEsUUFBQW9CLEVBQUE7SUFMSEMsRUFBQTtNQUFBQyxhQUFBLEVBQ1VILEVBQVU7TUFBQUksU0FBQSxFQUNkWixLQUFLO01BQUFhLFlBQUEsRUFDRko7SUFHaEIsQ0FBQztJQUFBcEIsQ0FBQSxNQUFBVyxLQUFBO0lBQUFYLENBQUEsTUFBQW1CLEVBQUE7SUFBQW5CLENBQUEsTUFBQW9CLEVBQUE7SUFBQXBCLENBQUEsTUFBQXFCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFyQixDQUFBO0VBQUE7RUF4Qkg7SUFBQXNCLGFBQUE7SUFBQUMsU0FBQTtJQUFBQztFQUFBLElBa0JFSCxFQU1DO0VBQ2MsSUFBQUksRUFBQTtFQUFBLElBQUF6QixDQUFBLFFBQUEvQixPQUFBLENBQUF5RCxTQUFBO0lBRUVELEVBQUEsR0FBQTlFLHVCQUF1QixDQUFDc0IsT0FBTyxDQUFBeUQsU0FBVSxDQUFDO0lBQUExQixDQUFBLE1BQUEvQixPQUFBLENBQUF5RCxTQUFBO0lBQUExQixDQUFBLE1BQUF5QixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBekIsQ0FBQTtFQUFBO0VBQTdELE1BQUEyQixVQUFBLEdBQW1CRixFQUEwQztFQUFBLElBQUFHLEVBQUE7RUFBQSxJQUFBNUIsQ0FBQSxRQUFBekIsTUFBQSxJQUFBeUIsQ0FBQSxRQUFBN0IsTUFBQTtJQUUzRHlELEVBQUEsR0FBQXJELE1BQ3lFLEtBRHpFLE1BQ09KLE1BQU0sQ0FBQyxrQ0FBa0MsRUFBRTtNQUFBRyxPQUFBLEVBQVc7SUFBUyxDQUFDLENBQUU7SUFBQTBCLENBQUEsTUFBQXpCLE1BQUE7SUFBQXlCLENBQUEsTUFBQTdCLE1BQUE7SUFBQTZCLENBQUEsT0FBQTRCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUE1QixDQUFBO0VBQUE7RUFGM0UsTUFBQTZCLGFBQUEsR0FDRUQsRUFDeUU7RUFDM0UsT0FBQUUsY0FBQSxFQUFBQyxpQkFBQSxJQUE0Q2pHLFFBQVEsQ0FBQyxLQUFLLENBQUM7RUFFM0QsSUFBSWdHLGNBQWM7SUFBQSxJQUFBRSxFQUFBO0lBQUEsSUFBQWhDLENBQUEsU0FBQWlDLE1BQUEsQ0FBQUMsR0FBQTtNQUlGRixFQUFBLEdBQUFBLENBQUEsS0FBTUQsaUJBQWlCLENBQUMsS0FBSyxDQUFDO01BQUEvQixDQUFBLE9BQUFnQyxFQUFBO0lBQUE7TUFBQUEsRUFBQSxHQUFBaEMsQ0FBQTtJQUFBO0lBQUEsSUFBQW1DLEVBQUE7SUFBQSxJQUFBbkMsQ0FBQSxTQUFBaUMsTUFBQSxDQUFBQyxHQUFBO01BSXRDQyxFQUFBLElBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyx1REFFZixFQUZDLElBQUksQ0FFRTtNQUFBbkMsQ0FBQSxPQUFBbUMsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQW5DLENBQUE7SUFBQTtJQUFBLElBQUFvQyxFQUFBO0lBQUEsSUFBQXBDLENBQUEsU0FBQWlDLE1BQUEsQ0FBQUMsR0FBQTtNQUdIRSxFQUFBO1FBQUFDLEtBQUEsRUFBUyxtQkFBbUI7UUFBQUMsS0FBQSxFQUFTLE1BQU0sSUFBSTFDO01BQU0sQ0FBQztNQUFBSSxDQUFBLE9BQUFvQyxFQUFBO0lBQUE7TUFBQUEsRUFBQSxHQUFBcEMsQ0FBQTtJQUFBO0lBQUEsSUFBQXVDLEVBQUE7SUFBQSxJQUFBdkMsQ0FBQSxTQUFBaUMsTUFBQSxDQUFBQyxHQUFBO01BRC9DSyxFQUFBLElBQ1BILEVBQXNELEVBQ3REO1FBQUFDLEtBQUEsRUFBUyxNQUFNO1FBQUFDLEtBQUEsRUFBUyxNQUFNLElBQUkxQztNQUFNLENBQUMsQ0FDMUM7TUFBQUksQ0FBQSxPQUFBdUMsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQXZDLENBQUE7SUFBQTtJQUFBLElBQUF3QyxHQUFBO0lBQUEsSUFBQXhDLENBQUEsU0FBQTZCLGFBQUEsSUFBQTdCLENBQUEsU0FBQXhCLE1BQUE7TUFiUGdFLEdBQUEsSUFBQyxNQUFNLENBQ0MsS0FBaUIsQ0FBakIsaUJBQWlCLENBQ2IsUUFBOEIsQ0FBOUIsQ0FBQVIsRUFBNkIsQ0FBQyxDQUNsQyxLQUFZLENBQVosWUFBWSxDQUVsQixDQUFDLEdBQUcsQ0FBZSxhQUFRLENBQVIsUUFBUSxDQUFNLEdBQUMsQ0FBRCxHQUFDLENBQ2hDLENBQUFHLEVBRU0sQ0FDTixDQUFDLE1BQU0sQ0FDSSxPQUdSLENBSFEsQ0FBQUksRUFHVCxDQUFDLENBQ1MsUUFPVCxDQVBTLENBQUFqRCxDQUFBO1lBQ1IsSUFBSUEsQ0FBQyxLQUFLLE1BQU07Y0FDZGQsTUFBTSxHQUFHLENBQUM7Y0FDVnFELGFBQWEsQ0FBQyxDQUFDO1lBQUE7Y0FFZkUsaUJBQWlCLENBQUMsS0FBSyxDQUFDO1lBQUE7VUFDekIsQ0FDSCxDQUFDLEdBRUwsRUFsQkMsR0FBRyxDQW1CTixFQXhCQyxNQUFNLENBd0JFO01BQUEvQixDQUFBLE9BQUE2QixhQUFBO01BQUE3QixDQUFBLE9BQUF4QixNQUFBO01BQUF3QixDQUFBLE9BQUF3QyxHQUFBO0lBQUE7TUFBQUEsR0FBQSxHQUFBeEMsQ0FBQTtJQUFBO0lBQUEsT0F4QlR3QyxHQXdCUztFQUFBO0VBU0YsTUFBQVIsRUFBQSxHQUFBNUIsS0FBSyxLQUFLLFlBQTRDLEdBQXREakUsY0FBc0QsR0FBdERDLFlBQXNEO0VBQUEsSUFBQStGLEVBQUE7RUFBQSxJQUFBbkMsQ0FBQSxTQUFBZ0MsRUFBQTtJQUR6REcsRUFBQSxJQUFDLElBQUksQ0FBTyxLQUFZLENBQVosWUFBWSxDQUNyQixDQUFBSCxFQUFxRCxDQUFHLElBQUUsQ0FDN0QsRUFGQyxJQUFJLENBRUU7SUFBQWhDLENBQUEsT0FBQWdDLEVBQUE7SUFBQWhDLENBQUEsT0FBQW1DLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFuQyxDQUFBO0VBQUE7RUFBQSxJQUFBb0MsRUFBQTtFQUFBLElBQUFwQyxDQUFBLFNBQUFpQyxNQUFBLENBQUFDLEdBQUE7SUFDUEUsRUFBQSxJQUFDLElBQUksQ0FBQyxJQUFJLENBQUosS0FBRyxDQUFDLENBQUMsU0FBUyxFQUFuQixJQUFJLENBQXNCO0lBQUFwQyxDQUFBLE9BQUFvQyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBcEMsQ0FBQTtFQUFBO0VBQUEsSUFBQXVDLEVBQUE7RUFBQSxJQUFBdkMsQ0FBQSxTQUFBTyxXQUFBLElBQUFQLENBQUEsU0FBQU0sVUFBQTtJQUMzQmlDLEVBQUEsSUFBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUNYLFNBQUksQ0FDSmhDLFlBQVUsQ0FDVixTQUFJLENBQ0pELFdBQVMsQ0FDWixFQUxDLElBQUksQ0FLRTtJQUFBTixDQUFBLE9BQUFPLFdBQUE7SUFBQVAsQ0FBQSxPQUFBTSxVQUFBO0lBQUFOLENBQUEsT0FBQXVDLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUF2QyxDQUFBO0VBQUE7RUFBQSxJQUFBd0MsR0FBQTtFQUFBLElBQUF4QyxDQUFBLFNBQUFtQyxFQUFBLElBQUFuQyxDQUFBLFNBQUF1QyxFQUFBO0lBVlRDLEdBQUEsSUFBQyxJQUFJLENBQ0gsQ0FBQUwsRUFFTSxDQUNOLENBQUFDLEVBQTBCLENBQzFCLENBQUFHLEVBS00sQ0FDUixFQVhDLElBQUksQ0FXRTtJQUFBdkMsQ0FBQSxPQUFBbUMsRUFBQTtJQUFBbkMsQ0FBQSxPQUFBdUMsRUFBQTtJQUFBdkMsQ0FBQSxPQUFBd0MsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQXhDLENBQUE7RUFBQTtFQUFBLElBQUF5QyxHQUFBO0VBQUEsSUFBQXpDLENBQUEsU0FBQUksS0FBQTtJQU9KcUMsR0FBQSxHQUFBckMsS0FBSyxLQUFLLFlBRVYsSUFEQyxDQUFDLElBQUksQ0FBTyxLQUFTLENBQVQsU0FBUyxDQUFFLENBQUF6RSxPQUFPLENBQUErRyxJQUFJLENBQUUsQ0FBQyxFQUFwQyxJQUFJLENBQ047SUFBQTFDLENBQUEsT0FBQUksS0FBQTtJQUFBSixDQUFBLE9BQUF5QyxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBekMsQ0FBQTtFQUFBO0VBQUEsSUFBQTJDLEdBQUE7RUFBQSxJQUFBM0MsQ0FBQSxTQUFBc0IsYUFBQTtJQUNnQnFCLEdBQUEsR0FBQXBGLE1BQU0sQ0FBQytELGFBQWEsRUFBRSxPQUFPLENBQUM7SUFBQXRCLENBQUEsT0FBQXNCLGFBQUE7SUFBQXRCLENBQUEsT0FBQTJDLEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUEzQyxDQUFBO0VBQUE7RUFDOUMsTUFBQTRDLEdBQUEsR0FBQXhDLEtBQUssR0FBR1AsVUFBVSxDQUFDTyxLQUFLLENBQWEsR0FBckMsU0FBcUM7RUFBQSxJQUFBeUMsR0FBQTtFQUFBLElBQUE3QyxDQUFBLFNBQUF1QixTQUFBO0lBQ3JDc0IsR0FBQSxHQUFBdEYsTUFBTSxDQUFDZ0UsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUFBdkIsQ0FBQSxPQUFBdUIsU0FBQTtJQUFBdkIsQ0FBQSxPQUFBNkMsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQTdDLENBQUE7RUFBQTtFQUFBLElBQUE4QyxHQUFBO0VBQUEsSUFBQTlDLENBQUEsU0FBQXNCLGFBQUEsSUFBQXRCLENBQUEsU0FBQXlDLEdBQUEsSUFBQXpDLENBQUEsU0FBQTJDLEdBQUEsSUFBQTNDLENBQUEsU0FBQTRDLEdBQUEsSUFBQTVDLENBQUEsU0FBQTZDLEdBQUEsSUFBQTdDLENBQUEsU0FBQXVCLFNBQUE7SUFONUJ1QixHQUFBLElBQUMsSUFBSSxDQUNGLENBQUFMLEdBRUQsQ0FDQ25CLGNBQVksQ0FBRSxDQUFFLENBQUFxQixHQUE2QixDQUFHLElBQUUsQ0FDbEQsQ0FBQUMsR0FBb0MsQ0FBRSxHQUFJckIsVUFBUSxDQUFFLEtBQU0sSUFBRSxDQUM1RCxDQUFBc0IsR0FBd0IsQ0FDM0IsRUFQQyxJQUFJLENBT0U7SUFBQTdDLENBQUEsT0FBQXNCLGFBQUE7SUFBQXRCLENBQUEsT0FBQXlDLEdBQUE7SUFBQXpDLENBQUEsT0FBQTJDLEdBQUE7SUFBQTNDLENBQUEsT0FBQTRDLEdBQUE7SUFBQTVDLENBQUEsT0FBQTZDLEdBQUE7SUFBQTdDLENBQUEsT0FBQXVCLFNBQUE7SUFBQXZCLENBQUEsT0FBQThDLEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUE5QyxDQUFBO0VBQUE7RUFBQSxJQUFBK0MsR0FBQTtFQUFBLElBQUEvQyxDQUFBLFNBQUF3QixZQUFBO0lBQ051QixHQUFBLEdBQUF2QixZQUFvRCxJQUFwQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUVBLGFBQVcsQ0FBRSxFQUE1QixJQUFJLENBQStCO0lBQUF4QixDQUFBLE9BQUF3QixZQUFBO0lBQUF4QixDQUFBLE9BQUErQyxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBL0MsQ0FBQTtFQUFBO0VBQUEsSUFBQWdELEdBQUE7RUFBQSxJQUFBaEQsQ0FBQSxTQUFBMkIsVUFBQTtJQUVuRHFCLEdBQUEsSUFBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFFckIsV0FBUyxDQUFFLEVBQTFCLElBQUksQ0FBNkI7SUFBQTNCLENBQUEsT0FBQTJCLFVBQUE7SUFBQTNCLENBQUEsT0FBQWdELEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUFoRCxDQUFBO0VBQUE7RUFBQSxJQUFBaUQsR0FBQTtFQUFBLElBQUFqRCxDQUFBLFNBQUEyQixVQUFBLElBQUEzQixDQUFBLFNBQUFnRCxHQUFBO0lBRHBDQyxHQUFBLElBQUMsSUFBSSxDQUFNdEIsR0FBVSxDQUFWQSxXQUFTLENBQUMsQ0FDbkIsQ0FBQXFCLEdBQWlDLENBQ25DLEVBRkMsSUFBSSxDQUVFO0lBQUFoRCxDQUFBLE9BQUEyQixVQUFBO0lBQUEzQixDQUFBLE9BQUFnRCxHQUFBO0lBQUFoRCxDQUFBLE9BQUFpRCxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBakQsQ0FBQTtFQUFBO0VBQUEsSUFBQWtELEdBQUE7RUFBQSxJQUFBbEQsQ0FBQSxTQUFBaUMsTUFBQSxDQUFBQyxHQUFBO0lBR0hnQixHQUFBO01BQUFiLEtBQUEsRUFDUyxrQ0FBa0M7TUFBQUMsS0FBQSxFQUNsQyxNQUFNLElBQUkxQztJQUNuQixDQUFDO0lBQUFJLENBQUEsT0FBQWtELEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUFsRCxDQUFBO0VBQUE7RUFBQSxJQUFBbUQsR0FBQTtFQUFBLElBQUFuRCxDQUFBLFNBQUF4QixNQUFBLElBQUF3QixDQUFBLFNBQUFFLE9BQUE7SUFDR2lELEdBQUEsR0FBQTNFLE1BQWlCLElBQWpCMEIsT0FFRSxHQUZGLENBQ0M7TUFBQW1DLEtBQUEsRUFBUyxnQkFBZ0I7TUFBQUMsS0FBQSxFQUFTLE1BQU0sSUFBSTFDO0lBQU0sQ0FBQyxDQUNsRCxHQUZGLEVBRUU7SUFBQUksQ0FBQSxPQUFBeEIsTUFBQTtJQUFBd0IsQ0FBQSxPQUFBRSxPQUFBO0lBQUFGLENBQUEsT0FBQW1ELEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUFuRCxDQUFBO0VBQUE7RUFBQSxJQUFBb0QsR0FBQTtFQUFBLElBQUFwRCxDQUFBLFNBQUFpQyxNQUFBLENBQUFDLEdBQUE7SUFDTmtCLEdBQUE7TUFBQWYsS0FBQSxFQUFTLE1BQU07TUFBQUMsS0FBQSxFQUFTLE1BQU0sSUFBSTFDO0lBQU0sQ0FBQztJQUFBSSxDQUFBLE9BQUFvRCxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBcEQsQ0FBQTtFQUFBO0VBQUEsSUFBQXFELEdBQUE7RUFBQSxJQUFBckQsQ0FBQSxTQUFBbUQsR0FBQTtJQVJsQ0UsR0FBQSxJQUNQSCxHQUdDLEtBQ0dDLEdBRUUsRUFDTkMsR0FBeUMsQ0FDMUM7SUFBQXBELENBQUEsT0FBQW1ELEdBQUE7SUFBQW5ELENBQUEsT0FBQXFELEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUFyRCxDQUFBO0VBQUE7RUFBQSxJQUFBc0QsR0FBQTtFQUFBLElBQUF0RCxDQUFBLFNBQUE2QixhQUFBLElBQUE3QixDQUFBLFNBQUE3QixNQUFBLElBQUE2QixDQUFBLFNBQUEyQixVQUFBO0lBQ1MyQixHQUFBLEdBQUFDLEdBQUE7TUFDUixRQUFRakUsR0FBQztRQUFBLEtBQ0YsTUFBTTtVQUFBO1lBQ0p0QyxXQUFXLENBQUMyRSxVQUFVLENBQUM7WUFJNUJ4RCxNQUFNLENBQUMsQ0FBQztZQUFBO1VBQUE7UUFBQSxLQUVMLE1BQU07VUFBQTtZQUNUNEQsaUJBQWlCLENBQUMsSUFBSSxDQUFDO1lBQUE7VUFBQTtRQUFBLEtBRXBCLE1BQU07VUFBQTtZQUNURixhQUFhLENBQUMsQ0FBQztZQUFBO1VBQUE7TUFFbkI7SUFBQyxDQUNGO0lBQUE3QixDQUFBLE9BQUE2QixhQUFBO0lBQUE3QixDQUFBLE9BQUE3QixNQUFBO0lBQUE2QixDQUFBLE9BQUEyQixVQUFBO0lBQUEzQixDQUFBLE9BQUFzRCxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBdEQsQ0FBQTtFQUFBO0VBQUEsSUFBQXdELEdBQUE7RUFBQSxJQUFBeEQsQ0FBQSxTQUFBcUQsR0FBQSxJQUFBckQsQ0FBQSxTQUFBc0QsR0FBQTtJQTNCSEUsR0FBQSxJQUFDLE1BQU0sQ0FDSSxPQVNSLENBVFEsQ0FBQUgsR0FTVCxDQUFDLENBQ1MsUUFnQlQsQ0FoQlMsQ0FBQUMsR0FnQlYsQ0FBQyxHQUNEO0lBQUF0RCxDQUFBLE9BQUFxRCxHQUFBO0lBQUFyRCxDQUFBLE9BQUFzRCxHQUFBO0lBQUF0RCxDQUFBLE9BQUF3RCxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBeEQsQ0FBQTtFQUFBO0VBQUEsSUFBQXlELEdBQUE7RUFBQSxJQUFBekQsQ0FBQSxTQUFBOEMsR0FBQSxJQUFBOUMsQ0FBQSxTQUFBK0MsR0FBQSxJQUFBL0MsQ0FBQSxTQUFBaUQsR0FBQSxJQUFBakQsQ0FBQSxTQUFBd0QsR0FBQTtJQXpDSkMsR0FBQSxJQUFDLEdBQUcsQ0FBZSxhQUFRLENBQVIsUUFBUSxDQUFNLEdBQUMsQ0FBRCxHQUFDLENBQ2hDLENBQUFYLEdBT00sQ0FDTCxDQUFBQyxHQUFtRCxDQUNwRCxDQUFBRSxHQUVNLENBQ04sQ0FBQU8sR0E0QkMsQ0FDSCxFQTFDQyxHQUFHLENBMENFO0lBQUF4RCxDQUFBLE9BQUE4QyxHQUFBO0lBQUE5QyxDQUFBLE9BQUErQyxHQUFBO0lBQUEvQyxDQUFBLE9BQUFpRCxHQUFBO0lBQUFqRCxDQUFBLE9BQUF3RCxHQUFBO0lBQUF4RCxDQUFBLE9BQUF5RCxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBekQsQ0FBQTtFQUFBO0VBQUEsSUFBQTBELEdBQUE7RUFBQSxJQUFBMUQsQ0FBQSxTQUFBNkIsYUFBQSxJQUFBN0IsQ0FBQSxTQUFBd0MsR0FBQSxJQUFBeEMsQ0FBQSxTQUFBeUQsR0FBQTtJQTVEUkMsR0FBQSxJQUFDLE1BQU0sQ0FFSCxLQVdPLENBWFAsQ0FBQWxCLEdBV00sQ0FBQyxDQUVDWCxRQUFhLENBQWJBLGNBQVksQ0FBQyxDQUNqQixLQUFZLENBQVosWUFBWSxDQUVsQixDQUFBNEIsR0EwQ0ssQ0FDUCxFQTdEQyxNQUFNLENBNkRFO0lBQUF6RCxDQUFBLE9BQUE2QixhQUFBO0lBQUE3QixDQUFBLE9BQUF3QyxHQUFBO0lBQUF4QyxDQUFBLE9BQUF5RCxHQUFBO0lBQUF6RCxDQUFBLE9BQUEwRCxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBMUQsQ0FBQTtFQUFBO0VBQUEsT0E3RFQwRCxHQTZEUztBQUFBO0FBSWIsTUFBTUMsTUFBTSxHQUFHLENBQUMsU0FBUyxFQUFFLFdBQVcsRUFBRSxjQUFjLENBQUMsSUFBSS9ELEtBQUs7QUFDaEUsTUFBTWdFLFlBQVksRUFBRUMsTUFBTSxDQUFDLENBQUMsT0FBT0YsTUFBTSxDQUFDLENBQUMsTUFBTSxDQUFDLEVBQUUsTUFBTSxDQUFDLEdBQUc7RUFDNURHLE9BQU8sRUFBRSxNQUFNO0VBQ2ZDLFNBQVMsRUFBRSxRQUFRO0VBQ25CQyxZQUFZLEVBQUU7QUFDaEIsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBQUMsY0FBQWxFLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBdUI7SUFBQWlFLEtBQUE7SUFBQUMsU0FBQTtJQUFBQztFQUFBLElBQUFyRSxFQVF0QjtFQUFBLElBQUFvQixFQUFBO0VBQUEsSUFBQW5CLENBQUEsUUFBQWtFLEtBQUE7SUFDb0IvQyxFQUFBLEdBQUErQyxLQUFLLEdBQUdQLE1BQU0sQ0FBQVUsT0FBUSxDQUFDSCxLQUFVLENBQUMsR0FBbEMsRUFBa0M7SUFBQWxFLENBQUEsTUFBQWtFLEtBQUE7SUFBQWxFLENBQUEsTUFBQW1CLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFuQixDQUFBO0VBQUE7RUFBckQsTUFBQXNFLFVBQUEsR0FBbUJuRCxFQUFrQztFQUNyRCxNQUFBb0QsT0FBQSxHQUFnQixDQUFDSixTQUF5QixJQUExQixDQUFlQyxXQUFXO0VBQUEsSUFBQWhELEVBQUE7RUFBQSxJQUFBcEIsQ0FBQSxRQUFBdUUsT0FBQTtJQUdyQ25ELEVBQUEsR0FBQW1ELE9BQU8sR0FDTixDQUFDLElBQUksQ0FBTyxLQUFZLENBQVosWUFBWSxDQUFDLEtBQUssRUFBN0IsSUFBSSxDQUdOLEdBREMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLEtBQUssRUFBbkIsSUFBSSxDQUNOO0lBQUF2RSxDQUFBLE1BQUF1RSxPQUFBO0lBQUF2RSxDQUFBLE1BQUFvQixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBcEIsQ0FBQTtFQUFBO0VBQUEsSUFBQXFCLEVBQUE7RUFBQSxJQUFBckIsQ0FBQSxRQUFBaUMsTUFBQSxDQUFBQyxHQUFBO0lBQ0RiLEVBQUEsSUFBQyxJQUFJLENBQUMsUUFBUSxDQUFSLEtBQU8sQ0FBQyxDQUFDLEdBQUcsRUFBakIsSUFBSSxDQUFvQjtJQUFBckIsQ0FBQSxNQUFBcUIsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQXJCLENBQUE7RUFBQTtFQUFBLElBQUF5QixFQUFBO0VBQUEsSUFBQXpCLENBQUEsUUFBQW1FLFNBQUEsSUFBQW5FLENBQUEsUUFBQXNFLFVBQUEsSUFBQXRFLENBQUEsUUFBQXVFLE9BQUE7SUFDeEI5QyxFQUFBLEdBQUFrQyxNQUFNLENBQUFhLEdBQUksQ0FBQyxDQUFBQyxDQUFBLEVBQUFDLENBQUE7TUFDVixNQUFBQyxTQUFBLEdBQWtCLENBQUNSLFNBQXFCLElBQXRCLENBQWVJLE9BQTJCLElBQWhCRyxDQUFDLEtBQUtKLFVBQVU7TUFBQSxPQUUxRCxnQkFBcUJHLEdBQUMsQ0FBREEsRUFBQSxDQUFDLENBQ25CLENBQUFDLENBQUMsR0FBRyxDQUE4QixJQUF6QixDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsR0FBRyxFQUFqQixJQUFJLENBQW1CLENBQ2pDLENBQUFDLFNBQVMsR0FDUixDQUFDLElBQUksQ0FBTyxLQUFZLENBQVosWUFBWSxDQUFFLENBQUFmLFlBQVksQ0FBQ2EsQ0FBQyxFQUFFLEVBQXpDLElBQUksQ0FHTixHQURDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBRSxDQUFBYixZQUFZLENBQUNhLENBQUMsRUFBRSxFQUEvQixJQUFJLENBQ1AsQ0FDRixpQkFBaUI7SUFBQSxDQUVwQixDQUFDO0lBQUF6RSxDQUFBLE1BQUFtRSxTQUFBO0lBQUFuRSxDQUFBLE1BQUFzRSxVQUFBO0lBQUF0RSxDQUFBLE1BQUF1RSxPQUFBO0lBQUF2RSxDQUFBLE1BQUF5QixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBekIsQ0FBQTtFQUFBO0VBQUEsSUFBQTRCLEVBQUE7RUFBQSxJQUFBNUIsQ0FBQSxRQUFBbUUsU0FBQTtJQUNEdkMsRUFBQSxHQUFBdUMsU0FBNEMsSUFBL0IsQ0FBQyxJQUFJLENBQU8sS0FBUyxDQUFULFNBQVMsQ0FBQyxFQUFFLEVBQXZCLElBQUksQ0FBMEI7SUFBQW5FLENBQUEsTUFBQW1FLFNBQUE7SUFBQW5FLENBQUEsT0FBQTRCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUE1QixDQUFBO0VBQUE7RUFBQSxJQUFBZ0MsRUFBQTtFQUFBLElBQUFoQyxDQUFBLFNBQUFvQixFQUFBLElBQUFwQixDQUFBLFNBQUF5QixFQUFBLElBQUF6QixDQUFBLFNBQUE0QixFQUFBO0lBcEIvQ0ksRUFBQSxJQUFDLElBQUksQ0FDRixDQUFBWixFQUlELENBQ0EsQ0FBQUMsRUFBd0IsQ0FDdkIsQ0FBQUksRUFZQSxDQUNBLENBQUFHLEVBQTJDLENBQzlDLEVBckJDLElBQUksQ0FxQkU7SUFBQTVCLENBQUEsT0FBQW9CLEVBQUE7SUFBQXBCLENBQUEsT0FBQXlCLEVBQUE7SUFBQXpCLENBQUEsT0FBQTRCLEVBQUE7SUFBQTVCLENBQUEsT0FBQWdDLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFoQyxDQUFBO0VBQUE7RUFBQSxPQXJCUGdDLEVBcUJPO0FBQUE7O0FBSVg7QUFDQTtBQUNBO0FBQ0EsU0FBUzRDLGdCQUFnQkEsQ0FDdkIzRyxPQUFPLEVBQUVoQyxhQUFhLENBQUNTLG9CQUFvQixDQUFDLENBQzdDLEVBQUUsTUFBTSxDQUFDO0VBQ1IsTUFBTW1JLENBQUMsR0FBRzVHLE9BQU8sQ0FBQzZHLGNBQWM7RUFDaEM7RUFDQTtFQUNBLElBQUksQ0FBQ0QsQ0FBQyxFQUFFLE9BQU81RyxPQUFPLENBQUNrQyxNQUFNLEtBQUssV0FBVyxHQUFHLE1BQU0sR0FBRyxZQUFZO0VBQ3JFLE1BQU00RSxRQUFRLEdBQUdGLENBQUMsQ0FBQ0csWUFBWTtFQUMvQixNQUFNQyxPQUFPLEdBQUdKLENBQUMsQ0FBQ0ssV0FBVyxJQUFJLENBQUM7RUFDbEMsSUFBSWpILE9BQU8sQ0FBQ2tDLE1BQU0sS0FBSyxXQUFXLEVBQUU7SUFDbEMsTUFBTWdGLEtBQUssR0FBRyxDQUFDLEdBQUdKLFFBQVEsSUFBSXhILE1BQU0sQ0FBQ3dILFFBQVEsRUFBRSxTQUFTLENBQUMsRUFBRSxDQUFDO0lBQzVELElBQUlFLE9BQU8sR0FBRyxDQUFDLEVBQUVFLEtBQUssQ0FBQ0MsSUFBSSxDQUFDLEdBQUdILE9BQU8sVUFBVSxDQUFDO0lBQ2pELE9BQU9FLEtBQUssQ0FBQ0UsSUFBSSxDQUFDLEtBQUssQ0FBQztFQUMxQjtFQUNBLE9BQU92SCx1QkFBdUIsQ0FBQytHLENBQUMsQ0FBQ1gsS0FBSyxFQUFFVyxDQUFDLENBQUNTLFNBQVMsRUFBRVAsUUFBUSxFQUFFRSxPQUFPLENBQUM7QUFDekU7QUFFQSxLQUFLTSxVQUFVLEdBQUcsTUFBTSxHQUFHLE1BQU0sR0FBRyxNQUFNLEdBQUcsU0FBUztBQUV0RCxTQUFBQyxvQkFBQXpGLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBNkI7SUFBQWhDLE9BQUE7SUFBQUUsTUFBQTtJQUFBSSxNQUFBO0lBQUFDO0VBQUEsSUFBQXVCLEVBS0c7RUFDOUIsTUFBQW9FLFNBQUEsR0FBa0JsRyxPQUFPLENBQUFrQyxNQUFPLEtBQUssV0FBVztFQUNoRCxNQUFBRCxPQUFBLEdBQWdCakMsT0FBTyxDQUFBa0MsTUFBTyxLQUFLLFNBQXlDLElBQTVCbEMsT0FBTyxDQUFBa0MsTUFBTyxLQUFLLFNBQVM7RUFDNUUsT0FBQTJCLGNBQUEsRUFBQUMsaUJBQUEsSUFBNENqRyxRQUFRLENBQUMsS0FBSyxDQUFDO0VBTTNELE1BQUF5RSxXQUFBLEdBQW9CbEUsY0FBYyxDQUNoQzRCLE9BQU8sQ0FBQXVDLFNBQVUsRUFDakJOLE9BQU8sRUFDUCxJQUFJLEVBQ0osQ0FBQyxFQUNEakMsT0FBTyxDQUFBd0MsT0FDVCxDQUFDO0VBQUEsSUFBQVUsRUFBQTtFQUFBLElBQUFuQixDQUFBLFFBQUE3QixNQUFBO0lBRW1CZ0QsRUFBQSxHQUFBQSxDQUFBLEtBQ2xCaEQsTUFBTSxDQUFDLGtDQUFrQyxFQUFFO01BQUFHLE9BQUEsRUFBVztJQUFTLENBQUMsQ0FBQztJQUFBMEIsQ0FBQSxNQUFBN0IsTUFBQTtJQUFBNkIsQ0FBQSxNQUFBbUIsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQW5CLENBQUE7RUFBQTtFQURuRSxNQUFBeUYsV0FBQSxHQUFvQnRFLEVBQytDO0VBQ25FLE1BQUFVLGFBQUEsR0FBc0J0RCxNQUFxQixJQUFyQmtILFdBQXFCO0VBQUEsSUFBQXJFLEVBQUE7RUFBQSxJQUFBcEIsQ0FBQSxRQUFBL0IsT0FBQSxDQUFBeUQsU0FBQTtJQUV4Qk4sRUFBQSxHQUFBekUsdUJBQXVCLENBQUNzQixPQUFPLENBQUF5RCxTQUFVLENBQUM7SUFBQTFCLENBQUEsTUFBQS9CLE9BQUEsQ0FBQXlELFNBQUE7SUFBQTFCLENBQUEsTUFBQW9CLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFwQixDQUFBO0VBQUE7RUFBN0QsTUFBQTJCLFVBQUEsR0FBbUJQLEVBQTBDO0VBQzdELE1BQUFzRSxXQUFBLEdBQW9CdkIsU0FBUyxHQUFULE9BQTBELEdBQXBDakUsT0FBTyxHQUFQLFNBQW9DLEdBQWRqQyxPQUFPLENBQUFrQyxNQUFPO0VBRTlFLElBQUkyQixjQUFjO0lBQUEsSUFBQVQsRUFBQTtJQUFBLElBQUFyQixDQUFBLFFBQUFpQyxNQUFBLENBQUFDLEdBQUE7TUFJRmIsRUFBQSxHQUFBQSxDQUFBLEtBQU1VLGlCQUFpQixDQUFDLEtBQUssQ0FBQztNQUFBL0IsQ0FBQSxNQUFBcUIsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQXJCLENBQUE7SUFBQTtJQUFBLElBQUF5QixFQUFBO0lBQUEsSUFBQXpCLENBQUEsUUFBQWlDLE1BQUEsQ0FBQUMsR0FBQTtNQUl0Q1QsRUFBQSxJQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsOEhBR2YsRUFIQyxJQUFJLENBR0U7TUFBQXpCLENBQUEsTUFBQXlCLEVBQUE7SUFBQTtNQUFBQSxFQUFBLEdBQUF6QixDQUFBO0lBQUE7SUFBQSxJQUFBNEIsRUFBQTtJQUFBLElBQUE1QixDQUFBLFFBQUFpQyxNQUFBLENBQUFDLEdBQUE7TUFHSE4sRUFBQTtRQUFBUyxLQUFBLEVBQVMsa0JBQWtCO1FBQUFDLEtBQUEsRUFBUyxNQUFNLElBQUkxQztNQUFNLENBQUM7TUFBQUksQ0FBQSxNQUFBNEIsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQTVCLENBQUE7SUFBQTtJQUFBLElBQUFnQyxFQUFBO0lBQUEsSUFBQWhDLENBQUEsUUFBQWlDLE1BQUEsQ0FBQUMsR0FBQTtNQUQ5Q0YsRUFBQSxJQUNQSixFQUFxRCxFQUNyRDtRQUFBUyxLQUFBLEVBQVMsTUFBTTtRQUFBQyxLQUFBLEVBQVMsTUFBTSxJQUFJMUM7TUFBTSxDQUFDLENBQzFDO01BQUFJLENBQUEsTUFBQWdDLEVBQUE7SUFBQTtNQUFBQSxFQUFBLEdBQUFoQyxDQUFBO0lBQUE7SUFBQSxJQUFBbUMsRUFBQTtJQUFBLElBQUFuQyxDQUFBLFFBQUE2QixhQUFBLElBQUE3QixDQUFBLFFBQUF4QixNQUFBO01BZFAyRCxFQUFBLElBQUMsTUFBTSxDQUNDLEtBQW1CLENBQW5CLG1CQUFtQixDQUNmLFFBQThCLENBQTlCLENBQUFkLEVBQTZCLENBQUMsQ0FDbEMsS0FBWSxDQUFaLFlBQVksQ0FFbEIsQ0FBQyxHQUFHLENBQWUsYUFBUSxDQUFSLFFBQVEsQ0FBTSxHQUFDLENBQUQsR0FBQyxDQUNoQyxDQUFBSSxFQUdNLENBQ04sQ0FBQyxNQUFNLENBQ0ksT0FHUixDQUhRLENBQUFPLEVBR1QsQ0FBQyxDQUNTLFFBT1QsQ0FQUyxDQUFBMUMsQ0FBQTtZQUNSLElBQUlBLENBQUMsS0FBSyxNQUFNO2NBQ2RkLE1BQU0sR0FBRyxDQUFDO2NBQ1ZxRCxhQUFhLENBQUMsQ0FBQztZQUFBO2NBRWZFLGlCQUFpQixDQUFDLEtBQUssQ0FBQztZQUFBO1VBQ3pCLENBQ0gsQ0FBQyxHQUVMLEVBbkJDLEdBQUcsQ0FvQk4sRUF6QkMsTUFBTSxDQXlCRTtNQUFBL0IsQ0FBQSxNQUFBNkIsYUFBQTtNQUFBN0IsQ0FBQSxNQUFBeEIsTUFBQTtNQUFBd0IsQ0FBQSxPQUFBbUMsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQW5DLENBQUE7SUFBQTtJQUFBLE9BekJUbUMsRUF5QlM7RUFBQTtFQUVaLElBQUFkLEVBQUE7RUFBQSxJQUFBckIsQ0FBQSxTQUFBbUUsU0FBQSxJQUFBbkUsQ0FBQSxTQUFBeEIsTUFBQSxJQUFBd0IsQ0FBQSxTQUFBRSxPQUFBO0lBRXVEbUIsRUFBQSxHQUFBOEMsU0FBUyxHQUFULENBRWxEO01BQUE5QixLQUFBLEVBQVMsZ0NBQWdDO01BQUFDLEtBQUEsRUFBUztJQUFPLENBQUMsRUFDMUQ7TUFBQUQsS0FBQSxFQUFTLFNBQVM7TUFBQUMsS0FBQSxFQUFTO0lBQVUsQ0FBQyxDQVF2QyxHQVhtRCxDQU1sRDtNQUFBRCxLQUFBLEVBQVMsZ0NBQWdDO01BQUFDLEtBQUEsRUFBUztJQUFPLENBQUMsTUFDdEQ5RCxNQUFpQixJQUFqQjBCLE9BRUUsR0FGRixDQUNDO01BQUFtQyxLQUFBLEVBQVMsa0JBQWtCO01BQUFDLEtBQUEsRUFBUyxNQUFNLElBQUkxQztJQUFNLENBQUMsQ0FDcEQsR0FGRixFQUVFLEdBQ047TUFBQXlDLEtBQUEsRUFBUyxNQUFNO01BQUFDLEtBQUEsRUFBUztJQUFPLENBQUMsQ0FDakM7SUFBQXRDLENBQUEsT0FBQW1FLFNBQUE7SUFBQW5FLENBQUEsT0FBQXhCLE1BQUE7SUFBQXdCLENBQUEsT0FBQUUsT0FBQTtJQUFBRixDQUFBLE9BQUFxQixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBckIsQ0FBQTtFQUFBO0VBWEwsTUFBQTNCLE9BQUEsR0FBd0RnRCxFQVduRDtFQUFBLElBQUFJLEVBQUE7RUFBQSxJQUFBekIsQ0FBQSxTQUFBNkIsYUFBQSxJQUFBN0IsQ0FBQSxTQUFBeUYsV0FBQSxJQUFBekYsQ0FBQSxTQUFBN0IsTUFBQSxJQUFBNkIsQ0FBQSxTQUFBMkIsVUFBQTtJQUVnQkYsRUFBQSxHQUFBa0UsTUFBQTtNQUFBQyxJQUFBLEVBQ25CLFFBQVFELE1BQU07UUFBQSxLQUNQLE1BQU07VUFBQTtZQUNKM0ksV0FBVyxDQUFDMkUsVUFBVSxDQUFDO1lBQzVCeEQsTUFBTSxDQUFDLENBQUM7WUFDUixNQUFBeUgsSUFBQTtVQUFLO1FBQUEsS0FDRixNQUFNO1VBQUE7WUFDVDdELGlCQUFpQixDQUFDLElBQUksQ0FBQztZQUN2QixNQUFBNkQsSUFBQTtVQUFLO1FBQUEsS0FDRixNQUFNO1VBQUE7WUFDVC9ELGFBQWEsQ0FBQyxDQUFDO1lBQ2YsTUFBQStELElBQUE7VUFBSztRQUFBLEtBQ0YsU0FBUztVQUFBO1lBQ1pILFdBQVcsQ0FBQyxDQUFDO1VBQUE7TUFFakI7SUFBQyxDQUNGO0lBQUF6RixDQUFBLE9BQUE2QixhQUFBO0lBQUE3QixDQUFBLE9BQUF5RixXQUFBO0lBQUF6RixDQUFBLE9BQUE3QixNQUFBO0lBQUE2QixDQUFBLE9BQUEyQixVQUFBO0lBQUEzQixDQUFBLE9BQUF5QixFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBekIsQ0FBQTtFQUFBO0VBaEJELE1BQUE2RixZQUFBLEdBQXFCcEUsRUFnQnBCO0VBT1UsTUFBQUcsRUFBQSxHQUFBdUMsU0FBUyxHQUFUaEksY0FBeUMsR0FBekNDLFlBQXlDO0VBQUEsSUFBQTRGLEVBQUE7RUFBQSxJQUFBaEMsQ0FBQSxTQUFBNEIsRUFBQTtJQUQ1Q0ksRUFBQSxJQUFDLElBQUksQ0FBTyxLQUFZLENBQVosWUFBWSxDQUNyQixDQUFBSixFQUF3QyxDQUFHLElBQUUsQ0FDaEQsRUFGQyxJQUFJLENBRUU7SUFBQTVCLENBQUEsT0FBQTRCLEVBQUE7SUFBQTVCLENBQUEsT0FBQWdDLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFoQyxDQUFBO0VBQUE7RUFBQSxJQUFBbUMsRUFBQTtFQUFBLElBQUFuQyxDQUFBLFNBQUFpQyxNQUFBLENBQUFDLEdBQUE7SUFDUEMsRUFBQSxJQUFDLElBQUksQ0FBQyxJQUFJLENBQUosS0FBRyxDQUFDLENBQUMsV0FBVyxFQUFyQixJQUFJLENBQXdCO0lBQUFuQyxDQUFBLE9BQUFtQyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBbkMsQ0FBQTtFQUFBO0VBQUEsSUFBQW9DLEVBQUE7RUFBQSxJQUFBcEMsQ0FBQSxTQUFBTyxXQUFBLElBQUFQLENBQUEsU0FBQTBGLFdBQUE7SUFDN0J0RCxFQUFBLElBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FDWCxTQUFJLENBQ0o3QixZQUFVLENBQ1YsU0FBSSxDQUNKbUYsWUFBVSxDQUNiLEVBTEMsSUFBSSxDQUtFO0lBQUExRixDQUFBLE9BQUFPLFdBQUE7SUFBQVAsQ0FBQSxPQUFBMEYsV0FBQTtJQUFBMUYsQ0FBQSxPQUFBb0MsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQXBDLENBQUE7RUFBQTtFQUFBLElBQUF1QyxFQUFBO0VBQUEsSUFBQXZDLENBQUEsU0FBQWdDLEVBQUEsSUFBQWhDLENBQUEsU0FBQW9DLEVBQUE7SUFWVEcsRUFBQSxJQUFDLElBQUksQ0FDSCxDQUFBUCxFQUVNLENBQ04sQ0FBQUcsRUFBNEIsQ0FDNUIsQ0FBQUMsRUFLTSxDQUNSLEVBWEMsSUFBSSxDQVdFO0lBQUFwQyxDQUFBLE9BQUFnQyxFQUFBO0lBQUFoQyxDQUFBLE9BQUFvQyxFQUFBO0lBQUFwQyxDQUFBLE9BQUF1QyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBdkMsQ0FBQTtFQUFBO0VBaUJFLE1BQUF3QyxHQUFBLEdBQUF2RSxPQUFPLENBQUE2RyxjQUFzQixFQUFBWixLQUFBO0VBRXZCLE1BQUF6QixHQUFBLElBQUMsQ0FBQ3hFLE9BQU8sQ0FBQTZHLGNBQWU7RUFBQSxJQUFBbkMsR0FBQTtFQUFBLElBQUEzQyxDQUFBLFNBQUFtRSxTQUFBLElBQUFuRSxDQUFBLFNBQUF3QyxHQUFBLElBQUF4QyxDQUFBLFNBQUF5QyxHQUFBO0lBSHZDRSxHQUFBLElBQUMsYUFBYSxDQUNMLEtBQTZCLENBQTdCLENBQUFILEdBQTRCLENBQUMsQ0FDekIyQixTQUFTLENBQVRBLFVBQVEsQ0FBQyxDQUNQLFdBQXdCLENBQXhCLENBQUExQixHQUF1QixDQUFDLEdBQ3JDO0lBQUF6QyxDQUFBLE9BQUFtRSxTQUFBO0lBQUFuRSxDQUFBLE9BQUF3QyxHQUFBO0lBQUF4QyxDQUFBLE9BQUF5QyxHQUFBO0lBQUF6QyxDQUFBLE9BQUEyQyxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBM0MsQ0FBQTtFQUFBO0VBQUEsSUFBQTRDLEdBQUE7RUFBQSxJQUFBNUMsQ0FBQSxTQUFBL0IsT0FBQTtJQUdPMkUsR0FBQSxHQUFBZ0MsZ0JBQWdCLENBQUMzRyxPQUFPLENBQUM7SUFBQStCLENBQUEsT0FBQS9CLE9BQUE7SUFBQStCLENBQUEsT0FBQTRDLEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUE1QyxDQUFBO0VBQUE7RUFBQSxJQUFBNkMsR0FBQTtFQUFBLElBQUE3QyxDQUFBLFNBQUE0QyxHQUFBO0lBQWhDQyxHQUFBLElBQUMsSUFBSSxDQUFFLENBQUFELEdBQXdCLENBQUUsRUFBaEMsSUFBSSxDQUFtQztJQUFBNUMsQ0FBQSxPQUFBNEMsR0FBQTtJQUFBNUMsQ0FBQSxPQUFBNkMsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQTdDLENBQUE7RUFBQTtFQUFBLElBQUE4QyxHQUFBO0VBQUEsSUFBQTlDLENBQUEsU0FBQTJCLFVBQUE7SUFFdENtQixHQUFBLElBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBRW5CLFdBQVMsQ0FBRSxFQUExQixJQUFJLENBQTZCO0lBQUEzQixDQUFBLE9BQUEyQixVQUFBO0lBQUEzQixDQUFBLE9BQUE4QyxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBOUMsQ0FBQTtFQUFBO0VBQUEsSUFBQStDLEdBQUE7RUFBQSxJQUFBL0MsQ0FBQSxTQUFBMkIsVUFBQSxJQUFBM0IsQ0FBQSxTQUFBOEMsR0FBQTtJQURwQ0MsR0FBQSxJQUFDLElBQUksQ0FBTXBCLEdBQVUsQ0FBVkEsV0FBUyxDQUFDLENBQ25CLENBQUFtQixHQUFpQyxDQUNuQyxFQUZDLElBQUksQ0FFRTtJQUFBOUMsQ0FBQSxPQUFBMkIsVUFBQTtJQUFBM0IsQ0FBQSxPQUFBOEMsR0FBQTtJQUFBOUMsQ0FBQSxPQUFBK0MsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQS9DLENBQUE7RUFBQTtFQUFBLElBQUFnRCxHQUFBO0VBQUEsSUFBQWhELENBQUEsU0FBQTZDLEdBQUEsSUFBQTdDLENBQUEsU0FBQStDLEdBQUE7SUFKVEMsR0FBQSxJQUFDLEdBQUcsQ0FBZSxhQUFRLENBQVIsUUFBUSxDQUN6QixDQUFBSCxHQUF1QyxDQUN2QyxDQUFBRSxHQUVNLENBQ1IsRUFMQyxHQUFHLENBS0U7SUFBQS9DLENBQUEsT0FBQTZDLEdBQUE7SUFBQTdDLENBQUEsT0FBQStDLEdBQUE7SUFBQS9DLENBQUEsT0FBQWdELEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUFoRCxDQUFBO0VBQUE7RUFBQSxJQUFBaUQsR0FBQTtFQUFBLElBQUFqRCxDQUFBLFNBQUE2RixZQUFBLElBQUE3RixDQUFBLFNBQUEzQixPQUFBO0lBRU40RSxHQUFBLElBQUMsTUFBTSxDQUFVNUUsT0FBTyxDQUFQQSxRQUFNLENBQUMsQ0FBWXdILFFBQVksQ0FBWkEsYUFBVyxDQUFDLEdBQUk7SUFBQTdGLENBQUEsT0FBQTZGLFlBQUE7SUFBQTdGLENBQUEsT0FBQTNCLE9BQUE7SUFBQTJCLENBQUEsT0FBQWlELEdBQUE7RUFBQTtJQUFBQSxHQUFBLEdBQUFqRCxDQUFBO0VBQUE7RUFBQSxJQUFBa0QsR0FBQTtFQUFBLElBQUFsRCxDQUFBLFNBQUEyQyxHQUFBLElBQUEzQyxDQUFBLFNBQUFnRCxHQUFBLElBQUFoRCxDQUFBLFNBQUFpRCxHQUFBO0lBZHREQyxHQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQU0sR0FBQyxDQUFELEdBQUMsQ0FDaEMsQ0FBQVAsR0FJQyxDQUVELENBQUFLLEdBS0ssQ0FFTCxDQUFBQyxHQUFtRCxDQUNyRCxFQWZDLEdBQUcsQ0FlRTtJQUFBakQsQ0FBQSxPQUFBMkMsR0FBQTtJQUFBM0MsQ0FBQSxPQUFBZ0QsR0FBQTtJQUFBaEQsQ0FBQSxPQUFBaUQsR0FBQTtJQUFBakQsQ0FBQSxPQUFBa0QsR0FBQTtFQUFBO0lBQUFBLEdBQUEsR0FBQWxELENBQUE7RUFBQTtFQUFBLElBQUFtRCxHQUFBO0VBQUEsSUFBQW5ELENBQUEsU0FBQTZCLGFBQUEsSUFBQTdCLENBQUEsU0FBQWtELEdBQUEsSUFBQWxELENBQUEsU0FBQXVDLEVBQUE7SUEzQ1JZLEdBQUEsSUFBQyxNQUFNLENBRUgsS0FXTyxDQVhQLENBQUFaLEVBV00sQ0FBQyxDQUVDVixRQUFhLENBQWJBLGNBQVksQ0FBQyxDQUNqQixLQUFZLENBQVosWUFBWSxDQUNOLFVBUVQsQ0FSUyxDQUFBaUUsS0FRVixDQUFDLENBR0gsQ0FBQTVDLEdBZUssQ0FDUCxFQTVDQyxNQUFNLENBNENFO0lBQUFsRCxDQUFBLE9BQUE2QixhQUFBO0lBQUE3QixDQUFBLE9BQUFrRCxHQUFBO0lBQUFsRCxDQUFBLE9BQUF1QyxFQUFBO0lBQUF2QyxDQUFBLE9BQUFtRCxHQUFBO0VBQUE7SUFBQUEsR0FBQSxHQUFBbkQsQ0FBQTtFQUFBO0VBQUEsT0E1Q1RtRCxHQTRDUztBQUFBO0FBeEliLFNBQUEyQyxNQUFBQyxTQUFBO0VBQUEsT0E4R1FBLFNBQVMsQ0FBQUMsT0FPUixHQU5DLENBQUMsSUFBSSxDQUFDLE1BQU8sQ0FBQUQsU0FBUyxDQUFBRSxPQUFPLENBQUUsY0FBYyxFQUE1QyxJQUFJLENBTU4sR0FKQyxDQUFDLE1BQU0sQ0FDTCxDQUFDLG9CQUFvQixDQUFVLFFBQU8sQ0FBUCxPQUFPLENBQVEsTUFBUSxDQUFSLFFBQVEsR0FDdEQsQ0FBQyxvQkFBb0IsQ0FBVSxRQUFLLENBQUwsS0FBSyxDQUFRLE1BQVMsQ0FBVCxTQUFTLEdBQ3ZELEVBSEMsTUFBTSxDQUlSO0FBQUE7QUF1QlQsT0FBTyxTQUFTQyx5QkFBeUJBLENBQUM7RUFDeENqSSxPQUFPO0VBQ1BDLGNBQWM7RUFDZEMsTUFBTTtFQUNOSSxNQUFNO0VBQ05DO0FBQ0ssQ0FBTixFQUFFUixLQUFLLENBQUMsRUFBRXBDLEtBQUssQ0FBQ3VLLFNBQVMsQ0FBQztFQUN6QixNQUFNLENBQUNDLGFBQWEsRUFBRUMsZ0JBQWdCLENBQUMsR0FBR3ZLLFFBQVEsQ0FBQyxLQUFLLENBQUM7RUFDekQsTUFBTSxDQUFDd0ssYUFBYSxFQUFFQyxnQkFBZ0IsQ0FBQyxHQUFHekssUUFBUSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUM7O0VBRXZFO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQSxNQUFNMEssWUFBWSxHQUFHM0ssT0FBTyxDQUFDLE1BQU07SUFDakMsSUFBSW9DLE9BQU8sQ0FBQ3dJLFdBQVcsSUFBSXhJLE9BQU8sQ0FBQ3lJLGNBQWMsRUFBRSxPQUFPLEVBQUU7SUFDNUQsT0FBT3BKLGlCQUFpQixDQUFDRixrQkFBa0IsQ0FBQ2EsT0FBTyxDQUFDNkMsR0FBRyxJQUFJL0UsVUFBVSxFQUFFLENBQUMsQ0FBQyxDQUN0RTRLLE1BQU0sQ0FBQ0MsQ0FBQyxJQUFJQSxDQUFDLENBQUM3RixJQUFJLEtBQUssVUFBVSxDQUFDLENBQ2xDOEYsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDO0VBQ2QsQ0FBQyxFQUFFLENBQUM1SSxPQUFPLENBQUMsQ0FBQztFQUViLElBQUlBLE9BQU8sQ0FBQ3dJLFdBQVcsRUFBRTtJQUN2QixPQUNFLENBQUMsc0JBQXNCLENBQ3JCLE9BQU8sQ0FBQyxDQUFDeEksT0FBTyxDQUFDLENBQ2pCLE1BQU0sQ0FBQyxDQUFDRSxNQUFNLENBQUMsQ0FDZixNQUFNLENBQUMsQ0FBQ0ksTUFBTSxDQUFDLENBQ2YsTUFBTSxDQUFDLENBQUNDLE1BQU0sQ0FBQyxHQUNmO0VBRU47O0VBRUE7RUFDQTtFQUNBLElBQUlQLE9BQU8sQ0FBQ3lJLGNBQWMsRUFBRTtJQUMxQixPQUNFLENBQUMsbUJBQW1CLENBQ2xCLE9BQU8sQ0FBQyxDQUFDekksT0FBTyxDQUFDLENBQ2pCLE1BQU0sQ0FBQyxDQUFDRSxNQUFNLENBQUMsQ0FDZixNQUFNLENBQUMsQ0FBQ0ksTUFBTSxDQUFDLENBQ2YsTUFBTSxDQUFDLENBQUNDLE1BQU0sQ0FBQyxHQUNmO0VBRU47RUFFQSxNQUFNaUgsV0FBVyxHQUFHQSxDQUFBLEtBQ2xCdEgsTUFBTSxDQUFDLGtDQUFrQyxFQUFFO0lBQUVHLE9BQU8sRUFBRTtFQUFTLENBQUMsQ0FBQzs7RUFFbkU7RUFDQTtFQUNBLE1BQU13SSxhQUFhLEdBQUdBLENBQUNDLENBQUMsRUFBRXpLLGFBQWEsS0FBSztJQUMxQyxJQUFJeUssQ0FBQyxDQUFDQyxHQUFHLEtBQUssR0FBRyxFQUFFO01BQ2pCRCxDQUFDLENBQUNFLGNBQWMsQ0FBQyxDQUFDO01BQ2xCOUksTUFBTSxDQUFDLGtDQUFrQyxFQUFFO1FBQUVHLE9BQU8sRUFBRTtNQUFTLENBQUMsQ0FBQztJQUNuRSxDQUFDLE1BQU0sSUFBSXlJLENBQUMsQ0FBQ0MsR0FBRyxLQUFLLE1BQU0sSUFBSXpJLE1BQU0sRUFBRTtNQUNyQ3dJLENBQUMsQ0FBQ0UsY0FBYyxDQUFDLENBQUM7TUFDbEIxSSxNQUFNLENBQUMsQ0FBQztJQUNWLENBQUMsTUFBTSxJQUFJd0ksQ0FBQyxDQUFDQyxHQUFHLEtBQUssR0FBRyxJQUFJLENBQUNaLGFBQWEsRUFBRTtNQUMxQ1csQ0FBQyxDQUFDRSxjQUFjLENBQUMsQ0FBQztNQUNsQixLQUFLQyxjQUFjLENBQUMsQ0FBQztJQUN2QixDQUFDLE1BQU0sSUFBSUgsQ0FBQyxDQUFDQyxHQUFHLEtBQUssUUFBUSxFQUFFO01BQzdCRCxDQUFDLENBQUNFLGNBQWMsQ0FBQyxDQUFDO01BQ2xCeEIsV0FBVyxDQUFDLENBQUM7SUFDZjtFQUNGLENBQUM7O0VBRUQ7RUFDQSxlQUFleUIsY0FBY0EsQ0FBQSxDQUFFLEVBQUVDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM3Q2QsZ0JBQWdCLENBQUMsSUFBSSxDQUFDO0lBQ3RCRSxnQkFBZ0IsQ0FBQyxJQUFJLENBQUM7SUFFdEIsSUFBSTtNQUNGLE1BQU0vSSx5QkFBeUIsQ0FBQ1MsT0FBTyxDQUFDeUQsU0FBUyxDQUFDO0lBQ3BELENBQUMsQ0FBQyxPQUFPMEYsR0FBRyxFQUFFO01BQ1piLGdCQUFnQixDQUFDdEosWUFBWSxDQUFDbUssR0FBRyxDQUFDLENBQUM7SUFDckMsQ0FBQyxTQUFTO01BQ1JmLGdCQUFnQixDQUFDLEtBQUssQ0FBQztJQUN6QjtFQUNGOztFQUVBO0VBQ0EsTUFBTWdCLFlBQVksR0FBR2xLLGVBQWUsQ0FBQ2MsT0FBTyxDQUFDcUosS0FBSyxFQUFFLEVBQUUsQ0FBQzs7RUFFdkQ7RUFDQSxNQUFNQyxhQUFhLEdBQ2pCdEosT0FBTyxDQUFDa0MsTUFBTSxLQUFLLFNBQVMsR0FBRyxVQUFVLEdBQUdsQyxPQUFPLENBQUNrQyxNQUFNO0VBRTVELE9BQ0UsQ0FBQyxHQUFHLENBQ0YsYUFBYSxDQUFDLFFBQVEsQ0FDdEIsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQ1osU0FBUyxDQUNULFNBQVMsQ0FBQyxDQUFDMkcsYUFBYSxDQUFDO0FBRS9CLE1BQU0sQ0FBQyxNQUFNLENBQ0wsS0FBSyxDQUFDLHdCQUF3QixDQUM5QixRQUFRLENBQUMsQ0FBQ3JCLFdBQVcsQ0FBQyxDQUN0QixLQUFLLENBQUMsWUFBWSxDQUNsQixVQUFVLENBQUMsQ0FBQ00sU0FBUyxJQUNuQkEsU0FBUyxDQUFDQyxPQUFPLEdBQ2YsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDRCxTQUFTLENBQUNFLE9BQU8sQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLEdBRXBELENBQUMsTUFBTTtBQUNuQixjQUFjLENBQUMxSCxNQUFNLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEdBQUc7QUFDL0UsY0FBYyxDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxpQkFBaUIsQ0FBQyxNQUFNLENBQUMsT0FBTztBQUM3RSxjQUFjLENBQUMsQ0FBQzZILGFBQWEsSUFDYixDQUFDLG9CQUFvQixDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLFVBQVUsR0FDckQ7QUFDZixZQUFZLEVBQUUsTUFBTSxDQUVaLENBQUM7QUFFVCxRQUFRLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxRQUFRO0FBQ25DLFVBQVUsQ0FBQyxJQUFJO0FBQ2YsWUFBWSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRztBQUN6QyxZQUFZLENBQUNtQixhQUFhLEtBQUssU0FBUyxJQUFJQSxhQUFhLEtBQUssVUFBVSxHQUMxRCxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUNBLGFBQWEsQ0FBQyxFQUFFLElBQUksQ0FBQyxHQUM3Q0EsYUFBYSxLQUFLLFdBQVcsR0FDL0IsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxDQUFDQSxhQUFhLENBQUMsRUFBRSxJQUFJLENBQUMsR0FFNUMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDQSxhQUFhLENBQUMsRUFBRSxJQUFJLENBQzFDO0FBQ2IsVUFBVSxFQUFFLElBQUk7QUFDaEIsVUFBVSxDQUFDLElBQUk7QUFDZixZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHO0FBQzFDLFlBQVksQ0FBQ3JLLGNBQWMsQ0FDYixDQUFDZSxPQUFPLENBQUN3QyxPQUFPLElBQUkrRyxJQUFJLENBQUNDLEdBQUcsQ0FBQyxDQUFDLElBQUl4SixPQUFPLENBQUN1QyxTQUM1QyxDQUFDO0FBQ2IsVUFBVSxFQUFFLElBQUk7QUFDaEIsVUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsY0FBYztBQUNuQyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQzZHLFlBQVk7QUFDbEQsVUFBVSxFQUFFLElBQUk7QUFDaEIsVUFBVSxDQUFDLElBQUk7QUFDZixZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHO0FBQzNDLFlBQVksQ0FBQyxxQkFBcUIsQ0FBQyxPQUFPLENBQUMsQ0FBQ3BKLE9BQU8sQ0FBQztBQUNwRCxVQUFVLEVBQUUsSUFBSTtBQUNoQixVQUFVLENBQUMsSUFBSTtBQUNmLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUc7QUFDOUMsWUFBWSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQ3RCLHVCQUF1QixDQUFDc0IsT0FBTyxDQUFDeUQsU0FBUyxDQUFDLENBQUM7QUFDbEUsY0FBYyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQy9FLHVCQUF1QixDQUFDc0IsT0FBTyxDQUFDeUQsU0FBUyxDQUFDLENBQUMsRUFBRSxJQUFJO0FBQy9FLFlBQVksRUFBRSxJQUFJO0FBQ2xCLFVBQVUsRUFBRSxJQUFJO0FBQ2hCLFFBQVEsRUFBRSxHQUFHO0FBQ2I7QUFDQSxRQUFRLENBQUMscUNBQXFDO0FBQzlDLFFBQVEsQ0FBQ3pELE9BQU8sQ0FBQzZDLEdBQUcsQ0FBQzRHLE1BQU0sR0FBRyxDQUFDLElBQ3JCLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ25ELFlBQVksQ0FBQyxJQUFJO0FBQ2pCLGNBQWMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUM7QUFDL0MsWUFBWSxFQUFFLElBQUk7QUFDbEIsWUFBWSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxRQUFRO0FBQ3RFLGNBQWMsQ0FBQ2xCLFlBQVksQ0FBQ2hDLEdBQUcsQ0FBQyxDQUFDM0QsR0FBRyxFQUFFNkQsQ0FBQyxLQUN2QixDQUFDLE9BQU8sQ0FDTixHQUFHLENBQUMsQ0FBQ0EsQ0FBQyxDQUFDLENBQ1AsT0FBTyxDQUFDLENBQUM3RCxHQUFHLENBQUMsQ0FDYixPQUFPLENBQUMsQ0FBQ3hELGFBQWEsQ0FBQyxDQUN2QixTQUFTLENBQUMsQ0FBQ3FILENBQUMsR0FBRyxDQUFDLENBQUMsQ0FDakIsS0FBSyxDQUFDLENBQUN4RyxjQUFjLENBQUNHLE9BQU8sQ0FBQ3NKLEtBQUssQ0FBQyxDQUNwQyxRQUFRLENBQUMsQ0FBQ3pKLGNBQWMsQ0FBQ0csT0FBTyxDQUFDdUosUUFBUSxDQUFDLENBQzFDLE9BQU8sQ0FBQyxDQUFDMUosY0FBYyxDQUFDRyxPQUFPLENBQUN3SixPQUFPLENBQUMsQ0FDeEMsb0JBQW9CLENBQUMsQ0FBQyxJQUFJQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQ2hDLDBCQUEwQixDQUFDLENBQUMsRUFBRSxDQUFDLENBQy9CLGFBQWEsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUNyQixhQUFhLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FDckIsS0FBSyxDQUFDLFdBQVcsQ0FDakIsZ0JBQWdCLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FDeEIsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLEdBRWxCLENBQUM7QUFDaEIsWUFBWSxFQUFFLEdBQUc7QUFDakIsWUFBWSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDOUIsY0FBYyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTTtBQUNuQyw2QkFBNkIsQ0FBQ3RCLFlBQVksQ0FBQ2tCLE1BQU0sQ0FBQyxJQUFJLENBQUN6SixPQUFPLENBQUM2QyxHQUFHLENBQUM0RyxNQUFNLENBQUMsQ0FBQyxHQUFHO0FBQzlFO0FBQ0EsY0FBYyxFQUFFLElBQUk7QUFDcEIsWUFBWSxFQUFFLEdBQUc7QUFDakIsVUFBVSxFQUFFLEdBQUcsQ0FDTjtBQUNUO0FBQ0EsUUFBUSxDQUFDLDRCQUE0QjtBQUNyQyxRQUFRLENBQUNwQixhQUFhLElBQ1osQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzVCLFlBQVksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQ0EsYUFBYSxDQUFDLEVBQUUsSUFBSTtBQUN0RSxVQUFVLEVBQUUsR0FBRyxDQUNOO0FBQ1Q7QUFDQSxRQUFRLENBQUMsd0JBQXdCO0FBQ2pDLFFBQVEsQ0FBQ0YsYUFBYSxJQUNaLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsdUJBQXVCLEVBQUUsSUFBSSxDQUN2RDtBQUNULE1BQU0sRUFBRSxNQUFNO0FBQ2QsSUFBSSxFQUFFLEdBQUcsQ0FBQztBQUVWIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/tasks/RemoteSessionProgress.tsx b/components/tasks/RemoteSessionProgress.tsx new file mode 100644 index 0000000..4e7a329 --- /dev/null +++ b/components/tasks/RemoteSessionProgress.tsx @@ -0,0 +1,243 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useRef } from 'react'; +import type { RemoteAgentTaskState } from 'src/tasks/RemoteAgentTask/RemoteAgentTask.js'; +import type { DeepImmutable } from 'src/types/utils.js'; +import { DIAMOND_FILLED, DIAMOND_OPEN } from '../../constants/figures.js'; +import { useSettings } from '../../hooks/useSettings.js'; +import { Text, useAnimationFrame } from '../../ink.js'; +import { count } from '../../utils/array.js'; +import { getRainbowColor } from '../../utils/thinking.js'; +const TICK_MS = 80; +type ReviewStage = NonNullable['stage']>; + +/** + * Stage-appropriate counts line for a running review. Shared between the + * one-line pill (below) and RemoteSessionDetailDialog's reviewCountsLine so + * the two can't drift — they have historically disagreed on whether to show + * refuted counts and what to call the synthesizing stage. + * + * Canonical behavior: word labels (not ✓/✗), hide refuted when 0, "deduping" + * for the synthesizing stage (matches STAGE_LABELS in the detail dialog). + */ +export function formatReviewStageCounts(stage: ReviewStage | undefined, found: number, verified: number, refuted: number): string { + // Pre-stage orchestrator images don't write the stage field. + if (!stage) return `${found} found · ${verified} verified`; + if (stage === 'synthesizing') { + const parts = [`${verified} verified`]; + if (refuted > 0) parts.push(`${refuted} refuted`); + parts.push('deduping'); + return parts.join(' · '); + } + if (stage === 'verifying') { + const parts = [`${found} found`, `${verified} verified`]; + if (refuted > 0) parts.push(`${refuted} refuted`); + return parts.join(' · '); + } + // stage === 'finding' + return found > 0 ? `${found} found` : 'finding'; +} + +// Per-character rainbow gradient, same treatment as the ultraplan keyword. +// The phase offset lets the gradient cycle — so the colors sweep along the +// text on each animation frame instead of being static. +function RainbowText(t0) { + const $ = _c(5); + const { + text, + phase: t1 + } = t0; + const phase = t1 === undefined ? 0 : t1; + let t2; + if ($[0] !== text) { + t2 = [...text]; + $[0] = text; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== phase || $[3] !== t2) { + t3 = <>{t2.map((ch, i) => {ch})}; + $[2] = phase; + $[3] = t2; + $[4] = t3; + } else { + t3 = $[4]; + } + return t3; +} + +// Smooth-tick a count toward target, +1 per frame. Same pattern as the +// token counter in SpinnerAnimationRow — the ref survives re-renders and +// the animation clock drives the tick. Target jumps (2→5) display as +// 2→3→4→5 instead of snapping. When `snap` is set (reduced motion, or +// the clock is frozen), bypass the tick and jump straight to target — +// otherwise a frozen `time` would leave the ref stuck at its init value. +function useSmoothCount(target: number, time: number, snap: boolean): number { + const displayed = useRef(target); + const lastTick = useRef(time); + if (snap || target < displayed.current) { + displayed.current = target; + } else if (target > displayed.current && time !== lastTick.current) { + displayed.current += 1; + lastTick.current = time; + } + return displayed.current; +} +function ReviewRainbowLine(t0) { + const $ = _c(15); + const { + session + } = t0; + const settings = useSettings(); + const reducedMotion = settings.prefersReducedMotion ?? false; + const p = session.reviewProgress; + const running = session.status === "running"; + const [, time] = useAnimationFrame(running && !reducedMotion ? TICK_MS : null); + const targetFound = p?.bugsFound ?? 0; + const targetVerified = p?.bugsVerified ?? 0; + const targetRefuted = p?.bugsRefuted ?? 0; + const snap = reducedMotion || !running; + const found = useSmoothCount(targetFound, time, snap); + const verified = useSmoothCount(targetVerified, time, snap); + const refuted = useSmoothCount(targetRefuted, time, snap); + const phase = Math.floor(time / (TICK_MS * 3)) % 7; + if (session.status === "completed") { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = <>{DIAMOND_FILLED} ready · shift+↓ to view; + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; + } + if (session.status === "failed") { + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = <>{DIAMOND_FILLED} {" \xB7 "}error; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + let t1; + if ($[2] !== found || $[3] !== p || $[4] !== refuted || $[5] !== verified) { + t1 = !p ? "setting up" : formatReviewStageCounts(p.stage, found, verified, refuted); + $[2] = found; + $[3] = p; + $[4] = refuted; + $[5] = verified; + $[6] = t1; + } else { + t1 = $[6]; + } + const tail = t1; + let t2; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t2 = {DIAMOND_OPEN} ; + $[7] = t2; + } else { + t2 = $[7]; + } + const t3 = running ? phase : 0; + let t4; + if ($[8] !== t3) { + t4 = ; + $[8] = t3; + $[9] = t4; + } else { + t4 = $[9]; + } + let t5; + if ($[10] !== tail) { + t5 = · {tail}; + $[10] = tail; + $[11] = t5; + } else { + t5 = $[11]; + } + let t6; + if ($[12] !== t4 || $[13] !== t5) { + t6 = <>{t2}{t4}{t5}; + $[12] = t4; + $[13] = t5; + $[14] = t6; + } else { + t6 = $[14]; + } + return t6; +} +export function RemoteSessionProgress(t0) { + const $ = _c(11); + const { + session + } = t0; + if (session.isRemoteReview) { + let t1; + if ($[0] !== session) { + t1 = ; + $[0] = session; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + if (session.status === "completed") { + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = done; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; + } + if (session.status === "failed") { + let t1; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t1 = error; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; + } + if (!session.todoList.length) { + let t1; + if ($[4] !== session.status) { + t1 = {session.status}…; + $[4] = session.status; + $[5] = t1; + } else { + t1 = $[5]; + } + return t1; + } + let t1; + if ($[6] !== session.todoList) { + t1 = count(session.todoList, _temp); + $[6] = session.todoList; + $[7] = t1; + } else { + t1 = $[7]; + } + const completed = t1; + const total = session.todoList.length; + let t2; + if ($[8] !== completed || $[9] !== total) { + t2 = {completed}/{total}; + $[8] = completed; + $[9] = total; + $[10] = t2; + } else { + t2 = $[10]; + } + return t2; +} +function _temp(_) { + return _.status === "completed"; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZVJlZiIsIlJlbW90ZUFnZW50VGFza1N0YXRlIiwiRGVlcEltbXV0YWJsZSIsIkRJQU1PTkRfRklMTEVEIiwiRElBTU9ORF9PUEVOIiwidXNlU2V0dGluZ3MiLCJUZXh0IiwidXNlQW5pbWF0aW9uRnJhbWUiLCJjb3VudCIsImdldFJhaW5ib3dDb2xvciIsIlRJQ0tfTVMiLCJSZXZpZXdTdGFnZSIsIk5vbk51bGxhYmxlIiwiZm9ybWF0UmV2aWV3U3RhZ2VDb3VudHMiLCJzdGFnZSIsImZvdW5kIiwidmVyaWZpZWQiLCJyZWZ1dGVkIiwicGFydHMiLCJwdXNoIiwiam9pbiIsIlJhaW5ib3dUZXh0IiwidDAiLCIkIiwiX2MiLCJ0ZXh0IiwicGhhc2UiLCJ0MSIsInVuZGVmaW5lZCIsInQyIiwidDMiLCJtYXAiLCJjaCIsImkiLCJ1c2VTbW9vdGhDb3VudCIsInRhcmdldCIsInRpbWUiLCJzbmFwIiwiZGlzcGxheWVkIiwibGFzdFRpY2siLCJjdXJyZW50IiwiUmV2aWV3UmFpbmJvd0xpbmUiLCJzZXNzaW9uIiwic2V0dGluZ3MiLCJyZWR1Y2VkTW90aW9uIiwicHJlZmVyc1JlZHVjZWRNb3Rpb24iLCJwIiwicmV2aWV3UHJvZ3Jlc3MiLCJydW5uaW5nIiwic3RhdHVzIiwidGFyZ2V0Rm91bmQiLCJidWdzRm91bmQiLCJ0YXJnZXRWZXJpZmllZCIsImJ1Z3NWZXJpZmllZCIsInRhcmdldFJlZnV0ZWQiLCJidWdzUmVmdXRlZCIsIk1hdGgiLCJmbG9vciIsIlN5bWJvbCIsImZvciIsInRhaWwiLCJ0NCIsInQ1IiwidDYiLCJSZW1vdGVTZXNzaW9uUHJvZ3Jlc3MiLCJpc1JlbW90ZVJldmlldyIsInRvZG9MaXN0IiwibGVuZ3RoIiwiX3RlbXAiLCJjb21wbGV0ZWQiLCJ0b3RhbCIsIl8iXSwic291cmNlcyI6WyJSZW1vdGVTZXNzaW9uUHJvZ3Jlc3MudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgUmVtb3RlQWdlbnRUYXNrU3RhdGUgfSBmcm9tICdzcmMvdGFza3MvUmVtb3RlQWdlbnRUYXNrL1JlbW90ZUFnZW50VGFzay5qcydcbmltcG9ydCB0eXBlIHsgRGVlcEltbXV0YWJsZSB9IGZyb20gJ3NyYy90eXBlcy91dGlscy5qcydcbmltcG9ydCB7IERJQU1PTkRfRklMTEVELCBESUFNT05EX09QRU4gfSBmcm9tICcuLi8uLi9jb25zdGFudHMvZmlndXJlcy5qcydcbmltcG9ydCB7IHVzZVNldHRpbmdzIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlU2V0dGluZ3MuanMnXG5pbXBvcnQgeyBUZXh0LCB1c2VBbmltYXRpb25GcmFtZSB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGNvdW50IH0gZnJvbSAnLi4vLi4vdXRpbHMvYXJyYXkuanMnXG5pbXBvcnQgeyBnZXRSYWluYm93Q29sb3IgfSBmcm9tICcuLi8uLi91dGlscy90aGlua2luZy5qcydcblxuY29uc3QgVElDS19NUyA9IDgwXG5cbnR5cGUgUmV2aWV3U3RhZ2UgPSBOb25OdWxsYWJsZTxcbiAgTm9uTnVsbGFibGU8UmVtb3RlQWdlbnRUYXNrU3RhdGVbJ3Jldmlld1Byb2dyZXNzJ10+WydzdGFnZSddXG4+XG5cbi8qKlxuICogU3RhZ2UtYXBwcm9wcmlhdGUgY291bnRzIGxpbmUgZm9yIGEgcnVubmluZyByZXZpZXcuIFNoYXJlZCBiZXR3ZWVuIHRoZVxuICogb25lLWxpbmUgcGlsbCAoYmVsb3cpIGFuZCBSZW1vdGVTZXNzaW9uRGV0YWlsRGlhbG9nJ3MgcmV2aWV3Q291bnRzTGluZSBzb1xuICogdGhlIHR3byBjYW4ndCBkcmlmdCDigJQgdGhleSBoYXZlIGhpc3RvcmljYWxseSBkaXNhZ3JlZWQgb24gd2hldGhlciB0byBzaG93XG4gKiByZWZ1dGVkIGNvdW50cyBhbmQgd2hhdCB0byBjYWxsIHRoZSBzeW50aGVzaXppbmcgc3RhZ2UuXG4gKlxuICogQ2Fub25pY2FsIGJlaGF2aW9yOiB3b3JkIGxhYmVscyAobm90IOKcky/inJcpLCBoaWRlIHJlZnV0ZWQgd2hlbiAwLCBcImRlZHVwaW5nXCJcbiAqIGZvciB0aGUgc3ludGhlc2l6aW5nIHN0YWdlIChtYXRjaGVzIFNUQUdFX0xBQkVMUyBpbiB0aGUgZGV0YWlsIGRpYWxvZykuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmb3JtYXRSZXZpZXdTdGFnZUNvdW50cyhcbiAgc3RhZ2U6IFJldmlld1N0YWdlIHwgdW5kZWZpbmVkLFxuICBmb3VuZDogbnVtYmVyLFxuICB2ZXJpZmllZDogbnVtYmVyLFxuICByZWZ1dGVkOiBudW1iZXIsXG4pOiBzdHJpbmcge1xuICAvLyBQcmUtc3RhZ2Ugb3JjaGVzdHJhdG9yIGltYWdlcyBkb24ndCB3cml0ZSB0aGUgc3RhZ2UgZmllbGQuXG4gIGlmICghc3RhZ2UpIHJldHVybiBgJHtmb3VuZH0gZm91bmQgwrcgJHt2ZXJpZmllZH0gdmVyaWZpZWRgXG4gIGlmIChzdGFnZSA9PT0gJ3N5bnRoZXNpemluZycpIHtcbiAgICBjb25zdCBwYXJ0cyA9IFtgJHt2ZXJpZmllZH0gdmVyaWZpZWRgXVxuICAgIGlmIChyZWZ1dGVkID4gMCkgcGFydHMucHVzaChgJHtyZWZ1dGVkfSByZWZ1dGVkYClcbiAgICBwYXJ0cy5wdXNoKCdkZWR1cGluZycpXG4gICAgcmV0dXJuIHBhcnRzLmpvaW4oJyDCtyAnKVxuICB9XG4gIGlmIChzdGFnZSA9PT0gJ3ZlcmlmeWluZycpIHtcbiAgICBjb25zdCBwYXJ0cyA9IFtgJHtmb3VuZH0gZm91bmRgLCBgJHt2ZXJpZmllZH0gdmVyaWZpZWRgXVxuICAgIGlmIChyZWZ1dGVkID4gMCkgcGFydHMucHVzaChgJHtyZWZ1dGVkfSByZWZ1dGVkYClcbiAgICByZXR1cm4gcGFydHMuam9pbignIMK3ICcpXG4gIH1cbiAgLy8gc3RhZ2UgPT09ICdmaW5kaW5nJ1xuICByZXR1cm4gZm91bmQgPiAwID8gYCR7Zm91bmR9IGZvdW5kYCA6ICdmaW5kaW5nJ1xufVxuXG4vLyBQZXItY2hhcmFjdGVyIHJhaW5ib3cgZ3JhZGllbnQsIHNhbWUgdHJlYXRtZW50IGFzIHRoZSB1bHRyYXBsYW4ga2V5d29yZC5cbi8vIFRoZSBwaGFzZSBvZmZzZXQgbGV0cyB0aGUgZ3JhZGllbnQgY3ljbGUg4oCUIHNvIHRoZSBjb2xvcnMgc3dlZXAgYWxvbmcgdGhlXG4vLyB0ZXh0IG9uIGVhY2ggYW5pbWF0aW9uIGZyYW1lIGluc3RlYWQgb2YgYmVpbmcgc3RhdGljLlxuZnVuY3Rpb24gUmFpbmJvd1RleHQoe1xuICB0ZXh0LFxuICBwaGFzZSA9IDAsXG59OiB7XG4gIHRleHQ6IHN0cmluZ1xuICBwaGFzZT86IG51bWJlclxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIHtbLi4udGV4dF0ubWFwKChjaCwgaSkgPT4gKFxuICAgICAgICA8VGV4dCBrZXk9e2l9IGNvbG9yPXtnZXRSYWluYm93Q29sb3IoaSArIHBoYXNlKX0+XG4gICAgICAgICAge2NofVxuICAgICAgICA8L1RleHQ+XG4gICAgICApKX1cbiAgICA8Lz5cbiAgKVxufVxuXG4vLyBTbW9vdGgtdGljayBhIGNvdW50IHRvd2FyZCB0YXJnZXQsICsxIHBlciBmcmFtZS4gU2FtZSBwYXR0ZXJuIGFzIHRoZVxuLy8gdG9rZW4gY291bnRlciBpbiBTcGlubmVyQW5pbWF0aW9uUm93IOKAlCB0aGUgcmVmIHN1cnZpdmVzIHJlLXJlbmRlcnMgYW5kXG4vLyB0aGUgYW5pbWF0aW9uIGNsb2NrIGRyaXZlcyB0aGUgdGljay4gVGFyZ2V0IGp1bXBzICgy4oaSNSkgZGlzcGxheSBhc1xuLy8gMuKGkjPihpI04oaSNSBpbnN0ZWFkIG9mIHNuYXBwaW5nLiBXaGVuIGBzbmFwYCBpcyBzZXQgKHJlZHVjZWQgbW90aW9uLCBvclxuLy8gdGhlIGNsb2NrIGlzIGZyb3plbiksIGJ5cGFzcyB0aGUgdGljayBhbmQganVtcCBzdHJhaWdodCB0byB0YXJnZXQg4oCUXG4vLyBvdGhlcndpc2UgYSBmcm96ZW4gYHRpbWVgIHdvdWxkIGxlYXZlIHRoZSByZWYgc3R1Y2sgYXQgaXRzIGluaXQgdmFsdWUuXG5mdW5jdGlvbiB1c2VTbW9vdGhDb3VudCh0YXJnZXQ6IG51bWJlciwgdGltZTogbnVtYmVyLCBzbmFwOiBib29sZWFuKTogbnVtYmVyIHtcbiAgY29uc3QgZGlzcGxheWVkID0gdXNlUmVmKHRhcmdldClcbiAgY29uc3QgbGFzdFRpY2sgPSB1c2VSZWYodGltZSlcbiAgaWYgKHNuYXAgfHwgdGFyZ2V0IDwgZGlzcGxheWVkLmN1cnJlbnQpIHtcbiAgICBkaXNwbGF5ZWQuY3VycmVudCA9IHRhcmdldFxuICB9IGVsc2UgaWYgKHRhcmdldCA+IGRpc3BsYXllZC5jdXJyZW50ICYmIHRpbWUgIT09IGxhc3RUaWNrLmN1cnJlbnQpIHtcbiAgICBkaXNwbGF5ZWQuY3VycmVudCArPSAxXG4gICAgbGFzdFRpY2suY3VycmVudCA9IHRpbWVcbiAgfVxuICByZXR1cm4gZGlzcGxheWVkLmN1cnJlbnRcbn1cblxuZnVuY3Rpb24gUmV2aWV3UmFpbmJvd0xpbmUoe1xuICBzZXNzaW9uLFxufToge1xuICBzZXNzaW9uOiBEZWVwSW1tdXRhYmxlPFJlbW90ZUFnZW50VGFza1N0YXRlPlxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHNldHRpbmdzID0gdXNlU2V0dGluZ3MoKVxuICBjb25zdCByZWR1Y2VkTW90aW9uID0gc2V0dGluZ3MucHJlZmVyc1JlZHVjZWRNb3Rpb24gPz8gZmFsc2VcbiAgY29uc3QgcCA9IHNlc3Npb24ucmV2aWV3UHJvZ3Jlc3NcbiAgY29uc3QgcnVubmluZyA9IHNlc3Npb24uc3RhdHVzID09PSAncnVubmluZydcbiAgLy8gQW5pbWF0aW9uIGNsb2NrIHJ1bnMgb25seSB3aGlsZSBydW5uaW5nIOKAlCBjb21wbGV0ZWQvZmFpbGVkIGFyZSBzdGF0aWMuXG4gIC8vIERpc2FibGVkIGVudGlyZWx5IHdoZW4gdGhlIHVzZXIgcHJlZmVycyByZWR1Y2VkIG1vdGlvbi5cbiAgLy9cbiAgLy8gVGhlIHJlZiBpcyBpbnRlbnRpb25hbGx5IGRpc2NhcmRlZDogdGhpcyBjb21wb25lbnQgaXMgcmVuZGVyZWQgaW5zaWRlXG4gIC8vIDxUZXh0PiB3cmFwcGVycyAoQmFja2dyb3VuZFRhc2tzRGlhbG9nLCBSZW1vdGVTZXNzaW9uRGV0YWlsRGlhbG9nKSwgYW5kXG4gIC8vIEluayBjYW4ndCBuZXN0IDxCb3g+IGluc2lkZSA8VGV4dD4uIERyb3BwaW5nIHRoZSByZWYgbWVhbnNcbiAgLy8gdXNlVGVybWluYWxWaWV3cG9ydCdzIGlzVmlzaWJsZSBzdGF5cyB0cnVlLCBzbyB0aGUgY2xvY2sgdGlja3MgZXZlbiB3aGVuXG4gIC8vIHNjcm9sbGVkIG9mZi1zY3JlZW4g4oCUIGFjY2VwdGFibGUgZm9yIGEgc2luZ2xlIDMwLWNoYXIgbGluZS5cbiAgY29uc3QgWywgdGltZV0gPSB1c2VBbmltYXRpb25GcmFtZShydW5uaW5nICYmICFyZWR1Y2VkTW90aW9uID8gVElDS19NUyA6IG51bGwpXG5cbiAgY29uc3QgdGFyZ2V0Rm91bmQgPSBwPy5idWdzRm91bmQgPz8gMFxuICBjb25zdCB0YXJnZXRWZXJpZmllZCA9IHA/LmJ1Z3NWZXJpZmllZCA/PyAwXG4gIGNvbnN0IHRhcmdldFJlZnV0ZWQgPSBwPy5idWdzUmVmdXRlZCA/PyAwXG4gIC8vIHNuYXAgd2hlbiB0aGUgY2xvY2sgaXNuJ3QgYWR2YW5jaW5nIChyZWR1Y2VkIG1vdGlvbiwgb3Igbm90IHJ1bm5pbmcpIOKAlFxuICAvLyB1c2VBbmltYXRpb25GcmFtZShudWxsKSBmcmVlemVzIGB0aW1lYCBhdCBpdHMgbW91bnQgdmFsdWUsIHdoaWNoIHdvdWxkXG4gIC8vIGxlYXZlIHRoZSB0aWNrLWdhdGUgcGVybWFuZW50bHkgZmFsc2UuXG4gIGNvbnN0IHNuYXAgPSByZWR1Y2VkTW90aW9uIHx8ICFydW5uaW5nXG4gIGNvbnN0IGZvdW5kID0gdXNlU21vb3RoQ291bnQodGFyZ2V0Rm91bmQsIHRpbWUsIHNuYXApXG4gIGNvbnN0IHZlcmlmaWVkID0gdXNlU21vb3RoQ291bnQodGFyZ2V0VmVyaWZpZWQsIHRpbWUsIHNuYXApXG4gIGNvbnN0IHJlZnV0ZWQgPSB1c2VTbW9vdGhDb3VudCh0YXJnZXRSZWZ1dGVkLCB0aW1lLCBzbmFwKVxuXG4gIC8vIFBoYXNlIGFkdmFuY2VzIGV2ZXJ5IDMgdGlja3Mgc28gdGhlIGdyYWRpZW50IHN3ZWVwIGlzIHZpc2libGUgYnV0XG4gIC8vIG5vdCBmcmFudGljLiBNb2R1bG8ga2VlcHMgaXQgaW4gdGhlIDctY29sb3IgY3ljbGUuXG4gIGNvbnN0IHBoYXNlID0gTWF0aC5mbG9vcih0aW1lIC8gKFRJQ0tfTVMgKiAzKSkgJSA3XG5cbiAgLy8g4peHIG9wZW4gZGlhbW9uZCB3aGlsZSBydW5uaW5nICh0ZWFsLCBtYXRjaGVzIGNsb3VkLXNlc3Npb24gYWNjZW50KSwg4peGXG4gIC8vIGZpbGxlZCB3aGVuIHRlcm1pbmFsLiBSYWluYm93IGlzIHNjb3BlZCB0byB0aGUgd29yZCBgdWx0cmFyZXZpZXdgIG9ubHkg4oCUXG4gIC8vIHBlciBkZXNpZ24gZmVlZGJhY2ssIFwidGhlcmUgaXMgYSBsaW1pdCB0byB0aGUgZ2xpdHRlcmluZyByYWluYm93XCIuXG4gIC8vIENvdW50cyBzdGF5IGRpbUNvbG9yLlxuICBpZiAoc2Vzc2lvbi5zdGF0dXMgPT09ICdjb21wbGV0ZWQnKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDw+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiPntESUFNT05EX0ZJTExFRH0gPC9UZXh0PlxuICAgICAgICA8UmFpbmJvd1RleHQgdGV4dD1cInVsdHJhcmV2aWV3XCIgcGhhc2U9ezB9IC8+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPiByZWFkeSDCtyBzaGlmdCvihpMgdG8gdmlldzwvVGV4dD5cbiAgICAgIDwvPlxuICAgIClcbiAgfVxuICBpZiAoc2Vzc2lvbi5zdGF0dXMgPT09ICdmYWlsZWQnKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDw+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiYmFja2dyb3VuZFwiPntESUFNT05EX0ZJTExFRH0gPC9UZXh0PlxuICAgICAgICA8UmFpbmJvd1RleHQgdGV4dD1cInVsdHJhcmV2aWV3XCIgcGhhc2U9ezB9IC8+XG4gICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIiBkaW1Db2xvcj5cbiAgICAgICAgICB7JyDCtyAnfVxuICAgICAgICAgIGVycm9yXG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvPlxuICAgIClcbiAgfVxuXG4gIC8vIFRoZSAhcCBicmFuY2ggKFwic2V0dGluZyB1cFwiKSBjb3ZlcnMgdGhlIHdpbmRvdyBiZWZvcmUgdGhlIG9yY2hlc3RyYXRvclxuICAvLyB3cml0ZXMgaXRzIGZpcnN0IHByb2dyZXNzIHNuYXBzaG90IOKAlCBjb250YWluZXIgYm9vdCArIHJlcG8gY2xvbmUgY2FuXG4gIC8vIHRha2UgMS0zIG1pbiwgZHVyaW5nIHdoaWNoIFwiMCBmb3VuZFwiIGxvb2tlZCBodW5nLlxuICBjb25zdCB0YWlsID0gIXBcbiAgICA/ICdzZXR0aW5nIHVwJ1xuICAgIDogZm9ybWF0UmV2aWV3U3RhZ2VDb3VudHMocC5zdGFnZSwgZm91bmQsIHZlcmlmaWVkLCByZWZ1dGVkKVxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8VGV4dCBjb2xvcj1cImJhY2tncm91bmRcIj57RElBTU9ORF9PUEVOfSA8L1RleHQ+XG4gICAgICA8UmFpbmJvd1RleHQgdGV4dD1cInVsdHJhcmV2aWV3XCIgcGhhc2U9e3J1bm5pbmcgPyBwaGFzZSA6IDB9IC8+XG4gICAgICA8VGV4dCBkaW1Db2xvcj4gwrcge3RhaWx9PC9UZXh0PlxuICAgIDwvPlxuICApXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBSZW1vdGVTZXNzaW9uUHJvZ3Jlc3Moe1xuICBzZXNzaW9uLFxufToge1xuICBzZXNzaW9uOiBEZWVwSW1tdXRhYmxlPFJlbW90ZUFnZW50VGFza1N0YXRlPlxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIExpdGUtcmV2aWV3OiByYWluYm93IGdyYWRpZW50IG92ZXIgdGhlIGZ1bGwgbGluZSwgdWx0cmFwbGFuLXN0eWxlLlxuICAvLyBCYWNrZ3JvdW5kVGFzay50c3ggZGVsZWdhdGVzIHRoZSB3aG9sZSA8VGV4dD4gd3JhcHBlciBoZXJlIHNvIHRoZVxuICAvLyBncmFkaWVudCBzcGFucyB0aGUgdGl0bGUsIG5vdCBqdXN0IHRoZSB0cmFpbGluZyBzdGF0dXMuXG4gIGlmIChzZXNzaW9uLmlzUmVtb3RlUmV2aWV3KSB7XG4gICAgcmV0dXJuIDxSZXZpZXdSYWluYm93TGluZSBzZXNzaW9uPXtzZXNzaW9ufSAvPlxuICB9XG5cbiAgaWYgKHNlc3Npb24uc3RhdHVzID09PSAnY29tcGxldGVkJykge1xuICAgIHJldHVybiAoXG4gICAgICA8VGV4dCBib2xkIGNvbG9yPVwic3VjY2Vzc1wiIGRpbUNvbG9yPlxuICAgICAgICBkb25lXG4gICAgICA8L1RleHQ+XG4gICAgKVxuICB9XG5cbiAgaWYgKHNlc3Npb24uc3RhdHVzID09PSAnZmFpbGVkJykge1xuICAgIHJldHVybiAoXG4gICAgICA8VGV4dCBib2xkIGNvbG9yPVwiZXJyb3JcIiBkaW1Db2xvcj5cbiAgICAgICAgZXJyb3JcbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH1cblxuICBpZiAoIXNlc3Npb24udG9kb0xpc3QubGVuZ3RoKSB7XG4gICAgcmV0dXJuIDxUZXh0IGRpbUNvbG9yPntzZXNzaW9uLnN0YXR1c33igKY8L1RleHQ+XG4gIH1cblxuICBjb25zdCBjb21wbGV0ZWQgPSBjb3VudChzZXNzaW9uLnRvZG9MaXN0LCBfID0+IF8uc3RhdHVzID09PSAnY29tcGxldGVkJylcbiAgY29uc3QgdG90YWwgPSBzZXNzaW9uLnRvZG9MaXN0Lmxlbmd0aFxuICByZXR1cm4gKFxuICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAge2NvbXBsZXRlZH0ve3RvdGFsfVxuICAgIDwvVGV4dD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJQyxNQUFNLFFBQVEsT0FBTztBQUNyQyxjQUFjQyxv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 \ No newline at end of file diff --git a/components/tasks/ShellDetailDialog.tsx b/components/tasks/ShellDetailDialog.tsx new file mode 100644 index 0000000..69130a4 --- /dev/null +++ b/components/tasks/ShellDetailDialog.tsx @@ -0,0 +1,404 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { Suspense, use, useDeferredValue, useEffect, useState } from 'react'; +import type { DeepImmutable } from 'src/types/utils.js'; +import type { CommandResultDisplay } from '../../commands.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +import type { LocalShellTaskState } from '../../tasks/LocalShellTask/guards.js'; +import { formatDuration, formatFileSize, truncateToWidth } from '../../utils/format.js'; +import { tailFile } from '../../utils/fsOperations.js'; +import { getTaskOutputPath } from '../../utils/task/diskOutput.js'; +import { Byline } from '../design-system/Byline.js'; +import { Dialog } from '../design-system/Dialog.js'; +import { KeyboardShortcutHint } from '../design-system/KeyboardShortcutHint.js'; +type Props = { + shell: DeepImmutable; + onDone: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; + onKillShell?: () => void; + onBack?: () => void; +}; +const SHELL_DETAIL_TAIL_BYTES = 8192; +type TaskOutputResult = { + content: string; + bytesTotal: number; +}; + +/** + * Read the tail of the task output file. Only reads the last few KB, + * not the entire file. + */ +async function getTaskOutput(shell: DeepImmutable): Promise { + const path = getTaskOutputPath(shell.id); + try { + const result = await tailFile(path, SHELL_DETAIL_TAIL_BYTES); + return { + content: result.content, + bytesTotal: result.bytesTotal + }; + } catch { + return { + content: '', + bytesTotal: 0 + }; + } +} +export function ShellDetailDialog(t0) { + const $ = _c(57); + const { + shell, + onDone, + onKillShell, + onBack + } = t0; + const { + columns + } = useTerminalSize(); + let t1; + if ($[0] !== shell) { + t1 = () => getTaskOutput(shell); + $[0] = shell; + $[1] = t1; + } else { + t1 = $[1]; + } + const [outputPromise, setOutputPromise] = useState(t1); + const deferredOutputPromise = useDeferredValue(outputPromise); + let t2; + if ($[2] !== shell) { + t2 = () => { + if (shell.status !== "running") { + return; + } + const timer = setInterval(_temp, 1000, setOutputPromise, shell); + return () => clearInterval(timer); + }; + $[2] = shell; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== shell.id || $[5] !== shell.status) { + t3 = [shell.id, shell.status]; + $[4] = shell.id; + $[5] = shell.status; + $[6] = t3; + } else { + t3 = $[6]; + } + useEffect(t2, t3); + let t4; + if ($[7] !== onDone) { + t4 = () => onDone("Shell details dismissed", { + display: "system" + }); + $[7] = onDone; + $[8] = t4; + } else { + t4 = $[8]; + } + const handleClose = t4; + let t5; + if ($[9] !== handleClose) { + t5 = { + "confirm:yes": handleClose + }; + $[9] = handleClose; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] === Symbol.for("react.memo_cache_sentinel")) { + t6 = { + context: "Confirmation" + }; + $[11] = t6; + } else { + t6 = $[11]; + } + useKeybindings(t5, t6); + let t7; + if ($[12] !== onBack || $[13] !== onDone || $[14] !== onKillShell || $[15] !== shell.status) { + t7 = e => { + if (e.key === " ") { + e.preventDefault(); + onDone("Shell details dismissed", { + display: "system" + }); + } else { + if (e.key === "left" && onBack) { + e.preventDefault(); + onBack(); + } else { + if (e.key === "x" && shell.status === "running" && onKillShell) { + e.preventDefault(); + onKillShell(); + } + } + } + }; + $[12] = onBack; + $[13] = onDone; + $[14] = onKillShell; + $[15] = shell.status; + $[16] = t7; + } else { + t7 = $[16]; + } + const handleKeyDown = t7; + const isMonitor = shell.kind === "monitor"; + let t8; + if ($[17] !== shell.command) { + t8 = truncateToWidth(shell.command, 280); + $[17] = shell.command; + $[18] = t8; + } else { + t8 = $[18]; + } + const displayCommand = t8; + const t9 = isMonitor ? "Monitor details" : "Shell details"; + let t10; + if ($[19] !== onBack || $[20] !== onKillShell || $[21] !== shell.status) { + t10 = exitState => exitState.pending ? Press {exitState.keyName} again to exit : {onBack && }{shell.status === "running" && onKillShell && }; + $[19] = onBack; + $[20] = onKillShell; + $[21] = shell.status; + $[22] = t10; + } else { + t10 = $[22]; + } + let t11; + if ($[23] === Symbol.for("react.memo_cache_sentinel")) { + t11 = Status:; + $[23] = t11; + } else { + t11 = $[23]; + } + let t12; + if ($[24] !== shell.result || $[25] !== shell.status) { + t12 = {t11}{" "}{shell.status === "running" ? {shell.status}{shell.result?.code !== undefined && ` (exit code: ${shell.result.code})`} : shell.status === "completed" ? {shell.status}{shell.result?.code !== undefined && ` (exit code: ${shell.result.code})`} : {shell.status}{shell.result?.code !== undefined && ` (exit code: ${shell.result.code})`}}; + $[24] = shell.result; + $[25] = shell.status; + $[26] = t12; + } else { + t12 = $[26]; + } + let t13; + if ($[27] === Symbol.for("react.memo_cache_sentinel")) { + t13 = Runtime:; + $[27] = t13; + } else { + t13 = $[27]; + } + let t14; + if ($[28] !== shell.endTime) { + t14 = shell.endTime ?? Date.now(); + $[28] = shell.endTime; + $[29] = t14; + } else { + t14 = $[29]; + } + const t15 = t14 - shell.startTime; + let t16; + if ($[30] !== t15) { + t16 = formatDuration(t15); + $[30] = t15; + $[31] = t16; + } else { + t16 = $[31]; + } + let t17; + if ($[32] !== t16) { + t17 = {t13}{" "}{t16}; + $[32] = t16; + $[33] = t17; + } else { + t17 = $[33]; + } + const t18 = isMonitor ? "Script:" : "Command:"; + let t19; + if ($[34] !== t18) { + t19 = {t18}; + $[34] = t18; + $[35] = t19; + } else { + t19 = $[35]; + } + let t20; + if ($[36] !== displayCommand || $[37] !== t19) { + t20 = {t19}{" "}{displayCommand}; + $[36] = displayCommand; + $[37] = t19; + $[38] = t20; + } else { + t20 = $[38]; + } + let t21; + if ($[39] !== t12 || $[40] !== t17 || $[41] !== t20) { + t21 = {t12}{t17}{t20}; + $[39] = t12; + $[40] = t17; + $[41] = t20; + $[42] = t21; + } else { + t21 = $[42]; + } + let t22; + if ($[43] === Symbol.for("react.memo_cache_sentinel")) { + t22 = Output:; + $[43] = t22; + } else { + t22 = $[43]; + } + let t23; + if ($[44] === Symbol.for("react.memo_cache_sentinel")) { + t23 = Loading output…; + $[44] = t23; + } else { + t23 = $[44]; + } + let t24; + if ($[45] !== columns || $[46] !== deferredOutputPromise) { + t24 = {t22}; + $[45] = columns; + $[46] = deferredOutputPromise; + $[47] = t24; + } else { + t24 = $[47]; + } + let t25; + if ($[48] !== handleClose || $[49] !== t10 || $[50] !== t21 || $[51] !== t24 || $[52] !== t9) { + t25 = {t21}{t24}; + $[48] = handleClose; + $[49] = t10; + $[50] = t21; + $[51] = t24; + $[52] = t9; + $[53] = t25; + } else { + t25 = $[53]; + } + let t26; + if ($[54] !== handleKeyDown || $[55] !== t25) { + t26 = {t25}; + $[54] = handleKeyDown; + $[55] = t25; + $[56] = t26; + } else { + t26 = $[56]; + } + return t26; +} +function _temp(setOutputPromise_0, shell_0) { + return setOutputPromise_0(getTaskOutput(shell_0)); +} +type ShellOutputContentProps = { + outputPromise: Promise; + columns: number; +}; +function ShellOutputContent(t0) { + const $ = _c(19); + const { + outputPromise, + columns + } = t0; + const { + content, + bytesTotal + } = use(outputPromise); + if (!content) { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = No output available; + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; + } + let isIncomplete; + let rendered; + if ($[1] !== bytesTotal || $[2] !== content) { + const starts = []; + let pos = content.length; + for (let i = 0; i < 10 && pos > 0; i++) { + const prev = content.lastIndexOf("\n", pos - 1); + starts.push(prev + 1); + pos = prev; + } + starts.reverse(); + isIncomplete = bytesTotal > content.length; + rendered = []; + for (let i_0 = 0; i_0 < starts.length; i_0++) { + const start = starts[i_0]; + const end = i_0 < starts.length - 1 ? starts[i_0 + 1] - 1 : content.length; + const line = content.slice(start, end); + if (line) { + rendered.push(line); + } + } + $[1] = bytesTotal; + $[2] = content; + $[3] = isIncomplete; + $[4] = rendered; + } else { + isIncomplete = $[3]; + rendered = $[4]; + } + const t1 = columns - 6; + let t2; + if ($[5] !== rendered) { + t2 = rendered.map(_temp2); + $[5] = rendered; + $[6] = t2; + } else { + t2 = $[6]; + } + let t3; + if ($[7] !== t1 || $[8] !== t2) { + t3 = {t2}; + $[7] = t1; + $[8] = t2; + $[9] = t3; + } else { + t3 = $[9]; + } + const t4 = `Showing ${rendered.length} lines`; + let t5; + if ($[10] !== bytesTotal || $[11] !== isIncomplete) { + t5 = isIncomplete ? ` of ${formatFileSize(bytesTotal)}` : ""; + $[10] = bytesTotal; + $[11] = isIncomplete; + $[12] = t5; + } else { + t5 = $[12]; + } + let t6; + if ($[13] !== t4 || $[14] !== t5) { + t6 = {t4}{t5}; + $[13] = t4; + $[14] = t5; + $[15] = t6; + } else { + t6 = $[15]; + } + let t7; + if ($[16] !== t3 || $[17] !== t6) { + t7 = <>{t3}{t6}; + $[16] = t3; + $[17] = t6; + $[18] = t7; + } else { + t7 = $[18]; + } + return t7; +} +function _temp2(line_0, i_1) { + return {line_0}; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlN1c3BlbnNlIiwidXNlIiwidXNlRGVmZXJyZWRWYWx1ZSIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiRGVlcEltbXV0YWJsZSIsIkNvbW1hbmRSZXN1bHREaXNwbGF5IiwidXNlVGVybWluYWxTaXplIiwiS2V5Ym9hcmRFdmVudCIsIkJveCIsIlRleHQiLCJ1c2VLZXliaW5kaW5ncyIsIkxvY2FsU2hlbGxUYXNrU3RhdGUiLCJmb3JtYXREdXJhdGlvbiIsImZvcm1hdEZpbGVTaXplIiwidHJ1bmNhdGVUb1dpZHRoIiwidGFpbEZpbGUiLCJnZXRUYXNrT3V0cHV0UGF0aCIsIkJ5bGluZSIsIkRpYWxvZyIsIktleWJvYXJkU2hvcnRjdXRIaW50IiwiUHJvcHMiLCJzaGVsbCIsIm9uRG9uZSIsInJlc3VsdCIsIm9wdGlvbnMiLCJkaXNwbGF5Iiwib25LaWxsU2hlbGwiLCJvbkJhY2siLCJTSEVMTF9ERVRBSUxfVEFJTF9CWVRFUyIsIlRhc2tPdXRwdXRSZXN1bHQiLCJjb250ZW50IiwiYnl0ZXNUb3RhbCIsImdldFRhc2tPdXRwdXQiLCJQcm9taXNlIiwicGF0aCIsImlkIiwiU2hlbGxEZXRhaWxEaWFsb2ciLCJ0MCIsIiQiLCJfYyIsImNvbHVtbnMiLCJ0MSIsIm91dHB1dFByb21pc2UiLCJzZXRPdXRwdXRQcm9taXNlIiwiZGVmZXJyZWRPdXRwdXRQcm9taXNlIiwidDIiLCJzdGF0dXMiLCJ0aW1lciIsInNldEludGVydmFsIiwiX3RlbXAiLCJjbGVhckludGVydmFsIiwidDMiLCJ0NCIsImhhbmRsZUNsb3NlIiwidDUiLCJ0NiIsIlN5bWJvbCIsImZvciIsImNvbnRleHQiLCJ0NyIsImUiLCJrZXkiLCJwcmV2ZW50RGVmYXVsdCIsImhhbmRsZUtleURvd24iLCJpc01vbml0b3IiLCJraW5kIiwidDgiLCJjb21tYW5kIiwiZGlzcGxheUNvbW1hbmQiLCJ0OSIsInQxMCIsImV4aXRTdGF0ZSIsInBlbmRpbmciLCJrZXlOYW1lIiwidDExIiwidDEyIiwiY29kZSIsInVuZGVmaW5lZCIsInQxMyIsInQxNCIsImVuZFRpbWUiLCJEYXRlIiwibm93IiwidDE1Iiwic3RhcnRUaW1lIiwidDE2IiwidDE3IiwidDE4IiwidDE5IiwidDIwIiwidDIxIiwidDIyIiwidDIzIiwidDI0IiwidDI1IiwidDI2Iiwic2V0T3V0cHV0UHJvbWlzZV8wIiwic2hlbGxfMCIsIlNoZWxsT3V0cHV0Q29udGVudFByb3BzIiwiU2hlbGxPdXRwdXRDb250ZW50IiwiaXNJbmNvbXBsZXRlIiwicmVuZGVyZWQiLCJzdGFydHMiLCJwb3MiLCJsZW5ndGgiLCJpIiwicHJldiIsImxhc3RJbmRleE9mIiwicHVzaCIsInJldmVyc2UiLCJpXzAiLCJzdGFydCIsImVuZCIsImxpbmUiLCJzbGljZSIsIm1hcCIsIl90ZW1wMiIsImxpbmVfMCIsImlfMSJdLCJzb3VyY2VzIjpbIlNoZWxsRGV0YWlsRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHtcbiAgU3VzcGVuc2UsXG4gIHVzZSxcbiAgdXNlRGVmZXJyZWRWYWx1ZSxcbiAgdXNlRWZmZWN0LFxuICB1c2VTdGF0ZSxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IERlZXBJbW11dGFibGUgfSBmcm9tICdzcmMvdHlwZXMvdXRpbHMuanMnXG5pbXBvcnQgdHlwZSB7IENvbW1hbmRSZXN1bHREaXNwbGF5IH0gZnJvbSAnLi4vLi4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgdHlwZSB7IEtleWJvYXJkRXZlbnQgfSBmcm9tICcuLi8uLi9pbmsvZXZlbnRzL2tleWJvYXJkLWV2ZW50LmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZ3MgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbFNoZWxsVGFza1N0YXRlIH0gZnJvbSAnLi4vLi4vdGFza3MvTG9jYWxTaGVsbFRhc2svZ3VhcmRzLmpzJ1xuaW1wb3J0IHtcbiAgZm9ybWF0RHVyYXRpb24sXG4gIGZvcm1hdEZpbGVTaXplLFxuICB0cnVuY2F0ZVRvV2lkdGgsXG59IGZyb20gJy4uLy4uL3V0aWxzL2Zvcm1hdC5qcydcbmltcG9ydCB7IHRhaWxGaWxlIH0gZnJvbSAnLi4vLi4vdXRpbHMvZnNPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHsgZ2V0VGFza091dHB1dFBhdGggfSBmcm9tICcuLi8uLi91dGlscy90YXNrL2Rpc2tPdXRwdXQuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgS2V5Ym9hcmRTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0tleWJvYXJkU2hvcnRjdXRIaW50LmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBzaGVsbDogRGVlcEltbXV0YWJsZTxMb2NhbFNoZWxsVGFza1N0YXRlPlxuICBvbkRvbmU6IChcbiAgICByZXN1bHQ/OiBzdHJpbmcsXG4gICAgb3B0aW9ucz86IHsgZGlzcGxheT86IENvbW1hbmRSZXN1bHREaXNwbGF5IH0sXG4gICkgPT4gdm9pZFxuICBvbktpbGxTaGVsbD86ICgpID0+IHZvaWRcbiAgb25CYWNrPzogKCkgPT4gdm9pZFxufVxuXG5jb25zdCBTSEVMTF9ERVRBSUxfVEFJTF9CWVRFUyA9IDgxOTJcblxudHlwZSBUYXNrT3V0cHV0UmVzdWx0ID0ge1xuICBjb250ZW50OiBzdHJpbmdcbiAgYnl0ZXNUb3RhbDogbnVtYmVyXG59XG5cbi8qKlxuICogUmVhZCB0aGUgdGFpbCBvZiB0aGUgdGFzayBvdXRwdXQgZmlsZS4gT25seSByZWFkcyB0aGUgbGFzdCBmZXcgS0IsXG4gKiBub3QgdGhlIGVudGlyZSBmaWxlLlxuICovXG5hc3luYyBmdW5jdGlvbiBnZXRUYXNrT3V0cHV0KFxuICBzaGVsbDogRGVlcEltbXV0YWJsZTxMb2NhbFNoZWxsVGFza1N0YXRlPixcbik6IFByb21pc2U8VGFza091dHB1dFJlc3VsdD4ge1xuICBjb25zdCBwYXRoID0gZ2V0VGFza091dHB1dFBhdGgoc2hlbGwuaWQpXG4gIHRyeSB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGFpbEZpbGUocGF0aCwgU0hFTExfREVUQUlMX1RBSUxfQllURVMpXG4gICAgcmV0dXJuIHsgY29udGVudDogcmVzdWx0LmNvbnRlbnQsIGJ5dGVzVG90YWw6IHJlc3VsdC5ieXRlc1RvdGFsIH1cbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIHsgY29udGVudDogJycsIGJ5dGVzVG90YWw6IDAgfVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTaGVsbERldGFpbERpYWxvZyh7XG4gIHNoZWxsLFxuICBvbkRvbmUsXG4gIG9uS2lsbFNoZWxsLFxuICBvbkJhY2ssXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHsgY29sdW1ucyB9ID0gdXNlVGVybWluYWxTaXplKClcblxuICAvLyBQcm9taXNlIGNyZWF0ZWQgaW4gaW5pdGlhbGl6ZXIgKG5vdCBkdXJpbmcgcmVuZGVyKS4gRm9yIHJ1bm5pbmcgc2hlbGxzLFxuICAvLyB0aGUgZWZmZWN0IHRpbWVyIHJlcGxhY2VzIGl0IHBlcmlvZGljYWxseSB0byBwaWNrIHVwIG5ldyBvdXRwdXQuXG4gIC8vIHVzZURlZmVycmVkVmFsdWUga2VlcHMgc2hvd2luZyB0aGUgcHJldmlvdXMgb3V0cHV0IHdoaWxlIHRoZSBuZXcgcHJvbWlzZVxuICAvLyByZXNvbHZlcywgcHJldmVudGluZyB0aGUgU3VzcGVuc2UgZmFsbGJhY2sgZnJvbSBmbGlja2VyaW5nLlxuICBjb25zdCBbb3V0cHV0UHJvbWlzZSwgc2V0T3V0cHV0UHJvbWlzZV0gPSB1c2VTdGF0ZTxQcm9taXNlPFRhc2tPdXRwdXRSZXN1bHQ+PihcbiAgICAoKSA9PiBnZXRUYXNrT3V0cHV0KHNoZWxsKSxcbiAgKVxuICBjb25zdCBkZWZlcnJlZE91dHB1dFByb21pc2UgPSB1c2VEZWZlcnJlZFZhbHVlKG91dHB1dFByb21pc2UpXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoc2hlbGwuc3RhdHVzICE9PSAncnVubmluZycpIHtcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBjb25zdCB0aW1lciA9IHNldEludGVydmFsKFxuICAgICAgKHNldE91dHB1dFByb21pc2UsIHNoZWxsKSA9PiBzZXRPdXRwdXRQcm9taXNlKGdldFRhc2tPdXRwdXQoc2hlbGwpKSxcbiAgICAgIDEwMDAsXG4gICAgICBzZXRPdXRwdXRQcm9taXNlLFxuICAgICAgc2hlbGwsXG4gICAgKVxuICAgIHJldHVybiAoKSA9PiBjbGVhckludGVydmFsKHRpbWVyKVxuICB9LCBbc2hlbGwuaWQsIHNoZWxsLnN0YXR1c10pXG5cbiAgLy8gSGFuZGxlIHN0YW5kYXJkIGNsb3NlIGFjdGlvblxuICBjb25zdCBoYW5kbGVDbG9zZSA9ICgpID0+XG4gICAgb25Eb25lKCdTaGVsbCBkZXRhaWxzIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcblxuICAvLyBIYW5kbGUgYWRkaXRpb25hbCBjbG9zZSBhY3Rpb25zIGJleW9uZCBEaWFsb2cncyBidWlsdC1pbiBFc2MgaGFuZGxlclxuICB1c2VLZXliaW5kaW5ncyhcbiAgICB7XG4gICAgICAnY29uZmlybTp5ZXMnOiBoYW5kbGVDbG9zZSxcbiAgICB9LFxuICAgIHsgY29udGV4dDogJ0NvbmZpcm1hdGlvbicgfSxcbiAgKVxuXG4gIC8vIEhhbmRsZSBkaWFsb2ctc3BlY2lmaWMga2V5c1xuICBjb25zdCBoYW5kbGVLZXlEb3duID0gKGU6IEtleWJvYXJkRXZlbnQpID0+IHtcbiAgICBpZiAoZS5rZXkgPT09ICcgJykge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBvbkRvbmUoJ1NoZWxsIGRldGFpbHMgZGlzbWlzc2VkJywgeyBkaXNwbGF5OiAnc3lzdGVtJyB9KVxuICAgIH0gZWxzZSBpZiAoZS5rZXkgPT09ICdsZWZ0JyAmJiBvbkJhY2spIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25CYWNrKClcbiAgICB9IGVsc2UgaWYgKGUua2V5ID09PSAneCcgJiYgc2hlbGwuc3RhdHVzID09PSAncnVubmluZycgJiYgb25LaWxsU2hlbGwpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgb25LaWxsU2hlbGwoKVxuICAgIH1cbiAgfVxuXG4gIC8vIFRydW5jYXRlIGNvbW1hbmQgaWYgdG9vIGxvbmcgKGZvciBkaXNwbGF5IHB1cnBvc2VzKVxuICBjb25zdCBpc01vbml0b3IgPSBzaGVsbC5raW5kID09PSAnbW9uaXRvcidcbiAgY29uc3QgZGlzcGxheUNvbW1hbmQgPSB0cnVuY2F0ZVRvV2lkdGgoc2hlbGwuY29tbWFuZCwgMjgwKVxuXG4gIHJldHVybiAoXG4gICAgPEJveFxuICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICB0YWJJbmRleD17MH1cbiAgICAgIGF1dG9Gb2N1c1xuICAgICAgb25LZXlEb3duPXtoYW5kbGVLZXlEb3dufVxuICAgID5cbiAgICAgIDxEaWFsb2dcbiAgICAgICAgdGl0bGU9e2lzTW9uaXRvciA/ICdNb25pdG9yIGRldGFpbHMnIDogJ1NoZWxsIGRldGFpbHMnfVxuICAgICAgICBvbkNhbmNlbD17aGFuZGxlQ2xvc2V9XG4gICAgICAgIGNvbG9yPVwiYmFja2dyb3VuZFwiXG4gICAgICAgIGlucHV0R3VpZGU9e2V4aXRTdGF0ZSA9PlxuICAgICAgICAgIGV4aXRTdGF0ZS5wZW5kaW5nID8gKFxuICAgICAgICAgICAgPFRleHQ+UHJlc3Mge2V4aXRTdGF0ZS5rZXlOYW1lfSBhZ2FpbiB0byBleGl0PC9UZXh0PlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICA8QnlsaW5lPlxuICAgICAgICAgICAgICB7b25CYWNrICYmIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cIuKGkFwiIGFjdGlvbj1cImdvIGJhY2tcIiAvPn1cbiAgICAgICAgICAgICAgPEtleWJvYXJkU2hvcnRjdXRIaW50IHNob3J0Y3V0PVwiRXNjL0VudGVyL1NwYWNlXCIgYWN0aW9uPVwiY2xvc2VcIiAvPlxuICAgICAgICAgICAgICB7c2hlbGwuc3RhdHVzID09PSAncnVubmluZycgJiYgb25LaWxsU2hlbGwgJiYgKFxuICAgICAgICAgICAgICAgIDxLZXlib2FyZFNob3J0Y3V0SGludCBzaG9ydGN1dD1cInhcIiBhY3Rpb249XCJzdG9wXCIgLz5cbiAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIDwvQnlsaW5lPlxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgPlxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+U3RhdHVzOjwvVGV4dD57JyAnfVxuICAgICAgICAgICAge3NoZWxsLnN0YXR1cyA9PT0gJ3J1bm5pbmcnID8gKFxuICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cImJhY2tncm91bmRcIj5cbiAgICAgICAgICAgICAgICB7c2hlbGwuc3RhdHVzfVxuICAgICAgICAgICAgICAgIHtzaGVsbC5yZXN1bHQ/LmNvZGUgIT09IHVuZGVmaW5lZCAmJlxuICAgICAgICAgICAgICAgICAgYCAoZXhpdCBjb2RlOiAke3NoZWxsLnJlc3VsdC5jb2RlfSlgfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICApIDogc2hlbGwuc3RhdHVzID09PSAnY29tcGxldGVkJyA/IChcbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+XG4gICAgICAgICAgICAgICAge3NoZWxsLnN0YXR1c31cbiAgICAgICAgICAgICAgICB7c2hlbGwucmVzdWx0Py5jb2RlICE9PSB1bmRlZmluZWQgJiZcbiAgICAgICAgICAgICAgICAgIGAgKGV4aXQgY29kZTogJHtzaGVsbC5yZXN1bHQuY29kZX0pYH1cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPlxuICAgICAgICAgICAgICAgIHtzaGVsbC5zdGF0dXN9XG4gICAgICAgICAgICAgICAge3NoZWxsLnJlc3VsdD8uY29kZSAhPT0gdW5kZWZpbmVkICYmXG4gICAgICAgICAgICAgICAgICBgIChleGl0IGNvZGU6ICR7c2hlbGwucmVzdWx0LmNvZGV9KWB9XG4gICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgYm9sZD5SdW50aW1lOjwvVGV4dD57JyAnfVxuICAgICAgICAgICAge2Zvcm1hdER1cmF0aW9uKChzaGVsbC5lbmRUaW1lID8/IERhdGUubm93KCkpIC0gc2hlbGwuc3RhcnRUaW1lKX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgd3JhcD1cIndyYXBcIj5cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+e2lzTW9uaXRvciA/ICdTY3JpcHQ6JyA6ICdDb21tYW5kOid9PC9UZXh0PnsnICd9XG4gICAgICAgICAgICB7ZGlzcGxheUNvbW1hbmR9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cblxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBib2xkPk91dHB1dDo8L1RleHQ+XG4gICAgICAgICAgPFN1c3BlbnNlIGZhbGxiYWNrPXs8VGV4dCBkaW1Db2xvcj5Mb2FkaW5nIG91dHB1dOKApjwvVGV4dD59PlxuICAgICAgICAgICAgPFNoZWxsT3V0cHV0Q29udGVudFxuICAgICAgICAgICAgICBvdXRwdXRQcm9taXNlPXtkZWZlcnJlZE91dHB1dFByb21pc2V9XG4gICAgICAgICAgICAgIGNvbHVtbnM9e2NvbHVtbnN9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgIDwvU3VzcGVuc2U+XG4gICAgICAgIDwvQm94PlxuICAgICAgPC9EaWFsb2c+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxudHlwZSBTaGVsbE91dHB1dENvbnRlbnRQcm9wcyA9IHtcbiAgb3V0cHV0UHJvbWlzZTogUHJvbWlzZTxUYXNrT3V0cHV0UmVzdWx0PlxuICBjb2x1bW5zOiBudW1iZXJcbn1cblxuZnVuY3Rpb24gU2hlbGxPdXRwdXRDb250ZW50KHtcbiAgb3V0cHV0UHJvbWlzZSxcbiAgY29sdW1ucyxcbn06IFNoZWxsT3V0cHV0Q29udGVudFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBjb250ZW50LCBieXRlc1RvdGFsIH0gPSB1c2Uob3V0cHV0UHJvbWlzZSlcblxuICBpZiAoIWNvbnRlbnQpIHtcbiAgICByZXR1cm4gPFRleHQgZGltQ29sb3I+Tm8gb3V0cHV0IGF2YWlsYWJsZTwvVGV4dD5cbiAgfVxuXG4gIC8vIEZpbmQgbGFzdCAxMCBsaW5lIGJvdW5kYXJpZXMgdmlhIGxhc3RJbmRleE9mXG4gIGNvbnN0IHN0YXJ0czogbnVtYmVyW10gPSBbXVxuICBsZXQgcG9zID0gY29udGVudC5sZW5ndGhcbiAgZm9yIChsZXQgaSA9IDA7IGkgPCAxMCAmJiBwb3MgPiAwOyBpKyspIHtcbiAgICBjb25zdCBwcmV2ID0gY29udGVudC5sYXN0SW5kZXhPZignXFxuJywgcG9zIC0gMSlcbiAgICBzdGFydHMucHVzaChwcmV2ICsgMSlcbiAgICBwb3MgPSBwcmV2XG4gIH1cbiAgc3RhcnRzLnJldmVyc2UoKVxuICBjb25zdCBpc0luY29tcGxldGUgPSBieXRlc1RvdGFsID4gY29udGVudC5sZW5ndGhcblxuICAvLyBCdWlsZCBsaW5lcywgc2tpcCBlbXB0eSB0cmFpbGluZy9sZWFkaW5nIHNlZ21lbnRzXG4gIGNvbnN0IHJlbmRlcmVkOiBzdHJpbmdbXSA9IFtdXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgc3RhcnRzLmxlbmd0aDsgaSsrKSB7XG4gICAgY29uc3Qgc3RhcnQgPSBzdGFydHNbaV0hXG4gICAgY29uc3QgZW5kID0gaSA8IHN0YXJ0cy5sZW5ndGggLSAxID8gc3RhcnRzW2kgKyAxXSEgLSAxIDogY29udGVudC5sZW5ndGhcbiAgICBjb25zdCBsaW5lID0gY29udGVudC5zbGljZShzdGFydCwgZW5kKVxuICAgIGlmIChsaW5lKSByZW5kZXJlZC5wdXNoKGxpbmUpXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8Qm94XG4gICAgICAgIGJvcmRlclN0eWxlPVwicm91bmRcIlxuICAgICAgICBwYWRkaW5nWD17MX1cbiAgICAgICAgZmxleERpcmVjdGlvbj1cImNvbHVtblwiXG4gICAgICAgIGhlaWdodD17MTJ9XG4gICAgICAgIG1heFdpZHRoPXtjb2x1bW5zIC0gNn1cbiAgICAgID5cbiAgICAgICAge3JlbmRlcmVkLm1hcCgobGluZSwgaSkgPT4gKFxuICAgICAgICAgIDxUZXh0IGtleT17aX0gd3JhcD1cInRydW5jYXRlLWVuZFwiPlxuICAgICAgICAgICAge2xpbmV9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApKX1cbiAgICAgIDwvQm94PlxuICAgICAgPFRleHQgZGltQ29sb3IgaXRhbGljPlxuICAgICAgICB7YFNob3dpbmcgJHtyZW5kZXJlZC5sZW5ndGh9IGxpbmVzYH1cbiAgICAgICAge2lzSW5jb21wbGV0ZSA/IGAgb2YgJHtmb3JtYXRGaWxlU2l6ZShieXRlc1RvdGFsKX1gIDogJyd9XG4gICAgICA8L1RleHQ+XG4gICAgPC8+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFDVkMsUUFBUSxFQUNSQyxHQUFHLEVBQ0hDLGdCQUFnQixFQUNoQkMsU0FBUyxFQUNUQyxRQUFRLFFBQ0gsT0FBTztBQUNkLGNBQWNDLGFBQWEsUUFBUSxvQkFBb0I7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 \ No newline at end of file diff --git a/components/tasks/ShellProgress.tsx b/components/tasks/ShellProgress.tsx new file mode 100644 index 0000000..02bd020 --- /dev/null +++ b/components/tasks/ShellProgress.tsx @@ -0,0 +1,87 @@ +import { c as _c } from "react/compiler-runtime"; +import type { ReactNode } from 'react'; +import React from 'react'; +import { Text } from 'src/ink.js'; +import type { TaskStatus } from 'src/Task.js'; +import type { LocalShellTaskState } from 'src/tasks/LocalShellTask/guards.js'; +import type { DeepImmutable } from 'src/types/utils.js'; +type TaskStatusTextProps = { + status: TaskStatus; + label?: string; + suffix?: string; +}; +export function TaskStatusText(t0) { + const $ = _c(4); + const { + status, + label, + suffix + } = t0; + const displayLabel = label ?? status; + const color = status === "completed" ? "success" : status === "failed" ? "error" : status === "killed" ? "warning" : undefined; + let t1; + if ($[0] !== color || $[1] !== displayLabel || $[2] !== suffix) { + t1 = ({displayLabel}{suffix}); + $[0] = color; + $[1] = displayLabel; + $[2] = suffix; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; +} +export function ShellProgress(t0) { + const $ = _c(4); + const { + shell + } = t0; + switch (shell.status) { + case "completed": + { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; + } + case "failed": + { + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + case "killed": + { + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; + } + case "running": + case "pending": + { + let t1; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[3] = t1; + } else { + t1 = $[3]; + } + return t1; + } + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdE5vZGUiLCJSZWFjdCIsIlRleHQiLCJUYXNrU3RhdHVzIiwiTG9jYWxTaGVsbFRhc2tTdGF0ZSIsIkRlZXBJbW11dGFibGUiLCJUYXNrU3RhdHVzVGV4dFByb3BzIiwic3RhdHVzIiwibGFiZWwiLCJzdWZmaXgiLCJUYXNrU3RhdHVzVGV4dCIsInQwIiwiJCIsIl9jIiwiZGlzcGxheUxhYmVsIiwiY29sb3IiLCJ1bmRlZmluZWQiLCJ0MSIsIlNoZWxsUHJvZ3Jlc3MiLCJzaGVsbCIsIlN5bWJvbCIsImZvciJdLCJzb3VyY2VzIjpbIlNoZWxsUHJvZ3Jlc3MudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnc3JjL2luay5qcydcbmltcG9ydCB0eXBlIHsgVGFza1N0YXR1cyB9IGZyb20gJ3NyYy9UYXNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBMb2NhbFNoZWxsVGFza1N0YXRlIH0gZnJvbSAnc3JjL3Rhc2tzL0xvY2FsU2hlbGxUYXNrL2d1YXJkcy5qcydcbmltcG9ydCB0eXBlIHsgRGVlcEltbXV0YWJsZSB9IGZyb20gJ3NyYy90eXBlcy91dGlscy5qcydcblxudHlwZSBUYXNrU3RhdHVzVGV4dFByb3BzID0ge1xuICBzdGF0dXM6IFRhc2tTdGF0dXNcbiAgbGFiZWw/OiBzdHJpbmdcbiAgc3VmZml4Pzogc3RyaW5nXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBUYXNrU3RhdHVzVGV4dCh7XG4gIHN0YXR1cyxcbiAgbGFiZWwsXG4gIHN1ZmZpeCxcbn06IFRhc2tTdGF0dXNUZXh0UHJvcHMpOiBSZWFjdE5vZGUge1xuICBjb25zdCBkaXNwbGF5TGFiZWwgPSBsYWJlbCA/PyBzdGF0dXNcbiAgY29uc3QgY29sb3IgPVxuICAgIHN0YXR1cyA9PT0gJ2NvbXBsZXRlZCdcbiAgICAgID8gJ3N1Y2Nlc3MnXG4gICAgICA6IHN0YXR1cyA9PT0gJ2ZhaWxlZCdcbiAgICAgICAgPyAnZXJyb3InXG4gICAgICAgIDogc3RhdHVzID09PSAna2lsbGVkJ1xuICAgICAgICAgID8gJ3dhcm5pbmcnXG4gICAgICAgICAgOiB1bmRlZmluZWRcbiAgcmV0dXJuIChcbiAgICA8VGV4dCBjb2xvcj17Y29sb3J9IGRpbUNvbG9yPlxuICAgICAgKHtkaXNwbGF5TGFiZWx9XG4gICAgICB7c3VmZml4fSlcbiAgICA8L1RleHQ+XG4gIClcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIFNoZWxsUHJvZ3Jlc3Moe1xuICBzaGVsbCxcbn06IHtcbiAgc2hlbGw6IERlZXBJbW11dGFibGU8TG9jYWxTaGVsbFRhc2tTdGF0ZT5cbn0pOiBSZWFjdE5vZGUge1xuICBzd2l0Y2ggKHNoZWxsLnN0YXR1cykge1xuICAgIGNhc2UgJ2NvbXBsZXRlZCc6XG4gICAgICByZXR1cm4gPFRhc2tTdGF0dXNUZXh0IHN0YXR1cz1cImNvbXBsZXRlZFwiIGxhYmVsPVwiZG9uZVwiIC8+XG4gICAgY2FzZSAnZmFpbGVkJzpcbiAgICAgIHJldHVybiA8VGFza1N0YXR1c1RleHQgc3RhdHVzPVwiZmFpbGVkXCIgbGFiZWw9XCJlcnJvclwiIC8+XG4gICAgY2FzZSAna2lsbGVkJzpcbiAgICAgIHJldHVybiA8VGFza1N0YXR1c1RleHQgc3RhdHVzPVwia2lsbGVkXCIgbGFiZWw9XCJzdG9wcGVkXCIgLz5cbiAgICBjYXNlICdydW5uaW5nJzpcbiAgICBjYXNlICdwZW5kaW5nJzpcbiAgICAgIHJldHVybiA8VGFza1N0YXR1c1RleHQgc3RhdHVzPVwicnVubmluZ1wiIC8+XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLGNBQWNBLFNBQVMsUUFBUSxPQUFPO0FBQ3RDLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLElBQUksUUFBUSxZQUFZO0FBQ2pDLGNBQWNDLFVBQVUsUUFBUSxhQUFhO0FBQzdDLGNBQWNDLG1CQUFtQixRQUFRLG9DQUFvQztBQUM3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 \ No newline at end of file diff --git a/components/tasks/renderToolActivity.tsx b/components/tasks/renderToolActivity.tsx new file mode 100644 index 0000000..c17c0ed --- /dev/null +++ b/components/tasks/renderToolActivity.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Text } from '../../ink.js'; +import type { Tools } from '../../Tool.js'; +import { findToolByName } from '../../Tool.js'; +import type { ToolActivity } from '../../tasks/LocalAgentTask/LocalAgentTask.js'; +import type { ThemeName } from '../../utils/theme.js'; +export function renderToolActivity(activity: ToolActivity, tools: Tools, theme: ThemeName): React.ReactNode { + const tool = findToolByName(tools, activity.toolName); + if (!tool) { + return activity.toolName; + } + try { + const parsed = tool.inputSchema.safeParse(activity.input); + const parsedInput = parsed.success ? parsed.data : {}; + const userFacingName = tool.userFacingName(parsedInput); + if (!userFacingName) { + return activity.toolName; + } + const toolArgs = tool.renderToolUseMessage(parsedInput, { + theme, + verbose: false + }); + if (toolArgs) { + return + {userFacingName}({toolArgs}) + ; + } + return userFacingName; + } catch { + return activity.toolName; + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJUb29scyIsImZpbmRUb29sQnlOYW1lIiwiVG9vbEFjdGl2aXR5IiwiVGhlbWVOYW1lIiwicmVuZGVyVG9vbEFjdGl2aXR5IiwiYWN0aXZpdHkiLCJ0b29scyIsInRoZW1lIiwiUmVhY3ROb2RlIiwidG9vbCIsInRvb2xOYW1lIiwicGFyc2VkIiwiaW5wdXRTY2hlbWEiLCJzYWZlUGFyc2UiLCJpbnB1dCIsInBhcnNlZElucHV0Iiwic3VjY2VzcyIsImRhdGEiLCJ1c2VyRmFjaW5nTmFtZSIsInRvb2xBcmdzIiwicmVuZGVyVG9vbFVzZU1lc3NhZ2UiLCJ2ZXJib3NlIl0sInNvdXJjZXMiOlsicmVuZGVyVG9vbEFjdGl2aXR5LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBUb29scyB9IGZyb20gJy4uLy4uL1Rvb2wuanMnXG5pbXBvcnQgeyBmaW5kVG9vbEJ5TmFtZSB9IGZyb20gJy4uLy4uL1Rvb2wuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xBY3Rpdml0eSB9IGZyb20gJy4uLy4uL3Rhc2tzL0xvY2FsQWdlbnRUYXNrL0xvY2FsQWdlbnRUYXNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZU5hbWUgfSBmcm9tICcuLi8uLi91dGlscy90aGVtZS5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIHJlbmRlclRvb2xBY3Rpdml0eShcbiAgYWN0aXZpdHk6IFRvb2xBY3Rpdml0eSxcbiAgdG9vbHM6IFRvb2xzLFxuICB0aGVtZTogVGhlbWVOYW1lLFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgdG9vbCA9IGZpbmRUb29sQnlOYW1lKHRvb2xzLCBhY3Rpdml0eS50b29sTmFtZSlcbiAgaWYgKCF0b29sKSB7XG4gICAgcmV0dXJuIGFjdGl2aXR5LnRvb2xOYW1lXG4gIH1cbiAgdHJ5IHtcbiAgICBjb25zdCBwYXJzZWQgPSB0b29sLmlucHV0U2NoZW1hLnNhZmVQYXJzZShhY3Rpdml0eS5pbnB1dClcbiAgICBjb25zdCBwYXJzZWRJbnB1dCA9IHBhcnNlZC5zdWNjZXNzID8gcGFyc2VkLmRhdGEgOiB7fVxuICAgIGNvbnN0IHVzZXJGYWNpbmdOYW1lID0gdG9vbC51c2VyRmFjaW5nTmFtZShwYXJzZWRJbnB1dClcbiAgICBpZiAoIXVzZXJGYWNpbmdOYW1lKSB7XG4gICAgICByZXR1cm4gYWN0aXZpdHkudG9vbE5hbWVcbiAgICB9XG4gICAgY29uc3QgdG9vbEFyZ3MgPSB0b29sLnJlbmRlclRvb2xVc2VNZXNzYWdlKHBhcnNlZElucHV0LCB7XG4gICAgICB0aGVtZSxcbiAgICAgIHZlcmJvc2U6IGZhbHNlLFxuICAgIH0pXG4gICAgaWYgKHRvb2xBcmdzKSB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICB7dXNlckZhY2luZ05hbWV9KHt0b29sQXJnc30pXG4gICAgICAgIDwvVGV4dD5cbiAgICAgIClcbiAgICB9XG4gICAgcmV0dXJuIHVzZXJGYWNpbmdOYW1lXG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiBhY3Rpdml0eS50b29sTmFtZVxuICB9XG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLElBQUksUUFBUSxjQUFjO0FBQ25DLGNBQWNDLEtBQUssUUFBUSxlQUFlO0FBQzFDLFNBQVNDLGNBQWMsUUFBUSxlQUFlO0FBQzlDLGNBQWNDLFlBQVksUUFBUSw4Q0FBOEM7QUFDaEYsY0FBY0MsU0FBUyxRQUFRLHNCQUFzQjtBQUVyRCxPQUFPLFNBQVNDLGtCQUFrQkEsQ0FDaENDLFFBQVEsRUFBRUgsWUFBWSxFQUN0QkksS0FBSyxFQUFFTixLQUFLLEVBQ1pPLEtBQUssRUFBRUosU0FBUyxDQUNqQixFQUFFTCxLQUFLLENBQUNVLFNBQVMsQ0FBQztFQUNqQixNQUFNQyxJQUFJLEdBQUdSLGNBQWMsQ0FBQ0ssS0FBSyxFQUFFRCxRQUFRLENBQUNLLFFBQVEsQ0FBQztFQUNyRCxJQUFJLENBQUNELElBQUksRUFBRTtJQUNULE9BQU9KLFFBQVEsQ0FBQ0ssUUFBUTtFQUMxQjtFQUNBLElBQUk7SUFDRixNQUFNQyxNQUFNLEdBQUdGLElBQUksQ0FBQ0csV0FBVyxDQUFDQyxTQUFTLENBQUNSLFFBQVEsQ0FBQ1MsS0FBSyxDQUFDO0lBQ3pELE1BQU1DLFdBQVcsR0FBR0osTUFBTSxDQUFDSyxPQUFPLEdBQUdMLE1BQU0sQ0FBQ00sSUFBSSxHQUFHLENBQUMsQ0FBQztJQUNyRCxNQUFNQyxjQUFjLEdBQUdULElBQUksQ0FBQ1MsY0FBYyxDQUFDSCxXQUFXLENBQUM7SUFDdkQsSUFBSSxDQUFDRyxjQUFjLEVBQUU7TUFDbkIsT0FBT2IsUUFBUSxDQUFDSyxRQUFRO0lBQzFCO0lBQ0EsTUFBTVMsUUFBUSxHQUFHVixJQUFJLENBQUNXLG9CQUFvQixDQUFDTCxXQUFXLEVBQUU7TUFDdERSLEtBQUs7TUFDTGMsT0FBTyxFQUFFO0lBQ1gsQ0FBQyxDQUFDO0lBQ0YsSUFBSUYsUUFBUSxFQUFFO01BQ1osT0FDRSxDQUFDLElBQUk7QUFDYixVQUFVLENBQUNELGNBQWMsQ0FBQyxDQUFDLENBQUNDLFFBQVEsQ0FBQztBQUNyQyxRQUFRLEVBQUUsSUFBSSxDQUFDO0lBRVg7SUFDQSxPQUFPRCxjQUFjO0VBQ3ZCLENBQUMsQ0FBQyxNQUFNO0lBQ04sT0FBT2IsUUFBUSxDQUFDSyxRQUFRO0VBQzFCO0FBQ0YiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/tasks/taskStatusUtils.tsx b/components/tasks/taskStatusUtils.tsx new file mode 100644 index 0000000..e7f804f --- /dev/null +++ b/components/tasks/taskStatusUtils.tsx @@ -0,0 +1,107 @@ +/** + * Shared utilities for displaying task status across different task types. + */ + +import figures from 'figures'; +import type { TaskStatus } from 'src/Task.js'; +import type { InProcessTeammateTaskState } from 'src/tasks/InProcessTeammateTask/types.js'; +import { isPanelAgentTask } from 'src/tasks/LocalAgentTask/LocalAgentTask.js'; +import { isBackgroundTask, type TaskState } from 'src/tasks/types.js'; +import type { DeepImmutable } from 'src/types/utils.js'; +import { summarizeRecentActivities } from 'src/utils/collapseReadSearch.js'; + +/** + * Returns true if the given task status represents a terminal (finished) state. + */ +export function isTerminalStatus(status: TaskStatus): boolean { + return status === 'completed' || status === 'failed' || status === 'killed'; +} + +/** + * Returns the appropriate icon for a task based on status and state flags. + */ +export function getTaskStatusIcon(status: TaskStatus, options?: { + isIdle?: boolean; + awaitingApproval?: boolean; + hasError?: boolean; + shutdownRequested?: boolean; +}): string { + const { + isIdle, + awaitingApproval, + hasError, + shutdownRequested + } = options ?? {}; + if (hasError) return figures.cross; + if (awaitingApproval) return figures.questionMarkPrefix; + if (shutdownRequested) return figures.warning; + if (status === 'running') { + if (isIdle) return figures.ellipsis; + return figures.play; + } + if (status === 'completed') return figures.tick; + if (status === 'failed' || status === 'killed') return figures.cross; + return figures.bullet; +} + +/** + * Returns the appropriate semantic color for a task based on status and state flags. + */ +export function getTaskStatusColor(status: TaskStatus, options?: { + isIdle?: boolean; + awaitingApproval?: boolean; + hasError?: boolean; + shutdownRequested?: boolean; +}): 'success' | 'error' | 'warning' | 'background' { + const { + isIdle, + awaitingApproval, + hasError, + shutdownRequested + } = options ?? {}; + if (hasError) return 'error'; + if (awaitingApproval) return 'warning'; + if (shutdownRequested) return 'warning'; + if (isIdle) return 'background'; + if (status === 'completed') return 'success'; + if (status === 'failed') return 'error'; + if (status === 'killed') return 'warning'; + return 'background'; +} + +/** + * Derives a human-readable activity string for an in-process teammate, + * accounting for shutdown/approval/idle states and falling back through + * recent-activity summary → last activity description → 'working'. + */ +export function describeTeammateActivity(t: DeepImmutable): string { + if (t.shutdownRequested) return 'stopping'; + if (t.awaitingPlanApproval) return 'awaiting approval'; + if (t.isIdle) return 'idle'; + return (t.progress?.recentActivities && summarizeRecentActivities(t.progress.recentActivities)) ?? t.progress?.lastActivity?.activityDescription ?? 'working'; +} + +/** + * Returns true when BackgroundTaskStatus would render nothing because the + * spinner tree is active and every visible background task is an in-process + * teammate (teammates are shown in the spinner tree instead). + * + * Uses the same task filtering as BackgroundTaskStatus: `isBackgroundTask()` + * plus exclusion of panel-managed agent tasks for ants (those are shown + * by CoordinatorTaskPanel). + */ +export function shouldHideTasksFooter(tasks: { + [taskId: string]: TaskState; +}, showSpinnerTree: boolean): boolean { + if (!showSpinnerTree) return false; + let hasVisibleTask = false; + for (const t of Object.values(tasks) as TaskState[]) { + if (!isBackgroundTask(t) || "external" === 'ant' && isPanelAgentTask(t)) { + continue; + } + hasVisibleTask = true; + if (t.type !== 'in_process_teammate') return false; + } + return hasVisibleTask; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiVGFza1N0YXR1cyIsIkluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlIiwiaXNQYW5lbEFnZW50VGFzayIsImlzQmFja2dyb3VuZFRhc2siLCJUYXNrU3RhdGUiLCJEZWVwSW1tdXRhYmxlIiwic3VtbWFyaXplUmVjZW50QWN0aXZpdGllcyIsImlzVGVybWluYWxTdGF0dXMiLCJzdGF0dXMiLCJnZXRUYXNrU3RhdHVzSWNvbiIsIm9wdGlvbnMiLCJpc0lkbGUiLCJhd2FpdGluZ0FwcHJvdmFsIiwiaGFzRXJyb3IiLCJzaHV0ZG93blJlcXVlc3RlZCIsImNyb3NzIiwicXVlc3Rpb25NYXJrUHJlZml4Iiwid2FybmluZyIsImVsbGlwc2lzIiwicGxheSIsInRpY2siLCJidWxsZXQiLCJnZXRUYXNrU3RhdHVzQ29sb3IiLCJkZXNjcmliZVRlYW1tYXRlQWN0aXZpdHkiLCJ0IiwiYXdhaXRpbmdQbGFuQXBwcm92YWwiLCJwcm9ncmVzcyIsInJlY2VudEFjdGl2aXRpZXMiLCJsYXN0QWN0aXZpdHkiLCJhY3Rpdml0eURlc2NyaXB0aW9uIiwic2hvdWxkSGlkZVRhc2tzRm9vdGVyIiwidGFza3MiLCJ0YXNrSWQiLCJzaG93U3Bpbm5lclRyZWUiLCJoYXNWaXNpYmxlVGFzayIsIk9iamVjdCIsInZhbHVlcyIsInR5cGUiXSwic291cmNlcyI6WyJ0YXNrU3RhdHVzVXRpbHMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2hhcmVkIHV0aWxpdGllcyBmb3IgZGlzcGxheWluZyB0YXNrIHN0YXR1cyBhY3Jvc3MgZGlmZmVyZW50IHRhc2sgdHlwZXMuXG4gKi9cblxuaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCB0eXBlIHsgVGFza1N0YXR1cyB9IGZyb20gJ3NyYy9UYXNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZSB9IGZyb20gJ3NyYy90YXNrcy9JblByb2Nlc3NUZWFtbWF0ZVRhc2svdHlwZXMuanMnXG5pbXBvcnQgeyBpc1BhbmVsQWdlbnRUYXNrIH0gZnJvbSAnc3JjL3Rhc2tzL0xvY2FsQWdlbnRUYXNrL0xvY2FsQWdlbnRUYXNrLmpzJ1xuaW1wb3J0IHsgaXNCYWNrZ3JvdW5kVGFzaywgdHlwZSBUYXNrU3RhdGUgfSBmcm9tICdzcmMvdGFza3MvdHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IERlZXBJbW11dGFibGUgfSBmcm9tICdzcmMvdHlwZXMvdXRpbHMuanMnXG5pbXBvcnQgeyBzdW1tYXJpemVSZWNlbnRBY3Rpdml0aWVzIH0gZnJvbSAnc3JjL3V0aWxzL2NvbGxhcHNlUmVhZFNlYXJjaC5qcydcblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgdGhlIGdpdmVuIHRhc2sgc3RhdHVzIHJlcHJlc2VudHMgYSB0ZXJtaW5hbCAoZmluaXNoZWQpIHN0YXRlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNUZXJtaW5hbFN0YXR1cyhzdGF0dXM6IFRhc2tTdGF0dXMpOiBib29sZWFuIHtcbiAgcmV0dXJuIHN0YXR1cyA9PT0gJ2NvbXBsZXRlZCcgfHwgc3RhdHVzID09PSAnZmFpbGVkJyB8fCBzdGF0dXMgPT09ICdraWxsZWQnXG59XG5cbi8qKlxuICogUmV0dXJucyB0aGUgYXBwcm9wcmlhdGUgaWNvbiBmb3IgYSB0YXNrIGJhc2VkIG9uIHN0YXR1cyBhbmQgc3RhdGUgZmxhZ3MuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRUYXNrU3RhdHVzSWNvbihcbiAgc3RhdHVzOiBUYXNrU3RhdHVzLFxuICBvcHRpb25zPzoge1xuICAgIGlzSWRsZT86IGJvb2xlYW5cbiAgICBhd2FpdGluZ0FwcHJvdmFsPzogYm9vbGVhblxuICAgIGhhc0Vycm9yPzogYm9vbGVhblxuICAgIHNodXRkb3duUmVxdWVzdGVkPzogYm9vbGVhblxuICB9LFxuKTogc3RyaW5nIHtcbiAgY29uc3QgeyBpc0lkbGUsIGF3YWl0aW5nQXBwcm92YWwsIGhhc0Vycm9yLCBzaHV0ZG93blJlcXVlc3RlZCB9ID1cbiAgICBvcHRpb25zID8/IHt9XG5cbiAgaWYgKGhhc0Vycm9yKSByZXR1cm4gZmlndXJlcy5jcm9zc1xuICBpZiAoYXdhaXRpbmdBcHByb3ZhbCkgcmV0dXJuIGZpZ3VyZXMucXVlc3Rpb25NYXJrUHJlZml4XG4gIGlmIChzaHV0ZG93blJlcXVlc3RlZCkgcmV0dXJuIGZpZ3VyZXMud2FybmluZ1xuXG4gIGlmIChzdGF0dXMgPT09ICdydW5uaW5nJykge1xuICAgIGlmIChpc0lkbGUpIHJldHVybiBmaWd1cmVzLmVsbGlwc2lzXG4gICAgcmV0dXJuIGZpZ3VyZXMucGxheVxuICB9XG4gIGlmIChzdGF0dXMgPT09ICdjb21wbGV0ZWQnKSByZXR1cm4gZmlndXJlcy50aWNrXG4gIGlmIChzdGF0dXMgPT09ICdmYWlsZWQnIHx8IHN0YXR1cyA9PT0gJ2tpbGxlZCcpIHJldHVybiBmaWd1cmVzLmNyb3NzXG4gIHJldHVybiBmaWd1cmVzLmJ1bGxldFxufVxuXG4vKipcbiAqIFJldHVybnMgdGhlIGFwcHJvcHJpYXRlIHNlbWFudGljIGNvbG9yIGZvciBhIHRhc2sgYmFzZWQgb24gc3RhdHVzIGFuZCBzdGF0ZSBmbGFncy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFRhc2tTdGF0dXNDb2xvcihcbiAgc3RhdHVzOiBUYXNrU3RhdHVzLFxuICBvcHRpb25zPzoge1xuICAgIGlzSWRsZT86IGJvb2xlYW5cbiAgICBhd2FpdGluZ0FwcHJvdmFsPzogYm9vbGVhblxuICAgIGhhc0Vycm9yPzogYm9vbGVhblxuICAgIHNodXRkb3duUmVxdWVzdGVkPzogYm9vbGVhblxuICB9LFxuKTogJ3N1Y2Nlc3MnIHwgJ2Vycm9yJyB8ICd3YXJuaW5nJyB8ICdiYWNrZ3JvdW5kJyB7XG4gIGNvbnN0IHsgaXNJZGxlLCBhd2FpdGluZ0FwcHJvdmFsLCBoYXNFcnJvciwgc2h1dGRvd25SZXF1ZXN0ZWQgfSA9XG4gICAgb3B0aW9ucyA/PyB7fVxuXG4gIGlmIChoYXNFcnJvcikgcmV0dXJuICdlcnJvcidcbiAgaWYgKGF3YWl0aW5nQXBwcm92YWwpIHJldHVybiAnd2FybmluZydcbiAgaWYgKHNodXRkb3duUmVxdWVzdGVkKSByZXR1cm4gJ3dhcm5pbmcnXG4gIGlmIChpc0lkbGUpIHJldHVybiAnYmFja2dyb3VuZCdcblxuICBpZiAoc3RhdHVzID09PSAnY29tcGxldGVkJykgcmV0dXJuICdzdWNjZXNzJ1xuICBpZiAoc3RhdHVzID09PSAnZmFpbGVkJykgcmV0dXJuICdlcnJvcidcbiAgaWYgKHN0YXR1cyA9PT0gJ2tpbGxlZCcpIHJldHVybiAnd2FybmluZydcbiAgcmV0dXJuICdiYWNrZ3JvdW5kJ1xufVxuXG4vKipcbiAqIERlcml2ZXMgYSBodW1hbi1yZWFkYWJsZSBhY3Rpdml0eSBzdHJpbmcgZm9yIGFuIGluLXByb2Nlc3MgdGVhbW1hdGUsXG4gKiBhY2NvdW50aW5nIGZvciBzaHV0ZG93bi9hcHByb3ZhbC9pZGxlIHN0YXRlcyBhbmQgZmFsbGluZyBiYWNrIHRocm91Z2hcbiAqIHJlY2VudC1hY3Rpdml0eSBzdW1tYXJ5IOKGkiBsYXN0IGFjdGl2aXR5IGRlc2NyaXB0aW9uIOKGkiAnd29ya2luZycuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZXNjcmliZVRlYW1tYXRlQWN0aXZpdHkoXG4gIHQ6IERlZXBJbW11dGFibGU8SW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGU+LFxuKTogc3RyaW5nIHtcbiAgaWYgKHQuc2h1dGRvd25SZXF1ZXN0ZWQpIHJldHVybiAnc3RvcHBpbmcnXG4gIGlmICh0LmF3YWl0aW5nUGxhbkFwcHJvdmFsKSByZXR1cm4gJ2F3YWl0aW5nIGFwcHJvdmFsJ1xuICBpZiAodC5pc0lkbGUpIHJldHVybiAnaWRsZSdcbiAgcmV0dXJuIChcbiAgICAodC5wcm9ncmVzcz8ucmVjZW50QWN0aXZpdGllcyAmJlxuICAgICAgc3VtbWFyaXplUmVjZW50QWN0aXZpdGllcyh0LnByb2dyZXNzLnJlY2VudEFjdGl2aXRpZXMpKSA/P1xuICAgIHQucHJvZ3Jlc3M/Lmxhc3RBY3Rpdml0eT8uYWN0aXZpdHlEZXNjcmlwdGlvbiA/P1xuICAgICd3b3JraW5nJ1xuICApXG59XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIHdoZW4gQmFja2dyb3VuZFRhc2tTdGF0dXMgd291bGQgcmVuZGVyIG5vdGhpbmcgYmVjYXVzZSB0aGVcbiAqIHNwaW5uZXIgdHJlZSBpcyBhY3RpdmUgYW5kIGV2ZXJ5IHZpc2libGUgYmFja2dyb3VuZCB0YXNrIGlzIGFuIGluLXByb2Nlc3NcbiAqIHRlYW1tYXRlICh0ZWFtbWF0ZXMgYXJlIHNob3duIGluIHRoZSBzcGlubmVyIHRyZWUgaW5zdGVhZCkuXG4gKlxuICogVXNlcyB0aGUgc2FtZSB0YXNrIGZpbHRlcmluZyBhcyBCYWNrZ3JvdW5kVGFza1N0YXR1czogYGlzQmFja2dyb3VuZFRhc2soKWBcbiAqIHBsdXMgZXhjbHVzaW9uIG9mIHBhbmVsLW1hbmFnZWQgYWdlbnQgdGFza3MgZm9yIGFudHMgKHRob3NlIGFyZSBzaG93blxuICogYnkgQ29vcmRpbmF0b3JUYXNrUGFuZWwpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2hvdWxkSGlkZVRhc2tzRm9vdGVyKFxuICB0YXNrczogeyBbdGFza0lkOiBzdHJpbmddOiBUYXNrU3RhdGUgfSxcbiAgc2hvd1NwaW5uZXJUcmVlOiBib29sZWFuLFxuKTogYm9vbGVhbiB7XG4gIGlmICghc2hvd1NwaW5uZXJUcmVlKSByZXR1cm4gZmFsc2VcbiAgbGV0IGhhc1Zpc2libGVUYXNrID0gZmFsc2VcbiAgZm9yIChjb25zdCB0IG9mIE9iamVjdC52YWx1ZXModGFza3MpIGFzIFRhc2tTdGF0ZVtdKSB7XG4gICAgaWYgKFxuICAgICAgIWlzQmFja2dyb3VuZFRhc2sodCkgfHxcbiAgICAgIChcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIGlzUGFuZWxBZ2VudFRhc2sodCkpXG4gICAgKSB7XG4gICAgICBjb250aW51ZVxuICAgIH1cbiAgICBoYXNWaXNpYmxlVGFzayA9IHRydWVcbiAgICBpZiAodC50eXBlICE9PSAnaW5fcHJvY2Vzc190ZWFtbWF0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 \ No newline at end of file diff --git a/components/teams/TeamStatus.tsx b/components/teams/TeamStatus.tsx new file mode 100644 index 0000000..61bde72 --- /dev/null +++ b/components/teams/TeamStatus.tsx @@ -0,0 +1,80 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Text } from '../../ink.js'; +import { useAppState } from '../../state/AppState.js'; +type Props = { + teamsSelected: boolean; + showHint: boolean; +}; + +/** + * Footer status indicator showing teammate count + * Similar to BackgroundTaskStatus but for teammates + */ +export function TeamStatus(t0) { + const $ = _c(14); + const { + teamsSelected, + showHint + } = t0; + const teamContext = useAppState(_temp); + let t1; + if ($[0] !== teamContext) { + t1 = teamContext ? Object.values(teamContext.teammates).filter(_temp2).length : 0; + $[0] = teamContext; + $[1] = t1; + } else { + t1 = $[1]; + } + const totalTeammates = t1; + if (totalTeammates === 0) { + return null; + } + let t2; + if ($[2] !== showHint || $[3] !== teamsSelected) { + t2 = showHint && teamsSelected ? <>· Enter to view : null; + $[2] = showHint; + $[3] = teamsSelected; + $[4] = t2; + } else { + t2 = $[4]; + } + const hint = t2; + const statusText = `${totalTeammates} ${totalTeammates === 1 ? "teammate" : "teammates"}`; + const t3 = teamsSelected ? "selected" : "normal"; + let t4; + if ($[5] !== statusText || $[6] !== t3 || $[7] !== teamsSelected) { + t4 = {statusText}; + $[5] = statusText; + $[6] = t3; + $[7] = teamsSelected; + $[8] = t4; + } else { + t4 = $[8]; + } + let t5; + if ($[9] !== hint) { + t5 = hint ? {hint} : null; + $[9] = hint; + $[10] = t5; + } else { + t5 = $[10]; + } + let t6; + if ($[11] !== t4 || $[12] !== t5) { + t6 = <>{t4}{t5}; + $[11] = t4; + $[12] = t5; + $[13] = t6; + } else { + t6 = $[13]; + } + return t6; +} +function _temp2(t) { + return t.name !== "team-lead"; +} +function _temp(s) { + return s.teamContext; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJ1c2VBcHBTdGF0ZSIsIlByb3BzIiwidGVhbXNTZWxlY3RlZCIsInNob3dIaW50IiwiVGVhbVN0YXR1cyIsInQwIiwiJCIsIl9jIiwidGVhbUNvbnRleHQiLCJfdGVtcCIsInQxIiwiT2JqZWN0IiwidmFsdWVzIiwidGVhbW1hdGVzIiwiZmlsdGVyIiwiX3RlbXAyIiwibGVuZ3RoIiwidG90YWxUZWFtbWF0ZXMiLCJ0MiIsImhpbnQiLCJzdGF0dXNUZXh0IiwidDMiLCJ0NCIsInQ1IiwidDYiLCJ0IiwibmFtZSIsInMiXSwic291cmNlcyI6WyJUZWFtU3RhdHVzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSB9IGZyb20gJy4uLy4uL3N0YXRlL0FwcFN0YXRlLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0ZWFtc1NlbGVjdGVkOiBib29sZWFuXG4gIHNob3dIaW50OiBib29sZWFuXG59XG5cbi8qKlxuICogRm9vdGVyIHN0YXR1cyBpbmRpY2F0b3Igc2hvd2luZyB0ZWFtbWF0ZSBjb3VudFxuICogU2ltaWxhciB0byBCYWNrZ3JvdW5kVGFza1N0YXR1cyBidXQgZm9yIHRlYW1tYXRlc1xuICovXG5leHBvcnQgZnVuY3Rpb24gVGVhbVN0YXR1cyh7XG4gIHRlYW1zU2VsZWN0ZWQsXG4gIHNob3dIaW50LFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB0ZWFtQ29udGV4dCA9IHVzZUFwcFN0YXRlKHMgPT4gcy50ZWFtQ29udGV4dClcblxuICAvLyBEZXJpdmUgdGVhbW1hdGUgY291bnQgZnJvbSB0ZWFtQ29udGV4dCAobm8gZmlsZXN5c3RlbSBJL08gbmVlZGVkKVxuICBjb25zdCB0b3RhbFRlYW1tYXRlcyA9IHRlYW1Db250ZXh0XG4gICAgPyBPYmplY3QudmFsdWVzKHRlYW1Db250ZXh0LnRlYW1tYXRlcykuZmlsdGVyKHQgPT4gdC5uYW1lICE9PSAndGVhbS1sZWFkJylcbiAgICAgICAgLmxlbmd0aFxuICAgIDogMFxuXG4gIGlmICh0b3RhbFRlYW1tYXRlcyA9PT0gMCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBoaW50ID1cbiAgICBzaG93SGludCAmJiB0ZWFtc1NlbGVjdGVkID8gKFxuICAgICAgPD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+wrcgPC9UZXh0PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5FbnRlciB0byB2aWV3PC9UZXh0PlxuICAgICAgPC8+XG4gICAgKSA6IG51bGxcblxuICBjb25zdCBzdGF0dXNUZXh0ID0gYCR7dG90YWxUZWFtbWF0ZXN9ICR7dG90YWxUZWFtbWF0ZXMgPT09IDEgPyAndGVhbW1hdGUnIDogJ3RlYW1tYXRlcyd9YFxuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxUZXh0XG4gICAgICAgIGtleT17dGVhbXNTZWxlY3RlZCA/ICdzZWxlY3RlZCcgOiAnbm9ybWFsJ31cbiAgICAgICAgY29sb3I9XCJiYWNrZ3JvdW5kXCJcbiAgICAgICAgaW52ZXJzZT17dGVhbXNTZWxlY3RlZH1cbiAgICAgID5cbiAgICAgICAge3N0YXR1c1RleHR9XG4gICAgICA8L1RleHQ+XG4gICAgICB7aGludCA/IDxUZXh0PiB7aGludH08L1RleHQ+IDogbnVsbH1cbiAgICA8Lz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxJQUFJLFFBQVEsY0FBYztBQUNuQyxTQUFTQyxXQUFXLFFBQVEseUJBQXlCO0FBRXJELEtBQUtDLEtBQUssR0FBRztFQUNYQyxhQUFhLEVBQUUsT0FBTztFQUN0QkMsUUFBUSxFQUFFLE9BQU87QUFDbkIsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU8sU0FBQUMsV0FBQUMsRUFBQTtF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 \ No newline at end of file diff --git a/components/teams/TeamsDialog.tsx b/components/teams/TeamsDialog.tsx new file mode 100644 index 0000000..4f8a6e6 --- /dev/null +++ b/components/teams/TeamsDialog.tsx @@ -0,0 +1,715 @@ +import { c as _c } from "react/compiler-runtime"; +import { randomUUID } from 'crypto'; +import figures from 'figures'; +import * as React from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useInterval } from 'usehooks-ts'; +import { useRegisterOverlay } from '../../context/overlayContext.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- raw j/k/arrow dialog navigation +import { Box, Text, useInput } from '../../ink.js'; +import { useKeybindings } from '../../keybindings/useKeybinding.js'; +import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js'; +import { type AppState, useAppState, useSetAppState } from '../../state/AppState.js'; +import { getEmptyToolPermissionContext } from '../../Tool.js'; +import { AGENT_COLOR_TO_THEME_COLOR } from '../../tools/AgentTool/agentColorManager.js'; +import { logForDebugging } from '../../utils/debug.js'; +import { execFileNoThrow } from '../../utils/execFileNoThrow.js'; +import { truncateToWidth } from '../../utils/format.js'; +import { getNextPermissionMode } from '../../utils/permissions/getNextPermissionMode.js'; +import { getModeColor, type PermissionMode, permissionModeFromString, permissionModeSymbol } from '../../utils/permissions/PermissionMode.js'; +import { jsonStringify } from '../../utils/slowOperations.js'; +import { IT2_COMMAND, isInsideTmuxSync } from '../../utils/swarm/backends/detection.js'; +import { ensureBackendsRegistered, getBackendByType, getCachedBackend } from '../../utils/swarm/backends/registry.js'; +import type { PaneBackendType } from '../../utils/swarm/backends/types.js'; +import { getSwarmSocketName, TMUX_COMMAND } from '../../utils/swarm/constants.js'; +import { addHiddenPaneId, removeHiddenPaneId, removeMemberFromTeam, setMemberMode, setMultipleMemberModes } from '../../utils/swarm/teamHelpers.js'; +import { listTasks, type Task, unassignTeammateTasks } from '../../utils/tasks.js'; +import { getTeammateStatuses, type TeammateStatus, type TeamSummary } from '../../utils/teamDiscovery.js'; +import { createModeSetRequestMessage, sendShutdownRequestToMailbox, writeToMailbox } from '../../utils/teammateMailbox.js'; +import { Dialog } from '../design-system/Dialog.js'; +import ThemedText from '../design-system/ThemedText.js'; +type Props = { + initialTeams?: TeamSummary[]; + onDone: () => void; +}; +type DialogLevel = { + type: 'teammateList'; + teamName: string; +} | { + type: 'teammateDetail'; + teamName: string; + memberName: string; +}; + +/** + * Dialog for viewing teammates in the current team + */ +export function TeamsDialog({ + initialTeams, + onDone +}: Props): React.ReactNode { + // Register as overlay so CancelRequestHandler doesn't intercept escape + useRegisterOverlay('teams-dialog'); + + // initialTeams is derived from teamContext in PromptInput (no filesystem I/O) + const setAppState = useSetAppState(); + + // Initialize dialogLevel with first team name if available + const firstTeamName = initialTeams?.[0]?.name ?? ''; + const [dialogLevel, setDialogLevel] = useState({ + type: 'teammateList', + teamName: firstTeamName + }); + const [selectedIndex, setSelectedIndex] = useState(0); + const [refreshKey, setRefreshKey] = useState(0); + + // initialTeams is now always provided from PromptInput (derived from teamContext) + // No filesystem I/O needed here + + const teammateStatuses = useMemo(() => { + return getTeammateStatuses(dialogLevel.teamName); + // eslint-disable-next-line react-hooks/exhaustive-deps + // biome-ignore lint/correctness/useExhaustiveDependencies: intentional + }, [dialogLevel.teamName, refreshKey]); + + // Periodically refresh to pick up mode changes from teammates + useInterval(() => { + setRefreshKey(k => k + 1); + }, 1000); + const currentTeammate = useMemo(() => { + if (dialogLevel.type !== 'teammateDetail') return null; + return teammateStatuses.find(t => t.name === dialogLevel.memberName) ?? null; + }, [dialogLevel, teammateStatuses]); + + // Get isBypassPermissionsModeAvailable from AppState + const isBypassAvailable = useAppState(s => s.toolPermissionContext.isBypassPermissionsModeAvailable); + const goBackToList = (): void => { + setDialogLevel({ + type: 'teammateList', + teamName: dialogLevel.teamName + }); + setSelectedIndex(0); + }; + + // Handler for confirm:cycleMode - cycle teammate permission modes + const handleCycleMode = useCallback(() => { + if (dialogLevel.type === 'teammateDetail' && currentTeammate) { + // Detail view: cycle just this teammate + cycleTeammateMode(currentTeammate, dialogLevel.teamName, isBypassAvailable); + setRefreshKey(k => k + 1); + } else if (dialogLevel.type === 'teammateList' && teammateStatuses.length > 0) { + // List view: cycle all teammates in tandem + cycleAllTeammateModes(teammateStatuses, dialogLevel.teamName, isBypassAvailable); + setRefreshKey(k => k + 1); + } + }, [dialogLevel, currentTeammate, teammateStatuses, isBypassAvailable]); + + // Use keybindings for mode cycling + useKeybindings({ + 'confirm:cycleMode': handleCycleMode + }, { + context: 'Confirmation' + }); + useInput((input, key) => { + // Handle left arrow to go back + if (key.leftArrow) { + if (dialogLevel.type === 'teammateDetail') { + goBackToList(); + } + return; + } + + // Handle up/down navigation + if (key.upArrow || key.downArrow) { + const maxIndex = getMaxIndex(); + if (key.upArrow) { + setSelectedIndex(prev => Math.max(0, prev - 1)); + } else { + setSelectedIndex(prev => Math.min(maxIndex, prev + 1)); + } + return; + } + + // Handle Enter to drill down or view output + if (key.return) { + if (dialogLevel.type === 'teammateList' && teammateStatuses[selectedIndex]) { + setDialogLevel({ + type: 'teammateDetail', + teamName: dialogLevel.teamName, + memberName: teammateStatuses[selectedIndex].name + }); + } else if (dialogLevel.type === 'teammateDetail' && currentTeammate) { + // View output - switch to tmux pane + void viewTeammateOutput(currentTeammate.tmuxPaneId, currentTeammate.backendType); + onDone(); + } + return; + } + + // Handle 'k' to kill teammate + if (input === 'k') { + if (dialogLevel.type === 'teammateList' && teammateStatuses[selectedIndex]) { + void killTeammate(teammateStatuses[selectedIndex].tmuxPaneId, teammateStatuses[selectedIndex].backendType, dialogLevel.teamName, teammateStatuses[selectedIndex].agentId, teammateStatuses[selectedIndex].name, setAppState).then(() => { + setRefreshKey(k => k + 1); + // Adjust selection if needed + setSelectedIndex(prev => Math.max(0, Math.min(prev, teammateStatuses.length - 2))); + }); + } else if (dialogLevel.type === 'teammateDetail' && currentTeammate) { + void killTeammate(currentTeammate.tmuxPaneId, currentTeammate.backendType, dialogLevel.teamName, currentTeammate.agentId, currentTeammate.name, setAppState); + goBackToList(); + } + return; + } + + // Handle 's' for shutdown of selected teammate + if (input === 's') { + if (dialogLevel.type === 'teammateList' && teammateStatuses[selectedIndex]) { + const teammate = teammateStatuses[selectedIndex]; + void sendShutdownRequestToMailbox(teammate.name, dialogLevel.teamName, 'Graceful shutdown requested by team lead'); + } else if (dialogLevel.type === 'teammateDetail' && currentTeammate) { + void sendShutdownRequestToMailbox(currentTeammate.name, dialogLevel.teamName, 'Graceful shutdown requested by team lead'); + goBackToList(); + } + return; + } + + // Handle 'h' to hide/show individual teammate (only for backends that support it) + if (input === 'h') { + const backend = getCachedBackend(); + const teammate = dialogLevel.type === 'teammateList' ? teammateStatuses[selectedIndex] : dialogLevel.type === 'teammateDetail' ? currentTeammate : null; + if (teammate && backend?.supportsHideShow) { + void toggleTeammateVisibility(teammate, dialogLevel.teamName).then(() => { + // Force refresh of teammate statuses + setRefreshKey(k => k + 1); + }); + if (dialogLevel.type === 'teammateDetail') { + goBackToList(); + } + } + return; + } + + // Handle 'H' to hide/show all teammates (only for backends that support it) + if (input === 'H' && dialogLevel.type === 'teammateList') { + const backend = getCachedBackend(); + if (backend?.supportsHideShow && teammateStatuses.length > 0) { + // If any are visible, hide all. Otherwise, show all. + const anyVisible = teammateStatuses.some(t => !t.isHidden); + void Promise.all(teammateStatuses.map(t => anyVisible ? hideTeammate(t, dialogLevel.teamName) : showTeammate(t, dialogLevel.teamName))).then(() => { + // Force refresh of teammate statuses + setRefreshKey(k => k + 1); + }); + } + return; + } + + // Handle 'p' to prune (kill) all idle teammates + if (input === 'p' && dialogLevel.type === 'teammateList') { + const idleTeammates = teammateStatuses.filter(t => t.status === 'idle'); + if (idleTeammates.length > 0) { + void Promise.all(idleTeammates.map(t => killTeammate(t.tmuxPaneId, t.backendType, dialogLevel.teamName, t.agentId, t.name, setAppState))).then(() => { + setRefreshKey(k => k + 1); + setSelectedIndex(prev => Math.max(0, Math.min(prev, teammateStatuses.length - idleTeammates.length - 1))); + }); + } + return; + } + + // Note: Mode cycling (shift+tab) is handled via useKeybindings with confirm:cycleMode action + }); + function getMaxIndex(): number { + if (dialogLevel.type === 'teammateList') { + return Math.max(0, teammateStatuses.length - 1); + } + return 0; + } + + // Render based on dialog level + if (dialogLevel.type === 'teammateList') { + return ; + } + if (dialogLevel.type === 'teammateDetail' && currentTeammate) { + return ; + } + return null; +} +type TeamDetailViewProps = { + teamName: string; + teammates: TeammateStatus[]; + selectedIndex: number; + onCancel: () => void; +}; +function TeamDetailView(t0) { + const $ = _c(13); + const { + teamName, + teammates, + selectedIndex, + onCancel + } = t0; + const subtitle = `${teammates.length} ${teammates.length === 1 ? "teammate" : "teammates"}`; + const supportsHideShow = getCachedBackend()?.supportsHideShow ?? false; + const cycleModeShortcut = useShortcutDisplay("confirm:cycleMode", "Confirmation", "shift+tab"); + const t1 = `Team ${teamName}`; + let t2; + if ($[0] !== selectedIndex || $[1] !== teammates) { + t2 = teammates.length === 0 ? No teammates : {teammates.map((teammate, index) => )}; + $[0] = selectedIndex; + $[1] = teammates; + $[2] = t2; + } else { + t2 = $[2]; + } + let t3; + if ($[3] !== onCancel || $[4] !== subtitle || $[5] !== t1 || $[6] !== t2) { + t3 = {t2}; + $[3] = onCancel; + $[4] = subtitle; + $[5] = t1; + $[6] = t2; + $[7] = t3; + } else { + t3 = $[7]; + } + let t4; + if ($[8] !== cycleModeShortcut) { + t4 = {figures.arrowUp}/{figures.arrowDown} select · Enter view · k kill · s shutdown · p prune idle{supportsHideShow && " \xB7 h hide/show \xB7 H hide/show all"}{" \xB7 "}{cycleModeShortcut} sync cycle modes for all · Esc close; + $[8] = cycleModeShortcut; + $[9] = t4; + } else { + t4 = $[9]; + } + let t5; + if ($[10] !== t3 || $[11] !== t4) { + t5 = <>{t3}{t4}; + $[10] = t3; + $[11] = t4; + $[12] = t5; + } else { + t5 = $[12]; + } + return t5; +} +type TeammateListItemProps = { + teammate: TeammateStatus; + isSelected: boolean; +}; +function TeammateListItem(t0) { + const $ = _c(21); + const { + teammate, + isSelected + } = t0; + const isIdle = teammate.status === "idle"; + const shouldDim = isIdle && !isSelected; + let modeSymbol; + let t1; + if ($[0] !== teammate.mode) { + const mode = teammate.mode ? permissionModeFromString(teammate.mode) : "default"; + modeSymbol = permissionModeSymbol(mode); + t1 = getModeColor(mode); + $[0] = teammate.mode; + $[1] = modeSymbol; + $[2] = t1; + } else { + modeSymbol = $[1]; + t1 = $[2]; + } + const modeColor = t1; + const t2 = isSelected ? "suggestion" : undefined; + const t3 = isSelected ? figures.pointer + " " : " "; + let t4; + if ($[3] !== teammate.isHidden) { + t4 = teammate.isHidden && [hidden] ; + $[3] = teammate.isHidden; + $[4] = t4; + } else { + t4 = $[4]; + } + let t5; + if ($[5] !== isIdle) { + t5 = isIdle && [idle] ; + $[5] = isIdle; + $[6] = t5; + } else { + t5 = $[6]; + } + let t6; + if ($[7] !== modeColor || $[8] !== modeSymbol) { + t6 = modeSymbol && {modeSymbol} ; + $[7] = modeColor; + $[8] = modeSymbol; + $[9] = t6; + } else { + t6 = $[9]; + } + let t7; + if ($[10] !== teammate.model) { + t7 = teammate.model && ({teammate.model}); + $[10] = teammate.model; + $[11] = t7; + } else { + t7 = $[11]; + } + let t8; + if ($[12] !== shouldDim || $[13] !== t2 || $[14] !== t3 || $[15] !== t4 || $[16] !== t5 || $[17] !== t6 || $[18] !== t7 || $[19] !== teammate.name) { + t8 = {t3}{t4}{t5}{t6}@{teammate.name}{t7}; + $[12] = shouldDim; + $[13] = t2; + $[14] = t3; + $[15] = t4; + $[16] = t5; + $[17] = t6; + $[18] = t7; + $[19] = teammate.name; + $[20] = t8; + } else { + t8 = $[20]; + } + return t8; +} +type TeammateDetailViewProps = { + teammate: TeammateStatus; + teamName: string; + onCancel: () => void; +}; +function TeammateDetailView(t0) { + const $ = _c(39); + const { + teammate, + teamName, + onCancel + } = t0; + const [promptExpanded, setPromptExpanded] = useState(false); + const cycleModeShortcut = useShortcutDisplay("confirm:cycleMode", "Confirmation", "shift+tab"); + const themeColor = teammate.color ? AGENT_COLOR_TO_THEME_COLOR[teammate.color as keyof typeof AGENT_COLOR_TO_THEME_COLOR] : undefined; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = []; + $[0] = t1; + } else { + t1 = $[0]; + } + const [teammateTasks, setTeammateTasks] = useState(t1); + let t2; + let t3; + if ($[1] !== teamName || $[2] !== teammate.agentId || $[3] !== teammate.name) { + t2 = () => { + let cancelled = false; + listTasks(teamName).then(allTasks => { + if (cancelled) { + return; + } + setTeammateTasks(allTasks.filter(task => task.owner === teammate.agentId || task.owner === teammate.name)); + }); + return () => { + cancelled = true; + }; + }; + t3 = [teamName, teammate.agentId, teammate.name]; + $[1] = teamName; + $[2] = teammate.agentId; + $[3] = teammate.name; + $[4] = t2; + $[5] = t3; + } else { + t2 = $[4]; + t3 = $[5]; + } + useEffect(t2, t3); + let t4; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t4 = input => { + if (input === "p") { + setPromptExpanded(_temp); + } + }; + $[6] = t4; + } else { + t4 = $[6]; + } + useInput(t4); + const workingPath = teammate.worktreePath || teammate.cwd; + let subtitleParts; + if ($[7] !== teammate.model || $[8] !== teammate.worktreePath || $[9] !== workingPath) { + subtitleParts = []; + if (teammate.model) { + subtitleParts.push(teammate.model); + } + if (workingPath) { + subtitleParts.push(teammate.worktreePath ? `worktree: ${workingPath}` : workingPath); + } + $[7] = teammate.model; + $[8] = teammate.worktreePath; + $[9] = workingPath; + $[10] = subtitleParts; + } else { + subtitleParts = $[10]; + } + const subtitle = subtitleParts.join(" \xB7 ") || undefined; + let modeSymbol; + let t5; + if ($[11] !== teammate.mode) { + const mode = teammate.mode ? permissionModeFromString(teammate.mode) : "default"; + modeSymbol = permissionModeSymbol(mode); + t5 = getModeColor(mode); + $[11] = teammate.mode; + $[12] = modeSymbol; + $[13] = t5; + } else { + modeSymbol = $[12]; + t5 = $[13]; + } + const modeColor = t5; + let t6; + if ($[14] !== modeColor || $[15] !== modeSymbol) { + t6 = modeSymbol && {modeSymbol} ; + $[14] = modeColor; + $[15] = modeSymbol; + $[16] = t6; + } else { + t6 = $[16]; + } + let t7; + if ($[17] !== teammate.name || $[18] !== themeColor) { + t7 = themeColor ? {`@${teammate.name}`} : `@${teammate.name}`; + $[17] = teammate.name; + $[18] = themeColor; + $[19] = t7; + } else { + t7 = $[19]; + } + let t8; + if ($[20] !== t6 || $[21] !== t7) { + t8 = <>{t6}{t7}; + $[20] = t6; + $[21] = t7; + $[22] = t8; + } else { + t8 = $[22]; + } + const title = t8; + let t9; + if ($[23] !== teammateTasks) { + t9 = teammateTasks.length > 0 && Tasks{teammateTasks.map(_temp2)}; + $[23] = teammateTasks; + $[24] = t9; + } else { + t9 = $[24]; + } + let t10; + if ($[25] !== promptExpanded || $[26] !== teammate.prompt) { + t10 = teammate.prompt && Prompt{promptExpanded ? teammate.prompt : truncateToWidth(teammate.prompt, 80)}{stringWidth(teammate.prompt) > 80 && !promptExpanded && (p to expand)}; + $[25] = promptExpanded; + $[26] = teammate.prompt; + $[27] = t10; + } else { + t10 = $[27]; + } + let t11; + if ($[28] !== onCancel || $[29] !== subtitle || $[30] !== t10 || $[31] !== t9 || $[32] !== title) { + t11 = {t9}{t10}; + $[28] = onCancel; + $[29] = subtitle; + $[30] = t10; + $[31] = t9; + $[32] = title; + $[33] = t11; + } else { + t11 = $[33]; + } + let t12; + if ($[34] !== cycleModeShortcut) { + t12 = {figures.arrowLeft} back · Esc close · k kill · s shutdown{getCachedBackend()?.supportsHideShow && " \xB7 h hide/show"}{" \xB7 "}{cycleModeShortcut} cycle mode; + $[34] = cycleModeShortcut; + $[35] = t12; + } else { + t12 = $[35]; + } + let t13; + if ($[36] !== t11 || $[37] !== t12) { + t13 = <>{t11}{t12}; + $[36] = t11; + $[37] = t12; + $[38] = t13; + } else { + t13 = $[38]; + } + return t13; +} +function _temp2(task_0) { + return {task_0.status === "completed" ? figures.tick : "\u25FC"}{" "}{task_0.subject}; +} +function _temp(prev) { + return !prev; +} +async function killTeammate(paneId: string, backendType: PaneBackendType | undefined, teamName: string, teammateId: string, teammateName: string, setAppState: (f: (prev: AppState) => AppState) => void): Promise { + // Kill the pane using the backend that created it (handles -s / -L flags correctly). + // Wrapped in try/catch so cleanup (removeMemberFromTeam, unassignTeammateTasks, + // setAppState) always runs — matches useInboxPoller.ts error isolation. + if (backendType) { + try { + // Use ensureBackendsRegistered (not detectAndGetBackend) — this process may + // be a teammate that never ran detection, but we only need class imports + // here, not subprocess probes that could throw in a different environment. + await ensureBackendsRegistered(); + await getBackendByType(backendType).killPane(paneId, !isInsideTmuxSync()); + } catch (error) { + logForDebugging(`[TeamsDialog] Failed to kill pane ${paneId}: ${error}`); + } + } else { + // backendType undefined: old team files predating this field, or in-process. + // Old tmux-file case is a migration gap — the pane is orphaned. In-process + // teammates have no pane to kill, so this is correct for them. + logForDebugging(`[TeamsDialog] Skipping pane kill for ${paneId}: no backendType recorded`); + } + // Remove from team config file + removeMemberFromTeam(teamName, paneId); + + // Unassign tasks and build notification message + const { + notificationMessage + } = await unassignTeammateTasks(teamName, teammateId, teammateName, 'terminated'); + + // Update AppState to keep status line in sync and notify the lead + setAppState(prev => { + if (!prev.teamContext?.teammates) return prev; + if (!(teammateId in prev.teamContext.teammates)) return prev; + const { + [teammateId]: _, + ...remainingTeammates + } = prev.teamContext.teammates; + return { + ...prev, + teamContext: { + ...prev.teamContext, + teammates: remainingTeammates + }, + inbox: { + messages: [...prev.inbox.messages, { + id: randomUUID(), + from: 'system', + text: jsonStringify({ + type: 'teammate_terminated', + message: notificationMessage + }), + timestamp: new Date().toISOString(), + status: 'pending' as const + }] + } + }; + }); + logForDebugging(`[TeamsDialog] Removed ${teammateId} from teamContext`); +} +async function viewTeammateOutput(paneId: string, backendType: PaneBackendType | undefined): Promise { + if (backendType === 'iterm2') { + // -s is required to target a specific session (ITermBackend.ts:216-217) + await execFileNoThrow(IT2_COMMAND, ['session', 'focus', '-s', paneId]); + } else { + // External-tmux teammates live on the swarm socket — without -L, this + // targets the default server and silently no-ops. Mirrors runTmuxInSwarm + // in TmuxBackend.ts:85-89. + const args = isInsideTmuxSync() ? ['select-pane', '-t', paneId] : ['-L', getSwarmSocketName(), 'select-pane', '-t', paneId]; + await execFileNoThrow(TMUX_COMMAND, args); + } +} + +/** + * Toggle visibility of a teammate pane (hide if visible, show if hidden) + */ +async function toggleTeammateVisibility(teammate: TeammateStatus, teamName: string): Promise { + if (teammate.isHidden) { + await showTeammate(teammate, teamName); + } else { + await hideTeammate(teammate, teamName); + } +} + +/** + * Hide a teammate pane using the backend abstraction. + * Only available for ant users (gated for dead code elimination in external builds) + */ +async function hideTeammate(teammate: TeammateStatus, teamName: string): Promise {} + +/** + * Show a previously hidden teammate pane using the backend abstraction. + * Only available for ant users (gated for dead code elimination in external builds) + */ +async function showTeammate(teammate: TeammateStatus, teamName: string): Promise {} + +/** + * Send a mode change message to a single teammate + * Also updates config.json directly so the UI reflects the change immediately + */ +function sendModeChangeToTeammate(teammateName: string, teamName: string, targetMode: PermissionMode): void { + // Update config.json directly so UI shows the change immediately + setMemberMode(teamName, teammateName, targetMode); + + // Also send message so teammate updates their local permission context + const message = createModeSetRequestMessage({ + mode: targetMode, + from: 'team-lead' + }); + void writeToMailbox(teammateName, { + from: 'team-lead', + text: jsonStringify(message), + timestamp: new Date().toISOString() + }, teamName); + logForDebugging(`[TeamsDialog] Sent mode change to ${teammateName}: ${targetMode}`); +} + +/** + * Cycle a single teammate's mode + */ +function cycleTeammateMode(teammate: TeammateStatus, teamName: string, isBypassAvailable: boolean): void { + const currentMode = teammate.mode ? permissionModeFromString(teammate.mode) : 'default'; + const context = { + ...getEmptyToolPermissionContext(), + mode: currentMode, + isBypassPermissionsModeAvailable: isBypassAvailable + }; + const nextMode = getNextPermissionMode(context); + sendModeChangeToTeammate(teammate.name, teamName, nextMode); +} + +/** + * Cycle all teammates' modes in tandem + * If modes differ, reset all to default first + * If same, cycle all to next mode + * Uses batch update to avoid race conditions + */ +function cycleAllTeammateModes(teammates: TeammateStatus[], teamName: string, isBypassAvailable: boolean): void { + if (teammates.length === 0) return; + const modes = teammates.map(t => t.mode ? permissionModeFromString(t.mode) : 'default'); + const allSame = modes.every(m => m === modes[0]); + + // Determine target mode for all teammates + const targetMode = !allSame ? 'default' : getNextPermissionMode({ + ...getEmptyToolPermissionContext(), + mode: modes[0] ?? 'default', + isBypassPermissionsModeAvailable: isBypassAvailable + }); + + // Batch update config.json in a single atomic operation + const modeUpdates = teammates.map(t => ({ + memberName: t.name, + mode: targetMode + })); + setMultipleMemberModes(teamName, modeUpdates); + + // Send mailbox messages to each teammate + for (const teammate of teammates) { + const message = createModeSetRequestMessage({ + mode: targetMode, + from: 'team-lead' + }); + void writeToMailbox(teammate.name, { + from: 'team-lead', + text: jsonStringify(message), + timestamp: new Date().toISOString() + }, teamName); + } + logForDebugging(`[TeamsDialog] Sent mode change to all ${teammates.length} teammates: ${targetMode}`); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJyYW5kb21VVUlEIiwiZmlndXJlcyIsIlJlYWN0IiwidXNlQ2FsbGJhY2siLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwidXNlU3RhdGUiLCJ1c2VJbnRlcnZhbCIsInVzZVJlZ2lzdGVyT3ZlcmxheSIsInN0cmluZ1dpZHRoIiwiQm94IiwiVGV4dCIsInVzZUlucHV0IiwidXNlS2V5YmluZGluZ3MiLCJ1c2VTaG9ydGN1dERpc3BsYXkiLCJBcHBTdGF0ZSIsInVzZUFwcFN0YXRlIiwidXNlU2V0QXBwU3RhdGUiLCJnZXRFbXB0eVRvb2xQZXJtaXNzaW9uQ29udGV4dCIsIkFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SIiwibG9nRm9yRGVidWdnaW5nIiwiZXhlY0ZpbGVOb1Rocm93IiwidHJ1bmNhdGVUb1dpZHRoIiwiZ2V0TmV4dFBlcm1pc3Npb25Nb2RlIiwiZ2V0TW9kZUNvbG9yIiwiUGVybWlzc2lvbk1vZGUiLCJwZXJtaXNzaW9uTW9kZUZyb21TdHJpbmciLCJwZXJtaXNzaW9uTW9kZVN5bWJvbCIsImpzb25TdHJpbmdpZnkiLCJJVDJfQ09NTUFORCIsImlzSW5zaWRlVG11eFN5bmMiLCJlbnN1cmVCYWNrZW5kc1JlZ2lzdGVyZWQiLCJnZXRCYWNrZW5kQnlUeXBlIiwiZ2V0Q2FjaGVkQmFja2VuZCIsIlBhbmVCYWNrZW5kVHlwZSIsImdldFN3YXJtU29ja2V0TmFtZSIsIlRNVVhfQ09NTUFORCIsImFkZEhpZGRlblBhbmVJZCIsInJlbW92ZUhpZGRlblBhbmVJZCIsInJlbW92ZU1lbWJlckZyb21UZWFtIiwic2V0TWVtYmVyTW9kZSIsInNldE11bHRpcGxlTWVtYmVyTW9kZXMiLCJsaXN0VGFza3MiLCJUYXNrIiwidW5hc3NpZ25UZWFtbWF0ZVRhc2tzIiwiZ2V0VGVhbW1hdGVTdGF0dXNlcyIsIlRlYW1tYXRlU3RhdHVzIiwiVGVhbVN1bW1hcnkiLCJjcmVhdGVNb2RlU2V0UmVxdWVzdE1lc3NhZ2UiLCJzZW5kU2h1dGRvd25SZXF1ZXN0VG9NYWlsYm94Iiwid3JpdGVUb01haWxib3giLCJEaWFsb2ciLCJUaGVtZWRUZXh0IiwiUHJvcHMiLCJpbml0aWFsVGVhbXMiLCJvbkRvbmUiLCJEaWFsb2dMZXZlbCIsInR5cGUiLCJ0ZWFtTmFtZSIsIm1lbWJlck5hbWUiLCJUZWFtc0RpYWxvZyIsIlJlYWN0Tm9kZSIsInNldEFwcFN0YXRlIiwiZmlyc3RUZWFtTmFtZSIsIm5hbWUiLCJkaWFsb2dMZXZlbCIsInNldERpYWxvZ0xldmVsIiwic2VsZWN0ZWRJbmRleCIsInNldFNlbGVjdGVkSW5kZXgiLCJyZWZyZXNoS2V5Iiwic2V0UmVmcmVzaEtleSIsInRlYW1tYXRlU3RhdHVzZXMiLCJrIiwiY3VycmVudFRlYW1tYXRlIiwiZmluZCIsInQiLCJpc0J5cGFzc0F2YWlsYWJsZSIsInMiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJpc0J5cGFzc1Blcm1pc3Npb25zTW9kZUF2YWlsYWJsZSIsImdvQmFja1RvTGlzdCIsImhhbmRsZUN5Y2xlTW9kZSIsImN5Y2xlVGVhbW1hdGVNb2RlIiwibGVuZ3RoIiwiY3ljbGVBbGxUZWFtbWF0ZU1vZGVzIiwiY29udGV4dCIsImlucHV0Iiwia2V5IiwibGVmdEFycm93IiwidXBBcnJvdyIsImRvd25BcnJvdyIsIm1heEluZGV4IiwiZ2V0TWF4SW5kZXgiLCJwcmV2IiwiTWF0aCIsIm1heCIsIm1pbiIsInJldHVybiIsInZpZXdUZWFtbWF0ZU91dHB1dCIsInRtdXhQYW5lSWQiLCJiYWNrZW5kVHlwZSIsImtpbGxUZWFtbWF0ZSIsImFnZW50SWQiLCJ0aGVuIiwidGVhbW1hdGUiLCJiYWNrZW5kIiwic3VwcG9ydHNIaWRlU2hvdyIsInRvZ2dsZVRlYW1tYXRlVmlzaWJpbGl0eSIsImFueVZpc2libGUiLCJzb21lIiwiaXNIaWRkZW4iLCJQcm9taXNlIiwiYWxsIiwibWFwIiwiaGlkZVRlYW1tYXRlIiwic2hvd1RlYW1tYXRlIiwiaWRsZVRlYW1tYXRlcyIsImZpbHRlciIsInN0YXR1cyIsIlRlYW1EZXRhaWxWaWV3UHJvcHMiLCJ0ZWFtbWF0ZXMiLCJvbkNhbmNlbCIsIlRlYW1EZXRhaWxWaWV3IiwidDAiLCIkIiwiX2MiLCJzdWJ0aXRsZSIsImN5Y2xlTW9kZVNob3J0Y3V0IiwidDEiLCJ0MiIsImluZGV4IiwidDMiLCJ0NCIsImFycm93VXAiLCJhcnJvd0Rvd24iLCJ0NSIsIlRlYW1tYXRlTGlzdEl0ZW1Qcm9wcyIsImlzU2VsZWN0ZWQiLCJUZWFtbWF0ZUxpc3RJdGVtIiwiaXNJZGxlIiwic2hvdWxkRGltIiwibW9kZVN5bWJvbCIsIm1vZGUiLCJtb2RlQ29sb3IiLCJ1bmRlZmluZWQiLCJwb2ludGVyIiwidDYiLCJ0NyIsIm1vZGVsIiwidDgiLCJUZWFtbWF0ZURldGFpbFZpZXdQcm9wcyIsIlRlYW1tYXRlRGV0YWlsVmlldyIsInByb21wdEV4cGFuZGVkIiwic2V0UHJvbXB0RXhwYW5kZWQiLCJ0aGVtZUNvbG9yIiwiY29sb3IiLCJTeW1ib2wiLCJmb3IiLCJ0ZWFtbWF0ZVRhc2tzIiwic2V0VGVhbW1hdGVUYXNrcyIsImNhbmNlbGxlZCIsImFsbFRhc2tzIiwidGFzayIsIm93bmVyIiwiX3RlbXAiLCJ3b3JraW5nUGF0aCIsIndvcmt0cmVlUGF0aCIsImN3ZCIsInN1YnRpdGxlUGFydHMiLCJwdXNoIiwiam9pbiIsInRpdGxlIiwidDkiLCJfdGVtcDIiLCJ0MTAiLCJwcm9tcHQiLCJ0MTEiLCJ0MTIiLCJhcnJvd0xlZnQiLCJ0MTMiLCJ0YXNrXzAiLCJpZCIsInRpY2siLCJzdWJqZWN0IiwicGFuZUlkIiwidGVhbW1hdGVJZCIsInRlYW1tYXRlTmFtZSIsImYiLCJraWxsUGFuZSIsImVycm9yIiwibm90aWZpY2F0aW9uTWVzc2FnZSIsInRlYW1Db250ZXh0IiwiXyIsInJlbWFpbmluZ1RlYW1tYXRlcyIsImluYm94IiwibWVzc2FnZXMiLCJmcm9tIiwidGV4dCIsIm1lc3NhZ2UiLCJ0aW1lc3RhbXAiLCJEYXRlIiwidG9JU09TdHJpbmciLCJjb25zdCIsImFyZ3MiLCJzZW5kTW9kZUNoYW5nZVRvVGVhbW1hdGUiLCJ0YXJnZXRNb2RlIiwiY3VycmVudE1vZGUiLCJuZXh0TW9kZSIsIm1vZGVzIiwiYWxsU2FtZSIsImV2ZXJ5IiwibSIsIm1vZGVVcGRhdGVzIl0sInNvdXJjZXMiOlsiVGVhbXNEaWFsb2cudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHJhbmRvbVVVSUQgfSBmcm9tICdjcnlwdG8nXG5pbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VDYWxsYmFjaywgdXNlRWZmZWN0LCB1c2VNZW1vLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlSW50ZXJ2YWwgfSBmcm9tICd1c2Vob29rcy10cydcbmltcG9ydCB7IHVzZVJlZ2lzdGVyT3ZlcmxheSB9IGZyb20gJy4uLy4uL2NvbnRleHQvb3ZlcmxheUNvbnRleHQuanMnXG5pbXBvcnQgeyBzdHJpbmdXaWR0aCB9IGZyb20gJy4uLy4uL2luay9zdHJpbmdXaWR0aC5qcydcbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvcHJlZmVyLXVzZS1rZXliaW5kaW5ncyAtLSByYXcgai9rL2Fycm93IGRpYWxvZyBuYXZpZ2F0aW9uXG5pbXBvcnQgeyBCb3gsIFRleHQsIHVzZUlucHV0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZ3MgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHsgdXNlU2hvcnRjdXREaXNwbGF5IH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlU2hvcnRjdXREaXNwbGF5LmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBcHBTdGF0ZSxcbiAgdXNlQXBwU3RhdGUsXG4gIHVzZVNldEFwcFN0YXRlLFxufSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IGdldEVtcHR5VG9vbFBlcm1pc3Npb25Db250ZXh0IH0gZnJvbSAnLi4vLi4vVG9vbC5qcydcbmltcG9ydCB7IEFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SIH0gZnJvbSAnLi4vLi4vdG9vbHMvQWdlbnRUb29sL2FnZW50Q29sb3JNYW5hZ2VyLmpzJ1xuaW1wb3J0IHsgbG9nRm9yRGVidWdnaW5nIH0gZnJvbSAnLi4vLi4vdXRpbHMvZGVidWcuanMnXG5pbXBvcnQgeyBleGVjRmlsZU5vVGhyb3cgfSBmcm9tICcuLi8uLi91dGlscy9leGVjRmlsZU5vVGhyb3cuanMnXG5pbXBvcnQgeyB0cnVuY2F0ZVRvV2lkdGggfSBmcm9tICcuLi8uLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQgeyBnZXROZXh0UGVybWlzc2lvbk1vZGUgfSBmcm9tICcuLi8uLi91dGlscy9wZXJtaXNzaW9ucy9nZXROZXh0UGVybWlzc2lvbk1vZGUuanMnXG5pbXBvcnQge1xuICBnZXRNb2RlQ29sb3IsXG4gIHR5cGUgUGVybWlzc2lvbk1vZGUsXG4gIHBlcm1pc3Npb25Nb2RlRnJvbVN0cmluZyxcbiAgcGVybWlzc2lvbk1vZGVTeW1ib2wsXG59IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL1Blcm1pc3Npb25Nb2RlLmpzJ1xuaW1wb3J0IHsganNvblN0cmluZ2lmeSB9IGZyb20gJy4uLy4uL3V0aWxzL3Nsb3dPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHtcbiAgSVQyX0NPTU1BTkQsXG4gIGlzSW5zaWRlVG11eFN5bmMsXG59IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL2JhY2tlbmRzL2RldGVjdGlvbi5qcydcbmltcG9ydCB7XG4gIGVuc3VyZUJhY2tlbmRzUmVnaXN0ZXJlZCxcbiAgZ2V0QmFja2VuZEJ5VHlwZSxcbiAgZ2V0Q2FjaGVkQmFja2VuZCxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvc3dhcm0vYmFja2VuZHMvcmVnaXN0cnkuanMnXG5pbXBvcnQgdHlwZSB7IFBhbmVCYWNrZW5kVHlwZSB9IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL2JhY2tlbmRzL3R5cGVzLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0U3dhcm1Tb2NrZXROYW1lLFxuICBUTVVYX0NPTU1BTkQsXG59IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL2NvbnN0YW50cy5qcydcbmltcG9ydCB7XG4gIGFkZEhpZGRlblBhbmVJZCxcbiAgcmVtb3ZlSGlkZGVuUGFuZUlkLFxuICByZW1vdmVNZW1iZXJGcm9tVGVhbSxcbiAgc2V0TWVtYmVyTW9kZSxcbiAgc2V0TXVsdGlwbGVNZW1iZXJNb2Rlcyxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvc3dhcm0vdGVhbUhlbHBlcnMuanMnXG5pbXBvcnQge1xuICBsaXN0VGFza3MsXG4gIHR5cGUgVGFzayxcbiAgdW5hc3NpZ25UZWFtbWF0ZVRhc2tzLFxufSBmcm9tICcuLi8uLi91dGlscy90YXNrcy5qcydcbmltcG9ydCB7XG4gIGdldFRlYW1tYXRlU3RhdHVzZXMsXG4gIHR5cGUgVGVhbW1hdGVTdGF0dXMsXG4gIHR5cGUgVGVhbVN1bW1hcnksXG59IGZyb20gJy4uLy4uL3V0aWxzL3RlYW1EaXNjb3ZlcnkuanMnXG5pbXBvcnQge1xuICBjcmVhdGVNb2RlU2V0UmVxdWVzdE1lc3NhZ2UsXG4gIHNlbmRTaHV0ZG93blJlcXVlc3RUb01haWxib3gsXG4gIHdyaXRlVG9NYWlsYm94LFxufSBmcm9tICcuLi8uLi91dGlscy90ZWFtbWF0ZU1haWxib3guanMnXG5pbXBvcnQgeyBEaWFsb2cgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0RpYWxvZy5qcydcbmltcG9ydCBUaGVtZWRUZXh0IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vVGhlbWVkVGV4dC5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgaW5pdGlhbFRlYW1zPzogVGVhbVN1bW1hcnlbXVxuICBvbkRvbmU6ICgpID0+IHZvaWRcbn1cblxudHlwZSBEaWFsb2dMZXZlbCA9XG4gIHwgeyB0eXBlOiAndGVhbW1hdGVMaXN0JzsgdGVhbU5hbWU6IHN0cmluZyB9XG4gIHwgeyB0eXBlOiAndGVhbW1hdGVEZXRhaWwnOyB0ZWFtTmFtZTogc3RyaW5nOyBtZW1iZXJOYW1lOiBzdHJpbmcgfVxuXG4vKipcbiAqIERpYWxvZyBmb3Igdmlld2luZyB0ZWFtbWF0ZXMgaW4gdGhlIGN1cnJlbnQgdGVhbVxuICovXG5leHBvcnQgZnVuY3Rpb24gVGVhbXNEaWFsb2coeyBpbml0aWFsVGVhbXMsIG9uRG9uZSB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIFJlZ2lzdGVyIGFzIG92ZXJsYXkgc28gQ2FuY2VsUmVxdWVzdEhhbmRsZXIgZG9lc24ndCBpbnRlcmNlcHQgZXNjYXBlXG4gIHVzZVJlZ2lzdGVyT3ZlcmxheSgndGVhbXMtZGlhbG9nJylcblxuICAvLyBpbml0aWFsVGVhbXMgaXMgZGVyaXZlZCBmcm9tIHRlYW1Db250ZXh0IGluIFByb21wdElucHV0IChubyBmaWxlc3lzdGVtIEkvTylcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG5cbiAgLy8gSW5pdGlhbGl6ZSBkaWFsb2dMZXZlbCB3aXRoIGZpcnN0IHRlYW0gbmFtZSBpZiBhdmFpbGFibGVcbiAgY29uc3QgZmlyc3RUZWFtTmFtZSA9IGluaXRpYWxUZWFtcz8uWzBdPy5uYW1lID8/ICcnXG4gIGNvbnN0IFtkaWFsb2dMZXZlbCwgc2V0RGlhbG9nTGV2ZWxdID0gdXNlU3RhdGU8RGlhbG9nTGV2ZWw+KHtcbiAgICB0eXBlOiAndGVhbW1hdGVMaXN0JyxcbiAgICB0ZWFtTmFtZTogZmlyc3RUZWFtTmFtZSxcbiAgfSlcbiAgY29uc3QgW3NlbGVjdGVkSW5kZXgsIHNldFNlbGVjdGVkSW5kZXhdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgW3JlZnJlc2hLZXksIHNldFJlZnJlc2hLZXldID0gdXNlU3RhdGUoMClcblxuICAvLyBpbml0aWFsVGVhbXMgaXMgbm93IGFsd2F5cyBwcm92aWRlZCBmcm9tIFByb21wdElucHV0IChkZXJpdmVkIGZyb20gdGVhbUNvbnRleHQpXG4gIC8vIE5vIGZpbGVzeXN0ZW0gSS9PIG5lZWRlZCBoZXJlXG5cbiAgY29uc3QgdGVhbW1hdGVTdGF0dXNlcyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIHJldHVybiBnZXRUZWFtbWF0ZVN0YXR1c2VzKGRpYWxvZ0xldmVsLnRlYW1OYW1lKVxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZWFjdC1ob29rcy9leGhhdXN0aXZlLWRlcHNcbiAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VFeGhhdXN0aXZlRGVwZW5kZW5jaWVzOiBpbnRlbnRpb25hbFxuICB9LCBbZGlhbG9nTGV2ZWwudGVhbU5hbWUsIHJlZnJlc2hLZXldKVxuXG4gIC8vIFBlcmlvZGljYWxseSByZWZyZXNoIHRvIHBpY2sgdXAgbW9kZSBjaGFuZ2VzIGZyb20gdGVhbW1hdGVzXG4gIHVzZUludGVydmFsKCgpID0+IHtcbiAgICBzZXRSZWZyZXNoS2V5KGsgPT4gayArIDEpXG4gIH0sIDEwMDApXG5cbiAgY29uc3QgY3VycmVudFRlYW1tYXRlID0gdXNlTWVtbygoKSA9PiB7XG4gICAgaWYgKGRpYWxvZ0xldmVsLnR5cGUgIT09ICd0ZWFtbWF0ZURldGFpbCcpIHJldHVybiBudWxsXG4gICAgcmV0dXJuIHRlYW1tYXRlU3RhdHVzZXMuZmluZCh0ID0+IHQubmFtZSA9PT0gZGlhbG9nTGV2ZWwubWVtYmVyTmFtZSkgPz8gbnVsbFxuICB9LCBbZGlhbG9nTGV2ZWwsIHRlYW1tYXRlU3RhdHVzZXNdKVxuXG4gIC8vIEdldCBpc0J5cGFzc1Blcm1pc3Npb25zTW9kZUF2YWlsYWJsZSBmcm9tIEFwcFN0YXRlXG4gIGNvbnN0IGlzQnlwYXNzQXZhaWxhYmxlID0gdXNlQXBwU3RhdGUoXG4gICAgcyA9PiBzLnRvb2xQZXJtaXNzaW9uQ29udGV4dC5pc0J5cGFzc1Blcm1pc3Npb25zTW9kZUF2YWlsYWJsZSxcbiAgKVxuXG4gIGNvbnN0IGdvQmFja1RvTGlzdCA9ICgpOiB2b2lkID0+IHtcbiAgICBzZXREaWFsb2dMZXZlbCh7IHR5cGU6ICd0ZWFtbWF0ZUxpc3QnLCB0ZWFtTmFtZTogZGlhbG9nTGV2ZWwudGVhbU5hbWUgfSlcbiAgICBzZXRTZWxlY3RlZEluZGV4KDApXG4gIH1cblxuICAvLyBIYW5kbGVyIGZvciBjb25maXJtOmN5Y2xlTW9kZSAtIGN5Y2xlIHRlYW1tYXRlIHBlcm1pc3Npb24gbW9kZXNcbiAgY29uc3QgaGFuZGxlQ3ljbGVNb2RlID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVEZXRhaWwnICYmIGN1cnJlbnRUZWFtbWF0ZSkge1xuICAgICAgLy8gRGV0YWlsIHZpZXc6IGN5Y2xlIGp1c3QgdGhpcyB0ZWFtbWF0ZVxuICAgICAgY3ljbGVUZWFtbWF0ZU1vZGUoXG4gICAgICAgIGN1cnJlbnRUZWFtbWF0ZSxcbiAgICAgICAgZGlhbG9nTGV2ZWwudGVhbU5hbWUsXG4gICAgICAgIGlzQnlwYXNzQXZhaWxhYmxlLFxuICAgICAgKVxuICAgICAgc2V0UmVmcmVzaEtleShrID0+IGsgKyAxKVxuICAgIH0gZWxzZSBpZiAoXG4gICAgICBkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVMaXN0JyAmJlxuICAgICAgdGVhbW1hdGVTdGF0dXNlcy5sZW5ndGggPiAwXG4gICAgKSB7XG4gICAgICAvLyBMaXN0IHZpZXc6IGN5Y2xlIGFsbCB0ZWFtbWF0ZXMgaW4gdGFuZGVtXG4gICAgICBjeWNsZUFsbFRlYW1tYXRlTW9kZXMoXG4gICAgICAgIHRlYW1tYXRlU3RhdHVzZXMsXG4gICAgICAgIGRpYWxvZ0xldmVsLnRlYW1OYW1lLFxuICAgICAgICBpc0J5cGFzc0F2YWlsYWJsZSxcbiAgICAgIClcbiAgICAgIHNldFJlZnJlc2hLZXkoayA9PiBrICsgMSlcbiAgICB9XG4gIH0sIFtkaWFsb2dMZXZlbCwgY3VycmVudFRlYW1tYXRlLCB0ZWFtbWF0ZVN0YXR1c2VzLCBpc0J5cGFzc0F2YWlsYWJsZV0pXG5cbiAgLy8gVXNlIGtleWJpbmRpbmdzIGZvciBtb2RlIGN5Y2xpbmdcbiAgdXNlS2V5YmluZGluZ3MoXG4gICAgeyAnY29uZmlybTpjeWNsZU1vZGUnOiBoYW5kbGVDeWNsZU1vZGUgfSxcbiAgICB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nIH0sXG4gIClcblxuICB1c2VJbnB1dCgoaW5wdXQsIGtleSkgPT4ge1xuICAgIC8vIEhhbmRsZSBsZWZ0IGFycm93IHRvIGdvIGJhY2tcbiAgICBpZiAoa2V5LmxlZnRBcnJvdykge1xuICAgICAgaWYgKGRpYWxvZ0xldmVsLnR5cGUgPT09ICd0ZWFtbWF0ZURldGFpbCcpIHtcbiAgICAgICAgZ29CYWNrVG9MaXN0KClcbiAgICAgIH1cbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIEhhbmRsZSB1cC9kb3duIG5hdmlnYXRpb25cbiAgICBpZiAoa2V5LnVwQXJyb3cgfHwga2V5LmRvd25BcnJvdykge1xuICAgICAgY29uc3QgbWF4SW5kZXggPSBnZXRNYXhJbmRleCgpXG4gICAgICBpZiAoa2V5LnVwQXJyb3cpIHtcbiAgICAgICAgc2V0U2VsZWN0ZWRJbmRleChwcmV2ID0+IE1hdGgubWF4KDAsIHByZXYgLSAxKSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNldFNlbGVjdGVkSW5kZXgocHJldiA9PiBNYXRoLm1pbihtYXhJbmRleCwgcHJldiArIDEpKVxuICAgICAgfVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIEVudGVyIHRvIGRyaWxsIGRvd24gb3IgdmlldyBvdXRwdXRcbiAgICBpZiAoa2V5LnJldHVybikge1xuICAgICAgaWYgKFxuICAgICAgICBkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVMaXN0JyAmJlxuICAgICAgICB0ZWFtbWF0ZVN0YXR1c2VzW3NlbGVjdGVkSW5kZXhdXG4gICAgICApIHtcbiAgICAgICAgc2V0RGlhbG9nTGV2ZWwoe1xuICAgICAgICAgIHR5cGU6ICd0ZWFtbWF0ZURldGFpbCcsXG4gICAgICAgICAgdGVhbU5hbWU6IGRpYWxvZ0xldmVsLnRlYW1OYW1lLFxuICAgICAgICAgIG1lbWJlck5hbWU6IHRlYW1tYXRlU3RhdHVzZXNbc2VsZWN0ZWRJbmRleF0ubmFtZSxcbiAgICAgICAgfSlcbiAgICAgIH0gZWxzZSBpZiAoZGlhbG9nTGV2ZWwudHlwZSA9PT0gJ3RlYW1tYXRlRGV0YWlsJyAmJiBjdXJyZW50VGVhbW1hdGUpIHtcbiAgICAgICAgLy8gVmlldyBvdXRwdXQgLSBzd2l0Y2ggdG8gdG11eCBwYW5lXG4gICAgICAgIHZvaWQgdmlld1RlYW1tYXRlT3V0cHV0KFxuICAgICAgICAgIGN1cnJlbnRUZWFtbWF0ZS50bXV4UGFuZUlkLFxuICAgICAgICAgIGN1cnJlbnRUZWFtbWF0ZS5iYWNrZW5kVHlwZSxcbiAgICAgICAgKVxuICAgICAgICBvbkRvbmUoKVxuICAgICAgfVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gSGFuZGxlICdrJyB0byBraWxsIHRlYW1tYXRlXG4gICAgaWYgKGlucHV0ID09PSAnaycpIHtcbiAgICAgIGlmIChcbiAgICAgICAgZGlhbG9nTGV2ZWwudHlwZSA9PT0gJ3RlYW1tYXRlTGlzdCcgJiZcbiAgICAgICAgdGVhbW1hdGVTdGF0dXNlc1tzZWxlY3RlZEluZGV4XVxuICAgICAgKSB7XG4gICAgICAgIHZvaWQga2lsbFRlYW1tYXRlKFxuICAgICAgICAgIHRlYW1tYXRlU3RhdHVzZXNbc2VsZWN0ZWRJbmRleF0udG11eFBhbmVJZCxcbiAgICAgICAgICB0ZWFtbWF0ZVN0YXR1c2VzW3NlbGVjdGVkSW5kZXhdLmJhY2tlbmRUeXBlLFxuICAgICAgICAgIGRpYWxvZ0xldmVsLnRlYW1OYW1lLFxuICAgICAgICAgIHRlYW1tYXRlU3RhdHVzZXNbc2VsZWN0ZWRJbmRleF0uYWdlbnRJZCxcbiAgICAgICAgICB0ZWFtbWF0ZVN0YXR1c2VzW3NlbGVjdGVkSW5kZXhdLm5hbWUsXG4gICAgICAgICAgc2V0QXBwU3RhdGUsXG4gICAgICAgICkudGhlbigoKSA9PiB7XG4gICAgICAgICAgc2V0UmVmcmVzaEtleShrID0+IGsgKyAxKVxuICAgICAgICAgIC8vIEFkanVzdCBzZWxlY3Rpb24gaWYgbmVlZGVkXG4gICAgICAgICAgc2V0U2VsZWN0ZWRJbmRleChwcmV2ID0+XG4gICAgICAgICAgICBNYXRoLm1heCgwLCBNYXRoLm1pbihwcmV2LCB0ZWFtbWF0ZVN0YXR1c2VzLmxlbmd0aCAtIDIpKSxcbiAgICAgICAgICApXG4gICAgICAgIH0pXG4gICAgICB9IGVsc2UgaWYgKGRpYWxvZ0xldmVsLnR5cGUgPT09ICd0ZWFtbWF0ZURldGFpbCcgJiYgY3VycmVudFRlYW1tYXRlKSB7XG4gICAgICAgIHZvaWQga2lsbFRlYW1tYXRlKFxuICAgICAgICAgIGN1cnJlbnRUZWFtbWF0ZS50bXV4UGFuZUlkLFxuICAgICAgICAgIGN1cnJlbnRUZWFtbWF0ZS5iYWNrZW5kVHlwZSxcbiAgICAgICAgICBkaWFsb2dMZXZlbC50ZWFtTmFtZSxcbiAgICAgICAgICBjdXJyZW50VGVhbW1hdGUuYWdlbnRJZCxcbiAgICAgICAgICBjdXJyZW50VGVhbW1hdGUubmFtZSxcbiAgICAgICAgICBzZXRBcHBTdGF0ZSxcbiAgICAgICAgKVxuICAgICAgICBnb0JhY2tUb0xpc3QoKVxuICAgICAgfVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gSGFuZGxlICdzJyBmb3Igc2h1dGRvd24gb2Ygc2VsZWN0ZWQgdGVhbW1hdGVcbiAgICBpZiAoaW5wdXQgPT09ICdzJykge1xuICAgICAgaWYgKFxuICAgICAgICBkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVMaXN0JyAmJlxuICAgICAgICB0ZWFtbWF0ZVN0YXR1c2VzW3NlbGVjdGVkSW5kZXhdXG4gICAgICApIHtcbiAgICAgICAgY29uc3QgdGVhbW1hdGUgPSB0ZWFtbWF0ZVN0YXR1c2VzW3NlbGVjdGVkSW5kZXhdXG4gICAgICAgIHZvaWQgc2VuZFNodXRkb3duUmVxdWVzdFRvTWFpbGJveChcbiAgICAgICAgICB0ZWFtbWF0ZS5uYW1lLFxuICAgICAgICAgIGRpYWxvZ0xldmVsLnRlYW1OYW1lLFxuICAgICAgICAgICdHcmFjZWZ1bCBzaHV0ZG93biByZXF1ZXN0ZWQgYnkgdGVhbSBsZWFkJyxcbiAgICAgICAgKVxuICAgICAgfSBlbHNlIGlmIChkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVEZXRhaWwnICYmIGN1cnJlbnRUZWFtbWF0ZSkge1xuICAgICAgICB2b2lkIHNlbmRTaHV0ZG93blJlcXVlc3RUb01haWxib3goXG4gICAgICAgICAgY3VycmVudFRlYW1tYXRlLm5hbWUsXG4gICAgICAgICAgZGlhbG9nTGV2ZWwudGVhbU5hbWUsXG4gICAgICAgICAgJ0dyYWNlZnVsIHNodXRkb3duIHJlcXVlc3RlZCBieSB0ZWFtIGxlYWQnLFxuICAgICAgICApXG4gICAgICAgIGdvQmFja1RvTGlzdCgpXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgJ2gnIHRvIGhpZGUvc2hvdyBpbmRpdmlkdWFsIHRlYW1tYXRlIChvbmx5IGZvciBiYWNrZW5kcyB0aGF0IHN1cHBvcnQgaXQpXG4gICAgaWYgKGlucHV0ID09PSAnaCcpIHtcbiAgICAgIGNvbnN0IGJhY2tlbmQgPSBnZXRDYWNoZWRCYWNrZW5kKClcbiAgICAgIGNvbnN0IHRlYW1tYXRlID1cbiAgICAgICAgZGlhbG9nTGV2ZWwudHlwZSA9PT0gJ3RlYW1tYXRlTGlzdCdcbiAgICAgICAgICA/IHRlYW1tYXRlU3RhdHVzZXNbc2VsZWN0ZWRJbmRleF1cbiAgICAgICAgICA6IGRpYWxvZ0xldmVsLnR5cGUgPT09ICd0ZWFtbWF0ZURldGFpbCdcbiAgICAgICAgICAgID8gY3VycmVudFRlYW1tYXRlXG4gICAgICAgICAgICA6IG51bGxcblxuICAgICAgaWYgKHRlYW1tYXRlICYmIGJhY2tlbmQ/LnN1cHBvcnRzSGlkZVNob3cpIHtcbiAgICAgICAgdm9pZCB0b2dnbGVUZWFtbWF0ZVZpc2liaWxpdHkodGVhbW1hdGUsIGRpYWxvZ0xldmVsLnRlYW1OYW1lKS50aGVuKFxuICAgICAgICAgICgpID0+IHtcbiAgICAgICAgICAgIC8vIEZvcmNlIHJlZnJlc2ggb2YgdGVhbW1hdGUgc3RhdHVzZXNcbiAgICAgICAgICAgIHNldFJlZnJlc2hLZXkoayA9PiBrICsgMSlcbiAgICAgICAgICB9LFxuICAgICAgICApXG4gICAgICAgIGlmIChkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVEZXRhaWwnKSB7XG4gICAgICAgICAgZ29CYWNrVG9MaXN0KClcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gSGFuZGxlICdIJyB0byBoaWRlL3Nob3cgYWxsIHRlYW1tYXRlcyAob25seSBmb3IgYmFja2VuZHMgdGhhdCBzdXBwb3J0IGl0KVxuICAgIGlmIChpbnB1dCA9PT0gJ0gnICYmIGRpYWxvZ0xldmVsLnR5cGUgPT09ICd0ZWFtbWF0ZUxpc3QnKSB7XG4gICAgICBjb25zdCBiYWNrZW5kID0gZ2V0Q2FjaGVkQmFja2VuZCgpXG4gICAgICBpZiAoYmFja2VuZD8uc3VwcG9ydHNIaWRlU2hvdyAmJiB0ZWFtbWF0ZVN0YXR1c2VzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgLy8gSWYgYW55IGFyZSB2aXNpYmxlLCBoaWRlIGFsbC4gT3RoZXJ3aXNlLCBzaG93IGFsbC5cbiAgICAgICAgY29uc3QgYW55VmlzaWJsZSA9IHRlYW1tYXRlU3RhdHVzZXMuc29tZSh0ID0+ICF0LmlzSGlkZGVuKVxuICAgICAgICB2b2lkIFByb21pc2UuYWxsKFxuICAgICAgICAgIHRlYW1tYXRlU3RhdHVzZXMubWFwKHQgPT5cbiAgICAgICAgICAgIGFueVZpc2libGVcbiAgICAgICAgICAgICAgPyBoaWRlVGVhbW1hdGUodCwgZGlhbG9nTGV2ZWwudGVhbU5hbWUpXG4gICAgICAgICAgICAgIDogc2hvd1RlYW1tYXRlKHQsIGRpYWxvZ0xldmVsLnRlYW1OYW1lKSxcbiAgICAgICAgICApLFxuICAgICAgICApLnRoZW4oKCkgPT4ge1xuICAgICAgICAgIC8vIEZvcmNlIHJlZnJlc2ggb2YgdGVhbW1hdGUgc3RhdHVzZXNcbiAgICAgICAgICBzZXRSZWZyZXNoS2V5KGsgPT4gayArIDEpXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgJ3AnIHRvIHBydW5lIChraWxsKSBhbGwgaWRsZSB0ZWFtbWF0ZXNcbiAgICBpZiAoaW5wdXQgPT09ICdwJyAmJiBkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVMaXN0Jykge1xuICAgICAgY29uc3QgaWRsZVRlYW1tYXRlcyA9IHRlYW1tYXRlU3RhdHVzZXMuZmlsdGVyKHQgPT4gdC5zdGF0dXMgPT09ICdpZGxlJylcbiAgICAgIGlmIChpZGxlVGVhbW1hdGVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgdm9pZCBQcm9taXNlLmFsbChcbiAgICAgICAgICBpZGxlVGVhbW1hdGVzLm1hcCh0ID0+XG4gICAgICAgICAgICBraWxsVGVhbW1hdGUoXG4gICAgICAgICAgICAgIHQudG11eFBhbmVJZCxcbiAgICAgICAgICAgICAgdC5iYWNrZW5kVHlwZSxcbiAgICAgICAgICAgICAgZGlhbG9nTGV2ZWwudGVhbU5hbWUsXG4gICAgICAgICAgICAgIHQuYWdlbnRJZCxcbiAgICAgICAgICAgICAgdC5uYW1lLFxuICAgICAgICAgICAgICBzZXRBcHBTdGF0ZSxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKSxcbiAgICAgICAgKS50aGVuKCgpID0+IHtcbiAgICAgICAgICBzZXRSZWZyZXNoS2V5KGsgPT4gayArIDEpXG4gICAgICAgICAgc2V0U2VsZWN0ZWRJbmRleChwcmV2ID0+XG4gICAgICAgICAgICBNYXRoLm1heChcbiAgICAgICAgICAgICAgMCxcbiAgICAgICAgICAgICAgTWF0aC5taW4oXG4gICAgICAgICAgICAgICAgcHJldixcbiAgICAgICAgICAgICAgICB0ZWFtbWF0ZVN0YXR1c2VzLmxlbmd0aCAtIGlkbGVUZWFtbWF0ZXMubGVuZ3RoIC0gMSxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKVxuICAgICAgICB9KVxuICAgICAgfVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gTm90ZTogTW9kZSBjeWNsaW5nIChzaGlmdCt0YWIpIGlzIGhhbmRsZWQgdmlhIHVzZUtleWJpbmRpbmdzIHdpdGggY29uZmlybTpjeWNsZU1vZGUgYWN0aW9uXG4gIH0pXG5cbiAgZnVuY3Rpb24gZ2V0TWF4SW5kZXgoKTogbnVtYmVyIHtcbiAgICBpZiAoZGlhbG9nTGV2ZWwudHlwZSA9PT0gJ3RlYW1tYXRlTGlzdCcpIHtcbiAgICAgIHJldHVybiBNYXRoLm1heCgwLCB0ZWFtbWF0ZVN0YXR1c2VzLmxlbmd0aCAtIDEpXG4gICAgfVxuICAgIHJldHVybiAwXG4gIH1cblxuICAvLyBSZW5kZXIgYmFzZWQgb24gZGlhbG9nIGxldmVsXG4gIGlmIChkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVMaXN0Jykge1xuICAgIHJldHVybiAoXG4gICAgICA8VGVhbURldGFpbFZpZXdcbiAgICAgICAgdGVhbU5hbWU9e2RpYWxvZ0xldmVsLnRlYW1OYW1lfVxuICAgICAgICB0ZWFtbWF0ZXM9e3RlYW1tYXRlU3RhdHVzZXN9XG4gICAgICAgIHNlbGVjdGVkSW5kZXg9e3NlbGVjdGVkSW5kZXh9XG4gICAgICAgIG9uQ2FuY2VsPXtvbkRvbmV9XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIGlmIChkaWFsb2dMZXZlbC50eXBlID09PSAndGVhbW1hdGVEZXRhaWwnICYmIGN1cnJlbnRUZWFtbWF0ZSkge1xuICAgIHJldHVybiAoXG4gICAgICA8VGVhbW1hdGVEZXRhaWxWaWV3XG4gICAgICAgIHRlYW1tYXRlPXtjdXJyZW50VGVhbW1hdGV9XG4gICAgICAgIHRlYW1OYW1lPXtkaWFsb2dMZXZlbC50ZWFtTmFtZX1cbiAgICAgICAgb25DYW5jZWw9e2dvQmFja1RvTGlzdH1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIG51bGxcbn1cblxudHlwZSBUZWFtRGV0YWlsVmlld1Byb3BzID0ge1xuICB0ZWFtTmFtZTogc3RyaW5nXG4gIHRlYW1tYXRlczogVGVhbW1hdGVTdGF0dXNbXVxuICBzZWxlY3RlZEluZGV4OiBudW1iZXJcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbn1cblxuZnVuY3Rpb24gVGVhbURldGFpbFZpZXcoe1xuICB0ZWFtTmFtZSxcbiAgdGVhbW1hdGVzLFxuICBzZWxlY3RlZEluZGV4LFxuICBvbkNhbmNlbCxcbn06IFRlYW1EZXRhaWxWaWV3UHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBzdWJ0aXRsZSA9IGAke3RlYW1tYXRlcy5sZW5ndGh9ICR7dGVhbW1hdGVzLmxlbmd0aCA9PT0gMSA/ICd0ZWFtbWF0ZScgOiAndGVhbW1hdGVzJ31gXG4gIC8vIENoZWNrIGlmIHRoZSBiYWNrZW5kIHN1cHBvcnRzIGhpZGUvc2hvd1xuICBjb25zdCBzdXBwb3J0c0hpZGVTaG93ID0gZ2V0Q2FjaGVkQmFja2VuZCgpPy5zdXBwb3J0c0hpZGVTaG93ID8/IGZhbHNlXG4gIC8vIEdldCB0aGUgZGlzcGxheSB0ZXh0IGZvciB0aGUgY3ljbGUgbW9kZSBzaG9ydGN1dFxuICBjb25zdCBjeWNsZU1vZGVTaG9ydGN1dCA9IHVzZVNob3J0Y3V0RGlzcGxheShcbiAgICAnY29uZmlybTpjeWNsZU1vZGUnLFxuICAgICdDb25maXJtYXRpb24nLFxuICAgICdzaGlmdCt0YWInLFxuICApXG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT17YFRlYW0gJHt0ZWFtTmFtZX1gfVxuICAgICAgICBzdWJ0aXRsZT17c3VidGl0bGV9XG4gICAgICAgIG9uQ2FuY2VsPXtvbkNhbmNlbH1cbiAgICAgICAgY29sb3I9XCJiYWNrZ3JvdW5kXCJcbiAgICAgICAgaGlkZUlucHV0R3VpZGVcbiAgICAgID5cbiAgICAgICAge3RlYW1tYXRlcy5sZW5ndGggPT09IDAgPyAoXG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+Tm8gdGVhbW1hdGVzPC9UZXh0PlxuICAgICAgICApIDogKFxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAge3RlYW1tYXRlcy5tYXAoKHRlYW1tYXRlLCBpbmRleCkgPT4gKFxuICAgICAgICAgICAgICA8VGVhbW1hdGVMaXN0SXRlbVxuICAgICAgICAgICAgICAgIGtleT17dGVhbW1hdGUuYWdlbnRJZH1cbiAgICAgICAgICAgICAgICB0ZWFtbWF0ZT17dGVhbW1hdGV9XG4gICAgICAgICAgICAgICAgaXNTZWxlY3RlZD17aW5kZXggPT09IHNlbGVjdGVkSW5kZXh9XG4gICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICApKX1cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgIDwvRGlhbG9nPlxuICAgICAgPEJveCBtYXJnaW5MZWZ0PXsxfT5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAge2ZpZ3VyZXMuYXJyb3dVcH0ve2ZpZ3VyZXMuYXJyb3dEb3dufSBzZWxlY3QgwrcgRW50ZXIgdmlldyDCtyBrIGtpbGwgwrcgc1xuICAgICAgICAgIHNodXRkb3duIMK3IHAgcHJ1bmUgaWRsZVxuICAgICAgICAgIHtzdXBwb3J0c0hpZGVTaG93ICYmICcgwrcgaCBoaWRlL3Nob3cgwrcgSCBoaWRlL3Nob3cgYWxsJ31cbiAgICAgICAgICB7JyDCtyAnfVxuICAgICAgICAgIHtjeWNsZU1vZGVTaG9ydGN1dH0gc3luYyBjeWNsZSBtb2RlcyBmb3IgYWxsIMK3IEVzYyBjbG9zZVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8Lz5cbiAgKVxufVxuXG50eXBlIFRlYW1tYXRlTGlzdEl0ZW1Qcm9wcyA9IHtcbiAgdGVhbW1hdGU6IFRlYW1tYXRlU3RhdHVzXG4gIGlzU2VsZWN0ZWQ6IGJvb2xlYW5cbn1cblxuZnVuY3Rpb24gVGVhbW1hdGVMaXN0SXRlbSh7XG4gIHRlYW1tYXRlLFxuICBpc1NlbGVjdGVkLFxufTogVGVhbW1hdGVMaXN0SXRlbVByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgaXNJZGxlID0gdGVhbW1hdGUuc3RhdHVzID09PSAnaWRsZSdcbiAgLy8gT25seSBkaW0gaWYgaWRsZSBBTkQgbm90IHNlbGVjdGVkIC0gc2VsZWN0aW9uIGhpZ2hsaWdodGluZyB0YWtlcyBwcmVjZWRlbmNlXG4gIGNvbnN0IHNob3VsZERpbSA9IGlzSWRsZSAmJiAhaXNTZWxlY3RlZFxuXG4gIC8vIEdldCBtb2RlIGRpc3BsYXlcbiAgY29uc3QgbW9kZSA9IHRlYW1tYXRlLm1vZGVcbiAgICA/IHBlcm1pc3Npb25Nb2RlRnJvbVN0cmluZyh0ZWFtbWF0ZS5tb2RlKVxuICAgIDogJ2RlZmF1bHQnXG4gIGNvbnN0IG1vZGVTeW1ib2wgPSBwZXJtaXNzaW9uTW9kZVN5bWJvbChtb2RlKVxuICBjb25zdCBtb2RlQ29sb3IgPSBnZXRNb2RlQ29sb3IobW9kZSlcblxuICByZXR1cm4gKFxuICAgIDxUZXh0IGNvbG9yPXtpc1NlbGVjdGVkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfSBkaW1Db2xvcj17c2hvdWxkRGltfT5cbiAgICAgIHtpc1NlbGVjdGVkID8gZmlndXJlcy5wb2ludGVyICsgJyAnIDogJyAgJ31cbiAgICAgIHt0ZWFtbWF0ZS5pc0hpZGRlbiAmJiA8VGV4dCBkaW1Db2xvcj5baGlkZGVuXSA8L1RleHQ+fVxuICAgICAge2lzSWRsZSAmJiA8VGV4dCBkaW1Db2xvcj5baWRsZV0gPC9UZXh0Pn1cbiAgICAgIHttb2RlU3ltYm9sICYmIDxUZXh0IGNvbG9yPXttb2RlQ29sb3J9Pnttb2RlU3ltYm9sfSA8L1RleHQ+fUBcbiAgICAgIHt0ZWFtbWF0ZS5uYW1lfVxuICAgICAge3RlYW1tYXRlLm1vZGVsICYmIDxUZXh0IGRpbUNvbG9yPiAoe3RlYW1tYXRlLm1vZGVsfSk8L1RleHQ+fVxuICAgIDwvVGV4dD5cbiAgKVxufVxuXG50eXBlIFRlYW1tYXRlRGV0YWlsVmlld1Byb3BzID0ge1xuICB0ZWFtbWF0ZTogVGVhbW1hdGVTdGF0dXNcbiAgdGVhbU5hbWU6IHN0cmluZ1xuICBvbkNhbmNlbDogKCkgPT4gdm9pZFxufVxuXG5mdW5jdGlvbiBUZWFtbWF0ZURldGFpbFZpZXcoe1xuICB0ZWFtbWF0ZSxcbiAgdGVhbU5hbWUsXG4gIG9uQ2FuY2VsLFxufTogVGVhbW1hdGVEZXRhaWxWaWV3UHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbcHJvbXB0RXhwYW5kZWQsIHNldFByb21wdEV4cGFuZGVkXSA9IHVzZVN0YXRlKGZhbHNlKVxuICAvLyBHZXQgdGhlIGRpc3BsYXkgdGV4dCBmb3IgdGhlIGN5Y2xlIG1vZGUgc2hvcnRjdXRcbiAgY29uc3QgY3ljbGVNb2RlU2hvcnRjdXQgPSB1c2VTaG9ydGN1dERpc3BsYXkoXG4gICAgJ2NvbmZpcm06Y3ljbGVNb2RlJyxcbiAgICAnQ29uZmlybWF0aW9uJyxcbiAgICAnc2hpZnQrdGFiJyxcbiAgKVxuICBjb25zdCB0aGVtZUNvbG9yID0gdGVhbW1hdGUuY29sb3JcbiAgICA/IEFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SW1xuICAgICAgICB0ZWFtbWF0ZS5jb2xvciBhcyBrZXlvZiB0eXBlb2YgQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1JcbiAgICAgIF1cbiAgICA6IHVuZGVmaW5lZFxuXG4gIC8vIEdldCB0YXNrcyBhc3NpZ25lZCB0byB0aGlzIHRlYW1tYXRlXG4gIGNvbnN0IFt0ZWFtbWF0ZVRhc2tzLCBzZXRUZWFtbWF0ZVRhc2tzXSA9IHVzZVN0YXRlPFRhc2tbXT4oW10pXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgbGV0IGNhbmNlbGxlZCA9IGZhbHNlXG4gICAgdm9pZCBsaXN0VGFza3ModGVhbU5hbWUpLnRoZW4oYWxsVGFza3MgPT4ge1xuICAgICAgaWYgKGNhbmNlbGxlZCkgcmV0dXJuXG4gICAgICAvLyBGaWx0ZXIgdGFza3Mgb3duZWQgYnkgdGhpcyB0ZWFtbWF0ZSAoYnkgYWdlbnRJZCBvciBuYW1lKVxuICAgICAgc2V0VGVhbW1hdGVUYXNrcyhcbiAgICAgICAgYWxsVGFza3MuZmlsdGVyKFxuICAgICAgICAgIHRhc2sgPT5cbiAgICAgICAgICAgIHRhc2sub3duZXIgPT09IHRlYW1tYXRlLmFnZW50SWQgfHwgdGFzay5vd25lciA9PT0gdGVhbW1hdGUubmFtZSxcbiAgICAgICAgKSxcbiAgICAgIClcbiAgICB9KVxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICBjYW5jZWxsZWQgPSB0cnVlXG4gICAgfVxuICB9LCBbdGVhbU5hbWUsIHRlYW1tYXRlLmFnZW50SWQsIHRlYW1tYXRlLm5hbWVdKVxuXG4gIHVzZUlucHV0KGlucHV0ID0+IHtcbiAgICAvLyBIYW5kbGUgJ3AnIHRvIGV4cGFuZC9jb2xsYXBzZSBwcm9tcHRcbiAgICBpZiAoaW5wdXQgPT09ICdwJykge1xuICAgICAgc2V0UHJvbXB0RXhwYW5kZWQocHJldiA9PiAhcHJldilcbiAgICB9XG4gIH0pXG5cbiAgLy8gRGV0ZXJtaW5lIHdvcmtpbmcgZGlyZWN0b3J5IGRpc3BsYXlcbiAgY29uc3Qgd29ya2luZ1BhdGggPSB0ZWFtbWF0ZS53b3JrdHJlZVBhdGggfHwgdGVhbW1hdGUuY3dkXG5cbiAgLy8gQnVpbGQgc3VidGl0bGUgd2l0aCBtZXRhZGF0YVxuICBjb25zdCBzdWJ0aXRsZVBhcnRzOiBzdHJpbmdbXSA9IFtdXG4gIGlmICh0ZWFtbWF0ZS5tb2RlbCkgc3VidGl0bGVQYXJ0cy5wdXNoKHRlYW1tYXRlLm1vZGVsKVxuICBpZiAod29ya2luZ1BhdGgpIHtcbiAgICBzdWJ0aXRsZVBhcnRzLnB1c2goXG4gICAgICB0ZWFtbWF0ZS53b3JrdHJlZVBhdGggPyBgd29ya3RyZWU6ICR7d29ya2luZ1BhdGh9YCA6IHdvcmtpbmdQYXRoLFxuICAgIClcbiAgfVxuICBjb25zdCBzdWJ0aXRsZSA9IHN1YnRpdGxlUGFydHMuam9pbignIMK3ICcpIHx8IHVuZGVmaW5lZFxuXG4gIC8vIEdldCBtb2RlIGRpc3BsYXkgZm9yIHRpdGxlXG4gIGNvbnN0IG1vZGUgPSB0ZWFtbWF0ZS5tb2RlXG4gICAgPyBwZXJtaXNzaW9uTW9kZUZyb21TdHJpbmcodGVhbW1hdGUubW9kZSlcbiAgICA6ICdkZWZhdWx0J1xuICBjb25zdCBtb2RlU3ltYm9sID0gcGVybWlzc2lvbk1vZGVTeW1ib2wobW9kZSlcbiAgY29uc3QgbW9kZUNvbG9yID0gZ2V0TW9kZUNvbG9yKG1vZGUpXG5cbiAgLy8gQnVpbGQgdGl0bGUgd2l0aCBtb2RlIHN5bWJvbCBhbmQgY29sb3JlZCBuYW1lIGlmIGFwcGxpY2FibGVcbiAgY29uc3QgdGl0bGUgPSAoXG4gICAgPD5cbiAgICAgIHttb2RlU3ltYm9sICYmIDxUZXh0IGNvbG9yPXttb2RlQ29sb3J9Pnttb2RlU3ltYm9sfSA8L1RleHQ+fVxuICAgICAge3RoZW1lQ29sb3IgPyAoXG4gICAgICAgIDxUaGVtZWRUZXh0IGNvbG9yPXt0aGVtZUNvbG9yfT57YEAke3RlYW1tYXRlLm5hbWV9YH08L1RoZW1lZFRleHQ+XG4gICAgICApIDogKFxuICAgICAgICBgQCR7dGVhbW1hdGUubmFtZX1gXG4gICAgICApfVxuICAgIDwvPlxuICApXG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPERpYWxvZ1xuICAgICAgICB0aXRsZT17dGl0bGV9XG4gICAgICAgIHN1YnRpdGxlPXtzdWJ0aXRsZX1cbiAgICAgICAgb25DYW5jZWw9e29uQ2FuY2VsfVxuICAgICAgICBjb2xvcj1cImJhY2tncm91bmRcIlxuICAgICAgICBoaWRlSW5wdXRHdWlkZVxuICAgICAgPlxuICAgICAgICB7LyogVGFza3Mgc2VjdGlvbiAqL31cbiAgICAgICAge3RlYW1tYXRlVGFza3MubGVuZ3RoID4gMCAmJiAoXG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICAgICAgICA8VGV4dCBib2xkPlRhc2tzPC9UZXh0PlxuICAgICAgICAgICAge3RlYW1tYXRlVGFza3MubWFwKHRhc2sgPT4gKFxuICAgICAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgICAgIGtleT17dGFzay5pZH1cbiAgICAgICAgICAgICAgICBjb2xvcj17dGFzay5zdGF0dXMgPT09ICdjb21wbGV0ZWQnID8gJ3N1Y2Nlc3MnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAge3Rhc2suc3RhdHVzID09PSAnY29tcGxldGVkJyA/IGZpZ3VyZXMudGljayA6ICfil7wnfXsnICd9XG4gICAgICAgICAgICAgICAge3Rhc2suc3ViamVjdH1cbiAgICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgKSl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICl9XG5cbiAgICAgICAgey8qIFByb21wdCBzZWN0aW9uICovfVxuICAgICAgICB7dGVhbW1hdGUucHJvbXB0ICYmIChcbiAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICAgIDxUZXh0IGJvbGQ+UHJvbXB0PC9UZXh0PlxuICAgICAgICAgICAgPFRleHQ+XG4gICAgICAgICAgICAgIHtwcm9tcHRFeHBhbmRlZFxuICAgICAgICAgICAgICAgID8gdGVhbW1hdGUucHJvbXB0XG4gICAgICAgICAgICAgICAgOiB0cnVuY2F0ZVRvV2lkdGgodGVhbW1hdGUucHJvbXB0LCA4MCl9XG4gICAgICAgICAgICAgIHtzdHJpbmdXaWR0aCh0ZWFtbWF0ZS5wcm9tcHQpID4gODAgJiYgIXByb21wdEV4cGFuZGVkICYmIChcbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj4gKHAgdG8gZXhwYW5kKTwvVGV4dD5cbiAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgKX1cbiAgICAgIDwvRGlhbG9nPlxuICAgICAgPEJveCBtYXJnaW5MZWZ0PXsxfT5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAge2ZpZ3VyZXMuYXJyb3dMZWZ0fSBiYWNrIMK3IEVzYyBjbG9zZSDCtyBrIGtpbGwgwrcgcyBzaHV0ZG93blxuICAgICAgICAgIHtnZXRDYWNoZWRCYWNrZW5kKCk/LnN1cHBvcnRzSGlkZVNob3cgJiYgJyDCtyBoIGhpZGUvc2hvdyd9XG4gICAgICAgICAgeycgwrcgJ31cbiAgICAgICAgICB7Y3ljbGVNb2RlU2hvcnRjdXR9IGN5Y2xlIG1vZGVcbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgPC8+XG4gIClcbn1cblxuYXN5bmMgZnVuY3Rpb24ga2lsbFRlYW1tYXRlKFxuICBwYW5lSWQ6IHN0cmluZyxcbiAgYmFja2VuZFR5cGU6IFBhbmVCYWNrZW5kVHlwZSB8IHVuZGVmaW5lZCxcbiAgdGVhbU5hbWU6IHN0cmluZyxcbiAgdGVhbW1hdGVJZDogc3RyaW5nLFxuICB0ZWFtbWF0ZU5hbWU6IHN0cmluZyxcbiAgc2V0QXBwU3RhdGU6IChmOiAocHJldjogQXBwU3RhdGUpID0+IEFwcFN0YXRlKSA9PiB2b2lkLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIC8vIEtpbGwgdGhlIHBhbmUgdXNpbmcgdGhlIGJhY2tlbmQgdGhhdCBjcmVhdGVkIGl0IChoYW5kbGVzIC1zIC8gLUwgZmxhZ3MgY29ycmVjdGx5KS5cbiAgLy8gV3JhcHBlZCBpbiB0cnkvY2F0Y2ggc28gY2xlYW51cCAocmVtb3ZlTWVtYmVyRnJvbVRlYW0sIHVuYXNzaWduVGVhbW1hdGVUYXNrcyxcbiAgLy8gc2V0QXBwU3RhdGUpIGFsd2F5cyBydW5zIOKAlCBtYXRjaGVzIHVzZUluYm94UG9sbGVyLnRzIGVycm9yIGlzb2xhdGlvbi5cbiAgaWYgKGJhY2tlbmRUeXBlKSB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIFVzZSBlbnN1cmVCYWNrZW5kc1JlZ2lzdGVyZWQgKG5vdCBkZXRlY3RBbmRHZXRCYWNrZW5kKSDigJQgdGhpcyBwcm9jZXNzIG1heVxuICAgICAgLy8gYmUgYSB0ZWFtbWF0ZSB0aGF0IG5ldmVyIHJhbiBkZXRlY3Rpb24sIGJ1dCB3ZSBvbmx5IG5lZWQgY2xhc3MgaW1wb3J0c1xuICAgICAgLy8gaGVyZSwgbm90IHN1YnByb2Nlc3MgcHJvYmVzIHRoYXQgY291bGQgdGhyb3cgaW4gYSBkaWZmZXJlbnQgZW52aXJvbm1lbnQuXG4gICAgICBhd2FpdCBlbnN1cmVCYWNrZW5kc1JlZ2lzdGVyZWQoKVxuICAgICAgYXdhaXQgZ2V0QmFja2VuZEJ5VHlwZShiYWNrZW5kVHlwZSkua2lsbFBhbmUocGFuZUlkLCAhaXNJbnNpZGVUbXV4U3luYygpKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoYFtUZWFtc0RpYWxvZ10gRmFpbGVkIHRvIGtpbGwgcGFuZSAke3BhbmVJZH06ICR7ZXJyb3J9YClcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgLy8gYmFja2VuZFR5cGUgdW5kZWZpbmVkOiBvbGQgdGVhbSBmaWxlcyBwcmVkYXRpbmcgdGhpcyBmaWVsZCwgb3IgaW4tcHJvY2Vzcy5cbiAgICAvLyBPbGQgdG11eC1maWxlIGNhc2UgaXMgYSBtaWdyYXRpb24gZ2FwIOKAlCB0aGUgcGFuZSBpcyBvcnBoYW5lZC4gSW4tcHJvY2Vzc1xuICAgIC8vIHRlYW1tYXRlcyBoYXZlIG5vIHBhbmUgdG8ga2lsbCwgc28gdGhpcyBpcyBjb3JyZWN0IGZvciB0aGVtLlxuICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgIGBbVGVhbXNEaWFsb2ddIFNraXBwaW5nIHBhbmUga2lsbCBmb3IgJHtwYW5lSWR9OiBubyBiYWNrZW5kVHlwZSByZWNvcmRlZGAsXG4gICAgKVxuICB9XG4gIC8vIFJlbW92ZSBmcm9tIHRlYW0gY29uZmlnIGZpbGVcbiAgcmVtb3ZlTWVtYmVyRnJvbVRlYW0odGVhbU5hbWUsIHBhbmVJZClcblxuICAvLyBVbmFzc2lnbiB0YXNrcyBhbmQgYnVpbGQgbm90aWZpY2F0aW9uIG1lc3NhZ2VcbiAgY29uc3QgeyBub3RpZmljYXRpb25NZXNzYWdlIH0gPSBhd2FpdCB1bmFzc2lnblRlYW1tYXRlVGFza3MoXG4gICAgdGVhbU5hbWUsXG4gICAgdGVhbW1hdGVJZCxcbiAgICB0ZWFtbWF0ZU5hbWUsXG4gICAgJ3Rlcm1pbmF0ZWQnLFxuICApXG5cbiAgLy8gVXBkYXRlIEFwcFN0YXRlIHRvIGtlZXAgc3RhdHVzIGxpbmUgaW4gc3luYyBhbmQgbm90aWZ5IHRoZSBsZWFkXG4gIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgIGlmICghcHJldi50ZWFtQ29udGV4dD8udGVhbW1hdGVzKSByZXR1cm4gcHJldlxuICAgIGlmICghKHRlYW1tYXRlSWQgaW4gcHJldi50ZWFtQ29udGV4dC50ZWFtbWF0ZXMpKSByZXR1cm4gcHJldlxuICAgIGNvbnN0IHsgW3RlYW1tYXRlSWRdOiBfLCAuLi5yZW1haW5pbmdUZWFtbWF0ZXMgfSA9XG4gICAgICBwcmV2LnRlYW1Db250ZXh0LnRlYW1tYXRlc1xuICAgIHJldHVybiB7XG4gICAgICAuLi5wcmV2LFxuICAgICAgdGVhbUNvbnRleHQ6IHtcbiAgICAgICAgLi4ucHJldi50ZWFtQ29udGV4dCxcbiAgICAgICAgdGVhbW1hdGVzOiByZW1haW5pbmdUZWFtbWF0ZXMsXG4gICAgICB9LFxuICAgICAgaW5ib3g6IHtcbiAgICAgICAgbWVzc2FnZXM6IFtcbiAgICAgICAgICAuLi5wcmV2LmluYm94Lm1lc3NhZ2VzLFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGlkOiByYW5kb21VVUlEKCksXG4gICAgICAgICAgICBmcm9tOiAnc3lzdGVtJyxcbiAgICAgICAgICAgIHRleHQ6IGpzb25TdHJpbmdpZnkoe1xuICAgICAgICAgICAgICB0eXBlOiAndGVhbW1hdGVfdGVybWluYXRlZCcsXG4gICAgICAgICAgICAgIG1lc3NhZ2U6IG5vdGlmaWNhdGlvbk1lc3NhZ2UsXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICAgICAgc3RhdHVzOiAncGVuZGluZycgYXMgY29uc3QsXG4gICAgICAgICAgfSxcbiAgICAgICAgXSxcbiAgICAgIH0sXG4gICAgfVxuICB9KVxuICBsb2dGb3JEZWJ1Z2dpbmcoYFtUZWFtc0RpYWxvZ10gUmVtb3ZlZCAke3RlYW1tYXRlSWR9IGZyb20gdGVhbUNvbnRleHRgKVxufVxuXG5hc3luYyBmdW5jdGlvbiB2aWV3VGVhbW1hdGVPdXRwdXQoXG4gIHBhbmVJZDogc3RyaW5nLFxuICBiYWNrZW5kVHlwZTogUGFuZUJhY2tlbmRUeXBlIHwgdW5kZWZpbmVkLFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGlmIChiYWNrZW5kVHlwZSA9PT0gJ2l0ZXJtMicpIHtcbiAgICAvLyAtcyBpcyByZXF1aXJlZCB0byB0YXJnZXQgYSBzcGVjaWZpYyBzZXNzaW9uIChJVGVybUJhY2tlbmQudHM6MjE2LTIxNylcbiAgICBhd2FpdCBleGVjRmlsZU5vVGhyb3coSVQyX0NPTU1BTkQsIFsnc2Vzc2lvbicsICdmb2N1cycsICctcycsIHBhbmVJZF0pXG4gIH0gZWxzZSB7XG4gICAgLy8gRXh0ZXJuYWwtdG11eCB0ZWFtbWF0ZXMgbGl2ZSBvbiB0aGUgc3dhcm0gc29ja2V0IOKAlCB3aXRob3V0IC1MLCB0aGlzXG4gICAgLy8gdGFyZ2V0cyB0aGUgZGVmYXVsdCBzZXJ2ZXIgYW5kIHNpbGVudGx5IG5vLW9wcy4gTWlycm9ycyBydW5UbXV4SW5Td2FybVxuICAgIC8vIGluIFRtdXhCYWNrZW5kLnRzOjg1LTg5LlxuICAgIGNvbnN0IGFyZ3MgPSBpc0luc2lkZVRtdXhTeW5jKClcbiAgICAgID8gWydzZWxlY3QtcGFuZScsICctdCcsIHBhbmVJZF1cbiAgICAgIDogWyctTCcsIGdldFN3YXJtU29ja2V0TmFtZSgpLCAnc2VsZWN0LXBhbmUnLCAnLXQnLCBwYW5lSWRdXG4gICAgYXdhaXQgZXhlY0ZpbGVOb1Rocm93KFRNVVhfQ09NTUFORCwgYXJncylcbiAgfVxufVxuXG4vKipcbiAqIFRvZ2dsZSB2aXNpYmlsaXR5IG9mIGEgdGVhbW1hdGUgcGFuZSAoaGlkZSBpZiB2aXNpYmxlLCBzaG93IGlmIGhpZGRlbilcbiAqL1xuYXN5bmMgZnVuY3Rpb24gdG9nZ2xlVGVhbW1hdGVWaXNpYmlsaXR5KFxuICB0ZWFtbWF0ZTogVGVhbW1hdGVTdGF0dXMsXG4gIHRlYW1OYW1lOiBzdHJpbmcsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgaWYgKHRlYW1tYXRlLmlzSGlkZGVuKSB7XG4gICAgYXdhaXQgc2hvd1RlYW1tYXRlKHRlYW1tYXRlLCB0ZWFtTmFtZSlcbiAgfSBlbHNlIHtcbiAgICBhd2FpdCBoaWRlVGVhbW1hdGUodGVhbW1hdGUsIHRlYW1OYW1lKVxuICB9XG59XG5cbi8qKlxuICogSGlkZSBhIHRlYW1tYXRlIHBhbmUgdXNpbmcgdGhlIGJhY2tlbmQgYWJzdHJhY3Rpb24uXG4gKiBPbmx5IGF2YWlsYWJsZSBmb3IgYW50IHVzZXJzIChnYXRlZCBmb3IgZGVhZCBjb2RlIGVsaW1pbmF0aW9uIGluIGV4dGVybmFsIGJ1aWxkcylcbiAqL1xuYXN5bmMgZnVuY3Rpb24gaGlkZVRlYW1tYXRlKFxuICB0ZWFtbWF0ZTogVGVhbW1hdGVTdGF0dXMsXG4gIHRlYW1OYW1lOiBzdHJpbmcsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbn1cblxuLyoqXG4gKiBTaG93IGEgcHJldmlvdXNseSBoaWRkZW4gdGVhbW1hdGUgcGFuZSB1c2luZyB0aGUgYmFja2VuZCBhYnN0cmFjdGlvbi5cbiAqIE9ubHkgYXZhaWxhYmxlIGZvciBhbnQgdXNlcnMgKGdhdGVkIGZvciBkZWFkIGNvZGUgZWxpbWluYXRpb24gaW4gZXh0ZXJuYWwgYnVpbGRzKVxuICovXG5hc3luYyBmdW5jdGlvbiBzaG93VGVhbW1hdGUoXG4gIHRlYW1tYXRlOiBUZWFtbWF0ZVN0YXR1cyxcbiAgdGVhbU5hbWU6IHN0cmluZyxcbik6IFByb21pc2U8dm9pZD4ge1xufVxuXG4vKipcbiAqIFNlbmQgYSBtb2RlIGNoYW5nZSBtZXNzYWdlIHRvIGEgc2luZ2xlIHRlYW1tYXRlXG4gKiBBbHNvIHVwZGF0ZXMgY29uZmlnLmpzb24gZGlyZWN0bHkgc28gdGhlIFVJIHJlZmxlY3RzIHRoZSBjaGFuZ2UgaW1tZWRpYXRlbHlcbiAqL1xuZnVuY3Rpb24gc2VuZE1vZGVDaGFuZ2VUb1RlYW1tYXRlKFxuICB0ZWFtbWF0ZU5hbWU6IHN0cmluZyxcbiAgdGVhbU5hbWU6IHN0cmluZyxcbiAgdGFyZ2V0TW9kZTogUGVybWlzc2lvbk1vZGUsXG4pOiB2b2lkIHtcbiAgLy8gVXBkYXRlIGNvbmZpZy5qc29uIGRpcmVjdGx5IHNvIFVJIHNob3dzIHRoZSBjaGFuZ2UgaW1tZWRpYXRlbHlcbiAgc2V0TWVtYmVyTW9kZSh0ZWFtTmFtZSwgdGVhbW1hdGVOYW1lLCB0YXJnZXRNb2RlKVxuXG4gIC8vIEFsc28gc2VuZCBtZXNzYWdlIHNvIHRlYW1tYXRlIHVwZGF0ZXMgdGhlaXIgbG9jYWwgcGVybWlzc2lvbiBjb250ZXh0XG4gIGNvbnN0IG1lc3NhZ2UgPSBjcmVhdGVNb2RlU2V0UmVxdWVzdE1lc3NhZ2Uoe1xuICAgIG1vZGU6IHRhcmdldE1vZGUsXG4gICAgZnJvbTogJ3RlYW0tbGVhZCcsXG4gIH0pXG4gIHZvaWQgd3JpdGVUb01haWxib3goXG4gICAgdGVhbW1hdGVOYW1lLFxuICAgIHtcbiAgICAgIGZyb206ICd0ZWFtLWxlYWQnLFxuICAgICAgdGV4dDoganNvblN0cmluZ2lmeShtZXNzYWdlKSxcbiAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgIH0sXG4gICAgdGVhbU5hbWUsXG4gIClcbiAgbG9nRm9yRGVidWdnaW5nKFxuICAgIGBbVGVhbXNEaWFsb2ddIFNlbnQgbW9kZSBjaGFuZ2UgdG8gJHt0ZWFtbWF0ZU5hbWV9OiAke3RhcmdldE1vZGV9YCxcbiAgKVxufVxuXG4vKipcbiAqIEN5Y2xlIGEgc2luZ2xlIHRlYW1tYXRlJ3MgbW9kZVxuICovXG5mdW5jdGlvbiBjeWNsZVRlYW1tYXRlTW9kZShcbiAgdGVhbW1hdGU6IFRlYW1tYXRlU3RhdHVzLFxuICB0ZWFtTmFtZTogc3RyaW5nLFxuICBpc0J5cGFzc0F2YWlsYWJsZTogYm9vbGVhbixcbik6IHZvaWQge1xuICBjb25zdCBjdXJyZW50TW9kZSA9IHRlYW1tYXRlLm1vZGVcbiAgICA/IHBlcm1pc3Npb25Nb2RlRnJvbVN0cmluZyh0ZWFtbWF0ZS5tb2RlKVxuICAgIDogJ2RlZmF1bHQnXG4gIGNvbnN0IGNvbnRleHQgPSB7XG4gICAgLi4uZ2V0RW1wdHlUb29sUGVybWlzc2lvbkNvbnRleHQoKSxcbiAgICBtb2RlOiBjdXJyZW50TW9kZSxcbiAgICBpc0J5cGFzc1Blcm1pc3Npb25zTW9kZUF2YWlsYWJsZTogaXNCeXBhc3NBdmFpbGFibGUsXG4gIH1cbiAgY29uc3QgbmV4dE1vZGUgPSBnZXROZXh0UGVybWlzc2lvbk1vZGUoY29udGV4dClcbiAgc2VuZE1vZGVDaGFuZ2VUb1RlYW1tYXRlKHRlYW1tYXRlLm5hbWUsIHRlYW1OYW1lLCBuZXh0TW9kZSlcbn1cblxuLyoqXG4gKiBDeWNsZSBhbGwgdGVhbW1hdGVzJyBtb2RlcyBpbiB0YW5kZW1cbiAqIElmIG1vZGVzIGRpZmZlciwgcmVzZXQgYWxsIHRvIGRlZmF1bHQgZmlyc3RcbiAqIElmIHNhbWUsIGN5Y2xlIGFsbCB0byBuZXh0IG1vZGVcbiAqIFVzZXMgYmF0Y2ggdXBkYXRlIHRvIGF2b2lkIHJhY2UgY29uZGl0aW9uc1xuICovXG5mdW5jdGlvbiBjeWNsZUFsbFRlYW1tYXRlTW9kZXMoXG4gIHRlYW1tYXRlczogVGVhbW1hdGVTdGF0dXNbXSxcbiAgdGVhbU5hbWU6IHN0cmluZyxcbiAgaXNCeXBhc3NBdmFpbGFibGU6IGJvb2xlYW4sXG4pOiB2b2lkIHtcbiAgaWYgKHRlYW1tYXRlcy5sZW5ndGggPT09IDApIHJldHVyblxuXG4gIGNvbnN0IG1vZGVzID0gdGVhbW1hdGVzLm1hcCh0ID0+XG4gICAgdC5tb2RlID8gcGVybWlzc2lvbk1vZGVGcm9tU3RyaW5nKHQubW9kZSkgOiAnZGVmYXVsdCcsXG4gIClcbiAgY29uc3QgYWxsU2FtZSA9IG1vZGVzLmV2ZXJ5KG0gPT4gbSA9PT0gbW9kZXNbMF0pXG5cbiAgLy8gRGV0ZXJtaW5lIHRhcmdldCBtb2RlIGZvciBhbGwgdGVhbW1hdGVzXG4gIGNvbnN0IHRhcmdldE1vZGUgPSAhYWxsU2FtZVxuICAgID8gJ2RlZmF1bHQnXG4gICAgOiBnZXROZXh0UGVybWlzc2lvbk1vZGUoe1xuICAgICAgICAuLi5nZXRFbXB0eVRvb2xQZXJtaXNzaW9uQ29udGV4dCgpLFxuICAgICAgICBtb2RlOiBtb2Rlc1swXSA/PyAnZGVmYXVsdCcsXG4gICAgICAgIGlzQnlwYXNzUGVybWlzc2lvbnNNb2RlQXZhaWxhYmxlOiBpc0J5cGFzc0F2YWlsYWJsZSxcbiAgICAgIH0pXG5cbiAgLy8gQmF0Y2ggdXBkYXRlIGNvbmZpZy5qc29uIGluIGEgc2luZ2xlIGF0b21pYyBvcGVyYXRpb25cbiAgY29uc3QgbW9kZVVwZGF0ZXMgPSB0ZWFtbWF0ZXMubWFwKHQgPT4gKHtcbiAgICBtZW1iZXJOYW1lOiB0Lm5hbWUsXG4gICAgbW9kZTogdGFyZ2V0TW9kZSxcbiAgfSkpXG4gIHNldE11bHRpcGxlTWVtYmVyTW9kZXModGVhbU5hbWUsIG1vZGVVcGRhdGVzKVxuXG4gIC8vIFNlbmQgbWFpbGJveCBtZXNzYWdlcyB0byBlYWNoIHRlYW1tYXRlXG4gIGZvciAoY29uc3QgdGVhbW1hdGUgb2YgdGVhbW1hdGVzKSB7XG4gICAgY29uc3QgbWVzc2FnZSA9IGNyZWF0ZU1vZGVTZXRSZXF1ZXN0TWVzc2FnZSh7XG4gICAgICBtb2RlOiB0YXJnZXRNb2RlLFxuICAgICAgZnJvbTogJ3RlYW0tbGVhZCcsXG4gICAgfSlcbiAgICB2b2lkIHdyaXRlVG9NYWlsYm94KFxuICAgICAgdGVhbW1hdGUubmFtZSxcbiAgICAgIHtcbiAgICAgICAgZnJvbTogJ3RlYW0tbGVhZCcsXG4gICAgICAgIHRleHQ6IGpzb25TdHJpbmdpZnkobWVzc2FnZSksXG4gICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgfSxcbiAgICAgIHRlYW1OYW1lLFxuICAgIClcbiAgfVxuICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgYFtUZWFtc0RpYWxvZ10gU2VudCBtb2RlIGNoYW5nZSB0byBhbGwgJHt0ZWFtbWF0ZXMubGVuZ3RofSB0ZWFtbWF0ZXM6ICR7dGFyZ2V0TW9kZX1gLFxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxVQUFVLFFBQVEsUUFBUTtBQUNuQyxPQUFPQyxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ2pFLFNBQVNDLFdBQVcsUUFBUSxhQUFhO0FBQ3pDLFNBQVNDLGtCQUFrQixRQUFRLGlDQUFpQztBQUNwRSxTQUFTQyxXQUFXLFFBQVEsMEJBQTBCO0FBQ3REO0FBQ0EsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLEVBQUVDLFFBQVEsUUFBUSxjQUFjO0FBQ2xELFNBQVNDLGNBQWMsUUFBUSxvQ0FBb0M7QUFDbkUsU0FBU0Msa0JBQWtCLFFBQVEseUNBQXlDO0FBQzVFLFNBQ0UsS0FBS0MsUUFBUSxFQUNiQyxXQUFXLEVBQ1hDLGNBQWMsUUFDVCx5QkFBeUI7QUFDaEMsU0FBU0MsNkJBQTZCLFFBQVEsZUFBZTtBQUM3RCxTQUFTQywwQkFBMEIsUUFBUSw0Q0FBNEM7QUFDdkYsU0FBU0MsZUFBZSxRQUFRLHNCQUFzQjtBQUN0RCxTQUFTQyxlQUFlLFFBQVEsZ0NBQWdDO0FBQ2hFLFNBQVNDLGVBQWUsUUFBUSx1QkFBdUI7QUFDdkQsU0FBU0MscUJBQXFCLFFBQVEsa0RBQWtEO0FBQ3hGLFNBQ0VDLFlBQVksRUFDWixLQUFLQyxjQUFjLEVBQ25CQyx3QkFBd0IsRUFDeEJDLG9CQUFvQixRQUNmLDJDQUEyQztBQUNsRCxTQUFTQyxhQUFhLFFBQVEsK0JBQStCO0FBQzdELFNBQ0VDLFdBQVcsRUFDWEMsZ0JBQWdCLFFBQ1gseUNBQXlDO0FBQ2hELFNBQ0VDLHdCQUF3QixFQUN4QkMsZ0JBQWdCLEVBQ2hCQyxnQkFBZ0IsUUFDWCx3Q0FBd0M7QUFDL0MsY0FBY0MsZUFBZSxRQUFR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QUMzQlksUUFBUSxFQUFFLE1BQU0sRUFDaEJrQixpQkFBaUIsRUFBRSxPQUFPLENBQzNCLEVBQUUsSUFBSSxDQUFDO0VBQ04sSUFBSTRDLFNBQVMsQ0FBQ3JDLE1BQU0sS0FBSyxDQUFDLEVBQUU7RUFFNUIsTUFBTTZILEtBQUssR0FBR3hGLFNBQVMsQ0FBQ1AsR0FBRyxDQUFDdEMsQ0FBQyxJQUMzQkEsQ0FBQyxDQUFDbUUsSUFBSSxHQUFHcEgsd0JBQXdCLENBQUNpRCxDQUFDLENBQUNtRSxJQUFJLENBQUMsR0FBRyxTQUM5QyxDQUFDO0VBQ0QsTUFBTW1FLE9BQU8sR0FBR0QsS0FBSyxDQUFDRSxLQUFLLENBQUNDLENBQUMsSUFBSUEsQ0FBQyxLQUFLSCxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7O0VBRWhEO0VBQ0EsTUFBTUgsVUFBVSxHQUFHLENBQUNJLE9BQU8sR0FDdkIsU0FBUyxHQUNUMUwscUJBQXFCLENBQUM7SUFDcEIsR0FBR0wsNkJBQTZCLENBQUMsQ0FBQztJQUNsQzRILElBQUksRUFBRWtFLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxTQUFTO0lBQzNCakksZ0NBQWdDLEVBQUVIO0VBQ3BDLENBQUMsQ0FBQzs7RUFFTjtFQUNBLE1BQU13SSxXQUFXLEdBQUc1RixTQUFTLENBQUNQLEdBQUcsQ0FBQ3RDLENBQUMsS0FBSztJQUN0Q2hCLFVBQVUsRUFBRWdCLENBQUMsQ0FBQ1gsSUFBSTtJQUNsQjhFLElBQUksRUFBRStEO0VBQ1IsQ0FBQyxDQUFDLENBQUM7RUFDSHBLLHNCQUFzQixDQUFDaUIsUUFBUSxFQUFFMEosV0FBVyxDQUFDOztFQUU3QztFQUNBLEtBQUssTUFBTTVHLFFBQVEsSUFBSWdCLFNBQVMsRUFBRTtJQUNoQyxNQUFNOEUsT0FBTyxHQUFHdEosMkJBQTJCLENBQUM7TUFDMUM4RixJQUFJLEVBQUUrRCxVQUFVO01BQ2hCVCxJQUFJLEVBQUU7SUFDUixDQUFDLENBQUM7SUFDRixLQUFLbEosY0FBYyxDQUNqQnNELFFBQVEsQ0FBQ3hDLElBQUksRUFDYjtNQUNFb0ksSUFBSSxFQUFFLFdBQVc7TUFDakJDLElBQUksRUFBRXpLLGFBQWEsQ0FBQzBLLE9BQU8sQ0FBQztNQUM1QkMsU0FBUyxFQUFFLElBQUlDLElBQUksQ0FBQyxDQUFDLENBQUNDLFdBQVcsQ0FBQztJQUNwQyxDQUFDLEVBQ0QvSSxRQUNGLENBQUM7RUFDSDtFQUNBdEMsZUFBZSxDQUNiLHlDQUF5Q29HLFNBQVMsQ0FBQ3JDLE1BQU0sZUFBZTBILFVBQVUsRUFDcEYsQ0FBQztBQUNIIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/components/ui/OrderedList.tsx b/components/ui/OrderedList.tsx new file mode 100644 index 0000000..54ca8a0 --- /dev/null +++ b/components/ui/OrderedList.tsx @@ -0,0 +1,71 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { createContext, isValidElement, type ReactNode, useContext } from 'react'; +import { Box } from '../../ink.js'; +import { OrderedListItem, OrderedListItemContext } from './OrderedListItem.js'; +const OrderedListContext = createContext({ + marker: '' +}); +type OrderedListProps = { + children: ReactNode; +}; +function OrderedListComponent(t0) { + const $ = _c(9); + const { + children + } = t0; + const { + marker: parentMarker + } = useContext(OrderedListContext); + let numberOfItems = 0; + for (const child of React.Children.toArray(children)) { + if (!isValidElement(child) || child.type !== OrderedListItem) { + continue; + } + numberOfItems++; + } + const maxMarkerWidth = String(numberOfItems).length; + let t1; + if ($[0] !== children || $[1] !== maxMarkerWidth || $[2] !== parentMarker) { + let t2; + if ($[4] !== maxMarkerWidth || $[5] !== parentMarker) { + t2 = (child_0, index) => { + if (!isValidElement(child_0) || child_0.type !== OrderedListItem) { + return child_0; + } + const paddedMarker = `${String(index + 1).padStart(maxMarkerWidth)}.`; + const marker = `${parentMarker}${paddedMarker}`; + return {child_0}; + }; + $[4] = maxMarkerWidth; + $[5] = parentMarker; + $[6] = t2; + } else { + t2 = $[6]; + } + t1 = React.Children.map(children, t2); + $[0] = children; + $[1] = maxMarkerWidth; + $[2] = parentMarker; + $[3] = t1; + } else { + t1 = $[3]; + } + let t2; + if ($[7] !== t1) { + t2 = {t1}; + $[7] = t1; + $[8] = t2; + } else { + t2 = $[8]; + } + return t2; +} + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +OrderedListComponent.Item = OrderedListItem; +export const OrderedList = OrderedListComponent; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImNyZWF0ZUNvbnRleHQiLCJpc1ZhbGlkRWxlbWVudCIsIlJlYWN0Tm9kZSIsInVzZUNvbnRleHQiLCJCb3giLCJPcmRlcmVkTGlzdEl0ZW0iLCJPcmRlcmVkTGlzdEl0ZW1Db250ZXh0IiwiT3JkZXJlZExpc3RDb250ZXh0IiwibWFya2VyIiwiT3JkZXJlZExpc3RQcm9wcyIsImNoaWxkcmVuIiwiT3JkZXJlZExpc3RDb21wb25lbnQiLCJ0MCIsIiQiLCJfYyIsInBhcmVudE1hcmtlciIsIm51bWJlck9mSXRlbXMiLCJjaGlsZCIsIkNoaWxkcmVuIiwidG9BcnJheSIsInR5cGUiLCJtYXhNYXJrZXJXaWR0aCIsIlN0cmluZyIsImxlbmd0aCIsInQxIiwidDIiLCJjaGlsZF8wIiwiaW5kZXgiLCJwYWRkZWRNYXJrZXIiLCJwYWRTdGFydCIsIm1hcCIsIkl0ZW0iLCJPcmRlcmVkTGlzdCJdLCJzb3VyY2VzIjpbIk9yZGVyZWRMaXN0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHtcbiAgY3JlYXRlQ29udGV4dCxcbiAgaXNWYWxpZEVsZW1lbnQsXG4gIHR5cGUgUmVhY3ROb2RlLFxuICB1c2VDb250ZXh0LFxufSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IE9yZGVyZWRMaXN0SXRlbSwgT3JkZXJlZExpc3RJdGVtQ29udGV4dCB9IGZyb20gJy4vT3JkZXJlZExpc3RJdGVtLmpzJ1xuXG5jb25zdCBPcmRlcmVkTGlzdENvbnRleHQgPSBjcmVhdGVDb250ZXh0KHsgbWFya2VyOiAnJyB9KVxuXG50eXBlIE9yZGVyZWRMaXN0UHJvcHMgPSB7XG4gIGNoaWxkcmVuOiBSZWFjdE5vZGVcbn1cblxuZnVuY3Rpb24gT3JkZXJlZExpc3RDb21wb25lbnQoeyBjaGlsZHJlbiB9OiBPcmRlcmVkTGlzdFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgeyBtYXJrZXI6IHBhcmVudE1hcmtlciB9ID0gdXNlQ29udGV4dChPcmRlcmVkTGlzdENvbnRleHQpXG5cbiAgbGV0IG51bWJlck9mSXRlbXMgPSAwXG4gIGZvciAoY29uc3QgY2hpbGQgb2YgUmVhY3QuQ2hpbGRyZW4udG9BcnJheShjaGlsZHJlbikpIHtcbiAgICBpZiAoIWlzVmFsaWRFbGVtZW50KGNoaWxkKSB8fCBjaGlsZC50eXBlICE9PSBPcmRlcmVkTGlzdEl0ZW0pIHtcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuICAgIG51bWJlck9mSXRlbXMrK1xuICB9XG5cbiAgY29uc3QgbWF4TWFya2VyV2lkdGggPSBTdHJpbmcobnVtYmVyT2ZJdGVtcykubGVuZ3RoXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgIHtSZWFjdC5DaGlsZHJlbi5tYXAoY2hpbGRyZW4sIChjaGlsZCwgaW5kZXgpID0+IHtcbiAgICAgICAgaWYgKCFpc1ZhbGlkRWxlbWVudChjaGlsZCkgfHwgY2hpbGQudHlwZSAhPT0gT3JkZXJlZExpc3RJdGVtKSB7XG4gICAgICAgICAgcmV0dXJuIGNoaWxkXG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBwYWRkZWRNYXJrZXIgPSBgJHtTdHJpbmcoaW5kZXggKyAxKS5wYWRTdGFydChtYXhNYXJrZXJXaWR0aCl9LmBcbiAgICAgICAgY29uc3QgbWFya2VyID0gYCR7cGFyZW50TWFya2VyfSR7cGFkZGVkTWFya2VyfWBcblxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIDxPcmRlcmVkTGlzdENvbnRleHQuUHJvdmlkZXIgdmFsdWU9e3sgbWFya2VyIH19PlxuICAgICAgICAgICAgPE9yZGVyZWRMaXN0SXRlbUNvbnRleHQuUHJvdmlkZXIgdmFsdWU9e3sgbWFya2VyIH19PlxuICAgICAgICAgICAgICB7Y2hpbGR9XG4gICAgICAgICAgICA8L09yZGVyZWRMaXN0SXRlbUNvbnRleHQuUHJvdmlkZXI+XG4gICAgICAgICAgPC9PcmRlcmVkTGlzdENvbnRleHQuUHJvdmlkZXI+XG4gICAgICAgIClcbiAgICAgIH0pfVxuICAgIDwvQm94PlxuICApXG59XG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tdG9wLWxldmVsLXNpZGUtZWZmZWN0c1xuT3JkZXJlZExpc3RDb21wb25lbnQuSXRlbSA9IE9yZGVyZWRMaXN0SXRlbVxuXG5leHBvcnQgY29uc3QgT3JkZXJlZExpc3QgPSBPcmRlcmVkTGlzdENvbXBvbmVudFxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUNWQyxhQUFhLEVBQ2JDLGNBQWMsRUFDZCxLQUFLQyxTQUFTLEVBQ2RDLFVBQVUsUUFDTCxPQUFPO0FBQ2QsU0FBU0MsR0FBRyxRQUFRLGNBQWM7QUFDbEMsU0FBU0MsZUFBZSxFQUFFQyxzQkFBc0IsUUFBUSxzQkFBc0I7QUFFOUUsTUFBTUMsa0JBQWtCLEdBQUdQLGFBQWEsQ0FBQztFQUFFUSxNQUFNLEVBQUU7QUFBRyxDQUFDLENBQUM7QUFFeEQsS0FBS0MsZ0JBQWdCLEdBQUc7RUFDdEJDLFFBQVEsRUFBRVIsU0FBUztBQUNyQixDQUFDO0FBRUQsU0FBQVMscUJBQUFDLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBOEI7SUFBQUo7RUFBQSxJQUFBRSxFQUE4QjtFQUMxRDtJQUFBSixNQUFBLEVBQUFPO0VBQUEsSUFBaUNaLFVBQVUsQ0FBQ0ksa0JBQWtCLENBQUM7RUFFL0QsSUFBQVMsYUFBQSxHQUFvQixDQUFDO0VBQ3JCLEtBQUssTUFBQUMsS0FBVyxJQUFJbEIsS0FBSyxDQUFBbUIsUUFBUyxDQUFBQyxPQUFRLENBQUNULFFBQVEsQ0FBQztJQUNsRCxJQUFJLENBQUNULGNBQWMsQ0FBQ2dCLEtBQUssQ0FBbUMsSUFBOUJBLEtBQUssQ0FBQUcsSUFBSyxLQUFLZixlQUFlO01BQzFEO0lBQVE7SUFFVlcsYUFBYSxFQUFFO0VBQUE7RUFHakIsTUFBQUssY0FBQSxHQUF1QkMsTUFBTSxDQUFDTixhQUFhLENBQUMsQ0FBQU8sTUFBTztFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBWCxDQUFBLFFBQUFILFFBQUEsSUFBQUcsQ0FBQSxRQUFBUSxjQUFBLElBQUFSLENBQUEsUUFBQUUsWUFBQTtJQUFBLElBQUFVLEVBQUE7SUFBQSxJQUFBWixDQUFBLFFBQUFRLGNBQUEsSUFBQVIsQ0FBQSxRQUFBRSxZQUFBO01BSWpCVSxFQUFBLEdBQUFBLENBQUFDLE9BQUEsRUFBQUMsS0FBQTtRQUM1QixJQUFJLENBQUMxQixjQUFjLENBQUNnQixPQUFLLENBQW1DLElBQTlCQSxPQUFLLENBQUFHLElBQUssS0FBS2YsZUFBZTtVQUFBLE9BQ25EWSxPQUFLO1FBQUE7UUFHZCxNQUFBVyxZQUFBLEdBQXFCLEdBQUdOLE1BQU0sQ0FBQ0ssS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFBRSxRQUFTLENBQUNSLGNBQWMsQ0FBQyxHQUFHO1FBQ3JFLE1BQUFiLE1BQUEsR0FBZSxHQUFHTyxZQUFZLEdBQUdhLFlBQVksRUFBRTtRQUFBLE9BRzdDLDZCQUFvQyxLQUFVLENBQVY7VUFBQXBCO1FBQVMsRUFBQyxDQUM1QyxpQ0FBd0MsS0FBVSxDQUFWO1lBQUFBO1VBQVMsRUFBQyxDQUMvQ1MsUUFBSSxDQUNQLGtDQUNGLDhCQUE4QjtNQUFBLENBRWpDO01BQUFKLENBQUEsTUFBQVEsY0FBQTtNQUFBUixDQUFBLE1BQUFFLFlBQUE7TUFBQUYsQ0FBQSxNQUFBWSxFQUFBO0lBQUE7TUFBQUEsRUFBQSxHQUFBWixDQUFBO0lBQUE7SUFmQVcsRUFBQSxHQUFBekIsS0FBSyxDQUFBbUIsUUFBUyxDQUFBWSxHQUFJLENBQUNwQixRQUFRLEVBQUVlLEVBZTdCLENBQUM7SUFBQVosQ0FBQSxNQUFBSCxRQUFBO0lBQUFHLENBQUEsTUFBQVEsY0FBQTtJQUFBUixDQUFBLE1BQUFFLFlBQUE7SUFBQUYsQ0FBQSxNQUFBVyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBWCxDQUFBO0VBQUE7RUFBQSxJQUFBWSxFQUFBO0VBQUEsSUFBQVosQ0FBQSxRQUFBVyxFQUFBO0lBaEJKQyxFQUFBLElBQUMsR0FBRyxDQUFlLGFBQVEsQ0FBUixRQUFRLENBQ3hCLENBQUFELEVBZUEsQ0FDSCxFQWpCQyxHQUFHLENBaUJFO0lBQUFYLENBQUEsTUFBQVcsRUFBQTtJQUFBWCxDQUFBLE1BQUFZLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFaLENBQUE7RUFBQTtFQUFBLE9BakJOWSxFQWlCTTtBQUFBOztBQUlWO0FBQ0FkLG9CQUFvQixDQUFDb0IsSUFBSSxHQUFHMUIsZUFBZTtBQUUzQyxPQUFPLE1BQU0yQixXQUFXLEdBQUdyQixvQkFBb0IiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/ui/OrderedListItem.tsx b/components/ui/OrderedListItem.tsx new file mode 100644 index 0000000..08b1b41 --- /dev/null +++ b/components/ui/OrderedListItem.tsx @@ -0,0 +1,45 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { createContext, type ReactNode, useContext } from 'react'; +import { Box, Text } from '../../ink.js'; +export const OrderedListItemContext = createContext({ + marker: '' +}); +type OrderedListItemProps = { + children: ReactNode; +}; +export function OrderedListItem(t0) { + const $ = _c(7); + const { + children + } = t0; + const { + marker + } = useContext(OrderedListItemContext); + let t1; + if ($[0] !== marker) { + t1 = {marker}; + $[0] = marker; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== children) { + t2 = {children}; + $[2] = children; + $[3] = t2; + } else { + t2 = $[3]; + } + let t3; + if ($[4] !== t1 || $[5] !== t2) { + t3 = {t1}{t2}; + $[4] = t1; + $[5] = t2; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImNyZWF0ZUNvbnRleHQiLCJSZWFjdE5vZGUiLCJ1c2VDb250ZXh0IiwiQm94IiwiVGV4dCIsIk9yZGVyZWRMaXN0SXRlbUNvbnRleHQiLCJtYXJrZXIiLCJPcmRlcmVkTGlzdEl0ZW1Qcm9wcyIsImNoaWxkcmVuIiwiT3JkZXJlZExpc3RJdGVtIiwidDAiLCIkIiwiX2MiLCJ0MSIsInQyIiwidDMiXSwic291cmNlcyI6WyJPcmRlcmVkTGlzdEl0ZW0udHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyBjcmVhdGVDb250ZXh0LCB0eXBlIFJlYWN0Tm9kZSwgdXNlQ29udGV4dCB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuXG5leHBvcnQgY29uc3QgT3JkZXJlZExpc3RJdGVtQ29udGV4dCA9IGNyZWF0ZUNvbnRleHQoeyBtYXJrZXI6ICcnIH0pXG5cbnR5cGUgT3JkZXJlZExpc3RJdGVtUHJvcHMgPSB7XG4gIGNoaWxkcmVuOiBSZWFjdE5vZGVcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIE9yZGVyZWRMaXN0SXRlbSh7XG4gIGNoaWxkcmVuLFxufTogT3JkZXJlZExpc3RJdGVtUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB7IG1hcmtlciB9ID0gdXNlQ29udGV4dChPcmRlcmVkTGlzdEl0ZW1Db250ZXh0KVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBnYXA9ezF9PlxuICAgICAgPFRleHQgZGltQ29sb3I+e21hcmtlcn08L1RleHQ+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj57Y2hpbGRyZW59PC9Cb3g+XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFBSUMsYUFBYSxFQUFFLEtBQUtDLFNBQVMsRUFBRUMsVUFBVSxRQUFRLE9BQU87QUFDeEUsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUV4QyxPQUFPLE1BQU1DLHNCQUFzQixHQUFHTCxhQUFhLENBQUM7RUFBRU0sTUFBTSxFQUFFO0FBQUcsQ0FBQyxDQUFDO0FBRW5FLEtBQUtDLG9CQUFvQixHQUFHO0VBQzFCQyxRQUFRLEVBQUVQLFNBQVM7QUFDckIsQ0FBQztBQUVELE9BQU8sU0FBQVEsZ0JBQUFDLEVBQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBeUI7SUFBQUo7RUFBQSxJQUFBRSxFQUVUO0VBQ3JCO0lBQUFKO0VBQUEsSUFBbUJKLFVBQVUsQ0FBQ0csc0JBQXNCLENBQUM7RUFBQSxJQUFBUSxFQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBTCxNQUFBO0lBSWpETyxFQUFBLElBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBRVAsT0FBSyxDQUFFLEVBQXRCLElBQUksQ0FBeUI7SUFBQUssQ0FBQSxNQUFBTCxNQUFBO0lBQUFLLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFILENBQUEsUUFBQUgsUUFBQTtJQUM5Qk0sRUFBQSxJQUFDLEdBQUcsQ0FBZSxhQUFRLENBQVIsUUFBUSxDQUFFTixTQUFPLENBQUUsRUFBckMsR0FBRyxDQUF3QztJQUFBRyxDQUFBLE1BQUFILFFBQUE7SUFBQUcsQ0FBQSxNQUFBRyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSCxDQUFBO0VBQUE7RUFBQSxJQUFBSSxFQUFBO0VBQUEsSUFBQUosQ0FBQSxRQUFBRSxFQUFBLElBQUFGLENBQUEsUUFBQUcsRUFBQTtJQUY5Q0MsRUFBQSxJQUFDLEdBQUcsQ0FBTSxHQUFDLENBQUQsR0FBQyxDQUNULENBQUFGLEVBQTZCLENBQzdCLENBQUFDLEVBQTJDLENBQzdDLEVBSEMsR0FBRyxDQUdFO0lBQUFILENBQUEsTUFBQUUsRUFBQTtJQUFBRixDQUFBLE1BQUFHLEVBQUE7SUFBQUgsQ0FBQSxNQUFBSSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSixDQUFBO0VBQUE7RUFBQSxPQUhOSSxFQUdNO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/components/ui/TreeSelect.tsx b/components/ui/TreeSelect.tsx new file mode 100644 index 0000000..d65d75c --- /dev/null +++ b/components/ui/TreeSelect.tsx @@ -0,0 +1,397 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'; +import { Box } from '../../ink.js'; +import { type OptionWithDescription, Select } from '../CustomSelect/select.js'; +export type TreeNode = { + id: string | number; + value: T; + label: string; + description?: string; + dimDescription?: boolean; + children?: TreeNode[]; + metadata?: Record; +}; +type FlattenedNode = { + node: TreeNode; + depth: number; + isExpanded: boolean; + hasChildren: boolean; + parentId?: string | number; +}; +export type TreeSelectProps = { + /** + * Tree nodes to display. + */ + readonly nodes: TreeNode[]; + + /** + * Callback when a node is selected. + */ + readonly onSelect: (node: TreeNode) => void; + + /** + * Callback when cancel is pressed. + */ + readonly onCancel?: () => void; + + /** + * Callback when focused node changes. + */ + readonly onFocus?: (node: TreeNode) => void; + + /** + * Node to focus by ID. + */ + readonly focusNodeId?: string | number; + + /** + * Number of visible options. + */ + readonly visibleOptionCount?: number; + + /** + * Layout of the options. + */ + readonly layout?: 'compact' | 'expanded' | 'compact-vertical'; + + /** + * When disabled, user input is ignored. + */ + readonly isDisabled?: boolean; + + /** + * When true, hides the numeric indexes next to each option. + */ + readonly hideIndexes?: boolean; + + /** + * Function to determine if a node should be initially expanded. + * If not provided, all nodes start collapsed. + */ + readonly isNodeExpanded?: (nodeId: string | number) => boolean; + + /** + * Callback when a node is expanded. + */ + readonly onExpand?: (nodeId: string | number) => void; + + /** + * Callback when a node is collapsed. + */ + readonly onCollapse?: (nodeId: string | number) => void; + + /** + * Custom prefix function for parent nodes + * @param isExpanded - Whether the parent node is currently expanded + * @returns The prefix string to display (default: '▼ ' when expanded, '▶ ' when collapsed) + */ + readonly getParentPrefix?: (isExpanded: boolean) => string; + + /** + * Custom prefix function for child nodes + * @param depth - The depth of the child node in the tree (0-indexed from parent) + * @returns The prefix string to display (default: ' ▸ ') + */ + readonly getChildPrefix?: (depth: number) => string; + + /** + * Callback when user presses up from the first item. + * If provided, navigation will not wrap to the last item. + */ + readonly onUpFromFirstItem?: () => void; +}; + +/** + * TreeSelect is a generic component for selecting items from a hierarchical tree structure. + * It handles expand/collapse state, keyboard navigation, and renders the tree as a flat list + * using the Select component. + */ +export function TreeSelect(t0) { + const $ = _c(48); + const { + nodes, + onSelect, + onCancel, + onFocus, + focusNodeId, + visibleOptionCount, + layout: t1, + isDisabled: t2, + hideIndexes: t3, + isNodeExpanded, + onExpand, + onCollapse, + getParentPrefix, + getChildPrefix, + onUpFromFirstItem + } = t0; + const layout = t1 === undefined ? "expanded" : t1; + const isDisabled = t2 === undefined ? false : t2; + const hideIndexes = t3 === undefined ? false : t3; + let t4; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t4 = new Set(); + $[0] = t4; + } else { + t4 = $[0]; + } + const [internalExpandedIds, setInternalExpandedIds] = React.useState(t4); + const isProgrammaticFocusRef = React.useRef(false); + const lastFocusedIdRef = React.useRef(null); + let t5; + if ($[1] !== internalExpandedIds || $[2] !== isNodeExpanded) { + t5 = nodeId => { + if (isNodeExpanded) { + return isNodeExpanded(nodeId); + } + return internalExpandedIds.has(nodeId); + }; + $[1] = internalExpandedIds; + $[2] = isNodeExpanded; + $[3] = t5; + } else { + t5 = $[3]; + } + const isExpanded = t5; + let result; + if ($[4] !== isExpanded || $[5] !== nodes) { + result = []; + function traverse(node, depth, parentId) { + const hasChildren = !!node.children && node.children.length > 0; + const nodeIsExpanded = isExpanded(node.id); + result.push({ + node, + depth, + isExpanded: nodeIsExpanded, + hasChildren, + parentId + }); + if (hasChildren && nodeIsExpanded && node.children) { + for (const child of node.children) { + traverse(child, depth + 1, node.id); + } + } + } + for (const node_0 of nodes) { + traverse(node_0, 0); + } + $[4] = isExpanded; + $[5] = nodes; + $[6] = result; + } else { + result = $[6]; + } + const flattenedNodes = result; + const defaultGetParentPrefix = _temp; + const defaultGetChildPrefix = _temp2; + const parentPrefixFn = getParentPrefix ?? defaultGetParentPrefix; + const childPrefixFn = getChildPrefix ?? defaultGetChildPrefix; + let t6; + if ($[7] !== childPrefixFn || $[8] !== parentPrefixFn) { + t6 = flatNode => { + let prefix = ""; + if (flatNode.hasChildren) { + prefix = parentPrefixFn(flatNode.isExpanded); + } else { + if (flatNode.depth > 0) { + prefix = childPrefixFn(flatNode.depth); + } + } + return prefix + flatNode.node.label; + }; + $[7] = childPrefixFn; + $[8] = parentPrefixFn; + $[9] = t6; + } else { + t6 = $[9]; + } + const buildLabel = t6; + let t7; + if ($[10] !== buildLabel || $[11] !== flattenedNodes) { + t7 = flattenedNodes.map(flatNode_0 => ({ + label: buildLabel(flatNode_0), + description: flatNode_0.node.description, + dimDescription: flatNode_0.node.dimDescription ?? true, + value: flatNode_0.node.id + })); + $[10] = buildLabel; + $[11] = flattenedNodes; + $[12] = t7; + } else { + t7 = $[12]; + } + const options = t7; + let map; + if ($[13] !== flattenedNodes) { + map = new Map(); + flattenedNodes.forEach(fn => map.set(fn.node.id, fn.node)); + $[13] = flattenedNodes; + $[14] = map; + } else { + map = $[14]; + } + const nodeMap = map; + let t8; + if ($[15] !== flattenedNodes) { + t8 = nodeId_0 => flattenedNodes.find(fn_0 => fn_0.node.id === nodeId_0); + $[15] = flattenedNodes; + $[16] = t8; + } else { + t8 = $[16]; + } + const findFlattenedNode = t8; + let t9; + if ($[17] !== findFlattenedNode || $[18] !== onCollapse || $[19] !== onExpand) { + t9 = (nodeId_1, shouldExpand) => { + const flatNode_1 = findFlattenedNode(nodeId_1); + if (!flatNode_1 || !flatNode_1.hasChildren) { + return; + } + if (shouldExpand) { + if (onExpand) { + onExpand(nodeId_1); + } else { + setInternalExpandedIds(prev => new Set(prev).add(nodeId_1)); + } + } else { + if (onCollapse) { + onCollapse(nodeId_1); + } else { + setInternalExpandedIds(prev_0 => { + const newSet = new Set(prev_0); + newSet.delete(nodeId_1); + return newSet; + }); + } + } + }; + $[17] = findFlattenedNode; + $[18] = onCollapse; + $[19] = onExpand; + $[20] = t9; + } else { + t9 = $[20]; + } + const toggleExpand = t9; + let t10; + if ($[21] !== findFlattenedNode || $[22] !== focusNodeId || $[23] !== isDisabled || $[24] !== nodeMap || $[25] !== onFocus || $[26] !== toggleExpand) { + t10 = e => { + if (!focusNodeId || isDisabled) { + return; + } + const flatNode_2 = findFlattenedNode(focusNodeId); + if (!flatNode_2) { + return; + } + if (e.key === "right" && flatNode_2.hasChildren) { + e.preventDefault(); + toggleExpand(focusNodeId, true); + } else { + if (e.key === "left") { + if (flatNode_2.hasChildren && flatNode_2.isExpanded) { + e.preventDefault(); + toggleExpand(focusNodeId, false); + } else { + if (flatNode_2.parentId !== undefined) { + e.preventDefault(); + isProgrammaticFocusRef.current = true; + toggleExpand(flatNode_2.parentId, false); + if (onFocus) { + const parentNode = nodeMap.get(flatNode_2.parentId); + if (parentNode) { + onFocus(parentNode); + } + } + } + } + } + } + }; + $[21] = findFlattenedNode; + $[22] = focusNodeId; + $[23] = isDisabled; + $[24] = nodeMap; + $[25] = onFocus; + $[26] = toggleExpand; + $[27] = t10; + } else { + t10 = $[27]; + } + const handleKeyDown = t10; + let t11; + if ($[28] !== nodeMap || $[29] !== onSelect) { + t11 = nodeId_2 => { + const node_1 = nodeMap.get(nodeId_2); + if (!node_1) { + return; + } + onSelect(node_1); + }; + $[28] = nodeMap; + $[29] = onSelect; + $[30] = t11; + } else { + t11 = $[30]; + } + const handleChange = t11; + let t12; + if ($[31] !== nodeMap || $[32] !== onFocus) { + t12 = nodeId_3 => { + if (isProgrammaticFocusRef.current) { + isProgrammaticFocusRef.current = false; + return; + } + if (lastFocusedIdRef.current === nodeId_3) { + return; + } + lastFocusedIdRef.current = nodeId_3; + if (onFocus) { + const node_2 = nodeMap.get(nodeId_3); + if (node_2) { + onFocus(node_2); + } + } + }; + $[31] = nodeMap; + $[32] = onFocus; + $[33] = t12; + } else { + t12 = $[33]; + } + const handleFocus = t12; + let t13; + if ($[34] !== focusNodeId || $[35] !== handleChange || $[36] !== handleFocus || $[37] !== hideIndexes || $[38] !== isDisabled || $[39] !== layout || $[40] !== onCancel || $[41] !== onUpFromFirstItem || $[42] !== options || $[43] !== visibleOptionCount) { + t13 = = Record> = (tool: ToolType, input: Input, toolUseContext: ToolUseContext, assistantMessage: AssistantMessage, toolUseID: string, forceDecision?: PermissionDecision) => Promise>; +function useCanUseTool(setToolUseConfirmQueue, setToolPermissionContext) { + const $ = _c(3); + let t0; + if ($[0] !== setToolPermissionContext || $[1] !== setToolUseConfirmQueue) { + t0 = async (tool, input, toolUseContext, assistantMessage, toolUseID, forceDecision) => new Promise(resolve => { + const ctx = createPermissionContext(tool, input, toolUseContext, assistantMessage, toolUseID, setToolPermissionContext, createPermissionQueueOps(setToolUseConfirmQueue)); + if (ctx.resolveIfAborted(resolve)) { + return; + } + const decisionPromise = forceDecision !== undefined ? Promise.resolve(forceDecision) : hasPermissionsToUseTool(tool, input, toolUseContext, assistantMessage, toolUseID); + return decisionPromise.then(async result => { + if (result.behavior === "allow") { + if (ctx.resolveIfAborted(resolve)) { + return; + } + if (feature("TRANSCRIPT_CLASSIFIER") && result.decisionReason?.type === "classifier" && result.decisionReason.classifier === "auto-mode") { + setYoloClassifierApproval(toolUseID, result.decisionReason.reason); + } + ctx.logDecision({ + decision: "accept", + source: "config" + }); + resolve(ctx.buildAllow(result.updatedInput ?? input, { + decisionReason: result.decisionReason + })); + return; + } + const appState = toolUseContext.getAppState(); + const description = await tool.description(input as never, { + isNonInteractiveSession: toolUseContext.options.isNonInteractiveSession, + toolPermissionContext: appState.toolPermissionContext, + tools: toolUseContext.options.tools + }); + if (ctx.resolveIfAborted(resolve)) { + return; + } + switch (result.behavior) { + case "deny": + { + logPermissionDecision({ + tool, + input, + toolUseContext, + messageId: ctx.messageId, + toolUseID + }, { + decision: "reject", + source: "config" + }); + if (feature("TRANSCRIPT_CLASSIFIER") && result.decisionReason?.type === "classifier" && result.decisionReason.classifier === "auto-mode") { + recordAutoModeDenial({ + toolName: tool.name, + display: description, + reason: result.decisionReason.reason ?? "", + timestamp: Date.now() + }); + toolUseContext.addNotification?.({ + key: "auto-mode-denied", + priority: "immediate", + jsx: <>{tool.userFacingName(input).toLowerCase()} denied by auto mode · /permissions + }); + } + resolve(result); + return; + } + case "ask": + { + if (appState.toolPermissionContext.awaitAutomatedChecksBeforeDialog) { + const coordinatorDecision = await handleCoordinatorPermission({ + ctx, + ...(feature("BASH_CLASSIFIER") ? { + pendingClassifierCheck: result.pendingClassifierCheck + } : {}), + updatedInput: result.updatedInput, + suggestions: result.suggestions, + permissionMode: appState.toolPermissionContext.mode + }); + if (coordinatorDecision) { + resolve(coordinatorDecision); + return; + } + } + if (ctx.resolveIfAborted(resolve)) { + return; + } + const swarmDecision = await handleSwarmWorkerPermission({ + ctx, + description, + ...(feature("BASH_CLASSIFIER") ? { + pendingClassifierCheck: result.pendingClassifierCheck + } : {}), + updatedInput: result.updatedInput, + suggestions: result.suggestions + }); + if (swarmDecision) { + resolve(swarmDecision); + return; + } + if (feature("BASH_CLASSIFIER") && result.pendingClassifierCheck && tool.name === BASH_TOOL_NAME && !appState.toolPermissionContext.awaitAutomatedChecksBeforeDialog) { + const speculativePromise = peekSpeculativeClassifierCheck((input as { + command: string; + }).command); + if (speculativePromise) { + const raceResult = await Promise.race([speculativePromise.then(_temp), new Promise(_temp2)]); + if (ctx.resolveIfAborted(resolve)) { + return; + } + if (raceResult.type === "result" && raceResult.result.matches && raceResult.result.confidence === "high" && feature("BASH_CLASSIFIER")) { + consumeSpeculativeClassifierCheck((input as { + command: string; + }).command); + const matchedRule = raceResult.result.matchedDescription ?? undefined; + if (matchedRule) { + setClassifierApproval(toolUseID, matchedRule); + } + ctx.logDecision({ + decision: "accept", + source: { + type: "classifier" + } + }); + resolve(ctx.buildAllow(result.updatedInput ?? input as Record, { + decisionReason: { + type: "classifier" as const, + classifier: "bash_allow" as const, + reason: `Allowed by prompt rule: "${raceResult.result.matchedDescription}"` + } + })); + return; + } + } + } + handleInteractivePermission({ + ctx, + description, + result, + awaitAutomatedChecksBeforeDialog: appState.toolPermissionContext.awaitAutomatedChecksBeforeDialog, + bridgeCallbacks: feature("BRIDGE_MODE") ? appState.replBridgePermissionCallbacks : undefined, + channelCallbacks: feature("KAIROS") || feature("KAIROS_CHANNELS") ? appState.channelPermissionCallbacks : undefined + }, resolve); + return; + } + } + }).catch(error => { + if (error instanceof AbortError || error instanceof APIUserAbortError) { + logForDebugging(`Permission check threw ${error.constructor.name} for tool=${tool.name}: ${error.message}`); + ctx.logCancelled(); + resolve(ctx.cancelAndAbort(undefined, true)); + } else { + logError(error); + resolve(ctx.cancelAndAbort(undefined, true)); + } + }).finally(() => { + clearClassifierChecking(toolUseID); + }); + }); + $[0] = setToolPermissionContext; + $[1] = setToolUseConfirmQueue; + $[2] = t0; + } else { + t0 = $[2]; + } + return t0; +} +function _temp2(res) { + return setTimeout(res, 2000, { + type: "timeout" as const + }); +} +function _temp(r) { + return { + type: "result" as const, + result: r + }; +} +export default useCanUseTool; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiQVBJVXNlckFib3J0RXJyb3IiLCJSZWFjdCIsInVzZUNhbGxiYWNrIiwiQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyIsImxvZ0V2ZW50Iiwic2FuaXRpemVUb29sTmFtZUZvckFuYWx5dGljcyIsIlRvb2xVc2VDb25maXJtIiwiVGV4dCIsIlRvb2xQZXJtaXNzaW9uQ29udGV4dCIsIlRvb2wiLCJUb29sVHlwZSIsIlRvb2xVc2VDb250ZXh0IiwiY29uc3VtZVNwZWN1bGF0aXZlQ2xhc3NpZmllckNoZWNrIiwicGVla1NwZWN1bGF0aXZlQ2xhc3NpZmllckNoZWNrIiwiQkFTSF9UT09MX05BTUUiLCJBc3Npc3RhbnRNZXNzYWdlIiwicmVjb3JkQXV0b01vZGVEZW5pYWwiLCJjbGVhckNsYXNzaWZpZXJDaGVja2luZyIsInNldENsYXNzaWZpZXJBcHByb3ZhbCIsInNldFlvbG9DbGFzc2lmaWVyQXBwcm92YWwiLCJsb2dGb3JEZWJ1Z2dpbmciLCJBYm9ydEVycm9yIiwibG9nRXJyb3IiLCJQZXJtaXNzaW9uRGVjaXNpb24iLCJoYXNQZXJtaXNzaW9uc1RvVXNlVG9vbCIsImpzb25TdHJpbmdpZnkiLCJoYW5kbGVDb29yZGluYXRvclBlcm1pc3Npb24iLCJoYW5kbGVJbnRlcmFjdGl2ZVBlcm1pc3Npb24iLCJoYW5kbGVTd2FybVdvcmtlclBlcm1pc3Npb24iLCJjcmVhdGVQZXJtaXNzaW9uQ29udGV4dCIsImNyZWF0ZVBlcm1pc3Npb25RdWV1ZU9wcyIsImxvZ1Blcm1pc3Npb25EZWNpc2lvbiIsIkNhblVzZVRvb2xGbiIsIlJlY29yZCIsInRvb2wiLCJpbnB1dCIsIklucHV0IiwidG9vbFVzZUNvbnRleHQiLCJhc3Npc3RhbnRNZXNzYWdlIiwidG9vbFVzZUlEIiwiZm9yY2VEZWNpc2lvbiIsIlByb21pc2UiLCJ1c2VDYW5Vc2VUb29sIiwic2V0VG9vbFVzZUNvbmZpcm1RdWV1ZSIsInNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCIsIiQiLCJfYyIsInQwIiwicmVzb2x2ZSIsImN0eCIsInJlc29sdmVJZkFib3J0ZWQiLCJkZWNpc2lvblByb21pc2UiLCJ1bmRlZmluZWQiLCJ0aGVuIiwicmVzdWx0IiwiYmVoYXZpb3IiLCJkZWNpc2lvblJlYXNvbiIsInR5cGUiLCJjbGFzc2lmaWVyIiwicmVhc29uIiwibG9nRGVjaXNpb24iLCJkZWNpc2lvbiIsInNvdXJjZSIsImJ1aWxkQWxsb3ciLCJ1cGRhdGVkSW5wdXQiLCJhcHBTdGF0ZSIsImdldEFwcFN0YXRlIiwiZGVzY3JpcHRpb24iLCJpc05vbkludGVyYWN0aXZlU2Vzc2lvbiIsIm9wdGlvbnMiLCJ0b29sUGVybWlzc2lvbkNvbnRleHQiLCJ0b29scyIsIm1lc3NhZ2VJZCIsInRvb2xOYW1lIiwibmFtZSIsImRpc3BsYXkiLCJ0aW1lc3RhbXAiLCJEYXRlIiwibm93IiwiYWRkTm90aWZpY2F0aW9uIiwia2V5IiwicHJpb3JpdHkiLCJqc3giLCJ1c2VyRmFjaW5nTmFtZSIsInRvTG93ZXJDYXNlIiwiYXdhaXRBdXRvbWF0ZWRDaGVja3NCZWZvcmVEaWFsb2ciLCJjb29yZGluYXRvckRlY2lzaW9uIiwicGVuZGluZ0NsYXNzaWZpZXJDaGVjayIsInN1Z2dlc3Rpb25zIiwicGVybWlzc2lvbk1vZGUiLCJtb2RlIiwic3dhcm1EZWNpc2lvbiIsInNwZWN1bGF0aXZlUHJvbWlzZSIsImNvbW1hbmQiLCJyYWNlUmVzdWx0IiwicmFjZSIsIl90ZW1wIiwiX3RlbXAyIiwibWF0Y2hlcyIsImNvbmZpZGVuY2UiLCJtYXRjaGVkUnVsZSIsIm1hdGNoZWREZXNjcmlwdGlvbiIsImNvbnN0IiwiYnJpZGdlQ2FsbGJhY2tzIiwicmVwbEJyaWRnZVBlcm1pc3Npb25DYWxsYmFja3MiLCJjaGFubmVsQ2FsbGJhY2tzIiwiY2hhbm5lbFBlcm1pc3Npb25DYWxsYmFja3MiLCJjYXRjaCIsImVycm9yIiwiY29uc3RydWN0b3IiLCJtZXNzYWdlIiwibG9nQ2FuY2VsbGVkIiwiY2FuY2VsQW5kQWJvcnQiLCJmaW5hbGx5IiwicmVzIiwic2V0VGltZW91dCIsInIiXSwic291cmNlcyI6WyJ1c2VDYW5Vc2VUb29sLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCB7IEFQSVVzZXJBYm9ydEVycm9yIH0gZnJvbSAnQGFudGhyb3BpYy1haS9zZGsnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZUNhbGxiYWNrIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICB0eXBlIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gIGxvZ0V2ZW50LFxufSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgc2FuaXRpemVUb29sTmFtZUZvckFuYWx5dGljcyB9IGZyb20gJ3NyYy9zZXJ2aWNlcy9hbmFseXRpY3MvbWV0YWRhdGEuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xVc2VDb25maXJtIH0gZnJvbSAnLi4vY29tcG9uZW50cy9wZXJtaXNzaW9ucy9QZXJtaXNzaW9uUmVxdWVzdC5qcydcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7XG4gIFRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgVG9vbCBhcyBUb29sVHlwZSxcbiAgVG9vbFVzZUNvbnRleHQsXG59IGZyb20gJy4uL1Rvb2wuanMnXG5pbXBvcnQge1xuICBjb25zdW1lU3BlY3VsYXRpdmVDbGFzc2lmaWVyQ2hlY2ssXG4gIHBlZWtTcGVjdWxhdGl2ZUNsYXNzaWZpZXJDaGVjayxcbn0gZnJvbSAnLi4vdG9vbHMvQmFzaFRvb2wvYmFzaFBlcm1pc3Npb25zLmpzJ1xuaW1wb3J0IHsgQkFTSF9UT09MX05BTUUgfSBmcm9tICcuLi90b29scy9CYXNoVG9vbC90b29sTmFtZS5qcydcbmltcG9ydCB0eXBlIHsgQXNzaXN0YW50TWVzc2FnZSB9IGZyb20gJy4uL3R5cGVzL21lc3NhZ2UuanMnXG5pbXBvcnQgeyByZWNvcmRBdXRvTW9kZURlbmlhbCB9IGZyb20gJy4uL3V0aWxzL2F1dG9Nb2RlRGVuaWFscy5qcydcbmltcG9ydCB7XG4gIGNsZWFyQ2xhc3NpZmllckNoZWNraW5nLFxuICBzZXRDbGFzc2lmaWVyQXBwcm92YWwsXG4gIHNldFlvbG9DbGFzc2lmaWVyQXBwcm92YWwsXG59IGZyb20gJy4uL3V0aWxzL2NsYXNzaWZpZXJBcHByb3ZhbHMuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IEFib3J0RXJyb3IgfSBmcm9tICcuLi91dGlscy9lcnJvcnMuanMnXG5pbXBvcnQgeyBsb2dFcnJvciB9IGZyb20gJy4uL3V0aWxzL2xvZy5qcydcbmltcG9ydCB0eXBlIHsgUGVybWlzc2lvbkRlY2lzaW9uIH0gZnJvbSAnLi4vdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvblJlc3VsdC5qcydcbmltcG9ydCB7IGhhc1Blcm1pc3Npb25zVG9Vc2VUb29sIH0gZnJvbSAnLi4vdXRpbHMvcGVybWlzc2lvbnMvcGVybWlzc2lvbnMuanMnXG5pbXBvcnQgeyBqc29uU3RyaW5naWZ5IH0gZnJvbSAnLi4vdXRpbHMvc2xvd09wZXJhdGlvbnMuanMnXG5pbXBvcnQgeyBoYW5kbGVDb29yZGluYXRvclBlcm1pc3Npb24gfSBmcm9tICcuL3Rvb2xQZXJtaXNzaW9uL2hhbmRsZXJzL2Nvb3JkaW5hdG9ySGFuZGxlci5qcydcbmltcG9ydCB7IGhhbmRsZUludGVyYWN0aXZlUGVybWlzc2lvbiB9IGZyb20gJy4vdG9vbFBlcm1pc3Npb24vaGFuZGxlcnMvaW50ZXJhY3RpdmVIYW5kbGVyLmpzJ1xuaW1wb3J0IHsgaGFuZGxlU3dhcm1Xb3JrZXJQZXJtaXNzaW9uIH0gZnJvbSAnLi90b29sUGVybWlzc2lvbi9oYW5kbGVycy9zd2FybVdvcmtlckhhbmRsZXIuanMnXG5pbXBvcnQge1xuICBjcmVhdGVQZXJtaXNzaW9uQ29udGV4dCxcbiAgY3JlYXRlUGVybWlzc2lvblF1ZXVlT3BzLFxufSBmcm9tICcuL3Rvb2xQZXJtaXNzaW9uL1Blcm1pc3Npb25Db250ZXh0LmpzJ1xuaW1wb3J0IHsgbG9nUGVybWlzc2lvbkRlY2lzaW9uIH0gZnJvbSAnLi90b29sUGVybWlzc2lvbi9wZXJtaXNzaW9uTG9nZ2luZy5qcydcblxuZXhwb3J0IHR5cGUgQ2FuVXNlVG9vbEZuPFxuICBJbnB1dCBleHRlbmRzIFJlY29yZDxzdHJpbmcsIHVua25vd24+ID0gUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4+ID0gKFxuICB0b29sOiBUb29sVHlwZSxcbiAgaW5wdXQ6IElucHV0LFxuICB0b29sVXNlQ29udGV4dDogVG9vbFVzZUNvbnRleHQsXG4gIGFzc2lzdGFudE1lc3NhZ2U6IEFzc2lzdGFudE1lc3NhZ2UsXG4gIHRvb2xVc2VJRDogc3RyaW5nLFxuICBmb3JjZURlY2lzaW9uPzogUGVybWlzc2lvbkRlY2lzaW9uPElucHV0PixcbikgPT4gUHJvbWlzZTxQZXJtaXNzaW9uRGVjaXNpb248SW5wdXQ+PlxuXG5mdW5jdGlvbiB1c2VDYW5Vc2VUb29sKFxuICBzZXRUb29sVXNlQ29uZmlybVF1ZXVlOiBSZWFjdC5EaXNwYXRjaDxcbiAgICBSZWFjdC5TZXRTdGF0ZUFjdGlvbjxUb29sVXNlQ29uZmlybVtdPlxuICA+LFxuICBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQ6IChjb250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHQpID0+IHZvaWQsXG4pOiBDYW5Vc2VUb29sRm4ge1xuICByZXR1cm4gdXNlQ2FsbGJhY2s8Q2FuVXNlVG9vbEZuPihcbiAgICBhc3luYyAoXG4gICAgICB0b29sLFxuICAgICAgaW5wdXQsXG4gICAgICB0b29sVXNlQ29udGV4dCxcbiAgICAgIGFzc2lzdGFudE1lc3NhZ2UsXG4gICAgICB0b29sVXNlSUQsXG4gICAgICBmb3JjZURlY2lzaW9uLFxuICAgICkgPT4ge1xuICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKHJlc29sdmUgPT4ge1xuICAgICAgICBjb25zdCBjdHggPSBjcmVhdGVQZXJtaXNzaW9uQ29udGV4dChcbiAgICAgICAgICB0b29sLFxuICAgICAgICAgIGlucHV0LFxuICAgICAgICAgIHRvb2xVc2VDb250ZXh0LFxuICAgICAgICAgIGFzc2lzdGFudE1lc3NhZ2UsXG4gICAgICAgICAgdG9vbFVzZUlELFxuICAgICAgICAgIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgICBjcmVhdGVQZXJtaXNzaW9uUXVldWVPcHMoc2V0VG9vbFVzZUNvbmZpcm1RdWV1ZSksXG4gICAgICAgIClcblxuICAgICAgICBpZiAoY3R4LnJlc29sdmVJZkFib3J0ZWQocmVzb2x2ZSkpIHJldHVyblxuXG4gICAgICAgIGNvbnN0IGRlY2lzaW9uUHJvbWlzZSA9XG4gICAgICAgICAgZm9yY2VEZWNpc2lvbiAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgICA/IFByb21pc2UucmVzb2x2ZShmb3JjZURlY2lzaW9uKVxuICAgICAgICAgICAgOiBoYXNQZXJtaXNzaW9uc1RvVXNlVG9vbChcbiAgICAgICAgICAgICAgICB0b29sLFxuICAgICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgICAgIHRvb2xVc2VDb250ZXh0LFxuICAgICAgICAgICAgICAgIGFzc2lzdGFudE1lc3NhZ2UsXG4gICAgICAgICAgICAgICAgdG9vbFVzZUlELFxuICAgICAgICAgICAgICApXG5cbiAgICAgICAgcmV0dXJuIGRlY2lzaW9uUHJvbWlzZVxuICAgICAgICAgIC50aGVuKGFzeW5jIHJlc3VsdCA9PiB7XG4gICAgICAgICAgICAvLyBbQU5ULU9OTFldIExvZyBhbGwgdG9vbCBwZXJtaXNzaW9uIGRlY2lzaW9ucyB3aXRoIHRvb2wgbmFtZSBhbmQgYXJnc1xuICAgICAgICAgICAgaWYgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcpIHtcbiAgICAgICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2ludGVybmFsX3Rvb2xfcGVybWlzc2lvbl9kZWNpc2lvbicsIHtcbiAgICAgICAgICAgICAgICB0b29sTmFtZTogc2FuaXRpemVUb29sTmFtZUZvckFuYWx5dGljcyh0b29sLm5hbWUpLFxuICAgICAgICAgICAgICAgIGJlaGF2aW9yOlxuICAgICAgICAgICAgICAgICAgcmVzdWx0LmJlaGF2aW9yIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgICAgICAgLy8gTm90ZTogaW5wdXQgY29udGFpbnMgY29kZS9maWxlcGF0aHMsIG9ubHkgbG9nIGZvciBhbnRzXG4gICAgICAgICAgICAgICAgaW5wdXQ6IGpzb25TdHJpbmdpZnkoXG4gICAgICAgICAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICAgICAgICApIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgICAgICAgbWVzc2FnZUlEOlxuICAgICAgICAgICAgICAgICAgY3R4Lm1lc3NhZ2VJZCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgICAgIGlzTWNwOiB0b29sLmlzTWNwID8/IGZhbHNlLFxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBIYXMgcGVybWlzc2lvbnMgdG8gdXNlIHRvb2wsIGdyYW50ZWQgaW4gY29uZmlnXG4gICAgICAgICAgICBpZiAocmVzdWx0LmJlaGF2aW9yID09PSAnYWxsb3cnKSB7XG4gICAgICAgICAgICAgIGlmIChjdHgucmVzb2x2ZUlmQWJvcnRlZChyZXNvbHZlKSkgcmV0dXJuXG4gICAgICAgICAgICAgIC8vIFRyYWNrIGF1dG8gbW9kZSBjbGFzc2lmaWVyIGFwcHJvdmFscyBmb3IgVUkgZGlzcGxheVxuICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgZmVhdHVyZSgnVFJBTlNDUklQVF9DTEFTU0lGSUVSJykgJiZcbiAgICAgICAgICAgICAgICByZXN1bHQuZGVjaXNpb25SZWFzb24/LnR5cGUgPT09ICdjbGFzc2lmaWVyJyAmJlxuICAgICAgICAgICAgICAgIHJlc3VsdC5kZWNpc2lvblJlYXNvbi5jbGFzc2lmaWVyID09PSAnYXV0by1tb2RlJ1xuICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICBzZXRZb2xvQ2xhc3NpZmllckFwcHJvdmFsKFxuICAgICAgICAgICAgICAgICAgdG9vbFVzZUlELFxuICAgICAgICAgICAgICAgICAgcmVzdWx0LmRlY2lzaW9uUmVhc29uLnJlYXNvbixcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBjdHgubG9nRGVjaXNpb24oeyBkZWNpc2lvbjogJ2FjY2VwdCcsIHNvdXJjZTogJ2NvbmZpZycgfSlcblxuICAgICAgICAgICAgICByZXNvbHZlKFxuICAgICAgICAgICAgICAgIGN0eC5idWlsZEFsbG93KHJlc3VsdC51cGRhdGVkSW5wdXQgPz8gaW5wdXQsIHtcbiAgICAgICAgICAgICAgICAgIGRlY2lzaW9uUmVhc29uOiByZXN1bHQuZGVjaXNpb25SZWFzb24sXG4gICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGFwcFN0YXRlID0gdG9vbFVzZUNvbnRleHQuZ2V0QXBwU3RhdGUoKVxuICAgICAgICAgICAgY29uc3QgZGVzY3JpcHRpb24gPSBhd2FpdCB0b29sLmRlc2NyaXB0aW9uKGlucHV0IGFzIG5ldmVyLCB7XG4gICAgICAgICAgICAgIGlzTm9uSW50ZXJhY3RpdmVTZXNzaW9uOlxuICAgICAgICAgICAgICAgIHRvb2xVc2VDb250ZXh0Lm9wdGlvbnMuaXNOb25JbnRlcmFjdGl2ZVNlc3Npb24sXG4gICAgICAgICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dDogYXBwU3RhdGUudG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICAgICAgICAgICAgICB0b29sczogdG9vbFVzZUNvbnRleHQub3B0aW9ucy50b29scyxcbiAgICAgICAgICAgIH0pXG5cbiAgICAgICAgICAgIGlmIChjdHgucmVzb2x2ZUlmQWJvcnRlZChyZXNvbHZlKSkgcmV0dXJuXG5cbiAgICAgICAgICAgIC8vIERvZXMgbm90IGhhdmUgcGVybWlzc2lvbnMgdG8gdXNlIHRvb2wsIGNoZWNrIHRoZSBiZWhhdmlvclxuICAgICAgICAgICAgc3dpdGNoIChyZXN1bHQuYmVoYXZpb3IpIHtcbiAgICAgICAgICAgICAgY2FzZSAnZGVueSc6IHtcbiAgICAgICAgICAgICAgICBsb2dQZXJtaXNzaW9uRGVjaXNpb24oXG4gICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIHRvb2wsXG4gICAgICAgICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgICAgICAgICB0b29sVXNlQ29udGV4dCxcbiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZUlkOiBjdHgubWVzc2FnZUlkLFxuICAgICAgICAgICAgICAgICAgICB0b29sVXNlSUQsXG4gICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgeyBkZWNpc2lvbjogJ3JlamVjdCcsIHNvdXJjZTogJ2NvbmZpZycgfSxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgZmVhdHVyZSgnVFJBTlNDUklQVF9DTEFTU0lGSUVSJykgJiZcbiAgICAgICAgICAgICAgICAgIHJlc3VsdC5kZWNpc2lvblJlYXNvbj8udHlwZSA9PT0gJ2NsYXNzaWZpZXInICYmXG4gICAgICAgICAgICAgICAgICByZXN1bHQuZGVjaXNpb25SZWFzb24uY2xhc3NpZmllciA9PT0gJ2F1dG8tbW9kZSdcbiAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgIHJlY29yZEF1dG9Nb2RlRGVuaWFsKHtcbiAgICAgICAgICAgICAgICAgICAgdG9vbE5hbWU6IHRvb2wubmFtZSxcbiAgICAgICAgICAgICAgICAgICAgZGlzcGxheTogZGVzY3JpcHRpb24sXG4gICAgICAgICAgICAgICAgICAgIHJlYXNvbjogcmVzdWx0LmRlY2lzaW9uUmVhc29uLnJlYXNvbiA/PyAnJyxcbiAgICAgICAgICAgICAgICAgICAgdGltZXN0YW1wOiBEYXRlLm5vdygpLFxuICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgIHRvb2xVc2VDb250ZXh0LmFkZE5vdGlmaWNhdGlvbj8uKHtcbiAgICAgICAgICAgICAgICAgICAga2V5OiAnYXV0by1tb2RlLWRlbmllZCcsXG4gICAgICAgICAgICAgICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgICAgICAgICAgICAganN4OiAoXG4gICAgICAgICAgICAgICAgICAgICAgPD5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAge3Rvb2wudXNlckZhY2luZ05hbWUoaW5wdXQpLnRvTG93ZXJDYXNlKCl9IGRlbmllZCBieVxuICAgICAgICAgICAgICAgICAgICAgICAgICBhdXRvIG1vZGVcbiAgICAgICAgICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiDCtyAvcGVybWlzc2lvbnM8L1RleHQ+XG4gICAgICAgICAgICAgICAgICAgICAgPC8+XG4gICAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICByZXNvbHZlKHJlc3VsdClcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIGNhc2UgJ2Fzayc6IHtcbiAgICAgICAgICAgICAgICAvLyBGb3IgY29vcmRpbmF0b3Igd29ya2VycywgYXdhaXQgYXV0b21hdGVkIGNoZWNrcyBiZWZvcmUgc2hvd2luZyBkaWFsb2cuXG4gICAgICAgICAgICAgICAgLy8gQmFja2dyb3VuZCB3b3JrZXJzIHNob3VsZCBvbmx5IGludGVycnVwdCB0aGUgdXNlciB3aGVuIGF1dG9tYXRlZCBjaGVja3MgY2FuJ3QgZGVjaWRlLlxuICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgIGFwcFN0YXRlLnRvb2xQZXJtaXNzaW9uQ29udGV4dFxuICAgICAgICAgICAgICAgICAgICAuYXdhaXRBdXRvbWF0ZWRDaGVja3NCZWZvcmVEaWFsb2dcbiAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGNvb3JkaW5hdG9yRGVjaXNpb24gPSBhd2FpdCBoYW5kbGVDb29yZGluYXRvclBlcm1pc3Npb24oXG4gICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICBjdHgsXG4gICAgICAgICAgICAgICAgICAgICAgLi4uKGZlYXR1cmUoJ0JBU0hfQ0xBU1NJRklFUicpXG4gICAgICAgICAgICAgICAgICAgICAgICA/IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZW5kaW5nQ2xhc3NpZmllckNoZWNrOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnBlbmRpbmdDbGFzc2lmaWVyQ2hlY2ssXG4gICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIDoge30pLFxuICAgICAgICAgICAgICAgICAgICAgIHVwZGF0ZWRJbnB1dDogcmVzdWx0LnVwZGF0ZWRJbnB1dCxcbiAgICAgICAgICAgICAgICAgICAgICBzdWdnZXN0aW9uczogcmVzdWx0LnN1Z2dlc3Rpb25zLFxuICAgICAgICAgICAgICAgICAgICAgIHBlcm1pc3Npb25Nb2RlOiBhcHBTdGF0ZS50b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSxcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgIGlmIChjb29yZGluYXRvckRlY2lzaW9uKSB7XG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoY29vcmRpbmF0b3JEZWNpc2lvbilcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAvLyBudWxsIG1lYW5zIG5laXRoZXIgYXV0b21hdGVkIGNoZWNrIHJlc29sdmVkIC0tIGZhbGwgdGhyb3VnaCB0byBkaWFsb2cgYmVsb3cuXG4gICAgICAgICAgICAgICAgICAvLyBIb29rcyBhbHJlYWR5IHJhbiwgY2xhc3NpZmllciBhbHJlYWR5IGNvbnN1bWVkLlxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIEFmdGVyIGF3YWl0aW5nIGF1dG9tYXRlZCBjaGVja3MsIHZlcmlmeSB0aGUgcmVxdWVzdCB3YXNuJ3QgYWJvcnRlZFxuICAgICAgICAgICAgICAgIC8vIHdoaWxlIHdlIHdlcmUgd2FpdGluZy4gV2l0aG91dCB0aGlzIGNoZWNrLCBhIHN0YWxlIGRpYWxvZyBjb3VsZCBhcHBlYXIuXG4gICAgICAgICAgICAgICAgaWYgKGN0eC5yZXNvbHZlSWZBYm9ydGVkKHJlc29sdmUpKSByZXR1cm5cblxuICAgICAgICAgICAgICAgIC8vIEZvciBzd2FybSB3b3JrZXJzLCB0cnkgY2xhc3NpZmllciBhdXRvLWFwcHJvdmFsIHRoZW5cbiAgICAgICAgICAgICAgICAvLyBmb3J3YXJkIHBlcm1pc3Npb24gcmVxdWVzdHMgdG8gdGhlIGxlYWRlciB2aWEgbWFpbGJveC5cbiAgICAgICAgICAgICAgICBjb25zdCBzd2FybURlY2lzaW9uID0gYXdhaXQgaGFuZGxlU3dhcm1Xb3JrZXJQZXJtaXNzaW9uKHtcbiAgICAgICAgICAgICAgICAgIGN0eCxcbiAgICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uLFxuICAgICAgICAgICAgICAgICAgLi4uKGZlYXR1cmUoJ0JBU0hfQ0xBU1NJRklFUicpXG4gICAgICAgICAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgICAgICAgICAgcGVuZGluZ0NsYXNzaWZpZXJDaGVjazogcmVzdWx0LnBlbmRpbmdDbGFzc2lmaWVyQ2hlY2ssXG4gICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICA6IHt9KSxcbiAgICAgICAgICAgICAgICAgIHVwZGF0ZWRJbnB1dDogcmVzdWx0LnVwZGF0ZWRJbnB1dCxcbiAgICAgICAgICAgICAgICAgIHN1Z2dlc3Rpb25zOiByZXN1bHQuc3VnZ2VzdGlvbnMsXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICBpZiAoc3dhcm1EZWNpc2lvbikge1xuICAgICAgICAgICAgICAgICAgcmVzb2x2ZShzd2FybURlY2lzaW9uKVxuICAgICAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gR3JhY2UgcGVyaW9kOiB3YWl0IHVwIHRvIDJzIGZvciBzcGVjdWxhdGl2ZSBjbGFzc2lmaWVyXG4gICAgICAgICAgICAgICAgLy8gdG8gcmVzb2x2ZSBiZWZvcmUgc2hvd2luZyB0aGUgZGlhbG9nIChtYWluIGFnZW50IG9ubHkpXG4gICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgZmVhdHVyZSgnQkFTSF9DTEFTU0lGSUVSJykgJiZcbiAgICAgICAgICAgICAgICAgIHJlc3VsdC5wZW5kaW5nQ2xhc3NpZmllckNoZWNrICYmXG4gICAgICAgICAgICAgICAgICB0b29sLm5hbWUgPT09IEJBU0hfVE9PTF9OQU1FICYmXG4gICAgICAgICAgICAgICAgICAhYXBwU3RhdGUudG9vbFBlcm1pc3Npb25Db250ZXh0XG4gICAgICAgICAgICAgICAgICAgIC5hd2FpdEF1dG9tYXRlZENoZWNrc0JlZm9yZURpYWxvZ1xuICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgY29uc3Qgc3BlY3VsYXRpdmVQcm9taXNlID0gcGVla1NwZWN1bGF0aXZlQ2xhc3NpZmllckNoZWNrKFxuICAgICAgICAgICAgICAgICAgICAoaW5wdXQgYXMgeyBjb21tYW5kOiBzdHJpbmcgfSkuY29tbWFuZCxcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgIGlmIChzcGVjdWxhdGl2ZVByb21pc2UpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgcmFjZVJlc3VsdCA9IGF3YWl0IFByb21pc2UucmFjZShbXG4gICAgICAgICAgICAgICAgICAgICAgc3BlY3VsYXRpdmVQcm9taXNlLnRoZW4ociA9PiAoe1xuICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ3Jlc3VsdCcgYXMgY29uc3QsXG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHQ6IHIsXG4gICAgICAgICAgICAgICAgICAgICAgfSkpLFxuICAgICAgICAgICAgICAgICAgICAgIG5ldyBQcm9taXNlPHsgdHlwZTogJ3RpbWVvdXQnIH0+KHJlcyA9PlxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXJlc3RyaWN0ZWQtc3ludGF4IC0tIHJlc29sdmVzIHdpdGggYSB2YWx1ZSwgbm90IHZvaWRcbiAgICAgICAgICAgICAgICAgICAgICAgIHNldFRpbWVvdXQocmVzLCAyMDAwLCB7IHR5cGU6ICd0aW1lb3V0JyBhcyBjb25zdCB9KSxcbiAgICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgICBdKVxuXG4gICAgICAgICAgICAgICAgICAgIGlmIChjdHgucmVzb2x2ZUlmQWJvcnRlZChyZXNvbHZlKSkgcmV0dXJuXG5cbiAgICAgICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgICAgIHJhY2VSZXN1bHQudHlwZSA9PT0gJ3Jlc3VsdCcgJiZcbiAgICAgICAgICAgICAgICAgICAgICByYWNlUmVzdWx0LnJlc3VsdC5tYXRjaGVzICYmXG4gICAgICAgICAgICAgICAgICAgICAgcmFjZVJlc3VsdC5yZXN1bHQuY29uZmlkZW5jZSA9PT0gJ2hpZ2gnICYmXG4gICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZSgnQkFTSF9DTEFTU0lGSUVSJylcbiAgICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgLy8gQ2xhc3NpZmllciBhcHByb3ZlZCB3aXRoaW4gZ3JhY2UgcGVyaW9kIOKAlCBza2lwIGRpYWxvZ1xuICAgICAgICAgICAgICAgICAgICAgIHZvaWQgY29uc3VtZVNwZWN1bGF0aXZlQ2xhc3NpZmllckNoZWNrKFxuICAgICAgICAgICAgICAgICAgICAgICAgKGlucHV0IGFzIHsgY29tbWFuZDogc3RyaW5nIH0pLmNvbW1hbmQsXG4gICAgICAgICAgICAgICAgICAgICAgKVxuXG4gICAgICAgICAgICAgICAgICAgICAgY29uc3QgbWF0Y2hlZFJ1bGUgPVxuICAgICAgICAgICAgICAgICAgICAgICAgcmFjZVJlc3VsdC5yZXN1bHQubWF0Y2hlZERlc2NyaXB0aW9uID8/IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgICAgIGlmIChtYXRjaGVkUnVsZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgc2V0Q2xhc3NpZmllckFwcHJvdmFsKHRvb2xVc2VJRCwgbWF0Y2hlZFJ1bGUpXG4gICAgICAgICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgICAgICAgY3R4LmxvZ0RlY2lzaW9uKHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRlY2lzaW9uOiAnYWNjZXB0JyxcbiAgICAgICAgICAgICAgICAgICAgICAgIHNvdXJjZTogeyB0eXBlOiAnY2xhc3NpZmllcicgfSxcbiAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoXG4gICAgICAgICAgICAgICAgICAgICAgICBjdHguYnVpbGRBbGxvdyhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0LnVwZGF0ZWRJbnB1dCA/P1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIChpbnB1dCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiksXG4gICAgICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWNpc2lvblJlYXNvbjoge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZTogJ2NsYXNzaWZpZXInIGFzIGNvbnN0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3NpZmllcjogJ2Jhc2hfYWxsb3cnIGFzIGNvbnN0LFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhc29uOiBgQWxsb3dlZCBieSBwcm9tcHQgcnVsZTogXCIke3JhY2VSZXN1bHQucmVzdWx0Lm1hdGNoZWREZXNjcmlwdGlvbn1cImAsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIC8vIFRpbWVvdXQgb3Igbm8gbWF0Y2gg4oCUIGZhbGwgdGhyb3VnaCB0byBzaG93IGRpYWxvZ1xuICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIC8vIFNob3cgZGlhbG9nIGFuZCBzdGFydCBob29rcy9jbGFzc2lmaWVyIGluIGJhY2tncm91bmRcbiAgICAgICAgICAgICAgICBoYW5kbGVJbnRlcmFjdGl2ZVBlcm1pc3Npb24oXG4gICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIGN0eCxcbiAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRpb24sXG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdCxcbiAgICAgICAgICAgICAgICAgICAgYXdhaXRBdXRvbWF0ZWRDaGVja3NCZWZvcmVEaWFsb2c6XG4gICAgICAgICAgICAgICAgICAgICAgYXBwU3RhdGUudG9vbFBlcm1pc3Npb25Db250ZXh0XG4gICAgICAgICAgICAgICAgICAgICAgICAuYXdhaXRBdXRvbWF0ZWRDaGVja3NCZWZvcmVEaWFsb2csXG4gICAgICAgICAgICAgICAgICAgIGJyaWRnZUNhbGxiYWNrczogZmVhdHVyZSgnQlJJREdFX01PREUnKVxuICAgICAgICAgICAgICAgICAgICAgID8gYXBwU3RhdGUucmVwbEJyaWRnZVBlcm1pc3Npb25DYWxsYmFja3NcbiAgICAgICAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICAgICAgY2hhbm5lbENhbGxiYWNrczpcbiAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQ0hBTk5FTFMnKVxuICAgICAgICAgICAgICAgICAgICAgICAgPyBhcHBTdGF0ZS5jaGFubmVsUGVybWlzc2lvbkNhbGxiYWNrc1xuICAgICAgICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgcmVzb2x2ZSxcbiAgICAgICAgICAgICAgICApXG5cbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pXG4gICAgICAgICAgLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgZXJyb3IgaW5zdGFuY2VvZiBBYm9ydEVycm9yIHx8XG4gICAgICAgICAgICAgIGVycm9yIGluc3RhbmNlb2YgQVBJVXNlckFib3J0RXJyb3JcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgICAgICAgYFBlcm1pc3Npb24gY2hlY2sgdGhyZXcgJHtlcnJvci5jb25zdHJ1Y3Rvci5uYW1lfSBmb3IgdG9vbD0ke3Rvb2wubmFtZX06ICR7ZXJyb3IubWVzc2FnZX1gLFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIGN0eC5sb2dDYW5jZWxsZWQoKVxuICAgICAgICAgICAgICByZXNvbHZlKGN0eC5jYW5jZWxBbmRBYm9ydCh1bmRlZmluZWQsIHRydWUpKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgbG9nRXJyb3IoZXJyb3IpXG4gICAgICAgICAgICAgIHJlc29sdmUoY3R4LmNhbmNlbEFuZEFib3J0KHVuZGVmaW5lZCwgdHJ1ZSkpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSlcbiAgICAgICAgICAuZmluYWxseSgoKSA9PiB7XG4gICAgICAgICAgICBjbGVhckNsYXNzaWZpZXJDaGVja2luZyh0b29sVXNlSUQpXG4gICAgICAgICAgfSlcbiAgICAgIH0pXG4gICAgfSxcbiAgICBbc2V0VG9vbFVzZUNvbmZpcm1RdWV1ZSwgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0XSxcbiAgKVxufVxuXG5leHBvcnQgZGVmYXVsdCB1c2VDYW5Vc2VUb29sXG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxPQUFPLFFBQVEsWUFBWTtBQUNwQyxTQUFTQyxpQkFBaUIsUUFBUSxtQkFBbUI7QUFDckQsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxXQUFXLFFBQVEsT0FBTztBQUNuQyxTQUNFLEtBQUtDLDBEQUEwRCxFQUMvREMsUUFBUSxRQUNILGlDQUFpQztBQUN4QyxTQUFTQyw0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 \ No newline at end of file diff --git a/hooks/useCancelRequest.ts b/hooks/useCancelRequest.ts new file mode 100644 index 0000000..4382e27 --- /dev/null +++ b/hooks/useCancelRequest.ts @@ -0,0 +1,276 @@ +/** + * CancelRequestHandler component for handling cancel/escape keybinding. + * + * Must be rendered inside KeybindingSetup to have access to the keybinding context. + * This component renders nothing - it just registers the cancel keybinding handler. + */ +import { useCallback, useRef } from 'react' +import { logEvent } from 'src/services/analytics/index.js' +import type { AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS } from 'src/services/analytics/metadata.js' +import { + useAppState, + useAppStateStore, + useSetAppState, +} from 'src/state/AppState.js' +import { isVimModeEnabled } from '../components/PromptInput/utils.js' +import type { ToolUseConfirm } from '../components/permissions/PermissionRequest.js' +import type { SpinnerMode } from '../components/Spinner/types.js' +import { useNotifications } from '../context/notifications.js' +import { useIsOverlayActive } from '../context/overlayContext.js' +import { useCommandQueue } from '../hooks/useCommandQueue.js' +import { getShortcutDisplay } from '../keybindings/shortcutFormat.js' +import { useKeybinding } from '../keybindings/useKeybinding.js' +import type { Screen } from '../screens/REPL.js' +import { exitTeammateView } from '../state/teammateViewHelpers.js' +import { + killAllRunningAgentTasks, + markAgentsNotified, +} from '../tasks/LocalAgentTask/LocalAgentTask.js' +import type { PromptInputMode, VimMode } from '../types/textInputTypes.js' +import { + clearCommandQueue, + enqueuePendingNotification, + hasCommandsInQueue, +} from '../utils/messageQueueManager.js' +import { emitTaskTerminatedSdk } from '../utils/sdkEventQueue.js' + +/** Time window in ms during which a second press kills all background agents. */ +const KILL_AGENTS_CONFIRM_WINDOW_MS = 3000 + +type CancelRequestHandlerProps = { + setToolUseConfirmQueue: ( + f: (toolUseConfirmQueue: ToolUseConfirm[]) => ToolUseConfirm[], + ) => void + onCancel: () => void + onAgentsKilled: () => void + isMessageSelectorVisible: boolean + screen: Screen + abortSignal?: AbortSignal + popCommandFromQueue?: () => void + vimMode?: VimMode + isLocalJSXCommand?: boolean + isSearchingHistory?: boolean + isHelpOpen?: boolean + inputMode?: PromptInputMode + inputValue?: string + streamMode?: SpinnerMode +} + +/** + * Component that handles cancel requests via keybinding. + * Renders null but registers the 'chat:cancel' keybinding handler. + */ +export function CancelRequestHandler(props: CancelRequestHandlerProps): null { + const { + setToolUseConfirmQueue, + onCancel, + onAgentsKilled, + isMessageSelectorVisible, + screen, + abortSignal, + popCommandFromQueue, + vimMode, + isLocalJSXCommand, + isSearchingHistory, + isHelpOpen, + inputMode, + inputValue, + streamMode, + } = props + const store = useAppStateStore() + const setAppState = useSetAppState() + const queuedCommandsLength = useCommandQueue().length + const { addNotification, removeNotification } = useNotifications() + const lastKillAgentsPressRef = useRef(0) + const viewSelectionMode = useAppState(s => s.viewSelectionMode) + + const handleCancel = useCallback(() => { + const cancelProps = { + source: + 'escape' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + streamMode: + streamMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + } + + // Priority 1: If there's an active task running, cancel it first + // This takes precedence over queue management so users can always interrupt Claude + if (abortSignal !== undefined && !abortSignal.aborted) { + logEvent('tengu_cancel', cancelProps) + setToolUseConfirmQueue(() => []) + onCancel() + return + } + + // Priority 2: Pop queue when Claude is idle (no running task to cancel) + if (hasCommandsInQueue()) { + if (popCommandFromQueue) { + popCommandFromQueue() + return + } + } + + // Fallback: nothing to cancel or pop (shouldn't reach here if isActive is correct) + logEvent('tengu_cancel', cancelProps) + setToolUseConfirmQueue(() => []) + onCancel() + }, [ + abortSignal, + popCommandFromQueue, + setToolUseConfirmQueue, + onCancel, + streamMode, + ]) + + // Determine if this handler should be active + // Other contexts (Transcript, HistorySearch, Help) have their own escape handlers + // Overlays (ModelPicker, ThinkingToggle, etc.) register themselves via useRegisterOverlay + // Local JSX commands (like /model, /btw) handle their own input + const isOverlayActive = useIsOverlayActive() + const canCancelRunningTask = abortSignal !== undefined && !abortSignal.aborted + const hasQueuedCommands = queuedCommandsLength > 0 + // When in bash/background mode with empty input, escape should exit the mode + // rather than cancel the request. Let PromptInput handle mode exit. + // This only applies to Escape, not Ctrl+C which should always cancel. + const isInSpecialModeWithEmptyInput = + inputMode !== undefined && inputMode !== 'prompt' && !inputValue + // When viewing a teammate's transcript, let useBackgroundTaskNavigation handle Escape + const isViewingTeammate = viewSelectionMode === 'viewing-agent' + // Context guards: other screens/overlays handle their own cancel + const isContextActive = + screen !== 'transcript' && + !isSearchingHistory && + !isMessageSelectorVisible && + !isLocalJSXCommand && + !isHelpOpen && + !isOverlayActive && + !(isVimModeEnabled() && vimMode === 'INSERT') + + // Escape (chat:cancel) defers to mode-exit when in special mode with empty + // input, and to useBackgroundTaskNavigation when viewing a teammate + const isEscapeActive = + isContextActive && + (canCancelRunningTask || hasQueuedCommands) && + !isInSpecialModeWithEmptyInput && + !isViewingTeammate + + // Ctrl+C (app:interrupt): when viewing a teammate, stops everything and + // returns to main thread. Otherwise just handleCancel. Must NOT claim + // ctrl+c when main is idle at the prompt — that blocks the copy-selection + // handler and double-press-to-exit from ever seeing the keypress. + const isCtrlCActive = + isContextActive && + (canCancelRunningTask || hasQueuedCommands || isViewingTeammate) + + useKeybinding('chat:cancel', handleCancel, { + context: 'Chat', + isActive: isEscapeActive, + }) + + // Shared kill path: stop all agents, suppress per-agent notifications, + // emit SDK events, enqueue a single aggregate model-facing notification. + // Returns true if anything was killed. + const killAllAgentsAndNotify = useCallback((): boolean => { + const tasks = store.getState().tasks + const running = Object.entries(tasks).filter( + ([, t]) => t.type === 'local_agent' && t.status === 'running', + ) + if (running.length === 0) return false + killAllRunningAgentTasks(tasks, setAppState) + const descriptions: string[] = [] + for (const [taskId, task] of running) { + markAgentsNotified(taskId, setAppState) + descriptions.push(task.description) + emitTaskTerminatedSdk(taskId, 'stopped', { + toolUseId: task.toolUseId, + summary: task.description, + }) + } + const summary = + descriptions.length === 1 + ? `Background agent "${descriptions[0]}" was stopped by the user.` + : `${descriptions.length} background agents were stopped by the user: ${descriptions.map(d => `"${d}"`).join(', ')}.` + enqueuePendingNotification({ value: summary, mode: 'task-notification' }) + onAgentsKilled() + return true + }, [store, setAppState, onAgentsKilled]) + + // Ctrl+C (app:interrupt). Scoped to teammate-view: killing agents from the + // main prompt stays a deliberate gesture (chat:killAgents), not a + // side-effect of cancelling a turn. + const handleInterrupt = useCallback(() => { + if (isViewingTeammate) { + killAllAgentsAndNotify() + exitTeammateView(setAppState) + } + if (canCancelRunningTask || hasQueuedCommands) { + handleCancel() + } + }, [ + isViewingTeammate, + killAllAgentsAndNotify, + setAppState, + canCancelRunningTask, + hasQueuedCommands, + handleCancel, + ]) + + useKeybinding('app:interrupt', handleInterrupt, { + context: 'Global', + isActive: isCtrlCActive, + }) + + // chat:killAgents uses a two-press pattern: first press shows a + // confirmation hint, second press within the window actually kills all + // agents. Reads tasks from the store directly to avoid stale closures. + const handleKillAgents = useCallback(() => { + const tasks = store.getState().tasks + const hasRunningAgents = Object.values(tasks).some( + t => t.type === 'local_agent' && t.status === 'running', + ) + if (!hasRunningAgents) { + addNotification({ + key: 'kill-agents-none', + text: 'No background agents running', + priority: 'immediate', + timeoutMs: 2000, + }) + return + } + const now = Date.now() + const elapsed = now - lastKillAgentsPressRef.current + if (elapsed <= KILL_AGENTS_CONFIRM_WINDOW_MS) { + // Second press within window -- kill all background agents + lastKillAgentsPressRef.current = 0 + removeNotification('kill-agents-confirm') + logEvent('tengu_cancel', { + source: + 'kill_agents' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + clearCommandQueue() + killAllAgentsAndNotify() + return + } + // First press -- show confirmation hint in status bar + lastKillAgentsPressRef.current = now + const shortcut = getShortcutDisplay( + 'chat:killAgents', + 'Chat', + 'ctrl+x ctrl+k', + ) + addNotification({ + key: 'kill-agents-confirm', + text: `Press ${shortcut} again to stop background agents`, + priority: 'immediate', + timeoutMs: KILL_AGENTS_CONFIRM_WINDOW_MS, + }) + }, [store, addNotification, removeNotification, killAllAgentsAndNotify]) + + // Must stay always-active: ctrl+x is consumed as a chord prefix regardless + // of isActive (because ctrl+x ctrl+e is always live), so an inactive handler + // here would leak ctrl+k to readline kill-line. Handler gates internally. + useKeybinding('chat:killAgents', handleKillAgents, { + context: 'Chat', + }) + + return null +} diff --git a/hooks/useChromeExtensionNotification.tsx b/hooks/useChromeExtensionNotification.tsx new file mode 100644 index 0000000..a7d4416 --- /dev/null +++ b/hooks/useChromeExtensionNotification.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { Text } from '../ink.js'; +import { isClaudeAISubscriber } from '../utils/auth.js'; +import { isChromeExtensionInstalled, shouldEnableClaudeInChrome } from '../utils/claudeInChrome/setup.js'; +import { isRunningOnHomespace } from '../utils/envUtils.js'; +import { useStartupNotification } from './notifs/useStartupNotification.js'; +function getChromeFlag(): boolean | undefined { + if (process.argv.includes('--chrome')) { + return true; + } + if (process.argv.includes('--no-chrome')) { + return false; + } + return undefined; +} +export function useChromeExtensionNotification() { + useStartupNotification(_temp); +} +async function _temp() { + const chromeFlag = getChromeFlag(); + if (!shouldEnableClaudeInChrome(chromeFlag)) { + return null; + } + if (true && !isClaudeAISubscriber()) { + return { + key: "chrome-requires-subscription", + jsx: Claude in Chrome requires a claude.ai subscription, + priority: "immediate", + timeoutMs: 5000 + }; + } + const installed = await isChromeExtensionInstalled(); + if (!installed && !isRunningOnHomespace()) { + return { + key: "chrome-extension-not-detected", + jsx: Chrome extension not detected · https://claude.ai/chrome to install, + priority: "immediate", + timeoutMs: 3000 + }; + } + if (chromeFlag === undefined) { + return { + key: "claude-in-chrome-default-enabled", + text: "Claude in Chrome enabled \xB7 /chrome", + priority: "low" + }; + } + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlRleHQiLCJpc0NsYXVkZUFJU3Vic2NyaWJlciIsImlzQ2hyb21lRXh0ZW5zaW9uSW5zdGFsbGVkIiwic2hvdWxkRW5hYmxlQ2xhdWRlSW5DaHJvbWUiLCJpc1J1bm5pbmdPbkhvbWVzcGFjZSIsInVzZVN0YXJ0dXBOb3RpZmljYXRpb24iLCJnZXRDaHJvbWVGbGFnIiwicHJvY2VzcyIsImFyZ3YiLCJpbmNsdWRlcyIsInVuZGVmaW5lZCIsInVzZUNocm9tZUV4dGVuc2lvbk5vdGlmaWNhdGlvbiIsIl90ZW1wIiwiY2hyb21lRmxhZyIsImtleSIsImpzeCIsInByaW9yaXR5IiwidGltZW91dE1zIiwiaW5zdGFsbGVkIiwidGV4dCJdLCJzb3VyY2VzIjpbInVzZUNocm9tZUV4dGVuc2lvbk5vdGlmaWNhdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgaXNDbGF1ZGVBSVN1YnNjcmliZXIgfSBmcm9tICcuLi91dGlscy9hdXRoLmpzJ1xuaW1wb3J0IHtcbiAgaXNDaHJvbWVFeHRlbnNpb25JbnN0YWxsZWQsXG4gIHNob3VsZEVuYWJsZUNsYXVkZUluQ2hyb21lLFxufSBmcm9tICcuLi91dGlscy9jbGF1ZGVJbkNocm9tZS9zZXR1cC5qcydcbmltcG9ydCB7IGlzUnVubmluZ09uSG9tZXNwYWNlIH0gZnJvbSAnLi4vdXRpbHMvZW52VXRpbHMuanMnXG5pbXBvcnQgeyB1c2VTdGFydHVwTm90aWZpY2F0aW9uIH0gZnJvbSAnLi9ub3RpZnMvdXNlU3RhcnR1cE5vdGlmaWNhdGlvbi5qcydcblxuZnVuY3Rpb24gZ2V0Q2hyb21lRmxhZygpOiBib29sZWFuIHwgdW5kZWZpbmVkIHtcbiAgaWYgKHByb2Nlc3MuYXJndi5pbmNsdWRlcygnLS1jaHJvbWUnKSkge1xuICAgIHJldHVybiB0cnVlXG4gIH1cbiAgaWYgKHByb2Nlc3MuYXJndi5pbmNsdWRlcygnLS1uby1jaHJvbWUnKSkge1xuICAgIHJldHVybiBmYWxzZVxuICB9XG4gIHJldHVybiB1bmRlZmluZWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVzZUNocm9tZUV4dGVuc2lvbk5vdGlmaWNhdGlvbigpOiB2b2lkIHtcbiAgdXNlU3RhcnR1cE5vdGlmaWNhdGlvbihhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgY2hyb21lRmxhZyA9IGdldENocm9tZUZsYWcoKVxuICAgIGlmICghc2hvdWxkRW5hYmxlQ2xhdWRlSW5DaHJvbWUoY2hyb21lRmxhZykpIHJldHVybiBudWxsXG5cbiAgICAvLyBDbGF1ZGUgaW4gQ2hyb21lIGlzIG9ubHkgc3VwcG9ydGVkIGZvciBjbGF1ZGUuYWkgc3Vic2NyaWJlcnMgKHVubGVzcyB1c2VyIGlzIGFudClcbiAgICBpZiAoXCJleHRlcm5hbFwiICE9PSAnYW50JyAmJiAhaXNDbGF1ZGVBSVN1YnNjcmliZXIoKSkge1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAga2V5OiAnY2hyb21lLXJlcXVpcmVzLXN1YnNjcmlwdGlvbicsXG4gICAgICAgIGpzeDogKFxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5cbiAgICAgICAgICAgIENsYXVkZSBpbiBDaHJvbWUgcmVxdWlyZXMgYSBjbGF1ZGUuYWkgc3Vic2NyaXB0aW9uXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApLFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogNTAwMCxcbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBpbnN0YWxsZWQgPSBhd2FpdCBpc0Nocm9tZUV4dGVuc2lvbkluc3RhbGxlZCgpXG4gICAgaWYgKCFpbnN0YWxsZWQgJiYgIWlzUnVubmluZ09uSG9tZXNwYWNlKCkpIHtcbiAgICAgIC8vIFNraXAgbm90aWZpY2F0aW9uIG9uIEhvbWVzcGFjZSBzaW5jZSBDaHJvbWUgc2V0dXAgcmVxdWlyZXMgZGlmZmVyZW50IHN0ZXBzIChzZWUgZ28vaHNwcm94eSlcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGtleTogJ2Nocm9tZS1leHRlbnNpb24tbm90LWRldGVjdGVkJyxcbiAgICAgICAganN4OiAoXG4gICAgICAgICAgPFRleHQgY29sb3I9XCJ3YXJuaW5nXCI+XG4gICAgICAgICAgICBDaHJvbWUgZXh0ZW5zaW9uIG5vdCBkZXRlY3RlZCDCtyBodHRwczovL2NsYXVkZS5haS9jaHJvbWUgdG8gaW5zdGFsbFxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKSxcbiAgICAgICAgLy8gVE9ETyhoYWNreW9uKTogTG93ZXIgdGhlIHByaW9yaXR5IGlmIHRoZSBjbGF1ZGUtaW4tY2hyb21lIGludGVncmF0aW9uIGlzIG5vIGxvbmdlciBvcHQtaW5cbiAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICB0aW1lb3V0TXM6IDMwMDAsXG4gICAgICB9XG4gICAgfVxuICAgIGlmIChjaHJvbWVGbGFnID09PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIFNob3cgbG93IHByaW9yaXR5IG5vdGlmaWNhdGlvbiBvbmx5IHdoZW4gQ2hyb21lIGlzIGVuYWJsZWQgYnkgZGVmYXVsdFxuICAgICAgLy8gKG5vdCBleHBsaWNpdGx5IGVuYWJsZWQgd2l0aCAtLWNocm9tZSBvciBkaXNhYmxlZCB3aXRoIC0tbm8tY2hyb21lKVxuICAgICAgcmV0dXJuIHtcbiAgICAgICAga2V5OiAnY2xhdWRlLWluLWNocm9tZS1kZWZhdWx0LWVuYWJsZWQnLFxuICAgICAgICB0ZXh0OiBgQ2xhdWRlIGluIENocm9tZSBlbmFibGVkIMK3IC9jaHJvbWVgLFxuICAgICAgICBwcmlvcml0eTogJ2xvdycsXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBudWxsXG4gIH0pXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsSUFBSSxRQUFRLFdBQVc7QUFDaEMsU0FBU0Msb0JBQW9CLFFBQVEsa0JBQWtCO0FBQ3ZELFNBQ0VDLDBCQUEwQixFQUMxQkMsMEJBQTBCLFFBQ3JCLGtDQUFrQztBQUN6QyxTQUFTQyxvQkFBb0IsUUFBUSxzQkFBc0I7QUFDM0QsU0FBU0Msc0JBQXNCLFFBQVEsb0NBQW9DO0FBRTNFLFNBQVNDLGFBQWFBLENBQUEsQ0FBRSxFQUFFLE9BQU8sR0FBRyxTQUFTLENBQUM7RUFDNUMsSUFBSUMsT0FBTyxDQUFDQyxJQUFJLENBQUNDLFFBQVEsQ0FBQyxVQUFVLENBQUMsRUFBRTtJQUNyQyxPQUFPLElBQUk7RUFDYjtFQUNBLElBQUlGLE9BQU8sQ0FBQ0MsSUFBSSxDQUFDQyxRQUFRLENBQUMsYUFBYSxDQUFDLEVBQUU7SUFDeEMsT0FBTyxLQUFLO0VBQ2Q7RUFDQSxPQUFPQyxTQUFTO0FBQ2xCO0FBRUEsT0FBTyxTQUFBQywrQkFBQTtFQUNMTixzQkFBc0IsQ0FBQ08sS0EyQ3RCLENBQUM7QUFBQTtBQTVDRyxlQUFBQSxNQUFBO0VBRUgsTUFBQUMsVUFBQSxHQUFtQlAsYUFBYSxDQUFDLENBQUM7RUFDbEMsSUFBSSxDQUFDSCwwQkFBMEIsQ0FBQ1UsVUFBVSxDQUFDO0lBQUEsT0FBUyxJQUFJO0VBQUE7RUFHeEQsSUFBSSxJQUErQyxJQUEvQyxDQUF5Qlosb0JBQW9CLENBQUMsQ0FBQztJQUFBLE9BQzFDO01BQUFhLEdBQUEsRUFDQSw4QkFBOEI7TUFBQUMsR0FBQSxFQUVqQyxDQUFDLElBQUksQ0FBTyxLQUFPLENBQVAsT0FBTyxDQUFDLGtEQUVwQixFQUZDLElBQUksQ0FFRTtNQUFBQyxRQUFBLEVBRUMsV0FBVztNQUFBQyxTQUFBLEVBQ1Y7SUFDYixDQUFDO0VBQUE7RUFHSCxNQUFBQyxTQUFBLEdBQWtCLE1BQU1oQiwwQkFBMEIsQ0FBQyxDQUFDO0VBQ3BELElBQUksQ0FBQ2dCLFNBQW9DLElBQXJDLENBQWVkLG9CQUFvQixDQUFDLENBQUM7SUFBQSxPQUVoQztNQUFBVSxHQUFBLEVBQ0EsK0JBQStCO01BQUFDLEdBQUEsRUFFbEMsQ0FBQyxJQUFJLENBQU8sS0FBUyxDQUFULFNBQVMsQ0FBQyxtRUFFdEIsRUFGQyxJQUFJLENBRUU7TUFBQUMsUUFBQSxFQUdDLFdBQVc7TUFBQUMsU0FBQSxFQUNWO0lBQ2IsQ0FBQztFQUFBO0VBRUgsSUFBSUosVUFBVSxLQUFLSCxTQUFTO0lBQUEsT0FHbkI7TUFBQUksR0FBQSxFQUNBLGtDQUFrQztNQUFBSyxJQUFBLEVBQ2pDLHVDQUFvQztNQUFBSCxRQUFBLEVBQ2hDO0lBQ1osQ0FBQztFQUFBO0VBQ0YsT0FDTSxJQUFJO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/hooks/useClaudeCodeHintRecommendation.tsx b/hooks/useClaudeCodeHintRecommendation.tsx new file mode 100644 index 0000000..390185b --- /dev/null +++ b/hooks/useClaudeCodeHintRecommendation.tsx @@ -0,0 +1,129 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * Surfaces plugin-install prompts driven by `` tags + * that CLIs/SDKs emit to stderr. See docs/claude-code-hints.md. + * + * Show-once semantics: each plugin is prompted for at most once ever, + * recorded in config regardless of yes/no. The pre-store gate in + * maybeRecordPluginHint already dropped installed/shown/capped hints, so + * anything that reaches this hook is worth resolving. + */ + +import * as React from 'react'; +import { useNotifications } from '../context/notifications.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, logEvent } from '../services/analytics/index.js'; +import { clearPendingHint, getPendingHintSnapshot, markShownThisSession, subscribeToPendingHint } from '../utils/claudeCodeHints.js'; +import { logForDebugging } from '../utils/debug.js'; +import { disableHintRecommendations, markHintPluginShown, type PluginHintRecommendation, resolvePluginHint } from '../utils/plugins/hintRecommendation.js'; +import { installPluginFromMarketplace } from '../utils/plugins/pluginInstallationHelpers.js'; +import { installPluginAndNotify, usePluginRecommendationBase } from './usePluginRecommendationBase.js'; +type UseClaudeCodeHintRecommendationResult = { + recommendation: PluginHintRecommendation | null; + handleResponse: (response: 'yes' | 'no' | 'disable') => void; +}; +export function useClaudeCodeHintRecommendation() { + const $ = _c(11); + const pendingHint = React.useSyncExternalStore(subscribeToPendingHint, getPendingHintSnapshot); + const { + addNotification + } = useNotifications(); + const { + recommendation, + clearRecommendation, + tryResolve + } = usePluginRecommendationBase(); + let t0; + let t1; + if ($[0] !== pendingHint || $[1] !== tryResolve) { + t0 = () => { + if (!pendingHint) { + return; + } + tryResolve(async () => { + const resolved = await resolvePluginHint(pendingHint); + if (resolved) { + logForDebugging(`[useClaudeCodeHintRecommendation] surfacing ${resolved.pluginId} from ${resolved.sourceCommand}`); + markShownThisSession(); + } + if (getPendingHintSnapshot() === pendingHint) { + clearPendingHint(); + } + return resolved; + }); + }; + t1 = [pendingHint, tryResolve]; + $[0] = pendingHint; + $[1] = tryResolve; + $[2] = t0; + $[3] = t1; + } else { + t0 = $[2]; + t1 = $[3]; + } + React.useEffect(t0, t1); + let t2; + if ($[4] !== addNotification || $[5] !== clearRecommendation || $[6] !== recommendation) { + t2 = response => { + if (!recommendation) { + return; + } + markHintPluginShown(recommendation.pluginId); + logEvent("tengu_plugin_hint_response", { + _PROTO_plugin_name: recommendation.pluginName as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + _PROTO_marketplace_name: recommendation.marketplaceName as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + response: response as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + bb15: switch (response) { + case "yes": + { + const { + pluginId, + pluginName, + marketplaceName + } = recommendation; + installPluginAndNotify(pluginId, pluginName, "hint-plugin", addNotification, async pluginData => { + const result = await installPluginFromMarketplace({ + pluginId, + entry: pluginData.entry, + marketplaceName, + scope: "user", + trigger: "hint" + }); + if (!result.success) { + throw new Error(result.error); + } + }); + break bb15; + } + case "disable": + { + disableHintRecommendations(); + break bb15; + } + case "no": + } + clearRecommendation(); + }; + $[4] = addNotification; + $[5] = clearRecommendation; + $[6] = recommendation; + $[7] = t2; + } else { + t2 = $[7]; + } + const handleResponse = t2; + let t3; + if ($[8] !== handleResponse || $[9] !== recommendation) { + t3 = { + recommendation, + handleResponse + }; + $[8] = handleResponse; + $[9] = recommendation; + $[10] = t3; + } else { + t3 = $[10]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZU5vdGlmaWNhdGlvbnMiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwiQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX1BJSV9UQUdHRUQiLCJsb2dFdmVudCIsImNsZWFyUGVuZGluZ0hpbnQiLCJnZXRQZW5kaW5nSGludFNuYXBzaG90IiwibWFya1Nob3duVGhpc1Nlc3Npb24iLCJzdWJzY3JpYmVUb1BlbmRpbmdIaW50IiwibG9nRm9yRGVidWdnaW5nIiwiZGlzYWJsZUhpbnRSZWNvbW1lbmRhdGlvbnMiLCJtYXJrSGludFBsdWdpblNob3duIiwiUGx1Z2luSGludFJlY29tbWVuZGF0aW9uIiwicmVzb2x2ZVBsdWdpbkhpbnQiLCJpbnN0YWxsUGx1Z2luRnJvbU1hcmtldHBsYWNlIiwiaW5zdGFsbFBsdWdpbkFuZE5vdGlmeSIsInVzZVBsdWdpblJlY29tbWVuZGF0aW9uQmFzZSIsIlVzZUNsYXVkZUNvZGVIaW50UmVjb21tZW5kYXRpb25SZXN1bHQiLCJyZWNvbW1lbmRhdGlvbiIsImhhbmRsZVJlc3BvbnNlIiwicmVzcG9uc2UiLCJ1c2VDbGF1ZGVDb2RlSGludFJlY29tbWVuZGF0aW9uIiwiJCIsIl9jIiwicGVuZGluZ0hpbnQiLCJ1c2VTeW5jRXh0ZXJuYWxTdG9yZSIsImFkZE5vdGlmaWNhdGlvbiIsImNsZWFyUmVjb21tZW5kYXRpb24iLCJ0cnlSZXNvbHZlIiwidDAiLCJ0MSIsInJlc29sdmVkIiwicGx1Z2luSWQiLCJzb3VyY2VDb21tYW5kIiwidXNlRWZmZWN0IiwidDIiLCJfUFJPVE9fcGx1Z2luX25hbWUiLCJwbHVnaW5OYW1lIiwiX1BST1RPX21hcmtldHBsYWNlX25hbWUiLCJtYXJrZXRwbGFjZU5hbWUiLCJiYjE1IiwicGx1Z2luRGF0YSIsInJlc3VsdCIsImVudHJ5Iiwic2NvcGUiLCJ0cmlnZ2VyIiwic3VjY2VzcyIsIkVycm9yIiwiZXJyb3IiLCJ0MyJdLCJzb3VyY2VzIjpbInVzZUNsYXVkZUNvZGVIaW50UmVjb21tZW5kYXRpb24udHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU3VyZmFjZXMgcGx1Z2luLWluc3RhbGwgcHJvbXB0cyBkcml2ZW4gYnkgYDxjbGF1ZGUtY29kZS1oaW50IC8+YCB0YWdzXG4gKiB0aGF0IENMSXMvU0RLcyBlbWl0IHRvIHN0ZGVyci4gU2VlIGRvY3MvY2xhdWRlLWNvZGUtaGludHMubWQuXG4gKlxuICogU2hvdy1vbmNlIHNlbWFudGljczogZWFjaCBwbHVnaW4gaXMgcHJvbXB0ZWQgZm9yIGF0IG1vc3Qgb25jZSBldmVyLFxuICogcmVjb3JkZWQgaW4gY29uZmlnIHJlZ2FyZGxlc3Mgb2YgeWVzL25vLiBUaGUgcHJlLXN0b3JlIGdhdGUgaW5cbiAqIG1heWJlUmVjb3JkUGx1Z2luSGludCBhbHJlYWR5IGRyb3BwZWQgaW5zdGFsbGVkL3Nob3duL2NhcHBlZCBoaW50cywgc29cbiAqIGFueXRoaW5nIHRoYXQgcmVhY2hlcyB0aGlzIGhvb2sgaXMgd29ydGggcmVzb2x2aW5nLlxuICovXG5cbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJy4uL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB7XG4gIHR5cGUgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfUElJX1RBR0dFRCxcbiAgbG9nRXZlbnQsXG59IGZyb20gJy4uL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7XG4gIGNsZWFyUGVuZGluZ0hpbnQsXG4gIGdldFBlbmRpbmdIaW50U25hcHNob3QsXG4gIG1hcmtTaG93blRoaXNTZXNzaW9uLFxuICBzdWJzY3JpYmVUb1BlbmRpbmdIaW50LFxufSBmcm9tICcuLi91dGlscy9jbGF1ZGVDb2RlSGludHMuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7XG4gIGRpc2FibGVIaW50UmVjb21tZW5kYXRpb25zLFxuICBtYXJrSGludFBsdWdpblNob3duLFxuICB0eXBlIFBsdWdpbkhpbnRSZWNvbW1lbmRhdGlvbixcbiAgcmVzb2x2ZVBsdWdpbkhpbnQsXG59IGZyb20gJy4uL3V0aWxzL3BsdWdpbnMvaGludFJlY29tbWVuZGF0aW9uLmpzJ1xuaW1wb3J0IHsgaW5zdGFsbFBsdWdpbkZyb21NYXJrZXRwbGFjZSB9IGZyb20gJy4uL3V0aWxzL3BsdWdpbnMvcGx1Z2luSW5zdGFsbGF0aW9uSGVscGVycy5qcydcbmltcG9ydCB7XG4gIGluc3RhbGxQbHVnaW5BbmROb3RpZnksXG4gIHVzZVBsdWdpblJlY29tbWVuZGF0aW9uQmFzZSxcbn0gZnJvbSAnLi91c2VQbHVnaW5SZWNvbW1lbmRhdGlvbkJhc2UuanMnXG5cbnR5cGUgVXNlQ2xhdWRlQ29kZUhpbnRSZWNvbW1lbmRhdGlvblJlc3VsdCA9IHtcbiAgcmVjb21tZW5kYXRpb246IFBsdWdpbkhpbnRSZWNvbW1lbmRhdGlvbiB8IG51bGxcbiAgaGFuZGxlUmVzcG9uc2U6IChyZXNwb25zZTogJ3llcycgfCAnbm8nIHwgJ2Rpc2FibGUnKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VDbGF1ZGVDb2RlSGludFJlY29tbWVuZGF0aW9uKCk6IFVzZUNsYXVkZUNvZGVIaW50UmVjb21tZW5kYXRpb25SZXN1bHQge1xuICBjb25zdCBwZW5kaW5nSGludCA9IFJlYWN0LnVzZVN5bmNFeHRlcm5hbFN0b3JlKFxuICAgIHN1YnNjcmliZVRvUGVuZGluZ0hpbnQsXG4gICAgZ2V0UGVuZGluZ0hpbnRTbmFwc2hvdCxcbiAgKVxuICBjb25zdCB7IGFkZE5vdGlmaWNhdGlvbiB9ID0gdXNlTm90aWZpY2F0aW9ucygpXG4gIGNvbnN0IHsgcmVjb21tZW5kYXRpb24sIGNsZWFyUmVjb21tZW5kYXRpb24sIHRyeVJlc29sdmUgfSA9XG4gICAgdXNlUGx1Z2luUmVjb21tZW5kYXRpb25CYXNlPFBsdWdpbkhpbnRSZWNvbW1lbmRhdGlvbj4oKVxuXG4gIFJlYWN0LnVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKCFwZW5kaW5nSGludCkgcmV0dXJuXG4gICAgdHJ5UmVzb2x2ZShhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCByZXNvbHZlZCA9IGF3YWl0IHJlc29sdmVQbHVnaW5IaW50KHBlbmRpbmdIaW50KVxuICAgICAgaWYgKHJlc29sdmVkKSB7XG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICBgW3VzZUNsYXVkZUNvZGVIaW50UmVjb21tZW5kYXRpb25dIHN1cmZhY2luZyAke3Jlc29sdmVkLnBsdWdpbklkfSBmcm9tICR7cmVzb2x2ZWQuc291cmNlQ29tbWFuZH1gLFxuICAgICAgICApXG4gICAgICAgIG1hcmtTaG93blRoaXNTZXNzaW9uKClcbiAgICAgIH1cbiAgICAgIC8vIERyb3AgdGhlIHNsb3Qg4oCUIGJ1dCBvbmx5IGlmIGl0IHN0aWxsIGhvbGRzIHRoZSBoaW50IHdlIGp1c3RcbiAgICAgIC8vIHJlc29sdmVkLiBBIG5ld2VyIGhpbnQgbWF5IGhhdmUgb3ZlcndyaXR0ZW4gaXQgZHVyaW5nIHRoZSBhc3luY1xuICAgICAgLy8gbG9va3VwOyBkb24ndCBjbG9iYmVyIHRoYXQuXG4gICAgICBpZiAoZ2V0UGVuZGluZ0hpbnRTbmFwc2hvdCgpID09PSBwZW5kaW5nSGludCkge1xuICAgICAgICBjbGVhclBlbmRpbmdIaW50KClcbiAgICAgIH1cbiAgICAgIHJldHVybiByZXNvbHZlZFxuICAgIH0pXG4gIH0sIFtwZW5kaW5nSGludCwgdHJ5UmVzb2x2ZV0pXG5cbiAgY29uc3QgaGFuZGxlUmVzcG9uc2UgPSBSZWFjdC51c2VDYWxsYmFjayhcbiAgICAocmVzcG9uc2U6ICd5ZXMnIHwgJ25vJyB8ICdkaXNhYmxlJykgPT4ge1xuICAgICAgaWYgKCFyZWNvbW1lbmRhdGlvbikgcmV0dXJuXG5cbiAgICAgIC8vIFJlY29yZCBzaG93LW9uY2UgaGVyZSwgbm90IGF0IHJlc29sdXRpb24tdGltZSDigJQgdGhlIGRpYWxvZyBtYXkgaGF2ZVxuICAgICAgLy8gYmVlbiBibG9ja2VkIGJ5IGEgaGlnaGVyLXByaW9yaXR5IGZvY3VzZWRJbnB1dERpYWxvZyBhbmQgbmV2ZXJcbiAgICAgIC8vIHJlbmRlcmVkLiBBdXRvLWRpc21pc3MgcmVhY2hlcyB0aGlzIHZpYSBvblJlc3BvbnNlKCdubycpLlxuICAgICAgbWFya0hpbnRQbHVnaW5TaG93bihyZWNvbW1lbmRhdGlvbi5wbHVnaW5JZClcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9wbHVnaW5faGludF9yZXNwb25zZScsIHtcbiAgICAgICAgX1BST1RPX3BsdWdpbl9uYW1lOlxuICAgICAgICAgIHJlY29tbWVuZGF0aW9uLnBsdWdpbk5hbWUgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX1BJSV9UQUdHRUQsXG4gICAgICAgIF9QUk9UT19tYXJrZXRwbGFjZV9uYW1lOlxuICAgICAgICAgIHJlY29tbWVuZGF0aW9uLm1hcmtldHBsYWNlTmFtZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfUElJX1RBR0dFRCxcbiAgICAgICAgcmVzcG9uc2U6XG4gICAgICAgICAgcmVzcG9uc2UgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgIH0pXG5cbiAgICAgIHN3aXRjaCAocmVzcG9uc2UpIHtcbiAgICAgICAgY2FzZSAneWVzJzoge1xuICAgICAgICAgIGNvbnN0IHsgcGx1Z2luSWQsIHBsdWdpbk5hbWUsIG1hcmtldHBsYWNlTmFtZSB9ID0gcmVjb21tZW5kYXRpb25cbiAgICAgICAgICB2b2lkIGluc3RhbGxQbHVnaW5BbmROb3RpZnkoXG4gICAgICAgICAgICBwbHVnaW5JZCxcbiAgICAgICAgICAgIHBsdWdpbk5hbWUsXG4gICAgICAgICAgICAnaGludC1wbHVnaW4nLFxuICAgICAgICAgICAgYWRkTm90aWZpY2F0aW9uLFxuICAgICAgICAgICAgYXN5bmMgcGx1Z2luRGF0YSA9PiB7XG4gICAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGluc3RhbGxQbHVnaW5Gcm9tTWFya2V0cGxhY2Uoe1xuICAgICAgICAgICAgICAgIHBsdWdpbklkLFxuICAgICAgICAgICAgICAgIGVudHJ5OiBwbHVnaW5EYXRhLmVudHJ5LFxuICAgICAgICAgICAgICAgIG1hcmtldHBsYWNlTmFtZSxcbiAgICAgICAgICAgICAgICBzY29wZTogJ3VzZXInLFxuICAgICAgICAgICAgICAgIHRyaWdnZXI6ICdoaW50JyxcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgaWYgKCFyZXN1bHQuc3VjY2Vzcykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihyZXN1bHQuZXJyb3IpXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgKVxuICAgICAgICAgIGJyZWFrXG4gICAgICAgIH1cbiAgICAgICAgY2FzZSAnZGlzYWJsZSc6XG4gICAgICAgICAgZGlzYWJsZUhpbnRSZWNvbW1lbmRhdGlvbnMoKVxuICAgICAgICAgIGJyZWFrXG4gICAgICAgIGNhc2UgJ25vJzpcbiAgICAgICAgICBicmVha1xuICAgICAgfVxuXG4gICAgICBjbGVhclJlY29tbWVuZGF0aW9uKClcbiAgICB9LFxuICAgIFtyZWNvbW1lbmRhdGlvbiwgYWRkTm90aWZpY2F0aW9uLCBjbGVhclJlY29tbWVuZGF0aW9uXSxcbiAgKVxuXG4gIHJldHVybiB7IHJlY29tbWVuZGF0aW9uLCBoYW5kbGVSZXNwb25zZSB9XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FB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 \ No newline at end of file diff --git a/hooks/useClipboardImageHint.ts b/hooks/useClipboardImageHint.ts new file mode 100644 index 0000000..48aa528 --- /dev/null +++ b/hooks/useClipboardImageHint.ts @@ -0,0 +1,77 @@ +import { useEffect, useRef } from 'react' +import { useNotifications } from '../context/notifications.js' +import { getShortcutDisplay } from '../keybindings/shortcutFormat.js' +import { hasImageInClipboard } from '../utils/imagePaste.js' + +const NOTIFICATION_KEY = 'clipboard-image-hint' +// Small debounce to batch rapid focus changes +const FOCUS_CHECK_DEBOUNCE_MS = 1000 +// Don't show the hint more than once per this interval +const HINT_COOLDOWN_MS = 30000 + +/** + * Hook that shows a notification when the terminal regains focus + * and the clipboard contains an image. + * + * @param isFocused - Whether the terminal is currently focused + * @param enabled - Whether image paste is enabled (onImagePaste is defined) + */ +export function useClipboardImageHint( + isFocused: boolean, + enabled: boolean, +): void { + const { addNotification } = useNotifications() + const lastFocusedRef = useRef(isFocused) + const lastHintTimeRef = useRef(0) + const checkTimeoutRef = useRef(null) + + useEffect(() => { + // Only trigger on focus regain (was unfocused, now focused) + const wasFocused = lastFocusedRef.current + lastFocusedRef.current = isFocused + + if (!enabled || !isFocused || wasFocused) { + return + } + + // Clear any pending check + if (checkTimeoutRef.current) { + clearTimeout(checkTimeoutRef.current) + } + + // Small debounce to batch rapid focus changes + checkTimeoutRef.current = setTimeout( + async (checkTimeoutRef, lastHintTimeRef, addNotification) => { + checkTimeoutRef.current = null + + // Check cooldown to avoid spamming the user + const now = Date.now() + if (now - lastHintTimeRef.current < HINT_COOLDOWN_MS) { + return + } + + // Check if clipboard has an image (async osascript call) + if (await hasImageInClipboard()) { + lastHintTimeRef.current = now + addNotification({ + key: NOTIFICATION_KEY, + text: `Image in clipboard · ${getShortcutDisplay('chat:imagePaste', 'Chat', 'ctrl+v')} to paste`, + priority: 'immediate', + timeoutMs: 8000, + }) + } + }, + FOCUS_CHECK_DEBOUNCE_MS, + checkTimeoutRef, + lastHintTimeRef, + addNotification, + ) + + return () => { + if (checkTimeoutRef.current) { + clearTimeout(checkTimeoutRef.current) + checkTimeoutRef.current = null + } + } + }, [isFocused, enabled, addNotification]) +} diff --git a/hooks/useCommandKeybindings.tsx b/hooks/useCommandKeybindings.tsx new file mode 100644 index 0000000..55810d6 --- /dev/null +++ b/hooks/useCommandKeybindings.tsx @@ -0,0 +1,108 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * Component that registers keybinding handlers for command bindings. + * + * Must be rendered inside KeybindingSetup to have access to the keybinding context. + * Reads "command:*" actions from the current keybinding configuration and registers + * handlers that invoke the corresponding slash command via onSubmit. + * + * Commands triggered via keybinding are treated as "immediate" - they execute right + * away and preserve the user's existing input text (the prompt is not cleared). + */ +import { useMemo } from 'react'; +import { useIsModalOverlayActive } from '../context/overlayContext.js'; +import { useOptionalKeybindingContext } from '../keybindings/KeybindingContext.js'; +import { useKeybindings } from '../keybindings/useKeybinding.js'; +import type { PromptInputHelpers } from '../utils/handlePromptSubmit.js'; +type Props = { + // onSubmit accepts additional parameters beyond what we pass here, + // so we use a rest parameter to allow any additional args + onSubmit: (input: string, helpers: PromptInputHelpers, ...rest: [speculationAccept?: undefined, options?: { + fromKeybinding?: boolean; + }]) => void; + /** Set to false to disable command keybindings (e.g., when a dialog is open) */ + isActive?: boolean; +}; +const NOOP_HELPERS: PromptInputHelpers = { + setCursorOffset: () => {}, + clearBuffer: () => {}, + resetHistory: () => {} +}; + +/** + * Registers keybinding handlers for all "command:*" actions found in the + * user's keybinding configuration. When triggered, each handler submits + * the corresponding slash command (e.g., "command:commit" submits "/commit"). + */ +export function CommandKeybindingHandlers(t0) { + const $ = _c(8); + const { + onSubmit, + isActive: t1 + } = t0; + const isActive = t1 === undefined ? true : t1; + const keybindingContext = useOptionalKeybindingContext(); + const isModalOverlayActive = useIsModalOverlayActive(); + let t2; + bb0: { + if (!keybindingContext) { + let t3; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t3 = new Set(); + $[0] = t3; + } else { + t3 = $[0]; + } + t2 = t3; + break bb0; + } + let actions; + if ($[1] !== keybindingContext.bindings) { + actions = new Set(); + for (const binding of keybindingContext.bindings) { + if (binding.action?.startsWith("command:")) { + actions.add(binding.action); + } + } + $[1] = keybindingContext.bindings; + $[2] = actions; + } else { + actions = $[2]; + } + t2 = actions; + } + const commandActions = t2; + let map; + if ($[3] !== commandActions || $[4] !== onSubmit) { + map = {}; + for (const action of commandActions) { + const commandName = action.slice(8); + map[action] = () => { + onSubmit(`/${commandName}`, NOOP_HELPERS, undefined, { + fromKeybinding: true + }); + }; + } + $[3] = commandActions; + $[4] = onSubmit; + $[5] = map; + } else { + map = $[5]; + } + const handlers = map; + const t3 = isActive && !isModalOverlayActive; + let t4; + if ($[6] !== t3) { + t4 = { + context: "Chat", + isActive: t3 + }; + $[6] = t3; + $[7] = t4; + } else { + t4 = $[7]; + } + useKeybindings(handlers, t4); + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1c2VNZW1vIiwidXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUiLCJ1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0IiwidXNlS2V5YmluZGluZ3MiLCJQcm9tcHRJbnB1dEhlbHBlcnMiLCJQcm9wcyIsIm9uU3VibWl0IiwiaW5wdXQiLCJoZWxwZXJzIiwicmVzdCIsInNwZWN1bGF0aW9uQWNjZXB0Iiwib3B0aW9ucyIsImZyb21LZXliaW5kaW5nIiwiaXNBY3RpdmUiLCJOT09QX0hFTFBFUlMiLCJzZXRDdXJzb3JPZmZzZXQiLCJjbGVhckJ1ZmZlciIsInJlc2V0SGlzdG9yeSIsIkNvbW1hbmRLZXliaW5kaW5nSGFuZGxlcnMiLCJ0MCIsIiQiLCJfYyIsInQxIiwidW5kZWZpbmVkIiwia2V5YmluZGluZ0NvbnRleHQiLCJpc01vZGFsT3ZlcmxheUFjdGl2ZSIsInQyIiwiYmIwIiwidDMiLCJTeW1ib2wiLCJmb3IiLCJTZXQiLCJhY3Rpb25zIiwiYmluZGluZ3MiLCJiaW5kaW5nIiwiYWN0aW9uIiwic3RhcnRzV2l0aCIsImFkZCIsImNvbW1hbmRBY3Rpb25zIiwibWFwIiwiY29tbWFuZE5hbWUiLCJzbGljZSIsImhhbmRsZXJzIiwidDQiLCJjb250ZXh0Il0sInNvdXJjZXMiOlsidXNlQ29tbWFuZEtleWJpbmRpbmdzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvbXBvbmVudCB0aGF0IHJlZ2lzdGVycyBrZXliaW5kaW5nIGhhbmRsZXJzIGZvciBjb21tYW5kIGJpbmRpbmdzLlxuICpcbiAqIE11c3QgYmUgcmVuZGVyZWQgaW5zaWRlIEtleWJpbmRpbmdTZXR1cCB0byBoYXZlIGFjY2VzcyB0byB0aGUga2V5YmluZGluZyBjb250ZXh0LlxuICogUmVhZHMgXCJjb21tYW5kOipcIiBhY3Rpb25zIGZyb20gdGhlIGN1cnJlbnQga2V5YmluZGluZyBjb25maWd1cmF0aW9uIGFuZCByZWdpc3RlcnNcbiAqIGhhbmRsZXJzIHRoYXQgaW52b2tlIHRoZSBjb3JyZXNwb25kaW5nIHNsYXNoIGNvbW1hbmQgdmlhIG9uU3VibWl0LlxuICpcbiAqIENvbW1hbmRzIHRyaWdnZXJlZCB2aWEga2V5YmluZGluZyBhcmUgdHJlYXRlZCBhcyBcImltbWVkaWF0ZVwiIC0gdGhleSBleGVjdXRlIHJpZ2h0XG4gKiBhd2F5IGFuZCBwcmVzZXJ2ZSB0aGUgdXNlcidzIGV4aXN0aW5nIGlucHV0IHRleHQgKHRoZSBwcm9tcHQgaXMgbm90IGNsZWFyZWQpLlxuICovXG5pbXBvcnQgeyB1c2VNZW1vIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VJc01vZGFsT3ZlcmxheUFjdGl2ZSB9IGZyb20gJy4uL2NvbnRleHQvb3ZlcmxheUNvbnRleHQuanMnXG5pbXBvcnQgeyB1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0IH0gZnJvbSAnLi4va2V5YmluZGluZ3MvS2V5YmluZGluZ0NvbnRleHQuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5ncyB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgdHlwZSB7IFByb21wdElucHV0SGVscGVycyB9IGZyb20gJy4uL3V0aWxzL2hhbmRsZVByb21wdFN1Ym1pdC5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgLy8gb25TdWJtaXQgYWNjZXB0cyBhZGRpdGlvbmFsIHBhcmFtZXRlcnMgYmV5b25kIHdoYXQgd2UgcGFzcyBoZXJlLFxuICAvLyBzbyB3ZSB1c2UgYSByZXN0IHBhcmFtZXRlciB0byBhbGxvdyBhbnkgYWRkaXRpb25hbCBhcmdzXG4gIG9uU3VibWl0OiAoXG4gICAgaW5wdXQ6IHN0cmluZyxcbiAgICBoZWxwZXJzOiBQcm9tcHRJbnB1dEhlbHBlcnMsXG4gICAgLi4ucmVzdDogW1xuICAgICAgc3BlY3VsYXRpb25BY2NlcHQ/OiB1bmRlZmluZWQsXG4gICAgICBvcHRpb25zPzogeyBmcm9tS2V5YmluZGluZz86IGJvb2xlYW4gfSxcbiAgICBdXG4gICkgPT4gdm9pZFxuICAvKiogU2V0IHRvIGZhbHNlIHRvIGRpc2FibGUgY29tbWFuZCBrZXliaW5kaW5ncyAoZS5nLiwgd2hlbiBhIGRpYWxvZyBpcyBvcGVuKSAqL1xuICBpc0FjdGl2ZT86IGJvb2xlYW5cbn1cblxuY29uc3QgTk9PUF9IRUxQRVJTOiBQcm9tcHRJbnB1dEhlbHBlcnMgPSB7XG4gIHNldEN1cnNvck9mZnNldDogKCkgPT4ge30sXG4gIGNsZWFyQnVmZmVyOiAoKSA9PiB7fSxcbiAgcmVzZXRIaXN0b3J5OiAoKSA9PiB7fSxcbn1cblxuLyoqXG4gKiBSZWdpc3RlcnMga2V5YmluZGluZyBoYW5kbGVycyBmb3IgYWxsIFwiY29tbWFuZDoqXCIgYWN0aW9ucyBmb3VuZCBpbiB0aGVcbiAqIHVzZXIncyBrZXliaW5kaW5nIGNvbmZpZ3VyYXRpb24uIFdoZW4gdHJpZ2dlcmVkLCBlYWNoIGhhbmRsZXIgc3VibWl0c1xuICogdGhlIGNvcnJlc3BvbmRpbmcgc2xhc2ggY29tbWFuZCAoZS5nLiwgXCJjb21tYW5kOmNvbW1pdFwiIHN1Ym1pdHMgXCIvY29tbWl0XCIpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gQ29tbWFuZEtleWJpbmRpbmdIYW5kbGVycyh7XG4gIG9uU3VibWl0LFxuICBpc0FjdGl2ZSA9IHRydWUsXG59OiBQcm9wcyk6IG51bGwge1xuICBjb25zdCBrZXliaW5kaW5nQ29udGV4dCA9IHVzZU9wdGlvbmFsS2V5YmluZGluZ0NvbnRleHQoKVxuICBjb25zdCBpc01vZGFsT3ZlcmxheUFjdGl2ZSA9IHVzZUlzTW9kYWxPdmVybGF5QWN0aXZlKClcblxuICAvLyBFeHRyYWN0IGNvbW1hbmQgYWN0aW9ucyBmcm9tIHBhcnNlZCBiaW5kaW5nc1xuICBjb25zdCBjb21tYW5kQWN0aW9ucyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmICgha2V5YmluZGluZ0NvbnRleHQpIHJldHVybiBuZXcgU2V0PHN0cmluZz4oKVxuICAgIGNvbnN0IGFjdGlvbnMgPSBuZXcgU2V0PHN0cmluZz4oKVxuICAgIGZvciAoY29uc3QgYmluZGluZyBvZiBrZXliaW5kaW5nQ29udGV4dC5iaW5kaW5ncykge1xuICAgICAgaWYgKGJpbmRpbmcuYWN0aW9uPy5zdGFydHNXaXRoKCdjb21tYW5kOicpKSB7XG4gICAgICAgIGFjdGlvbnMuYWRkKGJpbmRpbmcuYWN0aW9uKVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gYWN0aW9uc1xuICB9LCBba2V5YmluZGluZ0NvbnRleHRdKVxuXG4gIC8vIEJ1aWxkIGhhbmRsZXIgbWFwIGZvciBhbGwgY29tbWFuZCBhY3Rpb25zXG4gIGNvbnN0IGhhbmRsZXJzID0gdXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3QgbWFwOiBSZWNvcmQ8c3RyaW5nLCAoKSA9PiB2b2lkPiA9IHt9XG4gICAgZm9yIChjb25zdCBhY3Rpb24gb2YgY29tbWFuZEFjdGlvbnMpIHtcbiAgICAgIGNvbnN0IGNvbW1hbmROYW1lID0gYWN0aW9uLnNsaWNlKCdjb21tYW5kOicubGVuZ3RoKVxuICAgICAgbWFwW2FjdGlvbl0gPSAoKSA9PiB7XG4gICAgICAgIG9uU3VibWl0KGAvJHtjb21tYW5kTmFtZX1gLCBOT09QX0hFTFBFUlMsIHVuZGVmaW5lZCwge1xuICAgICAgICAgIGZyb21LZXliaW5kaW5nOiB0cnVlLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gbWFwXG4gIH0sIFtjb21tYW5kQWN0aW9ucywgb25TdWJtaXRdKVxuXG4gIHVzZUtleWJpbmRpbmdzKGhhbmRsZXJzLCB7XG4gICAgY29udGV4dDogJ0NoYXQnLFxuICAgIGlzQWN0aXZlOiBpc0FjdGl2ZSAmJiAhaXNNb2RhbE92ZXJsYXlBY3RpdmUsXG4gIH0pXG5cbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsU0FBU0EsT0FBTyxRQUFRLE9BQU87QUFDL0IsU0FBU0MsdUJBQXVCLFFBQVEsOEJBQThCO0FBQ3RFLFNBQVND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 \ No newline at end of file diff --git a/hooks/useCommandQueue.ts b/hooks/useCommandQueue.ts new file mode 100644 index 0000000..42ec532 --- /dev/null +++ b/hooks/useCommandQueue.ts @@ -0,0 +1,15 @@ +import { useSyncExternalStore } from 'react' +import type { QueuedCommand } from '../types/textInputTypes.js' +import { + getCommandQueueSnapshot, + subscribeToCommandQueue, +} from '../utils/messageQueueManager.js' + +/** + * React hook to subscribe to the unified command queue. + * Returns a frozen array that only changes reference on mutation. + * Components re-render only when the queue changes. + */ +export function useCommandQueue(): readonly QueuedCommand[] { + return useSyncExternalStore(subscribeToCommandQueue, getCommandQueueSnapshot) +} diff --git a/hooks/useCopyOnSelect.ts b/hooks/useCopyOnSelect.ts new file mode 100644 index 0000000..778ef5a --- /dev/null +++ b/hooks/useCopyOnSelect.ts @@ -0,0 +1,98 @@ +import { useEffect, useRef } from 'react' +import { useTheme } from '../components/design-system/ThemeProvider.js' +import type { useSelection } from '../ink/hooks/use-selection.js' +import { getGlobalConfig } from '../utils/config.js' +import { getTheme } from '../utils/theme.js' + +type Selection = ReturnType + +/** + * Auto-copy the selection to the clipboard when the user finishes dragging + * (mouse-up with a non-empty selection) or multi-clicks to select a word/line. + * Mirrors iTerm2's "Copy to pasteboard on selection" — the highlight is left + * intact so the user can see what was copied. Only fires in alt-screen mode + * (selection state is ink-instance-owned; outside alt-screen, the native + * terminal handles selection and this hook is a no-op via the ink stub). + * + * selection.subscribe fires on every mutation (start/update/finish/clear/ + * multiclick). Both char drags and multi-clicks set isDragging=true while + * pressed, so a selection appearing with isDragging=false is always a + * drag-finish. copiedRef guards against double-firing on spurious notifies. + * + * onCopied is optional — when omitted, copy is silent (clipboard is written + * but no toast/notification fires). FleetView uses this silent mode; the + * fullscreen REPL passes showCopiedToast for user feedback. + */ +export function useCopyOnSelect( + selection: Selection, + isActive: boolean, + onCopied?: (text: string) => void, +): void { + // Tracks whether the *previous* notification had a visible selection with + // isDragging=false (i.e., we already auto-copied it). Without this, the + // finish→clear transition would look like a fresh selection-gone-idle + // event and we'd toast twice for a single drag. + const copiedRef = useRef(false) + // onCopied is a fresh closure each render; read through a ref so the + // effect doesn't re-subscribe (which would reset copiedRef via unmount). + const onCopiedRef = useRef(onCopied) + onCopiedRef.current = onCopied + + useEffect(() => { + if (!isActive) return + + const unsubscribe = selection.subscribe(() => { + const sel = selection.getState() + const has = selection.hasSelection() + // Drag in progress — wait for finish. Reset copied flag so a new drag + // that ends on the same range still triggers a fresh copy. + if (sel?.isDragging) { + copiedRef.current = false + return + } + // No selection (cleared, or click-without-drag) — reset. + if (!has) { + copiedRef.current = false + return + } + // Selection settled (drag finished OR multi-click). Already copied + // this one — the only way to get here again without going through + // isDragging or !has is a spurious notify (shouldn't happen, but safe). + if (copiedRef.current) return + + // Default true: macOS users expect cmd+c to work. It can't — the + // terminal's Edit > Copy intercepts it before the pty sees it, and + // finds no native selection (mouse tracking disabled it). Auto-copy + // on mouse-up makes cmd+c a no-op that leaves the clipboard intact + // with the right content, so paste works as expected. + const enabled = getGlobalConfig().copyOnSelect ?? true + if (!enabled) return + + const text = selection.copySelectionNoClear() + // Whitespace-only (e.g., blank-line multi-click) — not worth a + // clipboard write or toast. Still set copiedRef so we don't retry. + if (!text || !text.trim()) { + copiedRef.current = true + return + } + copiedRef.current = true + onCopiedRef.current?.(text) + }) + return unsubscribe + }, [isActive, selection]) +} + +/** + * Pipe the theme's selectionBg color into the Ink StylePool so the + * selection overlay renders a solid blue bg instead of SGR-7 inverse. + * Ink is theme-agnostic (layering: colorize.ts "theme resolution happens + * at component layer, not here") — this is the bridge. Fires on mount + * (before any mouse input is possible) and again whenever /theme flips, + * so the selection color tracks the theme live. + */ +export function useSelectionBgColor(selection: Selection): void { + const [themeName] = useTheme() + useEffect(() => { + selection.setSelectionBgColor(getTheme(themeName).selectionBg) + }, [selection, themeName]) +} diff --git a/hooks/useDeferredHookMessages.ts b/hooks/useDeferredHookMessages.ts new file mode 100644 index 0000000..8989b55 --- /dev/null +++ b/hooks/useDeferredHookMessages.ts @@ -0,0 +1,46 @@ +import { useCallback, useEffect, useRef } from 'react' +import type { HookResultMessage, Message } from '../types/message.js' + +/** + * Manages deferred SessionStart hook messages so the REPL can render + * immediately instead of blocking on hook execution (~500ms). + * + * Hook messages are injected asynchronously when the promise resolves. + * Returns a callback that onSubmit should call before the first API + * request to ensure the model always sees hook context. + */ +export function useDeferredHookMessages( + pendingHookMessages: Promise | undefined, + setMessages: (action: React.SetStateAction) => void, +): () => Promise { + const pendingRef = useRef(pendingHookMessages ?? null) + const resolvedRef = useRef(!pendingHookMessages) + + useEffect(() => { + const promise = pendingRef.current + if (!promise) return + let cancelled = false + promise.then(msgs => { + if (cancelled) return + resolvedRef.current = true + pendingRef.current = null + if (msgs.length > 0) { + setMessages(prev => [...msgs, ...prev]) + } + }) + return () => { + cancelled = true + } + }, [setMessages]) + + return useCallback(async () => { + if (resolvedRef.current || !pendingRef.current) return + const msgs = await pendingRef.current + if (resolvedRef.current) return + resolvedRef.current = true + pendingRef.current = null + if (msgs.length > 0) { + setMessages(prev => [...msgs, ...prev]) + } + }, [setMessages]) +} diff --git a/hooks/useDiffData.ts b/hooks/useDiffData.ts new file mode 100644 index 0000000..176bcf0 --- /dev/null +++ b/hooks/useDiffData.ts @@ -0,0 +1,110 @@ +import type { StructuredPatchHunk } from 'diff' +import { useEffect, useMemo, useState } from 'react' +import { + fetchGitDiff, + fetchGitDiffHunks, + type GitDiffResult, + type GitDiffStats, +} from '../utils/gitDiff.js' + +const MAX_LINES_PER_FILE = 400 + +export type DiffFile = { + path: string + linesAdded: number + linesRemoved: number + isBinary: boolean + isLargeFile: boolean + isTruncated: boolean + isNewFile?: boolean + isUntracked?: boolean +} + +export type DiffData = { + stats: GitDiffStats | null + files: DiffFile[] + hunks: Map + loading: boolean +} + +/** + * Hook to fetch current git diff data on demand. + * Fetches both stats and hunks when component mounts. + */ +export function useDiffData(): DiffData { + const [diffResult, setDiffResult] = useState(null) + const [hunks, setHunks] = useState>( + new Map(), + ) + const [loading, setLoading] = useState(true) + + // Fetch diff data on mount + useEffect(() => { + let cancelled = false + + async function loadDiffData() { + try { + // Fetch both stats and hunks + const [statsResult, hunksResult] = await Promise.all([ + fetchGitDiff(), + fetchGitDiffHunks(), + ]) + + if (!cancelled) { + setDiffResult(statsResult) + setHunks(hunksResult) + setLoading(false) + } + } catch (_error) { + if (!cancelled) { + setDiffResult(null) + setHunks(new Map()) + setLoading(false) + } + } + } + + void loadDiffData() + + return () => { + cancelled = true + } + }, []) + + return useMemo(() => { + if (!diffResult) { + return { stats: null, files: [], hunks: new Map(), loading } + } + + const { stats, perFileStats } = diffResult + const files: DiffFile[] = [] + + // Iterate over perFileStats to get all files including large/skipped ones + for (const [path, fileStats] of perFileStats) { + const fileHunks = hunks.get(path) + const isUntracked = fileStats.isUntracked ?? false + + // Detect large file (in perFileStats but not in hunks, and not binary/untracked) + const isLargeFile = !fileStats.isBinary && !isUntracked && !fileHunks + + // Detect truncated file (total > limit means we truncated) + const totalLines = fileStats.added + fileStats.removed + const isTruncated = + !isLargeFile && !fileStats.isBinary && totalLines > MAX_LINES_PER_FILE + + files.push({ + path, + linesAdded: fileStats.added, + linesRemoved: fileStats.removed, + isBinary: fileStats.isBinary, + isLargeFile, + isTruncated, + isUntracked, + }) + } + + files.sort((a, b) => a.path.localeCompare(b.path)) + + return { stats, files, hunks, loading: false } + }, [diffResult, hunks, loading]) +} diff --git a/hooks/useDiffInIDE.ts b/hooks/useDiffInIDE.ts new file mode 100644 index 0000000..8fb0d10 --- /dev/null +++ b/hooks/useDiffInIDE.ts @@ -0,0 +1,379 @@ +import { randomUUID } from 'crypto' +import { basename } from 'path' +import { useEffect, useMemo, useRef, useState } from 'react' +import { logEvent } from 'src/services/analytics/index.js' +import { readFileSync } from 'src/utils/fileRead.js' +import { expandPath } from 'src/utils/path.js' +import type { PermissionOption } from '../components/permissions/FilePermissionDialog/permissionOptions.js' +import type { + MCPServerConnection, + McpSSEIDEServerConfig, + McpWebSocketIDEServerConfig, +} from '../services/mcp/types.js' +import type { ToolUseContext } from '../Tool.js' +import type { FileEdit } from '../tools/FileEditTool/types.js' +import { + getEditsForPatch, + getPatchForEdits, +} from '../tools/FileEditTool/utils.js' +import { getGlobalConfig } from '../utils/config.js' +import { getPatchFromContents } from '../utils/diff.js' +import { isENOENT } from '../utils/errors.js' +import { + callIdeRpc, + getConnectedIdeClient, + getConnectedIdeName, + hasAccessToIDEExtensionDiffFeature, +} from '../utils/ide.js' +import { WindowsToWSLConverter } from '../utils/idePathConversion.js' +import { logError } from '../utils/log.js' +import { getPlatform } from '../utils/platform.js' + +type Props = { + onChange( + option: PermissionOption, + input: { + file_path: string + edits: FileEdit[] + }, + ): void + toolUseContext: ToolUseContext + filePath: string + edits: FileEdit[] + editMode: 'single' | 'multiple' +} + +export function useDiffInIDE({ + onChange, + toolUseContext, + filePath, + edits, + editMode, +}: Props): { + closeTabInIDE: () => void + showingDiffInIDE: boolean + ideName: string + hasError: boolean +} { + const isUnmounted = useRef(false) + const [hasError, setHasError] = useState(false) + + const sha = useMemo(() => randomUUID().slice(0, 6), []) + const tabName = useMemo( + () => `✻ [Claude Code] ${basename(filePath)} (${sha}) ⧉`, + [filePath, sha], + ) + + const shouldShowDiffInIDE = + hasAccessToIDEExtensionDiffFeature(toolUseContext.options.mcpClients) && + getGlobalConfig().diffTool === 'auto' && + // Diffs should only be for file edits. + // File writes may come through here but are not supported for diffs. + !filePath.endsWith('.ipynb') + + const ideName = + getConnectedIdeName(toolUseContext.options.mcpClients) ?? 'IDE' + + async function showDiff(): Promise { + if (!shouldShowDiffInIDE) { + return + } + + try { + logEvent('tengu_ext_will_show_diff', {}) + + const { oldContent, newContent } = await showDiffInIDE( + filePath, + edits, + toolUseContext, + tabName, + ) + // Skip if component has been unmounted + if (isUnmounted.current) { + return + } + + logEvent('tengu_ext_diff_accepted', {}) + + const newEdits = computeEditsFromContents( + filePath, + oldContent, + newContent, + editMode, + ) + + if (newEdits.length === 0) { + // No changes -- edit was rejected (eg. reverted) + logEvent('tengu_ext_diff_rejected', {}) + // We close the tab here because 'no' no longer auto-closes + const ideClient = getConnectedIdeClient( + toolUseContext.options.mcpClients, + ) + if (ideClient) { + // Close the tab in the IDE + await closeTabInIDE(tabName, ideClient) + } + onChange( + { type: 'reject' }, + { + file_path: filePath, + edits: edits, + }, + ) + return + } + + // File was modified - edit was accepted + onChange( + { type: 'accept-once' }, + { + file_path: filePath, + edits: newEdits, + }, + ) + } catch (error) { + logError(error as Error) + setHasError(true) + } + } + + useEffect(() => { + void showDiff() + + // Set flag on unmount + return () => { + isUnmounted.current = true + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + return { + closeTabInIDE() { + const ideClient = getConnectedIdeClient(toolUseContext.options.mcpClients) + + if (!ideClient) { + return Promise.resolve() + } + + return closeTabInIDE(tabName, ideClient) + }, + showingDiffInIDE: shouldShowDiffInIDE && !hasError, + ideName: ideName, + hasError, + } +} + +/** + * Re-computes the edits from the old and new contents. This is necessary + * to apply any edits the user may have made to the new contents. + */ +export function computeEditsFromContents( + filePath: string, + oldContent: string, + newContent: string, + editMode: 'single' | 'multiple', +): FileEdit[] { + // Use unformatted patches, otherwise the edits will be formatted. + const singleHunk = editMode === 'single' + const patch = getPatchFromContents({ + filePath, + oldContent, + newContent, + singleHunk, + }) + + if (patch.length === 0) { + return [] + } + + // For single edit mode, verify we only got one hunk + if (singleHunk && patch.length > 1) { + logError( + new Error( + `Unexpected number of hunks: ${patch.length}. Expected 1 hunk.`, + ), + ) + } + + // Re-compute the edits to match the patch + return getEditsForPatch(patch) +} + +/** + * Done if: + * + * 1. Tab is closed in IDE + * 2. Tab is saved in IDE (we then close the tab) + * 3. User selected an option in IDE + * 4. User selected an option in terminal (or hit esc) + * + * Resolves with the new file content. + * + * TODO: Time out after 5 mins of inactivity? + * TODO: Update auto-approval UI when IDE exits + * TODO: Close the IDE tab when the approval prompt is unmounted + */ +async function showDiffInIDE( + file_path: string, + edits: FileEdit[], + toolUseContext: ToolUseContext, + tabName: string, +): Promise<{ oldContent: string; newContent: string }> { + let isCleanedUp = false + + const oldFilePath = expandPath(file_path) + let oldContent = '' + try { + oldContent = readFileSync(oldFilePath) + } catch (e: unknown) { + if (!isENOENT(e)) { + throw e + } + } + + async function cleanup() { + // Careful to avoid race conditions, since this + // function can be called from multiple places. + if (isCleanedUp) { + return + } + isCleanedUp = true + + // Don't fail if this fails + try { + await closeTabInIDE(tabName, ideClient) + } catch (e) { + logError(e as Error) + } + + process.off('beforeExit', cleanup) + toolUseContext.abortController.signal.removeEventListener('abort', cleanup) + } + + // Cleanup if the user hits esc to cancel the tool call - or on exit + toolUseContext.abortController.signal.addEventListener('abort', cleanup) + process.on('beforeExit', cleanup) + + // Open the diff in the IDE + const ideClient = getConnectedIdeClient(toolUseContext.options.mcpClients) + try { + const { updatedFile } = getPatchForEdits({ + filePath: oldFilePath, + fileContents: oldContent, + edits, + }) + + if (!ideClient || ideClient.type !== 'connected') { + throw new Error('IDE client not available') + } + let ideOldPath = oldFilePath + + // Only convert paths if we're in WSL and IDE is on Windows + const ideRunningInWindows = + (ideClient.config as McpSSEIDEServerConfig | McpWebSocketIDEServerConfig) + .ideRunningInWindows === true + if ( + getPlatform() === 'wsl' && + ideRunningInWindows && + process.env.WSL_DISTRO_NAME + ) { + const converter = new WindowsToWSLConverter(process.env.WSL_DISTRO_NAME) + ideOldPath = converter.toIDEPath(oldFilePath) + } + + const rpcResult = await callIdeRpc( + 'openDiff', + { + old_file_path: ideOldPath, + new_file_path: ideOldPath, + new_file_contents: updatedFile, + tab_name: tabName, + }, + ideClient, + ) + + // Convert the raw RPC result to a ToolCallResponse format + const data = Array.isArray(rpcResult) ? rpcResult : [rpcResult] + + // If the user saved the file then take the new contents and resolve with that. + if (isSaveMessage(data)) { + void cleanup() + return { + oldContent: oldContent, + newContent: data[1].text, + } + } else if (isClosedMessage(data)) { + void cleanup() + return { + oldContent: oldContent, + newContent: updatedFile, + } + } else if (isRejectedMessage(data)) { + void cleanup() + return { + oldContent: oldContent, + newContent: oldContent, + } + } + + // Indicates that the tool call completed with none of the expected + // results. Did the user close the IDE? + throw new Error('Not accepted') + } catch (error) { + logError(error as Error) + void cleanup() + throw error + } +} + +async function closeTabInIDE( + tabName: string, + ideClient?: MCPServerConnection | undefined, +): Promise { + try { + if (!ideClient || ideClient.type !== 'connected') { + throw new Error('IDE client not available') + } + + // Use direct RPC to close the tab + await callIdeRpc('close_tab', { tab_name: tabName }, ideClient) + } catch (error) { + logError(error as Error) + // Don't throw - this is a cleanup operation + } +} + +function isClosedMessage(data: unknown): data is { text: 'TAB_CLOSED' } { + return ( + Array.isArray(data) && + typeof data[0] === 'object' && + data[0] !== null && + 'type' in data[0] && + data[0].type === 'text' && + 'text' in data[0] && + data[0].text === 'TAB_CLOSED' + ) +} + +function isRejectedMessage(data: unknown): data is { text: 'DIFF_REJECTED' } { + return ( + Array.isArray(data) && + typeof data[0] === 'object' && + data[0] !== null && + 'type' in data[0] && + data[0].type === 'text' && + 'text' in data[0] && + data[0].text === 'DIFF_REJECTED' + ) +} + +function isSaveMessage( + data: unknown, +): data is [{ text: 'FILE_SAVED' }, { text: string }] { + return ( + Array.isArray(data) && + data[0]?.type === 'text' && + data[0].text === 'FILE_SAVED' && + typeof data[1].text === 'string' + ) +} diff --git a/hooks/useDirectConnect.ts b/hooks/useDirectConnect.ts new file mode 100644 index 0000000..2fd1952 --- /dev/null +++ b/hooks/useDirectConnect.ts @@ -0,0 +1,229 @@ +import { useCallback, useEffect, useMemo, useRef } from 'react' +import type { ToolUseConfirm } from '../components/permissions/PermissionRequest.js' +import type { RemotePermissionResponse } from '../remote/RemoteSessionManager.js' +import { + createSyntheticAssistantMessage, + createToolStub, +} from '../remote/remotePermissionBridge.js' +import { + convertSDKMessage, + isSessionEndMessage, +} from '../remote/sdkMessageAdapter.js' +import { + type DirectConnectConfig, + DirectConnectSessionManager, +} from '../server/directConnectManager.js' +import type { Tool } from '../Tool.js' +import { findToolByName } from '../Tool.js' +import type { Message as MessageType } from '../types/message.js' +import type { PermissionAskDecision } from '../types/permissions.js' +import { logForDebugging } from '../utils/debug.js' +import { gracefulShutdown } from '../utils/gracefulShutdown.js' +import type { RemoteMessageContent } from '../utils/teleport/api.js' + +type UseDirectConnectResult = { + isRemoteMode: boolean + sendMessage: (content: RemoteMessageContent) => Promise + cancelRequest: () => void + disconnect: () => void +} + +type UseDirectConnectProps = { + config: DirectConnectConfig | undefined + setMessages: React.Dispatch> + setIsLoading: (loading: boolean) => void + setToolUseConfirmQueue: React.Dispatch> + tools: Tool[] +} + +export function useDirectConnect({ + config, + setMessages, + setIsLoading, + setToolUseConfirmQueue, + tools, +}: UseDirectConnectProps): UseDirectConnectResult { + const isRemoteMode = !!config + + const managerRef = useRef(null) + const hasReceivedInitRef = useRef(false) + const isConnectedRef = useRef(false) + + // Keep a ref to tools so the WebSocket callback doesn't go stale + const toolsRef = useRef(tools) + useEffect(() => { + toolsRef.current = tools + }, [tools]) + + useEffect(() => { + if (!config) { + return + } + + hasReceivedInitRef.current = false + logForDebugging(`[useDirectConnect] Connecting to ${config.wsUrl}`) + + const manager = new DirectConnectSessionManager(config, { + onMessage: sdkMessage => { + if (isSessionEndMessage(sdkMessage)) { + setIsLoading(false) + } + + // Skip duplicate init messages (server sends one per turn) + if (sdkMessage.type === 'system' && sdkMessage.subtype === 'init') { + if (hasReceivedInitRef.current) { + return + } + hasReceivedInitRef.current = true + } + + const converted = convertSDKMessage(sdkMessage, { + convertToolResults: true, + }) + if (converted.type === 'message') { + setMessages(prev => [...prev, converted.message]) + } + }, + onPermissionRequest: (request, requestId) => { + logForDebugging( + `[useDirectConnect] Permission request for tool: ${request.tool_name}`, + ) + + const tool = + findToolByName(toolsRef.current, request.tool_name) ?? + createToolStub(request.tool_name) + + const syntheticMessage = createSyntheticAssistantMessage( + request, + requestId, + ) + + const permissionResult: PermissionAskDecision = { + behavior: 'ask', + message: + request.description ?? `${request.tool_name} requires permission`, + suggestions: request.permission_suggestions, + blockedPath: request.blocked_path, + } + + const toolUseConfirm: ToolUseConfirm = { + assistantMessage: syntheticMessage, + tool, + description: + request.description ?? `${request.tool_name} requires permission`, + input: request.input, + toolUseContext: {} as ToolUseConfirm['toolUseContext'], + toolUseID: request.tool_use_id, + permissionResult, + permissionPromptStartTimeMs: Date.now(), + onUserInteraction() { + // No-op for remote + }, + onAbort() { + const response: RemotePermissionResponse = { + behavior: 'deny', + message: 'User aborted', + } + manager.respondToPermissionRequest(requestId, response) + setToolUseConfirmQueue(queue => + queue.filter(item => item.toolUseID !== request.tool_use_id), + ) + }, + onAllow(updatedInput, _permissionUpdates, _feedback) { + const response: RemotePermissionResponse = { + behavior: 'allow', + updatedInput, + } + manager.respondToPermissionRequest(requestId, response) + setToolUseConfirmQueue(queue => + queue.filter(item => item.toolUseID !== request.tool_use_id), + ) + setIsLoading(true) + }, + onReject(feedback?: string) { + const response: RemotePermissionResponse = { + behavior: 'deny', + message: feedback ?? 'User denied permission', + } + manager.respondToPermissionRequest(requestId, response) + setToolUseConfirmQueue(queue => + queue.filter(item => item.toolUseID !== request.tool_use_id), + ) + }, + async recheckPermission() { + // No-op for remote + }, + } + + setToolUseConfirmQueue(queue => [...queue, toolUseConfirm]) + setIsLoading(false) + }, + onConnected: () => { + logForDebugging('[useDirectConnect] Connected') + isConnectedRef.current = true + }, + onDisconnected: () => { + logForDebugging('[useDirectConnect] Disconnected') + if (!isConnectedRef.current) { + // Never connected — connection failure (e.g. auth rejected) + process.stderr.write( + `\nFailed to connect to server at ${config.wsUrl}\n`, + ) + } else { + // Was connected then lost — server process exited or network dropped + process.stderr.write('\nServer disconnected.\n') + } + isConnectedRef.current = false + void gracefulShutdown(1) + setIsLoading(false) + }, + onError: error => { + logForDebugging(`[useDirectConnect] Error: ${error.message}`) + }, + }) + + managerRef.current = manager + manager.connect() + + return () => { + logForDebugging('[useDirectConnect] Cleanup - disconnecting') + manager.disconnect() + managerRef.current = null + } + }, [config, setMessages, setIsLoading, setToolUseConfirmQueue]) + + const sendMessage = useCallback( + async (content: RemoteMessageContent): Promise => { + const manager = managerRef.current + if (!manager) { + return false + } + + setIsLoading(true) + + return manager.sendMessage(content) + }, + [setIsLoading], + ) + + // Cancel the current request + const cancelRequest = useCallback(() => { + // Send interrupt signal to the server + managerRef.current?.sendInterrupt() + + setIsLoading(false) + }, [setIsLoading]) + + const disconnect = useCallback(() => { + managerRef.current?.disconnect() + managerRef.current = null + isConnectedRef.current = false + }, []) + + // Same stability concern as useRemoteSession — memoize so consumers + // that depend on the result object don't see a fresh reference per render. + return useMemo( + () => ({ isRemoteMode, sendMessage, cancelRequest, disconnect }), + [isRemoteMode, sendMessage, cancelRequest, disconnect], + ) +} diff --git a/hooks/useDoublePress.ts b/hooks/useDoublePress.ts new file mode 100644 index 0000000..7844fbd --- /dev/null +++ b/hooks/useDoublePress.ts @@ -0,0 +1,62 @@ +// Creates a function that calls one function on the first call and another +// function on the second call within a certain timeout + +import { useCallback, useEffect, useRef } from 'react' + +export const DOUBLE_PRESS_TIMEOUT_MS = 800 + +export function useDoublePress( + setPending: (pending: boolean) => void, + onDoublePress: () => void, + onFirstPress?: () => void, +): () => void { + const lastPressRef = useRef(0) + const timeoutRef = useRef(undefined) + + const clearTimeoutSafe = useCallback(() => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + timeoutRef.current = undefined + } + }, []) + + // Cleanup timeout on unmount + useEffect(() => { + return () => { + clearTimeoutSafe() + } + }, [clearTimeoutSafe]) + + return useCallback(() => { + const now = Date.now() + const timeSinceLastPress = now - lastPressRef.current + const isDoublePress = + timeSinceLastPress <= DOUBLE_PRESS_TIMEOUT_MS && + timeoutRef.current !== undefined + + if (isDoublePress) { + // Double press detected + clearTimeoutSafe() + setPending(false) + onDoublePress() + } else { + // First press + onFirstPress?.() + setPending(true) + + // Clear any existing timeout and set new one + clearTimeoutSafe() + timeoutRef.current = setTimeout( + (setPending, timeoutRef) => { + setPending(false) + timeoutRef.current = undefined + }, + DOUBLE_PRESS_TIMEOUT_MS, + setPending, + timeoutRef, + ) + } + + lastPressRef.current = now + }, [setPending, onDoublePress, onFirstPress, clearTimeoutSafe]) +} diff --git a/hooks/useDynamicConfig.ts b/hooks/useDynamicConfig.ts new file mode 100644 index 0000000..7edd5bb --- /dev/null +++ b/hooks/useDynamicConfig.ts @@ -0,0 +1,22 @@ +import React from 'react' +import { getDynamicConfig_BLOCKS_ON_INIT } from '../services/analytics/growthbook.js' + +/** + * React hook for dynamic config values. + * Returns the default value initially, then updates when the config is fetched. + */ +export function useDynamicConfig(configName: string, defaultValue: T): T { + const [configValue, setConfigValue] = React.useState(defaultValue) + + React.useEffect(() => { + if (process.env.NODE_ENV === 'test') { + // Prevents a test hang when using this hook in tests + return + } + void getDynamicConfig_BLOCKS_ON_INIT(configName, defaultValue).then( + setConfigValue, + ) + }, [configName, defaultValue]) + + return configValue +} diff --git a/hooks/useElapsedTime.ts b/hooks/useElapsedTime.ts new file mode 100644 index 0000000..71c7619 --- /dev/null +++ b/hooks/useElapsedTime.ts @@ -0,0 +1,37 @@ +import { useCallback, useSyncExternalStore } from 'react' +import { formatDuration } from '../utils/format.js' + +/** + * Hook that returns formatted elapsed time since startTime. + * Uses useSyncExternalStore with interval-based updates for efficiency. + * + * @param startTime - Unix timestamp in ms + * @param isRunning - Whether to actively update the timer + * @param ms - How often should we trigger updates? + * @param pausedMs - Total paused duration to subtract + * @param endTime - If set, freezes the duration at this timestamp (for + * terminal tasks). Without this, viewing a 2-min task 30 min after + * completion would show "32m". + * @returns Formatted duration string (e.g., "1m 23s") + */ +export function useElapsedTime( + startTime: number, + isRunning: boolean, + ms: number = 1000, + pausedMs: number = 0, + endTime?: number, +): string { + const get = () => + formatDuration(Math.max(0, (endTime ?? Date.now()) - startTime - pausedMs)) + + const subscribe = useCallback( + (notify: () => void) => { + if (!isRunning) return () => {} + const interval = setInterval(notify, ms) + return () => clearInterval(interval) + }, + [isRunning, ms], + ) + + return useSyncExternalStore(subscribe, get, get) +} diff --git a/hooks/useExitOnCtrlCD.ts b/hooks/useExitOnCtrlCD.ts new file mode 100644 index 0000000..23ba7ad --- /dev/null +++ b/hooks/useExitOnCtrlCD.ts @@ -0,0 +1,95 @@ +import { useCallback, useMemo, useState } from 'react' +import useApp from '../ink/hooks/use-app.js' +import type { KeybindingContextName } from '../keybindings/types.js' +import { useDoublePress } from './useDoublePress.js' + +export type ExitState = { + pending: boolean + keyName: 'Ctrl-C' | 'Ctrl-D' | null +} + +type KeybindingOptions = { + context?: KeybindingContextName + isActive?: boolean +} + +type UseKeybindingsHook = ( + handlers: Record void>, + options?: KeybindingOptions, +) => void + +/** + * Handle ctrl+c and ctrl+d for exiting the application. + * + * Uses a time-based double-press mechanism: + * - First press: Shows "Press X again to exit" message + * - Second press within timeout: Exits the application + * + * Note: We use time-based double-press rather than the chord system because + * we want the first ctrl+c to also trigger interrupt (handled elsewhere). + * The chord system would prevent the first press from firing any action. + * + * These keys are hardcoded and cannot be rebound via keybindings.json. + * + * @param useKeybindingsHook - The useKeybindings hook to use for registering handlers + * (dependency injection to avoid import cycles) + * @param onInterrupt - Optional callback for features to handle interrupt (ctrl+c). + * Return true if handled, false to fall through to double-press exit. + * @param onExit - Optional custom exit handler + * @param isActive - Whether the keybinding is active (default true). Set false + * while an embedded TextInput is focused — TextInput's own + * ctrl+c/d handlers will manage cancel/exit, and Dialog's + * handler would otherwise double-fire (child useInput runs + * before parent useKeybindings, so both see every keypress). + */ +export function useExitOnCtrlCD( + useKeybindingsHook: UseKeybindingsHook, + onInterrupt?: () => boolean, + onExit?: () => void, + isActive = true, +): ExitState { + const { exit } = useApp() + const [exitState, setExitState] = useState({ + pending: false, + keyName: null, + }) + + const exitFn = useMemo(() => onExit ?? exit, [onExit, exit]) + + // Double-press handler for ctrl+c + const handleCtrlCDoublePress = useDoublePress( + pending => setExitState({ pending, keyName: 'Ctrl-C' }), + exitFn, + ) + + // Double-press handler for ctrl+d + const handleCtrlDDoublePress = useDoublePress( + pending => setExitState({ pending, keyName: 'Ctrl-D' }), + exitFn, + ) + + // Handler for app:interrupt (ctrl+c by default) + // Let features handle interrupt first via callback + const handleInterrupt = useCallback(() => { + if (onInterrupt?.()) return // Feature handled it + handleCtrlCDoublePress() + }, [handleCtrlCDoublePress, onInterrupt]) + + // Handler for app:exit (ctrl+d by default) + // This also uses double-press to confirm exit + const handleExit = useCallback(() => { + handleCtrlDDoublePress() + }, [handleCtrlDDoublePress]) + + const handlers = useMemo( + () => ({ + 'app:interrupt': handleInterrupt, + 'app:exit': handleExit, + }), + [handleInterrupt, handleExit], + ) + + useKeybindingsHook(handlers, { context: 'Global', isActive }) + + return exitState +} diff --git a/hooks/useExitOnCtrlCDWithKeybindings.ts b/hooks/useExitOnCtrlCDWithKeybindings.ts new file mode 100644 index 0000000..7f30f55 --- /dev/null +++ b/hooks/useExitOnCtrlCDWithKeybindings.ts @@ -0,0 +1,24 @@ +import { useKeybindings } from '../keybindings/useKeybinding.js' +import { type ExitState, useExitOnCtrlCD } from './useExitOnCtrlCD.js' + +export type { ExitState } + +/** + * Convenience hook that wires up useExitOnCtrlCD with useKeybindings. + * + * This is the standard way to use useExitOnCtrlCD in components. + * The separation exists to avoid import cycles - useExitOnCtrlCD.ts + * doesn't import from the keybindings module directly. + * + * @param onExit - Optional custom exit handler + * @param onInterrupt - Optional callback for features to handle interrupt (ctrl+c). + * Return true if handled, false to fall through to double-press exit. + * @param isActive - Whether the keybinding is active (default true). + */ +export function useExitOnCtrlCDWithKeybindings( + onExit?: () => void, + onInterrupt?: () => boolean, + isActive?: boolean, +): ExitState { + return useExitOnCtrlCD(useKeybindings, onInterrupt, onExit, isActive) +} diff --git a/hooks/useFileHistorySnapshotInit.ts b/hooks/useFileHistorySnapshotInit.ts new file mode 100644 index 0000000..faf46b7 --- /dev/null +++ b/hooks/useFileHistorySnapshotInit.ts @@ -0,0 +1,25 @@ +import { useEffect, useRef } from 'react' +import { + type FileHistorySnapshot, + type FileHistoryState, + fileHistoryEnabled, + fileHistoryRestoreStateFromLog, +} from '../utils/fileHistory.js' + +export function useFileHistorySnapshotInit( + initialFileHistorySnapshots: FileHistorySnapshot[] | undefined, + fileHistoryState: FileHistoryState, + onUpdateState: (newState: FileHistoryState) => void, +): void { + const initialized = useRef(false) + + useEffect(() => { + if (!fileHistoryEnabled() || initialized.current) { + return + } + initialized.current = true + if (initialFileHistorySnapshots) { + fileHistoryRestoreStateFromLog(initialFileHistorySnapshots, onUpdateState) + } + }, [fileHistoryState, initialFileHistorySnapshots, onUpdateState]) +} diff --git a/hooks/useGlobalKeybindings.tsx b/hooks/useGlobalKeybindings.tsx new file mode 100644 index 0000000..5f1c39b --- /dev/null +++ b/hooks/useGlobalKeybindings.tsx @@ -0,0 +1,249 @@ +/** + * Component that registers global keybinding handlers. + * + * Must be rendered inside KeybindingSetup to have access to the keybinding context. + * This component renders nothing - it just registers the keybinding handlers. + */ +import { feature } from 'bun:bundle'; +import { useCallback } from 'react'; +import instances from '../ink/instances.js'; +import { useKeybinding } from '../keybindings/useKeybinding.js'; +import type { Screen } from '../screens/REPL.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../services/analytics/index.js'; +import { useAppState, useSetAppState } from '../state/AppState.js'; +import { count } from '../utils/array.js'; +import { getTerminalPanel } from '../utils/terminalPanel.js'; +type Props = { + screen: Screen; + setScreen: React.Dispatch>; + showAllInTranscript: boolean; + setShowAllInTranscript: React.Dispatch>; + messageCount: number; + onEnterTranscript?: () => void; + onExitTranscript?: () => void; + virtualScrollActive?: boolean; + searchBarOpen?: boolean; +}; + +/** + * Registers global keybinding handlers for: + * - ctrl+t: Toggle todo list + * - ctrl+o: Toggle transcript mode + * - ctrl+e: Toggle showing all messages in transcript + * - ctrl+c/escape: Exit transcript mode + */ +export function GlobalKeybindingHandlers({ + screen, + setScreen, + showAllInTranscript, + setShowAllInTranscript, + messageCount, + onEnterTranscript, + onExitTranscript, + virtualScrollActive, + searchBarOpen = false +}: Props): null { + const expandedView = useAppState(s => s.expandedView); + const setAppState = useSetAppState(); + + // Toggle todo list (ctrl+t) - cycles through views + const handleToggleTodos = useCallback(() => { + logEvent('tengu_toggle_todos', { + is_expanded: expandedView === 'tasks' + }); + setAppState(prev => { + const { + getAllInProcessTeammateTasks + } = + // eslint-disable-next-line @typescript-eslint/no-require-imports + require('../tasks/InProcessTeammateTask/InProcessTeammateTask.js') as typeof import('../tasks/InProcessTeammateTask/InProcessTeammateTask.js'); + const hasTeammates = count(getAllInProcessTeammateTasks(prev.tasks), t => t.status === 'running') > 0; + if (hasTeammates) { + // Both exist: none → tasks → teammates → none + switch (prev.expandedView) { + case 'none': + return { + ...prev, + expandedView: 'tasks' as const + }; + case 'tasks': + return { + ...prev, + expandedView: 'teammates' as const + }; + case 'teammates': + return { + ...prev, + expandedView: 'none' as const + }; + } + } + // Only tasks: none ↔ tasks + return { + ...prev, + expandedView: prev.expandedView === 'tasks' ? 'none' as const : 'tasks' as const + }; + }); + }, [expandedView, setAppState]); + + // Toggle transcript mode (ctrl+o). Two-way prompt ↔ transcript. + // Brief view has its own dedicated toggle on ctrl+shift+b. + const isBriefOnly = feature('KAIROS') || feature('KAIROS_BRIEF') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s_0 => s_0.isBriefOnly) : false; + const handleToggleTranscript = useCallback(() => { + if (feature('KAIROS') || feature('KAIROS_BRIEF')) { + // Escape hatch: GB kill-switch while defaultView=chat was persisted + // can leave isBriefOnly stuck on, showing a blank filterForBriefTool + // view. Users will reach for ctrl+o — clear the stuck state first. + // Only needed in the prompt screen — transcript mode already ignores + // isBriefOnly (Messages.tsx filter is gated on !isTranscriptMode). + /* eslint-disable @typescript-eslint/no-require-imports */ + const { + isBriefEnabled + } = require('../tools/BriefTool/BriefTool.js') as typeof import('../tools/BriefTool/BriefTool.js'); + /* eslint-enable @typescript-eslint/no-require-imports */ + if (!isBriefEnabled() && isBriefOnly && screen !== 'transcript') { + setAppState(prev_0 => { + if (!prev_0.isBriefOnly) return prev_0; + return { + ...prev_0, + isBriefOnly: false + }; + }); + return; + } + } + const isEnteringTranscript = screen !== 'transcript'; + logEvent('tengu_toggle_transcript', { + is_entering: isEnteringTranscript, + show_all: showAllInTranscript, + message_count: messageCount + }); + setScreen(s_1 => s_1 === 'transcript' ? 'prompt' : 'transcript'); + setShowAllInTranscript(false); + if (isEnteringTranscript && onEnterTranscript) { + onEnterTranscript(); + } + if (!isEnteringTranscript && onExitTranscript) { + onExitTranscript(); + } + }, [screen, setScreen, isBriefOnly, showAllInTranscript, setShowAllInTranscript, messageCount, setAppState, onEnterTranscript, onExitTranscript]); + + // Toggle showing all messages in transcript mode (ctrl+e) + const handleToggleShowAll = useCallback(() => { + logEvent('tengu_transcript_toggle_show_all', { + is_expanding: !showAllInTranscript, + message_count: messageCount + }); + setShowAllInTranscript(prev_1 => !prev_1); + }, [showAllInTranscript, setShowAllInTranscript, messageCount]); + + // Exit transcript mode (ctrl+c or escape) + const handleExitTranscript = useCallback(() => { + logEvent('tengu_transcript_exit', { + show_all: showAllInTranscript, + message_count: messageCount + }); + setScreen('prompt'); + setShowAllInTranscript(false); + if (onExitTranscript) { + onExitTranscript(); + } + }, [setScreen, showAllInTranscript, setShowAllInTranscript, messageCount, onExitTranscript]); + + // Toggle brief-only view (ctrl+shift+b). Pure display filter toggle — + // does not touch opt-in state. Asymmetric gate (mirrors /brief): OFF + // transition always allowed so the same key that got you in gets you + // out even if the GB kill-switch fires mid-session. + const handleToggleBrief = useCallback(() => { + if (feature('KAIROS') || feature('KAIROS_BRIEF')) { + /* eslint-disable @typescript-eslint/no-require-imports */ + const { + isBriefEnabled: isBriefEnabled_0 + } = require('../tools/BriefTool/BriefTool.js') as typeof import('../tools/BriefTool/BriefTool.js'); + /* eslint-enable @typescript-eslint/no-require-imports */ + if (!isBriefEnabled_0() && !isBriefOnly) return; + const next = !isBriefOnly; + logEvent('tengu_brief_mode_toggled', { + enabled: next, + gated: false, + source: 'keybinding' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + setAppState(prev_2 => { + if (prev_2.isBriefOnly === next) return prev_2; + return { + ...prev_2, + isBriefOnly: next + }; + }); + } + }, [isBriefOnly, setAppState]); + + // Register keybinding handlers + useKeybinding('app:toggleTodos', handleToggleTodos, { + context: 'Global' + }); + useKeybinding('app:toggleTranscript', handleToggleTranscript, { + context: 'Global' + }); + if (feature('KAIROS') || feature('KAIROS_BRIEF')) { + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useKeybinding('app:toggleBrief', handleToggleBrief, { + context: 'Global' + }); + } + + // Register teammate keybinding + useKeybinding('app:toggleTeammatePreview', () => { + setAppState(prev_3 => ({ + ...prev_3, + showTeammateMessagePreview: !prev_3.showTeammateMessagePreview + })); + }, { + context: 'Global' + }); + + // Toggle built-in terminal panel (meta+j). + // toggle() blocks in spawnSync until the user detaches from tmux. + const handleToggleTerminal = useCallback(() => { + if (feature('TERMINAL_PANEL')) { + if (!getFeatureValue_CACHED_MAY_BE_STALE('tengu_terminal_panel', false)) { + return; + } + getTerminalPanel().toggle(); + } + }, []); + useKeybinding('app:toggleTerminal', handleToggleTerminal, { + context: 'Global' + }); + + // Clear screen and force full redraw (ctrl+l). Recovery path when the + // terminal was cleared externally (macOS Cmd+K) and Ink's diff engine + // thinks unchanged cells don't need repainting. + const handleRedraw = useCallback(() => { + instances.get(process.stdout)?.forceRedraw(); + }, []); + useKeybinding('app:redraw', handleRedraw, { + context: 'Global' + }); + + // Transcript-specific bindings (only active when in transcript mode) + const isInTranscript = screen === 'transcript'; + useKeybinding('transcript:toggleShowAll', handleToggleShowAll, { + context: 'Transcript', + isActive: isInTranscript && !virtualScrollActive + }); + useKeybinding('transcript:exit', handleExitTranscript, { + context: 'Transcript', + // Bar-open is a mode (owns keystrokes). Navigating (highlights + // visible, n/N active, bar closed) is NOT — Esc exits transcript + // directly, same as less q. useSearchInput doesn't stopPropagation, + // so without this gate its onCancel AND this handler would both + // fire on one Esc (child registers first, fires first, bubbles). + isActive: isInTranscript && !searchBarOpen + }); + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwidXNlQ2FsbGJhY2siLCJpbnN0YW5jZXMiLCJ1c2VLZXliaW5kaW5nIiwiU2NyZWVuIiwiZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJ1c2VBcHBTdGF0ZSIsInVzZVNldEFwcFN0YXRlIiwiY291bnQiLCJnZXRUZXJtaW5hbFBhbmVsIiwiUHJvcHMiLCJzY3JlZW4iLCJzZXRTY3JlZW4iLCJSZWFjdCIsIkRpc3BhdGNoIiwiU2V0U3RhdGVBY3Rpb24iLCJzaG93QWxsSW5UcmFuc2NyaXB0Iiwic2V0U2hvd0FsbEluVHJhbnNjcmlwdCIsIm1lc3NhZ2VDb3VudCIsIm9uRW50ZXJUcmFuc2NyaXB0Iiwib25FeGl0VHJhbnNjcmlwdCIsInZpcnR1YWxTY3JvbGxBY3RpdmUiLCJzZWFyY2hCYXJPcGVuIiwiR2xvYmFsS2V5YmluZGluZ0hhbmRsZXJzIiwiZXhwYW5kZWRWaWV3IiwicyIsInNldEFwcFN0YXRlIiwiaGFuZGxlVG9nZ2xlVG9kb3MiLCJpc19leHBhbmRlZCIsInByZXYiLCJnZXRBbGxJblByb2Nlc3NUZWFtbWF0ZVRhc2tzIiwicmVxdWlyZSIsImhhc1RlYW1tYXRlcyIsInRhc2tzIiwidCIsInN0YXR1cyIsImNvbnN0IiwiaXNCcmllZk9ubHkiLCJoYW5kbGVUb2dnbGVUcmFuc2NyaXB0IiwiaXNCcmllZkVuYWJsZWQiLCJpc0VudGVyaW5nVHJhbnNjcmlwdCIsImlzX2VudGVyaW5nIiwic2hvd19hbGwiLCJtZXNzYWdlX2NvdW50IiwiaGFuZGxlVG9nZ2xlU2hvd0FsbCIsImlzX2V4cGFuZGluZyIsImhhbmRsZUV4aXRUcmFuc2NyaXB0IiwiaGFuZGxlVG9nZ2xlQnJpZWYiLCJuZXh0IiwiZW5hYmxlZCIsImdhdGVkIiwic291cmNlIiwiY29udGV4dCIsInNob3dUZWFtbWF0ZU1lc3NhZ2VQcmV2aWV3IiwiaGFuZGxlVG9nZ2xlVGVybWluYWwiLCJ0b2dnbGUiLCJoYW5kbGVSZWRyYXciLCJnZXQiLCJwcm9jZXNzIiwic3Rkb3V0IiwiZm9yY2VSZWRyYXciLCJpc0luVHJhbnNjcmlwdCIsImlzQWN0aXZlIl0sInNvdXJjZXMiOlsidXNlR2xvYmFsS2V5YmluZGluZ3MudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQ29tcG9uZW50IHRoYXQgcmVnaXN0ZXJzIGdsb2JhbCBrZXliaW5kaW5nIGhhbmRsZXJzLlxuICpcbiAqIE11c3QgYmUgcmVuZGVyZWQgaW5zaWRlIEtleWJpbmRpbmdTZXR1cCB0byBoYXZlIGFjY2VzcyB0byB0aGUga2V5YmluZGluZyBjb250ZXh0LlxuICogVGhpcyBjb21wb25lbnQgcmVuZGVycyBub3RoaW5nIC0gaXQganVzdCByZWdpc3RlcnMgdGhlIGtleWJpbmRpbmcgaGFuZGxlcnMuXG4gKi9cbmltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2sgfSBmcm9tICdyZWFjdCdcbmltcG9ydCBpbnN0YW5jZXMgZnJvbSAnLi4vaW5rL2luc3RhbmNlcy5qcydcbmltcG9ydCB7IHVzZUtleWJpbmRpbmcgfSBmcm9tICcuLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHR5cGUgeyBTY3JlZW4gfSBmcm9tICcuLi9zY3JlZW5zL1JFUEwuanMnXG5pbXBvcnQgeyBnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSB9IGZyb20gJy4uL3NlcnZpY2VzL2FuYWx5dGljcy9ncm93dGhib29rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnLi4vc2VydmljZXMvYW5hbHl0aWNzL2luZGV4LmpzJ1xuaW1wb3J0IHsgdXNlQXBwU3RhdGUsIHVzZVNldEFwcFN0YXRlIH0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgeyBjb3VudCB9IGZyb20gJy4uL3V0aWxzL2FycmF5LmpzJ1xuaW1wb3J0IHsgZ2V0VGVybWluYWxQYW5lbCB9IGZyb20gJy4uL3V0aWxzL3Rlcm1pbmFsUGFuZWwuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHNjcmVlbjogU2NyZWVuXG4gIHNldFNjcmVlbjogUmVhY3QuRGlzcGF0Y2g8UmVhY3QuU2V0U3RhdGVBY3Rpb248U2NyZWVuPj5cbiAgc2hvd0FsbEluVHJhbnNjcmlwdDogYm9vbGVhblxuICBzZXRTaG93QWxsSW5UcmFuc2NyaXB0OiBSZWFjdC5EaXNwYXRjaDxSZWFjdC5TZXRTdGF0ZUFjdGlvbjxib29sZWFuPj5cbiAgbWVzc2FnZUNvdW50OiBudW1iZXJcbiAgb25FbnRlclRyYW5zY3JpcHQ/OiAoKSA9PiB2b2lkXG4gIG9uRXhpdFRyYW5zY3JpcHQ/OiAoKSA9PiB2b2lkXG4gIHZpcnR1YWxTY3JvbGxBY3RpdmU/OiBib29sZWFuXG4gIHNlYXJjaEJhck9wZW4/OiBib29sZWFuXG59XG5cbi8qKlxuICogUmVnaXN0ZXJzIGdsb2JhbCBrZXliaW5kaW5nIGhhbmRsZXJzIGZvcjpcbiAqIC0gY3RybCt0OiBUb2dnbGUgdG9kbyBsaXN0XG4gKiAtIGN0cmwrbzogVG9nZ2xlIHRyYW5zY3JpcHQgbW9kZVxuICogLSBjdHJsK2U6IFRvZ2dsZSBzaG93aW5nIGFsbCBtZXNzYWdlcyBpbiB0cmFuc2NyaXB0XG4gKiAtIGN0cmwrYy9lc2NhcGU6IEV4aXQgdHJhbnNjcmlwdCBtb2RlXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBHbG9iYWxLZXliaW5kaW5nSGFuZGxlcnMoe1xuICBzY3JlZW4sXG4gIHNldFNjcmVlbixcbiAgc2hvd0FsbEluVHJhbnNjcmlwdCxcbiAgc2V0U2hvd0FsbEluVHJhbnNjcmlwdCxcbiAgbWVzc2FnZUNvdW50LFxuICBvbkVudGVyVHJhbnNjcmlwdCxcbiAgb25FeGl0VHJhbnNjcmlwdCxcbiAgdmlydHVhbFNjcm9sbEFjdGl2ZSxcbiAgc2VhcmNoQmFyT3BlbiA9IGZhbHNlLFxufTogUHJvcHMpOiBudWxsIHtcbiAgY29uc3QgZXhwYW5kZWRWaWV3ID0gdXNlQXBwU3RhdGUocyA9PiBzLmV4cGFuZGVkVmlldylcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG5cbiAgLy8gVG9nZ2xlIHRvZG8gbGlzdCAoY3RybCt0KSAtIGN5Y2xlcyB0aHJvdWdoIHZpZXdzXG4gIGNvbnN0IGhhbmRsZVRvZ2dsZVRvZG9zID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV90b2dnbGVfdG9kb3MnLCB7XG4gICAgICBpc19leHBhbmRlZDogZXhwYW5kZWRWaWV3ID09PSAndGFza3MnLFxuICAgIH0pXG4gICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICBjb25zdCB7IGdldEFsbEluUHJvY2Vzc1RlYW1tYXRlVGFza3MgfSA9XG4gICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzXG4gICAgICAgIHJlcXVpcmUoJy4uL3Rhc2tzL0luUHJvY2Vzc1RlYW1tYXRlVGFzay9JblByb2Nlc3NUZWFtbWF0ZVRhc2suanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuLi90YXNrcy9JblByb2Nlc3NUZWFtbWF0ZVRhc2svSW5Qcm9jZXNzVGVhbW1hdGVUYXNrLmpzJylcbiAgICAgIGNvbnN0IGhhc1RlYW1tYXRlcyA9XG4gICAgICAgIGNvdW50KFxuICAgICAgICAgIGdldEFsbEluUHJvY2Vzc1RlYW1tYXRlVGFza3MocHJldi50YXNrcyksXG4gICAgICAgICAgdCA9PiB0LnN0YXR1cyA9PT0gJ3J1bm5pbmcnLFxuICAgICAgICApID4gMFxuXG4gICAgICBpZiAoaGFzVGVhbW1hdGVzKSB7XG4gICAgICAgIC8vIEJvdGggZXhpc3Q6IG5vbmUg4oaSIHRhc2tzIOKGkiB0ZWFtbWF0ZXMg4oaSIG5vbmVcbiAgICAgICAgc3dpdGNoIChwcmV2LmV4cGFuZGVkVmlldykge1xuICAgICAgICAgIGNhc2UgJ25vbmUnOlxuICAgICAgICAgICAgcmV0dXJuIHsgLi4ucHJldiwgZXhwYW5kZWRWaWV3OiAndGFza3MnIGFzIGNvbnN0IH1cbiAgICAgICAgICBjYXNlICd0YXNrcyc6XG4gICAgICAgICAgICByZXR1cm4geyAuLi5wcmV2LCBleHBhbmRlZFZpZXc6ICd0ZWFtbWF0ZXMnIGFzIGNvbnN0IH1cbiAgICAgICAgICBjYXNlICd0ZWFtbWF0ZXMnOlxuICAgICAgICAgICAgcmV0dXJuIHsgLi4ucHJldiwgZXhwYW5kZWRWaWV3OiAnbm9uZScgYXMgY29uc3QgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICAvLyBPbmx5IHRhc2tzOiBub25lIOKGlCB0YXNrc1xuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4ucHJldixcbiAgICAgICAgZXhwYW5kZWRWaWV3OlxuICAgICAgICAgIHByZXYuZXhwYW5kZWRWaWV3ID09PSAndGFza3MnXG4gICAgICAgICAgICA/ICgnbm9uZScgYXMgY29uc3QpXG4gICAgICAgICAgICA6ICgndGFza3MnIGFzIGNvbnN0KSxcbiAgICAgIH1cbiAgICB9KVxuICB9LCBbZXhwYW5kZWRWaWV3LCBzZXRBcHBTdGF0ZV0pXG5cbiAgLy8gVG9nZ2xlIHRyYW5zY3JpcHQgbW9kZSAoY3RybCtvKS4gVHdvLXdheSBwcm9tcHQg4oaUIHRyYW5zY3JpcHQuXG4gIC8vIEJyaWVmIHZpZXcgaGFzIGl0cyBvd24gZGVkaWNhdGVkIHRvZ2dsZSBvbiBjdHJsK3NoaWZ0K2IuXG4gIGNvbnN0IGlzQnJpZWZPbmx5ID1cbiAgICBmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKVxuICAgICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICAgIHVzZUFwcFN0YXRlKHMgPT4gcy5pc0JyaWVmT25seSlcbiAgICAgIDogZmFsc2VcbiAgY29uc3QgaGFuZGxlVG9nZ2xlVHJhbnNjcmlwdCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoZmVhdHVyZSgnS0FJUk9TJykgfHwgZmVhdHVyZSgnS0FJUk9TX0JSSUVGJykpIHtcbiAgICAgIC8vIEVzY2FwZSBoYXRjaDogR0Iga2lsbC1zd2l0Y2ggd2hpbGUgZGVmYXVsdFZpZXc9Y2hhdCB3YXMgcGVyc2lzdGVkXG4gICAgICAvLyBjYW4gbGVhdmUgaXNCcmllZk9ubHkgc3R1Y2sgb24sIHNob3dpbmcgYSBibGFuayBmaWx0ZXJGb3JCcmllZlRvb2xcbiAgICAgIC8vIHZpZXcuIFVzZXJzIHdpbGwgcmVhY2ggZm9yIGN0cmwrbyDigJQgY2xlYXIgdGhlIHN0dWNrIHN0YXRlIGZpcnN0LlxuICAgICAgLy8gT25seSBuZWVkZWQgaW4gdGhlIHByb21wdCBzY3JlZW4g4oCUIHRyYW5zY3JpcHQgbW9kZSBhbHJlYWR5IGlnbm9yZXNcbiAgICAgIC8vIGlzQnJpZWZPbmx5IChNZXNzYWdlcy50c3ggZmlsdGVyIGlzIGdhdGVkIG9uICFpc1RyYW5zY3JpcHRNb2RlKS5cbiAgICAgIC8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbiAgICAgIGNvbnN0IHsgaXNCcmllZkVuYWJsZWQgfSA9XG4gICAgICAgIHJlcXVpcmUoJy4uL3Rvb2xzL0JyaWVmVG9vbC9CcmllZlRvb2wuanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuLi90b29scy9CcmllZlRvb2wvQnJpZWZUb29sLmpzJylcbiAgICAgIC8qIGVzbGludC1lbmFibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICAgICAgaWYgKCFpc0JyaWVmRW5hYmxlZCgpICYmIGlzQnJpZWZPbmx5ICYmIHNjcmVlbiAhPT0gJ3RyYW5zY3JpcHQnKSB7XG4gICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgIGlmICghcHJldi5pc0JyaWVmT25seSkgcmV0dXJuIHByZXZcbiAgICAgICAgICByZXR1cm4geyAuLi5wcmV2LCBpc0JyaWVmT25seTogZmFsc2UgfVxuICAgICAgICB9KVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCBpc0VudGVyaW5nVHJhbnNjcmlwdCA9IHNjcmVlbiAhPT0gJ3RyYW5zY3JpcHQnXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X3RvZ2dsZV90cmFuc2NyaXB0Jywge1xuICAgICAgaXNfZW50ZXJpbmc6IGlzRW50ZXJpbmdUcmFuc2NyaXB0LFxuICAgICAgc2hvd19hbGw6IHNob3dBbGxJblRyYW5zY3JpcHQsXG4gICAgICBtZXNzYWdlX2NvdW50OiBtZXNzYWdlQ291bnQsXG4gICAgfSlcbiAgICBzZXRTY3JlZW4ocyA9PiAocyA9PT0gJ3RyYW5zY3JpcHQnID8gJ3Byb21wdCcgOiAndHJhbnNjcmlwdCcpKVxuICAgIHNldFNob3dBbGxJblRyYW5zY3JpcHQoZmFsc2UpXG4gICAgaWYgKGlzRW50ZXJpbmdUcmFuc2NyaXB0ICYmIG9uRW50ZXJUcmFuc2NyaXB0KSB7XG4gICAgICBvbkVudGVyVHJhbnNjcmlwdCgpXG4gICAgfVxuICAgIGlmICghaXNFbnRlcmluZ1RyYW5zY3JpcHQgJiYgb25FeGl0VHJhbnNjcmlwdCkge1xuICAgICAgb25FeGl0VHJhbnNjcmlwdCgpXG4gICAgfVxuICB9LCBbXG4gICAgc2NyZWVuLFxuICAgIHNldFNjcmVlbixcbiAgICBpc0JyaWVmT25seSxcbiAgICBzaG93QWxsSW5UcmFuc2NyaXB0LFxuICAgIHNldFNob3dBbGxJblRyYW5zY3JpcHQsXG4gICAgbWVzc2FnZUNvdW50LFxuICAgIHNldEFwcFN0YXRlLFxuICAgIG9uRW50ZXJUcmFuc2NyaXB0LFxuICAgIG9uRXhpdFRyYW5zY3JpcHQsXG4gIF0pXG5cbiAgLy8gVG9nZ2xlIHNob3dpbmcgYWxsIG1lc3NhZ2VzIGluIHRyYW5zY3JpcHQgbW9kZSAoY3RybCtlKVxuICBjb25zdCBoYW5kbGVUb2dnbGVTaG93QWxsID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV90cmFuc2NyaXB0X3RvZ2dsZV9zaG93X2FsbCcsIHtcbiAgICAgIGlzX2V4cGFuZGluZzogIXNob3dBbGxJblRyYW5zY3JpcHQsXG4gICAgICBtZXNzYWdlX2NvdW50OiBtZXNzYWdlQ291bnQsXG4gICAgfSlcbiAgICBzZXRTaG93QWxsSW5UcmFuc2NyaXB0KHByZXYgPT4gIXByZXYpXG4gIH0sIFtzaG93QWxsSW5UcmFuc2NyaXB0LCBzZXRTaG93QWxsSW5UcmFuc2NyaXB0LCBtZXNzYWdlQ291bnRdKVxuXG4gIC8vIEV4aXQgdHJhbnNjcmlwdCBtb2RlIChjdHJsK2Mgb3IgZXNjYXBlKVxuICBjb25zdCBoYW5kbGVFeGl0VHJhbnNjcmlwdCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBsb2dFdmVudCgndGVuZ3VfdHJhbnNjcmlwdF9leGl0Jywge1xuICAgICAgc2hvd19hbGw6IHNob3dBbGxJblRyYW5zY3JpcHQsXG4gICAgICBtZXNzYWdlX2NvdW50OiBtZXNzYWdlQ291bnQsXG4gICAgfSlcbiAgICBzZXRTY3JlZW4oJ3Byb21wdCcpXG4gICAgc2V0U2hvd0FsbEluVHJhbnNjcmlwdChmYWxzZSlcbiAgICBpZiAob25FeGl0VHJhbnNjcmlwdCkge1xuICAgICAgb25FeGl0VHJhbnNjcmlwdCgpXG4gICAgfVxuICB9LCBbXG4gICAgc2V0U2NyZWVuLFxuICAgIHNob3dBbGxJblRyYW5zY3JpcHQsXG4gICAgc2V0U2hvd0FsbEluVHJhbnNjcmlwdCxcbiAgICBtZXNzYWdlQ291bnQsXG4gICAgb25FeGl0VHJhbnNjcmlwdCxcbiAgXSlcblxuICAvLyBUb2dnbGUgYnJpZWYtb25seSB2aWV3IChjdHJsK3NoaWZ0K2IpLiBQdXJlIGRpc3BsYXkgZmlsdGVyIHRvZ2dsZSDigJRcbiAgLy8gZG9lcyBub3QgdG91Y2ggb3B0LWluIHN0YXRlLiBBc3ltbWV0cmljIGdhdGUgKG1pcnJvcnMgL2JyaWVmKTogT0ZGXG4gIC8vIHRyYW5zaXRpb24gYWx3YXlzIGFsbG93ZWQgc28gdGhlIHNhbWUga2V5IHRoYXQgZ290IHlvdSBpbiBnZXRzIHlvdVxuICAvLyBvdXQgZXZlbiBpZiB0aGUgR0Iga2lsbC1zd2l0Y2ggZmlyZXMgbWlkLXNlc3Npb24uXG4gIGNvbnN0IGhhbmRsZVRvZ2dsZUJyaWVmID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKSkge1xuICAgICAgLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICAgICAgY29uc3QgeyBpc0JyaWVmRW5hYmxlZCB9ID1cbiAgICAgICAgcmVxdWlyZSgnLi4vdG9vbHMvQnJpZWZUb29sL0JyaWVmVG9vbC5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4uL3Rvb2xzL0JyaWVmVG9vbC9CcmllZlRvb2wuanMnKVxuICAgICAgLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG4gICAgICBpZiAoIWlzQnJpZWZFbmFibGVkKCkgJiYgIWlzQnJpZWZPbmx5KSByZXR1cm5cbiAgICAgIGNvbnN0IG5leHQgPSAhaXNCcmllZk9ubHlcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9icmllZl9tb2RlX3RvZ2dsZWQnLCB7XG4gICAgICAgIGVuYWJsZWQ6IG5leHQsXG4gICAgICAgIGdhdGVkOiBmYWxzZSxcbiAgICAgICAgc291cmNlOlxuICAgICAgICAgICdrZXliaW5kaW5nJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcbiAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICBpZiAocHJldi5pc0JyaWVmT25seSA9PT0gbmV4dCkgcmV0dXJuIHByZXZcbiAgICAgICAgcmV0dXJuIHsgLi4ucHJldiwgaXNCcmllZk9ubHk6IG5leHQgfVxuICAgICAgfSlcbiAgICB9XG4gIH0sIFtpc0JyaWVmT25seSwgc2V0QXBwU3RhdGVdKVxuXG4gIC8vIFJlZ2lzdGVyIGtleWJpbmRpbmcgaGFuZGxlcnNcbiAgdXNlS2V5YmluZGluZygnYXBwOnRvZ2dsZVRvZG9zJywgaGFuZGxlVG9nZ2xlVG9kb3MsIHtcbiAgICBjb250ZXh0OiAnR2xvYmFsJyxcbiAgfSlcbiAgdXNlS2V5YmluZGluZygnYXBwOnRvZ2dsZVRyYW5zY3JpcHQnLCBoYW5kbGVUb2dnbGVUcmFuc2NyaXB0LCB7XG4gICAgY29udGV4dDogJ0dsb2JhbCcsXG4gIH0pXG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKSkge1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgICB1c2VLZXliaW5kaW5nKCdhcHA6dG9nZ2xlQnJpZWYnLCBoYW5kbGVUb2dnbGVCcmllZiwge1xuICAgICAgY29udGV4dDogJ0dsb2JhbCcsXG4gICAgfSlcbiAgfVxuXG4gIC8vIFJlZ2lzdGVyIHRlYW1tYXRlIGtleWJpbmRpbmdcbiAgdXNlS2V5YmluZGluZyhcbiAgICAnYXBwOnRvZ2dsZVRlYW1tYXRlUHJldmlldycsXG4gICAgKCkgPT4ge1xuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiAoe1xuICAgICAgICAuLi5wcmV2LFxuICAgICAgICBzaG93VGVhbW1hdGVNZXNzYWdlUHJldmlldzogIXByZXYuc2hvd1RlYW1tYXRlTWVzc2FnZVByZXZpZXcsXG4gICAgICB9KSlcbiAgICB9LFxuICAgIHtcbiAgICAgIGNvbnRleHQ6ICdHbG9iYWwnLFxuICAgIH0sXG4gIClcblxuICAvLyBUb2dnbGUgYnVpbHQtaW4gdGVybWluYWwgcGFuZWwgKG1ldGEraikuXG4gIC8vIHRvZ2dsZSgpIGJsb2NrcyBpbiBzcGF3blN5bmMgdW50aWwgdGhlIHVzZXIgZGV0YWNoZXMgZnJvbSB0bXV4LlxuICBjb25zdCBoYW5kbGVUb2dnbGVUZXJtaW5hbCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoZmVhdHVyZSgnVEVSTUlOQUxfUEFORUwnKSkge1xuICAgICAgaWYgKCFnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSgndGVuZ3VfdGVybWluYWxfcGFuZWwnLCBmYWxzZSkpIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBnZXRUZXJtaW5hbFBhbmVsKCkudG9nZ2xlKClcbiAgICB9XG4gIH0sIFtdKVxuICB1c2VLZXliaW5kaW5nKCdhcHA6dG9nZ2xlVGVybWluYWwnLCBoYW5kbGVUb2dnbGVUZXJtaW5hbCwge1xuICAgIGNvbnRleHQ6ICdHbG9iYWwnLFxuICB9KVxuXG4gIC8vIENsZWFyIHNjcmVlbiBhbmQgZm9yY2UgZnVsbCByZWRyYXcgKGN0cmwrbCkuIFJlY292ZXJ5IHBhdGggd2hlbiB0aGVcbiAgLy8gdGVybWluYWwgd2FzIGNsZWFyZWQgZXh0ZXJuYWxseSAobWFjT1MgQ21kK0spIGFuZCBJbmsncyBkaWZmIGVuZ2luZVxuICAvLyB0aGlua3MgdW5jaGFuZ2VkIGNlbGxzIGRvbid0IG5lZWQgcmVwYWludGluZy5cbiAgY29uc3QgaGFuZGxlUmVkcmF3ID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGluc3RhbmNlcy5nZXQocHJvY2Vzcy5zdGRvdXQpPy5mb3JjZVJlZHJhdygpXG4gIH0sIFtdKVxuICB1c2VLZXliaW5kaW5nKCdhcHA6cmVkcmF3JywgaGFuZGxlUmVkcmF3LCB7IGNvbnRleHQ6ICdHbG9iYWwnIH0pXG5cbiAgLy8gVHJhbnNjcmlwdC1zcGVjaWZpYyBiaW5kaW5ncyAob25seSBhY3RpdmUgd2hlbiBpbiB0cmFuc2NyaXB0IG1vZGUpXG4gIGNvbnN0IGlzSW5UcmFuc2NyaXB0ID0gc2NyZWVuID09PSAndHJhbnNjcmlwdCdcbiAgdXNlS2V5YmluZGluZygndHJhbnNjcmlwdDp0b2dnbGVTaG93QWxsJywgaGFuZGxlVG9nZ2xlU2hvd0FsbCwge1xuICAgIGNvbnRleHQ6ICdUcmFuc2NyaXB0JyxcbiAgICBpc0FjdGl2ZTogaXNJblRyYW5zY3JpcHQgJiYgIXZpcnR1YWxTY3JvbGxBY3RpdmUsXG4gIH0pXG4gIHVzZUtleWJpbmRpbmcoJ3RyYW5zY3JpcHQ6ZXhpdCcsIGhhbmRsZUV4aXRUcmFuc2NyaXB0LCB7XG4gICAgY29udGV4dDogJ1RyYW5zY3JpcHQnLFxuICAgIC8vIEJhci1vcGVuIGlzIGEgbW9kZSAob3ducyBrZXlzdHJva2VzKS4gTmF2aWdhdGluZyAoaGlnaGxpZ2h0c1xuICAgIC8vIHZpc2libGUsIG4vTiBhY3RpdmUsIGJhciBjbG9zZWQpIGlzIE5PVCDigJQgRXNjIGV4aXRzIHRyYW5zY3JpcHRcbiAgICAvLyBkaXJlY3RseSwgc2FtZSBhcyBsZXNzIHEuIHVzZVNlYXJjaElucHV0IGRvZXNuJ3Qgc3RvcFByb3BhZ2F0aW9uLFxuICAgIC8vIHNvIHdpdGhvdXQgdGhpcyBnYXRlIGl0cyBvbkNhbmNlbCBBTkQgdGhpcyBoYW5kbGVyIHdvdWxkIGJvdGhcbiAgICAvLyBmaXJlIG9uIG9uZSBFc2MgKGNoaWxkIHJlZ2lzdGVycyBmaXJzdCwgZmlyZXMgZmlyc3QsIGJ1YmJsZXMpLlxuICAgIGlzQWN0aXZlOiBpc0luVHJhbnNjcmlwdCAmJiAhc2VhcmNoQmFyT3BlbixcbiAgfSlcblxuICByZXR1cm4gbnVsbFxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTQSxPQUFPLFFBQVEsWUFBWTtBQUNwQyxTQUFTQyxXQUFXLFFBQVEsT0FBTztBQUNuQyxPQUFPQyxTQUFTLE1BQU0scUJBQXFCO0FBQzNDLFNBQVNDLGFBQWEsUUFBUSxpQ0FBaUM7QUFDL0Qs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 \ No newline at end of file diff --git a/hooks/useHistorySearch.ts b/hooks/useHistorySearch.ts new file mode 100644 index 0000000..b48c880 --- /dev/null +++ b/hooks/useHistorySearch.ts @@ -0,0 +1,303 @@ +import { feature } from 'bun:bundle' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { + getModeFromInput, + getValueFromInput, +} from '../components/PromptInput/inputModes.js' +import { makeHistoryReader } from '../history.js' +import { KeyboardEvent } from '../ink/events/keyboard-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until consumers wire handleKeyDown to +import { useInput } from '../ink.js' +import { useKeybinding, useKeybindings } from '../keybindings/useKeybinding.js' +import type { PromptInputMode } from '../types/textInputTypes.js' +import type { HistoryEntry } from '../utils/config.js' + +export function useHistorySearch( + onAcceptHistory: (entry: HistoryEntry) => void, + currentInput: string, + onInputChange: (input: string) => void, + onCursorChange: (cursorOffset: number) => void, + currentCursorOffset: number, + onModeChange: (mode: PromptInputMode) => void, + currentMode: PromptInputMode, + isSearching: boolean, + setIsSearching: (isSearching: boolean) => void, + setPastedContents: (pastedContents: HistoryEntry['pastedContents']) => void, + currentPastedContents: HistoryEntry['pastedContents'], +): { + historyQuery: string + setHistoryQuery: (query: string) => void + historyMatch: HistoryEntry | undefined + historyFailedMatch: boolean + handleKeyDown: (e: KeyboardEvent) => void +} { + const [historyQuery, setHistoryQuery] = useState('') + const [historyFailedMatch, setHistoryFailedMatch] = useState(false) + const [originalInput, setOriginalInput] = useState('') + const [originalCursorOffset, setOriginalCursorOffset] = useState(0) + const [originalMode, setOriginalMode] = useState('prompt') + const [originalPastedContents, setOriginalPastedContents] = useState< + HistoryEntry['pastedContents'] + >({}) + const [historyMatch, setHistoryMatch] = useState( + undefined, + ) + const historyReader = useRef | undefined>( + undefined, + ) + const seenPrompts = useRef>(new Set()) + const searchAbortController = useRef(null) + + const closeHistoryReader = useCallback((): void => { + if (historyReader.current) { + // Must explicitly call .return() to trigger the finally block in readLinesReverse, + // which closes the file handle. Without this, file descriptors leak. + void historyReader.current.return(undefined) + historyReader.current = undefined + } + }, []) + + const reset = useCallback((): void => { + setIsSearching(false) + setHistoryQuery('') + setHistoryFailedMatch(false) + setOriginalInput('') + setOriginalCursorOffset(0) + setOriginalMode('prompt') + setOriginalPastedContents({}) + setHistoryMatch(undefined) + closeHistoryReader() + seenPrompts.current.clear() + }, [setIsSearching, closeHistoryReader]) + + const searchHistory = useCallback( + async (resume: boolean, signal?: AbortSignal): Promise => { + if (!isSearching) { + return + } + + if (historyQuery.length === 0) { + closeHistoryReader() + seenPrompts.current.clear() + setHistoryMatch(undefined) + setHistoryFailedMatch(false) + onInputChange(originalInput) + onCursorChange(originalCursorOffset) + onModeChange(originalMode) + setPastedContents(originalPastedContents) + return + } + + if (!resume) { + closeHistoryReader() + historyReader.current = makeHistoryReader() + seenPrompts.current.clear() + } + + if (!historyReader.current) { + return + } + + while (true) { + if (signal?.aborted) { + return + } + + const item = await historyReader.current.next() + if (item.done) { + // No match found - keep last match but mark as failed + setHistoryFailedMatch(true) + return + } + + const display = item.value.display + + const matchPosition = display.lastIndexOf(historyQuery) + if (matchPosition !== -1 && !seenPrompts.current.has(display)) { + seenPrompts.current.add(display) + setHistoryMatch(item.value) + setHistoryFailedMatch(false) + const mode = getModeFromInput(display) + onModeChange(mode) + onInputChange(display) + setPastedContents(item.value.pastedContents) + + // Position cursor relative to the clean value, not the display + const value = getValueFromInput(display) + const cleanMatchPosition = value.lastIndexOf(historyQuery) + onCursorChange( + cleanMatchPosition !== -1 ? cleanMatchPosition : matchPosition, + ) + return + } + } + }, + [ + isSearching, + historyQuery, + closeHistoryReader, + onInputChange, + onCursorChange, + onModeChange, + setPastedContents, + originalInput, + originalCursorOffset, + originalMode, + originalPastedContents, + ], + ) + + // Handler: Start history search (when not searching) + const handleStartSearch = useCallback(() => { + setIsSearching(true) + setOriginalInput(currentInput) + setOriginalCursorOffset(currentCursorOffset) + setOriginalMode(currentMode) + setOriginalPastedContents(currentPastedContents) + historyReader.current = makeHistoryReader() + seenPrompts.current.clear() + }, [ + setIsSearching, + currentInput, + currentCursorOffset, + currentMode, + currentPastedContents, + ]) + + // Handler: Find next match (when searching) + const handleNextMatch = useCallback(() => { + void searchHistory(true) + }, [searchHistory]) + + // Handler: Accept current match and exit search + const handleAccept = useCallback(() => { + if (historyMatch) { + const mode = getModeFromInput(historyMatch.display) + const value = getValueFromInput(historyMatch.display) + onInputChange(value) + onModeChange(mode) + setPastedContents(historyMatch.pastedContents) + } else { + // No match - restore original pasted contents + setPastedContents(originalPastedContents) + } + reset() + }, [ + historyMatch, + onInputChange, + onModeChange, + setPastedContents, + originalPastedContents, + reset, + ]) + + // Handler: Cancel search and restore original input + const handleCancel = useCallback(() => { + onInputChange(originalInput) + onCursorChange(originalCursorOffset) + setPastedContents(originalPastedContents) + reset() + }, [ + onInputChange, + onCursorChange, + setPastedContents, + originalInput, + originalCursorOffset, + originalPastedContents, + reset, + ]) + + // Handler: Execute (accept and submit) + const handleExecute = useCallback(() => { + if (historyQuery.length === 0) { + onAcceptHistory({ + display: originalInput, + pastedContents: originalPastedContents, + }) + } else if (historyMatch) { + const mode = getModeFromInput(historyMatch.display) + const value = getValueFromInput(historyMatch.display) + onModeChange(mode) + onAcceptHistory({ + display: value, + pastedContents: historyMatch.pastedContents, + }) + } + reset() + }, [ + historyQuery, + historyMatch, + onAcceptHistory, + onModeChange, + originalInput, + originalPastedContents, + reset, + ]) + + // Gated off under HISTORY_PICKER — the modal dialog owns ctrl+r there. + useKeybinding('history:search', handleStartSearch, { + context: 'Global', + isActive: feature('HISTORY_PICKER') ? false : !isSearching, + }) + + // History search context keybindings (only active when searching) + const historySearchHandlers = useMemo( + () => ({ + 'historySearch:next': handleNextMatch, + 'historySearch:accept': handleAccept, + 'historySearch:cancel': handleCancel, + 'historySearch:execute': handleExecute, + }), + [handleNextMatch, handleAccept, handleCancel, handleExecute], + ) + + useKeybindings(historySearchHandlers, { + context: 'HistorySearch', + isActive: isSearching, + }) + + // Handle backspace when query is empty (cancels search) + // This is a conditional behavior that doesn't fit the keybinding model + // well (backspace only cancels when query is empty) + const handleKeyDown = (e: KeyboardEvent): void => { + if (!isSearching) return + if (e.key === 'backspace' && historyQuery === '') { + e.preventDefault() + handleCancel() + } + } + + // Backward-compat bridge: PromptInput doesn't yet wire handleKeyDown to + // . Subscribe via useInput and adapt InputEvent → + // KeyboardEvent until the consumer is migrated (separate PR). + // TODO(onKeyDown-migration): remove once PromptInput passes handleKeyDown. + useInput( + (_input, _key, event) => { + handleKeyDown(new KeyboardEvent(event.keypress)) + }, + { isActive: isSearching }, + ) + + // Keep a ref to searchHistory to avoid it being a dependency of useEffect + const searchHistoryRef = useRef(searchHistory) + searchHistoryRef.current = searchHistory + + // Reset history search when query changes + useEffect(() => { + searchAbortController.current?.abort() + const controller = new AbortController() + searchAbortController.current = controller + void searchHistoryRef.current(false, controller.signal) + return () => { + controller.abort() + } + }, [historyQuery]) + + return { + historyQuery, + setHistoryQuery, + historyMatch, + historyFailedMatch, + handleKeyDown, + } +} diff --git a/hooks/useIDEIntegration.tsx b/hooks/useIDEIntegration.tsx new file mode 100644 index 0000000..65f9ff3 --- /dev/null +++ b/hooks/useIDEIntegration.tsx @@ -0,0 +1,70 @@ +import { c as _c } from "react/compiler-runtime"; +import { useEffect } from 'react'; +import type { ScopedMcpServerConfig } from '../services/mcp/types.js'; +import { getGlobalConfig } from '../utils/config.js'; +import { isEnvDefinedFalsy, isEnvTruthy } from '../utils/envUtils.js'; +import type { DetectedIDEInfo } from '../utils/ide.js'; +import { type IDEExtensionInstallationStatus, type IdeType, initializeIdeIntegration, isSupportedTerminal } from '../utils/ide.js'; +type UseIDEIntegrationProps = { + autoConnectIdeFlag?: boolean; + ideToInstallExtension: IdeType | null; + setDynamicMcpConfig: React.Dispatch | undefined>>; + setShowIdeOnboarding: React.Dispatch>; + setIDEInstallationState: React.Dispatch>; +}; +export function useIDEIntegration(t0) { + const $ = _c(7); + const { + autoConnectIdeFlag, + ideToInstallExtension, + setDynamicMcpConfig, + setShowIdeOnboarding, + setIDEInstallationState + } = t0; + let t1; + let t2; + if ($[0] !== autoConnectIdeFlag || $[1] !== ideToInstallExtension || $[2] !== setDynamicMcpConfig || $[3] !== setIDEInstallationState || $[4] !== setShowIdeOnboarding) { + t1 = () => { + const addIde = function addIde(ide) { + if (!ide) { + return; + } + const globalConfig = getGlobalConfig(); + const autoConnectEnabled = (globalConfig.autoConnectIde || autoConnectIdeFlag || isSupportedTerminal() || process.env.CLAUDE_CODE_SSE_PORT || ideToInstallExtension || isEnvTruthy(process.env.CLAUDE_CODE_AUTO_CONNECT_IDE)) && !isEnvDefinedFalsy(process.env.CLAUDE_CODE_AUTO_CONNECT_IDE); + if (!autoConnectEnabled) { + return; + } + setDynamicMcpConfig(prev => { + if (prev?.ide) { + return prev; + } + return { + ...prev, + ide: { + type: ide.url.startsWith("ws:") ? "ws-ide" : "sse-ide", + url: ide.url, + ideName: ide.name, + authToken: ide.authToken, + ideRunningInWindows: ide.ideRunningInWindows, + scope: "dynamic" as const + } + }; + }); + }; + initializeIdeIntegration(addIde, ideToInstallExtension, () => setShowIdeOnboarding(true), status => setIDEInstallationState(status)); + }; + t2 = [autoConnectIdeFlag, ideToInstallExtension, setDynamicMcpConfig, setShowIdeOnboarding, setIDEInstallationState]; + $[0] = autoConnectIdeFlag; + $[1] = ideToInstallExtension; + $[2] = setDynamicMcpConfig; + $[3] = setIDEInstallationState; + $[4] = setShowIdeOnboarding; + $[5] = t1; + $[6] = t2; + } else { + t1 = $[5]; + t2 = $[6]; + } + useEffect(t1, t2); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1c2VFZmZlY3QiLCJTY29wZWRNY3BTZXJ2ZXJDb25maWciLCJnZXRHbG9iYWxDb25maWciLCJpc0VudkRlZmluZWRGYWxzeSIsImlzRW52VHJ1dGh5IiwiRGV0ZWN0ZWRJREVJbmZvIiwiSURFRXh0ZW5zaW9uSW5zdGFsbGF0aW9uU3RhdHVzIiwiSWRlVHlwZSIsImluaXRpYWxpemVJZGVJbnRlZ3JhdGlvbiIsImlzU3VwcG9ydGVkVGVybWluYWwiLCJVc2VJREVJbnRlZ3JhdGlvblByb3BzIiwiYXV0b0Nvbm5lY3RJZGVGbGFnIiwiaWRlVG9JbnN0YWxsRXh0ZW5zaW9uIiwic2V0RHluYW1pY01jcENvbmZpZyIsIlJlYWN0IiwiRGlzcGF0Y2giLCJTZXRTdGF0ZUFjdGlvbiIsIlJlY29yZCIsInNldFNob3dJZGVPbmJvYXJkaW5nIiwic2V0SURFSW5zdGFsbGF0aW9uU3RhdGUiLCJ1c2VJREVJbnRlZ3JhdGlvbiIsInQwIiwiJCIsIl9jIiwidDEiLCJ0MiIsImFkZElkZSIsImlkZSIsImdsb2JhbENvbmZpZyIsImF1dG9Db25uZWN0RW5hYmxlZCIsImF1dG9Db25uZWN0SWRlIiwicHJvY2VzcyIsImVudiIsIkNMQVVERV9DT0RFX1NTRV9QT1JUIiwiQ0xBVURFX0NPREVfQVVUT19DT05ORUNUX0lERSIsInByZXYiLCJ0eXBlIiwidXJsIiwic3RhcnRzV2l0aCIsImlkZU5hbWUiLCJuYW1lIiwiYXV0aFRva2VuIiwiaWRlUnVubmluZ0luV2luZG93cyIsInNjb3BlIiwiY29uc3QiLCJzdGF0dXMiXSwic291cmNlcyI6WyJ1c2VJREVJbnRlZ3JhdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IFNjb3BlZE1jcFNlcnZlckNvbmZpZyB9IGZyb20gJy4uL3NlcnZpY2VzL21jcC90eXBlcy5qcydcbmltcG9ydCB7IGdldEdsb2JhbENvbmZpZyB9IGZyb20gJy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGlzRW52RGVmaW5lZEZhbHN5LCBpc0VudlRydXRoeSB9IGZyb20gJy4uL3V0aWxzL2VudlV0aWxzLmpzJ1xuaW1wb3J0IHR5cGUgeyBEZXRlY3RlZElERUluZm8gfSBmcm9tICcuLi91dGlscy9pZGUuanMnXG5pbXBvcnQge1xuICB0eXBlIElERUV4dGVuc2lvbkluc3RhbGxhdGlvblN0YXR1cyxcbiAgdHlwZSBJZGVUeXBlLFxuICBpbml0aWFsaXplSWRlSW50ZWdyYXRpb24sXG4gIGlzU3VwcG9ydGVkVGVybWluYWwsXG59IGZyb20gJy4uL3V0aWxzL2lkZS5qcydcblxudHlwZSBVc2VJREVJbnRlZ3JhdGlvblByb3BzID0ge1xuICBhdXRvQ29ubmVjdElkZUZsYWc/OiBib29sZWFuXG4gIGlkZVRvSW5zdGFsbEV4dGVuc2lvbjogSWRlVHlwZSB8IG51bGxcbiAgc2V0RHluYW1pY01jcENvbmZpZzogUmVhY3QuRGlzcGF0Y2g8XG4gICAgUmVhY3QuU2V0U3RhdGVBY3Rpb248UmVjb3JkPHN0cmluZywgU2NvcGVkTWNwU2VydmVyQ29uZmlnPiB8IHVuZGVmaW5lZD5cbiAgPlxuICBzZXRTaG93SWRlT25ib2FyZGluZzogUmVhY3QuRGlzcGF0Y2g8UmVhY3QuU2V0U3RhdGVBY3Rpb248Ym9vbGVhbj4+XG4gIHNldElERUluc3RhbGxhdGlvblN0YXRlOiBSZWFjdC5EaXNwYXRjaDxcbiAgICBSZWFjdC5TZXRTdGF0ZUFjdGlvbjxJREVFeHRlbnNpb25JbnN0YWxsYXRpb25TdGF0dXMgfCBudWxsPlxuICA+XG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VJREVJbnRlZ3JhdGlvbih7XG4gIGF1dG9Db25uZWN0SWRlRmxhZyxcbiAgaWRlVG9JbnN0YWxsRXh0ZW5zaW9uLFxuICBzZXREeW5hbWljTWNwQ29uZmlnLFxuICBzZXRTaG93SWRlT25ib2FyZGluZyxcbiAgc2V0SURFSW5zdGFsbGF0aW9uU3RhdGUsXG59OiBVc2VJREVJbnRlZ3JhdGlvblByb3BzKTogdm9pZCB7XG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgZnVuY3Rpb24gYWRkSWRlKGlkZTogRGV0ZWN0ZWRJREVJbmZvIHwgbnVsbCkge1xuICAgICAgaWYgKCFpZGUpIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGlmIGF1dG8tY29ubmVjdCBpcyBlbmFibGVkXG4gICAgICBjb25zdCBnbG9iYWxDb25maWcgPSBnZXRHbG9iYWxDb25maWcoKVxuICAgICAgY29uc3QgYXV0b0Nvbm5lY3RFbmFibGVkID1cbiAgICAgICAgKGdsb2JhbENvbmZpZy5hdXRvQ29ubmVjdElkZSB8fFxuICAgICAgICAgIGF1dG9Db25uZWN0SWRlRmxhZyB8fFxuICAgICAgICAgIGlzU3VwcG9ydGVkVGVybWluYWwoKSB8fFxuICAgICAgICAgIC8vIHRtdXgvc2NyZWVuIG92ZXJ3cml0ZSBURVJNX1BST0dSQU0sIGJyZWFraW5nIHRlcm1pbmFsIGRldGVjdGlvbiwgYnV0IHRoZVxuICAgICAgICAgIC8vIElERSBleHRlbnNpb24ncyBwb3J0IGVudiB2YXIgaXMgaW5oZXJpdGVkLiBJZiBzZXQsIGF1dG8tY29ubmVjdCBhbnl3YXkuXG4gICAgICAgICAgcHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfU1NFX1BPUlQgfHxcbiAgICAgICAgICBpZGVUb0luc3RhbGxFeHRlbnNpb24gfHxcbiAgICAgICAgICBpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9BVVRPX0NPTk5FQ1RfSURFKSkgJiZcbiAgICAgICAgIWlzRW52RGVmaW5lZEZhbHN5KHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0FVVE9fQ09OTkVDVF9JREUpXG5cbiAgICAgIGlmICghYXV0b0Nvbm5lY3RFbmFibGVkKSB7XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICBzZXREeW5hbWljTWNwQ29uZmlnKHByZXYgPT4ge1xuICAgICAgICAvLyBPbmx5IGFkZCB0aGUgSURFIGlmIHdlIGRvbid0IGFscmVhZHkgaGF2ZSBvbmVcbiAgICAgICAgaWYgKHByZXY/LmlkZSkge1xuICAgICAgICAgIHJldHVybiBwcmV2XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgIGlkZToge1xuICAgICAgICAgICAgdHlwZTogaWRlLnVybC5zdGFydHNXaXRoKCd3czonKSA/ICd3cy1pZGUnIDogJ3NzZS1pZGUnLFxuICAgICAgICAgICAgdXJsOiBpZGUudXJsLFxuICAgICAgICAgICAgaWRlTmFtZTogaWRlLm5hbWUsXG4gICAgICAgICAgICBhdXRoVG9rZW46IGlkZS5hdXRoVG9rZW4sXG4gICAgICAgICAgICBpZGVSdW5uaW5nSW5XaW5kb3dzOiBpZGUuaWRlUnVubmluZ0luV2luZG93cyxcbiAgICAgICAgICAgIHNjb3BlOiAnZHluYW1pYycgYXMgY29uc3QsXG4gICAgICAgICAgfSxcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBVc2UgdGhlIG5ldyB1dGlsaXR5IGZ1bmN0aW9uXG4gICAgdm9pZCBpbml0aWFsaXplSWRlSW50ZWdyYXRpb24oXG4gICAgICBhZGRJZGUsXG4gICAgICBpZGVUb0luc3RhbGxFeHRlbnNpb24sXG4gICAgICAoKSA9PiBzZXRTaG93SWRlT25ib2FyZGluZyh0cnVlKSxcbiAgICAgIHN0YXR1cyA9PiBzZXRJREVJbnN0YWxsYXRpb25TdGF0ZShzdGF0dXMpLFxuICAgIClcbiAgfSwgW1xuICAgIGF1dG9Db25uZWN0SWRlRmxhZyxcbiAgICBpZGVUb0luc3RhbGxFeHRlbnNpb24sXG4gICAgc2V0RHluYW1pY01jcENvbmZpZyxcbiAgICBzZXRTaG93SWRlT25ib2FyZGluZyxcbiAgICBzZXRJREVJbnN0YWxsYXRpb25TdGF0ZSxcbiAgXSlcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLFNBQVMsUUFBUSxPQUFPO0FBQ2pDLGNBQWNDLHFCQUFxQixRQUFRLDBCQUEwQjtBQUNyRSxTQUFTQyxlQUFlLFFBQVEsb0JBQW9CO0FBQ3BELFNBQVNDLGlCQUFpQixFQUFFQyxXQUFXLFFBQVEsc0JBQXNCO0FBQ3JFLGNBQWNDLGVBQWUsUUFBUSxpQkFBaUI7QUFDdEQsU0FDRSxLQUFLQyw4QkFBOEIsRUFDbkMsS0FBS0MsT0FBTyxFQUNaQyx3QkFBd0IsRUFDeEJDLG1CQUFtQixRQUNkLGlCQUFpQjtBQUV4QixLQUFLQyxzQkFBc0IsR0FBRztFQUM1QkMsa0JBQWtCLENBQUMsRUFBRSxPQUFPO0VBQzVCQyxxQkFBcUIsRUFBRUwsT0FBTyxHQUFHLElBQUk7RUFDckNNLG1CQUFtQixFQUFFQyxLQUFLLENBQUNDLFFBQVEsQ0FDakNELEtBQUssQ0FBQ0UsY0FBYyxDQUFDQyxNQUFNLENBQUMsTUFBTSxFQUFFaEIscUJBQXFCLENBQUMsR0FBRyxTQUFTLENBQUMsQ0FDeEU7RUFDRGlCLG9CQUFvQixFQUFFSixLQUFLLENBQUNDLFFBQVEsQ0FBQ0QsS0FBSyxDQUFDRSxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUM7RUFDbkVHLHVCQUF1QixFQUFFTCxLQUFLLENBQUNDLFFBQVEsQ0FDckNELEtBQUssQ0FBQ0UsY0FBYyxDQUFDViw4QkFBOEIsR0FBRyxJQUFJLENBQUMsQ0FDNUQ7QUFDSCxDQUFDO0FBRUQsT0FBTyxTQUFBYyxrQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUEyQjtJQUFBWixrQkFBQTtJQUFBQyxxQkFBQTtJQUFBQyxtQkFBQTtJQUFBSyxvQkFBQTtJQUFBQztFQUFBLElBQUFFLEVBTVQ7RUFBQSxJQUFBRyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFILENBQUEsUUFBQVgsa0JBQUEsSUFBQVcsQ0FBQSxRQUFBVixxQkFBQSxJQUFBVSxDQUFBLFFBQUFULG1CQUFBLElBQUFTLENBQUEsUUFBQUgsdUJBQUEsSUFBQUcsQ0FBQSxRQUFBSixvQkFBQTtJQUNiTSxFQUFBLEdBQUFBLENBQUE7TUFDUixNQUFBRSxNQUFBLFlBQUFBLE9BQUFDLEdBQUE7UUFDRSxJQUFJLENBQUNBLEdBQUc7VUFBQTtRQUFBO1FBS1IsTUFBQUMsWUFBQSxHQUFxQjFCLGVBQWUsQ0FBQyxDQUFDO1FBQ3RDLE1BQUEyQixrQkFBQSxHQUNFLENBQUNELFlBQVksQ0FBQUUsY0FDTyxJQURuQm5CLGtCQUVzQixJQUFyQkYsbUJBQW1CLENBQUMsQ0FHWSxJQUFoQ3NCLE9BQU8sQ0FBQUMsR0FBSSxDQUFBQyxvQkFDVSxJQU50QnJCLHFCQU9zRCxJQUFyRFIsV0FBVyxDQUFDMkIsT0FBTyxDQUFBQyxHQUFJLENBQUFFLDRCQUE2QixDQUNNLEtBUjVELENBUUMvQixpQkFBaUIsQ0FBQzRCLE9BQU8sQ0FBQUMsR0FBSSxDQUFBRSw0QkFBNkIsQ0FBQztRQUU5RCxJQUFJLENBQUNMLGtCQUFrQjtVQUFBO1FBQUE7UUFJdkJoQixtQkFBbUIsQ0FBQ3NCLElBQUE7VUFFbEIsSUFBSUEsSUFBSSxFQUFBUixHQUFLO1lBQUEsT0FDSlEsSUFBSTtVQUFBO1VBQ1osT0FDTTtZQUFBLEdBQ0ZBLElBQUk7WUFBQVIsR0FBQSxFQUNGO2NBQUFTLElBQUEsRUFDR1QsR0FBRyxDQUFBVSxHQUFJLENBQUFDLFVBQVcsQ0FBQyxLQUE0QixDQUFDLEdBQWhELFFBQWdELEdBQWhELFNBQWdEO2NBQUFELEdBQUEsRUFDakRWLEdBQUcsQ0FBQVUsR0FBSTtjQUFBRSxPQUFBLEVBQ0haLEdBQUcsQ0FBQWEsSUFBSztjQUFBQyxTQUFBLEVBQ05kLEdBQUcsQ0FBQWMsU0FBVTtjQUFBQyxtQkFBQSxFQUNIZixHQUFHLENBQUFlLG1CQUFvQjtjQUFBQyxLQUFBLEVBQ3JDLFNBQVMsSUFBSUM7WUFDdEI7VUFDRixDQUFDO1FBQUEsQ0FDRixDQUFDO01BQUEsQ0FDSDtNQUdJcEMsd0JBQXdCLENBQzNCa0IsTUFBTSxFQUNOZCxxQkFBcUIsRUFDckIsTUFBTU0sb0JBQW9CLENBQUMsSUFBSSxDQUFDLEVBQ2hDMkIsTUFBQSxJQUFVMUIsdUJBQXVCLENBQUMwQixNQUFNLENBQzFDLENBQUM7SUFBQSxDQUNGO0lBQUVwQixFQUFBLElBQ0RkLGtCQUFrQixFQUNsQkMscUJBQXFCLEVBQ3JCQyxtQkFBbUIsRUFDbkJLLG9CQUFvQixFQUNwQkMsdUJBQXVCLENBQ3hCO0lBQUFHLENBQUEsTUFBQVgsa0JBQUE7SUFBQVcsQ0FBQSxNQUFBVixxQkFBQTtJQUFBVSxDQUFBLE1BQUFULG1CQUFBO0lBQUFTLENBQUEsTUFBQUgsdUJBQUE7SUFBQUcsQ0FBQSxNQUFBSixvQkFBQTtJQUFBSSxDQUFBLE1BQUFFLEVBQUE7SUFBQUYsQ0FBQSxNQUFBRyxFQUFBO0VBQUE7SUFBQUQsRUFBQSxHQUFBRixDQUFBO0lBQUFHLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBdkREdEIsU0FBUyxDQUFDd0IsRUFpRFQsRUFBRUMsRUFNRixDQUFDO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/hooks/useIdeAtMentioned.ts b/hooks/useIdeAtMentioned.ts new file mode 100644 index 0000000..eb5977f --- /dev/null +++ b/hooks/useIdeAtMentioned.ts @@ -0,0 +1,76 @@ +import { useEffect, useRef } from 'react' +import { logError } from 'src/utils/log.js' +import { z } from 'zod/v4' +import type { + ConnectedMCPServer, + MCPServerConnection, +} from '../services/mcp/types.js' +import { getConnectedIdeClient } from '../utils/ide.js' +import { lazySchema } from '../utils/lazySchema.js' +export type IDEAtMentioned = { + filePath: string + lineStart?: number + lineEnd?: number +} + +const NOTIFICATION_METHOD = 'at_mentioned' + +const AtMentionedSchema = lazySchema(() => + z.object({ + method: z.literal(NOTIFICATION_METHOD), + params: z.object({ + filePath: z.string(), + lineStart: z.number().optional(), + lineEnd: z.number().optional(), + }), + }), +) + +/** + * A hook that tracks IDE at-mention notifications by directly registering + * with MCP client notification handlers, + */ +export function useIdeAtMentioned( + mcpClients: MCPServerConnection[], + onAtMentioned: (atMentioned: IDEAtMentioned) => void, +): void { + const ideClientRef = useRef(undefined) + + useEffect(() => { + // Find the IDE client from the MCP clients list + const ideClient = getConnectedIdeClient(mcpClients) + + if (ideClientRef.current !== ideClient) { + ideClientRef.current = ideClient + } + + // If we found a connected IDE client, register our handler + if (ideClient) { + ideClient.client.setNotificationHandler( + AtMentionedSchema(), + notification => { + if (ideClientRef.current !== ideClient) { + return + } + try { + const data = notification.params + // Adjust line numbers to be 1-based instead of 0-based + const lineStart = + data.lineStart !== undefined ? data.lineStart + 1 : undefined + const lineEnd = + data.lineEnd !== undefined ? data.lineEnd + 1 : undefined + onAtMentioned({ + filePath: data.filePath, + lineStart: lineStart, + lineEnd: lineEnd, + }) + } catch (error) { + logError(error as Error) + } + }, + ) + } + + // No cleanup needed as MCP clients manage their own lifecycle + }, [mcpClients, onAtMentioned]) +} diff --git a/hooks/useIdeConnectionStatus.ts b/hooks/useIdeConnectionStatus.ts new file mode 100644 index 0000000..418e3dc --- /dev/null +++ b/hooks/useIdeConnectionStatus.ts @@ -0,0 +1,33 @@ +import { useMemo } from 'react' +import type { MCPServerConnection } from '../services/mcp/types.js' + +export type IdeStatus = 'connected' | 'disconnected' | 'pending' | null + +type IdeConnectionResult = { + status: IdeStatus + ideName: string | null +} + +export function useIdeConnectionStatus( + mcpClients?: MCPServerConnection[], +): IdeConnectionResult { + return useMemo(() => { + const ideClient = mcpClients?.find(client => client.name === 'ide') + if (!ideClient) { + return { status: null, ideName: null } + } + // Extract IDE name from config if available + const config = ideClient.config + const ideName = + config.type === 'sse-ide' || config.type === 'ws-ide' + ? config.ideName + : null + if (ideClient.type === 'connected') { + return { status: 'connected', ideName } + } + if (ideClient.type === 'pending') { + return { status: 'pending', ideName } + } + return { status: 'disconnected', ideName } + }, [mcpClients]) +} diff --git a/hooks/useIdeLogging.ts b/hooks/useIdeLogging.ts new file mode 100644 index 0000000..e73c230 --- /dev/null +++ b/hooks/useIdeLogging.ts @@ -0,0 +1,41 @@ +import { useEffect } from 'react' +import { logEvent } from 'src/services/analytics/index.js' +import { z } from 'zod/v4' +import type { MCPServerConnection } from '../services/mcp/types.js' +import { getConnectedIdeClient } from '../utils/ide.js' +import { lazySchema } from '../utils/lazySchema.js' + +const LogEventSchema = lazySchema(() => + z.object({ + method: z.literal('log_event'), + params: z.object({ + eventName: z.string(), + eventData: z.object({}).passthrough(), + }), + }), +) + +export function useIdeLogging(mcpClients: MCPServerConnection[]): void { + useEffect(() => { + // Skip if there are no clients + if (!mcpClients.length) { + return + } + + // Find the IDE client from the MCP clients list + const ideClient = getConnectedIdeClient(mcpClients) + if (ideClient) { + // Register the log event handler + ideClient.client.setNotificationHandler( + LogEventSchema(), + notification => { + const { eventName, eventData } = notification.params + logEvent( + `tengu_ide_${eventName}`, + eventData as { [key: string]: boolean | number | undefined }, + ) + }, + ) + } + }, [mcpClients]) +} diff --git a/hooks/useIdeSelection.ts b/hooks/useIdeSelection.ts new file mode 100644 index 0000000..9fb2f46 --- /dev/null +++ b/hooks/useIdeSelection.ts @@ -0,0 +1,150 @@ +import { useEffect, useRef } from 'react' +import { logError } from 'src/utils/log.js' +import { z } from 'zod/v4' +import type { + ConnectedMCPServer, + MCPServerConnection, +} from '../services/mcp/types.js' +import { getConnectedIdeClient } from '../utils/ide.js' +import { lazySchema } from '../utils/lazySchema.js' +export type SelectionPoint = { + line: number + character: number +} + +export type SelectionData = { + selection: { + start: SelectionPoint + end: SelectionPoint + } | null + text?: string + filePath?: string +} + +export type IDESelection = { + lineCount: number + lineStart?: number + text?: string + filePath?: string +} + +// Define the selection changed notification schema +const SelectionChangedSchema = lazySchema(() => + z.object({ + method: z.literal('selection_changed'), + params: z.object({ + selection: z + .object({ + start: z.object({ + line: z.number(), + character: z.number(), + }), + end: z.object({ + line: z.number(), + character: z.number(), + }), + }) + .nullable() + .optional(), + text: z.string().optional(), + filePath: z.string().optional(), + }), + }), +) + +/** + * A hook that tracks IDE text selection information by directly registering + * with MCP client notification handlers + */ +export function useIdeSelection( + mcpClients: MCPServerConnection[], + onSelect: (selection: IDESelection) => void, +): void { + const handlersRegistered = useRef(false) + const currentIDERef = useRef(null) + + useEffect(() => { + // Find the IDE client from the MCP clients list + const ideClient = getConnectedIdeClient(mcpClients) + + // If the IDE client changed, we need to re-register handlers. + // Normalize undefined to null so the initial ref value (null) matches + // "no IDE found" (undefined), avoiding spurious resets on every MCP update. + if (currentIDERef.current !== (ideClient ?? null)) { + handlersRegistered.current = false + currentIDERef.current = ideClient || null + // Reset the selection when the IDE client changes. + onSelect({ + lineCount: 0, + lineStart: undefined, + text: undefined, + filePath: undefined, + }) + } + + // Skip if we've already registered handlers for the current IDE or if there's no IDE client + if (handlersRegistered.current || !ideClient) { + return + } + + // Handler function for selection changes + const selectionChangeHandler = (data: SelectionData) => { + if (data.selection?.start && data.selection?.end) { + const { start, end } = data.selection + let lineCount = end.line - start.line + 1 + // If on the first character of the line, do not count the line + // as being selected. + if (end.character === 0) { + lineCount-- + } + const selection = { + lineCount, + lineStart: start.line, + text: data.text, + filePath: data.filePath, + } + + onSelect(selection) + } + } + + // Register notification handler for selection_changed events + ideClient.client.setNotificationHandler( + SelectionChangedSchema(), + notification => { + if (currentIDERef.current !== ideClient) { + return + } + + try { + // Get the selection data from the notification params + const selectionData = notification.params + + // Process selection data - validate it has required properties + if ( + selectionData.selection && + selectionData.selection.start && + selectionData.selection.end + ) { + // Handle selection changes + selectionChangeHandler(selectionData as SelectionData) + } else if (selectionData.text !== undefined) { + // Handle empty selection (when text is empty string) + selectionChangeHandler({ + selection: null, + text: selectionData.text, + filePath: selectionData.filePath, + }) + } + } catch (error) { + logError(error as Error) + } + }, + ) + + // Mark that we've registered handlers + handlersRegistered.current = true + + // No cleanup needed as MCP clients manage their own lifecycle + }, [mcpClients, onSelect]) +} diff --git a/hooks/useInboxPoller.ts b/hooks/useInboxPoller.ts new file mode 100644 index 0000000..361ba63 --- /dev/null +++ b/hooks/useInboxPoller.ts @@ -0,0 +1,969 @@ +import { randomUUID } from 'crypto' +import { useCallback, useEffect, useRef } from 'react' +import { useInterval } from 'usehooks-ts' +import type { ToolUseConfirm } from '../components/permissions/PermissionRequest.js' +import { TEAMMATE_MESSAGE_TAG } from '../constants/xml.js' +import { useTerminalNotification } from '../ink/useTerminalNotification.js' +import { sendNotification } from '../services/notifier.js' +import { + type AppState, + useAppState, + useAppStateStore, + useSetAppState, +} from '../state/AppState.js' +import { findToolByName } from '../Tool.js' +import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js' +import { getAllBaseTools } from '../tools.js' +import type { PermissionUpdate } from '../types/permissions.js' +import { logForDebugging } from '../utils/debug.js' +import { + findInProcessTeammateTaskId, + handlePlanApprovalResponse, +} from '../utils/inProcessTeammateHelpers.js' +import { createAssistantMessage } from '../utils/messages.js' +import { + permissionModeFromString, + toExternalPermissionMode, +} from '../utils/permissions/PermissionMode.js' +import { applyPermissionUpdate } from '../utils/permissions/PermissionUpdate.js' +import { jsonStringify } from '../utils/slowOperations.js' +import { isInsideTmux } from '../utils/swarm/backends/detection.js' +import { + ensureBackendsRegistered, + getBackendByType, +} from '../utils/swarm/backends/registry.js' +import type { PaneBackendType } from '../utils/swarm/backends/types.js' +import { TEAM_LEAD_NAME } from '../utils/swarm/constants.js' +import { getLeaderToolUseConfirmQueue } from '../utils/swarm/leaderPermissionBridge.js' +import { sendPermissionResponseViaMailbox } from '../utils/swarm/permissionSync.js' +import { + removeTeammateFromTeamFile, + setMemberMode, +} from '../utils/swarm/teamHelpers.js' +import { unassignTeammateTasks } from '../utils/tasks.js' +import { + getAgentName, + isPlanModeRequired, + isTeamLead, + isTeammate, +} from '../utils/teammate.js' +import { isInProcessTeammate } from '../utils/teammateContext.js' +import { + isModeSetRequest, + isPermissionRequest, + isPermissionResponse, + isPlanApprovalRequest, + isPlanApprovalResponse, + isSandboxPermissionRequest, + isSandboxPermissionResponse, + isShutdownApproved, + isShutdownRequest, + isTeamPermissionUpdate, + markMessagesAsRead, + readUnreadMessages, + type TeammateMessage, + writeToMailbox, +} from '../utils/teammateMailbox.js' +import { + hasPermissionCallback, + hasSandboxPermissionCallback, + processMailboxPermissionResponse, + processSandboxPermissionResponse, +} from './useSwarmPermissionPoller.js' + +/** + * Get the agent name to poll for messages. + * - In-process teammates return undefined (they use waitForNextPromptOrShutdown instead) + * - Process-based teammates use their CLAUDE_CODE_AGENT_NAME + * - Team leads use their name from teamContext.teammates + * - Standalone sessions return undefined + */ +function getAgentNameToPoll(appState: AppState): string | undefined { + // In-process teammates should NOT use useInboxPoller - they have their own + // polling mechanism via waitForNextPromptOrShutdown() in inProcessRunner.ts. + // Using useInboxPoller would cause message routing issues since in-process + // teammates share the same React context and AppState with the leader. + // + // Note: This can be called when the leader's REPL re-renders while an + // in-process teammate's AsyncLocalStorage context is active (due to shared + // setAppState). We return undefined to gracefully skip polling rather than + // throwing, since this is a normal occurrence during concurrent execution. + if (isInProcessTeammate()) { + return undefined + } + if (isTeammate()) { + return getAgentName() + } + // Team lead polls using their agent name (not ID) + if (isTeamLead(appState.teamContext)) { + const leadAgentId = appState.teamContext!.leadAgentId + // Look up the lead's name from teammates map + const leadName = appState.teamContext!.teammates[leadAgentId]?.name + return leadName || 'team-lead' + } + return undefined +} + +const INBOX_POLL_INTERVAL_MS = 1000 + +type Props = { + enabled: boolean + isLoading: boolean + focusedInputDialog: string | undefined + // Returns true if submission succeeded, false if rejected (e.g., query already running) + // Dead code elimination: parameter named onSubmitMessage to avoid "teammate" string in external builds + onSubmitMessage: (formatted: string) => boolean +} + +/** + * Polls the teammate inbox for new messages and submits them as turns. + * + * This hook: + * 1. Polls every 1s for unread messages (teammates or team leads) + * 2. When idle: submits messages immediately as a new turn + * 3. When busy: queues messages in AppState.inbox for UI display, delivers when turn ends + */ +export function useInboxPoller({ + enabled, + isLoading, + focusedInputDialog, + onSubmitMessage, +}: Props): void { + // Assign to original name for clarity within the function + const onSubmitTeammateMessage = onSubmitMessage + const store = useAppStateStore() + const setAppState = useSetAppState() + const inboxMessageCount = useAppState(s => s.inbox.messages.length) + const terminal = useTerminalNotification() + + const poll = useCallback(async () => { + if (!enabled) return + + // Use ref to avoid dependency on appState object (prevents infinite loop) + const currentAppState = store.getState() + const agentName = getAgentNameToPoll(currentAppState) + if (!agentName) return + + const unread = await readUnreadMessages( + agentName, + currentAppState.teamContext?.teamName, + ) + + if (unread.length === 0) return + + logForDebugging(`[InboxPoller] Found ${unread.length} unread message(s)`) + + // Check for plan approval responses and transition out of plan mode if approved + // Security: Only accept approval responses from the team lead + if (isTeammate() && isPlanModeRequired()) { + for (const msg of unread) { + const approvalResponse = isPlanApprovalResponse(msg.text) + // Verify the message is from the team lead to prevent teammates from forging approvals + if (approvalResponse && msg.from === 'team-lead') { + logForDebugging( + `[InboxPoller] Received plan approval response from team-lead: approved=${approvalResponse.approved}`, + ) + if (approvalResponse.approved) { + // Use leader's permission mode if provided, otherwise default + const targetMode = approvalResponse.permissionMode ?? 'default' + + // Transition out of plan mode + setAppState(prev => ({ + ...prev, + toolPermissionContext: applyPermissionUpdate( + prev.toolPermissionContext, + { + type: 'setMode', + mode: toExternalPermissionMode(targetMode), + destination: 'session', + }, + ), + })) + logForDebugging( + `[InboxPoller] Plan approved by team lead, exited plan mode to ${targetMode}`, + ) + } else { + logForDebugging( + `[InboxPoller] Plan rejected by team lead: ${approvalResponse.feedback || 'No feedback provided'}`, + ) + } + } else if (approvalResponse) { + logForDebugging( + `[InboxPoller] Ignoring plan approval response from non-team-lead: ${msg.from}`, + ) + } + } + } + + // Helper to mark messages as read in the inbox file. + // Called after messages are successfully delivered or reliably queued. + const markRead = () => { + void markMessagesAsRead(agentName, currentAppState.teamContext?.teamName) + } + + // Separate permission messages from regular teammate messages + const permissionRequests: TeammateMessage[] = [] + const permissionResponses: TeammateMessage[] = [] + const sandboxPermissionRequests: TeammateMessage[] = [] + const sandboxPermissionResponses: TeammateMessage[] = [] + const shutdownRequests: TeammateMessage[] = [] + const shutdownApprovals: TeammateMessage[] = [] + const teamPermissionUpdates: TeammateMessage[] = [] + const modeSetRequests: TeammateMessage[] = [] + const planApprovalRequests: TeammateMessage[] = [] + const regularMessages: TeammateMessage[] = [] + + for (const m of unread) { + const permReq = isPermissionRequest(m.text) + const permResp = isPermissionResponse(m.text) + const sandboxReq = isSandboxPermissionRequest(m.text) + const sandboxResp = isSandboxPermissionResponse(m.text) + const shutdownReq = isShutdownRequest(m.text) + const shutdownApproval = isShutdownApproved(m.text) + const teamPermUpdate = isTeamPermissionUpdate(m.text) + const modeSetReq = isModeSetRequest(m.text) + const planApprovalReq = isPlanApprovalRequest(m.text) + + if (permReq) { + permissionRequests.push(m) + } else if (permResp) { + permissionResponses.push(m) + } else if (sandboxReq) { + sandboxPermissionRequests.push(m) + } else if (sandboxResp) { + sandboxPermissionResponses.push(m) + } else if (shutdownReq) { + shutdownRequests.push(m) + } else if (shutdownApproval) { + shutdownApprovals.push(m) + } else if (teamPermUpdate) { + teamPermissionUpdates.push(m) + } else if (modeSetReq) { + modeSetRequests.push(m) + } else if (planApprovalReq) { + planApprovalRequests.push(m) + } else { + regularMessages.push(m) + } + } + + // Handle permission requests (leader side) - route to ToolUseConfirmQueue + if ( + permissionRequests.length > 0 && + isTeamLead(currentAppState.teamContext) + ) { + logForDebugging( + `[InboxPoller] Found ${permissionRequests.length} permission request(s)`, + ) + + const setToolUseConfirmQueue = getLeaderToolUseConfirmQueue() + const teamName = currentAppState.teamContext?.teamName + + for (const m of permissionRequests) { + const parsed = isPermissionRequest(m.text) + if (!parsed) continue + + if (setToolUseConfirmQueue) { + // Route through the standard ToolUseConfirmQueue so tmux workers + // get the same tool-specific UI (BashPermissionRequest, FileEditToolDiff, etc.) + // as in-process teammates. + const tool = findToolByName(getAllBaseTools(), parsed.tool_name) + if (!tool) { + logForDebugging( + `[InboxPoller] Unknown tool ${parsed.tool_name}, skipping permission request`, + ) + continue + } + + const entry: ToolUseConfirm = { + assistantMessage: createAssistantMessage({ content: '' }), + tool, + description: parsed.description, + input: parsed.input, + toolUseContext: {} as ToolUseConfirm['toolUseContext'], + toolUseID: parsed.tool_use_id, + permissionResult: { + behavior: 'ask', + message: parsed.description, + }, + permissionPromptStartTimeMs: Date.now(), + workerBadge: { + name: parsed.agent_id, + color: 'cyan', + }, + onUserInteraction() { + // No-op for tmux workers (no classifier auto-approval) + }, + onAbort() { + void sendPermissionResponseViaMailbox( + parsed.agent_id, + { decision: 'rejected', resolvedBy: 'leader' }, + parsed.request_id, + teamName, + ) + }, + onAllow( + updatedInput: Record, + permissionUpdates: PermissionUpdate[], + ) { + void sendPermissionResponseViaMailbox( + parsed.agent_id, + { + decision: 'approved', + resolvedBy: 'leader', + updatedInput, + permissionUpdates, + }, + parsed.request_id, + teamName, + ) + }, + onReject(feedback?: string) { + void sendPermissionResponseViaMailbox( + parsed.agent_id, + { + decision: 'rejected', + resolvedBy: 'leader', + feedback, + }, + parsed.request_id, + teamName, + ) + }, + async recheckPermission() { + // No-op for tmux workers — permission state is on the worker side + }, + } + + // Deduplicate: if markMessagesAsRead failed on a prior poll, + // the same message will be re-read — skip if already queued. + setToolUseConfirmQueue(queue => { + if (queue.some(q => q.toolUseID === parsed.tool_use_id)) { + return queue + } + return [...queue, entry] + }) + } else { + logForDebugging( + `[InboxPoller] ToolUseConfirmQueue unavailable, dropping permission request from ${parsed.agent_id}`, + ) + } + } + + // Send desktop notification for the first request + const firstParsed = isPermissionRequest(permissionRequests[0]?.text ?? '') + if (firstParsed && !isLoading && !focusedInputDialog) { + void sendNotification( + { + message: `${firstParsed.agent_id} needs permission for ${firstParsed.tool_name}`, + notificationType: 'worker_permission_prompt', + }, + terminal, + ) + } + } + + // Handle permission responses (worker side) - invoke registered callbacks + if (permissionResponses.length > 0 && isTeammate()) { + logForDebugging( + `[InboxPoller] Found ${permissionResponses.length} permission response(s)`, + ) + + for (const m of permissionResponses) { + const parsed = isPermissionResponse(m.text) + if (!parsed) continue + + if (hasPermissionCallback(parsed.request_id)) { + logForDebugging( + `[InboxPoller] Processing permission response for ${parsed.request_id}: ${parsed.subtype}`, + ) + + if (parsed.subtype === 'success') { + processMailboxPermissionResponse({ + requestId: parsed.request_id, + decision: 'approved', + updatedInput: parsed.response?.updated_input, + permissionUpdates: parsed.response?.permission_updates, + }) + } else { + processMailboxPermissionResponse({ + requestId: parsed.request_id, + decision: 'rejected', + feedback: parsed.error, + }) + } + } + } + } + + // Handle sandbox permission requests (leader side) - add to workerSandboxPermissions queue + if ( + sandboxPermissionRequests.length > 0 && + isTeamLead(currentAppState.teamContext) + ) { + logForDebugging( + `[InboxPoller] Found ${sandboxPermissionRequests.length} sandbox permission request(s)`, + ) + + const newSandboxRequests: Array<{ + requestId: string + workerId: string + workerName: string + workerColor?: string + host: string + createdAt: number + }> = [] + + for (const m of sandboxPermissionRequests) { + const parsed = isSandboxPermissionRequest(m.text) + if (!parsed) continue + + // Validate required nested fields to prevent crashes from malformed messages + if (!parsed.hostPattern?.host) { + logForDebugging( + `[InboxPoller] Invalid sandbox permission request: missing hostPattern.host`, + ) + continue + } + + newSandboxRequests.push({ + requestId: parsed.requestId, + workerId: parsed.workerId, + workerName: parsed.workerName, + workerColor: parsed.workerColor, + host: parsed.hostPattern.host, + createdAt: parsed.createdAt, + }) + } + + if (newSandboxRequests.length > 0) { + setAppState(prev => ({ + ...prev, + workerSandboxPermissions: { + ...prev.workerSandboxPermissions, + queue: [ + ...prev.workerSandboxPermissions.queue, + ...newSandboxRequests, + ], + }, + })) + + // Send desktop notification for the first new request + const firstRequest = newSandboxRequests[0] + if (firstRequest && !isLoading && !focusedInputDialog) { + void sendNotification( + { + message: `${firstRequest.workerName} needs network access to ${firstRequest.host}`, + notificationType: 'worker_permission_prompt', + }, + terminal, + ) + } + } + } + + // Handle sandbox permission responses (worker side) - invoke registered callbacks + if (sandboxPermissionResponses.length > 0 && isTeammate()) { + logForDebugging( + `[InboxPoller] Found ${sandboxPermissionResponses.length} sandbox permission response(s)`, + ) + + for (const m of sandboxPermissionResponses) { + const parsed = isSandboxPermissionResponse(m.text) + if (!parsed) continue + + // Check if we have a registered callback for this request + if (hasSandboxPermissionCallback(parsed.requestId)) { + logForDebugging( + `[InboxPoller] Processing sandbox permission response for ${parsed.requestId}: allow=${parsed.allow}`, + ) + + // Process the response using the exported function + processSandboxPermissionResponse({ + requestId: parsed.requestId, + host: parsed.host, + allow: parsed.allow, + }) + + // Clear the pending sandbox request indicator + setAppState(prev => ({ + ...prev, + pendingSandboxRequest: null, + })) + } + } + } + + // Handle team permission updates (teammate side) - apply permission to context + if (teamPermissionUpdates.length > 0 && isTeammate()) { + logForDebugging( + `[InboxPoller] Found ${teamPermissionUpdates.length} team permission update(s)`, + ) + + for (const m of teamPermissionUpdates) { + const parsed = isTeamPermissionUpdate(m.text) + if (!parsed) { + logForDebugging( + `[InboxPoller] Failed to parse team permission update: ${m.text.substring(0, 100)}`, + ) + continue + } + + // Validate required nested fields to prevent crashes from malformed messages + if ( + !parsed.permissionUpdate?.rules || + !parsed.permissionUpdate?.behavior + ) { + logForDebugging( + `[InboxPoller] Invalid team permission update: missing permissionUpdate.rules or permissionUpdate.behavior`, + ) + continue + } + + // Apply the permission update to the teammate's context + logForDebugging( + `[InboxPoller] Applying team permission update: ${parsed.toolName} allowed in ${parsed.directoryPath}`, + ) + logForDebugging( + `[InboxPoller] Permission update rules: ${jsonStringify(parsed.permissionUpdate.rules)}`, + ) + + setAppState(prev => { + const updated = applyPermissionUpdate(prev.toolPermissionContext, { + type: 'addRules', + rules: parsed.permissionUpdate.rules, + behavior: parsed.permissionUpdate.behavior, + destination: 'session', + }) + logForDebugging( + `[InboxPoller] Updated session allow rules: ${jsonStringify(updated.alwaysAllowRules.session)}`, + ) + return { + ...prev, + toolPermissionContext: updated, + } + }) + } + } + + // Handle mode set requests (teammate side) - team lead changing teammate's mode + if (modeSetRequests.length > 0 && isTeammate()) { + logForDebugging( + `[InboxPoller] Found ${modeSetRequests.length} mode set request(s)`, + ) + + for (const m of modeSetRequests) { + // Only accept mode changes from team-lead + if (m.from !== 'team-lead') { + logForDebugging( + `[InboxPoller] Ignoring mode set request from non-team-lead: ${m.from}`, + ) + continue + } + + const parsed = isModeSetRequest(m.text) + if (!parsed) { + logForDebugging( + `[InboxPoller] Failed to parse mode set request: ${m.text.substring(0, 100)}`, + ) + continue + } + + const targetMode = permissionModeFromString(parsed.mode) + logForDebugging( + `[InboxPoller] Applying mode change from team-lead: ${targetMode}`, + ) + + // Update local permission context + setAppState(prev => ({ + ...prev, + toolPermissionContext: applyPermissionUpdate( + prev.toolPermissionContext, + { + type: 'setMode', + mode: toExternalPermissionMode(targetMode), + destination: 'session', + }, + ), + })) + + // Update config.json so team lead can see the new mode + const teamName = currentAppState.teamContext?.teamName + const agentName = getAgentName() + if (teamName && agentName) { + setMemberMode(teamName, agentName, targetMode) + } + } + } + + // Handle plan approval requests (leader side) - auto-approve and write response to teammate inbox + if ( + planApprovalRequests.length > 0 && + isTeamLead(currentAppState.teamContext) + ) { + logForDebugging( + `[InboxPoller] Found ${planApprovalRequests.length} plan approval request(s), auto-approving`, + ) + + const teamName = currentAppState.teamContext?.teamName + const leaderExternalMode = toExternalPermissionMode( + currentAppState.toolPermissionContext.mode, + ) + const modeToInherit = + leaderExternalMode === 'plan' ? 'default' : leaderExternalMode + + for (const m of planApprovalRequests) { + const parsed = isPlanApprovalRequest(m.text) + if (!parsed) continue + + // Write approval response to teammate's inbox + const approvalResponse = { + type: 'plan_approval_response', + requestId: parsed.requestId, + approved: true, + timestamp: new Date().toISOString(), + permissionMode: modeToInherit, + } + + void writeToMailbox( + m.from, + { + from: TEAM_LEAD_NAME, + text: jsonStringify(approvalResponse), + timestamp: new Date().toISOString(), + }, + teamName, + ) + + // Update in-process teammate task state if applicable + const taskId = findInProcessTeammateTaskId(m.from, currentAppState) + if (taskId) { + handlePlanApprovalResponse( + taskId, + { + type: 'plan_approval_response', + requestId: parsed.requestId, + approved: true, + timestamp: new Date().toISOString(), + permissionMode: modeToInherit, + }, + setAppState, + ) + } + + logForDebugging( + `[InboxPoller] Auto-approved plan from ${m.from} (request ${parsed.requestId})`, + ) + + // Still pass through as a regular message so the model has context + // about what the teammate is doing, but the approval is already sent + regularMessages.push(m) + } + } + + // Handle shutdown requests (teammate side) - preserve JSON for UI rendering + if (shutdownRequests.length > 0 && isTeammate()) { + logForDebugging( + `[InboxPoller] Found ${shutdownRequests.length} shutdown request(s)`, + ) + + // Pass through shutdown requests - the UI component will render them nicely + // and the model will receive instructions via the tool prompt documentation + for (const m of shutdownRequests) { + regularMessages.push(m) + } + } + + // Handle shutdown approvals (leader side) - kill the teammate's pane + if ( + shutdownApprovals.length > 0 && + isTeamLead(currentAppState.teamContext) + ) { + logForDebugging( + `[InboxPoller] Found ${shutdownApprovals.length} shutdown approval(s)`, + ) + + for (const m of shutdownApprovals) { + const parsed = isShutdownApproved(m.text) + if (!parsed) continue + + // Kill the pane if we have the info (pane-based teammates) + if (parsed.paneId && parsed.backendType) { + void (async () => { + try { + // Ensure backend classes are imported (no subprocess probes) + await ensureBackendsRegistered() + const insideTmux = await isInsideTmux() + const backend = getBackendByType( + parsed.backendType as PaneBackendType, + ) + const success = await backend?.killPane( + parsed.paneId!, + !insideTmux, + ) + logForDebugging( + `[InboxPoller] Killed pane ${parsed.paneId} for ${parsed.from}: ${success}`, + ) + } catch (error) { + logForDebugging( + `[InboxPoller] Failed to kill pane for ${parsed.from}: ${error}`, + ) + } + })() + } + + // Remove the teammate from teamContext.teammates so the count is accurate + const teammateToRemove = parsed.from + if (teammateToRemove && currentAppState.teamContext?.teammates) { + // Find the teammate ID by name + const teammateId = Object.entries( + currentAppState.teamContext.teammates, + ).find(([, t]) => t.name === teammateToRemove)?.[0] + + if (teammateId) { + // Remove from team file (leader owns team file mutations) + const teamName = currentAppState.teamContext?.teamName + if (teamName) { + removeTeammateFromTeamFile(teamName, { + agentId: teammateId, + name: teammateToRemove, + }) + } + + // Unassign tasks and build notification message + const { notificationMessage } = teamName + ? await unassignTeammateTasks( + teamName, + teammateId, + teammateToRemove, + 'shutdown', + ) + : { notificationMessage: `${teammateToRemove} has shut down.` } + + setAppState(prev => { + if (!prev.teamContext?.teammates) return prev + if (!(teammateId in prev.teamContext.teammates)) return prev + const { [teammateId]: _, ...remainingTeammates } = + prev.teamContext.teammates + + // Mark the teammate's task as completed so hasRunningTeammates + // becomes false and the spinner stops. Without this, out-of-process + // (tmux) teammate tasks stay status:'running' forever because + // only in-process teammates have a runner that sets 'completed'. + const updatedTasks = { ...prev.tasks } + for (const [tid, task] of Object.entries(updatedTasks)) { + if ( + isInProcessTeammateTask(task) && + task.identity.agentId === teammateId + ) { + updatedTasks[tid] = { + ...task, + status: 'completed' as const, + endTime: Date.now(), + } + } + } + + return { + ...prev, + tasks: updatedTasks, + teamContext: { + ...prev.teamContext, + teammates: remainingTeammates, + }, + inbox: { + messages: [ + ...prev.inbox.messages, + { + id: randomUUID(), + from: 'system', + text: jsonStringify({ + type: 'teammate_terminated', + message: notificationMessage, + }), + timestamp: new Date().toISOString(), + status: 'pending' as const, + }, + ], + }, + } + }) + logForDebugging( + `[InboxPoller] Removed ${teammateToRemove} (${teammateId}) from teamContext`, + ) + } + } + + // Pass through for UI rendering - the component will render it nicely + regularMessages.push(m) + } + } + + // Process regular teammate messages (existing logic) + if (regularMessages.length === 0) { + // No regular messages, but we may have processed non-regular messages + // (permissions, shutdown requests, etc.) above — mark those as read. + markRead() + return + } + + // Format messages with XML wrapper for Claude (include color if available) + // Transform plan approval requests to include instructions for Claude + const formatted = regularMessages + .map(m => { + const colorAttr = m.color ? ` color="${m.color}"` : '' + const summaryAttr = m.summary ? ` summary="${m.summary}"` : '' + const messageContent = m.text + + return `<${TEAMMATE_MESSAGE_TAG} teammate_id="${m.from}"${colorAttr}${summaryAttr}>\n${messageContent}\n` + }) + .join('\n\n') + + // Helper to queue messages in AppState for later delivery + const queueMessages = () => { + setAppState(prev => ({ + ...prev, + inbox: { + messages: [ + ...prev.inbox.messages, + ...regularMessages.map(m => ({ + id: randomUUID(), + from: m.from, + text: m.text, + timestamp: m.timestamp, + status: 'pending' as const, + color: m.color, + summary: m.summary, + })), + ], + }, + })) + } + + if (!isLoading && !focusedInputDialog) { + // IDLE: Submit as new turn immediately + logForDebugging(`[InboxPoller] Session idle, submitting immediately`) + const submitted = onSubmitTeammateMessage(formatted) + if (!submitted) { + // Submission rejected (query already running), queue for later + logForDebugging( + `[InboxPoller] Submission rejected, queuing for later delivery`, + ) + queueMessages() + } + } else { + // BUSY: Add to inbox queue for UI display + later delivery + logForDebugging(`[InboxPoller] Session busy, queuing for later delivery`) + queueMessages() + } + + // Mark messages as read only after they have been successfully delivered + // or reliably queued in AppState. This prevents permanent message loss + // when the session is busy — if we crash before this point, the messages + // will be re-read on the next poll cycle instead of being silently dropped. + markRead() + }, [ + enabled, + isLoading, + focusedInputDialog, + onSubmitTeammateMessage, + setAppState, + terminal, + store, + ]) + + // When session becomes idle, deliver any pending messages and clean up processed ones + useEffect(() => { + if (!enabled) return + + // Skip if busy or in a dialog + if (isLoading || focusedInputDialog) { + return + } + + // Use ref to avoid dependency on appState object (prevents infinite loop) + const currentAppState = store.getState() + const agentName = getAgentNameToPoll(currentAppState) + if (!agentName) return + + const pendingMessages = currentAppState.inbox.messages.filter( + m => m.status === 'pending', + ) + const processedMessages = currentAppState.inbox.messages.filter( + m => m.status === 'processed', + ) + + // Clean up processed messages (they were already delivered mid-turn as attachments) + if (processedMessages.length > 0) { + logForDebugging( + `[InboxPoller] Cleaning up ${processedMessages.length} processed message(s) that were delivered mid-turn`, + ) + const processedIds = new Set(processedMessages.map(m => m.id)) + setAppState(prev => ({ + ...prev, + inbox: { + messages: prev.inbox.messages.filter(m => !processedIds.has(m.id)), + }, + })) + } + + // No pending messages to deliver + if (pendingMessages.length === 0) return + + logForDebugging( + `[InboxPoller] Session idle, delivering ${pendingMessages.length} pending message(s)`, + ) + + // Format messages with XML wrapper for Claude (include color if available) + const formatted = pendingMessages + .map(m => { + const colorAttr = m.color ? ` color="${m.color}"` : '' + const summaryAttr = m.summary ? ` summary="${m.summary}"` : '' + return `<${TEAMMATE_MESSAGE_TAG} teammate_id="${m.from}"${colorAttr}${summaryAttr}>\n${m.text}\n` + }) + .join('\n\n') + + // Try to submit - only clear messages if successful + const submitted = onSubmitTeammateMessage(formatted) + if (submitted) { + // Clear the specific messages we just submitted by their IDs + const submittedIds = new Set(pendingMessages.map(m => m.id)) + setAppState(prev => ({ + ...prev, + inbox: { + messages: prev.inbox.messages.filter(m => !submittedIds.has(m.id)), + }, + })) + } else { + logForDebugging( + `[InboxPoller] Submission rejected, keeping messages queued`, + ) + } + }, [ + enabled, + isLoading, + focusedInputDialog, + onSubmitTeammateMessage, + setAppState, + inboxMessageCount, + store, + ]) + + // Poll if running as a teammate or as a team lead + const shouldPoll = enabled && !!getAgentNameToPoll(store.getState()) + useInterval(() => void poll(), shouldPoll ? INBOX_POLL_INTERVAL_MS : null) + + // Initial poll on mount (only once) + const hasDoneInitialPollRef = useRef(false) + useEffect(() => { + if (!enabled) return + if (hasDoneInitialPollRef.current) return + // Use store.getState() to avoid dependency on appState object + if (getAgentNameToPoll(store.getState())) { + hasDoneInitialPollRef.current = true + void poll() + } + // Note: poll uses store.getState() (not appState) so it won't re-run on appState changes + // The ref guard is a safety measure to ensure initial poll only happens once + }, [enabled, poll, store]) +} diff --git a/hooks/useInputBuffer.ts b/hooks/useInputBuffer.ts new file mode 100644 index 0000000..8dc8161 --- /dev/null +++ b/hooks/useInputBuffer.ts @@ -0,0 +1,132 @@ +import { useCallback, useRef, useState } from 'react' +import type { PastedContent } from '../utils/config.js' + +export type BufferEntry = { + text: string + cursorOffset: number + pastedContents: Record + timestamp: number +} + +export type UseInputBufferProps = { + maxBufferSize: number + debounceMs: number +} + +export type UseInputBufferResult = { + pushToBuffer: ( + text: string, + cursorOffset: number, + pastedContents?: Record, + ) => void + undo: () => BufferEntry | undefined + canUndo: boolean + clearBuffer: () => void +} + +export function useInputBuffer({ + maxBufferSize, + debounceMs, +}: UseInputBufferProps): UseInputBufferResult { + const [buffer, setBuffer] = useState([]) + const [currentIndex, setCurrentIndex] = useState(-1) + const lastPushTime = useRef(0) + const pendingPush = useRef | null>(null) + + const pushToBuffer = useCallback( + ( + text: string, + cursorOffset: number, + pastedContents: Record = {}, + ) => { + const now = Date.now() + + // Clear any pending push + if (pendingPush.current) { + clearTimeout(pendingPush.current) + pendingPush.current = null + } + + // Debounce rapid changes + if (now - lastPushTime.current < debounceMs) { + pendingPush.current = setTimeout( + pushToBuffer, + debounceMs, + text, + cursorOffset, + pastedContents, + ) + return + } + + lastPushTime.current = now + + setBuffer(prevBuffer => { + // If we're not at the end of the buffer, truncate everything after current position + const newBuffer = + currentIndex >= 0 ? prevBuffer.slice(0, currentIndex + 1) : prevBuffer + + // Don't add if it's the same as the last entry + const lastEntry = newBuffer[newBuffer.length - 1] + if (lastEntry && lastEntry.text === text) { + return newBuffer + } + + // Add new entry + const updatedBuffer = [ + ...newBuffer, + { text, cursorOffset, pastedContents, timestamp: now }, + ] + + // Limit buffer size + if (updatedBuffer.length > maxBufferSize) { + return updatedBuffer.slice(-maxBufferSize) + } + + return updatedBuffer + }) + + // Update current index to point to the new entry + setCurrentIndex(prev => { + const newIndex = prev >= 0 ? prev + 1 : buffer.length + return Math.min(newIndex, maxBufferSize - 1) + }) + }, + [debounceMs, maxBufferSize, currentIndex, buffer.length], + ) + + const undo = useCallback((): BufferEntry | undefined => { + if (currentIndex < 0 || buffer.length === 0) { + return undefined + } + + const targetIndex = Math.max(0, currentIndex - 1) + const entry = buffer[targetIndex] + + if (entry) { + setCurrentIndex(targetIndex) + return entry + } + + return undefined + }, [buffer, currentIndex]) + + const clearBuffer = useCallback(() => { + setBuffer([]) + setCurrentIndex(-1) + lastPushTime.current = 0 + if (pendingPush.current) { + clearTimeout(pendingPush.current) + pendingPush.current = null + } + }, [lastPushTime, pendingPush]) + + const canUndo = currentIndex > 0 && buffer.length > 1 + + return { + pushToBuffer, + undo, + canUndo, + clearBuffer, + } +} diff --git a/hooks/useIssueFlagBanner.ts b/hooks/useIssueFlagBanner.ts new file mode 100644 index 0000000..adb3083 --- /dev/null +++ b/hooks/useIssueFlagBanner.ts @@ -0,0 +1,133 @@ +import { useMemo, useRef } from 'react' +import { BASH_TOOL_NAME } from '../tools/BashTool/toolName.js' +import type { Message } from '../types/message.js' +import { getUserMessageText } from '../utils/messages.js' + +const EXTERNAL_COMMAND_PATTERNS = [ + /\bcurl\b/, + /\bwget\b/, + /\bssh\b/, + /\bkubectl\b/, + /\bsrun\b/, + /\bdocker\b/, + /\bbq\b/, + /\bgsutil\b/, + /\bgcloud\b/, + /\baws\b/, + /\bgit\s+push\b/, + /\bgit\s+pull\b/, + /\bgit\s+fetch\b/, + /\bgh\s+(pr|issue)\b/, + /\bnc\b/, + /\bncat\b/, + /\btelnet\b/, + /\bftp\b/, +] + +const FRICTION_PATTERNS = [ + // "No," or "No!" at start — comma/exclamation implies correction tone + // (avoids "No problem", "No thanks", "No I think we should...") + /^no[,!]\s/i, + // Direct corrections about Claude's output + /\bthat'?s (wrong|incorrect|not (what|right|correct))\b/i, + /\bnot what I (asked|wanted|meant|said)\b/i, + // Referencing prior instructions Claude missed + /\bI (said|asked|wanted|told you|already said)\b/i, + // Questioning Claude's actions + /\bwhy did you\b/i, + /\byou should(n'?t| not)? have\b/i, + /\byou were supposed to\b/i, + // Explicit retry/revert of Claude's work + /\btry again\b/i, + /\b(undo|revert) (that|this|it|what you)\b/i, +] + +export function isSessionContainerCompatible(messages: Message[]): boolean { + for (const msg of messages) { + if (msg.type !== 'assistant') { + continue + } + const content = msg.message.content + if (!Array.isArray(content)) { + continue + } + for (const block of content) { + if (block.type !== 'tool_use' || !('name' in block)) { + continue + } + const toolName = block.name as string + if (toolName.startsWith('mcp__')) { + return false + } + if (toolName === BASH_TOOL_NAME) { + const input = (block as { input?: Record }).input + const command = (input?.command as string) || '' + if (EXTERNAL_COMMAND_PATTERNS.some(p => p.test(command))) { + return false + } + } + } + } + return true +} + +export function hasFrictionSignal(messages: Message[]): boolean { + for (let i = messages.length - 1; i >= 0; i--) { + const msg = messages[i]! + if (msg.type !== 'user') { + continue + } + const text = getUserMessageText(msg) + if (!text) { + continue + } + return FRICTION_PATTERNS.some(p => p.test(text)) + } + return false +} + +const MIN_SUBMIT_COUNT = 3 +const COOLDOWN_MS = 30 * 60 * 1000 + +export function useIssueFlagBanner( + messages: Message[], + submitCount: number, +): boolean { + if (process.env.USER_TYPE !== 'ant') { + return false + } + + // biome-ignore lint/correctness/useHookAtTopLevel: process.env.USER_TYPE is a compile-time constant + const lastTriggeredAtRef = useRef(0) + // biome-ignore lint/correctness/useHookAtTopLevel: process.env.USER_TYPE is a compile-time constant + const activeForSubmitRef = useRef(-1) + + // Memoize the O(messages) scans. This hook runs on every REPL render + // (including every keystroke), but messages is stable during typing. + // isSessionContainerCompatible walks all messages + regex-tests each + // bash command — by far the heaviest work here. + // biome-ignore lint/correctness/useHookAtTopLevel: process.env.USER_TYPE is a compile-time constant + const shouldTrigger = useMemo( + () => isSessionContainerCompatible(messages) && hasFrictionSignal(messages), + [messages], + ) + + // Keep showing the banner until the user submits another message + if (activeForSubmitRef.current === submitCount) { + return true + } + + if (Date.now() - lastTriggeredAtRef.current < COOLDOWN_MS) { + return false + } + if (submitCount < MIN_SUBMIT_COUNT) { + return false + } + if (!shouldTrigger) { + return false + } + + lastTriggeredAtRef.current = Date.now() + activeForSubmitRef.current = submitCount + return true +} diff --git a/hooks/useLogMessages.ts b/hooks/useLogMessages.ts new file mode 100644 index 0000000..c244c29 --- /dev/null +++ b/hooks/useLogMessages.ts @@ -0,0 +1,119 @@ +import type { UUID } from 'crypto' +import { useEffect, useRef } from 'react' +import { useAppState } from '../state/AppState.js' +import type { Message } from '../types/message.js' +import { isAgentSwarmsEnabled } from '../utils/agentSwarmsEnabled.js' +import { + cleanMessagesForLogging, + isChainParticipant, + recordTranscript, +} from '../utils/sessionStorage.js' + +/** + * Hook that logs messages to the transcript + * conversation ID that only changes when a new conversation is started. + * + * @param messages The current conversation messages + * @param ignore When true, messages will not be recorded to the transcript + */ +export function useLogMessages(messages: Message[], ignore: boolean = false) { + const teamContext = useAppState(s => s.teamContext) + + // messages is append-only between compactions, so track where we left off + // and only pass the new tail to recordTranscript. Avoids O(n) filter+scan + // on every setMessages (~20x/turn, so n=3000 was ~120k wasted iterations). + const lastRecordedLengthRef = useRef(0) + const lastParentUuidRef = useRef(undefined) + // First-uuid change = compaction or /clear rebuilt the array; length alone + // can't detect this since post-compact [CB,summary,...keep,new] may be longer. + const firstMessageUuidRef = useRef(undefined) + // Guard against stale async .then() overwriting a fresher sync update when + // an incremental render fires before the compaction .then() resolves. + const callSeqRef = useRef(0) + + useEffect(() => { + if (ignore) return + + const currentFirstUuid = messages[0]?.uuid as UUID | undefined + const prevLength = lastRecordedLengthRef.current + + // First-render: firstMessageUuidRef is undefined. Compaction: first uuid changes. + // Both are !isIncremental, but first-render sync-walk is safe (no messagesToKeep). + const wasFirstRender = firstMessageUuidRef.current === undefined + const isIncremental = + currentFirstUuid !== undefined && + !wasFirstRender && + currentFirstUuid === firstMessageUuidRef.current && + prevLength <= messages.length + // Same-head shrink: tombstone filter, rewind, snip, partial-compact. + // Distinguished from compaction (first uuid changes) because the tail + // is either an existing on-disk message or a fresh message that this + // same effect's recordTranscript(fullArray) will write — see sync-walk + // guard below. + const isSameHeadShrink = + currentFirstUuid !== undefined && + !wasFirstRender && + currentFirstUuid === firstMessageUuidRef.current && + prevLength > messages.length + + const startIndex = isIncremental ? prevLength : 0 + if (startIndex === messages.length) return + + // Full array on first call + after compaction: recordTranscript's own + // O(n) dedup loop handles messagesToKeep interleaving correctly there. + const slice = startIndex === 0 ? messages : messages.slice(startIndex) + const parentHint = isIncremental ? lastParentUuidRef.current : undefined + + // Fire and forget - we don't want to block the UI. + const seq = ++callSeqRef.current + void recordTranscript( + slice, + isAgentSwarmsEnabled() + ? { + teamName: teamContext?.teamName, + agentName: teamContext?.selfAgentName, + } + : {}, + parentHint, + messages, + ).then(lastRecordedUuid => { + // For compaction/full array case (!isIncremental): use the async return + // value. After compaction, messagesToKeep in the array are skipped + // (already in transcript), so the sync loop would find a wrong UUID. + // Skip if a newer effect already ran (stale closure would overwrite the + // fresher sync update from the subsequent incremental render). + if (seq !== callSeqRef.current) return + if (lastRecordedUuid && !isIncremental) { + lastParentUuidRef.current = lastRecordedUuid + } + }) + + // Sync-walk safe for: incremental (pure new-tail slice), first-render + // (no messagesToKeep interleaving), and same-head shrink. Shrink is the + // subtle one: the picked uuid is either already on disk (tombstone/rewind + // — survivors were written before) or is being written by THIS effect's + // recordTranscript(fullArray) call (snip boundary / partial-compact tail + // — enqueueWrite ordering guarantees it lands before any later write that + // chains to it). Without this, the ref stays stale at a tombstoned uuid: + // the async .then() correction is raced out by the next effect's seq bump + // on large sessions where recordTranscript(fullArray) is slow. Only the + // compaction case (first uuid changed) remains unsafe — tail may be + // messagesToKeep whose last-actually-recorded uuid differs. + if (isIncremental || wasFirstRender || isSameHeadShrink) { + // Match EXACTLY what recordTranscript persists: cleanMessagesForLogging + // applies both the isLoggableMessage filter and (for external users) the + // REPL-strip + isVirtual-promote transform. Using the raw predicate here + // would pick a UUID that the transform drops, leaving the parent hint + // pointing at a message that never reached disk. Pass full messages as + // replId context — REPL tool_use and its tool_result land in separate + // render cycles, so the slice alone can't pair them. + const last = cleanMessagesForLogging(slice, messages).findLast( + isChainParticipant, + ) + if (last) lastParentUuidRef.current = last.uuid as UUID + } + + lastRecordedLengthRef.current = messages.length + firstMessageUuidRef.current = currentFirstUuid + }, [messages, ignore, teamContext?.teamName, teamContext?.selfAgentName]) +} diff --git a/hooks/useLspPluginRecommendation.tsx b/hooks/useLspPluginRecommendation.tsx new file mode 100644 index 0000000..7253b3d --- /dev/null +++ b/hooks/useLspPluginRecommendation.tsx @@ -0,0 +1,194 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * Hook for LSP plugin recommendations + * + * Detects file edits and recommends LSP plugins when: + * - File extension matches an LSP plugin + * - LSP binary is already installed on the system + * - Plugin is not already installed + * - User hasn't disabled recommendations + * + * Only shows one recommendation per session. + */ + +import { extname, join } from 'path'; +import * as React from 'react'; +import { hasShownLspRecommendationThisSession, setLspRecommendationShownThisSession } from '../bootstrap/state.js'; +import { useNotifications } from '../context/notifications.js'; +import { useAppState } from '../state/AppState.js'; +import { saveGlobalConfig } from '../utils/config.js'; +import { logForDebugging } from '../utils/debug.js'; +import { logError } from '../utils/log.js'; +import { addToNeverSuggest, getMatchingLspPlugins, incrementIgnoredCount } from '../utils/plugins/lspRecommendation.js'; +import { cacheAndRegisterPlugin } from '../utils/plugins/pluginInstallationHelpers.js'; +import { getSettingsForSource, updateSettingsForSource } from '../utils/settings/settings.js'; +import { installPluginAndNotify, usePluginRecommendationBase } from './usePluginRecommendationBase.js'; + +// Threshold for detecting timeout vs explicit dismiss (ms) +// Menu auto-dismisses at 30s, so anything over 28s is likely timeout +const TIMEOUT_THRESHOLD_MS = 28_000; +export type LspRecommendationState = { + pluginId: string; + pluginName: string; + pluginDescription?: string; + fileExtension: string; + shownAt: number; // Timestamp for timeout detection +} | null; +type UseLspPluginRecommendationResult = { + recommendation: LspRecommendationState; + handleResponse: (response: 'yes' | 'no' | 'never' | 'disable') => void; +}; +export function useLspPluginRecommendation() { + const $ = _c(12); + const trackedFiles = useAppState(_temp); + const { + addNotification + } = useNotifications(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = new Set(); + $[0] = t0; + } else { + t0 = $[0]; + } + const checkedFilesRef = React.useRef(t0); + const { + recommendation, + clearRecommendation, + tryResolve + } = usePluginRecommendationBase(); + let t1; + let t2; + if ($[1] !== trackedFiles || $[2] !== tryResolve) { + t1 = () => { + tryResolve(async () => { + if (hasShownLspRecommendationThisSession()) { + return null; + } + const newFiles = []; + for (const file of trackedFiles) { + if (!checkedFilesRef.current.has(file)) { + checkedFilesRef.current.add(file); + newFiles.push(file); + } + } + for (const filePath of newFiles) { + ; + try { + const matches = await getMatchingLspPlugins(filePath); + const match = matches[0]; + if (match) { + logForDebugging(`[useLspPluginRecommendation] Found match: ${match.pluginName} for ${filePath}`); + setLspRecommendationShownThisSession(true); + return { + pluginId: match.pluginId, + pluginName: match.pluginName, + pluginDescription: match.description, + fileExtension: extname(filePath), + shownAt: Date.now() + }; + } + } catch (t3) { + const error = t3; + logError(error); + } + } + return null; + }); + }; + t2 = [trackedFiles, tryResolve]; + $[1] = trackedFiles; + $[2] = tryResolve; + $[3] = t1; + $[4] = t2; + } else { + t1 = $[3]; + t2 = $[4]; + } + React.useEffect(t1, t2); + let t3; + if ($[5] !== addNotification || $[6] !== clearRecommendation || $[7] !== recommendation) { + t3 = response => { + if (!recommendation) { + return; + } + const { + pluginId, + pluginName, + shownAt + } = recommendation; + logForDebugging(`[useLspPluginRecommendation] User response: ${response} for ${pluginName}`); + bb60: switch (response) { + case "yes": + { + installPluginAndNotify(pluginId, pluginName, "lsp-plugin", addNotification, async pluginData => { + logForDebugging(`[useLspPluginRecommendation] Installing plugin: ${pluginId}`); + const localSourcePath = typeof pluginData.entry.source === "string" ? join(pluginData.marketplaceInstallLocation, pluginData.entry.source) : undefined; + await cacheAndRegisterPlugin(pluginId, pluginData.entry, "user", undefined, localSourcePath); + const settings = getSettingsForSource("userSettings"); + updateSettingsForSource("userSettings", { + enabledPlugins: { + ...settings?.enabledPlugins, + [pluginId]: true + } + }); + logForDebugging(`[useLspPluginRecommendation] Plugin installed: ${pluginId}`); + }); + break bb60; + } + case "no": + { + const elapsed = Date.now() - shownAt; + if (elapsed >= TIMEOUT_THRESHOLD_MS) { + logForDebugging(`[useLspPluginRecommendation] Timeout detected (${elapsed}ms), incrementing ignored count`); + incrementIgnoredCount(); + } + break bb60; + } + case "never": + { + addToNeverSuggest(pluginId); + break bb60; + } + case "disable": + { + saveGlobalConfig(_temp2); + } + } + clearRecommendation(); + }; + $[5] = addNotification; + $[6] = clearRecommendation; + $[7] = recommendation; + $[8] = t3; + } else { + t3 = $[8]; + } + const handleResponse = t3; + let t4; + if ($[9] !== handleResponse || $[10] !== recommendation) { + t4 = { + recommendation, + handleResponse + }; + $[9] = handleResponse; + $[10] = recommendation; + $[11] = t4; + } else { + t4 = $[11]; + } + return t4; +} +function _temp2(current) { + if (current.lspRecommendationDisabled) { + return current; + } + return { + ...current, + lspRecommendationDisabled: true + }; +} +function _temp(s) { + return s.fileHistory.trackedFiles; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJleHRuYW1lIiwiam9pbiIsIlJlYWN0IiwiaGFzU2hvd25Mc3BSZWNvbW1lbmRhdGlvblRoaXNTZXNzaW9uIiwic2V0THNwUmVjb21tZW5kYXRpb25TaG93blRoaXNTZXNzaW9uIiwidXNlTm90aWZpY2F0aW9ucyIsInVzZUFwcFN0YXRlIiwic2F2ZUdsb2JhbENvbmZpZyIsImxvZ0ZvckRlYnVnZ2luZyIsImxvZ0Vycm9yIiwiYWRkVG9OZXZlclN1Z2dlc3QiLCJnZXRNYXRjaGluZ0xzcFBsdWdpbnMiLCJpbmNyZW1lbnRJZ25vcmVkQ291bnQiLCJjYWNoZUFuZFJlZ2lzdGVyUGx1Z2luIiwiZ2V0U2V0dGluZ3NGb3JTb3VyY2UiLCJ1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSIsImluc3RhbGxQbHVnaW5BbmROb3RpZnkiLCJ1c2VQbHVnaW5SZWNvbW1lbmRhdGlvbkJhc2UiLCJUSU1FT1VUX1RIUkVTSE9MRF9NUyIsIkxzcFJlY29tbWVuZGF0aW9uU3RhdGUiLCJwbHVnaW5JZCIsInBsdWdpbk5hbWUiLCJwbHVnaW5EZXNjcmlwdGlvbiIsImZpbGVFeHRlbnNpb24iLCJzaG93bkF0IiwiVXNlTHNwUGx1Z2luUmVjb21tZW5kYXRpb25SZXN1bHQiLCJyZWNvbW1lbmRhdGlvbiIsImhhbmRsZVJlc3BvbnNlIiwicmVzcG9uc2UiLCJ1c2VMc3BQbHVnaW5SZWNvbW1lbmRhdGlvbiIsIiQiLCJfYyIsInRyYWNrZWRGaWxlcyIsIl90ZW1wIiwiYWRkTm90aWZpY2F0aW9uIiwidDAiLCJTeW1ib2wiLCJmb3IiLCJTZXQiLCJjaGVja2VkRmlsZXNSZWYiLCJ1c2VSZWYiLCJjbGVhclJlY29tbWVuZGF0aW9uIiwidHJ5UmVzb2x2ZSIsInQxIiwidDIiLCJuZXdGaWxlcyIsImZpbGUiLCJjdXJyZW50IiwiaGFzIiwiYWRkIiwicHVzaCIsImZpbGVQYXRoIiwibWF0Y2hlcyIsIm1hdGNoIiwiZGVzY3JpcHRpb24iLCJEYXRlIiwibm93IiwidDMiLCJlcnJvciIsInVzZUVmZmVjdCIsImJiNjAiLCJwbHVnaW5EYXRhIiwibG9jYWxTb3VyY2VQYXRoIiwiZW50cnkiLCJzb3VyY2UiLCJtYXJrZXRwbGFjZUluc3RhbGxMb2NhdGlvbiIsInVuZGVmaW5lZCIsInNldHRpbmdzIiwiZW5hYmxlZFBsdWdpbnMiLCJlbGFwc2VkIiwiX3RlbXAyIiwidDQiLCJsc3BSZWNvbW1lbmRhdGlvbkRpc2FibGVkIiwicyIsImZpbGVIaXN0b3J5Il0sInNvdXJjZXMiOlsidXNlTHNwUGx1Z2luUmVjb21tZW5kYXRpb24udHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogSG9vayBmb3IgTFNQIHBsdWdpbiByZWNvbW1lbmRhdGlvbnNcbiAqXG4gKiBEZXRlY3RzIGZpbGUgZWRpdHMgYW5kIHJlY29tbWVuZHMgTFNQIHBsdWdpbnMgd2hlbjpcbiAqIC0gRmlsZSBleHRlbnNpb24gbWF0Y2hlcyBhbiBMU1AgcGx1Z2luXG4gKiAtIExTUCBiaW5hcnkgaXMgYWxyZWFkeSBpbnN0YWxsZWQgb24gdGhlIHN5c3RlbVxuICogLSBQbHVnaW4gaXMgbm90IGFscmVhZHkgaW5zdGFsbGVkXG4gKiAtIFVzZXIgaGFzbid0IGRpc2FibGVkIHJlY29tbWVuZGF0aW9uc1xuICpcbiAqIE9ubHkgc2hvd3Mgb25lIHJlY29tbWVuZGF0aW9uIHBlciBzZXNzaW9uLlxuICovXG5cbmltcG9ydCB7IGV4dG5hbWUsIGpvaW4gfSBmcm9tICdwYXRoJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICBoYXNTaG93bkxzcFJlY29tbWVuZGF0aW9uVGhpc1Nlc3Npb24sXG4gIHNldExzcFJlY29tbWVuZGF0aW9uU2hvd25UaGlzU2Vzc2lvbixcbn0gZnJvbSAnLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJy4uL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlIH0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgeyBzYXZlR2xvYmFsQ29uZmlnIH0gZnJvbSAnLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgbG9nRm9yRGVidWdnaW5nIH0gZnJvbSAnLi4vdXRpbHMvZGVidWcuanMnXG5pbXBvcnQgeyBsb2dFcnJvciB9IGZyb20gJy4uL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7XG4gIGFkZFRvTmV2ZXJTdWdnZXN0LFxuICBnZXRNYXRjaGluZ0xzcFBsdWdpbnMsXG4gIGluY3JlbWVudElnbm9yZWRDb3VudCxcbn0gZnJvbSAnLi4vdXRpbHMvcGx1Z2lucy9sc3BSZWNvbW1lbmRhdGlvbi5qcydcbmltcG9ydCB7IGNhY2hlQW5kUmVnaXN0ZXJQbHVnaW4gfSBmcm9tICcuLi91dGlscy9wbHVnaW5zL3BsdWdpbkluc3RhbGxhdGlvbkhlbHBlcnMuanMnXG5pbXBvcnQge1xuICBnZXRTZXR0aW5nc0ZvclNvdXJjZSxcbiAgdXBkYXRlU2V0dGluZ3NGb3JTb3VyY2UsXG59IGZyb20gJy4uL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuaW1wb3J0IHtcbiAgaW5zdGFsbFBsdWdpbkFuZE5vdGlmeSxcbiAgdXNlUGx1Z2luUmVjb21tZW5kYXRpb25CYXNlLFxufSBmcm9tICcuL3VzZVBsdWdpblJlY29tbWVuZGF0aW9uQmFzZS5qcydcblxuLy8gVGhyZXNob2xkIGZvciBkZXRlY3RpbmcgdGltZW91dCB2cyBleHBsaWNpdCBkaXNtaXNzIChtcylcbi8vIE1lbnUgYXV0by1kaXNtaXNzZXMgYXQgMzBzLCBzbyBhbnl0aGluZyBvdmVyIDI4cyBpcyBsaWtlbHkgdGltZW91dFxuY29uc3QgVElNRU9VVF9USFJFU0hPTERfTVMgPSAyOF8wMDBcblxuZXhwb3J0IHR5cGUgTHNwUmVjb21tZW5kYXRpb25TdGF0ZSA9IHtcbiAgcGx1Z2luSWQ6IHN0cmluZ1xuICBwbHVnaW5OYW1lOiBzdHJpbmdcbiAgcGx1Z2luRGVzY3JpcHRpb24/OiBzdHJpbmdcbiAgZmlsZUV4dGVuc2lvbjogc3RyaW5nXG4gIHNob3duQXQ6IG51bWJlciAvLyBUaW1lc3RhbXAgZm9yIHRpbWVvdXQgZGV0ZWN0aW9uXG59IHwgbnVsbFxuXG50eXBlIFVzZUxzcFBsdWdpblJlY29tbWVuZGF0aW9uUmVzdWx0ID0ge1xuICByZWNvbW1lbmRhdGlvbjogTHNwUmVjb21tZW5kYXRpb25TdGF0ZVxuICBoYW5kbGVSZXNwb25zZTogKHJlc3BvbnNlOiAneWVzJyB8ICdubycgfCAnbmV2ZXInIHwgJ2Rpc2FibGUnKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VMc3BQbHVnaW5SZWNvbW1lbmRhdGlvbigpOiBVc2VMc3BQbHVnaW5SZWNvbW1lbmRhdGlvblJlc3VsdCB7XG4gIGNvbnN0IHRyYWNrZWRGaWxlcyA9IHVzZUFwcFN0YXRlKHMgPT4gcy5maWxlSGlzdG9yeS50cmFja2VkRmlsZXMpXG4gIGNvbnN0IHsgYWRkTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcbiAgY29uc3QgY2hlY2tlZEZpbGVzUmVmID0gUmVhY3QudXNlUmVmPFNldDxzdHJpbmc+PihuZXcgU2V0KCkpXG4gIGNvbnN0IHsgcmVjb21tZW5kYXRpb24sIGNsZWFyUmVjb21tZW5kYXRpb24sIHRyeVJlc29sdmUgfSA9XG4gICAgdXNlUGx1Z2luUmVjb21tZW5kYXRpb25CYXNlPE5vbk51bGxhYmxlPExzcFJlY29tbWVuZGF0aW9uU3RhdGU+PigpXG5cbiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHtcbiAgICB0cnlSZXNvbHZlKGFzeW5jICgpID0+IHtcbiAgICAgIGlmIChoYXNTaG93bkxzcFJlY29tbWVuZGF0aW9uVGhpc1Nlc3Npb24oKSkgcmV0dXJuIG51bGxcblxuICAgICAgY29uc3QgbmV3RmlsZXM6IHN0cmluZ1tdID0gW11cbiAgICAgIGZvciAoY29uc3QgZmlsZSBvZiB0cmFja2VkRmlsZXMpIHtcbiAgICAgICAgaWYgKCFjaGVja2VkRmlsZXNSZWYuY3VycmVudC5oYXMoZmlsZSkpIHtcbiAgICAgICAgICBjaGVja2VkRmlsZXNSZWYuY3VycmVudC5hZGQoZmlsZSlcbiAgICAgICAgICBuZXdGaWxlcy5wdXNoKGZpbGUpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgZm9yIChjb25zdCBmaWxlUGF0aCBvZiBuZXdGaWxlcykge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IG1hdGNoZXMgPSBhd2FpdCBnZXRNYXRjaGluZ0xzcFBsdWdpbnMoZmlsZVBhdGgpXG4gICAgICAgICAgY29uc3QgbWF0Y2ggPSBtYXRjaGVzWzBdIC8vIG9mZmljaWFsIHBsdWdpbnMgcHJpb3JpdGl6ZWRcbiAgICAgICAgICBpZiAobWF0Y2gpIHtcbiAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgICAgYFt1c2VMc3BQbHVnaW5SZWNvbW1lbmRhdGlvbl0gRm91bmQgbWF0Y2g6ICR7bWF0Y2gucGx1Z2luTmFtZX0gZm9yICR7ZmlsZVBhdGh9YCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHNldExzcFJlY29tbWVuZGF0aW9uU2hvd25UaGlzU2Vzc2lvbih0cnVlKVxuICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgcGx1Z2luSWQ6IG1hdGNoLnBsdWdpbklkLFxuICAgICAgICAgICAgICBwbHVnaW5OYW1lOiBtYXRjaC5wbHVnaW5OYW1lLFxuICAgICAgICAgICAgICBwbHVnaW5EZXNjcmlwdGlvbjogbWF0Y2guZGVzY3JpcHRpb24sXG4gICAgICAgICAgICAgIGZpbGVFeHRlbnNpb246IGV4dG5hbWUoZmlsZVBhdGgpLFxuICAgICAgICAgICAgICBzaG93bkF0OiBEYXRlLm5vdygpLFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBsb2dFcnJvcihlcnJvcilcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIG51bGxcbiAgICB9KVxuICB9LCBbdHJhY2tlZEZpbGVzLCB0cnlSZXNvbHZlXSlcblxuICBjb25zdCBoYW5kbGVSZXNwb25zZSA9IFJlYWN0LnVzZUNhbGxiYWNrKFxuICAgIChyZXNwb25zZTogJ3llcycgfCAnbm8nIHwgJ25ldmVyJyB8ICdkaXNhYmxlJykgPT4ge1xuICAgICAgaWYgKCFyZWNvbW1lbmRhdGlvbikgcmV0dXJuXG5cbiAgICAgIGNvbnN0IHsgcGx1Z2luSWQsIHBsdWdpbk5hbWUsIHNob3duQXQgfSA9IHJlY29tbWVuZGF0aW9uXG5cbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgYFt1c2VMc3BQbHVnaW5SZWNvbW1lbmRhdGlvbl0gVXNlciByZXNwb25zZTogJHtyZXNwb25zZX0gZm9yICR7cGx1Z2luTmFtZX1gLFxuICAgICAgKVxuXG4gICAgICBzd2l0Y2ggKHJlc3BvbnNlKSB7XG4gICAgICAgIGNhc2UgJ3llcyc6XG4gICAgICAgICAgdm9pZCBpbnN0YWxsUGx1Z2luQW5kTm90aWZ5KFxuICAgICAgICAgICAgcGx1Z2luSWQsXG4gICAgICAgICAgICBwbHVnaW5OYW1lLFxuICAgICAgICAgICAgJ2xzcC1wbHVnaW4nLFxuICAgICAgICAgICAgYWRkTm90aWZpY2F0aW9uLFxuICAgICAgICAgICAgYXN5bmMgcGx1Z2luRGF0YSA9PiB7XG4gICAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgICAgICBgW3VzZUxzcFBsdWdpblJlY29tbWVuZGF0aW9uXSBJbnN0YWxsaW5nIHBsdWdpbjogJHtwbHVnaW5JZH1gLFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIGNvbnN0IGxvY2FsU291cmNlUGF0aCA9XG4gICAgICAgICAgICAgICAgdHlwZW9mIHBsdWdpbkRhdGEuZW50cnkuc291cmNlID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgICAgICAgPyBqb2luKFxuICAgICAgICAgICAgICAgICAgICAgIHBsdWdpbkRhdGEubWFya2V0cGxhY2VJbnN0YWxsTG9jYXRpb24sXG4gICAgICAgICAgICAgICAgICAgICAgcGx1Z2luRGF0YS5lbnRyeS5zb3VyY2UsXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICAgIGF3YWl0IGNhY2hlQW5kUmVnaXN0ZXJQbHVnaW4oXG4gICAgICAgICAgICAgICAgcGx1Z2luSWQsXG4gICAgICAgICAgICAgICAgcGx1Z2luRGF0YS5lbnRyeSxcbiAgICAgICAgICAgICAgICAndXNlcicsXG4gICAgICAgICAgICAgICAgdW5kZWZpbmVkLCAvLyBwcm9qZWN0UGF0aCAtIG5vdCBuZWVkZWQgZm9yIHVzZXIgc2NvcGVcbiAgICAgICAgICAgICAgICBsb2NhbFNvdXJjZVBhdGgsXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgLy8gRW5hYmxlIGluIHVzZXIgc2V0dGluZ3Mgc28gaXQgbG9hZHMgb24gcmVzdGFydFxuICAgICAgICAgICAgICBjb25zdCBzZXR0aW5ncyA9IGdldFNldHRpbmdzRm9yU291cmNlKCd1c2VyU2V0dGluZ3MnKVxuICAgICAgICAgICAgICB1cGRhdGVTZXR0aW5nc0ZvclNvdXJjZSgndXNlclNldHRpbmdzJywge1xuICAgICAgICAgICAgICAgIGVuYWJsZWRQbHVnaW5zOiB7XG4gICAgICAgICAgICAgICAgICAuLi5zZXR0aW5ncz8uZW5hYmxlZFBsdWdpbnMsXG4gICAgICAgICAgICAgICAgICBbcGx1Z2luSWRdOiB0cnVlLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgICAgICBgW3VzZUxzcFBsdWdpblJlY29tbWVuZGF0aW9uXSBQbHVnaW4gaW5zdGFsbGVkOiAke3BsdWdpbklkfWAsXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgKVxuICAgICAgICAgIGJyZWFrXG5cbiAgICAgICAgY2FzZSAnbm8nOiB7XG4gICAgICAgICAgY29uc3QgZWxhcHNlZCA9IERhdGUubm93KCkgLSBzaG93bkF0XG4gICAgICAgICAgaWYgKGVsYXBzZWQgPj0gVElNRU9VVF9USFJFU0hPTERfTVMpIHtcbiAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgICAgYFt1c2VMc3BQbHVnaW5SZWNvbW1lbmRhdGlvbl0gVGltZW91dCBkZXRlY3RlZCAoJHtlbGFwc2VkfW1zKSwgaW5jcmVtZW50aW5nIGlnbm9yZWQgY291bnRgLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgaW5jcmVtZW50SWdub3JlZENvdW50KClcbiAgICAgICAgICB9XG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuXG4gICAgICAgIGNhc2UgJ25ldmVyJzpcbiAgICAgICAgICBhZGRUb05ldmVyU3VnZ2VzdChwbHVnaW5JZClcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGNhc2UgJ2Rpc2FibGUnOlxuICAgICAgICAgIHNhdmVHbG9iYWxDb25maWcoY3VycmVudCA9PiB7XG4gICAgICAgICAgICBpZiAoY3VycmVudC5sc3BSZWNvbW1lbmRhdGlvbkRpc2FibGVkKSByZXR1cm4gY3VycmVudFxuICAgICAgICAgICAgcmV0dXJuIHsgLi4uY3VycmVudCwgbHNwUmVjb21tZW5kYXRpb25EaXNhYmxlZDogdHJ1ZSB9XG4gICAgICAgICAgfSlcbiAgICAgICAgICBicmVha1xuICAgICAgfVxuXG4gICAgICBjbGVhclJlY29tbWVuZGF0aW9uKClcbiAgICB9LFxuICAgIFtyZWNvbW1lbmRhdGlvbiwgYWRkTm90aWZpY2F0aW9uLCBjbGVhclJlY29tbWVuZGF0aW9uXSxcbiAgKVxuXG4gIHJldHVybiB7IHJlY29tbWVuZGF0aW9uLCBoYW5kbGVSZXNwb25zZSB9XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLFNBQVNBLE9BQU8sRUFBRUMsSUFBSSxRQUFR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 \ No newline at end of file diff --git a/hooks/useMailboxBridge.ts b/hooks/useMailboxBridge.ts new file mode 100644 index 0000000..49825fc --- /dev/null +++ b/hooks/useMailboxBridge.ts @@ -0,0 +1,21 @@ +import { useCallback, useEffect, useMemo, useSyncExternalStore } from 'react' +import { useMailbox } from '../context/mailbox.js' + +type Props = { + isLoading: boolean + onSubmitMessage: (content: string) => boolean +} + +export function useMailboxBridge({ isLoading, onSubmitMessage }: Props): void { + const mailbox = useMailbox() + + const subscribe = useMemo(() => mailbox.subscribe.bind(mailbox), [mailbox]) + const getSnapshot = useCallback(() => mailbox.revision, [mailbox]) + const revision = useSyncExternalStore(subscribe, getSnapshot) + + useEffect(() => { + if (isLoading) return + const msg = mailbox.poll() + if (msg) onSubmitMessage(msg.content) + }, [isLoading, revision, mailbox, onSubmitMessage]) +} diff --git a/hooks/useMainLoopModel.ts b/hooks/useMainLoopModel.ts new file mode 100644 index 0000000..ceb5481 --- /dev/null +++ b/hooks/useMainLoopModel.ts @@ -0,0 +1,34 @@ +import { useEffect, useReducer } from 'react' +import { onGrowthBookRefresh } from '../services/analytics/growthbook.js' +import { useAppState } from '../state/AppState.js' +import { + getDefaultMainLoopModelSetting, + type ModelName, + parseUserSpecifiedModel, +} from '../utils/model/model.js' + +// The value of the selector is a full model name that can be used directly in +// API calls. Use this over getMainLoopModel() when the component needs to +// update upon a model config change. +export function useMainLoopModel(): ModelName { + const mainLoopModel = useAppState(s => s.mainLoopModel) + const mainLoopModelForSession = useAppState(s => s.mainLoopModelForSession) + + // parseUserSpecifiedModel reads tengu_ant_model_override via + // _CACHED_MAY_BE_STALE (in resolveAntModel). Until GB init completes, + // that's the stale disk cache; after, it's the in-memory remoteEval map. + // AppState doesn't change when GB init finishes, so we subscribe to the + // refresh signal and force a re-render to re-resolve with fresh values. + // Without this, the alias resolution is frozen until something else + // happens to re-render the component — the API would sample one model + // while /model (which also re-resolves) displays another. + const [, forceRerender] = useReducer(x => x + 1, 0) + useEffect(() => onGrowthBookRefresh(forceRerender), []) + + const model = parseUserSpecifiedModel( + mainLoopModelForSession ?? + mainLoopModel ?? + getDefaultMainLoopModelSetting(), + ) + return model +} diff --git a/hooks/useManagePlugins.ts b/hooks/useManagePlugins.ts new file mode 100644 index 0000000..7efe1d5 --- /dev/null +++ b/hooks/useManagePlugins.ts @@ -0,0 +1,304 @@ +import { useCallback, useEffect } from 'react' +import type { Command } from '../commands.js' +import { useNotifications } from '../context/notifications.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { reinitializeLspServerManager } from '../services/lsp/manager.js' +import { useAppState, useSetAppState } from '../state/AppState.js' +import type { AgentDefinition } from '../tools/AgentTool/loadAgentsDir.js' +import { count } from '../utils/array.js' +import { logForDebugging } from '../utils/debug.js' +import { logForDiagnosticsNoPII } from '../utils/diagLogs.js' +import { toError } from '../utils/errors.js' +import { logError } from '../utils/log.js' +import { loadPluginAgents } from '../utils/plugins/loadPluginAgents.js' +import { getPluginCommands } from '../utils/plugins/loadPluginCommands.js' +import { loadPluginHooks } from '../utils/plugins/loadPluginHooks.js' +import { loadPluginLspServers } from '../utils/plugins/lspPluginIntegration.js' +import { loadPluginMcpServers } from '../utils/plugins/mcpPluginIntegration.js' +import { detectAndUninstallDelistedPlugins } from '../utils/plugins/pluginBlocklist.js' +import { getFlaggedPlugins } from '../utils/plugins/pluginFlagging.js' +import { loadAllPlugins } from '../utils/plugins/pluginLoader.js' + +/** + * Hook to manage plugin state and synchronize with AppState. + * + * On mount: loads all plugins, runs delisting enforcement, surfaces flagged- + * plugin notifications, populates AppState.plugins. This is the initial + * Layer-3 load — subsequent refresh goes through /reload-plugins. + * + * On needsRefresh: shows a notification directing the user to /reload-plugins. + * Does NOT auto-refresh. All Layer-3 swap (commands, agents, hooks, MCP) + * goes through refreshActivePlugins() via /reload-plugins for one consistent + * mental model. See Outline: declarative-settings-hXHBMDIf4b PR 5c. + */ +export function useManagePlugins({ + enabled = true, +}: { + enabled?: boolean +} = {}) { + const setAppState = useSetAppState() + const needsRefresh = useAppState(s => s.plugins.needsRefresh) + const { addNotification } = useNotifications() + + // Initial plugin load. Runs once on mount. NOT used for refresh — all + // post-mount refresh goes through /reload-plugins → refreshActivePlugins(). + // Unlike refreshActivePlugins, this also runs delisting enforcement and + // flagged-plugin notifications (session-start concerns), and does NOT bump + // mcp.pluginReconnectKey (MCP effects fire on their own mount). + const initialPluginLoad = useCallback(async () => { + try { + // Load all plugins - capture errors array + const { enabled, disabled, errors } = await loadAllPlugins() + + // Detect delisted plugins, auto-uninstall them, and record as flagged. + await detectAndUninstallDelistedPlugins() + + // Notify if there are flagged plugins pending dismissal + const flagged = getFlaggedPlugins() + if (Object.keys(flagged).length > 0) { + addNotification({ + key: 'plugin-delisted-flagged', + text: 'Plugins flagged. Check /plugins', + color: 'warning', + priority: 'high', + }) + } + + // Load commands, agents, and hooks with individual error handling + // Errors are added to the errors array for user visibility in Doctor UI + let commands: Command[] = [] + let agents: AgentDefinition[] = [] + + try { + commands = await getPluginCommands() + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error) + errors.push({ + type: 'generic-error', + source: 'plugin-commands', + error: `Failed to load plugin commands: ${errorMessage}`, + }) + } + + try { + agents = await loadPluginAgents() + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error) + errors.push({ + type: 'generic-error', + source: 'plugin-agents', + error: `Failed to load plugin agents: ${errorMessage}`, + }) + } + + try { + await loadPluginHooks() + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error) + errors.push({ + type: 'generic-error', + source: 'plugin-hooks', + error: `Failed to load plugin hooks: ${errorMessage}`, + }) + } + + // Load MCP server configs per plugin to get an accurate count. + // LoadedPlugin.mcpServers is not populated by loadAllPlugins — it's a + // cache slot that extractMcpServersFromPlugins fills later, which races + // with this metric. Calling loadPluginMcpServers directly (as + // cli/handlers/plugins.ts does) gives the correct count and also + // warms the cache for the MCP connection manager. + // + // Runs BEFORE setAppState so any errors pushed by these loaders make it + // into AppState.plugins.errors (Doctor UI), not just telemetry. + const mcpServerCounts = await Promise.all( + enabled.map(async p => { + if (p.mcpServers) return Object.keys(p.mcpServers).length + const servers = await loadPluginMcpServers(p, errors) + if (servers) p.mcpServers = servers + return servers ? Object.keys(servers).length : 0 + }), + ) + const mcp_count = mcpServerCounts.reduce((sum, n) => sum + n, 0) + + // LSP: the primary fix for issue #15521 is in refresh.ts (via + // performBackgroundPluginInstallations → refreshActivePlugins, which + // clears caches first). This reinit is defensive — it reads the same + // memoized loadAllPlugins() result as the original init unless a cache + // invalidation happened between main.tsx:3203 and REPL mount (e.g. + // seed marketplace registration or policySettings hot-reload). + const lspServerCounts = await Promise.all( + enabled.map(async p => { + if (p.lspServers) return Object.keys(p.lspServers).length + const servers = await loadPluginLspServers(p, errors) + if (servers) p.lspServers = servers + return servers ? Object.keys(servers).length : 0 + }), + ) + const lsp_count = lspServerCounts.reduce((sum, n) => sum + n, 0) + reinitializeLspServerManager() + + // Update AppState - merge errors to preserve LSP errors + setAppState(prevState => { + // Keep existing LSP/non-plugin-loading errors (source 'lsp-manager' or 'plugin:*') + const existingLspErrors = prevState.plugins.errors.filter( + e => e.source === 'lsp-manager' || e.source.startsWith('plugin:'), + ) + // Deduplicate: remove existing LSP errors that are also in new errors + const newErrorKeys = new Set( + errors.map(e => + e.type === 'generic-error' + ? `generic-error:${e.source}:${e.error}` + : `${e.type}:${e.source}`, + ), + ) + const filteredExisting = existingLspErrors.filter(e => { + const key = + e.type === 'generic-error' + ? `generic-error:${e.source}:${e.error}` + : `${e.type}:${e.source}` + return !newErrorKeys.has(key) + }) + const mergedErrors = [...filteredExisting, ...errors] + + return { + ...prevState, + plugins: { + ...prevState.plugins, + enabled, + disabled, + commands, + errors: mergedErrors, + }, + } + }) + + logForDebugging( + `Loaded plugins - Enabled: ${enabled.length}, Disabled: ${disabled.length}, Commands: ${commands.length}, Agents: ${agents.length}, Errors: ${errors.length}`, + ) + + // Count component types across enabled plugins + const hook_count = enabled.reduce((sum, p) => { + if (!p.hooksConfig) return sum + return ( + sum + + Object.values(p.hooksConfig).reduce( + (s, matchers) => + s + (matchers?.reduce((h, m) => h + m.hooks.length, 0) ?? 0), + 0, + ) + ) + }, 0) + + return { + enabled_count: enabled.length, + disabled_count: disabled.length, + inline_count: count(enabled, p => p.source.endsWith('@inline')), + marketplace_count: count(enabled, p => !p.source.endsWith('@inline')), + error_count: errors.length, + skill_count: commands.length, + agent_count: agents.length, + hook_count, + mcp_count, + lsp_count, + // Ant-only: which plugins are enabled, to correlate with RSS/FPS. + // Kept separate from base metrics so it doesn't flow into + // logForDiagnosticsNoPII. + ant_enabled_names: + process.env.USER_TYPE === 'ant' && enabled.length > 0 + ? (enabled + .map(p => p.name) + .sort() + .join( + ',', + ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS) + : undefined, + } + } catch (error) { + // Only plugin loading errors should reach here - log for monitoring + const errorObj = toError(error) + logError(errorObj) + logForDebugging(`Error loading plugins: ${error}`) + // Set empty state on error, but preserve LSP errors and add the new error + setAppState(prevState => { + // Keep existing LSP/non-plugin-loading errors + const existingLspErrors = prevState.plugins.errors.filter( + e => e.source === 'lsp-manager' || e.source.startsWith('plugin:'), + ) + const newError = { + type: 'generic-error' as const, + source: 'plugin-system', + error: errorObj.message, + } + return { + ...prevState, + plugins: { + ...prevState.plugins, + enabled: [], + disabled: [], + commands: [], + errors: [...existingLspErrors, newError], + }, + } + }) + + return { + enabled_count: 0, + disabled_count: 0, + inline_count: 0, + marketplace_count: 0, + error_count: 1, + skill_count: 0, + agent_count: 0, + hook_count: 0, + mcp_count: 0, + lsp_count: 0, + load_failed: true, + ant_enabled_names: undefined, + } + } + }, [setAppState, addNotification]) + + // Load plugins on mount and emit telemetry + useEffect(() => { + if (!enabled) return + void initialPluginLoad().then(metrics => { + const { ant_enabled_names, ...baseMetrics } = metrics + const allMetrics = { + ...baseMetrics, + has_custom_plugin_cache_dir: !!process.env.CLAUDE_CODE_PLUGIN_CACHE_DIR, + } + logEvent('tengu_plugins_loaded', { + ...allMetrics, + ...(ant_enabled_names !== undefined && { + enabled_names: ant_enabled_names, + }), + }) + logForDiagnosticsNoPII('info', 'tengu_plugins_loaded', allMetrics) + }) + }, [initialPluginLoad, enabled]) + + // Plugin state changed on disk (background reconcile, /plugin menu, + // external settings edit). Show a notification; user runs /reload-plugins + // to apply. The previous auto-refresh here had a stale-cache bug (only + // cleared loadAllPlugins, downstream memoized loaders returned old data) + // and was incomplete (no MCP, no agentDefinitions). /reload-plugins + // handles all of that correctly via refreshActivePlugins(). + useEffect(() => { + if (!enabled || !needsRefresh) return + addNotification({ + key: 'plugin-reload-pending', + text: 'Plugins changed. Run /reload-plugins to activate.', + color: 'suggestion', + priority: 'low', + }) + // Do NOT auto-refresh. Do NOT reset needsRefresh — /reload-plugins + // consumes it via refreshActivePlugins(). + }, [enabled, needsRefresh, addNotification]) +} diff --git a/hooks/useMemoryUsage.ts b/hooks/useMemoryUsage.ts new file mode 100644 index 0000000..e6640e5 --- /dev/null +++ b/hooks/useMemoryUsage.ts @@ -0,0 +1,39 @@ +import { useState } from 'react' +import { useInterval } from 'usehooks-ts' + +export type MemoryUsageStatus = 'normal' | 'high' | 'critical' + +export type MemoryUsageInfo = { + heapUsed: number + status: MemoryUsageStatus +} + +const HIGH_MEMORY_THRESHOLD = 1.5 * 1024 * 1024 * 1024 // 1.5GB in bytes +const CRITICAL_MEMORY_THRESHOLD = 2.5 * 1024 * 1024 * 1024 // 2.5GB in bytes + +/** + * Hook to monitor Node.js process memory usage. + * Polls every 10 seconds; returns null while status is 'normal'. + */ +export function useMemoryUsage(): MemoryUsageInfo | null { + const [memoryUsage, setMemoryUsage] = useState(null) + + useInterval(() => { + const heapUsed = process.memoryUsage().heapUsed + const status: MemoryUsageStatus = + heapUsed >= CRITICAL_MEMORY_THRESHOLD + ? 'critical' + : heapUsed >= HIGH_MEMORY_THRESHOLD + ? 'high' + : 'normal' + setMemoryUsage(prev => { + // Bail when status is 'normal' — nothing is shown, so heapUsed is + // irrelevant and we avoid re-rendering the whole Notifications subtree + // every 10 seconds for the 99%+ of users who never reach 1.5GB. + if (status === 'normal') return prev === null ? prev : null + return { heapUsed, status } + }) + }, 10_000) + + return memoryUsage +} diff --git a/hooks/useMergedClients.ts b/hooks/useMergedClients.ts new file mode 100644 index 0000000..fa62783 --- /dev/null +++ b/hooks/useMergedClients.ts @@ -0,0 +1,23 @@ +import uniqBy from 'lodash-es/uniqBy.js' +import { useMemo } from 'react' +import type { MCPServerConnection } from '../services/mcp/types.js' + +export function mergeClients( + initialClients: MCPServerConnection[] | undefined, + mcpClients: readonly MCPServerConnection[] | undefined, +): MCPServerConnection[] { + if (initialClients && mcpClients && mcpClients.length > 0) { + return uniqBy([...initialClients, ...mcpClients], 'name') + } + return initialClients || [] +} + +export function useMergedClients( + initialClients: MCPServerConnection[] | undefined, + mcpClients: MCPServerConnection[] | undefined, +): MCPServerConnection[] { + return useMemo( + () => mergeClients(initialClients, mcpClients), + [initialClients, mcpClients], + ) +} diff --git a/hooks/useMergedCommands.ts b/hooks/useMergedCommands.ts new file mode 100644 index 0000000..37d83d4 --- /dev/null +++ b/hooks/useMergedCommands.ts @@ -0,0 +1,15 @@ +import uniqBy from 'lodash-es/uniqBy.js' +import { useMemo } from 'react' +import type { Command } from '../commands.js' + +export function useMergedCommands( + initialCommands: Command[], + mcpCommands: Command[], +): Command[] { + return useMemo(() => { + if (mcpCommands.length > 0) { + return uniqBy([...initialCommands, ...mcpCommands], 'name') + } + return initialCommands + }, [initialCommands, mcpCommands]) +} diff --git a/hooks/useMergedTools.ts b/hooks/useMergedTools.ts new file mode 100644 index 0000000..48b1dee --- /dev/null +++ b/hooks/useMergedTools.ts @@ -0,0 +1,44 @@ +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +import { useMemo } from 'react' +import type { Tools, ToolPermissionContext } from '../Tool.js' +import { assembleToolPool } from '../tools.js' +import { useAppState } from '../state/AppState.js' +import { mergeAndFilterTools } from '../utils/toolPool.js' + +/** + * React hook that assembles the full tool pool for the REPL. + * + * Uses assembleToolPool() (the shared pure function used by both REPL and runAgent) + * to combine built-in tools with MCP tools, applying deny rules and deduplication. + * Any extra initialTools are merged on top. + * + * @param initialTools - Extra tools to include (built-in + startup MCP from props). + * These are merged with the assembled pool and take precedence in deduplication. + * @param mcpTools - MCP tools discovered dynamically (from mcp state) + * @param toolPermissionContext - Permission context for filtering + */ +export function useMergedTools( + initialTools: Tools, + mcpTools: Tools, + toolPermissionContext: ToolPermissionContext, +): Tools { + let replBridgeEnabled = false + let replBridgeOutboundOnly = false + return useMemo(() => { + // assembleToolPool is the shared function that both REPL and runAgent use. + // It handles: getTools() + MCP deny-rule filtering + dedup + MCP CLI exclusion. + const assembled = assembleToolPool(toolPermissionContext, mcpTools) + + return mergeAndFilterTools( + initialTools, + assembled, + toolPermissionContext.mode, + ) + }, [ + initialTools, + mcpTools, + toolPermissionContext, + replBridgeEnabled, + replBridgeOutboundOnly, + ]) +} diff --git a/hooks/useMinDisplayTime.ts b/hooks/useMinDisplayTime.ts new file mode 100644 index 0000000..587b969 --- /dev/null +++ b/hooks/useMinDisplayTime.ts @@ -0,0 +1,35 @@ +import { useEffect, useRef, useState } from 'react' + +/** + * Throttles a value so each distinct value stays visible for at least `minMs`. + * Prevents fast-cycling progress text from flickering past before it's readable. + * + * Unlike debounce (wait for quiet) or throttle (limit rate), this guarantees + * each value gets its minimum screen time before being replaced. + */ +export function useMinDisplayTime(value: T, minMs: number): T { + const [displayed, setDisplayed] = useState(value) + const lastShownAtRef = useRef(0) + + useEffect(() => { + const elapsed = Date.now() - lastShownAtRef.current + if (elapsed >= minMs) { + lastShownAtRef.current = Date.now() + setDisplayed(value) + return + } + const timer = setTimeout( + (shownAtRef, setFn, v) => { + shownAtRef.current = Date.now() + setFn(v) + }, + minMs - elapsed, + lastShownAtRef, + setDisplayed, + value, + ) + return () => clearTimeout(timer) + }, [value, minMs]) + + return displayed +} diff --git a/hooks/useNotifyAfterTimeout.ts b/hooks/useNotifyAfterTimeout.ts new file mode 100644 index 0000000..8b0ce31 --- /dev/null +++ b/hooks/useNotifyAfterTimeout.ts @@ -0,0 +1,65 @@ +import { useEffect } from 'react' +import { + getLastInteractionTime, + updateLastInteractionTime, +} from '../bootstrap/state.js' +import { useTerminalNotification } from '../ink/useTerminalNotification.js' +import { sendNotification } from '../services/notifier.js' +// The time threshold in milliseconds for considering an interaction "recent" (6 seconds) +export const DEFAULT_INTERACTION_THRESHOLD_MS = 6000 + +function getTimeSinceLastInteraction(): number { + return Date.now() - getLastInteractionTime() +} + +function hasRecentInteraction(threshold: number): boolean { + return getTimeSinceLastInteraction() < threshold +} + +function shouldNotify(threshold: number): boolean { + return process.env.NODE_ENV !== 'test' && !hasRecentInteraction(threshold) +} + +// NOTE: User interaction tracking is now done in App.tsx's processKeysInBatch +// function, which calls updateLastInteractionTime() when any input is received. +// This avoids having a separate stdin 'data' listener that would compete with +// the main 'readable' listener and cause dropped input characters. + +/** + * Hook that manages desktop notifications after a timeout period. + * + * Shows a notification in two cases: + * 1. Immediately if the app has been idle for longer than the threshold + * 2. After the specified timeout if the user doesn't interact within that time + * + * @param message - The notification message to display + * @param timeout - The timeout in milliseconds (defaults to 6000ms) + */ +export function useNotifyAfterTimeout( + message: string, + notificationType: string, +): void { + const terminal = useTerminalNotification() + + // Reset interaction time when hook is called to make sure that requests + // that took a long time to complete don't pop up a notification right away. + // Must be immediate because useEffect runs after Ink's render cycle has + // already flushed; without it the timestamp stays stale and a premature + // notification fires if the user is idle (no subsequent renders to flush). + useEffect(() => { + updateLastInteractionTime(true) + }, []) + + useEffect(() => { + let hasNotified = false + const timer = setInterval(() => { + if (shouldNotify(DEFAULT_INTERACTION_THRESHOLD_MS) && !hasNotified) { + hasNotified = true + clearInterval(timer) + void sendNotification({ message, notificationType }, terminal) + } + }, DEFAULT_INTERACTION_THRESHOLD_MS) + + return () => clearInterval(timer) + }, [message, notificationType, terminal]) +} diff --git a/hooks/useOfficialMarketplaceNotification.tsx b/hooks/useOfficialMarketplaceNotification.tsx new file mode 100644 index 0000000..5c4d07a --- /dev/null +++ b/hooks/useOfficialMarketplaceNotification.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import type { Notification } from '../context/notifications.js'; +import { Text } from '../ink.js'; +import { logForDebugging } from '../utils/debug.js'; +import { checkAndInstallOfficialMarketplace } from '../utils/plugins/officialMarketplaceStartupCheck.js'; +import { useStartupNotification } from './notifs/useStartupNotification.js'; + +/** + * Hook that handles official marketplace auto-installation and shows + * notifications for success/failure in the bottom right of the REPL. + */ +export function useOfficialMarketplaceNotification() { + useStartupNotification(_temp); +} +async function _temp() { + const result = await checkAndInstallOfficialMarketplace(); + const notifs = []; + if (result.configSaveFailed) { + logForDebugging("Showing marketplace config save failure notification"); + notifs.push({ + key: "marketplace-config-save-failed", + jsx: Failed to save marketplace retry info · Check ~/.claude.json permissions, + priority: "immediate", + timeoutMs: 10000 + }); + } + if (result.installed) { + logForDebugging("Showing marketplace installation success notification"); + notifs.push({ + key: "marketplace-installed", + jsx: ✓ Anthropic marketplace installed · /plugin to see available plugins, + priority: "immediate", + timeoutMs: 7000 + }); + } else { + if (result.skipped && result.reason === "unknown") { + logForDebugging("Showing marketplace installation failure notification"); + notifs.push({ + key: "marketplace-install-failed", + jsx: Failed to install Anthropic marketplace · Will retry on next startup, + priority: "immediate", + timeoutMs: 8000 + }); + } + } + return notifs; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIk5vdGlmaWNhdGlvbiIsIlRleHQiLCJsb2dGb3JEZWJ1Z2dpbmciLCJjaGVja0FuZEluc3RhbGxPZmZpY2lhbE1hcmtldHBsYWNlIiwidXNlU3RhcnR1cE5vdGlmaWNhdGlvbiIsInVzZU9mZmljaWFsTWFya2V0cGxhY2VOb3RpZmljYXRpb24iLCJfdGVtcCIsInJlc3VsdCIsIm5vdGlmcyIsImNvbmZpZ1NhdmVGYWlsZWQiLCJwdXNoIiwia2V5IiwianN4IiwicHJpb3JpdHkiLCJ0aW1lb3V0TXMiLCJpbnN0YWxsZWQiLCJza2lwcGVkIiwicmVhc29uIl0sInNvdXJjZXMiOlsidXNlT2ZmaWNpYWxNYXJrZXRwbGFjZU5vdGlmaWNhdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IE5vdGlmaWNhdGlvbiB9IGZyb20gJy4uL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IGNoZWNrQW5kSW5zdGFsbE9mZmljaWFsTWFya2V0cGxhY2UgfSBmcm9tICcuLi91dGlscy9wbHVnaW5zL29mZmljaWFsTWFya2V0cGxhY2VTdGFydHVwQ2hlY2suanMnXG5pbXBvcnQgeyB1c2VTdGFydHVwTm90aWZpY2F0aW9uIH0gZnJvbSAnLi9ub3RpZnMvdXNlU3RhcnR1cE5vdGlmaWNhdGlvbi5qcydcblxuLyoqXG4gKiBIb29rIHRoYXQgaGFuZGxlcyBvZmZpY2lhbCBtYXJrZXRwbGFjZSBhdXRvLWluc3RhbGxhdGlvbiBhbmQgc2hvd3NcbiAqIG5vdGlmaWNhdGlvbnMgZm9yIHN1Y2Nlc3MvZmFpbHVyZSBpbiB0aGUgYm90dG9tIHJpZ2h0IG9mIHRoZSBSRVBMLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlT2ZmaWNpYWxNYXJrZXRwbGFjZU5vdGlmaWNhdGlvbigpOiB2b2lkIHtcbiAgdXNlU3RhcnR1cE5vdGlmaWNhdGlvbihhc3luYyAoKSA9PiB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgY2hlY2tBbmRJbnN0YWxsT2ZmaWNpYWxNYXJrZXRwbGFjZSgpXG4gICAgY29uc3Qgbm90aWZzOiBOb3RpZmljYXRpb25bXSA9IFtdXG5cbiAgICAvLyBDaGVjayBmb3IgY29uZmlnIHNhdmUgZmFpbHVyZSBmaXJzdCAtIHRoaXMgaXMgY3JpdGljYWxcbiAgICBpZiAocmVzdWx0LmNvbmZpZ1NhdmVGYWlsZWQpIHtcbiAgICAgIGxvZ0ZvckRlYnVnZ2luZygnU2hvd2luZyBtYXJrZXRwbGFjZSBjb25maWcgc2F2ZSBmYWlsdXJlIG5vdGlmaWNhdGlvbicpXG4gICAgICBub3RpZnMucHVzaCh7XG4gICAgICAgIGtleTogJ21hcmtldHBsYWNlLWNvbmZpZy1zYXZlLWZhaWxlZCcsXG4gICAgICAgIGpzeDogKFxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiZXJyb3JcIj5cbiAgICAgICAgICAgIEZhaWxlZCB0byBzYXZlIG1hcmtldHBsYWNlIHJldHJ5IGluZm8gwrcgQ2hlY2sgfi8uY2xhdWRlLmpzb25cbiAgICAgICAgICAgIHBlcm1pc3Npb25zXG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApLFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogMTAwMDAsXG4gICAgICB9KVxuICAgIH1cblxuICAgIGlmIChyZXN1bHQuaW5zdGFsbGVkKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoJ1Nob3dpbmcgbWFya2V0cGxhY2UgaW5zdGFsbGF0aW9uIHN1Y2Nlc3Mgbm90aWZpY2F0aW9uJylcbiAgICAgIG5vdGlmcy5wdXNoKHtcbiAgICAgICAga2V5OiAnbWFya2V0cGxhY2UtaW5zdGFsbGVkJyxcbiAgICAgICAganN4OiAoXG4gICAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+XG4gICAgICAgICAgICDinJMgQW50aHJvcGljIG1hcmtldHBsYWNlIGluc3RhbGxlZCDCtyAvcGx1Z2luIHRvIHNlZSBhdmFpbGFibGUgcGx1Z2luc1xuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKSxcbiAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICB0aW1lb3V0TXM6IDcwMDAsXG4gICAgICB9KVxuICAgIH0gZWxzZSBpZiAocmVzdWx0LnNraXBwZWQgJiYgcmVzdWx0LnJlYXNvbiA9PT0gJ3Vua25vd24nKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoJ1Nob3dpbmcgbWFya2V0cGxhY2UgaW5zdGFsbGF0aW9uIGZhaWx1cmUgbm90aWZpY2F0aW9uJylcbiAgICAgIG5vdGlmcy5wdXNoKHtcbiAgICAgICAga2V5OiAnbWFya2V0cGxhY2UtaW5zdGFsbC1mYWlsZWQnLFxuICAgICAgICBqc3g6IChcbiAgICAgICAgICA8VGV4dCBjb2xvcj1cIndhcm5pbmdcIj5cbiAgICAgICAgICAgIEZhaWxlZCB0byBpbnN0YWxsIEFudGhyb3BpYyBtYXJrZXRwbGFjZSDCtyBXaWxsIHJldHJ5IG9uIG5leHQgc3RhcnR1cFxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKSxcbiAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICB0aW1lb3V0TXM6IDgwMDAsXG4gICAgICB9KVxuICAgIH1cbiAgICAvLyBEb24ndCBzaG93IG5vdGlmaWNhdGlvbnMgZm9yOlxuICAgIC8vIC0gYWxyZWFkeV9pbnN0YWxsZWQgKHVzZXIgYWxyZWFkeSBoYXMgaXQpXG4gICAgLy8gLSBwb2xpY3lfYmxvY2tlZCAoZW50ZXJwcmlzZSBwb2xpY3ksIGRvbid0IG5hZylcbiAgICAvLyAtIGFscmVhZHlfYXR0ZW1wdGVkIChoYW5kbGVkIGJ5IHJldHJ5IGxvZ2ljIG5vdylcbiAgICAvLyAtIGdpdF91bmF2YWlsYWJsZSAobWFya2V0cGxhY2UgaXMgYSBuaWNlLXRvLWhhdmU7IGlmIGdpdCBpcyBtaXNzaW5nXG4gICAgLy8gICBvciBpcyBhIG5vbi1mdW5jdGlvbmFsIG1hY09TIHhjcnVuIHNoaW0sIHJldHJ5IHNpbGVudGx5IG9uIGJhY2tvZmZcbiAgICAvLyAgIHJhdGhlciB0aGFuIG5hZ2dpbmcg4oCUIHRoZSB1c2VyIHdpbGwgc29ydCBnaXQgb3V0IGZvciBvdGhlciByZWFzb25zKVxuICAgIHJldHVybiBub3RpZnNcbiAgfSlcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixjQUFjQyxZQUFZLFFBQVEsNkJBQTZCO0FBQy9ELFNBQVNDLElBQUksUUFBUSxXQUFXO0FBQ2hDLFNBQVNDLGVBQWUsUUFBUSxtQkFBbUI7QUFDbkQsU0FBU0Msa0NBQWtDLFFBQVEscURBQXFEO0FBQ3hHLFNBQVNDLHNCQUFzQixRQUFRLG9DQUFvQzs7QUFFM0U7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFDLG1DQUFBO0VBQ0xELHNCQUFzQixDQUFDRSxLQXFEdEIsQ0FBQztBQUFBO0FBdERHLGVBQUFBLE1BQUE7RUFFSCxNQUFBQyxNQUFBLEdBQWUsTUFBTUosa0NBQWtDLENBQUMsQ0FBQztFQUN6RCxNQUFBSyxNQUFBLEdBQStCLEVBQUU7RUFHakMsSUFBSUQsTUFBTSxDQUFBRSxnQkFBaUI7SUFDekJQLGVBQWUsQ0FBQyxzREFBc0QsQ0FBQztJQUN2RU0sTUFBTSxDQUFBRSxJQUFLLENBQUM7TUFBQUMsR0FBQSxFQUNMLGdDQUFnQztNQUFBQyxHQUFBLEVBRW5DLENBQUMsSUFBSSxDQUFPLEtBQU8sQ0FBUCxPQUFPLENBQUMsd0VBR3BCLEVBSEMsSUFBSSxDQUdFO01BQUFDLFFBQUEsRUFFQyxXQUFXO01BQUFDLFNBQUEsRUFDVjtJQUNiLENBQUMsQ0FBQztFQUFBO0VBR0osSUFBSVAsTUFBTSxDQUFBUSxTQUFVO0lBQ2xCYixlQUFlLENBQUMsdURBQXVELENBQUM7SUFDeEVNLE1BQU0sQ0FBQUUsSUFBSyxDQUFDO01BQUFDLEdBQUEsRUFDTCx1QkFBdUI7TUFBQUMsR0FBQSxFQUUxQixDQUFDLElBQUksQ0FBTyxLQUFTLENBQVQsU0FBUyxDQUFDLG9FQUV0QixFQUZDLElBQUksQ0FFRTtNQUFBQyxRQUFBLEVBRUMsV0FBVztNQUFBQyxTQUFBLEVBQ1Y7SUFDYixDQUFDLENBQUM7RUFBQTtJQUNHLElBQUlQLE1BQU0sQ0FBQVMsT0FBdUMsSUFBM0JULE1BQU0sQ0FBQVUsTUFBTyxLQUFLLFNBQVM7TUFDdERmLGVBQWUsQ0FBQyx1REFBdUQsQ0FBQztNQUN4RU0sTUFBTSxDQUFBRSxJQUFLLENBQUM7UUFBQUMsR0FBQSxFQUNMLDRCQUE0QjtRQUFBQyxHQUFBLEVBRS9CLENBQUMsSUFBSSxDQUFPLEtBQVMsQ0FBVCxTQUFTLENBQUMsb0VBRXRCLEVBRkMsSUFBSSxDQUVFO1FBQUFDLFFBQUEsRUFFQyxXQUFXO1FBQUFDLFNBQUEsRUFDVjtNQUNiLENBQUMsQ0FBQztJQUFBO0VBQ0g7RUFBQSxPQVFNTixNQUFNO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/hooks/usePasteHandler.ts b/hooks/usePasteHandler.ts new file mode 100644 index 0000000..d6257b9 --- /dev/null +++ b/hooks/usePasteHandler.ts @@ -0,0 +1,285 @@ +import { basename } from 'path' +import React from 'react' +import { logError } from 'src/utils/log.js' +import { useDebounceCallback } from 'usehooks-ts' +import type { InputEvent, Key } from '../ink.js' +import { + getImageFromClipboard, + isImageFilePath, + PASTE_THRESHOLD, + tryReadImageFromPath, +} from '../utils/imagePaste.js' +import type { ImageDimensions } from '../utils/imageResizer.js' +import { getPlatform } from '../utils/platform.js' + +const CLIPBOARD_CHECK_DEBOUNCE_MS = 50 +const PASTE_COMPLETION_TIMEOUT_MS = 100 + +type PasteHandlerProps = { + onPaste?: (text: string) => void + onInput: (input: string, key: Key) => void + onImagePaste?: ( + base64Image: string, + mediaType?: string, + filename?: string, + dimensions?: ImageDimensions, + sourcePath?: string, + ) => void +} + +export function usePasteHandler({ + onPaste, + onInput, + onImagePaste, +}: PasteHandlerProps): { + wrappedOnInput: (input: string, key: Key, event: InputEvent) => void + pasteState: { + chunks: string[] + timeoutId: ReturnType | null + } + isPasting: boolean +} { + const [pasteState, setPasteState] = React.useState<{ + chunks: string[] + timeoutId: ReturnType | null + }>({ chunks: [], timeoutId: null }) + const [isPasting, setIsPasting] = React.useState(false) + const isMountedRef = React.useRef(true) + // Mirrors pasteState.timeoutId but updated synchronously. When paste + a + // keystroke arrive in the same stdin chunk, both wrappedOnInput calls run + // in the same discreteUpdates batch before React commits — the second call + // reads stale pasteState.timeoutId (null) and takes the onInput path. If + // that key is Enter, it submits the old input and the paste is lost. + const pastePendingRef = React.useRef(false) + + const isMacOS = React.useMemo(() => getPlatform() === 'macos', []) + + React.useEffect(() => { + return () => { + isMountedRef.current = false + } + }, []) + + const checkClipboardForImageImpl = React.useCallback(() => { + if (!onImagePaste || !isMountedRef.current) return + + void getImageFromClipboard() + .then(imageData => { + if (imageData && isMountedRef.current) { + onImagePaste( + imageData.base64, + imageData.mediaType, + undefined, // no filename for clipboard images + imageData.dimensions, + ) + } + }) + .catch(error => { + if (isMountedRef.current) { + logError(error as Error) + } + }) + .finally(() => { + if (isMountedRef.current) { + setIsPasting(false) + } + }) + }, [onImagePaste]) + + const checkClipboardForImage = useDebounceCallback( + checkClipboardForImageImpl, + CLIPBOARD_CHECK_DEBOUNCE_MS, + ) + + const resetPasteTimeout = React.useCallback( + (currentTimeoutId: ReturnType | null) => { + if (currentTimeoutId) { + clearTimeout(currentTimeoutId) + } + return setTimeout( + ( + setPasteState, + onImagePaste, + onPaste, + setIsPasting, + checkClipboardForImage, + isMacOS, + pastePendingRef, + ) => { + pastePendingRef.current = false + setPasteState(({ chunks }) => { + // Join chunks and filter out orphaned focus sequences + // These can appear when focus events split during paste + const pastedText = chunks + .join('') + .replace(/\[I$/, '') + .replace(/\[O$/, '') + + // Check if the pasted text contains image file paths + // When dragging multiple images, they may come as: + // 1. Newline-separated paths (common in some terminals) + // 2. Space-separated paths (common when dragging from Finder) + // For space-separated paths, we split on spaces that precede absolute paths: + // - Unix: space followed by `/` (e.g., `/Users/...`) + // - Windows: space followed by drive letter and `:\` (e.g., `C:\Users\...`) + // This works because spaces within paths are escaped (e.g., `file\ name.png`) + const lines = pastedText + .split(/ (?=\/|[A-Za-z]:\\)/) + .flatMap(part => part.split('\n')) + .filter(line => line.trim()) + const imagePaths = lines.filter(line => isImageFilePath(line)) + + if (onImagePaste && imagePaths.length > 0) { + const isTempScreenshot = + /\/TemporaryItems\/.*screencaptureui.*\/Screenshot/i.test( + pastedText, + ) + + // Process all image paths + void Promise.all( + imagePaths.map(imagePath => tryReadImageFromPath(imagePath)), + ).then(results => { + const validImages = results.filter( + (r): r is NonNullable => r !== null, + ) + + if (validImages.length > 0) { + // Successfully read at least one image + for (const imageData of validImages) { + const filename = basename(imageData.path) + onImagePaste( + imageData.base64, + imageData.mediaType, + filename, + imageData.dimensions, + imageData.path, + ) + } + // If some paths weren't images, paste them as text + const nonImageLines = lines.filter( + line => !isImageFilePath(line), + ) + if (nonImageLines.length > 0 && onPaste) { + onPaste(nonImageLines.join('\n')) + } + setIsPasting(false) + } else if (isTempScreenshot && isMacOS) { + // For temporary screenshot files that no longer exist, try clipboard + checkClipboardForImage() + } else { + if (onPaste) { + onPaste(pastedText) + } + setIsPasting(false) + } + }) + return { chunks: [], timeoutId: null } + } + + // If paste is empty (common when trying to paste images with Cmd+V), + // check if clipboard has an image (macOS only) + if (isMacOS && onImagePaste && pastedText.length === 0) { + checkClipboardForImage() + return { chunks: [], timeoutId: null } + } + + // Handle regular paste + if (onPaste) { + onPaste(pastedText) + } + // Reset isPasting state after paste is complete + setIsPasting(false) + return { chunks: [], timeoutId: null } + }) + }, + PASTE_COMPLETION_TIMEOUT_MS, + setPasteState, + onImagePaste, + onPaste, + setIsPasting, + checkClipboardForImage, + isMacOS, + pastePendingRef, + ) + }, + [checkClipboardForImage, isMacOS, onImagePaste, onPaste], + ) + + // Paste detection is now done via the InputEvent's keypress.isPasted flag, + // which is set by the keypress parser when it detects bracketed paste mode. + // This avoids the race condition caused by having multiple listeners on stdin. + // Previously, we had a stdin.on('data') listener here which competed with + // the 'readable' listener in App.tsx, causing dropped characters. + + const wrappedOnInput = (input: string, key: Key, event: InputEvent): void => { + // Detect paste from the parsed keypress event. + // The keypress parser sets isPasted=true for content within bracketed paste. + const isFromPaste = event.keypress.isPasted + + // If this is pasted content, set isPasting state for UI feedback + if (isFromPaste) { + setIsPasting(true) + } + + // Handle large pastes (>PASTE_THRESHOLD chars) + // Usually we get one or two input characters at a time. If we + // get more than the threshold, the user has probably pasted. + // Unfortunately node batches long pastes, so it's possible + // that we would see e.g. 1024 characters and then just a few + // more in the next frame that belong with the original paste. + // This batching number is not consistent. + + // Handle potential image filenames (even if they're shorter than paste threshold) + // When dragging multiple images, they may come as newline-separated or + // space-separated paths. Split on spaces preceding absolute paths: + // - Unix: ` /` - Windows: ` C:\` etc. + const hasImageFilePath = input + .split(/ (?=\/|[A-Za-z]:\\)/) + .flatMap(part => part.split('\n')) + .some(line => isImageFilePath(line.trim())) + + // Handle empty paste (clipboard image on macOS) + // When the user pastes an image with Cmd+V, the terminal sends an empty + // bracketed paste sequence. The keypress parser emits this as isPasted=true + // with empty input. + if (isFromPaste && input.length === 0 && isMacOS && onImagePaste) { + checkClipboardForImage() + // Reset isPasting since there's no text content to process + setIsPasting(false) + return + } + + // Check if we should handle as paste (from bracketed paste, large input, or continuation) + const shouldHandleAsPaste = + onPaste && + (input.length > PASTE_THRESHOLD || + pastePendingRef.current || + hasImageFilePath || + isFromPaste) + + if (shouldHandleAsPaste) { + pastePendingRef.current = true + setPasteState(({ chunks, timeoutId }) => { + return { + chunks: [...chunks, input], + timeoutId: resetPasteTimeout(timeoutId), + } + }) + return + } + onInput(input, key) + if (input.length > 10) { + // Ensure that setIsPasting is turned off on any other multicharacter + // input, because the stdin buffer may chunk at arbitrary points and split + // the closing escape sequence if the input length is too long for the + // stdin buffer. + setIsPasting(false) + } + } + + return { + wrappedOnInput, + pasteState, + isPasting, + } +} diff --git a/hooks/usePluginRecommendationBase.tsx b/hooks/usePluginRecommendationBase.tsx new file mode 100644 index 0000000..9a2a2d4 --- /dev/null +++ b/hooks/usePluginRecommendationBase.tsx @@ -0,0 +1,105 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * Shared state machine + install helper for plugin-recommendation hooks + * (LSP, claude-code-hint). Centralizes the gate chain, async-guard, + * and success/failure notification JSX so new sources stay small. + */ + +import figures from 'figures'; +import * as React from 'react'; +import { getIsRemoteMode } from '../bootstrap/state.js'; +import type { useNotifications } from '../context/notifications.js'; +import { Text } from '../ink.js'; +import { logError } from '../utils/log.js'; +import { getPluginById } from '../utils/plugins/marketplaceManager.js'; +type AddNotification = ReturnType['addNotification']; +type PluginData = NonNullable>>; + +/** + * Call tryResolve inside a useEffect; it applies standard gates (remote + * mode, already-showing, in-flight) then runs resolve(). Non-null return + * becomes the recommendation. Include tryResolve in effect deps — its + * identity tracks recommendation, so clearing re-triggers resolution. + */ +export function usePluginRecommendationBase() { + const $ = _c(6); + const [recommendation, setRecommendation] = React.useState(null); + const isCheckingRef = React.useRef(false); + let t0; + if ($[0] !== recommendation) { + t0 = resolve => { + if (getIsRemoteMode()) { + return; + } + if (recommendation) { + return; + } + if (isCheckingRef.current) { + return; + } + isCheckingRef.current = true; + resolve().then(rec => { + if (rec) { + setRecommendation(rec); + } + }).catch(logError).finally(() => { + isCheckingRef.current = false; + }); + }; + $[0] = recommendation; + $[1] = t0; + } else { + t0 = $[1]; + } + const tryResolve = t0; + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => setRecommendation(null); + $[2] = t1; + } else { + t1 = $[2]; + } + const clearRecommendation = t1; + let t2; + if ($[3] !== recommendation || $[4] !== tryResolve) { + t2 = { + recommendation, + clearRecommendation, + tryResolve + }; + $[3] = recommendation; + $[4] = tryResolve; + $[5] = t2; + } else { + t2 = $[5]; + } + return t2; +} + +/** Look up plugin, run install(), emit standard success/failure notification. */ +export async function installPluginAndNotify(pluginId: string, pluginName: string, keyPrefix: string, addNotification: AddNotification, install: (pluginData: PluginData) => Promise): Promise { + try { + const pluginData = await getPluginById(pluginId); + if (!pluginData) { + throw new Error(`Plugin ${pluginId} not found in marketplace`); + } + await install(pluginData); + addNotification({ + key: `${keyPrefix}-installed`, + jsx: + {figures.tick} {pluginName} installed · restart to apply + , + priority: 'immediate', + timeoutMs: 5000 + }); + } catch (error) { + logError(error); + addNotification({ + key: `${keyPrefix}-install-failed`, + jsx: Failed to install {pluginName}, + priority: 'immediate', + timeoutMs: 5000 + }); + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJnZXRJc1JlbW90ZU1vZGUiLCJ1c2VOb3RpZmljYXRpb25zIiwiVGV4dCIsImxvZ0Vycm9yIiwiZ2V0UGx1Z2luQnlJZCIsIkFkZE5vdGlmaWNhdGlvbiIsIlJldHVyblR5cGUiLCJQbHVnaW5EYXRhIiwiTm9uTnVsbGFibGUiLCJBd2FpdGVkIiwidXNlUGx1Z2luUmVjb21tZW5kYXRpb25CYXNlIiwiJCIsIl9jIiwicmVjb21tZW5kYXRpb24iLCJzZXRSZWNvbW1lbmRhdGlvbiIsInVzZVN0YXRlIiwiaXNDaGVja2luZ1JlZiIsInVzZVJlZiIsInQwIiwicmVzb2x2ZSIsImN1cnJlbnQiLCJ0aGVuIiwicmVjIiwiY2F0Y2giLCJmaW5hbGx5IiwidHJ5UmVzb2x2ZSIsInQxIiwiU3ltYm9sIiwiZm9yIiwiY2xlYXJSZWNvbW1lbmRhdGlvbiIsInQyIiwiaW5zdGFsbFBsdWdpbkFuZE5vdGlmeSIsInBsdWdpbklkIiwicGx1Z2luTmFtZSIsImtleVByZWZpeCIsImFkZE5vdGlmaWNhdGlvbiIsImluc3RhbGwiLCJwbHVnaW5EYXRhIiwiUHJvbWlzZSIsIkVycm9yIiwia2V5IiwianN4IiwidGljayIsInByaW9yaXR5IiwidGltZW91dE1zIiwiZXJyb3IiXSwic291cmNlcyI6WyJ1c2VQbHVnaW5SZWNvbW1lbmRhdGlvbkJhc2UudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2hhcmVkIHN0YXRlIG1hY2hpbmUgKyBpbnN0YWxsIGhlbHBlciBmb3IgcGx1Z2luLXJlY29tbWVuZGF0aW9uIGhvb2tzXG4gKiAoTFNQLCBjbGF1ZGUtY29kZS1oaW50KS4gQ2VudHJhbGl6ZXMgdGhlIGdhdGUgY2hhaW4sIGFzeW5jLWd1YXJkLFxuICogYW5kIHN1Y2Nlc3MvZmFpbHVyZSBub3RpZmljYXRpb24gSlNYIHNvIG5ldyBzb3VyY2VzIHN0YXkgc21hbGwuXG4gKi9cblxuaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgZ2V0SXNSZW1vdGVNb2RlIH0gZnJvbSAnLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnLi4vY29udGV4dC9ub3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnLi4vdXRpbHMvbG9nLmpzJ1xuaW1wb3J0IHsgZ2V0UGx1Z2luQnlJZCB9IGZyb20gJy4uL3V0aWxzL3BsdWdpbnMvbWFya2V0cGxhY2VNYW5hZ2VyLmpzJ1xuXG50eXBlIEFkZE5vdGlmaWNhdGlvbiA9IFJldHVyblR5cGU8dHlwZW9mIHVzZU5vdGlmaWNhdGlvbnM+WydhZGROb3RpZmljYXRpb24nXVxudHlwZSBQbHVnaW5EYXRhID0gTm9uTnVsbGFibGU8QXdhaXRlZDxSZXR1cm5UeXBlPHR5cGVvZiBnZXRQbHVnaW5CeUlkPj4+XG5cbi8qKlxuICogQ2FsbCB0cnlSZXNvbHZlIGluc2lkZSBhIHVzZUVmZmVjdDsgaXQgYXBwbGllcyBzdGFuZGFyZCBnYXRlcyAocmVtb3RlXG4gKiBtb2RlLCBhbHJlYWR5LXNob3dpbmcsIGluLWZsaWdodCkgdGhlbiBydW5zIHJlc29sdmUoKS4gTm9uLW51bGwgcmV0dXJuXG4gKiBiZWNvbWVzIHRoZSByZWNvbW1lbmRhdGlvbi4gSW5jbHVkZSB0cnlSZXNvbHZlIGluIGVmZmVjdCBkZXBzIOKAlCBpdHNcbiAqIGlkZW50aXR5IHRyYWNrcyByZWNvbW1lbmRhdGlvbiwgc28gY2xlYXJpbmcgcmUtdHJpZ2dlcnMgcmVzb2x1dGlvbi5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZVBsdWdpblJlY29tbWVuZGF0aW9uQmFzZTxUPigpOiB7XG4gIHJlY29tbWVuZGF0aW9uOiBUIHwgbnVsbFxuICBjbGVhclJlY29tbWVuZGF0aW9uOiAoKSA9PiB2b2lkXG4gIHRyeVJlc29sdmU6IChyZXNvbHZlOiAoKSA9PiBQcm9taXNlPFQgfCBudWxsPikgPT4gdm9pZFxufSB7XG4gIGNvbnN0IFtyZWNvbW1lbmRhdGlvbiwgc2V0UmVjb21tZW5kYXRpb25dID0gUmVhY3QudXNlU3RhdGU8VCB8IG51bGw+KG51bGwpXG4gIGNvbnN0IGlzQ2hlY2tpbmdSZWYgPSBSZWFjdC51c2VSZWYoZmFsc2UpXG5cbiAgY29uc3QgdHJ5UmVzb2x2ZSA9IFJlYWN0LnVzZUNhbGxiYWNrKFxuICAgIChyZXNvbHZlOiAoKSA9PiBQcm9taXNlPFQgfCBudWxsPikgPT4ge1xuICAgICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICAgIGlmIChyZWNvbW1lbmRhdGlvbikgcmV0dXJuXG4gICAgICBpZiAoaXNDaGVja2luZ1JlZi5jdXJyZW50KSByZXR1cm5cblxuICAgICAgaXNDaGVja2luZ1JlZi5jdXJyZW50ID0gdHJ1ZVxuICAgICAgdm9pZCByZXNvbHZlKClcbiAgICAgICAgLnRoZW4ocmVjID0+IHtcbiAgICAgICAgICBpZiAocmVjKSBzZXRSZWNvbW1lbmRhdGlvbihyZWMpXG4gICAgICAgIH0pXG4gICAgICAgIC5jYXRjaChsb2dFcnJvcilcbiAgICAgICAgLmZpbmFsbHkoKCkgPT4ge1xuICAgICAgICAgIGlzQ2hlY2tpbmdSZWYuY3VycmVudCA9IGZhbHNlXG4gICAgICAgIH0pXG4gICAgfSxcbiAgICBbcmVjb21tZW5kYXRpb25dLFxuICApXG5cbiAgY29uc3QgY2xlYXJSZWNvbW1lbmRhdGlvbiA9IFJlYWN0LnVzZUNhbGxiYWNrKFxuICAgICgpID0+IHNldFJlY29tbWVuZGF0aW9uKG51bGwpLFxuICAgIFtdLFxuICApXG5cbiAgcmV0dXJuIHsgcmVjb21tZW5kYXRpb24sIGNsZWFyUmVjb21tZW5kYXRpb24sIHRyeVJlc29sdmUgfVxufVxuXG4vKiogTG9vayB1cCBwbHVnaW4sIHJ1biBpbnN0YWxsKCksIGVtaXQgc3RhbmRhcmQgc3VjY2Vzcy9mYWlsdXJlIG5vdGlmaWNhdGlvbi4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBpbnN0YWxsUGx1Z2luQW5kTm90aWZ5KFxuICBwbHVnaW5JZDogc3RyaW5nLFxuICBwbHVnaW5OYW1lOiBzdHJpbmcsXG4gIGtleVByZWZpeDogc3RyaW5nLFxuICBhZGROb3RpZmljYXRpb246IEFkZE5vdGlmaWNhdGlvbixcbiAgaW5zdGFsbDogKHBsdWdpbkRhdGE6IFBsdWdpbkRhdGEpID0+IFByb21pc2U8dm9pZD4sXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBwbHVnaW5EYXRhID0gYXdhaXQgZ2V0UGx1Z2luQnlJZChwbHVnaW5JZClcbiAgICBpZiAoIXBsdWdpbkRhdGEpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgUGx1Z2luICR7cGx1Z2luSWR9IG5vdCBmb3VuZCBpbiBtYXJrZXRwbGFjZWApXG4gICAgfVxuICAgIGF3YWl0IGluc3RhbGwocGx1Z2luRGF0YSlcbiAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAga2V5OiBgJHtrZXlQcmVmaXh9LWluc3RhbGxlZGAsXG4gICAgICBqc3g6IChcbiAgICAgICAgPFRleHQgY29sb3I9XCJzdWNjZXNzXCI+XG4gICAgICAgICAge2ZpZ3VyZXMudGlja30ge3BsdWdpbk5hbWV9IGluc3RhbGxlZCDCtyByZXN0YXJ0IHRvIGFwcGx5XG4gICAgICAgIDwvVGV4dD5cbiAgICAgICksXG4gICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICB0aW1lb3V0TXM6IDUwMDAsXG4gICAgfSlcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBsb2dFcnJvcihlcnJvcilcbiAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAga2V5OiBgJHtrZXlQcmVmaXh9LWluc3RhbGwtZmFpbGVkYCxcbiAgICAgIGpzeDogPFRleHQgY29sb3I9XCJlcnJvclwiPkZhaWxlZCB0byBpbnN0YWxsIHtwbHVnaW5OYW1lfTwvVGV4dD4sXG4gICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScs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 \ No newline at end of file diff --git a/hooks/usePrStatus.ts b/hooks/usePrStatus.ts new file mode 100644 index 0000000..42bd57e --- /dev/null +++ b/hooks/usePrStatus.ts @@ -0,0 +1,106 @@ +import { useEffect, useRef, useState } from 'react' +import { getLastInteractionTime } from '../bootstrap/state.js' +import { fetchPrStatus, type PrReviewState } from '../utils/ghPrStatus.js' + +const POLL_INTERVAL_MS = 60_000 +const SLOW_GH_THRESHOLD_MS = 4_000 +const IDLE_STOP_MS = 60 * 60_000 // stop polling after 60 min idle + +export type PrStatusState = { + number: number | null + url: string | null + reviewState: PrReviewState | null + lastUpdated: number +} + +const INITIAL_STATE: PrStatusState = { + number: null, + url: null, + reviewState: null, + lastUpdated: 0, +} + +/** + * Polls PR review status every 60s while the session is active. + * When no interaction is detected for 60 minutes, the loop stops — no + * timers remain. React re-runs the effect when isLoading changes + * (turn starts/ends), restarting the loop. Effect setup schedules + * the next poll relative to the last fetch time so turn boundaries + * don't spawn `gh` more than once per interval. Disables permanently + * if a fetch exceeds 4s. + * + * Pass `enabled: false` to skip polling entirely (hook still must be + * called unconditionally to satisfy the rules of hooks). + */ +export function usePrStatus(isLoading: boolean, enabled = true): PrStatusState { + const [prStatus, setPrStatus] = useState(INITIAL_STATE) + const timeoutRef = useRef | null>(null) + const disabledRef = useRef(false) + const lastFetchRef = useRef(0) + + useEffect(() => { + if (!enabled) return + if (disabledRef.current) return + + let cancelled = false + let lastSeenInteractionTime = -1 + let lastActivityTimestamp = Date.now() + + async function poll() { + if (cancelled) return + + const currentInteractionTime = getLastInteractionTime() + if (lastSeenInteractionTime !== currentInteractionTime) { + lastSeenInteractionTime = currentInteractionTime + lastActivityTimestamp = Date.now() + } else if (Date.now() - lastActivityTimestamp >= IDLE_STOP_MS) { + return + } + + const start = Date.now() + const result = await fetchPrStatus() + if (cancelled) return + lastFetchRef.current = start + + setPrStatus(prev => { + const newNumber = result?.number ?? null + const newReviewState = result?.reviewState ?? null + if (prev.number === newNumber && prev.reviewState === newReviewState) { + return prev + } + return { + number: newNumber, + url: result?.url ?? null, + reviewState: newReviewState, + lastUpdated: Date.now(), + } + }) + + if (Date.now() - start > SLOW_GH_THRESHOLD_MS) { + disabledRef.current = true + return + } + + if (!cancelled) { + timeoutRef.current = setTimeout(poll, POLL_INTERVAL_MS) + } + } + + const elapsed = Date.now() - lastFetchRef.current + if (elapsed >= POLL_INTERVAL_MS) { + void poll() + } else { + timeoutRef.current = setTimeout(poll, POLL_INTERVAL_MS - elapsed) + } + + return () => { + cancelled = true + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + timeoutRef.current = null + } + } + }, [isLoading, enabled]) + + return prStatus +} diff --git a/hooks/usePromptSuggestion.ts b/hooks/usePromptSuggestion.ts new file mode 100644 index 0000000..0a0a35f --- /dev/null +++ b/hooks/usePromptSuggestion.ts @@ -0,0 +1,177 @@ +import { useCallback, useRef } from 'react' +import { useTerminalFocus } from '../ink/hooks/use-terminal-focus.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { abortSpeculation } from '../services/PromptSuggestion/speculation.js' +import { useAppState, useSetAppState } from '../state/AppState.js' + +type Props = { + inputValue: string + isAssistantResponding: boolean +} + +export function usePromptSuggestion({ + inputValue, + isAssistantResponding, +}: Props): { + suggestion: string | null + markAccepted: () => void + markShown: () => void + logOutcomeAtSubmission: ( + finalInput: string, + opts?: { skipReset: boolean }, + ) => void +} { + const promptSuggestion = useAppState(s => s.promptSuggestion) + const setAppState = useSetAppState() + const isTerminalFocused = useTerminalFocus() + const { + text: suggestionText, + promptId, + shownAt, + acceptedAt, + generationRequestId, + } = promptSuggestion + + const suggestion = + isAssistantResponding || inputValue.length > 0 ? null : suggestionText + + const isValidSuggestion = suggestionText && shownAt > 0 + + // Track engagement depth for telemetry + const firstKeystrokeAt = useRef(0) + const wasFocusedWhenShown = useRef(true) + const prevShownAt = useRef(0) + + // Capture focus state when a new suggestion appears (shownAt changes) + if (shownAt > 0 && shownAt !== prevShownAt.current) { + prevShownAt.current = shownAt + wasFocusedWhenShown.current = isTerminalFocused + firstKeystrokeAt.current = 0 + } else if (shownAt === 0) { + prevShownAt.current = 0 + } + + // Record first keystroke while suggestion is visible + if ( + inputValue.length > 0 && + firstKeystrokeAt.current === 0 && + isValidSuggestion + ) { + firstKeystrokeAt.current = Date.now() + } + + const resetSuggestion = useCallback(() => { + abortSpeculation(setAppState) + + setAppState(prev => ({ + ...prev, + promptSuggestion: { + text: null, + promptId: null, + shownAt: 0, + acceptedAt: 0, + generationRequestId: null, + }, + })) + }, [setAppState]) + + const markAccepted = useCallback(() => { + if (!isValidSuggestion) return + setAppState(prev => ({ + ...prev, + promptSuggestion: { + ...prev.promptSuggestion, + acceptedAt: Date.now(), + }, + })) + }, [isValidSuggestion, setAppState]) + + const markShown = useCallback(() => { + // Check shownAt inside setAppState callback to avoid depending on it + // (depending on shownAt causes infinite loop when this callback is called) + setAppState(prev => { + // Only mark shown if not already shown and suggestion exists + if (prev.promptSuggestion.shownAt !== 0 || !prev.promptSuggestion.text) { + return prev + } + return { + ...prev, + promptSuggestion: { + ...prev.promptSuggestion, + shownAt: Date.now(), + }, + } + }) + }, [setAppState]) + + const logOutcomeAtSubmission = useCallback( + (finalInput: string, opts?: { skipReset: boolean }) => { + if (!isValidSuggestion) return + + // Determine if accepted: either Tab was pressed (acceptedAt set) OR + // final input matches suggestion (empty Enter case) + const tabWasPressed = acceptedAt > shownAt + const wasAccepted = tabWasPressed || finalInput === suggestionText + const timeMs = wasAccepted ? acceptedAt || Date.now() : Date.now() + + logEvent('tengu_prompt_suggestion', { + source: + 'cli' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + outcome: (wasAccepted + ? 'accepted' + : 'ignored') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + prompt_id: + promptId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + ...(generationRequestId && { + generationRequestId: + generationRequestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }), + ...(wasAccepted && { + acceptMethod: (tabWasPressed + ? 'tab' + : 'enter') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }), + ...(wasAccepted && { + timeToAcceptMs: timeMs - shownAt, + }), + ...(!wasAccepted && { + timeToIgnoreMs: timeMs - shownAt, + }), + ...(firstKeystrokeAt.current > 0 && { + timeToFirstKeystrokeMs: firstKeystrokeAt.current - shownAt, + }), + wasFocusedWhenShown: wasFocusedWhenShown.current, + similarity: + Math.round( + (finalInput.length / (suggestionText?.length || 1)) * 100, + ) / 100, + ...(process.env.USER_TYPE === 'ant' && { + suggestion: + suggestionText as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + userInput: + finalInput as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }), + }) + if (!opts?.skipReset) resetSuggestion() + }, + [ + isValidSuggestion, + acceptedAt, + shownAt, + suggestionText, + promptId, + generationRequestId, + resetSuggestion, + ], + ) + + return { + suggestion, + markAccepted, + markShown, + logOutcomeAtSubmission, + } +} diff --git a/hooks/usePromptsFromClaudeInChrome.tsx b/hooks/usePromptsFromClaudeInChrome.tsx new file mode 100644 index 0000000..bc4673a --- /dev/null +++ b/hooks/usePromptsFromClaudeInChrome.tsx @@ -0,0 +1,71 @@ +import { c as _c } from "react/compiler-runtime"; +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'; +import { useEffect, useRef } from 'react'; +import { logError } from 'src/utils/log.js'; +import { z } from 'zod/v4'; +import { callIdeRpc } from '../services/mcp/client.js'; +import type { ConnectedMCPServer, MCPServerConnection } from '../services/mcp/types.js'; +import type { PermissionMode } from '../types/permissions.js'; +import { CLAUDE_IN_CHROME_MCP_SERVER_NAME, isTrackedClaudeInChromeTabId } from '../utils/claudeInChrome/common.js'; +import { lazySchema } from '../utils/lazySchema.js'; +import { enqueuePendingNotification } from '../utils/messageQueueManager.js'; + +// Schema for the prompt notification from Chrome extension (JSON-RPC 2.0 format) +const ClaudeInChromePromptNotificationSchema = lazySchema(() => z.object({ + method: z.literal('notifications/message'), + params: z.object({ + prompt: z.string(), + image: z.object({ + type: z.literal('base64'), + media_type: z.enum(['image/jpeg', 'image/png', 'image/gif', 'image/webp']), + data: z.string() + }).optional(), + tabId: z.number().optional() + }) +})); + +/** + * A hook that listens for prompt notifications from the Claude for Chrome extension, + * enqueues them as user prompts, and syncs permission mode changes to the extension. + */ +export function usePromptsFromClaudeInChrome(mcpClients, toolPermissionMode) { + const $ = _c(6); + useRef(undefined); + let t0; + if ($[0] !== mcpClients) { + t0 = [mcpClients]; + $[0] = mcpClients; + $[1] = t0; + } else { + t0 = $[1]; + } + useEffect(_temp, t0); + let t1; + let t2; + if ($[2] !== mcpClients || $[3] !== toolPermissionMode) { + t1 = () => { + const chromeClient = findChromeClient(mcpClients); + if (!chromeClient) { + return; + } + const chromeMode = toolPermissionMode === "bypassPermissions" ? "skip_all_permission_checks" : "ask"; + callIdeRpc("set_permission_mode", { + mode: chromeMode + }, chromeClient); + }; + t2 = [mcpClients, toolPermissionMode]; + $[2] = mcpClients; + $[3] = toolPermissionMode; + $[4] = t1; + $[5] = t2; + } else { + t1 = $[4]; + t2 = $[5]; + } + useEffect(t1, t2); +} +function _temp() {} +function findChromeClient(clients: MCPServerConnection[]): ConnectedMCPServer | undefined { + return clients.find((client): client is ConnectedMCPServer => client.type === 'connected' && client.name === CLAUDE_IN_CHROME_MCP_SERVER_NAME); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJDb250ZW50QmxvY2tQYXJhbSIsInVzZUVmZmVjdCIsInVzZVJlZiIsImxvZ0Vycm9yIiwieiIsImNhbGxJZGVScGMiLCJDb25uZWN0ZWRNQ1BTZXJ2ZXIiLCJNQ1BTZXJ2ZXJDb25uZWN0aW9uIiwiUGVybWlzc2lvbk1vZGUiLCJDTEFVREVfSU5fQ0hST01FX01DUF9TRVJWRVJfTkFNRSIsImlzVHJhY2tlZENsYXVkZUluQ2hyb21lVGFiSWQiLCJsYXp5U2NoZW1hIiwiZW5xdWV1ZVBlbmRpbmdOb3RpZmljYXRpb24iLCJDbGF1ZGVJbkNocm9tZVByb21wdE5vdGlmaWNhdGlvblNjaGVtYSIsIm9iamVjdCIsIm1ldGhvZCIsImxpdGVyYWwiLCJwYXJhbXMiLCJwcm9tcHQiLCJzdHJpbmciLCJpbWFnZSIsInR5cGUiLCJtZWRpYV90eXBlIiwiZW51bSIsImRhdGEiLCJvcHRpb25hbCIsInRhYklkIiwibnVtYmVyIiwidXNlUHJvbXB0c0Zyb21DbGF1ZGVJbkNocm9tZSIsIm1jcENsaWVudHMiLCJ0b29sUGVybWlzc2lvbk1vZGUiLCIkIiwiX2MiLCJ1bmRlZmluZWQiLCJ0MCIsIl90ZW1wIiwidDEiLCJ0MiIsImNocm9tZUNsaWVudCIsImZpbmRDaHJvbWVDbGllbnQiLCJjaHJvbWVNb2RlIiwibW9kZSIsImNsaWVudHMiLCJmaW5kIiwiY2xpZW50IiwibmFtZSJdLCJzb3VyY2VzIjpbInVzZVByb21wdHNGcm9tQ2xhdWRlSW5DaHJvbWUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgQ29udGVudEJsb2NrUGFyYW0gfSBmcm9tICdAYW50aHJvcGljLWFpL3Nkay9yZXNvdXJjZXMvbWVzc2FnZXMubWpzJ1xuaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnc3JjL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7IHogfSBmcm9tICd6b2QvdjQnXG5pbXBvcnQgeyBjYWxsSWRlUnBjIH0gZnJvbSAnLi4vc2VydmljZXMvbWNwL2NsaWVudC5qcydcbmltcG9ydCB0eXBlIHtcbiAgQ29ubmVjdGVkTUNQU2VydmVyLFxuICBNQ1BTZXJ2ZXJDb25uZWN0aW9uLFxufSBmcm9tICcuLi9zZXJ2aWNlcy9tY3AvdHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IFBlcm1pc3Npb25Nb2RlIH0gZnJvbSAnLi4vdHlwZXMvcGVybWlzc2lvbnMuanMnXG5pbXBvcnQge1xuICBDTEFVREVfSU5fQ0hST01FX01DUF9TRVJWRVJfTkFNRSxcbiAgaXNUcmFja2VkQ2xhdWRlSW5DaHJvbWVUYWJJZCxcbn0gZnJvbSAnLi4vdXRpbHMvY2xhdWRlSW5DaHJvbWUvY29tbW9uLmpzJ1xuaW1wb3J0IHsgbGF6eVNjaGVtYSB9IGZyb20gJy4uL3V0aWxzL2xhenlTY2hlbWEuanMnXG5pbXBvcnQgeyBlbnF1ZXVlUGVuZGluZ05vdGlmaWNhdGlvbiB9IGZyb20gJy4uL3V0aWxzL21lc3NhZ2VRdWV1ZU1hbmFnZXIuanMnXG5cbi8vIFNjaGVtYSBmb3IgdGhlIHByb21wdCBub3RpZmljYXRpb24gZnJvbSBDaHJvbWUgZXh0ZW5zaW9uIChKU09OLVJQQyAyLjAgZm9ybWF0KVxuY29uc3QgQ2xhdWRlSW5DaHJvbWVQcm9tcHROb3RpZmljYXRpb25TY2hlbWEgPSBsYXp5U2NoZW1hKCgpID0+XG4gIHoub2JqZWN0KHtcbiAgICBtZXRob2Q6IHoubGl0ZXJhbCgnbm90aWZpY2F0aW9ucy9tZXNzYWdlJyksXG4gICAgcGFyYW1zOiB6Lm9iamVjdCh7XG4gICAgICBwcm9tcHQ6IHouc3RyaW5nKCksXG4gICAgICBpbWFnZTogelxuICAgICAgICAub2JqZWN0KHtcbiAgICAgICAgICB0eXBlOiB6LmxpdGVyYWwoJ2Jhc2U2NCcpLFxuICAgICAgICAgIG1lZGlhX3R5cGU6IHouZW51bShbXG4gICAgICAgICAgICAnaW1hZ2UvanBlZycsXG4gICAgICAgICAgICAnaW1hZ2UvcG5nJyxcbiAgICAgICAgICAgICdpbWFnZS9naWYnLFxuICAgICAgICAgICAgJ2ltYWdlL3dlYnAnLFxuICAgICAgICAgIF0pLFxuICAgICAgICAgIGRhdGE6IHouc3RyaW5nKCksXG4gICAgICAgIH0pXG4gICAgICAgIC5vcHRpb25hbCgpLFxuICAgICAgdGFiSWQ6IHoubnVtYmVyKCkub3B0aW9uYWwoKSxcbiAgICB9KSxcbiAgfSksXG4pXG5cbi8qKlxuICogQSBob29rIHRoYXQgbGlzdGVucyBmb3IgcHJvbXB0IG5vdGlmaWNhdGlvbnMgZnJvbSB0aGUgQ2xhdWRlIGZvciBDaHJvbWUgZXh0ZW5zaW9uLFxuICogZW5xdWV1ZXMgdGhlbSBhcyB1c2VyIHByb21wdHMsIGFuZCBzeW5jcyBwZXJtaXNzaW9uIG1vZGUgY2hhbmdlcyB0byB0aGUgZXh0ZW5zaW9uLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlUHJvbXB0c0Zyb21DbGF1ZGVJbkNocm9tZShcbiAgbWNwQ2xpZW50czogTUNQU2VydmVyQ29ubmVjdGlvbltdLFxuICB0b29sUGVybWlzc2lvbk1vZGU6IFBlcm1pc3Npb25Nb2RlLFxuKTogdm9pZCB7XG4gIGNvbnN0IG1jcENsaWVudFJlZiA9IHVzZVJlZjxDb25uZWN0ZWRNQ1BTZXJ2ZXIgfCB1bmRlZmluZWQ+KHVuZGVmaW5lZClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChcImV4dGVybmFsXCIgIT09ICdhbnQnKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjb25zdCBtY3BDbGllbnQgPSBmaW5kQ2hyb21lQ2xpZW50KG1jcENsaWVudHMpXG4gICAgaWYgKG1jcENsaWVudFJlZi5jdXJyZW50ICE9PSBtY3BDbGllbnQpIHtcbiAgICAgIG1jcENsaWVudFJlZi5jdXJyZW50ID0gbWNwQ2xpZW50XG4gICAgfVxuXG4gICAgaWYgKG1jcENsaWVudCkge1xuICAgICAgbWNwQ2xpZW50LmNsaWVudC5zZXROb3RpZmljYXRpb25IYW5kbGVyKFxuICAgICAgICBDbGF1ZGVJbkNocm9tZVByb21wdE5vdGlmaWNhdGlvblNjaGVtYSgpLFxuICAgICAgICBub3RpZmljYXRpb24gPT4ge1xuICAgICAgICAgIGlmIChtY3BDbGllbnRSZWYuY3VycmVudCAhPT0gbWNwQ2xpZW50KSB7XG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgeyB0YWJJZCwgcHJvbXB0LCBpbWFnZSB9ID0gbm90aWZpY2F0aW9uLnBhcmFtc1xuXG4gICAgICAgICAgLy8gUHJvY2VzcyBub3RpZmljYXRpb25zIGZyb20gdGFicyB3ZSdyZSB0cmFja2luZyBzaW5jZSBub3RpZmljYXRpb25zIGFyZSBicm9hZGNhc3RlZFxuICAgICAgICAgIGlmIChcbiAgICAgICAgICAgIHR5cGVvZiB0YWJJZCAhPT0gJ251bWJlcicgfHxcbiAgICAgICAgICAgICFpc1RyYWNrZWRDbGF1ZGVJbkNocm9tZVRhYklkKHRhYklkKVxuICAgICAgICAgICkge1xuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIEJ1aWxkIGNvbnRlbnQgYmxvY2tzIGlmIHRoZXJlJ3MgYW4gaW1hZ2UsIG90aGVyd2lzZSBqdXN0IHVzZSB0aGUgcHJvbXB0IHN0cmluZ1xuICAgICAgICAgICAgaWYgKGltYWdlKSB7XG4gICAgICAgICAgICAgIGNvbnN0IGNvbnRlbnRCbG9ja3M6IENvbnRlbnRCbG9ja1BhcmFtW10gPSBbXG4gICAgICAgICAgICAgICAgeyB0eXBlOiAndGV4dCcsIHRleHQ6IHByb21wdCB9LFxuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgIHR5cGU6ICdpbWFnZScsXG4gICAgICAgICAgICAgICAgICBzb3VyY2U6IHtcbiAgICAgICAgICAgICAgICAgICAgdHlwZTogaW1hZ2UudHlwZSxcbiAgICAgICAgICAgICAgICAgICAgbWVkaWFfdHlwZTogaW1hZ2UubWVkaWFfdHlwZSxcbiAgICAgICAgICAgICAgICAgICAgZGF0YTogaW1hZ2UuZGF0YSxcbiAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgXVxuICAgICAgICAgICAgICBlbnF1ZXVlUGVuZGluZ05vdGlmaWNhdGlvbih7XG4gICAgICAgICAgICAgICAgdmFsdWU6IGNvbnRlbnRCbG9ja3MsXG4gICAgICAgICAgICAgICAgbW9kZTogJ3Byb21wdCcsXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBlbnF1ZXVlUGVuZGluZ05vdGlmaWNhdGlvbih7IHZhbHVlOiBwcm9tcHQsIG1vZGU6ICdwcm9tcHQnIH0pXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgIGxvZ0Vycm9yKGVycm9yIGFzIEVycm9yKVxuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgIClcbiAgICB9XG4gIH0sIFttY3BDbGllbnRzXSlcblxuICAvLyBTeW5jIHBlcm1pc3Npb24gbW9kZSB3aXRoIENocm9tZSBleHRlbnNpb24gd2hlbmV2ZXIgaXQgY2hhbmdlc1xuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IGNocm9tZUNsaWVudCA9IGZpbmRDaHJvbWVDbGllbnQobWNwQ2xpZW50cylcbiAgICBpZiAoIWNocm9tZUNsaWVudCkgcmV0dXJuXG5cbiAgICBjb25zdCBjaHJvbWVNb2RlID1cbiAgICAgIHRvb2xQZXJtaXNzaW9uTW9kZSA9PT0gJ2J5cGFzc1Blcm1pc3Npb25zJ1xuICAgICAgICA/ICdza2lwX2FsbF9wZXJtaXNzaW9uX2NoZWNrcydcbiAgICAgICAgOiAnYXNrJ1xuXG4gICAgdm9pZCBjYWxsSWRlUnBjKCdzZXRfcGVybWlzc2lvbl9tb2RlJywgeyBtb2RlOiBjaHJvbWVNb2RlIH0sIGNocm9tZUNsaWVudClcbiAgfSwgW21jcENsaWVudHMsIHRvb2xQZXJtaXNzaW9uTW9kZV0pXG59XG5cbmZ1bmN0aW9uIGZpbmRDaHJvbWVDbGllbnQoXG4gIGNsaWVudHM6IE1DUFNlcnZlckNvbm5lY3Rpb25bXSxcbik6IENvbm5lY3RlZE1DUFNlcnZlciB8IHVuZGVmaW5lZCB7XG4gIHJldHVybiBjbGllbnRzLmZpbmQoXG4gICAgKGNsaWVudCk6IGNsaWVudCBpcyBDb25uZWN0ZWRNQ1BTZXJ2ZXIgPT5cbiAgICAgIGNsaWVudC50eXBlID09PSAnY29ubmVjdGVkJyAmJlxuICAgICAgY2xpZW50Lm5hbWUgPT09IENMQVVERV9JTl9DSFJPTUVfTUNQX1NFUlZFUl9OQU1FLFxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxjQUFjQSxpQkFBaUIsUUFBUSwwQ0FBMEM7QUFDakYsU0FBU0MsU0FBUyxFQUFFQyxNQUFNLFFBQVEsT0FBTztBQUN6QyxTQUFTQyxRQUFRLFFBQVEsa0JBQWtCO0FBQzNDLFNBQVNDLENBQUMsUUFBUSxRQUFRO0FBQzFCLFNBQVNDLFVBQVUsUUFBUSwyQkFBMkI7QUFDdEQsY0FDRUMsa0JBQWtCLEVBQ2xCQyxtQkFBbUIsUUFDZCwwQkFBMEI7QUFDakMsY0FBY0MsY0FBYyxRQUFRLHlCQUF5QjtBQUM3RCxTQUNFQyxnQ0FBZ0MsRUFDaENDLDRCQUE0QixRQUN2QixtQ0FBbUM7QUFDMUMsU0FBU0MsVUFBVSxRQUFRLHdCQUF3QjtBQUNuRCxTQUFTQywwQkFBMEIsUUFBUSxpQ0FBaUM7O0FBRTVFO0FBQ0EsTUFBTUMsc0NBQXNDLEdBQUdGLFVBQVUsQ0FBQyxNQUN4RFAsQ0FBQyxDQUFDVSxNQUFNLENBQUM7RUFDUEMsTUFBTSxFQUFFWCxDQUFDLENBQUNZLE9BQU8sQ0FBQyx1QkFBdUIsQ0FBQztFQUMxQ0MsTUFBTSxFQUFFYixDQUFDLENBQUNVLE1BQU0sQ0FBQztJQUNmSSxNQUFNLEVBQUVkLENBQUMsQ0FBQ2UsTUFBTSxDQUFDLENBQUM7SUFDbEJDLEtBQUssRUFBRWhCLENBQUMsQ0FDTFUsTUFBTSxDQUFDO01BQ05PLElBQUksRUFBRWpCLENBQUMsQ0FBQ1ksT0FBTyxDQUFDLFFBQVEsQ0FBQztNQUN6Qk0sVUFBVSxFQUFFbEIsQ0FBQyxDQUFDbUIsSUFBSSxDQUFDLENBQ2pCLFlBQVksRUFDWixXQUFXLEVBQ1gsV0FBVyxFQUNYLFlBQVksQ0FDYixDQUFDO01BQ0ZDLElBQUksRUFBRXBCLENBQUMsQ0FBQ2UsTUFBTSxDQUFDO0lBQ2pCLENBQUMsQ0FBQyxDQUNETSxRQUFRLENBQUMsQ0FBQztJQUNiQyxLQUFLLEVBQUV0QixDQUFDLENBQUN1QixNQUFNLENBQUMsQ0FBQyxDQUFDRixRQUFRLENBQUM7RUFDN0IsQ0FBQztBQUNILENBQUMsQ0FDSCxDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTyxTQUFBRyw2QkFBQUMsVUFBQSxFQUFBQyxrQkFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUlnQjlCLE1BQU0sQ0FBaUMrQixTQUFTLENBQUM7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBRixVQUFBO0lBd0RuRUssRUFBQSxJQUFDTCxVQUFVLENBQUM7SUFBQUUsQ0FBQSxNQUFBRixVQUFBO0lBQUFFLENBQUEsTUFBQUcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBdERmOUIsU0FBUyxDQUFDa0MsS0FzRFQsRUFBRUQsRUFBWSxDQUFDO0VBQUEsSUFBQUUsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBTixDQUFBLFFBQUFGLFVBQUEsSUFBQUUsQ0FBQSxRQUFBRCxrQkFBQTtJQUdOTSxFQUFBLEdBQUFBLENBQUE7TUFDUixNQUFBRSxZQUFBLEdBQXFCQyxnQkFBZ0IsQ0FBQ1YsVUFBVSxDQUFDO01BQ2pELElBQUksQ0FBQ1MsWUFBWTtRQUFBO01BQUE7TUFFakIsTUFBQUUsVUFBQSxHQUNFVixrQkFBa0IsS0FBSyxtQkFFZCxHQUZULDRCQUVTLEdBRlQsS0FFUztNQUVOekIsVUFBVSxDQUFDLHFCQUFxQixFQUFFO1FBQUFvQyxJQUFBLEVBQVFEO01BQVcsQ0FBQyxFQUFFRixZQUFZLENBQUM7SUFBQSxDQUMzRTtJQUFFRCxFQUFBLElBQUNSLFVBQVUsRUFBRUMsa0JBQWtCLENBQUM7SUFBQUMsQ0FBQSxNQUFBRixVQUFBO0lBQUFFLENBQUEsTUFBQUQsa0JBQUE7SUFBQUMsQ0FBQSxNQUFBSyxFQUFBO0lBQUFMLENBQUEsTUFBQU0sRUFBQTtFQUFBO0lBQUFELEVBQUEsR0FBQUwsQ0FBQTtJQUFBTSxFQUFBLEdBQUFOLENBQUE7RUFBQTtFQVZuQzlCLFNBQVMsQ0FBQ21DLEVBVVQsRUFBRUMsRUFBZ0MsQ0FBQztBQUFBO0FBekUvQixTQUFBRixNQUFBO0FBNEVQLFNBQVNJLGdCQUFnQkEsQ0FDdkJHLE9BQU8sRUFBRW5DLG1CQUFtQixFQUFFLENBQy9CLEVBQUVELGtCQUFrQixHQUFHLFNBQVMsQ0FBQztFQUNoQyxPQUFPb0MsT0FBTyxDQUFDQyxJQUFJLENBQ2pCLENBQUNDLE1BQU0sQ0FBQyxFQUFFQSxNQUFNLElBQUl0QyxrQkFBa0IsSUFDcENzQyxNQUFNLENBQUN2QixJQUFJLEtBQUssV0FBVyxJQUMzQnVCLE1BQU0sQ0FBQ0MsSUFBSSxLQUFLcEMsZ0NBQ3BCLENBQUM7QUFDSCIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/hooks/useQueueProcessor.ts b/hooks/useQueueProcessor.ts new file mode 100644 index 0000000..8f2b5f1 --- /dev/null +++ b/hooks/useQueueProcessor.ts @@ -0,0 +1,68 @@ +import { useEffect, useSyncExternalStore } from 'react' +import type { QueuedCommand } from '../types/textInputTypes.js' +import { + getCommandQueueSnapshot, + subscribeToCommandQueue, +} from '../utils/messageQueueManager.js' +import type { QueryGuard } from '../utils/QueryGuard.js' +import { processQueueIfReady } from '../utils/queueProcessor.js' + +type UseQueueProcessorParams = { + executeQueuedInput: (commands: QueuedCommand[]) => Promise + hasActiveLocalJsxUI: boolean + queryGuard: QueryGuard +} + +/** + * Hook that processes queued commands when conditions are met. + * + * Uses a single unified command queue (module-level store). Priority determines + * processing order: 'now' > 'next' (user input) > 'later' (task notifications). + * The dequeue() function handles priority ordering automatically. + * + * Processing triggers when: + * - No query active (queryGuard — reactive via useSyncExternalStore) + * - Queue has items + * - No active local JSX UI blocking input + */ +export function useQueueProcessor({ + executeQueuedInput, + hasActiveLocalJsxUI, + queryGuard, +}: UseQueueProcessorParams): void { + // Subscribe to the query guard. Re-renders when a query starts or ends + // (or when reserve/cancelReservation transitions dispatching state). + const isQueryActive = useSyncExternalStore( + queryGuard.subscribe, + queryGuard.getSnapshot, + ) + + // Subscribe to the unified command queue via useSyncExternalStore. + // This guarantees re-render when the store changes, bypassing + // React context propagation delays that cause missed notifications in Ink. + const queueSnapshot = useSyncExternalStore( + subscribeToCommandQueue, + getCommandQueueSnapshot, + ) + + useEffect(() => { + if (isQueryActive) return + if (hasActiveLocalJsxUI) return + if (queueSnapshot.length === 0) return + + // Reservation is now owned by handlePromptSubmit (inside executeUserInput's + // try block). The sync chain executeQueuedInput → handlePromptSubmit → + // executeUserInput → queryGuard.reserve() runs before the first real await, + // so by the time React re-runs this effect (due to the dequeue-triggered + // snapshot change), isQueryActive is already true (dispatching) and the + // guard above returns early. handlePromptSubmit's finally releases the + // reservation via cancelReservation() (no-op if onQuery already ran end()). + processQueueIfReady({ executeInput: executeQueuedInput }) + }, [ + queueSnapshot, + isQueryActive, + executeQueuedInput, + hasActiveLocalJsxUI, + queryGuard, + ]) +} diff --git a/hooks/useRemoteSession.ts b/hooks/useRemoteSession.ts new file mode 100644 index 0000000..d4084a8 --- /dev/null +++ b/hooks/useRemoteSession.ts @@ -0,0 +1,605 @@ +import { useCallback, useEffect, useMemo, useRef } from 'react' +import { BoundedUUIDSet } from '../bridge/bridgeMessaging.js' +import type { ToolUseConfirm } from '../components/permissions/PermissionRequest.js' +import type { SpinnerMode } from '../components/Spinner/types.js' +import { + type RemotePermissionResponse, + type RemoteSessionConfig, + RemoteSessionManager, +} from '../remote/RemoteSessionManager.js' +import { + createSyntheticAssistantMessage, + createToolStub, +} from '../remote/remotePermissionBridge.js' +import { + convertSDKMessage, + isSessionEndMessage, +} from '../remote/sdkMessageAdapter.js' +import { useSetAppState } from '../state/AppState.js' +import type { AppState } from '../state/AppStateStore.js' +import type { Tool } from '../Tool.js' +import { findToolByName } from '../Tool.js' +import type { Message as MessageType } from '../types/message.js' +import type { PermissionAskDecision } from '../types/permissions.js' +import { logForDebugging } from '../utils/debug.js' +import { truncateToWidth } from '../utils/format.js' +import { + createSystemMessage, + extractTextContent, + handleMessageFromStream, + type StreamingToolUse, +} from '../utils/messages.js' +import { generateSessionTitle } from '../utils/sessionTitle.js' +import type { RemoteMessageContent } from '../utils/teleport/api.js' +import { updateSessionTitle } from '../utils/teleport/api.js' + +// How long to wait for a response before showing a warning +const RESPONSE_TIMEOUT_MS = 60000 // 60 seconds +// Extended timeout during compaction — compact API calls take 5-30s and +// block other SDK messages, so the normal 60s timeout isn't enough when +// compaction itself runs close to the edge. +const COMPACTION_TIMEOUT_MS = 180000 // 3 minutes + +type UseRemoteSessionProps = { + config: RemoteSessionConfig | undefined + setMessages: React.Dispatch> + setIsLoading: (loading: boolean) => void + onInit?: (slashCommands: string[]) => void + setToolUseConfirmQueue: React.Dispatch> + tools: Tool[] + setStreamingToolUses?: React.Dispatch< + React.SetStateAction + > + setStreamMode?: React.Dispatch> + setInProgressToolUseIDs?: (f: (prev: Set) => Set) => void +} + +type UseRemoteSessionResult = { + isRemoteMode: boolean + sendMessage: ( + content: RemoteMessageContent, + opts?: { uuid?: string }, + ) => Promise + cancelRequest: () => void + disconnect: () => void +} + +/** + * Hook for managing a remote CCR session in the REPL. + * + * Handles: + * - WebSocket connection to CCR + * - Converting SDK messages to REPL messages + * - Sending user input to CCR via HTTP POST + * - Permission request/response flow via existing ToolUseConfirm queue + */ +export function useRemoteSession({ + config, + setMessages, + setIsLoading, + onInit, + setToolUseConfirmQueue, + tools, + setStreamingToolUses, + setStreamMode, + setInProgressToolUseIDs, +}: UseRemoteSessionProps): UseRemoteSessionResult { + const isRemoteMode = !!config + + const setAppState = useSetAppState() + const setConnStatus = useCallback( + (s: AppState['remoteConnectionStatus']) => + setAppState(prev => + prev.remoteConnectionStatus === s + ? prev + : { ...prev, remoteConnectionStatus: s }, + ), + [setAppState], + ) + + // Event-sourced count of subagents running inside the remote daemon child. + // The viewer's own AppState.tasks is empty — tasks live in a different + // process. task_started/task_notification reach us via the bridge WS. + const runningTaskIdsRef = useRef(new Set()) + const writeTaskCount = useCallback(() => { + const n = runningTaskIdsRef.current.size + setAppState(prev => + prev.remoteBackgroundTaskCount === n + ? prev + : { ...prev, remoteBackgroundTaskCount: n }, + ) + }, [setAppState]) + + // Timer for detecting stuck sessions + const responseTimeoutRef = useRef(null) + + // Track whether the remote session is compacting. During compaction the + // CLI worker is busy with an API call and won't emit messages for a while; + // use a longer timeout and suppress spurious "unresponsive" warnings. + const isCompactingRef = useRef(false) + + const managerRef = useRef(null) + + // Track whether we've already updated the session title (for no-initial-prompt sessions) + const hasUpdatedTitleRef = useRef(false) + + // UUIDs of user messages we POSTed locally — the WS echoes them back and + // we must filter them out when convertUserTextMessages is on, or the viewer + // sees every typed message twice (once from local createUserMessage, once + // from the echo). A single POST can echo MULTIPLE times with the same uuid: + // the server may broadcast the POST directly to /subscribe, AND the worker + // (cowork desktop / CLI daemon) echoes it again on its write path. A + // delete-on-first-match Set would let the second echo through — use a + // bounded ring instead. Cap is generous: users don't type 50 messages + // faster than echoes arrive. + // NOTE: this does NOT dedup history-vs-live overlap at attach time (nothing + // seeds the set from history UUIDs; only sendMessage populates it). + const sentUUIDsRef = useRef(new BoundedUUIDSet(50)) + + // Keep a ref to tools so the WebSocket callback doesn't go stale + const toolsRef = useRef(tools) + useEffect(() => { + toolsRef.current = tools + }, [tools]) + + // Initialize and connect to remote session + useEffect(() => { + // Skip if not in remote mode + if (!config) { + return + } + + logForDebugging( + `[useRemoteSession] Initializing for session ${config.sessionId}`, + ) + + const manager = new RemoteSessionManager(config, { + onMessage: sdkMessage => { + const parts = [`type=${sdkMessage.type}`] + if ('subtype' in sdkMessage) parts.push(`subtype=${sdkMessage.subtype}`) + if (sdkMessage.type === 'user') { + const c = sdkMessage.message?.content + parts.push( + `content=${Array.isArray(c) ? c.map(b => b.type).join(',') : typeof c}`, + ) + } + logForDebugging(`[useRemoteSession] Received ${parts.join(' ')}`) + + // Clear response timeout on any message received — including the WS + // echo of our own POST, which acts as a heartbeat. This must run + // BEFORE the echo filter, or slow-to-stream agents (compaction, cold + // start) spuriously trip the 60s unresponsive warning + reconnect. + if (responseTimeoutRef.current) { + clearTimeout(responseTimeoutRef.current) + responseTimeoutRef.current = null + } + + // Echo filter: drop user messages we already added locally before POST. + // The server and/or worker round-trip our own send back on the WS with + // the same uuid we passed to sendEventToRemoteSession. DO NOT delete on + // match — the same uuid can echo more than once (server broadcast + + // worker echo), and BoundedUUIDSet already caps growth via its ring. + if ( + sdkMessage.type === 'user' && + sdkMessage.uuid && + sentUUIDsRef.current.has(sdkMessage.uuid) + ) { + logForDebugging( + `[useRemoteSession] Dropping echoed user message ${sdkMessage.uuid}`, + ) + return + } + // Handle init message - extract available slash commands + if ( + sdkMessage.type === 'system' && + sdkMessage.subtype === 'init' && + onInit + ) { + logForDebugging( + `[useRemoteSession] Init received with ${sdkMessage.slash_commands.length} slash commands`, + ) + onInit(sdkMessage.slash_commands) + } + + // Track remote subagent lifecycle for the "N in background" counter. + // All task types (Agent/teammate/workflow/bash) flow through + // registerTask() → task_started, and complete via task_notification. + // Return early — these are status signals, not renderable messages. + if (sdkMessage.type === 'system') { + if (sdkMessage.subtype === 'task_started') { + runningTaskIdsRef.current.add(sdkMessage.task_id) + writeTaskCount() + return + } + if (sdkMessage.subtype === 'task_notification') { + runningTaskIdsRef.current.delete(sdkMessage.task_id) + writeTaskCount() + return + } + if (sdkMessage.subtype === 'task_progress') { + return + } + // Track compaction state. The CLI emits status='compacting' at + // the start and status=null when done; compact_boundary also + // signals completion. Repeated 'compacting' status messages + // (keep-alive ticks) update the ref but don't append to messages. + if (sdkMessage.subtype === 'status') { + const wasCompacting = isCompactingRef.current + isCompactingRef.current = sdkMessage.status === 'compacting' + if (wasCompacting && isCompactingRef.current) { + return + } + } + if (sdkMessage.subtype === 'compact_boundary') { + isCompactingRef.current = false + } + } + + // Check if session ended + if (isSessionEndMessage(sdkMessage)) { + isCompactingRef.current = false + setIsLoading(false) + } + + // Clear in-progress tool_use IDs when their tool_result arrives. + // Must read the RAW sdkMessage: in non-viewerOnly mode, + // convertSDKMessage returns {type:'ignored'} for user messages, so the + // delete would never fire post-conversion. Mirrors the add site below + // and inProcessRunner.ts; without this the set grows unbounded for the + // session lifetime (BQ: CCR cohort shows 5.2x higher RSS slope). + if (setInProgressToolUseIDs && sdkMessage.type === 'user') { + const content = sdkMessage.message?.content + if (Array.isArray(content)) { + const resultIds: string[] = [] + for (const block of content) { + if (block.type === 'tool_result') { + resultIds.push(block.tool_use_id) + } + } + if (resultIds.length > 0) { + setInProgressToolUseIDs(prev => { + const next = new Set(prev) + for (const id of resultIds) next.delete(id) + return next.size === prev.size ? prev : next + }) + } + } + } + + // Convert SDK message to REPL message. In viewerOnly mode, the + // remote agent runs BriefTool (SendUserMessage) — its tool_use block + // renders empty (userFacingName() === ''), actual content is in the + // tool_result. So we must convert tool_results to render them. + const converted = convertSDKMessage( + sdkMessage, + config.viewerOnly + ? { convertToolResults: true, convertUserTextMessages: true } + : undefined, + ) + + if (converted.type === 'message') { + // When we receive a complete message, clear streaming tool uses + // since the complete message replaces the partial streaming state + setStreamingToolUses?.(prev => (prev.length > 0 ? [] : prev)) + + // Mark tool_use blocks as in-progress so the UI shows the correct + // spinner state instead of "Waiting…" (queued). In local sessions, + // toolOrchestration.ts handles this, but remote sessions receive + // pre-built assistant messages without running local tool execution. + if ( + setInProgressToolUseIDs && + converted.message.type === 'assistant' + ) { + const toolUseIds = converted.message.message.content + .filter(block => block.type === 'tool_use') + .map(block => block.id) + if (toolUseIds.length > 0) { + setInProgressToolUseIDs(prev => { + const next = new Set(prev) + for (const id of toolUseIds) { + next.add(id) + } + return next + }) + } + } + + setMessages(prev => [...prev, converted.message]) + // Note: Don't stop loading on assistant messages - the agent may still be + // working (tool use loops). Loading stops only on session end or permission request. + } else if (converted.type === 'stream_event') { + // Process streaming events to update UI in real-time + if (setStreamingToolUses && setStreamMode) { + handleMessageFromStream( + converted.event, + message => setMessages(prev => [...prev, message]), + () => { + // No-op for response length - remote sessions don't track this + }, + setStreamMode, + setStreamingToolUses, + ) + } else { + logForDebugging( + `[useRemoteSession] Stream event received but streaming callbacks not provided`, + ) + } + } + // 'ignored' messages are silently dropped + }, + onPermissionRequest: (request, requestId) => { + logForDebugging( + `[useRemoteSession] Permission request for tool: ${request.tool_name}`, + ) + + // Look up the Tool object by name, or create a stub for unknown tools + const tool = + findToolByName(toolsRef.current, request.tool_name) ?? + createToolStub(request.tool_name) + + const syntheticMessage = createSyntheticAssistantMessage( + request, + requestId, + ) + + const permissionResult: PermissionAskDecision = { + behavior: 'ask', + message: + request.description ?? `${request.tool_name} requires permission`, + suggestions: request.permission_suggestions, + blockedPath: request.blocked_path, + } + + const toolUseConfirm: ToolUseConfirm = { + assistantMessage: syntheticMessage, + tool, + description: + request.description ?? `${request.tool_name} requires permission`, + input: request.input, + toolUseContext: {} as ToolUseConfirm['toolUseContext'], + toolUseID: request.tool_use_id, + permissionResult, + permissionPromptStartTimeMs: Date.now(), + onUserInteraction() { + // No-op for remote — classifier runs on the container + }, + onAbort() { + const response: RemotePermissionResponse = { + behavior: 'deny', + message: 'User aborted', + } + manager.respondToPermissionRequest(requestId, response) + setToolUseConfirmQueue(queue => + queue.filter(item => item.toolUseID !== request.tool_use_id), + ) + }, + onAllow(updatedInput, _permissionUpdates, _feedback) { + const response: RemotePermissionResponse = { + behavior: 'allow', + updatedInput, + } + manager.respondToPermissionRequest(requestId, response) + setToolUseConfirmQueue(queue => + queue.filter(item => item.toolUseID !== request.tool_use_id), + ) + // Resume loading indicator after approving + setIsLoading(true) + }, + onReject(feedback?: string) { + const response: RemotePermissionResponse = { + behavior: 'deny', + message: feedback ?? 'User denied permission', + } + manager.respondToPermissionRequest(requestId, response) + setToolUseConfirmQueue(queue => + queue.filter(item => item.toolUseID !== request.tool_use_id), + ) + }, + async recheckPermission() { + // No-op for remote — permission state is on the container + }, + } + + setToolUseConfirmQueue(queue => [...queue, toolUseConfirm]) + // Pause loading indicator while waiting for permission + setIsLoading(false) + }, + onPermissionCancelled: (requestId, toolUseId) => { + logForDebugging( + `[useRemoteSession] Permission request cancelled: ${requestId}`, + ) + const idToRemove = toolUseId ?? requestId + setToolUseConfirmQueue(queue => + queue.filter(item => item.toolUseID !== idToRemove), + ) + setIsLoading(true) + }, + onConnected: () => { + logForDebugging('[useRemoteSession] Connected') + setConnStatus('connected') + }, + onReconnecting: () => { + logForDebugging('[useRemoteSession] Reconnecting') + setConnStatus('reconnecting') + // WS gap = we may miss task_notification events. Clear rather than + // drift high forever. Undercounts tasks that span the gap; accepted. + runningTaskIdsRef.current.clear() + writeTaskCount() + // Same for tool_use IDs: missed tool_result during the gap would + // leave stale spinner state forever. + setInProgressToolUseIDs?.(prev => (prev.size > 0 ? new Set() : prev)) + }, + onDisconnected: () => { + logForDebugging('[useRemoteSession] Disconnected') + setConnStatus('disconnected') + setIsLoading(false) + runningTaskIdsRef.current.clear() + writeTaskCount() + setInProgressToolUseIDs?.(prev => (prev.size > 0 ? new Set() : prev)) + }, + onError: error => { + logForDebugging(`[useRemoteSession] Error: ${error.message}`) + }, + }) + + managerRef.current = manager + manager.connect() + + return () => { + logForDebugging('[useRemoteSession] Cleanup - disconnecting') + // Clear any pending timeout + if (responseTimeoutRef.current) { + clearTimeout(responseTimeoutRef.current) + responseTimeoutRef.current = null + } + manager.disconnect() + managerRef.current = null + } + }, [ + config, + setMessages, + setIsLoading, + onInit, + setToolUseConfirmQueue, + setStreamingToolUses, + setStreamMode, + setInProgressToolUseIDs, + setConnStatus, + writeTaskCount, + ]) + + // Send a user message to the remote session + const sendMessage = useCallback( + async ( + content: RemoteMessageContent, + opts?: { uuid?: string }, + ): Promise => { + const manager = managerRef.current + if (!manager) { + logForDebugging('[useRemoteSession] Cannot send - no manager') + return false + } + + // Clear any existing timeout + if (responseTimeoutRef.current) { + clearTimeout(responseTimeoutRef.current) + } + + setIsLoading(true) + + // Track locally-added message UUIDs so the WS echo can be filtered. + // Must record BEFORE the POST to close the race where the echo arrives + // before the POST promise resolves. + if (opts?.uuid) sentUUIDsRef.current.add(opts.uuid) + + const success = await manager.sendMessage(content, opts) + + if (!success) { + // No need to undo the pre-POST add — BoundedUUIDSet's ring evicts it. + setIsLoading(false) + return false + } + + // Update the session title after the first message when no initial prompt was provided. + // This gives the session a meaningful title on claude.ai instead of "Background task". + // Skip in viewerOnly mode — the remote agent owns the session title. + if ( + !hasUpdatedTitleRef.current && + config && + !config.hasInitialPrompt && + !config.viewerOnly + ) { + hasUpdatedTitleRef.current = true + const sessionId = config.sessionId + // Extract plain text from content (may be string or content block array) + const description = + typeof content === 'string' + ? content + : extractTextContent(content, ' ') + if (description) { + // generateSessionTitle never rejects (wraps body in try/catch, + // returns null on failure), so no .catch needed on this chain. + void generateSessionTitle( + description, + new AbortController().signal, + ).then(title => { + void updateSessionTitle( + sessionId, + title ?? truncateToWidth(description, 75), + ) + }) + } + } + + // Start timeout to detect stuck sessions. Skip in viewerOnly mode — + // the remote agent may be idle-shut and take >60s to respawn. + // Use a longer timeout when the remote session is compacting, since + // the CLI worker is busy with an API call and won't emit messages. + if (!config?.viewerOnly) { + const timeoutMs = isCompactingRef.current + ? COMPACTION_TIMEOUT_MS + : RESPONSE_TIMEOUT_MS + responseTimeoutRef.current = setTimeout( + (setMessages, manager) => { + logForDebugging( + '[useRemoteSession] Response timeout - attempting reconnect', + ) + // Add a warning message to the conversation + const warningMessage = createSystemMessage( + 'Remote session may be unresponsive. Attempting to reconnect…', + 'warning', + ) + setMessages(prev => [...prev, warningMessage]) + + // Attempt to reconnect the WebSocket - the subscription may have become stale + manager.reconnect() + }, + timeoutMs, + setMessages, + manager, + ) + } + + return success + }, + [config, setIsLoading, setMessages], + ) + + // Cancel the current request on the remote session + const cancelRequest = useCallback(() => { + // Clear any pending timeout + if (responseTimeoutRef.current) { + clearTimeout(responseTimeoutRef.current) + responseTimeoutRef.current = null + } + + // Send interrupt signal to CCR. Skip in viewerOnly mode — Ctrl+C + // should never interrupt the remote agent. + if (!config?.viewerOnly) { + managerRef.current?.cancelSession() + } + + setIsLoading(false) + }, [config, setIsLoading]) + + // Disconnect from the session + const disconnect = useCallback(() => { + // Clear any pending timeout + if (responseTimeoutRef.current) { + clearTimeout(responseTimeoutRef.current) + responseTimeoutRef.current = null + } + managerRef.current?.disconnect() + managerRef.current = null + }, []) + + // All four fields are already stable (boolean derived from a prop that + // doesn't change mid-session, three useCallbacks with stable deps). The + // result object is consumed by REPL's onSubmit useCallback deps — without + // memoization the fresh literal invalidates onSubmit on every REPL render, + // which in turn churns PromptInput's props and downstream memoization. + return useMemo( + () => ({ isRemoteMode, sendMessage, cancelRequest, disconnect }), + [isRemoteMode, sendMessage, cancelRequest, disconnect], + ) +} diff --git a/hooks/useReplBridge.tsx b/hooks/useReplBridge.tsx new file mode 100644 index 0000000..7c10ac6 --- /dev/null +++ b/hooks/useReplBridge.tsx @@ -0,0 +1,723 @@ +import { feature } from 'bun:bundle'; +import React, { useCallback, useEffect, useRef } from 'react'; +import { setMainLoopModelOverride } from '../bootstrap/state.js'; +import { type BridgePermissionCallbacks, type BridgePermissionResponse, isBridgePermissionResponse } from '../bridge/bridgePermissionCallbacks.js'; +import { buildBridgeConnectUrl } from '../bridge/bridgeStatusUtil.js'; +import { extractInboundMessageFields } from '../bridge/inboundMessages.js'; +import type { BridgeState, ReplBridgeHandle } from '../bridge/replBridge.js'; +import { setReplBridgeHandle } from '../bridge/replBridgeHandle.js'; +import type { Command } from '../commands.js'; +import { getSlashCommandToolSkills, isBridgeSafeCommand } from '../commands.js'; +import { getRemoteSessionUrl } from '../constants/product.js'; +import { useNotifications } from '../context/notifications.js'; +import type { PermissionMode, SDKMessage } from '../entrypoints/agentSdkTypes.js'; +import type { SDKControlResponse } from '../entrypoints/sdk/controlTypes.js'; +import { Text } from '../ink.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'; +import { useAppState, useAppStateStore, useSetAppState } from '../state/AppState.js'; +import type { Message } from '../types/message.js'; +import { getCwd } from '../utils/cwd.js'; +import { logForDebugging } from '../utils/debug.js'; +import { errorMessage } from '../utils/errors.js'; +import { enqueue } from '../utils/messageQueueManager.js'; +import { buildSystemInitMessage } from '../utils/messages/systemInit.js'; +import { createBridgeStatusMessage, createSystemMessage } from '../utils/messages.js'; +import { getAutoModeUnavailableNotification, getAutoModeUnavailableReason, isAutoModeGateEnabled, isBypassPermissionsModeDisabled, transitionPermissionMode } from '../utils/permissions/permissionSetup.js'; +import { getLeaderToolUseConfirmQueue } from '../utils/swarm/leaderPermissionBridge.js'; + +/** How long after a failure before replBridgeEnabled is auto-cleared (stops retries). */ +export const BRIDGE_FAILURE_DISMISS_MS = 10_000; + +/** + * Max consecutive initReplBridge failures before the hook stops re-attempting + * for the session lifetime. Guards against paths that flip replBridgeEnabled + * back on after auto-disable (settings sync, /remote-control, config tool) + * when the underlying OAuth is unrecoverable — each re-attempt is another + * guaranteed 401 against POST /v1/environments/bridge. Datadog 2026-03-08: + * top stuck client generated 2,879 × 401/day alone (17% of all 401s on the + * route). + */ +const MAX_CONSECUTIVE_INIT_FAILURES = 3; + +/** + * Hook that initializes an always-on bridge connection in the background + * and writes new user/assistant messages to the bridge session. + * + * Silently skips if bridge is not enabled or user is not OAuth-authenticated. + * + * Watches AppState.replBridgeEnabled — when toggled off (via /config or footer), + * the bridge is torn down. When toggled back on, it re-initializes. + * + * Inbound messages from claude.ai are injected into the REPL via queuedCommands. + */ +export function useReplBridge(messages: Message[], setMessages: (action: React.SetStateAction) => void, abortControllerRef: React.RefObject, commands: readonly Command[], mainLoopModel: string): { + sendBridgeResult: () => void; +} { + const handleRef = useRef(null); + const teardownPromiseRef = useRef | undefined>(undefined); + const lastWrittenIndexRef = useRef(0); + // Tracks UUIDs already flushed as initial messages. Persists across + // bridge reconnections so Bridge #2+ only sends new messages — sending + // duplicate UUIDs causes the server to kill the WebSocket. + const flushedUUIDsRef = useRef(new Set()); + const failureTimeoutRef = useRef | undefined>(undefined); + // Persists across effect re-runs (unlike the effect's local state). Reset + // only on successful init. Hits MAX_CONSECUTIVE_INIT_FAILURES → fuse blown + // for the session, regardless of replBridgeEnabled re-toggling. + const consecutiveFailuresRef = useRef(0); + const setAppState = useSetAppState(); + const commandsRef = useRef(commands); + commandsRef.current = commands; + const mainLoopModelRef = useRef(mainLoopModel); + mainLoopModelRef.current = mainLoopModel; + const messagesRef = useRef(messages); + messagesRef.current = messages; + const store = useAppStateStore(); + const { + addNotification + } = useNotifications(); + const replBridgeEnabled = feature('BRIDGE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s => s.replBridgeEnabled) : false; + const replBridgeConnected = feature('BRIDGE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s_0 => s_0.replBridgeConnected) : false; + const replBridgeOutboundOnly = feature('BRIDGE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s_1 => s_1.replBridgeOutboundOnly) : false; + const replBridgeInitialName = feature('BRIDGE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s_2 => s_2.replBridgeInitialName) : undefined; + + // Initialize/teardown bridge when enabled state changes. + // Passes current messages as initialMessages so the remote session + // starts with the existing conversation context (e.g. from /bridge). + useEffect(() => { + // feature() check must use positive pattern for dead code elimination — + // negative pattern (if (!feature(...)) return) does NOT eliminate + // dynamic imports below. + if (feature('BRIDGE_MODE')) { + if (!replBridgeEnabled) return; + const outboundOnly = replBridgeOutboundOnly; + function notifyBridgeFailed(detail?: string): void { + if (outboundOnly) return; + addNotification({ + key: 'bridge-failed', + jsx: <> + Remote Control failed + {detail && · {detail}} + , + priority: 'immediate' + }); + } + if (consecutiveFailuresRef.current >= MAX_CONSECUTIVE_INIT_FAILURES) { + logForDebugging(`[bridge:repl] Hook: ${consecutiveFailuresRef.current} consecutive init failures, not retrying this session`); + // Clear replBridgeEnabled so /remote-control doesn't mistakenly show + // BridgeDisconnectDialog for a bridge that never connected. + const fuseHint = 'disabled after repeated failures · restart to retry'; + notifyBridgeFailed(fuseHint); + setAppState(prev => { + if (prev.replBridgeError === fuseHint && !prev.replBridgeEnabled) return prev; + return { + ...prev, + replBridgeError: fuseHint, + replBridgeEnabled: false + }; + }); + return; + } + let cancelled = false; + // Capture messages.length now so we don't re-send initial messages + // through writeMessages after the bridge connects. + const initialMessageCount = messages.length; + void (async () => { + try { + // Wait for any in-progress teardown to complete before registering + // a new environment. Without this, the deregister HTTP call from + // the previous teardown races with the new register call, and the + // server may tear down the freshly-created environment. + if (teardownPromiseRef.current) { + logForDebugging('[bridge:repl] Hook: waiting for previous teardown to complete before re-init'); + await teardownPromiseRef.current; + teardownPromiseRef.current = undefined; + logForDebugging('[bridge:repl] Hook: previous teardown complete, proceeding with re-init'); + } + if (cancelled) return; + + // Dynamic import so the module is tree-shaken in external builds + const { + initReplBridge + } = await import('../bridge/initReplBridge.js'); + const { + shouldShowAppUpgradeMessage + } = await import('../bridge/envLessBridgeConfig.js'); + + // Assistant mode: perpetual bridge session — claude.ai shows one + // continuous conversation across CLI restarts instead of a new + // session per invocation. initBridgeCore reads bridge-pointer.json + // (the same crash-recovery file #20735 added) and reuses its + // {environmentId, sessionId} via reuseEnvironmentId + + // api.reconnectSession(). Teardown skips archive/deregister/ + // pointer-clear so the session survives clean exits, not just + // crashes. Non-assistant bridges clear the pointer on teardown + // (crash-recovery only). + let perpetual = false; + if (feature('KAIROS')) { + const { + isAssistantMode + } = await import('../assistant/index.js'); + perpetual = isAssistantMode(); + } + + // When a user message arrives from claude.ai, inject it into the REPL. + // Preserves the original UUID so that when the message is forwarded + // back to CCR, it matches the original — avoiding duplicate messages. + // + // Async because file_attachments (if present) need a network fetch + + // disk write before we enqueue with the @path prefix. Caller doesn't + // await — messages with attachments just land in the queue slightly + // later, which is fine (web messages aren't rapid-fire). + async function handleInboundMessage(msg: SDKMessage): Promise { + try { + const fields = extractInboundMessageFields(msg); + if (!fields) return; + const { + uuid + } = fields; + + // Dynamic import keeps the bridge code out of non-BRIDGE_MODE builds. + const { + resolveAndPrepend + } = await import('../bridge/inboundAttachments.js'); + let sanitized = fields.content; + if (feature('KAIROS_GITHUB_WEBHOOKS')) { + /* eslint-disable @typescript-eslint/no-require-imports */ + const { + sanitizeInboundWebhookContent + } = require('../bridge/webhookSanitizer.js') as typeof import('../bridge/webhookSanitizer.js'); + /* eslint-enable @typescript-eslint/no-require-imports */ + sanitized = sanitizeInboundWebhookContent(fields.content); + } + const content = await resolveAndPrepend(msg, sanitized); + const preview = typeof content === 'string' ? content.slice(0, 80) : `[${content.length} content blocks]`; + logForDebugging(`[bridge:repl] Injecting inbound user message: ${preview}${uuid ? ` uuid=${uuid}` : ''}`); + enqueue({ + value: content, + mode: 'prompt' as const, + uuid, + // skipSlashCommands stays true as defense-in-depth — + // processUserInputBase overrides it internally when bridgeOrigin + // is set AND the resolved command passes isBridgeSafeCommand. + // This keeps exit-word suppression and immediate-command blocks + // intact for any code path that checks skipSlashCommands directly. + skipSlashCommands: true, + bridgeOrigin: true + }); + } catch (e) { + logForDebugging(`[bridge:repl] handleInboundMessage failed: ${e}`, { + level: 'error' + }); + } + } + + // State change callback — maps bridge lifecycle events to AppState. + function handleStateChange(state: BridgeState, detail_0?: string): void { + if (cancelled) return; + if (outboundOnly) { + logForDebugging(`[bridge:repl] Mirror state=${state}${detail_0 ? ` detail=${detail_0}` : ''}`); + // Sync replBridgeConnected so the forwarding effect starts/stops + // writing as the transport comes up or dies. + if (state === 'failed') { + setAppState(prev_3 => { + if (!prev_3.replBridgeConnected) return prev_3; + return { + ...prev_3, + replBridgeConnected: false + }; + }); + } else if (state === 'ready' || state === 'connected') { + setAppState(prev_4 => { + if (prev_4.replBridgeConnected) return prev_4; + return { + ...prev_4, + replBridgeConnected: true + }; + }); + } + return; + } + const handle = handleRef.current; + switch (state) { + case 'ready': + setAppState(prev_9 => { + const connectUrl = handle && handle.environmentId !== '' ? buildBridgeConnectUrl(handle.environmentId, handle.sessionIngressUrl) : prev_9.replBridgeConnectUrl; + const sessionUrl = handle ? getRemoteSessionUrl(handle.bridgeSessionId, handle.sessionIngressUrl) : prev_9.replBridgeSessionUrl; + const envId = handle?.environmentId; + const sessionId = handle?.bridgeSessionId; + if (prev_9.replBridgeConnected && !prev_9.replBridgeSessionActive && !prev_9.replBridgeReconnecting && prev_9.replBridgeConnectUrl === connectUrl && prev_9.replBridgeSessionUrl === sessionUrl && prev_9.replBridgeEnvironmentId === envId && prev_9.replBridgeSessionId === sessionId) { + return prev_9; + } + return { + ...prev_9, + replBridgeConnected: true, + replBridgeSessionActive: false, + replBridgeReconnecting: false, + replBridgeConnectUrl: connectUrl, + replBridgeSessionUrl: sessionUrl, + replBridgeEnvironmentId: envId, + replBridgeSessionId: sessionId, + replBridgeError: undefined + }; + }); + break; + case 'connected': + { + setAppState(prev_8 => { + if (prev_8.replBridgeSessionActive) return prev_8; + return { + ...prev_8, + replBridgeConnected: true, + replBridgeSessionActive: true, + replBridgeReconnecting: false, + replBridgeError: undefined + }; + }); + // Send system/init so remote clients (web/iOS/Android) get + // session metadata. REPL uses query() directly — never hits + // QueryEngine's SDKMessage layer — so this is the only path + // to put system/init on the REPL-bridge wire. Skills load is + // async (memoized, cheap after REPL startup); fire-and-forget + // so the connected-state transition isn't blocked. + if (getFeatureValue_CACHED_MAY_BE_STALE('tengu_bridge_system_init', false)) { + void (async () => { + try { + const skills = await getSlashCommandToolSkills(getCwd()); + if (cancelled) return; + const state_0 = store.getState(); + handleRef.current?.writeSdkMessages([buildSystemInitMessage({ + // tools/mcpClients/plugins redacted for REPL-bridge: + // MCP-prefixed tool names and server names leak which + // integrations the user has wired up; plugin paths leak + // raw filesystem paths (username, project structure). + // CCR v2 persists SDK messages to Spanner — users who + // tap "Connect from phone" may not expect these on + // Anthropic's servers. QueryEngine (SDK) still emits + // full lists — SDK consumers expect full telemetry. + tools: [], + mcpClients: [], + model: mainLoopModelRef.current, + permissionMode: state_0.toolPermissionContext.mode as PermissionMode, + // TODO: avoid the cast + // Remote clients can only invoke bridge-safe commands — + // advertising unsafe ones (local-jsx, unallowed local) + // would let mobile/web attempt them and hit errors. + commands: commandsRef.current.filter(isBridgeSafeCommand), + agents: state_0.agentDefinitions.activeAgents, + skills, + plugins: [], + fastMode: state_0.fastMode + })]); + } catch (err_0) { + logForDebugging(`[bridge:repl] Failed to send system/init: ${errorMessage(err_0)}`, { + level: 'error' + }); + } + })(); + } + break; + } + case 'reconnecting': + setAppState(prev_7 => { + if (prev_7.replBridgeReconnecting) return prev_7; + return { + ...prev_7, + replBridgeReconnecting: true, + replBridgeSessionActive: false + }; + }); + break; + case 'failed': + // Clear any previous failure dismiss timer + clearTimeout(failureTimeoutRef.current); + notifyBridgeFailed(detail_0); + setAppState(prev_5 => ({ + ...prev_5, + replBridgeError: detail_0, + replBridgeReconnecting: false, + replBridgeSessionActive: false, + replBridgeConnected: false + })); + // Auto-disable after timeout so the hook stops retrying. + failureTimeoutRef.current = setTimeout(() => { + if (cancelled) return; + failureTimeoutRef.current = undefined; + setAppState(prev_6 => { + if (!prev_6.replBridgeError) return prev_6; + return { + ...prev_6, + replBridgeEnabled: false, + replBridgeError: undefined + }; + }); + }, BRIDGE_FAILURE_DISMISS_MS); + break; + } + } + + // Map of pending bridge permission response handlers, keyed by request_id. + // Each entry is an onResponse handler waiting for CCR to reply. + const pendingPermissionHandlers = new Map void>(); + + // Dispatch incoming control_response messages to registered handlers + function handlePermissionResponse(msg_0: SDKControlResponse): void { + const requestId = msg_0.response?.request_id; + if (!requestId) return; + const handler = pendingPermissionHandlers.get(requestId); + if (!handler) { + logForDebugging(`[bridge:repl] No handler for control_response request_id=${requestId}`); + return; + } + pendingPermissionHandlers.delete(requestId); + // Extract the permission decision from the control_response payload + const inner = msg_0.response; + if (inner.subtype === 'success' && inner.response && isBridgePermissionResponse(inner.response)) { + handler(inner.response); + } + } + const handle_0 = await initReplBridge({ + outboundOnly, + tags: outboundOnly ? ['ccr-mirror'] : undefined, + onInboundMessage: handleInboundMessage, + onPermissionResponse: handlePermissionResponse, + onInterrupt() { + abortControllerRef.current?.abort(); + }, + onSetModel(model) { + const resolved = model === 'default' ? null : model ?? null; + setMainLoopModelOverride(resolved); + setAppState(prev_10 => { + if (prev_10.mainLoopModelForSession === resolved) return prev_10; + return { + ...prev_10, + mainLoopModelForSession: resolved + }; + }); + }, + onSetMaxThinkingTokens(maxTokens) { + const enabled = maxTokens !== null; + setAppState(prev_11 => { + if (prev_11.thinkingEnabled === enabled) return prev_11; + return { + ...prev_11, + thinkingEnabled: enabled + }; + }); + }, + onSetPermissionMode(mode) { + // Policy guards MUST fire before transitionPermissionMode — + // its internal auto-gate check is a defensive throw (with a + // setAutoModeActive(true) side-effect BEFORE the throw) rather + // than a graceful reject. Letting that throw escape would: + // (1) leave STATE.autoModeActive=true while the mode is + // unchanged (3-way invariant violation per src/CLAUDE.md) + // (2) fail to send a control_response → server kills WS + // These mirror print.ts handleSetPermissionMode; the bridge + // can't import the checks directly (bootstrap-isolation), so + // it relies on this verdict to emit the error response. + if (mode === 'bypassPermissions') { + if (isBypassPermissionsModeDisabled()) { + return { + ok: false, + error: 'Cannot set permission mode to bypassPermissions because it is disabled by settings or configuration' + }; + } + if (!store.getState().toolPermissionContext.isBypassPermissionsModeAvailable) { + return { + ok: false, + error: 'Cannot set permission mode to bypassPermissions because the session was not launched with --dangerously-skip-permissions' + }; + } + } + if (feature('TRANSCRIPT_CLASSIFIER') && mode === 'auto' && !isAutoModeGateEnabled()) { + const reason = getAutoModeUnavailableReason(); + return { + ok: false, + error: reason ? `Cannot set permission mode to auto: ${getAutoModeUnavailableNotification(reason)}` : 'Cannot set permission mode to auto' + }; + } + // Guards passed — apply via the centralized transition so + // prePlanMode stashing and auto-mode state sync all fire. + setAppState(prev_12 => { + const current = prev_12.toolPermissionContext.mode; + if (current === mode) return prev_12; + const next = transitionPermissionMode(current, mode, prev_12.toolPermissionContext); + return { + ...prev_12, + toolPermissionContext: { + ...next, + mode + } + }; + }); + // Recheck queued permission prompts now that mode changed. + setImmediate(() => { + getLeaderToolUseConfirmQueue()?.(currentQueue => { + currentQueue.forEach(item => { + void item.recheckPermission(); + }); + return currentQueue; + }); + }); + return { + ok: true + }; + }, + onStateChange: handleStateChange, + initialMessages: messages.length > 0 ? messages : undefined, + getMessages: () => messagesRef.current, + previouslyFlushedUUIDs: flushedUUIDsRef.current, + initialName: replBridgeInitialName, + perpetual + }); + if (cancelled) { + // Effect was cancelled while initReplBridge was in flight. + // Tear down the handle to avoid leaking resources (poll loop, + // WebSocket, registered environment, cleanup callback). + logForDebugging(`[bridge:repl] Hook: init cancelled during flight, tearing down${handle_0 ? ` env=${handle_0.environmentId}` : ''}`); + if (handle_0) { + void handle_0.teardown(); + } + return; + } + if (!handle_0) { + // initReplBridge returned null — a precondition failed. For most + // cases (no_oauth, policy_denied, etc.) onStateChange('failed') + // already fired with a specific hint. The GrowthBook-gate-off case + // is intentionally silent — not a failure, just not rolled out. + consecutiveFailuresRef.current++; + logForDebugging(`[bridge:repl] Init returned null (precondition or session creation failed); consecutive failures: ${consecutiveFailuresRef.current}`); + clearTimeout(failureTimeoutRef.current); + setAppState(prev_13 => ({ + ...prev_13, + replBridgeError: prev_13.replBridgeError ?? 'check debug logs for details' + })); + failureTimeoutRef.current = setTimeout(() => { + if (cancelled) return; + failureTimeoutRef.current = undefined; + setAppState(prev_14 => { + if (!prev_14.replBridgeError) return prev_14; + return { + ...prev_14, + replBridgeEnabled: false, + replBridgeError: undefined + }; + }); + }, BRIDGE_FAILURE_DISMISS_MS); + return; + } + handleRef.current = handle_0; + setReplBridgeHandle(handle_0); + consecutiveFailuresRef.current = 0; + // Skip initial messages in the forwarding effect — they were + // already loaded as session events during creation. + lastWrittenIndexRef.current = initialMessageCount; + if (outboundOnly) { + setAppState(prev_15 => { + if (prev_15.replBridgeConnected && prev_15.replBridgeSessionId === handle_0.bridgeSessionId) return prev_15; + return { + ...prev_15, + replBridgeConnected: true, + replBridgeSessionId: handle_0.bridgeSessionId, + replBridgeSessionUrl: undefined, + replBridgeConnectUrl: undefined, + replBridgeError: undefined + }; + }); + logForDebugging(`[bridge:repl] Mirror initialized, session=${handle_0.bridgeSessionId}`); + } else { + // Build bridge permission callbacks so the interactive permission + // handler can race bridge responses against local user interaction. + const permissionCallbacks: BridgePermissionCallbacks = { + sendRequest(requestId_0, toolName, input, toolUseId, description, permissionSuggestions, blockedPath) { + handle_0.sendControlRequest({ + type: 'control_request', + request_id: requestId_0, + request: { + subtype: 'can_use_tool', + tool_name: toolName, + input, + tool_use_id: toolUseId, + description, + ...(permissionSuggestions ? { + permission_suggestions: permissionSuggestions + } : {}), + ...(blockedPath ? { + blocked_path: blockedPath + } : {}) + } + }); + }, + sendResponse(requestId_1, response) { + const payload: Record = { + ...response + }; + handle_0.sendControlResponse({ + type: 'control_response', + response: { + subtype: 'success', + request_id: requestId_1, + response: payload + } + }); + }, + cancelRequest(requestId_2) { + handle_0.sendControlCancelRequest(requestId_2); + }, + onResponse(requestId_3, handler_0) { + pendingPermissionHandlers.set(requestId_3, handler_0); + return () => { + pendingPermissionHandlers.delete(requestId_3); + }; + } + }; + setAppState(prev_16 => ({ + ...prev_16, + replBridgePermissionCallbacks: permissionCallbacks + })); + const url = getRemoteSessionUrl(handle_0.bridgeSessionId, handle_0.sessionIngressUrl); + // environmentId === '' signals the v2 env-less path. buildBridgeConnectUrl + // builds an env-specific connect URL, which doesn't exist without an env. + const hasEnv = handle_0.environmentId !== ''; + const connectUrl_0 = hasEnv ? buildBridgeConnectUrl(handle_0.environmentId, handle_0.sessionIngressUrl) : undefined; + setAppState(prev_17 => { + if (prev_17.replBridgeConnected && prev_17.replBridgeSessionUrl === url) { + return prev_17; + } + return { + ...prev_17, + replBridgeConnected: true, + replBridgeSessionUrl: url, + replBridgeConnectUrl: connectUrl_0 ?? prev_17.replBridgeConnectUrl, + replBridgeEnvironmentId: handle_0.environmentId, + replBridgeSessionId: handle_0.bridgeSessionId, + replBridgeError: undefined + }; + }); + + // Show bridge status with URL in the transcript. perpetual (KAIROS + // assistant mode) falls back to v1 at initReplBridge.ts — skip the + // v2-only upgrade nudge for them. Own try/catch so a cosmetic + // GrowthBook hiccup doesn't hit the outer init-failure handler. + const upgradeNudge = !perpetual ? await shouldShowAppUpgradeMessage().catch(() => false) : false; + if (cancelled) return; + setMessages(prev_18 => [...prev_18, createBridgeStatusMessage(url, upgradeNudge ? 'Please upgrade to the latest version of the Claude mobile app to see your Remote Control sessions.' : undefined)]); + logForDebugging(`[bridge:repl] Hook initialized, session=${handle_0.bridgeSessionId}`); + } + } catch (err) { + // Never crash the REPL — surface the error in the UI. + // Check cancelled first (symmetry with the !handle path at line ~386): + // if initReplBridge threw during rapid toggle-off (in-flight network + // error), don't count that toward the fuse or spam a stale error + // into the UI. Also fixes pre-existing spurious setAppState/ + // setMessages on cancelled throws. + if (cancelled) return; + consecutiveFailuresRef.current++; + const errMsg = errorMessage(err); + logForDebugging(`[bridge:repl] Init failed: ${errMsg}; consecutive failures: ${consecutiveFailuresRef.current}`); + clearTimeout(failureTimeoutRef.current); + notifyBridgeFailed(errMsg); + setAppState(prev_0 => ({ + ...prev_0, + replBridgeError: errMsg + })); + failureTimeoutRef.current = setTimeout(() => { + if (cancelled) return; + failureTimeoutRef.current = undefined; + setAppState(prev_1 => { + if (!prev_1.replBridgeError) return prev_1; + return { + ...prev_1, + replBridgeEnabled: false, + replBridgeError: undefined + }; + }); + }, BRIDGE_FAILURE_DISMISS_MS); + if (!outboundOnly) { + setMessages(prev_2 => [...prev_2, createSystemMessage(`Remote Control failed to connect: ${errMsg}`, 'warning')]); + } + } + })(); + return () => { + cancelled = true; + clearTimeout(failureTimeoutRef.current); + failureTimeoutRef.current = undefined; + if (handleRef.current) { + logForDebugging(`[bridge:repl] Hook cleanup: starting teardown for env=${handleRef.current.environmentId} session=${handleRef.current.bridgeSessionId}`); + teardownPromiseRef.current = handleRef.current.teardown(); + handleRef.current = null; + setReplBridgeHandle(null); + } + setAppState(prev_19 => { + if (!prev_19.replBridgeConnected && !prev_19.replBridgeSessionActive && !prev_19.replBridgeError) { + return prev_19; + } + return { + ...prev_19, + replBridgeConnected: false, + replBridgeSessionActive: false, + replBridgeReconnecting: false, + replBridgeConnectUrl: undefined, + replBridgeSessionUrl: undefined, + replBridgeEnvironmentId: undefined, + replBridgeSessionId: undefined, + replBridgeError: undefined, + replBridgePermissionCallbacks: undefined + }; + }); + lastWrittenIndexRef.current = 0; + }; + } + }, [replBridgeEnabled, replBridgeOutboundOnly, setAppState, setMessages, addNotification]); + + // Write new messages as they appear. + // Also re-runs when replBridgeConnected changes (bridge finishes init), + // so any messages that arrived before the bridge was ready get written. + useEffect(() => { + // Positive feature() guard — see first useEffect comment + if (feature('BRIDGE_MODE')) { + if (!replBridgeConnected) return; + const handle_1 = handleRef.current; + if (!handle_1) return; + + // Clamp the index in case messages were compacted (array shortened). + // After compaction the ref could exceed messages.length, and without + // clamping no new messages would be forwarded. + if (lastWrittenIndexRef.current > messages.length) { + logForDebugging(`[bridge:repl] Compaction detected: lastWrittenIndex=${lastWrittenIndexRef.current} > messages.length=${messages.length}, clamping`); + } + const startIndex = Math.min(lastWrittenIndexRef.current, messages.length); + + // Collect new messages since last write + const newMessages: Message[] = []; + for (let i = startIndex; i < messages.length; i++) { + const msg_1 = messages[i]; + if (msg_1 && (msg_1.type === 'user' || msg_1.type === 'assistant' || msg_1.type === 'system' && msg_1.subtype === 'local_command')) { + newMessages.push(msg_1); + } + } + lastWrittenIndexRef.current = messages.length; + if (newMessages.length > 0) { + handle_1.writeMessages(newMessages); + } + } + }, [messages, replBridgeConnected]); + const sendBridgeResult = useCallback(() => { + if (feature('BRIDGE_MODE')) { + handleRef.current?.sendResult(); + } + }, []); + return { + sendBridgeResult + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZUVmZmVjdCIsInVzZVJlZiIsInNldE1haW5Mb29wTW9kZWxPdmVycmlkZSIsIkJyaWRnZVBlcm1pc3Npb25DYWxsYmFja3MiLCJCcmlkZ2VQZXJtaXNzaW9uUmVzcG9uc2UiLCJpc0JyaWRnZVBlcm1pc3Npb25SZXNwb25zZSIsImJ1aWxkQnJpZGdlQ29ubmVjdFVybCIsImV4dHJhY3RJbmJvdW5kTWVzc2FnZUZpZWxkcyIsIkJyaWRnZVN0YXRlIiwiUmVwbEJyaWRnZUhhbmRsZSIsInNldFJlcGxCcmlkZ2VIYW5kbGUiLCJDb21tYW5kIiwiZ2V0U2xhc2hDb21tYW5kVG9vbFNraWxscyIsImlzQnJpZGdlU2FmZUNvbW1hbmQiLCJnZXRSZW1vdGVTZXNzaW9uVXJsIiwidXNlTm90aWZpY2F0aW9ucyIsIlBlcm1pc3Npb25Nb2RlIiwiU0RLTWVzc2FnZSIsIlNES0NvbnRyb2xSZXNwb25zZSIsIlRleHQiLCJnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSIsInVzZUFwcFN0YXRlIiwidXNlQXBwU3RhdGVTdG9yZSIsInVzZVNldEFwcFN0YXRlIiwiTWVzc2FnZSIsImdldEN3ZCIsImxvZ0ZvckRlYnVnZ2luZyIsImVycm9yTWVzc2FnZSIsImVucXVldWUiLCJidWlsZFN5c3RlbUluaXRNZXNzYWdlIiwiY3JlYXRlQnJpZGdlU3RhdHVzTWVzc2FnZSIsImNyZWF0ZVN5c3RlbU1lc3NhZ2UiLCJnZXRBdXRvTW9kZVVuYXZhaWxhYmxlTm90aWZpY2F0aW9uIiwiZ2V0QXV0b01vZGVVbmF2YWlsYWJsZVJlYXNvbiIsImlzQXV0b01vZGVHYXRlRW5hYmxlZCIsImlzQnlwYXNzUGVybWlzc2lvbnNNb2RlRGlzYWJsZWQiLCJ0cmFuc2l0aW9uUGVybWlzc2lvbk1vZGUiLCJnZXRMZWFkZXJUb29sVXNlQ29uZmlybVF1ZXVlIiwiQlJJREdFX0ZBSUxVUkVfRElTTUlTU19NUyIsIk1BWF9DT05TRUNVVElWRV9JTklUX0ZBSUxVUkVTIiwidXNlUmVwbEJyaWRnZSIsIm1lc3NhZ2VzIiwic2V0TWVzc2FnZXMiLCJhY3Rpb24iLCJTZXRTdGF0ZUFjdGlvbiIsImFib3J0Q29udHJvbGxlclJlZiIsIlJlZk9iamVjdCIsIkFib3J0Q29udHJvbGxlciIsImNvbW1hbmRzIiwibWFpbkxvb3BNb2RlbCIsInNlbmRCcmlkZ2VSZXN1bHQiLCJoYW5kbGVSZWYiLCJ0ZWFyZG93blByb21pc2VSZWYiLCJQcm9taXNlIiwidW5kZWZpbmVkIiwibGFzdFdyaXR0ZW5JbmRleFJlZiIsImZsdXNoZWRVVUlEc1JlZiIsIlNldCIsImZhaWx1cmVUaW1lb3V0UmVmIiwiUmV0dXJuVHlwZSIsInNldFRpbWVvdXQiLCJjb25zZWN1dGl2ZUZhaWx1cmVzUmVmIiwic2V0QXBwU3RhdGUiLCJjb21tYW5kc1JlZiIsImN1cnJlbnQiLCJtYWluTG9vcE1vZGVsUmVmIiwibWVzc2FnZXNSZWYiLCJzdG9yZSIsImFkZE5vdGlmaWNhdGlvbiIsInJlcGxCcmlkZ2VFbmFibGVkIiwicyIsInJlcGxCcmlkZ2VDb25uZWN0ZWQiLCJyZXBsQnJpZGdlT3V0Ym91bmRPbmx5IiwicmVwbEJyaWRnZUluaXRpYWxOYW1lIiwib3V0Ym91bmRPbmx5Iiwibm90aWZ5QnJpZGdlRmFpbGVkIiwiZGV0YWlsIiwia2V5IiwianN4IiwicHJpb3JpdHkiLCJmdXNlSGludCIsInByZXYiLCJyZXBsQnJpZGdlRXJyb3IiLCJjYW5jZWxsZWQiLCJpbml0aWFsTWVzc2FnZUNvdW50IiwibGVuZ3RoIiwiaW5pdFJlcGxCcmlkZ2UiLCJzaG91bGRTaG93QXBwVXBncmFkZU1lc3NhZ2UiLCJwZXJwZXR1YWwiLCJpc0Fzc2lzdGFudE1vZGUiLCJoYW5kbGVJbmJvdW5kTWVzc2FnZSIsIm1zZyIsImZpZWxkcyIsInV1aWQiLCJyZXNvbHZlQW5kUHJlcGVuZCIsInNhbml0aXplZCIsImNvbnRlbnQiLCJzYW5pdGl6ZUluYm91bmRXZWJob29rQ29udGVudCIsInJlcXVpcmUiLCJwcmV2aWV3Iiwic2xpY2UiLCJ2YWx1ZSIsIm1vZGUiLCJjb25zdCIsInNraXBTbGFzaENvbW1hbmRzIiwiYnJpZGdlT3JpZ2luIiwiZSIsImxldmVsIiwiaGFuZGxlU3RhdGVDaGFuZ2UiLCJzdGF0ZSIsImhhbmRsZSIsImNvbm5lY3RVcmwiLCJlbnZpcm9ubWVudElkIiwic2Vzc2lvbkluZ3Jlc3NVcmwiLCJyZXBsQnJpZGdlQ29ubmVjdFVybCIsInNlc3Npb25VcmwiLCJicmlkZ2VTZXNzaW9uSWQiLCJyZXBsQnJpZGdlU2Vzc2lvblVybCIsImVudklkIiwic2Vzc2lvbklkIiwicmVwbEJyaWRnZVNlc3Npb25BY3RpdmUiLCJyZXBsQnJpZGdlUmVjb25uZWN0aW5nIiwicmVwbEJyaWRnZUVudmlyb25tZW50SWQiLCJyZXBsQnJpZGdlU2Vzc2lvbklkIiwic2tpbGxzIiwiZ2V0U3RhdGUiLCJ3cml0ZVNka01lc3NhZ2VzIiwidG9vbHMiLCJtY3BDbGllbnRzIiwibW9kZWwiLCJwZXJtaXNzaW9uTW9kZSIsInRvb2xQZXJtaXNzaW9uQ29udGV4dCIsImZpbHRlciIsImFnZW50cyIsImFnZW50RGVmaW5pdGlvbnMiLCJhY3RpdmVBZ2VudHMiLCJwbHVnaW5zIiwiZmFzdE1vZGUiLCJlcnIiLCJjbGVhclRpbWVvdXQiLCJwZW5kaW5nUGVybWlzc2lvbkhhbmRsZXJzIiwiTWFwIiwicmVzcG9uc2UiLCJoYW5kbGVQZXJtaXNzaW9uUmVzcG9uc2UiLCJyZXF1ZXN0SWQiLCJyZXF1ZXN0X2lkIiwiaGFuZGxlciIsImdldCIsImRlbGV0ZSIsImlubmVyIiwic3VidHlwZSIsInRhZ3MiLCJvbkluYm91bmRNZXNzYWdlIiwib25QZXJtaXNzaW9uUmVzcG9uc2UiLCJvbkludGVycnVwdCIsImFib3J0Iiwib25TZXRNb2RlbCIsInJlc29sdmVkIiwibWFpbkxvb3BNb2RlbEZvclNlc3Npb24iLCJvblNldE1heFRoaW5raW5nVG9rZW5zIiwibWF4VG9rZW5zIiwiZW5hYmxlZCIsInRoaW5raW5nRW5hYmxlZCIsIm9uU2V0UGVybWlzc2lvbk1vZGUiLCJvayIsImVycm9yIiwiaXNCeXBhc3NQZXJtaXNzaW9uc01vZGVBdmFpbGFibGUiLCJyZWFzb24iLCJuZXh0Iiwic2V0SW1tZWRpYXRlIiwiY3VycmVudFF1ZXVlIiwiZm9yRWFjaCIsIml0ZW0iLCJyZWNoZWNrUGVybWlzc2lvbiIsIm9uU3RhdGVDaGFuZ2UiLCJpbml0aWFsTWVzc2FnZXMiLCJnZXRNZXNzYWdlcyIsInByZXZpb3VzbHlGbHVzaGVkVVVJRHMiLCJpbml0aWFsTmFtZSIsInRlYXJkb3duIiwicGVybWlzc2lvbkNhbGxiYWNrcyIsInNlbmRSZXF1ZXN0IiwidG9vbE5hbWUiLCJpbnB1dCIsInRvb2xVc2VJZCIsImRlc2NyaXB0aW9uIiwicGVybWlzc2lvblN1Z2dlc3Rpb25zIiwiYmxvY2tlZFBhdGgiLCJzZW5kQ29udHJvbFJlcXVlc3QiLCJ0eXBlIiwicmVxdWVzdCIsInRvb2xfbmFtZSIsInRvb2xfdXNlX2lkIiwicGVybWlzc2lvbl9zdWdnZXN0aW9ucyIsImJsb2NrZWRfcGF0aCIsInNlbmRSZXNwb25zZSIsInBheWxvYWQiLCJSZWNvcmQiLCJzZW5kQ29udHJvbFJlc3BvbnNlIiwiY2FuY2VsUmVxdWVzdCIsInNlbmRDb250cm9sQ2FuY2VsUmVxdWVzdCIsIm9uUmVzcG9uc2UiLCJzZXQiLCJyZXBsQnJpZGdlUGVybWlzc2lvbkNhbGxiYWNrcyIsInVybCIsImhhc0VudiIsInVwZ3JhZGVOdWRnZSIsImNhdGNoIiwiZXJyTXNnIiwic3RhcnRJbmRleCIsIk1hdGgiLCJtaW4iLCJuZXdNZXNzYWdlcyIsImkiLCJwdXNoIiwid3JpdGVNZXNzYWdlcyIsInNlbmRSZXN1bHQiXSwic291cmNlcyI6WyJ1c2VSZXBsQnJpZGdlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCBSZWFjdCwgeyB1c2VDYWxsYmFjaywgdXNlRWZmZWN0LCB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHNldE1haW5Mb29wTW9kZWxPdmVycmlkZSB9IGZyb20gJy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7XG4gIHR5cGUgQnJpZGdlUGVybWlzc2lvbkNhbGxiYWNrcyxcbiAgdHlwZSBCcmlkZ2VQZXJtaXNzaW9uUmVzcG9uc2UsXG4gIGlzQnJpZGdlUGVybWlzc2lvblJlc3BvbnNlLFxufSBmcm9tICcuLi9icmlkZ2UvYnJpZGdlUGVybWlzc2lvbkNhbGxiYWNrcy5qcydcbmltcG9ydCB7IGJ1aWxkQnJpZGdlQ29ubmVjdFVybCB9IGZyb20gJy4uL2JyaWRnZS9icmlkZ2VTdGF0dXNVdGlsLmpzJ1xuaW1wb3J0IHsgZXh0cmFjdEluYm91bmRNZXNzYWdlRmllbGRzIH0gZnJvbSAnLi4vYnJpZGdlL2luYm91bmRNZXNzYWdlcy5qcydcbmltcG9ydCB0eXBlIHsgQnJpZGdlU3RhdGUsIFJlcGxCcmlkZ2VIYW5kbGUgfSBmcm9tICcuLi9icmlkZ2UvcmVwbEJyaWRnZS5qcydcbmltcG9ydCB7IHNldFJlcGxCcmlkZ2VIYW5kbGUgfSBmcm9tICcuLi9icmlkZ2UvcmVwbEJyaWRnZUhhbmRsZS5qcydcbmltcG9ydCB0eXBlIHsgQ29tbWFuZCB9IGZyb20gJy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgZ2V0U2xhc2hDb21tYW5kVG9vbFNraWxscywgaXNCcmlkZ2VTYWZlQ29tbWFuZCB9IGZyb20gJy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgZ2V0UmVtb3RlU2Vzc2lvblVybCB9IGZyb20gJy4uL2NvbnN0YW50cy9wcm9kdWN0LmpzJ1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJy4uL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB0eXBlIHtcbiAgUGVybWlzc2lvbk1vZGUsXG4gIFNES01lc3NhZ2UsXG59IGZyb20gJy4uL2VudHJ5cG9pbnRzL2FnZW50U2RrVHlwZXMuanMnXG5pbXBvcnQgdHlwZSB7IFNES0NvbnRyb2xSZXNwb25zZSB9IGZyb20gJy4uL2VudHJ5cG9pbnRzL3Nkay9jb250cm9sVHlwZXMuanMnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHsgZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUgfSBmcm9tICcuLi9zZXJ2aWNlcy9hbmFseXRpY3MvZ3Jvd3RoYm9vay5qcydcbmltcG9ydCB7XG4gIHVzZUFwcFN0YXRlLFxuICB1c2VBcHBTdGF0ZVN0b3JlLFxuICB1c2VTZXRBcHBTdGF0ZSxcbn0gZnJvbSAnLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IE1lc3NhZ2UgfSBmcm9tICcuLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHsgZ2V0Q3dkIH0gZnJvbSAnLi4vdXRpbHMvY3dkLmpzJ1xuaW1wb3J0IHsgbG9nRm9yRGVidWdnaW5nIH0gZnJvbSAnLi4vdXRpbHMvZGVidWcuanMnXG5pbXBvcnQgeyBlcnJvck1lc3NhZ2UgfSBmcm9tICcuLi91dGlscy9lcnJvcnMuanMnXG5pbXBvcnQgeyBlbnF1ZXVlIH0gZnJvbSAnLi4vdXRpbHMvbWVzc2FnZVF1ZXVlTWFuYWdlci5qcydcbmltcG9ydCB7IGJ1aWxkU3lzdGVtSW5pdE1lc3NhZ2UgfSBmcm9tICcuLi91dGlscy9tZXNzYWdlcy9zeXN0ZW1Jbml0LmpzJ1xuaW1wb3J0IHtcbiAgY3JlYXRlQnJpZGdlU3RhdHVzTWVzc2FnZSxcbiAgY3JlYXRlU3lzdGVtTWVzc2FnZSxcbn0gZnJvbSAnLi4vdXRpbHMvbWVzc2FnZXMuanMnXG5pbXBvcnQge1xuICBnZXRBdXRvTW9kZVVuYXZhaWxhYmxlTm90aWZpY2F0aW9uLFxuICBnZXRBdXRvTW9kZVVuYXZhaWxhYmxlUmVhc29uLFxuICBpc0F1dG9Nb2RlR2F0ZUVuYWJsZWQsXG4gIGlzQnlwYXNzUGVybWlzc2lvbnNNb2RlRGlzYWJsZWQsXG4gIHRyYW5zaXRpb25QZXJtaXNzaW9uTW9kZSxcbn0gZnJvbSAnLi4vdXRpbHMvcGVybWlzc2lvbnMvcGVybWlzc2lvblNldHVwLmpzJ1xuaW1wb3J0IHsgZ2V0TGVhZGVyVG9vbFVzZUNvbmZpcm1RdWV1ZSB9IGZyb20gJy4uL3V0aWxzL3N3YXJtL2xlYWRlclBlcm1pc3Npb25CcmlkZ2UuanMnXG5cbi8qKiBIb3cgbG9uZyBhZnRlciBhIGZhaWx1cmUgYmVmb3JlIHJlcGxCcmlkZ2VFbmFibGVkIGlzIGF1dG8tY2xlYXJlZCAoc3RvcHMgcmV0cmllcykuICovXG5leHBvcnQgY29uc3QgQlJJREdFX0ZBSUxVUkVfRElTTUlTU19NUyA9IDEwXzAwMFxuXG4vKipcbiAqIE1heCBjb25zZWN1dGl2ZSBpbml0UmVwbEJyaWRnZSBmYWlsdXJlcyBiZWZvcmUgdGhlIGhvb2sgc3RvcHMgcmUtYXR0ZW1wdGluZ1xuICogZm9yIHRoZSBzZXNzaW9uIGxpZmV0aW1lLiBHdWFyZHMgYWdhaW5zdCBwYXRocyB0aGF0IGZsaXAgcmVwbEJyaWRnZUVuYWJsZWRcbiAqIGJhY2sgb24gYWZ0ZXIgYXV0by1kaXNhYmxlIChzZXR0aW5ncyBzeW5jLCAvcmVtb3RlLWNvbnRyb2wsIGNvbmZpZyB0b29sKVxuICogd2hlbiB0aGUgdW5kZXJseWluZyBPQXV0aCBpcyB1bnJlY292ZXJhYmxlIOKAlCBlYWNoIHJlLWF0dGVtcHQgaXMgYW5vdGhlclxuICogZ3VhcmFudGVlZCA0MDEgYWdhaW5zdCBQT1NUIC92MS9lbnZpcm9ubWVudHMvYnJpZGdlLiBEYXRhZG9nIDIwMjYtMDMtMDg6XG4gKiB0b3Agc3R1Y2sgY2xpZW50IGdlbmVyYXRlZCAyLDg3OSDDlyA0MDEvZGF5IGFsb25lICgxNyUgb2YgYWxsIDQwMXMgb24gdGhlXG4gKiByb3V0ZSkuXG4gKi9cbmNvbnN0IE1BWF9DT05TRUNVVElWRV9JTklUX0ZBSUxVUkVTID0gM1xuXG4vKipcbiAqIEhvb2sgdGhhdCBpbml0aWFsaXplcyBhbiBhbHdheXMtb24gYnJpZGdlIGNvbm5lY3Rpb24gaW4gdGhlIGJhY2tncm91bmRcbiAqIGFuZCB3cml0ZXMgbmV3IHVzZXIvYXNzaXN0YW50IG1lc3NhZ2VzIHRvIHRoZSBicmlkZ2Ugc2Vzc2lvbi5cbiAqXG4gKiBTaWxlbnRseSBza2lwcyBpZiBicmlkZ2UgaXMgbm90IGVuYWJsZWQgb3IgdXNlciBpcyBub3QgT0F1dGgtYXV0aGVudGljYXRlZC5cbiAqXG4gKiBXYXRjaGVzIEFwcFN0YXRlLnJlcGxCcmlkZ2VFbmFibGVkIOKAlCB3aGVuIHRvZ2dsZWQgb2ZmICh2aWEgL2NvbmZpZyBvciBmb290ZXIpLFxuICogdGhlIGJyaWRnZSBpcyB0b3JuIGRvd24uIFdoZW4gdG9nZ2xlZCBiYWNrIG9uLCBpdCByZS1pbml0aWFsaXplcy5cbiAqXG4gKiBJbmJvdW5kIG1lc3NhZ2VzIGZyb20gY2xhdWRlLmFpIGFyZSBpbmplY3RlZCBpbnRvIHRoZSBSRVBMIHZpYSBxdWV1ZWRDb21tYW5kcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZVJlcGxCcmlkZ2UoXG4gIG1lc3NhZ2VzOiBNZXNzYWdlW10sXG4gIHNldE1lc3NhZ2VzOiAoYWN0aW9uOiBSZWFjdC5TZXRTdGF0ZUFjdGlvbjxNZXNzYWdlW10+KSA9PiB2b2lkLFxuICBhYm9ydENvbnRyb2xsZXJSZWY6IFJlYWN0LlJlZk9iamVjdDxBYm9ydENvbnRyb2xsZXIgfCBudWxsPixcbiAgY29tbWFuZHM6IHJlYWRvbmx5IENvbW1hbmRbXSxcbiAgbWFpbkxvb3BNb2RlbDogc3RyaW5nLFxuKTogeyBzZW5kQnJpZGdlUmVzdWx0OiAoKSA9PiB2b2lkIH0ge1xuICBjb25zdCBoYW5kbGVSZWYgPSB1c2VSZWY8UmVwbEJyaWRnZUhhbmRsZSB8IG51bGw+KG51bGwpXG4gIGNvbnN0IHRlYXJkb3duUHJvbWlzZVJlZiA9IHVzZVJlZjxQcm9taXNlPHZvaWQ+IHwgdW5kZWZpbmVkPih1bmRlZmluZWQpXG4gIGNvbnN0IGxhc3RXcml0dGVuSW5kZXhSZWYgPSB1c2VSZWYoMClcbiAgLy8gVHJhY2tzIFVVSURzIGFscmVhZHkgZmx1c2hlZCBhcyBpbml0aWFsIG1lc3NhZ2VzLiBQZXJzaXN0cyBhY3Jvc3NcbiAgLy8gYnJpZGdlIHJlY29ubmVjdGlvbnMgc28gQnJpZGdlICMyKyBvbmx5IHNlbmRzIG5ldyBtZXNzYWdlcyDigJQgc2VuZGluZ1xuICAvLyBkdXBsaWNhdGUgVVVJRHMgY2F1c2VzIHRoZSBzZXJ2ZXIgdG8ga2lsbCB0aGUgV2ViU29ja2V0LlxuICBjb25zdCBmbHVzaGVkVVVJRHNSZWYgPSB1c2VSZWYobmV3IFNldDxzdHJpbmc+KCkpXG4gIGNvbnN0IGZhaWx1cmVUaW1lb3V0UmVmID0gdXNlUmVmPFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgdW5kZWZpbmVkPihcbiAgICB1bmRlZmluZWQsXG4gIClcbiAgLy8gUGVyc2lzdHMgYWNyb3NzIGVmZmVjdCByZS1ydW5zICh1bmxpa2UgdGhlIGVmZmVjdCdzIGxvY2FsIHN0YXRlKS4gUmVzZXRcbiAgLy8gb25seSBvbiBzdWNjZXNzZnVsIGluaXQuIEhpdHMgTUFYX0NPTlNFQ1VUSVZFX0lOSVRfRkFJTFVSRVMg4oaSIGZ1c2UgYmxvd25cbiAgLy8gZm9yIHRoZSBzZXNzaW9uLCByZWdhcmRsZXNzIG9mIHJlcGxCcmlkZ2VFbmFibGVkIHJlLXRvZ2dsaW5nLlxuICBjb25zdCBjb25zZWN1dGl2ZUZhaWx1cmVzUmVmID0gdXNlUmVmKDApXG4gIGNvbnN0IHNldEFwcFN0YXRlID0gdXNlU2V0QXBwU3RhdGUoKVxuICBjb25zdCBjb21tYW5kc1JlZiA9IHVzZVJlZihjb21tYW5kcylcbiAgY29tbWFuZHNSZWYuY3VycmVudCA9IGNvbW1hbmRzXG4gIGNvbnN0IG1haW5Mb29wTW9kZWxSZWYgPSB1c2VSZWYobWFpbkxvb3BNb2RlbClcbiAgbWFpbkxvb3BNb2RlbFJlZi5jdXJyZW50ID0gbWFpbkxvb3BNb2RlbFxuICBjb25zdCBtZXNzYWdlc1JlZiA9IHVzZVJlZihtZXNzYWdlcylcbiAgbWVzc2FnZXNSZWYuY3VycmVudCA9IG1lc3NhZ2VzXG4gIGNvbnN0IHN0b3JlID0gdXNlQXBwU3RhdGVTdG9yZSgpXG4gIGNvbnN0IHsgYWRkTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcbiAgY29uc3QgcmVwbEJyaWRnZUVuYWJsZWQgPSBmZWF0dXJlKCdCUklER0VfTU9ERScpXG4gICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICB1c2VBcHBTdGF0ZShzID0+IHMucmVwbEJyaWRnZUVuYWJsZWQpXG4gICAgOiBmYWxzZVxuICBjb25zdCByZXBsQnJpZGdlQ29ubmVjdGVkID0gZmVhdHVyZSgnQlJJREdFX01PREUnKVxuICAgID8gLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICAgICAgdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VDb25uZWN0ZWQpXG4gICAgOiBmYWxzZVxuICBjb25zdCByZXBsQnJpZGdlT3V0Ym91bmRPbmx5ID0gZmVhdHVyZSgnQlJJREdFX01PREUnKVxuICAgID8gLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICAgICAgdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VPdXRib3VuZE9ubHkpXG4gICAgOiBmYWxzZVxuICBjb25zdCByZXBsQnJpZGdlSW5pdGlhbE5hbWUgPSBmZWF0dXJlKCdCUklER0VfTU9ERScpXG4gICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICB1c2VBcHBTdGF0ZShzID0+IHMucmVwbEJyaWRnZUluaXRpYWxOYW1lKVxuICAgIDogdW5kZWZpbmVkXG5cbiAgLy8gSW5pdGlhbGl6ZS90ZWFyZG93biBicmlkZ2Ugd2hlbiBlbmFibGVkIHN0YXRlIGNoYW5nZXMuXG4gIC8vIFBhc3NlcyBjdXJyZW50IG1lc3NhZ2VzIGFzIGluaXRpYWxNZXNzYWdlcyBzbyB0aGUgcmVtb3RlIHNlc3Npb25cbiAgLy8gc3RhcnRzIHdpdGggdGhlIGV4aXN0aW5nIGNvbnZlcnNhdGlvbiBjb250ZXh0IChlLmcuIGZyb20gL2JyaWRnZSkuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgLy8gZmVhdHVyZSgpIGNoZWNrIG11c3QgdXNlIHBvc2l0aXZlIHBhdHRlcm4gZm9yIGRlYWQgY29kZSBlbGltaW5hdGlvbiDigJRcbiAgICAvLyBuZWdhdGl2ZSBwYXR0ZXJuIChpZiAoIWZlYXR1cmUoLi4uKSkgcmV0dXJuKSBkb2VzIE5PVCBlbGltaW5hdGVcbiAgICAvLyBkeW5hbWljIGltcG9ydHMgYmVsb3cuXG4gICAgaWYgKGZlYXR1cmUoJ0JSSURHRV9NT0RFJykpIHtcbiAgICAgIGlmICghcmVwbEJyaWRnZUVuYWJsZWQpIHJldHVyblxuXG4gICAgICBjb25zdCBvdXRib3VuZE9ubHkgPSByZXBsQnJpZGdlT3V0Ym91bmRPbmx5XG4gICAgICBmdW5jdGlvbiBub3RpZnlCcmlkZ2VGYWlsZWQoZGV0YWlsPzogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGlmIChvdXRib3VuZE9ubHkpIHJldHVyblxuICAgICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICAgIGtleTogJ2JyaWRnZS1mYWlsZWQnLFxuICAgICAgICAgIGpzeDogKFxuICAgICAgICAgICAgPD5cbiAgICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPlJlbW90ZSBDb250cm9sIGZhaWxlZDwvVGV4dD5cbiAgICAgICAgICAgICAge2RldGFpbCAmJiA8VGV4dCBkaW1Db2xvcj4gwrcge2RldGFpbH08L1RleHQ+fVxuICAgICAgICAgICAgPC8+XG4gICAgICAgICAgKSxcbiAgICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIH0pXG4gICAgICB9XG5cbiAgICAgIGlmIChjb25zZWN1dGl2ZUZhaWx1cmVzUmVmLmN1cnJlbnQgPj0gTUFYX0NPTlNFQ1VUSVZFX0lOSVRfRkFJTFVSRVMpIHtcbiAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgIGBbYnJpZGdlOnJlcGxdIEhvb2s6ICR7Y29uc2VjdXRpdmVGYWlsdXJlc1JlZi5jdXJyZW50fSBjb25zZWN1dGl2ZSBpbml0IGZhaWx1cmVzLCBub3QgcmV0cnlpbmcgdGhpcyBzZXNzaW9uYCxcbiAgICAgICAgKVxuICAgICAgICAvLyBDbGVhciByZXBsQnJpZGdlRW5hYmxlZCBzbyAvcmVtb3RlLWNvbnRyb2wgZG9lc24ndCBtaXN0YWtlbmx5IHNob3dcbiAgICAgICAgLy8gQnJpZGdlRGlzY29ubmVjdERpYWxvZyBmb3IgYSBicmlkZ2UgdGhhdCBuZXZlciBjb25uZWN0ZWQuXG4gICAgICAgIGNvbnN0IGZ1c2VIaW50ID0gJ2Rpc2FibGVkIGFmdGVyIHJlcGVhdGVkIGZhaWx1cmVzIMK3IHJlc3RhcnQgdG8gcmV0cnknXG4gICAgICAgIG5vdGlmeUJyaWRnZUZhaWxlZChmdXNlSGludClcbiAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICAgICAgaWYgKHByZXYucmVwbEJyaWRnZUVycm9yID09PSBmdXNlSGludCAmJiAhcHJldi5yZXBsQnJpZGdlRW5hYmxlZClcbiAgICAgICAgICAgIHJldHVybiBwcmV2XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICByZXBsQnJpZGdlRXJyb3I6IGZ1c2VIaW50LFxuICAgICAgICAgICAgcmVwbEJyaWRnZUVuYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGxldCBjYW5jZWxsZWQgPSBmYWxzZVxuICAgICAgLy8gQ2FwdHVyZSBtZXNzYWdlcy5sZW5ndGggbm93IHNvIHdlIGRvbid0IHJlLXNlbmQgaW5pdGlhbCBtZXNzYWdlc1xuICAgICAgLy8gdGhyb3VnaCB3cml0ZU1lc3NhZ2VzIGFmdGVyIHRoZSBicmlkZ2UgY29ubmVjdHMuXG4gICAgICBjb25zdCBpbml0aWFsTWVzc2FnZUNvdW50ID0gbWVzc2FnZXMubGVuZ3RoXG5cbiAgICAgIHZvaWQgKGFzeW5jICgpID0+IHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAvLyBXYWl0IGZvciBhbnkgaW4tcHJvZ3Jlc3MgdGVhcmRvd24gdG8gY29tcGxldGUgYmVmb3JlIHJlZ2lzdGVyaW5nXG4gICAgICAgICAgLy8gYSBuZXcgZW52aXJvbm1lbnQuIFdpdGhvdXQgdGhpcywgdGhlIGRlcmVnaXN0ZXIgSFRUUCBjYWxsIGZyb21cbiAgICAgICAgICAvLyB0aGUgcHJldmlvdXMgdGVhcmRvd24gcmFjZXMgd2l0aCB0aGUgbmV3IHJlZ2lzdGVyIGNhbGwsIGFuZCB0aGVcbiAgICAgICAgICAvLyBzZXJ2ZXIgbWF5IHRlYXIgZG93biB0aGUgZnJlc2hseS1jcmVhdGVkIGVudmlyb25tZW50LlxuICAgICAgICAgIGlmICh0ZWFyZG93blByb21pc2VSZWYuY3VycmVudCkge1xuICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgICAnW2JyaWRnZTpyZXBsXSBIb29rOiB3YWl0aW5nIGZvciBwcmV2aW91cyB0ZWFyZG93biB0byBjb21wbGV0ZSBiZWZvcmUgcmUtaW5pdCcsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBhd2FpdCB0ZWFyZG93blByb21pc2VSZWYuY3VycmVudFxuICAgICAgICAgICAgdGVhcmRvd25Qcm9taXNlUmVmLmN1cnJlbnQgPSB1bmRlZmluZWRcbiAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgICAgJ1ticmlkZ2U6cmVwbF0gSG9vazogcHJldmlvdXMgdGVhcmRvd24gY29tcGxldGUsIHByb2NlZWRpbmcgd2l0aCByZS1pbml0JyxcbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKGNhbmNlbGxlZCkgcmV0dXJuXG5cbiAgICAgICAgICAvLyBEeW5hbWljIGltcG9ydCBzbyB0aGUgbW9kdWxlIGlzIHRyZWUtc2hha2VuIGluIGV4dGVybmFsIGJ1aWxkc1xuICAgICAgICAgIGNvbnN0IHsgaW5pdFJlcGxCcmlkZ2UgfSA9IGF3YWl0IGltcG9ydCgnLi4vYnJpZGdlL2luaXRSZXBsQnJpZGdlLmpzJylcbiAgICAgICAgICBjb25zdCB7IHNob3VsZFNob3dBcHBVcGdyYWRlTWVzc2FnZSB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgJy4uL2JyaWRnZS9lbnZMZXNzQnJpZGdlQ29uZmlnLmpzJ1xuICAgICAgICAgIClcblxuICAgICAgICAgIC8vIEFzc2lzdGFudCBtb2RlOiBwZXJwZXR1YWwgYnJpZGdlIHNlc3Npb24g4oCUIGNsYXVkZS5haSBzaG93cyBvbmVcbiAgICAgICAgICAvLyBjb250aW51b3VzIGNvbnZlcnNhdGlvbiBhY3Jvc3MgQ0xJIHJlc3RhcnRzIGluc3RlYWQgb2YgYSBuZXdcbiAgICAgICAgICAvLyBzZXNzaW9uIHBlciBpbnZvY2F0aW9uLiBpbml0QnJpZGdlQ29yZSByZWFkcyBicmlkZ2UtcG9pbnRlci5qc29uXG4gICAgICAgICAgLy8gKHRoZSBzYW1lIGNyYXNoLXJlY292ZXJ5IGZpbGUgIzIwNzM1IGFkZGVkKSBhbmQgcmV1c2VzIGl0c1xuICAgICAgICAgIC8vIHtlbnZpcm9ubWVudElkLCBzZXNzaW9uSWR9IHZpYSByZXVzZUVudmlyb25tZW50SWQgK1xuICAgICAgICAgIC8vIGFwaS5yZWNvbm5lY3RTZXNzaW9uKCkuIFRlYXJkb3duIHNraXBzIGFyY2hpdmUvZGVyZWdpc3Rlci9cbiAgICAgICAgICAvLyBwb2ludGVyLWNsZWFyIHNvIHRoZSBzZXNzaW9uIHN1cnZpdmVzIGNsZWFuIGV4aXRzLCBub3QganVzdFxuICAgICAgICAgIC8vIGNyYXNoZXMuIE5vbi1hc3Npc3RhbnQgYnJpZGdlcyBjbGVhciB0aGUgcG9pbnRlciBvbiB0ZWFyZG93blxuICAgICAgICAgIC8vIChjcmFzaC1yZWNvdmVyeSBvbmx5KS5cbiAgICAgICAgICBsZXQgcGVycGV0dWFsID0gZmFsc2VcbiAgICAgICAgICBpZiAoZmVhdHVyZSgnS0FJUk9TJykpIHtcbiAgICAgICAgICAgIGNvbnN0IHsgaXNBc3Npc3RhbnRNb2RlIH0gPSBhd2FpdCBpbXBvcnQoJy4uL2Fzc2lzdGFudC9pbmRleC5qcycpXG4gICAgICAgICAgICBwZXJwZXR1YWwgPSBpc0Fzc2lzdGFudE1vZGUoKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIFdoZW4gYSB1c2VyIG1lc3NhZ2UgYXJyaXZlcyBmcm9tIGNsYXVkZS5haSwgaW5qZWN0IGl0IGludG8gdGhlIFJFUEwuXG4gICAgICAgICAgLy8gUHJlc2VydmVzIHRoZSBvcmlnaW5hbCBVVUlEIHNvIHRoYXQgd2hlbiB0aGUgbWVzc2FnZSBpcyBmb3J3YXJkZWRcbiAgICAgICAgICAvLyBiYWNrIHRvIENDUiwgaXQgbWF0Y2hlcyB0aGUgb3JpZ2luYWwg4oCUIGF2b2lkaW5nIGR1cGxpY2F0ZSBtZXNzYWdlcy5cbiAgICAgICAgICAvL1xuICAgICAgICAgIC8vIEFzeW5jIGJlY2F1c2UgZmlsZV9hdHRhY2htZW50cyAoaWYgcHJlc2VudCkgbmVlZCBhIG5ldHdvcmsgZmV0Y2ggK1xuICAgICAgICAgIC8vIGRpc2sgd3JpdGUgYmVmb3JlIHdlIGVucXVldWUgd2l0aCB0aGUgQHBhdGggcHJlZml4LiBDYWxsZXIgZG9lc24ndFxuICAgICAgICAgIC8vIGF3YWl0IOKAlCBtZXNzYWdlcyB3aXRoIGF0dGFjaG1lbnRzIGp1c3QgbGFuZCBpbiB0aGUgcXVldWUgc2xpZ2h0bHlcbiAgICAgICAgICAvLyBsYXRlciwgd2hpY2ggaXMgZmluZSAod2ViIG1lc3NhZ2VzIGFyZW4ndCByYXBpZC1maXJlKS5cbiAgICAgICAgICBhc3luYyBmdW5jdGlvbiBoYW5kbGVJbmJvdW5kTWVzc2FnZShtc2c6IFNES01lc3NhZ2UpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGNvbnN0IGZpZWxkcyA9IGV4dHJhY3RJbmJvdW5kTWVzc2FnZUZpZWxkcyhtc2cpXG4gICAgICAgICAgICAgIGlmICghZmllbGRzKSByZXR1cm5cblxuICAgICAgICAgICAgICBjb25zdCB7IHV1aWQgfSA9IGZpZWxkc1xuXG4gICAgICAgICAgICAgIC8vIER5bmFtaWMgaW1wb3J0IGtlZXBzIHRoZSBicmlkZ2UgY29kZSBvdXQgb2Ygbm9uLUJSSURHRV9NT0RFIGJ1aWxkcy5cbiAgICAgICAgICAgICAgY29uc3QgeyByZXNvbHZlQW5kUHJlcGVuZCB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgICAgICcuLi9icmlkZ2UvaW5ib3VuZEF0dGFjaG1lbnRzLmpzJ1xuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIGxldCBzYW5pdGl6ZWQgPSBmaWVsZHMuY29udGVudFxuICAgICAgICAgICAgICBpZiAoZmVhdHVyZSgnS0FJUk9TX0dJVEhVQl9XRUJIT09LUycpKSB7XG4gICAgICAgICAgICAgICAgLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICAgICAgICAgICAgICAgIGNvbnN0IHsgc2FuaXRpemVJbmJvdW5kV2ViaG9va0NvbnRlbnQgfSA9XG4gICAgICAgICAgICAgICAgICByZXF1aXJlKCcuLi9icmlkZ2Uvd2ViaG9va1Nhbml0aXplci5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4uL2JyaWRnZS93ZWJob29rU2FuaXRpemVyLmpzJylcbiAgICAgICAgICAgICAgICAvKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbiAgICAgICAgICAgICAgICBzYW5pdGl6ZWQgPSBzYW5pdGl6ZUluYm91bmRXZWJob29rQ29udGVudChmaWVsZHMuY29udGVudClcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgcmVzb2x2ZUFuZFByZXBlbmQobXNnLCBzYW5pdGl6ZWQpXG5cbiAgICAgICAgICAgICAgY29uc3QgcHJldmlldyA9XG4gICAgICAgICAgICAgICAgdHlwZW9mIGNvbnRlbnQgPT09ICdzdHJpbmcnXG4gICAgICAgICAgICAgICAgICA/IGNvbnRlbnQuc2xpY2UoMCwgODApXG4gICAgICAgICAgICAgICAgICA6IGBbJHtjb250ZW50Lmxlbmd0aH0gY29udGVudCBibG9ja3NdYFxuICAgICAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgICAgICAgYFticmlkZ2U6cmVwbF0gSW5qZWN0aW5nIGluYm91bmQgdXNlciBtZXNzYWdlOiAke3ByZXZpZXd9JHt1dWlkID8gYCB1dWlkPSR7dXVpZH1gIDogJyd9YCxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICBlbnF1ZXVlKHtcbiAgICAgICAgICAgICAgICB2YWx1ZTogY29udGVudCxcbiAgICAgICAgICAgICAgICBtb2RlOiAncHJvbXB0JyBhcyBjb25zdCxcbiAgICAgICAgICAgICAgICB1dWlkLFxuICAgICAgICAgICAgICAgIC8vIHNraXBTbGFzaENvbW1hbmRzIHN0YXlzIHRydWUgYXMgZGVmZW5zZS1pbi1kZXB0aCDigJRcbiAgICAgICAgICAgICAgICAvLyBwcm9jZXNzVXNlcklucHV0QmFzZSBvdmVycmlkZXMgaXQgaW50ZXJuYWxseSB3aGVuIGJyaWRnZU9yaWdpblxuICAgICAgICAgICAgICAgIC8vIGlzIHNldCBBTkQgdGhlIHJlc29sdmVkIGNvbW1hbmQgcGFzc2VzIGlzQnJpZGdlU2FmZUNvbW1hbmQuXG4gICAgICAgICAgICAgICAgLy8gVGhpcyBrZWVwcyBleGl0LXdvcmQgc3VwcHJlc3Npb24gYW5kIGltbWVkaWF0ZS1jb21tYW5kIGJsb2Nrc1xuICAgICAgICAgICAgICAgIC8vIGludGFjdCBmb3IgYW55IGNvZGUgcGF0aCB0aGF0IGNoZWNrcyBza2lwU2xhc2hDb21tYW5kcyBkaXJlY3RseS5cbiAgICAgICAgICAgICAgICBza2lwU2xhc2hDb21tYW5kczogdHJ1ZSxcbiAgICAgICAgICAgICAgICBicmlkZ2VPcmlnaW46IHRydWUsXG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgICAgICBgW2JyaWRnZTpyZXBsXSBoYW5kbGVJbmJvdW5kTWVzc2FnZSBmYWlsZWQ6ICR7ZX1gLFxuICAgICAgICAgICAgICAgIHsgbGV2ZWw6ICdlcnJvcicgfSxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIFN0YXRlIGNoYW5nZSBjYWxsYmFjayDigJQgbWFwcyBicmlkZ2UgbGlmZWN5Y2xlIGV2ZW50cyB0byBBcHBTdGF0ZS5cbiAgICAgICAgICBmdW5jdGlvbiBoYW5kbGVTdGF0ZUNoYW5nZShcbiAgICAgICAgICAgIHN0YXRlOiBCcmlkZ2VTdGF0ZSxcbiAgICAgICAgICAgIGRldGFpbD86IHN0cmluZyxcbiAgICAgICAgICApOiB2b2lkIHtcbiAgICAgICAgICAgIGlmIChjYW5jZWxsZWQpIHJldHVyblxuICAgICAgICAgICAgaWYgKG91dGJvdW5kT25seSkge1xuICAgICAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgICAgICAgYFticmlkZ2U6cmVwbF0gTWlycm9yIHN0YXRlPSR7c3RhdGV9JHtkZXRhaWwgPyBgIGRldGFpbD0ke2RldGFpbH1gIDogJyd9YCxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAvLyBTeW5jIHJlcGxCcmlkZ2VDb25uZWN0ZWQgc28gdGhlIGZvcndhcmRpbmcgZWZmZWN0IHN0YXJ0cy9zdG9wc1xuICAgICAgICAgICAgICAvLyB3cml0aW5nIGFzIHRoZSB0cmFuc3BvcnQgY29tZXMgdXAgb3IgZGllcy5cbiAgICAgICAgICAgICAgaWYgKHN0YXRlID09PSAnZmFpbGVkJykge1xuICAgICAgICAgICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgICAgICAgICAgaWYgKCFwcmV2LnJlcGxCcmlkZ2VDb25uZWN0ZWQpIHJldHVybiBwcmV2XG4gICAgICAgICAgICAgICAgICByZXR1cm4geyAuLi5wcmV2LCByZXBsQnJpZGdlQ29ubmVjdGVkOiBmYWxzZSB9XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgfSBlbHNlIGlmIChzdGF0ZSA9PT0gJ3JlYWR5JyB8fCBzdGF0ZSA9PT0gJ2Nvbm5lY3RlZCcpIHtcbiAgICAgICAgICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgICAgICAgICAgIGlmIChwcmV2LnJlcGxCcmlkZ2VDb25uZWN0ZWQpIHJldHVybiBwcmV2XG4gICAgICAgICAgICAgICAgICByZXR1cm4geyAuLi5wcmV2LCByZXBsQnJpZGdlQ29ubmVjdGVkOiB0cnVlIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgaGFuZGxlID0gaGFuZGxlUmVmLmN1cnJlbnRcbiAgICAgICAgICAgIHN3aXRjaCAoc3RhdGUpIHtcbiAgICAgICAgICAgICAgY2FzZSAncmVhZHknOlxuICAgICAgICAgICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgICAgICAgICAgY29uc3QgY29ubmVjdFVybCA9XG4gICAgICAgICAgICAgICAgICAgIGhhbmRsZSAmJiBoYW5kbGUuZW52aXJvbm1lbnRJZCAhPT0gJydcbiAgICAgICAgICAgICAgICAgICAgICA/IGJ1aWxkQnJpZGdlQ29ubmVjdFVybChcbiAgICAgICAgICAgICAgICAgICAgICAgICAgaGFuZGxlLmVudmlyb25tZW50SWQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgIGhhbmRsZS5zZXNzaW9uSW5ncmVzc1VybCxcbiAgICAgICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgICA6IHByZXYucmVwbEJyaWRnZUNvbm5lY3RVcmxcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHNlc3Npb25VcmwgPSBoYW5kbGVcbiAgICAgICAgICAgICAgICAgICAgPyBnZXRSZW1vdGVTZXNzaW9uVXJsKFxuICAgICAgICAgICAgICAgICAgICAgICAgaGFuZGxlLmJyaWRnZVNlc3Npb25JZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGhhbmRsZS5zZXNzaW9uSW5ncmVzc1VybCxcbiAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIDogcHJldi5yZXBsQnJpZGdlU2Vzc2lvblVybFxuICAgICAgICAgICAgICAgICAgY29uc3QgZW52SWQgPSBoYW5kbGU/LmVudmlyb25tZW50SWRcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHNlc3Npb25JZCA9IGhhbmRsZT8uYnJpZGdlU2Vzc2lvbklkXG4gICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgIHByZXYucmVwbEJyaWRnZUNvbm5lY3RlZCAmJlxuICAgICAgICAgICAgICAgICAgICAhcHJldi5yZXBsQnJpZGdlU2Vzc2lvbkFjdGl2ZSAmJlxuICAgICAgICAgICAgICAgICAgICAhcHJldi5yZXBsQnJpZGdlUmVjb25uZWN0aW5nICYmXG4gICAgICAgICAgICAgICAgICAgIHByZXYucmVwbEJyaWRnZUNvbm5lY3RVcmwgPT09IGNvbm5lY3RVcmwgJiZcbiAgICAgICAgICAgICAgICAgICAgcHJldi5yZXBsQnJpZGdlU2Vzc2lvblVybCA9PT0gc2Vzc2lvblVybCAmJlxuICAgICAgICAgICAgICAgICAgICBwcmV2LnJlcGxCcmlkZ2VFbnZpcm9ubWVudElkID09PSBlbnZJZCAmJlxuICAgICAgICAgICAgICAgICAgICBwcmV2LnJlcGxCcmlkZ2VTZXNzaW9uSWQgPT09IHNlc3Npb25JZFxuICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBwcmV2XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgICAgICAgICByZXBsQnJpZGdlQ29ubmVjdGVkOiB0cnVlLFxuICAgICAgICAgICAgICAgICAgICByZXBsQnJpZGdlU2Vzc2lvbkFjdGl2ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VSZWNvbm5lY3Rpbmc6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICByZXBsQnJpZGdlQ29ubmVjdFVybDogY29ubmVjdFVybCxcbiAgICAgICAgICAgICAgICAgICAgcmVwbEJyaWRnZVNlc3Npb25Vcmw6IHNlc3Npb25VcmwsXG4gICAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VFbnZpcm9ubWVudElkOiBlbnZJZCxcbiAgICAgICAgICAgICAgICAgICAgcmVwbEJyaWRnZVNlc3Npb25JZDogc2Vzc2lvbklkLFxuICAgICAgICAgICAgICAgICAgICByZXBsQnJpZGdlRXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgIGNhc2UgJ2Nvbm5lY3RlZCc6IHtcbiAgICAgICAgICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgICAgICAgICAgIGlmIChwcmV2LnJlcGxCcmlkZ2VTZXNzaW9uQWN0aXZlKSByZXR1cm4gcHJldlxuICAgICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgICAgICAgICAgcmVwbEJyaWRnZUNvbm5lY3RlZDogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgcmVwbEJyaWRnZVNlc3Npb25BY3RpdmU6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VSZWNvbm5lY3Rpbmc6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICByZXBsQnJpZGdlRXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC8vIFNlbmQgc3lzdGVtL2luaXQgc28gcmVtb3RlIGNsaWVudHMgKHdlYi9pT1MvQW5kcm9pZCkgZ2V0XG4gICAgICAgICAgICAgICAgLy8gc2Vzc2lvbiBtZXRhZGF0YS4gUkVQTCB1c2VzIHF1ZXJ5KCkgZGlyZWN0bHkg4oCUIG5ldmVyIGhpdHNcbiAgICAgICAgICAgICAgICAvLyBRdWVyeUVuZ2luZSdzIFNES01lc3NhZ2UgbGF5ZXIg4oCUIHNvIHRoaXMgaXMgdGhlIG9ubHkgcGF0aFxuICAgICAgICAgICAgICAgIC8vIHRvIHB1dCBzeXN0ZW0vaW5pdCBvbiB0aGUgUkVQTC1icmlkZ2Ugd2lyZS4gU2tpbGxzIGxvYWQgaXNcbiAgICAgICAgICAgICAgICAvLyBhc3luYyAobWVtb2l6ZWQsIGNoZWFwIGFmdGVyIFJFUEwgc3RhcnR1cCk7IGZpcmUtYW5kLWZvcmdldFxuICAgICAgICAgICAgICAgIC8vIHNvIHRoZSBjb25uZWN0ZWQtc3RhdGUgdHJhbnNpdGlvbiBpc24ndCBibG9ja2VkLlxuICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgIGdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFKFxuICAgICAgICAgICAgICAgICAgICAndGVuZ3VfYnJpZGdlX3N5c3RlbV9pbml0JyxcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICB2b2lkIChhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgY29uc3Qgc2tpbGxzID0gYXdhaXQgZ2V0U2xhc2hDb21tYW5kVG9vbFNraWxscyhnZXRDd2QoKSlcbiAgICAgICAgICAgICAgICAgICAgICBpZiAoY2FuY2VsbGVkKSByZXR1cm5cbiAgICAgICAgICAgICAgICAgICAgICBjb25zdCBzdGF0ZSA9IHN0b3JlLmdldFN0YXRlKClcbiAgICAgICAgICAgICAgICAgICAgICBoYW5kbGVSZWYuY3VycmVudD8ud3JpdGVTZGtNZXNzYWdlcyhbXG4gICAgICAgICAgICAgICAgICAgICAgICBidWlsZFN5c3RlbUluaXRNZXNzYWdlKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gdG9vbHMvbWNwQ2xpZW50cy9wbHVnaW5zIHJlZGFjdGVkIGZvciBSRVBMLWJyaWRnZTpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gTUNQLXByZWZpeGVkIHRvb2wgbmFtZXMgYW5kIHNlcnZlciBuYW1lcyBsZWFrIHdoaWNoXG4gICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGludGVncmF0aW9ucyB0aGUgdXNlciBoYXMgd2lyZWQgdXA7IHBsdWdpbiBwYXRocyBsZWFrXG4gICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHJhdyBmaWxlc3lzdGVtIHBhdGhzICh1c2VybmFtZSwgcHJvamVjdCBzdHJ1Y3R1cmUpLlxuICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBDQ1IgdjIgcGVyc2lzdHMgU0RLIG1lc3NhZ2VzIHRvIFNwYW5uZXIg4oCUIHVzZXJzIHdob1xuICAgICAgICAgICAgICAgICAgICAgICAgICAvLyB0YXAgXCJDb25uZWN0IGZyb20gcGhvbmVcIiBtYXkgbm90IGV4cGVjdCB0aGVzZSBvblxuICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBbnRocm9waWMncyBzZXJ2ZXJzLiBRdWVyeUVuZ2luZSAoU0RLKSBzdGlsbCBlbWl0c1xuICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBmdWxsIGxpc3RzIOKAlCBTREsgY29uc3VtZXJzIGV4cGVjdCBmdWxsIHRlbGVtZXRyeS5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgdG9vbHM6IFtdLFxuICAgICAgICAgICAgICAgICAgICAgICAgICBtY3BDbGllbnRzOiBbXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWw6IG1haW5Mb29wTW9kZWxSZWYuY3VycmVudCxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgcGVybWlzc2lvbk1vZGU6IHN0YXRlLnRvb2xQZXJtaXNzaW9uQ29udGV4dFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5tb2RlIGFzIFBlcm1pc3Npb25Nb2RlLCAvLyBUT0RPOiBhdm9pZCB0aGUgY2FzdFxuICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBSZW1vdGUgY2xpZW50cyBjYW4gb25seSBpbnZva2UgYnJpZGdlLXNhZmUgY29tbWFuZHMg4oCUXG4gICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFkdmVydGlzaW5nIHVuc2FmZSBvbmVzIChsb2NhbC1qc3gsIHVuYWxsb3dlZCBsb2NhbClcbiAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gd291bGQgbGV0IG1vYmlsZS93ZWIgYXR0ZW1wdCB0aGVtIGFuZCBoaXQgZXJyb3JzLlxuICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tYW5kczpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tYW5kc1JlZi5jdXJyZW50LmZpbHRlcihpc0JyaWRnZVNhZmVDb21tYW5kKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlbnRzOiBzdGF0ZS5hZ2VudERlZmluaXRpb25zLmFjdGl2ZUFnZW50cyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgc2tpbGxzLFxuICAgICAgICAgICAgICAgICAgICAgICAgICBwbHVnaW5zOiBbXSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgZmFzdE1vZGU6IHN0YXRlLmZhc3RNb2RlLFxuICAgICAgICAgICAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgICAgICAgICAgXSlcbiAgICAgICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgICAgICAgICAgICAgYFticmlkZ2U6cmVwbF0gRmFpbGVkIHRvIHNlbmQgc3lzdGVtL2luaXQ6ICR7ZXJyb3JNZXNzYWdlKGVycil9YCxcbiAgICAgICAgICAgICAgICAgICAgICAgIHsgbGV2ZWw6ICdlcnJvcicgfSxcbiAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIH0pKClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBjYXNlICdyZWNvbm5lY3RpbmcnOlxuICAgICAgICAgICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgICAgICAgICAgaWYgKHByZXYucmVwbEJyaWRnZVJlY29ubmVjdGluZykgcmV0dXJuIHByZXZcbiAgICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VSZWNvbm5lY3Rpbmc6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VTZXNzaW9uQWN0aXZlOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgICAgIGNhc2UgJ2ZhaWxlZCc6XG4gICAgICAgICAgICAgICAgLy8gQ2xlYXIgYW55IHByZXZpb3VzIGZhaWx1cmUgZGlzbWlzcyB0aW1lclxuICAgICAgICAgICAgICAgIGNsZWFyVGltZW91dChmYWlsdXJlVGltZW91dFJlZi5jdXJyZW50KVxuICAgICAgICAgICAgICAgIG5vdGlmeUJyaWRnZUZhaWxlZChkZXRhaWwpXG4gICAgICAgICAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiAoe1xuICAgICAgICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VFcnJvcjogZGV0YWlsLFxuICAgICAgICAgICAgICAgICAgcmVwbEJyaWRnZVJlY29ubmVjdGluZzogZmFsc2UsXG4gICAgICAgICAgICAgICAgICByZXBsQnJpZGdlU2Vzc2lvbkFjdGl2ZTogZmFsc2UsXG4gICAgICAgICAgICAgICAgICByZXBsQnJpZGdlQ29ubmVjdGVkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICB9KSlcbiAgICAgICAgICAgICAgICAvLyBBdXRvLWRpc2FibGUgYWZ0ZXIgdGltZW91dCBzbyB0aGUgaG9vayBzdG9wcyByZXRyeWluZy5cbiAgICAgICAgICAgICAgICBmYWlsdXJlVGltZW91dFJlZi5jdXJyZW50ID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgICBpZiAoY2FuY2VsbGVkKSByZXR1cm5cbiAgICAgICAgICAgICAgICAgIGZhaWx1cmVUaW1lb3V0UmVmLmN1cnJlbnQgPSB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAoIXByZXYucmVwbEJyaWRnZUVycm9yKSByZXR1cm4gcHJldlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgICAgICAgICAgcmVwbEJyaWRnZUVuYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VFcnJvcjogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIH0sIEJSSURHRV9GQUlMVVJFX0RJU01JU1NfTVMpXG4gICAgICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBNYXAgb2YgcGVuZGluZyBicmlkZ2UgcGVybWlzc2lvbiByZXNwb25zZSBoYW5kbGVycywga2V5ZWQgYnkgcmVxdWVzdF9pZC5cbiAgICAgICAgICAvLyBFYWNoIGVudHJ5IGlzIGFuIG9uUmVzcG9uc2UgaGFuZGxlciB3YWl0aW5nIGZvciBDQ1IgdG8gcmVwbHkuXG4gICAgICAgICAgY29uc3QgcGVuZGluZ1Blcm1pc3Npb25IYW5kbGVycyA9IG5ldyBNYXA8XG4gICAgICAgICAgICBzdHJpbmcsXG4gICAgICAgICAgICAocmVzcG9uc2U6IEJyaWRnZVBlcm1pc3Npb25SZXNwb25zZSkgPT4gdm9pZFxuICAgICAgICAgID4oKVxuXG4gICAgICAgICAgLy8gRGlzcGF0Y2ggaW5jb21pbmcgY29udHJvbF9yZXNwb25zZSBtZXNzYWdlcyB0byByZWdpc3RlcmVkIGhhbmRsZXJzXG4gICAgICAgICAgZnVuY3Rpb24gaGFuZGxlUGVybWlzc2lvblJlc3BvbnNlKG1zZzogU0RLQ29udHJvbFJlc3BvbnNlKTogdm9pZCB7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0SWQgPSBtc2cucmVzcG9uc2U/LnJlcXVlc3RfaWRcbiAgICAgICAgICAgIGlmICghcmVxdWVzdElkKSByZXR1cm5cbiAgICAgICAgICAgIGNvbnN0IGhhbmRsZXIgPSBwZW5kaW5nUGVybWlzc2lvbkhhbmRsZXJzLmdldChyZXF1ZXN0SWQpXG4gICAgICAgICAgICBpZiAoIWhhbmRsZXIpIHtcbiAgICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgICAgIGBbYnJpZGdlOnJlcGxdIE5vIGhhbmRsZXIgZm9yIGNvbnRyb2xfcmVzcG9uc2UgcmVxdWVzdF9pZD0ke3JlcXVlc3RJZH1gLFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcGVuZGluZ1Blcm1pc3Npb25IYW5kbGVycy5kZWxldGUocmVxdWVzdElkKVxuICAgICAgICAgICAgLy8gRXh0cmFjdCB0aGUgcGVybWlzc2lvbiBkZWNpc2lvbiBmcm9tIHRoZSBjb250cm9sX3Jlc3BvbnNlIHBheWxvYWRcbiAgICAgICAgICAgIGNvbnN0IGlubmVyID0gbXNnLnJlc3BvbnNlXG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgIGlubmVyLnN1YnR5cGUgPT09ICdzdWNjZXNzJyAmJlxuICAgICAgICAgICAgICBpbm5lci5yZXNwb25zZSAmJlxuICAgICAgICAgICAgICBpc0JyaWRnZVBlcm1pc3Npb25SZXNwb25zZShpbm5lci5yZXNwb25zZSlcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICBoYW5kbGVyKGlubmVyLnJlc3BvbnNlKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IGhhbmRsZSA9IGF3YWl0IGluaXRSZXBsQnJpZGdlKHtcbiAgICAgICAgICAgIG91dGJvdW5kT25seSxcbiAgICAgICAgICAgIHRhZ3M6IG91dGJvdW5kT25seSA/IFsnY2NyLW1pcnJvciddIDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgb25JbmJvdW5kTWVzc2FnZTogaGFuZGxlSW5ib3VuZE1lc3NhZ2UsXG4gICAgICAgICAgICBvblBlcm1pc3Npb25SZXNwb25zZTogaGFuZGxlUGVybWlzc2lvblJlc3BvbnNlLFxuICAgICAgICAgICAgb25JbnRlcnJ1cHQoKSB7XG4gICAgICAgICAgICAgIGFib3J0Q29udHJvbGxlclJlZi5jdXJyZW50Py5hYm9ydCgpXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgb25TZXRNb2RlbChtb2RlbCkge1xuICAgICAgICAgICAgICBjb25zdCByZXNvbHZlZCA9IG1vZGVsID09PSAnZGVmYXVsdCcgPyBudWxsIDogKG1vZGVsID8/IG51bGwpXG4gICAgICAgICAgICAgIHNldE1haW5Mb29wTW9kZWxPdmVycmlkZShyZXNvbHZlZClcbiAgICAgICAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHByZXYubWFpbkxvb3BNb2RlbEZvclNlc3Npb24gPT09IHJlc29sdmVkKSByZXR1cm4gcHJldlxuICAgICAgICAgICAgICAgIHJldHVybiB7IC4uLnByZXYsIG1haW5Mb29wTW9kZWxGb3JTZXNzaW9uOiByZXNvbHZlZCB9XG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgb25TZXRNYXhUaGlua2luZ1Rva2VucyhtYXhUb2tlbnMpIHtcbiAgICAgICAgICAgICAgY29uc3QgZW5hYmxlZCA9IG1heFRva2VucyAhPT0gbnVsbFxuICAgICAgICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgICAgICAgICBpZiAocHJldi50aGlua2luZ0VuYWJsZWQgPT09IGVuYWJsZWQpIHJldHVybiBwcmV2XG4gICAgICAgICAgICAgICAgcmV0dXJuIHsgLi4ucHJldiwgdGhpbmtpbmdFbmFibGVkOiBlbmFibGVkIH1cbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBvblNldFBlcm1pc3Npb25Nb2RlKG1vZGUpIHtcbiAgICAgICAgICAgICAgLy8gUG9saWN5IGd1YXJkcyBNVVNUIGZpcmUgYmVmb3JlIHRyYW5zaXRpb25QZXJtaXNzaW9uTW9kZSDigJRcbiAgICAgICAgICAgICAgLy8gaXRzIGludGVybmFsIGF1dG8tZ2F0ZSBjaGVjayBpcyBhIGRlZmVuc2l2ZSB0aHJvdyAod2l0aCBhXG4gICAgICAgICAgICAgIC8vIHNldEF1dG9Nb2RlQWN0aXZlKHRydWUpIHNpZGUtZWZmZWN0IEJFRk9SRSB0aGUgdGhyb3cpIHJhdGhlclxuICAgICAgICAgICAgICAvLyB0aGFuIGEgZ3JhY2VmdWwgcmVqZWN0LiBMZXR0aW5nIHRoYXQgdGhyb3cgZXNjYXBlIHdvdWxkOlxuICAgICAgICAgICAgICAvLyAoMSkgbGVhdmUgU1RBVEUuYXV0b01vZGVBY3RpdmU9dHJ1ZSB3aGlsZSB0aGUgbW9kZSBpc1xuICAgICAgICAgICAgICAvLyAgICAgdW5jaGFuZ2VkICgzLXdheSBpbnZhcmlhbnQgdmlvbGF0aW9uIHBlciBzcmMvQ0xBVURFLm1kKVxuICAgICAgICAgICAgICAvLyAoMikgZmFpbCB0byBzZW5kIGEgY29udHJvbF9yZXNwb25zZSDihpIgc2VydmVyIGtpbGxzIFdTXG4gICAgICAgICAgICAgIC8vIFRoZXNlIG1pcnJvciBwcmludC50cyBoYW5kbGVTZXRQZXJtaXNzaW9uTW9kZTsgdGhlIGJyaWRnZVxuICAgICAgICAgICAgICAvLyBjYW4ndCBpbXBvcnQgdGhlIGNoZWNrcyBkaXJlY3RseSAoYm9vdHN0cmFwLWlzb2xhdGlvbiksIHNvXG4gICAgICAgICAgICAgIC8vIGl0IHJlbGllcyBvbiB0aGlzIHZlcmRpY3QgdG8gZW1pdCB0aGUgZXJyb3IgcmVzcG9uc2UuXG4gICAgICAgICAgICAgIGlmIChtb2RlID09PSAnYnlwYXNzUGVybWlzc2lvbnMnKSB7XG4gICAgICAgICAgICAgICAgaWYgKGlzQnlwYXNzUGVybWlzc2lvbnNNb2RlRGlzYWJsZWQoKSkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgICAgb2s6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBlcnJvcjpcbiAgICAgICAgICAgICAgICAgICAgICAnQ2Fubm90IHNldCBwZXJtaXNzaW9uIG1vZGUgdG8gYnlwYXNzUGVybWlzc2lvbnMgYmVjYXVzZSBpdCBpcyBkaXNhYmxlZCBieSBzZXR0aW5ncyBvciBjb25maWd1cmF0aW9uJyxcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgIXN0b3JlLmdldFN0YXRlKCkudG9vbFBlcm1pc3Npb25Db250ZXh0XG4gICAgICAgICAgICAgICAgICAgIC5pc0J5cGFzc1Blcm1pc3Npb25zTW9kZUF2YWlsYWJsZVxuICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgICAgb2s6IGZhbHNlLFxuICAgICAgICAgICAgICAgICAgICBlcnJvcjpcbiAgICAgICAgICAgICAgICAgICAgICAnQ2Fubm90IHNldCBwZXJtaXNzaW9uIG1vZGUgdG8gYnlwYXNzUGVybWlzc2lvbnMgYmVjYXVzZSB0aGUgc2Vzc2lvbiB3YXMgbm90IGxhdW5jaGVkIHdpdGggLS1kYW5nZXJvdXNseS1za2lwLXBlcm1pc3Npb25zJyxcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgIGZlYXR1cmUoJ1RSQU5TQ1JJUFRfQ0xBU1NJRklFUicpICYmXG4gICAgICAgICAgICAgICAgbW9kZSA9PT0gJ2F1dG8nICYmXG4gICAgICAgICAgICAgICAgIWlzQXV0b01vZGVHYXRlRW5hYmxlZCgpXG4gICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlYXNvbiA9IGdldEF1dG9Nb2RlVW5hdmFpbGFibGVSZWFzb24oKVxuICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICBvazogZmFsc2UsXG4gICAgICAgICAgICAgICAgICBlcnJvcjogcmVhc29uXG4gICAgICAgICAgICAgICAgICAgID8gYENhbm5vdCBzZXQgcGVybWlzc2lvbiBtb2RlIHRvIGF1dG86ICR7Z2V0QXV0b01vZGVVbmF2YWlsYWJsZU5vdGlmaWNhdGlvbihyZWFzb24pfWBcbiAgICAgICAgICAgICAgICAgICAgOiAnQ2Fubm90IHNldCBwZXJtaXNzaW9uIG1vZGUgdG8gYXV0bycsXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIC8vIEd1YXJkcyBwYXNzZWQg4oCUIGFwcGx5IHZpYSB0aGUgY2VudHJhbGl6ZWQgdHJhbnNpdGlvbiBzb1xuICAgICAgICAgICAgICAvLyBwcmVQbGFuTW9kZSBzdGFzaGluZyBhbmQgYXV0by1tb2RlIHN0YXRlIHN5bmMgYWxsIGZpcmUuXG4gICAgICAgICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGN1cnJlbnQgPSBwcmV2LnRvb2xQZXJtaXNzaW9uQ29udGV4dC5tb2RlXG4gICAgICAgICAgICAgICAgaWYgKGN1cnJlbnQgPT09IG1vZGUpIHJldHVybiBwcmV2XG4gICAgICAgICAgICAgICAgY29uc3QgbmV4dCA9IHRyYW5zaXRpb25QZXJtaXNzaW9uTW9kZShcbiAgICAgICAgICAgICAgICAgIGN1cnJlbnQsXG4gICAgICAgICAgICAgICAgICBtb2RlLFxuICAgICAgICAgICAgICAgICAgcHJldi50b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiB7IC4uLm5leHQsIG1vZGUgfSxcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIC8vIFJlY2hlY2sgcXVldWVkIHBlcm1pc3Npb24gcHJvbXB0cyBub3cgdGhhdCBtb2RlIGNoYW5nZWQuXG4gICAgICAgICAgICAgIHNldEltbWVkaWF0ZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgZ2V0TGVhZGVyVG9vbFVzZUNvbmZpcm1RdWV1ZSgpPy4oY3VycmVudFF1ZXVlID0+IHtcbiAgICAgICAgICAgICAgICAgIGN1cnJlbnRRdWV1ZS5mb3JFYWNoKGl0ZW0gPT4ge1xuICAgICAgICAgICAgICAgICAgICB2b2lkIGl0ZW0ucmVjaGVja1Blcm1pc3Npb24oKVxuICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgIHJldHVybiBjdXJyZW50UXVldWVcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICByZXR1cm4geyBvazogdHJ1ZSB9XG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgb25TdGF0ZUNoYW5nZTogaGFuZGxlU3RhdGVDaGFuZ2UsXG4gICAgICAgICAgICBpbml0aWFsTWVzc2FnZXM6IG1lc3NhZ2VzLmxlbmd0aCA+IDAgPyBtZXNzYWdlcyA6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIGdldE1lc3NhZ2VzOiAoKSA9PiBtZXNzYWdlc1JlZi5jdXJyZW50LFxuICAgICAgICAgICAgcHJldmlvdXNseUZsdXNoZWRVVUlEczogZmx1c2hlZFVVSURzUmVmLmN1cnJlbnQsXG4gICAgICAgICAgICBpbml0aWFsTmFtZTogcmVwbEJyaWRnZUluaXRpYWxOYW1lLFxuICAgICAgICAgICAgcGVycGV0dWFsLFxuICAgICAgICAgIH0pXG4gICAgICAgICAgaWYgKGNhbmNlbGxlZCkge1xuICAgICAgICAgICAgLy8gRWZmZWN0IHdhcyBjYW5jZWxsZWQgd2hpbGUgaW5pdFJlcGxCcmlkZ2Ugd2FzIGluIGZsaWdodC5cbiAgICAgICAgICAgIC8vIFRlYXIgZG93biB0aGUgaGFuZGxlIHRvIGF2b2lkIGxlYWtpbmcgcmVzb3VyY2VzIChwb2xsIGxvb3AsXG4gICAgICAgICAgICAvLyBXZWJTb2NrZXQsIHJlZ2lzdGVyZWQgZW52aXJvbm1lbnQsIGNsZWFudXAgY2FsbGJhY2spLlxuICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgICBgW2JyaWRnZTpyZXBsXSBIb29rOiBpbml0IGNhbmNlbGxlZCBkdXJpbmcgZmxpZ2h0LCB0ZWFyaW5nIGRvd24ke2hhbmRsZSA/IGAgZW52PSR7aGFuZGxlLmVudmlyb25tZW50SWR9YCA6ICcnfWAsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBpZiAoaGFuZGxlKSB7XG4gICAgICAgICAgICAgIHZvaWQgaGFuZGxlLnRlYXJkb3duKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoIWhhbmRsZSkge1xuICAgICAgICAgICAgLy8gaW5pdFJlcGxCcmlkZ2UgcmV0dXJuZWQgbnVsbCDigJQgYSBwcmVjb25kaXRpb24gZmFpbGVkLiBGb3IgbW9zdFxuICAgICAgICAgICAgLy8gY2FzZXMgKG5vX29hdXRoLCBwb2xpY3lfZGVuaWVkLCBldGMuKSBvblN0YXRlQ2hhbmdlKCdmYWlsZWQnKVxuICAgICAgICAgICAgLy8gYWxyZWFkeSBmaXJlZCB3aXRoIGEgc3BlY2lmaWMgaGludC4gVGhlIEdyb3d0aEJvb2stZ2F0ZS1vZmYgY2FzZVxuICAgICAgICAgICAgLy8gaXMgaW50ZW50aW9uYWxseSBzaWxlbnQg4oCUIG5vdCBhIGZhaWx1cmUsIGp1c3Qgbm90IHJvbGxlZCBvdXQuXG4gICAgICAgICAgICBjb25zZWN1dGl2ZUZhaWx1cmVzUmVmLmN1cnJlbnQrK1xuICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgICBgW2JyaWRnZTpyZXBsXSBJbml0IHJldHVybmVkIG51bGwgKHByZWNvbmRpdGlvbiBvciBzZXNzaW9uIGNyZWF0aW9uIGZhaWxlZCk7IGNvbnNlY3V0aXZlIGZhaWx1cmVzOiAke2NvbnNlY3V0aXZlRmFpbHVyZXNSZWYuY3VycmVudH1gLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgY2xlYXJUaW1lb3V0KGZhaWx1cmVUaW1lb3V0UmVmLmN1cnJlbnQpXG4gICAgICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgIHJlcGxCcmlkZ2VFcnJvcjpcbiAgICAgICAgICAgICAgICBwcmV2LnJlcGxCcmlkZ2VFcnJvciA/PyAnY2hlY2sgZGVidWcgbG9ncyBmb3IgZGV0YWlscycsXG4gICAgICAgICAgICB9KSlcbiAgICAgICAgICAgIGZhaWx1cmVUaW1lb3V0UmVmLmN1cnJlbnQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgaWYgKGNhbmNlbGxlZCkgcmV0dXJuXG4gICAgICAgICAgICAgIGZhaWx1cmVUaW1lb3V0UmVmLmN1cnJlbnQgPSB1bmRlZmluZWRcbiAgICAgICAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKCFwcmV2LnJlcGxCcmlkZ2VFcnJvcikgcmV0dXJuIHByZXZcbiAgICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VFbmFibGVkOiBmYWxzZSxcbiAgICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VFcnJvcjogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIH0sIEJSSURHRV9GQUlMVVJFX0RJU01JU1NfTVMpXG4gICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICB9XG4gICAgICAgICAgaGFuZGxlUmVmLmN1cnJlbnQgPSBoYW5kbGVcbiAgICAgICAgICBzZXRSZXBsQnJpZGdlSGFuZGxlKGhhbmRsZSlcbiAgICAgICAgICBjb25zZWN1dGl2ZUZhaWx1cmVzUmVmLmN1cnJlbnQgPSAwXG4gICAgICAgICAgLy8gU2tpcCBpbml0aWFsIG1lc3NhZ2VzIGluIHRoZSBmb3J3YXJkaW5nIGVmZmVjdCDigJQgdGhleSB3ZXJlXG4gICAgICAgICAgLy8gYWxyZWFkeSBsb2FkZWQgYXMgc2Vzc2lvbiBldmVudHMgZHVyaW5nIGNyZWF0aW9uLlxuICAgICAgICAgIGxhc3RXcml0dGVuSW5kZXhSZWYuY3VycmVudCA9IGluaXRpYWxNZXNzYWdlQ291bnRcblxuICAgICAgICAgIGlmIChvdXRib3VuZE9ubHkpIHtcbiAgICAgICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgcHJldi5yZXBsQnJpZGdlQ29ubmVjdGVkICYmXG4gICAgICAgICAgICAgICAgcHJldi5yZXBsQnJpZGdlU2Vzc2lvbklkID09PSBoYW5kbGUuYnJpZGdlU2Vzc2lvbklkXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICByZXR1cm4gcHJldlxuICAgICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgICAgcmVwbEJyaWRnZUNvbm5lY3RlZDogdHJ1ZSxcbiAgICAgICAgICAgICAgICByZXBsQnJpZGdlU2Vzc2lvbklkOiBoYW5kbGUuYnJpZGdlU2Vzc2lvbklkLFxuICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VTZXNzaW9uVXJsOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgcmVwbEJyaWRnZUNvbm5lY3RVcmw6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICByZXBsQnJpZGdlRXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgICAgYFticmlkZ2U6cmVwbF0gTWlycm9yIGluaXRpYWxpemVkLCBzZXNzaW9uPSR7aGFuZGxlLmJyaWRnZVNlc3Npb25JZH1gLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBCdWlsZCBicmlkZ2UgcGVybWlzc2lvbiBjYWxsYmFja3Mgc28gdGhlIGludGVyYWN0aXZlIHBlcm1pc3Npb25cbiAgICAgICAgICAgIC8vIGhhbmRsZXIgY2FuIHJhY2UgYnJpZGdlIHJlc3BvbnNlcyBhZ2FpbnN0IGxvY2FsIHVzZXIgaW50ZXJhY3Rpb24uXG4gICAgICAgICAgICBjb25zdCBwZXJtaXNzaW9uQ2FsbGJhY2tzOiBCcmlkZ2VQZXJtaXNzaW9uQ2FsbGJhY2tzID0ge1xuICAgICAgICAgICAgICBzZW5kUmVxdWVzdChcbiAgICAgICAgICAgICAgICByZXF1ZXN0SWQsXG4gICAgICAgICAgICAgICAgdG9vbE5hbWUsXG4gICAgICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICAgICAgdG9vbFVzZUlkLFxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uLFxuICAgICAgICAgICAgICAgIHBlcm1pc3Npb25TdWdnZXN0aW9ucyxcbiAgICAgICAgICAgICAgICBibG9ja2VkUGF0aCxcbiAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgaGFuZGxlLnNlbmRDb250cm9sUmVxdWVzdCh7XG4gICAgICAgICAgICAgICAgICB0eXBlOiAnY29udHJvbF9yZXF1ZXN0JyxcbiAgICAgICAgICAgICAgICAgIHJlcXVlc3RfaWQ6IHJlcXVlc3RJZCxcbiAgICAgICAgICAgICAgICAgIHJlcXVlc3Q6IHtcbiAgICAgICAgICAgICAgICAgICAgc3VidHlwZTogJ2Nhbl91c2VfdG9vbCcsXG4gICAgICAgICAgICAgICAgICAgIHRvb2xfbmFtZTogdG9vbE5hbWUsXG4gICAgICAgICAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgICAgICAgICB0b29sX3VzZV9pZDogdG9vbFVzZUlkLFxuICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbixcbiAgICAgICAgICAgICAgICAgICAgLi4uKHBlcm1pc3Npb25TdWdnZXN0aW9uc1xuICAgICAgICAgICAgICAgICAgICAgID8geyBwZXJtaXNzaW9uX3N1Z2dlc3Rpb25zOiBwZXJtaXNzaW9uU3VnZ2VzdGlvbnMgfVxuICAgICAgICAgICAgICAgICAgICAgIDoge30pLFxuICAgICAgICAgICAgICAgICAgICAuLi4oYmxvY2tlZFBhdGggPyB7IGJsb2NrZWRfcGF0aDogYmxvY2tlZFBhdGggfSA6IHt9KSxcbiAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgc2VuZFJlc3BvbnNlKHJlcXVlc3RJZCwgcmVzcG9uc2UpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwYXlsb2FkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHsgLi4ucmVzcG9uc2UgfVxuICAgICAgICAgICAgICAgIGhhbmRsZS5zZW5kQ29udHJvbFJlc3BvbnNlKHtcbiAgICAgICAgICAgICAgICAgIHR5cGU6ICdjb250cm9sX3Jlc3BvbnNlJyxcbiAgICAgICAgICAgICAgICAgIHJlc3BvbnNlOiB7XG4gICAgICAgICAgICAgICAgICAgIHN1YnR5cGU6ICdzdWNjZXNzJyxcbiAgICAgICAgICAgICAgICAgICAgcmVxdWVzdF9pZDogcmVxdWVzdElkLFxuICAgICAgICAgICAgICAgICAgICByZXNwb25zZTogcGF5bG9hZCxcbiAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgY2FuY2VsUmVxdWVzdChyZXF1ZXN0SWQpIHtcbiAgICAgICAgICAgICAgICBoYW5kbGUuc2VuZENvbnRyb2xDYW5jZWxSZXF1ZXN0KHJlcXVlc3RJZClcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgb25SZXNwb25zZShyZXF1ZXN0SWQsIGhhbmRsZXIpIHtcbiAgICAgICAgICAgICAgICBwZW5kaW5nUGVybWlzc2lvbkhhbmRsZXJzLnNldChyZXF1ZXN0SWQsIGhhbmRsZXIpXG4gICAgICAgICAgICAgICAgcmV0dXJuICgpID0+IHtcbiAgICAgICAgICAgICAgICAgIHBlbmRpbmdQZXJtaXNzaW9uSGFuZGxlcnMuZGVsZXRlKHJlcXVlc3RJZClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgIHJlcGxCcmlkZ2VQZXJtaXNzaW9uQ2FsbGJhY2tzOiBwZXJtaXNzaW9uQ2FsbGJhY2tzLFxuICAgICAgICAgICAgfSkpXG4gICAgICAgICAgICBjb25zdCB1cmwgPSBnZXRSZW1vdGVTZXNzaW9uVXJsKFxuICAgICAgICAgICAgICBoYW5kbGUuYnJpZGdlU2Vzc2lvbklkLFxuICAgICAgICAgICAgICBoYW5kbGUuc2Vzc2lvbkluZ3Jlc3NVcmwsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICAvLyBlbnZpcm9ubWVudElkID09PSAnJyBzaWduYWxzIHRoZSB2MiBlbnYtbGVzcyBwYXRoLiBidWlsZEJyaWRnZUNvbm5lY3RVcmxcbiAgICAgICAgICAgIC8vIGJ1aWxkcyBhbiBlbnYtc3BlY2lmaWMgY29ubmVjdCBVUkwsIHdoaWNoIGRvZXNuJ3QgZXhpc3Qgd2l0aG91dCBhbiBlbnYuXG4gICAgICAgICAgICBjb25zdCBoYXNFbnYgPSBoYW5kbGUuZW52aXJvbm1lbnRJZCAhPT0gJydcbiAgICAgICAgICAgIGNvbnN0IGNvbm5lY3RVcmwgPSBoYXNFbnZcbiAgICAgICAgICAgICAgPyBidWlsZEJyaWRnZUNvbm5lY3RVcmwoXG4gICAgICAgICAgICAgICAgICBoYW5kbGUuZW52aXJvbm1lbnRJZCxcbiAgICAgICAgICAgICAgICAgIGhhbmRsZS5zZXNzaW9uSW5ncmVzc1VybCxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgIHByZXYucmVwbEJyaWRnZUNvbm5lY3RlZCAmJlxuICAgICAgICAgICAgICAgIHByZXYucmVwbEJyaWRnZVNlc3Npb25VcmwgPT09IHVybFxuICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcHJldlxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgICAgICByZXBsQnJpZGdlQ29ubmVjdGVkOiB0cnVlLFxuICAgICAgICAgICAgICAgIHJlcGxCcmlkZ2VTZXNzaW9uVXJsOiB1cmwsXG4gICAgICAgICAgICAgICAgcmVwbEJyaWRnZUNvbm5lY3RVcmw6IGNvbm5lY3RVcmwgPz8gcHJldi5yZXBsQnJpZGdlQ29ubmVjdFVybCxcbiAgICAgICAgICAgICAgICByZXBsQnJpZGdlRW52aXJvbm1lbnRJZDogaGFuZGxlLmVudmlyb25tZW50SWQsXG4gICAgICAgICAgICAgICAgcmVwbEJyaWRnZVNlc3Npb25JZDogaGFuZGxlLmJyaWRnZVNlc3Npb25JZCxcbiAgICAgICAgICAgICAgICByZXBsQnJpZGdlRXJyb3I6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgLy8gU2hvdyBicmlkZ2Ugc3RhdHVzIHdpdGggVVJMIGluIHRoZSB0cmFuc2NyaXB0LiBwZXJwZXR1YWwgKEtBSVJPU1xuICAgICAgICAgICAgLy8gYXNzaXN0YW50IG1vZGUpIGZhbGxzIGJhY2sgdG8gdjEgYXQgaW5pdFJlcGxCcmlkZ2UudHMg4oCUIHNraXAgdGhlXG4gICAgICAgICAgICAvLyB2Mi1vbmx5IHVwZ3JhZGUgbnVkZ2UgZm9yIHRoZW0uIE93biB0cnkvY2F0Y2ggc28gYSBjb3NtZXRpY1xuICAgICAgICAgICAgLy8gR3Jvd3RoQm9vayBoaWNjdXAgZG9lc24ndCBoaXQgdGhlIG91dGVyIGluaXQtZmFpbHVyZSBoYW5kbGVyLlxuICAgICAgICAgICAgY29uc3QgdXBncmFkZU51ZGdlID0gIXBlcnBldHVhbFxuICAgICAgICAgICAgICA/IGF3YWl0IHNob3VsZFNob3dBcHBVcGdyYWRlTWVzc2FnZSgpLmNhdGNoKCgpID0+IGZhbHNlKVxuICAgICAgICAgICAgICA6IGZhbHNlXG4gICAgICAgICAgICBpZiAoY2FuY2VsbGVkKSByZXR1cm5cbiAgICAgICAgICAgIHNldE1lc3NhZ2VzKHByZXYgPT4gW1xuICAgICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgICBjcmVhdGVCcmlkZ2VTdGF0dXNNZXNzYWdlKFxuICAgICAgICAgICAgICAgIHVybCxcbiAgICAgICAgICAgICAgICB1cGdyYWRlTnVkZ2VcbiAgICAgICAgICAgICAgICAgID8gJ1BsZWFzZSB1cGdyYWRlIHRvIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiB0aGUgQ2xhdWRlIG1vYmlsZSBhcHAgdG8gc2VlIHlvdXIgUmVtb3RlIENvbnRyb2wgc2Vzc2lvbnMuJ1xuICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICBdKVxuXG4gICAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgICAgIGBbYnJpZGdlOnJlcGxdIEhvb2sgaW5pdGlhbGl6ZWQsIHNlc3Npb249JHtoYW5kbGUuYnJpZGdlU2Vzc2lvbklkfWAsXG4gICAgICAgICAgICApXG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAvLyBOZXZlciBjcmFzaCB0aGUgUkVQTCDigJQgc3VyZmFjZSB0aGUgZXJyb3IgaW4gdGhlIFVJLlxuICAgICAgICAgIC8vIENoZWNrIGNhbmNlbGxlZCBmaXJzdCAoc3ltbWV0cnkgd2l0aCB0aGUgIWhhbmRsZSBwYXRoIGF0IGxpbmUgfjM4Nik6XG4gICAgICAgICAgLy8gaWYgaW5pdFJlcGxCcmlkZ2UgdGhyZXcgZHVyaW5nIHJhcGlkIHRvZ2dsZS1vZmYgKGluLWZsaWdodCBuZXR3b3JrXG4gICAgICAgICAgLy8gZXJyb3IpLCBkb24ndCBjb3VudCB0aGF0IHRvd2FyZCB0aGUgZnVzZSBvciBzcGFtIGEgc3RhbGUgZXJyb3JcbiAgICAgICAgICAvLyBpbnRvIHRoZSBVSS4gQWxzbyBmaXhlcyBwcmUtZXhpc3Rpbmcgc3B1cmlvdXMgc2V0QXBwU3RhdGUvXG4gICAgICAgICAgLy8gc2V0TWVzc2FnZXMgb24gY2FuY2VsbGVkIHRocm93cy5cbiAgICAgICAgICBpZiAoY2FuY2VsbGVkKSByZXR1cm5cbiAgICAgICAgICBjb25zZWN1dGl2ZUZhaWx1cmVzUmVmLmN1cnJlbnQrK1xuICAgICAgICAgIGNvbnN0IGVyck1zZyA9IGVycm9yTWVzc2FnZShlcnIpXG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYFticmlkZ2U6cmVwbF0gSW5pdCBmYWlsZWQ6ICR7ZXJyTXNnfTsgY29uc2VjdXRpdmUgZmFpbHVyZXM6ICR7Y29uc2VjdXRpdmVGYWlsdXJlc1JlZi5jdXJyZW50fWAsXG4gICAgICAgICAgKVxuICAgICAgICAgIGNsZWFyVGltZW91dChmYWlsdXJlVGltZW91dFJlZi5jdXJyZW50KVxuICAgICAgICAgIG5vdGlmeUJyaWRnZUZhaWxlZChlcnJNc2cpXG4gICAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiAoe1xuICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgIHJlcGxCcmlkZ2VFcnJvcjogZXJyTXNnLFxuICAgICAgICAgIH0pKVxuICAgICAgICAgIGZhaWx1cmVUaW1lb3V0UmVmLmN1cnJlbnQgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgIGlmIChjYW5jZWxsZWQpIHJldHVyblxuICAgICAgICAgICAgZmFpbHVyZVRpbWVvdXRSZWYuY3VycmVudCA9IHVuZGVmaW5lZFxuICAgICAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICAgICAgICAgIGlmICghcHJldi5yZXBsQnJpZGdlRXJyb3IpIHJldHVybiBwcmV2XG4gICAgICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgICAgICByZXBsQnJpZGdlRW5hYmxlZDogZmFsc2UsXG4gICAgICAgICAgICAgICAgcmVwbEJyaWRnZUVycm9yOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgfSwgQlJJREdFX0ZBSUxVUkVfRElTTUlTU19NUylcbiAgICAgICAgICBpZiAoIW91dGJvdW5kT25seSkge1xuICAgICAgICAgICAgc2V0TWVzc2FnZXMocHJldiA9PiBbXG4gICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgIGNyZWF0ZVN5c3RlbU1lc3NhZ2UoXG4gICAgICAgICAgICAgICAgYFJlbW90ZSBDb250cm9sIGZhaWxlZCB0byBjb25uZWN0OiAke2Vyck1zZ31gLFxuICAgICAgICAgICAgICAgICd3YXJuaW5nJyxcbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgIF0pXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9KSgpXG5cbiAgICAgIHJldHVybiAoKSA9PiB7XG4gICAgICAgIGNhbmNlbGxlZCA9IHRydWVcbiAgICAgICAgY2xlYXJUaW1lb3V0KGZhaWx1cmVUaW1lb3V0UmVmLmN1cnJlbnQpXG4gICAgICAgIGZhaWx1cmVUaW1lb3V0UmVmLmN1cnJlbnQgPSB1bmRlZmluZWRcbiAgICAgICAgaWYgKGhhbmRsZVJlZi5jdXJyZW50KSB7XG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYFticmlkZ2U6cmVwbF0gSG9vayBjbGVhbnVwOiBzdGFydGluZyB0ZWFyZG93biBmb3IgZW52PSR7aGFuZGxlUmVmLmN1cnJlbnQuZW52aXJvbm1lbnRJZH0gc2Vzc2lvbj0ke2hhbmRsZVJlZi5jdXJyZW50LmJyaWRnZVNlc3Npb25JZH1gLFxuICAgICAgICAgIClcbiAgICAgICAgICB0ZWFyZG93blByb21pc2VSZWYuY3VycmVudCA9IGhhbmRsZVJlZi5jdXJyZW50LnRlYXJkb3duKClcbiAgICAgICAgICBoYW5kbGVSZWYuY3VycmVudCA9IG51bGxcbiAgICAgICAgICBzZXRSZXBsQnJpZGdlSGFuZGxlKG51bGwpXG4gICAgICAgIH1cbiAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgIXByZXYucmVwbEJyaWRnZUNvbm5lY3RlZCAmJlxuICAgICAgICAgICAgIXByZXYucmVwbEJyaWRnZVNlc3Npb25BY3RpdmUgJiZcbiAgICAgICAgICAgICFwcmV2LnJlcGxCcmlkZ2VFcnJvclxuICAgICAgICAgICkge1xuICAgICAgICAgICAgcmV0dXJuIHByZXZcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICByZXBsQnJpZGdlQ29ubmVjdGVkOiBmYWxzZSxcbiAgICAgICAgICAgIHJlcGxCcmlkZ2VTZXNzaW9uQWN0aXZlOiBmYWxzZSxcbiAgICAgICAgICAgIHJlcGxCcmlkZ2VSZWNvbm5lY3Rpbmc6IGZhbHNlLFxuICAgICAgICAgICAgcmVwbEJyaWRnZUNvbm5lY3RVcmw6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIHJlcGxCcmlkZ2VTZXNzaW9uVXJsOiB1bmRlZmluZWQsXG4gICAgICAgICAgICByZXBsQnJpZGdlRW52aXJvbm1lbnRJZDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgcmVwbEJyaWRnZVNlc3Npb25JZDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgcmVwbEJyaWRnZUVycm9yOiB1bmRlZmluZWQsXG4gICAgICAgICAgICByZXBsQnJpZGdlUGVybWlzc2lvbkNhbGxiYWNrczogdW5kZWZpbmVkLFxuICAgICAgICAgIH1cbiAgICAgICAgfSlcbiAgICAgICAgbGFzdFdyaXR0ZW5JbmRleFJlZi5jdXJyZW50ID0gMFxuICAgICAgfVxuICAgIH1cbiAgfSwgW1xuICAgIHJlcGxCcmlkZ2VFbmFibGVkLFxuICAgIHJlcGxCcmlkZ2VPdXRib3VuZE9ubHksXG4gICAgc2V0QXBwU3RhdGUsXG4gICAgc2V0TWVzc2FnZXMsXG4gICAgYWRkTm90aWZpY2F0aW9uLFxuICBdKVxuXG4gIC8vIFdyaXRlIG5ldyBtZXNzYWdlcyBhcyB0aGV5IGFwcGVhci5cbiAgLy8gQWxzbyByZS1ydW5zIHdoZW4gcmVwbEJyaWRnZUNvbm5lY3RlZCBjaGFuZ2VzIChicmlkZ2UgZmluaXNoZXMgaW5pdCksXG4gIC8vIHNvIGFueSBtZXNzYWdlcyB0aGF0IGFycml2ZWQgYmVmb3JlIHRoZSBicmlkZ2Ugd2FzIHJlYWR5IGdldCB3cml0dGVuLlxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIC8vIFBvc2l0aXZlIGZlYXR1cmUoKSBndWFyZCDigJQgc2VlIGZpcnN0IHVzZUVmZmVjdCBjb21tZW50XG4gICAgaWYgKGZlYXR1cmUoJ0JSSURHRV9NT0RFJykpIHtcbiAgICAgIGlmICghcmVwbEJyaWRnZUNvbm5lY3RlZCkgcmV0dXJuXG5cbiAgICAgIGNvbnN0IGhhbmRsZSA9IGhhbmRsZVJlZi5jdXJyZW50XG4gICAgICBpZiAoIWhhbmRsZSkgcmV0dXJuXG5cbiAgICAgIC8vIENsYW1wIHRoZSBpbmRleCBpbiBjYXNlIG1lc3NhZ2VzIHdlcmUgY29tcGFjdGVkIChhcnJheSBzaG9ydGVuZWQpLlxuICAgICAgLy8gQWZ0ZXIgY29tcGFjdGlvbiB0aGUgcmVmIGNvdWxkIGV4Y2VlZCBtZXNzYWdlcy5sZW5ndGgsIGFuZCB3aXRob3V0XG4gICAgICAvLyBjbGFtcGluZyBubyBuZXcgbWVzc2FnZXMgd291bGQgYmUgZm9yd2FyZGVkLlxuICAgICAgaWYgKGxhc3RXcml0dGVuSW5kZXhSZWYuY3VycmVudCA+IG1lc3NhZ2VzLmxlbmd0aCkge1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgYFticmlkZ2U6cmVwbF0gQ29tcGFjdGlvbiBkZXRlY3RlZDogbGFzdFdyaXR0ZW5JbmRleD0ke2xhc3RXcml0dGVuSW5kZXhSZWYuY3VycmVudH0gPiBtZXNzYWdlcy5sZW5ndGg9JHttZXNzYWdlcy5sZW5ndGh9LCBjbGFtcGluZ2AsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGNvbnN0IHN0YXJ0SW5kZXggPSBNYXRoLm1pbihsYXN0V3JpdHRlbkluZGV4UmVmLmN1cnJlbnQsIG1lc3NhZ2VzLmxlbmd0aClcblxuICAgICAgLy8gQ29sbGVjdCBuZXcgbWVzc2FnZXMgc2luY2UgbGFzdCB3cml0ZVxuICAgICAgY29uc3QgbmV3TWVzc2FnZXM6IE1lc3NhZ2VbXSA9IFtdXG4gICAgICBmb3IgKGxldCBpID0gc3RhcnRJbmRleDsgaSA8IG1lc3NhZ2VzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGNvbnN0IG1zZyA9IG1lc3NhZ2VzW2ldXG4gICAgICAgIGlmIChcbiAgICAgICAgICBtc2cgJiZcbiAgICAgICAgICAobXNnLnR5cGUgPT09ICd1c2VyJyB8fFxuICAgICAgICAgICAgbXNnLnR5cGUgPT09ICdhc3Npc3RhbnQnIHx8XG4gICAgICAgICAgICAobXNnLnR5cGUgPT09ICdzeXN0ZW0nICYmIG1zZy5zdWJ0eXBlID09PSAnbG9jYWxfY29tbWFuZCcpKVxuICAgICAgICApIHtcbiAgICAgICAgICBuZXdNZXNzYWdlcy5wdXNoKG1zZylcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgbGFzdFdyaXR0ZW5JbmRleFJlZi5jdXJyZW50ID0gbWVzc2FnZXMubGVuZ3RoXG5cbiAgICAgIGlmIChuZXdNZXNzYWdlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGhhbmRsZS53cml0ZU1lc3NhZ2VzKG5ld01lc3NhZ2VzKVxuICAgICAgfVxuICAgIH1cbiAgfSwgW21lc3NhZ2VzLCByZXBsQnJpZGdlQ29ubmVjdGVkXSlcblxuICBjb25zdCBzZW5kQnJpZGdlUmVzdWx0ID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChmZWF0dXJlKCdCUklER0VfTU9ERScpKSB7XG4gICAgICBoYW5kbGVSZWYuY3VycmVudD8uc2VuZFJlc3VsdCgpXG4gICAgfVxuICB9LCBbXSlcblxuICByZXR1cm4geyBzZW5kQnJpZGdlUmVzdWx0IH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsT0FBT0Ms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 \ No newline at end of file diff --git a/hooks/useSSHSession.ts b/hooks/useSSHSession.ts new file mode 100644 index 0000000..35b3a06 --- /dev/null +++ b/hooks/useSSHSession.ts @@ -0,0 +1,241 @@ +/** + * REPL integration hook for `claude ssh` sessions. + * + * Sibling to useDirectConnect — same shape (isRemoteMode/sendMessage/ + * cancelRequest/disconnect), same REPL wiring, but drives an SSH child + * process instead of a WebSocket. Kept separate rather than generalizing + * useDirectConnect because the lifecycle differs: the ssh process and auth + * proxy are created BEFORE this hook runs (during startup, in main.tsx) and + * handed in; useDirectConnect creates its WebSocket inside the effect. + */ + +import { randomUUID } from 'crypto' +import { useCallback, useEffect, useMemo, useRef } from 'react' +import type { ToolUseConfirm } from '../components/permissions/PermissionRequest.js' +import { + createSyntheticAssistantMessage, + createToolStub, +} from '../remote/remotePermissionBridge.js' +import { + convertSDKMessage, + isSessionEndMessage, +} from '../remote/sdkMessageAdapter.js' +import type { SSHSession } from '../ssh/createSSHSession.js' +import type { SSHSessionManager } from '../ssh/SSHSessionManager.js' +import type { Tool } from '../Tool.js' +import { findToolByName } from '../Tool.js' +import type { Message as MessageType } from '../types/message.js' +import type { PermissionAskDecision } from '../types/permissions.js' +import { logForDebugging } from '../utils/debug.js' +import { gracefulShutdown } from '../utils/gracefulShutdown.js' +import type { RemoteMessageContent } from '../utils/teleport/api.js' + +type UseSSHSessionResult = { + isRemoteMode: boolean + sendMessage: (content: RemoteMessageContent) => Promise + cancelRequest: () => void + disconnect: () => void +} + +type UseSSHSessionProps = { + session: SSHSession | undefined + setMessages: React.Dispatch> + setIsLoading: (loading: boolean) => void + setToolUseConfirmQueue: React.Dispatch> + tools: Tool[] +} + +export function useSSHSession({ + session, + setMessages, + setIsLoading, + setToolUseConfirmQueue, + tools, +}: UseSSHSessionProps): UseSSHSessionResult { + const isRemoteMode = !!session + + const managerRef = useRef(null) + const hasReceivedInitRef = useRef(false) + const isConnectedRef = useRef(false) + + const toolsRef = useRef(tools) + useEffect(() => { + toolsRef.current = tools + }, [tools]) + + useEffect(() => { + if (!session) return + + hasReceivedInitRef.current = false + logForDebugging('[useSSHSession] wiring SSH session manager') + + const manager = session.createManager({ + onMessage: sdkMessage => { + if (isSessionEndMessage(sdkMessage)) { + setIsLoading(false) + } + + // Skip duplicate init messages (one per turn from stream-json mode). + if (sdkMessage.type === 'system' && sdkMessage.subtype === 'init') { + if (hasReceivedInitRef.current) return + hasReceivedInitRef.current = true + } + + const converted = convertSDKMessage(sdkMessage, { + convertToolResults: true, + }) + if (converted.type === 'message') { + setMessages(prev => [...prev, converted.message]) + } + }, + onPermissionRequest: (request, requestId) => { + logForDebugging( + `[useSSHSession] permission request: ${request.tool_name}`, + ) + + const tool = + findToolByName(toolsRef.current, request.tool_name) ?? + createToolStub(request.tool_name) + + const syntheticMessage = createSyntheticAssistantMessage( + request, + requestId, + ) + + const permissionResult: PermissionAskDecision = { + behavior: 'ask', + message: + request.description ?? `${request.tool_name} requires permission`, + suggestions: request.permission_suggestions, + blockedPath: request.blocked_path, + } + + const toolUseConfirm: ToolUseConfirm = { + assistantMessage: syntheticMessage, + tool, + description: + request.description ?? `${request.tool_name} requires permission`, + input: request.input, + toolUseContext: {} as ToolUseConfirm['toolUseContext'], + toolUseID: request.tool_use_id, + permissionResult, + permissionPromptStartTimeMs: Date.now(), + onUserInteraction() {}, + onAbort() { + manager.respondToPermissionRequest(requestId, { + behavior: 'deny', + message: 'User aborted', + }) + setToolUseConfirmQueue(q => + q.filter(i => i.toolUseID !== request.tool_use_id), + ) + }, + onAllow(updatedInput) { + manager.respondToPermissionRequest(requestId, { + behavior: 'allow', + updatedInput, + }) + setToolUseConfirmQueue(q => + q.filter(i => i.toolUseID !== request.tool_use_id), + ) + setIsLoading(true) + }, + onReject(feedback) { + manager.respondToPermissionRequest(requestId, { + behavior: 'deny', + message: feedback ?? 'User denied permission', + }) + setToolUseConfirmQueue(q => + q.filter(i => i.toolUseID !== request.tool_use_id), + ) + }, + async recheckPermission() {}, + } + + setToolUseConfirmQueue(q => [...q, toolUseConfirm]) + setIsLoading(false) + }, + onConnected: () => { + logForDebugging('[useSSHSession] connected') + isConnectedRef.current = true + }, + onReconnecting: (attempt, max) => { + logForDebugging( + `[useSSHSession] ssh dropped, reconnecting (${attempt}/${max})`, + ) + isConnectedRef.current = false + // Surface a transient system message in the transcript so the user + // knows what's happening — the next onConnected clears the state. + // Any in-flight request is lost; the remote's --continue reloads + // history but there's no turn in progress to resume. + setIsLoading(false) + const msg: MessageType = { + type: 'system', + subtype: 'informational', + content: `SSH connection dropped — reconnecting (attempt ${attempt}/${max})...`, + timestamp: new Date().toISOString(), + uuid: randomUUID(), + level: 'warning', + } + setMessages(prev => [...prev, msg]) + }, + onDisconnected: () => { + logForDebugging('[useSSHSession] ssh process exited (giving up)') + const stderr = session.getStderrTail().trim() + const connected = isConnectedRef.current + const exitCode = session.proc.exitCode + isConnectedRef.current = false + setIsLoading(false) + + let msg = connected + ? 'Remote session ended.' + : 'SSH session failed before connecting.' + // Surface remote stderr if it looks like an error (pre-connect always, + // post-connect only on nonzero exit — normal --verbose noise otherwise). + if (stderr && (!connected || exitCode !== 0)) { + msg += `\nRemote stderr (exit ${exitCode ?? 'signal ' + session.proc.signalCode}):\n${stderr}` + } + void gracefulShutdown(1, 'other', { finalMessage: msg }) + }, + onError: error => { + logForDebugging(`[useSSHSession] error: ${error.message}`) + }, + }) + + managerRef.current = manager + manager.connect() + + return () => { + logForDebugging('[useSSHSession] cleanup') + manager.disconnect() + session.proxy.stop() + managerRef.current = null + } + }, [session, setMessages, setIsLoading, setToolUseConfirmQueue]) + + const sendMessage = useCallback( + async (content: RemoteMessageContent): Promise => { + const m = managerRef.current + if (!m) return false + setIsLoading(true) + return m.sendMessage(content) + }, + [setIsLoading], + ) + + const cancelRequest = useCallback(() => { + managerRef.current?.sendInterrupt() + setIsLoading(false) + }, [setIsLoading]) + + const disconnect = useCallback(() => { + managerRef.current?.disconnect() + managerRef.current = null + isConnectedRef.current = false + }, []) + + return useMemo( + () => ({ isRemoteMode, sendMessage, cancelRequest, disconnect }), + [isRemoteMode, sendMessage, cancelRequest, disconnect], + ) +} diff --git a/hooks/useScheduledTasks.ts b/hooks/useScheduledTasks.ts new file mode 100644 index 0000000..eaf47e2 --- /dev/null +++ b/hooks/useScheduledTasks.ts @@ -0,0 +1,139 @@ +import { useEffect, useRef } from 'react' +import { useAppStateStore, useSetAppState } from '../state/AppState.js' +import { isTerminalTaskStatus } from '../Task.js' +import { + findTeammateTaskByAgentId, + injectUserMessageToTeammate, +} from '../tasks/InProcessTeammateTask/InProcessTeammateTask.js' +import { isKairosCronEnabled } from '../tools/ScheduleCronTool/prompt.js' +import type { Message } from '../types/message.js' +import { getCronJitterConfig } from '../utils/cronJitterConfig.js' +import { createCronScheduler } from '../utils/cronScheduler.js' +import { removeCronTasks } from '../utils/cronTasks.js' +import { logForDebugging } from '../utils/debug.js' +import { enqueuePendingNotification } from '../utils/messageQueueManager.js' +import { createScheduledTaskFireMessage } from '../utils/messages.js' +import { WORKLOAD_CRON } from '../utils/workloadContext.js' + +type Props = { + isLoading: boolean + /** + * When true, bypasses the isLoading gate so tasks can enqueue while a + * query is streaming rather than deferring to the next 1s check tick + * after the turn ends. Assistant mode no longer forces --proactive + * (#20425) so isLoading drops between turns like a normal REPL — this + * bypass is now a latency nicety, not a starvation fix. The prompt is + * enqueued at 'later' priority either way and drains between turns. + */ + assistantMode?: boolean + setMessages: React.Dispatch> +} + +/** + * REPL wrapper for the cron scheduler. Mounts the scheduler once and tears + * it down on unmount. Fired prompts go into the command queue as 'later' + * priority, which the REPL drains via useCommandQueue between turns. + * + * Scheduler core (timer, file watcher, fire logic) lives in cronScheduler.ts + * so SDK/-p mode can share it — see print.ts for the headless wiring. + */ +export function useScheduledTasks({ + isLoading, + assistantMode = false, + setMessages, +}: Props): void { + // Latest-value ref so the scheduler's isLoading() getter doesn't capture + // a stale closure. The effect mounts once; isLoading changes every turn. + const isLoadingRef = useRef(isLoading) + isLoadingRef.current = isLoading + + const store = useAppStateStore() + const setAppState = useSetAppState() + + useEffect(() => { + // Runtime gate checked here (not at the hook call site) so the hook + // stays unconditionally mounted — rules-of-hooks forbid wrapping the + // call in a dynamic condition. getFeatureValue_CACHED_WITH_REFRESH + // reads from disk; the 5-min TTL fires a background refetch but the + // effect won't re-run on value flip (assistantMode is the only dep), + // so this guard alone is launch-grain. The mid-session killswitch is + // the isKilled option below — check() polls it every tick. + if (!isKairosCronEnabled()) return + + // System-generated — hidden from queue preview and transcript UI. + // In brief mode, executeForkedSlashCommand runs as a background + // subagent and returns no visible messages. In normal mode, + // isMeta is only propagated for plain-text prompts (via + // processTextPrompt); slash commands like /context:fork do not + // forward isMeta, so their messages remain visible in the + // transcript. This is acceptable since normal mode is not the + // primary use case for scheduled tasks. + const enqueueForLead = (prompt: string) => + enqueuePendingNotification({ + value: prompt, + mode: 'prompt', + priority: 'later', + isMeta: true, + // Threaded through to cc_workload= in the billing-header + // attribution block so the API can serve cron-initiated requests + // at lower QoS when capacity is tight. No human is actively + // waiting on this response. + workload: WORKLOAD_CRON, + }) + + const scheduler = createCronScheduler({ + // Missed-task surfacing (onFire fallback). Teammate crons are always + // session-only (durable:false) so they never appear in the missed list, + // which is populated from disk at scheduler startup — this path only + // handles team-lead durable crons. + onFire: enqueueForLead, + // Normal fires receive the full CronTask so we can route by agentId. + onFireTask: task => { + if (task.agentId) { + const teammate = findTeammateTaskByAgentId( + task.agentId, + store.getState().tasks, + ) + if (teammate && !isTerminalTaskStatus(teammate.status)) { + injectUserMessageToTeammate(teammate.id, task.prompt, setAppState) + return + } + // Teammate is gone — clean up the orphaned cron so it doesn't keep + // firing into nowhere every tick. One-shots would auto-delete on + // fire anyway, but recurring crons would loop until auto-expiry. + logForDebugging( + `[ScheduledTasks] teammate ${task.agentId} gone, removing orphaned cron ${task.id}`, + ) + void removeCronTasks([task.id]) + return + } + const msg = createScheduledTaskFireMessage( + `Running scheduled task (${formatCronFireTime(new Date())})`, + ) + setMessages(prev => [...prev, msg]) + enqueueForLead(task.prompt) + }, + isLoading: () => isLoadingRef.current, + assistantMode, + getJitterConfig: getCronJitterConfig, + isKilled: () => !isKairosCronEnabled(), + }) + scheduler.start() + return () => scheduler.stop() + // assistantMode is stable for the session lifetime; store/setAppState are + // stable refs from useSyncExternalStore; setMessages is a stable useCallback. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [assistantMode]) +} + +function formatCronFireTime(d: Date): string { + return d + .toLocaleString('en-US', { + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: '2-digit', + }) + .replace(/,? at |, /, ' ') + .replace(/ ([AP]M)/, (_, ampm) => ampm.toLowerCase()) +} diff --git a/hooks/useSearchInput.ts b/hooks/useSearchInput.ts new file mode 100644 index 0000000..a72fbf4 --- /dev/null +++ b/hooks/useSearchInput.ts @@ -0,0 +1,364 @@ +import { useCallback, useState } from 'react' +import { KeyboardEvent } from '../ink/events/keyboard-event.js' +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until consumers wire handleKeyDown to +import { useInput } from '../ink.js' +import { + Cursor, + getLastKill, + pushToKillRing, + recordYank, + resetKillAccumulation, + resetYankState, + updateYankLength, + yankPop, +} from '../utils/Cursor.js' +import { useTerminalSize } from './useTerminalSize.js' + +type UseSearchInputOptions = { + isActive: boolean + onExit: () => void + /** Esc + Ctrl+C abandon (distinct from onExit = Enter commit). When + * provided: single-Esc calls this directly (no clear-first-then-exit + * two-press). When absent: current behavior — Esc clears non-empty + * query, exits on empty; Ctrl+C silently swallowed (no switch case). */ + onCancel?: () => void + onExitUp?: () => void + columns?: number + passthroughCtrlKeys?: string[] + initialQuery?: string + /** Backspace (and ctrl+h) on empty query calls onCancel ?? onExit — the + * less/vim "delete past the /" convention. Dialogs that want Esc-only + * cancel set this false so a held backspace doesn't eject the user. */ + backspaceExitsOnEmpty?: boolean +} + +type UseSearchInputReturn = { + query: string + setQuery: (q: string) => void + cursorOffset: number + handleKeyDown: (e: KeyboardEvent) => void +} + +function isKillKey(e: KeyboardEvent): boolean { + if (e.ctrl && (e.key === 'k' || e.key === 'u' || e.key === 'w')) { + return true + } + if (e.meta && e.key === 'backspace') { + return true + } + return false +} + +function isYankKey(e: KeyboardEvent): boolean { + return (e.ctrl || e.meta) && e.key === 'y' +} + +// Special key names that fall through the explicit handlers above the +// text-input branch (return/escape/arrows/home/end/tab/backspace/delete +// all early-return). Reject these so e.g. PageUp doesn't leak 'pageup' +// as literal text. The length>=1 check below is intentionally loose — +// batched input like stdin.write('abc') arrives as one multi-char e.key, +// matching the old useInput(input) behavior where cursor.insert(input) +// inserted the full chunk. +const UNHANDLED_SPECIAL_KEYS = new Set([ + 'pageup', + 'pagedown', + 'insert', + 'wheelup', + 'wheeldown', + 'mouse', + 'f1', + 'f2', + 'f3', + 'f4', + 'f5', + 'f6', + 'f7', + 'f8', + 'f9', + 'f10', + 'f11', + 'f12', +]) + +export function useSearchInput({ + isActive, + onExit, + onCancel, + onExitUp, + columns, + passthroughCtrlKeys = [], + initialQuery = '', + backspaceExitsOnEmpty = true, +}: UseSearchInputOptions): UseSearchInputReturn { + const { columns: terminalColumns } = useTerminalSize() + const effectiveColumns = columns ?? terminalColumns + const [query, setQueryState] = useState(initialQuery) + const [cursorOffset, setCursorOffset] = useState(initialQuery.length) + + const setQuery = useCallback((q: string) => { + setQueryState(q) + setCursorOffset(q.length) + }, []) + + const handleKeyDown = (e: KeyboardEvent): void => { + if (!isActive) return + + const cursor = Cursor.fromText(query, effectiveColumns, cursorOffset) + + // Check passthrough ctrl keys + if (e.ctrl && passthroughCtrlKeys.includes(e.key.toLowerCase())) { + return + } + + // Reset kill accumulation for non-kill keys + if (!isKillKey(e)) { + resetKillAccumulation() + } + + // Reset yank state for non-yank keys + if (!isYankKey(e)) { + resetYankState() + } + + // Exit conditions + if (e.key === 'return' || e.key === 'down') { + e.preventDefault() + onExit() + return + } + if (e.key === 'up') { + e.preventDefault() + if (onExitUp) { + onExitUp() + } + return + } + if (e.key === 'escape') { + e.preventDefault() + if (onCancel) { + onCancel() + } else if (query.length > 0) { + setQueryState('') + setCursorOffset(0) + } else { + onExit() + } + return + } + + // Backspace/Delete + if (e.key === 'backspace') { + e.preventDefault() + if (e.meta) { + // Meta+Backspace: kill word before + const { cursor: newCursor, killed } = cursor.deleteWordBefore() + pushToKillRing(killed, 'prepend') + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + if (query.length === 0) { + // Backspace past the / — cancel (clear + snap back), not commit. + // less: same. vim: deletes the / and exits command mode. + if (backspaceExitsOnEmpty) (onCancel ?? onExit)() + return + } + const newCursor = cursor.backspace() + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + + if (e.key === 'delete') { + e.preventDefault() + const newCursor = cursor.del() + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + + // Arrow keys with modifiers (word jump) + if (e.key === 'left' && (e.ctrl || e.meta || e.fn)) { + e.preventDefault() + const newCursor = cursor.prevWord() + setCursorOffset(newCursor.offset) + return + } + if (e.key === 'right' && (e.ctrl || e.meta || e.fn)) { + e.preventDefault() + const newCursor = cursor.nextWord() + setCursorOffset(newCursor.offset) + return + } + + // Plain arrow keys + if (e.key === 'left') { + e.preventDefault() + const newCursor = cursor.left() + setCursorOffset(newCursor.offset) + return + } + if (e.key === 'right') { + e.preventDefault() + const newCursor = cursor.right() + setCursorOffset(newCursor.offset) + return + } + + // Home/End + if (e.key === 'home') { + e.preventDefault() + setCursorOffset(0) + return + } + if (e.key === 'end') { + e.preventDefault() + setCursorOffset(query.length) + return + } + + // Ctrl key bindings + if (e.ctrl) { + e.preventDefault() + switch (e.key.toLowerCase()) { + case 'a': + setCursorOffset(0) + return + case 'e': + setCursorOffset(query.length) + return + case 'b': + setCursorOffset(cursor.left().offset) + return + case 'f': + setCursorOffset(cursor.right().offset) + return + case 'd': { + if (query.length === 0) { + ;(onCancel ?? onExit)() + return + } + const newCursor = cursor.del() + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + case 'h': { + if (query.length === 0) { + if (backspaceExitsOnEmpty) (onCancel ?? onExit)() + return + } + const newCursor = cursor.backspace() + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + case 'k': { + const { cursor: newCursor, killed } = cursor.deleteToLineEnd() + pushToKillRing(killed, 'append') + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + case 'u': { + const { cursor: newCursor, killed } = cursor.deleteToLineStart() + pushToKillRing(killed, 'prepend') + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + case 'w': { + const { cursor: newCursor, killed } = cursor.deleteWordBefore() + pushToKillRing(killed, 'prepend') + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + case 'y': { + const text = getLastKill() + if (text.length > 0) { + const startOffset = cursor.offset + const newCursor = cursor.insert(text) + recordYank(startOffset, text.length) + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + } + return + } + case 'g': + case 'c': + // Cancel (abandon search). ctrl+g is less's cancel key. Only + // fires if onCancel provided — otherwise falls through and + // returns silently (11 call sites, most expect ctrl+c to no-op). + if (onCancel) { + onCancel() + return + } + } + return + } + + // Meta key bindings + if (e.meta) { + e.preventDefault() + switch (e.key.toLowerCase()) { + case 'b': + setCursorOffset(cursor.prevWord().offset) + return + case 'f': + setCursorOffset(cursor.nextWord().offset) + return + case 'd': { + const newCursor = cursor.deleteWordAfter() + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + return + } + case 'y': { + const popResult = yankPop() + if (popResult) { + const { text, start, length } = popResult + const before = query.slice(0, start) + const after = query.slice(start + length) + const newText = before + text + after + const newOffset = start + text.length + updateYankLength(text.length) + setQueryState(newText) + setCursorOffset(newOffset) + } + return + } + } + return + } + + // Tab: ignore + if (e.key === 'tab') { + return + } + + // Regular character input. Accepts multi-char e.key so batched writes + // (stdin.write('abc') in tests, or paste outside bracketed-paste mode) + // insert the full chunk — matching the old useInput behavior. + if (e.key.length >= 1 && !UNHANDLED_SPECIAL_KEYS.has(e.key)) { + e.preventDefault() + const newCursor = cursor.insert(e.key) + setQueryState(newCursor.text) + setCursorOffset(newCursor.offset) + } + } + + // Backward-compat bridge: existing consumers don't yet wire handleKeyDown + // to . Subscribe via useInput and adapt InputEvent → + // KeyboardEvent until all 11 call sites are migrated (separate PRs). + // TODO(onKeyDown-migration): remove once all consumers pass handleKeyDown. + useInput( + (_input, _key, event) => { + handleKeyDown(new KeyboardEvent(event.keypress)) + }, + { isActive }, + ) + + return { query, setQuery, cursorOffset, handleKeyDown } +} diff --git a/hooks/useSessionBackgrounding.ts b/hooks/useSessionBackgrounding.ts new file mode 100644 index 0000000..b27c706 --- /dev/null +++ b/hooks/useSessionBackgrounding.ts @@ -0,0 +1,158 @@ +/** + * Hook for managing session backgrounding (Ctrl+B to background/foreground sessions). + * + * Handles: + * - Calling onBackgroundQuery to spawn a background task for the current query + * - Re-backgrounding foregrounded tasks + * - Syncing foregrounded task messages/state to main view + */ + +import { useCallback, useEffect, useRef } from 'react' +import { useAppState, useSetAppState } from '../state/AppState.js' +import type { Message } from '../types/message.js' + +type UseSessionBackgroundingProps = { + setMessages: (messages: Message[] | ((prev: Message[]) => Message[])) => void + setIsLoading: (loading: boolean) => void + resetLoadingState: () => void + setAbortController: (controller: AbortController | null) => void + onBackgroundQuery: () => void +} + +type UseSessionBackgroundingResult = { + /** Call when user wants to background (Ctrl+B) */ + handleBackgroundSession: () => void +} + +export function useSessionBackgrounding({ + setMessages, + setIsLoading, + resetLoadingState, + setAbortController, + onBackgroundQuery, +}: UseSessionBackgroundingProps): UseSessionBackgroundingResult { + const foregroundedTaskId = useAppState(s => s.foregroundedTaskId) + const foregroundedTask = useAppState(s => + s.foregroundedTaskId ? s.tasks[s.foregroundedTaskId] : undefined, + ) + const setAppState = useSetAppState() + const lastSyncedMessagesLengthRef = useRef(0) + + const handleBackgroundSession = useCallback(() => { + if (foregroundedTaskId) { + // Re-background the foregrounded task + setAppState(prev => { + const taskId = prev.foregroundedTaskId + if (!taskId) return prev + const task = prev.tasks[taskId] + if (!task) { + return { ...prev, foregroundedTaskId: undefined } + } + return { + ...prev, + foregroundedTaskId: undefined, + tasks: { + ...prev.tasks, + [taskId]: { ...task, isBackgrounded: true }, + }, + } + }) + setMessages([]) + resetLoadingState() + setAbortController(null) + return + } + + onBackgroundQuery() + }, [ + foregroundedTaskId, + setAppState, + setMessages, + resetLoadingState, + setAbortController, + onBackgroundQuery, + ]) + + // Sync foregrounded task's messages and loading state to the main view + useEffect(() => { + if (!foregroundedTaskId) { + // Reset when no foregrounded task + lastSyncedMessagesLengthRef.current = 0 + return + } + + if (!foregroundedTask || foregroundedTask.type !== 'local_agent') { + setAppState(prev => ({ ...prev, foregroundedTaskId: undefined })) + resetLoadingState() + lastSyncedMessagesLengthRef.current = 0 + return + } + + // Sync messages from background task to main view + // Only update if messages have actually changed to avoid redundant renders + const taskMessages = foregroundedTask.messages ?? [] + if (taskMessages.length !== lastSyncedMessagesLengthRef.current) { + lastSyncedMessagesLengthRef.current = taskMessages.length + setMessages([...taskMessages]) + } + + if (foregroundedTask.status === 'running') { + // Check if the task was aborted (user pressed Escape) + const taskAbortController = foregroundedTask.abortController + if (taskAbortController?.signal.aborted) { + // Task was aborted - clear foregrounded state immediately + setAppState(prev => { + if (!prev.foregroundedTaskId) return prev + const task = prev.tasks[prev.foregroundedTaskId] + if (!task) return { ...prev, foregroundedTaskId: undefined } + return { + ...prev, + foregroundedTaskId: undefined, + tasks: { + ...prev.tasks, + [prev.foregroundedTaskId]: { ...task, isBackgrounded: true }, + }, + } + }) + resetLoadingState() + setAbortController(null) + lastSyncedMessagesLengthRef.current = 0 + return + } + + setIsLoading(true) + // Set abort controller to the foregrounded task's controller for Escape handling + if (taskAbortController) { + setAbortController(taskAbortController) + } + } else { + // Task completed - restore to background and clear foregrounded view + setAppState(prev => { + const taskId = prev.foregroundedTaskId + if (!taskId) return prev + const task = prev.tasks[taskId] + if (!task) return { ...prev, foregroundedTaskId: undefined } + return { + ...prev, + foregroundedTaskId: undefined, + tasks: { ...prev.tasks, [taskId]: { ...task, isBackgrounded: true } }, + } + }) + resetLoadingState() + setAbortController(null) + lastSyncedMessagesLengthRef.current = 0 + } + }, [ + foregroundedTaskId, + foregroundedTask, + setAppState, + setMessages, + setIsLoading, + resetLoadingState, + setAbortController, + ]) + + return { + handleBackgroundSession, + } +} diff --git a/hooks/useSettings.ts b/hooks/useSettings.ts new file mode 100644 index 0000000..4045070 --- /dev/null +++ b/hooks/useSettings.ts @@ -0,0 +1,17 @@ +import { type AppState, useAppState } from '../state/AppState.js' + +/** + * Settings type as stored in AppState (DeepImmutable wrapped). + * Use this type when you need to annotate variables that hold settings from useSettings(). + */ +export type ReadonlySettings = AppState['settings'] + +/** + * React hook to access current settings from AppState. + * Settings automatically update when files change on disk via settingsChangeDetector. + * + * Use this instead of getSettings_DEPRECATED() in React components for reactive updates. + */ +export function useSettings(): ReadonlySettings { + return useAppState(s => s.settings) +} diff --git a/hooks/useSettingsChange.ts b/hooks/useSettingsChange.ts new file mode 100644 index 0000000..6eab0d0 --- /dev/null +++ b/hooks/useSettingsChange.ts @@ -0,0 +1,25 @@ +import { useCallback, useEffect } from 'react' +import { settingsChangeDetector } from '../utils/settings/changeDetector.js' +import type { SettingSource } from '../utils/settings/constants.js' +import { getSettings_DEPRECATED } from '../utils/settings/settings.js' +import type { SettingsJson } from '../utils/settings/types.js' + +export function useSettingsChange( + onChange: (source: SettingSource, settings: SettingsJson) => void, +): void { + const handleChange = useCallback( + (source: SettingSource) => { + // Cache is already reset by the notifier (changeDetector.fanOut) — + // resetting here caused N-way thrashing with N subscribers: each + // cleared the cache, re-read from disk, then the next cleared again. + const newSettings = getSettings_DEPRECATED() + onChange(source, newSettings) + }, + [onChange], + ) + + useEffect( + () => settingsChangeDetector.subscribe(handleChange), + [handleChange], + ) +} diff --git a/hooks/useSkillImprovementSurvey.ts b/hooks/useSkillImprovementSurvey.ts new file mode 100644 index 0000000..29f2725 --- /dev/null +++ b/hooks/useSkillImprovementSurvey.ts @@ -0,0 +1,105 @@ +import { useCallback, useRef, useState } from 'react' +import type { FeedbackSurveyResponse } from '../components/FeedbackSurvey/utils.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + logEvent, +} from '../services/analytics/index.js' +import { useAppState, useSetAppState } from '../state/AppState.js' +import type { Message } from '../types/message.js' +import type { SkillUpdate } from '../utils/hooks/skillImprovement.js' +import { applySkillImprovement } from '../utils/hooks/skillImprovement.js' +import { createSystemMessage } from '../utils/messages.js' + +type SkillImprovementSuggestion = { + skillName: string + updates: SkillUpdate[] +} + +type SetMessages = (fn: (prev: Message[]) => Message[]) => void + +export function useSkillImprovementSurvey(setMessages: SetMessages): { + isOpen: boolean + suggestion: SkillImprovementSuggestion | null + handleSelect: (selected: FeedbackSurveyResponse) => void +} { + const suggestion = useAppState(s => s.skillImprovement.suggestion) + const setAppState = useSetAppState() + const [isOpen, setIsOpen] = useState(false) + const lastSuggestionRef = useRef(suggestion) + const loggedAppearanceRef = useRef(false) + + // Track the suggestion for display even after clearing AppState + if (suggestion) { + lastSuggestionRef.current = suggestion + } + + // Open when a new suggestion arrives + if (suggestion && !isOpen) { + setIsOpen(true) + if (!loggedAppearanceRef.current) { + loggedAppearanceRef.current = true + logEvent('tengu_skill_improvement_survey', { + event_type: + 'appeared' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + // _PROTO_skill_name routes to the privileged skill_name BQ column. + // Unredacted names don't go in additional_metadata. + _PROTO_skill_name: (suggestion.skillName ?? + 'unknown') as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + }) + } + } + + const handleSelect = useCallback( + (selected: FeedbackSurveyResponse) => { + const current = lastSuggestionRef.current + if (!current) return + + const applied = selected !== 'dismissed' + + logEvent('tengu_skill_improvement_survey', { + event_type: + 'responded' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + response: (applied + ? 'applied' + : 'dismissed') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + // _PROTO_skill_name routes to the privileged skill_name BQ column. + // Unredacted names don't go in additional_metadata. + _PROTO_skill_name: + current.skillName as AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED, + }) + + if (applied) { + void applySkillImprovement(current.skillName, current.updates).then( + () => { + setMessages(prev => [ + ...prev, + createSystemMessage( + `Skill "${current.skillName}" updated with improvements.`, + 'suggestion', + ), + ]) + }, + ) + } + + // Close and clear + setIsOpen(false) + loggedAppearanceRef.current = false + setAppState(prev => { + if (!prev.skillImprovement.suggestion) return prev + return { + ...prev, + skillImprovement: { suggestion: null }, + } + }) + }, + [setAppState, setMessages], + ) + + return { + isOpen, + suggestion: lastSuggestionRef.current, + handleSelect, + } +} diff --git a/hooks/useSkillsChange.ts b/hooks/useSkillsChange.ts new file mode 100644 index 0000000..198675d --- /dev/null +++ b/hooks/useSkillsChange.ts @@ -0,0 +1,62 @@ +import { useCallback, useEffect } from 'react' +import type { Command } from '../commands.js' +import { + clearCommandMemoizationCaches, + clearCommandsCache, + getCommands, +} from '../commands.js' +import { onGrowthBookRefresh } from '../services/analytics/growthbook.js' +import { logError } from '../utils/log.js' +import { skillChangeDetector } from '../utils/skills/skillChangeDetector.js' + +/** + * Keep the commands list fresh across two triggers: + * + * 1. Skill file changes (watcher) — full cache clear + disk re-scan, since + * skill content changed on disk. + * 2. GrowthBook init/refresh — memo-only clear, since only `isEnabled()` + * predicates may have changed. Handles commands like /btw whose gate + * reads a flag that isn't in the disk cache yet on first session after + * a flag rename: getCommands() runs before GB init (main.tsx:2855 vs + * showSetupScreens at :3106), so the memoized list is baked with the + * default. Once init populates remoteEvalFeatureValues, re-filter. + */ +export function useSkillsChange( + cwd: string | undefined, + onCommandsChange: (commands: Command[]) => void, +): void { + const handleChange = useCallback(async () => { + if (!cwd) return + try { + // Clear all command caches to ensure fresh load + clearCommandsCache() + const commands = await getCommands(cwd) + onCommandsChange(commands) + } catch (error) { + // Errors during reload are non-fatal - log and continue + if (error instanceof Error) { + logError(error) + } + } + }, [cwd, onCommandsChange]) + + useEffect(() => skillChangeDetector.subscribe(handleChange), [handleChange]) + + const handleGrowthBookRefresh = useCallback(async () => { + if (!cwd) return + try { + clearCommandMemoizationCaches() + const commands = await getCommands(cwd) + onCommandsChange(commands) + } catch (error) { + if (error instanceof Error) { + logError(error) + } + } + }, [cwd, onCommandsChange]) + + useEffect( + () => onGrowthBookRefresh(handleGrowthBookRefresh), + [handleGrowthBookRefresh], + ) +} diff --git a/hooks/useSwarmInitialization.ts b/hooks/useSwarmInitialization.ts new file mode 100644 index 0000000..9b9cd61 --- /dev/null +++ b/hooks/useSwarmInitialization.ts @@ -0,0 +1,81 @@ +/** + * Swarm Initialization Hook + * + * Initializes swarm features: teammate hooks and context. + * Handles both fresh spawns and resumed teammate sessions. + * + * This hook is conditionally loaded to allow dead code elimination when swarms are disabled. + */ + +import { useEffect } from 'react' +import { getSessionId } from '../bootstrap/state.js' +import type { AppState } from '../state/AppState.js' +import type { Message } from '../types/message.js' +import { isAgentSwarmsEnabled } from '../utils/agentSwarmsEnabled.js' +import { initializeTeammateContextFromSession } from '../utils/swarm/reconnection.js' +import { readTeamFile } from '../utils/swarm/teamHelpers.js' +import { initializeTeammateHooks } from '../utils/swarm/teammateInit.js' +import { getDynamicTeamContext } from '../utils/teammate.js' + +type SetAppState = (f: (prevState: AppState) => AppState) => void + +/** + * Hook that initializes swarm features when ENABLE_AGENT_SWARMS is true. + * + * Handles both: + * - Resumed teammate sessions (from --resume or /resume) where teamName/agentName + * are stored in transcript messages + * - Fresh spawns where context is read from environment variables + */ +export function useSwarmInitialization( + setAppState: SetAppState, + initialMessages: Message[] | undefined, + { enabled = true }: { enabled?: boolean } = {}, +): void { + useEffect(() => { + if (!enabled) return + if (isAgentSwarmsEnabled()) { + // Check if this is a resumed agent session (from --resume or /resume) + // Resumed sessions have teamName/agentName stored in transcript messages + const firstMessage = initialMessages?.[0] + const teamName = + firstMessage && 'teamName' in firstMessage + ? (firstMessage.teamName as string | undefined) + : undefined + const agentName = + firstMessage && 'agentName' in firstMessage + ? (firstMessage.agentName as string | undefined) + : undefined + + if (teamName && agentName) { + // Resumed agent session - set up team context from stored info + initializeTeammateContextFromSession(setAppState, teamName, agentName) + + // Get agentId from team file for hook initialization + const teamFile = readTeamFile(teamName) + const member = teamFile?.members.find( + (m: { name: string }) => m.name === agentName, + ) + if (member) { + initializeTeammateHooks(setAppState, getSessionId(), { + teamName, + agentId: member.agentId, + agentName, + }) + } + } else { + // Fresh spawn or standalone session + // teamContext is already computed in main.tsx via computeInitialTeamContext() + // and included in initialState, so we only need to initialize hooks here + const context = getDynamicTeamContext?.() + if (context?.teamName && context?.agentId && context?.agentName) { + initializeTeammateHooks(setAppState, getSessionId(), { + teamName: context.teamName, + agentId: context.agentId, + agentName: context.agentName, + }) + } + } + } + }, [setAppState, initialMessages, enabled]) +} diff --git a/hooks/useSwarmPermissionPoller.ts b/hooks/useSwarmPermissionPoller.ts new file mode 100644 index 0000000..0223cef --- /dev/null +++ b/hooks/useSwarmPermissionPoller.ts @@ -0,0 +1,330 @@ +/** + * Swarm Permission Poller Hook + * + * This hook polls for permission responses from the team leader when running + * as a worker agent in a swarm. When a response is received, it calls the + * appropriate callback (onAllow/onReject) to continue execution. + * + * This hook should be used in conjunction with the worker-side integration + * in useCanUseTool.ts, which creates pending requests that this hook monitors. + */ + +import { useCallback, useEffect, useRef } from 'react' +import { useInterval } from 'usehooks-ts' +import { logForDebugging } from '../utils/debug.js' +import { errorMessage } from '../utils/errors.js' +import { + type PermissionUpdate, + permissionUpdateSchema, +} from '../utils/permissions/PermissionUpdateSchema.js' +import { + isSwarmWorker, + type PermissionResponse, + pollForResponse, + removeWorkerResponse, +} from '../utils/swarm/permissionSync.js' +import { getAgentName, getTeamName } from '../utils/teammate.js' + +const POLL_INTERVAL_MS = 500 + +/** + * Validate permissionUpdates from external sources (mailbox IPC, disk polling). + * Malformed entries from buggy/old teammate processes are filtered out rather + * than propagated unchecked into callback.onAllow(). + */ +function parsePermissionUpdates(raw: unknown): PermissionUpdate[] { + if (!Array.isArray(raw)) { + return [] + } + const schema = permissionUpdateSchema() + const valid: PermissionUpdate[] = [] + for (const entry of raw) { + const result = schema.safeParse(entry) + if (result.success) { + valid.push(result.data) + } else { + logForDebugging( + `[SwarmPermissionPoller] Dropping malformed permissionUpdate entry: ${result.error.message}`, + { level: 'warn' }, + ) + } + } + return valid +} + +/** + * Callback signature for handling permission responses + */ +export type PermissionResponseCallback = { + requestId: string + toolUseId: string + onAllow: ( + updatedInput: Record | undefined, + permissionUpdates: PermissionUpdate[], + feedback?: string, + ) => void + onReject: (feedback?: string) => void +} + +/** + * Registry for pending permission request callbacks + * This allows the poller to find and invoke the right callbacks when responses arrive + */ +type PendingCallbackRegistry = Map + +// Module-level registry that persists across renders +const pendingCallbacks: PendingCallbackRegistry = new Map() + +/** + * Register a callback for a pending permission request + * Called by useCanUseTool when a worker submits a permission request + */ +export function registerPermissionCallback( + callback: PermissionResponseCallback, +): void { + pendingCallbacks.set(callback.requestId, callback) + logForDebugging( + `[SwarmPermissionPoller] Registered callback for request ${callback.requestId}`, + ) +} + +/** + * Unregister a callback (e.g., when the request is resolved locally or times out) + */ +export function unregisterPermissionCallback(requestId: string): void { + pendingCallbacks.delete(requestId) + logForDebugging( + `[SwarmPermissionPoller] Unregistered callback for request ${requestId}`, + ) +} + +/** + * Check if a request has a registered callback + */ +export function hasPermissionCallback(requestId: string): boolean { + return pendingCallbacks.has(requestId) +} + +/** + * Clear all pending callbacks (both permission and sandbox). + * Called from clearSessionCaches() on /clear to reset stale state, + * and also used in tests for isolation. + */ +export function clearAllPendingCallbacks(): void { + pendingCallbacks.clear() + pendingSandboxCallbacks.clear() +} + +/** + * Process a permission response from a mailbox message. + * This is called by the inbox poller when it detects a permission_response message. + * + * @returns true if the response was processed, false if no callback was registered + */ +export function processMailboxPermissionResponse(params: { + requestId: string + decision: 'approved' | 'rejected' + feedback?: string + updatedInput?: Record + permissionUpdates?: unknown +}): boolean { + const callback = pendingCallbacks.get(params.requestId) + + if (!callback) { + logForDebugging( + `[SwarmPermissionPoller] No callback registered for mailbox response ${params.requestId}`, + ) + return false + } + + logForDebugging( + `[SwarmPermissionPoller] Processing mailbox response for request ${params.requestId}: ${params.decision}`, + ) + + // Remove from registry before invoking callback + pendingCallbacks.delete(params.requestId) + + if (params.decision === 'approved') { + const permissionUpdates = parsePermissionUpdates(params.permissionUpdates) + const updatedInput = params.updatedInput + callback.onAllow(updatedInput, permissionUpdates) + } else { + callback.onReject(params.feedback) + } + + return true +} + +// ============================================================================ +// Sandbox Permission Callback Registry +// ============================================================================ + +/** + * Callback signature for handling sandbox permission responses + */ +export type SandboxPermissionResponseCallback = { + requestId: string + host: string + resolve: (allow: boolean) => void +} + +// Module-level registry for sandbox permission callbacks +const pendingSandboxCallbacks: Map = + new Map() + +/** + * Register a callback for a pending sandbox permission request + * Called when a worker sends a sandbox permission request to the leader + */ +export function registerSandboxPermissionCallback( + callback: SandboxPermissionResponseCallback, +): void { + pendingSandboxCallbacks.set(callback.requestId, callback) + logForDebugging( + `[SwarmPermissionPoller] Registered sandbox callback for request ${callback.requestId}`, + ) +} + +/** + * Check if a sandbox request has a registered callback + */ +export function hasSandboxPermissionCallback(requestId: string): boolean { + return pendingSandboxCallbacks.has(requestId) +} + +/** + * Process a sandbox permission response from a mailbox message. + * Called by the inbox poller when it detects a sandbox_permission_response message. + * + * @returns true if the response was processed, false if no callback was registered + */ +export function processSandboxPermissionResponse(params: { + requestId: string + host: string + allow: boolean +}): boolean { + const callback = pendingSandboxCallbacks.get(params.requestId) + + if (!callback) { + logForDebugging( + `[SwarmPermissionPoller] No sandbox callback registered for request ${params.requestId}`, + ) + return false + } + + logForDebugging( + `[SwarmPermissionPoller] Processing sandbox response for request ${params.requestId}: allow=${params.allow}`, + ) + + // Remove from registry before invoking callback + pendingSandboxCallbacks.delete(params.requestId) + + // Resolve the promise with the allow decision + callback.resolve(params.allow) + + return true +} + +/** + * Process a permission response by invoking the registered callback + */ +function processResponse(response: PermissionResponse): boolean { + const callback = pendingCallbacks.get(response.requestId) + + if (!callback) { + logForDebugging( + `[SwarmPermissionPoller] No callback registered for request ${response.requestId}`, + ) + return false + } + + logForDebugging( + `[SwarmPermissionPoller] Processing response for request ${response.requestId}: ${response.decision}`, + ) + + // Remove from registry before invoking callback + pendingCallbacks.delete(response.requestId) + + if (response.decision === 'approved') { + const permissionUpdates = parsePermissionUpdates(response.permissionUpdates) + const updatedInput = response.updatedInput + callback.onAllow(updatedInput, permissionUpdates) + } else { + callback.onReject(response.feedback) + } + + return true +} + +/** + * Hook that polls for permission responses when running as a swarm worker. + * + * This hook: + * 1. Only activates when isSwarmWorker() returns true + * 2. Polls every 500ms for responses + * 3. When a response is found, invokes the registered callback + * 4. Cleans up the response file after processing + */ +export function useSwarmPermissionPoller(): void { + const isProcessingRef = useRef(false) + + const poll = useCallback(async () => { + // Don't poll if not a swarm worker + if (!isSwarmWorker()) { + return + } + + // Prevent concurrent polling + if (isProcessingRef.current) { + return + } + + // Don't poll if no callbacks are registered + if (pendingCallbacks.size === 0) { + return + } + + isProcessingRef.current = true + + try { + const agentName = getAgentName() + const teamName = getTeamName() + + if (!agentName || !teamName) { + return + } + + // Check each pending request for a response + for (const [requestId, _callback] of pendingCallbacks) { + const response = await pollForResponse(requestId, agentName, teamName) + + if (response) { + // Process the response + const processed = processResponse(response) + + if (processed) { + // Clean up the response from the worker's inbox + await removeWorkerResponse(requestId, agentName, teamName) + } + } + } + } catch (error) { + logForDebugging( + `[SwarmPermissionPoller] Error during poll: ${errorMessage(error)}`, + ) + } finally { + isProcessingRef.current = false + } + }, []) + + // Only poll if we're a swarm worker + const shouldPoll = isSwarmWorker() + useInterval(() => void poll(), shouldPoll ? POLL_INTERVAL_MS : null) + + // Initial poll on mount + useEffect(() => { + if (isSwarmWorker()) { + void poll() + } + }, [poll]) +} diff --git a/hooks/useTaskListWatcher.ts b/hooks/useTaskListWatcher.ts new file mode 100644 index 0000000..1fa3b90 --- /dev/null +++ b/hooks/useTaskListWatcher.ts @@ -0,0 +1,221 @@ +import { type FSWatcher, watch } from 'fs' +import { useEffect, useRef } from 'react' +import { logForDebugging } from '../utils/debug.js' +import { + claimTask, + DEFAULT_TASKS_MODE_TASK_LIST_ID, + ensureTasksDir, + getTasksDir, + listTasks, + type Task, + updateTask, +} from '../utils/tasks.js' + +const DEBOUNCE_MS = 1000 + +type Props = { + /** When undefined, the hook does nothing. The task list id is also used as the agent ID. */ + taskListId?: string + isLoading: boolean + /** + * Called when a task is ready to be worked on. + * Returns true if submission succeeded, false if rejected. + */ + onSubmitTask: (prompt: string) => boolean +} + +/** + * Hook that watches a task list directory and automatically picks up + * open, unowned tasks to work on. + * + * This enables "tasks mode" where Claude watches for externally-created + * tasks and processes them one at a time. + */ +export function useTaskListWatcher({ + taskListId, + isLoading, + onSubmitTask, +}: Props): void { + const currentTaskRef = useRef(null) + const debounceTimerRef = useRef | null>(null) + + // Stabilize unstable props via refs so the watcher effect doesn't depend on + // them. isLoading flips every turn, and onSubmitTask's identity changes + // whenever onQuery's deps change. Without this, the watcher effect re-runs + // on every turn, calling watcher.close() + watch() each time — which is a + // trigger for Bun's PathWatcherManager deadlock (oven-sh/bun#27469). + const isLoadingRef = useRef(isLoading) + isLoadingRef.current = isLoading + const onSubmitTaskRef = useRef(onSubmitTask) + onSubmitTaskRef.current = onSubmitTask + + const enabled = taskListId !== undefined + const agentId = taskListId ?? DEFAULT_TASKS_MODE_TASK_LIST_ID + + // checkForTasks reads isLoading and onSubmitTask from refs — always + // up-to-date, no stale closure, and doesn't force a new function identity + // per render. Stored in a ref so the watcher effect can call it without + // depending on it. + const checkForTasksRef = useRef<() => Promise>(async () => {}) + checkForTasksRef.current = async () => { + if (!enabled) { + return + } + + // Don't need to submit new tasks if we are already working + if (isLoadingRef.current) { + return + } + + const tasks = await listTasks(taskListId) + + // If we have a current task, check if it's been resolved + if (currentTaskRef.current !== null) { + const currentTask = tasks.find(t => t.id === currentTaskRef.current) + if (!currentTask || currentTask.status === 'completed') { + logForDebugging( + `[TaskListWatcher] Task #${currentTaskRef.current} is marked complete, ready for next task`, + ) + currentTaskRef.current = null + } else { + // Still working on current task + return + } + } + + // Find an open task with no owner that isn't blocked + const availableTask = findAvailableTask(tasks) + + if (!availableTask) { + return + } + + logForDebugging( + `[TaskListWatcher] Found available task #${availableTask.id}: ${availableTask.subject}`, + ) + + // Claim the task using the task list's agent ID + const result = await claimTask(taskListId, availableTask.id, agentId) + + if (!result.success) { + logForDebugging( + `[TaskListWatcher] Failed to claim task #${availableTask.id}: ${result.reason}`, + ) + return + } + + currentTaskRef.current = availableTask.id + + // Format the task as a prompt + const prompt = formatTaskAsPrompt(availableTask) + + logForDebugging( + `[TaskListWatcher] Submitting task #${availableTask.id} as prompt`, + ) + + const submitted = onSubmitTaskRef.current(prompt) + if (!submitted) { + logForDebugging( + `[TaskListWatcher] Failed to submit task #${availableTask.id}, releasing claim`, + ) + // Release the claim + await updateTask(taskListId, availableTask.id, { owner: undefined }) + currentTaskRef.current = null + } + } + + // -- Watcher setup + + // Schedules a check after DEBOUNCE_MS, collapsing rapid fs events. + // Shared between the watcher callback and the idle-trigger effect below. + const scheduleCheckRef = useRef<() => void>(() => {}) + + useEffect(() => { + if (!enabled) return + + void ensureTasksDir(taskListId) + const tasksDir = getTasksDir(taskListId) + + let watcher: FSWatcher | null = null + + const debouncedCheck = (): void => { + if (debounceTimerRef.current) { + clearTimeout(debounceTimerRef.current) + } + debounceTimerRef.current = setTimeout( + ref => void ref.current(), + DEBOUNCE_MS, + checkForTasksRef, + ) + } + scheduleCheckRef.current = debouncedCheck + + try { + watcher = watch(tasksDir, debouncedCheck) + watcher.unref() + logForDebugging(`[TaskListWatcher] Watching for tasks in ${tasksDir}`) + } catch (error) { + // fs.watch throws synchronously on ENOENT — ensureTasksDir should have + // created the dir, but handle the race gracefully + logForDebugging(`[TaskListWatcher] Failed to watch ${tasksDir}: ${error}`) + } + + // Initial check + debouncedCheck() + + return () => { + // This cleanup only fires when taskListId changes or on unmount — + // never per-turn. That keeps watcher.close() out of the Bun + // PathWatcherManager deadlock window. + scheduleCheckRef.current = () => {} + if (watcher) { + watcher.close() + } + if (debounceTimerRef.current) { + clearTimeout(debounceTimerRef.current) + } + } + }, [enabled, taskListId]) + + // Previously, the watcher effect depended on checkForTasks (and transitively + // isLoading), so going idle triggered a re-setup whose initial debouncedCheck + // would pick up the next task. Preserve that behavior explicitly: when + // isLoading drops, schedule a check. + useEffect(() => { + if (!enabled) return + if (isLoading) return + scheduleCheckRef.current() + }, [enabled, isLoading]) +} + +/** + * Find an available task that can be worked on: + * - Status is 'pending' + * - No owner assigned + * - Not blocked by any unresolved tasks + */ +function findAvailableTask(tasks: Task[]): Task | undefined { + const unresolvedTaskIds = new Set( + tasks.filter(t => t.status !== 'completed').map(t => t.id), + ) + + return tasks.find(task => { + if (task.status !== 'pending') return false + if (task.owner) return false + // Check all blockers are completed + return task.blockedBy.every(id => !unresolvedTaskIds.has(id)) + }) +} + +/** + * Format a task as a prompt for Claude to work on. + */ +function formatTaskAsPrompt(task: Task): string { + let prompt = `Complete all open tasks. Start with task #${task.id}: \n\n ${task.subject}` + + if (task.description) { + prompt += `\n\n${task.description}` + } + + return prompt +} diff --git a/hooks/useTasksV2.ts b/hooks/useTasksV2.ts new file mode 100644 index 0000000..6b7630a --- /dev/null +++ b/hooks/useTasksV2.ts @@ -0,0 +1,250 @@ +import { type FSWatcher, watch } from 'fs' +import { useEffect, useSyncExternalStore } from 'react' +import { useAppState, useSetAppState } from '../state/AppState.js' +import { createSignal } from '../utils/signal.js' +import type { Task } from '../utils/tasks.js' +import { + getTaskListId, + getTasksDir, + isTodoV2Enabled, + listTasks, + onTasksUpdated, + resetTaskList, +} from '../utils/tasks.js' +import { isTeamLead } from '../utils/teammate.js' + +const HIDE_DELAY_MS = 5000 +const DEBOUNCE_MS = 50 +const FALLBACK_POLL_MS = 5000 // Fallback in case fs.watch misses events + +/** + * Singleton store for the TodoV2 task list. Owns the file watcher, timers, + * and cached task list. Multiple hook instances (REPL, Spinner, + * PromptInputFooterLeftSide) subscribe to one shared store instead of each + * setting up their own fs.watch on the same directory. The Spinner mounts/ + * unmounts every turn — per-hook watchers caused constant watch/unwatch churn. + * + * Implements the useSyncExternalStore contract: subscribe/getSnapshot. + */ +class TasksV2Store { + /** Stable array reference; replaced only on fetch. undefined until started. */ + #tasks: Task[] | undefined = undefined + /** + * Set when the hide timer has elapsed (all tasks completed for >5s), or + * when the task list is empty. Starts false so the first fetch runs the + * "all completed → schedule 5s hide" path (matches original behavior: + * resuming a session with completed tasks shows them briefly). + */ + #hidden = false + #watcher: FSWatcher | null = null + #watchedDir: string | null = null + #hideTimer: ReturnType | null = null + #debounceTimer: ReturnType | null = null + #pollTimer: ReturnType | null = null + #unsubscribeTasksUpdated: (() => void) | null = null + #changed = createSignal() + #subscriberCount = 0 + #started = false + + /** + * useSyncExternalStore snapshot. Returns the same Task[] reference between + * updates (required for Object.is stability). Returns undefined when hidden. + */ + getSnapshot = (): Task[] | undefined => { + return this.#hidden ? undefined : this.#tasks + } + + subscribe = (fn: () => void): (() => void) => { + // Lazy init on first subscriber. useSyncExternalStore calls this + // post-commit, so I/O here is safe (no render-phase side effects). + // REPL.tsx keeps a subscription alive for the whole session, so + // Spinner mount/unmount churn never drives the count to zero. + const unsubscribe = this.#changed.subscribe(fn) + this.#subscriberCount++ + if (!this.#started) { + this.#started = true + this.#unsubscribeTasksUpdated = onTasksUpdated(this.#debouncedFetch) + // Fire-and-forget: subscribe is called post-commit (not in render), + // and the store notifies subscribers when the fetch resolves. + void this.#fetch() + } + let unsubscribed = false + return () => { + if (unsubscribed) return + unsubscribed = true + unsubscribe() + this.#subscriberCount-- + if (this.#subscriberCount === 0) this.#stop() + } + } + + #notify(): void { + this.#changed.emit() + } + + /** + * Point the file watcher at the current tasks directory. Called on start + * and whenever #fetch detects the task list ID has changed (e.g. when + * TeamCreateTool sets leaderTeamName mid-session). + */ + #rewatch(dir: string): void { + // Retry even on same dir if the previous watch attempt failed (dir + // didn't exist yet). Once the watcher is established, same-dir is a no-op. + if (dir === this.#watchedDir && this.#watcher !== null) return + this.#watcher?.close() + this.#watcher = null + this.#watchedDir = dir + try { + this.#watcher = watch(dir, this.#debouncedFetch) + this.#watcher.unref() + } catch { + // Directory may not exist yet (ensureTasksDir is called by writers). + // Not critical — onTasksUpdated covers in-process updates and the + // poll timer covers cross-process updates. + } + } + + #debouncedFetch = (): void => { + if (this.#debounceTimer) clearTimeout(this.#debounceTimer) + this.#debounceTimer = setTimeout(() => void this.#fetch(), DEBOUNCE_MS) + this.#debounceTimer.unref() + } + + #fetch = async (): Promise => { + const taskListId = getTaskListId() + // Task list ID can change mid-session (TeamCreateTool sets + // leaderTeamName) — point the watcher at the current dir. + this.#rewatch(getTasksDir(taskListId)) + const current = (await listTasks(taskListId)).filter( + t => !t.metadata?._internal, + ) + this.#tasks = current + + const hasIncomplete = current.some(t => t.status !== 'completed') + + if (hasIncomplete || current.length === 0) { + // Has unresolved tasks (open/in_progress) or empty — reset hide state + this.#hidden = current.length === 0 + this.#clearHideTimer() + } else if (this.#hideTimer === null && !this.#hidden) { + // All tasks just became completed — schedule clear + this.#hideTimer = setTimeout( + this.#onHideTimerFired.bind(this, taskListId), + HIDE_DELAY_MS, + ) + this.#hideTimer.unref() + } + + this.#notify() + + // Schedule fallback poll only when there are incomplete tasks that + // need monitoring. When all tasks are completed (or there are none), + // the fs.watch watcher and onTasksUpdated callback are sufficient to + // detect new activity — no need to keep polling and re-rendering. + if (this.#pollTimer) { + clearTimeout(this.#pollTimer) + this.#pollTimer = null + } + if (hasIncomplete) { + this.#pollTimer = setTimeout(this.#debouncedFetch, FALLBACK_POLL_MS) + this.#pollTimer.unref() + } + } + + #onHideTimerFired(scheduledForTaskListId: string): void { + this.#hideTimer = null + // Bail if the task list ID changed since scheduling (team created/deleted + // during the 5s window) — don't reset the wrong list. + const currentId = getTaskListId() + if (currentId !== scheduledForTaskListId) return + // Verify all tasks are still completed before clearing + void listTasks(currentId).then(async tasksToCheck => { + const allStillCompleted = + tasksToCheck.length > 0 && + tasksToCheck.every(t => t.status === 'completed') + if (allStillCompleted) { + await resetTaskList(currentId) + this.#tasks = [] + this.#hidden = true + } + this.#notify() + }) + } + + #clearHideTimer(): void { + if (this.#hideTimer) { + clearTimeout(this.#hideTimer) + this.#hideTimer = null + } + } + + /** + * Tear down the watcher, timers, and in-process subscription. Called when + * the last subscriber unsubscribes. Preserves #tasks/#hidden cache so a + * subsequent re-subscribe renders the last known state immediately. + */ + #stop(): void { + this.#watcher?.close() + this.#watcher = null + this.#watchedDir = null + this.#unsubscribeTasksUpdated?.() + this.#unsubscribeTasksUpdated = null + this.#clearHideTimer() + if (this.#debounceTimer) clearTimeout(this.#debounceTimer) + if (this.#pollTimer) clearTimeout(this.#pollTimer) + this.#debounceTimer = null + this.#pollTimer = null + this.#started = false + } +} + +let _store: TasksV2Store | null = null +function getStore(): TasksV2Store { + return (_store ??= new TasksV2Store()) +} + +// Stable no-ops for the disabled path so useSyncExternalStore doesn't +// churn its subscription on every render. +const NOOP = (): void => {} +const NOOP_SUBSCRIBE = (): (() => void) => NOOP +const NOOP_SNAPSHOT = (): undefined => undefined + +/** + * Hook to get the current task list for the persistent UI display. + * Returns tasks when TodoV2 is enabled, otherwise returns undefined. + * All hook instances share a single file watcher via TasksV2Store. + * Hides the list after 5 seconds if there are no open tasks. + */ +export function useTasksV2(): Task[] | undefined { + const teamContext = useAppState(s => s.teamContext) + + const enabled = isTodoV2Enabled() && (!teamContext || isTeamLead(teamContext)) + + const store = enabled ? getStore() : null + + return useSyncExternalStore( + store ? store.subscribe : NOOP_SUBSCRIBE, + store ? store.getSnapshot : NOOP_SNAPSHOT, + ) +} + +/** + * Same as useTasksV2, plus collapses the expanded task view when the list + * becomes hidden. Call this from exactly one always-mounted component (REPL) + * so the collapse effect runs once instead of N× per consumer. + */ +export function useTasksV2WithCollapseEffect(): Task[] | undefined { + const tasks = useTasksV2() + const setAppState = useSetAppState() + + const hidden = tasks === undefined + useEffect(() => { + if (!hidden) return + setAppState(prev => { + if (prev.expandedView !== 'tasks') return prev + return { ...prev, expandedView: 'none' as const } + }) + }, [hidden, setAppState]) + + return tasks +} diff --git a/hooks/useTeammateViewAutoExit.ts b/hooks/useTeammateViewAutoExit.ts new file mode 100644 index 0000000..ff381ae --- /dev/null +++ b/hooks/useTeammateViewAutoExit.ts @@ -0,0 +1,63 @@ +import { useEffect } from 'react' +import { useAppState, useSetAppState } from '../state/AppState.js' +import { exitTeammateView } from '../state/teammateViewHelpers.js' +import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js' + +/** + * Auto-exits teammate viewing mode when the viewed teammate + * is killed or encounters an error. Users stay viewing completed + * teammates so they can review the full transcript. + */ +export function useTeammateViewAutoExit(): void { + const setAppState = useSetAppState() + const viewingAgentTaskId = useAppState(s => s.viewingAgentTaskId) + // Select only the viewed task, not the full tasks map — otherwise every + // streaming update from any teammate re-renders this hook. + const task = useAppState(s => + s.viewingAgentTaskId ? s.tasks[s.viewingAgentTaskId] : undefined, + ) + + const viewedTask = task && isInProcessTeammateTask(task) ? task : undefined + const viewedStatus = viewedTask?.status + const viewedError = viewedTask?.error + const taskExists = task !== undefined + + useEffect(() => { + // Not viewing any teammate + if (!viewingAgentTaskId) { + return + } + + // Task no longer exists in the map — evicted out from under us. + // Check raw `task` not teammate-narrowed `viewedTask`; local_agent + // tasks exist but narrow to undefined, which would eject immediately. + if (!taskExists) { + exitTeammateView(setAppState) + return + } + // Status checks below are teammate-only (viewedTask is teammate-narrowed). + // For local_agent, viewedStatus is undefined → all checks falsy → no eject. + if (!viewedTask) return + + // Auto-exit if teammate is killed, stopped, has error, or is no longer running + // This handles shutdown scenarios where teammate becomes inactive + if ( + viewedStatus === 'killed' || + viewedStatus === 'failed' || + viewedError || + (viewedStatus !== 'running' && + viewedStatus !== 'completed' && + viewedStatus !== 'pending') + ) { + exitTeammateView(setAppState) + return + } + }, [ + viewingAgentTaskId, + taskExists, + viewedTask, + viewedStatus, + viewedError, + setAppState, + ]) +} diff --git a/hooks/useTeleportResume.tsx b/hooks/useTeleportResume.tsx new file mode 100644 index 0000000..9b459aa --- /dev/null +++ b/hooks/useTeleportResume.tsx @@ -0,0 +1,85 @@ +import { c as _c } from "react/compiler-runtime"; +import { useCallback, useState } from 'react'; +import { setTeleportedSessionInfo } from 'src/bootstrap/state.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import type { TeleportRemoteResponse } from 'src/utils/conversationRecovery.js'; +import type { CodeSession } from 'src/utils/teleport/api.js'; +import { errorMessage, TeleportOperationError } from '../utils/errors.js'; +import { teleportResumeCodeSession } from '../utils/teleport.js'; +export type TeleportResumeError = { + message: string; + formattedMessage?: string; + isOperationError: boolean; +}; +export type TeleportSource = 'cliArg' | 'localCommand'; +export function useTeleportResume(source) { + const $ = _c(8); + const [isResuming, setIsResuming] = useState(false); + const [error, setError] = useState(null); + const [selectedSession, setSelectedSession] = useState(null); + let t0; + if ($[0] !== source) { + t0 = async session => { + setIsResuming(true); + setError(null); + setSelectedSession(session); + logEvent("tengu_teleport_resume_session", { + source: source as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + session_id: session.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + ; + try { + const result = await teleportResumeCodeSession(session.id); + setTeleportedSessionInfo({ + sessionId: session.id + }); + setIsResuming(false); + return result; + } catch (t1) { + const err = t1; + const teleportError = { + message: err instanceof TeleportOperationError ? err.message : errorMessage(err), + formattedMessage: err instanceof TeleportOperationError ? err.formattedMessage : undefined, + isOperationError: err instanceof TeleportOperationError + }; + setError(teleportError); + setIsResuming(false); + return null; + } + }; + $[0] = source; + $[1] = t0; + } else { + t0 = $[1]; + } + const resumeSession = t0; + let t1; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + setError(null); + }; + $[2] = t1; + } else { + t1 = $[2]; + } + const clearError = t1; + let t2; + if ($[3] !== error || $[4] !== isResuming || $[5] !== resumeSession || $[6] !== selectedSession) { + t2 = { + resumeSession, + isResuming, + error, + selectedSession, + clearError + }; + $[3] = error; + $[4] = isResuming; + $[5] = resumeSession; + $[6] = selectedSession; + $[7] = t2; + } else { + t2 = $[7]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1c2VDYWxsYmFjayIsInVzZVN0YXRlIiwic2V0VGVsZXBvcnRlZFNlc3Npb25JbmZvIiwiQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyIsImxvZ0V2ZW50IiwiVGVsZXBvcnRSZW1vdGVSZXNwb25zZSIsIkNvZGVTZXNzaW9uIiwiZXJyb3JNZXNzYWdlIiwiVGVsZXBvcnRPcGVyYXRpb25FcnJvciIsInRlbGVwb3J0UmVzdW1lQ29kZVNlc3Npb24iLCJUZWxlcG9ydFJlc3VtZUVycm9yIiwibWVzc2FnZSIsImZvcm1hdHRlZE1lc3NhZ2UiLCJpc09wZXJhdGlvbkVycm9yIiwiVGVsZXBvcnRTb3VyY2UiLCJ1c2VUZWxlcG9ydFJlc3VtZSIsInNvdXJjZSIsIiQiLCJfYyIsImlzUmVzdW1pbmciLCJzZXRJc1Jlc3VtaW5nIiwiZXJyb3IiLCJzZXRFcnJvciIsInNlbGVjdGVkU2Vzc2lvbiIsInNldFNlbGVjdGVkU2Vzc2lvbiIsInQwIiwic2Vzc2lvbiIsInNlc3Npb25faWQiLCJpZCIsInJlc3VsdCIsInNlc3Npb25JZCIsInQxIiwiZXJyIiwidGVsZXBvcnRFcnJvciIsInVuZGVmaW5lZCIsInJlc3VtZVNlc3Npb24iLCJTeW1ib2wiLCJmb3IiLCJjbGVhckVycm9yIiwidDIiXSwic291cmNlcyI6WyJ1c2VUZWxlcG9ydFJlc3VtZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBzZXRUZWxlcG9ydGVkU2Vzc2lvbkluZm8gfSBmcm9tICdzcmMvYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB0eXBlIHsgVGVsZXBvcnRSZW1vdGVSZXNwb25zZSB9IGZyb20gJ3NyYy91dGlscy9jb252ZXJzYXRpb25SZWNvdmVyeS5qcydcbmltcG9ydCB0eXBlIHsgQ29kZVNlc3Npb24gfSBmcm9tICdzcmMvdXRpbHMvdGVsZXBvcnQvYXBpLmpzJ1xuaW1wb3J0IHsgZXJyb3JNZXNzYWdlLCBUZWxlcG9ydE9wZXJhdGlvbkVycm9yIH0gZnJvbSAnLi4vdXRpbHMvZXJyb3JzLmpzJ1xuaW1wb3J0IHsgdGVsZXBvcnRSZXN1bWVDb2RlU2Vzc2lvbiB9IGZyb20gJy4uL3V0aWxzL3RlbGVwb3J0LmpzJ1xuXG5leHBvcnQgdHlwZSBUZWxlcG9ydFJlc3VtZUVycm9yID0ge1xuICBtZXNzYWdlOiBzdHJpbmdcbiAgZm9ybWF0dGVkTWVzc2FnZT86IHN0cmluZ1xuICBpc09wZXJhdGlvbkVycm9yOiBib29sZWFuXG59XG5cbmV4cG9ydCB0eXBlIFRlbGVwb3J0U291cmNlID0gJ2NsaUFyZycgfCAnbG9jYWxDb21tYW5kJ1xuXG5leHBvcnQgZnVuY3Rpb24gdXNlVGVsZXBvcnRSZXN1bWUoc291cmNlOiBUZWxlcG9ydFNvdXJjZSkge1xuICBjb25zdCBbaXNSZXN1bWluZywgc2V0SXNSZXN1bWluZ10gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW2Vycm9yLCBzZXRFcnJvcl0gPSB1c2VTdGF0ZTxUZWxlcG9ydFJlc3VtZUVycm9yIHwgbnVsbD4obnVsbClcbiAgY29uc3QgW3NlbGVjdGVkU2Vzc2lvbiwgc2V0U2VsZWN0ZWRTZXNzaW9uXSA9IHVzZVN0YXRlPENvZGVTZXNzaW9uIHwgbnVsbD4oXG4gICAgbnVsbCxcbiAgKVxuXG4gIGNvbnN0IHJlc3VtZVNlc3Npb24gPSB1c2VDYWxsYmFjayhcbiAgICBhc3luYyAoc2Vzc2lvbjogQ29kZVNlc3Npb24pOiBQcm9taXNlPFRlbGVwb3J0UmVtb3RlUmVzcG9uc2UgfCBudWxsPiA9PiB7XG4gICAgICBzZXRJc1Jlc3VtaW5nKHRydWUpXG4gICAgICBzZXRFcnJvcihudWxsKVxuICAgICAgc2V0U2VsZWN0ZWRTZXNzaW9uKHNlc3Npb24pXG5cbiAgICAgIC8vIExvZyB0ZWxlcG9ydCBzZXNzaW9uIHNlbGVjdGlvblxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3RlbGVwb3J0X3Jlc3VtZV9zZXNzaW9uJywge1xuICAgICAgICBzb3VyY2U6XG4gICAgICAgICAgc291cmNlIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHNlc3Npb25faWQ6XG4gICAgICAgICAgc2Vzc2lvbi5pZCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcblxuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGVsZXBvcnRSZXN1bWVDb2RlU2Vzc2lvbihzZXNzaW9uLmlkKVxuICAgICAgICAvLyBUcmFjayB0ZWxlcG9ydGVkIHNlc3Npb24gZm9yIHJlbGlhYmlsaXR5IGxvZ2dpbmdcbiAgICAgICAgc2V0VGVsZXBvcnRlZFNlc3Npb25JbmZvKHsgc2Vzc2lvbklkOiBzZXNzaW9uLmlkIH0pXG4gICAgICAgIHNldElzUmVzdW1pbmcoZmFsc2UpXG4gICAgICAgIHJldHVybiByZXN1bHRcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBjb25zdCB0ZWxlcG9ydEVycm9yOiBUZWxlcG9ydFJlc3VtZUVycm9yID0ge1xuICAgICAgICAgIG1lc3NhZ2U6XG4gICAgICAgICAgICBlcnIgaW5zdGFuY2VvZiBUZWxlcG9ydE9wZXJhdGlvbkVycm9yXG4gICAgICAgICAgICAgID8gZXJyLm1lc3NhZ2VcbiAgICAgICAgICAgICAgOiBlcnJvck1lc3NhZ2UoZXJyKSxcbiAgICAgICAgICBmb3JtYXR0ZWRNZXNzYWdlOlxuICAgICAgICAgICAgZXJyIGluc3RhbmNlb2YgVGVsZXBvcnRPcGVyYXRpb25FcnJvclxuICAgICAgICAgICAgICA/IGVyci5mb3JtYXR0ZWRNZXNzYWdlXG4gICAgICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgICAgICAgIGlzT3BlcmF0aW9uRXJyb3I6IGVyciBpbnN0YW5jZW9mIFRlbGVwb3J0T3BlcmF0aW9uRXJyb3IsXG4gICAgICAgIH1cbiAgICAgICAgc2V0RXJyb3IodGVsZXBvcnRFcnJvcilcbiAgICAgICAgc2V0SXNSZXN1bWluZyhmYWxzZSlcbiAgICAgICAgcmV0dXJuIG51bGxcbiAgICAgIH1cbiAgICB9LFxuICAgIFtzb3VyY2VdLFxuICApXG5cbiAgY29uc3QgY2xlYXJFcnJvciA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRFcnJvcihudWxsKVxuICB9LCBbXSlcblxuICByZXR1cm4ge1xuICAgIHJlc3VtZVNlc3Npb24sXG4gICAgaXNSZXN1bWluZyxcbiAgICBlcnJvcixcbiAgICBzZWxlY3RlZFNlc3Npb24sXG4gICAgY2xlYXJFcnJvcixcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsU0FBU0EsV0FBVyxFQUFFQyxRQUFRLFFBQVEsT0FBTztBQUM3QyxTQUFTQyx3QkFBd0IsUUFBUSx3QkFBd0I7QUFDakUsU0FDRSxLQUFLQywwREFBMEQsRUFDL0RDLFFBQVEsUUFDSCxpQ0FBaUM7QUFDeEMsY0FBY0Msc0JBQXNCLFFBQVEsbUNBQW1DO0FBQy9FLGNBQWNDLFdBQVcsUUFBUSwyQkFBMkI7QUFDNUQsU0FBU0MsWUFBWSxFQUFFQyxzQkFBc0IsUUFBUSxvQkFBb0I7QUFDekUsU0FBU0MseUJBQXlCLFFBQVEsc0JBQXNCO0FBRWhFLE9BQU8sS0FBS0MsbUJBQW1CLEdBQUc7RUFDaENDLE9BQU8sRUFBRSxNQUFNO0VBQ2ZDLGdCQUFnQixDQUFDLEVBQUUsTUFBTTtFQUN6QkMsZ0JBQWdCLEVBQUUsT0FBTztBQUMzQixDQUFDO0FBRUQsT0FBTyxLQUFLQyxjQUFjLEdBQUcsUUFBUSxHQUFHLGNBQWM7QUFFdEQsT0FBTyxT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 \ No newline at end of file diff --git a/hooks/useTerminalSize.ts b/hooks/useTerminalSize.ts new file mode 100644 index 0000000..68e24df --- /dev/null +++ b/hooks/useTerminalSize.ts @@ -0,0 +1,15 @@ +import { useContext } from 'react' +import { + type TerminalSize, + TerminalSizeContext, +} from 'src/ink/components/TerminalSizeContext.js' + +export function useTerminalSize(): TerminalSize { + const size = useContext(TerminalSizeContext) + + if (!size) { + throw new Error('useTerminalSize must be used within an Ink App component') + } + + return size +} diff --git a/hooks/useTextInput.ts b/hooks/useTextInput.ts new file mode 100644 index 0000000..90c4c4f --- /dev/null +++ b/hooks/useTextInput.ts @@ -0,0 +1,529 @@ +import { isInputModeCharacter } from 'src/components/PromptInput/inputModes.js' +import { useNotifications } from 'src/context/notifications.js' +import stripAnsi from 'strip-ansi' +import { markBackslashReturnUsed } from '../commands/terminalSetup/terminalSetup.js' +import { addToHistory } from '../history.js' +import type { Key } from '../ink.js' +import type { + InlineGhostText, + TextInputState, +} from '../types/textInputTypes.js' +import { + Cursor, + getLastKill, + pushToKillRing, + recordYank, + resetKillAccumulation, + resetYankState, + updateYankLength, + yankPop, +} from '../utils/Cursor.js' +import { env } from '../utils/env.js' +import { isFullscreenEnvEnabled } from '../utils/fullscreen.js' +import type { ImageDimensions } from '../utils/imageResizer.js' +import { isModifierPressed, prewarmModifiers } from '../utils/modifiers.js' +import { useDoublePress } from './useDoublePress.js' + +type MaybeCursor = void | Cursor +type InputHandler = (input: string) => MaybeCursor +type InputMapper = (input: string) => MaybeCursor +const NOOP_HANDLER: InputHandler = () => {} +function mapInput(input_map: Array<[string, InputHandler]>): InputMapper { + const map = new Map(input_map) + return function (input: string): MaybeCursor { + return (map.get(input) ?? NOOP_HANDLER)(input) + } +} + +export type UseTextInputProps = { + value: string + onChange: (value: string) => void + onSubmit?: (value: string) => void + onExit?: () => void + onExitMessage?: (show: boolean, key?: string) => void + onHistoryUp?: () => void + onHistoryDown?: () => void + onHistoryReset?: () => void + onClearInput?: () => void + focus?: boolean + mask?: string + multiline?: boolean + cursorChar: string + highlightPastedText?: boolean + invert: (text: string) => string + themeText: (text: string) => string + columns: number + onImagePaste?: ( + base64Image: string, + mediaType?: string, + filename?: string, + dimensions?: ImageDimensions, + sourcePath?: string, + ) => void + disableCursorMovementForUpDownKeys?: boolean + disableEscapeDoublePress?: boolean + maxVisibleLines?: number + externalOffset: number + onOffsetChange: (offset: number) => void + inputFilter?: (input: string, key: Key) => string + inlineGhostText?: InlineGhostText + dim?: (text: string) => string +} + +export function useTextInput({ + value: originalValue, + onChange, + onSubmit, + onExit, + onExitMessage, + onHistoryUp, + onHistoryDown, + onHistoryReset, + onClearInput, + mask = '', + multiline = false, + cursorChar, + invert, + columns, + onImagePaste: _onImagePaste, + disableCursorMovementForUpDownKeys = false, + disableEscapeDoublePress = false, + maxVisibleLines, + externalOffset, + onOffsetChange, + inputFilter, + inlineGhostText, + dim, +}: UseTextInputProps): TextInputState { + // Pre-warm the modifiers module for Apple Terminal (has internal guard, safe to call multiple times) + if (env.terminal === 'Apple_Terminal') { + prewarmModifiers() + } + + const offset = externalOffset + const setOffset = onOffsetChange + const cursor = Cursor.fromText(originalValue, columns, offset) + const { addNotification, removeNotification } = useNotifications() + + const handleCtrlC = useDoublePress( + show => { + onExitMessage?.(show, 'Ctrl-C') + }, + () => onExit?.(), + () => { + if (originalValue) { + onChange('') + setOffset(0) + onHistoryReset?.() + } + }, + ) + + // NOTE(keybindings): This escape handler is intentionally NOT migrated to the keybindings system. + // It's a text-level double-press escape for clearing input, not an action-level keybinding. + // Double-press Esc clears the input and saves to history - this is text editing behavior, + // not dialog dismissal, and needs the double-press safety mechanism. + const handleEscape = useDoublePress( + (show: boolean) => { + if (!originalValue || !show) { + return + } + addNotification({ + key: 'escape-again-to-clear', + text: 'Esc again to clear', + priority: 'immediate', + timeoutMs: 1000, + }) + }, + () => { + // Remove the "Esc again to clear" notification immediately + removeNotification('escape-again-to-clear') + onClearInput?.() + if (originalValue) { + // Track double-escape usage for feature discovery + // Save to history before clearing + if (originalValue.trim() !== '') { + addToHistory(originalValue) + } + onChange('') + setOffset(0) + onHistoryReset?.() + } + }, + ) + + const handleEmptyCtrlD = useDoublePress( + show => { + if (originalValue !== '') { + return + } + onExitMessage?.(show, 'Ctrl-D') + }, + () => { + if (originalValue !== '') { + return + } + onExit?.() + }, + ) + + function handleCtrlD(): MaybeCursor { + if (cursor.text === '') { + // When input is empty, handle double-press + handleEmptyCtrlD() + return cursor + } + // When input is not empty, delete forward like iPython + return cursor.del() + } + + function killToLineEnd(): Cursor { + const { cursor: newCursor, killed } = cursor.deleteToLineEnd() + pushToKillRing(killed, 'append') + return newCursor + } + + function killToLineStart(): Cursor { + const { cursor: newCursor, killed } = cursor.deleteToLineStart() + pushToKillRing(killed, 'prepend') + return newCursor + } + + function killWordBefore(): Cursor { + const { cursor: newCursor, killed } = cursor.deleteWordBefore() + pushToKillRing(killed, 'prepend') + return newCursor + } + + function yank(): Cursor { + const text = getLastKill() + if (text.length > 0) { + const startOffset = cursor.offset + const newCursor = cursor.insert(text) + recordYank(startOffset, text.length) + return newCursor + } + return cursor + } + + function handleYankPop(): Cursor { + const popResult = yankPop() + if (!popResult) { + return cursor + } + const { text, start, length } = popResult + // Replace the previously yanked text with the new one + const before = cursor.text.slice(0, start) + const after = cursor.text.slice(start + length) + const newText = before + text + after + const newOffset = start + text.length + updateYankLength(text.length) + return Cursor.fromText(newText, columns, newOffset) + } + + const handleCtrl = mapInput([ + ['a', () => cursor.startOfLine()], + ['b', () => cursor.left()], + ['c', handleCtrlC], + ['d', handleCtrlD], + ['e', () => cursor.endOfLine()], + ['f', () => cursor.right()], + ['h', () => cursor.deleteTokenBefore() ?? cursor.backspace()], + ['k', killToLineEnd], + ['n', () => downOrHistoryDown()], + ['p', () => upOrHistoryUp()], + ['u', killToLineStart], + ['w', killWordBefore], + ['y', yank], + ]) + + const handleMeta = mapInput([ + ['b', () => cursor.prevWord()], + ['f', () => cursor.nextWord()], + ['d', () => cursor.deleteWordAfter()], + ['y', handleYankPop], + ]) + + function handleEnter(key: Key) { + if ( + multiline && + cursor.offset > 0 && + cursor.text[cursor.offset - 1] === '\\' + ) { + // Track that the user has used backslash+return + markBackslashReturnUsed() + return cursor.backspace().insert('\n') + } + // Meta+Enter or Shift+Enter inserts a newline + if (key.meta || key.shift) { + return cursor.insert('\n') + } + // Apple Terminal doesn't support custom Shift+Enter keybindings, + // so we use native macOS modifier detection to check if Shift is held + if (env.terminal === 'Apple_Terminal' && isModifierPressed('shift')) { + return cursor.insert('\n') + } + onSubmit?.(originalValue) + } + + function upOrHistoryUp() { + if (disableCursorMovementForUpDownKeys) { + onHistoryUp?.() + return cursor + } + // Try to move by wrapped lines first + const cursorUp = cursor.up() + if (!cursorUp.equals(cursor)) { + return cursorUp + } + + // If we can't move by wrapped lines and this is multiline input, + // try to move by logical lines (to handle paragraph boundaries) + if (multiline) { + const cursorUpLogical = cursor.upLogicalLine() + if (!cursorUpLogical.equals(cursor)) { + return cursorUpLogical + } + } + + // Can't move up at all - trigger history navigation + onHistoryUp?.() + return cursor + } + function downOrHistoryDown() { + if (disableCursorMovementForUpDownKeys) { + onHistoryDown?.() + return cursor + } + // Try to move by wrapped lines first + const cursorDown = cursor.down() + if (!cursorDown.equals(cursor)) { + return cursorDown + } + + // If we can't move by wrapped lines and this is multiline input, + // try to move by logical lines (to handle paragraph boundaries) + if (multiline) { + const cursorDownLogical = cursor.downLogicalLine() + if (!cursorDownLogical.equals(cursor)) { + return cursorDownLogical + } + } + + // Can't move down at all - trigger history navigation + onHistoryDown?.() + return cursor + } + + function mapKey(key: Key): InputMapper { + switch (true) { + case key.escape: + return () => { + // Skip when a keybinding context (e.g. Autocomplete) owns escape. + // useKeybindings can't shield us via stopImmediatePropagation — + // BaseTextInput's useInput registers first (child effects fire + // before parent effects), so this handler has already run by the + // time the keybinding's handler stops propagation. + if (disableEscapeDoublePress) return cursor + handleEscape() + // Return the current cursor unchanged - handleEscape manages state internally + return cursor + } + case key.leftArrow && (key.ctrl || key.meta || key.fn): + return () => cursor.prevWord() + case key.rightArrow && (key.ctrl || key.meta || key.fn): + return () => cursor.nextWord() + case key.backspace: + return key.meta || key.ctrl + ? killWordBefore + : () => cursor.deleteTokenBefore() ?? cursor.backspace() + case key.delete: + return key.meta ? killToLineEnd : () => cursor.del() + case key.ctrl: + return handleCtrl + case key.home: + return () => cursor.startOfLine() + case key.end: + return () => cursor.endOfLine() + case key.pageDown: + // In fullscreen mode, PgUp/PgDn scroll the message viewport instead + // of moving the cursor — no-op here, ScrollKeybindingHandler handles it. + if (isFullscreenEnvEnabled()) { + return NOOP_HANDLER + } + return () => cursor.endOfLine() + case key.pageUp: + if (isFullscreenEnvEnabled()) { + return NOOP_HANDLER + } + return () => cursor.startOfLine() + case key.wheelUp: + case key.wheelDown: + // Mouse wheel events only exist when fullscreen mouse tracking is on. + // ScrollKeybindingHandler handles them; no-op here to avoid inserting + // the raw SGR sequence as text. + return NOOP_HANDLER + case key.return: + // Must come before key.meta so Option+Return inserts newline + return () => handleEnter(key) + case key.meta: + return handleMeta + case key.tab: + return () => cursor + case key.upArrow && !key.shift: + return upOrHistoryUp + case key.downArrow && !key.shift: + return downOrHistoryDown + case key.leftArrow: + return () => cursor.left() + case key.rightArrow: + return () => cursor.right() + default: { + return function (input: string) { + switch (true) { + // Home key + case input === '\x1b[H' || input === '\x1b[1~': + return cursor.startOfLine() + // End key + case input === '\x1b[F' || input === '\x1b[4~': + return cursor.endOfLine() + default: { + // Trailing \r after text is SSH-coalesced Enter ("o\r") — + // strip it so the Enter isn't inserted as content. Lone \r + // here is Alt+Enter leaking through (META_KEY_CODE_RE doesn't + // match \x1b\r) — leave it for the \r→\n below. Embedded \r + // is multi-line paste from a terminal without bracketed + // paste — convert to \n. Backslash+\r is a stale VS Code + // Shift+Enter binding (pre-#8991 /terminal-setup wrote + // args.text "\\\r\n" to keybindings.json); keep the \r so + // it becomes \n below (anthropics/claude-code#31316). + const text = stripAnsi(input) + // eslint-disable-next-line custom-rules/no-lookbehind-regex -- .replace(re, str) on 1-2 char keystrokes: no-match returns same string (Object.is), regex never runs + .replace(/(?<=[^\\\r\n])\r$/, '') + .replace(/\r/g, '\n') + if (cursor.isAtStart() && isInputModeCharacter(input)) { + return cursor.insert(text).left() + } + return cursor.insert(text) + } + } + } + } + } + } + + // Check if this is a kill command (Ctrl+K, Ctrl+U, Ctrl+W, or Meta+Backspace/Delete) + function isKillKey(key: Key, input: string): boolean { + if (key.ctrl && (input === 'k' || input === 'u' || input === 'w')) { + return true + } + if (key.meta && (key.backspace || key.delete)) { + return true + } + return false + } + + // Check if this is a yank command (Ctrl+Y or Alt+Y) + function isYankKey(key: Key, input: string): boolean { + return (key.ctrl || key.meta) && input === 'y' + } + + function onInput(input: string, key: Key): void { + // Note: Image paste shortcut (chat:imagePaste) is handled via useKeybindings in PromptInput + + // Apply filter if provided + const filteredInput = inputFilter ? inputFilter(input, key) : input + + // If the input was filtered out, do nothing + if (filteredInput === '' && input !== '') { + return + } + + // Fix Issue #1853: Filter DEL characters that interfere with backspace in SSH/tmux + // In SSH/tmux environments, backspace generates both key events and raw DEL chars + if (!key.backspace && !key.delete && input.includes('\x7f')) { + const delCount = (input.match(/\x7f/g) || []).length + + // Apply all DEL characters as backspace operations synchronously + // Try to delete tokens first, fall back to character backspace + let currentCursor = cursor + for (let i = 0; i < delCount; i++) { + currentCursor = + currentCursor.deleteTokenBefore() ?? currentCursor.backspace() + } + + // Update state once with the final result + if (!cursor.equals(currentCursor)) { + if (cursor.text !== currentCursor.text) { + onChange(currentCursor.text) + } + setOffset(currentCursor.offset) + } + resetKillAccumulation() + resetYankState() + return + } + + // Reset kill accumulation for non-kill keys + if (!isKillKey(key, filteredInput)) { + resetKillAccumulation() + } + + // Reset yank state for non-yank keys (breaks yank-pop chain) + if (!isYankKey(key, filteredInput)) { + resetYankState() + } + + const nextCursor = mapKey(key)(filteredInput) + if (nextCursor) { + if (!cursor.equals(nextCursor)) { + if (cursor.text !== nextCursor.text) { + onChange(nextCursor.text) + } + setOffset(nextCursor.offset) + } + // SSH-coalesced Enter: on slow links, "o" + Enter can arrive as one + // chunk "o\r". parseKeypress only matches s === '\r', so it hit the + // default handler above (which stripped the trailing \r). Text with + // exactly one trailing \r is coalesced Enter; lone \r is Alt+Enter + // (newline); embedded \r is multi-line paste. + if ( + filteredInput.length > 1 && + filteredInput.endsWith('\r') && + !filteredInput.slice(0, -1).includes('\r') && + // Backslash+CR is a stale VS Code Shift+Enter binding, not + // coalesced Enter. See default handler above. + filteredInput[filteredInput.length - 2] !== '\\' + ) { + onSubmit?.(nextCursor.text) + } + } + } + + // Prepare ghost text for rendering - validate insertPosition matches current + // cursor offset to prevent stale ghost text from a previous keystroke causing + // a one-frame jitter (ghost text state is updated via useEffect after render) + const ghostTextForRender = + inlineGhostText && dim && inlineGhostText.insertPosition === offset + ? { text: inlineGhostText.text, dim } + : undefined + + const cursorPos = cursor.getPosition() + + return { + onInput, + renderedValue: cursor.render( + cursorChar, + mask, + invert, + ghostTextForRender, + maxVisibleLines, + ), + offset, + setOffset, + cursorLine: cursorPos.line - cursor.getViewportStartLine(maxVisibleLines), + cursorColumn: cursorPos.column, + viewportCharOffset: cursor.getViewportCharOffset(maxVisibleLines), + viewportCharEnd: cursor.getViewportCharEnd(maxVisibleLines), + } +} diff --git a/hooks/useTimeout.ts b/hooks/useTimeout.ts new file mode 100644 index 0000000..faed236 --- /dev/null +++ b/hooks/useTimeout.ts @@ -0,0 +1,14 @@ +import { useEffect, useState } from 'react' + +export function useTimeout(delay: number, resetTrigger?: number): boolean { + const [isElapsed, setIsElapsed] = useState(false) + + useEffect(() => { + setIsElapsed(false) + const timer = setTimeout(setIsElapsed, delay, true) + + return () => clearTimeout(timer) + }, [delay, resetTrigger]) + + return isElapsed +} diff --git a/hooks/useTurnDiffs.ts b/hooks/useTurnDiffs.ts new file mode 100644 index 0000000..1fc2fa6 --- /dev/null +++ b/hooks/useTurnDiffs.ts @@ -0,0 +1,213 @@ +import type { StructuredPatchHunk } from 'diff' +import { useMemo, useRef } from 'react' +import type { FileEditOutput } from '../tools/FileEditTool/types.js' +import type { Output as FileWriteOutput } from '../tools/FileWriteTool/FileWriteTool.js' +import type { Message } from '../types/message.js' + +export type TurnFileDiff = { + filePath: string + hunks: StructuredPatchHunk[] + isNewFile: boolean + linesAdded: number + linesRemoved: number +} + +export type TurnDiff = { + turnIndex: number + userPromptPreview: string + timestamp: string + files: Map + stats: { + filesChanged: number + linesAdded: number + linesRemoved: number + } +} + +type FileEditResult = FileEditOutput | FileWriteOutput + +type TurnDiffCache = { + completedTurns: TurnDiff[] + currentTurn: TurnDiff | null + lastProcessedIndex: number + lastTurnIndex: number +} + +function isFileEditResult(result: unknown): result is FileEditResult { + if (!result || typeof result !== 'object') return false + const r = result as Record + // FileEditTool: has structuredPatch with content + // FileWriteTool (update): has structuredPatch with content + // FileWriteTool (create): has type='create' and content (structuredPatch is empty) + const hasFilePath = typeof r.filePath === 'string' + const hasStructuredPatch = + Array.isArray(r.structuredPatch) && r.structuredPatch.length > 0 + const isNewFile = r.type === 'create' && typeof r.content === 'string' + return hasFilePath && (hasStructuredPatch || isNewFile) +} + +function isFileWriteOutput(result: FileEditResult): result is FileWriteOutput { + return ( + 'type' in result && (result.type === 'create' || result.type === 'update') + ) +} + +function countHunkLines(hunks: StructuredPatchHunk[]): { + added: number + removed: number +} { + let added = 0 + let removed = 0 + for (const hunk of hunks) { + for (const line of hunk.lines) { + if (line.startsWith('+')) added++ + else if (line.startsWith('-')) removed++ + } + } + return { added, removed } +} + +function getUserPromptPreview(message: Message): string { + if (message.type !== 'user') return '' + const content = message.message.content + const text = typeof content === 'string' ? content : '' + // Truncate to ~30 chars + if (text.length <= 30) return text + return text.slice(0, 29) + '…' +} + +function computeTurnStats(turn: TurnDiff): void { + let totalAdded = 0 + let totalRemoved = 0 + for (const file of turn.files.values()) { + totalAdded += file.linesAdded + totalRemoved += file.linesRemoved + } + turn.stats = { + filesChanged: turn.files.size, + linesAdded: totalAdded, + linesRemoved: totalRemoved, + } +} + +/** + * Extract turn-based diffs from messages. + * A turn is defined as a user prompt followed by assistant responses and tool results. + * Each turn with file edits is included in the result. + * + * Uses incremental accumulation - only processes new messages since last render. + */ +export function useTurnDiffs(messages: Message[]): TurnDiff[] { + const cache = useRef({ + completedTurns: [], + currentTurn: null, + lastProcessedIndex: 0, + lastTurnIndex: 0, + }) + + return useMemo(() => { + const c = cache.current + + // Reset if messages shrunk (user rewound conversation) + if (messages.length < c.lastProcessedIndex) { + c.completedTurns = [] + c.currentTurn = null + c.lastProcessedIndex = 0 + c.lastTurnIndex = 0 + } + + // Process only new messages + for (let i = c.lastProcessedIndex; i < messages.length; i++) { + const message = messages[i] + if (!message || message.type !== 'user') continue + + // Check if this is a user prompt (not a tool result) + const isToolResult = + message.toolUseResult || + (Array.isArray(message.message.content) && + message.message.content[0]?.type === 'tool_result') + + if (!isToolResult && !message.isMeta) { + // Start a new turn on user prompt + if (c.currentTurn && c.currentTurn.files.size > 0) { + computeTurnStats(c.currentTurn) + c.completedTurns.push(c.currentTurn) + } + + c.lastTurnIndex++ + c.currentTurn = { + turnIndex: c.lastTurnIndex, + userPromptPreview: getUserPromptPreview(message), + timestamp: message.timestamp, + files: new Map(), + stats: { filesChanged: 0, linesAdded: 0, linesRemoved: 0 }, + } + } else if (c.currentTurn && message.toolUseResult) { + // Collect file edits from tool results + const result = message.toolUseResult + if (isFileEditResult(result)) { + const { filePath, structuredPatch } = result + const isNewFile = 'type' in result && result.type === 'create' + + // Get or create file entry + let fileEntry = c.currentTurn.files.get(filePath) + if (!fileEntry) { + fileEntry = { + filePath, + hunks: [], + isNewFile, + linesAdded: 0, + linesRemoved: 0, + } + c.currentTurn.files.set(filePath, fileEntry) + } + + // For new files, generate synthetic hunk from content + if ( + isNewFile && + structuredPatch.length === 0 && + isFileWriteOutput(result) + ) { + const content = result.content + const lines = content.split('\n') + const syntheticHunk: StructuredPatchHunk = { + oldStart: 0, + oldLines: 0, + newStart: 1, + newLines: lines.length, + lines: lines.map(l => '+' + l), + } + fileEntry.hunks.push(syntheticHunk) + fileEntry.linesAdded += lines.length + } else { + // Append hunks (same file may be edited multiple times in a turn) + fileEntry.hunks.push(...structuredPatch) + + // Update line counts + const { added, removed } = countHunkLines(structuredPatch) + fileEntry.linesAdded += added + fileEntry.linesRemoved += removed + } + + // If file was created and then edited, it's still a new file + if (isNewFile) { + fileEntry.isNewFile = true + } + } + } + } + + c.lastProcessedIndex = messages.length + + // Build result: completed turns + current turn if it has files + const result = [...c.completedTurns] + if (c.currentTurn && c.currentTurn.files.size > 0) { + // Compute stats for current turn before including + computeTurnStats(c.currentTurn) + result.push(c.currentTurn) + } + + // Return in reverse order (most recent first) + return result.reverse() + }, [messages]) +} diff --git a/hooks/useTypeahead.tsx b/hooks/useTypeahead.tsx new file mode 100644 index 0000000..a269902 --- /dev/null +++ b/hooks/useTypeahead.tsx @@ -0,0 +1,1385 @@ +import * as React from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useNotifications } from 'src/context/notifications.js'; +import { Text } from 'src/ink.js'; +import { logEvent } from 'src/services/analytics/index.js'; +import { useDebounceCallback } from 'usehooks-ts'; +import { type Command, getCommandName } from '../commands.js'; +import { getModeFromInput, getValueFromInput } from '../components/PromptInput/inputModes.js'; +import type { SuggestionItem, SuggestionType } from '../components/PromptInput/PromptInputFooterSuggestions.js'; +import { useIsModalOverlayActive, useRegisterOverlay } from '../context/overlayContext.js'; +import { KeyboardEvent } from '../ink/events/keyboard-event.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until consumers wire handleKeyDown to +import { useInput } from '../ink.js'; +import { useOptionalKeybindingContext, useRegisterKeybindingContext } from '../keybindings/KeybindingContext.js'; +import { useKeybindings } from '../keybindings/useKeybinding.js'; +import { useShortcutDisplay } from '../keybindings/useShortcutDisplay.js'; +import { useAppState, useAppStateStore } from '../state/AppState.js'; +import type { AgentDefinition } from '../tools/AgentTool/loadAgentsDir.js'; +import type { InlineGhostText, PromptInputMode } from '../types/textInputTypes.js'; +import { isAgentSwarmsEnabled } from '../utils/agentSwarmsEnabled.js'; +import { generateProgressiveArgumentHint, parseArguments } from '../utils/argumentSubstitution.js'; +import { getShellCompletions, type ShellCompletionType } from '../utils/bash/shellCompletion.js'; +import { formatLogMetadata } from '../utils/format.js'; +import { getSessionIdFromLog, searchSessionsByCustomTitle } from '../utils/sessionStorage.js'; +import { applyCommandSuggestion, findMidInputSlashCommand, generateCommandSuggestions, getBestCommandMatch, isCommandInput } from '../utils/suggestions/commandSuggestions.js'; +import { getDirectoryCompletions, getPathCompletions, isPathLikeToken } from '../utils/suggestions/directoryCompletion.js'; +import { getShellHistoryCompletion } from '../utils/suggestions/shellHistoryCompletion.js'; +import { getSlackChannelSuggestions, hasSlackMcpServer } from '../utils/suggestions/slackChannelSuggestions.js'; +import { TEAM_LEAD_NAME } from '../utils/swarm/constants.js'; +import { applyFileSuggestion, findLongestCommonPrefix, onIndexBuildComplete, startBackgroundCacheRefresh } from './fileSuggestions.js'; +import { generateUnifiedSuggestions } from './unifiedSuggestions.js'; + +// Unicode-aware character class for file path tokens: +// \p{L} = letters (CJK, Latin, Cyrillic, etc.) +// \p{N} = numbers (incl. fullwidth) +// \p{M} = combining marks (macOS NFD accents, Devanagari vowel signs) +const AT_TOKEN_HEAD_RE = /^@[\p{L}\p{N}\p{M}_\-./\\()[\]~:]*/u; +const PATH_CHAR_HEAD_RE = /^[\p{L}\p{N}\p{M}_\-./\\()[\]~:]+/u; +const TOKEN_WITH_AT_RE = /(@[\p{L}\p{N}\p{M}_\-./\\()[\]~:]*|[\p{L}\p{N}\p{M}_\-./\\()[\]~:]+)$/u; +const TOKEN_WITHOUT_AT_RE = /[\p{L}\p{N}\p{M}_\-./\\()[\]~:]+$/u; +const HAS_AT_SYMBOL_RE = /(^|\s)@([\p{L}\p{N}\p{M}_\-./\\()[\]~:]*|"[^"]*"?)$/u; +const HASH_CHANNEL_RE = /(^|\s)#([a-z0-9][a-z0-9_-]*)$/; + +// Type guard for path completion metadata +function isPathMetadata(metadata: unknown): metadata is { + type: 'directory' | 'file'; +} { + return typeof metadata === 'object' && metadata !== null && 'type' in metadata && (metadata.type === 'directory' || metadata.type === 'file'); +} + +// Helper to determine selectedSuggestion when updating suggestions +function getPreservedSelection(prevSuggestions: SuggestionItem[], prevSelection: number, newSuggestions: SuggestionItem[]): number { + // No new suggestions + if (newSuggestions.length === 0) { + return -1; + } + + // No previous selection + if (prevSelection < 0) { + return 0; + } + + // Get the previously selected item + const prevSelectedItem = prevSuggestions[prevSelection]; + if (!prevSelectedItem) { + return 0; + } + + // Try to find the same item in the new list by ID + const newIndex = newSuggestions.findIndex(item => item.id === prevSelectedItem.id); + + // Return the new index if found, otherwise default to 0 + return newIndex >= 0 ? newIndex : 0; +} +function buildResumeInputFromSuggestion(suggestion: SuggestionItem): string { + const metadata = suggestion.metadata as { + sessionId: string; + } | undefined; + return metadata?.sessionId ? `/resume ${metadata.sessionId}` : `/resume ${suggestion.displayText}`; +} +type Props = { + onInputChange: (value: string) => void; + onSubmit: (value: string, isSubmittingSlashCommand?: boolean) => void; + setCursorOffset: (offset: number) => void; + input: string; + cursorOffset: number; + commands: Command[]; + mode: string; + agents: AgentDefinition[]; + setSuggestionsState: (f: (previousSuggestionsState: { + suggestions: SuggestionItem[]; + selectedSuggestion: number; + commandArgumentHint?: string; + }) => { + suggestions: SuggestionItem[]; + selectedSuggestion: number; + commandArgumentHint?: string; + }) => void; + suggestionsState: { + suggestions: SuggestionItem[]; + selectedSuggestion: number; + commandArgumentHint?: string; + }; + suppressSuggestions?: boolean; + markAccepted: () => void; + onModeChange?: (mode: PromptInputMode) => void; +}; +type UseTypeaheadResult = { + suggestions: SuggestionItem[]; + selectedSuggestion: number; + suggestionType: SuggestionType; + maxColumnWidth?: number; + commandArgumentHint?: string; + inlineGhostText?: InlineGhostText; + handleKeyDown: (e: KeyboardEvent) => void; +}; + +/** + * Extract search token from a completion token by removing @ prefix and quotes + * @param completionToken The completion token + * @returns The search token with @ and quotes removed + */ +export function extractSearchToken(completionToken: { + token: string; + isQuoted?: boolean; +}): string { + if (completionToken.isQuoted) { + // Remove @" prefix and optional closing " + return completionToken.token.slice(2).replace(/"$/, ''); + } else if (completionToken.token.startsWith('@')) { + return completionToken.token.substring(1); + } else { + return completionToken.token; + } +} + +/** + * Format a replacement value with proper @ prefix and quotes based on context + * @param options Configuration for formatting + * @param options.displayText The text to display + * @param options.mode The current mode (bash or prompt) + * @param options.hasAtPrefix Whether the original token has @ prefix + * @param options.needsQuotes Whether the text needs quotes (contains spaces) + * @param options.isQuoted Whether the original token was already quoted (user typed @"...) + * @param options.isComplete Whether this is a complete suggestion (adds trailing space) + * @returns The formatted replacement value + */ +export function formatReplacementValue(options: { + displayText: string; + mode: string; + hasAtPrefix: boolean; + needsQuotes: boolean; + isQuoted?: boolean; + isComplete: boolean; +}): string { + const { + displayText, + mode, + hasAtPrefix, + needsQuotes, + isQuoted, + isComplete + } = options; + const space = isComplete ? ' ' : ''; + if (isQuoted || needsQuotes) { + // Use quoted format + return mode === 'bash' ? `"${displayText}"${space}` : `@"${displayText}"${space}`; + } else if (hasAtPrefix) { + return mode === 'bash' ? `${displayText}${space}` : `@${displayText}${space}`; + } else { + return displayText; + } +} + +/** + * Apply a shell completion suggestion by replacing the current word + */ +export function applyShellSuggestion(suggestion: SuggestionItem, input: string, cursorOffset: number, onInputChange: (value: string) => void, setCursorOffset: (offset: number) => void, completionType: ShellCompletionType | undefined): void { + const beforeCursor = input.slice(0, cursorOffset); + const lastSpaceIndex = beforeCursor.lastIndexOf(' '); + const wordStart = lastSpaceIndex + 1; + + // Prepare the replacement text based on completion type + let replacementText: string; + if (completionType === 'variable') { + replacementText = '$' + suggestion.displayText + ' '; + } else if (completionType === 'command') { + replacementText = suggestion.displayText + ' '; + } else { + replacementText = suggestion.displayText; + } + const newInput = input.slice(0, wordStart) + replacementText + input.slice(cursorOffset); + onInputChange(newInput); + setCursorOffset(wordStart + replacementText.length); +} +const DM_MEMBER_RE = /(^|\s)@[\w-]*$/; +function applyTriggerSuggestion(suggestion: SuggestionItem, input: string, cursorOffset: number, triggerRe: RegExp, onInputChange: (value: string) => void, setCursorOffset: (offset: number) => void): void { + const m = input.slice(0, cursorOffset).match(triggerRe); + if (!m || m.index === undefined) return; + const prefixStart = m.index + (m[1]?.length ?? 0); + const before = input.slice(0, prefixStart); + const newInput = before + suggestion.displayText + ' ' + input.slice(cursorOffset); + onInputChange(newInput); + setCursorOffset(before.length + suggestion.displayText.length + 1); +} +let currentShellCompletionAbortController: AbortController | null = null; + +/** + * Generate bash shell completion suggestions + */ +async function generateBashSuggestions(input: string, cursorOffset: number): Promise { + try { + if (currentShellCompletionAbortController) { + currentShellCompletionAbortController.abort(); + } + currentShellCompletionAbortController = new AbortController(); + const suggestions = await getShellCompletions(input, cursorOffset, currentShellCompletionAbortController.signal); + return suggestions; + } catch { + // Silent failure - don't break UX + logEvent('tengu_shell_completion_failed', {}); + return []; + } +} + +/** + * Apply a directory/path completion suggestion to the input + * Always adds @ prefix since we're replacing the entire token (including any existing @) + * + * @param input The current input text + * @param suggestionId The ID of the suggestion to apply + * @param tokenStartPos The start position of the token being replaced + * @param tokenLength The length of the token being replaced + * @param isDirectory Whether the suggestion is a directory (adds / suffix) or file (adds space) + * @returns Object with the new input text and cursor position + */ +export function applyDirectorySuggestion(input: string, suggestionId: string, tokenStartPos: number, tokenLength: number, isDirectory: boolean): { + newInput: string; + cursorPos: number; +} { + const suffix = isDirectory ? '/' : ' '; + const before = input.slice(0, tokenStartPos); + const after = input.slice(tokenStartPos + tokenLength); + // Always add @ prefix - if token already has it, we're replacing + // the whole token (including @) with @suggestion.id + const replacement = '@' + suggestionId + suffix; + const newInput = before + replacement + after; + return { + newInput, + cursorPos: before.length + replacement.length + }; +} + +/** + * Extract a completable token at the cursor position + * @param text The input text + * @param cursorPos The cursor position + * @param includeAtSymbol Whether to consider @ symbol as part of the token + * @returns The completable token and its start position, or null if not found + */ +export function extractCompletionToken(text: string, cursorPos: number, includeAtSymbol = false): { + token: string; + startPos: number; + isQuoted?: boolean; +} | null { + // Empty input check + if (!text) return null; + + // Get text up to cursor + const textBeforeCursor = text.substring(0, cursorPos); + + // Check for quoted @ mention first (e.g., @"my file with spaces") + if (includeAtSymbol) { + const quotedAtRegex = /@"([^"]*)"?$/; + const quotedMatch = textBeforeCursor.match(quotedAtRegex); + if (quotedMatch && quotedMatch.index !== undefined) { + // Include any remaining quoted content after cursor until closing quote or end + const textAfterCursor = text.substring(cursorPos); + const afterQuotedMatch = textAfterCursor.match(/^[^"]*"?/); + const quotedSuffix = afterQuotedMatch ? afterQuotedMatch[0] : ''; + return { + token: quotedMatch[0] + quotedSuffix, + startPos: quotedMatch.index, + isQuoted: true + }; + } + } + + // Fast path for @ tokens: use lastIndexOf to avoid expensive $ anchor scan + if (includeAtSymbol) { + const atIdx = textBeforeCursor.lastIndexOf('@'); + if (atIdx >= 0 && (atIdx === 0 || /\s/.test(textBeforeCursor[atIdx - 1]!))) { + const fromAt = textBeforeCursor.substring(atIdx); + const atHeadMatch = fromAt.match(AT_TOKEN_HEAD_RE); + if (atHeadMatch && atHeadMatch[0].length === fromAt.length) { + const textAfterCursor = text.substring(cursorPos); + const afterMatch = textAfterCursor.match(PATH_CHAR_HEAD_RE); + const tokenSuffix = afterMatch ? afterMatch[0] : ''; + return { + token: atHeadMatch[0] + tokenSuffix, + startPos: atIdx, + isQuoted: false + }; + } + } + } + + // Non-@ token or cursor outside @ token — use $ anchor on (short) tail + const tokenRegex = includeAtSymbol ? TOKEN_WITH_AT_RE : TOKEN_WITHOUT_AT_RE; + const match = textBeforeCursor.match(tokenRegex); + if (!match || match.index === undefined) { + return null; + } + + // Check if cursor is in the MIDDLE of a token (more word characters after cursor) + // If so, extend the token to include all characters until whitespace or end of string + const textAfterCursor = text.substring(cursorPos); + const afterMatch = textAfterCursor.match(PATH_CHAR_HEAD_RE); + const tokenSuffix = afterMatch ? afterMatch[0] : ''; + return { + token: match[0] + tokenSuffix, + startPos: match.index, + isQuoted: false + }; +} +function extractCommandNameAndArgs(value: string): { + commandName: string; + args: string; +} | null { + if (isCommandInput(value)) { + const spaceIndex = value.indexOf(' '); + if (spaceIndex === -1) return { + commandName: value.slice(1), + args: '' + }; + return { + commandName: value.slice(1, spaceIndex), + args: value.slice(spaceIndex + 1) + }; + } + return null; +} +function hasCommandWithArguments(isAtEndWithWhitespace: boolean, value: string) { + // If value.endsWith(' ') but the user is not at the end, then the user has + // potentially gone back to the command in an effort to edit the command name + // (but preserve the arguments). + return !isAtEndWithWhitespace && value.includes(' ') && !value.endsWith(' '); +} + +/** + * Hook for handling typeahead functionality for both commands and file paths + */ +export function useTypeahead({ + commands, + onInputChange, + onSubmit, + setCursorOffset, + input, + cursorOffset, + mode, + agents, + setSuggestionsState, + suggestionsState: { + suggestions, + selectedSuggestion, + commandArgumentHint + }, + suppressSuggestions = false, + markAccepted, + onModeChange +}: Props): UseTypeaheadResult { + const { + addNotification + } = useNotifications(); + const thinkingToggleShortcut = useShortcutDisplay('chat:thinkingToggle', 'Chat', 'alt+t'); + const [suggestionType, setSuggestionType] = useState('none'); + + // Compute max column width from ALL commands once (not filtered results) + // This prevents layout shift when filtering + const allCommandsMaxWidth = useMemo(() => { + const visibleCommands = commands.filter(cmd => !cmd.isHidden); + if (visibleCommands.length === 0) return undefined; + const maxLen = Math.max(...visibleCommands.map(cmd => getCommandName(cmd).length)); + return maxLen + 6; // +1 for "/" prefix, +5 for padding + }, [commands]); + const [maxColumnWidth, setMaxColumnWidth] = useState(undefined); + const mcpResources = useAppState(s => s.mcp.resources); + const store = useAppStateStore(); + const promptSuggestion = useAppState(s => s.promptSuggestion); + // PromptInput hides suggestion ghost text in teammate view — mirror that + // gate here so Tab/rightArrow can't accept what isn't displayed. + const isViewingTeammate = useAppState(s => !!s.viewingAgentTaskId); + + // Access keybinding context to check for pending chord sequences + const keybindingContext = useOptionalKeybindingContext(); + + // State for inline ghost text (bash history completion - async) + const [inlineGhostText, setInlineGhostText] = useState(undefined); + + // Synchronous ghost text for prompt mode mid-input slash commands. + // Computed during render via useMemo to eliminate the one-frame flicker + // that occurs when using useState + useEffect (effect runs after render). + const syncPromptGhostText = useMemo((): InlineGhostText | undefined => { + if (mode !== 'prompt' || suppressSuggestions) return undefined; + const midInputCommand = findMidInputSlashCommand(input, cursorOffset); + if (!midInputCommand) return undefined; + const match = getBestCommandMatch(midInputCommand.partialCommand, commands); + if (!match) return undefined; + return { + text: match.suffix, + fullCommand: match.fullCommand, + insertPosition: midInputCommand.startPos + 1 + midInputCommand.partialCommand.length + }; + }, [input, cursorOffset, mode, commands, suppressSuggestions]); + + // Merged ghost text: prompt mode uses synchronous useMemo, bash mode uses async useState + const effectiveGhostText = suppressSuggestions ? undefined : mode === 'prompt' ? syncPromptGhostText : inlineGhostText; + + // Use a ref for cursorOffset to avoid re-triggering suggestions on cursor movement alone + // We only want to re-fetch suggestions when the actual search token changes + const cursorOffsetRef = useRef(cursorOffset); + cursorOffsetRef.current = cursorOffset; + + // Track the latest search token to discard stale results from slow async operations + const latestSearchTokenRef = useRef(null); + // Track previous input to detect actual text changes vs. callback recreations + const prevInputRef = useRef(''); + // Track the latest path token to discard stale results from path completion + const latestPathTokenRef = useRef(''); + // Track the latest bash input to discard stale results from history completion + const latestBashInputRef = useRef(''); + // Track the latest slack channel token to discard stale results from MCP + const latestSlackTokenRef = useRef(''); + // Track suggestions via ref to avoid updateSuggestions being recreated on selection changes + const suggestionsRef = useRef(suggestions); + suggestionsRef.current = suggestions; + // Track the input value when suggestions were manually dismissed to prevent re-triggering + const dismissedForInputRef = useRef(null); + + // Clear all suggestions + const clearSuggestions = useCallback(() => { + setSuggestionsState(() => ({ + commandArgumentHint: undefined, + suggestions: [], + selectedSuggestion: -1 + })); + setSuggestionType('none'); + setMaxColumnWidth(undefined); + setInlineGhostText(undefined); + }, [setSuggestionsState]); + + // Expensive async operation to fetch file/resource suggestions + const fetchFileSuggestions = useCallback(async (searchToken: string, isAtSymbol = false): Promise => { + latestSearchTokenRef.current = searchToken; + const combinedItems = await generateUnifiedSuggestions(searchToken, mcpResources, agents, isAtSymbol); + // Discard stale results if a newer query was initiated while waiting + if (latestSearchTokenRef.current !== searchToken) { + return; + } + if (combinedItems.length === 0) { + // Inline clearSuggestions logic to avoid needing debouncedFetchFileSuggestions + setSuggestionsState(() => ({ + commandArgumentHint: undefined, + suggestions: [], + selectedSuggestion: -1 + })); + setSuggestionType('none'); + setMaxColumnWidth(undefined); + return; + } + setSuggestionsState(prev => ({ + commandArgumentHint: undefined, + suggestions: combinedItems, + selectedSuggestion: getPreservedSelection(prev.suggestions, prev.selectedSuggestion, combinedItems) + })); + setSuggestionType(combinedItems.length > 0 ? 'file' : 'none'); + setMaxColumnWidth(undefined); // No fixed width for file suggestions + }, [mcpResources, setSuggestionsState, setSuggestionType, setMaxColumnWidth, agents]); + + // Pre-warm the file index on mount so the first @-mention doesn't block. + // The build runs in background with ~4ms event-loop yields, so it doesn't + // delay first render — it just races the user's first @ keystroke. + // + // If the user types before the build finishes, they get partial results + // from the ready chunks; when the build completes, re-fire the last + // search so partial upgrades to full. Clears the token ref so the same + // query isn't discarded as stale. + // + // Skipped under NODE_ENV=test: REPL-mounting tests would spawn git ls-files + // against the real CI workspace (270k+ files on Windows runners), and the + // background build outlives the test — its setImmediate chain leaks into + // subsequent tests in the shard. The subscriber still registers so + // fileSuggestions tests that trigger a refresh directly work correctly. + useEffect(() => { + if ("production" !== 'test') { + startBackgroundCacheRefresh(); + } + return onIndexBuildComplete(() => { + const token = latestSearchTokenRef.current; + if (token !== null) { + latestSearchTokenRef.current = null; + void fetchFileSuggestions(token, token === ''); + } + }); + }, [fetchFileSuggestions]); + + // Debounce the file fetch operation. 50ms sits just above macOS default + // key-repeat (~33ms) so held-delete/backspace coalesces into one search + // instead of stuttering on each repeated key. The search itself is ~8–15ms + // on a 270k-file index. + const debouncedFetchFileSuggestions = useDebounceCallback(fetchFileSuggestions, 50); + const fetchSlackChannels = useCallback(async (partial: string): Promise => { + latestSlackTokenRef.current = partial; + const channels = await getSlackChannelSuggestions(store.getState().mcp.clients, partial); + if (latestSlackTokenRef.current !== partial) return; + setSuggestionsState(prev => ({ + commandArgumentHint: undefined, + suggestions: channels, + selectedSuggestion: getPreservedSelection(prev.suggestions, prev.selectedSuggestion, channels) + })); + setSuggestionType(channels.length > 0 ? 'slack-channel' : 'none'); + setMaxColumnWidth(undefined); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps -- store is a stable context ref + [setSuggestionsState]); + + // First keystroke after # needs the MCP round-trip; subsequent keystrokes + // that share the same first-word segment hit the cache synchronously. + const debouncedFetchSlackChannels = useDebounceCallback(fetchSlackChannels, 150); + + // Handle immediate suggestion logic (cheap operations) + // biome-ignore lint/correctness/useExhaustiveDependencies: store is a stable context ref, read imperatively at call-time + const updateSuggestions = useCallback(async (value: string, inputCursorOffset?: number): Promise => { + // Use provided cursor offset or fall back to ref (avoids dependency on cursorOffset) + const effectiveCursorOffset = inputCursorOffset ?? cursorOffsetRef.current; + if (suppressSuggestions) { + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + return; + } + + // Check for mid-input slash command (e.g., "help me /com") + // Only in prompt mode, not when input starts with "/" (handled separately) + // Note: ghost text for prompt mode is computed synchronously via syncPromptGhostText useMemo. + // We only need to clear dropdown suggestions here when ghost text is active. + if (mode === 'prompt') { + const midInputCommand = findMidInputSlashCommand(value, effectiveCursorOffset); + if (midInputCommand) { + const match = getBestCommandMatch(midInputCommand.partialCommand, commands); + if (match) { + // Clear dropdown suggestions when showing ghost text + setSuggestionsState(() => ({ + commandArgumentHint: undefined, + suggestions: [], + selectedSuggestion: -1 + })); + setSuggestionType('none'); + setMaxColumnWidth(undefined); + return; + } + } + } + + // Bash mode: check for history-based ghost text completion + if (mode === 'bash' && value.trim()) { + latestBashInputRef.current = value; + const historyMatch = await getShellHistoryCompletion(value); + // Discard stale results if input changed while waiting + if (latestBashInputRef.current !== value) { + return; + } + if (historyMatch) { + setInlineGhostText({ + text: historyMatch.suffix, + fullCommand: historyMatch.fullCommand, + insertPosition: value.length + }); + // Clear dropdown suggestions when showing ghost text + setSuggestionsState(() => ({ + commandArgumentHint: undefined, + suggestions: [], + selectedSuggestion: -1 + })); + setSuggestionType('none'); + setMaxColumnWidth(undefined); + return; + } else { + // No history match, clear ghost text + setInlineGhostText(undefined); + } + } + + // Check for @ to trigger team member / named subagent suggestions + // Must check before @ file symbol to prevent conflict + // Skip in bash mode - @ has no special meaning in shell commands + const atMatch = mode !== 'bash' ? value.substring(0, effectiveCursorOffset).match(/(^|\s)@([\w-]*)$/) : null; + if (atMatch) { + const partialName = (atMatch[2] ?? '').toLowerCase(); + // Imperative read — reading at call-time fixes staleness for + // teammates/subagents added mid-session. + const state = store.getState(); + const members: SuggestionItem[] = []; + const seen = new Set(); + if (isAgentSwarmsEnabled() && state.teamContext) { + for (const t of Object.values(state.teamContext.teammates ?? {})) { + if (t.name === TEAM_LEAD_NAME) continue; + if (!t.name.toLowerCase().startsWith(partialName)) continue; + seen.add(t.name); + members.push({ + id: `dm-${t.name}`, + displayText: `@${t.name}`, + description: 'send message' + }); + } + } + for (const [name, agentId] of state.agentNameRegistry) { + if (seen.has(name)) continue; + if (!name.toLowerCase().startsWith(partialName)) continue; + const status = state.tasks[agentId]?.status; + members.push({ + id: `dm-${name}`, + displayText: `@${name}`, + description: status ? `send message · ${status}` : 'send message' + }); + } + if (members.length > 0) { + debouncedFetchFileSuggestions.cancel(); + setSuggestionsState(prev => ({ + commandArgumentHint: undefined, + suggestions: members, + selectedSuggestion: getPreservedSelection(prev.suggestions, prev.selectedSuggestion, members) + })); + setSuggestionType('agent'); + setMaxColumnWidth(undefined); + return; + } + } + + // Check for # to trigger Slack channel suggestions (requires Slack MCP server) + if (mode === 'prompt') { + const hashMatch = value.substring(0, effectiveCursorOffset).match(HASH_CHANNEL_RE); + if (hashMatch && hasSlackMcpServer(store.getState().mcp.clients)) { + debouncedFetchSlackChannels(hashMatch[2]!); + return; + } else if (suggestionType === 'slack-channel') { + debouncedFetchSlackChannels.cancel(); + clearSuggestions(); + } + } + + // Check for @ symbol to trigger file suggestions (including quoted paths) + // Includes colon for MCP resources (e.g., server:resource/path) + const hasAtSymbol = value.substring(0, effectiveCursorOffset).match(HAS_AT_SYMBOL_RE); + + // First, check for slash command suggestions (higher priority than @ symbol) + // Only show slash command selector if cursor is not on the "/" character itself + // Also don't show if cursor is at end of line with whitespace before it + // Don't show slash commands in bash mode + const isAtEndWithWhitespace = effectiveCursorOffset === value.length && effectiveCursorOffset > 0 && value.length > 0 && value[effectiveCursorOffset - 1] === ' '; + + // Handle directory completion for commands + if (mode === 'prompt' && isCommandInput(value) && effectiveCursorOffset > 0) { + const parsedCommand = extractCommandNameAndArgs(value); + if (parsedCommand && parsedCommand.commandName === 'add-dir' && parsedCommand.args) { + const { + args + } = parsedCommand; + + // Clear suggestions if args end with whitespace (user is done with path) + if (args.match(/\s+$/)) { + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + return; + } + const dirSuggestions = await getDirectoryCompletions(args); + if (dirSuggestions.length > 0) { + setSuggestionsState(prev => ({ + suggestions: dirSuggestions, + selectedSuggestion: getPreservedSelection(prev.suggestions, prev.selectedSuggestion, dirSuggestions), + commandArgumentHint: undefined + })); + setSuggestionType('directory'); + return; + } + + // No suggestions found - clear and return + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + return; + } + + // Handle custom title completion for /resume command + if (parsedCommand && parsedCommand.commandName === 'resume' && parsedCommand.args !== undefined && value.includes(' ')) { + const { + args + } = parsedCommand; + + // Get custom title suggestions using partial match + const matches = await searchSessionsByCustomTitle(args, { + limit: 10 + }); + const suggestions = matches.map(log => { + const sessionId = getSessionIdFromLog(log); + return { + id: `resume-title-${sessionId}`, + displayText: log.customTitle!, + description: formatLogMetadata(log), + metadata: { + sessionId + } + }; + }); + if (suggestions.length > 0) { + setSuggestionsState(prev => ({ + suggestions, + selectedSuggestion: getPreservedSelection(prev.suggestions, prev.selectedSuggestion, suggestions), + commandArgumentHint: undefined + })); + setSuggestionType('custom-title'); + return; + } + + // No suggestions found - clear and return + clearSuggestions(); + return; + } + } + + // Determine whether to display the argument hint and command suggestions. + if (mode === 'prompt' && isCommandInput(value) && effectiveCursorOffset > 0 && !hasCommandWithArguments(isAtEndWithWhitespace, value)) { + let commandArgumentHint: string | undefined = undefined; + if (value.length > 1) { + // We have a partial or complete command without arguments + // Check if it matches a command exactly and has an argument hint + + // Extract command name: everything after / until the first space (or end) + const spaceIndex = value.indexOf(' '); + const commandName = spaceIndex === -1 ? value.slice(1) : value.slice(1, spaceIndex); + + // Check if there are real arguments (non-whitespace after the command) + const hasRealArguments = spaceIndex !== -1 && value.slice(spaceIndex + 1).trim().length > 0; + + // Check if input is exactly "command + single space" (ready for arguments) + const hasExactlyOneTrailingSpace = spaceIndex !== -1 && value.length === spaceIndex + 1; + + // If input has a space after the command, don't show suggestions + // This prevents Enter from selecting a different command after Tab completion + if (spaceIndex !== -1) { + const exactMatch = commands.find(cmd => getCommandName(cmd) === commandName); + if (exactMatch || hasRealArguments) { + // Priority 1: Static argumentHint (only on first trailing space for backwards compat) + if (exactMatch?.argumentHint && hasExactlyOneTrailingSpace) { + commandArgumentHint = exactMatch.argumentHint; + } + // Priority 2: Progressive hint from argNames (show when trailing space) + else if (exactMatch?.type === 'prompt' && exactMatch.argNames?.length && value.endsWith(' ')) { + const argsText = value.slice(spaceIndex + 1); + const typedArgs = parseArguments(argsText); + commandArgumentHint = generateProgressiveArgumentHint(exactMatch.argNames, typedArgs); + } + setSuggestionsState(() => ({ + commandArgumentHint, + suggestions: [], + selectedSuggestion: -1 + })); + setSuggestionType('none'); + setMaxColumnWidth(undefined); + return; + } + } + + // Note: argument hint is only shown when there's exactly one trailing space + // (set above when hasExactlyOneTrailingSpace is true) + } + const commandItems = generateCommandSuggestions(value, commands); + setSuggestionsState(() => ({ + commandArgumentHint, + suggestions: commandItems, + selectedSuggestion: commandItems.length > 0 ? 0 : -1 + })); + setSuggestionType(commandItems.length > 0 ? 'command' : 'none'); + + // Use stable width from all commands (prevents layout shift when filtering) + if (commandItems.length > 0) { + setMaxColumnWidth(allCommandsMaxWidth); + } + return; + } + if (suggestionType === 'command') { + // If we had command suggestions but the input no longer starts with '/' + // we need to clear the suggestions. However, we should not return + // because there may be relevant @ symbol and file suggestions. + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } else if (isCommandInput(value) && hasCommandWithArguments(isAtEndWithWhitespace, value)) { + // If we have a command with arguments (no trailing space), clear any stale hint + // This prevents the hint from flashing when transitioning between states + setSuggestionsState(prev => prev.commandArgumentHint ? { + ...prev, + commandArgumentHint: undefined + } : prev); + } + if (suggestionType === 'custom-title') { + // If we had custom-title suggestions but the input is no longer /resume + // we need to clear the suggestions. + clearSuggestions(); + } + if (suggestionType === 'agent' && suggestionsRef.current.some((s: SuggestionItem) => s.id?.startsWith('dm-'))) { + // If we had team member suggestions but the input no longer has @ + // we need to clear the suggestions. + const hasAt = value.substring(0, effectiveCursorOffset).match(/(^|\s)@([\w-]*)$/); + if (!hasAt) { + clearSuggestions(); + } + } + + // Check for @ symbol to trigger file and MCP resource suggestions + // Skip @ autocomplete in bash mode - @ has no special meaning in shell commands + if (hasAtSymbol && mode !== 'bash') { + // Get the @ token (including the @ symbol) + const completionToken = extractCompletionToken(value, effectiveCursorOffset, true); + if (completionToken && completionToken.token.startsWith('@')) { + const searchToken = extractSearchToken(completionToken); + + // If the token after @ is path-like, use path completion instead of fuzzy search + // This handles cases like @~/path, @./path, @/path for directory traversal + if (isPathLikeToken(searchToken)) { + latestPathTokenRef.current = searchToken; + const pathSuggestions = await getPathCompletions(searchToken, { + maxResults: 10 + }); + // Discard stale results if a newer query was initiated while waiting + if (latestPathTokenRef.current !== searchToken) { + return; + } + if (pathSuggestions.length > 0) { + setSuggestionsState(prev => ({ + suggestions: pathSuggestions, + selectedSuggestion: getPreservedSelection(prev.suggestions, prev.selectedSuggestion, pathSuggestions), + commandArgumentHint: undefined + })); + setSuggestionType('directory'); + return; + } + } + + // Skip if we already fetched for this exact token (prevents loop from + // suggestions dependency causing updateSuggestions to be recreated) + if (latestSearchTokenRef.current === searchToken) { + return; + } + void debouncedFetchFileSuggestions(searchToken, true); + return; + } + } + + // If we have active file suggestions or the input changed, check for file suggestions + if (suggestionType === 'file') { + const completionToken = extractCompletionToken(value, effectiveCursorOffset, true); + if (completionToken) { + const searchToken = extractSearchToken(completionToken); + // Skip if we already fetched for this exact token + if (latestSearchTokenRef.current === searchToken) { + return; + } + void debouncedFetchFileSuggestions(searchToken, false); + } else { + // If we had file suggestions but now there's no completion token + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } + } + + // Clear shell suggestions if not in bash mode OR if input has changed + if (suggestionType === 'shell') { + const inputSnapshot = (suggestionsRef.current[0]?.metadata as { + inputSnapshot?: string; + })?.inputSnapshot; + if (mode !== 'bash' || value !== inputSnapshot) { + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } + } + }, [suggestionType, commands, setSuggestionsState, clearSuggestions, debouncedFetchFileSuggestions, debouncedFetchSlackChannels, mode, suppressSuggestions, + // Note: using suggestionsRef instead of suggestions to avoid recreating + // this callback when only selectedSuggestion changes (not the suggestions list) + allCommandsMaxWidth]); + + // Update suggestions when input changes + // Note: We intentionally don't depend on cursorOffset here - cursor movement alone + // shouldn't re-trigger suggestions. The cursorOffsetRef is used to get the current + // position when needed without causing re-renders. + useEffect(() => { + // If suggestions were dismissed for this exact input, don't re-trigger + if (dismissedForInputRef.current === input) { + return; + } + // When the actual input text changes (not just updateSuggestions being recreated), + // reset the search token ref so the same query can be re-fetched. + // This fixes: type @readme.md, clear, retype @readme.md → no suggestions. + if (prevInputRef.current !== input) { + prevInputRef.current = input; + latestSearchTokenRef.current = null; + } + // Clear the dismissed state when input changes + dismissedForInputRef.current = null; + void updateSuggestions(input); + }, [input, updateSuggestions]); + + // Handle tab key press - complete suggestions or trigger file suggestions + const handleTab = useCallback(async () => { + // If we have inline ghost text, apply it + if (effectiveGhostText) { + // Check for bash mode history completion first + if (mode === 'bash') { + // Replace the input with the full command from history + onInputChange(effectiveGhostText.fullCommand); + setCursorOffset(effectiveGhostText.fullCommand.length); + setInlineGhostText(undefined); + return; + } + + // Find the mid-input command to get its position (for prompt mode) + const midInputCommand = findMidInputSlashCommand(input, cursorOffset); + if (midInputCommand) { + // Replace the partial command with the full command + space + const before = input.slice(0, midInputCommand.startPos); + const after = input.slice(midInputCommand.startPos + midInputCommand.token.length); + const newInput = before + '/' + effectiveGhostText.fullCommand + ' ' + after; + const newCursorOffset = midInputCommand.startPos + 1 + effectiveGhostText.fullCommand.length + 1; + onInputChange(newInput); + setCursorOffset(newCursorOffset); + return; + } + } + + // If we have active suggestions, select one + if (suggestions.length > 0) { + // Cancel any pending debounced fetches to prevent flicker when accepting + debouncedFetchFileSuggestions.cancel(); + debouncedFetchSlackChannels.cancel(); + const index = selectedSuggestion === -1 ? 0 : selectedSuggestion; + const suggestion = suggestions[index]; + if (suggestionType === 'command' && index < suggestions.length) { + if (suggestion) { + applyCommandSuggestion(suggestion, false, + // don't execute on tab + commands, onInputChange, setCursorOffset, onSubmit); + clearSuggestions(); + } + } else if (suggestionType === 'custom-title' && suggestions.length > 0) { + // Apply custom title to /resume command with sessionId + if (suggestion) { + const newInput = buildResumeInputFromSuggestion(suggestion); + onInputChange(newInput); + setCursorOffset(newInput.length); + clearSuggestions(); + } + } else if (suggestionType === 'directory' && suggestions.length > 0) { + const suggestion = suggestions[index]; + if (suggestion) { + // Check if this is a command context (e.g., /add-dir) or general path completion + const isInCommandContext = isCommandInput(input); + let newInput: string; + if (isInCommandContext) { + // Command context: replace just the argument portion + const spaceIndex = input.indexOf(' '); + const commandPart = input.slice(0, spaceIndex + 1); // Include the space + const cmdSuffix = isPathMetadata(suggestion.metadata) && suggestion.metadata.type === 'directory' ? '/' : ' '; + newInput = commandPart + suggestion.id + cmdSuffix; + onInputChange(newInput); + setCursorOffset(newInput.length); + if (isPathMetadata(suggestion.metadata) && suggestion.metadata.type === 'directory') { + // For directories, fetch new suggestions for the updated path + setSuggestionsState(prev => ({ + ...prev, + commandArgumentHint: undefined + })); + void updateSuggestions(newInput, newInput.length); + } else { + clearSuggestions(); + } + } else { + // General path completion: replace the path token in input with @-prefixed path + // Try to get token with @ prefix first to check if already prefixed + const completionTokenWithAt = extractCompletionToken(input, cursorOffset, true); + const completionToken = completionTokenWithAt ?? extractCompletionToken(input, cursorOffset, false); + if (completionToken) { + const isDir = isPathMetadata(suggestion.metadata) && suggestion.metadata.type === 'directory'; + const result = applyDirectorySuggestion(input, suggestion.id, completionToken.startPos, completionToken.token.length, isDir); + newInput = result.newInput; + onInputChange(newInput); + setCursorOffset(result.cursorPos); + if (isDir) { + // For directories, fetch new suggestions for the updated path + setSuggestionsState(prev => ({ + ...prev, + commandArgumentHint: undefined + })); + void updateSuggestions(newInput, result.cursorPos); + } else { + // For files, clear suggestions + clearSuggestions(); + } + } else { + // No completion token found (e.g., cursor after space) - just clear suggestions + // without modifying input to avoid data loss + clearSuggestions(); + } + } + } + } else if (suggestionType === 'shell' && suggestions.length > 0) { + const suggestion = suggestions[index]; + if (suggestion) { + const metadata = suggestion.metadata as { + completionType: ShellCompletionType; + } | undefined; + applyShellSuggestion(suggestion, input, cursorOffset, onInputChange, setCursorOffset, metadata?.completionType); + clearSuggestions(); + } + } else if (suggestionType === 'agent' && suggestions.length > 0 && suggestions[index]?.id?.startsWith('dm-')) { + const suggestion = suggestions[index]; + if (suggestion) { + applyTriggerSuggestion(suggestion, input, cursorOffset, DM_MEMBER_RE, onInputChange, setCursorOffset); + clearSuggestions(); + } + } else if (suggestionType === 'slack-channel' && suggestions.length > 0) { + const suggestion = suggestions[index]; + if (suggestion) { + applyTriggerSuggestion(suggestion, input, cursorOffset, HASH_CHANNEL_RE, onInputChange, setCursorOffset); + clearSuggestions(); + } + } else if (suggestionType === 'file' && suggestions.length > 0) { + const completionToken = extractCompletionToken(input, cursorOffset, true); + if (!completionToken) { + clearSuggestions(); + return; + } + + // Check if all suggestions share a common prefix longer than the current input + const commonPrefix = findLongestCommonPrefix(suggestions); + + // Determine if token starts with @ to preserve it during replacement + const hasAtPrefix = completionToken.token.startsWith('@'); + // The effective token length excludes the @ and quotes if present + let effectiveTokenLength: number; + if (completionToken.isQuoted) { + // Remove @" prefix and optional closing " to get effective length + effectiveTokenLength = completionToken.token.slice(2).replace(/"$/, '').length; + } else if (hasAtPrefix) { + effectiveTokenLength = completionToken.token.length - 1; + } else { + effectiveTokenLength = completionToken.token.length; + } + + // If there's a common prefix longer than what the user has typed, + // replace the current input with the common prefix + if (commonPrefix.length > effectiveTokenLength) { + const replacementValue = formatReplacementValue({ + displayText: commonPrefix, + mode, + hasAtPrefix, + needsQuotes: false, + // common prefix doesn't need quotes unless already quoted + isQuoted: completionToken.isQuoted, + isComplete: false // partial completion + }); + applyFileSuggestion(replacementValue, input, completionToken.token, completionToken.startPos, onInputChange, setCursorOffset); + // Don't clear suggestions so user can continue typing or select a specific option + // Instead, update for the new prefix + void updateSuggestions(input.replace(completionToken.token, replacementValue), cursorOffset); + } else if (index < suggestions.length) { + // Otherwise, apply the selected suggestion + const suggestion = suggestions[index]; + if (suggestion) { + const needsQuotes = suggestion.displayText.includes(' '); + const replacementValue = formatReplacementValue({ + displayText: suggestion.displayText, + mode, + hasAtPrefix, + needsQuotes, + isQuoted: completionToken.isQuoted, + isComplete: true // complete suggestion + }); + applyFileSuggestion(replacementValue, input, completionToken.token, completionToken.startPos, onInputChange, setCursorOffset); + clearSuggestions(); + } + } + } + } else if (input.trim() !== '') { + let suggestionType: SuggestionType; + let suggestionItems: SuggestionItem[]; + if (mode === 'bash') { + suggestionType = 'shell'; + // This should be very fast, taking <10ms + const bashSuggestions = await generateBashSuggestions(input, cursorOffset); + if (bashSuggestions.length === 1) { + // If single suggestion, apply it immediately + const suggestion = bashSuggestions[0]; + if (suggestion) { + const metadata = suggestion.metadata as { + completionType: ShellCompletionType; + } | undefined; + applyShellSuggestion(suggestion, input, cursorOffset, onInputChange, setCursorOffset, metadata?.completionType); + } + suggestionItems = []; + } else { + suggestionItems = bashSuggestions; + } + } else { + suggestionType = 'file'; + // If no suggestions, fetch file and MCP resource suggestions + const completionInfo = extractCompletionToken(input, cursorOffset, true); + if (completionInfo) { + // If token starts with @, search without the @ prefix + const isAtSymbol = completionInfo.token.startsWith('@'); + const searchToken = isAtSymbol ? completionInfo.token.substring(1) : completionInfo.token; + suggestionItems = await generateUnifiedSuggestions(searchToken, mcpResources, agents, isAtSymbol); + } else { + suggestionItems = []; + } + } + if (suggestionItems.length > 0) { + // Multiple suggestions or not bash mode: show list + setSuggestionsState(prev => ({ + commandArgumentHint: undefined, + suggestions: suggestionItems, + selectedSuggestion: getPreservedSelection(prev.suggestions, prev.selectedSuggestion, suggestionItems) + })); + setSuggestionType(suggestionType); + setMaxColumnWidth(undefined); + } + } + }, [suggestions, selectedSuggestion, input, suggestionType, commands, mode, onInputChange, setCursorOffset, onSubmit, clearSuggestions, cursorOffset, updateSuggestions, mcpResources, setSuggestionsState, agents, debouncedFetchFileSuggestions, debouncedFetchSlackChannels, effectiveGhostText]); + + // Handle enter key press - apply and execute suggestions + const handleEnter = useCallback(() => { + if (selectedSuggestion < 0 || suggestions.length === 0) return; + const suggestion = suggestions[selectedSuggestion]; + if (suggestionType === 'command' && selectedSuggestion < suggestions.length) { + if (suggestion) { + applyCommandSuggestion(suggestion, true, + // execute on return + commands, onInputChange, setCursorOffset, onSubmit); + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } + } else if (suggestionType === 'custom-title' && selectedSuggestion < suggestions.length) { + // Apply custom title and execute /resume command with sessionId + if (suggestion) { + const newInput = buildResumeInputFromSuggestion(suggestion); + onInputChange(newInput); + setCursorOffset(newInput.length); + onSubmit(newInput, /* isSubmittingSlashCommand */true); + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } + } else if (suggestionType === 'shell' && selectedSuggestion < suggestions.length) { + const suggestion = suggestions[selectedSuggestion]; + if (suggestion) { + const metadata = suggestion.metadata as { + completionType: ShellCompletionType; + } | undefined; + applyShellSuggestion(suggestion, input, cursorOffset, onInputChange, setCursorOffset, metadata?.completionType); + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } + } else if (suggestionType === 'agent' && selectedSuggestion < suggestions.length && suggestion?.id?.startsWith('dm-')) { + applyTriggerSuggestion(suggestion, input, cursorOffset, DM_MEMBER_RE, onInputChange, setCursorOffset); + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } else if (suggestionType === 'slack-channel' && selectedSuggestion < suggestions.length) { + if (suggestion) { + applyTriggerSuggestion(suggestion, input, cursorOffset, HASH_CHANNEL_RE, onInputChange, setCursorOffset); + debouncedFetchSlackChannels.cancel(); + clearSuggestions(); + } + } else if (suggestionType === 'file' && selectedSuggestion < suggestions.length) { + // Extract completion token directly when needed + const completionInfo = extractCompletionToken(input, cursorOffset, true); + if (completionInfo) { + if (suggestion) { + const hasAtPrefix = completionInfo.token.startsWith('@'); + const needsQuotes = suggestion.displayText.includes(' '); + const replacementValue = formatReplacementValue({ + displayText: suggestion.displayText, + mode, + hasAtPrefix, + needsQuotes, + isQuoted: completionInfo.isQuoted, + isComplete: true // complete suggestion + }); + applyFileSuggestion(replacementValue, input, completionInfo.token, completionInfo.startPos, onInputChange, setCursorOffset); + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } + } + } else if (suggestionType === 'directory' && selectedSuggestion < suggestions.length) { + if (suggestion) { + // In command context (e.g., /add-dir), Enter submits the command + // rather than applying the directory suggestion. Just clear + // suggestions and let the submit handler process the current input. + if (isCommandInput(input)) { + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + return; + } + + // General path completion: replace the path token + const completionTokenWithAt = extractCompletionToken(input, cursorOffset, true); + const completionToken = completionTokenWithAt ?? extractCompletionToken(input, cursorOffset, false); + if (completionToken) { + const isDir = isPathMetadata(suggestion.metadata) && suggestion.metadata.type === 'directory'; + const result = applyDirectorySuggestion(input, suggestion.id, completionToken.startPos, completionToken.token.length, isDir); + onInputChange(result.newInput); + setCursorOffset(result.cursorPos); + } + // If no completion token found (e.g., cursor after space), don't modify input + // to avoid data loss - just clear suggestions + + debouncedFetchFileSuggestions.cancel(); + clearSuggestions(); + } + } + }, [suggestions, selectedSuggestion, suggestionType, commands, input, cursorOffset, mode, onInputChange, setCursorOffset, onSubmit, clearSuggestions, debouncedFetchFileSuggestions, debouncedFetchSlackChannels]); + + // Handler for autocomplete:accept - accepts current suggestion via Tab or Right Arrow + const handleAutocompleteAccept = useCallback(() => { + void handleTab(); + }, [handleTab]); + + // Handler for autocomplete:dismiss - clears suggestions and prevents re-triggering + const handleAutocompleteDismiss = useCallback(() => { + debouncedFetchFileSuggestions.cancel(); + debouncedFetchSlackChannels.cancel(); + clearSuggestions(); + // Remember the input when dismissed to prevent immediate re-triggering + dismissedForInputRef.current = input; + }, [debouncedFetchFileSuggestions, debouncedFetchSlackChannels, clearSuggestions, input]); + + // Handler for autocomplete:previous - selects previous suggestion + const handleAutocompletePrevious = useCallback(() => { + setSuggestionsState(prev => ({ + ...prev, + selectedSuggestion: prev.selectedSuggestion <= 0 ? suggestions.length - 1 : prev.selectedSuggestion - 1 + })); + }, [suggestions.length, setSuggestionsState]); + + // Handler for autocomplete:next - selects next suggestion + const handleAutocompleteNext = useCallback(() => { + setSuggestionsState(prev => ({ + ...prev, + selectedSuggestion: prev.selectedSuggestion >= suggestions.length - 1 ? 0 : prev.selectedSuggestion + 1 + })); + }, [suggestions.length, setSuggestionsState]); + + // Autocomplete context keybindings - only active when suggestions are visible + const autocompleteHandlers = useMemo(() => ({ + 'autocomplete:accept': handleAutocompleteAccept, + 'autocomplete:dismiss': handleAutocompleteDismiss, + 'autocomplete:previous': handleAutocompletePrevious, + 'autocomplete:next': handleAutocompleteNext + }), [handleAutocompleteAccept, handleAutocompleteDismiss, handleAutocompletePrevious, handleAutocompleteNext]); + + // Register autocomplete as an overlay so CancelRequestHandler defers ESC handling + // This ensures ESC dismisses autocomplete before canceling running tasks + const isAutocompleteActive = suggestions.length > 0 || !!effectiveGhostText; + const isModalOverlayActive = useIsModalOverlayActive(); + useRegisterOverlay('autocomplete', isAutocompleteActive); + // Register Autocomplete context so it appears in activeContexts for other handlers. + // This allows Chat's resolver to see Autocomplete and defer to its bindings for up/down. + useRegisterKeybindingContext('Autocomplete', isAutocompleteActive); + + // Disable autocomplete keybindings when a modal overlay (e.g., DiffDialog) is active, + // so escape reaches the overlay's handler instead of dismissing autocomplete + useKeybindings(autocompleteHandlers, { + context: 'Autocomplete', + isActive: isAutocompleteActive && !isModalOverlayActive + }); + function acceptSuggestionText(text: string): void { + const detectedMode = getModeFromInput(text); + if (detectedMode !== 'prompt' && onModeChange) { + onModeChange(detectedMode); + const stripped = getValueFromInput(text); + onInputChange(stripped); + setCursorOffset(stripped.length); + } else { + onInputChange(text); + setCursorOffset(text.length); + } + } + + // Handle keyboard input for behaviors not covered by keybindings + const handleKeyDown = (e: KeyboardEvent): void => { + // Handle right arrow to accept prompt suggestion ghost text + if (e.key === 'right' && !isViewingTeammate) { + const suggestionText = promptSuggestion.text; + const suggestionShownAt = promptSuggestion.shownAt; + if (suggestionText && suggestionShownAt > 0 && input === '') { + markAccepted(); + acceptSuggestionText(suggestionText); + e.stopImmediatePropagation(); + return; + } + } + + // Handle Tab key fallback behaviors when no autocomplete suggestions + // Don't handle tab if shift is pressed (used for mode cycle) + if (e.key === 'tab' && !e.shift) { + // Skip if autocomplete is handling this (suggestions or ghost text exist) + if (suggestions.length > 0 || effectiveGhostText) { + return; + } + // Accept prompt suggestion if it exists in AppState + const suggestionText = promptSuggestion.text; + const suggestionShownAt = promptSuggestion.shownAt; + if (suggestionText && suggestionShownAt > 0 && input === '' && !isViewingTeammate) { + e.preventDefault(); + markAccepted(); + acceptSuggestionText(suggestionText); + return; + } + // Remind user about thinking toggle shortcut if empty input + if (input.trim() === '') { + e.preventDefault(); + addNotification({ + key: 'thinking-toggle-hint', + jsx: + Use {thinkingToggleShortcut} to toggle thinking + , + priority: 'immediate', + timeoutMs: 3000 + }); + } + return; + } + + // Only continue with navigation if we have suggestions + if (suggestions.length === 0) return; + + // Handle Ctrl-N/P for navigation (arrows handled by keybindings) + // Skip if we're in the middle of a chord sequence to allow chords like ctrl+f n + const hasPendingChord = keybindingContext?.pendingChord != null; + if (e.ctrl && e.key === 'n' && !hasPendingChord) { + e.preventDefault(); + handleAutocompleteNext(); + return; + } + if (e.ctrl && e.key === 'p' && !hasPendingChord) { + e.preventDefault(); + handleAutocompletePrevious(); + return; + } + + // Handle selection and execution via return/enter + // Shift+Enter and Meta+Enter insert newlines (handled by useTextInput), + // so don't accept the suggestion for those. + if (e.key === 'return' && !e.shift && !e.meta) { + e.preventDefault(); + handleEnter(); + } + }; + + // Backward-compat bridge: PromptInput doesn't yet wire handleKeyDown to + // . Subscribe via useInput and adapt InputEvent → + // KeyboardEvent until the consumer is migrated (separate PR). + // TODO(onKeyDown-migration): remove once PromptInput passes handleKeyDown. + useInput((_input, _key, event) => { + const kbEvent = new KeyboardEvent(event.keypress); + handleKeyDown(kbEvent); + if (kbEvent.didStopImmediatePropagation()) { + event.stopImmediatePropagation(); + } + }); + return { + suggestions, + selectedSuggestion, + suggestionType, + maxColumnWidth, + commandArgumentHint, + inlineGhostText: effectiveGhostText, + handleKeyDown + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlRWZmZWN0IiwidXNlTWVtbyIsInVzZVJlZiIsInVzZVN0YXRlIiwidXNlTm90aWZpY2F0aW9ucyIsIlRleHQiLCJsb2dFdmVudCIsInVzZURlYm91bmNlQ2FsbGJhY2siLCJDb21tYW5kIiwiZ2V0Q29tbWFuZE5hbWUiLCJnZXRNb2RlRnJvbUlucHV0IiwiZ2V0VmFsdWVGcm9tSW5wdXQiLCJTdWdnZXN0aW9uSXRlbSIsIlN1Z2dlc3Rpb25UeXBlIiwidXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUiLCJ1c2VSZWdpc3Rlck92ZXJsYXkiLCJLZXlib2FyZEV2ZW50IiwidXNlSW5wdXQiLCJ1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0IiwidXNlUmVnaXN0ZXJLZXliaW5kaW5nQ29udGV4dCIsInVzZUtleWJpbmRpbmdzIiwidXNlU2hvcnRjdXREaXNwbGF5IiwidXNlQXBwU3RhdGUiLCJ1c2VBcHBTdGF0ZVN0b3JlIiwiQWdlbnREZWZpbml0aW9uIiwiSW5saW5lR2hvc3RUZXh0IiwiUHJvbXB0SW5wdXRNb2RlIiwiaXNBZ2VudFN3YXJtc0VuYWJsZWQiLCJnZW5lcmF0ZVByb2dyZXNzaXZlQXJndW1lbnRIaW50IiwicGFyc2VBcmd1bWVudHMiLCJnZXRTaGVsbENvbXBsZXRpb25zIiwiU2hlbGxDb21wbGV0aW9uVHlwZSIsImZvcm1hdExvZ01ldGFkYXRhIiwiZ2V0U2Vzc2lvbklkRnJvbUxvZyIsInNlYXJjaFNlc3Npb25zQnlDdXN0b21UaXRsZSIsImFwcGx5Q29tbWFuZFN1Z2dlc3Rpb24iLCJmaW5kTWlkSW5wdXRTbGFzaENvbW1hbmQiLCJnZW5lcmF0ZUNvbW1hbmRTdWdnZXN0aW9ucyIsImdldEJlc3RDb21tYW5kTWF0Y2giLCJpc0NvbW1hbmRJbnB1dCIsImdldERpcmVjdG9yeUNvbXBsZXRpb25zIiwiZ2V0UGF0aENvbXBsZXRpb25zIiwiaXNQYXRoTGlrZVRva2VuIiwiZ2V0U2hlbGxIaXN0b3J5Q29tcGxldGlvbiIsImdldFNsYWNrQ2hhbm5lbFN1Z2dlc3Rpb25zIiwiaGFzU2xhY2tNY3BTZXJ2ZXIiLCJURUFNX0xFQURfTkFNRSIsImFwcGx5RmlsZVN1Z2dlc3Rpb24iLCJmaW5kTG9uZ2VzdENvbW1vblByZWZpeCIsIm9uSW5kZXhCdWlsZENvbXBsZXRlIiwic3RhcnRCYWNrZ3JvdW5kQ2FjaGVSZWZyZXNoIiwiZ2VuZXJhdGVVbmlmaWVkU3VnZ2VzdGlvbnMiLCJBVF9UT0tFTl9IRUFEX1JFIiwiUEFUSF9DSEFSX0hFQURfUkUiLCJUT0tFTl9XSVRIX0FUX1JFIiwiVE9LRU5fV0lUSE9VVF9BVF9SRSIsIkhBU19BVF9TWU1CT0xfUkUiLCJIQVNIX0NIQU5ORUxfUkUiLCJpc1BhdGhNZXRhZGF0YSIsIm1ldGFkYXRhIiwidHlwZSIsImdldFByZXNlcnZlZFNlbGVjdGlvbiIsInByZXZTdWdnZXN0aW9ucyIsInByZXZTZWxlY3Rpb24iLCJuZXdTdWdnZXN0aW9ucyIsImxlbmd0aCIsInByZXZTZWxlY3RlZEl0ZW0iLCJuZXdJbmRleCIsImZpbmRJbmRleCIsIml0ZW0iLCJpZCIsImJ1aWxkUmVzdW1lSW5wdXRGcm9tU3VnZ2VzdGlvbiIsInN1Z2dlc3Rpb24iLCJzZXNzaW9uSWQiLCJkaXNwbGF5VGV4dCIsIlByb3BzIiwib25JbnB1dENoYW5nZSIsInZhbHVlIiwib25TdWJtaXQiLCJpc1N1Ym1pdHRpbmdTbGFzaENvbW1hbmQiLCJzZXRDdXJzb3JPZmZzZXQiLCJvZmZzZXQiLCJpbnB1dCIsImN1cnNvck9mZnNldCIsImNvbW1hbmRzIiwibW9kZSIsImFnZW50cyIsInNldFN1Z2dlc3Rpb25zU3RhdGUiLCJmIiwicHJldmlvdXNTdWdnZXN0aW9uc1N0YXRlIiwic3VnZ2VzdGlvbnMiLCJzZWxlY3RlZFN1Z2dlc3Rpb24iLCJjb21tYW5kQXJndW1lbnRIaW50Iiwic3VnZ2VzdGlvbnNTdGF0ZSIsInN1cHByZXNzU3VnZ2VzdGlvbnMiLCJtYXJrQWNjZXB0ZWQiLCJvbk1vZGVDaGFuZ2UiLCJVc2VUeXBlYWhlYWRSZXN1bHQiLCJzdWdnZXN0aW9uVHlwZSIsIm1heENvbHVtbldpZHRoIiwiaW5saW5lR2hvc3RUZXh0IiwiaGFuZGxlS2V5RG93biIsImUiLCJleHRyYWN0U2VhcmNoVG9rZW4iLCJjb21wbGV0aW9uVG9rZW4iLCJ0b2tlbiIsImlzUXVvdGVkIiwic2xpY2UiLCJyZXBsYWNlIiwic3RhcnRzV2l0aCIsInN1YnN0cmluZyIsImZvcm1hdFJlcGxhY2VtZW50VmFsdWUiLCJvcHRpb25zIiwiaGFzQXRQcmVmaXgiLCJuZWVkc1F1b3RlcyIsImlzQ29tcGxldGUiLCJzcGFjZSIsImFwcGx5U2hlbGxTdWdnZXN0aW9uIiwiY29tcGxldGlvblR5cGUiLCJiZWZvcmVDdXJzb3IiLCJsYXN0U3BhY2VJbmRleCIsImxhc3RJbmRleE9mIiwid29yZFN0YXJ0IiwicmVwbGFjZW1lbnRUZXh0IiwibmV3SW5wdXQiLCJETV9NRU1CRVJfUkUiLCJhcHBseVRyaWdnZXJTdWdnZXN0aW9uIiwidHJpZ2dlclJlIiwiUmVnRXhwIiwibSIsIm1hdGNoIiwiaW5kZXgiLCJ1bmRlZmluZWQiLCJwcmVmaXhTdGFydCIsImJlZm9yZSIsImN1cnJlbnRTaGVsbENvbXBsZXRpb25BYm9ydENvbnRyb2xsZXIiLCJBYm9ydENvbnRyb2xsZXIiLCJnZW5lcmF0ZUJhc2hTdWdnZXN0aW9ucyIsIlByb21pc2UiLCJhYm9ydCIsInNpZ25hbCIsImFwcGx5RGlyZWN0b3J5U3VnZ2VzdGlvbiIsInN1Z2dlc3Rpb25JZCIsInRva2VuU3RhcnRQb3MiLCJ0b2tlbkxlbmd0aCIsImlzRGlyZWN0b3J5IiwiY3Vyc29yUG9zIiwic3VmZml4IiwiYWZ0ZXIiLCJyZXBsYWNlbWVudCIsImV4dHJhY3RDb21wbGV0aW9uVG9rZW4iLCJ0ZXh0IiwiaW5jbHVkZUF0U3ltYm9sIiwic3RhcnRQb3MiLCJ0ZXh0QmVmb3JlQ3Vyc29yIiwicXVvdGVkQXRSZWdleCIsInF1b3RlZE1hdGNoIiwidGV4dEFmdGVyQ3Vyc29yIiwiYWZ0ZXJRdW90ZWRNYXRjaCIsInF1b3RlZFN1ZmZpeCIsImF0SWR4IiwidGVzdCIsImZyb21BdCIsImF0SGVhZE1hdGNoIiwiYWZ0ZXJNYXRjaCIsInRva2VuU3VmZml4IiwidG9rZW5SZWdleCIsImV4dHJhY3RDb21tYW5kTmFtZUFuZEFyZ3MiLCJjb21tYW5kTmFtZSIsImFyZ3MiLCJzcGFjZUluZGV4IiwiaW5kZXhPZiIsImhhc0NvbW1hbmRXaXRoQXJndW1lbnRzIiwiaXNBdEVuZFdpdGhXaGl0ZXNwYWNlIiwiaW5jbHVkZXMiLCJlbmRzV2l0aCIsInVzZVR5cGVhaGVhZCIsImFkZE5vdGlmaWNhdGlvbiIsInRoaW5raW5nVG9nZ2xlU2hvcnRjdXQiLCJzZXRTdWdnZXN0aW9uVHlwZSIsImFsbENvbW1hbmRzTWF4V2lkdGgiLCJ2aXNpYmxlQ29tbWFuZHMiLCJmaWx0ZXIiLCJjbWQiLCJpc0hpZGRlbiIsIm1heExlbiIsIk1hdGgiLCJtYXgiLCJtYXAiLCJzZXRNYXhDb2x1bW5XaWR0aCIsIm1jcFJlc291cmNlcyIsInMiLCJtY3AiLCJyZXNvdXJjZXMiLCJzdG9yZSIsInByb21wdFN1Z2dlc3Rpb24iLCJpc1ZpZXdpbmdUZWFtbWF0ZSIsInZpZXdpbmdBZ2VudFRhc2tJZCIsImtleWJpbmRpbmdDb250ZXh0Iiwic2V0SW5saW5lR2hvc3RUZXh0Iiwic3luY1Byb21wdEdob3N0VGV4dCIsIm1pZElucHV0Q29tbWFuZCIsInBhcnRpYWxDb21tYW5kIiwiZnVsbENvbW1hbmQiLCJpbnNlcnRQb3NpdGlvbiIsImVmZmVjdGl2ZUdob3N0VGV4dCIsImN1cnNvck9mZnNldFJlZiIsImN1cnJlbnQiLCJsYXRlc3RTZWFyY2hUb2tlblJlZiIsInByZXZJbnB1dFJlZiIsImxhdGVzdFBhdGhUb2tlblJlZiIsImxhdGVzdEJhc2hJbnB1dFJlZiIsImxhdGVzdFNsYWNrVG9rZW5SZWYiLCJzdWdnZXN0aW9uc1JlZiIsImRpc21pc3NlZEZvcklucHV0UmVmIiwiY2xlYXJTdWdnZXN0aW9ucyIsImZldGNoRmlsZVN1Z2dlc3Rpb25zIiwic2VhcmNoVG9rZW4iLCJpc0F0U3ltYm9sIiwiY29tYmluZWRJdGVtcyIsInByZXYiLCJkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucyIsImZldGNoU2xhY2tDaGFubmVscyIsInBhcnRpYWwiLCJjaGFubmVscyIsImdldFN0YXRlIiwiY2xpZW50cyIsImRlYm91bmNlZEZldGNoU2xhY2tDaGFubmVscyIsInVwZGF0ZVN1Z2dlc3Rpb25zIiwiaW5wdXRDdXJzb3JPZmZzZXQiLCJlZmZlY3RpdmVDdXJzb3JPZmZzZXQiLCJjYW5jZWwiLCJ0cmltIiwiaGlzdG9yeU1hdGNoIiwiYXRNYXRjaCIsInBhcnRpYWxOYW1lIiwidG9Mb3dlckNhc2UiLCJzdGF0ZSIsIm1lbWJlcnMiLCJzZWVuIiwiU2V0IiwidGVhbUNvbnRleHQiLCJ0IiwiT2JqZWN0IiwidmFsdWVzIiwidGVhbW1hdGVzIiwibmFtZSIsImFkZCIsInB1c2giLCJkZXNjcmlwdGlvbiIsImFnZW50SWQiLCJhZ2VudE5hbWVSZWdpc3RyeSIsImhhcyIsInN0YXR1cyIsInRhc2tzIiwiaGFzaE1hdGNoIiwiaGFzQXRTeW1ib2wiLCJwYXJzZWRDb21tYW5kIiwiZGlyU3VnZ2VzdGlvbnMiLCJtYXRjaGVzIiwibGltaXQiLCJsb2ciLCJjdXN0b21UaXRsZSIsImhhc1JlYWxBcmd1bWVudHMiLCJoYXNFeGFjdGx5T25lVHJhaWxpbmdTcGFjZSIsImV4YWN0TWF0Y2giLCJmaW5kIiwiYXJndW1lbnRIaW50IiwiYXJnTmFtZXMiLCJhcmdzVGV4dCIsInR5cGVkQXJncyIsImNvbW1hbmRJdGVtcyIsInNvbWUiLCJoYXNBdCIsInBhdGhTdWdnZXN0aW9ucyIsIm1heFJlc3VsdHMiLCJpbnB1dFNuYXBzaG90IiwiaGFuZGxlVGFiIiwibmV3Q3Vyc29yT2Zmc2V0IiwiaXNJbkNvbW1hbmRDb250ZXh0IiwiY29tbWFuZFBhcnQiLCJjbWRTdWZmaXgiLCJjb21wbGV0aW9uVG9rZW5XaXRoQXQiLCJpc0RpciIsInJlc3VsdCIsImNvbW1vblByZWZpeCIsImVmZmVjdGl2ZVRva2VuTGVuZ3RoIiwicmVwbGFjZW1lbnRWYWx1ZSIsInN1Z2dlc3Rpb25JdGVtcyIsImJhc2hTdWdnZXN0aW9ucyIsImNvbXBsZXRpb25JbmZvIiwiaGFuZGxlRW50ZXIiLCJoYW5kbGVBdXRvY29tcGxldGVBY2NlcHQiLCJoYW5kbGVBdXRvY29tcGxldGVEaXNtaXNzIiwiaGFuZGxlQXV0b2NvbXBsZXRlUHJldmlvdXMiLCJoYW5kbGVBdXRvY29tcGxldGVOZXh0IiwiYXV0b2NvbXBsZXRlSGFuZGxlcnMiLCJpc0F1dG9jb21wbGV0ZUFjdGl2ZSIsImlzTW9kYWxPdmVybGF5QWN0aXZlIiwiY29udGV4dCIsImlzQWN0aXZlIiwiYWNjZXB0U3VnZ2VzdGlvblRleHQiLCJkZXRlY3RlZE1vZGUiLCJzdHJpcHBlZCIsImtleSIsInN1Z2dlc3Rpb25UZXh0Iiwic3VnZ2VzdGlvblNob3duQXQiLCJzaG93bkF0Iiwic3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uIiwic2hpZnQiLCJwcmV2ZW50RGVmYXVsdCIsImpzeCIsInByaW9yaXR5IiwidGltZW91dE1zIiwiaGFzUGVuZGluZ0Nob3JkIiwicGVuZGluZ0Nob3JkIiwiY3RybCIsIm1ldGEiLCJfaW5wdXQiLCJfa2V5IiwiZXZlbnQiLCJrYkV2ZW50Iiwia2V5cHJlc3MiLCJkaWRTdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24iXSwic291cmNlcyI6WyJ1c2VUeXBlYWhlYWQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlTWVtbywgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJ3NyYy9jb250ZXh0L25vdGlmaWNhdGlvbnMuanMnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnc3JjL2luay5qcydcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IHVzZURlYm91bmNlQ2FsbGJhY2sgfSBmcm9tICd1c2Vob29rcy10cydcbmltcG9ydCB7IHR5cGUgQ29tbWFuZCwgZ2V0Q29tbWFuZE5hbWUgfSBmcm9tICcuLi9jb21tYW5kcy5qcydcbmltcG9ydCB7XG4gIGdldE1vZGVGcm9tSW5wdXQsXG4gIGdldFZhbHVlRnJvbUlucHV0LFxufSBmcm9tICcuLi9jb21wb25lbnRzL1Byb21wdElucHV0L2lucHV0TW9kZXMuanMnXG5pbXBvcnQgdHlwZSB7XG4gIFN1Z2dlc3Rpb25JdGVtLFxuICBTdWdnZXN0aW9uVHlwZSxcbn0gZnJvbSAnLi4vY29tcG9uZW50cy9Qcm9tcHRJbnB1dC9Qcm9tcHRJbnB1dEZvb3RlclN1Z2dlc3Rpb25zLmpzJ1xuaW1wb3J0IHtcbiAgdXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUsXG4gIHVzZVJlZ2lzdGVyT3ZlcmxheSxcbn0gZnJvbSAnLi4vY29udGV4dC9vdmVybGF5Q29udGV4dC5qcydcbmltcG9ydCB7IEtleWJvYXJkRXZlbnQgfSBmcm9tICcuLi9pbmsvZXZlbnRzL2tleWJvYXJkLWV2ZW50LmpzJ1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9wcmVmZXItdXNlLWtleWJpbmRpbmdzIC0tIGJhY2t3YXJkLWNvbXBhdCBicmlkZ2UgdW50aWwgY29uc3VtZXJzIHdpcmUgaGFuZGxlS2V5RG93biB0byA8Qm94IG9uS2V5RG93bj5cbmltcG9ydCB7IHVzZUlucHV0IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHtcbiAgdXNlT3B0aW9uYWxLZXliaW5kaW5nQ29udGV4dCxcbiAgdXNlUmVnaXN0ZXJLZXliaW5kaW5nQ29udGV4dCxcbn0gZnJvbSAnLi4va2V5YmluZGluZ3MvS2V5YmluZGluZ0NvbnRleHQuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5ncyB9IGZyb20gJy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyB1c2VTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi9rZXliaW5kaW5ncy91c2VTaG9ydGN1dERpc3BsYXkuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSwgdXNlQXBwU3RhdGVTdG9yZSB9IGZyb20gJy4uL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBBZ2VudERlZmluaXRpb24gfSBmcm9tICcuLi90b29scy9BZ2VudFRvb2wvbG9hZEFnZW50c0Rpci5qcydcbmltcG9ydCB0eXBlIHtcbiAgSW5saW5lR2hvc3RUZXh0LFxuICBQcm9tcHRJbnB1dE1vZGUsXG59IGZyb20gJy4uL3R5cGVzL3RleHRJbnB1dFR5cGVzLmpzJ1xuaW1wb3J0IHsgaXNBZ2VudFN3YXJtc0VuYWJsZWQgfSBmcm9tICcuLi91dGlscy9hZ2VudFN3YXJtc0VuYWJsZWQuanMnXG5pbXBvcnQge1xuICBnZW5lcmF0ZVByb2dyZXNzaXZlQXJndW1lbnRIaW50LFxuICBwYXJzZUFyZ3VtZW50cyxcbn0gZnJvbSAnLi4vdXRpbHMvYXJndW1lbnRTdWJzdGl0dXRpb24uanMnXG5pbXBvcnQge1xuICBnZXRTaGVsbENvbXBsZXRpb25zLFxuICB0eXBlIFNoZWxsQ29tcGxldGlvblR5cGUsXG59IGZyb20gJy4uL3V0aWxzL2Jhc2gvc2hlbGxDb21wbGV0aW9uLmpzJ1xuaW1wb3J0IHsgZm9ybWF0TG9nTWV0YWRhdGEgfSBmcm9tICcuLi91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQge1xuICBnZXRTZXNzaW9uSWRGcm9tTG9nLFxuICBzZWFyY2hTZXNzaW9uc0J5Q3VzdG9tVGl0bGUsXG59IGZyb20gJy4uL3V0aWxzL3Nlc3Npb25TdG9yYWdlLmpzJ1xuaW1wb3J0IHtcbiAgYXBwbHlDb21tYW5kU3VnZ2VzdGlvbixcbiAgZmluZE1pZElucHV0U2xhc2hDb21tYW5kLFxuICBnZW5lcmF0ZUNvbW1hbmRTdWdnZXN0aW9ucyxcbiAgZ2V0QmVzdENvbW1hbmRNYXRjaCxcbiAgaXNDb21tYW5kSW5wdXQsXG59IGZyb20gJy4uL3V0aWxzL3N1Z2dlc3Rpb25zL2NvbW1hbmRTdWdnZXN0aW9ucy5qcydcbmltcG9ydCB7XG4gIGdldERpcmVjdG9yeUNvbXBsZXRpb25zLFxuICBnZXRQYXRoQ29tcGxldGlvbnMsXG4gIGlzUGF0aExpa2VUb2tlbixcbn0gZnJvbSAnLi4vdXRpbHMvc3VnZ2VzdGlvbnMvZGlyZWN0b3J5Q29tcGxldGlvbi5qcydcbmltcG9ydCB7IGdldFNoZWxsSGlzdG9yeUNvbXBsZXRpb24gfSBmcm9tICcuLi91dGlscy9zdWdnZXN0aW9ucy9zaGVsbEhpc3RvcnlDb21wbGV0aW9uLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0U2xhY2tDaGFubmVsU3VnZ2VzdGlvbnMsXG4gIGhhc1NsYWNrTWNwU2VydmVyLFxufSBmcm9tICcuLi91dGlscy9zdWdnZXN0aW9ucy9zbGFja0NoYW5uZWxTdWdnZXN0aW9ucy5qcydcbmltcG9ydCB7IFRFQU1fTEVBRF9OQU1FIH0gZnJvbSAnLi4vdXRpbHMvc3dhcm0vY29uc3RhbnRzLmpzJ1xuaW1wb3J0IHtcbiAgYXBwbHlGaWxlU3VnZ2VzdGlvbixcbiAgZmluZExvbmdlc3RDb21tb25QcmVmaXgsXG4gIG9uSW5kZXhCdWlsZENvbXBsZXRlLFxuICBzdGFydEJhY2tncm91bmRDYWNoZVJlZnJlc2gsXG59IGZyb20gJy4vZmlsZVN1Z2dlc3Rpb25zLmpzJ1xuaW1wb3J0IHsgZ2VuZXJhdGVVbmlmaWVkU3VnZ2VzdGlvbnMgfSBmcm9tICcuL3VuaWZpZWRTdWdnZXN0aW9ucy5qcydcblxuLy8gVW5pY29kZS1hd2FyZSBjaGFyYWN0ZXIgY2xhc3MgZm9yIGZpbGUgcGF0aCB0b2tlbnM6XG4vLyBcXHB7TH0gPSBsZXR0ZXJzIChDSkssIExhdGluLCBDeXJpbGxpYywgZXRjLilcbi8vIFxccHtOfSA9IG51bWJlcnMgKGluY2wuIGZ1bGx3aWR0aClcbi8vIFxccHtNfSA9IGNvbWJpbmluZyBtYXJrcyAobWFjT1MgTkZEIGFjY2VudHMsIERldmFuYWdhcmkgdm93ZWwgc2lnbnMpXG5jb25zdCBBVF9UT0tFTl9IRUFEX1JFID0gL15AW1xccHtMfVxccHtOfVxccHtNfV9cXC0uL1xcXFwoKVtcXF1+Ol0qL3VcbmNvbnN0IFBBVEhfQ0hBUl9IRUFEX1JFID0gL15bXFxwe0x9XFxwe059XFxwe019X1xcLS4vXFxcXCgpW1xcXX46XSsvdVxuY29uc3QgVE9LRU5fV0lUSF9BVF9SRSA9XG4gIC8oQFtcXHB7TH1cXHB7Tn1cXHB7TX1fXFwtLi9cXFxcKClbXFxdfjpdKnxbXFxwe0x9XFxwe059XFxwe019X1xcLS4vXFxcXCgpW1xcXX46XSspJC91XG5jb25zdCBUT0tFTl9XSVRIT1VUX0FUX1JFID0gL1tcXHB7TH1cXHB7Tn1cXHB7TX1fXFwtLi9cXFxcKClbXFxdfjpdKyQvdVxuY29uc3QgSEFTX0FUX1NZTUJPTF9SRSA9IC8oXnxcXHMpQChbXFxwe0x9XFxwe059XFxwe019X1xcLS4vXFxcXCgpW1xcXX46XSp8XCJbXlwiXSpcIj8pJC91XG5jb25zdCBIQVNIX0NIQU5ORUxfUkUgPSAvKF58XFxzKSMoW2EtejAtOV1bYS16MC05Xy1dKikkL1xuXG4vLyBUeXBlIGd1YXJkIGZvciBwYXRoIGNvbXBsZXRpb24gbWV0YWRhdGFcbmZ1bmN0aW9uIGlzUGF0aE1ldGFkYXRhKFxuICBtZXRhZGF0YTogdW5rbm93bixcbik6IG1ldGFkYXRhIGlzIHsgdHlwZTogJ2RpcmVjdG9yeScgfCAnZmlsZScgfSB7XG4gIHJldHVybiAoXG4gICAgdHlwZW9mIG1ldGFkYXRhID09PSAnb2JqZWN0JyAmJlxuICAgIG1ldGFkYXRhICE9PSBudWxsICYmXG4gICAgJ3R5cGUnIGluIG1ldGFkYXRhICYmXG4gICAgKG1ldGFkYXRhLnR5cGUgPT09ICdkaXJlY3RvcnknIHx8IG1ldGFkYXRhLnR5cGUgPT09ICdmaWxlJylcbiAgKVxufVxuXG4vLyBIZWxwZXIgdG8gZGV0ZXJtaW5lIHNlbGVjdGVkU3VnZ2VzdGlvbiB3aGVuIHVwZGF0aW5nIHN1Z2dlc3Rpb25zXG5mdW5jdGlvbiBnZXRQcmVzZXJ2ZWRTZWxlY3Rpb24oXG4gIHByZXZTdWdnZXN0aW9uczogU3VnZ2VzdGlvbkl0ZW1bXSxcbiAgcHJldlNlbGVjdGlvbjogbnVtYmVyLFxuICBuZXdTdWdnZXN0aW9uczogU3VnZ2VzdGlvbkl0ZW1bXSxcbik6IG51bWJlciB7XG4gIC8vIE5vIG5ldyBzdWdnZXN0aW9uc1xuICBpZiAobmV3U3VnZ2VzdGlvbnMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIC0xXG4gIH1cblxuICAvLyBObyBwcmV2aW91cyBzZWxlY3Rpb25cbiAgaWYgKHByZXZTZWxlY3Rpb24gPCAwKSB7XG4gICAgcmV0dXJuIDBcbiAgfVxuXG4gIC8vIEdldCB0aGUgcHJldmlvdXNseSBzZWxlY3RlZCBpdGVtXG4gIGNvbnN0IHByZXZTZWxlY3RlZEl0ZW0gPSBwcmV2U3VnZ2VzdGlvbnNbcHJldlNlbGVjdGlvbl1cbiAgaWYgKCFwcmV2U2VsZWN0ZWRJdGVtKSB7XG4gICAgcmV0dXJuIDBcbiAgfVxuXG4gIC8vIFRyeSB0byBmaW5kIHRoZSBzYW1lIGl0ZW0gaW4gdGhlIG5ldyBsaXN0IGJ5IElEXG4gIGNvbnN0IG5ld0luZGV4ID0gbmV3U3VnZ2VzdGlvbnMuZmluZEluZGV4KFxuICAgIGl0ZW0gPT4gaXRlbS5pZCA9PT0gcHJldlNlbGVjdGVkSXRlbS5pZCxcbiAgKVxuXG4gIC8vIFJldHVybiB0aGUgbmV3IGluZGV4IGlmIGZvdW5kLCBvdGhlcndpc2UgZGVmYXVsdCB0byAwXG4gIHJldHVybiBuZXdJbmRleCA+PSAwID8gbmV3SW5kZXggOiAwXG59XG5cbmZ1bmN0aW9uIGJ1aWxkUmVzdW1lSW5wdXRGcm9tU3VnZ2VzdGlvbihzdWdnZXN0aW9uOiBTdWdnZXN0aW9uSXRlbSk6IHN0cmluZyB7XG4gIGNvbnN0IG1ldGFkYXRhID0gc3VnZ2VzdGlvbi5tZXRhZGF0YSBhcyB7IHNlc3Npb25JZDogc3RyaW5nIH0gfCB1bmRlZmluZWRcbiAgcmV0dXJuIG1ldGFkYXRhPy5zZXNzaW9uSWRcbiAgICA/IGAvcmVzdW1lICR7bWV0YWRhdGEuc2Vzc2lvbklkfWBcbiAgICA6IGAvcmVzdW1lICR7c3VnZ2VzdGlvbi5kaXNwbGF5VGV4dH1gXG59XG5cbnR5cGUgUHJvcHMgPSB7XG4gIG9uSW5wdXRDaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG4gIG9uU3VibWl0OiAodmFsdWU6IHN0cmluZywgaXNTdWJtaXR0aW5nU2xhc2hDb21tYW5kPzogYm9vbGVhbikgPT4gdm9pZFxuICBzZXRDdXJzb3JPZmZzZXQ6IChvZmZzZXQ6IG51bWJlcikgPT4gdm9pZFxuICBpbnB1dDogc3RyaW5nXG4gIGN1cnNvck9mZnNldDogbnVtYmVyXG4gIGNvbW1hbmRzOiBDb21tYW5kW11cbiAgbW9kZTogc3RyaW5nXG4gIGFnZW50czogQWdlbnREZWZpbml0aW9uW11cbiAgc2V0U3VnZ2VzdGlvbnNTdGF0ZTogKFxuICAgIGY6IChwcmV2aW91c1N1Z2dlc3Rpb25zU3RhdGU6IHtcbiAgICAgIHN1Z2dlc3Rpb25zOiBTdWdnZXN0aW9uSXRlbVtdXG4gICAgICBzZWxlY3RlZFN1Z2dlc3Rpb246IG51bWJlclxuICAgICAgY29tbWFuZEFyZ3VtZW50SGludD86IHN0cmluZ1xuICAgIH0pID0+IHtcbiAgICAgIHN1Z2dlc3Rpb25zOiBTdWdnZXN0aW9uSXRlbVtdXG4gICAgICBzZWxlY3RlZFN1Z2dlc3Rpb246IG51bWJlclxuICAgICAgY29tbWFuZEFyZ3VtZW50SGludD86IHN0cmluZ1xuICAgIH0sXG4gICkgPT4gdm9pZFxuICBzdWdnZXN0aW9uc1N0YXRlOiB7XG4gICAgc3VnZ2VzdGlvbnM6IFN1Z2dlc3Rpb25JdGVtW11cbiAgICBzZWxlY3RlZFN1Z2dlc3Rpb246IG51bWJlclxuICAgIGNvbW1hbmRBcmd1bWVudEhpbnQ/OiBzdHJpbmdcbiAgfVxuICBzdXBwcmVzc1N1Z2dlc3Rpb25zPzogYm9vbGVhblxuICBtYXJrQWNjZXB0ZWQ6ICgpID0+IHZvaWRcbiAgb25Nb2RlQ2hhbmdlPzogKG1vZGU6IFByb21wdElucHV0TW9kZSkgPT4gdm9pZFxufVxuXG50eXBlIFVzZVR5cGVhaGVhZFJlc3VsdCA9IHtcbiAgc3VnZ2VzdGlvbnM6IFN1Z2dlc3Rpb25JdGVtW11cbiAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBudW1iZXJcbiAgc3VnZ2VzdGlvblR5cGU6IFN1Z2dlc3Rpb25UeXBlXG4gIG1heENvbHVtbldpZHRoPzogbnVtYmVyXG4gIGNvbW1hbmRBcmd1bWVudEhpbnQ/OiBzdHJpbmdcbiAgaW5saW5lR2hvc3RUZXh0PzogSW5saW5lR2hvc3RUZXh0XG4gIGhhbmRsZUtleURvd246IChlOiBLZXlib2FyZEV2ZW50KSA9PiB2b2lkXG59XG5cbi8qKlxuICogRXh0cmFjdCBzZWFyY2ggdG9rZW4gZnJvbSBhIGNvbXBsZXRpb24gdG9rZW4gYnkgcmVtb3ZpbmcgQCBwcmVmaXggYW5kIHF1b3Rlc1xuICogQHBhcmFtIGNvbXBsZXRpb25Ub2tlbiBUaGUgY29tcGxldGlvbiB0b2tlblxuICogQHJldHVybnMgVGhlIHNlYXJjaCB0b2tlbiB3aXRoIEAgYW5kIHF1b3RlcyByZW1vdmVkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBleHRyYWN0U2VhcmNoVG9rZW4oY29tcGxldGlvblRva2VuOiB7XG4gIHRva2VuOiBzdHJpbmdcbiAgaXNRdW90ZWQ/OiBib29sZWFuXG59KTogc3RyaW5nIHtcbiAgaWYgKGNvbXBsZXRpb25Ub2tlbi5pc1F1b3RlZCkge1xuICAgIC8vIFJlbW92ZSBAXCIgcHJlZml4IGFuZCBvcHRpb25hbCBjbG9zaW5nIFwiXG4gICAgcmV0dXJuIGNvbXBsZXRpb25Ub2tlbi50b2tlbi5zbGljZSgyKS5yZXBsYWNlKC9cIiQvLCAnJylcbiAgfSBlbHNlIGlmIChjb21wbGV0aW9uVG9rZW4udG9rZW4uc3RhcnRzV2l0aCgnQCcpKSB7XG4gICAgcmV0dXJuIGNvbXBsZXRpb25Ub2tlbi50b2tlbi5zdWJzdHJpbmcoMSlcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gY29tcGxldGlvblRva2VuLnRva2VuXG4gIH1cbn1cblxuLyoqXG4gKiBGb3JtYXQgYSByZXBsYWNlbWVudCB2YWx1ZSB3aXRoIHByb3BlciBAIHByZWZpeCBhbmQgcXVvdGVzIGJhc2VkIG9uIGNvbnRleHRcbiAqIEBwYXJhbSBvcHRpb25zIENvbmZpZ3VyYXRpb24gZm9yIGZvcm1hdHRpbmdcbiAqIEBwYXJhbSBvcHRpb25zLmRpc3BsYXlUZXh0IFRoZSB0ZXh0IHRvIGRpc3BsYXlcbiAqIEBwYXJhbSBvcHRpb25zLm1vZGUgVGhlIGN1cnJlbnQgbW9kZSAoYmFzaCBvciBwcm9tcHQpXG4gKiBAcGFyYW0gb3B0aW9ucy5oYXNBdFByZWZpeCBXaGV0aGVyIHRoZSBvcmlnaW5hbCB0b2tlbiBoYXMgQCBwcmVmaXhcbiAqIEBwYXJhbSBvcHRpb25zLm5lZWRzUXVvdGVzIFdoZXRoZXIgdGhlIHRleHQgbmVlZHMgcXVvdGVzIChjb250YWlucyBzcGFjZXMpXG4gKiBAcGFyYW0gb3B0aW9ucy5pc1F1b3RlZCBXaGV0aGVyIHRoZSBvcmlnaW5hbCB0b2tlbiB3YXMgYWxyZWFkeSBxdW90ZWQgKHVzZXIgdHlwZWQgQFwiLi4uKVxuICogQHBhcmFtIG9wdGlvbnMuaXNDb21wbGV0ZSBXaGV0aGVyIHRoaXMgaXMgYSBjb21wbGV0ZSBzdWdnZXN0aW9uIChhZGRzIHRyYWlsaW5nIHNwYWNlKVxuICogQHJldHVybnMgVGhlIGZvcm1hdHRlZCByZXBsYWNlbWVudCB2YWx1ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZm9ybWF0UmVwbGFjZW1lbnRWYWx1ZShvcHRpb25zOiB7XG4gIGRpc3BsYXlUZXh0OiBzdHJpbmdcbiAgbW9kZTogc3RyaW5nXG4gIGhhc0F0UHJlZml4OiBib29sZWFuXG4gIG5lZWRzUXVvdGVzOiBib29sZWFuXG4gIGlzUXVvdGVkPzogYm9vbGVhblxuICBpc0NvbXBsZXRlOiBib29sZWFuXG59KTogc3RyaW5nIHtcbiAgY29uc3QgeyBkaXNwbGF5VGV4dCwgbW9kZSwgaGFzQXRQcmVmaXgsIG5lZWRzUXVvdGVzLCBpc1F1b3RlZCwgaXNDb21wbGV0ZSB9ID1cbiAgICBvcHRpb25zXG4gIGNvbnN0IHNwYWNlID0gaXNDb21wbGV0ZSA/ICcgJyA6ICcnXG5cbiAgaWYgKGlzUXVvdGVkIHx8IG5lZWRzUXVvdGVzKSB7XG4gICAgLy8gVXNlIHF1b3RlZCBmb3JtYXRcbiAgICByZXR1cm4gbW9kZSA9PT0gJ2Jhc2gnXG4gICAgICA/IGBcIiR7ZGlzcGxheVRleHR9XCIke3NwYWNlfWBcbiAgICAgIDogYEBcIiR7ZGlzcGxheVRleHR9XCIke3NwYWNlfWBcbiAgfSBlbHNlIGlmIChoYXNBdFByZWZpeCkge1xuICAgIHJldHVybiBtb2RlID09PSAnYmFzaCdcbiAgICAgID8gYCR7ZGlzcGxheVRleHR9JHtzcGFjZX1gXG4gICAgICA6IGBAJHtkaXNwbGF5VGV4dH0ke3NwYWNlfWBcbiAgfSBlbHNlIHtcbiAgICByZXR1cm4gZGlzcGxheVRleHRcbiAgfVxufVxuXG4vKipcbiAqIEFwcGx5IGEgc2hlbGwgY29tcGxldGlvbiBzdWdnZXN0aW9uIGJ5IHJlcGxhY2luZyB0aGUgY3VycmVudCB3b3JkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhcHBseVNoZWxsU3VnZ2VzdGlvbihcbiAgc3VnZ2VzdGlvbjogU3VnZ2VzdGlvbkl0ZW0sXG4gIGlucHV0OiBzdHJpbmcsXG4gIGN1cnNvck9mZnNldDogbnVtYmVyLFxuICBvbklucHV0Q2hhbmdlOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZCxcbiAgc2V0Q3Vyc29yT2Zmc2V0OiAob2Zmc2V0OiBudW1iZXIpID0+IHZvaWQsXG4gIGNvbXBsZXRpb25UeXBlOiBTaGVsbENvbXBsZXRpb25UeXBlIHwgdW5kZWZpbmVkLFxuKTogdm9pZCB7XG4gIGNvbnN0IGJlZm9yZUN1cnNvciA9IGlucHV0LnNsaWNlKDAsIGN1cnNvck9mZnNldClcbiAgY29uc3QgbGFzdFNwYWNlSW5kZXggPSBiZWZvcmVDdXJzb3IubGFzdEluZGV4T2YoJyAnKVxuICBjb25zdCB3b3JkU3RhcnQgPSBsYXN0U3BhY2VJbmRleCArIDFcblxuICAvLyBQcmVwYXJlIHRoZSByZXBsYWNlbWVudCB0ZXh0IGJhc2VkIG9uIGNvbXBsZXRpb24gdHlwZVxuICBsZXQgcmVwbGFjZW1lbnRUZXh0OiBzdHJpbmdcbiAgaWYgKGNvbXBsZXRpb25UeXBlID09PSAndmFyaWFibGUnKSB7XG4gICAgcmVwbGFjZW1lbnRUZXh0ID0gJyQnICsgc3VnZ2VzdGlvbi5kaXNwbGF5VGV4dCArICcgJ1xuICB9IGVsc2UgaWYgKGNvbXBsZXRpb25UeXBlID09PSAnY29tbWFuZCcpIHtcbiAgICByZXBsYWNlbWVudFRleHQgPSBzdWdnZXN0aW9uLmRpc3BsYXlUZXh0ICsgJyAnXG4gIH0gZWxzZSB7XG4gICAgcmVwbGFjZW1lbnRUZXh0ID0gc3VnZ2VzdGlvbi5kaXNwbGF5VGV4dFxuICB9XG5cbiAgY29uc3QgbmV3SW5wdXQgPVxuICAgIGlucHV0LnNsaWNlKDAsIHdvcmRTdGFydCkgKyByZXBsYWNlbWVudFRleHQgKyBpbnB1dC5zbGljZShjdXJzb3JPZmZzZXQpXG5cbiAgb25JbnB1dENoYW5nZShuZXdJbnB1dClcbiAgc2V0Q3Vyc29yT2Zmc2V0KHdvcmRTdGFydCArIHJlcGxhY2VtZW50VGV4dC5sZW5ndGgpXG59XG5cbmNvbnN0IERNX01FTUJFUl9SRSA9IC8oXnxcXHMpQFtcXHctXSokL1xuXG5mdW5jdGlvbiBhcHBseVRyaWdnZXJTdWdnZXN0aW9uKFxuICBzdWdnZXN0aW9uOiBTdWdnZXN0aW9uSXRlbSxcbiAgaW5wdXQ6IHN0cmluZyxcbiAgY3Vyc29yT2Zmc2V0OiBudW1iZXIsXG4gIHRyaWdnZXJSZTogUmVnRXhwLFxuICBvbklucHV0Q2hhbmdlOiAodmFsdWU6IHN0cmluZykgPT4gdm9pZCxcbiAgc2V0Q3Vyc29yT2Zmc2V0OiAob2Zmc2V0OiBudW1iZXIpID0+IHZvaWQsXG4pOiB2b2lkIHtcbiAgY29uc3QgbSA9IGlucHV0LnNsaWNlKDAsIGN1cnNvck9mZnNldCkubWF0Y2godHJpZ2dlclJlKVxuICBpZiAoIW0gfHwgbS5pbmRleCA9PT0gdW5kZWZpbmVkKSByZXR1cm5cbiAgY29uc3QgcHJlZml4U3RhcnQgPSBtLmluZGV4ICsgKG1bMV0/Lmxlbmd0aCA/PyAwKVxuICBjb25zdCBiZWZvcmUgPSBpbnB1dC5zbGljZSgwLCBwcmVmaXhTdGFydClcbiAgY29uc3QgbmV3SW5wdXQgPVxuICAgIGJlZm9yZSArIHN1Z2dlc3Rpb24uZGlzcGxheVRleHQgKyAnICcgKyBpbnB1dC5zbGljZShjdXJzb3JPZmZzZXQpXG4gIG9uSW5wdXRDaGFuZ2UobmV3SW5wdXQpXG4gIHNldEN1cnNvck9mZnNldChiZWZvcmUubGVuZ3RoICsgc3VnZ2VzdGlvbi5kaXNwbGF5VGV4dC5sZW5ndGggKyAxKVxufVxuXG5sZXQgY3VycmVudFNoZWxsQ29tcGxldGlvbkFib3J0Q29udHJvbGxlcjogQWJvcnRDb250cm9sbGVyIHwgbnVsbCA9IG51bGxcblxuLyoqXG4gKiBHZW5lcmF0ZSBiYXNoIHNoZWxsIGNvbXBsZXRpb24gc3VnZ2VzdGlvbnNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gZ2VuZXJhdGVCYXNoU3VnZ2VzdGlvbnMoXG4gIGlucHV0OiBzdHJpbmcsXG4gIGN1cnNvck9mZnNldDogbnVtYmVyLFxuKTogUHJvbWlzZTxTdWdnZXN0aW9uSXRlbVtdPiB7XG4gIHRyeSB7XG4gICAgaWYgKGN1cnJlbnRTaGVsbENvbXBsZXRpb25BYm9ydENvbnRyb2xsZXIpIHtcbiAgICAgIGN1cnJlbnRTaGVsbENvbXBsZXRpb25BYm9ydENvbnRyb2xsZXIuYWJvcnQoKVxuICAgIH1cblxuICAgIGN1cnJlbnRTaGVsbENvbXBsZXRpb25BYm9ydENvbnRyb2xsZXIgPSBuZXcgQWJvcnRDb250cm9sbGVyKClcbiAgICBjb25zdCBzdWdnZXN0aW9ucyA9IGF3YWl0IGdldFNoZWxsQ29tcGxldGlvbnMoXG4gICAgICBpbnB1dCxcbiAgICAgIGN1cnNvck9mZnNldCxcbiAgICAgIGN1cnJlbnRTaGVsbENvbXBsZXRpb25BYm9ydENvbnRyb2xsZXIuc2lnbmFsLFxuICAgIClcblxuICAgIHJldHVybiBzdWdnZXN0aW9uc1xuICB9IGNhdGNoIHtcbiAgICAvLyBTaWxlbnQgZmFpbHVyZSAtIGRvbid0IGJyZWFrIFVYXG4gICAgbG9nRXZlbnQoJ3Rlbmd1X3NoZWxsX2NvbXBsZXRpb25fZmFpbGVkJywge30pXG4gICAgcmV0dXJuIFtdXG4gIH1cbn1cblxuLyoqXG4gKiBBcHBseSBhIGRpcmVjdG9yeS9wYXRoIGNvbXBsZXRpb24gc3VnZ2VzdGlvbiB0byB0aGUgaW5wdXRcbiAqIEFsd2F5cyBhZGRzIEAgcHJlZml4IHNpbmNlIHdlJ3JlIHJlcGxhY2luZyB0aGUgZW50aXJlIHRva2VuIChpbmNsdWRpbmcgYW55IGV4aXN0aW5nIEApXG4gKlxuICogQHBhcmFtIGlucHV0IFRoZSBjdXJyZW50IGlucHV0IHRleHRcbiAqIEBwYXJhbSBzdWdnZXN0aW9uSWQgVGhlIElEIG9mIHRoZSBzdWdnZXN0aW9uIHRvIGFwcGx5XG4gKiBAcGFyYW0gdG9rZW5TdGFydFBvcyBUaGUgc3RhcnQgcG9zaXRpb24gb2YgdGhlIHRva2VuIGJlaW5nIHJlcGxhY2VkXG4gKiBAcGFyYW0gdG9rZW5MZW5ndGggVGhlIGxlbmd0aCBvZiB0aGUgdG9rZW4gYmVpbmcgcmVwbGFjZWRcbiAqIEBwYXJhbSBpc0RpcmVjdG9yeSBXaGV0aGVyIHRoZSBzdWdnZXN0aW9uIGlzIGEgZGlyZWN0b3J5IChhZGRzIC8gc3VmZml4KSBvciBmaWxlIChhZGRzIHNwYWNlKVxuICogQHJldHVybnMgT2JqZWN0IHdpdGggdGhlIG5ldyBpbnB1dCB0ZXh0IGFuZCBjdXJzb3IgcG9zaXRpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFwcGx5RGlyZWN0b3J5U3VnZ2VzdGlvbihcbiAgaW5wdXQ6IHN0cmluZyxcbiAgc3VnZ2VzdGlvbklkOiBzdHJpbmcsXG4gIHRva2VuU3RhcnRQb3M6IG51bWJlcixcbiAgdG9rZW5MZW5ndGg6IG51bWJlcixcbiAgaXNEaXJlY3Rvcnk6IGJvb2xlYW4sXG4pOiB7IG5ld0lucHV0OiBzdHJpbmc7IGN1cnNvclBvczogbnVtYmVyIH0ge1xuICBjb25zdCBzdWZmaXggPSBpc0RpcmVjdG9yeSA/ICcvJyA6ICcgJ1xuICBjb25zdCBiZWZvcmUgPSBpbnB1dC5zbGljZSgwLCB0b2tlblN0YXJ0UG9zKVxuICBjb25zdCBhZnRlciA9IGlucHV0LnNsaWNlKHRva2VuU3RhcnRQb3MgKyB0b2tlbkxlbmd0aClcbiAgLy8gQWx3YXlzIGFkZCBAIHByZWZpeCAtIGlmIHRva2VuIGFscmVhZHkgaGFzIGl0LCB3ZSdyZSByZXBsYWNpbmdcbiAgLy8gdGhlIHdob2xlIHRva2VuIChpbmNsdWRpbmcgQCkgd2l0aCBAc3VnZ2VzdGlvbi5pZFxuICBjb25zdCByZXBsYWNlbWVudCA9ICdAJyArIHN1Z2dlc3Rpb25JZCArIHN1ZmZpeFxuICBjb25zdCBuZXdJbnB1dCA9IGJlZm9yZSArIHJlcGxhY2VtZW50ICsgYWZ0ZXJcblxuICByZXR1cm4ge1xuICAgIG5ld0lucHV0LFxuICAgIGN1cnNvclBvczogYmVmb3JlLmxlbmd0aCArIHJlcGxhY2VtZW50Lmxlbmd0aCxcbiAgfVxufVxuXG4vKipcbiAqIEV4dHJhY3QgYSBjb21wbGV0YWJsZSB0b2tlbiBhdCB0aGUgY3Vyc29yIHBvc2l0aW9uXG4gKiBAcGFyYW0gdGV4dCBUaGUgaW5wdXQgdGV4dFxuICogQHBhcmFtIGN1cnNvclBvcyBUaGUgY3Vyc29yIHBvc2l0aW9uXG4gKiBAcGFyYW0gaW5jbHVkZUF0U3ltYm9sIFdoZXRoZXIgdG8gY29uc2lkZXIgQCBzeW1ib2wgYXMgcGFydCBvZiB0aGUgdG9rZW5cbiAqIEByZXR1cm5zIFRoZSBjb21wbGV0YWJsZSB0b2tlbiBhbmQgaXRzIHN0YXJ0IHBvc2l0aW9uLCBvciBudWxsIGlmIG5vdCBmb3VuZFxuICovXG5leHBvcnQgZnVuY3Rpb24gZXh0cmFjdENvbXBsZXRpb25Ub2tlbihcbiAgdGV4dDogc3RyaW5nLFxuICBjdXJzb3JQb3M6IG51bWJlcixcbiAgaW5jbHVkZUF0U3ltYm9sID0gZmFsc2UsXG4pOiB7IHRva2VuOiBzdHJpbmc7IHN0YXJ0UG9zOiBudW1iZXI7IGlzUXVvdGVkPzogYm9vbGVhbiB9IHwgbnVsbCB7XG4gIC8vIEVtcHR5IGlucHV0IGNoZWNrXG4gIGlmICghdGV4dCkgcmV0dXJuIG51bGxcblxuICAvLyBHZXQgdGV4dCB1cCB0byBjdXJzb3JcbiAgY29uc3QgdGV4dEJlZm9yZUN1cnNvciA9IHRleHQuc3Vic3RyaW5nKDAsIGN1cnNvclBvcylcblxuICAvLyBDaGVjayBmb3IgcXVvdGVkIEAgbWVudGlvbiBmaXJzdCAoZS5nLiwgQFwibXkgZmlsZSB3aXRoIHNwYWNlc1wiKVxuICBpZiAoaW5jbHVkZUF0U3ltYm9sKSB7XG4gICAgY29uc3QgcXVvdGVkQXRSZWdleCA9IC9AXCIoW15cIl0qKVwiPyQvXG4gICAgY29uc3QgcXVvdGVkTWF0Y2ggPSB0ZXh0QmVmb3JlQ3Vyc29yLm1hdGNoKHF1b3RlZEF0UmVnZXgpXG4gICAgaWYgKHF1b3RlZE1hdGNoICYmIHF1b3RlZE1hdGNoLmluZGV4ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIEluY2x1ZGUgYW55IHJlbWFpbmluZyBxdW90ZWQgY29udGVudCBhZnRlciBjdXJzb3IgdW50aWwgY2xvc2luZyBxdW90ZSBvciBlbmRcbiAgICAgIGNvbnN0IHRleHRBZnRlckN1cnNvciA9IHRleHQuc3Vic3RyaW5nKGN1cnNvclBvcylcbiAgICAgIGNvbnN0IGFmdGVyUXVvdGVkTWF0Y2ggPSB0ZXh0QWZ0ZXJDdXJzb3IubWF0Y2goL15bXlwiXSpcIj8vKVxuICAgICAgY29uc3QgcXVvdGVkU3VmZml4ID0gYWZ0ZXJRdW90ZWRNYXRjaCA/IGFmdGVyUXVvdGVkTWF0Y2hbMF0gOiAnJ1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICB0b2tlbjogcXVvdGVkTWF0Y2hbMF0gKyBxdW90ZWRTdWZmaXgsXG4gICAgICAgIHN0YXJ0UG9zOiBxdW90ZWRNYXRjaC5pbmRleCxcbiAgICAgICAgaXNRdW90ZWQ6IHRydWUsXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gRmFzdCBwYXRoIGZvciBAIHRva2VuczogdXNlIGxhc3RJbmRleE9mIHRvIGF2b2lkIGV4cGVuc2l2ZSAkIGFuY2hvciBzY2FuXG4gIGlmIChpbmNsdWRlQXRTeW1ib2wpIHtcbiAgICBjb25zdCBhdElkeCA9IHRleHRCZWZvcmVDdXJzb3IubGFzdEluZGV4T2YoJ0AnKVxuICAgIGlmIChcbiAgICAgIGF0SWR4ID49IDAgJiZcbiAgICAgIChhdElkeCA9PT0gMCB8fCAvXFxzLy50ZXN0KHRleHRCZWZvcmVDdXJzb3JbYXRJZHggLSAxXSEpKVxuICAgICkge1xuICAgICAgY29uc3QgZnJvbUF0ID0gdGV4dEJlZm9yZUN1cnNvci5zdWJzdHJpbmcoYXRJZHgpXG4gICAgICBjb25zdCBhdEhlYWRNYXRjaCA9IGZyb21BdC5tYXRjaChBVF9UT0tFTl9IRUFEX1JFKVxuICAgICAgaWYgKGF0SGVhZE1hdGNoICYmIGF0SGVhZE1hdGNoWzBdLmxlbmd0aCA9PT0gZnJvbUF0Lmxlbmd0aCkge1xuICAgICAgICBjb25zdCB0ZXh0QWZ0ZXJDdXJzb3IgPSB0ZXh0LnN1YnN0cmluZyhjdXJzb3JQb3MpXG4gICAgICAgIGNvbnN0IGFmdGVyTWF0Y2ggPSB0ZXh0QWZ0ZXJDdXJzb3IubWF0Y2goUEFUSF9DSEFSX0hFQURfUkUpXG4gICAgICAgIGNvbnN0IHRva2VuU3VmZml4ID0gYWZ0ZXJNYXRjaCA/IGFmdGVyTWF0Y2hbMF0gOiAnJ1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHRva2VuOiBhdEhlYWRNYXRjaFswXSArIHRva2VuU3VmZml4LFxuICAgICAgICAgIHN0YXJ0UG9zOiBhdElkeCxcbiAgICAgICAgICBpc1F1b3RlZDogZmFsc2UsXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBOb24tQCB0b2tlbiBvciBjdXJzb3Igb3V0c2lkZSBAIHRva2VuIOKAlCB1c2UgJCBhbmNob3Igb24gKHNob3J0KSB0YWlsXG4gIGNvbnN0IHRva2VuUmVnZXggPSBpbmNsdWRlQXRTeW1ib2wgPyBUT0tFTl9XSVRIX0FUX1JFIDogVE9LRU5fV0lUSE9VVF9BVF9SRVxuICBjb25zdCBtYXRjaCA9IHRleHRCZWZvcmVDdXJzb3IubWF0Y2godG9rZW5SZWdleClcbiAgaWYgKCFtYXRjaCB8fCBtYXRjaC5pbmRleCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIC8vIENoZWNrIGlmIGN1cnNvciBpcyBpbiB0aGUgTUlERExFIG9mIGEgdG9rZW4gKG1vcmUgd29yZCBjaGFyYWN0ZXJzIGFmdGVyIGN1cnNvcilcbiAgLy8gSWYgc28sIGV4dGVuZCB0aGUgdG9rZW4gdG8gaW5jbHVkZSBhbGwgY2hhcmFjdGVycyB1bnRpbCB3aGl0ZXNwYWNlIG9yIGVuZCBvZiBzdHJpbmdcbiAgY29uc3QgdGV4dEFmdGVyQ3Vyc29yID0gdGV4dC5zdWJzdHJpbmcoY3Vyc29yUG9zKVxuICBjb25zdCBhZnRlck1hdGNoID0gdGV4dEFmdGVyQ3Vyc29yLm1hdGNoKFBBVEhfQ0hBUl9IRUFEX1JFKVxuICBjb25zdCB0b2tlblN1ZmZpeCA9IGFmdGVyTWF0Y2ggPyBhZnRlck1hdGNoWzBdIDogJydcblxuICByZXR1cm4ge1xuICAgIHRva2VuOiBtYXRjaFswXSArIHRva2VuU3VmZml4LFxuICAgIHN0YXJ0UG9zOiBtYXRjaC5pbmRleCxcbiAgICBpc1F1b3RlZDogZmFsc2UsXG4gIH1cbn1cblxuZnVuY3Rpb24gZXh0cmFjdENvbW1hbmROYW1lQW5kQXJncyh2YWx1ZTogc3RyaW5nKToge1xuICBjb21tYW5kTmFtZTogc3RyaW5nXG4gIGFyZ3M6IHN0cmluZ1xufSB8IG51bGwge1xuICBpZiAoaXNDb21tYW5kSW5wdXQodmFsdWUpKSB7XG4gICAgY29uc3Qgc3BhY2VJbmRleCA9IHZhbHVlLmluZGV4T2YoJyAnKVxuICAgIGlmIChzcGFjZUluZGV4ID09PSAtMSlcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvbW1hbmROYW1lOiB2YWx1ZS5zbGljZSgxKSxcbiAgICAgICAgYXJnczogJycsXG4gICAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIGNvbW1hbmROYW1lOiB2YWx1ZS5zbGljZSgxLCBzcGFjZUluZGV4KSxcbiAgICAgIGFyZ3M6IHZhbHVlLnNsaWNlKHNwYWNlSW5kZXggKyAxKSxcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGxcbn1cblxuZnVuY3Rpb24gaGFzQ29tbWFuZFdpdGhBcmd1bWVudHMoXG4gIGlzQXRFbmRXaXRoV2hpdGVzcGFjZTogYm9vbGVhbixcbiAgdmFsdWU6IHN0cmluZyxcbikge1xuICAvLyBJZiB2YWx1ZS5lbmRzV2l0aCgnICcpIGJ1dCB0aGUgdXNlciBpcyBub3QgYXQgdGhlIGVuZCwgdGhlbiB0aGUgdXNlciBoYXNcbiAgLy8gcG90ZW50aWFsbHkgZ29uZSBiYWNrIHRvIHRoZSBjb21tYW5kIGluIGFuIGVmZm9ydCB0byBlZGl0IHRoZSBjb21tYW5kIG5hbWVcbiAgLy8gKGJ1dCBwcmVzZXJ2ZSB0aGUgYXJndW1lbnRzKS5cbiAgcmV0dXJuICFpc0F0RW5kV2l0aFdoaXRlc3BhY2UgJiYgdmFsdWUuaW5jbHVkZXMoJyAnKSAmJiAhdmFsdWUuZW5kc1dpdGgoJyAnKVxufVxuXG4vKipcbiAqIEhvb2sgZm9yIGhhbmRsaW5nIHR5cGVhaGVhZCBmdW5jdGlvbmFsaXR5IGZvciBib3RoIGNvbW1hbmRzIGFuZCBmaWxlIHBhdGhzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1c2VUeXBlYWhlYWQoe1xuICBjb21tYW5kcyxcbiAgb25JbnB1dENoYW5nZSxcbiAgb25TdWJtaXQsXG4gIHNldEN1cnNvck9mZnNldCxcbiAgaW5wdXQsXG4gIGN1cnNvck9mZnNldCxcbiAgbW9kZSxcbiAgYWdlbnRzLFxuICBzZXRTdWdnZXN0aW9uc1N0YXRlLFxuICBzdWdnZXN0aW9uc1N0YXRlOiB7IHN1Z2dlc3Rpb25zLCBzZWxlY3RlZFN1Z2dlc3Rpb24sIGNvbW1hbmRBcmd1bWVudEhpbnQgfSxcbiAgc3VwcHJlc3NTdWdnZXN0aW9ucyA9IGZhbHNlLFxuICBtYXJrQWNjZXB0ZWQsXG4gIG9uTW9kZUNoYW5nZSxcbn06IFByb3BzKTogVXNlVHlwZWFoZWFkUmVzdWx0IHtcbiAgY29uc3QgeyBhZGROb3RpZmljYXRpb24gfSA9IHVzZU5vdGlmaWNhdGlvbnMoKVxuICBjb25zdCB0aGlua2luZ1RvZ2dsZVNob3J0Y3V0ID0gdXNlU2hvcnRjdXREaXNwbGF5KFxuICAgICdjaGF0OnRoaW5raW5nVG9nZ2xlJyxcbiAgICAnQ2hhdCcsXG4gICAgJ2FsdCt0JyxcbiAgKVxuICBjb25zdCBbc3VnZ2VzdGlvblR5cGUsIHNldFN1Z2dlc3Rpb25UeXBlXSA9IHVzZVN0YXRlPFN1Z2dlc3Rpb25UeXBlPignbm9uZScpXG5cbiAgLy8gQ29tcHV0ZSBtYXggY29sdW1uIHdpZHRoIGZyb20gQUxMIGNvbW1hbmRzIG9uY2UgKG5vdCBmaWx0ZXJlZCByZXN1bHRzKVxuICAvLyBUaGlzIHByZXZlbnRzIGxheW91dCBzaGlmdCB3aGVuIGZpbHRlcmluZ1xuICBjb25zdCBhbGxDb21tYW5kc01heFdpZHRoID0gdXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3QgdmlzaWJsZUNvbW1hbmRzID0gY29tbWFuZHMuZmlsdGVyKGNtZCA9PiAhY21kLmlzSGlkZGVuKVxuICAgIGlmICh2aXNpYmxlQ29tbWFuZHMubGVuZ3RoID09PSAwKSByZXR1cm4gdW5kZWZpbmVkXG4gICAgY29uc3QgbWF4TGVuID0gTWF0aC5tYXgoXG4gICAgICAuLi52aXNpYmxlQ29tbWFuZHMubWFwKGNtZCA9PiBnZXRDb21tYW5kTmFtZShjbWQpLmxlbmd0aCksXG4gICAgKVxuICAgIHJldHVybiBtYXhMZW4gKyA2IC8vICsxIGZvciBcIi9cIiBwcmVmaXgsICs1IGZvciBwYWRkaW5nXG4gIH0sIFtjb21tYW5kc10pXG5cbiAgY29uc3QgW21heENvbHVtbldpZHRoLCBzZXRNYXhDb2x1bW5XaWR0aF0gPSB1c2VTdGF0ZTxudW1iZXIgfCB1bmRlZmluZWQ+KFxuICAgIHVuZGVmaW5lZCxcbiAgKVxuICBjb25zdCBtY3BSZXNvdXJjZXMgPSB1c2VBcHBTdGF0ZShzID0+IHMubWNwLnJlc291cmNlcylcbiAgY29uc3Qgc3RvcmUgPSB1c2VBcHBTdGF0ZVN0b3JlKClcbiAgY29uc3QgcHJvbXB0U3VnZ2VzdGlvbiA9IHVzZUFwcFN0YXRlKHMgPT4gcy5wcm9tcHRTdWdnZXN0aW9uKVxuICAvLyBQcm9tcHRJbnB1dCBoaWRlcyBzdWdnZXN0aW9uIGdob3N0IHRleHQgaW4gdGVhbW1hdGUgdmlldyDigJQgbWlycm9yIHRoYXRcbiAgLy8gZ2F0ZSBoZXJlIHNvIFRhYi9yaWdodEFycm93IGNhbid0IGFjY2VwdCB3aGF0IGlzbid0IGRpc3BsYXllZC5cbiAgY29uc3QgaXNWaWV3aW5nVGVhbW1hdGUgPSB1c2VBcHBTdGF0ZShzID0+ICEhcy52aWV3aW5nQWdlbnRUYXNrSWQpXG5cbiAgLy8gQWNjZXNzIGtleWJpbmRpbmcgY29udGV4dCB0byBjaGVjayBmb3IgcGVuZGluZyBjaG9yZCBzZXF1ZW5jZXNcbiAgY29uc3Qga2V5YmluZGluZ0NvbnRleHQgPSB1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0KClcblxuICAvLyBTdGF0ZSBmb3IgaW5saW5lIGdob3N0IHRleHQgKGJhc2ggaGlzdG9yeSBjb21wbGV0aW9uIC0gYXN5bmMpXG4gIGNvbnN0IFtpbmxpbmVHaG9zdFRleHQsIHNldElubGluZUdob3N0VGV4dF0gPSB1c2VTdGF0ZTxcbiAgICBJbmxpbmVHaG9zdFRleHQgfCB1bmRlZmluZWRcbiAgPih1bmRlZmluZWQpXG5cbiAgLy8gU3luY2hyb25vdXMgZ2hvc3QgdGV4dCBmb3IgcHJvbXB0IG1vZGUgbWlkLWlucHV0IHNsYXNoIGNvbW1hbmRzLlxuICAvLyBDb21wdXRlZCBkdXJpbmcgcmVuZGVyIHZpYSB1c2VNZW1vIHRvIGVsaW1pbmF0ZSB0aGUgb25lLWZyYW1lIGZsaWNrZXJcbiAgLy8gdGhhdCBvY2N1cnMgd2hlbiB1c2luZyB1c2VTdGF0ZSArIHVzZUVmZmVjdCAoZWZmZWN0IHJ1bnMgYWZ0ZXIgcmVuZGVyKS5cbiAgY29uc3Qgc3luY1Byb21wdEdob3N0VGV4dCA9IHVzZU1lbW8oKCk6IElubGluZUdob3N0VGV4dCB8IHVuZGVmaW5lZCA9PiB7XG4gICAgaWYgKG1vZGUgIT09ICdwcm9tcHQnIHx8IHN1cHByZXNzU3VnZ2VzdGlvbnMpIHJldHVybiB1bmRlZmluZWRcbiAgICBjb25zdCBtaWRJbnB1dENvbW1hbmQgPSBmaW5kTWlkSW5wdXRTbGFzaENvbW1hbmQoaW5wdXQsIGN1cnNvck9mZnNldClcbiAgICBpZiAoIW1pZElucHV0Q29tbWFuZCkgcmV0dXJuIHVuZGVmaW5lZFxuICAgIGNvbnN0IG1hdGNoID0gZ2V0QmVzdENvbW1hbmRNYXRjaChtaWRJbnB1dENvbW1hbmQucGFydGlhbENvbW1hbmQsIGNvbW1hbmRzKVxuICAgIGlmICghbWF0Y2gpIHJldHVybiB1bmRlZmluZWRcbiAgICByZXR1cm4ge1xuICAgICAgdGV4dDogbWF0Y2guc3VmZml4LFxuICAgICAgZnVsbENvbW1hbmQ6IG1hdGNoLmZ1bGxDb21tYW5kLFxuICAgICAgaW5zZXJ0UG9zaXRpb246XG4gICAgICAgIG1pZElucHV0Q29tbWFuZC5zdGFydFBvcyArIDEgKyBtaWRJbnB1dENvbW1hbmQucGFydGlhbENvbW1hbmQubGVuZ3RoLFxuICAgIH1cbiAgfSwgW2lucHV0LCBjdXJzb3JPZmZzZXQsIG1vZGUsIGNvbW1hbmRzLCBzdXBwcmVzc1N1Z2dlc3Rpb25zXSlcblxuICAvLyBNZXJnZWQgZ2hvc3QgdGV4dDogcHJvbXB0IG1vZGUgdXNlcyBzeW5jaHJvbm91cyB1c2VNZW1vLCBiYXNoIG1vZGUgdXNlcyBhc3luYyB1c2VTdGF0ZVxuICBjb25zdCBlZmZlY3RpdmVHaG9zdFRleHQgPSBzdXBwcmVzc1N1Z2dlc3Rpb25zXG4gICAgPyB1bmRlZmluZWRcbiAgICA6IG1vZGUgPT09ICdwcm9tcHQnXG4gICAgICA/IHN5bmNQcm9tcHRHaG9zdFRleHRcbiAgICAgIDogaW5saW5lR2hvc3RUZXh0XG5cbiAgLy8gVXNlIGEgcmVmIGZvciBjdXJzb3JPZmZzZXQgdG8gYXZvaWQgcmUtdHJpZ2dlcmluZyBzdWdnZXN0aW9ucyBvbiBjdXJzb3IgbW92ZW1lbnQgYWxvbmVcbiAgLy8gV2Ugb25seSB3YW50IHRvIHJlLWZldGNoIHN1Z2dlc3Rpb25zIHdoZW4gdGhlIGFjdHVhbCBzZWFyY2ggdG9rZW4gY2hhbmdlc1xuICBjb25zdCBjdXJzb3JPZmZzZXRSZWYgPSB1c2VSZWYoY3Vyc29yT2Zmc2V0KVxuICBjdXJzb3JPZmZzZXRSZWYuY3VycmVudCA9IGN1cnNvck9mZnNldFxuXG4gIC8vIFRyYWNrIHRoZSBsYXRlc3Qgc2VhcmNoIHRva2VuIHRvIGRpc2NhcmQgc3RhbGUgcmVzdWx0cyBmcm9tIHNsb3cgYXN5bmMgb3BlcmF0aW9uc1xuICBjb25zdCBsYXRlc3RTZWFyY2hUb2tlblJlZiA9IHVzZVJlZjxzdHJpbmcgfCBudWxsPihudWxsKVxuICAvLyBUcmFjayBwcmV2aW91cyBpbnB1dCB0byBkZXRlY3QgYWN0dWFsIHRleHQgY2hhbmdlcyB2cy4gY2FsbGJhY2sgcmVjcmVhdGlvbnNcbiAgY29uc3QgcHJldklucHV0UmVmID0gdXNlUmVmKCcnKVxuICAvLyBUcmFjayB0aGUgbGF0ZXN0IHBhdGggdG9rZW4gdG8gZGlzY2FyZCBzdGFsZSByZXN1bHRzIGZyb20gcGF0aCBjb21wbGV0aW9uXG4gIGNvbnN0IGxhdGVzdFBhdGhUb2tlblJlZiA9IHVzZVJlZignJylcbiAgLy8gVHJhY2sgdGhlIGxhdGVzdCBiYXNoIGlucHV0IHRvIGRpc2NhcmQgc3RhbGUgcmVzdWx0cyBmcm9tIGhpc3RvcnkgY29tcGxldGlvblxuICBjb25zdCBsYXRlc3RCYXNoSW5wdXRSZWYgPSB1c2VSZWYoJycpXG4gIC8vIFRyYWNrIHRoZSBsYXRlc3Qgc2xhY2sgY2hhbm5lbCB0b2tlbiB0byBkaXNjYXJkIHN0YWxlIHJlc3VsdHMgZnJvbSBNQ1BcbiAgY29uc3QgbGF0ZXN0U2xhY2tUb2tlblJlZiA9IHVzZVJlZignJylcbiAgLy8gVHJhY2sgc3VnZ2VzdGlvbnMgdmlhIHJlZiB0byBhdm9pZCB1cGRhdGVTdWdnZXN0aW9ucyBiZWluZyByZWNyZWF0ZWQgb24gc2VsZWN0aW9uIGNoYW5nZXNcbiAgY29uc3Qgc3VnZ2VzdGlvbnNSZWYgPSB1c2VSZWYoc3VnZ2VzdGlvbnMpXG4gIHN1Z2dlc3Rpb25zUmVmLmN1cnJlbnQgPSBzdWdnZXN0aW9uc1xuICAvLyBUcmFjayB0aGUgaW5wdXQgdmFsdWUgd2hlbiBzdWdnZXN0aW9ucyB3ZXJlIG1hbnVhbGx5IGRpc21pc3NlZCB0byBwcmV2ZW50IHJlLXRyaWdnZXJpbmdcbiAgY29uc3QgZGlzbWlzc2VkRm9ySW5wdXRSZWYgPSB1c2VSZWY8c3RyaW5nIHwgbnVsbD4obnVsbClcblxuICAvLyBDbGVhciBhbGwgc3VnZ2VzdGlvbnNcbiAgY29uc3QgY2xlYXJTdWdnZXN0aW9ucyA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTdWdnZXN0aW9uc1N0YXRlKCgpID0+ICh7XG4gICAgICBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQsXG4gICAgICBzdWdnZXN0aW9uczogW10sXG4gICAgICBzZWxlY3RlZFN1Z2dlc3Rpb246IC0xLFxuICAgIH0pKVxuICAgIHNldFN1Z2dlc3Rpb25UeXBlKCdub25lJylcbiAgICBzZXRNYXhDb2x1bW5XaWR0aCh1bmRlZmluZWQpXG4gICAgc2V0SW5saW5lR2hvc3RUZXh0KHVuZGVmaW5lZClcbiAgfSwgW3NldFN1Z2dlc3Rpb25zU3RhdGVdKVxuXG4gIC8vIEV4cGVuc2l2ZSBhc3luYyBvcGVyYXRpb24gdG8gZmV0Y2ggZmlsZS9yZXNvdXJjZSBzdWdnZXN0aW9uc1xuICBjb25zdCBmZXRjaEZpbGVTdWdnZXN0aW9ucyA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jIChzZWFyY2hUb2tlbjogc3RyaW5nLCBpc0F0U3ltYm9sID0gZmFsc2UpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgIGxhdGVzdFNlYXJjaFRva2VuUmVmLmN1cnJlbnQgPSBzZWFyY2hUb2tlblxuICAgICAgY29uc3QgY29tYmluZWRJdGVtcyA9IGF3YWl0IGdlbmVyYXRlVW5pZmllZFN1Z2dlc3Rpb25zKFxuICAgICAgICBzZWFyY2hUb2tlbixcbiAgICAgICAgbWNwUmVzb3VyY2VzLFxuICAgICAgICBhZ2VudHMsXG4gICAgICAgIGlzQXRTeW1ib2wsXG4gICAgICApXG4gICAgICAvLyBEaXNjYXJkIHN0YWxlIHJlc3VsdHMgaWYgYSBuZXdlciBxdWVyeSB3YXMgaW5pdGlhdGVkIHdoaWxlIHdhaXRpbmdcbiAgICAgIGlmIChsYXRlc3RTZWFyY2hUb2tlblJlZi5jdXJyZW50ICE9PSBzZWFyY2hUb2tlbikge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICAgIGlmIChjb21iaW5lZEl0ZW1zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAvLyBJbmxpbmUgY2xlYXJTdWdnZXN0aW9ucyBsb2dpYyB0byBhdm9pZCBuZWVkaW5nIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zXG4gICAgICAgIHNldFN1Z2dlc3Rpb25zU3RhdGUoKCkgPT4gKHtcbiAgICAgICAgICBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQsXG4gICAgICAgICAgc3VnZ2VzdGlvbnM6IFtdLFxuICAgICAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbjogLTEsXG4gICAgICAgIH0pKVxuICAgICAgICBzZXRTdWdnZXN0aW9uVHlwZSgnbm9uZScpXG4gICAgICAgIHNldE1heENvbHVtbldpZHRoKHVuZGVmaW5lZClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBzZXRTdWdnZXN0aW9uc1N0YXRlKHByZXYgPT4gKHtcbiAgICAgICAgY29tbWFuZEFyZ3VtZW50SGludDogdW5kZWZpbmVkLFxuICAgICAgICBzdWdnZXN0aW9uczogY29tYmluZWRJdGVtcyxcbiAgICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBnZXRQcmVzZXJ2ZWRTZWxlY3Rpb24oXG4gICAgICAgICAgcHJldi5zdWdnZXN0aW9ucyxcbiAgICAgICAgICBwcmV2LnNlbGVjdGVkU3VnZ2VzdGlvbixcbiAgICAgICAgICBjb21iaW5lZEl0ZW1zLFxuICAgICAgICApLFxuICAgICAgfSkpXG4gICAgICBzZXRTdWdnZXN0aW9uVHlwZShjb21iaW5lZEl0ZW1zLmxlbmd0aCA+IDAgPyAnZmlsZScgOiAnbm9uZScpXG4gICAgICBzZXRNYXhDb2x1bW5XaWR0aCh1bmRlZmluZWQpIC8vIE5vIGZpeGVkIHdpZHRoIGZvciBmaWxlIHN1Z2dlc3Rpb25zXG4gICAgfSxcbiAgICBbXG4gICAgICBtY3BSZXNvdXJjZXMsXG4gICAgICBzZXRTdWdnZXN0aW9uc1N0YXRlLFxuICAgICAgc2V0U3VnZ2VzdGlvblR5cGUsXG4gICAgICBzZXRNYXhDb2x1bW5XaWR0aCxcbiAgICAgIGFnZW50cyxcbiAgICBdLFxuICApXG5cbiAgLy8gUHJlLXdhcm0gdGhlIGZpbGUgaW5kZXggb24gbW91bnQgc28gdGhlIGZpcnN0IEAtbWVudGlvbiBkb2Vzbid0IGJsb2NrLlxuICAvLyBUaGUgYnVpbGQgcnVucyBpbiBiYWNrZ3JvdW5kIHdpdGggfjRtcyBldmVudC1sb29wIHlpZWxkcywgc28gaXQgZG9lc24ndFxuICAvLyBkZWxheSBmaXJzdCByZW5kZXIg4oCUIGl0IGp1c3QgcmFjZXMgdGhlIHVzZXIncyBmaXJzdCBAIGtleXN0cm9rZS5cbiAgLy9cbiAgLy8gSWYgdGhlIHVzZXIgdHlwZXMgYmVmb3JlIHRoZSBidWlsZCBmaW5pc2hlcywgdGhleSBnZXQgcGFydGlhbCByZXN1bHRzXG4gIC8vIGZyb20gdGhlIHJlYWR5IGNodW5rczsgd2hlbiB0aGUgYnVpbGQgY29tcGxldGVzLCByZS1maXJlIHRoZSBsYXN0XG4gIC8vIHNlYXJjaCBzbyBwYXJ0aWFsIHVwZ3JhZGVzIHRvIGZ1bGwuIENsZWFycyB0aGUgdG9rZW4gcmVmIHNvIHRoZSBzYW1lXG4gIC8vIHF1ZXJ5IGlzbid0IGRpc2NhcmRlZCBhcyBzdGFsZS5cbiAgLy9cbiAgLy8gU2tpcHBlZCB1bmRlciBOT0RFX0VOVj10ZXN0OiBSRVBMLW1vdW50aW5nIHRlc3RzIHdvdWxkIHNwYXduIGdpdCBscy1maWxlc1xuICAvLyBhZ2FpbnN0IHRoZSByZWFsIENJIHdvcmtzcGFjZSAoMjcwaysgZmlsZXMgb24gV2luZG93cyBydW5uZXJzKSwgYW5kIHRoZVxuICAvLyBiYWNrZ3JvdW5kIGJ1aWxkIG91dGxpdmVzIHRoZSB0ZXN0IOKAlCBpdHMgc2V0SW1tZWRpYXRlIGNoYWluIGxlYWtzIGludG9cbiAgLy8gc3Vic2VxdWVudCB0ZXN0cyBpbiB0aGUgc2hhcmQuIFRoZSBzdWJzY3JpYmVyIHN0aWxsIHJlZ2lzdGVycyBzb1xuICAvLyBmaWxlU3VnZ2VzdGlvbnMgdGVzdHMgdGhhdCB0cmlnZ2VyIGEgcmVmcmVzaCBkaXJlY3RseSB3b3JrIGNvcnJlY3RseS5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoXCJwcm9kdWN0aW9uXCIgIT09ICd0ZXN0Jykge1xuICAgICAgc3RhcnRCYWNrZ3JvdW5kQ2FjaGVSZWZyZXNoKClcbiAgICB9XG4gICAgcmV0dXJuIG9uSW5kZXhCdWlsZENvbXBsZXRlKCgpID0+IHtcbiAgICAgIGNvbnN0IHRva2VuID0gbGF0ZXN0U2VhcmNoVG9rZW5SZWYuY3VycmVudFxuICAgICAgaWYgKHRva2VuICE9PSBudWxsKSB7XG4gICAgICAgIGxhdGVzdFNlYXJjaFRva2VuUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICAgIHZvaWQgZmV0Y2hGaWxlU3VnZ2VzdGlvbnModG9rZW4sIHRva2VuID09PSAnJylcbiAgICAgIH1cbiAgICB9KVxuICB9LCBbZmV0Y2hGaWxlU3VnZ2VzdGlvbnNdKVxuXG4gIC8vIERlYm91bmNlIHRoZSBmaWxlIGZldGNoIG9wZXJhdGlvbi4gNTBtcyBzaXRzIGp1c3QgYWJvdmUgbWFjT1MgZGVmYXVsdFxuICAvLyBrZXktcmVwZWF0ICh+MzNtcykgc28gaGVsZC1kZWxldGUvYmFja3NwYWNlIGNvYWxlc2NlcyBpbnRvIG9uZSBzZWFyY2hcbiAgLy8gaW5zdGVhZCBvZiBzdHV0dGVyaW5nIG9uIGVhY2ggcmVwZWF0ZWQga2V5LiBUaGUgc2VhcmNoIGl0c2VsZiBpcyB+OOKAkzE1bXNcbiAgLy8gb24gYSAyNzBrLWZpbGUgaW5kZXguXG4gIGNvbnN0IGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zID0gdXNlRGVib3VuY2VDYWxsYmFjayhcbiAgICBmZXRjaEZpbGVTdWdnZXN0aW9ucyxcbiAgICA1MCxcbiAgKVxuXG4gIGNvbnN0IGZldGNoU2xhY2tDaGFubmVscyA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jIChwYXJ0aWFsOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgIGxhdGVzdFNsYWNrVG9rZW5SZWYuY3VycmVudCA9IHBhcnRpYWxcbiAgICAgIGNvbnN0IGNoYW5uZWxzID0gYXdhaXQgZ2V0U2xhY2tDaGFubmVsU3VnZ2VzdGlvbnMoXG4gICAgICAgIHN0b3JlLmdldFN0YXRlKCkubWNwLmNsaWVudHMsXG4gICAgICAgIHBhcnRpYWwsXG4gICAgICApXG4gICAgICBpZiAobGF0ZXN0U2xhY2tUb2tlblJlZi5jdXJyZW50ICE9PSBwYXJ0aWFsKSByZXR1cm5cbiAgICAgIHNldFN1Z2dlc3Rpb25zU3RhdGUocHJldiA9PiAoe1xuICAgICAgICBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQsXG4gICAgICAgIHN1Z2dlc3Rpb25zOiBjaGFubmVscyxcbiAgICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBnZXRQcmVzZXJ2ZWRTZWxlY3Rpb24oXG4gICAgICAgICAgcHJldi5zdWdnZXN0aW9ucyxcbiAgICAgICAgICBwcmV2LnNlbGVjdGVkU3VnZ2VzdGlvbixcbiAgICAgICAgICBjaGFubmVscyxcbiAgICAgICAgKSxcbiAgICAgIH0pKVxuICAgICAgc2V0U3VnZ2VzdGlvblR5cGUoY2hhbm5lbHMubGVuZ3RoID4gMCA/ICdzbGFjay1jaGFubmVsJyA6ICdub25lJylcbiAgICAgIHNldE1heENvbHVtbldpZHRoKHVuZGVmaW5lZClcbiAgICB9LFxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZWFjdC1ob29rcy9leGhhdXN0aXZlLWRlcHMgLS0gc3RvcmUgaXMgYSBzdGFibGUgY29udGV4dCByZWZcbiAgICBbc2V0U3VnZ2VzdGlvbnNTdGF0ZV0sXG4gIClcblxuICAvLyBGaXJzdCBrZXlzdHJva2UgYWZ0ZXIgIyBuZWVkcyB0aGUgTUNQIHJvdW5kLXRyaXA7IHN1YnNlcXVlbnQga2V5c3Ryb2tlc1xuICAvLyB0aGF0IHNoYXJlIHRoZSBzYW1lIGZpcnN0LXdvcmQgc2VnbWVudCBoaXQgdGhlIGNhY2hlIHN5bmNocm9ub3VzbHkuXG4gIGNvbnN0IGRlYm91bmNlZEZldGNoU2xhY2tDaGFubmVscyA9IHVzZURlYm91bmNlQ2FsbGJhY2soXG4gICAgZmV0Y2hTbGFja0NoYW5uZWxzLFxuICAgIDE1MCxcbiAgKVxuXG4gIC8vIEhhbmRsZSBpbW1lZGlhdGUgc3VnZ2VzdGlvbiBsb2dpYyAoY2hlYXAgb3BlcmF0aW9ucylcbiAgLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlRXhoYXVzdGl2ZURlcGVuZGVuY2llczogc3RvcmUgaXMgYSBzdGFibGUgY29udGV4dCByZWYsIHJlYWQgaW1wZXJhdGl2ZWx5IGF0IGNhbGwtdGltZVxuICBjb25zdCB1cGRhdGVTdWdnZXN0aW9ucyA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jICh2YWx1ZTogc3RyaW5nLCBpbnB1dEN1cnNvck9mZnNldD86IG51bWJlcik6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgLy8gVXNlIHByb3ZpZGVkIGN1cnNvciBvZmZzZXQgb3IgZmFsbCBiYWNrIHRvIHJlZiAoYXZvaWRzIGRlcGVuZGVuY3kgb24gY3Vyc29yT2Zmc2V0KVxuICAgICAgY29uc3QgZWZmZWN0aXZlQ3Vyc29yT2Zmc2V0ID0gaW5wdXRDdXJzb3JPZmZzZXQgPz8gY3Vyc29yT2Zmc2V0UmVmLmN1cnJlbnRcbiAgICAgIGlmIChzdXBwcmVzc1N1Z2dlc3Rpb25zKSB7XG4gICAgICAgIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zLmNhbmNlbCgpXG4gICAgICAgIGNsZWFyU3VnZ2VzdGlvbnMoKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgZm9yIG1pZC1pbnB1dCBzbGFzaCBjb21tYW5kIChlLmcuLCBcImhlbHAgbWUgL2NvbVwiKVxuICAgICAgLy8gT25seSBpbiBwcm9tcHQgbW9kZSwgbm90IHdoZW4gaW5wdXQgc3RhcnRzIHdpdGggXCIvXCIgKGhhbmRsZWQgc2VwYXJhdGVseSlcbiAgICAgIC8vIE5vdGU6IGdob3N0IHRleHQgZm9yIHByb21wdCBtb2RlIGlzIGNvbXB1dGVkIHN5bmNocm9ub3VzbHkgdmlhIHN5bmNQcm9tcHRHaG9zdFRleHQgdXNlTWVtby5cbiAgICAgIC8vIFdlIG9ubHkgbmVlZCB0byBjbGVhciBkcm9wZG93biBzdWdnZXN0aW9ucyBoZXJlIHdoZW4gZ2hvc3QgdGV4dCBpcyBhY3RpdmUuXG4gICAgICBpZiAobW9kZSA9PT0gJ3Byb21wdCcpIHtcbiAgICAgICAgY29uc3QgbWlkSW5wdXRDb21tYW5kID0gZmluZE1pZElucHV0U2xhc2hDb21tYW5kKFxuICAgICAgICAgIHZhbHVlLFxuICAgICAgICAgIGVmZmVjdGl2ZUN1cnNvck9mZnNldCxcbiAgICAgICAgKVxuICAgICAgICBpZiAobWlkSW5wdXRDb21tYW5kKSB7XG4gICAgICAgICAgY29uc3QgbWF0Y2ggPSBnZXRCZXN0Q29tbWFuZE1hdGNoKFxuICAgICAgICAgICAgbWlkSW5wdXRDb21tYW5kLnBhcnRpYWxDb21tYW5kLFxuICAgICAgICAgICAgY29tbWFuZHMsXG4gICAgICAgICAgKVxuICAgICAgICAgIGlmIChtYXRjaCkge1xuICAgICAgICAgICAgLy8gQ2xlYXIgZHJvcGRvd24gc3VnZ2VzdGlvbnMgd2hlbiBzaG93aW5nIGdob3N0IHRleHRcbiAgICAgICAgICAgIHNldFN1Z2dlc3Rpb25zU3RhdGUoKCkgPT4gKHtcbiAgICAgICAgICAgICAgY29tbWFuZEFyZ3VtZW50SGludDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICBzdWdnZXN0aW9uczogW10sXG4gICAgICAgICAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbjogLTEsXG4gICAgICAgICAgICB9KSlcbiAgICAgICAgICAgIHNldFN1Z2dlc3Rpb25UeXBlKCdub25lJylcbiAgICAgICAgICAgIHNldE1heENvbHVtbldpZHRoKHVuZGVmaW5lZClcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBCYXNoIG1vZGU6IGNoZWNrIGZvciBoaXN0b3J5LWJhc2VkIGdob3N0IHRleHQgY29tcGxldGlvblxuICAgICAgaWYgKG1vZGUgPT09ICdiYXNoJyAmJiB2YWx1ZS50cmltKCkpIHtcbiAgICAgICAgbGF0ZXN0QmFzaElucHV0UmVmLmN1cnJlbnQgPSB2YWx1ZVxuICAgICAgICBjb25zdCBoaXN0b3J5TWF0Y2ggPSBhd2FpdCBnZXRTaGVsbEhpc3RvcnlDb21wbGV0aW9uKHZhbHVlKVxuICAgICAgICAvLyBEaXNjYXJkIHN0YWxlIHJlc3VsdHMgaWYgaW5wdXQgY2hhbmdlZCB3aGlsZSB3YWl0aW5nXG4gICAgICAgIGlmIChsYXRlc3RCYXNoSW5wdXRSZWYuY3VycmVudCAhPT0gdmFsdWUpIHtcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAoaGlzdG9yeU1hdGNoKSB7XG4gICAgICAgICAgc2V0SW5saW5lR2hvc3RUZXh0KHtcbiAgICAgICAgICAgIHRleHQ6IGhpc3RvcnlNYXRjaC5zdWZmaXgsXG4gICAgICAgICAgICBmdWxsQ29tbWFuZDogaGlzdG9yeU1hdGNoLmZ1bGxDb21tYW5kLFxuICAgICAgICAgICAgaW5zZXJ0UG9zaXRpb246IHZhbHVlLmxlbmd0aCxcbiAgICAgICAgICB9KVxuICAgICAgICAgIC8vIENsZWFyIGRyb3Bkb3duIHN1Z2dlc3Rpb25zIHdoZW4gc2hvd2luZyBnaG9zdCB0ZXh0XG4gICAgICAgICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZSgoKSA9PiAoe1xuICAgICAgICAgICAgY29tbWFuZEFyZ3VtZW50SGludDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgc3VnZ2VzdGlvbnM6IFtdLFxuICAgICAgICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiAtMSxcbiAgICAgICAgICB9KSlcbiAgICAgICAgICBzZXRTdWdnZXN0aW9uVHlwZSgnbm9uZScpXG4gICAgICAgICAgc2V0TWF4Q29sdW1uV2lkdGgodW5kZWZpbmVkKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIE5vIGhpc3RvcnkgbWF0Y2gsIGNsZWFyIGdob3N0IHRleHRcbiAgICAgICAgICBzZXRJbmxpbmVHaG9zdFRleHQodW5kZWZpbmVkKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGZvciBAIHRvIHRyaWdnZXIgdGVhbSBtZW1iZXIgLyBuYW1lZCBzdWJhZ2VudCBzdWdnZXN0aW9uc1xuICAgICAgLy8gTXVzdCBjaGVjayBiZWZvcmUgQCBmaWxlIHN5bWJvbCB0byBwcmV2ZW50IGNvbmZsaWN0XG4gICAgICAvLyBTa2lwIGluIGJhc2ggbW9kZSAtIEAgaGFzIG5vIHNwZWNpYWwgbWVhbmluZyBpbiBzaGVsbCBjb21tYW5kc1xuICAgICAgY29uc3QgYXRNYXRjaCA9XG4gICAgICAgIG1vZGUgIT09ICdiYXNoJ1xuICAgICAgICAgID8gdmFsdWUuc3Vic3RyaW5nKDAsIGVmZmVjdGl2ZUN1cnNvck9mZnNldCkubWF0Y2goLyhefFxccylAKFtcXHctXSopJC8pXG4gICAgICAgICAgOiBudWxsXG4gICAgICBpZiAoYXRNYXRjaCkge1xuICAgICAgICBjb25zdCBwYXJ0aWFsTmFtZSA9IChhdE1hdGNoWzJdID8/ICcnKS50b0xvd2VyQ2FzZSgpXG4gICAgICAgIC8vIEltcGVyYXRpdmUgcmVhZCDigJQgcmVhZGluZyBhdCBjYWxsLXRpbWUgZml4ZXMgc3RhbGVuZXNzIGZvclxuICAgICAgICAvLyB0ZWFtbWF0ZXMvc3ViYWdlbnRzIGFkZGVkIG1pZC1zZXNzaW9uLlxuICAgICAgICBjb25zdCBzdGF0ZSA9IHN0b3JlLmdldFN0YXRlKClcbiAgICAgICAgY29uc3QgbWVtYmVyczogU3VnZ2VzdGlvbkl0ZW1bXSA9IFtdXG4gICAgICAgIGNvbnN0IHNlZW4gPSBuZXcgU2V0PHN0cmluZz4oKVxuXG4gICAgICAgIGlmIChpc0FnZW50U3dhcm1zRW5hYmxlZCgpICYmIHN0YXRlLnRlYW1Db250ZXh0KSB7XG4gICAgICAgICAgZm9yIChjb25zdCB0IG9mIE9iamVjdC52YWx1ZXMoc3RhdGUudGVhbUNvbnRleHQudGVhbW1hdGVzID8/IHt9KSkge1xuICAgICAgICAgICAgaWYgKHQubmFtZSA9PT0gVEVBTV9MRUFEX05BTUUpIGNvbnRpbnVlXG4gICAgICAgICAgICBpZiAoIXQubmFtZS50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgocGFydGlhbE5hbWUpKSBjb250aW51ZVxuICAgICAgICAgICAgc2Vlbi5hZGQodC5uYW1lKVxuICAgICAgICAgICAgbWVtYmVycy5wdXNoKHtcbiAgICAgICAgICAgICAgaWQ6IGBkbS0ke3QubmFtZX1gLFxuICAgICAgICAgICAgICBkaXNwbGF5VGV4dDogYEAke3QubmFtZX1gLFxuICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogJ3NlbmQgbWVzc2FnZScsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGZvciAoY29uc3QgW25hbWUsIGFnZW50SWRdIG9mIHN0YXRlLmFnZW50TmFtZVJlZ2lzdHJ5KSB7XG4gICAgICAgICAgaWYgKHNlZW4uaGFzKG5hbWUpKSBjb250aW51ZVxuICAgICAgICAgIGlmICghbmFtZS50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgocGFydGlhbE5hbWUpKSBjb250aW51ZVxuICAgICAgICAgIGNvbnN0IHN0YXR1cyA9IHN0YXRlLnRhc2tzW2FnZW50SWRdPy5zdGF0dXNcbiAgICAgICAgICBtZW1iZXJzLnB1c2goe1xuICAgICAgICAgICAgaWQ6IGBkbS0ke25hbWV9YCxcbiAgICAgICAgICAgIGRpc3BsYXlUZXh0OiBgQCR7bmFtZX1gLFxuICAgICAgICAgICAgZGVzY3JpcHRpb246IHN0YXR1cyA/IGBzZW5kIG1lc3NhZ2UgwrcgJHtzdGF0dXN9YCA6ICdzZW5kIG1lc3NhZ2UnLFxuICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICBpZiAobWVtYmVycy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgZGVib3VuY2VkRmV0Y2hGaWxlU3VnZ2VzdGlvbnMuY2FuY2VsKClcbiAgICAgICAgICBzZXRTdWdnZXN0aW9uc1N0YXRlKHByZXYgPT4gKHtcbiAgICAgICAgICAgIGNvbW1hbmRBcmd1bWVudEhpbnQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIHN1Z2dlc3Rpb25zOiBtZW1iZXJzLFxuICAgICAgICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBnZXRQcmVzZXJ2ZWRTZWxlY3Rpb24oXG4gICAgICAgICAgICAgIHByZXYuc3VnZ2VzdGlvbnMsXG4gICAgICAgICAgICAgIHByZXYuc2VsZWN0ZWRTdWdnZXN0aW9uLFxuICAgICAgICAgICAgICBtZW1iZXJzLFxuICAgICAgICAgICAgKSxcbiAgICAgICAgICB9KSlcbiAgICAgICAgICBzZXRTdWdnZXN0aW9uVHlwZSgnYWdlbnQnKVxuICAgICAgICAgIHNldE1heENvbHVtbldpZHRoKHVuZGVmaW5lZClcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBDaGVjayBmb3IgIyB0byB0cmlnZ2VyIFNsYWNrIGNoYW5uZWwgc3VnZ2VzdGlvbnMgKHJlcXVpcmVzIFNsYWNrIE1DUCBzZXJ2ZXIpXG4gICAgICBpZiAobW9kZSA9PT0gJ3Byb21wdCcpIHtcbiAgICAgICAgY29uc3QgaGFzaE1hdGNoID0gdmFsdWVcbiAgICAgICAgICAuc3Vic3RyaW5nKDAsIGVmZmVjdGl2ZUN1cnNvck9mZnNldClcbiAgICAgICAgICAubWF0Y2goSEFTSF9DSEFOTkVMX1JFKVxuICAgICAgICBpZiAoaGFzaE1hdGNoICYmIGhhc1NsYWNrTWNwU2VydmVyKHN0b3JlLmdldFN0YXRlKCkubWNwLmNsaWVudHMpKSB7XG4gICAgICAgICAgZGVib3VuY2VkRmV0Y2hTbGFja0NoYW5uZWxzKGhhc2hNYXRjaFsyXSEpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH0gZWxzZSBpZiAoc3VnZ2VzdGlvblR5cGUgPT09ICdzbGFjay1jaGFubmVsJykge1xuICAgICAgICAgIGRlYm91bmNlZEZldGNoU2xhY2tDaGFubmVscy5jYW5jZWwoKVxuICAgICAgICAgIGNsZWFyU3VnZ2VzdGlvbnMoKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIENoZWNrIGZvciBAIHN5bWJvbCB0byB0cmlnZ2VyIGZpbGUgc3VnZ2VzdGlvbnMgKGluY2x1ZGluZyBxdW90ZWQgcGF0aHMpXG4gICAgICAvLyBJbmNsdWRlcyBjb2xvbiBmb3IgTUNQIHJlc291cmNlcyAoZS5nLiwgc2VydmVyOnJlc291cmNlL3BhdGgpXG4gICAgICBjb25zdCBoYXNBdFN5bWJvbCA9IHZhbHVlXG4gICAgICAgIC5zdWJzdHJpbmcoMCwgZWZmZWN0aXZlQ3Vyc29yT2Zmc2V0KVxuICAgICAgICAubWF0Y2goSEFTX0FUX1NZTUJPTF9SRSlcblxuICAgICAgLy8gRmlyc3QsIGNoZWNrIGZvciBzbGFzaCBjb21tYW5kIHN1Z2dlc3Rpb25zIChoaWdoZXIgcHJpb3JpdHkgdGhhbiBAIHN5bWJvbClcbiAgICAgIC8vIE9ubHkgc2hvdyBzbGFzaCBjb21tYW5kIHNlbGVjdG9yIGlmIGN1cnNvciBpcyBub3Qgb24gdGhlIFwiL1wiIGNoYXJhY3RlciBpdHNlbGZcbiAgICAgIC8vIEFsc28gZG9uJ3Qgc2hvdyBpZiBjdXJzb3IgaXMgYXQgZW5kIG9mIGxpbmUgd2l0aCB3aGl0ZXNwYWNlIGJlZm9yZSBpdFxuICAgICAgLy8gRG9uJ3Qgc2hvdyBzbGFzaCBjb21tYW5kcyBpbiBiYXNoIG1vZGVcbiAgICAgIGNvbnN0IGlzQXRFbmRXaXRoV2hpdGVzcGFjZSA9XG4gICAgICAgIGVmZmVjdGl2ZUN1cnNvck9mZnNldCA9PT0gdmFsdWUubGVuZ3RoICYmXG4gICAgICAgIGVmZmVjdGl2ZUN1cnNvck9mZnNldCA+IDAgJiZcbiAgICAgICAgdmFsdWUubGVuZ3RoID4gMCAmJlxuICAgICAgICB2YWx1ZVtlZmZlY3RpdmVDdXJzb3JPZmZzZXQgLSAxXSA9PT0gJyAnXG5cbiAgICAgIC8vIEhhbmRsZSBkaXJlY3RvcnkgY29tcGxldGlvbiBmb3IgY29tbWFuZHNcbiAgICAgIGlmIChcbiAgICAgICAgbW9kZSA9PT0gJ3Byb21wdCcgJiZcbiAgICAgICAgaXNDb21tYW5kSW5wdXQodmFsdWUpICYmXG4gICAgICAgIGVmZmVjdGl2ZUN1cnNvck9mZnNldCA+IDBcbiAgICAgICkge1xuICAgICAgICBjb25zdCBwYXJzZWRDb21tYW5kID0gZXh0cmFjdENvbW1hbmROYW1lQW5kQXJncyh2YWx1ZSlcblxuICAgICAgICBpZiAoXG4gICAgICAgICAgcGFyc2VkQ29tbWFuZCAmJlxuICAgICAgICAgIHBhcnNlZENvbW1hbmQuY29tbWFuZE5hbWUgPT09ICdhZGQtZGlyJyAmJlxuICAgICAgICAgIHBhcnNlZENvbW1hbmQuYXJnc1xuICAgICAgICApIHtcbiAgICAgICAgICBjb25zdCB7IGFyZ3MgfSA9IHBhcnNlZENvbW1hbmRcblxuICAgICAgICAgIC8vIENsZWFyIHN1Z2dlc3Rpb25zIGlmIGFyZ3MgZW5kIHdpdGggd2hpdGVzcGFjZSAodXNlciBpcyBkb25lIHdpdGggcGF0aClcbiAgICAgICAgICBpZiAoYXJncy5tYXRjaCgvXFxzKyQvKSkge1xuICAgICAgICAgICAgZGVib3VuY2VkRmV0Y2hGaWxlU3VnZ2VzdGlvbnMuY2FuY2VsKClcbiAgICAgICAgICAgIGNsZWFyU3VnZ2VzdGlvbnMoKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgZGlyU3VnZ2VzdGlvbnMgPSBhd2FpdCBnZXREaXJlY3RvcnlDb21wbGV0aW9ucyhhcmdzKVxuICAgICAgICAgIGlmIChkaXJTdWdnZXN0aW9ucy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBzZXRTdWdnZXN0aW9uc1N0YXRlKHByZXYgPT4gKHtcbiAgICAgICAgICAgICAgc3VnZ2VzdGlvbnM6IGRpclN1Z2dlc3Rpb25zLFxuICAgICAgICAgICAgICBzZWxlY3RlZFN1Z2dlc3Rpb246IGdldFByZXNlcnZlZFNlbGVjdGlvbihcbiAgICAgICAgICAgICAgICBwcmV2LnN1Z2dlc3Rpb25zLFxuICAgICAgICAgICAgICAgIHByZXYuc2VsZWN0ZWRTdWdnZXN0aW9uLFxuICAgICAgICAgICAgICAgIGRpclN1Z2dlc3Rpb25zLFxuICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQsXG4gICAgICAgICAgICB9KSlcbiAgICAgICAgICAgIHNldFN1Z2dlc3Rpb25UeXBlKCdkaXJlY3RvcnknKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gTm8gc3VnZ2VzdGlvbnMgZm91bmQgLSBjbGVhciBhbmQgcmV0dXJuXG4gICAgICAgICAgZGVib3VuY2VkRmV0Y2hGaWxlU3VnZ2VzdGlvbnMuY2FuY2VsKClcbiAgICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEhhbmRsZSBjdXN0b20gdGl0bGUgY29tcGxldGlvbiBmb3IgL3Jlc3VtZSBjb21tYW5kXG4gICAgICAgIGlmIChcbiAgICAgICAgICBwYXJzZWRDb21tYW5kICYmXG4gICAgICAgICAgcGFyc2VkQ29tbWFuZC5jb21tYW5kTmFtZSA9PT0gJ3Jlc3VtZScgJiZcbiAgICAgICAgICBwYXJzZWRDb21tYW5kLmFyZ3MgIT09IHVuZGVmaW5lZCAmJlxuICAgICAgICAgIHZhbHVlLmluY2x1ZGVzKCcgJylcbiAgICAgICAgKSB7XG4gICAgICAgICAgY29uc3QgeyBhcmdzIH0gPSBwYXJzZWRDb21tYW5kXG5cbiAgICAgICAgICAvLyBHZXQgY3VzdG9tIHRpdGxlIHN1Z2dlc3Rpb25zIHVzaW5nIHBhcnRpYWwgbWF0Y2hcbiAgICAgICAgICBjb25zdCBtYXRjaGVzID0gYXdhaXQgc2VhcmNoU2Vzc2lvbnNCeUN1c3RvbVRpdGxlKGFyZ3MsIHtcbiAgICAgICAgICAgIGxpbWl0OiAxMCxcbiAgICAgICAgICB9KVxuXG4gICAgICAgICAgY29uc3Qgc3VnZ2VzdGlvbnMgPSBtYXRjaGVzLm1hcChsb2cgPT4ge1xuICAgICAgICAgICAgY29uc3Qgc2Vzc2lvbklkID0gZ2V0U2Vzc2lvbklkRnJvbUxvZyhsb2cpXG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICBpZDogYHJlc3VtZS10aXRsZS0ke3Nlc3Npb25JZH1gLFxuICAgICAgICAgICAgICBkaXNwbGF5VGV4dDogbG9nLmN1c3RvbVRpdGxlISxcbiAgICAgICAgICAgICAgZGVzY3JpcHRpb246IGZvcm1hdExvZ01ldGFkYXRhKGxvZyksXG4gICAgICAgICAgICAgIG1ldGFkYXRhOiB7IHNlc3Npb25JZCB9LFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pXG5cbiAgICAgICAgICBpZiAoc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgICAgIHN1Z2dlc3Rpb25zLFxuICAgICAgICAgICAgICBzZWxlY3RlZFN1Z2dlc3Rpb246IGdldFByZXNlcnZlZFNlbGVjdGlvbihcbiAgICAgICAgICAgICAgICBwcmV2LnN1Z2dlc3Rpb25zLFxuICAgICAgICAgICAgICAgIHByZXYuc2VsZWN0ZWRTdWdnZXN0aW9uLFxuICAgICAgICAgICAgICAgIHN1Z2dlc3Rpb25zLFxuICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQsXG4gICAgICAgICAgICB9KSlcbiAgICAgICAgICAgIHNldFN1Z2dlc3Rpb25UeXBlKCdjdXN0b20tdGl0bGUnKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gTm8gc3VnZ2VzdGlvbnMgZm91bmQgLSBjbGVhciBhbmQgcmV0dXJuXG4gICAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gRGV0ZXJtaW5lIHdoZXRoZXIgdG8gZGlzcGxheSB0aGUgYXJndW1lbnQgaGludCBhbmQgY29tbWFuZCBzdWdnZXN0aW9ucy5cbiAgICAgIGlmIChcbiAgICAgICAgbW9kZSA9PT0gJ3Byb21wdCcgJiZcbiAgICAgICAgaXNDb21tYW5kSW5wdXQodmFsdWUpICYmXG4gICAgICAgIGVmZmVjdGl2ZUN1cnNvck9mZnNldCA+IDAgJiZcbiAgICAgICAgIWhhc0NvbW1hbmRXaXRoQXJndW1lbnRzKGlzQXRFbmRXaXRoV2hpdGVzcGFjZSwgdmFsdWUpXG4gICAgICApIHtcbiAgICAgICAgbGV0IGNvbW1hbmRBcmd1bWVudEhpbnQ6IHN0cmluZyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZFxuICAgICAgICBpZiAodmFsdWUubGVuZ3RoID4gMSkge1xuICAgICAgICAgIC8vIFdlIGhhdmUgYSBwYXJ0aWFsIG9yIGNvbXBsZXRlIGNvbW1hbmQgd2l0aG91dCBhcmd1bWVudHNcbiAgICAgICAgICAvLyBDaGVjayBpZiBpdCBtYXRjaGVzIGEgY29tbWFuZCBleGFjdGx5IGFuZCBoYXMgYW4gYXJndW1lbnQgaGludFxuXG4gICAgICAgICAgLy8gRXh0cmFjdCBjb21tYW5kIG5hbWU6IGV2ZXJ5dGhpbmcgYWZ0ZXIgLyB1bnRpbCB0aGUgZmlyc3Qgc3BhY2UgKG9yIGVuZClcbiAgICAgICAgICBjb25zdCBzcGFjZUluZGV4ID0gdmFsdWUuaW5kZXhPZignICcpXG4gICAgICAgICAgY29uc3QgY29tbWFuZE5hbWUgPVxuICAgICAgICAgICAgc3BhY2VJbmRleCA9PT0gLTEgPyB2YWx1ZS5zbGljZSgxKSA6IHZhbHVlLnNsaWNlKDEsIHNwYWNlSW5kZXgpXG5cbiAgICAgICAgICAvLyBDaGVjayBpZiB0aGVyZSBhcmUgcmVhbCBhcmd1bWVudHMgKG5vbi13aGl0ZXNwYWNlIGFmdGVyIHRoZSBjb21tYW5kKVxuICAgICAgICAgIGNvbnN0IGhhc1JlYWxBcmd1bWVudHMgPVxuICAgICAgICAgICAgc3BhY2VJbmRleCAhPT0gLTEgJiYgdmFsdWUuc2xpY2Uoc3BhY2VJbmRleCArIDEpLnRyaW0oKS5sZW5ndGggPiAwXG5cbiAgICAgICAgICAvLyBDaGVjayBpZiBpbnB1dCBpcyBleGFjdGx5IFwiY29tbWFuZCArIHNpbmdsZSBzcGFjZVwiIChyZWFkeSBmb3IgYXJndW1lbnRzKVxuICAgICAgICAgIGNvbnN0IGhhc0V4YWN0bHlPbmVUcmFpbGluZ1NwYWNlID1cbiAgICAgICAgICAgIHNwYWNlSW5kZXggIT09IC0xICYmIHZhbHVlLmxlbmd0aCA9PT0gc3BhY2VJbmRleCArIDFcblxuICAgICAgICAgIC8vIElmIGlucHV0IGhhcyBhIHNwYWNlIGFmdGVyIHRoZSBjb21tYW5kLCBkb24ndCBzaG93IHN1Z2dlc3Rpb25zXG4gICAgICAgICAgLy8gVGhpcyBwcmV2ZW50cyBFbnRlciBmcm9tIHNlbGVjdGluZyBhIGRpZmZlcmVudCBjb21tYW5kIGFmdGVyIFRhYiBjb21wbGV0aW9uXG4gICAgICAgICAgaWYgKHNwYWNlSW5kZXggIT09IC0xKSB7XG4gICAgICAgICAgICBjb25zdCBleGFjdE1hdGNoID0gY29tbWFuZHMuZmluZChcbiAgICAgICAgICAgICAgY21kID0+IGdldENvbW1hbmROYW1lKGNtZCkgPT09IGNvbW1hbmROYW1lLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgaWYgKGV4YWN0TWF0Y2ggfHwgaGFzUmVhbEFyZ3VtZW50cykge1xuICAgICAgICAgICAgICAvLyBQcmlvcml0eSAxOiBTdGF0aWMgYXJndW1lbnRIaW50IChvbmx5IG9uIGZpcnN0IHRyYWlsaW5nIHNwYWNlIGZvciBiYWNrd2FyZHMgY29tcGF0KVxuICAgICAgICAgICAgICBpZiAoZXhhY3RNYXRjaD8uYXJndW1lbnRIaW50ICYmIGhhc0V4YWN0bHlPbmVUcmFpbGluZ1NwYWNlKSB7XG4gICAgICAgICAgICAgICAgY29tbWFuZEFyZ3VtZW50SGludCA9IGV4YWN0TWF0Y2guYXJndW1lbnRIaW50XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgLy8gUHJpb3JpdHkgMjogUHJvZ3Jlc3NpdmUgaGludCBmcm9tIGFyZ05hbWVzIChzaG93IHdoZW4gdHJhaWxpbmcgc3BhY2UpXG4gICAgICAgICAgICAgIGVsc2UgaWYgKFxuICAgICAgICAgICAgICAgIGV4YWN0TWF0Y2g/LnR5cGUgPT09ICdwcm9tcHQnICYmXG4gICAgICAgICAgICAgICAgZXhhY3RNYXRjaC5hcmdOYW1lcz8ubGVuZ3RoICYmXG4gICAgICAgICAgICAgICAgdmFsdWUuZW5kc1dpdGgoJyAnKVxuICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICBjb25zdCBhcmdzVGV4dCA9IHZhbHVlLnNsaWNlKHNwYWNlSW5kZXggKyAxKVxuICAgICAgICAgICAgICAgIGNvbnN0IHR5cGVkQXJncyA9IHBhcnNlQXJndW1lbnRzKGFyZ3NUZXh0KVxuICAgICAgICAgICAgICAgIGNvbW1hbmRBcmd1bWVudEhpbnQgPSBnZW5lcmF0ZVByb2dyZXNzaXZlQXJndW1lbnRIaW50KFxuICAgICAgICAgICAgICAgICAgZXhhY3RNYXRjaC5hcmdOYW1lcyxcbiAgICAgICAgICAgICAgICAgIHR5cGVkQXJncyxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZSgoKSA9PiAoe1xuICAgICAgICAgICAgICAgIGNvbW1hbmRBcmd1bWVudEhpbnQsXG4gICAgICAgICAgICAgICAgc3VnZ2VzdGlvbnM6IFtdLFxuICAgICAgICAgICAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbjogLTEsXG4gICAgICAgICAgICAgIH0pKVxuICAgICAgICAgICAgICBzZXRTdWdnZXN0aW9uVHlwZSgnbm9uZScpXG4gICAgICAgICAgICAgIHNldE1heENvbHVtbldpZHRoKHVuZGVmaW5lZClcbiAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gTm90ZTogYXJndW1lbnQgaGludCBpcyBvbmx5IHNob3duIHdoZW4gdGhlcmUncyBleGFjdGx5IG9uZSB0cmFpbGluZyBzcGFjZVxuICAgICAgICAgIC8vIChzZXQgYWJvdmUgd2hlbiBoYXNFeGFjdGx5T25lVHJhaWxpbmdTcGFjZSBpcyB0cnVlKVxuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgY29tbWFuZEl0ZW1zID0gZ2VuZXJhdGVDb21tYW5kU3VnZ2VzdGlvbnModmFsdWUsIGNvbW1hbmRzKVxuICAgICAgICBzZXRTdWdnZXN0aW9uc1N0YXRlKCgpID0+ICh7XG4gICAgICAgICAgY29tbWFuZEFyZ3VtZW50SGludCxcbiAgICAgICAgICBzdWdnZXN0aW9uczogY29tbWFuZEl0ZW1zLFxuICAgICAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbjogY29tbWFuZEl0ZW1zLmxlbmd0aCA+IDAgPyAwIDogLTEsXG4gICAgICAgIH0pKVxuICAgICAgICBzZXRTdWdnZXN0aW9uVHlwZShjb21tYW5kSXRlbXMubGVuZ3RoID4gMCA/ICdjb21tYW5kJyA6ICdub25lJylcblxuICAgICAgICAvLyBVc2Ugc3RhYmxlIHdpZHRoIGZyb20gYWxsIGNvbW1hbmRzIChwcmV2ZW50cyBsYXlvdXQgc2hpZnQgd2hlbiBmaWx0ZXJpbmcpXG4gICAgICAgIGlmIChjb21tYW5kSXRlbXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgIHNldE1heENvbHVtbldpZHRoKGFsbENvbW1hbmRzTWF4V2lkdGgpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGlmIChzdWdnZXN0aW9uVHlwZSA9PT0gJ2NvbW1hbmQnKSB7XG4gICAgICAgIC8vIElmIHdlIGhhZCBjb21tYW5kIHN1Z2dlc3Rpb25zIGJ1dCB0aGUgaW5wdXQgbm8gbG9uZ2VyIHN0YXJ0cyB3aXRoICcvJ1xuICAgICAgICAvLyB3ZSBuZWVkIHRvIGNsZWFyIHRoZSBzdWdnZXN0aW9ucy4gSG93ZXZlciwgd2Ugc2hvdWxkIG5vdCByZXR1cm5cbiAgICAgICAgLy8gYmVjYXVzZSB0aGVyZSBtYXkgYmUgcmVsZXZhbnQgQCBzeW1ib2wgYW5kIGZpbGUgc3VnZ2VzdGlvbnMuXG4gICAgICAgIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zLmNhbmNlbCgpXG4gICAgICAgIGNsZWFyU3VnZ2VzdGlvbnMoKVxuICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgaXNDb21tYW5kSW5wdXQodmFsdWUpICYmXG4gICAgICAgIGhhc0NvbW1hbmRXaXRoQXJndW1lbnRzKGlzQXRFbmRXaXRoV2hpdGVzcGFjZSwgdmFsdWUpXG4gICAgICApIHtcbiAgICAgICAgLy8gSWYgd2UgaGF2ZSBhIGNvbW1hbmQgd2l0aCBhcmd1bWVudHMgKG5vIHRyYWlsaW5nIHNwYWNlKSwgY2xlYXIgYW55IHN0YWxlIGhpbnRcbiAgICAgICAgLy8gVGhpcyBwcmV2ZW50cyB0aGUgaGludCBmcm9tIGZsYXNoaW5nIHdoZW4gdHJhbnNpdGlvbmluZyBiZXR3ZWVuIHN0YXRlc1xuICAgICAgICBzZXRTdWdnZXN0aW9uc1N0YXRlKHByZXYgPT5cbiAgICAgICAgICBwcmV2LmNvbW1hbmRBcmd1bWVudEhpbnRcbiAgICAgICAgICAgID8geyAuLi5wcmV2LCBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQgfVxuICAgICAgICAgICAgOiBwcmV2LFxuICAgICAgICApXG4gICAgICB9XG5cbiAgICAgIGlmIChzdWdnZXN0aW9uVHlwZSA9PT0gJ2N1c3RvbS10aXRsZScpIHtcbiAgICAgICAgLy8gSWYgd2UgaGFkIGN1c3RvbS10aXRsZSBzdWdnZXN0aW9ucyBidXQgdGhlIGlucHV0IGlzIG5vIGxvbmdlciAvcmVzdW1lXG4gICAgICAgIC8vIHdlIG5lZWQgdG8gY2xlYXIgdGhlIHN1Z2dlc3Rpb25zLlxuICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgIH1cblxuICAgICAgaWYgKFxuICAgICAgICBzdWdnZXN0aW9uVHlwZSA9PT0gJ2FnZW50JyAmJlxuICAgICAgICBzdWdnZXN0aW9uc1JlZi5jdXJyZW50LnNvbWUoKHM6IFN1Z2dlc3Rpb25JdGVtKSA9PlxuICAgICAgICAgIHMuaWQ/LnN0YXJ0c1dpdGgoJ2RtLScpLFxuICAgICAgICApXG4gICAgICApIHtcbiAgICAgICAgLy8gSWYgd2UgaGFkIHRlYW0gbWVtYmVyIHN1Z2dlc3Rpb25zIGJ1dCB0aGUgaW5wdXQgbm8gbG9uZ2VyIGhhcyBAXG4gICAgICAgIC8vIHdlIG5lZWQgdG8gY2xlYXIgdGhlIHN1Z2dlc3Rpb25zLlxuICAgICAgICBjb25zdCBoYXNBdCA9IHZhbHVlXG4gICAgICAgICAgLnN1YnN0cmluZygwLCBlZmZlY3RpdmVDdXJzb3JPZmZzZXQpXG4gICAgICAgICAgLm1hdGNoKC8oXnxcXHMpQChbXFx3LV0qKSQvKVxuICAgICAgICBpZiAoIWhhc0F0KSB7XG4gICAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgZm9yIEAgc3ltYm9sIHRvIHRyaWdnZXIgZmlsZSBhbmQgTUNQIHJlc291cmNlIHN1Z2dlc3Rpb25zXG4gICAgICAvLyBTa2lwIEAgYXV0b2NvbXBsZXRlIGluIGJhc2ggbW9kZSAtIEAgaGFzIG5vIHNwZWNpYWwgbWVhbmluZyBpbiBzaGVsbCBjb21tYW5kc1xuICAgICAgaWYgKGhhc0F0U3ltYm9sICYmIG1vZGUgIT09ICdiYXNoJykge1xuICAgICAgICAvLyBHZXQgdGhlIEAgdG9rZW4gKGluY2x1ZGluZyB0aGUgQCBzeW1ib2wpXG4gICAgICAgIGNvbnN0IGNvbXBsZXRpb25Ub2tlbiA9IGV4dHJhY3RDb21wbGV0aW9uVG9rZW4oXG4gICAgICAgICAgdmFsdWUsXG4gICAgICAgICAgZWZmZWN0aXZlQ3Vyc29yT2Zmc2V0LFxuICAgICAgICAgIHRydWUsXG4gICAgICAgIClcbiAgICAgICAgaWYgKGNvbXBsZXRpb25Ub2tlbiAmJiBjb21wbGV0aW9uVG9rZW4udG9rZW4uc3RhcnRzV2l0aCgnQCcpKSB7XG4gICAgICAgICAgY29uc3Qgc2VhcmNoVG9rZW4gPSBleHRyYWN0U2VhcmNoVG9rZW4oY29tcGxldGlvblRva2VuKVxuXG4gICAgICAgICAgLy8gSWYgdGhlIHRva2VuIGFmdGVyIEAgaXMgcGF0aC1saWtlLCB1c2UgcGF0aCBjb21wbGV0aW9uIGluc3RlYWQgb2YgZnV6enkgc2VhcmNoXG4gICAgICAgICAgLy8gVGhpcyBoYW5kbGVzIGNhc2VzIGxpa2UgQH4vcGF0aCwgQC4vcGF0aCwgQC9wYXRoIGZvciBkaXJlY3RvcnkgdHJhdmVyc2FsXG4gICAgICAgICAgaWYgKGlzUGF0aExpa2VUb2tlbihzZWFyY2hUb2tlbikpIHtcbiAgICAgICAgICAgIGxhdGVzdFBhdGhUb2tlblJlZi5jdXJyZW50ID0gc2VhcmNoVG9rZW5cbiAgICAgICAgICAgIGNvbnN0IHBhdGhTdWdnZXN0aW9ucyA9IGF3YWl0IGdldFBhdGhDb21wbGV0aW9ucyhzZWFyY2hUb2tlbiwge1xuICAgICAgICAgICAgICBtYXhSZXN1bHRzOiAxMCxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAvLyBEaXNjYXJkIHN0YWxlIHJlc3VsdHMgaWYgYSBuZXdlciBxdWVyeSB3YXMgaW5pdGlhdGVkIHdoaWxlIHdhaXRpbmdcbiAgICAgICAgICAgIGlmIChsYXRlc3RQYXRoVG9rZW5SZWYuY3VycmVudCAhPT0gc2VhcmNoVG9rZW4pIHtcbiAgICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpZiAocGF0aFN1Z2dlc3Rpb25zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgICAgICAgc3VnZ2VzdGlvbnM6IHBhdGhTdWdnZXN0aW9ucyxcbiAgICAgICAgICAgICAgICBzZWxlY3RlZFN1Z2dlc3Rpb246IGdldFByZXNlcnZlZFNlbGVjdGlvbihcbiAgICAgICAgICAgICAgICAgIHByZXYuc3VnZ2VzdGlvbnMsXG4gICAgICAgICAgICAgICAgICBwcmV2LnNlbGVjdGVkU3VnZ2VzdGlvbixcbiAgICAgICAgICAgICAgICAgIHBhdGhTdWdnZXN0aW9ucyxcbiAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgIGNvbW1hbmRBcmd1bWVudEhpbnQ6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgfSkpXG4gICAgICAgICAgICAgIHNldFN1Z2dlc3Rpb25UeXBlKCdkaXJlY3RvcnknKVxuICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBTa2lwIGlmIHdlIGFscmVhZHkgZmV0Y2hlZCBmb3IgdGhpcyBleGFjdCB0b2tlbiAocHJldmVudHMgbG9vcCBmcm9tXG4gICAgICAgICAgLy8gc3VnZ2VzdGlvbnMgZGVwZW5kZW5jeSBjYXVzaW5nIHVwZGF0ZVN1Z2dlc3Rpb25zIHRvIGJlIHJlY3JlYXRlZClcbiAgICAgICAgICBpZiAobGF0ZXN0U2VhcmNoVG9rZW5SZWYuY3VycmVudCA9PT0gc2VhcmNoVG9rZW4pIHtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH1cbiAgICAgICAgICB2b2lkIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zKHNlYXJjaFRva2VuLCB0cnVlKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIElmIHdlIGhhdmUgYWN0aXZlIGZpbGUgc3VnZ2VzdGlvbnMgb3IgdGhlIGlucHV0IGNoYW5nZWQsIGNoZWNrIGZvciBmaWxlIHN1Z2dlc3Rpb25zXG4gICAgICBpZiAoc3VnZ2VzdGlvblR5cGUgPT09ICdmaWxlJykge1xuICAgICAgICBjb25zdCBjb21wbGV0aW9uVG9rZW4gPSBleHRyYWN0Q29tcGxldGlvblRva2VuKFxuICAgICAgICAgIHZhbHVlLFxuICAgICAgICAgIGVmZmVjdGl2ZUN1cnNvck9mZnNldCxcbiAgICAgICAgICB0cnVlLFxuICAgICAgICApXG4gICAgICAgIGlmIChjb21wbGV0aW9uVG9rZW4pIHtcbiAgICAgICAgICBjb25zdCBzZWFyY2hUb2tlbiA9IGV4dHJhY3RTZWFyY2hUb2tlbihjb21wbGV0aW9uVG9rZW4pXG4gICAgICAgICAgLy8gU2tpcCBpZiB3ZSBhbHJlYWR5IGZldGNoZWQgZm9yIHRoaXMgZXhhY3QgdG9rZW5cbiAgICAgICAgICBpZiAobGF0ZXN0U2VhcmNoVG9rZW5SZWYuY3VycmVudCA9PT0gc2VhcmNoVG9rZW4pIHtcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH1cbiAgICAgICAgICB2b2lkIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zKHNlYXJjaFRva2VuLCBmYWxzZSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBJZiB3ZSBoYWQgZmlsZSBzdWdnZXN0aW9ucyBidXQgbm93IHRoZXJlJ3Mgbm8gY29tcGxldGlvbiB0b2tlblxuICAgICAgICAgIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zLmNhbmNlbCgpXG4gICAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gQ2xlYXIgc2hlbGwgc3VnZ2VzdGlvbnMgaWYgbm90IGluIGJhc2ggbW9kZSBPUiBpZiBpbnB1dCBoYXMgY2hhbmdlZFxuICAgICAgaWYgKHN1Z2dlc3Rpb25UeXBlID09PSAnc2hlbGwnKSB7XG4gICAgICAgIGNvbnN0IGlucHV0U25hcHNob3QgPSAoXG4gICAgICAgICAgc3VnZ2VzdGlvbnNSZWYuY3VycmVudFswXT8ubWV0YWRhdGEgYXMgeyBpbnB1dFNuYXBzaG90Pzogc3RyaW5nIH1cbiAgICAgICAgKT8uaW5wdXRTbmFwc2hvdFxuXG4gICAgICAgIGlmIChtb2RlICE9PSAnYmFzaCcgfHwgdmFsdWUgIT09IGlucHV0U25hcHNob3QpIHtcbiAgICAgICAgICBkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucy5jYW5jZWwoKVxuICAgICAgICAgIGNsZWFyU3VnZ2VzdGlvbnMoKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSxcbiAgICBbXG4gICAgICBzdWdnZXN0aW9uVHlwZSxcbiAgICAgIGNvbW1hbmRzLFxuICAgICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZSxcbiAgICAgIGNsZWFyU3VnZ2VzdGlvbnMsXG4gICAgICBkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucyxcbiAgICAgIGRlYm91bmNlZEZldGNoU2xhY2tDaGFubmVscyxcbiAgICAgIG1vZGUsXG4gICAgICBzdXBwcmVzc1N1Z2dlc3Rpb25zLFxuICAgICAgLy8gTm90ZTogdXNpbmcgc3VnZ2VzdGlvbnNSZWYgaW5zdGVhZCBvZiBzdWdnZXN0aW9ucyB0byBhdm9pZCByZWNyZWF0aW5nXG4gICAgICAvLyB0aGlzIGNhbGxiYWNrIHdoZW4gb25seSBzZWxlY3RlZFN1Z2dlc3Rpb24gY2hhbmdlcyAobm90IHRoZSBzdWdnZXN0aW9ucyBsaXN0KVxuICAgICAgYWxsQ29tbWFuZHNNYXhXaWR0aCxcbiAgICBdLFxuICApXG5cbiAgLy8gVXBkYXRlIHN1Z2dlc3Rpb25zIHdoZW4gaW5wdXQgY2hhbmdlc1xuICAvLyBOb3RlOiBXZSBpbnRlbnRpb25hbGx5IGRvbid0IGRlcGVuZCBvbiBjdXJzb3JPZmZzZXQgaGVyZSAtIGN1cnNvciBtb3ZlbWVudCBhbG9uZVxuICAvLyBzaG91bGRuJ3QgcmUtdHJpZ2dlciBzdWdnZXN0aW9ucy4gVGhlIGN1cnNvck9mZnNldFJlZiBpcyB1c2VkIHRvIGdldCB0aGUgY3VycmVudFxuICAvLyBwb3NpdGlvbiB3aGVuIG5lZWRlZCB3aXRob3V0IGNhdXNpbmcgcmUtcmVuZGVycy5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICAvLyBJZiBzdWdnZXN0aW9ucyB3ZXJlIGRpc21pc3NlZCBmb3IgdGhpcyBleGFjdCBpbnB1dCwgZG9uJ3QgcmUtdHJpZ2dlclxuICAgIGlmIChkaXNtaXNzZWRGb3JJbnB1dFJlZi5jdXJyZW50ID09PSBpbnB1dCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIC8vIFdoZW4gdGhlIGFjdHVhbCBpbnB1dCB0ZXh0IGNoYW5nZXMgKG5vdCBqdXN0IHVwZGF0ZVN1Z2dlc3Rpb25zIGJlaW5nIHJlY3JlYXRlZCksXG4gICAgLy8gcmVzZXQgdGhlIHNlYXJjaCB0b2tlbiByZWYgc28gdGhlIHNhbWUgcXVlcnkgY2FuIGJlIHJlLWZldGNoZWQuXG4gICAgLy8gVGhpcyBmaXhlczogdHlwZSBAcmVhZG1lLm1kLCBjbGVhciwgcmV0eXBlIEByZWFkbWUubWQg4oaSIG5vIHN1Z2dlc3Rpb25zLlxuICAgIGlmIChwcmV2SW5wdXRSZWYuY3VycmVudCAhPT0gaW5wdXQpIHtcbiAgICAgIHByZXZJbnB1dFJlZi5jdXJyZW50ID0gaW5wdXRcbiAgICAgIGxhdGVzdFNlYXJjaFRva2VuUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgfVxuICAgIC8vIENsZWFyIHRoZSBkaXNtaXNzZWQgc3RhdGUgd2hlbiBpbnB1dCBjaGFuZ2VzXG4gICAgZGlzbWlzc2VkRm9ySW5wdXRSZWYuY3VycmVudCA9IG51bGxcbiAgICB2b2lkIHVwZGF0ZVN1Z2dlc3Rpb25zKGlucHV0KVxuICB9LCBbaW5wdXQsIHVwZGF0ZVN1Z2dlc3Rpb25zXSlcblxuICAvLyBIYW5kbGUgdGFiIGtleSBwcmVzcyAtIGNvbXBsZXRlIHN1Z2dlc3Rpb25zIG9yIHRyaWdnZXIgZmlsZSBzdWdnZXN0aW9uc1xuICBjb25zdCBoYW5kbGVUYWIgPSB1c2VDYWxsYmFjayhhc3luYyAoKSA9PiB7XG4gICAgLy8gSWYgd2UgaGF2ZSBpbmxpbmUgZ2hvc3QgdGV4dCwgYXBwbHkgaXRcbiAgICBpZiAoZWZmZWN0aXZlR2hvc3RUZXh0KSB7XG4gICAgICAvLyBDaGVjayBmb3IgYmFzaCBtb2RlIGhpc3RvcnkgY29tcGxldGlvbiBmaXJzdFxuICAgICAgaWYgKG1vZGUgPT09ICdiYXNoJykge1xuICAgICAgICAvLyBSZXBsYWNlIHRoZSBpbnB1dCB3aXRoIHRoZSBmdWxsIGNvbW1hbmQgZnJvbSBoaXN0b3J5XG4gICAgICAgIG9uSW5wdXRDaGFuZ2UoZWZmZWN0aXZlR2hvc3RUZXh0LmZ1bGxDb21tYW5kKVxuICAgICAgICBzZXRDdXJzb3JPZmZzZXQoZWZmZWN0aXZlR2hvc3RUZXh0LmZ1bGxDb21tYW5kLmxlbmd0aClcbiAgICAgICAgc2V0SW5saW5lR2hvc3RUZXh0KHVuZGVmaW5lZClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIEZpbmQgdGhlIG1pZC1pbnB1dCBjb21tYW5kIHRvIGdldCBpdHMgcG9zaXRpb24gKGZvciBwcm9tcHQgbW9kZSlcbiAgICAgIGNvbnN0IG1pZElucHV0Q29tbWFuZCA9IGZpbmRNaWRJbnB1dFNsYXNoQ29tbWFuZChpbnB1dCwgY3Vyc29yT2Zmc2V0KVxuICAgICAgaWYgKG1pZElucHV0Q29tbWFuZCkge1xuICAgICAgICAvLyBSZXBsYWNlIHRoZSBwYXJ0aWFsIGNvbW1hbmQgd2l0aCB0aGUgZnVsbCBjb21tYW5kICsgc3BhY2VcbiAgICAgICAgY29uc3QgYmVmb3JlID0gaW5wdXQuc2xpY2UoMCwgbWlkSW5wdXRDb21tYW5kLnN0YXJ0UG9zKVxuICAgICAgICBjb25zdCBhZnRlciA9IGlucHV0LnNsaWNlKFxuICAgICAgICAgIG1pZElucHV0Q29tbWFuZC5zdGFydFBvcyArIG1pZElucHV0Q29tbWFuZC50b2tlbi5sZW5ndGgsXG4gICAgICAgIClcbiAgICAgICAgY29uc3QgbmV3SW5wdXQgPVxuICAgICAgICAgIGJlZm9yZSArICcvJyArIGVmZmVjdGl2ZUdob3N0VGV4dC5mdWxsQ29tbWFuZCArICcgJyArIGFmdGVyXG4gICAgICAgIGNvbnN0IG5ld0N1cnNvck9mZnNldCA9XG4gICAgICAgICAgbWlkSW5wdXRDb21tYW5kLnN0YXJ0UG9zICtcbiAgICAgICAgICAxICtcbiAgICAgICAgICBlZmZlY3RpdmVHaG9zdFRleHQuZnVsbENvbW1hbmQubGVuZ3RoICtcbiAgICAgICAgICAxXG5cbiAgICAgICAgb25JbnB1dENoYW5nZShuZXdJbnB1dClcbiAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KG5ld0N1cnNvck9mZnNldClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gSWYgd2UgaGF2ZSBhY3RpdmUgc3VnZ2VzdGlvbnMsIHNlbGVjdCBvbmVcbiAgICBpZiAoc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCkge1xuICAgICAgLy8gQ2FuY2VsIGFueSBwZW5kaW5nIGRlYm91bmNlZCBmZXRjaGVzIHRvIHByZXZlbnQgZmxpY2tlciB3aGVuIGFjY2VwdGluZ1xuICAgICAgZGVib3VuY2VkRmV0Y2hGaWxlU3VnZ2VzdGlvbnMuY2FuY2VsKClcbiAgICAgIGRlYm91bmNlZEZldGNoU2xhY2tDaGFubmVscy5jYW5jZWwoKVxuXG4gICAgICBjb25zdCBpbmRleCA9IHNlbGVjdGVkU3VnZ2VzdGlvbiA9PT0gLTEgPyAwIDogc2VsZWN0ZWRTdWdnZXN0aW9uXG4gICAgICBjb25zdCBzdWdnZXN0aW9uID0gc3VnZ2VzdGlvbnNbaW5kZXhdXG5cbiAgICAgIGlmIChzdWdnZXN0aW9uVHlwZSA9PT0gJ2NvbW1hbmQnICYmIGluZGV4IDwgc3VnZ2VzdGlvbnMubGVuZ3RoKSB7XG4gICAgICAgIGlmIChzdWdnZXN0aW9uKSB7XG4gICAgICAgICAgYXBwbHlDb21tYW5kU3VnZ2VzdGlvbihcbiAgICAgICAgICAgIHN1Z2dlc3Rpb24sXG4gICAgICAgICAgICBmYWxzZSwgLy8gZG9uJ3QgZXhlY3V0ZSBvbiB0YWJcbiAgICAgICAgICAgIGNvbW1hbmRzLFxuICAgICAgICAgICAgb25JbnB1dENoYW5nZSxcbiAgICAgICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgICAgIG9uU3VibWl0LFxuICAgICAgICAgIClcbiAgICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChzdWdnZXN0aW9uVHlwZSA9PT0gJ2N1c3RvbS10aXRsZScgJiYgc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCkge1xuICAgICAgICAvLyBBcHBseSBjdXN0b20gdGl0bGUgdG8gL3Jlc3VtZSBjb21tYW5kIHdpdGggc2Vzc2lvbklkXG4gICAgICAgIGlmIChzdWdnZXN0aW9uKSB7XG4gICAgICAgICAgY29uc3QgbmV3SW5wdXQgPSBidWlsZFJlc3VtZUlucHV0RnJvbVN1Z2dlc3Rpb24oc3VnZ2VzdGlvbilcbiAgICAgICAgICBvbklucHV0Q2hhbmdlKG5ld0lucHV0KVxuICAgICAgICAgIHNldEN1cnNvck9mZnNldChuZXdJbnB1dC5sZW5ndGgpXG4gICAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoc3VnZ2VzdGlvblR5cGUgPT09ICdkaXJlY3RvcnknICYmIHN1Z2dlc3Rpb25zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3Qgc3VnZ2VzdGlvbiA9IHN1Z2dlc3Rpb25zW2luZGV4XVxuICAgICAgICBpZiAoc3VnZ2VzdGlvbikge1xuICAgICAgICAgIC8vIENoZWNrIGlmIHRoaXMgaXMgYSBjb21tYW5kIGNvbnRleHQgKGUuZy4sIC9hZGQtZGlyKSBvciBnZW5lcmFsIHBhdGggY29tcGxldGlvblxuICAgICAgICAgIGNvbnN0IGlzSW5Db21tYW5kQ29udGV4dCA9IGlzQ29tbWFuZElucHV0KGlucHV0KVxuXG4gICAgICAgICAgbGV0IG5ld0lucHV0OiBzdHJpbmdcbiAgICAgICAgICBpZiAoaXNJbkNvbW1hbmRDb250ZXh0KSB7XG4gICAgICAgICAgICAvLyBDb21tYW5kIGNvbnRleHQ6IHJlcGxhY2UganVzdCB0aGUgYXJndW1lbnQgcG9ydGlvblxuICAgICAgICAgICAgY29uc3Qgc3BhY2VJbmRleCA9IGlucHV0LmluZGV4T2YoJyAnKVxuICAgICAgICAgICAgY29uc3QgY29tbWFuZFBhcnQgPSBpbnB1dC5zbGljZSgwLCBzcGFjZUluZGV4ICsgMSkgLy8gSW5jbHVkZSB0aGUgc3BhY2VcbiAgICAgICAgICAgIGNvbnN0IGNtZFN1ZmZpeCA9XG4gICAgICAgICAgICAgIGlzUGF0aE1ldGFkYXRhKHN1Z2dlc3Rpb24ubWV0YWRhdGEpICYmXG4gICAgICAgICAgICAgIHN1Z2dlc3Rpb24ubWV0YWRhdGEudHlwZSA9PT0gJ2RpcmVjdG9yeSdcbiAgICAgICAgICAgICAgICA/ICcvJ1xuICAgICAgICAgICAgICAgIDogJyAnXG4gICAgICAgICAgICBuZXdJbnB1dCA9IGNvbW1hbmRQYXJ0ICsgc3VnZ2VzdGlvbi5pZCArIGNtZFN1ZmZpeFxuXG4gICAgICAgICAgICBvbklucHV0Q2hhbmdlKG5ld0lucHV0KVxuICAgICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KG5ld0lucHV0Lmxlbmd0aClcblxuICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICBpc1BhdGhNZXRhZGF0YShzdWdnZXN0aW9uLm1ldGFkYXRhKSAmJlxuICAgICAgICAgICAgICBzdWdnZXN0aW9uLm1ldGFkYXRhLnR5cGUgPT09ICdkaXJlY3RvcnknXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgLy8gRm9yIGRpcmVjdG9yaWVzLCBmZXRjaCBuZXcgc3VnZ2VzdGlvbnMgZm9yIHRoZSB1cGRhdGVkIHBhdGhcbiAgICAgICAgICAgICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICAgICAgICBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgIH0pKVxuICAgICAgICAgICAgICB2b2lkIHVwZGF0ZVN1Z2dlc3Rpb25zKG5ld0lucHV0LCBuZXdJbnB1dC5sZW5ndGgpXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gR2VuZXJhbCBwYXRoIGNvbXBsZXRpb246IHJlcGxhY2UgdGhlIHBhdGggdG9rZW4gaW4gaW5wdXQgd2l0aCBALXByZWZpeGVkIHBhdGhcbiAgICAgICAgICAgIC8vIFRyeSB0byBnZXQgdG9rZW4gd2l0aCBAIHByZWZpeCBmaXJzdCB0byBjaGVjayBpZiBhbHJlYWR5IHByZWZpeGVkXG4gICAgICAgICAgICBjb25zdCBjb21wbGV0aW9uVG9rZW5XaXRoQXQgPSBleHRyYWN0Q29tcGxldGlvblRva2VuKFxuICAgICAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICAgICAgICB0cnVlLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgY29uc3QgY29tcGxldGlvblRva2VuID1cbiAgICAgICAgICAgICAgY29tcGxldGlvblRva2VuV2l0aEF0ID8/XG4gICAgICAgICAgICAgIGV4dHJhY3RDb21wbGV0aW9uVG9rZW4oaW5wdXQsIGN1cnNvck9mZnNldCwgZmFsc2UpXG5cbiAgICAgICAgICAgIGlmIChjb21wbGV0aW9uVG9rZW4pIHtcbiAgICAgICAgICAgICAgY29uc3QgaXNEaXIgPVxuICAgICAgICAgICAgICAgIGlzUGF0aE1ldGFkYXRhKHN1Z2dlc3Rpb24ubWV0YWRhdGEpICYmXG4gICAgICAgICAgICAgICAgc3VnZ2VzdGlvbi5tZXRhZGF0YS50eXBlID09PSAnZGlyZWN0b3J5J1xuICAgICAgICAgICAgICBjb25zdCByZXN1bHQgPSBhcHBseURpcmVjdG9yeVN1Z2dlc3Rpb24oXG4gICAgICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICAgICAgc3VnZ2VzdGlvbi5pZCxcbiAgICAgICAgICAgICAgICBjb21wbGV0aW9uVG9rZW4uc3RhcnRQb3MsXG4gICAgICAgICAgICAgICAgY29tcGxldGlvblRva2VuLnRva2VuLmxlbmd0aCxcbiAgICAgICAgICAgICAgICBpc0RpcixcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICBuZXdJbnB1dCA9IHJlc3VsdC5uZXdJbnB1dFxuXG4gICAgICAgICAgICAgIG9uSW5wdXRDaGFuZ2UobmV3SW5wdXQpXG4gICAgICAgICAgICAgIHNldEN1cnNvck9mZnNldChyZXN1bHQuY3Vyc29yUG9zKVxuXG4gICAgICAgICAgICAgIGlmIChpc0Rpcikge1xuICAgICAgICAgICAgICAgIC8vIEZvciBkaXJlY3RvcmllcywgZmV0Y2ggbmV3IHN1Z2dlc3Rpb25zIGZvciB0aGUgdXBkYXRlZCBwYXRoXG4gICAgICAgICAgICAgICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgICAgICAgY29tbWFuZEFyZ3VtZW50SGludDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIH0pKVxuICAgICAgICAgICAgICAgIHZvaWQgdXBkYXRlU3VnZ2VzdGlvbnMobmV3SW5wdXQsIHJlc3VsdC5jdXJzb3JQb3MpXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gRm9yIGZpbGVzLCBjbGVhciBzdWdnZXN0aW9uc1xuICAgICAgICAgICAgICAgIGNsZWFyU3VnZ2VzdGlvbnMoKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAvLyBObyBjb21wbGV0aW9uIHRva2VuIGZvdW5kIChlLmcuLCBjdXJzb3IgYWZ0ZXIgc3BhY2UpIC0ganVzdCBjbGVhciBzdWdnZXN0aW9uc1xuICAgICAgICAgICAgICAvLyB3aXRob3V0IG1vZGlmeWluZyBpbnB1dCB0byBhdm9pZCBkYXRhIGxvc3NcbiAgICAgICAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKHN1Z2dlc3Rpb25UeXBlID09PSAnc2hlbGwnICYmIHN1Z2dlc3Rpb25zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgY29uc3Qgc3VnZ2VzdGlvbiA9IHN1Z2dlc3Rpb25zW2luZGV4XVxuICAgICAgICBpZiAoc3VnZ2VzdGlvbikge1xuICAgICAgICAgIGNvbnN0IG1ldGFkYXRhID0gc3VnZ2VzdGlvbi5tZXRhZGF0YSBhc1xuICAgICAgICAgICAgfCB7IGNvbXBsZXRpb25UeXBlOiBTaGVsbENvbXBsZXRpb25UeXBlIH1cbiAgICAgICAgICAgIHwgdW5kZWZpbmVkXG4gICAgICAgICAgYXBwbHlTaGVsbFN1Z2dlc3Rpb24oXG4gICAgICAgICAgICBzdWdnZXN0aW9uLFxuICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICBjdXJzb3JPZmZzZXQsXG4gICAgICAgICAgICBvbklucHV0Q2hhbmdlLFxuICAgICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0LFxuICAgICAgICAgICAgbWV0YWRhdGE/LmNvbXBsZXRpb25UeXBlLFxuICAgICAgICAgIClcbiAgICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgc3VnZ2VzdGlvblR5cGUgPT09ICdhZ2VudCcgJiZcbiAgICAgICAgc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCAmJlxuICAgICAgICBzdWdnZXN0aW9uc1tpbmRleF0/LmlkPy5zdGFydHNXaXRoKCdkbS0nKVxuICAgICAgKSB7XG4gICAgICAgIGNvbnN0IHN1Z2dlc3Rpb24gPSBzdWdnZXN0aW9uc1tpbmRleF1cbiAgICAgICAgaWYgKHN1Z2dlc3Rpb24pIHtcbiAgICAgICAgICBhcHBseVRyaWdnZXJTdWdnZXN0aW9uKFxuICAgICAgICAgICAgc3VnZ2VzdGlvbixcbiAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICAgICAgRE1fTUVNQkVSX1JFLFxuICAgICAgICAgICAgb25JbnB1dENoYW5nZSxcbiAgICAgICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgICApXG4gICAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoc3VnZ2VzdGlvblR5cGUgPT09ICdzbGFjay1jaGFubmVsJyAmJiBzdWdnZXN0aW9ucy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IHN1Z2dlc3Rpb24gPSBzdWdnZXN0aW9uc1tpbmRleF1cbiAgICAgICAgaWYgKHN1Z2dlc3Rpb24pIHtcbiAgICAgICAgICBhcHBseVRyaWdnZXJTdWdnZXN0aW9uKFxuICAgICAgICAgICAgc3VnZ2VzdGlvbixcbiAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICAgICAgSEFTSF9DSEFOTkVMX1JFLFxuICAgICAgICAgICAgb25JbnB1dENoYW5nZSxcbiAgICAgICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgICApXG4gICAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoc3VnZ2VzdGlvblR5cGUgPT09ICdmaWxlJyAmJiBzdWdnZXN0aW9ucy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IGNvbXBsZXRpb25Ub2tlbiA9IGV4dHJhY3RDb21wbGV0aW9uVG9rZW4oXG4gICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICAgIHRydWUsXG4gICAgICAgIClcbiAgICAgICAgaWYgKCFjb21wbGV0aW9uVG9rZW4pIHtcbiAgICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENoZWNrIGlmIGFsbCBzdWdnZXN0aW9ucyBzaGFyZSBhIGNvbW1vbiBwcmVmaXggbG9uZ2VyIHRoYW4gdGhlIGN1cnJlbnQgaW5wdXRcbiAgICAgICAgY29uc3QgY29tbW9uUHJlZml4ID0gZmluZExvbmdlc3RDb21tb25QcmVmaXgoc3VnZ2VzdGlvbnMpXG5cbiAgICAgICAgLy8gRGV0ZXJtaW5lIGlmIHRva2VuIHN0YXJ0cyB3aXRoIEAgdG8gcHJlc2VydmUgaXQgZHVyaW5nIHJlcGxhY2VtZW50XG4gICAgICAgIGNvbnN0IGhhc0F0UHJlZml4ID0gY29tcGxldGlvblRva2VuLnRva2VuLnN0YXJ0c1dpdGgoJ0AnKVxuICAgICAgICAvLyBUaGUgZWZmZWN0aXZlIHRva2VuIGxlbmd0aCBleGNsdWRlcyB0aGUgQCBhbmQgcXVvdGVzIGlmIHByZXNlbnRcbiAgICAgICAgbGV0IGVmZmVjdGl2ZVRva2VuTGVuZ3RoOiBudW1iZXJcbiAgICAgICAgaWYgKGNvbXBsZXRpb25Ub2tlbi5pc1F1b3RlZCkge1xuICAgICAgICAgIC8vIFJlbW92ZSBAXCIgcHJlZml4IGFuZCBvcHRpb25hbCBjbG9zaW5nIFwiIHRvIGdldCBlZmZlY3RpdmUgbGVuZ3RoXG4gICAgICAgICAgZWZmZWN0aXZlVG9rZW5MZW5ndGggPSBjb21wbGV0aW9uVG9rZW4udG9rZW5cbiAgICAgICAgICAgIC5zbGljZSgyKVxuICAgICAgICAgICAgLnJlcGxhY2UoL1wiJC8sICcnKS5sZW5ndGhcbiAgICAgICAgfSBlbHNlIGlmIChoYXNBdFByZWZpeCkge1xuICAgICAgICAgIGVmZmVjdGl2ZVRva2VuTGVuZ3RoID0gY29tcGxldGlvblRva2VuLnRva2VuLmxlbmd0aCAtIDFcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBlZmZlY3RpdmVUb2tlbkxlbmd0aCA9IGNvbXBsZXRpb25Ub2tlbi50b2tlbi5sZW5ndGhcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIElmIHRoZXJlJ3MgYSBjb21tb24gcHJlZml4IGxvbmdlciB0aGFuIHdoYXQgdGhlIHVzZXIgaGFzIHR5cGVkLFxuICAgICAgICAvLyByZXBsYWNlIHRoZSBjdXJyZW50IGlucHV0IHdpdGggdGhlIGNvbW1vbiBwcmVmaXhcbiAgICAgICAgaWYgKGNvbW1vblByZWZpeC5sZW5ndGggPiBlZmZlY3RpdmVUb2tlbkxlbmd0aCkge1xuICAgICAgICAgIGNvbnN0IHJlcGxhY2VtZW50VmFsdWUgPSBmb3JtYXRSZXBsYWNlbWVudFZhbHVlKHtcbiAgICAgICAgICAgIGRpc3BsYXlUZXh0OiBjb21tb25QcmVmaXgsXG4gICAgICAgICAgICBtb2RlLFxuICAgICAgICAgICAgaGFzQXRQcmVmaXgsXG4gICAgICAgICAgICBuZWVkc1F1b3RlczogZmFsc2UsIC8vIGNvbW1vbiBwcmVmaXggZG9lc24ndCBuZWVkIHF1b3RlcyB1bmxlc3MgYWxyZWFkeSBxdW90ZWRcbiAgICAgICAgICAgIGlzUXVvdGVkOiBjb21wbGV0aW9uVG9rZW4uaXNRdW90ZWQsXG4gICAgICAgICAgICBpc0NvbXBsZXRlOiBmYWxzZSwgLy8gcGFydGlhbCBjb21wbGV0aW9uXG4gICAgICAgICAgfSlcblxuICAgICAgICAgIGFwcGx5RmlsZVN1Z2dlc3Rpb24oXG4gICAgICAgICAgICByZXBsYWNlbWVudFZhbHVlLFxuICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICBjb21wbGV0aW9uVG9rZW4udG9rZW4sXG4gICAgICAgICAgICBjb21wbGV0aW9uVG9rZW4uc3RhcnRQb3MsXG4gICAgICAgICAgICBvbklucHV0Q2hhbmdlLFxuICAgICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0LFxuICAgICAgICAgIClcbiAgICAgICAgICAvLyBEb24ndCBjbGVhciBzdWdnZXN0aW9ucyBzbyB1c2VyIGNhbiBjb250aW51ZSB0eXBpbmcgb3Igc2VsZWN0IGEgc3BlY2lmaWMgb3B0aW9uXG4gICAgICAgICAgLy8gSW5zdGVhZCwgdXBkYXRlIGZvciB0aGUgbmV3IHByZWZpeFxuICAgICAgICAgIHZvaWQgdXBkYXRlU3VnZ2VzdGlvbnMoXG4gICAgICAgICAgICBpbnB1dC5yZXBsYWNlKGNvbXBsZXRpb25Ub2tlbi50b2tlbiwgcmVwbGFjZW1lbnRWYWx1ZSksXG4gICAgICAgICAgICBjdXJzb3JPZmZzZXQsXG4gICAgICAgICAgKVxuICAgICAgICB9IGVsc2UgaWYgKGluZGV4IDwgc3VnZ2VzdGlvbnMubGVuZ3RoKSB7XG4gICAgICAgICAgLy8gT3RoZXJ3aXNlLCBhcHBseSB0aGUgc2VsZWN0ZWQgc3VnZ2VzdGlvblxuICAgICAgICAgIGNvbnN0IHN1Z2dlc3Rpb24gPSBzdWdnZXN0aW9uc1tpbmRleF1cbiAgICAgICAgICBpZiAoc3VnZ2VzdGlvbikge1xuICAgICAgICAgICAgY29uc3QgbmVlZHNRdW90ZXMgPSBzdWdnZXN0aW9uLmRpc3BsYXlUZXh0LmluY2x1ZGVzKCcgJylcbiAgICAgICAgICAgIGNvbnN0IHJlcGxhY2VtZW50VmFsdWUgPSBmb3JtYXRSZXBsYWNlbWVudFZhbHVlKHtcbiAgICAgICAgICAgICAgZGlzcGxheVRleHQ6IHN1Z2dlc3Rpb24uZGlzcGxheVRleHQsXG4gICAgICAgICAgICAgIG1vZGUsXG4gICAgICAgICAgICAgIGhhc0F0UHJlZml4LFxuICAgICAgICAgICAgICBuZWVkc1F1b3RlcyxcbiAgICAgICAgICAgICAgaXNRdW90ZWQ6IGNvbXBsZXRpb25Ub2tlbi5pc1F1b3RlZCxcbiAgICAgICAgICAgICAgaXNDb21wbGV0ZTogdHJ1ZSwgLy8gY29tcGxldGUgc3VnZ2VzdGlvblxuICAgICAgICAgICAgfSlcblxuICAgICAgICAgICAgYXBwbHlGaWxlU3VnZ2VzdGlvbihcbiAgICAgICAgICAgICAgcmVwbGFjZW1lbnRWYWx1ZSxcbiAgICAgICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgICAgIGNvbXBsZXRpb25Ub2tlbi50b2tlbixcbiAgICAgICAgICAgICAgY29tcGxldGlvblRva2VuLnN0YXJ0UG9zLFxuICAgICAgICAgICAgICBvbklucHV0Q2hhbmdlLFxuICAgICAgICAgICAgICBzZXRDdXJzb3JPZmZzZXQsXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGlucHV0LnRyaW0oKSAhPT0gJycpIHtcbiAgICAgIGxldCBzdWdnZXN0aW9uVHlwZTogU3VnZ2VzdGlvblR5cGVcbiAgICAgIGxldCBzdWdnZXN0aW9uSXRlbXM6IFN1Z2dlc3Rpb25JdGVtW11cblxuICAgICAgaWYgKG1vZGUgPT09ICdiYXNoJykge1xuICAgICAgICBzdWdnZXN0aW9uVHlwZSA9ICdzaGVsbCdcbiAgICAgICAgLy8gVGhpcyBzaG91bGQgYmUgdmVyeSBmYXN0LCB0YWtpbmcgPDEwbXNcbiAgICAgICAgY29uc3QgYmFzaFN1Z2dlc3Rpb25zID0gYXdhaXQgZ2VuZXJhdGVCYXNoU3VnZ2VzdGlvbnMoXG4gICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICApXG4gICAgICAgIGlmIChiYXNoU3VnZ2VzdGlvbnMubGVuZ3RoID09PSAxKSB7XG4gICAgICAgICAgLy8gSWYgc2luZ2xlIHN1Z2dlc3Rpb24sIGFwcGx5IGl0IGltbWVkaWF0ZWx5XG4gICAgICAgICAgY29uc3Qgc3VnZ2VzdGlvbiA9IGJhc2hTdWdnZXN0aW9uc1swXVxuICAgICAgICAgIGlmIChzdWdnZXN0aW9uKSB7XG4gICAgICAgICAgICBjb25zdCBtZXRhZGF0YSA9IHN1Z2dlc3Rpb24ubWV0YWRhdGEgYXNcbiAgICAgICAgICAgICAgfCB7IGNvbXBsZXRpb25UeXBlOiBTaGVsbENvbXBsZXRpb25UeXBlIH1cbiAgICAgICAgICAgICAgfCB1bmRlZmluZWRcbiAgICAgICAgICAgIGFwcGx5U2hlbGxTdWdnZXN0aW9uKFxuICAgICAgICAgICAgICBzdWdnZXN0aW9uLFxuICAgICAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICAgICAgICBvbklucHV0Q2hhbmdlLFxuICAgICAgICAgICAgICBzZXRDdXJzb3JPZmZzZXQsXG4gICAgICAgICAgICAgIG1ldGFkYXRhPy5jb21wbGV0aW9uVHlwZSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG4gICAgICAgICAgc3VnZ2VzdGlvbkl0ZW1zID0gW11cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzdWdnZXN0aW9uSXRlbXMgPSBiYXNoU3VnZ2VzdGlvbnNcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc3VnZ2VzdGlvblR5cGUgPSAnZmlsZSdcbiAgICAgICAgLy8gSWYgbm8gc3VnZ2VzdGlvbnMsIGZldGNoIGZpbGUgYW5kIE1DUCByZXNvdXJjZSBzdWdnZXN0aW9uc1xuICAgICAgICBjb25zdCBjb21wbGV0aW9uSW5mbyA9IGV4dHJhY3RDb21wbGV0aW9uVG9rZW4oaW5wdXQsIGN1cnNvck9mZnNldCwgdHJ1ZSlcbiAgICAgICAgaWYgKGNvbXBsZXRpb25JbmZvKSB7XG4gICAgICAgICAgLy8gSWYgdG9rZW4gc3RhcnRzIHdpdGggQCwgc2VhcmNoIHdpdGhvdXQgdGhlIEAgcHJlZml4XG4gICAgICAgICAgY29uc3QgaXNBdFN5bWJvbCA9IGNvbXBsZXRpb25JbmZvLnRva2VuLnN0YXJ0c1dpdGgoJ0AnKVxuICAgICAgICAgIGNvbnN0IHNlYXJjaFRva2VuID0gaXNBdFN5bWJvbFxuICAgICAgICAgICAgPyBjb21wbGV0aW9uSW5mby50b2tlbi5zdWJzdHJpbmcoMSlcbiAgICAgICAgICAgIDogY29tcGxldGlvbkluZm8udG9rZW5cblxuICAgICAgICAgIHN1Z2dlc3Rpb25JdGVtcyA9IGF3YWl0IGdlbmVyYXRlVW5pZmllZFN1Z2dlc3Rpb25zKFxuICAgICAgICAgICAgc2VhcmNoVG9rZW4sXG4gICAgICAgICAgICBtY3BSZXNvdXJjZXMsXG4gICAgICAgICAgICBhZ2VudHMsXG4gICAgICAgICAgICBpc0F0U3ltYm9sLFxuICAgICAgICAgIClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzdWdnZXN0aW9uSXRlbXMgPSBbXVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmIChzdWdnZXN0aW9uSXRlbXMubGVuZ3RoID4gMCkge1xuICAgICAgICAvLyBNdWx0aXBsZSBzdWdnZXN0aW9ucyBvciBub3QgYmFzaCBtb2RlOiBzaG93IGxpc3RcbiAgICAgICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgY29tbWFuZEFyZ3VtZW50SGludDogdW5kZWZpbmVkLFxuICAgICAgICAgIHN1Z2dlc3Rpb25zOiBzdWdnZXN0aW9uSXRlbXMsXG4gICAgICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBnZXRQcmVzZXJ2ZWRTZWxlY3Rpb24oXG4gICAgICAgICAgICBwcmV2LnN1Z2dlc3Rpb25zLFxuICAgICAgICAgICAgcHJldi5zZWxlY3RlZFN1Z2dlc3Rpb24sXG4gICAgICAgICAgICBzdWdnZXN0aW9uSXRlbXMsXG4gICAgICAgICAgKSxcbiAgICAgICAgfSkpXG4gICAgICAgIHNldFN1Z2dlc3Rpb25UeXBlKHN1Z2dlc3Rpb25UeXBlKVxuICAgICAgICBzZXRNYXhDb2x1bW5XaWR0aCh1bmRlZmluZWQpXG4gICAgICB9XG4gICAgfVxuICB9LCBbXG4gICAgc3VnZ2VzdGlvbnMsXG4gICAgc2VsZWN0ZWRTdWdnZXN0aW9uLFxuICAgIGlucHV0LFxuICAgIHN1Z2dlc3Rpb25UeXBlLFxuICAgIGNvbW1hbmRzLFxuICAgIG1vZGUsXG4gICAgb25JbnB1dENoYW5nZSxcbiAgICBzZXRDdXJzb3JPZmZzZXQsXG4gICAgb25TdWJtaXQsXG4gICAgY2xlYXJTdWdnZXN0aW9ucyxcbiAgICBjdXJzb3JPZmZzZXQsXG4gICAgdXBkYXRlU3VnZ2VzdGlvbnMsXG4gICAgbWNwUmVzb3VyY2VzLFxuICAgIHNldFN1Z2dlc3Rpb25zU3RhdGUsXG4gICAgYWdlbnRzLFxuICAgIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zLFxuICAgIGRlYm91bmNlZEZldGNoU2xhY2tDaGFubmVscyxcbiAgICBlZmZlY3RpdmVHaG9zdFRleHQsXG4gIF0pXG5cbiAgLy8gSGFuZGxlIGVudGVyIGtleSBwcmVzcyAtIGFwcGx5IGFuZCBleGVjdXRlIHN1Z2dlc3Rpb25zXG4gIGNvbnN0IGhhbmRsZUVudGVyID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChzZWxlY3RlZFN1Z2dlc3Rpb24gPCAwIHx8IHN1Z2dlc3Rpb25zLmxlbmd0aCA9PT0gMCkgcmV0dXJuXG5cbiAgICBjb25zdCBzdWdnZXN0aW9uID0gc3VnZ2VzdGlvbnNbc2VsZWN0ZWRTdWdnZXN0aW9uXVxuXG4gICAgaWYgKFxuICAgICAgc3VnZ2VzdGlvblR5cGUgPT09ICdjb21tYW5kJyAmJlxuICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uIDwgc3VnZ2VzdGlvbnMubGVuZ3RoXG4gICAgKSB7XG4gICAgICBpZiAoc3VnZ2VzdGlvbikge1xuICAgICAgICBhcHBseUNvbW1hbmRTdWdnZXN0aW9uKFxuICAgICAgICAgIHN1Z2dlc3Rpb24sXG4gICAgICAgICAgdHJ1ZSwgLy8gZXhlY3V0ZSBvbiByZXR1cm5cbiAgICAgICAgICBjb21tYW5kcyxcbiAgICAgICAgICBvbklucHV0Q2hhbmdlLFxuICAgICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgICBvblN1Ym1pdCxcbiAgICAgICAgKVxuICAgICAgICBkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucy5jYW5jZWwoKVxuICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKFxuICAgICAgc3VnZ2VzdGlvblR5cGUgPT09ICdjdXN0b20tdGl0bGUnICYmXG4gICAgICBzZWxlY3RlZFN1Z2dlc3Rpb24gPCBzdWdnZXN0aW9ucy5sZW5ndGhcbiAgICApIHtcbiAgICAgIC8vIEFwcGx5IGN1c3RvbSB0aXRsZSBhbmQgZXhlY3V0ZSAvcmVzdW1lIGNvbW1hbmQgd2l0aCBzZXNzaW9uSWRcbiAgICAgIGlmIChzdWdnZXN0aW9uKSB7XG4gICAgICAgIGNvbnN0IG5ld0lucHV0ID0gYnVpbGRSZXN1bWVJbnB1dEZyb21TdWdnZXN0aW9uKHN1Z2dlc3Rpb24pXG4gICAgICAgIG9uSW5wdXRDaGFuZ2UobmV3SW5wdXQpXG4gICAgICAgIHNldEN1cnNvck9mZnNldChuZXdJbnB1dC5sZW5ndGgpXG4gICAgICAgIG9uU3VibWl0KG5ld0lucHV0LCAvKiBpc1N1Ym1pdHRpbmdTbGFzaENvbW1hbmQgKi8gdHJ1ZSlcbiAgICAgICAgZGVib3VuY2VkRmV0Y2hGaWxlU3VnZ2VzdGlvbnMuY2FuY2VsKClcbiAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChcbiAgICAgIHN1Z2dlc3Rpb25UeXBlID09PSAnc2hlbGwnICYmXG4gICAgICBzZWxlY3RlZFN1Z2dlc3Rpb24gPCBzdWdnZXN0aW9ucy5sZW5ndGhcbiAgICApIHtcbiAgICAgIGNvbnN0IHN1Z2dlc3Rpb24gPSBzdWdnZXN0aW9uc1tzZWxlY3RlZFN1Z2dlc3Rpb25dXG4gICAgICBpZiAoc3VnZ2VzdGlvbikge1xuICAgICAgICBjb25zdCBtZXRhZGF0YSA9IHN1Z2dlc3Rpb24ubWV0YWRhdGEgYXNcbiAgICAgICAgICB8IHsgY29tcGxldGlvblR5cGU6IFNoZWxsQ29tcGxldGlvblR5cGUgfVxuICAgICAgICAgIHwgdW5kZWZpbmVkXG4gICAgICAgIGFwcGx5U2hlbGxTdWdnZXN0aW9uKFxuICAgICAgICAgIHN1Z2dlc3Rpb24sXG4gICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICAgIG9uSW5wdXRDaGFuZ2UsXG4gICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0LFxuICAgICAgICAgIG1ldGFkYXRhPy5jb21wbGV0aW9uVHlwZSxcbiAgICAgICAgKVxuICAgICAgICBkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucy5jYW5jZWwoKVxuICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKFxuICAgICAgc3VnZ2VzdGlvblR5cGUgPT09ICdhZ2VudCcgJiZcbiAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbiA8IHN1Z2dlc3Rpb25zLmxlbmd0aCAmJlxuICAgICAgc3VnZ2VzdGlvbj8uaWQ/LnN0YXJ0c1dpdGgoJ2RtLScpXG4gICAgKSB7XG4gICAgICBhcHBseVRyaWdnZXJTdWdnZXN0aW9uKFxuICAgICAgICBzdWdnZXN0aW9uLFxuICAgICAgICBpbnB1dCxcbiAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICBETV9NRU1CRVJfUkUsXG4gICAgICAgIG9uSW5wdXRDaGFuZ2UsXG4gICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgIClcbiAgICAgIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zLmNhbmNlbCgpXG4gICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICB9IGVsc2UgaWYgKFxuICAgICAgc3VnZ2VzdGlvblR5cGUgPT09ICdzbGFjay1jaGFubmVsJyAmJlxuICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uIDwgc3VnZ2VzdGlvbnMubGVuZ3RoXG4gICAgKSB7XG4gICAgICBpZiAoc3VnZ2VzdGlvbikge1xuICAgICAgICBhcHBseVRyaWdnZXJTdWdnZXN0aW9uKFxuICAgICAgICAgIHN1Z2dlc3Rpb24sXG4gICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICAgIEhBU0hfQ0hBTk5FTF9SRSxcbiAgICAgICAgICBvbklucHV0Q2hhbmdlLFxuICAgICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgKVxuICAgICAgICBkZWJvdW5jZWRGZXRjaFNsYWNrQ2hhbm5lbHMuY2FuY2VsKClcbiAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICB9XG4gICAgfSBlbHNlIGlmIChcbiAgICAgIHN1Z2dlc3Rpb25UeXBlID09PSAnZmlsZScgJiZcbiAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbiA8IHN1Z2dlc3Rpb25zLmxlbmd0aFxuICAgICkge1xuICAgICAgLy8gRXh0cmFjdCBjb21wbGV0aW9uIHRva2VuIGRpcmVjdGx5IHdoZW4gbmVlZGVkXG4gICAgICBjb25zdCBjb21wbGV0aW9uSW5mbyA9IGV4dHJhY3RDb21wbGV0aW9uVG9rZW4oaW5wdXQsIGN1cnNvck9mZnNldCwgdHJ1ZSlcbiAgICAgIGlmIChjb21wbGV0aW9uSW5mbykge1xuICAgICAgICBpZiAoc3VnZ2VzdGlvbikge1xuICAgICAgICAgIGNvbnN0IGhhc0F0UHJlZml4ID0gY29tcGxldGlvbkluZm8udG9rZW4uc3RhcnRzV2l0aCgnQCcpXG4gICAgICAgICAgY29uc3QgbmVlZHNRdW90ZXMgPSBzdWdnZXN0aW9uLmRpc3BsYXlUZXh0LmluY2x1ZGVzKCcgJylcbiAgICAgICAgICBjb25zdCByZXBsYWNlbWVudFZhbHVlID0gZm9ybWF0UmVwbGFjZW1lbnRWYWx1ZSh7XG4gICAgICAgICAgICBkaXNwbGF5VGV4dDogc3VnZ2VzdGlvbi5kaXNwbGF5VGV4dCxcbiAgICAgICAgICAgIG1vZGUsXG4gICAgICAgICAgICBoYXNBdFByZWZpeCxcbiAgICAgICAgICAgIG5lZWRzUXVvdGVzLFxuICAgICAgICAgICAgaXNRdW90ZWQ6IGNvbXBsZXRpb25JbmZvLmlzUXVvdGVkLFxuICAgICAgICAgICAgaXNDb21wbGV0ZTogdHJ1ZSwgLy8gY29tcGxldGUgc3VnZ2VzdGlvblxuICAgICAgICAgIH0pXG5cbiAgICAgICAgICBhcHBseUZpbGVTdWdnZXN0aW9uKFxuICAgICAgICAgICAgcmVwbGFjZW1lbnRWYWx1ZSxcbiAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgY29tcGxldGlvbkluZm8udG9rZW4sXG4gICAgICAgICAgICBjb21wbGV0aW9uSW5mby5zdGFydFBvcyxcbiAgICAgICAgICAgIG9uSW5wdXRDaGFuZ2UsXG4gICAgICAgICAgICBzZXRDdXJzb3JPZmZzZXQsXG4gICAgICAgICAgKVxuICAgICAgICAgIGRlYm91bmNlZEZldGNoRmlsZVN1Z2dlc3Rpb25zLmNhbmNlbCgpXG4gICAgICAgICAgY2xlYXJTdWdnZXN0aW9ucygpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKFxuICAgICAgc3VnZ2VzdGlvblR5cGUgPT09ICdkaXJlY3RvcnknICYmXG4gICAgICBzZWxlY3RlZFN1Z2dlc3Rpb24gPCBzdWdnZXN0aW9ucy5sZW5ndGhcbiAgICApIHtcbiAgICAgIGlmIChzdWdnZXN0aW9uKSB7XG4gICAgICAgIC8vIEluIGNvbW1hbmQgY29udGV4dCAoZS5nLiwgL2FkZC1kaXIpLCBFbnRlciBzdWJtaXRzIHRoZSBjb21tYW5kXG4gICAgICAgIC8vIHJhdGhlciB0aGFuIGFwcGx5aW5nIHRoZSBkaXJlY3Rvcnkgc3VnZ2VzdGlvbi4gSnVzdCBjbGVhclxuICAgICAgICAvLyBzdWdnZXN0aW9ucyBhbmQgbGV0IHRoZSBzdWJtaXQgaGFuZGxlciBwcm9jZXNzIHRoZSBjdXJyZW50IGlucHV0LlxuICAgICAgICBpZiAoaXNDb21tYW5kSW5wdXQoaW5wdXQpKSB7XG4gICAgICAgICAgZGVib3VuY2VkRmV0Y2hGaWxlU3VnZ2VzdGlvbnMuY2FuY2VsKClcbiAgICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEdlbmVyYWwgcGF0aCBjb21wbGV0aW9uOiByZXBsYWNlIHRoZSBwYXRoIHRva2VuXG4gICAgICAgIGNvbnN0IGNvbXBsZXRpb25Ub2tlbldpdGhBdCA9IGV4dHJhY3RDb21wbGV0aW9uVG9rZW4oXG4gICAgICAgICAgaW5wdXQsXG4gICAgICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgICAgIHRydWUsXG4gICAgICAgIClcbiAgICAgICAgY29uc3QgY29tcGxldGlvblRva2VuID1cbiAgICAgICAgICBjb21wbGV0aW9uVG9rZW5XaXRoQXQgPz9cbiAgICAgICAgICBleHRyYWN0Q29tcGxldGlvblRva2VuKGlucHV0LCBjdXJzb3JPZmZzZXQsIGZhbHNlKVxuXG4gICAgICAgIGlmIChjb21wbGV0aW9uVG9rZW4pIHtcbiAgICAgICAgICBjb25zdCBpc0RpciA9XG4gICAgICAgICAgICBpc1BhdGhNZXRhZGF0YShzdWdnZXN0aW9uLm1ldGFkYXRhKSAmJlxuICAgICAgICAgICAgc3VnZ2VzdGlvbi5tZXRhZGF0YS50eXBlID09PSAnZGlyZWN0b3J5J1xuICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGFwcGx5RGlyZWN0b3J5U3VnZ2VzdGlvbihcbiAgICAgICAgICAgIGlucHV0LFxuICAgICAgICAgICAgc3VnZ2VzdGlvbi5pZCxcbiAgICAgICAgICAgIGNvbXBsZXRpb25Ub2tlbi5zdGFydFBvcyxcbiAgICAgICAgICAgIGNvbXBsZXRpb25Ub2tlbi50b2tlbi5sZW5ndGgsXG4gICAgICAgICAgICBpc0RpcixcbiAgICAgICAgICApXG4gICAgICAgICAgb25JbnB1dENoYW5nZShyZXN1bHQubmV3SW5wdXQpXG4gICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KHJlc3VsdC5jdXJzb3JQb3MpXG4gICAgICAgIH1cbiAgICAgICAgLy8gSWYgbm8gY29tcGxldGlvbiB0b2tlbiBmb3VuZCAoZS5nLiwgY3Vyc29yIGFmdGVyIHNwYWNlKSwgZG9uJ3QgbW9kaWZ5IGlucHV0XG4gICAgICAgIC8vIHRvIGF2b2lkIGRhdGEgbG9zcyAtIGp1c3QgY2xlYXIgc3VnZ2VzdGlvbnNcblxuICAgICAgICBkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucy5jYW5jZWwoKVxuICAgICAgICBjbGVhclN1Z2dlc3Rpb25zKClcbiAgICAgIH1cbiAgICB9XG4gIH0sIFtcbiAgICBzdWdnZXN0aW9ucyxcbiAgICBzZWxlY3RlZFN1Z2dlc3Rpb24sXG4gICAgc3VnZ2VzdGlvblR5cGUsXG4gICAgY29tbWFuZHMsXG4gICAgaW5wdXQsXG4gICAgY3Vyc29yT2Zmc2V0LFxuICAgIG1vZGUsXG4gICAgb25JbnB1dENoYW5nZSxcbiAgICBzZXRDdXJzb3JPZmZzZXQsXG4gICAgb25TdWJtaXQsXG4gICAgY2xlYXJTdWdnZXN0aW9ucyxcbiAgICBkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucyxcbiAgICBkZWJvdW5jZWRGZXRjaFNsYWNrQ2hhbm5lbHMsXG4gIF0pXG5cbiAgLy8gSGFuZGxlciBmb3IgYXV0b2NvbXBsZXRlOmFjY2VwdCAtIGFjY2VwdHMgY3VycmVudCBzdWdnZXN0aW9uIHZpYSBUYWIgb3IgUmlnaHQgQXJyb3dcbiAgY29uc3QgaGFuZGxlQXV0b2NvbXBsZXRlQWNjZXB0ID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHZvaWQgaGFuZGxlVGFiKClcbiAgfSwgW2hhbmRsZVRhYl0pXG5cbiAgLy8gSGFuZGxlciBmb3IgYXV0b2NvbXBsZXRlOmRpc21pc3MgLSBjbGVhcnMgc3VnZ2VzdGlvbnMgYW5kIHByZXZlbnRzIHJlLXRyaWdnZXJpbmdcbiAgY29uc3QgaGFuZGxlQXV0b2NvbXBsZXRlRGlzbWlzcyA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucy5jYW5jZWwoKVxuICAgIGRlYm91bmNlZEZldGNoU2xhY2tDaGFubmVscy5jYW5jZWwoKVxuICAgIGNsZWFyU3VnZ2VzdGlvbnMoKVxuICAgIC8vIFJlbWVtYmVyIHRoZSBpbnB1dCB3aGVuIGRpc21pc3NlZCB0byBwcmV2ZW50IGltbWVkaWF0ZSByZS10cmlnZ2VyaW5nXG4gICAgZGlzbWlzc2VkRm9ySW5wdXRSZWYuY3VycmVudCA9IGlucHV0XG4gIH0sIFtcbiAgICBkZWJvdW5jZWRGZXRjaEZpbGVTdWdnZXN0aW9ucyxcbiAgICBkZWJvdW5jZWRGZXRjaFNsYWNrQ2hhbm5lbHMsXG4gICAgY2xlYXJTdWdnZXN0aW9ucyxcbiAgICBpbnB1dCxcbiAgXSlcblxuICAvLyBIYW5kbGVyIGZvciBhdXRvY29tcGxldGU6cHJldmlvdXMgLSBzZWxlY3RzIHByZXZpb3VzIHN1Z2dlc3Rpb25cbiAgY29uc3QgaGFuZGxlQXV0b2NvbXBsZXRlUHJldmlvdXMgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAuLi5wcmV2LFxuICAgICAgc2VsZWN0ZWRTdWdnZXN0aW9uOlxuICAgICAgICBwcmV2LnNlbGVjdGVkU3VnZ2VzdGlvbiA8PSAwXG4gICAgICAgICAgPyBzdWdnZXN0aW9ucy5sZW5ndGggLSAxXG4gICAgICAgICAgOiBwcmV2LnNlbGVjdGVkU3VnZ2VzdGlvbiAtIDEsXG4gICAgfSkpXG4gIH0sIFtzdWdnZXN0aW9ucy5sZW5ndGgsIHNldFN1Z2dlc3Rpb25zU3RhdGVdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGF1dG9jb21wbGV0ZTpuZXh0IC0gc2VsZWN0cyBuZXh0IHN1Z2dlc3Rpb25cbiAgY29uc3QgaGFuZGxlQXV0b2NvbXBsZXRlTmV4dCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTdWdnZXN0aW9uc1N0YXRlKHByZXYgPT4gKHtcbiAgICAgIC4uLnByZXYsXG4gICAgICBzZWxlY3RlZFN1Z2dlc3Rpb246XG4gICAgICAgIHByZXYuc2VsZWN0ZWRTdWdnZXN0aW9uID49IHN1Z2dlc3Rpb25zLmxlbmd0aCAtIDFcbiAgICAgICAgICA/IDBcbiAgICAgICAgICA6IHByZXYuc2VsZWN0ZWRTdWdnZXN0aW9uICsgMSxcbiAgICB9KSlcbiAgfSwgW3N1Z2dlc3Rpb25zLmxlbmd0aCwgc2V0U3VnZ2VzdGlvbnNTdGF0ZV0pXG5cbiAgLy8gQXV0b2NvbXBsZXRlIGNvbnRleHQga2V5YmluZGluZ3MgLSBvbmx5IGFjdGl2ZSB3aGVuIHN1Z2dlc3Rpb25zIGFyZSB2aXNpYmxlXG4gIGNvbnN0IGF1dG9jb21wbGV0ZUhhbmRsZXJzID0gdXNlTWVtbyhcbiAgICAoKSA9PiAoe1xuICAgICAgJ2F1dG9jb21wbGV0ZTphY2NlcHQnOiBoYW5kbGVBdXRvY29tcGxldGVBY2NlcHQsXG4gICAgICAnYXV0b2NvbXBsZXRlOmRpc21pc3MnOiBoYW5kbGVBdXRvY29tcGxldGVEaXNtaXNzLFxuICAgICAgJ2F1dG9jb21wbGV0ZTpwcmV2aW91cyc6IGhhbmRsZUF1dG9jb21wbGV0ZVByZXZpb3VzLFxuICAgICAgJ2F1dG9jb21wbGV0ZTpuZXh0JzogaGFuZGxlQXV0b2NvbXBsZXRlTmV4dCxcbiAgICB9KSxcbiAgICBbXG4gICAgICBoYW5kbGVBdXRvY29tcGxldGVBY2NlcHQsXG4gICAgICBoYW5kbGVBdXRvY29tcGxldGVEaXNtaXNzLFxuICAgICAgaGFuZGxlQXV0b2NvbXBsZXRlUHJldmlvdXMsXG4gICAgICBoYW5kbGVBdXRvY29tcGxldGVOZXh0LFxuICAgIF0sXG4gIClcblxuICAvLyBSZWdpc3RlciBhdXRvY29tcGxldGUgYXMgYW4gb3ZlcmxheSBzbyBDYW5jZWxSZXF1ZXN0SGFuZGxlciBkZWZlcnMgRVNDIGhhbmRsaW5nXG4gIC8vIFRoaXMgZW5zdXJlcyBFU0MgZGlzbWlzc2VzIGF1dG9jb21wbGV0ZSBiZWZvcmUgY2FuY2VsaW5nIHJ1bm5pbmcgdGFza3NcbiAgY29uc3QgaXNBdXRvY29tcGxldGVBY3RpdmUgPSBzdWdnZXN0aW9ucy5sZW5ndGggPiAwIHx8ICEhZWZmZWN0aXZlR2hvc3RUZXh0XG4gIGNvbnN0IGlzTW9kYWxPdmVybGF5QWN0aXZlID0gdXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUoKVxuICB1c2VSZWdpc3Rlck92ZXJsYXkoJ2F1dG9jb21wbGV0ZScsIGlzQXV0b2NvbXBsZXRlQWN0aXZlKVxuICAvLyBSZWdpc3RlciBBdXRvY29tcGxldGUgY29udGV4dCBzbyBpdCBhcHBlYXJzIGluIGFjdGl2ZUNvbnRleHRzIGZvciBvdGhlciBoYW5kbGVycy5cbiAgLy8gVGhpcyBhbGxvd3MgQ2hhdCdzIHJlc29sdmVyIHRvIHNlZSBBdXRvY29tcGxldGUgYW5kIGRlZmVyIHRvIGl0cyBiaW5kaW5ncyBmb3IgdXAvZG93bi5cbiAgdXNlUmVnaXN0ZXJLZXliaW5kaW5nQ29udGV4dCgnQXV0b2NvbXBsZXRlJywgaXNBdXRvY29tcGxldGVBY3RpdmUpXG5cbiAgLy8gRGlzYWJsZSBhdXRvY29tcGxldGUga2V5YmluZGluZ3Mgd2hlbiBhIG1vZGFsIG92ZXJsYXkgKGUuZy4sIERpZmZEaWFsb2cpIGlzIGFjdGl2ZSxcbiAgLy8gc28gZXNjYXBlIHJlYWNoZXMgdGhlIG92ZXJsYXkncyBoYW5kbGVyIGluc3RlYWQgb2YgZGlzbWlzc2luZyBhdXRvY29tcGxldGVcbiAgdXNlS2V5YmluZGluZ3MoYXV0b2NvbXBsZXRlSGFuZGxlcnMsIHtcbiAgICBjb250ZXh0OiAnQXV0b2NvbXBsZXRlJyxcbiAgICBpc0FjdGl2ZTogaXNBdXRvY29tcGxldGVBY3RpdmUgJiYgIWlzTW9kYWxPdmVybGF5QWN0aXZlLFxuICB9KVxuXG4gIGZ1bmN0aW9uIGFjY2VwdFN1Z2dlc3Rpb25UZXh0KHRleHQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IGRldGVjdGVkTW9kZSA9IGdldE1vZGVGcm9tSW5wdXQodGV4dClcbiAgICBpZiAoZGV0ZWN0ZWRNb2RlICE9PSAncHJvbXB0JyAmJiBvbk1vZGVDaGFuZ2UpIHtcbiAgICAgIG9uTW9kZUNoYW5nZShkZXRlY3RlZE1vZGUpXG4gICAgICBjb25zdCBzdHJpcHBlZCA9IGdldFZhbHVlRnJvbUlucHV0KHRleHQpXG4gICAgICBvbklucHV0Q2hhbmdlKHN0cmlwcGVkKVxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KHN0cmlwcGVkLmxlbmd0aClcbiAgICB9IGVsc2Uge1xuICAgICAgb25JbnB1dENoYW5nZSh0ZXh0KVxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KHRleHQubGVuZ3RoKVxuICAgIH1cbiAgfVxuXG4gIC8vIEhhbmRsZSBrZXlib2FyZCBpbnB1dCBmb3IgYmVoYXZpb3JzIG5vdCBjb3ZlcmVkIGJ5IGtleWJpbmRpbmdzXG4gIGNvbnN0IGhhbmRsZUtleURvd24gPSAoZTogS2V5Ym9hcmRFdmVudCk6IHZvaWQgPT4ge1xuICAgIC8vIEhhbmRsZSByaWdodCBhcnJvdyB0byBhY2NlcHQgcHJvbXB0IHN1Z2dlc3Rpb24gZ2hvc3QgdGV4dFxuICAgIGlmIChlLmtleSA9PT0gJ3JpZ2h0JyAmJiAhaXNWaWV3aW5nVGVhbW1hdGUpIHtcbiAgICAgIGNvbnN0IHN1Z2dlc3Rpb25UZXh0ID0gcHJvbXB0U3VnZ2VzdGlvbi50ZXh0XG4gICAgICBjb25zdCBzdWdnZXN0aW9uU2hvd25BdCA9IHByb21wdFN1Z2dlc3Rpb24uc2hvd25BdFxuICAgICAgaWYgKHN1Z2dlc3Rpb25UZXh0ICYmIHN1Z2dlc3Rpb25TaG93bkF0ID4gMCAmJiBpbnB1dCA9PT0gJycpIHtcbiAgICAgICAgbWFya0FjY2VwdGVkKClcbiAgICAgICAgYWNjZXB0U3VnZ2VzdGlvblRleHQoc3VnZ2VzdGlvblRleHQpXG4gICAgICAgIGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gSGFuZGxlIFRhYiBrZXkgZmFsbGJhY2sgYmVoYXZpb3JzIHdoZW4gbm8gYXV0b2NvbXBsZXRlIHN1Z2dlc3Rpb25zXG4gICAgLy8gRG9uJ3QgaGFuZGxlIHRhYiBpZiBzaGlmdCBpcyBwcmVzc2VkICh1c2VkIGZvciBtb2RlIGN5Y2xlKVxuICAgIGlmIChlLmtleSA9PT0gJ3RhYicgJiYgIWUuc2hpZnQpIHtcbiAgICAgIC8vIFNraXAgaWYgYXV0b2NvbXBsZXRlIGlzIGhhbmRsaW5nIHRoaXMgKHN1Z2dlc3Rpb25zIG9yIGdob3N0IHRleHQgZXhpc3QpXG4gICAgICBpZiAoc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCB8fCBlZmZlY3RpdmVHaG9zdFRleHQpIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICAvLyBBY2NlcHQgcHJvbXB0IHN1Z2dlc3Rpb24gaWYgaXQgZXhpc3RzIGluIEFwcFN0YXRlXG4gICAgICBjb25zdCBzdWdnZXN0aW9uVGV4dCA9IHByb21wdFN1Z2dlc3Rpb24udGV4dFxuICAgICAgY29uc3Qgc3VnZ2VzdGlvblNob3duQXQgPSBwcm9tcHRTdWdnZXN0aW9uLnNob3duQXRcbiAgICAgIGlmIChcbiAgICAgICAgc3VnZ2VzdGlvblRleHQgJiZcbiAgICAgICAgc3VnZ2VzdGlvblNob3duQXQgPiAwICYmXG4gICAgICAgIGlucHV0ID09PSAnJyAmJlxuICAgICAgICAhaXNWaWV3aW5nVGVhbW1hdGVcbiAgICAgICkge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgbWFya0FjY2VwdGVkKClcbiAgICAgICAgYWNjZXB0U3VnZ2VzdGlvblRleHQoc3VnZ2VzdGlvblRleHQpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuICAgICAgLy8gUmVtaW5kIHVzZXIgYWJvdXQgdGhpbmtpbmcgdG9nZ2xlIHNob3J0Y3V0IGlmIGVtcHR5IGlucHV0XG4gICAgICBpZiAoaW5wdXQudHJpbSgpID09PSAnJykge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAgICBrZXk6ICd0aGlua2luZy10b2dnbGUtaGludCcsXG4gICAgICAgICAganN4OiAoXG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgVXNlIHt0aGlua2luZ1RvZ2dsZVNob3J0Y3V0fSB0byB0b2dnbGUgdGhpbmtpbmdcbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICApLFxuICAgICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgICB0aW1lb3V0TXM6IDMwMDAsXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBPbmx5IGNvbnRpbnVlIHdpdGggbmF2aWdhdGlvbiBpZiB3ZSBoYXZlIHN1Z2dlc3Rpb25zXG4gICAgaWYgKHN1Z2dlc3Rpb25zLmxlbmd0aCA9PT0gMCkgcmV0dXJuXG5cbiAgICAvLyBIYW5kbGUgQ3RybC1OL1AgZm9yIG5hdmlnYXRpb24gKGFycm93cyBoYW5kbGVkIGJ5IGtleWJpbmRpbmdzKVxuICAgIC8vIFNraXAgaWYgd2UncmUgaW4gdGhlIG1pZGRsZSBvZiBhIGNob3JkIHNlcXVlbmNlIHRvIGFsbG93IGNob3JkcyBsaWtlIGN0cmwrZiBuXG4gICAgY29uc3QgaGFzUGVuZGluZ0Nob3JkID0ga2V5YmluZGluZ0NvbnRleHQ/LnBlbmRpbmdDaG9yZCAhPSBudWxsXG4gICAgaWYgKGUuY3RybCAmJiBlLmtleSA9PT0gJ24nICYmICFoYXNQZW5kaW5nQ2hvcmQpIHtcbiAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgaGFuZGxlQXV0b2NvbXBsZXRlTmV4dCgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoZS5jdHJsICYmIGUua2V5ID09PSAncCcgJiYgIWhhc1BlbmRpbmdDaG9yZCkge1xuICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICBoYW5kbGVBdXRvY29tcGxldGVQcmV2aW91cygpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgc2VsZWN0aW9uIGFuZCBleGVjdXRpb24gdmlhIHJldHVybi9lbnRlclxuICAgIC8vIFNoaWZ0K0VudGVyIGFuZCBNZXRhK0VudGVyIGluc2VydCBuZXdsaW5lcyAoaGFuZGxlZCBieSB1c2VUZXh0SW5wdXQpLFxuICAgIC8vIHNvIGRvbid0IGFjY2VwdCB0aGUgc3VnZ2VzdGlvbiBmb3IgdGhvc2UuXG4gICAgaWYgKGUua2V5ID09PSAncmV0dXJuJyAmJiAhZS5zaGlmdCAmJiAhZS5tZXRhKSB7XG4gICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgIGhhbmRsZUVudGVyKClcbiAgICB9XG4gIH1cblxuICAvLyBCYWNrd2FyZC1jb21wYXQgYnJpZGdlOiBQcm9tcHRJbnB1dCBkb2Vzbid0IHlldCB3aXJlIGhhbmRsZUtleURvd24gdG9cbiAgLy8gPEJveCBvbktleURvd24+LiBTdWJzY3JpYmUgdmlhIHVzZUlucHV0IGFuZCBhZGFwdCBJbnB1dEV2ZW50IOKGklxuICAvLyBLZXlib2FyZEV2ZW50IHVudGlsIHRoZSBjb25zdW1lciBpcyBtaWdyYXRlZCAoc2VwYXJhdGUgUFIpLlxuICAvLyBUT0RPKG9uS2V5RG93bi1taWdyYXRpb24pOiByZW1vdmUgb25jZSBQcm9tcHRJbnB1dCBwYXNzZXMgaGFuZGxlS2V5RG93bi5cbiAgdXNlSW5wdXQoKF9pbnB1dCwgX2tleSwgZXZlbnQpID0+IHtcbiAgICBjb25zdCBrYkV2ZW50ID0gbmV3IEtleWJvYXJkRXZlbnQoZXZlbnQua2V5cHJlc3MpXG4gICAgaGFuZGxlS2V5RG93bihrYkV2ZW50KVxuICAgIGlmIChrYkV2ZW50LmRpZFN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpKSB7XG4gICAgICBldmVudC5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKVxuICAgIH1cbiAgfSlcblxuICByZXR1cm4ge1xuICAgIHN1Z2dlc3Rpb25zLFxuICAgIHNlbGVjdGVkU3VnZ2VzdGlvbixcbiAgICBzdWdnZXN0aW9uVHlwZSxcbiAgICBtYXhDb2x1bW5XaWR0aCxcbiAgICBjb21tYW5kQXJndW1lbnRIaW50LFxuICAgIGlubGluZUdob3N0VGV4dDogZWZmZWN0aXZlR2hvc3RUZXh0LFxuICAgIGhhbmRsZUtleURvd24sXG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxXQUFXLEVBQUVDLFNBQVMsRUFBRUMsT0FBTyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3pFLFNBQVNDLGdCQUFnQixRQUFRLDhCQUE4QjtBQUMvRCxTQUFTQyxJQUFJLFFBQVEsWUFBWTtBQUNqQyxTQUFTQyxRQUFRLFFBQVEsaUNBQWlDO0FBQzFELFNBQVNDLG1CQUFtQixRQUFRLGFBQWE7QUFDakQsU0FBUyxLQUFLQyxPQUFPLEVBQUVDLGNBQWMsUUFBUSxnQkFBZ0I7QUFDN0QsU0FDRUMsZ0JBQWdCLEVBQ2hCQyxpQkFBaUIsUUFDWix5Q0FBeUM7QUFDaEQsY0FDRUMsY0FBYyxFQUNkQyxjQUFjLFFBQ1QsMkRBQTJEO0FBQ2xFLFNBQ0VDLHVCQUF1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 \ No newline at end of file diff --git a/hooks/useUpdateNotification.ts b/hooks/useUpdateNotification.ts new file mode 100644 index 0000000..c9a7b2a --- /dev/null +++ b/hooks/useUpdateNotification.ts @@ -0,0 +1,34 @@ +import { useState } from 'react' +import { major, minor, patch } from 'semver' + +export function getSemverPart(version: string): string { + return `${major(version, { loose: true })}.${minor(version, { loose: true })}.${patch(version, { loose: true })}` +} + +export function shouldShowUpdateNotification( + updatedVersion: string, + lastNotifiedSemver: string | null, +): boolean { + const updatedSemver = getSemverPart(updatedVersion) + return updatedSemver !== lastNotifiedSemver +} + +export function useUpdateNotification( + updatedVersion: string | null | undefined, + initialVersion: string = MACRO.VERSION, +): string | null { + const [lastNotifiedSemver, setLastNotifiedSemver] = useState( + () => getSemverPart(initialVersion), + ) + + if (!updatedVersion) { + return null + } + + const updatedSemver = getSemverPart(updatedVersion) + if (updatedSemver !== lastNotifiedSemver) { + setLastNotifiedSemver(updatedSemver) + return updatedSemver + } + return null +} diff --git a/hooks/useVimInput.ts b/hooks/useVimInput.ts new file mode 100644 index 0000000..0aabc91 --- /dev/null +++ b/hooks/useVimInput.ts @@ -0,0 +1,316 @@ +import React, { useCallback, useState } from 'react' +import type { Key } from '../ink.js' +import type { VimInputState, VimMode } from '../types/textInputTypes.js' +import { Cursor } from '../utils/Cursor.js' +import { lastGrapheme } from '../utils/intl.js' +import { + executeIndent, + executeJoin, + executeOpenLine, + executeOperatorFind, + executeOperatorMotion, + executeOperatorTextObj, + executeReplace, + executeToggleCase, + executeX, + type OperatorContext, +} from '../vim/operators.js' +import { type TransitionContext, transition } from '../vim/transitions.js' +import { + createInitialPersistentState, + createInitialVimState, + type PersistentState, + type RecordedChange, + type VimState, +} from '../vim/types.js' +import { type UseTextInputProps, useTextInput } from './useTextInput.js' + +type UseVimInputProps = Omit & { + onModeChange?: (mode: VimMode) => void + onUndo?: () => void + inputFilter?: UseTextInputProps['inputFilter'] +} + +export function useVimInput(props: UseVimInputProps): VimInputState { + const vimStateRef = React.useRef(createInitialVimState()) + const [mode, setMode] = useState('INSERT') + + const persistentRef = React.useRef( + createInitialPersistentState(), + ) + + // inputFilter is applied once at the top of handleVimInput (not here) so + // vim-handled paths that return without calling textInput.onInput still + // run the filter — otherwise a stateful filter (e.g. lazy-space-after- + // pill) stays armed across an Escape → NORMAL → INSERT round-trip. + const textInput = useTextInput({ ...props, inputFilter: undefined }) + const { onModeChange, inputFilter } = props + + const switchToInsertMode = useCallback( + (offset?: number): void => { + if (offset !== undefined) { + textInput.setOffset(offset) + } + vimStateRef.current = { mode: 'INSERT', insertedText: '' } + setMode('INSERT') + onModeChange?.('INSERT') + }, + [textInput, onModeChange], + ) + + const switchToNormalMode = useCallback((): void => { + const current = vimStateRef.current + if (current.mode === 'INSERT' && current.insertedText) { + persistentRef.current.lastChange = { + type: 'insert', + text: current.insertedText, + } + } + + // Vim behavior: move cursor left by 1 when exiting insert mode + // (unless at beginning of line or at offset 0) + const offset = textInput.offset + if (offset > 0 && props.value[offset - 1] !== '\n') { + textInput.setOffset(offset - 1) + } + + vimStateRef.current = { mode: 'NORMAL', command: { type: 'idle' } } + setMode('NORMAL') + onModeChange?.('NORMAL') + }, [onModeChange, textInput, props.value]) + + function createOperatorContext( + cursor: Cursor, + isReplay: boolean = false, + ): OperatorContext { + return { + cursor, + text: props.value, + setText: (newText: string) => props.onChange(newText), + setOffset: (offset: number) => textInput.setOffset(offset), + enterInsert: (offset: number) => switchToInsertMode(offset), + getRegister: () => persistentRef.current.register, + setRegister: (content: string, linewise: boolean) => { + persistentRef.current.register = content + persistentRef.current.registerIsLinewise = linewise + }, + getLastFind: () => persistentRef.current.lastFind, + setLastFind: (type, char) => { + persistentRef.current.lastFind = { type, char } + }, + recordChange: isReplay + ? () => {} + : (change: RecordedChange) => { + persistentRef.current.lastChange = change + }, + } + } + + function replayLastChange(): void { + const change = persistentRef.current.lastChange + if (!change) return + + const cursor = Cursor.fromText(props.value, props.columns, textInput.offset) + const ctx = createOperatorContext(cursor, true) + + switch (change.type) { + case 'insert': + if (change.text) { + const newCursor = cursor.insert(change.text) + props.onChange(newCursor.text) + textInput.setOffset(newCursor.offset) + } + break + + case 'x': + executeX(change.count, ctx) + break + + case 'replace': + executeReplace(change.char, change.count, ctx) + break + + case 'toggleCase': + executeToggleCase(change.count, ctx) + break + + case 'indent': + executeIndent(change.dir, change.count, ctx) + break + + case 'join': + executeJoin(change.count, ctx) + break + + case 'openLine': + executeOpenLine(change.direction, ctx) + break + + case 'operator': + executeOperatorMotion(change.op, change.motion, change.count, ctx) + break + + case 'operatorFind': + executeOperatorFind( + change.op, + change.find, + change.char, + change.count, + ctx, + ) + break + + case 'operatorTextObj': + executeOperatorTextObj( + change.op, + change.scope, + change.objType, + change.count, + ctx, + ) + break + } + } + + function handleVimInput(rawInput: string, key: Key): void { + const state = vimStateRef.current + // Run inputFilter in all modes so stateful filters disarm on any key, + // but only apply the transformed input in INSERT — NORMAL-mode command + // lookups expect single chars and a prepended space would break them. + const filtered = inputFilter ? inputFilter(rawInput, key) : rawInput + const input = state.mode === 'INSERT' ? filtered : rawInput + const cursor = Cursor.fromText(props.value, props.columns, textInput.offset) + + if (key.ctrl) { + textInput.onInput(input, key) + return + } + + // NOTE(keybindings): This escape handler is intentionally NOT migrated to the keybindings system. + // It's vim's standard INSERT->NORMAL mode switch - a vim-specific behavior that should not be + // configurable via keybindings. Vim users expect Esc to always exit INSERT mode. + if (key.escape && state.mode === 'INSERT') { + switchToNormalMode() + return + } + + // Escape in NORMAL mode cancels any pending command (replace, operator, etc.) + if (key.escape && state.mode === 'NORMAL') { + vimStateRef.current = { mode: 'NORMAL', command: { type: 'idle' } } + return + } + + // Pass Enter to base handler regardless of mode (allows submission from NORMAL) + if (key.return) { + textInput.onInput(input, key) + return + } + + if (state.mode === 'INSERT') { + // Track inserted text for dot-repeat + if (key.backspace || key.delete) { + if (state.insertedText.length > 0) { + vimStateRef.current = { + mode: 'INSERT', + insertedText: state.insertedText.slice( + 0, + -(lastGrapheme(state.insertedText).length || 1), + ), + } + } + } else { + vimStateRef.current = { + mode: 'INSERT', + insertedText: state.insertedText + input, + } + } + textInput.onInput(input, key) + return + } + + if (state.mode !== 'NORMAL') { + return + } + + // In idle state, delegate arrow keys to base handler for cursor movement + // and history fallback (upOrHistoryUp / downOrHistoryDown) + if ( + state.command.type === 'idle' && + (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) + ) { + textInput.onInput(input, key) + return + } + + const ctx: TransitionContext = { + ...createOperatorContext(cursor, false), + onUndo: props.onUndo, + onDotRepeat: replayLastChange, + } + + // Backspace/Delete are only mapped in motion-expecting states. In + // literal-char states (replace, find, operatorFind), mapping would turn + // r+Backspace into "replace with h" and df+Delete into "delete to next x". + // Delete additionally skips count state: in vim, N removes a count + // digit rather than executing Nx; we don't implement digit removal but + // should at least not turn a cancel into a destructive Nx. + const expectsMotion = + state.command.type === 'idle' || + state.command.type === 'count' || + state.command.type === 'operator' || + state.command.type === 'operatorCount' + + // Map arrow keys to vim motions in NORMAL mode + let vimInput = input + if (key.leftArrow) vimInput = 'h' + else if (key.rightArrow) vimInput = 'l' + else if (key.upArrow) vimInput = 'k' + else if (key.downArrow) vimInput = 'j' + else if (expectsMotion && key.backspace) vimInput = 'h' + else if (expectsMotion && state.command.type !== 'count' && key.delete) + vimInput = 'x' + + const result = transition(state.command, vimInput, ctx) + + if (result.execute) { + result.execute() + } + + // Update command state (only if execute didn't switch to INSERT) + if (vimStateRef.current.mode === 'NORMAL') { + if (result.next) { + vimStateRef.current = { mode: 'NORMAL', command: result.next } + } else if (result.execute) { + vimStateRef.current = { mode: 'NORMAL', command: { type: 'idle' } } + } + } + + if ( + input === '?' && + state.mode === 'NORMAL' && + state.command.type === 'idle' + ) { + props.onChange('?') + } + } + + const setModeExternal = useCallback( + (newMode: VimMode) => { + if (newMode === 'INSERT') { + vimStateRef.current = { mode: 'INSERT', insertedText: '' } + } else { + vimStateRef.current = { mode: 'NORMAL', command: { type: 'idle' } } + } + setMode(newMode) + onModeChange?.(newMode) + }, + [onModeChange], + ) + + return { + ...textInput, + onInput: handleVimInput, + mode, + setMode: setModeExternal, + } +} diff --git a/hooks/useVirtualScroll.ts b/hooks/useVirtualScroll.ts new file mode 100644 index 0000000..388b0ba --- /dev/null +++ b/hooks/useVirtualScroll.ts @@ -0,0 +1,721 @@ +import type { RefObject } from 'react' +import { + useCallback, + useDeferredValue, + useLayoutEffect, + useMemo, + useRef, + useSyncExternalStore, +} from 'react' +import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js' +import type { DOMElement } from '../ink/dom.js' + +/** + * Estimated height (rows) for items not yet measured. Intentionally LOW: + * overestimating causes blank space (we stop mounting too early and the + * viewport bottom shows empty spacer), while underestimating just mounts + * a few extra items into overscan. The asymmetry means we'd rather err low. + */ +const DEFAULT_ESTIMATE = 3 +/** + * Extra rows rendered above and below the viewport. Generous because real + * heights can be 10x the estimate for long tool results. + */ +const OVERSCAN_ROWS = 80 +/** Items rendered before the ScrollBox has laid out (viewportHeight=0). */ +const COLD_START_COUNT = 30 +/** + * scrollTop quantization for the useSyncExternalStore snapshot. Without + * this, every wheel tick (3-5 per notch) triggers a full React commit + + * Yoga calculateLayout() + Ink diff cycle — the CPU spike. Visual scroll + * stays smooth regardless: ScrollBox.forceRender fires on every scrollBy + * and Ink reads the REAL scrollTop from the DOM node, independent of what + * React thinks. React only needs to re-render when the mounted range must + * shift; half of OVERSCAN_ROWS is the tightest safe bin (guarantees ≥40 + * rows of overscan remain before the new range is needed). + */ +const SCROLL_QUANTUM = OVERSCAN_ROWS >> 1 +/** + * Worst-case height assumed for unmeasured items when computing coverage. + * A MessageRow can be as small as 1 row (single-line tool call). Using 1 + * here guarantees the mounted span physically reaches the viewport bottom + * regardless of how small items actually are — at the cost of over-mounting + * when items are larger (which is fine, overscan absorbs it). + */ +const PESSIMISTIC_HEIGHT = 1 +/** Cap on mounted items to bound fiber allocation even in degenerate cases. */ +const MAX_MOUNTED_ITEMS = 300 +/** + * Max NEW items to mount in a single commit. Scrolling into a fresh range + * with PESSIMISTIC_HEIGHT=1 would mount 194 items at once (OVERSCAN_ROWS*2+ + * viewportH = 194); each fresh MessageRow render costs ~1.5ms (marked lexer + * + formatToken + ~11 createInstance) = ~290ms sync block. Sliding the range + * toward the target over multiple commits keeps per-commit mount cost + * bounded. The render-time clamp (scrollClampMin/Max) holds the viewport at + * the edge of mounted content so there's no blank during catch-up. + */ +const SLIDE_STEP = 25 + +const NOOP_UNSUB = () => {} + +export type VirtualScrollResult = { + /** [startIndex, endIndex) half-open slice of items to render. */ + range: readonly [number, number] + /** Height (rows) of spacer before the first rendered item. */ + topSpacer: number + /** Height (rows) of spacer after the last rendered item. */ + bottomSpacer: number + /** + * Callback ref factory. Attach `measureRef(itemKey)` to each rendered + * item's root Box; after Yoga layout, the computed height is cached. + */ + measureRef: (key: string) => (el: DOMElement | null) => void + /** + * Attach to the topSpacer Box. Its Yoga computedTop IS listOrigin + * (first child of the virtualized region, so its top = cumulative + * height of everything rendered before the list in the ScrollBox). + * Drift-free: no subtraction of offsets, no dependence on item + * heights that change between renders (tmux resize). + */ + spacerRef: RefObject + /** + * Cumulative y-offset of each item in list-wrapper coords (NOT scrollbox + * coords — logo/siblings before this list shift the origin). + * offsets[i] = rows above item i; offsets[n] = totalHeight. + * Recomputed every render — don't memo on identity. + */ + offsets: ArrayLike + /** + * Read Yoga computedTop for item at index. Returns -1 if the item isn't + * mounted or hasn't been laid out. Item Boxes are direct Yoga children + * of the ScrollBox content wrapper (fragments collapse in the Ink DOM), + * so this is content-wrapper-relative — same coordinate space as + * scrollTop. Yoga layout is scroll-independent (translation happens + * later in renderNodeToOutput), so positions stay valid across scrolls + * without waiting for Ink to re-render. StickyTracker walks the mount + * range with this to find the viewport boundary at per-scroll-tick + * granularity (finer than the 40-row quantum this hook re-renders at). + */ + getItemTop: (index: number) => number + /** + * Get the mounted DOMElement for item at index, or null. For + * ScrollBox.scrollToElement — anchoring by element ref defers the + * Yoga-position read to render time (deterministic; no throttle race). + */ + getItemElement: (index: number) => DOMElement | null + /** Measured Yoga height. undefined = not yet measured; 0 = rendered nothing. */ + getItemHeight: (index: number) => number | undefined + /** + * Scroll so item `i` is in the mounted range. Sets scrollTop = + * offsets[i] + listOrigin. The range logic finds start from + * scrollTop vs offsets[] — BOTH use the same offsets value, so they + * agree by construction regardless of whether offsets[i] is the + * "true" position. Item i mounts; its screen position may be off by + * a few-dozen rows (overscan-worth of estimate drift), but it's in + * the DOM. Follow with getItemTop(i) for the precise position. + */ + scrollToIndex: (i: number) => void +} + +/** + * React-level virtualization for items inside a ScrollBox. + * + * The ScrollBox already does Ink-output-level viewport culling + * (render-node-to-output.ts:617 skips children outside the visible window), + * but all React fibers + Yoga nodes are still allocated. At ~250 KB RSS per + * MessageRow, a 1000-message session costs ~250 MB of grow-only memory + * (Ink screen buffer, WASM linear memory, JSC page retention all grow-only). + * + * This hook mounts only items in viewport + overscan. Spacer boxes hold the + * scroll height constant for the rest at O(1) fiber cost each. + * + * Height estimation: fixed DEFAULT_ESTIMATE for unmeasured items, replaced + * by real Yoga heights after first layout. No scroll anchoring — overscan + * absorbs estimate errors. If drift is noticeable in practice, anchoring + * (scrollBy(delta) when topSpacer changes) is a straightforward followup. + * + * stickyScroll caveat: render-node-to-output.ts:450 sets scrollTop=maxScroll + * during Ink's render phase, which does NOT fire ScrollBox.subscribe. The + * at-bottom check below handles this — when pinned to the bottom, we render + * the last N items regardless of what scrollTop claims. + */ +export function useVirtualScroll( + scrollRef: RefObject, + itemKeys: readonly string[], + /** + * Terminal column count. On change, cached heights are stale (text + * rewraps) — SCALED by oldCols/newCols rather than cleared. Clearing + * made the pessimistic coverage back-walk mount ~190 items (every + * uncached item → PESSIMISTIC_HEIGHT=1 → walk 190 to reach + * viewport+2×overscan). Each fresh mount runs marked.lexer + syntax + * highlighting ≈ 3ms; ~600ms React reconcile on first resize with a + * long conversation. Scaling keeps heightCache populated → back-walk + * uses real-ish heights → mount range stays tight. Scaled estimates + * are overwritten by real Yoga heights on next useLayoutEffect. + * + * Scaled heights are close enough that the black-screen-on-widen bug + * (inflated pre-resize offsets overshoot post-resize scrollTop → end + * loop stops short of tail) doesn't trigger: ratio<1 on widen scales + * heights DOWN, keeping offsets roughly aligned with post-resize Yoga. + */ + columns: number, +): VirtualScrollResult { + const heightCache = useRef(new Map()) + // Bump whenever heightCache mutates so offsets rebuild on next read. Ref + // (not state) — checked during render phase, zero extra commits. + const offsetVersionRef = useRef(0) + // scrollTop at last commit, for detecting fast-scroll mode (slide cap gate). + const lastScrollTopRef = useRef(0) + const offsetsRef = useRef<{ arr: Float64Array; version: number; n: number }>({ + arr: new Float64Array(0), + version: -1, + n: -1, + }) + const itemRefs = useRef(new Map()) + const refCache = useRef(new Map void>()) + // Inline ref-compare: must run before offsets is computed below. The + // skip-flag guards useLayoutEffect from re-populating heightCache with + // PRE-resize Yoga heights (useLayoutEffect reads Yoga from the frame + // BEFORE this render's calculateLayout — the one that had the old width). + // Next render's useLayoutEffect reads post-resize Yoga → correct. + const prevColumns = useRef(columns) + const skipMeasurementRef = useRef(false) + // Freeze the mount range for the resize-settling cycle. Already-mounted + // items have warm useMemo (marked.lexer, highlighting); recomputing range + // from scaled/pessimistic estimates causes mount/unmount churn (~3ms per + // fresh mount = ~150ms visible as a second flash). The pre-resize range is + // as good as any — items visible at old width are what the user wants at + // new width. Frozen for 2 renders: render #1 has skipMeasurement (Yoga + // still pre-resize), render #2's useLayoutEffect reads post-resize Yoga + // into heightCache. Render #3 has accurate heights → normal recompute. + const prevRangeRef = useRef(null) + const freezeRendersRef = useRef(0) + if (prevColumns.current !== columns) { + const ratio = prevColumns.current / columns + prevColumns.current = columns + for (const [k, h] of heightCache.current) { + heightCache.current.set(k, Math.max(1, Math.round(h * ratio))) + } + offsetVersionRef.current++ + skipMeasurementRef.current = true + freezeRendersRef.current = 2 + } + const frozenRange = freezeRendersRef.current > 0 ? prevRangeRef.current : null + // List origin in content-wrapper coords. scrollTop is content-wrapper- + // relative, but offsets[] are list-local (0 = first virtualized item). + // Siblings that render BEFORE this list inside the ScrollBox — Logo, + // StatusNotices, truncation divider in Messages.tsx — shift item Yoga + // positions by their cumulative height. Without subtracting this, the + // non-sticky branch's effLo/effHi are inflated and start advances past + // items that are actually in view (blank viewport on click/scroll when + // sticky breaks while scrollTop is near max). Read from the topSpacer's + // Yoga computedTop — it's the first child of the virtualized region, so + // its top IS listOrigin. No subtraction of offsets → no drift when item + // heights change between renders (tmux resize: columns change → re-wrap + // → heights shrink → the old item-sample subtraction went negative → + // effLo inflated → black screen). One-frame lag like heightCache. + const listOriginRef = useRef(0) + const spacerRef = useRef(null) + + // useSyncExternalStore ties re-renders to imperative scroll. Snapshot is + // scrollTop QUANTIZED to SCROLL_QUANTUM bins — Object.is sees no change + // for small scrolls (most wheel ticks), so React skips the commit + Yoga + // + Ink cycle entirely until the accumulated delta crosses a bin. + // Sticky is folded into the snapshot (sign bit) so sticky→broken also + // triggers: scrollToBottom sets sticky=true without moving scrollTop + // (Ink moves it later), and the first scrollBy after may land in the + // same bin. NaN sentinel = ref not attached. + const subscribe = useCallback( + (listener: () => void) => + scrollRef.current?.subscribe(listener) ?? NOOP_UNSUB, + [scrollRef], + ) + useSyncExternalStore(subscribe, () => { + const s = scrollRef.current + if (!s) return NaN + // Snapshot uses the TARGET (scrollTop + pendingDelta), not committed + // scrollTop. scrollBy only mutates pendingDelta (renderer drains it + // across frames); committed scrollTop lags. Using target means + // notify() on scrollBy actually changes the snapshot → React remounts + // children for the destination before Ink's drain frames need them. + const target = s.getScrollTop() + s.getPendingDelta() + const bin = Math.floor(target / SCROLL_QUANTUM) + return s.isSticky() ? ~bin : bin + }) + // Read the REAL committed scrollTop (not quantized) for range math — + // quantization is only the re-render gate, not the position. + const scrollTop = scrollRef.current?.getScrollTop() ?? -1 + // Range must span BOTH committed scrollTop (where Ink is rendering NOW) + // and target (where pending will drain to). During drain, intermediate + // frames render at scrollTops between the two — if we only mount for + // the target, those frames find no children (blank rows). + const pendingDelta = scrollRef.current?.getPendingDelta() ?? 0 + const viewportH = scrollRef.current?.getViewportHeight() ?? 0 + // True means the ScrollBox is pinned to the bottom. This is the ONLY + // stable "at bottom" signal: scrollTop/scrollHeight both reflect the + // PREVIOUS render's layout, which depends on what WE rendered (topSpacer + + // items), creating a feedback loop (range → layout → atBottom → range). + // stickyScroll is set by user action (scrollToBottom/scrollBy), the initial + // attribute, AND by render-node-to-output when its positional follow fires + // (scrollTop>=prevMax → pin to new max → set flag). The renderer write is + // feedback-safe: it only flips false→true, only when already at the + // positional bottom, and the flag being true here just means "tail-walk, + // clear clamp" — the same behavior as if we'd read scrollTop==maxScroll + // directly, minus the instability. Default true: before the ref attaches, + // assume bottom (sticky will pin us there on first Ink render). + const isSticky = scrollRef.current?.isSticky() ?? true + + // GC stale cache entries (compaction, /clear, screenToggleId bump). Only + // runs when itemKeys identity changes — scrolling doesn't touch keys. + // itemRefs self-cleans via ref(null) on unmount. + // eslint-disable-next-line react-hooks/exhaustive-deps -- refs are stable + useMemo(() => { + const live = new Set(itemKeys) + let dirty = false + for (const k of heightCache.current.keys()) { + if (!live.has(k)) { + heightCache.current.delete(k) + dirty = true + } + } + for (const k of refCache.current.keys()) { + if (!live.has(k)) refCache.current.delete(k) + } + if (dirty) offsetVersionRef.current++ + }, [itemKeys]) + + // Offsets cached across renders, invalidated by offsetVersion ref bump. + // The previous approach allocated new Array(n+1) + ran n Map.get per + // render; for n≈27k at key-repeat scroll rate (~11 commits/sec) that's + // ~300k lookups/sec on a freshly-allocated array → GC churn + ~2ms/render. + // Version bumped by heightCache writers (measureRef, resize-scale, GC). + // No setState — the rebuild is read-side-lazy via ref version check during + // render (same commit, zero extra schedule). The flicker that forced + // inline-recompute came from setState-driven invalidation. + const n = itemKeys.length + if ( + offsetsRef.current.version !== offsetVersionRef.current || + offsetsRef.current.n !== n + ) { + const arr = + offsetsRef.current.arr.length >= n + 1 + ? offsetsRef.current.arr + : new Float64Array(n + 1) + arr[0] = 0 + for (let i = 0; i < n; i++) { + arr[i + 1] = + arr[i]! + (heightCache.current.get(itemKeys[i]!) ?? DEFAULT_ESTIMATE) + } + offsetsRef.current = { arr, version: offsetVersionRef.current, n } + } + const offsets = offsetsRef.current.arr + const totalHeight = offsets[n]! + + let start: number + let end: number + + if (frozenRange) { + // Column just changed. Keep the pre-resize range to avoid mount churn. + // Clamp to n in case messages were removed (/clear, compaction). + ;[start, end] = frozenRange + start = Math.min(start, n) + end = Math.min(end, n) + } else if (viewportH === 0 || scrollTop < 0) { + // Cold start: ScrollBox hasn't laid out yet. Render the tail — sticky + // scroll pins to the bottom on first Ink render, so these are the items + // the user actually sees. Any scroll-up after that goes through + // scrollBy → subscribe fires → we re-render with real values. + start = Math.max(0, n - COLD_START_COUNT) + end = n + } else { + if (isSticky) { + // Sticky-scroll fallback. render-node-to-output may have moved scrollTop + // without notifying us, so trust "at bottom" over the stale snapshot. + // Walk back from the tail until we've covered viewport + overscan. + const budget = viewportH + OVERSCAN_ROWS + start = n + while (start > 0 && totalHeight - offsets[start - 1]! < budget) { + start-- + } + end = n + } else { + // User has scrolled up. Compute start from offsets (estimate-based: + // may undershoot which is fine — we just start mounting a bit early). + // Then extend end by CUMULATIVE BEST-KNOWN HEIGHT, not estimated + // offsets. The invariant is: + // topSpacer + sum(real_heights[start..end]) >= scrollTop + viewportH + overscan + // Since topSpacer = offsets[start] ≤ scrollTop - overscan, we need: + // sum(real_heights) >= viewportH + 2*overscan + // For unmeasured items, assume PESSIMISTIC_HEIGHT=1 — the smallest a + // MessageRow can be. This over-mounts when items are large, but NEVER + // leaves the viewport showing empty spacer during fast scroll through + // unmeasured territory. Once heights are cached (next render), + // coverage is computed with real values and the range tightens. + // Advance start past item K only if K is safe to fold into topSpacer + // without a visible jump. Two cases are safe: + // (a) K is NOT currently mounted (itemRefs has no entry). Its + // contribution to offsets has ALWAYS been the estimate — the + // spacer already matches what was there. No layout change. + // (b) K is mounted AND its height is cached. offsets[start+1] uses + // the real height, so topSpacer = offsets[start+1] exactly + // equals the Yoga span K occupied. Seamless unmount. + // The unsafe case — K is mounted but uncached — is the one-render + // window between mount and useLayoutEffect measurement. Keeping K + // mounted that one extra render lets the measurement land. + // Mount range spans [committed, target] so every drain frame is + // covered. Clamp at 0: aggressive wheel-up can push pendingDelta + // far past zero (MX Master free-spin), but scrollTop never goes + // negative. Without the clamp, effLo drags start to 0 while effHi + // stays at the current (high) scrollTop — span exceeds what + // MAX_MOUNTED_ITEMS can cover and early drain frames see blank. + // listOrigin translates scrollTop (content-wrapper coords) into + // list-local coords before comparing against offsets[]. Without + // this, pre-list siblings (Logo+notices in Messages.tsx) inflate + // scrollTop by their height and start over-advances — eats overscan + // first, then visible rows once the inflation exceeds OVERSCAN_ROWS. + const listOrigin = listOriginRef.current + // Cap the [committed..target] span. When input outpaces render, + // pendingDelta grows unbounded → effLo..effHi covers hundreds of + // unmounted rows → one commit mounts 194 fresh MessageRows → 3s+ + // sync block → more input queues → bigger delta next time. Death + // spiral. Capping the span bounds fresh mounts per commit; the + // clamp (setClampBounds) shows edge-of-mounted during catch-up so + // there's no blank screen — scroll reaches target over a few + // frames instead of freezing once for seconds. + const MAX_SPAN_ROWS = viewportH * 3 + const rawLo = Math.min(scrollTop, scrollTop + pendingDelta) + const rawHi = Math.max(scrollTop, scrollTop + pendingDelta) + const span = rawHi - rawLo + const clampedLo = + span > MAX_SPAN_ROWS + ? pendingDelta < 0 + ? rawHi - MAX_SPAN_ROWS // scrolling up: keep near target (low end) + : rawLo // scrolling down: keep near committed + : rawLo + const clampedHi = clampedLo + Math.min(span, MAX_SPAN_ROWS) + const effLo = Math.max(0, clampedLo - listOrigin) + const effHi = clampedHi - listOrigin + const lo = effLo - OVERSCAN_ROWS + // Binary search for start — offsets is monotone-increasing. The + // linear while(start++) scan iterated ~27k times per render for the + // 27k-msg session (scrolling from bottom, start≈27200). O(log n). + { + let l = 0 + let r = n + while (l < r) { + const m = (l + r) >> 1 + if (offsets[m + 1]! <= lo) l = m + 1 + else r = m + } + start = l + } + // Guard: don't advance past mounted-but-unmeasured items. During the + // one-render window between mount and useLayoutEffect measurement, + // unmounting such items would use DEFAULT_ESTIMATE in topSpacer, + // which doesn't match their (unknown) real span → flicker. Mounted + // items are in [prevStart, prevEnd); scan that, not all n. + { + const p = prevRangeRef.current + if (p && p[0] < start) { + for (let i = p[0]; i < Math.min(start, p[1]); i++) { + const k = itemKeys[i]! + if (itemRefs.current.has(k) && !heightCache.current.has(k)) { + start = i + break + } + } + } + } + + const needed = viewportH + 2 * OVERSCAN_ROWS + const maxEnd = Math.min(n, start + MAX_MOUNTED_ITEMS) + let coverage = 0 + end = start + while ( + end < maxEnd && + (coverage < needed || offsets[end]! < effHi + viewportH + OVERSCAN_ROWS) + ) { + coverage += + heightCache.current.get(itemKeys[end]!) ?? PESSIMISTIC_HEIGHT + end++ + } + } + // Same coverage guarantee for the atBottom path (it walked start back + // by estimated offsets, which can undershoot if items are small). + const needed = viewportH + 2 * OVERSCAN_ROWS + const minStart = Math.max(0, end - MAX_MOUNTED_ITEMS) + let coverage = 0 + for (let i = start; i < end; i++) { + coverage += heightCache.current.get(itemKeys[i]!) ?? PESSIMISTIC_HEIGHT + } + while (start > minStart && coverage < needed) { + start-- + coverage += + heightCache.current.get(itemKeys[start]!) ?? PESSIMISTIC_HEIGHT + } + // Slide cap: limit how many NEW items mount this commit. Scrolling into + // a fresh range would otherwise mount 194 items at PESSIMISTIC_HEIGHT=1 + // coverage — ~290ms React render block. Gates on scroll VELOCITY + // (|scrollTop delta since last commit| > 2×viewportH — key-repeat PageUp + // moves ~viewportH/2 per press, 3+ presses batched = fast mode). Covers + // both scrollBy (pendingDelta) and scrollTo (direct write). Normal + // single-PageUp or sticky-break jumps skip this. The clamp + // (setClampBounds) holds the viewport at the mounted edge during + // catch-up. Only caps range GROWTH; shrinking is unbounded. + const prev = prevRangeRef.current + const scrollVelocity = + Math.abs(scrollTop - lastScrollTopRef.current) + Math.abs(pendingDelta) + if (prev && scrollVelocity > viewportH * 2) { + const [pS, pE] = prev + if (start < pS - SLIDE_STEP) start = pS - SLIDE_STEP + if (end > pE + SLIDE_STEP) end = pE + SLIDE_STEP + // A large forward jump can push start past the capped end (start + // advances via binary search while end is capped at pE + SLIDE_STEP). + // Mount SLIDE_STEP items from the new start so the viewport isn't + // blank during catch-up. + if (start > end) end = Math.min(start + SLIDE_STEP, n) + } + lastScrollTopRef.current = scrollTop + } + + // Decrement freeze AFTER range is computed. Don't update prevRangeRef + // during freeze so both frozen renders reuse the ORIGINAL pre-resize + // range (not the clamped-to-n version if messages changed mid-freeze). + if (freezeRendersRef.current > 0) { + freezeRendersRef.current-- + } else { + prevRangeRef.current = [start, end] + } + // useDeferredValue lets React render with the OLD range first (cheap — + // all memo hits) then transition to the NEW range (expensive — fresh + // mounts with marked.lexer + formatToken). The urgent render keeps Ink + // painting at input rate; fresh mounts happen in a non-blocking + // background render. This is React's native time-slicing: the 62ms + // fresh-mount block becomes interruptible. The clamp (setClampBounds) + // already handles viewport pinning so there's no visual artifact from + // the deferred range lagging briefly behind scrollTop. + // + // Only defer range GROWTH (start moving earlier / end moving later adds + // fresh mounts). Shrinking is cheap (unmount = remove fiber, no parse) + // and the deferred value lagging shrink causes stale overscan to stay + // mounted one extra tick — harmless but fails tests checking exact + // range after measurement-driven tightening. + const dStart = useDeferredValue(start) + const dEnd = useDeferredValue(end) + let effStart = start < dStart ? dStart : start + let effEnd = end > dEnd ? dEnd : end + // A large jump can make effStart > effEnd (start jumps forward while dEnd + // still holds the old range's end). Skip deferral to avoid an inverted + // range. Also skip when sticky — scrollToBottom needs the tail mounted + // NOW so scrollTop=maxScroll lands on content, not bottomSpacer. The + // deferred dEnd (still at old range) would render an incomplete tail, + // maxScroll stays at the old content height, and "jump to bottom" stops + // short. Sticky snap is a single frame, not continuous scroll — the + // time-slicing benefit doesn't apply. + if (effStart > effEnd || isSticky) { + effStart = start + effEnd = end + } + // Scrolling DOWN (pendingDelta > 0): bypass effEnd deferral so the tail + // mounts immediately. Without this, the clamp (based on effEnd) holds + // scrollTop short of the real bottom — user scrolls down, hits clampMax, + // stops, React catches up effEnd, clampMax widens, but the user already + // released. Feels stuck-before-bottom. effStart stays deferred so + // scroll-UP keeps time-slicing (older messages parse on mount — the + // expensive direction). + if (pendingDelta > 0) { + effEnd = end + } + // Final O(viewport) enforcement. The intermediate caps (maxEnd=start+ + // MAX_MOUNTED_ITEMS, slide cap, deferred-intersection) bound [start,end] + // but the deferred+bypass combinations above can let [effStart,effEnd] + // slip: e.g. during sustained PageUp when concurrent mode interleaves + // dStart updates with effEnd=end bypasses across commits, the effective + // window can drift wider than either immediate or deferred alone. On a + // 10K-line resumed session this showed as +270MB RSS during PageUp spam + // (yoga Node constructor + createWorkInProgress fiber alloc proportional + // to scroll distance). Trim the far edge — by viewport position — to keep + // fiber count O(viewport) regardless of deferred-value scheduling. + if (effEnd - effStart > MAX_MOUNTED_ITEMS) { + // Trim side is decided by viewport POSITION, not pendingDelta direction. + // pendingDelta drains to 0 between frames while dStart/dEnd lag under + // concurrent scheduling; a direction-based trim then flips from "trim + // tail" to "trim head" mid-settle, bumping effStart → effTopSpacer → + // clampMin → setClampBounds yanks scrollTop down → scrollback vanishes. + // Position-based: keep whichever end the viewport is closer to. + const mid = (offsets[effStart]! + offsets[effEnd]!) / 2 + if (scrollTop - listOriginRef.current < mid) { + effEnd = effStart + MAX_MOUNTED_ITEMS + } else { + effStart = effEnd - MAX_MOUNTED_ITEMS + } + } + + // Write render-time clamp bounds in a layout effect (not during render — + // mutating DOM during React render violates purity). render-node-to-output + // clamps scrollTop to this span so burst scrollTo calls that race past + // React's async re-render show the EDGE of mounted content (the last/first + // visible message) instead of blank spacer. + // + // Clamp MUST use the EFFECTIVE (deferred) range, not the immediate one. + // During fast scroll, immediate [start,end] may already cover the new + // scrollTop position, but the children still render at the deferred + // (older) range. If clamp uses immediate bounds, the drain-gate in + // render-node-to-output sees scrollTop within clamp → drains past the + // deferred children's span → viewport lands in spacer → white flash. + // Using effStart/effEnd keeps clamp synced with what's actually mounted. + // + // Skip clamp when sticky — render-node-to-output pins scrollTop=maxScroll + // authoritatively. Clamping during cold-start/load causes flicker: first + // render uses estimate-based offsets, clamp set, sticky-follow moves + // scrollTop, measurement fires, offsets rebuild with real heights, second + // render's clamp differs → scrollTop clamp-adjusts → content shifts. + const listOrigin = listOriginRef.current + const effTopSpacer = offsets[effStart]! + // At effStart=0 there's no unmounted content above — the clamp must allow + // scrolling past listOrigin to see pre-list content (logo, header) that + // sits in the ScrollBox but outside VirtualMessageList. Only clamp when + // the topSpacer is nonzero (there ARE unmounted items above). + const clampMin = effStart === 0 ? 0 : effTopSpacer + listOrigin + // At effEnd=n there's no bottomSpacer — nothing to avoid racing past. Using + // offsets[n] here would bake in heightCache (one render behind Yoga), and + // when the tail item is STREAMING its cached height lags its real height by + // however much arrived since last measure. Sticky-break then clamps + // scrollTop below the real max, pushing the streaming text off-viewport + // (the "scrolled up, response disappeared" bug). Infinity = unbounded: + // render-node-to-output's own Math.min(cur, maxScroll) governs instead. + const clampMax = + effEnd === n + ? Infinity + : Math.max(effTopSpacer, offsets[effEnd]! - viewportH) + listOrigin + useLayoutEffect(() => { + if (isSticky) { + scrollRef.current?.setClampBounds(undefined, undefined) + } else { + scrollRef.current?.setClampBounds(clampMin, clampMax) + } + }) + + // Measure heights from the PREVIOUS Ink render. Runs every commit (no + // deps) because Yoga recomputes layout without React knowing. yogaNode + // heights for items mounted ≥1 frame ago are valid; brand-new items + // haven't been laid out yet (that happens in resetAfterCommit → onRender, + // after this effect). + // + // Distinguishing "h=0: Yoga hasn't run" (transient, skip) from "h=0: + // MessageRow rendered null" (permanent, cache it): getComputedWidth() > 0 + // proves Yoga HAS laid out this node (width comes from the container, + // always non-zero for a Box in a column). If width is set and height is + // 0, the item is genuinely empty — cache 0 so the start-advance gate + // doesn't block on it forever. Without this, a null-rendering message + // at the start boundary freezes the range (seen as blank viewport when + // scrolling down after scrolling up). + // + // NO setState. A setState here would schedule a second commit with + // shifted offsets, and since Ink writes stdout on every commit + // (reconciler.resetAfterCommit → onRender), that's two writes with + // different spacer heights → visible flicker. Heights propagate to + // offsets on the next natural render. One-frame lag, absorbed by overscan. + useLayoutEffect(() => { + const spacerYoga = spacerRef.current?.yogaNode + if (spacerYoga && spacerYoga.getComputedWidth() > 0) { + listOriginRef.current = spacerYoga.getComputedTop() + } + if (skipMeasurementRef.current) { + skipMeasurementRef.current = false + return + } + let anyChanged = false + for (const [key, el] of itemRefs.current) { + const yoga = el.yogaNode + if (!yoga) continue + const h = yoga.getComputedHeight() + const prev = heightCache.current.get(key) + if (h > 0) { + if (prev !== h) { + heightCache.current.set(key, h) + anyChanged = true + } + } else if (yoga.getComputedWidth() > 0 && prev !== 0) { + heightCache.current.set(key, 0) + anyChanged = true + } + } + if (anyChanged) offsetVersionRef.current++ + }) + + // Stable per-key callback refs. React's ref-swap dance (old(null) then + // new(el)) is a no-op when the callback is identity-stable, avoiding + // itemRefs churn on every render. GC'd alongside heightCache above. + // The ref(null) path also captures height at unmount — the yogaNode is + // still valid then (reconciler calls ref(null) before removeChild → + // freeRecursive), so we get the final measurement before WASM release. + const measureRef = useCallback((key: string) => { + let fn = refCache.current.get(key) + if (!fn) { + fn = (el: DOMElement | null) => { + if (el) { + itemRefs.current.set(key, el) + } else { + const yoga = itemRefs.current.get(key)?.yogaNode + if (yoga && !skipMeasurementRef.current) { + const h = yoga.getComputedHeight() + if ( + (h > 0 || yoga.getComputedWidth() > 0) && + heightCache.current.get(key) !== h + ) { + heightCache.current.set(key, h) + offsetVersionRef.current++ + } + } + itemRefs.current.delete(key) + } + } + refCache.current.set(key, fn) + } + return fn + }, []) + + const getItemTop = useCallback( + (index: number) => { + const yoga = itemRefs.current.get(itemKeys[index]!)?.yogaNode + if (!yoga || yoga.getComputedWidth() === 0) return -1 + return yoga.getComputedTop() + }, + [itemKeys], + ) + + const getItemElement = useCallback( + (index: number) => itemRefs.current.get(itemKeys[index]!) ?? null, + [itemKeys], + ) + const getItemHeight = useCallback( + (index: number) => heightCache.current.get(itemKeys[index]!), + [itemKeys], + ) + const scrollToIndex = useCallback( + (i: number) => { + // offsetsRef.current holds latest cached offsets (event handlers run + // between renders; a render-time closure would be stale). + const o = offsetsRef.current + if (i < 0 || i >= o.n) return + scrollRef.current?.scrollTo(o.arr[i]! + listOriginRef.current) + }, + [scrollRef], + ) + + const effBottomSpacer = totalHeight - offsets[effEnd]! + + return { + range: [effStart, effEnd], + topSpacer: effTopSpacer, + bottomSpacer: effBottomSpacer, + measureRef, + spacerRef, + offsets, + getItemTop, + getItemElement, + getItemHeight, + scrollToIndex, + } +} diff --git a/hooks/useVoice.ts b/hooks/useVoice.ts new file mode 100644 index 0000000..30c0991 --- /dev/null +++ b/hooks/useVoice.ts @@ -0,0 +1,1144 @@ +// React hook for hold-to-talk voice input using Anthropic voice_stream STT. +// +// Hold the keybinding to record; release to stop and submit. Auto-repeat +// key events reset an internal timer — when no keypress arrives within +// RELEASE_TIMEOUT_MS the recording stops automatically. Uses the native +// audio module (macOS) or SoX for recording, and Anthropic's voice_stream +// endpoint (conversation_engine) for STT. + +import { useCallback, useEffect, useRef, useState } from 'react' +import { useSetVoiceState } from '../context/voice.js' +import { useTerminalFocus } from '../ink/hooks/use-terminal-focus.js' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { getVoiceKeyterms } from '../services/voiceKeyterms.js' +import { + connectVoiceStream, + type FinalizeSource, + isVoiceStreamAvailable, + type VoiceStreamConnection, +} from '../services/voiceStreamSTT.js' +import { logForDebugging } from '../utils/debug.js' +import { toError } from '../utils/errors.js' +import { getSystemLocaleLanguage } from '../utils/intl.js' +import { logError } from '../utils/log.js' +import { getInitialSettings } from '../utils/settings/settings.js' +import { sleep } from '../utils/sleep.js' + +// ─── Language normalization ───────────────────────────────────────────── + +const DEFAULT_STT_LANGUAGE = 'en' + +// Maps language names (English and native) to BCP-47 codes supported by +// the voice_stream Deepgram backend. Keys must be lowercase. +// +// This list must be a SUBSET of the server-side supported_language_codes +// allowlist (GrowthBook: speech_to_text_voice_stream_config). +// If the CLI sends a code the server rejects, the WebSocket closes with +// 1008 "Unsupported language" and voice breaks. Unsupported languages +// fall back to DEFAULT_STT_LANGUAGE so recording still works. +const LANGUAGE_NAME_TO_CODE: Record = { + english: 'en', + spanish: 'es', + español: 'es', + espanol: 'es', + french: 'fr', + français: 'fr', + francais: 'fr', + japanese: 'ja', + 日本語: 'ja', + german: 'de', + deutsch: 'de', + portuguese: 'pt', + português: 'pt', + portugues: 'pt', + italian: 'it', + italiano: 'it', + korean: 'ko', + 한국어: 'ko', + hindi: 'hi', + हिन्दी: 'hi', + हिंदी: 'hi', + indonesian: 'id', + 'bahasa indonesia': 'id', + bahasa: 'id', + russian: 'ru', + русский: 'ru', + polish: 'pl', + polski: 'pl', + turkish: 'tr', + türkçe: 'tr', + turkce: 'tr', + dutch: 'nl', + nederlands: 'nl', + ukrainian: 'uk', + українська: 'uk', + greek: 'el', + ελληνικά: 'el', + czech: 'cs', + čeština: 'cs', + cestina: 'cs', + danish: 'da', + dansk: 'da', + swedish: 'sv', + svenska: 'sv', + norwegian: 'no', + norsk: 'no', +} + +// Subset of the GrowthBook speech_to_text_voice_stream_config allowlist. +// Sending a code not in the server allowlist closes the connection. +const SUPPORTED_LANGUAGE_CODES = new Set([ + 'en', + 'es', + 'fr', + 'ja', + 'de', + 'pt', + 'it', + 'ko', + 'hi', + 'id', + 'ru', + 'pl', + 'tr', + 'nl', + 'uk', + 'el', + 'cs', + 'da', + 'sv', + 'no', +]) + +// Normalize a language preference string (from settings.language) to a +// BCP-47 code supported by the voice_stream endpoint. Returns the +// default language if the input cannot be resolved. When the input is +// non-empty but unsupported, fellBackFrom is set to the original input so +// callers can surface a warning. +export function normalizeLanguageForSTT(language: string | undefined): { + code: string + fellBackFrom?: string +} { + if (!language) return { code: DEFAULT_STT_LANGUAGE } + const lower = language.toLowerCase().trim() + if (!lower) return { code: DEFAULT_STT_LANGUAGE } + if (SUPPORTED_LANGUAGE_CODES.has(lower)) return { code: lower } + const fromName = LANGUAGE_NAME_TO_CODE[lower] + if (fromName) return { code: fromName } + const base = lower.split('-')[0] + if (base && SUPPORTED_LANGUAGE_CODES.has(base)) return { code: base } + return { code: DEFAULT_STT_LANGUAGE, fellBackFrom: language } +} + +// Lazy-loaded voice module. We defer importing voice.ts (and its native +// audio-capture-napi dependency) until voice input is actually activated. +// On macOS, loading the native audio module can trigger a TCC microphone +// permission prompt — we must avoid that until voice input is actually enabled. +type VoiceModule = typeof import('../services/voice.js') +let voiceModule: VoiceModule | null = null + +type VoiceState = 'idle' | 'recording' | 'processing' + +type UseVoiceOptions = { + onTranscript: (text: string) => void + onError?: (message: string) => void + enabled: boolean + focusMode: boolean +} + +type UseVoiceReturn = { + state: VoiceState + handleKeyEvent: (fallbackMs?: number) => void +} + +// Gap (ms) between auto-repeat key events that signals key release. +// Terminal auto-repeat typically fires every 30-80ms; 200ms comfortably +// covers jitter while still feeling responsive. +const RELEASE_TIMEOUT_MS = 200 + +// Fallback (ms) to arm the release timer if no auto-repeat is seen. +// macOS default key repeat delay is ~500ms; 600ms gives headroom. +// If the user tapped and released before auto-repeat started, this +// ensures the release timer gets armed and recording stops. +// +// For modifier-combo first-press activation (handleKeyEvent called at +// t=0, before any auto-repeat), callers should pass FIRST_PRESS_FALLBACK_MS +// instead — the gap to the next keypress is the OS initial repeat *delay* +// (up to ~2s on macOS with slider at "Long"), not the repeat *rate*. +const REPEAT_FALLBACK_MS = 600 +export const FIRST_PRESS_FALLBACK_MS = 2000 + +// How long (ms) to keep a focus-mode session alive without any speech +// before tearing it down to free the WebSocket connection. Re-arms on +// the next focus cycle (blur → refocus). +const FOCUS_SILENCE_TIMEOUT_MS = 5_000 + +// Number of bars shown in the recording waveform visualizer. +const AUDIO_LEVEL_BARS = 16 + +// Compute RMS amplitude from a 16-bit signed PCM buffer and return a +// normalized 0-1 value. A sqrt curve spreads quieter levels across more +// of the visual range so the waveform uses the full set of block heights. +export function computeLevel(chunk: Buffer): number { + const samples = chunk.length >> 1 // 16-bit = 2 bytes per sample + if (samples === 0) return 0 + let sumSq = 0 + for (let i = 0; i < chunk.length - 1; i += 2) { + // Read 16-bit signed little-endian + const sample = ((chunk[i]! | (chunk[i + 1]! << 8)) << 16) >> 16 + sumSq += sample * sample + } + const rms = Math.sqrt(sumSq / samples) + const normalized = Math.min(rms / 2000, 1) + return Math.sqrt(normalized) +} + +export function useVoice({ + onTranscript, + onError, + enabled, + focusMode, +}: UseVoiceOptions): UseVoiceReturn { + const [state, setState] = useState('idle') + const stateRef = useRef('idle') + const connectionRef = useRef(null) + const accumulatedRef = useRef('') + const onTranscriptRef = useRef(onTranscript) + const onErrorRef = useRef(onError) + const cleanupTimerRef = useRef | null>(null) + const releaseTimerRef = useRef | null>(null) + // True once we've seen a second keypress (auto-repeat) while recording. + // The OS key repeat delay (~500ms on macOS) means the first keypress is + // solo — arming the release timer before auto-repeat starts would cause + // a false release. + const seenRepeatRef = useRef(false) + const repeatFallbackTimerRef = useRef | null>( + null, + ) + // True when the current recording session was started by terminal focus + // (not by a keypress). Focus-driven sessions end on blur, not key release. + const focusTriggeredRef = useRef(false) + // Timer that tears down the session after prolonged silence in focus mode. + const focusSilenceTimerRef = useRef | null>( + null, + ) + // Set when a focus-mode session is torn down due to silence. Prevents + // the focus effect from immediately restarting. Cleared on blur so the + // next focus cycle re-arms recording. + const silenceTimedOutRef = useRef(false) + const recordingStartRef = useRef(0) + // Incremented on each startRecordingSession(). Callbacks capture their + // generation and bail if a newer session has started — prevents a zombie + // slow-connecting WS from an abandoned session from overwriting + // connectionRef mid-way through the next session. + const sessionGenRef = useRef(0) + // True if the early-error retry fired during this session. + // Tracked for the tengu_voice_recording_completed analytics event. + const retryUsedRef = useRef(false) + // Full audio captured this session, kept for silent-drop replay. ~1% of + // sessions get a sticky-broken CE pod that accepts audio but returns zero + // transcripts (anthropics/anthropic#287008 session-sticky variant); when + // finalize() resolves via no_data_timeout with hadAudioSignal=true, we + // replay the buffer on a fresh WS once. Bounded: 32KB/s × ~60s max ≈ 2MB. + const fullAudioRef = useRef([]) + const silentDropRetriedRef = useRef(false) + // Bumped when the early-error retry is scheduled. Captured per + // attemptConnect — onError swallows stale-gen events (conn 1's + // trailing close-error) but surfaces current-gen ones (conn 2's + // genuine failure). Same shape as sessionGenRef, one level down. + const attemptGenRef = useRef(0) + // Running total of chars flushed in focus mode (each final transcript is + // injected immediately and accumulatedRef reset). Added to transcriptChars + // in the completed event so focus-mode sessions don't false-positive as + // silent-drops (transcriptChars=0 despite successful transcription). + const focusFlushedCharsRef = useRef(0) + // True if at least one audio chunk with non-trivial signal was received. + // Used to distinguish "microphone is silent/inaccessible" from "speech not detected". + const hasAudioSignalRef = useRef(false) + // True once onReady fired for the current session. Unlike connectionRef + // (which cleanup() nulls), this survives effect-order races where Effect 3 + // cleanup runs before Effect 2's finishRecording() — e.g. /voice toggled + // off mid-recording in focus mode. Used for the wsConnected analytics + // dimension and error-message branching. Reset in startRecordingSession. + const everConnectedRef = useRef(false) + const audioLevelsRef = useRef([]) + const isFocused = useTerminalFocus() + const setVoiceState = useSetVoiceState() + + // Keep callback refs current without triggering re-renders + onTranscriptRef.current = onTranscript + onErrorRef.current = onError + + function updateState(newState: VoiceState): void { + stateRef.current = newState + setState(newState) + setVoiceState(prev => { + if (prev.voiceState === newState) return prev + return { ...prev, voiceState: newState } + }) + } + + const cleanup = useCallback((): void => { + // Stale any in-flight session (main connection isStale(), replay + // isStale(), finishRecording continuation). Without this, disabling + // voice during the replay window lets the stale replay open a WS, + // accumulate transcript, and inject it after voice was torn down. + sessionGenRef.current++ + if (cleanupTimerRef.current) { + clearTimeout(cleanupTimerRef.current) + cleanupTimerRef.current = null + } + if (releaseTimerRef.current) { + clearTimeout(releaseTimerRef.current) + releaseTimerRef.current = null + } + if (repeatFallbackTimerRef.current) { + clearTimeout(repeatFallbackTimerRef.current) + repeatFallbackTimerRef.current = null + } + if (focusSilenceTimerRef.current) { + clearTimeout(focusSilenceTimerRef.current) + focusSilenceTimerRef.current = null + } + silenceTimedOutRef.current = false + voiceModule?.stopRecording() + if (connectionRef.current) { + connectionRef.current.close() + connectionRef.current = null + } + accumulatedRef.current = '' + audioLevelsRef.current = [] + fullAudioRef.current = [] + setVoiceState(prev => { + if (prev.voiceInterimTranscript === '' && !prev.voiceAudioLevels.length) + return prev + return { ...prev, voiceInterimTranscript: '', voiceAudioLevels: [] } + }) + }, [setVoiceState]) + + function finishRecording(): void { + logForDebugging( + '[voice] finishRecording: stopping recording, transitioning to processing', + ) + // Session ending — stale any in-flight attempt so its late onError + // (conn 2 responding after user released key) doesn't double-fire on + // top of the "check network" message below. + attemptGenRef.current++ + // Capture focusTriggered BEFORE clearing it — needed as an event dimension + // so BigQuery can filter out passive focus-mode auto-recordings (user focused + // terminal without speaking → ambient noise sets hadAudioSignal=true → false + // silent-drop signature). focusFlushedCharsRef fixes transcriptChars accuracy + // for sessions WITH speech; focusTriggered enables filtering sessions WITHOUT. + const focusTriggered = focusTriggeredRef.current + focusTriggeredRef.current = false + updateState('processing') + voiceModule?.stopRecording() + // Capture duration BEFORE the finalize round-trip so that the WebSocket + // wait time is not included (otherwise a quick tap looks like > 2s). + // All ref-backed values are captured here, BEFORE the async boundary — + // a keypress during the finalize wait can start a new session and reset + // these refs (e.g. focusFlushedCharsRef = 0 in startRecordingSession), + // reproducing the silent-drop false-positive this ref exists to prevent. + const recordingDurationMs = Date.now() - recordingStartRef.current + const hadAudioSignal = hasAudioSignalRef.current + const retried = retryUsedRef.current + const focusFlushedChars = focusFlushedCharsRef.current + // wsConnected distinguishes "backend received audio but dropped it" (the + // bug backend PR #287008 fixes) from "WS handshake never completed" — + // in the latter case audio is still in audioBuffer, never reached the + // server, but hasAudioSignalRef is already true from ambient noise. + const wsConnected = everConnectedRef.current + // Capture generation BEFORE the .then() — if a new session starts during + // the finalize wait, sessionGenRef has already advanced by the time the + // continuation runs, so capturing inside the .then() would yield the new + // session's gen and every staleness check would be a no-op. + const myGen = sessionGenRef.current + const isStale = () => sessionGenRef.current !== myGen + logForDebugging('[voice] Recording stopped') + + // Send finalize and wait for the WebSocket to close before reading the + // accumulated transcript. The close handler promotes any unreported + // interim text to final, so we must wait for it to fire. + const finalizePromise: Promise = + connectionRef.current + ? connectionRef.current.finalize() + : Promise.resolve(undefined) + + void finalizePromise + .then(async finalizeSource => { + if (isStale()) return + // Silent-drop replay: when the server accepted audio (wsConnected), + // the mic captured real signal (hadAudioSignal), but finalize timed + // out with zero transcript — the ~1% session-sticky CE-pod bug. + // Replay the buffered audio on a fresh connection once. A 250ms + // backoff clears the same-pod rapid-reconnect race (same gap as the + // early-error retry path below). + if ( + finalizeSource === 'no_data_timeout' && + hadAudioSignal && + wsConnected && + !focusTriggered && + focusFlushedChars === 0 && + accumulatedRef.current.trim() === '' && + !silentDropRetriedRef.current && + fullAudioRef.current.length > 0 + ) { + silentDropRetriedRef.current = true + logForDebugging( + `[voice] Silent-drop detected (no_data_timeout, ${String(fullAudioRef.current.length)} chunks); replaying on fresh connection`, + ) + logEvent('tengu_voice_silent_drop_replay', { + recordingDurationMs, + chunkCount: fullAudioRef.current.length, + }) + if (connectionRef.current) { + connectionRef.current.close() + connectionRef.current = null + } + const replayBuffer = fullAudioRef.current + await sleep(250) + if (isStale()) return + const stt = normalizeLanguageForSTT(getInitialSettings().language) + const keyterms = await getVoiceKeyterms() + if (isStale()) return + await new Promise(resolve => { + void connectVoiceStream( + { + onTranscript: (t, isFinal) => { + if (isStale()) return + if (isFinal && t.trim()) { + if (accumulatedRef.current) accumulatedRef.current += ' ' + accumulatedRef.current += t.trim() + } + }, + onError: () => resolve(), + onClose: () => {}, + onReady: conn => { + if (isStale()) { + conn.close() + resolve() + return + } + connectionRef.current = conn + const SLICE = 32_000 + let slice: Buffer[] = [] + let bytes = 0 + for (const c of replayBuffer) { + if (bytes > 0 && bytes + c.length > SLICE) { + conn.send(Buffer.concat(slice)) + slice = [] + bytes = 0 + } + slice.push(c) + bytes += c.length + } + if (slice.length) conn.send(Buffer.concat(slice)) + void conn.finalize().then(() => { + conn.close() + resolve() + }) + }, + }, + { language: stt.code, keyterms }, + ).then( + c => { + if (!c) resolve() + }, + () => resolve(), + ) + }) + if (isStale()) return + } + fullAudioRef.current = [] + + const text = accumulatedRef.current.trim() + logForDebugging( + `[voice] Final transcript assembled (${String(text.length)} chars): "${text.slice(0, 200)}"`, + ) + + // Tracks silent-drop rate: transcriptChars=0 + hadAudioSignal=true + // + recordingDurationMs>2000 = the bug backend PR #287008 fixes. + // focusFlushedCharsRef makes transcriptChars accurate for focus mode + // (where each final is injected immediately and accumulatedRef reset). + // + // NOTE: this fires only on the finishRecording() path. The onError + // fallthrough and !conn (no-OAuth) paths bypass this → don't compute + // COUNT(completed)/COUNT(started) as a success rate; the silent-drop + // denominator (completed events only) is internally consistent. + logEvent('tengu_voice_recording_completed', { + transcriptChars: text.length + focusFlushedChars, + recordingDurationMs, + hadAudioSignal, + retried, + silentDropRetried: silentDropRetriedRef.current, + wsConnected, + focusTriggered, + }) + + if (connectionRef.current) { + connectionRef.current.close() + connectionRef.current = null + } + + if (text) { + logForDebugging( + `[voice] Injecting transcript (${String(text.length)} chars)`, + ) + onTranscriptRef.current(text) + } else if (focusFlushedChars === 0 && recordingDurationMs > 2000) { + // Only warn about empty transcript if nothing was flushed in focus + // mode either, and recording was > 2s (short recordings = accidental + // taps → silently return to idle). + if (!wsConnected) { + // WS never connected → audio never reached backend. Not a silent + // drop; a connection failure (slow OAuth refresh, network, etc). + onErrorRef.current?.( + 'Voice connection failed. Check your network and try again.', + ) + } else if (!hadAudioSignal) { + // Distinguish silent mic (capture issue) from speech not recognized. + onErrorRef.current?.( + 'No audio detected from microphone. Check that the correct input device is selected and that Claude Code has microphone access.', + ) + } else { + onErrorRef.current?.('No speech detected.') + } + } + + accumulatedRef.current = '' + setVoiceState(prev => { + if (prev.voiceInterimTranscript === '') return prev + return { ...prev, voiceInterimTranscript: '' } + }) + updateState('idle') + }) + .catch(err => { + logError(toError(err)) + if (!isStale()) updateState('idle') + }) + } + + // When voice is enabled, lazy-import voice.ts so checkRecordingAvailability + // et al. are ready when the user presses the voice key. Do NOT preload the + // native module — require('audio-capture.node') is a synchronous dlopen of + // CoreAudio/AudioUnit that blocks the event loop for ~1s (warm) to ~8s + // (cold coreaudiod). setImmediate doesn't help: it yields one tick, then the + // dlopen still blocks. The first voice keypress pays the dlopen cost instead. + useEffect(() => { + if (enabled && !voiceModule) { + void import('../services/voice.js').then(mod => { + voiceModule = mod + }) + } + }, [enabled]) + + // ── Focus silence timer ──────────────────────────────────────────── + // Arms (or resets) a timer that tears down the focus-mode session + // after FOCUS_SILENCE_TIMEOUT_MS of no speech. Called when a session + // starts and after each flushed transcript. + function armFocusSilenceTimer(): void { + if (focusSilenceTimerRef.current) { + clearTimeout(focusSilenceTimerRef.current) + } + focusSilenceTimerRef.current = setTimeout( + ( + focusSilenceTimerRef, + stateRef, + focusTriggeredRef, + silenceTimedOutRef, + finishRecording, + ) => { + focusSilenceTimerRef.current = null + if (stateRef.current === 'recording' && focusTriggeredRef.current) { + logForDebugging( + '[voice] Focus silence timeout — tearing down session', + ) + silenceTimedOutRef.current = true + finishRecording() + } + }, + FOCUS_SILENCE_TIMEOUT_MS, + focusSilenceTimerRef, + stateRef, + focusTriggeredRef, + silenceTimedOutRef, + finishRecording, + ) + } + + // ── Focus-driven recording ────────────────────────────────────────── + // In focus mode, start recording when the terminal gains focus and + // stop when it loses focus. This enables a "multi-clauding army" + // workflow where voice input follows window focus. + useEffect(() => { + if (!enabled || !focusMode) { + // Focus mode was disabled while a focus-driven recording was active — + // stop the recording so it doesn't linger until the silence timer fires. + if (focusTriggeredRef.current && stateRef.current === 'recording') { + logForDebugging( + '[voice] Focus mode disabled during recording, finishing', + ) + finishRecording() + } + return + } + let cancelled = false + if ( + isFocused && + stateRef.current === 'idle' && + !silenceTimedOutRef.current + ) { + const beginFocusRecording = (): void => { + // Re-check conditions — state or enabled/focusMode may have changed + // during the await (effect cleanup sets cancelled). + if ( + cancelled || + stateRef.current !== 'idle' || + silenceTimedOutRef.current + ) + return + logForDebugging('[voice] Focus gained, starting recording session') + focusTriggeredRef.current = true + void startRecordingSession() + armFocusSilenceTimer() + } + if (voiceModule) { + beginFocusRecording() + } else { + // Voice module is loading (async import resolves from cache as a + // microtask). Wait for it before starting the recording session. + void import('../services/voice.js').then(mod => { + voiceModule = mod + beginFocusRecording() + }) + } + } else if (!isFocused) { + // Clear the silence timeout flag on blur so the next focus + // cycle re-arms recording. + silenceTimedOutRef.current = false + if (stateRef.current === 'recording') { + logForDebugging('[voice] Focus lost, finishing recording') + finishRecording() + } + } + return () => { + cancelled = true + } + }, [enabled, focusMode, isFocused]) + + // ── Start a new recording session (voice_stream connect + audio) ── + async function startRecordingSession(): Promise { + if (!voiceModule) { + onErrorRef.current?.( + 'Voice module not loaded yet. Try again in a moment.', + ) + return + } + + // Transition to 'recording' synchronously, BEFORE any await. Callers + // read state synchronously right after `void startRecordingSession()`: + // - useVoiceIntegration.tsx space-hold guard reads voiceState from the + // store immediately — if it sees 'idle' it clears isSpaceHoldActiveRef + // and space auto-repeat leaks into the text input (100% repro) + // - handleKeyEvent's `currentState === 'idle'` re-entry check below + // If an await runs first, both see stale 'idle'. See PR #20873 review. + updateState('recording') + recordingStartRef.current = Date.now() + accumulatedRef.current = '' + seenRepeatRef.current = false + hasAudioSignalRef.current = false + retryUsedRef.current = false + silentDropRetriedRef.current = false + fullAudioRef.current = [] + focusFlushedCharsRef.current = 0 + everConnectedRef.current = false + const myGen = ++sessionGenRef.current + + // ── Pre-check: can we actually record audio? ────────────── + const availability = await voiceModule.checkRecordingAvailability() + if (!availability.available) { + logForDebugging( + `[voice] Recording not available: ${availability.reason ?? 'unknown'}`, + ) + onErrorRef.current?.( + availability.reason ?? 'Audio recording is not available.', + ) + cleanup() + updateState('idle') + return + } + + logForDebugging( + '[voice] Starting recording session, connecting voice stream', + ) + // Clear any previous error + setVoiceState(prev => { + if (!prev.voiceError) return prev + return { ...prev, voiceError: null } + }) + + // Buffer audio chunks while the WebSocket connects. Once the connection + // is ready (onReady fires), buffered chunks are flushed and subsequent + // chunks are sent directly. + const audioBuffer: Buffer[] = [] + + // Start recording IMMEDIATELY — audio is buffered until the WebSocket + // opens, eliminating the 1-2s latency from waiting for OAuth + WS connect. + logForDebugging( + '[voice] startRecording: buffering audio while WebSocket connects', + ) + audioLevelsRef.current = [] + const started = await voiceModule.startRecording( + (chunk: Buffer) => { + // Copy for fullAudioRef replay buffer. send() in voiceStreamSTT + // copies again defensively — acceptable overhead at audio rates. + // Skip buffering in focus mode — replay is gated on !focusTriggered + // so the buffer is dead weight (up to ~20MB for a 10min session). + const owned = Buffer.from(chunk) + if (!focusTriggeredRef.current) { + fullAudioRef.current.push(owned) + } + if (connectionRef.current) { + connectionRef.current.send(owned) + } else { + audioBuffer.push(owned) + } + // Update audio level histogram for the recording visualizer + const level = computeLevel(chunk) + if (!hasAudioSignalRef.current && level > 0.01) { + hasAudioSignalRef.current = true + } + const levels = audioLevelsRef.current + if (levels.length >= AUDIO_LEVEL_BARS) { + levels.shift() + } + levels.push(level) + // Copy the array so React sees a new reference + const snapshot = [...levels] + audioLevelsRef.current = snapshot + setVoiceState(prev => ({ ...prev, voiceAudioLevels: snapshot })) + }, + () => { + // External end (e.g. device error) - treat as stop + if (stateRef.current === 'recording') { + finishRecording() + } + }, + { silenceDetection: false }, + ) + + if (!started) { + logError(new Error('[voice] Recording failed — no audio tool found')) + onErrorRef.current?.( + 'Failed to start audio capture. Check that your microphone is accessible.', + ) + cleanup() + updateState('idle') + setVoiceState(prev => ({ + ...prev, + voiceError: 'Recording failed — no audio tool found', + })) + return + } + + const rawLanguage = getInitialSettings().language + const stt = normalizeLanguageForSTT(rawLanguage) + logEvent('tengu_voice_recording_started', { + focusTriggered: focusTriggeredRef.current, + sttLanguage: + stt.code as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + sttLanguageIsDefault: !rawLanguage?.trim(), + sttLanguageFellBack: stt.fellBackFrom !== undefined, + // ISO 639 subtag from Intl (bounded set, never user text). undefined if + // Intl failed — omitted from the payload, no retry cost (cached). + systemLocaleLanguage: + getSystemLocaleLanguage() as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + + // Retry once if the connection errors before delivering any transcript. + // The conversation-engine proxy can reject rapid reconnects (~1/N_pods + // same-pod collision) or CE's Deepgram upstream can fail during its own + // teardown window (anthropics/anthropic#287008 surfaces this as + // TranscriptError instead of silent-drop). A 250ms backoff clears both. + // Audio captured during the retry window routes to audioBuffer (via the + // connectionRef.current null check in the recording callback above) and + // is flushed by the second onReady. + let sawTranscript = false + + // Connect WebSocket in parallel with audio recording. + // Gather keyterms first (async but fast — no model calls), then connect. + // Bail from callbacks if a newer session has started. Prevents a + // slow-connecting zombie WS (e.g. user released, pressed again, first + // WS still handshaking) from firing onReady/onError into the new + // session and corrupting its connectionRef / triggering a bogus retry. + const isStale = () => sessionGenRef.current !== myGen + + const attemptConnect = (keyterms: string[]): void => { + const myAttemptGen = attemptGenRef.current + void connectVoiceStream( + { + onTranscript: (text: string, isFinal: boolean) => { + if (isStale()) return + sawTranscript = true + logForDebugging( + `[voice] onTranscript: isFinal=${String(isFinal)} text="${text}"`, + ) + if (isFinal && text.trim()) { + if (focusTriggeredRef.current) { + // Focus mode: flush each final transcript immediately and + // keep recording. This gives continuous transcription while + // the terminal is focused. + logForDebugging( + `[voice] Focus mode: flushing final transcript immediately: "${text.trim()}"`, + ) + onTranscriptRef.current(text.trim()) + focusFlushedCharsRef.current += text.trim().length + setVoiceState(prev => { + if (prev.voiceInterimTranscript === '') return prev + return { ...prev, voiceInterimTranscript: '' } + }) + accumulatedRef.current = '' + // User is actively speaking — reset the silence timer. + armFocusSilenceTimer() + } else { + // Hold-to-talk: accumulate final transcripts separated by spaces + if (accumulatedRef.current) { + accumulatedRef.current += ' ' + } + accumulatedRef.current += text.trim() + logForDebugging( + `[voice] Accumulated final transcript: "${accumulatedRef.current}"`, + ) + // Clear interim since final supersedes it + setVoiceState(prev => { + const preview = accumulatedRef.current + if (prev.voiceInterimTranscript === preview) return prev + return { ...prev, voiceInterimTranscript: preview } + }) + } + } else if (!isFinal) { + // Active interim speech resets the focus silence timer. + // Nova 3 disables auto-finalize so isFinal is never true + // mid-stream — without this, the 5s timer fires during + // active speech and tears down the session. + if (focusTriggeredRef.current) { + armFocusSilenceTimer() + } + // Show accumulated finals + current interim as live preview + const interim = text.trim() + const preview = accumulatedRef.current + ? accumulatedRef.current + (interim ? ' ' + interim : '') + : interim + setVoiceState(prev => { + if (prev.voiceInterimTranscript === preview) return prev + return { ...prev, voiceInterimTranscript: preview } + }) + } + }, + onError: (error: string, opts?: { fatal?: boolean }) => { + if (isStale()) { + logForDebugging( + `[voice] ignoring onError from stale session: ${error}`, + ) + return + } + // Swallow errors from superseded attempts. Covers conn 1's + // trailing close after retry is scheduled, AND the current + // conn's ws close event after its ws error already surfaced + // below (gen bumped at surface). + if (attemptGenRef.current !== myAttemptGen) { + logForDebugging( + `[voice] ignoring stale onError from superseded attempt: ${error}`, + ) + return + } + // Early-failure retry: server error before any transcript = + // likely a transient upstream race (CE rejection, Deepgram + // not ready). Clear connectionRef so audio re-buffers, back + // off, reconnect. Skip if the user has already released the + // key (state left 'recording') — no point retrying a session + // they've ended. Fatal errors (Cloudflare bot challenge, auth + // rejection) are the same failure on every retry attempt, so + // fall through to surface the message. + if ( + !opts?.fatal && + !sawTranscript && + stateRef.current === 'recording' + ) { + if (!retryUsedRef.current) { + retryUsedRef.current = true + logForDebugging( + `[voice] early voice_stream error (pre-transcript), retrying once: ${error}`, + ) + logEvent('tengu_voice_stream_early_retry', {}) + connectionRef.current = null + attemptGenRef.current++ + setTimeout( + (stateRef, attemptConnect, keyterms) => { + if (stateRef.current === 'recording') { + attemptConnect(keyterms) + } + }, + 250, + stateRef, + attemptConnect, + keyterms, + ) + return + } + } + // Surfacing — bump gen so this conn's trailing close-error + // (ws fires error then close 1006) is swallowed above. + attemptGenRef.current++ + logError(new Error(`[voice] voice_stream error: ${error}`)) + onErrorRef.current?.(`Voice stream error: ${error}`) + // Clear the audio buffer on error to avoid memory leaks + audioBuffer.length = 0 + focusTriggeredRef.current = false + cleanup() + updateState('idle') + }, + onClose: () => { + // no-op; lifecycle handled by cleanup() + }, + onReady: conn => { + // Only proceed if we're still in recording state AND this is + // still the current session. A zombie late-connecting WS from + // an abandoned session can pass the 'recording' check if the + // user has since started a new session. + if (isStale() || stateRef.current !== 'recording') { + conn.close() + return + } + + // The WebSocket is now truly open — assign connectionRef so + // subsequent audio callbacks send directly instead of buffering. + connectionRef.current = conn + everConnectedRef.current = true + + // Flush all audio chunks that were buffered while the WebSocket + // was connecting. This is safe because onReady fires from the + // WebSocket 'open' event, guaranteeing send() will not be dropped. + // + // Coalesce into ~1s slices rather than one ws.send per chunk + // — fewer WS frames means less overhead on both ends. + const SLICE_TARGET_BYTES = 32_000 // ~1s at 16kHz/16-bit/mono + if (audioBuffer.length > 0) { + let totalBytes = 0 + for (const c of audioBuffer) totalBytes += c.length + const slices: Buffer[][] = [[]] + let sliceBytes = 0 + for (const chunk of audioBuffer) { + if ( + sliceBytes > 0 && + sliceBytes + chunk.length > SLICE_TARGET_BYTES + ) { + slices.push([]) + sliceBytes = 0 + } + slices[slices.length - 1]!.push(chunk) + sliceBytes += chunk.length + } + logForDebugging( + `[voice] onReady: flushing ${String(audioBuffer.length)} buffered chunks (${String(totalBytes)} bytes) as ${String(slices.length)} coalesced frame(s)`, + ) + for (const slice of slices) { + conn.send(Buffer.concat(slice)) + } + } + audioBuffer.length = 0 + + // Reset the release timer now that the WebSocket is ready. + // Only arm it if auto-repeat has been seen — otherwise the OS + // key repeat delay (~500ms) hasn't elapsed yet and the timer + // would fire prematurely. + if (releaseTimerRef.current) { + clearTimeout(releaseTimerRef.current) + } + if (seenRepeatRef.current) { + releaseTimerRef.current = setTimeout( + (releaseTimerRef, stateRef, finishRecording) => { + releaseTimerRef.current = null + if (stateRef.current === 'recording') { + finishRecording() + } + }, + RELEASE_TIMEOUT_MS, + releaseTimerRef, + stateRef, + finishRecording, + ) + } + }, + }, + { + language: stt.code, + keyterms, + }, + ).then(conn => { + if (isStale()) { + conn?.close() + return + } + if (!conn) { + logForDebugging( + '[voice] Failed to connect to voice_stream (no OAuth token?)', + ) + onErrorRef.current?.( + 'Voice mode requires a Claude.ai account. Please run /login to sign in.', + ) + // Clear the audio buffer on failure + audioBuffer.length = 0 + cleanup() + updateState('idle') + return + } + + // Safety check: if the user released the key before connectVoiceStream + // resolved (but after onReady already ran), close the connection. + if (stateRef.current !== 'recording') { + audioBuffer.length = 0 + conn.close() + return + } + }) + } + + void getVoiceKeyterms().then(attemptConnect) + } + + // ── Hold-to-talk handler ──────────────────────────────────────────── + // Called on every keypress (including terminal auto-repeats while + // the key is held). A gap longer than RELEASE_TIMEOUT_MS between + // events is interpreted as key release. + // + // Recording starts immediately on the first keypress to eliminate + // startup delay. The release timer is only armed after auto-repeat + // is detected (to avoid false releases during the OS key repeat + // delay of ~500ms on macOS). + const handleKeyEvent = useCallback( + (fallbackMs = REPEAT_FALLBACK_MS): void => { + if (!enabled || !isVoiceStreamAvailable()) { + return + } + + // In focus mode, recording is driven by terminal focus, not keypresses. + if (focusTriggeredRef.current) { + // Active focus recording — ignore key events (session ends on blur). + return + } + if (focusMode && silenceTimedOutRef.current) { + // Focus session timed out due to silence — keypress re-arms it. + logForDebugging( + '[voice] Re-arming focus recording after silence timeout', + ) + silenceTimedOutRef.current = false + focusTriggeredRef.current = true + void startRecordingSession() + armFocusSilenceTimer() + return + } + + const currentState = stateRef.current + + // Ignore keypresses while processing + if (currentState === 'processing') { + return + } + + if (currentState === 'idle') { + logForDebugging( + '[voice] handleKeyEvent: idle, starting recording session immediately', + ) + void startRecordingSession() + // Fallback: if no auto-repeat arrives within REPEAT_FALLBACK_MS, + // arm the release timer anyway (the user likely tapped and released). + repeatFallbackTimerRef.current = setTimeout( + ( + repeatFallbackTimerRef, + stateRef, + seenRepeatRef, + releaseTimerRef, + finishRecording, + ) => { + repeatFallbackTimerRef.current = null + if (stateRef.current === 'recording' && !seenRepeatRef.current) { + logForDebugging( + '[voice] No auto-repeat seen, arming release timer via fallback', + ) + seenRepeatRef.current = true + releaseTimerRef.current = setTimeout( + (releaseTimerRef, stateRef, finishRecording) => { + releaseTimerRef.current = null + if (stateRef.current === 'recording') { + finishRecording() + } + }, + RELEASE_TIMEOUT_MS, + releaseTimerRef, + stateRef, + finishRecording, + ) + } + }, + fallbackMs, + repeatFallbackTimerRef, + stateRef, + seenRepeatRef, + releaseTimerRef, + finishRecording, + ) + } else if (currentState === 'recording') { + // Second+ keypress while recording — auto-repeat has started. + seenRepeatRef.current = true + if (repeatFallbackTimerRef.current) { + clearTimeout(repeatFallbackTimerRef.current) + repeatFallbackTimerRef.current = null + } + } + + // Reset the release timer on every keypress (including auto-repeats) + if (releaseTimerRef.current) { + clearTimeout(releaseTimerRef.current) + } + + // Only arm the release timer once auto-repeat has been seen. + // The OS key repeat delay is ~500ms on macOS; without this gate + // the 200ms timer fires before repeat starts, causing a false release. + if (stateRef.current === 'recording' && seenRepeatRef.current) { + releaseTimerRef.current = setTimeout( + (releaseTimerRef, stateRef, finishRecording) => { + releaseTimerRef.current = null + if (stateRef.current === 'recording') { + finishRecording() + } + }, + RELEASE_TIMEOUT_MS, + releaseTimerRef, + stateRef, + finishRecording, + ) + } + }, + [enabled, focusMode, cleanup], + ) + + // Cleanup only when disabled or unmounted - NOT on state changes + useEffect(() => { + if (!enabled && stateRef.current !== 'idle') { + cleanup() + updateState('idle') + } + return () => { + cleanup() + } + }, [enabled, cleanup]) + + return { + state, + handleKeyEvent, + } +} diff --git a/hooks/useVoiceEnabled.ts b/hooks/useVoiceEnabled.ts new file mode 100644 index 0000000..ece0691 --- /dev/null +++ b/hooks/useVoiceEnabled.ts @@ -0,0 +1,25 @@ +import { useMemo } from 'react' +import { useAppState } from '../state/AppState.js' +import { + hasVoiceAuth, + isVoiceGrowthBookEnabled, +} from '../voice/voiceModeEnabled.js' + +/** + * Combines user intent (settings.voiceEnabled) with auth + GB kill-switch. + * Only the auth half is memoized on authVersion — it's the expensive one + * (cold getClaudeAIOAuthTokens memoize → sync `security` spawn, ~60ms/call, + * ~180ms total in profile v5 when token refresh cleared the cache mid-session). + * GB is a cheap cached-map lookup and stays outside the memo so a mid-session + * kill-switch flip still takes effect on the next render. + * + * authVersion bumps on /login only. Background token refresh leaves it alone + * (user is still authed), so the auth memo stays correct without re-eval. + */ +export function useVoiceEnabled(): boolean { + const userIntent = useAppState(s => s.settings.voiceEnabled === true) + const authVersion = useAppState(s => s.authVersion) + // eslint-disable-next-line react-hooks/exhaustive-deps + const authed = useMemo(hasVoiceAuth, [authVersion]) + return userIntent && authed && isVoiceGrowthBookEnabled() +} diff --git a/hooks/useVoiceIntegration.tsx b/hooks/useVoiceIntegration.tsx new file mode 100644 index 0000000..0082f07 --- /dev/null +++ b/hooks/useVoiceIntegration.tsx @@ -0,0 +1,677 @@ +import { feature } from 'bun:bundle'; +import * as React from 'react'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useNotifications } from '../context/notifications.js'; +import { useIsModalOverlayActive } from '../context/overlayContext.js'; +import { useGetVoiceState, useSetVoiceState, useVoiceState } from '../context/voice.js'; +import { KeyboardEvent } from '../ink/events/keyboard-event.js'; +// eslint-disable-next-line custom-rules/prefer-use-keybindings -- backward-compat bridge until REPL wires handleKeyDown to +import { useInput } from '../ink.js'; +import { useOptionalKeybindingContext } from '../keybindings/KeybindingContext.js'; +import { keystrokesEqual } from '../keybindings/resolver.js'; +import type { ParsedKeystroke } from '../keybindings/types.js'; +import { normalizeFullWidthSpace } from '../utils/stringUtils.js'; +import { useVoiceEnabled } from './useVoiceEnabled.js'; + +// Dead code elimination: conditional import for voice input hook. +/* eslint-disable @typescript-eslint/no-require-imports */ +// Capture the module namespace, not the function: spyOn() mutates the module +// object, so `voiceNs.useVoice(...)` resolves to the spy even if this module +// was loaded before the spy was installed (test ordering independence). +const voiceNs: { + useVoice: typeof import('./useVoice.js').useVoice; +} = feature('VOICE_MODE') ? require('./useVoice.js') : { + useVoice: ({ + enabled: _e + }: { + onTranscript: (t: string) => void; + enabled: boolean; + }) => ({ + state: 'idle' as const, + handleKeyEvent: (_fallbackMs?: number) => {} + }) +}; +/* eslint-enable @typescript-eslint/no-require-imports */ + +// Maximum gap (ms) between key presses to count as held (auto-repeat). +// Terminal auto-repeat fires every 30-80ms; 120ms covers jitter while +// excluding normal typing speed (100-300ms between keystrokes). +const RAPID_KEY_GAP_MS = 120; + +// Fallback (ms) for modifier-combo first-press activation. Must match +// FIRST_PRESS_FALLBACK_MS in useVoice.ts. Covers the max OS initial +// key-repeat delay (~2s on macOS with slider at "Long") so holding a +// modifier combo doesn't fragment into two sessions when the first +// auto-repeat arrives after the default 600ms REPEAT_FALLBACK_MS. +const MODIFIER_FIRST_PRESS_FALLBACK_MS = 2000; + +// Number of rapid consecutive key events required to activate voice. +// Only applies to bare-char bindings (space, v, etc.) where a single press +// could be normal typing. Modifier combos activate on the first press. +const HOLD_THRESHOLD = 5; + +// Number of rapid key events to start showing warmup feedback. +const WARMUP_THRESHOLD = 2; + +// Match a KeyboardEvent against a ParsedKeystroke. Replaces the legacy +// matchesKeystroke(input, Key, ...) path which assumed useInput's raw +// `input` arg — KeyboardEvent.key holds normalized names (e.g. 'space', +// 'f9') that getKeyName() didn't handle, so modifier combos and f-keys +// silently failed to match after the onKeyDown migration (#23524). +function matchesKeyboardEvent(e: KeyboardEvent, target: ParsedKeystroke): boolean { + // KeyboardEvent stores key names; ParsedKeystroke stores ' ' for space + // and 'enter' for return (see parser.ts case 'space'/'return'). + const key = e.key === 'space' ? ' ' : e.key === 'return' ? 'enter' : e.key.toLowerCase(); + if (key !== target.key) return false; + if (e.ctrl !== target.ctrl) return false; + if (e.shift !== target.shift) return false; + // KeyboardEvent.meta folds alt|option (terminal limitation — esc-prefix); + // ParsedKeystroke has both alt and meta as aliases for the same thing. + if (e.meta !== (target.alt || target.meta)) return false; + if (e.superKey !== target.super) return false; + return true; +} + +// Hardcoded default for when there's no KeybindingProvider at all (e.g. +// headless/test contexts). NOT used when the provider exists and the +// lookup returns null — that means the user null-unbound or reassigned +// space, and falling back to space would pick a dead or conflicting key. +const DEFAULT_VOICE_KEYSTROKE: ParsedKeystroke = { + key: ' ', + ctrl: false, + alt: false, + shift: false, + meta: false, + super: false +}; +type InsertTextHandle = { + insert: (text: string) => void; + setInputWithCursor: (value: string, cursor: number) => void; + cursorOffset: number; +}; +type UseVoiceIntegrationArgs = { + setInputValueRaw: React.Dispatch>; + inputValueRef: React.RefObject; + insertTextRef: React.RefObject; +}; +type InterimRange = { + start: number; + end: number; +}; +type StripOpts = { + // Which char to strip (the configured hold key). Defaults to space. + char?: string; + // Capture the voice prefix/suffix anchor at the stripped position. + anchor?: boolean; + // Minimum trailing count to leave behind — prevents stripping the + // intentional warmup chars when defensively cleaning up leaks. + floor?: number; +}; +type UseVoiceIntegrationResult = { + // Returns the number of trailing chars remaining after stripping. + stripTrailing: (maxStrip: number, opts?: StripOpts) => number; + // Undo the gap space and reset anchor refs after a failed voice activation. + resetAnchor: () => void; + handleKeyEvent: (fallbackMs?: number) => void; + interimRange: InterimRange | null; +}; +export function useVoiceIntegration({ + setInputValueRaw, + inputValueRef, + insertTextRef +}: UseVoiceIntegrationArgs): UseVoiceIntegrationResult { + const { + addNotification + } = useNotifications(); + + // Tracks the input content before/after the cursor when voice starts, + // so interim transcripts can be inserted at the cursor position without + // clobbering surrounding user text. + const voicePrefixRef = useRef(null); + const voiceSuffixRef = useRef(''); + // Tracks the last input value this hook wrote (via anchor, interim effect, + // or handleVoiceTranscript). If inputValueRef.current diverges, the user + // submitted or edited — both write paths bail to avoid clobbering. This is + // the only guard that correctly handles empty-prefix-empty-suffix: a + // startsWith('')/endsWith('') check vacuously passes, and a length check + // can't distinguish a cleared input from a never-set one. + const lastSetInputRef = useRef(null); + + // Strip trailing hold-key chars (and optionally capture the voice + // anchor). Called during warmup (to clean up chars that leaked past + // stopImmediatePropagation — listener order is not guaranteed) and + // on activation (with anchor=true to capture the prefix/suffix around + // the cursor for interim transcript placement). The caller passes the + // exact count it expects to strip so pre-existing chars at the + // boundary are preserved (e.g. the "v" in "hav" when hold-key is "v"). + // The floor option sets a minimum trailing count to leave behind + // (during warmup this is the count we intentionally let through, so + // defensive cleanup only removes leaks). Returns the number of + // trailing chars remaining after stripping. When nothing changes, no + // state update is performed. + const stripTrailing = useCallback((maxStrip: number, { + char = ' ', + anchor = false, + floor = 0 + }: StripOpts = {}) => { + const prev = inputValueRef.current; + const offset = insertTextRef.current?.cursorOffset ?? prev.length; + const beforeCursor = prev.slice(0, offset); + const afterCursor = prev.slice(offset); + // When the hold key is space, also count full-width spaces (U+3000) + // that a CJK IME may have inserted for the same physical key. + // U+3000 is BMP single-code-unit so indices align with beforeCursor. + const scan = char === ' ' ? normalizeFullWidthSpace(beforeCursor) : beforeCursor; + let trailing = 0; + while (trailing < scan.length && scan[scan.length - 1 - trailing] === char) { + trailing++; + } + const stripCount = Math.max(0, Math.min(trailing - floor, maxStrip)); + const remaining = trailing - stripCount; + const stripped = beforeCursor.slice(0, beforeCursor.length - stripCount); + // When anchoring with a non-space suffix, insert a gap space so the + // waveform cursor sits on the gap instead of covering the first + // suffix letter. The interim transcript effect maintains this same + // structure (prefix + leading + interim + trailing + suffix), so + // the gap is seamless once transcript text arrives. + // Always overwrite on anchor — if a prior activation failed to start + // voice (voiceState stayed 'idle'), the cleanup effect didn't fire and + // the old anchor is stale. anchor=true is only passed on the single + // activation call, never during recording, so overwrite is safe. + let gap = ''; + if (anchor) { + voicePrefixRef.current = stripped; + voiceSuffixRef.current = afterCursor; + if (afterCursor.length > 0 && !/^\s/.test(afterCursor)) { + gap = ' '; + } + } + const newValue = stripped + gap + afterCursor; + if (anchor) lastSetInputRef.current = newValue; + if (newValue === prev && stripCount === 0) return remaining; + if (insertTextRef.current) { + insertTextRef.current.setInputWithCursor(newValue, stripped.length); + } else { + setInputValueRaw(newValue); + } + return remaining; + }, [setInputValueRaw, inputValueRef, insertTextRef]); + + // Undo the gap space inserted by stripTrailing(..., {anchor:true}) and + // reset the voice prefix/suffix refs. Called when voice activation fails + // (voiceState stays 'idle' after voiceHandleKeyEvent), so the cleanup + // effect (voiceState useEffect below) — which only fires on voiceState transitions — can't + // reach the stale anchor. Without this, the gap space and stale refs + // persist in the input. + const resetAnchor = useCallback(() => { + const prefix = voicePrefixRef.current; + if (prefix === null) return; + const suffix = voiceSuffixRef.current; + voicePrefixRef.current = null; + voiceSuffixRef.current = ''; + const restored = prefix + suffix; + if (insertTextRef.current) { + insertTextRef.current.setInputWithCursor(restored, prefix.length); + } else { + setInputValueRaw(restored); + } + }, [setInputValueRaw, insertTextRef]); + + // Voice state selectors. useVoiceEnabled = user intent (settings) + + // auth + GB kill-switch, with the auth half memoized on authVersion so + // render loops never hit a cold keychain spawn. + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const voiceEnabled = feature('VOICE_MODE') ? useVoiceEnabled() : false; + const voiceState = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useVoiceState(s => s.voiceState) : 'idle' as const; + const voiceInterimTranscript = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useVoiceState(s_0 => s_0.voiceInterimTranscript) : ''; + + // Set the voice anchor for focus mode (where recording starts via terminal + // focus, not key hold). Key-hold sets the anchor in stripTrailing. + useEffect(() => { + if (!feature('VOICE_MODE')) return; + if (voiceState === 'recording' && voicePrefixRef.current === null) { + const input = inputValueRef.current; + const offset_0 = insertTextRef.current?.cursorOffset ?? input.length; + voicePrefixRef.current = input.slice(0, offset_0); + voiceSuffixRef.current = input.slice(offset_0); + lastSetInputRef.current = input; + } + if (voiceState === 'idle') { + voicePrefixRef.current = null; + voiceSuffixRef.current = ''; + lastSetInputRef.current = null; + } + }, [voiceState, inputValueRef, insertTextRef]); + + // Live-update the prompt input with the interim transcript as voice + // transcribes speech. The prefix (user-typed text before the cursor) is + // preserved and the transcript is inserted between prefix and suffix. + useEffect(() => { + if (!feature('VOICE_MODE')) return; + if (voicePrefixRef.current === null) return; + const prefix_0 = voicePrefixRef.current; + const suffix_0 = voiceSuffixRef.current; + // Submit race: if the input isn't what this hook last set it to, the + // user submitted (clearing it) or edited it. voicePrefixRef is only + // cleared on voiceState→idle, so it's still set during the 'processing' + // window between CloseStream and WS close — this catches refined + // TranscriptText arriving then and re-filling a cleared input. + if (inputValueRef.current !== lastSetInputRef.current) return; + const needsSpace = prefix_0.length > 0 && !/\s$/.test(prefix_0) && voiceInterimTranscript.length > 0; + // Don't gate on voiceInterimTranscript.length -- when interim clears to '' + // after handleVoiceTranscript sets the final text, the trailing space + // between prefix and suffix must still be preserved. + const needsTrailingSpace = suffix_0.length > 0 && !/^\s/.test(suffix_0); + const leadingSpace = needsSpace ? ' ' : ''; + const trailingSpace = needsTrailingSpace ? ' ' : ''; + const newValue_0 = prefix_0 + leadingSpace + voiceInterimTranscript + trailingSpace + suffix_0; + // Position cursor after the transcribed text (before suffix) + const cursorPos = prefix_0.length + leadingSpace.length + voiceInterimTranscript.length; + if (insertTextRef.current) { + insertTextRef.current.setInputWithCursor(newValue_0, cursorPos); + } else { + setInputValueRaw(newValue_0); + } + lastSetInputRef.current = newValue_0; + }, [voiceInterimTranscript, setInputValueRaw, inputValueRef, insertTextRef]); + const handleVoiceTranscript = useCallback((text: string) => { + if (!feature('VOICE_MODE')) return; + const prefix_1 = voicePrefixRef.current; + // No voice anchor — voice was reset (or never started). Nothing to do. + if (prefix_1 === null) return; + const suffix_1 = voiceSuffixRef.current; + // Submit race: finishRecording() → user presses Enter (input cleared) + // → WebSocket close → this callback fires with stale prefix/suffix. + // If the input isn't what this hook last set (via the interim effect + // or anchor), the user submitted or edited — don't re-fill. Comparing + // against `text.length` would false-positive when the final is longer + // than the interim (ASR routinely adds punctuation/corrections). + if (inputValueRef.current !== lastSetInputRef.current) return; + const needsSpace_0 = prefix_1.length > 0 && !/\s$/.test(prefix_1) && text.length > 0; + const needsTrailingSpace_0 = suffix_1.length > 0 && !/^\s/.test(suffix_1) && text.length > 0; + const leadingSpace_0 = needsSpace_0 ? ' ' : ''; + const trailingSpace_0 = needsTrailingSpace_0 ? ' ' : ''; + const newInput = prefix_1 + leadingSpace_0 + text + trailingSpace_0 + suffix_1; + // Position cursor after the transcribed text (before suffix) + const cursorPos_0 = prefix_1.length + leadingSpace_0.length + text.length; + if (insertTextRef.current) { + insertTextRef.current.setInputWithCursor(newInput, cursorPos_0); + } else { + setInputValueRaw(newInput); + } + lastSetInputRef.current = newInput; + // Update the prefix to include this chunk so focus mode can continue + // appending subsequent transcripts after it. + voicePrefixRef.current = prefix_1 + leadingSpace_0 + text; + }, [setInputValueRaw, inputValueRef, insertTextRef]); + const voice = voiceNs.useVoice({ + onTranscript: handleVoiceTranscript, + onError: (message: string) => { + addNotification({ + key: 'voice-error', + text: message, + color: 'error', + priority: 'immediate', + timeoutMs: 10_000 + }); + }, + enabled: voiceEnabled, + focusMode: false + }); + + // Compute the character range of interim (not-yet-finalized) transcript + // text in the input value, so the UI can dim it. + const interimRange = useMemo((): InterimRange | null => { + if (!feature('VOICE_MODE')) return null; + if (voicePrefixRef.current === null) return null; + if (voiceInterimTranscript.length === 0) return null; + const prefix_2 = voicePrefixRef.current; + const needsSpace_1 = prefix_2.length > 0 && !/\s$/.test(prefix_2) && voiceInterimTranscript.length > 0; + const start = prefix_2.length + (needsSpace_1 ? 1 : 0); + const end = start + voiceInterimTranscript.length; + return { + start, + end + }; + }, [voiceInterimTranscript]); + return { + stripTrailing, + resetAnchor, + handleKeyEvent: voice.handleKeyEvent, + interimRange + }; +} + +/** + * Component that handles hold-to-talk voice activation. + * + * The activation key is configurable via keybindings (voice:pushToTalk, + * default: space). Hold detection depends on OS auto-repeat delivering a + * stream of events at 30-80ms intervals. Two binding types work: + * + * **Modifier + letter (meta+k, ctrl+x, alt+v):** Cleanest. Activates on + * the first press — a modifier combo is unambiguous intent (can't be + * typed accidentally), so no hold threshold applies. The letter part + * auto-repeats while held, feeding release detection in useVoice.ts. + * No flow-through, no stripping. + * + * **Bare chars (space, v, x):** Require HOLD_THRESHOLD rapid presses to + * activate (a single space could be normal typing). The first + * WARMUP_THRESHOLD presses flow into the input so a single press types + * normally. Past that, rapid presses are swallowed; on activation the + * flow-through chars are stripped. Binding "v" doesn't make "v" + * untypable — normal typing (>120ms between keystrokes) flows through; + * only rapid auto-repeat from a held key triggers activation. + * + * Known broken: modifier+space (NUL → parsed as ctrl+backtick), chords + * (discrete sequences, no hold). Validation warns on these. + */ +export function useVoiceKeybindingHandler({ + voiceHandleKeyEvent, + stripTrailing, + resetAnchor, + isActive +}: { + voiceHandleKeyEvent: (fallbackMs?: number) => void; + stripTrailing: (maxStrip: number, opts?: StripOpts) => number; + resetAnchor: () => void; + isActive: boolean; +}): { + handleKeyDown: (e: KeyboardEvent) => void; +} { + const getVoiceState = useGetVoiceState(); + const setVoiceState = useSetVoiceState(); + const keybindingContext = useOptionalKeybindingContext(); + const isModalOverlayActive = useIsModalOverlayActive(); + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + const voiceEnabled = feature('VOICE_MODE') ? useVoiceEnabled() : false; + const voiceState = feature('VOICE_MODE') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useVoiceState(s => s.voiceState) : 'idle'; + + // Find the configured key for voice:pushToTalk from keybinding context. + // Forward iteration with last-wins (matching the resolver): if a later + // Chat binding overrides the same chord with null or a different + // action, the voice binding is discarded and null is returned — the + // user explicitly disabled hold-to-talk via binding override, so + // don't second-guess them with a fallback. The DEFAULT is only used + // when there's no provider at all. Context filter is required — space + // is also bound in Settings/Confirmation/Plugin (select:accept etc.); + // without the filter those would null out the default. + const voiceKeystroke = useMemo((): ParsedKeystroke | null => { + if (!keybindingContext) return DEFAULT_VOICE_KEYSTROKE; + let result: ParsedKeystroke | null = null; + for (const binding of keybindingContext.bindings) { + if (binding.context !== 'Chat') continue; + if (binding.chord.length !== 1) continue; + const ks = binding.chord[0]; + if (!ks) continue; + if (binding.action === 'voice:pushToTalk') { + result = ks; + } else if (result !== null && keystrokesEqual(ks, result)) { + // A later binding overrides this chord (null unbind or reassignment) + result = null; + } + } + return result; + }, [keybindingContext]); + + // If the binding is a bare (unmodified) single printable char, terminal + // auto-repeat may batch N keystrokes into one input event (e.g. "vvv"), + // and the char flows into the text input — we need flow-through + strip. + // Modifier combos (meta+k, ctrl+x) also auto-repeat (the letter part + // repeats) but don't insert text, so they're swallowed from the first + // press with no stripping needed. matchesKeyboardEvent handles those. + const bareChar = voiceKeystroke !== null && voiceKeystroke.key.length === 1 && !voiceKeystroke.ctrl && !voiceKeystroke.alt && !voiceKeystroke.shift && !voiceKeystroke.meta && !voiceKeystroke.super ? voiceKeystroke.key : null; + const rapidCountRef = useRef(0); + // How many rapid chars we intentionally let through to the text + // input (the first WARMUP_THRESHOLD). The activation strip removes + // up to this many + the activation event's potential leak. For the + // default (space) this is precise — pre-existing trailing spaces are + // rare. For letter bindings (validation warns) this may over-strip + // one pre-existing char if the input already ended in the bound + // letter (e.g. "hav" + hold "v" → "ha"). We don't track that + // boundary — it's best-effort and the warning says so. + const charsInInputRef = useRef(0); + // Trailing-char count remaining after the activation strip — these + // belong to the user's anchored prefix and must be preserved during + // recording's defensive leak cleanup. + const recordingFloorRef = useRef(0); + // True when the current recording was started by key-hold (not focus). + // Used to avoid swallowing keypresses during focus-mode recording. + const isHoldActiveRef = useRef(false); + const resetTimerRef = useRef | null>(null); + + // Reset hold state as soon as we leave 'recording'. The physical hold + // ends when key-repeat stops (state → 'processing'); keeping the ref + // set through 'processing' swallows new space presses the user types + // while the transcript finalizes. + useEffect(() => { + if (voiceState !== 'recording') { + isHoldActiveRef.current = false; + rapidCountRef.current = 0; + charsInInputRef.current = 0; + recordingFloorRef.current = 0; + setVoiceState(prev => { + if (!prev.voiceWarmingUp) return prev; + return { + ...prev, + voiceWarmingUp: false + }; + }); + } + }, [voiceState, setVoiceState]); + const handleKeyDown = (e: KeyboardEvent): void => { + if (!voiceEnabled) return; + + // PromptInput is not a valid transcript target — let the hold key + // flow through instead of swallowing it into stale refs (#33556). + // Two distinct unmount/unfocus paths (both needed): + // - !isActive: local-jsx command hid PromptInput (shouldHidePromptInput) + // without registering an overlay — e.g. /install-github-app, + // /plugin. Mirrors CommandKeybindingHandlers' isActive gate. + // - isModalOverlayActive: overlay (permission dialog, Select with + // onCancel) has focus; PromptInput is mounted but focus=false. + if (!isActive || isModalOverlayActive) return; + + // null means the user overrode the default (null-unbind/reassign) — + // hold-to-talk is disabled via binding. To toggle the feature + // itself, use /voice. + if (voiceKeystroke === null) return; + + // Match the configured key. Bare chars match by content (handles + // batched auto-repeat like "vvv") with a modifier reject so e.g. + // ctrl+v doesn't trip a "v" binding. Modifier combos go through + // matchesKeyboardEvent (one event per repeat, no batching). + let repeatCount: number; + if (bareChar !== null) { + if (e.ctrl || e.meta || e.shift) return; + // When bound to space, also accept U+3000 (full-width space) — + // CJK IMEs emit it for the same physical key. + const normalized = bareChar === ' ' ? normalizeFullWidthSpace(e.key) : e.key; + // Fast-path: normal typing (any char that isn't the bound one) + // bails here without allocating. The repeat() check only matters + // for batched auto-repeat (input.length > 1) which is rare. + if (normalized[0] !== bareChar) return; + if (normalized.length > 1 && normalized !== bareChar.repeat(normalized.length)) return; + repeatCount = normalized.length; + } else { + if (!matchesKeyboardEvent(e, voiceKeystroke)) return; + repeatCount = 1; + } + + // Guard: only swallow keypresses when recording was triggered by + // key-hold. Focus-mode recording also sets voiceState to 'recording', + // but keypresses should flow through normally (voiceHandleKeyEvent + // returns early for focus-triggered sessions). We also check voiceState + // from the store so that if voiceHandleKeyEvent() fails to transition + // state (module not loaded, stream unavailable) we don't permanently + // swallow keypresses. + const currentVoiceState = getVoiceState().voiceState; + if (isHoldActiveRef.current && currentVoiceState !== 'idle') { + // Already recording — swallow continued keypresses and forward + // to voice for release detection. For bare chars, defensively + // strip in case the text input handler fired before this one + // (listener order is not guaranteed). Modifier combos don't + // insert text, so nothing to strip. + e.stopImmediatePropagation(); + if (bareChar !== null) { + stripTrailing(repeatCount, { + char: bareChar, + floor: recordingFloorRef.current + }); + } + voiceHandleKeyEvent(); + return; + } + + // Non-hold recording (focus-mode) or processing is active. + // Modifier combos must not re-activate: stripTrailing(0,{anchor:true}) + // would overwrite voicePrefixRef with interim text and duplicate the + // transcript on the next interim update. Pre-#22144, a single tap + // hit the warmup else-branch (swallow only). Bare chars flow through + // unconditionally — user may be typing during focus-recording. + if (currentVoiceState !== 'idle') { + if (bareChar === null) e.stopImmediatePropagation(); + return; + } + const countBefore = rapidCountRef.current; + rapidCountRef.current += repeatCount; + + // ── Activation ──────────────────────────────────────────── + // Handled first so the warmup branch below does NOT also run + // on this event — two strip calls in the same tick would both + // read the stale inputValueRef and the second would under-strip. + // Modifier combos activate on the first press — they can't be + // typed accidentally, so the hold threshold (which exists to + // distinguish typing a space from holding space) doesn't apply. + if (bareChar === null || rapidCountRef.current >= HOLD_THRESHOLD) { + e.stopImmediatePropagation(); + if (resetTimerRef.current) { + clearTimeout(resetTimerRef.current); + resetTimerRef.current = null; + } + rapidCountRef.current = 0; + isHoldActiveRef.current = true; + setVoiceState(prev_0 => { + if (!prev_0.voiceWarmingUp) return prev_0; + return { + ...prev_0, + voiceWarmingUp: false + }; + }); + if (bareChar !== null) { + // Strip the intentional warmup chars plus this event's leak + // (if text input fired first). Cap covers both; min(trailing) + // handles the no-leak case. Anchor the voice prefix here. + // The return value (remaining) becomes the floor for + // recording-time leak cleanup. + recordingFloorRef.current = stripTrailing(charsInInputRef.current + repeatCount, { + char: bareChar, + anchor: true + }); + charsInInputRef.current = 0; + voiceHandleKeyEvent(); + } else { + // Modifier combo: nothing inserted, nothing to strip. Just + // anchor the voice prefix at the current cursor position. + // Longer fallback: this call is at t=0 (before auto-repeat), + // so the gap to the next keypress is the OS initial repeat + // *delay* (up to ~2s), not the repeat *rate* (~30-80ms). + stripTrailing(0, { + anchor: true + }); + voiceHandleKeyEvent(MODIFIER_FIRST_PRESS_FALLBACK_MS); + } + // If voice failed to transition (module not loaded, stream + // unavailable, stale enabled), clear the ref so a later + // focus-mode recording doesn't inherit stale hold state + // and swallow keypresses. Store is synchronous — the check is + // immediate. The anchor set by stripTrailing above will + // be overwritten on retry (anchor always overwrites now). + if (getVoiceState().voiceState === 'idle') { + isHoldActiveRef.current = false; + resetAnchor(); + } + return; + } + + // ── Warmup (bare-char only; modifier combos activated above) ── + // First WARMUP_THRESHOLD chars flow to the text input so normal + // typing has zero latency (a single press types normally). + // Subsequent rapid chars are swallowed so the input stays aligned + // with the warmup UI. Strip defensively (listener order is not + // guaranteed — text input may have already added the char). The + // floor preserves the intentional warmup chars; the strip is a + // no-op when nothing leaked. Check countBefore so the event that + // crosses the threshold still flows through (terminal batching). + if (countBefore >= WARMUP_THRESHOLD) { + e.stopImmediatePropagation(); + stripTrailing(repeatCount, { + char: bareChar, + floor: charsInInputRef.current + }); + } else { + charsInInputRef.current += repeatCount; + } + + // Show warmup feedback once we detect a hold pattern + if (rapidCountRef.current >= WARMUP_THRESHOLD) { + setVoiceState(prev_1 => { + if (prev_1.voiceWarmingUp) return prev_1; + return { + ...prev_1, + voiceWarmingUp: true + }; + }); + } + if (resetTimerRef.current) { + clearTimeout(resetTimerRef.current); + } + resetTimerRef.current = setTimeout((resetTimerRef_0, rapidCountRef_0, charsInInputRef_0, setVoiceState_0) => { + resetTimerRef_0.current = null; + rapidCountRef_0.current = 0; + charsInInputRef_0.current = 0; + setVoiceState_0(prev_2 => { + if (!prev_2.voiceWarmingUp) return prev_2; + return { + ...prev_2, + voiceWarmingUp: false + }; + }); + }, RAPID_KEY_GAP_MS, resetTimerRef, rapidCountRef, charsInInputRef, setVoiceState); + }; + + // Backward-compat bridge: REPL.tsx doesn't yet wire handleKeyDown to + // . Subscribe via useInput and adapt InputEvent → + // KeyboardEvent until the consumer is migrated (separate PR). + // TODO(onKeyDown-migration): remove once REPL passes handleKeyDown. + useInput((_input, _key, event) => { + const kbEvent = new KeyboardEvent(event.keypress); + handleKeyDown(kbEvent); + // handleKeyDown stopped the adapter event, not the InputEvent the + // emitter actually checks — forward it so the text input's useInput + // listener is skipped and held spaces don't leak into the prompt. + if (kbEvent.didStopImmediatePropagation()) { + event.stopImmediatePropagation(); + } + }, { + isActive + }); + return { + handleKeyDown + }; +} + +// TODO(onKeyDown-migration): temporary shim so existing JSX callers +// () keep compiling. Remove once REPL.tsx +// wires handleKeyDown directly. +export function VoiceKeybindingHandler(props) { + useVoiceKeybindingHandler(props); + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJ1c2VSZWYiLCJ1c2VOb3RpZmljYXRpb25zIiwidXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUiLCJ1c2VHZXRWb2ljZVN0YXRlIiwidXNlU2V0Vm9pY2VTdGF0ZSIsInVzZVZvaWNlU3RhdGUiLCJLZXlib2FyZEV2ZW50IiwidXNlSW5wdXQiLCJ1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0Iiwia2V5c3Ryb2tlc0VxdWFsIiwiUGFyc2VkS2V5c3Ryb2tlIiwibm9ybWFsaXplRnVsbFdpZHRoU3BhY2UiLCJ1c2VWb2ljZUVuYWJsZWQiLCJ2b2ljZU5zIiwidXNlVm9pY2UiLCJyZXF1aXJlIiwiZW5hYmxlZCIsIl9lIiwib25UcmFuc2NyaXB0IiwidCIsInN0YXRlIiwiY29uc3QiLCJoYW5kbGVLZXlFdmVudCIsIl9mYWxsYmFja01zIiwiUkFQSURfS0VZX0dBUF9NUyIsIk1PRElGSUVSX0ZJUlNUX1BSRVNTX0ZBTExCQUNLX01TIiwiSE9MRF9USFJFU0hPTEQiLCJXQVJNVVBfVEhSRVNIT0xEIiwibWF0Y2hlc0tleWJvYXJkRXZlbnQiLCJlIiwidGFyZ2V0Iiwia2V5IiwidG9Mb3dlckNhc2UiLCJjdHJsIiwic2hpZnQiLCJtZXRhIiwiYWx0Iiwic3VwZXJLZXkiLCJzdXBlciIsIkRFRkFVTFRfVk9JQ0VfS0VZU1RST0tFIiwiSW5zZXJ0VGV4dEhhbmRsZSIsImluc2VydCIsInRleHQiLCJzZXRJbnB1dFdpdGhDdXJzb3IiLCJ2YWx1ZSIsImN1cnNvciIsImN1cnNvck9mZnNldCIsIlVzZVZvaWNlSW50ZWdyYXRpb25BcmdzIiwic2V0SW5wdXRWYWx1ZVJhdyIsIkRpc3BhdGNoIiwiU2V0U3RhdGVBY3Rpb24iLCJpbnB1dFZhbHVlUmVmIiwiUmVmT2JqZWN0IiwiaW5zZXJ0VGV4dFJlZiIsIkludGVyaW1SYW5nZSIsInN0YXJ0IiwiZW5kIiwiU3RyaXBPcHRzIiwiY2hhciIsImFuY2hvciIsImZsb29yIiwiVXNlVm9pY2VJbnRlZ3JhdGlvblJlc3VsdCIsInN0cmlwVHJhaWxpbmciLCJtYXhTdHJpcCIsIm9wdHMiLCJyZXNldEFuY2hvciIsImZhbGxiYWNrTXMiLCJpbnRlcmltUmFuZ2UiLCJ1c2VWb2ljZUludGVncmF0aW9uIiwiYWRkTm90aWZpY2F0aW9uIiwidm9pY2VQcmVmaXhSZWYiLCJ2b2ljZVN1ZmZpeFJlZiIsImxhc3RTZXRJbnB1dFJlZiIsInByZXYiLCJjdXJyZW50Iiwib2Zmc2V0IiwibGVuZ3RoIiwiYmVmb3JlQ3Vyc29yIiwic2xpY2UiLCJhZnRlckN1cnNvciIsInNjYW4iLCJ0cmFpbGluZyIsInN0cmlwQ291bnQiLCJNYXRoIiwibWF4IiwibWluIiwicmVtYWluaW5nIiwic3RyaXBwZWQiLCJnYXAiLCJ0ZXN0IiwibmV3VmFsdWUiLCJwcmVmaXgiLCJzdWZmaXgiLCJyZXN0b3JlZCIsInZvaWNlRW5hYmxlZCIsInZvaWNlU3RhdGUiLCJzIiwidm9pY2VJbnRlcmltVHJhbnNjcmlwdCIsImlucHV0IiwibmVlZHNTcGFjZSIsIm5lZWRzVHJhaWxpbmdTcGFjZSIsImxlYWRpbmdTcGFjZSIsInRyYWlsaW5nU3BhY2UiLCJjdXJzb3JQb3MiLCJoYW5kbGVWb2ljZVRyYW5zY3JpcHQiLCJuZXdJbnB1dCIsInZvaWNlIiwib25FcnJvciIsIm1lc3NhZ2UiLCJjb2xvciIsInByaW9yaXR5IiwidGltZW91dE1zIiwiZm9jdXNNb2RlIiwidXNlVm9pY2VLZXliaW5kaW5nSGFuZGxlciIsInZvaWNlSGFuZGxlS2V5RXZlbnQiLCJpc0FjdGl2ZSIsImhhbmRsZUtleURvd24iLCJnZXRWb2ljZVN0YXRlIiwic2V0Vm9pY2VTdGF0ZSIsImtleWJpbmRpbmdDb250ZXh0IiwiaXNNb2RhbE92ZXJsYXlBY3RpdmUiLCJ2b2ljZUtleXN0cm9rZSIsInJlc3VsdCIsImJpbmRpbmciLCJiaW5kaW5ncyIsImNvbnRleHQiLCJjaG9yZCIsImtzIiwiYWN0aW9uIiwiYmFyZUNoYXIiLCJyYXBpZENvdW50UmVmIiwiY2hhcnNJbklucHV0UmVmIiwicmVjb3JkaW5nRmxvb3JSZWYiLCJpc0hvbGRBY3RpdmVSZWYiLCJyZXNldFRpbWVyUmVmIiwiUmV0dXJuVHlwZSIsInNldFRpbWVvdXQiLCJ2b2ljZVdhcm1pbmdVcCIsInJlcGVhdENvdW50Iiwibm9ybWFsaXplZCIsInJlcGVhdCIsImN1cnJlbnRWb2ljZVN0YXRlIiwic3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uIiwiY291bnRCZWZvcmUiLCJjbGVhclRpbWVvdXQiLCJfaW5wdXQiLCJfa2V5IiwiZXZlbnQiLCJrYkV2ZW50Iiwia2V5cHJlc3MiLCJkaWRTdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24iLCJWb2ljZUtleWJpbmRpbmdIYW5kbGVyIiwicHJvcHMiXSwic291cmNlcyI6WyJ1c2VWb2ljZUludGVncmF0aW9uLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlTWVtbywgdXNlUmVmIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnLi4vY29udGV4dC9ub3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IHsgdXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUgfSBmcm9tICcuLi9jb250ZXh0L292ZXJsYXlDb250ZXh0LmpzJ1xuaW1wb3J0IHtcbiAgdXNlR2V0Vm9pY2VTdGF0ZSxcbiAgdXNlU2V0Vm9pY2VTdGF0ZSxcbiAgdXNlVm9pY2VTdGF0ZSxcbn0gZnJvbSAnLi4vY29udGV4dC92b2ljZS5qcydcbmltcG9ydCB7IEtleWJvYXJkRXZlbnQgfSBmcm9tICcuLi9pbmsvZXZlbnRzL2tleWJvYXJkLWV2ZW50LmpzJ1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9wcmVmZXItdXNlLWtleWJpbmRpbmdzIC0tIGJhY2t3YXJkLWNvbXBhdCBicmlkZ2UgdW50aWwgUkVQTCB3aXJlcyBoYW5kbGVLZXlEb3duIHRvIDxCb3ggb25LZXlEb3duPlxuaW1wb3J0IHsgdXNlSW5wdXQgfSBmcm9tICcuLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0IH0gZnJvbSAnLi4va2V5YmluZGluZ3MvS2V5YmluZGluZ0NvbnRleHQuanMnXG5pbXBvcnQgeyBrZXlzdHJva2VzRXF1YWwgfSBmcm9tICcuLi9rZXliaW5kaW5ncy9yZXNvbHZlci5qcydcbmltcG9ydCB0eXBlIHsgUGFyc2VkS2V5c3Ryb2tlIH0gZnJvbSAnLi4va2V5YmluZGluZ3MvdHlwZXMuanMnXG5pbXBvcnQgeyBub3JtYWxpemVGdWxsV2lkdGhTcGFjZSB9IGZyb20gJy4uL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuaW1wb3J0IHsgdXNlVm9pY2VFbmFibGVkIH0gZnJvbSAnLi91c2VWb2ljZUVuYWJsZWQuanMnXG5cbi8vIERlYWQgY29kZSBlbGltaW5hdGlvbjogY29uZGl0aW9uYWwgaW1wb3J0IGZvciB2b2ljZSBpbnB1dCBob29rLlxuLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuLy8gQ2FwdHVyZSB0aGUgbW9kdWxlIG5hbWVzcGFjZSwgbm90IHRoZSBmdW5jdGlvbjogc3B5T24oKSBtdXRhdGVzIHRoZSBtb2R1bGVcbi8vIG9iamVjdCwgc28gYHZvaWNlTnMudXNlVm9pY2UoLi4uKWAgcmVzb2x2ZXMgdG8gdGhlIHNweSBldmVuIGlmIHRoaXMgbW9kdWxlXG4vLyB3YXMgbG9hZGVkIGJlZm9yZSB0aGUgc3B5IHdhcyBpbnN0YWxsZWQgKHRlc3Qgb3JkZXJpbmcgaW5kZXBlbmRlbmNlKS5cbmNvbnN0IHZvaWNlTnM6IHsgdXNlVm9pY2U6IHR5cGVvZiBpbXBvcnQoJy4vdXNlVm9pY2UuanMnKS51c2VWb2ljZSB9ID0gZmVhdHVyZShcbiAgJ1ZPSUNFX01PREUnLFxuKVxuICA/IHJlcXVpcmUoJy4vdXNlVm9pY2UuanMnKVxuICA6IHtcbiAgICAgIHVzZVZvaWNlOiAoe1xuICAgICAgICBlbmFibGVkOiBfZSxcbiAgICAgIH06IHtcbiAgICAgICAgb25UcmFuc2NyaXB0OiAodDogc3RyaW5nKSA9PiB2b2lkXG4gICAgICAgIGVuYWJsZWQ6IGJvb2xlYW5cbiAgICAgIH0pID0+ICh7XG4gICAgICAgIHN0YXRlOiAnaWRsZScgYXMgY29uc3QsXG4gICAgICAgIGhhbmRsZUtleUV2ZW50OiAoX2ZhbGxiYWNrTXM/OiBudW1iZXIpID0+IHt9LFxuICAgICAgfSksXG4gICAgfVxuLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG5cbi8vIE1heGltdW0gZ2FwIChtcykgYmV0d2VlbiBrZXkgcHJlc3NlcyB0byBjb3VudCBhcyBoZWxkIChhdXRvLXJlcGVhdCkuXG4vLyBUZXJtaW5hbCBhdXRvLXJlcGVhdCBmaXJlcyBldmVyeSAzMC04MG1zOyAxMjBtcyBjb3ZlcnMgaml0dGVyIHdoaWxlXG4vLyBleGNsdWRpbmcgbm9ybWFsIHR5cGluZyBzcGVlZCAoMTAwLTMwMG1zIGJldHdlZW4ga2V5c3Ryb2tlcykuXG5jb25zdCBSQVBJRF9LRVlfR0FQX01TID0gMTIwXG5cbi8vIEZhbGxiYWNrIChtcykgZm9yIG1vZGlmaWVyLWNvbWJvIGZpcnN0LXByZXNzIGFjdGl2YXRpb24uIE11c3QgbWF0Y2hcbi8vIEZJUlNUX1BSRVNTX0ZBTExCQUNLX01TIGluIHVzZVZvaWNlLnRzLiBDb3ZlcnMgdGhlIG1heCBPUyBpbml0aWFsXG4vLyBrZXktcmVwZWF0IGRlbGF5ICh+MnMgb24gbWFjT1Mgd2l0aCBzbGlkZXIgYXQgXCJMb25nXCIpIHNvIGhvbGRpbmcgYVxuLy8gbW9kaWZpZXIgY29tYm8gZG9lc24ndCBmcmFnbWVudCBpbnRvIHR3byBzZXNzaW9ucyB3aGVuIHRoZSBmaXJzdFxuLy8gYXV0by1yZXBlYXQgYXJyaXZlcyBhZnRlciB0aGUgZGVmYXVsdCA2MDBtcyBSRVBFQVRfRkFMTEJBQ0tfTVMuXG5jb25zdCBNT0RJRklFUl9GSVJTVF9QUkVTU19GQUxMQkFDS19NUyA9IDIwMDBcblxuLy8gTnVtYmVyIG9mIHJhcGlkIGNvbnNlY3V0aXZlIGtleSBldmVudHMgcmVxdWlyZWQgdG8gYWN0aXZhdGUgdm9pY2UuXG4vLyBPbmx5IGFwcGxpZXMgdG8gYmFyZS1jaGFyIGJpbmRpbmdzIChzcGFjZSwgdiwgZXRjLikgd2hlcmUgYSBzaW5nbGUgcHJlc3Ncbi8vIGNvdWxkIGJlIG5vcm1hbCB0eXBpbmcuIE1vZGlmaWVyIGNvbWJvcyBhY3RpdmF0ZSBvbiB0aGUgZmlyc3QgcHJlc3MuXG5jb25zdCBIT0xEX1RIUkVTSE9MRCA9IDVcblxuLy8gTnVtYmVyIG9mIHJhcGlkIGtleSBldmVudHMgdG8gc3RhcnQgc2hvd2luZyB3YXJtdXAgZmVlZGJhY2suXG5jb25zdCBXQVJNVVBfVEhSRVNIT0xEID0gMlxuXG4vLyBNYXRjaCBhIEtleWJvYXJkRXZlbnQgYWdhaW5zdCBhIFBhcnNlZEtleXN0cm9rZS4gUmVwbGFjZXMgdGhlIGxlZ2FjeVxuLy8gbWF0Y2hlc0tleXN0cm9rZShpbnB1dCwgS2V5LCAuLi4pIHBhdGggd2hpY2ggYXNzdW1lZCB1c2VJbnB1dCdzIHJhd1xuLy8gYGlucHV0YCBhcmcg4oCUIEtleWJvYXJkRXZlbnQua2V5IGhvbGRzIG5vcm1hbGl6ZWQgbmFtZXMgKGUuZy4gJ3NwYWNlJyxcbi8vICdmOScpIHRoYXQgZ2V0S2V5TmFtZSgpIGRpZG4ndCBoYW5kbGUsIHNvIG1vZGlmaWVyIGNvbWJvcyBhbmQgZi1rZXlzXG4vLyBzaWxlbnRseSBmYWlsZWQgdG8gbWF0Y2ggYWZ0ZXIgdGhlIG9uS2V5RG93biBtaWdyYXRpb24gKCMyMzUyNCkuXG5mdW5jdGlvbiBtYXRjaGVzS2V5Ym9hcmRFdmVudChcbiAgZTogS2V5Ym9hcmRFdmVudCxcbiAgdGFyZ2V0OiBQYXJzZWRLZXlzdHJva2UsXG4pOiBib29sZWFuIHtcbiAgLy8gS2V5Ym9hcmRFdmVudCBzdG9yZXMga2V5IG5hbWVzOyBQYXJzZWRLZXlzdHJva2Ugc3RvcmVzICcgJyBmb3Igc3BhY2VcbiAgLy8gYW5kICdlbnRlcicgZm9yIHJldHVybiAoc2VlIHBhcnNlci50cyBjYXNlICdzcGFjZScvJ3JldHVybicpLlxuICBjb25zdCBrZXkgPVxuICAgIGUua2V5ID09PSAnc3BhY2UnID8gJyAnIDogZS5rZXkgPT09ICdyZXR1cm4nID8gJ2VudGVyJyA6IGUua2V5LnRvTG93ZXJDYXNlKClcbiAgaWYgKGtleSAhPT0gdGFyZ2V0LmtleSkgcmV0dXJuIGZhbHNlXG4gIGlmIChlLmN0cmwgIT09IHRhcmdldC5jdHJsKSByZXR1cm4gZmFsc2VcbiAgaWYgKGUuc2hpZnQgIT09IHRhcmdldC5zaGlmdCkgcmV0dXJuIGZhbHNlXG4gIC8vIEtleWJvYXJkRXZlbnQubWV0YSBmb2xkcyBhbHR8b3B0aW9uICh0ZXJtaW5hbCBsaW1pdGF0aW9uIOKAlCBlc2MtcHJlZml4KTtcbiAgLy8gUGFyc2VkS2V5c3Ryb2tlIGhhcyBib3RoIGFsdCBhbmQgbWV0YSBhcyBhbGlhc2VzIGZvciB0aGUgc2FtZSB0aGluZy5cbiAgaWYgKGUubWV0YSAhPT0gKHRhcmdldC5hbHQgfHwgdGFyZ2V0Lm1ldGEpKSByZXR1cm4gZmFsc2VcbiAgaWYgKGUuc3VwZXJLZXkgIT09IHRhcmdldC5zdXBlcikgcmV0dXJuIGZhbHNlXG4gIHJldHVybiB0cnVlXG59XG5cbi8vIEhhcmRjb2RlZCBkZWZhdWx0IGZvciB3aGVuIHRoZXJlJ3Mgbm8gS2V5YmluZGluZ1Byb3ZpZGVyIGF0IGFsbCAoZS5nLlxuLy8gaGVhZGxlc3MvdGVzdCBjb250ZXh0cykuIE5PVCB1c2VkIHdoZW4gdGhlIHByb3ZpZGVyIGV4aXN0cyBhbmQgdGhlXG4vLyBsb29rdXAgcmV0dXJucyBudWxsIOKAlCB0aGF0IG1lYW5zIHRoZSB1c2VyIG51bGwtdW5ib3VuZCBvciByZWFzc2lnbmVkXG4vLyBzcGFjZSwgYW5kIGZhbGxpbmcgYmFjayB0byBzcGFjZSB3b3VsZCBwaWNrIGEgZGVhZCBvciBjb25mbGljdGluZyBrZXkuXG5jb25zdCBERUZBVUxUX1ZPSUNFX0tFWVNUUk9LRTogUGFyc2VkS2V5c3Ryb2tlID0ge1xuICBrZXk6ICcgJyxcbiAgY3RybDogZmFsc2UsXG4gIGFsdDogZmFsc2UsXG4gIHNoaWZ0OiBmYWxzZSxcbiAgbWV0YTogZmFsc2UsXG4gIHN1cGVyOiBmYWxzZSxcbn1cblxudHlwZSBJbnNlcnRUZXh0SGFuZGxlID0ge1xuICBpbnNlcnQ6ICh0ZXh0OiBzdHJpbmcpID0+IHZvaWRcbiAgc2V0SW5wdXRXaXRoQ3Vyc29yOiAodmFsdWU6IHN0cmluZywgY3Vyc29yOiBudW1iZXIpID0+IHZvaWRcbiAgY3Vyc29yT2Zmc2V0OiBudW1iZXJcbn1cblxudHlwZSBVc2VWb2ljZUludGVncmF0aW9uQXJncyA9IHtcbiAgc2V0SW5wdXRWYWx1ZVJhdzogUmVhY3QuRGlzcGF0Y2g8UmVhY3QuU2V0U3RhdGVBY3Rpb248c3RyaW5nPj5cbiAgaW5wdXRWYWx1ZVJlZjogUmVhY3QuUmVmT2JqZWN0PHN0cmluZz5cbiAgaW5zZXJ0VGV4dFJlZjogUmVhY3QuUmVmT2JqZWN0PEluc2VydFRleHRIYW5kbGUgfCBudWxsPlxufVxuXG50eXBlIEludGVyaW1SYW5nZSA9IHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfVxuXG50eXBlIFN0cmlwT3B0cyA9IHtcbiAgLy8gV2hpY2ggY2hhciB0byBzdHJpcCAodGhlIGNvbmZpZ3VyZWQgaG9sZCBrZXkpLiBEZWZhdWx0cyB0byBzcGFjZS5cbiAgY2hhcj86IHN0cmluZ1xuICAvLyBDYXB0dXJlIHRoZSB2b2ljZSBwcmVmaXgvc3VmZml4IGFuY2hvciBhdCB0aGUgc3RyaXBwZWQgcG9zaXRpb24uXG4gIGFuY2hvcj86IGJvb2xlYW5cbiAgLy8gTWluaW11bSB0cmFpbGluZyBjb3VudCB0byBsZWF2ZSBiZWhpbmQg4oCUIHByZXZlbnRzIHN0cmlwcGluZyB0aGVcbiAgLy8gaW50ZW50aW9uYWwgd2FybXVwIGNoYXJzIHdoZW4gZGVmZW5zaXZlbHkgY2xlYW5pbmcgdXAgbGVha3MuXG4gIGZsb29yPzogbnVtYmVyXG59XG5cbnR5cGUgVXNlVm9pY2VJbnRlZ3JhdGlvblJlc3VsdCA9IHtcbiAgLy8gUmV0dXJucyB0aGUgbnVtYmVyIG9mIHRyYWlsaW5nIGNoYXJzIHJlbWFpbmluZyBhZnRlciBzdHJpcHBpbmcuXG4gIHN0cmlwVHJhaWxpbmc6IChtYXhTdHJpcDogbnVtYmVyLCBvcHRzPzogU3RyaXBPcHRzKSA9PiBudW1iZXJcbiAgLy8gVW5kbyB0aGUgZ2FwIHNwYWNlIGFuZCByZXNldCBhbmNob3IgcmVmcyBhZnRlciBhIGZhaWxlZCB2b2ljZSBhY3RpdmF0aW9uLlxuICByZXNldEFuY2hvcjogKCkgPT4gdm9pZFxuICBoYW5kbGVLZXlFdmVudDogKGZhbGxiYWNrTXM/OiBudW1iZXIpID0+IHZvaWRcbiAgaW50ZXJpbVJhbmdlOiBJbnRlcmltUmFuZ2UgfCBudWxsXG59XG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VWb2ljZUludGVncmF0aW9uKHtcbiAgc2V0SW5wdXRWYWx1ZVJhdyxcbiAgaW5wdXRWYWx1ZVJlZixcbiAgaW5zZXJ0VGV4dFJlZixcbn06IFVzZVZvaWNlSW50ZWdyYXRpb25BcmdzKTogVXNlVm9pY2VJbnRlZ3JhdGlvblJlc3VsdCB7XG4gIGNvbnN0IHsgYWRkTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcblxuICAvLyBUcmFja3MgdGhlIGlucHV0IGNvbnRlbnQgYmVmb3JlL2FmdGVyIHRoZSBjdXJzb3Igd2hlbiB2b2ljZSBzdGFydHMsXG4gIC8vIHNvIGludGVyaW0gdHJhbnNjcmlwdHMgY2FuIGJlIGluc2VydGVkIGF0IHRoZSBjdXJzb3IgcG9zaXRpb24gd2l0aG91dFxuICAvLyBjbG9iYmVyaW5nIHN1cnJvdW5kaW5nIHVzZXIgdGV4dC5cbiAgY29uc3Qgdm9pY2VQcmVmaXhSZWYgPSB1c2VSZWY8c3RyaW5nIHwgbnVsbD4obnVsbClcbiAgY29uc3Qgdm9pY2VTdWZmaXhSZWYgPSB1c2VSZWY8c3RyaW5nPignJylcbiAgLy8gVHJhY2tzIHRoZSBsYXN0IGlucHV0IHZhbHVlIHRoaXMgaG9vayB3cm90ZSAodmlhIGFuY2hvciwgaW50ZXJpbSBlZmZlY3QsXG4gIC8vIG9yIGhhbmRsZVZvaWNlVHJhbnNjcmlwdCkuIElmIGlucHV0VmFsdWVSZWYuY3VycmVudCBkaXZlcmdlcywgdGhlIHVzZXJcbiAgLy8gc3VibWl0dGVkIG9yIGVkaXRlZCDigJQgYm90aCB3cml0ZSBwYXRocyBiYWlsIHRvIGF2b2lkIGNsb2JiZXJpbmcuIFRoaXMgaXNcbiAgLy8gdGhlIG9ubHkgZ3VhcmQgdGhhdCBjb3JyZWN0bHkgaGFuZGxlcyBlbXB0eS1wcmVmaXgtZW1wdHktc3VmZml4OiBhXG4gIC8vIHN0YXJ0c1dpdGgoJycpL2VuZHNXaXRoKCcnKSBjaGVjayB2YWN1b3VzbHkgcGFzc2VzLCBhbmQgYSBsZW5ndGggY2hlY2tcbiAgLy8gY2FuJ3QgZGlzdGluZ3Vpc2ggYSBjbGVhcmVkIGlucHV0IGZyb20gYSBuZXZlci1zZXQgb25lLlxuICBjb25zdCBsYXN0U2V0SW5wdXRSZWYgPSB1c2VSZWY8c3RyaW5nIHwgbnVsbD4obnVsbClcblxuICAvLyBTdHJpcCB0cmFpbGluZyBob2xkLWtleSBjaGFycyAoYW5kIG9wdGlvbmFsbHkgY2FwdHVyZSB0aGUgdm9pY2VcbiAgLy8gYW5jaG9yKS4gQ2FsbGVkIGR1cmluZyB3YXJtdXAgKHRvIGNsZWFuIHVwIGNoYXJzIHRoYXQgbGVha2VkIHBhc3RcbiAgLy8gc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uIOKAlCBsaXN0ZW5lciBvcmRlciBpcyBub3QgZ3VhcmFudGVlZCkgYW5kXG4gIC8vIG9uIGFjdGl2YXRpb24gKHdpdGggYW5jaG9yPXRydWUgdG8gY2FwdHVyZSB0aGUgcHJlZml4L3N1ZmZpeCBhcm91bmRcbiAgLy8gdGhlIGN1cnNvciBmb3IgaW50ZXJpbSB0cmFuc2NyaXB0IHBsYWNlbWVudCkuIFRoZSBjYWxsZXIgcGFzc2VzIHRoZVxuICAvLyBleGFjdCBjb3VudCBpdCBleHBlY3RzIHRvIHN0cmlwIHNvIHByZS1leGlzdGluZyBjaGFycyBhdCB0aGVcbiAgLy8gYm91bmRhcnkgYXJlIHByZXNlcnZlZCAoZS5nLiB0aGUgXCJ2XCIgaW4gXCJoYXZcIiB3aGVuIGhvbGQta2V5IGlzIFwidlwiKS5cbiAgLy8gVGhlIGZsb29yIG9wdGlvbiBzZXRzIGEgbWluaW11bSB0cmFpbGluZyBjb3VudCB0byBsZWF2ZSBiZWhpbmRcbiAgLy8gKGR1cmluZyB3YXJtdXAgdGhpcyBpcyB0aGUgY291bnQgd2UgaW50ZW50aW9uYWxseSBsZXQgdGhyb3VnaCwgc29cbiAgLy8gZGVmZW5zaXZlIGNsZWFudXAgb25seSByZW1vdmVzIGxlYWtzKS4gUmV0dXJucyB0aGUgbnVtYmVyIG9mXG4gIC8vIHRyYWlsaW5nIGNoYXJzIHJlbWFpbmluZyBhZnRlciBzdHJpcHBpbmcuIFdoZW4gbm90aGluZyBjaGFuZ2VzLCBub1xuICAvLyBzdGF0ZSB1cGRhdGUgaXMgcGVyZm9ybWVkLlxuICBjb25zdCBzdHJpcFRyYWlsaW5nID0gdXNlQ2FsbGJhY2soXG4gICAgKFxuICAgICAgbWF4U3RyaXA6IG51bWJlcixcbiAgICAgIHsgY2hhciA9ICcgJywgYW5jaG9yID0gZmFsc2UsIGZsb29yID0gMCB9OiBTdHJpcE9wdHMgPSB7fSxcbiAgICApID0+IHtcbiAgICAgIGNvbnN0IHByZXYgPSBpbnB1dFZhbHVlUmVmLmN1cnJlbnRcbiAgICAgIGNvbnN0IG9mZnNldCA9IGluc2VydFRleHRSZWYuY3VycmVudD8uY3Vyc29yT2Zmc2V0ID8/IHByZXYubGVuZ3RoXG4gICAgICBjb25zdCBiZWZvcmVDdXJzb3IgPSBwcmV2LnNsaWNlKDAsIG9mZnNldClcbiAgICAgIGNvbnN0IGFmdGVyQ3Vyc29yID0gcHJldi5zbGljZShvZmZzZXQpXG4gICAgICAvLyBXaGVuIHRoZSBob2xkIGtleSBpcyBzcGFjZSwgYWxzbyBjb3VudCBmdWxsLXdpZHRoIHNwYWNlcyAoVSszMDAwKVxuICAgICAgLy8gdGhhdCBhIENKSyBJTUUgbWF5IGhhdmUgaW5zZXJ0ZWQgZm9yIHRoZSBzYW1lIHBoeXNpY2FsIGtleS5cbiAgICAgIC8vIFUrMzAwMCBpcyBCTVAgc2luZ2xlLWNvZGUtdW5pdCBzbyBpbmRpY2VzIGFsaWduIHdpdGggYmVmb3JlQ3Vyc29yLlxuICAgICAgY29uc3Qgc2NhbiA9XG4gICAgICAgIGNoYXIgPT09ICcgJyA/IG5vcm1hbGl6ZUZ1bGxXaWR0aFNwYWNlKGJlZm9yZUN1cnNvcikgOiBiZWZvcmVDdXJzb3JcbiAgICAgIGxldCB0cmFpbGluZyA9IDBcbiAgICAgIHdoaWxlIChcbiAgICAgICAgdHJhaWxpbmcgPCBzY2FuLmxlbmd0aCAmJlxuICAgICAgICBzY2FuW3NjYW4ubGVuZ3RoIC0gMSAtIHRyYWlsaW5nXSA9PT0gY2hhclxuICAgICAgKSB7XG4gICAgICAgIHRyYWlsaW5nKytcbiAgICAgIH1cbiAgICAgIGNvbnN0IHN0cmlwQ291bnQgPSBNYXRoLm1heCgwLCBNYXRoLm1pbih0cmFpbGluZyAtIGZsb29yLCBtYXhTdHJpcCkpXG4gICAgICBjb25zdCByZW1haW5pbmcgPSB0cmFpbGluZyAtIHN0cmlwQ291bnRcbiAgICAgIGNvbnN0IHN0cmlwcGVkID0gYmVmb3JlQ3Vyc29yLnNsaWNlKDAsIGJlZm9yZUN1cnNvci5sZW5ndGggLSBzdHJpcENvdW50KVxuICAgICAgLy8gV2hlbiBhbmNob3Jpbmcgd2l0aCBhIG5vbi1zcGFjZSBzdWZmaXgsIGluc2VydCBhIGdhcCBzcGFjZSBzbyB0aGVcbiAgICAgIC8vIHdhdmVmb3JtIGN1cnNvciBzaXRzIG9uIHRoZSBnYXAgaW5zdGVhZCBvZiBjb3ZlcmluZyB0aGUgZmlyc3RcbiAgICAgIC8vIHN1ZmZpeCBsZXR0ZXIuIFRoZSBpbnRlcmltIHRyYW5zY3JpcHQgZWZmZWN0IG1haW50YWlucyB0aGlzIHNhbWVcbiAgICAgIC8vIHN0cnVjdHVyZSAocHJlZml4ICsgbGVhZGluZyArIGludGVyaW0gKyB0cmFpbGluZyArIHN1ZmZpeCksIHNvXG4gICAgICAvLyB0aGUgZ2FwIGlzIHNlYW1sZXNzIG9uY2UgdHJhbnNjcmlwdCB0ZXh0IGFycml2ZXMuXG4gICAgICAvLyBBbHdheXMgb3ZlcndyaXRlIG9uIGFuY2hvciDigJQgaWYgYSBwcmlvciBhY3RpdmF0aW9uIGZhaWxlZCB0byBzdGFydFxuICAgICAgLy8gdm9pY2UgKHZvaWNlU3RhdGUgc3RheWVkICdpZGxlJyksIHRoZSBjbGVhbnVwIGVmZmVjdCBkaWRuJ3QgZmlyZSBhbmRcbiAgICAgIC8vIHRoZSBvbGQgYW5jaG9yIGlzIHN0YWxlLiBhbmNob3I9dHJ1ZSBpcyBvbmx5IHBhc3NlZCBvbiB0aGUgc2luZ2xlXG4gICAgICAvLyBhY3RpdmF0aW9uIGNhbGwsIG5ldmVyIGR1cmluZyByZWNvcmRpbmcsIHNvIG92ZXJ3cml0ZSBpcyBzYWZlLlxuICAgICAgbGV0IGdhcCA9ICcnXG4gICAgICBpZiAoYW5jaG9yKSB7XG4gICAgICAgIHZvaWNlUHJlZml4UmVmLmN1cnJlbnQgPSBzdHJpcHBlZFxuICAgICAgICB2b2ljZVN1ZmZpeFJlZi5jdXJyZW50ID0gYWZ0ZXJDdXJzb3JcbiAgICAgICAgaWYgKGFmdGVyQ3Vyc29yLmxlbmd0aCA+IDAgJiYgIS9eXFxzLy50ZXN0KGFmdGVyQ3Vyc29yKSkge1xuICAgICAgICAgIGdhcCA9ICcgJ1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb25zdCBuZXdWYWx1ZSA9IHN0cmlwcGVkICsgZ2FwICsgYWZ0ZXJDdXJzb3JcbiAgICAgIGlmIChhbmNob3IpIGxhc3RTZXRJbnB1dFJlZi5jdXJyZW50ID0gbmV3VmFsdWVcbiAgICAgIGlmIChuZXdWYWx1ZSA9PT0gcHJldiAmJiBzdHJpcENvdW50ID09PSAwKSByZXR1cm4gcmVtYWluaW5nXG4gICAgICBpZiAoaW5zZXJ0VGV4dFJlZi5jdXJyZW50KSB7XG4gICAgICAgIGluc2VydFRleHRSZWYuY3VycmVudC5zZXRJbnB1dFdpdGhDdXJzb3IobmV3VmFsdWUsIHN0cmlwcGVkLmxlbmd0aClcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNldElucHV0VmFsdWVSYXcobmV3VmFsdWUpXG4gICAgICB9XG4gICAgICByZXR1cm4gcmVtYWluaW5nXG4gICAgfSxcbiAgICBbc2V0SW5wdXRWYWx1ZVJhdywgaW5wdXRWYWx1ZVJlZiwgaW5zZXJ0VGV4dFJlZl0sXG4gIClcblxuICAvLyBVbmRvIHRoZSBnYXAgc3BhY2UgaW5zZXJ0ZWQgYnkgc3RyaXBUcmFpbGluZyguLi4sIHthbmNob3I6dHJ1ZX0pIGFuZFxuICAvLyByZXNldCB0aGUgdm9pY2UgcHJlZml4L3N1ZmZpeCByZWZzLiBDYWxsZWQgd2hlbiB2b2ljZSBhY3RpdmF0aW9uIGZhaWxzXG4gIC8vICh2b2ljZVN0YXRlIHN0YXlzICdpZGxlJyBhZnRlciB2b2ljZUhhbmRsZUtleUV2ZW50KSwgc28gdGhlIGNsZWFudXBcbiAgLy8gZWZmZWN0ICh2b2ljZVN0YXRlIHVzZUVmZmVjdCBiZWxvdykg4oCUIHdoaWNoIG9ubHkgZmlyZXMgb24gdm9pY2VTdGF0ZSB0cmFuc2l0aW9ucyDigJQgY2FuJ3RcbiAgLy8gcmVhY2ggdGhlIHN0YWxlIGFuY2hvci4gV2l0aG91dCB0aGlzLCB0aGUgZ2FwIHNwYWNlIGFuZCBzdGFsZSByZWZzXG4gIC8vIHBlcnNpc3QgaW4gdGhlIGlucHV0LlxuICBjb25zdCByZXNldEFuY2hvciA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBjb25zdCBwcmVmaXggPSB2b2ljZVByZWZpeFJlZi5jdXJyZW50XG4gICAgaWYgKHByZWZpeCA9PT0gbnVsbCkgcmV0dXJuXG4gICAgY29uc3Qgc3VmZml4ID0gdm9pY2VTdWZmaXhSZWYuY3VycmVudFxuICAgIHZvaWNlUHJlZml4UmVmLmN1cnJlbnQgPSBudWxsXG4gICAgdm9pY2VTdWZmaXhSZWYuY3VycmVudCA9ICcnXG4gICAgY29uc3QgcmVzdG9yZWQgPSBwcmVmaXggKyBzdWZmaXhcbiAgICBpZiAoaW5zZXJ0VGV4dFJlZi5jdXJyZW50KSB7XG4gICAgICBpbnNlcnRUZXh0UmVmLmN1cnJlbnQuc2V0SW5wdXRXaXRoQ3Vyc29yKHJlc3RvcmVkLCBwcmVmaXgubGVuZ3RoKVxuICAgIH0gZWxzZSB7XG4gICAgICBzZXRJbnB1dFZhbHVlUmF3KHJlc3RvcmVkKVxuICAgIH1cbiAgfSwgW3NldElucHV0VmFsdWVSYXcsIGluc2VydFRleHRSZWZdKVxuXG4gIC8vIFZvaWNlIHN0YXRlIHNlbGVjdG9ycy4gdXNlVm9pY2VFbmFibGVkID0gdXNlciBpbnRlbnQgKHNldHRpbmdzKSArXG4gIC8vIGF1dGggKyBHQiBraWxsLXN3aXRjaCwgd2l0aCB0aGUgYXV0aCBoYWxmIG1lbW9pemVkIG9uIGF1dGhWZXJzaW9uIHNvXG4gIC8vIHJlbmRlciBsb29wcyBuZXZlciBoaXQgYSBjb2xkIGtleWNoYWluIHNwYXduLlxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gIGNvbnN0IHZvaWNlRW5hYmxlZCA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKSA/IHVzZVZvaWNlRW5hYmxlZCgpIDogZmFsc2VcbiAgY29uc3Qgdm9pY2VTdGF0ZSA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKVxuICAgID8gLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICAgICAgdXNlVm9pY2VTdGF0ZShzID0+IHMudm9pY2VTdGF0ZSlcbiAgICA6ICgnaWRsZScgYXMgY29uc3QpXG4gIGNvbnN0IHZvaWNlSW50ZXJpbVRyYW5zY3JpcHQgPSBmZWF0dXJlKCdWT0lDRV9NT0RFJylcbiAgICA/IC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgICAgIHVzZVZvaWNlU3RhdGUocyA9PiBzLnZvaWNlSW50ZXJpbVRyYW5zY3JpcHQpXG4gICAgOiAnJ1xuXG4gIC8vIFNldCB0aGUgdm9pY2UgYW5jaG9yIGZvciBmb2N1cyBtb2RlICh3aGVyZSByZWNvcmRpbmcgc3RhcnRzIHZpYSB0ZXJtaW5hbFxuICAvLyBmb2N1cywgbm90IGtleSBob2xkKS4gS2V5LWhvbGQgc2V0cyB0aGUgYW5jaG9yIGluIHN0cmlwVHJhaWxpbmcuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKCFmZWF0dXJlKCdWT0lDRV9NT0RFJykpIHJldHVyblxuICAgIGlmICh2b2ljZVN0YXRlID09PSAncmVjb3JkaW5nJyAmJiB2b2ljZVByZWZpeFJlZi5jdXJyZW50ID09PSBudWxsKSB7XG4gICAgICBjb25zdCBpbnB1dCA9IGlucHV0VmFsdWVSZWYuY3VycmVudFxuICAgICAgY29uc3Qgb2Zmc2V0ID0gaW5zZXJ0VGV4dFJlZi5jdXJyZW50Py5jdXJzb3JPZmZzZXQgPz8gaW5wdXQubGVuZ3RoXG4gICAgICB2b2ljZVByZWZpeFJlZi5jdXJyZW50ID0gaW5wdXQuc2xpY2UoMCwgb2Zmc2V0KVxuICAgICAgdm9pY2VTdWZmaXhSZWYuY3VycmVudCA9IGlucHV0LnNsaWNlKG9mZnNldClcbiAgICAgIGxhc3RTZXRJbnB1dFJlZi5jdXJyZW50ID0gaW5wdXRcbiAgICB9XG4gICAgaWYgKHZvaWNlU3RhdGUgPT09ICdpZGxlJykge1xuICAgICAgdm9pY2VQcmVmaXhSZWYuY3VycmVudCA9IG51bGxcbiAgICAgIHZvaWNlU3VmZml4UmVmLmN1cnJlbnQgPSAnJ1xuICAgICAgbGFzdFNldElucHV0UmVmLmN1cnJlbnQgPSBudWxsXG4gICAgfVxuICB9LCBbdm9pY2VTdGF0ZSwgaW5wdXRWYWx1ZVJlZiwgaW5zZXJ0VGV4dFJlZl0pXG5cbiAgLy8gTGl2ZS11cGRhdGUgdGhlIHByb21wdCBpbnB1dCB3aXRoIHRoZSBpbnRlcmltIHRyYW5zY3JpcHQgYXMgdm9pY2VcbiAgLy8gdHJhbnNjcmliZXMgc3BlZWNoLiBUaGUgcHJlZml4ICh1c2VyLXR5cGVkIHRleHQgYmVmb3JlIHRoZSBjdXJzb3IpIGlzXG4gIC8vIHByZXNlcnZlZCBhbmQgdGhlIHRyYW5zY3JpcHQgaXMgaW5zZXJ0ZWQgYmV0d2VlbiBwcmVmaXggYW5kIHN1ZmZpeC5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoIWZlYXR1cmUoJ1ZPSUNFX01PREUnKSkgcmV0dXJuXG4gICAgaWYgKHZvaWNlUHJlZml4UmVmLmN1cnJlbnQgPT09IG51bGwpIHJldHVyblxuICAgIGNvbnN0IHByZWZpeCA9IHZvaWNlUHJlZml4UmVmLmN1cnJlbnRcbiAgICBjb25zdCBzdWZmaXggPSB2b2ljZVN1ZmZpeFJlZi5jdXJyZW50XG4gICAgLy8gU3VibWl0IHJhY2U6IGlmIHRoZSBpbnB1dCBpc24ndCB3aGF0IHRoaXMgaG9vayBsYXN0IHNldCBpdCB0bywgdGhlXG4gICAgLy8gdXNlciBzdWJtaXR0ZWQgKGNsZWFyaW5nIGl0KSBvciBlZGl0ZWQgaXQuIHZvaWNlUHJlZml4UmVmIGlzIG9ubHlcbiAgICAvLyBjbGVhcmVkIG9uIHZvaWNlU3RhdGXihpJpZGxlLCBzbyBpdCdzIHN0aWxsIHNldCBkdXJpbmcgdGhlICdwcm9jZXNzaW5nJ1xuICAgIC8vIHdpbmRvdyBiZXR3ZWVuIENsb3NlU3RyZWFtIGFuZCBXUyBjbG9zZSDigJQgdGhpcyBjYXRjaGVzIHJlZmluZWRcbiAgICAvLyBUcmFuc2NyaXB0VGV4dCBhcnJpdmluZyB0aGVuIGFuZCByZS1maWxsaW5nIGEgY2xlYXJlZCBpbnB1dC5cbiAgICBpZiAoaW5wdXRWYWx1ZVJlZi5jdXJyZW50ICE9PSBsYXN0U2V0SW5wdXRSZWYuY3VycmVudCkgcmV0dXJuXG4gICAgY29uc3QgbmVlZHNTcGFjZSA9XG4gICAgICBwcmVmaXgubGVuZ3RoID4gMCAmJlxuICAgICAgIS9cXHMkLy50ZXN0KHByZWZpeCkgJiZcbiAgICAgIHZvaWNlSW50ZXJpbVRyYW5zY3JpcHQubGVuZ3RoID4gMFxuICAgIC8vIERvbid0IGdhdGUgb24gdm9pY2VJbnRlcmltVHJhbnNjcmlwdC5sZW5ndGggLS0gd2hlbiBpbnRlcmltIGNsZWFycyB0byAnJ1xuICAgIC8vIGFmdGVyIGhhbmRsZVZvaWNlVHJhbnNjcmlwdCBzZXRzIHRoZSBmaW5hbCB0ZXh0LCB0aGUgdHJhaWxpbmcgc3BhY2VcbiAgICAvLyBiZXR3ZWVuIHByZWZpeCBhbmQgc3VmZml4IG11c3Qgc3RpbGwgYmUgcHJlc2VydmVkLlxuICAgIGNvbnN0IG5lZWRzVHJhaWxpbmdTcGFjZSA9IHN1ZmZpeC5sZW5ndGggPiAwICYmICEvXlxccy8udGVzdChzdWZmaXgpXG4gICAgY29uc3QgbGVhZGluZ1NwYWNlID0gbmVlZHNTcGFjZSA/ICcgJyA6ICcnXG4gICAgY29uc3QgdHJhaWxpbmdTcGFjZSA9IG5lZWRzVHJhaWxpbmdTcGFjZSA/ICcgJyA6ICcnXG4gICAgY29uc3QgbmV3VmFsdWUgPVxuICAgICAgcHJlZml4ICsgbGVhZGluZ1NwYWNlICsgdm9pY2VJbnRlcmltVHJhbnNjcmlwdCArIHRyYWlsaW5nU3BhY2UgKyBzdWZmaXhcbiAgICAvLyBQb3NpdGlvbiBjdXJzb3IgYWZ0ZXIgdGhlIHRyYW5zY3JpYmVkIHRleHQgKGJlZm9yZSBzdWZmaXgpXG4gICAgY29uc3QgY3Vyc29yUG9zID1cbiAgICAgIHByZWZpeC5sZW5ndGggKyBsZWFkaW5nU3BhY2UubGVuZ3RoICsgdm9pY2VJbnRlcmltVHJhbnNjcmlwdC5sZW5ndGhcbiAgICBpZiAoaW5zZXJ0VGV4dFJlZi5jdXJyZW50KSB7XG4gICAgICBpbnNlcnRUZXh0UmVmLmN1cnJlbnQuc2V0SW5wdXRXaXRoQ3Vyc29yKG5ld1ZhbHVlLCBjdXJzb3JQb3MpXG4gICAgfSBlbHNlIHtcbiAgICAgIHNldElucHV0VmFsdWVSYXcobmV3VmFsdWUpXG4gICAgfVxuICAgIGxhc3RTZXRJbnB1dFJlZi5jdXJyZW50ID0gbmV3VmFsdWVcbiAgfSwgW3ZvaWNlSW50ZXJpbVRyYW5zY3JpcHQsIHNldElucHV0VmFsdWVSYXcsIGlucHV0VmFsdWVSZWYsIGluc2VydFRleHRSZWZdKVxuXG4gIGNvbnN0IGhhbmRsZVZvaWNlVHJhbnNjcmlwdCA9IHVzZUNhbGxiYWNrKFxuICAgICh0ZXh0OiBzdHJpbmcpID0+IHtcbiAgICAgIGlmICghZmVhdHVyZSgnVk9JQ0VfTU9ERScpKSByZXR1cm5cbiAgICAgIGNvbnN0IHByZWZpeCA9IHZvaWNlUHJlZml4UmVmLmN1cnJlbnRcbiAgICAgIC8vIE5vIHZvaWNlIGFuY2hvciDigJQgdm9pY2Ugd2FzIHJlc2V0IChvciBuZXZlciBzdGFydGVkKS4gTm90aGluZyB0byBkby5cbiAgICAgIGlmIChwcmVmaXggPT09IG51bGwpIHJldHVyblxuICAgICAgY29uc3Qgc3VmZml4ID0gdm9pY2VTdWZmaXhSZWYuY3VycmVudFxuICAgICAgLy8gU3VibWl0IHJhY2U6IGZpbmlzaFJlY29yZGluZygpIOKGkiB1c2VyIHByZXNzZXMgRW50ZXIgKGlucHV0IGNsZWFyZWQpXG4gICAgICAvLyDihpIgV2ViU29ja2V0IGNsb3NlIOKGkiB0aGlzIGNhbGxiYWNrIGZpcmVzIHdpdGggc3RhbGUgcHJlZml4L3N1ZmZpeC5cbiAgICAgIC8vIElmIHRoZSBpbnB1dCBpc24ndCB3aGF0IHRoaXMgaG9vayBsYXN0IHNldCAodmlhIHRoZSBpbnRlcmltIGVmZmVjdFxuICAgICAgLy8gb3IgYW5jaG9yKSwgdGhlIHVzZXIgc3VibWl0dGVkIG9yIGVkaXRlZCDigJQgZG9uJ3QgcmUtZmlsbC4gQ29tcGFyaW5nXG4gICAgICAvLyBhZ2FpbnN0IGB0ZXh0Lmxlbmd0aGAgd291bGQgZmFsc2UtcG9zaXRpdmUgd2hlbiB0aGUgZmluYWwgaXMgbG9uZ2VyXG4gICAgICAvLyB0aGFuIHRoZSBpbnRlcmltIChBU1Igcm91dGluZWx5IGFkZHMgcHVuY3R1YXRpb24vY29ycmVjdGlvbnMpLlxuICAgICAgaWYgKGlucHV0VmFsdWVSZWYuY3VycmVudCAhPT0gbGFzdFNldElucHV0UmVmLmN1cnJlbnQpIHJldHVyblxuICAgICAgY29uc3QgbmVlZHNTcGFjZSA9XG4gICAgICAgIHByZWZpeC5sZW5ndGggPiAwICYmICEvXFxzJC8udGVzdChwcmVmaXgpICYmIHRleHQubGVuZ3RoID4gMFxuICAgICAgY29uc3QgbmVlZHNUcmFpbGluZ1NwYWNlID1cbiAgICAgICAgc3VmZml4Lmxlbmd0aCA+IDAgJiYgIS9eXFxzLy50ZXN0KHN1ZmZpeCkgJiYgdGV4dC5sZW5ndGggPiAwXG4gICAgICBjb25zdCBsZWFkaW5nU3BhY2UgPSBuZWVkc1NwYWNlID8gJyAnIDogJydcbiAgICAgIGNvbnN0IHRyYWlsaW5nU3BhY2UgPSBuZWVkc1RyYWlsaW5nU3BhY2UgPyAnICcgOiAnJ1xuICAgICAgY29uc3QgbmV3SW5wdXQgPSBwcmVmaXggKyBsZWFkaW5nU3BhY2UgKyB0ZXh0ICsgdHJhaWxpbmdTcGFjZSArIHN1ZmZpeFxuICAgICAgLy8gUG9zaXRpb24gY3Vyc29yIGFmdGVyIHRoZSB0cmFuc2NyaWJlZCB0ZXh0IChiZWZvcmUgc3VmZml4KVxuICAgICAgY29uc3QgY3Vyc29yUG9zID0gcHJlZml4Lmxlbmd0aCArIGxlYWRpbmdTcGFjZS5sZW5ndGggKyB0ZXh0Lmxlbmd0aFxuICAgICAgaWYgKGluc2VydFRleHRSZWYuY3VycmVudCkge1xuICAgICAgICBpbnNlcnRUZXh0UmVmLmN1cnJlbnQuc2V0SW5wdXRXaXRoQ3Vyc29yKG5ld0lucHV0LCBjdXJzb3JQb3MpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZXRJbnB1dFZhbHVlUmF3KG5ld0lucHV0KVxuICAgICAgfVxuICAgICAgbGFzdFNldElucHV0UmVmLmN1cnJlbnQgPSBuZXdJbnB1dFxuICAgICAgLy8gVXBkYXRlIHRoZSBwcmVmaXggdG8gaW5jbHVkZSB0aGlzIGNodW5rIHNvIGZvY3VzIG1vZGUgY2FuIGNvbnRpbnVlXG4gICAgICAvLyBhcHBlbmRpbmcgc3Vic2VxdWVudCB0cmFuc2NyaXB0cyBhZnRlciBpdC5cbiAgICAgIHZvaWNlUHJlZml4UmVmLmN1cnJlbnQgPSBwcmVmaXggKyBsZWFkaW5nU3BhY2UgKyB0ZXh0XG4gICAgfSxcbiAgICBbc2V0SW5wdXRWYWx1ZVJhdywgaW5wdXRWYWx1ZVJlZiwgaW5zZXJ0VGV4dFJlZl0sXG4gIClcblxuICBjb25zdCB2b2ljZSA9IHZvaWNlTnMudXNlVm9pY2Uoe1xuICAgIG9uVHJhbnNjcmlwdDogaGFuZGxlVm9pY2VUcmFuc2NyaXB0LFxuICAgIG9uRXJyb3I6IChtZXNzYWdlOiBzdHJpbmcpID0+IHtcbiAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgIGtleTogJ3ZvaWNlLWVycm9yJyxcbiAgICAgICAgdGV4dDogbWVzc2FnZSxcbiAgICAgICAgY29sb3I6ICdlcnJvcicsXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgdGltZW91dE1zOiAxMF8wMDAsXG4gICAgICB9KVxuICAgIH0sXG4gICAgZW5hYmxlZDogdm9pY2VFbmFibGVkLFxuICAgIGZvY3VzTW9kZTogZmFsc2UsXG4gIH0pXG5cbiAgLy8gQ29tcHV0ZSB0aGUgY2hhcmFjdGVyIHJhbmdlIG9mIGludGVyaW0gKG5vdC15ZXQtZmluYWxpemVkKSB0cmFuc2NyaXB0XG4gIC8vIHRleHQgaW4gdGhlIGlucHV0IHZhbHVlLCBzbyB0aGUgVUkgY2FuIGRpbSBpdC5cbiAgY29uc3QgaW50ZXJpbVJhbmdlID0gdXNlTWVtbygoKTogSW50ZXJpbVJhbmdlIHwgbnVsbCA9PiB7XG4gICAgaWYgKCFmZWF0dXJlKCdWT0lDRV9NT0RFJykpIHJldHVybiBudWxsXG4gICAgaWYgKHZvaWNlUHJlZml4UmVmLmN1cnJlbnQgPT09IG51bGwpIHJldHVybiBudWxsXG4gICAgaWYgKHZvaWNlSW50ZXJpbVRyYW5zY3JpcHQubGVuZ3RoID09PSAwKSByZXR1cm4gbnVsbFxuICAgIGNvbnN0IHByZWZpeCA9IHZvaWNlUHJlZml4UmVmLmN1cnJlbnRcbiAgICBjb25zdCBuZWVkc1NwYWNlID1cbiAgICAgIHByZWZpeC5sZW5ndGggPiAwICYmXG4gICAgICAhL1xccyQvLnRlc3QocHJlZml4KSAmJlxuICAgICAgdm9pY2VJbnRlcmltVHJhbnNjcmlwdC5sZW5ndGggPiAwXG4gICAgY29uc3Qgc3RhcnQgPSBwcmVmaXgubGVuZ3RoICsgKG5lZWRzU3BhY2UgPyAxIDogMClcbiAgICBjb25zdCBlbmQgPSBzdGFydCArIHZvaWNlSW50ZXJpbVRyYW5zY3JpcHQubGVuZ3RoXG4gICAgcmV0dXJuIHsgc3RhcnQsIGVuZCB9XG4gIH0sIFt2b2ljZUludGVyaW1UcmFuc2NyaXB0XSlcblxuICByZXR1cm4ge1xuICAgIHN0cmlwVHJhaWxpbmcsXG4gICAgcmVzZXRBbmNob3IsXG4gICAgaGFuZGxlS2V5RXZlbnQ6IHZvaWNlLmhhbmRsZUtleUV2ZW50LFxuICAgIGludGVyaW1SYW5nZSxcbiAgfVxufVxuXG4vKipcbiAqIENvbXBvbmVudCB0aGF0IGhhbmRsZXMgaG9sZC10by10YWxrIHZvaWNlIGFjdGl2YXRpb24uXG4gKlxuICogVGhlIGFjdGl2YXRpb24ga2V5IGlzIGNvbmZpZ3VyYWJsZSB2aWEga2V5YmluZGluZ3MgKHZvaWNlOnB1c2hUb1RhbGssXG4gKiBkZWZhdWx0OiBzcGFjZSkuIEhvbGQgZGV0ZWN0aW9uIGRlcGVuZHMgb24gT1MgYXV0by1yZXBlYXQgZGVsaXZlcmluZyBhXG4gKiBzdHJlYW0gb2YgZXZlbnRzIGF0IDMwLTgwbXMgaW50ZXJ2YWxzLiBUd28gYmluZGluZyB0eXBlcyB3b3JrOlxuICpcbiAqICoqTW9kaWZpZXIgKyBsZXR0ZXIgKG1ldGEraywgY3RybCt4LCBhbHQrdik6KiogQ2xlYW5lc3QuIEFjdGl2YXRlcyBvblxuICogdGhlIGZpcnN0IHByZXNzIOKAlCBhIG1vZGlmaWVyIGNvbWJvIGlzIHVuYW1iaWd1b3VzIGludGVudCAoY2FuJ3QgYmVcbiAqIHR5cGVkIGFjY2lkZW50YWxseSksIHNvIG5vIGhvbGQgdGhyZXNob2xkIGFwcGxpZXMuIFRoZSBsZXR0ZXIgcGFydFxuICogYXV0by1yZXBlYXRzIHdoaWxlIGhlbGQsIGZlZWRpbmcgcmVsZWFzZSBkZXRlY3Rpb24gaW4gdXNlVm9pY2UudHMuXG4gKiBObyBmbG93LXRocm91Z2gsIG5vIHN0cmlwcGluZy5cbiAqXG4gKiAqKkJhcmUgY2hhcnMgKHNwYWNlLCB2LCB4KToqKiBSZXF1aXJlIEhPTERfVEhSRVNIT0xEIHJhcGlkIHByZXNzZXMgdG9cbiAqIGFjdGl2YXRlIChhIHNpbmdsZSBzcGFjZSBjb3VsZCBiZSBub3JtYWwgdHlwaW5nKS4gVGhlIGZpcnN0XG4gKiBXQVJNVVBfVEhSRVNIT0xEIHByZXNzZXMgZmxvdyBpbnRvIHRoZSBpbnB1dCBzbyBhIHNpbmdsZSBwcmVzcyB0eXBlc1xuICogbm9ybWFsbHkuIFBhc3QgdGhhdCwgcmFwaWQgcHJlc3NlcyBhcmUgc3dhbGxvd2VkOyBvbiBhY3RpdmF0aW9uIHRoZVxuICogZmxvdy10aHJvdWdoIGNoYXJzIGFyZSBzdHJpcHBlZC4gQmluZGluZyBcInZcIiBkb2Vzbid0IG1ha2UgXCJ2XCJcbiAqIHVudHlwYWJsZSDigJQgbm9ybWFsIHR5cGluZyAoPjEyMG1zIGJldHdlZW4ga2V5c3Ryb2tlcykgZmxvd3MgdGhyb3VnaDtcbiAqIG9ubHkgcmFwaWQgYXV0by1yZXBlYXQgZnJvbSBhIGhlbGQga2V5IHRyaWdnZXJzIGFjdGl2YXRpb24uXG4gKlxuICogS25vd24gYnJva2VuOiBtb2RpZmllcitzcGFjZSAoTlVMIOKGkiBwYXJzZWQgYXMgY3RybCtiYWNrdGljayksIGNob3Jkc1xuICogKGRpc2NyZXRlIHNlcXVlbmNlcywgbm8gaG9sZCkuIFZhbGlkYXRpb24gd2FybnMgb24gdGhlc2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1c2VWb2ljZUtleWJpbmRpbmdIYW5kbGVyKHtcbiAgdm9pY2VIYW5kbGVLZXlFdmVudCxcbiAgc3RyaXBUcmFpbGluZyxcbiAgcmVzZXRBbmNob3IsXG4gIGlzQWN0aXZlLFxufToge1xuICB2b2ljZUhhbmRsZUtleUV2ZW50OiAoZmFsbGJhY2tNcz86IG51bWJlcikgPT4gdm9pZFxuICBzdHJpcFRyYWlsaW5nOiAobWF4U3RyaXA6IG51bWJlciwgb3B0cz86IFN0cmlwT3B0cykgPT4gbnVtYmVyXG4gIHJlc2V0QW5jaG9yOiAoKSA9PiB2b2lkXG4gIGlzQWN0aXZlOiBib29sZWFuXG59KTogeyBoYW5kbGVLZXlEb3duOiAoZTogS2V5Ym9hcmRFdmVudCkgPT4gdm9pZCB9IHtcbiAgY29uc3QgZ2V0Vm9pY2VTdGF0ZSA9IHVzZUdldFZvaWNlU3RhdGUoKVxuICBjb25zdCBzZXRWb2ljZVN0YXRlID0gdXNlU2V0Vm9pY2VTdGF0ZSgpXG4gIGNvbnN0IGtleWJpbmRpbmdDb250ZXh0ID0gdXNlT3B0aW9uYWxLZXliaW5kaW5nQ29udGV4dCgpXG4gIGNvbnN0IGlzTW9kYWxPdmVybGF5QWN0aXZlID0gdXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUoKVxuICAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gIGNvbnN0IHZvaWNlRW5hYmxlZCA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKSA/IHVzZVZvaWNlRW5hYmxlZCgpIDogZmFsc2VcbiAgY29uc3Qgdm9pY2VTdGF0ZSA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKVxuICAgID8gLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICAgICAgdXNlVm9pY2VTdGF0ZShzID0+IHMudm9pY2VTdGF0ZSlcbiAgICA6ICdpZGxlJ1xuXG4gIC8vIEZpbmQgdGhlIGNvbmZpZ3VyZWQga2V5IGZvciB2b2ljZTpwdXNoVG9UYWxrIGZyb20ga2V5YmluZGluZyBjb250ZXh0LlxuICAvLyBGb3J3YXJkIGl0ZXJhdGlvbiB3aXRoIGxhc3Qtd2lucyAobWF0Y2hpbmcgdGhlIHJlc29sdmVyKTogaWYgYSBsYXRlclxuICAvLyBDaGF0IGJpbmRpbmcgb3ZlcnJpZGVzIHRoZSBzYW1lIGNob3JkIHdpdGggbnVsbCBvciBhIGRpZmZlcmVudFxuICAvLyBhY3Rpb24sIHRoZSB2b2ljZSBiaW5kaW5nIGlzIGRpc2NhcmRlZCBhbmQgbnVsbCBpcyByZXR1cm5lZCDigJQgdGhlXG4gIC8vIHVzZXIgZXhwbGljaXRseSBkaXNhYmxlZCBob2xkLXRvLXRhbGsgdmlhIGJpbmRpbmcgb3ZlcnJpZGUsIHNvXG4gIC8vIGRvbid0IHNlY29uZC1ndWVzcyB0aGVtIHdpdGggYSBmYWxsYmFjay4gVGhlIERFRkFVTFQgaXMgb25seSB1c2VkXG4gIC8vIHdoZW4gdGhlcmUncyBubyBwcm92aWRlciBhdCBhbGwuIENvbnRleHQgZmlsdGVyIGlzIHJlcXVpcmVkIOKAlCBzcGFjZVxuICAvLyBpcyBhbHNvIGJvdW5kIGluIFNldHRpbmdzL0NvbmZpcm1hdGlvbi9QbHVnaW4gKHNlbGVjdDphY2NlcHQgZXRjLik7XG4gIC8vIHdpdGhvdXQgdGhlIGZpbHRlciB0aG9zZSB3b3VsZCBudWxsIG91dCB0aGUgZGVmYXVsdC5cbiAgY29uc3Qgdm9pY2VLZXlzdHJva2UgPSB1c2VNZW1vKCgpOiBQYXJzZWRLZXlzdHJva2UgfCBudWxsID0+IHtcbiAgICBpZiAoIWtleWJpbmRpbmdDb250ZXh0KSByZXR1cm4gREVGQVVMVF9WT0lDRV9LRVlTVFJPS0VcbiAgICBsZXQgcmVzdWx0OiBQYXJzZWRLZXlzdHJva2UgfCBudWxsID0gbnVsbFxuICAgIGZvciAoY29uc3QgYmluZGluZyBvZiBrZXliaW5kaW5nQ29udGV4dC5iaW5kaW5ncykge1xuICAgICAgaWYgKGJpbmRpbmcuY29udGV4dCAhPT0gJ0NoYXQnKSBjb250aW51ZVxuICAgICAgaWYgKGJpbmRpbmcuY2hvcmQubGVuZ3RoICE9PSAxKSBjb250aW51ZVxuICAgICAgY29uc3Qga3MgPSBiaW5kaW5nLmNob3JkWzBdXG4gICAgICBpZiAoIWtzKSBjb250aW51ZVxuICAgICAgaWYgKGJpbmRpbmcuYWN0aW9uID09PSAndm9pY2U6cHVzaFRvVGFsaycpIHtcbiAgICAgICAgcmVzdWx0ID0ga3NcbiAgICAgIH0gZWxzZSBpZiAocmVzdWx0ICE9PSBudWxsICYmIGtleXN0cm9rZXNFcXVhbChrcywgcmVzdWx0KSkge1xuICAgICAgICAvLyBBIGxhdGVyIGJpbmRpbmcgb3ZlcnJpZGVzIHRoaXMgY2hvcmQgKG51bGwgdW5iaW5kIG9yIHJlYXNzaWdubWVudClcbiAgICAgICAgcmVzdWx0ID0gbnVsbFxuICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0XG4gIH0sIFtrZXliaW5kaW5nQ29udGV4dF0pXG5cbiAgLy8gSWYgdGhlIGJpbmRpbmcgaXMgYSBiYXJlICh1bm1vZGlmaWVkKSBzaW5nbGUgcHJpbnRhYmxlIGNoYXIsIHRlcm1pbmFsXG4gIC8vIGF1dG8tcmVwZWF0IG1heSBiYXRjaCBOIGtleXN0cm9rZXMgaW50byBvbmUgaW5wdXQgZXZlbnQgKGUuZy4gXCJ2dnZcIiksXG4gIC8vIGFuZCB0aGUgY2hhciBmbG93cyBpbnRvIHRoZSB0ZXh0IGlucHV0IOKAlCB3ZSBuZWVkIGZsb3ctdGhyb3VnaCArIHN0cmlwLlxuICAvLyBNb2RpZmllciBjb21ib3MgKG1ldGEraywgY3RybCt4KSBhbHNvIGF1dG8tcmVwZWF0ICh0aGUgbGV0dGVyIHBhcnRcbiAgLy8gcmVwZWF0cykgYnV0IGRvbid0IGluc2VydCB0ZXh0LCBzbyB0aGV5J3JlIHN3YWxsb3dlZCBmcm9tIHRoZSBmaXJzdFxuICAvLyBwcmVzcyB3aXRoIG5vIHN0cmlwcGluZyBuZWVkZWQuIG1hdGNoZXNLZXlib2FyZEV2ZW50IGhhbmRsZXMgdGhvc2UuXG4gIGNvbnN0IGJhcmVDaGFyID1cbiAgICB2b2ljZUtleXN0cm9rZSAhPT0gbnVsbCAmJlxuICAgIHZvaWNlS2V5c3Ryb2tlLmtleS5sZW5ndGggPT09IDEgJiZcbiAgICAhdm9pY2VLZXlzdHJva2UuY3RybCAmJlxuICAgICF2b2ljZUtleXN0cm9rZS5hbHQgJiZcbiAgICAhdm9pY2VLZXlzdHJva2Uuc2hpZnQgJiZcbiAgICAhdm9pY2VLZXlzdHJva2UubWV0YSAmJlxuICAgICF2b2ljZUtleXN0cm9rZS5zdXBlclxuICAgICAgPyB2b2ljZUtleXN0cm9rZS5rZXlcbiAgICAgIDogbnVsbFxuXG4gIGNvbnN0IHJhcGlkQ291bnRSZWYgPSB1c2VSZWYoMClcbiAgLy8gSG93IG1hbnkgcmFwaWQgY2hhcnMgd2UgaW50ZW50aW9uYWxseSBsZXQgdGhyb3VnaCB0byB0aGUgdGV4dFxuICAvLyBpbnB1dCAodGhlIGZpcnN0IFdBUk1VUF9USFJFU0hPTEQpLiBUaGUgYWN0aXZhdGlvbiBzdHJpcCByZW1vdmVzXG4gIC8vIHVwIHRvIHRoaXMgbWFueSArIHRoZSBhY3RpdmF0aW9uIGV2ZW50J3MgcG90ZW50aWFsIGxlYWsuIEZvciB0aGVcbiAgLy8gZGVmYXVsdCAoc3BhY2UpIHRoaXMgaXMgcHJlY2lzZSDigJQgcHJlLWV4aXN0aW5nIHRyYWlsaW5nIHNwYWNlcyBhcmVcbiAgLy8gcmFyZS4gRm9yIGxldHRlciBiaW5kaW5ncyAodmFsaWRhdGlvbiB3YXJucykgdGhpcyBtYXkgb3Zlci1zdHJpcFxuICAvLyBvbmUgcHJlLWV4aXN0aW5nIGNoYXIgaWYgdGhlIGlucHV0IGFscmVhZHkgZW5kZWQgaW4gdGhlIGJvdW5kXG4gIC8vIGxldHRlciAoZS5nLiBcImhhdlwiICsgaG9sZCBcInZcIiDihpIgXCJoYVwiKS4gV2UgZG9uJ3QgdHJhY2sgdGhhdFxuICAvLyBib3VuZGFyeSDigJQgaXQncyBiZXN0LWVmZm9ydCBhbmQgdGhlIHdhcm5pbmcgc2F5cyBzby5cbiAgY29uc3QgY2hhcnNJbklucHV0UmVmID0gdXNlUmVmKDApXG4gIC8vIFRyYWlsaW5nLWNoYXIgY291bnQgcmVtYWluaW5nIGFmdGVyIHRoZSBhY3RpdmF0aW9uIHN0cmlwIOKAlCB0aGVzZVxuICAvLyBiZWxvbmcgdG8gdGhlIHVzZXIncyBhbmNob3JlZCBwcmVmaXggYW5kIG11c3QgYmUgcHJlc2VydmVkIGR1cmluZ1xuICAvLyByZWNvcmRpbmcncyBkZWZlbnNpdmUgbGVhayBjbGVhbnVwLlxuICBjb25zdCByZWNvcmRpbmdGbG9vclJlZiA9IHVzZVJlZigwKVxuICAvLyBUcnVlIHdoZW4gdGhlIGN1cnJlbnQgcmVjb3JkaW5nIHdhcyBzdGFydGVkIGJ5IGtleS1ob2xkIChub3QgZm9jdXMpLlxuICAvLyBVc2VkIHRvIGF2b2lkIHN3YWxsb3dpbmcga2V5cHJlc3NlcyBkdXJpbmcgZm9jdXMtbW9kZSByZWNvcmRpbmcuXG4gIGNvbnN0IGlzSG9sZEFjdGl2ZVJlZiA9IHVzZVJlZihmYWxzZSlcbiAgY29uc3QgcmVzZXRUaW1lclJlZiA9IHVzZVJlZjxSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IG51bGw+KG51bGwpXG5cbiAgLy8gUmVzZXQgaG9sZCBzdGF0ZSBhcyBzb29uIGFzIHdlIGxlYXZlICdyZWNvcmRpbmcnLiBUaGUgcGh5c2ljYWwgaG9sZFxuICAvLyBlbmRzIHdoZW4ga2V5LXJlcGVhdCBzdG9wcyAoc3RhdGUg4oaSICdwcm9jZXNzaW5nJyk7IGtlZXBpbmcgdGhlIHJlZlxuICAvLyBzZXQgdGhyb3VnaCAncHJvY2Vzc2luZycgc3dhbGxvd3MgbmV3IHNwYWNlIHByZXNzZXMgdGhlIHVzZXIgdHlwZXNcbiAgLy8gd2hpbGUgdGhlIHRyYW5zY3JpcHQgZmluYWxpemVzLlxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICh2b2ljZVN0YXRlICE9PSAncmVjb3JkaW5nJykge1xuICAgICAgaXNIb2xkQWN0aXZlUmVmLmN1cnJlbnQgPSBmYWxzZVxuICAgICAgcmFwaWRDb3VudFJlZi5jdXJyZW50ID0gMFxuICAgICAgY2hhcnNJbklucHV0UmVmLmN1cnJlbnQgPSAwXG4gICAgICByZWNvcmRpbmdGbG9vclJlZi5jdXJyZW50ID0gMFxuICAgICAgc2V0Vm9pY2VTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgaWYgKCFwcmV2LnZvaWNlV2FybWluZ1VwKSByZXR1cm4gcHJldlxuICAgICAgICByZXR1cm4geyAuLi5wcmV2LCB2b2ljZVdhcm1pbmdVcDogZmFsc2UgfVxuICAgICAgfSlcbiAgICB9XG4gIH0sIFt2b2ljZVN0YXRlLCBzZXRWb2ljZVN0YXRlXSlcblxuICBjb25zdCBoYW5kbGVLZXlEb3duID0gKGU6IEtleWJvYXJkRXZlbnQpOiB2b2lkID0+IHtcbiAgICBpZiAoIXZvaWNlRW5hYmxlZCkgcmV0dXJuXG5cbiAgICAvLyBQcm9tcHRJbnB1dCBpcyBub3QgYSB2YWxpZCB0cmFuc2NyaXB0IHRhcmdldCDigJQgbGV0IHRoZSBob2xkIGtleVxuICAgIC8vIGZsb3cgdGhyb3VnaCBpbnN0ZWFkIG9mIHN3YWxsb3dpbmcgaXQgaW50byBzdGFsZSByZWZzICgjMzM1NTYpLlxuICAgIC8vIFR3byBkaXN0aW5jdCB1bm1vdW50L3VuZm9jdXMgcGF0aHMgKGJvdGggbmVlZGVkKTpcbiAgICAvLyAgIC0gIWlzQWN0aXZlOiBsb2NhbC1qc3ggY29tbWFuZCBoaWQgUHJvbXB0SW5wdXQgKHNob3VsZEhpZGVQcm9tcHRJbnB1dClcbiAgICAvLyAgICAgd2l0aG91dCByZWdpc3RlcmluZyBhbiBvdmVybGF5IOKAlCBlLmcuIC9pbnN0YWxsLWdpdGh1Yi1hcHAsXG4gICAgLy8gICAgIC9wbHVnaW4uIE1pcnJvcnMgQ29tbWFuZEtleWJpbmRpbmdIYW5kbGVycycgaXNBY3RpdmUgZ2F0ZS5cbiAgICAvLyAgIC0gaXNNb2RhbE92ZXJsYXlBY3RpdmU6IG92ZXJsYXkgKHBlcm1pc3Npb24gZGlhbG9nLCBTZWxlY3Qgd2l0aFxuICAgIC8vICAgICBvbkNhbmNlbCkgaGFzIGZvY3VzOyBQcm9tcHRJbnB1dCBpcyBtb3VudGVkIGJ1dCBmb2N1cz1mYWxzZS5cbiAgICBpZiAoIWlzQWN0aXZlIHx8IGlzTW9kYWxPdmVybGF5QWN0aXZlKSByZXR1cm5cblxuICAgIC8vIG51bGwgbWVhbnMgdGhlIHVzZXIgb3ZlcnJvZGUgdGhlIGRlZmF1bHQgKG51bGwtdW5iaW5kL3JlYXNzaWduKSDigJRcbiAgICAvLyBob2xkLXRvLXRhbGsgaXMgZGlzYWJsZWQgdmlhIGJpbmRpbmcuIFRvIHRvZ2dsZSB0aGUgZmVhdHVyZVxuICAgIC8vIGl0c2VsZiwgdXNlIC92b2ljZS5cbiAgICBpZiAodm9pY2VLZXlzdHJva2UgPT09IG51bGwpIHJldHVyblxuXG4gICAgLy8gTWF0Y2ggdGhlIGNvbmZpZ3VyZWQga2V5LiBCYXJlIGNoYXJzIG1hdGNoIGJ5IGNvbnRlbnQgKGhhbmRsZXNcbiAgICAvLyBiYXRjaGVkIGF1dG8tcmVwZWF0IGxpa2UgXCJ2dnZcIikgd2l0aCBhIG1vZGlmaWVyIHJlamVjdCBzbyBlLmcuXG4gICAgLy8gY3RybCt2IGRvZXNuJ3QgdHJpcCBhIFwidlwiIGJpbmRpbmcuIE1vZGlmaWVyIGNvbWJvcyBnbyB0aHJvdWdoXG4gICAgLy8gbWF0Y2hlc0tleWJvYXJkRXZlbnQgKG9uZSBldmVudCBwZXIgcmVwZWF0LCBubyBiYXRjaGluZykuXG4gICAgbGV0IHJlcGVhdENvdW50OiBudW1iZXJcbiAgICBpZiAoYmFyZUNoYXIgIT09IG51bGwpIHtcbiAgICAgIGlmIChlLmN0cmwgfHwgZS5tZXRhIHx8IGUuc2hpZnQpIHJldHVyblxuICAgICAgLy8gV2hlbiBib3VuZCB0byBzcGFjZSwgYWxzbyBhY2NlcHQgVSszMDAwIChmdWxsLXdpZHRoIHNwYWNlKSDigJRcbiAgICAgIC8vIENKSyBJTUVzIGVtaXQgaXQgZm9yIHRoZSBzYW1lIHBoeXNpY2FsIGtleS5cbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWQgPVxuICAgICAgICBiYXJlQ2hhciA9PT0gJyAnID8gbm9ybWFsaXplRnVsbFdpZHRoU3BhY2UoZS5rZXkpIDogZS5rZXlcbiAgICAgIC8vIEZhc3QtcGF0aDogbm9ybWFsIHR5cGluZyAoYW55IGNoYXIgdGhhdCBpc24ndCB0aGUgYm91bmQgb25lKVxuICAgICAgLy8gYmFpbHMgaGVyZSB3aXRob3V0IGFsbG9jYXRpbmcuIFRoZSByZXBlYXQoKSBjaGVjayBvbmx5IG1hdHRlcnNcbiAgICAgIC8vIGZvciBiYXRjaGVkIGF1dG8tcmVwZWF0IChpbnB1dC5sZW5ndGggPiAxKSB3aGljaCBpcyByYXJlLlxuICAgICAgaWYgKG5vcm1hbGl6ZWRbMF0gIT09IGJhcmVDaGFyKSByZXR1cm5cbiAgICAgIGlmIChcbiAgICAgICAgbm9ybWFsaXplZC5sZW5ndGggPiAxICYmXG4gICAgICAgIG5vcm1hbGl6ZWQgIT09IGJhcmVDaGFyLnJlcGVhdChub3JtYWxpemVkLmxlbmd0aClcbiAgICAgIClcbiAgICAgICAgcmV0dXJuXG4gICAgICByZXBlYXRDb3VudCA9IG5vcm1hbGl6ZWQubGVuZ3RoXG4gICAgfSBlbHNlIHtcbiAgICAgIGlmICghbWF0Y2hlc0tleWJvYXJkRXZlbnQoZSwgdm9pY2VLZXlzdHJva2UpKSByZXR1cm5cbiAgICAgIHJlcGVhdENvdW50ID0gMVxuICAgIH1cblxuICAgIC8vIEd1YXJkOiBvbmx5IHN3YWxsb3cga2V5cHJlc3NlcyB3aGVuIHJlY29yZGluZyB3YXMgdHJpZ2dlcmVkIGJ5XG4gICAgLy8ga2V5LWhvbGQuIEZvY3VzLW1vZGUgcmVjb3JkaW5nIGFsc28gc2V0cyB2b2ljZVN0YXRlIHRvICdyZWNvcmRpbmcnLFxuICAgIC8vIGJ1dCBrZXlwcmVzc2VzIHNob3VsZCBmbG93IHRocm91Z2ggbm9ybWFsbHkgKHZvaWNlSGFuZGxlS2V5RXZlbnRcbiAgICAvLyByZXR1cm5zIGVhcmx5IGZvciBmb2N1cy10cmlnZ2VyZWQgc2Vzc2lvbnMpLiBXZSBhbHNvIGNoZWNrIHZvaWNlU3RhdGVcbiAgICAvLyBmcm9tIHRoZSBzdG9yZSBzbyB0aGF0IGlmIHZvaWNlSGFuZGxlS2V5RXZlbnQoKSBmYWlscyB0byB0cmFuc2l0aW9uXG4gICAgLy8gc3RhdGUgKG1vZHVsZSBub3QgbG9hZGVkLCBzdHJlYW0gdW5hdmFpbGFibGUpIHdlIGRvbid0IHBlcm1hbmVudGx5XG4gICAgLy8gc3dhbGxvdyBrZXlwcmVzc2VzLlxuICAgIGNvbnN0IGN1cnJlbnRWb2ljZVN0YXRlID0gZ2V0Vm9pY2VTdGF0ZSgpLnZvaWNlU3RhdGVcbiAgICBpZiAoaXNIb2xkQWN0aXZlUmVmLmN1cnJlbnQgJiYgY3VycmVudFZvaWNlU3RhdGUgIT09ICdpZGxlJykge1xuICAgICAgLy8gQWxyZWFkeSByZWNvcmRpbmcg4oCUIHN3YWxsb3cgY29udGludWVkIGtleXByZXNzZXMgYW5kIGZvcndhcmRcbiAgICAgIC8vIHRvIHZvaWNlIGZvciByZWxlYXNlIGRldGVjdGlvbi4gRm9yIGJhcmUgY2hhcnMsIGRlZmVuc2l2ZWx5XG4gICAgICAvLyBzdHJpcCBpbiBjYXNlIHRoZSB0ZXh0IGlucHV0IGhhbmRsZXIgZmlyZWQgYmVmb3JlIHRoaXMgb25lXG4gICAgICAvLyAobGlzdGVuZXIgb3JkZXIgaXMgbm90IGd1YXJhbnRlZWQpLiBNb2RpZmllciBjb21ib3MgZG9uJ3RcbiAgICAgIC8vIGluc2VydCB0ZXh0LCBzbyBub3RoaW5nIHRvIHN0cmlwLlxuICAgICAgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKVxuICAgICAgaWYgKGJhcmVDaGFyICE9PSBudWxsKSB7XG4gICAgICAgIHN0cmlwVHJhaWxpbmcocmVwZWF0Q291bnQsIHtcbiAgICAgICAgICBjaGFyOiBiYXJlQ2hhcixcbiAgICAgICAgICBmbG9vcjogcmVjb3JkaW5nRmxvb3JSZWYuY3VycmVudCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIHZvaWNlSGFuZGxlS2V5RXZlbnQoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gTm9uLWhvbGQgcmVjb3JkaW5nIChmb2N1cy1tb2RlKSBvciBwcm9jZXNzaW5nIGlzIGFjdGl2ZS5cbiAgICAvLyBNb2RpZmllciBjb21ib3MgbXVzdCBub3QgcmUtYWN0aXZhdGU6IHN0cmlwVHJhaWxpbmcoMCx7YW5jaG9yOnRydWV9KVxuICAgIC8vIHdvdWxkIG92ZXJ3cml0ZSB2b2ljZVByZWZpeFJlZiB3aXRoIGludGVyaW0gdGV4dCBhbmQgZHVwbGljYXRlIHRoZVxuICAgIC8vIHRyYW5zY3JpcHQgb24gdGhlIG5leHQgaW50ZXJpbSB1cGRhdGUuIFByZS0jMjIxNDQsIGEgc2luZ2xlIHRhcFxuICAgIC8vIGhpdCB0aGUgd2FybXVwIGVsc2UtYnJhbmNoIChzd2FsbG93IG9ubHkpLiBCYXJlIGNoYXJzIGZsb3cgdGhyb3VnaFxuICAgIC8vIHVuY29uZGl0aW9uYWxseSDigJQgdXNlciBtYXkgYmUgdHlwaW5nIGR1cmluZyBmb2N1cy1yZWNvcmRpbmcuXG4gICAgaWYgKGN1cnJlbnRWb2ljZVN0YXRlICE9PSAnaWRsZScpIHtcbiAgICAgIGlmIChiYXJlQ2hhciA9PT0gbnVsbCkgZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3QgY291bnRCZWZvcmUgPSByYXBpZENvdW50UmVmLmN1cnJlbnRcbiAgICByYXBpZENvdW50UmVmLmN1cnJlbnQgKz0gcmVwZWF0Q291bnRcblxuICAgIC8vIOKUgOKUgCBBY3RpdmF0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgFxuICAgIC8vIEhhbmRsZWQgZmlyc3Qgc28gdGhlIHdhcm11cCBicmFuY2ggYmVsb3cgZG9lcyBOT1QgYWxzbyBydW5cbiAgICAvLyBvbiB0aGlzIGV2ZW50IOKAlCB0d28gc3RyaXAgY2FsbHMgaW4gdGhlIHNhbWUgdGljayB3b3VsZCBib3RoXG4gICAgLy8gcmVhZCB0aGUgc3RhbGUgaW5wdXRWYWx1ZVJlZiBhbmQgdGhlIHNlY29uZCB3b3VsZCB1bmRlci1zdHJpcC5cbiAgICAvLyBNb2RpZmllciBjb21ib3MgYWN0aXZhdGUgb24gdGhlIGZpcnN0IHByZXNzIOKAlCB0aGV5IGNhbid0IGJlXG4gICAgLy8gdHlwZWQgYWNjaWRlbnRhbGx5LCBzbyB0aGUgaG9sZCB0aHJlc2hvbGQgKHdoaWNoIGV4aXN0cyB0b1xuICAgIC8vIGRpc3Rpbmd1aXNoIHR5cGluZyBhIHNwYWNlIGZyb20gaG9sZGluZyBzcGFjZSkgZG9lc24ndCBhcHBseS5cbiAgICBpZiAoYmFyZUNoYXIgPT09IG51bGwgfHwgcmFwaWRDb3VudFJlZi5jdXJyZW50ID49IEhPTERfVEhSRVNIT0xEKSB7XG4gICAgICBlLnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpXG4gICAgICBpZiAocmVzZXRUaW1lclJlZi5jdXJyZW50KSB7XG4gICAgICAgIGNsZWFyVGltZW91dChyZXNldFRpbWVyUmVmLmN1cnJlbnQpXG4gICAgICAgIHJlc2V0VGltZXJSZWYuY3VycmVudCA9IG51bGxcbiAgICAgIH1cbiAgICAgIHJhcGlkQ291bnRSZWYuY3VycmVudCA9IDBcbiAgICAgIGlzSG9sZEFjdGl2ZVJlZi5jdXJyZW50ID0gdHJ1ZVxuICAgICAgc2V0Vm9pY2VTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgaWYgKCFwcmV2LnZvaWNlV2FybWluZ1VwKSByZXR1cm4gcHJldlxuICAgICAgICByZXR1cm4geyAuLi5wcmV2LCB2b2ljZVdhcm1pbmdVcDogZmFsc2UgfVxuICAgICAgfSlcbiAgICAgIGlmIChiYXJlQ2hhciAhPT0gbnVsbCkge1xuICAgICAgICAvLyBTdHJpcCB0aGUgaW50ZW50aW9uYWwgd2FybXVwIGNoYXJzIHBsdXMgdGhpcyBldmVudCdzIGxlYWtcbiAgICAgICAgLy8gKGlmIHRleHQgaW5wdXQgZmlyZWQgZmlyc3QpLiBDYXAgY292ZXJzIGJvdGg7IG1pbih0cmFpbGluZylcbiAgICAgICAgLy8gaGFuZGxlcyB0aGUgbm8tbGVhayBjYXNlLiBBbmNob3IgdGhlIHZvaWNlIHByZWZpeCBoZXJlLlxuICAgICAgICAvLyBUaGUgcmV0dXJuIHZhbHVlIChyZW1haW5pbmcpIGJlY29tZXMgdGhlIGZsb29yIGZvclxuICAgICAgICAvLyByZWNvcmRpbmctdGltZSBsZWFrIGNsZWFudXAuXG4gICAgICAgIHJlY29yZGluZ0Zsb29yUmVmLmN1cnJlbnQgPSBzdHJpcFRyYWlsaW5nKFxuICAgICAgICAgIGNoYXJzSW5JbnB1dFJlZi5jdXJyZW50ICsgcmVwZWF0Q291bnQsXG4gICAgICAgICAgeyBjaGFyOiBiYXJlQ2hhciwgYW5jaG9yOiB0cnVlIH0sXG4gICAgICAgIClcbiAgICAgICAgY2hhcnNJbklucHV0UmVmLmN1cnJlbnQgPSAwXG4gICAgICAgIHZvaWNlSGFuZGxlS2V5RXZlbnQoKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gTW9kaWZpZXIgY29tYm86IG5vdGhpbmcgaW5zZXJ0ZWQsIG5vdGhpbmcgdG8gc3RyaXAuIEp1c3RcbiAgICAgICAgLy8gYW5jaG9yIHRoZSB2b2ljZSBwcmVmaXggYXQgdGhlIGN1cnJlbnQgY3Vyc29yIHBvc2l0aW9uLlxuICAgICAgICAvLyBMb25nZXIgZmFsbGJhY2s6IHRoaXMgY2FsbCBpcyBhdCB0PTAgKGJlZm9yZSBhdXRvLXJlcGVhdCksXG4gICAgICAgIC8vIHNvIHRoZSBnYXAgdG8gdGhlIG5leHQga2V5cHJlc3MgaXMgdGhlIE9TIGluaXRpYWwgcmVwZWF0XG4gICAgICAgIC8vICpkZWxheSogKHVwIHRvIH4ycyksIG5vdCB0aGUgcmVwZWF0ICpyYXRlKiAofjMwLTgwbXMpLlxuICAgICAgICBzdHJpcFRyYWlsaW5nKDAsIHsgYW5jaG9yOiB0cnVlIH0pXG4gICAgICAgIHZvaWNlSGFuZGxlS2V5RXZlbnQoTU9ESUZJRVJfRklSU1RfUFJFU1NfRkFMTEJBQ0tfTVMpXG4gICAgICB9XG4gICAgICAvLyBJZiB2b2ljZSBmYWlsZWQgdG8gdHJhbnNpdGlvbiAobW9kdWxlIG5vdCBsb2FkZWQsIHN0cmVhbVxuICAgICAgLy8gdW5hdmFpbGFibGUsIHN0YWxlIGVuYWJsZWQpLCBjbGVhciB0aGUgcmVmIHNvIGEgbGF0ZXJcbiAgICAgIC8vIGZvY3VzLW1vZGUgcmVjb3JkaW5nIGRvZXNuJ3QgaW5oZXJpdCBzdGFsZSBob2xkIHN0YXRlXG4gICAgICAvLyBhbmQgc3dhbGxvdyBrZXlwcmVzc2VzLiBTdG9yZSBpcyBzeW5jaHJvbm91cyDigJQgdGhlIGNoZWNrIGlzXG4gICAgICAvLyBpbW1lZGlhdGUuIFRoZSBhbmNob3Igc2V0IGJ5IHN0cmlwVHJhaWxpbmcgYWJvdmUgd2lsbFxuICAgICAgLy8gYmUgb3ZlcndyaXR0ZW4gb24gcmV0cnkgKGFuY2hvciBhbHdheXMgb3ZlcndyaXRlcyBub3cpLlxuICAgICAgaWYgKGdldFZvaWNlU3RhdGUoKS52b2ljZVN0YXRlID09PSAnaWRsZScpIHtcbiAgICAgICAgaXNIb2xkQWN0aXZlUmVmLmN1cnJlbnQgPSBmYWxzZVxuICAgICAgICByZXNldEFuY2hvcigpXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyDilIDilIAgV2FybXVwIChiYXJlLWNoYXIgb25seTsgbW9kaWZpZXIgY29tYm9zIGFjdGl2YXRlZCBhYm92ZSkg4pSA4pSAXG4gICAgLy8gRmlyc3QgV0FSTVVQX1RIUkVTSE9MRCBjaGFycyBmbG93IHRvIHRoZSB0ZXh0IGlucHV0IHNvIG5vcm1hbFxuICAgIC8vIHR5cGluZyBoYXMgemVybyBsYXRlbmN5IChhIHNpbmdsZSBwcmVzcyB0eXBlcyBub3JtYWxseSkuXG4gICAgLy8gU3Vic2VxdWVudCByYXBpZCBjaGFycyBhcmUgc3dhbGxvd2VkIHNvIHRoZSBpbnB1dCBzdGF5cyBhbGlnbmVkXG4gICAgLy8gd2l0aCB0aGUgd2FybXVwIFVJLiBTdHJpcCBkZWZlbnNpdmVseSAobGlzdGVuZXIgb3JkZXIgaXMgbm90XG4gICAgLy8gZ3VhcmFudGVlZCDigJQgdGV4dCBpbnB1dCBtYXkgaGF2ZSBhbHJlYWR5IGFkZGVkIHRoZSBjaGFyKS4gVGhlXG4gICAgLy8gZmxvb3IgcHJlc2VydmVzIHRoZSBpbnRlbnRpb25hbCB3YXJtdXAgY2hhcnM7IHRoZSBzdHJpcCBpcyBhXG4gICAgLy8gbm8tb3Agd2hlbiBub3RoaW5nIGxlYWtlZC4gQ2hlY2sgY291bnRCZWZvcmUgc28gdGhlIGV2ZW50IHRoYXRcbiAgICAvLyBjcm9zc2VzIHRoZSB0aHJlc2hvbGQgc3RpbGwgZmxvd3MgdGhyb3VnaCAodGVybWluYWwgYmF0Y2hpbmcpLlxuICAgIGlmIChjb3VudEJlZm9yZSA+PSBXQVJNVVBfVEhSRVNIT0xEKSB7XG4gICAgICBlLnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpXG4gICAgICBzdHJpcFRyYWlsaW5nKHJlcGVhdENvdW50LCB7XG4gICAgICAgIGNoYXI6IGJhcmVDaGFyLFxuICAgICAgICBmbG9vcjogY2hhcnNJbklucHV0UmVmLmN1cnJlbnQsXG4gICAgICB9KVxuICAgIH0gZWxzZSB7XG4gICAgICBjaGFyc0luSW5wdXRSZWYuY3VycmVudCArPSByZXBlYXRDb3VudFxuICAgIH1cblxuICAgIC8vIFNob3cgd2FybXVwIGZlZWRiYWNrIG9uY2Ugd2UgZGV0ZWN0IGEgaG9sZCBwYXR0ZXJuXG4gICAgaWYgKHJhcGlkQ291bnRSZWYuY3VycmVudCA+PSBXQVJNVVBfVEhSRVNIT0xEKSB7XG4gICAgICBzZXRWb2ljZVN0YXRlKHByZXYgPT4ge1xuICAgICAgICBpZiAocHJldi52b2ljZVdhcm1pbmdVcCkgcmV0dXJuIHByZXZcbiAgICAgICAgcmV0dXJuIHsgLi4ucHJldiwgdm9pY2VXYXJtaW5nVXA6IHRydWUgfVxuICAgICAgfSlcbiAgICB9XG5cbiAgICBpZiAocmVzZXRUaW1lclJlZi5jdXJyZW50KSB7XG4gICAgICBjbGVhclRpbWVvdXQocmVzZXRUaW1lclJlZi5jdXJyZW50KVxuICAgIH1cbiAgICByZXNldFRpbWVyUmVmLmN1cnJlbnQgPSBzZXRUaW1lb3V0KFxuICAgICAgKHJlc2V0VGltZXJSZWYsIHJhcGlkQ291bnRSZWYsIGNoYXJzSW5JbnB1dFJlZiwgc2V0Vm9pY2VTdGF0ZSkgPT4ge1xuICAgICAgICByZXNldFRpbWVyUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICAgIHJhcGlkQ291bnRSZWYuY3VycmVudCA9IDBcbiAgICAgICAgY2hhcnNJbklucHV0UmVmLmN1cnJlbnQgPSAwXG4gICAgICAgIHNldFZvaWNlU3RhdGUocHJldiA9PiB7XG4gICAgICAgICAgaWYgKCFwcmV2LnZvaWNlV2FybWluZ1VwKSByZXR1cm4gcHJldlxuICAgICAgICAgIHJldHVybiB7IC4uLnByZXYsIHZvaWNlV2FybWluZ1VwOiBmYWxzZSB9XG4gICAgICAgIH0pXG4gICAgICB9LFxuICAgICAgUkFQSURfS0VZX0dBUF9NUyxcbiAgICAgIHJlc2V0VGltZXJSZWYsXG4gICAgICByYXBpZENvdW50UmVmLFxuICAgICAgY2hhcnNJbklucHV0UmVmLFxuICAgICAgc2V0Vm9pY2VTdGF0ZSxcbiAgICApXG4gIH1cblxuICAvLyBCYWNrd2FyZC1jb21wYXQgYnJpZGdlOiBSRVBMLnRzeCBkb2Vzbid0IHlldCB3aXJlIGhhbmRsZUtleURvd24gdG9cbiAgLy8gPEJveCBvbktleURvd24+LiBTdWJzY3JpYmUgdmlhIHVzZUlucHV0IGFuZCBhZGFwdCBJbnB1dEV2ZW50IOKGklxuICAvLyBLZXlib2FyZEV2ZW50IHVudGlsIHRoZSBjb25zdW1lciBpcyBtaWdyYXRlZCAoc2VwYXJhdGUgUFIpLlxuICAvLyBUT0RPKG9uS2V5RG93bi1taWdyYXRpb24pOiByZW1vdmUgb25jZSBSRVBMIHBhc3NlcyBoYW5kbGVLZXlEb3duLlxuICB1c2VJbnB1dChcbiAgICAoX2lucHV0LCBfa2V5LCBldmVudCkgPT4ge1xuICAgICAgY29uc3Qga2JFdmVudCA9IG5ldyBLZXlib2FyZEV2ZW50KGV2ZW50LmtleXByZXNzKVxuICAgICAgaGFuZGxlS2V5RG93bihrYkV2ZW50KVxuICAgICAgLy8gaGFuZGxlS2V5RG93biBzdG9wcGVkIHRoZSBhZGFwdGVyIGV2ZW50LCBub3QgdGhlIElucHV0RXZlbnQgdGhlXG4gICAgICAvLyBlbWl0dGVyIGFjdHVhbGx5IGNoZWNrcyDigJQgZm9yd2FyZCBpdCBzbyB0aGUgdGV4dCBpbnB1dCdzIHVzZUlucHV0XG4gICAgICAvLyBsaXN0ZW5lciBpcyBza2lwcGVkIGFuZCBoZWxkIHNwYWNlcyBkb24ndCBsZWFrIGludG8gdGhlIHByb21wdC5cbiAgICAgIGlmIChrYkV2ZW50LmRpZFN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpKSB7XG4gICAgICAgIGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpXG4gICAgICB9XG4gICAgfSxcbiAgICB7IGlzQWN0aXZlIH0sXG4gIClcblxuICByZXR1cm4geyBoYW5kbGVLZXlEb3duIH1cbn1cblxuLy8gVE9ETyhvbktleURvd24tbWlncmF0aW9uKTogdGVtcG9yYXJ5IHNoaW0gc28gZXhpc3RpbmcgSlNYIGNhbGxlcnNcbi8vICg8Vm9pY2VLZXliaW5kaW5nSGFuZGxlciAuLi4vPikga2VlcCBjb21waWxpbmcuIFJlbW92ZSBvbmNlIFJFUEwudHN4XG4vLyB3aXJlcyBoYW5kbGVLZXlEb3duIGRpcmVjdGx5LlxuZXhwb3J0IGZ1bmN0aW9uIFZvaWNlS2V5YmluZGluZ0hhbmRsZXIocHJvcHM6IHtcbiAgdm9pY2VIYW5kbGVLZXlFdmVudDogKGZhbGxiYWNrTXM/OiBudW1iZXIpID0+IHZvaWRcbiAgc3RyaXBUcmFpbGluZzogKG1heFN0cmlwOiBudW1iZXIsIG9wdHM/OiBTdHJpcE9wdHMpID0+IG51bWJlclxuICByZXNldEFuY2hvcjogKCkgPT4gdm9pZFxuICBpc0FjdGl2ZTogYm9vbGVhblxufSk6IG51bGwge1xuICB1c2VWb2ljZUtleWJpbmRpbmdIYW5kbGVyKHByb3BzKVxuICByZXR1cm4gbnVsbFxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxPQUFPLFFBQVEsWUFBWTtBQUNwQyxPQUFPLEtBQUtDLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLE1BQU0sUUFBUSxPQUFPO0FBQy9ELFNBQVNDLGdCQUFnQixRQUFRLDZCQUE2QjtBQUM5RCxTQUFTQyx1QkFBdUIsUUFBUSw4QkFBOEI7QUFDdEUsU0FDRUMsZ0JBQWdCLEVBQ2hCQyxnQkFBZ0IsRUFDaEJDLGFBQWEsUUFDUixxQkFBcUI7QUFDNUIsU0FBU0MsYUFBYSxRQUFRLGlDQUFpQztBQUMvRDtBQUNBLFNBQVNDLFFB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 \ No newline at end of file diff --git a/ink.ts b/ink.ts new file mode 100644 index 0000000..a06b343 --- /dev/null +++ b/ink.ts @@ -0,0 +1,85 @@ +import { createElement, type ReactNode } from 'react' +import { ThemeProvider } from './components/design-system/ThemeProvider.js' +import inkRender, { + type Instance, + createRoot as inkCreateRoot, + type RenderOptions, + type Root, +} from './ink/root.js' + +export type { RenderOptions, Instance, Root } + +// Wrap all CC render calls with ThemeProvider so ThemedBox/ThemedText work +// without every call site having to mount it. Ink itself is theme-agnostic. +function withTheme(node: ReactNode): ReactNode { + return createElement(ThemeProvider, null, node) +} + +export async function render( + node: ReactNode, + options?: NodeJS.WriteStream | RenderOptions, +): Promise { + return inkRender(withTheme(node), options) +} + +export async function createRoot(options?: RenderOptions): Promise { + const root = await inkCreateRoot(options) + return { + ...root, + render: node => root.render(withTheme(node)), + } +} + +export { color } from './components/design-system/color.js' +export type { Props as BoxProps } from './components/design-system/ThemedBox.js' +export { default as Box } from './components/design-system/ThemedBox.js' +export type { Props as TextProps } from './components/design-system/ThemedText.js' +export { default as Text } from './components/design-system/ThemedText.js' +export { + ThemeProvider, + usePreviewTheme, + useTheme, + useThemeSetting, +} from './components/design-system/ThemeProvider.js' +export { Ansi } from './ink/Ansi.js' +export type { Props as AppProps } from './ink/components/AppContext.js' +export type { Props as BaseBoxProps } from './ink/components/Box.js' +export { default as BaseBox } from './ink/components/Box.js' +export type { + ButtonState, + Props as ButtonProps, +} from './ink/components/Button.js' +export { default as Button } from './ink/components/Button.js' +export type { Props as LinkProps } from './ink/components/Link.js' +export { default as Link } from './ink/components/Link.js' +export type { Props as NewlineProps } from './ink/components/Newline.js' +export { default as Newline } from './ink/components/Newline.js' +export { NoSelect } from './ink/components/NoSelect.js' +export { RawAnsi } from './ink/components/RawAnsi.js' +export { default as Spacer } from './ink/components/Spacer.js' +export type { Props as StdinProps } from './ink/components/StdinContext.js' +export type { Props as BaseTextProps } from './ink/components/Text.js' +export { default as BaseText } from './ink/components/Text.js' +export type { DOMElement } from './ink/dom.js' +export { ClickEvent } from './ink/events/click-event.js' +export { EventEmitter } from './ink/events/emitter.js' +export { Event } from './ink/events/event.js' +export type { Key } from './ink/events/input-event.js' +export { InputEvent } from './ink/events/input-event.js' +export type { TerminalFocusEventType } from './ink/events/terminal-focus-event.js' +export { TerminalFocusEvent } from './ink/events/terminal-focus-event.js' +export { FocusManager } from './ink/focus.js' +export type { FlickerReason } from './ink/frame.js' +export { useAnimationFrame } from './ink/hooks/use-animation-frame.js' +export { default as useApp } from './ink/hooks/use-app.js' +export { default as useInput } from './ink/hooks/use-input.js' +export { useAnimationTimer, useInterval } from './ink/hooks/use-interval.js' +export { useSelection } from './ink/hooks/use-selection.js' +export { default as useStdin } from './ink/hooks/use-stdin.js' +export { useTabStatus } from './ink/hooks/use-tab-status.js' +export { useTerminalFocus } from './ink/hooks/use-terminal-focus.js' +export { useTerminalTitle } from './ink/hooks/use-terminal-title.js' +export { useTerminalViewport } from './ink/hooks/use-terminal-viewport.js' +export { default as measureElement } from './ink/measure-element.js' +export { supportsTabStatus } from './ink/termio/osc.js' +export { default as wrapText } from './ink/wrap-text.js' diff --git a/ink/Ansi.tsx b/ink/Ansi.tsx new file mode 100644 index 0000000..aef5f60 --- /dev/null +++ b/ink/Ansi.tsx @@ -0,0 +1,292 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import Link from './components/Link.js'; +import Text from './components/Text.js'; +import type { Color } from './styles.js'; +import { type NamedColor, Parser, type Color as TermioColor, type TextStyle } from './termio.js'; +type Props = { + children: string; + /** When true, force all text to be rendered with dim styling */ + dimColor?: boolean; +}; +type SpanProps = { + color?: Color; + backgroundColor?: Color; + dim?: boolean; + bold?: boolean; + italic?: boolean; + underline?: boolean; + strikethrough?: boolean; + inverse?: boolean; + hyperlink?: string; +}; + +/** + * Component that parses ANSI escape codes and renders them using Text components. + * + * Use this as an escape hatch when you have pre-formatted ANSI strings from + * external tools (like cli-highlight) that need to be rendered in Ink. + * + * Memoized to prevent re-renders when parent changes but children string is the same. + */ +export const Ansi = React.memo(function Ansi(t0) { + const $ = _c(12); + const { + children, + dimColor + } = t0; + if (typeof children !== "string") { + let t1; + if ($[0] !== children || $[1] !== dimColor) { + t1 = dimColor ? {String(children)} : {String(children)}; + $[0] = children; + $[1] = dimColor; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; + } + if (children === "") { + return null; + } + let t1; + let t2; + if ($[3] !== children || $[4] !== dimColor) { + t2 = Symbol.for("react.early_return_sentinel"); + bb0: { + const spans = parseToSpans(children); + if (spans.length === 0) { + t2 = null; + break bb0; + } + if (spans.length === 1 && !hasAnyProps(spans[0].props)) { + t2 = dimColor ? {spans[0].text} : {spans[0].text}; + break bb0; + } + let t3; + if ($[7] !== dimColor) { + t3 = (span, i) => { + const hyperlink = span.props.hyperlink; + if (dimColor) { + span.props.dim = true; + } + const hasTextProps = hasAnyTextProps(span.props); + if (hyperlink) { + return hasTextProps ? {span.text} : {span.text}; + } + return hasTextProps ? {span.text} : span.text; + }; + $[7] = dimColor; + $[8] = t3; + } else { + t3 = $[8]; + } + t1 = spans.map(t3); + } + $[3] = children; + $[4] = dimColor; + $[5] = t1; + $[6] = t2; + } else { + t1 = $[5]; + t2 = $[6]; + } + if (t2 !== Symbol.for("react.early_return_sentinel")) { + return t2; + } + const content = t1; + let t3; + if ($[9] !== content || $[10] !== dimColor) { + t3 = dimColor ? {content} : {content}; + $[9] = content; + $[10] = dimColor; + $[11] = t3; + } else { + t3 = $[11]; + } + return t3; +}); +type Span = { + text: string; + props: SpanProps; +}; + +/** + * Parse an ANSI string into spans using the termio parser. + */ +function parseToSpans(input: string): Span[] { + const parser = new Parser(); + const actions = parser.feed(input); + const spans: Span[] = []; + let currentHyperlink: string | undefined; + for (const action of actions) { + if (action.type === 'link') { + if (action.action.type === 'start') { + currentHyperlink = action.action.url; + } else { + currentHyperlink = undefined; + } + continue; + } + if (action.type === 'text') { + const text = action.graphemes.map(g => g.value).join(''); + if (!text) continue; + const props = textStyleToSpanProps(action.style); + if (currentHyperlink) { + props.hyperlink = currentHyperlink; + } + + // Try to merge with previous span if props match + const lastSpan = spans[spans.length - 1]; + if (lastSpan && propsEqual(lastSpan.props, props)) { + lastSpan.text += text; + } else { + spans.push({ + text, + props + }); + } + } + } + return spans; +} + +/** + * Convert termio's TextStyle to SpanProps. + */ +function textStyleToSpanProps(style: TextStyle): SpanProps { + const props: SpanProps = {}; + if (style.bold) props.bold = true; + if (style.dim) props.dim = true; + if (style.italic) props.italic = true; + if (style.underline !== 'none') props.underline = true; + if (style.strikethrough) props.strikethrough = true; + if (style.inverse) props.inverse = true; + const fgColor = colorToString(style.fg); + if (fgColor) props.color = fgColor; + const bgColor = colorToString(style.bg); + if (bgColor) props.backgroundColor = bgColor; + return props; +} + +// Map termio named colors to the ansi: format +const NAMED_COLOR_MAP: Record = { + black: 'ansi:black', + red: 'ansi:red', + green: 'ansi:green', + yellow: 'ansi:yellow', + blue: 'ansi:blue', + magenta: 'ansi:magenta', + cyan: 'ansi:cyan', + white: 'ansi:white', + brightBlack: 'ansi:blackBright', + brightRed: 'ansi:redBright', + brightGreen: 'ansi:greenBright', + brightYellow: 'ansi:yellowBright', + brightBlue: 'ansi:blueBright', + brightMagenta: 'ansi:magentaBright', + brightCyan: 'ansi:cyanBright', + brightWhite: 'ansi:whiteBright' +}; + +/** + * Convert termio's Color to the string format used by Ink. + */ +function colorToString(color: TermioColor): Color | undefined { + switch (color.type) { + case 'named': + return NAMED_COLOR_MAP[color.name] as Color; + case 'indexed': + return `ansi256(${color.index})` as Color; + case 'rgb': + return `rgb(${color.r},${color.g},${color.b})` as Color; + case 'default': + return undefined; + } +} + +/** + * Check if two SpanProps are equal for merging. + */ +function propsEqual(a: SpanProps, b: SpanProps): boolean { + return a.color === b.color && a.backgroundColor === b.backgroundColor && a.bold === b.bold && a.dim === b.dim && a.italic === b.italic && a.underline === b.underline && a.strikethrough === b.strikethrough && a.inverse === b.inverse && a.hyperlink === b.hyperlink; +} +function hasAnyProps(props: SpanProps): boolean { + return props.color !== undefined || props.backgroundColor !== undefined || props.dim === true || props.bold === true || props.italic === true || props.underline === true || props.strikethrough === true || props.inverse === true || props.hyperlink !== undefined; +} +function hasAnyTextProps(props: SpanProps): boolean { + return props.color !== undefined || props.backgroundColor !== undefined || props.dim === true || props.bold === true || props.italic === true || props.underline === true || props.strikethrough === true || props.inverse === true; +} + +// Text style props without weight (bold/dim) - these are handled separately +type BaseTextStyleProps = { + color?: Color; + backgroundColor?: Color; + italic?: boolean; + underline?: boolean; + strikethrough?: boolean; + inverse?: boolean; +}; + +// Wrapper component that handles bold/dim mutual exclusivity for Text +function StyledText(t0) { + const $ = _c(14); + let bold; + let children; + let dim; + let rest; + if ($[0] !== t0) { + ({ + bold, + dim, + children, + ...rest + } = t0); + $[0] = t0; + $[1] = bold; + $[2] = children; + $[3] = dim; + $[4] = rest; + } else { + bold = $[1]; + children = $[2]; + dim = $[3]; + rest = $[4]; + } + if (dim) { + let t1; + if ($[5] !== children || $[6] !== rest) { + t1 = {children}; + $[5] = children; + $[6] = rest; + $[7] = t1; + } else { + t1 = $[7]; + } + return t1; + } + if (bold) { + let t1; + if ($[8] !== children || $[9] !== rest) { + t1 = {children}; + $[8] = children; + $[9] = rest; + $[10] = t1; + } else { + t1 = $[10]; + } + return t1; + } + let t1; + if ($[11] !== children || $[12] !== rest) { + t1 = {children}; + $[11] = children; + $[12] = rest; + $[13] = t1; + } else { + t1 = $[13]; + } + return t1; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkxpbmsiLCJUZXh0IiwiQ29sb3IiLCJOYW1lZENvbG9yIiwiUGFyc2VyIiwiVGVybWlvQ29sb3IiLCJUZXh0U3R5bGUiLCJQcm9wcyIsImNoaWxkcmVuIiwiZGltQ29sb3IiLCJTcGFuUHJvcHMiLCJjb2xvciIsImJhY2tncm91bmRDb2xvciIsImRpbSIsImJvbGQiLCJpdGFsaWMiLCJ1bmRlcmxpbmUiLCJzdHJpa2V0aHJvdWdoIiwiaW52ZXJzZSIsImh5cGVybGluayIsIkFuc2kiLCJtZW1vIiwidDAiLCIkIiwiX2MiLCJ0MSIsIlN0cmluZyIsInQyIiwiU3ltYm9sIiwiZm9yIiwiYmIwIiwic3BhbnMiLCJwYXJzZVRvU3BhbnMiLCJsZW5ndGgiLCJoYXNBbnlQcm9wcyIsInByb3BzIiwidGV4dCIsInQzIiwic3BhbiIsImkiLCJoYXNUZXh0UHJvcHMiLCJoYXNBbnlUZXh0UHJvcHMiLCJtYXAiLCJjb250ZW50IiwiU3BhbiIsImlucHV0IiwicGFyc2VyIiwiYWN0aW9ucyIsImZlZWQiLCJjdXJyZW50SHlwZXJsaW5rIiwiYWN0aW9uIiwidHlwZSIsInVybCIsInVuZGVmaW5lZCIsImdyYXBoZW1lcyIsImciLCJ2YWx1ZSIsImpvaW4iLCJ0ZXh0U3R5bGVUb1NwYW5Qcm9wcyIsInN0eWxlIiwibGFzdFNwYW4iLCJwcm9wc0VxdWFsIiwicHVzaCIsImZnQ29sb3IiLCJjb2xvclRvU3RyaW5nIiwiZmciLCJiZ0NvbG9yIiwiYmciLCJOQU1FRF9DT0xPUl9NQVAiLCJSZWNvcmQiLCJibGFjayIsInJlZCIsImdyZWVuIiwieWVsbG93IiwiYmx1ZSIsIm1hZ2VudGEiLCJjeWFuIiwid2hpdGUiLCJicmlnaHRCbGFjayIsImJyaWdodFJlZCIsImJyaWdodEdyZWVuIiwiYnJpZ2h0WWVsbG93IiwiYnJpZ2h0Qmx1ZSIsImJyaWdodE1hZ2VudGEiLCJicmlnaHRDeWFuIiwiYnJpZ2h0V2hpdGUiLCJuYW1lIiwiaW5kZXgiLCJyIiwiYiIsImEiLCJCYXNlVGV4dFN0eWxlUHJvcHMiLCJTdHlsZWRUZXh0IiwicmVzdCJdLCJzb3VyY2VzIjpbIkFuc2kudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCBMaW5rIGZyb20gJy4vY29tcG9uZW50cy9MaW5rLmpzJ1xuaW1wb3J0IFRleHQgZnJvbSAnLi9jb21wb25lbnRzL1RleHQuanMnXG5pbXBvcnQgdHlwZSB7IENvbG9yIH0gZnJvbSAnLi9zdHlsZXMuanMnXG5pbXBvcnQge1xuICB0eXBlIE5hbWVkQ29sb3IsXG4gIFBhcnNlcixcbiAgdHlwZSBDb2xvciBhcyBUZXJtaW9Db2xvcixcbiAgdHlwZSBUZXh0U3R5bGUsXG59IGZyb20gJy4vdGVybWlvLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICBjaGlsZHJlbjogc3RyaW5nXG4gIC8qKiBXaGVuIHRydWUsIGZvcmNlIGFsbCB0ZXh0IHRvIGJlIHJlbmRlcmVkIHdpdGggZGltIHN0eWxpbmcgKi9cbiAgZGltQ29sb3I/OiBib29sZWFuXG59XG5cbnR5cGUgU3BhblByb3BzID0ge1xuICBjb2xvcj86IENvbG9yXG4gIGJhY2tncm91bmRDb2xvcj86IENvbG9yXG4gIGRpbT86IGJvb2xlYW5cbiAgYm9sZD86IGJvb2xlYW5cbiAgaXRhbGljPzogYm9vbGVhblxuICB1bmRlcmxpbmU/OiBib29sZWFuXG4gIHN0cmlrZXRocm91Z2g/OiBib29sZWFuXG4gIGludmVyc2U/OiBib29sZWFuXG4gIGh5cGVybGluaz86IHN0cmluZ1xufVxuXG4vKipcbiAqIENvbXBvbmVudCB0aGF0IHBhcnNlcyBBTlNJIGVzY2FwZSBjb2RlcyBhbmQgcmVuZGVycyB0aGVtIHVzaW5nIFRleHQgY29tcG9uZW50cy5cbiAqXG4gKiBVc2UgdGhpcyBhcyBhbiBlc2NhcGUgaGF0Y2ggd2hlbiB5b3UgaGF2ZSBwcmUtZm9ybWF0dGVkIEFOU0kgc3RyaW5ncyBmcm9tXG4gKiBleHRlcm5hbCB0b29scyAobGlrZSBjbGktaGlnaGxpZ2h0KSB0aGF0IG5lZWQgdG8gYmUgcmVuZGVyZWQgaW4gSW5rLlxuICpcbiAqIE1lbW9pemVkIHRvIHByZXZlbnQgcmUtcmVuZGVycyB3aGVuIHBhcmVudCBjaGFuZ2VzIGJ1dCBjaGlsZHJlbiBzdHJpbmcgaXMgdGhlIHNhbWUuXG4gKi9cbmV4cG9ydCBjb25zdCBBbnNpID0gUmVhY3QubWVtbyhmdW5jdGlvbiBBbnNpKHtcbiAgY2hpbGRyZW4sXG4gIGRpbUNvbG9yLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAodHlwZW9mIGNoaWxkcmVuICE9PSAnc3RyaW5nJykge1xuICAgIHJldHVybiBkaW1Db2xvciA/IChcbiAgICAgIDxUZXh0IGRpbT57U3RyaW5nKGNoaWxkcmVuKX08L1RleHQ+XG4gICAgKSA6IChcbiAgICAgIDxUZXh0PntTdHJpbmcoY2hpbGRyZW4pfTwvVGV4dD5cbiAgICApXG4gIH1cblxuICBpZiAoY2hpbGRyZW4gPT09ICcnKSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIGNvbnN0IHNwYW5zID0gcGFyc2VUb1NwYW5zKGNoaWxkcmVuKVxuXG4gIGlmIChzcGFucy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKHNwYW5zLmxlbmd0aCA9PT0gMSAmJiAhaGFzQW55UHJvcHMoc3BhbnNbMF0hLnByb3BzKSkge1xuICAgIHJldHVybiBkaW1Db2xvciA/IChcbiAgICAgIDxUZXh0IGRpbT57c3BhbnNbMF0hLnRleHR9PC9UZXh0PlxuICAgICkgOiAoXG4gICAgICA8VGV4dD57c3BhbnNbMF0hLnRleHR9PC9UZXh0PlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IGNvbnRlbnQgPSBzcGFucy5tYXAoKHNwYW4sIGkpID0+IHtcbiAgICBjb25zdCBoeXBlcmxpbmsgPSBzcGFuLnByb3BzLmh5cGVybGlua1xuICAgIC8vIFdoZW4gZGltQ29sb3IgaXMgZm9yY2VkLCBvdmVycmlkZSB0aGUgc3BhbidzIGRpbSBwcm9wXG4gICAgaWYgKGRpbUNvbG9yKSB7XG4gICAgICBzcGFuLnByb3BzLmRpbSA9IHRydWVcbiAgICB9XG4gICAgY29uc3QgaGFzVGV4dFByb3BzID0gaGFzQW55VGV4dFByb3BzKHNwYW4ucHJvcHMpXG5cbiAgICBpZiAoaHlwZXJsaW5rKSB7XG4gICAgICByZXR1cm4gaGFzVGV4dFByb3BzID8gKFxuICAgICAgICA8TGluayBrZXk9e2l9IHVybD17aHlwZXJsaW5rfT5cbiAgICAgICAgICA8U3R5bGVkVGV4dFxuICAgICAgICAgICAgY29sb3I9e3NwYW4ucHJvcHMuY29sb3J9XG4gICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3I9e3NwYW4ucHJvcHMuYmFja2dyb3VuZENvbG9yfVxuICAgICAgICAgICAgZGltPXtzcGFuLnByb3BzLmRpbX1cbiAgICAgICAgICAgIGJvbGQ9e3NwYW4ucHJvcHMuYm9sZH1cbiAgICAgICAgICAgIGl0YWxpYz17c3Bhbi5wcm9wcy5pdGFsaWN9XG4gICAgICAgICAgICB1bmRlcmxpbmU9e3NwYW4ucHJvcHMudW5kZXJsaW5lfVxuICAgICAgICAgICAgc3RyaWtldGhyb3VnaD17c3Bhbi5wcm9wcy5zdHJpa2V0aHJvdWdofVxuICAgICAgICAgICAgaW52ZXJzZT17c3Bhbi5wcm9wcy5pbnZlcnNlfVxuICAgICAgICAgID5cbiAgICAgICAgICAgIHtzcGFuLnRleHR9XG4gICAgICAgICAgPC9TdHlsZWRUZXh0PlxuICAgICAgICA8L0xpbms+XG4gICAgICApIDogKFxuICAgICAgICA8TGluayBrZXk9e2l9IHVybD17aHlwZXJsaW5rfT5cbiAgICAgICAgICB7c3Bhbi50ZXh0fVxuICAgICAgICA8L0xpbms+XG4gICAgICApXG4gICAgfVxuXG4gICAgcmV0dXJuIGhhc1RleHRQcm9wcyA/IChcbiAgICAgIDxTdHlsZWRUZXh0XG4gICAgICAgIGtleT17aX1cbiAgICAgICAgY29sb3I9e3NwYW4ucHJvcHMuY29sb3J9XG4gICAgICAgIGJhY2tncm91bmRDb2xvcj17c3Bhbi5wcm9wcy5iYWNrZ3JvdW5kQ29sb3J9XG4gICAgICAgIGRpbT17c3Bhbi5wcm9wcy5kaW19XG4gICAgICAgIGJvbGQ9e3NwYW4ucHJvcHMuYm9sZH1cbiAgICAgICAgaXRhbGljPXtzcGFuLnByb3BzLml0YWxpY31cbiAgICAgICAgdW5kZXJsaW5lPXtzcGFuLnByb3BzLnVuZGVybGluZX1cbiAgICAgICAgc3RyaWtldGhyb3VnaD17c3Bhbi5wcm9wcy5zdHJpa2V0aHJvdWdofVxuICAgICAgICBpbnZlcnNlPXtzcGFuLnByb3BzLmludmVyc2V9XG4gICAgICA+XG4gICAgICAgIHtzcGFuLnRleHR9XG4gICAgICA8L1N0eWxlZFRleHQ+XG4gICAgKSA6IChcbiAgICAgIHNwYW4udGV4dFxuICAgIClcbiAgfSlcblxuICByZXR1cm4gZGltQ29sb3IgPyA8VGV4dCBkaW0+e2NvbnRlbnR9PC9UZXh0PiA6IDxUZXh0Pntjb250ZW50fTwvVGV4dD5cbn0pXG5cbnR5cGUgU3BhbiA9IHtcbiAgdGV4dDogc3RyaW5nXG4gIHByb3BzOiBTcGFuUHJvcHNcbn1cblxuLyoqXG4gKiBQYXJzZSBhbiBBTlNJIHN0cmluZyBpbnRvIHNwYW5zIHVzaW5nIHRoZSB0ZXJtaW8gcGFyc2VyLlxuICovXG5mdW5jdGlvbiBwYXJzZVRvU3BhbnMoaW5wdXQ6IHN0cmluZyk6IFNwYW5bXSB7XG4gIGNvbnN0IHBhcnNlciA9IG5ldyBQYXJzZXIoKVxuICBjb25zdCBhY3Rpb25zID0gcGFyc2VyLmZlZWQoaW5wdXQpXG4gIGNvbnN0IHNwYW5zOiBTcGFuW10gPSBbXVxuXG4gIGxldCBjdXJyZW50SHlwZXJsaW5rOiBzdHJpbmcgfCB1bmRlZmluZWRcblxuICBmb3IgKGNvbnN0IGFjdGlvbiBvZiBhY3Rpb25zKSB7XG4gICAgaWYgKGFjdGlvbi50eXBlID09PSAnbGluaycpIHtcbiAgICAgIGlmIChhY3Rpb24uYWN0aW9uLnR5cGUgPT09ICdzdGFydCcpIHtcbiAgICAgICAgY3VycmVudEh5cGVybGluayA9IGFjdGlvbi5hY3Rpb24udXJsXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjdXJyZW50SHlwZXJsaW5rID0gdW5kZWZpbmVkXG4gICAgICB9XG4gICAgICBjb250aW51ZVxuICAgIH1cblxuICAgIGlmIChhY3Rpb24udHlwZSA9PT0gJ3RleHQnKSB7XG4gICAgICBjb25zdCB0ZXh0ID0gYWN0aW9uLmdyYXBoZW1lcy5tYXAoZyA9PiBnLnZhbHVlKS5qb2luKCcnKVxuICAgICAgaWYgKCF0ZXh0KSBjb250aW51ZVxuXG4gICAgICBjb25zdCBwcm9wcyA9IHRleHRTdHlsZVRvU3BhblByb3BzKGFjdGlvbi5zdHlsZSlcbiAgICAgIGlmIChjdXJyZW50SHlwZXJsaW5rKSB7XG4gICAgICAgIHByb3BzLmh5cGVybGluayA9IGN1cnJlbnRIeXBlcmxpbmtcbiAgICAgIH1cblxuICAgICAgLy8gVHJ5IHRvIG1lcmdlIHdpdGggcHJldmlvdXMgc3BhbiBpZiBwcm9wcyBtYXRjaFxuICAgICAgY29uc3QgbGFzdFNwYW4gPSBzcGFuc1tzcGFucy5sZW5ndGggLSAxXVxuICAgICAgaWYgKGxhc3RTcGFuICYmIHByb3BzRXF1YWwobGFzdFNwYW4ucHJvcHMsIHByb3BzKSkge1xuICAgICAgICBsYXN0U3Bhbi50ZXh0ICs9IHRleHRcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNwYW5zLnB1c2goeyB0ZXh0LCBwcm9wcyB9KVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBzcGFuc1xufVxuXG4vKipcbiAqIENvbnZlcnQgdGVybWlvJ3MgVGV4dFN0eWxlIHRvIFNwYW5Qcm9wcy5cbiAqL1xuZnVuY3Rpb24gdGV4dFN0eWxlVG9TcGFuUHJvcHMoc3R5bGU6IFRleHRTdHlsZSk6IFNwYW5Qcm9wcyB7XG4gIGNvbnN0IHByb3BzOiBTcGFuUHJvcHMgPSB7fVxuXG4gIGlmIChzdHlsZS5ib2xkKSBwcm9wcy5ib2xkID0gdHJ1ZVxuICBpZiAoc3R5bGUuZGltKSBwcm9wcy5kaW0gPSB0cnVlXG4gIGlmIChzdHlsZS5pdGFsaWMpIHByb3BzLml0YWxpYyA9IHRydWVcbiAgaWYgKHN0eWxlLnVuZGVybGluZSAhPT0gJ25vbmUnKSBwcm9wcy51bmRlcmxpbmUgPSB0cnVlXG4gIGlmIChzdHlsZS5zdHJpa2V0aHJvdWdoKSBwcm9wcy5zdHJpa2V0aHJvdWdoID0gdHJ1ZVxuICBpZiAoc3R5bGUuaW52ZXJzZSkgcHJvcHMuaW52ZXJzZSA9IHRydWVcblxuICBjb25zdCBmZ0NvbG9yID0gY29sb3JUb1N0cmluZyhzdHlsZS5mZylcbiAgaWYgKGZnQ29sb3IpIHByb3BzLmNvbG9yID0gZmdDb2xvclxuXG4gIGNvbnN0IGJnQ29sb3IgPSBjb2xvclRvU3RyaW5nKHN0eWxlLmJnKVxuICBpZiAoYmdDb2xvcikgcHJvcHMuYmFja2dyb3VuZENvbG9yID0gYmdDb2xvclxuXG4gIHJldHVybiBwcm9wc1xufVxuXG4vLyBNYXAgdGVybWlvIG5hbWVkIGNvbG9ycyB0byB0aGUgYW5zaTogZm9ybWF0XG5jb25zdCBOQU1FRF9DT0xPUl9NQVA6IFJlY29yZDxOYW1lZENvbG9yLCBzdHJpbmc+ID0ge1xuICBibGFjazogJ2Fuc2k6YmxhY2snLFxuICByZWQ6ICdhbnNpOnJlZCcsXG4gIGdyZWVuOiAnYW5zaTpncmVlbicsXG4gIHllbGxvdzogJ2Fuc2k6eWVsbG93JyxcbiAgYmx1ZTogJ2Fuc2k6Ymx1ZScsXG4gIG1hZ2VudGE6ICdhbnNpOm1hZ2VudGEnLFxuICBjeWFuOiAnYW5zaTpjeWFuJyxcbiAgd2hpdGU6ICdhbnNpOndoaXRlJyxcbiAgYnJpZ2h0QmxhY2s6ICdhbnNpOmJsYWNrQnJpZ2h0JyxcbiAgYnJpZ2h0UmVkOiAnYW5zaTpyZWRCcmlnaHQnLFxuICBicmlnaHRHcmVlbjogJ2Fuc2k6Z3JlZW5CcmlnaHQnLFxuICBicmlnaHRZZWxsb3c6ICdhbnNpOnllbGxvd0JyaWdodCcsXG4gIGJyaWdodEJsdWU6ICdhbnNpOmJsdWVCcmlnaHQnLFxuICBicmlnaHRNYWdlbnRhOiAnYW5zaTptYWdlbnRhQnJpZ2h0JyxcbiAgYnJpZ2h0Q3lhbjogJ2Fuc2k6Y3lhbkJyaWdodCcsXG4gIGJyaWdodFdoaXRlOiAnYW5zaTp3aGl0ZUJyaWdodCcsXG59XG5cbi8qKlxuICogQ29udmVydCB0ZXJtaW8ncyBDb2xvciB0byB0aGUgc3RyaW5nIGZvcm1hdCB1c2VkIGJ5IEluay5cbiAqL1xuZnVuY3Rpb24gY29sb3JUb1N0cmluZyhjb2xvcjogVGVybWlvQ29sb3IpOiBDb2xvciB8IHVuZGVmaW5lZCB7XG4gIHN3aXRjaCAoY29sb3IudHlwZSkge1xuICAgIGNhc2UgJ25hbWVkJzpcbiAgICAgIHJldHVybiBOQU1FRF9DT0xPUl9NQVBbY29sb3IubmFtZV0gYXMgQ29sb3JcbiAgICBjYXNlICdpbmRleGVkJzpcbiAgICAgIHJldHVybiBgYW5zaTI1Nigke2NvbG9yLmluZGV4fSlgIGFzIENvbG9yXG4gICAgY2FzZSAncmdiJzpcbiAgICAgIHJldHVybiBgcmdiKCR7Y29sb3Iucn0sJHtjb2xvci5nfSwke2NvbG9yLmJ9KWAgYXMgQ29sb3JcbiAgICBjYXNlICdkZWZhdWx0JzpcbiAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgfVxufVxuXG4vKipcbiAqIENoZWNrIGlmIHR3byBTcGFuUHJvcHMgYXJlIGVxdWFsIGZvciBtZXJnaW5nLlxuICovXG5mdW5jdGlvbiBwcm9wc0VxdWFsKGE6IFNwYW5Qcm9wcywgYjogU3BhblByb3BzKTogYm9vbGVhbiB7XG4gIHJldHVybiAoXG4gICAgYS5jb2xvciA9PT0gYi5jb2xvciAmJlxuICAgIGEuYmFja2dyb3VuZENvbG9yID09PSBiLmJhY2tncm91bmRDb2xvciAmJlxuICAgIGEuYm9sZCA9PT0gYi5ib2xkICYmXG4gICAgYS5kaW0gPT09IGIuZGltICYmXG4gICAgYS5pdGFsaWMgPT09IGIuaXRhbGljICYmXG4gICAgYS51bmRlcmxpbmUgPT09IGIudW5kZXJsaW5lICYmXG4gICAgYS5zdHJpa2V0aHJvdWdoID09PSBiLnN0cmlrZXRocm91Z2ggJiZcbiAgICBhLmludmVyc2UgPT09IGIuaW52ZXJzZSAmJlxuICAgIGEuaHlwZXJsaW5rID09PSBiLmh5cGVybGlua1xuICApXG59XG5cbmZ1bmN0aW9uIGhhc0FueVByb3BzKHByb3BzOiBTcGFuUHJvcHMpOiBib29sZWFuIHtcbiAgcmV0dXJuIChcbiAgICBwcm9wcy5jb2xvciAhPT0gdW5kZWZpbmVkIHx8XG4gICAgcHJvcHMuYmFja2dyb3VuZENvbG9yICE9PSB1bmRlZmluZWQgfHxcbiAgICBwcm9wcy5kaW0gPT09IHRydWUgfHxcbiAgICBwcm9wcy5ib2xkID09PSB0cnVlIHx8XG4gICAgcHJvcHMuaXRhbGljID09PSB0cnVlIHx8XG4gICAgcHJvcHMudW5kZXJsaW5lID09PSB0cnVlIHx8XG4gICAgcHJvcHMuc3RyaWtldGhyb3VnaCA9PT0gdHJ1ZSB8fFxuICAgIHByb3BzLmludmVyc2UgPT09IHRydWUgfHxcbiAgICBwcm9wcy5oeXBlcmxpbmsgIT09IHVuZGVmaW5lZFxuICApXG59XG5cbmZ1bmN0aW9uIGhhc0FueVRleHRQcm9wcyhwcm9wczogU3BhblByb3BzKTogYm9vbGVhbiB7XG4gIHJldHVybiAoXG4gICAgcHJvcHMuY29sb3IgIT09IHVuZGVmaW5lZCB8fFxuICAgIHByb3BzLmJhY2tncm91bmRDb2xvciAhPT0gdW5kZWZpbmVkIHx8XG4gICAgcHJvcHMuZGltID09PSB0cnVlIHx8XG4gICAgcHJvcHMuYm9sZCA9PT0gdHJ1ZSB8fFxuICAgIHByb3BzLml0YWxpYyA9PT0gdHJ1ZSB8fFxuICAgIHByb3BzLnVuZGVybGluZSA9PT0gdHJ1ZSB8fFxuICAgIHByb3BzLnN0cmlrZXRocm91Z2ggPT09IHRydWUgfHxcbiAgICBwcm9wcy5pbnZlcnNlID09PSB0cnVlXG4gIClcbn1cblxuLy8gVGV4dCBzdHlsZSBwcm9wcyB3aXRob3V0IHdlaWdodCAoYm9sZC9kaW0pIC0gdGhlc2UgYXJlIGhhbmRsZWQgc2VwYXJhdGVseVxudHlwZSBCYXNlVGV4dFN0eWxlUHJvcHMgPSB7XG4gIGNvbG9yPzogQ29sb3JcbiAgYmFja2dyb3VuZENvbG9yPzogQ29sb3JcbiAgaXRhbGljPzogYm9vbGVhblxuICB1bmRlcmxpbmU/OiBib29sZWFuXG4gIHN0cmlrZXRocm91Z2g/OiBib29sZWFuXG4gIGludmVyc2U/OiBib29sZWFuXG59XG5cbi8vIFdyYXBwZXIgY29tcG9uZW50IHRoYXQgaGFuZGxlcyBib2xkL2RpbSBtdXR1YWwgZXhjbHVzaXZpdHkgZm9yIFRleHRcbmZ1bmN0aW9uIFN0eWxlZFRleHQoe1xuICBib2xkLFxuICBkaW0sXG4gIGNoaWxkcmVuLFxuICAuLi5yZXN0XG59OiBCYXNlVGV4dFN0eWxlUHJvcHMgJiB7XG4gIGJvbGQ/OiBib29sZWFuXG4gIGRpbT86IGJvb2xlYW5cbiAgY2hpbGRyZW46IHN0cmluZ1xufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIC8vIGRpbSB0YWtlcyBwcmVjZWRlbmNlIG92ZXIgYm9sZCB3aGVuIGJvdGggYXJlIHNldCAodGVybWluYWxzIHRyZWF0IHRoZW0gYXMgbXV0dWFsbHkgZXhjbHVzaXZlKVxuICBpZiAoZGltKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxUZXh0IHsuLi5yZXN0fSBkaW0+XG4gICAgICAgIHtjaGlsZHJlbn1cbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH1cbiAgaWYgKGJvbGQpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPFRleHQgey4uLnJlc3R9IGJvbGQ+XG4gICAgICAgIHtjaGlsZHJlbn1cbiAgICAgIDwvVGV4dD5cbiAgICApXG4gIH1cbiAgcmV0dXJuIDxUZXh0IHsuLi5yZXN0fT57Y2hpbGRyZW59PC9UZXh0PlxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxNQUFNLE9BQU87QUFDekIsT0FBT0MsSUFBSSxNQUFNLHNCQUFzQjtBQUN2QyxPQUFPQyxJQUFJLE1BQU0sc0JBQXNCO0FBQ3ZDLGNBQWNDLEtBQUssUUFBUSxhQUFhO0FBQ3hDLFNBQ0UsS0FBS0MsVUFBVSxFQUNmQyxNQUFNLEVBQ04sS0FBS0YsS0FBSyxJQUFJRyxXQUFXLEVBQ3pCLEtBQUtDLFNBQVMsUUFDVCxhQUFhO0FBRXBC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 \ No newline at end of file diff --git a/ink/bidi.ts b/ink/bidi.ts new file mode 100644 index 0000000..bed474d --- /dev/null +++ b/ink/bidi.ts @@ -0,0 +1,139 @@ +/** + * Bidirectional text reordering for terminal rendering. + * + * Terminals on Windows do not implement the Unicode Bidi Algorithm, + * so RTL text (Hebrew, Arabic, etc.) appears reversed. This module + * applies the bidi algorithm to reorder ClusteredChar arrays from + * logical order to visual order before Ink's LTR cell placement loop. + * + * On macOS terminals (Terminal.app, iTerm2) bidi works natively. + * Windows Terminal (including WSL) does not implement bidi + * (https://github.com/microsoft/terminal/issues/538). + * + * Detection: Windows Terminal sets WT_SESSION; native Windows cmd/conhost + * also lacks bidi. We enable bidi reordering when running on Windows or + * inside Windows Terminal (covers WSL). + */ +import bidiFactory from 'bidi-js' + +type ClusteredChar = { + value: string + width: number + styleId: number + hyperlink: string | undefined +} + +let bidiInstance: ReturnType | undefined +let needsSoftwareBidi: boolean | undefined + +function needsBidi(): boolean { + if (needsSoftwareBidi === undefined) { + needsSoftwareBidi = + process.platform === 'win32' || + typeof process.env['WT_SESSION'] === 'string' || // WSL in Windows Terminal + process.env['TERM_PROGRAM'] === 'vscode' // VS Code integrated terminal (xterm.js) + } + return needsSoftwareBidi +} + +function getBidi() { + if (!bidiInstance) { + bidiInstance = bidiFactory() + } + return bidiInstance +} + +/** + * Reorder an array of ClusteredChars from logical order to visual order + * using the Unicode Bidi Algorithm. Active on terminals that lack native + * bidi support (Windows Terminal, conhost, WSL). + * + * Returns the same array on bidi-capable terminals (no-op). + */ +export function reorderBidi(characters: ClusteredChar[]): ClusteredChar[] { + if (!needsBidi() || characters.length === 0) { + return characters + } + + // Build a plain string from the clustered chars to run through bidi + const plainText = characters.map(c => c.value).join('') + + // Check if there are any RTL characters — skip bidi if pure LTR + if (!hasRTLCharacters(plainText)) { + return characters + } + + const bidi = getBidi() + const { levels } = bidi.getEmbeddingLevels(plainText, 'auto') + + // Map bidi levels back to ClusteredChar indices. + // Each ClusteredChar may be multiple code units in the joined string. + const charLevels: number[] = [] + let offset = 0 + for (let i = 0; i < characters.length; i++) { + charLevels.push(levels[offset]!) + offset += characters[i]!.value.length + } + + // Get reorder segments from bidi-js, but we need to work at the + // ClusteredChar level, not the string level. We'll implement the + // standard bidi reordering: find the max level, then for each level + // from max down to 1, reverse all contiguous runs >= that level. + const reordered = [...characters] + const maxLevel = Math.max(...charLevels) + + for (let level = maxLevel; level >= 1; level--) { + let i = 0 + while (i < reordered.length) { + if (charLevels[i]! >= level) { + // Find the end of this run + let j = i + 1 + while (j < reordered.length && charLevels[j]! >= level) { + j++ + } + // Reverse the run in both arrays + reverseRange(reordered, i, j - 1) + reverseRangeNumbers(charLevels, i, j - 1) + i = j + } else { + i++ + } + } + } + + return reordered +} + +function reverseRange(arr: T[], start: number, end: number): void { + while (start < end) { + const temp = arr[start]! + arr[start] = arr[end]! + arr[end] = temp + start++ + end-- + } +} + +function reverseRangeNumbers(arr: number[], start: number, end: number): void { + while (start < end) { + const temp = arr[start]! + arr[start] = arr[end]! + arr[end] = temp + start++ + end-- + } +} + +/** + * Quick check for RTL characters (Hebrew, Arabic, and related scripts). + * Avoids running the full bidi algorithm on pure-LTR text. + */ +function hasRTLCharacters(text: string): boolean { + // Hebrew: U+0590-U+05FF, U+FB1D-U+FB4F + // Arabic: U+0600-U+06FF, U+0750-U+077F, U+08A0-U+08FF, U+FB50-U+FDFF, U+FE70-U+FEFF + // Thaana: U+0780-U+07BF + // Syriac: U+0700-U+074F + return /[\u0590-\u05FF\uFB1D-\uFB4F\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF\u0780-\u07BF\u0700-\u074F]/u.test( + text, + ) +} diff --git a/ink/clearTerminal.ts b/ink/clearTerminal.ts new file mode 100644 index 0000000..38d4a68 --- /dev/null +++ b/ink/clearTerminal.ts @@ -0,0 +1,74 @@ +/** + * Cross-platform terminal clearing with scrollback support. + * Detects modern terminals that support ESC[3J for clearing scrollback. + */ + +import { + CURSOR_HOME, + csi, + ERASE_SCREEN, + ERASE_SCROLLBACK, +} from './termio/csi.js' + +// HVP (Horizontal Vertical Position) - legacy Windows cursor home +const CURSOR_HOME_WINDOWS = csi(0, 'f') + +function isWindowsTerminal(): boolean { + return process.platform === 'win32' && !!process.env.WT_SESSION +} + +function isMintty(): boolean { + // mintty 3.1.5+ sets TERM_PROGRAM to 'mintty' + if (process.env.TERM_PROGRAM === 'mintty') { + return true + } + // GitBash/MSYS2/MINGW use mintty and set MSYSTEM + if (process.platform === 'win32' && process.env.MSYSTEM) { + return true + } + return false +} + +function isModernWindowsTerminal(): boolean { + // Windows Terminal sets WT_SESSION environment variable + if (isWindowsTerminal()) { + return true + } + + // VS Code integrated terminal on Windows with ConPTY support + if ( + process.platform === 'win32' && + process.env.TERM_PROGRAM === 'vscode' && + process.env.TERM_PROGRAM_VERSION + ) { + return true + } + + // mintty (GitBash/MSYS2/Cygwin) supports modern escape sequences + if (isMintty()) { + return true + } + + return false +} + +/** + * Returns the ANSI escape sequence to clear the terminal including scrollback. + * Automatically detects terminal capabilities. + */ +export function getClearTerminalSequence(): string { + if (process.platform === 'win32') { + if (isModernWindowsTerminal()) { + return ERASE_SCREEN + ERASE_SCROLLBACK + CURSOR_HOME + } else { + // Legacy Windows console - can't clear scrollback + return ERASE_SCREEN + CURSOR_HOME_WINDOWS + } + } + return ERASE_SCREEN + ERASE_SCROLLBACK + CURSOR_HOME +} + +/** + * Clears the terminal screen. On supported terminals, also clears scrollback. + */ +export const clearTerminal = getClearTerminalSequence() diff --git a/ink/colorize.ts b/ink/colorize.ts new file mode 100644 index 0000000..9117a84 --- /dev/null +++ b/ink/colorize.ts @@ -0,0 +1,231 @@ +import chalk from 'chalk' +import type { Color, TextStyles } from './styles.js' + +/** + * xterm.js (VS Code, Cursor, code-server, Coder) has supported truecolor + * since 2017, but code-server/Coder containers often don't set + * COLORTERM=truecolor. chalk's supports-color doesn't recognize + * TERM_PROGRAM=vscode (it only knows iTerm.app/Apple_Terminal), so it falls + * through to the -256color regex → level 2. At level 2, chalk.rgb() + * downgrades to the nearest 6×6×6 cube color: rgb(215,119,87) (Claude + * orange) → idx 174 rgb(215,135,135) — washed-out salmon. + * + * Gated on level === 2 (not < 3) to respect NO_COLOR / FORCE_COLOR=0 — + * those yield level 0 and are an explicit "no colors" request. Desktop VS + * Code sets COLORTERM=truecolor itself, so this is a no-op there (already 3). + * + * Must run BEFORE the tmux clamp — if tmux is running inside a VS Code + * terminal, tmux's passthrough limitation wins and we want level 2. + */ +function boostChalkLevelForXtermJs(): boolean { + if (process.env.TERM_PROGRAM === 'vscode' && chalk.level === 2) { + chalk.level = 3 + return true + } + return false +} + +/** + * tmux parses truecolor SGR (\e[48;2;r;g;bm) into its cell buffer correctly, + * but its client-side emitter only re-emits truecolor to the outer terminal if + * the outer terminal advertises Tc/RGB capability (via terminal-overrides). + * Default tmux config doesn't set this, so tmux emits the cell to iTerm2/etc + * WITHOUT the bg sequence — outer terminal's buffer has bg=default → black on + * dark profiles. Clamping to level 2 makes chalk emit 256-color (\e[48;5;Nm), + * which tmux passes through cleanly. grey93 (255) is visually identical to + * rgb(240,240,240). + * + * Users who HAVE set `terminal-overrides ,*:Tc` get a technically-unnecessary + * downgrade, but the visual difference is imperceptible. Querying + * `tmux show -gv terminal-overrides` to detect this would add a subprocess on + * startup — not worth it. + * + * $TMUX is a pty-lifecycle env var set by tmux itself; it never comes from + * globalSettings.env, so reading it here is correct. chalk is a singleton, so + * this clamps ALL truecolor output (fg+bg+hex) across the entire app. + */ +function clampChalkLevelForTmux(): boolean { + // bg.ts sets terminal-overrides :Tc before attach, so truecolor passes + // through — skip the clamp. General escape hatch for anyone who's + // configured their tmux correctly. + if (process.env.CLAUDE_CODE_TMUX_TRUECOLOR) return false + if (process.env.TMUX && chalk.level > 2) { + chalk.level = 2 + return true + } + return false +} +// Computed once at module load — terminal/tmux environment doesn't change mid-session. +// Order matters: boost first so the tmux clamp can re-clamp if tmux is running +// inside a VS Code terminal. Exported for debugging — tree-shaken if unused. +export const CHALK_BOOSTED_FOR_XTERMJS = boostChalkLevelForXtermJs() +export const CHALK_CLAMPED_FOR_TMUX = clampChalkLevelForTmux() + +export type ColorType = 'foreground' | 'background' + +const RGB_REGEX = /^rgb\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/ +const ANSI_REGEX = /^ansi256\(\s?(\d+)\s?\)$/ + +export const colorize = ( + str: string, + color: string | undefined, + type: ColorType, +): string => { + if (!color) { + return str + } + + if (color.startsWith('ansi:')) { + const value = color.substring('ansi:'.length) + switch (value) { + case 'black': + return type === 'foreground' ? chalk.black(str) : chalk.bgBlack(str) + case 'red': + return type === 'foreground' ? chalk.red(str) : chalk.bgRed(str) + case 'green': + return type === 'foreground' ? chalk.green(str) : chalk.bgGreen(str) + case 'yellow': + return type === 'foreground' ? chalk.yellow(str) : chalk.bgYellow(str) + case 'blue': + return type === 'foreground' ? chalk.blue(str) : chalk.bgBlue(str) + case 'magenta': + return type === 'foreground' ? chalk.magenta(str) : chalk.bgMagenta(str) + case 'cyan': + return type === 'foreground' ? chalk.cyan(str) : chalk.bgCyan(str) + case 'white': + return type === 'foreground' ? chalk.white(str) : chalk.bgWhite(str) + case 'blackBright': + return type === 'foreground' + ? chalk.blackBright(str) + : chalk.bgBlackBright(str) + case 'redBright': + return type === 'foreground' + ? chalk.redBright(str) + : chalk.bgRedBright(str) + case 'greenBright': + return type === 'foreground' + ? chalk.greenBright(str) + : chalk.bgGreenBright(str) + case 'yellowBright': + return type === 'foreground' + ? chalk.yellowBright(str) + : chalk.bgYellowBright(str) + case 'blueBright': + return type === 'foreground' + ? chalk.blueBright(str) + : chalk.bgBlueBright(str) + case 'magentaBright': + return type === 'foreground' + ? chalk.magentaBright(str) + : chalk.bgMagentaBright(str) + case 'cyanBright': + return type === 'foreground' + ? chalk.cyanBright(str) + : chalk.bgCyanBright(str) + case 'whiteBright': + return type === 'foreground' + ? chalk.whiteBright(str) + : chalk.bgWhiteBright(str) + } + } + + if (color.startsWith('#')) { + return type === 'foreground' + ? chalk.hex(color)(str) + : chalk.bgHex(color)(str) + } + + if (color.startsWith('ansi256')) { + const matches = ANSI_REGEX.exec(color) + + if (!matches) { + return str + } + + const value = Number(matches[1]) + + return type === 'foreground' + ? chalk.ansi256(value)(str) + : chalk.bgAnsi256(value)(str) + } + + if (color.startsWith('rgb')) { + const matches = RGB_REGEX.exec(color) + + if (!matches) { + return str + } + + const firstValue = Number(matches[1]) + const secondValue = Number(matches[2]) + const thirdValue = Number(matches[3]) + + return type === 'foreground' + ? chalk.rgb(firstValue, secondValue, thirdValue)(str) + : chalk.bgRgb(firstValue, secondValue, thirdValue)(str) + } + + return str +} + +/** + * Apply TextStyles to a string using chalk. + * This is the inverse of parsing ANSI codes - we generate them from structured styles. + * Theme resolution happens at component layer, not here. + */ +export function applyTextStyles(text: string, styles: TextStyles): string { + let result = text + + // Apply styles in reverse order of desired nesting. + // chalk wraps text so later calls become outer wrappers. + // Desired order (outermost to innermost): + // background > foreground > text modifiers + // So we apply: text modifiers first, then foreground, then background last. + + if (styles.inverse) { + result = chalk.inverse(result) + } + + if (styles.strikethrough) { + result = chalk.strikethrough(result) + } + + if (styles.underline) { + result = chalk.underline(result) + } + + if (styles.italic) { + result = chalk.italic(result) + } + + if (styles.bold) { + result = chalk.bold(result) + } + + if (styles.dim) { + result = chalk.dim(result) + } + + if (styles.color) { + // Color is now always a raw color value (theme resolution happens at component layer) + result = colorize(result, styles.color, 'foreground') + } + + if (styles.backgroundColor) { + // backgroundColor is now always a raw color value + result = colorize(result, styles.backgroundColor, 'background') + } + + return result +} + +/** + * Apply a raw color value to text. + * Theme resolution should happen at component layer, not here. + */ +export function applyColor(text: string, color: Color | undefined): string { + if (!color) { + return text + } + return colorize(text, color, 'foreground') +} diff --git a/ink/components/AlternateScreen.tsx b/ink/components/AlternateScreen.tsx new file mode 100644 index 0000000..b736f92 --- /dev/null +++ b/ink/components/AlternateScreen.tsx @@ -0,0 +1,80 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type PropsWithChildren, useContext, useInsertionEffect } from 'react'; +import instances from '../instances.js'; +import { DISABLE_MOUSE_TRACKING, ENABLE_MOUSE_TRACKING, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN } from '../termio/dec.js'; +import { TerminalWriteContext } from '../useTerminalNotification.js'; +import Box from './Box.js'; +import { TerminalSizeContext } from './TerminalSizeContext.js'; +type Props = PropsWithChildren<{ + /** Enable SGR mouse tracking (wheel + click/drag). Default true. */ + mouseTracking?: boolean; +}>; + +/** + * Run children in the terminal's alternate screen buffer, constrained to + * the viewport height. While mounted: + * + * - Enters the alt screen (DEC 1049), clears it, homes the cursor + * - Constrains its own height to the terminal row count, so overflow must + * be handled via `overflow: scroll` / flexbox (no native scrollback) + * - Optionally enables SGR mouse tracking (wheel + click/drag) — events + * surface as `ParsedKey` (wheel) and update the Ink instance's + * selection state (click/drag) + * + * On unmount, disables mouse tracking and exits the alt screen, restoring + * the main screen's content. Safe for use in ctrl-o transcript overlays + * and similar temporary fullscreen views — the main screen is preserved. + * + * Notifies the Ink instance via `setAltScreenActive()` so the renderer + * keeps the cursor inside the viewport (preventing the cursor-restore LF + * from scrolling content) and so signal-exit cleanup can exit the alt + * screen if the component's own unmount doesn't run. + */ +export function AlternateScreen(t0) { + const $ = _c(7); + const { + children, + mouseTracking: t1 + } = t0; + const mouseTracking = t1 === undefined ? true : t1; + const size = useContext(TerminalSizeContext); + const writeRaw = useContext(TerminalWriteContext); + let t2; + let t3; + if ($[0] !== mouseTracking || $[1] !== writeRaw) { + t2 = () => { + const ink = instances.get(process.stdout); + if (!writeRaw) { + return; + } + writeRaw(ENTER_ALT_SCREEN + "\x1B[2J\x1B[H" + (mouseTracking ? ENABLE_MOUSE_TRACKING : "")); + ink?.setAltScreenActive(true, mouseTracking); + return () => { + ink?.setAltScreenActive(false); + ink?.clearTextSelection(); + writeRaw((mouseTracking ? DISABLE_MOUSE_TRACKING : "") + EXIT_ALT_SCREEN); + }; + }; + t3 = [writeRaw, mouseTracking]; + $[0] = mouseTracking; + $[1] = writeRaw; + $[2] = t2; + $[3] = t3; + } else { + t2 = $[2]; + t3 = $[3]; + } + useInsertionEffect(t2, t3); + const t4 = size?.rows ?? 24; + let t5; + if ($[4] !== children || $[5] !== t4) { + t5 = {children}; + $[4] = children; + $[5] = t4; + $[6] = t5; + } else { + t5 = $[6]; + } + return t5; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzV2l0aENoaWxkcmVuIiwidXNlQ29udGV4dCIsInVzZUluc2VydGlvbkVmZmVjdCIsImluc3RhbmNlcyIsIkRJU0FCTEVfTU9VU0VfVFJBQ0tJTkciLCJFTkFCTEVfTU9VU0VfVFJBQ0tJTkciLCJFTlRFUl9BTFRfU0NSRUVOIiwiRVhJVF9BTFRfU0NSRUVOIiwiVGVybWluYWxXcml0ZUNvbnRleHQiLCJCb3giLCJUZXJtaW5hbFNpemVDb250ZXh0IiwiUHJvcHMiLCJtb3VzZVRyYWNraW5nIiwiQWx0ZXJuYXRlU2NyZWVuIiwidDAiLCIkIiwiX2MiLCJjaGlsZHJlbiIsInQxIiwidW5kZWZpbmVkIiwic2l6ZSIsIndyaXRlUmF3IiwidDIiLCJ0MyIsImluayIsImdldCIsInByb2Nlc3MiLCJzdGRvdXQiLCJzZXRBbHRTY3JlZW5BY3RpdmUiLCJjbGVhclRleHRTZWxlY3Rpb24iLCJ0NCIsInJvd3MiLCJ0NSJdLCJzb3VyY2VzIjpbIkFsdGVybmF0ZVNjcmVlbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7XG4gIHR5cGUgUHJvcHNXaXRoQ2hpbGRyZW4sXG4gIHVzZUNvbnRleHQsXG4gIHVzZUluc2VydGlvbkVmZmVjdCxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgaW5zdGFuY2VzIGZyb20gJy4uL2luc3RhbmNlcy5qcydcbmltcG9ydCB7XG4gIERJU0FCTEVfTU9VU0VfVFJBQ0tJTkcsXG4gIEVOQUJMRV9NT1VTRV9UUkFDS0lORyxcbiAgRU5URVJfQUxUX1NDUkVFTixcbiAgRVhJVF9BTFRfU0NSRUVOLFxufSBmcm9tICcuLi90ZXJtaW8vZGVjLmpzJ1xuaW1wb3J0IHsgVGVybWluYWxXcml0ZUNvbnRleHQgfSBmcm9tICcuLi91c2VUZXJtaW5hbE5vdGlmaWNhdGlvbi5qcydcbmltcG9ydCBCb3ggZnJvbSAnLi9Cb3guanMnXG5pbXBvcnQgeyBUZXJtaW5hbFNpemVDb250ZXh0IH0gZnJvbSAnLi9UZXJtaW5hbFNpemVDb250ZXh0LmpzJ1xuXG50eXBlIFByb3BzID0gUHJvcHNXaXRoQ2hpbGRyZW48e1xuICAvKiogRW5hYmxlIFNHUiBtb3VzZSB0cmFja2luZyAod2hlZWwgKyBjbGljay9kcmFnKS4gRGVmYXVsdCB0cnVlLiAqL1xuICBtb3VzZVRyYWNraW5nPzogYm9vbGVhblxufT5cblxuLyoqXG4gKiBSdW4gY2hpbGRyZW4gaW4gdGhlIHRlcm1pbmFsJ3MgYWx0ZXJuYXRlIHNjcmVlbiBidWZmZXIsIGNvbnN0cmFpbmVkIHRvXG4gKiB0aGUgdmlld3BvcnQgaGVpZ2h0LiBXaGlsZSBtb3VudGVkOlxuICpcbiAqIC0gRW50ZXJzIHRoZSBhbHQgc2NyZWVuIChERUMgMTA0OSksIGNsZWFycyBpdCwgaG9tZXMgdGhlIGN1cnNvclxuICogLSBDb25zdHJhaW5zIGl0cyBvd24gaGVpZ2h0IHRvIHRoZSB0ZXJtaW5hbCByb3cgY291bnQsIHNvIG92ZXJmbG93IG11c3RcbiAqICAgYmUgaGFuZGxlZCB2aWEgYG92ZXJmbG93OiBzY3JvbGxgIC8gZmxleGJveCAobm8gbmF0aXZlIHNjcm9sbGJhY2spXG4gKiAtIE9wdGlvbmFsbHkgZW5hYmxlcyBTR1IgbW91c2UgdHJhY2tpbmcgKHdoZWVsICsgY2xpY2svZHJhZykg4oCUIGV2ZW50c1xuICogICBzdXJmYWNlIGFzIGBQYXJzZWRLZXlgICh3aGVlbCkgYW5kIHVwZGF0ZSB0aGUgSW5rIGluc3RhbmNlJ3NcbiAqICAgc2VsZWN0aW9uIHN0YXRlIChjbGljay9kcmFnKVxuICpcbiAqIE9uIHVubW91bnQsIGRpc2FibGVzIG1vdXNlIHRyYWNraW5nIGFuZCBleGl0cyB0aGUgYWx0IHNjcmVlbiwgcmVzdG9yaW5nXG4gKiB0aGUgbWFpbiBzY3JlZW4ncyBjb250ZW50LiBTYWZlIGZvciB1c2UgaW4gY3RybC1vIHRyYW5zY3JpcHQgb3ZlcmxheXNcbiAqIGFuZCBzaW1pbGFyIHRlbXBvcmFyeSBmdWxsc2NyZWVuIHZpZXdzIOKAlCB0aGUgbWFpbiBzY3JlZW4gaXMgcHJlc2VydmVkLlxuICpcbiAqIE5vdGlmaWVzIHRoZSBJbmsgaW5zdGFuY2UgdmlhIGBzZXRBbHRTY3JlZW5BY3RpdmUoKWAgc28gdGhlIHJlbmRlcmVyXG4gKiBrZWVwcyB0aGUgY3Vyc29yIGluc2lkZSB0aGUgdmlld3BvcnQgKHByZXZlbnRpbmcgdGhlIGN1cnNvci1yZXN0b3JlIExGXG4gKiBmcm9tIHNjcm9sbGluZyBjb250ZW50KSBhbmQgc28gc2lnbmFsLWV4aXQgY2xlYW51cCBjYW4gZXhpdCB0aGUgYWx0XG4gKiBzY3JlZW4gaWYgdGhlIGNvbXBvbmVudCdzIG93biB1bm1vdW50IGRvZXNuJ3QgcnVuLlxuICovXG5leHBvcnQgZnVuY3Rpb24gQWx0ZXJuYXRlU2NyZWVuKHtcbiAgY2hpbGRyZW4sXG4gIG1vdXNlVHJhY2tpbmcgPSB0cnVlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBzaXplID0gdXNlQ29udGV4dChUZXJtaW5hbFNpemVDb250ZXh0KVxuICBjb25zdCB3cml0ZVJhdyA9IHVzZUNvbnRleHQoVGVybWluYWxXcml0ZUNvbnRleHQpXG5cbiAgLy8gdXNlSW5zZXJ0aW9uRWZmZWN0IChub3QgdXNlTGF5b3V0RWZmZWN0KTogcmVhY3QtcmVjb25jaWxlciBjYWxsc1xuICAvLyByZXNldEFmdGVyQ29tbWl0IGJldHdlZW4gdGhlIG11dGF0aW9uIGFuZCBsYXlvdXQgY29tbWl0IHBoYXNlcywgYW5kXG4gIC8vIEluaydzIHJlc2V0QWZ0ZXJDb21taXQgdHJpZ2dlcnMgb25SZW5kZXIuIFdpdGggdXNlTGF5b3V0RWZmZWN0LCB0aGF0XG4gIC8vIGZpcnN0IG9uUmVuZGVyIGZpcmVzIEJFRk9SRSB0aGlzIGVmZmVjdCDigJQgd3JpdGluZyBhIGZ1bGwgZnJhbWUgdG8gdGhlXG4gIC8vIG1haW4gc2NyZWVuIHdpdGggYWx0U2NyZWVuPWZhbHNlLiBUaGF0IGZyYW1lIGlzIHByZXNlcnZlZCB3aGVuIHdlXG4gIC8vIGVudGVyIGFsdCBzY3JlZW4gYW5kIHJldmVhbGVkIG9uIGV4aXQgYXMgYSBicm9rZW4gdmlldy4gSW5zZXJ0aW9uXG4gIC8vIGVmZmVjdHMgZmlyZSBkdXJpbmcgdGhlIG11dGF0aW9uIHBoYXNlLCBiZWZvcmUgcmVzZXRBZnRlckNvbW1pdCwgc29cbiAgLy8gRU5URVJfQUxUX1NDUkVFTiByZWFjaGVzIHRoZSB0ZXJtaW5hbCBiZWZvcmUgdGhlIGZpcnN0IGZyYW1lIGRvZXMuXG4gIC8vIENsZWFudXAgdGltaW5nIGlzIHVuY2hhbmdlZDogYm90aCBpbnNlcnRpb24gYW5kIGxheW91dCBlZmZlY3QgY2xlYW51cFxuICAvLyBydW4gaW4gdGhlIG11dGF0aW9uIHBoYXNlIG9uIHVubW91bnQsIGJlZm9yZSByZXNldEFmdGVyQ29tbWl0LlxuICB1c2VJbnNlcnRpb25FZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IGluayA9IGluc3RhbmNlcy5nZXQocHJvY2Vzcy5zdGRvdXQpXG4gICAgaWYgKCF3cml0ZVJhdykgcmV0dXJuXG5cbiAgICB3cml0ZVJhdyhcbiAgICAgIEVOVEVSX0FMVF9TQ1JFRU4gK1xuICAgICAgICAnXFx4MWJbMkpcXHgxYltIJyArXG4gICAgICAgIChtb3VzZVRyYWNraW5nID8gRU5BQkxFX01PVVNFX1RSQUNLSU5HIDogJycpLFxuICAgIClcbiAgICBpbms/LnNldEFsdFNjcmVlbkFjdGl2ZSh0cnVlLCBtb3VzZVRyYWNraW5nKVxuXG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGluaz8uc2V0QWx0U2NyZWVuQWN0aXZlKGZhbHNlKVxuICAgICAgaW5rPy5jbGVhclRleHRTZWxlY3Rpb24oKVxuICAgICAgd3JpdGVSYXcoKG1vdXNlVHJhY2tpbmcgPyBESVNBQkxFX01PVVNFX1RSQUNLSU5HIDogJycpICsgRVhJVF9BTFRfU0NSRUVOKVxuICAgIH1cbiAgfSwgW3dyaXRlUmF3LCBtb3VzZVRyYWNraW5nXSlcblxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgaGVpZ2h0PXtzaXplPy5yb3dzID8/IDI0fVxuICAgICAgd2lkdGg9XCIxMDAlXCJcbiAgICAgIGZsZXhTaHJpbms9ezB9XG4gICAgPlxuICAgICAge2NoaWxkcmVufVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQ1YsS0FBS0MsaUJBQWlCLEVBQ3RCQyxVQUFVLEVBQ1ZDLGtCQUFrQixRQUNiLE9BQU87QUFDZCxPQUFPQyxTQUFTLE1BQU0saUJBQWlCO0FBQ3ZDLFNBQ0VDLHNCQUFzQixFQUN0QkMscUJBQXFCLEVBQ3JCQyxnQkFBZ0IsRUFDaEJDLGVBQWUsUUFDVixrQkFBa0I7QUFDekIsU0FBU0Msb0JBQW9CLFFBQVEsK0JBQStCO0FBQ3BFLE9BQU9DLEdBQUcsTUFBTSxVQUFVO0FBQzFCLFNBQVNDLG1CQUFtQixRQUFRLDBCQUEwQjtBQUU5RCxLQUFLQyxLQUFLLEdBQUdYLGlCQUFpQixDQUFDO0VBQzdCO0VBQ0FZLGFBQWEsQ0FBQyxFQUFFLE9BQU87QUFDekIsQ0FBQyxDQUFDOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFDLGdCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXlCO0lBQUFDLFFBQUE7SUFBQUwsYUFBQSxFQUFBTTtFQUFBLElBQUFKLEVBR3hCO0VBRE4sTUFBQUYsYUFBQSxHQUFBTSxFQUFvQixLQUFwQkMsU0FBb0IsR0FBcEIsSUFBb0IsR0FBcEJELEVBQW9CO0VBRXBCLE1BQUFFLElBQUEsR0FBYW5CLFVBQVUsQ0FBQ1MsbUJBQW1CLENBQUM7RUFDNUMsTUFBQVcsUUFBQSxHQUFpQnBCLFVBQVUsQ0FBQ08sb0JBQW9CLENBQUM7RUFBQSxJQUFBYyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFSLENBQUEsUUFBQUgsYUFBQSxJQUFBRyxDQUFBLFFBQUFNLFFBQUE7SUFZOUJDLEVBQUEsR0FBQUEsQ0FBQTtNQUNqQixNQUFBRSxHQUFBLEdBQVlyQixTQUFTLENBQUFzQixHQUFJLENBQUNDLE9BQU8sQ0FBQUMsTUFBTyxDQUFDO01BQ3pDLElBQUksQ0FBQ04sUUFBUTtRQUFBO01BQUE7TUFFYkEsUUFBUSxDQUNOZixnQkFBZ0IsR0FDZCxlQUFlLElBQ2RNLGFBQWEsR0FBYlAscUJBQTBDLEdBQTFDLEVBQTBDLENBQy9DLENBQUM7TUFDRG1CLEdBQUcsRUFBQUksa0JBQXlDLENBQXBCLElBQUksRUFBRWhCLGFBQWEsQ0FBQztNQUFBLE9BRXJDO1FBQ0xZLEdBQUcsRUFBQUksa0JBQTJCLENBQU4sS0FBSyxDQUFDO1FBQzlCSixHQUFHLEVBQUFLLGtCQUFzQixDQUFELENBQUM7UUFDekJSLFFBQVEsQ0FBQyxDQUFDVCxhQUFhLEdBQWJSLHNCQUEyQyxHQUEzQyxFQUEyQyxJQUFJRyxlQUFlLENBQUM7TUFBQSxDQUMxRTtJQUFBLENBQ0Y7SUFBRWdCLEVBQUEsSUFBQ0YsUUFBUSxFQUFFVCxhQUFhLENBQUM7SUFBQUcsQ0FBQSxNQUFBSCxhQUFBO0lBQUFHLENBQUEsTUFBQU0sUUFBQTtJQUFBTixDQUFBLE1BQUFPLEVBQUE7SUFBQVAsQ0FBQSxNQUFBUSxFQUFBO0VBQUE7SUFBQUQsRUFBQSxHQUFBUCxDQUFBO0lBQUFRLEVBQUEsR0FBQVIsQ0FBQTtFQUFBO0VBaEI1QmIsa0JBQWtCLENBQUNvQixFQWdCbEIsRUFBRUMsRUFBeUIsQ0FBQztFQUtqQixNQUFBTyxFQUFBLEdBQUFWLElBQUksRUFBQVcsSUFBWSxJQUFoQixFQUFnQjtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBakIsQ0FBQSxRQUFBRSxRQUFBLElBQUFGLENBQUEsUUFBQWUsRUFBQTtJQUYxQkUsRUFBQSxJQUFDLEdBQUcsQ0FDWSxhQUFRLENBQVIsUUFBUSxDQUNkLE1BQWdCLENBQWhCLENBQUFGLEVBQWUsQ0FBQyxDQUNsQixLQUFNLENBQU4sTUFBTSxDQUNBLFVBQUMsQ0FBRCxHQUFDLENBRVpiLFNBQU8sQ0FDVixFQVBDLEdBQUcsQ0FPRTtJQUFBRixDQUFBLE1BQUFFLFFBQUE7SUFBQUYsQ0FBQSxNQUFBZSxFQUFBO0lBQUFmLENBQUEsTUFBQWlCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFqQixDQUFBO0VBQUE7RUFBQSxPQVBOaUIsRUFPTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/ink/components/App.tsx b/ink/components/App.tsx new file mode 100644 index 0000000..f9d248d --- /dev/null +++ b/ink/components/App.tsx @@ -0,0 +1,658 @@ +import React, { PureComponent, type ReactNode } from 'react'; +import { updateLastInteractionTime } from '../../bootstrap/state.js'; +import { logForDebugging } from '../../utils/debug.js'; +import { stopCapturingEarlyInput } from '../../utils/earlyInput.js'; +import { isEnvTruthy } from '../../utils/envUtils.js'; +import { isMouseClicksDisabled } from '../../utils/fullscreen.js'; +import { logError } from '../../utils/log.js'; +import { EventEmitter } from '../events/emitter.js'; +import { InputEvent } from '../events/input-event.js'; +import { TerminalFocusEvent } from '../events/terminal-focus-event.js'; +import { INITIAL_STATE, type ParsedInput, type ParsedKey, type ParsedMouse, parseMultipleKeypresses } from '../parse-keypress.js'; +import reconciler from '../reconciler.js'; +import { finishSelection, hasSelection, type SelectionState, startSelection } from '../selection.js'; +import { isXtermJs, setXtversionName, supportsExtendedKeys } from '../terminal.js'; +import { getTerminalFocused, setTerminalFocused } from '../terminal-focus-state.js'; +import { TerminalQuerier, xtversion } from '../terminal-querier.js'; +import { DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, ENABLE_KITTY_KEYBOARD, ENABLE_MODIFY_OTHER_KEYS, FOCUS_IN, FOCUS_OUT } from '../termio/csi.js'; +import { DBP, DFE, DISABLE_MOUSE_TRACKING, EBP, EFE, HIDE_CURSOR, SHOW_CURSOR } from '../termio/dec.js'; +import AppContext from './AppContext.js'; +import { ClockProvider } from './ClockContext.js'; +import CursorDeclarationContext, { type CursorDeclarationSetter } from './CursorDeclarationContext.js'; +import ErrorOverview from './ErrorOverview.js'; +import StdinContext from './StdinContext.js'; +import { TerminalFocusProvider } from './TerminalFocusContext.js'; +import { TerminalSizeContext } from './TerminalSizeContext.js'; + +// Platforms that support Unix-style process suspension (SIGSTOP/SIGCONT) +const SUPPORTS_SUSPEND = process.platform !== 'win32'; + +// After this many milliseconds of stdin silence, the next chunk triggers +// a terminal mode re-assert (mouse tracking). Catches tmux detach→attach, +// ssh reconnect, and laptop wake — the terminal resets DEC private modes +// but no signal reaches us. 5s is well above normal inter-keystroke gaps +// but short enough that the first scroll after reattach works. +const STDIN_RESUME_GAP_MS = 5000; +type Props = { + readonly children: ReactNode; + readonly stdin: NodeJS.ReadStream; + readonly stdout: NodeJS.WriteStream; + readonly stderr: NodeJS.WriteStream; + readonly exitOnCtrlC: boolean; + readonly onExit: (error?: Error) => void; + readonly terminalColumns: number; + readonly terminalRows: number; + // Text selection state. App mutates this directly from mouse events + // and calls onSelectionChange to trigger a repaint. Mouse events only + // arrive when (or similar) enables mouse tracking, + // so the handler is always wired but dormant until tracking is on. + readonly selection: SelectionState; + readonly onSelectionChange: () => void; + // Dispatch a click at (col, row) — hit-tests the DOM tree and bubbles + // onClick handlers. Returns true if a DOM handler consumed the click. + // No-op (returns false) outside fullscreen mode (Ink.dispatchClick + // gates on altScreenActive). + readonly onClickAt: (col: number, row: number) => boolean; + // Dispatch hover (onMouseEnter/onMouseLeave) as the pointer moves over + // DOM elements. Called for mode-1003 motion events with no button held. + // No-op outside fullscreen (Ink.dispatchHover gates on altScreenActive). + readonly onHoverAt: (col: number, row: number) => void; + // Look up the OSC 8 hyperlink at (col, row) synchronously at click + // time. Returns the URL or undefined. The browser-open is deferred by + // MULTI_CLICK_TIMEOUT_MS so double-click can cancel it. + readonly getHyperlinkAt: (col: number, row: number) => string | undefined; + // Open a hyperlink URL in the browser. Called after the timer fires. + readonly onOpenHyperlink: (url: string) => void; + // Called on double/triple-click PRESS at (col, row). count=2 selects + // the word under the cursor; count=3 selects the line. Ink reads the + // screen buffer to find word/line boundaries and mutates selection, + // setting isDragging=true so a subsequent drag extends by word/line. + readonly onMultiClick: (col: number, row: number, count: 2 | 3) => void; + // Called on drag-motion. Mode-aware: char mode updates focus to the + // exact cell; word/line mode snaps to word/line boundaries. Needs + // screen-buffer access (word boundaries) so lives on Ink, not here. + readonly onSelectionDrag: (col: number, row: number) => void; + // Called when stdin data arrives after a >STDIN_RESUME_GAP_MS gap. + // Ink re-asserts terminal modes: extended key reporting, and (when in + // fullscreen) re-enters alt-screen + mouse tracking. Idempotent on the + // terminal side. Optional so testing.tsx doesn't need to stub it. + readonly onStdinResume?: () => void; + // Receives the declared native-cursor position from useDeclaredCursor + // so ink.tsx can park the terminal cursor there after each frame. + // Enables IME composition at the input caret and lets screen readers / + // magnifiers track the input. Optional so testing.tsx doesn't stub it. + readonly onCursorDeclaration?: CursorDeclarationSetter; + // Dispatch a keyboard event through the DOM tree. Called for each + // parsed key alongside the legacy EventEmitter path. + readonly dispatchKeyboardEvent: (parsedKey: ParsedKey) => void; +}; + +// Multi-click detection thresholds. 500ms is the macOS default; a small +// position tolerance allows for trackpad jitter between clicks. +const MULTI_CLICK_TIMEOUT_MS = 500; +const MULTI_CLICK_DISTANCE = 1; +type State = { + readonly error?: Error; +}; + +// Root component for all Ink apps +// It renders stdin and stdout contexts, so that children can access them if needed +// It also handles Ctrl+C exiting and cursor visibility +export default class App extends PureComponent { + static displayName = 'InternalApp'; + static getDerivedStateFromError(error: Error) { + return { + error + }; + } + override state = { + error: undefined + }; + + // Count how many components enabled raw mode to avoid disabling + // raw mode until all components don't need it anymore + rawModeEnabledCount = 0; + internal_eventEmitter = new EventEmitter(); + keyParseState = INITIAL_STATE; + // Timer for flushing incomplete escape sequences + incompleteEscapeTimer: NodeJS.Timeout | null = null; + // Timeout durations for incomplete sequences (ms) + readonly NORMAL_TIMEOUT = 50; // Short timeout for regular esc sequences + readonly PASTE_TIMEOUT = 500; // Longer timeout for paste operations + + // Terminal query/response dispatch. Responses arrive on stdin (parsed + // out by parse-keypress) and are routed to pending promise resolvers. + querier = new TerminalQuerier(this.props.stdout); + + // Multi-click tracking for double/triple-click text selection. A click + // within MULTI_CLICK_TIMEOUT_MS and MULTI_CLICK_DISTANCE of the previous + // click increments clickCount; otherwise it resets to 1. + lastClickTime = 0; + lastClickCol = -1; + lastClickRow = -1; + clickCount = 0; + // Deferred hyperlink-open timer — cancelled if a second click arrives + // within MULTI_CLICK_TIMEOUT_MS (so double-clicking a hyperlink selects + // the word without also opening the browser). DOM onClick dispatch is + // NOT deferred — it returns true from onClickAt and skips this timer. + pendingHyperlinkTimer: ReturnType | null = null; + // Last mode-1003 motion position. Terminals already dedupe to cell + // granularity but this also lets us skip dispatchHover entirely on + // repeat events (drag-then-release at same cell, etc.). + lastHoverCol = -1; + lastHoverRow = -1; + + // Timestamp of last stdin chunk. Used to detect long gaps (tmux attach, + // ssh reconnect, laptop wake) and trigger terminal mode re-assert. + // Initialized to now so startup doesn't false-trigger. + lastStdinTime = Date.now(); + + // Determines if TTY is supported on the provided stdin + isRawModeSupported(): boolean { + return this.props.stdin.isTTY; + } + override render() { + return + + + + + {})}> + {this.state.error ? : this.props.children} + + + + + + ; + } + override componentDidMount() { + // In accessibility mode, keep the native cursor visible for screen magnifiers and other tools + if (this.props.stdout.isTTY && !isEnvTruthy(process.env.CLAUDE_CODE_ACCESSIBILITY)) { + this.props.stdout.write(HIDE_CURSOR); + } + } + override componentWillUnmount() { + if (this.props.stdout.isTTY) { + this.props.stdout.write(SHOW_CURSOR); + } + + // Clear any pending timers + if (this.incompleteEscapeTimer) { + clearTimeout(this.incompleteEscapeTimer); + this.incompleteEscapeTimer = null; + } + if (this.pendingHyperlinkTimer) { + clearTimeout(this.pendingHyperlinkTimer); + this.pendingHyperlinkTimer = null; + } + // ignore calling setRawMode on an handle stdin it cannot be called + if (this.isRawModeSupported()) { + this.handleSetRawMode(false); + } + } + override componentDidCatch(error: Error) { + this.handleExit(error); + } + handleSetRawMode = (isEnabled: boolean): void => { + const { + stdin + } = this.props; + if (!this.isRawModeSupported()) { + if (stdin === process.stdin) { + throw new Error('Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported'); + } else { + throw new Error('Raw mode is not supported on the stdin provided to Ink.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported'); + } + } + stdin.setEncoding('utf8'); + if (isEnabled) { + // Ensure raw mode is enabled only once + if (this.rawModeEnabledCount === 0) { + // Stop early input capture right before we add our own readable handler. + // Both use the same stdin 'readable' + read() pattern, so they can't + // coexist -- our handler would drain stdin before Ink's can see it. + // The buffered text is preserved for REPL.tsx via consumeEarlyInput(). + stopCapturingEarlyInput(); + stdin.ref(); + stdin.setRawMode(true); + stdin.addListener('readable', this.handleReadable); + // Enable bracketed paste mode + this.props.stdout.write(EBP); + // Enable terminal focus reporting (DECSET 1004) + this.props.stdout.write(EFE); + // Enable extended key reporting so ctrl+shift+ is + // distinguishable from ctrl+. We write both the kitty stack + // push (CSI >1u) and xterm modifyOtherKeys level 2 (CSI >4;2m) — + // terminals honor whichever they implement (tmux only accepts the + // latter). + if (supportsExtendedKeys()) { + this.props.stdout.write(ENABLE_KITTY_KEYBOARD); + this.props.stdout.write(ENABLE_MODIFY_OTHER_KEYS); + } + // Probe terminal identity. XTVERSION survives SSH (query/reply goes + // through the pty), unlike TERM_PROGRAM. Used for wheel-scroll base + // detection when env vars are absent. Fire-and-forget: the DA1 + // sentinel bounds the round-trip, and if the terminal ignores the + // query, flush() still resolves and name stays undefined. + // Deferred to next tick so it fires AFTER the current synchronous + // init sequence completes — avoids interleaving with alt-screen/mouse + // tracking enable writes that may happen in the same render cycle. + setImmediate(() => { + void Promise.all([this.querier.send(xtversion()), this.querier.flush()]).then(([r]) => { + if (r) { + setXtversionName(r.name); + logForDebugging(`XTVERSION: terminal identified as "${r.name}"`); + } else { + logForDebugging('XTVERSION: no reply (terminal ignored query)'); + } + }); + }); + } + this.rawModeEnabledCount++; + return; + } + + // Disable raw mode only when no components left that are using it + if (--this.rawModeEnabledCount === 0) { + this.props.stdout.write(DISABLE_MODIFY_OTHER_KEYS); + this.props.stdout.write(DISABLE_KITTY_KEYBOARD); + // Disable terminal focus reporting (DECSET 1004) + this.props.stdout.write(DFE); + // Disable bracketed paste mode + this.props.stdout.write(DBP); + stdin.setRawMode(false); + stdin.removeListener('readable', this.handleReadable); + stdin.unref(); + } + }; + + // Helper to flush incomplete escape sequences + flushIncomplete = (): void => { + // Clear the timer reference + this.incompleteEscapeTimer = null; + + // Only proceed if we have incomplete sequences + if (!this.keyParseState.incomplete) return; + + // Fullscreen: if stdin has data waiting, it's almost certainly the + // continuation of the buffered sequence (e.g. `[<64;74;16M` after a + // lone ESC). Node's event loop runs the timers phase before the poll + // phase, so when a heavy render blocks the loop past 50ms, this timer + // fires before the queued readable event even though the bytes are + // already buffered. Re-arm instead of flushing: handleReadable will + // drain stdin next and clear this timer. Prevents both the spurious + // Escape key and the lost scroll event. + if (this.props.stdin.readableLength > 0) { + this.incompleteEscapeTimer = setTimeout(this.flushIncomplete, this.NORMAL_TIMEOUT); + return; + } + + // Process incomplete as a flush operation (input=null) + // This reuses all existing parsing logic + this.processInput(null); + }; + + // Process input through the parser and handle the results + processInput = (input: string | Buffer | null): void => { + // Parse input using our state machine + const [keys, newState] = parseMultipleKeypresses(this.keyParseState, input); + this.keyParseState = newState; + + // Process ALL keys in a SINGLE discreteUpdates call to prevent + // "Maximum update depth exceeded" error when many keys arrive at once + // (e.g., from paste operations or holding keys rapidly). + // This batches all state updates from handleInput and all useInput + // listeners together within one high-priority update context. + if (keys.length > 0) { + reconciler.discreteUpdates(processKeysInBatch, this, keys, undefined, undefined); + } + + // If we have incomplete escape sequences, set a timer to flush them + if (this.keyParseState.incomplete) { + // Cancel any existing timer first + if (this.incompleteEscapeTimer) { + clearTimeout(this.incompleteEscapeTimer); + } + this.incompleteEscapeTimer = setTimeout(this.flushIncomplete, this.keyParseState.mode === 'IN_PASTE' ? this.PASTE_TIMEOUT : this.NORMAL_TIMEOUT); + } + }; + handleReadable = (): void => { + // Detect long stdin gaps (tmux attach, ssh reconnect, laptop wake). + // The terminal may have reset DEC private modes; re-assert mouse + // tracking. Checked before the read loop so one Date.now() covers + // all chunks in this readable event. + const now = Date.now(); + if (now - this.lastStdinTime > STDIN_RESUME_GAP_MS) { + this.props.onStdinResume?.(); + } + this.lastStdinTime = now; + try { + let chunk; + while ((chunk = this.props.stdin.read() as string | null) !== null) { + // Process the input chunk + this.processInput(chunk); + } + } catch (error) { + // In Bun, an uncaught throw inside a stream 'readable' handler can + // permanently wedge the stream: data stays buffered and 'readable' + // never re-emits. Catching here ensures the stream stays healthy so + // subsequent keystrokes are still delivered. + logError(error); + + // Re-attach the listener in case the exception detached it. + // Bun may remove the listener after an error; without this, + // the session freezes permanently (stdin reader dead, event loop alive). + const { + stdin + } = this.props; + if (this.rawModeEnabledCount > 0 && !stdin.listeners('readable').includes(this.handleReadable)) { + logForDebugging('handleReadable: re-attaching stdin readable listener after error recovery', { + level: 'warn' + }); + stdin.addListener('readable', this.handleReadable); + } + } + }; + handleInput = (input: string | undefined): void => { + // Exit on Ctrl+C + if (input === '\x03' && this.props.exitOnCtrlC) { + this.handleExit(); + } + + // Note: Ctrl+Z (suspend) is now handled in processKeysInBatch using the + // parsed key to support both raw (\x1a) and CSI u format from Kitty + // keyboard protocol terminals (Ghostty, iTerm2, kitty, WezTerm) + }; + handleExit = (error?: Error): void => { + if (this.isRawModeSupported()) { + this.handleSetRawMode(false); + } + this.props.onExit(error); + }; + handleTerminalFocus = (isFocused: boolean): void => { + // setTerminalFocused notifies subscribers: TerminalFocusProvider (context) + // and Clock (interval speed) — no App setState needed. + setTerminalFocused(isFocused); + }; + handleSuspend = (): void => { + if (!this.isRawModeSupported()) { + return; + } + + // Store the exact raw mode count to restore it properly + const rawModeCountBeforeSuspend = this.rawModeEnabledCount; + + // Completely disable raw mode before suspending + while (this.rawModeEnabledCount > 0) { + this.handleSetRawMode(false); + } + + // Show cursor, disable focus reporting, and disable mouse tracking + // before suspending. DISABLE_MOUSE_TRACKING is a no-op if tracking + // wasn't enabled, so it's safe to emit unconditionally — without + // it, SGR mouse sequences would appear as garbled text at the + // shell prompt while suspended. + if (this.props.stdout.isTTY) { + this.props.stdout.write(SHOW_CURSOR + DFE + DISABLE_MOUSE_TRACKING); + } + + // Emit suspend event for Claude Code to handle. Mostly just has a notification + this.internal_eventEmitter.emit('suspend'); + + // Set up resume handler + const resumeHandler = () => { + // Restore raw mode to exact previous state + for (let i = 0; i < rawModeCountBeforeSuspend; i++) { + if (this.isRawModeSupported()) { + this.handleSetRawMode(true); + } + } + + // Hide cursor (unless in accessibility mode) and re-enable focus reporting after resuming + if (this.props.stdout.isTTY) { + if (!isEnvTruthy(process.env.CLAUDE_CODE_ACCESSIBILITY)) { + this.props.stdout.write(HIDE_CURSOR); + } + // Re-enable focus reporting to restore terminal state + this.props.stdout.write(EFE); + } + + // Emit resume event for Claude Code to handle + this.internal_eventEmitter.emit('resume'); + process.removeListener('SIGCONT', resumeHandler); + }; + process.on('SIGCONT', resumeHandler); + process.kill(process.pid, 'SIGSTOP'); + }; +} + +// Helper to process all keys within a single discrete update context. +// discreteUpdates expects (fn, a, b, c, d) -> fn(a, b, c, d) +function processKeysInBatch(app: App, items: ParsedInput[], _unused1: undefined, _unused2: undefined): void { + // Update interaction time for notification timeout tracking. + // This is called from the central input handler to avoid having multiple + // stdin listeners that can cause race conditions and dropped input. + // Terminal responses (kind: 'response') are automated, not user input. + // Mode-1003 no-button motion is also excluded — passive cursor drift is + // not engagement (would suppress idle notifications + defer housekeeping). + if (items.some(i => i.kind === 'key' || i.kind === 'mouse' && !((i.button & 0x20) !== 0 && (i.button & 0x03) === 3))) { + updateLastInteractionTime(); + } + for (const item of items) { + // Terminal responses (DECRPM, DA1, OSC replies, etc.) are not user + // input — route them to the querier to resolve pending promises. + if (item.kind === 'response') { + app.querier.onResponse(item.response); + continue; + } + + // Mouse click/drag events update selection state (fullscreen only). + // Terminal sends 1-indexed col/row; convert to 0-indexed for the + // screen buffer. Button bit 0x20 = drag (motion while button held). + if (item.kind === 'mouse') { + handleMouseEvent(app, item); + continue; + } + const sequence = item.sequence; + + // Handle terminal focus events (DECSET 1004) + if (sequence === FOCUS_IN) { + app.handleTerminalFocus(true); + const event = new TerminalFocusEvent('terminalfocus'); + app.internal_eventEmitter.emit('terminalfocus', event); + continue; + } + if (sequence === FOCUS_OUT) { + app.handleTerminalFocus(false); + // Defensive: if we lost the release event (mouse released outside + // terminal window — some emulators drop it rather than capturing the + // pointer), focus-out is the next observable signal that the drag is + // over. Without this, drag-to-scroll's timer runs until the scroll + // boundary is hit. + if (app.props.selection.isDragging) { + finishSelection(app.props.selection); + app.props.onSelectionChange(); + } + const event = new TerminalFocusEvent('terminalblur'); + app.internal_eventEmitter.emit('terminalblur', event); + continue; + } + + // Failsafe: if we receive input, the terminal must be focused + if (!getTerminalFocused()) { + setTerminalFocused(true); + } + + // Handle Ctrl+Z (suspend) using parsed key to support both raw (\x1a) and + // CSI u format (\x1b[122;5u) from Kitty keyboard protocol terminals + if (item.name === 'z' && item.ctrl && SUPPORTS_SUSPEND) { + app.handleSuspend(); + continue; + } + app.handleInput(sequence); + const event = new InputEvent(item); + app.internal_eventEmitter.emit('input', event); + + // Also dispatch through the DOM tree so onKeyDown handlers fire. + app.props.dispatchKeyboardEvent(item); + } +} + +/** Exported for testing. Mutates app.props.selection and click/hover state. */ +export function handleMouseEvent(app: App, m: ParsedMouse): void { + // Allow disabling click handling while keeping wheel scroll (which goes + // through the keybinding system as 'wheelup'/'wheeldown', not here). + if (isMouseClicksDisabled()) return; + const sel = app.props.selection; + // Terminal coords are 1-indexed; screen buffer is 0-indexed + const col = m.col - 1; + const row = m.row - 1; + const baseButton = m.button & 0x03; + if (m.action === 'press') { + if ((m.button & 0x20) !== 0 && baseButton === 3) { + // Mode-1003 motion with no button held. Dispatch hover; skip the + // rest of this handler (no selection, no click-count side effects). + // Lost-release recovery: no-button motion while isDragging=true means + // the release happened outside the terminal window (iTerm2 doesn't + // capture the pointer past window bounds, so the SGR 'm' never + // arrives). Finish the selection here so copy-on-select fires. The + // FOCUS_OUT handler covers the "switched apps" case but not "released + // past the edge, came back" — and tmux drops focus events unless + // `focus-events on` is set, so this is the more reliable signal. + if (sel.isDragging) { + finishSelection(sel); + app.props.onSelectionChange(); + } + if (col === app.lastHoverCol && row === app.lastHoverRow) return; + app.lastHoverCol = col; + app.lastHoverRow = row; + app.props.onHoverAt(col, row); + return; + } + if (baseButton !== 0) { + // Non-left press breaks the multi-click chain. + app.clickCount = 0; + return; + } + if ((m.button & 0x20) !== 0) { + // Drag motion: mode-aware extension (char/word/line). onSelectionDrag + // calls notifySelectionChange internally — no extra onSelectionChange. + app.props.onSelectionDrag(col, row); + return; + } + // Lost-release fallback for mode-1002-only terminals: a fresh press + // while isDragging=true means the previous release was dropped (cursor + // left the window). Finish that selection so copy-on-select fires + // before startSelection/onMultiClick clobbers it. Mode-1003 terminals + // hit the no-button-motion recovery above instead, so this is rare. + if (sel.isDragging) { + finishSelection(sel); + app.props.onSelectionChange(); + } + // Fresh left press. Detect multi-click HERE (not on release) so the + // word/line highlight appears immediately and a subsequent drag can + // extend by word/line like native macOS. Previously detected on + // release, which meant (a) visible latency before the word highlights + // and (b) double-click+drag fell through to char-mode selection. + const now = Date.now(); + const nearLast = now - app.lastClickTime < MULTI_CLICK_TIMEOUT_MS && Math.abs(col - app.lastClickCol) <= MULTI_CLICK_DISTANCE && Math.abs(row - app.lastClickRow) <= MULTI_CLICK_DISTANCE; + app.clickCount = nearLast ? app.clickCount + 1 : 1; + app.lastClickTime = now; + app.lastClickCol = col; + app.lastClickRow = row; + if (app.clickCount >= 2) { + // Cancel any pending hyperlink-open from the first click — this is + // a double-click, not a single-click on a link. + if (app.pendingHyperlinkTimer) { + clearTimeout(app.pendingHyperlinkTimer); + app.pendingHyperlinkTimer = null; + } + // Cap at 3 (line select) for quadruple+ clicks. + const count = app.clickCount === 2 ? 2 : 3; + app.props.onMultiClick(col, row, count); + return; + } + startSelection(sel, col, row); + // SGR bit 0x08 = alt (xterm.js wires altKey here, not metaKey — see + // comment at the hyperlink-open guard below). On macOS xterm.js, + // receiving alt means macOptionClickForcesSelection is OFF (otherwise + // xterm.js would have consumed the event for native selection). + sel.lastPressHadAlt = (m.button & 0x08) !== 0; + app.props.onSelectionChange(); + return; + } + + // Release: end the drag even for non-zero button codes. Some terminals + // encode release with the motion bit or button=3 "no button" (carried + // over from pre-SGR X10 encoding) — filtering those would orphan + // isDragging=true and leave drag-to-scroll's timer running until the + // scroll boundary. Only act on non-left releases when we ARE dragging + // (so an unrelated middle/right click-release doesn't touch selection). + if (baseButton !== 0) { + if (!sel.isDragging) return; + finishSelection(sel); + app.props.onSelectionChange(); + return; + } + finishSelection(sel); + // NOTE: unlike the old release-based detection we do NOT reset clickCount + // on release-after-drag. This aligns with NSEvent.clickCount semantics: + // an intervening drag doesn't break the click chain. Practical upside: + // trackpad jitter during an intended double-click (press→wobble→release + // →press) now correctly resolves to word-select instead of breaking to a + // fresh single click. The nearLast window (500ms, 1 cell) bounds the + // effect — a deliberate drag past that just starts a fresh chain. + // A press+release with no drag in char mode is a click: anchor set, + // focus null → hasSelection false. In word/line mode the press already + // set anchor+focus (hasSelection true), so release just keeps the + // highlight. The anchor check guards against an orphaned release (no + // prior press — e.g. button was held when mouse tracking was enabled). + if (!hasSelection(sel) && sel.anchor) { + // Single click: dispatch DOM click immediately (cursor repositioning + // etc. are latency-sensitive). If no DOM handler consumed it, defer + // the hyperlink check so a second click can cancel it. + if (!app.props.onClickAt(col, row)) { + // Resolve the hyperlink URL synchronously while the screen buffer + // still reflects what the user clicked — deferring only the + // browser-open so double-click can cancel it. + const url = app.props.getHyperlinkAt(col, row); + // xterm.js (VS Code, Cursor, Windsurf, etc.) has its own OSC 8 link + // handler that fires on Cmd+click *without consuming the mouse event* + // (Linkifier._handleMouseUp calls link.activate() but never + // preventDefault/stopPropagation). The click is also forwarded to the + // pty as SGR, so both VS Code's terminalLinkManager AND our handler + // here would open the URL — twice. We can't filter on Cmd: xterm.js + // drops metaKey before SGR encoding (ICoreMouseEvent has no meta + // field; the SGR bit we call 'meta' is wired to alt). Let xterm.js + // own link-opening; Cmd+click is the native UX there anyway. + // TERM_PROGRAM is the sync fast-path; isXtermJs() is the XTVERSION + // probe result (catches SSH + non-VS Code embedders like Hyper). + if (url && process.env.TERM_PROGRAM !== 'vscode' && !isXtermJs()) { + // Clear any prior pending timer — clicking a second link + // supersedes the first (only the latest click opens). + if (app.pendingHyperlinkTimer) { + clearTimeout(app.pendingHyperlinkTimer); + } + app.pendingHyperlinkTimer = setTimeout((app, url) => { + app.pendingHyperlinkTimer = null; + app.props.onOpenHyperlink(url); + }, MULTI_CLICK_TIMEOUT_MS, app, url); + } + } + } + app.props.onSelectionChange(); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlB1cmVDb21wb25lbnQiLCJSZWFjdE5vZGUiLCJ1cGRhdGVMYXN0SW50ZXJhY3Rpb25UaW1lIiwibG9nRm9yRGVidWdnaW5nIiwic3RvcENhcHR1cmluZ0Vhcmx5SW5wdXQiLCJpc0VudlRydXRoeSIsImlzTW91c2VDbGlja3NEaXNhYmxlZCIsImxvZ0Vycm9yIiwiRXZlbnRFbWl0dGVyIiwiSW5wdXRFdmVudCIsIlRlcm1pbmFsRm9jdXNFdmVudCIsIklOSVRJQUxfU1RBVEUiLCJQYXJzZWRJbnB1dCIsIlBhcnNlZEtleSIsIlBhcnNlZE1vdXNlIiwicGFyc2VNdWx0aXBsZUtleXByZXNzZXMiLCJyZWNvbmNpbGVyIiwiZmluaXNoU2VsZWN0aW9uIiwiaGFzU2VsZWN0aW9uIiwiU2VsZWN0aW9uU3RhdGUiLCJzdGFydFNlbGVjdGlvbiIsImlzWHRlcm1KcyIsInNldFh0dmVyc2lvbk5hbWUiLCJzdXBwb3J0c0V4dGVuZGVkS2V5cyIsImdldFRlcm1pbmFsRm9jdXNlZCIsInNldFRlcm1pbmFsRm9jdXNlZCIsIlRlcm1pbmFsUXVlcmllciIsInh0dmVyc2lvbiIsIkRJU0FCTEVfS0lUVFlfS0VZQk9BUkQiLCJESVNBQkxFX01PRElGWV9PVEhFUl9LRVlTIiwiRU5BQkxFX0tJVFRZX0tFWUJPQVJEIiwiRU5BQkxFX01PRElGWV9PVEhFUl9LRVlTIiwiRk9DVVNfSU4iLCJGT0NVU19PVVQiLCJEQlAiLCJERkUiLCJESVNBQkxFX01PVVNFX1RSQUNLSU5HIiwiRUJQIiwiRUZFIiwiSElERV9DVVJTT1IiLCJTSE9XX0NVUlNPUiIsIkFwcENvbnRleHQiLCJDbG9ja1Byb3ZpZGVyIiwiQ3Vyc29yRGVjbGFyYXRpb25Db250ZXh0IiwiQ3Vyc29yRGVjbGFyYXRpb25TZXR0ZXIiLCJFcnJvck92ZXJ2aWV3IiwiU3RkaW5Db250ZXh0IiwiVGVybWluYWxGb2N1c1Byb3ZpZGVyIiwiVGVybWluYWxTaXplQ29udGV4dCIsIlNVUFBPUlRTX1NVU1BFTkQiLCJwcm9jZXNzIiwicGxhdGZvcm0iLCJTVERJTl9SRVNVTUVfR0FQX01TIiwiUHJvcHMiLCJjaGlsZHJlbiIsInN0ZGluIiwiTm9kZUpTIiwiUmVhZFN0cmVhbSIsInN0ZG91dCIsIldyaXRlU3RyZWFtIiwic3RkZXJyIiwiZXhpdE9uQ3RybEMiLCJvbkV4aXQiLCJlcnJvciIsIkVycm9yIiwidGVybWluYWxDb2x1bW5zIiwidGVybWluYWxSb3dzIiwic2VsZWN0aW9uIiwib25TZWxlY3Rpb25DaGFuZ2UiLCJvbkNsaWNrQXQiLCJjb2wiLCJyb3ciLCJvbkhvdmVyQXQiLCJnZXRIeXBlcmxpbmtBdCIsIm9uT3Blbkh5cGVybGluayIsInVybCIsIm9uTXVsdGlDbGljayIsImNvdW50Iiwib25TZWxlY3Rpb25EcmFnIiwib25TdGRpblJlc3VtZSIsIm9uQ3Vyc29yRGVjbGFyYXRpb24iLCJkaXNwYXRjaEtleWJvYXJkRXZlbnQiLCJwYXJzZWRLZXkiLCJNVUxUSV9DTElDS19USU1FT1VUX01TIiwiTVVMVElfQ0xJQ0tfRElTVEFOQ0UiLCJTdGF0ZSIsIkFwcCIsImRpc3BsYXlOYW1lIiwiZ2V0RGVyaXZlZFN0YXRlRnJvbUVycm9yIiwic3RhdGUiLCJ1bmRlZmluZWQiLCJyYXdNb2RlRW5hYmxlZENvdW50IiwiaW50ZXJuYWxfZXZlbnRFbWl0dGVyIiwia2V5UGFyc2VTdGF0ZSIsImluY29tcGxldGVFc2NhcGVUaW1lciIsIlRpbWVvdXQiLCJOT1JNQUxfVElNRU9VVCIsIlBBU1RFX1RJTUVPVVQiLCJxdWVyaWVyIiwicHJvcHMiLCJsYXN0Q2xpY2tUaW1lIiwibGFzdENsaWNrQ29sIiwibGFzdENsaWNrUm93IiwiY2xpY2tDb3VudCIsInBlbmRpbmdIeXBlcmxpbmtUaW1lciIsIlJldHVyblR5cGUiLCJzZXRUaW1lb3V0IiwibGFzdEhvdmVyQ29sIiwibGFzdEhvdmVyUm93IiwibGFzdFN0ZGluVGltZSIsIkRhdGUiLCJub3ciLCJpc1Jhd01vZGVTdXBwb3J0ZWQiLCJpc1RUWSIsInJlbmRlciIsImNvbHVtbnMiLCJyb3dzIiwiZXhpdCIsImhhbmRsZUV4aXQiLCJzZXRSYXdNb2RlIiwiaGFuZGxlU2V0UmF3TW9kZSIsImludGVybmFsX2V4aXRPbkN0cmxDIiwiaW50ZXJuYWxfcXVlcmllciIsImNvbXBvbmVudERpZE1vdW50IiwiZW52IiwiQ0xBVURFX0NPREVfQUNDRVNTSUJJTElUWSIsIndyaXRlIiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJjbGVhclRpbWVvdXQiLCJjb21wb25lbnREaWRDYXRjaCIsImlzRW5hYmxlZCIsInNldEVuY29kaW5nIiwicmVmIiwiYWRkTGlzdGVuZXIiLCJoYW5kbGVSZWFkYWJsZSIsInNldEltbWVkaWF0ZSIsIlByb21pc2UiLCJhbGwiLCJzZW5kIiwiZmx1c2giLCJ0aGVuIiwiciIsIm5hbWUiLCJyZW1vdmVMaXN0ZW5lciIsInVucmVmIiwiZmx1c2hJbmNvbXBsZXRlIiwiaW5jb21wbGV0ZSIsInJlYWRhYmxlTGVuZ3RoIiwicHJvY2Vzc0lucHV0IiwiaW5wdXQiLCJCdWZmZXIiLCJrZXlzIiwibmV3U3RhdGUiLCJsZW5ndGgiLCJkaXNjcmV0ZVVwZGF0ZXMiLCJwcm9jZXNzS2V5c0luQmF0Y2giLCJtb2RlIiwiY2h1bmsiLCJyZWFkIiwibGlzdGVuZXJzIiwiaW5jbHVkZXMiLCJsZXZlbCIsImhhbmRsZUlucHV0IiwiaGFuZGxlVGVybWluYWxGb2N1cyIsImlzRm9jdXNlZCIsImhhbmRsZVN1c3BlbmQiLCJyYXdNb2RlQ291bnRCZWZvcmVTdXNwZW5kIiwiZW1pdCIsInJlc3VtZUhhbmRsZXIiLCJpIiwib24iLCJraWxsIiwicGlkIiwiYXBwIiwiaXRlbXMiLCJfdW51c2VkMSIsIl91bnVzZWQyIiwic29tZSIsImtpbmQiLCJidXR0b24iLCJpdGVtIiwib25SZXNwb25zZSIsInJlc3BvbnNlIiwiaGFuZGxlTW91c2VFdmVudCIsInNlcXVlbmNlIiwiZXZlbnQiLCJpc0RyYWdnaW5nIiwiY3RybCIsIm0iLCJzZWwiLCJiYXNlQnV0dG9uIiwiYWN0aW9uIiwibmVhckxhc3QiLCJNYXRoIiwiYWJzIiwibGFzdFByZXNzSGFkQWx0IiwiYW5jaG9yIiwiVEVSTV9QUk9HUkFNIl0sInNvdXJjZXMiOlsiQXBwLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgUHVyZUNvbXBvbmVudCwgdHlwZSBSZWFjdE5vZGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVwZGF0ZUxhc3RJbnRlcmFjdGlvblRpbWUgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi8uLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IHN0b3BDYXB0dXJpbmdFYXJseUlucHV0IH0gZnJvbSAnLi4vLi4vdXRpbHMvZWFybHlJbnB1dC5qcydcbmltcG9ydCB7IGlzRW52VHJ1dGh5IH0gZnJvbSAnLi4vLi4vdXRpbHMvZW52VXRpbHMuanMnXG5pbXBvcnQgeyBpc01vdXNlQ2xpY2tzRGlzYWJsZWQgfSBmcm9tICcuLi8uLi91dGlscy9mdWxsc2NyZWVuLmpzJ1xuaW1wb3J0IHsgbG9nRXJyb3IgfSBmcm9tICcuLi8uLi91dGlscy9sb2cuanMnXG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICcuLi9ldmVudHMvZW1pdHRlci5qcydcbmltcG9ydCB7IElucHV0RXZlbnQgfSBmcm9tICcuLi9ldmVudHMvaW5wdXQtZXZlbnQuanMnXG5pbXBvcnQgeyBUZXJtaW5hbEZvY3VzRXZlbnQgfSBmcm9tICcuLi9ldmVudHMvdGVybWluYWwtZm9jdXMtZXZlbnQuanMnXG5pbXBvcnQge1xuICBJTklUSUFMX1NUQVRFLFxuICB0eXBlIFBhcnNlZElucHV0LFxuICB0eXBlIFBhcnNlZEtleSxcbiAgdHlwZSBQYXJzZWRNb3VzZSxcbiAgcGFyc2VNdWx0aXBsZUtleXByZXNzZXMsXG59IGZyb20gJy4uL3BhcnNlLWtleXByZXNzLmpzJ1xuaW1wb3J0IHJlY29uY2lsZXIgZnJvbSAnLi4vcmVjb25jaWxlci5qcydcbmltcG9ydCB7XG4gIGZpbmlzaFNlbGVjdGlvbixcbiAgaGFzU2VsZWN0aW9uLFxuICB0eXBlIFNlbGVjdGlvblN0YXRlLFxuICBzdGFydFNlbGVjdGlvbixcbn0gZnJvbSAnLi4vc2VsZWN0aW9uLmpzJ1xuaW1wb3J0IHtcbiAgaXNYdGVybUpzLFxuICBzZXRYdHZlcnNpb25OYW1lLFxuICBzdXBwb3J0c0V4dGVuZGVkS2V5cyxcbn0gZnJvbSAnLi4vdGVybWluYWwuanMnXG5pbXBvcnQge1xuICBnZXRUZXJtaW5hbEZvY3VzZWQsXG4gIHNldFRlcm1pbmFsRm9jdXNlZCxcbn0gZnJvbSAnLi4vdGVybWluYWwtZm9jdXMtc3RhdGUuanMnXG5pbXBvcnQgeyBUZXJtaW5hbFF1ZXJpZXIsIHh0dmVyc2lvbiB9IGZyb20gJy4uL3Rlcm1pbmFsLXF1ZXJpZXIuanMnXG5pbXBvcnQge1xuICBESVNBQkxFX0tJVFRZX0tFWUJPQVJELFxuICBESVNBQkxFX01PRElGWV9PVEhFUl9LRVlTLFxuICBFTkFCTEVfS0lUVFlfS0VZQk9BUkQsXG4gIEVOQUJMRV9NT0RJRllfT1RIRVJfS0VZUyxcbiAgRk9DVVNfSU4sXG4gIEZPQ1VTX09VVCxcbn0gZnJvbSAnLi4vdGVybWlvL2NzaS5qcydcbmltcG9ydCB7XG4gIERCUCxcbiAgREZFLFxuICBESVNBQkxFX01PVVNFX1RSQUNLSU5HLFxuICBFQlAsXG4gIEVGRSxcbiAgSElERV9DVVJTT1IsXG4gIFNIT1dfQ1VSU09SLFxufSBmcm9tICcuLi90ZXJtaW8vZGVjLmpzJ1xuaW1wb3J0IEFwcENvbnRleHQgZnJvbSAnLi9BcHBDb250ZXh0LmpzJ1xuaW1wb3J0IHsgQ2xvY2tQcm92aWRlciB9IGZyb20gJy4vQ2xvY2tDb250ZXh0LmpzJ1xuaW1wb3J0IEN1cnNvckRlY2xhcmF0aW9uQ29udGV4dCwge1xuICB0eXBlIEN1cnNvckRlY2xhcmF0aW9uU2V0dGVyLFxufSBmcm9tICcuL0N1cnNvckRlY2xhcmF0aW9uQ29udGV4dC5qcydcbmltcG9ydCBFcnJvck92ZXJ2aWV3IGZyb20gJy4vRXJyb3JPdmVydmlldy5qcydcbmltcG9ydCBTdGRpbkNvbnRleHQgZnJvbSAnLi9TdGRpbkNvbnRleHQuanMnXG5pbXBvcnQgeyBUZXJtaW5hbEZvY3VzUHJvdmlkZXIgfSBmcm9tICcuL1Rlcm1pbmFsRm9jdXNDb250ZXh0LmpzJ1xuaW1wb3J0IHsgVGVybWluYWxTaXplQ29udGV4dCB9IGZyb20gJy4vVGVybWluYWxTaXplQ29udGV4dC5qcydcblxuLy8gUGxhdGZvcm1zIHRoYXQgc3VwcG9ydCBVbml4LXN0eWxlIHByb2Nlc3Mgc3VzcGVuc2lvbiAoU0lHU1RPUC9TSUdDT05UKVxuY29uc3QgU1VQUE9SVFNfU1VTUEVORCA9IHByb2Nlc3MucGxhdGZvcm0gIT09ICd3aW4zMidcblxuLy8gQWZ0ZXIgdGhpcyBtYW55IG1pbGxpc2Vjb25kcyBvZiBzdGRpbiBzaWxlbmNlLCB0aGUgbmV4dCBjaHVuayB0cmlnZ2Vyc1xuLy8gYSB0ZXJtaW5hbCBtb2RlIHJlLWFzc2VydCAobW91c2UgdHJhY2tpbmcpLiBDYXRjaGVzIHRtdXggZGV0YWNo4oaSYXR0YWNoLFxuLy8gc3NoIHJlY29ubmVjdCwgYW5kIGxhcHRvcCB3YWtlIOKAlCB0aGUgdGVybWluYWwgcmVzZXRzIERFQyBwcml2YXRlIG1vZGVzXG4vLyBidXQgbm8gc2lnbmFsIHJlYWNoZXMgdXMuIDVzIGlzIHdlbGwgYWJvdmUgbm9ybWFsIGludGVyLWtleXN0cm9rZSBnYXBzXG4vLyBidXQgc2hvcnQgZW5vdWdoIHRoYXQgdGhlIGZpcnN0IHNjcm9sbCBhZnRlciByZWF0dGFjaCB3b3Jrcy5cbmNvbnN0IFNURElOX1JFU1VNRV9HQVBfTVMgPSA1MDAwXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHJlYWRvbmx5IGNoaWxkcmVuOiBSZWFjdE5vZGVcbiAgcmVhZG9ubHkgc3RkaW46IE5vZGVKUy5SZWFkU3RyZWFtXG4gIHJlYWRvbmx5IHN0ZG91dDogTm9kZUpTLldyaXRlU3RyZWFtXG4gIHJlYWRvbmx5IHN0ZGVycjogTm9kZUpTLldyaXRlU3RyZWFtXG4gIHJlYWRvbmx5IGV4aXRPbkN0cmxDOiBib29sZWFuXG4gIHJlYWRvbmx5IG9uRXhpdDogKGVycm9yPzogRXJyb3IpID0+IHZvaWRcbiAgcmVhZG9ubHkgdGVybWluYWxDb2x1bW5zOiBudW1iZXJcbiAgcmVhZG9ubHkgdGVybWluYWxSb3dzOiBudW1iZXJcbiAgLy8gVGV4dCBzZWxlY3Rpb24gc3RhdGUuIEFwcCBtdXRhdGVzIHRoaXMgZGlyZWN0bHkgZnJvbSBtb3VzZSBldmVudHNcbiAgLy8gYW5kIGNhbGxzIG9uU2VsZWN0aW9uQ2hhbmdlIHRvIHRyaWdnZXIgYSByZXBhaW50LiBNb3VzZSBldmVudHMgb25seVxuICAvLyBhcnJpdmUgd2hlbiA8QWx0ZXJuYXRlU2NyZWVuPiAob3Igc2ltaWxhcikgZW5hYmxlcyBtb3VzZSB0cmFja2luZyxcbiAgLy8gc28gdGhlIGhhbmRsZXIgaXMgYWx3YXlzIHdpcmVkIGJ1dCBkb3JtYW50IHVudGlsIHRyYWNraW5nIGlzIG9uLlxuICByZWFkb25seSBzZWxlY3Rpb246IFNlbGVjdGlvblN0YXRlXG4gIHJlYWRvbmx5IG9uU2VsZWN0aW9uQ2hhbmdlOiAoKSA9PiB2b2lkXG4gIC8vIERpc3BhdGNoIGEgY2xpY2sgYXQgKGNvbCwgcm93KSDigJQgaGl0LXRlc3RzIHRoZSBET00gdHJlZSBhbmQgYnViYmxlc1xuICAvLyBvbkNsaWNrIGhhbmRsZXJzLiBSZXR1cm5zIHRydWUgaWYgYSBET00gaGFuZGxlciBjb25zdW1lZCB0aGUgY2xpY2suXG4gIC8vIE5vLW9wIChyZXR1cm5zIGZhbHNlKSBvdXRzaWRlIGZ1bGxzY3JlZW4gbW9kZSAoSW5rLmRpc3BhdGNoQ2xpY2tcbiAgLy8gZ2F0ZXMgb24gYWx0U2NyZWVuQWN0aXZlKS5cbiAgcmVhZG9ubHkgb25DbGlja0F0OiAoY29sOiBudW1iZXIsIHJvdzogbnVtYmVyKSA9PiBib29sZWFuXG4gIC8vIERpc3BhdGNoIGhvdmVyIChvbk1vdXNlRW50ZXIvb25Nb3VzZUxlYXZlKSBhcyB0aGUgcG9pbnRlciBtb3ZlcyBvdmVyXG4gIC8vIERPTSBlbGVtZW50cy4gQ2FsbGVkIGZvciBtb2RlLTEwMDMgbW90aW9uIGV2ZW50cyB3aXRoIG5vIGJ1dHRvbiBoZWxkLlxuICAvLyBOby1vcCBvdXRzaWRlIGZ1bGxzY3JlZW4gKEluay5kaXNwYXRjaEhvdmVyIGdhdGVzIG9uIGFsdFNjcmVlbkFjdGl2ZSkuXG4gIHJlYWRvbmx5IG9uSG92ZXJBdDogKGNvbDogbnVtYmVyLCByb3c6IG51bWJlcikgPT4gdm9pZFxuICAvLyBMb29rIHVwIHRoZSBPU0MgOCBoeXBlcmxpbmsgYXQgKGNvbCwgcm93KSBzeW5jaHJvbm91c2x5IGF0IGNsaWNrXG4gIC8vIHRpbWUuIFJldHVybnMgdGhlIFVSTCBvciB1bmRlZmluZWQuIFRoZSBicm93c2VyLW9wZW4gaXMgZGVmZXJyZWQgYnlcbiAgLy8gTVVMVElfQ0xJQ0tfVElNRU9VVF9NUyBzbyBkb3VibGUtY2xpY2sgY2FuIGNhbmNlbCBpdC5cbiAgcmVhZG9ubHkgZ2V0SHlwZXJsaW5rQXQ6IChjb2w6IG51bWJlciwgcm93OiBudW1iZXIpID0+IHN0cmluZyB8IHVuZGVmaW5lZFxuICAvLyBPcGVuIGEgaHlwZXJsaW5rIFVSTCBpbiB0aGUgYnJvd3Nlci4gQ2FsbGVkIGFmdGVyIHRoZSB0aW1lciBmaXJlcy5cbiAgcmVhZG9ubHkgb25PcGVuSHlwZXJsaW5rOiAodXJsOiBzdHJpbmcpID0+IHZvaWRcbiAgLy8gQ2FsbGVkIG9uIGRvdWJsZS90cmlwbGUtY2xpY2sgUFJFU1MgYXQgKGNvbCwgcm93KS4gY291bnQ9MiBzZWxlY3RzXG4gIC8vIHRoZSB3b3JkIHVuZGVyIHRoZSBjdXJzb3I7IGNvdW50PTMgc2VsZWN0cyB0aGUgbGluZS4gSW5rIHJlYWRzIHRoZVxuICAvLyBzY3JlZW4gYnVmZmVyIHRvIGZpbmQgd29yZC9saW5lIGJvdW5kYXJpZXMgYW5kIG11dGF0ZXMgc2VsZWN0aW9uLFxuICAvLyBzZXR0aW5nIGlzRHJhZ2dpbmc9dHJ1ZSBzbyBhIHN1YnNlcXVlbnQgZHJhZyBleHRlbmRzIGJ5IHdvcmQvbGluZS5cbiAgcmVhZG9ubHkgb25NdWx0aUNsaWNrOiAoY29sOiBudW1iZXIsIHJvdzogbnVtYmVyLCBjb3VudDogMiB8IDMpID0+IHZvaWRcbiAgLy8gQ2FsbGVkIG9uIGRyYWctbW90aW9uLiBNb2RlLWF3YXJlOiBjaGFyIG1vZGUgdXBkYXRlcyBmb2N1cyB0byB0aGVcbiAgLy8gZXhhY3QgY2VsbDsgd29yZC9saW5lIG1vZGUgc25hcHMgdG8gd29yZC9saW5lIGJvdW5kYXJpZXMuIE5lZWRzXG4gIC8vIHNjcmVlbi1idWZmZXIgYWNjZXNzICh3b3JkIGJvdW5kYXJpZXMpIHNvIGxpdmVzIG9uIEluaywgbm90IGhlcmUuXG4gIHJlYWRvbmx5IG9uU2VsZWN0aW9uRHJhZzogKGNvbDogbnVtYmVyLCByb3c6IG51bWJlcikgPT4gdm9pZFxuICAvLyBDYWxsZWQgd2hlbiBzdGRpbiBkYXRhIGFycml2ZXMgYWZ0ZXIgYSA+U1RESU5fUkVTVU1FX0dBUF9NUyBnYXAuXG4gIC8vIEluayByZS1hc3NlcnRzIHRlcm1pbmFsIG1vZGVzOiBleHRlbmRlZCBrZXkgcmVwb3J0aW5nLCBhbmQgKHdoZW4gaW5cbiAgLy8gZnVsbHNjcmVlbikgcmUtZW50ZXJzIGFsdC1zY3JlZW4gKyBtb3VzZSB0cmFja2luZy4gSWRlbXBvdGVudCBvbiB0aGVcbiAgLy8gdGVybWluYWwgc2lkZS4gT3B0aW9uYWwgc28gdGVzdGluZy50c3ggZG9lc24ndCBuZWVkIHRvIHN0dWIgaXQuXG4gIHJlYWRvbmx5IG9uU3RkaW5SZXN1bWU/OiAoKSA9PiB2b2lkXG4gIC8vIFJlY2VpdmVzIHRoZSBkZWNsYXJlZCBuYXRpdmUtY3Vyc29yIHBvc2l0aW9uIGZyb20gdXNlRGVjbGFyZWRDdXJzb3JcbiAgLy8gc28gaW5rLnRzeCBjYW4gcGFyayB0aGUgdGVybWluYWwgY3Vyc29yIHRoZXJlIGFmdGVyIGVhY2ggZnJhbWUuXG4gIC8vIEVuYWJsZXMgSU1FIGNvbXBvc2l0aW9uIGF0IHRoZSBpbnB1dCBjYXJldCBhbmQgbGV0cyBzY3JlZW4gcmVhZGVycyAvXG4gIC8vIG1hZ25pZmllcnMgdHJhY2sgdGhlIGlucHV0LiBPcHRpb25hbCBzbyB0ZXN0aW5nLnRzeCBkb2Vzbid0IHN0dWIgaXQuXG4gIHJlYWRvbmx5IG9uQ3Vyc29yRGVjbGFyYXRpb24/OiBDdXJzb3JEZWNsYXJhdGlvblNldHRlclxuICAvLyBEaXNwYXRjaCBhIGtleWJvYXJkIGV2ZW50IHRocm91Z2ggdGhlIERPTSB0cmVlLiBDYWxsZWQgZm9yIGVhY2hcbiAgLy8gcGFyc2VkIGtleSBhbG9uZ3NpZGUgdGhlIGxlZ2FjeSBFdmVudEVtaXR0ZXIgcGF0aC5cbiAgcmVhZG9ubHkgZGlzcGF0Y2hLZXlib2FyZEV2ZW50OiAocGFyc2VkS2V5OiBQYXJzZWRLZXkpID0+IHZvaWRcbn1cblxuLy8gTXVsdGktY2xpY2sgZGV0ZWN0aW9uIHRocmVzaG9sZHMuIDUwMG1zIGlzIHRoZSBtYWNPUyBkZWZhdWx0OyBhIHNtYWxsXG4vLyBwb3NpdGlvbiB0b2xlcmFuY2UgYWxsb3dzIGZvciB0cmFja3BhZCBqaXR0ZXIgYmV0d2VlbiBjbGlja3MuXG5jb25zdCBNVUxUSV9DTElDS19USU1FT1VUX01TID0gNTAwXG5jb25zdCBNVUxUSV9DTElDS19ESVNUQU5DRSA9IDFcblxudHlwZSBTdGF0ZSA9IHtcbiAgcmVhZG9ubHkgZXJyb3I/OiBFcnJvclxufVxuXG4vLyBSb290IGNvbXBvbmVudCBmb3IgYWxsIEluayBhcHBzXG4vLyBJdCByZW5kZXJzIHN0ZGluIGFuZCBzdGRvdXQgY29udGV4dHMsIHNvIHRoYXQgY2hpbGRyZW4gY2FuIGFjY2VzcyB0aGVtIGlmIG5lZWRlZFxuLy8gSXQgYWxzbyBoYW5kbGVzIEN0cmwrQyBleGl0aW5nIGFuZCBjdXJzb3IgdmlzaWJpbGl0eVxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQXBwIGV4dGVuZHMgUHVyZUNvbXBvbmVudDxQcm9wcywgU3RhdGU+IHtcbiAgc3RhdGljIGRpc3BsYXlOYW1lID0gJ0ludGVybmFsQXBwJ1xuXG4gIHN0YXRpYyBnZXREZXJpdmVkU3RhdGVGcm9tRXJyb3IoZXJyb3I6IEVycm9yKSB7XG4gICAgcmV0dXJuIHsgZXJyb3IgfVxuICB9XG5cbiAgb3ZlcnJpZGUgc3RhdGUgPSB7XG4gICAgZXJyb3I6IHVuZGVmaW5lZCxcbiAgfVxuXG4gIC8vIENvdW50IGhvdyBtYW55IGNvbXBvbmVudHMgZW5hYmxlZCByYXcgbW9kZSB0byBhdm9pZCBkaXNhYmxpbmdcbiAgLy8gcmF3IG1vZGUgdW50aWwgYWxsIGNvbXBvbmVudHMgZG9uJ3QgbmVlZCBpdCBhbnltb3JlXG4gIHJhd01vZGVFbmFibGVkQ291bnQgPSAwXG5cbiAgaW50ZXJuYWxfZXZlbnRFbWl0dGVyID0gbmV3IEV2ZW50RW1pdHRlcigpXG4gIGtleVBhcnNlU3RhdGUgPSBJTklUSUFMX1NUQVRFXG4gIC8vIFRpbWVyIGZvciBmbHVzaGluZyBpbmNvbXBsZXRlIGVzY2FwZSBzZXF1ZW5jZXNcbiAgaW5jb21wbGV0ZUVzY2FwZVRpbWVyOiBOb2RlSlMuVGltZW91dCB8IG51bGwgPSBudWxsXG4gIC8vIFRpbWVvdXQgZHVyYXRpb25zIGZvciBpbmNvbXBsZXRlIHNlcXVlbmNlcyAobXMpXG4gIHJlYWRvbmx5IE5PUk1BTF9USU1FT1VUID0gNTAgLy8gU2hvcnQgdGltZW91dCBmb3IgcmVndWxhciBlc2Mgc2VxdWVuY2VzXG4gIHJlYWRvbmx5IFBBU1RFX1RJTUVPVVQgPSA1MDAgLy8gTG9uZ2VyIHRpbWVvdXQgZm9yIHBhc3RlIG9wZXJhdGlvbnNcblxuICAvLyBUZXJtaW5hbCBxdWVyeS9yZXNwb25zZSBkaXNwYXRjaC4gUmVzcG9uc2VzIGFycml2ZSBvbiBzdGRpbiAocGFyc2VkXG4gIC8vIG91dCBieSBwYXJzZS1rZXlwcmVzcykgYW5kIGFyZSByb3V0ZWQgdG8gcGVuZGluZyBwcm9taXNlIHJlc29sdmVycy5cbiAgcXVlcmllciA9IG5ldyBUZXJtaW5hbFF1ZXJpZXIodGhpcy5wcm9wcy5zdGRvdXQpXG5cbiAgLy8gTXVsdGktY2xpY2sgdHJhY2tpbmcgZm9yIGRvdWJsZS90cmlwbGUtY2xpY2sgdGV4dCBzZWxlY3Rpb24uIEEgY2xpY2tcbiAgLy8gd2l0aGluIE1VTFRJX0NMSUNLX1RJTUVPVVRfTVMgYW5kIE1VTFRJX0NMSUNLX0RJU1RBTkNFIG9mIHRoZSBwcmV2aW91c1xuICAvLyBjbGljayBpbmNyZW1lbnRzIGNsaWNrQ291bnQ7IG90aGVyd2lzZSBpdCByZXNldHMgdG8gMS5cbiAgbGFzdENsaWNrVGltZSA9IDBcbiAgbGFzdENsaWNrQ29sID0gLTFcbiAgbGFzdENsaWNrUm93ID0gLTFcbiAgY2xpY2tDb3VudCA9IDBcbiAgLy8gRGVmZXJyZWQgaHlwZXJsaW5rLW9wZW4gdGltZXIg4oCUIGNhbmNlbGxlZCBpZiBhIHNlY29uZCBjbGljayBhcnJpdmVzXG4gIC8vIHdpdGhpbiBNVUxUSV9DTElDS19USU1FT1VUX01TIChzbyBkb3VibGUtY2xpY2tpbmcgYSBoeXBlcmxpbmsgc2VsZWN0c1xuICAvLyB0aGUgd29yZCB3aXRob3V0IGFsc28gb3BlbmluZyB0aGUgYnJvd3NlcikuIERPTSBvbkNsaWNrIGRpc3BhdGNoIGlzXG4gIC8vIE5PVCBkZWZlcnJlZCDigJQgaXQgcmV0dXJucyB0cnVlIGZyb20gb25DbGlja0F0IGFuZCBza2lwcyB0aGlzIHRpbWVyLlxuICBwZW5kaW5nSHlwZXJsaW5rVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgbnVsbCA9IG51bGxcbiAgLy8gTGFzdCBtb2RlLTEwMDMgbW90aW9uIHBvc2l0aW9uLiBUZXJtaW5hbHMgYWxyZWFkeSBkZWR1cGUgdG8gY2VsbFxuICAvLyBncmFudWxhcml0eSBidXQgdGhpcyBhbHNvIGxldHMgdXMgc2tpcCBkaXNwYXRjaEhvdmVyIGVudGlyZWx5IG9uXG4gIC8vIHJlcGVhdCBldmVudHMgKGRyYWctdGhlbi1yZWxlYXNlIGF0IHNhbWUgY2VsbCwgZXRjLikuXG4gIGxhc3RIb3ZlckNvbCA9IC0xXG4gIGxhc3RIb3ZlclJvdyA9IC0xXG5cbiAgLy8gVGltZXN0YW1wIG9mIGxhc3Qgc3RkaW4gY2h1bmsuIFVzZWQgdG8gZGV0ZWN0IGxvbmcgZ2FwcyAodG11eCBhdHRhY2gsXG4gIC8vIHNzaCByZWNvbm5lY3QsIGxhcHRvcCB3YWtlKSBhbmQgdHJpZ2dlciB0ZXJtaW5hbCBtb2RlIHJlLWFzc2VydC5cbiAgLy8gSW5pdGlhbGl6ZWQgdG8gbm93IHNvIHN0YXJ0dXAgZG9lc24ndCBmYWxzZS10cmlnZ2VyLlxuICBsYXN0U3RkaW5UaW1lID0gRGF0ZS5ub3coKVxuXG4gIC8vIERldGVybWluZXMgaWYgVFRZIGlzIHN1cHBvcnRlZCBvbiB0aGUgcHJvdmlkZWQgc3RkaW5cbiAgaXNSYXdNb2RlU3VwcG9ydGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnByb3BzLnN0ZGluLmlzVFRZXG4gIH1cblxuICBvdmVycmlkZSByZW5kZXIoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxUZXJtaW5hbFNpemVDb250ZXh0LlByb3ZpZGVyXG4gICAgICAgIHZhbHVlPXt7XG4gICAgICAgICAgY29sdW1uczogdGhpcy5wcm9wcy50ZXJtaW5hbENvbHVtbnMsXG4gICAgICAgICAgcm93czogdGhpcy5wcm9wcy50ZXJtaW5hbFJvd3MsXG4gICAgICAgIH19XG4gICAgICA+XG4gICAgICAgIDxBcHBDb250ZXh0LlByb3ZpZGVyXG4gICAgICAgICAgdmFsdWU9e3tcbiAgICAgICAgICAgIGV4aXQ6IHRoaXMuaGFuZGxlRXhpdCxcbiAgICAgICAgICB9fVxuICAgICAgICA+XG4gICAgICAgICAgPFN0ZGluQ29udGV4dC5Qcm92aWRlclxuICAgICAgICAgICAgdmFsdWU9e3tcbiAgICAgICAgICAgICAgc3RkaW46IHRoaXMucHJvcHMuc3RkaW4sXG4gICAgICAgICAgICAgIHNldFJhd01vZGU6IHRoaXMuaGFuZGxlU2V0UmF3TW9kZSxcbiAgICAgICAgICAgICAgaXNSYXdNb2RlU3VwcG9ydGVkOiB0aGlzLmlzUmF3TW9kZVN1cHBvcnRlZCgpLFxuXG4gICAgICAgICAgICAgIGludGVybmFsX2V4aXRPbkN0cmxDOiB0aGlzLnByb3BzLmV4aXRPbkN0cmxDLFxuXG4gICAgICAgICAgICAgIGludGVybmFsX2V2ZW50RW1pdHRlcjogdGhpcy5pbnRlcm5hbF9ldmVudEVtaXR0ZXIsXG4gICAgICAgICAgICAgIGludGVybmFsX3F1ZXJpZXI6IHRoaXMucXVlcmllcixcbiAgICAgICAgICAgIH19XG4gICAgICAgICAgPlxuICAgICAgICAgICAgPFRlcm1pbmFsRm9jdXNQcm92aWRlcj5cbiAgICAgICAgICAgICAgPENsb2NrUHJvdmlkZXI+XG4gICAgICAgICAgICAgICAgPEN1cnNvckRlY2xhcmF0aW9uQ29udGV4dC5Qcm92aWRlclxuICAgICAgICAgICAgICAgICAgdmFsdWU9e3RoaXMucHJvcHMub25DdXJzb3JEZWNsYXJhdGlvbiA/PyAoKCkgPT4ge30pfVxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgIHt0aGlzLnN0YXRlLmVycm9yID8gKFxuICAgICAgICAgICAgICAgICAgICA8RXJyb3JPdmVydmlldyBlcnJvcj17dGhpcy5zdGF0ZS5lcnJvciBhcyBFcnJvcn0gLz5cbiAgICAgICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgICAgIHRoaXMucHJvcHMuY2hpbGRyZW5cbiAgICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgICAgPC9DdXJzb3JEZWNsYXJhdGlvbkNvbnRleHQuUHJvdmlkZXI+XG4gICAgICAgICAgICAgIDwvQ2xvY2tQcm92aWRlcj5cbiAgICAgICAgICAgIDwvVGVybWluYWxGb2N1c1Byb3ZpZGVyPlxuICAgICAgICAgIDwvU3RkaW5Db250ZXh0LlByb3ZpZGVyPlxuICAgICAgICA8L0FwcENvbnRleHQuUHJvdmlkZXI+XG4gICAgICA8L1Rlcm1pbmFsU2l6ZUNvbnRleHQuUHJvdmlkZXI+XG4gICAgKVxuICB9XG5cbiAgb3ZlcnJpZGUgY29tcG9uZW50RGlkTW91bnQoKSB7XG4gICAgLy8gSW4gYWNjZXNzaWJpbGl0eSBtb2RlLCBrZWVwIHRoZSBuYXRpdmUgY3Vyc29yIHZpc2libGUgZm9yIHNjcmVlbiBtYWduaWZpZXJzIGFuZCBvdGhlciB0b29sc1xuICAgIGlmIChcbiAgICAgIHRoaXMucHJvcHMuc3Rkb3V0LmlzVFRZICYmXG4gICAgICAhaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfQUNDRVNTSUJJTElUWSlcbiAgICApIHtcbiAgICAgIHRoaXMucHJvcHMuc3Rkb3V0LndyaXRlKEhJREVfQ1VSU09SKVxuICAgIH1cbiAgfVxuXG4gIG92ZXJyaWRlIGNvbXBvbmVudFdpbGxVbm1vdW50KCkge1xuICAgIGlmICh0aGlzLnByb3BzLnN0ZG91dC5pc1RUWSkge1xuICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoU0hPV19DVVJTT1IpXG4gICAgfVxuXG4gICAgLy8gQ2xlYXIgYW55IHBlbmRpbmcgdGltZXJzXG4gICAgaWYgKHRoaXMuaW5jb21wbGV0ZUVzY2FwZVRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIpXG4gICAgICB0aGlzLmluY29tcGxldGVFc2NhcGVUaW1lciA9IG51bGxcbiAgICB9XG4gICAgaWYgKHRoaXMucGVuZGluZ0h5cGVybGlua1RpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5wZW5kaW5nSHlwZXJsaW5rVGltZXIpXG4gICAgICB0aGlzLnBlbmRpbmdIeXBlcmxpbmtUaW1lciA9IG51bGxcbiAgICB9XG4gICAgLy8gaWdub3JlIGNhbGxpbmcgc2V0UmF3TW9kZSBvbiBhbiBoYW5kbGUgc3RkaW4gaXQgY2Fubm90IGJlIGNhbGxlZFxuICAgIGlmICh0aGlzLmlzUmF3TW9kZVN1cHBvcnRlZCgpKSB7XG4gICAgICB0aGlzLmhhbmRsZVNldFJhd01vZGUoZmFsc2UpXG4gICAgfVxuICB9XG5cbiAgb3ZlcnJpZGUgY29tcG9uZW50RGlkQ2F0Y2goZXJyb3I6IEVycm9yKSB7XG4gICAgdGhpcy5oYW5kbGVFeGl0KGVycm9yKVxuICB9XG5cbiAgaGFuZGxlU2V0UmF3TW9kZSA9IChpc0VuYWJsZWQ6IGJvb2xlYW4pOiB2b2lkID0+IHtcbiAgICBjb25zdCB7IHN0ZGluIH0gPSB0aGlzLnByb3BzXG5cbiAgICBpZiAoIXRoaXMuaXNSYXdNb2RlU3VwcG9ydGVkKCkpIHtcbiAgICAgIGlmIChzdGRpbiA9PT0gcHJvY2Vzcy5zdGRpbikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ1JhdyBtb2RlIGlzIG5vdCBzdXBwb3J0ZWQgb24gdGhlIGN1cnJlbnQgcHJvY2Vzcy5zdGRpbiwgd2hpY2ggSW5rIHVzZXMgYXMgaW5wdXQgc3RyZWFtIGJ5IGRlZmF1bHQuXFxuUmVhZCBhYm91dCBob3cgdG8gcHJldmVudCB0aGlzIGVycm9yIG9uIGh0dHBzOi8vZ2l0aHViLmNvbS92YWRpbWRlbWVkZXMvaW5rLyNpc3Jhd21vZGVzdXBwb3J0ZWQnLFxuICAgICAgICApXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ1JhdyBtb2RlIGlzIG5vdCBzdXBwb3J0ZWQgb24gdGhlIHN0ZGluIHByb3ZpZGVkIHRvIEluay5cXG5SZWFkIGFib3V0IGhvdyB0byBwcmV2ZW50IHRoaXMgZXJyb3Igb24gaHR0cHM6Ly9naXRodWIuY29tL3ZhZGltZGVtZWRlcy9pbmsvI2lzcmF3bW9kZXN1cHBvcnRlZCcsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBzdGRpbi5zZXRFbmNvZGluZygndXRmOCcpXG5cbiAgICBpZiAoaXNFbmFibGVkKSB7XG4gICAgICAvLyBFbnN1cmUgcmF3IG1vZGUgaXMgZW5hYmxlZCBvbmx5IG9uY2VcbiAgICAgIGlmICh0aGlzLnJhd01vZGVFbmFibGVkQ291bnQgPT09IDApIHtcbiAgICAgICAgLy8gU3RvcCBlYXJseSBpbnB1dCBjYXB0dXJlIHJpZ2h0IGJlZm9yZSB3ZSBhZGQgb3VyIG93biByZWFkYWJsZSBoYW5kbGVyLlxuICAgICAgICAvLyBCb3RoIHVzZSB0aGUgc2FtZSBzdGRpbiAncmVhZGFibGUnICsgcmVhZCgpIHBhdHRlcm4sIHNvIHRoZXkgY2FuJ3RcbiAgICAgICAgLy8gY29leGlzdCAtLSBvdXIgaGFuZGxlciB3b3VsZCBkcmFpbiBzdGRpbiBiZWZvcmUgSW5rJ3MgY2FuIHNlZSBpdC5cbiAgICAgICAgLy8gVGhlIGJ1ZmZlcmVkIHRleHQgaXMgcHJlc2VydmVkIGZvciBSRVBMLnRzeCB2aWEgY29uc3VtZUVhcmx5SW5wdXQoKS5cbiAgICAgICAgc3RvcENhcHR1cmluZ0Vhcmx5SW5wdXQoKVxuICAgICAgICBzdGRpbi5yZWYoKVxuICAgICAgICBzdGRpbi5zZXRSYXdNb2RlKHRydWUpXG4gICAgICAgIHN0ZGluLmFkZExpc3RlbmVyKCdyZWFkYWJsZScsIHRoaXMuaGFuZGxlUmVhZGFibGUpXG4gICAgICAgIC8vIEVuYWJsZSBicmFja2V0ZWQgcGFzdGUgbW9kZVxuICAgICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShFQlApXG4gICAgICAgIC8vIEVuYWJsZSB0ZXJtaW5hbCBmb2N1cyByZXBvcnRpbmcgKERFQ1NFVCAxMDA0KVxuICAgICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShFRkUpXG4gICAgICAgIC8vIEVuYWJsZSBleHRlbmRlZCBrZXkgcmVwb3J0aW5nIHNvIGN0cmwrc2hpZnQrPGxldHRlcj4gaXNcbiAgICAgICAgLy8gZGlzdGluZ3Vpc2hhYmxlIGZyb20gY3RybCs8bGV0dGVyPi4gV2Ugd3JpdGUgYm90aCB0aGUga2l0dHkgc3RhY2tcbiAgICAgICAgLy8gcHVzaCAoQ1NJID4xdSkgYW5kIHh0ZXJtIG1vZGlmeU90aGVyS2V5cyBsZXZlbCAyIChDU0kgPjQ7Mm0pIOKAlFxuICAgICAgICAvLyB0ZXJtaW5hbHMgaG9ub3Igd2hpY2hldmVyIHRoZXkgaW1wbGVtZW50ICh0bXV4IG9ubHkgYWNjZXB0cyB0aGVcbiAgICAgICAgLy8gbGF0dGVyKS5cbiAgICAgICAgaWYgKHN1cHBvcnRzRXh0ZW5kZWRLZXlzKCkpIHtcbiAgICAgICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShFTkFCTEVfS0lUVFlfS0VZQk9BUkQpXG4gICAgICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoRU5BQkxFX01PRElGWV9PVEhFUl9LRVlTKVxuICAgICAgICB9XG4gICAgICAgIC8vIFByb2JlIHRlcm1pbmFsIGlkZW50aXR5LiBYVFZFUlNJT04gc3Vydml2ZXMgU1NIIChxdWVyeS9yZXBseSBnb2VzXG4gICAgICAgIC8vIHRocm91Z2ggdGhlIHB0eSksIHVubGlrZSBURVJNX1BST0dSQU0uIFVzZWQgZm9yIHdoZWVsLXNjcm9sbCBiYXNlXG4gICAgICAgIC8vIGRldGVjdGlvbiB3aGVuIGVudiB2YXJzIGFyZSBhYnNlbnQuIEZpcmUtYW5kLWZvcmdldDogdGhlIERBMVxuICAgICAgICAvLyBzZW50aW5lbCBib3VuZHMgdGhlIHJvdW5kLXRyaXAsIGFuZCBpZiB0aGUgdGVybWluYWwgaWdub3JlcyB0aGVcbiAgICAgICAgLy8gcXVlcnksIGZsdXNoKCkgc3RpbGwgcmVzb2x2ZXMgYW5kIG5hbWUgc3RheXMgdW5kZWZpbmVkLlxuICAgICAgICAvLyBEZWZlcnJlZCB0byBuZXh0IHRpY2sgc28gaXQgZmlyZXMgQUZURVIgdGhlIGN1cnJlbnQgc3luY2hyb25vdXNcbiAgICAgICAgLy8gaW5pdCBzZXF1ZW5jZSBjb21wbGV0ZXMg4oCUIGF2b2lkcyBpbnRlcmxlYXZpbmcgd2l0aCBhbHQtc2NyZWVuL21vdXNlXG4gICAgICAgIC8vIHRyYWNraW5nIGVuYWJsZSB3cml0ZXMgdGhhdCBtYXkgaGFwcGVuIGluIHRoZSBzYW1lIHJlbmRlciBjeWNsZS5cbiAgICAgICAgc2V0SW1tZWRpYXRlKCgpID0+IHtcbiAgICAgICAgICB2b2lkIFByb21pc2UuYWxsKFtcbiAgICAgICAgICAgIHRoaXMucXVlcmllci5zZW5kKHh0dmVyc2lvbigpKSxcbiAgICAgICAgICAgIHRoaXMucXVlcmllci5mbHVzaCgpLFxuICAgICAgICAgIF0pLnRoZW4oKFtyXSkgPT4ge1xuICAgICAgICAgICAgaWYgKHIpIHtcbiAgICAgICAgICAgICAgc2V0WHR2ZXJzaW9uTmFtZShyLm5hbWUpXG4gICAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgWFRWRVJTSU9OOiB0ZXJtaW5hbCBpZGVudGlmaWVkIGFzIFwiJHtyLm5hbWV9XCJgKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKCdYVFZFUlNJT046IG5vIHJlcGx5ICh0ZXJtaW5hbCBpZ25vcmVkIHF1ZXJ5KScpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSlcbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgdGhpcy5yYXdNb2RlRW5hYmxlZENvdW50KytcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIERpc2FibGUgcmF3IG1vZGUgb25seSB3aGVuIG5vIGNvbXBvbmVudHMgbGVmdCB0aGF0IGFyZSB1c2luZyBpdFxuICAgIGlmICgtLXRoaXMucmF3TW9kZUVuYWJsZWRDb3VudCA9PT0gMCkge1xuICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoRElTQUJMRV9NT0RJRllfT1RIRVJfS0VZUylcbiAgICAgIHRoaXMucHJvcHMuc3Rkb3V0LndyaXRlKERJU0FCTEVfS0lUVFlfS0VZQk9BUkQpXG4gICAgICAvLyBEaXNhYmxlIHRlcm1pbmFsIGZvY3VzIHJlcG9ydGluZyAoREVDU0VUIDEwMDQpXG4gICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShERkUpXG4gICAgICAvLyBEaXNhYmxlIGJyYWNrZXRlZCBwYXN0ZSBtb2RlXG4gICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShEQlApXG4gICAgICBzdGRpbi5zZXRSYXdNb2RlKGZhbHNlKVxuICAgICAgc3RkaW4ucmVtb3ZlTGlzdGVuZXIoJ3JlYWRhYmxlJywgdGhpcy5oYW5kbGVSZWFkYWJsZSlcbiAgICAgIHN0ZGluLnVucmVmKClcbiAgICB9XG4gIH1cblxuICAvLyBIZWxwZXIgdG8gZmx1c2ggaW5jb21wbGV0ZSBlc2NhcGUgc2VxdWVuY2VzXG4gIGZsdXNoSW5jb21wbGV0ZSA9ICgpOiB2b2lkID0+IHtcbiAgICAvLyBDbGVhciB0aGUgdGltZXIgcmVmZXJlbmNlXG4gICAgdGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIgPSBudWxsXG5cbiAgICAvLyBPbmx5IHByb2NlZWQgaWYgd2UgaGF2ZSBpbmNvbXBsZXRlIHNlcXVlbmNlc1xuICAgIGlmICghdGhpcy5rZXlQYXJzZVN0YXRlLmluY29tcGxldGUpIHJldHVyblxuXG4gICAgLy8gRnVsbHNjcmVlbjogaWYgc3RkaW4gaGFzIGRhdGEgd2FpdGluZywgaXQncyBhbG1vc3QgY2VydGFpbmx5IHRoZVxuICAgIC8vIGNvbnRpbnVhdGlvbiBvZiB0aGUgYnVmZmVyZWQgc2VxdWVuY2UgKGUuZy4gYFs8NjQ7NzQ7MTZNYCBhZnRlciBhXG4gICAgLy8gbG9uZSBFU0MpLiBOb2RlJ3MgZXZlbnQgbG9vcCBydW5zIHRoZSB0aW1lcnMgcGhhc2UgYmVmb3JlIHRoZSBwb2xsXG4gICAgLy8gcGhhc2UsIHNvIHdoZW4gYSBoZWF2eSByZW5kZXIgYmxvY2tzIHRoZSBsb29wIHBhc3QgNTBtcywgdGhpcyB0aW1lclxuICAgIC8vIGZpcmVzIGJlZm9yZSB0aGUgcXVldWVkIHJlYWRhYmxlIGV2ZW50IGV2ZW4gdGhvdWdoIHRoZSBieXRlcyBhcmVcbiAgICAvLyBhbHJlYWR5IGJ1ZmZlcmVkLiBSZS1hcm0gaW5zdGVhZCBvZiBmbHVzaGluZzogaGFuZGxlUmVhZGFibGUgd2lsbFxuICAgIC8vIGRyYWluIHN0ZGluIG5leHQgYW5kIGNsZWFyIHRoaXMgdGltZXIuIFByZXZlbnRzIGJvdGggdGhlIHNwdXJpb3VzXG4gICAgLy8gRXNjYXBlIGtleSBhbmQgdGhlIGxvc3Qgc2Nyb2xsIGV2ZW50LlxuICAgIGlmICh0aGlzLnByb3BzLnN0ZGluLnJlYWRhYmxlTGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIgPSBzZXRUaW1lb3V0KFxuICAgICAgICB0aGlzLmZsdXNoSW5jb21wbGV0ZSxcbiAgICAgICAgdGhpcy5OT1JNQUxfVElNRU9VVCxcbiAgICAgIClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIFByb2Nlc3MgaW5jb21wbGV0ZSBhcyBhIGZsdXNoIG9wZXJhdGlvbiAoaW5wdXQ9bnVsbClcbiAgICAvLyBUaGlzIHJldXNlcyBhbGwgZXhpc3RpbmcgcGFyc2luZyBsb2dpY1xuICAgIHRoaXMucHJvY2Vzc0lucHV0KG51bGwpXG4gIH1cblxuICAvLyBQcm9jZXNzIGlucHV0IHRocm91Z2ggdGhlIHBhcnNlciBhbmQgaGFuZGxlIHRoZSByZXN1bHRzXG4gIHByb2Nlc3NJbnB1dCA9IChpbnB1dDogc3RyaW5nIHwgQnVmZmVyIHwgbnVsbCk6IHZvaWQgPT4ge1xuICAgIC8vIFBhcnNlIGlucHV0IHVzaW5nIG91ciBzdGF0ZSBtYWNoaW5lXG4gICAgY29uc3QgW2tleXMsIG5ld1N0YXRlXSA9IHBhcnNlTXVsdGlwbGVLZXlwcmVzc2VzKHRoaXMua2V5UGFyc2VTdGF0ZSwgaW5wdXQpXG4gICAgdGhpcy5rZXlQYXJzZVN0YXRlID0gbmV3U3RhdGVcblxuICAgIC8vIFByb2Nlc3MgQUxMIGtleXMgaW4gYSBTSU5HTEUgZGlzY3JldGVVcGRhdGVzIGNhbGwgdG8gcHJldmVudFxuICAgIC8vIFwiTWF4aW11bSB1cGRhdGUgZGVwdGggZXhjZWVkZWRcIiBlcnJvciB3aGVuIG1hbnkga2V5cyBhcnJpdmUgYXQgb25jZVxuICAgIC8vIChlLmcuLCBmcm9tIHBhc3RlIG9wZXJhdGlvbnMgb3IgaG9sZGluZyBrZXlzIHJhcGlkbHkpLlxuICAgIC8vIFRoaXMgYmF0Y2hlcyBhbGwgc3RhdGUgdXBkYXRlcyBmcm9tIGhhbmRsZUlucHV0IGFuZCBhbGwgdXNlSW5wdXRcbiAgICAvLyBsaXN0ZW5lcnMgdG9nZXRoZXIgd2l0aGluIG9uZSBoaWdoLXByaW9yaXR5IHVwZGF0ZSBjb250ZXh0LlxuICAgIGlmIChrZXlzLmxlbmd0aCA+IDApIHtcbiAgICAgIHJlY29uY2lsZXIuZGlzY3JldGVVcGRhdGVzKFxuICAgICAgICBwcm9jZXNzS2V5c0luQmF0Y2gsXG4gICAgICAgIHRoaXMsXG4gICAgICAgIGtleXMsXG4gICAgICAgIHVuZGVmaW5lZCxcbiAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgKVxuICAgIH1cblxuICAgIC8vIElmIHdlIGhhdmUgaW5jb21wbGV0ZSBlc2NhcGUgc2VxdWVuY2VzLCBzZXQgYSB0aW1lciB0byBmbHVzaCB0aGVtXG4gICAgaWYgKHRoaXMua2V5UGFyc2VTdGF0ZS5pbmNvbXBsZXRlKSB7XG4gICAgICAvLyBDYW5jZWwgYW55IGV4aXN0aW5nIHRpbWVyIGZpcnN0XG4gICAgICBpZiAodGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIpIHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuaW5jb21wbGV0ZUVzY2FwZVRpbWVyKVxuICAgICAgfVxuICAgICAgdGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIgPSBzZXRUaW1lb3V0KFxuICAgICAgICB0aGlzLmZsdXNoSW5jb21wbGV0ZSxcbiAgICAgICAgdGhpcy5rZXlQYXJzZVN0YXRlLm1vZGUgPT09ICdJTl9QQVNURSdcbiAgICAgICAgICA/IHRoaXMuUEFTVEVfVElNRU9VVFxuICAgICAgICAgIDogdGhpcy5OT1JNQUxfVElNRU9VVCxcbiAgICAgIClcbiAgICB9XG4gIH1cblxuICBoYW5kbGVSZWFkYWJsZSA9ICgpOiB2b2lkID0+IHtcbiAgICAvLyBEZXRlY3QgbG9uZyBzdGRpbiBnYXBzICh0bXV4IGF0dGFjaCwgc3NoIHJlY29ubmVjdCwgbGFwdG9wIHdha2UpLlxuICAgIC8vIFRoZSB0ZXJtaW5hbCBtYXkgaGF2ZSByZXNldCBERUMgcHJpdmF0ZSBtb2RlczsgcmUtYXNzZXJ0IG1vdXNlXG4gICAgLy8gdHJhY2tpbmcuIENoZWNrZWQgYmVmb3JlIHRoZSByZWFkIGxvb3Agc28gb25lIERhdGUubm93KCkgY292ZXJzXG4gICAgLy8gYWxsIGNodW5rcyBpbiB0aGlzIHJlYWRhYmxlIGV2ZW50LlxuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KClcbiAgICBpZiAobm93IC0gdGhpcy5sYXN0U3RkaW5UaW1lID4gU1RESU5fUkVTVU1FX0dBUF9NUykge1xuICAgICAgdGhpcy5wcm9wcy5vblN0ZGluUmVzdW1lPy4oKVxuICAgIH1cbiAgICB0aGlzLmxhc3RTdGRpblRpbWUgPSBub3dcbiAgICB0cnkge1xuICAgICAgbGV0IGNodW5rXG4gICAgICB3aGlsZSAoKGNodW5rID0gdGhpcy5wcm9wcy5zdGRpbi5yZWFkKCkgYXMgc3RyaW5nIHwgbnVsbCkgIT09IG51bGwpIHtcbiAgICAgICAgLy8gUHJvY2VzcyB0aGUgaW5wdXQgY2h1bmtcbiAgICAgICAgdGhpcy5wcm9jZXNzSW5wdXQoY2h1bmspXG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIEluIEJ1biwgYW4gdW5jYXVnaHQgdGhyb3cgaW5zaWRlIGEgc3RyZWFtICdyZWFkYWJsZScgaGFuZGxlciBjYW5cbiAgICAgIC8vIHBlcm1hbmVudGx5IHdlZGdlIHRoZSBzdHJlYW06IGRhdGEgc3RheXMgYnVmZmVyZWQgYW5kICdyZWFkYWJsZSdcbiAgICAgIC8vIG5ldmVyIHJlLWVtaXRzLiBDYXRjaGluZyBoZXJlIGVuc3VyZXMgdGhlIHN0cmVhbSBzdGF5cyBoZWFsdGh5IHNvXG4gICAgICAvLyBzdWJzZXF1ZW50IGtleXN0cm9rZXMgYXJlIHN0aWxsIGRlbGl2ZXJlZC5cbiAgICAgIGxvZ0Vycm9yKGVycm9yKVxuXG4gICAgICAvLyBSZS1hdHRhY2ggdGhlIGxpc3RlbmVyIGluIGNhc2UgdGhlIGV4Y2VwdGlvbiBkZXRhY2hlZCBpdC5cbiAgICAgIC8vIEJ1biBtYXkgcmVtb3ZlIHRoZSBsaXN0ZW5lciBhZnRlciBhbiBlcnJvcjsgd2l0aG91dCB0aGlzLFxuICAgICAgLy8gdGhlIHNlc3Npb24gZnJlZXplcyBwZXJtYW5lbnRseSAoc3RkaW4gcmVhZGVyIGRlYWQsIGV2ZW50IGxvb3AgYWxpdmUpLlxuICAgICAgY29uc3QgeyBzdGRpbiB9ID0gdGhpcy5wcm9wc1xuICAgICAgaWYgKFxuICAgICAgICB0aGlzLnJhd01vZGVFbmFibGVkQ291bnQgPiAwICYmXG4gICAgICAgICFzdGRpbi5saXN0ZW5lcnMoJ3JlYWRhYmxlJykuaW5jbHVkZXModGhpcy5oYW5kbGVSZWFkYWJsZSlcbiAgICAgICkge1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgJ2hhbmRsZVJlYWRhYmxlOiByZS1hdHRhY2hpbmcgc3RkaW4gcmVhZGFibGUgbGlzdGVuZXIgYWZ0ZXIgZXJyb3IgcmVjb3ZlcnknLFxuICAgICAgICAgIHsgbGV2ZWw6ICd3YXJuJyB9LFxuICAgICAgICApXG4gICAgICAgIHN0ZGluLmFkZExpc3RlbmVyKCdyZWFkYWJsZScsIHRoaXMuaGFuZGxlUmVhZGFibGUpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgaGFuZGxlSW5wdXQgPSAoaW5wdXQ6IHN0cmluZyB8IHVuZGVmaW5lZCk6IHZvaWQgPT4ge1xuICAgIC8vIEV4aXQgb24gQ3RybCtDXG4gICAgaWYgKGlucHV0ID09PSAnXFx4MDMnICYmIHRoaXMucHJvcHMuZXhpdE9uQ3RybEMpIHtcbiAgICAgIHRoaXMuaGFuZGxlRXhpdCgpXG4gICAgfVxuXG4gICAgLy8gTm90ZTogQ3RybCtaIChzdXNwZW5kKSBpcyBub3cgaGFuZGxlZCBpbiBwcm9jZXNzS2V5c0luQmF0Y2ggdXNpbmcgdGhlXG4gICAgLy8gcGFyc2VkIGtleSB0byBzdXBwb3J0IGJvdGggcmF3IChcXHgxYSkgYW5kIENTSSB1IGZvcm1hdCBmcm9tIEtpdHR5XG4gICAgLy8ga2V5Ym9hcmQgcHJvdG9jb2wgdGVybWluYWxzIChHaG9zdHR5LCBpVGVybTIsIGtpdHR5LCBXZXpUZXJtKVxuICB9XG5cbiAgaGFuZGxlRXhpdCA9IChlcnJvcj86IEVycm9yKTogdm9pZCA9PiB7XG4gICAgaWYgKHRoaXMuaXNSYXdNb2RlU3VwcG9ydGVkKCkpIHtcbiAgICAgIHRoaXMuaGFuZGxlU2V0UmF3TW9kZShmYWxzZSlcbiAgICB9XG5cbiAgICB0aGlzLnByb3BzLm9uRXhpdChlcnJvcilcbiAgfVxuXG4gIGhhbmRsZVRlcm1pbmFsRm9jdXMgPSAoaXNGb2N1c2VkOiBib29sZWFuKTogdm9pZCA9PiB7XG4gICAgLy8gc2V0VGVybWluYWxGb2N1c2VkIG5vdGlmaWVzIHN1YnNjcmliZXJzOiBUZXJtaW5hbEZvY3VzUHJvdmlkZXIgKGNvbnRleHQpXG4gICAgLy8gYW5kIENsb2NrIChpbnRlcnZhbCBzcGVlZCkg4oCUIG5vIEFwcCBzZXRTdGF0ZSBuZWVkZWQuXG4gICAgc2V0VGVybWluYWxGb2N1c2VkKGlzRm9jdXNlZClcbiAgfVxuXG4gIGhhbmRsZVN1c3BlbmQgPSAoKTogdm9pZCA9PiB7XG4gICAgaWYgKCF0aGlzLmlzUmF3TW9kZVN1cHBvcnRlZCgpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBTdG9yZSB0aGUgZXhhY3QgcmF3IG1vZGUgY291bnQgdG8gcmVzdG9yZSBpdCBwcm9wZXJseVxuICAgIGNvbnN0IHJhd01vZGVDb3VudEJlZm9yZVN1c3BlbmQgPSB0aGlzLnJhd01vZGVFbmFibGVkQ291bnRcblxuICAgIC8vIENvbXBsZXRlbHkgZGlzYWJsZSByYXcgbW9kZSBiZWZvcmUgc3VzcGVuZGluZ1xuICAgIHdoaWxlICh0aGlzLnJhd01vZGVFbmFibGVkQ291bnQgPiAwKSB7XG4gICAgICB0aGlzLmhhbmRsZVNldFJhd01vZGUoZmFsc2UpXG4gICAgfVxuXG4gICAgLy8gU2hvdyBjdXJzb3IsIGRpc2FibGUgZm9jdXMgcmVwb3J0aW5nLCBhbmQgZGlzYWJsZSBtb3VzZSB0cmFja2luZ1xuICAgIC8vIGJlZm9yZSBzdXNwZW5kaW5nLiBESVNBQkxFX01PVVNFX1RSQUNLSU5HIGlzIGEgbm8tb3AgaWYgdHJhY2tpbmdcbiAgICAvLyB3YXNuJ3QgZW5hYmxlZCwgc28gaXQncyBzYWZlIHRvIGVtaXQgdW5jb25kaXRpb25hbGx5IOKAlCB3aXRob3V0XG4gICAgLy8gaXQsIFNHUiBtb3VzZSBzZXF1ZW5jZXMgd291bGQgYXBwZWFyIGFzIGdhcmJsZWQgdGV4dCBhdCB0aGVcbiAgICAvLyBzaGVsbCBwcm9tcHQgd2hpbGUgc3VzcGVuZGVkLlxuICAgIGlmICh0aGlzLnByb3BzLnN0ZG91dC5pc1RUWSkge1xuICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoU0hPV19DVVJTT1IgKyBERkUgKyBESVNBQkxFX01PVVNFX1RSQUNLSU5HKVxuICAgIH1cblxuICAgIC8vIEVtaXQgc3VzcGVuZCBldmVudCBmb3IgQ2xhdWRlIENvZGUgdG8gaGFuZGxlLiBNb3N0bHkganVzdCBoYXMgYSBub3RpZmljYXRpb25cbiAgICB0aGlzLmludGVybmFsX2V2ZW50RW1pdHRlci5lbWl0KCdzdXNwZW5kJylcblxuICAgIC8vIFNldCB1cCByZXN1bWUgaGFuZGxlclxuICAgIGNvbnN0IHJlc3VtZUhhbmRsZXIgPSAoKSA9PiB7XG4gICAgICAvLyBSZXN0b3JlIHJhdyBtb2RlIHRvIGV4YWN0IHByZXZpb3VzIHN0YXRlXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJhd01vZGVDb3VudEJlZm9yZVN1c3BlbmQ7IGkrKykge1xuICAgICAgICBpZiAodGhpcy5pc1Jhd01vZGVTdXBwb3J0ZWQoKSkge1xuICAgICAgICAgIHRoaXMuaGFuZGxlU2V0UmF3TW9kZSh0cnVlKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEhpZGUgY3Vyc29yICh1bmxlc3MgaW4gYWNjZXNzaWJpbGl0eSBtb2RlKSBhbmQgcmUtZW5hYmxlIGZvY3VzIHJlcG9ydGluZyBhZnRlciByZXN1bWluZ1xuICAgICAgaWYgKHRoaXMucHJvcHMuc3Rkb3V0LmlzVFRZKSB7XG4gICAgICAgIGlmICghaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfQUNDRVNTSUJJTElUWSkpIHtcbiAgICAgICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShISURFX0NVUlNPUilcbiAgICAgICAgfVxuICAgICAgICAvLyBSZS1lbmFibGUgZm9jdXMgcmVwb3J0aW5nIHRvIHJlc3RvcmUgdGVybWluYWwgc3RhdGVcbiAgICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoRUZFKVxuICAgICAgfVxuXG4gICAgICAvLyBFbWl0IHJlc3VtZSBldmVudCBmb3IgQ2xhdWRlIENvZGUgdG8gaGFuZGxlXG4gICAgICB0aGlzLmludGVybmFsX2V2ZW50RW1pdHRlci5lbWl0KCdyZXN1bWUnKVxuXG4gICAgICBwcm9jZXNzLnJlbW92ZUxpc3RlbmVyKCdTSUdDT05UJywgcmVzdW1lSGFuZGxlcilcbiAgICB9XG5cbiAgICBwcm9jZXNzLm9uKCdTSUdDT05UJywgcmVzdW1lSGFuZGxlcilcbiAgICBwcm9jZXNzLmtpbGwocHJvY2Vzcy5waWQsICdTSUdTVE9QJylcbiAgfVxufVxuXG4vLyBIZWxwZXIgdG8gcHJvY2VzcyBhbGwga2V5cyB3aXRoaW4gYSBzaW5nbGUgZGlzY3JldGUgdXBkYXRlIGNvbnRleHQuXG4vLyBkaXNjcmV0ZVVwZGF0ZXMgZXhwZWN0cyAoZm4sIGEsIGIsIGMsIGQpIC0+IGZuKGEsIGIsIGMsIGQpXG5mdW5jdGlvbiBwcm9jZXNzS2V5c0luQmF0Y2goXG4gIGFwcDogQXBwLFxuICBpdGVtczogUGFyc2VkSW5wdXRbXSxcbiAgX3VudXNlZDE6IHVuZGVmaW5lZCxcbiAgX3VudXNlZDI6IHVuZGVmaW5lZCxcbik6IHZvaWQge1xuICAvLyBVcGRhdGUgaW50ZXJhY3Rpb24gdGltZSBmb3Igbm90aWZpY2F0aW9uIHRpbWVvdXQgdHJhY2tpbmcuXG4gIC8vIFRoaXMgaXMgY2FsbGVkIGZyb20gdGhlIGNlbnRyYWwgaW5wdXQgaGFuZGxlciB0byBhdm9pZCBoYXZpbmcgbXVsdGlwbGVcbiAgLy8gc3RkaW4gbGlzdGVuZXJzIHRoYXQgY2FuIGNhdXNlIHJhY2UgY29uZGl0aW9ucyBhbmQgZHJvcHBlZCBpbnB1dC5cbiAgLy8gVGVybWluYWwgcmVzcG9uc2VzIChraW5kOiAncmVzcG9uc2UnKSBhcmUgYXV0b21hdGVkLCBub3QgdXNlciBpbnB1dC5cbiAgLy8gTW9kZS0xMDAzIG5vLWJ1dHRvbiBtb3Rpb24gaXMgYWxzbyBleGNsdWRlZCDigJQgcGFzc2l2ZSBjdXJzb3IgZHJpZnQgaXNcbiAgLy8gbm90IGVuZ2FnZW1lbnQgKHdvdWxkIHN1cHByZXNzIGlkbGUgbm90aWZpY2F0aW9ucyArIGRlZmVyIGhvdXNla2VlcGluZykuXG4gIGlmIChcbiAgICBpdGVtcy5zb21lKFxuICAgICAgaSA9PlxuICAgICAgICBpLmtpbmQgPT09ICdrZXknIHx8XG4gICAgICAgIChpLmtpbmQgPT09ICdtb3VzZScgJiZcbiAgICAgICAgICAhKChpLmJ1dHRvbiAmIDB4MjApICE9PSAwICYmIChpLmJ1dHRvbiAmIDB4MDMpID09PSAzKSksXG4gICAgKVxuICApIHtcbiAgICB1cGRhdGVMYXN0SW50ZXJhY3Rpb25UaW1lKClcbiAgfVxuXG4gIGZvciAoY29uc3QgaXRlbSBvZiBpdGVtcykge1xuICAgIC8vIFRlcm1pbmFsIHJlc3BvbnNlcyAoREVDUlBNLCBEQTEsIE9TQyByZXBsaWVzLCBldGMuKSBhcmUgbm90IHVzZXJcbiAgICAvLyBpbnB1dCDigJQgcm91dGUgdGhlbSB0byB0aGUgcXVlcmllciB0byByZXNvbHZlIHBlbmRpbmcgcHJvbWlzZXMuXG4gICAgaWYgKGl0ZW0ua2luZCA9PT0gJ3Jlc3BvbnNlJykge1xuICAgICAgYXBwLnF1ZXJpZXIub25SZXNwb25zZShpdGVtLnJlc3BvbnNlKVxuICAgICAgY29udGludWVcbiAgICB9XG5cbiAgICAvLyBNb3VzZSBjbGljay9kcmFnIGV2ZW50cyB1cGRhdGUgc2VsZWN0aW9uIHN0YXRlIChmdWxsc2NyZWVuIG9ubHkpLlxuICAgIC8vIFRlcm1pbmFsIHNlbmRzIDEtaW5kZXhlZCBjb2wvcm93OyBjb252ZXJ0IHRvIDAtaW5kZXhlZCBmb3IgdGhlXG4gICAgLy8gc2NyZWVuIGJ1ZmZlci4gQnV0dG9uIGJpdCAweDIwID0gZHJhZyAobW90aW9uIHdoaWxlIGJ1dHRvbiBoZWxkKS5cbiAgICBpZiAoaXRlbS5raW5kID09PSAnbW91c2UnKSB7XG4gICAgICBoYW5kbGVNb3VzZUV2ZW50KGFwcCwgaXRlbSlcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgY29uc3Qgc2VxdWVuY2UgPSBpdGVtLnNlcXVlbmNlXG5cbiAgICAvLyBIYW5kbGUgdGVybWluYWwgZm9jdXMgZXZlbnRzIChERUNTRVQgMTAwNClcbiAgICBpZiAoc2VxdWVuY2UgPT09IEZPQ1VTX0lOKSB7XG4gICAgICBhcHAuaGFuZGxlVGVybWluYWxGb2N1cyh0cnVlKVxuICAgICAgY29uc3QgZXZlbnQgPSBuZXcgVGVybWluYWxGb2N1c0V2ZW50KCd0ZXJtaW5hbGZvY3VzJylcbiAgICAgIGFwcC5pbnRlcm5hbF9ldmVudEVtaXR0ZXIuZW1pdCgndGVybWluYWxmb2N1cycsIGV2ZW50KVxuICAgICAgY29udGludWVcbiAgICB9XG4gICAgaWYgKHNlcXVlbmNlID09PSBGT0NVU19PVVQpIHtcbiAgICAgIGFwcC5oYW5kbGVUZXJtaW5hbEZvY3VzKGZhbHNlKVxuICAgICAgLy8gRGVmZW5zaXZlOiBpZiB3ZSBsb3N0IHRoZSByZWxlYXNlIGV2ZW50IChtb3VzZSByZWxlYXNlZCBvdXRzaWRlXG4gICAgICAvLyB0ZXJtaW5hbCB3aW5kb3cg4oCUIHNvbWUgZW11bGF0b3JzIGRyb3AgaXQgcmF0aGVyIHRoYW4gY2FwdHVyaW5nIHRoZVxuICAgICAgLy8gcG9pbnRlciksIGZvY3VzLW91dCBpcyB0aGUgbmV4dCBvYnNlcnZhYmxlIHNpZ25hbCB0aGF0IHRoZSBkcmFnIGlzXG4gICAgICAvLyBvdmVyLiBXaXRob3V0IHRoaXMsIGRyYWctdG8tc2Nyb2xsJ3MgdGltZXIgcnVucyB1bnRpbCB0aGUgc2Nyb2xsXG4gICAgICAvLyBib3VuZGFyeSBpcyBoaXQuXG4gICAgICBpZiAoYXBwLnByb3BzLnNlbGVjdGlvbi5pc0RyYWdnaW5nKSB7XG4gICAgICAgIGZpbmlzaFNlbGVjdGlvbihhcHAucHJvcHMuc2VsZWN0aW9uKVxuICAgICAgICBhcHAucHJvcHMub25TZWxlY3Rpb25DaGFuZ2UoKVxuICAgICAgfVxuICAgICAgY29uc3QgZXZlbnQgPSBuZXcgVGVybWluYWxGb2N1c0V2ZW50KCd0ZXJtaW5hbGJsdXInKVxuICAgICAgYXBwLmludGVybmFsX2V2ZW50RW1pdHRlci5lbWl0KCd0ZXJtaW5hbGJsdXInLCBldmVudClcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgLy8gRmFpbHNhZmU6IGlmIHdlIHJlY2VpdmUgaW5wdXQsIHRoZSB0ZXJtaW5hbCBtdXN0IGJlIGZvY3VzZWRcbiAgICBpZiAoIWdldFRlcm1pbmFsRm9jdXNlZCgpKSB7XG4gICAgICBzZXRUZXJtaW5hbEZvY3VzZWQodHJ1ZSlcbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgQ3RybCtaIChzdXNwZW5kKSB1c2luZyBwYXJzZWQga2V5IHRvIHN1cHBvcnQgYm90aCByYXcgKFxceDFhKSBhbmRcbiAgICAvLyBDU0kgdSBmb3JtYXQgKFxceDFiWzEyMjs1dSkgZnJvbSBLaXR0eSBrZXlib2FyZCBwcm90b2NvbCB0ZXJtaW5hbHNcbiAgICBpZiAoaXRlbS5uYW1lID09PSAneicgJiYgaXRlbS5jdHJsICYmIFNVUFBPUlRTX1NVU1BFTkQpIHtcbiAgICAgIGFwcC5oYW5kbGVTdXNwZW5kKClcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgYXBwLmhhbmRsZUlucHV0KHNlcXVlbmNlKVxuICAgIGNvbnN0IGV2ZW50ID0gbmV3IElucHV0RXZlbnQoaXRlbSlcbiAgICBhcHAuaW50ZXJuYWxfZXZlbnRFbWl0dGVyLmVtaXQoJ2lucHV0JywgZXZlbnQpXG5cbiAgICAvLyBBbHNvIGRpc3BhdGNoIHRocm91Z2ggdGhlIERPTSB0cmVlIHNvIG9uS2V5RG93biBoYW5kbGVycyBmaXJlLlxuICAgIGFwcC5wcm9wcy5kaXNwYXRjaEtleWJvYXJkRXZlbnQoaXRlbSlcbiAgfVxufVxuXG4vKiogRXhwb3J0ZWQgZm9yIHRlc3RpbmcuIE11dGF0ZXMgYXBwLnByb3BzLnNlbGVjdGlvbiBhbmQgY2xpY2svaG92ZXIgc3RhdGUuICovXG5leHBvcnQgZnVuY3Rpb24gaGFuZGxlTW91c2VFdmVudChhcHA6IEFwcCwgbTogUGFyc2VkTW91c2UpOiB2b2lkIHtcbiAgLy8gQWxsb3cgZGlzYWJsaW5nIGNsaWNrIGhhbmRsaW5nIHdoaWxlIGtlZXBpbmcgd2hlZWwgc2Nyb2xsICh3aGljaCBnb2VzXG4gIC8vIHRocm91Z2ggdGhlIGtleWJpbmRpbmcgc3lzdGVtIGFzICd3aGVlbHVwJy8nd2hlZWxkb3duJywgbm90IGhlcmUpLlxuICBpZiAoaXNNb3VzZUNsaWNrc0Rpc2FibGVkKCkpIHJldHVyblxuXG4gIGNvbnN0IHNlbCA9IGFwcC5wcm9wcy5zZWxlY3Rpb25cbiAgLy8gVGVybWluYWwgY29vcmRzIGFyZSAxLWluZGV4ZWQ7IHNjcmVlbiBidWZmZXIgaXMgMC1pbmRleGVkXG4gIGNvbnN0IGNvbCA9IG0uY29sIC0gMVxuICBjb25zdCByb3cgPSBtLnJvdyAtIDFcbiAgY29uc3QgYmFzZUJ1dHRvbiA9IG0uYnV0dG9uICYgMHgwM1xuXG4gIGlmIChtLmFjdGlvbiA9PT0gJ3ByZXNzJykge1xuICAgIGlmICgobS5idXR0b24gJiAweDIwKSAhPT0gMCAmJiBiYXNlQnV0dG9uID09PSAzKSB7XG4gICAgICAvLyBNb2RlLTEwMDMgbW90aW9uIHdpdGggbm8gYnV0dG9uIGhlbGQuIERpc3BhdGNoIGhvdmVyOyBza2lwIHRoZVxuICAgICAgLy8gcmVzdCBvZiB0aGlzIGhhbmRsZXIgKG5vIHNlbGVjdGlvbiwgbm8gY2xpY2stY291bnQgc2lkZSBlZmZlY3RzKS5cbiAgICAgIC8vIExvc3QtcmVsZWFzZSByZWNvdmVyeTogbm8tYnV0dG9uIG1vdGlvbiB3aGlsZSBpc0RyYWdnaW5nPXRydWUgbWVhbnNcbiAgICAgIC8vIHRoZSByZWxlYXNlIGhhcHBlbmVkIG91dHNpZGUgdGhlIHRlcm1pbmFsIHdpbmRvdyAoaVRlcm0yIGRvZXNuJ3RcbiAgICAgIC8vIGNhcHR1cmUgdGhlIHBvaW50ZXIgcGFzdCB3aW5kb3cgYm91bmRzLCBzbyB0aGUgU0dSICdtJyBuZXZlclxuICAgICAgLy8gYXJyaXZlcykuIEZpbmlzaCB0aGUgc2VsZWN0aW9uIGhlcmUgc28gY29weS1vbi1zZWxlY3QgZmlyZXMuIFRoZVxuICAgICAgLy8gRk9DVVNfT1VUIGhhbmRsZXIgY292ZXJzIHRoZSBcInN3aXRjaGVkIGFwcHNcIiBjYXNlIGJ1dCBub3QgXCJyZWxlYXNlZFxuICAgICAgLy8gcGFzdCB0aGUgZWRnZSwgY2FtZSBiYWNrXCIg4oCUIGFuZCB0bXV4IGRyb3BzIGZvY3VzIGV2ZW50cyB1bmxlc3NcbiAgICAgIC8vIGBmb2N1cy1ldmVudHMgb25gIGlzIHNldCwgc28gdGhpcyBpcyB0aGUgbW9yZSByZWxpYWJsZSBzaWduYWwuXG4gICAgICBpZiAoc2VsLmlzRHJhZ2dpbmcpIHtcbiAgICAgICAgZmluaXNoU2VsZWN0aW9uKHNlbClcbiAgICAgICAgYXBwLnByb3BzLm9uU2VsZWN0aW9uQ2hhbmdlKClcbiAgICAgIH1cbiAgICAgIGlmIChjb2wgPT09IGFwcC5sYXN0SG92ZXJDb2wgJiYgcm93ID09PSBhcHAubGFzdEhvdmVyUm93KSByZXR1cm5cbiAgICAgIGFwcC5sYXN0SG92ZXJDb2wgPSBjb2xcbiAgICAgIGFwcC5sYXN0SG92ZXJSb3cgPSByb3dcbiAgICAgIGFwcC5wcm9wcy5vbkhvdmVyQXQoY29sLCByb3cpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgaWYgKGJhc2VCdXR0b24gIT09IDApIHtcbiAgICAgIC8vIE5vbi1sZWZ0IHByZXNzIGJyZWFrcyB0aGUgbXVsdGktY2xpY2sgY2hhaW4uXG4gICAgICBhcHAuY2xpY2tDb3VudCA9IDBcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBpZiAoKG0uYnV0dG9uICYgMHgyMCkgIT09IDApIHtcbiAgICAgIC8vIERyYWcgbW90aW9uOiBtb2RlLWF3YXJlIGV4dGVuc2lvbiAoY2hhci93b3JkL2xpbmUpLiBvblNlbGVjdGlvbkRyYWdcbiAgICAgIC8vIGNhbGxzIG5vdGlmeVNlbGVjdGlvbkNoYW5nZSBpbnRlcm5hbGx5IOKAlCBubyBleHRyYSBvblNlbGVjdGlvbkNoYW5nZS5cbiAgICAgIGFwcC5wcm9wcy5vblNlbGVjdGlvbkRyYWcoY29sLCByb3cpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgLy8gTG9zdC1yZWxlYXNlIGZhbGxiYWNrIGZvciBtb2RlLTEwMDItb25seSB0ZXJtaW5hbHM6IGEgZnJlc2ggcHJlc3NcbiAgICAvLyB3aGlsZSBpc0RyYWdnaW5nPXRydWUgbWVhbnMgdGhlIHByZXZpb3VzIHJlbGVhc2Ugd2FzIGRyb3BwZWQgKGN1cnNvclxuICAgIC8vIGxlZnQgdGhlIHdpbmRvdykuIEZpbmlzaCB0aGF0IHNlbGVjdGlvbiBzbyBjb3B5LW9uLXNlbGVjdCBmaXJlc1xuICAgIC8vIGJlZm9yZSBzdGFydFNlbGVjdGlvbi9vbk11bHRpQ2xpY2sgY2xvYmJlcnMgaXQuIE1vZGUtMTAwMyB0ZXJtaW5hbHNcbiAgICAvLyBoaXQgdGhlIG5vLWJ1dHRvbi1tb3Rpb24gcmVjb3ZlcnkgYWJvdmUgaW5zdGVhZCwgc28gdGhpcyBpcyByYXJlLlxuICAgIGlmIChzZWwuaXNEcmFnZ2luZykge1xuICAgICAgZmluaXNoU2VsZWN0aW9uKHNlbClcbiAgICAgIGFwcC5wcm9wcy5vblNlbGVjdGlvbkNoYW5nZSgpXG4gICAgfVxuICAgIC8vIEZyZXNoIGxlZnQgcHJlc3MuIERldGVjdCBtdWx0aS1jbGljayBIRVJFIChub3Qgb24gcmVsZWFzZSkgc28gdGhlXG4gICAgLy8gd29yZC9saW5lIGhpZ2hsaWdodCBhcHBlYXJzIGltbWVkaWF0ZWx5IGFuZCBhIHN1YnNlcXVlbnQgZHJhZyBjYW5cbiAgICAvLyBleHRlbmQgYnkgd29yZC9saW5lIGxpa2UgbmF0aXZlIG1hY09TLiBQcmV2aW91c2x5IGRldGVjdGVkIG9uXG4gICAgLy8gcmVsZWFzZSwgd2hpY2ggbWVhbnQgKGEpIHZpc2libGUgbGF0ZW5jeSBiZWZvcmUgdGhlIHdvcmQgaGlnaGxpZ2h0c1xuICAgIC8vIGFuZCAoYikgZG91YmxlLWNsaWNrK2RyYWcgZmVsbCB0aHJvdWdoIHRvIGNoYXItbW9kZSBzZWxlY3Rpb24uXG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKVxuICAgIGNvbnN0IG5lYXJMYXN0ID1cbiAgICAgIG5vdyAtIGFwcC5sYXN0Q2xpY2tUaW1lIDwgTVVMVElfQ0xJQ0tfVElNRU9VVF9NUyAmJlxuICAgICAgTWF0aC5hYnMoY29sIC0gYXBwLmxhc3RDbGlja0NvbCkgPD0gTVVMVElfQ0xJQ0tfRElTVEFOQ0UgJiZcbiAgICAgIE1hdGguYWJzKHJvdyAtIGFwcC5sYXN0Q2xpY2tSb3cpIDw9IE1VTFRJX0NMSUNLX0RJU1RBTkNFXG4gICAgYXBwLmNsaWNrQ291bnQgPSBuZWFyTGFzdCA/IGFwcC5jbGlja0NvdW50ICsgMSA6IDFcbiAgICBhcHAubGFzdENsaWNrVGltZSA9IG5vd1xuICAgIGFwcC5sYXN0Q2xpY2tDb2wgPSBjb2xcbiAgICBhcHAubGFzdENsaWNrUm93ID0gcm93XG4gICAgaWYgKGFwcC5jbGlja0NvdW50ID49IDIpIHtcbiAgICAgIC8vIENhbmNlbCBhbnkgcGVuZGluZyBoeXBlcmxpbmstb3BlbiBmcm9tIHRoZSBmaXJzdCBjbGljayDigJQgdGhpcyBpc1xuICAgICAgLy8gYSBkb3VibGUtY2xpY2ssIG5vdCBhIHNpbmdsZS1jbGljayBvbiBhIGxpbmsuXG4gICAgICBpZiAoYXBwLnBlbmRpbmdIeXBlcmxpbmtUaW1lcikge1xuICAgICAgICBjbGVhclRpbWVvdXQoYXBwLnBlbmRpbmdIeXBlcmxpbmtUaW1lcilcbiAgICAgICAgYXBwLnBlbmRpbmdIeXBlcmxpbmtUaW1lciA9IG51bGxcbiAgICAgIH1cbiAgICAgIC8vIENhcCBhdCAzIChsaW5lIHNlbGVjdCkgZm9yIHF1YWRydXBsZSsgY2xpY2tzLlxuICAgICAgY29uc3QgY291bnQgPSBhcHAuY2xpY2tDb3VudCA9PT0gMiA/IDIgOiAzXG4gICAgICBhcHAucHJvcHMub25NdWx0aUNsaWNrKGNvbCwgcm93LCBjb3VudClcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBzdGFydFNlbGVjdGlvbihzZWwsIGNvbCwgcm93KVxuICAgIC8vIFNHUiBiaXQgMHgwOCA9IGFsdCAoeHRlcm0uanMgd2lyZXMgYWx0S2V5IGhlcmUsIG5vdCBtZXRhS2V5IOKAlCBzZWVcbiAgICAvLyBjb21tZW50IGF0IHRoZSBoeXBlcmxpbmstb3BlbiBndWFyZCBiZWxvdykuIE9uIG1hY09TIHh0ZXJtLmpzLFxuICAgIC8vIHJlY2VpdmluZyBhbHQgbWVhbnMgbWFjT3B0aW9uQ2xpY2tGb3JjZXNTZWxlY3Rpb24gaXMgT0ZGIChvdGhlcndpc2VcbiAgICAvLyB4dGVybS5qcyB3b3VsZCBoYXZlIGNvbnN1bWVkIHRoZSBldmVudCBmb3IgbmF0aXZlIHNlbGVjdGlvbikuXG4gICAgc2VsLmxhc3RQcmVzc0hhZEFsdCA9IChtLmJ1dHRvbiAmIDB4MDgpICE9PSAwXG4gICAgYXBwLnByb3BzLm9uU2VsZWN0aW9uQ2hhbmdlKClcbiAgICByZXR1cm5cbiAgfVxuXG4gIC8vIFJlbGVhc2U6IGVuZCB0aGUgZHJhZyBldmVuIGZvciBub24temVybyBidXR0b24gY29kZXMuIFNvbWUgdGVybWluYWxzXG4gIC8vIGVuY29kZSByZWxlYXNlIHdpdGggdGhlIG1vdGlvbiBiaXQgb3IgYnV0dG9uPTMgXCJubyBidXR0b25cIiAoY2FycmllZFxuICAvLyBvdmVyIGZyb20gcHJlLVNHUiBYMTAgZW5jb2RpbmcpIOKAlCBmaWx0ZXJpbmcgdGhvc2Ugd291bGQgb3JwaGFuXG4gIC8vIGlzRHJhZ2dpbmc9dHJ1ZSBhbmQgbGVhdmUgZHJhZy10by1zY3JvbGwncyB0aW1lciBydW5uaW5nIHVudGlsIHRoZVxuICAvLyBzY3JvbGwgYm91bmRhcnkuIE9ubHkgYWN0IG9uIG5vbi1sZWZ0IHJlbGVhc2VzIHdoZW4gd2UgQVJFIGRyYWdnaW5nXG4gIC8vIChzbyBhbiB1bnJlbGF0ZWQgbWlkZGxlL3JpZ2h0IGNsaWNrLXJlbGVhc2UgZG9lc24ndCB0b3VjaCBzZWxlY3Rpb24pLlxuICBpZiAoYmFzZUJ1dHRvbiAhPT0gMCkge1xuICAgIGlmICghc2VsLmlzRHJhZ2dpbmcpIHJldHVyblxuICAgIGZpbmlzaFNlbGVjdGlvbihzZWwpXG4gICAgYXBwLnByb3BzLm9uU2VsZWN0aW9uQ2hhbmdlKClcbiAgICByZXR1cm5cbiAgfVxuICBmaW5pc2hTZWxlY3Rpb24oc2VsKVxuICAvLyBOT1RFOiB1bmxpa2UgdGhlIG9sZCByZWxlYXNlLWJhc2VkIGRldGVjdGlvbiB3ZSBkbyBOT1QgcmVzZXQgY2xpY2tDb3VudFxuICAvLyBvbiByZWxlYXNlLWFmdGVyLWRyYWcuIFRoaXMgYWxpZ25zIHdpdGggTlNFdmVudC5jbGlja0NvdW50IHNlbWFudGljczpcbiAgLy8gYW4gaW50ZXJ2ZW5pbmcgZHJhZyBkb2Vzbid0IGJyZWFrIHRoZSBjbGljayBjaGFpbi4gUHJhY3RpY2FsIHVwc2lkZTpcbiAgLy8gdHJhY2twYWQgaml0dGVyIGR1cmluZyBhbiBpbnRlbmRlZCBkb3VibGUtY2xpY2sgKHByZXNz4oaSd29iYmxl4oaScmVsZWFzZVxuICAvLyDihpJwcmVzcykgbm93IGNvcnJlY3RseSByZXNvbHZlcyB0byB3b3JkLXNlbGVjdCBpbnN0ZWFkIG9mIGJyZWFraW5nIHRvIGFcbiAgLy8gZnJlc2ggc2luZ2xlIGNsaWNrLiBUaGUgbmVhckxhc3Qgd2luZG93ICg1MDBtcywgMSBjZWxsKSBib3VuZHMgdGhlXG4gIC8vIGVmZmVjdCDigJQgYSBkZWxpYmVyYXRlIGRyYWcgcGFzdCB0aGF0IGp1c3Qgc3RhcnRzIGEgZnJlc2ggY2hhaW4uXG4gIC8vIEEgcHJlc3MrcmVsZWFzZSB3aXRoIG5vIGRyYWcgaW4gY2hhciBtb2RlIGlzIGEgY2xpY2s6IGFuY2hvciBzZXQsXG4gIC8vIGZvY3VzIG51bGwg4oaSIGhhc1NlbGVjdGlvbiBmYWxzZS4gSW4gd29yZC9saW5lIG1vZGUgdGhlIHByZXNzIGFscmVhZHlcbiAgLy8gc2V0IGFuY2hvcitmb2N1cyAoaGFzU2VsZWN0aW9uIHRydWUpLCBzbyByZWxlYXNlIGp1c3Qga2VlcHMgdGhlXG4gIC8vIGhpZ2hsaWdodC4gVGhlIGFuY2hvciBjaGVjayBndWFyZHMgYWdhaW5zdCBhbiBvcnBoYW5lZCByZWxlYXNlIChub1xuICAvLyBwcmlvciBwcmVzcyDigJQgZS5nLiBidXR0b24gd2FzIGhlbGQgd2hlbiBtb3VzZSB0cmFja2luZyB3YXMgZW5hYmxlZCkuXG4gIGlmICghaGFzU2VsZWN0aW9uKHNlbCkgJiYgc2VsLmFuY2hvcikge1xuICAgIC8vIFNpbmdsZSBjbGljazogZGlzcGF0Y2ggRE9NIGNsaWNrIGltbWVkaWF0ZWx5IChjdXJzb3IgcmVwb3NpdGlvbmluZ1xuICAgIC8vIGV0Yy4gYXJlIGxhdGVuY3ktc2Vuc2l0aXZlKS4gSWYgbm8gRE9NIGhhbmRsZXIgY29uc3VtZWQgaXQsIGRlZmVyXG4gICAgLy8gdGhlIGh5cGVybGluayBjaGVjayBzbyBhIHNlY29uZCBjbGljayBjYW4gY2FuY2VsIGl0LlxuICAgIGlmICghYXBwLnByb3BzLm9uQ2xpY2tBdChjb2wsIHJvdykpIHtcbiAgICAgIC8vIFJlc29sdmUgdGhlIGh5cGVybGluayBVUkwgc3luY2hyb25vdXNseSB3aGlsZSB0aGUgc2NyZWVuIGJ1ZmZlclxuICAgICAgLy8gc3RpbGwgcmVmbGVjdHMgd2hhdCB0aGUgdXNlciBjbGlja2VkIOKAlCBkZWZlcnJpbmcgb25seSB0aGVcbiAgICAgIC8vIGJyb3dzZXItb3BlbiBzbyBkb3VibGUtY2xpY2sgY2FuIGNhbmNlbCBpdC5cbiAgICAgIGNvbnN0IHVybCA9IGFwcC5wcm9wcy5nZXRIeXBlcmxpbmtBdChjb2wsIHJvdylcbiAgICAgIC8vIHh0ZXJtLmpzIChWUyBDb2RlLCBDdXJzb3IsIFdpbmRzdXJmLCBldGMuKSBoYXMgaXRzIG93biBPU0MgOCBsaW5rXG4gICAgICAvLyBoYW5kbGVyIHRoYXQgZmlyZXMgb24gQ21kK2NsaWNrICp3aXRob3V0IGNvbnN1bWluZyB0aGUgbW91c2UgZXZlbnQqXG4gICAgICAvLyAoTGlua2lmaWVyLl9oYW5kbGVNb3VzZVVwIGNhbGxzIGxpbmsuYWN0aXZhdGUoKSBidXQgbmV2ZXJcbiAgICAgIC8vIHByZXZlbnREZWZhdWx0L3N0b3BQcm9wYWdhdGlvbikuIFRoZSBjbGljayBpcyBhbHNvIGZvcndhcmRlZCB0byB0aGVcbiAgICAgIC8vIHB0eSBhcyBTR1IsIHNvIGJvdGggVlMgQ29kZSdzIHRlcm1pbmFsTGlua01hbmFnZXIgQU5EIG91ciBoYW5kbGVyXG4gICAgICAvLyBoZXJlIHdvdWxkIG9wZW4gdGhlIFVSTCDigJQgdHdpY2UuIFdlIGNhbid0IGZpbHRlciBvbiBDbWQ6IHh0ZXJtLmpzXG4gICAgICAvLyBkcm9wcyBtZXRhS2V5IGJlZm9yZSBTR1IgZW5jb2RpbmcgKElDb3JlTW91c2VFdmVudCBoYXMgbm8gbWV0YVxuICAgICAgLy8gZmllbGQ7IHRoZSBTR1IgYml0IHdlIGNhbGwgJ21ldGEnIGlzIHdpcmVkIHRvIGFsdCkuIExldCB4dGVybS5qc1xuICAgICAgLy8gb3duIGxpbmstb3BlbmluZzsgQ21kK2NsaWNrIGlzIHRoZSBuYXRpdmUgVVggdGhlcmUgYW55d2F5LlxuICAgICAgLy8gVEVSTV9QUk9HUkFNIGlzIHRoZSBzeW5jIGZhc3QtcGF0aDsgaXNYdGVybUpzKCkgaXMgdGhlIFhUVkVSU0lPTlxuICAgICAgLy8gcHJvYmUgcmVzdWx0IChjYXRjaGVzIFNTSCArIG5vbi1WUyBDb2RlIGVtYmVkZGVycyBsaWtlIEh5cGVyKS5cbiAgICAgIGlmICh1cmwgJiYgcHJvY2Vzcy5lbnYuVEVSTV9QUk9HUkFNICE9PSAndnNjb2RlJyAmJiAhaXNYdGVybUpzKCkpIHtcbiAgICAgICAgLy8gQ2xlYXIgYW55IHByaW9yIHBlbmRpbmcgdGltZXIg4oCUIGNsaWNraW5nIGEgc2Vjb25kIGxpbmtcbiAgICAgICAgLy8gc3VwZXJzZWRlcyB0aGUgZmlyc3QgKG9ubHkgdGhlIGxhdGVzdCBjbGljayBvcGVucykuXG4gICAgICAgIGlmIChhcHAucGVuZGluZ0h5cGVybGlua1RpbWVyKSB7XG4gICAgICAgICAgY2xlYXJUaW1lb3V0KGFwcC5wZW5kaW5nSHlwZXJsaW5rVGltZXIpXG4gICAgICAgIH1cbiAgICAgICAgYXBwLnBlbmRpbmdIeXBlcmxpbmtUaW1lciA9IHNldFRpbWVvdXQoXG4gICAgICAgICAgKGFwcCwgdXJsKSA9PiB7XG4gICAgICAgICAgICBhcHAucGVuZGluZ0h5cGVybGlua1RpbWVyID0gbnVsbFxuICAgICAgICAgICAgYXBwLnByb3BzLm9uT3Blbkh5cGVybGluayh1cmwpXG4gICAgICAgICAgfSxcbiAgICAgICAgICBNVUxUSV9DTElDS19USU1FT1VUX01TLFxuICAgICAgICAgIGFwcCxcbiAgICAgICAgICB1cmwsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgYXBwLnByb3BzLm9uU2VsZWN0aW9uQ2hhbmdlKClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxJQUFJQyxhQUFhLEVBQUUsS0FBS0MsU0FBUyxRQUFRLE9BQU87QUFDNUQsU0FBU0MseUJBQXlCLFFBQVEsMEJBQTBCO0FBQ3BFLFNBQVNDLGVBQWUsUUFBUSxzQkFBc0I7QUFDdEQsU0FBU0MsdUJBQXVCLFFBQVEsMkJBQTJCO0FBQ25FLFNBQVNDLFdBQVcsUUFBUSx5QkFBeUI7QUFDckQsU0FBU0MscUJBQXFCLFFBQVEsMkJBQTJCO0FBQ2pFLFNBQVNDLFFBQVEsUUFBUSxvQkFBb0I7QUFDN0MsU0FBU0MsWUFBWSxRQUFRLHNCQUFzQjtBQUNuRCxTQUFTQyxVQUFVLFFBQVEsMEJBQTBCO0FBQ3JELFNBQVNDLGtCQUFrQixRQUFRLG1DQUFtQztBQUN0RSxTQUNFQyxhQUFhLEVBQ2IsS0FBS0MsV0FBVyxFQUNoQixLQUFLQyxTQUFTLEVBQ2QsS0FBS0MsV0FBVyxFQUNoQkMsdUJBQXVCLFFBQ2xCLHNCQUFzQjtBQUM3QixPQUFPQyxVQUFVLE1BQU0sa0JBQWtCO0FBQ3pDLFNBQ0VDLGVBQWUsRUFDZkMsWUFBWSxFQUNaLEtBQUtDLGNBQWMsRUFDbkJDLGNBQWMsUUFDVCxpQkFBaUI7QUFDeEIsU0FDRUMsU0FBUyxFQUNUQyxnQkFBZ0IsRUFDaEJDLG9CQUFvQixRQUNmLGdCQUFnQjtBQUN2QixTQUNFQyxrQkFBa0IsRUFDbEJDLGtCQUFrQixRQUNiLDRCQUE0QjtBQUNuQyxTQUFTQyxlQUFlLEVBQUVDLFNBQVMsUUFBUSx3QkFBd0I7QUFDbkUsU0FDRUMsc0JBQXNCLEVBQ3RCQyx5QkFBeUIsRUFDekJDLHFCQUFxQixFQUNyQkMsd0JBQXdCLEVBQ3hCQyxRQUFRLEVBQ1JDLFNBQVMsUUFDSixrQkFBa0I7QUFDekIsU0FDRUMsR0FBRyxFQUNIQyxHQUFHLEVBQ0hDLHNCQUFzQixFQUN0QkMsR0FBRyxFQUNIQyxHQUFHLEVBQ0hDLFdBQVcsRUFDWEMsV0FBVyxRQUNOLGtCQUFrQjtBQUN6QixPQUFPQyxVQUFVLE1BQU0saUJBQWlCO0FBQ3hDLFNBQVNDLGFBQWEsUUFBUSxtQkFBbUI7QUFDakQsT0FBT0Msd0JBQXdCLElBQzdCLEtBQUtDLHVCQUF1QixRQUN2QiwrQkFBK0I7QUFDdEMsT0FBT0MsYUFBYSxNQUFNLG9CQUFvQjtBQUM5QyxPQUFPQyxZQUFZLE1BQU0sbUJBQW1CO0FBQzVDLFNBQVNDLHFCQUFxQixRQUFRLDJCQUEyQjtBQUNqRSxTQUFTQyxtQkFBbUIsUUFBUSwwQkFBMEI7O0FBRTlEO0FBQ0EsTUFBTUMsZ0JBQWdCLEdBQUdDLE9BQU8sQ0FBQ0MsUUFBUSxLQUFLLE9BQU87O0FBRXJEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNQyxtQkFBbUIsR0FBRyxJQUFJO0FBRWhDLEtBQUtDLEtBQUssR0FBRztFQUNYLFNBQVNDLFFBQVEsRUFBRXJELFNBQVM7RUFDNUIsU0FBU3NELEtBQUssRUFBRUMsTUFBTSxDQUFDQyxVQUFVO0VBQ2pDLFNBQVNDLE1BQU0sRUFBRUYsTUFBTSxDQUFDRyxXQUFXO0VBQ25DLFNBQVNDLE1BQU0sRUFBRUosTUFBTSxDQUFDRyxXQUFXO0VBQ25DLFNBQVNFLFdBQVcsRUFBRSxPQUFPO0VBQzdCLFNBQVNDLE1BQU0sRUFBRSxDQUFDQyxLQUFhLENBQVAsRUFBRUMsS0FBSyxFQUFFLEdBQUcsSUFBSTtFQUN4QyxTQUFTQyxlQUFlLEVBQUUsTUFBTTtFQUNoQyxTQUFTQyxZQUFZLEVBQUUsTUFBTTtFQUM3QjtFQUNBO0VBQ0E7RUFDQTtFQUNBLFNBQVNDLFNBQVMsRUFBRWhELGNBQWM7RUFDbEMsU0FBU2lELGlCQUFpQixFQUFFLEdBQUcsR0FBRyxJQUFJO0VBQ3RDO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsU0FBU0MsU0FBUyxFQUFFLENBQUNDLEdBQUcsRUFBRSxNQUFNLEVBQUVDLEdBQUcsRUFBRSxNQUFNLEVBQUUsR0FBRyxPQUFPO0VBQ3pEO0VBQ0E7RUFDQTtFQUNBLFNBQVNDLFNBQVMsRUFBRSxDQUFDRixHQUFHLEVBQUUsTUFBTSxFQUFFQyxHQUFHLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSTtFQUN0RDtFQUNBO0VBQ0E7RUFDQSxTQUFTRSxjQUFjLEVBQUUsQ0FBQ0gsR0FBRyxFQUFFLE1BQU0sRUFBRUMsR0FBRyxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sR0FBRyxTQUFTO0VBQ3pFO0VBQ0EsU0FBU0csZUFBZSxFQUFFLENBQUNDLEdBQUcsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQy9DO0VBQ0E7RUFDQTtFQUNBO0VBQ0EsU0FBU0MsWUFBWSxFQUFFLENBQUNOLEdBQUcsRUFBRSxNQUFNLEVBQUVDLEdBQUcsRUFBRSxNQUFNLEVBQUVNLEtBQUssRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEdBQUcsSUFBSTtFQUN2RTtFQUNBO0VBQ0E7RUFDQSxTQUFTQyxlQUFlLEVBQUUsQ0FBQ1IsR0FBRyxFQUFFLE1BQU0sRUFBRUMsR0FBRyxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUk7RUFDNUQ7RUFDQTtFQUNBO0VBQ0E7RUFDQSxTQUFTUSxhQUFhLENBQUMsRUFBRSxHQUFHLEdBQUcsSUFBSTtFQUNuQztFQUNBO0VBQ0E7RUFDQTtFQUNBLFNBQVNDLG1CQUFtQixDQUFDLEVBQUVwQyx1QkFBdUI7RUFDdEQ7RUFDQTtFQUNBLFNBQVNxQyxxQkFBcUIsRUFBRSxDQUFDQyxTQUFTLEVBQUVyRSxTQUFTLEVBQUUsR0FBRyxJQUFJO0FBQ2hFLENBQUM7O0FBRUQ7QUFDQTtBQUNBLE1BQU1zRSxzQkFBc0IsR0FBRyxHQUFHO0FBQ2xDLE1BQU1DLG9CQUFvQixHQUFHLENBQUM7QUFFOUIsS0FBS0MsS0FBSyxHQUFHO0VBQ1gsU0FBU3RCLEtBQUssQ0FBQyxFQUFFQyxLQUFLO0FBQ3hCLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0EsZUFBZSxNQUFNc0IsR0FBRyxTQUFTdEYsYUFBYSxDQUFDcUQsS0FBSyxFQUFFZ0MsS0FBSyxDQUFDLENBQUM7RUFDM0QsT0FBT0UsV0FBVyxHQUFHLGFBQWE7RUFFbEMsT0FBT0Msd0JBQXdCQSxDQUFDekIsS0FBSyxFQUFFQyxLQUFLLEVBQUU7SUFDNUMsT0FBTztNQUFFRDtJQUFNLENBQUM7RUFDbEI7RUFFQSxTQUFTMEIsS0FBSyxHQUFHO0lBQ2YxQixLQUFLLEVBQUUyQjtFQUNULENBQUM7O0VBRUQ7RUFDQTtFQUNBQyxtQkFBbUIsR0FBRyxDQUFDO0VBRXZCQyxxQkFBcUIsR0FBRyxJQUFJcEYsWUFBWSxDQUFDLENBQUM7RUFDMUNxRixhQUFhLEdBQUdsRixhQUFhO0VBQzdCO0VBQ0FtRixxQkFBcUIsRUFBRXRDLE1BQU0sQ0FBQ3VDLE9BQU8sR0FBRyxJQUFJLEdBQUcsSUFBSTtFQUNuRDtFQUNBLFNBQVNDLGNBQWMsR0FBRyxFQUFFLEVBQUM7RUFDN0IsU0FBU0MsYUFBYSxHQUFHLEdBQUcsRUFBQzs7RUFFN0I7RUFDQTtFQUNBQyxPQUFPLEdBQUcsSUFBSXhFLGVBQWUsQ0FBQyxJQUFJLENBQUN5RSxLQUFLLENBQUN6QyxNQUFNLENBQUM7O0VBRWhEO0VBQ0E7RUFDQTtFQUNBMEMsYUFBYSxHQUFHLENBQUM7RUFDakJDLFlBQVksR0FBRyxDQUFDLENBQUM7RUFDakJDLFlBQVksR0FBRyxDQUFDLENBQUM7RUFDakJDLFVBQVUsR0FBRyxDQUFDO0VBQ2Q7RUFDQTtFQUNBO0VBQ0E7RUFDQUMscUJBQXFCLEVBQUVDLFVBQVUsQ0FBQyxPQUFPQyxVQUFVLENBQUMsR0FBRyxJQUFJLEdBQUcsSUFBSTtFQUNsRTtFQUNBO0VBQ0E7RUFDQUMsWUFBWSxHQUFHLENBQUMsQ0FBQztFQUNqQkMsWUFBWSxHQUFHLENBQUMsQ0FBQzs7RUFFakI7RUFDQTtFQUNBO0VBQ0FDLGFBQWEsR0FBR0MsSUFBSSxDQUFDQyxHQUFHLENBQUMsQ0FBQzs7RUFFMUI7RUFDQUMsa0JBQWtCQSxDQUFBLENBQUUsRUFBRSxPQUFPLENBQUM7SUFDNUIsT0FBTyxJQUFJLENBQUNiLEtBQUssQ0FBQzVDLEtBQUssQ0FBQzBELEtBQUs7RUFDL0I7RUFFQSxTQUFTQyxNQUFNQSxDQUFBLEVBQUc7SUFDaEIsT0FDRSxDQUFDLG1CQUFtQixDQUFDLFFBQVEsQ0FDM0IsS0FBSyxDQUFDLENBQUM7TUFDTEMsT0FBTyxFQUFFLElBQUksQ0FBQ2hCLEtBQUssQ0FBQ2xDLGVBQWU7TUFDbkNtRCxJQUFJLEVBQUUsSUFBSSxDQUFDakIsS0FBSyxDQUFDakM7SUFDbkIsQ0FBQyxDQUFDO0FBRVYsUUFBUSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQ2xCLEtBQUssQ0FBQyxDQUFDO1FBQ0xtRCxJQUFJLEVBQUUsSUFBSSxDQUFDQztNQUNiLENBQUMsQ0FBQztBQUVaLFVBQVUsQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUNwQixLQUFLLENBQUMsQ0FBQztVQUNML0QsS0FBSyxFQUFFLElBQUksQ0FBQzRDLEtBQUssQ0FBQzVDLEtBQUs7VUFDdkJnRSxVQUFVLEVBQUUsSUFBSSxDQUFDQyxnQkFBZ0I7VUFDakNSLGtCQUFrQixFQUFFLElBQUksQ0FBQ0Esa0JBQWtCLENBQUMsQ0FBQztVQUU3Q1Msb0JBQW9CLEVBQUUsSUFBSSxDQUFDdEIsS0FBSyxDQUFDdEMsV0FBVztVQUU1QytCLHFCQUFxQixFQUFFLElBQUksQ0FBQ0EscUJBQXFCO1VBQ2pEOEIsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDeEI7UUFDekIsQ0FBQyxDQUFDO0FBRWQsWUFBWSxDQUFDLHFCQUFxQjtBQUNsQyxjQUFjLENBQUMsYUFBYTtBQUM1QixnQkFBZ0IsQ0FBQyx3QkFBd0IsQ0FBQyxRQUFRLENBQ2hDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQ0MsS0FBSyxDQUFDbkIsbUJBQW1CLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBRXRFLGtCQUFrQixDQUFDLElBQUksQ0FBQ1MsS0FBSyxDQUFDMUIsS0FBSyxHQUNmLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQzBCLEtBQUssQ0FBQzFCLEtBQUssSUFBSUMsS0FBSyxDQUFDLEdBQUcsR0FFbkQsSUFBSSxDQUFDbUMsS0FBSyxDQUFDN0MsUUFDWjtBQUNuQixnQkFBZ0IsRUFBRSx3QkFBd0IsQ0FBQyxRQUFRO0FBQ25ELGNBQWMsRUFBRSxhQUFhO0FBQzdCLFlBQVksRUFBRSxxQkFBcUI7QUFDbkMsVUFBVSxFQUFFLFlBQVksQ0FBQyxRQUFRO0FBQ2pDLFFBQVEsRUFBRSxVQUFVLENBQUMsUUFBUTtBQUM3QixNQUFNLEVBQUUsbUJBQW1CLENBQUMsUUFBUSxDQUFDO0VBRW5DO0VBRUEsU0FBU3FFLGlCQUFpQkEsQ0FBQSxFQUFHO0lBQzNCO0lBQ0EsSUFDRSxJQUFJLENBQUN4QixLQUFLLENBQUN6QyxNQUFNLENBQUN1RCxLQUFLLElBQ3ZCLENBQUM1RyxXQUFXLENBQUM2QyxPQUFPLENBQUMwRSxHQUFHLENBQUNDLHlCQUF5QixDQUFDLEVBQ25EO01BQ0EsSUFBSSxDQUFDMUIsS0FBSyxDQUFDekMsTUFBTSxDQUFDb0UsS0FBSyxDQUFDdkYsV0FBVyxDQUFDO0lBQ3RDO0VBQ0Y7RUFFQSxTQUFTd0Ysb0JBQW9CQSxDQUFBLEVBQUc7SUFDOUIsSUFBSSxJQUFJLENBQUM1QixLQUFLLENBQUN6QyxNQUFNLENBQUN1RCxLQUFLLEVBQUU7TUFDM0IsSUFBSSxDQUFDZCxLQUFLLENBQUN6QyxNQUFNLENBQUNvRSxLQUFLLENBQUN0RixXQUFXLENBQUM7SUFDdEM7O0lBRUE7SUFDQSxJQUFJLElBQUksQ0FBQ3NELHFCQUFxQixFQUFFO01BQzlCa0MsWUFBWSxDQUFDLElBQUksQ0FBQ2xDLHFCQUFxQixDQUFDO01BQ3hDLElBQUksQ0FBQ0EscUJBQXFCLEdBQUcsSUFBSTtJQUNuQztJQUNBLElBQUksSUFBSSxDQUFDVSxxQkFBcUIsRUFBRTtNQUM5QndCLFlBQVksQ0FBQyxJQUFJLENBQUN4QixxQkFBcUIsQ0FBQztNQUN4QyxJQUFJLENBQUNBLHFCQUFxQixHQUFHLElBQUk7SUFDbkM7SUFDQTtJQUNBLElBQUksSUFBSSxDQUFDUSxrQkFBa0IsQ0FBQyxDQUFDLEVBQUU7TUFDN0IsSUFBSSxDQUFDUSxnQkFBZ0IsQ0FBQyxLQUFLLENBQUM7SUFDOUI7RUFDRjtFQUVBLFNBQVNTLGlCQUFpQkEsQ0FBQ2xFLEtBQUssRUFBRUMsS0FBSyxFQUFFO0lBQ3ZDLElBQUksQ0FBQ3NELFVBQVUsQ0FBQ3ZELEtBQUssQ0FBQztFQUN4QjtFQUVBeUQsZ0JBQWdCLEdBQUdBLENBQUNVLFNBQVMsRUFBRSxPQUFPLENBQUMsRUFBRSxJQUFJLElBQUk7SUFDL0MsTUFBTTtNQUFFM0U7SUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDNEMsS0FBSztJQUU1QixJQUFJLENBQUMsSUFBSSxDQUFDYSxrQkFBa0IsQ0FBQyxDQUFDLEVBQUU7TUFDOUIsSUFBSXpELEtBQUssS0FBS0wsT0FBTyxDQUFDSyxLQUFLLEVBQUU7UUFDM0IsTUFBTSxJQUFJUyxLQUFLLENBQ2IscU1BQ0YsQ0FBQztNQUNILENBQUMsTUFBTTtRQUNMLE1BQU0sSUFBSUEsS0FBSyxDQUNiLDBKQUNGLENBQUM7TUFDSDtJQUNGO0lBRUFULEtBQUssQ0FBQzRFLFdBQVcsQ0FBQyxNQUFNLENBQUM7SUFFekIsSUFBSUQsU0FBUyxFQUFFO01BQ2I7TUFDQSxJQUFJLElBQUksQ0FBQ3ZDLG1CQUFtQixLQUFLLENBQUMsRUFBRTtRQUNsQztRQUNBO1FBQ0E7UUFDQTtRQUNBdkYsdUJBQXVCLENBQUMsQ0FBQztRQUN6Qm1ELEtBQUssQ0FBQzZFLEdBQUcsQ0FBQyxDQUFDO1FBQ1g3RSxLQUFLLENBQUNnRSxVQUFVLENBQUMsSUFBSSxDQUFDO1FBQ3RCaEUsS0FBSyxDQUFDOEUsV0FBVyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUNDLGNBQWMsQ0FBQztRQUNsRDtRQUNBLElBQUksQ0FBQ25DLEtBQUssQ0FBQ3pDLE1BQU0sQ0FBQ29FLEtBQUssQ0FBQ3pGLEdBQUcsQ0FBQztRQUM1QjtRQUNBLElBQUksQ0FBQzhELEtBQUssQ0FBQ3pDLE1BQU0sQ0FBQ29FLEtBQUssQ0FBQ3hGLEdBQUcsQ0FBQztRQUM1QjtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0EsSUFBSWYsb0JBQW9CLENBQUMsQ0FBQyxFQUFFO1VBQzFCLElBQUksQ0FBQzRFLEtBQUssQ0FBQ3pDLE1BQU0sQ0FBQ29FLEtBQUssQ0FBQ2hHLHFCQUFxQixDQUFDO1VBQzlDLElBQUksQ0FBQ3FFLEtBQUssQ0FBQ3pDLE1BQU0sQ0FBQ29FLEtBQUssQ0FBQy9GLHdCQUF3QixDQUFDO1FBQ25EO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBd0csWUFBWSxDQUFDLE1BQU07VUFDakIsS0FBS0MsT0FBTyxDQUFDQyxHQUFHLENBQUMsQ0FDZixJQUFJLENBQUN2QyxPQUFPLENBQUN3QyxJQUFJLENBQUMvRyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQzlCLElBQUksQ0FBQ3VFLE9BQU8sQ0FBQ3lDLEtBQUssQ0FBQyxDQUFDLENBQ3JCLENBQUMsQ0FBQ0MsSUFBSSxDQUFDLENBQUMsQ0FBQ0MsQ0FBQyxDQUFDLEtBQUs7WUFDZixJQUFJQSxDQUFDLEVBQUU7Y0FDTHZILGdCQUFnQixDQUFDdUgsQ0FBQyxDQUFDQyxJQUFJLENBQUM7Y0FDeEIzSSxlQUFlLENBQUMsc0NBQXNDMEksQ0FBQyxDQUFDQyxJQUFJLEdBQUcsQ0FBQztZQUNsRSxDQUFDLE1BQU07Y0FDTDNJLGVBQWUsQ0FBQyw4Q0FBOEMsQ0FBQztZQUNqRTtVQUNGLENBQUMsQ0FBQztRQUNKLENBQUMsQ0FBQztNQUNKO01BRUEsSUFBSSxDQUFDd0YsbUJBQW1CLEVBQUU7TUFDMUI7SUFDRjs7SUFFQTtJQUNBLElBQUksRUFBRSxJQUFJLENBQUNBLG1CQUFtQixLQUFLLENBQUMsRUFBRTtNQUNwQyxJQUFJLENBQUNRLEtBQUssQ0FBQ3pDLE1BQU0sQ0FBQ29FLEtBQUssQ0FBQ2pHLHlCQUF5QixDQUFDO01BQ2xELElBQUksQ0FBQ3NFLEtBQUssQ0FBQ3pDLE1BQU0sQ0FBQ29FLEtBQUssQ0FBQ2xHLHNCQUFzQixDQUFDO01BQy9DO01BQ0EsSUFBSSxDQUFDdUUsS0FBSyxDQUFDekMsTUFBTSxDQUFDb0UsS0FBSyxDQUFDM0YsR0FBRyxDQUFDO01BQzVCO01BQ0EsSUFBSSxDQUFDZ0UsS0FBSyxDQUFDekMsTUFBTSxDQUFDb0UsS0FBSyxDQUFDNUYsR0FBRyxDQUFDO01BQzVCcUIsS0FBSyxDQUFDZ0UsVUFBVSxDQUFDLEtBQUssQ0FBQztNQUN2QmhFLEtBQUssQ0FBQ3dGLGNBQWMsQ0FBQyxVQUFVLEVBQUUsSUFBSSxDQUFDVCxjQUFjLENBQUM7TUFDckQvRSxLQUFLLENBQUN5RixLQUFLLENBQUMsQ0FBQztJQUNmO0VBQ0YsQ0FBQzs7RUFFRDtFQUNBQyxlQUFlLEdBQUdBLENBQUEsQ0FBRSxFQUFFLElBQUksSUFBSTtJQUM1QjtJQUNBLElBQUksQ0FBQ25ELHFCQUFxQixHQUFHLElBQUk7O0lBRWpDO0lBQ0EsSUFBSSxDQUFDLElBQUksQ0FBQ0QsYUFBYSxDQUFDcUQsVUFBVSxFQUFFOztJQUVwQztJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0EsSUFBSSxJQUFJLENBQUMvQyxLQUFLLENBQUM1QyxLQUFLLENBQUM0RixjQUFjLEdBQUcsQ0FBQyxFQUFFO01BQ3ZDLElBQUksQ0FBQ3JELHFCQUFxQixHQUFHWSxVQUFVLENBQ3JDLElBQUksQ0FBQ3VDLGVBQWUsRUFDcEIsSUFBSSxDQUFDakQsY0FDUCxDQUFDO01BQ0Q7SUFDRjs7SUFFQTtJQUNBO0lBQ0EsSUFBSSxDQUFDb0QsWUFBWSxDQUFDLElBQUksQ0FBQztFQUN6QixDQUFDOztFQUVEO0VBQ0FBLFlBQVksR0FBR0EsQ0FBQ0MsS0FBSyxFQUFFLE1BQU0sR0FBR0MsTUFBTSxHQUFHLElBQUksQ0FBQyxFQUFFLElBQUksSUFBSTtJQUN0RDtJQUNBLE1BQU0sQ0FBQ0MsSUFBSSxFQUFFQyxRQUFRLENBQUMsR0FBR3pJLHVCQUF1QixDQUFDLElBQUksQ0FBQzhFLGFBQWEsRUFBRXdELEtBQUssQ0FBQztJQUMzRSxJQUFJLENBQUN4RCxhQUFhLEdBQUcyRCxRQUFROztJQUU3QjtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0EsSUFBSUQsSUFBSSxDQUFDRSxNQUFNLEdBQUcsQ0FBQyxFQUFFO01BQ25CekksVUFBVSxDQUFDMEksZUFBZSxDQUN4QkMsa0JBQWtCLEVBQ2xCLElBQUksRUFDSkosSUFBSSxFQUNKN0QsU0FBUyxFQUNUQSxTQUNGLENBQUM7SUFDSDs7SUFFQTtJQUNBLElBQUksSUFBSSxDQUFDRyxhQUFhLENBQUNxRCxVQUFVLEVBQUU7TUFDakM7TUFDQSxJQUFJLElBQUksQ0FBQ3BELHFCQUFxQixFQUFFO1FBQzlCa0MsWUFBWSxDQUFDLElBQUksQ0FBQ2xDLHFCQUFxQixDQUFDO01BQzFDO01BQ0EsSUFBSSxDQUFDQSxxQkFBcUIsR0FBR1ksVUFBVSxDQUNyQyxJQUFJLENBQUN1QyxlQUFlLEVBQ3BCLElBQUksQ0FBQ3BELGFBQWEsQ0FBQytELElBQUksS0FBSyxVQUFVLEdBQ2xDLElBQUksQ0FBQzNELGFBQWEsR0FDbEIsSUFBSSxDQUFDRCxjQUNYLENBQUM7SUFDSDtFQUNGLENBQUM7RUFFRHNDLGNBQWMsR0FBR0EsQ0FBQSxDQUFFLEVBQUUsSUFBSSxJQUFJO0lBQzNCO0lBQ0E7SUFDQTtJQUNBO0lBQ0EsTUFBTXZCLEdBQUcsR0FBR0QsSUFBSSxDQUFDQyxHQUFHLENBQUMsQ0FBQztJQUN0QixJQUFJQSxHQUFHLEdBQUcsSUFBSSxDQUFDRixhQUFhLEdBQUd6RCxtQkFBbUIsRUFBRTtNQUNsRCxJQUFJLENBQUMrQyxLQUFLLENBQUNwQixhQUFhLEdBQUcsQ0FBQztJQUM5QjtJQUNBLElBQUksQ0FBQzhCLGFBQWEsR0FBR0UsR0FBRztJQUN4QixJQUFJO01BQ0YsSUFBSThDLEtBQUs7TUFDVCxPQUFPLENBQUNBLEtBQUssR0FBRyxJQUFJLENBQUMxRCxLQUFLLENBQUM1QyxLQUFLLENBQUN1RyxJQUFJLENBQUMsQ0FBQyxJQUFJLE1BQU0sR0FBRyxJQUFJLE1BQU0sSUFBSSxFQUFFO1FBQ2xFO1FBQ0EsSUFBSSxDQUFDVixZQUFZLENBQUNTLEtBQUssQ0FBQztNQUMxQjtJQUNGLENBQUMsQ0FBQyxPQUFPOUYsS0FBSyxFQUFFO01BQ2Q7TUFDQTtNQUNBO01BQ0E7TUFDQXhELFFBQVEsQ0FBQ3dELEtBQUssQ0FBQzs7TUFFZjtNQUNBO01BQ0E7TUFDQSxNQUFNO1FBQUVSO01BQU0sQ0FBQyxHQUFHLElBQUksQ0FBQzRDLEtBQUs7TUFDNUIsSUFDRSxJQUFJLENBQUNSLG1CQUFtQixHQUFHLENBQUMsSUFDNUIsQ0FBQ3BDLEtBQUssQ0FBQ3dHLFNBQVMsQ0FBQyxVQUFVLENBQUMsQ0FBQ0MsUUFBUSxDQUFDLElBQUksQ0FBQzFCLGNBQWMsQ0FBQyxFQUMxRDtRQUNBbkksZUFBZSxDQUNiLDJFQUEyRSxFQUMzRTtVQUFFOEosS0FBSyxFQUFFO1FBQU8sQ0FDbEIsQ0FBQztRQUNEMUcsS0FBSyxDQUFDOEUsV0FBVyxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUNDLGNBQWMsQ0FBQztNQUNwRDtJQUNGO0VBQ0YsQ0FBQztFQUVENEIsV0FBVyxHQUFHQSxDQUFDYixLQUFLLEVBQUUsTUFBTSxHQUFHLFNBQVMsQ0FBQyxFQUFFLElBQUksSUFBSTtJQUNqRDtJQUNBLElBQUlBLEtBQUssS0FBSyxNQUFNLElBQUksSUFBSSxDQUFDbEQsS0FBSyxDQUFDdEMsV0FBVyxFQUFFO01BQzlDLElBQUksQ0FBQ3lELFVBQVUsQ0FBQyxDQUFDO0lBQ25COztJQUVBO0lBQ0E7SUFDQTtFQUNGLENBQUM7RUFFREEsVUFBVSxHQUFHQSxDQUFDdkQsS0FBYSxDQUFQLEVBQUVDLEtBQUssQ0FBQyxFQUFFLElBQUksSUFBSTtJQUNwQyxJQUFJLElBQUksQ0FBQ2dELGtCQUFrQixDQUFDLENBQUMsRUFBRTtNQUM3QixJQUFJLENBQUNRLGdCQUFnQixDQUFDLEtBQUssQ0FBQztJQUM5QjtJQUVBLElBQUksQ0FBQ3JCLEtBQUssQ0FBQ3JDLE1BQU0sQ0FBQ0MsS0FBSyxDQUFDO0VBQzFCLENBQUM7RUFFRG9HLG1CQUFtQixHQUFHQSxDQUFDQyxTQUFTLEVBQUUsT0FBTyxDQUFDLEVBQUUsSUFBSSxJQUFJO0lBQ2xEO0lBQ0E7SUFDQTNJLGtCQUFrQixDQUFDMkksU0FBUyxDQUFDO0VBQy9CLENBQUM7RUFFREMsYUFBYSxHQUFHQSxDQUFBLENBQUUsRUFBRSxJQUFJLElBQUk7SUFDMUIsSUFBSSxDQUFDLElBQUksQ0FBQ3JELGtCQUFrQixDQUFDLENBQUMsRUFBRTtNQUM5QjtJQUNGOztJQUVBO0lBQ0EsTUFBTXNELHlCQUF5QixHQUFHLElBQUksQ0FBQzNFLG1CQUFtQjs7SUFFMUQ7SUFDQSxPQUFPLElBQUksQ0FBQ0EsbUJBQW1CLEdBQUcsQ0FBQyxFQUFFO01BQ25DLElBQUksQ0FBQzZCLGdCQUFnQixDQUFDLEtBQUssQ0FBQztJQUM5Qjs7SUFFQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0EsSUFBSSxJQUFJLENBQUNyQixLQUFLLENBQUN6QyxNQUFNLENBQUN1RCxLQUFLLEVBQUU7TUFDM0IsSUFBSSxDQUFDZCxLQUFLLENBQUN6QyxNQUFNLENBQUNvRSxLQUFLLENBQUN0RixXQUFXLEdBQUdMLEdBQUcsR0FBR0Msc0JBQXNCLENBQUM7SUFDckU7O0lBRUE7SUFDQSxJQUFJLENBQUN3RCxxQkFBcUIsQ0FBQzJFLElBQUksQ0FBQyxTQUFTLENBQUM7O0lBRTFDO0lBQ0EsTUFBTUMsYUFBYSxHQUFHQSxDQUFBLEtBQU07TUFDMUI7TUFDQSxLQUFLLElBQUlDLENBQUMsR0FBRyxDQUFDLEVBQUVBLENBQUMsR0FBR0gseUJBQXlCLEVBQUVHLENBQUMsRUFBRSxFQUFFO1FBQ2xELElBQUksSUFBSSxDQUFDekQsa0JBQWtCLENBQUMsQ0FBQyxFQUFFO1VBQzdCLElBQUksQ0FBQ1EsZ0JBQWdCLENBQUMsSUFBSSxDQUFDO1FBQzdCO01BQ0Y7O01BRUE7TUFDQSxJQUFJLElBQUksQ0FBQ3JCLEtBQUssQ0FBQ3pDLE1BQU0sQ0FBQ3VELEtBQUssRUFBRTtRQUMzQixJQUFJLENBQUM1RyxXQUFXLENBQUM2QyxPQUFPLENBQUMwRSxHQUFHLENBQUNDLHlCQUF5QixDQUFDLEVBQUU7VUFDdkQsSUFBSSxDQUFDMUIsS0FBSyxDQUFDekMsTUFBTSxDQUFDb0UsS0FBSyxDQUFDdkYsV0FBVyxDQUFDO1FBQ3RDO1FBQ0E7UUFDQSxJQUFJLENBQUM0RCxLQUFLLENBQUN6QyxNQUFNLENBQUNvRSxLQUFLLENBQUN4RixHQUFHLENBQUM7TUFDOUI7O01BRUE7TUFDQSxJQUFJLENBQUNzRCxxQkFBcUIsQ0FBQzJFLElBQUksQ0FBQyxRQUFRLENBQUM7TUFFekNySCxPQUFPLENBQUM2RixjQUFjLENBQUMsU0FBUyxFQUFFeUIsYUFBYSxDQUFDO0lBQ2xELENBQUM7SUFFRHRILE9BQU8sQ0FBQ3dILEVBQUUsQ0FBQyxTQUFTLEVBQUVGLGFBQWEsQ0FBQztJQUNwQ3RILE9BQU8sQ0FBQ3lILElBQUksQ0FBQ3pILE9BQU8sQ0FBQzBILEdBQUcsRUFBRSxTQUFTLENBQUM7RUFDdEMsQ0FBQztBQUNIOztBQUVBO0FBQ0E7QUFDQSxTQUFTakIsa0JBQWtCQSxDQUN6QmtCLEdBQUcsRUFBRXZGLEdBQUcsRUFDUndGLEtBQUssRUFBRWxLLFdBQVcsRUFBRSxFQUNwQm1LLFFBQVEsRUFBRSxTQUFTLEVBQ25CQyxRQUFRLEVBQUUsU0FBUyxDQUNwQixFQUFFLElBQUksQ0FBQztFQUNOO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBLElBQ0VGLEtBQUssQ0FBQ0csSUFBSSxDQUNSUixDQUFDLElBQ0NBLENBQUMsQ0FBQ1MsSUFBSSxLQUFLLEtBQUssSUFDZlQsQ0FBQyxDQUFDUyxJQUFJLEtBQUssT0FBTyxJQUNqQixFQUFFLENBQUNULENBQUMsQ0FBQ1UsTUFBTSxHQUFHLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQ1YsQ0FBQyxDQUFDVSxNQUFNLEdBQUcsSUFBSSxNQUFNLENBQUMsQ0FDMUQsQ0FBQyxFQUNEO0lBQ0FqTCx5QkFBeUIsQ0FBQyxDQUFDO0VBQzdCO0VBRUEsS0FBSyxNQUFNa0wsSUFBSSxJQUFJTixLQUFLLEVBQUU7SUFDeEI7SUFDQTtJQUNBLElBQUlNLElBQUksQ0FBQ0YsSUFBSSxLQUFLLFVBQVUsRUFBRTtNQUM1QkwsR0FBRyxDQUFDM0UsT0FBTyxDQUFDbUYsVUFBVSxDQUFDRCxJQUFJLENBQUNFLFFBQVEsQ0FBQztNQUNyQztJQUNGOztJQUVBO0lBQ0E7SUFDQTtJQUNBLElBQUlGLElBQUksQ0FBQ0YsSUFBSSxLQUFLLE9BQU8sRUFBRTtNQUN6QkssZ0JBQWdCLENBQUNWLEdBQUcsRUFBRU8sSUFBSSxDQUFDO01BQzNCO0lBQ0Y7SUFFQSxNQUFNSSxRQUFRLEdBQUdKLElBQUksQ0FBQ0ksUUFBUTs7SUFFOUI7SUFDQSxJQUFJQSxRQUFRLEtBQUt4SixRQUFRLEVBQUU7TUFDekI2SSxHQUFHLENBQUNWLG1CQUFtQixDQUFDLElBQUksQ0FBQztNQUM3QixNQUFNc0IsS0FBSyxHQUFHLElBQUkvSyxrQkFBa0IsQ0FBQyxlQUFlLENBQUM7TUFDckRtSyxHQUFHLENBQUNqRixxQkFBcUIsQ0FBQzJFLElBQUksQ0FBQyxlQUFlLEVBQUVrQixLQUFLLENBQUM7TUFDdEQ7SUFDRjtJQUNBLElBQUlELFFBQVEsS0FBS3ZKLFNBQVMsRUFBRTtNQUMxQjRJLEdBQUcsQ0FBQ1YsbUJBQW1CLENBQUMsS0FBSyxDQUFDO01BQzlCO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQSxJQUFJVSxHQUFHLENBQUMxRSxLQUFLLENBQUNoQyxTQUFTLENBQUN1SCxVQUFVLEVBQUU7UUFDbEN6SyxlQUFlLENBQUM0SixHQUFHLENBQUMxRSxLQUFLLENBQUNoQyxTQUFTLENBQUM7UUFDcEMwRyxHQUFHLENBQUMxRSxLQUFLLENBQUMvQixpQkFBaUIsQ0FBQyxDQUFDO01BQy9CO01BQ0EsTUFBTXFILEtBQUssR0FBRyxJQUFJL0ssa0JBQWtCLENBQUMsY0FBYyxDQUFDO01BQ3BEbUssR0FBRyxDQUFDakYscUJBQXFCLENBQUMyRSxJQUFJLENBQUMsY0FBYyxFQUFFa0IsS0FBSyxDQUFDO01BQ3JEO0lBQ0Y7O0lBRUE7SUFDQSxJQUFJLENBQUNqSyxrQkFBa0IsQ0FBQyxDQUFDLEVBQUU7TUFDekJDLGtCQUFrQixDQUFDLElBQUksQ0FBQztJQUMxQjs7SUFFQTtJQUNBO0lBQ0EsSUFBSTJKLElBQUksQ0FBQ3RDLElBQUksS0FBSyxHQUFHLElBQUlzQyxJQUFJLENBQUNPLElBQUksSUFBSTFJLGdCQUFnQixFQUFFO01BQ3RENEgsR0FBRyxDQUFDUixhQUFhLENBQUMsQ0FBQztNQUNuQjtJQUNGO0lBRUFRLEdBQUcsQ0FBQ1gsV0FBVyxDQUFDc0IsUUFBUSxDQUFDO0lBQ3pCLE1BQU1DLEtBQUssR0FBRyxJQUFJaEwsVUFBVSxDQUFDMkssSUFBSSxDQUFDO0lBQ2xDUCxHQUFHLENBQUNqRixxQkFBcUIsQ0FBQzJFLElBQUksQ0FBQyxPQUFPLEVBQUVrQixLQUFLLENBQUM7O0lBRTlDO0lBQ0FaLEdBQUcsQ0FBQzFFLEtBQUssQ0FBQ2xCLHFCQUFxQixDQUFDbUcsSUFBSSxDQUFDO0VBQ3ZDO0FBQ0Y7O0FBRUE7QUFDQSxPQUFPLFNBQVNHLGdCQUFnQkEsQ0FBQ1YsR0FBRyxFQUFFdkYsR0FBRyxFQUFFc0csQ0FBQyxFQUFFOUssV0FBVyxDQUFDLEVBQUUsSUFBSSxDQUFDO0VBQy9EO0VBQ0E7RUFDQSxJQUFJUixxQkFBcUIsQ0FBQyxDQUFDLEVBQUU7RUFFN0IsTUFBTXVMLEdBQUcsR0FBR2hCLEdBQUcsQ0FBQzFFLEtBQUssQ0FBQ2hDLFNBQVM7RUFDL0I7RUFDQSxNQUFNRyxHQUFHLEdBQUdzSCxDQUFDLENBQUN0SCxHQUFHLEdBQUcsQ0FBQztFQUNyQixNQUFNQyxHQUFHLEdBQUdxSCxDQUFDLENBQUNySCxHQUFHLEdBQUcsQ0FBQztFQUNyQixNQUFNdUgsVUFBVSxHQUFHRixDQUFDLENBQUNULE1BQU0sR0FBRyxJQUFJO0VBRWxDLElBQUlTLENBQUMsQ0FBQ0csTUFBTSxLQUFLLE9BQU8sRUFBRTtJQUN4QixJQUFJLENBQUNILENBQUMsQ0FBQ1QsTUFBTSxHQUFHLElBQUksTUFBTSxDQUFDLElBQUlXLFVBQVUsS0FBSyxDQUFDLEVBQUU7TUFDL0M7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0EsSUFBSUQsR0FBRyxDQUFDSCxVQUFVLEVBQUU7UUFDbEJ6SyxlQUFlLENBQUM0SyxHQUFHLENBQUM7UUFDcEJoQixHQUFHLENBQUMxRSxLQUFLLENBQUMvQixpQkFBaUIsQ0FBQyxDQUFDO01BQy9CO01BQ0EsSUFBSUUsR0FBRyxLQUFLdUcsR0FBRyxDQUFDbEUsWUFBWSxJQUFJcEMsR0FBRyxLQUFLc0csR0FBRyxDQUFDakUsWUFBWSxFQUFFO01BQzFEaUUsR0FBRyxDQUFDbEUsWUFBWSxHQUFHckMsR0FBRztNQUN0QnVHLEdBQUcsQ0FBQ2pFLFlBQVksR0FBR3JDLEdBQUc7TUFDdEJzRyxHQUFHLENBQUMxRSxLQUFLLENBQUMzQixTQUFTLENBQUNGLEdBQUcsRUFBRUMsR0FBRyxDQUFDO01BQzdCO0lBQ0Y7SUFDQSxJQUFJdUgsVUFBVSxLQUFLLENBQUMsRUFBRTtNQUNwQjtNQUNBakIsR0FBRyxDQUFDdEUsVUFBVSxHQUFHLENBQUM7TUFDbEI7SUFDRjtJQUNBLElBQUksQ0FBQ3FGLENBQUMsQ0FBQ1QsTUFBTSxHQUFHLElBQUksTUFBTSxDQUFDLEVBQUU7TUFDM0I7TUFDQTtNQUNBTixHQUFHLENBQUMxRSxLQUFLLENBQUNyQixlQUFlLENBQUNSLEdBQUcsRUFBRUMsR0FBRyxDQUFDO01BQ25DO0lBQ0Y7SUFDQTtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0EsSUFBSXNILEdBQUcsQ0FBQ0gsVUFBVSxFQUFFO01BQ2xCekssZUFBZSxDQUFDNEssR0FBRyxDQUFDO01BQ3BCaEIsR0FBRyxDQUFDMUUsS0FBSyxDQUFDL0IsaUJBQWlCLENBQUMsQ0FBQztJQUMvQjtJQUNBO0lBQ0E7SUFDQTtJQUNBO0lBQ0E7SUFDQSxNQUFNMkMsR0FBRyxHQUFHRCxJQUFJLENBQUNDLEdBQUcsQ0FBQyxDQUFDO0lBQ3RCLE1BQU1pRixRQUFRLEdBQ1pqRixHQUFHLEdBQUc4RCxHQUFHLENBQUN6RSxhQUFhLEdBQUdqQixzQkFBc0IsSUFDaEQ4RyxJQUFJLENBQUNDLEdBQUcsQ0FBQzVILEdBQUcsR0FBR3VHLEdBQUcsQ0FBQ3hFLFlBQVksQ0FBQyxJQUFJakIsb0JBQW9CLElBQ3hENkcsSUFBSSxDQUFDQyxHQUFHLENBQUMzSCxHQUFHLEdBQUdzRyxHQUFHLENBQUN2RSxZQUFZLENBQUMsSUFBSWxCLG9CQUFvQjtJQUMxRHlGLEdBQUcsQ0FBQ3RFLFVBQVUsR0FBR3lGLFFBQVEsR0FBR25CLEdBQUcsQ0FBQ3RFLFVBQVUsR0FBRyxDQUFDLEdBQUcsQ0FBQztJQUNsRHNFLEdBQUcsQ0FBQ3pFLGFBQWEsR0FBR1csR0FBRztJQUN2QjhELEdBQUcsQ0FBQ3hFLFlBQVksR0FBRy9CLEdBQUc7SUFDdEJ1RyxHQUFHLENBQUN2RSxZQUFZLEdBQUcvQixHQUFHO0lBQ3RCLElBQUlzRyxHQUFHLENBQUN0RSxVQUFVLElBQUksQ0FBQyxFQUFFO01BQ3ZCO01BQ0E7TUFDQSxJQUFJc0UsR0FBRyxDQUFDckUscUJBQXFCLEVBQUU7UUFDN0J3QixZQUFZLENBQUM2QyxHQUFHLENBQUNyRSxxQkFBcUIsQ0FBQztRQUN2Q3FFLEdBQUcsQ0FBQ3JFLHFCQUFxQixHQUFHLElBQUk7TUFDbEM7TUFDQTtNQUNBLE1BQU0zQixLQUFLLEdBQUdnRyxHQUFHLENBQUN0RSxVQUFVLEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDO01BQzFDc0UsR0FBRyxDQUFDMUUsS0FBSyxDQUFDdkIsWUFBWSxDQUFDTixHQUFHLEVBQUVDLEdBQUcsRUFBRU0sS0FBSyxDQUFDO01BQ3ZDO0lBQ0Y7SUFDQXpELGNBQWMsQ0FBQ3lLLEdBQUcsRUFBRXZILEdBQUcsRUFBRUMsR0FBRyxDQUFDO0lBQzdCO0lBQ0E7SUFDQTtJQUNBO0lBQ0FzSCxHQUFHLENBQUNNLGVBQWUsR0FBRyxDQUFDUCxDQUFDLENBQUNULE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FBQztJQUM3Q04sR0FBRyxDQUFDMUUsS0FBSyxDQUFDL0IsaUJBQWlCLENBQUMsQ0FBQztJQUM3QjtFQUNGOztFQUVBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBLElBQUkwSCxVQUFVLEtBQUssQ0FBQyxFQUFFO0lBQ3BCLElBQUksQ0FBQ0QsR0FBRyxDQUFDSCxVQUFVLEVBQUU7SUFDckJ6SyxlQUFlLENBQUM0SyxHQUFHLENBQUM7SUFDcEJoQixHQUFHLENBQUMxRSxLQUFLLENBQUMvQixpQkFBaUIsQ0FBQyxDQUFDO0lBQzdCO0VBQ0Y7RUFDQW5ELGVBQWUsQ0FBQzRLLEdBQUcsQ0FBQztFQUNwQjtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQSxJQUFJLENBQUMzSyxZQUFZLENBQUMySyxHQUFHLENBQUMsSUFBSUEsR0FBRyxDQUFDTyxNQUFNLEVBQUU7SUFDcEM7SUFDQTtJQUNBO0lBQ0EsSUFBSSxDQUFDdkIsR0FBRyxDQUFDMUUsS0FBSyxDQUFDOUIsU0FBUyxDQUFDQyxHQUFHLEVBQUVDLEdBQUcsQ0FBQyxFQUFFO01BQ2xDO01BQ0E7TUFDQTtNQUNBLE1BQU1JLEdBQUcsR0FBR2tHLEdBQUcsQ0FBQzFFLEtBQUssQ0FBQzFCLGNBQWMsQ0FBQ0gsR0FBRyxFQUFFQyxHQUFHLENBQUM7TUFDOUM7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBO01BQ0E7TUFDQTtNQUNBLElBQUlJLEdBQUcsSUFBSXpCLE9BQU8sQ0FBQzBFLEdBQUcsQ0FBQ3lFLFlBQVksS0FBSyxRQUFRLElBQUksQ0FBQ2hMLFNBQVMsQ0FBQyxDQUFDLEVBQUU7UUFDaEU7UUFDQTtRQUNBLElBQUl3SixHQUFHLENBQUNyRSxxQkFBcUIsRUFBRTtVQUM3QndCLFlBQVksQ0FBQzZDLEdBQUcsQ0FBQ3JFLHFCQUFxQixDQUFDO1FBQ3pDO1FBQ0FxRSxHQUFHLENBQUNyRSxxQkFBcUIsR0FBR0UsVUFBVSxDQUNwQyxDQUFDbUUsR0FBRyxFQUFFbEcsR0FBRyxLQUFLO1VBQ1prRyxHQUFHLENBQUNyRSxxQkFBcUIsR0FBRyxJQUFJO1VBQ2hDcUUsR0FBRyxDQUFDMUUsS0FBSyxDQUFDekIsZUFBZSxDQUFDQyxHQUFHLENBQUM7UUFDaEMsQ0FBQyxFQUNEUSxzQkFBc0IsRUFDdEIwRixHQUFHLEVBQ0hsRyxHQUNGLENBQUM7TUFDSDtJQUNGO0VBQ0Y7RUFDQWtHLEdBQUcsQ0FBQzFFLEtBQUssQ0FBQy9CLGlCQUFpQixDQUFDLENBQUM7QUFDL0IiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/ink/components/AppContext.ts b/ink/components/AppContext.ts new file mode 100644 index 0000000..c0409c4 --- /dev/null +++ b/ink/components/AppContext.ts @@ -0,0 +1,21 @@ +import { createContext } from 'react' + +export type Props = { + /** + * Exit (unmount) the whole Ink app. + */ + readonly exit: (error?: Error) => void +} + +/** + * `AppContext` is a React context, which exposes a method to manually exit the app (unmount). + */ +// eslint-disable-next-line @typescript-eslint/naming-convention +const AppContext = createContext({ + exit() {}, +}) + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +AppContext.displayName = 'InternalAppContext' + +export default AppContext diff --git a/ink/components/Box.tsx b/ink/components/Box.tsx new file mode 100644 index 0000000..67f2500 --- /dev/null +++ b/ink/components/Box.tsx @@ -0,0 +1,214 @@ +import { c as _c } from "react/compiler-runtime"; +import '../global.d.ts'; +import React, { type PropsWithChildren, type Ref } from 'react'; +import type { Except } from 'type-fest'; +import type { DOMElement } from '../dom.js'; +import type { ClickEvent } from '../events/click-event.js'; +import type { FocusEvent } from '../events/focus-event.js'; +import type { KeyboardEvent } from '../events/keyboard-event.js'; +import type { Styles } from '../styles.js'; +import * as warn from '../warn.js'; +export type Props = Except & { + ref?: Ref; + /** + * Tab order index. Nodes with `tabIndex >= 0` participate in + * Tab/Shift+Tab cycling; `-1` means programmatically focusable only. + */ + tabIndex?: number; + /** + * Focus this element when it mounts. Like the HTML `autofocus` + * attribute — the FocusManager calls `focus(node)` during the + * reconciler's `commitMount` phase. + */ + autoFocus?: boolean; + /** + * Fired on left-button click (press + release without drag). Only works + * inside `` where mouse tracking is enabled — no-op + * otherwise. The event bubbles from the deepest hit Box up through + * ancestors; call `event.stopImmediatePropagation()` to stop bubbling. + */ + onClick?: (event: ClickEvent) => void; + onFocus?: (event: FocusEvent) => void; + onFocusCapture?: (event: FocusEvent) => void; + onBlur?: (event: FocusEvent) => void; + onBlurCapture?: (event: FocusEvent) => void; + onKeyDown?: (event: KeyboardEvent) => void; + onKeyDownCapture?: (event: KeyboardEvent) => void; + /** + * Fired when the mouse moves into this Box's rendered rect. Like DOM + * `mouseenter`, does NOT bubble — moving between children does not + * re-fire on the parent. Only works inside `` where + * mode-1003 mouse tracking is enabled. + */ + onMouseEnter?: () => void; + /** Fired when the mouse moves out of this Box's rendered rect. */ + onMouseLeave?: () => void; +}; + +/** + * `` is an essential Ink component to build your layout. It's like `
` in the browser. + */ +function Box(t0) { + const $ = _c(42); + let autoFocus; + let children; + let flexDirection; + let flexGrow; + let flexShrink; + let flexWrap; + let onBlur; + let onBlurCapture; + let onClick; + let onFocus; + let onFocusCapture; + let onKeyDown; + let onKeyDownCapture; + let onMouseEnter; + let onMouseLeave; + let ref; + let style; + let tabIndex; + if ($[0] !== t0) { + const { + children: t1, + flexWrap: t2, + flexDirection: t3, + flexGrow: t4, + flexShrink: t5, + ref: t6, + tabIndex: t7, + autoFocus: t8, + onClick: t9, + onFocus: t10, + onFocusCapture: t11, + onBlur: t12, + onBlurCapture: t13, + onMouseEnter: t14, + onMouseLeave: t15, + onKeyDown: t16, + onKeyDownCapture: t17, + ...t18 + } = t0; + children = t1; + ref = t6; + tabIndex = t7; + autoFocus = t8; + onClick = t9; + onFocus = t10; + onFocusCapture = t11; + onBlur = t12; + onBlurCapture = t13; + onMouseEnter = t14; + onMouseLeave = t15; + onKeyDown = t16; + onKeyDownCapture = t17; + style = t18; + flexWrap = t2 === undefined ? "nowrap" : t2; + flexDirection = t3 === undefined ? "row" : t3; + flexGrow = t4 === undefined ? 0 : t4; + flexShrink = t5 === undefined ? 1 : t5; + warn.ifNotInteger(style.margin, "margin"); + warn.ifNotInteger(style.marginX, "marginX"); + warn.ifNotInteger(style.marginY, "marginY"); + warn.ifNotInteger(style.marginTop, "marginTop"); + warn.ifNotInteger(style.marginBottom, "marginBottom"); + warn.ifNotInteger(style.marginLeft, "marginLeft"); + warn.ifNotInteger(style.marginRight, "marginRight"); + warn.ifNotInteger(style.padding, "padding"); + warn.ifNotInteger(style.paddingX, "paddingX"); + warn.ifNotInteger(style.paddingY, "paddingY"); + warn.ifNotInteger(style.paddingTop, "paddingTop"); + warn.ifNotInteger(style.paddingBottom, "paddingBottom"); + warn.ifNotInteger(style.paddingLeft, "paddingLeft"); + warn.ifNotInteger(style.paddingRight, "paddingRight"); + warn.ifNotInteger(style.gap, "gap"); + warn.ifNotInteger(style.columnGap, "columnGap"); + warn.ifNotInteger(style.rowGap, "rowGap"); + $[0] = t0; + $[1] = autoFocus; + $[2] = children; + $[3] = flexDirection; + $[4] = flexGrow; + $[5] = flexShrink; + $[6] = flexWrap; + $[7] = onBlur; + $[8] = onBlurCapture; + $[9] = onClick; + $[10] = onFocus; + $[11] = onFocusCapture; + $[12] = onKeyDown; + $[13] = onKeyDownCapture; + $[14] = onMouseEnter; + $[15] = onMouseLeave; + $[16] = ref; + $[17] = style; + $[18] = tabIndex; + } else { + autoFocus = $[1]; + children = $[2]; + flexDirection = $[3]; + flexGrow = $[4]; + flexShrink = $[5]; + flexWrap = $[6]; + onBlur = $[7]; + onBlurCapture = $[8]; + onClick = $[9]; + onFocus = $[10]; + onFocusCapture = $[11]; + onKeyDown = $[12]; + onKeyDownCapture = $[13]; + onMouseEnter = $[14]; + onMouseLeave = $[15]; + ref = $[16]; + style = $[17]; + tabIndex = $[18]; + } + const t1 = style.overflowX ?? style.overflow ?? "visible"; + const t2 = style.overflowY ?? style.overflow ?? "visible"; + let t3; + if ($[19] !== flexDirection || $[20] !== flexGrow || $[21] !== flexShrink || $[22] !== flexWrap || $[23] !== style || $[24] !== t1 || $[25] !== t2) { + t3 = { + flexWrap, + flexDirection, + flexGrow, + flexShrink, + ...style, + overflowX: t1, + overflowY: t2 + }; + $[19] = flexDirection; + $[20] = flexGrow; + $[21] = flexShrink; + $[22] = flexWrap; + $[23] = style; + $[24] = t1; + $[25] = t2; + $[26] = t3; + } else { + t3 = $[26]; + } + let t4; + if ($[27] !== autoFocus || $[28] !== children || $[29] !== onBlur || $[30] !== onBlurCapture || $[31] !== onClick || $[32] !== onFocus || $[33] !== onFocusCapture || $[34] !== onKeyDown || $[35] !== onKeyDownCapture || $[36] !== onMouseEnter || $[37] !== onMouseLeave || $[38] !== ref || $[39] !== t3 || $[40] !== tabIndex) { + t4 = {children}; + $[27] = autoFocus; + $[28] = children; + $[29] = onBlur; + $[30] = onBlurCapture; + $[31] = onClick; + $[32] = onFocus; + $[33] = onFocusCapture; + $[34] = onKeyDown; + $[35] = onKeyDownCapture; + $[36] = onMouseEnter; + $[37] = onMouseLeave; + $[38] = ref; + $[39] = t3; + $[40] = tabIndex; + $[41] = t4; + } else { + t4 = $[41]; + } + return t4; +} +export default Box; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzV2l0aENoaWxkcmVuIiwiUmVmIiwiRXhjZXB0IiwiRE9NRWxlbWVudCIsIkNsaWNrRXZlbnQiLCJGb2N1c0V2ZW50IiwiS2V5Ym9hcmRFdmVudCIsIlN0eWxlcyIsIndhcm4iLCJQcm9wcyIsInJlZiIsInRhYkluZGV4IiwiYXV0b0ZvY3VzIiwib25DbGljayIsImV2ZW50Iiwib25Gb2N1cyIsIm9uRm9jdXNDYXB0dXJlIiwib25CbHVyIiwib25CbHVyQ2FwdHVyZSIsIm9uS2V5RG93biIsIm9uS2V5RG93bkNhcHR1cmUiLCJvbk1vdXNlRW50ZXIiLCJvbk1vdXNlTGVhdmUiLCJCb3giLCJ0MCIsIiQiLCJfYyIsImNoaWxkcmVuIiwiZmxleERpcmVjdGlvbiIsImZsZXhHcm93IiwiZmxleFNocmluayIsImZsZXhXcmFwIiwic3R5bGUiLCJ0MSIsInQyIiwidDMiLCJ0NCIsInQ1IiwidDYiLCJ0NyIsInQ4IiwidDkiLCJ0MTAiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJ0MTYiLCJ0MTciLCJ0MTgiLCJ1bmRlZmluZWQiLCJpZk5vdEludGVnZXIiLCJtYXJnaW4iLCJtYXJnaW5YIiwibWFyZ2luWSIsIm1hcmdpblRvcCIsIm1hcmdpbkJvdHRvbSIsIm1hcmdpbkxlZnQiLCJtYXJnaW5SaWdodCIsInBhZGRpbmciLCJwYWRkaW5nWCIsInBhZGRpbmdZIiwicGFkZGluZ1RvcCIsInBhZGRpbmdCb3R0b20iLCJwYWRkaW5nTGVmdCIsInBhZGRpbmdSaWdodCIsImdhcCIsImNvbHVtbkdhcCIsInJvd0dhcCIsIm92ZXJmbG93WCIsIm92ZXJmbG93Iiwib3ZlcmZsb3dZIl0sInNvdXJjZXMiOlsiQm94LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgJy4uL2dsb2JhbC5kLnRzJ1xuaW1wb3J0IFJlYWN0LCB7IHR5cGUgUHJvcHNXaXRoQ2hpbGRyZW4sIHR5cGUgUmVmIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IEV4Y2VwdCB9IGZyb20gJ3R5cGUtZmVzdCdcbmltcG9ydCB0eXBlIHsgRE9NRWxlbWVudCB9IGZyb20gJy4uL2RvbS5qcydcbmltcG9ydCB0eXBlIHsgQ2xpY2tFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9jbGljay1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgRm9jdXNFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9mb2N1cy1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgU3R5bGVzIH0gZnJvbSAnLi4vc3R5bGVzLmpzJ1xuaW1wb3J0ICogYXMgd2FybiBmcm9tICcuLi93YXJuLmpzJ1xuXG5leHBvcnQgdHlwZSBQcm9wcyA9IEV4Y2VwdDxTdHlsZXMsICd0ZXh0V3JhcCc+ICYge1xuICByZWY/OiBSZWY8RE9NRWxlbWVudD5cbiAgLyoqXG4gICAqIFRhYiBvcmRlciBpbmRleC4gTm9kZXMgd2l0aCBgdGFiSW5kZXggPj0gMGAgcGFydGljaXBhdGUgaW5cbiAgICogVGFiL1NoaWZ0K1RhYiBjeWNsaW5nOyBgLTFgIG1lYW5zIHByb2dyYW1tYXRpY2FsbHkgZm9jdXNhYmxlIG9ubHkuXG4gICAqL1xuICB0YWJJbmRleD86IG51bWJlclxuICAvKipcbiAgICogRm9jdXMgdGhpcyBlbGVtZW50IHdoZW4gaXQgbW91bnRzLiBMaWtlIHRoZSBIVE1MIGBhdXRvZm9jdXNgXG4gICAqIGF0dHJpYnV0ZSDigJQgdGhlIEZvY3VzTWFuYWdlciBjYWxscyBgZm9jdXMobm9kZSlgIGR1cmluZyB0aGVcbiAgICogcmVjb25jaWxlcidzIGBjb21taXRNb3VudGAgcGhhc2UuXG4gICAqL1xuICBhdXRvRm9jdXM/OiBib29sZWFuXG4gIC8qKlxuICAgKiBGaXJlZCBvbiBsZWZ0LWJ1dHRvbiBjbGljayAocHJlc3MgKyByZWxlYXNlIHdpdGhvdXQgZHJhZykuIE9ubHkgd29ya3NcbiAgICogaW5zaWRlIGA8QWx0ZXJuYXRlU2NyZWVuPmAgd2hlcmUgbW91c2UgdHJhY2tpbmcgaXMgZW5hYmxlZCDigJQgbm8tb3BcbiAgICogb3RoZXJ3aXNlLiBUaGUgZXZlbnQgYnViYmxlcyBmcm9tIHRoZSBkZWVwZXN0IGhpdCBCb3ggdXAgdGhyb3VnaFxuICAgKiBhbmNlc3RvcnM7IGNhbGwgYGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpYCB0byBzdG9wIGJ1YmJsaW5nLlxuICAgKi9cbiAgb25DbGljaz86IChldmVudDogQ2xpY2tFdmVudCkgPT4gdm9pZFxuICBvbkZvY3VzPzogKGV2ZW50OiBGb2N1c0V2ZW50KSA9PiB2b2lkXG4gIG9uRm9jdXNDYXB0dXJlPzogKGV2ZW50OiBGb2N1c0V2ZW50KSA9PiB2b2lkXG4gIG9uQmx1cj86IChldmVudDogRm9jdXNFdmVudCkgPT4gdm9pZFxuICBvbkJsdXJDYXB0dXJlPzogKGV2ZW50OiBGb2N1c0V2ZW50KSA9PiB2b2lkXG4gIG9uS2V5RG93bj86IChldmVudDogS2V5Ym9hcmRFdmVudCkgPT4gdm9pZFxuICBvbktleURvd25DYXB0dXJlPzogKGV2ZW50OiBLZXlib2FyZEV2ZW50KSA9PiB2b2lkXG4gIC8qKlxuICAgKiBGaXJlZCB3aGVuIHRoZSBtb3VzZSBtb3ZlcyBpbnRvIHRoaXMgQm94J3MgcmVuZGVyZWQgcmVjdC4gTGlrZSBET01cbiAgICogYG1vdXNlZW50ZXJgLCBkb2VzIE5PVCBidWJibGUg4oCUIG1vdmluZyBiZXR3ZWVuIGNoaWxkcmVuIGRvZXMgbm90XG4gICAqIHJlLWZpcmUgb24gdGhlIHBhcmVudC4gT25seSB3b3JrcyBpbnNpZGUgYDxBbHRlcm5hdGVTY3JlZW4+YCB3aGVyZVxuICAgKiBtb2RlLTEwMDMgbW91c2UgdHJhY2tpbmcgaXMgZW5hYmxlZC5cbiAgICovXG4gIG9uTW91c2VFbnRlcj86ICgpID0+IHZvaWRcbiAgLyoqIEZpcmVkIHdoZW4gdGhlIG1vdXNlIG1vdmVzIG91dCBvZiB0aGlzIEJveCdzIHJlbmRlcmVkIHJlY3QuICovXG4gIG9uTW91c2VMZWF2ZT86ICgpID0+IHZvaWRcbn1cblxuLyoqXG4gKiBgPEJveD5gIGlzIGFuIGVzc2VudGlhbCBJbmsgY29tcG9uZW50IHRvIGJ1aWxkIHlvdXIgbGF5b3V0LiBJdCdzIGxpa2UgYDxkaXYgc3R5bGU9XCJkaXNwbGF5OiBmbGV4XCI+YCBpbiB0aGUgYnJvd3Nlci5cbiAqL1xuZnVuY3Rpb24gQm94KHtcbiAgY2hpbGRyZW4sXG4gIGZsZXhXcmFwID0gJ25vd3JhcCcsXG4gIGZsZXhEaXJlY3Rpb24gPSAncm93JyxcbiAgZmxleEdyb3cgPSAwLFxuICBmbGV4U2hyaW5rID0gMSxcbiAgcmVmLFxuICB0YWJJbmRleCxcbiAgYXV0b0ZvY3VzLFxuICBvbkNsaWNrLFxuICBvbkZvY3VzLFxuICBvbkZvY3VzQ2FwdHVyZSxcbiAgb25CbHVyLFxuICBvbkJsdXJDYXB0dXJlLFxuICBvbk1vdXNlRW50ZXIsXG4gIG9uTW91c2VMZWF2ZSxcbiAgb25LZXlEb3duLFxuICBvbktleURvd25DYXB0dXJlLFxuICAuLi5zdHlsZVxufTogUHJvcHNXaXRoQ2hpbGRyZW48UHJvcHM+KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgLy8gV2FybiBpZiBzcGFjaW5nIHZhbHVlcyBhcmUgbm90IGludGVnZXJzIHRvIHByZXZlbnQgZnJhY3Rpb25hbCBsYXlvdXQgZGltZW5zaW9uc1xuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5tYXJnaW4sICdtYXJnaW4nKVxuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5tYXJnaW5YLCAnbWFyZ2luWCcpXG4gIHdhcm4uaWZOb3RJbnRlZ2VyKHN0eWxlLm1hcmdpblksICdtYXJnaW5ZJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUubWFyZ2luVG9wLCAnbWFyZ2luVG9wJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUubWFyZ2luQm90dG9tLCAnbWFyZ2luQm90dG9tJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUubWFyZ2luTGVmdCwgJ21hcmdpbkxlZnQnKVxuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5tYXJnaW5SaWdodCwgJ21hcmdpblJpZ2h0JylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucGFkZGluZywgJ3BhZGRpbmcnKVxuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5wYWRkaW5nWCwgJ3BhZGRpbmdYJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucGFkZGluZ1ksICdwYWRkaW5nWScpXG4gIHdhcm4uaWZOb3RJbnRlZ2VyKHN0eWxlLnBhZGRpbmdUb3AsICdwYWRkaW5nVG9wJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucGFkZGluZ0JvdHRvbSwgJ3BhZGRpbmdCb3R0b20nKVxuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5wYWRkaW5nTGVmdCwgJ3BhZGRpbmdMZWZ0JylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucGFkZGluZ1JpZ2h0LCAncGFkZGluZ1JpZ2h0JylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUuZ2FwLCAnZ2FwJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUuY29sdW1uR2FwLCAnY29sdW1uR2FwJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucm93R2FwLCAncm93R2FwJylcblxuICByZXR1cm4gKFxuICAgIDxpbmstYm94XG4gICAgICByZWY9e3JlZn1cbiAgICAgIHRhYkluZGV4PXt0YWJJbmRleH1cbiAgICAgIGF1dG9Gb2N1cz17YXV0b0ZvY3VzfVxuICAgICAgb25DbGljaz17b25DbGlja31cbiAgICAgIG9uRm9jdXM9e29uRm9jdXN9XG4gICAgICBvbkZvY3VzQ2FwdHVyZT17b25Gb2N1c0NhcHR1cmV9XG4gICAgICBvbkJsdXI9e29uQmx1cn1cbiAgICAgIG9uQmx1ckNhcHR1cmU9e29uQmx1ckNhcHR1cmV9XG4gICAgICBvbk1vdXNlRW50ZXI9e29uTW91c2VFbnRlcn1cbiAgICAgIG9uTW91c2VMZWF2ZT17b25Nb3VzZUxlYXZlfVxuICAgICAgb25LZXlEb3duPXtvbktleURvd259XG4gICAgICBvbktleURvd25DYXB0dXJlPXtvbktleURvd25DYXB0dXJlfVxuICAgICAgc3R5bGU9e3tcbiAgICAgICAgZmxleFdyYXAsXG4gICAgICAgIGZsZXhEaXJlY3Rpb24sXG4gICAgICAgIGZsZXhHcm93LFxuICAgICAgICBmbGV4U2hyaW5rLFxuICAgICAgICAuLi5zdHlsZSxcbiAgICAgICAgb3ZlcmZsb3dYOiBzdHlsZS5vdmVyZmxvd1ggPz8gc3R5bGUub3ZlcmZsb3cgPz8gJ3Zpc2libGUnLFxuICAgICAgICBvdmVyZmxvd1k6IHN0eWxlLm92ZXJmbG93WSA/PyBzdHlsZS5vdmVyZmxvdyA/PyAndmlzaWJsZScsXG4gICAgICB9fVxuICAgID5cbiAgICAgIHtjaGlsZHJlbn1cbiAgICA8L2luay1ib3g+XG4gIClcbn1cblxuZXhwb3J0IGRlZmF1bHQgQm94XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLGdCQUFnQjtBQUN2QixPQUFPQSxLQUFLLElBQUksS0FBS0MsaUJBQWlCLEVBQUUsS0FBS0MsR0FBRyxRQUFRLE9BQU87QUFDL0QsY0FBY0MsTUFBTSxRQUFRLFdBQVc7QUFDdkMsY0FBY0MsVUFBVSxRQUFRLFdBQVc7QUFDM0MsY0FBY0MsVUFBVSxRQUFRLDBCQUEwQjtBQUMxRCxjQUFjQyxVQUFVLFFBQVEsMEJBQTBCO0FBQzFELGNBQWNDLGFBQWEsUUFBUSw2QkFBNkI7QUFDaEUsY0FBY0MsTUFBTSxRQUFRLGNBQWM7QUFDMUMsT0FBTyxLQUFLQyxJQUFJLE1BQU0sWUFBWTtBQUVsQyxPQUFPLEtBQUtDLEtBQUssR0FBR1AsTUFBTSxDQUFDSyxNQUFNLEVBQUUsVUFBVSxDQUFDLEdBQUc7RUFDL0NHLEdBQUcsQ0FBQyxFQUFFVCxHQUFHLENBQUNFLFVBQVUsQ0FBQztFQUNyQjtBQUNGO0FBQ0E7QUFDQTtFQUNFUSxRQUFRLENBQUMsRUFBRSxNQUFNO0VBQ2pCO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7RUFDRUMsU0FBUyxDQUFDLEVBQUUsT0FBTztFQUNu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 \ No newline at end of file diff --git a/ink/components/Button.tsx b/ink/components/Button.tsx new file mode 100644 index 0000000..8dc35f0 --- /dev/null +++ b/ink/components/Button.tsx @@ -0,0 +1,192 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type Ref, useCallback, useEffect, useRef, useState } from 'react'; +import type { Except } from 'type-fest'; +import type { DOMElement } from '../dom.js'; +import type { ClickEvent } from '../events/click-event.js'; +import type { FocusEvent } from '../events/focus-event.js'; +import type { KeyboardEvent } from '../events/keyboard-event.js'; +import type { Styles } from '../styles.js'; +import Box from './Box.js'; +type ButtonState = { + focused: boolean; + hovered: boolean; + active: boolean; +}; +export type Props = Except & { + ref?: Ref; + /** + * Called when the button is activated via Enter, Space, or click. + */ + onAction: () => void; + /** + * Tab order index. Defaults to 0 (in tab order). + * Set to -1 for programmatically focusable only. + */ + tabIndex?: number; + /** + * Focus this button when it mounts. + */ + autoFocus?: boolean; + /** + * Render prop receiving the interactive state. Use this to + * style children based on focus/hover/active — Button itself + * is intentionally unstyled. + * + * If not provided, children render as-is (no state-dependent styling). + */ + children: ((state: ButtonState) => React.ReactNode) | React.ReactNode; +}; +function Button(t0) { + const $ = _c(30); + let autoFocus; + let children; + let onAction; + let ref; + let style; + let t1; + if ($[0] !== t0) { + ({ + onAction, + tabIndex: t1, + autoFocus, + children, + ref, + ...style + } = t0); + $[0] = t0; + $[1] = autoFocus; + $[2] = children; + $[3] = onAction; + $[4] = ref; + $[5] = style; + $[6] = t1; + } else { + autoFocus = $[1]; + children = $[2]; + onAction = $[3]; + ref = $[4]; + style = $[5]; + t1 = $[6]; + } + const tabIndex = t1 === undefined ? 0 : t1; + const [isFocused, setIsFocused] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const [isActive, setIsActive] = useState(false); + const activeTimer = useRef(null); + let t2; + let t3; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => () => { + if (activeTimer.current) { + clearTimeout(activeTimer.current); + } + }; + t3 = []; + $[7] = t2; + $[8] = t3; + } else { + t2 = $[7]; + t3 = $[8]; + } + useEffect(t2, t3); + let t4; + if ($[9] !== onAction) { + t4 = e => { + if (e.key === "return" || e.key === " ") { + e.preventDefault(); + setIsActive(true); + onAction(); + if (activeTimer.current) { + clearTimeout(activeTimer.current); + } + activeTimer.current = setTimeout(_temp, 100, setIsActive); + } + }; + $[9] = onAction; + $[10] = t4; + } else { + t4 = $[10]; + } + const handleKeyDown = t4; + let t5; + if ($[11] !== onAction) { + t5 = _e => { + onAction(); + }; + $[11] = onAction; + $[12] = t5; + } else { + t5 = $[12]; + } + const handleClick = t5; + let t6; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t6 = _e_0 => setIsFocused(true); + $[13] = t6; + } else { + t6 = $[13]; + } + const handleFocus = t6; + let t7; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t7 = _e_1 => setIsFocused(false); + $[14] = t7; + } else { + t7 = $[14]; + } + const handleBlur = t7; + let t8; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t8 = () => setIsHovered(true); + $[15] = t8; + } else { + t8 = $[15]; + } + const handleMouseEnter = t8; + let t9; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t9 = () => setIsHovered(false); + $[16] = t9; + } else { + t9 = $[16]; + } + const handleMouseLeave = t9; + let t10; + if ($[17] !== children || $[18] !== isActive || $[19] !== isFocused || $[20] !== isHovered) { + const state = { + focused: isFocused, + hovered: isHovered, + active: isActive + }; + t10 = typeof children === "function" ? children(state) : children; + $[17] = children; + $[18] = isActive; + $[19] = isFocused; + $[20] = isHovered; + $[21] = t10; + } else { + t10 = $[21]; + } + const content = t10; + let t11; + if ($[22] !== autoFocus || $[23] !== content || $[24] !== handleClick || $[25] !== handleKeyDown || $[26] !== ref || $[27] !== style || $[28] !== tabIndex) { + t11 = {content}; + $[22] = autoFocus; + $[23] = content; + $[24] = handleClick; + $[25] = handleKeyDown; + $[26] = ref; + $[27] = style; + $[28] = tabIndex; + $[29] = t11; + } else { + t11 = $[29]; + } + return t11; +} +function _temp(setter) { + return setter(false); +} +export default Button; +export type { ButtonState }; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlZiIsInVzZUNhbGxiYWNrIiwidXNlRWZmZWN0IiwidXNlUmVmIiwidXNlU3RhdGUiLCJFeGNlcHQiLCJET01FbGVtZW50IiwiQ2xpY2tFdmVudCIsIkZvY3VzRXZlbnQiLCJLZXlib2FyZEV2ZW50IiwiU3R5bGVzIiwiQm94IiwiQnV0dG9uU3RhdGUiLCJmb2N1c2VkIiwiaG92ZXJlZCIsImFjdGl2ZSIsIlByb3BzIiwicmVmIiwib25BY3Rpb24iLCJ0YWJJbmRleCIsImF1dG9Gb2N1cyIsImNoaWxkcmVuIiwic3RhdGUiLCJSZWFjdE5vZGUiLCJCdXR0b24iLCJ0MCIsIiQiLCJfYyIsInN0eWxlIiwidDEiLCJ1bmRlZmluZWQiLCJpc0ZvY3VzZWQiLCJzZXRJc0ZvY3VzZWQiLCJpc0hvdmVyZWQiLCJzZXRJc0hvdmVyZWQiLCJpc0FjdGl2ZSIsInNldElzQWN0aXZlIiwiYWN0aXZlVGltZXIiLCJ0MiIsInQzIiwiU3ltYm9sIiwiZm9yIiwiY3VycmVudCIsImNsZWFyVGltZW91dCIsInQ0IiwiZSIsImtleSIsInByZXZlbnREZWZhdWx0Iiwic2V0VGltZW91dCIsIl90ZW1wIiwiaGFuZGxlS2V5RG93biIsInQ1IiwiX2UiLCJoYW5kbGVDbGljayIsInQ2IiwiX2VfMCIsImhhbmRsZUZvY3VzIiwidDciLCJfZV8xIiwiaGFuZGxlQmx1ciIsInQ4IiwiaGFuZGxlTW91c2VFbnRlciIsInQ5IiwiaGFuZGxlTW91c2VMZWF2ZSIsInQxMCIsImNvbnRlbnQiLCJ0MTEiLCJzZXR0ZXIiXSwic291cmNlcyI6WyJCdXR0b24udHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwge1xuICB0eXBlIFJlZixcbiAgdXNlQ2FsbGJhY2ssXG4gIHVzZUVmZmVjdCxcbiAgdXNlUmVmLFxuICB1c2VTdGF0ZSxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IEV4Y2VwdCB9IGZyb20gJ3R5cGUtZmVzdCdcbmltcG9ydCB0eXBlIHsgRE9NRWxlbWVudCB9IGZyb20gJy4uL2RvbS5qcydcbmltcG9ydCB0eXBlIHsgQ2xpY2tFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9jbGljay1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgRm9jdXNFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9mb2N1cy1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgU3R5bGVzIH0gZnJvbSAnLi4vc3R5bGVzLmpzJ1xuaW1wb3J0IEJveCBmcm9tICcuL0JveC5qcydcblxudHlwZSBCdXR0b25TdGF0ZSA9IHtcbiAgZm9jdXNlZDogYm9vbGVhblxuICBob3ZlcmVkOiBib29sZWFuXG4gIGFjdGl2ZTogYm9vbGVhblxufVxuXG5leHBvcnQgdHlwZSBQcm9wcyA9IEV4Y2VwdDxTdHlsZXMsICd0ZXh0V3JhcCc+ICYge1xuICByZWY/OiBSZWY8RE9NRWxlbWVudD5cbiAgLyoqXG4gICAqIENhbGxlZCB3aGVuIHRoZSBidXR0b24gaXMgYWN0aXZhdGVkIHZpYSBFbnRlciwgU3BhY2UsIG9yIGNsaWNrLlxuICAgKi9cbiAgb25BY3Rpb246ICgpID0+IHZvaWRcbiAgLyoqXG4gICAqIFRhYiBvcmRlciBpbmRleC4gRGVmYXVsdHMgdG8gMCAoaW4gdGFiIG9yZGVyKS5cbiAgICogU2V0IHRvIC0xIGZvciBwcm9ncmFtbWF0aWNhbGx5IGZvY3VzYWJsZSBvbmx5LlxuICAgKi9cbiAgdGFiSW5kZXg/OiBudW1iZXJcbiAgLyoqXG4gICAqIEZvY3VzIHRoaXMgYnV0dG9uIHdoZW4gaXQgbW91bnRzLlxuICAgKi9cbiAgYXV0b0ZvY3VzPzogYm9vbGVhblxuICAvKipcbiAgICogUmVuZGVyIHByb3AgcmVjZWl2aW5nIHRoZSBpbnRlcmFjdGl2ZSBzdGF0ZS4gVXNlIHRoaXMgdG9cbiAgICogc3R5bGUgY2hpbGRyZW4gYmFzZWQgb24gZm9jdXMvaG92ZXIvYWN0aXZlIOKAlCBCdXR0b24gaXRzZWxmXG4gICAqIGlzIGludGVudGlvbmFsbHkgdW5zdHlsZWQuXG4gICAqXG4gICAqIElmIG5vdCBwcm92aWRlZCwgY2hpbGRyZW4gcmVuZGVyIGFzLWlzIChubyBzdGF0ZS1kZXBlbmRlbnQgc3R5bGluZykuXG4gICAqL1xuICBjaGlsZHJlbjogKChzdGF0ZTogQnV0dG9uU3RhdGUpID0+IFJlYWN0LlJlYWN0Tm9kZSkgfCBSZWFjdC5SZWFjdE5vZGVcbn1cblxuZnVuY3Rpb24gQnV0dG9uKHtcbiAgb25BY3Rpb24sXG4gIHRhYkluZGV4ID0gMCxcbiAgYXV0b0ZvY3VzLFxuICBjaGlsZHJlbixcbiAgcmVmLFxuICAuLi5zdHlsZVxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbaXNGb2N1c2VkLCBzZXRJc0ZvY3VzZWRdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtpc0hvdmVyZWQsIHNldElzSG92ZXJlZF0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW2lzQWN0aXZlLCBzZXRJc0FjdGl2ZV0gPSB1c2VTdGF0ZShmYWxzZSlcblxuICBjb25zdCBhY3RpdmVUaW1lciA9IHVzZVJlZjxSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IG51bGw+KG51bGwpXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgaWYgKGFjdGl2ZVRpbWVyLmN1cnJlbnQpIGNsZWFyVGltZW91dChhY3RpdmVUaW1lci5jdXJyZW50KVxuICAgIH1cbiAgfSwgW10pXG5cbiAgY29uc3QgaGFuZGxlS2V5RG93biA9IHVzZUNhbGxiYWNrKFxuICAgIChlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgICBpZiAoZS5rZXkgPT09ICdyZXR1cm4nIHx8IGUua2V5ID09PSAnICcpIHtcbiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIHNldElzQWN0aXZlKHRydWUpXG4gICAgICAgIG9uQWN0aW9uKClcbiAgICAgICAgaWYgKGFjdGl2ZVRpbWVyLmN1cnJlbnQpIGNsZWFyVGltZW91dChhY3RpdmVUaW1lci5jdXJyZW50KVxuICAgICAgICBhY3RpdmVUaW1lci5jdXJyZW50ID0gc2V0VGltZW91dChcbiAgICAgICAgICBzZXR0ZXIgPT4gc2V0dGVyKGZhbHNlKSxcbiAgICAgICAgICAxMDAsXG4gICAgICAgICAgc2V0SXNBY3RpdmUsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9LFxuICAgIFtvbkFjdGlvbl0sXG4gIClcblxuICBjb25zdCBoYW5kbGVDbGljayA9IHVzZUNhbGxiYWNrKFxuICAgIChfZTogQ2xpY2tFdmVudCkgPT4ge1xuICAgICAgb25BY3Rpb24oKVxuICAgIH0sXG4gICAgW29uQWN0aW9uXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZUZvY3VzID0gdXNlQ2FsbGJhY2soKF9lOiBGb2N1c0V2ZW50KSA9PiBzZXRJc0ZvY3VzZWQodHJ1ZSksIFtdKVxuICBjb25zdCBoYW5kbGVCbHVyID0gdXNlQ2FsbGJhY2soKF9lOiBGb2N1c0V2ZW50KSA9PiBzZXRJc0ZvY3VzZWQoZmFsc2UpLCBbXSlcbiAgY29uc3QgaGFuZGxlTW91c2VFbnRlciA9IHVzZUNhbGxiYWNrKCgpID0+IHNldElzSG92ZXJlZCh0cnVlKSwgW10pXG4gIGNvbnN0IGhhbmRsZU1vdXNlTGVhdmUgPSB1c2VDYWxsYmFjaygoKSA9PiBzZXRJc0hvdmVyZWQoZmFsc2UpLCBbXSlcblxuICBjb25zdCBzdGF0ZTogQnV0dG9uU3RhdGUgPSB7XG4gICAgZm9jdXNlZDogaXNGb2N1c2VkLFxuICAgIGhvdmVyZWQ6IGlzSG92ZXJlZCxcbiAgICBhY3RpdmU6IGlzQWN0aXZlLFxuICB9XG4gIGNvbnN0IGNvbnRlbnQgPSB0eXBlb2YgY2hpbGRyZW4gPT09ICdmdW5jdGlvbicgPyBjaGlsZHJlbihzdGF0ZSkgOiBjaGlsZHJlblxuXG4gIHJldHVybiAoXG4gICAgPEJveFxuICAgICAgcmVmPXtyZWZ9XG4gICAgICB0YWJJbmRleD17dGFiSW5kZXh9XG4gICAgICBhdXRvRm9jdXM9e2F1dG9Gb2N1c31cbiAgICAgIG9uS2V5RG93bj17aGFuZGxlS2V5RG93bn1cbiAgICAgIG9uQ2xpY2s9e2hhbmRsZUNsaWNrfVxuICAgICAgb25Gb2N1cz17aGFuZGxlRm9jdXN9XG4gICAgICBvbkJsdXI9e2hhbmRsZUJsdXJ9XG4gICAgICBvbk1vdXNlRW50ZXI9e2hhbmRsZU1vdXNlRW50ZXJ9XG4gICAgICBvbk1vdXNlTGVhdmU9e2hhbmRsZU1vdXNlTGVhdmV9XG4gICAgICB7Li4uc3R5bGV9XG4gICAgPlxuICAgICAge2NvbnRlbnR9XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuZXhwb3J0IGRlZmF1bHQgQnV0dG9uXG5leHBvcnQgdHlwZSB7IEJ1dHRvblN0YXRlIH1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFDVixLQUFLQyxHQUFHLEVBQ1JDLFdBQVcsRUFDWEMsU0FBUyxFQUNUQyxNQUFNLEVBQ05DLFFBQVEsUUFDSCxPQUFPO0FBQ2QsY0FBY0MsTUFBTSxRQUFRLFdBQVc7QUFDdkMsY0FBY0MsVUFBVSxRQUFRLFdBQVc7QUFDM0MsY0FBY0MsVUFBVSxRQUFRLDBCQUEwQjtBQUMxRCxjQUFjQyxVQUFVLFFBQVEsMEJBQTBCO0FBQzFELGNBQWNDLGFBQWEsUUFBUSw2QkFBNkI7QUFDaEUsY0FBY0MsTUFBTSxRQUFRLGNBQWM7QUFDMUMsT0FBT0MsR0FBRyxNQUFNLFVBQVU7QUFFMUIsS0FBS0MsV0FBVyxHQUFHO0VBQ2pCQyxPQUFPLEVBQUUsT0FBTztFQUNoQkMsT0FBTyxFQUFFLE9BQU87RUFDaEJDLE1BQU0sRUFBRSxPQUFPO0FBQ2pCLENBQUM7QUFFRCxPQUFPLEtBQUtDLEtBQUssR0FBR1gsTUFBTSxDQUFDSyxNQUFNLEVBQUUsVUFBVSxDQUFDLEdBQUc7RUFDL0NPLEdBQUcsQ0FBQyxFQUFFakIsR0FBRyxDQUFDTSxVQUFVLENBQUM7RUFDckI7QUFDRjtBQUNBO0VBQ0VZLFFBQVEsRUFBRSxHQUFHLEdBQUcsSUFBSTtFQUNwQjtB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 \ No newline at end of file diff --git a/ink/components/ClockContext.tsx b/ink/components/ClockContext.tsx new file mode 100644 index 0000000..0f24839 --- /dev/null +++ b/ink/components/ClockContext.tsx @@ -0,0 +1,112 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { createContext, useEffect, useState } from 'react'; +import { FRAME_INTERVAL_MS } from '../constants.js'; +import { useTerminalFocus } from '../hooks/use-terminal-focus.js'; +export type Clock = { + subscribe: (onChange: () => void, keepAlive: boolean) => () => void; + now: () => number; + setTickInterval: (ms: number) => void; +}; +export function createClock(tickIntervalMs: number): Clock { + const subscribers = new Map<() => void, boolean>(); + let interval: ReturnType | null = null; + let currentTickIntervalMs = tickIntervalMs; + let startTime = 0; + // Snapshot of the current tick's time, ensuring all subscribers in the same + // tick see the same value (keeps animations synchronized) + let tickTime = 0; + function tick(): void { + tickTime = Date.now() - startTime; + for (const onChange of subscribers.keys()) { + onChange(); + } + } + function updateInterval(): void { + const anyKeepAlive = [...subscribers.values()].some(Boolean); + if (anyKeepAlive) { + if (interval) { + clearInterval(interval); + interval = null; + } + if (startTime === 0) { + startTime = Date.now(); + } + interval = setInterval(tick, currentTickIntervalMs); + } else if (interval) { + clearInterval(interval); + interval = null; + } + } + return { + subscribe(onChange, keepAlive) { + subscribers.set(onChange, keepAlive); + updateInterval(); + return () => { + subscribers.delete(onChange); + updateInterval(); + }; + }, + now() { + if (startTime === 0) { + startTime = Date.now(); + } + // When the clock interval is running, return the synchronized tickTime + // so all subscribers in the same tick see the same value. + // When paused (no keepAlive subscribers), return real-time to avoid + // returning a stale tickTime from the last tick before the pause. + if (interval && tickTime) { + return tickTime; + } + return Date.now() - startTime; + }, + setTickInterval(ms) { + if (ms === currentTickIntervalMs) return; + currentTickIntervalMs = ms; + updateInterval(); + } + }; +} +export const ClockContext = createContext(null); +const BLURRED_TICK_INTERVAL_MS = FRAME_INTERVAL_MS * 2; + +// Own component so App.tsx doesn't re-render when the clock is created. +// The clock value is stable (created once via useState), so the provider +// never causes consumer re-renders on its own. +export function ClockProvider(t0) { + const $ = _c(7); + const { + children + } = t0; + const [clock] = useState(_temp); + const focused = useTerminalFocus(); + let t1; + let t2; + if ($[0] !== clock || $[1] !== focused) { + t1 = () => { + clock.setTickInterval(focused ? FRAME_INTERVAL_MS : BLURRED_TICK_INTERVAL_MS); + }; + t2 = [clock, focused]; + $[0] = clock; + $[1] = focused; + $[2] = t1; + $[3] = t2; + } else { + t1 = $[2]; + t2 = $[3]; + } + useEffect(t1, t2); + let t3; + if ($[4] !== children || $[5] !== clock) { + t3 = {children}; + $[4] = children; + $[5] = clock; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; +} +function _temp() { + return createClock(FRAME_INTERVAL_MS); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImNyZWF0ZUNvbnRleHQiLCJ1c2VFZmZlY3QiLCJ1c2VTdGF0ZSIsIkZSQU1FX0lOVEVSVkFMX01TIiwidXNlVGVybWluYWxGb2N1cyIsIkNsb2NrIiwic3Vic2NyaWJlIiwib25DaGFuZ2UiLCJrZWVwQWxpdmUiLCJub3ciLCJzZXRUaWNrSW50ZXJ2YWwiLCJtcyIsImNyZWF0ZUNsb2NrIiwidGlja0ludGVydmFsTXMiLCJzdWJzY3JpYmVycyIsIk1hcCIsImludGVydmFsIiwiUmV0dXJuVHlwZSIsInNldEludGVydmFsIiwiY3VycmVudFRpY2tJbnRlcnZhbE1zIiwic3RhcnRUaW1lIiwidGlja1RpbWUiLCJ0aWNrIiwiRGF0ZSIsImtleXMiLCJ1cGRhdGVJbnRlcnZhbCIsImFueUtlZXBBbGl2ZSIsInZhbHVlcyIsInNvbWUiLCJCb29sZWFuIiwiY2xlYXJJbnRlcnZhbCIsInNldCIsImRlbGV0ZSIsIkNsb2NrQ29udGV4dCIsIkJMVVJSRURfVElDS19JTlRFUlZBTF9NUyIsIkNsb2NrUHJvdmlkZXIiLCJ0MCIsIiQiLCJfYyIsImNoaWxkcmVuIiwiY2xvY2siLCJfdGVtcCIsImZvY3VzZWQiLCJ0MSIsInQyIiwidDMiXSwic291cmNlcyI6WyJDbG9ja0NvbnRleHQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyBjcmVhdGVDb250ZXh0LCB1c2VFZmZlY3QsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBGUkFNRV9JTlRFUlZBTF9NUyB9IGZyb20gJy4uL2NvbnN0YW50cy5qcydcbmltcG9ydCB7IHVzZVRlcm1pbmFsRm9jdXMgfSBmcm9tICcuLi9ob29rcy91c2UtdGVybWluYWwtZm9jdXMuanMnXG5cbmV4cG9ydCB0eXBlIENsb2NrID0ge1xuICBzdWJzY3JpYmU6IChvbkNoYW5nZTogKCkgPT4gdm9pZCwga2VlcEFsaXZlOiBib29sZWFuKSA9PiAoKSA9PiB2b2lkXG4gIG5vdzogKCkgPT4gbnVtYmVyXG4gIHNldFRpY2tJbnRlcnZhbDogKG1zOiBudW1iZXIpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUNsb2NrKHRpY2tJbnRlcnZhbE1zOiBudW1iZXIpOiBDbG9jayB7XG4gIGNvbnN0IHN1YnNjcmliZXJzID0gbmV3IE1hcDwoKSA9PiB2b2lkLCBib29sZWFuPigpXG4gIGxldCBpbnRlcnZhbDogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0SW50ZXJ2YWw+IHwgbnVsbCA9IG51bGxcbiAgbGV0IGN1cnJlbnRUaWNrSW50ZXJ2YWxNcyA9IHRpY2tJbnRlcnZhbE1zXG4gIGxldCBzdGFydFRpbWUgPSAwXG4gIC8vIFNuYXBzaG90IG9mIHRoZSBjdXJyZW50IHRpY2sncyB0aW1lLCBlbnN1cmluZyBhbGwgc3Vic2NyaWJlcnMgaW4gdGhlIHNhbWVcbiAgLy8gdGljayBzZWUgdGhlIHNhbWUgdmFsdWUgKGtlZXBzIGFuaW1hdGlvbnMgc3luY2hyb25pemVkKVxuICBsZXQgdGlja1RpbWUgPSAwXG5cbiAgZnVuY3Rpb24gdGljaygpOiB2b2lkIHtcbiAgICB0aWNrVGltZSA9IERhdGUubm93KCkgLSBzdGFydFRpbWVcbiAgICBmb3IgKGNvbnN0IG9uQ2hhbmdlIG9mIHN1YnNjcmliZXJzLmtleXMoKSkge1xuICAgICAgb25DaGFuZ2UoKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHVwZGF0ZUludGVydmFsKCk6IHZvaWQge1xuICAgIGNvbnN0IGFueUtlZXBBbGl2ZSA9IFsuLi5zdWJzY3JpYmVycy52YWx1ZXMoKV0uc29tZShCb29sZWFuKVxuXG4gICAgaWYgKGFueUtlZXBBbGl2ZSkge1xuICAgICAgaWYgKGludGVydmFsKSB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwoaW50ZXJ2YWwpXG4gICAgICAgIGludGVydmFsID0gbnVsbFxuICAgICAgfVxuICAgICAgaWYgKHN0YXJ0VGltZSA9PT0gMCkge1xuICAgICAgICBzdGFydFRpbWUgPSBEYXRlLm5vdygpXG4gICAgICB9XG4gICAgICBpbnRlcnZhbCA9IHNldEludGVydmFsKHRpY2ssIGN1cnJlbnRUaWNrSW50ZXJ2YWxNcylcbiAgICB9IGVsc2UgaWYgKGludGVydmFsKSB7XG4gICAgICBjbGVhckludGVydmFsKGludGVydmFsKVxuICAgICAgaW50ZXJ2YWwgPSBudWxsXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBzdWJzY3JpYmUob25DaGFuZ2UsIGtlZXBBbGl2ZSkge1xuICAgICAgc3Vic2NyaWJlcnMuc2V0KG9uQ2hhbmdlLCBrZWVwQWxpdmUpXG4gICAgICB1cGRhdGVJbnRlcnZhbCgpXG4gICAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBzdWJzY3JpYmVycy5kZWxldGUob25DaGFuZ2UpXG4gICAgICAgIHVwZGF0ZUludGVydmFsKClcbiAgICAgIH1cbiAgICB9LFxuXG4gICAgbm93KCkge1xuICAgICAgaWYgKHN0YXJ0VGltZSA9PT0gMCkge1xuICAgICAgICBzdGFydFRpbWUgPSBEYXRlLm5vdygpXG4gICAgICB9XG4gICAgICAvLyBXaGVuIHRoZSBjbG9jayBpbnRlcnZhbCBpcyBydW5uaW5nLCByZXR1cm4gdGhlIHN5bmNocm9uaXplZCB0aWNrVGltZVxuICAgICAgLy8gc28gYWxsIHN1YnNjcmliZXJzIGluIHRoZSBzYW1lIHRpY2sgc2VlIHRoZSBzYW1lIHZhbHVlLlxuICAgICAgLy8gV2hlbiBwYXVzZWQgKG5vIGtlZXBBbGl2ZSBzdWJzY3JpYmVycyksIHJldHVybiByZWFsLXRpbWUgdG8gYXZvaWRcbiAgICAgIC8vIHJldHVybmluZyBhIHN0YWxlIHRpY2tUaW1lIGZyb20gdGhlIGxhc3QgdGljayBiZWZvcmUgdGhlIHBhdXNlLlxuICAgICAgaWYgKGludGVydmFsICYmIHRpY2tUaW1lKSB7XG4gICAgICAgIHJldHVybiB0aWNrVGltZVxuICAgICAgfVxuICAgICAgcmV0dXJuIERhdGUubm93KCkgLSBzdGFydFRpbWVcbiAgICB9LFxuXG4gICAgc2V0VGlja0ludGVydmFsKG1zKSB7XG4gICAgICBpZiAobXMgPT09IGN1cnJlbnRUaWNrSW50ZXJ2YWxNcykgcmV0dXJuXG4gICAgICBjdXJyZW50VGlja0ludGVydmFsTXMgPSBtc1xuICAgICAgdXBkYXRlSW50ZXJ2YWwoKVxuICAgIH0sXG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IENsb2NrQ29udGV4dCA9IGNyZWF0ZUNvbnRleHQ8Q2xvY2sgfCBudWxsPihudWxsKVxuXG5jb25zdCBCTFVSUkVEX1RJQ0tfSU5URVJWQUxfTVMgPSBGUkFNRV9JTlRFUlZBTF9NUyAqIDJcblxuLy8gT3duIGNvbXBvbmVudCBzbyBBcHAudHN4IGRvZXNuJ3QgcmUtcmVuZGVyIHdoZW4gdGhlIGNsb2NrIGlzIGNyZWF0ZWQuXG4vLyBUaGUgY2xvY2sgdmFsdWUgaXMgc3RhYmxlIChjcmVhdGVkIG9uY2UgdmlhIHVzZVN0YXRlKSwgc28gdGhlIHByb3ZpZGVyXG4vLyBuZXZlciBjYXVzZXMgY29uc3VtZXIgcmUtcmVuZGVycyBvbiBpdHMgb3duLlxuZXhwb3J0IGZ1bmN0aW9uIENsb2NrUHJvdmlkZXIoe1xuICBjaGlsZHJlbixcbn06IHtcbiAgY2hpbGRyZW46IFJlYWN0LlJlYWN0Tm9kZVxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtjbG9ja10gPSB1c2VTdGF0ZSgoKSA9PiBjcmVhdGVDbG9jayhGUkFNRV9JTlRFUlZBTF9NUykpXG4gIGNvbnN0IGZvY3VzZWQgPSB1c2VUZXJtaW5hbEZvY3VzKClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNsb2NrLnNldFRpY2tJbnRlcnZhbChcbiAgICAgIGZvY3VzZWQgPyBGUkFNRV9JTlRFUlZBTF9NUyA6IEJMVVJSRURfVElDS19JTlRFUlZBTF9NUyxcbiAgICApXG4gIH0sIFtjbG9jaywgZm9jdXNlZF0pXG5cbiAgcmV0dXJuIDxDbG9ja0NvbnRleHQuUHJvdmlkZXIgdmFsdWU9e2Nsb2NrfT57Y2hpbGRyZW59PC9DbG9ja0NvbnRleHQuUHJvdmlkZXI+XG59XG4iXSwibWFwcGluZ3MiOiI7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QUtRLHFCQUFxQixFQUFFO01BQ2xDQSxxQkFBcUIsR0FBR1IsRUFBRTtNQUMxQmMsY0FBYyxDQUFDLENBQUM7SUFDbEI7RUFDRixDQUFDO0FBQ0g7QUFFQSxPQUFPLE1BQU1RLFlBQVksR0FBR2pDLGFBQWEsQ0FBQ0ssS0FBSyxHQUFHLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQztBQUU3RCxNQUFNNkIsd0JBQXdCLEdBQUcvQixpQkFBaUIsR0FBRyxDQUFDOztBQUV0RDtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFnQyxjQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXVCO0lBQUFDO0VBQUEsSUFBQUgsRUFJN0I7RUFDQyxPQUFBSSxLQUFBLElBQWdCdEMsUUFBUSxDQUFDdUMsS0FBb0MsQ0FBQztFQUM5RCxNQUFBQyxPQUFBLEdBQWdCdEMsZ0JBQWdCLENBQUMsQ0FBQztFQUFBLElBQUF1QyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFQLENBQUEsUUFBQUcsS0FBQSxJQUFBSCxDQUFBLFFBQUFLLE9BQUE7SUFFeEJDLEVBQUEsR0FBQUEsQ0FBQTtNQUNSSCxLQUFLLENBQUE5QixlQUFnQixDQUNuQmdDLE9BQU8sR0FBUHZDLGlCQUFzRCxHQUF0RCtCLHdCQUNGLENBQUM7SUFBQSxDQUNGO0lBQUVVLEVBQUEsSUFBQ0osS0FBSyxFQUFFRSxPQUFPLENBQUM7SUFBQUwsQ0FBQSxNQUFBRyxLQUFBO0lBQUFILENBQUEsTUFBQUssT0FBQTtJQUFBTCxDQUFBLE1BQUFNLEVBQUE7SUFBQU4sQ0FBQSxNQUFBTyxFQUFBO0VBQUE7SUFBQUQsRUFBQSxHQUFBTixDQUFBO0lBQUFPLEVBQUEsR0FBQVAsQ0FBQTtFQUFBO0VBSm5CcEMsU0FBUyxDQUFDMEMsRUFJVCxFQUFFQyxFQUFnQixDQUFDO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFSLENBQUEsUUFBQUUsUUFBQSxJQUFBRixDQUFBLFFBQUFHLEtBQUE7SUFFYkssRUFBQSwwQkFBOEJMLEtBQUssQ0FBTEEsTUFBSSxDQUFDLENBQUdELFNBQU8sQ0FBRSx3QkFBd0I7SUFBQUYsQ0FBQSxNQUFBRSxRQUFBO0lBQUFGLENBQUEsTUFBQUcsS0FBQTtJQUFBSCxDQUFBLE1BQUFRLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFSLENBQUE7RUFBQTtFQUFBLE9BQXZFUSxFQUF1RTtBQUFBO0FBZHpFLFNBQUFKLE1BQUE7RUFBQSxPQUswQjdCLFdBQVcsQ0FBQ1QsaUJBQWlCLENBQUM7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/ink/components/CursorDeclarationContext.ts b/ink/components/CursorDeclarationContext.ts new file mode 100644 index 0000000..358c804 --- /dev/null +++ b/ink/components/CursorDeclarationContext.ts @@ -0,0 +1,32 @@ +import { createContext } from 'react' +import type { DOMElement } from '../dom.js' + +export type CursorDeclaration = { + /** Display column (terminal cell width) within the declared node */ + readonly relativeX: number + /** Line number within the declared node */ + readonly relativeY: number + /** The ink-box DOMElement whose yoga layout provides the absolute origin */ + readonly node: DOMElement +} + +/** + * Setter for the declared cursor position. + * + * The optional second argument makes `null` a conditional clear: the + * declaration is only cleared if the currently-declared node matches + * `clearIfNode`. This makes the hook safe for sibling components + * (e.g. list items) that transfer focus among themselves — without the + * node check, a newly-unfocused item's clear could clobber a + * newly-focused sibling's set depending on layout-effect order. + */ +export type CursorDeclarationSetter = ( + declaration: CursorDeclaration | null, + clearIfNode?: DOMElement | null, +) => void + +const CursorDeclarationContext = createContext( + () => {}, +) + +export default CursorDeclarationContext diff --git a/ink/components/ErrorOverview.tsx b/ink/components/ErrorOverview.tsx new file mode 100644 index 0000000..c889f90 --- /dev/null +++ b/ink/components/ErrorOverview.tsx @@ -0,0 +1,109 @@ +import codeExcerpt, { type CodeExcerpt } from 'code-excerpt'; +import { readFileSync } from 'fs'; +import React from 'react'; +import StackUtils from 'stack-utils'; +import Box from './Box.js'; +import Text from './Text.js'; + +/* eslint-disable custom-rules/no-process-cwd -- stack trace file:// paths are relative to the real OS cwd, not the virtual cwd */ + +// Error's source file is reported as file:///home/user/file.js +// This function removes the file://[cwd] part +const cleanupPath = (path: string | undefined): string | undefined => { + return path?.replace(`file://${process.cwd()}/`, ''); +}; +let stackUtils: StackUtils | undefined; +function getStackUtils(): StackUtils { + return stackUtils ??= new StackUtils({ + cwd: process.cwd(), + internals: StackUtils.nodeInternals() + }); +} + +/* eslint-enable custom-rules/no-process-cwd */ + +type Props = { + readonly error: Error; +}; +export default function ErrorOverview({ + error +}: Props) { + const stack = error.stack ? error.stack.split('\n').slice(1) : undefined; + const origin = stack ? getStackUtils().parseLine(stack[0]!) : undefined; + const filePath = cleanupPath(origin?.file); + let excerpt: CodeExcerpt[] | undefined; + let lineWidth = 0; + if (filePath && origin?.line) { + try { + // eslint-disable-next-line custom-rules/no-sync-fs -- sync render path; error overlay can't go async without suspense restructuring + const sourceCode = readFileSync(filePath, 'utf8'); + excerpt = codeExcerpt(sourceCode, origin.line); + if (excerpt) { + for (const { + line + } of excerpt) { + lineWidth = Math.max(lineWidth, String(line).length); + } + } + } catch { + // file not readable — skip source context + } + } + return + + + {' '} + ERROR{' '} + + + {error.message} + + + {origin && filePath && + + {filePath}:{origin.line}:{origin.column} + + } + + {origin && excerpt && + {excerpt.map(({ + line: line_0, + value + }) => + + + {String(line_0).padStart(lineWidth, ' ')}: + + + + + {' ' + value} + + )} + } + + {error.stack && + {error.stack.split('\n').slice(1).map(line_1 => { + const parsedLine = getStackUtils().parseLine(line_1); + + // If the line from the stack cannot be parsed, we print out the unparsed line. + if (!parsedLine) { + return + - + {line_1} + ; + } + return + - + {parsedLine.function} + + {' '} + ({cleanupPath(parsedLine.file) ?? ''}:{parsedLine.line}: + {parsedLine.column}) + + ; + })} + } + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjb2RlRXhjZXJwdCIsIkNvZGVFeGNlcnB0IiwicmVhZEZpbGVTeW5jIiwiUmVhY3QiLCJTdGFja1V0aWxzIiwiQm94IiwiVGV4dCIsImNsZWFudXBQYXRoIiwicGF0aCIsInJlcGxhY2UiLCJwcm9jZXNzIiwiY3dkIiwic3RhY2tVdGlscyIsImdldFN0YWNrVXRpbHMiLCJpbnRlcm5hbHMiLCJub2RlSW50ZXJuYWxzIiwiUHJvcHMiLCJlcnJvciIsIkVycm9yIiwiRXJyb3JPdmVydmlldyIsInN0YWNrIiwic3BsaXQiLCJzbGljZSIsInVuZGVmaW5lZCIsIm9yaWdpbiIsInBhcnNlTGluZSIsImZpbGVQYXRoIiwiZmlsZSIsImV4Y2VycHQiLCJsaW5lV2lkdGgiLCJsaW5lIiwic291cmNlQ29kZSIsIk1hdGgiLCJtYXgiLCJTdHJpbmciLCJsZW5ndGgiLCJtZXNzYWdlIiwiY29sdW1uIiwibWFwIiwidmFsdWUiLCJwYWRTdGFydCIsInBhcnNlZExpbmUiLCJmdW5jdGlvbiJdLCJzb3VyY2VzIjpbIkVycm9yT3ZlcnZpZXcudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjb2RlRXhjZXJwdCwgeyB0eXBlIENvZGVFeGNlcnB0IH0gZnJvbSAnY29kZS1leGNlcnB0J1xuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSAnZnMnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgU3RhY2tVdGlscyBmcm9tICdzdGFjay11dGlscydcbmltcG9ydCBCb3ggZnJvbSAnLi9Cb3guanMnXG5pbXBvcnQgVGV4dCBmcm9tICcuL1RleHQuanMnXG5cbi8qIGVzbGludC1kaXNhYmxlIGN1c3RvbS1ydWxlcy9uby1wcm9jZXNzLWN3ZCAtLSBzdGFjayB0cmFjZSBmaWxlOi8vIHBhdGhzIGFyZSByZWxhdGl2ZSB0byB0aGUgcmVhbCBPUyBjd2QsIG5vdCB0aGUgdmlydHVhbCBjd2QgKi9cblxuLy8gRXJyb3IncyBzb3VyY2UgZmlsZSBpcyByZXBvcnRlZCBhcyBmaWxlOi8vL2hvbWUvdXNlci9maWxlLmpzXG4vLyBUaGlzIGZ1bmN0aW9uIHJlbW92ZXMgdGhlIGZpbGU6Ly9bY3dkXSBwYXJ0XG5jb25zdCBjbGVhbnVwUGF0aCA9IChwYXRoOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBzdHJpbmcgfCB1bmRlZmluZWQgPT4ge1xuICByZXR1cm4gcGF0aD8ucmVwbGFjZShgZmlsZTovLyR7cHJvY2Vzcy5jd2QoKX0vYCwgJycpXG59XG5cbmxldCBzdGFja1V0aWxzOiBTdGFja1V0aWxzIHwgdW5kZWZpbmVkXG5mdW5jdGlvbiBnZXRTdGFja1V0aWxzKCk6IFN0YWNrVXRpbHMge1xuICByZXR1cm4gKHN0YWNrVXRpbHMgPz89IG5ldyBTdGFja1V0aWxzKHtcbiAgICBjd2Q6IHByb2Nlc3MuY3dkKCksXG4gICAgaW50ZXJuYWxzOiBTdGFja1V0aWxzLm5vZGVJbnRlcm5hbHMoKSxcbiAgfSkpXG59XG5cbi8qIGVzbGludC1lbmFibGUgY3VzdG9tLXJ1bGVzL25vLXByb2Nlc3MtY3dkICovXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHJlYWRvbmx5IGVycm9yOiBFcnJvclxufVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBFcnJvck92ZXJ2aWV3KHsgZXJyb3IgfTogUHJvcHMpIHtcbiAgY29uc3Qgc3RhY2sgPSBlcnJvci5zdGFjayA/IGVycm9yLnN0YWNrLnNwbGl0KCdcXG4nKS5zbGljZSgxKSA6IHVuZGVmaW5lZFxuICBjb25zdCBvcmlnaW4gPSBzdGFjayA/IGdldFN0YWNrVXRpbHMoKS5wYXJzZUxpbmUoc3RhY2tbMF0hKSA6IHVuZGVmaW5lZFxuICBjb25zdCBmaWxlUGF0aCA9IGNsZWFudXBQYXRoKG9yaWdpbj8uZmlsZSlcbiAgbGV0IGV4Y2VycHQ6IENvZGVFeGNlcnB0W10gfCB1bmRlZmluZWRcbiAgbGV0IGxpbmVXaWR0aCA9IDBcblxuICBpZiAoZmlsZVBhdGggJiYgb3JpZ2luPy5saW5lKSB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tc3luYy1mcyAtLSBzeW5jIHJlbmRlciBwYXRoOyBlcnJvciBvdmVybGF5IGNhbid0IGdvIGFzeW5jIHdpdGhvdXQgc3VzcGVuc2UgcmVzdHJ1Y3R1cmluZ1xuICAgICAgY29uc3Qgc291cmNlQ29kZSA9IHJlYWRGaWxlU3luYyhmaWxlUGF0aCwgJ3V0ZjgnKVxuICAgICAgZXhjZXJwdCA9IGNvZGVFeGNlcnB0KHNvdXJjZUNvZGUsIG9yaWdpbi5saW5lKVxuXG4gICAgICBpZiAoZXhjZXJwdCkge1xuICAgICAgICBmb3IgKGNvbnN0IHsgbGluZSB9IG9mIGV4Y2VycHQpIHtcbiAgICAgICAgICBsaW5lV2lkdGggPSBNYXRoLm1heChsaW5lV2lkdGgsIFN0cmluZyhsaW5lKS5sZW5ndGgpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIHtcbiAgICAgIC8vIGZpbGUgbm90IHJlYWRhYmxlIOKAlCBza2lwIHNvdXJjZSBjb250ZXh0XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBwYWRkaW5nPXsxfT5cbiAgICAgIDxCb3g+XG4gICAgICAgIDxUZXh0IGJhY2tncm91bmRDb2xvcj1cImFuc2k6cmVkXCIgY29sb3I9XCJhbnNpOndoaXRlXCI+XG4gICAgICAgICAgeycgJ31cbiAgICAgICAgICBFUlJPUnsnICd9XG4gICAgICAgIDwvVGV4dD5cblxuICAgICAgICA8VGV4dD4ge2Vycm9yLm1lc3NhZ2V9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG5cbiAgICAgIHtvcmlnaW4gJiYgZmlsZVBhdGggJiYgKFxuICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgPFRleHQgZGltPlxuICAgICAgICAgICAge2ZpbGVQYXRofTp7b3JpZ2luLmxpbmV9OntvcmlnaW4uY29sdW1ufVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuXG4gICAgICB7b3JpZ2luICYmIGV4Y2VycHQgJiYgKFxuICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIHtleGNlcnB0Lm1hcCgoeyBsaW5lLCB2YWx1ZSB9KSA9PiAoXG4gICAgICAgICAgICA8Qm94IGtleT17bGluZX0+XG4gICAgICAgICAgICAgIDxCb3ggd2lkdGg9e2xpbmVXaWR0aCArIDF9PlxuICAgICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgICBkaW09e2xpbmUgIT09IG9yaWdpbi5saW5lfVxuICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtcbiAgICAgICAgICAgICAgICAgICAgbGluZSA9PT0gb3JpZ2luLmxpbmUgPyAnYW5zaTpyZWQnIDogdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICBjb2xvcj17bGluZSA9PT0gb3JpZ2luLmxpbmUgPyAnYW5zaTp3aGl0ZScgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAge1N0cmluZyhsaW5lKS5wYWRTdGFydChsaW5lV2lkdGgsICcgJyl9OlxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgPC9Cb3g+XG5cbiAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICBrZXk9e2xpbmV9XG4gICAgICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtsaW5lID09PSBvcmlnaW4ubGluZSA/ICdhbnNpOnJlZCcgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICAgICAgY29sb3I9e2xpbmUgPT09IG9yaWdpbi5saW5lID8gJ2Fuc2k6d2hpdGUnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgeycgJyArIHZhbHVlfVxuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICApKX1cbiAgICAgICAgPC9Cb3g+XG4gICAgICApfVxuXG4gICAgICB7ZXJyb3Iuc3RhY2sgJiYgKFxuICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgIHtlcnJvci5zdGFja1xuICAgICAgICAgICAgLnNwbGl0KCdcXG4nKVxuICAgICAgICAgICAgLnNsaWNlKDEpXG4gICAgICAgICAgICAubWFwKGxpbmUgPT4ge1xuICAgICAgICAgICAgICBjb25zdCBwYXJzZWRMaW5lID0gZ2V0U3RhY2tVdGlscygpLnBhcnNlTGluZShsaW5lKVxuXG4gICAgICAgICAgICAgIC8vIElmIHRoZSBsaW5lIGZyb20gdGhlIHN0YWNrIGNhbm5vdCBiZSBwYXJzZWQsIHdlIHByaW50IG91dCB0aGUgdW5wYXJzZWQgbGluZS5cbiAgICAgICAgICAgICAgaWYgKCFwYXJzZWRMaW5lKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICAgICAgIDxCb3gga2V5PXtsaW5lfT5cbiAgICAgICAgICAgICAgICAgICAgPFRleHQgZGltPi0gPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgICA8VGV4dCBib2xkPntsaW5lfTwvVGV4dD5cbiAgICAgICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgPEJveCBrZXk9e2xpbmV9PlxuICAgICAgICAgICAgICAgICAgPFRleHQgZGltPi0gPC9UZXh0PlxuICAgICAgICAgICAgICAgICAgPFRleHQgYm9sZD57cGFyc2VkTGluZS5mdW5jdGlvbn08L1RleHQ+XG4gICAgICAgICAgICAgICAgICA8VGV4dCBkaW0+XG4gICAgICAgICAgICAgICAgICAgIHsnICd9XG4gICAgICAgICAgICAgICAgICAgICh7Y2xlYW51cFBhdGgocGFyc2VkTGluZS5maWxlKSA/PyAnJ306e3BhcnNlZExpbmUubGluZX06XG4gICAgICAgICAgICAgICAgICAgIHtwYXJzZWRMaW5lLmNvbHVtbn0pXG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH0pfVxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgPC9Cb3g+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsV0FBVyxJQUFJLEtBQUtDLFdBQVcsUUFBUSxjQUFjO0FBQzVELFNBQVNDLFlBQVksUUFBUSxJQUFJO0FBQ2pDLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLE9BQU9DLFVBQVUsTUFBTSxhQUFhO0FBQ3BDLE9BQU9DLEdBQUcsTUFBTSxVQUFVO0FBQzFCLE9BQU9DLElBQUksTUFBTSxXQUFXOztBQUU1Qjs7QUFFQTtBQUNBO0FBQ0EsTUFBTUMsV0FBVyxHQUFHQSxDQUFDQyxJQUFJLEVBQUUsTUFBTSxHQUFHLFNBQVMsQ0FBQyxFQUFFLE1BQU0sR0FBRyxTQUFTLElBQUk7RUFDcEUsT0FBT0EsSUFBSSxFQUFFQyxPQUFPLENBQUMsVUFBVUMsT0FBTyxDQUFDQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDO0FBQ3RELENBQUM7QUFFRCxJQUFJQyxVQUFVLEVBQUVSLFVBQVUsR0FBRyxTQUFTO0FBQ3RDLFNBQVNTLGFBQWFBLENBQUEsQ0FBRSxFQUFFVCxVQUFVLENBQUM7RUFDbkMsT0FBUVEsVUFBVSxLQUFLLElBQUlSLFVBQVUsQ0FBQztJQUNwQ08sR0FBRyxFQUFFRCxPQUFPLENBQUNDLEdBQUcsQ0FBQyxDQUFDO0lBQ2xCRyxTQUFTLEVBQUVWLFVBQVUsQ0FBQ1csYUFBYSxDQUFDO0VBQ3RDLENBQUMsQ0FBQztBQUNKOztBQUVBOztBQUVBLEtBQUtDLEtBQUssR0FBRztFQUNYLFNBQVNDLEtBQUssRUFBRUMsS0FBSztBQUN2QixDQUFDO0FBRUQsZUFBZSxTQUFTQyxhQUFhQSxDQUFD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 \ No newline at end of file diff --git a/ink/components/Link.tsx b/ink/components/Link.tsx new file mode 100644 index 0000000..82341db --- /dev/null +++ b/ink/components/Link.tsx @@ -0,0 +1,42 @@ +import { c as _c } from "react/compiler-runtime"; +import type { ReactNode } from 'react'; +import React from 'react'; +import { supportsHyperlinks } from '../supports-hyperlinks.js'; +import Text from './Text.js'; +export type Props = { + readonly children?: ReactNode; + readonly url: string; + readonly fallback?: ReactNode; +}; +export default function Link(t0) { + const $ = _c(5); + const { + children, + url, + fallback + } = t0; + const content = children ?? url; + if (supportsHyperlinks()) { + let t1; + if ($[0] !== content || $[1] !== url) { + t1 = {content}; + $[0] = content; + $[1] = url; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; + } + const t1 = fallback ?? content; + let t2; + if ($[3] !== t1) { + t2 = {t1}; + $[3] = t1; + $[4] = t2; + } else { + t2 = $[4]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdE5vZGUiLCJSZWFjdCIsInN1cHBvcnRzSHlwZXJsaW5rcyIsIlRleHQiLCJQcm9wcyIsImNoaWxkcmVuIiwidXJsIiwiZmFsbGJhY2siLCJMaW5rIiwidDAiLCIkIiwiX2MiLCJjb250ZW50IiwidDEiLCJ0MiJdLCJzb3VyY2VzIjpbIkxpbmsudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBzdXBwb3J0c0h5cGVybGlua3MgfSBmcm9tICcuLi9zdXBwb3J0cy1oeXBlcmxpbmtzLmpzJ1xuaW1wb3J0IFRleHQgZnJvbSAnLi9UZXh0LmpzJ1xuXG5leHBvcnQgdHlwZSBQcm9wcyA9IHtcbiAgcmVhZG9ubHkgY2hpbGRyZW4/OiBSZWFjdE5vZGVcbiAgcmVhZG9ubHkgdXJsOiBzdHJpbmdcbiAgcmVhZG9ubHkgZmFsbGJhY2s/OiBSZWFjdE5vZGVcbn1cblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gTGluayh7XG4gIGNoaWxkcmVuLFxuICB1cmwsXG4gIGZhbGxiYWNrLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBVc2UgY2hpbGRyZW4gaWYgcHJvdmlkZWQsIG90aGVyd2lzZSBkaXNwbGF5IHRoZSBVUkxcbiAgY29uc3QgY29udGVudCA9IGNoaWxkcmVuID8/IHVybFxuXG4gIGlmIChzdXBwb3J0c0h5cGVybGlua3MoKSkge1xuICAgIC8vIFdyYXAgaW4gVGV4dCB0byBlbnN1cmUgd2UncmUgaW4gYSB0ZXh0IGNvbnRleHRcbiAgICAvLyAoaW5rLWxpbmsgaXMgYSB0ZXh0IGVsZW1lbnQgbGlrZSBpbmstdGV4dClcbiAgICByZXR1cm4gKFxuICAgICAgPFRleHQ+XG4gICAgICAgIDxpbmstbGluayBocmVmPXt1cmx9Pntjb250ZW50fTwvaW5rLWxpbms+XG4gICAgICA8L1RleHQ+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIDxUZXh0PntmYWxsYmFjayA/PyBjb250ZW50fTwvVGV4dD5cbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLGNBQWNBLFNBQVMsUUFBUSxPQUFPO0FBQ3RDLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLGtCQUFrQixRQUFRLDJCQUEyQjtBQUM5RCxPQUFPQyxJQUFJLE1BQU0sV0FBVztBQUU1QixPQUFPLEtBQUtDLEtBQUssR0FBRztFQUNsQixTQUFTQyxRQUFRLENBQUMsRUFBRUwsU0FBUztFQUM3QixTQUFTTSxHQUFHLEVBQUUsTUFBTTtFQUNwQixTQUFTQyxRQUFRLENBQUMsRUFBRVAsU0FBUztBQUMvQixDQUFDO0FBRUQsZUFBZSxTQUFBUSxLQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWM7SUFBQU4sUUFBQTtJQUFBQyxHQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFJckI7RUFFTixNQUFBRyxPQUFBLEdBQWdCUCxRQUFlLElBQWZDLEdBQWU7RUFFL0IsSUFBSUosa0JBQWtCLENBQUMsQ0FBQztJQUFBLElBQUFXLEVBQUE7SUFBQSxJQUFBSCxDQUFBLFFBQUFFLE9BQUEsSUFBQUYsQ0FBQSxRQUFBSixHQUFBO01BSXBCTyxFQUFBLElBQUMsSUFBSSxDQUNILFNBQXlDLENBQXpCUCxJQUFHLENBQUhBLElBQUUsQ0FBQyxDQUFHTSxRQUFNLENBQUUsRUFBOUIsUUFBeUMsQ0FDM0MsRUFGQyxJQUFJLENBRUU7TUFBQUYsQ0FBQSxNQUFBRSxPQUFBO01BQUFGLENBQUEsTUFBQUosR0FBQTtNQUFBSSxDQUFBLE1BQUFHLEVBQUE7SUFBQTtNQUFBQSxFQUFBLEdBQUFILENBQUE7SUFBQTtJQUFBLE9BRlBHLEVBRU87RUFBQTtFQUlHLE1BQUFBLEVBQUEsR0FBQU4sUUFBbUIsSUFBbkJLLE9BQW1CO0VBQUEsSUFBQUUsRUFBQTtFQUFBLElBQUFKLENBQUEsUUFBQUcsRUFBQTtJQUExQkMsRUFBQSxJQUFDLElBQUksQ0FBRSxDQUFBRCxFQUFrQixDQUFFLEVBQTFCLElBQUksQ0FBNkI7SUFBQUgsQ0FBQSxNQUFBRyxFQUFBO0lBQUFILENBQUEsTUFBQUksRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUosQ0FBQTtFQUFBO0VBQUEsT0FBbENJLEVBQWtDO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/ink/components/Newline.tsx b/ink/components/Newline.tsx new file mode 100644 index 0000000..5edf618 --- /dev/null +++ b/ink/components/Newline.tsx @@ -0,0 +1,39 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +export type Props = { + /** + * Number of newlines to insert. + * + * @default 1 + */ + readonly count?: number; +}; + +/** + * Adds one or more newline (\n) characters. Must be used within components. + */ +export default function Newline(t0) { + const $ = _c(4); + const { + count: t1 + } = t0; + const count = t1 === undefined ? 1 : t1; + let t2; + if ($[0] !== count) { + t2 = "\n".repeat(count); + $[0] = count; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== t2) { + t3 = {t2}; + $[2] = t2; + $[3] = t3; + } else { + t3 = $[3]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzIiwiY291bnQiLCJOZXdsaW5lIiwidDAiLCIkIiwiX2MiLCJ0MSIsInVuZGVmaW5lZCIsInQyIiwicmVwZWF0IiwidDMiXSwic291cmNlcyI6WyJOZXdsaW5lLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5cbmV4cG9ydCB0eXBlIFByb3BzID0ge1xuICAvKipcbiAgICogTnVtYmVyIG9mIG5ld2xpbmVzIHRvIGluc2VydC5cbiAgICpcbiAgICogQGRlZmF1bHQgMVxuICAgKi9cbiAgcmVhZG9ubHkgY291bnQ/OiBudW1iZXJcbn1cblxuLyoqXG4gKiBBZGRzIG9uZSBvciBtb3JlIG5ld2xpbmUgKFxcbikgY2hhcmFjdGVycy4gTXVzdCBiZSB1c2VkIHdpdGhpbiA8VGV4dD4gY29tcG9uZW50cy5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gTmV3bGluZSh7IGNvdW50ID0gMSB9OiBQcm9wcykge1xuICByZXR1cm4gPGluay10ZXh0PnsnXFxuJy5yZXBlYXQoY291bnQpfTwvaW5rLXRleHQ+XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUV6QixPQUFPLEtBQUtDLEtBQUssR0FBRztFQUNsQjtBQUNGO0FBQ0E7QUFDQTtBQUNBO0VBQ0UsU0FBU0MsS0FBSyxDQUFDLEVBQUUsTUFBTTtBQUN6QixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBLGVBQWUsU0FBQUMsUUFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFpQjtJQUFBSixLQUFBLEVBQUFLO0VBQUEsSUFBQUgsRUFBb0I7RUFBbEIsTUFBQUYsS0FBQSxHQUFBSyxFQUFTLEtBQVRDLFNBQVMsR0FBVCxDQUFTLEdBQVRELEVBQVM7RUFBQSxJQUFBRSxFQUFBO0VBQUEsSUFBQUosQ0FBQSxRQUFBSCxLQUFBO0lBQ3ZCTyxFQUFBLE9BQUksQ0FBQUMsTUFBTyxDQUFDUixLQUFLLENBQUM7SUFBQUcsQ0FBQSxNQUFBSCxLQUFBO0lBQUFHLENBQUEsTUFBQUksRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUosQ0FBQTtFQUFBO0VBQUEsSUFBQU0sRUFBQTtFQUFBLElBQUFOLENBQUEsUUFBQUksRUFBQTtJQUE3QkUsRUFBQSxZQUF5QyxDQUE5QixDQUFBRixFQUFpQixDQUFFLEVBQTlCLFFBQXlDO0lBQUFKLENBQUEsTUFBQUksRUFBQTtJQUFBSixDQUFBLE1BQUFNLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFOLENBQUE7RUFBQTtFQUFBLE9BQXpDTSxFQUF5QztBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/ink/components/NoSelect.tsx b/ink/components/NoSelect.tsx new file mode 100644 index 0000000..d21b8d7 --- /dev/null +++ b/ink/components/NoSelect.tsx @@ -0,0 +1,68 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type PropsWithChildren } from 'react'; +import Box, { type Props as BoxProps } from './Box.js'; +type Props = Omit & { + /** + * Extend the exclusion zone from column 0 to this box's right edge, + * for every row this box occupies. Use for gutters rendered inside a + * wider indented container (e.g. a diff inside a tool message row): + * without this, a multi-row drag picks up the container's leading + * indent on rows below the prefix. + * + * @default false + */ + fromLeftEdge?: boolean; +}; + +/** + * Marks its contents as non-selectable in fullscreen text selection. + * Cells inside this box are skipped by both the selection highlight and + * the copied text — the gutter stays visually unchanged while the user + * drags, making it clear what will be copied. + * + * Use to fence off gutters (line numbers, diff +/- sigils, list bullets) + * so click-drag over rendered code yields clean pasteable content: + * + * + * 42 + + * const x = 1 + * + * + * Only affects alt-screen text selection ( with mouse + * tracking). No-op in the main-screen scrollback render where the + * terminal's native selection is used instead. + */ +export function NoSelect(t0) { + const $ = _c(8); + let boxProps; + let children; + let fromLeftEdge; + if ($[0] !== t0) { + ({ + children, + fromLeftEdge, + ...boxProps + } = t0); + $[0] = t0; + $[1] = boxProps; + $[2] = children; + $[3] = fromLeftEdge; + } else { + boxProps = $[1]; + children = $[2]; + fromLeftEdge = $[3]; + } + const t1 = fromLeftEdge ? "from-left-edge" : true; + let t2; + if ($[4] !== boxProps || $[5] !== children || $[6] !== t1) { + t2 = {children}; + $[4] = boxProps; + $[5] = children; + $[6] = t1; + $[7] = t2; + } else { + t2 = $[7]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzV2l0aENoaWxkcmVuIiwiQm94IiwiUHJvcHMiLCJCb3hQcm9wcyIsIk9taXQiLCJmcm9tTGVmdEVkZ2UiLCJOb1NlbGVjdCIsInQwIiwiJCIsIl9jIiwiYm94UHJvcHMiLCJjaGlsZHJlbiIsInQxIiwidDIiXSwic291cmNlcyI6WyJOb1NlbGVjdC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IHR5cGUgUHJvcHNXaXRoQ2hpbGRyZW4gfSBmcm9tICdyZWFjdCdcbmltcG9ydCBCb3gsIHsgdHlwZSBQcm9wcyBhcyBCb3hQcm9wcyB9IGZyb20gJy4vQm94LmpzJ1xuXG50eXBlIFByb3BzID0gT21pdDxCb3hQcm9wcywgJ25vU2VsZWN0Jz4gJiB7XG4gIC8qKlxuICAgKiBFeHRlbmQgdGhlIGV4Y2x1c2lvbiB6b25lIGZyb20gY29sdW1uIDAgdG8gdGhpcyBib3gncyByaWdodCBlZGdlLFxuICAgKiBmb3IgZXZlcnkgcm93IHRoaXMgYm94IG9jY3VwaWVzLiBVc2UgZm9yIGd1dHRlcnMgcmVuZGVyZWQgaW5zaWRlIGFcbiAgICogd2lkZXIgaW5kZW50ZWQgY29udGFpbmVyIChlLmcuIGEgZGlmZiBpbnNpZGUgYSB0b29sIG1lc3NhZ2Ugcm93KTpcbiAgICogd2l0aG91dCB0aGlzLCBhIG11bHRpLXJvdyBkcmFnIHBpY2tzIHVwIHRoZSBjb250YWluZXIncyBsZWFkaW5nXG4gICAqIGluZGVudCBvbiByb3dzIGJlbG93IHRoZSBwcmVmaXguXG4gICAqXG4gICAqIEBkZWZhdWx0IGZhbHNlXG4gICAqL1xuICBmcm9tTGVmdEVkZ2U/OiBib29sZWFuXG59XG5cbi8qKlxuICogTWFya3MgaXRzIGNvbnRlbnRzIGFzIG5vbi1zZWxlY3RhYmxlIGluIGZ1bGxzY3JlZW4gdGV4dCBzZWxlY3Rpb24uXG4gKiBDZWxscyBpbnNpZGUgdGhpcyBib3ggYXJlIHNraXBwZWQgYnkgYm90aCB0aGUgc2VsZWN0aW9uIGhpZ2hsaWdodCBhbmRcbiAqIHRoZSBjb3BpZWQgdGV4dCDigJQgdGhlIGd1dHRlciBzdGF5cyB2aXN1YWxseSB1bmNoYW5nZWQgd2hpbGUgdGhlIHVzZXJcbiAqIGRyYWdzLCBtYWtpbmcgaXQgY2xlYXIgd2hhdCB3aWxsIGJlIGNvcGllZC5cbiAqXG4gKiBVc2UgdG8gZmVuY2Ugb2ZmIGd1dHRlcnMgKGxpbmUgbnVtYmVycywgZGlmZiArLy0gc2lnaWxzLCBsaXN0IGJ1bGxldHMpXG4gKiBzbyBjbGljay1kcmFnIG92ZXIgcmVuZGVyZWQgY29kZSB5aWVsZHMgY2xlYW4gcGFzdGVhYmxlIGNvbnRlbnQ6XG4gKlxuICogICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIj5cbiAqICAgICA8Tm9TZWxlY3QgZnJvbUxlZnRFZGdlPjxUZXh0IGRpbUNvbG9yPiA0MiArPC9UZXh0PjwvTm9TZWxlY3Q+XG4gKiAgICAgPFRleHQ+Y29uc3QgeCA9IDE8L1RleHQ+XG4gKiAgIDwvQm94PlxuICpcbiAqIE9ubHkgYWZmZWN0cyBhbHQtc2NyZWVuIHRleHQgc2VsZWN0aW9uICg8QWx0ZXJuYXRlU2NyZWVuPiB3aXRoIG1vdXNlXG4gKiB0cmFja2luZykuIE5vLW9wIGluIHRoZSBtYWluLXNjcmVlbiBzY3JvbGxiYWNrIHJlbmRlciB3aGVyZSB0aGVcbiAqIHRlcm1pbmFsJ3MgbmF0aXZlIHNlbGVjdGlvbiBpcyB1c2VkIGluc3RlYWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBOb1NlbGVjdCh7XG4gIGNoaWxkcmVuLFxuICBmcm9tTGVmdEVkZ2UsXG4gIC4uLmJveFByb3BzXG59OiBQcm9wc1dpdGhDaGlsZHJlbjxQcm9wcz4pOiBSZWFjdC5SZWFjdE5vZGUge1xuICByZXR1cm4gKFxuICAgIDxCb3ggey4uLmJveFByb3BzfSBub1NlbGVjdD17ZnJvbUxlZnRFZGdlID8gJ2Zyb20tbGVmdC1lZGdlJyA6IHRydWV9PlxuICAgICAge2NoaWxkcmVufVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUksS0FBS0MsaUJBQWlCLFFBQVEsT0FBTztBQUNyRCxPQUFPQyxHQUFHLElBQUksS0FBS0MsS0FBSyxJQUFJQyxRQUFRLFFBQVEsVUFBVTtBQUV0RCxLQUFLRCxLQUFLLEdBQUdFLElBQUksQ0FBQ0QsUUFBUSxFQUFFLFVBQVUsQ0FBQyxHQUFHO0VBQ3hDO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtFQUNFRSxZQUFZLENBQUMsRUFBRSxPQUFPO0FBQ3hCLENBQUM7O0FBRUQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTyxTQUFBQyxTQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQUEsSUFBQUMsUUFBQTtFQUFBLElBQUFDLFFBQUE7RUFBQSxJQUFBTixZQUFBO0VBQUEsSUFBQUcsQ0FBQSxRQUFBRCxFQUFBO0lBQWtCO01BQUFJLFFBQUE7TUFBQU4sWUFBQTtNQUFBLEdBQUFLO0lBQUEsSUFBQUgsRUFJRTtJQUFBQyxDQUFBLE1BQUFELEVBQUE7SUFBQUMsQ0FBQSxNQUFBRSxRQUFBO0lBQUFGLENBQUEsTUFBQUcsUUFBQTtJQUFBSCxDQUFBLE1BQUFILFlBQUE7RUFBQTtJQUFBSyxRQUFBLEdBQUFGLENBQUE7SUFBQUcsUUFBQSxHQUFBSCxDQUFBO0lBQUFILFlBQUEsR0FBQUcsQ0FBQTtFQUFBO0VBRU0sTUFBQUksRUFBQSxHQUFBUCxZQUFZLEdBQVosZ0JBQXNDLEdBQXRDLElBQXNDO0VBQUEsSUFBQVEsRUFBQTtFQUFBLElBQUFMLENBQUEsUUFBQUUsUUFBQSxJQUFBRixDQUFBLFFBQUFHLFFBQUEsSUFBQUgsQ0FBQSxRQUFBSSxFQUFBO0lBQW5FQyxFQUFBLElBQUMsR0FBRyxLQUFLSCxRQUFRLEVBQVksUUFBc0MsQ0FBdEMsQ0FBQUUsRUFBcUMsQ0FBQyxDQUNoRUQsU0FBTyxDQUNWLEVBRkMsR0FBRyxDQUVFO0lBQUFILENBQUEsTUFBQUUsUUFBQTtJQUFBRixDQUFBLE1BQUFHLFFBQUE7SUFBQUgsQ0FBQSxNQUFBSSxFQUFBO0lBQUFKLENBQUEsTUFBQUssRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUwsQ0FBQTtFQUFBO0VBQUEsT0FGTkssRUFFTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/ink/components/RawAnsi.tsx b/ink/components/RawAnsi.tsx new file mode 100644 index 0000000..919e453 --- /dev/null +++ b/ink/components/RawAnsi.tsx @@ -0,0 +1,57 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +type Props = { + /** + * Pre-rendered ANSI lines. Each element must be exactly one terminal row + * (already wrapped to `width` by the producer) with ANSI escape codes inline. + */ + lines: string[]; + /** Column width the producer wrapped to. Sent to Yoga as the fixed leaf width. */ + width: number; +}; + +/** + * Bypass the → React tree → Yoga → squash → re-serialize roundtrip for + * content that is already terminal-ready. + * + * Use this when an external renderer (e.g. the ColorDiff NAPI module) has + * already produced ANSI-escaped, width-wrapped output. A normal mount + * reparses that output into one React per style span, lays out each + * span as a Yoga flex child, then walks the tree to re-emit the same escape + * codes it was given. For a long transcript full of syntax-highlighted diffs + * that roundtrip is the dominant cost of the render. + * + * This component emits a single Yoga leaf with a constant-time measure func + * (width × lines.length) and hands the joined string straight to output.write(), + * which already splits on '\n' and parses ANSI into the screen buffer. + */ +export function RawAnsi(t0) { + const $ = _c(6); + const { + lines, + width + } = t0; + if (lines.length === 0) { + return null; + } + let t1; + if ($[0] !== lines) { + t1 = lines.join("\n"); + $[0] = lines; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== lines.length || $[3] !== t1 || $[4] !== width) { + t2 = ; + $[2] = lines.length; + $[3] = t1; + $[4] = width; + $[5] = t2; + } else { + t2 = $[5]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzIiwibGluZXMiLCJ3aWR0aCIsIlJhd0Fuc2kiLCJ0MCIsIiQiLCJfYyIsImxlbmd0aCIsInQxIiwiam9pbiIsInQyIl0sInNvdXJjZXMiOlsiUmF3QW5zaS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuXG50eXBlIFByb3BzID0ge1xuICAvKipcbiAgICogUHJlLXJlbmRlcmVkIEFOU0kgbGluZXMuIEVhY2ggZWxlbWVudCBtdXN0IGJlIGV4YWN0bHkgb25lIHRlcm1pbmFsIHJvd1xuICAgKiAoYWxyZWFkeSB3cmFwcGVkIHRvIGB3aWR0aGAgYnkgdGhlIHByb2R1Y2VyKSB3aXRoIEFOU0kgZXNjYXBlIGNvZGVzIGlubGluZS5cbiAgICovXG4gIGxpbmVzOiBzdHJpbmdbXVxuICAvKiogQ29sdW1uIHdpZHRoIHRoZSBwcm9kdWNlciB3cmFwcGVkIHRvLiBTZW50IHRvIFlvZ2EgYXMgdGhlIGZpeGVkIGxlYWYgd2lkdGguICovXG4gIHdpZHRoOiBudW1iZXJcbn1cblxuLyoqXG4gKiBCeXBhc3MgdGhlIDxBbnNpPiDihpIgUmVhY3QgdHJlZSDihpIgWW9nYSDihpIgc3F1YXNoIOKGkiByZS1zZXJpYWxpemUgcm91bmR0cmlwIGZvclxuICogY29udGVudCB0aGF0IGlzIGFscmVhZHkgdGVybWluYWwtcmVhZHkuXG4gKlxuICogVXNlIHRoaXMgd2hlbiBhbiBleHRlcm5hbCByZW5kZXJlciAoZS5nLiB0aGUgQ29sb3JEaWZmIE5BUEkgbW9kdWxlKSBoYXNcbiAqIGFscmVhZHkgcHJvZHVjZWQgQU5TSS1lc2NhcGVkLCB3aWR0aC13cmFwcGVkIG91dHB1dC4gQSBub3JtYWwgPEFuc2k+IG1vdW50XG4gKiByZXBhcnNlcyB0aGF0IG91dHB1dCBpbnRvIG9uZSBSZWFjdCA8VGV4dD4gcGVyIHN0eWxlIHNwYW4sIGxheXMgb3V0IGVhY2hcbiAqIHNwYW4gYXMgYSBZb2dhIGZsZXggY2hpbGQsIHRoZW4gd2Fsa3MgdGhlIHRyZWUgdG8gcmUtZW1pdCB0aGUgc2FtZSBlc2NhcGVcbiAqIGNvZGVzIGl0IHdhcyBnaXZlbi4gRm9yIGEgbG9uZyB0cmFuc2NyaXB0IGZ1bGwgb2Ygc3ludGF4LWhpZ2hsaWdodGVkIGRpZmZzXG4gKiB0aGF0IHJvdW5kdHJpcCBpcyB0aGUgZG9taW5hbnQgY29zdCBvZiB0aGUgcmVuZGVyLlxuICpcbiAqIFRoaXMgY29tcG9uZW50IGVtaXRzIGEgc2luZ2xlIFlvZ2EgbGVhZiB3aXRoIGEgY29uc3RhbnQtdGltZSBtZWFzdXJlIGZ1bmNcbiAqICh3aWR0aCDDlyBsaW5lcy5sZW5ndGgpIGFuZCBoYW5kcyB0aGUgam9pbmVkIHN0cmluZyBzdHJhaWdodCB0byBvdXRwdXQud3JpdGUoKSxcbiAqIHdoaWNoIGFscmVhZHkgc3BsaXRzIG9uICdcXG4nIGFuZCBwYXJzZXMgQU5TSSBpbnRvIHRoZSBzY3JlZW4gYnVmZmVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gUmF3QW5zaSh7IGxpbmVzLCB3aWR0aCB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGlmIChsaW5lcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG4gIHJldHVybiAoXG4gICAgPGluay1yYXctYW5zaVxuICAgICAgcmF3VGV4dD17bGluZXMuam9pbignXFxuJyl9XG4gICAgICByYXdXaWR0aD17d2lkdGh9XG4gICAgICByYXdIZWlnaHQ9e2xpbmVzLmxlbmd0aH1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUV6QixLQUFLQyxLQUFLLEdBQUc7RUFDWDtBQUNGO0FBQ0E7QUFDQTtFQUNFQyxLQUFLLEVBQUUsTUFBTSxFQUFFO0VBQ2Y7RUFDQUMsS0FBSyxFQUFFLE1BQU07QUFDZixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU8sU0FBQUMsUUFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFpQjtJQUFBTCxLQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFBdUI7RUFDN0MsSUFBSUgsS0FBSyxDQUFBTSxNQUFPLEtBQUssQ0FBQztJQUFBLE9BQ2IsSUFBSTtFQUFBO0VBQ1osSUFBQUMsRUFBQTtFQUFBLElBQUFILENBQUEsUUFBQUosS0FBQTtJQUdZTyxFQUFBLEdBQUFQLEtBQUssQ0FBQVEsSUFBSyxDQUFDLElBQUksQ0FBQztJQUFBSixDQUFBLE1BQUFKLEtBQUE7SUFBQUksQ0FBQSxNQUFBRyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSCxDQUFBO0VBQUE7RUFBQSxJQUFBSyxFQUFBO0VBQUEsSUFBQUwsQ0FBQSxRQUFBSixLQUFBLENBQUFNLE1BQUEsSUFBQUYsQ0FBQSxRQUFBRyxFQUFBLElBQUFILENBQUEsUUFBQUgsS0FBQTtJQUQzQlEsRUFBQSxnQkFJRSxDQUhTLE9BQWdCLENBQWhCLENBQUFGLEVBQWUsQ0FBQyxDQUNmTixRQUFLLENBQUxBLE1BQUksQ0FBQyxDQUNKLFNBQVksQ0FBWixDQUFBRCxLQUFLLENBQUFNLE1BQU0sQ0FBQyxHQUN2QjtJQUFBRixDQUFBLE1BQUFKLEtBQUEsQ0FBQU0sTUFBQTtJQUFBRixDQUFBLE1BQUFHLEVBQUE7SUFBQUgsQ0FBQSxNQUFBSCxLQUFBO0lBQUFHLENBQUEsTUFBQUssRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUwsQ0FBQTtFQUFBO0VBQUEsT0FKRkssRUFJRTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/ink/components/ScrollBox.tsx b/ink/components/ScrollBox.tsx new file mode 100644 index 0000000..03e4a31 --- /dev/null +++ b/ink/components/ScrollBox.tsx @@ -0,0 +1,237 @@ +import React, { type PropsWithChildren, type Ref, useImperativeHandle, useRef, useState } from 'react'; +import type { Except } from 'type-fest'; +import { markScrollActivity } from '../../bootstrap/state.js'; +import type { DOMElement } from '../dom.js'; +import { markDirty, scheduleRenderFrom } from '../dom.js'; +import { markCommitStart } from '../reconciler.js'; +import type { Styles } from '../styles.js'; +import '../global.d.ts'; +import Box from './Box.js'; +export type ScrollBoxHandle = { + scrollTo: (y: number) => void; + scrollBy: (dy: number) => void; + /** + * Scroll so `el`'s top is at the viewport top (plus `offset`). Unlike + * scrollTo which bakes a number that's stale by the time the throttled + * render fires, this defers the position read to render time — + * render-node-to-output reads `el.yogaNode.getComputedTop()` in the + * SAME Yoga pass that computes scrollHeight. Deterministic. One-shot. + */ + scrollToElement: (el: DOMElement, offset?: number) => void; + scrollToBottom: () => void; + getScrollTop: () => number; + getPendingDelta: () => number; + getScrollHeight: () => number; + /** + * Like getScrollHeight, but reads Yoga directly instead of the cached + * value written by render-node-to-output (throttled, up to 16ms stale). + * Use when you need a fresh value in useLayoutEffect after a React commit + * that grew content. Slightly more expensive (native Yoga call). + */ + getFreshScrollHeight: () => number; + getViewportHeight: () => number; + /** + * Absolute screen-buffer row of the first visible content line (inside + * padding). Used for drag-to-scroll edge detection. + */ + getViewportTop: () => number; + /** + * True when scroll is pinned to the bottom. Set by scrollToBottom, the + * initial stickyScroll attribute, and by the renderer when positional + * follow fires (scrollTop at prevMax, content grows). Cleared by + * scrollTo/scrollBy. Stable signal for "at bottom" that doesn't depend on + * layout values (unlike scrollTop+viewportH >= scrollHeight). + */ + isSticky: () => boolean; + /** + * Subscribe to imperative scroll changes (scrollTo/scrollBy/scrollToBottom). + * Does NOT fire for stickyScroll updates done by the Ink renderer — those + * happen during Ink's render phase after React has committed. Callers that + * care about the sticky case should treat "at bottom" as a fallback. + */ + subscribe: (listener: () => void) => () => void; + /** + * Set the render-time scrollTop clamp to the currently-mounted children's + * coverage span. Called by useVirtualScroll after computing its range; + * render-node-to-output clamps scrollTop to [min, max] so burst scrollTo + * calls that race past React's async re-render show the edge of mounted + * content instead of blank spacer. Pass undefined to disable (sticky, + * cold start). + */ + setClampBounds: (min: number | undefined, max: number | undefined) => void; +}; +export type ScrollBoxProps = Except & { + ref?: Ref; + /** + * When true, automatically pins scroll position to the bottom when content + * grows. Unset manually via scrollTo/scrollBy to break the stickiness. + */ + stickyScroll?: boolean; +}; + +/** + * A Box with `overflow: scroll` and an imperative scroll API. + * + * Children are laid out at their full Yoga-computed height inside a + * constrained container. At render time, only children intersecting the + * visible window (scrollTop..scrollTop+height) are rendered (viewport + * culling). Content is translated by -scrollTop and clipped to the box bounds. + * + * Works best inside a fullscreen (constrained-height root) Ink tree. + */ +function ScrollBox({ + children, + ref, + stickyScroll, + ...style +}: PropsWithChildren): React.ReactNode { + const domRef = useRef(null); + // scrollTo/scrollBy bypass React: they mutate scrollTop on the DOM node, + // mark it dirty, and call the root's throttled scheduleRender directly. + // The Ink renderer reads scrollTop from the node — no React state needed, + // no reconciler overhead per wheel event. The microtask defer coalesces + // multiple scrollBy calls in one input batch (discreteUpdates) into one + // render — otherwise scheduleRender's leading edge fires on the FIRST + // event before subsequent events mutate scrollTop. scrollToBottom still + // forces a React render: sticky is attribute-observed, no DOM-only path. + const [, forceRender] = useState(0); + const listenersRef = useRef(new Set<() => void>()); + const renderQueuedRef = useRef(false); + const notify = () => { + for (const l of listenersRef.current) l(); + }; + function scrollMutated(el: DOMElement): void { + // Signal background intervals (IDE poll, LSP poll, GCS fetch, orphan + // check) to skip their next tick — they compete for the event loop and + // contributed to 1402ms max frame gaps during scroll drain. + markScrollActivity(); + markDirty(el); + markCommitStart(); + notify(); + if (renderQueuedRef.current) return; + renderQueuedRef.current = true; + queueMicrotask(() => { + renderQueuedRef.current = false; + scheduleRenderFrom(el); + }); + } + useImperativeHandle(ref, (): ScrollBoxHandle => ({ + scrollTo(y: number) { + const el = domRef.current; + if (!el) return; + // Explicit false overrides the DOM attribute so manual scroll + // breaks stickiness. Render code checks ?? precedence. + el.stickyScroll = false; + el.pendingScrollDelta = undefined; + el.scrollAnchor = undefined; + el.scrollTop = Math.max(0, Math.floor(y)); + scrollMutated(el); + }, + scrollToElement(el: DOMElement, offset = 0) { + const box = domRef.current; + if (!box) return; + box.stickyScroll = false; + box.pendingScrollDelta = undefined; + box.scrollAnchor = { + el, + offset + }; + scrollMutated(box); + }, + scrollBy(dy: number) { + const el = domRef.current; + if (!el) return; + el.stickyScroll = false; + // Wheel input cancels any in-flight anchor seek — user override. + el.scrollAnchor = undefined; + // Accumulate in pendingScrollDelta; renderer drains it at a capped + // rate so fast flicks show intermediate frames. Pure accumulator: + // scroll-up followed by scroll-down naturally cancels. + el.pendingScrollDelta = (el.pendingScrollDelta ?? 0) + Math.floor(dy); + scrollMutated(el); + }, + scrollToBottom() { + const el = domRef.current; + if (!el) return; + el.pendingScrollDelta = undefined; + el.stickyScroll = true; + markDirty(el); + notify(); + forceRender(n => n + 1); + }, + getScrollTop() { + return domRef.current?.scrollTop ?? 0; + }, + getPendingDelta() { + // Accumulated-but-not-yet-drained delta. useVirtualScroll needs + // this to mount the union [committed, committed+pending] range — + // otherwise intermediate drain frames find no children (blank). + return domRef.current?.pendingScrollDelta ?? 0; + }, + getScrollHeight() { + return domRef.current?.scrollHeight ?? 0; + }, + getFreshScrollHeight() { + const content = domRef.current?.childNodes[0] as DOMElement | undefined; + return content?.yogaNode?.getComputedHeight() ?? domRef.current?.scrollHeight ?? 0; + }, + getViewportHeight() { + return domRef.current?.scrollViewportHeight ?? 0; + }, + getViewportTop() { + return domRef.current?.scrollViewportTop ?? 0; + }, + isSticky() { + const el = domRef.current; + if (!el) return false; + return el.stickyScroll ?? Boolean(el.attributes['stickyScroll']); + }, + subscribe(listener: () => void) { + listenersRef.current.add(listener); + return () => listenersRef.current.delete(listener); + }, + setClampBounds(min, max) { + const el = domRef.current; + if (!el) return; + el.scrollClampMin = min; + el.scrollClampMax = max; + } + }), + // notify/scrollMutated are inline (no useCallback) but only close over + // refs + imports — stable. Empty deps avoids rebuilding the handle on + // every render (which re-registers the ref = churn). + // eslint-disable-next-line react-hooks/exhaustive-deps + []); + + // Structure: outer viewport (overflow:scroll, constrained height) > + // inner content (flexGrow:1, flexShrink:0 — fills at least the viewport + // but grows beyond it for tall content). flexGrow:1 lets children use + // spacers to pin elements to the bottom of the scroll area. Yoga's + // Overflow.Scroll prevents the viewport from growing to fit the content. + // The renderer computes scrollHeight from the content box and culls + // content's children based on scrollTop. + // + // stickyScroll is passed as a DOM attribute (via ink-box directly) so it's + // available on the first render — ref callbacks fire after the initial + // commit, which is too late for the first frame. + return { + domRef.current = el; + if (el) el.scrollTop ??= 0; + }} style={{ + flexWrap: 'nowrap', + flexDirection: style.flexDirection ?? 'row', + flexGrow: style.flexGrow ?? 0, + flexShrink: style.flexShrink ?? 1, + ...style, + overflowX: 'scroll', + overflowY: 'scroll' + }} {...stickyScroll ? { + stickyScroll: true + } : {}}> + + {children} + + ; +} +export default ScrollBox; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzV2l0aENoaWxkcmVuIiwiUmVmIiwidXNlSW1wZXJhdGl2ZUhhbmRsZSIsInVzZVJlZiIsInVzZVN0YXRlIiwiRXhjZXB0IiwibWFya1Njcm9sbEFjdGl2aXR5IiwiRE9NRWxlbWVudCIsIm1hcmtEaXJ0eSIsInNjaGVkdWxlUmVuZGVyRnJvbSIsIm1hcmtDb21taXRTdGFydCIsIlN0eWxlcyIsIkJveCIsIlNjcm9sbEJveEhhbmRsZSIsInNjcm9sbFRvIiwieSIsInNjcm9sbEJ5IiwiZHkiLCJzY3JvbGxUb0VsZW1lbnQiLCJlbCIsIm9mZnNldCIsInNjcm9sbFRvQm90dG9tIiwiZ2V0U2Nyb2xsVG9wIiwiZ2V0UGVuZGluZ0RlbHRhIiwiZ2V0U2Nyb2xsSGVpZ2h0IiwiZ2V0RnJlc2hTY3JvbGxIZWlnaHQiLCJnZXRWaWV3cG9ydEhlaWdodCIsImdldFZpZXdwb3J0VG9wIiwiaXNTdGlja3kiLCJzdWJzY3JpYmUiLCJsaXN0ZW5lciIsInNldENsYW1wQm91bmRzIiwibWluIiwibWF4IiwiU2Nyb2xsQm94UHJvcHMiLCJyZWYiLCJzdGlja3lTY3JvbGwiLCJTY3JvbGxCb3giLCJjaGlsZHJlbiIsInN0eWxlIiwiUmVhY3ROb2RlIiwiZG9tUmVmIiwiZm9yY2VSZW5kZXIiLCJsaXN0ZW5lcnNSZWYiLCJTZXQiLCJyZW5kZXJRdWV1ZWRSZWYiLCJub3RpZnkiLCJsIiwiY3VycmVudCIsInNjcm9sbE11dGF0ZWQiLCJxdWV1ZU1pY3JvdGFzayIsInBlbmRpbmdTY3JvbGxEZWx0YSIsInVuZGVmaW5lZCIsInNjcm9sbEFuY2hvciIsInNjcm9sbFRvcCIsIk1hdGgiLCJmbG9vciIsImJveCIsIm4iLCJzY3JvbGxIZWlnaHQiLCJjb250ZW50IiwiY2hpbGROb2RlcyIsInlvZ2FOb2RlIiwiZ2V0Q29tcHV0ZWRIZWlnaHQiLCJzY3JvbGxWaWV3cG9ydEhlaWdodCIsInNjcm9sbFZpZXdwb3J0VG9wIiwiQm9vbGVhbiIsImF0dHJpYnV0ZXMiLCJhZGQiLCJkZWxldGUiLCJzY3JvbGxDbGFtcE1pbiIsInNjcm9sbENsYW1wTWF4IiwiZmxleFdyYXAiLCJmbGV4RGlyZWN0aW9uIiwiZmxleEdyb3ciLCJmbGV4U2hyaW5rIiwib3ZlcmZsb3dYIiwib3ZlcmZsb3dZIl0sInNvdXJjZXMiOlsiU2Nyb2xsQm94LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHtcbiAgdHlwZSBQcm9wc1dpdGhDaGlsZHJlbixcbiAgdHlwZSBSZWYsXG4gIHVzZUltcGVyYXRpdmVIYW5kbGUsXG4gIHVzZVJlZixcbiAgdXNlU3RhdGUsXG59IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBFeGNlcHQgfSBmcm9tICd0eXBlLWZlc3QnXG5pbXBvcnQgeyBtYXJrU2Nyb2xsQWN0aXZpdHkgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IERPTUVsZW1lbnQgfSBmcm9tICcuLi9kb20uanMnXG5pbXBvcnQgeyBtYXJrRGlydHksIHNjaGVkdWxlUmVuZGVyRnJvbSB9IGZyb20gJy4uL2RvbS5qcydcbmltcG9ydCB7IG1hcmtDb21taXRTdGFydCB9IGZyb20gJy4uL3JlY29uY2lsZXIuanMnXG5pbXBvcnQgdHlwZSB7IFN0eWxlcyB9IGZyb20gJy4uL3N0eWxlcy5qcydcbmltcG9ydCAnLi4vZ2xvYmFsLmQudHMnXG5pbXBvcnQgQm94IGZyb20gJy4vQm94LmpzJ1xuXG5leHBvcnQgdHlwZSBTY3JvbGxCb3hIYW5kbGUgPSB7XG4gIHNjcm9sbFRvOiAoeTogbnVtYmVyKSA9PiB2b2lkXG4gIHNjcm9sbEJ5OiAoZHk6IG51bWJlcikgPT4gdm9pZFxuICAvKipcbiAgICogU2Nyb2xsIHNvIGBlbGAncyB0b3AgaXMgYXQgdGhlIHZpZXdwb3J0IHRvcCAocGx1cyBgb2Zmc2V0YCkuIFVubGlrZVxuICAgKiBzY3JvbGxUbyB3aGljaCBiYWtlcyBhIG51bWJlciB0aGF0J3Mgc3RhbGUgYnkgdGhlIHRpbWUgdGhlIHRocm90dGxlZFxuICAgKiByZW5kZXIgZmlyZXMsIHRoaXMgZGVmZXJzIHRoZSBwb3NpdGlvbiByZWFkIHRvIHJlbmRlciB0aW1lIOKAlFxuICAgKiByZW5kZXItbm9kZS10by1vdXRwdXQgcmVhZHMgYGVsLnlvZ2FOb2RlLmdldENvbXB1dGVkVG9wKClgIGluIHRoZVxuICAgKiBTQU1FIFlvZ2EgcGFzcyB0aGF0IGNvbXB1dGVzIHNjcm9sbEhlaWdodC4gRGV0ZXJtaW5pc3RpYy4gT25lLXNob3QuXG4gICAqL1xuICBzY3JvbGxUb0VsZW1lbnQ6IChlbDogRE9NRWxlbWVudCwgb2Zmc2V0PzogbnVtYmVyKSA9PiB2b2lkXG4gIHNjcm9sbFRvQm90dG9tOiAoKSA9PiB2b2lkXG4gIGdldFNjcm9sbFRvcDogKCkgPT4gbnVtYmVyXG4gIGdldFBlbmRpbmdEZWx0YTogKCkgPT4gbnVtYmVyXG4gIGdldFNjcm9sbEhlaWdodDogKCkgPT4gbnVtYmVyXG4gIC8qKlxuICAgKiBMaWtlIGdldFNjcm9sbEhlaWdodCwgYnV0IHJlYWRzIFlvZ2EgZGlyZWN0bHkgaW5zdGVhZCBvZiB0aGUgY2FjaGVkXG4gICAqIHZhbHVlIHdyaXR0ZW4gYnkgcmVuZGVyLW5vZGUtdG8tb3V0cHV0ICh0aHJvdHRsZWQsIHVwIHRvIDE2bXMgc3RhbGUpLlxuICAgKiBVc2Ugd2hlbiB5b3UgbmVlZCBhIGZyZXNoIHZhbHVlIGluIHVzZUxheW91dEVmZmVjdCBhZnRlciBhIFJlYWN0IGNvbW1pdFxuICAgKiB0aGF0IGdyZXcgY29udGVudC4gU2xpZ2h0bHkgbW9yZSBleHBlbnNpdmUgKG5hdGl2ZSBZb2dhIGNhbGwpLlxuICAgKi9cbiAgZ2V0RnJlc2hTY3JvbGxIZWlnaHQ6ICgpID0+IG51bWJlclxuICBnZXRWaWV3cG9ydEhlaWdodDogKCkgPT4gbnVtYmVyXG4gIC8qKlxuICAgKiBBYnNvbHV0ZSBzY3JlZW4tYnVmZmVyIHJvdyBvZiB0aGUgZmlyc3QgdmlzaWJsZSBjb250ZW50IGxpbmUgKGluc2lkZVxuICAgKiBwYWRkaW5nKS4gVXNlZCBmb3IgZHJhZy10by1zY3JvbGwgZWRnZSBkZXRlY3Rpb24uXG4gICAqL1xuICBnZXRWaWV3cG9ydFRvcDogKCkgPT4gbnVtYmVyXG4gIC8qKlxuICAgKiBUcnVlIHdoZW4gc2Nyb2xsIGlzIHBpbm5lZCB0byB0aGUgYm90dG9tLiBTZXQgYnkgc2Nyb2xsVG9Cb3R0b20sIHRoZVxuICAgKiBpbml0aWFsIHN0aWNreVNjcm9sbCBhdHRyaWJ1dGUsIGFuZCBieSB0aGUgcmVuZGVyZXIgd2hlbiBwb3NpdGlvbmFsXG4gICAqIGZvbGxvdyBmaXJlcyAoc2Nyb2xsVG9wIGF0IHByZXZNYXgsIGNvbnRlbnQgZ3Jvd3MpLiBDbGVhcmVkIGJ5XG4gICAqIHNjcm9sbFRvL3Njcm9sbEJ5LiBTdGFibGUgc2lnbmFsIGZvciBcImF0IGJvdHRvbVwiIHRoYXQgZG9lc24ndCBkZXBlbmQgb25cbiAgICogbGF5b3V0IHZhbHVlcyAodW5saWtlIHNjcm9sbFRvcCt2aWV3cG9ydEggPj0gc2Nyb2xsSGVpZ2h0KS5cbiAgICovXG4gIGlzU3RpY2t5OiAoKSA9PiBib29sZWFuXG4gIC8qKlxuICAgKiBTdWJzY3JpYmUgdG8gaW1wZXJhdGl2ZSBzY3JvbGwgY2hhbmdlcyAoc2Nyb2xsVG8vc2Nyb2xsQnkvc2Nyb2xsVG9Cb3R0b20pLlxuICAgKiBEb2VzIE5PVCBmaXJlIGZvciBzdGlja3lTY3JvbGwgdXBkYXRlcyBkb25lIGJ5IHRoZSBJbmsgcmVuZGVyZXIg4oCUIHRob3NlXG4gICAqIGhhcHBlbiBkdXJpbmcgSW5rJ3MgcmVuZGVyIHBoYXNlIGFmdGVyIFJlYWN0IGhhcyBjb21taXR0ZWQuIENhbGxlcnMgdGhhdFxuICAgKiBjYXJlIGFib3V0IHRoZSBzdGlja3kgY2FzZSBzaG91bGQgdHJlYXQgXCJhdCBib3R0b21cIiBhcyBhIGZhbGxiYWNrLlxuICAgKi9cbiAgc3Vic2NyaWJlOiAobGlzdGVuZXI6ICgpID0+IHZvaWQpID0+ICgpID0+IHZvaWRcbiAgLyoqXG4gICAqIFNldCB0aGUgcmVuZGVyLXRpbWUgc2Nyb2xsVG9wIGNsYW1wIHRvIHRoZSBjdXJyZW50bHktbW91bnRlZCBjaGlsZHJlbidzXG4gICAqIGNvdmVyYWdlIHNwYW4uIENhbGxlZCBieSB1c2VWaXJ0dWFsU2Nyb2xsIGFmdGVyIGNvbXB1dGluZyBpdHMgcmFuZ2U7XG4gICAqIHJlbmRlci1ub2RlLXRvLW91dHB1dCBjbGFtcHMgc2Nyb2xsVG9wIHRvIFttaW4sIG1heF0gc28gYnVyc3Qgc2Nyb2xsVG9cbiAgICogY2FsbHMgdGhhdCByYWNlIHBhc3QgUmVhY3QncyBhc3luYyByZS1yZW5kZXIgc2hvdyB0aGUgZWRnZSBvZiBtb3VudGVkXG4gICAqIGNvbnRlbnQgaW5zdGVhZCBvZiBibGFuayBzcGFjZXIuIFBhc3MgdW5kZWZpbmVkIHRvIGRpc2FibGUgKHN0aWNreSxcbiAgICogY29sZCBzdGFydCkuXG4gICAqL1xuICBzZXRDbGFtcEJvdW5kczogKG1pbjogbnVtYmVyIHwgdW5kZWZpbmVkLCBtYXg6IG51bWJlciB8IHVuZGVmaW5lZCkgPT4gdm9pZFxufVxuXG5leHBvcnQgdHlwZSBTY3JvbGxCb3hQcm9wcyA9IEV4Y2VwdDxcbiAgU3R5bGVzLFxuICAndGV4dFdyYXAnIHwgJ292ZXJmbG93JyB8ICdvdmVyZmxvd1gnIHwgJ292ZXJmbG93WSdcbj4gJiB7XG4gIHJlZj86IFJlZjxTY3JvbGxCb3hIYW5kbGU+XG4gIC8qKlxuICAgKiBXaGVuIHRydWUsIGF1dG9tYXRpY2FsbHkgcGlucyBzY3JvbGwgcG9zaXRpb24gdG8gdGhlIGJvdHRvbSB3aGVuIGNvbnRlbnRcbiAgICogZ3Jvd3MuIFVuc2V0IG1hbnVhbGx5IHZpYSBzY3JvbGxUby9zY3JvbGxCeSB0byBicmVhayB0aGUgc3RpY2tpbmVzcy5cbiAgICovXG4gIHN0aWNreVNjcm9sbD86IGJvb2xlYW5cbn1cblxuLyoqXG4gKiBBIEJveCB3aXRoIGBvdmVyZmxvdzogc2Nyb2xsYCBhbmQgYW4gaW1wZXJhdGl2ZSBzY3JvbGwgQVBJLlxuICpcbiAqIENoaWxkcmVuIGFyZSBsYWlkIG91dCBhdCB0aGVpciBmdWxsIFlvZ2EtY29tcHV0ZWQgaGVpZ2h0IGluc2lkZSBhXG4gKiBjb25zdHJhaW5lZCBjb250YWluZXIuIEF0IHJlbmRlciB0aW1lLCBvbmx5IGNoaWxkcmVuIGludGVyc2VjdGluZyB0aGVcbiAqIHZpc2libGUgd2luZG93IChzY3JvbGxUb3AuLnNjcm9sbFRvcCtoZWlnaHQpIGFyZSByZW5kZXJlZCAodmlld3BvcnRcbiAqIGN1bGxpbmcpLiBDb250ZW50IGlzIHRyYW5zbGF0ZWQgYnkgLXNjcm9sbFRvcCBhbmQgY2xpcHBlZCB0byB0aGUgYm94IGJvdW5kcy5cbiAqXG4gKiBXb3JrcyBiZXN0IGluc2lkZSBhIGZ1bGxzY3JlZW4gKGNvbnN0cmFpbmVkLWhlaWdodCByb290KSBJbmsgdHJlZS5cbiAqL1xuZnVuY3Rpb24gU2Nyb2xsQm94KHtcbiAgY2hpbGRyZW4sXG4gIHJlZixcbiAgc3RpY2t5U2Nyb2xsLFxuICAuLi5zdHlsZVxufTogUHJvcHNXaXRoQ2hpbGRyZW48U2Nyb2xsQm94UHJvcHM+KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgZG9tUmVmID0gdXNlUmVmPERPTUVsZW1lbnQ+KG51bGwpXG4gIC8vIHNjcm9sbFRvL3Njcm9sbEJ5IGJ5cGFzcyBSZWFjdDogdGhleSBtdXRhdGUgc2Nyb2xsVG9wIG9uIHRoZSBET00gbm9kZSxcbiAgLy8gbWFyayBpdCBkaXJ0eSwgYW5kIGNhbGwgdGhlIHJvb3QncyB0aHJvdHRsZWQgc2NoZWR1bGVSZW5kZXIgZGlyZWN0bHkuXG4gIC8vIFRoZSBJbmsgcmVuZGVyZXIgcmVhZHMgc2Nyb2xsVG9wIGZyb20gdGhlIG5vZGUg4oCUIG5vIFJlYWN0IHN0YXRlIG5lZWRlZCxcbiAgLy8gbm8gcmVjb25jaWxlciBvdmVyaGVhZCBwZXIgd2hlZWwgZXZlbnQuIFRoZSBtaWNyb3Rhc2sgZGVmZXIgY29hbGVzY2VzXG4gIC8vIG11bHRpcGxlIHNjcm9sbEJ5IGNhbGxzIGluIG9uZSBpbnB1dCBiYXRjaCAoZGlzY3JldGVVcGRhdGVzKSBpbnRvIG9uZVxuICAvLyByZW5kZXIg4oCUIG90aGVyd2lzZSBzY2hlZHVsZVJlbmRlcidzIGxlYWRpbmcgZWRnZSBmaXJlcyBvbiB0aGUgRklSU1RcbiAgLy8gZXZlbnQgYmVmb3JlIHN1YnNlcXVlbnQgZXZlbnRzIG11dGF0ZSBzY3JvbGxUb3AuIHNjcm9sbFRvQm90dG9tIHN0aWxsXG4gIC8vIGZvcmNlcyBhIFJlYWN0IHJlbmRlcjogc3RpY2t5IGlzIGF0dHJpYnV0ZS1vYnNlcnZlZCwgbm8gRE9NLW9ubHkgcGF0aC5cbiAgY29uc3QgWywgZm9yY2VSZW5kZXJdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgbGlzdGVuZXJzUmVmID0gdXNlUmVmKG5ldyBTZXQ8KCkgPT4gdm9pZD4oKSlcbiAgY29uc3QgcmVuZGVyUXVldWVkUmVmID0gdXNlUmVmKGZhbHNlKVxuXG4gIGNvbnN0IG5vdGlmeSA9ICgpID0+IHtcbiAgICBmb3IgKGNvbnN0IGwgb2YgbGlzdGVuZXJzUmVmLmN1cnJlbnQpIGwoKVxuICB9XG5cbiAgZnVuY3Rpb24gc2Nyb2xsTXV0YXRlZChlbDogRE9NRWxlbWVudCk6IHZvaWQge1xuICAgIC8vIFNpZ25hbCBiYWNrZ3JvdW5kIGludGVydmFscyAoSURFIHBvbGwsIExTUCBwb2xsLCBHQ1MgZmV0Y2gsIG9ycGhhblxuICAgIC8vIGNoZWNrKSB0byBza2lwIHRoZWlyIG5leHQgdGljayDigJQgdGhleSBjb21wZXRlIGZvciB0aGUgZXZlbnQgbG9vcCBhbmRcbiAgICAvLyBjb250cmlidXRlZCB0byAxNDAybXMgbWF4IGZyYW1lIGdhcHMgZHVyaW5nIHNjcm9sbCBkcmFpbi5cbiAgICBtYXJrU2Nyb2xsQWN0aXZpdHkoKVxuICAgIG1hcmtEaXJ0eShlbClcbiAgICBtYXJrQ29tbWl0U3RhcnQoKVxuICAgIG5vdGlmeSgpXG4gICAgaWYgKHJlbmRlclF1ZXVlZFJlZi5jdXJyZW50KSByZXR1cm5cbiAgICByZW5kZXJRdWV1ZWRSZWYuY3VycmVudCA9IHRydWVcbiAgICBxdWV1ZU1pY3JvdGFzaygoKSA9PiB7XG4gICAgICByZW5kZXJRdWV1ZWRSZWYuY3VycmVudCA9IGZhbHNlXG4gICAgICBzY2hlZHVsZVJlbmRlckZyb20oZWwpXG4gICAgfSlcbiAgfVxuXG4gIHVzZUltcGVyYXRpdmVIYW5kbGUoXG4gICAgcmVmLFxuICAgICgpOiBTY3JvbGxCb3hIYW5kbGUgPT4gKHtcbiAgICAgIHNjcm9sbFRvKHk6IG51bWJlcikge1xuICAgICAgICBjb25zdCBlbCA9IGRvbVJlZi5jdXJyZW50XG4gICAgICAgIGlmICghZWwpIHJldHVyblxuICAgICAgICAvLyBFeHBsaWNpdCBmYWxzZSBvdmVycmlkZXMgdGhlIERPTSBhdHRyaWJ1dGUgc28gbWFudWFsIHNjcm9sbFxuICAgICAgICAvLyBicmVha3Mgc3RpY2tpbmVzcy4gUmVuZGVyIGNvZGUgY2hlY2tzID8/IHByZWNlZGVuY2UuXG4gICAgICAgIGVsLnN0aWNreVNjcm9sbCA9IGZhbHNlXG4gICAgICAgIGVsLnBlbmRpbmdTY3JvbGxEZWx0YSA9IHVuZGVmaW5lZFxuICAgICAgICBlbC5zY3JvbGxBbmNob3IgPSB1bmRlZmluZWRcbiAgICAgICAgZWwuc2Nyb2xsVG9wID0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcih5KSlcbiAgICAgICAgc2Nyb2xsTXV0YXRlZChlbClcbiAgICAgIH0sXG4gICAgICBzY3JvbGxUb0VsZW1lbnQoZWw6IERPTUVsZW1lbnQsIG9mZnNldCA9IDApIHtcbiAgICAgICAgY29uc3QgYm94ID0gZG9tUmVmLmN1cnJlbnRcbiAgICAgICAgaWYgKCFib3gpIHJldHVyblxuICAgICAgICBib3guc3RpY2t5U2Nyb2xsID0gZmFsc2VcbiAgICAgICAgYm94LnBlbmRpbmdTY3JvbGxEZWx0YSA9IHVuZGVmaW5lZFxuICAgICAgICBib3guc2Nyb2xsQW5jaG9yID0geyBlbCwgb2Zmc2V0IH1cbiAgICAgICAgc2Nyb2xsTXV0YXRlZChib3gpXG4gICAgICB9LFxuICAgICAgc2Nyb2xsQnkoZHk6IG51bWJlcikge1xuICAgICAgICBjb25zdCBlbCA9IGRvbVJlZi5jdXJyZW50XG4gICAgICAgIGlmICghZWwpIHJldHVyblxuICAgICAgICBlbC5zdGlja3lTY3JvbGwgPSBmYWxzZVxuICAgICAgICAvLyBXaGVlbCBpbnB1dCBjYW5jZWxzIGFueSBpbi1mbGlnaHQgYW5jaG9yIHNlZWsg4oCUIHVzZXIgb3ZlcnJpZGUuXG4gICAgICAgIGVsLnNjcm9sbEFuY2hvciA9IHVuZGVmaW5lZFxuICAgICAgICAvLyBBY2N1bXVsYXRlIGluIHBlbmRpbmdTY3JvbGxEZWx0YTsgcmVuZGVyZXIgZHJhaW5zIGl0IGF0IGEgY2FwcGVkXG4gICAgICAgIC8vIHJhdGUgc28gZmFzdCBmbGlja3Mgc2hvdyBpbnRlcm1lZGlhdGUgZnJhbWVzLiBQdXJlIGFjY3VtdWxhdG9yOlxuICAgICAgICAvLyBzY3JvbGwtdXAgZm9sbG93ZWQgYnkgc2Nyb2xsLWRvd24gbmF0dXJhbGx5IGNhbmNlbHMuXG4gICAgICAgIGVsLnBlbmRpbmdTY3JvbGxEZWx0YSA9IChlbC5wZW5kaW5nU2Nyb2xsRGVsdGEgPz8gMCkgKyBNYXRoLmZsb29yKGR5KVxuICAgICAgICBzY3JvbGxNdXRhdGVkKGVsKVxuICAgICAgfSxcbiAgICAgIHNjcm9sbFRvQm90dG9tKCkge1xuICAgICAgICBjb25zdCBlbCA9IGRvbVJlZi5jdXJyZW50XG4gICAgICAgIGlmICghZWwpIHJldHVyblxuICAgICAgICBlbC5wZW5kaW5nU2Nyb2xsRGVsdGEgPSB1bmRlZmluZWRcbiAgICAgICAgZWwuc3RpY2t5U2Nyb2xsID0gdHJ1ZVxuICAgICAgICBtYXJrRGlydHkoZWwpXG4gICAgICAgIG5vdGlmeSgpXG4gICAgICAgIGZvcmNlUmVuZGVyKG4gPT4gbiArIDEpXG4gICAgICB9LFxuICAgICAgZ2V0U2Nyb2xsVG9wKCkge1xuICAgICAgICByZXR1cm4gZG9tUmVmLmN1cnJlbnQ/LnNjcm9sbFRvcCA/PyAwXG4gICAgICB9LFxuICAgICAgZ2V0UGVuZGluZ0RlbHRhKCkge1xuICAgICAgICAvLyBBY2N1bXVsYXRlZC1idXQtbm90LXlldC1kcmFpbmVkIGRlbHRhLiB1c2VWaXJ0dWFsU2Nyb2xsIG5lZWRzXG4gICAgICAgIC8vIHRoaXMgdG8gbW91bnQgdGhlIHVuaW9uIFtjb21taXR0ZWQsIGNvbW1pdHRlZCtwZW5kaW5nXSByYW5nZSDigJRcbiAgICAgICAgLy8gb3RoZXJ3aXNlIGludGVybWVkaWF0ZSBkcmFpbiBmcmFtZXMgZmluZCBubyBjaGlsZHJlbiAoYmxhbmspLlxuICAgICAgICByZXR1cm4gZG9tUmVmLmN1cnJlbnQ/LnBlbmRpbmdTY3JvbGxEZWx0YSA/PyAwXG4gICAgICB9LFxuICAgICAgZ2V0U2Nyb2xsSGVpZ2h0KCkge1xuICAgICAgICByZXR1cm4gZG9tUmVmLmN1cnJlbnQ/LnNjcm9sbEhlaWdodCA/PyAwXG4gICAgICB9LFxuICAgICAgZ2V0RnJlc2hTY3JvbGxIZWlnaHQoKSB7XG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBkb21SZWYuY3VycmVudD8uY2hpbGROb2Rlc1swXSBhcyBET01FbGVtZW50IHwgdW5kZWZpbmVkXG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgY29udGVudD8ueW9nYU5vZGU/LmdldENvbXB1dGVkSGVpZ2h0KCkgPz9cbiAgICAgICAgICBkb21SZWYuY3VycmVudD8uc2Nyb2xsSGVpZ2h0ID8/XG4gICAgICAgICAgMFxuICAgICAgICApXG4gICAgICB9LFxuICAgICAgZ2V0Vmlld3BvcnRIZWlnaHQoKSB7XG4gICAgICAgIHJldHVybiBkb21SZWYuY3VycmVudD8uc2Nyb2xsVmlld3BvcnRIZWlnaHQgPz8gMFxuICAgICAgfSxcbiAgICAgIGdldFZpZXdwb3J0VG9wKCkge1xuICAgICAgICByZXR1cm4gZG9tUmVmLmN1cnJlbnQ/LnNjcm9sbFZpZXdwb3J0VG9wID8/IDBcbiAgICAgIH0sXG4gICAgICBpc1N0aWNreSgpIHtcbiAgICAgICAgY29uc3QgZWwgPSBkb21SZWYuY3VycmVudFxuICAgICAgICBpZiAoIWVsKSByZXR1cm4gZmFsc2VcbiAgICAgICAgcmV0dXJuIGVsLnN0aWNreVNjcm9sbCA/PyBCb29sZWFuKGVsLmF0dHJpYnV0ZXNbJ3N0aWNreVNjcm9sbCddKVxuICAgICAgfSxcbiAgICAgIHN1YnNjcmliZShsaXN0ZW5lcjogKCkgPT4gdm9pZCkge1xuICAgICAgICBsaXN0ZW5lcnNSZWYuY3VycmVudC5hZGQobGlzdGVuZXIpXG4gICAgICAgIHJldHVybiAoKSA9PiBsaXN0ZW5lcnNSZWYuY3VycmVudC5kZWxldGUobGlzdGVuZXIpXG4gICAgICB9LFxuICAgICAgc2V0Q2xhbXBCb3VuZHMobWluLCBtYXgpIHtcbiAgICAgICAgY29uc3QgZWwgPSBkb21SZWYuY3VycmVudFxuICAgICAgICBpZiAoIWVsKSByZXR1cm5cbiAgICAgICAgZWwuc2Nyb2xsQ2xhbXBNaW4gPSBtaW5cbiAgICAgICAgZWwuc2Nyb2xsQ2xhbXBNYXggPSBtYXhcbiAgICAgIH0sXG4gICAgfSksXG4gICAgLy8gbm90aWZ5L3Njcm9sbE11dGF0ZWQgYXJlIGlubGluZSAobm8gdXNlQ2FsbGJhY2spIGJ1dCBvbmx5IGNsb3NlIG92ZXJcbiAgICAvLyByZWZzICsgaW1wb3J0cyDigJQgc3RhYmxlLiBFbXB0eSBkZXBzIGF2b2lkcyByZWJ1aWxkaW5nIHRoZSBoYW5kbGUgb25cbiAgICAvLyBldmVyeSByZW5kZXIgKHdoaWNoIHJlLXJlZ2lzdGVycyB0aGUgcmVmID0gY2h1cm4pLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZWFjdC1ob29rcy9leGhhdXN0aXZlLWRlcHNcbiAgICBbXSxcbiAgKVxuXG4gIC8vIFN0cnVjdHVyZTogb3V0ZXIgdmlld3BvcnQgKG92ZXJmbG93OnNjcm9sbCwgY29uc3RyYWluZWQgaGVpZ2h0KSA+XG4gIC8vIGlubmVyIGNvbnRlbnQgKGZsZXhHcm93OjEsIGZsZXhTaHJpbms6MCDigJQgZmlsbHMgYXQgbGVhc3QgdGhlIHZpZXdwb3J0XG4gIC8vIGJ1dCBncm93cyBiZXlvbmQgaXQgZm9yIHRhbGwgY29udGVudCkuIGZsZXhHcm93OjEgbGV0cyBjaGlsZHJlbiB1c2VcbiAgLy8gc3BhY2VycyB0byBwaW4gZWxlbWVudHMgdG8gdGhlIGJvdHRvbSBvZiB0aGUgc2Nyb2xsIGFyZWEuIFlvZ2Enc1xuICAvLyBPdmVyZmxvdy5TY3JvbGwgcHJldmVudHMgdGhlIHZpZXdwb3J0IGZyb20gZ3Jvd2luZyB0byBmaXQgdGhlIGNvbnRlbnQuXG4gIC8vIFRoZSByZW5kZXJlciBjb21wdXRlcyBzY3JvbGxIZWlnaHQgZnJvbSB0aGUgY29udGVudCBib3ggYW5kIGN1bGxzXG4gIC8vIGNvbnRlbnQncyBjaGlsZHJlbiBiYXNlZCBvbiBzY3JvbGxUb3AuXG4gIC8vXG4gIC8vIHN0aWNreVNjcm9sbCBpcyBwYXNzZWQgYXMgYSBET00gYXR0cmlidXRlICh2aWEgaW5rLWJveCBkaXJlY3RseSkgc28gaXQnc1xuICAvLyBhdmFpbGFibGUgb24gdGhlIGZpcnN0IHJlbmRlciDigJQgcmVmIGNhbGxiYWNrcyBmaXJlIGFmdGVyIHRoZSBpbml0aWFsXG4gIC8vIGNvbW1pdCwgd2hpY2ggaXMgdG9vIGxhdGUgZm9yIHRoZSBmaXJzdCBmcmFtZS5cbiAgcmV0dXJuIChcbiAgICA8aW5rLWJveFxuICAgICAgcmVmPXtlbCA9PiB7XG4gICAgICAgIGRvbVJlZi5jdXJyZW50ID0gZWxcbiAgICAgICAgaWYgKGVsKSBlbC5zY3JvbGxUb3AgPz89IDBcbiAgICAgIH19XG4gICAgICBzdHlsZT17e1xuICAgICAgICBmbGV4V3JhcDogJ25vd3JhcCcsXG4gICAgICAgIGZsZXhEaXJlY3Rpb246IHN0eWxlLmZsZXhEaXJlY3Rpb24gPz8gJ3JvdycsXG4gICAgICAgIGZsZXhHcm93OiBzdHlsZS5mbGV4R3JvdyA/PyAwLFxuICAgICAgICBmbGV4U2hyaW5rOiBzdHlsZS5mbGV4U2hyaW5rID8/IDEsXG4gICAgICAgIC4uLnN0eWxlLFxuICAgICAgICBvdmVyZmxvd1g6ICdzY3JvbGwnLFxuICAgICAgICBvdmVyZmxvd1k6ICdzY3JvbGwnLFxuICAgICAgfX1cbiAgICAgIHsuLi4oc3RpY2t5U2Nyb2xsID8geyBzdGlja3lTY3JvbGw6IHRydWUgfSA6IHt9KX1cbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBmbGV4R3Jvdz17MX0gZmxleFNocmluaz17MH0gd2lkdGg9XCIxMDAlXCI+XG4gICAgICAgIHtjaGlsZHJlbn1cbiAgICAgIDwvQm94PlxuICAgIDwvaW5rLWJveD5cbiAgKVxufVxuXG5leHBvcnQgZGVmYXVsdCBTY3JvbGxCb3hcbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxJQUNWLEtBQUtDLGlCQUFpQixFQUN0QixLQUFLQyxHQUFHLEVBQ1JDLG1CQUFtQixFQUNuQkMsTUFBTSxFQUNOQyxRQUFRLFFBQ0gsT0FBTztBQUNkLGNBQWNDLE1BQU0sUUFBUSxXQUFXO0FBQ3ZDLFNBQVNDLGtCQUFrQixRQUFRLDBCQUEwQjtBQUM3RCxjQUFjQyxVQUFVLFFBQVEsV0FBVztBQUMzQyxTQUFTQyxTQUFTLEVBQUVDLGtCQUFrQixRQUFRLFdBQVc7QUFDekQsU0FBU0MsZUFBZSxRQUFRLGtCQUFrQjtBQUNsRCxjQUFjQyxNQUFNLFFBQVEsY0FBYztBQUMxQyxPQUFPLGdCQUFnQjtBQUN2QixPQUFPQyxHQUFHLE1BQU0sVUFBVTtBQUUxQixPQUFPLEtBQUtDLGVBQWUsR0FBRztFQUM1QkMsUUFBUSxFQUFFLENBQUNDLENBQUMsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQzdCQyxRQUFRLEVBQUUsQ0FBQ0MsRUFBRSxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUk7RUFDOUI7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7RUFDRUMsZUFBZSxFQUFFLENBQUNDLEVBQUUsRUFBRVosVUFBVSxFQUFFYSxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQzFEQyxjQUFjLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDMUJDLFlBQVksRUFBRSxHQUFHLEdBQUcsTUFBTTtFQUMxQkMsZUFBZSxFQUFFLEdB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 \ No newline at end of file diff --git a/ink/components/Spacer.tsx b/ink/components/Spacer.tsx new file mode 100644 index 0000000..4d0af40 --- /dev/null +++ b/ink/components/Spacer.tsx @@ -0,0 +1,20 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import Box from './Box.js'; + +/** + * A flexible space that expands along the major axis of its containing layout. + * It's useful as a shortcut for filling all the available spaces between elements. + */ +export default function Spacer() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = ; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlNwYWNlciIsIiQiLCJfYyIsInQwIiwiU3ltYm9sIiwiZm9yIl0sInNvdXJjZXMiOlsiU3BhY2VyLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgQm94IGZyb20gJy4vQm94LmpzJ1xuXG4vKipcbiAqIEEgZmxleGlibGUgc3BhY2UgdGhhdCBleHBhbmRzIGFsb25nIHRoZSBtYWpvciBheGlzIG9mIGl0cyBjb250YWluaW5nIGxheW91dC5cbiAqIEl0J3MgdXNlZnVsIGFzIGEgc2hvcnRjdXQgZm9yIGZpbGxpbmcgYWxsIHRoZSBhdmFpbGFibGUgc3BhY2VzIGJldHdlZW4gZWxlbWVudHMuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIFNwYWNlcigpIHtcbiAgcmV0dXJuIDxCb3ggZmxleEdyb3c9ezF9IC8+XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixPQUFPQyxHQUFHLE1BQU0sVUFBVTs7QUFFMUI7QUFDQTtBQUNBO0FBQ0E7QUFDQSxlQUFlLFNBQUFDLE9BQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBRyxNQUFBLENBQUFDLEdBQUE7SUFDTkYsRUFBQSxJQUFDLEdBQUcsQ0FBVyxRQUFDLENBQUQsR0FBQyxHQUFJO0lBQUFGLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsT0FBcEJFLEVBQW9CO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/ink/components/StdinContext.ts b/ink/components/StdinContext.ts new file mode 100644 index 0000000..0b1a497 --- /dev/null +++ b/ink/components/StdinContext.ts @@ -0,0 +1,49 @@ +import { createContext } from 'react' +import { EventEmitter } from '../events/emitter.js' +import type { TerminalQuerier } from '../terminal-querier.js' + +export type Props = { + /** + * Stdin stream passed to `render()` in `options.stdin` or `process.stdin` by default. Useful if your app needs to handle user input. + */ + readonly stdin: NodeJS.ReadStream + + /** + * Ink exposes this function via own `` to be able to handle Ctrl+C, that's why you should use Ink's `setRawMode` instead of `process.stdin.setRawMode`. + * If the `stdin` stream passed to Ink does not support setRawMode, this function does nothing. + */ + readonly setRawMode: (value: boolean) => void + + /** + * A boolean flag determining if the current `stdin` supports `setRawMode`. A component using `setRawMode` might want to use `isRawModeSupported` to nicely fall back in environments where raw mode is not supported. + */ + readonly isRawModeSupported: boolean + + readonly internal_exitOnCtrlC: boolean + + readonly internal_eventEmitter: EventEmitter + + /** Query the terminal and await responses (DECRQM, OSC 11, etc.). + * Null only in the never-reached default context value. */ + readonly internal_querier: TerminalQuerier | null +} + +/** + * `StdinContext` is a React context, which exposes input stream. + */ + +const StdinContext = createContext({ + stdin: process.stdin, + + internal_eventEmitter: new EventEmitter(), + setRawMode() {}, + isRawModeSupported: false, + + internal_exitOnCtrlC: true, + internal_querier: null, +}) + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +StdinContext.displayName = 'InternalStdinContext' + +export default StdinContext diff --git a/ink/components/TerminalFocusContext.tsx b/ink/components/TerminalFocusContext.tsx new file mode 100644 index 0000000..e017b64 --- /dev/null +++ b/ink/components/TerminalFocusContext.tsx @@ -0,0 +1,52 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { createContext, useMemo, useSyncExternalStore } from 'react'; +import { getTerminalFocused, getTerminalFocusState, subscribeTerminalFocus, type TerminalFocusState } from '../terminal-focus-state.js'; +export type { TerminalFocusState }; +export type TerminalFocusContextProps = { + readonly isTerminalFocused: boolean; + readonly terminalFocusState: TerminalFocusState; +}; +const TerminalFocusContext = createContext({ + isTerminalFocused: true, + terminalFocusState: 'unknown' +}); + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +TerminalFocusContext.displayName = 'TerminalFocusContext'; + +// Separate component so App.tsx doesn't re-render on focus changes. +// Children are a stable prop reference, so they don't re-render either — +// only components that consume the context will re-render. +export function TerminalFocusProvider(t0) { + const $ = _c(6); + const { + children + } = t0; + const isTerminalFocused = useSyncExternalStore(subscribeTerminalFocus, getTerminalFocused); + const terminalFocusState = useSyncExternalStore(subscribeTerminalFocus, getTerminalFocusState); + let t1; + if ($[0] !== isTerminalFocused || $[1] !== terminalFocusState) { + t1 = { + isTerminalFocused, + terminalFocusState + }; + $[0] = isTerminalFocused; + $[1] = terminalFocusState; + $[2] = t1; + } else { + t1 = $[2]; + } + const value = t1; + let t2; + if ($[3] !== children || $[4] !== value) { + t2 = {children}; + $[3] = children; + $[4] = value; + $[5] = t2; + } else { + t2 = $[5]; + } + return t2; +} +export default TerminalFocusContext; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImNyZWF0ZUNvbnRleHQiLCJ1c2VNZW1vIiwidXNlU3luY0V4dGVybmFsU3RvcmUiLCJnZXRUZXJtaW5hbEZvY3VzZWQiLCJnZXRUZXJtaW5hbEZvY3VzU3RhdGUiLCJzdWJzY3JpYmVUZXJtaW5hbEZvY3VzIiwiVGVybWluYWxGb2N1c1N0YXRlIiwiVGVybWluYWxGb2N1c0NvbnRleHRQcm9wcyIsImlzVGVybWluYWxGb2N1c2VkIiwidGVybWluYWxGb2N1c1N0YXRlIiwiVGVybWluYWxGb2N1c0NvbnRleHQiLCJkaXNwbGF5TmFtZSIsIlRlcm1pbmFsRm9jdXNQcm92aWRlciIsInQwIiwiJCIsIl9jIiwiY2hpbGRyZW4iLCJ0MSIsInZhbHVlIiwidDIiXSwic291cmNlcyI6WyJUZXJtaW5hbEZvY3VzQ29udGV4dC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IGNyZWF0ZUNvbnRleHQsIHVzZU1lbW8sIHVzZVN5bmNFeHRlcm5hbFN0b3JlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICBnZXRUZXJtaW5hbEZvY3VzZWQsXG4gIGdldFRlcm1pbmFsRm9jdXNTdGF0ZSxcbiAgc3Vic2NyaWJlVGVybWluYWxGb2N1cyxcbiAgdHlwZSBUZXJtaW5hbEZvY3VzU3RhdGUsXG59IGZyb20gJy4uL3Rlcm1pbmFsLWZvY3VzLXN0YXRlLmpzJ1xuXG5leHBvcnQgdHlwZSB7IFRlcm1pbmFsRm9jdXNTdGF0ZSB9XG5cbmV4cG9ydCB0eXBlIFRlcm1pbmFsRm9jdXNDb250ZXh0UHJvcHMgPSB7XG4gIHJlYWRvbmx5IGlzVGVybWluYWxGb2N1c2VkOiBib29sZWFuXG4gIHJlYWRvbmx5IHRlcm1pbmFsRm9jdXNTdGF0ZTogVGVybWluYWxGb2N1c1N0YXRlXG59XG5cbmNvbnN0IFRlcm1pbmFsRm9jdXNDb250ZXh0ID0gY3JlYXRlQ29udGV4dDxUZXJtaW5hbEZvY3VzQ29udGV4dFByb3BzPih7XG4gIGlzVGVybWluYWxGb2N1c2VkOiB0cnVlLFxuICB0ZXJtaW5hbEZvY3VzU3RhdGU6ICd1bmtub3duJyxcbn0pXG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tdG9wLWxldmVsLXNpZGUtZWZmZWN0c1xuVGVybWluYWxGb2N1c0NvbnRleHQuZGlzcGxheU5hbWUgPSAnVGVybWluYWxGb2N1c0NvbnRleHQnXG5cbi8vIFNlcGFyYXRlIGNvbXBvbmVudCBzbyBBcHAudHN4IGRvZXNuJ3QgcmUtcmVuZGVyIG9uIGZvY3VzIGNoYW5nZXMuXG4vLyBDaGlsZHJlbiBhcmUgYSBzdGFibGUgcHJvcCByZWZlcmVuY2UsIHNvIHRoZXkgZG9uJ3QgcmUtcmVuZGVyIGVpdGhlciDigJRcbi8vIG9ubHkgY29tcG9uZW50cyB0aGF0IGNvbnN1bWUgdGhlIGNvbnRleHQgd2lsbCByZS1yZW5kZXIuXG5leHBvcnQgZnVuY3Rpb24gVGVybWluYWxGb2N1c1Byb3ZpZGVyKHtcbiAgY2hpbGRyZW4sXG59OiB7XG4gIGNoaWxkcmVuOiBSZWFjdC5SZWFjdE5vZGVcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBpc1Rlcm1pbmFsRm9jdXNlZCA9IHVzZVN5bmNFeHRlcm5hbFN0b3JlKFxuICAgIHN1YnNjcmliZVRlcm1pbmFsRm9jdXMsXG4gICAgZ2V0VGVybWluYWxGb2N1c2VkLFxuICApXG4gIGNvbnN0IHRlcm1pbmFsRm9jdXNTdGF0ZSA9IHVzZVN5bmNFeHRlcm5hbFN0b3JlKFxuICAgIHN1YnNjcmliZVRlcm1pbmFsRm9jdXMsXG4gICAgZ2V0VGVybWluYWxGb2N1c1N0YXRlLFxuICApXG5cbiAgY29uc3QgdmFsdWUgPSB1c2VNZW1vKFxuICAgICgpID0+ICh7IGlzVGVybWluYWxGb2N1c2VkLCB0ZXJtaW5hbEZvY3VzU3RhdGUgfSksXG4gICAgW2lzVGVybWluYWxGb2N1c2VkLCB0ZXJtaW5hbEZvY3VzU3RhdGVdLFxuICApXG5cbiAgcmV0dXJuIChcbiAgICA8VGVybWluYWxGb2N1c0NvbnRleHQuUHJvdmlkZXIgdmFsdWU9e3ZhbHVlfT5cbiAgICAgIHtjaGlsZHJlbn1cbiAgICA8L1Rlcm1pbmFsRm9jdXNDb250ZXh0LlByb3ZpZGVyPlxuICApXG59XG5cbmV4cG9ydCBkZWZhdWx0IFRlcm1pbmFsRm9jdXNDb250ZXh0XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUlDLGFBQWEsRUFBRUMsT0FBTyxFQUFFQyxvQkFBb0IsUUFBUSxPQUFPO0FBQzNFLFNBQ0VDLGtCQUFrQixFQUNsQkMscUJBQXFCLEVBQ3JCQyxzQkFBc0IsRUFDdEIsS0FBS0Msa0JBQWtCLFFBQ2xCLDRCQUE0QjtBQUVuQyxjQUFjQSxrQkFBa0I7QUFFaEMsT0FBTyxLQUFLQyx5QkFBeUIsR0FBRztFQUN0QyxTQUFTQyxpQkFBaUIsRUFBRSxPQUFPO0VBQ25DLFNBQVNDLGtCQUFrQixFQUFFSCxrQkFBa0I7QUFDakQsQ0FBQztBQUVELE1BQU1JLG9CQUFvQixHQUFHVixhQUFhLENBQUNPLHlCQUF5QixDQUFDLENBQUM7RUFDcEVDLGlCQUFpQixFQUFFLElBQUk7RUFDdkJDLGtCQUFrQixFQUFFO0FBQ3RCLENBQUMsQ0FBQzs7QUFFRjtBQUNBQyxvQkFBb0IsQ0FBQ0MsV0FBVyxHQUFHLHNCQUFzQjs7QUFFekQ7QUFDQTtBQUNBO0FBQ0EsT0FBTyxTQUFBQyxzQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUErQjtJQUFBQztFQUFBLElBQUFILEVBSXJDO0VBQ0MsTUFBQUwsaUJBQUEsR0FBMEJOLG9CQUFvQixDQUM1Q0csc0JBQXNCLEVBQ3RCRixrQkFDRixDQUFDO0VBQ0QsTUFBQU0sa0JBQUEsR0FBMkJQLG9CQUFvQixDQUM3Q0csc0JBQXNCLEVBQ3RCRCxxQkFDRixDQUFDO0VBQUEsSUFBQWEsRUFBQTtFQUFBLElBQUFILENBQUEsUUFBQU4saUJBQUEsSUFBQU0sQ0FBQSxRQUFBTCxrQkFBQTtJQUdRUSxFQUFBO01BQUFULGlCQUFBO01BQUFDO0lBQXdDLENBQUM7SUFBQUssQ0FBQSxNQUFBTixpQkFBQTtJQUFBTSxDQUFBLE1BQUFMLGtCQUFBO0lBQUFLLENBQUEsTUFBQUcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBRGxELE1BQUFJLEtBQUEsR0FDU0QsRUFBeUM7RUFFakQsSUFBQUUsRUFBQTtFQUFBLElBQUFMLENBQUEsUUFBQUUsUUFBQSxJQUFBRixDQUFBLFFBQUFJLEtBQUE7SUFHQ0MsRUFBQSxrQ0FBc0NELEtBQUssQ0FBTEEsTUFBSSxDQUFDLENBQ3hDRixTQUFPLENBQ1YsZ0NBQWdDO0lBQUFGLENBQUEsTUFBQUUsUUFBQTtJQUFBRixDQUFBLE1BQUFJLEtBQUE7SUFBQUosQ0FBQSxNQUFBSyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBTCxDQUFBO0VBQUE7RUFBQSxPQUZoQ0ssRUFFZ0M7QUFBQTtBQUlwQyxlQUFlVCxvQkFBb0IiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/ink/components/TerminalSizeContext.tsx b/ink/components/TerminalSizeContext.tsx new file mode 100644 index 0000000..8ca447e --- /dev/null +++ b/ink/components/TerminalSizeContext.tsx @@ -0,0 +1,7 @@ +import { createContext } from 'react'; +export type TerminalSize = { + columns: number; + rows: number; +}; +export const TerminalSizeContext = createContext(null); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjcmVhdGVDb250ZXh0IiwiVGVybWluYWxTaXplIiwiY29sdW1ucyIsInJvd3MiLCJUZXJtaW5hbFNpemVDb250ZXh0Il0sInNvdXJjZXMiOlsiVGVybWluYWxTaXplQ29udGV4dC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRlQ29udGV4dCB9IGZyb20gJ3JlYWN0J1xuXG5leHBvcnQgdHlwZSBUZXJtaW5hbFNpemUgPSB7XG4gIGNvbHVtbnM6IG51bWJlclxuICByb3dzOiBudW1iZXJcbn1cblxuZXhwb3J0IGNvbnN0IFRlcm1pbmFsU2l6ZUNvbnRleHQgPSBjcmVhdGVDb250ZXh0PFRlcm1pbmFsU2l6ZSB8IG51bGw+KG51bGwpXG4iXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLGFBQWEsUUFBUSxPQUFPO0FBRXJDLE9BQU8sS0FBS0MsWUFBWSxHQUFHO0VBQ3pCQyxPQUFPLEVBQUUsTUFBTTtFQUNmQyxJQUFJLEVBQUUsTUFBTTtBQUNkLENBQUM7QUFFRCxPQUFPLE1BQU1DLG1CQUFtQixHQUFHSixhQUFhLENBQUNDLFlBQVksR0FBRyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/ink/components/Text.tsx b/ink/components/Text.tsx new file mode 100644 index 0000000..b53d834 --- /dev/null +++ b/ink/components/Text.tsx @@ -0,0 +1,254 @@ +import { c as _c } from "react/compiler-runtime"; +import type { ReactNode } from 'react'; +import React from 'react'; +import type { Color, Styles, TextStyles } from '../styles.js'; +type BaseProps = { + /** + * Change text color. Accepts a raw color value (rgb, hex, ansi). + */ + readonly color?: Color; + + /** + * Same as `color`, but for background. + */ + readonly backgroundColor?: Color; + + /** + * Make the text italic. + */ + readonly italic?: boolean; + + /** + * Make the text underlined. + */ + readonly underline?: boolean; + + /** + * Make the text crossed with a line. + */ + readonly strikethrough?: boolean; + + /** + * Inverse background and foreground colors. + */ + readonly inverse?: boolean; + + /** + * This property tells Ink to wrap or truncate text if its width is larger than container. + * If `wrap` is passed (by default), Ink will wrap text and split it into multiple lines. + * If `truncate-*` is passed, Ink will truncate text instead, which will result in one line of text with the rest cut off. + */ + readonly wrap?: Styles['textWrap']; + readonly children?: ReactNode; +}; + +/** + * Bold and dim are mutually exclusive in terminals. + * This type ensures you can use one or the other, but not both. + */ +type WeightProps = { + bold?: never; + dim?: never; +} | { + bold: boolean; + dim?: never; +} | { + dim: boolean; + bold?: never; +}; +export type Props = BaseProps & WeightProps; +const memoizedStylesForWrap: Record, Styles> = { + wrap: { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + textWrap: 'wrap' + }, + 'wrap-trim': { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + textWrap: 'wrap-trim' + }, + end: { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + textWrap: 'end' + }, + middle: { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + textWrap: 'middle' + }, + 'truncate-end': { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + textWrap: 'truncate-end' + }, + truncate: { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + textWrap: 'truncate' + }, + 'truncate-middle': { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + textWrap: 'truncate-middle' + }, + 'truncate-start': { + flexGrow: 0, + flexShrink: 1, + flexDirection: 'row', + textWrap: 'truncate-start' + } +} as const; + +/** + * This component can display text, and change its style to make it colorful, bold, underline, italic or strikethrough. + */ +export default function Text(t0) { + const $ = _c(29); + const { + color, + backgroundColor, + bold, + dim, + italic: t1, + underline: t2, + strikethrough: t3, + inverse: t4, + wrap: t5, + children + } = t0; + const italic = t1 === undefined ? false : t1; + const underline = t2 === undefined ? false : t2; + const strikethrough = t3 === undefined ? false : t3; + const inverse = t4 === undefined ? false : t4; + const wrap = t5 === undefined ? "wrap" : t5; + if (children === undefined || children === null) { + return null; + } + let t6; + if ($[0] !== color) { + t6 = color && { + color + }; + $[0] = color; + $[1] = t6; + } else { + t6 = $[1]; + } + let t7; + if ($[2] !== backgroundColor) { + t7 = backgroundColor && { + backgroundColor + }; + $[2] = backgroundColor; + $[3] = t7; + } else { + t7 = $[3]; + } + let t8; + if ($[4] !== dim) { + t8 = dim && { + dim + }; + $[4] = dim; + $[5] = t8; + } else { + t8 = $[5]; + } + let t9; + if ($[6] !== bold) { + t9 = bold && { + bold + }; + $[6] = bold; + $[7] = t9; + } else { + t9 = $[7]; + } + let t10; + if ($[8] !== italic) { + t10 = italic && { + italic + }; + $[8] = italic; + $[9] = t10; + } else { + t10 = $[9]; + } + let t11; + if ($[10] !== underline) { + t11 = underline && { + underline + }; + $[10] = underline; + $[11] = t11; + } else { + t11 = $[11]; + } + let t12; + if ($[12] !== strikethrough) { + t12 = strikethrough && { + strikethrough + }; + $[12] = strikethrough; + $[13] = t12; + } else { + t12 = $[13]; + } + let t13; + if ($[14] !== inverse) { + t13 = inverse && { + inverse + }; + $[14] = inverse; + $[15] = t13; + } else { + t13 = $[15]; + } + let t14; + if ($[16] !== t10 || $[17] !== t11 || $[18] !== t12 || $[19] !== t13 || $[20] !== t6 || $[21] !== t7 || $[22] !== t8 || $[23] !== t9) { + t14 = { + ...t6, + ...t7, + ...t8, + ...t9, + ...t10, + ...t11, + ...t12, + ...t13 + }; + $[16] = t10; + $[17] = t11; + $[18] = t12; + $[19] = t13; + $[20] = t6; + $[21] = t7; + $[22] = t8; + $[23] = t9; + $[24] = t14; + } else { + t14 = $[24]; + } + const textStyles = t14; + const t15 = memoizedStylesForWrap[wrap]; + let t16; + if ($[25] !== children || $[26] !== t15 || $[27] !== textStyles) { + t16 = {children}; + $[25] = children; + $[26] = t15; + $[27] = textStyles; + $[28] = t16; + } else { + t16 = $[28]; + } + return t16; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdE5vZGUiLCJSZWFjdCIsIkNvbG9yIiwiU3R5bGVzIiwiVGV4dFN0eWxlcyIsIkJhc2VQcm9wcyIsImNvbG9yIiwiYmFja2dyb3VuZENvbG9yIiwiaXRhbGljIiwidW5kZXJsaW5lIiwic3RyaWtldGhyb3VnaCIsImludmVyc2UiLCJ3cmFwIiwiY2hpbGRyZW4iLCJXZWlnaHRQcm9wcyIsImJvbGQiLCJkaW0iLCJQcm9wcyIsIm1lbW9pemVkU3R5bGVzRm9yV3JhcCIsIlJlY29yZCIsIk5vbk51bGxhYmxlIiwiZmxleEdyb3ciLCJmbGV4U2hyaW5rIiwiZmxleERpcmVjdGlvbiIsInRleHRXcmFwIiwiZW5kIiwibWlkZGxlIiwidHJ1bmNhdGUiLCJjb25zdCIsIlRleHQiLCJ0MCIsIiQiLCJfYyIsInQxIiwidDIiLCJ0MyIsInQ0IiwidDUiLCJ1bmRlZmluZWQiLCJ0NiIsInQ3IiwidDgiLCJ0OSIsInQxMCIsInQxMSIsInQxMiIsInQxMyIsInQxNCIsInRleHRTdHlsZXMiLCJ0MTUiLCJ0MTYiXSwic291cmNlcyI6WyJUZXh0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IFJlYWN0Tm9kZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBDb2xvciwgU3R5bGVzLCBUZXh0U3R5bGVzIH0gZnJvbSAnLi4vc3R5bGVzLmpzJ1xuXG50eXBlIEJhc2VQcm9wcyA9IHtcbiAgLyoqXG4gICAqIENoYW5nZSB0ZXh0IGNvbG9yLiBBY2NlcHRzIGEgcmF3IGNvbG9yIHZhbHVlIChyZ2IsIGhleCwgYW5zaSkuXG4gICAqL1xuICByZWFkb25seSBjb2xvcj86IENvbG9yXG5cbiAgLyoqXG4gICAqIFNhbWUgYXMgYGNvbG9yYCwgYnV0IGZvciBiYWNrZ3JvdW5kLlxuICAgKi9cbiAgcmVhZG9ubHkgYmFja2dyb3VuZENvbG9yPzogQ29sb3JcblxuICAvKipcbiAgICogTWFrZSB0aGUgdGV4dCBpdGFsaWMuXG4gICAqL1xuICByZWFkb25seSBpdGFsaWM/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIE1ha2UgdGhlIHRleHQgdW5kZXJsaW5lZC5cbiAgICovXG4gIHJlYWRvbmx5IHVuZGVybGluZT86IGJvb2xlYW5cblxuICAvKipcbiAgICogTWFrZSB0aGUgdGV4dCBjcm9zc2VkIHdpdGggYSBsaW5lLlxuICAgKi9cbiAgcmVhZG9ubHkgc3RyaWtldGhyb3VnaD86IGJvb2xlYW5cblxuICAvKipcbiAgICogSW52ZXJzZSBiYWNrZ3JvdW5kIGFuZCBmb3JlZ3JvdW5kIGNvbG9ycy5cbiAgICovXG4gIHJlYWRvbmx5IGludmVyc2U/OiBib29sZWFuXG5cbiAgLyoqXG4gICAqIFRoaXMgcHJvcGVydHkgdGVsbHMgSW5rIHRvIHdyYXAgb3IgdHJ1bmNhdGUgdGV4dCBpZiBpdHMgd2lkdGggaXMgbGFyZ2VyIHRoYW4gY29udGFpbmVyLlxuICAgKiBJZiBgd3JhcGAgaXMgcGFzc2VkIChieSBkZWZhdWx0KSwgSW5rIHdpbGwgd3JhcCB0ZXh0IGFuZCBzcGxpdCBpdCBpbnRvIG11bHRpcGxlIGxpbmVzLlxuICAgKiBJZiBgdHJ1bmNhdGUtKmAgaXMgcGFzc2VkLCBJbmsgd2lsbCB0cnVuY2F0ZSB0ZXh0IGluc3RlYWQsIHdoaWNoIHdpbGwgcmVzdWx0IGluIG9uZSBsaW5lIG9mIHRleHQgd2l0aCB0aGUgcmVzdCBjdXQgb2ZmLlxuICAgKi9cbiAgcmVhZG9ubHkgd3JhcD86IFN0eWxlc1sndGV4dFdyYXAnXVxuXG4gIHJlYWRvbmx5IGNoaWxkcmVuPzogUmVhY3ROb2RlXG59XG5cbi8qKlxuICogQm9sZCBhbmQgZGltIGFyZSBtdXR1YWxseSBleGNsdXNpdmUgaW4gdGVybWluYWxzLlxuICogVGhpcyB0eXBlIGVuc3VyZXMgeW91IGNhbiB1c2Ugb25lIG9yIHRoZSBvdGhlciwgYnV0IG5vdCBib3RoLlxuICovXG50eXBlIFdlaWdodFByb3BzID1cbiAgfCB7IGJvbGQ/OiBuZXZlcjsgZGltPzogbmV2ZXIgfVxuICB8IHsgYm9sZDogYm9vbGVhbjsgZGltPzogbmV2ZXIgfVxuICB8IHsgZGltOiBib29sZWFuOyBib2xkPzogbmV2ZXIgfVxuXG5leHBvcnQgdHlwZSBQcm9wcyA9IEJhc2VQcm9wcyAmIFdlaWdodFByb3BzXG5cbmNvbnN0IG1lbW9pemVkU3R5bGVzRm9yV3JhcDogUmVjb3JkPE5vbk51bGxhYmxlPFN0eWxlc1sndGV4dFdyYXAnXT4sIFN0eWxlcz4gPSB7XG4gIHdyYXA6IHtcbiAgICBmbGV4R3JvdzogMCxcbiAgICBmbGV4U2hyaW5rOiAxLFxuICAgIGZsZXhEaXJlY3Rpb246ICdyb3cnLFxuICAgIHRleHRXcmFwOiAnd3JhcCcsXG4gIH0sXG4gICd3cmFwLXRyaW0nOiB7XG4gICAgZmxleEdyb3c6IDAsXG4gICAgZmxleFNocmluazogMSxcbiAgICBmbGV4RGlyZWN0aW9uOiAncm93JyxcbiAgICB0ZXh0V3JhcDogJ3dyYXAtdHJpbScsXG4gIH0sXG4gIGVuZDoge1xuICAgIGZsZXhHcm93OiAwLFxuICAgIGZsZXhTaHJpbms6IDEsXG4gICAgZmxleERpcmVjdGlvbjogJ3JvdycsXG4gICAgdGV4dFdyYXA6ICdlbmQnLFxuICB9LFxuICBtaWRkbGU6IHtcbiAgICBmbGV4R3JvdzogMCxcbiAgICBmbGV4U2hyaW5rOiAxLFxuICAgIGZsZXhEaXJlY3Rpb246ICdyb3cnLFxuICAgIHRleHRXcmFwOiAnbWlkZGxlJyxcbiAgfSxcbiAgJ3RydW5jYXRlLWVuZCc6IHtcbiAgICBmbGV4R3JvdzogMCxcbiAgICBmbGV4U2hyaW5rOiAxLFxuICAgIGZsZXhEaXJlY3Rpb246ICdyb3cnLFxuICAgIHRleHRXcmFwOiAndHJ1bmNhdGUtZW5kJyxcbiAgfSxcbiAgdHJ1bmNhdGU6IHtcbiAgICBmbGV4R3JvdzogMCxcbiAgICBmbGV4U2hyaW5rOiAxLFxuICAgIGZsZXhEaXJlY3Rpb246ICdyb3cnLFxuICAgIHRleHRXcmFwOiAndHJ1bmNhdGUnLFxuICB9LFxuICAndHJ1bmNhdGUtbWlkZGxlJzoge1xuICAgIGZsZXhHcm93OiAwLFxuICAgIGZsZXhTaHJpbms6IDEsXG4gICAgZmxleERpcmVjdGlvbjogJ3JvdycsXG4gICAgdGV4dFdyYXA6ICd0cnVuY2F0ZS1taWRkbGUnLFxuICB9LFxuICAndHJ1bmNhdGUtc3RhcnQnOiB7XG4gICAgZmxleEdyb3c6IDAsXG4gICAgZmxleFNocmluazogMSxcbiAgICBmbGV4RGlyZWN0aW9uOiAncm93JyxcbiAgICB0ZXh0V3JhcDogJ3RydW5jYXRlLXN0YXJ0JyxcbiAgfSxcbn0gYXMgY29uc3RcblxuLyoqXG4gKiBUaGlzIGNvbXBvbmVudCBjYW4gZGlzcGxheSB0ZXh0LCBhbmQgY2hhbmdlIGl0cyBzdHlsZSB0byBtYWtlIGl0IGNvbG9yZnVsLCBib2xkLCB1bmRlcmxpbmUsIGl0YWxpYyBvciBzdHJpa2V0aHJvdWdoLlxuICovXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBUZXh0KHtcbiAgY29sb3IsXG4gIGJhY2tncm91bmRDb2xvcixcbiAgYm9sZCxcbiAgZGltLFxuICBpdGFsaWMgPSBmYWxzZSxcbiAgdW5kZXJsaW5lID0gZmFsc2UsXG4gIHN0cmlrZXRocm91Z2ggPSBmYWxzZSxcbiAgaW52ZXJzZSA9IGZhbHNlLFxuICB3cmFwID0gJ3dyYXAnLFxuICBjaGlsZHJlbixcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgaWYgKGNoaWxkcmVuID09PSB1bmRlZmluZWQgfHwgY2hpbGRyZW4gPT09IG51bGwpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgLy8gQnVpbGQgdGV4dFN0eWxlcyBvYmplY3Qgd2l0aCBvbmx5IHRoZSBwcm9wZXJ0aWVzIHRoYXQgYXJlIHNldFxuICBjb25zdCB0ZXh0U3R5bGVzOiBUZXh0U3R5bGVzID0ge1xuICAgIC4uLihjb2xvciAmJiB7IGNvbG9yIH0pLFxuICAgIC4uLihiYWNrZ3JvdW5kQ29sb3IgJiYgeyBiYWNrZ3JvdW5kQ29sb3IgfSksXG4gICAgLi4uKGRpbSAmJiB7IGRpbSB9KSxcbiAgICAuLi4oYm9sZCAmJiB7IGJvbGQgfSksXG4gICAgLi4uKGl0YWxpYyAmJiB7IGl0YWxpYyB9KSxcbiAgICAuLi4odW5kZXJsaW5lICYmIHsgdW5kZXJsaW5lIH0pLFxuICAgIC4uLihzdHJpa2V0aHJvdWdoICYmIHsgc3RyaWtldGhyb3VnaCB9KSxcbiAgICAuLi4oaW52ZXJzZSAmJiB7IGludmVyc2UgfSksXG4gIH1cblxuICByZXR1cm4gKFxuICAgIDxpbmstdGV4dCBzdHlsZT17bWVtb2l6ZWRTdHlsZXNGb3JXcmFwW3dyYXBdfSB0ZXh0U3R5bGVzPXt0ZXh0U3R5bGVzfT5cbiAgICAgIHtjaGlsZHJlbn1cbiAgICA8L2luay10ZXh0PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxjQUFjQSxTQUFTLFFBQVEsT0FBTztBQUN0QyxPQUFPQyxLQUFLLE1BQU0sT0FBTztBQUN6QixjQUFjQyxLQUFLLEVBQUVDLE1BQU0sRUFBRUMsVUFBVSxRQUFRLGNBQWM7QUFFN0QsS0FBS0MsU0FBUyxHQUFHO0VBQ2Y7QUFDRjtBQUNBO0VBQ0UsU0FBU0MsS0FBSyxDQUFDLEVBQUVKLEtBQUs7O0VBRXRCO0FBQ0Y7QUFDQTtFQUNFLFNBQVNLLGVBQWUsQ0FBQyxFQUFFTCxLQUFLOztFQUVoQztBQUNGO0FBQ0E7RUFDRSxTQUFTTSxNQUFNLENBQUMsRUFBRSxPQUFPOztFQUV6QjtBQUNGO0FBQ0E7RUFDRSxTQUFTQyxTQUFTLENBQUMsRUFBRSxPQUFPOztFQUU1QjtBQUNGO0FBQ0E7RUFDRSxTQUFTQyxhQUFhLENBQUMsRUFBRSxPQUFPOztFQUVoQztBQUNGO0FBQ0E7RUFDRSxTQUFTQyxPQUFPLENBQUMsRUFBRSxPQUFPOztFQUUxQjtBQUNGO0FBQ0E7QUFDQTtBQUNBO0VBQ0UsU0FBU0MsSUFBSSxDQUFDLEVBQUVULE1BQU0sQ0FBQyxVQUFVLENBQUM7RUFFbEMsU0FBU1UsUUFBUSxDQUFDLEVBQUViLFNBQVM7QUFDL0IsQ0FBQzs7QUFFRDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUtjLFdBQVcsR0FDWjtF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 \ No newline at end of file diff --git a/ink/constants.ts b/ink/constants.ts new file mode 100644 index 0000000..bff7331 --- /dev/null +++ b/ink/constants.ts @@ -0,0 +1,2 @@ +// Shared frame interval for render throttling and animations (~60fps) +export const FRAME_INTERVAL_MS = 16 diff --git a/ink/dom.ts b/ink/dom.ts new file mode 100644 index 0000000..993dadd --- /dev/null +++ b/ink/dom.ts @@ -0,0 +1,484 @@ +import type { FocusManager } from './focus.js' +import { createLayoutNode } from './layout/engine.js' +import type { LayoutNode } from './layout/node.js' +import { LayoutDisplay, LayoutMeasureMode } from './layout/node.js' +import measureText from './measure-text.js' +import { addPendingClear, nodeCache } from './node-cache.js' +import squashTextNodes from './squash-text-nodes.js' +import type { Styles, TextStyles } from './styles.js' +import { expandTabs } from './tabstops.js' +import wrapText from './wrap-text.js' + +type InkNode = { + parentNode: DOMElement | undefined + yogaNode?: LayoutNode + style: Styles +} + +export type TextName = '#text' +export type ElementNames = + | 'ink-root' + | 'ink-box' + | 'ink-text' + | 'ink-virtual-text' + | 'ink-link' + | 'ink-progress' + | 'ink-raw-ansi' + +export type NodeNames = ElementNames | TextName + +// eslint-disable-next-line @typescript-eslint/naming-convention +export type DOMElement = { + nodeName: ElementNames + attributes: Record + childNodes: DOMNode[] + textStyles?: TextStyles + + // Internal properties + onComputeLayout?: () => void + onRender?: () => void + onImmediateRender?: () => void + // Used to skip empty renders during React 19's effect double-invoke in test mode + hasRenderedContent?: boolean + + // When true, this node needs re-rendering + dirty: boolean + // Set by the reconciler's hideInstance/unhideInstance; survives style updates. + isHidden?: boolean + // Event handlers set by the reconciler for the capture/bubble dispatcher. + // Stored separately from attributes so handler identity changes don't + // mark dirty and defeat the blit optimization. + _eventHandlers?: Record + + // Scroll state for overflow: 'scroll' boxes. scrollTop is the number of + // rows the content is scrolled down by. scrollHeight/scrollViewportHeight + // are computed at render time and stored for imperative access. stickyScroll + // auto-pins scrollTop to the bottom when content grows. + scrollTop?: number + // Accumulated scroll delta not yet applied to scrollTop. The renderer + // drains this at SCROLL_MAX_PER_FRAME rows/frame so fast flicks show + // intermediate frames instead of one big jump. Direction reversal + // naturally cancels (pure accumulator, no target tracking). + pendingScrollDelta?: number + // Render-time clamp bounds for virtual scroll. useVirtualScroll writes + // the currently-mounted children's coverage span; render-node-to-output + // clamps scrollTop to stay within it. Prevents blank screen when + // scrollTo's direct write races past React's async re-render — instead + // of painting spacer (blank), the renderer holds at the edge of mounted + // content until React catches up (next commit updates these bounds and + // the clamp releases). Undefined = no clamp (sticky-scroll, cold start). + scrollClampMin?: number + scrollClampMax?: number + scrollHeight?: number + scrollViewportHeight?: number + scrollViewportTop?: number + stickyScroll?: boolean + // Set by ScrollBox.scrollToElement; render-node-to-output reads + // el.yogaNode.getComputedTop() (FRESH — same Yoga pass as scrollHeight) + // and sets scrollTop = top + offset, then clears this. Unlike an + // imperative scrollTo(N) which bakes in a number that's stale by the + // time the throttled render fires, the element ref defers the position + // read to paint time. One-shot. + scrollAnchor?: { el: DOMElement; offset: number } + // Only set on ink-root. The document owns focus — any node can + // reach it by walking parentNode, like browser getRootNode(). + focusManager?: FocusManager + // React component stack captured at createInstance time (reconciler.ts), + // e.g. ['ToolUseLoader', 'Messages', 'REPL']. Only populated when + // CLAUDE_CODE_DEBUG_REPAINTS is set. Used by findOwnerChainAtRow to + // attribute scrollback-diff full-resets to the component that caused them. + debugOwnerChain?: string[] +} & InkNode + +export type TextNode = { + nodeName: TextName + nodeValue: string +} & InkNode + +// eslint-disable-next-line @typescript-eslint/naming-convention +export type DOMNode = T extends { + nodeName: infer U +} + ? U extends '#text' + ? TextNode + : DOMElement + : never + +// eslint-disable-next-line @typescript-eslint/naming-convention +export type DOMNodeAttribute = boolean | string | number + +export const createNode = (nodeName: ElementNames): DOMElement => { + const needsYogaNode = + nodeName !== 'ink-virtual-text' && + nodeName !== 'ink-link' && + nodeName !== 'ink-progress' + const node: DOMElement = { + nodeName, + style: {}, + attributes: {}, + childNodes: [], + parentNode: undefined, + yogaNode: needsYogaNode ? createLayoutNode() : undefined, + dirty: false, + } + + if (nodeName === 'ink-text') { + node.yogaNode?.setMeasureFunc(measureTextNode.bind(null, node)) + } else if (nodeName === 'ink-raw-ansi') { + node.yogaNode?.setMeasureFunc(measureRawAnsiNode.bind(null, node)) + } + + return node +} + +export const appendChildNode = ( + node: DOMElement, + childNode: DOMElement, +): void => { + if (childNode.parentNode) { + removeChildNode(childNode.parentNode, childNode) + } + + childNode.parentNode = node + node.childNodes.push(childNode) + + if (childNode.yogaNode) { + node.yogaNode?.insertChild( + childNode.yogaNode, + node.yogaNode.getChildCount(), + ) + } + + markDirty(node) +} + +export const insertBeforeNode = ( + node: DOMElement, + newChildNode: DOMNode, + beforeChildNode: DOMNode, +): void => { + if (newChildNode.parentNode) { + removeChildNode(newChildNode.parentNode, newChildNode) + } + + newChildNode.parentNode = node + + const index = node.childNodes.indexOf(beforeChildNode) + + if (index >= 0) { + // Calculate yoga index BEFORE modifying childNodes. + // We can't use DOM index directly because some children (like ink-progress, + // ink-link, ink-virtual-text) don't have yogaNodes, so DOM indices don't + // match yoga indices. + let yogaIndex = 0 + if (newChildNode.yogaNode && node.yogaNode) { + for (let i = 0; i < index; i++) { + if (node.childNodes[i]?.yogaNode) { + yogaIndex++ + } + } + } + + node.childNodes.splice(index, 0, newChildNode) + + if (newChildNode.yogaNode && node.yogaNode) { + node.yogaNode.insertChild(newChildNode.yogaNode, yogaIndex) + } + + markDirty(node) + return + } + + node.childNodes.push(newChildNode) + + if (newChildNode.yogaNode) { + node.yogaNode?.insertChild( + newChildNode.yogaNode, + node.yogaNode.getChildCount(), + ) + } + + markDirty(node) +} + +export const removeChildNode = ( + node: DOMElement, + removeNode: DOMNode, +): void => { + if (removeNode.yogaNode) { + removeNode.parentNode?.yogaNode?.removeChild(removeNode.yogaNode) + } + + // Collect cached rects from the removed subtree so they can be cleared + collectRemovedRects(node, removeNode) + + removeNode.parentNode = undefined + + const index = node.childNodes.indexOf(removeNode) + if (index >= 0) { + node.childNodes.splice(index, 1) + } + + markDirty(node) +} + +function collectRemovedRects( + parent: DOMElement, + removed: DOMNode, + underAbsolute = false, +): void { + if (removed.nodeName === '#text') return + const elem = removed as DOMElement + // If this node or any ancestor in the removed subtree was absolute, + // its painted pixels may overlap non-siblings — flag for global blit + // disable. Normal-flow removals only affect direct siblings, which + // hasRemovedChild already handles. + const isAbsolute = underAbsolute || elem.style.position === 'absolute' + const cached = nodeCache.get(elem) + if (cached) { + addPendingClear(parent, cached, isAbsolute) + nodeCache.delete(elem) + } + for (const child of elem.childNodes) { + collectRemovedRects(parent, child, isAbsolute) + } +} + +export const setAttribute = ( + node: DOMElement, + key: string, + value: DOMNodeAttribute, +): void => { + // Skip 'children' - React handles children via appendChild/removeChild, + // not attributes. React always passes a new children reference, so + // tracking it as an attribute would mark everything dirty every render. + if (key === 'children') { + return + } + // Skip if unchanged + if (node.attributes[key] === value) { + return + } + node.attributes[key] = value + markDirty(node) +} + +export const setStyle = (node: DOMNode, style: Styles): void => { + // Compare style properties to avoid marking dirty unnecessarily. + // React creates new style objects on every render even when unchanged. + if (stylesEqual(node.style, style)) { + return + } + node.style = style + markDirty(node) +} + +export const setTextStyles = ( + node: DOMElement, + textStyles: TextStyles, +): void => { + // Same dirty-check guard as setStyle: React (and buildTextStyles in Text.tsx) + // allocate a new textStyles object on every render even when values are + // unchanged, so compare by value to avoid markDirty -> yoga re-measurement + // on every Text re-render. + if (shallowEqual(node.textStyles, textStyles)) { + return + } + node.textStyles = textStyles + markDirty(node) +} + +function stylesEqual(a: Styles, b: Styles): boolean { + return shallowEqual(a, b) +} + +function shallowEqual( + a: T | undefined, + b: T | undefined, +): boolean { + // Fast path: same object reference (or both undefined) + if (a === b) return true + if (a === undefined || b === undefined) return false + + // Get all keys from both objects + const aKeys = Object.keys(a) as (keyof T)[] + const bKeys = Object.keys(b) as (keyof T)[] + + // Different number of properties + if (aKeys.length !== bKeys.length) return false + + // Compare each property + for (const key of aKeys) { + if (a[key] !== b[key]) return false + } + + return true +} + +export const createTextNode = (text: string): TextNode => { + const node: TextNode = { + nodeName: '#text', + nodeValue: text, + yogaNode: undefined, + parentNode: undefined, + style: {}, + } + + setTextNodeValue(node, text) + + return node +} + +const measureTextNode = function ( + node: DOMNode, + width: number, + widthMode: LayoutMeasureMode, +): { width: number; height: number } { + const rawText = + node.nodeName === '#text' ? node.nodeValue : squashTextNodes(node) + + // Expand tabs for measurement (worst case: 8 spaces each). + // Actual tab expansion happens in output.ts based on screen position. + const text = expandTabs(rawText) + + const dimensions = measureText(text, width) + + // Text fits into container, no need to wrap + if (dimensions.width <= width) { + return dimensions + } + + // This is happening when is shrinking child nodes and layout asks + // if we can fit this text node in a <1px space, so we just say "no" + if (dimensions.width >= 1 && width > 0 && width < 1) { + return dimensions + } + + // For text with embedded newlines (pre-wrapped content), avoid re-wrapping + // at measurement width when layout is asking for intrinsic size (Undefined mode). + // This prevents height inflation during min/max size checks. + // + // However, when layout provides an actual constraint (Exactly or AtMost mode), + // we must respect it and measure at that width. Otherwise, if the actual + // rendering width is smaller than the natural width, the text will wrap to + // more lines than layout expects, causing content to be truncated. + if (text.includes('\n') && widthMode === LayoutMeasureMode.Undefined) { + const effectiveWidth = Math.max(width, dimensions.width) + return measureText(text, effectiveWidth) + } + + const textWrap = node.style?.textWrap ?? 'wrap' + const wrappedText = wrapText(text, width, textWrap) + + return measureText(wrappedText, width) +} + +// ink-raw-ansi nodes hold pre-rendered ANSI strings with known dimensions. +// No stringWidth, no wrapping, no tab expansion — the producer (e.g. ColorDiff) +// already wrapped to the target width and each line is exactly one terminal row. +const measureRawAnsiNode = function (node: DOMElement): { + width: number + height: number +} { + return { + width: node.attributes['rawWidth'] as number, + height: node.attributes['rawHeight'] as number, + } +} + +/** + * Mark a node and all its ancestors as dirty for re-rendering. + * Also marks yoga dirty for text remeasurement if this is a text node. + */ +export const markDirty = (node?: DOMNode): void => { + let current: DOMNode | undefined = node + let markedYoga = false + + while (current) { + if (current.nodeName !== '#text') { + ;(current as DOMElement).dirty = true + // Only mark yoga dirty on leaf nodes that have measure functions + if ( + !markedYoga && + (current.nodeName === 'ink-text' || + current.nodeName === 'ink-raw-ansi') && + current.yogaNode + ) { + current.yogaNode.markDirty() + markedYoga = true + } + } + current = current.parentNode + } +} + +// Walk to root and call its onRender (the throttled scheduleRender). Use for +// DOM-level mutations (scrollTop changes) that should trigger an Ink frame +// without going through React's reconciler. Pair with markDirty() so the +// renderer knows which subtree to re-evaluate. +export const scheduleRenderFrom = (node?: DOMNode): void => { + let cur: DOMNode | undefined = node + while (cur?.parentNode) cur = cur.parentNode + if (cur && cur.nodeName !== '#text') (cur as DOMElement).onRender?.() +} + +export const setTextNodeValue = (node: TextNode, text: string): void => { + if (typeof text !== 'string') { + text = String(text) + } + + // Skip if unchanged + if (node.nodeValue === text) { + return + } + + node.nodeValue = text + markDirty(node) +} + +function isDOMElement(node: DOMElement | TextNode): node is DOMElement { + return node.nodeName !== '#text' +} + +// Clear yogaNode references recursively before freeing. +// freeRecursive() frees the node and ALL its children, so we must clear +// all yogaNode references to prevent dangling pointers. +export const clearYogaNodeReferences = (node: DOMElement | TextNode): void => { + if ('childNodes' in node) { + for (const child of node.childNodes) { + clearYogaNodeReferences(child) + } + } + node.yogaNode = undefined +} + +/** + * Find the React component stack responsible for content at screen row `y`. + * + * DFS the DOM tree accumulating yoga offsets. Returns the debugOwnerChain of + * the deepest node whose bounding box contains `y`. Called from ink.tsx when + * log-update triggers a full reset, to attribute the flicker to its source. + * + * Only useful when CLAUDE_CODE_DEBUG_REPAINTS is set (otherwise chains are + * undefined and this returns []). + */ +export function findOwnerChainAtRow(root: DOMElement, y: number): string[] { + let best: string[] = [] + walk(root, 0) + return best + + function walk(node: DOMElement, offsetY: number): void { + const yoga = node.yogaNode + if (!yoga || yoga.getDisplay() === LayoutDisplay.None) return + + const top = offsetY + yoga.getComputedTop() + const height = yoga.getComputedHeight() + if (y < top || y >= top + height) return + + if (node.debugOwnerChain) best = node.debugOwnerChain + + for (const child of node.childNodes) { + if (isDOMElement(child)) walk(child, top) + } + } +} diff --git a/ink/events/click-event.ts b/ink/events/click-event.ts new file mode 100644 index 0000000..1f58659 --- /dev/null +++ b/ink/events/click-event.ts @@ -0,0 +1,38 @@ +import { Event } from './event.js' + +/** + * Mouse click event. Fired on left-button release without drag, only when + * mouse tracking is enabled (i.e. inside ). + * + * Bubbles from the deepest hit node up through parentNode. Call + * stopImmediatePropagation() to prevent ancestors' onClick from firing. + */ +export class ClickEvent extends Event { + /** 0-indexed screen column of the click */ + readonly col: number + /** 0-indexed screen row of the click */ + readonly row: number + /** + * Click column relative to the current handler's Box (col - box.x). + * Recomputed by dispatchClick before each handler fires, so an onClick + * on a container sees coords relative to that container, not to any + * child the click landed on. + */ + localCol = 0 + /** Click row relative to the current handler's Box (row - box.y). */ + localRow = 0 + /** + * True if the clicked cell has no visible content (unwritten in the + * screen buffer — both packed words are 0). Handlers can check this to + * ignore clicks on blank space to the right of text, so accidental + * clicks on empty terminal space don't toggle state. + */ + readonly cellIsBlank: boolean + + constructor(col: number, row: number, cellIsBlank: boolean) { + super() + this.col = col + this.row = row + this.cellIsBlank = cellIsBlank + } +} diff --git a/ink/events/dispatcher.ts b/ink/events/dispatcher.ts new file mode 100644 index 0000000..a310d38 --- /dev/null +++ b/ink/events/dispatcher.ts @@ -0,0 +1,233 @@ +import { + ContinuousEventPriority, + DefaultEventPriority, + DiscreteEventPriority, + NoEventPriority, +} from 'react-reconciler/constants.js' +import { logError } from '../../utils/log.js' +import { HANDLER_FOR_EVENT } from './event-handlers.js' +import type { EventTarget, TerminalEvent } from './terminal-event.js' + +// -- + +type DispatchListener = { + node: EventTarget + handler: (event: TerminalEvent) => void + phase: 'capturing' | 'at_target' | 'bubbling' +} + +function getHandler( + node: EventTarget, + eventType: string, + capture: boolean, +): ((event: TerminalEvent) => void) | undefined { + const handlers = node._eventHandlers + if (!handlers) return undefined + + const mapping = HANDLER_FOR_EVENT[eventType] + if (!mapping) return undefined + + const propName = capture ? mapping.capture : mapping.bubble + if (!propName) return undefined + + return handlers[propName] as ((event: TerminalEvent) => void) | undefined +} + +/** + * Collect all listeners for an event in dispatch order. + * + * Uses react-dom's two-phase accumulation pattern: + * - Walk from target to root + * - Capture handlers are prepended (unshift) → root-first + * - Bubble handlers are appended (push) → target-first + * + * Result: [root-cap, ..., parent-cap, target-cap, target-bub, parent-bub, ..., root-bub] + */ +function collectListeners( + target: EventTarget, + event: TerminalEvent, +): DispatchListener[] { + const listeners: DispatchListener[] = [] + + let node: EventTarget | undefined = target + while (node) { + const isTarget = node === target + + const captureHandler = getHandler(node, event.type, true) + const bubbleHandler = getHandler(node, event.type, false) + + if (captureHandler) { + listeners.unshift({ + node, + handler: captureHandler, + phase: isTarget ? 'at_target' : 'capturing', + }) + } + + if (bubbleHandler && (event.bubbles || isTarget)) { + listeners.push({ + node, + handler: bubbleHandler, + phase: isTarget ? 'at_target' : 'bubbling', + }) + } + + node = node.parentNode + } + + return listeners +} + +/** + * Execute collected listeners with propagation control. + * + * Before each handler, calls event._prepareForTarget(node) so event + * subclasses can do per-node setup. + */ +function processDispatchQueue( + listeners: DispatchListener[], + event: TerminalEvent, +): void { + let previousNode: EventTarget | undefined + + for (const { node, handler, phase } of listeners) { + if (event._isImmediatePropagationStopped()) { + break + } + + if (event._isPropagationStopped() && node !== previousNode) { + break + } + + event._setEventPhase(phase) + event._setCurrentTarget(node) + event._prepareForTarget(node) + + try { + handler(event) + } catch (error) { + logError(error) + } + + previousNode = node + } +} + +// -- + +/** + * Map terminal event types to React scheduling priorities. + * Mirrors react-dom's getEventPriority() switch. + */ +function getEventPriority(eventType: string): number { + switch (eventType) { + case 'keydown': + case 'keyup': + case 'click': + case 'focus': + case 'blur': + case 'paste': + return DiscreteEventPriority as number + case 'resize': + case 'scroll': + case 'mousemove': + return ContinuousEventPriority as number + default: + return DefaultEventPriority as number + } +} + +// -- + +type DiscreteUpdates = ( + fn: (a: A, b: B) => boolean, + a: A, + b: B, + c: undefined, + d: undefined, +) => boolean + +/** + * Owns event dispatch state and the capture/bubble dispatch loop. + * + * The reconciler host config reads currentEvent and currentUpdatePriority + * to implement resolveUpdatePriority, resolveEventType, and + * resolveEventTimeStamp — mirroring how react-dom's host config reads + * ReactDOMSharedInternals and window.event. + * + * discreteUpdates is injected after construction (by InkReconciler) + * to break the import cycle. + */ +export class Dispatcher { + currentEvent: TerminalEvent | null = null + currentUpdatePriority: number = DefaultEventPriority as number + discreteUpdates: DiscreteUpdates | null = null + + /** + * Infer event priority from the currently-dispatching event. + * Called by the reconciler host config's resolveUpdatePriority + * when no explicit priority has been set. + */ + resolveEventPriority(): number { + if (this.currentUpdatePriority !== (NoEventPriority as number)) { + return this.currentUpdatePriority + } + if (this.currentEvent) { + return getEventPriority(this.currentEvent.type) + } + return DefaultEventPriority as number + } + + /** + * Dispatch an event through capture and bubble phases. + * Returns true if preventDefault() was NOT called. + */ + dispatch(target: EventTarget, event: TerminalEvent): boolean { + const previousEvent = this.currentEvent + this.currentEvent = event + try { + event._setTarget(target) + + const listeners = collectListeners(target, event) + processDispatchQueue(listeners, event) + + event._setEventPhase('none') + event._setCurrentTarget(null) + + return !event.defaultPrevented + } finally { + this.currentEvent = previousEvent + } + } + + /** + * Dispatch with discrete (sync) priority. + * For user-initiated events: keyboard, click, focus, paste. + */ + dispatchDiscrete(target: EventTarget, event: TerminalEvent): boolean { + if (!this.discreteUpdates) { + return this.dispatch(target, event) + } + return this.discreteUpdates( + (t, e) => this.dispatch(t, e), + target, + event, + undefined, + undefined, + ) + } + + /** + * Dispatch with continuous priority. + * For high-frequency events: resize, scroll, mouse move. + */ + dispatchContinuous(target: EventTarget, event: TerminalEvent): boolean { + const previousPriority = this.currentUpdatePriority + try { + this.currentUpdatePriority = ContinuousEventPriority as number + return this.dispatch(target, event) + } finally { + this.currentUpdatePriority = previousPriority + } + } +} diff --git a/ink/events/emitter.ts b/ink/events/emitter.ts new file mode 100644 index 0000000..56a4b0d --- /dev/null +++ b/ink/events/emitter.ts @@ -0,0 +1,39 @@ +import { EventEmitter as NodeEventEmitter } from 'events' +import { Event } from './event.js' + +// Similar to node's builtin EventEmitter, but is also aware of our `Event` +// class, and so `emit` respects `stopImmediatePropagation()`. +export class EventEmitter extends NodeEventEmitter { + constructor() { + super() + // Disable the default maxListeners warning. In React, many components + // can legitimately listen to the same event (e.g., useInput hooks). + // The default limit of 10 causes spurious warnings. + this.setMaxListeners(0) + } + + override emit(type: string | symbol, ...args: unknown[]): boolean { + // Delegate to node for `error`, since it's not treated like a normal event + if (type === 'error') { + return super.emit(type, ...args) + } + + const listeners = this.rawListeners(type) + + if (listeners.length === 0) { + return false + } + + const ccEvent = args[0] instanceof Event ? args[0] : null + + for (const listener of listeners) { + listener.apply(this, args) + + if (ccEvent?.didStopImmediatePropagation()) { + break + } + } + + return true + } +} diff --git a/ink/events/event-handlers.ts b/ink/events/event-handlers.ts new file mode 100644 index 0000000..7865f5b --- /dev/null +++ b/ink/events/event-handlers.ts @@ -0,0 +1,73 @@ +import type { ClickEvent } from './click-event.js' +import type { FocusEvent } from './focus-event.js' +import type { KeyboardEvent } from './keyboard-event.js' +import type { PasteEvent } from './paste-event.js' +import type { ResizeEvent } from './resize-event.js' + +type KeyboardEventHandler = (event: KeyboardEvent) => void +type FocusEventHandler = (event: FocusEvent) => void +type PasteEventHandler = (event: PasteEvent) => void +type ResizeEventHandler = (event: ResizeEvent) => void +type ClickEventHandler = (event: ClickEvent) => void +type HoverEventHandler = () => void + +/** + * Props for event handlers on Box and other host components. + * + * Follows the React/DOM naming convention: + * - onEventName: handler for bubble phase + * - onEventNameCapture: handler for capture phase + */ +export type EventHandlerProps = { + onKeyDown?: KeyboardEventHandler + onKeyDownCapture?: KeyboardEventHandler + + onFocus?: FocusEventHandler + onFocusCapture?: FocusEventHandler + onBlur?: FocusEventHandler + onBlurCapture?: FocusEventHandler + + onPaste?: PasteEventHandler + onPasteCapture?: PasteEventHandler + + onResize?: ResizeEventHandler + + onClick?: ClickEventHandler + onMouseEnter?: HoverEventHandler + onMouseLeave?: HoverEventHandler +} + +/** + * Reverse lookup: event type string → handler prop names. + * Used by the dispatcher for O(1) handler lookup per node. + */ +export const HANDLER_FOR_EVENT: Record< + string, + { bubble?: keyof EventHandlerProps; capture?: keyof EventHandlerProps } +> = { + keydown: { bubble: 'onKeyDown', capture: 'onKeyDownCapture' }, + focus: { bubble: 'onFocus', capture: 'onFocusCapture' }, + blur: { bubble: 'onBlur', capture: 'onBlurCapture' }, + paste: { bubble: 'onPaste', capture: 'onPasteCapture' }, + resize: { bubble: 'onResize' }, + click: { bubble: 'onClick' }, +} + +/** + * Set of all event handler prop names, for the reconciler to detect + * event props and store them in _eventHandlers instead of attributes. + */ +export const EVENT_HANDLER_PROPS = new Set([ + 'onKeyDown', + 'onKeyDownCapture', + 'onFocus', + 'onFocusCapture', + 'onBlur', + 'onBlurCapture', + 'onPaste', + 'onPasteCapture', + 'onResize', + 'onClick', + 'onMouseEnter', + 'onMouseLeave', +]) diff --git a/ink/events/event.ts b/ink/events/event.ts new file mode 100644 index 0000000..6187400 --- /dev/null +++ b/ink/events/event.ts @@ -0,0 +1,11 @@ +export class Event { + private _didStopImmediatePropagation = false + + didStopImmediatePropagation(): boolean { + return this._didStopImmediatePropagation + } + + stopImmediatePropagation(): void { + this._didStopImmediatePropagation = true + } +} diff --git a/ink/events/focus-event.ts b/ink/events/focus-event.ts new file mode 100644 index 0000000..a552e54 --- /dev/null +++ b/ink/events/focus-event.ts @@ -0,0 +1,21 @@ +import { type EventTarget, TerminalEvent } from './terminal-event.js' + +/** + * Focus event for component focus changes. + * + * Dispatched when focus moves between elements. 'focus' fires on the + * newly focused element, 'blur' fires on the previously focused one. + * Both bubble, matching react-dom's use of focusin/focusout semantics + * so parent components can observe descendant focus changes. + */ +export class FocusEvent extends TerminalEvent { + readonly relatedTarget: EventTarget | null + + constructor( + type: 'focus' | 'blur', + relatedTarget: EventTarget | null = null, + ) { + super(type, { bubbles: true, cancelable: false }) + this.relatedTarget = relatedTarget + } +} diff --git a/ink/events/input-event.ts b/ink/events/input-event.ts new file mode 100644 index 0000000..4905028 --- /dev/null +++ b/ink/events/input-event.ts @@ -0,0 +1,205 @@ +import { nonAlphanumericKeys, type ParsedKey } from '../parse-keypress.js' +import { Event } from './event.js' + +export type Key = { + upArrow: boolean + downArrow: boolean + leftArrow: boolean + rightArrow: boolean + pageDown: boolean + pageUp: boolean + wheelUp: boolean + wheelDown: boolean + home: boolean + end: boolean + return: boolean + escape: boolean + ctrl: boolean + shift: boolean + fn: boolean + tab: boolean + backspace: boolean + delete: boolean + meta: boolean + super: boolean +} + +function parseKey(keypress: ParsedKey): [Key, string] { + const key: Key = { + upArrow: keypress.name === 'up', + downArrow: keypress.name === 'down', + leftArrow: keypress.name === 'left', + rightArrow: keypress.name === 'right', + pageDown: keypress.name === 'pagedown', + pageUp: keypress.name === 'pageup', + wheelUp: keypress.name === 'wheelup', + wheelDown: keypress.name === 'wheeldown', + home: keypress.name === 'home', + end: keypress.name === 'end', + return: keypress.name === 'return', + escape: keypress.name === 'escape', + fn: keypress.fn, + ctrl: keypress.ctrl, + shift: keypress.shift, + tab: keypress.name === 'tab', + backspace: keypress.name === 'backspace', + delete: keypress.name === 'delete', + // `parseKeypress` parses \u001B\u001B[A (meta + up arrow) as meta = false + // but with option = true, so we need to take this into account here + // to avoid breaking changes in Ink. + // TODO(vadimdemedes): consider removing this in the next major version. + meta: keypress.meta || keypress.name === 'escape' || keypress.option, + // Super (Cmd on macOS / Win key) — only arrives via kitty keyboard + // protocol CSI u sequences. Distinct from meta (Alt/Option) so + // bindings like cmd+c can be expressed separately from opt+c. + super: keypress.super, + } + + let input = keypress.ctrl ? keypress.name : keypress.sequence + + // Handle undefined input case + if (input === undefined) { + input = '' + } + + // When ctrl is set, keypress.name for space is the literal word "space". + // Convert to actual space character for consistency with the CSI u branch + // (which maps 'space' → ' '). Without this, ctrl+space leaks the literal + // word "space" into text input. + if (keypress.ctrl && input === 'space') { + input = ' ' + } + + // Suppress unrecognized escape sequences that were parsed as function keys + // (matched by FN_KEY_RE) but have no name in the keyName map. + // Examples: ESC[25~ (F13/Right Alt on Windows), ESC[26~ (F14), etc. + // Without this, the ESC prefix is stripped below and the remainder (e.g., + // "[25~") leaks into the input as literal text. + if (keypress.code && !keypress.name) { + input = '' + } + + // Suppress ESC-less SGR mouse fragments. When a heavy React commit blocks + // the event loop past App's 50ms NORMAL_TIMEOUT flush, a CSI split across + // stdin chunks gets its buffered ESC flushed as a lone Escape key, and the + // continuation arrives as a text token with name='' — which falls through + // all of parseKeypress's ESC-anchored regexes and the nonAlphanumericKeys + // clear below (name is falsy). The fragment then leaks into the prompt as + // literal `[<64;74;16M`. This is the same defensive sink as the F13 guard + // above; the underlying tokenizer-flush race is upstream of this layer. + if (!keypress.name && /^\[<\d+;\d+;\d+[Mm]/.test(input)) { + input = '' + } + + // Strip meta if it's still remaining after `parseKeypress` + // TODO(vadimdemedes): remove this in the next major version. + if (input.startsWith('\u001B')) { + input = input.slice(1) + } + + // Track whether we've already processed this as a special sequence + // that converted input to the key name (CSI u or application keypad mode). + // For these, we don't want to clear input with nonAlphanumericKeys check. + let processedAsSpecialSequence = false + + // Handle CSI u sequences (Kitty keyboard protocol): after stripping ESC, + // we're left with "[codepoint;modifieru" (e.g., "[98;3u" for Alt+b). + // Use the parsed key name instead for input handling. Require a digit + // after [ — real CSI u is always […u, and a bare startsWith('[') + // false-matches X10 mouse at row 85 (Cy = 85+32 = 'u'), leaking the + // literal text "mouse" into the prompt via processedAsSpecialSequence. + if (/^\[\d/.test(input) && input.endsWith('u')) { + if (!keypress.name) { + // Unmapped Kitty functional key (Caps Lock 57358, F13–F35, KP nav, + // bare modifiers, etc.) — keycodeToName() returned undefined. Swallow + // so the raw "[57358u" doesn't leak into the prompt. See #38781. + input = '' + } else { + // 'space' → ' '; 'escape' → '' (key.escape carries it; + // processedAsSpecialSequence bypasses the nonAlphanumericKeys + // clear below, so we must handle it explicitly here); + // otherwise use key name. + input = + keypress.name === 'space' + ? ' ' + : keypress.name === 'escape' + ? '' + : keypress.name + } + processedAsSpecialSequence = true + } + + // Handle xterm modifyOtherKeys sequences: after stripping ESC, we're left + // with "[27;modifier;keycode~" (e.g., "[27;3;98~" for Alt+b). Same + // extraction as CSI u — without this, printable-char keycodes (single-letter + // names) skip the nonAlphanumericKeys clear and leak "[27;..." as input. + if (input.startsWith('[27;') && input.endsWith('~')) { + if (!keypress.name) { + // Unmapped modifyOtherKeys keycode — swallow for consistency with + // the CSI u handler above. Practically untriggerable today (xterm + // modifyOtherKeys only sends ASCII keycodes, all mapped), but + // guards against future terminal behavior. + input = '' + } else { + input = + keypress.name === 'space' + ? ' ' + : keypress.name === 'escape' + ? '' + : keypress.name + } + processedAsSpecialSequence = true + } + + // Handle application keypad mode sequences: after stripping ESC, + // we're left with "O" (e.g., "Op" for numpad 0, "Oy" for numpad 9). + // Use the parsed key name (the digit character) for input handling. + if ( + input.startsWith('O') && + input.length === 2 && + keypress.name && + keypress.name.length === 1 + ) { + input = keypress.name + processedAsSpecialSequence = true + } + + // Clear input for non-alphanumeric keys (arrows, function keys, etc.) + // Skip this for CSI u and application keypad mode sequences since + // those were already converted to their proper input characters. + if ( + !processedAsSpecialSequence && + keypress.name && + nonAlphanumericKeys.includes(keypress.name) + ) { + input = '' + } + + // Set shift=true for uppercase letters (A-Z) + // Must check it's actually a letter, not just any char unchanged by toUpperCase + if ( + input.length === 1 && + typeof input[0] === 'string' && + input[0] >= 'A' && + input[0] <= 'Z' + ) { + key.shift = true + } + + return [key, input] +} + +export class InputEvent extends Event { + readonly keypress: ParsedKey + readonly key: Key + readonly input: string + + constructor(keypress: ParsedKey) { + super() + const [key, input] = parseKey(keypress) + + this.keypress = keypress + this.key = key + this.input = input + } +} diff --git a/ink/events/keyboard-event.ts b/ink/events/keyboard-event.ts new file mode 100644 index 0000000..1210efd --- /dev/null +++ b/ink/events/keyboard-event.ts @@ -0,0 +1,51 @@ +import type { ParsedKey } from '../parse-keypress.js' +import { TerminalEvent } from './terminal-event.js' + +/** + * Keyboard event dispatched through the DOM tree via capture/bubble. + * + * Follows browser KeyboardEvent semantics: `key` is the literal character + * for printable keys ('a', '3', ' ', '/') and a multi-char name for + * special keys ('down', 'return', 'escape', 'f1'). The idiomatic + * printable-char check is `e.key.length === 1`. + */ +export class KeyboardEvent extends TerminalEvent { + readonly key: string + readonly ctrl: boolean + readonly shift: boolean + readonly meta: boolean + readonly superKey: boolean + readonly fn: boolean + + constructor(parsedKey: ParsedKey) { + super('keydown', { bubbles: true, cancelable: true }) + + this.key = keyFromParsed(parsedKey) + this.ctrl = parsedKey.ctrl + this.shift = parsedKey.shift + this.meta = parsedKey.meta || parsedKey.option + this.superKey = parsedKey.super + this.fn = parsedKey.fn + } +} + +function keyFromParsed(parsed: ParsedKey): string { + const seq = parsed.sequence ?? '' + const name = parsed.name ?? '' + + // Ctrl combos: sequence is a control byte (\x03 for ctrl+c), name is the + // letter. Browsers report e.key === 'c' with e.ctrlKey === true. + if (parsed.ctrl) return name + + // Single printable char (space through ~, plus anything above ASCII): + // use the literal char. Browsers report e.key === '3', not 'Digit3'. + if (seq.length === 1) { + const code = seq.charCodeAt(0) + if (code >= 0x20 && code !== 0x7f) return seq + } + + // Special keys (arrows, F-keys, return, tab, escape, etc.): sequence is + // either an escape sequence (\x1b[B) or a control byte (\r, \t), so use + // the parsed name. Browsers report e.key === 'ArrowDown'. + return name || seq +} diff --git a/ink/events/terminal-event.ts b/ink/events/terminal-event.ts new file mode 100644 index 0000000..9a86bf8 --- /dev/null +++ b/ink/events/terminal-event.ts @@ -0,0 +1,107 @@ +import { Event } from './event.js' + +type EventPhase = 'none' | 'capturing' | 'at_target' | 'bubbling' + +type TerminalEventInit = { + bubbles?: boolean + cancelable?: boolean +} + +/** + * Base class for all terminal events with DOM-style propagation. + * + * Extends Event so existing event types (ClickEvent, InputEvent, + * TerminalFocusEvent) share a common ancestor and can migrate later. + * + * Mirrors the browser's Event API: target, currentTarget, eventPhase, + * stopPropagation(), preventDefault(), timeStamp. + */ +export class TerminalEvent extends Event { + readonly type: string + readonly timeStamp: number + readonly bubbles: boolean + readonly cancelable: boolean + + private _target: EventTarget | null = null + private _currentTarget: EventTarget | null = null + private _eventPhase: EventPhase = 'none' + private _propagationStopped = false + private _defaultPrevented = false + + constructor(type: string, init?: TerminalEventInit) { + super() + this.type = type + this.timeStamp = performance.now() + this.bubbles = init?.bubbles ?? true + this.cancelable = init?.cancelable ?? true + } + + get target(): EventTarget | null { + return this._target + } + + get currentTarget(): EventTarget | null { + return this._currentTarget + } + + get eventPhase(): EventPhase { + return this._eventPhase + } + + get defaultPrevented(): boolean { + return this._defaultPrevented + } + + stopPropagation(): void { + this._propagationStopped = true + } + + override stopImmediatePropagation(): void { + super.stopImmediatePropagation() + this._propagationStopped = true + } + + preventDefault(): void { + if (this.cancelable) { + this._defaultPrevented = true + } + } + + // -- Internal setters used by the Dispatcher + + /** @internal */ + _setTarget(target: EventTarget): void { + this._target = target + } + + /** @internal */ + _setCurrentTarget(target: EventTarget | null): void { + this._currentTarget = target + } + + /** @internal */ + _setEventPhase(phase: EventPhase): void { + this._eventPhase = phase + } + + /** @internal */ + _isPropagationStopped(): boolean { + return this._propagationStopped + } + + /** @internal */ + _isImmediatePropagationStopped(): boolean { + return this.didStopImmediatePropagation() + } + + /** + * Hook for subclasses to do per-node setup before each handler fires. + * Default is a no-op. + */ + _prepareForTarget(_target: EventTarget): void {} +} + +export type EventTarget = { + parentNode: EventTarget | undefined + _eventHandlers?: Record +} diff --git a/ink/events/terminal-focus-event.ts b/ink/events/terminal-focus-event.ts new file mode 100644 index 0000000..6d0303f --- /dev/null +++ b/ink/events/terminal-focus-event.ts @@ -0,0 +1,19 @@ +import { Event } from './event.js' + +export type TerminalFocusEventType = 'terminalfocus' | 'terminalblur' + +/** + * Event fired when the terminal window gains or loses focus. + * + * Uses DECSET 1004 focus reporting - the terminal sends: + * - CSI I (\x1b[I) when the terminal gains focus + * - CSI O (\x1b[O) when the terminal loses focus + */ +export class TerminalFocusEvent extends Event { + readonly type: TerminalFocusEventType + + constructor(type: TerminalFocusEventType) { + super() + this.type = type + } +} diff --git a/ink/focus.ts b/ink/focus.ts new file mode 100644 index 0000000..7072de1 --- /dev/null +++ b/ink/focus.ts @@ -0,0 +1,181 @@ +import type { DOMElement } from './dom.js' +import { FocusEvent } from './events/focus-event.js' + +const MAX_FOCUS_STACK = 32 + +/** + * DOM-like focus manager for the Ink terminal UI. + * + * Pure state — tracks activeElement and a focus stack. Has no reference + * to the tree; callers pass the root when tree walks are needed. + * + * Stored on the root DOMElement so any node can reach it by walking + * parentNode (like browser's `node.ownerDocument`). + */ +export class FocusManager { + activeElement: DOMElement | null = null + private dispatchFocusEvent: (target: DOMElement, event: FocusEvent) => boolean + private enabled = true + private focusStack: DOMElement[] = [] + + constructor( + dispatchFocusEvent: (target: DOMElement, event: FocusEvent) => boolean, + ) { + this.dispatchFocusEvent = dispatchFocusEvent + } + + focus(node: DOMElement): void { + if (node === this.activeElement) return + if (!this.enabled) return + + const previous = this.activeElement + if (previous) { + // Deduplicate before pushing to prevent unbounded growth from Tab cycling + const idx = this.focusStack.indexOf(previous) + if (idx !== -1) this.focusStack.splice(idx, 1) + this.focusStack.push(previous) + if (this.focusStack.length > MAX_FOCUS_STACK) this.focusStack.shift() + this.dispatchFocusEvent(previous, new FocusEvent('blur', node)) + } + this.activeElement = node + this.dispatchFocusEvent(node, new FocusEvent('focus', previous)) + } + + blur(): void { + if (!this.activeElement) return + + const previous = this.activeElement + this.activeElement = null + this.dispatchFocusEvent(previous, new FocusEvent('blur', null)) + } + + /** + * Called by the reconciler when a node is removed from the tree. + * Handles both the exact node and any focused descendant within + * the removed subtree. Dispatches blur and restores focus from stack. + */ + handleNodeRemoved(node: DOMElement, root: DOMElement): void { + // Remove the node and any descendants from the stack + this.focusStack = this.focusStack.filter( + n => n !== node && isInTree(n, root), + ) + + // Check if activeElement is the removed node OR a descendant + if (!this.activeElement) return + if (this.activeElement !== node && isInTree(this.activeElement, root)) { + return + } + + const removed = this.activeElement + this.activeElement = null + this.dispatchFocusEvent(removed, new FocusEvent('blur', null)) + + // Restore focus to the most recent still-mounted element + while (this.focusStack.length > 0) { + const candidate = this.focusStack.pop()! + if (isInTree(candidate, root)) { + this.activeElement = candidate + this.dispatchFocusEvent(candidate, new FocusEvent('focus', removed)) + return + } + } + } + + handleAutoFocus(node: DOMElement): void { + this.focus(node) + } + + handleClickFocus(node: DOMElement): void { + const tabIndex = node.attributes['tabIndex'] + if (typeof tabIndex !== 'number') return + this.focus(node) + } + + enable(): void { + this.enabled = true + } + + disable(): void { + this.enabled = false + } + + focusNext(root: DOMElement): void { + this.moveFocus(1, root) + } + + focusPrevious(root: DOMElement): void { + this.moveFocus(-1, root) + } + + private moveFocus(direction: 1 | -1, root: DOMElement): void { + if (!this.enabled) return + + const tabbable = collectTabbable(root) + if (tabbable.length === 0) return + + const currentIndex = this.activeElement + ? tabbable.indexOf(this.activeElement) + : -1 + + const nextIndex = + currentIndex === -1 + ? direction === 1 + ? 0 + : tabbable.length - 1 + : (currentIndex + direction + tabbable.length) % tabbable.length + + const next = tabbable[nextIndex] + if (next) { + this.focus(next) + } + } +} + +function collectTabbable(root: DOMElement): DOMElement[] { + const result: DOMElement[] = [] + walkTree(root, result) + return result +} + +function walkTree(node: DOMElement, result: DOMElement[]): void { + const tabIndex = node.attributes['tabIndex'] + if (typeof tabIndex === 'number' && tabIndex >= 0) { + result.push(node) + } + + for (const child of node.childNodes) { + if (child.nodeName !== '#text') { + walkTree(child, result) + } + } +} + +function isInTree(node: DOMElement, root: DOMElement): boolean { + let current: DOMElement | undefined = node + while (current) { + if (current === root) return true + current = current.parentNode + } + return false +} + +/** + * Walk up to root and return it. The root is the node that holds + * the FocusManager — like browser's `node.getRootNode()`. + */ +export function getRootNode(node: DOMElement): DOMElement { + let current: DOMElement | undefined = node + while (current) { + if (current.focusManager) return current + current = current.parentNode + } + throw new Error('Node is not in a tree with a FocusManager') +} + +/** + * Walk up to root and return its FocusManager. + * Like browser's `node.ownerDocument` — focus belongs to the root. + */ +export function getFocusManager(node: DOMElement): FocusManager { + return getRootNode(node).focusManager! +} diff --git a/ink/frame.ts b/ink/frame.ts new file mode 100644 index 0000000..ccbbed0 --- /dev/null +++ b/ink/frame.ts @@ -0,0 +1,124 @@ +import type { Cursor } from './cursor.js' +import type { Size } from './layout/geometry.js' +import type { ScrollHint } from './render-node-to-output.js' +import { + type CharPool, + createScreen, + type HyperlinkPool, + type Screen, + type StylePool, +} from './screen.js' + +export type Frame = { + readonly screen: Screen + readonly viewport: Size + readonly cursor: Cursor + /** DECSTBM scroll optimization hint (alt-screen only, null otherwise). */ + readonly scrollHint?: ScrollHint | null + /** A ScrollBox has remaining pendingScrollDelta — schedule another frame. */ + readonly scrollDrainPending?: boolean +} + +export function emptyFrame( + rows: number, + columns: number, + stylePool: StylePool, + charPool: CharPool, + hyperlinkPool: HyperlinkPool, +): Frame { + return { + screen: createScreen(0, 0, stylePool, charPool, hyperlinkPool), + viewport: { width: columns, height: rows }, + cursor: { x: 0, y: 0, visible: true }, + } +} + +export type FlickerReason = 'resize' | 'offscreen' | 'clear' + +export type FrameEvent = { + durationMs: number + /** Phase breakdown in ms + patch count. Populated when the ink instance + * has frame-timing instrumentation enabled (via onFrame wiring). */ + phases?: { + /** createRenderer output: DOM → yoga layout → screen buffer */ + renderer: number + /** LogUpdate.render(): screen diff → Patch[] (the hot path this PR optimizes) */ + diff: number + /** optimize(): patch merge/dedupe */ + optimize: number + /** writeDiffToTerminal(): serialize patches → ANSI → stdout */ + write: number + /** Pre-optimize patch count (proxy for how much changed this frame) */ + patches: number + /** yoga calculateLayout() time (runs in resetAfterCommit, before onRender) */ + yoga: number + /** React reconcile time: scrollMutated → resetAfterCommit. 0 if no commit. */ + commit: number + /** layoutNode() calls this frame (recursive, includes cache-hit returns) */ + yogaVisited: number + /** measureFunc (text wrap/width) calls — the expensive part */ + yogaMeasured: number + /** early returns via _hasL single-slot cache */ + yogaCacheHits: number + /** total yoga Node instances alive (create - free). Growth = leak. */ + yogaLive: number + } + flickers: Array<{ + desiredHeight: number + availableHeight: number + reason: FlickerReason + }> +} + +export type Patch = + | { type: 'stdout'; content: string } + | { type: 'clear'; count: number } + | { + type: 'clearTerminal' + reason: FlickerReason + // Populated by log-update when a scrollback diff triggers the reset. + // ink.tsx uses triggerY with findOwnerChainAtRow to attribute the + // flicker to its source React component. + debug?: { triggerY: number; prevLine: string; nextLine: string } + } + | { type: 'cursorHide' } + | { type: 'cursorShow' } + | { type: 'cursorMove'; x: number; y: number } + | { type: 'cursorTo'; col: number } + | { type: 'carriageReturn' } + | { type: 'hyperlink'; uri: string } + // Pre-serialized style transition string from StylePool.transition() — + // cached by (fromId, toId), zero allocations after warmup. + | { type: 'styleStr'; str: string } + +export type Diff = Patch[] + +/** + * Determines whether the screen should be cleared based on the current and previous frame. + * Returns the reason for clearing, or undefined if no clear is needed. + * + * Screen clearing is triggered when: + * 1. Terminal has been resized (viewport dimensions changed) → 'resize' + * 2. Current frame screen height exceeds available terminal rows → 'offscreen' + * 3. Previous frame screen height exceeded available terminal rows → 'offscreen' + */ +export function shouldClearScreen( + prevFrame: Frame, + frame: Frame, +): FlickerReason | undefined { + const didResize = + frame.viewport.height !== prevFrame.viewport.height || + frame.viewport.width !== prevFrame.viewport.width + if (didResize) { + return 'resize' + } + + const currentFrameOverflows = frame.screen.height >= frame.viewport.height + const previousFrameOverflowed = + prevFrame.screen.height >= prevFrame.viewport.height + if (currentFrameOverflows || previousFrameOverflowed) { + return 'offscreen' + } + + return undefined +} diff --git a/ink/get-max-width.ts b/ink/get-max-width.ts new file mode 100644 index 0000000..e079463 --- /dev/null +++ b/ink/get-max-width.ts @@ -0,0 +1,27 @@ +import { LayoutEdge, type LayoutNode } from './layout/node.js' + +/** + * Returns the yoga node's content width (computed width minus padding and + * border). + * + * Warning: can return a value WIDER than the parent container. In a + * column-direction flex parent, width is the cross axis — align-items: + * stretch never shrinks children below their intrinsic size, so the text + * node overflows (standard CSS behavior). Yoga measures leaf nodes in two + * passes: the AtMost pass determines width, the Exactly pass determines + * height. getComputedWidth() reflects the wider AtMost result while + * getComputedHeight() reflects the narrower Exactly result. Callers that + * use this for wrapping should clamp to actual available screen space so + * the rendered line count stays consistent with the layout height. + */ +const getMaxWidth = (yogaNode: LayoutNode): number => { + return ( + yogaNode.getComputedWidth() - + yogaNode.getComputedPadding(LayoutEdge.Left) - + yogaNode.getComputedPadding(LayoutEdge.Right) - + yogaNode.getComputedBorder(LayoutEdge.Left) - + yogaNode.getComputedBorder(LayoutEdge.Right) + ) +} + +export default getMaxWidth diff --git a/ink/hit-test.ts b/ink/hit-test.ts new file mode 100644 index 0000000..53ddb86 --- /dev/null +++ b/ink/hit-test.ts @@ -0,0 +1,130 @@ +import type { DOMElement } from './dom.js' +import { ClickEvent } from './events/click-event.js' +import type { EventHandlerProps } from './events/event-handlers.js' +import { nodeCache } from './node-cache.js' + +/** + * Find the deepest DOM element whose rendered rect contains (col, row). + * + * Uses the nodeCache populated by renderNodeToOutput — rects are in screen + * coordinates with all offsets (including scrollTop translation) already + * applied. Children are traversed in reverse so later siblings (painted on + * top) win. Nodes not in nodeCache (not rendered this frame, or lacking a + * yogaNode) are skipped along with their subtrees. + * + * Returns the hit node even if it has no onClick — dispatchClick walks up + * via parentNode to find handlers. + */ +export function hitTest( + node: DOMElement, + col: number, + row: number, +): DOMElement | null { + const rect = nodeCache.get(node) + if (!rect) return null + if ( + col < rect.x || + col >= rect.x + rect.width || + row < rect.y || + row >= rect.y + rect.height + ) { + return null + } + // Later siblings paint on top; reversed traversal returns topmost hit. + for (let i = node.childNodes.length - 1; i >= 0; i--) { + const child = node.childNodes[i]! + if (child.nodeName === '#text') continue + const hit = hitTest(child, col, row) + if (hit) return hit + } + return node +} + +/** + * Hit-test the root at (col, row) and bubble a ClickEvent from the deepest + * containing node up through parentNode. Only nodes with an onClick handler + * fire. Stops when a handler calls stopImmediatePropagation(). Returns + * true if at least one onClick handler fired. + */ +export function dispatchClick( + root: DOMElement, + col: number, + row: number, + cellIsBlank = false, +): boolean { + let target: DOMElement | undefined = hitTest(root, col, row) ?? undefined + if (!target) return false + + // Click-to-focus: find the closest focusable ancestor and focus it. + // root is always ink-root, which owns the FocusManager. + if (root.focusManager) { + let focusTarget: DOMElement | undefined = target + while (focusTarget) { + if (typeof focusTarget.attributes['tabIndex'] === 'number') { + root.focusManager.handleClickFocus(focusTarget) + break + } + focusTarget = focusTarget.parentNode + } + } + const event = new ClickEvent(col, row, cellIsBlank) + let handled = false + while (target) { + const handler = target._eventHandlers?.onClick as + | ((event: ClickEvent) => void) + | undefined + if (handler) { + handled = true + const rect = nodeCache.get(target) + if (rect) { + event.localCol = col - rect.x + event.localRow = row - rect.y + } + handler(event) + if (event.didStopImmediatePropagation()) return true + } + target = target.parentNode + } + return handled +} + +/** + * Fire onMouseEnter/onMouseLeave as the pointer moves. Like DOM + * mouseenter/mouseleave: does NOT bubble — moving between children does + * not re-fire on the parent. Walks up from the hit node collecting every + * ancestor with a hover handler; diffs against the previous hovered set; + * fires leave on the nodes exited, enter on the nodes entered. + * + * Mutates `hovered` in place so the caller (App instance) can hold it + * across calls. Clears the set when the hit is null (cursor moved into a + * non-rendered gap or off the root rect). + */ +export function dispatchHover( + root: DOMElement, + col: number, + row: number, + hovered: Set, +): void { + const next = new Set() + let node: DOMElement | undefined = hitTest(root, col, row) ?? undefined + while (node) { + const h = node._eventHandlers as EventHandlerProps | undefined + if (h?.onMouseEnter || h?.onMouseLeave) next.add(node) + node = node.parentNode + } + for (const old of hovered) { + if (!next.has(old)) { + hovered.delete(old) + // Skip handlers on detached nodes (removed between mouse events) + if (old.parentNode) { + ;(old._eventHandlers as EventHandlerProps | undefined)?.onMouseLeave?.() + } + } + } + for (const n of next) { + if (!hovered.has(n)) { + hovered.add(n) + ;(n._eventHandlers as EventHandlerProps | undefined)?.onMouseEnter?.() + } + } +} diff --git a/ink/hooks/use-animation-frame.ts b/ink/hooks/use-animation-frame.ts new file mode 100644 index 0000000..d4dd38a --- /dev/null +++ b/ink/hooks/use-animation-frame.ts @@ -0,0 +1,57 @@ +import { useContext, useEffect, useState } from 'react' +import { ClockContext } from '../components/ClockContext.js' +import type { DOMElement } from '../dom.js' +import { useTerminalViewport } from './use-terminal-viewport.js' + +/** + * Hook for synchronized animations that pause when offscreen. + * + * Returns a ref to attach to the animated element and the current animation time. + * All instances share the same clock, so animations stay in sync. + * The clock only runs when at least one keepAlive subscriber exists. + * + * Pass `null` to pause — unsubscribes from the clock so no ticks fire. + * Time freezes at the last value and resumes from the current clock time + * when a number is passed again. + * + * @param intervalMs - How often to update, or null to pause + * @returns [ref, time] - Ref to attach to element, elapsed time in ms + * + * @example + * function Spinner() { + * const [ref, time] = useAnimationFrame(120) + * const frame = Math.floor(time / 120) % FRAMES.length + * return {FRAMES[frame]} + * } + * + * The clock automatically slows when the terminal is blurred, + * so consumers don't need to handle focus state. + */ +export function useAnimationFrame( + intervalMs: number | null = 16, +): [ref: (element: DOMElement | null) => void, time: number] { + const clock = useContext(ClockContext) + const [viewportRef, { isVisible }] = useTerminalViewport() + const [time, setTime] = useState(() => clock?.now() ?? 0) + + const active = isVisible && intervalMs !== null + + useEffect(() => { + if (!clock || !active) return + + let lastUpdate = clock.now() + + const onChange = (): void => { + const now = clock.now() + if (now - lastUpdate >= intervalMs!) { + lastUpdate = now + setTime(now) + } + } + + // keepAlive: true — visible animations drive the clock + return clock.subscribe(onChange, true) + }, [clock, intervalMs, active]) + + return [viewportRef, time] +} diff --git a/ink/hooks/use-app.ts b/ink/hooks/use-app.ts new file mode 100644 index 0000000..5545f35 --- /dev/null +++ b/ink/hooks/use-app.ts @@ -0,0 +1,8 @@ +import { useContext } from 'react' +import AppContext from '../components/AppContext.js' + +/** + * `useApp` is a React hook, which exposes a method to manually exit the app (unmount). + */ +const useApp = () => useContext(AppContext) +export default useApp diff --git a/ink/hooks/use-declared-cursor.ts b/ink/hooks/use-declared-cursor.ts new file mode 100644 index 0000000..e49668b --- /dev/null +++ b/ink/hooks/use-declared-cursor.ts @@ -0,0 +1,73 @@ +import { useCallback, useContext, useLayoutEffect, useRef } from 'react' +import CursorDeclarationContext from '../components/CursorDeclarationContext.js' +import type { DOMElement } from '../dom.js' + +/** + * Declares where the terminal cursor should be parked after each frame. + * + * Terminal emulators render IME preedit text at the physical cursor + * position, and screen readers / screen magnifiers track the native + * cursor — so parking it at the text input's caret makes CJK input + * appear inline and lets accessibility tools follow the input. + * + * Returns a ref callback to attach to the Box that contains the input. + * The declared (line, column) is interpreted relative to that Box's + * nodeCache rect (populated by renderNodeToOutput). + * + * Timing: Both ref attach and useLayoutEffect fire in React's layout + * phase — after resetAfterCommit calls scheduleRender. scheduleRender + * defers onRender via queueMicrotask, so onRender runs AFTER layout + * effects commit and reads the fresh declaration on the first frame + * (no one-keystroke lag). Test env uses onImmediateRender (synchronous, + * no microtask), so tests compensate by calling ink.onRender() + * explicitly after render. + */ +export function useDeclaredCursor({ + line, + column, + active, +}: { + line: number + column: number + active: boolean +}): (element: DOMElement | null) => void { + const setCursorDeclaration = useContext(CursorDeclarationContext) + const nodeRef = useRef(null) + + const setNode = useCallback((node: DOMElement | null) => { + nodeRef.current = node + }, []) + + // When active, set unconditionally. When inactive, clear conditionally + // (only if the currently-declared node is ours). The node-identity check + // handles two hazards: + // 1. A memo()ized active instance elsewhere (e.g. the search input in + // a memo'd Footer) doesn't re-render this commit — an inactive + // instance re-rendering here must not clobber it. + // 2. Sibling handoff (menu focus moving between list items) — when + // focus moves opposite to sibling order, the newly-inactive item's + // effect runs AFTER the newly-active item's set. Without the node + // check it would clobber. + // No dep array: must re-declare every commit so the active instance + // re-claims the declaration after another instance's unmount-cleanup or + // sibling handoff nulls it. + useLayoutEffect(() => { + const node = nodeRef.current + if (active && node) { + setCursorDeclaration({ relativeX: column, relativeY: line, node }) + } else { + setCursorDeclaration(null, node) + } + }) + + // Clear on unmount (conditionally — another instance may own by then). + // Separate effect with empty deps so cleanup only fires once — not on + // every line/column change, which would transiently null between commits. + useLayoutEffect(() => { + return () => { + setCursorDeclaration(null, nodeRef.current) + } + }, [setCursorDeclaration]) + + return setNode +} diff --git a/ink/hooks/use-input.ts b/ink/hooks/use-input.ts new file mode 100644 index 0000000..7cf75b3 --- /dev/null +++ b/ink/hooks/use-input.ts @@ -0,0 +1,92 @@ +import { useEffect, useLayoutEffect } from 'react' +import { useEventCallback } from 'usehooks-ts' +import type { InputEvent, Key } from '../events/input-event.js' +import useStdin from './use-stdin.js' + +type Handler = (input: string, key: Key, event: InputEvent) => void + +type Options = { + /** + * Enable or disable capturing of user input. + * Useful when there are multiple useInput hooks used at once to avoid handling the same input several times. + * + * @default true + */ + isActive?: boolean +} + +/** + * This hook is used for handling user input. + * It's a more convenient alternative to using `StdinContext` and listening to `data` events. + * The callback you pass to `useInput` is called for each character when user enters any input. + * However, if user pastes text and it's more than one character, the callback will be called only once and the whole string will be passed as `input`. + * + * ``` + * import {useInput} from 'ink'; + * + * const UserInput = () => { + * useInput((input, key) => { + * if (input === 'q') { + * // Exit program + * } + * + * if (key.leftArrow) { + * // Left arrow key pressed + * } + * }); + * + * return … + * }; + * ``` + */ +const useInput = (inputHandler: Handler, options: Options = {}) => { + const { setRawMode, internal_exitOnCtrlC, internal_eventEmitter } = useStdin() + + // useLayoutEffect (not useEffect) so that raw mode is enabled synchronously + // during React's commit phase, before render() returns. With useEffect, raw + // mode setup is deferred to the next event loop tick via React's scheduler, + // leaving the terminal in cooked mode — keystrokes echo and the cursor is + // visible until the effect fires. + useLayoutEffect(() => { + if (options.isActive === false) { + return + } + + setRawMode(true) + + return () => { + setRawMode(false) + } + }, [options.isActive, setRawMode]) + + // Register the listener once on mount so its slot in the EventEmitter's + // listener array is stable. If isActive were in the effect's deps, the + // listener would re-append on false→true, moving it behind listeners + // that registered while it was inactive — breaking + // stopImmediatePropagation() ordering. useEventCallback keeps the + // reference stable while reading latest isActive/inputHandler from + // closure (it syncs via useLayoutEffect, so it's compiler-safe). + const handleData = useEventCallback((event: InputEvent) => { + if (options.isActive === false) { + return + } + const { input, key } = event + + // If app is not supposed to exit on Ctrl+C, then let input listener handle it + // Note: discreteUpdates is called at the App level when emitting events, + // so all listeners are already within a high-priority update context. + if (!(input === 'c' && key.ctrl) || !internal_exitOnCtrlC) { + inputHandler(input, key, event) + } + }) + + useEffect(() => { + internal_eventEmitter?.on('input', handleData) + + return () => { + internal_eventEmitter?.removeListener('input', handleData) + } + }, [internal_eventEmitter, handleData]) +} + +export default useInput diff --git a/ink/hooks/use-interval.ts b/ink/hooks/use-interval.ts new file mode 100644 index 0000000..49c3ee6 --- /dev/null +++ b/ink/hooks/use-interval.ts @@ -0,0 +1,67 @@ +import { useContext, useEffect, useRef, useState } from 'react' +import { ClockContext } from '../components/ClockContext.js' + +/** + * Returns the clock time, updating at the given interval. + * Subscribes as non-keepAlive — won't keep the clock alive on its own, + * but updates whenever a keepAlive subscriber (e.g. the spinner) + * is driving the clock. + * + * Use this to drive pure time-based computations (shimmer position, + * frame index) from the shared clock. + */ +export function useAnimationTimer(intervalMs: number): number { + const clock = useContext(ClockContext) + const [time, setTime] = useState(() => clock?.now() ?? 0) + + useEffect(() => { + if (!clock) return + + let lastUpdate = clock.now() + + const onChange = (): void => { + const now = clock.now() + if (now - lastUpdate >= intervalMs) { + lastUpdate = now + setTime(now) + } + } + + return clock.subscribe(onChange, false) + }, [clock, intervalMs]) + + return time +} + +/** + * Interval hook backed by the shared Clock. + * + * Unlike `useInterval` from `usehooks-ts` (which creates its own setInterval), + * this piggybacks on the single shared clock so all timers consolidate into + * one wake-up. Pass `null` for intervalMs to pause. + */ +export function useInterval( + callback: () => void, + intervalMs: number | null, +): void { + const callbackRef = useRef(callback) + callbackRef.current = callback + + const clock = useContext(ClockContext) + + useEffect(() => { + if (!clock || intervalMs === null) return + + let lastUpdate = clock.now() + + const onChange = (): void => { + const now = clock.now() + if (now - lastUpdate >= intervalMs) { + lastUpdate = now + callbackRef.current() + } + } + + return clock.subscribe(onChange, false) + }, [clock, intervalMs]) +} diff --git a/ink/hooks/use-search-highlight.ts b/ink/hooks/use-search-highlight.ts new file mode 100644 index 0000000..ce9fc36 --- /dev/null +++ b/ink/hooks/use-search-highlight.ts @@ -0,0 +1,53 @@ +import { useContext, useMemo } from 'react' +import StdinContext from '../components/StdinContext.js' +import type { DOMElement } from '../dom.js' +import instances from '../instances.js' +import type { MatchPosition } from '../render-to-screen.js' + +/** + * Set the search highlight query on the Ink instance. Non-empty → all + * visible occurrences are inverted on the next frame (SGR 7, screen-buffer + * overlay, same damage machinery as selection). Empty → clears. + * + * This is a screen-space highlight — it matches the RENDERED text, not the + * source message text. Works for anything visible (bash output, file paths, + * error messages) regardless of where it came from in the message tree. A + * query that matched in source but got truncated/ellipsized in rendering + * won't highlight; that's acceptable — we highlight what you see. + */ +export function useSearchHighlight(): { + setQuery: (query: string) => void + /** Paint an existing DOM subtree (from the MAIN tree) to a fresh + * Screen at its natural height, scan. Element-relative positions + * (row 0 = element top). Zero context duplication — the element + * IS the one built with all real providers. */ + scanElement: (el: DOMElement) => MatchPosition[] + /** Position-based CURRENT highlight. Every frame writes yellow at + * positions[currentIdx] + rowOffset. The scan-highlight (inverse on + * all matches) still runs — this overlays on top. rowOffset tracks + * scroll; positions stay stable (message-relative). null clears. */ + setPositions: ( + state: { + positions: MatchPosition[] + rowOffset: number + currentIdx: number + } | null, + ) => void +} { + useContext(StdinContext) // anchor to App subtree for hook rules + const ink = instances.get(process.stdout) + return useMemo(() => { + if (!ink) { + return { + setQuery: () => {}, + scanElement: () => [], + setPositions: () => {}, + } + } + return { + setQuery: (query: string) => ink.setSearchHighlight(query), + scanElement: (el: DOMElement) => ink.scanElementSubtree(el), + setPositions: state => ink.setSearchPositions(state), + } + }, [ink]) +} diff --git a/ink/hooks/use-selection.ts b/ink/hooks/use-selection.ts new file mode 100644 index 0000000..f7e1d45 --- /dev/null +++ b/ink/hooks/use-selection.ts @@ -0,0 +1,104 @@ +import { useContext, useMemo, useSyncExternalStore } from 'react' +import StdinContext from '../components/StdinContext.js' +import instances from '../instances.js' +import { + type FocusMove, + type SelectionState, + shiftAnchor, +} from '../selection.js' + +/** + * Access to text selection operations on the Ink instance (fullscreen only). + * Returns no-op functions when fullscreen mode is disabled. + */ +export function useSelection(): { + copySelection: () => string + /** Copy without clearing the highlight (for copy-on-select). */ + copySelectionNoClear: () => string + clearSelection: () => void + hasSelection: () => boolean + /** Read the raw mutable selection state (for drag-to-scroll). */ + getState: () => SelectionState | null + /** Subscribe to selection mutations (start/update/finish/clear). */ + subscribe: (cb: () => void) => () => void + /** Shift the anchor row by dRow, clamped to [minRow, maxRow]. */ + shiftAnchor: (dRow: number, minRow: number, maxRow: number) => void + /** Shift anchor AND focus by dRow (keyboard scroll: whole selection + * tracks content). Clamped points get col reset to the full-width edge + * since their content was captured by captureScrolledRows. Reads + * screen.width from the ink instance for the col-reset boundary. */ + shiftSelection: (dRow: number, minRow: number, maxRow: number) => void + /** Keyboard selection extension (shift+arrow): move focus, anchor fixed. + * Left/right wrap across rows; up/down clamp at viewport edges. */ + moveFocus: (move: FocusMove) => void + /** Capture text from rows about to scroll out of the viewport (call + * BEFORE scrollBy so the screen buffer still has the outgoing rows). */ + captureScrolledRows: ( + firstRow: number, + lastRow: number, + side: 'above' | 'below', + ) => void + /** Set the selection highlight bg color (theme-piping; solid bg + * replaces the old SGR-7 inverse so syntax highlighting stays readable + * under selection). Call once on mount + whenever theme changes. */ + setSelectionBgColor: (color: string) => void +} { + // Look up the Ink instance via stdout — same pattern as instances map. + // StdinContext is available (it's always provided), and the Ink instance + // is keyed by stdout which we can get from process.stdout since there's + // only one Ink instance per process in practice. + useContext(StdinContext) // anchor to App subtree for hook rules + const ink = instances.get(process.stdout) + // Memoize so callers can safely use the return value in dependency arrays. + // ink is a singleton per stdout — stable across renders. + return useMemo(() => { + if (!ink) { + return { + copySelection: () => '', + copySelectionNoClear: () => '', + clearSelection: () => {}, + hasSelection: () => false, + getState: () => null, + subscribe: () => () => {}, + shiftAnchor: () => {}, + shiftSelection: () => {}, + moveFocus: () => {}, + captureScrolledRows: () => {}, + setSelectionBgColor: () => {}, + } + } + return { + copySelection: () => ink.copySelection(), + copySelectionNoClear: () => ink.copySelectionNoClear(), + clearSelection: () => ink.clearTextSelection(), + hasSelection: () => ink.hasTextSelection(), + getState: () => ink.selection, + subscribe: (cb: () => void) => ink.subscribeToSelectionChange(cb), + shiftAnchor: (dRow: number, minRow: number, maxRow: number) => + shiftAnchor(ink.selection, dRow, minRow, maxRow), + shiftSelection: (dRow, minRow, maxRow) => + ink.shiftSelectionForScroll(dRow, minRow, maxRow), + moveFocus: (move: FocusMove) => ink.moveSelectionFocus(move), + captureScrolledRows: (firstRow, lastRow, side) => + ink.captureScrolledRows(firstRow, lastRow, side), + setSelectionBgColor: (color: string) => ink.setSelectionBgColor(color), + } + }, [ink]) +} + +const NO_SUBSCRIBE = () => () => {} +const ALWAYS_FALSE = () => false + +/** + * Reactive selection-exists state. Re-renders the caller when a text + * selection is created or cleared. Always returns false outside + * fullscreen mode (selection is only available in alt-screen). + */ +export function useHasSelection(): boolean { + useContext(StdinContext) + const ink = instances.get(process.stdout) + return useSyncExternalStore( + ink ? ink.subscribeToSelectionChange : NO_SUBSCRIBE, + ink ? ink.hasTextSelection : ALWAYS_FALSE, + ) +} diff --git a/ink/hooks/use-stdin.ts b/ink/hooks/use-stdin.ts new file mode 100644 index 0000000..997f3c3 --- /dev/null +++ b/ink/hooks/use-stdin.ts @@ -0,0 +1,8 @@ +import { useContext } from 'react' +import StdinContext from '../components/StdinContext.js' + +/** + * `useStdin` is a React hook, which exposes stdin stream. + */ +const useStdin = () => useContext(StdinContext) +export default useStdin diff --git a/ink/hooks/use-tab-status.ts b/ink/hooks/use-tab-status.ts new file mode 100644 index 0000000..be60142 --- /dev/null +++ b/ink/hooks/use-tab-status.ts @@ -0,0 +1,72 @@ +import { useContext, useEffect, useRef } from 'react' +import { + CLEAR_TAB_STATUS, + supportsTabStatus, + tabStatus, + wrapForMultiplexer, +} from '../termio/osc.js' +import type { Color } from '../termio/types.js' +import { TerminalWriteContext } from '../useTerminalNotification.js' + +export type TabStatusKind = 'idle' | 'busy' | 'waiting' + +const rgb = (r: number, g: number, b: number): Color => ({ + type: 'rgb', + r, + g, + b, +}) + +// Per the OSC 21337 usage guide's suggested mapping. +const TAB_STATUS_PRESETS: Record< + TabStatusKind, + { indicator: Color; status: string; statusColor: Color } +> = { + idle: { + indicator: rgb(0, 215, 95), + status: 'Idle', + statusColor: rgb(136, 136, 136), + }, + busy: { + indicator: rgb(255, 149, 0), + status: 'Working…', + statusColor: rgb(255, 149, 0), + }, + waiting: { + indicator: rgb(95, 135, 255), + status: 'Waiting', + statusColor: rgb(95, 135, 255), + }, +} + +/** + * Declaratively set the tab-status indicator (OSC 21337). + * + * Emits a colored dot + short status text to the tab sidebar. Terminals + * that don't support OSC 21337 discard the sequence silently, so this is + * safe to call unconditionally. Wrapped for tmux/screen passthrough. + * + * Pass `null` to opt out. If a status was previously set, transitioning to + * `null` emits CLEAR_TAB_STATUS so toggling off mid-session doesn't leave + * a stale dot. Process-exit cleanup is handled by ink.tsx's unmount path. + */ +export function useTabStatus(kind: TabStatusKind | null): void { + const writeRaw = useContext(TerminalWriteContext) + const prevKindRef = useRef(null) + + useEffect(() => { + // When kind transitions from non-null to null (e.g. user toggles off + // showStatusInTerminalTab mid-session), clear the stale dot. + if (kind === null) { + if (prevKindRef.current !== null && writeRaw && supportsTabStatus()) { + writeRaw(wrapForMultiplexer(CLEAR_TAB_STATUS)) + } + prevKindRef.current = null + return + } + + prevKindRef.current = kind + if (!writeRaw || !supportsTabStatus()) return + writeRaw(wrapForMultiplexer(tabStatus(TAB_STATUS_PRESETS[kind]))) + }, [kind, writeRaw]) +} diff --git a/ink/hooks/use-terminal-focus.ts b/ink/hooks/use-terminal-focus.ts new file mode 100644 index 0000000..b717f7b --- /dev/null +++ b/ink/hooks/use-terminal-focus.ts @@ -0,0 +1,16 @@ +import { useContext } from 'react' +import TerminalFocusContext from '../components/TerminalFocusContext.js' + +/** + * Hook to check if the terminal has focus. + * + * Uses DECSET 1004 focus reporting - the terminal sends escape sequences + * when it gains or loses focus. These are handled automatically + * by Ink and filtered from useInput. + * + * @returns true if the terminal is focused (or focus state is unknown) + */ +export function useTerminalFocus(): boolean { + const { isTerminalFocused } = useContext(TerminalFocusContext) + return isTerminalFocused +} diff --git a/ink/hooks/use-terminal-title.ts b/ink/hooks/use-terminal-title.ts new file mode 100644 index 0000000..d820cd7 --- /dev/null +++ b/ink/hooks/use-terminal-title.ts @@ -0,0 +1,31 @@ +import { useContext, useEffect } from 'react' +import stripAnsi from 'strip-ansi' +import { OSC, osc } from '../termio/osc.js' +import { TerminalWriteContext } from '../useTerminalNotification.js' + +/** + * Declaratively set the terminal tab/window title. + * + * Pass a string to set the title. ANSI escape sequences are stripped + * automatically so callers don't need to know about terminal encoding. + * Pass `null` to opt out — the hook becomes a no-op and leaves the + * terminal title untouched. + * + * On Windows, uses `process.title` (classic conhost doesn't support OSC). + * Elsewhere, writes OSC 0 (set title+icon) via Ink's stdout. + */ +export function useTerminalTitle(title: string | null): void { + const writeRaw = useContext(TerminalWriteContext) + + useEffect(() => { + if (title === null || !writeRaw) return + + const clean = stripAnsi(title) + + if (process.platform === 'win32') { + process.title = clean + } else { + writeRaw(osc(OSC.SET_TITLE_AND_ICON, clean)) + } + }, [title, writeRaw]) +} diff --git a/ink/hooks/use-terminal-viewport.ts b/ink/hooks/use-terminal-viewport.ts new file mode 100644 index 0000000..91193bf --- /dev/null +++ b/ink/hooks/use-terminal-viewport.ts @@ -0,0 +1,96 @@ +import { useCallback, useContext, useLayoutEffect, useRef } from 'react' +import { TerminalSizeContext } from '../components/TerminalSizeContext.js' +import type { DOMElement } from '../dom.js' + +type ViewportEntry = { + /** + * Whether the element is currently within the terminal viewport + */ + isVisible: boolean +} + +/** + * Hook to detect if a component is within the terminal viewport. + * + * Returns a callback ref and a viewport entry object. + * Attach the ref to the component you want to track. + * + * The entry is updated during the layout phase (useLayoutEffect) so callers + * always read fresh values during render. Visibility changes do NOT trigger + * re-renders on their own — callers that re-render for other reasons (e.g. + * animation ticks, state changes) will pick up the latest value naturally. + * This avoids infinite update loops when combined with other layout effects + * that also call setState. + * + * @example + * const [ref, entry] = useTerminalViewport() + * return ... + */ +export function useTerminalViewport(): [ + ref: (element: DOMElement | null) => void, + entry: ViewportEntry, +] { + const terminalSize = useContext(TerminalSizeContext) + const elementRef = useRef(null) + const entryRef = useRef({ isVisible: true }) + + const setElement = useCallback((el: DOMElement | null) => { + elementRef.current = el + }, []) + + // Runs on every render because yoga layout values can change + // without React being aware. Only updates the ref — no setState + // to avoid cascading re-renders during the commit phase. + // Walks the DOM ancestor chain fresh each time to avoid holding stale + // references after yoga tree rebuilds. + useLayoutEffect(() => { + const element = elementRef.current + if (!element?.yogaNode || !terminalSize) { + return + } + + const height = element.yogaNode.getComputedHeight() + const rows = terminalSize.rows + + // Walk the DOM parent chain (not yoga.getParent()) so we can detect + // scroll containers and subtract their scrollTop. Yoga computes layout + // positions without scroll offset — scrollTop is applied at render time. + // Without this, an element inside a ScrollBox whose yoga position exceeds + // terminalRows would be considered offscreen even when scrolled into view + // (e.g., the spinner in fullscreen mode after enough messages accumulate). + let absoluteTop = element.yogaNode.getComputedTop() + let parent: DOMElement | undefined = element.parentNode + let root = element.yogaNode + while (parent) { + if (parent.yogaNode) { + absoluteTop += parent.yogaNode.getComputedTop() + root = parent.yogaNode + } + // scrollTop is only ever set on scroll containers (by ScrollBox + renderer). + // Non-scroll nodes have undefined scrollTop → falsy fast-path. + if (parent.scrollTop) absoluteTop -= parent.scrollTop + parent = parent.parentNode + } + + // Only the root's height matters + const screenHeight = root.getComputedHeight() + + const bottom = absoluteTop + height + // When content overflows the viewport (screenHeight > rows), the + // cursor-restore at frame end scrolls one extra row into scrollback. + // log-update.ts accounts for this with scrollbackRows = viewportY + 1. + // We must match, otherwise an element at the boundary is considered + // "visible" here (animation keeps ticking) but its row is treated as + // scrollback by log-update (content change → full reset → flicker). + const cursorRestoreScroll = screenHeight > rows ? 1 : 0 + const viewportY = Math.max(0, screenHeight - rows) + cursorRestoreScroll + const viewportBottom = viewportY + rows + const visible = bottom > viewportY && absoluteTop < viewportBottom + + if (visible !== entryRef.current.isVisible) { + entryRef.current = { isVisible: visible } + } + }) + + return [setElement, entryRef.current] +} diff --git a/ink/ink.tsx b/ink/ink.tsx new file mode 100644 index 0000000..1cf479d --- /dev/null +++ b/ink/ink.tsx @@ -0,0 +1,1723 @@ +import autoBind from 'auto-bind'; +import { closeSync, constants as fsConstants, openSync, readSync, writeSync } from 'fs'; +import noop from 'lodash-es/noop.js'; +import throttle from 'lodash-es/throttle.js'; +import React, { type ReactNode } from 'react'; +import type { FiberRoot } from 'react-reconciler'; +import { ConcurrentRoot } from 'react-reconciler/constants.js'; +import { onExit } from 'signal-exit'; +import { flushInteractionTime } from 'src/bootstrap/state.js'; +import { getYogaCounters } from 'src/native-ts/yoga-layout/index.js'; +import { logForDebugging } from 'src/utils/debug.js'; +import { logError } from 'src/utils/log.js'; +import { format } from 'util'; +import { colorize } from './colorize.js'; +import App from './components/App.js'; +import type { CursorDeclaration, CursorDeclarationSetter } from './components/CursorDeclarationContext.js'; +import { FRAME_INTERVAL_MS } from './constants.js'; +import * as dom from './dom.js'; +import { KeyboardEvent } from './events/keyboard-event.js'; +import { FocusManager } from './focus.js'; +import { emptyFrame, type Frame, type FrameEvent } from './frame.js'; +import { dispatchClick, dispatchHover } from './hit-test.js'; +import instances from './instances.js'; +import { LogUpdate } from './log-update.js'; +import { nodeCache } from './node-cache.js'; +import { optimize } from './optimizer.js'; +import Output from './output.js'; +import type { ParsedKey } from './parse-keypress.js'; +import reconciler, { dispatcher, getLastCommitMs, getLastYogaMs, isDebugRepaintsEnabled, recordYogaMs, resetProfileCounters } from './reconciler.js'; +import renderNodeToOutput, { consumeFollowScroll, didLayoutShift } from './render-node-to-output.js'; +import { applyPositionedHighlight, type MatchPosition, scanPositions } from './render-to-screen.js'; +import createRenderer, { type Renderer } from './renderer.js'; +import { CellWidth, CharPool, cellAt, createScreen, HyperlinkPool, isEmptyCellAt, migrateScreenPools, StylePool } from './screen.js'; +import { applySearchHighlight } from './searchHighlight.js'; +import { applySelectionOverlay, captureScrolledRows, clearSelection, createSelectionState, extendSelection, type FocusMove, findPlainTextUrlAt, getSelectedText, hasSelection, moveFocus, type SelectionState, selectLineAt, selectWordAt, shiftAnchor, shiftSelection, shiftSelectionForFollow, startSelection, updateSelection } from './selection.js'; +import { SYNC_OUTPUT_SUPPORTED, supportsExtendedKeys, type Terminal, writeDiffToTerminal } from './terminal.js'; +import { CURSOR_HOME, cursorMove, cursorPosition, DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, ENABLE_KITTY_KEYBOARD, ENABLE_MODIFY_OTHER_KEYS, ERASE_SCREEN } from './termio/csi.js'; +import { DBP, DFE, DISABLE_MOUSE_TRACKING, ENABLE_MOUSE_TRACKING, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, SHOW_CURSOR } from './termio/dec.js'; +import { CLEAR_ITERM2_PROGRESS, CLEAR_TAB_STATUS, setClipboard, supportsTabStatus, wrapForMultiplexer } from './termio/osc.js'; +import { TerminalWriteProvider } from './useTerminalNotification.js'; + +// Alt-screen: renderer.ts sets cursor.visible = !isTTY || screen.height===0, +// which is always false in alt-screen (TTY + content fills screen). +// Reusing a frozen object saves 1 allocation per frame. +const ALT_SCREEN_ANCHOR_CURSOR = Object.freeze({ + x: 0, + y: 0, + visible: false +}); +const CURSOR_HOME_PATCH = Object.freeze({ + type: 'stdout' as const, + content: CURSOR_HOME +}); +const ERASE_THEN_HOME_PATCH = Object.freeze({ + type: 'stdout' as const, + content: ERASE_SCREEN + CURSOR_HOME +}); + +// Cached per-Ink-instance, invalidated on resize. frame.cursor.y for +// alt-screen is always terminalRows - 1 (renderer.ts). +function makeAltScreenParkPatch(terminalRows: number) { + return Object.freeze({ + type: 'stdout' as const, + content: cursorPosition(terminalRows, 1) + }); +} +export type Options = { + stdout: NodeJS.WriteStream; + stdin: NodeJS.ReadStream; + stderr: NodeJS.WriteStream; + exitOnCtrlC: boolean; + patchConsole: boolean; + waitUntilExit?: () => Promise; + onFrame?: (event: FrameEvent) => void; +}; +export default class Ink { + private readonly log: LogUpdate; + private readonly terminal: Terminal; + private scheduleRender: (() => void) & { + cancel?: () => void; + }; + // Ignore last render after unmounting a tree to prevent empty output before exit + private isUnmounted = false; + private isPaused = false; + private readonly container: FiberRoot; + private rootNode: dom.DOMElement; + readonly focusManager: FocusManager; + private renderer: Renderer; + private readonly stylePool: StylePool; + private charPool: CharPool; + private hyperlinkPool: HyperlinkPool; + private exitPromise?: Promise; + private restoreConsole?: () => void; + private restoreStderr?: () => void; + private readonly unsubscribeTTYHandlers?: () => void; + private terminalColumns: number; + private terminalRows: number; + private currentNode: ReactNode = null; + private frontFrame: Frame; + private backFrame: Frame; + private lastPoolResetTime = performance.now(); + private drainTimer: ReturnType | null = null; + private lastYogaCounters: { + ms: number; + visited: number; + measured: number; + cacheHits: number; + live: number; + } = { + ms: 0, + visited: 0, + measured: 0, + cacheHits: 0, + live: 0 + }; + private altScreenParkPatch: Readonly<{ + type: 'stdout'; + content: string; + }>; + // Text selection state (alt-screen only). Owned here so the overlay + // pass in onRender can read it and App.tsx can update it from mouse + // events. Public so instances.get() callers can access. + readonly selection: SelectionState = createSelectionState(); + // Search highlight query (alt-screen only). Setter below triggers + // scheduleRender; applySearchHighlight in onRender inverts matching cells. + private searchHighlightQuery = ''; + // Position-based highlight. VML scans positions ONCE (via + // scanElementSubtree, when the target message is mounted), stores them + // message-relative, sets this for every-frame apply. rowOffset = + // message's current screen-top. currentIdx = which position is + // "current" (yellow). null clears. Positions are known upfront — + // navigation is index arithmetic, no scan-feedback loop. + private searchPositions: { + positions: MatchPosition[]; + rowOffset: number; + currentIdx: number; + } | null = null; + // React-land subscribers for selection state changes (useHasSelection). + // Fired alongside the terminal repaint whenever the selection mutates + // so UI (e.g. footer hints) can react to selection appearing/clearing. + private readonly selectionListeners = new Set<() => void>(); + // DOM nodes currently under the pointer (mode-1003 motion). Held here + // so App.tsx's handleMouseEvent is stateless — dispatchHover diffs + // against this set and mutates it in place. + private readonly hoveredNodes = new Set(); + // Set by via setAltScreenActive(). Controls the + // renderer's cursor.y clamping (keeps cursor in-viewport to avoid + // LF-induced scroll when screen.height === terminalRows) and gates + // alt-screen-aware SIGCONT/resize/unmount handling. + private altScreenActive = false; + // Set alongside altScreenActive so SIGCONT resume knows whether to + // re-enable mouse tracking (not all uses want it). + private altScreenMouseTracking = false; + // True when the previous frame's screen buffer cannot be trusted for + // blit — selection overlay mutated it, resetFramesForAltScreen() + // replaced it with blanks, or forceRedraw() reset it to 0×0. Forces + // one full-render frame; steady-state frames after clear it and regain + // the blit + narrow-damage fast path. + private prevFrameContaminated = false; + // Set by handleResize: prepend ERASE_SCREEN to the next onRender's patches + // INSIDE the BSU/ESU block so clear+paint is atomic. Writing ERASE_SCREEN + // synchronously in handleResize would leave the screen blank for the ~80ms + // render() takes; deferring into the atomic block means old content stays + // visible until the new frame is fully ready. + private needsEraseBeforePaint = false; + // Native cursor positioning: a component (via useDeclaredCursor) declares + // where the terminal cursor should be parked after each frame. Terminal + // emulators render IME preedit text at the physical cursor position, and + // screen readers / screen magnifiers track it — so parking at the text + // input's caret makes CJK input appear inline and lets a11y tools follow. + private cursorDeclaration: CursorDeclaration | null = null; + // Main-screen: physical cursor position after the declared-cursor move, + // tracked separately from frame.cursor (which must stay at content-bottom + // for log-update's relative-move invariants). Alt-screen doesn't need + // this — every frame begins with CSI H. null = no move emitted last frame. + private displayCursor: { + x: number; + y: number; + } | null = null; + constructor(private readonly options: Options) { + autoBind(this); + if (this.options.patchConsole) { + this.restoreConsole = this.patchConsole(); + this.restoreStderr = this.patchStderr(); + } + this.terminal = { + stdout: options.stdout, + stderr: options.stderr + }; + this.terminalColumns = options.stdout.columns || 80; + this.terminalRows = options.stdout.rows || 24; + this.altScreenParkPatch = makeAltScreenParkPatch(this.terminalRows); + this.stylePool = new StylePool(); + this.charPool = new CharPool(); + this.hyperlinkPool = new HyperlinkPool(); + this.frontFrame = emptyFrame(this.terminalRows, this.terminalColumns, this.stylePool, this.charPool, this.hyperlinkPool); + this.backFrame = emptyFrame(this.terminalRows, this.terminalColumns, this.stylePool, this.charPool, this.hyperlinkPool); + this.log = new LogUpdate({ + isTTY: options.stdout.isTTY as boolean | undefined || false, + stylePool: this.stylePool + }); + + // scheduleRender is called from the reconciler's resetAfterCommit, which + // runs BEFORE React's layout phase (ref attach + useLayoutEffect). Any + // state set in layout effects — notably the cursorDeclaration from + // useDeclaredCursor — would lag one commit behind if we rendered + // synchronously. Deferring to a microtask runs onRender after layout + // effects have committed, so the native cursor tracks the caret without + // a one-keystroke lag. Same event-loop tick, so throughput is unchanged. + // Test env uses onImmediateRender (direct onRender, no throttle) so + // existing synchronous lastFrame() tests are unaffected. + const deferredRender = (): void => queueMicrotask(this.onRender); + this.scheduleRender = throttle(deferredRender, FRAME_INTERVAL_MS, { + leading: true, + trailing: true + }); + + // Ignore last render after unmounting a tree to prevent empty output before exit + this.isUnmounted = false; + + // Unmount when process exits + this.unsubscribeExit = onExit(this.unmount, { + alwaysLast: false + }); + if (options.stdout.isTTY) { + options.stdout.on('resize', this.handleResize); + process.on('SIGCONT', this.handleResume); + this.unsubscribeTTYHandlers = () => { + options.stdout.off('resize', this.handleResize); + process.off('SIGCONT', this.handleResume); + }; + } + this.rootNode = dom.createNode('ink-root'); + this.focusManager = new FocusManager((target, event) => dispatcher.dispatchDiscrete(target, event)); + this.rootNode.focusManager = this.focusManager; + this.renderer = createRenderer(this.rootNode, this.stylePool); + this.rootNode.onRender = this.scheduleRender; + this.rootNode.onImmediateRender = this.onRender; + this.rootNode.onComputeLayout = () => { + // Calculate layout during React's commit phase so useLayoutEffect hooks + // have access to fresh layout data + // Guard against accessing freed Yoga nodes after unmount + if (this.isUnmounted) { + return; + } + if (this.rootNode.yogaNode) { + const t0 = performance.now(); + this.rootNode.yogaNode.setWidth(this.terminalColumns); + this.rootNode.yogaNode.calculateLayout(this.terminalColumns); + const ms = performance.now() - t0; + recordYogaMs(ms); + const c = getYogaCounters(); + this.lastYogaCounters = { + ms, + ...c + }; + } + }; + + // @ts-expect-error @types/react-reconciler@0.32.3 declares 11 args with transitionCallbacks, + // but react-reconciler 0.33.0 source only accepts 10 args (no transitionCallbacks) + this.container = reconciler.createContainer(this.rootNode, ConcurrentRoot, null, false, null, 'id', noop, + // onUncaughtError + noop, + // onCaughtError + noop, + // onRecoverableError + noop // onDefaultTransitionIndicator + ); + if ("production" === 'development') { + reconciler.injectIntoDevTools({ + bundleType: 0, + // Reporting React DOM's version, not Ink's + // See https://github.com/facebook/react/issues/16666#issuecomment-532639905 + version: '16.13.1', + rendererPackageName: 'ink' + }); + } + } + private handleResume = () => { + if (!this.options.stdout.isTTY) { + return; + } + + // Alt screen: after SIGCONT, content is stale (shell may have written + // to main screen, switching focus away) and mouse tracking was + // disabled by handleSuspend. + if (this.altScreenActive) { + this.reenterAltScreen(); + return; + } + + // Main screen: start fresh to prevent clobbering terminal content + this.frontFrame = emptyFrame(this.frontFrame.viewport.height, this.frontFrame.viewport.width, this.stylePool, this.charPool, this.hyperlinkPool); + this.backFrame = emptyFrame(this.backFrame.viewport.height, this.backFrame.viewport.width, this.stylePool, this.charPool, this.hyperlinkPool); + this.log.reset(); + // Physical cursor position is unknown after the shell took over during + // suspend. Clear displayCursor so the next frame's cursor preamble + // doesn't emit a relative move from a stale park position. + this.displayCursor = null; + }; + + // NOT debounced. A debounce opens a window where stdout.columns is NEW + // but this.terminalColumns/Yoga are OLD — any scheduleRender during that + // window (spinner, clock) makes log-update detect a width change and + // clear the screen, then the debounce fires and clears again (double + // blank→paint flicker). useVirtualScroll's height scaling already bounds + // the per-resize cost; synchronous handling keeps dimensions consistent. + private handleResize = () => { + const cols = this.options.stdout.columns || 80; + const rows = this.options.stdout.rows || 24; + // Terminals often emit 2+ resize events for one user action (window + // settling). Same-dimension events are no-ops; skip to avoid redundant + // frame resets and renders. + if (cols === this.terminalColumns && rows === this.terminalRows) return; + this.terminalColumns = cols; + this.terminalRows = rows; + this.altScreenParkPatch = makeAltScreenParkPatch(this.terminalRows); + + // Alt screen: reset frame buffers so the next render repaints from + // scratch (prevFrameContaminated → every cell written, wrapped in + // BSU/ESU — old content stays visible until the new frame swaps + // atomically). Re-assert mouse tracking (some emulators reset it on + // resize). Do NOT write ENTER_ALT_SCREEN: iTerm2 treats ?1049h as a + // buffer clear even when already in alt — that's the blank flicker. + // Self-healing re-entry (if something kicked us out of alt) is handled + // by handleResume (SIGCONT) and the sleep-wake detector; resize itself + // doesn't exit alt-screen. Do NOT write ERASE_SCREEN: render() below + // can take ~80ms; erasing first leaves the screen blank that whole time. + if (this.altScreenActive && !this.isPaused && this.options.stdout.isTTY) { + if (this.altScreenMouseTracking) { + this.options.stdout.write(ENABLE_MOUSE_TRACKING); + } + this.resetFramesForAltScreen(); + this.needsEraseBeforePaint = true; + } + + // Re-render the React tree with updated props so the context value changes. + // React's commit phase will call onComputeLayout() to recalculate yoga layout + // with the new dimensions, then call onRender() to render the updated frame. + // We don't call scheduleRender() here because that would render before the + // layout is updated, causing a mismatch between viewport and content dimensions. + if (this.currentNode !== null) { + this.render(this.currentNode); + } + }; + resolveExitPromise: () => void = () => {}; + rejectExitPromise: (reason?: Error) => void = () => {}; + unsubscribeExit: () => void = () => {}; + + /** + * Pause Ink and hand the terminal over to an external TUI (e.g. git + * commit editor). In non-fullscreen mode this enters the alt screen; + * in fullscreen mode we're already in alt so we just clear it. + * Call `exitAlternateScreen()` when done to restore Ink. + */ + enterAlternateScreen(): void { + this.pause(); + this.suspendStdin(); + this.options.stdout.write( + // Disable extended key reporting first — editors that don't speak + // CSI-u (e.g. nano) show "Unknown sequence" for every Ctrl- if + // kitty/modifyOtherKeys stays active. exitAlternateScreen re-enables. + DISABLE_KITTY_KEYBOARD + DISABLE_MODIFY_OTHER_KEYS + (this.altScreenMouseTracking ? DISABLE_MOUSE_TRACKING : '') + ( + // disable mouse (no-op if off) + this.altScreenActive ? '' : '\x1b[?1049h') + + // enter alt (already in alt if fullscreen) + '\x1b[?1004l' + + // disable focus reporting + '\x1b[0m' + + // reset attributes + '\x1b[?25h' + + // show cursor + '\x1b[2J' + + // clear screen + '\x1b[H' // cursor home + ); + } + + /** + * Resume Ink after an external TUI handoff with a full repaint. + * In non-fullscreen mode this exits the alt screen back to main; + * in fullscreen mode we re-enter alt and clear + repaint. + * + * The re-enter matters: terminal editors (vim, nano, less) write + * smcup/rmcup (?1049h/?1049l), so even though we started in alt, + * the editor's rmcup on exit drops us to main screen. Without + * re-entering, the 2J below wipes the user's main-screen scrollback + * and subsequent renders land in main — native terminal scroll + * returns, fullscreen scroll is dead. + */ + exitAlternateScreen(): void { + this.options.stdout.write((this.altScreenActive ? ENTER_ALT_SCREEN : '') + + // re-enter alt — vim's rmcup dropped us to main + '\x1b[2J' + + // clear screen (now alt if fullscreen) + '\x1b[H' + ( + // cursor home + this.altScreenMouseTracking ? ENABLE_MOUSE_TRACKING : '') + ( + // re-enable mouse (skip if CLAUDE_CODE_DISABLE_MOUSE) + this.altScreenActive ? '' : '\x1b[?1049l') + + // exit alt (non-fullscreen only) + '\x1b[?25l' // hide cursor (Ink manages) + ); + this.resumeStdin(); + if (this.altScreenActive) { + this.resetFramesForAltScreen(); + } else { + this.repaint(); + } + this.resume(); + // Re-enable focus reporting and extended key reporting — terminal + // editors (vim, nano, etc.) write their own modifyOtherKeys level on + // entry and reset it on exit, leaving us unable to distinguish + // ctrl+shift+ from ctrl+. Pop-before-push keeps the + // Kitty stack balanced (a well-behaved editor restores our entry, so + // without the pop we'd accumulate depth on each editor round-trip). + this.options.stdout.write('\x1b[?1004h' + (supportsExtendedKeys() ? DISABLE_KITTY_KEYBOARD + ENABLE_KITTY_KEYBOARD + ENABLE_MODIFY_OTHER_KEYS : '')); + } + onRender() { + if (this.isUnmounted || this.isPaused) { + return; + } + // Entering a render cancels any pending drain tick — this render will + // handle the drain (and re-schedule below if needed). Prevents a + // wheel-event-triggered render AND a drain-timer render both firing. + if (this.drainTimer !== null) { + clearTimeout(this.drainTimer); + this.drainTimer = null; + } + + // Flush deferred interaction-time update before rendering so we call + // Date.now() at most once per frame instead of once per keypress. + // Done before the render to avoid dirtying state that would trigger + // an extra React re-render cycle. + flushInteractionTime(); + const renderStart = performance.now(); + const terminalWidth = this.options.stdout.columns || 80; + const terminalRows = this.options.stdout.rows || 24; + const frame = this.renderer({ + frontFrame: this.frontFrame, + backFrame: this.backFrame, + isTTY: this.options.stdout.isTTY, + terminalWidth, + terminalRows, + altScreen: this.altScreenActive, + prevFrameContaminated: this.prevFrameContaminated + }); + const rendererMs = performance.now() - renderStart; + + // Sticky/auto-follow scrolled the ScrollBox this frame. Translate the + // selection by the same delta so the highlight stays anchored to the + // TEXT (native terminal behavior — the selection walks up the screen + // as content scrolls, eventually clipping at the top). frontFrame + // still holds the PREVIOUS frame's screen (swap is at ~500 below), so + // captureScrolledRows reads the rows that are about to scroll out + // before they're overwritten — the text stays copyable until the + // selection scrolls entirely off. During drag, focus tracks the mouse + // (screen-local) so only anchor shifts — selection grows toward the + // mouse as the anchor walks up. After release, both ends are text- + // anchored and move as a block. + const follow = consumeFollowScroll(); + if (follow && this.selection.anchor && + // Only translate if the selection is ON scrollbox content. Selections + // in the footer/prompt/StickyPromptHeader are on static text — the + // scroll doesn't move what's under them. Without this guard, a + // footer selection would be shifted by -delta then clamped to + // viewportBottom, teleporting it into the scrollbox. Mirror the + // bounds check the deleted check() in ScrollKeybindingHandler had. + this.selection.anchor.row >= follow.viewportTop && this.selection.anchor.row <= follow.viewportBottom) { + const { + delta, + viewportTop, + viewportBottom + } = follow; + // captureScrolledRows and shift* are a pair: capture grabs rows about + // to scroll off, shift moves the selection endpoint so the same rows + // won't intersect again next frame. Capturing without shifting leaves + // the endpoint in place, so the SAME viewport rows re-intersect every + // frame and scrolledOffAbove grows without bound — getSelectedText + // then returns ever-growing text on each re-copy. Keep capture inside + // each shift branch so the pairing can't be broken by a new guard. + if (this.selection.isDragging) { + if (hasSelection(this.selection)) { + captureScrolledRows(this.selection, this.frontFrame.screen, viewportTop, viewportTop + delta - 1, 'above'); + } + shiftAnchor(this.selection, -delta, viewportTop, viewportBottom); + } else if ( + // Flag-3 guard: the anchor check above only proves ONE endpoint is + // on scrollbox content. A drag from row 3 (scrollbox) into the + // footer at row 6, then release, leaves focus outside the viewport + // — shiftSelectionForFollow would clamp it to viewportBottom, + // teleporting the highlight from static footer into the scrollbox. + // Symmetric check: require BOTH ends inside to translate. A + // straddling selection falls through to NEITHER shift NOR capture: + // the footer endpoint pins the selection, text scrolls away under + // the highlight, and getSelectedText reads the CURRENT screen + // contents — no accumulation. Dragging branch doesn't need this: + // shiftAnchor ignores focus, and the anchor DOES shift (so capture + // is correct there even when focus is in the footer). + !this.selection.focus || this.selection.focus.row >= viewportTop && this.selection.focus.row <= viewportBottom) { + if (hasSelection(this.selection)) { + captureScrolledRows(this.selection, this.frontFrame.screen, viewportTop, viewportTop + delta - 1, 'above'); + } + const cleared = shiftSelectionForFollow(this.selection, -delta, viewportTop, viewportBottom); + // Auto-clear (both ends overshot minRow) must notify React-land + // so useHasSelection re-renders and the footer copy/escape hint + // disappears. notifySelectionChange() would recurse into onRender; + // fire the listeners directly — they schedule a React update for + // LATER, they don't re-enter this frame. + if (cleared) for (const cb of this.selectionListeners) cb(); + } + } + + // Selection overlay: invert cell styles in the screen buffer itself, + // so the diff picks up selection as ordinary cell changes and + // LogUpdate remains a pure diff engine. + // + // Full-screen damage (PR #20120) is a correctness backstop for the + // sibling-resize bleed: when flexbox siblings resize between frames + // (spinner appears → bottom grows → scrollbox shrinks), the + // cached-clear + clip-and-cull + setCellAt damage union can miss + // transition cells at the boundary. But that only happens when layout + // actually SHIFTS — didLayoutShift() tracks exactly this (any node's + // cached yoga position/size differs from current, or a child was + // removed). Steady-state frames (spinner rotate, clock tick, text + // stream into fixed-height box) don't shift layout, so normal damage + // bounds are correct and diffEach only compares the damaged region. + // + // Selection also requires full damage: overlay writes via setCellStyleId + // which doesn't track damage, and prev-frame overlay cells need to be + // compared when selection moves/clears. prevFrameContaminated covers + // the frame-after-selection-clears case. + let selActive = false; + let hlActive = false; + if (this.altScreenActive) { + selActive = hasSelection(this.selection); + if (selActive) { + applySelectionOverlay(frame.screen, this.selection, this.stylePool); + } + // Scan-highlight: inverse on ALL visible matches (less/vim style). + // Position-highlight (below) overlays CURRENT (yellow) on top. + hlActive = applySearchHighlight(frame.screen, this.searchHighlightQuery, this.stylePool); + // Position-based CURRENT: write yellow at positions[currentIdx] + + // rowOffset. No scanning — positions came from a prior scan when + // the message first mounted. Message-relative + rowOffset = screen. + if (this.searchPositions) { + const sp = this.searchPositions; + const posApplied = applyPositionedHighlight(frame.screen, this.stylePool, sp.positions, sp.rowOffset, sp.currentIdx); + hlActive = hlActive || posApplied; + } + } + + // Full-damage backstop: applies on BOTH alt-screen and main-screen. + // Layout shifts (spinner appears, status line resizes) can leave stale + // cells at sibling boundaries that per-node damage tracking misses. + // Selection/highlight overlays write via setCellStyleId which doesn't + // track damage. prevFrameContaminated covers the cleanup frame. + if (didLayoutShift() || selActive || hlActive || this.prevFrameContaminated) { + frame.screen.damage = { + x: 0, + y: 0, + width: frame.screen.width, + height: frame.screen.height + }; + } + + // Alt-screen: anchor the physical cursor to (0,0) before every diff. + // All cursor moves in log-update are RELATIVE to prev.cursor; if tmux + // (or any emulator) perturbs the physical cursor out-of-band (status + // bar refresh, pane redraw, Cmd+K wipe), the relative moves drift and + // content creeps up 1 row/frame. CSI H resets the physical cursor; + // passing prev.cursor=(0,0) makes the diff compute from the same spot. + // Self-healing against any external cursor manipulation. Main-screen + // can't do this — cursor.y tracks scrollback rows CSI H can't reach. + // The CSI H write is deferred until after the diff is computed so we + // can skip it for empty diffs (no writes → physical cursor unused). + let prevFrame = this.frontFrame; + if (this.altScreenActive) { + prevFrame = { + ...this.frontFrame, + cursor: ALT_SCREEN_ANCHOR_CURSOR + }; + } + const tDiff = performance.now(); + const diff = this.log.render(prevFrame, frame, this.altScreenActive, + // DECSTBM needs BSU/ESU atomicity — without it the outer terminal + // renders the scrolled-but-not-yet-repainted intermediate state. + // tmux is the main case (re-emits DECSTBM with its own timing and + // doesn't implement DEC 2026, so SYNC_OUTPUT_SUPPORTED is false). + SYNC_OUTPUT_SUPPORTED); + const diffMs = performance.now() - tDiff; + // Swap buffers + this.backFrame = this.frontFrame; + this.frontFrame = frame; + + // Periodically reset char/hyperlink pools to prevent unbounded growth + // during long sessions. 5 minutes is infrequent enough that the O(cells) + // migration cost is negligible. Reuses renderStart to avoid extra clock call. + if (renderStart - this.lastPoolResetTime > 5 * 60 * 1000) { + this.resetPools(); + this.lastPoolResetTime = renderStart; + } + const flickers: FrameEvent['flickers'] = []; + for (const patch of diff) { + if (patch.type === 'clearTerminal') { + flickers.push({ + desiredHeight: frame.screen.height, + availableHeight: frame.viewport.height, + reason: patch.reason + }); + if (isDebugRepaintsEnabled() && patch.debug) { + const chain = dom.findOwnerChainAtRow(this.rootNode, patch.debug.triggerY); + logForDebugging(`[REPAINT] full reset · ${patch.reason} · row ${patch.debug.triggerY}\n` + ` prev: "${patch.debug.prevLine}"\n` + ` next: "${patch.debug.nextLine}"\n` + ` culprit: ${chain.length ? chain.join(' < ') : '(no owner chain captured)'}`, { + level: 'warn' + }); + } + } + } + const tOptimize = performance.now(); + const optimized = optimize(diff); + const optimizeMs = performance.now() - tOptimize; + const hasDiff = optimized.length > 0; + if (this.altScreenActive && hasDiff) { + // Prepend CSI H to anchor the physical cursor to (0,0) so + // log-update's relative moves compute from a known spot (self-healing + // against out-of-band cursor drift, see the ALT_SCREEN_ANCHOR_CURSOR + // comment above). Append CSI row;1 H to park the cursor at the bottom + // row (where the prompt input is) — without this, the cursor ends + // wherever the last diff write landed (a different row every frame), + // making iTerm2's cursor guide flicker as it chases the cursor. + // BSU/ESU protects content atomicity but iTerm2's guide tracks cursor + // position independently. Parking at bottom (not 0,0) keeps the guide + // where the user's attention is. + // + // After resize, prepend ERASE_SCREEN too. The diff only writes cells + // that changed; cells where new=blank and prev-buffer=blank get skipped + // — but the physical terminal still has stale content there (shorter + // lines at new width leave old-width text tails visible). ERASE inside + // BSU/ESU is atomic: old content stays visible until the whole + // erase+paint lands, then swaps in one go. Writing ERASE_SCREEN + // synchronously in handleResize would blank the screen for the ~80ms + // render() takes. + if (this.needsEraseBeforePaint) { + this.needsEraseBeforePaint = false; + optimized.unshift(ERASE_THEN_HOME_PATCH); + } else { + optimized.unshift(CURSOR_HOME_PATCH); + } + optimized.push(this.altScreenParkPatch); + } + + // Native cursor positioning: park the terminal cursor at the declared + // position so IME preedit text renders inline and screen readers / + // magnifiers can follow the input. nodeCache holds the absolute screen + // rect populated by renderNodeToOutput this frame (including scrollTop + // translation) — if the declared node didn't render (stale declaration + // after remount, or scrolled out of view), it won't be in the cache + // and no move is emitted. + const decl = this.cursorDeclaration; + const rect = decl !== null ? nodeCache.get(decl.node) : undefined; + const target = decl !== null && rect !== undefined ? { + x: rect.x + decl.relativeX, + y: rect.y + decl.relativeY + } : null; + const parked = this.displayCursor; + + // Preserve the empty-diff zero-write fast path: skip all cursor writes + // when nothing rendered AND the park target is unchanged. + const targetMoved = target !== null && (parked === null || parked.x !== target.x || parked.y !== target.y); + if (hasDiff || targetMoved || target === null && parked !== null) { + // Main-screen preamble: log-update's relative moves assume the + // physical cursor is at prevFrame.cursor. If last frame parked it + // elsewhere, move back before the diff runs. Alt-screen's CSI H + // already resets to (0,0) so no preamble needed. + if (parked !== null && !this.altScreenActive && hasDiff) { + const pdx = prevFrame.cursor.x - parked.x; + const pdy = prevFrame.cursor.y - parked.y; + if (pdx !== 0 || pdy !== 0) { + optimized.unshift({ + type: 'stdout', + content: cursorMove(pdx, pdy) + }); + } + } + if (target !== null) { + if (this.altScreenActive) { + // Absolute CUP (1-indexed); next frame's CSI H resets regardless. + // Emitted after altScreenParkPatch so the declared position wins. + const row = Math.min(Math.max(target.y + 1, 1), terminalRows); + const col = Math.min(Math.max(target.x + 1, 1), terminalWidth); + optimized.push({ + type: 'stdout', + content: cursorPosition(row, col) + }); + } else { + // After the diff (or preamble), cursor is at frame.cursor. If no + // diff AND previously parked, it's still at the old park position + // (log-update wrote nothing). Otherwise it's at frame.cursor. + const from = !hasDiff && parked !== null ? parked : { + x: frame.cursor.x, + y: frame.cursor.y + }; + const dx = target.x - from.x; + const dy = target.y - from.y; + if (dx !== 0 || dy !== 0) { + optimized.push({ + type: 'stdout', + content: cursorMove(dx, dy) + }); + } + } + this.displayCursor = target; + } else { + // Declaration cleared (input blur, unmount). Restore physical cursor + // to frame.cursor before forgetting the park position — otherwise + // displayCursor=null lies about where the cursor is, and the NEXT + // frame's preamble (or log-update's relative moves) computes from a + // wrong spot. The preamble above handles hasDiff; this handles + // !hasDiff (e.g. accessibility mode where blur doesn't change + // renderedValue since invert is identity). + if (parked !== null && !this.altScreenActive && !hasDiff) { + const rdx = frame.cursor.x - parked.x; + const rdy = frame.cursor.y - parked.y; + if (rdx !== 0 || rdy !== 0) { + optimized.push({ + type: 'stdout', + content: cursorMove(rdx, rdy) + }); + } + } + this.displayCursor = null; + } + } + const tWrite = performance.now(); + writeDiffToTerminal(this.terminal, optimized, this.altScreenActive && !SYNC_OUTPUT_SUPPORTED); + const writeMs = performance.now() - tWrite; + + // Update blit safety for the NEXT frame. The frame just rendered + // becomes frontFrame (= next frame's prevScreen). If we applied the + // selection overlay, that buffer has inverted cells. selActive/hlActive + // are only ever true in alt-screen; in main-screen this is false→false. + this.prevFrameContaminated = selActive || hlActive; + + // A ScrollBox has pendingScrollDelta left to drain — schedule the next + // frame. MUST NOT call this.scheduleRender() here: we're inside a + // trailing-edge throttle invocation, timerId is undefined, and lodash's + // debounce sees timeSinceLastCall >= wait (last call was at the start + // of this window) → leadingEdge fires IMMEDIATELY → double render ~0.1ms + // apart → jank. Use a plain timeout. If a wheel event arrives first, + // its scheduleRender path fires a render which clears this timer at + // the top of onRender — no double. + // + // Drain frames are cheap (DECSTBM + ~10 patches, ~200 bytes) so run at + // quarter interval (~250fps, setTimeout practical floor) for max scroll + // speed. Regular renders stay at FRAME_INTERVAL_MS via the throttle. + if (frame.scrollDrainPending) { + this.drainTimer = setTimeout(() => this.onRender(), FRAME_INTERVAL_MS >> 2); + } + const yogaMs = getLastYogaMs(); + const commitMs = getLastCommitMs(); + const yc = this.lastYogaCounters; + // Reset so drain-only frames (no React commit) don't repeat stale values. + resetProfileCounters(); + this.lastYogaCounters = { + ms: 0, + visited: 0, + measured: 0, + cacheHits: 0, + live: 0 + }; + this.options.onFrame?.({ + durationMs: performance.now() - renderStart, + phases: { + renderer: rendererMs, + diff: diffMs, + optimize: optimizeMs, + write: writeMs, + patches: diff.length, + yoga: yogaMs, + commit: commitMs, + yogaVisited: yc.visited, + yogaMeasured: yc.measured, + yogaCacheHits: yc.cacheHits, + yogaLive: yc.live + }, + flickers + }); + } + pause(): void { + // Flush pending React updates and render before pausing. + // @ts-expect-error flushSyncFromReconciler exists in react-reconciler 0.31 but not in @types/react-reconciler + reconciler.flushSyncFromReconciler(); + this.onRender(); + this.isPaused = true; + } + resume(): void { + this.isPaused = false; + this.onRender(); + } + + /** + * Reset frame buffers so the next render writes the full screen from scratch. + * Call this before resume() when the terminal content has been corrupted by + * an external process (e.g. tmux, shell, full-screen TUI). + */ + repaint(): void { + this.frontFrame = emptyFrame(this.frontFrame.viewport.height, this.frontFrame.viewport.width, this.stylePool, this.charPool, this.hyperlinkPool); + this.backFrame = emptyFrame(this.backFrame.viewport.height, this.backFrame.viewport.width, this.stylePool, this.charPool, this.hyperlinkPool); + this.log.reset(); + // Physical cursor position is unknown after external terminal corruption. + // Clear displayCursor so the cursor preamble doesn't emit a stale + // relative move from where we last parked it. + this.displayCursor = null; + } + + /** + * Clear the physical terminal and force a full redraw. + * + * The traditional readline ctrl+l — clears the visible screen and + * redraws the current content. Also the recovery path when the terminal + * was cleared externally (macOS Cmd+K) and Ink's diff engine thinks + * unchanged cells don't need repainting. Scrollback is preserved. + */ + forceRedraw(): void { + if (!this.options.stdout.isTTY || this.isUnmounted || this.isPaused) return; + this.options.stdout.write(ERASE_SCREEN + CURSOR_HOME); + if (this.altScreenActive) { + this.resetFramesForAltScreen(); + } else { + this.repaint(); + // repaint() resets frontFrame to 0×0. Without this flag the next + // frame's blit optimization copies from that empty screen and the + // diff sees no content. onRender resets the flag at frame end. + this.prevFrameContaminated = true; + } + this.onRender(); + } + + /** + * Mark the previous frame as untrustworthy for blit, forcing the next + * render to do a full-damage diff instead of the per-node fast path. + * + * Lighter than forceRedraw() — no screen clear, no extra write. Call + * from a useLayoutEffect cleanup when unmounting a tall overlay: the + * blit fast path can copy stale cells from the overlay frame into rows + * the shrunken layout no longer reaches, leaving a ghost title/divider. + * onRender resets the flag at frame end so it's one-shot. + */ + invalidatePrevFrame(): void { + this.prevFrameContaminated = true; + } + + /** + * Called by the component on mount/unmount. + * Controls cursor.y clamping in the renderer and gates alt-screen-aware + * behavior in SIGCONT/resize/unmount handlers. Repaints on change so + * the first alt-screen frame (and first main-screen frame on exit) is + * a full redraw with no stale diff state. + */ + setAltScreenActive(active: boolean, mouseTracking = false): void { + if (this.altScreenActive === active) return; + this.altScreenActive = active; + this.altScreenMouseTracking = active && mouseTracking; + if (active) { + this.resetFramesForAltScreen(); + } else { + this.repaint(); + } + } + get isAltScreenActive(): boolean { + return this.altScreenActive; + } + + /** + * Re-assert terminal modes after a gap (>5s stdin silence or event-loop + * stall). Catches tmux detach→attach, ssh reconnect, and laptop + * sleep/wake — none of which send SIGCONT. The terminal may reset DEC + * private modes on reconnect; this method restores them. + * + * Always re-asserts extended key reporting and mouse tracking. Mouse + * tracking is idempotent (DEC private mode set-when-set is a no-op). The + * Kitty keyboard protocol is NOT — CSI >1u is a stack push, so we pop + * first to keep depth balanced (pop on empty stack is a no-op per spec, + * so after a terminal reset this still restores depth 0→1). Without the + * pop, each >5s idle gap adds a stack entry, and the single pop on exit + * or suspend can't drain them — the shell is left in CSI u mode where + * Ctrl+C/Ctrl+D leak as escape sequences. The alt-screen + * re-entry (ERASE_SCREEN + frame reset) is NOT idempotent — it blanks the + * screen — so it's opt-in via includeAltScreen. The stdin-gap caller fires + * on ordinary >5s idle + keypress and must not erase; the event-loop stall + * detector fires on genuine sleep/wake and opts in. tmux attach / ssh + * reconnect typically send a resize, which already covers alt-screen via + * handleResize. + */ + reassertTerminalModes = (includeAltScreen = false): void => { + if (!this.options.stdout.isTTY) return; + // Don't touch the terminal during an editor handoff — re-enabling kitty + // keyboard here would undo enterAlternateScreen's disable and nano would + // start seeing CSI-u sequences again. + if (this.isPaused) return; + // Extended keys — re-assert if enabled (App.tsx enables these on + // allowlisted terminals at raw-mode entry; a terminal reset clears them). + // Pop-before-push keeps Kitty stack depth at 1 instead of accumulating + // on each call. + if (supportsExtendedKeys()) { + this.options.stdout.write(DISABLE_KITTY_KEYBOARD + ENABLE_KITTY_KEYBOARD + ENABLE_MODIFY_OTHER_KEYS); + } + if (!this.altScreenActive) return; + // Mouse tracking — idempotent, safe to re-assert on every stdin gap. + if (this.altScreenMouseTracking) { + this.options.stdout.write(ENABLE_MOUSE_TRACKING); + } + // Alt-screen re-entry — destructive (ERASE_SCREEN). Only for callers that + // have a strong signal the terminal actually dropped mode 1049. + if (includeAltScreen) { + this.reenterAltScreen(); + } + }; + + /** + * Mark this instance as unmounted so future unmount() calls early-return. + * Called by gracefulShutdown's cleanupTerminalModes() after it has sent + * EXIT_ALT_SCREEN but before the remaining terminal-reset sequences. + * Without this, signal-exit's deferred ink.unmount() (triggered by + * process.exit()) runs the full unmount path: onRender() + writeSync + * cleanup block + updateContainerSync → AlternateScreen unmount cleanup. + * The result is 2-3 redundant EXIT_ALT_SCREEN sequences landing on the + * main screen AFTER printResumeHint(), which tmux (at least) interprets + * as restoring the saved cursor position — clobbering the resume hint. + */ + detachForShutdown(): void { + this.isUnmounted = true; + // Cancel any pending throttled render so it doesn't fire between + // cleanupTerminalModes() and process.exit() and write to main screen. + this.scheduleRender.cancel?.(); + // Restore stdin from raw mode. unmount() used to do this via React + // unmount (App.componentWillUnmount → handleSetRawMode(false)) but we're + // short-circuiting that path. Must use this.options.stdin — NOT + // process.stdin — because getStdinOverride() may have opened /dev/tty + // when stdin is piped. + const stdin = this.options.stdin as NodeJS.ReadStream & { + isRaw?: boolean; + setRawMode?: (m: boolean) => void; + }; + this.drainStdin(); + if (stdin.isTTY && stdin.isRaw && stdin.setRawMode) { + stdin.setRawMode(false); + } + } + + /** @see drainStdin */ + drainStdin(): void { + drainStdin(this.options.stdin); + } + + /** + * Re-enter alt-screen, clear, home, re-enable mouse tracking, and reset + * frame buffers so the next render repaints from scratch. Self-heal for + * SIGCONT, resize, and stdin-gap/event-loop-stall (sleep/wake) — any of + * which can leave the terminal in main-screen mode while altScreenActive + * stays true. ENTER_ALT_SCREEN is a terminal-side no-op if already in alt. + */ + private reenterAltScreen(): void { + this.options.stdout.write(ENTER_ALT_SCREEN + ERASE_SCREEN + CURSOR_HOME + (this.altScreenMouseTracking ? ENABLE_MOUSE_TRACKING : '')); + this.resetFramesForAltScreen(); + } + + /** + * Seed prev/back frames with full-size BLANK screens (rows×cols of empty + * cells, not 0×0). In alt-screen mode, next.screen.height is always + * terminalRows; if prev.screen.height is 0 (emptyFrame's default), + * log-update sees heightDelta > 0 ('growing') and calls renderFrameSlice, + * whose trailing per-row CR+LF at the last row scrolls the alt screen, + * permanently desyncing the virtual and physical cursors by 1 row. + * + * With a rows×cols blank prev, heightDelta === 0 → standard diffEach + * → moveCursorTo (CSI cursorMove, no LF, no scroll). + * + * viewport.height = rows + 1 matches the renderer's alt-screen output, + * preventing a spurious resize trigger on the first frame. cursor.y = 0 + * matches the physical cursor after ENTER_ALT_SCREEN + CSI H (home). + */ + private resetFramesForAltScreen(): void { + const rows = this.terminalRows; + const cols = this.terminalColumns; + const blank = (): Frame => ({ + screen: createScreen(cols, rows, this.stylePool, this.charPool, this.hyperlinkPool), + viewport: { + width: cols, + height: rows + 1 + }, + cursor: { + x: 0, + y: 0, + visible: true + } + }); + this.frontFrame = blank(); + this.backFrame = blank(); + this.log.reset(); + // Defense-in-depth: alt-screen skips the cursor preamble anyway (CSI H + // resets), but a stale displayCursor would be misleading if we later + // exit to main-screen without an intervening render. + this.displayCursor = null; + // Fresh frontFrame is blank rows×cols — blitting from it would copy + // blanks over content. Next alt-screen frame must full-render. + this.prevFrameContaminated = true; + } + + /** + * Copy the current selection to the clipboard without clearing the + * highlight. Matches iTerm2's copy-on-select behavior where the selected + * region stays visible after the automatic copy. + */ + copySelectionNoClear(): string { + if (!hasSelection(this.selection)) return ''; + const text = getSelectedText(this.selection, this.frontFrame.screen); + if (text) { + // Raw OSC 52, or DCS-passthrough-wrapped OSC 52 inside tmux (tmux + // drops it silently unless allow-passthrough is on — no regression). + void setClipboard(text).then(raw => { + if (raw) this.options.stdout.write(raw); + }); + } + return text; + } + + /** + * Copy the current text selection to the system clipboard via OSC 52 + * and clear the selection. Returns the copied text (empty if no selection). + */ + copySelection(): string { + if (!hasSelection(this.selection)) return ''; + const text = this.copySelectionNoClear(); + clearSelection(this.selection); + this.notifySelectionChange(); + return text; + } + + /** Clear the current text selection without copying. */ + clearTextSelection(): void { + if (!hasSelection(this.selection)) return; + clearSelection(this.selection); + this.notifySelectionChange(); + } + + /** + * Set the search highlight query. Non-empty → all visible occurrences + * are inverted (SGR 7) on the next frame; first one also underlined. + * Empty → clears (prevFrameContaminated handles the frame after). Same + * damage-tracking machinery as selection — setCellStyleId doesn't track + * damage, so the overlay forces full-frame damage while active. + */ + setSearchHighlight(query: string): void { + if (this.searchHighlightQuery === query) return; + this.searchHighlightQuery = query; + this.scheduleRender(); + } + + /** Paint an EXISTING DOM subtree to a fresh Screen at its natural + * height, scan for query. Returns positions relative to the element's + * bounding box (row 0 = element top). + * + * The element comes from the MAIN tree — built with all real + * providers, yoga already computed. We paint it to a fresh buffer + * with offsets so it lands at (0,0). Same paint path as the main + * render. Zero drift. No second React root, no context bridge. + * + * ~1-2ms (paint only, no reconcile — the DOM is already built). */ + scanElementSubtree(el: dom.DOMElement): MatchPosition[] { + if (!this.searchHighlightQuery || !el.yogaNode) return []; + const width = Math.ceil(el.yogaNode.getComputedWidth()); + const height = Math.ceil(el.yogaNode.getComputedHeight()); + if (width <= 0 || height <= 0) return []; + // renderNodeToOutput adds el's OWN computedLeft/Top to offsetX/Y. + // Passing -elLeft/-elTop nets to 0 → paints at (0,0) in our buffer. + const elLeft = el.yogaNode.getComputedLeft(); + const elTop = el.yogaNode.getComputedTop(); + const screen = createScreen(width, height, this.stylePool, this.charPool, this.hyperlinkPool); + const output = new Output({ + width, + height, + stylePool: this.stylePool, + screen + }); + renderNodeToOutput(el, output, { + offsetX: -elLeft, + offsetY: -elTop, + prevScreen: undefined + }); + const rendered = output.get(); + // renderNodeToOutput wrote our offset positions to nodeCache — + // corrupts the main render (it'd blit from wrong coords). Mark the + // subtree dirty so the next main render repaints + re-caches + // correctly. One extra paint of this message, but correct > fast. + dom.markDirty(el); + const positions = scanPositions(rendered, this.searchHighlightQuery); + logForDebugging(`scanElementSubtree: q='${this.searchHighlightQuery}' ` + `el=${width}x${height}@(${elLeft},${elTop}) n=${positions.length} ` + `[${positions.slice(0, 10).map(p => `${p.row}:${p.col}`).join(',')}` + `${positions.length > 10 ? ',…' : ''}]`); + return positions; + } + + /** Set the position-based highlight state. Every frame, writes CURRENT + * style at positions[currentIdx] + rowOffset. null clears. The scan- + * highlight (inverse on all matches) still runs — this overlays yellow + * on top. rowOffset changes as the user scrolls (= message's current + * screen-top); positions stay stable (message-relative). */ + setSearchPositions(state: { + positions: MatchPosition[]; + rowOffset: number; + currentIdx: number; + } | null): void { + this.searchPositions = state; + this.scheduleRender(); + } + + /** + * Set the selection highlight background color. Replaces the per-cell + * SGR-7 inverse with a solid theme-aware bg (matches native terminal + * selection). Accepts the same color formats as Text backgroundColor + * (rgb(), ansi:name, #hex, ansi256()) — colorize() routes through + * chalk so the tmux/xterm.js level clamps in colorize.ts apply and + * the emitted SGR is correct for the current terminal. + * + * Called by React-land once theme is known (ScrollKeybindingHandler's + * useEffect watching useTheme). Before that call, withSelectionBg + * falls back to withInverse so selection still renders on the first + * frame; the effect fires before any mouse input so the fallback is + * unobservable in practice. + */ + setSelectionBgColor(color: string): void { + // Wrap a NUL marker, then split on it to extract the open/close SGR. + // colorize returns the input unchanged if the color string is bad — + // no NUL-split then, so fall through to null (inverse fallback). + const wrapped = colorize('\0', color, 'background'); + const nul = wrapped.indexOf('\0'); + if (nul <= 0 || nul === wrapped.length - 1) { + this.stylePool.setSelectionBg(null); + return; + } + this.stylePool.setSelectionBg({ + type: 'ansi', + code: wrapped.slice(0, nul), + endCode: wrapped.slice(nul + 1) // always \x1b[49m for bg + }); + // No scheduleRender: this is called from a React effect that already + // runs inside the render cycle, and the bg only matters once a + // selection exists (which itself triggers a full-damage frame). + } + + /** + * Capture text from rows about to scroll out of the viewport during + * drag-to-scroll. Must be called BEFORE the ScrollBox scrolls so the + * screen buffer still holds the outgoing content. Accumulated into + * the selection state and joined back in by getSelectedText. + */ + captureScrolledRows(firstRow: number, lastRow: number, side: 'above' | 'below'): void { + captureScrolledRows(this.selection, this.frontFrame.screen, firstRow, lastRow, side); + } + + /** + * Shift anchor AND focus by dRow, clamped to [minRow, maxRow]. Used by + * keyboard scroll handlers (PgUp/PgDn etc.) so the highlight tracks the + * content instead of disappearing. Unlike shiftAnchor (drag-to-scroll), + * this moves BOTH endpoints — the user isn't holding the mouse at one + * edge. Supplies screen.width for the col-reset-on-clamp boundary. + */ + shiftSelectionForScroll(dRow: number, minRow: number, maxRow: number): void { + const hadSel = hasSelection(this.selection); + shiftSelection(this.selection, dRow, minRow, maxRow, this.frontFrame.screen.width); + // shiftSelection clears when both endpoints overshoot the same edge + // (Home/g/End/G page-jump past the selection). Notify subscribers so + // useHasSelection updates. Safe to call notifySelectionChange here — + // this runs from keyboard handlers, not inside onRender(). + if (hadSel && !hasSelection(this.selection)) { + this.notifySelectionChange(); + } + } + + /** + * Keyboard selection extension (shift+arrow/home/end). Moves focus; + * anchor stays fixed so the highlight grows or shrinks relative to it. + * Left/right wrap across row boundaries — native macOS text-edit + * behavior: shift+left at col 0 wraps to end of the previous row. + * Up/down clamp at viewport edges (no scroll-to-extend yet). Drops to + * char mode. No-op outside alt-screen or without an active selection. + */ + moveSelectionFocus(move: FocusMove): void { + if (!this.altScreenActive) return; + const { + focus + } = this.selection; + if (!focus) return; + const { + width, + height + } = this.frontFrame.screen; + const maxCol = width - 1; + const maxRow = height - 1; + let { + col, + row + } = focus; + switch (move) { + case 'left': + if (col > 0) col--;else if (row > 0) { + col = maxCol; + row--; + } + break; + case 'right': + if (col < maxCol) col++;else if (row < maxRow) { + col = 0; + row++; + } + break; + case 'up': + if (row > 0) row--; + break; + case 'down': + if (row < maxRow) row++; + break; + case 'lineStart': + col = 0; + break; + case 'lineEnd': + col = maxCol; + break; + } + if (col === focus.col && row === focus.row) return; + moveFocus(this.selection, col, row); + this.notifySelectionChange(); + } + + /** Whether there is an active text selection. */ + hasTextSelection(): boolean { + return hasSelection(this.selection); + } + + /** + * Subscribe to selection state changes. Fires whenever the selection + * is started, updated, cleared, or copied. Returns an unsubscribe fn. + */ + subscribeToSelectionChange(cb: () => void): () => void { + this.selectionListeners.add(cb); + return () => this.selectionListeners.delete(cb); + } + private notifySelectionChange(): void { + this.onRender(); + for (const cb of this.selectionListeners) cb(); + } + + /** + * Hit-test the rendered DOM tree at (col, row) and bubble a ClickEvent + * from the deepest hit node up through ancestors with onClick handlers. + * Returns true if a DOM handler consumed the click. Gated on + * altScreenActive — clicks only make sense with a fixed viewport where + * nodeCache rects map 1:1 to terminal cells (no scrollback offset). + */ + dispatchClick(col: number, row: number): boolean { + if (!this.altScreenActive) return false; + const blank = isEmptyCellAt(this.frontFrame.screen, col, row); + return dispatchClick(this.rootNode, col, row, blank); + } + dispatchHover(col: number, row: number): void { + if (!this.altScreenActive) return; + dispatchHover(this.rootNode, col, row, this.hoveredNodes); + } + dispatchKeyboardEvent(parsedKey: ParsedKey): void { + const target = this.focusManager.activeElement ?? this.rootNode; + const event = new KeyboardEvent(parsedKey); + dispatcher.dispatchDiscrete(target, event); + + // Tab cycling is the default action — only fires if no handler + // called preventDefault(). Mirrors browser behavior. + if (!event.defaultPrevented && parsedKey.name === 'tab' && !parsedKey.ctrl && !parsedKey.meta) { + if (parsedKey.shift) { + this.focusManager.focusPrevious(this.rootNode); + } else { + this.focusManager.focusNext(this.rootNode); + } + } + } + /** + * Look up the URL at (col, row) in the current front frame. Checks for + * an OSC 8 hyperlink first, then falls back to scanning the row for a + * plain-text URL (mouse tracking intercepts the terminal's native + * Cmd+Click URL detection, so we replicate it). This is a pure lookup + * with no side effects — call it synchronously at click time so the + * result reflects the screen the user actually clicked on, then defer + * the browser-open action via a timer. + */ + getHyperlinkAt(col: number, row: number): string | undefined { + if (!this.altScreenActive) return undefined; + const screen = this.frontFrame.screen; + const cell = cellAt(screen, col, row); + let url = cell?.hyperlink; + // SpacerTail cells (right half of wide/CJK/emoji chars) store the + // hyperlink on the head cell at col-1. + if (!url && cell?.width === CellWidth.SpacerTail && col > 0) { + url = cellAt(screen, col - 1, row)?.hyperlink; + } + return url ?? findPlainTextUrlAt(screen, col, row); + } + + /** + * Optional callback fired when clicking an OSC 8 hyperlink in fullscreen + * mode. Set by FullscreenLayout via useLayoutEffect. + */ + onHyperlinkClick: ((url: string) => void) | undefined; + + /** + * Stable prototype wrapper for onHyperlinkClick. Passed to as + * onOpenHyperlink so the prop is a bound method (autoBind'd) that reads + * the mutable field at call time — not the undefined-at-render value. + */ + openHyperlink(url: string): void { + this.onHyperlinkClick?.(url); + } + + /** + * Handle a double- or triple-click at (col, row): select the word or + * line under the cursor by reading the current screen buffer. Called on + * PRESS (not release) so the highlight appears immediately and drag can + * extend the selection word-by-word / line-by-line. Falls back to + * char-mode startSelection if the click lands on a noSelect cell. + */ + handleMultiClick(col: number, row: number, count: 2 | 3): void { + if (!this.altScreenActive) return; + const screen = this.frontFrame.screen; + // selectWordAt/selectLineAt no-op on noSelect/out-of-bounds. Seed with + // a char-mode selection so the press still starts a drag even if the + // word/line scan finds nothing selectable. + startSelection(this.selection, col, row); + if (count === 2) selectWordAt(this.selection, screen, col, row);else selectLineAt(this.selection, screen, row); + // Ensure hasSelection is true so release doesn't re-dispatch onClickAt. + // selectWordAt no-ops on noSelect; selectLineAt no-ops out-of-bounds. + if (!this.selection.focus) this.selection.focus = this.selection.anchor; + this.notifySelectionChange(); + } + + /** + * Handle a drag-motion at (col, row). In char mode updates focus to the + * exact cell. In word/line mode snaps to word/line boundaries so the + * selection extends by word/line like native macOS. Gated on + * altScreenActive for the same reason as dispatchClick. + */ + handleSelectionDrag(col: number, row: number): void { + if (!this.altScreenActive) return; + const sel = this.selection; + if (sel.anchorSpan) { + extendSelection(sel, this.frontFrame.screen, col, row); + } else { + updateSelection(sel, col, row); + } + this.notifySelectionChange(); + } + + // Methods to properly suspend stdin for external editor usage + // This is needed to prevent Ink from swallowing keystrokes when an external editor is active + private stdinListeners: Array<{ + event: string; + listener: (...args: unknown[]) => void; + }> = []; + private wasRawMode = false; + suspendStdin(): void { + const stdin = this.options.stdin; + if (!stdin.isTTY) { + return; + } + + // Store and remove all 'readable' event listeners temporarily + // This prevents Ink from consuming stdin while the editor is active + const readableListeners = stdin.listeners('readable'); + logForDebugging(`[stdin] suspendStdin: removing ${readableListeners.length} readable listener(s), wasRawMode=${(stdin as NodeJS.ReadStream & { + isRaw?: boolean; + }).isRaw ?? false}`); + readableListeners.forEach(listener => { + this.stdinListeners.push({ + event: 'readable', + listener: listener as (...args: unknown[]) => void + }); + stdin.removeListener('readable', listener as (...args: unknown[]) => void); + }); + + // If raw mode is enabled, disable it temporarily + const stdinWithRaw = stdin as NodeJS.ReadStream & { + isRaw?: boolean; + setRawMode?: (mode: boolean) => void; + }; + if (stdinWithRaw.isRaw && stdinWithRaw.setRawMode) { + stdinWithRaw.setRawMode(false); + this.wasRawMode = true; + } + } + resumeStdin(): void { + const stdin = this.options.stdin; + if (!stdin.isTTY) { + return; + } + + // Re-attach all the stored listeners + if (this.stdinListeners.length === 0 && !this.wasRawMode) { + logForDebugging('[stdin] resumeStdin: called with no stored listeners and wasRawMode=false (possible desync)', { + level: 'warn' + }); + } + logForDebugging(`[stdin] resumeStdin: re-attaching ${this.stdinListeners.length} listener(s), wasRawMode=${this.wasRawMode}`); + this.stdinListeners.forEach(({ + event, + listener + }) => { + stdin.addListener(event, listener); + }); + this.stdinListeners = []; + + // Re-enable raw mode if it was enabled before + if (this.wasRawMode) { + const stdinWithRaw = stdin as NodeJS.ReadStream & { + setRawMode?: (mode: boolean) => void; + }; + if (stdinWithRaw.setRawMode) { + stdinWithRaw.setRawMode(true); + } + this.wasRawMode = false; + } + } + + // Stable identity for TerminalWriteContext. An inline arrow here would + // change on every render() call (initial mount + each resize), which + // cascades through useContext → 's useLayoutEffect dep + // array → spurious exit+re-enter of the alt screen on every SIGWINCH. + private writeRaw(data: string): void { + this.options.stdout.write(data); + } + private setCursorDeclaration: CursorDeclarationSetter = (decl, clearIfNode) => { + if (decl === null && clearIfNode !== undefined && this.cursorDeclaration?.node !== clearIfNode) { + return; + } + this.cursorDeclaration = decl; + }; + render(node: ReactNode): void { + this.currentNode = node; + const tree = + + {node} + + ; + + // @ts-expect-error updateContainerSync exists in react-reconciler but not in @types/react-reconciler + reconciler.updateContainerSync(tree, this.container, null, noop); + // @ts-expect-error flushSyncWork exists in react-reconciler but not in @types/react-reconciler + reconciler.flushSyncWork(); + } + unmount(error?: Error | number | null): void { + if (this.isUnmounted) { + return; + } + this.onRender(); + this.unsubscribeExit(); + if (typeof this.restoreConsole === 'function') { + this.restoreConsole(); + } + this.restoreStderr?.(); + this.unsubscribeTTYHandlers?.(); + + // Non-TTY environments don't handle erasing ansi escapes well, so it's better to + // only render last frame of non-static output + const diff = this.log.renderPreviousOutput_DEPRECATED(this.frontFrame); + writeDiffToTerminal(this.terminal, optimize(diff)); + + // Clean up terminal modes synchronously before process exit. + // React's componentWillUnmount won't run in time when process.exit() is called, + // so we must reset terminal modes here to prevent escape sequence leakage. + // Use writeSync to stdout (fd 1) to ensure writes complete before exit. + // We unconditionally send all disable sequences because terminal detection + // may not work correctly (e.g., in tmux, screen) and these are no-ops on + // terminals that don't support them. + /* eslint-disable custom-rules/no-sync-fs -- process exiting; async writes would be dropped */ + if (this.options.stdout.isTTY) { + if (this.altScreenActive) { + // 's unmount effect won't run during signal-exit. + // Exit alt screen FIRST so other cleanup sequences go to the main screen. + writeSync(1, EXIT_ALT_SCREEN); + } + // Disable mouse tracking — unconditional because altScreenActive can be + // stale if AlternateScreen's unmount (which flips the flag) raced a + // blocked event loop + SIGINT. No-op if tracking was never enabled. + writeSync(1, DISABLE_MOUSE_TRACKING); + // Drain stdin so in-flight mouse events don't leak to the shell + this.drainStdin(); + // Disable extended key reporting (both kitty and modifyOtherKeys) + writeSync(1, DISABLE_MODIFY_OTHER_KEYS); + writeSync(1, DISABLE_KITTY_KEYBOARD); + // Disable focus events (DECSET 1004) + writeSync(1, DFE); + // Disable bracketed paste mode + writeSync(1, DBP); + // Show cursor + writeSync(1, SHOW_CURSOR); + // Clear iTerm2 progress bar + writeSync(1, CLEAR_ITERM2_PROGRESS); + // Clear tab status (OSC 21337) so a stale dot doesn't linger + if (supportsTabStatus()) writeSync(1, wrapForMultiplexer(CLEAR_TAB_STATUS)); + } + /* eslint-enable custom-rules/no-sync-fs */ + + this.isUnmounted = true; + + // Cancel any pending throttled renders to prevent accessing freed Yoga nodes + this.scheduleRender.cancel?.(); + if (this.drainTimer !== null) { + clearTimeout(this.drainTimer); + this.drainTimer = null; + } + + // @ts-expect-error updateContainerSync exists in react-reconciler but not in @types/react-reconciler + reconciler.updateContainerSync(null, this.container, null, noop); + // @ts-expect-error flushSyncWork exists in react-reconciler but not in @types/react-reconciler + reconciler.flushSyncWork(); + instances.delete(this.options.stdout); + + // Free the root yoga node, then clear its reference. Children are already + // freed by the reconciler's removeChildFromContainer; using .free() (not + // .freeRecursive()) avoids double-freeing them. + this.rootNode.yogaNode?.free(); + this.rootNode.yogaNode = undefined; + if (error instanceof Error) { + this.rejectExitPromise(error); + } else { + this.resolveExitPromise(); + } + } + async waitUntilExit(): Promise { + this.exitPromise ||= new Promise((resolve, reject) => { + this.resolveExitPromise = resolve; + this.rejectExitPromise = reject; + }); + return this.exitPromise; + } + resetLineCount(): void { + if (this.options.stdout.isTTY) { + // Swap so old front becomes back (for screen reuse), then reset front + this.backFrame = this.frontFrame; + this.frontFrame = emptyFrame(this.frontFrame.viewport.height, this.frontFrame.viewport.width, this.stylePool, this.charPool, this.hyperlinkPool); + this.log.reset(); + // frontFrame is reset, so frame.cursor on the next render is (0,0). + // Clear displayCursor so the preamble doesn't compute a stale delta. + this.displayCursor = null; + } + } + + /** + * Replace char/hyperlink pools with fresh instances to prevent unbounded + * growth during long sessions. Migrates the front frame's screen IDs into + * the new pools so diffing remains correct. The back frame doesn't need + * migration — resetScreen zeros it before any reads. + * + * Call between conversation turns or periodically. + */ + resetPools(): void { + this.charPool = new CharPool(); + this.hyperlinkPool = new HyperlinkPool(); + migrateScreenPools(this.frontFrame.screen, this.charPool, this.hyperlinkPool); + // Back frame's data is zeroed by resetScreen before reads, but its pool + // references are used by the renderer to intern new characters. Point + // them at the new pools so the next frame's IDs are comparable. + this.backFrame.screen.charPool = this.charPool; + this.backFrame.screen.hyperlinkPool = this.hyperlinkPool; + } + patchConsole(): () => void { + // biome-ignore lint/suspicious/noConsole: intentionally patching global console + const con = console; + const originals: Partial> = {}; + const toDebug = (...args: unknown[]) => logForDebugging(`console.log: ${format(...args)}`); + const toError = (...args: unknown[]) => logError(new Error(`console.error: ${format(...args)}`)); + for (const m of CONSOLE_STDOUT_METHODS) { + originals[m] = con[m]; + con[m] = toDebug; + } + for (const m of CONSOLE_STDERR_METHODS) { + originals[m] = con[m]; + con[m] = toError; + } + originals.assert = con.assert; + con.assert = (condition: unknown, ...args: unknown[]) => { + if (!condition) toError(...args); + }; + return () => Object.assign(con, originals); + } + + /** + * Intercept process.stderr.write so stray writes (config.ts, hooks.ts, + * third-party deps) don't corrupt the alt-screen buffer. patchConsole only + * hooks console.* methods — direct stderr writes bypass it, land at the + * parked cursor, scroll the alt-screen, and desync frontFrame from the + * physical terminal. Next diff writes only changed-in-React cells at + * absolute coords → interleaved garbage. + * + * Swallows the write (routes text to the debug log) and, in alt-screen, + * forces a full-damage repaint as a defensive recovery. Not patching + * process.stdout — Ink itself writes there. + */ + private patchStderr(): () => void { + const stderr = process.stderr; + const originalWrite = stderr.write; + let reentered = false; + const intercept = (chunk: Uint8Array | string, encodingOrCb?: BufferEncoding | ((err?: Error) => void), cb?: (err?: Error) => void): boolean => { + const callback = typeof encodingOrCb === 'function' ? encodingOrCb : cb; + // Reentrancy guard: logForDebugging → writeToStderr → here. Pass + // through to the original so --debug-to-stderr still works and we + // don't stack-overflow. + if (reentered) { + const encoding = typeof encodingOrCb === 'string' ? encodingOrCb : undefined; + return originalWrite.call(stderr, chunk, encoding, callback); + } + reentered = true; + try { + const text = typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8'); + logForDebugging(`[stderr] ${text}`, { + level: 'warn' + }); + if (this.altScreenActive && !this.isUnmounted && !this.isPaused) { + this.prevFrameContaminated = true; + this.scheduleRender(); + } + } finally { + reentered = false; + callback?.(); + } + return true; + }; + stderr.write = intercept; + return () => { + if (stderr.write === intercept) { + stderr.write = originalWrite; + } + }; + } +} + +/** + * Discard pending stdin bytes so in-flight escape sequences (mouse tracking + * reports, bracketed-paste markers) don't leak to the shell after exit. + * + * Two layers of trickiness: + * + * 1. setRawMode is termios, not fcntl — the stdin fd stays blocking, so + * readSync on it would hang forever. Node doesn't expose fcntl, so we + * open /dev/tty fresh with O_NONBLOCK (all fds to the controlling + * terminal share one line-discipline input queue). + * + * 2. By the time forceExit calls this, detachForShutdown has already put + * the TTY back in cooked (canonical) mode. Canonical mode line-buffers + * input until newline, so O_NONBLOCK reads return EAGAIN even when + * mouse bytes are sitting in the buffer. We briefly re-enter raw mode + * so reads return any available bytes, then restore cooked mode. + * + * Safe to call multiple times. Call as LATE as possible in the exit path: + * DISABLE_MOUSE_TRACKING has terminal round-trip latency, so events can + * arrive for a few ms after it's written. + */ +/* eslint-disable custom-rules/no-sync-fs -- must be sync; called from signal handler / unmount */ +export function drainStdin(stdin: NodeJS.ReadStream = process.stdin): void { + if (!stdin.isTTY) return; + // Drain Node's stream buffer (bytes libuv already pulled in). read() + // returns null when empty — never blocks. + try { + while (stdin.read() !== null) { + /* discard */ + } + } catch { + /* stream may be destroyed */ + } + // No /dev/tty on Windows; CONIN$ doesn't support O_NONBLOCK semantics. + // Windows Terminal also doesn't buffer mouse reports the same way. + if (process.platform === 'win32') return; + // termios is per-device: flip stdin to raw so canonical-mode line + // buffering doesn't hide partial input from the non-blocking read. + // Restored in the finally block. + const tty = stdin as NodeJS.ReadStream & { + isRaw?: boolean; + setRawMode?: (raw: boolean) => void; + }; + const wasRaw = tty.isRaw === true; + // Drain the kernel TTY buffer via a fresh O_NONBLOCK fd. Bounded at 64 + // reads (64KB) — a real mouse burst is a few hundred bytes; the cap + // guards against a terminal that ignores O_NONBLOCK. + let fd = -1; + try { + // setRawMode inside try: on revoked TTY (SIGHUP/SSH disconnect) the + // ioctl throws EBADF — same recovery path as openSync/readSync below. + if (!wasRaw) tty.setRawMode?.(true); + fd = openSync('/dev/tty', fsConstants.O_RDONLY | fsConstants.O_NONBLOCK); + const buf = Buffer.alloc(1024); + for (let i = 0; i < 64; i++) { + if (readSync(fd, buf, 0, buf.length, null) <= 0) break; + } + } catch { + // EAGAIN (buffer empty — expected), ENXIO/ENOENT (no controlling tty), + // EBADF/EIO (TTY revoked — SIGHUP, SSH disconnect) + } finally { + if (fd >= 0) { + try { + closeSync(fd); + } catch { + /* ignore */ + } + } + if (!wasRaw) { + try { + tty.setRawMode?.(false); + } catch { + /* TTY may be gone */ + } + } + } +} +/* eslint-enable custom-rules/no-sync-fs */ + +const CONSOLE_STDOUT_METHODS = ['log', 'info', 'debug', 'dir', 'dirxml', 'count', 'countReset', 'group', 'groupCollapsed', 'groupEnd', 'table', 'time', 'timeEnd', 'timeLog'] as const; +const CONSOLE_STDERR_METHODS = ['warn', 'error', 'trace'] as const; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhdXRvQmluZCIsImNsb3NlU3luYyIsImNvbnN0YW50cyIsImZzQ29uc3RhbnRzIiwib3BlblN5bmMiLCJyZWFkU3luYyIsIndyaXRlU3luYyIsIm5vb3AiLCJ0aHJvdHRsZSIsIlJlYWN0IiwiUmVhY3ROb2RlIiwiRmliZXJSb290IiwiQ29uY3VycmVudFJvb3QiLCJvbkV4aXQiLCJmbHVzaEludGVyYWN0aW9uVGltZSIsImdldFlvZ2FDb3VudGVycyIsImxvZ0ZvckRlYnVnZ2luZyIsImxvZ0Vycm9yIiwiZm9ybWF0IiwiY29sb3JpemUiLCJBcHAiLCJDdXJzb3JEZWNsYXJhdGlvbiIsIkN1cnNvckRlY2xhcmF0aW9uU2V0dGVyIiwiRlJBTUVfSU5URVJWQUxfTVMiLCJkb20iLCJLZXlib2FyZEV2ZW50IiwiRm9jdXNNYW5hZ2VyIiwiZW1wdHlGcmFtZSIsIkZyYW1lIiwiRnJhbWVFdmVudCIsImRpc3BhdGNoQ2xpY2siLCJkaXNwYXRjaEhvdmVyIiwiaW5zdGFuY2VzIiwiTG9nVXBkYXRlIiwibm9kZUNhY2hlIiwib3B0aW1pemUiLCJPdXRwdXQiLCJQYXJzZWRLZXkiLCJyZWNvbmNpbGVyIiwiZGlzcGF0Y2hlciIsImdldExhc3RDb21taXRNcyIsImdldExhc3RZb2dhTXMiLCJpc0RlYnVnUmVwYWludHNFbmFibGVkIiwicmVjb3JkWW9nYU1zIiwicmVzZXRQcm9maWxlQ291bnRlcnMiLCJyZW5kZXJOb2RlVG9PdXRwdXQiLCJjb25zdW1lRm9sbG93U2Nyb2xsIiwiZGlkTGF5b3V0U2hpZnQiLCJhcHBseVBvc2l0aW9uZWRIaWdobGlnaHQiLCJNYXRjaFBvc2l0aW9uIiwic2NhblBvc2l0aW9ucyIsImNyZWF0ZVJlbmRlcmVyIiwiUmVuZGVyZXIiLCJDZWxsV2lkdGgiLCJDaGFyUG9vbCIsImNlbGxBdCIsImNyZWF0ZVNjcmVlbiIsIkh5cGVybGlua1Bvb2wiLCJpc0VtcHR5Q2VsbEF0IiwibWlncmF0ZVNjcmVlblBvb2xzIiwiU3R5bGVQb29sIiwiYXBwbHlTZWFyY2hIaWdobGlnaHQiLCJhcHBseVNlbGVjdGlvbk92ZXJsYXkiLCJjYXB0dXJlU2Nyb2xsZWRSb3dzIiwiY2xlYXJTZWxlY3Rpb24iLCJjcmVhdGVTZWxlY3Rpb25TdGF0ZSIsImV4dGVuZFNlbGVjdGlvbiIsIkZvY3VzTW92ZSIsImZpbmRQbGFpblRleHRVcmxBdCIsImdldFNlbGVjdGVkVGV4dCIsImhhc1NlbGVjdGlvbiIsIm1vdmVGb2N1cyIsIlNlbGVjdGlvblN0YXRlIiwic2VsZWN0TGluZUF0Iiwic2VsZWN0V29yZEF0Iiwic2hpZnRBbmNob3IiLCJzaGlmdFNlbGVjdGlvbiIsInNoaWZ0U2VsZWN0aW9uRm9yRm9sbG93Iiwic3RhcnRTZWxlY3Rpb24iLCJ1cGRhdGVTZWxlY3Rpb24iLCJTWU5DX09VVFBVVF9TVVBQT1JURUQiLCJzdXBwb3J0c0V4dGVuZGVkS2V5cyIsIlRlcm1pbmFsIiwid3JpdGVEaWZmVG9UZXJtaW5hbCIsIkNVUlNPUl9IT01FIiwiY3Vyc29yTW92ZSIsImN1cnNvclBvc2l0aW9uIiwiRElTQUJMRV9LSVRUWV9LRVlCT0FSRCIsIkRJU0FCTEVfTU9ESUZZX09USEVSX0tFWVMiLCJFTkFCTEVfS0lUVFlfS0VZQk9BUkQiLCJFTkFCTEVfTU9ESUZZX09USEVSX0tFWVMiLCJFUkFTRV9TQ1JFRU4iLCJEQlAiLCJERkUiLCJESVNBQkxFX01PVVNFX1RSQUNLSU5HIiwiRU5BQkxFX01PVVNFX1RSQUNLSU5HIiwiRU5URVJfQUxUX1NDUkVFTiIsIkVYSVRfQUxUX1NDUkVFTiIsIlNIT1dfQ1VSU09SIiwiQ0xFQVJfSVRFUk0yX1BST0dSRVNTIiwiQ0xFQVJfVEFCX1NUQVRVUyIsInNldENsaXBib2FyZCIsInN1cHBvcnRzVGFiU3RhdHVzIiwid3JhcEZvck11bHRpcGxleGVyIiwiVGVybWluYWxXcml0ZVByb3ZpZGVyIiwiQUxUX1NDUkVFTl9BTkNIT1JfQ1VSU09SIiwiT2JqZWN0IiwiZnJlZXplIiwieCIsInkiLCJ2aXNpYmxlIiwiQ1VSU09SX0hPTUVfUEFUQ0giLCJ0eXBlIiwiY29uc3QiLCJjb250ZW50IiwiRVJBU0VfVEhFTl9IT01FX1BBVENIIiwibWFrZUFsdFNjcmVlblBhcmtQYXRjaCIsInRlcm1pbmFsUm93cyIsIk9wdGlvbnMiLCJzdGRvdXQiLCJOb2RlSlMiLCJXcml0ZVN0cmVhbSIsInN0ZGluIiwiUmVhZFN0cmVhbSIsInN0ZGVyciIsImV4aXRPbkN0cmxDIiwicGF0Y2hDb25zb2xlIiwid2FpdFVudGlsRXhpdCIsIlByb21pc2UiLCJvbkZyYW1lIiwiZXZlbnQiLCJJbmsiLCJsb2ciLCJ0ZXJtaW5hbCIsInNjaGVkdWxlUmVuZGVyIiwiY2FuY2VsIiwiaXNVbm1vdW50ZWQiLCJpc1BhdXNlZCIsImNvbnRhaW5lciIsInJvb3ROb2RlIiwiRE9NRWxlbWVudCIsImZvY3VzTWFuYWdlciIsInJlbmRlcmVyIiwic3R5bGVQb29sIiwiY2hhclBvb2wiLCJoeXBlcmxpbmtQb29sIiwiZXhpdFByb21pc2UiLCJyZXN0b3JlQ29uc29sZSIsInJlc3RvcmVTdGRlcnIiLCJ1bnN1YnNjcmliZVRUWUhhbmRsZXJzIiwidGVybWluYWxDb2x1bW5zIiwiY3VycmVudE5vZGUiLCJmcm9udEZyYW1lIiwiYmFja0ZyYW1lIiwibGFzdFBvb2xSZXNldFRpbWUiLCJwZXJmb3JtYW5jZSIsIm5vdyIsImRyYWluVGltZXIiLCJSZXR1cm5UeXBlIiwic2V0VGltZW91dCIsImxhc3RZb2dhQ291bnRlcnMiLCJtcyIsInZpc2l0ZWQiLCJtZWFzdXJlZCIsImNhY2hlSGl0cyIsImxpdmUiLCJhbHRTY3JlZW5QYXJrUGF0Y2giLCJSZWFkb25seSIsInNlbGVjdGlvbiIsInNlYXJjaEhpZ2hsaWdodFF1ZXJ5Iiwic2VhcmNoUG9zaXRpb25zIiwicG9zaXRpb25zIiwicm93T2Zmc2V0IiwiY3VycmVudElkeCIsInNlbGVjdGlvbkxpc3RlbmVycyIsIlNldCIsImhvdmVyZWROb2RlcyIsImFsdFNjcmVlbkFjdGl2ZSIsImFsdFNjcmVlbk1vdXNlVHJhY2tpbmciLCJwcmV2RnJhbWVDb250YW1pbmF0ZWQiLCJuZWVkc0VyYXNlQmVmb3JlUGFpbnQiLCJjdXJzb3JEZWNsYXJhdGlvbiIsImRpc3BsYXlDdXJzb3IiLCJjb25zdHJ1Y3RvciIsIm9wdGlvbnMiLCJwYXRjaFN0ZGVyciIsImNvbHVtbnMiLCJyb3dzIiwiaXNUVFkiLCJkZWZlcnJlZFJlbmRlciIsInF1ZXVlTWljcm90YXNrIiwib25SZW5kZXIiLCJsZWFkaW5nIiwidHJhaWxpbmciLCJ1bnN1YnNjcmliZUV4aXQiLCJ1bm1vdW50IiwiYWx3YXlzTGFzdCIsIm9uIiwiaGFuZGxlUmVzaXplIiwicHJvY2VzcyIsImhhbmRsZVJlc3VtZSIsIm9mZiIsImNyZWF0ZU5vZGUiLCJ0YXJnZXQiLCJkaXNwYXRjaERpc2NyZXRlIiwib25JbW1lZGlhdGVSZW5kZXIiLCJvbkNvbXB1dGVMYXlvdXQiLCJ5b2dhTm9kZSIsInQwIiwic2V0V2lkdGgiLCJjYWxjdWxhdGVMYXlvdXQiLCJjIiwiY3JlYXRlQ29udGFpbmVyIiwiaW5qZWN0SW50b0RldlRvb2xzIiwiYnVuZGxlVHlwZSIsInZlcnNpb24iLCJyZW5kZXJlclBhY2thZ2VOYW1lIiwicmVlbnRlckFsdFNjcmVlbiIsInZpZXdwb3J0IiwiaGVpZ2h0Iiwid2lkdGgiLCJyZXNldCIsImNvbHMiLCJ3cml0ZSIsInJlc2V0RnJhbWVzRm9yQWx0U2NyZWVuIiwicmVuZGVyIiwicmVzb2x2ZUV4aXRQcm9taXNlIiwicmVqZWN0RXhpdFByb21pc2UiLCJyZWFzb24iLCJFcnJvciIsImVudGVyQWx0ZXJuYXRlU2NyZWVuIiwicGF1c2UiLCJzdXNwZW5kU3RkaW4iLCJleGl0QWx0ZXJuYXRlU2NyZWVuIiwicmVzdW1lU3RkaW4iLCJyZXBhaW50IiwicmVzdW1lIiwiY2xlYXJUaW1lb3V0IiwicmVuZGVyU3RhcnQiLCJ0ZXJtaW5hbFdpZHRoIiwiZnJhbWUiLCJhbHRTY3JlZW4iLCJyZW5kZXJlck1zIiwiZm9sbG93IiwiYW5jaG9yIiwicm93Iiwidmlld3BvcnRUb3AiLCJ2aWV3cG9ydEJvdHRvbSIsImRlbHRhIiwiaXNEcmFnZ2luZyIsInNjcmVlbiIsImZvY3VzIiwiY2xlYXJlZCIsImNiIiwic2VsQWN0aXZlIiwiaGxBY3RpdmUiLCJzcCIsInBvc0FwcGxpZWQiLCJkYW1hZ2UiLCJwcmV2RnJhbWUiLCJjdXJzb3IiLCJ0RGlmZiIsImRpZmYiLCJkaWZmTXMiLCJyZXNldFBvb2xzIiwiZmxpY2tlcnMiLCJwYXRjaCIsInB1c2giLCJkZXNpcmVkSGVpZ2h0IiwiYXZhaWxhYmxlSGVpZ2h0IiwiZGVidWciLCJjaGFpbiIsImZpbmRPd25lckNoYWluQXRSb3ciLCJ0cmlnZ2VyWSIsInByZXZMaW5lIiwibmV4dExpbmUiLCJsZW5ndGgiLCJqb2luIiwibGV2ZWwiLCJ0T3B0aW1pemUiLCJvcHRpbWl6ZWQiLCJvcHRpbWl6ZU1zIiwiaGFzRGlmZiIsInVuc2hpZnQiLCJkZWNsIiwicmVjdCIsImdldCIsIm5vZGUiLCJ1bmRlZmluZWQiLCJyZWxhdGl2ZVgiLCJyZWxhdGl2ZVkiLCJwYXJrZWQiLCJ0YXJnZXRNb3ZlZCIsInBkeCIsInBkeSIsIk1hdGgiLCJtaW4iLCJtYXgiLCJjb2wiLCJmcm9tIiwiZHgiLCJkeSIsInJkeCIsInJkeSIsInRXcml0ZSIsIndyaXRlTXMiLCJzY3JvbGxEcmFpblBlbmRpbmciLCJ5b2dhTXMiLCJjb21taXRNcyIsInljIiwiZHVyYXRpb25NcyIsInBoYXNlcyIsInBhdGNoZXMiLCJ5b2dhIiwiY29tbWl0IiwieW9nYVZpc2l0ZWQiLCJ5b2dhTWVhc3VyZWQiLCJ5b2dhQ2FjaGVIaXRzIiwieW9nYUxpdmUiLCJmbHVzaFN5bmNGcm9tUmVjb25jaWxlciIsImZvcmNlUmVkcmF3IiwiaW52YWxpZGF0ZVByZXZGcmFtZSIsInNldEFsdFNjcmVlbkFjdGl2ZSIsImFjdGl2ZSIsIm1vdXNlVHJhY2tpbmciLCJpc0FsdFNjcmVlbkFjdGl2ZSIsInJlYXNzZXJ0VGVybWluYWxNb2RlcyIsImluY2x1ZGVBbHRTY3JlZW4iLCJkZXRhY2hGb3JTaHV0ZG93biIsImlzUmF3Iiwic2V0UmF3TW9kZSIsIm0iLCJkcmFpblN0ZGluIiwiYmxhbmsiLCJjb3B5U2VsZWN0aW9uTm9DbGVhciIsInRleHQiLCJ0aGVuIiwicmF3IiwiY29weVNlbGVjdGlvbiIsIm5vdGlmeVNlbGVjdGlvbkNoYW5nZSIsImNsZWFyVGV4dFNlbGVjdGlvbiIsInNldFNlYXJjaEhpZ2hsaWdodCIsInF1ZXJ5Iiwic2NhbkVsZW1lbnRTdWJ0cmVlIiwiZWwiLCJjZWlsIiwiZ2V0Q29tcHV0ZWRXaWR0aCIsImdldENvbXB1dGVkSGVpZ2h0IiwiZWxMZWZ0IiwiZ2V0Q29tcHV0ZWRMZWZ0IiwiZWxUb3AiLCJnZXRDb21wdXRlZFRvcCIsIm91dHB1dCIsIm9mZnNldFgiLCJvZmZzZXRZIiwicHJldlNjcmVlbiIsInJlbmRlcmVkIiwibWFya0RpcnR5Iiwic2xpY2UiLCJtYXAiLCJwIiwic2V0U2VhcmNoUG9zaXRpb25zIiwic3RhdGUiLCJzZXRTZWxlY3Rpb25CZ0NvbG9yIiwiY29sb3IiLCJ3cmFwcGVkIiwibnVsIiwiaW5kZXhPZiIsInNldFNlbGVjdGlvbkJnIiwiY29kZSIsImVuZENvZGUiLCJmaXJzdFJvdyIsImxhc3RSb3ciLCJzaWRlIiwic2hpZnRTZWxlY3Rpb25Gb3JTY3JvbGwiLCJkUm93IiwibWluUm93IiwibWF4Um93IiwiaGFkU2VsIiwibW92ZVNlbGVjdGlvbkZvY3VzIiwibW92ZSIsIm1heENvbCIsImhhc1RleHRTZWxlY3Rpb24iLCJzdWJzY3JpYmVUb1NlbGVjdGlvbkNoYW5nZSIsImFkZCIsImRlbGV0ZSIsImRpc3BhdGNoS2V5Ym9hcmRFdmVudCIsInBhcnNlZEtleSIsImFjdGl2ZUVsZW1lbnQiLCJkZWZhdWx0UHJldmVudGVkIiwibmFtZSIsImN0cmwiLCJtZXRhIiwic2hpZnQiLCJmb2N1c1ByZXZpb3VzIiwiZm9jdXNOZXh0IiwiZ2V0SHlwZXJsaW5rQXQiLCJjZWxsIiwidXJsIiwiaHlwZXJsaW5rIiwiU3BhY2VyVGFpbCIsIm9uSHlwZXJsaW5rQ2xpY2siLCJvcGVuSHlwZXJsaW5rIiwiaGFuZGxlTXVsdGlDbGljayIsImNvdW50IiwiaGFuZGxlU2VsZWN0aW9uRHJhZyIsInNlbCIsImFuY2hvclNwYW4iLCJzdGRpbkxpc3RlbmVycyIsIkFycmF5IiwibGlzdGVuZXIiLCJhcmdzIiwid2FzUmF3TW9kZSIsInJlYWRhYmxlTGlzdGVuZXJzIiwibGlzdGVuZXJzIiwiZm9yRWFjaCIsInJlbW92ZUxpc3RlbmVyIiwic3RkaW5XaXRoUmF3IiwibW9kZSIsImFkZExpc3RlbmVyIiwid3JpdGVSYXciLCJkYXRhIiwic2V0Q3Vyc29yRGVjbGFyYXRpb24iLCJjbGVhcklmTm9kZSIsInRyZWUiLCJ1cGRhdGVDb250YWluZXJTeW5jIiwiZmx1c2hTeW5jV29yayIsImVycm9yIiwicmVuZGVyUHJldmlvdXNPdXRwdXRfREVQUkVDQVRFRCIsImZyZWUiLCJyZXNvbHZlIiwicmVqZWN0IiwicmVzZXRMaW5lQ291bnQiLCJjb24iLCJjb25zb2xlIiwib3JpZ2luYWxzIiwiUGFydGlhbCIsIlJlY29yZCIsIkNvbnNvbGUiLCJ0b0RlYnVnIiwidG9FcnJvciIsIkNPTlNPTEVfU1RET1VUX01FVEhPRFMiLCJDT05TT0xFX1NUREVSUl9NRVRIT0RTIiwiYXNzZXJ0IiwiY29uZGl0aW9uIiwiYXNzaWduIiwib3JpZ2luYWxXcml0ZSIsInJlZW50ZXJlZCIsImludGVyY2VwdCIsImNodW5rIiwiVWludDhBcnJheSIsImVuY29kaW5nT3JDYiIsIkJ1ZmZlckVuY29kaW5nIiwiZXJyIiwiY2FsbGJhY2siLCJlbmNvZGluZyIsImNhbGwiLCJCdWZmZXIiLCJ0b1N0cmluZyIsInJlYWQiLCJwbGF0Zm9ybSIsInR0eSIsIndhc1JhdyIsImZkIiwiT19SRE9OTFkiLCJPX05PTkJMT0NLIiwiYnVmIiwiYWxsb2MiLCJpIl0sInNvdXJjZXMiOlsiaW5rLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgYXV0b0JpbmQgZnJvbSAnYXV0by1iaW5kJ1xuaW1wb3J0IHtcbiAgY2xvc2VTeW5jLFxuICBjb25zdGFudHMgYXMgZnNDb25zdGFudHMsXG4gIG9wZW5TeW5jLFxuICByZWFkU3luYyxcbiAgd3JpdGVTeW5jLFxufSBmcm9tICdmcydcbmltcG9ydCBub29wIGZyb20gJ2xvZGFzaC1lcy9ub29wLmpzJ1xuaW1wb3J0IHRocm90dGxlIGZyb20gJ2xvZGFzaC1lcy90aHJvdHRsZS5qcydcbmltcG9ydCBSZWFjdCwgeyB0eXBlIFJlYWN0Tm9kZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBGaWJlclJvb3QgfSBmcm9tICdyZWFjdC1yZWNvbmNpbGVyJ1xuaW1wb3J0IHsgQ29uY3VycmVudFJvb3QgfSBmcm9tICdyZWFjdC1yZWNvbmNpbGVyL2NvbnN0YW50cy5qcydcbmltcG9ydCB7IG9uRXhpdCB9IGZyb20gJ3NpZ25hbC1leGl0J1xuaW1wb3J0IHsgZmx1c2hJbnRlcmFjdGlvblRpbWUgfSBmcm9tICdzcmMvYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHsgZ2V0WW9nYUNvdW50ZXJzIH0gZnJvbSAnc3JjL25hdGl2ZS10cy95b2dhLWxheW91dC9pbmRleC5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJ3NyYy91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnc3JjL3V0aWxzL2xvZy5qcydcbmltcG9ydCB7IGZvcm1hdCB9IGZyb20gJ3V0aWwnXG5pbXBvcnQgeyBjb2xvcml6ZSB9IGZyb20gJy4vY29sb3JpemUuanMnXG5pbXBvcnQgQXBwIGZyb20gJy4vY29tcG9uZW50cy9BcHAuanMnXG5pbXBvcnQgdHlwZSB7XG4gIEN1cnNvckRlY2xhcmF0aW9uLFxuICBDdXJzb3JEZWNsYXJhdGlvblNldHRlcixcbn0gZnJvbSAnLi9jb21wb25lbnRzL0N1cnNvckRlY2xhcmF0aW9uQ29udGV4dC5qcydcbmltcG9ydCB7IEZSQU1FX0lOVEVSVkFMX01TIH0gZnJvbSAnLi9jb25zdGFudHMuanMnXG5pbXBvcnQgKiBhcyBkb20gZnJvbSAnLi9kb20uanMnXG5pbXBvcnQgeyBLZXlib2FyZEV2ZW50IH0gZnJvbSAnLi9ldmVudHMva2V5Ym9hcmQtZXZlbnQuanMnXG5pbXBvcnQgeyBGb2N1c01hbmFnZXIgfSBmcm9tICcuL2ZvY3VzLmpzJ1xuaW1wb3J0IHsgZW1wdHlGcmFtZSwgdHlwZSBGcmFtZSwgdHlwZSBGcmFtZUV2ZW50IH0gZnJvbSAnLi9mcmFtZS5qcydcbmltcG9ydCB7IGRpc3BhdGNoQ2xpY2ssIGRpc3BhdGNoSG92ZXIgfSBmcm9tICcuL2hpdC10ZXN0LmpzJ1xuaW1wb3J0IGluc3RhbmNlcyBmcm9tICcuL2luc3RhbmNlcy5qcydcbmltcG9ydCB7IExvZ1VwZGF0ZSB9IGZyb20gJy4vbG9nLXVwZGF0ZS5qcydcbmltcG9ydCB7IG5vZGVDYWNoZSB9IGZyb20gJy4vbm9kZS1jYWNoZS5qcydcbmltcG9ydCB7IG9wdGltaXplIH0gZnJvbSAnLi9vcHRpbWl6ZXIuanMnXG5pbXBvcnQgT3V0cHV0IGZyb20gJy4vb3V0cHV0LmpzJ1xuaW1wb3J0IHR5cGUgeyBQYXJzZWRLZXkgfSBmcm9tICcuL3BhcnNlLWtleXByZXNzLmpzJ1xuaW1wb3J0IHJlY29uY2lsZXIsIHtcbiAgZGlzcGF0Y2hlcixcbiAgZ2V0TGFzdENvbW1pdE1zLFxuICBnZXRMYXN0WW9nYU1zLFxuICBpc0RlYnVnUmVwYWludHNFbmFibGVkLFxuICByZWNvcmRZb2dhTXMsXG4gIHJlc2V0UHJvZmlsZUNvdW50ZXJzLFxufSBmcm9tICcuL3JlY29uY2lsZXIuanMnXG5pbXBvcnQgcmVuZGVyTm9kZVRvT3V0cHV0LCB7XG4gIGNvbnN1bWVGb2xsb3dTY3JvbGwsXG4gIGRpZExheW91dFNoaWZ0LFxufSBmcm9tICcuL3JlbmRlci1ub2RlLXRvLW91dHB1dC5qcydcbmltcG9ydCB7XG4gIGFwcGx5UG9zaXRpb25lZEhpZ2hsaWdodCxcbiAgdHlwZSBNYXRjaFBvc2l0aW9uLFxuICBzY2FuUG9zaXRpb25zLFxufSBmcm9tICcuL3JlbmRlci10by1zY3JlZW4uanMnXG5pbXBvcnQgY3JlYXRlUmVuZGVyZXIsIHsgdHlwZSBSZW5kZXJlciB9IGZyb20gJy4vcmVuZGVyZXIuanMnXG5pbXBvcnQge1xuICBDZWxsV2lkdGgsXG4gIENoYXJQb29sLFxuICBjZWxsQXQsXG4gIGNyZWF0ZVNjcmVlbixcbiAgSHlwZXJsaW5rUG9vbCxcbiAgaXNFbXB0eUNlbGxBdCxcbiAgbWlncmF0ZVNjcmVlblBvb2xzLFxuICBTdHlsZVBvb2wsXG59IGZyb20gJy4vc2NyZWVuLmpzJ1xuaW1wb3J0IHsgYXBwbHlTZWFyY2hIaWdobGlnaHQgfSBmcm9tICcuL3NlYXJjaEhpZ2hsaWdodC5qcydcbmltcG9ydCB7XG4gIGFwcGx5U2VsZWN0aW9uT3ZlcmxheSxcbiAgY2FwdHVyZVNjcm9sbGVkUm93cyxcbiAgY2xlYXJTZWxlY3Rpb24sXG4gIGNyZWF0ZVNlbGVjdGlvblN0YXRlLFxuICBleHRlbmRTZWxlY3Rpb24sXG4gIHR5cGUgRm9jdXNNb3ZlLFxuICBmaW5kUGxhaW5UZXh0VXJsQXQsXG4gIGdldFNlbGVjdGVkVGV4dCxcbiAgaGFzU2VsZWN0aW9uLFxuICBtb3ZlRm9jdXMsXG4gIHR5cGUgU2VsZWN0aW9uU3RhdGUsXG4gIHNlbGVjdExpbmVBdCxcbiAgc2VsZWN0V29yZEF0LFxuICBzaGlmdEFuY2hvcixcbiAgc2hpZnRTZWxlY3Rpb24sXG4gIHNoaWZ0U2VsZWN0aW9uRm9yRm9sbG93LFxuICBzdGFydFNlbGVjdGlvbixcbiAgdXBkYXRlU2VsZWN0aW9uLFxufSBmcm9tICcuL3NlbGVjdGlvbi5qcydcbmltcG9ydCB7XG4gIFNZTkNfT1VUUFVUX1NVUFBPUlRFRCxcbiAgc3VwcG9ydHNFeHRlbmRlZEtleXMsXG4gIHR5cGUgVGVybWluYWwsXG4gIHdyaXRlRGlmZlRvVGVybWluYWwsXG59IGZyb20gJy4vdGVybWluYWwuanMnXG5pbXBvcnQge1xuICBDVVJTT1JfSE9NRSxcbiAgY3Vyc29yTW92ZSxcbiAgY3Vyc29yUG9zaXRpb24sXG4gIERJU0FCTEVfS0lUVFlfS0VZQk9BUkQsXG4gIERJU0FCTEVfTU9ESUZZX09USEVSX0tFWVMsXG4gIEVOQUJMRV9LSVRUWV9LRVlCT0FSRCxcbiAgRU5BQkxFX01PRElGWV9PVEhFUl9LRVlTLFxuICBFUkFTRV9TQ1JFRU4sXG59IGZyb20gJy4vdGVybWlvL2NzaS5qcydcbmltcG9ydCB7XG4gIERCUCxcbiAgREZFLFxuICBESVNBQkxFX01PVVNFX1RSQUNLSU5HLFxuICBFTkFCTEVfTU9VU0VfVFJBQ0tJTkcsXG4gIEVOVEVSX0FMVF9TQ1JFRU4sXG4gIEVYSVRfQUxUX1NDUkVFTixcbiAgU0hPV19DVVJTT1IsXG59IGZyb20gJy4vdGVybWlvL2RlYy5qcydcbmltcG9ydCB7XG4gIENMRUFSX0lURVJNMl9QUk9HUkVTUyxcbiAgQ0xFQVJfVEFCX1NUQVRVUyxcbiAgc2V0Q2xpcGJvYXJkLFxuICBzdXBwb3J0c1RhYlN0YXR1cyxcbiAgd3JhcEZvck11bHRpcGxleGVyLFxufSBmcm9tICcuL3Rlcm1pby9vc2MuanMnXG5pbXBvcnQgeyBUZXJtaW5hbFdyaXRlUHJvdmlkZXIgfSBmcm9tICcuL3VzZVRlcm1pbmFsTm90aWZpY2F0aW9uLmpzJ1xuXG4vLyBBbHQtc2NyZWVuOiByZW5kZXJlci50cyBzZXRzIGN1cnNvci52aXNpYmxlID0gIWlzVFRZIHx8IHNjcmVlbi5oZWlnaHQ9PT0wLFxuLy8gd2hpY2ggaXMgYWx3YXlzIGZhbHNlIGluIGFsdC1zY3JlZW4gKFRUWSArIGNvbnRlbnQgZmlsbHMgc2NyZWVuKS5cbi8vIFJldXNpbmcgYSBmcm96ZW4gb2JqZWN0IHNhdmVzIDEgYWxsb2NhdGlvbiBwZXIgZnJhbWUuXG5jb25zdCBBTFRfU0NSRUVOX0FOQ0hPUl9DVVJTT1IgPSBPYmplY3QuZnJlZXplKHsgeDogMCwgeTogMCwgdmlzaWJsZTogZmFsc2UgfSlcbmNvbnN0IENVUlNPUl9IT01FX1BBVENIID0gT2JqZWN0LmZyZWV6ZSh7XG4gIHR5cGU6ICdzdGRvdXQnIGFzIGNvbnN0LFxuICBjb250ZW50OiBDVVJTT1JfSE9NRSxcbn0pXG5jb25zdCBFUkFTRV9USEVOX0hPTUVfUEFUQ0ggPSBPYmplY3QuZnJlZXplKHtcbiAgdHlwZTogJ3N0ZG91dCcgYXMgY29uc3QsXG4gIGNvbnRlbnQ6IEVSQVNFX1NDUkVFTiArIENVUlNPUl9IT01FLFxufSlcblxuLy8gQ2FjaGVkIHBlci1JbmstaW5zdGFuY2UsIGludmFsaWRhdGVkIG9uIHJlc2l6ZS4gZnJhbWUuY3Vyc29yLnkgZm9yXG4vLyBhbHQtc2NyZWVuIGlzIGFsd2F5cyB0ZXJtaW5hbFJvd3MgLSAxIChyZW5kZXJlci50cykuXG5mdW5jdGlvbiBtYWtlQWx0U2NyZWVuUGFya1BhdGNoKHRlcm1pbmFsUm93czogbnVtYmVyKSB7XG4gIHJldHVybiBPYmplY3QuZnJlZXplKHtcbiAgICB0eXBlOiAnc3Rkb3V0JyBhcyBjb25zdCxcbiAgICBjb250ZW50OiBjdXJzb3JQb3NpdGlvbih0ZXJtaW5hbFJvd3MsIDEpLFxuICB9KVxufVxuXG5leHBvcnQgdHlwZSBPcHRpb25zID0ge1xuICBzdGRvdXQ6IE5vZGVKUy5Xcml0ZVN0cmVhbVxuICBzdGRpbjogTm9kZUpTLlJlYWRTdHJlYW1cbiAgc3RkZXJyOiBOb2RlSlMuV3JpdGVTdHJlYW1cbiAgZXhpdE9uQ3RybEM6IGJvb2xlYW5cbiAgcGF0Y2hDb25zb2xlOiBib29sZWFuXG4gIHdhaXRVbnRpbEV4aXQ/OiAoKSA9PiBQcm9taXNlPHZvaWQ+XG4gIG9uRnJhbWU/OiAoZXZlbnQ6IEZyYW1lRXZlbnQpID0+IHZvaWRcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgSW5rIHtcbiAgcHJpdmF0ZSByZWFkb25seSBsb2c6IExvZ1VwZGF0ZVxuICBwcml2YXRlIHJlYWRvbmx5IHRlcm1pbmFsOiBUZXJtaW5hbFxuICBwcml2YXRlIHNjaGVkdWxlUmVuZGVyOiAoKCkgPT4gdm9pZCkgJiB7IGNhbmNlbD86ICgpID0+IHZvaWQgfVxuICAvLyBJZ25vcmUgbGFzdCByZW5kZXIgYWZ0ZXIgdW5tb3VudGluZyBhIHRyZWUgdG8gcHJldmVudCBlbXB0eSBvdXRwdXQgYmVmb3JlIGV4aXRcbiAgcHJpdmF0ZSBpc1VubW91bnRlZCA9IGZhbHNlXG4gIHByaXZhdGUgaXNQYXVzZWQgPSBmYWxzZVxuICBwcml2YXRlIHJlYWRvbmx5IGNvbnRhaW5lcjogRmliZXJSb290XG4gIHByaXZhdGUgcm9vdE5vZGU6IGRvbS5ET01FbGVtZW50XG4gIHJlYWRvbmx5IGZvY3VzTWFuYWdlcjogRm9jdXNNYW5hZ2VyXG4gIHByaXZhdGUgcmVuZGVyZXI6IFJlbmRlcmVyXG4gIHByaXZhdGUgcmVhZG9ubHkgc3R5bGVQb29sOiBTdHlsZVBvb2xcbiAgcHJpdmF0ZSBjaGFyUG9vbDogQ2hhclBvb2xcbiAgcHJpdmF0ZSBoeXBlcmxpbmtQb29sOiBIeXBlcmxpbmtQb29sXG4gIHByaXZhdGUgZXhpdFByb21pc2U/OiBQcm9taXNlPHZvaWQ+XG4gIHByaXZhdGUgcmVzdG9yZUNvbnNvbGU/OiAoKSA9PiB2b2lkXG4gIHByaXZhdGUgcmVzdG9yZVN0ZGVycj86ICgpID0+IHZvaWRcbiAgcHJpdmF0ZSByZWFkb25seSB1bnN1YnNjcmliZVRUWUhhbmRsZXJzPzogKCkgPT4gdm9pZFxuICBwcml2YXRlIHRlcm1pbmFsQ29sdW1uczogbnVtYmVyXG4gIHByaXZhdGUgdGVybWluYWxSb3dzOiBudW1iZXJcbiAgcHJpdmF0ZSBjdXJyZW50Tm9kZTogUmVhY3ROb2RlID0gbnVsbFxuICBwcml2YXRlIGZyb250RnJhbWU6IEZyYW1lXG4gIHByaXZhdGUgYmFja0ZyYW1lOiBGcmFtZVxuICBwcml2YXRlIGxhc3RQb29sUmVzZXRUaW1lID0gcGVyZm9ybWFuY2Uubm93KClcbiAgcHJpdmF0ZSBkcmFpblRpbWVyOiBSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IG51bGwgPSBudWxsXG4gIHByaXZhdGUgbGFzdFlvZ2FDb3VudGVyczoge1xuICAgIG1zOiBudW1iZXJcbiAgICB2aXNpdGVkOiBudW1iZXJcbiAgICBtZWFzdXJlZDogbnVtYmVyXG4gICAgY2FjaGVIaXRzOiBudW1iZXJcbiAgICBsaXZlOiBudW1iZXJcbiAgfSA9IHsgbXM6IDAsIHZpc2l0ZWQ6IDAsIG1lYXN1cmVkOiAwLCBjYWNoZUhpdHM6IDAsIGxpdmU6IDAgfVxuICBwcml2YXRlIGFsdFNjcmVlblBhcmtQYXRjaDogUmVhZG9ubHk8eyB0eXBlOiAnc3Rkb3V0JzsgY29udGVudDogc3RyaW5nIH0+XG4gIC8vIFRleHQgc2VsZWN0aW9uIHN0YXRlIChhbHQtc2NyZWVuIG9ubHkpLiBPd25lZCBoZXJlIHNvIHRoZSBvdmVybGF5XG4gIC8vIHBhc3MgaW4gb25SZW5kZXIgY2FuIHJlYWQgaXQgYW5kIEFwcC50c3ggY2FuIHVwZGF0ZSBpdCBmcm9tIG1vdXNlXG4gIC8vIGV2ZW50cy4gUHVibGljIHNvIGluc3RhbmNlcy5nZXQoKSBjYWxsZXJzIGNhbiBhY2Nlc3MuXG4gIHJlYWRvbmx5IHNlbGVjdGlvbjogU2VsZWN0aW9uU3RhdGUgPSBjcmVhdGVTZWxlY3Rpb25TdGF0ZSgpXG4gIC8vIFNlYXJjaCBoaWdobGlnaHQgcXVlcnkgKGFsdC1zY3JlZW4gb25seSkuIFNldHRlciBiZWxvdyB0cmlnZ2Vyc1xuICAvLyBzY2hlZHVsZVJlbmRlcjsgYXBwbHlTZWFyY2hIaWdobGlnaHQgaW4gb25SZW5kZXIgaW52ZXJ0cyBtYXRjaGluZyBjZWxscy5cbiAgcHJpdmF0ZSBzZWFyY2hIaWdobGlnaHRRdWVyeSA9ICcnXG4gIC8vIFBvc2l0aW9uLWJhc2VkIGhpZ2hsaWdodC4gVk1MIHNjYW5zIHBvc2l0aW9ucyBPTkNFICh2aWFcbiAgLy8gc2NhbkVsZW1lbnRTdWJ0cmVlLCB3aGVuIHRoZSB0YXJnZXQgbWVzc2FnZSBpcyBtb3VudGVkKSwgc3RvcmVzIHRoZW1cbiAgLy8gbWVzc2FnZS1yZWxhdGl2ZSwgc2V0cyB0aGlzIGZvciBldmVyeS1mcmFtZSBhcHBseS4gcm93T2Zmc2V0ID1cbiAgLy8gbWVzc2FnZSdzIGN1cnJlbnQgc2NyZWVuLXRvcC4gY3VycmVudElkeCA9IHdoaWNoIHBvc2l0aW9uIGlzXG4gIC8vIFwiY3VycmVudFwiICh5ZWxsb3cpLiBudWxsIGNsZWFycy4gUG9zaXRpb25zIGFyZSBrbm93biB1cGZyb250IOKAlFxuICAvLyBuYXZpZ2F0aW9uIGlzIGluZGV4IGFyaXRobWV0aWMsIG5vIHNjYW4tZmVlZGJhY2sgbG9vcC5cbiAgcHJpdmF0ZSBzZWFyY2hQb3NpdGlvbnM6IHtcbiAgICBwb3NpdGlvbnM6IE1hdGNoUG9zaXRpb25bXVxuICAgIHJvd09mZnNldDogbnVtYmVyXG4gICAgY3VycmVudElkeDogbnVtYmVyXG4gIH0gfCBudWxsID0gbnVsbFxuICAvLyBSZWFjdC1sYW5kIHN1YnNjcmliZXJzIGZvciBzZWxlY3Rpb24gc3RhdGUgY2hhbmdlcyAodXNlSGFzU2VsZWN0aW9uKS5cbiAgLy8gRmlyZWQgYWxvbmdzaWRlIHRoZSB0ZXJtaW5hbCByZXBhaW50IHdoZW5ldmVyIHRoZSBzZWxlY3Rpb24gbXV0YXRlc1xuICAvLyBzbyBVSSAoZS5nLiBmb290ZXIgaGludHMpIGNhbiByZWFjdCB0byBzZWxlY3Rpb24gYXBwZWFyaW5nL2NsZWFyaW5nLlxuICBwcml2YXRlIHJlYWRvbmx5IHNlbGVjdGlvbkxpc3RlbmVycyA9IG5ldyBTZXQ8KCkgPT4gdm9pZD4oKVxuICAvLyBET00gbm9kZXMgY3VycmVudGx5IHVuZGVyIHRoZSBwb2ludGVyIChtb2RlLTEwMDMgbW90aW9uKS4gSGVsZCBoZXJlXG4gIC8vIHNvIEFwcC50c3gncyBoYW5kbGVNb3VzZUV2ZW50IGlzIHN0YXRlbGVzcyDigJQgZGlzcGF0Y2hIb3ZlciBkaWZmc1xuICAvLyBhZ2FpbnN0IHRoaXMgc2V0IGFuZCBtdXRhdGVzIGl0IGluIHBsYWNlLlxuICBwcml2YXRlIHJlYWRvbmx5IGhvdmVyZWROb2RlcyA9IG5ldyBTZXQ8ZG9tLkRPTUVsZW1lbnQ+KClcbiAgLy8gU2V0IGJ5IDxBbHRlcm5hdGVTY3JlZW4+IHZpYSBzZXRBbHRTY3JlZW5BY3RpdmUoKS4gQ29udHJvbHMgdGhlXG4gIC8vIHJlbmRlcmVyJ3MgY3Vyc29yLnkgY2xhbXBpbmcgKGtlZXBzIGN1cnNvciBpbi12aWV3cG9ydCB0byBhdm9pZFxuICAvLyBMRi1pbmR1Y2VkIHNjcm9sbCB3aGVuIHNjcmVlbi5oZWlnaHQgPT09IHRlcm1pbmFsUm93cykgYW5kIGdhdGVzXG4gIC8vIGFsdC1zY3JlZW4tYXdhcmUgU0lHQ09OVC9yZXNpemUvdW5tb3VudCBoYW5kbGluZy5cbiAgcHJpdmF0ZSBhbHRTY3JlZW5BY3RpdmUgPSBmYWxzZVxuICAvLyBTZXQgYWxvbmdzaWRlIGFsdFNjcmVlbkFjdGl2ZSBzbyBTSUdDT05UIHJlc3VtZSBrbm93cyB3aGV0aGVyIHRvXG4gIC8vIHJlLWVuYWJsZSBtb3VzZSB0cmFja2luZyAobm90IGFsbCA8QWx0ZXJuYXRlU2NyZWVuPiB1c2VzIHdhbnQgaXQpLlxuICBwcml2YXRlIGFsdFNjcmVlbk1vdXNlVHJhY2tpbmcgPSBmYWxzZVxuICAvLyBUcnVlIHdoZW4gdGhlIHByZXZpb3VzIGZyYW1lJ3Mgc2NyZWVuIGJ1ZmZlciBjYW5ub3QgYmUgdHJ1c3RlZCBmb3JcbiAgLy8gYmxpdCDigJQgc2VsZWN0aW9uIG92ZXJsYXkgbXV0YXRlZCBpdCwgcmVzZXRGcmFtZXNGb3JBbHRTY3JlZW4oKVxuICAvLyByZXBsYWNlZCBpdCB3aXRoIGJsYW5rcywgb3IgZm9yY2VSZWRyYXcoKSByZXNldCBpdCB0byAww5cwLiBGb3JjZXNcbiAgLy8gb25lIGZ1bGwtcmVuZGVyIGZyYW1lOyBzdGVhZHktc3RhdGUgZnJhbWVzIGFmdGVyIGNsZWFyIGl0IGFuZCByZWdhaW5cbiAgLy8gdGhlIGJsaXQgKyBuYXJyb3ctZGFtYWdlIGZhc3QgcGF0aC5cbiAgcHJpdmF0ZSBwcmV2RnJhbWVDb250YW1pbmF0ZWQgPSBmYWxzZVxuICAvLyBTZXQgYnkgaGFuZGxlUmVzaXplOiBwcmVwZW5kIEVSQVNFX1NDUkVFTiB0byB0aGUgbmV4dCBvblJlbmRlcidzIHBhdGNoZXNcbiAgLy8gSU5TSURFIHRoZSBCU1UvRVNVIGJsb2NrIHNvIGNsZWFyK3BhaW50IGlzIGF0b21pYy4gV3JpdGluZyBFUkFTRV9TQ1JFRU5cbiAgLy8gc3luY2hyb25vdXNseSBpbiBoYW5kbGVSZXNpemUgd291bGQgbGVhdmUgdGhlIHNjcmVlbiBibGFuayBmb3IgdGhlIH44MG1zXG4gIC8vIHJlbmRlcigpIHRha2VzOyBkZWZlcnJpbmcgaW50byB0aGUgYXRvbWljIGJsb2NrIG1lYW5zIG9sZCBjb250ZW50IHN0YXlzXG4gIC8vIHZpc2libGUgdW50aWwgdGhlIG5ldyBmcmFtZSBpcyBmdWxseSByZWFkeS5cbiAgcHJpdmF0ZSBuZWVkc0VyYXNlQmVmb3JlUGFpbnQgPSBmYWxzZVxuICAvLyBOYXRpdmUgY3Vyc29yIHBvc2l0aW9uaW5nOiBhIGNvbXBvbmVudCAodmlhIHVzZURlY2xhcmVkQ3Vyc29yKSBkZWNsYXJlc1xuICAvLyB3aGVyZSB0aGUgdGVybWluYWwgY3Vyc29yIHNob3VsZCBiZSBwYXJrZWQgYWZ0ZXIgZWFjaCBmcmFtZS4gVGVybWluYWxcbiAgLy8gZW11bGF0b3JzIHJlbmRlciBJTUUgcHJlZWRpdCB0ZXh0IGF0IHRoZSBwaHlzaWNhbCBjdXJzb3IgcG9zaXRpb24sIGFuZFxuICAvLyBzY3JlZW4gcmVhZGVycyAvIHNjcmVlbiBtYWduaWZpZXJzIHRyYWNrIGl0IOKAlCBzbyBwYXJraW5nIGF0IHRoZSB0ZXh0XG4gIC8vIGlucHV0J3MgY2FyZXQgbWFrZXMgQ0pLIGlucHV0IGFwcGVhciBpbmxpbmUgYW5kIGxldHMgYTExeSB0b29scyBmb2xsb3cuXG4gIHByaXZhdGUgY3Vyc29yRGVjbGFyYXRpb246IEN1cnNvckRlY2xhcmF0aW9uIHwgbnVsbCA9IG51bGxcbiAgLy8gTWFpbi1zY3JlZW46IHBoeXNpY2FsIGN1cnNvciBwb3NpdGlvbiBhZnRlciB0aGUgZGVjbGFyZWQtY3Vyc29yIG1vdmUsXG4gIC8vIHRyYWNrZWQgc2VwYXJhdGVseSBmcm9tIGZyYW1lLmN1cnNvciAod2hpY2ggbXVzdCBzdGF5IGF0IGNvbnRlbnQtYm90dG9tXG4gIC8vIGZvciBsb2ctdXBkYXRlJ3MgcmVsYXRpdmUtbW92ZSBpbnZhcmlhbnRzKS4gQWx0LXNjcmVlbiBkb2Vzbid0IG5lZWRcbiAgLy8gdGhpcyDigJQgZXZlcnkgZnJhbWUgYmVnaW5zIHdpdGggQ1NJIEguIG51bGwgPSBubyBtb3ZlIGVtaXR0ZWQgbGFzdCBmcmFtZS5cbiAgcHJpdmF0ZSBkaXNwbGF5Q3Vyc29yOiB7IHg6IG51bWJlcjsgeTogbnVtYmVyIH0gfCBudWxsID0gbnVsbFxuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVhZG9ubHkgb3B0aW9uczogT3B0aW9ucykge1xuICAgIGF1dG9CaW5kKHRoaXMpXG5cbiAgICBpZiAodGhpcy5vcHRpb25zLnBhdGNoQ29uc29sZSkge1xuICAgICAgdGhpcy5yZXN0b3JlQ29uc29sZSA9IHRoaXMucGF0Y2hDb25zb2xlKClcbiAgICAgIHRoaXMucmVzdG9yZVN0ZGVyciA9IHRoaXMucGF0Y2hTdGRlcnIoKVxuICAgIH1cblxuICAgIHRoaXMudGVybWluYWwgPSB7XG4gICAgICBzdGRvdXQ6IG9wdGlvbnMuc3Rkb3V0LFxuICAgICAgc3RkZXJyOiBvcHRpb25zLnN0ZGVycixcbiAgICB9XG5cbiAgICB0aGlzLnRlcm1pbmFsQ29sdW1ucyA9IG9wdGlvbnMuc3Rkb3V0LmNvbHVtbnMgfHwgODBcbiAgICB0aGlzLnRlcm1pbmFsUm93cyA9IG9wdGlvbnMuc3Rkb3V0LnJvd3MgfHwgMjRcbiAgICB0aGlzLmFsdFNjcmVlblBhcmtQYXRjaCA9IG1ha2VBbHRTY3JlZW5QYXJrUGF0Y2godGhpcy50ZXJtaW5hbFJvd3MpXG4gICAgdGhpcy5zdHlsZVBvb2wgPSBuZXcgU3R5bGVQb29sKClcbiAgICB0aGlzLmNoYXJQb29sID0gbmV3IENoYXJQb29sKClcbiAgICB0aGlzLmh5cGVybGlua1Bvb2wgPSBuZXcgSHlwZXJsaW5rUG9vbCgpXG4gICAgdGhpcy5mcm9udEZyYW1lID0gZW1wdHlGcmFtZShcbiAgICAgIHRoaXMudGVybWluYWxSb3dzLFxuICAgICAgdGhpcy50ZXJtaW5hbENvbHVtbnMsXG4gICAgICB0aGlzLnN0eWxlUG9vbCxcbiAgICAgIHRoaXMuY2hhclBvb2wsXG4gICAgICB0aGlzLmh5cGVybGlua1Bvb2wsXG4gICAgKVxuICAgIHRoaXMuYmFja0ZyYW1lID0gZW1wdHlGcmFtZShcbiAgICAgIHRoaXMudGVybWluYWxSb3dzLFxuICAgICAgdGhpcy50ZXJtaW5hbENvbHVtbnMsXG4gICAgICB0aGlzLnN0eWxlUG9vbCxcbiAgICAgIHRoaXMuY2hhclBvb2wsXG4gICAgICB0aGlzLmh5cGVybGlua1Bvb2wsXG4gICAgKVxuXG4gICAgdGhpcy5sb2cgPSBuZXcgTG9nVXBkYXRlKHtcbiAgICAgIGlzVFRZOiAob3B0aW9ucy5zdGRvdXQuaXNUVFkgYXMgYm9vbGVhbiB8IHVuZGVmaW5lZCkgfHwgZmFsc2UsXG4gICAgICBzdHlsZVBvb2w6IHRoaXMuc3R5bGVQb29sLFxuICAgIH0pXG5cbiAgICAvLyBzY2hlZHVsZVJlbmRlciBpcyBjYWxsZWQgZnJvbSB0aGUgcmVjb25jaWxlcidzIHJlc2V0QWZ0ZXJDb21taXQsIHdoaWNoXG4gICAgLy8gcnVucyBCRUZPUkUgUmVhY3QncyBsYXlvdXQgcGhhc2UgKHJlZiBhdHRhY2ggKyB1c2VMYXlvdXRFZmZlY3QpLiBBbnlcbiAgICAvLyBzdGF0ZSBzZXQgaW4gbGF5b3V0IGVmZmVjdHMg4oCUIG5vdGFibHkgdGhlIGN1cnNvckRlY2xhcmF0aW9uIGZyb21cbiAgICAvLyB1c2VEZWNsYXJlZEN1cnNvciDigJQgd291bGQgbGFnIG9uZSBjb21taXQgYmVoaW5kIGlmIHdlIHJlbmRlcmVkXG4gICAgLy8gc3luY2hyb25vdXNseS4gRGVmZXJyaW5nIHRvIGEgbWljcm90YXNrIHJ1bnMgb25SZW5kZXIgYWZ0ZXIgbGF5b3V0XG4gICAgLy8gZWZmZWN0cyBoYXZlIGNvbW1pdHRlZCwgc28gdGhlIG5hdGl2ZSBjdXJzb3IgdHJhY2tzIHRoZSBjYXJldCB3aXRob3V0XG4gICAgLy8gYSBvbmUta2V5c3Ryb2tlIGxhZy4gU2FtZSBldmVudC1sb29wIHRpY2ssIHNvIHRocm91Z2hwdXQgaXMgdW5jaGFuZ2VkLlxuICAgIC8vIFRlc3QgZW52IHVzZXMgb25JbW1lZGlhdGVSZW5kZXIgKGRpcmVjdCBvblJlbmRlciwgbm8gdGhyb3R0bGUpIHNvXG4gICAgLy8gZXhpc3Rpbmcgc3luY2hyb25vdXMgbGFzdEZyYW1lKCkgdGVzdHMgYXJlIHVuYWZmZWN0ZWQuXG4gICAgY29uc3QgZGVmZXJyZWRSZW5kZXIgPSAoKTogdm9pZCA9PiBxdWV1ZU1pY3JvdGFzayh0aGlzLm9uUmVuZGVyKVxuICAgIHRoaXMuc2NoZWR1bGVSZW5kZXIgPSB0aHJvdHRsZShkZWZlcnJlZFJlbmRlciwgRlJBTUVfSU5URVJWQUxfTVMsIHtcbiAgICAgIGxlYWRpbmc6IHRydWUsXG4gICAgICB0cmFpbGluZzogdHJ1ZSxcbiAgICB9KVxuXG4gICAgLy8gSWdub3JlIGxhc3QgcmVuZGVyIGFmdGVyIHVubW91bnRpbmcgYSB0cmVlIHRvIHByZXZlbnQgZW1wdHkgb3V0cHV0IGJlZm9yZSBleGl0XG4gICAgdGhpcy5pc1VubW91bnRlZCA9IGZhbHNlXG5cbiAgICAvLyBVbm1vdW50IHdoZW4gcHJvY2VzcyBleGl0c1xuICAgIHRoaXMudW5zdWJzY3JpYmVFeGl0ID0gb25FeGl0KHRoaXMudW5tb3VudCwgeyBhbHdheXNMYXN0OiBmYWxzZSB9KVxuXG4gICAgaWYgKG9wdGlvbnMuc3Rkb3V0LmlzVFRZKSB7XG4gICAgICBvcHRpb25zLnN0ZG91dC5vbigncmVzaXplJywgdGhpcy5oYW5kbGVSZXNpemUpXG4gICAgICBwcm9jZXNzLm9uKCdTSUdDT05UJywgdGhpcy5oYW5kbGVSZXN1bWUpXG5cbiAgICAgIHRoaXMudW5zdWJzY3JpYmVUVFlIYW5kbGVycyA9ICgpID0+IHtcbiAgICAgICAgb3B0aW9ucy5zdGRvdXQub2ZmKCdyZXNpemUnLCB0aGlzLmhhbmRsZVJlc2l6ZSlcbiAgICAgICAgcHJvY2Vzcy5vZmYoJ1NJR0NPTlQnLCB0aGlzLmhhbmRsZVJlc3VtZSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLnJvb3ROb2RlID0gZG9tLmNyZWF0ZU5vZGUoJ2luay1yb290JylcbiAgICB0aGlzLmZvY3VzTWFuYWdlciA9IG5ldyBGb2N1c01hbmFnZXIoKHRhcmdldCwgZXZlbnQpID0+XG4gICAgICBkaXNwYXRjaGVyLmRpc3BhdGNoRGlzY3JldGUodGFyZ2V0LCBldmVudCksXG4gICAgKVxuICAgIHRoaXMucm9vdE5vZGUuZm9jdXNNYW5hZ2VyID0gdGhpcy5mb2N1c01hbmFnZXJcbiAgICB0aGlzLnJlbmRlcmVyID0gY3JlYXRlUmVuZGVyZXIodGhpcy5yb290Tm9kZSwgdGhpcy5zdHlsZVBvb2wpXG4gICAgdGhpcy5yb290Tm9kZS5vblJlbmRlciA9IHRoaXMuc2NoZWR1bGVSZW5kZXJcbiAgICB0aGlzLnJvb3ROb2RlLm9uSW1tZWRpYXRlUmVuZGVyID0gdGhpcy5vblJlbmRlclxuICAgIHRoaXMucm9vdE5vZGUub25Db21wdXRlTGF5b3V0ID0gKCkgPT4ge1xuICAgICAgLy8gQ2FsY3VsYXRlIGxheW91dCBkdXJpbmcgUmVhY3QncyBjb21taXQgcGhhc2Ugc28gdXNlTGF5b3V0RWZmZWN0IGhvb2tzXG4gICAgICAvLyBoYXZlIGFjY2VzcyB0byBmcmVzaCBsYXlvdXQgZGF0YVxuICAgICAgLy8gR3VhcmQgYWdhaW5zdCBhY2Nlc3NpbmcgZnJlZWQgWW9nYSBub2RlcyBhZnRlciB1bm1vdW50XG4gICAgICBpZiAodGhpcy5pc1VubW91bnRlZCkge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgaWYgKHRoaXMucm9vdE5vZGUueW9nYU5vZGUpIHtcbiAgICAgICAgY29uc3QgdDAgPSBwZXJmb3JtYW5jZS5ub3coKVxuICAgICAgICB0aGlzLnJvb3ROb2RlLnlvZ2FOb2RlLnNldFdpZHRoKHRoaXMudGVybWluYWxDb2x1bW5zKVxuICAgICAgICB0aGlzLnJvb3ROb2RlLnlvZ2FOb2RlLmNhbGN1bGF0ZUxheW91dCh0aGlzLnRlcm1pbmFsQ29sdW1ucylcbiAgICAgICAgY29uc3QgbXMgPSBwZXJmb3JtYW5jZS5ub3coKSAtIHQwXG4gICAgICAgIHJlY29yZFlvZ2FNcyhtcylcbiAgICAgICAgY29uc3QgYyA9IGdldFlvZ2FDb3VudGVycygpXG4gICAgICAgIHRoaXMubGFzdFlvZ2FDb3VudGVycyA9IHsgbXMsIC4uLmMgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEB0cy1leHBlY3QtZXJyb3IgQHR5cGVzL3JlYWN0LXJlY29uY2lsZXJAMC4zMi4zIGRlY2xhcmVzIDExIGFyZ3Mgd2l0aCB0cmFuc2l0aW9uQ2FsbGJhY2tzLFxuICAgIC8vIGJ1dCByZWFjdC1yZWNvbmNpbGVyIDAuMzMuMCBzb3VyY2Ugb25seSBhY2NlcHRzIDEwIGFyZ3MgKG5vIHRyYW5zaXRpb25DYWxsYmFja3MpXG4gICAgdGhpcy5jb250YWluZXIgPSByZWNvbmNpbGVyLmNyZWF0ZUNvbnRhaW5lcihcbiAgICAgIHRoaXMucm9vdE5vZGUsXG4gICAgICBDb25jdXJyZW50Um9vdCxcbiAgICAgIG51bGwsXG4gICAgICBmYWxzZSxcbiAgICAgIG51bGwsXG4gICAgICAnaWQnLFxuICAgICAgbm9vcCwgLy8gb25VbmNhdWdodEVycm9yXG4gICAgICBub29wLCAvLyBvbkNhdWdodEVycm9yXG4gICAgICBub29wLCAvLyBvblJlY292ZXJhYmxlRXJyb3JcbiAgICAgIG5vb3AsIC8vIG9uRGVmYXVsdFRyYW5zaXRpb25JbmRpY2F0b3JcbiAgICApXG5cbiAgICBpZiAoXCJwcm9kdWN0aW9uXCIgPT09ICdkZXZlbG9wbWVudCcpIHtcbiAgICAgIHJlY29uY2lsZXIuaW5qZWN0SW50b0RldlRvb2xzKHtcbiAgICAgICAgYnVuZGxlVHlwZTogMCxcbiAgICAgICAgLy8gUmVwb3J0aW5nIFJlYWN0IERPTSdzIHZlcnNpb24sIG5vdCBJbmsnc1xuICAgICAgICAvLyBTZWUgaHR0cHM6Ly9naXRodWIuY29tL2ZhY2Vib29rL3JlYWN0L2lzc3Vlcy8xNjY2NiNpc3N1ZWNvbW1lbnQtNTMyNjM5OTA1XG4gICAgICAgIHZlcnNpb246ICcxNi4xMy4xJyxcbiAgICAgICAgcmVuZGVyZXJQYWNrYWdlTmFtZTogJ2luaycsXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgaGFuZGxlUmVzdW1lID0gKCkgPT4ge1xuICAgIGlmICghdGhpcy5vcHRpb25zLnN0ZG91dC5pc1RUWSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gQWx0IHNjcmVlbjogYWZ0ZXIgU0lHQ09OVCwgY29udGVudCBpcyBzdGFsZSAoc2hlbGwgbWF5IGhhdmUgd3JpdHRlblxuICAgIC8vIHRvIG1haW4gc2NyZWVuLCBzd2l0Y2hpbmcgZm9jdXMgYXdheSkgYW5kIG1vdXNlIHRyYWNraW5nIHdhc1xuICAgIC8vIGRpc2FibGVkIGJ5IGhhbmRsZVN1c3BlbmQuXG4gICAgaWYgKHRoaXMuYWx0U2NyZWVuQWN0aXZlKSB7XG4gICAgICB0aGlzLnJlZW50ZXJBbHRTY3JlZW4oKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gTWFpbiBzY3JlZW46IHN0YXJ0IGZyZXNoIHRvIHByZXZlbnQgY2xvYmJlcmluZyB0ZXJtaW5hbCBjb250ZW50XG4gICAgdGhpcy5mcm9udEZyYW1lID0gZW1wdHlGcmFtZShcbiAgICAgIHRoaXMuZnJvbnRGcmFtZS52aWV3cG9ydC5oZWlnaHQsXG4gICAgICB0aGlzLmZyb250RnJhbWUudmlld3BvcnQud2lkdGgsXG4gICAgICB0aGlzLnN0eWxlUG9vbCxcbiAgICAgIHRoaXMuY2hhclBvb2wsXG4gICAgICB0aGlzLmh5cGVybGlua1Bvb2wsXG4gICAgKVxuICAgIHRoaXMuYmFja0ZyYW1lID0gZW1wdHlGcmFtZShcbiAgICAgIHRoaXMuYmFja0ZyYW1lLnZpZXdwb3J0LmhlaWdodCxcbiAgICAgIHRoaXMuYmFja0ZyYW1lLnZpZXdwb3J0LndpZHRoLFxuICAgICAgdGhpcy5zdHlsZVBvb2wsXG4gICAgICB0aGlzLmNoYXJQb29sLFxuICAgICAgdGhpcy5oeXBlcmxpbmtQb29sLFxuICAgIClcbiAgICB0aGlzLmxvZy5yZXNldCgpXG4gICAgLy8gUGh5c2ljYWwgY3Vyc29yIHBvc2l0aW9uIGlzIHVua25vd24gYWZ0ZXIgdGhlIHNoZWxsIHRvb2sgb3ZlciBkdXJpbmdcbiAgICAvLyBzdXNwZW5kLiBDbGVhciBkaXNwbGF5Q3Vyc29yIHNvIHRoZSBuZXh0IGZyYW1lJ3MgY3Vyc29yIHByZWFtYmxlXG4gICAgLy8gZG9lc24ndCBlbWl0IGEgcmVsYXRpdmUgbW92ZSBmcm9tIGEgc3RhbGUgcGFyayBwb3NpdGlvbi5cbiAgICB0aGlzLmRpc3BsYXlDdXJzb3IgPSBudWxsXG4gIH1cblxuICAvLyBOT1QgZGVib3VuY2VkLiBBIGRlYm91bmNlIG9wZW5zIGEgd2luZG93IHdoZXJlIHN0ZG91dC5jb2x1bW5zIGlzIE5FV1xuICAvLyBidXQgdGhpcy50ZXJtaW5hbENvbHVtbnMvWW9nYSBhcmUgT0xEIOKAlCBhbnkgc2NoZWR1bGVSZW5kZXIgZHVyaW5nIHRoYXRcbiAgLy8gd2luZG93IChzcGlubmVyLCBjbG9jaykgbWFrZXMgbG9nLXVwZGF0ZSBkZXRlY3QgYSB3aWR0aCBjaGFuZ2UgYW5kXG4gIC8vIGNsZWFyIHRoZSBzY3JlZW4sIHRoZW4gdGhlIGRlYm91bmNlIGZpcmVzIGFuZCBjbGVhcnMgYWdhaW4gKGRvdWJsZVxuICAvLyBibGFua+KGknBhaW50IGZsaWNrZXIpLiB1c2VWaXJ0dWFsU2Nyb2xsJ3MgaGVpZ2h0IHNjYWxpbmcgYWxyZWFkeSBib3VuZHNcbiAgLy8gdGhlIHBlci1yZXNpemUgY29zdDsgc3luY2hyb25vdXMgaGFuZGxpbmcga2VlcHMgZGltZW5zaW9ucyBjb25zaXN0ZW50LlxuICBwcml2YXRlIGhhbmRsZVJlc2l6ZSA9ICgpID0+IHtcbiAgICBjb25zdCBjb2xzID0gdGhpcy5vcHRpb25zLnN0ZG91dC5jb2x1bW5zIHx8IDgwXG4gICAgY29uc3Qgcm93cyA9IHRoaXMub3B0aW9ucy5zdGRvdXQucm93cyB8fCAyNFxuICAgIC8vIFRlcm1pbmFscyBvZnRlbiBlbWl0IDIrIHJlc2l6ZSBldmVudHMgZm9yIG9uZSB1c2VyIGFjdGlvbiAod2luZG93XG4gICAgLy8gc2V0dGxpbmcpLiBTYW1lLWRpbWVuc2lvbiBldmVudHMgYXJlIG5vLW9wczsgc2tpcCB0byBhdm9pZCByZWR1bmRhbnRcbiAgICAvLyBmcmFtZSByZXNldHMgYW5kIHJlbmRlcnMuXG4gICAgaWYgKGNvbHMgPT09IHRoaXMudGVybWluYWxDb2x1bW5zICYmIHJvd3MgPT09IHRoaXMudGVybWluYWxSb3dzKSByZXR1cm5cbiAgICB0aGlzLnRlcm1pbmFsQ29sdW1ucyA9IGNvbHNcbiAgICB0aGlzLnRlcm1pbmFsUm93cyA9IHJvd3NcbiAgICB0aGlzLmFsdFNjcmVlblBhcmtQYXRjaCA9IG1ha2VBbHRTY3JlZW5QYXJrUGF0Y2godGhpcy50ZXJtaW5hbFJvd3MpXG5cbiAgICAvLyBBbHQgc2NyZWVuOiByZXNldCBmcmFtZSBidWZmZXJzIHNvIHRoZSBuZXh0IHJlbmRlciByZXBhaW50cyBmcm9tXG4gICAgLy8gc2NyYXRjaCAocHJldkZyYW1lQ29udGFtaW5hdGVkIOKGkiBldmVyeSBjZWxsIHdyaXR0ZW4sIHdyYXBwZWQgaW5cbiAgICAvLyBCU1UvRVNVIOKAlCBvbGQgY29udGVudCBzdGF5cyB2aXNpYmxlIHVudGlsIHRoZSBuZXcgZnJhbWUgc3dhcHNcbiAgICAvLyBhdG9taWNhbGx5KS4gUmUtYXNzZXJ0IG1vdXNlIHRyYWNraW5nIChzb21lIGVtdWxhdG9ycyByZXNldCBpdCBvblxuICAgIC8vIHJlc2l6ZSkuIERvIE5PVCB3cml0ZSBFTlRFUl9BTFRfU0NSRUVOOiBpVGVybTIgdHJlYXRzID8xMDQ5aCBhcyBhXG4gICAgLy8gYnVmZmVyIGNsZWFyIGV2ZW4gd2hlbiBhbHJlYWR5IGluIGFsdCDigJQgdGhhdCdzIHRoZSBibGFuayBmbGlja2VyLlxuICAgIC8vIFNlbGYtaGVhbGluZyByZS1lbnRyeSAoaWYgc29tZXRoaW5nIGtpY2tlZCB1cyBvdXQgb2YgYWx0KSBpcyBoYW5kbGVkXG4gICAgLy8gYnkgaGFuZGxlUmVzdW1lIChTSUdDT05UKSBhbmQgdGhlIHNsZWVwLXdha2UgZGV0ZWN0b3I7IHJlc2l6ZSBpdHNlbGZcbiAgICAvLyBkb2Vzbid0IGV4aXQgYWx0LXNjcmVlbi4gRG8gTk9UIHdyaXRlIEVSQVNFX1NDUkVFTjogcmVuZGVyKCkgYmVsb3dcbiAgICAvLyBjYW4gdGFrZSB+ODBtczsgZXJhc2luZyBmaXJzdCBsZWF2ZXMgdGhlIHNjcmVlbiBibGFuayB0aGF0IHdob2xlIHRpbWUuXG4gICAgaWYgKHRoaXMuYWx0U2NyZWVuQWN0aXZlICYmICF0aGlzLmlzUGF1c2VkICYmIHRoaXMub3B0aW9ucy5zdGRvdXQuaXNUVFkpIHtcbiAgICAgIGlmICh0aGlzLmFsdFNjcmVlbk1vdXNlVHJhY2tpbmcpIHtcbiAgICAgICAgdGhpcy5vcHRpb25zLnN0ZG91dC53cml0ZShFTkFCTEVfTU9VU0VfVFJBQ0tJTkcpXG4gICAgICB9XG4gICAgICB0aGlzLnJlc2V0RnJhbWVzRm9yQWx0U2NyZWVuKClcbiAgICAgIHRoaXMubmVlZHNFcmFzZUJlZm9yZVBhaW50ID0gdHJ1ZVxuICAgIH1cblxuICAgIC8vIFJlLXJlbmRlciB0aGUgUmVhY3QgdHJlZSB3aXRoIHVwZGF0ZWQgcHJvcHMgc28gdGhlIGNvbnRleHQgdmFsdWUgY2hhbmdlcy5cbiAgICAvLyBSZWFjdCdzIGNvbW1pdCBwaGFzZSB3aWxsIGNhbGwgb25Db21wdXRlTGF5b3V0KCkgdG8gcmVjYWxjdWxhdGUgeW9nYSBsYXlvdXRcbiAgICAvLyB3aXRoIHRoZSBuZXcgZGltZW5zaW9ucywgdGhlbiBjYWxsIG9uUmVuZGVyKCkgdG8gcmVuZGVyIHRoZSB1cGRhdGVkIGZyYW1lLlxuICAgIC8vIFdlIGRvbid0IGNhbGwgc2NoZWR1bGVSZW5kZXIoKSBoZXJlIGJlY2F1c2UgdGhhdCB3b3VsZCByZW5kZXIgYmVmb3JlIHRoZVxuICAgIC8vIGxheW91dCBpcyB1cGRhdGVkLCBjYXVzaW5nIGEgbWlzbWF0Y2ggYmV0d2VlbiB2aWV3cG9ydCBhbmQgY29udGVudCBkaW1lbnNpb25zLlxuICAgIGlmICh0aGlzLmN1cnJlbnROb2RlICE9PSBudWxsKSB7XG4gICAgICB0aGlzLnJlbmRlcih0aGlzLmN1cnJlbnROb2RlKVxuICAgIH1cbiAgfVxuXG4gIHJlc29sdmVFeGl0UHJvbWlzZTogKCkgPT4gdm9pZCA9ICgpID0+IHt9XG4gIHJlamVjdEV4aXRQcm9taXNlOiAocmVhc29uPzogRXJyb3IpID0+IHZvaWQgPSAoKSA9PiB7fVxuICB1bnN1YnNjcmliZUV4aXQ6ICgpID0+IHZvaWQgPSAoKSA9PiB7fVxuXG4gIC8qKlxuICAgKiBQYXVzZSBJbmsgYW5kIGhhbmQgdGhlIHRlcm1pbmFsIG92ZXIgdG8gYW4gZXh0ZXJuYWwgVFVJIChlLmcuIGdpdFxuICAgKiBjb21taXQgZWRpdG9yKS4gSW4gbm9uLWZ1bGxzY3JlZW4gbW9kZSB0aGlzIGVudGVycyB0aGUgYWx0IHNjcmVlbjtcbiAgICogaW4gZnVsbHNjcmVlbiBtb2RlIHdlJ3JlIGFscmVhZHkgaW4gYWx0IHNvIHdlIGp1c3QgY2xlYXIgaXQuXG4gICAqIENhbGwgYGV4aXRBbHRlcm5hdGVTY3JlZW4oKWAgd2hlbiBkb25lIHRvIHJlc3RvcmUgSW5rLlxuICAgKi9cbiAgZW50ZXJBbHRlcm5hdGVTY3JlZW4oKTogdm9pZCB7XG4gICAgdGhpcy5wYXVzZSgpXG4gICAgdGhpcy5zdXNwZW5kU3RkaW4oKVxuICAgIHRoaXMub3B0aW9ucy5zdGRvdXQud3JpdGUoXG4gICAgICAvLyBEaXNhYmxlIGV4dGVuZGVkIGtleSByZXBvcnRpbmcgZmlyc3Qg4oCUIGVkaXRvcnMgdGhhdCBkb24ndCBzcGVha1xuICAgICAgLy8gQ1NJLXUgKGUuZy4gbmFubykgc2hvdyBcIlVua25vd24gc2VxdWVuY2VcIiBmb3IgZXZlcnkgQ3RybC08a2V5PiBpZlxuICAgICAgLy8ga2l0dHkvbW9kaWZ5T3RoZXJLZXlzIHN0YXlzIGFjdGl2ZS4gZXhpdEFsdGVybmF0ZVNjcmVlbiByZS1lbmFibGVzLlxuICAgICAgRElTQUJMRV9LSVRUWV9LRVlCT0FSRCArXG4gICAgICAgIERJU0FCTEVfTU9ESUZZX09USEVSX0tFWVMgK1xuICAgICAgICAodGhpcy5hbHRTY3JlZW5Nb3VzZVRyYWNraW5nID8gRElTQUJMRV9NT1VTRV9UUkFDS0lORyA6ICcnKSArIC8vIGRpc2FibGUgbW91c2UgKG5vLW9wIGlmIG9mZilcbiAgICAgICAgKHRoaXMuYWx0U2NyZWVuQWN0aXZlID8gJycgOiAnXFx4MWJbPzEwNDloJykgKyAvLyBlbnRlciBhbHQgKGFscmVhZHkgaW4gYWx0IGlmIGZ1bGxzY3JlZW4pXG4gICAgICAgICdcXHgxYls/MTAwNGwnICsgLy8gZGlzYWJsZSBmb2N1cyByZXBvcnRpbmdcbiAgICAgICAgJ1xceDFiWzBtJyArIC8vIHJlc2V0IGF0dHJpYnV0ZXNcbiAgICAgICAgJ1xceDFiWz8yNWgnICsgLy8gc2hvdyBjdXJzb3JcbiAgICAgICAgJ1xceDFiWzJKJyArIC8vIGNsZWFyIHNjcmVlblxuICAgICAgICAnXFx4MWJbSCcsIC8vIGN1cnNvciBob21lXG4gICAgKVxuICB9XG5cbiAgLyoqXG4gICAqIFJlc3VtZSBJbmsgYWZ0ZXIgYW4gZXh0ZXJuYWwgVFVJIGhhbmRvZmYgd2l0aCBhIGZ1bGwgcmVwYWludC5cbiAgICogSW4gbm9uLWZ1bGxzY3JlZW4gbW9kZSB0aGlzIGV4aXRzIHRoZSBhbHQgc2NyZWVuIGJhY2sgdG8gbWFpbjtcbiAgICogaW4gZnVsbHNjcmVlbiBtb2RlIHdlIHJlLWVudGVyIGFsdCBhbmQgY2xlYXIgKyByZXBhaW50LlxuICAgKlxuICAgKiBUaGUgcmUtZW50ZXIgbWF0dGVyczogdGVybWluYWwgZWRpdG9ycyAodmltLCBuYW5vLCBsZXNzKSB3cml0ZVxuICAgKiBzbWN1cC9ybWN1cCAoPzEwNDloLz8xMDQ5bCksIHNvIGV2ZW4gdGhvdWdoIHdlIHN0YXJ0ZWQgaW4gYWx0LFxuICAgKiB0aGUgZWRpdG9yJ3Mgcm1jdXAgb24gZXhpdCBkcm9wcyB1cyB0byBtYWluIHNjcmVlbi4gV2l0aG91dFxuICAgKiByZS1lbnRlcmluZywgdGhlIDJKIGJlbG93IHdpcGVzIHRoZSB1c2VyJ3MgbWFpbi1zY3JlZW4gc2Nyb2xsYmFja1xuICAgKiBhbmQgc3Vic2VxdWVudCByZW5kZXJzIGxhbmQgaW4gbWFpbiDigJQgbmF0aXZlIHRlcm1pbmFsIHNjcm9sbFxuICAgKiByZXR1cm5zLCBmdWxsc2NyZWVuIHNjcm9sbCBpcyBkZWFkLlxuICAgKi9cbiAgZXhpdEFsdGVybmF0ZVNjcmVlbigpOiB2b2lkIHtcbiAgICB0aGlzLm9wdGlvbnMuc3Rkb3V0LndyaXRlKFxuICAgICAgKHRoaXMuYWx0U2NyZWVuQWN0aXZlID8gRU5URVJfQUxUX1NDUkVFTiA6ICcnKSArIC8vIHJlLWVudGVyIGFsdCDigJQgdmltJ3Mgcm1jdXAgZHJvcHBlZCB1cyB0byBtYWluXG4gICAgICAgICdcXHgxYlsySicgKyAvLyBjbGVhciBzY3JlZW4gKG5vdyBhbHQgaWYgZnVsbHNjcmVlbilcbiAgICAgICAgJ1xceDFiW0gnICsgLy8gY3Vyc29yIGhvbWVcbiAgICAgICAgKHRoaXMuYWx0U2NyZWVuTW91c2VUcmFja2luZyA/IEVOQUJMRV9NT1VTRV9UUkFDS0lORyA6ICcnKSArIC8vIHJlLWVuYWJsZSBtb3VzZSAoc2tpcCBpZiBDTEFVREVfQ09ERV9ESVNBQkxFX01PVVNFKVxuICAgICAgICAodGhpcy5hbHRTY3JlZW5BY3RpdmUgPyAnJyA6ICdcXHgxYls/MTA0OWwnKSArIC8vIGV4aXQgYWx0IChub24tZnVsbHNjcmVlbiBvbmx5KVxuICAgICAgICAnXFx4MWJbPzI1bCcsIC8vIGhpZGUgY3Vyc29yIChJbmsgbWFuYWdlcylcbiAgICApXG4gICAgdGhpcy5yZXN1bWVTdGRpbigpXG4gICAgaWYgKHRoaXMuYWx0U2NyZWVuQWN0aXZlKSB7XG4gICAgICB0aGlzLnJlc2V0RnJhbWVzRm9yQWx0U2NyZWVuKClcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZXBhaW50KClcbiAgICB9XG4gICAgdGhpcy5yZXN1bWUoKVxuICAgIC8vIFJlLWVuYWJsZSBmb2N1cyByZXBvcnRpbmcgYW5kIGV4dGVuZGVkIGtleSByZXBvcnRpbmcg4oCUIHRlcm1pbmFsXG4gICAgLy8gZWRpdG9ycyAodmltLCBuYW5vLCBldGMuKSB3cml0ZSB0aGVpciBvd24gbW9kaWZ5T3RoZXJLZXlzIGxldmVsIG9uXG4gICAgLy8gZW50cnkgYW5kIHJlc2V0IGl0IG9uIGV4aXQsIGxlYXZpbmcgdXMgdW5hYmxlIHRvIGRpc3Rpbmd1aXNoXG4gICAgLy8gY3RybCtzaGlmdCs8bGV0dGVyPiBmcm9tIGN0cmwrPGxldHRlcj4uIFBvcC1iZWZvcmUtcHVzaCBrZWVwcyB0aGVcbiAgICAvLyBLaXR0eSBzdGFjayBiYWxhbmNlZCAoYSB3ZWxsLWJlaGF2ZWQgZWRpdG9yIHJlc3RvcmVzIG91ciBlbnRyeSwgc29cbiAgICAvLyB3aXRob3V0IHRoZSBwb3Agd2UnZCBhY2N1bXVsYXRlIGRlcHRoIG9uIGVhY2ggZWRpdG9yIHJvdW5kLXRyaXApLlxuICAgIHRoaXMub3B0aW9ucy5zdGRvdXQud3JpdGUoXG4gICAgICAnXFx4MWJbPzEwMDRoJyArXG4gICAgICAgIChzdXBwb3J0c0V4dGVuZGVkS2V5cygpXG4gICAgICAgICAgPyBESVNBQkxFX0tJVFRZX0tFWUJPQVJEICtcbiAgICAgICAgICAgIEVOQUJMRV9LSVRUWV9LRVlCT0FSRCArXG4gICAgICAgICAgICBFTkFCTEVfTU9ESUZZX09USEVSX0tFWVNcbiAgICAgICAgICA6ICcnKSxcbiAgICApXG4gIH1cblxuICBvblJlbmRlcigpIHtcbiAgICBpZiAodGhpcy5pc1VubW91bnRlZCB8fCB0aGlzLmlzUGF1c2VkKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgLy8gRW50ZXJpbmcgYSByZW5kZXIgY2FuY2VscyBhbnkgcGVuZGluZyBkcmFpbiB0aWNrIOKAlCB0aGlzIHJlbmRlciB3aWxsXG4gICAgLy8gaGFuZGxlIHRoZSBkcmFpbiAoYW5kIHJlLXNjaGVkdWxlIGJlbG93IGlmIG5lZWRlZCkuIFByZXZlbnRzIGFcbiAgICAvLyB3aGVlbC1ldmVudC10cmlnZ2VyZWQgcmVuZGVyIEFORCBhIGRyYWluLXRpbWVyIHJlbmRlciBib3RoIGZpcmluZy5cbiAgICBpZiAodGhpcy5kcmFpblRpbWVyICE9PSBudWxsKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5kcmFpblRpbWVyKVxuICAgICAgdGhpcy5kcmFpblRpbWVyID0gbnVsbFxuICAgIH1cblxuICAgIC8vIEZsdXNoIGRlZmVycmVkIGludGVyYWN0aW9uLXRpbWUgdXBkYXRlIGJlZm9yZSByZW5kZXJpbmcgc28gd2UgY2FsbFxuICAgIC8vIERhdGUubm93KCkgYXQgbW9zdCBvbmNlIHBlciBmcmFtZSBpbnN0ZWFkIG9mIG9uY2UgcGVyIGtleXByZXNzLlxuICAgIC8vIERvbmUgYmVmb3JlIHRoZSByZW5kZXIgdG8gYXZvaWQgZGlydHlpbmcgc3RhdGUgdGhhdCB3b3VsZCB0cmlnZ2VyXG4gICAgLy8gYW4gZXh0cmEgUmVhY3QgcmUtcmVuZGVyIGN5Y2xlLlxuICAgIGZsdXNoSW50ZXJhY3Rpb25UaW1lKClcblxuICAgIGNvbnN0IHJlbmRlclN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KClcbiAgICBjb25zdCB0ZXJtaW5hbFdpZHRoID0gdGhpcy5vcHRpb25zLnN0ZG91dC5jb2x1bW5zIHx8IDgwXG4gICAgY29uc3QgdGVybWluYWxSb3dzID0gdGhpcy5vcHRpb25zLnN0ZG91dC5yb3dzIHx8IDI0XG5cbiAgICBjb25zdCBmcmFtZSA9IHRoaXMucmVuZGVyZXIoe1xuICAgICAgZnJvbnRGcmFtZTogdGhpcy5mcm9udEZyYW1lLFxuICAgICAgYmFja0ZyYW1lOiB0aGlzLmJhY2tGcmFtZSxcbiAgICAgIGlzVFRZOiB0aGlzLm9wdGlvbnMuc3Rkb3V0LmlzVFRZLFxuICAgICAgdGVybWluYWxXaWR0aCxcbiAgICAgIHRlcm1pbmFsUm93cyxcbiAgICAgIGFsdFNjcmVlbjogdGhpcy5hbHRTY3JlZW5BY3RpdmUsXG4gICAgICBwcmV2RnJhbWVDb250YW1pbmF0ZWQ6IHRoaXMucHJldkZyYW1lQ29udGFtaW5hdGVkLFxuICAgIH0pXG4gICAgY29uc3QgcmVuZGVyZXJNcyA9IHBlcmZvcm1hbmNlLm5vdygpIC0gcmVuZGVyU3RhcnRcblxuICAgIC8vIFN0aWNreS9hdXRvLWZvbGxvdyBzY3JvbGxlZCB0aGUgU2Nyb2xsQm94IHRoaXMgZnJhbWUuIFRyYW5zbGF0ZSB0aGVcbiAgICAvLyBzZWxlY3Rpb24gYnkgdGhlIHNhbWUgZGVsdGEgc28gdGhlIGhpZ2hsaWdodCBzdGF5cyBhbmNob3JlZCB0byB0aGVcbiAgICAvLyBURVhUIChuYXRpdmUgdGVybWluYWwgYmVoYXZpb3Ig4oCUIHRoZSBzZWxlY3Rpb24gd2Fsa3MgdXAgdGhlIHNjcmVlblxuICAgIC8vIGFzIGNvbnRlbnQgc2Nyb2xscywgZXZlbnR1YWxseSBjbGlwcGluZyBhdCB0aGUgdG9wKS4gZnJvbnRGcmFtZVxuICAgIC8vIHN0aWxsIGhvbGRzIHRoZSBQUkVWSU9VUyBmcmFtZSdzIHNjcmVlbiAoc3dhcCBpcyBhdCB+NTAwIGJlbG93KSwgc29cbiAgICAvLyBjYXB0dXJlU2Nyb2xsZWRSb3dzIHJlYWRzIHRoZSByb3dzIHRoYXQgYXJlIGFib3V0IHRvIHNjcm9sbCBvdXRcbiAgICAvLyBiZWZvcmUgdGhleSdyZSBvdmVyd3JpdHRlbiDigJQgdGhlIHRleHQgc3RheXMgY29weWFibGUgdW50aWwgdGhlXG4gICAgLy8gc2VsZWN0aW9uIHNjcm9sbHMgZW50aXJlbHkgb2ZmLiBEdXJpbmcgZHJhZywgZm9jdXMgdHJhY2tzIHRoZSBtb3VzZVxuICAgIC8vIChzY3JlZW4tbG9jYWwpIHNvIG9ubHkgYW5jaG9yIHNoaWZ0cyDigJQgc2VsZWN0aW9uIGdyb3dzIHRvd2FyZCB0aGVcbiAgICAvLyBtb3VzZSBhcyB0aGUgYW5jaG9yIHdhbGtzIHVwLiBBZnRlciByZWxlYXNlLCBib3RoIGVuZHMgYXJlIHRleHQtXG4gICAgLy8gYW5jaG9yZWQgYW5kIG1vdmUgYXMgYSBibG9jay5cbiAgICBjb25zdCBmb2xsb3cgPSBjb25zdW1lRm9sbG93U2Nyb2xsKClcbiAgICBpZiAoXG4gICAgICBmb2xsb3cgJiZcbiAgICAgIHRoaXMuc2VsZWN0aW9uLmFuY2hvciAmJlxuICAgICAgLy8gT25seSB0cmFuc2xhdGUgaWYgdGhlIHNlbGVjdGlvbiBpcyBPTiBzY3JvbGxib3ggY29udGVudC4gU2VsZWN0aW9uc1xuICAgICAgLy8gaW4gdGhlIGZvb3Rlci9wcm9tcHQvU3RpY2t5UHJvbXB0SGVhZGVyIGFyZSBvbiBzdGF0aWMgdGV4dCDigJQgdGhlXG4gICAgICAvLyBzY3JvbGwgZG9lc24ndCBtb3ZlIHdoYXQncyB1bmRlciB0aGVtLiBXaXRob3V0IHRoaXMgZ3VhcmQsIGFcbiAgICAgIC8vIGZvb3RlciBzZWxlY3Rpb24gd291bGQgYmUgc2hpZnRlZCBieSAtZGVsdGEgdGhlbiBjbGFtcGVkIHRvXG4gICAgICAvLyB2aWV3cG9ydEJvdHRvbSwgdGVsZXBvcnRpbmcgaXQgaW50byB0aGUgc2Nyb2xsYm94LiBNaXJyb3IgdGhlXG4gICAgICAvLyBib3VuZHMgY2hlY2sgdGhlIGRlbGV0ZWQgY2hlY2soKSBpbiBTY3JvbGxLZXliaW5kaW5nSGFuZGxlciBoYWQuXG4gICAgICB0aGlzLnNlbGVjdGlvbi5hbmNob3Iucm93ID49IGZvbGxvdy52aWV3cG9ydFRvcCAmJlxuICAgICAgdGhpcy5zZWxlY3Rpb24uYW5jaG9yLnJvdyA8PSBmb2xsb3cudmlld3BvcnRCb3R0b21cbiAgICApIHtcbiAgICAgIGNvbnN0IHsgZGVsdGEsIHZpZXdwb3J0VG9wLCB2aWV3cG9ydEJvdHRvbSB9ID0gZm9sbG93XG4gICAgICAvLyBjYXB0dXJlU2Nyb2xsZWRSb3dzIGFuZCBzaGlmdCogYXJlIGEgcGFpcjogY2FwdHVyZSBncmFicyByb3dzIGFib3V0XG4gICAgICAvLyB0byBzY3JvbGwgb2ZmLCBzaGlmdCBtb3ZlcyB0aGUgc2VsZWN0aW9uIGVuZHBvaW50IHNvIHRoZSBzYW1lIHJvd3NcbiAgICAgIC8vIHdvbid0IGludGVyc2VjdCBhZ2FpbiBuZXh0IGZyYW1lLiBDYXB0dXJpbmcgd2l0aG91dCBzaGlmdGluZyBsZWF2ZXNcbiAgICAgIC8vIHRoZSBlbmRwb2ludCBpbiBwbGFjZSwgc28gdGhlIFNBTUUgdmlld3BvcnQgcm93cyByZS1pbnRlcnNlY3QgZXZlcnlcbiAgICAgIC8vIGZyYW1lIGFuZCBzY3JvbGxlZE9mZkFib3ZlIGdyb3dzIHdpdGhvdXQgYm91bmQg4oCUIGdldFNlbGVjdGVkVGV4dFxuICAgICAgLy8gdGhlbiByZXR1cm5zIGV2ZXItZ3Jvd2luZyB0ZXh0IG9uIGVhY2ggcmUtY29weS4gS2VlcCBjYXB0dXJlIGluc2lkZVxuICAgICAgLy8gZWFjaCBzaGlmdCBicmFuY2ggc28gdGhlIHBhaXJpbmcgY2FuJ3QgYmUgYnJva2VuIGJ5IGEgbmV3IGd1YXJkLlxuICAgICAgaWYgKHRoaXMuc2VsZWN0aW9uLmlzRHJhZ2dpbmcpIHtcbiAgICAgICAgaWYgKGhhc1NlbGVjdGlvbih0aGlzLnNlbGVjdGlvbikpIHtcbiAgICAgICAgICBjYXB0dXJlU2Nyb2xsZWRSb3dzKFxuICAgICAgICAgICAgdGhpcy5zZWxlY3Rpb24sXG4gICAgICAgICAgICB0aGlzLmZyb250RnJhbWUuc2NyZWVuLFxuICAgICAgICAgICAgdmlld3BvcnRUb3AsXG4gICAgICAgICAgICB2aWV3cG9ydFRvcCArIGRlbHRhIC0gMSxcbiAgICAgICAgICAgICdhYm92ZScsXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIHNoaWZ0QW5jaG9yKHRoaXMuc2VsZWN0aW9uLCAtZGVsdGEsIHZpZXdwb3J0VG9wLCB2aWV3cG9ydEJvdHRvbSlcbiAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgIC8vIEZsYWctMyBndWFyZDogdGhlIGFuY2hvciBjaGVjayBhYm92ZSBvbmx5IHByb3ZlcyBPTkUgZW5kcG9pbnQgaXNcbiAgICAgICAgLy8gb24gc2Nyb2xsYm94IGNvbnRlbnQuIEEgZHJhZyBmcm9tIHJvdyAzIChzY3JvbGxib3gpIGludG8gdGhlXG4gICAgICAgIC8vIGZvb3RlciBhdCByb3cgNiwgdGhlbiByZWxlYXNlLCBsZWF2ZXMgZm9jdXMgb3V0c2lkZSB0aGUgdmlld3BvcnRcbiAgICAgICAgLy8g4oCUIHNoaWZ0U2VsZWN0aW9uRm9yRm9sbG93IHdvdWxkIGNsYW1wIGl0IHRvIHZpZXdwb3J0Qm90dG9tLFxuICAgICAgICAvLyB0ZWxlcG9ydGluZyB0aGUgaGlnaGxpZ2h0IGZyb20gc3RhdGljIGZvb3RlciBpbnRvIHRoZSBzY3JvbGxib3guXG4gICAgICAgIC8vIFN5bW1ldHJpYyBjaGVjazogcmVxdWlyZSBCT1RIIGVuZHMgaW5zaWRlIHRvIHRyYW5zbGF0ZS4gQVxuICAgICAgICAvLyBzdHJhZGRsaW5nIHNlbGVjdGlvbiBmYWxscyB0aHJvdWdoIHRvIE5FSVRIRVIgc2hpZnQgTk9SIGNhcHR1cmU6XG4gICAgICAgIC8vIHRoZSBmb290ZXIgZW5kcG9pbnQgcGlucyB0aGUgc2VsZWN0aW9uLCB0ZXh0IHNjcm9sbHMgYXdheSB1bmRlclxuICAgICAgICAvLyB0aGUgaGlnaGxpZ2h0LCBhbmQgZ2V0U2VsZWN0ZWRUZXh0IHJlYWRzIHRoZSBDVVJSRU5UIHNjcmVlblxuICAgICAgICAvLyBjb250ZW50cyDigJQgbm8gYWNjdW11bGF0aW9uLiBEcmFnZ2luZyBicmFuY2ggZG9lc24ndCBuZWVkIHRoaXM6XG4gICAgICAgIC8vIHNoaWZ0QW5jaG9yIGlnbm9yZXMgZm9jdXMsIGFuZCB0aGUgYW5jaG9yIERPRVMgc2hpZnQgKHNvIGNhcHR1cmVcbiAgICAgICAgLy8gaXMgY29ycmVjdCB0aGVyZSBldmVuIHdoZW4gZm9jdXMgaXMgaW4gdGhlIGZvb3RlcikuXG4gICAgICAgICF0aGlzLnNlbGVjdGlvbi5mb2N1cyB8fFxuICAgICAgICAodGhpcy5zZWxlY3Rpb24uZm9jdXMucm93ID49IHZpZXdwb3J0VG9wICYmXG4gICAgICAgICAgdGhpcy5zZWxlY3Rpb24uZm9jdXMucm93IDw9IHZpZXdwb3J0Qm90dG9tKVxuICAgICAgKSB7XG4gICAgICAgIGlmIChoYXNTZWxlY3Rpb24odGhpcy5zZWxlY3Rpb24pKSB7XG4gICAgICAgICAgY2FwdHVyZVNjcm9sbGVkUm93cyhcbiAgICAgICAgICAgIHRoaXMuc2VsZWN0aW9uLFxuICAgICAgICAgICAgdGhpcy5mcm9udEZyYW1lLnNjcmVlbixcbiAgICAgICAgICAgIHZpZXdwb3J0VG9wLFxuICAgICAgICAgICAgdmlld3BvcnRUb3AgKyBkZWx0YSAtIDEsXG4gICAgICAgICAgICAnYWJvdmUnLFxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjbGVhcmVkID0gc2hpZnRTZWxlY3Rpb25Gb3JGb2xsb3coXG4gICAgICAgICAgdGhpcy5zZWxlY3Rpb24sXG4gICAgICAgICAgLWRlbHRhLFxuICAgICAgICAgIHZpZXdwb3J0VG9wLFxuICAgICAgICAgIHZpZXdwb3J0Qm90dG9tLFxuICAgICAgICApXG4gICAgICAgIC8vIEF1dG8tY2xlYXIgKGJvdGggZW5kcyBvdmVyc2hvdCBtaW5Sb3cpIG11c3Qgbm90aWZ5IFJlYWN0LWxhbmRcbiAgICAgICAgLy8gc28gdXNlSGFzU2VsZWN0aW9uIHJlLXJlbmRlcnMgYW5kIHRoZSBmb290ZXIgY29weS9lc2NhcGUgaGludFxuICAgICAgICAvLyBkaXNhcHBlYXJzLiBub3RpZnlTZWxlY3Rpb25DaGFuZ2UoKSB3b3VsZCByZWN1cnNlIGludG8gb25SZW5kZXI7XG4gICAgICAgIC8vIGZpcmUgdGhlIGxpc3RlbmVycyBkaXJlY3RseSDigJQgdGhleSBzY2hlZHVsZSBhIFJlYWN0IHVwZGF0ZSBmb3JcbiAgICAgICAgLy8gTEFURVIsIHRoZXkgZG9uJ3QgcmUtZW50ZXIgdGhpcyBmcmFtZS5cbiAgICAgICAgaWYgKGNsZWFyZWQpIGZvciAoY29uc3QgY2Igb2YgdGhpcy5zZWxlY3Rpb25MaXN0ZW5lcnMpIGNiKClcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBTZWxlY3Rpb24gb3ZlcmxheTogaW52ZXJ0IGNlbGwgc3R5bGVzIGluIHRoZSBzY3JlZW4gYnVmZmVyIGl0c2VsZixcbiAgICAvLyBzbyB0aGUgZGlmZiBwaWNrcyB1cCBzZWxlY3Rpb24gYXMgb3JkaW5hcnkgY2VsbCBjaGFuZ2VzIGFuZFxuICAgIC8vIExvZ1VwZGF0ZSByZW1haW5zIGEgcHVyZSBkaWZmIGVuZ2luZS5cbiAgICAvL1xuICAgIC8vIEZ1bGwtc2NyZWVuIGRhbWFnZSAoUFIgIzIwMTIwKSBpcyBhIGNvcnJlY3RuZXNzIGJhY2tzdG9wIGZvciB0aGVcbiAgICAvLyBzaWJsaW5nLXJlc2l6ZSBibGVlZDogd2hlbiBmbGV4Ym94IHNpYmxpbmdzIHJlc2l6ZSBiZXR3ZWVuIGZyYW1lc1xuICAgIC8vIChzcGlubmVyIGFwcGVhcnMg4oaSIGJvdHRvbSBncm93cyDihpIgc2Nyb2xsYm94IHNocmlua3MpLCB0aGVcbiAgICAvLyBjYWNoZWQtY2xlYXIgKyBjbGlwLWFuZC1jdWxsICsgc2V0Q2VsbEF0IGRhbWFnZSB1bmlvbiBjYW4gbWlzc1xuICAgIC8vIHRyYW5zaXRpb24gY2VsbHMgYXQgdGhlIGJvdW5kYXJ5LiBCdXQgdGhhdCBvbmx5IGhhcHBlbnMgd2hlbiBsYXlvdXRcbiAgICAvLyBhY3R1YWxseSBTSElGVFMg4oCUIGRpZExheW91dFNoaWZ0KCkgdHJhY2tzIGV4YWN0bHkgdGhpcyAoYW55IG5vZGUnc1xuICAgIC8vIGNhY2hlZCB5b2dhIHBvc2l0aW9uL3NpemUgZGlmZmVycyBmcm9tIGN1cnJlbnQsIG9yIGEgY2hpbGQgd2FzXG4gICAgLy8gcmVtb3ZlZCkuIFN0ZWFkeS1zdGF0ZSBmcmFtZXMgKHNwaW5uZXIgcm90YXRlLCBjbG9jayB0aWNrLCB0ZXh0XG4gICAgLy8gc3RyZWFtIGludG8gZml4ZWQtaGVpZ2h0IGJveCkgZG9uJ3Qgc2hpZnQgbGF5b3V0LCBzbyBub3JtYWwgZGFtYWdlXG4gICAgLy8gYm91bmRzIGFyZSBjb3JyZWN0IGFuZCBkaWZmRWFjaCBvbmx5IGNvbXBhcmVzIHRoZSBkYW1hZ2VkIHJlZ2lvbi5cbiAgICAvL1xuICAgIC8vIFNlbGVjdGlvbiBhbHNvIHJlcXVpcmVzIGZ1bGwgZGFtYWdlOiBvdmVybGF5IHdyaXRlcyB2aWEgc2V0Q2VsbFN0eWxlSWRcbiAgICAvLyB3aGljaCBkb2Vzbid0IHRyYWNrIGRhbWFnZSwgYW5kIHByZXYtZnJhbWUgb3ZlcmxheSBjZWxscyBuZWVkIHRvIGJlXG4gICAgLy8gY29tcGFyZWQgd2hlbiBzZWxlY3Rpb24gbW92ZXMvY2xlYXJzLiBwcmV2RnJhbWVDb250YW1pbmF0ZWQgY292ZXJzXG4gICAgLy8gdGhlIGZyYW1lLWFmdGVyLXNlbGVjdGlvbi1jbGVhcnMgY2FzZS5cbiAgICBsZXQgc2VsQWN0aXZlID0gZmFsc2VcbiAgICBsZXQgaGxBY3RpdmUgPSBmYWxzZVxuICAgIGlmICh0aGlzLmFsdFNjcmVlbkFjdGl2ZSkge1xuICAgICAgc2VsQWN0aXZlID0gaGFzU2VsZWN0aW9uKHRoaXMuc2VsZWN0aW9uKVxuICAgICAgaWYgKHNlbEFjdGl2ZSkge1xuICAgICAgICBhcHBseVNlbGVjdGlvbk92ZXJsYXkoZnJhbWUuc2NyZWVuLCB0aGlzLnNlbGVjdGlvbiwgdGhpcy5zdHlsZVBvb2wpXG4gICAgICB9XG4gICAgICAvLyBTY2FuLWhpZ2hsaWdodDogaW52ZXJzZSBvbiBBTEwgdmlzaWJsZSBtYXRjaGVzIChsZXNzL3ZpbSBzdHlsZSkuXG4gICAgICAvLyBQb3NpdGlvbi1oaWdobGlnaHQgKGJlbG93KSBvdmVybGF5cyBDVVJSRU5UICh5ZWxsb3cpIG9uIHRvcC5cbiAgICAgIGhsQWN0aXZlID0gYXBwbHlTZWFyY2hIaWdobGlnaHQoXG4gICAgICAgIGZyYW1lLnNjcmVlbixcbiAgICAgICAgdGhpcy5zZWFyY2hIaWdobGlnaHRRdWVyeSxcbiAgICAgICAgdGhpcy5zdHlsZVBvb2wsXG4gICAgICApXG4gICAgICAvLyBQb3NpdGlvbi1iYXNlZCBDVVJSRU5UOiB3cml0ZSB5ZWxsb3cgYXQgcG9zaXRpb25zW2N1cnJlbnRJZHhdICtcbiAgICAgIC8vIHJvd09mZnNldC4gTm8gc2Nhbm5pbmcg4oCUIHBvc2l0aW9ucyBjYW1lIGZyb20gYSBwcmlvciBzY2FuIHdoZW5cbiAgICAgIC8vIHRoZSBtZXNzYWdlIGZpcnN0IG1vdW50ZWQuIE1lc3NhZ2UtcmVsYXRpdmUgKyByb3dPZmZzZXQgPSBzY3JlZW4uXG4gICAgICBpZiAodGhpcy5zZWFyY2hQb3NpdGlvbnMpIHtcbiAgICAgICAgY29uc3Qgc3AgPSB0aGlzLnNlYXJjaFBvc2l0aW9uc1xuICAgICAgICBjb25zdCBwb3NBcHBsaWVkID0gYXBwbHlQb3NpdGlvbmVkSGlnaGxpZ2h0KFxuICAgICAgICAgIGZyYW1lLnNjcmVlbixcbiAgICAgICAgICB0aGlzLnN0eWxlUG9vbCxcbiAgICAgICAgICBzcC5wb3NpdGlvbnMsXG4gICAgICAgICAgc3Aucm93T2Zmc2V0LFxuICAgICAgICAgIHNwLmN1cnJlbnRJZHgsXG4gICAgICAgIClcbiAgICAgICAgaGxBY3RpdmUgPSBobEFjdGl2ZSB8fCBwb3NBcHBsaWVkXG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gRnVsbC1kYW1hZ2UgYmFja3N0b3A6IGFwcGxpZXMgb24gQk9USCBhbHQtc2NyZWVuIGFuZCBtYWluLXNjcmVlbi5cbiAgICAvLyBMYXlvdXQgc2hpZnRzIChzcGlubmVyIGFwcGVhcnMsIHN0YXR1cyBsaW5lIHJlc2l6ZXMpIGNhbiBsZWF2ZSBzdGFsZVxuICAgIC8vIGNlbGxzIGF0IHNpYmxpbmcgYm91bmRhcmllcyB0aGF0IHBlci1ub2RlIGRhbWFnZSB0cmFja2luZyBtaXNzZXMuXG4gICAgLy8gU2VsZWN0aW9uL2hpZ2hsaWdodCBvdmVybGF5cyB3cml0ZSB2aWEgc2V0Q2VsbFN0eWxlSWQgd2hpY2ggZG9lc24ndFxuICAgIC8vIHRyYWNrIGRhbWFnZS4gcHJldkZyYW1lQ29udGFtaW5hdGVkIGNvdmVycyB0aGUgY2xlYW51cCBmcmFtZS5cbiAgICBpZiAoXG4gICAgICBkaWRMYXlvdXRTaGlmdCgpIHx8XG4gICAgICBzZWxBY3RpdmUgfHxcbiAgICAgIGhsQWN0aXZlIHx8XG4gICAgICB0aGlzLnByZXZGcmFtZUNvbnRhbWluYXRlZFxuICAgICkge1xuICAgICAgZnJhbWUuc2NyZWVuLmRhbWFnZSA9IHtcbiAgICAgICAgeDogMCxcbiAgICAgICAgeTogMCxcbiAgICAgICAgd2lkdGg6IGZyYW1lLnNjcmVlbi53aWR0aCxcbiAgICAgICAgaGVpZ2h0OiBmcmFtZS5zY3JlZW4uaGVpZ2h0LFxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIEFsdC1zY3JlZW46IGFuY2hvciB0aGUgcGh5c2ljYWwgY3Vyc29yIHRvICgwLDApIGJlZm9yZSBldmVyeSBkaWZmLlxuICAgIC8vIEFsbCBjdXJzb3IgbW92ZXMgaW4gbG9nLXVwZGF0ZSBhcmUgUkVMQVRJVkUgdG8gcHJldi5jdXJzb3I7IGlmIHRtdXhcbiAgICAvLyAob3IgYW55IGVtdWxhdG9yKSBwZXJ0dXJicyB0aGUgcGh5c2ljYWwgY3Vyc29yIG91dC1vZi1iYW5kIChzdGF0dXNcbiAgICAvLyBiYXIgcmVmcmVzaCwgcGFuZSByZWRyYXcsIENtZCtLIHdpcGUpLCB0aGUgcmVsYXRpdmUgbW92ZXMgZHJpZnQgYW5kXG4gICAgLy8gY29udGVudCBjcmVlcHMgdXAgMSByb3cvZnJhbWUuIENTSSBIIHJlc2V0cyB0aGUgcGh5c2ljYWwgY3Vyc29yO1xuICAgIC8vIHBhc3NpbmcgcHJldi5jdXJzb3I9KDAsMCkgbWFrZXMgdGhlIGRpZmYgY29tcHV0ZSBmcm9tIHRoZSBzYW1lIHNwb3QuXG4gICAgLy8gU2VsZi1oZWFsaW5nIGFnYWluc3QgYW55IGV4dGVybmFsIGN1cnNvciBtYW5pcHVsYXRpb24uIE1haW4tc2NyZWVuXG4gICAgLy8gY2FuJ3QgZG8gdGhpcyDigJQgY3Vyc29yLnkgdHJhY2tzIHNjcm9sbGJhY2sgcm93cyBDU0kgSCBjYW4ndCByZWFjaC5cbiAgICAvLyBUaGUgQ1NJIEggd3JpdGUgaXMgZGVmZXJyZWQgdW50aWwgYWZ0ZXIgdGhlIGRpZmYgaXMgY29tcHV0ZWQgc28gd2VcbiAgICAvLyBjYW4gc2tpcCBpdCBmb3IgZW1wdHkgZGlmZnMgKG5vIHdyaXRlcyDihpIgcGh5c2ljYWwgY3Vyc29yIHVudXNlZCkuXG4gICAgbGV0IHByZXZGcmFtZSA9IHRoaXMuZnJvbnRGcmFtZVxuICAgIGlmICh0aGlzLmFsdFNjcmVlbkFjdGl2ZSkge1xuICAgICAgcHJldkZyYW1lID0geyAuLi50aGlzLmZyb250RnJhbWUsIGN1cnNvcjogQUxUX1NDUkVFTl9BTkNIT1JfQ1VSU09SIH1cbiAgICB9XG5cbiAgICBjb25zdCB0RGlmZiA9IHBlcmZvcm1hbmNlLm5vdygpXG4gICAgY29uc3QgZGlmZiA9IHRoaXMubG9nLnJlbmRlcihcbiAgICAgIHByZXZGcmFtZSxcbiAgICAgIGZyYW1lLFxuICAgICAgdGhpcy5hbHRTY3JlZW5BY3RpdmUsXG4gICAgICAvLyBERUNTVEJNIG5lZWRzIEJTVS9FU1UgYXRvbWljaXR5IOKAlCB3aXRob3V0IGl0IHRoZSBvdXRlciB0ZXJtaW5hbFxuICAgICAgLy8gcmVuZGVycyB0aGUgc2Nyb2xsZWQtYnV0LW5vdC15ZXQtcmVwYWludGVkIGludGVybWVkaWF0ZSBzdGF0ZS5cbiAgICAgIC8vIHRtdXggaXMgdGhlIG1haW4gY2FzZSAocmUtZW1pdHMgREVDU1RCTSB3aXRoIGl0cyBvd24gdGltaW5nIGFuZFxuICAgICAgLy8gZG9lc24ndCBpbXBsZW1lbnQgREVDIDIwMjYsIHNvIFNZTkNfT1VUUFVUX1NVUFBPUlRFRCBpcyBmYWxzZSkuXG4gICAgICBTWU5DX09VVFBVVF9TVVBQT1JURUQsXG4gICAgKVxuICAgIGNvbnN0IGRpZmZNcyA9IHBlcmZvcm1hbmNlLm5vdygpIC0gdERpZmZcbiAgICAvLyBTd2FwIGJ1ZmZlcnNcbiAgICB0aGlzLmJhY2tGcmFtZSA9IHRoaXMuZnJvbnRGcmFtZVxuICAgIHRoaXMuZnJvbnRGcmFtZSA9IGZyYW1lXG5cbiAgICAvLyBQZXJpb2RpY2FsbHkgcmVzZXQgY2hhci9oeXBlcmxpbmsgcG9vbHMgdG8gcHJldmVudCB1bmJvdW5kZWQgZ3Jvd3RoXG4gICAgLy8gZHVyaW5nIGxvbmcgc2Vzc2lvbnMuIDUgbWludXRlcyBpcyBpbmZyZXF1ZW50IGVub3VnaCB0aGF0IHRoZSBPKGNlbGxzKVxuICAgIC8vIG1pZ3JhdGlvbiBjb3N0IGlzIG5lZ2xpZ2libGUuIFJldXNlcyByZW5kZXJTdGFydCB0byBhdm9pZCBleHRyYSBjbG9jayBjYWxsLlxuICAgIGlmIChyZW5kZXJTdGFydCAtIHRoaXMubGFzdFBvb2xSZXNldFRpbWUgPiA1ICogNjAgKiAxMDAwKSB7XG4gICAgICB0aGlzLnJlc2V0UG9vbHMoKVxuICAgICAgdGhpcy5sYXN0UG9vbFJlc2V0VGltZSA9IHJlbmRlclN0YXJ0XG4gICAgfVxuXG4gICAgY29uc3QgZmxpY2tlcnM6IEZyYW1lRXZlbnRbJ2ZsaWNrZXJzJ10gPSBbXVxuICAgIGZvciAoY29uc3QgcGF0Y2ggb2YgZGlmZikge1xuICAgICAgaWYgKHBhdGNoLnR5cGUgPT09ICdjbGVhclRlcm1pbmFsJykge1xuICAgICAgICBmbGlja2Vycy5wdXNoKHtcbiAgICAgICAgICBkZXNpcmVkSGVpZ2h0OiBmcmFtZS5zY3JlZW4uaGVpZ2h0LFxuICAgICAgICAgIGF2YWlsYWJsZUhlaWdodDogZnJhbWUudmlld3BvcnQuaGVpZ2h0LFxuICAgICAgICAgIHJlYXNvbjogcGF0Y2gucmVhc29uLFxuICAgICAgICB9KVxuICAgICAgICBpZiAoaXNEZWJ1Z1JlcGFpbnRzRW5hYmxlZCgpICYmIHBhdGNoLmRlYnVnKSB7XG4gICAgICAgICAgY29uc3QgY2hhaW4gPSBkb20uZmluZE93bmVyQ2hhaW5BdFJvdyhcbiAgICAgICAgICAgIHRoaXMucm9vdE5vZGUsXG4gICAgICAgICAgICBwYXRjaC5kZWJ1Zy50cmlnZ2VyWSxcbiAgICAgICAgICApXG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYFtSRVBBSU5UXSBmdWxsIHJlc2V0IMK3ICR7cGF0Y2gucmVhc29ufSDCtyByb3cgJHtwYXRjaC5kZWJ1Zy50cmlnZ2VyWX1cXG5gICtcbiAgICAgICAgICAgICAgYCAgcHJldjogXCIke3BhdGNoLmRlYnVnLnByZXZMaW5lfVwiXFxuYCArXG4gICAgICAgICAgICAgIGAgIG5leHQ6IFwiJHtwYXRjaC5kZWJ1Zy5uZXh0TGluZX1cIlxcbmAgK1xuICAgICAgICAgICAgICBgICBjdWxwcml0OiAke2NoYWluLmxlbmd0aCA/IGNoYWluLmpvaW4oJyA8ICcpIDogJyhubyBvd25lciBjaGFpbiBjYXB0dXJlZCknfWAsXG4gICAgICAgICAgICB7IGxldmVsOiAnd2FybicgfSxcbiAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBjb25zdCB0T3B0aW1pemUgPSBwZXJmb3JtYW5jZS5ub3coKVxuICAgIGNvbnN0IG9wdGltaXplZCA9IG9wdGltaXplKGRpZmYpXG4gICAgY29uc3Qgb3B0aW1pemVNcyA9IHBlcmZvcm1hbmNlLm5vdygpIC0gdE9wdGltaXplXG4gICAgY29uc3QgaGFzRGlmZiA9IG9wdGltaXplZC5sZW5ndGggPiAwXG4gICAgaWYgKHRoaXMuYWx0U2NyZWVuQWN0aXZlICYmIGhhc0RpZmYpIHtcbiAgICAgIC8vIFByZXBlbmQgQ1NJIEggdG8gYW5jaG9yIHRoZSBwaHlzaWNhbCBjdXJzb3IgdG8gKDAsMCkgc29cbiAgICAgIC8vIGxvZy11cGRhdGUncyByZWxhdGl2ZSBtb3ZlcyBjb21wdXRlIGZyb20gYSBrbm93biBzcG90IChzZWxmLWhlYWxpbmdcbiAgICAgIC8vIGFnYWluc3Qgb3V0LW9mLWJhbmQgY3Vyc29yIGRyaWZ0LCBzZWUgdGhlIEFMVF9TQ1JFRU5fQU5DSE9SX0NVUlNPUlxuICAgICAgLy8gY29tbWVudCBhYm92ZSkuIEFwcGVuZCBDU0kgcm93OzEgSCB0byBwYXJrIHRoZSBjdXJzb3IgYXQgdGhlIGJvdHRvbVxuICAgICAgLy8gcm93ICh3aGVyZSB0aGUgcHJvbXB0IGlucHV0IGlzKSDigJQgd2l0aG91dCB0aGlzLCB0aGUgY3Vyc29yIGVuZHNcbiAgICAgIC8vIHdoZXJldmVyIHRoZSBsYXN0IGRpZmYgd3JpdGUgbGFuZGVkIChhIGRpZmZlcmVudCByb3cgZXZlcnkgZnJhbWUpLFxuICAgICAgLy8gbWFraW5nIGlUZXJtMidzIGN1cnNvciBndWlkZSBmbGlja2VyIGFzIGl0IGNoYXNlcyB0aGUgY3Vyc29yLlxuICAgICAgLy8gQlNVL0VTVSBwcm90ZWN0cyBjb250ZW50IGF0b21pY2l0eSBidXQgaVRlcm0yJ3MgZ3VpZGUgdHJhY2tzIGN1cnNvclxuICAgICAgLy8gcG9zaXRpb24gaW5kZXBlbmRlbnRseS4gUGFya2luZyBhdCBib3R0b20gKG5vdCAwLDApIGtlZXBzIHRoZSBndWlkZVxuICAgICAgLy8gd2hlcmUgdGhlIHVzZXIncyBhdHRlbnRpb24gaXMuXG4gICAgICAvL1xuICAgICAgLy8gQWZ0ZXIgcmVzaXplLCBwcmVwZW5kIEVSQVNFX1NDUkVFTiB0b28uIFRoZSBkaWZmIG9ubHkgd3JpdGVzIGNlbGxzXG4gICAgICAvLyB0aGF0IGNoYW5nZWQ7IGNlbGxzIHdoZXJlIG5ldz1ibGFuayBhbmQgcHJldi1idWZmZXI9YmxhbmsgZ2V0IHNraXBwZWRcbiAgICAgIC8vIOKAlCBidXQgdGhlIHBoeXNpY2FsIHRlcm1pbmFsIHN0aWxsIGhhcyBzdGFsZSBjb250ZW50IHRoZXJlIChzaG9ydGVyXG4gICAgICAvLyBsaW5lcyBhdCBuZXcgd2lkdGggbGVhdmUgb2xkLXdpZHRoIHRleHQgdGFpbHMgdmlzaWJsZSkuIEVSQVNFIGluc2lkZVxuICAgICAgLy8gQlNVL0VTVSBpcyBhdG9taWM6IG9sZCBjb250ZW50IHN0YXlzIHZpc2libGUgdW50aWwgdGhlIHdob2xlXG4gICAgICAvLyBlcmFzZStwYWludCBsYW5kcywgdGhlbiBzd2FwcyBpbiBvbmUgZ28uIFdyaXRpbmcgRVJBU0VfU0NSRUVOXG4gICAgICAvLyBzeW5jaHJvbm91c2x5IGluIGhhbmRsZVJlc2l6ZSB3b3VsZCBibGFuayB0aGUgc2NyZWVuIGZvciB0aGUgfjgwbXNcbiAgICAgIC8vIHJlbmRlcigpIHRha2VzLlxuICAgICAgaWYgKHRoaXMubmVlZHNFcmFzZUJlZm9yZVBhaW50KSB7XG4gICAgICAgIHRoaXMubmVlZHNFcmFzZUJlZm9yZVBhaW50ID0gZmFsc2VcbiAgICAgICAgb3B0aW1pemVkLnVuc2hpZnQoRVJBU0VfVEhFTl9IT01FX1BBVENIKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgb3B0aW1pemVkLnVuc2hpZnQoQ1VSU09SX0hPTUVfUEFUQ0gpXG4gICAgICB9XG4gICAgICBvcHRpbWl6ZWQucHVzaCh0aGlzLmFsdFNjcmVlblBhcmtQYXRjaClcbiAgICB9XG5cbiAgICAvLyBOYXRpdmUgY3Vyc29yIHBvc2l0aW9uaW5nOiBwYXJrIHRoZSB0ZXJtaW5hbCBjdXJzb3IgYXQgdGhlIGRlY2xhcmVkXG4gICAgLy8gcG9zaXRpb24gc28gSU1FIHByZWVkaXQgdGV4dCByZW5kZXJzIGlubGluZSBhbmQgc2NyZWVuIHJlYWRlcnMgL1xuICAgIC8vIG1hZ25pZmllcnMgY2FuIGZvbGxvdyB0aGUgaW5wdXQuIG5vZGVDYWNoZSBob2xkcyB0aGUgYWJzb2x1dGUgc2NyZWVuXG4gICAgLy8gcmVjdCBwb3B1bGF0ZWQgYnkgcmVuZGVyTm9kZVRvT3V0cHV0IHRoaXMgZnJhbWUgKGluY2x1ZGluZyBzY3JvbGxUb3BcbiAgICAvLyB0cmFuc2xhdGlvbikg4oCUIGlmIHRoZSBkZWNsYXJlZCBub2RlIGRpZG4ndCByZW5kZXIgKHN0YWxlIGRlY2xhcmF0aW9uXG4gICAgLy8gYWZ0ZXIgcmVtb3VudCwgb3Igc2Nyb2xsZWQgb3V0IG9mIHZpZXcpLCBpdCB3b24ndCBiZSBpbiB0aGUgY2FjaGVcbiAgICAvLyBhbmQgbm8gbW92ZSBpcyBlbWl0dGVkLlxuICAgIGNvbnN0IGRlY2wgPSB0aGlzLmN1cnNvckRlY2xhcmF0aW9uXG4gICAgY29uc3QgcmVjdCA9IGRlY2wgIT09IG51bGwgPyBub2RlQ2FjaGUuZ2V0KGRlY2wubm9kZSkgOiB1bmRlZmluZWRcbiAgICBjb25zdCB0YXJnZXQgPVxuICAgICAgZGVjbCAhPT0gbnVsbCAmJiByZWN0ICE9PSB1bmRlZmluZWRcbiAgICAgICAgPyB7IHg6IHJlY3QueCArIGRlY2wucmVsYXRpdmVYLCB5OiByZWN0LnkgKyBkZWNsLnJlbGF0aXZlWSB9XG4gICAgICAgIDogbnVsbFxuICAgIGNvbnN0IHBhcmtlZCA9IHRoaXMuZGlzcGxheUN1cnNvclxuXG4gICAgLy8gUHJlc2VydmUgdGhlIGVtcHR5LWRpZmYgemVyby13cml0ZSBmYXN0IHBhdGg6IHNraXAgYWxsIGN1cnNvciB3cml0ZXNcbiAgICAvLyB3aGVuIG5vdGhpbmcgcmVuZGVyZWQgQU5EIHRoZSBwYXJrIHRhcmdldCBpcyB1bmNoYW5nZWQuXG4gICAgY29uc3QgdGFyZ2V0TW92ZWQgPVxuICAgICAgdGFyZ2V0ICE9PSBudWxsICYmXG4gICAgICAocGFya2VkID09PSBudWxsIHx8IHBhcmtlZC54ICE9PSB0YXJnZXQueCB8fCBwYXJrZWQueSAhPT0gdGFyZ2V0LnkpXG4gICAgaWYgKGhhc0RpZmYgfHwgdGFyZ2V0TW92ZWQgfHwgKHRhcmdldCA9PT0gbnVsbCAmJiBwYXJrZWQgIT09IG51bGwpKSB7XG4gICAgICAvLyBNYWluLXNjcmVlbiBwcmVhbWJsZTogbG9nLXVwZGF0ZSdzIHJlbGF0aXZlIG1vdmVzIGFzc3VtZSB0aGVcbiAgICAgIC8vIHBoeXNpY2FsIGN1cnNvciBpcyBhdCBwcmV2RnJhbWUuY3Vyc29yLiBJZiBsYXN0IGZyYW1lIHBhcmtlZCBpdFxuICAgICAgLy8gZWxzZXdoZXJlLCBtb3ZlIGJhY2sgYmVmb3JlIHRoZSBkaWZmIHJ1bnMuIEFsdC1zY3JlZW4ncyBDU0kgSFxuICAgICAgLy8gYWxyZWFkeSByZXNldHMgdG8gKDAsMCkgc28gbm8gcHJlYW1ibGUgbmVlZGVkLlxuICAgICAgaWYgKHBhcmtlZCAhPT0gbnVsbCAmJiAhdGhpcy5hbHRTY3JlZW5BY3RpdmUgJiYgaGFzRGlmZikge1xuICAgICAgICBjb25zdCBwZHggPSBwcmV2RnJhbWUuY3Vyc29yLnggLSBwYXJrZWQueFxuICAgICAgICBjb25zdCBwZHkgPSBwcmV2RnJhbWUuY3Vyc29yLnkgLSBwYXJrZWQueVxuICAgICAgICBpZiAocGR4ICE9PSAwIHx8IHBkeSAhPT0gMCkge1xuICAgICAgICAgIG9wdGltaXplZC51bnNoaWZ0KHsgdHlwZTogJ3N0ZG91dCcsIGNvbnRlbnQ6IGN1cnNvck1vdmUocGR4LCBwZHkpIH0pXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKHRhcmdldCAhPT0gbnVsbCkge1xuICAgICAgICBpZiAodGhpcy5hbHRTY3JlZW5BY3RpdmUpIHtcbiAgICAgICAgICAvLyBBYnNvbHV0ZSBDVVAgKDEtaW5kZXhlZCk7IG5leHQgZnJhbWUncyBDU0kgSCByZXNldHMgcmVnYXJkbGVzcy5cbiAgICAgICAgICAvLyBFbWl0dGVkIGFmdGVyIGFsdFNjcmVlblBhcmtQYXRjaCBzbyB0aGUgZGVjbGFyZWQgcG9zaXRpb24gd2lucy5cbiAgICAgICAgICBjb25zdCByb3cgPSBNYXRoLm1pbihNYXRoLm1heCh0YXJnZXQueSArIDEsIDEpLCB0ZXJtaW5hbFJvd3MpXG4gICAgICAgICAgY29uc3QgY29sID0gTWF0aC5taW4oTWF0aC5tYXgodGFyZ2V0LnggKyAxLCAxKSwgdGVybWluYWxXaWR0aClcbiAgICAgICAgICBvcHRpbWl6ZWQucHVzaCh7IHR5cGU6ICdzdGRvdXQnLCBjb250ZW50OiBjdXJzb3JQb3NpdGlvbihyb3csIGNvbCkgfSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBBZnRlciB0aGUgZGlmZiAob3IgcHJlYW1ibGUpLCBjdXJzb3IgaXMgYXQgZnJhbWUuY3Vyc29yLiBJZiBub1xuICAgICAgICAgIC8vIGRpZmYgQU5EIHByZXZpb3VzbHkgcGFya2VkLCBpdCdzIHN0aWxsIGF0IHRoZSBvbGQgcGFyayBwb3NpdGlvblxuICAgICAgICAgIC8vIChsb2ctdXBkYXRlIHdyb3RlIG5vdGhpbmcpLiBPdGhlcndpc2UgaXQncyBhdCBmcmFtZS5jdXJzb3IuXG4gICAgICAgICAgY29uc3QgZnJvbSA9XG4gICAgICAgICAgICAhaGFzRGlmZiAmJiBwYXJrZWQgIT09IG51bGxcbiAgICAgICAgICAgICAgPyBwYXJrZWRcbiAgICAgICAgICAgICAgOiB7IHg6IGZyYW1lLmN1cnNvci54LCB5OiBmcmFtZS5jdXJzb3IueSB9XG4gICAgICAgICAgY29uc3QgZHggPSB0YXJnZXQueCAtIGZyb20ueFxuICAgICAgICAgIGNvbnN0IGR5ID0gdGFyZ2V0LnkgLSBmcm9tLnlcbiAgICAgICAgICBpZiAoZHggIT09IDAgfHwgZHkgIT09IDApIHtcbiAgICAgICAgICAgIG9wdGltaXplZC5wdXNoKHsgdHlwZTogJ3N0ZG91dCcsIGNvbnRlbnQ6IGN1cnNvck1vdmUoZHgsIGR5KSB9KVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLmRpc3BsYXlDdXJzb3IgPSB0YXJnZXRcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIERlY2xhcmF0aW9uIGNsZWFyZWQgKGlucHV0IGJsdXIsIHVubW91bnQpLiBSZXN0b3JlIHBoeXNpY2FsIGN1cnNvclxuICAgICAgICAvLyB0byBmcmFtZS5jdXJzb3IgYmVmb3JlIGZvcmdldHRpbmcgdGhlIHBhcmsgcG9zaXRpb24g4oCUIG90aGVyd2lzZVxuICAgICAgICAvLyBkaXNwbGF5Q3Vyc29yPW51bGwgbGllcyBhYm91dCB3aGVyZSB0aGUgY3Vyc29yIGlzLCBhbmQgdGhlIE5FWFRcbiAgICAgICAgLy8gZnJhbWUncyBwcmVhbWJsZSAob3IgbG9nLXVwZGF0ZSdzIHJlbGF0aXZlIG1vdmVzKSBjb21wdXRlcyBmcm9tIGFcbiAgICAgICAgLy8gd3Jvbmcgc3BvdC4gVGhlIHByZWFtYmxlIGFib3ZlIGhhbmRsZXMgaGFzRGlmZjsgdGhpcyBoYW5kbGVzXG4gICAgICAgIC8vICFoYXNEaWZmIChlLmcuIGFjY2Vzc2liaWxpdHkgbW9kZSB3aGVyZSBibHVyIGRvZXNuJ3QgY2hhbmdlXG4gICAgICAgIC8vIHJlbmRlcmVkVmFsdWUgc2luY2UgaW52ZXJ0IGlzIGlkZW50aXR5KS5cbiAgICAgICAgaWYgKHBhcmtlZCAhPT0gbnVsbCAmJiAhdGhpcy5hbHRTY3JlZW5BY3RpdmUgJiYgIWhhc0RpZmYpIHtcbiAgICAgICAgICBjb25zdCByZHggPSBmcmFtZS5jdXJzb3IueCAtIHBhcmtlZC54XG4gICAgICAgICAgY29uc3QgcmR5ID0gZnJhbWUuY3Vyc29yLnkgLSBwYXJrZWQueVxuICAgICAgICAgIGlmIChyZHggIT09IDAgfHwgcmR5ICE9PSAwKSB7XG4gICAgICAgICAgICBvcHRpbWl6ZWQucHVzaCh7IHR5cGU6ICdzdGRvdXQnLCBjb250ZW50OiBjdXJzb3JNb3ZlKHJkeCwgcmR5KSB9KVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLmRpc3BsYXlDdXJzb3IgPSBudWxsXG4gICAgICB9XG4gICAgfVxuXG4gICAgY29uc3QgdFdyaXRlID0gcGVyZm9ybWFuY2Uubm93KClcbiAgICB3cml0ZURpZmZUb1Rlcm1pbmFsKFxuICAgICAgdGhpcy50ZXJtaW5hbCxcbiAgICAgIG9wdGltaXplZCxcbiAgICAgIHRoaXMuYWx0U2NyZWVuQWN0aXZlICYmICFTWU5DX09VVFBVVF9TVVBQT1JURUQsXG4gICAgKVxuICAgIGNvbnN0IHdyaXRlTXMgPSBwZXJmb3JtYW5jZS5ub3coKSAtIHRXcml0ZVxuXG4gICAgLy8gVXBkYXRlIGJsaXQgc2FmZXR5IGZvciB0aGUgTkVYVCBmcmFtZS4gVGhlIGZyYW1lIGp1c3QgcmVuZGVyZWRcbiAgICAvLyBiZWNvbWVzIGZyb250RnJhbWUgKD0gbmV4dCBmcmFtZSdzIHByZXZTY3JlZW4pLiBJZiB3ZSBhcHBsaWVkIHRoZVxuICAgIC8vIHNlbGVjdGlvbiBvdmVybGF5LCB0aGF0IGJ1ZmZlciBoYXMgaW52ZXJ0ZWQgY2VsbHMuIHNlbEFjdGl2ZS9obEFjdGl2ZVxuICAgIC8vIGFyZSBvbmx5IGV2ZXIgdHJ1ZSBpbiBhbHQtc2NyZWVuOyBpbiBtYWluLXNjcmVlbiB0aGlzIGlzIGZhbHNl4oaSZmFsc2UuXG4gICAgdGhpcy5wcmV2RnJhbWVDb250YW1pbmF0ZWQgPSBzZWxBY3RpdmUgfHwgaGxBY3RpdmVcblxuICAgIC8vIEEgU2Nyb2xsQm94IGhhcyBwZW5kaW5nU2Nyb2xsRGVsdGEgbGVmdCB0byBkcmFpbiDigJQgc2NoZWR1bGUgdGhlIG5leHRcbiAgICAvLyBmcmFtZS4gTVVTVCBOT1QgY2FsbCB0aGlzLnNjaGVkdWxlUmVuZGVyKCkgaGVyZTogd2UncmUgaW5zaWRlIGFcbiAgICAvLyB0cmFpbGluZy1lZGdlIHRocm90dGxlIGludm9jYXRpb24sIHRpbWVySWQgaXMgdW5kZWZpbmVkLCBhbmQgbG9kYXNoJ3NcbiAgICAvLyBkZWJvdW5jZSBzZWVzIHRpbWVTaW5jZUxhc3RDYWxsID49IHdhaXQgKGxhc3QgY2FsbCB3YXMgYXQgdGhlIHN0YXJ0XG4gICAgLy8gb2YgdGhpcyB3aW5kb3cpIOKGkiBsZWFkaW5nRWRnZSBmaXJlcyBJTU1FRElBVEVMWSDihpIgZG91YmxlIHJlbmRlciB+MC4xbXNcbiAgICAvLyBhcGFydCDihpIgamFuay4gVXNlIGEgcGxhaW4gdGltZW91dC4gSWYgYSB3aGVlbCBldmVudCBhcnJpdmVzIGZpcnN0LFxuICAgIC8vIGl0cyBzY2hlZHVsZVJlbmRlciBwYXRoIGZpcmVzIGEgcmVuZGVyIHdoaWNoIGNsZWFycyB0aGlzIHRpbWVyIGF0XG4gICAgLy8gdGhlIHRvcCBvZiBvblJlbmRlciDigJQgbm8gZG91YmxlLlxuICAgIC8vXG4gICAgLy8gRHJhaW4gZnJhbWVzIGFyZSBjaGVhcCAoREVDU1RCTSArIH4xMCBwYXRjaGVzLCB+MjAwIGJ5dGVzKSBzbyBydW4gYXRcbiAgICAvLyBxdWFydGVyIGludGVydmFsICh+MjUwZnBzLCBzZXRUaW1lb3V0IHByYWN0aWNhbCBmbG9vcikgZm9yIG1heCBzY3JvbGxcbiAgICAvLyBzcGVlZC4gUmVndWxhciByZW5kZXJzIHN0YXkgYXQgRlJBTUVfSU5URVJWQUxfTVMgdmlhIHRoZSB0aHJvdHRsZS5cbiAgICBpZiAoZnJhbWUuc2Nyb2xsRHJhaW5QZW5kaW5nKSB7XG4gICAgICB0aGlzLmRyYWluVGltZXIgPSBzZXRUaW1lb3V0KFxuICAgICAgICAoKSA9PiB0aGlzLm9uUmVuZGVyKCksXG4gICAgICAgIEZSQU1FX0lOVEVSVkFMX01TID4+IDIsXG4gICAgICApXG4gICAgfVxuXG4gICAgY29uc3QgeW9nYU1zID0gZ2V0TGFzdFlvZ2FNcygpXG4gICAgY29uc3QgY29tbWl0TXMgPSBnZXRMYXN0Q29tbWl0TXMoKVxuICAgIGNvbnN0IHljID0gdGhpcy5sYXN0WW9nYUNvdW50ZXJzXG4gICAgLy8gUmVzZXQgc28gZHJhaW4tb25seSBmcmFtZXMgKG5vIFJlYWN0IGNvbW1pdCkgZG9uJ3QgcmVwZWF0IHN0YWxlIHZhbHVlcy5cbiAgICByZXNldFByb2ZpbGVDb3VudGVycygpXG4gICAgdGhpcy5sYXN0WW9nYUNvdW50ZXJzID0ge1xuICAgICAgbXM6IDAsXG4gICAgICB2aXNpdGVkOiAwLFxuICAgICAgbWVhc3VyZWQ6IDAsXG4gICAgICBjYWNoZUhpdHM6IDAsXG4gICAgICBsaXZlOiAwLFxuICAgIH1cbiAgICB0aGlzLm9wdGlvbnMub25GcmFtZT8uKHtcbiAgICAgIGR1cmF0aW9uTXM6IHBlcmZvcm1hbmNlLm5vdygpIC0gcmVuZGVyU3RhcnQsXG4gICAgICBwaGFzZXM6IHtcbiAgICAgICAgcmVuZGVyZXI6IHJlbmRlcmVyTXMsXG4gICAgICAgIGRpZmY6IGRpZmZNcyxcbiAgICAgICAgb3B0aW1pemU6IG9wdGltaXplTXMsXG4gICAgICAgIHdyaXRlOiB3cml0ZU1zLFxuICAgICAgICBwYXRjaGVzOiBkaWZmLmxlbmd0aCxcbiAgICAgICAgeW9nYTogeW9nYU1zLFxuICAgICAgICBjb21taXQ6IGNvbW1pdE1zLFxuICAgICAgICB5b2dhVmlzaXRlZDogeWMudmlzaXRlZCxcbiAgICAgICAgeW9nYU1lYXN1cmVkOiB5Yy5tZWFzdXJlZCxcbiAgICAgICAgeW9nYUNhY2hlSGl0czogeWMuY2FjaGVIaXRzLFxuICAgICAgICB5b2dhTGl2ZTogeWMubGl2ZSxcbiAgICAgIH0sXG4gICAgICBmbGlja2VycyxcbiAgICB9KVxuICB9XG5cbiAgcGF1c2UoKTogdm9pZCB7XG4gICAgLy8gRmx1c2ggcGVuZGluZyBSZWFjdCB1cGRhdGVzIGFuZCByZW5kZXIgYmVmb3JlIHBhdXNpbmcuXG4gICAgLy8gQHRzLWV4cGVjdC1lcnJvciBmbHVzaFN5bmNGcm9tUmVjb25jaWxlciBleGlzdHMgaW4gcmVhY3QtcmVjb25jaWxlciAwLjMxIGJ1dCBub3QgaW4gQHR5cGVzL3JlYWN0LXJlY29uY2lsZXJcbiAgICByZWNvbmNpbGVyLmZsdXNoU3luY0Zyb21SZWNvbmNpbGVyKClcbiAgICB0aGlzLm9uUmVuZGVyKClcblxuICAgIHRoaXMuaXNQYXVzZWQgPSB0cnVlXG4gIH1cblxuICByZXN1bWUoKTogdm9pZCB7XG4gICAgdGhpcy5pc1BhdXNlZCA9IGZhbHNlXG4gICAgdGhpcy5vblJlbmRlcigpXG4gIH1cblxuICAvKipcbiAgICogUmVzZXQgZnJhbWUgYnVmZmVycyBzbyB0aGUgbmV4dCByZW5kZXIgd3JpdGVzIHRoZSBmdWxsIHNjcmVlbiBmcm9tIHNjcmF0Y2guXG4gICAqIENhbGwgdGhpcyBiZWZvcmUgcmVzdW1lKCkgd2hlbiB0aGUgdGVybWluYWwgY29udGVudCBoYXMgYmVlbiBjb3JydXB0ZWQgYnlcbiAgICogYW4gZXh0ZXJuYWwgcHJvY2VzcyAoZS5nLiB0bXV4LCBzaGVsbCwgZnVsbC1zY3JlZW4gVFVJKS5cbiAgICovXG4gIHJlcGFpbnQoKTogdm9pZCB7XG4gICAgdGhpcy5mcm9udEZyYW1lID0gZW1wdHlGcmFtZShcbiAgICAgIHRoaXMuZnJvbnRGcmFtZS52aWV3cG9ydC5oZWlnaHQsXG4gICAgICB0aGlzLmZyb250RnJhbWUudmlld3BvcnQud2lkdGgsXG4gICAgICB0aGlzLnN0eWxlUG9vbCxcbiAgICAgIHRoaXMuY2hhclBvb2wsXG4gICAgICB0aGlzLmh5cGVybGlua1Bvb2wsXG4gICAgKVxuICAgIHRoaXMuYmFja0ZyYW1lID0gZW1wdHlGcmFtZShcbiAgICAgIHRoaXMuYmFja0ZyYW1lLnZpZXdwb3J0LmhlaWdodCxcbiAgICAgIHRoaXMuYmFja0ZyYW1lLnZpZXdwb3J0LndpZHRoLFxuICAgICAgdGhpcy5zdHlsZVBvb2wsXG4gICAgICB0aGlzLmNoYXJQb29sLFxuICAgICAgdGhpcy5oeXBlcmxpbmtQb29sLFxuICAgIClcbiAgICB0aGlzLmxvZy5yZXNldCgpXG4gICAgLy8gUGh5c2ljYWwgY3Vyc29yIHBvc2l0aW9uIGlzIHVua25vd24gYWZ0ZXIgZXh0ZXJuYWwgdGVybWluYWwgY29ycnVwdGlvbi5cbiAgICAvLyBDbGVhciBkaXNwbGF5Q3Vyc29yIHNvIHRoZSBjdXJzb3IgcHJlYW1ibGUgZG9lc24ndCBlbWl0IGEgc3RhbGVcbiAgICAvLyByZWxhdGl2ZSBtb3ZlIGZyb20gd2hlcmUgd2UgbGFzdCBwYXJrZWQgaXQuXG4gICAgdGhpcy5kaXNwbGF5Q3Vyc29yID0gbnVsbFxuICB9XG5cbiAgLyoqXG4gICAqIENsZWFyIHRoZSBwaHlzaWNhbCB0ZXJtaW5hbCBhbmQgZm9yY2UgYSBmdWxsIHJlZHJhdy5cbiAgICpcbiAgICogVGhlIHRyYWRpdGlvbmFsIHJlYWRsaW5lIGN0cmwrbCDigJQgY2xlYXJzIHRoZSB2aXNpYmxlIHNjcmVlbiBhbmRcbiAgICogcmVkcmF3cyB0aGUgY3VycmVudCBjb250ZW50LiBBbHNvIHRoZSByZWNvdmVyeSBwYXRoIHdoZW4gdGhlIHRlcm1pbmFsXG4gICAqIHdhcyBjbGVhcmVkIGV4dGVybmFsbHkgKG1hY09TIENtZCtLKSBhbmQgSW5rJ3MgZGlmZiBlbmdpbmUgdGhpbmtzXG4gICAqIHVuY2hhbmdlZCBjZWxscyBkb24ndCBuZWVkIHJlcGFpbnRpbmcuIFNjcm9sbGJhY2sgaXMgcHJlc2VydmVkLlxuICAgKi9cbiAgZm9yY2VSZWRyYXcoKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLm9wdGlvbnMuc3Rkb3V0LmlzVFRZIHx8IHRoaXMuaXNVbm1vdW50ZWQgfHwgdGhpcy5pc1BhdXNlZCkgcmV0dXJuXG4gICAgdGhpcy5vcHRpb25zLnN0ZG91dC53cml0ZShFUkFTRV9TQ1JFRU4gKyBDVVJTT1JfSE9NRSlcbiAgICBpZiAodGhpcy5hbHRTY3JlZW5BY3RpdmUpIHtcbiAgICAgIHRoaXMucmVzZXRGcmFtZXNGb3JBbHRTY3JlZW4oKVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJlcGFpbnQoKVxuICAgICAgLy8gcmVwYWludCgpIHJlc2V0cyBmcm9udEZyYW1lIHRvIDDDlzAuIFdpdGhvdXQgdGhpcyBmbGFnIHRoZSBuZXh0XG4gICAgICAvLyBmcmFtZSdzIGJsaXQgb3B0aW1pemF0aW9uIGNvcGllcyBmcm9tIHRoYXQgZW1wdHkgc2NyZWVuIGFuZCB0aGVcbiAgICAgIC8vIGRpZmYgc2VlcyBubyBjb250ZW50LiBvblJlbmRlciByZXNldHMgdGhlIGZsYWcgYXQgZnJhbWUgZW5kLlxuICAgICAgdGhpcy5wcmV2RnJhbWVDb250YW1pbmF0ZWQgPSB0cnVlXG4gICAgfVxuICAgIHRoaXMub25SZW5kZXIoKVxuICB9XG5cbiAgLyoqXG4gICAqIE1hcmsgdGhlIHByZXZpb3VzIGZyYW1lIGFzIHVudHJ1c3R3b3J0aHkgZm9yIGJsaXQsIGZvcmNpbmcgdGhlIG5leHRcbiAgICogcmVuZGVyIHRvIGRvIGEgZnVsbC1kYW1hZ2UgZGlmZiBpbnN0ZWFkIG9mIHRoZSBwZXItbm9kZSBmYXN0IHBhdGguXG4gICAqXG4gICAqIExpZ2h0ZXIgdGhhbiBmb3JjZVJlZHJhdygpIOKAlCBubyBzY3JlZW4gY2xlYXIsIG5vIGV4dHJhIHdyaXRlLiBDYWxsXG4gICAqIGZyb20gYSB1c2VMYXlvdXRFZmZlY3QgY2xlYW51cCB3aGVuIHVubW91bnRpbmcgYSB0YWxsIG92ZXJsYXk6IHRoZVxuICAgKiBibGl0IGZhc3QgcGF0aCBjYW4gY29weSBzdGFsZSBjZWxscyBmcm9tIHRoZSBvdmVybGF5IGZyYW1lIGludG8gcm93c1xuICAgKiB0aGUgc2hydW5rZW4gbGF5b3V0IG5vIGxvbmdlciByZWFjaGVzLCBsZWF2aW5nIGEgZ2hvc3QgdGl0bGUvZGl2aWRlci5cbiAgICogb25SZW5kZXIgcmVzZXRzIHRoZSBmbGFnIGF0IGZyYW1lIGVuZCBzbyBpdCdzIG9uZS1zaG90LlxuICAgKi9cbiAgaW52YWxpZGF0ZVByZXZGcmFtZSgpOiB2b2lkIHtcbiAgICB0aGlzLnByZXZGcmFtZUNvbnRhbWluYXRlZCA9IHRydWVcbiAgfVxuXG4gIC8qKlxuICAgKiBDYWxsZWQgYnkgdGhlIDxBbHRlcm5hdGVTY3JlZW4+IGNvbXBvbmVudCBvbiBtb3VudC91bm1vdW50LlxuICAgKiBDb250cm9scyBjdXJzb3IueSBjbGFtcGluZyBpbiB0aGUgcmVuZGVyZXIgYW5kIGdhdGVzIGFsdC1zY3JlZW4tYXdhcmVcbiAgICogYmVoYXZpb3IgaW4gU0lHQ09OVC9yZXNpemUvdW5tb3VudCBoYW5kbGVycy4gUmVwYWludHMgb24gY2hhbmdlIHNvXG4gICAqIHRoZSBmaXJzdCBhbHQtc2NyZWVuIGZyYW1lIChhbmQgZmlyc3QgbWFpbi1zY3JlZW4gZnJhbWUgb24gZXhpdCkgaXNcbiAgICogYSBmdWxsIHJlZHJhdyB3aXRoIG5vIHN0YWxlIGRpZmYgc3RhdGUuXG4gICAqL1xuICBzZXRBbHRTY3JlZW5BY3RpdmUoYWN0aXZlOiBib29sZWFuLCBtb3VzZVRyYWNraW5nID0gZmFsc2UpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5hbHRTY3JlZW5BY3RpdmUgPT09IGFjdGl2ZSkgcmV0dXJuXG4gICAgdGhpcy5hbHRTY3JlZW5BY3RpdmUgPSBhY3RpdmVcbiAgICB0aGlzLmFsdFNjcmVlbk1vdXNlVHJhY2tpbmcgPSBhY3RpdmUgJiYgbW91c2VUcmFja2luZ1xuICAgIGlmIChhY3RpdmUpIHtcbiAgICAgIHRoaXMucmVzZXRGcmFtZXNGb3JBbHRTY3JlZW4oKVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLnJlcGFpbnQoKVxuICAgIH1cbiAgfVxuXG4gIGdldCBpc0FsdFNjcmVlbkFjdGl2ZSgpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5hbHRTY3JlZW5BY3RpdmVcbiAgfVxuXG4gIC8qKlxuICAgKiBSZS1hc3NlcnQgdGVybWluYWwgbW9kZXMgYWZ0ZXIgYSBnYXAgKD41cyBzdGRpbiBzaWxlbmNlIG9yIGV2ZW50LWxvb3BcbiAgICogc3RhbGwpLiBDYXRjaGVzIHRtdXggZGV0YWNo4oaSYXR0YWNoLCBzc2ggcmVjb25uZWN0LCBhbmQgbGFwdG9wXG4gICAqIHNsZWVwL3dha2Ug4oCUIG5vbmUgb2Ygd2hpY2ggc2VuZCBTSUdDT05ULiBUaGUgdGVybWluYWwgbWF5IHJlc2V0IERFQ1xuICAgKiBwcml2YXRlIG1vZGVzIG9uIHJlY29ubmVjdDsgdGhpcyBtZXRob2QgcmVzdG9yZXMgdGhlbS5cbiAgICpcbiAgICogQWx3YXlzIHJlLWFzc2VydHMgZXh0ZW5kZWQga2V5IHJlcG9ydGluZyBhbmQgbW91c2UgdHJhY2tpbmcuIE1vdXNlXG4gICAqIHRyYWNraW5nIGlzIGlkZW1wb3RlbnQgKERFQyBwcml2YXRlIG1vZGUgc2V0LXdoZW4tc2V0IGlzIGEgbm8tb3ApLiBUaGVcbiAgICogS2l0dHkga2V5Ym9hcmQgcHJvdG9jb2wgaXMgTk9UIOKAlCBDU0kgPjF1IGlzIGEgc3RhY2sgcHVzaCwgc28gd2UgcG9wXG4gICAqIGZpcnN0IHRvIGtlZXAgZGVwdGggYmFsYW5jZWQgKHBvcCBvbiBlbXB0eSBzdGFjayBpcyBhIG5vLW9wIHBlciBzcGVjLFxuICAgKiBzbyBhZnRlciBhIHRlcm1pbmFsIHJlc2V0IHRoaXMgc3RpbGwgcmVzdG9yZXMgZGVwdGggMOKGkjEpLiBXaXRob3V0IHRoZVxuICAgKiBwb3AsIGVhY2ggPjVzIGlkbGUgZ2FwIGFkZHMgYSBzdGFjayBlbnRyeSwgYW5kIHRoZSBzaW5nbGUgcG9wIG9uIGV4aXRcbiAgICogb3Igc3VzcGVuZCBjYW4ndCBkcmFpbiB0aGVtIOKAlCB0aGUgc2hlbGwgaXMgbGVmdCBpbiBDU0kgdSBtb2RlIHdoZXJlXG4gICAqIEN0cmwrQy9DdHJsK0QgbGVhayBhcyBlc2NhcGUgc2VxdWVuY2VzLiBUaGUgYWx0LXNjcmVlblxuICAgKiByZS1lbnRyeSAoRVJBU0VfU0NSRUVOICsgZnJhbWUgcmVzZXQpIGlzIE5PVCBpZGVtcG90ZW50IOKAlCBpdCBibGFua3MgdGhlXG4gICAqIHNjcmVlbiDigJQgc28gaXQncyBvcHQtaW4gdmlhIGluY2x1ZGVBbHRTY3JlZW4uIFRoZSBzdGRpbi1nYXAgY2FsbGVyIGZpcmVzXG4gICAqIG9uIG9yZGluYXJ5ID41cyBpZGxlICsga2V5cHJlc3MgYW5kIG11c3Qgbm90IGVyYXNlOyB0aGUgZXZlbnQtbG9vcCBzdGFsbFxuICAgKiBkZXRlY3RvciBmaXJlcyBvbiBnZW51aW5lIHNsZWVwL3dha2UgYW5kIG9wdHMgaW4uIHRtdXggYXR0YWNoIC8gc3NoXG4gICAqIHJlY29ubmVjdCB0eXBpY2FsbHkgc2VuZCBhIHJlc2l6ZSwgd2hpY2ggYWxyZWFkeSBjb3ZlcnMgYWx0LXNjcmVlbiB2aWFcbiAgICogaGFuZGxlUmVzaXplLlxuICAgKi9cbiAgcmVhc3NlcnRUZXJtaW5hbE1vZGVzID0gKGluY2x1ZGVBbHRTY3JlZW4gPSBmYWxzZSk6IHZvaWQgPT4ge1xuICAgIGlmICghdGhpcy5vcHRpb25zLnN0ZG91dC5pc1RUWSkgcmV0dXJuXG4gICAgLy8gRG9uJ3QgdG91Y2ggdGhlIHRlcm1pbmFsIGR1cmluZyBhbiBlZGl0b3IgaGFuZG9mZiDigJQgcmUtZW5hYmxpbmcga2l0dHlcbiAgICAvLyBrZXlib2FyZCBoZXJlIHdvdWxkIHVuZG8gZW50ZXJBbHRlcm5hdGVTY3JlZW4ncyBkaXNhYmxlIGFuZCBuYW5vIHdvdWxkXG4gICAgLy8gc3RhcnQgc2VlaW5nIENTSS11IHNlcXVlbmNlcyBhZ2Fpbi5cbiAgICBpZiAodGhpcy5pc1BhdXNlZCkgcmV0dXJuXG4gICAgLy8gRXh0ZW5kZWQga2V5cyDigJQgcmUtYXNzZXJ0IGlmIGVuYWJsZWQgKEFwcC50c3ggZW5hYmxlcyB0aGVzZSBvblxuICAgIC8vIGFsbG93bGlzdGVkIHRlcm1pbmFscyBhdCByYXctbW9kZSBlbnRyeTsgYSB0ZXJtaW5hbCByZXNldCBjbGVhcnMgdGhlbSkuXG4gICAgLy8gUG9wLWJlZm9yZS1wdXNoIGtlZXBzIEtpdHR5IHN0YWNrIGRlcHRoIGF0IDEgaW5zdGVhZCBvZiBhY2N1bXVsYXRpbmdcbiAgICAvLyBvbiBlYWNoIGNhbGwuXG4gICAgaWYgKHN1cHBvcnRzRXh0ZW5kZWRLZXlzKCkpIHtcbiAgICAgIHRoaXMub3B0aW9ucy5zdGRvdXQud3JpdGUoXG4gICAgICAgIERJU0FCTEVfS0lUVFlfS0VZQk9BUkQgK1xuICAgICAgICAgIEVOQUJMRV9LSVRUWV9LRVlCT0FSRCArXG4gICAgICAgICAgRU5BQkxFX01PRElGWV9PVEhFUl9LRVlTLFxuICAgICAgKVxuICAgIH1cbiAgICBpZiAoIXRoaXMuYWx0U2NyZWVuQWN0aXZlKSByZXR1cm5cbiAgICAvLyBNb3VzZSB0cmFja2luZyDigJQgaWRlbXBvdGVudCwgc2FmZSB0byByZS1hc3NlcnQgb24gZXZlcnkgc3RkaW4gZ2FwLlxuICAgIGlmICh0aGlzLmFsdFNjcmVlbk1vdXNlVHJhY2tpbmcpIHtcbiAgICAgIHRoaXMub3B0aW9ucy5zdGRvdXQud3JpdGUoRU5BQkxFX01PVVNFX1RSQUNLSU5HKVxuICAgIH1cbiAgICAvLyBBbHQtc2NyZWVuIHJlLWVudHJ5IOKAlCBkZXN0cnVjdGl2ZSAoRVJBU0VfU0NSRUVOKS4gT25seSBmb3IgY2FsbGVycyB0aGF0XG4gICAgLy8gaGF2ZSBhIHN0cm9uZyBzaWduYWwgdGhlIHRlcm1pbmFsIGFjdHVhbGx5IGRyb3BwZWQgbW9kZSAxMDQ5LlxuICAgIGlmIChpbmNsdWRlQWx0U2NyZWVuKSB7XG4gICAgICB0aGlzLnJlZW50ZXJBbHRTY3JlZW4oKVxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNYXJrIHRoaXMgaW5zdGFuY2UgYXMgdW5tb3VudGVkIHNvIGZ1dHVyZSB1bm1vdW50KCkgY2FsbHMgZWFybHktcmV0dXJuLlxuICAgKiBDYWxsZWQgYnkgZ3JhY2VmdWxTaHV0ZG93bidzIGNsZWFudXBUZXJtaW5hbE1vZGVzKCkgYWZ0ZXIgaXQgaGFzIHNlbnRcbiAgICogRVhJVF9BTFRfU0NSRUVOIGJ1dCBiZWZvcmUgdGhlIHJlbWFpbmluZyB0ZXJtaW5hbC1yZXNldCBzZXF1ZW5jZXMuXG4gICAqIFdpdGhvdXQgdGhpcywgc2lnbmFsLWV4aXQncyBkZWZlcnJlZCBpbmsudW5tb3VudCgpICh0cmlnZ2VyZWQgYnlcbiAgICogcHJvY2Vzcy5leGl0KCkpIHJ1bnMgdGhlIGZ1bGwgdW5tb3VudCBwYXRoOiBvblJlbmRlcigpICsgd3JpdGVTeW5jXG4gICAqIGNsZWFudXAgYmxvY2sgKyB1cGRhdGVDb250YWluZXJTeW5jIOKGkiBBbHRlcm5hdGVTY3JlZW4gdW5tb3VudCBjbGVhbnVwLlxuICAgKiBUaGUgcmVzdWx0IGlzIDItMyByZWR1bmRhbnQgRVhJVF9BTFRfU0NSRUVOIHNlcXVlbmNlcyBsYW5kaW5nIG9uIHRoZVxuICAgKiBtYWluIHNjcmVlbiBBRlRFUiBwcmludFJlc3VtZUhpbnQoKSwgd2hpY2ggdG11eCAoYXQgbGVhc3QpIGludGVycHJldHNcbiAgICogYXMgcmVzdG9yaW5nIHRoZSBzYXZlZCBjdXJzb3IgcG9zaXRpb24g4oCUIGNsb2JiZXJpbmcgdGhlIHJlc3VtZSBoaW50LlxuICAgKi9cbiAgZGV0YWNoRm9yU2h1dGRvd24oKTogdm9pZCB7XG4gICAgdGhpcy5pc1VubW91bnRlZCA9IHRydWVcbiAgICAvLyBDYW5jZWwgYW55IHBlbmRpbmcgdGhyb3R0bGVkIHJlbmRlciBzbyBpdCBkb2Vzbid0IGZpcmUgYmV0d2VlblxuICAgIC8vIGNsZWFudXBUZXJtaW5hbE1vZGVzKCkgYW5kIHByb2Nlc3MuZXhpdCgpIGFuZCB3cml0ZSB0byBtYWluIHNjcmVlbi5cbiAgICB0aGlzLnNjaGVkdWxlUmVuZGVyLmNhbmNlbD8uKClcbiAgICAvLyBSZXN0b3JlIHN0ZGluIGZyb20gcmF3IG1vZGUuIHVubW91bnQoKSB1c2VkIHRvIGRvIHRoaXMgdmlhIFJlYWN0XG4gICAgLy8gdW5tb3VudCAoQXBwLmNvbXBvbmVudFdpbGxVbm1vdW50IOKGkiBoYW5kbGVTZXRSYXdNb2RlKGZhbHNlKSkgYnV0IHdlJ3JlXG4gICAgLy8gc2hvcnQtY2lyY3VpdGluZyB0aGF0IHBhdGguIE11c3QgdXNlIHRoaXMub3B0aW9ucy5zdGRpbiDigJQgTk9UXG4gICAgLy8gcHJvY2Vzcy5zdGRpbiDigJQgYmVjYXVzZSBnZXRTdGRpbk92ZXJyaWRlKCkgbWF5IGhhdmUgb3BlbmVkIC9kZXYvdHR5XG4gICAgLy8gd2hlbiBzdGRpbiBpcyBwaXBlZC5cbiAgICBjb25zdCBzdGRpbiA9IHRoaXMub3B0aW9ucy5zdGRpbiBhcyBOb2RlSlMuUmVhZFN0cmVhbSAmIHtcbiAgICAgIGlzUmF3PzogYm9vbGVhblxuICAgICAgc2V0UmF3TW9kZT86IChtOiBib29sZWFuKSA9PiB2b2lkXG4gICAgfVxuICAgIHRoaXMuZHJhaW5TdGRpbigpXG4gICAgaWYgKHN0ZGluLmlzVFRZICYmIHN0ZGluLmlzUmF3ICYmIHN0ZGluLnNldFJhd01vZGUpIHtcbiAgICAgIHN0ZGluLnNldFJhd01vZGUoZmFsc2UpXG4gICAgfVxuICB9XG5cbiAgLyoqIEBzZWUgZHJhaW5TdGRpbiAqL1xuICBkcmFpblN0ZGluKCk6IHZvaWQge1xuICAgIGRyYWluU3RkaW4odGhpcy5vcHRpb25zLnN0ZGluKVxuICB9XG5cbiAgLyoqXG4gICAqIFJlLWVudGVyIGFsdC1zY3JlZW4sIGNsZWFyLCBob21lLCByZS1lbmFibGUgbW91c2UgdHJhY2tpbmcsIGFuZCByZXNldFxuICAgKiBmcmFtZSBidWZmZXJzIHNvIHRoZSBuZXh0IHJlbmRlciByZXBhaW50cyBmcm9tIHNjcmF0Y2guIFNlbGYtaGVhbCBmb3JcbiAgICogU0lHQ09OVCwgcmVzaXplLCBhbmQgc3RkaW4tZ2FwL2V2ZW50LWxvb3Atc3RhbGwgKHNsZWVwL3dha2UpIOKAlCBhbnkgb2ZcbiAgICogd2hpY2ggY2FuIGxlYXZlIHRoZSB0ZXJtaW5hbCBpbiBtYWluLXNjcmVlbiBtb2RlIHdoaWxlIGFsdFNjcmVlbkFjdGl2ZVxuICAgKiBzdGF5cyB0cnVlLiBFTlRFUl9BTFRfU0NSRUVOIGlzIGEgdGVybWluYWwtc2lkZSBuby1vcCBpZiBhbHJlYWR5IGluIGFsdC5cbiAgICovXG4gIHByaXZhdGUgcmVlbnRlckFsdFNjcmVlbigpOiB2b2lkIHtcbiAgICB0aGlzLm9wdGlvbnMuc3Rkb3V0LndyaXRlKFxuICAgICAgRU5URVJfQUxUX1NDUkVFTiArXG4gICAgICAgIEVSQVNFX1NDUkVFTiArXG4gICAgICAgIENVUlNPUl9IT01FICtcbiAgICAgICAgKHRoaXMuYWx0U2NyZWVuTW91c2VUcmFja2luZyA/IEVOQUJMRV9NT1VTRV9UUkFDS0lORyA6ICcnKSxcbiAgICApXG4gICAgdGhpcy5yZXNldEZyYW1lc0ZvckFsdFNjcmVlbigpXG4gIH1cblxuICAvKipcbiAgICogU2VlZCBwcmV2L2JhY2sgZnJhbWVzIHdpdGggZnVsbC1zaXplIEJMQU5LIHNjcmVlbnMgKHJvd3PDl2NvbHMgb2YgZW1wdHlcbiAgICogY2VsbHMsIG5vdCAww5cwKS4gSW4gYWx0LXNjcmVlbiBtb2RlLCBuZXh0LnNjcmVlbi5oZWlnaHQgaXMgYWx3YXlzXG4gICAqIHRlcm1pbmFsUm93czsgaWYgcHJldi5zY3JlZW4uaGVpZ2h0IGlzIDAgKGVtcHR5RnJhbWUncyBkZWZhdWx0KSxcbiAgICogbG9nLXVwZGF0ZSBzZWVzIGhlaWdodERlbHRhID4gMCAoJ2dyb3dpbmcnKSBhbmQgY2FsbHMgcmVuZGVyRnJhbWVTbGljZSxcbiAgICogd2hvc2UgdHJhaWxpbmcgcGVyLXJvdyBDUitMRiBhdCB0aGUgbGFzdCByb3cgc2Nyb2xscyB0aGUgYWx0IHNjcmVlbixcbiAgICogcGVybWFuZW50bHkgZGVzeW5jaW5nIHRoZSB2aXJ0dWFsIGFuZCBwaHlzaWNhbCBjdXJzb3JzIGJ5IDEgcm93LlxuICAgKlxuICAgKiBXaXRoIGEgcm93c8OXY29scyBibGFuayBwcmV2LCBoZWlnaHREZWx0YSA9PT0gMCDihpIgc3RhbmRhcmQgZGlmZkVhY2hcbiAgICog4oaSIG1vdmVDdXJzb3JUbyAoQ1NJIGN1cnNvck1vdmUsIG5vIExGLCBubyBzY3JvbGwpLlxuICAgKlxuICAgKiB2aWV3cG9ydC5oZWlnaHQgPSByb3dzICsgMSBtYXRjaGVzIHRoZSByZW5kZXJlcidzIGFsdC1zY3JlZW4gb3V0cHV0LFxuICAgKiBwcmV2ZW50aW5nIGEgc3B1cmlvdXMgcmVzaXplIHRyaWdnZXIgb24gdGhlIGZpcnN0IGZyYW1lLiBjdXJzb3IueSA9IDBcbiAgICogbWF0Y2hlcyB0aGUgcGh5c2ljYWwgY3Vyc29yIGFmdGVyIEVOVEVSX0FMVF9TQ1JFRU4gKyBDU0kgSCAoaG9tZSkuXG4gICAqL1xuICBwcml2YXRlIHJlc2V0RnJhbWVzRm9yQWx0U2NyZWVuKCk6IHZvaWQge1xuICAgIGNvbnN0IHJvd3MgPSB0aGlzLnRlcm1pbmFsUm93c1xuICAgIGNvbnN0IGNvbHMgPSB0aGlzLnRlcm1pbmFsQ29sdW1uc1xuICAgIGNvbnN0IGJsYW5rID0gKCk6IEZyYW1lID0+ICh7XG4gICAgICBzY3JlZW46IGNyZWF0ZVNjcmVlbihcbiAgICAgICAgY29scyxcbiAgICAgICAgcm93cyxcbiAgICAgICAgdGhpcy5zdHlsZVBvb2wsXG4gICAgICAgIHRoaXMuY2hhclBvb2wsXG4gICAgICAgIHRoaXMuaHlwZXJsaW5rUG9vbCxcbiAgICAgICksXG4gICAgICB2aWV3cG9ydDogeyB3aWR0aDogY29scywgaGVpZ2h0OiByb3dzICsgMSB9LFxuICAgICAgY3Vyc29yOiB7IHg6IDAsIHk6IDAsIHZpc2libGU6IHRydWUgfSxcbiAgICB9KVxuICAgIHRoaXMuZnJvbnRGcmFtZSA9IGJsYW5rKClcbiAgICB0aGlzLmJhY2tGcmFtZSA9IGJsYW5rKClcbiAgICB0aGlzLmxvZy5yZXNldCgpXG4gICAgLy8gRGVmZW5zZS1pbi1kZXB0aDogYWx0LXNjcmVlbiBza2lwcyB0aGUgY3Vyc29yIHByZWFtYmxlIGFueXdheSAoQ1NJIEhcbiAgICAvLyByZXNldHMpLCBidXQgYSBzdGFsZSBkaXNwbGF5Q3Vyc29yIHdvdWxkIGJlIG1pc2xlYWRpbmcgaWYgd2UgbGF0ZXJcbiAgICAvLyBleGl0IHRvIG1haW4tc2NyZWVuIHdpdGhvdXQgYW4gaW50ZXJ2ZW5pbmcgcmVuZGVyLlxuICAgIHRoaXMuZGlzcGxheUN1cnNvciA9IG51bGxcbiAgICAvLyBGcmVzaCBmcm9udEZyYW1lIGlzIGJsYW5rIHJvd3PDl2NvbHMg4oCUIGJsaXR0aW5nIGZyb20gaXQgd291bGQgY29weVxuICAgIC8vIGJsYW5rcyBvdmVyIGNvbnRlbnQuIE5leHQgYWx0LXNjcmVlbiBmcmFtZSBtdXN0IGZ1bGwtcmVuZGVyLlxuICAgIHRoaXMucHJldkZyYW1lQ29udGFtaW5hdGVkID0gdHJ1ZVxuICB9XG5cbiAgLyoqXG4gICAqIENvcHkgdGhlIGN1cnJlbnQgc2VsZWN0aW9uIHRvIHRoZSBjbGlwYm9hcmQgd2l0aG91dCBjbGVhcmluZyB0aGVcbiAgICogaGlnaGxpZ2h0LiBNYXRjaGVzIGlUZXJtMidzIGNvcHktb24tc2VsZWN0IGJlaGF2aW9yIHdoZXJlIHRoZSBzZWxlY3RlZFxuICAgKiByZWdpb24gc3RheXMgdmlzaWJsZSBhZnRlciB0aGUgYXV0b21hdGljIGNvcHkuXG4gICAqL1xuICBjb3B5U2VsZWN0aW9uTm9DbGVhcigpOiBzdHJpbmcge1xuICAgIGlmICghaGFzU2VsZWN0aW9uKHRoaXMuc2VsZWN0aW9uKSkgcmV0dXJuICcnXG4gICAgY29uc3QgdGV4dCA9IGdldFNlbGVjdGVkVGV4dCh0aGlzLnNlbGVjdGlvbiwgdGhpcy5mcm9udEZyYW1lLnNjcmVlbilcbiAgICBpZiAodGV4dCkge1xuICAgICAgLy8gUmF3IE9TQyA1Miwgb3IgRENTLXBhc3N0aHJvdWdoLXdyYXBwZWQgT1NDIDUyIGluc2lkZSB0bXV4ICh0bXV4XG4gICAgICAvLyBkcm9wcyBpdCBzaWxlbnRseSB1bmxlc3MgYWxsb3ctcGFzc3Rocm91Z2ggaXMgb24g4oCUIG5vIHJlZ3Jlc3Npb24pLlxuICAgICAgdm9pZCBzZXRDbGlwYm9hcmQodGV4dCkudGhlbihyYXcgPT4ge1xuICAgICAgICBpZiAocmF3KSB0aGlzLm9wdGlvbnMuc3Rkb3V0LndyaXRlKHJhdylcbiAgICAgIH0pXG4gICAgfVxuICAgIHJldHVybiB0ZXh0XG4gIH1cblxuICAvKipcbiAgICogQ29weSB0aGUgY3VycmVudCB0ZXh0IHNlbGVjdGlvbiB0byB0aGUgc3lzdGVtIGNsaXBib2FyZCB2aWEgT1NDIDUyXG4gICAqIGFuZCBjbGVhciB0aGUgc2VsZWN0aW9uLiBSZXR1cm5zIHRoZSBjb3BpZWQgdGV4dCAoZW1wdHkgaWYgbm8gc2VsZWN0aW9uKS5cbiAgICovXG4gIGNvcHlTZWxlY3Rpb24oKTogc3RyaW5nIHtcbiAgICBpZiAoIWhhc1NlbGVjdGlvbih0aGlzLnNlbGVjdGlvbikpIHJldHVybiAnJ1xuICAgIGNvbnN0IHRleHQgPSB0aGlzLmNvcHlTZWxlY3Rpb25Ob0NsZWFyKClcbiAgICBjbGVhclNlbGVjdGlvbih0aGlzLnNlbGVjdGlvbilcbiAgICB0aGlzLm5vdGlmeVNlbGVjdGlvbkNoYW5nZSgpXG4gICAgcmV0dXJuIHRleHRcbiAgfVxuXG4gIC8qKiBDbGVhciB0aGUgY3VycmVudCB0ZXh0IHNlbGVjdGlvbiB3aXRob3V0IGNvcHlpbmcuICovXG4gIGNsZWFyVGV4dFNlbGVjdGlvbigpOiB2b2lkIHtcbiAgICBpZiAoIWhhc1NlbGVjdGlvbih0aGlzLnNlbGVjdGlvbikpIHJldHVyblxuICAgIGNsZWFyU2VsZWN0aW9uKHRoaXMuc2VsZWN0aW9uKVxuICAgIHRoaXMubm90aWZ5U2VsZWN0aW9uQ2hhbmdlKClcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXQgdGhlIHNlYXJjaCBoaWdobGlnaHQgcXVlcnkuIE5vbi1lbXB0eSDihpIgYWxsIHZpc2libGUgb2NjdXJyZW5jZXNcbiAgICogYXJlIGludmVydGVkIChTR1IgNykgb24gdGhlIG5leHQgZnJhbWU7IGZpcnN0IG9uZSBhbHNvIHVuZGVybGluZWQuXG4gICAqIEVtcHR5IOKGkiBjbGVhcnMgKHByZXZGcmFtZUNvbnRhbWluYXRlZCBoYW5kbGVzIHRoZSBmcmFtZSBhZnRlcikuIFNhbWVcbiAgICogZGFtYWdlLXRyYWNraW5nIG1hY2hpbmVyeSBhcyBzZWxlY3Rpb24g4oCUIHNldENlbGxTdHlsZUlkIGRvZXNuJ3QgdHJhY2tcbiAgICogZGFtYWdlLCBzbyB0aGUgb3ZlcmxheSBmb3JjZXMgZnVsbC1mcmFtZSBkYW1hZ2Ugd2hpbGUgYWN0aXZlLlxuICAgKi9cbiAgc2V0U2VhcmNoSGlnaGxpZ2h0KHF1ZXJ5OiBzdHJpbmcpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5zZWFyY2hIaWdobGlnaHRRdWVyeSA9PT0gcXVlcnkpIHJldHVyblxuICAgIHRoaXMuc2VhcmNoSGlnaGxpZ2h0UXVlcnkgPSBxdWVyeVxuICAgIHRoaXMuc2NoZWR1bGVSZW5kZXIoKVxuICB9XG5cbiAgLyoqIFBhaW50IGFuIEVYSVNUSU5HIERPTSBzdWJ0cmVlIHRvIGEgZnJlc2ggU2NyZWVuIGF0IGl0cyBuYXR1cmFsXG4gICAqICBoZWlnaHQsIHNjYW4gZm9yIHF1ZXJ5LiBSZXR1cm5zIHBvc2l0aW9ucyByZWxhdGl2ZSB0byB0aGUgZWxlbWVudCdzXG4gICAqICBib3VuZGluZyBib3ggKHJvdyAwID0gZWxlbWVudCB0b3ApLlxuICAgKlxuICAgKiAgVGhlIGVsZW1lbnQgY29tZXMgZnJvbSB0aGUgTUFJTiB0cmVlIOKAlCBidWlsdCB3aXRoIGFsbCByZWFsXG4gICAqICBwcm92aWRlcnMsIHlvZ2EgYWxyZWFkeSBjb21wdXRlZC4gV2UgcGFpbnQgaXQgdG8gYSBmcmVzaCBidWZmZXJcbiAgICogIHdpdGggb2Zmc2V0cyBzbyBpdCBsYW5kcyBhdCAoMCwwKS4gU2FtZSBwYWludCBwYXRoIGFzIHRoZSBtYWluXG4gICAqICByZW5kZXIuIFplcm8gZHJpZnQuIE5vIHNlY29uZCBSZWFjdCByb290LCBubyBjb250ZXh0IGJyaWRnZS5cbiAgICpcbiAgICogIH4xLTJtcyAocGFpbnQgb25seSwgbm8gcmVjb25jaWxlIOKAlCB0aGUgRE9NIGlzIGFscmVhZHkgYnVpbHQpLiAqL1xuICBzY2FuRWxlbWVudFN1YnRyZWUoZWw6IGRvbS5ET01FbGVtZW50KTogTWF0Y2hQb3NpdGlvbltdIHtcbiAgICBpZiAoIXRoaXMuc2VhcmNoSGlnaGxpZ2h0UXVlcnkgfHwgIWVsLnlvZ2FOb2RlKSByZXR1cm4gW11cbiAgICBjb25zdCB3aWR0aCA9IE1hdGguY2VpbChlbC55b2dhTm9kZS5nZXRDb21wdXRlZFdpZHRoKCkpXG4gICAgY29uc3QgaGVpZ2h0ID0gTWF0aC5jZWlsKGVsLnlvZ2FOb2RlLmdldENvbXB1dGVkSGVpZ2h0KCkpXG4gICAgaWYgKHdpZHRoIDw9IDAgfHwgaGVpZ2h0IDw9IDApIHJldHVybiBbXVxuICAgIC8vIHJlbmRlck5vZGVUb091dHB1dCBhZGRzIGVsJ3MgT1dOIGNvbXB1dGVkTGVmdC9Ub3AgdG8gb2Zmc2V0WC9ZLlxuICAgIC8vIFBhc3NpbmcgLWVsTGVmdC8tZWxUb3AgbmV0cyB0byAwIOKGkiBwYWludHMgYXQgKDAsMCkgaW4gb3VyIGJ1ZmZlci5cbiAgICBjb25zdCBlbExlZnQgPSBlbC55b2dhTm9kZS5nZXRDb21wdXRlZExlZnQoKVxuICAgIGNvbnN0IGVsVG9wID0gZWwueW9nYU5vZGUuZ2V0Q29tcHV0ZWRUb3AoKVxuICAgIGNvbnN0IHNjcmVlbiA9IGNyZWF0ZVNjcmVlbihcbiAgICAgIHdpZHRoLFxuICAgICAgaGVpZ2h0LFxuICAgICAgdGhpcy5zdHlsZVBvb2wsXG4gICAgICB0aGlzLmNoYXJQb29sLFxuICAgICAgdGhpcy5oeXBlcmxpbmtQb29sLFxuICAgIClcbiAgICBjb25zdCBvdXRwdXQgPSBuZXcgT3V0cHV0KHtcbiAgICAgIHdpZHRoLFxuICAgICAgaGVpZ2h0LFxuICAgICAgc3R5bGVQb29sOiB0aGlzLnN0eWxlUG9vbCxcbiAgICAgIHNjcmVlbixcbiAgICB9KVxuICAgIHJlbmRlck5vZGVUb091dHB1dChlbCwgb3V0cHV0LCB7XG4gICAgICBvZmZzZXRYOiAtZWxMZWZ0LFxuICAgICAgb2Zmc2V0WTogLWVsVG9wLFxuICAgICAgcHJldlNjcmVlbjogdW5kZWZpbmVkLFxuICAgIH0pXG4gICAgY29uc3QgcmVuZGVyZWQgPSBvdXRwdXQuZ2V0KClcbiAgICAvLyByZW5kZXJOb2RlVG9PdXRwdXQgd3JvdGUgb3VyIG9mZnNldCBwb3NpdGlvbnMgdG8gbm9kZUNhY2hlIOKAlFxuICAgIC8vIGNvcnJ1cHRzIHRoZSBtYWluIHJlbmRlciAoaXQnZCBibGl0IGZyb20gd3JvbmcgY29vcmRzKS4gTWFyayB0aGVcbiAgICAvLyBzdWJ0cmVlIGRpcnR5IHNvIHRoZSBuZXh0IG1haW4gcmVuZGVyIHJlcGFpbnRzICsgcmUtY2FjaGVzXG4gICAgLy8gY29ycmVjdGx5LiBPbmUgZXh0cmEgcGFpbnQgb2YgdGhpcyBtZXNzYWdlLCBidXQgY29ycmVjdCA+IGZhc3QuXG4gICAgZG9tLm1hcmtEaXJ0eShlbClcbiAgICBjb25zdCBwb3NpdGlvbnMgPSBzY2FuUG9zaXRpb25zKHJlbmRlcmVkLCB0aGlzLnNlYXJjaEhpZ2hsaWdodFF1ZXJ5KVxuICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgIGBzY2FuRWxlbWVudFN1YnRyZWU6IHE9JyR7dGhpcy5zZWFyY2hIaWdobGlnaHRRdWVyeX0nIGAgK1xuICAgICAgICBgZWw9JHt3aWR0aH14JHtoZWlnaHR9QCgke2VsTGVmdH0sJHtlbFRvcH0pIG49JHtwb3NpdGlvbnMubGVuZ3RofSBgICtcbiAgICAgICAgYFske3Bvc2l0aW9uc1xuICAgICAgICAgIC5zbGljZSgwLCAxMClcbiAgICAgICAgICAubWFwKHAgPT4gYCR7cC5yb3d9OiR7cC5jb2x9YClcbiAgICAgICAgICAuam9pbignLCcpfWAgK1xuICAgICAgICBgJHtwb3NpdGlvbnMubGVuZ3RoID4gMTAgPyAnLOKApicgOiAnJ31dYCxcbiAgICApXG4gICAgcmV0dXJuIHBvc2l0aW9uc1xuICB9XG5cbiAgLyoqIFNldCB0aGUgcG9zaXRpb24tYmFzZWQgaGlnaGxpZ2h0IHN0YXRlLiBFdmVyeSBmcmFtZSwgd3JpdGVzIENVUlJFTlRcbiAgICogIHN0eWxlIGF0IHBvc2l0aW9uc1tjdXJyZW50SWR4XSArIHJvd09mZnNldC4gbnVsbCBjbGVhcnMuIFRoZSBzY2FuLVxuICAgKiAgaGlnaGxpZ2h0IChpbnZlcnNlIG9uIGFsbCBtYXRjaGVzKSBzdGlsbCBydW5zIOKAlCB0aGlzIG92ZXJsYXlzIHllbGxvd1xuICAgKiAgb24gdG9wLiByb3dPZmZzZXQgY2hhbmdlcyBhcyB0aGUgdXNlciBzY3JvbGxzICg9IG1lc3NhZ2UncyBjdXJyZW50XG4gICAqICBzY3JlZW4tdG9wKTsgcG9zaXRpb25zIHN0YXkgc3RhYmxlIChtZXNzYWdlLXJlbGF0aXZlKS4gKi9cbiAgc2V0U2VhcmNoUG9zaXRpb25zKFxuICAgIHN0YXRlOiB7XG4gICAgICBwb3NpdGlvbnM6IE1hdGNoUG9zaXRpb25bXVxuICAgICAgcm93T2Zmc2V0OiBudW1iZXJcbiAgICAgIGN1cnJlbnRJZHg6IG51bWJlclxuICAgIH0gfCBudWxsLFxuICApOiB2b2lkIHtcbiAgICB0aGlzLnNlYXJjaFBvc2l0aW9ucyA9IHN0YXRlXG4gICAgdGhpcy5zY2hlZHVsZVJlbmRlcigpXG4gIH1cblxuICAvKipcbiAgICogU2V0IHRoZSBzZWxlY3Rpb24gaGlnaGxpZ2h0IGJhY2tncm91bmQgY29sb3IuIFJlcGxhY2VzIHRoZSBwZXItY2VsbFxuICAgKiBTR1ItNyBpbnZlcnNlIHdpdGggYSBzb2xpZCB0aGVtZS1hd2FyZSBiZyAobWF0Y2hlcyBuYXRpdmUgdGVybWluYWxcbiAgICogc2VsZWN0aW9uKS4gQWNjZXB0cyB0aGUgc2FtZSBjb2xvciBmb3JtYXRzIGFzIFRleHQgYmFja2dyb3VuZENvbG9yXG4gICAqIChyZ2IoKSwgYW5zaTpuYW1lLCAjaGV4LCBhbnNpMjU2KCkpIOKAlCBjb2xvcml6ZSgpIHJvdXRlcyB0aHJvdWdoXG4gICAqIGNoYWxrIHNvIHRoZSB0bXV4L3h0ZXJtLmpzIGxldmVsIGNsYW1wcyBpbiBjb2xvcml6ZS50cyBhcHBseSBhbmRcbiAgICogdGhlIGVtaXR0ZWQgU0dSIGlzIGNvcnJlY3QgZm9yIHRoZSBjdXJyZW50IHRlcm1pbmFsLlxuICAgKlxuICAgKiBDYWxsZWQgYnkgUmVhY3QtbGFuZCBvbmNlIHRoZW1lIGlzIGtub3duIChTY3JvbGxLZXliaW5kaW5nSGFuZGxlcidzXG4gICAqIHVzZUVmZmVjdCB3YXRjaGluZyB1c2VUaGVtZSkuIEJlZm9yZSB0aGF0IGNhbGwsIHdpdGhTZWxlY3Rpb25CZ1xuICAgKiBmYWxscyBiYWNrIHRvIHdpdGhJbnZlcnNlIHNvIHNlbGVjdGlvbiBzdGlsbCByZW5kZXJzIG9uIHRoZSBmaXJzdFxuICAgKiBmcmFtZTsgdGhlIGVmZmVjdCBmaXJlcyBiZWZvcmUgYW55IG1vdXNlIGlucHV0IHNvIHRoZSBmYWxsYmFjayBpc1xuICAgKiB1bm9ic2VydmFibGUgaW4gcHJhY3RpY2UuXG4gICAqL1xuICBzZXRTZWxlY3Rpb25CZ0NvbG9yKGNvbG9yOiBzdHJpbmcpOiB2b2lkIHtcbiAgICAvLyBXcmFwIGEgTlVMIG1hcmtlciwgdGhlbiBzcGxpdCBvbiBpdCB0byBleHRyYWN0IHRoZSBvcGVuL2Nsb3NlIFNHUi5cbiAgICAvLyBjb2xvcml6ZSByZXR1cm5zIHRoZSBpbnB1dCB1bmNoYW5nZWQgaWYgdGhlIGNvbG9yIHN0cmluZyBpcyBiYWQg4oCUXG4gICAgLy8gbm8gTlVMLXNwbGl0IHRoZW4sIHNvIGZhbGwgdGhyb3VnaCB0byBudWxsIChpbnZlcnNlIGZhbGxiYWNrKS5cbiAgICBjb25zdCB3cmFwcGVkID0gY29sb3JpemUoJ1xcMCcsIGNvbG9yLCAnYmFja2dyb3VuZCcpXG4gICAgY29uc3QgbnVsID0gd3JhcHBlZC5pbmRleE9mKCdcXDAnKVxuICAgIGlmIChudWwgPD0gMCB8fCBudWwgPT09IHdyYXBwZWQubGVuZ3RoIC0gMSkge1xuICAgICAgdGhpcy5zdHlsZVBvb2wuc2V0U2VsZWN0aW9uQmcobnVsbClcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICB0aGlzLnN0eWxlUG9vbC5zZXRTZWxlY3Rpb25CZyh7XG4gICAgICB0eXBlOiAnYW5zaScsXG4gICAgICBjb2RlOiB3cmFwcGVkLnNsaWNlKDAsIG51bCksXG4gICAgICBlbmRDb2RlOiB3cmFwcGVkLnNsaWNlKG51bCArIDEpLCAvLyBhbHdheXMgXFx4MWJbNDltIGZvciBiZ1xuICAgIH0pXG4gICAgLy8gTm8gc2NoZWR1bGVSZW5kZXI6IHRoaXMgaXMgY2FsbGVkIGZyb20gYSBSZWFjdCBlZmZlY3QgdGhhdCBhbHJlYWR5XG4gICAgLy8gcnVucyBpbnNpZGUgdGhlIHJlbmRlciBjeWNsZSwgYW5kIHRoZSBiZyBvbmx5IG1hdHRlcnMgb25jZSBhXG4gICAgLy8gc2VsZWN0aW9uIGV4aXN0cyAod2hpY2ggaXRzZWxmIHRyaWdnZXJzIGEgZnVsbC1kYW1hZ2UgZnJhbWUpLlxuICB9XG5cbiAgLyoqXG4gICAqIENhcHR1cmUgdGV4dCBmcm9tIHJvd3MgYWJvdXQgdG8gc2Nyb2xsIG91dCBvZiB0aGUgdmlld3BvcnQgZHVyaW5nXG4gICAqIGRyYWctdG8tc2Nyb2xsLiBNdXN0IGJlIGNhbGxlZCBCRUZPUkUgdGhlIFNjcm9sbEJveCBzY3JvbGxzIHNvIHRoZVxuICAgKiBzY3JlZW4gYnVmZmVyIHN0aWxsIGhvbGRzIHRoZSBvdXRnb2luZyBjb250ZW50LiBBY2N1bXVsYXRlZCBpbnRvXG4gICAqIHRoZSBzZWxlY3Rpb24gc3RhdGUgYW5kIGpvaW5lZCBiYWNrIGluIGJ5IGdldFNlbGVjdGVkVGV4dC5cbiAgICovXG4gIGNhcHR1cmVTY3JvbGxlZFJvd3MoXG4gICAgZmlyc3RSb3c6IG51bWJlcixcbiAgICBsYXN0Um93OiBudW1iZXIsXG4gICAgc2lkZTogJ2Fib3ZlJyB8ICdiZWxvdycsXG4gICk6IHZvaWQge1xuICAgIGNhcHR1cmVTY3JvbGxlZFJvd3MoXG4gICAgICB0aGlzLnNlbGVjdGlvbixcbiAgICAgIHRoaXMuZnJvbnRGcmFtZS5zY3JlZW4sXG4gICAgICBmaXJzdFJvdyxcbiAgICAgIGxhc3RSb3csXG4gICAgICBzaWRlLFxuICAgIClcbiAgfVxuXG4gIC8qKlxuICAgKiBTaGlmdCBhbmNob3IgQU5EIGZvY3VzIGJ5IGRSb3csIGNsYW1wZWQgdG8gW21pblJvdywgbWF4Um93XS4gVXNlZCBieVxuICAgKiBrZXlib2FyZCBzY3JvbGwgaGFuZGxlcnMgKFBnVXAvUGdEbiBldGMuKSBzbyB0aGUgaGlnaGxpZ2h0IHRyYWNrcyB0aGVcbiAgICogY29udGVudCBpbnN0ZWFkIG9mIGRpc2FwcGVhcmluZy4gVW5saWtlIHNoaWZ0QW5jaG9yIChkcmFnLXRvLXNjcm9sbCksXG4gICAqIHRoaXMgbW92ZXMgQk9USCBlbmRwb2ludHMg4oCUIHRoZSB1c2VyIGlzbid0IGhvbGRpbmcgdGhlIG1vdXNlIGF0IG9uZVxuICAgKiBlZGdlLiBTdXBwbGllcyBzY3JlZW4ud2lkdGggZm9yIHRoZSBjb2wtcmVzZXQtb24tY2xhbXAgYm91bmRhcnkuXG4gICAqL1xuICBzaGlmdFNlbGVjdGlvbkZvclNjcm9sbChkUm93OiBudW1iZXIsIG1pblJvdzogbnVtYmVyLCBtYXhSb3c6IG51bWJlcik6IHZvaWQge1xuICAgIGNvbnN0IGhhZFNlbCA9IGhhc1NlbGVjdGlvbih0aGlzLnNlbGVjdGlvbilcbiAgICBzaGlmdFNlbGVjdGlvbihcbiAgICAgIHRoaXMuc2VsZWN0aW9uLFxuICAgICAgZFJvdyxcbiAgICAgIG1pblJvdyxcbiAgICAgIG1heFJvdyxcbiAgICAgIHRoaXMuZnJvbnRGcmFtZS5zY3JlZW4ud2lkdGgsXG4gICAgKVxuICAgIC8vIHNoaWZ0U2VsZWN0aW9uIGNsZWFycyB3aGVuIGJvdGggZW5kcG9pbnRzIG92ZXJzaG9vdCB0aGUgc2FtZSBlZGdlXG4gICAgLy8gKEhvbWUvZy9FbmQvRyBwYWdlLWp1bXAgcGFzdCB0aGUgc2VsZWN0aW9uKS4gTm90aWZ5IHN1YnNjcmliZXJzIHNvXG4gICAgLy8gdXNlSGFzU2VsZWN0aW9uIHVwZGF0ZXMuIFNhZmUgdG8gY2FsbCBub3RpZnlTZWxlY3Rpb25DaGFuZ2UgaGVyZSDigJRcbiAgICAvLyB0aGlzIHJ1bnMgZnJvbSBrZXlib2FyZCBoYW5kbGVycywgbm90IGluc2lkZSBvblJlbmRlcigpLlxuICAgIGlmIChoYWRTZWwgJiYgIWhhc1NlbGVjdGlvbih0aGlzLnNlbGVjdGlvbikpIHtcbiAgICAgIHRoaXMubm90aWZ5U2VsZWN0aW9uQ2hhbmdlKClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogS2V5Ym9hcmQgc2VsZWN0aW9uIGV4dGVuc2lvbiAoc2hpZnQrYXJyb3cvaG9tZS9lbmQpLiBNb3ZlcyBmb2N1cztcbiAgICogYW5jaG9yIHN0YXlzIGZpeGVkIHNvIHRoZSBoaWdobGlnaHQgZ3Jvd3Mgb3Igc2hyaW5rcyByZWxhdGl2ZSB0byBpdC5cbiAgICogTGVmdC9yaWdodCB3cmFwIGFjcm9zcyByb3cgYm91bmRhcmllcyDigJQgbmF0aXZlIG1hY09TIHRleHQtZWRpdFxuICAgKiBiZWhhdmlvcjogc2hpZnQrbGVmdCBhdCBjb2wgMCB3cmFwcyB0byBlbmQgb2YgdGhlIHByZXZpb3VzIHJvdy5cbiAgICogVXAvZG93biBjbGFtcCBhdCB2aWV3cG9ydCBlZGdlcyAobm8gc2Nyb2xsLXRvLWV4dGVuZCB5ZXQpLiBEcm9wcyB0b1xuICAgKiBjaGFyIG1vZGUuIE5vLW9wIG91dHNpZGUgYWx0LXNjcmVlbiBvciB3aXRob3V0IGFuIGFjdGl2ZSBzZWxlY3Rpb24uXG4gICAqL1xuICBtb3ZlU2VsZWN0aW9uRm9jdXMobW92ZTogRm9jdXNNb3ZlKTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmFsdFNjcmVlbkFjdGl2ZSkgcmV0dXJuXG4gICAgY29uc3QgeyBmb2N1cyB9ID0gdGhpcy5zZWxlY3Rpb25cbiAgICBpZiAoIWZvY3VzKSByZXR1cm5cbiAgICBjb25zdCB7IHdpZHRoLCBoZWlnaHQgfSA9IHRoaXMuZnJvbnRGcmFtZS5zY3JlZW5cbiAgICBjb25zdCBtYXhDb2wgPSB3aWR0aCAtIDFcbiAgICBjb25zdCBtYXhSb3cgPSBoZWlnaHQgLSAxXG4gICAgbGV0IHsgY29sLCByb3cgfSA9IGZvY3VzXG4gICAgc3dpdGNoIChtb3ZlKSB7XG4gICAgICBjYXNlICdsZWZ0JzpcbiAgICAgICAgaWYgKGNvbCA+IDApIGNvbC0tXG4gICAgICAgIGVsc2UgaWYgKHJvdyA+IDApIHtcbiAgICAgICAgICBjb2wgPSBtYXhDb2xcbiAgICAgICAgICByb3ctLVxuICAgICAgICB9XG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdyaWdodCc6XG4gICAgICAgIGlmIChjb2wgPCBtYXhDb2wpIGNvbCsrXG4gICAgICAgIGVsc2UgaWYgKHJvdyA8IG1heFJvdykge1xuICAgICAgICAgIGNvbCA9IDBcbiAgICAgICAgICByb3crK1xuICAgICAgICB9XG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICd1cCc6XG4gICAgICAgIGlmIChyb3cgPiAwKSByb3ctLVxuICAgICAgICBicmVha1xuICAgICAgY2FzZSAnZG93bic6XG4gICAgICAgIGlmIChyb3cgPCBtYXhSb3cpIHJvdysrXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdsaW5lU3RhcnQnOlxuICAgICAgICBjb2wgPSAwXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdsaW5lRW5kJzpcbiAgICAgICAgY29sID0gbWF4Q29sXG4gICAgICAgIGJyZWFrXG4gICAgfVxuICAgIGlmIChjb2wgPT09IGZvY3VzLmNvbCAmJiByb3cgPT09IGZvY3VzLnJvdykgcmV0dXJuXG4gICAgbW92ZUZvY3VzKHRoaXMuc2VsZWN0aW9uLCBjb2wsIHJvdylcbiAgICB0aGlzLm5vdGlmeVNlbGVjdGlvbkNoYW5nZSgpXG4gIH1cblxuICAvKiogV2hldGhlciB0aGVyZSBpcyBhbiBhY3RpdmUgdGV4dCBzZWxlY3Rpb24uICovXG4gIGhhc1RleHRTZWxlY3Rpb24oKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGhhc1NlbGVjdGlvbih0aGlzLnNlbGVjdGlvbilcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJzY3JpYmUgdG8gc2VsZWN0aW9uIHN0YXRlIGNoYW5nZXMuIEZpcmVzIHdoZW5ldmVyIHRoZSBzZWxlY3Rpb25cbiAgICogaXMgc3RhcnRlZCwgdXBkYXRlZCwgY2xlYXJlZCwgb3IgY29waWVkLiBSZXR1cm5zIGFuIHVuc3Vic2NyaWJlIGZuLlxuICAgKi9cbiAgc3Vic2NyaWJlVG9TZWxlY3Rpb25DaGFuZ2UoY2I6ICgpID0+IHZvaWQpOiAoKSA9PiB2b2lkIHtcbiAgICB0aGlzLnNlbGVjdGlvbkxpc3RlbmVycy5hZGQoY2IpXG4gICAgcmV0dXJuICgpID0+IHRoaXMuc2VsZWN0aW9uTGlzdGVuZXJzLmRlbGV0ZShjYilcbiAgfVxuXG4gIHByaXZhdGUgbm90aWZ5U2VsZWN0aW9uQ2hhbmdlKCk6IHZvaWQge1xuICAgIHRoaXMub25SZW5kZXIoKVxuICAgIGZvciAoY29uc3QgY2Igb2YgdGhpcy5zZWxlY3Rpb25MaXN0ZW5lcnMpIGNiKClcbiAgfVxuXG4gIC8qKlxuICAgKiBIaXQtdGVzdCB0aGUgcmVuZGVyZWQgRE9NIHRyZWUgYXQgKGNvbCwgcm93KSBhbmQgYnViYmxlIGEgQ2xpY2tFdmVudFxuICAgKiBmcm9tIHRoZSBkZWVwZXN0IGhpdCBub2RlIHVwIHRocm91Z2ggYW5jZXN0b3JzIHdpdGggb25DbGljayBoYW5kbGVycy5cbiAgICogUmV0dXJucyB0cnVlIGlmIGEgRE9NIGhhbmRsZXIgY29uc3VtZWQgdGhlIGNsaWNrLiBHYXRlZCBvblxuICAgKiBhbHRTY3JlZW5BY3RpdmUg4oCUIGNsaWNrcyBvbmx5IG1ha2Ugc2Vuc2Ugd2l0aCBhIGZpeGVkIHZpZXdwb3J0IHdoZXJlXG4gICAqIG5vZGVDYWNoZSByZWN0cyBtYXAgMToxIHRvIHRlcm1pbmFsIGNlbGxzIChubyBzY3JvbGxiYWNrIG9mZnNldCkuXG4gICAqL1xuICBkaXNwYXRjaENsaWNrKGNvbDogbnVtYmVyLCByb3c6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgIGlmICghdGhpcy5hbHRTY3JlZW5BY3RpdmUpIHJldHVybiBmYWxzZVxuICAgIGNvbnN0IGJsYW5rID0gaXNFbXB0eUNlbGxBdCh0aGlzLmZyb250RnJhbWUuc2NyZWVuLCBjb2wsIHJvdylcbiAgICByZXR1cm4gZGlzcGF0Y2hDbGljayh0aGlzLnJvb3ROb2RlLCBjb2wsIHJvdywgYmxhbmspXG4gIH1cblxuICBkaXNwYXRjaEhvdmVyKGNvbDogbnVtYmVyLCByb3c6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICghdGhpcy5hbHRTY3JlZW5BY3RpdmUpIHJldHVyblxuICAgIGRpc3BhdGNoSG92ZXIodGhpcy5yb290Tm9kZSwgY29sLCByb3csIHRoaXMuaG92ZXJlZE5vZGVzKVxuICB9XG5cbiAgZGlzcGF0Y2hLZXlib2FyZEV2ZW50KHBhcnNlZEtleTogUGFyc2VkS2V5KTogdm9pZCB7XG4gICAgY29uc3QgdGFyZ2V0ID0gdGhpcy5mb2N1c01hbmFnZXIuYWN0aXZlRWxlbWVudCA/PyB0aGlzLnJvb3ROb2RlXG4gICAgY29uc3QgZXZlbnQgPSBuZXcgS2V5Ym9hcmRFdmVudChwYXJzZWRLZXkpXG4gICAgZGlzcGF0Y2hlci5kaXNwYXRjaERpc2NyZXRlKHRhcmdldCwgZXZlbnQpXG5cbiAgICAvLyBUYWIgY3ljbGluZyBpcyB0aGUgZGVmYXVsdCBhY3Rpb24g4oCUIG9ubHkgZmlyZXMgaWYgbm8gaGFuZGxlclxuICAgIC8vIGNhbGxlZCBwcmV2ZW50RGVmYXVsdCgpLiBNaXJyb3JzIGJyb3dzZXIgYmVoYXZpb3IuXG4gICAgaWYgKFxuICAgICAgIWV2ZW50LmRlZmF1bHRQcmV2ZW50ZWQgJiZcbiAgICAgIHBhcnNlZEtleS5uYW1lID09PSAndGFiJyAmJlxuICAgICAgIXBhcnNlZEtleS5jdHJsICYmXG4gICAgICAhcGFyc2VkS2V5Lm1ldGFcbiAgICApIHtcbiAgICAgIGlmIChwYXJzZWRLZXkuc2hpZnQpIHtcbiAgICAgICAgdGhpcy5mb2N1c01hbmFnZXIuZm9jdXNQcmV2aW91cyh0aGlzLnJvb3ROb2RlKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5mb2N1c01hbmFnZXIuZm9jdXNOZXh0KHRoaXMucm9vdE5vZGUpXG4gICAgICB9XG4gICAgfVxuICB9XG4gIC8qKlxuICAgKiBMb29rIHVwIHRoZSBVUkwgYXQgKGNvbCwgcm93KSBpbiB0aGUgY3VycmVudCBmcm9udCBmcmFtZS4gQ2hlY2tzIGZvclxuICAgKiBhbiBPU0MgOCBoeXBlcmxpbmsgZmlyc3QsIHRoZW4gZmFsbHMgYmFjayB0byBzY2FubmluZyB0aGUgcm93IGZvciBhXG4gICAqIHBsYWluLXRleHQgVVJMIChtb3VzZSB0cmFja2luZyBpbnRlcmNlcHRzIHRoZSB0ZXJtaW5hbCdzIG5hdGl2ZVxuICAgKiBDbWQrQ2xpY2sgVVJMIGRldGVjdGlvbiwgc28gd2UgcmVwbGljYXRlIGl0KS4gVGhpcyBpcyBhIHB1cmUgbG9va3VwXG4gICAqIHdpdGggbm8gc2lkZSBlZmZlY3RzIOKAlCBjYWxsIGl0IHN5bmNocm9ub3VzbHkgYXQgY2xpY2sgdGltZSBzbyB0aGVcbiAgICogcmVzdWx0IHJlZmxlY3RzIHRoZSBzY3JlZW4gdGhlIHVzZXIgYWN0dWFsbHkgY2xpY2tlZCBvbiwgdGhlbiBkZWZlclxuICAgKiB0aGUgYnJvd3Nlci1vcGVuIGFjdGlvbiB2aWEgYSB0aW1lci5cbiAgICovXG4gIGdldEh5cGVybGlua0F0KGNvbDogbnVtYmVyLCByb3c6IG51bWJlcik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgaWYgKCF0aGlzLmFsdFNjcmVlbkFjdGl2ZSkgcmV0dXJuIHVuZGVmaW5lZFxuICAgIGNvbnN0IHNjcmVlbiA9IHRoaXMuZnJvbnRGcmFtZS5zY3JlZW5cbiAgICBjb25zdCBjZWxsID0gY2VsbEF0KHNjcmVlbiwgY29sLCByb3cpXG4gICAgbGV0IHVybCA9IGNlbGw/Lmh5cGVybGlua1xuICAgIC8vIFNwYWNlclRhaWwgY2VsbHMgKHJpZ2h0IGhhbGYgb2Ygd2lkZS9DSksvZW1vamkgY2hhcnMpIHN0b3JlIHRoZVxuICAgIC8vIGh5cGVybGluayBvbiB0aGUgaGVhZCBjZWxsIGF0IGNvbC0xLlxuICAgIGlmICghdXJsICYmIGNlbGw/LndpZHRoID09PSBDZWxsV2lkdGguU3BhY2VyVGFpbCAmJiBjb2wgPiAwKSB7XG4gICAgICB1cmwgPSBjZWxsQXQoc2NyZWVuLCBjb2wgLSAxLCByb3cpPy5oeXBlcmxpbmtcbiAgICB9XG4gICAgcmV0dXJuIHVybCA/PyBmaW5kUGxhaW5UZXh0VXJsQXQoc2NyZWVuLCBjb2wsIHJvdylcbiAgfVxuXG4gIC8qKlxuICAgKiBPcHRpb25hbCBjYWxsYmFjayBmaXJlZCB3aGVuIGNsaWNraW5nIGFuIE9TQyA4IGh5cGVybGluayBpbiBmdWxsc2NyZWVuXG4gICAqIG1vZGUuIFNldCBieSBGdWxsc2NyZWVuTGF5b3V0IHZpYSB1c2VMYXlvdXRFZmZlY3QuXG4gICAqL1xuICBvbkh5cGVybGlua0NsaWNrOiAoKHVybDogc3RyaW5nKSA9PiB2b2lkKSB8IHVuZGVmaW5lZFxuXG4gIC8qKlxuICAgKiBTdGFibGUgcHJvdG90eXBlIHdyYXBwZXIgZm9yIG9uSHlwZXJsaW5rQ2xpY2suIFBhc3NlZCB0byA8QXBwPiBhc1xuICAgKiBvbk9wZW5IeXBlcmxpbmsgc28gdGhlIHByb3AgaXMgYSBib3VuZCBtZXRob2QgKGF1dG9CaW5kJ2QpIHRoYXQgcmVhZHNcbiAgICogdGhlIG11dGFibGUgZmllbGQgYXQgY2FsbCB0aW1lIOKAlCBub3QgdGhlIHVuZGVmaW5lZC1hdC1yZW5kZXIgdmFsdWUuXG4gICAqL1xuICBvcGVuSHlwZXJsaW5rKHVybDogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5vbkh5cGVybGlua0NsaWNrPy4odXJsKVxuICB9XG5cbiAgLyoqXG4gICAqIEhhbmRsZSBhIGRvdWJsZS0gb3IgdHJpcGxlLWNsaWNrIGF0IChjb2wsIHJvdyk6IHNlbGVjdCB0aGUgd29yZCBvclxuICAgKiBsaW5lIHVuZGVyIHRoZSBjdXJzb3IgYnkgcmVhZGluZyB0aGUgY3VycmVudCBzY3JlZW4gYnVmZmVyLiBDYWxsZWQgb25cbiAgICogUFJFU1MgKG5vdCByZWxlYXNlKSBzbyB0aGUgaGlnaGxpZ2h0IGFwcGVhcnMgaW1tZWRpYXRlbHkgYW5kIGRyYWcgY2FuXG4gICAqIGV4dGVuZCB0aGUgc2VsZWN0aW9uIHdvcmQtYnktd29yZCAvIGxpbmUtYnktbGluZS4gRmFsbHMgYmFjayB0b1xuICAgKiBjaGFyLW1vZGUgc3RhcnRTZWxlY3Rpb24gaWYgdGhlIGNsaWNrIGxhbmRzIG9uIGEgbm9TZWxlY3QgY2VsbC5cbiAgICovXG4gIGhhbmRsZU11bHRpQ2xpY2soY29sOiBudW1iZXIsIHJvdzogbnVtYmVyLCBjb3VudDogMiB8IDMpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuYWx0U2NyZWVuQWN0aXZlKSByZXR1cm5cbiAgICBjb25zdCBzY3JlZW4gPSB0aGlzLmZyb250RnJhbWUuc2NyZWVuXG4gICAgLy8gc2VsZWN0V29yZEF0L3NlbGVjdExpbmVBdCBuby1vcCBvbiBub1NlbGVjdC9vdXQtb2YtYm91bmRzLiBTZWVkIHdpdGhcbiAgICAvLyBhIGNoYXItbW9kZSBzZWxlY3Rpb24gc28gdGhlIHByZXNzIHN0aWxsIHN0YXJ0cyBhIGRyYWcgZXZlbiBpZiB0aGVcbiAgICAvLyB3b3JkL2xpbmUgc2NhbiBmaW5kcyBub3RoaW5nIHNlbGVjdGFibGUuXG4gICAgc3RhcnRTZWxlY3Rpb24odGhpcy5zZWxlY3Rpb24sIGNvbCwgcm93KVxuICAgIGlmIChjb3VudCA9PT0gMikgc2VsZWN0V29yZEF0KHRoaXMuc2VsZWN0aW9uLCBzY3JlZW4sIGNvbCwgcm93KVxuICAgIGVsc2Ugc2VsZWN0TGluZUF0KHRoaXMuc2VsZWN0aW9uLCBzY3JlZW4sIHJvdylcbiAgICAvLyBFbnN1cmUgaGFzU2VsZWN0aW9uIGlzIHRydWUgc28gcmVsZWFzZSBkb2Vzbid0IHJlLWRpc3BhdGNoIG9uQ2xpY2tBdC5cbiAgICAvLyBzZWxlY3RXb3JkQXQgbm8tb3BzIG9uIG5vU2VsZWN0OyBzZWxlY3RMaW5lQXQgbm8tb3BzIG91dC1vZi1ib3VuZHMuXG4gICAgaWYgKCF0aGlzLnNlbGVjdGlvbi5mb2N1cykgdGhpcy5zZWxlY3Rpb24uZm9jdXMgPSB0aGlzLnNlbGVjdGlvbi5hbmNob3JcbiAgICB0aGlzLm5vdGlmeVNlbGVjdGlvbkNoYW5nZSgpXG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlIGEgZHJhZy1tb3Rpb24gYXQgKGNvbCwgcm93KS4gSW4gY2hhciBtb2RlIHVwZGF0ZXMgZm9jdXMgdG8gdGhlXG4gICAqIGV4YWN0IGNlbGwuIEluIHdvcmQvbGluZSBtb2RlIHNuYXBzIHRvIHdvcmQvbGluZSBib3VuZGFyaWVzIHNvIHRoZVxuICAgKiBzZWxlY3Rpb24gZXh0ZW5kcyBieSB3b3JkL2xpbmUgbGlrZSBuYXRpdmUgbWFjT1MuIEdhdGVkIG9uXG4gICAqIGFsdFNjcmVlbkFjdGl2ZSBmb3IgdGhlIHNhbWUgcmVhc29uIGFzIGRpc3BhdGNoQ2xpY2suXG4gICAqL1xuICBoYW5kbGVTZWxlY3Rpb25EcmFnKGNvbDogbnVtYmVyLCByb3c6IG51bWJlcik6IHZvaWQge1xuICAgIGlmICghdGhpcy5hbHRTY3JlZW5BY3RpdmUpIHJldHVyblxuICAgIGNvbnN0IHNlbCA9IHRoaXMuc2VsZWN0aW9uXG4gICAgaWYgKHNlbC5hbmNob3JTcGFuKSB7XG4gICAgICBleHRlbmRTZWxlY3Rpb24oc2VsLCB0aGlzLmZyb250RnJhbWUuc2NyZWVuLCBjb2wsIHJvdylcbiAgICB9IGVsc2Uge1xuICAgICAgdXBkYXRlU2VsZWN0aW9uKHNlbCwgY29sLCByb3cpXG4gICAgfVxuICAgIHRoaXMubm90aWZ5U2VsZWN0aW9uQ2hhbmdlKClcbiAgfVxuXG4gIC8vIE1ldGhvZHMgdG8gcHJvcGVybHkgc3VzcGVuZCBzdGRpbiBmb3IgZXh0ZXJuYWwgZWRpdG9yIHVzYWdlXG4gIC8vIFRoaXMgaXMgbmVlZGVkIHRvIHByZXZlbnQgSW5rIGZyb20gc3dhbGxvd2luZyBrZXlzdHJva2VzIHdoZW4gYW4gZXh0ZXJuYWwgZWRpdG9yIGlzIGFjdGl2ZVxuICBwcml2YXRlIHN0ZGluTGlzdGVuZXJzOiBBcnJheTx7XG4gICAgZXZlbnQ6IHN0cmluZ1xuICAgIGxpc3RlbmVyOiAoLi4uYXJnczogdW5rbm93bltdKSA9PiB2b2lkXG4gIH0+ID0gW11cbiAgcHJpdmF0ZSB3YXNSYXdNb2RlID0gZmFsc2VcblxuICBzdXNwZW5kU3RkaW4oKTogdm9pZCB7XG4gICAgY29uc3Qgc3RkaW4gPSB0aGlzLm9wdGlvbnMuc3RkaW5cbiAgICBpZiAoIXN0ZGluLmlzVFRZKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBTdG9yZSBhbmQgcmVtb3ZlIGFsbCAncmVhZGFibGUnIGV2ZW50IGxpc3RlbmVycyB0ZW1wb3JhcmlseVxuICAgIC8vIFRoaXMgcHJldmVudHMgSW5rIGZyb20gY29uc3VtaW5nIHN0ZGluIHdoaWxlIHRoZSBlZGl0b3IgaXMgYWN0aXZlXG4gICAgY29uc3QgcmVhZGFibGVMaXN0ZW5lcnMgPSBzdGRpbi5saXN0ZW5lcnMoJ3JlYWRhYmxlJylcbiAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICBgW3N0ZGluXSBzdXNwZW5kU3RkaW46IHJlbW92aW5nICR7cmVhZGFibGVMaXN0ZW5lcnMubGVuZ3RofSByZWFkYWJsZSBsaXN0ZW5lcihzKSwgd2FzUmF3TW9kZT0keyhzdGRpbiBhcyBOb2RlSlMuUmVhZFN0cmVhbSAmIHsgaXNSYXc/OiBib29sZWFuIH0pLmlzUmF3ID8/IGZhbHNlfWAsXG4gICAgKVxuICAgIHJlYWRhYmxlTGlzdGVuZXJzLmZvckVhY2gobGlzdGVuZXIgPT4ge1xuICAgICAgdGhpcy5zdGRpbkxpc3RlbmVycy5wdXNoKHtcbiAgICAgICAgZXZlbnQ6ICdyZWFkYWJsZScsXG4gICAgICAgIGxpc3RlbmVyOiBsaXN0ZW5lciBhcyAoLi4uYXJnczogdW5rbm93bltdKSA9PiB2b2lkLFxuICAgICAgfSlcbiAgICAgIHN0ZGluLnJlbW92ZUxpc3RlbmVyKCdyZWFkYWJsZScsIGxpc3RlbmVyIGFzICguLi5hcmdzOiB1bmtub3duW10pID0+IHZvaWQpXG4gICAgfSlcblxuICAgIC8vIElmIHJhdyBtb2RlIGlzIGVuYWJsZWQsIGRpc2FibGUgaXQgdGVtcG9yYXJpbHlcbiAgICBjb25zdCBzdGRpbldpdGhSYXcgPSBzdGRpbiBhcyBOb2RlSlMuUmVhZFN0cmVhbSAmIHtcbiAgICAgIGlzUmF3PzogYm9vbGVhblxuICAgICAgc2V0UmF3TW9kZT86IChtb2RlOiBib29sZWFuKSA9PiB2b2lkXG4gICAgfVxuICAgIGlmIChzdGRpbldpdGhSYXcuaXNSYXcgJiYgc3RkaW5XaXRoUmF3LnNldFJhd01vZGUpIHtcbiAgICAgIHN0ZGluV2l0aFJhdy5zZXRSYXdNb2RlKGZhbHNlKVxuICAgICAgdGhpcy53YXNSYXdNb2RlID0gdHJ1ZVxuICAgIH1cbiAgfVxuXG4gIHJlc3VtZVN0ZGluKCk6IHZvaWQge1xuICAgIGNvbnN0IHN0ZGluID0gdGhpcy5vcHRpb25zLnN0ZGluXG4gICAgaWYgKCFzdGRpbi5pc1RUWSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gUmUtYXR0YWNoIGFsbCB0aGUgc3RvcmVkIGxpc3RlbmVyc1xuICAgIGlmICh0aGlzLnN0ZGluTGlzdGVuZXJzLmxlbmd0aCA9PT0gMCAmJiAhdGhpcy53YXNSYXdNb2RlKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICdbc3RkaW5dIHJlc3VtZVN0ZGluOiBjYWxsZWQgd2l0aCBubyBzdG9yZWQgbGlzdGVuZXJzIGFuZCB3YXNSYXdNb2RlPWZhbHNlIChwb3NzaWJsZSBkZXN5bmMpJyxcbiAgICAgICAgeyBsZXZlbDogJ3dhcm4nIH0sXG4gICAgICApXG4gICAgfVxuICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgIGBbc3RkaW5dIHJlc3VtZVN0ZGluOiByZS1hdHRhY2hpbmcgJHt0aGlzLnN0ZGluTGlzdGVuZXJzLmxlbmd0aH0gbGlzdGVuZXIocyksIHdhc1Jhd01vZGU9JHt0aGlzLndhc1Jhd01vZGV9YCxcbiAgICApXG4gICAgdGhpcy5zdGRpbkxpc3RlbmVycy5mb3JFYWNoKCh7IGV2ZW50LCBsaXN0ZW5lciB9KSA9PiB7XG4gICAgICBzdGRpbi5hZGRMaXN0ZW5lcihldmVudCwgbGlzdGVuZXIpXG4gICAgfSlcbiAgICB0aGlzLnN0ZGluTGlzdGVuZXJzID0gW11cblxuICAgIC8vIFJlLWVuYWJsZSByYXcgbW9kZSBpZiBpdCB3YXMgZW5hYmxlZCBiZWZvcmVcbiAgICBpZiAodGhpcy53YXNSYXdNb2RlKSB7XG4gICAgICBjb25zdCBzdGRpbldpdGhSYXcgPSBzdGRpbiBhcyBOb2RlSlMuUmVhZFN0cmVhbSAmIHtcbiAgICAgICAgc2V0UmF3TW9kZT86IChtb2RlOiBib29sZWFuKSA9PiB2b2lkXG4gICAgICB9XG4gICAgICBpZiAoc3RkaW5XaXRoUmF3LnNldFJhd01vZGUpIHtcbiAgICAgICAgc3RkaW5XaXRoUmF3LnNldFJhd01vZGUodHJ1ZSlcbiAgICAgIH1cbiAgICAgIHRoaXMud2FzUmF3TW9kZSA9IGZhbHNlXG4gICAgfVxuICB9XG5cbiAgLy8gU3RhYmxlIGlkZW50aXR5IGZvciBUZXJtaW5hbFdyaXRlQ29udGV4dC4gQW4gaW5saW5lIGFycm93IGhlcmUgd291bGRcbiAgLy8gY2hhbmdlIG9uIGV2ZXJ5IHJlbmRlcigpIGNhbGwgKGluaXRpYWwgbW91bnQgKyBlYWNoIHJlc2l6ZSksIHdoaWNoXG4gIC8vIGNhc2NhZGVzIHRocm91Z2ggdXNlQ29udGV4dCDihpIgPEFsdGVybmF0ZVNjcmVlbj4ncyB1c2VMYXlvdXRFZmZlY3QgZGVwXG4gIC8vIGFycmF5IOKGkiBzcHVyaW91cyBleGl0K3JlLWVudGVyIG9mIHRoZSBhbHQgc2NyZWVuIG9uIGV2ZXJ5IFNJR1dJTkNILlxuICBwcml2YXRlIHdyaXRlUmF3KGRhdGE6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMub3B0aW9ucy5zdGRvdXQud3JpdGUoZGF0YSlcbiAgfVxuXG4gIHByaXZhdGUgc2V0Q3Vyc29yRGVjbGFyYXRpb246IEN1cnNvckRlY2xhcmF0aW9uU2V0dGVyID0gKFxuICAgIGRlY2wsXG4gICAgY2xlYXJJZk5vZGUsXG4gICkgPT4ge1xuICAgIGlmIChcbiAgICAgIGRlY2wgPT09IG51bGwgJiZcbiAgICAgIGNsZWFySWZOb2RlICE9PSB1bmRlZmluZWQgJiZcbiAgICAgIHRoaXMuY3Vyc29yRGVjbGFyYXRpb24/Lm5vZGUgIT09IGNsZWFySWZOb2RlXG4gICAgKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgdGhpcy5jdXJzb3JEZWNsYXJhdGlvbiA9IGRlY2xcbiAgfVxuXG4gIHJlbmRlcihub2RlOiBSZWFjdE5vZGUpOiB2b2lkIHtcbiAgICB0aGlzLmN1cnJlbnROb2RlID0gbm9kZVxuXG4gICAgY29uc3QgdHJlZSA9IChcbiAgICAgIDxBcHBcbiAgICAgICAgc3RkaW49e3RoaXMub3B0aW9ucy5zdGRpbn1cbiAgICAgICAgc3Rkb3V0PXt0aGlzLm9wdGlvbnMuc3Rkb3V0fVxuICAgICAgICBzdGRlcnI9e3RoaXMub3B0aW9ucy5zdGRlcnJ9XG4gICAgICAgIGV4aXRPbkN0cmxDPXt0aGlzLm9wdGlvbnMuZXhpdE9uQ3RybEN9XG4gICAgICAgIG9uRXhpdD17dGhpcy51bm1vdW50fVxuICAgICAgICB0ZXJtaW5hbENvbHVtbnM9e3RoaXMudGVybWluYWxDb2x1bW5zfVxuICAgICAgICB0ZXJtaW5hbFJvd3M9e3RoaXMudGVybWluYWxSb3dzfVxuICAgICAgICBzZWxlY3Rpb249e3RoaXMuc2VsZWN0aW9ufVxuICAgICAgICBvblNlbGVjdGlvbkNoYW5nZT17dGhpcy5ub3RpZnlTZWxlY3Rpb25DaGFuZ2V9XG4gICAgICAgIG9uQ2xpY2tBdD17dGhpcy5kaXNwYXRjaENsaWNrfVxuICAgICAgICBvbkhvdmVyQXQ9e3RoaXMuZGlzcGF0Y2hIb3Zlcn1cbiAgICAgICAgZ2V0SHlwZXJsaW5rQXQ9e3RoaXMuZ2V0SHlwZXJsaW5rQXR9XG4gICAgICAgIG9uT3Blbkh5cGVybGluaz17dGhpcy5vcGVuSHlwZXJsaW5rfVxuICAgICAgICBvbk11bHRpQ2xpY2s9e3RoaXMuaGFuZGxlTXVsdGlDbGlja31cbiAgICAgICAgb25TZWxlY3Rpb25EcmFnPXt0aGlzLmhhbmRsZVNlbGVjdGlvbkRyYWd9XG4gICAgICAgIG9uU3RkaW5SZXN1bWU9e3RoaXMucmVhc3NlcnRUZXJtaW5hbE1vZGVzfVxuICAgICAgICBvbkN1cnNvckRlY2xhcmF0aW9uPXt0aGlzLnNldEN1cnNvckRlY2xhcmF0aW9ufVxuICAgICAgICBkaXNwYXRjaEtleWJvYXJkRXZlbnQ9e3RoaXMuZGlzcGF0Y2hLZXlib2FyZEV2ZW50fVxuICAgICAgPlxuICAgICAgICA8VGVybWluYWxXcml0ZVByb3ZpZGVyIHZhbHVlPXt0aGlzLndyaXRlUmF3fT5cbiAgICAgICAgICB7bm9kZX1cbiAgICAgICAgPC9UZXJtaW5hbFdyaXRlUHJvdmlkZXI+XG4gICAgICA8L0FwcD5cbiAgICApXG5cbiAgICAvLyBAdHMtZXhwZWN0LWVycm9yIHVwZGF0ZUNvbnRhaW5lclN5bmMgZXhpc3RzIGluIHJlYWN0LXJlY29uY2lsZXIgYnV0IG5vdCBpbiBAdHlwZXMvcmVhY3QtcmVjb25jaWxlclxuICAgIHJlY29uY2lsZXIudXBkYXRlQ29udGFpbmVyU3luYyh0cmVlLCB0aGlzLmNvbnRhaW5lciwgbnVsbCwgbm9vcClcbiAgICAvLyBAdHMtZXhwZWN0LWVycm9yIGZsdXNoU3luY1dvcmsgZXhpc3RzIGluIHJlYWN0LXJlY29uY2lsZXIgYnV0IG5vdCBpbiBAdHlwZXMvcmVhY3QtcmVjb25jaWxlclxuICAgIHJlY29uY2lsZXIuZmx1c2hTeW5jV29yaygpXG4gIH1cblxuICB1bm1vdW50KGVycm9yPzogRXJyb3IgfCBudW1iZXIgfCBudWxsKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuaXNVbm1vdW50ZWQpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHRoaXMub25SZW5kZXIoKVxuICAgIHRoaXMudW5zdWJzY3JpYmVFeGl0KClcblxuICAgIGlmICh0eXBlb2YgdGhpcy5yZXN0b3JlQ29uc29sZSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgdGhpcy5yZXN0b3JlQ29uc29sZSgpXG4gICAgfVxuICAgIHRoaXMucmVzdG9yZVN0ZGVycj8uKClcblxuICAgIHRoaXMudW5zdWJzY3JpYmVUVFlIYW5kbGVycz8uKClcblxuICAgIC8vIE5vbi1UVFkgZW52aXJvbm1lbnRzIGRvbid0IGhhbmRsZSBlcmFzaW5nIGFuc2kgZXNjYXBlcyB3ZWxsLCBzbyBpdCdzIGJldHRlciB0b1xuICAgIC8vIG9ubHkgcmVuZGVyIGxhc3QgZnJhbWUgb2Ygbm9uLXN0YXRpYyBvdXRwdXRcbiAgICBjb25zdCBkaWZmID0gdGhpcy5sb2cucmVuZGVyUHJldmlvdXNPdXRwdXRfREVQUkVDQVRFRCh0aGlzLmZyb250RnJhbWUpXG4gICAgd3JpdGVEaWZmVG9UZXJtaW5hbCh0aGlzLnRlcm1pbmFsLCBvcHRpbWl6ZShkaWZmKSlcblxuICAgIC8vIENsZWFuIHVwIHRlcm1pbmFsIG1vZGVzIHN5bmNocm9ub3VzbHkgYmVmb3JlIHByb2Nlc3MgZXhpdC5cbiAgICAvLyBSZWFjdCdzIGNvbXBvbmVudFdpbGxVbm1vdW50IHdvbid0IHJ1biBpbiB0aW1lIHdoZW4gcHJvY2Vzcy5leGl0KCkgaXMgY2FsbGVkLFxuICAgIC8vIHNvIHdlIG11c3QgcmVzZXQgdGVybWluYWwgbW9kZXMgaGVyZSB0byBwcmV2ZW50IGVzY2FwZSBzZXF1ZW5jZSBsZWFrYWdlLlxuICAgIC8vIFVzZSB3cml0ZVN5bmMgdG8gc3Rkb3V0IChmZCAxKSB0byBlbnN1cmUgd3JpdGVzIGNvbXBsZXRlIGJlZm9yZSBleGl0LlxuICAgIC8vIFdlIHVuY29uZGl0aW9uYWxseSBzZW5kIGFsbCBkaXNhYmxlIHNlcXVlbmNlcyBiZWNhdXNlIHRlcm1pbmFsIGRldGVjdGlvblxuICAgIC8vIG1heSBub3Qgd29yayBjb3JyZWN0bHkgKGUuZy4sIGluIHRtdXgsIHNjcmVlbikgYW5kIHRoZXNlIGFyZSBuby1vcHMgb25cbiAgICAvLyB0ZXJtaW5hbHMgdGhhdCBkb24ndCBzdXBwb3J0IHRoZW0uXG4gICAgLyogZXNsaW50LWRpc2FibGUgY3VzdG9tLXJ1bGVzL25vLXN5bmMtZnMgLS0gcHJvY2VzcyBleGl0aW5nOyBhc3luYyB3cml0ZXMgd291bGQgYmUgZHJvcHBlZCAqL1xuICAgIGlmICh0aGlzLm9wdGlvbnMuc3Rkb3V0LmlzVFRZKSB7XG4gICAgICBpZiAodGhpcy5hbHRTY3JlZW5BY3RpdmUpIHtcbiAgICAgICAgLy8gPEFsdGVybmF0ZVNjcmVlbj4ncyB1bm1vdW50IGVmZmVjdCB3b24ndCBydW4gZHVyaW5nIHNpZ25hbC1leGl0LlxuICAgICAgICAvLyBFeGl0IGFsdCBzY3JlZW4gRklSU1Qgc28gb3RoZXIgY2xlYW51cCBzZXF1ZW5jZXMgZ28gdG8gdGhlIG1haW4gc2NyZWVuLlxuICAgICAgICB3cml0ZVN5bmMoMSwgRVhJVF9BTFRfU0NSRUVOKVxuICAgICAgfVxuICAgICAgLy8gRGlzYWJsZSBtb3VzZSB0cmFja2luZyDigJQgdW5jb25kaXRpb25hbCBiZWNhdXNlIGFsdFNjcmVlbkFjdGl2ZSBjYW4gYmVcbiAgICAgIC8vIHN0YWxlIGlmIEFsdGVybmF0ZVNjcmVlbidzIHVubW91bnQgKHdoaWNoIGZsaXBzIHRoZSBmbGFnKSByYWNlZCBhXG4gICAgICAvLyBibG9ja2VkIGV2ZW50IGxvb3AgKyBTSUdJTlQuIE5vLW9wIGlmIHRyYWNraW5nIHdhcyBuZXZlciBlbmFibGVkLlxuICAgICAgd3JpdGVTeW5jKDEsIERJU0FCTEVfTU9VU0VfVFJBQ0tJTkcpXG4gICAgICAvLyBEcmFpbiBzdGRpbiBzbyBpbi1mbGlnaHQgbW91c2UgZXZlbnRzIGRvbid0IGxlYWsgdG8gdGhlIHNoZWxsXG4gICAgICB0aGlzLmRyYWluU3RkaW4oKVxuICAgICAgLy8gRGlzYWJsZSBleHRlbmRlZCBrZXkgcmVwb3J0aW5nIChib3RoIGtpdHR5IGFuZCBtb2RpZnlPdGhlcktleXMpXG4gICAgICB3cml0ZVN5bmMoMSwgRElTQUJMRV9NT0RJRllfT1RIRVJfS0VZUylcbiAgICAgIHdyaXRlU3luYygxLCBESVNBQkxFX0tJVFRZX0tFWUJPQVJEKVxuICAgICAgLy8gRGlzYWJsZSBmb2N1cyBldmVudHMgKERFQ1NFVCAxMDA0KVxuICAgICAgd3JpdGVTeW5jKDEsIERGRSlcbiAgICAgIC8vIERpc2FibGUgYnJhY2tldGVkIHBhc3RlIG1vZGVcbiAgICAgIHdyaXRlU3luYygxLCBEQlApXG4gICAgICAvLyBTaG93IGN1cnNvclxuICAgICAgd3JpdGVTeW5jKDEsIFNIT1dfQ1VSU09SKVxuICAgICAgLy8gQ2xlYXIgaVRlcm0yIHByb2dyZXNzIGJhclxuICAgICAgd3JpdGVTeW5jKDEsIENMRUFSX0lURVJNMl9QUk9HUkVTUylcbiAgICAgIC8vIENsZWFyIHRhYiBzdGF0dXMgKE9TQyAyMTMzNykgc28gYSBzdGFsZSBkb3QgZG9lc24ndCBsaW5nZXJcbiAgICAgIGlmIChzdXBwb3J0c1RhYlN0YXR1cygpKVxuICAgICAgICB3cml0ZVN5bmMoMSwgd3JhcEZvck11bHRpcGxleGVyKENMRUFSX1RBQl9TVEFUVVMpKVxuICAgIH1cbiAgICAvKiBlc2xpbnQtZW5hYmxlIGN1c3RvbS1ydWxlcy9uby1zeW5jLWZzICovXG5cbiAgICB0aGlzLmlzVW5tb3VudGVkID0gdHJ1ZVxuXG4gICAgLy8gQ2FuY2VsIGFueSBwZW5kaW5nIHRocm90dGxlZCByZW5kZXJzIHRvIHByZXZlbnQgYWNjZXNzaW5nIGZyZWVkIFlvZ2Egbm9kZXNcbiAgICB0aGlzLnNjaGVkdWxlUmVuZGVyLmNhbmNlbD8uKClcbiAgICBpZiAodGhpcy5kcmFpblRpbWVyICE9PSBudWxsKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5kcmFpblRpbWVyKVxuICAgICAgdGhpcy5kcmFpblRpbWVyID0gbnVsbFxuICAgIH1cblxuICAgIC8vIEB0cy1leHBlY3QtZXJyb3IgdXBkYXRlQ29udGFpbmVyU3luYyBleGlzdHMgaW4gcmVhY3QtcmVjb25jaWxlciBidXQgbm90IGluIEB0eXBlcy9yZWFjdC1yZWNvbmNpbGVyXG4gICAgcmVjb25jaWxlci51cGRhdGVDb250YWluZXJTeW5jKG51bGwsIHRoaXMuY29udGFpbmVyLCBudWxsLCBub29wKVxuICAgIC8vIEB0cy1leHBlY3QtZXJyb3IgZmx1c2hTeW5jV29yayBleGlzdHMgaW4gcmVhY3QtcmVjb25jaWxlciBidXQgbm90IGluIEB0eXBlcy9yZWFjdC1yZWNvbmNpbGVyXG4gICAgcmVjb25jaWxlci5mbHVzaFN5bmNXb3JrKClcbiAgICBpbnN0YW5jZXMuZGVsZXRlKHRoaXMub3B0aW9ucy5zdGRvdXQpXG5cbiAgICAvLyBGcmVlIHRoZSByb290IHlvZ2Egbm9kZSwgdGhlbiBjbGVhciBpdHMgcmVmZXJlbmNlLiBDaGlsZHJlbiBhcmUgYWxyZWFkeVxuICAgIC8vIGZyZWVkIGJ5IHRoZSByZWNvbmNpbGVyJ3MgcmVtb3ZlQ2hpbGRGcm9tQ29udGFpbmVyOyB1c2luZyAuZnJlZSgpIChub3RcbiAgICAvLyAuZnJlZVJlY3Vyc2l2ZSgpKSBhdm9pZHMgZG91YmxlLWZyZWVpbmcgdGhlbS5cbiAgICB0aGlzLnJvb3ROb2RlLnlvZ2FOb2RlPy5mcmVlKClcbiAgICB0aGlzLnJvb3ROb2RlLnlvZ2FOb2RlID0gdW5kZWZpbmVkXG5cbiAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgdGhpcy5yZWplY3RFeGl0UHJvbWlzZShlcnJvcilcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5yZXNvbHZlRXhpdFByb21pc2UoKVxuICAgIH1cbiAgfVxuXG4gIGFzeW5jIHdhaXRVbnRpbEV4aXQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5leGl0UHJvbWlzZSB8fD0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdGhpcy5yZXNvbHZlRXhpdFByb21pc2UgPSByZXNvbHZlXG4gICAgICB0aGlzLnJlamVjdEV4aXRQcm9taXNlID0gcmVqZWN0XG4gICAgfSlcblxuICAgIHJldHVybiB0aGlzLmV4aXRQcm9taXNlXG4gIH1cblxuICByZXNldExpbmVDb3VudCgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5vcHRpb25zLnN0ZG91dC5pc1RUWSkge1xuICAgICAgLy8gU3dhcCBzbyBvbGQgZnJvbnQgYmVjb21lcyBiYWNrIChmb3Igc2NyZWVuIHJldXNlKSwgdGhlbiByZXNldCBmcm9udFxuICAgICAgdGhpcy5iYWNrRnJhbWUgPSB0aGlzLmZyb250RnJhbWVcbiAgICAgIHRoaXMuZnJvbnRGcmFtZSA9IGVtcHR5RnJhbWUoXG4gICAgICAgIHRoaXMuZnJvbnRGcmFtZS52aWV3cG9ydC5oZWlnaHQsXG4gICAgICAgIHRoaXMuZnJvbnRGcmFtZS52aWV3cG9ydC53aWR0aCxcbiAgICAgICAgdGhpcy5zdHlsZVBvb2wsXG4gICAgICAgIHRoaXMuY2hhclBvb2wsXG4gICAgICAgIHRoaXMuaHlwZXJsaW5rUG9vbCxcbiAgICAgIClcbiAgICAgIHRoaXMubG9nLnJlc2V0KClcbiAgICAgIC8vIGZyb250RnJhbWUgaXMgcmVzZXQsIHNvIGZyYW1lLmN1cnNvciBvbiB0aGUgbmV4dCByZW5kZXIgaXMgKDAsMCkuXG4gICAgICAvLyBDbGVhciBkaXNwbGF5Q3Vyc29yIHNvIHRoZSBwcmVhbWJsZSBkb2Vzbid0IGNvbXB1dGUgYSBzdGFsZSBkZWx0YS5cbiAgICAgIHRoaXMuZGlzcGxheUN1cnNvciA9IG51bGxcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVwbGFjZSBjaGFyL2h5cGVybGluayBwb29scyB3aXRoIGZyZXNoIGluc3RhbmNlcyB0byBwcmV2ZW50IHVuYm91bmRlZFxuICAgKiBncm93dGggZHVyaW5nIGxvbmcgc2Vzc2lvbnMuIE1pZ3JhdGVzIHRoZSBmcm9udCBmcmFtZSdzIHNjcmVlbiBJRHMgaW50b1xuICAgKiB0aGUgbmV3IHBvb2xzIHNvIGRpZmZpbmcgcmVtYWlucyBjb3JyZWN0LiBUaGUgYmFjayBmcmFtZSBkb2Vzbid0IG5lZWRcbiAgICogbWlncmF0aW9uIOKAlCByZXNldFNjcmVlbiB6ZXJvcyBpdCBiZWZvcmUgYW55IHJlYWRzLlxuICAgKlxuICAgKiBDYWxsIGJldHdlZW4gY29udmVyc2F0aW9uIHR1cm5zIG9yIHBlcmlvZGljYWxseS5cbiAgICovXG4gIHJlc2V0UG9vbHMoKTogdm9pZCB7XG4gICAgdGhpcy5jaGFyUG9vbCA9IG5ldyBDaGFyUG9vbCgpXG4gICAgdGhpcy5oeXBlcmxpbmtQb29sID0gbmV3IEh5cGVybGlua1Bvb2woKVxuICAgIG1pZ3JhdGVTY3JlZW5Qb29scyhcbiAgICAgIHRoaXMuZnJvbnRGcmFtZS5zY3JlZW4sXG4gICAgICB0aGlzLmNoYXJQb29sLFxuICAgICAgdGhpcy5oeXBlcmxpbmtQb29sLFxuICAgIClcbiAgICAvLyBCYWNrIGZyYW1lJ3MgZGF0YSBpcyB6ZXJvZWQgYnkgcmVzZXRTY3JlZW4gYmVmb3JlIHJlYWRzLCBidXQgaXRzIHBvb2xcbiAgICAvLyByZWZlcmVuY2VzIGFyZSB1c2VkIGJ5IHRoZSByZW5kZXJlciB0byBpbnRlcm4gbmV3IGNoYXJhY3RlcnMuIFBvaW50XG4gICAgLy8gdGhlbSBhdCB0aGUgbmV3IHBvb2xzIHNvIHRoZSBuZXh0IGZyYW1lJ3MgSURzIGFyZSBjb21wYXJhYmxlLlxuICAgIHRoaXMuYmFja0ZyYW1lLnNjcmVlbi5jaGFyUG9vbCA9IHRoaXMuY2hhclBvb2xcbiAgICB0aGlzLmJhY2tGcmFtZS5zY3JlZW4uaHlwZXJsaW5rUG9vbCA9IHRoaXMuaHlwZXJsaW5rUG9vbFxuICB9XG5cbiAgcGF0Y2hDb25zb2xlKCk6ICgpID0+IHZvaWQge1xuICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db25zb2xlOiBpbnRlbnRpb25hbGx5IHBhdGNoaW5nIGdsb2JhbCBjb25zb2xlXG4gICAgY29uc3QgY29uID0gY29uc29sZVxuICAgIGNvbnN0IG9yaWdpbmFsczogUGFydGlhbDxSZWNvcmQ8a2V5b2YgQ29uc29sZSwgQ29uc29sZVtrZXlvZiBDb25zb2xlXT4+ID0ge31cbiAgICBjb25zdCB0b0RlYnVnID0gKC4uLmFyZ3M6IHVua25vd25bXSkgPT5cbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgY29uc29sZS5sb2c6ICR7Zm9ybWF0KC4uLmFyZ3MpfWApXG4gICAgY29uc3QgdG9FcnJvciA9ICguLi5hcmdzOiB1bmtub3duW10pID0+XG4gICAgICBsb2dFcnJvcihuZXcgRXJyb3IoYGNvbnNvbGUuZXJyb3I6ICR7Zm9ybWF0KC4uLmFyZ3MpfWApKVxuICAgIGZvciAoY29uc3QgbSBvZiBDT05TT0xFX1NURE9VVF9NRVRIT0RTKSB7XG4gICAgICBvcmlnaW5hbHNbbV0gPSBjb25bbV1cbiAgICAgIGNvblttXSA9IHRvRGVidWdcbiAgICB9XG4gICAgZm9yIChjb25zdCBtIG9mIENPTlNPTEVfU1RERVJSX01FVEhPRFMpIHtcbiAgICAgIG9yaWdpbmFsc1ttXSA9IGNvblttXVxuICAgICAgY29uW21dID0gdG9FcnJvclxuICAgIH1cbiAgICBvcmlnaW5hbHMuYXNzZXJ0ID0gY29uLmFzc2VydFxuICAgIGNvbi5hc3NlcnQgPSAoY29uZGl0aW9uOiB1bmtub3duLCAuLi5hcmdzOiB1bmtub3duW10pID0+IHtcbiAgICAgIGlmICghY29uZGl0aW9uKSB0b0Vycm9yKC4uLmFyZ3MpXG4gICAgfVxuICAgIHJldHVybiAoKSA9PiBPYmplY3QuYXNzaWduKGNvbiwgb3JpZ2luYWxzKVxuICB9XG5cbiAgLyoqXG4gICAqIEludGVyY2VwdCBwcm9jZXNzLnN0ZGVyci53cml0ZSBzbyBzdHJheSB3cml0ZXMgKGNvbmZpZy50cywgaG9va3MudHMsXG4gICAqIHRoaXJkLXBhcnR5IGRlcHMpIGRvbid0IGNvcnJ1cHQgdGhlIGFsdC1zY3JlZW4gYnVmZmVyLiBwYXRjaENvbnNvbGUgb25seVxuICAgKiBob29rcyBjb25zb2xlLiogbWV0aG9kcyDigJQgZGlyZWN0IHN0ZGVyciB3cml0ZXMgYnlwYXNzIGl0LCBsYW5kIGF0IHRoZVxuICAgKiBwYXJrZWQgY3Vyc29yLCBzY3JvbGwgdGhlIGFsdC1zY3JlZW4sIGFuZCBkZXN5bmMgZnJvbnRGcmFtZSBmcm9tIHRoZVxuICAgKiBwaHlzaWNhbCB0ZXJtaW5hbC4gTmV4dCBkaWZmIHdyaXRlcyBvbmx5IGNoYW5nZWQtaW4tUmVhY3QgY2VsbHMgYXRcbiAgICogYWJzb2x1dGUgY29vcmRzIOKGkiBpbnRlcmxlYXZlZCBnYXJiYWdlLlxuICAgKlxuICAgKiBTd2FsbG93cyB0aGUgd3JpdGUgKHJvdXRlcyB0ZXh0IHRvIHRoZSBkZWJ1ZyBsb2cpIGFuZCwgaW4gYWx0LXNjcmVlbixcbiAgICogZm9yY2VzIGEgZnVsbC1kYW1hZ2UgcmVwYWludCBhcyBhIGRlZmVuc2l2ZSByZWNvdmVyeS4gTm90IHBhdGNoaW5nXG4gICAqIHByb2Nlc3Muc3Rkb3V0IOKAlCBJbmsgaXRzZWxmIHdyaXRlcyB0aGVyZS5cbiAgICovXG4gIHByaXZhdGUgcGF0Y2hTdGRlcnIoKTogKCkgPT4gdm9pZCB7XG4gICAgY29uc3Qgc3RkZXJyID0gcHJvY2Vzcy5zdGRlcnJcbiAgICBjb25zdCBvcmlnaW5hbFdyaXRlID0gc3RkZXJyLndyaXRlXG4gICAgbGV0IHJlZW50ZXJlZCA9IGZhbHNlXG4gICAgY29uc3QgaW50ZXJjZXB0ID0gKFxuICAgICAgY2h1bms6IFVpbnQ4QXJyYXkgfCBzdHJpbmcsXG4gICAgICBlbmNvZGluZ09yQ2I/OiBCdWZmZXJFbmNvZGluZyB8ICgoZXJyPzogRXJyb3IpID0+IHZvaWQpLFxuICAgICAgY2I/OiAoZXJyPzogRXJyb3IpID0+IHZvaWQsXG4gICAgKTogYm9vbGVhbiA9PiB7XG4gICAgICBjb25zdCBjYWxsYmFjayA9IHR5cGVvZiBlbmNvZGluZ09yQ2IgPT09ICdmdW5jdGlvbicgPyBlbmNvZGluZ09yQ2IgOiBjYlxuICAgICAgLy8gUmVlbnRyYW5jeSBndWFyZDogbG9nRm9yRGVidWdnaW5nIOKGkiB3cml0ZVRvU3RkZXJyIOKGkiBoZXJlLiBQYXNzXG4gICAgICAvLyB0aHJvdWdoIHRvIHRoZSBvcmlnaW5hbCBzbyAtLWRlYnVnLXRvLXN0ZGVyciBzdGlsbCB3b3JrcyBhbmQgd2VcbiAgICAgIC8vIGRvbid0IHN0YWNrLW92ZXJmbG93LlxuICAgICAgaWYgKHJlZW50ZXJlZCkge1xuICAgICAgICBjb25zdCBlbmNvZGluZyA9XG4gICAgICAgICAgdHlwZW9mIGVuY29kaW5nT3JDYiA9PT0gJ3N0cmluZycgPyBlbmNvZGluZ09yQ2IgOiB1bmRlZmluZWRcbiAgICAgICAgcmV0dXJuIG9yaWdpbmFsV3JpdGUuY2FsbChzdGRlcnIsIGNodW5rLCBlbmNvZGluZywgY2FsbGJhY2spXG4gICAgICB9XG4gICAgICByZWVudGVyZWQgPSB0cnVlXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB0ZXh0ID1cbiAgICAgICAgICB0eXBlb2YgY2h1bmsgPT09ICdzdHJpbmcnXG4gICAgICAgICAgICA/IGNodW5rXG4gICAgICAgICAgICA6IEJ1ZmZlci5mcm9tKGNodW5rKS50b1N0cmluZygndXRmOCcpXG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgW3N0ZGVycl0gJHt0ZXh0fWAsIHsgbGV2ZWw6ICd3YXJuJyB9KVxuICAgICAgICBpZiAodGhpcy5hbHRTY3JlZW5BY3RpdmUgJiYgIXRoaXMuaXNVbm1vdW50ZWQgJiYgIXRoaXMuaXNQYXVzZWQpIHtcbiAgICAgICAgICB0aGlzLnByZXZGcmFtZUNvbnRhbWluYXRlZCA9IHRydWVcbiAgICAgICAgICB0aGlzLnNjaGVkdWxlUmVuZGVyKClcbiAgICAgICAgfVxuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgcmVlbnRlcmVkID0gZmFsc2VcbiAgICAgICAgY2FsbGJhY2s/LigpXG4gICAgICB9XG4gICAgICByZXR1cm4gdHJ1ZVxuICAgIH1cbiAgICBzdGRlcnIud3JpdGUgPSBpbnRlcmNlcHRcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgaWYgKHN0ZGVyci53cml0ZSA9PT0gaW50ZXJjZXB0KSB7XG4gICAgICAgIHN0ZGVyci53cml0ZSA9IG9yaWdpbmFsV3JpdGVcbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBEaXNjYXJkIHBlbmRpbmcgc3RkaW4gYnl0ZXMgc28gaW4tZmxpZ2h0IGVzY2FwZSBzZXF1ZW5jZXMgKG1vdXNlIHRyYWNraW5nXG4gKiByZXBvcnRzLCBicmFja2V0ZWQtcGFzdGUgbWFya2VycykgZG9uJ3QgbGVhayB0byB0aGUgc2hlbGwgYWZ0ZXIgZXhpdC5cbiAqXG4gKiBUd28gbGF5ZXJzIG9mIHRyaWNraW5lc3M6XG4gKlxuICogMS4gc2V0UmF3TW9kZSBpcyB0ZXJtaW9zLCBub3QgZmNudGwg4oCUIHRoZSBzdGRpbiBmZCBzdGF5cyBibG9ja2luZywgc29cbiAqICAgIHJlYWRTeW5jIG9uIGl0IHdvdWxkIGhhbmcgZm9yZXZlci4gTm9kZSBkb2Vzbid0IGV4cG9zZSBmY250bCwgc28gd2VcbiAqICAgIG9wZW4gL2Rldi90dHkgZnJlc2ggd2l0aCBPX05PTkJMT0NLIChhbGwgZmRzIHRvIHRoZSBjb250cm9sbGluZ1xuICogICAgdGVybWluYWwgc2hhcmUgb25lIGxpbmUtZGlzY2lwbGluZSBpbnB1dCBxdWV1ZSkuXG4gKlxuICogMi4gQnkgdGhlIHRpbWUgZm9yY2VFeGl0IGNhbGxzIHRoaXMsIGRldGFjaEZvclNodXRkb3duIGhhcyBhbHJlYWR5IHB1dFxuICogICAgdGhlIFRUWSBiYWNrIGluIGNvb2tlZCAoY2Fub25pY2FsKSBtb2RlLiBDYW5vbmljYWwgbW9kZSBsaW5lLWJ1ZmZlcnNcbiAqICAgIGlucHV0IHVudGlsIG5ld2xpbmUsIHNvIE9fTk9OQkxPQ0sgcmVhZHMgcmV0dXJuIEVBR0FJTiBldmVuIHdoZW5cbiAqICAgIG1vdXNlIGJ5dGVzIGFyZSBzaXR0aW5nIGluIHRoZSBidWZmZXIuIFdlIGJyaWVmbHkgcmUtZW50ZXIgcmF3IG1vZGVcbiAqICAgIHNvIHJlYWRzIHJldHVybiBhbnkgYXZhaWxhYmxlIGJ5dGVzLCB0aGVuIHJlc3RvcmUgY29va2VkIG1vZGUuXG4gKlxuICogU2FmZSB0byBjYWxsIG11bHRpcGxlIHRpbWVzLiBDYWxsIGFzIExBVEUgYXMgcG9zc2libGUgaW4gdGhlIGV4aXQgcGF0aDpcbiAqIERJU0FCTEVfTU9VU0VfVFJBQ0tJTkcgaGFzIHRlcm1pbmFsIHJvdW5kLXRyaXAgbGF0ZW5jeSwgc28gZXZlbnRzIGNhblxuICogYXJyaXZlIGZvciBhIGZldyBtcyBhZnRlciBpdCdzIHdyaXR0ZW4uXG4gKi9cbi8qIGVzbGludC1kaXNhYmxlIGN1c3RvbS1ydWxlcy9uby1zeW5jLWZzIC0tIG11c3QgYmUgc3luYzsgY2FsbGVkIGZyb20gc2lnbmFsIGhhbmRsZXIgLyB1bm1vdW50ICovXG5leHBvcnQgZnVuY3Rpb24gZHJhaW5TdGRpbihzdGRpbjogTm9kZUpTLlJlYWRTdHJlYW0gPSBwcm9jZXNzLnN0ZGluKTogdm9pZCB7XG4gIGlmICghc3RkaW4uaXNUVFkpIHJldHVyblxuICAvLyBEcmFpbiBOb2RlJ3Mgc3RyZWFtIGJ1ZmZlciAoYnl0ZXMgbGlidXYgYWxyZWFkeSBwdWxsZWQgaW4pLiByZWFkKClcbiAgLy8gcmV0dXJucyBudWxsIHdoZW4gZW1wdHkg4oCUIG5ldmVyIGJsb2Nrcy5cbiAgdHJ5IHtcbiAgICB3aGlsZSAoc3RkaW4ucmVhZCgpICE9PSBudWxsKSB7XG4gICAgICAvKiBkaXNjYXJkICovXG4gICAgfVxuICB9IGNhdGNoIHtcbiAgICAvKiBzdHJlYW0gbWF5IGJlIGRlc3Ryb3llZCAqL1xuICB9XG4gIC8vIE5vIC9kZXYvdHR5IG9uIFdpbmRvd3M7IENPTklOJCBkb2Vzbid0IHN1cHBvcnQgT19OT05CTE9DSyBzZW1hbnRpY3MuXG4gIC8vIFdpbmRvd3MgVGVybWluYWwgYWxzbyBkb2Vzbid0IGJ1ZmZlciBtb3VzZSByZXBvcnRzIHRoZSBzYW1lIHdheS5cbiAgaWYgKHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicpIHJldHVyblxuICAvLyB0ZXJtaW9zIGlzIHBlci1kZXZpY2U6IGZsaXAgc3RkaW4gdG8gcmF3IHNvIGNhbm9uaWNhbC1tb2RlIGxpbmVcbiAgLy8gYnVmZmVyaW5nIGRvZXNuJ3QgaGlkZSBwYXJ0aWFsIGlucHV0IGZyb20gdGhlIG5vbi1ibG9ja2luZyByZWFkLlxuICAvLyBSZXN0b3JlZCBpbiB0aGUgZmluYWxseSBibG9jay5cbiAgY29uc3QgdHR5ID0gc3RkaW4gYXMgTm9kZUpTLlJlYWRTdHJlYW0gJiB7XG4gICAgaXNSYXc/OiBib29sZWFuXG4gICAgc2V0UmF3TW9kZT86IChyYXc6IGJvb2xlYW4pID0+IHZvaWRcbiAgfVxuICBjb25zdCB3YXNSYXcgPSB0dHkuaXNSYXcgPT09IHRydWVcbiAgLy8gRHJhaW4gdGhlIGtlcm5lbCBUVFkgYnVmZmVyIHZpYSBhIGZyZXNoIE9fTk9OQkxPQ0sgZmQuIEJvdW5kZWQgYXQgNjRcbiAgLy8gcmVhZHMgKDY0S0IpIOKAlCBhIHJlYWwgbW91c2UgYnVyc3QgaXMgYSBmZXcgaHVuZHJlZCBieXRlczsgdGhlIGNhcFxuICAvLyBndWFyZHMgYWdhaW5zdCBhIHRlcm1pbmFsIHRoYXQgaWdub3JlcyBPX05PTkJMT0NLLlxuICBsZXQgZmQgPSAtMVxuICB0cnkge1xuICAgIC8vIHNldFJhd01vZGUgaW5zaWRlIHRyeTogb24gcmV2b2tlZCBUVFkgKFNJR0hVUC9TU0ggZGlzY29ubmVjdCkgdGhlXG4gICAgLy8gaW9jdGwgdGhyb3dzIEVCQURGIOKAlCBzYW1lIHJlY292ZXJ5IHBhdGggYXMgb3BlblN5bmMvcmVhZFN5bmMgYmVsb3cuXG4gICAgaWYgKCF3YXNSYXcpIHR0eS5zZXRSYXdNb2RlPy4odHJ1ZSlcbiAgICBmZCA9IG9wZW5TeW5jKCcvZGV2L3R0eScsIGZzQ29uc3RhbnRzLk9fUkRPTkxZIHwgZnNDb25zdGFudHMuT19OT05CTE9DSylcbiAgICBjb25zdCBidWYgPSBCdWZmZXIuYWxsb2MoMTAyNClcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IDY0OyBpKyspIHtcbiAgICAgIGlmIChyZWFkU3luYyhmZCwgYnVmLCAwLCBidWYubGVuZ3RoLCBudWxsKSA8PSAwKSBicmVha1xuICAgIH1cbiAgfSBjYXRjaCB7XG4gICAgLy8gRUFHQUlOIChidWZmZXIgZW1wdHkg4oCUIGV4cGVjdGVkKSwgRU5YSU8vRU5PRU5UIChubyBjb250cm9sbGluZyB0dHkpLFxuICAgIC8vIEVCQURGL0VJTyAoVFRZIHJldm9rZWQg4oCUIFNJR0hVUCwgU1NIIGRpc2Nvbm5lY3QpXG4gIH0gZmluYWxseSB7XG4gICAgaWYgKGZkID49IDApIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNsb3NlU3luYyhmZClcbiAgICAgIH0gY2F0Y2gge1xuICAgICAgICAvKiBpZ25vcmUgKi9cbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKCF3YXNSYXcpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHR0eS5zZXRSYXdNb2RlPy4oZmFsc2UpXG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgLyogVFRZIG1heSBiZSBnb25lICovXG4gICAgICB9XG4gICAgfVxuICB9XG59XG4vKiBlc2xpbnQtZW5hYmxlIGN1c3RvbS1ydWxlcy9uby1zeW5jLWZzICovXG5cbmNvbnN0IENPTlNPTEVfU1RET1VUX01FVEhPRFMgPSBbXG4gICdsb2cnLFxuICAnaW5mbycsXG4gICdkZWJ1ZycsXG4gICdkaXInLFxuICAnZGlyeG1sJyxcbiAgJ2NvdW50JyxcbiAgJ2NvdW50UmVzZXQnLFxuICAnZ3JvdXAnLFxuICAnZ3JvdXBDb2xsYXBzZWQnLFxuICAnZ3JvdXBFbmQnLFxuICAndGFibGUnLFxuICAndGltZScsXG4gICd0aW1lRW5kJyxcbiAgJ3RpbWVMb2cnLFxuXSBhcyBjb25zdFxuY29uc3QgQ09OU09MRV9TVERFUlJfTUVUSE9EUyA9IFsnd2FybicsICdlcnJvcicsICd0cmFjZSddIGFzIGNvbnN0XG4iXSwibWFwcGluZ3MiOiJBQUFBLE9BQU9BLFFBQVEsTUFBTSxXQUFXO0FBQ2hDLFNBQ0VDLFNBQVMsRUFDVEMsU0FBUyxJQUFJQyxXQUFXLEVBQ3hCQyxRQUFRLEVBQ1JDLFFBQVEsRUFDUkMsU0FBUyxRQUNKLElBQUk7QUFDWCxPQUFPQyxJQUFJLE1BQU0sbUJBQW1CO0FBQ3BDLE9BQU9DLFFBQVEsTUFBTSx1QkFBdUI7QUFDNUMsT0FBT0MsS0FBSyxJQUFJLEtBQUtDLFNBQVMsUUFBUSxPQUFPO0FBQzdDLGNBQWNDLFNBQVMsUUFBUSxrQkFBa0I7QUFDakQsU0FBU0MsY0FBYyxRQUFRLCtCQUErQjtBQUM5RCxTQUFTQyxNQUFNLFFBQVEsYUFBYTtBQUNwQyxTQUFTQyxvQkFBb0IsUUFBUSx3QkFBd0I7QUFDN0QsU0FBU0MsZUFBZSxRQUFRLG9DQUFvQztBQUNwRSxTQUFTQyxlQUFlLFFBQVEsb0JBQW9CO0FBQ3BELFNBQVNDLFFBQVEsUUFBUSxrQkFBa0I7QUFDM0MsU0FBU0MsTUFBTSxRQUFRLE1BQU07QUFDN0Is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QUMsRUFBRSxPQUFPO0lBQ2ZDLFVBQVUsQ0FBQyxFQUFFLENBQUNPLEdBQUcsRUFBRSxPQUFPLEVBQUUsR0FBRyxJQUFJO0VBQ3JDLENBQUM7RUFDRCxNQUFNNkgsTUFBTSxHQUFHRCxHQUFHLENBQUNwSSxLQUFLLEtBQUssSUFBSTtFQUNqQztFQUNBO0VBQ0E7RUFDQSxJQUFJc0ksRUFBRSxHQUFHLENBQUMsQ0FBQztFQUNYLElBQUk7SUFDRjtJQUNBO0lBQ0EsSUFBSSxDQUFDRCxNQUFNLEVBQUVELEdBQUcsQ0FBQ25JLFVBQVUsR0FBRyxJQUFJLENBQUM7SUFDbkNxSSxFQUFFLEdBQUczYyxRQUFRLENBQUMsVUFBVSxFQUFFRCxXQUFXLENBQUM2YyxRQUFRLEdBQUc3YyxXQUFXLENBQUM4YyxVQUFVLENBQUM7SUFDeEUsTUFBTUMsR0FBRyxHQUFHVCxNQUFNLENBQUNVLEtBQUssQ0FBQyxJQUFJLENBQUM7SUFDOUIsS0FBSyxJQUFJQyxDQUFDLEdBQUcsQ0FBQyxFQUFFQSxDQUFDLEdBQUcsRUFBRSxFQUFFQSxDQUFDLEVBQUUsRUFBRTtNQUMzQixJQUFJL2MsUUFBUSxDQUFDMGMsRUFBRSxFQUFFRyxHQUFHLEVBQUUsQ0FBQyxFQUFFQSxHQUFHLENBQUM5TCxNQUFNLEVBQUUsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFO0lBQ25EO0VBQ0YsQ0FBQyxDQUFDLE1BQU07SUFDTjtJQUNBO0VBQUEsQ0FDRCxTQUFTO0lBQ1IsSUFBSTJMLEVBQUUsSUFBSSxDQUFDLEVBQUU7TUFDWCxJQUFJO1FBQ0Y5YyxTQUFTLENBQUM4YyxFQUFFLENBQUM7TUFDZixDQUFDLENBQUMsTUFBTTtRQUNOO01BQUE7SUFFSjtJQUNBLElBQUksQ0FBQ0QsTUFBTSxFQUFFO01BQ1gsSUFBSTtRQUNGRCxHQUFHLENBQUNuSSxVQUFVLEdBQUcsS0FBSyxDQUFDO01BQ3pCLENBQUMsQ0FBQyxNQUFNO1FBQ047TUFBQTtJQUVKO0VBQ0Y7QUFDRjtBQUNBOztBQUVBLE1BQU0rRyxzQkFBc0IsR0FBRyxDQUM3QixLQUFLLEVBQ0wsTUFBTSxFQUNOLE9BQU8sRUFDUCxLQUFLLEVBQ0wsUUFBUSxFQUNSLE9BQU8sRUFDUCxZQUFZLEVBQ1osT0FBTyxFQUNQLGdCQUFnQixFQUNoQixVQUFVLEVBQ1YsT0FBTyxFQUNQLE1BQU0sRUFDTixTQUFTLEVBQ1QsU0FBUyxDQUNWLElBQUl4VSxLQUFLO0FBQ1YsTUFBTXlVLHNCQUFzQixHQUFHLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLENBQUMsSUFBSXpVLEtBQUsiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/ink/instances.ts b/ink/instances.ts new file mode 100644 index 0000000..389384a --- /dev/null +++ b/ink/instances.ts @@ -0,0 +1,10 @@ +// Store all instances of Ink (instance.js) to ensure that consecutive render() calls +// use the same instance of Ink and don't create a new one +// +// This map has to be stored in a separate file, because render.js creates instances, +// but instance.js should delete itself from the map on unmount + +import type Ink from './ink.js' + +const instances = new Map() +export default instances diff --git a/ink/layout/engine.ts b/ink/layout/engine.ts new file mode 100644 index 0000000..38f6dcb --- /dev/null +++ b/ink/layout/engine.ts @@ -0,0 +1,6 @@ +import type { LayoutNode } from './node.js' +import { createYogaLayoutNode } from './yoga.js' + +export function createLayoutNode(): LayoutNode { + return createYogaLayoutNode() +} diff --git a/ink/layout/geometry.ts b/ink/layout/geometry.ts new file mode 100644 index 0000000..e586f8e --- /dev/null +++ b/ink/layout/geometry.ts @@ -0,0 +1,97 @@ +export type Point = { + x: number + y: number +} + +export type Size = { + width: number + height: number +} + +export type Rectangle = Point & Size + +/** Edge insets (padding, margin, border) */ +export type Edges = { + top: number + right: number + bottom: number + left: number +} + +/** Create uniform edges */ +export function edges(all: number): Edges +export function edges(vertical: number, horizontal: number): Edges +export function edges( + top: number, + right: number, + bottom: number, + left: number, +): Edges +export function edges(a: number, b?: number, c?: number, d?: number): Edges { + if (b === undefined) { + return { top: a, right: a, bottom: a, left: a } + } + if (c === undefined) { + return { top: a, right: b, bottom: a, left: b } + } + return { top: a, right: b, bottom: c, left: d! } +} + +/** Add two edge values */ +export function addEdges(a: Edges, b: Edges): Edges { + return { + top: a.top + b.top, + right: a.right + b.right, + bottom: a.bottom + b.bottom, + left: a.left + b.left, + } +} + +/** Zero edges constant */ +export const ZERO_EDGES: Edges = { top: 0, right: 0, bottom: 0, left: 0 } + +/** Convert partial edges to full edges with defaults */ +export function resolveEdges(partial?: Partial): Edges { + return { + top: partial?.top ?? 0, + right: partial?.right ?? 0, + bottom: partial?.bottom ?? 0, + left: partial?.left ?? 0, + } +} + +export function unionRect(a: Rectangle, b: Rectangle): Rectangle { + const minX = Math.min(a.x, b.x) + const minY = Math.min(a.y, b.y) + const maxX = Math.max(a.x + a.width, b.x + b.width) + const maxY = Math.max(a.y + a.height, b.y + b.height) + return { x: minX, y: minY, width: maxX - minX, height: maxY - minY } +} + +export function clampRect(rect: Rectangle, size: Size): Rectangle { + const minX = Math.max(0, rect.x) + const minY = Math.max(0, rect.y) + const maxX = Math.min(size.width - 1, rect.x + rect.width - 1) + const maxY = Math.min(size.height - 1, rect.y + rect.height - 1) + return { + x: minX, + y: minY, + width: Math.max(0, maxX - minX + 1), + height: Math.max(0, maxY - minY + 1), + } +} + +export function withinBounds(size: Size, point: Point): boolean { + return ( + point.x >= 0 && + point.y >= 0 && + point.x < size.width && + point.y < size.height + ) +} + +export function clamp(value: number, min?: number, max?: number): number { + if (min !== undefined && value < min) return min + if (max !== undefined && value > max) return max + return value +} diff --git a/ink/layout/node.ts b/ink/layout/node.ts new file mode 100644 index 0000000..5ebf177 --- /dev/null +++ b/ink/layout/node.ts @@ -0,0 +1,152 @@ +// -- +// Adapter interface for the layout engine (Yoga) + +export const LayoutEdge = { + All: 'all', + Horizontal: 'horizontal', + Vertical: 'vertical', + Left: 'left', + Right: 'right', + Top: 'top', + Bottom: 'bottom', + Start: 'start', + End: 'end', +} as const +export type LayoutEdge = (typeof LayoutEdge)[keyof typeof LayoutEdge] + +export const LayoutGutter = { + All: 'all', + Column: 'column', + Row: 'row', +} as const +export type LayoutGutter = (typeof LayoutGutter)[keyof typeof LayoutGutter] + +export const LayoutDisplay = { + Flex: 'flex', + None: 'none', +} as const +export type LayoutDisplay = (typeof LayoutDisplay)[keyof typeof LayoutDisplay] + +export const LayoutFlexDirection = { + Row: 'row', + RowReverse: 'row-reverse', + Column: 'column', + ColumnReverse: 'column-reverse', +} as const +export type LayoutFlexDirection = + (typeof LayoutFlexDirection)[keyof typeof LayoutFlexDirection] + +export const LayoutAlign = { + Auto: 'auto', + Stretch: 'stretch', + FlexStart: 'flex-start', + Center: 'center', + FlexEnd: 'flex-end', +} as const +export type LayoutAlign = (typeof LayoutAlign)[keyof typeof LayoutAlign] + +export const LayoutJustify = { + FlexStart: 'flex-start', + Center: 'center', + FlexEnd: 'flex-end', + SpaceBetween: 'space-between', + SpaceAround: 'space-around', + SpaceEvenly: 'space-evenly', +} as const +export type LayoutJustify = (typeof LayoutJustify)[keyof typeof LayoutJustify] + +export const LayoutWrap = { + NoWrap: 'nowrap', + Wrap: 'wrap', + WrapReverse: 'wrap-reverse', +} as const +export type LayoutWrap = (typeof LayoutWrap)[keyof typeof LayoutWrap] + +export const LayoutPositionType = { + Relative: 'relative', + Absolute: 'absolute', +} as const +export type LayoutPositionType = + (typeof LayoutPositionType)[keyof typeof LayoutPositionType] + +export const LayoutOverflow = { + Visible: 'visible', + Hidden: 'hidden', + Scroll: 'scroll', +} as const +export type LayoutOverflow = + (typeof LayoutOverflow)[keyof typeof LayoutOverflow] + +export type LayoutMeasureFunc = ( + width: number, + widthMode: LayoutMeasureMode, +) => { width: number; height: number } + +export const LayoutMeasureMode = { + Undefined: 'undefined', + Exactly: 'exactly', + AtMost: 'at-most', +} as const +export type LayoutMeasureMode = + (typeof LayoutMeasureMode)[keyof typeof LayoutMeasureMode] + +export type LayoutNode = { + // Tree + insertChild(child: LayoutNode, index: number): void + removeChild(child: LayoutNode): void + getChildCount(): number + getParent(): LayoutNode | null + + // Layout computation + calculateLayout(width?: number, height?: number): void + setMeasureFunc(fn: LayoutMeasureFunc): void + unsetMeasureFunc(): void + markDirty(): void + + // Layout reading (post-layout) + getComputedLeft(): number + getComputedTop(): number + getComputedWidth(): number + getComputedHeight(): number + getComputedBorder(edge: LayoutEdge): number + getComputedPadding(edge: LayoutEdge): number + + // Style setters + setWidth(value: number): void + setWidthPercent(value: number): void + setWidthAuto(): void + setHeight(value: number): void + setHeightPercent(value: number): void + setHeightAuto(): void + setMinWidth(value: number): void + setMinWidthPercent(value: number): void + setMinHeight(value: number): void + setMinHeightPercent(value: number): void + setMaxWidth(value: number): void + setMaxWidthPercent(value: number): void + setMaxHeight(value: number): void + setMaxHeightPercent(value: number): void + setFlexDirection(dir: LayoutFlexDirection): void + setFlexGrow(value: number): void + setFlexShrink(value: number): void + setFlexBasis(value: number): void + setFlexBasisPercent(value: number): void + setFlexWrap(wrap: LayoutWrap): void + setAlignItems(align: LayoutAlign): void + setAlignSelf(align: LayoutAlign): void + setJustifyContent(justify: LayoutJustify): void + setDisplay(display: LayoutDisplay): void + getDisplay(): LayoutDisplay + setPositionType(type: LayoutPositionType): void + setPosition(edge: LayoutEdge, value: number): void + setPositionPercent(edge: LayoutEdge, value: number): void + setOverflow(overflow: LayoutOverflow): void + setMargin(edge: LayoutEdge, value: number): void + setPadding(edge: LayoutEdge, value: number): void + setBorder(edge: LayoutEdge, value: number): void + setGap(gutter: LayoutGutter, value: number): void + + // Lifecycle + free(): void + freeRecursive(): void +} diff --git a/ink/layout/yoga.ts b/ink/layout/yoga.ts new file mode 100644 index 0000000..58f2646 --- /dev/null +++ b/ink/layout/yoga.ts @@ -0,0 +1,308 @@ +import Yoga, { + Align, + Direction, + Display, + Edge, + FlexDirection, + Gutter, + Justify, + MeasureMode, + Overflow, + PositionType, + Wrap, + type Node as YogaNode, +} from 'src/native-ts/yoga-layout/index.js' +import { + type LayoutAlign, + LayoutDisplay, + type LayoutEdge, + type LayoutFlexDirection, + type LayoutGutter, + type LayoutJustify, + type LayoutMeasureFunc, + LayoutMeasureMode, + type LayoutNode, + type LayoutOverflow, + type LayoutPositionType, + type LayoutWrap, +} from './node.js' + +// -- +// Edge/Gutter mapping + +const EDGE_MAP: Record = { + all: Edge.All, + horizontal: Edge.Horizontal, + vertical: Edge.Vertical, + left: Edge.Left, + right: Edge.Right, + top: Edge.Top, + bottom: Edge.Bottom, + start: Edge.Start, + end: Edge.End, +} + +const GUTTER_MAP: Record = { + all: Gutter.All, + column: Gutter.Column, + row: Gutter.Row, +} + +// -- +// Yoga adapter + +export class YogaLayoutNode implements LayoutNode { + readonly yoga: YogaNode + + constructor(yoga: YogaNode) { + this.yoga = yoga + } + + // Tree + + insertChild(child: LayoutNode, index: number): void { + this.yoga.insertChild((child as YogaLayoutNode).yoga, index) + } + + removeChild(child: LayoutNode): void { + this.yoga.removeChild((child as YogaLayoutNode).yoga) + } + + getChildCount(): number { + return this.yoga.getChildCount() + } + + getParent(): LayoutNode | null { + const p = this.yoga.getParent() + return p ? new YogaLayoutNode(p) : null + } + + // Layout + + calculateLayout(width?: number, _height?: number): void { + this.yoga.calculateLayout(width, undefined, Direction.LTR) + } + + setMeasureFunc(fn: LayoutMeasureFunc): void { + this.yoga.setMeasureFunc((w, wMode) => { + const mode = + wMode === MeasureMode.Exactly + ? LayoutMeasureMode.Exactly + : wMode === MeasureMode.AtMost + ? LayoutMeasureMode.AtMost + : LayoutMeasureMode.Undefined + return fn(w, mode) + }) + } + + unsetMeasureFunc(): void { + this.yoga.unsetMeasureFunc() + } + + markDirty(): void { + this.yoga.markDirty() + } + + // Computed layout + + getComputedLeft(): number { + return this.yoga.getComputedLeft() + } + + getComputedTop(): number { + return this.yoga.getComputedTop() + } + + getComputedWidth(): number { + return this.yoga.getComputedWidth() + } + + getComputedHeight(): number { + return this.yoga.getComputedHeight() + } + + getComputedBorder(edge: LayoutEdge): number { + return this.yoga.getComputedBorder(EDGE_MAP[edge]!) + } + + getComputedPadding(edge: LayoutEdge): number { + return this.yoga.getComputedPadding(EDGE_MAP[edge]!) + } + + // Style setters + + setWidth(value: number): void { + this.yoga.setWidth(value) + } + setWidthPercent(value: number): void { + this.yoga.setWidthPercent(value) + } + setWidthAuto(): void { + this.yoga.setWidthAuto() + } + setHeight(value: number): void { + this.yoga.setHeight(value) + } + setHeightPercent(value: number): void { + this.yoga.setHeightPercent(value) + } + setHeightAuto(): void { + this.yoga.setHeightAuto() + } + setMinWidth(value: number): void { + this.yoga.setMinWidth(value) + } + setMinWidthPercent(value: number): void { + this.yoga.setMinWidthPercent(value) + } + setMinHeight(value: number): void { + this.yoga.setMinHeight(value) + } + setMinHeightPercent(value: number): void { + this.yoga.setMinHeightPercent(value) + } + setMaxWidth(value: number): void { + this.yoga.setMaxWidth(value) + } + setMaxWidthPercent(value: number): void { + this.yoga.setMaxWidthPercent(value) + } + setMaxHeight(value: number): void { + this.yoga.setMaxHeight(value) + } + setMaxHeightPercent(value: number): void { + this.yoga.setMaxHeightPercent(value) + } + + setFlexDirection(dir: LayoutFlexDirection): void { + const map: Record = { + row: FlexDirection.Row, + 'row-reverse': FlexDirection.RowReverse, + column: FlexDirection.Column, + 'column-reverse': FlexDirection.ColumnReverse, + } + this.yoga.setFlexDirection(map[dir]!) + } + + setFlexGrow(value: number): void { + this.yoga.setFlexGrow(value) + } + setFlexShrink(value: number): void { + this.yoga.setFlexShrink(value) + } + setFlexBasis(value: number): void { + this.yoga.setFlexBasis(value) + } + setFlexBasisPercent(value: number): void { + this.yoga.setFlexBasisPercent(value) + } + + setFlexWrap(wrap: LayoutWrap): void { + const map: Record = { + nowrap: Wrap.NoWrap, + wrap: Wrap.Wrap, + 'wrap-reverse': Wrap.WrapReverse, + } + this.yoga.setFlexWrap(map[wrap]!) + } + + setAlignItems(align: LayoutAlign): void { + const map: Record = { + auto: Align.Auto, + stretch: Align.Stretch, + 'flex-start': Align.FlexStart, + center: Align.Center, + 'flex-end': Align.FlexEnd, + } + this.yoga.setAlignItems(map[align]!) + } + + setAlignSelf(align: LayoutAlign): void { + const map: Record = { + auto: Align.Auto, + stretch: Align.Stretch, + 'flex-start': Align.FlexStart, + center: Align.Center, + 'flex-end': Align.FlexEnd, + } + this.yoga.setAlignSelf(map[align]!) + } + + setJustifyContent(justify: LayoutJustify): void { + const map: Record = { + 'flex-start': Justify.FlexStart, + center: Justify.Center, + 'flex-end': Justify.FlexEnd, + 'space-between': Justify.SpaceBetween, + 'space-around': Justify.SpaceAround, + 'space-evenly': Justify.SpaceEvenly, + } + this.yoga.setJustifyContent(map[justify]!) + } + + setDisplay(display: LayoutDisplay): void { + this.yoga.setDisplay(display === 'flex' ? Display.Flex : Display.None) + } + + getDisplay(): LayoutDisplay { + return this.yoga.getDisplay() === Display.None + ? LayoutDisplay.None + : LayoutDisplay.Flex + } + + setPositionType(type: LayoutPositionType): void { + this.yoga.setPositionType( + type === 'absolute' ? PositionType.Absolute : PositionType.Relative, + ) + } + + setPosition(edge: LayoutEdge, value: number): void { + this.yoga.setPosition(EDGE_MAP[edge]!, value) + } + + setPositionPercent(edge: LayoutEdge, value: number): void { + this.yoga.setPositionPercent(EDGE_MAP[edge]!, value) + } + + setOverflow(overflow: LayoutOverflow): void { + const map: Record = { + visible: Overflow.Visible, + hidden: Overflow.Hidden, + scroll: Overflow.Scroll, + } + this.yoga.setOverflow(map[overflow]!) + } + + setMargin(edge: LayoutEdge, value: number): void { + this.yoga.setMargin(EDGE_MAP[edge]!, value) + } + setPadding(edge: LayoutEdge, value: number): void { + this.yoga.setPadding(EDGE_MAP[edge]!, value) + } + setBorder(edge: LayoutEdge, value: number): void { + this.yoga.setBorder(EDGE_MAP[edge]!, value) + } + setGap(gutter: LayoutGutter, value: number): void { + this.yoga.setGap(GUTTER_MAP[gutter]!, value) + } + + // Lifecycle + + free(): void { + this.yoga.free() + } + freeRecursive(): void { + this.yoga.freeRecursive() + } +} + +// -- +// Instance management +// +// The TS yoga-layout port is synchronous — no WASM loading, no linear memory +// growth, so no preload/swap/reset machinery is needed. The Yoga instance is +// just a plain JS object available at import time. + +export function createYogaLayoutNode(): LayoutNode { + return new YogaLayoutNode(Yoga.Node.create()) +} diff --git a/ink/line-width-cache.ts b/ink/line-width-cache.ts new file mode 100644 index 0000000..d7d503b --- /dev/null +++ b/ink/line-width-cache.ts @@ -0,0 +1,24 @@ +import { stringWidth } from './stringWidth.js' + +// During streaming, text grows but completed lines are immutable. +// Caching stringWidth per-line avoids re-measuring hundreds of +// unchanged lines on every token (~50x reduction in stringWidth calls). +const cache = new Map() + +const MAX_CACHE_SIZE = 4096 + +export function lineWidth(line: string): number { + const cached = cache.get(line) + if (cached !== undefined) return cached + + const width = stringWidth(line) + + // Evict when cache grows too large (e.g. after many different responses). + // Simple full-clear is fine — the cache repopulates in one frame. + if (cache.size >= MAX_CACHE_SIZE) { + cache.clear() + } + + cache.set(line, width) + return width +} diff --git a/ink/log-update.ts b/ink/log-update.ts new file mode 100644 index 0000000..4434b94 --- /dev/null +++ b/ink/log-update.ts @@ -0,0 +1,773 @@ +import { + type AnsiCode, + ansiCodesToString, + diffAnsiCodes, +} from '@alcalzone/ansi-tokenize' +import { logForDebugging } from '../utils/debug.js' +import type { Diff, FlickerReason, Frame } from './frame.js' +import type { Point } from './layout/geometry.js' +import { + type Cell, + CellWidth, + cellAt, + charInCellAt, + diffEach, + type Hyperlink, + isEmptyCellAt, + type Screen, + type StylePool, + shiftRows, + visibleCellAtIndex, +} from './screen.js' +import { + CURSOR_HOME, + scrollDown as csiScrollDown, + scrollUp as csiScrollUp, + RESET_SCROLL_REGION, + setScrollRegion, +} from './termio/csi.js' +import { LINK_END, link as oscLink } from './termio/osc.js' + +type State = { + previousOutput: string +} + +type Options = { + isTTY: boolean + stylePool: StylePool +} + +const CARRIAGE_RETURN = { type: 'carriageReturn' } as const +const NEWLINE = { type: 'stdout', content: '\n' } as const + +export class LogUpdate { + private state: State + + constructor(private readonly options: Options) { + this.state = { + previousOutput: '', + } + } + + renderPreviousOutput_DEPRECATED(prevFrame: Frame): Diff { + if (!this.options.isTTY) { + // Non-TTY output is no longer supported (string output was removed) + return [NEWLINE] + } + return this.getRenderOpsForDone(prevFrame) + } + + // Called when process resumes from suspension (SIGCONT) to prevent clobbering terminal content + reset(): void { + this.state.previousOutput = '' + } + + private renderFullFrame(frame: Frame): Diff { + const { screen } = frame + const lines: string[] = [] + let currentStyles: AnsiCode[] = [] + let currentHyperlink: Hyperlink = undefined + for (let y = 0; y < screen.height; y++) { + let line = '' + for (let x = 0; x < screen.width; x++) { + const cell = cellAt(screen, x, y) + if (cell && cell.width !== CellWidth.SpacerTail) { + // Handle hyperlink transitions + if (cell.hyperlink !== currentHyperlink) { + if (currentHyperlink !== undefined) { + line += LINK_END + } + if (cell.hyperlink !== undefined) { + line += oscLink(cell.hyperlink) + } + currentHyperlink = cell.hyperlink + } + const cellStyles = this.options.stylePool.get(cell.styleId) + const styleDiff = diffAnsiCodes(currentStyles, cellStyles) + if (styleDiff.length > 0) { + line += ansiCodesToString(styleDiff) + currentStyles = cellStyles + } + line += cell.char + } + } + // Close any open hyperlink before resetting styles + if (currentHyperlink !== undefined) { + line += LINK_END + currentHyperlink = undefined + } + // Reset styles at end of line so trimEnd doesn't leave dangling codes + const resetCodes = diffAnsiCodes(currentStyles, []) + if (resetCodes.length > 0) { + line += ansiCodesToString(resetCodes) + currentStyles = [] + } + lines.push(line.trimEnd()) + } + + if (lines.length === 0) { + return [] + } + return [{ type: 'stdout', content: lines.join('\n') }] + } + + private getRenderOpsForDone(prev: Frame): Diff { + this.state.previousOutput = '' + + if (!prev.cursor.visible) { + return [{ type: 'cursorShow' }] + } + return [] + } + + render( + prev: Frame, + next: Frame, + altScreen = false, + decstbmSafe = true, + ): Diff { + if (!this.options.isTTY) { + return this.renderFullFrame(next) + } + + const startTime = performance.now() + const stylePool = this.options.stylePool + + // Since we assume the cursor is at the bottom on the screen, we only need + // to clear when the viewport gets shorter (i.e. the cursor position drifts) + // or when it gets thinner (and text wraps). We _could_ figure out how to + // not reset here but that would involve predicting the current layout + // _after_ the viewport change which means calcuating text wrapping. + // Resizing is a rare enough event that it's not practically a big issue. + if ( + next.viewport.height < prev.viewport.height || + (prev.viewport.width !== 0 && next.viewport.width !== prev.viewport.width) + ) { + return fullResetSequence_CAUSES_FLICKER(next, 'resize', stylePool) + } + + // DECSTBM scroll optimization: when a ScrollBox's scrollTop changed, + // shift content with a hardware scroll (CSI top;bot r + CSI n S/T) + // instead of rewriting the whole scroll region. The shiftRows on + // prev.screen simulates the shift so the diff loop below naturally + // finds only the rows that scrolled IN as diffs. prev.screen is + // about to become backFrame (reused next render) so mutation is safe. + // CURSOR_HOME after RESET_SCROLL_REGION is defensive — DECSTBM reset + // homes cursor per spec but terminal implementations vary. + // + // decstbmSafe: caller passes false when the DECSTBM→diff sequence + // can't be made atomic (no DEC 2026 / BSU/ESU). Without atomicity the + // outer terminal renders the intermediate state — region scrolled, + // edge rows not yet painted — a visible vertical jump on every frame + // where scrollTop moves. Falling through to the diff loop writes all + // shifted rows: more bytes, no intermediate state. next.screen from + // render-node-to-output's blit+shift is correct either way. + let scrollPatch: Diff = [] + if (altScreen && next.scrollHint && decstbmSafe) { + const { top, bottom, delta } = next.scrollHint + if ( + top >= 0 && + bottom < prev.screen.height && + bottom < next.screen.height + ) { + shiftRows(prev.screen, top, bottom, delta) + scrollPatch = [ + { + type: 'stdout', + content: + setScrollRegion(top + 1, bottom + 1) + + (delta > 0 ? csiScrollUp(delta) : csiScrollDown(-delta)) + + RESET_SCROLL_REGION + + CURSOR_HOME, + }, + ] + } + } + + // We have to use purely relative operations to manipulate the cursor since + // we don't know its starting point. + // + // When content height >= viewport height AND cursor is at the bottom, + // the cursor restore at the end of the previous frame caused terminal scroll. + // viewportY tells us how many rows are in scrollback from content overflow. + // Additionally, the cursor-restore scroll pushes 1 more row into scrollback. + // We need fullReset if any changes are to rows that are now in scrollback. + // + // This early full-reset check only applies in "steady state" (not growing). + // For growing, the viewportY calculation below (with cursorRestoreScroll) + // catches unreachable scrollback rows in the diff loop instead. + const cursorAtBottom = prev.cursor.y >= prev.screen.height + const isGrowing = next.screen.height > prev.screen.height + // When content fills the viewport exactly (height == viewport) and the + // cursor is at the bottom, the cursor-restore LF at the end of the + // previous frame scrolled 1 row into scrollback. Use >= to catch this. + const prevHadScrollback = + cursorAtBottom && prev.screen.height >= prev.viewport.height + const isShrinking = next.screen.height < prev.screen.height + const nextFitsViewport = next.screen.height <= prev.viewport.height + + // When shrinking from above-viewport to at-or-below-viewport, content that + // was in scrollback should now be visible. Terminal clear operations can't + // bring scrollback content into view, so we need a full reset. + // Use <= (not <) because even when next height equals viewport height, the + // scrollback depth from the previous render differs from a fresh render. + if (prevHadScrollback && nextFitsViewport && isShrinking) { + logForDebugging( + `Full reset (shrink->below): prevHeight=${prev.screen.height}, nextHeight=${next.screen.height}, viewport=${prev.viewport.height}`, + ) + return fullResetSequence_CAUSES_FLICKER(next, 'offscreen', stylePool) + } + + if ( + prev.screen.height >= prev.viewport.height && + prev.screen.height > 0 && + cursorAtBottom && + !isGrowing + ) { + // viewportY = rows in scrollback from content overflow + // +1 for the row pushed by cursor-restore scroll + const viewportY = prev.screen.height - prev.viewport.height + const scrollbackRows = viewportY + 1 + + let scrollbackChangeY = -1 + diffEach(prev.screen, next.screen, (_x, y) => { + if (y < scrollbackRows) { + scrollbackChangeY = y + return true // early exit + } + }) + if (scrollbackChangeY >= 0) { + const prevLine = readLine(prev.screen, scrollbackChangeY) + const nextLine = readLine(next.screen, scrollbackChangeY) + return fullResetSequence_CAUSES_FLICKER(next, 'offscreen', stylePool, { + triggerY: scrollbackChangeY, + prevLine, + nextLine, + }) + } + } + + const screen = new VirtualScreen(prev.cursor, next.viewport.width) + + // Treat empty screen as height 1 to avoid spurious adjustments on first render + const heightDelta = + Math.max(next.screen.height, 1) - Math.max(prev.screen.height, 1) + const shrinking = heightDelta < 0 + const growing = heightDelta > 0 + + // Handle shrinking: clear lines from the bottom + if (shrinking) { + const linesToClear = prev.screen.height - next.screen.height + + // eraseLines only works within the viewport - it can't clear scrollback. + // If we need to clear more lines than fit in the viewport, some are in + // scrollback, so we need a full reset. + if (linesToClear > prev.viewport.height) { + return fullResetSequence_CAUSES_FLICKER( + next, + 'offscreen', + this.options.stylePool, + ) + } + + // clear(N) moves cursor UP by N-1 lines and to column 0 + // This puts us at line prev.screen.height - N = next.screen.height + // But we want to be at next.screen.height - 1 (bottom of new screen) + screen.txn(prev => [ + [ + { type: 'clear', count: linesToClear }, + { type: 'cursorMove', x: 0, y: -1 }, + ], + { dx: -prev.x, dy: -linesToClear }, + ]) + } + + // viewportY = number of rows in scrollback (not visible on terminal). + // For shrinking: use max(prev, next) because terminal clears don't scroll. + // For growing: use prev state because new rows haven't scrolled old ones yet. + // When prevHadScrollback, add 1 for the cursor-restore LF that scrolled + // an additional row out of view at the end of the previous frame. Without + // this, the diff loop treats that row as reachable — but the cursor clamps + // at viewport top, causing writes to land 1 row off and garbling the output. + const cursorRestoreScroll = prevHadScrollback ? 1 : 0 + const viewportY = growing + ? Math.max( + 0, + prev.screen.height - prev.viewport.height + cursorRestoreScroll, + ) + : Math.max(prev.screen.height, next.screen.height) - + next.viewport.height + + cursorRestoreScroll + + let currentStyleId = stylePool.none + let currentHyperlink: Hyperlink = undefined + + // First pass: render changes to existing rows (rows < prev.screen.height) + let needsFullReset = false + let resetTriggerY = -1 + diffEach(prev.screen, next.screen, (x, y, removed, added) => { + // Skip new rows - we'll render them directly after + if (growing && y >= prev.screen.height) { + return + } + + // Skip spacers during rendering because the terminal will automatically + // advance 2 columns when we write the wide character itself. + // SpacerTail: Second cell of a wide character + // SpacerHead: Marks line-end position where wide char wraps to next line + if ( + added && + (added.width === CellWidth.SpacerTail || + added.width === CellWidth.SpacerHead) + ) { + return + } + + if ( + removed && + (removed.width === CellWidth.SpacerTail || + removed.width === CellWidth.SpacerHead) && + !added + ) { + return + } + + // Skip empty cells that don't need to overwrite existing content. + // This prevents writing trailing spaces that would cause unnecessary + // line wrapping at the edge of the screen. + // Uses isEmptyCellAt to check if both packed words are zero (empty cell). + if (added && isEmptyCellAt(next.screen, x, y) && !removed) { + return + } + + // If the cell outside the viewport range has changed, we need to reset + // because we can't move the cursor there to draw. + if (y < viewportY) { + needsFullReset = true + resetTriggerY = y + return true // early exit + } + + moveCursorTo(screen, x, y) + + if (added) { + const targetHyperlink = added.hyperlink + currentHyperlink = transitionHyperlink( + screen.diff, + currentHyperlink, + targetHyperlink, + ) + const styleStr = stylePool.transition(currentStyleId, added.styleId) + if (writeCellWithStyleStr(screen, added, styleStr)) { + currentStyleId = added.styleId + } + } else if (removed) { + // Cell was removed - clear it with a space + // (This handles shrinking content) + // Reset any active styles/hyperlinks first to avoid leaking into cleared cells + const styleIdToReset = currentStyleId + const hyperlinkToReset = currentHyperlink + currentStyleId = stylePool.none + currentHyperlink = undefined + + screen.txn(() => { + const patches: Diff = [] + transitionStyle(patches, stylePool, styleIdToReset, stylePool.none) + transitionHyperlink(patches, hyperlinkToReset, undefined) + patches.push({ type: 'stdout', content: ' ' }) + return [patches, { dx: 1, dy: 0 }] + }) + } + }) + if (needsFullReset) { + return fullResetSequence_CAUSES_FLICKER(next, 'offscreen', stylePool, { + triggerY: resetTriggerY, + prevLine: readLine(prev.screen, resetTriggerY), + nextLine: readLine(next.screen, resetTriggerY), + }) + } + + // Reset styles before rendering new rows (they'll set their own styles) + currentStyleId = transitionStyle( + screen.diff, + stylePool, + currentStyleId, + stylePool.none, + ) + currentHyperlink = transitionHyperlink( + screen.diff, + currentHyperlink, + undefined, + ) + + // Handle growth: render new rows directly (they naturally scroll the terminal) + if (growing) { + renderFrameSlice( + screen, + next, + prev.screen.height, + next.screen.height, + stylePool, + ) + } + + // Restore cursor. Skipped in alt-screen: the cursor is hidden, its + // position only matters as the starting point for the NEXT frame's + // relative moves, and in alt-screen the next frame always begins with + // CSI H (see ink.tsx onRender) which resets to (0,0) regardless. This + // saves a CR + cursorMove round-trip (~6-10 bytes) every frame. + // + // Main screen: if cursor needs to be past the last line of content + // (typical: cursor.y = screen.height), emit \n to create that line + // since cursor movement can't create new lines. + if (altScreen) { + // no-op; next frame's CSI H anchors cursor + } else if (next.cursor.y >= next.screen.height) { + // Move to column 0 of current line, then emit newlines to reach target row + screen.txn(prev => { + const rowsToCreate = next.cursor.y - prev.y + if (rowsToCreate > 0) { + // Use CR to resolve pending wrap (if any) without advancing + // to the next line, then LF to create each new row. + const patches: Diff = new Array(1 + rowsToCreate) + patches[0] = CARRIAGE_RETURN + for (let i = 0; i < rowsToCreate; i++) { + patches[1 + i] = NEWLINE + } + return [patches, { dx: -prev.x, dy: rowsToCreate }] + } + // At or past target row - need to move cursor to correct position + const dy = next.cursor.y - prev.y + if (dy !== 0 || prev.x !== next.cursor.x) { + // Use CR to clear pending wrap (if any), then cursor move + const patches: Diff = [CARRIAGE_RETURN] + patches.push({ type: 'cursorMove', x: next.cursor.x, y: dy }) + return [patches, { dx: next.cursor.x - prev.x, dy }] + } + return [[], { dx: 0, dy: 0 }] + }) + } else { + moveCursorTo(screen, next.cursor.x, next.cursor.y) + } + + const elapsed = performance.now() - startTime + if (elapsed > 50) { + const damage = next.screen.damage + const damageInfo = damage + ? `${damage.width}x${damage.height} at (${damage.x},${damage.y})` + : 'none' + logForDebugging( + `Slow render: ${elapsed.toFixed(1)}ms, screen: ${next.screen.height}x${next.screen.width}, damage: ${damageInfo}, changes: ${screen.diff.length}`, + ) + } + + return scrollPatch.length > 0 + ? [...scrollPatch, ...screen.diff] + : screen.diff + } +} + +function transitionHyperlink( + diff: Diff, + current: Hyperlink, + target: Hyperlink, +): Hyperlink { + if (current !== target) { + diff.push({ type: 'hyperlink', uri: target ?? '' }) + return target + } + return current +} + +function transitionStyle( + diff: Diff, + stylePool: StylePool, + currentId: number, + targetId: number, +): number { + const str = stylePool.transition(currentId, targetId) + if (str.length > 0) { + diff.push({ type: 'styleStr', str }) + } + return targetId +} + +function readLine(screen: Screen, y: number): string { + let line = '' + for (let x = 0; x < screen.width; x++) { + line += charInCellAt(screen, x, y) ?? ' ' + } + return line.trimEnd() +} + +function fullResetSequence_CAUSES_FLICKER( + frame: Frame, + reason: FlickerReason, + stylePool: StylePool, + debug?: { triggerY: number; prevLine: string; nextLine: string }, +): Diff { + // After clearTerminal, cursor is at (0, 0) + const screen = new VirtualScreen({ x: 0, y: 0 }, frame.viewport.width) + renderFrame(screen, frame, stylePool) + return [{ type: 'clearTerminal', reason, debug }, ...screen.diff] +} + +function renderFrame( + screen: VirtualScreen, + frame: Frame, + stylePool: StylePool, +): void { + renderFrameSlice(screen, frame, 0, frame.screen.height, stylePool) +} + +/** + * Render a slice of rows from the frame's screen. + * Each row is rendered followed by a newline. Cursor ends at (0, endY). + */ +function renderFrameSlice( + screen: VirtualScreen, + frame: Frame, + startY: number, + endY: number, + stylePool: StylePool, +): VirtualScreen { + let currentStyleId = stylePool.none + let currentHyperlink: Hyperlink = undefined + // Track the styleId of the last rendered cell on this line (-1 if none). + // Passed to visibleCellAtIndex to enable fg-only space optimization. + let lastRenderedStyleId = -1 + + const { width: screenWidth, cells, charPool, hyperlinkPool } = frame.screen + + let index = startY * screenWidth + for (let y = startY; y < endY; y += 1) { + // Advance cursor to this row using LF (not CSI CUD / cursor-down). + // CSI CUD stops at the viewport bottom margin and cannot scroll, + // but LF scrolls the viewport to create new lines. Without this, + // when the cursor is at the viewport bottom, moveCursorTo's + // cursor-down silently fails, creating a permanent off-by-one + // between the virtual cursor and the real terminal cursor. + if (screen.cursor.y < y) { + const rowsToAdvance = y - screen.cursor.y + screen.txn(prev => { + const patches: Diff = new Array(1 + rowsToAdvance) + patches[0] = CARRIAGE_RETURN + for (let i = 0; i < rowsToAdvance; i++) { + patches[1 + i] = NEWLINE + } + return [patches, { dx: -prev.x, dy: rowsToAdvance }] + }) + } + // Reset at start of each line — no cell rendered yet + lastRenderedStyleId = -1 + + for (let x = 0; x < screenWidth; x += 1, index += 1) { + // Skip spacers, unstyled empty cells, and fg-only styled spaces that + // match the last rendered style (since cursor-forward produces identical + // visual result). visibleCellAtIndex handles the optimization internally + // to avoid allocating Cell objects for skipped cells. + const cell = visibleCellAtIndex( + cells, + charPool, + hyperlinkPool, + index, + lastRenderedStyleId, + ) + if (!cell) { + continue + } + + moveCursorTo(screen, x, y) + + // Handle hyperlink + const targetHyperlink = cell.hyperlink + currentHyperlink = transitionHyperlink( + screen.diff, + currentHyperlink, + targetHyperlink, + ) + + // Style transition — cached string, zero allocations after warmup + const styleStr = stylePool.transition(currentStyleId, cell.styleId) + if (writeCellWithStyleStr(screen, cell, styleStr)) { + currentStyleId = cell.styleId + lastRenderedStyleId = cell.styleId + } + } + // Reset styles/hyperlinks before newline so background color doesn't + // bleed into the next line when the terminal scrolls. The old code + // reset implicitly by writing trailing unstyled spaces; now that we + // skip empty cells, we must reset explicitly. + currentStyleId = transitionStyle( + screen.diff, + stylePool, + currentStyleId, + stylePool.none, + ) + currentHyperlink = transitionHyperlink( + screen.diff, + currentHyperlink, + undefined, + ) + // CR+LF at end of row — \r resets to column 0, \n moves to next line. + // Without \r, the terminal cursor stays at whatever column content ended + // (since we skip trailing spaces, this can be mid-row). + screen.txn(prev => [[CARRIAGE_RETURN, NEWLINE], { dx: -prev.x, dy: 1 }]) + } + + // Reset any open style/hyperlink at end of slice + transitionStyle(screen.diff, stylePool, currentStyleId, stylePool.none) + transitionHyperlink(screen.diff, currentHyperlink, undefined) + + return screen +} + +type Delta = { dx: number; dy: number } + +/** + * Write a cell with a pre-serialized style transition string (from + * StylePool.transition). Inlines the txn logic to avoid closure/tuple/delta + * allocations on every cell. + * + * Returns true if the cell was written, false if skipped (wide char at + * viewport edge). Callers MUST gate currentStyleId updates on this — when + * skipped, styleStr is never pushed and the terminal's style state is + * unchanged. Updating the virtual tracker anyway desyncs it from the + * terminal, and the next transition is computed from phantom state. + */ +function writeCellWithStyleStr( + screen: VirtualScreen, + cell: Cell, + styleStr: string, +): boolean { + const cellWidth = cell.width === CellWidth.Wide ? 2 : 1 + const px = screen.cursor.x + const vw = screen.viewportWidth + + // Don't write wide chars that would cross the viewport edge. + // Single-codepoint chars (CJK) at vw-2 are safe; multi-codepoint + // graphemes (flags, ZWJ emoji) need stricter threshold. + if (cellWidth === 2 && px < vw) { + const threshold = cell.char.length > 2 ? vw : vw + 1 + if (px + 2 >= threshold) { + return false + } + } + + const diff = screen.diff + if (styleStr.length > 0) { + diff.push({ type: 'styleStr', str: styleStr }) + } + + const needsCompensation = cellWidth === 2 && needsWidthCompensation(cell.char) + + // On terminals with old wcwidth tables, a compensated emoji only advances + // the cursor 1 column, so the CHA below skips column x+1 without painting + // it. Write a styled space there first — on correct terminals the emoji + // glyph (width 2) overwrites it harmlessly; on old terminals it fills the + // gap with the emoji's background. Also clears any stale content at x+1. + // CHA is 1-based, so column px+1 (0-based) is CHA target px+2. + if (needsCompensation && px + 1 < vw) { + diff.push({ type: 'cursorTo', col: px + 2 }) + diff.push({ type: 'stdout', content: ' ' }) + diff.push({ type: 'cursorTo', col: px + 1 }) + } + + diff.push({ type: 'stdout', content: cell.char }) + + // Force terminal cursor to correct column after the emoji. + if (needsCompensation) { + diff.push({ type: 'cursorTo', col: px + cellWidth + 1 }) + } + + // Update cursor — mutate in place to avoid Point allocation + if (px >= vw) { + screen.cursor.x = cellWidth + screen.cursor.y++ + } else { + screen.cursor.x = px + cellWidth + } + return true +} + +function moveCursorTo(screen: VirtualScreen, targetX: number, targetY: number) { + screen.txn(prev => { + const dx = targetX - prev.x + const dy = targetY - prev.y + const inPendingWrap = prev.x >= screen.viewportWidth + + // If we're in pending wrap state (cursor.x >= width), use CR + // to reset to column 0 on the current line without advancing + // to the next line, then issue the cursor movement. + if (inPendingWrap) { + return [ + [CARRIAGE_RETURN, { type: 'cursorMove', x: targetX, y: dy }], + { dx, dy }, + ] + } + + // When moving to a different line, use carriage return (\r) to reset to + // column 0 first, then cursor move. + if (dy !== 0) { + return [ + [CARRIAGE_RETURN, { type: 'cursorMove', x: targetX, y: dy }], + { dx, dy }, + ] + } + + // Standard same-line cursor move + return [[{ type: 'cursorMove', x: dx, y: dy }], { dx, dy }] + }) +} + +/** + * Identify emoji where the terminal's wcwidth may disagree with Unicode. + * On terminals with correct tables, the CHA we emit is a harmless no-op. + * + * Two categories: + * 1. Newer emoji (Unicode 12.0+) missing from terminal wcwidth tables. + * 2. Text-by-default emoji + VS16 (U+FE0F): the base codepoint is width 1 + * in wcwidth, but VS16 triggers emoji presentation making it width 2. + * Examples: ⚔️ (U+2694), ☠️ (U+2620), ❤️ (U+2764). + */ +function needsWidthCompensation(char: string): boolean { + const cp = char.codePointAt(0) + if (cp === undefined) return false + // U+1FA70-U+1FAFF: Symbols and Pictographs Extended-A (Unicode 12.0-15.0) + // U+1FB00-U+1FBFF: Symbols for Legacy Computing (Unicode 13.0) + if ((cp >= 0x1fa70 && cp <= 0x1faff) || (cp >= 0x1fb00 && cp <= 0x1fbff)) { + return true + } + // Text-by-default emoji with VS16: scan for U+FE0F in multi-codepoint + // graphemes. Single BMP chars (length 1) and surrogate pairs without VS16 + // skip this check. VS16 (0xFE0F) can't collide with surrogates (0xD800-0xDFFF). + if (char.length >= 2) { + for (let i = 0; i < char.length; i++) { + if (char.charCodeAt(i) === 0xfe0f) return true + } + } + return false +} + +class VirtualScreen { + // Public for direct mutation by writeCellWithStyleStr (avoids txn overhead). + // File-private class — not exposed outside log-update.ts. + cursor: Point + diff: Diff = [] + + constructor( + origin: Point, + readonly viewportWidth: number, + ) { + this.cursor = { ...origin } + } + + txn(fn: (prev: Point) => [patches: Diff, next: Delta]): void { + const [patches, next] = fn(this.cursor) + for (const patch of patches) { + this.diff.push(patch) + } + this.cursor.x += next.dx + this.cursor.y += next.dy + } +} diff --git a/ink/measure-element.ts b/ink/measure-element.ts new file mode 100644 index 0000000..ed56eaf --- /dev/null +++ b/ink/measure-element.ts @@ -0,0 +1,23 @@ +import type { DOMElement } from './dom.js' + +type Output = { + /** + * Element width. + */ + width: number + + /** + * Element height. + */ + height: number +} + +/** + * Measure the dimensions of a particular `` element. + */ +const measureElement = (node: DOMElement): Output => ({ + width: node.yogaNode?.getComputedWidth() ?? 0, + height: node.yogaNode?.getComputedHeight() ?? 0, +}) + +export default measureElement diff --git a/ink/measure-text.ts b/ink/measure-text.ts new file mode 100644 index 0000000..cc8ae45 --- /dev/null +++ b/ink/measure-text.ts @@ -0,0 +1,47 @@ +import { lineWidth } from './line-width-cache.js' + +type Output = { + width: number + height: number +} + +// Single-pass measurement: computes both width and height in one +// iteration instead of two (widestLine + countVisualLines). +// Uses indexOf to avoid array allocation from split('\n'). +function measureText(text: string, maxWidth: number): Output { + if (text.length === 0) { + return { + width: 0, + height: 0, + } + } + + // Infinite or non-positive width means no wrapping — each line is one visual line. + // Must check before the loop since Math.ceil(w / Infinity) = 0. + const noWrap = maxWidth <= 0 || !Number.isFinite(maxWidth) + + let height = 0 + let width = 0 + let start = 0 + + while (start <= text.length) { + const end = text.indexOf('\n', start) + const line = end === -1 ? text.substring(start) : text.substring(start, end) + + const w = lineWidth(line) + width = Math.max(width, w) + + if (noWrap) { + height++ + } else { + height += w === 0 ? 1 : Math.ceil(w / maxWidth) + } + + if (end === -1) break + start = end + 1 + } + + return { width, height } +} + +export default measureText diff --git a/ink/node-cache.ts b/ink/node-cache.ts new file mode 100644 index 0000000..f887325 --- /dev/null +++ b/ink/node-cache.ts @@ -0,0 +1,54 @@ +import type { DOMElement } from './dom.js' +import type { Rectangle } from './layout/geometry.js' + +/** + * Cached layout bounds for each rendered node (used for blit + clearing). + * `top` is the yoga-local getComputedTop() — stored so ScrollBox viewport + * culling can skip yoga reads for clean children whose position hasn't + * shifted (O(dirty) instead of O(mounted) first-pass). + */ +export type CachedLayout = { + x: number + y: number + width: number + height: number + top?: number +} + +export const nodeCache = new WeakMap() + +/** Rects of removed children that need clearing on next render */ +export const pendingClears = new WeakMap() + +/** + * Set when a pendingClear is added for an absolute-positioned node. + * Signals renderer to disable blit for the next frame: the removed node + * may have painted over non-siblings (e.g. an overlay over a ScrollBox + * earlier in tree order), so their blits from prevScreen would restore + * the overlay's pixels. Normal-flow removals are already handled by + * hasRemovedChild at the parent level; only absolute positioning paints + * cross-subtree. Reset at the start of each render. + */ +let absoluteNodeRemoved = false + +export function addPendingClear( + parent: DOMElement, + rect: Rectangle, + isAbsolute: boolean, +): void { + const existing = pendingClears.get(parent) + if (existing) { + existing.push(rect) + } else { + pendingClears.set(parent, [rect]) + } + if (isAbsolute) { + absoluteNodeRemoved = true + } +} + +export function consumeAbsoluteRemovedFlag(): boolean { + const had = absoluteNodeRemoved + absoluteNodeRemoved = false + return had +} diff --git a/ink/optimizer.ts b/ink/optimizer.ts new file mode 100644 index 0000000..70016ef --- /dev/null +++ b/ink/optimizer.ts @@ -0,0 +1,93 @@ +import type { Diff } from './frame.js' + +/** + * Optimize a diff by applying all optimization rules in a single pass. + * This reduces the number of patches that need to be written to the terminal. + * + * Rules applied: + * - Remove empty stdout patches + * - Merge consecutive cursorMove patches + * - Remove no-op cursorMove (0,0) patches + * - Concat adjacent style patches (transition diffs — can't drop either) + * - Dedupe consecutive hyperlinks with same URI + * - Cancel cursor hide/show pairs + * - Remove clear patches with count 0 + */ +export function optimize(diff: Diff): Diff { + if (diff.length <= 1) { + return diff + } + + const result: Diff = [] + let len = 0 + + for (const patch of diff) { + const type = patch.type + + // Skip no-ops + if (type === 'stdout') { + if (patch.content === '') continue + } else if (type === 'cursorMove') { + if (patch.x === 0 && patch.y === 0) continue + } else if (type === 'clear') { + if (patch.count === 0) continue + } + + // Try to merge with previous patch + if (len > 0) { + const lastIdx = len - 1 + const last = result[lastIdx]! + const lastType = last.type + + // Merge consecutive cursorMove + if (type === 'cursorMove' && lastType === 'cursorMove') { + result[lastIdx] = { + type: 'cursorMove', + x: last.x + patch.x, + y: last.y + patch.y, + } + continue + } + + // Collapse consecutive cursorTo (only the last one matters) + if (type === 'cursorTo' && lastType === 'cursorTo') { + result[lastIdx] = patch + continue + } + + // Concat adjacent style patches. styleStr is a transition diff + // (computed by diffAnsiCodes(from, to)), not a setter — dropping + // the first is only sound if its undo-codes are a subset of the + // second's, which is NOT guaranteed. e.g. [\e[49m, \e[2m]: dropping + // the bg reset leaks it into the next \e[2J/\e[2K via BCE. + if (type === 'styleStr' && lastType === 'styleStr') { + result[lastIdx] = { type: 'styleStr', str: last.str + patch.str } + continue + } + + // Dedupe hyperlinks + if ( + type === 'hyperlink' && + lastType === 'hyperlink' && + patch.uri === last.uri + ) { + continue + } + + // Cancel cursor hide/show pairs + if ( + (type === 'cursorShow' && lastType === 'cursorHide') || + (type === 'cursorHide' && lastType === 'cursorShow') + ) { + result.pop() + len-- + continue + } + } + + result.push(patch) + len++ + } + + return result +} diff --git a/ink/output.ts b/ink/output.ts new file mode 100644 index 0000000..16b5ae2 --- /dev/null +++ b/ink/output.ts @@ -0,0 +1,797 @@ +import { + type AnsiCode, + type StyledChar, + styledCharsFromTokens, + tokenize, +} from '@alcalzone/ansi-tokenize' +import { logForDebugging } from '../utils/debug.js' +import { getGraphemeSegmenter } from '../utils/intl.js' +import sliceAnsi from '../utils/sliceAnsi.js' +import { reorderBidi } from './bidi.js' +import { type Rectangle, unionRect } from './layout/geometry.js' +import { + blitRegion, + CellWidth, + extractHyperlinkFromStyles, + filterOutHyperlinkStyles, + markNoSelectRegion, + OSC8_PREFIX, + resetScreen, + type Screen, + type StylePool, + setCellAt, + shiftRows, +} from './screen.js' +import { stringWidth } from './stringWidth.js' +import { widestLine } from './widest-line.js' + +/** + * A grapheme cluster with precomputed terminal width, styleId, and hyperlink. + * Built once per unique line (cached via charCache), so the per-char hot loop + * is just property reads + setCellAt — no stringWidth, no style interning, + * no hyperlink extraction per frame. + * + * styleId is safe to cache: StylePool is session-lived (never reset). + * hyperlink is stored as a string (not interned ID) since hyperlinkPool + * resets every 5 min; setCellAt interns it per-frame (cheap Map.get). + */ +type ClusteredChar = { + value: string + width: number + styleId: number + hyperlink: string | undefined +} + +/** + * Collects write/blit/clear/clip operations from the render tree, then + * applies them to a Screen buffer in `get()`. The Screen is what gets + * diffed against the previous frame to produce terminal updates. + */ + +type Options = { + width: number + height: number + stylePool: StylePool + /** + * Screen to render into. Will be reset before use. + * For double-buffering, pass a reusable screen. Otherwise create a new one. + */ + screen: Screen +} + +export type Operation = + | WriteOperation + | ClipOperation + | UnclipOperation + | BlitOperation + | ClearOperation + | NoSelectOperation + | ShiftOperation + +type WriteOperation = { + type: 'write' + x: number + y: number + text: string + /** + * Per-line soft-wrap flags, parallel to text.split('\n'). softWrap[i]=true + * means line i is a continuation of line i-1 (the `\n` before it was + * inserted by word-wrap, not in the source). Index 0 is always false. + * Undefined means the producer didn't track wrapping (e.g. fills, + * raw-ansi) — the screen's per-row bitmap is left untouched. + */ + softWrap?: boolean[] +} + +type ClipOperation = { + type: 'clip' + clip: Clip +} + +export type Clip = { + x1: number | undefined + x2: number | undefined + y1: number | undefined + y2: number | undefined +} + +/** + * Intersect two clips. `undefined` on an axis means unbounded; the other + * clip's bound wins. If both are bounded, take the tighter constraint + * (max of mins, min of maxes). If the resulting region is empty + * (x1 >= x2 or y1 >= y2), writes clipped by it will be dropped. + */ +function intersectClip(parent: Clip | undefined, child: Clip): Clip { + if (!parent) return child + return { + x1: maxDefined(parent.x1, child.x1), + x2: minDefined(parent.x2, child.x2), + y1: maxDefined(parent.y1, child.y1), + y2: minDefined(parent.y2, child.y2), + } +} + +function maxDefined( + a: number | undefined, + b: number | undefined, +): number | undefined { + if (a === undefined) return b + if (b === undefined) return a + return Math.max(a, b) +} + +function minDefined( + a: number | undefined, + b: number | undefined, +): number | undefined { + if (a === undefined) return b + if (b === undefined) return a + return Math.min(a, b) +} + +type UnclipOperation = { + type: 'unclip' +} + +type BlitOperation = { + type: 'blit' + src: Screen + x: number + y: number + width: number + height: number +} + +type ShiftOperation = { + type: 'shift' + top: number + bottom: number + n: number +} + +type ClearOperation = { + type: 'clear' + region: Rectangle + /** + * Set when the clear is for an absolute-positioned node's old bounds. + * Absolute nodes overlay normal-flow siblings, so their stale paint is + * what an earlier sibling's clean-subtree blit wrongly restores from + * prevScreen. Normal-flow siblings' clears don't have this problem — + * their old position can't have been painted on top of a sibling. + */ + fromAbsolute?: boolean +} + +type NoSelectOperation = { + type: 'noSelect' + region: Rectangle +} + +export default class Output { + width: number + height: number + private readonly stylePool: StylePool + private screen: Screen + + private readonly operations: Operation[] = [] + + private charCache: Map = new Map() + + constructor(options: Options) { + const { width, height, stylePool, screen } = options + + this.width = width + this.height = height + this.stylePool = stylePool + this.screen = screen + + resetScreen(screen, width, height) + } + + /** + * Reuse this Output for a new frame. Zeroes the screen buffer, clears + * the operation list (backing storage is retained), and caps charCache + * growth. Preserving charCache across frames is the main win — most + * lines don't change between renders, so tokenize + grapheme clustering + * becomes a cache hit. + */ + reset(width: number, height: number, screen: Screen): void { + this.width = width + this.height = height + this.screen = screen + this.operations.length = 0 + resetScreen(screen, width, height) + if (this.charCache.size > 16384) this.charCache.clear() + } + + /** + * Copy cells from a source screen region (blit = block image transfer). + */ + blit(src: Screen, x: number, y: number, width: number, height: number): void { + this.operations.push({ type: 'blit', src, x, y, width, height }) + } + + /** + * Shift full-width rows within [top, bottom] by n. n > 0 = up. Mirrors + * what DECSTBM + SU/SD does to the terminal. Paired with blit() to reuse + * prevScreen content during pure scroll, avoiding full child re-render. + */ + shift(top: number, bottom: number, n: number): void { + this.operations.push({ type: 'shift', top, bottom, n }) + } + + /** + * Clear a region by writing empty cells. Used when a node shrinks to + * ensure stale content from the previous frame is removed. + */ + clear(region: Rectangle, fromAbsolute?: boolean): void { + this.operations.push({ type: 'clear', region, fromAbsolute }) + } + + /** + * Mark a region as non-selectable (excluded from fullscreen text + * selection copy + highlight). Used by to fence off + * gutters (line numbers, diff sigils). Applied AFTER blit/write so + * the mark wins regardless of what's blitted into the region. + */ + noSelect(region: Rectangle): void { + this.operations.push({ type: 'noSelect', region }) + } + + write(x: number, y: number, text: string, softWrap?: boolean[]): void { + if (!text) { + return + } + + this.operations.push({ + type: 'write', + x, + y, + text, + softWrap, + }) + } + + clip(clip: Clip) { + this.operations.push({ + type: 'clip', + clip, + }) + } + + unclip() { + this.operations.push({ + type: 'unclip', + }) + } + + get(): Screen { + const screen = this.screen + const screenWidth = this.width + const screenHeight = this.height + + // Track blit vs write cell counts for debugging + let blitCells = 0 + let writeCells = 0 + + // Pass 1: expand damage to cover clear regions. The buffer is freshly + // zeroed by resetScreen, so this pass only marks damage so diff() + // checks these regions against the previous frame. + // + // Also collect clears from absolute-positioned nodes. An absolute + // node overlays normal-flow siblings; when it shrinks, its clear is + // pushed AFTER those siblings' clean-subtree blits (DOM order). The + // blit copies the absolute node's own stale paint from prevScreen, + // and since clear is damage-only, the ghost survives diff. Normal- + // flow clears don't need this — a normal-flow node's old position + // can't have been painted on top of a sibling's current position. + const absoluteClears: Rectangle[] = [] + for (const operation of this.operations) { + if (operation.type !== 'clear') continue + const { x, y, width, height } = operation.region + const startX = Math.max(0, x) + const startY = Math.max(0, y) + const maxX = Math.min(x + width, screenWidth) + const maxY = Math.min(y + height, screenHeight) + if (startX >= maxX || startY >= maxY) continue + const rect = { + x: startX, + y: startY, + width: maxX - startX, + height: maxY - startY, + } + screen.damage = screen.damage ? unionRect(screen.damage, rect) : rect + if (operation.fromAbsolute) absoluteClears.push(rect) + } + + const clips: Clip[] = [] + + for (const operation of this.operations) { + switch (operation.type) { + case 'clear': + // handled in pass 1 + continue + + case 'clip': + // Intersect with the parent clip (if any) so nested + // overflow:hidden boxes can't write outside their ancestor's + // clip region. Without this, a message with overflow:hidden at + // the bottom of a scrollbox pushes its OWN clip (based on its + // layout bounds, already translated by -scrollTop) which can + // extend below the scrollbox viewport — writes escape into + // the sibling bottom section's rows. + clips.push(intersectClip(clips.at(-1), operation.clip)) + continue + + case 'unclip': + clips.pop() + continue + + case 'blit': { + // Bulk-copy cells from source screen region using TypedArray.set(). + // Tracking damage ensures diff() checks blitted cells for stale content + // when a parent blits an area that previously contained child content. + const { + src, + x: regionX, + y: regionY, + width: regionWidth, + height: regionHeight, + } = operation + // Intersect with active clip — a child's clean-blit passes its full + // cached rect, but the parent ScrollBox may have shrunk (pill mount). + // Without this, the blit writes past the ScrollBox's new bottom edge + // into the pill's row. + const clip = clips.at(-1) + const startX = Math.max(regionX, clip?.x1 ?? 0) + const startY = Math.max(regionY, clip?.y1 ?? 0) + const maxY = Math.min( + regionY + regionHeight, + screenHeight, + src.height, + clip?.y2 ?? Infinity, + ) + const maxX = Math.min( + regionX + regionWidth, + screenWidth, + src.width, + clip?.x2 ?? Infinity, + ) + if (startX >= maxX || startY >= maxY) continue + // Skip rows covered by an absolute-positioned node's clear. + // Absolute nodes overlay normal-flow siblings, so prevScreen in + // that region holds the absolute node's stale paint — blitting + // it back would ghost. See absoluteClears collection above. + if (absoluteClears.length === 0) { + blitRegion(screen, src, startX, startY, maxX, maxY) + blitCells += (maxY - startY) * (maxX - startX) + continue + } + let rowStart = startY + for (let row = startY; row <= maxY; row++) { + const excluded = + row < maxY && + absoluteClears.some( + r => + row >= r.y && + row < r.y + r.height && + startX >= r.x && + maxX <= r.x + r.width, + ) + if (excluded || row === maxY) { + if (row > rowStart) { + blitRegion(screen, src, startX, rowStart, maxX, row) + blitCells += (row - rowStart) * (maxX - startX) + } + rowStart = row + 1 + } + } + continue + } + + case 'shift': { + shiftRows(screen, operation.top, operation.bottom, operation.n) + continue + } + + case 'write': { + const { text, softWrap } = operation + let { x, y } = operation + let lines = text.split('\n') + let swFrom = 0 + let prevContentEnd = 0 + + const clip = clips.at(-1) + + if (clip) { + const clipHorizontally = + typeof clip?.x1 === 'number' && typeof clip?.x2 === 'number' + + const clipVertically = + typeof clip?.y1 === 'number' && typeof clip?.y2 === 'number' + + // If text is positioned outside of clipping area altogether, + // skip to the next operation to avoid unnecessary calculations + if (clipHorizontally) { + const width = widestLine(text) + + if (x + width <= clip.x1! || x >= clip.x2!) { + continue + } + } + + if (clipVertically) { + const height = lines.length + + if (y + height <= clip.y1! || y >= clip.y2!) { + continue + } + } + + if (clipHorizontally) { + lines = lines.map(line => { + const from = x < clip.x1! ? clip.x1! - x : 0 + const width = stringWidth(line) + const to = x + width > clip.x2! ? clip.x2! - x : width + let sliced = sliceAnsi(line, from, to) + // Wide chars (CJK, emoji) occupy 2 cells. When `to` lands + // on the first cell of a wide char, sliceAnsi includes the + // entire glyph and the result overflows clip.x2 by one cell, + // writing a SpacerTail into the adjacent sibling. Re-slice + // one cell earlier; wide chars are exactly 2 cells, so a + // single retry always fits. + if (stringWidth(sliced) > to - from) { + sliced = sliceAnsi(line, from, to - 1) + } + return sliced + }) + + if (x < clip.x1!) { + x = clip.x1! + } + } + + if (clipVertically) { + const from = y < clip.y1! ? clip.y1! - y : 0 + const height = lines.length + const to = y + height > clip.y2! ? clip.y2! - y : height + + // If the first visible line is a soft-wrap continuation, we + // need the clipped previous line's content end so + // screen.softWrap[lineY] correctly records the join point + // even though that line's cells were never written. + if (softWrap && from > 0 && softWrap[from] === true) { + prevContentEnd = x + stringWidth(lines[from - 1]!) + } + + lines = lines.slice(from, to) + swFrom = from + + if (y < clip.y1!) { + y = clip.y1! + } + } + } + + const swBits = screen.softWrap + let offsetY = 0 + + for (const line of lines) { + const lineY = y + offsetY + // Line can be outside screen if `text` is taller than screen height + if (lineY >= screenHeight) { + break + } + const contentEnd = writeLineToScreen( + screen, + line, + x, + lineY, + screenWidth, + this.stylePool, + this.charCache, + ) + writeCells += contentEnd - x + // See Screen.softWrap docstring for the encoding. contentEnd + // from writeLineToScreen is tab-expansion-aware, unlike + // x+stringWidth(line) which treats tabs as width 0. + if (softWrap) { + const isSW = softWrap[swFrom + offsetY] === true + swBits[lineY] = isSW ? prevContentEnd : 0 + prevContentEnd = contentEnd + } + offsetY++ + } + continue + } + } + } + + // noSelect ops go LAST so they win over blits (which copy noSelect + // from prevScreen) and writes (which don't touch noSelect). This way + // a box correctly fences its region even when the parent + // blits, and moving a between frames correctly clears the + // old region (resetScreen already zeroed the bitmap). + for (const operation of this.operations) { + if (operation.type === 'noSelect') { + const { x, y, width, height } = operation.region + markNoSelectRegion(screen, x, y, width, height) + } + } + + // Log blit/write ratio for debugging - high write count suggests blitting isn't working + const totalCells = blitCells + writeCells + if (totalCells > 1000 && writeCells > blitCells) { + logForDebugging( + `High write ratio: blit=${blitCells}, write=${writeCells} (${((writeCells / totalCells) * 100).toFixed(1)}% writes), screen=${screenHeight}x${screenWidth}`, + ) + } + + return screen + } +} + +function stylesEqual(a: AnsiCode[], b: AnsiCode[]): boolean { + if (a === b) return true // Reference equality fast path + const len = a.length + if (len !== b.length) return false + if (len === 0) return true // Both empty + for (let i = 0; i < len; i++) { + if (a[i]!.code !== b[i]!.code) return false + } + return true +} + +/** + * Convert a string with ANSI codes into styled characters with proper grapheme + * clustering. Fixes ansi-tokenize splitting grapheme clusters (like family + * emojis) into individual code points. + * + * Also precomputes styleId + hyperlink per style run (not per char) — an + * 80-char line with 3 style runs does 3 intern calls instead of 80. + */ +function styledCharsWithGraphemeClustering( + chars: StyledChar[], + stylePool: StylePool, +): ClusteredChar[] { + const charCount = chars.length + if (charCount === 0) return [] + + const result: ClusteredChar[] = [] + const bufferChars: string[] = [] + let bufferStyles: AnsiCode[] = chars[0]!.styles + + for (let i = 0; i < charCount; i++) { + const char = chars[i]! + const styles = char.styles + + // Different styles means we need to flush and start new buffer + if (bufferChars.length > 0 && !stylesEqual(styles, bufferStyles)) { + flushBuffer(bufferChars.join(''), bufferStyles, stylePool, result) + bufferChars.length = 0 + } + + bufferChars.push(char.value) + bufferStyles = styles + } + + // Final flush + if (bufferChars.length > 0) { + flushBuffer(bufferChars.join(''), bufferStyles, stylePool, result) + } + + return result +} + +function flushBuffer( + buffer: string, + styles: AnsiCode[], + stylePool: StylePool, + out: ClusteredChar[], +): void { + // Compute styleId + hyperlink ONCE for the whole style run. + // Every grapheme in this buffer shares the same styles. + // + // Extract and track hyperlinks separately, filter from styles. + // Always check for OSC 8 codes to filter, not just when a URL is + // extracted. The tokenizer treats OSC 8 close codes (empty URL) as + // active styles, so they must be filtered even when no hyperlink + // URL is present. + const hyperlink = extractHyperlinkFromStyles(styles) ?? undefined + const hasOsc8Styles = + hyperlink !== undefined || + styles.some( + s => + s.code.length >= OSC8_PREFIX.length && s.code.startsWith(OSC8_PREFIX), + ) + const filteredStyles = hasOsc8Styles + ? filterOutHyperlinkStyles(styles) + : styles + const styleId = stylePool.intern(filteredStyles) + + for (const { segment: grapheme } of getGraphemeSegmenter().segment(buffer)) { + out.push({ + value: grapheme, + width: stringWidth(grapheme), + styleId, + hyperlink, + }) + } +} + +/** + * Write a single line's characters into the screen buffer. + * Extracted from Output.get() so JSC can optimize this tight, + * monomorphic loop independently — better register allocation, + * setCellAt inlining, and type feedback than when buried inside + * a 300-line dispatch function. + * + * Returns the end column (x + visual width, including tab expansion) so + * the caller can record it in screen.softWrap without re-walking the + * line via stringWidth(). Caller computes the debug cell-count as end-x. + */ +function writeLineToScreen( + screen: Screen, + line: string, + x: number, + y: number, + screenWidth: number, + stylePool: StylePool, + charCache: Map, +): number { + let characters = charCache.get(line) + if (!characters) { + characters = reorderBidi( + styledCharsWithGraphemeClustering( + styledCharsFromTokens(tokenize(line)), + stylePool, + ), + ) + charCache.set(line, characters) + } + + let offsetX = x + + for (let charIdx = 0; charIdx < characters.length; charIdx++) { + const character = characters[charIdx]! + const codePoint = character.value.codePointAt(0) + + // Handle C0 control characters (0x00-0x1F) that cause cursor movement + // mismatches. stringWidth treats these as width 0, but terminals may + // move the cursor differently. + if (codePoint !== undefined && codePoint <= 0x1f) { + // Tab (0x09): expand to spaces to reach next tab stop + if (codePoint === 0x09) { + const tabWidth = 8 + const spacesToNextStop = tabWidth - (offsetX % tabWidth) + for (let i = 0; i < spacesToNextStop && offsetX < screenWidth; i++) { + setCellAt(screen, offsetX, y, { + char: ' ', + styleId: stylePool.none, + width: CellWidth.Narrow, + hyperlink: undefined, + }) + offsetX++ + } + } + // ESC (0x1B): skip incomplete escape sequences that ansi-tokenize + // didn't recognize. ansi-tokenize only parses SGR sequences (ESC[...m) + // and OSC 8 hyperlinks (ESC]8;;url BEL). Other sequences like cursor + // movement, screen clearing, or terminal title become individual char + // tokens that we need to skip here. + else if (codePoint === 0x1b) { + const nextChar = characters[charIdx + 1]?.value + const nextCode = nextChar?.codePointAt(0) + if ( + nextChar === '(' || + nextChar === ')' || + nextChar === '*' || + nextChar === '+' + ) { + // Charset selection: ESC ( X, ESC ) X, etc. + // Skip the intermediate char and the charset designator + charIdx += 2 + } else if (nextChar === '[') { + // CSI sequence: ESC [ ... final-byte + // Final byte is in range 0x40-0x7E (@, A-Z, [\]^_`, a-z, {|}~) + // Examples: ESC[2J (clear), ESC[?25l (cursor hide), ESC[H (home) + charIdx++ // skip the [ + while (charIdx < characters.length - 1) { + charIdx++ + const c = characters[charIdx]?.value.codePointAt(0) + // Final byte terminates the sequence + if (c !== undefined && c >= 0x40 && c <= 0x7e) { + break + } + } + } else if ( + nextChar === ']' || + nextChar === 'P' || + nextChar === '_' || + nextChar === '^' || + nextChar === 'X' + ) { + // String-based sequences terminated by BEL (0x07) or ST (ESC \): + // - OSC: ESC ] ... (Operating System Command) + // - DCS: ESC P ... (Device Control String) + // - APC: ESC _ ... (Application Program Command) + // - PM: ESC ^ ... (Privacy Message) + // - SOS: ESC X ... (Start of String) + charIdx++ // skip the introducer char + while (charIdx < characters.length - 1) { + charIdx++ + const c = characters[charIdx]?.value + // BEL (0x07) terminates the sequence + if (c === '\x07') { + break + } + // ST (String Terminator) is ESC \ + // When we see ESC, check if next char is backslash + if (c === '\x1b') { + const nextC = characters[charIdx + 1]?.value + if (nextC === '\\') { + charIdx++ // skip the backslash too + break + } + } + } + } else if ( + nextCode !== undefined && + nextCode >= 0x30 && + nextCode <= 0x7e + ) { + // Single-character escape sequences: ESC followed by 0x30-0x7E + // (excluding the multi-char introducers already handled above) + // - Fp range (0x30-0x3F): ESC 7 (save cursor), ESC 8 (restore) + // - Fe range (0x40-0x5F): ESC D (index), ESC M (reverse index) + // - Fs range (0x60-0x7E): ESC c (reset) + charIdx++ // skip the command char + } + } + // Carriage return (0x0D): would move cursor to column 0, skip it + // Backspace (0x08): would move cursor left, skip it + // Bell (0x07), vertical tab (0x0B), form feed (0x0C): skip + // All other control chars (0x00-0x06, 0x0E-0x1F): skip + // Note: newline (0x0A) is already handled by line splitting + continue + } + + // Zero-width characters (combining marks, ZWNJ, ZWS, etc.) + // don't occupy terminal cells — storing them as Narrow cells + // desyncs the virtual cursor from the real terminal cursor. + // Width was computed once during clustering (cached via charCache). + const charWidth = character.width + if (charWidth === 0) { + continue + } + + const isWideCharacter = charWidth >= 2 + + // Wide char at last column can't fit — terminal would wrap it to + // the next line, desyncing our cursor model. Place a SpacerHead + // to mark the blank column, matching terminal behavior. + if (isWideCharacter && offsetX + 2 > screenWidth) { + setCellAt(screen, offsetX, y, { + char: ' ', + styleId: stylePool.none, + width: CellWidth.SpacerHead, + hyperlink: undefined, + }) + offsetX++ + continue + } + + // styleId + hyperlink were precomputed during clustering (once per + // style run, cached via charCache). Hot loop is now just property + // reads — no intern, no extract, no filter per frame. + setCellAt(screen, offsetX, y, { + char: character.value, + styleId: character.styleId, + width: isWideCharacter ? CellWidth.Wide : CellWidth.Narrow, + hyperlink: character.hyperlink, + }) + offsetX += isWideCharacter ? 2 : 1 + } + + return offsetX +} diff --git a/ink/parse-keypress.ts b/ink/parse-keypress.ts new file mode 100644 index 0000000..a7e43ad --- /dev/null +++ b/ink/parse-keypress.ts @@ -0,0 +1,801 @@ +/** + * Keyboard input parser - converts terminal input to key events + * + * Uses the termio tokenizer for escape sequence boundary detection, + * then interprets sequences as keypresses. + */ +import { Buffer } from 'buffer' +import { PASTE_END, PASTE_START } from './termio/csi.js' +import { createTokenizer, type Tokenizer } from './termio/tokenize.js' + +// eslint-disable-next-line no-control-regex +const META_KEY_CODE_RE = /^(?:\x1b)([a-zA-Z0-9])$/ + +// eslint-disable-next-line no-control-regex +const FN_KEY_RE = + // eslint-disable-next-line no-control-regex + /^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/ + +// CSI u (kitty keyboard protocol): ESC [ codepoint [; modifier] u +// Example: ESC[13;2u = Shift+Enter, ESC[27u = Escape (no modifiers) +// Modifier is optional - when absent, defaults to 1 (no modifiers) +// eslint-disable-next-line no-control-regex +const CSI_U_RE = /^\x1b\[(\d+)(?:;(\d+))?u/ + +// xterm modifyOtherKeys: ESC [ 27 ; modifier ; keycode ~ +// Example: ESC[27;2;13~ = Shift+Enter. Emitted by Ghostty/tmux/xterm when +// modifyOtherKeys=2 is active or via user keybinds, typically over SSH where +// TERM sniffing misses Ghostty and we never push Kitty keyboard mode. +// Note param order is reversed vs CSI u (modifier first, keycode second). +// eslint-disable-next-line no-control-regex +const MODIFY_OTHER_KEYS_RE = /^\x1b\[27;(\d+);(\d+)~/ + +// -- Terminal response patterns (inbound sequences from the terminal itself) -- +// DECRPM: CSI ? Ps ; Pm $ y — response to DECRQM (request mode) +// eslint-disable-next-line no-control-regex +const DECRPM_RE = /^\x1b\[\?(\d+);(\d+)\$y$/ +// DA1: CSI ? Ps ; ... c — primary device attributes response +// eslint-disable-next-line no-control-regex +const DA1_RE = /^\x1b\[\?([\d;]*)c$/ +// DA2: CSI > Ps ; ... c — secondary device attributes response +// eslint-disable-next-line no-control-regex +const DA2_RE = /^\x1b\[>([\d;]*)c$/ +// Kitty keyboard flags: CSI ? flags u — response to CSI ? u query +// (private ? marker distinguishes from CSI u key events) +// eslint-disable-next-line no-control-regex +const KITTY_FLAGS_RE = /^\x1b\[\?(\d+)u$/ +// DECXCPR cursor position: CSI ? row ; col R +// The ? marker disambiguates from modified F3 keys (Shift+F3 = CSI 1;2 R, +// Ctrl+F3 = CSI 1;5 R, etc.) — plain CSI row;col R is genuinely ambiguous. +// eslint-disable-next-line no-control-regex +const CURSOR_POSITION_RE = /^\x1b\[\?(\d+);(\d+)R$/ +// OSC response: OSC code ; data (BEL|ST) +// eslint-disable-next-line no-control-regex +const OSC_RESPONSE_RE = /^\x1b\](\d+);(.*?)(?:\x07|\x1b\\)$/s +// XTVERSION: DCS > | name ST — terminal name/version string (answer to CSI > 0 q). +// xterm.js replies "xterm.js(X.Y.Z)"; Ghostty, kitty, iTerm2, etc. reply with +// their own name. Unlike TERM_PROGRAM, this survives SSH since the query/reply +// goes through the pty, not the environment. +// eslint-disable-next-line no-control-regex +const XTVERSION_RE = /^\x1bP>\|(.*?)(?:\x07|\x1b\\)$/s +// SGR mouse event: CSI < button ; col ; row M (press) or m (release) +// Button codes: 64=wheel-up, 65=wheel-down (0x40 | wheel-bit). +// Button 32=left-drag (0x20 | motion-bit). Plain 0/1/2 = left/mid/right click. +// eslint-disable-next-line no-control-regex +const SGR_MOUSE_RE = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])$/ + +function createPasteKey(content: string): ParsedKey { + return { + kind: 'key', + name: '', + fn: false, + ctrl: false, + meta: false, + shift: false, + option: false, + super: false, + sequence: content, + raw: content, + isPasted: true, + } +} + +/** DECRPM status values (response to DECRQM) */ +export const DECRPM_STATUS = { + NOT_RECOGNIZED: 0, + SET: 1, + RESET: 2, + PERMANENTLY_SET: 3, + PERMANENTLY_RESET: 4, +} as const + +/** + * A response sequence received from the terminal (not a keypress). + * Emitted in answer to queries like DECRQM, DA1, OSC 11, etc. + */ +export type TerminalResponse = + /** DECRPM: answer to DECRQM (request DEC private mode status) */ + | { type: 'decrpm'; mode: number; status: number } + /** DA1: primary device attributes (used as a universal sentinel) */ + | { type: 'da1'; params: number[] } + /** DA2: secondary device attributes (terminal version info) */ + | { type: 'da2'; params: number[] } + /** Kitty keyboard protocol: current flags (answer to CSI ? u) */ + | { type: 'kittyKeyboard'; flags: number } + /** DSR: cursor position report (answer to CSI 6 n) */ + | { type: 'cursorPosition'; row: number; col: number } + /** OSC response: generic operating-system-command reply (e.g. OSC 11 bg color) */ + | { type: 'osc'; code: number; data: string } + /** XTVERSION: terminal name/version string (answer to CSI > 0 q). + * Example values: "xterm.js(5.5.0)", "ghostty 1.2.0", "iTerm2 3.6". */ + | { type: 'xtversion'; name: string } + +/** + * Try to recognize a sequence token as a terminal response. + * Returns null if the sequence is not a known response pattern + * (i.e. it should be treated as a keypress). + * + * These patterns are syntactically distinguishable from keyboard input — + * no physical key produces CSI ? ... c or CSI ? ... $ y, so they can be + * safely parsed out of the input stream at any time. + */ +function parseTerminalResponse(s: string): TerminalResponse | null { + // CSI-prefixed responses + if (s.startsWith('\x1b[')) { + let m: RegExpExecArray | null + + if ((m = DECRPM_RE.exec(s))) { + return { + type: 'decrpm', + mode: parseInt(m[1]!, 10), + status: parseInt(m[2]!, 10), + } + } + + if ((m = DA1_RE.exec(s))) { + return { type: 'da1', params: splitNumericParams(m[1]!) } + } + + if ((m = DA2_RE.exec(s))) { + return { type: 'da2', params: splitNumericParams(m[1]!) } + } + + if ((m = KITTY_FLAGS_RE.exec(s))) { + return { type: 'kittyKeyboard', flags: parseInt(m[1]!, 10) } + } + + if ((m = CURSOR_POSITION_RE.exec(s))) { + return { + type: 'cursorPosition', + row: parseInt(m[1]!, 10), + col: parseInt(m[2]!, 10), + } + } + + return null + } + + // OSC responses (e.g. OSC 11 ; rgb:... for bg color query) + if (s.startsWith('\x1b]')) { + const m = OSC_RESPONSE_RE.exec(s) + if (m) { + return { type: 'osc', code: parseInt(m[1]!, 10), data: m[2]! } + } + } + + // DCS responses (e.g. XTVERSION: DCS > | name ST) + if (s.startsWith('\x1bP')) { + const m = XTVERSION_RE.exec(s) + if (m) { + return { type: 'xtversion', name: m[1]! } + } + } + + return null +} + +function splitNumericParams(params: string): number[] { + if (!params) return [] + return params.split(';').map(p => parseInt(p, 10)) +} + +export type KeyParseState = { + mode: 'NORMAL' | 'IN_PASTE' + incomplete: string + pasteBuffer: string + // Internal tokenizer instance + _tokenizer?: Tokenizer +} + +export const INITIAL_STATE: KeyParseState = { + mode: 'NORMAL', + incomplete: '', + pasteBuffer: '', +} + +function inputToString(input: Buffer | string): string { + if (Buffer.isBuffer(input)) { + if (input[0]! > 127 && input[1] === undefined) { + ;(input[0] as unknown as number) -= 128 + return '\x1b' + String(input) + } else { + return String(input) + } + } else if (input !== undefined && typeof input !== 'string') { + return String(input) + } else if (!input) { + return '' + } else { + return input + } +} + +export function parseMultipleKeypresses( + prevState: KeyParseState, + input: Buffer | string | null = '', +): [ParsedInput[], KeyParseState] { + const isFlush = input === null + const inputString = isFlush ? '' : inputToString(input) + + // Get or create tokenizer + const tokenizer = prevState._tokenizer ?? createTokenizer({ x10Mouse: true }) + + // Tokenize the input + const tokens = isFlush ? tokenizer.flush() : tokenizer.feed(inputString) + + // Convert tokens to parsed keys, handling paste mode + const keys: ParsedInput[] = [] + let inPaste = prevState.mode === 'IN_PASTE' + let pasteBuffer = prevState.pasteBuffer + + for (const token of tokens) { + if (token.type === 'sequence') { + if (token.value === PASTE_START) { + inPaste = true + pasteBuffer = '' + } else if (token.value === PASTE_END) { + // Always emit a paste key, even for empty pastes. This allows + // downstream handlers to detect empty pastes (e.g., for clipboard + // image handling on macOS). The paste content may be empty string. + keys.push(createPasteKey(pasteBuffer)) + inPaste = false + pasteBuffer = '' + } else if (inPaste) { + // Sequences inside paste are treated as literal text + pasteBuffer += token.value + } else { + const response = parseTerminalResponse(token.value) + if (response) { + keys.push({ kind: 'response', sequence: token.value, response }) + } else { + const mouse = parseMouseEvent(token.value) + if (mouse) { + keys.push(mouse) + } else { + keys.push(parseKeypress(token.value)) + } + } + } + } else if (token.type === 'text') { + if (inPaste) { + pasteBuffer += token.value + } else if ( + /^\[<\d+;\d+;\d+[Mm]$/.test(token.value) || + /^\[M[\x60-\x7f][\x20-\uffff]{2}$/.test(token.value) + ) { + // Orphaned SGR/X10 mouse tail (fullscreen only — mouse tracking is off + // otherwise). A heavy render blocked the event loop past App's 50ms + // flush timer, so the buffered ESC was flushed as a lone Escape and + // the continuation `[ = { + /* xterm/gnome ESC O letter */ + OP: 'f1', + OQ: 'f2', + OR: 'f3', + OS: 'f4', + /* Application keypad mode (numpad digits 0-9) */ + Op: '0', + Oq: '1', + Or: '2', + Os: '3', + Ot: '4', + Ou: '5', + Ov: '6', + Ow: '7', + Ox: '8', + Oy: '9', + /* Application keypad mode (numpad operators) */ + Oj: '*', + Ok: '+', + Ol: ',', + Om: '-', + On: '.', + Oo: '/', + OM: 'return', + /* xterm/rxvt ESC [ number ~ */ + '[11~': 'f1', + '[12~': 'f2', + '[13~': 'f3', + '[14~': 'f4', + /* from Cygwin and used in libuv */ + '[[A': 'f1', + '[[B': 'f2', + '[[C': 'f3', + '[[D': 'f4', + '[[E': 'f5', + /* common */ + '[15~': 'f5', + '[17~': 'f6', + '[18~': 'f7', + '[19~': 'f8', + '[20~': 'f9', + '[21~': 'f10', + '[23~': 'f11', + '[24~': 'f12', + /* xterm ESC [ letter */ + '[A': 'up', + '[B': 'down', + '[C': 'right', + '[D': 'left', + '[E': 'clear', + '[F': 'end', + '[H': 'home', + /* xterm/gnome ESC O letter */ + OA: 'up', + OB: 'down', + OC: 'right', + OD: 'left', + OE: 'clear', + OF: 'end', + OH: 'home', + /* xterm/rxvt ESC [ number ~ */ + '[1~': 'home', + '[2~': 'insert', + '[3~': 'delete', + '[4~': 'end', + '[5~': 'pageup', + '[6~': 'pagedown', + /* putty */ + '[[5~': 'pageup', + '[[6~': 'pagedown', + /* rxvt */ + '[7~': 'home', + '[8~': 'end', + /* rxvt keys with modifiers */ + '[a': 'up', + '[b': 'down', + '[c': 'right', + '[d': 'left', + '[e': 'clear', + + '[2$': 'insert', + '[3$': 'delete', + '[5$': 'pageup', + '[6$': 'pagedown', + '[7$': 'home', + '[8$': 'end', + + Oa: 'up', + Ob: 'down', + Oc: 'right', + Od: 'left', + Oe: 'clear', + + '[2^': 'insert', + '[3^': 'delete', + '[5^': 'pageup', + '[6^': 'pagedown', + '[7^': 'home', + '[8^': 'end', + /* misc. */ + '[Z': 'tab', +} + +export const nonAlphanumericKeys = [ + // Filter out single-character values (digits, operators from numpad) since + // those are printable characters that should produce input + ...Object.values(keyName).filter(v => v.length > 1), + // escape and backspace are assigned directly in parseKeypress (not via the + // keyName map), so the spread above misses them. Without these, ctrl+escape + // via Kitty/modifyOtherKeys leaks the literal word "escape" as input text + // (input-event.ts:58 assigns keypress.name when ctrl is set). + 'escape', + 'backspace', + 'wheelup', + 'wheeldown', + 'mouse', +] + +const isShiftKey = (code: string): boolean => { + return [ + '[a', + '[b', + '[c', + '[d', + '[e', + '[2$', + '[3$', + '[5$', + '[6$', + '[7$', + '[8$', + '[Z', + ].includes(code) +} + +const isCtrlKey = (code: string): boolean => { + return [ + 'Oa', + 'Ob', + 'Oc', + 'Od', + 'Oe', + '[2^', + '[3^', + '[5^', + '[6^', + '[7^', + '[8^', + ].includes(code) +} + +/** + * Decode XTerm-style modifier value to individual flags. + * Modifier encoding: 1 + (shift ? 1 : 0) + (alt ? 2 : 0) + (ctrl ? 4 : 0) + (super ? 8 : 0) + * + * Note: `meta` here means Alt/Option (bit 2). `super` is a distinct + * modifier (bit 8, i.e. Cmd on macOS / Win key). Most legacy terminal + * sequences can't express super — it only arrives via kitty keyboard + * protocol (CSI u) or xterm modifyOtherKeys. + */ +function decodeModifier(modifier: number): { + shift: boolean + meta: boolean + ctrl: boolean + super: boolean +} { + const m = modifier - 1 + return { + shift: !!(m & 1), + meta: !!(m & 2), + ctrl: !!(m & 4), + super: !!(m & 8), + } +} + +/** + * Map keycode to key name for modifyOtherKeys/CSI u sequences. + * Handles both ASCII keycodes and Kitty keyboard protocol functional keys. + * + * Numpad codepoints are from Unicode Private Use Area, defined at: + * https://sw.kovidgoyal.net/kitty/keyboard-protocol/#functional-key-definitions + */ +function keycodeToName(keycode: number): string | undefined { + switch (keycode) { + case 9: + return 'tab' + case 13: + return 'return' + case 27: + return 'escape' + case 32: + return 'space' + case 127: + return 'backspace' + // Kitty keyboard protocol numpad keys (KP_0 through KP_9) + case 57399: + return '0' + case 57400: + return '1' + case 57401: + return '2' + case 57402: + return '3' + case 57403: + return '4' + case 57404: + return '5' + case 57405: + return '6' + case 57406: + return '7' + case 57407: + return '8' + case 57408: + return '9' + case 57409: // KP_DECIMAL + return '.' + case 57410: // KP_DIVIDE + return '/' + case 57411: // KP_MULTIPLY + return '*' + case 57412: // KP_SUBTRACT + return '-' + case 57413: // KP_ADD + return '+' + case 57414: // KP_ENTER + return 'return' + case 57415: // KP_EQUAL + return '=' + default: + // Printable ASCII characters + if (keycode >= 32 && keycode <= 126) { + return String.fromCharCode(keycode).toLowerCase() + } + return undefined + } +} + +export type ParsedKey = { + kind: 'key' + fn: boolean + name: string | undefined + ctrl: boolean + meta: boolean + shift: boolean + option: boolean + super: boolean + sequence: string | undefined + raw: string | undefined + code?: string + isPasted: boolean +} + +/** A terminal response sequence (DECRPM, DA1, OSC reply, etc.) parsed + * out of the input stream. Not user input — consumers should dispatch + * to a response handler. */ +export type ParsedResponse = { + kind: 'response' + /** Raw escape sequence bytes, for debugging/logging */ + sequence: string + response: TerminalResponse +} + +/** SGR mouse event with coordinates. Emitted for clicks, drags, and + * releases (wheel events remain ParsedKey). col/row are 1-indexed + * from the terminal sequence (CSI < btn;col;row M/m). */ +export type ParsedMouse = { + kind: 'mouse' + /** Raw SGR button code. Low 2 bits = button (0=left,1=mid,2=right), + * bit 5 (0x20) = drag/motion, bit 6 (0x40) = wheel. */ + button: number + /** 'press' for M terminator, 'release' for m terminator */ + action: 'press' | 'release' + /** 1-indexed column (from terminal) */ + col: number + /** 1-indexed row (from terminal) */ + row: number + sequence: string +} + +/** Everything that can come out of the input parser: a user keypress/paste, + * a mouse click/drag event, or a terminal response to a query we sent. */ +export type ParsedInput = ParsedKey | ParsedMouse | ParsedResponse + +/** + * Parse an SGR mouse event sequence into a ParsedMouse, or null if not a + * mouse event or if it's a wheel event (wheel stays as ParsedKey for the + * keybinding system). Button bit 0x40 = wheel, bit 0x20 = drag/motion. + */ +function parseMouseEvent(s: string): ParsedMouse | null { + const match = SGR_MOUSE_RE.exec(s) + if (!match) return null + const button = parseInt(match[1]!, 10) + // Wheel events (bit 6 set, low bits 0/1 for up/down) stay as ParsedKey + // so the keybinding system can route them to scroll handlers. + if ((button & 0x40) !== 0) return null + return { + kind: 'mouse', + button, + action: match[4] === 'M' ? 'press' : 'release', + col: parseInt(match[2]!, 10), + row: parseInt(match[3]!, 10), + sequence: s, + } +} + +function parseKeypress(s: string = ''): ParsedKey { + let parts + + const key: ParsedKey = { + kind: 'key', + name: '', + fn: false, + ctrl: false, + meta: false, + shift: false, + option: false, + super: false, + sequence: s, + raw: s, + isPasted: false, + } + + key.sequence = key.sequence || s || key.name + + // Handle CSI u (kitty keyboard protocol): ESC [ codepoint [; modifier] u + // Example: ESC[13;2u = Shift+Enter, ESC[27u = Escape (no modifiers) + let match: RegExpExecArray | null + if ((match = CSI_U_RE.exec(s))) { + const codepoint = parseInt(match[1]!, 10) + // Modifier defaults to 1 (no modifiers) when not present + const modifier = match[2] ? parseInt(match[2], 10) : 1 + const mods = decodeModifier(modifier) + const name = keycodeToName(codepoint) + return { + kind: 'key', + name, + fn: false, + ctrl: mods.ctrl, + meta: mods.meta, + shift: mods.shift, + option: false, + super: mods.super, + sequence: s, + raw: s, + isPasted: false, + } + } + + // Handle xterm modifyOtherKeys: ESC [ 27 ; modifier ; keycode ~ + // Must run before FN_KEY_RE — FN_KEY_RE only allows 2 params before ~ and + // would leave the tail as garbage if it partially matched. + if ((match = MODIFY_OTHER_KEYS_RE.exec(s))) { + const mods = decodeModifier(parseInt(match[1]!, 10)) + const name = keycodeToName(parseInt(match[2]!, 10)) + return { + kind: 'key', + name, + fn: false, + ctrl: mods.ctrl, + meta: mods.meta, + shift: mods.shift, + option: false, + super: mods.super, + sequence: s, + raw: s, + isPasted: false, + } + } + + // SGR mouse wheel events. Click/drag/release events are handled + // earlier by parseMouseEvent and emitted as ParsedMouse, so they + // never reach here. Mask with 0x43 (bits 6+1+0) to check wheel-flag + // + direction while ignoring modifier bits (Shift=0x04, Meta=0x08, + // Ctrl=0x10) — modified wheel events (e.g. Ctrl+scroll, button=80) + // should still be recognized as wheelup/wheeldown. + if ((match = SGR_MOUSE_RE.exec(s))) { + const button = parseInt(match[1]!, 10) + if ((button & 0x43) === 0x40) return createNavKey(s, 'wheelup', false) + if ((button & 0x43) === 0x41) return createNavKey(s, 'wheeldown', false) + // Shouldn't reach here (parseMouseEvent catches non-wheel) but be safe + return createNavKey(s, 'mouse', false) + } + + // X10 mouse: CSI M + 3 raw bytes (Cb+32, Cx+32, Cy+32). Terminals that + // ignore DECSET 1006 (SGR) but honor 1000/1002 emit this legacy encoding. + // Button bits match SGR: 0x40 = wheel, low bit = direction. Non-wheel + // X10 events (clicks/drags) are swallowed here — we only enable mouse + // tracking in alt-screen and only need wheel for ScrollBox. + if (s.length === 6 && s.startsWith('\x1b[M')) { + const button = s.charCodeAt(3) - 32 + if ((button & 0x43) === 0x40) return createNavKey(s, 'wheelup', false) + if ((button & 0x43) === 0x41) return createNavKey(s, 'wheeldown', false) + return createNavKey(s, 'mouse', false) + } + + if (s === '\r') { + key.raw = undefined + key.name = 'return' + } else if (s === '\n') { + key.name = 'enter' + } else if (s === '\t') { + key.name = 'tab' + } else if (s === '\b' || s === '\x1b\b') { + key.name = 'backspace' + key.meta = s.charAt(0) === '\x1b' + } else if (s === '\x7f' || s === '\x1b\x7f') { + key.name = 'backspace' + key.meta = s.charAt(0) === '\x1b' + } else if (s === '\x1b' || s === '\x1b\x1b') { + key.name = 'escape' + key.meta = s.length === 2 + } else if (s === ' ' || s === '\x1b ') { + key.name = 'space' + key.meta = s.length === 2 + } else if (s === '\x1f') { + key.name = '_' + key.ctrl = true + } else if (s <= '\x1a' && s.length === 1) { + key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1) + key.ctrl = true + } else if (s.length === 1 && s >= '0' && s <= '9') { + key.name = 'number' + } else if (s.length === 1 && s >= 'a' && s <= 'z') { + key.name = s + } else if (s.length === 1 && s >= 'A' && s <= 'Z') { + key.name = s.toLowerCase() + key.shift = true + } else if ((parts = META_KEY_CODE_RE.exec(s))) { + key.meta = true + key.shift = /^[A-Z]$/.test(parts[1]!) + } else if ((parts = FN_KEY_RE.exec(s))) { + const segs = [...s] + + if (segs[0] === '\u001b' && segs[1] === '\u001b') { + key.option = true + } + + const code = [parts[1], parts[2], parts[4], parts[6]] + .filter(Boolean) + .join('') + + const modifier = ((parts[3] || parts[5] || 1) as number) - 1 + + key.ctrl = !!(modifier & 4) + key.meta = !!(modifier & 2) + key.super = !!(modifier & 8) + key.shift = !!(modifier & 1) + key.code = code + + key.name = keyName[code] + key.shift = isShiftKey(code) || key.shift + key.ctrl = isCtrlKey(code) || key.ctrl + } + + // iTerm in natural text editing mode + if (key.raw === '\x1Bb') { + key.meta = true + key.name = 'left' + } else if (key.raw === '\x1Bf') { + key.meta = true + key.name = 'right' + } + + switch (s) { + case '\u001b[1~': + return createNavKey(s, 'home', false) + case '\u001b[4~': + return createNavKey(s, 'end', false) + case '\u001b[5~': + return createNavKey(s, 'pageup', false) + case '\u001b[6~': + return createNavKey(s, 'pagedown', false) + case '\u001b[1;5D': + return createNavKey(s, 'left', true) + case '\u001b[1;5C': + return createNavKey(s, 'right', true) + } + + return key +} + +function createNavKey(s: string, name: string, ctrl: boolean): ParsedKey { + return { + kind: 'key', + name, + ctrl, + meta: false, + shift: false, + option: false, + super: false, + fn: false, + sequence: s, + raw: s, + isPasted: false, + } +} diff --git a/ink/reconciler.ts b/ink/reconciler.ts new file mode 100644 index 0000000..f5c6813 --- /dev/null +++ b/ink/reconciler.ts @@ -0,0 +1,512 @@ +/* eslint-disable custom-rules/no-top-level-side-effects */ + +import { appendFileSync } from 'fs' +import createReconciler from 'react-reconciler' +import { getYogaCounters } from 'src/native-ts/yoga-layout/index.js' +import { isEnvTruthy } from '../utils/envUtils.js' +import { + appendChildNode, + clearYogaNodeReferences, + createNode, + createTextNode, + type DOMElement, + type DOMNodeAttribute, + type ElementNames, + insertBeforeNode, + markDirty, + removeChildNode, + setAttribute, + setStyle, + setTextNodeValue, + setTextStyles, + type TextNode, +} from './dom.js' +import { Dispatcher } from './events/dispatcher.js' +import { EVENT_HANDLER_PROPS } from './events/event-handlers.js' +import { getFocusManager, getRootNode } from './focus.js' +import { LayoutDisplay } from './layout/node.js' +import applyStyles, { type Styles, type TextStyles } from './styles.js' + +// We need to conditionally perform devtools connection to avoid +// accidentally breaking other third-party code. +// See https://github.com/vadimdemedes/ink/issues/384 +if (process.env.NODE_ENV === 'development') { + try { + // eslint-disable-next-line custom-rules/no-top-level-dynamic-import -- dev-only; NODE_ENV check is DCE'd in production + void import('./devtools.js') + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + if (error.code === 'ERR_MODULE_NOT_FOUND') { + // biome-ignore lint/suspicious/noConsole: intentional warning + console.warn( + ` +The environment variable DEV is set to true, so Ink tried to import \`react-devtools-core\`, +but this failed as it was not installed. Debugging with React Devtools requires it. + +To install use this command: + +$ npm install --save-dev react-devtools-core + `.trim() + '\n', + ) + } else { + // eslint-disable-next-line @typescript-eslint/only-throw-error + throw error + } + } +} + +// -- + +type AnyObject = Record + +const diff = (before: AnyObject, after: AnyObject): AnyObject | undefined => { + if (before === after) { + return + } + + if (!before) { + return after + } + + const changed: AnyObject = {} + let isChanged = false + + for (const key of Object.keys(before)) { + const isDeleted = after ? !Object.hasOwn(after, key) : true + + if (isDeleted) { + changed[key] = undefined + isChanged = true + } + } + + if (after) { + for (const key of Object.keys(after)) { + if (after[key] !== before[key]) { + changed[key] = after[key] + isChanged = true + } + } + } + + return isChanged ? changed : undefined +} + +const cleanupYogaNode = (node: DOMElement | TextNode): void => { + const yogaNode = node.yogaNode + if (yogaNode) { + yogaNode.unsetMeasureFunc() + // Clear all references BEFORE freeing to prevent other code from + // accessing freed WASM memory during concurrent operations + clearYogaNodeReferences(node) + yogaNode.freeRecursive() + } +} + +// -- + +type Props = Record + +type HostContext = { + isInsideText: boolean +} + +function setEventHandler(node: DOMElement, key: string, value: unknown): void { + if (!node._eventHandlers) { + node._eventHandlers = {} + } + node._eventHandlers[key] = value +} + +function applyProp(node: DOMElement, key: string, value: unknown): void { + if (key === 'children') return + + if (key === 'style') { + setStyle(node, value as Styles) + if (node.yogaNode) { + applyStyles(node.yogaNode, value as Styles) + } + return + } + + if (key === 'textStyles') { + node.textStyles = value as TextStyles + return + } + + if (EVENT_HANDLER_PROPS.has(key)) { + setEventHandler(node, key, value) + return + } + + setAttribute(node, key, value as DOMNodeAttribute) +} + +// -- + +// react-reconciler's Fiber shape — only the fields we walk. The 5th arg to +// createInstance is the Fiber (`workInProgress` in react-reconciler.dev.js). +// _debugOwner is the component that rendered this element (dev builds only); +// return is the parent fiber (always present). We prefer _debugOwner since it +// skips past Box/Text wrappers to the actual named component. +type FiberLike = { + elementType?: { displayName?: string; name?: string } | string | null + _debugOwner?: FiberLike | null + return?: FiberLike | null +} + +export function getOwnerChain(fiber: unknown): string[] { + const chain: string[] = [] + const seen = new Set() + let cur = fiber as FiberLike | null | undefined + for (let i = 0; cur && i < 50; i++) { + if (seen.has(cur)) break + seen.add(cur) + const t = cur.elementType + const name = + typeof t === 'function' + ? (t as { displayName?: string; name?: string }).displayName || + (t as { displayName?: string; name?: string }).name + : typeof t === 'string' + ? undefined // host element (ink-box etc) — skip + : t?.displayName || t?.name + if (name && name !== chain[chain.length - 1]) chain.push(name) + cur = cur._debugOwner ?? cur.return + } + return chain +} + +let debugRepaints: boolean | undefined +export function isDebugRepaintsEnabled(): boolean { + if (debugRepaints === undefined) { + debugRepaints = isEnvTruthy(process.env.CLAUDE_CODE_DEBUG_REPAINTS) + } + return debugRepaints +} + +export const dispatcher = new Dispatcher() + +// --- COMMIT INSTRUMENTATION (temp debugging) --- +// eslint-disable-next-line custom-rules/no-process-env-top-level -- debug instrumentation, read-once is fine +const COMMIT_LOG = process.env.CLAUDE_CODE_COMMIT_LOG +let _commits = 0 +let _lastLog = 0 +let _lastCommitAt = 0 +let _maxGapMs = 0 +let _createCount = 0 +let _prepareAt = 0 +// --- END --- + +// --- SCROLL PROFILING (bench/scroll-e2e.sh reads via getLastYogaMs) --- +// Set by onComputeLayout wrapper in ink.tsx; read by onRender for phases. +let _lastYogaMs = 0 +let _lastCommitMs = 0 +let _commitStart = 0 +export function recordYogaMs(ms: number): void { + _lastYogaMs = ms +} +export function getLastYogaMs(): number { + return _lastYogaMs +} +export function markCommitStart(): void { + _commitStart = performance.now() +} +export function getLastCommitMs(): number { + return _lastCommitMs +} +export function resetProfileCounters(): void { + _lastYogaMs = 0 + _lastCommitMs = 0 + _commitStart = 0 +} +// --- END --- + +const reconciler = createReconciler< + ElementNames, + Props, + DOMElement, + DOMElement, + TextNode, + DOMElement, + unknown, + unknown, + DOMElement, + HostContext, + null, // UpdatePayload - not used in React 19 + NodeJS.Timeout, + -1, + null +>({ + getRootHostContext: () => ({ isInsideText: false }), + prepareForCommit: () => { + if (COMMIT_LOG) _prepareAt = performance.now() + return null + }, + preparePortalMount: () => null, + clearContainer: () => false, + resetAfterCommit(rootNode) { + _lastCommitMs = _commitStart > 0 ? performance.now() - _commitStart : 0 + _commitStart = 0 + if (COMMIT_LOG) { + const now = performance.now() + _commits++ + const gap = _lastCommitAt > 0 ? now - _lastCommitAt : 0 + if (gap > _maxGapMs) _maxGapMs = gap + _lastCommitAt = now + const reconcileMs = _prepareAt > 0 ? now - _prepareAt : 0 + if (gap > 30 || reconcileMs > 20 || _createCount > 50) { + // eslint-disable-next-line custom-rules/no-sync-fs -- debug instrumentation + appendFileSync( + COMMIT_LOG, + `${now.toFixed(1)} gap=${gap.toFixed(1)}ms reconcile=${reconcileMs.toFixed(1)}ms creates=${_createCount}\n`, + ) + } + _createCount = 0 + if (now - _lastLog > 1000) { + // eslint-disable-next-line custom-rules/no-sync-fs -- debug instrumentation + appendFileSync( + COMMIT_LOG, + `${now.toFixed(1)} commits=${_commits}/s maxGap=${_maxGapMs.toFixed(1)}ms\n`, + ) + _commits = 0 + _maxGapMs = 0 + _lastLog = now + } + } + const _t0 = COMMIT_LOG ? performance.now() : 0 + if (typeof rootNode.onComputeLayout === 'function') { + rootNode.onComputeLayout() + } + if (COMMIT_LOG) { + const layoutMs = performance.now() - _t0 + if (layoutMs > 20) { + const c = getYogaCounters() + // eslint-disable-next-line custom-rules/no-sync-fs -- debug instrumentation + appendFileSync( + COMMIT_LOG, + `${_t0.toFixed(1)} SLOW_YOGA ${layoutMs.toFixed(1)}ms visited=${c.visited} measured=${c.measured} hits=${c.cacheHits} live=${c.live}\n`, + ) + } + } + + if (process.env.NODE_ENV === 'test') { + if (rootNode.childNodes.length === 0 && rootNode.hasRenderedContent) { + return + } + if (rootNode.childNodes.length > 0) { + rootNode.hasRenderedContent = true + } + rootNode.onImmediateRender?.() + return + } + + const _tr = COMMIT_LOG ? performance.now() : 0 + rootNode.onRender?.() + if (COMMIT_LOG) { + const renderMs = performance.now() - _tr + if (renderMs > 10) { + // eslint-disable-next-line custom-rules/no-sync-fs -- debug instrumentation + appendFileSync( + COMMIT_LOG, + `${_tr.toFixed(1)} SLOW_PAINT ${renderMs.toFixed(1)}ms\n`, + ) + } + } + }, + getChildHostContext( + parentHostContext: HostContext, + type: ElementNames, + ): HostContext { + const previousIsInsideText = parentHostContext.isInsideText + const isInsideText = + type === 'ink-text' || type === 'ink-virtual-text' || type === 'ink-link' + + if (previousIsInsideText === isInsideText) { + return parentHostContext + } + + return { isInsideText } + }, + shouldSetTextContent: () => false, + createInstance( + originalType: ElementNames, + newProps: Props, + _root: DOMElement, + hostContext: HostContext, + internalHandle?: unknown, + ): DOMElement { + if (hostContext.isInsideText && originalType === 'ink-box') { + throw new Error(` can't be nested inside component`) + } + + const type = + originalType === 'ink-text' && hostContext.isInsideText + ? 'ink-virtual-text' + : originalType + + const node = createNode(type) + if (COMMIT_LOG) _createCount++ + + for (const [key, value] of Object.entries(newProps)) { + applyProp(node, key, value) + } + + if (isDebugRepaintsEnabled()) { + node.debugOwnerChain = getOwnerChain(internalHandle) + } + + return node + }, + createTextInstance( + text: string, + _root: DOMElement, + hostContext: HostContext, + ): TextNode { + if (!hostContext.isInsideText) { + throw new Error( + `Text string "${text}" must be rendered inside component`, + ) + } + + return createTextNode(text) + }, + resetTextContent() {}, + hideTextInstance(node) { + setTextNodeValue(node, '') + }, + unhideTextInstance(node, text) { + setTextNodeValue(node, text) + }, + getPublicInstance: (instance): DOMElement => instance as DOMElement, + hideInstance(node) { + node.isHidden = true + node.yogaNode?.setDisplay(LayoutDisplay.None) + markDirty(node) + }, + unhideInstance(node) { + node.isHidden = false + node.yogaNode?.setDisplay(LayoutDisplay.Flex) + markDirty(node) + }, + appendInitialChild: appendChildNode, + appendChild: appendChildNode, + insertBefore: insertBeforeNode, + finalizeInitialChildren( + _node: DOMElement, + _type: ElementNames, + props: Props, + ): boolean { + return props['autoFocus'] === true + }, + commitMount(node: DOMElement): void { + getFocusManager(node).handleAutoFocus(node) + }, + isPrimaryRenderer: true, + supportsMutation: true, + supportsPersistence: false, + supportsHydration: false, + scheduleTimeout: setTimeout, + cancelTimeout: clearTimeout, + noTimeout: -1, + getCurrentUpdatePriority: () => dispatcher.currentUpdatePriority, + beforeActiveInstanceBlur() {}, + afterActiveInstanceBlur() {}, + detachDeletedInstance() {}, + getInstanceFromNode: () => null, + prepareScopeUpdate() {}, + getInstanceFromScope: () => null, + appendChildToContainer: appendChildNode, + insertInContainerBefore: insertBeforeNode, + removeChildFromContainer(node: DOMElement, removeNode: DOMElement): void { + removeChildNode(node, removeNode) + cleanupYogaNode(removeNode) + getFocusManager(node).handleNodeRemoved(removeNode, node) + }, + // React 19 commitUpdate receives old and new props directly instead of an updatePayload + commitUpdate( + node: DOMElement, + _type: ElementNames, + oldProps: Props, + newProps: Props, + ): void { + const props = diff(oldProps, newProps) + const style = diff(oldProps['style'] as Styles, newProps['style'] as Styles) + + if (props) { + for (const [key, value] of Object.entries(props)) { + if (key === 'style') { + setStyle(node, value as Styles) + continue + } + + if (key === 'textStyles') { + setTextStyles(node, value as TextStyles) + continue + } + + if (EVENT_HANDLER_PROPS.has(key)) { + setEventHandler(node, key, value) + continue + } + + setAttribute(node, key, value as DOMNodeAttribute) + } + } + + if (style && node.yogaNode) { + applyStyles(node.yogaNode, style, newProps['style'] as Styles) + } + }, + commitTextUpdate(node: TextNode, _oldText: string, newText: string): void { + setTextNodeValue(node, newText) + }, + removeChild(node, removeNode) { + removeChildNode(node, removeNode) + cleanupYogaNode(removeNode) + if (removeNode.nodeName !== '#text') { + const root = getRootNode(node) + root.focusManager!.handleNodeRemoved(removeNode, root) + } + }, + // React 19 required methods + maySuspendCommit(): boolean { + return false + }, + preloadInstance(): boolean { + return true + }, + startSuspendingCommit(): void {}, + suspendInstance(): void {}, + waitForCommitToBeReady(): null { + return null + }, + NotPendingTransition: null, + HostTransitionContext: { + $$typeof: Symbol.for('react.context'), + _currentValue: null, + } as never, + setCurrentUpdatePriority(newPriority: number): void { + dispatcher.currentUpdatePriority = newPriority + }, + resolveUpdatePriority(): number { + return dispatcher.resolveEventPriority() + }, + resetFormInstance(): void {}, + requestPostPaintCallback(): void {}, + shouldAttemptEagerTransition(): boolean { + return false + }, + trackSchedulerEvent(): void {}, + resolveEventType(): string | null { + return dispatcher.currentEvent?.type ?? null + }, + resolveEventTimeStamp(): number { + return dispatcher.currentEvent?.timeStamp ?? -1.1 + }, +}) + +// Wire the reconciler's discreteUpdates into the dispatcher. +// This breaks the import cycle: dispatcher.ts doesn't import reconciler.ts. +dispatcher.discreteUpdates = reconciler.discreteUpdates.bind(reconciler) + +export default reconciler diff --git a/ink/render-border.ts b/ink/render-border.ts new file mode 100644 index 0000000..ec3df8f --- /dev/null +++ b/ink/render-border.ts @@ -0,0 +1,231 @@ +import chalk from 'chalk' +import cliBoxes, { type Boxes, type BoxStyle } from 'cli-boxes' +import { applyColor } from './colorize.js' +import type { DOMNode } from './dom.js' +import type Output from './output.js' +import { stringWidth } from './stringWidth.js' +import type { Color } from './styles.js' + +export type BorderTextOptions = { + content: string // Pre-rendered string with ANSI color codes + position: 'top' | 'bottom' + align: 'start' | 'end' | 'center' + offset?: number // Only used with 'start' or 'end' alignment. Number of characters from the edge. +} + +export const CUSTOM_BORDER_STYLES = { + dashed: { + top: '╌', + left: '╎', + right: '╎', + bottom: '╌', + // there aren't any line-drawing characters for dashes unfortunately + topLeft: ' ', + topRight: ' ', + bottomLeft: ' ', + bottomRight: ' ', + }, +} as const + +export type BorderStyle = + | keyof Boxes + | keyof typeof CUSTOM_BORDER_STYLES + | BoxStyle + +function embedTextInBorder( + borderLine: string, + text: string, + align: 'start' | 'end' | 'center', + offset: number = 0, + borderChar: string, +): [before: string, text: string, after: string] { + const textLength = stringWidth(text) + const borderLength = borderLine.length + + if (textLength >= borderLength - 2) { + return ['', text.substring(0, borderLength), ''] + } + + let position: number + if (align === 'center') { + position = Math.floor((borderLength - textLength) / 2) + } else if (align === 'start') { + position = offset + 1 // +1 to account for corner character + } else { + // align === 'end' + position = borderLength - textLength - offset - 1 // -1 for corner character + } + + // Ensure position is valid + position = Math.max(1, Math.min(position, borderLength - textLength - 1)) + + const before = borderLine.substring(0, 1) + borderChar.repeat(position - 1) + const after = + borderChar.repeat(borderLength - position - textLength - 1) + + borderLine.substring(borderLength - 1) + + return [before, text, after] +} + +function styleBorderLine( + line: string, + color: Color | undefined, + dim: boolean | undefined, +): string { + let styled = applyColor(line, color) + if (dim) { + styled = chalk.dim(styled) + } + return styled +} + +const renderBorder = ( + x: number, + y: number, + node: DOMNode, + output: Output, +): void => { + if (node.style.borderStyle) { + const width = Math.floor(node.yogaNode!.getComputedWidth()) + const height = Math.floor(node.yogaNode!.getComputedHeight()) + const box = + typeof node.style.borderStyle === 'string' + ? (CUSTOM_BORDER_STYLES[ + node.style.borderStyle as keyof typeof CUSTOM_BORDER_STYLES + ] ?? cliBoxes[node.style.borderStyle as keyof Boxes]) + : node.style.borderStyle + + const topBorderColor = node.style.borderTopColor ?? node.style.borderColor + const bottomBorderColor = + node.style.borderBottomColor ?? node.style.borderColor + const leftBorderColor = node.style.borderLeftColor ?? node.style.borderColor + const rightBorderColor = + node.style.borderRightColor ?? node.style.borderColor + + const dimTopBorderColor = + node.style.borderTopDimColor ?? node.style.borderDimColor + + const dimBottomBorderColor = + node.style.borderBottomDimColor ?? node.style.borderDimColor + + const dimLeftBorderColor = + node.style.borderLeftDimColor ?? node.style.borderDimColor + + const dimRightBorderColor = + node.style.borderRightDimColor ?? node.style.borderDimColor + + const showTopBorder = node.style.borderTop !== false + const showBottomBorder = node.style.borderBottom !== false + const showLeftBorder = node.style.borderLeft !== false + const showRightBorder = node.style.borderRight !== false + + const contentWidth = Math.max( + 0, + width - (showLeftBorder ? 1 : 0) - (showRightBorder ? 1 : 0), + ) + + const topBorderLine = showTopBorder + ? (showLeftBorder ? box.topLeft : '') + + box.top.repeat(contentWidth) + + (showRightBorder ? box.topRight : '') + : '' + + // Handle text in top border + let topBorder: string | undefined + if (showTopBorder && node.style.borderText?.position === 'top') { + const [before, text, after] = embedTextInBorder( + topBorderLine, + node.style.borderText.content, + node.style.borderText.align, + node.style.borderText.offset, + box.top, + ) + topBorder = + styleBorderLine(before, topBorderColor, dimTopBorderColor) + + text + + styleBorderLine(after, topBorderColor, dimTopBorderColor) + } else if (showTopBorder) { + topBorder = styleBorderLine( + topBorderLine, + topBorderColor, + dimTopBorderColor, + ) + } + + let verticalBorderHeight = height + + if (showTopBorder) { + verticalBorderHeight -= 1 + } + + if (showBottomBorder) { + verticalBorderHeight -= 1 + } + + verticalBorderHeight = Math.max(0, verticalBorderHeight) + + let leftBorder = (applyColor(box.left, leftBorderColor) + '\n').repeat( + verticalBorderHeight, + ) + + if (dimLeftBorderColor) { + leftBorder = chalk.dim(leftBorder) + } + + let rightBorder = (applyColor(box.right, rightBorderColor) + '\n').repeat( + verticalBorderHeight, + ) + + if (dimRightBorderColor) { + rightBorder = chalk.dim(rightBorder) + } + + const bottomBorderLine = showBottomBorder + ? (showLeftBorder ? box.bottomLeft : '') + + box.bottom.repeat(contentWidth) + + (showRightBorder ? box.bottomRight : '') + : '' + + // Handle text in bottom border + let bottomBorder: string | undefined + if (showBottomBorder && node.style.borderText?.position === 'bottom') { + const [before, text, after] = embedTextInBorder( + bottomBorderLine, + node.style.borderText.content, + node.style.borderText.align, + node.style.borderText.offset, + box.bottom, + ) + bottomBorder = + styleBorderLine(before, bottomBorderColor, dimBottomBorderColor) + + text + + styleBorderLine(after, bottomBorderColor, dimBottomBorderColor) + } else if (showBottomBorder) { + bottomBorder = styleBorderLine( + bottomBorderLine, + bottomBorderColor, + dimBottomBorderColor, + ) + } + + const offsetY = showTopBorder ? 1 : 0 + + if (topBorder) { + output.write(x, y, topBorder) + } + + if (showLeftBorder) { + output.write(x, y + offsetY, leftBorder) + } + + if (showRightBorder) { + output.write(x + width - 1, y + offsetY, rightBorder) + } + + if (bottomBorder) { + output.write(x, y + height - 1, bottomBorder) + } + } +} + +export default renderBorder diff --git a/ink/render-node-to-output.ts b/ink/render-node-to-output.ts new file mode 100644 index 0000000..73bbbbe --- /dev/null +++ b/ink/render-node-to-output.ts @@ -0,0 +1,1462 @@ +import indentString from 'indent-string' +import { applyTextStyles } from './colorize.js' +import type { DOMElement } from './dom.js' +import getMaxWidth from './get-max-width.js' +import type { Rectangle } from './layout/geometry.js' +import { LayoutDisplay, LayoutEdge, type LayoutNode } from './layout/node.js' +import { nodeCache, pendingClears } from './node-cache.js' +import type Output from './output.js' +import renderBorder from './render-border.js' +import type { Screen } from './screen.js' +import { + type StyledSegment, + squashTextNodesToSegments, +} from './squash-text-nodes.js' +import type { Color } from './styles.js' +import { isXtermJs } from './terminal.js' +import { widestLine } from './widest-line.js' +import wrapText from './wrap-text.js' + +// Matches detectXtermJsWheel() in ScrollKeybindingHandler.tsx — the curve +// and drain must agree on terminal detection. TERM_PROGRAM check is the sync +// fallback; isXtermJs() is the authoritative XTVERSION-probe result. +function isXtermJsHost(): boolean { + return process.env.TERM_PROGRAM === 'vscode' || isXtermJs() +} + +// Per-frame scratch: set when any node's yoga position/size differs from +// its cached value, or a child was removed. Read by ink.tsx to decide +// whether the full-damage sledgehammer (PR #20120) is needed this frame. +// Applies on both alt-screen and main-screen. Steady-state frames +// (spinner tick, clock tick, text append into a fixed-height box) don't +// shift layout → narrow damage bounds → O(changed cells) diff instead of +// O(rows×cols). +let layoutShifted = false + +export function resetLayoutShifted(): void { + layoutShifted = false +} + +export function didLayoutShift(): boolean { + return layoutShifted +} + +// DECSTBM scroll optimization hint. When a ScrollBox's scrollTop changes +// between frames (and nothing else moved), log-update.ts can emit a +// hardware scroll (DECSTBM + SU/SD) instead of rewriting the whole +// viewport. top/bottom are 0-indexed inclusive screen rows; delta > 0 = +// content moved up (scrollTop increased, CSI n S). +export type ScrollHint = { top: number; bottom: number; delta: number } +let scrollHint: ScrollHint | null = null + +// Rects of position:absolute nodes from the PREVIOUS frame, used by +// ScrollBox's blit+shift third-pass repair (see usage site). Recorded at +// three paths — full-render nodeCache.set, node-level blit early-return, +// blitEscapingAbsoluteDescendants — so clean-overlay consecutive scrolls +// still have the rect. +let absoluteRectsPrev: Rectangle[] = [] +let absoluteRectsCur: Rectangle[] = [] + +export function resetScrollHint(): void { + scrollHint = null + absoluteRectsPrev = absoluteRectsCur + absoluteRectsCur = [] +} + +export function getScrollHint(): ScrollHint | null { + return scrollHint +} + +// The ScrollBox DOM node (if any) with pendingScrollDelta left after this +// frame's drain. renderer.ts calls markDirty(it) post-render so the NEXT +// frame's root blit check fails and we descend to continue draining. +// Without this, after the scrollbox's dirty flag is cleared (line ~721), +// the next frame blits root and never reaches the scrollbox — drain stalls. +let scrollDrainNode: DOMElement | null = null + +export function resetScrollDrainNode(): void { + scrollDrainNode = null +} + +export function getScrollDrainNode(): DOMElement | null { + return scrollDrainNode +} + +// At-bottom follow scroll event this frame. When streaming content +// triggers scrollTop = maxScroll, the ScrollBox records the delta + +// viewport bounds here. ink.tsx consumes it post-render to translate any active +// text selection by -delta so the highlight stays anchored to the TEXT +// (native terminal behavior — the selection walks up the screen as content +// scrolls, eventually clipping at the top). The frontFrame screen buffer +// still holds the old content at that point — captureScrolledRows reads +// from it before the front/back swap to preserve the text for copy. +export type FollowScroll = { + delta: number + viewportTop: number + viewportBottom: number +} +let followScroll: FollowScroll | null = null + +export function consumeFollowScroll(): FollowScroll | null { + const f = followScroll + followScroll = null + return f +} + +// ── Native terminal drain (iTerm2/Ghostty/etc. — proportional events) ── +// Minimum rows applied per frame. Above this, drain is proportional (~3/4 +// of remaining) so big bursts catch up in log₄ frames while the tail +// decelerates smoothly. Hard cap is innerHeight-1 so DECSTBM hint fires. +const SCROLL_MIN_PER_FRAME = 4 + +// ── xterm.js (VS Code) smooth drain ── +// Low pending (≤5) drains ALL in one frame — slow wheel clicks should be +// instant (click → visible jump → done), not micro-stutter 1-row frames. +// Higher pending drains at a small fixed step so fast-scroll animation +// stays smooth (no big jumps). Pending >MAX snaps excess. +const SCROLL_INSTANT_THRESHOLD = 5 // ≤ this: drain all at once +const SCROLL_HIGH_PENDING = 12 // threshold for HIGH step +const SCROLL_STEP_MED = 2 // pending (INSTANT, HIGH): catch-up +const SCROLL_STEP_HIGH = 3 // pending ≥ HIGH: fast flick +const SCROLL_MAX_PENDING = 30 // snap excess beyond this + +// xterm.js adaptive drain. Returns rows applied; mutates pendingScrollDelta. +function drainAdaptive( + node: DOMElement, + pending: number, + innerHeight: number, +): number { + const sign = pending > 0 ? 1 : -1 + let abs = Math.abs(pending) + let applied = 0 + // Snap excess beyond animation window so big flicks don't coast. + if (abs > SCROLL_MAX_PENDING) { + applied += sign * (abs - SCROLL_MAX_PENDING) + abs = SCROLL_MAX_PENDING + } + // ≤5: drain all (slow click = instant). Above: small fixed step. + const step = + abs <= SCROLL_INSTANT_THRESHOLD + ? abs + : abs < SCROLL_HIGH_PENDING + ? SCROLL_STEP_MED + : SCROLL_STEP_HIGH + applied += sign * step + const rem = abs - step + // Cap total at innerHeight-1 so DECSTBM blit+shift fast path fires + // (matches drainProportional). Excess stays in pendingScrollDelta. + const cap = Math.max(1, innerHeight - 1) + const totalAbs = Math.abs(applied) + if (totalAbs > cap) { + const excess = totalAbs - cap + node.pendingScrollDelta = sign * (rem + excess) + return sign * cap + } + node.pendingScrollDelta = rem > 0 ? sign * rem : undefined + return applied +} + +// Native proportional drain. step = max(MIN, floor(abs*3/4)), capped at +// innerHeight-1 so DECSTBM + blit+shift fast path fire. +function drainProportional( + node: DOMElement, + pending: number, + innerHeight: number, +): number { + const abs = Math.abs(pending) + const cap = Math.max(1, innerHeight - 1) + const step = Math.min(cap, Math.max(SCROLL_MIN_PER_FRAME, (abs * 3) >> 2)) + if (abs <= step) { + node.pendingScrollDelta = undefined + return pending + } + const applied = pending > 0 ? step : -step + node.pendingScrollDelta = pending - applied + return applied +} + +// OSC 8 hyperlink escape sequences. Empty params (;;) — ansi-tokenize only +// recognizes this exact prefix. The id= param (for grouping wrapped lines) +// is added at terminal-output time in termio/osc.ts link(). +const OSC = '\u001B]' +const BEL = '\u0007' + +function wrapWithOsc8Link(text: string, url: string): string { + return `${OSC}8;;${url}${BEL}${text}${OSC}8;;${BEL}` +} + +/** + * Build a mapping from each character position in the plain text to its segment index. + * Returns an array where charToSegment[i] is the segment index for character i. + */ +function buildCharToSegmentMap(segments: StyledSegment[]): number[] { + const map: number[] = [] + for (let i = 0; i < segments.length; i++) { + const len = segments[i]!.text.length + for (let j = 0; j < len; j++) { + map.push(i) + } + } + return map +} + +/** + * Apply styles to wrapped text by mapping each character back to its original segment. + * This preserves per-segment styles even when text wraps across lines. + * + * @param trimEnabled - Whether whitespace trimming is enabled (wrap-trim mode). + * When true, we skip whitespace in the original that was trimmed from the output. + * When false (wrap mode), all whitespace is preserved so no skipping is needed. + */ +function applyStylesToWrappedText( + wrappedPlain: string, + segments: StyledSegment[], + charToSegment: number[], + originalPlain: string, + trimEnabled: boolean = false, +): string { + const lines = wrappedPlain.split('\n') + const resultLines: string[] = [] + + let charIndex = 0 + for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) { + const line = lines[lineIdx]! + + // In trim mode, skip leading whitespace that was trimmed from this line. + // Only skip if the original has whitespace but the output line doesn't start + // with whitespace (meaning it was trimmed). If both have whitespace, the + // whitespace was preserved and we shouldn't skip. + if (trimEnabled && line.length > 0) { + const lineStartsWithWhitespace = /\s/.test(line[0]!) + const originalHasWhitespace = + charIndex < originalPlain.length && /\s/.test(originalPlain[charIndex]!) + + // Only skip if original has whitespace but line doesn't + if (originalHasWhitespace && !lineStartsWithWhitespace) { + while ( + charIndex < originalPlain.length && + /\s/.test(originalPlain[charIndex]!) + ) { + charIndex++ + } + } + } + + let styledLine = '' + let runStart = 0 + let runSegmentIndex = charToSegment[charIndex] ?? 0 + + for (let i = 0; i < line.length; i++) { + const currentSegmentIndex = charToSegment[charIndex] ?? runSegmentIndex + + if (currentSegmentIndex !== runSegmentIndex) { + // Flush the current run + const runText = line.slice(runStart, i) + const segment = segments[runSegmentIndex] + if (segment) { + let styled = applyTextStyles(runText, segment.styles) + if (segment.hyperlink) { + styled = wrapWithOsc8Link(styled, segment.hyperlink) + } + styledLine += styled + } else { + styledLine += runText + } + runStart = i + runSegmentIndex = currentSegmentIndex + } + + charIndex++ + } + + // Flush the final run + const runText = line.slice(runStart) + const segment = segments[runSegmentIndex] + if (segment) { + let styled = applyTextStyles(runText, segment.styles) + if (segment.hyperlink) { + styled = wrapWithOsc8Link(styled, segment.hyperlink) + } + styledLine += styled + } else { + styledLine += runText + } + + resultLines.push(styledLine) + + // Skip newline character in original that corresponds to this line break. + // This is needed when the original text contains actual newlines (not just + // wrapping-inserted newlines). Without this, charIndex gets out of sync + // because the newline is in originalPlain/charToSegment but not in the + // split lines. + if (charIndex < originalPlain.length && originalPlain[charIndex] === '\n') { + charIndex++ + } + + // In trim mode, skip whitespace that was replaced by newline when wrapping. + // We skip whitespace in the original until we reach a character that matches + // the first character of the next line. This handles cases like: + // - "AB \tD" wrapped to "AB\n\tD" - skip spaces until we hit the tab + // In non-trim mode, whitespace is preserved so no skipping is needed. + if (trimEnabled && lineIdx < lines.length - 1) { + const nextLine = lines[lineIdx + 1]! + const nextLineFirstChar = nextLine.length > 0 ? nextLine[0] : null + + // Skip whitespace until we hit a char that matches the next line's first char + while ( + charIndex < originalPlain.length && + /\s/.test(originalPlain[charIndex]!) + ) { + // Stop if we found the character that starts the next line + if ( + nextLineFirstChar !== null && + originalPlain[charIndex] === nextLineFirstChar + ) { + break + } + charIndex++ + } + } + } + + return resultLines.join('\n') +} + +/** + * Wrap text and record which output lines are soft-wrap continuations + * (i.e. the `\n` before them was inserted by word-wrap, not in the + * source). wrapAnsi already processes each input line independently, so + * wrapping per-input-line here gives identical output to a single + * whole-string wrap while letting us mark per-piece provenance. + * Truncate modes never add newlines (cli-truncate is whole-string) so + * they fall through with softWrap undefined — no tracking, no behavior + * change from the pre-softWrap path. + */ +function wrapWithSoftWrap( + plainText: string, + maxWidth: number, + textWrap: Parameters[2], +): { wrapped: string; softWrap: boolean[] | undefined } { + if (textWrap !== 'wrap' && textWrap !== 'wrap-trim') { + return { + wrapped: wrapText(plainText, maxWidth, textWrap), + softWrap: undefined, + } + } + const origLines = plainText.split('\n') + const outLines: string[] = [] + const softWrap: boolean[] = [] + for (const orig of origLines) { + const pieces = wrapText(orig, maxWidth, textWrap).split('\n') + for (let i = 0; i < pieces.length; i++) { + outLines.push(pieces[i]!) + softWrap.push(i > 0) + } + } + return { wrapped: outLines.join('\n'), softWrap } +} + +// If parent container is ``, text nodes will be treated as separate nodes in +// the tree and will have their own coordinates in the layout. +// To ensure text nodes are aligned correctly, take X and Y of the first text node +// and use it as offset for the rest of the nodes +// Only first node is taken into account, because other text nodes can't have margin or padding, +// so their coordinates will be relative to the first node anyway +function applyPaddingToText( + node: DOMElement, + text: string, + softWrap?: boolean[], +): string { + const yogaNode = node.childNodes[0]?.yogaNode + + if (yogaNode) { + const offsetX = yogaNode.getComputedLeft() + const offsetY = yogaNode.getComputedTop() + text = '\n'.repeat(offsetY) + indentString(text, offsetX) + if (softWrap && offsetY > 0) { + // Prepend `false` for each padding line so indices stay aligned + // with text.split('\n'). Mutate in place — caller owns the array. + softWrap.unshift(...Array(offsetY).fill(false)) + } + } + + return text +} + +// After nodes are laid out, render each to output object, which later gets rendered to terminal +function renderNodeToOutput( + node: DOMElement, + output: Output, + { + offsetX = 0, + offsetY = 0, + prevScreen, + skipSelfBlit = false, + inheritedBackgroundColor, + }: { + offsetX?: number + offsetY?: number + prevScreen: Screen | undefined + // Force this node to descend instead of blitting its own rect, while + // still passing prevScreen to children. Used for non-opaque absolute + // overlays over a dirty clipped region: the overlay's full rect has + // transparent gaps (stale underlying content in prevScreen), but its + // opaque descendants' narrower rects are safe to blit. + skipSelfBlit?: boolean + inheritedBackgroundColor?: Color + }, +): void { + const { yogaNode } = node + + if (yogaNode) { + if (yogaNode.getDisplay() === LayoutDisplay.None) { + // Clear old position if node was visible before becoming hidden + if (node.dirty) { + const cached = nodeCache.get(node) + if (cached) { + output.clear({ + x: Math.floor(cached.x), + y: Math.floor(cached.y), + width: Math.floor(cached.width), + height: Math.floor(cached.height), + }) + // Drop descendants' cache too — hideInstance's markDirty walks UP + // only, so descendants' .dirty stays false. Their nodeCache entries + // survive with pre-hide rects. On unhide, if position didn't shift, + // the blit check at line ~432 passes and copies EMPTY cells from + // prevScreen (cleared here) → content vanishes. + dropSubtreeCache(node) + layoutShifted = true + } + } + return + } + + // Left and top positions in Yoga are relative to their parent node + const x = offsetX + yogaNode.getComputedLeft() + const yogaTop = yogaNode.getComputedTop() + let y = offsetY + yogaTop + const width = yogaNode.getComputedWidth() + const height = yogaNode.getComputedHeight() + + // Absolute-positioned overlays (e.g. autocomplete menus with bottom='100%') + // can compute negative screen y when they extend above the viewport. Without + // clamping, setCellAt drops cells at y<0, clipping the TOP of the content + // (best matches in an autocomplete). By clamping to 0, we shift the element + // down so the top rows are visible and the bottom overflows below — the + // opaque prop ensures it paints over whatever is underneath. + if (y < 0 && node.style.position === 'absolute') { + y = 0 + } + + // Check if we can skip this subtree (clean node with unchanged layout). + // Blit cells from previous screen instead of re-rendering. + const cached = nodeCache.get(node) + if ( + !node.dirty && + !skipSelfBlit && + node.pendingScrollDelta === undefined && + cached && + cached.x === x && + cached.y === y && + cached.width === width && + cached.height === height && + prevScreen + ) { + const fx = Math.floor(x) + const fy = Math.floor(y) + const fw = Math.floor(width) + const fh = Math.floor(height) + output.blit(prevScreen, fx, fy, fw, fh) + if (node.style.position === 'absolute') { + absoluteRectsCur.push(cached) + } + // Absolute descendants can paint outside this node's layout bounds + // (e.g. a slash menu with position='absolute' bottom='100%' floats + // above). If a dirty clipped sibling re-rendered and overwrote those + // cells, the blit above only restored this node's own rect — the + // absolute descendants' cells are lost. Re-blit them from prevScreen + // so the overlays survive. + blitEscapingAbsoluteDescendants(node, output, prevScreen, fx, fy, fw, fh) + return + } + + // Clear stale content from the old position when re-rendering. + // Dirty: content changed. Moved: position/size changed (e.g., sibling + // above changed height), old cells still on the terminal. + const positionChanged = + cached !== undefined && + (cached.x !== x || + cached.y !== y || + cached.width !== width || + cached.height !== height) + if (positionChanged) { + layoutShifted = true + } + if (cached && (node.dirty || positionChanged)) { + output.clear( + { + x: Math.floor(cached.x), + y: Math.floor(cached.y), + width: Math.floor(cached.width), + height: Math.floor(cached.height), + }, + node.style.position === 'absolute', + ) + } + + // Read before deleting — hasRemovedChild disables prevScreen blitting + // for siblings to prevent stale overflow content from being restored. + const clears = pendingClears.get(node) + const hasRemovedChild = clears !== undefined + if (hasRemovedChild) { + layoutShifted = true + for (const rect of clears) { + output.clear({ + x: Math.floor(rect.x), + y: Math.floor(rect.y), + width: Math.floor(rect.width), + height: Math.floor(rect.height), + }) + } + pendingClears.delete(node) + } + + // Yoga squeezed this node to zero height (overflow in a height-constrained + // parent) AND a sibling lands at the same y. Skip rendering — both would + // write to the same row; if the sibling's content is shorter, this node's + // tail chars ghost (e.g. "false" + "true" = "truee"). The clear above + // already handled the visible→squeezed transition. + // + // The sibling-overlap check is load-bearing: Yoga's pixel-grid rounding + // can give a box h=0 while still leaving a row for it (next sibling at + // y+1, not y). HelpV2's third shortcuts column hits this — skipping + // unconditionally drops "ctrl + z to suspend" from /help output. + if (height === 0 && siblingSharesY(node, yogaNode)) { + nodeCache.set(node, { x, y, width, height, top: yogaTop }) + node.dirty = false + return + } + + if (node.nodeName === 'ink-raw-ansi') { + // Pre-rendered ANSI content. The producer already wrapped to width and + // emitted terminal-ready escape codes. Skip squash, measure, wrap, and + // style re-application — output.write() parses ANSI directly into cells. + const text = node.attributes['rawText'] as string + if (text) { + output.write(x, y, text) + } + } else if (node.nodeName === 'ink-text') { + const segments = squashTextNodesToSegments( + node, + inheritedBackgroundColor + ? { backgroundColor: inheritedBackgroundColor } + : undefined, + ) + + // First, get plain text to check if wrapping is needed + const plainText = segments.map(s => s.text).join('') + + if (plainText.length > 0) { + // Upstream Ink uses getMaxWidth(yogaNode) unclamped here. That + // width comes from Yoga's AtMost pass and can exceed the actual + // screen space (see getMaxWidth docstring). Yoga's height for this + // node already reflects the constrained Exactly pass, so clamping + // the wrap width here keeps line count consistent with layout. + // Without this, characters past the screen edge are dropped by + // setCellAt's bounds check. + const maxWidth = Math.min(getMaxWidth(yogaNode), output.width - x) + const textWrap = node.style.textWrap ?? 'wrap' + + // Check if wrapping is needed + const needsWrapping = widestLine(plainText) > maxWidth + + let text: string + let softWrap: boolean[] | undefined + if (needsWrapping && segments.length === 1) { + // Single segment: wrap plain text first, then apply styles to each line + const segment = segments[0]! + const w = wrapWithSoftWrap(plainText, maxWidth, textWrap) + softWrap = w.softWrap + text = w.wrapped + .split('\n') + .map(line => { + let styled = applyTextStyles(line, segment.styles) + // Apply OSC 8 hyperlink per-line so each line is independently + // clickable. output.ts splits on newlines and tokenizes each + // line separately, so a single wrapper around the whole block + // would only apply the hyperlink to the first line. + if (segment.hyperlink) { + styled = wrapWithOsc8Link(styled, segment.hyperlink) + } + return styled + }) + .join('\n') + } else if (needsWrapping) { + // Multiple segments with wrapping: wrap plain text first, then re-apply + // each segment's styles based on character positions. This preserves + // per-segment styles even when text wraps across lines. + const w = wrapWithSoftWrap(plainText, maxWidth, textWrap) + softWrap = w.softWrap + const charToSegment = buildCharToSegmentMap(segments) + text = applyStylesToWrappedText( + w.wrapped, + segments, + charToSegment, + plainText, + textWrap === 'wrap-trim', + ) + // Hyperlinks are handled per-run in applyStylesToWrappedText via + // wrapWithOsc8Link, similar to how styles are applied per-run. + } else { + // No wrapping needed: apply styles directly + text = segments + .map(segment => { + let styledText = applyTextStyles(segment.text, segment.styles) + if (segment.hyperlink) { + styledText = wrapWithOsc8Link(styledText, segment.hyperlink) + } + return styledText + }) + .join('') + } + + text = applyPaddingToText(node, text, softWrap) + + output.write(x, y, text, softWrap) + } + } else if (node.nodeName === 'ink-box') { + const boxBackgroundColor = + node.style.backgroundColor ?? inheritedBackgroundColor + + // Mark this box's region as non-selectable (fullscreen text + // selection). noSelect ops are applied AFTER blits/writes in + // output.get(), so this wins regardless of what's rendered into + // the region — including blits from prevScreen when the box is + // clean (the op is emitted on both the dirty-render path here + // AND on the blit fast-path at line ~235 since blitRegion copies + // the noSelect bitmap alongside cells). + // + // 'from-left-edge' extends the exclusion from col 0 so any + // upstream indentation (tool prefix, tree lines) is covered too + // — a multi-row drag over a diff gutter shouldn't pick up the + // ` ⎿ ` prefix on row 0 or the blank cells under it on row 1+. + if (node.style.noSelect) { + const boxX = Math.floor(x) + const fromEdge = node.style.noSelect === 'from-left-edge' + output.noSelect({ + x: fromEdge ? 0 : boxX, + y: Math.floor(y), + width: fromEdge ? boxX + Math.floor(width) : Math.floor(width), + height: Math.floor(height), + }) + } + + const overflowX = node.style.overflowX ?? node.style.overflow + const overflowY = node.style.overflowY ?? node.style.overflow + const clipHorizontally = overflowX === 'hidden' || overflowX === 'scroll' + const clipVertically = overflowY === 'hidden' || overflowY === 'scroll' + const isScrollY = overflowY === 'scroll' + + const needsClip = clipHorizontally || clipVertically + let y1: number | undefined + let y2: number | undefined + if (needsClip) { + const x1 = clipHorizontally + ? x + yogaNode.getComputedBorder(LayoutEdge.Left) + : undefined + + const x2 = clipHorizontally + ? x + + yogaNode.getComputedWidth() - + yogaNode.getComputedBorder(LayoutEdge.Right) + : undefined + + y1 = clipVertically + ? y + yogaNode.getComputedBorder(LayoutEdge.Top) + : undefined + + y2 = clipVertically + ? y + + yogaNode.getComputedHeight() - + yogaNode.getComputedBorder(LayoutEdge.Bottom) + : undefined + + output.clip({ x1, x2, y1, y2 }) + } + + if (isScrollY) { + // Scroll containers follow the ScrollBox component structure: + // a single content-wrapper child with flexShrink:0 (doesn't shrink + // to fit), whose children are the scrollable items. scrollHeight + // comes from the wrapper's intrinsic Yoga height. The wrapper is + // rendered with its Y translated by -scrollTop; its children are + // culled against the visible window. + const padTop = yogaNode.getComputedPadding(LayoutEdge.Top) + const innerHeight = Math.max( + 0, + (y2 ?? y + height) - + (y1 ?? y) - + padTop - + yogaNode.getComputedPadding(LayoutEdge.Bottom), + ) + + const content = node.childNodes.find(c => (c as DOMElement).yogaNode) as + | DOMElement + | undefined + const contentYoga = content?.yogaNode + // scrollHeight is the intrinsic height of the content wrapper. + // Do NOT add getComputedTop() — that's the wrapper's offset + // within the viewport (equal to the scroll container's + // paddingTop), and innerHeight already subtracts padding, so + // including it double-counts padding and inflates maxScroll. + const scrollHeight = contentYoga?.getComputedHeight() ?? 0 + // Capture previous scroll bounds BEFORE overwriting — the at-bottom + // follow check compares against last frame's max. + const prevScrollHeight = node.scrollHeight ?? scrollHeight + const prevInnerHeight = node.scrollViewportHeight ?? innerHeight + node.scrollHeight = scrollHeight + node.scrollViewportHeight = innerHeight + // Absolute screen-buffer row where the scrollable area (inside + // padding) begins. Exposed via ScrollBoxHandle.getViewportTop() so + // drag-to-scroll can detect when the drag leaves the scroll viewport. + node.scrollViewportTop = (y1 ?? y) + padTop + + const maxScroll = Math.max(0, scrollHeight - innerHeight) + // scrollAnchor: scroll so the anchored element's top is at the + // viewport top (plus offset). Yoga is FRESH — same calculateLayout + // pass that just produced scrollHeight. Deterministic alternative + // to scrollTo(N) which bakes a number that's stale by the throttled + // render; the element ref defers the read to now. One-shot snap. + // A prior eased-seek version (proportional drain over ~5 frames) + // moved scrollTop without firing React's notify → parent's quantized + // store snapshot never updated → StickyTracker got stale range props + // → firstVisible wrong. Also: SCROLL_MIN_PER_FRAME=4 with snap-at-1 + // ping-ponged forever at delta=2. Smooth needs drain-end notify + // plumbing; shipping instant first. stickyScroll overrides. + if (node.scrollAnchor) { + const anchorTop = node.scrollAnchor.el.yogaNode?.getComputedTop() + if (anchorTop != null) { + node.scrollTop = anchorTop + node.scrollAnchor.offset + node.pendingScrollDelta = undefined + } + node.scrollAnchor = undefined + } + // At-bottom follow. Positional: if scrollTop was at (or past) the + // previous max, pin to the new max. Scroll away → stop following; + // scroll back (or scrollToBottom/sticky attr) → resume. The sticky + // flag is OR'd in for cold start (scrollTop=0 before first layout) + // and scrollToBottom-from-far-away (flag set before scrollTop moves) + // — the imperative field takes precedence over the attribute so + // scrollTo/scrollBy can break stickiness. pendingDelta<0 guard: + // don't cancel an in-flight scroll-up when content races in. + // Capture scrollTop before follow so ink.tsx can translate any + // active text selection by the same delta (native terminal behavior: + // view keeps scrolling, highlight walks up with the text). + const scrollTopBeforeFollow = node.scrollTop ?? 0 + const sticky = + node.stickyScroll ?? Boolean(node.attributes['stickyScroll']) + const prevMaxScroll = Math.max(0, prevScrollHeight - prevInnerHeight) + // Positional check only valid when content grew — virtualization can + // transiently SHRINK scrollHeight (tail unmount + stale heightCache + // spacer) making scrollTop >= prevMaxScroll true by artifact, not + // because the user was at bottom. + const grew = scrollHeight >= prevScrollHeight + const atBottom = + sticky || (grew && scrollTopBeforeFollow >= prevMaxScroll) + if (atBottom && (node.pendingScrollDelta ?? 0) >= 0) { + node.scrollTop = maxScroll + node.pendingScrollDelta = undefined + // Sync flag so useVirtualScroll's isSticky() agrees with positional + // state — sticky-broken-but-at-bottom (wheel tremor, click-select + // at max) otherwise leaves useVirtualScroll's clamp holding the + // viewport short of new streaming content. scrollTo/scrollBy set + // false; this restores true, same as scrollToBottom() would. + // Only restore when (a) positionally at bottom and (b) the flag + // was explicitly broken (===false) by scrollTo/scrollBy. When + // undefined (never set by user action) leave it alone — setting it + // would make the sticky flag sticky-by-default and lock out + // direct scrollTop writes (e.g. the alt-screen-perf test). + if ( + node.stickyScroll === false && + scrollTopBeforeFollow >= prevMaxScroll + ) { + node.stickyScroll = true + } + } + const followDelta = (node.scrollTop ?? 0) - scrollTopBeforeFollow + if (followDelta > 0) { + const vpTop = node.scrollViewportTop ?? 0 + followScroll = { + delta: followDelta, + viewportTop: vpTop, + viewportBottom: vpTop + innerHeight - 1, + } + } + // Drain pendingScrollDelta. Native terminals (proportional burst + // events) use proportional drain; xterm.js (VS Code, sparse events + + // app-side accel curve) uses adaptive small-step drain. isXtermJs() + // depends on the async XTVERSION probe, but by the time this runs + // (pendingScrollDelta is only set by wheel events, >>50ms after + // startup) the probe has resolved — same timing guarantee the + // wheel-accel curve relies on. + let cur = node.scrollTop ?? 0 + const pending = node.pendingScrollDelta + const cMin = node.scrollClampMin + const cMax = node.scrollClampMax + const haveClamp = cMin !== undefined && cMax !== undefined + if (pending !== undefined && pending !== 0) { + // Drain continues even past the clamp — the render-clamp below + // holds the VISUAL at the mounted edge regardless. Hard-stopping + // here caused stop-start jutter: drain hits edge → pause → React + // commits → clamp widens → drain resumes → edge again. Letting + // scrollTop advance smoothly while the clamp lags gives continuous + // visual scroll at React's commit rate (the clamp catches up each + // commit). But THROTTLE the drain when already past the clamp so + // scrollTop doesn't race 5000 rows ahead of the mounted range + // (slide-cap would then take 200 commits to catch up = long + // perceived stall at the edge). Past-clamp drain caps at ~4 rows/ + // frame, roughly matching React's slide rate so the gap stays + // bounded and catch-up is quick once input stops. + const pastClamp = + haveClamp && + ((pending < 0 && cur < cMin) || (pending > 0 && cur > cMax)) + const eff = pastClamp ? Math.min(4, innerHeight >> 3) : innerHeight + cur += isXtermJsHost() + ? drainAdaptive(node, pending, eff) + : drainProportional(node, pending, eff) + } else if (pending === 0) { + // Opposite scrollBy calls cancelled to zero — clear so we don't + // schedule an infinite loop of no-op drain frames. + node.pendingScrollDelta = undefined + } + let scrollTop = Math.max(0, Math.min(cur, maxScroll)) + // Virtual-scroll clamp: if scrollTop raced past the currently-mounted + // range (burst PageUp before React re-renders), render at the EDGE of + // the mounted children instead of blank spacer. Do NOT write back to + // node.scrollTop — the clamped value is for this paint only; the real + // scrollTop stays so React's next commit sees the target and mounts + // the right range. Not scheduling scrollDrainNode here keeps the + // clamp passive — React's commit → resetAfterCommit → onRender will + // paint again with fresh bounds. + const clamped = haveClamp + ? Math.max(cMin, Math.min(scrollTop, cMax)) + : scrollTop + node.scrollTop = scrollTop + // Clamp hitting top/bottom consumes any remainder. Set drainPending + // only after clamp so a wasted no-op frame isn't scheduled. + if (scrollTop !== cur) node.pendingScrollDelta = undefined + if (node.pendingScrollDelta !== undefined) scrollDrainNode = node + scrollTop = clamped + + if (content && contentYoga) { + // Compute content wrapper's absolute render position with scroll + // offset applied, then render its children with culling. + const contentX = x + contentYoga.getComputedLeft() + const contentY = y + contentYoga.getComputedTop() - scrollTop + // layoutShifted detection gap: when scrollTop moves by >= viewport + // height (batched PageUps, fast wheel), every visible child gets + // culled (cache dropped) and every newly-visible child has no + // cache — so the children's positionChanged check can't fire. + // The content wrapper's cached y (which encodes -scrollTop) is + // the only node that survives to witness the scroll. + const contentCached = nodeCache.get(content) + let hint: ScrollHint | null = null + if (contentCached && contentCached.y !== contentY) { + // delta = newScrollTop - oldScrollTop (positive = scrolled down). + // Capture a DECSTBM hint if the container itself didn't move + // and the shift fits within the viewport — otherwise the full + // rewrite is needed anyway, and layoutShifted stays the fallback. + const delta = contentCached.y - contentY + const regionTop = Math.floor(y + contentYoga.getComputedTop()) + const regionBottom = regionTop + innerHeight - 1 + if ( + cached?.y === y && + cached.height === height && + innerHeight > 0 && + Math.abs(delta) < innerHeight + ) { + hint = { top: regionTop, bottom: regionBottom, delta } + scrollHint = hint + } else { + layoutShifted = true + } + } + // Fast path: scroll (hint captured) with usable prevScreen. + // Blit prevScreen's scroll region into next.screen, shift in-place + // by delta (mirrors DECSTBM), then render ONLY the edge rows. The + // nested clip keeps child writes out of stable rows — a tall child + // that spans edge+stable still renders but stable cells are + // clipped, preserving the blit. Avoids re-rendering every visible + // child (expensive for long syntax-highlighted transcripts). + // + // When content.dirty (e.g. streaming text at the bottom of the + // scroll), we still use the fast path — the dirty child is almost + // always in the edge rows (the bottom, where new content appears). + // After edge rendering, any dirty children in stable rows are + // re-rendered in a second pass to avoid showing stale blitted + // content. + // + // Guard: the fast path only handles pure scroll or bottom-append. + // Child removal/insertion changes the content height in a way that + // doesn't match the scroll delta — fall back to the full path so + // removed children don't leave stale cells and shifted siblings + // render at their new positions. + const scrollHeight = contentYoga.getComputedHeight() + const prevHeight = contentCached?.height ?? scrollHeight + const heightDelta = scrollHeight - prevHeight + const safeForFastPath = + !hint || + heightDelta === 0 || + (hint.delta > 0 && heightDelta === hint.delta) + // scrollHint is set above when hint is captured. If safeForFastPath + // is false the full path renders a next.screen that doesn't match + // the DECSTBM shift — emitting DECSTBM leaves stale rows (seen as + // content bleeding through during scroll-up + streaming). Clear it. + if (!safeForFastPath) scrollHint = null + if (hint && prevScreen && safeForFastPath) { + const { top, bottom, delta } = hint + const w = Math.floor(width) + output.blit(prevScreen, Math.floor(x), top, w, bottom - top + 1) + output.shift(top, bottom, delta) + // Edge rows: new content entering the viewport. + const edgeTop = delta > 0 ? bottom - delta + 1 : top + const edgeBottom = delta > 0 ? bottom : top - delta - 1 + output.clear({ + x: Math.floor(x), + y: edgeTop, + width: w, + height: edgeBottom - edgeTop + 1, + }) + output.clip({ + x1: undefined, + x2: undefined, + y1: edgeTop, + y2: edgeBottom + 1, + }) + // Snapshot dirty children before the first pass — the first + // pass clears dirty flags, and edge-spanning children would be + // missed by the second pass without this snapshot. + const dirtyChildren = content.dirty + ? new Set(content.childNodes.filter(c => (c as DOMElement).dirty)) + : null + renderScrolledChildren( + content, + output, + contentX, + contentY, + hasRemovedChild, + undefined, + // Cull to edge in child-local coords (inverse of contentY offset). + edgeTop - contentY, + edgeBottom + 1 - contentY, + boxBackgroundColor, + true, + ) + output.unclip() + + // Second pass: re-render children in stable rows whose screen + // position doesn't match where the shift put their old pixels. + // Covers TWO cases: + // 1. Dirty children — their content changed, blitted pixels are + // stale regardless of position. + // 2. Clean children BELOW a middle-growth point — when a dirty + // sibling above them grows, their yogaTop increases but + // scrollTop increases by the same amount (sticky), so their + // screenY is CONSTANT. The shift moved their old pixels to + // screenY-delta (wrong); they should stay at screenY. Without + // this, the spinner/tmux-monitor ghost at shifted positions + // during streaming (e.g. triple spinner, pill duplication). + // For bottom-append (the common case), all clean children are + // ABOVE the growth point; their screenY decreased by delta and + // the shift put them at the right place — skipped here, fast + // path preserved. + if (dirtyChildren) { + const edgeTopLocal = edgeTop - contentY + const edgeBottomLocal = edgeBottom + 1 - contentY + const spaces = ' '.repeat(w) + // Track cumulative height change of children iterated so far. + // A clean child's yogaTop is unchanged iff this is zero (no + // sibling above it grew/shrank/mounted). When zero, the skip + // check cached.y−delta === screenY reduces to delta === delta + // (tautology) → skip without yoga reads. Restores O(dirty) + // that #24536 traded away: for bottom-append the dirty child + // is last (all clean children skip); for virtual-scroll range + // shift the topSpacer shrink + new-item heights self-balance + // to zero before reaching the clean block. Middle-growth + // leaves shift non-zero → clean children after the growth + // point fall through to yoga + the fine-grained check below, + // preserving the ghost-box fix. + let cumHeightShift = 0 + for (const childNode of content.childNodes) { + const childElem = childNode as DOMElement + const isDirty = dirtyChildren.has(childNode) + if (!isDirty && cumHeightShift === 0) { + if (nodeCache.has(childElem)) continue + // Uncached = culled last frame, now re-entering. blit + // never painted it → fall through to yoga + render. + // Height unchanged (clean), so cumHeightShift stays 0. + } + const cy = childElem.yogaNode + if (!cy) continue + const childTop = cy.getComputedTop() + const childH = cy.getComputedHeight() + const childBottom = childTop + childH + if (isDirty) { + const prev = nodeCache.get(childElem) + cumHeightShift += childH - (prev ? prev.height : 0) + } + // Skip culled children (outside viewport) + if ( + childBottom <= scrollTop || + childTop >= scrollTop + innerHeight + ) + continue + // Skip children entirely within edge rows (already rendered) + if (childTop >= edgeTopLocal && childBottom <= edgeBottomLocal) + continue + const screenY = Math.floor(contentY + childTop) + // Clean children reaching here have cumHeightShift ≠ 0 OR + // no cache. Re-check precisely: cached.y − delta is where + // the shift left old pixels; if it equals new screenY the + // blit is correct (shift re-balanced at this child, or + // yogaTop happens to net out). No cache → blit never + // painted it → render. + if (!isDirty) { + const childCached = nodeCache.get(childElem) + if ( + childCached && + Math.floor(childCached.y) - delta === screenY + ) { + continue + } + } + // Wipe this child's region with spaces to overwrite stale + // blitted content — output.clear() only expands damage and + // cannot zero cells that the blit already wrote. + const screenBottom = Math.min( + Math.floor(contentY + childBottom), + Math.floor((y1 ?? y) + padTop + innerHeight), + ) + if (screenY < screenBottom) { + const fill = Array(screenBottom - screenY) + .fill(spaces) + .join('\n') + output.write(Math.floor(x), screenY, fill) + output.clip({ + x1: undefined, + x2: undefined, + y1: screenY, + y2: screenBottom, + }) + renderNodeToOutput(childElem, output, { + offsetX: contentX, + offsetY: contentY, + prevScreen: undefined, + inheritedBackgroundColor: boxBackgroundColor, + }) + output.unclip() + } + } + } + + // Third pass: repair rows where shifted copies of absolute + // overlays landed. The blit copied prevScreen cells INCLUDING + // overlay pixels (overlays render AFTER this ScrollBox so they + // painted into prevScreen's scroll region). After shift, those + // pixels sit at (rect.y - delta) — neither edge render nor the + // overlay's own re-render covers them. Wipe and re-render + // ScrollBox content so the diff writes correct cells. + const spaces = absoluteRectsPrev.length ? ' '.repeat(w) : '' + for (const r of absoluteRectsPrev) { + if (r.y >= bottom + 1 || r.y + r.height <= top) continue + const shiftedTop = Math.max(top, Math.floor(r.y) - delta) + const shiftedBottom = Math.min( + bottom + 1, + Math.floor(r.y + r.height) - delta, + ) + // Skip if entirely within edge rows (already rendered). + if (shiftedTop >= edgeTop && shiftedBottom <= edgeBottom + 1) + continue + if (shiftedTop >= shiftedBottom) continue + const fill = Array(shiftedBottom - shiftedTop) + .fill(spaces) + .join('\n') + output.write(Math.floor(x), shiftedTop, fill) + output.clip({ + x1: undefined, + x2: undefined, + y1: shiftedTop, + y2: shiftedBottom, + }) + renderScrolledChildren( + content, + output, + contentX, + contentY, + hasRemovedChild, + undefined, + shiftedTop - contentY, + shiftedBottom - contentY, + boxBackgroundColor, + true, + ) + output.unclip() + } + } else { + // Full path. Two sub-cases: + // + // Scrolled without a usable hint (big jump, container moved): + // child positions in prevScreen are stale. Clear the viewport + // and disable blit so children don't restore shifted content. + // + // No scroll (spinner tick, content edit): child positions in + // prevScreen are still valid. Skip the viewport clear and pass + // prevScreen so unchanged children blit. Dirty children already + // self-clear via their own cached-rect clear. Without this, a + // spinner inside ScrollBox forces a full-content rewrite every + // frame — on wide terminals over tmux (no BSU/ESU) the + // bandwidth crosses the chunk boundary and the frame tears. + const scrolled = contentCached && contentCached.y !== contentY + if (scrolled && y1 !== undefined && y2 !== undefined) { + output.clear({ + x: Math.floor(x), + y: Math.floor(y1), + width: Math.floor(width), + height: Math.floor(y2 - y1), + }) + } + // positionChanged (ScrollBox height shrunk — pill mount) means a + // child spanning the old bottom edge would blit its full cached + // rect past the new clip. output.ts clips blits now, but also + // disable prevScreen here so the partial-row child re-renders at + // correct bounds instead of blitting a clipped (truncated) old + // rect. + renderScrolledChildren( + content, + output, + contentX, + contentY, + hasRemovedChild, + scrolled || positionChanged ? undefined : prevScreen, + scrollTop, + scrollTop + innerHeight, + boxBackgroundColor, + ) + } + nodeCache.set(content, { + x: contentX, + y: contentY, + width: contentYoga.getComputedWidth(), + height: contentYoga.getComputedHeight(), + }) + content.dirty = false + } + } else { + // Fill interior with background color before rendering children. + // This covers padding areas and empty space; child text inherits + // the color via inheritedBackgroundColor so written cells also + // get the background. + // Disable prevScreen for children: the fill overwrites the entire + // interior each render, so child blits from prevScreen would restore + // stale cells (wrong bg if it changed) on top of the fresh fill. + const ownBackgroundColor = node.style.backgroundColor + if (ownBackgroundColor || node.style.opaque) { + const borderLeft = yogaNode.getComputedBorder(LayoutEdge.Left) + const borderRight = yogaNode.getComputedBorder(LayoutEdge.Right) + const borderTop = yogaNode.getComputedBorder(LayoutEdge.Top) + const borderBottom = yogaNode.getComputedBorder(LayoutEdge.Bottom) + const innerWidth = Math.floor(width) - borderLeft - borderRight + const innerHeight = Math.floor(height) - borderTop - borderBottom + if (innerWidth > 0 && innerHeight > 0) { + const spaces = ' '.repeat(innerWidth) + const fillLine = ownBackgroundColor + ? applyTextStyles(spaces, { backgroundColor: ownBackgroundColor }) + : spaces + const fill = Array(innerHeight).fill(fillLine).join('\n') + output.write(x + borderLeft, y + borderTop, fill) + } + } + + renderChildren( + node, + output, + x, + y, + hasRemovedChild, + // backgroundColor and opaque both disable child blit: the fill + // overwrites the entire interior each render, so any child whose + // layout position shifted would blit stale cells from prevScreen + // on top of the fresh fill. Previously opaque kept blit enabled + // on the assumption that plain-space fill + unchanged children = + // valid composite, but children CAN reposition (ScrollBox remeasure + // on re-render → /permissions body blanked on Down arrow, #25436). + ownBackgroundColor || node.style.opaque ? undefined : prevScreen, + boxBackgroundColor, + ) + } + + if (needsClip) { + output.unclip() + } + + // Render border AFTER children to ensure it's not overwritten by child + // clearing operations. When a child shrinks, it clears its old area, + // which may overlap with where the parent's border now is. + renderBorder(x, y, node, output) + } else if (node.nodeName === 'ink-root') { + renderChildren( + node, + output, + x, + y, + hasRemovedChild, + prevScreen, + inheritedBackgroundColor, + ) + } + + // Cache layout bounds for dirty tracking + const rect = { x, y, width, height, top: yogaTop } + nodeCache.set(node, rect) + if (node.style.position === 'absolute') { + absoluteRectsCur.push(rect) + } + node.dirty = false + } +} + +// Overflow contamination: content overflows right/down, so clean siblings +// AFTER a dirty/removed sibling can contain stale overflow in prevScreen. +// Disable blit for siblings after a dirty child — but still pass prevScreen +// TO the dirty child itself so its clean descendants can blit. The dirty +// child's own blit check already fails (node.dirty=true at line 216), so +// passing prevScreen only benefits its subtree. +// For removed children we don't know their original position, so +// conservatively disable blit for all. +// +// Clipped children (overflow hidden/scroll on both axes) cannot overflow +// onto later siblings — their content is confined to their layout bounds. +// Skip the contamination guard for them so later siblings can still blit. +// Without this, a spinner inside a ScrollBox dirties the wrapper on every +// tick and the bottom prompt section never blits → 100% writes every frame. +// +// Exception: absolute-positioned clipped children may have layout bounds +// that overlap arbitrary siblings, so the clipping does not help. +// +// Overlap contamination (seenDirtyClipped): a later ABSOLUTE sibling whose +// rect sits inside a dirty clipped child's bounds would blit stale cells +// from prevScreen — the clipped child just rewrote those cells this frame. +// The clipsBothAxes skip only protects against OVERFLOW (clipped child +// painting outside its bounds), not overlap (absolute sibling painting +// inside them). For non-opaque absolute siblings, skipSelfBlit forces +// descent (the full-width rect has transparent gaps → stale blit) while +// still passing prevScreen so opaque descendants can blit their narrower +// rects (NewMessagesPill's inner Text with backgroundColor). Opaque +// absolute siblings fill their entire rect — direct blit is safe. +function renderChildren( + node: DOMElement, + output: Output, + offsetX: number, + offsetY: number, + hasRemovedChild: boolean, + prevScreen: Screen | undefined, + inheritedBackgroundColor: Color | undefined, +): void { + let seenDirtyChild = false + let seenDirtyClipped = false + for (const childNode of node.childNodes) { + const childElem = childNode as DOMElement + // Capture dirty before rendering — renderNodeToOutput clears the flag + const wasDirty = childElem.dirty + const isAbsolute = childElem.style.position === 'absolute' + renderNodeToOutput(childElem, output, { + offsetX, + offsetY, + prevScreen: hasRemovedChild || seenDirtyChild ? undefined : prevScreen, + // Short-circuits on seenDirtyClipped (false in the common case) so + // the opaque/bg reads don't happen per-child per-frame. + skipSelfBlit: + seenDirtyClipped && + isAbsolute && + !childElem.style.opaque && + childElem.style.backgroundColor === undefined, + inheritedBackgroundColor, + }) + if (wasDirty && !seenDirtyChild) { + if (!clipsBothAxes(childElem) || isAbsolute) { + seenDirtyChild = true + } else { + seenDirtyClipped = true + } + } + } +} + +function clipsBothAxes(node: DOMElement): boolean { + const ox = node.style.overflowX ?? node.style.overflow + const oy = node.style.overflowY ?? node.style.overflow + return ( + (ox === 'hidden' || ox === 'scroll') && (oy === 'hidden' || oy === 'scroll') + ) +} + +// When Yoga squeezes a box to h=0, the ghost only happens if a sibling +// lands at the same computed top — then both write to that row and the +// shorter content leaves the longer's tail visible. Yoga's pixel-grid +// rounding can give h=0 while still advancing the next sibling's top +// (HelpV2's third shortcuts column), so h=0 alone isn't sufficient. +function siblingSharesY(node: DOMElement, yogaNode: LayoutNode): boolean { + const parent = node.parentNode + if (!parent) return false + const myTop = yogaNode.getComputedTop() + const siblings = parent.childNodes + const idx = siblings.indexOf(node) + for (let i = idx + 1; i < siblings.length; i++) { + const sib = (siblings[i] as DOMElement).yogaNode + if (!sib) continue + return sib.getComputedTop() === myTop + } + // No next sibling with a yoga node — check previous. A run of h=0 boxes + // at the tail would all share y with each other. + for (let i = idx - 1; i >= 0; i--) { + const sib = (siblings[i] as DOMElement).yogaNode + if (!sib) continue + return sib.getComputedTop() === myTop + } + return false +} + +// When a node blits, its absolute-positioned descendants that paint outside +// the node's layout bounds are NOT covered by the blit (which only copies +// the node's own rect). If a dirty sibling re-rendered and overwrote those +// cells, we must re-blit them from prevScreen so the overlays survive. +// Example: PromptInputFooter's slash menu uses position='absolute' bottom='100%' +// to float above the prompt; a spinner tick in the ScrollBox above re-renders +// and overwrites those cells. Without this, the menu vanishes on the next frame. +function blitEscapingAbsoluteDescendants( + node: DOMElement, + output: Output, + prevScreen: Screen, + px: number, + py: number, + pw: number, + ph: number, +): void { + const pr = px + pw + const pb = py + ph + for (const child of node.childNodes) { + if (child.nodeName === '#text') continue + const elem = child as DOMElement + if (elem.style.position === 'absolute') { + const cached = nodeCache.get(elem) + if (cached) { + absoluteRectsCur.push(cached) + const cx = Math.floor(cached.x) + const cy = Math.floor(cached.y) + const cw = Math.floor(cached.width) + const ch = Math.floor(cached.height) + // Only blit rects that extend outside the parent's layout bounds — + // cells within the parent rect are already covered by the parent blit. + if (cx < px || cy < py || cx + cw > pr || cy + ch > pb) { + output.blit(prevScreen, cx, cy, cw, ch) + } + } + } + // Recurse — absolute descendants can be nested arbitrarily deep + blitEscapingAbsoluteDescendants(elem, output, prevScreen, px, py, pw, ph) + } +} + +// Render children of a scroll container with viewport culling. +// scrollTopY..scrollBottomY are the visible window in CHILD-LOCAL Yoga coords +// (i.e. what getComputedTop() returns). Children entirely outside this window +// are skipped; their nodeCache entry is deleted so if they re-enter the +// viewport later they don't emit a stale clear for a position now occupied +// by a sibling. +function renderScrolledChildren( + node: DOMElement, + output: Output, + offsetX: number, + offsetY: number, + hasRemovedChild: boolean, + prevScreen: Screen | undefined, + scrollTopY: number, + scrollBottomY: number, + inheritedBackgroundColor: Color | undefined, + // When true (DECSTBM fast path), culled children keep their cache — + // the blit+shift put stable rows in next.screen so stale cache is + // never read. Avoids walking O(total_children * subtree_depth) per frame. + preserveCulledCache = false, +): void { + let seenDirtyChild = false + // Track cumulative height shift of dirty children iterated so far. When + // zero, a clean child's yogaTop is unchanged (no sibling above it grew), + // so cached.top is fresh and the cull check skips yoga. Bottom-append + // has the dirty child last → all prior clean children hit cache → + // O(dirty) not O(mounted). Middle-growth leaves shift non-zero after + // the dirty child → subsequent children yoga-read (needed for correct + // culling since their yogaTop shifted). + let cumHeightShift = 0 + for (const childNode of node.childNodes) { + const childElem = childNode as DOMElement + const cy = childElem.yogaNode + if (cy) { + const cached = nodeCache.get(childElem) + let top: number + let height: number + if ( + cached?.top !== undefined && + !childElem.dirty && + cumHeightShift === 0 + ) { + top = cached.top + height = cached.height + } else { + top = cy.getComputedTop() + height = cy.getComputedHeight() + if (childElem.dirty) { + cumHeightShift += height - (cached ? cached.height : 0) + } + // Refresh cached top so next frame's cumShift===0 path stays + // correct. For culled children with preserveCulledCache=true this + // is the ONLY refresh point — without it, a middle-growth frame + // leaves stale tops that misfire next frame. + if (cached) cached.top = top + } + const bottom = top + height + if (bottom <= scrollTopY || top >= scrollBottomY) { + // Culled — outside visible window. Drop stale cache entries from + // the subtree so when this child re-enters it doesn't fire clears + // at positions now occupied by siblings. The viewport-clear on + // scroll-change handles the visible-area repaint. + if (!preserveCulledCache) dropSubtreeCache(childElem) + continue + } + } + const wasDirty = childElem.dirty + renderNodeToOutput(childElem, output, { + offsetX, + offsetY, + prevScreen: hasRemovedChild || seenDirtyChild ? undefined : prevScreen, + inheritedBackgroundColor, + }) + if (wasDirty) { + seenDirtyChild = true + } + } +} + +function dropSubtreeCache(node: DOMElement): void { + nodeCache.delete(node) + for (const child of node.childNodes) { + if (child.nodeName !== '#text') { + dropSubtreeCache(child as DOMElement) + } + } +} + +// Exported for testing +export { buildCharToSegmentMap, applyStylesToWrappedText } + +export default renderNodeToOutput diff --git a/ink/render-to-screen.ts b/ink/render-to-screen.ts new file mode 100644 index 0000000..1c375c0 --- /dev/null +++ b/ink/render-to-screen.ts @@ -0,0 +1,231 @@ +import noop from 'lodash-es/noop.js' +import type { ReactElement } from 'react' +import { LegacyRoot } from 'react-reconciler/constants.js' +import { logForDebugging } from '../utils/debug.js' +import { createNode, type DOMElement } from './dom.js' +import { FocusManager } from './focus.js' +import Output from './output.js' +import reconciler from './reconciler.js' +import renderNodeToOutput, { + resetLayoutShifted, +} from './render-node-to-output.js' +import { + CellWidth, + CharPool, + cellAtIndex, + createScreen, + HyperlinkPool, + type Screen, + StylePool, + setCellStyleId, +} from './screen.js' + +/** Position of a match within a rendered message, relative to the message's + * own bounding box (row 0 = message top). Stable across scroll — to + * highlight on the real screen, add the message's screen-row offset. */ +export type MatchPosition = { + row: number + col: number + /** Number of CELLS the match spans (= query.length for ASCII, more + * for wide chars in the query). */ + len: number +} + +// Shared across calls. Pools accumulate style/char interns — reusing them +// means later calls hit cache more. Root/container reuse saves the +// createContainer cost (~1ms). LegacyRoot: all work sync, no scheduling — +// ConcurrentRoot's scheduler backlog leaks across roots via flushSyncWork. +let root: DOMElement | undefined +let container: ReturnType | undefined +let stylePool: StylePool | undefined +let charPool: CharPool | undefined +let hyperlinkPool: HyperlinkPool | undefined +let output: Output | undefined + +const timing = { reconcile: 0, yoga: 0, paint: 0, scan: 0, calls: 0 } +const LOG_EVERY = 20 + +/** Render a React element (wrapped in all contexts the component needs — + * caller's job) to an isolated Screen buffer at the given width. Returns + * the Screen + natural height (from yoga). Used for search: render ONE + * message, scan its Screen for the query, get exact (row, col) positions. + * + * ~1-3ms per call (yoga alloc + calculateLayout + paint). The + * flushSyncWork cross-root leak measured ~0.0003ms/call growth — fine + * for on-demand single-message rendering, pathological for render-all- + * 8k-upfront. Cache per (msg, query, width) upstream. + * + * Unmounts between calls. Root/container/pools persist for reuse. */ +export function renderToScreen( + el: ReactElement, + width: number, +): { screen: Screen; height: number } { + if (!root) { + root = createNode('ink-root') + root.focusManager = new FocusManager(() => false) + stylePool = new StylePool() + charPool = new CharPool() + hyperlinkPool = new HyperlinkPool() + // @ts-expect-error react-reconciler 0.33 takes 10 args; @types says 11 + container = reconciler.createContainer( + root, + LegacyRoot, + null, + false, + null, + 'search-render', + noop, + noop, + noop, + noop, + ) + } + + const t0 = performance.now() + // @ts-expect-error updateContainerSync exists but not in @types + reconciler.updateContainerSync(el, container, null, noop) + // @ts-expect-error flushSyncWork exists but not in @types + reconciler.flushSyncWork() + const t1 = performance.now() + + // Yoga layout. Root might not have a yogaNode if the tree is empty. + root.yogaNode?.setWidth(width) + root.yogaNode?.calculateLayout(width) + const height = Math.ceil(root.yogaNode?.getComputedHeight() ?? 0) + const t2 = performance.now() + + // Paint to a fresh Screen. Width = given, height = yoga's natural. + // No alt-screen, no prevScreen (every call is fresh). + const screen = createScreen( + width, + Math.max(1, height), // avoid 0-height Screen (createScreen may choke) + stylePool!, + charPool!, + hyperlinkPool!, + ) + if (!output) { + output = new Output({ width, height, stylePool: stylePool!, screen }) + } else { + output.reset(width, height, screen) + } + resetLayoutShifted() + renderNodeToOutput(root, output, { prevScreen: undefined }) + // renderNodeToOutput queues writes into Output; .get() flushes the + // queue into the Screen's cell arrays. Without this the screen is + // blank (constructor-zero). + const rendered = output.get() + const t3 = performance.now() + + // Unmount so next call gets a fresh tree. Leaves root/container/pools. + // @ts-expect-error updateContainerSync exists but not in @types + reconciler.updateContainerSync(null, container, null, noop) + // @ts-expect-error flushSyncWork exists but not in @types + reconciler.flushSyncWork() + + timing.reconcile += t1 - t0 + timing.yoga += t2 - t1 + timing.paint += t3 - t2 + if (++timing.calls % LOG_EVERY === 0) { + const total = timing.reconcile + timing.yoga + timing.paint + timing.scan + logForDebugging( + `renderToScreen: ${timing.calls} calls · ` + + `reconcile=${timing.reconcile.toFixed(1)}ms yoga=${timing.yoga.toFixed(1)}ms ` + + `paint=${timing.paint.toFixed(1)}ms scan=${timing.scan.toFixed(1)}ms · ` + + `total=${total.toFixed(1)}ms · avg ${(total / timing.calls).toFixed(2)}ms/call`, + ) + } + + return { screen: rendered, height } +} + +/** Scan a Screen buffer for all occurrences of query. Returns positions + * relative to the buffer (row 0 = buffer top). Same cell-skip logic as + * applySearchHighlight (SpacerTail/SpacerHead/noSelect) so positions + * match what the overlay highlight would find. Case-insensitive. + * + * For the side-render use: this Screen is the FULL message (natural + * height, not viewport-clipped). Positions are stable — to highlight + * on the real screen, add the message's screen offset (lo). */ +export function scanPositions(screen: Screen, query: string): MatchPosition[] { + const lq = query.toLowerCase() + if (!lq) return [] + const qlen = lq.length + const w = screen.width + const h = screen.height + const noSelect = screen.noSelect + const positions: MatchPosition[] = [] + + const t0 = performance.now() + for (let row = 0; row < h; row++) { + const rowOff = row * w + // Same text-build as applySearchHighlight. Keep in sync — or extract + // to a shared helper (TODO once both are stable). codeUnitToCell + // maps indexOf positions (code units in the LOWERCASED text) to cell + // indices in colOf — surrogate pairs (emoji) and multi-unit lowercase + // (Turkish İ → i + U+0307) make text.length > colOf.length. + let text = '' + const colOf: number[] = [] + const codeUnitToCell: number[] = [] + for (let col = 0; col < w; col++) { + const idx = rowOff + col + const cell = cellAtIndex(screen, idx) + if ( + cell.width === CellWidth.SpacerTail || + cell.width === CellWidth.SpacerHead || + noSelect[idx] === 1 + ) { + continue + } + const lc = cell.char.toLowerCase() + const cellIdx = colOf.length + for (let i = 0; i < lc.length; i++) { + codeUnitToCell.push(cellIdx) + } + text += lc + colOf.push(col) + } + // Non-overlapping — same advance as applySearchHighlight. + let pos = text.indexOf(lq) + while (pos >= 0) { + const startCi = codeUnitToCell[pos]! + const endCi = codeUnitToCell[pos + qlen - 1]! + const col = colOf[startCi]! + const endCol = colOf[endCi]! + 1 + positions.push({ row, col, len: endCol - col }) + pos = text.indexOf(lq, pos + qlen) + } + } + timing.scan += performance.now() - t0 + + return positions +} + +/** Write CURRENT (yellow+bold+underline) at positions[currentIdx] + + * rowOffset. OTHER positions are NOT styled here — the scan-highlight + * (applySearchHighlight with null hint) does inverse for all visible + * matches, including these. Two-layer: scan = 'you could go here', + * position = 'you ARE here'. Writing inverse again here would be a + * no-op (withInverse idempotent) but wasted work. + * + * Positions are message-relative (row 0 = message top). rowOffset = + * message's current screen-top (lo). Clips outside [0, height). */ +export function applyPositionedHighlight( + screen: Screen, + stylePool: StylePool, + positions: MatchPosition[], + rowOffset: number, + currentIdx: number, +): boolean { + if (currentIdx < 0 || currentIdx >= positions.length) return false + const p = positions[currentIdx]! + const row = p.row + rowOffset + if (row < 0 || row >= screen.height) return false + const transform = (id: number) => stylePool.withCurrentMatch(id) + const rowOff = row * screen.width + for (let col = p.col; col < p.col + p.len; col++) { + if (col < 0 || col >= screen.width) continue + const cell = cellAtIndex(screen, rowOff + col) + setCellStyleId(screen, col, row, transform(cell.styleId)) + } + return true +} diff --git a/ink/renderer.ts b/ink/renderer.ts new file mode 100644 index 0000000..d87fb3d --- /dev/null +++ b/ink/renderer.ts @@ -0,0 +1,178 @@ +import { logForDebugging } from 'src/utils/debug.js' +import { type DOMElement, markDirty } from './dom.js' +import type { Frame } from './frame.js' +import { consumeAbsoluteRemovedFlag } from './node-cache.js' +import Output from './output.js' +import renderNodeToOutput, { + getScrollDrainNode, + getScrollHint, + resetLayoutShifted, + resetScrollDrainNode, + resetScrollHint, +} from './render-node-to-output.js' +import { createScreen, type StylePool } from './screen.js' + +export type RenderOptions = { + frontFrame: Frame + backFrame: Frame + isTTY: boolean + terminalWidth: number + terminalRows: number + altScreen: boolean + // True when the previous frame's screen buffer was mutated post-render + // (selection overlay), reset to blank (alt-screen enter/resize/SIGCONT), + // or reset to 0×0 (forceRedraw). Blitting from such a prevScreen would + // copy stale inverted cells, blanks, or nothing. When false, blit is safe. + prevFrameContaminated: boolean +} + +export type Renderer = (options: RenderOptions) => Frame + +export default function createRenderer( + node: DOMElement, + stylePool: StylePool, +): Renderer { + // Reuse Output across frames so charCache (tokenize + grapheme clustering) + // persists — most lines don't change between renders. + let output: Output | undefined + return options => { + const { frontFrame, backFrame, isTTY, terminalWidth, terminalRows } = + options + const prevScreen = frontFrame.screen + const backScreen = backFrame.screen + // Read pools from the back buffer's screen — pools may be replaced + // between frames (generational reset), so we can't capture them in the closure + const charPool = backScreen.charPool + const hyperlinkPool = backScreen.hyperlinkPool + + // Return empty frame if yoga node doesn't exist or layout hasn't been computed yet. + // getComputedHeight() returns NaN before calculateLayout() is called. + // Also check for invalid dimensions (negative, Infinity) that would cause RangeError + // when creating arrays. + const computedHeight = node.yogaNode?.getComputedHeight() + const computedWidth = node.yogaNode?.getComputedWidth() + const hasInvalidHeight = + computedHeight === undefined || + !Number.isFinite(computedHeight) || + computedHeight < 0 + const hasInvalidWidth = + computedWidth === undefined || + !Number.isFinite(computedWidth) || + computedWidth < 0 + + if (!node.yogaNode || hasInvalidHeight || hasInvalidWidth) { + // Log to help diagnose root cause (visible with --debug flag) + if (node.yogaNode && (hasInvalidHeight || hasInvalidWidth)) { + logForDebugging( + `Invalid yoga dimensions: width=${computedWidth}, height=${computedHeight}, ` + + `childNodes=${node.childNodes.length}, terminalWidth=${terminalWidth}, terminalRows=${terminalRows}`, + ) + } + return { + screen: createScreen( + terminalWidth, + 0, + stylePool, + charPool, + hyperlinkPool, + ), + viewport: { width: terminalWidth, height: terminalRows }, + cursor: { x: 0, y: 0, visible: true }, + } + } + + const width = Math.floor(node.yogaNode.getComputedWidth()) + const yogaHeight = Math.floor(node.yogaNode.getComputedHeight()) + // Alt-screen: the screen buffer IS the alt buffer — always exactly + // terminalRows tall. wraps children in , so yogaHeight should equal + // terminalRows. But if something renders as a SIBLING of that Box + // (bug: MessageSelector was outside ), yogaHeight + // exceeds rows and every assumption below (viewport +1 hack, cursor.y + // clamp, log-update's heightDelta===0 fast path) breaks, desyncing + // virtual/physical cursors. Clamping here enforces the invariant: + // overflow writes land at y >= screen.height and setCellAt drops + // them. The sibling is invisible (obvious, easy to find) instead of + // corrupting the whole terminal. + const height = options.altScreen ? terminalRows : yogaHeight + if (options.altScreen && yogaHeight > terminalRows) { + logForDebugging( + `alt-screen: yoga height ${yogaHeight} > terminalRows ${terminalRows} — ` + + `something is rendering outside . Overflow clipped.`, + { level: 'warn' }, + ) + } + const screen = + backScreen ?? + createScreen(width, height, stylePool, charPool, hyperlinkPool) + if (output) { + output.reset(width, height, screen) + } else { + output = new Output({ width, height, stylePool, screen }) + } + + resetLayoutShifted() + resetScrollHint() + resetScrollDrainNode() + + // prevFrameContaminated: selection overlay mutated the returned screen + // buffer post-render (in ink.tsx), resetFramesForAltScreen() replaced it + // with blanks, or forceRedraw() reset it to 0×0. Blit on the NEXT frame + // would copy stale inverted cells / blanks / nothing. When clean, blit + // restores the O(unchanged) fast path for steady-state frames (spinner + // tick, text stream). + // Removing an absolute-positioned node poisons prevScreen: it may + // have painted over non-siblings (e.g. an overlay over a ScrollBox + // earlier in tree order), so their blits would restore the removed + // node's pixels. hasRemovedChild only shields direct siblings. + // Normal-flow removals don't paint cross-subtree and are fine. + const absoluteRemoved = consumeAbsoluteRemovedFlag() + renderNodeToOutput(node, output, { + prevScreen: + absoluteRemoved || options.prevFrameContaminated + ? undefined + : prevScreen, + }) + + const renderedScreen = output.get() + + // Drain continuation: render cleared scrollbox.dirty, so next frame's + // root blit would skip the subtree. markDirty walks ancestors so the + // next frame descends. Done AFTER render so the clear-dirty at the end + // of renderNodeToOutput doesn't overwrite this. + const drainNode = getScrollDrainNode() + if (drainNode) markDirty(drainNode) + + return { + scrollHint: options.altScreen ? getScrollHint() : null, + scrollDrainPending: drainNode !== null, + screen: renderedScreen, + viewport: { + width: terminalWidth, + // Alt screen: fake viewport.height = rows + 1 so that + // shouldClearScreen()'s `screen.height >= viewport.height` check + // (which treats exactly-filling content as "overflows" for + // scrollback purposes) never fires. Alt-screen content is always + // exactly `rows` tall (via ) but never + // scrolls — the cursor.y clamp below keeps the cursor-restore + // from emitting an LF. With the standard diff path, every frame + // is incremental; no fullResetSequence_CAUSES_FLICKER. + height: options.altScreen ? terminalRows + 1 : terminalRows, + }, + cursor: { + x: 0, + // In the alt screen, keep the cursor inside the viewport. When + // screen.height === terminalRows exactly (content fills the alt + // screen), cursor.y = screen.height would trigger log-update's + // cursor-restore LF at the last row, scrolling one row off the top + // of the alt buffer and desyncing the diff's cursor model. The + // cursor is hidden so its position only matters for diff coords. + y: options.altScreen + ? Math.max(0, Math.min(screen.height, terminalRows) - 1) + : screen.height, + // Hide cursor when there's dynamic output to render (only in TTY mode) + visible: !isTTY || screen.height === 0, + }, + } + } +} diff --git a/ink/root.ts b/ink/root.ts new file mode 100644 index 0000000..067bbd4 --- /dev/null +++ b/ink/root.ts @@ -0,0 +1,184 @@ +import type { ReactNode } from 'react' +import { logForDebugging } from 'src/utils/debug.js' +import { Stream } from 'stream' +import type { FrameEvent } from './frame.js' +import Ink, { type Options as InkOptions } from './ink.js' +import instances from './instances.js' + +export type RenderOptions = { + /** + * Output stream where app will be rendered. + * + * @default process.stdout + */ + stdout?: NodeJS.WriteStream + /** + * Input stream where app will listen for input. + * + * @default process.stdin + */ + stdin?: NodeJS.ReadStream + /** + * Error stream. + * @default process.stderr + */ + stderr?: NodeJS.WriteStream + /** + * Configure whether Ink should listen to Ctrl+C keyboard input and exit the app. This is needed in case `process.stdin` is in raw mode, because then Ctrl+C is ignored by default and process is expected to handle it manually. + * + * @default true + */ + exitOnCtrlC?: boolean + + /** + * Patch console methods to ensure console output doesn't mix with Ink output. + * + * @default true + */ + patchConsole?: boolean + + /** + * Called after each frame render with timing and flicker information. + */ + onFrame?: (event: FrameEvent) => void +} + +export type Instance = { + /** + * Replace previous root node with a new one or update props of the current root node. + */ + rerender: Ink['render'] + /** + * Manually unmount the whole Ink app. + */ + unmount: Ink['unmount'] + /** + * Returns a promise, which resolves when app is unmounted. + */ + waitUntilExit: Ink['waitUntilExit'] + cleanup: () => void +} + +/** + * A managed Ink root, similar to react-dom's createRoot API. + * Separates instance creation from rendering so the same root + * can be reused for multiple sequential screens. + */ +export type Root = { + render: (node: ReactNode) => void + unmount: () => void + waitUntilExit: () => Promise +} + +/** + * Mount a component and render the output. + */ +export const renderSync = ( + node: ReactNode, + options?: NodeJS.WriteStream | RenderOptions, +): Instance => { + const opts = getOptions(options) + const inkOptions: InkOptions = { + stdout: process.stdout, + stdin: process.stdin, + stderr: process.stderr, + exitOnCtrlC: true, + patchConsole: true, + ...opts, + } + + const instance: Ink = getInstance( + inkOptions.stdout, + () => new Ink(inkOptions), + ) + + instance.render(node) + + return { + rerender: instance.render, + unmount() { + instance.unmount() + }, + waitUntilExit: instance.waitUntilExit, + cleanup: () => instances.delete(inkOptions.stdout), + } +} + +const wrappedRender = async ( + node: ReactNode, + options?: NodeJS.WriteStream | RenderOptions, +): Promise => { + // Preserve the microtask boundary that `await loadYoga()` used to provide. + // Without it, the first render fires synchronously before async startup work + // (e.g. useReplBridge notification state) settles, and the subsequent Static + // write overwrites scrollback instead of appending below the logo. + await Promise.resolve() + const instance = renderSync(node, options) + logForDebugging( + `[render] first ink render: ${Math.round(process.uptime() * 1000)}ms since process start`, + ) + return instance +} + +export default wrappedRender + +/** + * Create an Ink root without rendering anything yet. + * Like react-dom's createRoot — call root.render() to mount a tree. + */ +export async function createRoot({ + stdout = process.stdout, + stdin = process.stdin, + stderr = process.stderr, + exitOnCtrlC = true, + patchConsole = true, + onFrame, +}: RenderOptions = {}): Promise { + // See wrappedRender — preserve microtask boundary from the old WASM await. + await Promise.resolve() + const instance = new Ink({ + stdout, + stdin, + stderr, + exitOnCtrlC, + patchConsole, + onFrame, + }) + + // Register in the instances map so that code that looks up the Ink + // instance by stdout (e.g. external editor pause/resume) can find it. + instances.set(stdout, instance) + + return { + render: node => instance.render(node), + unmount: () => instance.unmount(), + waitUntilExit: () => instance.waitUntilExit(), + } +} + +const getOptions = ( + stdout: NodeJS.WriteStream | RenderOptions | undefined = {}, +): RenderOptions => { + if (stdout instanceof Stream) { + return { + stdout, + stdin: process.stdin, + } + } + + return stdout +} + +const getInstance = ( + stdout: NodeJS.WriteStream, + createInstance: () => Ink, +): Ink => { + let instance = instances.get(stdout) + + if (!instance) { + instance = createInstance() + instances.set(stdout, instance) + } + + return instance +} diff --git a/ink/screen.ts b/ink/screen.ts new file mode 100644 index 0000000..5b206d9 --- /dev/null +++ b/ink/screen.ts @@ -0,0 +1,1486 @@ +import { + type AnsiCode, + ansiCodesToString, + diffAnsiCodes, +} from '@alcalzone/ansi-tokenize' +import { + type Point, + type Rectangle, + type Size, + unionRect, +} from './layout/geometry.js' +import { BEL, ESC, SEP } from './termio/ansi.js' +import * as warn from './warn.js' + +// --- Shared Pools (interning for memory efficiency) --- + +// Character string pool shared across all screens. +// With a shared pool, interned char IDs are valid across screens, +// so blitRegion can copy IDs directly (no re-interning) and +// diffEach can compare IDs as integers (no string lookup). +export class CharPool { + private strings: string[] = [' ', ''] // Index 0 = space, 1 = empty (spacer) + private stringMap = new Map([ + [' ', 0], + ['', 1], + ]) + private ascii: Int32Array = initCharAscii() // charCode → index, -1 = not interned + + intern(char: string): number { + // ASCII fast-path: direct array lookup instead of Map.get + if (char.length === 1) { + const code = char.charCodeAt(0) + if (code < 128) { + const cached = this.ascii[code]! + if (cached !== -1) return cached + const index = this.strings.length + this.strings.push(char) + this.ascii[code] = index + return index + } + } + const existing = this.stringMap.get(char) + if (existing !== undefined) return existing + const index = this.strings.length + this.strings.push(char) + this.stringMap.set(char, index) + return index + } + + get(index: number): string { + return this.strings[index] ?? ' ' + } +} + +// Hyperlink string pool shared across all screens. +// Index 0 = no hyperlink. +export class HyperlinkPool { + private strings: string[] = [''] // Index 0 = no hyperlink + private stringMap = new Map() + + intern(hyperlink: string | undefined): number { + if (!hyperlink) return 0 + let id = this.stringMap.get(hyperlink) + if (id === undefined) { + id = this.strings.length + this.strings.push(hyperlink) + this.stringMap.set(hyperlink, id) + } + return id + } + + get(id: number): string | undefined { + return id === 0 ? undefined : this.strings[id] + } +} + +// SGR 7 (inverse) as an AnsiCode. endCode '\x1b[27m' flags VISIBLE_ON_SPACE +// so bit 0 of the resulting styleId is set → renderer won't skip inverted +// spaces as invisible. +const INVERSE_CODE: AnsiCode = { + type: 'ansi', + code: '\x1b[7m', + endCode: '\x1b[27m', +} +// Bold (SGR 1) — stacks cleanly, no reflow in monospace. endCode 22 +// also cancels dim (SGR 2); harmless here since we never add dim. +const BOLD_CODE: AnsiCode = { + type: 'ansi', + code: '\x1b[1m', + endCode: '\x1b[22m', +} +// Underline (SGR 4). Kept alongside yellow+bold — the underline is the +// unambiguous visible-on-any-theme marker. Yellow-bg-via-inverse can +// clash with existing bg colors (user-prompt style, tool chrome, syntax +// bg). If you see underline but no yellow, the yellow is being lost in +// the existing cell styling — the overlay IS finding the match. +const UNDERLINE_CODE: AnsiCode = { + type: 'ansi', + code: '\x1b[4m', + endCode: '\x1b[24m', +} +// fg→yellow (SGR 33). With inverse already in the stack, the terminal +// swaps fg↔bg at render — so yellow-fg becomes yellow-BG. Original bg +// becomes fg (readable on most themes: dark-bg → dark-text on yellow). +// endCode 39 is 'default fg' — cancels any prior fg color cleanly. +const YELLOW_FG_CODE: AnsiCode = { + type: 'ansi', + code: '\x1b[33m', + endCode: '\x1b[39m', +} + +export class StylePool { + private ids = new Map() + private styles: AnsiCode[][] = [] + private transitionCache = new Map() + readonly none: number + + constructor() { + this.none = this.intern([]) + } + + /** + * Intern a style and return its ID. Bit 0 of the ID encodes whether the + * style has a visible effect on space characters (background, inverse, + * underline, etc.). Foreground-only styles get even IDs; styles visible + * on spaces get odd IDs. This lets the renderer skip invisible spaces + * with a single bitmask check on the packed word. + */ + intern(styles: AnsiCode[]): number { + const key = styles.length === 0 ? '' : styles.map(s => s.code).join('\0') + let id = this.ids.get(key) + if (id === undefined) { + const rawId = this.styles.length + this.styles.push(styles.length === 0 ? [] : styles) + id = + (rawId << 1) | + (styles.length > 0 && hasVisibleSpaceEffect(styles) ? 1 : 0) + this.ids.set(key, id) + } + return id + } + + /** Recover styles from an encoded ID. Strips the bit-0 flag via >>> 1. */ + get(id: number): AnsiCode[] { + return this.styles[id >>> 1] ?? [] + } + + /** + * Returns the pre-serialized ANSI string to transition from one style to + * another. Cached by (fromId, toId) — zero allocations after first call + * for a given pair. + */ + transition(fromId: number, toId: number): string { + if (fromId === toId) return '' + const key = fromId * 0x100000 + toId + let str = this.transitionCache.get(key) + if (str === undefined) { + str = ansiCodesToString(diffAnsiCodes(this.get(fromId), this.get(toId))) + this.transitionCache.set(key, str) + } + return str + } + + /** + * Intern a style that is `base + inverse`. Cached by base ID so + * repeated calls for the same underlying style don't re-scan the + * AnsiCode[] array. Used by the selection overlay. + */ + private inverseCache = new Map() + withInverse(baseId: number): number { + let id = this.inverseCache.get(baseId) + if (id === undefined) { + const baseCodes = this.get(baseId) + // If already inverted, use as-is (avoids SGR 7 stacking) + const hasInverse = baseCodes.some(c => c.endCode === '\x1b[27m') + id = hasInverse ? baseId : this.intern([...baseCodes, INVERSE_CODE]) + this.inverseCache.set(baseId, id) + } + return id + } + + /** Inverse + bold + yellow-bg-via-fg-swap for the CURRENT search match. + * OTHER matches are plain inverse — bg inherits from the theme. Current + * gets a distinct yellow bg (via fg-then-inverse swap) plus bold weight + * so it stands out in a sea of inverse. Underline was too subtle. Zero + * reflow risk: all pure SGR overlays, per-cell, post-layout. The yellow + * overrides any existing fg (syntax highlighting) on those cells — fine, + * the "you are here" signal IS the point, syntax color can yield. */ + private currentMatchCache = new Map() + withCurrentMatch(baseId: number): number { + let id = this.currentMatchCache.get(baseId) + if (id === undefined) { + const baseCodes = this.get(baseId) + // Filter BOTH fg + bg so yellow-via-inverse is unambiguous. + // User-prompt cells have an explicit bg (grey box); with that bg + // still set, inverse swaps yellow-fg↔grey-bg → grey-on-yellow on + // SOME terminals, yellow-on-grey on others (inverse semantics vary + // when both colors are explicit). Filtering both gives clean + // yellow-bg + terminal-default-fg everywhere. Bold/dim/italic + // coexist — keep those. + const codes = baseCodes.filter( + c => c.endCode !== '\x1b[39m' && c.endCode !== '\x1b[49m', + ) + // fg-yellow FIRST so inverse swaps it to bg. Bold after inverse is + // fine — SGR 1 is fg-attribute-only, order-independent vs 7. + codes.push(YELLOW_FG_CODE) + if (!baseCodes.some(c => c.endCode === '\x1b[27m')) + codes.push(INVERSE_CODE) + if (!baseCodes.some(c => c.endCode === '\x1b[22m')) codes.push(BOLD_CODE) + // Underline as the unambiguous marker — yellow-bg can clash with + // existing bg styling (user-prompt bg, syntax bg). If you see + // underline but no yellow on a match, the overlay IS finding it; + // the yellow is just losing a styling fight. + if (!baseCodes.some(c => c.endCode === '\x1b[24m')) + codes.push(UNDERLINE_CODE) + id = this.intern(codes) + this.currentMatchCache.set(baseId, id) + } + return id + } + + /** + * Selection overlay: REPLACE the cell's background with a solid color + * while preserving its foreground (color, bold, italic, dim, underline). + * Matches native terminal selection — a dedicated bg color, not SGR-7 + * inverse. Inverse swaps fg/bg per-cell, which fragments visually over + * syntax-highlighted text (every fg color becomes a different bg stripe). + * + * Strips any existing bg (endCode 49m — REPLACES, so diff-added green + * etc. don't bleed through) and any existing inverse (endCode 27m — + * inverse on top of a solid bg would re-swap and look wrong). + * + * bg is set via setSelectionBg(); null → fallback to withInverse() so the + * overlay still works before theme wiring sets a color (tests, first frame). + * Cache is keyed by baseId only — setSelectionBg() clears it on change. + */ + private selectionBgCode: AnsiCode | null = null + private selectionBgCache = new Map() + setSelectionBg(bg: AnsiCode | null): void { + if (this.selectionBgCode?.code === bg?.code) return + this.selectionBgCode = bg + this.selectionBgCache.clear() + } + withSelectionBg(baseId: number): number { + const bg = this.selectionBgCode + if (bg === null) return this.withInverse(baseId) + let id = this.selectionBgCache.get(baseId) + if (id === undefined) { + // Keep everything except bg (49m) and inverse (27m). Fg, bold, dim, + // italic, underline, strikethrough all preserved. + const kept = this.get(baseId).filter( + c => c.endCode !== '\x1b[49m' && c.endCode !== '\x1b[27m', + ) + kept.push(bg) + id = this.intern(kept) + this.selectionBgCache.set(baseId, id) + } + return id + } +} + +// endCodes that produce visible effects on space characters +const VISIBLE_ON_SPACE = new Set([ + '\x1b[49m', // background color + '\x1b[27m', // inverse + '\x1b[24m', // underline + '\x1b[29m', // strikethrough + '\x1b[55m', // overline +]) + +function hasVisibleSpaceEffect(styles: AnsiCode[]): boolean { + for (const style of styles) { + if (VISIBLE_ON_SPACE.has(style.endCode)) return true + } + return false +} + +/** + * Cell width classification for handling double-wide characters (CJK, emoji, + * etc.) + * + * We use explicit spacer cells rather than inferring width at render time. This + * makes the data structure self-describing and simplifies cursor positioning + * logic. + * + * @see https://mitchellh.com/writing/grapheme-clusters-in-terminals + */ +// const enum is inlined at compile time - no runtime object, no property access +export const enum CellWidth { + // Not a wide character, cell width 1 + Narrow = 0, + // Wide character, cell width 2. This cell contains the actual character. + Wide = 1, + // Spacer occupying the second visual column of a wide character. Do not render. + SpacerTail = 2, + // Spacer at the end of a soft-wrapped line indicating that a wide character + // continues on the next line. Used for preserving wide character semantics + // across line breaks during soft wrapping. + SpacerHead = 3, +} + +export type Hyperlink = string | undefined + +/** + * Cell is a view type returned by cellAt(). Cells are stored as packed typed + * arrays internally to avoid GC pressure from allocating objects per cell. + */ +export type Cell = { + char: string + styleId: number + width: CellWidth + hyperlink: Hyperlink +} + +// Constants for empty/spacer cells to enable fast comparisons +// These are indices into the charStrings table, not codepoints +const EMPTY_CHAR_INDEX = 0 // ' ' (space) +const SPACER_CHAR_INDEX = 1 // '' (empty string for spacer cells) +// Unwritten cells are [EMPTY_CHAR_INDEX=0, packWord1(emptyStyleId=0,0,0)=0]. +// Since StylePool.none is always 0 (first intern), unwritten cells are +// indistinguishable from explicitly-cleared cells in the packed array. +// This is intentional: diffEach can compare raw ints with zero normalization. +// isEmptyCellByIndex checks if both words are 0 to identify "never visually written" cells. + +function initCharAscii(): Int32Array { + const table = new Int32Array(128) + table.fill(-1) + table[32] = EMPTY_CHAR_INDEX // ' ' (space) + return table +} + +// --- Packed cell layout --- +// Each cell is 2 consecutive Int32 elements in the cells array: +// word0 (cells[ci]): charId (full 32 bits) +// word1 (cells[ci + 1]): styleId[31:17] | hyperlinkId[16:2] | width[1:0] +const STYLE_SHIFT = 17 +const HYPERLINK_SHIFT = 2 +const HYPERLINK_MASK = 0x7fff // 15 bits +const WIDTH_MASK = 3 // 2 bits + +// Pack styleId, hyperlinkId, and width into a single Int32 +function packWord1( + styleId: number, + hyperlinkId: number, + width: number, +): number { + return (styleId << STYLE_SHIFT) | (hyperlinkId << HYPERLINK_SHIFT) | width +} + +// Unwritten cell as BigInt64 — both words are 0, so the 64-bit value is 0n. +// Used by BigInt64Array.fill() for bulk clears (resetScreen, clearRegion). +// Not used for comparison — BigInt element reads cause heap allocation. +const EMPTY_CELL_VALUE = 0n + +/** + * Screen uses a packed Int32Array instead of Cell objects to eliminate GC + * pressure. For a 200x120 screen, this avoids allocating 24,000 objects. + * + * Cell data is stored as 2 Int32s per cell in a single contiguous array: + * word0: charId (full 32 bits — index into CharPool) + * word1: styleId[31:17] | hyperlinkId[16:2] | width[1:0] + * + * This layout halves memory accesses in diffEach (2 int loads vs 4) and + * enables future SIMD comparison via Bun.indexOfFirstDifference. + */ +export type Screen = Size & { + // Packed cell data — 2 Int32s per cell: [charId, packed(styleId|hyperlinkId|width)] + // cells and cells64 are views over the same ArrayBuffer. + cells: Int32Array + cells64: BigInt64Array // 1 BigInt64 per cell — used for bulk fill in resetScreen/clearRegion + + // Shared pools — IDs are valid across all screens using the same pools + charPool: CharPool + hyperlinkPool: HyperlinkPool + + // Empty style ID for comparisons + emptyStyleId: number + + /** + * Bounding box of cells that were written to (not blitted) during rendering. + * Used by diff() to limit iteration to only the region that could have changed. + */ + damage: Rectangle | undefined + + /** + * Per-cell noSelect bitmap — 1 byte per cell, 1 = exclude from text + * selection (copy + highlight). Used by to mark gutters + * (line numbers, diff sigils) so click-drag over a diff yields clean + * copyable code. Fully reset each frame in resetScreen; blitRegion + * copies it alongside cells so the blit optimization preserves marks. + */ + noSelect: Uint8Array + + /** + * Per-ROW soft-wrap continuation marker. softWrap[r]=N>0 means row r + * is a word-wrap continuation of row r-1 (the `\n` before it was + * inserted by wrapAnsi, not in the source), and row r-1's written + * content ends at absolute column N (exclusive — cells [0..N) are the + * fragment, past N is unwritten padding). 0 means row r is NOT a + * continuation (hard newline or first row). Selection copy checks + * softWrap[r]>0 to join row r onto row r-1 without a newline, and + * reads softWrap[r+1] to know row r's content end when row r+1 + * continues from it. The content-end column is needed because an + * unwritten cell and a written-unstyled-space are indistinguishable in + * the packed typed array (both all-zero) — without it we'd either drop + * the word-separator space (trim) or include trailing padding (no + * trim). This encoding (continuation-on-self, prev-content-end-here) + * is chosen so shiftRows preserves the is-continuation semantics: when + * row r scrolls off the top and row r+1 shifts to row r, sw[r] gets + * old sw[r+1] — which correctly says the new row r is a continuation + * of what's now in scrolledOffAbove. Reset each frame; copied by + * blitRegion/shiftRows. + */ + softWrap: Int32Array +} + +function isEmptyCellByIndex(screen: Screen, index: number): boolean { + // An empty/unwritten cell has both words === 0: + // word0 = EMPTY_CHAR_INDEX (0), word1 = packWord1(emptyStyleId=0, 0, 0) = 0. + const ci = index << 1 + return screen.cells[ci] === 0 && screen.cells[ci | 1] === 0 +} + +export function isEmptyCellAt(screen: Screen, x: number, y: number): boolean { + if (x < 0 || y < 0 || x >= screen.width || y >= screen.height) return true + return isEmptyCellByIndex(screen, y * screen.width + x) +} + +/** + * Check if a Cell (view object) represents an empty cell. + */ +export function isCellEmpty(screen: Screen, cell: Cell): boolean { + // Check if cell looks like an empty cell (space, empty style, narrow, no link). + // Note: After cellAt mapping, unwritten cells have emptyStyleId, so this + // returns true for both unwritten AND cleared cells. Use isEmptyCellAt + // for the internal distinction. + return ( + cell.char === ' ' && + cell.styleId === screen.emptyStyleId && + cell.width === CellWidth.Narrow && + !cell.hyperlink + ) +} +// Intern a hyperlink string and return its ID (0 = no hyperlink) +function internHyperlink(screen: Screen, hyperlink: Hyperlink): number { + return screen.hyperlinkPool.intern(hyperlink) +} + +// --- + +export function createScreen( + width: number, + height: number, + styles: StylePool, + charPool: CharPool, + hyperlinkPool: HyperlinkPool, +): Screen { + // Warn if dimensions are not valid integers (likely bad yoga layout output) + warn.ifNotInteger(width, 'createScreen width') + warn.ifNotInteger(height, 'createScreen height') + + // Ensure width and height are valid integers to prevent crashes + if (!Number.isInteger(width) || width < 0) { + width = Math.max(0, Math.floor(width) || 0) + } + if (!Number.isInteger(height) || height < 0) { + height = Math.max(0, Math.floor(height) || 0) + } + + const size = width * height + + // Allocate one buffer, two views: Int32Array for per-word access, + // BigInt64Array for bulk fill in resetScreen/clearRegion. + // ArrayBuffer is zero-filled, which is exactly the empty cell value: + // [EMPTY_CHAR_INDEX=0, packWord1(emptyStyleId=0,0,0)=0]. + const buf = new ArrayBuffer(size << 3) // 8 bytes per cell + const cells = new Int32Array(buf) + const cells64 = new BigInt64Array(buf) + + return { + width, + height, + cells, + cells64, + charPool, + hyperlinkPool, + emptyStyleId: styles.none, + damage: undefined, + noSelect: new Uint8Array(size), + softWrap: new Int32Array(height), + } +} + +/** + * Reset an existing screen for reuse, avoiding allocation of new typed arrays. + * Resizes if needed and clears all cells to empty/unwritten state. + * + * For double-buffering, this allows swapping between front and back buffers + * without allocating new Screen objects each frame. + */ +export function resetScreen( + screen: Screen, + width: number, + height: number, +): void { + // Warn if dimensions are not valid integers + warn.ifNotInteger(width, 'resetScreen width') + warn.ifNotInteger(height, 'resetScreen height') + + // Ensure width and height are valid integers to prevent crashes + if (!Number.isInteger(width) || width < 0) { + width = Math.max(0, Math.floor(width) || 0) + } + if (!Number.isInteger(height) || height < 0) { + height = Math.max(0, Math.floor(height) || 0) + } + + const size = width * height + + // Resize if needed (only grow, to avoid reallocations) + if (screen.cells64.length < size) { + const buf = new ArrayBuffer(size << 3) + screen.cells = new Int32Array(buf) + screen.cells64 = new BigInt64Array(buf) + screen.noSelect = new Uint8Array(size) + } + if (screen.softWrap.length < height) { + screen.softWrap = new Int32Array(height) + } + + // Reset all cells — single fill call, no loop + screen.cells64.fill(EMPTY_CELL_VALUE, 0, size) + screen.noSelect.fill(0, 0, size) + screen.softWrap.fill(0, 0, height) + + // Update dimensions + screen.width = width + screen.height = height + + // Shared pools accumulate — no clearing needed. Unique char/hyperlink sets are bounded. + + // Clear damage tracking + screen.damage = undefined +} + +/** + * Re-intern a screen's char and hyperlink IDs into new pools. + * Used for generational pool reset — after migrating, the screen's + * typed arrays contain valid IDs for the new pools, and the old pools + * can be GC'd. + * + * O(width * height) but only called occasionally (e.g., between conversation turns). + */ +export function migrateScreenPools( + screen: Screen, + charPool: CharPool, + hyperlinkPool: HyperlinkPool, +): void { + const oldCharPool = screen.charPool + const oldHyperlinkPool = screen.hyperlinkPool + if (oldCharPool === charPool && oldHyperlinkPool === hyperlinkPool) return + + const size = screen.width * screen.height + const cells = screen.cells + + // Re-intern chars and hyperlinks in a single pass, stride by 2 + for (let ci = 0; ci < size << 1; ci += 2) { + // Re-intern charId (word0) + const oldCharId = cells[ci]! + cells[ci] = charPool.intern(oldCharPool.get(oldCharId)) + + // Re-intern hyperlinkId (packed in word1) + const word1 = cells[ci + 1]! + const oldHyperlinkId = (word1 >>> HYPERLINK_SHIFT) & HYPERLINK_MASK + if (oldHyperlinkId !== 0) { + const oldStr = oldHyperlinkPool.get(oldHyperlinkId) + const newHyperlinkId = hyperlinkPool.intern(oldStr) + // Repack word1 with new hyperlinkId, preserving styleId and width + const styleId = word1 >>> STYLE_SHIFT + const width = word1 & WIDTH_MASK + cells[ci + 1] = packWord1(styleId, newHyperlinkId, width) + } + } + + screen.charPool = charPool + screen.hyperlinkPool = hyperlinkPool +} + +/** + * Get a Cell view at the given position. Returns a new object each call - + * this is intentional as cells are stored packed, not as objects. + */ +export function cellAt(screen: Screen, x: number, y: number): Cell | undefined { + if (x < 0 || y < 0 || x >= screen.width || y >= screen.height) + return undefined + return cellAtIndex(screen, y * screen.width + x) +} +/** + * Get a Cell view by pre-computed array index. Skips bounds checks and + * index computation — caller must ensure index is valid. + */ +export function cellAtIndex(screen: Screen, index: number): Cell { + const ci = index << 1 + const word1 = screen.cells[ci + 1]! + const hid = (word1 >>> HYPERLINK_SHIFT) & HYPERLINK_MASK + return { + // Unwritten cells have charIndex=0 (EMPTY_CHAR_INDEX); charPool.get(0) returns ' ' + char: screen.charPool.get(screen.cells[ci]!), + styleId: word1 >>> STYLE_SHIFT, + width: word1 & WIDTH_MASK, + hyperlink: hid === 0 ? undefined : screen.hyperlinkPool.get(hid), + } +} + +/** + * Get a Cell at the given index, or undefined if it has no visible content. + * Returns undefined for spacer cells (charId 1), empty unstyled spaces, and + * fg-only styled spaces that match lastRenderedStyleId (cursor-forward + * produces an identical visual result, avoiding a Cell allocation). + * + * @param lastRenderedStyleId - styleId of the last rendered cell on this + * line, or -1 if none yet. + */ +export function visibleCellAtIndex( + cells: Int32Array, + charPool: CharPool, + hyperlinkPool: HyperlinkPool, + index: number, + lastRenderedStyleId: number, +): Cell | undefined { + const ci = index << 1 + const charId = cells[ci]! + if (charId === 1) return undefined // spacer + const word1 = cells[ci + 1]! + // For spaces: 0x3fffc masks bits 2-17 (hyperlinkId + styleId visibility + // bit). If zero, the space has no hyperlink and at most a fg-only style. + // Then word1 >>> STYLE_SHIFT is the foreground style — skip if it's zero + // (truly invisible) or matches the last rendered style on this line. + if (charId === 0 && (word1 & 0x3fffc) === 0) { + const fgStyle = word1 >>> STYLE_SHIFT + if (fgStyle === 0 || fgStyle === lastRenderedStyleId) return undefined + } + const hid = (word1 >>> HYPERLINK_SHIFT) & HYPERLINK_MASK + return { + char: charPool.get(charId), + styleId: word1 >>> STYLE_SHIFT, + width: word1 & WIDTH_MASK, + hyperlink: hid === 0 ? undefined : hyperlinkPool.get(hid), + } +} + +/** + * Write cell data into an existing Cell object to avoid allocation. + * Caller must ensure index is valid. + */ +function cellAtCI(screen: Screen, ci: number, out: Cell): void { + const w1 = ci | 1 + const word1 = screen.cells[w1]! + out.char = screen.charPool.get(screen.cells[ci]!) + out.styleId = word1 >>> STYLE_SHIFT + out.width = word1 & WIDTH_MASK + const hid = (word1 >>> HYPERLINK_SHIFT) & HYPERLINK_MASK + out.hyperlink = hid === 0 ? undefined : screen.hyperlinkPool.get(hid) +} + +export function charInCellAt( + screen: Screen, + x: number, + y: number, +): string | undefined { + if (x < 0 || y < 0 || x >= screen.width || y >= screen.height) + return undefined + const ci = (y * screen.width + x) << 1 + return screen.charPool.get(screen.cells[ci]!) +} +/** + * Set a cell, optionally creating a spacer for wide characters. + * + * Wide characters (CJK, emoji) occupy 2 cells in the buffer: + * 1. First cell: Contains the actual character with width = Wide + * 2. Second cell: Spacer cell with width = SpacerTail (empty, not rendered) + * + * If the cell has width = Wide, this function automatically creates the + * corresponding SpacerTail in the next column. This two-cell model keeps + * the buffer aligned to visual columns, making cursor positioning + * straightforward. + * + * TODO: When soft-wrapping is implemented, SpacerHead cells will be explicitly + * placed by the wrapping logic at line-end positions where wide characters + * wrap to the next line. This function doesn't need to handle SpacerHead + * automatically - it will be set directly by the wrapping code. + */ +export function setCellAt( + screen: Screen, + x: number, + y: number, + cell: Cell, +): void { + if (x < 0 || y < 0 || x >= screen.width || y >= screen.height) return + const ci = (y * screen.width + x) << 1 + const cells = screen.cells + + // When a Wide char is overwritten by a Narrow char, its SpacerTail remains + // as a ghost cell that the diff/render pipeline skips, causing stale content + // to leak through from previous frames. + const prevWidth = cells[ci + 1]! & WIDTH_MASK + if (prevWidth === CellWidth.Wide && cell.width !== CellWidth.Wide) { + const spacerX = x + 1 + if (spacerX < screen.width) { + const spacerCI = ci + 2 + if ((cells[spacerCI + 1]! & WIDTH_MASK) === CellWidth.SpacerTail) { + cells[spacerCI] = EMPTY_CHAR_INDEX + cells[spacerCI + 1] = packWord1( + screen.emptyStyleId, + 0, + CellWidth.Narrow, + ) + } + } + } + // Track cleared Wide position for damage expansion below + let clearedWideX = -1 + if ( + prevWidth === CellWidth.SpacerTail && + cell.width !== CellWidth.SpacerTail + ) { + // Overwriting a SpacerTail: clear the orphaned Wide char at (x-1). + // Keeping the wide character with Narrow width would cause the terminal + // to still render it with width 2, desyncing the cursor model. + if (x > 0) { + const wideCI = ci - 2 + if ((cells[wideCI + 1]! & WIDTH_MASK) === CellWidth.Wide) { + cells[wideCI] = EMPTY_CHAR_INDEX + cells[wideCI + 1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow) + clearedWideX = x - 1 + } + } + } + + // Pack cell data into cells array + cells[ci] = internCharString(screen, cell.char) + cells[ci + 1] = packWord1( + cell.styleId, + internHyperlink(screen, cell.hyperlink), + cell.width, + ) + + // Track damage - expand bounds in place instead of allocating new objects + // Include the main cell position and any cleared orphan cells + const minX = clearedWideX >= 0 ? Math.min(x, clearedWideX) : x + const damage = screen.damage + if (damage) { + const right = damage.x + damage.width + const bottom = damage.y + damage.height + if (minX < damage.x) { + damage.width += damage.x - minX + damage.x = minX + } else if (x >= right) { + damage.width = x - damage.x + 1 + } + if (y < damage.y) { + damage.height += damage.y - y + damage.y = y + } else if (y >= bottom) { + damage.height = y - damage.y + 1 + } + } else { + screen.damage = { x: minX, y, width: x - minX + 1, height: 1 } + } + + // If this is a wide character, create a spacer in the next column + if (cell.width === CellWidth.Wide) { + const spacerX = x + 1 + if (spacerX < screen.width) { + const spacerCI = ci + 2 + // If the cell we're overwriting with our SpacerTail is itself Wide, + // clear ITS SpacerTail at x+2 too. Otherwise the orphan SpacerTail + // makes diffEach report it as `added` and log-update's skip-spacer + // rule prevents clearing whatever prev content was at that column. + // Scenario: [a, 💻, spacer] → [本, spacer, ORPHAN spacer] when + // yoga squishes a💻 to height 0 and 本 renders at the same y. + if ((cells[spacerCI + 1]! & WIDTH_MASK) === CellWidth.Wide) { + const orphanCI = spacerCI + 2 + if ( + spacerX + 1 < screen.width && + (cells[orphanCI + 1]! & WIDTH_MASK) === CellWidth.SpacerTail + ) { + cells[orphanCI] = EMPTY_CHAR_INDEX + cells[orphanCI + 1] = packWord1( + screen.emptyStyleId, + 0, + CellWidth.Narrow, + ) + } + } + cells[spacerCI] = SPACER_CHAR_INDEX + cells[spacerCI + 1] = packWord1( + screen.emptyStyleId, + 0, + CellWidth.SpacerTail, + ) + + // Expand damage to include SpacerTail so diff() scans it + const d = screen.damage + if (d && spacerX >= d.x + d.width) { + d.width = spacerX - d.x + 1 + } + } + } +} + +/** + * Replace the styleId of a cell in-place without disturbing char, width, + * or hyperlink. Preserves empty cells as-is (char stays ' '). Tracks damage + * for the cell so diffEach picks up the change. + */ +export function setCellStyleId( + screen: Screen, + x: number, + y: number, + styleId: number, +): void { + if (x < 0 || y < 0 || x >= screen.width || y >= screen.height) return + const ci = (y * screen.width + x) << 1 + const cells = screen.cells + const word1 = cells[ci + 1]! + const width = word1 & WIDTH_MASK + // Skip spacer cells — inverse on the head cell visually covers both columns + if (width === CellWidth.SpacerTail || width === CellWidth.SpacerHead) return + const hid = (word1 >>> HYPERLINK_SHIFT) & HYPERLINK_MASK + cells[ci + 1] = packWord1(styleId, hid, width) + // Expand damage so diffEach scans this cell + const d = screen.damage + if (d) { + screen.damage = unionRect(d, { x, y, width: 1, height: 1 }) + } else { + screen.damage = { x, y, width: 1, height: 1 } + } +} + +/** + * Intern a character string via the screen's shared CharPool. + * Supports grapheme clusters like family emoji. + */ +function internCharString(screen: Screen, char: string): number { + return screen.charPool.intern(char) +} + +/** + * Bulk-copy a rectangular region from src to dst using TypedArray.set(). + * Single cells.set() call per row (or one call for contiguous blocks). + * Damage is computed once for the whole region. + * + * Clamps negative regionX/regionY to 0 (matching clearRegion) — absolute- + * positioned overlays in tiny terminals can compute negative screen coords. + * maxX/maxY should already be clamped to both screen bounds by the caller. + */ +export function blitRegion( + dst: Screen, + src: Screen, + regionX: number, + regionY: number, + maxX: number, + maxY: number, +): void { + regionX = Math.max(0, regionX) + regionY = Math.max(0, regionY) + if (regionX >= maxX || regionY >= maxY) return + + const rowLen = maxX - regionX + const srcStride = src.width << 1 + const dstStride = dst.width << 1 + const rowBytes = rowLen << 1 // 2 Int32s per cell + const srcCells = src.cells + const dstCells = dst.cells + const srcNoSel = src.noSelect + const dstNoSel = dst.noSelect + + // softWrap is per-row — copy the row range regardless of stride/width. + // Partial-width blits still carry the row's wrap provenance since the + // blitted content (a cached ink-text node) is what set the bit. + dst.softWrap.set(src.softWrap.subarray(regionY, maxY), regionY) + + // Fast path: contiguous memory when copying full-width rows at same stride + if (regionX === 0 && maxX === src.width && src.width === dst.width) { + const srcStart = regionY * srcStride + const totalBytes = (maxY - regionY) * srcStride + dstCells.set( + srcCells.subarray(srcStart, srcStart + totalBytes), + srcStart, // srcStart === dstStart when strides match and regionX === 0 + ) + // noSelect is 1 byte/cell vs cells' 8 — same region, different scale + const nsStart = regionY * src.width + const nsLen = (maxY - regionY) * src.width + dstNoSel.set(srcNoSel.subarray(nsStart, nsStart + nsLen), nsStart) + } else { + // Per-row copy for partial-width or mismatched-stride regions + let srcRowCI = regionY * srcStride + (regionX << 1) + let dstRowCI = regionY * dstStride + (regionX << 1) + let srcRowNS = regionY * src.width + regionX + let dstRowNS = regionY * dst.width + regionX + for (let y = regionY; y < maxY; y++) { + dstCells.set(srcCells.subarray(srcRowCI, srcRowCI + rowBytes), dstRowCI) + dstNoSel.set(srcNoSel.subarray(srcRowNS, srcRowNS + rowLen), dstRowNS) + srcRowCI += srcStride + dstRowCI += dstStride + srcRowNS += src.width + dstRowNS += dst.width + } + } + + // Compute damage once for the whole region + const regionRect = { + x: regionX, + y: regionY, + width: rowLen, + height: maxY - regionY, + } + if (dst.damage) { + dst.damage = unionRect(dst.damage, regionRect) + } else { + dst.damage = regionRect + } + + // Handle wide char at right edge: spacer might be outside blit region + // but still within dst bounds. Per-row check only at the boundary column. + if (maxX < dst.width) { + let srcLastCI = (regionY * src.width + (maxX - 1)) << 1 + let dstSpacerCI = (regionY * dst.width + maxX) << 1 + let wroteSpacerOutsideRegion = false + for (let y = regionY; y < maxY; y++) { + if ((srcCells[srcLastCI + 1]! & WIDTH_MASK) === CellWidth.Wide) { + dstCells[dstSpacerCI] = SPACER_CHAR_INDEX + dstCells[dstSpacerCI + 1] = packWord1( + dst.emptyStyleId, + 0, + CellWidth.SpacerTail, + ) + wroteSpacerOutsideRegion = true + } + srcLastCI += srcStride + dstSpacerCI += dstStride + } + // Expand damage to include SpacerTail column if we wrote any + if (wroteSpacerOutsideRegion && dst.damage) { + const rightEdge = dst.damage.x + dst.damage.width + if (rightEdge === maxX) { + dst.damage = { ...dst.damage, width: dst.damage.width + 1 } + } + } + } +} + +/** + * Bulk-clear a rectangular region of the screen. + * Uses BigInt64Array.fill() for fast row clears. + * Handles wide character boundary cleanup at region edges. + */ +export function clearRegion( + screen: Screen, + regionX: number, + regionY: number, + regionWidth: number, + regionHeight: number, +): void { + const startX = Math.max(0, regionX) + const startY = Math.max(0, regionY) + const maxX = Math.min(regionX + regionWidth, screen.width) + const maxY = Math.min(regionY + regionHeight, screen.height) + if (startX >= maxX || startY >= maxY) return + + const cells = screen.cells + const cells64 = screen.cells64 + const screenWidth = screen.width + const rowBase = startY * screenWidth + let damageMinX = startX + let damageMaxX = maxX + + // EMPTY_CELL_VALUE (0n) matches the zero-initialized state: + // word0=EMPTY_CHAR_INDEX(0), word1=packWord1(0,0,0)=0 + if (startX === 0 && maxX === screenWidth) { + // Full-width: single fill, no boundary checks needed + cells64.fill( + EMPTY_CELL_VALUE, + rowBase, + rowBase + (maxY - startY) * screenWidth, + ) + } else { + // Partial-width: single loop handles boundary cleanup and fill per row. + const stride = screenWidth << 1 // 2 Int32s per cell + const rowLen = maxX - startX + const checkLeft = startX > 0 + const checkRight = maxX < screenWidth + let leftEdge = (rowBase + startX) << 1 + let rightEdge = (rowBase + maxX - 1) << 1 + let fillStart = rowBase + startX + + for (let y = startY; y < maxY; y++) { + // Left boundary: if cell at startX is a SpacerTail, the Wide char + // at startX-1 (outside the region) will be orphaned. Clear it. + if (checkLeft) { + // leftEdge points to word0 of cell at startX; +1 is its word1 + if ((cells[leftEdge + 1]! & WIDTH_MASK) === CellWidth.SpacerTail) { + // word1 of cell at startX-1 is leftEdge-1; word0 is leftEdge-2 + const prevW1 = leftEdge - 1 + if ((cells[prevW1]! & WIDTH_MASK) === CellWidth.Wide) { + cells[prevW1 - 1] = EMPTY_CHAR_INDEX + cells[prevW1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow) + damageMinX = startX - 1 + } + } + } + + // Right boundary: if cell at maxX-1 is Wide, its SpacerTail at maxX + // (outside the region) will be orphaned. Clear it. + if (checkRight) { + // rightEdge points to word0 of cell at maxX-1; +1 is its word1 + if ((cells[rightEdge + 1]! & WIDTH_MASK) === CellWidth.Wide) { + // word1 of cell at maxX is rightEdge+3 (+2 to next word0, +1 to word1) + const nextW1 = rightEdge + 3 + if ((cells[nextW1]! & WIDTH_MASK) === CellWidth.SpacerTail) { + cells[nextW1 - 1] = EMPTY_CHAR_INDEX + cells[nextW1] = packWord1(screen.emptyStyleId, 0, CellWidth.Narrow) + damageMaxX = maxX + 1 + } + } + } + + cells64.fill(EMPTY_CELL_VALUE, fillStart, fillStart + rowLen) + leftEdge += stride + rightEdge += stride + fillStart += screenWidth + } + } + + // Update damage once for the whole region + const regionRect = { + x: damageMinX, + y: startY, + width: damageMaxX - damageMinX, + height: maxY - startY, + } + if (screen.damage) { + screen.damage = unionRect(screen.damage, regionRect) + } else { + screen.damage = regionRect + } +} + +/** + * Shift full-width rows within [top, bottom] (inclusive, 0-indexed) by n. + * n > 0 shifts UP (simulating CSI n S); n < 0 shifts DOWN (CSI n T). + * Vacated rows are cleared. Does NOT update damage. Both cells and the + * noSelect bitmap are shifted so text-selection markers stay aligned when + * this is applied to next.screen during scroll fast path. + */ +export function shiftRows( + screen: Screen, + top: number, + bottom: number, + n: number, +): void { + if (n === 0 || top < 0 || bottom >= screen.height || top > bottom) return + const w = screen.width + const cells64 = screen.cells64 + const noSel = screen.noSelect + const sw = screen.softWrap + const absN = Math.abs(n) + if (absN > bottom - top) { + cells64.fill(EMPTY_CELL_VALUE, top * w, (bottom + 1) * w) + noSel.fill(0, top * w, (bottom + 1) * w) + sw.fill(0, top, bottom + 1) + return + } + if (n > 0) { + // SU: row top+n..bottom → top..bottom-n; clear bottom-n+1..bottom + cells64.copyWithin(top * w, (top + n) * w, (bottom + 1) * w) + noSel.copyWithin(top * w, (top + n) * w, (bottom + 1) * w) + sw.copyWithin(top, top + n, bottom + 1) + cells64.fill(EMPTY_CELL_VALUE, (bottom - n + 1) * w, (bottom + 1) * w) + noSel.fill(0, (bottom - n + 1) * w, (bottom + 1) * w) + sw.fill(0, bottom - n + 1, bottom + 1) + } else { + // SD: row top..bottom+n → top-n..bottom; clear top..top-n-1 + cells64.copyWithin((top - n) * w, top * w, (bottom + n + 1) * w) + noSel.copyWithin((top - n) * w, top * w, (bottom + n + 1) * w) + sw.copyWithin(top - n, top, bottom + n + 1) + cells64.fill(EMPTY_CELL_VALUE, top * w, (top - n) * w) + noSel.fill(0, top * w, (top - n) * w) + sw.fill(0, top, top - n) + } +} + +// Matches OSC 8 ; ; URI BEL +const OSC8_REGEX = new RegExp(`^${ESC}\\]8${SEP}${SEP}([^${BEL}]*)${BEL}$`) +// OSC8 prefix: ESC ] 8 ; — cheap check to skip regex for the vast majority of styles (SGR = ESC [) +export const OSC8_PREFIX = `${ESC}]8${SEP}` + +export function extractHyperlinkFromStyles( + styles: AnsiCode[], +): Hyperlink | null { + for (const style of styles) { + const code = style.code + if (code.length < 5 || !code.startsWith(OSC8_PREFIX)) continue + const match = code.match(OSC8_REGEX) + if (match) { + return match[1] || null + } + } + return null +} + +export function filterOutHyperlinkStyles(styles: AnsiCode[]): AnsiCode[] { + return styles.filter( + style => + !style.code.startsWith(OSC8_PREFIX) || !OSC8_REGEX.test(style.code), + ) +} + +// --- + +/** + * Returns an array of all changes between two screens. Used by tests. + * Production code should use diffEach() to avoid allocations. + */ +export function diff( + prev: Screen, + next: Screen, +): [point: Point, removed: Cell | undefined, added: Cell | undefined][] { + const output: [Point, Cell | undefined, Cell | undefined][] = [] + diffEach(prev, next, (x, y, removed, added) => { + // Copy cells since diffEach reuses the objects + output.push([ + { x, y }, + removed ? { ...removed } : undefined, + added ? { ...added } : undefined, + ]) + }) + return output +} + +type DiffCallback = ( + x: number, + y: number, + removed: Cell | undefined, + added: Cell | undefined, +) => boolean | void + +/** + * Like diff(), but calls a callback for each change instead of building an array. + * Reuses two Cell objects to avoid per-change allocations. The callback must not + * retain references to the Cell objects — their contents are overwritten each call. + * + * Returns true if the callback ever returned true (early exit signal). + */ +export function diffEach( + prev: Screen, + next: Screen, + cb: DiffCallback, +): boolean { + const prevWidth = prev.width + const nextWidth = next.width + const prevHeight = prev.height + const nextHeight = next.height + + let region: Rectangle + if (prevWidth === 0 && prevHeight === 0) { + region = { x: 0, y: 0, width: nextWidth, height: nextHeight } + } else if (next.damage) { + region = next.damage + if (prev.damage) { + region = unionRect(region, prev.damage) + } + } else if (prev.damage) { + region = prev.damage + } else { + region = { x: 0, y: 0, width: 0, height: 0 } + } + + if (prevHeight > nextHeight) { + region = unionRect(region, { + x: 0, + y: nextHeight, + width: prevWidth, + height: prevHeight - nextHeight, + }) + } + if (prevWidth > nextWidth) { + region = unionRect(region, { + x: nextWidth, + y: 0, + width: prevWidth - nextWidth, + height: prevHeight, + }) + } + + const maxHeight = Math.max(prevHeight, nextHeight) + const maxWidth = Math.max(prevWidth, nextWidth) + const endY = Math.min(region.y + region.height, maxHeight) + const endX = Math.min(region.x + region.width, maxWidth) + + if (prevWidth === nextWidth) { + return diffSameWidth(prev, next, region.x, endX, region.y, endY, cb) + } + return diffDifferentWidth(prev, next, region.x, endX, region.y, endY, cb) +} + +/** + * Scan for the next cell that differs between two Int32Arrays. + * Returns the number of matching cells before the first difference, + * or `count` if all cells match. Tiny and pure for JIT inlining. + */ +function findNextDiff( + a: Int32Array, + b: Int32Array, + w0: number, + count: number, +): number { + for (let i = 0; i < count; i++, w0 += 2) { + const w1 = w0 | 1 + if (a[w0] !== b[w0] || a[w1] !== b[w1]) return i + } + return count +} + +/** + * Diff one row where both screens are in bounds. + * Scans for differences with findNextDiff, unpacks and calls cb for each. + */ +function diffRowBoth( + prevCells: Int32Array, + nextCells: Int32Array, + prev: Screen, + next: Screen, + ci: number, + y: number, + startX: number, + endX: number, + prevCell: Cell, + nextCell: Cell, + cb: DiffCallback, +): boolean { + let x = startX + while (x < endX) { + const skip = findNextDiff(prevCells, nextCells, ci, endX - x) + x += skip + ci += skip << 1 + if (x >= endX) break + cellAtCI(prev, ci, prevCell) + cellAtCI(next, ci, nextCell) + if (cb(x, y, prevCell, nextCell)) return true + x++ + ci += 2 + } + return false +} + +/** + * Emit removals for a row that only exists in prev (height shrank). + * Cannot skip empty cells — the terminal still has content from the + * previous frame that needs to be cleared. + */ +function diffRowRemoved( + prev: Screen, + ci: number, + y: number, + startX: number, + endX: number, + prevCell: Cell, + cb: DiffCallback, +): boolean { + for (let x = startX; x < endX; x++, ci += 2) { + cellAtCI(prev, ci, prevCell) + if (cb(x, y, prevCell, undefined)) return true + } + return false +} + +/** + * Emit additions for a row that only exists in next (height grew). + * Skips empty/unwritten cells. + */ +function diffRowAdded( + nextCells: Int32Array, + next: Screen, + ci: number, + y: number, + startX: number, + endX: number, + nextCell: Cell, + cb: DiffCallback, +): boolean { + for (let x = startX; x < endX; x++, ci += 2) { + if (nextCells[ci] === 0 && nextCells[ci | 1] === 0) continue + cellAtCI(next, ci, nextCell) + if (cb(x, y, undefined, nextCell)) return true + } + return false +} + +/** + * Diff two screens with identical width. + * Dispatches each row to a small, JIT-friendly function. + */ +function diffSameWidth( + prev: Screen, + next: Screen, + startX: number, + endX: number, + startY: number, + endY: number, + cb: DiffCallback, +): boolean { + const prevCells = prev.cells + const nextCells = next.cells + const width = prev.width + const prevHeight = prev.height + const nextHeight = next.height + const stride = width << 1 + + const prevCell: Cell = { + char: ' ', + styleId: 0, + width: CellWidth.Narrow, + hyperlink: undefined, + } + const nextCell: Cell = { + char: ' ', + styleId: 0, + width: CellWidth.Narrow, + hyperlink: undefined, + } + + const rowEndX = Math.min(endX, width) + let rowCI = (startY * width + startX) << 1 + + for (let y = startY; y < endY; y++) { + const prevIn = y < prevHeight + const nextIn = y < nextHeight + + if (prevIn && nextIn) { + if ( + diffRowBoth( + prevCells, + nextCells, + prev, + next, + rowCI, + y, + startX, + rowEndX, + prevCell, + nextCell, + cb, + ) + ) + return true + } else if (prevIn) { + if (diffRowRemoved(prev, rowCI, y, startX, rowEndX, prevCell, cb)) + return true + } else if (nextIn) { + if ( + diffRowAdded(nextCells, next, rowCI, y, startX, rowEndX, nextCell, cb) + ) + return true + } + + rowCI += stride + } + + return false +} + +/** + * Fallback: diff two screens with different widths (resize). + * Separate indices for prev and next cells arrays. + */ +function diffDifferentWidth( + prev: Screen, + next: Screen, + startX: number, + endX: number, + startY: number, + endY: number, + cb: DiffCallback, +): boolean { + const prevWidth = prev.width + const nextWidth = next.width + const prevCells = prev.cells + const nextCells = next.cells + + const prevCell: Cell = { + char: ' ', + styleId: 0, + width: CellWidth.Narrow, + hyperlink: undefined, + } + const nextCell: Cell = { + char: ' ', + styleId: 0, + width: CellWidth.Narrow, + hyperlink: undefined, + } + + const prevStride = prevWidth << 1 + const nextStride = nextWidth << 1 + let prevRowCI = (startY * prevWidth + startX) << 1 + let nextRowCI = (startY * nextWidth + startX) << 1 + + for (let y = startY; y < endY; y++) { + const prevIn = y < prev.height + const nextIn = y < next.height + const prevEndX = prevIn ? Math.min(endX, prevWidth) : startX + const nextEndX = nextIn ? Math.min(endX, nextWidth) : startX + const bothEndX = Math.min(prevEndX, nextEndX) + + let prevCI = prevRowCI + let nextCI = nextRowCI + + for (let x = startX; x < bothEndX; x++) { + if ( + prevCells[prevCI] === nextCells[nextCI] && + prevCells[prevCI + 1] === nextCells[nextCI + 1] + ) { + prevCI += 2 + nextCI += 2 + continue + } + cellAtCI(prev, prevCI, prevCell) + cellAtCI(next, nextCI, nextCell) + prevCI += 2 + nextCI += 2 + if (cb(x, y, prevCell, nextCell)) return true + } + + if (prevEndX > bothEndX) { + prevCI = prevRowCI + ((bothEndX - startX) << 1) + for (let x = bothEndX; x < prevEndX; x++) { + cellAtCI(prev, prevCI, prevCell) + prevCI += 2 + if (cb(x, y, prevCell, undefined)) return true + } + } + + if (nextEndX > bothEndX) { + nextCI = nextRowCI + ((bothEndX - startX) << 1) + for (let x = bothEndX; x < nextEndX; x++) { + if (nextCells[nextCI] === 0 && nextCells[nextCI | 1] === 0) { + nextCI += 2 + continue + } + cellAtCI(next, nextCI, nextCell) + nextCI += 2 + if (cb(x, y, undefined, nextCell)) return true + } + } + + prevRowCI += prevStride + nextRowCI += nextStride + } + + return false +} + +/** + * Mark a rectangular region as noSelect (exclude from text selection). + * Clamps to screen bounds. Called from output.ts when a box + * renders. No damage tracking — noSelect doesn't affect terminal output, + * only getSelectedText/applySelectionOverlay which read it directly. + */ +export function markNoSelectRegion( + screen: Screen, + x: number, + y: number, + width: number, + height: number, +): void { + const maxX = Math.min(x + width, screen.width) + const maxY = Math.min(y + height, screen.height) + const noSel = screen.noSelect + const stride = screen.width + for (let row = Math.max(0, y); row < maxY; row++) { + const rowStart = row * stride + noSel.fill(1, rowStart + Math.max(0, x), rowStart + maxX) + } +} diff --git a/ink/searchHighlight.ts b/ink/searchHighlight.ts new file mode 100644 index 0000000..c7c8647 --- /dev/null +++ b/ink/searchHighlight.ts @@ -0,0 +1,93 @@ +import { + CellWidth, + cellAtIndex, + type Screen, + type StylePool, + setCellStyleId, +} from './screen.js' + +/** + * Highlight all visible occurrences of `query` in the screen buffer by + * inverting cell styles (SGR 7). Post-render, same damage-tracking machinery + * as applySelectionOverlay — the diff picks up highlighted cells as ordinary + * changes, LogUpdate stays a pure diff engine. + * + * Case-insensitive. Handles wide characters (CJK, emoji) by building a + * col-of-char map per row — the Nth character isn't at col N when wide chars + * are present (each occupies 2 cells: head + SpacerTail). + * + * This ONLY inverts — there is no "current match" logic here. The yellow + * current-match overlay is handled separately by applyPositionedHighlight + * (render-to-screen.ts), which writes on top using positions scanned from + * the target message's DOM subtree. + * + * Returns true if any match was highlighted (damage gate — caller forces + * full-frame damage when true). + */ +export function applySearchHighlight( + screen: Screen, + query: string, + stylePool: StylePool, +): boolean { + if (!query) return false + const lq = query.toLowerCase() + const qlen = lq.length + const w = screen.width + const noSelect = screen.noSelect + const height = screen.height + + let applied = false + for (let row = 0; row < height; row++) { + const rowOff = row * w + // Build row text (already lowercased) + code-unit→cell-index map. + // Three skip conditions, all aligned with setCellStyleId / + // extractRowText (selection.ts): + // - SpacerTail: 2nd cell of a wide char, no char of its own + // - SpacerHead: end-of-line padding when a wide char wraps + // - noSelect: gutters (⎿, line numbers) — same exclusion as + // applySelectionOverlay. "Highlight what you see" still holds for + // content; gutters aren't search targets. + // Lowercasing per-char (not on the joined string at the end) means + // codeUnitToCell maps positions in the LOWERCASED text — U+0130 + // (Turkish İ) lowercases to 2 code units, so lowering the joined + // string would desync indexOf positions from the map. + let text = '' + const colOf: number[] = [] + const codeUnitToCell: number[] = [] + for (let col = 0; col < w; col++) { + const idx = rowOff + col + const cell = cellAtIndex(screen, idx) + if ( + cell.width === CellWidth.SpacerTail || + cell.width === CellWidth.SpacerHead || + noSelect[idx] === 1 + ) { + continue + } + const lc = cell.char.toLowerCase() + const cellIdx = colOf.length + for (let i = 0; i < lc.length; i++) { + codeUnitToCell.push(cellIdx) + } + text += lc + colOf.push(col) + } + + let pos = text.indexOf(lq) + while (pos >= 0) { + applied = true + const startCi = codeUnitToCell[pos]! + const endCi = codeUnitToCell[pos + qlen - 1]! + for (let ci = startCi; ci <= endCi; ci++) { + const col = colOf[ci]! + const cell = cellAtIndex(screen, rowOff + col) + setCellStyleId(screen, col, row, stylePool.withInverse(cell.styleId)) + } + // Non-overlapping advance (less/vim/grep/Ctrl+F). pos+1 would find + // 'aa' at 0 AND 1 in 'aaa' → double-invert cell 1. + pos = text.indexOf(lq, pos + qlen) + } + } + + return applied +} diff --git a/ink/selection.ts b/ink/selection.ts new file mode 100644 index 0000000..0025534 --- /dev/null +++ b/ink/selection.ts @@ -0,0 +1,917 @@ +/** + * Text selection state for fullscreen mode. + * + * Tracks a linear selection in screen-buffer coordinates (0-indexed col/row). + * Selection is line-based: cells from (startCol, startRow) through + * (endCol, endRow) inclusive, wrapping across line boundaries. This matches + * terminal-native selection behavior (not rectangular/block). + * + * The selection is stored as ANCHOR (where the drag started) + FOCUS (where + * the cursor is now). The rendered highlight normalizes to start ≤ end. + */ + +import { clamp } from './layout/geometry.js' +import type { Screen, StylePool } from './screen.js' +import { CellWidth, cellAt, cellAtIndex, setCellStyleId } from './screen.js' + +type Point = { col: number; row: number } + +export type SelectionState = { + /** Where the mouse-down occurred. Null when no selection. */ + anchor: Point | null + /** Current drag position (updated on mouse-move while dragging). */ + focus: Point | null + /** True between mouse-down and mouse-up. */ + isDragging: boolean + /** For word/line mode: the initial word/line bounds from the first + * multi-click. Drag extends from this span to the word/line at the + * current mouse position so the original word/line stays selected + * even when dragging backward past it. Null ⇔ char mode. The kind + * tells extendSelection whether to snap to word or line boundaries. */ + anchorSpan: { lo: Point; hi: Point; kind: 'word' | 'line' } | null + /** Text from rows that scrolled out ABOVE the viewport during + * drag-to-scroll. The screen buffer only holds the current viewport, + * so without this accumulator, dragging down past the bottom edge + * loses the top of the selection once the anchor clamps. Prepended + * to the on-screen text by getSelectedText. Reset on start/clear. */ + scrolledOffAbove: string[] + /** Symmetric: rows scrolled out BELOW when dragging up. Appended. */ + scrolledOffBelow: string[] + /** Soft-wrap bits parallel to scrolledOffAbove — true means the row + * is a continuation of the one before it (the `\n` was inserted by + * word-wrap, not in the source). Captured alongside the text at + * scroll time since the screen's softWrap bitmap shifts with content. + * getSelectedText uses these to join wrapped rows back into logical + * lines. */ + scrolledOffAboveSW: boolean[] + /** Parallel to scrolledOffBelow. */ + scrolledOffBelowSW: boolean[] + /** Pre-clamp anchor row. Set when shiftSelection clamps anchor so a + * reverse scroll can restore the true position and pop accumulators. + * Without this, PgDn (clamps anchor) → PgUp leaves anchor at the wrong + * row AND scrolledOffAbove stale — highlight ≠ copy. Undefined when + * anchor is in-bounds (no clamp debt). Cleared on start/clear. */ + virtualAnchorRow?: number + /** Same for focus. */ + virtualFocusRow?: number + /** True if the mouse-down that started this selection had the alt + * modifier set (SGR button bit 0x08). On macOS xterm.js this is a + * signal that VS Code's macOptionClickForcesSelection is OFF — if it + * were on, xterm.js would have consumed the event for native selection + * and we'd never receive it. Used by the footer to show the right hint. */ + lastPressHadAlt: boolean +} + +export function createSelectionState(): SelectionState { + return { + anchor: null, + focus: null, + isDragging: false, + anchorSpan: null, + scrolledOffAbove: [], + scrolledOffBelow: [], + scrolledOffAboveSW: [], + scrolledOffBelowSW: [], + lastPressHadAlt: false, + } +} + +export function startSelection( + s: SelectionState, + col: number, + row: number, +): void { + s.anchor = { col, row } + // Focus is not set until the first drag motion. A click-release with no + // drag leaves focus null → hasSelection/selectionBounds return false/null + // via the `!s.focus` check, so a bare click never highlights a cell. + s.focus = null + s.isDragging = true + s.anchorSpan = null + s.scrolledOffAbove = [] + s.scrolledOffBelow = [] + s.scrolledOffAboveSW = [] + s.scrolledOffBelowSW = [] + s.virtualAnchorRow = undefined + s.virtualFocusRow = undefined + s.lastPressHadAlt = false +} + +export function updateSelection( + s: SelectionState, + col: number, + row: number, +): void { + if (!s.isDragging) return + // First motion at the same cell as anchor is a no-op. Terminals in mode + // 1002 can fire a drag event at the anchor cell (sub-pixel tremor, or a + // motion-release pair). Setting focus here would turn a bare click into + // a 1-cell selection and clobber the clipboard via useCopyOnSelect. Once + // focus is set (real drag), we track normally including back to anchor. + if (!s.focus && s.anchor && s.anchor.col === col && s.anchor.row === row) + return + s.focus = { col, row } +} + +export function finishSelection(s: SelectionState): void { + s.isDragging = false + // Keep anchor/focus so highlight stays visible and text can be copied. + // Clear via clearSelection() on Esc or after copy. +} + +export function clearSelection(s: SelectionState): void { + s.anchor = null + s.focus = null + s.isDragging = false + s.anchorSpan = null + s.scrolledOffAbove = [] + s.scrolledOffBelow = [] + s.scrolledOffAboveSW = [] + s.scrolledOffBelowSW = [] + s.virtualAnchorRow = undefined + s.virtualFocusRow = undefined + s.lastPressHadAlt = false +} + +// Unicode-aware word character matcher: letters (any script), digits, +// and the punctuation set iTerm2 treats as word-part by default. +// Matching iTerm2's default means double-clicking a path like +// `/usr/bin/bash` or `~/.claude/config.json` selects the whole thing, +// which is the muscle memory most macOS terminal users have. +// iTerm2 default "characters considered part of a word": /-+\~_. +const WORD_CHAR = /[\p{L}\p{N}_/.\-+~\\]/u + +/** + * Character class for double-click word-expansion. Cells with the same + * class as the clicked cell are included in the selection; a class change + * is a boundary. Matches typical terminal-emulator behavior (iTerm2 etc.): + * double-click on `foo` selects `foo`, on `->` selects `->`, on spaces + * selects the whitespace run. + */ +function charClass(c: string): 0 | 1 | 2 { + if (c === ' ' || c === '') return 0 + if (WORD_CHAR.test(c)) return 1 + return 2 +} + +/** + * Find the bounds of the same-class character run at (col, row). Returns + * null if the click is out of bounds or lands on a noSelect cell. Used by + * selectWordAt (initial double-click) and extendWordSelection (drag). + */ +function wordBoundsAt( + screen: Screen, + col: number, + row: number, +): { lo: number; hi: number } | null { + if (row < 0 || row >= screen.height) return null + const width = screen.width + const noSelect = screen.noSelect + const rowOff = row * width + + // If the click landed on the spacer tail of a wide char, step back to + // the head so the class check sees the actual grapheme. + let c = col + if (c > 0) { + const cell = cellAt(screen, c, row) + if (cell && cell.width === CellWidth.SpacerTail) c -= 1 + } + if (c < 0 || c >= width || noSelect[rowOff + c] === 1) return null + + const startCell = cellAt(screen, c, row) + if (!startCell) return null + const cls = charClass(startCell.char) + + // Expand left: include cells of the same class, stop at noSelect or + // class change. SpacerTail cells are stepped over (the wide-char head + // at the preceding column determines the class). + let lo = c + while (lo > 0) { + const prev = lo - 1 + if (noSelect[rowOff + prev] === 1) break + const pc = cellAt(screen, prev, row) + if (!pc) break + if (pc.width === CellWidth.SpacerTail) { + // Step over the spacer to the wide-char head + if (prev === 0 || noSelect[rowOff + prev - 1] === 1) break + const head = cellAt(screen, prev - 1, row) + if (!head || charClass(head.char) !== cls) break + lo = prev - 1 + continue + } + if (charClass(pc.char) !== cls) break + lo = prev + } + + // Expand right: same logic, skipping spacer tails. + let hi = c + while (hi < width - 1) { + const next = hi + 1 + if (noSelect[rowOff + next] === 1) break + const nc = cellAt(screen, next, row) + if (!nc) break + if (nc.width === CellWidth.SpacerTail) { + // Include the spacer tail in the selection range (it belongs to + // the wide char at hi) and continue past it. + hi = next + continue + } + if (charClass(nc.char) !== cls) break + hi = next + } + + return { lo, hi } +} + +/** -1 if a < b, 1 if a > b, 0 if equal (reading order: row then col). */ +function comparePoints(a: Point, b: Point): number { + if (a.row !== b.row) return a.row < b.row ? -1 : 1 + if (a.col !== b.col) return a.col < b.col ? -1 : 1 + return 0 +} + +/** + * Select the word at (col, row) by scanning the screen buffer for the + * bounds of the same-class character run. Mutates the selection in place. + * No-op if the click is out of bounds or lands on a noSelect cell. + * Sets isDragging=true and anchorSpan so a subsequent drag extends the + * selection word-by-word (native macOS behavior). + */ +export function selectWordAt( + s: SelectionState, + screen: Screen, + col: number, + row: number, +): void { + const b = wordBoundsAt(screen, col, row) + if (!b) return + const lo = { col: b.lo, row } + const hi = { col: b.hi, row } + s.anchor = lo + s.focus = hi + s.isDragging = true + s.anchorSpan = { lo, hi, kind: 'word' } +} + +// Printable ASCII minus terminal URL delimiters. Restricting to single- +// codeunit ASCII keeps cell-count === string-index, so the column-span +// check below is exact (no wide-char/grapheme drift). +const URL_BOUNDARY = new Set([...'<>"\'` ']) +function isUrlChar(c: string): boolean { + if (c.length !== 1) return false + const code = c.charCodeAt(0) + return code >= 0x21 && code <= 0x7e && !URL_BOUNDARY.has(c) +} + +/** + * Scan the screen buffer for a plain-text URL at (col, row). Mirrors the + * terminal's native Cmd+Click URL detection, which fullscreen mode's mouse + * tracking intercepts. Called from getHyperlinkAt as a fallback when the + * cell has no OSC 8 hyperlink. + */ +export function findPlainTextUrlAt( + screen: Screen, + col: number, + row: number, +): string | undefined { + if (row < 0 || row >= screen.height) return undefined + const width = screen.width + const noSelect = screen.noSelect + const rowOff = row * width + + let c = col + if (c > 0) { + const cell = cellAt(screen, c, row) + if (cell && cell.width === CellWidth.SpacerTail) c -= 1 + } + if (c < 0 || c >= width || noSelect[rowOff + c] === 1) return undefined + + const startCell = cellAt(screen, c, row) + if (!startCell || !isUrlChar(startCell.char)) return undefined + + // Expand left/right to the bounds of the URL-char run. URLs are ASCII + // (CellWidth.Narrow, 1 codeunit), so hitting a non-ASCII/wide/spacer + // cell is a boundary — no need to step over spacers like wordBoundsAt. + let lo = c + while (lo > 0) { + const prev = lo - 1 + if (noSelect[rowOff + prev] === 1) break + const pc = cellAt(screen, prev, row) + if (!pc || pc.width !== CellWidth.Narrow || !isUrlChar(pc.char)) break + lo = prev + } + let hi = c + while (hi < width - 1) { + const next = hi + 1 + if (noSelect[rowOff + next] === 1) break + const nc = cellAt(screen, next, row) + if (!nc || nc.width !== CellWidth.Narrow || !isUrlChar(nc.char)) break + hi = next + } + + let token = '' + for (let i = lo; i <= hi; i++) token += cellAt(screen, i, row)!.char + + // 1 cell = 1 char across [lo, hi] (ASCII-only run), so string index = + // column offset. Find the last scheme anchor at or before the click — + // a run like `https://a.com,https://b.com` has two, and clicking the + // second should return the second URL, not the greedy match of both. + const clickIdx = c - lo + const schemeRe = /(?:https?|file):\/\//g + let urlStart = -1 + let urlEnd = token.length + for (let m; (m = schemeRe.exec(token)); ) { + if (m.index > clickIdx) { + urlEnd = m.index + break + } + urlStart = m.index + } + if (urlStart < 0) return undefined + let url = token.slice(urlStart, urlEnd) + + // Strip trailing sentence punctuation. For closers () ] }, only strip + // if unbalanced — `/wiki/Foo_(bar)` keeps `)`, `/arr[0]` keeps `]`. + const OPENER: Record = { ')': '(', ']': '[', '}': '{' } + while (url.length > 0) { + const last = url.at(-1)! + if ('.,;:!?'.includes(last)) { + url = url.slice(0, -1) + continue + } + const opener = OPENER[last] + if (!opener) break + let opens = 0 + let closes = 0 + for (let i = 0; i < url.length; i++) { + const ch = url.charAt(i) + if (ch === opener) opens++ + else if (ch === last) closes++ + } + if (closes > opens) url = url.slice(0, -1) + else break + } + + // urlStart already guarantees click >= URL start; check right edge. + if (clickIdx >= urlStart + url.length) return undefined + + return url +} + +/** + * Select the entire row. Sets isDragging=true and anchorSpan so a + * subsequent drag extends the selection line-by-line. The anchor/focus + * span from col 0 to width-1; getSelectedText handles noSelect skipping + * and trailing-whitespace trimming so the copied text is just the visible + * line content. + */ +export function selectLineAt( + s: SelectionState, + screen: Screen, + row: number, +): void { + if (row < 0 || row >= screen.height) return + const lo = { col: 0, row } + const hi = { col: screen.width - 1, row } + s.anchor = lo + s.focus = hi + s.isDragging = true + s.anchorSpan = { lo, hi, kind: 'line' } +} + +/** + * Extend a word/line-mode selection to the word/line at (col, row). The + * anchor span (the original multi-clicked word/line) stays selected; the + * selection grows from that span to the word/line at the current mouse + * position. Word mode falls back to the raw cell when the mouse is over a + * noSelect cell or out of bounds, so dragging into gutters still extends. + */ +export function extendSelection( + s: SelectionState, + screen: Screen, + col: number, + row: number, +): void { + if (!s.isDragging || !s.anchorSpan) return + const span = s.anchorSpan + let mLo: Point + let mHi: Point + if (span.kind === 'word') { + const b = wordBoundsAt(screen, col, row) + mLo = { col: b ? b.lo : col, row } + mHi = { col: b ? b.hi : col, row } + } else { + const r = clamp(row, 0, screen.height - 1) + mLo = { col: 0, row: r } + mHi = { col: screen.width - 1, row: r } + } + if (comparePoints(mHi, span.lo) < 0) { + // Mouse target ends before anchor span: extend backward. + s.anchor = span.hi + s.focus = mLo + } else if (comparePoints(mLo, span.hi) > 0) { + // Mouse target starts after anchor span: extend forward. + s.anchor = span.lo + s.focus = mHi + } else { + // Mouse overlaps the anchor span: just select the anchor span. + s.anchor = span.lo + s.focus = span.hi + } +} + +/** Semantic keyboard focus moves. See moveSelectionFocus in ink.tsx for + * how screen bounds + row-wrap are applied. */ +export type FocusMove = + | 'left' + | 'right' + | 'up' + | 'down' + | 'lineStart' + | 'lineEnd' + +/** + * Set focus to (col, row) for keyboard selection extension (shift+arrow). + * Anchor stays fixed; selection grows or shrinks depending on where focus + * moves relative to anchor. Drops to char mode (clears anchorSpan) — + * native macOS does this too: shift+arrow after a double-click word-select + * extends char-by-char from the word edge, not word-by-word. Scrolled-off + * accumulators are preserved: keyboard-extending a drag-scrolled selection + * keeps the off-screen rows. Caller supplies coords already clamped/wrapped. + */ +export function moveFocus(s: SelectionState, col: number, row: number): void { + if (!s.focus) return + s.anchorSpan = null + s.focus = { col, row } + // Explicit user repositioning — any stale virtual focus (from a prior + // shiftSelection clamp) no longer reflects intent. Anchor stays put so + // virtualAnchorRow is still valid for its own round-trip. + s.virtualFocusRow = undefined +} + +/** + * Shift anchor AND focus by dRow, clamped to [minRow, maxRow]. Used for + * keyboard scroll (PgUp/PgDn/ctrl+u/d/b/f): the whole selection must track + * the content, unlike drag-to-scroll where focus stays at the mouse. Any + * point that hits a clamp bound gets its col reset to the full-width edge — + * its original content scrolled off-screen and was captured by + * captureScrolledRows, so the col constraint was already consumed. Keeping + * it would truncate the NEW content now at that screen row. Clamp col is 0 + * for dRow<0 (scrolling down, top leaves, 'above' semantics) or width-1 for + * dRow>0 (scrolling up, bottom leaves, 'below' semantics). + * + * If both ends overshoot the SAME viewport edge (select text → Home/End/g/G + * jumps far enough that both are out of view), clear — otherwise both clamp + * to the same corner cell and a ghost 1-cell highlight lingers, and + * getSelectedText returns one unrelated char from that corner. Symmetric + * with shiftSelectionForFollow's top-edge check, but bidirectional: keyboard + * scroll can jump either way. + */ +export function shiftSelection( + s: SelectionState, + dRow: number, + minRow: number, + maxRow: number, + width: number, +): void { + if (!s.anchor || !s.focus) return + // Virtual rows track pre-clamp positions so reverse scrolls restore + // correctly. Without this, clamp(5→0) + shift(+10) = 10, not the true 5, + // and scrolledOffAbove stays stale (highlight ≠ copy). + const vAnchor = (s.virtualAnchorRow ?? s.anchor.row) + dRow + const vFocus = (s.virtualFocusRow ?? s.focus.row) + dRow + if ( + (vAnchor < minRow && vFocus < minRow) || + (vAnchor > maxRow && vFocus > maxRow) + ) { + clearSelection(s) + return + } + // Debt = how far the nearer endpoint overshoots each edge. When debt + // shrinks (reverse scroll), those rows are back on-screen — pop from + // the accumulator so getSelectedText doesn't double-count them. + const oldMin = Math.min( + s.virtualAnchorRow ?? s.anchor.row, + s.virtualFocusRow ?? s.focus.row, + ) + const oldMax = Math.max( + s.virtualAnchorRow ?? s.anchor.row, + s.virtualFocusRow ?? s.focus.row, + ) + const oldAboveDebt = Math.max(0, minRow - oldMin) + const oldBelowDebt = Math.max(0, oldMax - maxRow) + const newAboveDebt = Math.max(0, minRow - Math.min(vAnchor, vFocus)) + const newBelowDebt = Math.max(0, Math.max(vAnchor, vFocus) - maxRow) + if (newAboveDebt < oldAboveDebt) { + // scrolledOffAbove pushes newest at the end (closest to on-screen). + const drop = oldAboveDebt - newAboveDebt + s.scrolledOffAbove.length -= drop + s.scrolledOffAboveSW.length = s.scrolledOffAbove.length + } + if (newBelowDebt < oldBelowDebt) { + // scrolledOffBelow unshifts newest at the front (closest to on-screen). + const drop = oldBelowDebt - newBelowDebt + s.scrolledOffBelow.splice(0, drop) + s.scrolledOffBelowSW.splice(0, drop) + } + // Invariant: accumulator length ≤ debt. If the accumulator exceeds debt, + // the excess is stale — e.g., moveFocus cleared virtualFocusRow without + // trimming the accumulator, orphaning entries the pop above can never + // reach because oldDebt was ALREADY 0. Truncate to debt (keeping the + // newest = closest-to-on-screen entries). Check newDebt (not oldDebt): + // captureScrolledRows runs BEFORE this shift in the real flow (ink.tsx), + // so at entry the accumulator is populated but oldDebt is still 0 — + // that's the normal establish-debt path, not stale. + if (s.scrolledOffAbove.length > newAboveDebt) { + // Above pushes newest at END → keep END. + s.scrolledOffAbove = + newAboveDebt > 0 ? s.scrolledOffAbove.slice(-newAboveDebt) : [] + s.scrolledOffAboveSW = + newAboveDebt > 0 ? s.scrolledOffAboveSW.slice(-newAboveDebt) : [] + } + if (s.scrolledOffBelow.length > newBelowDebt) { + // Below unshifts newest at FRONT → keep FRONT. + s.scrolledOffBelow = s.scrolledOffBelow.slice(0, newBelowDebt) + s.scrolledOffBelowSW = s.scrolledOffBelowSW.slice(0, newBelowDebt) + } + // Clamp col depends on which EDGE (not dRow direction): virtual tracking + // means a top-clamped point can stay top-clamped during a dRow>0 reverse + // shift — dRow-based clampCol would give it the bottom col. + const shift = (p: Point, vRow: number): Point => { + if (vRow < minRow) return { col: 0, row: minRow } + if (vRow > maxRow) return { col: width - 1, row: maxRow } + return { col: p.col, row: vRow } + } + s.anchor = shift(s.anchor, vAnchor) + s.focus = shift(s.focus, vFocus) + s.virtualAnchorRow = + vAnchor < minRow || vAnchor > maxRow ? vAnchor : undefined + s.virtualFocusRow = vFocus < minRow || vFocus > maxRow ? vFocus : undefined + // anchorSpan not virtual-tracked: it's for word/line extend-on-drag, + // irrelevant to the keyboard-scroll round-trip case. + if (s.anchorSpan) { + const sp = (p: Point): Point => { + const r = p.row + dRow + if (r < minRow) return { col: 0, row: minRow } + if (r > maxRow) return { col: width - 1, row: maxRow } + return { col: p.col, row: r } + } + s.anchorSpan = { + lo: sp(s.anchorSpan.lo), + hi: sp(s.anchorSpan.hi), + kind: s.anchorSpan.kind, + } + } +} + +/** + * Shift the anchor row by dRow, clamped to [minRow, maxRow]. Used during + * drag-to-scroll: when the ScrollBox scrolls by N rows, the content that + * was under the anchor is now at a different viewport row, so the anchor + * must follow it. Focus is left unchanged (it stays at the mouse position). + */ +export function shiftAnchor( + s: SelectionState, + dRow: number, + minRow: number, + maxRow: number, +): void { + if (!s.anchor) return + // Same virtual-row tracking as shiftSelection/shiftSelectionForFollow: the + // drag→follow transition hands off to shiftSelectionForFollow, which reads + // (virtualAnchorRow ?? anchor.row). Without this, drag-phase clamping + // leaves virtual undefined → follow initializes from the already-clamped + // row, under-counting total drift → shiftSelection's invariant-restore + // prematurely clears valid drag-phase accumulator entries. + const raw = (s.virtualAnchorRow ?? s.anchor.row) + dRow + s.anchor = { col: s.anchor.col, row: clamp(raw, minRow, maxRow) } + s.virtualAnchorRow = raw < minRow || raw > maxRow ? raw : undefined + // anchorSpan not virtual-tracked (word/line extend, irrelevant to + // keyboard-scroll round-trip) — plain clamp from current row. + if (s.anchorSpan) { + const shift = (p: Point): Point => ({ + col: p.col, + row: clamp(p.row + dRow, minRow, maxRow), + }) + s.anchorSpan = { + lo: shift(s.anchorSpan.lo), + hi: shift(s.anchorSpan.hi), + kind: s.anchorSpan.kind, + } + } +} + +/** + * Shift the whole selection (anchor + focus + anchorSpan) by dRow, clamped + * to [minRow, maxRow]. Used when sticky/auto-follow scrolls the ScrollBox + * while a selection is active — native terminal behavior is for the + * highlight to walk up the screen with the text (not stay at the same + * screen position). + * + * Differs from shiftAnchor: during drag-to-scroll, focus tracks the live + * mouse position and only anchor follows the text. During streaming-follow, + * the selection is text-anchored at both ends — both must move. The + * isDragging check in ink.tsx picks which shift to apply. + * + * If both ends would shift strictly BELOW minRow (unclamped), the selected + * text has scrolled entirely off the top. Clear it — otherwise a single + * inverted cell lingers at the viewport top as a ghost (native terminals + * drop the selection when it leaves scrollback). Landing AT minRow is + * still valid: that cell holds the correct text. Returns true if the + * selection was cleared so the caller can notify React-land subscribers + * (useHasSelection) — the caller is inside onRender so it can't use + * notifySelectionChange (recursion), must fire listeners directly. + */ +export function shiftSelectionForFollow( + s: SelectionState, + dRow: number, + minRow: number, + maxRow: number, +): boolean { + if (!s.anchor) return false + // Mirror shiftSelection: compute raw (unclamped) positions from virtual + // if set, else current. This handles BOTH the update path (virtual already + // set from a prior keyboard scroll) AND the initialize path (first clamp + // happens HERE via follow-scroll, no prior keyboard scroll). Without the + // initialize path, follow-scroll-first leaves virtual undefined even + // though the clamp below occurred → a later PgUp computes debt from the + // clamped row instead of the true pre-clamp row and never pops the + // accumulator — getSelectedText double-counts the off-screen rows. + const rawAnchor = (s.virtualAnchorRow ?? s.anchor.row) + dRow + const rawFocus = s.focus + ? (s.virtualFocusRow ?? s.focus.row) + dRow + : undefined + if (rawAnchor < minRow && rawFocus !== undefined && rawFocus < minRow) { + clearSelection(s) + return true + } + // Clamp from raw, not p.row+dRow — so a virtual position coming back + // in-bounds lands at the TRUE position, not the stale clamped one. + s.anchor = { col: s.anchor.col, row: clamp(rawAnchor, minRow, maxRow) } + if (s.focus && rawFocus !== undefined) { + s.focus = { col: s.focus.col, row: clamp(rawFocus, minRow, maxRow) } + } + s.virtualAnchorRow = + rawAnchor < minRow || rawAnchor > maxRow ? rawAnchor : undefined + s.virtualFocusRow = + rawFocus !== undefined && (rawFocus < minRow || rawFocus > maxRow) + ? rawFocus + : undefined + // anchorSpan not virtual-tracked (word/line extend, irrelevant to + // keyboard-scroll round-trip) — plain clamp from current row. + if (s.anchorSpan) { + const shift = (p: Point): Point => ({ + col: p.col, + row: clamp(p.row + dRow, minRow, maxRow), + }) + s.anchorSpan = { + lo: shift(s.anchorSpan.lo), + hi: shift(s.anchorSpan.hi), + kind: s.anchorSpan.kind, + } + } + return false +} + +export function hasSelection(s: SelectionState): boolean { + return s.anchor !== null && s.focus !== null +} + +/** + * Normalized selection bounds: start is always before end in reading order. + * Returns null if no active selection. + */ +export function selectionBounds(s: SelectionState): { + start: { col: number; row: number } + end: { col: number; row: number } +} | null { + if (!s.anchor || !s.focus) return null + return comparePoints(s.anchor, s.focus) <= 0 + ? { start: s.anchor, end: s.focus } + : { start: s.focus, end: s.anchor } +} + +/** + * Check if a cell at (col, row) is within the current selection range. + * Used by the renderer to apply inverse style. + */ +export function isCellSelected( + s: SelectionState, + col: number, + row: number, +): boolean { + const b = selectionBounds(s) + if (!b) return false + const { start, end } = b + if (row < start.row || row > end.row) return false + if (row === start.row && col < start.col) return false + if (row === end.row && col > end.col) return false + return true +} + +/** Extract text from one screen row. When the next row is a soft-wrap + * continuation (screen.softWrap[row+1]>0), clamp to that content-end + * column and skip the trailing trim so the word-separator space survives + * the join. See Screen.softWrap for why the clamp is necessary. */ +function extractRowText( + screen: Screen, + row: number, + colStart: number, + colEnd: number, +): string { + const noSelect = screen.noSelect + const rowOff = row * screen.width + const contentEnd = row + 1 < screen.height ? screen.softWrap[row + 1]! : 0 + const lastCol = contentEnd > 0 ? Math.min(colEnd, contentEnd - 1) : colEnd + let line = '' + for (let col = colStart; col <= lastCol; col++) { + // Skip cells marked noSelect (gutters, line numbers, diff sigils). + // Check before cellAt to avoid the decode cost for excluded cells. + if (noSelect[rowOff + col] === 1) continue + const cell = cellAt(screen, col, row) + if (!cell) continue + // Skip spacer tails (second half of wide chars) — the head already + // contains the full grapheme. SpacerHead is a blank at line-end. + if ( + cell.width === CellWidth.SpacerTail || + cell.width === CellWidth.SpacerHead + ) { + continue + } + line += cell.char + } + return contentEnd > 0 ? line : line.replace(/\s+$/, '') +} + +/** Accumulator for selected text that merges soft-wrapped rows back + * into logical lines. push(text, sw) appends a newline before text + * only when sw=false (i.e. the row starts a new logical line). Rows + * with sw=true are concatenated onto the previous row. */ +function joinRows( + lines: string[], + text: string, + sw: boolean | undefined, +): void { + if (sw && lines.length > 0) { + lines[lines.length - 1] += text + } else { + lines.push(text) + } +} + +/** + * Extract text from the screen buffer within the selection range. + * Rows are joined with newlines unless the screen's softWrap bitmap + * marks a row as a word-wrap continuation — those rows are concatenated + * onto the previous row so the copied text matches the logical source + * line, not the visual wrapped layout. Trailing whitespace on the last + * fragment of each logical line is trimmed. Wide-char spacer cells are + * skipped. Rows that scrolled out of the viewport during drag-to-scroll + * are joined back in from the scrolledOffAbove/Below accumulators along + * with their captured softWrap bits. + */ +export function getSelectedText(s: SelectionState, screen: Screen): string { + const b = selectionBounds(s) + if (!b) return '' + const { start, end } = b + const sw = screen.softWrap + const lines: string[] = [] + + for (let i = 0; i < s.scrolledOffAbove.length; i++) { + joinRows(lines, s.scrolledOffAbove[i]!, s.scrolledOffAboveSW[i]) + } + + for (let row = start.row; row <= end.row; row++) { + const rowStart = row === start.row ? start.col : 0 + const rowEnd = row === end.row ? end.col : screen.width - 1 + joinRows(lines, extractRowText(screen, row, rowStart, rowEnd), sw[row]! > 0) + } + + for (let i = 0; i < s.scrolledOffBelow.length; i++) { + joinRows(lines, s.scrolledOffBelow[i]!, s.scrolledOffBelowSW[i]) + } + + return lines.join('\n') +} + +/** + * Capture text from rows about to scroll out of the viewport during + * drag-to-scroll, BEFORE scrollBy overwrites them. Only the rows that + * intersect the selection are captured, using the selection's col bounds + * for the anchor-side boundary row. After capturing the anchor row, the + * anchor.col AND anchorSpan cols are reset to the full-width boundary so + * subsequent captures and the final getSelectedText don't re-apply a stale + * col constraint to content that's no longer under the original anchor. + * Both span cols are reset (not just the near side): after a blocked + * reversal the drag can flip direction, and extendSelection then reads the + * OPPOSITE span side — which would otherwise still hold the original word + * boundary and truncate one subsequently-captured row. + * + * side='above': rows scrolling out the top (dragging down, anchor=start). + * side='below': rows scrolling out the bottom (dragging up, anchor=end). + */ +export function captureScrolledRows( + s: SelectionState, + screen: Screen, + firstRow: number, + lastRow: number, + side: 'above' | 'below', +): void { + const b = selectionBounds(s) + if (!b || firstRow > lastRow) return + const { start, end } = b + // Intersect [firstRow, lastRow] with [start.row, end.row]. Rows outside + // the selection aren't captured — they weren't selected. + const lo = Math.max(firstRow, start.row) + const hi = Math.min(lastRow, end.row) + if (lo > hi) return + + const width = screen.width + const sw = screen.softWrap + const captured: string[] = [] + const capturedSW: boolean[] = [] + for (let row = lo; row <= hi; row++) { + const colStart = row === start.row ? start.col : 0 + const colEnd = row === end.row ? end.col : width - 1 + captured.push(extractRowText(screen, row, colStart, colEnd)) + capturedSW.push(sw[row]! > 0) + } + + if (side === 'above') { + // Newest rows go at the bottom of the above-accumulator (closest to + // the on-screen content in reading order). + s.scrolledOffAbove.push(...captured) + s.scrolledOffAboveSW.push(...capturedSW) + // We just captured the top of the selection. The anchor (=start when + // dragging down) is now pointing at content that will scroll out; its + // col constraint was applied to the captured row. Reset to col 0 so + // the NEXT tick and the final getSelectedText read the full row. + if (s.anchor && s.anchor.row === start.row && lo === start.row) { + s.anchor = { col: 0, row: s.anchor.row } + if (s.anchorSpan) { + s.anchorSpan = { + kind: s.anchorSpan.kind, + lo: { col: 0, row: s.anchorSpan.lo.row }, + hi: { col: width - 1, row: s.anchorSpan.hi.row }, + } + } + } + } else { + // Newest rows go at the TOP of the below-accumulator — they're + // closest to the on-screen content. + s.scrolledOffBelow.unshift(...captured) + s.scrolledOffBelowSW.unshift(...capturedSW) + if (s.anchor && s.anchor.row === end.row && hi === end.row) { + s.anchor = { col: width - 1, row: s.anchor.row } + if (s.anchorSpan) { + s.anchorSpan = { + kind: s.anchorSpan.kind, + lo: { col: 0, row: s.anchorSpan.lo.row }, + hi: { col: width - 1, row: s.anchorSpan.hi.row }, + } + } + } + } +} + +/** + * Apply the selection overlay directly to the screen buffer by changing + * the style of every cell in the selection range. Called after the + * renderer produces the Frame but before the diff — the normal diffEach + * then picks up the restyled cells as ordinary changes, so LogUpdate + * stays a pure diff engine with no selection awareness. + * + * Uses a SOLID selection background (theme-provided via StylePool. + * setSelectionBg) that REPLACES each cell's bg while PRESERVING its fg — + * matches native terminal selection. Previously SGR-7 inverse (swapped + * fg/bg per cell), which fragmented badly over syntax-highlighted text: + * every distinct fg color became a different bg stripe. + * + * Uses StylePool caches so on drag the only work per cell is a Map + * lookup + packed-int write. + */ +export function applySelectionOverlay( + screen: Screen, + selection: SelectionState, + stylePool: StylePool, +): void { + const b = selectionBounds(selection) + if (!b) return + const { start, end } = b + const width = screen.width + const noSelect = screen.noSelect + for (let row = start.row; row <= end.row && row < screen.height; row++) { + const colStart = row === start.row ? start.col : 0 + const colEnd = row === end.row ? Math.min(end.col, width - 1) : width - 1 + const rowOff = row * width + for (let col = colStart; col <= colEnd; col++) { + const idx = rowOff + col + // Skip noSelect cells — gutters stay visually unchanged so it's + // clear they're not part of the copy. Surrounding selectable cells + // still highlight so the selection extent remains visible. + if (noSelect[idx] === 1) continue + const cell = cellAtIndex(screen, idx) + setCellStyleId(screen, col, row, stylePool.withSelectionBg(cell.styleId)) + } + } +} diff --git a/ink/squash-text-nodes.ts b/ink/squash-text-nodes.ts new file mode 100644 index 0000000..133a024 --- /dev/null +++ b/ink/squash-text-nodes.ts @@ -0,0 +1,92 @@ +import type { DOMElement } from './dom.js' +import type { TextStyles } from './styles.js' + +/** + * A segment of text with its associated styles. + * Used for structured rendering without ANSI string transforms. + */ +export type StyledSegment = { + text: string + styles: TextStyles + hyperlink?: string +} + +/** + * Squash text nodes into styled segments, propagating styles down through the tree. + * This allows structured styling without relying on ANSI string transforms. + */ +export function squashTextNodesToSegments( + node: DOMElement, + inheritedStyles: TextStyles = {}, + inheritedHyperlink?: string, + out: StyledSegment[] = [], +): StyledSegment[] { + const mergedStyles = node.textStyles + ? { ...inheritedStyles, ...node.textStyles } + : inheritedStyles + + for (const childNode of node.childNodes) { + if (childNode === undefined) { + continue + } + + if (childNode.nodeName === '#text') { + if (childNode.nodeValue.length > 0) { + out.push({ + text: childNode.nodeValue, + styles: mergedStyles, + hyperlink: inheritedHyperlink, + }) + } + } else if ( + childNode.nodeName === 'ink-text' || + childNode.nodeName === 'ink-virtual-text' + ) { + squashTextNodesToSegments( + childNode, + mergedStyles, + inheritedHyperlink, + out, + ) + } else if (childNode.nodeName === 'ink-link') { + const href = childNode.attributes['href'] as string | undefined + squashTextNodesToSegments( + childNode, + mergedStyles, + href || inheritedHyperlink, + out, + ) + } + } + + return out +} + +/** + * Squash text nodes into a plain string (without styles). + * Used for text measurement in layout calculations. + */ +function squashTextNodes(node: DOMElement): string { + let text = '' + + for (const childNode of node.childNodes) { + if (childNode === undefined) { + continue + } + + if (childNode.nodeName === '#text') { + text += childNode.nodeValue + } else if ( + childNode.nodeName === 'ink-text' || + childNode.nodeName === 'ink-virtual-text' + ) { + text += squashTextNodes(childNode) + } else if (childNode.nodeName === 'ink-link') { + text += squashTextNodes(childNode) + } + } + + return text +} + +export default squashTextNodes diff --git a/ink/stringWidth.ts b/ink/stringWidth.ts new file mode 100644 index 0000000..83f7bcb --- /dev/null +++ b/ink/stringWidth.ts @@ -0,0 +1,222 @@ +import emojiRegex from 'emoji-regex' +import { eastAsianWidth } from 'get-east-asian-width' +import stripAnsi from 'strip-ansi' +import { getGraphemeSegmenter } from '../utils/intl.js' + +const EMOJI_REGEX = emojiRegex() + +/** + * Fallback JavaScript implementation of stringWidth when Bun.stringWidth is not available. + * + * Get the display width of a string as it would appear in a terminal. + * + * This is a more accurate alternative to the string-width package that correctly handles + * characters like ⚠ (U+26A0) which string-width incorrectly reports as width 2. + * + * The implementation uses eastAsianWidth directly with ambiguousAsWide: false, + * which correctly treats ambiguous-width characters as narrow (width 1) as + * recommended by the Unicode standard for Western contexts. + */ +function stringWidthJavaScript(str: string): number { + if (typeof str !== 'string' || str.length === 0) { + return 0 + } + + // Fast path: pure ASCII string (no ANSI codes, no wide chars) + let isPureAscii = true + for (let i = 0; i < str.length; i++) { + const code = str.charCodeAt(i) + // Check for non-ASCII or ANSI escape (0x1b) + if (code >= 127 || code === 0x1b) { + isPureAscii = false + break + } + } + if (isPureAscii) { + // Count printable characters (exclude control chars) + let width = 0 + for (let i = 0; i < str.length; i++) { + const code = str.charCodeAt(i) + if (code > 0x1f) { + width++ + } + } + return width + } + + // Strip ANSI if escape character is present + if (str.includes('\x1b')) { + str = stripAnsi(str) + if (str.length === 0) { + return 0 + } + } + + // Fast path: simple Unicode (no emoji, variation selectors, or joiners) + if (!needsSegmentation(str)) { + let width = 0 + for (const char of str) { + const codePoint = char.codePointAt(0)! + if (!isZeroWidth(codePoint)) { + width += eastAsianWidth(codePoint, { ambiguousAsWide: false }) + } + } + return width + } + + let width = 0 + + for (const { segment: grapheme } of getGraphemeSegmenter().segment(str)) { + // Check for emoji first (most emoji sequences are width 2) + EMOJI_REGEX.lastIndex = 0 + if (EMOJI_REGEX.test(grapheme)) { + width += getEmojiWidth(grapheme) + continue + } + + // Calculate width for non-emoji graphemes + // For grapheme clusters (like Devanagari conjuncts with virama+ZWJ), only count + // the first non-zero-width character's width since the cluster renders as one glyph + for (const char of grapheme) { + const codePoint = char.codePointAt(0)! + if (!isZeroWidth(codePoint)) { + width += eastAsianWidth(codePoint, { ambiguousAsWide: false }) + break + } + } + } + + return width +} + +function needsSegmentation(str: string): boolean { + for (const char of str) { + const cp = char.codePointAt(0)! + // Emoji ranges + if (cp >= 0x1f300 && cp <= 0x1faff) return true + if (cp >= 0x2600 && cp <= 0x27bf) return true + if (cp >= 0x1f1e6 && cp <= 0x1f1ff) return true + // Variation selectors, ZWJ + if (cp >= 0xfe00 && cp <= 0xfe0f) return true + if (cp === 0x200d) return true + } + return false +} + +function getEmojiWidth(grapheme: string): number { + // Regional indicators: single = 1, pair = 2 + const first = grapheme.codePointAt(0)! + if (first >= 0x1f1e6 && first <= 0x1f1ff) { + let count = 0 + for (const _ of grapheme) count++ + return count === 1 ? 1 : 2 + } + + // Incomplete keycap: digit/symbol + VS16 without U+20E3 + if (grapheme.length === 2) { + const second = grapheme.codePointAt(1) + if ( + second === 0xfe0f && + ((first >= 0x30 && first <= 0x39) || first === 0x23 || first === 0x2a) + ) { + return 1 + } + } + + return 2 +} + +function isZeroWidth(codePoint: number): boolean { + // Fast path for common printable range + if (codePoint >= 0x20 && codePoint < 0x7f) return false + if (codePoint >= 0xa0 && codePoint < 0x0300) return codePoint === 0x00ad + + // Control characters + if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) return true + + // Zero-width and invisible characters + if ( + (codePoint >= 0x200b && codePoint <= 0x200d) || // ZW space/joiner + codePoint === 0xfeff || // BOM + (codePoint >= 0x2060 && codePoint <= 0x2064) // Word joiner etc. + ) { + return true + } + + // Variation selectors + if ( + (codePoint >= 0xfe00 && codePoint <= 0xfe0f) || + (codePoint >= 0xe0100 && codePoint <= 0xe01ef) + ) { + return true + } + + // Combining diacritical marks + if ( + (codePoint >= 0x0300 && codePoint <= 0x036f) || + (codePoint >= 0x1ab0 && codePoint <= 0x1aff) || + (codePoint >= 0x1dc0 && codePoint <= 0x1dff) || + (codePoint >= 0x20d0 && codePoint <= 0x20ff) || + (codePoint >= 0xfe20 && codePoint <= 0xfe2f) + ) { + return true + } + + // Indic script combining marks (covers Devanagari through Malayalam) + if (codePoint >= 0x0900 && codePoint <= 0x0d4f) { + // Signs and vowel marks at start of each script block + const offset = codePoint & 0x7f + if (offset <= 0x03) return true // Signs at block start + if (offset >= 0x3a && offset <= 0x4f) return true // Vowel signs, virama + if (offset >= 0x51 && offset <= 0x57) return true // Stress signs + if (offset >= 0x62 && offset <= 0x63) return true // Vowel signs + } + + // Thai/Lao combining marks + // Note: U+0E32 (SARA AA), U+0E33 (SARA AM), U+0EB2, U+0EB3 are spacing vowels (width 1), not combining marks + if ( + codePoint === 0x0e31 || // Thai MAI HAN-AKAT + (codePoint >= 0x0e34 && codePoint <= 0x0e3a) || // Thai vowel signs (skip U+0E32, U+0E33) + (codePoint >= 0x0e47 && codePoint <= 0x0e4e) || // Thai vowel signs and marks + codePoint === 0x0eb1 || // Lao MAI KAN + (codePoint >= 0x0eb4 && codePoint <= 0x0ebc) || // Lao vowel signs (skip U+0EB2, U+0EB3) + (codePoint >= 0x0ec8 && codePoint <= 0x0ecd) // Lao tone marks + ) { + return true + } + + // Arabic formatting + if ( + (codePoint >= 0x0600 && codePoint <= 0x0605) || + codePoint === 0x06dd || + codePoint === 0x070f || + codePoint === 0x08e2 + ) { + return true + } + + // Surrogates, tag characters + if (codePoint >= 0xd800 && codePoint <= 0xdfff) return true + if (codePoint >= 0xe0000 && codePoint <= 0xe007f) return true + + return false +} + +// Note: complex-script graphemes like Devanagari क्ष (ka+virama+ZWJ+ssa) render +// as a single ligature glyph but occupy 2 terminal cells (wcwidth sums the base +// consonants). Bun.stringWidth=2 matches terminal cell allocation, which is what +// we need for cursor positioning — the JS fallback's grapheme-cluster width of 1 +// would desync Ink's layout from the terminal. +// +// Bun.stringWidth is resolved once at module scope rather than checked on every +// call — typeof guards deopt property access and this is a hot path (~100k calls/frame). +const bunStringWidth = + typeof Bun !== 'undefined' && typeof Bun.stringWidth === 'function' + ? Bun.stringWidth + : null + +const BUN_STRING_WIDTH_OPTS = { ambiguousIsNarrow: true } as const + +export const stringWidth: (str: string) => number = bunStringWidth + ? str => bunStringWidth(str, BUN_STRING_WIDTH_OPTS) + : stringWidthJavaScript diff --git a/ink/styles.ts b/ink/styles.ts new file mode 100644 index 0000000..50986f4 --- /dev/null +++ b/ink/styles.ts @@ -0,0 +1,771 @@ +import { + LayoutAlign, + LayoutDisplay, + LayoutEdge, + LayoutFlexDirection, + LayoutGutter, + LayoutJustify, + type LayoutNode, + LayoutOverflow, + LayoutPositionType, + LayoutWrap, +} from './layout/node.js' +import type { BorderStyle, BorderTextOptions } from './render-border.js' + +export type RGBColor = `rgb(${number},${number},${number})` +export type HexColor = `#${string}` +export type Ansi256Color = `ansi256(${number})` +export type AnsiColor = + | 'ansi:black' + | 'ansi:red' + | 'ansi:green' + | 'ansi:yellow' + | 'ansi:blue' + | 'ansi:magenta' + | 'ansi:cyan' + | 'ansi:white' + | 'ansi:blackBright' + | 'ansi:redBright' + | 'ansi:greenBright' + | 'ansi:yellowBright' + | 'ansi:blueBright' + | 'ansi:magentaBright' + | 'ansi:cyanBright' + | 'ansi:whiteBright' + +/** Raw color value - not a theme key */ +export type Color = RGBColor | HexColor | Ansi256Color | AnsiColor + +/** + * Structured text styling properties. + * Used to style text without relying on ANSI string transforms. + * Colors are raw values - theme resolution happens at the component layer. + */ +export type TextStyles = { + readonly color?: Color + readonly backgroundColor?: Color + readonly dim?: boolean + readonly bold?: boolean + readonly italic?: boolean + readonly underline?: boolean + readonly strikethrough?: boolean + readonly inverse?: boolean +} + +export type Styles = { + readonly textWrap?: + | 'wrap' + | 'wrap-trim' + | 'end' + | 'middle' + | 'truncate-end' + | 'truncate' + | 'truncate-middle' + | 'truncate-start' + + readonly position?: 'absolute' | 'relative' + readonly top?: number | `${number}%` + readonly bottom?: number | `${number}%` + readonly left?: number | `${number}%` + readonly right?: number | `${number}%` + + /** + * Size of the gap between an element's columns. + */ + readonly columnGap?: number + + /** + * Size of the gap between element's rows. + */ + readonly rowGap?: number + + /** + * Size of the gap between an element's columns and rows. Shorthand for `columnGap` and `rowGap`. + */ + readonly gap?: number + + /** + * Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`. + */ + readonly margin?: number + + /** + * Horizontal margin. Equivalent to setting `marginLeft` and `marginRight`. + */ + readonly marginX?: number + + /** + * Vertical margin. Equivalent to setting `marginTop` and `marginBottom`. + */ + readonly marginY?: number + + /** + * Top margin. + */ + readonly marginTop?: number + + /** + * Bottom margin. + */ + readonly marginBottom?: number + + /** + * Left margin. + */ + readonly marginLeft?: number + + /** + * Right margin. + */ + readonly marginRight?: number + + /** + * Padding on all sides. Equivalent to setting `paddingTop`, `paddingBottom`, `paddingLeft` and `paddingRight`. + */ + readonly padding?: number + + /** + * Horizontal padding. Equivalent to setting `paddingLeft` and `paddingRight`. + */ + readonly paddingX?: number + + /** + * Vertical padding. Equivalent to setting `paddingTop` and `paddingBottom`. + */ + readonly paddingY?: number + + /** + * Top padding. + */ + readonly paddingTop?: number + + /** + * Bottom padding. + */ + readonly paddingBottom?: number + + /** + * Left padding. + */ + readonly paddingLeft?: number + + /** + * Right padding. + */ + readonly paddingRight?: number + + /** + * This property defines the ability for a flex item to grow if necessary. + * See [flex-grow](https://css-tricks.com/almanac/properties/f/flex-grow/). + */ + readonly flexGrow?: number + + /** + * It specifies the “flex shrink factor”, which determines how much the flex item will shrink relative to the rest of the flex items in the flex container when there isn’t enough space on the row. + * See [flex-shrink](https://css-tricks.com/almanac/properties/f/flex-shrink/). + */ + readonly flexShrink?: number + + /** + * It establishes the main-axis, thus defining the direction flex items are placed in the flex container. + * See [flex-direction](https://css-tricks.com/almanac/properties/f/flex-direction/). + */ + readonly flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse' + + /** + * It specifies the initial size of the flex item, before any available space is distributed according to the flex factors. + * See [flex-basis](https://css-tricks.com/almanac/properties/f/flex-basis/). + */ + readonly flexBasis?: number | string + + /** + * It defines whether the flex items are forced in a single line or can be flowed into multiple lines. If set to multiple lines, it also defines the cross-axis which determines the direction new lines are stacked in. + * See [flex-wrap](https://css-tricks.com/almanac/properties/f/flex-wrap/). + */ + readonly flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse' + + /** + * The align-items property defines the default behavior for how items are laid out along the cross axis (perpendicular to the main axis). + * See [align-items](https://css-tricks.com/almanac/properties/a/align-items/). + */ + readonly alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch' + + /** + * It makes possible to override the align-items value for specific flex items. + * See [align-self](https://css-tricks.com/almanac/properties/a/align-self/). + */ + readonly alignSelf?: 'flex-start' | 'center' | 'flex-end' | 'auto' + + /** + * It defines the alignment along the main axis. + * See [justify-content](https://css-tricks.com/almanac/properties/j/justify-content/). + */ + readonly justifyContent?: + | 'flex-start' + | 'flex-end' + | 'space-between' + | 'space-around' + | 'space-evenly' + | 'center' + + /** + * Width of the element in spaces. + * You can also set it in percent, which will calculate the width based on the width of parent element. + */ + readonly width?: number | string + + /** + * Height of the element in lines (rows). + * You can also set it in percent, which will calculate the height based on the height of parent element. + */ + readonly height?: number | string + + /** + * Sets a minimum width of the element. + */ + readonly minWidth?: number | string + + /** + * Sets a minimum height of the element. + */ + readonly minHeight?: number | string + + /** + * Sets a maximum width of the element. + */ + readonly maxWidth?: number | string + + /** + * Sets a maximum height of the element. + */ + readonly maxHeight?: number | string + + /** + * Set this property to `none` to hide the element. + */ + readonly display?: 'flex' | 'none' + + /** + * Add a border with a specified style. + * If `borderStyle` is `undefined` (which it is by default), no border will be added. + */ + readonly borderStyle?: BorderStyle + + /** + * Determines whether top border is visible. + * + * @default true + */ + readonly borderTop?: boolean + + /** + * Determines whether bottom border is visible. + * + * @default true + */ + readonly borderBottom?: boolean + + /** + * Determines whether left border is visible. + * + * @default true + */ + readonly borderLeft?: boolean + + /** + * Determines whether right border is visible. + * + * @default true + */ + readonly borderRight?: boolean + + /** + * Change border color. + * Shorthand for setting `borderTopColor`, `borderRightColor`, `borderBottomColor` and `borderLeftColor`. + */ + readonly borderColor?: Color + + /** + * Change top border color. + * Accepts raw color values (rgb, hex, ansi). + */ + readonly borderTopColor?: Color + + /** + * Change bottom border color. + * Accepts raw color values (rgb, hex, ansi). + */ + readonly borderBottomColor?: Color + + /** + * Change left border color. + * Accepts raw color values (rgb, hex, ansi). + */ + readonly borderLeftColor?: Color + + /** + * Change right border color. + * Accepts raw color values (rgb, hex, ansi). + */ + readonly borderRightColor?: Color + + /** + * Dim the border color. + * Shorthand for setting `borderTopDimColor`, `borderBottomDimColor`, `borderLeftDimColor` and `borderRightDimColor`. + * + * @default false + */ + readonly borderDimColor?: boolean + + /** + * Dim the top border color. + * + * @default false + */ + readonly borderTopDimColor?: boolean + + /** + * Dim the bottom border color. + * + * @default false + */ + readonly borderBottomDimColor?: boolean + + /** + * Dim the left border color. + * + * @default false + */ + readonly borderLeftDimColor?: boolean + + /** + * Dim the right border color. + * + * @default false + */ + readonly borderRightDimColor?: boolean + + /** + * Add text within the border. Only applies to top or bottom borders. + */ + readonly borderText?: BorderTextOptions + + /** + * Background color for the box. Fills the interior with background-colored + * spaces and is inherited by child text nodes as their default background. + */ + readonly backgroundColor?: Color + + /** + * Fill the box's interior (padding included) with spaces before + * rendering children, so nothing behind it shows through. Like + * `backgroundColor` but without emitting any SGR — the terminal's + * default background is used. Useful for absolute-positioned overlays + * where Box padding/gaps would otherwise be transparent. + */ + readonly opaque?: boolean + + /** + * Behavior for an element's overflow in both directions. + * 'scroll' constrains the container's size (children do not expand it) + * and enables scrollTop-based virtualized scrolling at render time. + * + * @default 'visible' + */ + readonly overflow?: 'visible' | 'hidden' | 'scroll' + + /** + * Behavior for an element's overflow in horizontal direction. + * + * @default 'visible' + */ + readonly overflowX?: 'visible' | 'hidden' | 'scroll' + + /** + * Behavior for an element's overflow in vertical direction. + * + * @default 'visible' + */ + readonly overflowY?: 'visible' | 'hidden' | 'scroll' + + /** + * Exclude this box's cells from text selection in fullscreen mode. + * Cells inside this region are skipped by both the selection highlight + * and the copied text — useful for fencing off gutters (line numbers, + * diff sigils) so click-drag over a diff yields clean copyable code. + * Only affects alt-screen text selection; no-op otherwise. + * + * `'from-left-edge'` extends the exclusion from column 0 to the box's + * right edge for every row it occupies — this covers any upstream + * indentation (tool message prefix, tree lines) so a multi-row drag + * doesn't pick up leading whitespace from middle rows. + */ + readonly noSelect?: boolean | 'from-left-edge' +} + +const applyPositionStyles = (node: LayoutNode, style: Styles): void => { + if ('position' in style) { + node.setPositionType( + style.position === 'absolute' + ? LayoutPositionType.Absolute + : LayoutPositionType.Relative, + ) + } + if ('top' in style) applyPositionEdge(node, 'top', style.top) + if ('bottom' in style) applyPositionEdge(node, 'bottom', style.bottom) + if ('left' in style) applyPositionEdge(node, 'left', style.left) + if ('right' in style) applyPositionEdge(node, 'right', style.right) +} + +function applyPositionEdge( + node: LayoutNode, + edge: 'top' | 'bottom' | 'left' | 'right', + v: number | `${number}%` | undefined, +): void { + if (typeof v === 'string') { + node.setPositionPercent(edge, Number.parseInt(v, 10)) + } else if (typeof v === 'number') { + node.setPosition(edge, v) + } else { + node.setPosition(edge, Number.NaN) + } +} + +const applyOverflowStyles = (node: LayoutNode, style: Styles): void => { + // Yoga's Overflow controls whether children expand the container. + // 'hidden' and 'scroll' both prevent expansion; 'scroll' additionally + // signals that the renderer should apply scrollTop translation. + // overflowX/Y are render-time concerns; for layout we use the union. + const y = style.overflowY ?? style.overflow + const x = style.overflowX ?? style.overflow + if (y === 'scroll' || x === 'scroll') { + node.setOverflow(LayoutOverflow.Scroll) + } else if (y === 'hidden' || x === 'hidden') { + node.setOverflow(LayoutOverflow.Hidden) + } else if ( + 'overflow' in style || + 'overflowX' in style || + 'overflowY' in style + ) { + node.setOverflow(LayoutOverflow.Visible) + } +} + +const applyMarginStyles = (node: LayoutNode, style: Styles): void => { + if ('margin' in style) { + node.setMargin(LayoutEdge.All, style.margin ?? 0) + } + + if ('marginX' in style) { + node.setMargin(LayoutEdge.Horizontal, style.marginX ?? 0) + } + + if ('marginY' in style) { + node.setMargin(LayoutEdge.Vertical, style.marginY ?? 0) + } + + if ('marginLeft' in style) { + node.setMargin(LayoutEdge.Start, style.marginLeft || 0) + } + + if ('marginRight' in style) { + node.setMargin(LayoutEdge.End, style.marginRight || 0) + } + + if ('marginTop' in style) { + node.setMargin(LayoutEdge.Top, style.marginTop || 0) + } + + if ('marginBottom' in style) { + node.setMargin(LayoutEdge.Bottom, style.marginBottom || 0) + } +} + +const applyPaddingStyles = (node: LayoutNode, style: Styles): void => { + if ('padding' in style) { + node.setPadding(LayoutEdge.All, style.padding ?? 0) + } + + if ('paddingX' in style) { + node.setPadding(LayoutEdge.Horizontal, style.paddingX ?? 0) + } + + if ('paddingY' in style) { + node.setPadding(LayoutEdge.Vertical, style.paddingY ?? 0) + } + + if ('paddingLeft' in style) { + node.setPadding(LayoutEdge.Left, style.paddingLeft || 0) + } + + if ('paddingRight' in style) { + node.setPadding(LayoutEdge.Right, style.paddingRight || 0) + } + + if ('paddingTop' in style) { + node.setPadding(LayoutEdge.Top, style.paddingTop || 0) + } + + if ('paddingBottom' in style) { + node.setPadding(LayoutEdge.Bottom, style.paddingBottom || 0) + } +} + +const applyFlexStyles = (node: LayoutNode, style: Styles): void => { + if ('flexGrow' in style) { + node.setFlexGrow(style.flexGrow ?? 0) + } + + if ('flexShrink' in style) { + node.setFlexShrink( + typeof style.flexShrink === 'number' ? style.flexShrink : 1, + ) + } + + if ('flexWrap' in style) { + if (style.flexWrap === 'nowrap') { + node.setFlexWrap(LayoutWrap.NoWrap) + } + + if (style.flexWrap === 'wrap') { + node.setFlexWrap(LayoutWrap.Wrap) + } + + if (style.flexWrap === 'wrap-reverse') { + node.setFlexWrap(LayoutWrap.WrapReverse) + } + } + + if ('flexDirection' in style) { + if (style.flexDirection === 'row') { + node.setFlexDirection(LayoutFlexDirection.Row) + } + + if (style.flexDirection === 'row-reverse') { + node.setFlexDirection(LayoutFlexDirection.RowReverse) + } + + if (style.flexDirection === 'column') { + node.setFlexDirection(LayoutFlexDirection.Column) + } + + if (style.flexDirection === 'column-reverse') { + node.setFlexDirection(LayoutFlexDirection.ColumnReverse) + } + } + + if ('flexBasis' in style) { + if (typeof style.flexBasis === 'number') { + node.setFlexBasis(style.flexBasis) + } else if (typeof style.flexBasis === 'string') { + node.setFlexBasisPercent(Number.parseInt(style.flexBasis, 10)) + } else { + node.setFlexBasis(Number.NaN) + } + } + + if ('alignItems' in style) { + if (style.alignItems === 'stretch' || !style.alignItems) { + node.setAlignItems(LayoutAlign.Stretch) + } + + if (style.alignItems === 'flex-start') { + node.setAlignItems(LayoutAlign.FlexStart) + } + + if (style.alignItems === 'center') { + node.setAlignItems(LayoutAlign.Center) + } + + if (style.alignItems === 'flex-end') { + node.setAlignItems(LayoutAlign.FlexEnd) + } + } + + if ('alignSelf' in style) { + if (style.alignSelf === 'auto' || !style.alignSelf) { + node.setAlignSelf(LayoutAlign.Auto) + } + + if (style.alignSelf === 'flex-start') { + node.setAlignSelf(LayoutAlign.FlexStart) + } + + if (style.alignSelf === 'center') { + node.setAlignSelf(LayoutAlign.Center) + } + + if (style.alignSelf === 'flex-end') { + node.setAlignSelf(LayoutAlign.FlexEnd) + } + } + + if ('justifyContent' in style) { + if (style.justifyContent === 'flex-start' || !style.justifyContent) { + node.setJustifyContent(LayoutJustify.FlexStart) + } + + if (style.justifyContent === 'center') { + node.setJustifyContent(LayoutJustify.Center) + } + + if (style.justifyContent === 'flex-end') { + node.setJustifyContent(LayoutJustify.FlexEnd) + } + + if (style.justifyContent === 'space-between') { + node.setJustifyContent(LayoutJustify.SpaceBetween) + } + + if (style.justifyContent === 'space-around') { + node.setJustifyContent(LayoutJustify.SpaceAround) + } + + if (style.justifyContent === 'space-evenly') { + node.setJustifyContent(LayoutJustify.SpaceEvenly) + } + } +} + +const applyDimensionStyles = (node: LayoutNode, style: Styles): void => { + if ('width' in style) { + if (typeof style.width === 'number') { + node.setWidth(style.width) + } else if (typeof style.width === 'string') { + node.setWidthPercent(Number.parseInt(style.width, 10)) + } else { + node.setWidthAuto() + } + } + + if ('height' in style) { + if (typeof style.height === 'number') { + node.setHeight(style.height) + } else if (typeof style.height === 'string') { + node.setHeightPercent(Number.parseInt(style.height, 10)) + } else { + node.setHeightAuto() + } + } + + if ('minWidth' in style) { + if (typeof style.minWidth === 'string') { + node.setMinWidthPercent(Number.parseInt(style.minWidth, 10)) + } else { + node.setMinWidth(style.minWidth ?? 0) + } + } + + if ('minHeight' in style) { + if (typeof style.minHeight === 'string') { + node.setMinHeightPercent(Number.parseInt(style.minHeight, 10)) + } else { + node.setMinHeight(style.minHeight ?? 0) + } + } + + if ('maxWidth' in style) { + if (typeof style.maxWidth === 'string') { + node.setMaxWidthPercent(Number.parseInt(style.maxWidth, 10)) + } else { + node.setMaxWidth(style.maxWidth ?? 0) + } + } + + if ('maxHeight' in style) { + if (typeof style.maxHeight === 'string') { + node.setMaxHeightPercent(Number.parseInt(style.maxHeight, 10)) + } else { + node.setMaxHeight(style.maxHeight ?? 0) + } + } +} + +const applyDisplayStyles = (node: LayoutNode, style: Styles): void => { + if ('display' in style) { + node.setDisplay( + style.display === 'flex' ? LayoutDisplay.Flex : LayoutDisplay.None, + ) + } +} + +const applyBorderStyles = ( + node: LayoutNode, + style: Styles, + resolvedStyle?: Styles, +): void => { + // resolvedStyle is the full current style (already set on the DOM node). + // style may be a diff with only changed properties. For border side props, + // we need the resolved value because `borderStyle` in a diff may not include + // unchanged border side values (e.g. borderTop stays false but isn't in the diff). + const resolved = resolvedStyle ?? style + + if ('borderStyle' in style) { + const borderWidth = style.borderStyle ? 1 : 0 + + node.setBorder( + LayoutEdge.Top, + resolved.borderTop !== false ? borderWidth : 0, + ) + node.setBorder( + LayoutEdge.Bottom, + resolved.borderBottom !== false ? borderWidth : 0, + ) + node.setBorder( + LayoutEdge.Left, + resolved.borderLeft !== false ? borderWidth : 0, + ) + node.setBorder( + LayoutEdge.Right, + resolved.borderRight !== false ? borderWidth : 0, + ) + } else { + // Handle individual border property changes (when only borderX changes without borderStyle). + // Skip undefined values — they mean the prop was removed or never set, + // not that a border should be enabled. + if ('borderTop' in style && style.borderTop !== undefined) { + node.setBorder(LayoutEdge.Top, style.borderTop === false ? 0 : 1) + } + if ('borderBottom' in style && style.borderBottom !== undefined) { + node.setBorder(LayoutEdge.Bottom, style.borderBottom === false ? 0 : 1) + } + if ('borderLeft' in style && style.borderLeft !== undefined) { + node.setBorder(LayoutEdge.Left, style.borderLeft === false ? 0 : 1) + } + if ('borderRight' in style && style.borderRight !== undefined) { + node.setBorder(LayoutEdge.Right, style.borderRight === false ? 0 : 1) + } + } +} + +const applyGapStyles = (node: LayoutNode, style: Styles): void => { + if ('gap' in style) { + node.setGap(LayoutGutter.All, style.gap ?? 0) + } + + if ('columnGap' in style) { + node.setGap(LayoutGutter.Column, style.columnGap ?? 0) + } + + if ('rowGap' in style) { + node.setGap(LayoutGutter.Row, style.rowGap ?? 0) + } +} + +const styles = ( + node: LayoutNode, + style: Styles = {}, + resolvedStyle?: Styles, +): void => { + applyPositionStyles(node, style) + applyOverflowStyles(node, style) + applyMarginStyles(node, style) + applyPaddingStyles(node, style) + applyFlexStyles(node, style) + applyDimensionStyles(node, style) + applyDisplayStyles(node, style) + applyBorderStyles(node, style, resolvedStyle) + applyGapStyles(node, style) +} + +export default styles diff --git a/ink/supports-hyperlinks.ts b/ink/supports-hyperlinks.ts new file mode 100644 index 0000000..0af3745 --- /dev/null +++ b/ink/supports-hyperlinks.ts @@ -0,0 +1,57 @@ +import supportsHyperlinksLib from 'supports-hyperlinks' + +// Additional terminals that support OSC 8 hyperlinks but aren't detected by supports-hyperlinks. +// Checked against both TERM_PROGRAM and LC_TERMINAL (the latter is preserved inside tmux). +export const ADDITIONAL_HYPERLINK_TERMINALS = [ + 'ghostty', + 'Hyper', + 'kitty', + 'alacritty', + 'iTerm.app', + 'iTerm2', +] + +type EnvLike = Record + +type SupportsHyperlinksOptions = { + env?: EnvLike + stdoutSupported?: boolean +} + +/** + * Returns whether stdout supports OSC 8 hyperlinks. + * Extends the supports-hyperlinks library with additional terminal detection. + * @param options Optional overrides for testing (env, stdoutSupported) + */ +export function supportsHyperlinks( + options?: SupportsHyperlinksOptions, +): boolean { + const stdoutSupported = + options?.stdoutSupported ?? supportsHyperlinksLib.stdout + if (stdoutSupported) { + return true + } + + const env = options?.env ?? process.env + + // Check for additional terminals not detected by supports-hyperlinks + const termProgram = env['TERM_PROGRAM'] + if (termProgram && ADDITIONAL_HYPERLINK_TERMINALS.includes(termProgram)) { + return true + } + + // LC_TERMINAL is set by some terminals (e.g. iTerm2) and preserved inside tmux, + // where TERM_PROGRAM is overwritten to 'tmux'. + const lcTerminal = env['LC_TERMINAL'] + if (lcTerminal && ADDITIONAL_HYPERLINK_TERMINALS.includes(lcTerminal)) { + return true + } + + // Kitty sets TERM=xterm-kitty + const term = env['TERM'] + if (term?.includes('kitty')) { + return true + } + + return false +} diff --git a/ink/tabstops.ts b/ink/tabstops.ts new file mode 100644 index 0000000..fc519fb --- /dev/null +++ b/ink/tabstops.ts @@ -0,0 +1,46 @@ +// Tab expansion, inspired by Ghostty's Tabstops.zig +// Uses 8-column intervals (POSIX default, hardcoded in terminals like Ghostty) + +import { stringWidth } from './stringWidth.js' +import { createTokenizer } from './termio/tokenize.js' + +const DEFAULT_TAB_INTERVAL = 8 + +export function expandTabs( + text: string, + interval = DEFAULT_TAB_INTERVAL, +): string { + if (!text.includes('\t')) { + return text + } + + const tokenizer = createTokenizer() + const tokens = tokenizer.feed(text) + tokens.push(...tokenizer.flush()) + + let result = '' + let column = 0 + + for (const token of tokens) { + if (token.type === 'sequence') { + result += token.value + } else { + const parts = token.value.split(/(\t|\n)/) + for (const part of parts) { + if (part === '\t') { + const spaces = interval - (column % interval) + result += ' '.repeat(spaces) + column += spaces + } else if (part === '\n') { + result += part + column = 0 + } else { + result += part + column += stringWidth(part) + } + } + } + } + + return result +} diff --git a/ink/terminal-focus-state.ts b/ink/terminal-focus-state.ts new file mode 100644 index 0000000..dfc3df1 --- /dev/null +++ b/ink/terminal-focus-state.ts @@ -0,0 +1,47 @@ +// Terminal focus state signal — non-React access to DECSET 1004 focus events. +// 'unknown' is the default for terminals that don't support focus reporting; +// consumers treat 'unknown' identically to 'focused' (no throttling). +// Subscribers are notified synchronously when focus changes, used by +// TerminalFocusProvider to avoid polling. +export type TerminalFocusState = 'focused' | 'blurred' | 'unknown' + +let focusState: TerminalFocusState = 'unknown' +const resolvers: Set<() => void> = new Set() +const subscribers: Set<() => void> = new Set() + +export function setTerminalFocused(v: boolean): void { + focusState = v ? 'focused' : 'blurred' + // Notify useSyncExternalStore subscribers + for (const cb of subscribers) { + cb() + } + if (!v) { + for (const resolve of resolvers) { + resolve() + } + resolvers.clear() + } +} + +export function getTerminalFocused(): boolean { + return focusState !== 'blurred' +} + +export function getTerminalFocusState(): TerminalFocusState { + return focusState +} + +// For useSyncExternalStore +export function subscribeTerminalFocus(cb: () => void): () => void { + subscribers.add(cb) + return () => { + subscribers.delete(cb) + } +} + +export function resetTerminalFocusState(): void { + focusState = 'unknown' + for (const cb of subscribers) { + cb() + } +} diff --git a/ink/terminal-querier.ts b/ink/terminal-querier.ts new file mode 100644 index 0000000..e190f1f --- /dev/null +++ b/ink/terminal-querier.ts @@ -0,0 +1,212 @@ +/** + * Query the terminal and await responses without timeouts. + * + * Terminal queries (DECRQM, DA1, OSC 11, etc.) share the stdin stream + * with keyboard input. Response sequences are syntactically + * distinguishable from key events, so the input parser recognizes them + * and dispatches them here. + * + * To avoid timeouts, each query batch is terminated by a DA1 sentinel + * (CSI c) — every terminal since VT100 responds to DA1, and terminals + * answer queries in order. So: if your query's response arrives before + * DA1's, the terminal supports it; if DA1 arrives first, it doesn't. + * + * Usage: + * const [sync, grapheme] = await Promise.all([ + * querier.send(decrqm(2026)), + * querier.send(decrqm(2027)), + * querier.flush(), + * ]) + * // sync and grapheme are DECRPM responses or undefined if unsupported + */ + +import type { TerminalResponse } from './parse-keypress.js' +import { csi } from './termio/csi.js' +import { osc } from './termio/osc.js' + +/** A terminal query: an outbound request sequence paired with a matcher + * that recognizes the expected inbound response. Built by `decrqm()`, + * `oscColor()`, `kittyKeyboard()`, etc. */ +export type TerminalQuery = { + /** Escape sequence to write to stdout */ + request: string + /** Recognizes the expected response in the inbound stream */ + match: (r: TerminalResponse) => r is T +} + +type DecrpmResponse = Extract +type Da1Response = Extract +type Da2Response = Extract +type KittyResponse = Extract +type CursorPosResponse = Extract +type OscResponse = Extract +type XtversionResponse = Extract + +// -- Query builders -- + +/** DECRQM: request DEC private mode status (CSI ? mode $ p). + * Terminal replies with DECRPM (CSI ? mode ; status $ y) or ignores. */ +export function decrqm(mode: number): TerminalQuery { + return { + request: csi(`?${mode}$p`), + match: (r): r is DecrpmResponse => r.type === 'decrpm' && r.mode === mode, + } +} + +/** Primary Device Attributes query (CSI c). Every terminal answers this — + * used internally by flush() as a universal sentinel. Call directly if + * you want the DA1 params. */ +export function da1(): TerminalQuery { + return { + request: csi('c'), + match: (r): r is Da1Response => r.type === 'da1', + } +} + +/** Secondary Device Attributes query (CSI > c). Returns terminal version. */ +export function da2(): TerminalQuery { + return { + request: csi('>c'), + match: (r): r is Da2Response => r.type === 'da2', + } +} + +/** Query current Kitty keyboard protocol flags (CSI ? u). + * Terminal replies with CSI ? flags u or ignores. */ +export function kittyKeyboard(): TerminalQuery { + return { + request: csi('?u'), + match: (r): r is KittyResponse => r.type === 'kittyKeyboard', + } +} + +/** DECXCPR: request cursor position with DEC-private marker (CSI ? 6 n). + * Terminal replies with CSI ? row ; col R. The `?` marker is critical — + * the plain DSR form (CSI 6 n → CSI row;col R) is ambiguous with + * modified F3 keys (Shift+F3 = CSI 1;2 R, etc.). */ +export function cursorPosition(): TerminalQuery { + return { + request: csi('?6n'), + match: (r): r is CursorPosResponse => r.type === 'cursorPosition', + } +} + +/** OSC dynamic color query (e.g. OSC 11 for bg color, OSC 10 for fg). + * The `?` data slot asks the terminal to reply with the current value. */ +export function oscColor(code: number): TerminalQuery { + return { + request: osc(code, '?'), + match: (r): r is OscResponse => r.type === 'osc' && r.code === code, + } +} + +/** XTVERSION: request terminal name/version (CSI > 0 q). + * Terminal replies with DCS > | name ST (e.g. "xterm.js(5.5.0)") or ignores. + * This survives SSH — the query goes through the pty, not the environment, + * so it identifies the *client* terminal even when TERM_PROGRAM isn't + * forwarded. Used to detect xterm.js for wheel-scroll compensation. */ +export function xtversion(): TerminalQuery { + return { + request: csi('>0q'), + match: (r): r is XtversionResponse => r.type === 'xtversion', + } +} + +// -- Querier -- + +/** Sentinel request sequence (DA1). Kept internal; flush() writes it. */ +const SENTINEL = csi('c') + +type Pending = + | { + kind: 'query' + match: (r: TerminalResponse) => boolean + resolve: (r: TerminalResponse | undefined) => void + } + | { kind: 'sentinel'; resolve: () => void } + +export class TerminalQuerier { + /** + * Interleaved queue of queries and sentinels in send order. Terminals + * respond in order, so each flush() barrier only drains queries queued + * before it — concurrent batches from independent callers stay isolated. + */ + private queue: Pending[] = [] + + constructor(private stdout: NodeJS.WriteStream) {} + + /** + * Send a query and wait for its response. + * + * Resolves with the response when `query.match` matches an incoming + * TerminalResponse, or with `undefined` when a flush() sentinel arrives + * before any matching response (meaning the terminal ignored the query). + * + * Never rejects; never times out on its own. If you never call flush() + * and the terminal doesn't respond, the promise remains pending. + */ + send( + query: TerminalQuery, + ): Promise { + return new Promise(resolve => { + this.queue.push({ + kind: 'query', + match: query.match, + resolve: r => resolve(r as T | undefined), + }) + this.stdout.write(query.request) + }) + } + + /** + * Send the DA1 sentinel. Resolves when DA1's response arrives. + * + * As a side effect, all queries still pending when DA1 arrives are + * resolved with `undefined` (terminal didn't respond → doesn't support + * the query). This is the barrier that makes send() timeout-free. + * + * Safe to call with no pending queries — still waits for a round-trip. + */ + flush(): Promise { + return new Promise(resolve => { + this.queue.push({ kind: 'sentinel', resolve }) + this.stdout.write(SENTINEL) + }) + } + + /** + * Dispatch a response parsed from stdin. Called by App.tsx's + * processKeysInBatch for every `kind: 'response'` item. + * + * Matching strategy: + * - First, try to match a pending query (FIFO, first match wins). + * This lets callers send(da1()) explicitly if they want the DA1 + * params — a separate DA1 write means the terminal sends TWO DA1 + * responses. The first matches the explicit query; the second + * (unmatched) fires the sentinel. + * - Otherwise, if this is a DA1, fire the FIRST pending sentinel: + * resolve any queries queued before that sentinel with undefined + * (the terminal answered DA1 without answering them → unsupported) + * and signal its flush() completion. Only draining up to the first + * sentinel keeps later batches intact when multiple callers have + * concurrent queries in flight. + * - Unsolicited responses (no match, no sentinel) are silently dropped. + */ + onResponse(r: TerminalResponse): void { + const idx = this.queue.findIndex(p => p.kind === 'query' && p.match(r)) + if (idx !== -1) { + const [q] = this.queue.splice(idx, 1) + if (q?.kind === 'query') q.resolve(r) + return + } + + if (r.type === 'da1') { + const s = this.queue.findIndex(p => p.kind === 'sentinel') + if (s === -1) return + for (const p of this.queue.splice(0, s + 1)) { + if (p.kind === 'query') p.resolve(undefined) + else p.resolve() + } + } + } +} diff --git a/ink/terminal.ts b/ink/terminal.ts new file mode 100644 index 0000000..2aad947 --- /dev/null +++ b/ink/terminal.ts @@ -0,0 +1,248 @@ +import { coerce } from 'semver' +import type { Writable } from 'stream' +import { env } from '../utils/env.js' +import { gte } from '../utils/semver.js' +import { getClearTerminalSequence } from './clearTerminal.js' +import type { Diff } from './frame.js' +import { cursorMove, cursorTo, eraseLines } from './termio/csi.js' +import { BSU, ESU, HIDE_CURSOR, SHOW_CURSOR } from './termio/dec.js' +import { link } from './termio/osc.js' + +export type Progress = { + state: 'running' | 'completed' | 'error' | 'indeterminate' + percentage?: number +} + +/** + * Checks if the terminal supports OSC 9;4 progress reporting. + * Supported terminals: + * - ConEmu (Windows) - all versions + * - Ghostty 1.2.0+ + * - iTerm2 3.6.6+ + * + * Note: Windows Terminal interprets OSC 9;4 as notifications, not progress. + */ +export function isProgressReportingAvailable(): boolean { + // Only available if we have a TTY (not piped) + if (!process.stdout.isTTY) { + return false + } + + // Explicitly exclude Windows Terminal, which interprets OSC 9;4 as + // notifications rather than progress indicators + if (process.env.WT_SESSION) { + return false + } + + // ConEmu supports OSC 9;4 for progress (all versions) + if ( + process.env.ConEmuANSI || + process.env.ConEmuPID || + process.env.ConEmuTask + ) { + return true + } + + const version = coerce(process.env.TERM_PROGRAM_VERSION) + if (!version) { + return false + } + + // Ghostty 1.2.0+ supports OSC 9;4 for progress + // https://ghostty.org/docs/install/release-notes/1-2-0 + if (process.env.TERM_PROGRAM === 'ghostty') { + return gte(version.version, '1.2.0') + } + + // iTerm2 3.6.6+ supports OSC 9;4 for progress + // https://iterm2.com/downloads.html + if (process.env.TERM_PROGRAM === 'iTerm.app') { + return gte(version.version, '3.6.6') + } + + return false +} + +/** + * Checks if the terminal supports DEC mode 2026 (synchronized output). + * When supported, BSU/ESU sequences prevent visible flicker during redraws. + */ +export function isSynchronizedOutputSupported(): boolean { + // tmux parses and proxies every byte but doesn't implement DEC 2026. + // BSU/ESU pass through to the outer terminal but tmux has already + // broken atomicity by chunking. Skip to save 16 bytes/frame + parser work. + if (process.env.TMUX) return false + + const termProgram = process.env.TERM_PROGRAM + const term = process.env.TERM + + // Modern terminals with known DEC 2026 support + if ( + termProgram === 'iTerm.app' || + termProgram === 'WezTerm' || + termProgram === 'WarpTerminal' || + termProgram === 'ghostty' || + termProgram === 'contour' || + termProgram === 'vscode' || + termProgram === 'alacritty' + ) { + return true + } + + // kitty sets TERM=xterm-kitty or KITTY_WINDOW_ID + if (term?.includes('kitty') || process.env.KITTY_WINDOW_ID) return true + + // Ghostty may set TERM=xterm-ghostty without TERM_PROGRAM + if (term === 'xterm-ghostty') return true + + // foot sets TERM=foot or TERM=foot-extra + if (term?.startsWith('foot')) return true + + // Alacritty may set TERM containing 'alacritty' + if (term?.includes('alacritty')) return true + + // Zed uses the alacritty_terminal crate which supports DEC 2026 + if (process.env.ZED_TERM) return true + + // Windows Terminal + if (process.env.WT_SESSION) return true + + // VTE-based terminals (GNOME Terminal, Tilix, etc.) since VTE 0.68 + const vteVersion = process.env.VTE_VERSION + if (vteVersion) { + const version = parseInt(vteVersion, 10) + if (version >= 6800) return true + } + + return false +} + +// -- XTVERSION-detected terminal name (populated async at startup) -- +// +// TERM_PROGRAM is not forwarded over SSH by default, so env-based detection +// fails when claude runs remotely inside a VS Code integrated terminal. +// XTVERSION (CSI > 0 q → DCS > | name ST) goes through the pty — the query +// reaches the *client* terminal and the reply comes back through stdin. +// App.tsx fires the query when raw mode enables; setXtversionName() is called +// from the response handler. Readers should treat undefined as "not yet known" +// and fall back to env-var detection. + +let xtversionName: string | undefined + +/** Record the XTVERSION response. Called once from App.tsx when the reply + * arrives on stdin. No-op if already set (defend against re-probe). */ +export function setXtversionName(name: string): void { + if (xtversionName === undefined) xtversionName = name +} + +/** True if running in an xterm.js-based terminal (VS Code, Cursor, Windsurf + * integrated terminals). Combines TERM_PROGRAM env check (fast, sync, but + * not forwarded over SSH) with the XTVERSION probe result (async, survives + * SSH — query/reply goes through the pty). Early calls may miss the probe + * reply — call lazily (e.g. in an event handler) if SSH detection matters. */ +export function isXtermJs(): boolean { + if (process.env.TERM_PROGRAM === 'vscode') return true + return xtversionName?.startsWith('xterm.js') ?? false +} + +// Terminals known to correctly implement the Kitty keyboard protocol +// (CSI >1u) and/or xterm modifyOtherKeys (CSI >4;2m) for ctrl+shift+ +// disambiguation. We previously enabled unconditionally (#23350), assuming +// terminals silently ignore unknown CSI — but some terminals honor the enable +// and emit codepoints our input parser doesn't handle (notably over SSH and +// in xterm.js-based terminals like VS Code). tmux is allowlisted because it +// accepts modifyOtherKeys and doesn't forward the kitty sequence to the outer +// terminal. +const EXTENDED_KEYS_TERMINALS = [ + 'iTerm.app', + 'kitty', + 'WezTerm', + 'ghostty', + 'tmux', + 'windows-terminal', +] + +/** True if this terminal correctly handles extended key reporting + * (Kitty keyboard protocol + xterm modifyOtherKeys). */ +export function supportsExtendedKeys(): boolean { + return EXTENDED_KEYS_TERMINALS.includes(env.terminal ?? '') +} + +/** True if the terminal scrolls the viewport when it receives cursor-up + * sequences that reach above the visible area. On Windows, conhost's + * SetConsoleCursorPosition follows the cursor into scrollback + * (microsoft/terminal#14774), yanking users to the top of their buffer + * mid-stream. WT_SESSION catches WSL-in-Windows-Terminal where platform + * is linux but output still routes through conhost. */ +export function hasCursorUpViewportYankBug(): boolean { + return process.platform === 'win32' || !!process.env.WT_SESSION +} + +// Computed once at module load — terminal capabilities don't change mid-session. +// Exported so callers can pass a sync-skip hint gated to specific modes. +export const SYNC_OUTPUT_SUPPORTED = isSynchronizedOutputSupported() + +export type Terminal = { + stdout: Writable + stderr: Writable +} + +export function writeDiffToTerminal( + terminal: Terminal, + diff: Diff, + skipSyncMarkers = false, +): void { + // No output if there are no patches + if (diff.length === 0) { + return + } + + // BSU/ESU wrapping is opt-out to keep main-screen behavior unchanged. + // Callers pass skipSyncMarkers=true when the terminal doesn't support + // DEC 2026 (e.g. tmux) AND the cost matters (high-frequency alt-screen). + const useSync = !skipSyncMarkers + + // Buffer all writes into a single string to avoid multiple write calls + let buffer = useSync ? BSU : '' + + for (const patch of diff) { + switch (patch.type) { + case 'stdout': + buffer += patch.content + break + case 'clear': + if (patch.count > 0) { + buffer += eraseLines(patch.count) + } + break + case 'clearTerminal': + buffer += getClearTerminalSequence() + break + case 'cursorHide': + buffer += HIDE_CURSOR + break + case 'cursorShow': + buffer += SHOW_CURSOR + break + case 'cursorMove': + buffer += cursorMove(patch.x, patch.y) + break + case 'cursorTo': + buffer += cursorTo(patch.col) + break + case 'carriageReturn': + buffer += '\r' + break + case 'hyperlink': + buffer += link(patch.uri) + break + case 'styleStr': + buffer += patch.str + break + } + } + + // Add synchronized update end and flush buffer + if (useSync) buffer += ESU + terminal.stdout.write(buffer) +} diff --git a/ink/termio.ts b/ink/termio.ts new file mode 100644 index 0000000..39f4fb2 --- /dev/null +++ b/ink/termio.ts @@ -0,0 +1,42 @@ +/** + * ANSI Parser Module + * + * A semantic ANSI escape sequence parser inspired by ghostty, tmux, and iTerm2. + * + * Key features: + * - Semantic output: produces structured actions, not string tokens + * - Streaming: can parse input incrementally via Parser class + * - Style tracking: maintains text style state across parse calls + * - Comprehensive: supports SGR, CSI, OSC, ESC sequences + * + * Usage: + * + * ```typescript + * import { Parser } from './termio.js' + * + * const parser = new Parser() + * const actions = parser.feed('\x1b[31mred\x1b[0m') + * // => [{ type: 'text', graphemes: [...], style: { fg: { type: 'named', name: 'red' }, ... } }] + * ``` + */ + +// Parser +export { Parser } from './termio/parser.js' +// Types +export type { + Action, + Color, + CursorAction, + CursorDirection, + EraseAction, + Grapheme, + LinkAction, + ModeAction, + NamedColor, + ScrollAction, + TextSegment, + TextStyle, + TitleAction, + UnderlineStyle, +} from './termio/types.js' +export { colorsEqual, defaultStyle, stylesEqual } from './termio/types.js' diff --git a/ink/termio/ansi.ts b/ink/termio/ansi.ts new file mode 100644 index 0000000..c6e8eff --- /dev/null +++ b/ink/termio/ansi.ts @@ -0,0 +1,75 @@ +/** + * ANSI Control Characters and Escape Sequence Introducers + * + * Based on ECMA-48 / ANSI X3.64 standards. + */ + +/** + * C0 (7-bit) control characters + */ +export const C0 = { + NUL: 0x00, + SOH: 0x01, + STX: 0x02, + ETX: 0x03, + EOT: 0x04, + ENQ: 0x05, + ACK: 0x06, + BEL: 0x07, + BS: 0x08, + HT: 0x09, + LF: 0x0a, + VT: 0x0b, + FF: 0x0c, + CR: 0x0d, + SO: 0x0e, + SI: 0x0f, + DLE: 0x10, + DC1: 0x11, + DC2: 0x12, + DC3: 0x13, + DC4: 0x14, + NAK: 0x15, + SYN: 0x16, + ETB: 0x17, + CAN: 0x18, + EM: 0x19, + SUB: 0x1a, + ESC: 0x1b, + FS: 0x1c, + GS: 0x1d, + RS: 0x1e, + US: 0x1f, + DEL: 0x7f, +} as const + +// String constants for output generation +export const ESC = '\x1b' +export const BEL = '\x07' +export const SEP = ';' + +/** + * Escape sequence type introducers (byte after ESC) + */ +export const ESC_TYPE = { + CSI: 0x5b, // [ - Control Sequence Introducer + OSC: 0x5d, // ] - Operating System Command + DCS: 0x50, // P - Device Control String + APC: 0x5f, // _ - Application Program Command + PM: 0x5e, // ^ - Privacy Message + SOS: 0x58, // X - Start of String + ST: 0x5c, // \ - String Terminator +} as const + +/** Check if a byte is a C0 control character */ +export function isC0(byte: number): boolean { + return byte < 0x20 || byte === 0x7f +} + +/** + * Check if a byte is an ESC sequence final byte (0-9, :, ;, <, =, >, ?, @ through ~) + * ESC sequences have a wider final byte range than CSI + */ +export function isEscFinal(byte: number): boolean { + return byte >= 0x30 && byte <= 0x7e +} diff --git a/ink/termio/csi.ts b/ink/termio/csi.ts new file mode 100644 index 0000000..f3b2f52 --- /dev/null +++ b/ink/termio/csi.ts @@ -0,0 +1,319 @@ +/** + * CSI (Control Sequence Introducer) Types + * + * Enums and types for CSI command parameters. + */ + +import { ESC, ESC_TYPE, SEP } from './ansi.js' + +export const CSI_PREFIX = ESC + String.fromCharCode(ESC_TYPE.CSI) + +/** + * CSI parameter byte ranges + */ +export const CSI_RANGE = { + PARAM_START: 0x30, + PARAM_END: 0x3f, + INTERMEDIATE_START: 0x20, + INTERMEDIATE_END: 0x2f, + FINAL_START: 0x40, + FINAL_END: 0x7e, +} as const + +/** Check if a byte is a CSI parameter byte */ +export function isCSIParam(byte: number): boolean { + return byte >= CSI_RANGE.PARAM_START && byte <= CSI_RANGE.PARAM_END +} + +/** Check if a byte is a CSI intermediate byte */ +export function isCSIIntermediate(byte: number): boolean { + return ( + byte >= CSI_RANGE.INTERMEDIATE_START && byte <= CSI_RANGE.INTERMEDIATE_END + ) +} + +/** Check if a byte is a CSI final byte (@ through ~) */ +export function isCSIFinal(byte: number): boolean { + return byte >= CSI_RANGE.FINAL_START && byte <= CSI_RANGE.FINAL_END +} + +/** + * Generate a CSI sequence: ESC [ p1;p2;...;pN final + * Single arg: treated as raw body + * Multiple args: last is final byte, rest are params joined by ; + */ +export function csi(...args: (string | number)[]): string { + if (args.length === 0) return CSI_PREFIX + if (args.length === 1) return `${CSI_PREFIX}${args[0]}` + const params = args.slice(0, -1) + const final = args[args.length - 1] + return `${CSI_PREFIX}${params.join(SEP)}${final}` +} + +/** + * CSI final bytes - the command identifier + */ +export const CSI = { + // Cursor movement + CUU: 0x41, // A - Cursor Up + CUD: 0x42, // B - Cursor Down + CUF: 0x43, // C - Cursor Forward + CUB: 0x44, // D - Cursor Back + CNL: 0x45, // E - Cursor Next Line + CPL: 0x46, // F - Cursor Previous Line + CHA: 0x47, // G - Cursor Horizontal Absolute + CUP: 0x48, // H - Cursor Position + CHT: 0x49, // I - Cursor Horizontal Tab + VPA: 0x64, // d - Vertical Position Absolute + HVP: 0x66, // f - Horizontal Vertical Position + + // Erase + ED: 0x4a, // J - Erase in Display + EL: 0x4b, // K - Erase in Line + ECH: 0x58, // X - Erase Character + + // Insert/Delete + IL: 0x4c, // L - Insert Lines + DL: 0x4d, // M - Delete Lines + ICH: 0x40, // @ - Insert Characters + DCH: 0x50, // P - Delete Characters + + // Scroll + SU: 0x53, // S - Scroll Up + SD: 0x54, // T - Scroll Down + + // Modes + SM: 0x68, // h - Set Mode + RM: 0x6c, // l - Reset Mode + + // SGR + SGR: 0x6d, // m - Select Graphic Rendition + + // Other + DSR: 0x6e, // n - Device Status Report + DECSCUSR: 0x71, // q - Set Cursor Style (with space intermediate) + DECSTBM: 0x72, // r - Set Top and Bottom Margins + SCOSC: 0x73, // s - Save Cursor Position + SCORC: 0x75, // u - Restore Cursor Position + CBT: 0x5a, // Z - Cursor Backward Tabulation +} as const + +/** + * Erase in Display regions (ED command parameter) + */ +export const ERASE_DISPLAY = ['toEnd', 'toStart', 'all', 'scrollback'] as const + +/** + * Erase in Line regions (EL command parameter) + */ +export const ERASE_LINE_REGION = ['toEnd', 'toStart', 'all'] as const + +/** + * Cursor styles (DECSCUSR) + */ +export type CursorStyle = 'block' | 'underline' | 'bar' + +export const CURSOR_STYLES: Array<{ style: CursorStyle; blinking: boolean }> = [ + { style: 'block', blinking: true }, // 0 - default + { style: 'block', blinking: true }, // 1 + { style: 'block', blinking: false }, // 2 + { style: 'underline', blinking: true }, // 3 + { style: 'underline', blinking: false }, // 4 + { style: 'bar', blinking: true }, // 5 + { style: 'bar', blinking: false }, // 6 +] + +// Cursor movement generators + +/** Move cursor up n lines (CSI n A) */ +export function cursorUp(n = 1): string { + return n === 0 ? '' : csi(n, 'A') +} + +/** Move cursor down n lines (CSI n B) */ +export function cursorDown(n = 1): string { + return n === 0 ? '' : csi(n, 'B') +} + +/** Move cursor forward n columns (CSI n C) */ +export function cursorForward(n = 1): string { + return n === 0 ? '' : csi(n, 'C') +} + +/** Move cursor back n columns (CSI n D) */ +export function cursorBack(n = 1): string { + return n === 0 ? '' : csi(n, 'D') +} + +/** Move cursor to column n (1-indexed) (CSI n G) */ +export function cursorTo(col: number): string { + return csi(col, 'G') +} + +/** Move cursor to column 1 (CSI G) */ +export const CURSOR_LEFT = csi('G') + +/** Move cursor to row, col (1-indexed) (CSI row ; col H) */ +export function cursorPosition(row: number, col: number): string { + return csi(row, col, 'H') +} + +/** Move cursor to home position (CSI H) */ +export const CURSOR_HOME = csi('H') + +/** + * Move cursor relative to current position + * Positive x = right, negative x = left + * Positive y = down, negative y = up + */ +export function cursorMove(x: number, y: number): string { + let result = '' + // Horizontal first (matches ansi-escapes behavior) + if (x < 0) { + result += cursorBack(-x) + } else if (x > 0) { + result += cursorForward(x) + } + // Then vertical + if (y < 0) { + result += cursorUp(-y) + } else if (y > 0) { + result += cursorDown(y) + } + return result +} + +// Save/restore cursor position + +/** Save cursor position (CSI s) */ +export const CURSOR_SAVE = csi('s') + +/** Restore cursor position (CSI u) */ +export const CURSOR_RESTORE = csi('u') + +// Erase generators + +/** Erase from cursor to end of line (CSI K) */ +export function eraseToEndOfLine(): string { + return csi('K') +} + +/** Erase from cursor to start of line (CSI 1 K) */ +export function eraseToStartOfLine(): string { + return csi(1, 'K') +} + +/** Erase entire line (CSI 2 K) */ +export function eraseLine(): string { + return csi(2, 'K') +} + +/** Erase entire line - constant form */ +export const ERASE_LINE = csi(2, 'K') + +/** Erase from cursor to end of screen (CSI J) */ +export function eraseToEndOfScreen(): string { + return csi('J') +} + +/** Erase from cursor to start of screen (CSI 1 J) */ +export function eraseToStartOfScreen(): string { + return csi(1, 'J') +} + +/** Erase entire screen (CSI 2 J) */ +export function eraseScreen(): string { + return csi(2, 'J') +} + +/** Erase entire screen - constant form */ +export const ERASE_SCREEN = csi(2, 'J') + +/** Erase scrollback buffer (CSI 3 J) */ +export const ERASE_SCROLLBACK = csi(3, 'J') + +/** + * Erase n lines starting from cursor line, moving cursor up + * This erases each line and moves up, ending at column 1 + */ +export function eraseLines(n: number): string { + if (n <= 0) return '' + let result = '' + for (let i = 0; i < n; i++) { + result += ERASE_LINE + if (i < n - 1) { + result += cursorUp(1) + } + } + result += CURSOR_LEFT + return result +} + +// Scroll + +/** Scroll up n lines (CSI n S) */ +export function scrollUp(n = 1): string { + return n === 0 ? '' : csi(n, 'S') +} + +/** Scroll down n lines (CSI n T) */ +export function scrollDown(n = 1): string { + return n === 0 ? '' : csi(n, 'T') +} + +/** Set scroll region (DECSTBM, CSI top;bottom r). 1-indexed, inclusive. */ +export function setScrollRegion(top: number, bottom: number): string { + return csi(top, bottom, 'r') +} + +/** Reset scroll region to full screen (DECSTBM, CSI r). Homes the cursor. */ +export const RESET_SCROLL_REGION = csi('r') + +// Bracketed paste markers (input from terminal, not output) +// These are sent by the terminal to delimit pasted content when +// bracketed paste mode is enabled (via DEC mode 2004) + +/** Sent by terminal before pasted content (CSI 200 ~) */ +export const PASTE_START = csi('200~') + +/** Sent by terminal after pasted content (CSI 201 ~) */ +export const PASTE_END = csi('201~') + +// Focus event markers (input from terminal, not output) +// These are sent by the terminal when focus changes while +// focus events mode is enabled (via DEC mode 1004) + +/** Sent by terminal when it gains focus (CSI I) */ +export const FOCUS_IN = csi('I') + +/** Sent by terminal when it loses focus (CSI O) */ +export const FOCUS_OUT = csi('O') + +// Kitty keyboard protocol (CSI u) +// Enables enhanced key reporting with modifier information +// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/ + +/** + * Enable Kitty keyboard protocol with basic modifier reporting + * CSI > 1 u - pushes mode with flags=1 (disambiguate escape codes) + * This makes Shift+Enter send CSI 13;2 u instead of just CR + */ +export const ENABLE_KITTY_KEYBOARD = csi('>1u') + +/** + * Disable Kitty keyboard protocol + * CSI < u - pops the keyboard mode stack + */ +export const DISABLE_KITTY_KEYBOARD = csi('4;2m') + +/** + * Disable xterm modifyOtherKeys (reset to default). + */ +export const DISABLE_MODIFY_OTHER_KEYS = csi('>4m') diff --git a/ink/termio/dec.ts b/ink/termio/dec.ts new file mode 100644 index 0000000..ac8bcc7 --- /dev/null +++ b/ink/termio/dec.ts @@ -0,0 +1,60 @@ +/** + * DEC (Digital Equipment Corporation) Private Mode Sequences + * + * DEC private modes use CSI ? N h (set) and CSI ? N l (reset) format. + * These are terminal-specific extensions to the ANSI standard. + */ + +import { csi } from './csi.js' + +/** + * DEC private mode numbers + */ +export const DEC = { + CURSOR_VISIBLE: 25, + ALT_SCREEN: 47, + ALT_SCREEN_CLEAR: 1049, + MOUSE_NORMAL: 1000, + MOUSE_BUTTON: 1002, + MOUSE_ANY: 1003, + MOUSE_SGR: 1006, + FOCUS_EVENTS: 1004, + BRACKETED_PASTE: 2004, + SYNCHRONIZED_UPDATE: 2026, +} as const + +/** Generate CSI ? N h sequence (set mode) */ +export function decset(mode: number): string { + return csi(`?${mode}h`) +} + +/** Generate CSI ? N l sequence (reset mode) */ +export function decreset(mode: number): string { + return csi(`?${mode}l`) +} + +// Pre-generated sequences for common modes +export const BSU = decset(DEC.SYNCHRONIZED_UPDATE) +export const ESU = decreset(DEC.SYNCHRONIZED_UPDATE) +export const EBP = decset(DEC.BRACKETED_PASTE) +export const DBP = decreset(DEC.BRACKETED_PASTE) +export const EFE = decset(DEC.FOCUS_EVENTS) +export const DFE = decreset(DEC.FOCUS_EVENTS) +export const SHOW_CURSOR = decset(DEC.CURSOR_VISIBLE) +export const HIDE_CURSOR = decreset(DEC.CURSOR_VISIBLE) +export const ENTER_ALT_SCREEN = decset(DEC.ALT_SCREEN_CLEAR) +export const EXIT_ALT_SCREEN = decreset(DEC.ALT_SCREEN_CLEAR) +// Mouse tracking: 1000 reports button press/release/wheel, 1002 adds drag +// events (button-motion), 1003 adds all-motion (no button held — for +// hover), 1006 uses SGR format (CSI < btn;col;row M/m) instead of legacy +// X10 bytes. Combined: wheel + click/drag for selection + hover. +export const ENABLE_MOUSE_TRACKING = + decset(DEC.MOUSE_NORMAL) + + decset(DEC.MOUSE_BUTTON) + + decset(DEC.MOUSE_ANY) + + decset(DEC.MOUSE_SGR) +export const DISABLE_MOUSE_TRACKING = + decreset(DEC.MOUSE_SGR) + + decreset(DEC.MOUSE_ANY) + + decreset(DEC.MOUSE_BUTTON) + + decreset(DEC.MOUSE_NORMAL) diff --git a/ink/termio/esc.ts b/ink/termio/esc.ts new file mode 100644 index 0000000..6d4cc92 --- /dev/null +++ b/ink/termio/esc.ts @@ -0,0 +1,67 @@ +/** + * ESC Sequence Parser + * + * Handles simple escape sequences: ESC + one or two characters + */ + +import type { Action } from './types.js' + +/** + * Parse a simple ESC sequence + * + * @param chars - Characters after ESC (not including ESC itself) + */ +export function parseEsc(chars: string): Action | null { + if (chars.length === 0) return null + + const first = chars[0]! + + // Full reset (RIS) + if (first === 'c') { + return { type: 'reset' } + } + + // Cursor save (DECSC) + if (first === '7') { + return { type: 'cursor', action: { type: 'save' } } + } + + // Cursor restore (DECRC) + if (first === '8') { + return { type: 'cursor', action: { type: 'restore' } } + } + + // Index - move cursor down (IND) + if (first === 'D') { + return { + type: 'cursor', + action: { type: 'move', direction: 'down', count: 1 }, + } + } + + // Reverse index - move cursor up (RI) + if (first === 'M') { + return { + type: 'cursor', + action: { type: 'move', direction: 'up', count: 1 }, + } + } + + // Next line (NEL) + if (first === 'E') { + return { type: 'cursor', action: { type: 'nextLine', count: 1 } } + } + + // Horizontal tab set (HTS) + if (first === 'H') { + return null // Tab stop, not commonly needed + } + + // Charset selection (ESC ( X, ESC ) X, etc.) - silently ignore + if ('()'.includes(first) && chars.length >= 2) { + return null + } + + // Unknown + return { type: 'unknown', sequence: `\x1b${chars}` } +} diff --git a/ink/termio/osc.ts b/ink/termio/osc.ts new file mode 100644 index 0000000..9bef515 --- /dev/null +++ b/ink/termio/osc.ts @@ -0,0 +1,493 @@ +/** + * OSC (Operating System Command) Types and Parser + */ + +import { Buffer } from 'buffer' +import { env } from '../../utils/env.js' +import { execFileNoThrow } from '../../utils/execFileNoThrow.js' +import { BEL, ESC, ESC_TYPE, SEP } from './ansi.js' +import type { Action, Color, TabStatusAction } from './types.js' + +export const OSC_PREFIX = ESC + String.fromCharCode(ESC_TYPE.OSC) + +/** String Terminator (ESC \) - alternative to BEL for terminating OSC */ +export const ST = ESC + '\\' + +/** Generate an OSC sequence: ESC ] p1;p2;...;pN + * Uses ST terminator for Kitty (avoids beeps), BEL for others */ +export function osc(...parts: (string | number)[]): string { + const terminator = env.terminal === 'kitty' ? ST : BEL + return `${OSC_PREFIX}${parts.join(SEP)}${terminator}` +} + +/** + * Wrap an escape sequence for terminal multiplexer passthrough. + * tmux and GNU screen intercept escape sequences; DCS passthrough + * tunnels them to the outer terminal unmodified. + * + * tmux 3.3+ gates this behind `allow-passthrough` (default off). When off, + * tmux silently drops the whole DCS — no junk, no worse than unwrapped OSC. + * Users who want passthrough set it in their .tmux.conf; we don't mutate it. + * + * Do NOT wrap BEL: raw \x07 triggers tmux's bell-action (window flag); + * wrapped \x07 is opaque DCS payload and tmux never sees the bell. + */ +export function wrapForMultiplexer(sequence: string): string { + if (process.env['TMUX']) { + const escaped = sequence.replaceAll('\x1b', '\x1b\x1b') + return `\x1bPtmux;${escaped}\x1b\\` + } + if (process.env['STY']) { + return `\x1bP${sequence}\x1b\\` + } + return sequence +} + +/** + * Which path setClipboard() will take, based on env state. Synchronous so + * callers can show an honest toast without awaiting the copy itself. + * + * - 'native': pbcopy (or equivalent) will run — high-confidence system + * clipboard write. tmux buffer may also be loaded as a bonus. + * - 'tmux-buffer': tmux load-buffer will run, but no native tool — paste + * with prefix+] works. System clipboard depends on tmux's set-clipboard + * option + outer terminal OSC 52 support; can't know from here. + * - 'osc52': only the raw OSC 52 sequence will be written to stdout. + * Best-effort; iTerm2 disables OSC 52 by default. + * + * pbcopy gating uses SSH_CONNECTION specifically, not SSH_TTY — tmux panes + * inherit SSH_TTY forever even after local reattach, but SSH_CONNECTION is + * in tmux's default update-environment set and gets cleared. + */ +export type ClipboardPath = 'native' | 'tmux-buffer' | 'osc52' + +export function getClipboardPath(): ClipboardPath { + const nativeAvailable = + process.platform === 'darwin' && !process.env['SSH_CONNECTION'] + if (nativeAvailable) return 'native' + if (process.env['TMUX']) return 'tmux-buffer' + return 'osc52' +} + +/** + * Wrap a payload in tmux's DCS passthrough: ESC P tmux ; ESC \ + * tmux forwards the payload to the outer terminal, bypassing its own parser. + * Inner ESCs must be doubled. Requires `set -g allow-passthrough on` in + * ~/.tmux.conf; without it, tmux silently drops the whole DCS (no regression). + */ +function tmuxPassthrough(payload: string): string { + return `${ESC}Ptmux;${payload.replaceAll(ESC, ESC + ESC)}${ST}` +} + +/** + * Load text into tmux's paste buffer via `tmux load-buffer`. + * -w (tmux 3.2+) propagates to the outer terminal's clipboard via tmux's + * own OSC 52 emission. -w is dropped for iTerm2: tmux's OSC 52 emission + * crashes the iTerm2 session over SSH. + * + * Returns true if the buffer was loaded successfully. + */ +export async function tmuxLoadBuffer(text: string): Promise { + if (!process.env['TMUX']) return false + const args = + process.env['LC_TERMINAL'] === 'iTerm2' + ? ['load-buffer', '-'] + : ['load-buffer', '-w', '-'] + const { code } = await execFileNoThrow('tmux', args, { + input: text, + useCwd: false, + timeout: 2000, + }) + return code === 0 +} + +/** + * OSC 52 clipboard write: ESC ] 52 ; c ; BEL/ST + * 'c' selects the clipboard (vs 'p' for primary selection on X11). + * + * When inside tmux ($TMUX set), `tmux load-buffer -w -` is the primary + * path. tmux's buffer is always reachable — works over SSH, survives + * detach/reattach, immune to stale env vars. The -w flag (tmux 3.2+) tells + * tmux to also propagate to the outer terminal via its own OSC 52 path, + * which tmux wraps correctly for the attached client. On older tmux, -w is + * ignored and the buffer is still loaded. -w is dropped for iTerm2 (#22432) + * because tmux's own OSC 52 emission (empty selection param: ESC]52;;b64) + * crashes iTerm2 over SSH. + * + * After load-buffer succeeds, we ALSO return a DCS-passthrough-wrapped + * OSC 52 for the caller to write to stdout. Our sequence uses explicit `c` + * (not tmux's crashy empty-param variant), so it sidesteps the #22432 path. + * With `allow-passthrough on` + an OSC-52-capable outer terminal, selection + * reaches the system clipboard; with either off, tmux silently drops the + * DCS and prefix+] still works. See Greg Smith's "free pony" in + * https://anthropic.slack.com/archives/C07VBSHV7EV/p1773177228548119. + * + * If load-buffer fails entirely, fall through to raw OSC 52. + * + * Outside tmux, write raw OSC 52 to stdout (caller handles the write). + * + * Local (no SSH_CONNECTION): also shell out to a native clipboard utility. + * OSC 52 and tmux -w both depend on terminal settings — iTerm2 disables + * OSC 52 by default, VS Code shows a permission prompt on first use. Native + * utilities (pbcopy/wl-copy/xclip/xsel/clip.exe) always work locally. Over + * SSH these would write to the remote clipboard — OSC 52 is the right path there. + * + * Returns the sequence for the caller to write to stdout (raw OSC 52 + * outside tmux, DCS-wrapped inside). + */ +export async function setClipboard(text: string): Promise { + const b64 = Buffer.from(text, 'utf8').toString('base64') + const raw = osc(OSC.CLIPBOARD, 'c', b64) + + // Native safety net — fire FIRST, before the tmux await, so a quick + // focus-switch after selecting doesn't race pbcopy. Previously this ran + // AFTER awaiting tmux load-buffer, adding ~50-100ms of subprocess latency + // before pbcopy even started — fast cmd+tab → paste would beat it + // (https://anthropic.slack.com/archives/C07VBSHV7EV/p1773943921788829). + // Gated on SSH_CONNECTION (not SSH_TTY) since tmux panes inherit SSH_TTY + // forever but SSH_CONNECTION is in tmux's default update-environment and + // clears on local attach. Fire-and-forget. + if (!process.env['SSH_CONNECTION']) copyNative(text) + + const tmuxBufferLoaded = await tmuxLoadBuffer(text) + + // Inner OSC uses BEL directly (not osc()) — ST's ESC would need doubling + // too, and BEL works everywhere for OSC 52. + if (tmuxBufferLoaded) return tmuxPassthrough(`${ESC}]52;c;${b64}${BEL}`) + return raw +} + +// Linux clipboard tool: undefined = not yet probed, null = none available. +// Probe order: wl-copy (Wayland) → xclip (X11) → xsel (X11 fallback). +// Cached after first attempt so repeated mouse-ups skip the probe chain. +let linuxCopy: 'wl-copy' | 'xclip' | 'xsel' | null | undefined + +/** + * Shell out to a native clipboard utility as a safety net for OSC 52. + * Only called when not in an SSH session (over SSH, these would write to + * the remote machine's clipboard — OSC 52 is the right path there). + * Fire-and-forget: failures are silent since OSC 52 may have succeeded. + */ +function copyNative(text: string): void { + const opts = { input: text, useCwd: false, timeout: 2000 } + switch (process.platform) { + case 'darwin': + void execFileNoThrow('pbcopy', [], opts) + return + case 'linux': { + if (linuxCopy === null) return + if (linuxCopy === 'wl-copy') { + void execFileNoThrow('wl-copy', [], opts) + return + } + if (linuxCopy === 'xclip') { + void execFileNoThrow('xclip', ['-selection', 'clipboard'], opts) + return + } + if (linuxCopy === 'xsel') { + void execFileNoThrow('xsel', ['--clipboard', '--input'], opts) + return + } + // First call: probe wl-copy (Wayland) then xclip/xsel (X11), cache winner. + void execFileNoThrow('wl-copy', [], opts).then(r => { + if (r.code === 0) { + linuxCopy = 'wl-copy' + return + } + void execFileNoThrow('xclip', ['-selection', 'clipboard'], opts).then( + r2 => { + if (r2.code === 0) { + linuxCopy = 'xclip' + return + } + void execFileNoThrow('xsel', ['--clipboard', '--input'], opts).then( + r3 => { + linuxCopy = r3.code === 0 ? 'xsel' : null + }, + ) + }, + ) + }) + return + } + case 'win32': + // clip.exe is always available on Windows. Unicode handling is + // imperfect (system locale encoding) but good enough for a fallback. + void execFileNoThrow('clip', [], opts) + return + } +} + +/** @internal test-only */ +export function _resetLinuxCopyCache(): void { + linuxCopy = undefined +} + +/** + * OSC command numbers + */ +export const OSC = { + SET_TITLE_AND_ICON: 0, + SET_ICON: 1, + SET_TITLE: 2, + SET_COLOR: 4, + SET_CWD: 7, + HYPERLINK: 8, + ITERM2: 9, // iTerm2 proprietary sequences + SET_FG_COLOR: 10, + SET_BG_COLOR: 11, + SET_CURSOR_COLOR: 12, + CLIPBOARD: 52, + KITTY: 99, // Kitty notification protocol + RESET_COLOR: 104, + RESET_FG_COLOR: 110, + RESET_BG_COLOR: 111, + RESET_CURSOR_COLOR: 112, + SEMANTIC_PROMPT: 133, + GHOSTTY: 777, // Ghostty notification protocol + TAB_STATUS: 21337, // Tab status extension +} as const + +/** + * Parse an OSC sequence into an action + * + * @param content - The sequence content (without ESC ] and terminator) + */ +export function parseOSC(content: string): Action | null { + const semicolonIdx = content.indexOf(';') + const command = semicolonIdx >= 0 ? content.slice(0, semicolonIdx) : content + const data = semicolonIdx >= 0 ? content.slice(semicolonIdx + 1) : '' + + const commandNum = parseInt(command, 10) + + // Window/icon title + if (commandNum === OSC.SET_TITLE_AND_ICON) { + return { type: 'title', action: { type: 'both', title: data } } + } + if (commandNum === OSC.SET_ICON) { + return { type: 'title', action: { type: 'iconName', name: data } } + } + if (commandNum === OSC.SET_TITLE) { + return { type: 'title', action: { type: 'windowTitle', title: data } } + } + + // Hyperlinks (OSC 8) + if (commandNum === OSC.HYPERLINK) { + const parts = data.split(';') + const paramsStr = parts[0] ?? '' + const url = parts.slice(1).join(';') + + if (url === '') { + return { type: 'link', action: { type: 'end' } } + } + + const params: Record = {} + if (paramsStr) { + for (const pair of paramsStr.split(':')) { + const eqIdx = pair.indexOf('=') + if (eqIdx >= 0) { + params[pair.slice(0, eqIdx)] = pair.slice(eqIdx + 1) + } + } + } + + return { + type: 'link', + action: { + type: 'start', + url, + params: Object.keys(params).length > 0 ? params : undefined, + }, + } + } + + // Tab status (OSC 21337) + if (commandNum === OSC.TAB_STATUS) { + return { type: 'tabStatus', action: parseTabStatus(data) } + } + + return { type: 'unknown', sequence: `\x1b]${content}` } +} + +/** + * Parse an XParseColor-style color spec into an RGB Color. + * Accepts `#RRGGBB` and `rgb:R/G/B` (1–4 hex digits per component, scaled + * to 8-bit). Returns null on parse failure. + */ +export function parseOscColor(spec: string): Color | null { + const hex = spec.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i) + if (hex) { + return { + type: 'rgb', + r: parseInt(hex[1]!, 16), + g: parseInt(hex[2]!, 16), + b: parseInt(hex[3]!, 16), + } + } + const rgb = spec.match( + /^rgb:([0-9a-f]{1,4})\/([0-9a-f]{1,4})\/([0-9a-f]{1,4})$/i, + ) + if (rgb) { + // XParseColor: N hex digits → value / (16^N - 1), scale to 0-255 + const scale = (s: string) => + Math.round((parseInt(s, 16) / (16 ** s.length - 1)) * 255) + return { + type: 'rgb', + r: scale(rgb[1]!), + g: scale(rgb[2]!), + b: scale(rgb[3]!), + } + } + return null +} + +/** + * Parse OSC 21337 payload: `key=value;key=value;...` with `\;` and `\\` + * escapes inside values. Bare key or `key=` clears that field; unknown + * keys are ignored. + */ +function parseTabStatus(data: string): TabStatusAction { + const action: TabStatusAction = {} + for (const [key, value] of splitTabStatusPairs(data)) { + switch (key) { + case 'indicator': + action.indicator = value === '' ? null : parseOscColor(value) + break + case 'status': + action.status = value === '' ? null : value + break + case 'status-color': + action.statusColor = value === '' ? null : parseOscColor(value) + break + } + } + return action +} + +/** Split `k=v;k=v` honoring `\;` and `\\` escapes. Yields [key, unescapedValue]. */ +function* splitTabStatusPairs(data: string): Generator<[string, string]> { + let key = '' + let val = '' + let inVal = false + let esc = false + for (const c of data) { + if (esc) { + if (inVal) val += c + else key += c + esc = false + } else if (c === '\\') { + esc = true + } else if (c === ';') { + yield [key, val] + key = '' + val = '' + inVal = false + } else if (c === '=' && !inVal) { + inVal = true + } else if (inVal) { + val += c + } else { + key += c + } + } + if (key || inVal) yield [key, val] +} + +// Output generators + +/** Start a hyperlink (OSC 8). Auto-assigns an id= param derived from the URL + * so terminals group wrapped lines of the same link together (the spec says + * cells with matching URI *and* nonempty id are joined; without an id each + * wrapped line is a separate link — inconsistent hover, partial tooltips). + * Empty url = close sequence (empty params per spec). */ +export function link(url: string, params?: Record): string { + if (!url) return LINK_END + const p = { id: osc8Id(url), ...params } + const paramStr = Object.entries(p) + .map(([k, v]) => `${k}=${v}`) + .join(':') + return osc(OSC.HYPERLINK, paramStr, url) +} + +function osc8Id(url: string): string { + let h = 0 + for (let i = 0; i < url.length; i++) + h = ((h << 5) - h + url.charCodeAt(i)) | 0 + return (h >>> 0).toString(36) +} + +/** End a hyperlink (OSC 8) */ +export const LINK_END = osc(OSC.HYPERLINK, '', '') + +// iTerm2 OSC 9 subcommands + +/** iTerm2 OSC 9 subcommand numbers */ +export const ITERM2 = { + NOTIFY: 0, + BADGE: 2, + PROGRESS: 4, +} as const + +/** Progress operation codes (for use with ITERM2.PROGRESS) */ +export const PROGRESS = { + CLEAR: 0, + SET: 1, + ERROR: 2, + INDETERMINATE: 3, +} as const + +/** + * Clear iTerm2 progress bar sequence (OSC 9;4;0;BEL) + * Uses BEL terminator since this is for cleanup (not runtime notification) + * and we want to ensure it's always sent regardless of terminal type. + */ +export const CLEAR_ITERM2_PROGRESS = `${OSC_PREFIX}${OSC.ITERM2};${ITERM2.PROGRESS};${PROGRESS.CLEAR};${BEL}` + +/** + * Clear terminal title sequence (OSC 0 with empty string + BEL). + * Uses BEL terminator for cleanup — safe on all terminals. + */ +export const CLEAR_TERMINAL_TITLE = `${OSC_PREFIX}${OSC.SET_TITLE_AND_ICON};${BEL}` + +/** Clear all three OSC 21337 tab-status fields. Used on exit. */ +export const CLEAR_TAB_STATUS = osc( + OSC.TAB_STATUS, + 'indicator=;status=;status-color=', +) + +/** + * Gate for emitting OSC 21337 (tab-status indicator). Ant-only while the + * spec is unstable. Terminals that don't recognize it discard silently, so + * emission is safe unconditionally — we don't gate on terminal detection + * since support is expected across several terminals. + * + * Callers must wrap output with wrapForMultiplexer() so tmux/screen + * DCS-passthrough carries the sequence to the outer terminal. + */ +export function supportsTabStatus(): boolean { + return process.env.USER_TYPE === 'ant' +} + +/** + * Emit an OSC 21337 tab-status sequence. Omitted fields are left unchanged + * by the receiving terminal; `null` sends an empty value to clear. + * `;` and `\` in status text are escaped per the spec. + */ +export function tabStatus(fields: TabStatusAction): string { + const parts: string[] = [] + const rgb = (c: Color) => + c.type === 'rgb' + ? `#${[c.r, c.g, c.b].map(n => n.toString(16).padStart(2, '0')).join('')}` + : '' + if ('indicator' in fields) + parts.push(`indicator=${fields.indicator ? rgb(fields.indicator) : ''}`) + if ('status' in fields) + parts.push( + `status=${fields.status?.replaceAll('\\', '\\\\').replaceAll(';', '\\;') ?? ''}`, + ) + if ('statusColor' in fields) + parts.push( + `status-color=${fields.statusColor ? rgb(fields.statusColor) : ''}`, + ) + return osc(OSC.TAB_STATUS, parts.join(';')) +} diff --git a/ink/termio/parser.ts b/ink/termio/parser.ts new file mode 100644 index 0000000..301f14c --- /dev/null +++ b/ink/termio/parser.ts @@ -0,0 +1,394 @@ +/** + * ANSI Parser - Semantic Action Generator + * + * A streaming parser for ANSI escape sequences that produces semantic actions. + * Uses the tokenizer for escape sequence boundary detection, then interprets + * each sequence to produce structured actions. + * + * Key design decisions: + * - Streaming: can process input incrementally + * - Semantic output: produces structured actions, not string tokens + * - Style tracking: maintains current text style state + */ + +import { getGraphemeSegmenter } from '../../utils/intl.js' +import { C0 } from './ansi.js' +import { CSI, CURSOR_STYLES, ERASE_DISPLAY, ERASE_LINE_REGION } from './csi.js' +import { DEC } from './dec.js' +import { parseEsc } from './esc.js' +import { parseOSC } from './osc.js' +import { applySGR } from './sgr.js' +import { createTokenizer, type Token, type Tokenizer } from './tokenize.js' +import type { Action, Grapheme, TextStyle } from './types.js' +import { defaultStyle } from './types.js' + +// ============================================================================= +// Grapheme Utilities +// ============================================================================= + +function isEmoji(codePoint: number): boolean { + return ( + (codePoint >= 0x2600 && codePoint <= 0x26ff) || + (codePoint >= 0x2700 && codePoint <= 0x27bf) || + (codePoint >= 0x1f300 && codePoint <= 0x1f9ff) || + (codePoint >= 0x1fa00 && codePoint <= 0x1faff) || + (codePoint >= 0x1f1e0 && codePoint <= 0x1f1ff) + ) +} + +function isEastAsianWide(codePoint: number): boolean { + return ( + (codePoint >= 0x1100 && codePoint <= 0x115f) || + (codePoint >= 0x2e80 && codePoint <= 0x9fff) || + (codePoint >= 0xac00 && codePoint <= 0xd7a3) || + (codePoint >= 0xf900 && codePoint <= 0xfaff) || + (codePoint >= 0xfe10 && codePoint <= 0xfe1f) || + (codePoint >= 0xfe30 && codePoint <= 0xfe6f) || + (codePoint >= 0xff00 && codePoint <= 0xff60) || + (codePoint >= 0xffe0 && codePoint <= 0xffe6) || + (codePoint >= 0x20000 && codePoint <= 0x2fffd) || + (codePoint >= 0x30000 && codePoint <= 0x3fffd) + ) +} + +function hasMultipleCodepoints(str: string): boolean { + let count = 0 + for (const _ of str) { + count++ + if (count > 1) return true + } + return false +} + +function graphemeWidth(grapheme: string): 1 | 2 { + if (hasMultipleCodepoints(grapheme)) return 2 + const codePoint = grapheme.codePointAt(0) + if (codePoint === undefined) return 1 + if (isEmoji(codePoint) || isEastAsianWide(codePoint)) return 2 + return 1 +} + +function* segmentGraphemes(str: string): Generator { + for (const { segment } of getGraphemeSegmenter().segment(str)) { + yield { value: segment, width: graphemeWidth(segment) } + } +} + +// ============================================================================= +// Sequence Parsing +// ============================================================================= + +function parseCSIParams(paramStr: string): number[] { + if (paramStr === '') return [] + return paramStr.split(/[;:]/).map(s => (s === '' ? 0 : parseInt(s, 10))) +} + +/** Parse a raw CSI sequence (e.g., "\x1b[31m") into an action */ +function parseCSI(rawSequence: string): Action | null { + const inner = rawSequence.slice(2) + if (inner.length === 0) return null + + const finalByte = inner.charCodeAt(inner.length - 1) + const beforeFinal = inner.slice(0, -1) + + let privateMode = '' + let paramStr = beforeFinal + let intermediate = '' + + if (beforeFinal.length > 0 && '?>='.includes(beforeFinal[0]!)) { + privateMode = beforeFinal[0]! + paramStr = beforeFinal.slice(1) + } + + const intermediateMatch = paramStr.match(/([^0-9;:]+)$/) + if (intermediateMatch) { + intermediate = intermediateMatch[1]! + paramStr = paramStr.slice(0, -intermediate.length) + } + + const params = parseCSIParams(paramStr) + const p0 = params[0] ?? 1 + const p1 = params[1] ?? 1 + + // SGR (Select Graphic Rendition) + if (finalByte === CSI.SGR && privateMode === '') { + return { type: 'sgr', params: paramStr } + } + + // Cursor movement + if (finalByte === CSI.CUU) { + return { + type: 'cursor', + action: { type: 'move', direction: 'up', count: p0 }, + } + } + if (finalByte === CSI.CUD) { + return { + type: 'cursor', + action: { type: 'move', direction: 'down', count: p0 }, + } + } + if (finalByte === CSI.CUF) { + return { + type: 'cursor', + action: { type: 'move', direction: 'forward', count: p0 }, + } + } + if (finalByte === CSI.CUB) { + return { + type: 'cursor', + action: { type: 'move', direction: 'back', count: p0 }, + } + } + if (finalByte === CSI.CNL) { + return { type: 'cursor', action: { type: 'nextLine', count: p0 } } + } + if (finalByte === CSI.CPL) { + return { type: 'cursor', action: { type: 'prevLine', count: p0 } } + } + if (finalByte === CSI.CHA) { + return { type: 'cursor', action: { type: 'column', col: p0 } } + } + if (finalByte === CSI.CUP || finalByte === CSI.HVP) { + return { type: 'cursor', action: { type: 'position', row: p0, col: p1 } } + } + if (finalByte === CSI.VPA) { + return { type: 'cursor', action: { type: 'row', row: p0 } } + } + + // Erase + if (finalByte === CSI.ED) { + const region = ERASE_DISPLAY[params[0] ?? 0] ?? 'toEnd' + return { type: 'erase', action: { type: 'display', region } } + } + if (finalByte === CSI.EL) { + const region = ERASE_LINE_REGION[params[0] ?? 0] ?? 'toEnd' + return { type: 'erase', action: { type: 'line', region } } + } + if (finalByte === CSI.ECH) { + return { type: 'erase', action: { type: 'chars', count: p0 } } + } + + // Scroll + if (finalByte === CSI.SU) { + return { type: 'scroll', action: { type: 'up', count: p0 } } + } + if (finalByte === CSI.SD) { + return { type: 'scroll', action: { type: 'down', count: p0 } } + } + if (finalByte === CSI.DECSTBM) { + return { + type: 'scroll', + action: { type: 'setRegion', top: p0, bottom: p1 }, + } + } + + // Cursor save/restore + if (finalByte === CSI.SCOSC) { + return { type: 'cursor', action: { type: 'save' } } + } + if (finalByte === CSI.SCORC) { + return { type: 'cursor', action: { type: 'restore' } } + } + + // Cursor style + if (finalByte === CSI.DECSCUSR && intermediate === ' ') { + const styleInfo = CURSOR_STYLES[p0] ?? CURSOR_STYLES[0]! + return { type: 'cursor', action: { type: 'style', ...styleInfo } } + } + + // Private modes + if (privateMode === '?' && (finalByte === CSI.SM || finalByte === CSI.RM)) { + const enabled = finalByte === CSI.SM + + if (p0 === DEC.CURSOR_VISIBLE) { + return { + type: 'cursor', + action: enabled ? { type: 'show' } : { type: 'hide' }, + } + } + if (p0 === DEC.ALT_SCREEN_CLEAR || p0 === DEC.ALT_SCREEN) { + return { type: 'mode', action: { type: 'alternateScreen', enabled } } + } + if (p0 === DEC.BRACKETED_PASTE) { + return { type: 'mode', action: { type: 'bracketedPaste', enabled } } + } + if (p0 === DEC.MOUSE_NORMAL) { + return { + type: 'mode', + action: { type: 'mouseTracking', mode: enabled ? 'normal' : 'off' }, + } + } + if (p0 === DEC.MOUSE_BUTTON) { + return { + type: 'mode', + action: { type: 'mouseTracking', mode: enabled ? 'button' : 'off' }, + } + } + if (p0 === DEC.MOUSE_ANY) { + return { + type: 'mode', + action: { type: 'mouseTracking', mode: enabled ? 'any' : 'off' }, + } + } + if (p0 === DEC.FOCUS_EVENTS) { + return { type: 'mode', action: { type: 'focusEvents', enabled } } + } + } + + return { type: 'unknown', sequence: rawSequence } +} + +/** + * Identify the type of escape sequence from its raw form. + */ +function identifySequence( + seq: string, +): 'csi' | 'osc' | 'esc' | 'ss3' | 'unknown' { + if (seq.length < 2) return 'unknown' + if (seq.charCodeAt(0) !== C0.ESC) return 'unknown' + + const second = seq.charCodeAt(1) + if (second === 0x5b) return 'csi' // [ + if (second === 0x5d) return 'osc' // ] + if (second === 0x4f) return 'ss3' // O + return 'esc' +} + +// ============================================================================= +// Main Parser +// ============================================================================= + +/** + * Parser class - maintains state for streaming/incremental parsing + * + * Usage: + * ```typescript + * const parser = new Parser() + * const actions1 = parser.feed('partial\x1b[') + * const actions2 = parser.feed('31mred') // state maintained internally + * ``` + */ +export class Parser { + private tokenizer: Tokenizer = createTokenizer() + + style: TextStyle = defaultStyle() + inLink = false + linkUrl: string | undefined + + reset(): void { + this.tokenizer.reset() + this.style = defaultStyle() + this.inLink = false + this.linkUrl = undefined + } + + /** Feed input and get resulting actions */ + feed(input: string): Action[] { + const tokens = this.tokenizer.feed(input) + const actions: Action[] = [] + + for (const token of tokens) { + const tokenActions = this.processToken(token) + actions.push(...tokenActions) + } + + return actions + } + + private processToken(token: Token): Action[] { + switch (token.type) { + case 'text': + return this.processText(token.value) + + case 'sequence': + return this.processSequence(token.value) + } + } + + private processText(text: string): Action[] { + // Handle BEL characters embedded in text + const actions: Action[] = [] + let current = '' + + for (const char of text) { + if (char.charCodeAt(0) === C0.BEL) { + if (current) { + const graphemes = [...segmentGraphemes(current)] + if (graphemes.length > 0) { + actions.push({ type: 'text', graphemes, style: { ...this.style } }) + } + current = '' + } + actions.push({ type: 'bell' }) + } else { + current += char + } + } + + if (current) { + const graphemes = [...segmentGraphemes(current)] + if (graphemes.length > 0) { + actions.push({ type: 'text', graphemes, style: { ...this.style } }) + } + } + + return actions + } + + private processSequence(seq: string): Action[] { + const seqType = identifySequence(seq) + + switch (seqType) { + case 'csi': { + const action = parseCSI(seq) + if (!action) return [] + if (action.type === 'sgr') { + this.style = applySGR(action.params, this.style) + return [] + } + return [action] + } + + case 'osc': { + // Extract OSC content (between ESC ] and terminator) + let content = seq.slice(2) + // Remove terminator (BEL or ESC \) + if (content.endsWith('\x07')) { + content = content.slice(0, -1) + } else if (content.endsWith('\x1b\\')) { + content = content.slice(0, -2) + } + + const action = parseOSC(content) + if (action) { + if (action.type === 'link') { + if (action.action.type === 'start') { + this.inLink = true + this.linkUrl = action.action.url + } else { + this.inLink = false + this.linkUrl = undefined + } + } + return [action] + } + return [] + } + + case 'esc': { + const escContent = seq.slice(1) + const action = parseEsc(escContent) + return action ? [action] : [] + } + + case 'ss3': + // SS3 sequences are typically cursor keys in application mode + // For output parsing, treat as unknown + return [{ type: 'unknown', sequence: seq }] + + default: + return [{ type: 'unknown', sequence: seq }] + } + } +} diff --git a/ink/termio/sgr.ts b/ink/termio/sgr.ts new file mode 100644 index 0000000..4c5a022 --- /dev/null +++ b/ink/termio/sgr.ts @@ -0,0 +1,308 @@ +/** + * SGR (Select Graphic Rendition) Parser + * + * Parses SGR parameters and applies them to a TextStyle. + * Handles both semicolon (;) and colon (:) separated parameters. + */ + +import type { NamedColor, TextStyle, UnderlineStyle } from './types.js' +import { defaultStyle } from './types.js' + +const NAMED_COLORS: NamedColor[] = [ + 'black', + 'red', + 'green', + 'yellow', + 'blue', + 'magenta', + 'cyan', + 'white', + 'brightBlack', + 'brightRed', + 'brightGreen', + 'brightYellow', + 'brightBlue', + 'brightMagenta', + 'brightCyan', + 'brightWhite', +] + +const UNDERLINE_STYLES: UnderlineStyle[] = [ + 'none', + 'single', + 'double', + 'curly', + 'dotted', + 'dashed', +] + +type Param = { value: number | null; subparams: number[]; colon: boolean } + +function parseParams(str: string): Param[] { + if (str === '') return [{ value: 0, subparams: [], colon: false }] + + const result: Param[] = [] + let current: Param = { value: null, subparams: [], colon: false } + let num = '' + let inSub = false + + for (let i = 0; i <= str.length; i++) { + const c = str[i] + if (c === ';' || c === undefined) { + const n = num === '' ? null : parseInt(num, 10) + if (inSub) { + if (n !== null) current.subparams.push(n) + } else { + current.value = n + } + result.push(current) + current = { value: null, subparams: [], colon: false } + num = '' + inSub = false + } else if (c === ':') { + const n = num === '' ? null : parseInt(num, 10) + if (!inSub) { + current.value = n + current.colon = true + inSub = true + } else { + if (n !== null) current.subparams.push(n) + } + num = '' + } else if (c >= '0' && c <= '9') { + num += c + } + } + return result +} + +function parseExtendedColor( + params: Param[], + idx: number, +): { r: number; g: number; b: number } | { index: number } | null { + const p = params[idx] + if (!p) return null + + if (p.colon && p.subparams.length >= 1) { + if (p.subparams[0] === 5 && p.subparams.length >= 2) { + return { index: p.subparams[1]! } + } + if (p.subparams[0] === 2 && p.subparams.length >= 4) { + const off = p.subparams.length >= 5 ? 1 : 0 + return { + r: p.subparams[1 + off]!, + g: p.subparams[2 + off]!, + b: p.subparams[3 + off]!, + } + } + } + + const next = params[idx + 1] + if (!next) return null + if ( + next.value === 5 && + params[idx + 2]?.value !== null && + params[idx + 2]?.value !== undefined + ) { + return { index: params[idx + 2]!.value! } + } + if (next.value === 2) { + const r = params[idx + 2]?.value + const g = params[idx + 3]?.value + const b = params[idx + 4]?.value + if ( + r !== null && + r !== undefined && + g !== null && + g !== undefined && + b !== null && + b !== undefined + ) { + return { r, g, b } + } + } + return null +} + +export function applySGR(paramStr: string, style: TextStyle): TextStyle { + const params = parseParams(paramStr) + let s = { ...style } + let i = 0 + + while (i < params.length) { + const p = params[i]! + const code = p.value ?? 0 + + if (code === 0) { + s = defaultStyle() + i++ + continue + } + if (code === 1) { + s.bold = true + i++ + continue + } + if (code === 2) { + s.dim = true + i++ + continue + } + if (code === 3) { + s.italic = true + i++ + continue + } + if (code === 4) { + s.underline = p.colon + ? (UNDERLINE_STYLES[p.subparams[0]!] ?? 'single') + : 'single' + i++ + continue + } + if (code === 5 || code === 6) { + s.blink = true + i++ + continue + } + if (code === 7) { + s.inverse = true + i++ + continue + } + if (code === 8) { + s.hidden = true + i++ + continue + } + if (code === 9) { + s.strikethrough = true + i++ + continue + } + if (code === 21) { + s.underline = 'double' + i++ + continue + } + if (code === 22) { + s.bold = false + s.dim = false + i++ + continue + } + if (code === 23) { + s.italic = false + i++ + continue + } + if (code === 24) { + s.underline = 'none' + i++ + continue + } + if (code === 25) { + s.blink = false + i++ + continue + } + if (code === 27) { + s.inverse = false + i++ + continue + } + if (code === 28) { + s.hidden = false + i++ + continue + } + if (code === 29) { + s.strikethrough = false + i++ + continue + } + if (code === 53) { + s.overline = true + i++ + continue + } + if (code === 55) { + s.overline = false + i++ + continue + } + + if (code >= 30 && code <= 37) { + s.fg = { type: 'named', name: NAMED_COLORS[code - 30]! } + i++ + continue + } + if (code === 39) { + s.fg = { type: 'default' } + i++ + continue + } + if (code >= 40 && code <= 47) { + s.bg = { type: 'named', name: NAMED_COLORS[code - 40]! } + i++ + continue + } + if (code === 49) { + s.bg = { type: 'default' } + i++ + continue + } + if (code >= 90 && code <= 97) { + s.fg = { type: 'named', name: NAMED_COLORS[code - 90 + 8]! } + i++ + continue + } + if (code >= 100 && code <= 107) { + s.bg = { type: 'named', name: NAMED_COLORS[code - 100 + 8]! } + i++ + continue + } + + if (code === 38) { + const c = parseExtendedColor(params, i) + if (c) { + s.fg = + 'index' in c + ? { type: 'indexed', index: c.index } + : { type: 'rgb', ...c } + i += p.colon ? 1 : 'index' in c ? 3 : 5 + continue + } + } + if (code === 48) { + const c = parseExtendedColor(params, i) + if (c) { + s.bg = + 'index' in c + ? { type: 'indexed', index: c.index } + : { type: 'rgb', ...c } + i += p.colon ? 1 : 'index' in c ? 3 : 5 + continue + } + } + if (code === 58) { + const c = parseExtendedColor(params, i) + if (c) { + s.underlineColor = + 'index' in c + ? { type: 'indexed', index: c.index } + : { type: 'rgb', ...c } + i += p.colon ? 1 : 'index' in c ? 3 : 5 + continue + } + } + if (code === 59) { + s.underlineColor = { type: 'default' } + i++ + continue + } + + i++ + } + return s +} diff --git a/ink/termio/tokenize.ts b/ink/termio/tokenize.ts new file mode 100644 index 0000000..68a0d11 --- /dev/null +++ b/ink/termio/tokenize.ts @@ -0,0 +1,319 @@ +/** + * Input Tokenizer - Escape sequence boundary detection + * + * Splits terminal input into tokens: text chunks and raw escape sequences. + * Unlike the Parser which interprets sequences semantically, this just + * identifies boundaries for use by keyboard input parsing. + */ + +import { C0, ESC_TYPE, isEscFinal } from './ansi.js' +import { isCSIFinal, isCSIIntermediate, isCSIParam } from './csi.js' + +export type Token = + | { type: 'text'; value: string } + | { type: 'sequence'; value: string } + +type State = + | 'ground' + | 'escape' + | 'escapeIntermediate' + | 'csi' + | 'ss3' + | 'osc' + | 'dcs' + | 'apc' + +export type Tokenizer = { + /** Feed input and get resulting tokens */ + feed(input: string): Token[] + /** Flush any buffered incomplete sequences */ + flush(): Token[] + /** Reset tokenizer state */ + reset(): void + /** Get any buffered incomplete sequence */ + buffer(): string +} + +type TokenizerOptions = { + /** + * Treat `CSI M` as an X10 mouse event prefix and consume 3 payload bytes. + * Only enable for stdin input — `\x1b[M` is also CSI DL (Delete Lines) in + * output streams, and enabling this there swallows display text. Default false. + */ + x10Mouse?: boolean +} + +/** + * Create a streaming tokenizer for terminal input. + * + * Usage: + * ```typescript + * const tokenizer = createTokenizer() + * const tokens1 = tokenizer.feed('hello\x1b[') + * const tokens2 = tokenizer.feed('A') // completes the escape sequence + * const remaining = tokenizer.flush() // force output incomplete sequences + * ``` + */ +export function createTokenizer(options?: TokenizerOptions): Tokenizer { + let currentState: State = 'ground' + let currentBuffer = '' + const x10Mouse = options?.x10Mouse ?? false + + return { + feed(input: string): Token[] { + const result = tokenize( + input, + currentState, + currentBuffer, + false, + x10Mouse, + ) + currentState = result.state.state + currentBuffer = result.state.buffer + return result.tokens + }, + + flush(): Token[] { + const result = tokenize('', currentState, currentBuffer, true, x10Mouse) + currentState = result.state.state + currentBuffer = result.state.buffer + return result.tokens + }, + + reset(): void { + currentState = 'ground' + currentBuffer = '' + }, + + buffer(): string { + return currentBuffer + }, + } +} + +type InternalState = { + state: State + buffer: string +} + +function tokenize( + input: string, + initialState: State, + initialBuffer: string, + flush: boolean, + x10Mouse: boolean, +): { tokens: Token[]; state: InternalState } { + const tokens: Token[] = [] + const result: InternalState = { + state: initialState, + buffer: '', + } + + const data = initialBuffer + input + let i = 0 + let textStart = 0 + let seqStart = 0 + + const flushText = (): void => { + if (i > textStart) { + const text = data.slice(textStart, i) + if (text) { + tokens.push({ type: 'text', value: text }) + } + } + textStart = i + } + + const emitSequence = (seq: string): void => { + if (seq) { + tokens.push({ type: 'sequence', value: seq }) + } + result.state = 'ground' + textStart = i + } + + while (i < data.length) { + const code = data.charCodeAt(i) + + switch (result.state) { + case 'ground': + if (code === C0.ESC) { + flushText() + seqStart = i + result.state = 'escape' + i++ + } else { + i++ + } + break + + case 'escape': + if (code === ESC_TYPE.CSI) { + result.state = 'csi' + i++ + } else if (code === ESC_TYPE.OSC) { + result.state = 'osc' + i++ + } else if (code === ESC_TYPE.DCS) { + result.state = 'dcs' + i++ + } else if (code === ESC_TYPE.APC) { + result.state = 'apc' + i++ + } else if (code === 0x4f) { + // 'O' - SS3 + result.state = 'ss3' + i++ + } else if (isCSIIntermediate(code)) { + // Intermediate byte (e.g., ESC ( for charset) - continue buffering + result.state = 'escapeIntermediate' + i++ + } else if (isEscFinal(code)) { + // Two-character escape sequence + i++ + emitSequence(data.slice(seqStart, i)) + } else if (code === C0.ESC) { + // Double escape - emit first, start new + emitSequence(data.slice(seqStart, i)) + seqStart = i + result.state = 'escape' + i++ + } else { + // Invalid - treat ESC as text + result.state = 'ground' + textStart = seqStart + } + break + + case 'escapeIntermediate': + // After intermediate byte(s), wait for final byte + if (isCSIIntermediate(code)) { + // More intermediate bytes + i++ + } else if (isEscFinal(code)) { + // Final byte - complete the sequence + i++ + emitSequence(data.slice(seqStart, i)) + } else { + // Invalid - treat as text + result.state = 'ground' + textStart = seqStart + } + break + + case 'csi': + // X10 mouse: CSI M + 3 raw payload bytes (Cb+32, Cx+32, Cy+32). + // M immediately after [ (offset 2) means no params — SGR mouse + // (CSI < … M) has a `<` param byte first and reaches M at offset > 2. + // Terminals that ignore DECSET 1006 but honor 1000/1002 emit this + // legacy encoding; without this branch the 3 payload bytes leak + // through as text (`` `rK `` / `arK` garbage in the prompt). + // + // Gated on x10Mouse — `\x1b[M` is also CSI DL (Delete Lines) and + // blindly consuming 3 chars corrupts output rendering (Parser/Ansi) + // and fragments bracketed-paste PASTE_END. Only stdin enables this. + // The ≥0x20 check on each payload slot is belt-and-suspenders: X10 + // guarantees Cb≥32, Cx≥33, Cy≥33, so a control byte (ESC=0x1B) in + // any slot means this is CSI DL adjacent to another sequence, not a + // mouse event. Checking all three slots prevents PASTE_END's ESC + // from being consumed when paste content ends in `\x1b[M`+0-2 chars. + // + // Known limitation: this counts JS string chars, but X10 is byte- + // oriented and stdin uses utf8 encoding (App.tsx). At col 162-191 × + // row 96-159 the two coord bytes (0xC2-0xDF, 0x80-0xBF) form a valid + // UTF-8 2-byte sequence and collapse to one char — the length check + // fails and the event buffers until the next keypress absorbs it. + // Fixing this requires latin1 stdin; X10's 223-coord cap is exactly + // why SGR was invented, and no-SGR terminals at 162+ cols are rare. + if ( + x10Mouse && + code === 0x4d /* M */ && + i - seqStart === 2 && + (i + 1 >= data.length || data.charCodeAt(i + 1) >= 0x20) && + (i + 2 >= data.length || data.charCodeAt(i + 2) >= 0x20) && + (i + 3 >= data.length || data.charCodeAt(i + 3) >= 0x20) + ) { + if (i + 4 <= data.length) { + i += 4 + emitSequence(data.slice(seqStart, i)) + } else { + // Incomplete — exit loop; end-of-input buffers from seqStart. + // Re-entry re-tokenizes from ground via the invalid-CSI fallthrough. + i = data.length + } + break + } + if (isCSIFinal(code)) { + i++ + emitSequence(data.slice(seqStart, i)) + } else if (isCSIParam(code) || isCSIIntermediate(code)) { + i++ + } else { + // Invalid CSI - abort, treat as text + result.state = 'ground' + textStart = seqStart + } + break + + case 'ss3': + // SS3 sequences: ESC O followed by a single final byte + if (code >= 0x40 && code <= 0x7e) { + i++ + emitSequence(data.slice(seqStart, i)) + } else { + // Invalid - treat as text + result.state = 'ground' + textStart = seqStart + } + break + + case 'osc': + if (code === C0.BEL) { + i++ + emitSequence(data.slice(seqStart, i)) + } else if ( + code === C0.ESC && + i + 1 < data.length && + data.charCodeAt(i + 1) === ESC_TYPE.ST + ) { + i += 2 + emitSequence(data.slice(seqStart, i)) + } else { + i++ + } + break + + case 'dcs': + case 'apc': + if (code === C0.BEL) { + i++ + emitSequence(data.slice(seqStart, i)) + } else if ( + code === C0.ESC && + i + 1 < data.length && + data.charCodeAt(i + 1) === ESC_TYPE.ST + ) { + i += 2 + emitSequence(data.slice(seqStart, i)) + } else { + i++ + } + break + } + } + + // Handle end of input + if (result.state === 'ground') { + flushText() + } else if (flush) { + // Force output incomplete sequence + const remaining = data.slice(seqStart) + if (remaining) tokens.push({ type: 'sequence', value: remaining }) + result.state = 'ground' + } else { + // Buffer incomplete sequence for next call + result.buffer = data.slice(seqStart) + } + + return { tokens, state: result } +} diff --git a/ink/termio/types.ts b/ink/termio/types.ts new file mode 100644 index 0000000..6c9bf73 --- /dev/null +++ b/ink/termio/types.ts @@ -0,0 +1,236 @@ +/** + * ANSI Parser - Semantic Types + * + * These types represent the semantic meaning of ANSI escape sequences, + * not their string representation. Inspired by ghostty's action-based design. + */ + +// ============================================================================= +// Colors +// ============================================================================= + +/** Named colors from the 16-color palette */ +export type NamedColor = + | 'black' + | 'red' + | 'green' + | 'yellow' + | 'blue' + | 'magenta' + | 'cyan' + | 'white' + | 'brightBlack' + | 'brightRed' + | 'brightGreen' + | 'brightYellow' + | 'brightBlue' + | 'brightMagenta' + | 'brightCyan' + | 'brightWhite' + +/** Color specification - can be named, indexed (256), or RGB */ +export type Color = + | { type: 'named'; name: NamedColor } + | { type: 'indexed'; index: number } // 0-255 + | { type: 'rgb'; r: number; g: number; b: number } + | { type: 'default' } + +// ============================================================================= +// Text Styles +// ============================================================================= + +/** Underline style variants */ +export type UnderlineStyle = + | 'none' + | 'single' + | 'double' + | 'curly' + | 'dotted' + | 'dashed' + +/** Text style attributes - represents current styling state */ +export type TextStyle = { + bold: boolean + dim: boolean + italic: boolean + underline: UnderlineStyle + blink: boolean + inverse: boolean + hidden: boolean + strikethrough: boolean + overline: boolean + fg: Color + bg: Color + underlineColor: Color +} + +/** Create a default (reset) text style */ +export function defaultStyle(): TextStyle { + return { + bold: false, + dim: false, + italic: false, + underline: 'none', + blink: false, + inverse: false, + hidden: false, + strikethrough: false, + overline: false, + fg: { type: 'default' }, + bg: { type: 'default' }, + underlineColor: { type: 'default' }, + } +} + +/** Check if two styles are equal */ +export function stylesEqual(a: TextStyle, b: TextStyle): boolean { + return ( + a.bold === b.bold && + a.dim === b.dim && + a.italic === b.italic && + a.underline === b.underline && + a.blink === b.blink && + a.inverse === b.inverse && + a.hidden === b.hidden && + a.strikethrough === b.strikethrough && + a.overline === b.overline && + colorsEqual(a.fg, b.fg) && + colorsEqual(a.bg, b.bg) && + colorsEqual(a.underlineColor, b.underlineColor) + ) +} + +/** Check if two colors are equal */ +export function colorsEqual(a: Color, b: Color): boolean { + if (a.type !== b.type) return false + switch (a.type) { + case 'named': + return a.name === (b as typeof a).name + case 'indexed': + return a.index === (b as typeof a).index + case 'rgb': + return ( + a.r === (b as typeof a).r && + a.g === (b as typeof a).g && + a.b === (b as typeof a).b + ) + case 'default': + return true + } +} + +// ============================================================================= +// Cursor Actions +// ============================================================================= + +export type CursorDirection = 'up' | 'down' | 'forward' | 'back' + +export type CursorAction = + | { type: 'move'; direction: CursorDirection; count: number } + | { type: 'position'; row: number; col: number } + | { type: 'column'; col: number } + | { type: 'row'; row: number } + | { type: 'save' } + | { type: 'restore' } + | { type: 'show' } + | { type: 'hide' } + | { + type: 'style' + style: 'block' | 'underline' | 'bar' + blinking: boolean + } + | { type: 'nextLine'; count: number } + | { type: 'prevLine'; count: number } + +// ============================================================================= +// Erase Actions +// ============================================================================= + +export type EraseAction = + | { type: 'display'; region: 'toEnd' | 'toStart' | 'all' | 'scrollback' } + | { type: 'line'; region: 'toEnd' | 'toStart' | 'all' } + | { type: 'chars'; count: number } + +// ============================================================================= +// Scroll Actions +// ============================================================================= + +export type ScrollAction = + | { type: 'up'; count: number } + | { type: 'down'; count: number } + | { type: 'setRegion'; top: number; bottom: number } + +// ============================================================================= +// Mode Actions +// ============================================================================= + +export type ModeAction = + | { type: 'alternateScreen'; enabled: boolean } + | { type: 'bracketedPaste'; enabled: boolean } + | { type: 'mouseTracking'; mode: 'off' | 'normal' | 'button' | 'any' } + | { type: 'focusEvents'; enabled: boolean } + +// ============================================================================= +// Link Actions (OSC 8) +// ============================================================================= + +export type LinkAction = + | { type: 'start'; url: string; params?: Record } + | { type: 'end' } + +// ============================================================================= +// Title Actions (OSC 0/1/2) +// ============================================================================= + +export type TitleAction = + | { type: 'windowTitle'; title: string } + | { type: 'iconName'; name: string } + | { type: 'both'; title: string } + +// ============================================================================= +// Tab Status Action (OSC 21337) +// ============================================================================= + +/** + * Per-tab chrome metadata. Tristate for each field: + * - property absent → not mentioned in sequence, no change + * - null → explicitly cleared (bare key or key= with empty value) + * - value → set to this + */ +export type TabStatusAction = { + indicator?: Color | null + status?: string | null + statusColor?: Color | null +} + +// ============================================================================= +// Parsed Segments - The output of the parser +// ============================================================================= + +/** A segment of styled text */ +export type TextSegment = { + type: 'text' + text: string + style: TextStyle +} + +/** A grapheme (visual character unit) with width info */ +export type Grapheme = { + value: string + width: 1 | 2 // Display width in columns +} + +/** All possible parsed actions */ +export type Action = + | { type: 'text'; graphemes: Grapheme[]; style: TextStyle } + | { type: 'cursor'; action: CursorAction } + | { type: 'erase'; action: EraseAction } + | { type: 'scroll'; action: ScrollAction } + | { type: 'mode'; action: ModeAction } + | { type: 'link'; action: LinkAction } + | { type: 'title'; action: TitleAction } + | { type: 'tabStatus'; action: TabStatusAction } + | { type: 'sgr'; params: string } // Select Graphic Rendition (style change) + | { type: 'bell' } + | { type: 'reset' } // Full terminal reset (ESC c) + | { type: 'unknown'; sequence: string } // Unrecognized sequence diff --git a/ink/useTerminalNotification.ts b/ink/useTerminalNotification.ts new file mode 100644 index 0000000..90e53eb --- /dev/null +++ b/ink/useTerminalNotification.ts @@ -0,0 +1,126 @@ +import { createContext, useCallback, useContext, useMemo } from 'react' +import { isProgressReportingAvailable, type Progress } from './terminal.js' +import { BEL } from './termio/ansi.js' +import { ITERM2, OSC, osc, PROGRESS, wrapForMultiplexer } from './termio/osc.js' + +type WriteRaw = (data: string) => void + +export const TerminalWriteContext = createContext(null) + +export const TerminalWriteProvider = TerminalWriteContext.Provider + +export type TerminalNotification = { + notifyITerm2: (opts: { message: string; title?: string }) => void + notifyKitty: (opts: { message: string; title: string; id: number }) => void + notifyGhostty: (opts: { message: string; title: string }) => void + notifyBell: () => void + /** + * Report progress to the terminal via OSC 9;4 sequences. + * Supported terminals: ConEmu, Ghostty 1.2.0+, iTerm2 3.6.6+ + * Pass state=null to clear progress. + */ + progress: (state: Progress['state'] | null, percentage?: number) => void +} + +export function useTerminalNotification(): TerminalNotification { + const writeRaw = useContext(TerminalWriteContext) + if (!writeRaw) { + throw new Error( + 'useTerminalNotification must be used within TerminalWriteProvider', + ) + } + + const notifyITerm2 = useCallback( + ({ message, title }: { message: string; title?: string }) => { + const displayString = title ? `${title}:\n${message}` : message + writeRaw(wrapForMultiplexer(osc(OSC.ITERM2, `\n\n${displayString}`))) + }, + [writeRaw], + ) + + const notifyKitty = useCallback( + ({ + message, + title, + id, + }: { + message: string + title: string + id: number + }) => { + writeRaw(wrapForMultiplexer(osc(OSC.KITTY, `i=${id}:d=0:p=title`, title))) + writeRaw(wrapForMultiplexer(osc(OSC.KITTY, `i=${id}:p=body`, message))) + writeRaw(wrapForMultiplexer(osc(OSC.KITTY, `i=${id}:d=1:a=focus`, ''))) + }, + [writeRaw], + ) + + const notifyGhostty = useCallback( + ({ message, title }: { message: string; title: string }) => { + writeRaw(wrapForMultiplexer(osc(OSC.GHOSTTY, 'notify', title, message))) + }, + [writeRaw], + ) + + const notifyBell = useCallback(() => { + // Raw BEL — inside tmux this triggers tmux's bell-action (window flag). + // Wrapping would make it opaque DCS payload and lose that fallback. + writeRaw(BEL) + }, [writeRaw]) + + const progress = useCallback( + (state: Progress['state'] | null, percentage?: number) => { + if (!isProgressReportingAvailable()) { + return + } + if (!state) { + writeRaw( + wrapForMultiplexer( + osc(OSC.ITERM2, ITERM2.PROGRESS, PROGRESS.CLEAR, ''), + ), + ) + return + } + const pct = Math.max(0, Math.min(100, Math.round(percentage ?? 0))) + switch (state) { + case 'completed': + writeRaw( + wrapForMultiplexer( + osc(OSC.ITERM2, ITERM2.PROGRESS, PROGRESS.CLEAR, ''), + ), + ) + break + case 'error': + writeRaw( + wrapForMultiplexer( + osc(OSC.ITERM2, ITERM2.PROGRESS, PROGRESS.ERROR, pct), + ), + ) + break + case 'indeterminate': + writeRaw( + wrapForMultiplexer( + osc(OSC.ITERM2, ITERM2.PROGRESS, PROGRESS.INDETERMINATE, ''), + ), + ) + break + case 'running': + writeRaw( + wrapForMultiplexer( + osc(OSC.ITERM2, ITERM2.PROGRESS, PROGRESS.SET, pct), + ), + ) + break + case null: + // Handled by the if guard above + break + } + }, + [writeRaw], + ) + + return useMemo( + () => ({ notifyITerm2, notifyKitty, notifyGhostty, notifyBell, progress }), + [notifyITerm2, notifyKitty, notifyGhostty, notifyBell, progress], + ) +} diff --git a/ink/warn.ts b/ink/warn.ts new file mode 100644 index 0000000..98e6bce --- /dev/null +++ b/ink/warn.ts @@ -0,0 +1,9 @@ +import { logForDebugging } from '../utils/debug.js' + +export function ifNotInteger(value: number | undefined, name: string): void { + if (value === undefined) return + if (Number.isInteger(value)) return + logForDebugging(`${name} should be an integer, got ${value}`, { + level: 'warn', + }) +} diff --git a/ink/widest-line.ts b/ink/widest-line.ts new file mode 100644 index 0000000..80091e4 --- /dev/null +++ b/ink/widest-line.ts @@ -0,0 +1,19 @@ +import { lineWidth } from './line-width-cache.js' + +export function widestLine(string: string): number { + let maxWidth = 0 + let start = 0 + + while (start <= string.length) { + const end = string.indexOf('\n', start) + const line = + end === -1 ? string.substring(start) : string.substring(start, end) + + maxWidth = Math.max(maxWidth, lineWidth(line)) + + if (end === -1) break + start = end + 1 + } + + return maxWidth +} diff --git a/ink/wrap-text.ts b/ink/wrap-text.ts new file mode 100644 index 0000000..434412c --- /dev/null +++ b/ink/wrap-text.ts @@ -0,0 +1,74 @@ +import sliceAnsi from '../utils/sliceAnsi.js' +import { stringWidth } from './stringWidth.js' +import type { Styles } from './styles.js' +import { wrapAnsi } from './wrapAnsi.js' + +const ELLIPSIS = '…' + +// sliceAnsi may include a boundary-spanning wide char (e.g. CJK at position +// end-1 with width 2 overshoots by 1). Retry with a tighter bound once. +function sliceFit(text: string, start: number, end: number): string { + const s = sliceAnsi(text, start, end) + return stringWidth(s) > end - start ? sliceAnsi(text, start, end - 1) : s +} + +function truncate( + text: string, + columns: number, + position: 'start' | 'middle' | 'end', +): string { + if (columns < 1) return '' + if (columns === 1) return ELLIPSIS + + const length = stringWidth(text) + if (length <= columns) return text + + if (position === 'start') { + return ELLIPSIS + sliceFit(text, length - columns + 1, length) + } + if (position === 'middle') { + const half = Math.floor(columns / 2) + return ( + sliceFit(text, 0, half) + + ELLIPSIS + + sliceFit(text, length - (columns - half) + 1, length) + ) + } + return sliceFit(text, 0, columns - 1) + ELLIPSIS +} + +export default function wrapText( + text: string, + maxWidth: number, + wrapType: Styles['textWrap'], +): string { + if (wrapType === 'wrap') { + return wrapAnsi(text, maxWidth, { + trim: false, + hard: true, + }) + } + + if (wrapType === 'wrap-trim') { + return wrapAnsi(text, maxWidth, { + trim: true, + hard: true, + }) + } + + if (wrapType!.startsWith('truncate')) { + let position: 'end' | 'middle' | 'start' = 'end' + + if (wrapType === 'truncate-middle') { + position = 'middle' + } + + if (wrapType === 'truncate-start') { + position = 'start' + } + + return truncate(text, maxWidth, position) + } + + return text +} diff --git a/ink/wrapAnsi.ts b/ink/wrapAnsi.ts new file mode 100644 index 0000000..eff436b --- /dev/null +++ b/ink/wrapAnsi.ts @@ -0,0 +1,20 @@ +import wrapAnsiNpm from 'wrap-ansi' + +type WrapAnsiOptions = { + hard?: boolean + wordWrap?: boolean + trim?: boolean +} + +const wrapAnsiBun = + typeof Bun !== 'undefined' && typeof Bun.wrapAnsi === 'function' + ? Bun.wrapAnsi + : null + +const wrapAnsi: ( + input: string, + columns: number, + options?: WrapAnsiOptions, +) => string = wrapAnsiBun ?? wrapAnsiNpm + +export { wrapAnsi } diff --git a/interactiveHelpers.tsx b/interactiveHelpers.tsx new file mode 100644 index 0000000..b88dbbf --- /dev/null +++ b/interactiveHelpers.tsx @@ -0,0 +1,366 @@ +import { feature } from 'bun:bundle'; +import { appendFileSync } from 'fs'; +import React from 'react'; +import { logEvent } from 'src/services/analytics/index.js'; +import { gracefulShutdown, gracefulShutdownSync } from 'src/utils/gracefulShutdown.js'; +import { type ChannelEntry, getAllowedChannels, setAllowedChannels, setHasDevChannels, setSessionTrustAccepted, setStatsStore } from './bootstrap/state.js'; +import type { Command } from './commands.js'; +import { createStatsStore, type StatsStore } from './context/stats.js'; +import { getSystemContext } from './context.js'; +import { initializeTelemetryAfterTrust } from './entrypoints/init.js'; +import { isSynchronizedOutputSupported } from './ink/terminal.js'; +import type { RenderOptions, Root, TextProps } from './ink.js'; +import { KeybindingSetup } from './keybindings/KeybindingProviderSetup.js'; +import { startDeferredPrefetches } from './main.js'; +import { checkGate_CACHED_OR_BLOCKING, initializeGrowthBook, resetGrowthBook } from './services/analytics/growthbook.js'; +import { isQualifiedForGrove } from './services/api/grove.js'; +import { handleMcpjsonServerApprovals } from './services/mcpServerApproval.js'; +import { AppStateProvider } from './state/AppState.js'; +import { onChangeAppState } from './state/onChangeAppState.js'; +import { normalizeApiKeyForConfig } from './utils/authPortable.js'; +import { getExternalClaudeMdIncludes, getMemoryFiles, shouldShowClaudeMdExternalIncludesWarning } from './utils/claudemd.js'; +import { checkHasTrustDialogAccepted, getCustomApiKeyStatus, getGlobalConfig, saveGlobalConfig } from './utils/config.js'; +import { updateDeepLinkTerminalPreference } from './utils/deepLink/terminalPreference.js'; +import { isEnvTruthy, isRunningOnHomespace } from './utils/envUtils.js'; +import { type FpsMetrics, FpsTracker } from './utils/fpsTracker.js'; +import { updateGithubRepoPathMapping } from './utils/githubRepoPathMapping.js'; +import { applyConfigEnvironmentVariables } from './utils/managedEnv.js'; +import type { PermissionMode } from './utils/permissions/PermissionMode.js'; +import { getBaseRenderOptions } from './utils/renderOptions.js'; +import { getSettingsWithAllErrors } from './utils/settings/allErrors.js'; +import { hasAutoModeOptIn, hasSkipDangerousModePermissionPrompt } from './utils/settings/settings.js'; +export function completeOnboarding(): void { + saveGlobalConfig(current => ({ + ...current, + hasCompletedOnboarding: true, + lastOnboardingVersion: MACRO.VERSION + })); +} +export function showDialog(root: Root, renderer: (done: (result: T) => void) => React.ReactNode): Promise { + return new Promise(resolve => { + const done = (result: T): void => void resolve(result); + root.render(renderer(done)); + }); +} + +/** + * Render an error message through Ink, then unmount and exit. + * Use this for fatal errors after the Ink root has been created — + * console.error is swallowed by Ink's patchConsole, so we render + * through the React tree instead. + */ +export async function exitWithError(root: Root, message: string, beforeExit?: () => Promise): Promise { + return exitWithMessage(root, message, { + color: 'error', + beforeExit + }); +} + +/** + * Render a message through Ink, then unmount and exit. + * Use this for messages after the Ink root has been created — + * console output is swallowed by Ink's patchConsole, so we render + * through the React tree instead. + */ +export async function exitWithMessage(root: Root, message: string, options?: { + color?: TextProps['color']; + exitCode?: number; + beforeExit?: () => Promise; +}): Promise { + const { + Text + } = await import('./ink.js'); + const color = options?.color; + const exitCode = options?.exitCode ?? 1; + root.render(color ? {message} : {message}); + root.unmount(); + await options?.beforeExit?.(); + // eslint-disable-next-line custom-rules/no-process-exit -- exit after Ink unmount + process.exit(exitCode); +} + +/** + * Show a setup dialog wrapped in AppStateProvider + KeybindingSetup. + * Reduces boilerplate in showSetupScreens() where every dialog needs these wrappers. + */ +export function showSetupDialog(root: Root, renderer: (done: (result: T) => void) => React.ReactNode, options?: { + onChangeAppState?: typeof onChangeAppState; +}): Promise { + return showDialog(root, done => + {renderer(done)} + ); +} + +/** + * Render the main UI into the root and wait for it to exit. + * Handles the common epilogue: start deferred prefetches, wait for exit, graceful shutdown. + */ +export async function renderAndRun(root: Root, element: React.ReactNode): Promise { + root.render(element); + startDeferredPrefetches(); + await root.waitUntilExit(); + await gracefulShutdown(0); +} +export async function showSetupScreens(root: Root, permissionMode: PermissionMode, allowDangerouslySkipPermissions: boolean, commands?: Command[], claudeInChrome?: boolean, devChannels?: ChannelEntry[]): Promise { + if ("production" === 'test' || isEnvTruthy(false) || process.env.IS_DEMO // Skip onboarding in demo mode + ) { + return false; + } + const config = getGlobalConfig(); + let onboardingShown = false; + if (!config.theme || !config.hasCompletedOnboarding // always show onboarding at least once + ) { + onboardingShown = true; + const { + Onboarding + } = await import('./components/Onboarding.js'); + await showSetupDialog(root, done => { + completeOnboarding(); + void done(); + }} />, { + onChangeAppState + }); + } + + // Always show the trust dialog in interactive sessions, regardless of permission mode. + // The trust dialog is the workspace trust boundary — it warns about untrusted repos + // and checks CLAUDE.md external includes. bypassPermissions mode + // only affects tool execution permissions, not workspace trust. + // Note: non-interactive sessions (CI/CD with -p) never reach showSetupScreens at all. + // Skip permission checks in claubbit + if (!isEnvTruthy(process.env.CLAUBBIT)) { + // Fast-path: skip TrustDialog import+render when CWD is already trusted. + // If it returns true, the TrustDialog would auto-resolve regardless of + // security features, so we can skip the dynamic import and render cycle. + if (!checkHasTrustDialogAccepted()) { + const { + TrustDialog + } = await import('./components/TrustDialog/TrustDialog.js'); + await showSetupDialog(root, done => ); + } + + // Signal that trust has been verified for this session. + // GrowthBook checks this to decide whether to include auth headers. + setSessionTrustAccepted(true); + + // Reset and reinitialize GrowthBook after trust is established. + // Defense for login/logout: clears any prior client so the next init + // picks up fresh auth headers. + resetGrowthBook(); + void initializeGrowthBook(); + + // Now that trust is established, prefetch system context if it wasn't already + void getSystemContext(); + + // If settings are valid, check for any mcp.json servers that need approval + const { + errors: allErrors + } = getSettingsWithAllErrors(); + if (allErrors.length === 0) { + await handleMcpjsonServerApprovals(root); + } + + // Check for claude.md includes that need approval + if (await shouldShowClaudeMdExternalIncludesWarning()) { + const externalIncludes = getExternalClaudeMdIncludes(await getMemoryFiles(true)); + const { + ClaudeMdExternalIncludesDialog + } = await import('./components/ClaudeMdExternalIncludesDialog.js'); + await showSetupDialog(root, done => ); + } + } + + // Track current repo path for teleport directory switching (fire-and-forget) + // This must happen AFTER trust to prevent untrusted directories from poisoning the mapping + void updateGithubRepoPathMapping(); + if (feature('LODESTONE')) { + updateDeepLinkTerminalPreference(); + } + + // Apply full environment variables after trust dialog is accepted OR in bypass mode + // In bypass mode (CI/CD, automation), we trust the environment so apply all variables + // In normal mode, this happens after the trust dialog is accepted + // This includes potentially dangerous environment variables from untrusted sources + applyConfigEnvironmentVariables(); + + // Initialize telemetry after env vars are applied so OTEL endpoint env vars and + // otelHeadersHelper (which requires trust to execute) are available. + // Defer to next tick so the OTel dynamic import resolves after first render + // instead of during the pre-render microtask queue. + setImmediate(() => initializeTelemetryAfterTrust()); + if (await isQualifiedForGrove()) { + const { + GroveDialog + } = await import('src/components/grove/Grove.js'); + const decision = await showSetupDialog(root, done => ); + if (decision === 'escape') { + logEvent('tengu_grove_policy_exited', {}); + gracefulShutdownSync(0); + return false; + } + } + + // Check for custom API key + // On homespace, ANTHROPIC_API_KEY is preserved in process.env for child + // processes but ignored by Claude Code itself (see auth.ts). + if (process.env.ANTHROPIC_API_KEY && !isRunningOnHomespace()) { + const customApiKeyTruncated = normalizeApiKeyForConfig(process.env.ANTHROPIC_API_KEY); + const keyStatus = getCustomApiKeyStatus(customApiKeyTruncated); + if (keyStatus === 'new') { + const { + ApproveApiKey + } = await import('./components/ApproveApiKey.js'); + await showSetupDialog(root, done => , { + onChangeAppState + }); + } + } + if ((permissionMode === 'bypassPermissions' || allowDangerouslySkipPermissions) && !hasSkipDangerousModePermissionPrompt()) { + const { + BypassPermissionsModeDialog + } = await import('./components/BypassPermissionsModeDialog.js'); + await showSetupDialog(root, done => ); + } + if (feature('TRANSCRIPT_CLASSIFIER')) { + // Only show the opt-in dialog if auto mode actually resolved — if the + // gate denied it (org not allowlisted, settings disabled), showing + // consent for an unavailable feature is pointless. The + // verifyAutoModeGateAccess notification will explain why instead. + if (permissionMode === 'auto' && !hasAutoModeOptIn()) { + const { + AutoModeOptInDialog + } = await import('./components/AutoModeOptInDialog.js'); + await showSetupDialog(root, done => gracefulShutdownSync(1)} declineExits />); + } + } + + // --dangerously-load-development-channels confirmation. On accept, append + // dev channels to any --channels list already set in main.tsx. Org policy + // is NOT bypassed — gateChannelServer() still runs; this flag only exists + // to sidestep the --channels approved-server allowlist. + if (feature('KAIROS') || feature('KAIROS_CHANNELS')) { + // gateChannelServer and ChannelsNotice read tengu_harbor after this + // function returns. A cold disk cache (fresh install, or first run after + // the flag was added server-side) defaults to false and silently drops + // channel notifications for the whole session — gh#37026. + // checkGate_CACHED_OR_BLOCKING returns immediately if disk already says + // true; only blocks on a cold/stale-false cache (awaits the same memoized + // initializeGrowthBook promise fired earlier). Also warms the + // isChannelsEnabled() check in the dev-channels dialog below. + if (getAllowedChannels().length > 0 || (devChannels?.length ?? 0) > 0) { + await checkGate_CACHED_OR_BLOCKING('tengu_harbor'); + } + if (devChannels && devChannels.length > 0) { + const [{ + isChannelsEnabled + }, { + getClaudeAIOAuthTokens + }] = await Promise.all([import('./services/mcp/channelAllowlist.js'), import('./utils/auth.js')]); + // Skip the dialog when channels are blocked (tengu_harbor off or no + // OAuth) — accepting then immediately seeing "not available" in + // ChannelsNotice is worse than no dialog. Append entries anyway so + // ChannelsNotice renders the blocked branch with the dev entries + // named. dev:true here is for the flag label in ChannelsNotice + // (hasNonDev check); the allowlist bypass it also grants is moot + // since the gate blocks upstream. + if (!isChannelsEnabled() || !getClaudeAIOAuthTokens()?.accessToken) { + setAllowedChannels([...getAllowedChannels(), ...devChannels.map(c => ({ + ...c, + dev: true + }))]); + setHasDevChannels(true); + } else { + const { + DevChannelsDialog + } = await import('./components/DevChannelsDialog.js'); + await showSetupDialog(root, done => { + // Mark dev entries per-entry so the allowlist bypass doesn't leak + // to --channels entries when both flags are passed. + setAllowedChannels([...getAllowedChannels(), ...devChannels.map(c => ({ + ...c, + dev: true + }))]); + setHasDevChannels(true); + void done(); + }} />); + } + } + } + + // Show Chrome onboarding for first-time Claude in Chrome users + if (claudeInChrome && !getGlobalConfig().hasCompletedClaudeInChromeOnboarding) { + const { + ClaudeInChromeOnboarding + } = await import('./components/ClaudeInChromeOnboarding.js'); + await showSetupDialog(root, done => ); + } + return onboardingShown; +} +export function getRenderContext(exitOnCtrlC: boolean): { + renderOptions: RenderOptions; + getFpsMetrics: () => FpsMetrics | undefined; + stats: StatsStore; +} { + let lastFlickerTime = 0; + const baseOptions = getBaseRenderOptions(exitOnCtrlC); + + // Log analytics event when stdin override is active + if (baseOptions.stdin) { + logEvent('tengu_stdin_interactive', {}); + } + const fpsTracker = new FpsTracker(); + const stats = createStatsStore(); + setStatsStore(stats); + + // Bench mode: when set, append per-frame phase timings as JSONL for + // offline analysis by bench/repl-scroll.ts. Captures the full TUI + // render pipeline (yoga → screen buffer → diff → optimize → stdout) + // so perf work on any phase can be validated against real user flows. + const frameTimingLogPath = process.env.CLAUDE_CODE_FRAME_TIMING_LOG; + return { + getFpsMetrics: () => fpsTracker.getMetrics(), + stats, + renderOptions: { + ...baseOptions, + onFrame: event => { + fpsTracker.record(event.durationMs); + stats.observe('frame_duration_ms', event.durationMs); + if (frameTimingLogPath && event.phases) { + // Bench-only env-var-gated path: sync write so no frames dropped + // on abrupt exit. ~100 bytes at ≤60fps is negligible. rss/cpu are + // single syscalls; cpu is cumulative — bench side computes delta. + const line = + // eslint-disable-next-line custom-rules/no-direct-json-operations -- tiny object, hot bench path + JSON.stringify({ + total: event.durationMs, + ...event.phases, + rss: process.memoryUsage.rss(), + cpu: process.cpuUsage() + }) + '\n'; + // eslint-disable-next-line custom-rules/no-sync-fs -- bench-only, sync so no frames dropped on exit + appendFileSync(frameTimingLogPath, line); + } + // Skip flicker reporting for terminals with synchronized output — + // DEC 2026 buffers between BSU/ESU so clear+redraw is atomic. + if (isSynchronizedOutputSupported()) { + return; + } + for (const flicker of event.flickers) { + if (flicker.reason === 'resize') { + continue; + } + const now = Date.now(); + if (now - lastFlickerTime < 1000) { + logEvent('tengu_flicker', { + desiredHeight: flicker.desiredHeight, + actualHeight: flicker.availableHeight, + reason: flicker.reason + } as unknown as Record); + } + lastFlickerTime = now; + } + } + } + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiYXBwZW5kRmlsZVN5bmMiLCJSZWFjdCIsImxvZ0V2ZW50IiwiZ3JhY2VmdWxTaHV0ZG93biIsImdyYWNlZnVsU2h1dGRvd25TeW5jIiwiQ2hhbm5lbEVudHJ5IiwiZ2V0QWxsb3dlZENoYW5uZWxzIiwic2V0QWxsb3dlZENoYW5uZWxzIiwic2V0SGFzRGV2Q2hhbm5lbHMiLCJzZXRTZXNzaW9uVHJ1c3RBY2NlcHRlZCIsInNldFN0YXRzU3RvcmUiLCJDb21tYW5kIiwiY3JlYXRlU3RhdHNTdG9yZSIsIlN0YXRzU3RvcmUiLCJnZXRTeXN0ZW1Db250ZXh0IiwiaW5pdGlhbGl6ZVRlbGVtZXRyeUFmdGVyVHJ1c3QiLCJpc1N5bmNocm9uaXplZE91dHB1dFN1cHBvcnRlZCIsIlJlbmRlck9wdGlvbnMiLCJSb290IiwiVGV4dFByb3BzIiwiS2V5YmluZGluZ1NldHVwIiwic3RhcnREZWZlcnJlZFByZWZldGNoZXMiLCJjaGVja0dhdGVfQ0FDSEVEX09SX0JMT0NLSU5HIiwiaW5pdGlhbGl6ZUdyb3d0aEJvb2siLCJyZXNldEdyb3d0aEJvb2siLCJpc1F1YWxpZmllZEZvckdyb3ZlIiwiaGFuZGxlTWNwanNvblNlcnZlckFwcHJvdmFscyIsIkFwcFN0YXRlUHJvdmlkZXIiLCJvbkNoYW5nZUFwcFN0YXRlIiwibm9ybWFsaXplQXBpS2V5Rm9yQ29uZmlnIiwiZ2V0RXh0ZXJuYWxDbGF1ZGVNZEluY2x1ZGVzIiwiZ2V0TWVtb3J5RmlsZXMiLCJzaG91bGRTaG93Q2xhdWRlTWRFeHRlcm5hbEluY2x1ZGVzV2FybmluZyIsImNoZWNrSGFzVHJ1c3REaWFsb2dBY2NlcHRlZCIsImdldEN1c3RvbUFwaUtleVN0YXR1cyIsImdldEdsb2JhbENvbmZpZyIsInNhdmVHbG9iYWxDb25maWciLCJ1cGRhdGVEZWVwTGlua1Rlcm1pbmFsUHJlZmVyZW5jZSIsImlzRW52VHJ1dGh5IiwiaXNSdW5uaW5nT25Ib21lc3BhY2UiLCJGcHNNZXRyaWNzIiwiRnBzVHJhY2tlciIsInVwZGF0ZUdpdGh1YlJlcG9QYXRoTWFwcGluZyIsImFwcGx5Q29uZmlnRW52aXJvbm1lbnRWYXJpYWJsZXMiLCJQZXJtaXNzaW9uTW9kZSIsImdldEJhc2VSZW5kZXJPcHRpb25zIiwiZ2V0U2V0dGluZ3NXaXRoQWxsRXJyb3JzIiwiaGFzQXV0b01vZGVPcHRJbiIsImhhc1NraXBEYW5nZXJvdXNNb2RlUGVybWlzc2lvblByb21wdCIsImNvbXBsZXRlT25ib2FyZGluZyIsImN1cnJlbnQiLCJoYXNDb21wbGV0ZWRPbmJvYXJkaW5nIiwibGFzdE9uYm9hcmRpbmdWZXJzaW9uIiwiTUFDUk8iLCJWRVJTSU9OIiwic2hvd0RpYWxvZyIsInJvb3QiLCJyZW5kZXJlciIsImRvbmUiLCJyZXN1bHQiLCJUIiwiUmVhY3ROb2RlIiwiUHJvbWlzZSIsInJlc29sdmUiLCJyZW5kZXIiLCJleGl0V2l0aEVycm9yIiwibWVzc2FnZSIsImJlZm9yZUV4aXQiLCJleGl0V2l0aE1lc3NhZ2UiLCJjb2xvciIsIm9wdGlvbnMiLCJleGl0Q29kZSIsIlRleHQiLCJ1bm1vdW50IiwicHJvY2VzcyIsImV4aXQiLCJzaG93U2V0dXBEaWFsb2ciLCJyZW5kZXJBbmRSdW4iLCJlbGVtZW50Iiwid2FpdFVudGlsRXhpdCIsInNob3dTZXR1cFNjcmVlbnMiLCJwZXJtaXNzaW9uTW9kZSIsImFsbG93RGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnMiLCJjb21tYW5kcyIsImNsYXVkZUluQ2hyb21lIiwiZGV2Q2hhbm5lbHMiLCJlbnYiLCJJU19ERU1PIiwiY29uZmlnIiwib25ib2FyZGluZ1Nob3duIiwidGhlbWUiLCJPbmJvYXJkaW5nIiwiQ0xBVUJCSVQiLCJUcnVzdERpYWxvZyIsImVycm9ycyIsImFsbEVycm9ycyIsImxlbmd0aCIsImV4dGVybmFsSW5jbHVkZXMiLCJDbGF1ZGVNZEV4dGVybmFsSW5jbHVkZXNEaWFsb2ciLCJzZXRJbW1lZGlhdGUiLCJHcm92ZURpYWxvZyIsImRlY2lzaW9uIiwiQU5USFJPUElDX0FQSV9LRVkiLCJjdXN0b21BcGlLZXlUcnVuY2F0ZWQiLCJrZXlTdGF0dXMiLCJBcHByb3ZlQXBpS2V5IiwiQnlwYXNzUGVybWlzc2lvbnNNb2RlRGlhbG9nIiwiQXV0b01vZGVPcHRJbkRpYWxvZyIsImlzQ2hhbm5lbHNFbmFibGVkIiwiZ2V0Q2xhdWRlQUlPQXV0aFRva2VucyIsImFsbCIsImFjY2Vzc1Rva2VuIiwibWFwIiwiYyIsImRldiIsIkRldkNoYW5uZWxzRGlhbG9nIiwiaGFzQ29tcGxldGVkQ2xhdWRlSW5DaHJvbWVPbmJvYXJkaW5nIiwiQ2xhdWRlSW5DaHJvbWVPbmJvYXJkaW5nIiwiZ2V0UmVuZGVyQ29udGV4dCIsImV4aXRPbkN0cmxDIiwicmVuZGVyT3B0aW9ucyIsImdldEZwc01ldHJpY3MiLCJzdGF0cyIsImxhc3RGbGlja2VyVGltZSIsImJhc2VPcHRpb25zIiwic3RkaW4iLCJmcHNUcmFja2VyIiwiZnJhbWVUaW1pbmdMb2dQYXRoIiwiQ0xBVURFX0NPREVfRlJBTUVfVElNSU5HX0xPRyIsImdldE1ldHJpY3MiLCJvbkZyYW1lIiwiZXZlbnQiLCJyZWNvcmQiLCJkdXJhdGlvbk1zIiwib2JzZXJ2ZSIsInBoYXNlcyIsImxpbmUiLCJKU09OIiwic3RyaW5naWZ5IiwidG90YWwiLCJyc3MiLCJtZW1vcnlVc2FnZSIsImNwdSIsImNwdVVzYWdlIiwiZmxpY2tlciIsImZsaWNrZXJzIiwicmVhc29uIiwibm93IiwiRGF0ZSIsImRlc2lyZWRIZWlnaHQiLCJhY3R1YWxIZWlnaHQiLCJhdmFpbGFibGVIZWlnaHQiLCJSZWNvcmQiXSwic291cmNlcyI6WyJpbnRlcmFjdGl2ZUhlbHBlcnMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0IHsgYXBwZW5kRmlsZVN5bmMgfSBmcm9tICdmcydcbmltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGxvZ0V2ZW50IH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7XG4gIGdyYWNlZnVsU2h1dGRvd24sXG4gIGdyYWNlZnVsU2h1dGRvd25TeW5jLFxufSBmcm9tICdzcmMvdXRpbHMvZ3JhY2VmdWxTaHV0ZG93bi5qcydcbmltcG9ydCB7XG4gIHR5cGUgQ2hhbm5lbEVudHJ5LFxuICBnZXRBbGxvd2VkQ2hhbm5lbHMsXG4gIHNldEFsbG93ZWRDaGFubmVscyxcbiAgc2V0SGFzRGV2Q2hhbm5lbHMsXG4gIHNldFNlc3Npb25UcnVzdEFjY2VwdGVkLFxuICBzZXRTdGF0c1N0b3JlLFxufSBmcm9tICcuL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB0eXBlIHsgQ29tbWFuZCB9IGZyb20gJy4vY29tbWFuZHMuanMnXG5pbXBvcnQgeyBjcmVhdGVTdGF0c1N0b3JlLCB0eXBlIFN0YXRzU3RvcmUgfSBmcm9tICcuL2NvbnRleHQvc3RhdHMuanMnXG5pbXBvcnQgeyBnZXRTeXN0ZW1Db250ZXh0IH0gZnJvbSAnLi9jb250ZXh0LmpzJ1xuaW1wb3J0IHsgaW5pdGlhbGl6ZVRlbGVtZXRyeUFmdGVyVHJ1c3QgfSBmcm9tICcuL2VudHJ5cG9pbnRzL2luaXQuanMnXG5pbXBvcnQgeyBpc1N5bmNocm9uaXplZE91dHB1dFN1cHBvcnRlZCB9IGZyb20gJy4vaW5rL3Rlcm1pbmFsLmpzJ1xuaW1wb3J0IHR5cGUgeyBSZW5kZXJPcHRpb25zLCBSb290LCBUZXh0UHJvcHMgfSBmcm9tICcuL2luay5qcydcbmltcG9ydCB7IEtleWJpbmRpbmdTZXR1cCB9IGZyb20gJy4va2V5YmluZGluZ3MvS2V5YmluZGluZ1Byb3ZpZGVyU2V0dXAuanMnXG5pbXBvcnQgeyBzdGFydERlZmVycmVkUHJlZmV0Y2hlcyB9IGZyb20gJy4vbWFpbi5qcydcbmltcG9ydCB7XG4gIGNoZWNrR2F0ZV9DQUNIRURfT1JfQkxPQ0tJTkcsXG4gIGluaXRpYWxpemVHcm93dGhCb29rLFxuICByZXNldEdyb3d0aEJvb2ssXG59IGZyb20gJy4vc2VydmljZXMvYW5hbHl0aWNzL2dyb3d0aGJvb2suanMnXG5pbXBvcnQgeyBpc1F1YWxpZmllZEZvckdyb3ZlIH0gZnJvbSAnLi9zZXJ2aWNlcy9hcGkvZ3JvdmUuanMnXG5pbXBvcnQgeyBoYW5kbGVNY3Bqc29uU2VydmVyQXBwcm92YWxzIH0gZnJvbSAnLi9zZXJ2aWNlcy9tY3BTZXJ2ZXJBcHByb3ZhbC5qcydcbmltcG9ydCB7IEFwcFN0YXRlUHJvdmlkZXIgfSBmcm9tICcuL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHsgb25DaGFuZ2VBcHBTdGF0ZSB9IGZyb20gJy4vc3RhdGUvb25DaGFuZ2VBcHBTdGF0ZS5qcydcbmltcG9ydCB7IG5vcm1hbGl6ZUFwaUtleUZvckNvbmZpZyB9IGZyb20gJy4vdXRpbHMvYXV0aFBvcnRhYmxlLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0RXh0ZXJuYWxDbGF1ZGVNZEluY2x1ZGVzLFxuICBnZXRNZW1vcnlGaWxlcyxcbiAgc2hvdWxkU2hvd0NsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc1dhcm5pbmcsXG59IGZyb20gJy4vdXRpbHMvY2xhdWRlbWQuanMnXG5pbXBvcnQge1xuICBjaGVja0hhc1RydXN0RGlhbG9nQWNjZXB0ZWQsXG4gIGdldEN1c3RvbUFwaUtleVN0YXR1cyxcbiAgZ2V0R2xvYmFsQ29uZmlnLFxuICBzYXZlR2xvYmFsQ29uZmlnLFxufSBmcm9tICcuL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IHVwZGF0ZURlZXBMaW5rVGVybWluYWxQcmVmZXJlbmNlIH0gZnJvbSAnLi91dGlscy9kZWVwTGluay90ZXJtaW5hbFByZWZlcmVuY2UuanMnXG5pbXBvcnQgeyBpc0VudlRydXRoeSwgaXNSdW5uaW5nT25Ib21lc3BhY2UgfSBmcm9tICcuL3V0aWxzL2VudlV0aWxzLmpzJ1xuaW1wb3J0IHsgdHlwZSBGcHNNZXRyaWNzLCBGcHNUcmFja2VyIH0gZnJvbSAnLi91dGlscy9mcHNUcmFja2VyLmpzJ1xuaW1wb3J0IHsgdXBkYXRlR2l0aHViUmVwb1BhdGhNYXBwaW5nIH0gZnJvbSAnLi91dGlscy9naXRodWJSZXBvUGF0aE1hcHBpbmcuanMnXG5pbXBvcnQgeyBhcHBseUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzIH0gZnJvbSAnLi91dGlscy9tYW5hZ2VkRW52LmpzJ1xuaW1wb3J0IHR5cGUgeyBQZXJtaXNzaW9uTW9kZSB9IGZyb20gJy4vdXRpbHMvcGVybWlzc2lvbnMvUGVybWlzc2lvbk1vZGUuanMnXG5pbXBvcnQgeyBnZXRCYXNlUmVuZGVyT3B0aW9ucyB9IGZyb20gJy4vdXRpbHMvcmVuZGVyT3B0aW9ucy5qcydcbmltcG9ydCB7IGdldFNldHRpbmdzV2l0aEFsbEVycm9ycyB9IGZyb20gJy4vdXRpbHMvc2V0dGluZ3MvYWxsRXJyb3JzLmpzJ1xuaW1wb3J0IHtcbiAgaGFzQXV0b01vZGVPcHRJbixcbiAgaGFzU2tpcERhbmdlcm91c01vZGVQZXJtaXNzaW9uUHJvbXB0LFxufSBmcm9tICcuL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gY29tcGxldGVPbmJvYXJkaW5nKCk6IHZvaWQge1xuICBzYXZlR2xvYmFsQ29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAuLi5jdXJyZW50LFxuICAgIGhhc0NvbXBsZXRlZE9uYm9hcmRpbmc6IHRydWUsXG4gICAgbGFzdE9uYm9hcmRpbmdWZXJzaW9uOiBNQUNSTy5WRVJTSU9OLFxuICB9KSlcbn1cbmV4cG9ydCBmdW5jdGlvbiBzaG93RGlhbG9nPFQgPSB2b2lkPihcbiAgcm9vdDogUm9vdCxcbiAgcmVuZGVyZXI6IChkb25lOiAocmVzdWx0OiBUKSA9PiB2b2lkKSA9PiBSZWFjdC5SZWFjdE5vZGUsXG4pOiBQcm9taXNlPFQ+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlPFQ+KHJlc29sdmUgPT4ge1xuICAgIGNvbnN0IGRvbmUgPSAocmVzdWx0OiBUKTogdm9pZCA9PiB2b2lkIHJlc29sdmUocmVzdWx0KVxuICAgIHJvb3QucmVuZGVyKHJlbmRlcmVyKGRvbmUpKVxuICB9KVxufVxuXG4vKipcbiAqIFJlbmRlciBhbiBlcnJvciBtZXNzYWdlIHRocm91Z2ggSW5rLCB0aGVuIHVubW91bnQgYW5kIGV4aXQuXG4gKiBVc2UgdGhpcyBmb3IgZmF0YWwgZXJyb3JzIGFmdGVyIHRoZSBJbmsgcm9vdCBoYXMgYmVlbiBjcmVhdGVkIOKAlFxuICogY29uc29sZS5lcnJvciBpcyBzd2FsbG93ZWQgYnkgSW5rJ3MgcGF0Y2hDb25zb2xlLCBzbyB3ZSByZW5kZXJcbiAqIHRocm91Z2ggdGhlIFJlYWN0IHRyZWUgaW5zdGVhZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGV4aXRXaXRoRXJyb3IoXG4gIHJvb3Q6IFJvb3QsXG4gIG1lc3NhZ2U6IHN0cmluZyxcbiAgYmVmb3JlRXhpdD86ICgpID0+IFByb21pc2U8dm9pZD4sXG4pOiBQcm9taXNlPG5ldmVyPiB7XG4gIHJldHVybiBleGl0V2l0aE1lc3NhZ2Uocm9vdCwgbWVzc2FnZSwgeyBjb2xvcjogJ2Vycm9yJywgYmVmb3JlRXhpdCB9KVxufVxuXG4vKipcbiAqIFJlbmRlciBhIG1lc3NhZ2UgdGhyb3VnaCBJbmssIHRoZW4gdW5tb3VudCBhbmQgZXhpdC5cbiAqIFVzZSB0aGlzIGZvciBtZXNzYWdlcyBhZnRlciB0aGUgSW5rIHJvb3QgaGFzIGJlZW4gY3JlYXRlZCDigJRcbiAqIGNvbnNvbGUgb3V0cHV0IGlzIHN3YWxsb3dlZCBieSBJbmsncyBwYXRjaENvbnNvbGUsIHNvIHdlIHJlbmRlclxuICogdGhyb3VnaCB0aGUgUmVhY3QgdHJlZSBpbnN0ZWFkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZXhpdFdpdGhNZXNzYWdlKFxuICByb290OiBSb290LFxuICBtZXNzYWdlOiBzdHJpbmcsXG4gIG9wdGlvbnM/OiB7XG4gICAgY29sb3I/OiBUZXh0UHJvcHNbJ2NvbG9yJ11cbiAgICBleGl0Q29kZT86IG51bWJlclxuICAgIGJlZm9yZUV4aXQ/OiAoKSA9PiBQcm9taXNlPHZvaWQ+XG4gIH0sXG4pOiBQcm9taXNlPG5ldmVyPiB7XG4gIGNvbnN0IHsgVGV4dCB9ID0gYXdhaXQgaW1wb3J0KCcuL2luay5qcycpXG4gIGNvbnN0IGNvbG9yID0gb3B0aW9ucz8uY29sb3JcbiAgY29uc3QgZXhpdENvZGUgPSBvcHRpb25zPy5leGl0Q29kZSA/PyAxXG4gIHJvb3QucmVuZGVyKFxuICAgIGNvbG9yID8gPFRleHQgY29sb3I9e2NvbG9yfT57bWVzc2FnZX08L1RleHQ+IDogPFRleHQ+e21lc3NhZ2V9PC9UZXh0PixcbiAgKVxuICByb290LnVubW91bnQoKVxuICBhd2FpdCBvcHRpb25zPy5iZWZvcmVFeGl0Py4oKVxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL25vLXByb2Nlc3MtZXhpdCAtLSBleGl0IGFmdGVyIEluayB1bm1vdW50XG4gIHByb2Nlc3MuZXhpdChleGl0Q29kZSlcbn1cblxuLyoqXG4gKiBTaG93IGEgc2V0dXAgZGlhbG9nIHdyYXBwZWQgaW4gQXBwU3RhdGVQcm92aWRlciArIEtleWJpbmRpbmdTZXR1cC5cbiAqIFJlZHVjZXMgYm9pbGVycGxhdGUgaW4gc2hvd1NldHVwU2NyZWVucygpIHdoZXJlIGV2ZXJ5IGRpYWxvZyBuZWVkcyB0aGVzZSB3cmFwcGVycy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNob3dTZXR1cERpYWxvZzxUID0gdm9pZD4oXG4gIHJvb3Q6IFJvb3QsXG4gIHJlbmRlcmVyOiAoZG9uZTogKHJlc3VsdDogVCkgPT4gdm9pZCkgPT4gUmVhY3QuUmVhY3ROb2RlLFxuICBvcHRpb25zPzogeyBvbkNoYW5nZUFwcFN0YXRlPzogdHlwZW9mIG9uQ2hhbmdlQXBwU3RhdGUgfSxcbik6IFByb21pc2U8VD4ge1xuICByZXR1cm4gc2hvd0RpYWxvZzxUPihyb290LCBkb25lID0+IChcbiAgICA8QXBwU3RhdGVQcm92aWRlciBvbkNoYW5nZUFwcFN0YXRlPXtvcHRpb25zPy5vbkNoYW5nZUFwcFN0YXRlfT5cbiAgICAgIDxLZXliaW5kaW5nU2V0dXA+e3JlbmRlcmVyKGRvbmUpfTwvS2V5YmluZGluZ1NldHVwPlxuICAgIDwvQXBwU3RhdGVQcm92aWRlcj5cbiAgKSlcbn1cblxuLyoqXG4gKiBSZW5kZXIgdGhlIG1haW4gVUkgaW50byB0aGUgcm9vdCBhbmQgd2FpdCBmb3IgaXQgdG8gZXhpdC5cbiAqIEhhbmRsZXMgdGhlIGNvbW1vbiBlcGlsb2d1ZTogc3RhcnQgZGVmZXJyZWQgcHJlZmV0Y2hlcywgd2FpdCBmb3IgZXhpdCwgZ3JhY2VmdWwgc2h1dGRvd24uXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZW5kZXJBbmRSdW4oXG4gIHJvb3Q6IFJvb3QsXG4gIGVsZW1lbnQ6IFJlYWN0LlJlYWN0Tm9kZSxcbik6IFByb21pc2U8dm9pZD4ge1xuICByb290LnJlbmRlcihlbGVtZW50KVxuICBzdGFydERlZmVycmVkUHJlZmV0Y2hlcygpXG4gIGF3YWl0IHJvb3Qud2FpdFVudGlsRXhpdCgpXG4gIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMClcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNob3dTZXR1cFNjcmVlbnMoXG4gIHJvb3Q6IFJvb3QsXG4gIHBlcm1pc3Npb25Nb2RlOiBQZXJtaXNzaW9uTW9kZSxcbiAgYWxsb3dEYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uczogYm9vbGVhbixcbiAgY29tbWFuZHM/OiBDb21tYW5kW10sXG4gIGNsYXVkZUluQ2hyb21lPzogYm9vbGVhbixcbiAgZGV2Q2hhbm5lbHM/OiBDaGFubmVsRW50cnlbXSxcbik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICBpZiAoXG4gICAgXCJwcm9kdWN0aW9uXCIgPT09ICd0ZXN0JyB8fFxuICAgIGlzRW52VHJ1dGh5KGZhbHNlKSB8fFxuICAgIHByb2Nlc3MuZW52LklTX0RFTU8gLy8gU2tpcCBvbmJvYXJkaW5nIGluIGRlbW8gbW9kZVxuICApIHtcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIGNvbnN0IGNvbmZpZyA9IGdldEdsb2JhbENvbmZpZygpXG4gIGxldCBvbmJvYXJkaW5nU2hvd24gPSBmYWxzZVxuICBpZiAoXG4gICAgIWNvbmZpZy50aGVtZSB8fFxuICAgICFjb25maWcuaGFzQ29tcGxldGVkT25ib2FyZGluZyAvLyBhbHdheXMgc2hvdyBvbmJvYXJkaW5nIGF0IGxlYXN0IG9uY2VcbiAgKSB7XG4gICAgb25ib2FyZGluZ1Nob3duID0gdHJ1ZVxuICAgIGNvbnN0IHsgT25ib2FyZGluZyB9ID0gYXdhaXQgaW1wb3J0KCcuL2NvbXBvbmVudHMvT25ib2FyZGluZy5qcycpXG4gICAgYXdhaXQgc2hvd1NldHVwRGlhbG9nKFxuICAgICAgcm9vdCxcbiAgICAgIGRvbmUgPT4gKFxuICAgICAgICA8T25ib2FyZGluZ1xuICAgICAgICAgIG9uRG9uZT17KCkgPT4ge1xuICAgICAgICAgICAgY29tcGxldGVPbmJvYXJkaW5nKClcbiAgICAgICAgICAgIHZvaWQgZG9uZSgpXG4gICAgICAgICAgfX1cbiAgICAgICAgLz5cbiAgICAgICksXG4gICAgICB7IG9uQ2hhbmdlQXBwU3RhdGUgfSxcbiAgICApXG4gIH1cblxuICAvLyBBbHdheXMgc2hvdyB0aGUgdHJ1c3QgZGlhbG9nIGluIGludGVyYWN0aXZlIHNlc3Npb25zLCByZWdhcmRsZXNzIG9mIHBlcm1pc3Npb24gbW9kZS5cbiAgLy8gVGhlIHRydXN0IGRpYWxvZyBpcyB0aGUgd29ya3NwYWNlIHRydXN0IGJvdW5kYXJ5IOKAlCBpdCB3YXJucyBhYm91dCB1bnRydXN0ZWQgcmVwb3NcbiAgLy8gYW5kIGNoZWNrcyBDTEFVREUubWQgZXh0ZXJuYWwgaW5jbHVkZXMuIGJ5cGFzc1Blcm1pc3Npb25zIG1vZGVcbiAgLy8gb25seSBhZmZlY3RzIHRvb2wgZXhlY3V0aW9uIHBlcm1pc3Npb25zLCBub3Qgd29ya3NwYWNlIHRydXN0LlxuICAvLyBOb3RlOiBub24taW50ZXJhY3RpdmUgc2Vzc2lvbnMgKENJL0NEIHdpdGggLXApIG5ldmVyIHJlYWNoIHNob3dTZXR1cFNjcmVlbnMgYXQgYWxsLlxuICAvLyBTa2lwIHBlcm1pc3Npb24gY2hlY2tzIGluIGNsYXViYml0XG4gIGlmICghaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVUJCSVQpKSB7XG4gICAgLy8gRmFzdC1wYXRoOiBza2lwIFRydXN0RGlhbG9nIGltcG9ydCtyZW5kZXIgd2hlbiBDV0QgaXMgYWxyZWFkeSB0cnVzdGVkLlxuICAgIC8vIElmIGl0IHJldHVybnMgdHJ1ZSwgdGhlIFRydXN0RGlhbG9nIHdvdWxkIGF1dG8tcmVzb2x2ZSByZWdhcmRsZXNzIG9mXG4gICAgLy8gc2VjdXJpdHkgZmVhdHVyZXMsIHNvIHdlIGNhbiBza2lwIHRoZSBkeW5hbWljIGltcG9ydCBhbmQgcmVuZGVyIGN5Y2xlLlxuICAgIGlmICghY2hlY2tIYXNUcnVzdERpYWxvZ0FjY2VwdGVkKCkpIHtcbiAgICAgIGNvbnN0IHsgVHJ1c3REaWFsb2cgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICAgJy4vY29tcG9uZW50cy9UcnVzdERpYWxvZy9UcnVzdERpYWxvZy5qcydcbiAgICAgIClcbiAgICAgIGF3YWl0IHNob3dTZXR1cERpYWxvZyhyb290LCBkb25lID0+IChcbiAgICAgICAgPFRydXN0RGlhbG9nIGNvbW1hbmRzPXtjb21tYW5kc30gb25Eb25lPXtkb25lfSAvPlxuICAgICAgKSlcbiAgICB9XG5cbiAgICAvLyBTaWduYWwgdGhhdCB0cnVzdCBoYXMgYmVlbiB2ZXJpZmllZCBmb3IgdGhpcyBzZXNzaW9uLlxuICAgIC8vIEdyb3d0aEJvb2sgY2hlY2tzIHRoaXMgdG8gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSBhdXRoIGhlYWRlcnMuXG4gICAgc2V0U2Vzc2lvblRydXN0QWNjZXB0ZWQodHJ1ZSlcblxuICAgIC8vIFJlc2V0IGFuZCByZWluaXRpYWxpemUgR3Jvd3RoQm9vayBhZnRlciB0cnVzdCBpcyBlc3RhYmxpc2hlZC5cbiAgICAvLyBEZWZlbnNlIGZvciBsb2dpbi9sb2dvdXQ6IGNsZWFycyBhbnkgcHJpb3IgY2xpZW50IHNvIHRoZSBuZXh0IGluaXRcbiAgICAvLyBwaWNrcyB1cCBmcmVzaCBhdXRoIGhlYWRlcnMuXG4gICAgcmVzZXRHcm93dGhCb29rKClcbiAgICB2b2lkIGluaXRpYWxpemVHcm93dGhCb29rKClcblxuICAgIC8vIE5vdyB0aGF0IHRydXN0IGlzIGVzdGFibGlzaGVkLCBwcmVmZXRjaCBzeXN0ZW0gY29udGV4dCBpZiBpdCB3YXNuJ3QgYWxyZWFkeVxuICAgIHZvaWQgZ2V0U3lzdGVtQ29udGV4dCgpXG5cbiAgICAvLyBJZiBzZXR0aW5ncyBhcmUgdmFsaWQsIGNoZWNrIGZvciBhbnkgbWNwLmpzb24gc2VydmVycyB0aGF0IG5lZWQgYXBwcm92YWxcbiAgICBjb25zdCB7IGVycm9yczogYWxsRXJyb3JzIH0gPSBnZXRTZXR0aW5nc1dpdGhBbGxFcnJvcnMoKVxuICAgIGlmIChhbGxFcnJvcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICBhd2FpdCBoYW5kbGVNY3Bqc29uU2VydmVyQXBwcm92YWxzKHJvb3QpXG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgZm9yIGNsYXVkZS5tZCBpbmNsdWRlcyB0aGF0IG5lZWQgYXBwcm92YWxcbiAgICBpZiAoYXdhaXQgc2hvdWxkU2hvd0NsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc1dhcm5pbmcoKSkge1xuICAgICAgY29uc3QgZXh0ZXJuYWxJbmNsdWRlcyA9IGdldEV4dGVybmFsQ2xhdWRlTWRJbmNsdWRlcyhcbiAgICAgICAgYXdhaXQgZ2V0TWVtb3J5RmlsZXModHJ1ZSksXG4gICAgICApXG4gICAgICBjb25zdCB7IENsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc0RpYWxvZyB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAnLi9jb21wb25lbnRzL0NsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc0RpYWxvZy5qcydcbiAgICAgIClcbiAgICAgIGF3YWl0IHNob3dTZXR1cERpYWxvZyhyb290LCBkb25lID0+IChcbiAgICAgICAgPENsYXVkZU1kRXh0ZXJuYWxJbmNsdWRlc0RpYWxvZ1xuICAgICAgICAgIG9uRG9uZT17ZG9uZX1cbiAgICAgICAgICBpc1N0YW5kYWxvbmVEaWFsb2dcbiAgICAgICAgICBleHRlcm5hbEluY2x1ZGVzPXtleHRlcm5hbEluY2x1ZGVzfVxuICAgICAgICAvPlxuICAgICAgKSlcbiAgICB9XG4gIH1cblxuICAvLyBUcmFjayBjdXJyZW50IHJlcG8gcGF0aCBmb3IgdGVsZXBvcnQgZGlyZWN0b3J5IHN3aXRjaGluZyAoZmlyZS1hbmQtZm9yZ2V0KVxuICAvLyBUaGlzIG11c3QgaGFwcGVuIEFGVEVSIHRydXN0IHRvIHByZXZlbnQgdW50cnVzdGVkIGRpcmVjdG9yaWVzIGZyb20gcG9pc29uaW5nIHRoZSBtYXBwaW5nXG4gIHZvaWQgdXBkYXRlR2l0aHViUmVwb1BhdGhNYXBwaW5nKClcbiAgaWYgKGZlYXR1cmUoJ0xPREVTVE9ORScpKSB7XG4gICAgdXBkYXRlRGVlcExpbmtUZXJtaW5hbFByZWZlcmVuY2UoKVxuICB9XG5cbiAgLy8gQXBwbHkgZnVsbCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYWZ0ZXIgdHJ1c3QgZGlhbG9nIGlzIGFjY2VwdGVkIE9SIGluIGJ5cGFzcyBtb2RlXG4gIC8vIEluIGJ5cGFzcyBtb2RlIChDSS9DRCwgYXV0b21hdGlvbiksIHdlIHRydXN0IHRoZSBlbnZpcm9ubWVudCBzbyBhcHBseSBhbGwgdmFyaWFibGVzXG4gIC8vIEluIG5vcm1hbCBtb2RlLCB0aGlzIGhhcHBlbnMgYWZ0ZXIgdGhlIHRydXN0IGRpYWxvZyBpcyBhY2NlcHRlZFxuICAvLyBUaGlzIGluY2x1ZGVzIHBvdGVudGlhbGx5IGRhbmdlcm91cyBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZnJvbSB1bnRydXN0ZWQgc291cmNlc1xuICBhcHBseUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzKClcblxuICAvLyBJbml0aWFsaXplIHRlbGVtZXRyeSBhZnRlciBlbnYgdmFycyBhcmUgYXBwbGllZCBzbyBPVEVMIGVuZHBvaW50IGVudiB2YXJzIGFuZFxuICAvLyBvdGVsSGVhZGVyc0hlbHBlciAod2hpY2ggcmVxdWlyZXMgdHJ1c3QgdG8gZXhlY3V0ZSkgYXJlIGF2YWlsYWJsZS5cbiAgLy8gRGVmZXIgdG8gbmV4dCB0aWNrIHNvIHRoZSBPVGVsIGR5bmFtaWMgaW1wb3J0IHJlc29sdmVzIGFmdGVyIGZpcnN0IHJlbmRlclxuICAvLyBpbnN0ZWFkIG9mIGR1cmluZyB0aGUgcHJlLXJlbmRlciBtaWNyb3Rhc2sgcXVldWUuXG4gIHNldEltbWVkaWF0ZSgoKSA9PiBpbml0aWFsaXplVGVsZW1ldHJ5QWZ0ZXJUcnVzdCgpKVxuXG4gIGlmIChhd2FpdCBpc1F1YWxpZmllZEZvckdyb3ZlKCkpIHtcbiAgICBjb25zdCB7IEdyb3ZlRGlhbG9nIH0gPSBhd2FpdCBpbXBvcnQoJ3NyYy9jb21wb25lbnRzL2dyb3ZlL0dyb3ZlLmpzJylcbiAgICBjb25zdCBkZWNpc2lvbiA9IGF3YWl0IHNob3dTZXR1cERpYWxvZzxzdHJpbmc+KHJvb3QsIGRvbmUgPT4gKFxuICAgICAgPEdyb3ZlRGlhbG9nXG4gICAgICAgIHNob3dJZkFscmVhZHlWaWV3ZWQ9e2ZhbHNlfVxuICAgICAgICBsb2NhdGlvbj17b25ib2FyZGluZ1Nob3duID8gJ29uYm9hcmRpbmcnIDogJ3BvbGljeV91cGRhdGVfbW9kYWwnfVxuICAgICAgICBvbkRvbmU9e2RvbmV9XG4gICAgICAvPlxuICAgICkpXG4gICAgaWYgKGRlY2lzaW9uID09PSAnZXNjYXBlJykge1xuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2dyb3ZlX3BvbGljeV9leGl0ZWQnLCB7fSlcbiAgICAgIGdyYWNlZnVsU2h1dGRvd25TeW5jKDApXG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG4gIH1cblxuICAvLyBDaGVjayBmb3IgY3VzdG9tIEFQSSBrZXlcbiAgLy8gT24gaG9tZXNwYWNlLCBBTlRIUk9QSUNfQVBJX0tFWSBpcyBwcmVzZXJ2ZWQgaW4gcHJvY2Vzcy5lbnYgZm9yIGNoaWxkXG4gIC8vIHByb2Nlc3NlcyBidXQgaWdub3JlZCBieSBDbGF1ZGUgQ29kZSBpdHNlbGYgKHNlZSBhdXRoLnRzKS5cbiAgaWYgKHByb2Nlc3MuZW52LkFOVEhST1BJQ19BUElfS0VZICYmICFpc1J1bm5pbmdPbkhvbWVzcGFjZSgpKSB7XG4gICAgY29uc3QgY3VzdG9tQXBpS2V5VHJ1bmNhdGVkID0gbm9ybWFsaXplQXBpS2V5Rm9yQ29uZmlnKFxuICAgICAgcHJvY2Vzcy5lbnYuQU5USFJPUElDX0FQSV9LRVksXG4gICAgKVxuICAgIGNvbnN0IGtleVN0YXR1cyA9IGdldEN1c3RvbUFwaUtleVN0YXR1cyhjdXN0b21BcGlLZXlUcnVuY2F0ZWQpXG4gICAgaWYgKGtleVN0YXR1cyA9PT0gJ25ldycpIHtcbiAgICAgIGNvbnN0IHsgQXBwcm92ZUFwaUtleSB9ID0gYXdhaXQgaW1wb3J0KCcuL2NvbXBvbmVudHMvQXBwcm92ZUFwaUtleS5qcycpXG4gICAgICBhd2FpdCBzaG93U2V0dXBEaWFsb2c8Ym9vbGVhbj4oXG4gICAgICAgIHJvb3QsXG4gICAgICAgIGRvbmUgPT4gKFxuICAgICAgICAgIDxBcHByb3ZlQXBpS2V5XG4gICAgICAgICAgICBjdXN0b21BcGlLZXlUcnVuY2F0ZWQ9e2N1c3RvbUFwaUtleVRydW5jYXRlZH1cbiAgICAgICAgICAgIG9uRG9uZT17ZG9uZX1cbiAgICAgICAgICAvPlxuICAgICAgICApLFxuICAgICAgICB7IG9uQ2hhbmdlQXBwU3RhdGUgfSxcbiAgICAgIClcbiAgICB9XG4gIH1cblxuICBpZiAoXG4gICAgKHBlcm1pc3Npb25Nb2RlID09PSAnYnlwYXNzUGVybWlzc2lvbnMnIHx8XG4gICAgICBhbGxvd0Rhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zKSAmJlxuICAgICFoYXNTa2lwRGFuZ2Vyb3VzTW9kZVBlcm1pc3Npb25Qcm9tcHQoKVxuICApIHtcbiAgICBjb25zdCB7IEJ5cGFzc1Blcm1pc3Npb25zTW9kZURpYWxvZyB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgJy4vY29tcG9uZW50cy9CeXBhc3NQZXJtaXNzaW9uc01vZGVEaWFsb2cuanMnXG4gICAgKVxuICAgIGF3YWl0IHNob3dTZXR1cERpYWxvZyhyb290LCBkb25lID0+IChcbiAgICAgIDxCeXBhc3NQZXJtaXNzaW9uc01vZGVEaWFsb2cgb25BY2NlcHQ9e2RvbmV9IC8+XG4gICAgKSlcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgIC8vIE9ubHkgc2hvdyB0aGUgb3B0LWluIGRpYWxvZyBpZiBhdXRvIG1vZGUgYWN0dWFsbHkgcmVzb2x2ZWQg4oCUIGlmIHRoZVxuICAgIC8vIGdhdGUgZGVuaWVkIGl0IChvcmcgbm90IGFsbG93bGlzdGVkLCBzZXR0aW5ncyBkaXNhYmxlZCksIHNob3dpbmdcbiAgICAvLyBjb25zZW50IGZvciBhbiB1bmF2YWlsYWJsZSBmZWF0dXJlIGlzIHBvaW50bGVzcy4gVGhlXG4gICAgLy8gdmVyaWZ5QXV0b01vZGVHYXRlQWNjZXNzIG5vdGlmaWNhdGlvbiB3aWxsIGV4cGxhaW4gd2h5IGluc3RlYWQuXG4gICAgaWYgKHBlcm1pc3Npb25Nb2RlID09PSAnYXV0bycgJiYgIWhhc0F1dG9Nb2RlT3B0SW4oKSkge1xuICAgICAgY29uc3QgeyBBdXRvTW9kZU9wdEluRGlhbG9nIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICcuL2NvbXBvbmVudHMvQXV0b01vZGVPcHRJbkRpYWxvZy5qcydcbiAgICAgIClcbiAgICAgIGF3YWl0IHNob3dTZXR1cERpYWxvZyhyb290LCBkb25lID0+IChcbiAgICAgICAgPEF1dG9Nb2RlT3B0SW5EaWFsb2dcbiAgICAgICAgICBvbkFjY2VwdD17ZG9uZX1cbiAgICAgICAgICBvbkRlY2xpbmU9eygpID0+IGdyYWNlZnVsU2h1dGRvd25TeW5jKDEpfVxuICAgICAgICAgIGRlY2xpbmVFeGl0c1xuICAgICAgICAvPlxuICAgICAgKSlcbiAgICB9XG4gIH1cblxuICAvLyAtLWRhbmdlcm91c2x5LWxvYWQtZGV2ZWxvcG1lbnQtY2hhbm5lbHMgY29uZmlybWF0aW9uLiBPbiBhY2NlcHQsIGFwcGVuZFxuICAvLyBkZXYgY2hhbm5lbHMgdG8gYW55IC0tY2hhbm5lbHMgbGlzdCBhbHJlYWR5IHNldCBpbiBtYWluLnRzeC4gT3JnIHBvbGljeVxuICAvLyBpcyBOT1QgYnlwYXNzZWQg4oCUIGdhdGVDaGFubmVsU2VydmVyKCkgc3RpbGwgcnVuczsgdGhpcyBmbGFnIG9ubHkgZXhpc3RzXG4gIC8vIHRvIHNpZGVzdGVwIHRoZSAtLWNoYW5uZWxzIGFwcHJvdmVkLXNlcnZlciBhbGxvd2xpc3QuXG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQ0hBTk5FTFMnKSkge1xuICAgIC8vIGdhdGVDaGFubmVsU2VydmVyIGFuZCBDaGFubmVsc05vdGljZSByZWFkIHRlbmd1X2hhcmJvciBhZnRlciB0aGlzXG4gICAgLy8gZnVuY3Rpb24gcmV0dXJucy4gQSBjb2xkIGRpc2sgY2FjaGUgKGZyZXNoIGluc3RhbGwsIG9yIGZpcnN0IHJ1biBhZnRlclxuICAgIC8vIHRoZSBmbGFnIHdhcyBhZGRlZCBzZXJ2ZXItc2lkZSkgZGVmYXVsdHMgdG8gZmFsc2UgYW5kIHNpbGVudGx5IGRyb3BzXG4gICAgLy8gY2hhbm5lbCBub3RpZmljYXRpb25zIGZvciB0aGUgd2hvbGUgc2Vzc2lvbiDigJQgZ2gjMzcwMjYuXG4gICAgLy8gY2hlY2tHYXRlX0NBQ0hFRF9PUl9CTE9DS0lORyByZXR1cm5zIGltbWVkaWF0ZWx5IGlmIGRpc2sgYWxyZWFkeSBzYXlzXG4gICAgLy8gdHJ1ZTsgb25seSBibG9ja3Mgb24gYSBjb2xkL3N0YWxlLWZhbHNlIGNhY2hlIChhd2FpdHMgdGhlIHNhbWUgbWVtb2l6ZWRcbiAgICAvLyBpbml0aWFsaXplR3Jvd3RoQm9vayBwcm9taXNlIGZpcmVkIGVhcmxpZXIpLiBBbHNvIHdhcm1zIHRoZVxuICAgIC8vIGlzQ2hhbm5lbHNFbmFibGVkKCkgY2hlY2sgaW4gdGhlIGRldi1jaGFubmVscyBkaWFsb2cgYmVsb3cuXG4gICAgaWYgKGdldEFsbG93ZWRDaGFubmVscygpLmxlbmd0aCA+IDAgfHwgKGRldkNoYW5uZWxzPy5sZW5ndGggPz8gMCkgPiAwKSB7XG4gICAgICBhd2FpdCBjaGVja0dhdGVfQ0FDSEVEX09SX0JMT0NLSU5HKCd0ZW5ndV9oYXJib3InKVxuICAgIH1cblxuICAgIGlmIChkZXZDaGFubmVscyAmJiBkZXZDaGFubmVscy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBbeyBpc0NoYW5uZWxzRW5hYmxlZCB9LCB7IGdldENsYXVkZUFJT0F1dGhUb2tlbnMgfV0gPVxuICAgICAgICBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAgICAgaW1wb3J0KCcuL3NlcnZpY2VzL21jcC9jaGFubmVsQWxsb3dsaXN0LmpzJyksXG4gICAgICAgICAgaW1wb3J0KCcuL3V0aWxzL2F1dGguanMnKSxcbiAgICAgICAgXSlcbiAgICAgIC8vIFNraXAgdGhlIGRpYWxvZyB3aGVuIGNoYW5uZWxzIGFyZSBibG9ja2VkICh0ZW5ndV9oYXJib3Igb2ZmIG9yIG5vXG4gICAgICAvLyBPQXV0aCkg4oCUIGFjY2VwdGluZyB0aGVuIGltbWVkaWF0ZWx5IHNlZWluZyBcIm5vdCBhdmFpbGFibGVcIiBpblxuICAgICAgLy8gQ2hhbm5lbHNOb3RpY2UgaXMgd29yc2UgdGhhbiBubyBkaWFsb2cuIEFwcGVuZCBlbnRyaWVzIGFueXdheSBzb1xuICAgICAgLy8gQ2hhbm5lbHNOb3RpY2UgcmVuZGVycyB0aGUgYmxvY2tlZCBicmFuY2ggd2l0aCB0aGUgZGV2IGVudHJpZXNcbiAgICAgIC8vIG5hbWVkLiBkZXY6dHJ1ZSBoZXJlIGlzIGZvciB0aGUgZmxhZyBsYWJlbCBpbiBDaGFubmVsc05vdGljZVxuICAgICAgLy8gKGhhc05vbkRldiBjaGVjayk7IHRoZSBhbGxvd2xpc3QgYnlwYXNzIGl0IGFsc28gZ3JhbnRzIGlzIG1vb3RcbiAgICAgIC8vIHNpbmNlIHRoZSBnYXRlIGJsb2NrcyB1cHN0cmVhbS5cbiAgICAgIGlmICghaXNDaGFubmVsc0VuYWJsZWQoKSB8fCAhZ2V0Q2xhdWRlQUlPQXV0aFRva2VucygpPy5hY2Nlc3NUb2tlbikge1xuICAgICAgICBzZXRBbGxvd2VkQ2hhbm5lbHMoW1xuICAgICAgICAgIC4uLmdldEFsbG93ZWRDaGFubmVscygpLFxuICAgICAgICAgIC4uLmRldkNoYW5uZWxzLm1hcChjID0+ICh7IC4uLmMsIGRldjogdHJ1ZSB9KSksXG4gICAgICAgIF0pXG4gICAgICAgIHNldEhhc0RldkNoYW5uZWxzKHRydWUpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjb25zdCB7IERldkNoYW5uZWxzRGlhbG9nIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICAgJy4vY29tcG9uZW50cy9EZXZDaGFubmVsc0RpYWxvZy5qcydcbiAgICAgICAgKVxuICAgICAgICBhd2FpdCBzaG93U2V0dXBEaWFsb2cocm9vdCwgZG9uZSA9PiAoXG4gICAgICAgICAgPERldkNoYW5uZWxzRGlhbG9nXG4gICAgICAgICAgICBjaGFubmVscz17ZGV2Q2hhbm5lbHN9XG4gICAgICAgICAgICBvbkFjY2VwdD17KCkgPT4ge1xuICAgICAgICAgICAgICAvLyBNYXJrIGRldiBlbnRyaWVzIHBlci1lbnRyeSBzbyB0aGUgYWxsb3dsaXN0IGJ5cGFzcyBkb2Vzbid0IGxlYWtcbiAgICAgICAgICAgICAgLy8gdG8gLS1jaGFubmVscyBlbnRyaWVzIHdoZW4gYm90aCBmbGFncyBhcmUgcGFzc2VkLlxuICAgICAgICAgICAgICBzZXRBbGxvd2VkQ2hhbm5lbHMoW1xuICAgICAgICAgICAgICAgIC4uLmdldEFsbG93ZWRDaGFubmVscygpLFxuICAgICAgICAgICAgICAgIC4uLmRldkNoYW5uZWxzLm1hcChjID0+ICh7IC4uLmMsIGRldjogdHJ1ZSB9KSksXG4gICAgICAgICAgICAgIF0pXG4gICAgICAgICAgICAgIHNldEhhc0RldkNoYW5uZWxzKHRydWUpXG4gICAgICAgICAgICAgIHZvaWQgZG9uZSgpXG4gICAgICAgICAgICB9fVxuICAgICAgICAgIC8+XG4gICAgICAgICkpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLy8gU2hvdyBDaHJvbWUgb25ib2FyZGluZyBmb3IgZmlyc3QtdGltZSBDbGF1ZGUgaW4gQ2hyb21lIHVzZXJzXG4gIGlmIChcbiAgICBjbGF1ZGVJbkNocm9tZSAmJlxuICAgICFnZXRHbG9iYWxDb25maWcoKS5oYXNDb21wbGV0ZWRDbGF1ZGVJbkNocm9tZU9uYm9hcmRpbmdcbiAgKSB7XG4gICAgY29uc3QgeyBDbGF1ZGVJbkNocm9tZU9uYm9hcmRpbmcgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICcuL2NvbXBvbmVudHMvQ2xhdWRlSW5DaHJvbWVPbmJvYXJkaW5nLmpzJ1xuICAgIClcbiAgICBhd2FpdCBzaG93U2V0dXBEaWFsb2cocm9vdCwgZG9uZSA9PiAoXG4gICAgICA8Q2xhdWRlSW5DaHJvbWVPbmJvYXJkaW5nIG9uRG9uZT17ZG9uZX0gLz5cbiAgICApKVxuICB9XG5cbiAgcmV0dXJuIG9uYm9hcmRpbmdTaG93blxufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UmVuZGVyQ29udGV4dChleGl0T25DdHJsQzogYm9vbGVhbik6IHtcbiAgcmVuZGVyT3B0aW9uczogUmVuZGVyT3B0aW9uc1xuICBnZXRGcHNNZXRyaWNzOiAoKSA9PiBGcHNNZXRyaWNzIHwgdW5kZWZpbmVkXG4gIHN0YXRzOiBTdGF0c1N0b3JlXG59IHtcbiAgbGV0IGxhc3RGbGlja2VyVGltZSA9IDBcbiAgY29uc3QgYmFzZU9wdGlvbnMgPSBnZXRCYXNlUmVuZGVyT3B0aW9ucyhleGl0T25DdHJsQylcblxuICAvLyBMb2cgYW5hbHl0aWNzIGV2ZW50IHdoZW4gc3RkaW4gb3ZlcnJpZGUgaXMgYWN0aXZlXG4gIGlmIChiYXNlT3B0aW9ucy5zdGRpbikge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV9zdGRpbl9pbnRlcmFjdGl2ZScsIHt9KVxuICB9XG5cbiAgY29uc3QgZnBzVHJhY2tlciA9IG5ldyBGcHNUcmFja2VyKClcbiAgY29uc3Qgc3RhdHMgPSBjcmVhdGVTdGF0c1N0b3JlKClcbiAgc2V0U3RhdHNTdG9yZShzdGF0cylcblxuICAvLyBCZW5jaCBtb2RlOiB3aGVuIHNldCwgYXBwZW5kIHBlci1mcmFtZSBwaGFzZSB0aW1pbmdzIGFzIEpTT05MIGZvclxuICAvLyBvZmZsaW5lIGFuYWx5c2lzIGJ5IGJlbmNoL3JlcGwtc2Nyb2xsLnRzLiBDYXB0dXJlcyB0aGUgZnVsbCBUVUlcbiAgLy8gcmVuZGVyIHBpcGVsaW5lICh5b2dhIOKGkiBzY3JlZW4gYnVmZmVyIOKGkiBkaWZmIOKGkiBvcHRpbWl6ZSDihpIgc3Rkb3V0KVxuICAvLyBzbyBwZXJmIHdvcmsgb24gYW55IHBoYXNlIGNhbiBiZSB2YWxpZGF0ZWQgYWdhaW5zdCByZWFsIHVzZXIgZmxvd3MuXG4gIGNvbnN0IGZyYW1lVGltaW5nTG9nUGF0aCA9IHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0ZSQU1FX1RJTUlOR19MT0dcbiAgcmV0dXJuIHtcbiAgICBnZXRGcHNNZXRyaWNzOiAoKSA9PiBmcHNUcmFja2VyLmdldE1ldHJpY3MoKSxcbiAgICBzdGF0cyxcbiAgICByZW5kZXJPcHRpb25zOiB7XG4gICAgICAuLi5iYXNlT3B0aW9ucyxcbiAgICAgIG9uRnJhbWU6IGV2ZW50ID0+IHtcbiAgICAgICAgZnBzVHJhY2tlci5yZWNvcmQoZXZlbnQuZHVyYXRpb25NcylcbiAgICAgICAgc3RhdHMub2JzZXJ2ZSgnZnJhbWVfZHVyYXRpb25fbXMnLCBldmVudC5kdXJhdGlvbk1zKVxuICAgICAgICBpZiAoZnJhbWVUaW1pbmdMb2dQYXRoICYmIGV2ZW50LnBoYXNlcykge1xuICAgICAgICAgIC8vIEJlbmNoLW9ubHkgZW52LXZhci1nYXRlZCBwYXRoOiBzeW5jIHdyaXRlIHNvIG5vIGZyYW1lcyBkcm9wcGVkXG4gICAgICAgICAgLy8gb24gYWJydXB0IGV4aXQuIH4xMDAgYnl0ZXMgYXQg4omkNjBmcHMgaXMgbmVnbGlnaWJsZS4gcnNzL2NwdSBhcmVcbiAgICAgICAgICAvLyBzaW5nbGUgc3lzY2FsbHM7IGNwdSBpcyBjdW11bGF0aXZlIOKAlCBiZW5jaCBzaWRlIGNvbXB1dGVzIGRlbHRhLlxuICAgICAgICAgIGNvbnN0IGxpbmUgPVxuICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9uby1kaXJlY3QtanNvbi1vcGVyYXRpb25zIC0tIHRpbnkgb2JqZWN0LCBob3QgYmVuY2ggcGF0aFxuICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgICAgICB0b3RhbDogZXZlbnQuZHVyYXRpb25NcyxcbiAgICAgICAgICAgICAgLi4uZXZlbnQucGhhc2VzLFxuICAgICAgICAgICAgICByc3M6IHByb2Nlc3MubWVtb3J5VXNhZ2UucnNzKCksXG4gICAgICAgICAgICAgIGNwdTogcHJvY2Vzcy5jcHVVc2FnZSgpLFxuICAgICAgICAgICAgfSkgKyAnXFxuJ1xuICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tc3luYy1mcyAtLSBiZW5jaC1vbmx5LCBzeW5jIHNvIG5vIGZyYW1lcyBkcm9wcGVkIG9uIGV4aXRcbiAgICAgICAgICBhcHBlbmRGaWxlU3luYyhmcmFtZVRpbWluZ0xvZ1BhdGgsIGxpbmUpXG4gICAgICAgIH1cbiAgICAgICAgLy8gU2tpcCBmbGlja2VyIHJlcG9ydGluZyBmb3IgdGVybWluYWxzIHdpdGggc3luY2hyb25pemVkIG91dHB1dCDigJRcbiAgICAgICAgLy8gREVDIDIwMjYgYnVmZmVycyBiZXR3ZWVuIEJTVS9FU1Ugc28gY2xlYXIrcmVkcmF3IGlzIGF0b21pYy5cbiAgICAgICAgaWYgKGlzU3luY2hyb25pemVkT3V0cHV0U3VwcG9ydGVkKCkpIHtcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBmb3IgKGNvbnN0IGZsaWNrZXIgb2YgZXZlbnQuZmxpY2tlcnMpIHtcbiAgICAgICAgICBpZiAoZmxpY2tlci5yZWFzb24gPT09ICdyZXNpemUnKSB7XG4gICAgICAgICAgICBjb250aW51ZVxuICAgICAgICAgIH1cbiAgICAgICAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpXG4gICAgICAgICAgaWYgKG5vdyAtIGxhc3RGbGlja2VyVGltZSA8IDEwMDApIHtcbiAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9mbGlja2VyJywge1xuICAgICAgICAgICAgICBkZXNpcmVkSGVpZ2h0OiBmbGlja2VyLmRlc2lyZWRIZWlnaHQsXG4gICAgICAgICAgICAgIGFjdHVhbEhlaWdodDogZmxpY2tlci5hdmFpbGFibGVIZWlnaHQsXG4gICAgICAgICAgICAgIHJlYXNvbjogZmxpY2tlci5yZWFzb24sXG4gICAgICAgICAgICB9IGFzIHVua25vd24gYXMgUmVjb3JkPHN0cmluZywgYm9vbGVhbiB8IG51bWJlciB8IHVuZGVmaW5lZD4pXG4gICAgICAgICAgfVxuICAgICAgICAgIGxhc3RGbGlja2VyVGltZSA9IG5vd1xuICAgICAgICB9XG4gICAgICB9LFxuICAgIH0sXG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsT0FBTyxRQUFRLFlBQVk7QUFDcEMsU0FBU0MsY0FBYyxRQUFRLElBQUk7QUFDbkMsT0FBT0MsS0FBSyxNQUFNLE9BQU87QUFDekIsU0FBU0MsUUFBUSxRQUFRLGlDQUFpQztBQUMxRCxTQUNFQyxnQkFBZ0IsRUFDaEJDLG9CQUFvQixRQUNmLCtCQUErQjtBQUN0QyxTQUNFLEtBQUtDLFlBQVksRUFDakJDLGtCQUFrQixFQUNsQkMsa0JBQWtCLEVBQ2xCQyxpQkFBaUIsRUFDakJDLHVCQUF1QixFQUN2QkMsYUFBYSxRQUNSLHNCQUFzQjtBQUM3QixjQUFjQyxPQUFPLFFBQVEsZUFBZTtBQUM1QyxTQUFTQyxnQkFBZ0IsRUFBRSxLQUFLQyxVQUFVLFFBQVEsb0JBQW9CO0FBQ3RFLFNBQVNDLGdCQUFnQixRQUFRLGNBQWM7QUFDL0MsU0FBU0MsNkJBQTZCLFFBQVEsdUJBQXVCO0FBQ3JFLFNBQVNDLDZCQUE2QixRQUFRLG1CQUFtQjtBQUNqRSxjQUFjQyxhQUFhLEVBQUVDLElBQUksRUFBRUMsU0FBUyxRQUFR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 \ No newline at end of file diff --git a/keybindings/KeybindingContext.tsx b/keybindings/KeybindingContext.tsx new file mode 100644 index 0000000..8ea317d --- /dev/null +++ b/keybindings/KeybindingContext.tsx @@ -0,0 +1,243 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { createContext, type RefObject, useContext, useLayoutEffect, useMemo } from 'react'; +import type { Key } from '../ink.js'; +import { type ChordResolveResult, getBindingDisplayText, resolveKeyWithChordState } from './resolver.js'; +import type { KeybindingContextName, ParsedBinding, ParsedKeystroke } from './types.js'; + +/** Handler registration for action callbacks */ +type HandlerRegistration = { + action: string; + context: KeybindingContextName; + handler: () => void; +}; +type KeybindingContextValue = { + /** Resolve a key input to an action name (with chord support) */ + resolve: (input: string, key: Key, activeContexts: KeybindingContextName[]) => ChordResolveResult; + + /** Update the pending chord state */ + setPendingChord: (pending: ParsedKeystroke[] | null) => void; + + /** Get display text for an action (e.g., "ctrl+t") */ + getDisplayText: (action: string, context: KeybindingContextName) => string | undefined; + + /** All parsed bindings (for help display) */ + bindings: ParsedBinding[]; + + /** Current pending chord keystrokes (null if not in a chord) */ + pendingChord: ParsedKeystroke[] | null; + + /** Currently active keybinding contexts (for priority resolution) */ + activeContexts: Set; + + /** Register a context as active (call on mount) */ + registerActiveContext: (context: KeybindingContextName) => void; + + /** Unregister a context (call on unmount) */ + unregisterActiveContext: (context: KeybindingContextName) => void; + + /** Register a handler for an action (used by useKeybinding) */ + registerHandler: (registration: HandlerRegistration) => () => void; + + /** Invoke all handlers for an action (used by ChordInterceptor) */ + invokeAction: (action: string) => boolean; +}; +const KeybindingContext = createContext(null); +type ProviderProps = { + bindings: ParsedBinding[]; + /** Ref for immediate access to pending chord (avoids React state delay) */ + pendingChordRef: RefObject; + /** State value for re-renders (UI updates) */ + pendingChord: ParsedKeystroke[] | null; + setPendingChord: (pending: ParsedKeystroke[] | null) => void; + activeContexts: Set; + registerActiveContext: (context: KeybindingContextName) => void; + unregisterActiveContext: (context: KeybindingContextName) => void; + /** Ref to handler registry (used by ChordInterceptor) */ + handlerRegistryRef: RefObject>>; + children: React.ReactNode; +}; +export function KeybindingProvider(t0) { + const $ = _c(24); + const { + bindings, + pendingChordRef, + pendingChord, + setPendingChord, + activeContexts, + registerActiveContext, + unregisterActiveContext, + handlerRegistryRef, + children + } = t0; + let t1; + if ($[0] !== bindings) { + t1 = (action, context) => getBindingDisplayText(action, context, bindings); + $[0] = bindings; + $[1] = t1; + } else { + t1 = $[1]; + } + const getDisplay = t1; + let t2; + if ($[2] !== handlerRegistryRef) { + t2 = registration => { + const registry = handlerRegistryRef.current; + if (!registry) { + return _temp; + } + if (!registry.has(registration.action)) { + registry.set(registration.action, new Set()); + } + registry.get(registration.action).add(registration); + return () => { + const handlers = registry.get(registration.action); + if (handlers) { + handlers.delete(registration); + if (handlers.size === 0) { + registry.delete(registration.action); + } + } + }; + }; + $[2] = handlerRegistryRef; + $[3] = t2; + } else { + t2 = $[3]; + } + const registerHandler = t2; + let t3; + if ($[4] !== activeContexts || $[5] !== handlerRegistryRef) { + t3 = action_0 => { + const registry_0 = handlerRegistryRef.current; + if (!registry_0) { + return false; + } + const handlers_0 = registry_0.get(action_0); + if (!handlers_0 || handlers_0.size === 0) { + return false; + } + for (const registration_0 of handlers_0) { + if (activeContexts.has(registration_0.context)) { + registration_0.handler(); + return true; + } + } + return false; + }; + $[4] = activeContexts; + $[5] = handlerRegistryRef; + $[6] = t3; + } else { + t3 = $[6]; + } + const invokeAction = t3; + let t4; + if ($[7] !== bindings || $[8] !== pendingChordRef) { + t4 = (input, key, contexts) => resolveKeyWithChordState(input, key, contexts, bindings, pendingChordRef.current); + $[7] = bindings; + $[8] = pendingChordRef; + $[9] = t4; + } else { + t4 = $[9]; + } + let t5; + if ($[10] !== activeContexts || $[11] !== bindings || $[12] !== getDisplay || $[13] !== invokeAction || $[14] !== pendingChord || $[15] !== registerActiveContext || $[16] !== registerHandler || $[17] !== setPendingChord || $[18] !== t4 || $[19] !== unregisterActiveContext) { + t5 = { + resolve: t4, + setPendingChord, + getDisplayText: getDisplay, + bindings, + pendingChord, + activeContexts, + registerActiveContext, + unregisterActiveContext, + registerHandler, + invokeAction + }; + $[10] = activeContexts; + $[11] = bindings; + $[12] = getDisplay; + $[13] = invokeAction; + $[14] = pendingChord; + $[15] = registerActiveContext; + $[16] = registerHandler; + $[17] = setPendingChord; + $[18] = t4; + $[19] = unregisterActiveContext; + $[20] = t5; + } else { + t5 = $[20]; + } + const value = t5; + let t6; + if ($[21] !== children || $[22] !== value) { + t6 = {children}; + $[21] = children; + $[22] = value; + $[23] = t6; + } else { + t6 = $[23]; + } + return t6; +} +function _temp() {} +export function useKeybindingContext() { + const ctx = useContext(KeybindingContext); + if (!ctx) { + throw new Error("useKeybindingContext must be used within KeybindingProvider"); + } + return ctx; +} + +/** + * Optional hook that returns undefined outside of KeybindingProvider. + * Useful for components that may render before provider is available. + */ +export function useOptionalKeybindingContext() { + return useContext(KeybindingContext); +} + +/** + * Hook to register a keybinding context as active while the component is mounted. + * + * When a context is registered, its keybindings take precedence over Global bindings. + * This allows context-specific bindings (like ThemePicker's ctrl+t) to override + * global bindings (like the todo toggle) when the context is active. + * + * @example + * ```tsx + * function ThemePicker() { + * useRegisterKeybindingContext('ThemePicker') + * // Now ThemePicker's ctrl+t binding takes precedence over Global + * } + * ``` + */ +export function useRegisterKeybindingContext(context, t0) { + const $ = _c(5); + const isActive = t0 === undefined ? true : t0; + const keybindingContext = useOptionalKeybindingContext(); + let t1; + let t2; + if ($[0] !== context || $[1] !== isActive || $[2] !== keybindingContext) { + t1 = () => { + if (!keybindingContext || !isActive) { + return; + } + keybindingContext.registerActiveContext(context); + return () => { + keybindingContext.unregisterActiveContext(context); + }; + }; + t2 = [context, keybindingContext, isActive]; + $[0] = context; + $[1] = isActive; + $[2] = keybindingContext; + $[3] = t1; + $[4] = t2; + } else { + t1 = $[3]; + t2 = $[4]; + } + useLayoutEffect(t1, t2); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImNyZWF0ZUNvbnRleHQiLCJSZWZPYmplY3QiLCJ1c2VDb250ZXh0IiwidXNlTGF5b3V0RWZmZWN0IiwidXNlTWVtbyIsIktleSIsIkNob3JkUmVzb2x2ZVJlc3VsdCIsImdldEJpbmRpbmdEaXNwbGF5VGV4dCIsInJlc29sdmVLZXlXaXRoQ2hvcmRTdGF0ZSIsIktleWJpbmRpbmdDb250ZXh0TmFtZSIsIlBhcnNlZEJpbmRpbmciLCJQYXJzZWRLZXlzdHJva2UiLCJIYW5kbGVyUmVnaXN0cmF0aW9uIiwiYWN0aW9uIiwiY29udGV4dCIsImhhbmRsZXIiLCJLZXliaW5kaW5nQ29udGV4dFZhbHVlIiwicmVzb2x2ZSIsImlucHV0Iiwia2V5IiwiYWN0aXZlQ29udGV4dHMiLCJzZXRQZW5kaW5nQ2hvcmQiLCJwZW5kaW5nIiwiZ2V0RGlzcGxheVRleHQiLCJiaW5kaW5ncyIsInBlbmRpbmdDaG9yZCIsIlNldCIsInJlZ2lzdGVyQWN0aXZlQ29udGV4dCIsInVucmVnaXN0ZXJBY3RpdmVDb250ZXh0IiwicmVnaXN0ZXJIYW5kbGVyIiwicmVnaXN0cmF0aW9uIiwiaW52b2tlQWN0aW9uIiwiS2V5YmluZGluZ0NvbnRleHQiLCJQcm92aWRlclByb3BzIiwicGVuZGluZ0Nob3JkUmVmIiwiaGFuZGxlclJlZ2lzdHJ5UmVmIiwiTWFwIiwiY2hpbGRyZW4iLCJSZWFjdE5vZGUiLCJLZXliaW5kaW5nUHJvdmlkZXIiLCJ0MCIsIiQiLCJfYyIsInQxIiwiZ2V0RGlzcGxheSIsInQyIiwicmVnaXN0cnkiLCJjdXJyZW50IiwiX3RlbXAiLCJoYXMiLCJzZXQiLCJnZXQiLCJhZGQiLCJoYW5kbGVycyIsImRlbGV0ZSIsInNpemUiLCJ0MyIsImFjdGlvbl8wIiwicmVnaXN0cnlfMCIsImhhbmRsZXJzXzAiLCJyZWdpc3RyYXRpb25fMCIsInQ0IiwiY29udGV4dHMiLCJ0NSIsInZhbHVlIiwidDYiLCJ1c2VLZXliaW5kaW5nQ29udGV4dCIsImN0eCIsIkVycm9yIiwidXNlT3B0aW9uYWxLZXliaW5kaW5nQ29udGV4dCIsInVzZVJlZ2lzdGVyS2V5YmluZGluZ0NvbnRleHQiLCJpc0FjdGl2ZSIsInVuZGVmaW5lZCIsImtleWJpbmRpbmdDb250ZXh0Il0sInNvdXJjZXMiOlsiS2V5YmluZGluZ0NvbnRleHQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwge1xuICBjcmVhdGVDb250ZXh0LFxuICB0eXBlIFJlZk9iamVjdCxcbiAgdXNlQ29udGV4dCxcbiAgdXNlTGF5b3V0RWZmZWN0LFxuICB1c2VNZW1vLFxufSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgS2V5IH0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBDaG9yZFJlc29sdmVSZXN1bHQsXG4gIGdldEJpbmRpbmdEaXNwbGF5VGV4dCxcbiAgcmVzb2x2ZUtleVdpdGhDaG9yZFN0YXRlLFxufSBmcm9tICcuL3Jlc29sdmVyLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBLZXliaW5kaW5nQ29udGV4dE5hbWUsXG4gIFBhcnNlZEJpbmRpbmcsXG4gIFBhcnNlZEtleXN0cm9rZSxcbn0gZnJvbSAnLi90eXBlcy5qcydcblxuLyoqIEhhbmRsZXIgcmVnaXN0cmF0aW9uIGZvciBhY3Rpb24gY2FsbGJhY2tzICovXG50eXBlIEhhbmRsZXJSZWdpc3RyYXRpb24gPSB7XG4gIGFjdGlvbjogc3RyaW5nXG4gIGNvbnRleHQ6IEtleWJpbmRpbmdDb250ZXh0TmFtZVxuICBoYW5kbGVyOiAoKSA9PiB2b2lkXG59XG5cbnR5cGUgS2V5YmluZGluZ0NvbnRleHRWYWx1ZSA9IHtcbiAgLyoqIFJlc29sdmUgYSBrZXkgaW5wdXQgdG8gYW4gYWN0aW9uIG5hbWUgKHdpdGggY2hvcmQgc3VwcG9ydCkgKi9cbiAgcmVzb2x2ZTogKFxuICAgIGlucHV0OiBzdHJpbmcsXG4gICAga2V5OiBLZXksXG4gICAgYWN0aXZlQ29udGV4dHM6IEtleWJpbmRpbmdDb250ZXh0TmFtZVtdLFxuICApID0+IENob3JkUmVzb2x2ZVJlc3VsdFxuXG4gIC8qKiBVcGRhdGUgdGhlIHBlbmRpbmcgY2hvcmQgc3RhdGUgKi9cbiAgc2V0UGVuZGluZ0Nob3JkOiAocGVuZGluZzogUGFyc2VkS2V5c3Ryb2tlW10gfCBudWxsKSA9PiB2b2lkXG5cbiAgLyoqIEdldCBkaXNwbGF5IHRleHQgZm9yIGFuIGFjdGlvbiAoZS5nLiwgXCJjdHJsK3RcIikgKi9cbiAgZ2V0RGlzcGxheVRleHQ6IChcbiAgICBhY3Rpb246IHN0cmluZyxcbiAgICBjb250ZXh0OiBLZXliaW5kaW5nQ29udGV4dE5hbWUsXG4gICkgPT4gc3RyaW5nIHwgdW5kZWZpbmVkXG5cbiAgLyoqIEFsbCBwYXJzZWQgYmluZGluZ3MgKGZvciBoZWxwIGRpc3BsYXkpICovXG4gIGJpbmRpbmdzOiBQYXJzZWRCaW5kaW5nW11cblxuICAvKiogQ3VycmVudCBwZW5kaW5nIGNob3JkIGtleXN0cm9rZXMgKG51bGwgaWYgbm90IGluIGEgY2hvcmQpICovXG4gIHBlbmRpbmdDaG9yZDogUGFyc2VkS2V5c3Ryb2tlW10gfCBudWxsXG5cbiAgLyoqIEN1cnJlbnRseSBhY3RpdmUga2V5YmluZGluZyBjb250ZXh0cyAoZm9yIHByaW9yaXR5IHJlc29sdXRpb24pICovXG4gIGFjdGl2ZUNvbnRleHRzOiBTZXQ8S2V5YmluZGluZ0NvbnRleHROYW1lPlxuXG4gIC8qKiBSZWdpc3RlciBhIGNvbnRleHQgYXMgYWN0aXZlIChjYWxsIG9uIG1vdW50KSAqL1xuICByZWdpc3RlckFjdGl2ZUNvbnRleHQ6IChjb250ZXh0OiBLZXliaW5kaW5nQ29udGV4dE5hbWUpID0+IHZvaWRcblxuICAvKiogVW5yZWdpc3RlciBhIGNvbnRleHQgKGNhbGwgb24gdW5tb3VudCkgKi9cbiAgdW5yZWdpc3RlckFjdGl2ZUNvbnRleHQ6IChjb250ZXh0OiBLZXliaW5kaW5nQ29udGV4dE5hbWUpID0+IHZvaWRcblxuICAvKiogUmVnaXN0ZXIgYSBoYW5kbGVyIGZvciBhbiBhY3Rpb24gKHVzZWQgYnkgdXNlS2V5YmluZGluZykgKi9cbiAgcmVnaXN0ZXJIYW5kbGVyOiAocmVnaXN0cmF0aW9uOiBIYW5kbGVyUmVnaXN0cmF0aW9uKSA9PiAoKSA9PiB2b2lkXG5cbiAgLyoqIEludm9rZSBhbGwgaGFuZGxlcnMgZm9yIGFuIGFjdGlvbiAodXNlZCBieSBDaG9yZEludGVyY2VwdG9yKSAqL1xuICBpbnZva2VBY3Rpb246IChhY3Rpb246IHN0cmluZykgPT4gYm9vbGVhblxufVxuXG5jb25zdCBLZXliaW5kaW5nQ29udGV4dCA9IGNyZWF0ZUNvbnRleHQ8S2V5YmluZGluZ0NvbnRleHRWYWx1ZSB8IG51bGw+KG51bGwpXG5cbnR5cGUgUHJvdmlkZXJQcm9wcyA9IHtcbiAgYmluZGluZ3M6IFBhcnNlZEJpbmRpbmdbXVxuICAvKiogUmVmIGZvciBpbW1lZGlhdGUgYWNjZXNzIHRvIHBlbmRpbmcgY2hvcmQgKGF2b2lkcyBSZWFjdCBzdGF0ZSBkZWxheSkgKi9cbiAgcGVuZGluZ0Nob3JkUmVmOiBSZWZPYmplY3Q8UGFyc2VkS2V5c3Ryb2tlW10gfCBudWxsPlxuICAvKiogU3RhdGUgdmFsdWUgZm9yIHJlLXJlbmRlcnMgKFVJIHVwZGF0ZXMpICovXG4gIHBlbmRpbmdDaG9yZDogUGFyc2VkS2V5c3Ryb2tlW10gfCBudWxsXG4gIHNldFBlbmRpbmdDaG9yZDogKHBlbmRpbmc6IFBhcnNlZEtleXN0cm9rZVtdIHwgbnVsbCkgPT4gdm9pZFxuICBhY3RpdmVDb250ZXh0czogU2V0PEtleWJpbmRpbmdDb250ZXh0TmFtZT5cbiAgcmVnaXN0ZXJBY3RpdmVDb250ZXh0OiAoY29udGV4dDogS2V5YmluZGluZ0NvbnRleHROYW1lKSA9PiB2b2lkXG4gIHVucmVnaXN0ZXJBY3RpdmVDb250ZXh0OiAoY29udGV4dDogS2V5YmluZGluZ0NvbnRleHROYW1lKSA9PiB2b2lkXG4gIC8qKiBSZWYgdG8gaGFuZGxlciByZWdpc3RyeSAodXNlZCBieSBDaG9yZEludGVyY2VwdG9yKSAqL1xuICBoYW5kbGVyUmVnaXN0cnlSZWY6IFJlZk9iamVjdDxNYXA8c3RyaW5nLCBTZXQ8SGFuZGxlclJlZ2lzdHJhdGlvbj4+PlxuICBjaGlsZHJlbjogUmVhY3QuUmVhY3ROb2RlXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBLZXliaW5kaW5nUHJvdmlkZXIoe1xuICBiaW5kaW5ncyxcbiAgcGVuZGluZ0Nob3JkUmVmLFxuICBwZW5kaW5nQ2hvcmQsXG4gIHNldFBlbmRpbmdDaG9yZCxcbiAgYWN0aXZlQ29udGV4dHMsXG4gIHJlZ2lzdGVyQWN0aXZlQ29udGV4dCxcbiAgdW5yZWdpc3RlckFjdGl2ZUNvbnRleHQsXG4gIGhhbmRsZXJSZWdpc3RyeVJlZixcbiAgY2hpbGRyZW4sXG59OiBQcm92aWRlclByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgdmFsdWUgPSB1c2VNZW1vPEtleWJpbmRpbmdDb250ZXh0VmFsdWU+KCgpID0+IHtcbiAgICBjb25zdCBnZXREaXNwbGF5ID0gKGFjdGlvbjogc3RyaW5nLCBjb250ZXh0OiBLZXliaW5kaW5nQ29udGV4dE5hbWUpID0+XG4gICAgICBnZXRCaW5kaW5nRGlzcGxheVRleHQoYWN0aW9uLCBjb250ZXh0LCBiaW5kaW5ncylcblxuICAgIC8vIFJlZ2lzdGVyIGEgaGFuZGxlciBmb3IgYW4gYWN0aW9uXG4gICAgY29uc3QgcmVnaXN0ZXJIYW5kbGVyID0gKHJlZ2lzdHJhdGlvbjogSGFuZGxlclJlZ2lzdHJhdGlvbikgPT4ge1xuICAgICAgY29uc3QgcmVnaXN0cnkgPSBoYW5kbGVyUmVnaXN0cnlSZWYuY3VycmVudFxuICAgICAgaWYgKCFyZWdpc3RyeSkgcmV0dXJuICgpID0+IHt9XG5cbiAgICAgIGlmICghcmVnaXN0cnkuaGFzKHJlZ2lzdHJhdGlvbi5hY3Rpb24pKSB7XG4gICAgICAgIHJlZ2lzdHJ5LnNldChyZWdpc3RyYXRpb24uYWN0aW9uLCBuZXcgU2V0KCkpXG4gICAgICB9XG4gICAgICByZWdpc3RyeS5nZXQocmVnaXN0cmF0aW9uLmFjdGlvbikhLmFkZChyZWdpc3RyYXRpb24pXG5cbiAgICAgIC8vIFJldHVybiB1bnJlZ2lzdGVyIGZ1bmN0aW9uXG4gICAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBjb25zdCBoYW5kbGVycyA9IHJlZ2lzdHJ5LmdldChyZWdpc3RyYXRpb24uYWN0aW9uKVxuICAgICAgICBpZiAoaGFuZGxlcnMpIHtcbiAgICAgICAgICBoYW5kbGVycy5kZWxldGUocmVnaXN0cmF0aW9uKVxuICAgICAgICAgIGlmIChoYW5kbGVycy5zaXplID09PSAwKSB7XG4gICAgICAgICAgICByZWdpc3RyeS5kZWxldGUocmVnaXN0cmF0aW9uLmFjdGlvbilcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBJbnZva2UgYWxsIGhhbmRsZXJzIGZvciBhbiBhY3Rpb25cbiAgICBjb25zdCBpbnZva2VBY3Rpb24gPSAoYWN0aW9uOiBzdHJpbmcpOiBib29sZWFuID0+IHtcbiAgICAgIGNvbnN0IHJlZ2lzdHJ5ID0gaGFuZGxlclJlZ2lzdHJ5UmVmLmN1cnJlbnRcbiAgICAgIGlmICghcmVnaXN0cnkpIHJldHVybiBmYWxzZVxuXG4gICAgICBjb25zdCBoYW5kbGVycyA9IHJlZ2lzdHJ5LmdldChhY3Rpb24pXG4gICAgICBpZiAoIWhhbmRsZXJzIHx8IGhhbmRsZXJzLnNpemUgPT09IDApIHJldHVybiBmYWxzZVxuXG4gICAgICAvLyBGaW5kIGhhbmRsZXJzIHdob3NlIGNvbnRleHQgaXMgYWN0aXZlXG4gICAgICBmb3IgKGNvbnN0IHJlZ2lzdHJhdGlvbiBvZiBoYW5kbGVycykge1xuICAgICAgICBpZiAoYWN0aXZlQ29udGV4dHMuaGFzKHJlZ2lzdHJhdGlvbi5jb250ZXh0KSkge1xuICAgICAgICAgIHJlZ2lzdHJhdGlvbi5oYW5kbGVyKClcbiAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgLy8gVXNlIHJlZiBmb3IgaW1tZWRpYXRlIGFjY2VzcyB0byBwZW5kaW5nIGNob3JkLCBhdm9pZGluZyBSZWFjdCBzdGF0ZSBkZWxheVxuICAgICAgLy8gVGhpcyBpcyBjcml0aWNhbCBmb3IgY2hvcmQgc2VxdWVuY2VzIHdoZXJlIHRoZSBzZWNvbmQga2V5IG1pZ2h0IGJlIHByZXNzZWRcbiAgICAgIC8vIGJlZm9yZSBSZWFjdCByZS1yZW5kZXJzIHdpdGggdGhlIHVwZGF0ZWQgcGVuZGluZ0Nob3JkIHN0YXRlXG4gICAgICByZXNvbHZlOiAoaW5wdXQsIGtleSwgY29udGV4dHMpID0+XG4gICAgICAgIHJlc29sdmVLZXlXaXRoQ2hvcmRTdGF0ZShcbiAgICAgICAgICBpbnB1dCxcbiAgICAgICAgICBrZXksXG4gICAgICAgICAgY29udGV4dHMsXG4gICAgICAgICAgYmluZGluZ3MsXG4gICAgICAgICAgcGVuZGluZ0Nob3JkUmVmLmN1cnJlbnQsXG4gICAgICAgICksXG4gICAgICBzZXRQZW5kaW5nQ2hvcmQsXG4gICAgICBnZXREaXNwbGF5VGV4dDogZ2V0RGlzcGxheSxcbiAgICAgIGJpbmRpbmdzLFxuICAgICAgcGVuZGluZ0Nob3JkLFxuICAgICAgYWN0aXZlQ29udGV4dHMsXG4gICAgICByZWdpc3RlckFjdGl2ZUNvbnRleHQsXG4gICAgICB1bnJlZ2lzdGVyQWN0aXZlQ29udGV4dCxcbiAgICAgIHJlZ2lzdGVySGFuZGxlcixcbiAgICAgIGludm9rZUFjdGlvbixcbiAgICB9XG4gIH0sIFtcbiAgICBiaW5kaW5ncyxcbiAgICBwZW5kaW5nQ2hvcmRSZWYsXG4gICAgcGVuZGluZ0Nob3JkLFxuICAgIHNldFBlbmRpbmdDaG9yZCxcbiAgICBhY3RpdmVDb250ZXh0cyxcbiAgICByZWdpc3RlckFjdGl2ZUNvbnRleHQsXG4gICAgdW5yZWdpc3RlckFjdGl2ZUNvbnRleHQsXG4gICAgaGFuZGxlclJlZ2lzdHJ5UmVmLFxuICBdKVxuXG4gIHJldHVybiAoXG4gICAgPEtleWJpbmRpbmdDb250ZXh0LlByb3ZpZGVyIHZhbHVlPXt2YWx1ZX0+XG4gICAgICB7Y2hpbGRyZW59XG4gICAgPC9LZXliaW5kaW5nQ29udGV4dC5Qcm92aWRlcj5cbiAgKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gdXNlS2V5YmluZGluZ0NvbnRleHQoKTogS2V5YmluZGluZ0NvbnRleHRWYWx1ZSB7XG4gIGNvbnN0IGN0eCA9IHVzZUNvbnRleHQoS2V5YmluZGluZ0NvbnRleHQpXG4gIGlmICghY3R4KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgJ3VzZUtleWJpbmRpbmdDb250ZXh0IG11c3QgYmUgdXNlZCB3aXRoaW4gS2V5YmluZGluZ1Byb3ZpZGVyJyxcbiAgICApXG4gIH1cbiAgcmV0dXJuIGN0eFxufVxuXG4vKipcbiAqIE9wdGlvbmFsIGhvb2sgdGhhdCByZXR1cm5zIHVuZGVmaW5lZCBvdXRzaWRlIG9mIEtleWJpbmRpbmdQcm92aWRlci5cbiAqIFVzZWZ1bCBmb3IgY29tcG9uZW50cyB0aGF0IG1heSByZW5kZXIgYmVmb3JlIHByb3ZpZGVyIGlzIGF2YWlsYWJsZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZU9wdGlvbmFsS2V5YmluZGluZ0NvbnRleHQoKTogS2V5YmluZGluZ0NvbnRleHRWYWx1ZSB8IG51bGwge1xuICByZXR1cm4gdXNlQ29udGV4dChLZXliaW5kaW5nQ29udGV4dClcbn1cblxuLyoqXG4gKiBIb29rIHRvIHJlZ2lzdGVyIGEga2V5YmluZGluZyBjb250ZXh0IGFzIGFjdGl2ZSB3aGlsZSB0aGUgY29tcG9uZW50IGlzIG1vdW50ZWQuXG4gKlxuICogV2hlbiBhIGNvbnRleHQgaXMgcmVnaXN0ZXJlZCwgaXRzIGtleWJpbmRpbmdzIHRha2UgcHJlY2VkZW5jZSBvdmVyIEdsb2JhbCBiaW5kaW5ncy5cbiAqIFRoaXMgYWxsb3dzIGNvbnRleHQtc3BlY2lmaWMgYmluZGluZ3MgKGxpa2UgVGhlbWVQaWNrZXIncyBjdHJsK3QpIHRvIG92ZXJyaWRlXG4gKiBnbG9iYWwgYmluZGluZ3MgKGxpa2UgdGhlIHRvZG8gdG9nZ2xlKSB3aGVuIHRoZSBjb250ZXh0IGlzIGFjdGl2ZS5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHN4XG4gKiBmdW5jdGlvbiBUaGVtZVBpY2tlcigpIHtcbiAqICAgdXNlUmVnaXN0ZXJLZXliaW5kaW5nQ29udGV4dCgnVGhlbWVQaWNrZXInKVxuICogICAvLyBOb3cgVGhlbWVQaWNrZXIncyBjdHJsK3QgYmluZGluZyB0YWtlcyBwcmVjZWRlbmNlIG92ZXIgR2xvYmFsXG4gKiB9XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZVJlZ2lzdGVyS2V5YmluZGluZ0NvbnRleHQoXG4gIGNvbnRleHQ6IEtleWJpbmRpbmdDb250ZXh0TmFtZSxcbiAgaXNBY3RpdmU6IGJvb2xlYW4gPSB0cnVlLFxuKTogdm9pZCB7XG4gIGNvbnN0IGtleWJpbmRpbmdDb250ZXh0ID0gdXNlT3B0aW9uYWxLZXliaW5kaW5nQ29udGV4dCgpXG5cbiAgdXNlTGF5b3V0RWZmZWN0KCgpID0+IHtcbiAgICBpZiAoIWtleWJpbmRpbmdDb250ZXh0IHx8ICFpc0FjdGl2ZSkgcmV0dXJuXG5cbiAgICBrZXliaW5kaW5nQ29udGV4dC5yZWdpc3RlckFjdGl2ZUNvbnRleHQoY29udGV4dClcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAga2V5YmluZGluZ0NvbnRleHQudW5yZWdpc3RlckFjdGl2ZUNvbnRleHQoY29udGV4dClcbiAgICB9XG4gIH0sIFtjb250ZXh0LCBrZXliaW5kaW5nQ29udGV4dCwgaXNBY3RpdmVdKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUNWQyxhQUFhLEVBQ2IsS0FBS0MsU0FBUyxFQUNkQyxVQUFVLEVBQ1ZDLGVBQWUsRUFDZkMsT0FBTyxRQUNGLE9BQU87QUFDZCxjQUFjQyxHQUFHLFFBQVEsV0FBVztBQUNwQyxTQUNFLEtBQUtDLGtCQUFrQixFQUN2QkMscUJBQXFCLEVBQ3JCQyx3QkFBd0IsUUFDbkIsZUFBZTtBQUN0QixjQUNFQyxxQkFBcUIs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 \ No newline at end of file diff --git a/keybindings/KeybindingProviderSetup.tsx b/keybindings/KeybindingProviderSetup.tsx new file mode 100644 index 0000000..84817ea --- /dev/null +++ b/keybindings/KeybindingProviderSetup.tsx @@ -0,0 +1,308 @@ +import { c as _c } from "react/compiler-runtime"; +/** + * Setup utilities for integrating KeybindingProvider into the app. + * + * This file provides the bindings and a composed provider that can be + * added to the app's component tree. It loads both default bindings and + * user-defined bindings from ~/.claude/keybindings.json, with hot-reload + * support when the file changes. + */ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useNotifications } from '../context/notifications.js'; +import type { InputEvent } from '../ink/events/input-event.js'; +// ChordInterceptor intentionally uses useInput to intercept all keystrokes before +// other handlers process them - this is required for chord sequence support +// eslint-disable-next-line custom-rules/prefer-use-keybindings +import { type Key, useInput } from '../ink.js'; +import { count } from '../utils/array.js'; +import { logForDebugging } from '../utils/debug.js'; +import { plural } from '../utils/stringUtils.js'; +import { KeybindingProvider } from './KeybindingContext.js'; +import { initializeKeybindingWatcher, type KeybindingsLoadResult, loadKeybindingsSyncWithWarnings, subscribeToKeybindingChanges } from './loadUserBindings.js'; +import { resolveKeyWithChordState } from './resolver.js'; +import type { KeybindingContextName, ParsedBinding, ParsedKeystroke } from './types.js'; +import type { KeybindingWarning } from './validate.js'; + +/** + * Timeout for chord sequences in milliseconds. + * If the user doesn't complete the chord within this time, it's cancelled. + */ +const CHORD_TIMEOUT_MS = 1000; +type Props = { + children: React.ReactNode; +}; + +/** + * Keybinding provider with default + user bindings and hot-reload support. + * + * Usage: Wrap your app with this provider to enable keybinding support. + * + * ```tsx + * + * + * + * + * + * ``` + * + * Features: + * - Loads default bindings from code + * - Merges with user bindings from ~/.claude/keybindings.json + * - Watches for file changes and reloads automatically (hot-reload) + * - User bindings override defaults (later entries win) + * - Chord support with automatic timeout + */ +/** + * Display keybinding warnings to the user via notifications. + * Shows a brief message pointing to /doctor for details. + */ +function useKeybindingWarnings(warnings, isReload) { + const $ = _c(9); + const { + addNotification, + removeNotification + } = useNotifications(); + let t0; + if ($[0] !== addNotification || $[1] !== removeNotification || $[2] !== warnings) { + t0 = () => { + if (warnings.length === 0) { + removeNotification("keybinding-config-warning"); + return; + } + const errorCount = count(warnings, _temp); + const warnCount = count(warnings, _temp2); + let message; + if (errorCount > 0 && warnCount > 0) { + message = `Found ${errorCount} keybinding ${plural(errorCount, "error")} and ${warnCount} ${plural(warnCount, "warning")}`; + } else { + if (errorCount > 0) { + message = `Found ${errorCount} keybinding ${plural(errorCount, "error")}`; + } else { + message = `Found ${warnCount} keybinding ${plural(warnCount, "warning")}`; + } + } + message = message + " \xB7 /doctor for details"; + addNotification({ + key: "keybinding-config-warning", + text: message, + color: errorCount > 0 ? "error" : "warning", + priority: errorCount > 0 ? "immediate" : "high", + timeoutMs: 60000 + }); + }; + $[0] = addNotification; + $[1] = removeNotification; + $[2] = warnings; + $[3] = t0; + } else { + t0 = $[3]; + } + let t1; + if ($[4] !== addNotification || $[5] !== isReload || $[6] !== removeNotification || $[7] !== warnings) { + t1 = [warnings, isReload, addNotification, removeNotification]; + $[4] = addNotification; + $[5] = isReload; + $[6] = removeNotification; + $[7] = warnings; + $[8] = t1; + } else { + t1 = $[8]; + } + useEffect(t0, t1); +} +function _temp2(w_0) { + return w_0.severity === "warning"; +} +function _temp(w) { + return w.severity === "error"; +} +export function KeybindingSetup({ + children +}: Props): React.ReactNode { + // Load bindings synchronously for initial render + const [{ + bindings, + warnings + }, setLoadResult] = useState(() => { + const result = loadKeybindingsSyncWithWarnings(); + logForDebugging(`[keybindings] KeybindingSetup initialized with ${result.bindings.length} bindings, ${result.warnings.length} warnings`); + return result; + }); + + // Track if this is a reload (not initial load) + const [isReload, setIsReload] = useState(false); + + // Display warnings via notifications + useKeybindingWarnings(warnings, isReload); + + // Chord state management - use ref for immediate access, state for re-renders + // The ref is used by resolve() to get the current value without waiting for re-render + // The state is used to trigger re-renders when needed (e.g., for UI updates) + const pendingChordRef = useRef(null); + const [pendingChord, setPendingChordState] = useState(null); + const chordTimeoutRef = useRef(null); + + // Handler registry for action callbacks (used by ChordInterceptor to invoke handlers) + const handlerRegistryRef = useRef(new Map void; + }>>()); + + // Active context tracking for keybinding priority resolution + // Using a ref instead of state for synchronous updates - input handlers need + // to see the current value immediately, not after a React render cycle. + const activeContextsRef = useRef>(new Set()); + const registerActiveContext = useCallback((context: KeybindingContextName) => { + activeContextsRef.current.add(context); + }, []); + const unregisterActiveContext = useCallback((context_0: KeybindingContextName) => { + activeContextsRef.current.delete(context_0); + }, []); + + // Clear chord timeout when component unmounts or chord changes + const clearChordTimeout = useCallback(() => { + if (chordTimeoutRef.current) { + clearTimeout(chordTimeoutRef.current); + chordTimeoutRef.current = null; + } + }, []); + + // Wrapper for setPendingChord that manages timeout and syncs ref+state + const setPendingChord = useCallback((pending: ParsedKeystroke[] | null) => { + clearChordTimeout(); + if (pending !== null) { + // Set timeout to cancel chord if not completed + chordTimeoutRef.current = setTimeout((pendingChordRef_0, setPendingChordState_0) => { + logForDebugging('[keybindings] Chord timeout - cancelling'); + pendingChordRef_0.current = null; + setPendingChordState_0(null); + }, CHORD_TIMEOUT_MS, pendingChordRef, setPendingChordState); + } + + // Update ref immediately for synchronous access in resolve() + pendingChordRef.current = pending; + // Update state to trigger re-renders for UI updates + setPendingChordState(pending); + }, [clearChordTimeout]); + useEffect(() => { + // Initialize file watcher (idempotent - only runs once) + void initializeKeybindingWatcher(); + + // Subscribe to changes + const unsubscribe = subscribeToKeybindingChanges(result_0 => { + // Any callback invocation is a reload since initial load happens + // synchronously in useState, not via this subscription + setIsReload(true); + setLoadResult(result_0); + logForDebugging(`[keybindings] Reloaded: ${result_0.bindings.length} bindings, ${result_0.warnings.length} warnings`); + }); + return () => { + unsubscribe(); + clearChordTimeout(); + }; + }, [clearChordTimeout]); + return + + {children} + ; +} + +/** + * Global chord interceptor that registers useInput FIRST (before children). + * + * This component intercepts keystrokes that are part of chord sequences and + * stops propagation before other handlers (like PromptInput) can see them. + * + * Without this, the second key of a chord (e.g., 'r' in "ctrl+c r") would be + * captured by PromptInput and added to the input field before the keybinding + * system could recognize it as completing a chord. + */ +type HandlerRegistration = { + action: string; + context: KeybindingContextName; + handler: () => void; +}; +function ChordInterceptor(t0) { + const $ = _c(6); + const { + bindings, + pendingChordRef, + setPendingChord, + activeContexts, + handlerRegistryRef + } = t0; + let t1; + if ($[0] !== activeContexts || $[1] !== bindings || $[2] !== handlerRegistryRef || $[3] !== pendingChordRef || $[4] !== setPendingChord) { + t1 = (input, key, event) => { + if ((key.wheelUp || key.wheelDown) && pendingChordRef.current === null) { + return; + } + const registry = handlerRegistryRef.current; + const handlerContexts = new Set(); + if (registry) { + for (const handlers of registry.values()) { + for (const registration of handlers) { + handlerContexts.add(registration.context); + } + } + } + const contexts = [...handlerContexts, ...activeContexts, "Global"]; + const wasInChord = pendingChordRef.current !== null; + const result = resolveKeyWithChordState(input, key, contexts, bindings, pendingChordRef.current); + bb23: switch (result.type) { + case "chord_started": + { + setPendingChord(result.pending); + event.stopImmediatePropagation(); + break bb23; + } + case "match": + { + setPendingChord(null); + if (wasInChord) { + const contextsSet = new Set(contexts); + if (registry) { + const handlers_0 = registry.get(result.action); + if (handlers_0 && handlers_0.size > 0) { + for (const registration_0 of handlers_0) { + if (contextsSet.has(registration_0.context)) { + registration_0.handler(); + event.stopImmediatePropagation(); + break; + } + } + } + } + } + break bb23; + } + case "chord_cancelled": + { + setPendingChord(null); + event.stopImmediatePropagation(); + break bb23; + } + case "unbound": + { + setPendingChord(null); + event.stopImmediatePropagation(); + break bb23; + } + case "none": + } + }; + $[0] = activeContexts; + $[1] = bindings; + $[2] = handlerRegistryRef; + $[3] = pendingChordRef; + $[4] = setPendingChord; + $[5] = t1; + } else { + t1 = $[5]; + } + const handleInput = t1; + useInput(handleInput); + return null; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUNhbGxiYWNrIiwidXNlRWZmZWN0IiwidXNlUmVmIiwidXNlU3RhdGUiLCJ1c2VOb3RpZmljYXRpb25zIiwiSW5wdXRFdmVudCIsIktleSIsInVzZUlucHV0IiwiY291bnQiLCJsb2dGb3JEZWJ1Z2dpbmciLCJwbHVyYWwiLCJLZXliaW5kaW5nUHJvdmlkZXIiLCJpbml0aWFsaXplS2V5YmluZGluZ1dhdGNoZXIiLCJLZXliaW5kaW5nc0xvYWRSZXN1bHQiLCJsb2FkS2V5YmluZGluZ3NTeW5jV2l0aFdhcm5pbmdzIiwic3Vic2NyaWJlVG9LZXliaW5kaW5nQ2hhbmdlcyIsInJlc29sdmVLZXlXaXRoQ2hvcmRTdGF0ZSIsIktleWJpbmRpbmdDb250ZXh0TmFtZSIsIlBhcnNlZEJpbmRpbmciLCJQYXJzZWRLZXlzdHJva2UiLCJLZXliaW5kaW5nV2FybmluZyIsIkNIT1JEX1RJTUVPVVRfTVMiLCJQcm9wcyIsImNoaWxkcmVuIiwiUmVhY3ROb2RlIiwidXNlS2V5YmluZGluZ1dhcm5pbmdzIiwid2FybmluZ3MiLCJpc1JlbG9hZCIsIiQiLCJfYyIsImFkZE5vdGlmaWNhdGlvbiIsInJlbW92ZU5vdGlmaWNhdGlvbiIsInQwIiwibGVuZ3RoIiwiZXJyb3JDb3VudCIsIl90ZW1wIiwid2FybkNvdW50IiwiX3RlbXAyIiwibWVzc2FnZSIsImtleSIsInRleHQiLCJjb2xvciIsInByaW9yaXR5IiwidGltZW91dE1zIiwidDEiLCJ3XzAiLCJ3Iiwic2V2ZXJpdHkiLCJLZXliaW5kaW5nU2V0dXAiLCJiaW5kaW5ncyIsInNldExvYWRSZXN1bHQiLCJyZXN1bHQiLCJzZXRJc1JlbG9hZCIsInBlbmRpbmdDaG9yZFJlZiIsInBlbmRpbmdDaG9yZCIsInNldFBlbmRpbmdDaG9yZFN0YXRlIiwiY2hvcmRUaW1lb3V0UmVmIiwiTm9kZUpTIiwiVGltZW91dCIsImhhbmRsZXJSZWdpc3RyeVJlZiIsIk1hcCIsIlNldCIsImFjdGlvbiIsImNvbnRleHQiLCJoYW5kbGVyIiwiYWN0aXZlQ29udGV4dHNSZWYiLCJyZWdpc3RlckFjdGl2ZUNvbnRleHQiLCJjdXJyZW50IiwiYWRkIiwidW5yZWdpc3RlckFjdGl2ZUNvbnRleHQiLCJkZWxldGUiLCJjbGVhckNob3JkVGltZW91dCIsImNsZWFyVGltZW91dCIsInNldFBlbmRpbmdDaG9yZCIsInBlbmRpbmciLCJzZXRUaW1lb3V0IiwidW5zdWJzY3JpYmUiLCJIYW5kbGVyUmVnaXN0cmF0aW9uIiwiQ2hvcmRJbnRlcmNlcHRvciIsImFjdGl2ZUNvbnRleHRzIiwiaW5wdXQiLCJldmVudCIsIndoZWVsVXAiLCJ3aGVlbERvd24iLCJyZWdpc3RyeSIsImhhbmRsZXJDb250ZXh0cyIsImhhbmRsZXJzIiwidmFsdWVzIiwicmVnaXN0cmF0aW9uIiwiY29udGV4dHMiLCJ3YXNJbkNob3JkIiwiYmIyMyIsInR5cGUiLCJzdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24iLCJjb250ZXh0c1NldCIsImhhbmRsZXJzXzAiLCJnZXQiLCJzaXplIiwicmVnaXN0cmF0aW9uXzAiLCJoYXMiLCJoYW5kbGVJbnB1dCJdLCJzb3VyY2VzIjpbIktleWJpbmRpbmdQcm92aWRlclNldHVwLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFNldHVwIHV0aWxpdGllcyBmb3IgaW50ZWdyYXRpbmcgS2V5YmluZGluZ1Byb3ZpZGVyIGludG8gdGhlIGFwcC5cbiAqXG4gKiBUaGlzIGZpbGUgcHJvdmlkZXMgdGhlIGJpbmRpbmdzIGFuZCBhIGNvbXBvc2VkIHByb3ZpZGVyIHRoYXQgY2FuIGJlXG4gKiBhZGRlZCB0byB0aGUgYXBwJ3MgY29tcG9uZW50IHRyZWUuIEl0IGxvYWRzIGJvdGggZGVmYXVsdCBiaW5kaW5ncyBhbmRcbiAqIHVzZXItZGVmaW5lZCBiaW5kaW5ncyBmcm9tIH4vLmNsYXVkZS9rZXliaW5kaW5ncy5qc29uLCB3aXRoIGhvdC1yZWxvYWRcbiAqIHN1cHBvcnQgd2hlbiB0aGUgZmlsZSBjaGFuZ2VzLlxuICovXG5pbXBvcnQgUmVhY3QsIHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJy4uL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB0eXBlIHsgSW5wdXRFdmVudCB9IGZyb20gJy4uL2luay9ldmVudHMvaW5wdXQtZXZlbnQuanMnXG4vLyBDaG9yZEludGVyY2VwdG9yIGludGVudGlvbmFsbHkgdXNlcyB1c2VJbnB1dCB0byBpbnRlcmNlcHQgYWxsIGtleXN0cm9rZXMgYmVmb3JlXG4vLyBvdGhlciBoYW5kbGVycyBwcm9jZXNzIHRoZW0gLSB0aGlzIGlzIHJlcXVpcmVkIGZvciBjaG9yZCBzZXF1ZW5jZSBzdXBwb3J0XG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL3ByZWZlci11c2Uta2V5YmluZGluZ3NcbmltcG9ydCB7IHR5cGUgS2V5LCB1c2VJbnB1dCB9IGZyb20gJy4uL2luay5qcydcbmltcG9ydCB7IGNvdW50IH0gZnJvbSAnLi4vdXRpbHMvYXJyYXkuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IHBsdXJhbCB9IGZyb20gJy4uL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuaW1wb3J0IHsgS2V5YmluZGluZ1Byb3ZpZGVyIH0gZnJvbSAnLi9LZXliaW5kaW5nQ29udGV4dC5qcydcbmltcG9ydCB7XG4gIGluaXRpYWxpemVLZXliaW5kaW5nV2F0Y2hlcixcbiAgdHlwZSBLZXliaW5kaW5nc0xvYWRSZXN1bHQsXG4gIGxvYWRLZXliaW5kaW5nc1N5bmNXaXRoV2FybmluZ3MsXG4gIHN1YnNjcmliZVRvS2V5YmluZGluZ0NoYW5nZXMsXG59IGZyb20gJy4vbG9hZFVzZXJCaW5kaW5ncy5qcydcbmltcG9ydCB7IHJlc29sdmVLZXlXaXRoQ2hvcmRTdGF0ZSB9IGZyb20gJy4vcmVzb2x2ZXIuanMnXG5pbXBvcnQgdHlwZSB7XG4gIEtleWJpbmRpbmdDb250ZXh0TmFtZSxcbiAgUGFyc2VkQmluZGluZyxcbiAgUGFyc2VkS2V5c3Ryb2tlLFxufSBmcm9tICcuL3R5cGVzLmpzJ1xuaW1wb3J0IHR5cGUgeyBLZXliaW5kaW5nV2FybmluZyB9IGZyb20gJy4vdmFsaWRhdGUuanMnXG5cbi8qKlxuICogVGltZW91dCBmb3IgY2hvcmQgc2VxdWVuY2VzIGluIG1pbGxpc2Vjb25kcy5cbiAqIElmIHRoZSB1c2VyIGRvZXNuJ3QgY29tcGxldGUgdGhlIGNob3JkIHdpdGhpbiB0aGlzIHRpbWUsIGl0J3MgY2FuY2VsbGVkLlxuICovXG5jb25zdCBDSE9SRF9USU1FT1VUX01TID0gMTAwMFxuXG50eXBlIFByb3BzID0ge1xuICBjaGlsZHJlbjogUmVhY3QuUmVhY3ROb2RlXG59XG5cbi8qKlxuICogS2V5YmluZGluZyBwcm92aWRlciB3aXRoIGRlZmF1bHQgKyB1c2VyIGJpbmRpbmdzIGFuZCBob3QtcmVsb2FkIHN1cHBvcnQuXG4gKlxuICogVXNhZ2U6IFdyYXAgeW91ciBhcHAgd2l0aCB0aGlzIHByb3ZpZGVyIHRvIGVuYWJsZSBrZXliaW5kaW5nIHN1cHBvcnQuXG4gKlxuICogYGBgdHN4XG4gKiA8QXBwU3RhdGVQcm92aWRlcj5cbiAqICAgPEtleWJpbmRpbmdTZXR1cD5cbiAqICAgICA8UkVQTCAuLi4gLz5cbiAqICAgPC9LZXliaW5kaW5nU2V0dXA+XG4gKiA8L0FwcFN0YXRlUHJvdmlkZXI+XG4gKiBgYGBcbiAqXG4gKiBGZWF0dXJlczpcbiAqIC0gTG9hZHMgZGVmYXVsdCBiaW5kaW5ncyBmcm9tIGNvZGVcbiAqIC0gTWVyZ2VzIHdpdGggdXNlciBiaW5kaW5ncyBmcm9tIH4vLmNsYXVkZS9rZXliaW5kaW5ncy5qc29uXG4gKiAtIFdhdGNoZXMgZm9yIGZpbGUgY2hhbmdlcyBhbmQgcmVsb2FkcyBhdXRvbWF0aWNhbGx5IChob3QtcmVsb2FkKVxuICogLSBVc2VyIGJpbmRpbmdzIG92ZXJyaWRlIGRlZmF1bHRzIChsYXRlciBlbnRyaWVzIHdpbilcbiAqIC0gQ2hvcmQgc3VwcG9ydCB3aXRoIGF1dG9tYXRpYyB0aW1lb3V0XG4gKi9cbi8qKlxuICogRGlzcGxheSBrZXliaW5kaW5nIHdhcm5pbmdzIHRvIHRoZSB1c2VyIHZpYSBub3RpZmljYXRpb25zLlxuICogU2hvd3MgYSBicmllZiBtZXNzYWdlIHBvaW50aW5nIHRvIC9kb2N0b3IgZm9yIGRldGFpbHMuXG4gKi9cbmZ1bmN0aW9uIHVzZUtleWJpbmRpbmdXYXJuaW5ncyhcbiAgd2FybmluZ3M6IEtleWJpbmRpbmdXYXJuaW5nW10sXG4gIGlzUmVsb2FkOiBib29sZWFuLFxuKTogdm9pZCB7XG4gIGNvbnN0IHsgYWRkTm90aWZpY2F0aW9uLCByZW1vdmVOb3RpZmljYXRpb24gfSA9IHVzZU5vdGlmaWNhdGlvbnMoKVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc3Qgbm90aWZpY2F0aW9uS2V5ID0gJ2tleWJpbmRpbmctY29uZmlnLXdhcm5pbmcnXG5cbiAgICBpZiAod2FybmluZ3MubGVuZ3RoID09PSAwKSB7XG4gICAgICByZW1vdmVOb3RpZmljYXRpb24obm90aWZpY2F0aW9uS2V5KVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgY29uc3QgZXJyb3JDb3VudCA9IGNvdW50KHdhcm5pbmdzLCB3ID0+IHcuc2V2ZXJpdHkgPT09ICdlcnJvcicpXG4gICAgY29uc3Qgd2FybkNvdW50ID0gY291bnQod2FybmluZ3MsIHcgPT4gdy5zZXZlcml0eSA9PT0gJ3dhcm5pbmcnKVxuXG4gICAgbGV0IG1lc3NhZ2U6IHN0cmluZ1xuICAgIGlmIChlcnJvckNvdW50ID4gMCAmJiB3YXJuQ291bnQgPiAwKSB7XG4gICAgICBtZXNzYWdlID0gYEZvdW5kICR7ZXJyb3JDb3VudH0ga2V5YmluZGluZyAke3BsdXJhbChlcnJvckNvdW50LCAnZXJyb3InKX0gYW5kICR7d2FybkNvdW50fSAke3BsdXJhbCh3YXJuQ291bnQsICd3YXJuaW5nJyl9YFxuICAgIH0gZWxzZSBpZiAoZXJyb3JDb3VudCA+IDApIHtcbiAgICAgIG1lc3NhZ2UgPSBgRm91bmQgJHtlcnJvckNvdW50fSBrZXliaW5kaW5nICR7cGx1cmFsKGVycm9yQ291bnQsICdlcnJvcicpfWBcbiAgICB9IGVsc2Uge1xuICAgICAgbWVzc2FnZSA9IGBGb3VuZCAke3dhcm5Db3VudH0ga2V5YmluZGluZyAke3BsdXJhbCh3YXJuQ291bnQsICd3YXJuaW5nJyl9YFxuICAgIH1cbiAgICBtZXNzYWdlICs9ICcgwrcgL2RvY3RvciBmb3IgZGV0YWlscydcblxuICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICBrZXk6IG5vdGlmaWNhdGlvbktleSxcbiAgICAgIHRleHQ6IG1lc3NhZ2UsXG4gICAgICBjb2xvcjogZXJyb3JDb3VudCA+IDAgPyAnZXJyb3InIDogJ3dhcm5pbmcnLFxuICAgICAgcHJpb3JpdHk6IGVycm9yQ291bnQgPiAwID8gJ2ltbWVkaWF0ZScgOiAnaGlnaCcsXG4gICAgICAvLyBLZWVwIHZpc2libGUgZm9yIDYwIHNlY29uZHMgbGlrZSBzZXR0aW5ncyBlcnJvcnNcbiAgICAgIHRpbWVvdXRNczogNjAwMDAsXG4gICAgfSlcbiAgfSwgW3dhcm5pbmdzLCBpc1JlbG9hZCwgYWRkTm90aWZpY2F0aW9uLCByZW1vdmVOb3RpZmljYXRpb25dKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gS2V5YmluZGluZ1NldHVwKHsgY2hpbGRyZW4gfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBMb2FkIGJpbmRpbmdzIHN5bmNocm9ub3VzbHkgZm9yIGluaXRpYWwgcmVuZGVyXG4gIGNvbnN0IFt7IGJpbmRpbmdzLCB3YXJuaW5ncyB9LCBzZXRMb2FkUmVzdWx0XSA9XG4gICAgdXNlU3RhdGU8S2V5YmluZGluZ3NMb2FkUmVzdWx0PigoKSA9PiB7XG4gICAgICBjb25zdCByZXN1bHQgPSBsb2FkS2V5YmluZGluZ3NTeW5jV2l0aFdhcm5pbmdzKClcbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgYFtrZXliaW5kaW5nc10gS2V5YmluZGluZ1NldHVwIGluaXRpYWxpemVkIHdpdGggJHtyZXN1bHQuYmluZGluZ3MubGVuZ3RofSBiaW5kaW5ncywgJHtyZXN1bHQud2FybmluZ3MubGVuZ3RofSB3YXJuaW5nc2AsXG4gICAgICApXG4gICAgICByZXR1cm4gcmVzdWx0XG4gICAgfSlcblxuICAvLyBUcmFjayBpZiB0aGlzIGlzIGEgcmVsb2FkIChub3QgaW5pdGlhbCBsb2FkKVxuICBjb25zdCBbaXNSZWxvYWQsIHNldElzUmVsb2FkXSA9IHVzZVN0YXRlKGZhbHNlKVxuXG4gIC8vIERpc3BsYXkgd2FybmluZ3MgdmlhIG5vdGlmaWNhdGlvbnNcbiAgdXNlS2V5YmluZGluZ1dhcm5pbmdzKHdhcm5pbmdzLCBpc1JlbG9hZClcblxuICAvLyBDaG9yZCBzdGF0ZSBtYW5hZ2VtZW50IC0gdXNlIHJlZiBmb3IgaW1tZWRpYXRlIGFjY2Vzcywgc3RhdGUgZm9yIHJlLXJlbmRlcnNcbiAgLy8gVGhlIHJlZiBpcyB1c2VkIGJ5IHJlc29sdmUoKSB0byBnZXQgdGhlIGN1cnJlbnQgdmFsdWUgd2l0aG91dCB3YWl0aW5nIGZvciByZS1yZW5kZXJcbiAgLy8gVGhlIHN0YXRlIGlzIHVzZWQgdG8gdHJpZ2dlciByZS1yZW5kZXJzIHdoZW4gbmVlZGVkIChlLmcuLCBmb3IgVUkgdXBkYXRlcylcbiAgY29uc3QgcGVuZGluZ0Nob3JkUmVmID0gdXNlUmVmPFBhcnNlZEtleXN0cm9rZVtdIHwgbnVsbD4obnVsbClcbiAgY29uc3QgW3BlbmRpbmdDaG9yZCwgc2V0UGVuZGluZ0Nob3JkU3RhdGVdID0gdXNlU3RhdGU8XG4gICAgUGFyc2VkS2V5c3Ryb2tlW10gfCBudWxsXG4gID4obnVsbClcbiAgY29uc3QgY2hvcmRUaW1lb3V0UmVmID0gdXNlUmVmPE5vZGVKUy5UaW1lb3V0IHwgbnVsbD4obnVsbClcblxuICAvLyBIYW5kbGVyIHJlZ2lzdHJ5IGZvciBhY3Rpb24gY2FsbGJhY2tzICh1c2VkIGJ5IENob3JkSW50ZXJjZXB0b3IgdG8gaW52b2tlIGhhbmRsZXJzKVxuICBjb25zdCBoYW5kbGVyUmVnaXN0cnlSZWYgPSB1c2VSZWYoXG4gICAgbmV3IE1hcDxcbiAgICAgIHN0cmluZyxcbiAgICAgIFNldDx7XG4gICAgICAgIGFjdGlvbjogc3RyaW5nXG4gICAgICAgIGNvbnRleHQ6IEtleWJpbmRpbmdDb250ZXh0TmFtZVxuICAgICAgICBoYW5kbGVyOiAoKSA9PiB2b2lkXG4gICAgICB9PlxuICAgID4oKSxcbiAgKVxuXG4gIC8vIEFjdGl2ZSBjb250ZXh0IHRyYWNraW5nIGZvciBrZXliaW5kaW5nIHByaW9yaXR5IHJlc29sdXRpb25cbiAgLy8gVXNpbmcgYSByZWYgaW5zdGVhZCBvZiBzdGF0ZSBmb3Igc3luY2hyb25vdXMgdXBkYXRlcyAtIGlucHV0IGhhbmRsZXJzIG5lZWRcbiAgLy8gdG8gc2VlIHRoZSBjdXJyZW50IHZhbHVlIGltbWVkaWF0ZWx5LCBub3QgYWZ0ZXIgYSBSZWFjdCByZW5kZXIgY3ljbGUuXG4gIGNvbnN0IGFjdGl2ZUNvbnRleHRzUmVmID0gdXNlUmVmPFNldDxLZXliaW5kaW5nQ29udGV4dE5hbWU+PihuZXcgU2V0KCkpXG5cbiAgY29uc3QgcmVnaXN0ZXJBY3RpdmVDb250ZXh0ID0gdXNlQ2FsbGJhY2soXG4gICAgKGNvbnRleHQ6IEtleWJpbmRpbmdDb250ZXh0TmFtZSkgPT4ge1xuICAgICAgYWN0aXZlQ29udGV4dHNSZWYuY3VycmVudC5hZGQoY29udGV4dClcbiAgICB9LFxuICAgIFtdLFxuICApXG5cbiAgY29uc3QgdW5yZWdpc3RlckFjdGl2ZUNvbnRleHQgPSB1c2VDYWxsYmFjayhcbiAgICAoY29udGV4dDogS2V5YmluZGluZ0NvbnRleHROYW1lKSA9PiB7XG4gICAgICBhY3RpdmVDb250ZXh0c1JlZi5jdXJyZW50LmRlbGV0ZShjb250ZXh0KVxuICAgIH0sXG4gICAgW10sXG4gIClcblxuICAvLyBDbGVhciBjaG9yZCB0aW1lb3V0IHdoZW4gY29tcG9uZW50IHVubW91bnRzIG9yIGNob3JkIGNoYW5nZXNcbiAgY29uc3QgY2xlYXJDaG9yZFRpbWVvdXQgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgaWYgKGNob3JkVGltZW91dFJlZi5jdXJyZW50KSB7XG4gICAgICBjbGVhclRpbWVvdXQoY2hvcmRUaW1lb3V0UmVmLmN1cnJlbnQpXG4gICAgICBjaG9yZFRpbWVvdXRSZWYuY3VycmVudCA9IG51bGxcbiAgICB9XG4gIH0sIFtdKVxuXG4gIC8vIFdyYXBwZXIgZm9yIHNldFBlbmRpbmdDaG9yZCB0aGF0IG1hbmFnZXMgdGltZW91dCBhbmQgc3luY3MgcmVmK3N0YXRlXG4gIGNvbnN0IHNldFBlbmRpbmdDaG9yZCA9IHVzZUNhbGxiYWNrKFxuICAgIChwZW5kaW5nOiBQYXJzZWRLZXlzdHJva2VbXSB8IG51bGwpID0+IHtcbiAgICAgIGNsZWFyQ2hvcmRUaW1lb3V0KClcblxuICAgICAgaWYgKHBlbmRpbmcgIT09IG51bGwpIHtcbiAgICAgICAgLy8gU2V0IHRpbWVvdXQgdG8gY2FuY2VsIGNob3JkIGlmIG5vdCBjb21wbGV0ZWRcbiAgICAgICAgY2hvcmRUaW1lb3V0UmVmLmN1cnJlbnQgPSBzZXRUaW1lb3V0KFxuICAgICAgICAgIChwZW5kaW5nQ2hvcmRSZWYsIHNldFBlbmRpbmdDaG9yZFN0YXRlKSA9PiB7XG4gICAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoJ1trZXliaW5kaW5nc10gQ2hvcmQgdGltZW91dCAtIGNhbmNlbGxpbmcnKVxuICAgICAgICAgICAgcGVuZGluZ0Nob3JkUmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICAgICAgICBzZXRQZW5kaW5nQ2hvcmRTdGF0ZShudWxsKVxuICAgICAgICAgIH0sXG4gICAgICAgICAgQ0hPUkRfVElNRU9VVF9NUyxcbiAgICAgICAgICBwZW5kaW5nQ2hvcmRSZWYsXG4gICAgICAgICAgc2V0UGVuZGluZ0Nob3JkU3RhdGUsXG4gICAgICAgIClcbiAgICAgIH1cblxuICAgICAgLy8gVXBkYXRlIHJlZiBpbW1lZGlhdGVseSBmb3Igc3luY2hyb25vdXMgYWNjZXNzIGluIHJlc29sdmUoKVxuICAgICAgcGVuZGluZ0Nob3JkUmVmLmN1cnJlbnQgPSBwZW5kaW5nXG4gICAgICAvLyBVcGRhdGUgc3RhdGUgdG8gdHJpZ2dlciByZS1yZW5kZXJzIGZvciBVSSB1cGRhdGVzXG4gICAgICBzZXRQZW5kaW5nQ2hvcmRTdGF0ZShwZW5kaW5nKVxuICAgIH0sXG4gICAgW2NsZWFyQ2hvcmRUaW1lb3V0XSxcbiAgKVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgLy8gSW5pdGlhbGl6ZSBmaWxlIHdhdGNoZXIgKGlkZW1wb3RlbnQgLSBvbmx5IHJ1bnMgb25jZSlcbiAgICB2b2lkIGluaXRpYWxpemVLZXliaW5kaW5nV2F0Y2hlcigpXG5cbiAgICAvLyBTdWJzY3JpYmUgdG8gY2hhbmdlc1xuICAgIGNvbnN0IHVuc3Vic2NyaWJlID0gc3Vic2NyaWJlVG9LZXliaW5kaW5nQ2hhbmdlcyhyZXN1bHQgPT4ge1xuICAgICAgLy8gQW55IGNhbGxiYWNrIGludm9jYXRpb24gaXMgYSByZWxvYWQgc2luY2UgaW5pdGlhbCBsb2FkIGhhcHBlbnNcbiAgICAgIC8vIHN5bmNocm9ub3VzbHkgaW4gdXNlU3RhdGUsIG5vdCB2aWEgdGhpcyBzdWJzY3JpcHRpb25cbiAgICAgIHNldElzUmVsb2FkKHRydWUpXG5cbiAgICAgIHNldExvYWRSZXN1bHQocmVzdWx0KVxuICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICBgW2tleWJpbmRpbmdzXSBSZWxvYWRlZDogJHtyZXN1bHQuYmluZGluZ3MubGVuZ3RofSBiaW5kaW5ncywgJHtyZXN1bHQud2FybmluZ3MubGVuZ3RofSB3YXJuaW5nc2AsXG4gICAgICApXG4gICAgfSlcblxuICAgIHJldHVybiAoKSA9PiB7XG4gICAgICB1bnN1YnNjcmliZSgpXG4gICAgICBjbGVhckNob3JkVGltZW91dCgpXG4gICAgfVxuICB9LCBbY2xlYXJDaG9yZFRpbWVvdXRdKVxuXG4gIHJldHVybiAoXG4gICAgPEtleWJpbmRpbmdQcm92aWRlclxuICAgICAgYmluZGluZ3M9e2JpbmRpbmdzfVxuICAgICAgcGVuZGluZ0Nob3JkUmVmPXtwZW5kaW5nQ2hvcmRSZWZ9XG4gICAgICBwZW5kaW5nQ2hvcmQ9e3BlbmRpbmdDaG9yZH1cbiAgICAgIHNldFBlbmRpbmdDaG9yZD17c2V0UGVuZGluZ0Nob3JkfVxuICAgICAgYWN0aXZlQ29udGV4dHM9e2FjdGl2ZUNvbnRleHRzUmVmLmN1cnJlbnR9XG4gICAgICByZWdpc3RlckFjdGl2ZUNvbnRleHQ9e3JlZ2lzdGVyQWN0aXZlQ29udGV4dH1cbiAgICAgIHVucmVnaXN0ZXJBY3RpdmVDb250ZXh0PXt1bnJlZ2lzdGVyQWN0aXZlQ29udGV4dH1cbiAgICAgIGhhbmRsZXJSZWdpc3RyeVJlZj17aGFuZGxlclJlZ2lzdHJ5UmVmfVxuICAgID5cbiAgICAgIDxDaG9yZEludGVyY2VwdG9yXG4gICAgICAgIGJpbmRpbmdzPXtiaW5kaW5nc31cbiAgICAgICAgcGVuZGluZ0Nob3JkUmVmPXtwZW5kaW5nQ2hvcmRSZWZ9XG4gICAgICAgIHNldFBlbmRpbmdDaG9yZD17c2V0UGVuZGluZ0Nob3JkfVxuICAgICAgICBhY3RpdmVDb250ZXh0cz17YWN0aXZlQ29udGV4dHNSZWYuY3VycmVudH1cbiAgICAgICAgaGFuZGxlclJlZ2lzdHJ5UmVmPXtoYW5kbGVyUmVnaXN0cnlSZWZ9XG4gICAgICAvPlxuICAgICAge2NoaWxkcmVufVxuICAgIDwvS2V5YmluZGluZ1Byb3ZpZGVyPlxuICApXG59XG5cbi8qKlxuICogR2xvYmFsIGNob3JkIGludGVyY2VwdG9yIHRoYXQgcmVnaXN0ZXJzIHVzZUlucHV0IEZJUlNUIChiZWZvcmUgY2hpbGRyZW4pLlxuICpcbiAqIFRoaXMgY29tcG9uZW50IGludGVyY2VwdHMga2V5c3Ryb2tlcyB0aGF0IGFyZSBwYXJ0IG9mIGNob3JkIHNlcXVlbmNlcyBhbmRcbiAqIHN0b3BzIHByb3BhZ2F0aW9uIGJlZm9yZSBvdGhlciBoYW5kbGVycyAobGlrZSBQcm9tcHRJbnB1dCkgY2FuIHNlZSB0aGVtLlxuICpcbiAqIFdpdGhvdXQgdGhpcywgdGhlIHNlY29uZCBrZXkgb2YgYSBjaG9yZCAoZS5nLiwgJ3InIGluIFwiY3RybCtjIHJcIikgd291bGQgYmVcbiAqIGNhcHR1cmVkIGJ5IFByb21wdElucHV0IGFuZCBhZGRlZCB0byB0aGUgaW5wdXQgZmllbGQgYmVmb3JlIHRoZSBrZXliaW5kaW5nXG4gKiBzeXN0ZW0gY291bGQgcmVjb2duaXplIGl0IGFzIGNvbXBsZXRpbmcgYSBjaG9yZC5cbiAqL1xudHlwZSBIYW5kbGVyUmVnaXN0cmF0aW9uID0ge1xuICBhY3Rpb246IHN0cmluZ1xuICBjb250ZXh0OiBLZXliaW5kaW5nQ29udGV4dE5hbWVcbiAgaGFuZGxlcjogKCkgPT4gdm9pZFxufVxuXG5mdW5jdGlvbiBDaG9yZEludGVyY2VwdG9yKHtcbiAgYmluZGluZ3MsXG4gIHBlbmRpbmdDaG9yZFJlZixcbiAgc2V0UGVuZGluZ0Nob3JkLFxuICBhY3RpdmVDb250ZXh0cyxcbiAgaGFuZGxlclJlZ2lzdHJ5UmVmLFxufToge1xuICBiaW5kaW5nczogUGFyc2VkQmluZGluZ1tdXG4gIHBlbmRpbmdDaG9yZFJlZjogUmVhY3QuUmVmT2JqZWN0PFBhcnNlZEtleXN0cm9rZVtdIHwgbnVsbD5cbiAgc2V0UGVuZGluZ0Nob3JkOiAocGVuZGluZzogUGFyc2VkS2V5c3Ryb2tlW10gfCBudWxsKSA9PiB2b2lkXG4gIGFjdGl2ZUNvbnRleHRzOiBTZXQ8S2V5YmluZGluZ0NvbnRleHROYW1lPlxuICBoYW5kbGVyUmVnaXN0cnlSZWY6IFJlYWN0LlJlZk9iamVjdDxNYXA8c3RyaW5nLCBTZXQ8SGFuZGxlclJlZ2lzdHJhdGlvbj4+PlxufSk6IG51bGwge1xuICBjb25zdCBoYW5kbGVJbnB1dCA9IHVzZUNhbGxiYWNrKFxuICAgIChpbnB1dDogc3RyaW5nLCBrZXk6IEtleSwgZXZlbnQ6IElucHV0RXZlbnQpID0+IHtcbiAgICAgIC8vIFdoZWVsIGV2ZW50cyBjYW4gbmV2ZXIgc3RhcnQgY2hvcmQgc2VxdWVuY2VzIOKAlCBzY3JvbGw6bGluZVVwL0Rvd24gYXJlXG4gICAgICAvLyBzaW5nbGUta2V5IGJpbmRpbmdzIGhhbmRsZWQgYnkgcGVyLWNvbXBvbmVudCB1c2VLZXliaW5kaW5ncyBob29rcywgbm90XG4gICAgICAvLyBoZXJlLiBTa2lwIHRoZSByZWdpc3RyeSBzY2FuLiBNaWQtY2hvcmQgd2hlZWwgc3RpbGwgZmFsbHMgdGhyb3VnaCBzb1xuICAgICAgLy8gc2Nyb2xsaW5nIGNhbmNlbHMgdGhlIHBlbmRpbmcgY2hvcmQgbGlrZSBhbnkgb3RoZXIgbm9uLW1hdGNoaW5nIGtleS5cbiAgICAgIGlmICgoa2V5LndoZWVsVXAgfHwga2V5LndoZWVsRG93bikgJiYgcGVuZGluZ0Nob3JkUmVmLmN1cnJlbnQgPT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIEJ1aWxkIGNvbnRleHQgbGlzdCBmcm9tIHJlZ2lzdGVyZWQgaGFuZGxlcnMgKyBhY3RpdmVDb250ZXh0cyArIEdsb2JhbFxuICAgICAgLy8gVGhpcyBlbnN1cmVzIHdlIGNhbiByZXNvbHZlIGNob3JkcyBmb3IgYWxsIGNvbnRleHRzIHRoYXQgaGF2ZSBoYW5kbGVyc1xuICAgICAgY29uc3QgcmVnaXN0cnkgPSBoYW5kbGVyUmVnaXN0cnlSZWYuY3VycmVudFxuICAgICAgY29uc3QgaGFuZGxlckNvbnRleHRzID0gbmV3IFNldDxLZXliaW5kaW5nQ29udGV4dE5hbWU+KClcbiAgICAgIGlmIChyZWdpc3RyeSkge1xuICAgICAgICBmb3IgKGNvbnN0IGhhbmRsZXJzIG9mIHJlZ2lzdHJ5LnZhbHVlcygpKSB7XG4gICAgICAgICAgZm9yIChjb25zdCByZWdpc3RyYXRpb24gb2YgaGFuZGxlcnMpIHtcbiAgICAgICAgICAgIGhhbmRsZXJDb250ZXh0cy5hZGQocmVnaXN0cmF0aW9uLmNvbnRleHQpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBjb25zdCBjb250ZXh0czogS2V5YmluZGluZ0NvbnRleHROYW1lW10gPSBbXG4gICAgICAgIC4uLmhhbmRsZXJDb250ZXh0cyxcbiAgICAgICAgLi4uYWN0aXZlQ29udGV4dHMsXG4gICAgICAgICdHbG9iYWwnLFxuICAgICAgXVxuXG4gICAgICAvLyBUcmFjayB3aGV0aGVyIHdlJ3JlIGNvbXBsZXRpbmcgYSBjaG9yZCAocGVuZGluZyB3YXMgbm9uLW51bGwpXG4gICAgICBjb25zdCB3YXNJbkNob3JkID0gcGVuZGluZ0Nob3JkUmVmLmN1cnJlbnQgIT09IG51bGxcblxuICAgICAgLy8gQ2hlY2sgaWYgdGhpcyBrZXlzdHJva2UgaXMgcGFydCBvZiBhIGNob3JkIHNlcXVlbmNlXG4gICAgICBjb25zdCByZXN1bHQgPSByZXNvbHZlS2V5V2l0aENob3JkU3RhdGUoXG4gICAgICAgIGlucHV0LFxuICAgICAgICBrZXksXG4gICAgICAgIGNvbnRleHRzLFxuICAgICAgICBiaW5kaW5ncyxcbiAgICAgICAgcGVuZGluZ0Nob3JkUmVmLmN1cnJlbnQsXG4gICAgICApXG5cbiAgICAgIHN3aXRjaCAocmVzdWx0LnR5cGUpIHtcbiAgICAgICAgY2FzZSAnY2hvcmRfc3RhcnRlZCc6XG4gICAgICAgICAgLy8gVGhpcyBrZXkgc3RhcnRzIGEgY2hvcmQgLSBzdG9yZSBwZW5kaW5nIHN0YXRlIGFuZCBzdG9wIHByb3BhZ2F0aW9uXG4gICAgICAgICAgc2V0UGVuZGluZ0Nob3JkKHJlc3VsdC5wZW5kaW5nKVxuICAgICAgICAgIGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpXG4gICAgICAgICAgYnJlYWtcblxuICAgICAgICBjYXNlICdtYXRjaCc6IHtcbiAgICAgICAgICAvLyBDbGVhciBwZW5kaW5nIHN0YXRlXG4gICAgICAgICAgc2V0UGVuZGluZ0Nob3JkKG51bGwpXG5cbiAgICAgICAgICAvLyBPbmx5IGludm9rZSBoYW5kbGVycyBhbmQgc3RvcCBwcm9wYWdhdGlvbiBmb3IgY2hvcmQgY29tcGxldGlvbnNcbiAgICAgICAgICAvLyAobXVsdGkta2V5c3Ryb2tlIHNlcXVlbmNlcykuIFNpbmdsZS1rZXlzdHJva2UgbWF0Y2hlcyBzaG91bGQgcHJvcGFnYXRlXG4gICAgICAgICAgLy8gdG8gcGVyLWhvb2sgaGFuZGxlcnMgdG8gYXZvaWQgaW50ZXJmZXJpbmcgd2l0aCBvdGhlciBpbnB1dCBoYW5kbGluZ1xuICAgICAgICAgIC8vIChlLmcuLCBFbnRlciBuZWVkcyB0byByZWFjaCB1c2VUeXBlYWhlYWQgZm9yIGF1dG9jb21wbGV0ZSBhY2NlcHRhbmNlXG4gICAgICAgICAgLy8gYmVmb3JlIHRoZSBzdWJtaXQgaGFuZGxlciBmaXJlcykuXG4gICAgICAgICAgaWYgKHdhc0luQ2hvcmQpIHtcbiAgICAgICAgICAgIC8vIEZpbmQgYW5kIGludm9rZSB0aGUgaGFuZGxlciBmb3IgdGhpcyBhY3Rpb25cbiAgICAgICAgICAgIC8vIFdlIG5lZWQgdG8gY2hlY2sgdGhhdCB0aGUgaGFuZGxlcidzIGNvbnRleHQgaXMgaW4gb3VyIHJlc29sdmVkIGNvbnRleHRzXG4gICAgICAgICAgICAvLyAod2hpY2ggaW5jbHVkZXMgaGFuZGxlckNvbnRleHRzICsgYWN0aXZlQ29udGV4dHMgKyBHbG9iYWwpXG4gICAgICAgICAgICBjb25zdCBjb250ZXh0c1NldCA9IG5ldyBTZXQoY29udGV4dHMpXG4gICAgICAgICAgICBpZiAocmVnaXN0cnkpIHtcbiAgICAgICAgICAgICAgY29uc3QgaGFuZGxlcnMgPSByZWdpc3RyeS5nZXQocmVzdWx0LmFjdGlvbilcbiAgICAgICAgICAgICAgaWYgKGhhbmRsZXJzICYmIGhhbmRsZXJzLnNpemUgPiAwKSB7XG4gICAgICAgICAgICAgICAgLy8gRmluZCBoYW5kbGVycyB3aG9zZSBjb250ZXh0IGlzIGluIG91ciByZXNvbHZlZCBjb250ZXh0c1xuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgcmVnaXN0cmF0aW9uIG9mIGhhbmRsZXJzKSB7XG4gICAgICAgICAgICAgICAgICBpZiAoY29udGV4dHNTZXQuaGFzKHJlZ2lzdHJhdGlvbi5jb250ZXh0KSkge1xuICAgICAgICAgICAgICAgICAgICByZWdpc3RyYXRpb24uaGFuZGxlcigpXG4gICAgICAgICAgICAgICAgICAgIGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpXG4gICAgICAgICAgICAgICAgICAgIGJyZWFrIC8vIE9ubHkgaW52b2tlIHRoZSBmaXJzdCBtYXRjaGluZyBoYW5kbGVyXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIGJyZWFrXG4gICAgICAgIH1cblxuICAgICAgICBjYXNlICdjaG9yZF9jYW5jZWxsZWQnOlxuICAgICAgICAgIC8vIEludmFsaWQga2V5IGR1cmluZyBjaG9yZCAtIGNsZWFyIHBlbmRpbmcgc3RhdGUgYW5kIHN3YWxsb3cgdGhlXG4gICAgICAgICAgLy8ga2V5c3Ryb2tlIHNvIGl0IGRvZXNuJ3QgcHJvcGFnYXRlIGFzIGEgc3RhbmRhbG9uZSBhY3Rpb25cbiAgICAgICAgICAvLyAoZS5nLiwgY3RybCt4IGN0cmwrYyBzaG91bGQgbm90IGZpcmUgYXBwOmludGVycnVwdCkuXG4gICAgICAgICAgc2V0UGVuZGluZ0Nob3JkKG51bGwpXG4gICAgICAgICAgZXZlbnQuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKClcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGNhc2UgJ3VuYm91bmQnOlxuICAgICAgICAgIC8vIEtleSBpcyBleHBsaWNpdGx5IHVuYm91bmQgLSBjbGVhciBwZW5kaW5nIHN0YXRlIGFuZCBzd2FsbG93XG4gICAgICAgICAgLy8gdGhlIGtleXN0cm9rZSAoaXQgd2FzIHBhcnQgb2YgYSBjaG9yZCBzZXF1ZW5jZSkuXG4gICAgICAgICAgc2V0UGVuZGluZ0Nob3JkKG51bGwpXG4gICAgICAgICAgZXZlbnQuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKClcbiAgICAgICAgICBicmVha1xuXG4gICAgICAgIGNhc2UgJ25vbmUnOlxuICAgICAgICAgIC8vIE5vIGNob3JkIGludm9sdmVtZW50IC0gbGV0IG90aGVyIGhhbmRsZXJzIHByb2Nlc3NcbiAgICAgICAgICBicmVha1xuICAgICAgfVxuICAgIH0sXG4gICAgW1xuICAgICAgYmluZGluZ3MsXG4gICAgICBwZW5kaW5nQ2hvcmRSZWYsXG4gICAgICBzZXRQZW5kaW5nQ2hvcmQsXG4gICAgICBhY3RpdmVDb250ZXh0cyxcbiAgICAgIGhhbmRsZXJSZWdpc3RyeVJlZixcbiAgICBdLFxuICApXG5cbiAgdXNlSW5wdXQoaGFuZGxlSW5wdXQpXG5cbiAgcmV0dXJuIG51bGxcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPQSxLQUFLLElBQUlDLFdBQVcsRUFBRUMsU0FBUyxFQUFFQyxNQUFNLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQ3ZFLFNBQVNDLGdCQUFnQixRQUFRLDZCQUE2QjtBQUM5RCxjQUFjQyxVQUFVLFFBQVEsOEJBQThCO0FBQzlEO0FBQ0E7QUFDQTtBQUNBLFNBQVMsS0FBS0MsR0FBRyxFQUFFQyxRQUFRLFFBQVEsV0FBVztBQUM5QyxTQUFTQyxLQUFLLFFBQVEsbUJBQW1CO0FBQ3pDLFNBQVNDLGVBQWUsUUFBUSxtQkFBbUI7QUFDbkQsU0FBU0MsTUFBTSxRQUFRLHlCQUF5QjtBQUNoRCxTQUFTQyxrQkFBa0IsUUFBUSx3QkFBd0I7QUFDM0QsU0FDRUMsMkJBQTJCLEVBQzNCLEtBQUtDLHFCQUFxQixFQUMxQkMsK0JBQStCLEVBQy9CQyw0QkFBNEIsUUFDdkIsdUJBQXVCO0FBQzlCLFNBQVNDLHdCQUF3QixRQUFRLGVBQWU7QUFDeEQsY0FDRUMscUJBQXFCLEVBQ3JCQyxhQUFhLEVBQ2JDLGVBQWUsUUFDVixZQUFZO0FBQ25CLGNBQWNDLGlCQUFpQixRQUFRLGVBQWU7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 \ No newline at end of file diff --git a/keybindings/defaultBindings.ts b/keybindings/defaultBindings.ts new file mode 100644 index 0000000..8629809 --- /dev/null +++ b/keybindings/defaultBindings.ts @@ -0,0 +1,340 @@ +import { feature } from 'bun:bundle' +import { satisfies } from 'src/utils/semver.js' +import { isRunningWithBun } from '../utils/bundledMode.js' +import { getPlatform } from '../utils/platform.js' +import type { KeybindingBlock } from './types.js' + +/** + * Default keybindings that match current Claude Code behavior. + * These are loaded first, then user keybindings.json overrides them. + */ + +// Platform-specific image paste shortcut: +// - Windows: alt+v (ctrl+v is system paste) +// - Other platforms: ctrl+v +const IMAGE_PASTE_KEY = getPlatform() === 'windows' ? 'alt+v' : 'ctrl+v' + +// Modifier-only chords (like shift+tab) may fail on Windows Terminal without VT mode +// See: https://github.com/microsoft/terminal/issues/879#issuecomment-618801651 +// Node enabled VT mode in 24.2.0 / 22.17.0: https://github.com/nodejs/node/pull/58358 +// Bun enabled VT mode in 1.2.23: https://github.com/oven-sh/bun/pull/21161 +const SUPPORTS_TERMINAL_VT_MODE = + getPlatform() !== 'windows' || + (isRunningWithBun() + ? satisfies(process.versions.bun, '>=1.2.23') + : satisfies(process.versions.node, '>=22.17.0 <23.0.0 || >=24.2.0')) + +// Platform-specific mode cycle shortcut: +// - Windows without VT mode: meta+m (shift+tab doesn't work reliably) +// - Other platforms: shift+tab +const MODE_CYCLE_KEY = SUPPORTS_TERMINAL_VT_MODE ? 'shift+tab' : 'meta+m' + +export const DEFAULT_BINDINGS: KeybindingBlock[] = [ + { + context: 'Global', + bindings: { + // ctrl+c and ctrl+d use special time-based double-press handling. + // They ARE defined here so the resolver can find them, but they + // CANNOT be rebound by users - validation in reservedShortcuts.ts + // will show an error if users try to override these keys. + 'ctrl+c': 'app:interrupt', + 'ctrl+d': 'app:exit', + 'ctrl+l': 'app:redraw', + 'ctrl+t': 'app:toggleTodos', + 'ctrl+o': 'app:toggleTranscript', + ...(feature('KAIROS') || feature('KAIROS_BRIEF') + ? { 'ctrl+shift+b': 'app:toggleBrief' as const } + : {}), + 'ctrl+shift+o': 'app:toggleTeammatePreview', + 'ctrl+r': 'history:search', + // File navigation. cmd+ bindings only fire on kitty-protocol terminals; + // ctrl+shift is the portable fallback. + ...(feature('QUICK_SEARCH') + ? { + 'ctrl+shift+f': 'app:globalSearch' as const, + 'cmd+shift+f': 'app:globalSearch' as const, + 'ctrl+shift+p': 'app:quickOpen' as const, + 'cmd+shift+p': 'app:quickOpen' as const, + } + : {}), + ...(feature('TERMINAL_PANEL') ? { 'meta+j': 'app:toggleTerminal' } : {}), + }, + }, + { + context: 'Chat', + bindings: { + escape: 'chat:cancel', + // ctrl+x chord prefix avoids shadowing readline editing keys (ctrl+a/b/e/f/...). + 'ctrl+x ctrl+k': 'chat:killAgents', + [MODE_CYCLE_KEY]: 'chat:cycleMode', + 'meta+p': 'chat:modelPicker', + 'meta+o': 'chat:fastMode', + 'meta+t': 'chat:thinkingToggle', + enter: 'chat:submit', + up: 'history:previous', + down: 'history:next', + // Editing shortcuts (defined here, migration in progress) + // Undo has two bindings to support different terminal behaviors: + // - ctrl+_ for legacy terminals (send \x1f control char) + // - ctrl+shift+- for Kitty protocol (sends physical key with modifiers) + 'ctrl+_': 'chat:undo', + 'ctrl+shift+-': 'chat:undo', + // ctrl+x ctrl+e is the readline-native edit-and-execute-command binding. + 'ctrl+x ctrl+e': 'chat:externalEditor', + 'ctrl+g': 'chat:externalEditor', + 'ctrl+s': 'chat:stash', + // Image paste shortcut (platform-specific key defined above) + [IMAGE_PASTE_KEY]: 'chat:imagePaste', + ...(feature('MESSAGE_ACTIONS') + ? { 'shift+up': 'chat:messageActions' as const } + : {}), + // Voice activation (hold-to-talk). Registered so getShortcutDisplay + // finds it without hitting the fallback analytics log. To rebind, + // add a voice:pushToTalk entry (last wins); to disable, use /voice + // — null-unbinding space hits a pre-existing useKeybinding.ts trap + // where 'unbound' swallows the event (space dead for typing). + ...(feature('VOICE_MODE') ? { space: 'voice:pushToTalk' } : {}), + }, + }, + { + context: 'Autocomplete', + bindings: { + tab: 'autocomplete:accept', + escape: 'autocomplete:dismiss', + up: 'autocomplete:previous', + down: 'autocomplete:next', + }, + }, + { + context: 'Settings', + bindings: { + // Settings menu uses escape only (not 'n') to dismiss + escape: 'confirm:no', + // Config panel list navigation (reuses Select actions) + up: 'select:previous', + down: 'select:next', + k: 'select:previous', + j: 'select:next', + 'ctrl+p': 'select:previous', + 'ctrl+n': 'select:next', + // Toggle/activate the selected setting (space only — enter saves & closes) + space: 'select:accept', + // Save and close the config panel + enter: 'settings:close', + // Enter search mode + '/': 'settings:search', + // Retry loading usage data (only active on error) + r: 'settings:retry', + }, + }, + { + context: 'Confirmation', + bindings: { + y: 'confirm:yes', + n: 'confirm:no', + enter: 'confirm:yes', + escape: 'confirm:no', + // Navigation for dialogs with lists + up: 'confirm:previous', + down: 'confirm:next', + tab: 'confirm:nextField', + space: 'confirm:toggle', + // Cycle modes (used in file permission dialogs and teams dialog) + 'shift+tab': 'confirm:cycleMode', + // Toggle permission explanation in permission dialogs + 'ctrl+e': 'confirm:toggleExplanation', + // Toggle permission debug info + 'ctrl+d': 'permission:toggleDebug', + }, + }, + { + context: 'Tabs', + bindings: { + // Tab cycling navigation + tab: 'tabs:next', + 'shift+tab': 'tabs:previous', + right: 'tabs:next', + left: 'tabs:previous', + }, + }, + { + context: 'Transcript', + bindings: { + 'ctrl+e': 'transcript:toggleShowAll', + 'ctrl+c': 'transcript:exit', + escape: 'transcript:exit', + // q — pager convention (less, tmux copy-mode). Transcript is a modal + // reading view with no prompt, so q-as-literal-char has no owner. + q: 'transcript:exit', + }, + }, + { + context: 'HistorySearch', + bindings: { + 'ctrl+r': 'historySearch:next', + escape: 'historySearch:accept', + tab: 'historySearch:accept', + 'ctrl+c': 'historySearch:cancel', + enter: 'historySearch:execute', + }, + }, + { + context: 'Task', + bindings: { + // Background running foreground tasks (bash commands, agents) + // In tmux, users must press ctrl+b twice (tmux prefix escape) + 'ctrl+b': 'task:background', + }, + }, + { + context: 'ThemePicker', + bindings: { + 'ctrl+t': 'theme:toggleSyntaxHighlighting', + }, + }, + { + context: 'Scroll', + bindings: { + pageup: 'scroll:pageUp', + pagedown: 'scroll:pageDown', + wheelup: 'scroll:lineUp', + wheeldown: 'scroll:lineDown', + 'ctrl+home': 'scroll:top', + 'ctrl+end': 'scroll:bottom', + // Selection copy. ctrl+shift+c is standard terminal copy. + // cmd+c only fires on terminals using the kitty keyboard + // protocol (kitty/WezTerm/ghostty/iTerm2) where the super + // modifier actually reaches the pty — inert elsewhere. + // Esc-to-clear and contextual ctrl+c are handled via raw + // useInput so they can conditionally propagate. + 'ctrl+shift+c': 'selection:copy', + 'cmd+c': 'selection:copy', + }, + }, + { + context: 'Help', + bindings: { + escape: 'help:dismiss', + }, + }, + // Attachment navigation (select dialog image attachments) + { + context: 'Attachments', + bindings: { + right: 'attachments:next', + left: 'attachments:previous', + backspace: 'attachments:remove', + delete: 'attachments:remove', + down: 'attachments:exit', + escape: 'attachments:exit', + }, + }, + // Footer indicator navigation (tasks, teams, diff, loop) + { + context: 'Footer', + bindings: { + up: 'footer:up', + 'ctrl+p': 'footer:up', + down: 'footer:down', + 'ctrl+n': 'footer:down', + right: 'footer:next', + left: 'footer:previous', + enter: 'footer:openSelected', + escape: 'footer:clearSelection', + }, + }, + // Message selector (rewind dialog) navigation + { + context: 'MessageSelector', + bindings: { + up: 'messageSelector:up', + down: 'messageSelector:down', + k: 'messageSelector:up', + j: 'messageSelector:down', + 'ctrl+p': 'messageSelector:up', + 'ctrl+n': 'messageSelector:down', + 'ctrl+up': 'messageSelector:top', + 'shift+up': 'messageSelector:top', + 'meta+up': 'messageSelector:top', + 'shift+k': 'messageSelector:top', + 'ctrl+down': 'messageSelector:bottom', + 'shift+down': 'messageSelector:bottom', + 'meta+down': 'messageSelector:bottom', + 'shift+j': 'messageSelector:bottom', + enter: 'messageSelector:select', + }, + }, + // PromptInput unmounts while cursor active — no key conflict. + ...(feature('MESSAGE_ACTIONS') + ? [ + { + context: 'MessageActions' as const, + bindings: { + up: 'messageActions:prev' as const, + down: 'messageActions:next' as const, + k: 'messageActions:prev' as const, + j: 'messageActions:next' as const, + // meta = cmd on macOS; super for kitty keyboard-protocol — bind both. + 'meta+up': 'messageActions:top' as const, + 'meta+down': 'messageActions:bottom' as const, + 'super+up': 'messageActions:top' as const, + 'super+down': 'messageActions:bottom' as const, + // Mouse selection extends on shift+arrow (ScrollKeybindingHandler:573) when present — + // correct layered UX: esc clears selection, then shift+↑ jumps. + 'shift+up': 'messageActions:prevUser' as const, + 'shift+down': 'messageActions:nextUser' as const, + escape: 'messageActions:escape' as const, + 'ctrl+c': 'messageActions:ctrlc' as const, + // Mirror MESSAGE_ACTIONS. Not imported — would pull React/ink into this config module. + enter: 'messageActions:enter' as const, + c: 'messageActions:c' as const, + p: 'messageActions:p' as const, + }, + }, + ] + : []), + // Diff dialog navigation + { + context: 'DiffDialog', + bindings: { + escape: 'diff:dismiss', + left: 'diff:previousSource', + right: 'diff:nextSource', + up: 'diff:previousFile', + down: 'diff:nextFile', + enter: 'diff:viewDetails', + // Note: diff:back is handled by left arrow in detail mode + }, + }, + // Model picker effort cycling (ant-only) + { + context: 'ModelPicker', + bindings: { + left: 'modelPicker:decreaseEffort', + right: 'modelPicker:increaseEffort', + }, + }, + // Select component navigation (used by /model, /resume, permission prompts, etc.) + { + context: 'Select', + bindings: { + up: 'select:previous', + down: 'select:next', + j: 'select:next', + k: 'select:previous', + 'ctrl+n': 'select:next', + 'ctrl+p': 'select:previous', + enter: 'select:accept', + escape: 'select:cancel', + }, + }, + // Plugin dialog actions (manage, browse, discover plugins) + // Navigation (select:*) uses the Select context above + { + context: 'Plugin', + bindings: { + space: 'plugin:toggle', + i: 'plugin:install', + }, + }, +] diff --git a/keybindings/loadUserBindings.ts b/keybindings/loadUserBindings.ts new file mode 100644 index 0000000..416abe7 --- /dev/null +++ b/keybindings/loadUserBindings.ts @@ -0,0 +1,472 @@ +/** + * User keybinding configuration loader with hot-reload support. + * + * Loads keybindings from ~/.claude/keybindings.json and watches + * for changes to reload them automatically. + * + * NOTE: User keybinding customization is currently only available for + * Anthropic employees (USER_TYPE === 'ant'). External users always + * use the default bindings. + */ + +import chokidar, { type FSWatcher } from 'chokidar' +import { readFileSync } from 'fs' +import { readFile, stat } from 'fs/promises' +import { dirname, join } from 'path' +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' +import { logEvent } from '../services/analytics/index.js' +import { registerCleanup } from '../utils/cleanupRegistry.js' +import { logForDebugging } from '../utils/debug.js' +import { getClaudeConfigHomeDir } from '../utils/envUtils.js' +import { errorMessage, isENOENT } from '../utils/errors.js' +import { createSignal } from '../utils/signal.js' +import { jsonParse } from '../utils/slowOperations.js' +import { DEFAULT_BINDINGS } from './defaultBindings.js' +import { parseBindings } from './parser.js' +import type { KeybindingBlock, ParsedBinding } from './types.js' +import { + checkDuplicateKeysInJson, + type KeybindingWarning, + validateBindings, +} from './validate.js' + +/** + * Check if keybinding customization is enabled. + * + * Returns true if the tengu_keybinding_customization_release GrowthBook gate is enabled. + * + * This function is exported so other parts of the codebase (e.g., /doctor) + * can check the same condition consistently. + */ +export function isKeybindingCustomizationEnabled(): boolean { + return getFeatureValue_CACHED_MAY_BE_STALE( + 'tengu_keybinding_customization_release', + false, + ) +} + +/** + * Time in milliseconds to wait for file writes to stabilize. + */ +const FILE_STABILITY_THRESHOLD_MS = 500 + +/** + * Polling interval for checking file stability. + */ +const FILE_STABILITY_POLL_INTERVAL_MS = 200 + +/** + * Result of loading keybindings, including any validation warnings. + */ +export type KeybindingsLoadResult = { + bindings: ParsedBinding[] + warnings: KeybindingWarning[] +} + +let watcher: FSWatcher | null = null +let initialized = false +let disposed = false +let cachedBindings: ParsedBinding[] | null = null +let cachedWarnings: KeybindingWarning[] = [] +const keybindingsChanged = createSignal<[result: KeybindingsLoadResult]>() + +/** + * Tracks the date (YYYY-MM-DD) when we last logged a custom keybindings load event. + * Used to ensure we fire the event at most once per day. + */ +let lastCustomBindingsLogDate: string | null = null + +/** + * Log a telemetry event when custom keybindings are loaded, at most once per day. + * This lets us estimate the percentage of users who customize their keybindings. + */ +function logCustomBindingsLoadedOncePerDay(userBindingCount: number): void { + const today = new Date().toISOString().slice(0, 10) + if (lastCustomBindingsLogDate === today) return + lastCustomBindingsLogDate = today + logEvent('tengu_custom_keybindings_loaded', { + user_binding_count: userBindingCount, + }) +} + +/** + * Type guard to check if an object is a valid KeybindingBlock. + */ +function isKeybindingBlock(obj: unknown): obj is KeybindingBlock { + if (typeof obj !== 'object' || obj === null) return false + const b = obj as Record + return ( + typeof b.context === 'string' && + typeof b.bindings === 'object' && + b.bindings !== null + ) +} + +/** + * Type guard to check if an array contains only valid KeybindingBlocks. + */ +function isKeybindingBlockArray(arr: unknown): arr is KeybindingBlock[] { + return Array.isArray(arr) && arr.every(isKeybindingBlock) +} + +/** + * Get the path to the user keybindings file. + */ +export function getKeybindingsPath(): string { + return join(getClaudeConfigHomeDir(), 'keybindings.json') +} + +/** + * Parse default bindings (cached for performance). + */ +function getDefaultParsedBindings(): ParsedBinding[] { + return parseBindings(DEFAULT_BINDINGS) +} + +/** + * Load and parse keybindings from user config file. + * Returns merged default + user bindings along with validation warnings. + * + * For external users, always returns default bindings only. + * User customization is currently gated to Anthropic employees. + */ +export async function loadKeybindings(): Promise { + const defaultBindings = getDefaultParsedBindings() + + // Skip user config loading for external users + if (!isKeybindingCustomizationEnabled()) { + return { bindings: defaultBindings, warnings: [] } + } + + const userPath = getKeybindingsPath() + + try { + const content = await readFile(userPath, 'utf-8') + const parsed: unknown = jsonParse(content) + + // Extract bindings array from object wrapper format: { "bindings": [...] } + let userBlocks: unknown + if (typeof parsed === 'object' && parsed !== null && 'bindings' in parsed) { + userBlocks = (parsed as { bindings: unknown }).bindings + } else { + // Invalid format - missing bindings property + const errorMessage = 'keybindings.json must have a "bindings" array' + const suggestion = 'Use format: { "bindings": [ ... ] }' + logForDebugging(`[keybindings] Invalid keybindings.json: ${errorMessage}`) + return { + bindings: defaultBindings, + warnings: [ + { + type: 'parse_error', + severity: 'error', + message: errorMessage, + suggestion, + }, + ], + } + } + + // Validate structure - bindings must be an array of valid keybinding blocks + if (!isKeybindingBlockArray(userBlocks)) { + const errorMessage = !Array.isArray(userBlocks) + ? '"bindings" must be an array' + : 'keybindings.json contains invalid block structure' + const suggestion = !Array.isArray(userBlocks) + ? 'Set "bindings" to an array of keybinding blocks' + : 'Each block must have "context" (string) and "bindings" (object)' + logForDebugging(`[keybindings] Invalid keybindings.json: ${errorMessage}`) + return { + bindings: defaultBindings, + warnings: [ + { + type: 'parse_error', + severity: 'error', + message: errorMessage, + suggestion, + }, + ], + } + } + + const userParsed = parseBindings(userBlocks) + logForDebugging( + `[keybindings] Loaded ${userParsed.length} user bindings from ${userPath}`, + ) + + // User bindings come after defaults, so they override + const mergedBindings = [...defaultBindings, ...userParsed] + + logCustomBindingsLoadedOncePerDay(userParsed.length) + + // Run validation on user config + // First check for duplicate keys in raw JSON (JSON.parse silently drops earlier values) + const duplicateKeyWarnings = checkDuplicateKeysInJson(content) + const warnings = [ + ...duplicateKeyWarnings, + ...validateBindings(userBlocks, mergedBindings), + ] + + if (warnings.length > 0) { + logForDebugging( + `[keybindings] Found ${warnings.length} validation issue(s)`, + ) + } + + return { bindings: mergedBindings, warnings } + } catch (error) { + // File doesn't exist - use defaults (user can run /keybindings to create) + if (isENOENT(error)) { + return { bindings: defaultBindings, warnings: [] } + } + + // Other error - log and return defaults with warning + logForDebugging( + `[keybindings] Error loading ${userPath}: ${errorMessage(error)}`, + ) + return { + bindings: defaultBindings, + warnings: [ + { + type: 'parse_error', + severity: 'error', + message: `Failed to parse keybindings.json: ${errorMessage(error)}`, + }, + ], + } + } +} + +/** + * Load keybindings synchronously (for initial render). + * Uses cached value if available. + */ +export function loadKeybindingsSync(): ParsedBinding[] { + if (cachedBindings) { + return cachedBindings + } + + const result = loadKeybindingsSyncWithWarnings() + return result.bindings +} + +/** + * Load keybindings synchronously with validation warnings. + * Uses cached values if available. + * + * For external users, always returns default bindings only. + * User customization is currently gated to Anthropic employees. + */ +export function loadKeybindingsSyncWithWarnings(): KeybindingsLoadResult { + if (cachedBindings) { + return { bindings: cachedBindings, warnings: cachedWarnings } + } + + const defaultBindings = getDefaultParsedBindings() + + // Skip user config loading for external users + if (!isKeybindingCustomizationEnabled()) { + cachedBindings = defaultBindings + cachedWarnings = [] + return { bindings: cachedBindings, warnings: cachedWarnings } + } + + const userPath = getKeybindingsPath() + + try { + // sync IO: called from sync context (React useState initializer) + const content = readFileSync(userPath, 'utf-8') + const parsed: unknown = jsonParse(content) + + // Extract bindings array from object wrapper format: { "bindings": [...] } + let userBlocks: unknown + if (typeof parsed === 'object' && parsed !== null && 'bindings' in parsed) { + userBlocks = (parsed as { bindings: unknown }).bindings + } else { + // Invalid format - missing bindings property + cachedBindings = defaultBindings + cachedWarnings = [ + { + type: 'parse_error', + severity: 'error', + message: 'keybindings.json must have a "bindings" array', + suggestion: 'Use format: { "bindings": [ ... ] }', + }, + ] + return { bindings: cachedBindings, warnings: cachedWarnings } + } + + // Validate structure - bindings must be an array of valid keybinding blocks + if (!isKeybindingBlockArray(userBlocks)) { + const errorMessage = !Array.isArray(userBlocks) + ? '"bindings" must be an array' + : 'keybindings.json contains invalid block structure' + const suggestion = !Array.isArray(userBlocks) + ? 'Set "bindings" to an array of keybinding blocks' + : 'Each block must have "context" (string) and "bindings" (object)' + cachedBindings = defaultBindings + cachedWarnings = [ + { + type: 'parse_error', + severity: 'error', + message: errorMessage, + suggestion, + }, + ] + return { bindings: cachedBindings, warnings: cachedWarnings } + } + + const userParsed = parseBindings(userBlocks) + logForDebugging( + `[keybindings] Loaded ${userParsed.length} user bindings from ${userPath}`, + ) + cachedBindings = [...defaultBindings, ...userParsed] + + logCustomBindingsLoadedOncePerDay(userParsed.length) + + // Run validation - check for duplicate keys in raw JSON first + const duplicateKeyWarnings = checkDuplicateKeysInJson(content) + cachedWarnings = [ + ...duplicateKeyWarnings, + ...validateBindings(userBlocks, cachedBindings), + ] + if (cachedWarnings.length > 0) { + logForDebugging( + `[keybindings] Found ${cachedWarnings.length} validation issue(s)`, + ) + } + + return { bindings: cachedBindings, warnings: cachedWarnings } + } catch { + // File doesn't exist or error - use defaults (user can run /keybindings to create) + cachedBindings = defaultBindings + cachedWarnings = [] + return { bindings: cachedBindings, warnings: cachedWarnings } + } +} + +/** + * Initialize file watching for keybindings.json. + * Call this once when the app starts. + * + * For external users, this is a no-op since user customization is disabled. + */ +export async function initializeKeybindingWatcher(): Promise { + if (initialized || disposed) return + + // Skip file watching for external users + if (!isKeybindingCustomizationEnabled()) { + logForDebugging( + '[keybindings] Skipping file watcher - user customization disabled', + ) + return + } + + const userPath = getKeybindingsPath() + const watchDir = dirname(userPath) + + // Only watch if parent directory exists + try { + const stats = await stat(watchDir) + if (!stats.isDirectory()) { + logForDebugging( + `[keybindings] Not watching: ${watchDir} is not a directory`, + ) + return + } + } catch { + logForDebugging(`[keybindings] Not watching: ${watchDir} does not exist`) + return + } + + // Set initialized only after we've confirmed we can watch + initialized = true + + logForDebugging(`[keybindings] Watching for changes to ${userPath}`) + + watcher = chokidar.watch(userPath, { + persistent: true, + ignoreInitial: true, + awaitWriteFinish: { + stabilityThreshold: FILE_STABILITY_THRESHOLD_MS, + pollInterval: FILE_STABILITY_POLL_INTERVAL_MS, + }, + ignorePermissionErrors: true, + usePolling: false, + atomic: true, + }) + + watcher.on('add', handleChange) + watcher.on('change', handleChange) + watcher.on('unlink', handleDelete) + + // Register cleanup + registerCleanup(async () => disposeKeybindingWatcher()) +} + +/** + * Clean up the file watcher. + */ +export function disposeKeybindingWatcher(): void { + disposed = true + if (watcher) { + void watcher.close() + watcher = null + } + keybindingsChanged.clear() +} + +/** + * Subscribe to keybinding changes. + * The listener receives the new parsed bindings when the file changes. + */ +export const subscribeToKeybindingChanges = keybindingsChanged.subscribe + +async function handleChange(path: string): Promise { + logForDebugging(`[keybindings] Detected change to ${path}`) + + try { + const result = await loadKeybindings() + cachedBindings = result.bindings + cachedWarnings = result.warnings + + // Notify all listeners with the full result + keybindingsChanged.emit(result) + } catch (error) { + logForDebugging(`[keybindings] Error reloading: ${errorMessage(error)}`) + } +} + +function handleDelete(path: string): void { + logForDebugging(`[keybindings] Detected deletion of ${path}`) + + // Reset to defaults when file is deleted + const defaultBindings = getDefaultParsedBindings() + cachedBindings = defaultBindings + cachedWarnings = [] + + keybindingsChanged.emit({ bindings: defaultBindings, warnings: [] }) +} + +/** + * Get the cached keybinding warnings. + * Returns empty array if no warnings or bindings haven't been loaded yet. + */ +export function getCachedKeybindingWarnings(): KeybindingWarning[] { + return cachedWarnings +} + +/** + * Reset internal state for testing. + */ +export function resetKeybindingLoaderForTesting(): void { + initialized = false + disposed = false + cachedBindings = null + cachedWarnings = [] + lastCustomBindingsLogDate = null + if (watcher) { + void watcher.close() + watcher = null + } + keybindingsChanged.clear() +} diff --git a/keybindings/match.ts b/keybindings/match.ts new file mode 100644 index 0000000..2b40717 --- /dev/null +++ b/keybindings/match.ts @@ -0,0 +1,120 @@ +import type { Key } from '../ink.js' +import type { ParsedBinding, ParsedKeystroke } from './types.js' + +/** + * Modifier keys from Ink's Key type that we care about for matching. + * Note: `fn` from Key is intentionally excluded as it's rarely used and + * not commonly configurable in terminal applications. + */ +type InkModifiers = Pick + +/** + * Extract modifiers from an Ink Key object. + * This function ensures we're explicitly extracting the modifiers we care about. + */ +function getInkModifiers(key: Key): InkModifiers { + return { + ctrl: key.ctrl, + shift: key.shift, + meta: key.meta, + super: key.super, + } +} + +/** + * Extract the normalized key name from Ink's Key + input. + * Maps Ink's boolean flags (key.escape, key.return, etc.) to string names + * that match our ParsedKeystroke.key format. + */ +export function getKeyName(input: string, key: Key): string | null { + if (key.escape) return 'escape' + if (key.return) return 'enter' + if (key.tab) return 'tab' + if (key.backspace) return 'backspace' + if (key.delete) return 'delete' + if (key.upArrow) return 'up' + if (key.downArrow) return 'down' + if (key.leftArrow) return 'left' + if (key.rightArrow) return 'right' + if (key.pageUp) return 'pageup' + if (key.pageDown) return 'pagedown' + if (key.wheelUp) return 'wheelup' + if (key.wheelDown) return 'wheeldown' + if (key.home) return 'home' + if (key.end) return 'end' + if (input.length === 1) return input.toLowerCase() + return null +} + +/** + * Check if all modifiers match between Ink Key and ParsedKeystroke. + * + * Alt and Meta: Ink historically set `key.meta` for Alt/Option. A `meta` + * modifier in config is treated as an alias for `alt` — both match when + * `key.meta` is true. + * + * Super (Cmd/Win): distinct from alt/meta. Only arrives via the kitty + * keyboard protocol on supporting terminals. A `cmd`/`super` binding will + * simply never fire on terminals that don't send it. + */ +function modifiersMatch( + inkMods: InkModifiers, + target: ParsedKeystroke, +): boolean { + // Check ctrl modifier + if (inkMods.ctrl !== target.ctrl) return false + + // Check shift modifier + if (inkMods.shift !== target.shift) return false + + // Alt and meta both map to key.meta in Ink (terminal limitation) + // So we check if EITHER alt OR meta is required in target + const targetNeedsMeta = target.alt || target.meta + if (inkMods.meta !== targetNeedsMeta) return false + + // Super (cmd/win) is a distinct modifier from alt/meta + if (inkMods.super !== target.super) return false + + return true +} + +/** + * Check if a ParsedKeystroke matches the given Ink input + Key. + * + * The display text will show platform-appropriate names (opt on macOS, alt elsewhere). + */ +export function matchesKeystroke( + input: string, + key: Key, + target: ParsedKeystroke, +): boolean { + const keyName = getKeyName(input, key) + if (keyName !== target.key) return false + + const inkMods = getInkModifiers(key) + + // QUIRK: Ink sets key.meta=true when escape is pressed (see input-event.ts). + // This is a legacy behavior from how escape sequences work in terminals. + // We need to ignore the meta modifier when matching the escape key itself, + // otherwise bindings like "escape" (without modifiers) would never match. + if (key.escape) { + return modifiersMatch({ ...inkMods, meta: false }, target) + } + + return modifiersMatch(inkMods, target) +} + +/** + * Check if Ink's Key + input matches a parsed binding's first keystroke. + * For single-keystroke bindings only (Phase 1). + */ +export function matchesBinding( + input: string, + key: Key, + binding: ParsedBinding, +): boolean { + if (binding.chord.length !== 1) return false + const keystroke = binding.chord[0] + if (!keystroke) return false + return matchesKeystroke(input, key, keystroke) +} diff --git a/keybindings/parser.ts b/keybindings/parser.ts new file mode 100644 index 0000000..ead1a1a --- /dev/null +++ b/keybindings/parser.ts @@ -0,0 +1,203 @@ +import type { + Chord, + KeybindingBlock, + ParsedBinding, + ParsedKeystroke, +} from './types.js' + +/** + * Parse a keystroke string like "ctrl+shift+k" into a ParsedKeystroke. + * Supports various modifier aliases (ctrl/control, alt/opt/option/meta, + * cmd/command/super/win). + */ +export function parseKeystroke(input: string): ParsedKeystroke { + const parts = input.split('+') + const keystroke: ParsedKeystroke = { + key: '', + ctrl: false, + alt: false, + shift: false, + meta: false, + super: false, + } + for (const part of parts) { + const lower = part.toLowerCase() + switch (lower) { + case 'ctrl': + case 'control': + keystroke.ctrl = true + break + case 'alt': + case 'opt': + case 'option': + keystroke.alt = true + break + case 'shift': + keystroke.shift = true + break + case 'meta': + keystroke.meta = true + break + case 'cmd': + case 'command': + case 'super': + case 'win': + keystroke.super = true + break + case 'esc': + keystroke.key = 'escape' + break + case 'return': + keystroke.key = 'enter' + break + case 'space': + keystroke.key = ' ' + break + case '↑': + keystroke.key = 'up' + break + case '↓': + keystroke.key = 'down' + break + case '←': + keystroke.key = 'left' + break + case '→': + keystroke.key = 'right' + break + default: + keystroke.key = lower + break + } + } + + return keystroke +} + +/** + * Parse a chord string like "ctrl+k ctrl+s" into an array of ParsedKeystrokes. + */ +export function parseChord(input: string): Chord { + // A lone space character IS the space key binding, not a separator + if (input === ' ') return [parseKeystroke('space')] + return input.trim().split(/\s+/).map(parseKeystroke) +} + +/** + * Convert a ParsedKeystroke to its canonical string representation for display. + */ +export function keystrokeToString(ks: ParsedKeystroke): string { + const parts: string[] = [] + if (ks.ctrl) parts.push('ctrl') + if (ks.alt) parts.push('alt') + if (ks.shift) parts.push('shift') + if (ks.meta) parts.push('meta') + if (ks.super) parts.push('cmd') + // Use readable names for display + const displayKey = keyToDisplayName(ks.key) + parts.push(displayKey) + return parts.join('+') +} + +/** + * Map internal key names to human-readable display names. + */ +function keyToDisplayName(key: string): string { + switch (key) { + case 'escape': + return 'Esc' + case ' ': + return 'Space' + case 'tab': + return 'tab' + case 'enter': + return 'Enter' + case 'backspace': + return 'Backspace' + case 'delete': + return 'Delete' + case 'up': + return '↑' + case 'down': + return '↓' + case 'left': + return '←' + case 'right': + return '→' + case 'pageup': + return 'PageUp' + case 'pagedown': + return 'PageDown' + case 'home': + return 'Home' + case 'end': + return 'End' + default: + return key + } +} + +/** + * Convert a Chord to its canonical string representation for display. + */ +export function chordToString(chord: Chord): string { + return chord.map(keystrokeToString).join(' ') +} + +/** + * Display platform type - a subset of Platform that we care about for display. + * WSL and unknown are treated as linux for display purposes. + */ +type DisplayPlatform = 'macos' | 'windows' | 'linux' | 'wsl' | 'unknown' + +/** + * Convert a ParsedKeystroke to a platform-appropriate display string. + * Uses "opt" for alt on macOS, "alt" elsewhere. + */ +export function keystrokeToDisplayString( + ks: ParsedKeystroke, + platform: DisplayPlatform = 'linux', +): string { + const parts: string[] = [] + if (ks.ctrl) parts.push('ctrl') + // Alt/meta are equivalent in terminals, show platform-appropriate name + if (ks.alt || ks.meta) { + // Only macOS uses "opt", all other platforms use "alt" + parts.push(platform === 'macos' ? 'opt' : 'alt') + } + if (ks.shift) parts.push('shift') + if (ks.super) { + parts.push(platform === 'macos' ? 'cmd' : 'super') + } + // Use readable names for display + const displayKey = keyToDisplayName(ks.key) + parts.push(displayKey) + return parts.join('+') +} + +/** + * Convert a Chord to a platform-appropriate display string. + */ +export function chordToDisplayString( + chord: Chord, + platform: DisplayPlatform = 'linux', +): string { + return chord.map(ks => keystrokeToDisplayString(ks, platform)).join(' ') +} + +/** + * Parse keybinding blocks (from JSON config) into a flat list of ParsedBindings. + */ +export function parseBindings(blocks: KeybindingBlock[]): ParsedBinding[] { + const bindings: ParsedBinding[] = [] + for (const block of blocks) { + for (const [key, action] of Object.entries(block.bindings)) { + bindings.push({ + chord: parseChord(key), + action, + context: block.context, + }) + } + } + return bindings +} diff --git a/keybindings/reservedShortcuts.ts b/keybindings/reservedShortcuts.ts new file mode 100644 index 0000000..8223cc3 --- /dev/null +++ b/keybindings/reservedShortcuts.ts @@ -0,0 +1,127 @@ +import { getPlatform } from '../utils/platform.js' + +/** + * Shortcuts that are typically intercepted by the OS, terminal, or shell + * and will likely never reach the application. + */ +export type ReservedShortcut = { + key: string + reason: string + severity: 'error' | 'warning' +} + +/** + * Shortcuts that cannot be rebound - they are hardcoded in Claude Code. + */ +export const NON_REBINDABLE: ReservedShortcut[] = [ + { + key: 'ctrl+c', + reason: 'Cannot be rebound - used for interrupt/exit (hardcoded)', + severity: 'error', + }, + { + key: 'ctrl+d', + reason: 'Cannot be rebound - used for exit (hardcoded)', + severity: 'error', + }, + { + key: 'ctrl+m', + reason: + 'Cannot be rebound - identical to Enter in terminals (both send CR)', + severity: 'error', + }, +] + +/** + * Terminal control shortcuts that are intercepted by the terminal/OS. + * These will likely never reach the application. + * + * Note: ctrl+s (XOFF) and ctrl+q (XON) are NOT included here because: + * - Most modern terminals disable flow control by default + * - We use ctrl+s for the stash feature + */ +export const TERMINAL_RESERVED: ReservedShortcut[] = [ + { + key: 'ctrl+z', + reason: 'Unix process suspend (SIGTSTP)', + severity: 'warning', + }, + { + key: 'ctrl+\\', + reason: 'Terminal quit signal (SIGQUIT)', + severity: 'error', + }, +] + +/** + * macOS-specific shortcuts that the OS intercepts. + */ +export const MACOS_RESERVED: ReservedShortcut[] = [ + { key: 'cmd+c', reason: 'macOS system copy', severity: 'error' }, + { key: 'cmd+v', reason: 'macOS system paste', severity: 'error' }, + { key: 'cmd+x', reason: 'macOS system cut', severity: 'error' }, + { key: 'cmd+q', reason: 'macOS quit application', severity: 'error' }, + { key: 'cmd+w', reason: 'macOS close window/tab', severity: 'error' }, + { key: 'cmd+tab', reason: 'macOS app switcher', severity: 'error' }, + { key: 'cmd+space', reason: 'macOS Spotlight', severity: 'error' }, +] + +/** + * Get all reserved shortcuts for the current platform. + * Includes non-rebindable shortcuts and terminal-reserved shortcuts. + */ +export function getReservedShortcuts(): ReservedShortcut[] { + const platform = getPlatform() + // Non-rebindable shortcuts first (highest priority) + const reserved = [...NON_REBINDABLE, ...TERMINAL_RESERVED] + + if (platform === 'macos') { + reserved.push(...MACOS_RESERVED) + } + + return reserved +} + +/** + * Normalize a key string for comparison (lowercase, sorted modifiers). + * Chords (space-separated steps like "ctrl+x ctrl+b") are normalized + * per-step — splitting on '+' first would mangle "x ctrl" into a mainKey + * overwritten by the next step, collapsing the chord into its last key. + */ +export function normalizeKeyForComparison(key: string): string { + return key.trim().split(/\s+/).map(normalizeStep).join(' ') +} + +function normalizeStep(step: string): string { + const parts = step.split('+') + const modifiers: string[] = [] + let mainKey = '' + + for (const part of parts) { + const lower = part.trim().toLowerCase() + if ( + [ + 'ctrl', + 'control', + 'alt', + 'opt', + 'option', + 'meta', + 'cmd', + 'command', + 'shift', + ].includes(lower) + ) { + // Normalize modifier names + if (lower === 'control') modifiers.push('ctrl') + else if (lower === 'option' || lower === 'opt') modifiers.push('alt') + else if (lower === 'command' || lower === 'cmd') modifiers.push('cmd') + else modifiers.push(lower) + } else { + mainKey = lower + } + } + + modifiers.sort() + return [...modifiers, mainKey].join('+') +} diff --git a/keybindings/resolver.ts b/keybindings/resolver.ts new file mode 100644 index 0000000..7464049 --- /dev/null +++ b/keybindings/resolver.ts @@ -0,0 +1,244 @@ +import type { Key } from '../ink.js' +import { getKeyName, matchesBinding } from './match.js' +import { chordToString } from './parser.js' +import type { + KeybindingContextName, + ParsedBinding, + ParsedKeystroke, +} from './types.js' + +export type ResolveResult = + | { type: 'match'; action: string } + | { type: 'none' } + | { type: 'unbound' } + +export type ChordResolveResult = + | { type: 'match'; action: string } + | { type: 'none' } + | { type: 'unbound' } + | { type: 'chord_started'; pending: ParsedKeystroke[] } + | { type: 'chord_cancelled' } + +/** + * Resolve a key input to an action. + * Pure function - no state, no side effects, just matching logic. + * + * @param input - The character input from Ink + * @param key - The Key object from Ink with modifier flags + * @param activeContexts - Array of currently active contexts (e.g., ['Chat', 'Global']) + * @param bindings - All parsed bindings to search through + * @returns The resolution result + */ +export function resolveKey( + input: string, + key: Key, + activeContexts: KeybindingContextName[], + bindings: ParsedBinding[], +): ResolveResult { + // Find matching bindings (last one wins for user overrides) + let match: ParsedBinding | undefined + const ctxSet = new Set(activeContexts) + + for (const binding of bindings) { + // Phase 1: Only single-keystroke bindings + if (binding.chord.length !== 1) continue + if (!ctxSet.has(binding.context)) continue + + if (matchesBinding(input, key, binding)) { + match = binding + } + } + + if (!match) { + return { type: 'none' } + } + + if (match.action === null) { + return { type: 'unbound' } + } + + return { type: 'match', action: match.action } +} + +/** + * Get display text for an action from bindings (e.g., "ctrl+t" for "app:toggleTodos"). + * Searches in reverse order so user overrides take precedence. + */ +export function getBindingDisplayText( + action: string, + context: KeybindingContextName, + bindings: ParsedBinding[], +): string | undefined { + // Find the last binding for this action in this context + const binding = bindings.findLast( + b => b.action === action && b.context === context, + ) + return binding ? chordToString(binding.chord) : undefined +} + +/** + * Build a ParsedKeystroke from Ink's input/key. + */ +function buildKeystroke(input: string, key: Key): ParsedKeystroke | null { + const keyName = getKeyName(input, key) + if (!keyName) return null + + // QUIRK: Ink sets key.meta=true when escape is pressed (see input-event.ts). + // This is legacy terminal behavior - we should NOT record this as a modifier + // for the escape key itself, otherwise chord matching will fail. + const effectiveMeta = key.escape ? false : key.meta + + return { + key: keyName, + ctrl: key.ctrl, + alt: effectiveMeta, + shift: key.shift, + meta: effectiveMeta, + super: key.super, + } +} + +/** + * Compare two ParsedKeystrokes for equality. Collapses alt/meta into + * one logical modifier — legacy terminals can't distinguish them (see + * match.ts modifiersMatch), so "alt+k" and "meta+k" are the same key. + * Super (cmd/win) is distinct — only arrives via kitty keyboard protocol. + */ +export function keystrokesEqual( + a: ParsedKeystroke, + b: ParsedKeystroke, +): boolean { + return ( + a.key === b.key && + a.ctrl === b.ctrl && + a.shift === b.shift && + (a.alt || a.meta) === (b.alt || b.meta) && + a.super === b.super + ) +} + +/** + * Check if a chord prefix matches the beginning of a binding's chord. + */ +function chordPrefixMatches( + prefix: ParsedKeystroke[], + binding: ParsedBinding, +): boolean { + if (prefix.length >= binding.chord.length) return false + for (let i = 0; i < prefix.length; i++) { + const prefixKey = prefix[i] + const bindingKey = binding.chord[i] + if (!prefixKey || !bindingKey) return false + if (!keystrokesEqual(prefixKey, bindingKey)) return false + } + return true +} + +/** + * Check if a full chord matches a binding's chord. + */ +function chordExactlyMatches( + chord: ParsedKeystroke[], + binding: ParsedBinding, +): boolean { + if (chord.length !== binding.chord.length) return false + for (let i = 0; i < chord.length; i++) { + const chordKey = chord[i] + const bindingKey = binding.chord[i] + if (!chordKey || !bindingKey) return false + if (!keystrokesEqual(chordKey, bindingKey)) return false + } + return true +} + +/** + * Resolve a key with chord state support. + * + * This function handles multi-keystroke chord bindings like "ctrl+k ctrl+s". + * + * @param input - The character input from Ink + * @param key - The Key object from Ink with modifier flags + * @param activeContexts - Array of currently active contexts + * @param bindings - All parsed bindings + * @param pending - Current chord state (null if not in a chord) + * @returns Resolution result with chord state + */ +export function resolveKeyWithChordState( + input: string, + key: Key, + activeContexts: KeybindingContextName[], + bindings: ParsedBinding[], + pending: ParsedKeystroke[] | null, +): ChordResolveResult { + // Cancel chord on escape + if (key.escape && pending !== null) { + return { type: 'chord_cancelled' } + } + + // Build current keystroke + const currentKeystroke = buildKeystroke(input, key) + if (!currentKeystroke) { + if (pending !== null) { + return { type: 'chord_cancelled' } + } + return { type: 'none' } + } + + // Build the full chord sequence to test + const testChord = pending + ? [...pending, currentKeystroke] + : [currentKeystroke] + + // Filter bindings by active contexts (Set lookup: O(n) instead of O(n·m)) + const ctxSet = new Set(activeContexts) + const contextBindings = bindings.filter(b => ctxSet.has(b.context)) + + // Check if this could be a prefix for longer chords. Group by chord + // string so a later null-override shadows the default it unbinds — + // otherwise null-unbinding `ctrl+x ctrl+k` still makes `ctrl+x` enter + // chord-wait and the single-key binding on the prefix never fires. + const chordWinners = new Map() + for (const binding of contextBindings) { + if ( + binding.chord.length > testChord.length && + chordPrefixMatches(testChord, binding) + ) { + chordWinners.set(chordToString(binding.chord), binding.action) + } + } + let hasLongerChords = false + for (const action of chordWinners.values()) { + if (action !== null) { + hasLongerChords = true + break + } + } + + // If this keystroke could start a longer chord, prefer that + // (even if there's an exact single-key match) + if (hasLongerChords) { + return { type: 'chord_started', pending: testChord } + } + + // Check for exact matches (last one wins) + let exactMatch: ParsedBinding | undefined + for (const binding of contextBindings) { + if (chordExactlyMatches(testChord, binding)) { + exactMatch = binding + } + } + + if (exactMatch) { + if (exactMatch.action === null) { + return { type: 'unbound' } + } + return { type: 'match', action: exactMatch.action } + } + + // No match and no potential longer chords + if (pending !== null) { + return { type: 'chord_cancelled' } + } + + return { type: 'none' } +} diff --git a/keybindings/schema.ts b/keybindings/schema.ts new file mode 100644 index 0000000..3e61d63 --- /dev/null +++ b/keybindings/schema.ts @@ -0,0 +1,236 @@ +/** + * Zod schema for keybindings.json configuration. + * Used for validation and JSON schema generation. + */ + +import { z } from 'zod/v4' +import { lazySchema } from '../utils/lazySchema.js' + +/** + * Valid context names where keybindings can be applied. + */ +export const KEYBINDING_CONTEXTS = [ + 'Global', + 'Chat', + 'Autocomplete', + 'Confirmation', + 'Help', + 'Transcript', + 'HistorySearch', + 'Task', + 'ThemePicker', + 'Settings', + 'Tabs', + // New contexts for keybindings migration + 'Attachments', + 'Footer', + 'MessageSelector', + 'DiffDialog', + 'ModelPicker', + 'Select', + 'Plugin', +] as const + +/** + * Human-readable descriptions for each keybinding context. + */ +export const KEYBINDING_CONTEXT_DESCRIPTIONS: Record< + (typeof KEYBINDING_CONTEXTS)[number], + string +> = { + Global: 'Active everywhere, regardless of focus', + Chat: 'When the chat input is focused', + Autocomplete: 'When autocomplete menu is visible', + Confirmation: 'When a confirmation/permission dialog is shown', + Help: 'When the help overlay is open', + Transcript: 'When viewing the transcript', + HistorySearch: 'When searching command history (ctrl+r)', + Task: 'When a task/agent is running in the foreground', + ThemePicker: 'When the theme picker is open', + Settings: 'When the settings menu is open', + Tabs: 'When tab navigation is active', + Attachments: 'When navigating image attachments in a select dialog', + Footer: 'When footer indicators are focused', + MessageSelector: 'When the message selector (rewind) is open', + DiffDialog: 'When the diff dialog is open', + ModelPicker: 'When the model picker is open', + Select: 'When a select/list component is focused', + Plugin: 'When the plugin dialog is open', +} + +/** + * All valid keybinding action identifiers. + */ +export const KEYBINDING_ACTIONS = [ + // App-level actions (Global context) + 'app:interrupt', + 'app:exit', + 'app:toggleTodos', + 'app:toggleTranscript', + 'app:toggleBrief', + 'app:toggleTeammatePreview', + 'app:toggleTerminal', + 'app:redraw', + 'app:globalSearch', + 'app:quickOpen', + // History navigation + 'history:search', + 'history:previous', + 'history:next', + // Chat input actions + 'chat:cancel', + 'chat:killAgents', + 'chat:cycleMode', + 'chat:modelPicker', + 'chat:fastMode', + 'chat:thinkingToggle', + 'chat:submit', + 'chat:newline', + 'chat:undo', + 'chat:externalEditor', + 'chat:stash', + 'chat:imagePaste', + 'chat:messageActions', + // Autocomplete menu actions + 'autocomplete:accept', + 'autocomplete:dismiss', + 'autocomplete:previous', + 'autocomplete:next', + // Confirmation dialog actions + 'confirm:yes', + 'confirm:no', + 'confirm:previous', + 'confirm:next', + 'confirm:nextField', + 'confirm:previousField', + 'confirm:cycleMode', + 'confirm:toggle', + 'confirm:toggleExplanation', + // Tabs navigation actions + 'tabs:next', + 'tabs:previous', + // Transcript viewer actions + 'transcript:toggleShowAll', + 'transcript:exit', + // History search actions + 'historySearch:next', + 'historySearch:accept', + 'historySearch:cancel', + 'historySearch:execute', + // Task/agent actions + 'task:background', + // Theme picker actions + 'theme:toggleSyntaxHighlighting', + // Help menu actions + 'help:dismiss', + // Attachment navigation (select dialog image attachments) + 'attachments:next', + 'attachments:previous', + 'attachments:remove', + 'attachments:exit', + // Footer indicator actions + 'footer:up', + 'footer:down', + 'footer:next', + 'footer:previous', + 'footer:openSelected', + 'footer:clearSelection', + 'footer:close', + // Message selector (rewind) actions + 'messageSelector:up', + 'messageSelector:down', + 'messageSelector:top', + 'messageSelector:bottom', + 'messageSelector:select', + // Diff dialog actions + 'diff:dismiss', + 'diff:previousSource', + 'diff:nextSource', + 'diff:back', + 'diff:viewDetails', + 'diff:previousFile', + 'diff:nextFile', + // Model picker actions (ant-only) + 'modelPicker:decreaseEffort', + 'modelPicker:increaseEffort', + // Select component actions (distinct from confirm: to avoid collisions) + 'select:next', + 'select:previous', + 'select:accept', + 'select:cancel', + // Plugin dialog actions + 'plugin:toggle', + 'plugin:install', + // Permission dialog actions + 'permission:toggleDebug', + // Settings config panel actions + 'settings:search', + 'settings:retry', + 'settings:close', + // Voice actions + 'voice:pushToTalk', +] as const + +/** + * Schema for a single keybinding block. + */ +export const KeybindingBlockSchema = lazySchema(() => + z + .object({ + context: z + .enum(KEYBINDING_CONTEXTS) + .describe( + 'UI context where these bindings apply. Global bindings work everywhere.', + ), + bindings: z + .record( + z + .string() + .describe('Keystroke pattern (e.g., "ctrl+k", "shift+tab")'), + z + .union([ + z.enum(KEYBINDING_ACTIONS), + z + .string() + .regex(/^command:[a-zA-Z0-9:\-_]+$/) + .describe( + 'Command binding (e.g., "command:help", "command:compact"). Executes the slash command as if typed.', + ), + z.null().describe('Set to null to unbind a default shortcut'), + ]) + .describe( + 'Action to trigger, command to invoke, or null to unbind', + ), + ) + .describe('Map of keystroke patterns to actions'), + }) + .describe('A block of keybindings for a specific context'), +) + +/** + * Schema for the entire keybindings.json file. + * Uses object wrapper format with optional $schema and $docs metadata. + */ +export const KeybindingsSchema = lazySchema(() => + z + .object({ + $schema: z + .string() + .optional() + .describe('JSON Schema URL for editor validation'), + $docs: z.string().optional().describe('Documentation URL'), + bindings: z + .array(KeybindingBlockSchema()) + .describe('Array of keybinding blocks by context'), + }) + .describe( + 'Claude Code keybindings configuration. Customize keyboard shortcuts by context.', + ), +) + +/** + * TypeScript types derived from the schema. + */ +export type KeybindingsSchemaType = z.infer< + ReturnType +> diff --git a/keybindings/shortcutFormat.ts b/keybindings/shortcutFormat.ts new file mode 100644 index 0000000..45db3b0 --- /dev/null +++ b/keybindings/shortcutFormat.ts @@ -0,0 +1,63 @@ +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { loadKeybindingsSync } from './loadUserBindings.js' +import { getBindingDisplayText } from './resolver.js' +import type { KeybindingContextName } from './types.js' + +// TODO(keybindings-migration): Remove fallback parameter after migration is +// complete and we've confirmed no 'keybinding_fallback_used' events are being +// logged. The fallback exists as a safety net during migration - if bindings +// fail to load or an action isn't found, we fall back to hardcoded values. +// Once stable, callers should be able to trust that getBindingDisplayText +// always returns a value for known actions, and we can remove this defensive +// pattern. + +// Track which action+context pairs have already logged a fallback event +// to avoid duplicate events from repeated calls in non-React contexts. +const LOGGED_FALLBACKS = new Set() + +/** + * Get the display text for a configured shortcut without React hooks. + * Use this in non-React contexts (commands, services, etc.). + * + * This lives in its own module (not useShortcutDisplay.ts) so that + * non-React callers like query/stopHooks.ts don't pull React into their + * module graph via the sibling hook. + * + * @param action - The action name (e.g., 'app:toggleTranscript') + * @param context - The keybinding context (e.g., 'Global') + * @param fallback - Fallback text if binding not found + * @returns The configured shortcut display text + * + * @example + * const expandShortcut = getShortcutDisplay('app:toggleTranscript', 'Global', 'ctrl+o') + * // Returns the user's configured binding, or 'ctrl+o' as default + */ +export function getShortcutDisplay( + action: string, + context: KeybindingContextName, + fallback: string, +): string { + const bindings = loadKeybindingsSync() + const resolved = getBindingDisplayText(action, context, bindings) + if (resolved === undefined) { + const key = `${action}:${context}` + if (!LOGGED_FALLBACKS.has(key)) { + LOGGED_FALLBACKS.add(key) + logEvent('tengu_keybinding_fallback_used', { + action: + action as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + context: + context as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + fallback: + fallback as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + reason: + 'action_not_found' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + } + return fallback + } + return resolved +} diff --git a/keybindings/template.ts b/keybindings/template.ts new file mode 100644 index 0000000..fafdcd8 --- /dev/null +++ b/keybindings/template.ts @@ -0,0 +1,52 @@ +/** + * Keybindings template generator. + * Generates a well-documented template file for ~/.claude/keybindings.json + */ + +import { jsonStringify } from '../utils/slowOperations.js' +import { DEFAULT_BINDINGS } from './defaultBindings.js' +import { + NON_REBINDABLE, + normalizeKeyForComparison, +} from './reservedShortcuts.js' +import type { KeybindingBlock } from './types.js' + +/** + * Filter out reserved shortcuts that cannot be rebound. + * These would cause /doctor to warn, so we exclude them from the template. + */ +function filterReservedShortcuts(blocks: KeybindingBlock[]): KeybindingBlock[] { + const reservedKeys = new Set( + NON_REBINDABLE.map(r => normalizeKeyForComparison(r.key)), + ) + + return blocks + .map(block => { + const filteredBindings: Record = {} + for (const [key, action] of Object.entries(block.bindings)) { + if (!reservedKeys.has(normalizeKeyForComparison(key))) { + filteredBindings[key] = action + } + } + return { context: block.context, bindings: filteredBindings } + }) + .filter(block => Object.keys(block.bindings).length > 0) +} + +/** + * Generate a template keybindings.json file content. + * Creates a fully valid JSON file with all default bindings that users can customize. + */ +export function generateKeybindingsTemplate(): string { + // Filter out reserved shortcuts that cannot be rebound + const bindings = filterReservedShortcuts(DEFAULT_BINDINGS) + + // Format as object wrapper with bindings array + const config = { + $schema: 'https://www.schemastore.org/claude-code-keybindings.json', + $docs: 'https://code.claude.com/docs/en/keybindings', + bindings, + } + + return jsonStringify(config, null, 2) + '\n' +} diff --git a/keybindings/useKeybinding.ts b/keybindings/useKeybinding.ts new file mode 100644 index 0000000..02b07ce --- /dev/null +++ b/keybindings/useKeybinding.ts @@ -0,0 +1,196 @@ +import { useCallback, useEffect } from 'react' +import type { InputEvent } from '../ink/events/input-event.js' +import { type Key, useInput } from '../ink.js' +import { useOptionalKeybindingContext } from './KeybindingContext.js' +import type { KeybindingContextName } from './types.js' + +type Options = { + /** Which context this binding belongs to (default: 'Global') */ + context?: KeybindingContextName + /** Only handle when active (like useInput's isActive) */ + isActive?: boolean +} + +/** + * Ink-native hook for handling a keybinding. + * + * The handler stays in the component (React way). + * The binding (keystroke → action) comes from config. + * + * Supports chord sequences (e.g., "ctrl+k ctrl+s"). When a chord is started, + * the hook will manage the pending state automatically. + * + * Uses stopImmediatePropagation() to prevent other handlers from firing + * once this binding is handled. + * + * @example + * ```tsx + * useKeybinding('app:toggleTodos', () => { + * setShowTodos(prev => !prev) + * }, { context: 'Global' }) + * ``` + */ +export function useKeybinding( + action: string, + handler: () => void | false | Promise, + options: Options = {}, +): void { + const { context = 'Global', isActive = true } = options + const keybindingContext = useOptionalKeybindingContext() + + // Register handler with the context for ChordInterceptor to invoke + useEffect(() => { + if (!keybindingContext || !isActive) return + return keybindingContext.registerHandler({ action, context, handler }) + }, [action, context, handler, keybindingContext, isActive]) + + const handleInput = useCallback( + (input: string, key: Key, event: InputEvent) => { + // If no keybinding context available, skip resolution + if (!keybindingContext) return + + // Build context list: registered active contexts + this context + Global + // More specific contexts (registered ones) take precedence over Global + const contextsToCheck: KeybindingContextName[] = [ + ...keybindingContext.activeContexts, + context, + 'Global', + ] + // Deduplicate while preserving order (first occurrence wins for priority) + const uniqueContexts = [...new Set(contextsToCheck)] + + const result = keybindingContext.resolve(input, key, uniqueContexts) + + switch (result.type) { + case 'match': + // Chord completed (if any) - clear pending state + keybindingContext.setPendingChord(null) + if (result.action === action) { + if (handler() !== false) { + event.stopImmediatePropagation() + } + } + break + case 'chord_started': + // User started a chord sequence - update pending state + keybindingContext.setPendingChord(result.pending) + event.stopImmediatePropagation() + break + case 'chord_cancelled': + // Chord was cancelled (escape or invalid key) + keybindingContext.setPendingChord(null) + break + case 'unbound': + // Explicitly unbound - clear any pending chord + keybindingContext.setPendingChord(null) + event.stopImmediatePropagation() + break + case 'none': + // No match - let other handlers try + break + } + }, + [action, context, handler, keybindingContext], + ) + + useInput(handleInput, { isActive }) +} + +/** + * Handle multiple keybindings in one hook (reduces useInput calls). + * + * Supports chord sequences. When a chord is started, the hook will + * manage the pending state automatically. + * + * @example + * ```tsx + * useKeybindings({ + * 'chat:submit': () => handleSubmit(), + * 'chat:cancel': () => handleCancel(), + * }, { context: 'Chat' }) + * ``` + */ +export function useKeybindings( + // Handler returning `false` means "not consumed" — the event propagates + // to later useInput/useKeybindings handlers. Useful for fall-through: + // e.g. ScrollKeybindingHandler's scroll:line* returns false when the + // ScrollBox content fits (scroll is a no-op), letting a child component's + // handler take the wheel event for list navigation instead. Promise + // is allowed for fire-and-forget async handlers (the `!== false` check + // only skips propagation for a sync `false`, not a pending Promise). + handlers: Record void | false | Promise>, + options: Options = {}, +): void { + const { context = 'Global', isActive = true } = options + const keybindingContext = useOptionalKeybindingContext() + + // Register all handlers with the context for ChordInterceptor to invoke + useEffect(() => { + if (!keybindingContext || !isActive) return + + const unregisterFns: Array<() => void> = [] + for (const [action, handler] of Object.entries(handlers)) { + unregisterFns.push( + keybindingContext.registerHandler({ action, context, handler }), + ) + } + + return () => { + for (const unregister of unregisterFns) { + unregister() + } + } + }, [context, handlers, keybindingContext, isActive]) + + const handleInput = useCallback( + (input: string, key: Key, event: InputEvent) => { + // If no keybinding context available, skip resolution + if (!keybindingContext) return + + // Build context list: registered active contexts + this context + Global + // More specific contexts (registered ones) take precedence over Global + const contextsToCheck: KeybindingContextName[] = [ + ...keybindingContext.activeContexts, + context, + 'Global', + ] + // Deduplicate while preserving order (first occurrence wins for priority) + const uniqueContexts = [...new Set(contextsToCheck)] + + const result = keybindingContext.resolve(input, key, uniqueContexts) + + switch (result.type) { + case 'match': + // Chord completed (if any) - clear pending state + keybindingContext.setPendingChord(null) + if (result.action in handlers) { + const handler = handlers[result.action] + if (handler && handler() !== false) { + event.stopImmediatePropagation() + } + } + break + case 'chord_started': + // User started a chord sequence - update pending state + keybindingContext.setPendingChord(result.pending) + event.stopImmediatePropagation() + break + case 'chord_cancelled': + // Chord was cancelled (escape or invalid key) + keybindingContext.setPendingChord(null) + break + case 'unbound': + // Explicitly unbound - clear any pending chord + keybindingContext.setPendingChord(null) + event.stopImmediatePropagation() + break + case 'none': + // No match - let other handlers try + break + } + }, + [context, handlers, keybindingContext], + ) + + useInput(handleInput, { isActive }) +} diff --git a/keybindings/useShortcutDisplay.ts b/keybindings/useShortcutDisplay.ts new file mode 100644 index 0000000..d821748 --- /dev/null +++ b/keybindings/useShortcutDisplay.ts @@ -0,0 +1,59 @@ +import { useEffect, useRef } from 'react' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { useOptionalKeybindingContext } from './KeybindingContext.js' +import type { KeybindingContextName } from './types.js' + +// TODO(keybindings-migration): Remove fallback parameter after migration is complete +// and we've confirmed no 'keybinding_fallback_used' events are being logged. +// The fallback exists as a safety net during migration - if bindings fail to load +// or an action isn't found, we fall back to hardcoded values. Once stable, callers +// should be able to trust that getBindingDisplayText always returns a value for +// known actions, and we can remove this defensive pattern. + +/** + * Hook to get the display text for a configured shortcut. + * Returns the configured binding or a fallback if unavailable. + * + * @param action - The action name (e.g., 'app:toggleTranscript') + * @param context - The keybinding context (e.g., 'Global') + * @param fallback - Fallback text if keybinding context unavailable + * @returns The configured shortcut display text + * + * @example + * const expandShortcut = useShortcutDisplay('app:toggleTranscript', 'Global', 'ctrl+o') + * // Returns the user's configured binding, or 'ctrl+o' as default + */ +export function useShortcutDisplay( + action: string, + context: KeybindingContextName, + fallback: string, +): string { + const keybindingContext = useOptionalKeybindingContext() + const resolved = keybindingContext?.getDisplayText(action, context) + const isFallback = resolved === undefined + const reason = keybindingContext ? 'action_not_found' : 'no_context' + + // Log fallback usage once per mount (not on every render) to avoid + // flooding analytics with events from frequent re-renders. + const hasLoggedRef = useRef(false) + useEffect(() => { + if (isFallback && !hasLoggedRef.current) { + hasLoggedRef.current = true + logEvent('tengu_keybinding_fallback_used', { + action: + action as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + context: + context as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + fallback: + fallback as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + reason: + reason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + } + }, [isFallback, action, context, fallback, reason]) + + return isFallback ? fallback : resolved +} diff --git a/keybindings/validate.ts b/keybindings/validate.ts new file mode 100644 index 0000000..5ea5c4c --- /dev/null +++ b/keybindings/validate.ts @@ -0,0 +1,498 @@ +import { plural } from '../utils/stringUtils.js' +import { chordToString, parseChord, parseKeystroke } from './parser.js' +import { + getReservedShortcuts, + normalizeKeyForComparison, +} from './reservedShortcuts.js' +import type { + KeybindingBlock, + KeybindingContextName, + ParsedBinding, +} from './types.js' + +/** + * Types of validation issues that can occur with keybindings. + */ +export type KeybindingWarningType = + | 'parse_error' + | 'duplicate' + | 'reserved' + | 'invalid_context' + | 'invalid_action' + +/** + * A warning or error about a keybinding configuration issue. + */ +export type KeybindingWarning = { + type: KeybindingWarningType + severity: 'error' | 'warning' + message: string + key?: string + context?: string + action?: string + suggestion?: string +} + +/** + * Type guard to check if an object is a valid KeybindingBlock. + */ +function isKeybindingBlock(obj: unknown): obj is KeybindingBlock { + if (typeof obj !== 'object' || obj === null) return false + const b = obj as Record + return ( + typeof b.context === 'string' && + typeof b.bindings === 'object' && + b.bindings !== null + ) +} + +/** + * Type guard to check if an array contains only valid KeybindingBlocks. + */ +function isKeybindingBlockArray(arr: unknown): arr is KeybindingBlock[] { + return Array.isArray(arr) && arr.every(isKeybindingBlock) +} + +/** + * Valid context names for keybindings. + * Must match KeybindingContextName in types.ts + */ +const VALID_CONTEXTS: KeybindingContextName[] = [ + 'Global', + 'Chat', + 'Autocomplete', + 'Confirmation', + 'Help', + 'Transcript', + 'HistorySearch', + 'Task', + 'ThemePicker', + 'Settings', + 'Tabs', + 'Attachments', + 'Footer', + 'MessageSelector', + 'DiffDialog', + 'ModelPicker', + 'Select', + 'Plugin', +] + +/** + * Type guard to check if a string is a valid context name. + */ +function isValidContext(value: string): value is KeybindingContextName { + return (VALID_CONTEXTS as readonly string[]).includes(value) +} + +/** + * Validate a single keystroke string and return any parse errors. + */ +function validateKeystroke(keystroke: string): KeybindingWarning | null { + const parts = keystroke.toLowerCase().split('+') + + for (const part of parts) { + const trimmed = part.trim() + if (!trimmed) { + return { + type: 'parse_error', + severity: 'error', + message: `Empty key part in "${keystroke}"`, + key: keystroke, + suggestion: 'Remove extra "+" characters', + } + } + } + + // Try to parse and see if it fails + const parsed = parseKeystroke(keystroke) + if ( + !parsed.key && + !parsed.ctrl && + !parsed.alt && + !parsed.shift && + !parsed.meta + ) { + return { + type: 'parse_error', + severity: 'error', + message: `Could not parse keystroke "${keystroke}"`, + key: keystroke, + } + } + + return null +} + +/** + * Validate a keybinding block from user config. + */ +function validateBlock( + block: unknown, + blockIndex: number, +): KeybindingWarning[] { + const warnings: KeybindingWarning[] = [] + + if (typeof block !== 'object' || block === null) { + warnings.push({ + type: 'parse_error', + severity: 'error', + message: `Keybinding block ${blockIndex + 1} is not an object`, + }) + return warnings + } + + const b = block as Record + + // Validate context - extract to narrowed variable for type safety + const rawContext = b.context + let contextName: string | undefined + if (typeof rawContext !== 'string') { + warnings.push({ + type: 'parse_error', + severity: 'error', + message: `Keybinding block ${blockIndex + 1} missing "context" field`, + }) + } else if (!isValidContext(rawContext)) { + warnings.push({ + type: 'invalid_context', + severity: 'error', + message: `Unknown context "${rawContext}"`, + context: rawContext, + suggestion: `Valid contexts: ${VALID_CONTEXTS.join(', ')}`, + }) + } else { + contextName = rawContext + } + + // Validate bindings + if (typeof b.bindings !== 'object' || b.bindings === null) { + warnings.push({ + type: 'parse_error', + severity: 'error', + message: `Keybinding block ${blockIndex + 1} missing "bindings" field`, + }) + return warnings + } + + const bindings = b.bindings as Record + for (const [key, action] of Object.entries(bindings)) { + // Validate key syntax + const keyError = validateKeystroke(key) + if (keyError) { + keyError.context = contextName + warnings.push(keyError) + } + + // Validate action + if (action !== null && typeof action !== 'string') { + warnings.push({ + type: 'invalid_action', + severity: 'error', + message: `Invalid action for "${key}": must be a string or null`, + key, + context: contextName, + }) + } else if (typeof action === 'string' && action.startsWith('command:')) { + // Validate command binding format + if (!/^command:[a-zA-Z0-9:\-_]+$/.test(action)) { + warnings.push({ + type: 'invalid_action', + severity: 'warning', + message: `Invalid command binding "${action}" for "${key}": command name may only contain alphanumeric characters, colons, hyphens, and underscores`, + key, + context: contextName, + action, + }) + } + // Command bindings must be in Chat context + if (contextName && contextName !== 'Chat') { + warnings.push({ + type: 'invalid_action', + severity: 'warning', + message: `Command binding "${action}" must be in "Chat" context, not "${contextName}"`, + key, + context: contextName, + action, + suggestion: 'Move this binding to a block with "context": "Chat"', + }) + } + } else if (action === 'voice:pushToTalk') { + // Hold detection needs OS auto-repeat. Bare letters print into the + // input during warmup and the activation strip is best-effort — + // space (default) or a modifier combo like meta+k avoid that. + const ks = parseChord(key)[0] + if ( + ks && + !ks.ctrl && + !ks.alt && + !ks.shift && + !ks.meta && + !ks.super && + /^[a-z]$/.test(ks.key) + ) { + warnings.push({ + type: 'invalid_action', + severity: 'warning', + message: `Binding "${key}" to voice:pushToTalk prints into the input during warmup; use space or a modifier combo like meta+k`, + key, + context: contextName, + action, + }) + } + } + } + + return warnings +} + +/** + * Detect duplicate keys within the same bindings block in a JSON string. + * JSON.parse silently uses the last value for duplicate keys, + * so we need to check the raw string to warn users. + * + * Only warns about duplicates within the same context's bindings object. + * Duplicates across different contexts are allowed (e.g., "enter" in Chat + * and "enter" in Confirmation). + */ +export function checkDuplicateKeysInJson( + jsonString: string, +): KeybindingWarning[] { + const warnings: KeybindingWarning[] = [] + + // Find each "bindings" block and check for duplicates within it + // Pattern: "bindings" : { ... } + const bindingsBlockPattern = + /"bindings"\s*:\s*\{([^{}]*(?:\{[^{}]*\}[^{}]*)*)\}/g + + let blockMatch + while ((blockMatch = bindingsBlockPattern.exec(jsonString)) !== null) { + const blockContent = blockMatch[1] + if (!blockContent) continue + + // Find the context for this block by looking backwards + const textBeforeBlock = jsonString.slice(0, blockMatch.index) + const contextMatch = textBeforeBlock.match( + /"context"\s*:\s*"([^"]+)"[^{]*$/, + ) + const context = contextMatch?.[1] ?? 'unknown' + + // Find all keys within this bindings block + const keyPattern = /"([^"]+)"\s*:/g + const keysByName = new Map() + + let keyMatch + while ((keyMatch = keyPattern.exec(blockContent)) !== null) { + const key = keyMatch[1] + if (!key) continue + + const count = (keysByName.get(key) ?? 0) + 1 + keysByName.set(key, count) + + if (count === 2) { + // Only warn on the second occurrence + warnings.push({ + type: 'duplicate', + severity: 'warning', + message: `Duplicate key "${key}" in ${context} bindings`, + key, + context, + suggestion: `This key appears multiple times in the same context. JSON uses the last value, earlier values are ignored.`, + }) + } + } + } + + return warnings +} + +/** + * Validate user keybinding config and return all warnings. + */ +export function validateUserConfig(userBlocks: unknown): KeybindingWarning[] { + const warnings: KeybindingWarning[] = [] + + if (!Array.isArray(userBlocks)) { + warnings.push({ + type: 'parse_error', + severity: 'error', + message: 'keybindings.json must contain an array', + suggestion: 'Wrap your bindings in [ ]', + }) + return warnings + } + + for (let i = 0; i < userBlocks.length; i++) { + warnings.push(...validateBlock(userBlocks[i], i)) + } + + return warnings +} + +/** + * Check for duplicate bindings within the same context. + * Only checks user bindings (not default + user merged). + */ +export function checkDuplicates( + blocks: KeybindingBlock[], +): KeybindingWarning[] { + const warnings: KeybindingWarning[] = [] + const seenByContext = new Map>() + + for (const block of blocks) { + const contextMap = + seenByContext.get(block.context) ?? new Map() + seenByContext.set(block.context, contextMap) + + for (const [key, action] of Object.entries(block.bindings)) { + const normalizedKey = normalizeKeyForComparison(key) + const existingAction = contextMap.get(normalizedKey) + + if (existingAction && existingAction !== action) { + warnings.push({ + type: 'duplicate', + severity: 'warning', + message: `Duplicate binding "${key}" in ${block.context} context`, + key, + context: block.context, + action: action ?? 'null (unbind)', + suggestion: `Previously bound to "${existingAction}". Only the last binding will be used.`, + }) + } + + contextMap.set(normalizedKey, action ?? 'null') + } + } + + return warnings +} + +/** + * Check for reserved shortcuts that may not work. + */ +export function checkReservedShortcuts( + bindings: ParsedBinding[], +): KeybindingWarning[] { + const warnings: KeybindingWarning[] = [] + const reserved = getReservedShortcuts() + + for (const binding of bindings) { + const keyDisplay = chordToString(binding.chord) + const normalizedKey = normalizeKeyForComparison(keyDisplay) + + // Check against reserved shortcuts + for (const res of reserved) { + if (normalizeKeyForComparison(res.key) === normalizedKey) { + warnings.push({ + type: 'reserved', + severity: res.severity, + message: `"${keyDisplay}" may not work: ${res.reason}`, + key: keyDisplay, + context: binding.context, + action: binding.action ?? undefined, + }) + } + } + } + + return warnings +} + +/** + * Parse user blocks into bindings for validation. + * This is separate from the main parser to avoid importing it. + */ +function getUserBindingsForValidation( + userBlocks: KeybindingBlock[], +): ParsedBinding[] { + const bindings: ParsedBinding[] = [] + for (const block of userBlocks) { + for (const [key, action] of Object.entries(block.bindings)) { + const chord = key.split(' ').map(k => parseKeystroke(k)) + bindings.push({ + chord, + action, + context: block.context, + }) + } + } + return bindings +} + +/** + * Run all validations and return combined warnings. + */ +export function validateBindings( + userBlocks: unknown, + _parsedBindings: ParsedBinding[], +): KeybindingWarning[] { + const warnings: KeybindingWarning[] = [] + + // Validate user config structure + warnings.push(...validateUserConfig(userBlocks)) + + // Check for duplicates in user config + if (isKeybindingBlockArray(userBlocks)) { + warnings.push(...checkDuplicates(userBlocks)) + + // Check for reserved/conflicting shortcuts - only check USER bindings + const userBindings = getUserBindingsForValidation(userBlocks) + warnings.push(...checkReservedShortcuts(userBindings)) + } + + // Deduplicate warnings (same key+context+type) + const seen = new Set() + return warnings.filter(w => { + const key = `${w.type}:${w.key}:${w.context}` + if (seen.has(key)) return false + seen.add(key) + return true + }) +} + +/** + * Format a warning for display to the user. + */ +export function formatWarning(warning: KeybindingWarning): string { + const icon = warning.severity === 'error' ? '✗' : '⚠' + let msg = `${icon} Keybinding ${warning.severity}: ${warning.message}` + + if (warning.suggestion) { + msg += `\n ${warning.suggestion}` + } + + return msg +} + +/** + * Format multiple warnings for display. + */ +export function formatWarnings(warnings: KeybindingWarning[]): string { + if (warnings.length === 0) return '' + + const errors = warnings.filter(w => w.severity === 'error') + const warns = warnings.filter(w => w.severity === 'warning') + + const lines: string[] = [] + + if (errors.length > 0) { + lines.push( + `Found ${errors.length} keybinding ${plural(errors.length, 'error')}:`, + ) + for (const e of errors) { + lines.push(formatWarning(e)) + } + } + + if (warns.length > 0) { + if (lines.length > 0) lines.push('') + lines.push( + `Found ${warns.length} keybinding ${plural(warns.length, 'warning')}:`, + ) + for (const w of warns) { + lines.push(formatWarning(w)) + } + } + + return lines.join('\n') +} diff --git a/main.tsx b/main.tsx new file mode 100644 index 0000000..ea51d90 --- /dev/null +++ b/main.tsx @@ -0,0 +1,4684 @@ +// These side-effects must run before all other imports: +// 1. profileCheckpoint marks entry before heavy module evaluation begins +// 2. startMdmRawRead fires MDM subprocesses (plutil/reg query) so they run in +// parallel with the remaining ~135ms of imports below +// 3. startKeychainPrefetch fires both macOS keychain reads (OAuth + legacy API +// key) in parallel — isRemoteManagedSettingsEligible() otherwise reads them +// sequentially via sync spawn inside applySafeConfigEnvironmentVariables() +// (~65ms on every macOS startup) +import { profileCheckpoint, profileReport } from './utils/startupProfiler.js'; + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +profileCheckpoint('main_tsx_entry'); +import { startMdmRawRead } from './utils/settings/mdm/rawRead.js'; + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +startMdmRawRead(); +import { ensureKeychainPrefetchCompleted, startKeychainPrefetch } from './utils/secureStorage/keychainPrefetch.js'; + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +startKeychainPrefetch(); +import { feature } from 'bun:bundle'; +import { Command as CommanderCommand, InvalidArgumentError, Option } from '@commander-js/extra-typings'; +import chalk from 'chalk'; +import { readFileSync } from 'fs'; +import mapValues from 'lodash-es/mapValues.js'; +import pickBy from 'lodash-es/pickBy.js'; +import uniqBy from 'lodash-es/uniqBy.js'; +import React from 'react'; +import { getOauthConfig } from './constants/oauth.js'; +import { getRemoteSessionUrl } from './constants/product.js'; +import { getSystemContext, getUserContext } from './context.js'; +import { init, initializeTelemetryAfterTrust } from './entrypoints/init.js'; +import { addToHistory } from './history.js'; +import type { Root } from './ink.js'; +import { launchRepl } from './replLauncher.js'; +import { hasGrowthBookEnvOverride, initializeGrowthBook, refreshGrowthBookAfterAuthChange } from './services/analytics/growthbook.js'; +import { fetchBootstrapData } from './services/api/bootstrap.js'; +import { type DownloadResult, downloadSessionFiles, type FilesApiConfig, parseFileSpecs } from './services/api/filesApi.js'; +import { prefetchPassesEligibility } from './services/api/referral.js'; +import { prefetchOfficialMcpUrls } from './services/mcp/officialRegistry.js'; +import type { McpSdkServerConfig, McpServerConfig, ScopedMcpServerConfig } from './services/mcp/types.js'; +import { isPolicyAllowed, loadPolicyLimits, refreshPolicyLimits, waitForPolicyLimitsToLoad } from './services/policyLimits/index.js'; +import { loadRemoteManagedSettings, refreshRemoteManagedSettings } from './services/remoteManagedSettings/index.js'; +import type { ToolInputJSONSchema } from './Tool.js'; +import { createSyntheticOutputTool, isSyntheticOutputToolEnabled } from './tools/SyntheticOutputTool/SyntheticOutputTool.js'; +import { getTools } from './tools.js'; +import { canUserConfigureAdvisor, getInitialAdvisorSetting, isAdvisorEnabled, isValidAdvisorModel, modelSupportsAdvisor } from './utils/advisor.js'; +import { isAgentSwarmsEnabled } from './utils/agentSwarmsEnabled.js'; +import { count, uniq } from './utils/array.js'; +import { installAsciicastRecorder } from './utils/asciicast.js'; +import { getSubscriptionType, isClaudeAISubscriber, prefetchAwsCredentialsAndBedRockInfoIfSafe, prefetchGcpCredentialsIfSafe, validateForceLoginOrg } from './utils/auth.js'; +import { checkHasTrustDialogAccepted, getGlobalConfig, getRemoteControlAtStartup, isAutoUpdaterDisabled, saveGlobalConfig } from './utils/config.js'; +import { seedEarlyInput, stopCapturingEarlyInput } from './utils/earlyInput.js'; +import { getInitialEffortSetting, parseEffortValue } from './utils/effort.js'; +import { getInitialFastModeSetting, isFastModeEnabled, prefetchFastModeStatus, resolveFastModeStatusFromCache } from './utils/fastMode.js'; +import { applyConfigEnvironmentVariables } from './utils/managedEnv.js'; +import { createSystemMessage, createUserMessage } from './utils/messages.js'; +import { getPlatform } from './utils/platform.js'; +import { getBaseRenderOptions } from './utils/renderOptions.js'; +import { getSessionIngressAuthToken } from './utils/sessionIngressAuth.js'; +import { settingsChangeDetector } from './utils/settings/changeDetector.js'; +import { skillChangeDetector } from './utils/skills/skillChangeDetector.js'; +import { jsonParse, writeFileSync_DEPRECATED } from './utils/slowOperations.js'; +import { computeInitialTeamContext } from './utils/swarm/reconnection.js'; +import { initializeWarningHandler } from './utils/warningHandler.js'; +import { isWorktreeModeEnabled } from './utils/worktreeModeEnabled.js'; + +// Lazy require to avoid circular dependency: teammate.ts -> AppState.tsx -> ... -> main.tsx +/* eslint-disable @typescript-eslint/no-require-imports */ +const getTeammateUtils = () => require('./utils/teammate.js') as typeof import('./utils/teammate.js'); +const getTeammatePromptAddendum = () => require('./utils/swarm/teammatePromptAddendum.js') as typeof import('./utils/swarm/teammatePromptAddendum.js'); +const getTeammateModeSnapshot = () => require('./utils/swarm/backends/teammateModeSnapshot.js') as typeof import('./utils/swarm/backends/teammateModeSnapshot.js'); +/* eslint-enable @typescript-eslint/no-require-imports */ +// Dead code elimination: conditional import for COORDINATOR_MODE +/* eslint-disable @typescript-eslint/no-require-imports */ +const coordinatorModeModule = feature('COORDINATOR_MODE') ? require('./coordinator/coordinatorMode.js') as typeof import('./coordinator/coordinatorMode.js') : null; +/* eslint-enable @typescript-eslint/no-require-imports */ +// Dead code elimination: conditional import for KAIROS (assistant mode) +/* eslint-disable @typescript-eslint/no-require-imports */ +const assistantModule = feature('KAIROS') ? require('./assistant/index.js') as typeof import('./assistant/index.js') : null; +const kairosGate = feature('KAIROS') ? require('./assistant/gate.js') as typeof import('./assistant/gate.js') : null; +import { relative, resolve } from 'path'; +import { isAnalyticsDisabled } from 'src/services/analytics/config.js'; +import { getFeatureValue_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { initializeAnalyticsGates } from 'src/services/analytics/sink.js'; +import { getOriginalCwd, setAdditionalDirectoriesForClaudeMd, setIsRemoteMode, setMainLoopModelOverride, setMainThreadAgentType, setTeleportedSessionInfo } from './bootstrap/state.js'; +import { filterCommandsForRemoteMode, getCommands } from './commands.js'; +import type { StatsStore } from './context/stats.js'; +import { launchAssistantInstallWizard, launchAssistantSessionChooser, launchInvalidSettingsDialog, launchResumeChooser, launchSnapshotUpdateDialog, launchTeleportRepoMismatchDialog, launchTeleportResumeWrapper } from './dialogLaunchers.js'; +import { SHOW_CURSOR } from './ink/termio/dec.js'; +import { exitWithError, exitWithMessage, getRenderContext, renderAndRun, showSetupScreens } from './interactiveHelpers.js'; +import { initBuiltinPlugins } from './plugins/bundled/index.js'; +/* eslint-enable @typescript-eslint/no-require-imports */ +import { checkQuotaStatus } from './services/claudeAiLimits.js'; +import { getMcpToolsCommandsAndResources, prefetchAllMcpResources } from './services/mcp/client.js'; +import { VALID_INSTALLABLE_SCOPES, VALID_UPDATE_SCOPES } from './services/plugins/pluginCliCommands.js'; +import { initBundledSkills } from './skills/bundled/index.js'; +import type { AgentColorName } from './tools/AgentTool/agentColorManager.js'; +import { getActiveAgentsFromList, getAgentDefinitionsWithOverrides, isBuiltInAgent, isCustomAgent, parseAgentsFromJson } from './tools/AgentTool/loadAgentsDir.js'; +import type { LogOption } from './types/logs.js'; +import type { Message as MessageType } from './types/message.js'; +import { assertMinVersion } from './utils/autoUpdater.js'; +import { CLAUDE_IN_CHROME_SKILL_HINT, CLAUDE_IN_CHROME_SKILL_HINT_WITH_WEBBROWSER } from './utils/claudeInChrome/prompt.js'; +import { setupClaudeInChrome, shouldAutoEnableClaudeInChrome, shouldEnableClaudeInChrome } from './utils/claudeInChrome/setup.js'; +import { getContextWindowForModel } from './utils/context.js'; +import { loadConversationForResume } from './utils/conversationRecovery.js'; +import { buildDeepLinkBanner } from './utils/deepLink/banner.js'; +import { hasNodeOption, isBareMode, isEnvTruthy, isInProtectedNamespace } from './utils/envUtils.js'; +import { refreshExampleCommands } from './utils/exampleCommands.js'; +import type { FpsMetrics } from './utils/fpsTracker.js'; +import { getWorktreePaths } from './utils/getWorktreePaths.js'; +import { findGitRoot, getBranch, getIsGit, getWorktreeCount } from './utils/git.js'; +import { getGhAuthStatus } from './utils/github/ghAuthStatus.js'; +import { safeParseJSON } from './utils/json.js'; +import { logError } from './utils/log.js'; +import { getModelDeprecationWarning } from './utils/model/deprecation.js'; +import { getDefaultMainLoopModel, getUserSpecifiedModelSetting, normalizeModelStringForAPI, parseUserSpecifiedModel } from './utils/model/model.js'; +import { ensureModelStringsInitialized } from './utils/model/modelStrings.js'; +import { PERMISSION_MODES } from './utils/permissions/PermissionMode.js'; +import { checkAndDisableBypassPermissions, getAutoModeEnabledStateIfCached, initializeToolPermissionContext, initialPermissionModeFromCLI, isDefaultPermissionModeAuto, parseToolListFromCLI, removeDangerousPermissions, stripDangerousPermissionsForAutoMode, verifyAutoModeGateAccess } from './utils/permissions/permissionSetup.js'; +import { cleanupOrphanedPluginVersionsInBackground } from './utils/plugins/cacheUtils.js'; +import { initializeVersionedPlugins } from './utils/plugins/installedPluginsManager.js'; +import { getManagedPluginNames } from './utils/plugins/managedPlugins.js'; +import { getGlobExclusionsForPluginCache } from './utils/plugins/orphanedPluginFilter.js'; +import { getPluginSeedDirs } from './utils/plugins/pluginDirectories.js'; +import { countFilesRoundedRg } from './utils/ripgrep.js'; +import { processSessionStartHooks, processSetupHooks } from './utils/sessionStart.js'; +import { cacheSessionTitle, getSessionIdFromLog, loadTranscriptFromFile, saveAgentSetting, saveMode, searchSessionsByCustomTitle, sessionIdExists } from './utils/sessionStorage.js'; +import { ensureMdmSettingsLoaded } from './utils/settings/mdm/settings.js'; +import { getInitialSettings, getManagedSettingsKeysForLogging, getSettingsForSource, getSettingsWithErrors } from './utils/settings/settings.js'; +import { resetSettingsCache } from './utils/settings/settingsCache.js'; +import type { ValidationError } from './utils/settings/validation.js'; +import { DEFAULT_TASKS_MODE_TASK_LIST_ID, TASK_STATUSES } from './utils/tasks.js'; +import { logPluginLoadErrors, logPluginsEnabledForSession } from './utils/telemetry/pluginTelemetry.js'; +import { logSkillsLoaded } from './utils/telemetry/skillLoadedEvent.js'; +import { generateTempFilePath } from './utils/tempfile.js'; +import { validateUuid } from './utils/uuid.js'; +// Plugin startup checks are now handled non-blockingly in REPL.tsx + +import { registerMcpAddCommand } from 'src/commands/mcp/addCommand.js'; +import { registerMcpXaaIdpCommand } from 'src/commands/mcp/xaaIdpCommand.js'; +import { logPermissionContextForAnts } from 'src/services/internalLogging.js'; +import { fetchClaudeAIMcpConfigsIfEligible } from 'src/services/mcp/claudeai.js'; +import { clearServerCache } from 'src/services/mcp/client.js'; +import { areMcpConfigsAllowedWithEnterpriseMcpConfig, dedupClaudeAiMcpServers, doesEnterpriseMcpConfigExist, filterMcpServersByPolicy, getClaudeCodeMcpConfigs, getMcpServerSignature, parseMcpConfig, parseMcpConfigFromFilePath } from 'src/services/mcp/config.js'; +import { excludeCommandsByServer, excludeResourcesByServer } from 'src/services/mcp/utils.js'; +import { isXaaEnabled } from 'src/services/mcp/xaaIdpLogin.js'; +import { getRelevantTips } from 'src/services/tips/tipRegistry.js'; +import { logContextMetrics } from 'src/utils/api.js'; +import { CLAUDE_IN_CHROME_MCP_SERVER_NAME, isClaudeInChromeMCPServer } from 'src/utils/claudeInChrome/common.js'; +import { registerCleanup } from 'src/utils/cleanupRegistry.js'; +import { eagerParseCliFlag } from 'src/utils/cliArgs.js'; +import { createEmptyAttributionState } from 'src/utils/commitAttribution.js'; +import { countConcurrentSessions, registerSession, updateSessionName } from 'src/utils/concurrentSessions.js'; +import { getCwd } from 'src/utils/cwd.js'; +import { logForDebugging, setHasFormattedOutput } from 'src/utils/debug.js'; +import { errorMessage, getErrnoCode, isENOENT, TeleportOperationError, toError } from 'src/utils/errors.js'; +import { getFsImplementation, safeResolvePath } from 'src/utils/fsOperations.js'; +import { gracefulShutdown, gracefulShutdownSync } from 'src/utils/gracefulShutdown.js'; +import { setAllHookEventsEnabled } from 'src/utils/hooks/hookEvents.js'; +import { refreshModelCapabilities } from 'src/utils/model/modelCapabilities.js'; +import { peekForStdinData, writeToStderr } from 'src/utils/process.js'; +import { setCwd } from 'src/utils/Shell.js'; +import { type ProcessedResume, processResumedConversation } from 'src/utils/sessionRestore.js'; +import { parseSettingSourcesFlag } from 'src/utils/settings/constants.js'; +import { plural } from 'src/utils/stringUtils.js'; +import { type ChannelEntry, getInitialMainLoopModel, getIsNonInteractiveSession, getSdkBetas, getSessionId, getUserMsgOptIn, setAllowedChannels, setAllowedSettingSources, setChromeFlagOverride, setClientType, setCwdState, setDirectConnectServerUrl, setFlagSettingsPath, setInitialMainLoopModel, setInlinePlugins, setIsInteractive, setKairosActive, setOriginalCwd, setQuestionPreviewFormat, setSdkBetas, setSessionBypassPermissionsMode, setSessionPersistenceDisabled, setSessionSource, setUserMsgOptIn, switchSession } from './bootstrap/state.js'; + +/* eslint-disable @typescript-eslint/no-require-imports */ +const autoModeStateModule = feature('TRANSCRIPT_CLASSIFIER') ? require('./utils/permissions/autoModeState.js') as typeof import('./utils/permissions/autoModeState.js') : null; + +// TeleportRepoMismatchDialog, TeleportResumeWrapper dynamically imported at call sites +import { migrateAutoUpdatesToSettings } from './migrations/migrateAutoUpdatesToSettings.js'; +import { migrateBypassPermissionsAcceptedToSettings } from './migrations/migrateBypassPermissionsAcceptedToSettings.js'; +import { migrateEnableAllProjectMcpServersToSettings } from './migrations/migrateEnableAllProjectMcpServersToSettings.js'; +import { migrateFennecToOpus } from './migrations/migrateFennecToOpus.js'; +import { migrateLegacyOpusToCurrent } from './migrations/migrateLegacyOpusToCurrent.js'; +import { migrateOpusToOpus1m } from './migrations/migrateOpusToOpus1m.js'; +import { migrateReplBridgeEnabledToRemoteControlAtStartup } from './migrations/migrateReplBridgeEnabledToRemoteControlAtStartup.js'; +import { migrateSonnet1mToSonnet45 } from './migrations/migrateSonnet1mToSonnet45.js'; +import { migrateSonnet45ToSonnet46 } from './migrations/migrateSonnet45ToSonnet46.js'; +import { resetAutoModeOptInForDefaultOffer } from './migrations/resetAutoModeOptInForDefaultOffer.js'; +import { resetProToOpusDefault } from './migrations/resetProToOpusDefault.js'; +import { createRemoteSessionConfig } from './remote/RemoteSessionManager.js'; +/* eslint-enable @typescript-eslint/no-require-imports */ +// teleportWithProgress dynamically imported at call site +import { createDirectConnectSession, DirectConnectError } from './server/createDirectConnectSession.js'; +import { initializeLspServerManager } from './services/lsp/manager.js'; +import { shouldEnablePromptSuggestion } from './services/PromptSuggestion/promptSuggestion.js'; +import { type AppState, getDefaultAppState, IDLE_SPECULATION_STATE } from './state/AppStateStore.js'; +import { onChangeAppState } from './state/onChangeAppState.js'; +import { createStore } from './state/store.js'; +import { asSessionId } from './types/ids.js'; +import { filterAllowedSdkBetas } from './utils/betas.js'; +import { isInBundledMode, isRunningWithBun } from './utils/bundledMode.js'; +import { logForDiagnosticsNoPII } from './utils/diagLogs.js'; +import { filterExistingPaths, getKnownPathsForRepo } from './utils/githubRepoPathMapping.js'; +import { clearPluginCache, loadAllPluginsCacheOnly } from './utils/plugins/pluginLoader.js'; +import { migrateChangelogFromConfig } from './utils/releaseNotes.js'; +import { SandboxManager } from './utils/sandbox/sandbox-adapter.js'; +import { fetchSession, prepareApiRequest } from './utils/teleport/api.js'; +import { checkOutTeleportedSessionBranch, processMessagesForTeleportResume, teleportToRemoteWithErrorHandling, validateGitState, validateSessionRepository } from './utils/teleport.js'; +import { shouldEnableThinkingByDefault, type ThinkingConfig } from './utils/thinking.js'; +import { initUser, resetUserCache } from './utils/user.js'; +import { getTmuxInstallInstructions, isTmuxAvailable, parsePRReference } from './utils/worktree.js'; + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +profileCheckpoint('main_tsx_imports_loaded'); + +/** + * Log managed settings keys to Statsig for analytics. + * This is called after init() completes to ensure settings are loaded + * and environment variables are applied before model resolution. + */ +function logManagedSettings(): void { + try { + const policySettings = getSettingsForSource('policySettings'); + if (policySettings) { + const allKeys = getManagedSettingsKeysForLogging(policySettings); + logEvent('tengu_managed_settings_loaded', { + keyCount: allKeys.length, + keys: allKeys.join(',') as unknown as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + } catch { + // Silently ignore errors - this is just for analytics + } +} + +// Check if running in debug/inspection mode +function isBeingDebugged() { + const isBun = isRunningWithBun(); + + // Check for inspect flags in process arguments (including all variants) + const hasInspectArg = process.execArgv.some(arg => { + if (isBun) { + // Note: Bun has an issue with single-file executables where application arguments + // from process.argv leak into process.execArgv (similar to https://github.com/oven-sh/bun/issues/11673) + // This breaks use of --debug mode if we omit this branch + // We're fine to skip that check, because Bun doesn't support Node.js legacy --debug or --debug-brk flags + return /--inspect(-brk)?/.test(arg); + } else { + // In Node.js, check for both --inspect and legacy --debug flags + return /--inspect(-brk)?|--debug(-brk)?/.test(arg); + } + }); + + // Check if NODE_OPTIONS contains inspect flags + const hasInspectEnv = process.env.NODE_OPTIONS && /--inspect(-brk)?|--debug(-brk)?/.test(process.env.NODE_OPTIONS); + + // Check if inspector is available and active (indicates debugging) + try { + // Dynamic import would be better but is async - use global object instead + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const inspector = (global as any).require('inspector'); + const hasInspectorUrl = !!inspector.url(); + return hasInspectorUrl || hasInspectArg || hasInspectEnv; + } catch { + // Ignore error and fall back to argument detection + return hasInspectArg || hasInspectEnv; + } +} + +// Exit if we detect node debugging or inspection +if ("external" !== 'ant' && isBeingDebugged()) { + // Use process.exit directly here since we're in the top-level code before imports + // and gracefulShutdown is not yet available + // eslint-disable-next-line custom-rules/no-top-level-side-effects + process.exit(1); +} + +/** + * Per-session skill/plugin telemetry. Called from both the interactive path + * and the headless -p path (before runHeadless) — both go through + * main.tsx but branch before the interactive startup path, so it needs two + * call sites here rather than one here + one in QueryEngine. + */ +function logSessionTelemetry(): void { + const model = parseUserSpecifiedModel(getInitialMainLoopModel() ?? getDefaultMainLoopModel()); + void logSkillsLoaded(getCwd(), getContextWindowForModel(model, getSdkBetas())); + void loadAllPluginsCacheOnly().then(({ + enabled, + errors + }) => { + const managedNames = getManagedPluginNames(); + logPluginsEnabledForSession(enabled, managedNames, getPluginSeedDirs()); + logPluginLoadErrors(errors, managedNames); + }).catch(err => logError(err)); +} +function getCertEnvVarTelemetry(): Record { + const result: Record = {}; + if (process.env.NODE_EXTRA_CA_CERTS) { + result.has_node_extra_ca_certs = true; + } + if (process.env.CLAUDE_CODE_CLIENT_CERT) { + result.has_client_cert = true; + } + if (hasNodeOption('--use-system-ca')) { + result.has_use_system_ca = true; + } + if (hasNodeOption('--use-openssl-ca')) { + result.has_use_openssl_ca = true; + } + return result; +} +async function logStartupTelemetry(): Promise { + if (isAnalyticsDisabled()) return; + const [isGit, worktreeCount, ghAuthStatus] = await Promise.all([getIsGit(), getWorktreeCount(), getGhAuthStatus()]); + logEvent('tengu_startup_telemetry', { + is_git: isGit, + worktree_count: worktreeCount, + gh_auth_status: ghAuthStatus as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + sandbox_enabled: SandboxManager.isSandboxingEnabled(), + are_unsandboxed_commands_allowed: SandboxManager.areUnsandboxedCommandsAllowed(), + is_auto_bash_allowed_if_sandbox_enabled: SandboxManager.isAutoAllowBashIfSandboxedEnabled(), + auto_updater_disabled: isAutoUpdaterDisabled(), + prefers_reduced_motion: getInitialSettings().prefersReducedMotion ?? false, + ...getCertEnvVarTelemetry() + }); +} + +// @[MODEL LAUNCH]: Consider any migrations you may need for model strings. See migrateSonnet1mToSonnet45.ts for an example. +// Bump this when adding a new sync migration so existing users re-run the set. +const CURRENT_MIGRATION_VERSION = 11; +function runMigrations(): void { + if (getGlobalConfig().migrationVersion !== CURRENT_MIGRATION_VERSION) { + migrateAutoUpdatesToSettings(); + migrateBypassPermissionsAcceptedToSettings(); + migrateEnableAllProjectMcpServersToSettings(); + resetProToOpusDefault(); + migrateSonnet1mToSonnet45(); + migrateLegacyOpusToCurrent(); + migrateSonnet45ToSonnet46(); + migrateOpusToOpus1m(); + migrateReplBridgeEnabledToRemoteControlAtStartup(); + if (feature('TRANSCRIPT_CLASSIFIER')) { + resetAutoModeOptInForDefaultOffer(); + } + if ("external" === 'ant') { + migrateFennecToOpus(); + } + saveGlobalConfig(prev => prev.migrationVersion === CURRENT_MIGRATION_VERSION ? prev : { + ...prev, + migrationVersion: CURRENT_MIGRATION_VERSION + }); + } + // Async migration - fire and forget since it's non-blocking + migrateChangelogFromConfig().catch(() => { + // Silently ignore migration errors - will retry on next startup + }); +} + +/** + * Prefetch system context (including git status) only when it's safe to do so. + * Git commands can execute arbitrary code via hooks and config (e.g., core.fsmonitor, + * diff.external), so we must only run them after trust is established or in + * non-interactive mode where trust is implicit. + */ +function prefetchSystemContextIfSafe(): void { + const isNonInteractiveSession = getIsNonInteractiveSession(); + + // In non-interactive mode (--print), trust dialog is skipped and + // execution is considered trusted (as documented in help text) + if (isNonInteractiveSession) { + logForDiagnosticsNoPII('info', 'prefetch_system_context_non_interactive'); + void getSystemContext(); + return; + } + + // In interactive mode, only prefetch if trust has already been established + const hasTrust = checkHasTrustDialogAccepted(); + if (hasTrust) { + logForDiagnosticsNoPII('info', 'prefetch_system_context_has_trust'); + void getSystemContext(); + } else { + logForDiagnosticsNoPII('info', 'prefetch_system_context_skipped_no_trust'); + } + // Otherwise, don't prefetch - wait for trust to be established first +} + +/** + * Start background prefetches and housekeeping that are NOT needed before first render. + * These are deferred from setup() to reduce event loop contention and child process + * spawning during the critical startup path. + * Call this after the REPL has been rendered. + */ +export function startDeferredPrefetches(): void { + // This function runs after first render, so it doesn't block the initial paint. + // However, the spawned processes and async work still contend for CPU and event + // loop time, which skews startup benchmarks (CPU profiles, time-to-first-render + // measurements). Skip all of it when we're only measuring startup performance. + if (isEnvTruthy(process.env.CLAUDE_CODE_EXIT_AFTER_FIRST_RENDER) || + // --bare: skip ALL prefetches. These are cache-warms for the REPL's + // first-turn responsiveness (initUser, getUserContext, tips, countFiles, + // modelCapabilities, change detectors). Scripted -p calls don't have a + // "user is typing" window to hide this work in — it's pure overhead on + // the critical path. + isBareMode()) { + return; + } + + // Process-spawning prefetches (consumed at first API call, user is still typing) + void initUser(); + void getUserContext(); + prefetchSystemContextIfSafe(); + void getRelevantTips(); + if (isEnvTruthy(process.env.CLAUDE_CODE_USE_BEDROCK) && !isEnvTruthy(process.env.CLAUDE_CODE_SKIP_BEDROCK_AUTH)) { + void prefetchAwsCredentialsAndBedRockInfoIfSafe(); + } + if (isEnvTruthy(process.env.CLAUDE_CODE_USE_VERTEX) && !isEnvTruthy(process.env.CLAUDE_CODE_SKIP_VERTEX_AUTH)) { + void prefetchGcpCredentialsIfSafe(); + } + void countFilesRoundedRg(getCwd(), AbortSignal.timeout(3000), []); + + // Analytics and feature flag initialization + void initializeAnalyticsGates(); + void prefetchOfficialMcpUrls(); + void refreshModelCapabilities(); + + // File change detectors deferred from init() to unblock first render + void settingsChangeDetector.initialize(); + if (!isBareMode()) { + void skillChangeDetector.initialize(); + } + + // Event loop stall detector — logs when the main thread is blocked >500ms + if ("external" === 'ant') { + void import('./utils/eventLoopStallDetector.js').then(m => m.startEventLoopStallDetector()); + } +} +function loadSettingsFromFlag(settingsFile: string): void { + try { + const trimmedSettings = settingsFile.trim(); + const looksLikeJson = trimmedSettings.startsWith('{') && trimmedSettings.endsWith('}'); + let settingsPath: string; + if (looksLikeJson) { + // It's a JSON string - validate and create temp file + const parsedJson = safeParseJSON(trimmedSettings); + if (!parsedJson) { + process.stderr.write(chalk.red('Error: Invalid JSON provided to --settings\n')); + process.exit(1); + } + + // Create a temporary file and write the JSON to it. + // Use a content-hash-based path instead of random UUID to avoid + // busting the Anthropic API prompt cache. The settings path ends up + // in the Bash tool's sandbox denyWithinAllow list, which is part of + // the tool description sent to the API. A random UUID per subprocess + // changes the tool description on every query() call, invalidating + // the cache prefix and causing a 12x input token cost penalty. + // The content hash ensures identical settings produce the same path + // across process boundaries (each SDK query() spawns a new process). + settingsPath = generateTempFilePath('claude-settings', '.json', { + contentHash: trimmedSettings + }); + writeFileSync_DEPRECATED(settingsPath, trimmedSettings, 'utf8'); + } else { + // It's a file path - resolve and validate by attempting to read + const { + resolvedPath: resolvedSettingsPath + } = safeResolvePath(getFsImplementation(), settingsFile); + try { + readFileSync(resolvedSettingsPath, 'utf8'); + } catch (e) { + if (isENOENT(e)) { + process.stderr.write(chalk.red(`Error: Settings file not found: ${resolvedSettingsPath}\n`)); + process.exit(1); + } + throw e; + } + settingsPath = resolvedSettingsPath; + } + setFlagSettingsPath(settingsPath); + resetSettingsCache(); + } catch (error) { + if (error instanceof Error) { + logError(error); + } + process.stderr.write(chalk.red(`Error processing settings: ${errorMessage(error)}\n`)); + process.exit(1); + } +} +function loadSettingSourcesFromFlag(settingSourcesArg: string): void { + try { + const sources = parseSettingSourcesFlag(settingSourcesArg); + setAllowedSettingSources(sources); + resetSettingsCache(); + } catch (error) { + if (error instanceof Error) { + logError(error); + } + process.stderr.write(chalk.red(`Error processing --setting-sources: ${errorMessage(error)}\n`)); + process.exit(1); + } +} + +/** + * Parse and load settings flags early, before init() + * This ensures settings are filtered from the start of initialization + */ +function eagerLoadSettings(): void { + profileCheckpoint('eagerLoadSettings_start'); + // Parse --settings flag early to ensure settings are loaded before init() + const settingsFile = eagerParseCliFlag('--settings'); + if (settingsFile) { + loadSettingsFromFlag(settingsFile); + } + + // Parse --setting-sources flag early to control which sources are loaded + const settingSourcesArg = eagerParseCliFlag('--setting-sources'); + if (settingSourcesArg !== undefined) { + loadSettingSourcesFromFlag(settingSourcesArg); + } + profileCheckpoint('eagerLoadSettings_end'); +} +function initializeEntrypoint(isNonInteractive: boolean): void { + // Skip if already set (e.g., by SDK or other entrypoints) + if (process.env.CLAUDE_CODE_ENTRYPOINT) { + return; + } + const cliArgs = process.argv.slice(2); + + // Check for MCP serve command (handle flags before mcp serve, e.g., --debug mcp serve) + const mcpIndex = cliArgs.indexOf('mcp'); + if (mcpIndex !== -1 && cliArgs[mcpIndex + 1] === 'serve') { + process.env.CLAUDE_CODE_ENTRYPOINT = 'mcp'; + return; + } + if (isEnvTruthy(process.env.CLAUDE_CODE_ACTION)) { + process.env.CLAUDE_CODE_ENTRYPOINT = 'claude-code-github-action'; + return; + } + + // Note: 'local-agent' entrypoint is set by the local agent mode launcher + // via CLAUDE_CODE_ENTRYPOINT env var (handled by early return above) + + // Set based on interactive status + process.env.CLAUDE_CODE_ENTRYPOINT = isNonInteractive ? 'sdk-cli' : 'cli'; +} + +// Set by early argv processing when `claude open ` is detected (interactive mode only) +type PendingConnect = { + url: string | undefined; + authToken: string | undefined; + dangerouslySkipPermissions: boolean; +}; +const _pendingConnect: PendingConnect | undefined = feature('DIRECT_CONNECT') ? { + url: undefined, + authToken: undefined, + dangerouslySkipPermissions: false +} : undefined; + +// Set by early argv processing when `claude assistant [sessionId]` is detected +type PendingAssistantChat = { + sessionId?: string; + discover: boolean; +}; +const _pendingAssistantChat: PendingAssistantChat | undefined = feature('KAIROS') ? { + sessionId: undefined, + discover: false +} : undefined; + +// `claude ssh [dir]` — parsed from argv early (same pattern as +// DIRECT_CONNECT above) so the main command path can pick it up and hand +// the REPL an SSH-backed session instead of a local one. +type PendingSSH = { + host: string | undefined; + cwd: string | undefined; + permissionMode: string | undefined; + dangerouslySkipPermissions: boolean; + /** --local: spawn the child CLI directly, skip ssh/probe/deploy. e2e test mode. */ + local: boolean; + /** Extra CLI args to forward to the remote CLI on initial spawn (--resume, -c). */ + extraCliArgs: string[]; +}; +const _pendingSSH: PendingSSH | undefined = feature('SSH_REMOTE') ? { + host: undefined, + cwd: undefined, + permissionMode: undefined, + dangerouslySkipPermissions: false, + local: false, + extraCliArgs: [] +} : undefined; +export async function main() { + profileCheckpoint('main_function_start'); + + // SECURITY: Prevent Windows from executing commands from current directory + // This must be set before ANY command execution to prevent PATH hijacking attacks + // See: https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-searchpathw + process.env.NoDefaultCurrentDirectoryInExePath = '1'; + + // Initialize warning handler early to catch warnings + initializeWarningHandler(); + process.on('exit', () => { + resetCursor(); + }); + process.on('SIGINT', () => { + // In print mode, print.ts registers its own SIGINT handler that aborts + // the in-flight query and calls gracefulShutdown; skip here to avoid + // preempting it with a synchronous process.exit(). + if (process.argv.includes('-p') || process.argv.includes('--print')) { + return; + } + process.exit(0); + }); + profileCheckpoint('main_warning_handler_initialized'); + + // Check for cc:// or cc+unix:// URL in argv — rewrite so the main command + // handles it, giving the full interactive TUI instead of a stripped-down subcommand. + // For headless (-p), we rewrite to the internal `open` subcommand. + if (feature('DIRECT_CONNECT')) { + const rawCliArgs = process.argv.slice(2); + const ccIdx = rawCliArgs.findIndex(a => a.startsWith('cc://') || a.startsWith('cc+unix://')); + if (ccIdx !== -1 && _pendingConnect) { + const ccUrl = rawCliArgs[ccIdx]!; + const { + parseConnectUrl + } = await import('./server/parseConnectUrl.js'); + const parsed = parseConnectUrl(ccUrl); + _pendingConnect.dangerouslySkipPermissions = rawCliArgs.includes('--dangerously-skip-permissions'); + if (rawCliArgs.includes('-p') || rawCliArgs.includes('--print')) { + // Headless: rewrite to internal `open` subcommand + const stripped = rawCliArgs.filter((_, i) => i !== ccIdx); + const dspIdx = stripped.indexOf('--dangerously-skip-permissions'); + if (dspIdx !== -1) { + stripped.splice(dspIdx, 1); + } + process.argv = [process.argv[0]!, process.argv[1]!, 'open', ccUrl, ...stripped]; + } else { + // Interactive: strip cc:// URL and flags, run main command + _pendingConnect.url = parsed.serverUrl; + _pendingConnect.authToken = parsed.authToken; + const stripped = rawCliArgs.filter((_, i) => i !== ccIdx); + const dspIdx = stripped.indexOf('--dangerously-skip-permissions'); + if (dspIdx !== -1) { + stripped.splice(dspIdx, 1); + } + process.argv = [process.argv[0]!, process.argv[1]!, ...stripped]; + } + } + } + + // Handle deep link URIs early — this is invoked by the OS protocol handler + // and should bail out before full init since it only needs to parse the URI + // and open a terminal. + if (feature('LODESTONE')) { + const handleUriIdx = process.argv.indexOf('--handle-uri'); + if (handleUriIdx !== -1 && process.argv[handleUriIdx + 1]) { + const { + enableConfigs + } = await import('./utils/config.js'); + enableConfigs(); + const uri = process.argv[handleUriIdx + 1]!; + const { + handleDeepLinkUri + } = await import('./utils/deepLink/protocolHandler.js'); + const exitCode = await handleDeepLinkUri(uri); + process.exit(exitCode); + } + + // macOS URL handler: when LaunchServices launches our .app bundle, the + // URL arrives via Apple Event (not argv). LaunchServices overwrites + // __CFBundleIdentifier to the launching bundle's ID, which is a precise + // positive signal — cheaper than importing and guessing with heuristics. + if (process.platform === 'darwin' && process.env.__CFBundleIdentifier === 'com.anthropic.claude-code-url-handler') { + const { + enableConfigs + } = await import('./utils/config.js'); + enableConfigs(); + const { + handleUrlSchemeLaunch + } = await import('./utils/deepLink/protocolHandler.js'); + const urlSchemeResult = await handleUrlSchemeLaunch(); + process.exit(urlSchemeResult ?? 1); + } + } + + // `claude assistant [sessionId]` — stash and strip so the main + // command handles it, giving the full interactive TUI. Position-0 only + // (matching the ssh pattern below) — indexOf would false-positive on + // `claude -p "explain assistant"`. Root-flag-before-subcommand + // (e.g. `--debug assistant`) falls through to the stub, which + // prints usage. + if (feature('KAIROS') && _pendingAssistantChat) { + const rawArgs = process.argv.slice(2); + if (rawArgs[0] === 'assistant') { + const nextArg = rawArgs[1]; + if (nextArg && !nextArg.startsWith('-')) { + _pendingAssistantChat.sessionId = nextArg; + rawArgs.splice(0, 2); // drop 'assistant' and sessionId + process.argv = [process.argv[0]!, process.argv[1]!, ...rawArgs]; + } else if (!nextArg) { + _pendingAssistantChat.discover = true; + rawArgs.splice(0, 1); // drop 'assistant' + process.argv = [process.argv[0]!, process.argv[1]!, ...rawArgs]; + } + // else: `claude assistant --help` → fall through to stub + } + } + + // `claude ssh [dir]` — strip from argv so the main command handler + // runs (full interactive TUI), stash the host/dir for the REPL branch at + // ~line 3720 to pick up. Headless (-p) mode not supported in v1: SSH + // sessions need the local REPL to drive them (interrupt, permissions). + if (feature('SSH_REMOTE') && _pendingSSH) { + const rawCliArgs = process.argv.slice(2); + // SSH-specific flags can appear before the host positional (e.g. + // `ssh --permission-mode auto host /tmp` — standard POSIX flags-before- + // positionals). Pull them all out BEFORE checking whether a host was + // given, so `claude ssh --permission-mode auto host` and `claude ssh host + // --permission-mode auto` are equivalent. The host check below only needs + // to guard against `-h`/`--help` (which commander should handle). + if (rawCliArgs[0] === 'ssh') { + const localIdx = rawCliArgs.indexOf('--local'); + if (localIdx !== -1) { + _pendingSSH.local = true; + rawCliArgs.splice(localIdx, 1); + } + const dspIdx = rawCliArgs.indexOf('--dangerously-skip-permissions'); + if (dspIdx !== -1) { + _pendingSSH.dangerouslySkipPermissions = true; + rawCliArgs.splice(dspIdx, 1); + } + const pmIdx = rawCliArgs.indexOf('--permission-mode'); + if (pmIdx !== -1 && rawCliArgs[pmIdx + 1] && !rawCliArgs[pmIdx + 1]!.startsWith('-')) { + _pendingSSH.permissionMode = rawCliArgs[pmIdx + 1]; + rawCliArgs.splice(pmIdx, 2); + } + const pmEqIdx = rawCliArgs.findIndex(a => a.startsWith('--permission-mode=')); + if (pmEqIdx !== -1) { + _pendingSSH.permissionMode = rawCliArgs[pmEqIdx]!.split('=')[1]; + rawCliArgs.splice(pmEqIdx, 1); + } + // Forward session-resume + model flags to the remote CLI's initial spawn. + // --continue/-c and --resume operate on the REMOTE session history + // (which persists under the remote's ~/.claude/projects//). + // --model controls which model the remote uses. + const extractFlag = (flag: string, opts: { + hasValue?: boolean; + as?: string; + } = {}) => { + const i = rawCliArgs.indexOf(flag); + if (i !== -1) { + _pendingSSH.extraCliArgs.push(opts.as ?? flag); + const val = rawCliArgs[i + 1]; + if (opts.hasValue && val && !val.startsWith('-')) { + _pendingSSH.extraCliArgs.push(val); + rawCliArgs.splice(i, 2); + } else { + rawCliArgs.splice(i, 1); + } + } + const eqI = rawCliArgs.findIndex(a => a.startsWith(`${flag}=`)); + if (eqI !== -1) { + _pendingSSH.extraCliArgs.push(opts.as ?? flag, rawCliArgs[eqI]!.slice(flag.length + 1)); + rawCliArgs.splice(eqI, 1); + } + }; + extractFlag('-c', { + as: '--continue' + }); + extractFlag('--continue'); + extractFlag('--resume', { + hasValue: true + }); + extractFlag('--model', { + hasValue: true + }); + } + // After pre-extraction, any remaining dash-arg at [1] is either -h/--help + // (commander handles) or an unknown-to-ssh flag (fall through to commander + // so it surfaces a proper error). Only a non-dash arg is the host. + if (rawCliArgs[0] === 'ssh' && rawCliArgs[1] && !rawCliArgs[1].startsWith('-')) { + _pendingSSH.host = rawCliArgs[1]; + // Optional positional cwd. + let consumed = 2; + if (rawCliArgs[2] && !rawCliArgs[2].startsWith('-')) { + _pendingSSH.cwd = rawCliArgs[2]; + consumed = 3; + } + const rest = rawCliArgs.slice(consumed); + + // Headless (-p) mode is not supported with SSH in v1 — reject early + // so the flag doesn't silently cause local execution. + if (rest.includes('-p') || rest.includes('--print')) { + process.stderr.write('Error: headless (-p/--print) mode is not supported with claude ssh\n'); + gracefulShutdownSync(1); + return; + } + + // Rewrite argv so the main command sees remaining flags but not `ssh`. + process.argv = [process.argv[0]!, process.argv[1]!, ...rest]; + } + } + + // Check for -p/--print and --init-only flags early to set isInteractiveSession before init() + // This is needed because telemetry initialization calls auth functions that need this flag + const cliArgs = process.argv.slice(2); + const hasPrintFlag = cliArgs.includes('-p') || cliArgs.includes('--print'); + const hasInitOnlyFlag = cliArgs.includes('--init-only'); + const hasSdkUrl = cliArgs.some(arg => arg.startsWith('--sdk-url')); + const isNonInteractive = hasPrintFlag || hasInitOnlyFlag || hasSdkUrl || !process.stdout.isTTY; + + // Stop capturing early input for non-interactive modes + if (isNonInteractive) { + stopCapturingEarlyInput(); + } + + // Set simplified tracking fields + const isInteractive = !isNonInteractive; + setIsInteractive(isInteractive); + + // Initialize entrypoint based on mode - needs to be set before any event is logged + initializeEntrypoint(isNonInteractive); + + // Determine client type + const clientType = (() => { + if (isEnvTruthy(process.env.GITHUB_ACTIONS)) return 'github-action'; + if (process.env.CLAUDE_CODE_ENTRYPOINT === 'sdk-ts') return 'sdk-typescript'; + if (process.env.CLAUDE_CODE_ENTRYPOINT === 'sdk-py') return 'sdk-python'; + if (process.env.CLAUDE_CODE_ENTRYPOINT === 'sdk-cli') return 'sdk-cli'; + if (process.env.CLAUDE_CODE_ENTRYPOINT === 'claude-vscode') return 'claude-vscode'; + if (process.env.CLAUDE_CODE_ENTRYPOINT === 'local-agent') return 'local-agent'; + if (process.env.CLAUDE_CODE_ENTRYPOINT === 'claude-desktop') return 'claude-desktop'; + + // Check if session-ingress token is provided (indicates remote session) + const hasSessionIngressToken = process.env.CLAUDE_CODE_SESSION_ACCESS_TOKEN || process.env.CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR; + if (process.env.CLAUDE_CODE_ENTRYPOINT === 'remote' || hasSessionIngressToken) { + return 'remote'; + } + return 'cli'; + })(); + setClientType(clientType); + const previewFormat = process.env.CLAUDE_CODE_QUESTION_PREVIEW_FORMAT; + if (previewFormat === 'markdown' || previewFormat === 'html') { + setQuestionPreviewFormat(previewFormat); + } else if (!clientType.startsWith('sdk-') && + // Desktop and CCR pass previewFormat via toolConfig; when the feature is + // gated off they pass undefined — don't override that with markdown. + clientType !== 'claude-desktop' && clientType !== 'local-agent' && clientType !== 'remote') { + setQuestionPreviewFormat('markdown'); + } + + // Tag sessions created via `claude remote-control` so the backend can identify them + if (process.env.CLAUDE_CODE_ENVIRONMENT_KIND === 'bridge') { + setSessionSource('remote-control'); + } + profileCheckpoint('main_client_type_determined'); + + // Parse and load settings flags early, before init() + eagerLoadSettings(); + profileCheckpoint('main_before_run'); + await run(); + profileCheckpoint('main_after_run'); +} +async function getInputPrompt(prompt: string, inputFormat: 'text' | 'stream-json'): Promise> { + if (!process.stdin.isTTY && + // Input hijacking breaks MCP. + !process.argv.includes('mcp')) { + if (inputFormat === 'stream-json') { + return process.stdin; + } + process.stdin.setEncoding('utf8'); + let data = ''; + const onData = (chunk: string) => { + data += chunk; + }; + process.stdin.on('data', onData); + // If no data arrives in 3s, stop waiting and warn. Stdin is likely an + // inherited pipe from a parent that isn't writing (subprocess spawned + // without explicit stdin handling). 3s covers slow producers like curl, + // jq on large files, python with import overhead. The warning makes + // silent data loss visible for the rare producer that's slower still. + const timedOut = await peekForStdinData(process.stdin, 3000); + process.stdin.off('data', onData); + if (timedOut) { + process.stderr.write('Warning: no stdin data received in 3s, proceeding without it. ' + 'If piping from a slow command, redirect stdin explicitly: < /dev/null to skip, or wait longer.\n'); + } + return [prompt, data].filter(Boolean).join('\n'); + } + return prompt; +} +async function run(): Promise { + profileCheckpoint('run_function_start'); + + // Create help config that sorts options by long option name. + // Commander supports compareOptions at runtime but @commander-js/extra-typings + // doesn't include it in the type definitions, so we use Object.assign to add it. + function createSortedHelpConfig(): { + sortSubcommands: true; + sortOptions: true; + } { + const getOptionSortKey = (opt: Option): string => opt.long?.replace(/^--/, '') ?? opt.short?.replace(/^-/, '') ?? ''; + return Object.assign({ + sortSubcommands: true, + sortOptions: true + } as const, { + compareOptions: (a: Option, b: Option) => getOptionSortKey(a).localeCompare(getOptionSortKey(b)) + }); + } + const program = new CommanderCommand().configureHelp(createSortedHelpConfig()).enablePositionalOptions(); + profileCheckpoint('run_commander_initialized'); + + // Use preAction hook to run initialization only when executing a command, + // not when displaying help. This avoids the need for env variable signaling. + program.hook('preAction', async thisCommand => { + profileCheckpoint('preAction_start'); + // Await async subprocess loads started at module evaluation (lines 12-20). + // Nearly free — subprocesses complete during the ~135ms of imports above. + // Must resolve before init() which triggers the first settings read + // (applySafeConfigEnvironmentVariables → getSettingsForSource('policySettings') + // → isRemoteManagedSettingsEligible → sync keychain reads otherwise ~65ms). + await Promise.all([ensureMdmSettingsLoaded(), ensureKeychainPrefetchCompleted()]); + profileCheckpoint('preAction_after_mdm'); + await init(); + profileCheckpoint('preAction_after_init'); + + // process.title on Windows sets the console title directly; on POSIX, + // terminal shell integration may mirror the process name to the tab. + // After init() so settings.json env can also gate this (gh-4765). + if (!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_TERMINAL_TITLE)) { + process.title = 'claude'; + } + + // Attach logging sinks so subcommand handlers can use logEvent/logError. + // Before PR #11106 logEvent dispatched directly; after, events queue until + // a sink attaches. setup() attaches sinks for the default command, but + // subcommands (doctor, mcp, plugin, auth) never call setup() and would + // silently drop events on process.exit(). Both inits are idempotent. + const { + initSinks + } = await import('./utils/sinks.js'); + initSinks(); + profileCheckpoint('preAction_after_sinks'); + + // gh-33508: --plugin-dir is a top-level program option. The default + // action reads it from its own options destructure, but subcommands + // (plugin list, plugin install, mcp *) have their own actions and + // never see it. Wire it up here so getInlinePlugins() works everywhere. + // thisCommand.opts() is typed {} here because this hook is attached + // before .option('--plugin-dir', ...) in the chain — extra-typings + // builds the type as options are added. Narrow with a runtime guard; + // the collect accumulator + [] default guarantee string[] in practice. + const pluginDir = thisCommand.getOptionValue('pluginDir'); + if (Array.isArray(pluginDir) && pluginDir.length > 0 && pluginDir.every(p => typeof p === 'string')) { + setInlinePlugins(pluginDir); + clearPluginCache('preAction: --plugin-dir inline plugins'); + } + runMigrations(); + profileCheckpoint('preAction_after_migrations'); + + // Load remote managed settings for enterprise customers (non-blocking) + // Fails open - if fetch fails, continues without remote settings + // Settings are applied via hot-reload when they arrive + // Must happen after init() to ensure config reading is allowed + void loadRemoteManagedSettings(); + void loadPolicyLimits(); + profileCheckpoint('preAction_after_remote_settings'); + + // Load settings sync (non-blocking, fail-open) + // CLI: uploads local settings to remote (CCR download is handled by print.ts) + if (feature('UPLOAD_USER_SETTINGS')) { + void import('./services/settingsSync/index.js').then(m => m.uploadUserSettingsInBackground()); + } + profileCheckpoint('preAction_after_settings_sync'); + }); + program.name('claude').description(`Claude Code - starts an interactive session by default, use -p/--print for non-interactive output`).argument('[prompt]', 'Your prompt', String) + // Subcommands inherit helpOption via commander's copyInheritedSettings — + // setting it once here covers mcp, plugin, auth, and all other subcommands. + .helpOption('-h, --help', 'Display help for command').option('-d, --debug [filter]', 'Enable debug mode with optional category filtering (e.g., "api,hooks" or "!1p,!file")', (_value: string | true) => { + // If value is provided, it will be the filter string + // If not provided but flag is present, value will be true + // The actual filtering is handled in debug.ts by parsing process.argv + return true; + }).addOption(new Option('-d2e, --debug-to-stderr', 'Enable debug mode (to stderr)').argParser(Boolean).hideHelp()).option('--debug-file ', 'Write debug logs to a specific file path (implicitly enables debug mode)', () => true).option('--verbose', 'Override verbose mode setting from config', () => true).option('-p, --print', 'Print response and exit (useful for pipes). Note: The workspace trust dialog is skipped when Claude is run with the -p mode. Only use this flag in directories you trust.', () => true).option('--bare', 'Minimal mode: skip hooks, LSP, plugin sync, attribution, auto-memory, background prefetches, keychain reads, and CLAUDE.md auto-discovery. Sets CLAUDE_CODE_SIMPLE=1. Anthropic auth is strictly ANTHROPIC_API_KEY or apiKeyHelper via --settings (OAuth and keychain are never read). 3P providers (Bedrock/Vertex/Foundry) use their own credentials. Skills still resolve via /skill-name. Explicitly provide context via: --system-prompt[-file], --append-system-prompt[-file], --add-dir (CLAUDE.md dirs), --mcp-config, --settings, --agents, --plugin-dir.', () => true).addOption(new Option('--init', 'Run Setup hooks with init trigger, then continue').hideHelp()).addOption(new Option('--init-only', 'Run Setup and SessionStart:startup hooks, then exit').hideHelp()).addOption(new Option('--maintenance', 'Run Setup hooks with maintenance trigger, then continue').hideHelp()).addOption(new Option('--output-format ', 'Output format (only works with --print): "text" (default), "json" (single result), or "stream-json" (realtime streaming)').choices(['text', 'json', 'stream-json'])).addOption(new Option('--json-schema ', 'JSON Schema for structured output validation. ' + 'Example: {"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}').argParser(String)).option('--include-hook-events', 'Include all hook lifecycle events in the output stream (only works with --output-format=stream-json)', () => true).option('--include-partial-messages', 'Include partial message chunks as they arrive (only works with --print and --output-format=stream-json)', () => true).addOption(new Option('--input-format ', 'Input format (only works with --print): "text" (default), or "stream-json" (realtime streaming input)').choices(['text', 'stream-json'])).option('--mcp-debug', '[DEPRECATED. Use --debug instead] Enable MCP debug mode (shows MCP server errors)', () => true).option('--dangerously-skip-permissions', 'Bypass all permission checks. Recommended only for sandboxes with no internet access.', () => true).option('--allow-dangerously-skip-permissions', 'Enable bypassing all permission checks as an option, without it being enabled by default. Recommended only for sandboxes with no internet access.', () => true).addOption(new Option('--thinking ', 'Thinking mode: enabled (equivalent to adaptive), disabled').choices(['enabled', 'adaptive', 'disabled']).hideHelp()).addOption(new Option('--max-thinking-tokens ', '[DEPRECATED. Use --thinking instead for newer models] Maximum number of thinking tokens (only works with --print)').argParser(Number).hideHelp()).addOption(new Option('--max-turns ', 'Maximum number of agentic turns in non-interactive mode. This will early exit the conversation after the specified number of turns. (only works with --print)').argParser(Number).hideHelp()).addOption(new Option('--max-budget-usd ', 'Maximum dollar amount to spend on API calls (only works with --print)').argParser(value => { + const amount = Number(value); + if (isNaN(amount) || amount <= 0) { + throw new Error('--max-budget-usd must be a positive number greater than 0'); + } + return amount; + })).addOption(new Option('--task-budget ', 'API-side task budget in tokens (output_config.task_budget)').argParser(value => { + const tokens = Number(value); + if (isNaN(tokens) || tokens <= 0 || !Number.isInteger(tokens)) { + throw new Error('--task-budget must be a positive integer'); + } + return tokens; + }).hideHelp()).option('--replay-user-messages', 'Re-emit user messages from stdin back on stdout for acknowledgment (only works with --input-format=stream-json and --output-format=stream-json)', () => true).addOption(new Option('--enable-auth-status', 'Enable auth status messages in SDK mode').default(false).hideHelp()).option('--allowedTools, --allowed-tools ', 'Comma or space-separated list of tool names to allow (e.g. "Bash(git:*) Edit")').option('--tools ', 'Specify the list of available tools from the built-in set. Use "" to disable all tools, "default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read").').option('--disallowedTools, --disallowed-tools ', 'Comma or space-separated list of tool names to deny (e.g. "Bash(git:*) Edit")').option('--mcp-config ', 'Load MCP servers from JSON files or strings (space-separated)').addOption(new Option('--permission-prompt-tool ', 'MCP tool to use for permission prompts (only works with --print)').argParser(String).hideHelp()).addOption(new Option('--system-prompt ', 'System prompt to use for the session').argParser(String)).addOption(new Option('--system-prompt-file ', 'Read system prompt from a file').argParser(String).hideHelp()).addOption(new Option('--append-system-prompt ', 'Append a system prompt to the default system prompt').argParser(String)).addOption(new Option('--append-system-prompt-file ', 'Read system prompt from a file and append to the default system prompt').argParser(String).hideHelp()).addOption(new Option('--permission-mode ', 'Permission mode to use for the session').argParser(String).choices(PERMISSION_MODES)).option('-c, --continue', 'Continue the most recent conversation in the current directory', () => true).option('-r, --resume [value]', 'Resume a conversation by session ID, or open interactive picker with optional search term', value => value || true).option('--fork-session', 'When resuming, create a new session ID instead of reusing the original (use with --resume or --continue)', () => true).addOption(new Option('--prefill ', 'Pre-fill the prompt input with text without submitting it').hideHelp()).addOption(new Option('--deep-link-origin', 'Signal that this session was launched from a deep link').hideHelp()).addOption(new Option('--deep-link-repo ', 'Repo slug the deep link ?repo= parameter resolved to the current cwd').hideHelp()).addOption(new Option('--deep-link-last-fetch ', 'FETCH_HEAD mtime in epoch ms, precomputed by the deep link trampoline').argParser(v => { + const n = Number(v); + return Number.isFinite(n) ? n : undefined; + }).hideHelp()).option('--from-pr [value]', 'Resume a session linked to a PR by PR number/URL, or open interactive picker with optional search term', value => value || true).option('--no-session-persistence', 'Disable session persistence - sessions will not be saved to disk and cannot be resumed (only works with --print)').addOption(new Option('--resume-session-at ', 'When resuming, only messages up to and including the assistant message with (use with --resume in print mode)').argParser(String).hideHelp()).addOption(new Option('--rewind-files ', 'Restore files to state at the specified user message and exit (requires --resume)').hideHelp()) + // @[MODEL LAUNCH]: Update the example model ID in the --model help text. + .option('--model ', `Model for the current session. Provide an alias for the latest model (e.g. 'sonnet' or 'opus') or a model's full name (e.g. 'claude-sonnet-4-6').`).addOption(new Option('--effort ', `Effort level for the current session (low, medium, high, max)`).argParser((rawValue: string) => { + const value = rawValue.toLowerCase(); + const allowed = ['low', 'medium', 'high', 'max']; + if (!allowed.includes(value)) { + throw new InvalidArgumentError(`It must be one of: ${allowed.join(', ')}`); + } + return value; + })).option('--agent ', `Agent for the current session. Overrides the 'agent' setting.`).option('--betas ', 'Beta headers to include in API requests (API key users only)').option('--fallback-model ', 'Enable automatic fallback to specified model when default model is overloaded (only works with --print)').addOption(new Option('--workload ', 'Workload tag for billing-header attribution (cc_workload). Process-scoped; set by SDK daemon callers that spawn subprocesses for cron work. (only works with --print)').hideHelp()).option('--settings ', 'Path to a settings JSON file or a JSON string to load additional settings from').option('--add-dir ', 'Additional directories to allow tool access to').option('--ide', 'Automatically connect to IDE on startup if exactly one valid IDE is available', () => true).option('--strict-mcp-config', 'Only use MCP servers from --mcp-config, ignoring all other MCP configurations', () => true).option('--session-id ', 'Use a specific session ID for the conversation (must be a valid UUID)').option('-n, --name ', 'Set a display name for this session (shown in /resume and terminal title)').option('--agents ', 'JSON object defining custom agents (e.g. \'{"reviewer": {"description": "Reviews code", "prompt": "You are a code reviewer"}}\')').option('--setting-sources ', 'Comma-separated list of setting sources to load (user, project, local).') + // gh-33508: (variadic) consumed everything until the next + // --flag. `claude --plugin-dir /path mcp add --transport http` swallowed + // `mcp` and `add` as paths, then choked on --transport as an unknown + // top-level option. Single-value + collect accumulator means each + // --plugin-dir takes exactly one arg; repeat the flag for multiple dirs. + .option('--plugin-dir ', 'Load plugins from a directory for this session only (repeatable: --plugin-dir A --plugin-dir B)', (val: string, prev: string[]) => [...prev, val], [] as string[]).option('--disable-slash-commands', 'Disable all skills', () => true).option('--chrome', 'Enable Claude in Chrome integration').option('--no-chrome', 'Disable Claude in Chrome integration').option('--file ', 'File resources to download at startup. Format: file_id:relative_path (e.g., --file file_abc:doc.txt file_def:img.png)').action(async (prompt, options) => { + profileCheckpoint('action_handler_start'); + + // --bare = one-switch minimal mode. Sets SIMPLE so all the existing + // gates fire (CLAUDE.md, skills, hooks inside executeHooks, agent + // dir-walk). Must be set before setup() / any of the gated work runs. + if ((options as { + bare?: boolean; + }).bare) { + process.env.CLAUDE_CODE_SIMPLE = '1'; + } + + // Ignore "code" as a prompt - treat it the same as no prompt + if (prompt === 'code') { + logEvent('tengu_code_prompt_ignored', {}); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.warn(chalk.yellow('Tip: You can launch Claude Code with just `claude`')); + prompt = undefined; + } + + // Log event for any single-word prompt + if (prompt && typeof prompt === 'string' && !/\s/.test(prompt) && prompt.length > 0) { + logEvent('tengu_single_word_prompt', { + length: prompt.length + }); + } + + // Assistant mode: when .claude/settings.json has assistant: true AND + // the tengu_kairos GrowthBook gate is on, force brief on. Permission + // mode is left to the user — settings defaultMode or --permission-mode + // apply as normal. REPL-typed messages already default to 'next' + // priority (messageQueueManager.enqueue) so they drain mid-turn between + // tool calls. SendUserMessage (BriefTool) is enabled via the brief env + // var. SleepTool stays disabled (its isEnabled() gates on proactive). + // kairosEnabled is computed once here and reused at the + // getAssistantSystemPromptAddendum() call site further down. + // + // Trust gate: .claude/settings.json is attacker-controllable in an + // untrusted clone. We run ~1000 lines before showSetupScreens() shows + // the trust dialog, and by then we've already appended + // .claude/agents/assistant.md to the system prompt. Refuse to activate + // until the directory has been explicitly trusted. + let kairosEnabled = false; + let assistantTeamContext: Awaited['initializeAssistantTeam']>> | undefined; + if (feature('KAIROS') && (options as { + assistant?: boolean; + }).assistant && assistantModule) { + // --assistant (Agent SDK daemon mode): force the latch before + // isAssistantMode() runs below. The daemon has already checked + // entitlement — don't make the child re-check tengu_kairos. + assistantModule.markAssistantForced(); + } + if (feature('KAIROS') && assistantModule?.isAssistantMode() && + // Spawned teammates share the leader's cwd + settings.json, so + // isAssistantMode() is true for them too. --agent-id being set + // means we ARE a spawned teammate (extractTeammateOptions runs + // ~170 lines later so check the raw commander option) — don't + // re-init the team or override teammateMode/proactive/brief. + !(options as { + agentId?: unknown; + }).agentId && kairosGate) { + if (!checkHasTrustDialogAccepted()) { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.warn(chalk.yellow('Assistant mode disabled: directory is not trusted. Accept the trust dialog and restart.')); + } else { + // Blocking gate check — returns cached `true` instantly; if disk + // cache is false/missing, lazily inits GrowthBook and fetches fresh + // (max ~5s). --assistant skips the gate entirely (daemon is + // pre-entitled). + kairosEnabled = assistantModule.isAssistantForced() || (await kairosGate.isKairosEnabled()); + if (kairosEnabled) { + const opts = options as { + brief?: boolean; + }; + opts.brief = true; + setKairosActive(true); + // Pre-seed an in-process team so Agent(name: "foo") spawns + // teammates without TeamCreate. Must run BEFORE setup() captures + // the teammateMode snapshot (initializeAssistantTeam calls + // setCliTeammateModeOverride internally). + assistantTeamContext = await assistantModule.initializeAssistantTeam(); + } + } + } + const { + debug = false, + debugToStderr = false, + dangerouslySkipPermissions, + allowDangerouslySkipPermissions = false, + tools: baseTools = [], + allowedTools = [], + disallowedTools = [], + mcpConfig = [], + permissionMode: permissionModeCli, + addDir = [], + fallbackModel, + betas = [], + ide = false, + sessionId, + includeHookEvents, + includePartialMessages + } = options; + if (options.prefill) { + seedEarlyInput(options.prefill); + } + + // Promise for file downloads - started early, awaited before REPL renders + let fileDownloadPromise: Promise | undefined; + const agentsJson = options.agents; + const agentCli = options.agent; + if (feature('BG_SESSIONS') && agentCli) { + process.env.CLAUDE_CODE_AGENT = agentCli; + } + + // NOTE: LSP manager initialization is intentionally deferred until after + // the trust dialog is accepted. This prevents plugin LSP servers from + // executing code in untrusted directories before user consent. + + // Extract these separately so they can be modified if needed + let outputFormat = options.outputFormat; + let inputFormat = options.inputFormat; + let verbose = options.verbose ?? getGlobalConfig().verbose; + let print = options.print; + const init = options.init ?? false; + const initOnly = options.initOnly ?? false; + const maintenance = options.maintenance ?? false; + + // Extract disable slash commands flag + const disableSlashCommands = options.disableSlashCommands || false; + + // Extract tasks mode options (ant-only) + const tasksOption = "external" === 'ant' && (options as { + tasks?: boolean | string; + }).tasks; + const taskListId = tasksOption ? typeof tasksOption === 'string' ? tasksOption : DEFAULT_TASKS_MODE_TASK_LIST_ID : undefined; + if ("external" === 'ant' && taskListId) { + process.env.CLAUDE_CODE_TASK_LIST_ID = taskListId; + } + + // Extract worktree option + // worktree can be true (flag without value) or a string (custom name or PR reference) + const worktreeOption = isWorktreeModeEnabled() ? (options as { + worktree?: boolean | string; + }).worktree : undefined; + let worktreeName = typeof worktreeOption === 'string' ? worktreeOption : undefined; + const worktreeEnabled = worktreeOption !== undefined; + + // Check if worktree name is a PR reference (#N or GitHub PR URL) + let worktreePRNumber: number | undefined; + if (worktreeName) { + const prNum = parsePRReference(worktreeName); + if (prNum !== null) { + worktreePRNumber = prNum; + worktreeName = undefined; // slug will be generated in setup() + } + } + + // Extract tmux option (requires --worktree) + const tmuxEnabled = isWorktreeModeEnabled() && (options as { + tmux?: boolean; + }).tmux === true; + + // Validate tmux option + if (tmuxEnabled) { + if (!worktreeEnabled) { + process.stderr.write(chalk.red('Error: --tmux requires --worktree\n')); + process.exit(1); + } + if (getPlatform() === 'windows') { + process.stderr.write(chalk.red('Error: --tmux is not supported on Windows\n')); + process.exit(1); + } + if (!(await isTmuxAvailable())) { + process.stderr.write(chalk.red(`Error: tmux is not installed.\n${getTmuxInstallInstructions()}\n`)); + process.exit(1); + } + } + + // Extract teammate options (for tmux-spawned agents) + // Declared outside the if block so it's accessible later for system prompt addendum + let storedTeammateOpts: TeammateOptions | undefined; + if (isAgentSwarmsEnabled()) { + // Extract agent identity options (for tmux-spawned agents) + // These replace the CLAUDE_CODE_* environment variables + const teammateOpts = extractTeammateOptions(options); + storedTeammateOpts = teammateOpts; + + // If any teammate identity option is provided, all three required ones must be present + const hasAnyTeammateOpt = teammateOpts.agentId || teammateOpts.agentName || teammateOpts.teamName; + const hasAllRequiredTeammateOpts = teammateOpts.agentId && teammateOpts.agentName && teammateOpts.teamName; + if (hasAnyTeammateOpt && !hasAllRequiredTeammateOpts) { + process.stderr.write(chalk.red('Error: --agent-id, --agent-name, and --team-name must all be provided together\n')); + process.exit(1); + } + + // If teammate identity is provided via CLI, set up dynamicTeamContext + if (teammateOpts.agentId && teammateOpts.agentName && teammateOpts.teamName) { + getTeammateUtils().setDynamicTeamContext?.({ + agentId: teammateOpts.agentId, + agentName: teammateOpts.agentName, + teamName: teammateOpts.teamName, + color: teammateOpts.agentColor, + planModeRequired: teammateOpts.planModeRequired ?? false, + parentSessionId: teammateOpts.parentSessionId + }); + } + + // Set teammate mode CLI override if provided + // This must be done before setup() captures the snapshot + if (teammateOpts.teammateMode) { + getTeammateModeSnapshot().setCliTeammateModeOverride?.(teammateOpts.teammateMode); + } + } + + // Extract remote sdk options + const sdkUrl = (options as { + sdkUrl?: string; + }).sdkUrl ?? undefined; + + // Allow env var to enable partial messages (used by sandbox gateway for baku) + const effectiveIncludePartialMessages = includePartialMessages || isEnvTruthy(process.env.CLAUDE_CODE_INCLUDE_PARTIAL_MESSAGES); + + // Enable all hook event types when explicitly requested via SDK option + // or when running in CLAUDE_CODE_REMOTE mode (CCR needs them). + // Without this, only SessionStart and Setup events are emitted. + if (includeHookEvents || isEnvTruthy(process.env.CLAUDE_CODE_REMOTE)) { + setAllHookEventsEnabled(true); + } + + // Auto-set input/output formats, verbose mode, and print mode when SDK URL is provided + if (sdkUrl) { + // If SDK URL is provided, automatically use stream-json formats unless explicitly set + if (!inputFormat) { + inputFormat = 'stream-json'; + } + if (!outputFormat) { + outputFormat = 'stream-json'; + } + // Auto-enable verbose mode unless explicitly disabled or already set + if (options.verbose === undefined) { + verbose = true; + } + // Auto-enable print mode unless explicitly disabled + if (!options.print) { + print = true; + } + } + + // Extract teleport option + const teleport = (options as { + teleport?: string | true; + }).teleport ?? null; + + // Extract remote option (can be true if no description provided, or a string) + const remoteOption = (options as { + remote?: string | true; + }).remote; + const remote = remoteOption === true ? '' : remoteOption ?? null; + + // Extract --remote-control / --rc flag (enable bridge in interactive session) + const remoteControlOption = (options as { + remoteControl?: string | true; + }).remoteControl ?? (options as { + rc?: string | true; + }).rc; + // Actual bridge check is deferred to after showSetupScreens() so that + // trust is established and GrowthBook has auth headers. + let remoteControl = false; + const remoteControlName = typeof remoteControlOption === 'string' && remoteControlOption.length > 0 ? remoteControlOption : undefined; + + // Validate session ID if provided + if (sessionId) { + // Check for conflicting flags + // --session-id can be used with --continue or --resume when --fork-session is also provided + // (to specify a custom ID for the forked session) + if ((options.continue || options.resume) && !options.forkSession) { + process.stderr.write(chalk.red('Error: --session-id can only be used with --continue or --resume if --fork-session is also specified.\n')); + process.exit(1); + } + + // When --sdk-url is provided (bridge/remote mode), the session ID is a + // server-assigned tagged ID (e.g. "session_local_01...") rather than a + // UUID. Skip UUID validation and local existence checks in that case. + if (!sdkUrl) { + const validatedSessionId = validateUuid(sessionId); + if (!validatedSessionId) { + process.stderr.write(chalk.red('Error: Invalid session ID. Must be a valid UUID.\n')); + process.exit(1); + } + + // Check if session ID already exists + if (sessionIdExists(validatedSessionId)) { + process.stderr.write(chalk.red(`Error: Session ID ${validatedSessionId} is already in use.\n`)); + process.exit(1); + } + } + } + + // Download file resources if specified via --file flag + const fileSpecs = (options as { + file?: string[]; + }).file; + if (fileSpecs && fileSpecs.length > 0) { + // Get session ingress token (provided by EnvManager via CLAUDE_CODE_SESSION_ACCESS_TOKEN) + const sessionToken = getSessionIngressAuthToken(); + if (!sessionToken) { + process.stderr.write(chalk.red('Error: Session token required for file downloads. CLAUDE_CODE_SESSION_ACCESS_TOKEN must be set.\n')); + process.exit(1); + } + + // Resolve session ID: prefer remote session ID, fall back to internal session ID + const fileSessionId = process.env.CLAUDE_CODE_REMOTE_SESSION_ID || getSessionId(); + const files = parseFileSpecs(fileSpecs); + if (files.length > 0) { + // Use ANTHROPIC_BASE_URL if set (by EnvManager), otherwise use OAuth config + // This ensures consistency with session ingress API in all environments + const config: FilesApiConfig = { + baseUrl: process.env.ANTHROPIC_BASE_URL || getOauthConfig().BASE_API_URL, + oauthToken: sessionToken, + sessionId: fileSessionId + }; + + // Start download without blocking startup - await before REPL renders + fileDownloadPromise = downloadSessionFiles(files, config); + } + } + + // Get isNonInteractiveSession from state (was set before init()) + const isNonInteractiveSession = getIsNonInteractiveSession(); + + // Validate that fallback model is different from main model + if (fallbackModel && options.model && fallbackModel === options.model) { + process.stderr.write(chalk.red('Error: Fallback model cannot be the same as the main model. Please specify a different model for --fallback-model.\n')); + process.exit(1); + } + + // Handle system prompt options + let systemPrompt = options.systemPrompt; + if (options.systemPromptFile) { + if (options.systemPrompt) { + process.stderr.write(chalk.red('Error: Cannot use both --system-prompt and --system-prompt-file. Please use only one.\n')); + process.exit(1); + } + try { + const filePath = resolve(options.systemPromptFile); + systemPrompt = readFileSync(filePath, 'utf8'); + } catch (error) { + const code = getErrnoCode(error); + if (code === 'ENOENT') { + process.stderr.write(chalk.red(`Error: System prompt file not found: ${resolve(options.systemPromptFile)}\n`)); + process.exit(1); + } + process.stderr.write(chalk.red(`Error reading system prompt file: ${errorMessage(error)}\n`)); + process.exit(1); + } + } + + // Handle append system prompt options + let appendSystemPrompt = options.appendSystemPrompt; + if (options.appendSystemPromptFile) { + if (options.appendSystemPrompt) { + process.stderr.write(chalk.red('Error: Cannot use both --append-system-prompt and --append-system-prompt-file. Please use only one.\n')); + process.exit(1); + } + try { + const filePath = resolve(options.appendSystemPromptFile); + appendSystemPrompt = readFileSync(filePath, 'utf8'); + } catch (error) { + const code = getErrnoCode(error); + if (code === 'ENOENT') { + process.stderr.write(chalk.red(`Error: Append system prompt file not found: ${resolve(options.appendSystemPromptFile)}\n`)); + process.exit(1); + } + process.stderr.write(chalk.red(`Error reading append system prompt file: ${errorMessage(error)}\n`)); + process.exit(1); + } + } + + // Add teammate-specific system prompt addendum for tmux teammates + if (isAgentSwarmsEnabled() && storedTeammateOpts?.agentId && storedTeammateOpts?.agentName && storedTeammateOpts?.teamName) { + const addendum = getTeammatePromptAddendum().TEAMMATE_SYSTEM_PROMPT_ADDENDUM; + appendSystemPrompt = appendSystemPrompt ? `${appendSystemPrompt}\n\n${addendum}` : addendum; + } + const { + mode: permissionMode, + notification: permissionModeNotification + } = initialPermissionModeFromCLI({ + permissionModeCli, + dangerouslySkipPermissions + }); + + // Store session bypass permissions mode for trust dialog check + setSessionBypassPermissionsMode(permissionMode === 'bypassPermissions'); + if (feature('TRANSCRIPT_CLASSIFIER')) { + // autoModeFlagCli is the "did the user intend auto this session" signal. + // Set when: --enable-auto-mode, --permission-mode auto, resolved mode + // is auto, OR settings defaultMode is auto but the gate denied it + // (permissionMode resolved to default with no explicit CLI override). + // Used by verifyAutoModeGateAccess to decide whether to notify on + // auto-unavailable, and by tengu_auto_mode_config opt-in carousel. + if ((options as { + enableAutoMode?: boolean; + }).enableAutoMode || permissionModeCli === 'auto' || permissionMode === 'auto' || !permissionModeCli && isDefaultPermissionModeAuto()) { + autoModeStateModule?.setAutoModeFlagCli(true); + } + } + + // Parse the MCP config files/strings if provided + let dynamicMcpConfig: Record = {}; + if (mcpConfig && mcpConfig.length > 0) { + // Process mcpConfig array + const processedConfigs = mcpConfig.map(config => config.trim()).filter(config => config.length > 0); + let allConfigs: Record = {}; + const allErrors: ValidationError[] = []; + for (const configItem of processedConfigs) { + let configs: Record | null = null; + let errors: ValidationError[] = []; + + // First try to parse as JSON string + const parsedJson = safeParseJSON(configItem); + if (parsedJson) { + const result = parseMcpConfig({ + configObject: parsedJson, + filePath: 'command line', + expandVars: true, + scope: 'dynamic' + }); + if (result.config) { + configs = result.config.mcpServers; + } else { + errors = result.errors; + } + } else { + // Try as file path + const configPath = resolve(configItem); + const result = parseMcpConfigFromFilePath({ + filePath: configPath, + expandVars: true, + scope: 'dynamic' + }); + if (result.config) { + configs = result.config.mcpServers; + } else { + errors = result.errors; + } + } + if (errors.length > 0) { + allErrors.push(...errors); + } else if (configs) { + // Merge configs, later ones override earlier ones + allConfigs = { + ...allConfigs, + ...configs + }; + } + } + if (allErrors.length > 0) { + const formattedErrors = allErrors.map(err => `${err.path ? err.path + ': ' : ''}${err.message}`).join('\n'); + logForDebugging(`--mcp-config validation failed (${allErrors.length} errors): ${formattedErrors}`, { + level: 'error' + }); + process.stderr.write(`Error: Invalid MCP configuration:\n${formattedErrors}\n`); + process.exit(1); + } + if (Object.keys(allConfigs).length > 0) { + // SDK hosts (Nest/Desktop) own their server naming and may reuse + // built-in names — skip reserved-name checks for type:'sdk'. + const nonSdkConfigNames = Object.entries(allConfigs).filter(([, config]) => config.type !== 'sdk').map(([name]) => name); + let reservedNameError: string | null = null; + if (nonSdkConfigNames.some(isClaudeInChromeMCPServer)) { + reservedNameError = `Invalid MCP configuration: "${CLAUDE_IN_CHROME_MCP_SERVER_NAME}" is a reserved MCP name.`; + } else if (feature('CHICAGO_MCP')) { + const { + isComputerUseMCPServer, + COMPUTER_USE_MCP_SERVER_NAME + } = await import('src/utils/computerUse/common.js'); + if (nonSdkConfigNames.some(isComputerUseMCPServer)) { + reservedNameError = `Invalid MCP configuration: "${COMPUTER_USE_MCP_SERVER_NAME}" is a reserved MCP name.`; + } + } + if (reservedNameError) { + // stderr+exit(1) — a throw here becomes a silent unhandled + // rejection in stream-json mode (void main() in cli.tsx). + process.stderr.write(`Error: ${reservedNameError}\n`); + process.exit(1); + } + + // Add dynamic scope to all configs. type:'sdk' entries pass through + // unchanged — they're extracted into sdkMcpConfigs downstream and + // passed to print.ts. The Python SDK relies on this path (it doesn't + // send sdkMcpServers in the initialize message). Dropping them here + // broke Coworker (inc-5122). The policy filter below already exempts + // type:'sdk', and the entries are inert without an SDK transport on + // stdin, so there's no bypass risk from letting them through. + const scopedConfigs = mapValues(allConfigs, config => ({ + ...config, + scope: 'dynamic' as const + })); + + // Enforce managed policy (allowedMcpServers / deniedMcpServers) on + // --mcp-config servers. Without this, the CLI flag bypasses the + // enterprise allowlist that user/project/local configs go through in + // getClaudeCodeMcpConfigs — callers spread dynamicMcpConfig back on + // top of filtered results. Filter here at the source so all + // downstream consumers see the policy-filtered set. + const { + allowed, + blocked + } = filterMcpServersByPolicy(scopedConfigs); + if (blocked.length > 0) { + process.stderr.write(`Warning: MCP ${plural(blocked.length, 'server')} blocked by enterprise policy: ${blocked.join(', ')}\n`); + } + dynamicMcpConfig = { + ...dynamicMcpConfig, + ...allowed + }; + } + } + + // Extract Claude in Chrome option and enforce claude.ai subscriber check (unless user is ant) + const chromeOpts = options as { + chrome?: boolean; + }; + // Store the explicit CLI flag so teammates can inherit it + setChromeFlagOverride(chromeOpts.chrome); + const enableClaudeInChrome = shouldEnableClaudeInChrome(chromeOpts.chrome) && ("external" === 'ant' || isClaudeAISubscriber()); + const autoEnableClaudeInChrome = !enableClaudeInChrome && shouldAutoEnableClaudeInChrome(); + if (enableClaudeInChrome) { + const platform = getPlatform(); + try { + logEvent('tengu_claude_in_chrome_setup', { + platform: platform as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + const { + mcpConfig: chromeMcpConfig, + allowedTools: chromeMcpTools, + systemPrompt: chromeSystemPrompt + } = setupClaudeInChrome(); + dynamicMcpConfig = { + ...dynamicMcpConfig, + ...chromeMcpConfig + }; + allowedTools.push(...chromeMcpTools); + if (chromeSystemPrompt) { + appendSystemPrompt = appendSystemPrompt ? `${chromeSystemPrompt}\n\n${appendSystemPrompt}` : chromeSystemPrompt; + } + } catch (error) { + logEvent('tengu_claude_in_chrome_setup_failed', { + platform: platform as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + logForDebugging(`[Claude in Chrome] Error: ${error}`); + logError(error); + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(`Error: Failed to run with Claude in Chrome.`); + process.exit(1); + } + } else if (autoEnableClaudeInChrome) { + try { + const { + mcpConfig: chromeMcpConfig + } = setupClaudeInChrome(); + dynamicMcpConfig = { + ...dynamicMcpConfig, + ...chromeMcpConfig + }; + const hint = feature('WEB_BROWSER_TOOL') && typeof Bun !== 'undefined' && 'WebView' in Bun ? CLAUDE_IN_CHROME_SKILL_HINT_WITH_WEBBROWSER : CLAUDE_IN_CHROME_SKILL_HINT; + appendSystemPrompt = appendSystemPrompt ? `${appendSystemPrompt}\n\n${hint}` : hint; + } catch (error) { + // Silently skip any errors for the auto-enable + logForDebugging(`[Claude in Chrome] Error (auto-enable): ${error}`); + } + } + + // Extract strict MCP config flag + const strictMcpConfig = options.strictMcpConfig || false; + + // Check if enterprise MCP configuration exists. When it does, only allow dynamic MCP + // configs that contain special server types (sdk) + if (doesEnterpriseMcpConfigExist()) { + if (strictMcpConfig) { + process.stderr.write(chalk.red('You cannot use --strict-mcp-config when an enterprise MCP config is present')); + process.exit(1); + } + + // For --mcp-config, allow if all servers are internal types (sdk) + if (dynamicMcpConfig && !areMcpConfigsAllowedWithEnterpriseMcpConfig(dynamicMcpConfig)) { + process.stderr.write(chalk.red('You cannot dynamically configure MCP servers when an enterprise MCP config is present')); + process.exit(1); + } + } + + // chicago MCP: guarded Computer Use (app allowlist + frontmost gate + + // SCContentFilter screenshots). Ant-only, GrowthBook-gated — failures + // are silent (this is dogfooding). Platform + interactive checks inline + // so non-macOS / print-mode ants skip the heavy @ant/computer-use-mcp + // import entirely. gates.js is light (type-only package import). + // + // Placed AFTER the enterprise-MCP-config check: that check rejects any + // dynamicMcpConfig entry with `type !== 'sdk'`, and our config is + // `type: 'stdio'`. An enterprise-config ant with the GB gate on would + // otherwise process.exit(1). Chrome has the same latent issue but has + // shipped without incident; chicago places itself correctly. + if (feature('CHICAGO_MCP') && getPlatform() === 'macos' && !getIsNonInteractiveSession()) { + try { + const { + getChicagoEnabled + } = await import('src/utils/computerUse/gates.js'); + if (getChicagoEnabled()) { + const { + setupComputerUseMCP + } = await import('src/utils/computerUse/setup.js'); + const { + mcpConfig, + allowedTools: cuTools + } = setupComputerUseMCP(); + dynamicMcpConfig = { + ...dynamicMcpConfig, + ...mcpConfig + }; + allowedTools.push(...cuTools); + } + } catch (error) { + logForDebugging(`[Computer Use MCP] Setup failed: ${errorMessage(error)}`); + } + } + + // Store additional directories for CLAUDE.md loading (controlled by env var) + setAdditionalDirectoriesForClaudeMd(addDir); + + // Channel server allowlist from --channels flag — servers whose + // inbound push notifications should register this session. The option + // is added inside a feature() block so TS doesn't know about it + // on the options type — same pattern as --assistant at main.tsx:1824. + // devChannels is deferred: showSetupScreens shows a confirmation dialog + // and only appends to allowedChannels on accept. + let devChannels: ChannelEntry[] | undefined; + if (feature('KAIROS') || feature('KAIROS_CHANNELS')) { + // Parse plugin:name@marketplace / server:Y tags into typed entries. + // Tag decides trust model downstream: plugin-kind hits marketplace + // verification + GrowthBook allowlist, server-kind always fails + // allowlist (schema is plugin-only) unless dev flag is set. + // Untagged or marketplace-less plugin entries are hard errors — + // silently not-matching in the gate would look like channels are + // "on" but nothing ever fires. + const parseChannelEntries = (raw: string[], flag: string): ChannelEntry[] => { + const entries: ChannelEntry[] = []; + const bad: string[] = []; + for (const c of raw) { + if (c.startsWith('plugin:')) { + const rest = c.slice(7); + const at = rest.indexOf('@'); + if (at <= 0 || at === rest.length - 1) { + bad.push(c); + } else { + entries.push({ + kind: 'plugin', + name: rest.slice(0, at), + marketplace: rest.slice(at + 1) + }); + } + } else if (c.startsWith('server:') && c.length > 7) { + entries.push({ + kind: 'server', + name: c.slice(7) + }); + } else { + bad.push(c); + } + } + if (bad.length > 0) { + process.stderr.write(chalk.red(`${flag} entries must be tagged: ${bad.join(', ')}\n` + ` plugin:@ — plugin-provided channel (allowlist enforced)\n` + ` server: — manually configured MCP server\n`)); + process.exit(1); + } + return entries; + }; + const channelOpts = options as { + channels?: string[]; + dangerouslyLoadDevelopmentChannels?: string[]; + }; + const rawChannels = channelOpts.channels; + const rawDev = channelOpts.dangerouslyLoadDevelopmentChannels; + // Always parse + set. ChannelsNotice reads getAllowedChannels() and + // renders the appropriate branch (disabled/noAuth/policyBlocked/ + // listening) in the startup screen. gateChannelServer() enforces. + // --channels works in both interactive and print/SDK modes; dev-channels + // stays interactive-only (requires a confirmation dialog). + let channelEntries: ChannelEntry[] = []; + if (rawChannels && rawChannels.length > 0) { + channelEntries = parseChannelEntries(rawChannels, '--channels'); + setAllowedChannels(channelEntries); + } + if (!isNonInteractiveSession) { + if (rawDev && rawDev.length > 0) { + devChannels = parseChannelEntries(rawDev, '--dangerously-load-development-channels'); + } + } + // Flag-usage telemetry. Plugin identifiers are logged (same tier as + // tengu_plugin_installed — public-registry-style names); server-kind + // names are not (MCP-server-name tier, opt-in-only elsewhere). + // Per-server gate outcomes land in tengu_mcp_channel_gate once + // servers connect. Dev entries go through a confirmation dialog after + // this — dev_plugins captures what was typed, not what was accepted. + if (channelEntries.length > 0 || (devChannels?.length ?? 0) > 0) { + const joinPluginIds = (entries: ChannelEntry[]) => { + const ids = entries.flatMap(e => e.kind === 'plugin' ? [`${e.name}@${e.marketplace}`] : []); + return ids.length > 0 ? ids.sort().join(',') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS : undefined; + }; + logEvent('tengu_mcp_channel_flags', { + channels_count: channelEntries.length, + dev_count: devChannels?.length ?? 0, + plugins: joinPluginIds(channelEntries), + dev_plugins: joinPluginIds(devChannels ?? []) + }); + } + } + + // SDK opt-in for SendUserMessage via --tools. All sessions require + // explicit opt-in; listing it in --tools signals intent. Runs BEFORE + // initializeToolPermissionContext so getToolsForDefaultPreset() sees + // the tool as enabled when computing the base-tools disallow filter. + // Conditional require avoids leaking the tool-name string into + // external builds. + if ((feature('KAIROS') || feature('KAIROS_BRIEF')) && baseTools.length > 0) { + /* eslint-disable @typescript-eslint/no-require-imports */ + const { + BRIEF_TOOL_NAME, + LEGACY_BRIEF_TOOL_NAME + } = require('./tools/BriefTool/prompt.js') as typeof import('./tools/BriefTool/prompt.js'); + const { + isBriefEntitled + } = require('./tools/BriefTool/BriefTool.js') as typeof import('./tools/BriefTool/BriefTool.js'); + /* eslint-enable @typescript-eslint/no-require-imports */ + const parsed = parseToolListFromCLI(baseTools); + if ((parsed.includes(BRIEF_TOOL_NAME) || parsed.includes(LEGACY_BRIEF_TOOL_NAME)) && isBriefEntitled()) { + setUserMsgOptIn(true); + } + } + + // This await replaces blocking existsSync/statSync calls that were already in + // the startup path. Wall-clock time is unchanged; we just yield to the event + // loop during the fs I/O instead of blocking it. See #19661. + const initResult = await initializeToolPermissionContext({ + allowedToolsCli: allowedTools, + disallowedToolsCli: disallowedTools, + baseToolsCli: baseTools, + permissionMode, + allowDangerouslySkipPermissions, + addDirs: addDir + }); + let toolPermissionContext = initResult.toolPermissionContext; + const { + warnings, + dangerousPermissions, + overlyBroadBashPermissions + } = initResult; + + // Handle overly broad shell allow rules for ant users (Bash(*), PowerShell(*)) + if ("external" === 'ant' && overlyBroadBashPermissions.length > 0) { + for (const permission of overlyBroadBashPermissions) { + logForDebugging(`Ignoring overly broad shell permission ${permission.ruleDisplay} from ${permission.sourceDisplay}`); + } + toolPermissionContext = removeDangerousPermissions(toolPermissionContext, overlyBroadBashPermissions); + } + if (feature('TRANSCRIPT_CLASSIFIER') && dangerousPermissions.length > 0) { + toolPermissionContext = stripDangerousPermissionsForAutoMode(toolPermissionContext); + } + + // Print any warnings from initialization + warnings.forEach(warning => { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(warning); + }); + void assertMinVersion(); + + // claude.ai config fetch: -p mode only (interactive uses useManageMCPConnections + // two-phase loading). Kicked off here to overlap with setup(); awaited + // before runHeadless so single-turn -p sees connectors. Skipped under + // enterprise/strict MCP to preserve policy boundaries. + const claudeaiConfigPromise: Promise> = isNonInteractiveSession && !strictMcpConfig && !doesEnterpriseMcpConfigExist() && + // --bare / SIMPLE: skip claude.ai proxy servers (datadog, Gmail, + // Slack, BigQuery, PubMed — 6-14s each to connect). Scripted calls + // that need MCP pass --mcp-config explicitly. + !isBareMode() ? fetchClaudeAIMcpConfigsIfEligible().then(configs => { + const { + allowed, + blocked + } = filterMcpServersByPolicy(configs); + if (blocked.length > 0) { + process.stderr.write(`Warning: claude.ai MCP ${plural(blocked.length, 'server')} blocked by enterprise policy: ${blocked.join(', ')}\n`); + } + return allowed; + }) : Promise.resolve({}); + + // Kick off MCP config loading early (safe - just reads files, no execution). + // Both interactive and -p use getClaudeCodeMcpConfigs (local file reads only). + // The local promise is awaited later (before prefetchAllMcpResources) to + // overlap config I/O with setup(), commands loading, and trust dialog. + logForDebugging('[STARTUP] Loading MCP configs...'); + const mcpConfigStart = Date.now(); + let mcpConfigResolvedMs: number | undefined; + // --bare skips auto-discovered MCP (.mcp.json, user settings, plugins) — + // only explicit --mcp-config works. dynamicMcpConfig is spread onto + // allMcpConfigs downstream so it survives this skip. + const mcpConfigPromise = (strictMcpConfig || isBareMode() ? Promise.resolve({ + servers: {} as Record + }) : getClaudeCodeMcpConfigs(dynamicMcpConfig)).then(result => { + mcpConfigResolvedMs = Date.now() - mcpConfigStart; + return result; + }); + + // NOTE: We do NOT call prefetchAllMcpResources here - that's deferred until after trust dialog + + if (inputFormat && inputFormat !== 'text' && inputFormat !== 'stream-json') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(`Error: Invalid input format "${inputFormat}".`); + process.exit(1); + } + if (inputFormat === 'stream-json' && outputFormat !== 'stream-json') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(`Error: --input-format=stream-json requires output-format=stream-json.`); + process.exit(1); + } + + // Validate sdkUrl is only used with appropriate formats (formats are auto-set above) + if (sdkUrl) { + if (inputFormat !== 'stream-json' || outputFormat !== 'stream-json') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(`Error: --sdk-url requires both --input-format=stream-json and --output-format=stream-json.`); + process.exit(1); + } + } + + // Validate replayUserMessages is only used with stream-json formats + if (options.replayUserMessages) { + if (inputFormat !== 'stream-json' || outputFormat !== 'stream-json') { + // biome-ignore lint/suspicious/noConsole:: intentional console output + console.error(`Error: --replay-user-messages requires both --input-format=stream-json and --output-format=stream-json.`); + process.exit(1); + } + } + + // Validate includePartialMessages is only used with print mode and stream-json output + if (effectiveIncludePartialMessages) { + if (!isNonInteractiveSession || outputFormat !== 'stream-json') { + writeToStderr(`Error: --include-partial-messages requires --print and --output-format=stream-json.`); + process.exit(1); + } + } + + // Validate --no-session-persistence is only used with print mode + if (options.sessionPersistence === false && !isNonInteractiveSession) { + writeToStderr(`Error: --no-session-persistence can only be used with --print mode.`); + process.exit(1); + } + const effectivePrompt = prompt || ''; + let inputPrompt = await getInputPrompt(effectivePrompt, (inputFormat ?? 'text') as 'text' | 'stream-json'); + profileCheckpoint('action_after_input_prompt'); + + // Activate proactive mode BEFORE getTools() so SleepTool.isEnabled() + // (which returns isProactiveActive()) passes and Sleep is included. + // The later REPL-path maybeActivateProactive() calls are idempotent. + maybeActivateProactive(options); + let tools = getTools(toolPermissionContext); + + // Apply coordinator mode tool filtering for headless path + // (mirrors useMergedTools.ts filtering for REPL/interactive path) + if (feature('COORDINATOR_MODE') && isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)) { + const { + applyCoordinatorToolFilter + } = await import('./utils/toolPool.js'); + tools = applyCoordinatorToolFilter(tools); + } + profileCheckpoint('action_tools_loaded'); + let jsonSchema: ToolInputJSONSchema | undefined; + if (isSyntheticOutputToolEnabled({ + isNonInteractiveSession + }) && options.jsonSchema) { + jsonSchema = jsonParse(options.jsonSchema) as ToolInputJSONSchema; + } + if (jsonSchema) { + const syntheticOutputResult = createSyntheticOutputTool(jsonSchema); + if ('tool' in syntheticOutputResult) { + // Add SyntheticOutputTool to the tools array AFTER getTools() filtering. + // This tool is excluded from normal filtering (see tools.ts) because it's + // an implementation detail for structured output, not a user-controlled tool. + tools = [...tools, syntheticOutputResult.tool]; + logEvent('tengu_structured_output_enabled', { + schema_property_count: Object.keys(jsonSchema.properties as Record || {}).length as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + has_required_fields: Boolean(jsonSchema.required) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } else { + logEvent('tengu_structured_output_failure', { + error: 'Invalid JSON schema' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + } + + // IMPORTANT: setup() must be called before any other code that depends on the cwd or worktree setup + profileCheckpoint('action_before_setup'); + logForDebugging('[STARTUP] Running setup()...'); + const setupStart = Date.now(); + const { + setup + } = await import('./setup.js'); + const messagingSocketPath = feature('UDS_INBOX') ? (options as { + messagingSocketPath?: string; + }).messagingSocketPath : undefined; + // Parallelize setup() with commands+agents loading. setup()'s ~28ms is + // mostly startUdsMessaging (socket bind, ~20ms) — not disk-bound, so it + // doesn't contend with getCommands' file reads. Gated on !worktreeEnabled + // since --worktree makes setup() process.chdir() (setup.ts:203), and + // commands/agents need the post-chdir cwd. + const preSetupCwd = getCwd(); + // Register bundled skills/plugins before kicking getCommands() — they're + // pure in-memory array pushes (<1ms, zero I/O) that getBundledSkills() + // reads synchronously. Previously ran inside setup() after ~20ms of + // await points, so the parallel getCommands() memoized an empty list. + if (process.env.CLAUDE_CODE_ENTRYPOINT !== 'local-agent') { + initBuiltinPlugins(); + initBundledSkills(); + } + const setupPromise = setup(preSetupCwd, permissionMode, allowDangerouslySkipPermissions, worktreeEnabled, worktreeName, tmuxEnabled, sessionId ? validateUuid(sessionId) : undefined, worktreePRNumber, messagingSocketPath); + const commandsPromise = worktreeEnabled ? null : getCommands(preSetupCwd); + const agentDefsPromise = worktreeEnabled ? null : getAgentDefinitionsWithOverrides(preSetupCwd); + // Suppress transient unhandledRejection if these reject during the + // ~28ms setupPromise await before Promise.all joins them below. + commandsPromise?.catch(() => {}); + agentDefsPromise?.catch(() => {}); + await setupPromise; + logForDebugging(`[STARTUP] setup() completed in ${Date.now() - setupStart}ms`); + profileCheckpoint('action_after_setup'); + + // Replay user messages into stream-json only when the socket was + // explicitly requested. The auto-generated socket is passive — it + // lets tools inject if they want to, but turning it on by default + // shouldn't reshape stream-json for SDK consumers who never touch it. + // Callers who inject and also want those injections visible in the + // stream pass --messaging-socket-path explicitly (or --replay-user-messages). + let effectiveReplayUserMessages = !!options.replayUserMessages; + if (feature('UDS_INBOX')) { + if (!effectiveReplayUserMessages && outputFormat === 'stream-json') { + effectiveReplayUserMessages = !!(options as { + messagingSocketPath?: string; + }).messagingSocketPath; + } + } + if (getIsNonInteractiveSession()) { + // Apply full merged settings env now (including project-scoped + // .claude/settings.json PATH/GIT_DIR/GIT_WORK_TREE) so gitExe() and + // the git spawn below see it. Trust is implicit in -p mode; the + // docstring at managedEnv.ts:96-97 says this applies "potentially + // dangerous environment variables such as LD_PRELOAD, PATH" from all + // sources. The later call in the isNonInteractiveSession block below + // is idempotent (Object.assign, configureGlobalAgents ejects prior + // interceptor) and picks up any plugin-contributed env after plugin + // init. Project settings are already loaded here: + // applySafeConfigEnvironmentVariables in init() called + // getSettings_DEPRECATED at managedEnv.ts:86 which merges all enabled + // sources including projectSettings/localSettings. + applyConfigEnvironmentVariables(); + + // Spawn git status/log/branch now so the subprocess execution overlaps + // with the getCommands await below and startDeferredPrefetches. After + // setup() so cwd is final (setup.ts:254 may process.chdir(worktreePath) + // for --worktree) and after the applyConfigEnvironmentVariables above + // so PATH/GIT_DIR/GIT_WORK_TREE from all sources (trusted + project) + // are applied. getSystemContext is memoized; the + // prefetchSystemContextIfSafe call in startDeferredPrefetches becomes + // a cache hit. The microtask from await getIsGit() drains at the + // getCommands Promise.all await below. Trust is implicit in -p mode + // (same gate as prefetchSystemContextIfSafe). + void getSystemContext(); + // Kick getUserContext now too — its first await (fs.readFile in + // getMemoryFiles) yields naturally, so the CLAUDE.md directory walk + // runs during the ~280ms overlap window before the context + // Promise.all join in print.ts. The void getUserContext() in + // startDeferredPrefetches becomes a memoize cache-hit. + void getUserContext(); + // Kick ensureModelStringsInitialized now — for Bedrock this triggers + // a 100-200ms profile fetch that was awaited serially at + // print.ts:739. updateBedrockModelStrings is sequential()-wrapped so + // the await joins the in-flight fetch. Non-Bedrock is a sync + // early-return (zero-cost). + void ensureModelStringsInitialized(); + } + + // Apply --name: cache-only so no orphan file is created before the + // session ID is finalized by --continue/--resume. materializeSessionFile + // persists it on the first user message; REPL's useTerminalTitle reads it + // via getCurrentSessionTitle. + const sessionNameArg = options.name?.trim(); + if (sessionNameArg) { + cacheSessionTitle(sessionNameArg); + } + + // Ant model aliases (capybara-fast etc.) resolve via the + // tengu_ant_model_override GrowthBook flag. _CACHED_MAY_BE_STALE reads + // disk synchronously; disk is populated by a fire-and-forget write. On a + // cold cache, parseUserSpecifiedModel returns the unresolved alias, the + // API 404s, and -p exits before the async write lands — crashloop on + // fresh pods. Awaiting init here populates the in-memory payload map that + // _CACHED_MAY_BE_STALE now checks first. Gated so the warm path stays + // non-blocking: + // - explicit model via --model or ANTHROPIC_MODEL (both feed alias resolution) + // - no env override (which short-circuits _CACHED_MAY_BE_STALE before disk) + // - flag absent from disk (== null also catches pre-#22279 poisoned null) + const explicitModel = options.model || process.env.ANTHROPIC_MODEL; + if ("external" === 'ant' && explicitModel && explicitModel !== 'default' && !hasGrowthBookEnvOverride('tengu_ant_model_override') && getGlobalConfig().cachedGrowthBookFeatures?.['tengu_ant_model_override'] == null) { + await initializeGrowthBook(); + } + + // Special case the default model with the null keyword + // NOTE: Model resolution happens after setup() to ensure trust is established before AWS auth + const userSpecifiedModel = options.model === 'default' ? getDefaultMainLoopModel() : options.model; + const userSpecifiedFallbackModel = fallbackModel === 'default' ? getDefaultMainLoopModel() : fallbackModel; + + // Reuse preSetupCwd unless setup() chdir'd (worktreeEnabled). Saves a + // getCwd() syscall in the common path. + const currentCwd = worktreeEnabled ? getCwd() : preSetupCwd; + logForDebugging('[STARTUP] Loading commands and agents...'); + const commandsStart = Date.now(); + // Join the promises kicked before setup() (or start fresh if + // worktreeEnabled gated the early kick). Both memoized by cwd. + const [commands, agentDefinitionsResult] = await Promise.all([commandsPromise ?? getCommands(currentCwd), agentDefsPromise ?? getAgentDefinitionsWithOverrides(currentCwd)]); + logForDebugging(`[STARTUP] Commands and agents loaded in ${Date.now() - commandsStart}ms`); + profileCheckpoint('action_commands_loaded'); + + // Parse CLI agents if provided via --agents flag + let cliAgents: typeof agentDefinitionsResult.activeAgents = []; + if (agentsJson) { + try { + const parsedAgents = safeParseJSON(agentsJson); + if (parsedAgents) { + cliAgents = parseAgentsFromJson(parsedAgents, 'flagSettings'); + } + } catch (error) { + logError(error); + } + } + + // Merge CLI agents with existing ones + const allAgents = [...agentDefinitionsResult.allAgents, ...cliAgents]; + const agentDefinitions = { + ...agentDefinitionsResult, + allAgents, + activeAgents: getActiveAgentsFromList(allAgents) + }; + + // Look up main thread agent from CLI flag or settings + const agentSetting = agentCli ?? getInitialSettings().agent; + let mainThreadAgentDefinition: (typeof agentDefinitions.activeAgents)[number] | undefined; + if (agentSetting) { + mainThreadAgentDefinition = agentDefinitions.activeAgents.find(agent => agent.agentType === agentSetting); + if (!mainThreadAgentDefinition) { + logForDebugging(`Warning: agent "${agentSetting}" not found. ` + `Available agents: ${agentDefinitions.activeAgents.map(a => a.agentType).join(', ')}. ` + `Using default behavior.`); + } + } + + // Store the main thread agent type in bootstrap state so hooks can access it + setMainThreadAgentType(mainThreadAgentDefinition?.agentType); + + // Log agent flag usage — only log agent name for built-in agents to avoid leaking custom agent names + if (mainThreadAgentDefinition) { + logEvent('tengu_agent_flag', { + agentType: isBuiltInAgent(mainThreadAgentDefinition) ? mainThreadAgentDefinition.agentType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS : 'custom' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + ...(agentCli && { + source: 'cli' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }) + }); + } + + // Persist agent setting to session transcript for resume view display and restoration + if (mainThreadAgentDefinition?.agentType) { + saveAgentSetting(mainThreadAgentDefinition.agentType); + } + + // Apply the agent's system prompt for non-interactive sessions + // (interactive mode uses buildEffectiveSystemPrompt instead) + if (isNonInteractiveSession && mainThreadAgentDefinition && !systemPrompt && !isBuiltInAgent(mainThreadAgentDefinition)) { + const agentSystemPrompt = mainThreadAgentDefinition.getSystemPrompt(); + if (agentSystemPrompt) { + systemPrompt = agentSystemPrompt; + } + } + + // initialPrompt goes first so its slash command (if any) is processed; + // user-provided text becomes trailing context. + // Only concatenate when inputPrompt is a string. When it's an + // AsyncIterable (SDK stream-json mode), template interpolation would + // call .toString() producing "[object Object]". The AsyncIterable case + // is handled in print.ts via structuredIO.prependUserMessage(). + if (mainThreadAgentDefinition?.initialPrompt) { + if (typeof inputPrompt === 'string') { + inputPrompt = inputPrompt ? `${mainThreadAgentDefinition.initialPrompt}\n\n${inputPrompt}` : mainThreadAgentDefinition.initialPrompt; + } else if (!inputPrompt) { + inputPrompt = mainThreadAgentDefinition.initialPrompt; + } + } + + // Compute effective model early so hooks can run in parallel with MCP + // If user didn't specify a model but agent has one, use the agent's model + let effectiveModel = userSpecifiedModel; + if (!effectiveModel && mainThreadAgentDefinition?.model && mainThreadAgentDefinition.model !== 'inherit') { + effectiveModel = parseUserSpecifiedModel(mainThreadAgentDefinition.model); + } + setMainLoopModelOverride(effectiveModel); + + // Compute resolved model for hooks (use user-specified model at launch) + setInitialMainLoopModel(getUserSpecifiedModelSetting() || null); + const initialMainLoopModel = getInitialMainLoopModel(); + const resolvedInitialModel = parseUserSpecifiedModel(initialMainLoopModel ?? getDefaultMainLoopModel()); + let advisorModel: string | undefined; + if (isAdvisorEnabled()) { + const advisorOption = canUserConfigureAdvisor() ? (options as { + advisor?: string; + }).advisor : undefined; + if (advisorOption) { + logForDebugging(`[AdvisorTool] --advisor ${advisorOption}`); + if (!modelSupportsAdvisor(resolvedInitialModel)) { + process.stderr.write(chalk.red(`Error: The model "${resolvedInitialModel}" does not support the advisor tool.\n`)); + process.exit(1); + } + const normalizedAdvisorModel = normalizeModelStringForAPI(parseUserSpecifiedModel(advisorOption)); + if (!isValidAdvisorModel(normalizedAdvisorModel)) { + process.stderr.write(chalk.red(`Error: The model "${advisorOption}" cannot be used as an advisor.\n`)); + process.exit(1); + } + } + advisorModel = canUserConfigureAdvisor() ? advisorOption ?? getInitialAdvisorSetting() : advisorOption; + if (advisorModel) { + logForDebugging(`[AdvisorTool] Advisor model: ${advisorModel}`); + } + } + + // For tmux teammates with --agent-type, append the custom agent's prompt + if (isAgentSwarmsEnabled() && storedTeammateOpts?.agentId && storedTeammateOpts?.agentName && storedTeammateOpts?.teamName && storedTeammateOpts?.agentType) { + // Look up the custom agent definition + const customAgent = agentDefinitions.activeAgents.find(a => a.agentType === storedTeammateOpts.agentType); + if (customAgent) { + // Get the prompt - need to handle both built-in and custom agents + let customPrompt: string | undefined; + if (customAgent.source === 'built-in') { + // Built-in agents have getSystemPrompt that takes toolUseContext + // We can't access full toolUseContext here, so skip for now + logForDebugging(`[teammate] Built-in agent ${storedTeammateOpts.agentType} - skipping custom prompt (not supported)`); + } else { + // Custom agents have getSystemPrompt that takes no args + customPrompt = customAgent.getSystemPrompt(); + } + + // Log agent memory loaded event for tmux teammates + if (customAgent.memory) { + logEvent('tengu_agent_memory_loaded', { + ...("external" === 'ant' && { + agent_type: customAgent.agentType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }), + scope: customAgent.memory as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + source: 'teammate' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + } + if (customPrompt) { + const customInstructions = `\n# Custom Agent Instructions\n${customPrompt}`; + appendSystemPrompt = appendSystemPrompt ? `${appendSystemPrompt}\n\n${customInstructions}` : customInstructions; + } + } else { + logForDebugging(`[teammate] Custom agent ${storedTeammateOpts.agentType} not found in available agents`); + } + } + maybeActivateBrief(options); + // defaultView: 'chat' is a persisted opt-in — check entitlement and set + // userMsgOptIn so the tool + prompt section activate. Interactive-only: + // defaultView is a display preference; SDK sessions have no display, and + // the assistant installer writes defaultView:'chat' to settings.local.json + // which would otherwise leak into --print sessions in the same directory. + // Runs right after maybeActivateBrief() so all startup opt-in paths fire + // BEFORE any isBriefEnabled() read below (proactive prompt's + // briefVisibility). A persisted 'chat' after a GB kill-switch falls + // through (entitlement fails). + if ((feature('KAIROS') || feature('KAIROS_BRIEF')) && !getIsNonInteractiveSession() && !getUserMsgOptIn() && getInitialSettings().defaultView === 'chat') { + /* eslint-disable @typescript-eslint/no-require-imports */ + const { + isBriefEntitled + } = require('./tools/BriefTool/BriefTool.js') as typeof import('./tools/BriefTool/BriefTool.js'); + /* eslint-enable @typescript-eslint/no-require-imports */ + if (isBriefEntitled()) { + setUserMsgOptIn(true); + } + } + // Coordinator mode has its own system prompt and filters out Sleep, so + // the generic proactive prompt would tell it to call a tool it can't + // access and conflict with delegation instructions. + if ((feature('PROACTIVE') || feature('KAIROS')) && ((options as { + proactive?: boolean; + }).proactive || isEnvTruthy(process.env.CLAUDE_CODE_PROACTIVE)) && !coordinatorModeModule?.isCoordinatorMode()) { + /* eslint-disable @typescript-eslint/no-require-imports */ + const briefVisibility = feature('KAIROS') || feature('KAIROS_BRIEF') ? (require('./tools/BriefTool/BriefTool.js') as typeof import('./tools/BriefTool/BriefTool.js')).isBriefEnabled() ? 'Call SendUserMessage at checkpoints to mark where things stand.' : 'The user will see any text you output.' : 'The user will see any text you output.'; + /* eslint-enable @typescript-eslint/no-require-imports */ + const proactivePrompt = `\n# Proactive Mode\n\nYou are in proactive mode. Take initiative — explore, act, and make progress without waiting for instructions.\n\nStart by briefly greeting the user.\n\nYou will receive periodic prompts. These are check-ins. Do whatever seems most useful, or call Sleep if there's nothing to do. ${briefVisibility}`; + appendSystemPrompt = appendSystemPrompt ? `${appendSystemPrompt}\n\n${proactivePrompt}` : proactivePrompt; + } + if (feature('KAIROS') && kairosEnabled && assistantModule) { + const assistantAddendum = assistantModule.getAssistantSystemPromptAddendum(); + appendSystemPrompt = appendSystemPrompt ? `${appendSystemPrompt}\n\n${assistantAddendum}` : assistantAddendum; + } + + // Ink root is only needed for interactive sessions — patchConsole in the + // Ink constructor would swallow console output in headless mode. + let root!: Root; + let getFpsMetrics!: () => FpsMetrics | undefined; + let stats!: StatsStore; + + // Show setup screens after commands are loaded + if (!isNonInteractiveSession) { + const ctx = getRenderContext(false); + getFpsMetrics = ctx.getFpsMetrics; + stats = ctx.stats; + // Install asciicast recorder before Ink mounts (ant-only, opt-in via CLAUDE_CODE_TERMINAL_RECORDING=1) + if ("external" === 'ant') { + installAsciicastRecorder(); + } + const { + createRoot + } = await import('./ink.js'); + root = await createRoot(ctx.renderOptions); + + // Log startup time now, before any blocking dialog renders. Logging + // from REPL's first render (the old location) included however long + // the user sat on trust/OAuth/onboarding/resume-picker — p99 was ~70s + // dominated by dialog-wait time, not code-path startup. + logEvent('tengu_timer', { + event: 'startup' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + durationMs: Math.round(process.uptime() * 1000) + }); + logForDebugging('[STARTUP] Running showSetupScreens()...'); + const setupScreensStart = Date.now(); + const onboardingShown = await showSetupScreens(root, permissionMode, allowDangerouslySkipPermissions, commands, enableClaudeInChrome, devChannels); + logForDebugging(`[STARTUP] showSetupScreens() completed in ${Date.now() - setupScreensStart}ms`); + + // Now that trust is established and GrowthBook has auth headers, + // resolve the --remote-control / --rc entitlement gate. + if (feature('BRIDGE_MODE') && remoteControlOption !== undefined) { + const { + getBridgeDisabledReason + } = await import('./bridge/bridgeEnabled.js'); + const disabledReason = await getBridgeDisabledReason(); + remoteControl = disabledReason === null; + if (disabledReason) { + process.stderr.write(chalk.yellow(`${disabledReason}\n--rc flag ignored.\n`)); + } + } + + // Check for pending agent memory snapshot updates (only for --agent mode, ant-only) + if (feature('AGENT_MEMORY_SNAPSHOT') && mainThreadAgentDefinition && isCustomAgent(mainThreadAgentDefinition) && mainThreadAgentDefinition.memory && mainThreadAgentDefinition.pendingSnapshotUpdate) { + const agentDef = mainThreadAgentDefinition; + const choice = await launchSnapshotUpdateDialog(root, { + agentType: agentDef.agentType, + scope: agentDef.memory!, + snapshotTimestamp: agentDef.pendingSnapshotUpdate!.snapshotTimestamp + }); + if (choice === 'merge') { + const { + buildMergePrompt + } = await import('./components/agents/SnapshotUpdateDialog.js'); + const mergePrompt = buildMergePrompt(agentDef.agentType, agentDef.memory!); + inputPrompt = inputPrompt ? `${mergePrompt}\n\n${inputPrompt}` : mergePrompt; + } + agentDef.pendingSnapshotUpdate = undefined; + } + + // Skip executing /login if we just completed onboarding for it + if (onboardingShown && prompt?.trim().toLowerCase() === '/login') { + prompt = ''; + } + if (onboardingShown) { + // Refresh auth-dependent services now that the user has logged in during onboarding. + // Keep in sync with the post-login logic in src/commands/login.tsx + void refreshRemoteManagedSettings(); + void refreshPolicyLimits(); + // Clear user data cache BEFORE GrowthBook refresh so it picks up fresh credentials + resetUserCache(); + // Refresh GrowthBook after login to get updated feature flags (e.g., for claude.ai MCPs) + refreshGrowthBookAfterAuthChange(); + // Clear any stale trusted device token then enroll for Remote Control. + // Both self-gate on tengu_sessions_elevated_auth_enforcement internally + // — enrollTrustedDevice() via checkGate_CACHED_OR_BLOCKING (awaits + // the GrowthBook reinit above), clearTrustedDeviceToken() via the + // sync cached check (acceptable since clear is idempotent). + void import('./bridge/trustedDevice.js').then(m => { + m.clearTrustedDeviceToken(); + return m.enrollTrustedDevice(); + }); + } + + // Validate that the active token's org matches forceLoginOrgUUID (if set + // in managed settings). Runs after onboarding so managed settings and + // login state are fully loaded. + const orgValidation = await validateForceLoginOrg(); + if (!orgValidation.valid) { + await exitWithError(root, orgValidation.message); + } + } + + // If gracefulShutdown was initiated (e.g., user rejected trust dialog), + // process.exitCode will be set. Skip all subsequent operations that could + // trigger code execution before the process exits (e.g. we don't want apiKeyHelper + // to run if trust was not established). + if (process.exitCode !== undefined) { + logForDebugging('Graceful shutdown initiated, skipping further initialization'); + return; + } + + // Initialize LSP manager AFTER trust is established (or in non-interactive mode + // where trust is implicit). This prevents plugin LSP servers from executing + // code in untrusted directories before user consent. + // Must be after inline plugins are set (if any) so --plugin-dir LSP servers are included. + initializeLspServerManager(); + + // Show settings validation errors after trust is established + // MCP config errors don't block settings from loading, so exclude them + if (!isNonInteractiveSession) { + const { + errors + } = getSettingsWithErrors(); + const nonMcpErrors = errors.filter(e => !e.mcpErrorMetadata); + if (nonMcpErrors.length > 0) { + await launchInvalidSettingsDialog(root, { + settingsErrors: nonMcpErrors, + onExit: () => gracefulShutdownSync(1) + }); + } + } + + // Check quota status, fast mode, passes eligibility, and bootstrap data + // after trust is established. These make API calls which could trigger + // apiKeyHelper execution. + // --bare / SIMPLE: skip — these are cache-warms for the REPL's + // first-turn responsiveness (quota, passes, fastMode, bootstrap data). Fast + // mode doesn't apply to the Agent SDK anyway (see getFastModeUnavailableReason). + const bgRefreshThrottleMs = getFeatureValue_CACHED_MAY_BE_STALE('tengu_cicada_nap_ms', 0); + const lastPrefetched = getGlobalConfig().startupPrefetchedAt ?? 0; + const skipStartupPrefetches = isBareMode() || bgRefreshThrottleMs > 0 && Date.now() - lastPrefetched < bgRefreshThrottleMs; + if (!skipStartupPrefetches) { + const lastPrefetchedInfo = lastPrefetched > 0 ? ` last ran ${Math.round((Date.now() - lastPrefetched) / 1000)}s ago` : ''; + logForDebugging(`Starting background startup prefetches${lastPrefetchedInfo}`); + checkQuotaStatus().catch(error => logError(error)); + + // Fetch bootstrap data from the server and update all cache values. + void fetchBootstrapData(); + + // TODO: Consolidate other prefetches into a single bootstrap request. + void prefetchPassesEligibility(); + if (!getFeatureValue_CACHED_MAY_BE_STALE('tengu_miraculo_the_bard', false)) { + void prefetchFastModeStatus(); + } else { + // Kill switch skips the network call, not org-policy enforcement. + // Resolve from cache so orgStatus doesn't stay 'pending' (which + // getFastModeUnavailableReason treats as permissive). + resolveFastModeStatusFromCache(); + } + if (bgRefreshThrottleMs > 0) { + saveGlobalConfig(current => ({ + ...current, + startupPrefetchedAt: Date.now() + })); + } + } else { + logForDebugging(`Skipping startup prefetches, last ran ${Math.round((Date.now() - lastPrefetched) / 1000)}s ago`); + // Resolve fast mode org status from cache (no network) + resolveFastModeStatusFromCache(); + } + if (!isNonInteractiveSession) { + void refreshExampleCommands(); // Pre-fetch example commands (runs git log, no API call) + } + + // Resolve MCP configs (started early, overlaps with setup/trust dialog work) + const { + servers: existingMcpConfigs + } = await mcpConfigPromise; + logForDebugging(`[STARTUP] MCP configs resolved in ${mcpConfigResolvedMs}ms (awaited at +${Date.now() - mcpConfigStart}ms)`); + // CLI flag (--mcp-config) should override file-based configs, matching settings precedence + const allMcpConfigs = { + ...existingMcpConfigs, + ...dynamicMcpConfig + }; + + // Separate SDK configs from regular MCP configs + const sdkMcpConfigs: Record = {}; + const regularMcpConfigs: Record = {}; + for (const [name, config] of Object.entries(allMcpConfigs)) { + const typedConfig = config as ScopedMcpServerConfig | McpSdkServerConfig; + if (typedConfig.type === 'sdk') { + sdkMcpConfigs[name] = typedConfig as McpSdkServerConfig; + } else { + regularMcpConfigs[name] = typedConfig as ScopedMcpServerConfig; + } + } + profileCheckpoint('action_mcp_configs_loaded'); + + // Prefetch MCP resources after trust dialog (this is where execution happens). + // Interactive mode only: print mode defers connects until headlessStore exists + // and pushes per-server (below), so ToolSearch's pending-client handling works + // and one slow server doesn't block the batch. + const localMcpPromise = isNonInteractiveSession ? Promise.resolve({ + clients: [], + tools: [], + commands: [] + }) : prefetchAllMcpResources(regularMcpConfigs); + const claudeaiMcpPromise = isNonInteractiveSession ? Promise.resolve({ + clients: [], + tools: [], + commands: [] + }) : claudeaiConfigPromise.then(configs => Object.keys(configs).length > 0 ? prefetchAllMcpResources(configs) : { + clients: [], + tools: [], + commands: [] + }); + // Merge with dedup by name: each prefetchAllMcpResources call independently + // adds helper tools (ListMcpResourcesTool, ReadMcpResourceTool) via + // local dedup flags, so merging two calls can yield duplicates. print.ts + // already uniqBy's the final tool pool, but dedup here keeps appState clean. + const mcpPromise = Promise.all([localMcpPromise, claudeaiMcpPromise]).then(([local, claudeai]) => ({ + clients: [...local.clients, ...claudeai.clients], + tools: uniqBy([...local.tools, ...claudeai.tools], 'name'), + commands: uniqBy([...local.commands, ...claudeai.commands], 'name') + })); + + // Start hooks early so they run in parallel with MCP connections. + // Skip for initOnly/init/maintenance (handled separately), non-interactive + // (handled via setupTrigger), and resume/continue (conversationRecovery.ts + // fires 'resume' instead — without this guard, hooks fire TWICE on /resume + // and the second systemMessage clobbers the first. gh-30825) + const hooksPromise = initOnly || init || maintenance || isNonInteractiveSession || options.continue || options.resume ? null : processSessionStartHooks('startup', { + agentType: mainThreadAgentDefinition?.agentType, + model: resolvedInitialModel + }); + + // MCP never blocks REPL render OR turn 1 TTFT. useManageMCPConnections + // populates appState.mcp async as servers connect (connectToServer is + // memoized — the prefetch calls above and the hook converge on the same + // connections). getToolUseContext reads store.getState() fresh via + // computeTools(), so turn 1 sees whatever's connected by query time. + // Slow servers populate for turn 2+. Matches interactive-no-prompt + // behavior. Print mode: per-server push into headlessStore (below). + const hookMessages: Awaited> = []; + // Suppress transient unhandledRejection — the prefetch warms the + // memoized connectToServer cache but nobody awaits it in interactive. + mcpPromise.catch(() => {}); + const mcpClients: Awaited['clients'] = []; + const mcpTools: Awaited['tools'] = []; + const mcpCommands: Awaited['commands'] = []; + let thinkingEnabled = shouldEnableThinkingByDefault(); + let thinkingConfig: ThinkingConfig = thinkingEnabled !== false ? { + type: 'adaptive' + } : { + type: 'disabled' + }; + if (options.thinking === 'adaptive' || options.thinking === 'enabled') { + thinkingEnabled = true; + thinkingConfig = { + type: 'adaptive' + }; + } else if (options.thinking === 'disabled') { + thinkingEnabled = false; + thinkingConfig = { + type: 'disabled' + }; + } else { + const maxThinkingTokens = process.env.MAX_THINKING_TOKENS ? parseInt(process.env.MAX_THINKING_TOKENS, 10) : options.maxThinkingTokens; + if (maxThinkingTokens !== undefined) { + if (maxThinkingTokens > 0) { + thinkingEnabled = true; + thinkingConfig = { + type: 'enabled', + budgetTokens: maxThinkingTokens + }; + } else if (maxThinkingTokens === 0) { + thinkingEnabled = false; + thinkingConfig = { + type: 'disabled' + }; + } + } + } + logForDiagnosticsNoPII('info', 'started', { + version: MACRO.VERSION, + is_native_binary: isInBundledMode() + }); + registerCleanup(async () => { + logForDiagnosticsNoPII('info', 'exited'); + }); + void logTenguInit({ + hasInitialPrompt: Boolean(prompt), + hasStdin: Boolean(inputPrompt), + verbose, + debug, + debugToStderr, + print: print ?? false, + outputFormat: outputFormat ?? 'text', + inputFormat: inputFormat ?? 'text', + numAllowedTools: allowedTools.length, + numDisallowedTools: disallowedTools.length, + mcpClientCount: Object.keys(allMcpConfigs).length, + worktreeEnabled, + skipWebFetchPreflight: getInitialSettings().skipWebFetchPreflight, + githubActionInputs: process.env.GITHUB_ACTION_INPUTS, + dangerouslySkipPermissionsPassed: dangerouslySkipPermissions ?? false, + permissionMode, + modeIsBypass: permissionMode === 'bypassPermissions', + allowDangerouslySkipPermissionsPassed: allowDangerouslySkipPermissions, + systemPromptFlag: systemPrompt ? options.systemPromptFile ? 'file' : 'flag' : undefined, + appendSystemPromptFlag: appendSystemPrompt ? options.appendSystemPromptFile ? 'file' : 'flag' : undefined, + thinkingConfig, + assistantActivationPath: feature('KAIROS') && kairosEnabled ? assistantModule?.getAssistantActivationPath() : undefined + }); + + // Log context metrics once at initialization + void logContextMetrics(regularMcpConfigs, toolPermissionContext); + void logPermissionContextForAnts(null, 'initialization'); + logManagedSettings(); + + // Register PID file for concurrent-session detection (~/.claude/sessions/) + // and fire multi-clauding telemetry. Lives here (not init.ts) so only the + // REPL path registers — not subcommands like `claude doctor`. Chained: + // count must run after register's write completes or it misses our own file. + void registerSession().then(registered => { + if (!registered) return; + if (sessionNameArg) { + void updateSessionName(sessionNameArg); + } + void countConcurrentSessions().then(count => { + if (count >= 2) { + logEvent('tengu_concurrent_sessions', { + num_sessions: count + }); + } + }); + }); + + // Initialize versioned plugins system (triggers V1→V2 migration if + // needed). Then run orphan GC, THEN warm the Grep/Glob exclusion cache. + // Sequencing matters: the warmup scans disk for .orphaned_at markers, + // so it must see the GC's Pass 1 (remove markers from reinstalled + // versions) and Pass 2 (stamp unmarked orphans) already applied. The + // warm also lands before autoupdate (fires on first submit in REPL) + // can orphan this session's active version underneath us. + // --bare / SIMPLE: skip plugin version sync + orphan cleanup. These + // are install/upgrade bookkeeping that scripted calls don't need — + // the next interactive session will reconcile. The await here was + // blocking -p on a marketplace round-trip. + if (isBareMode()) { + // skip — no-op + } else if (isNonInteractiveSession) { + // In headless mode, await to ensure plugin sync completes before CLI exits + await initializeVersionedPlugins(); + profileCheckpoint('action_after_plugins_init'); + void cleanupOrphanedPluginVersionsInBackground().then(() => getGlobExclusionsForPluginCache()); + } else { + // In interactive mode, fire-and-forget — this is purely bookkeeping + // that doesn't affect runtime behavior of the current session + void initializeVersionedPlugins().then(async () => { + profileCheckpoint('action_after_plugins_init'); + await cleanupOrphanedPluginVersionsInBackground(); + void getGlobExclusionsForPluginCache(); + }); + } + const setupTrigger = initOnly || init ? 'init' : maintenance ? 'maintenance' : null; + if (initOnly) { + applyConfigEnvironmentVariables(); + await processSetupHooks('init', { + forceSyncExecution: true + }); + await processSessionStartHooks('startup', { + forceSyncExecution: true + }); + gracefulShutdownSync(0); + return; + } + + // --print mode + if (isNonInteractiveSession) { + if (outputFormat === 'stream-json' || outputFormat === 'json') { + setHasFormattedOutput(true); + } + + // Apply full environment variables in print mode since trust dialog is bypassed + // This includes potentially dangerous environment variables from untrusted sources + // but print mode is considered trusted (as documented in help text) + applyConfigEnvironmentVariables(); + + // Initialize telemetry after env vars are applied so OTEL endpoint env vars and + // otelHeadersHelper (which requires trust to execute) are available. + initializeTelemetryAfterTrust(); + + // Kick SessionStart hooks now so the subprocess spawn overlaps with + // MCP connect + plugin init + print.ts import below. loadInitialMessages + // joins this at print.ts:4397. Guarded same as loadInitialMessages — + // continue/resume/teleport paths don't fire startup hooks (or fire them + // conditionally inside the resume branch, where this promise is + // undefined and the ?? fallback runs). Also skip when setupTrigger is + // set — those paths run setup hooks first (print.ts:544), and session + // start hooks must wait until setup completes. + const sessionStartHooksPromise = options.continue || options.resume || teleport || setupTrigger ? undefined : processSessionStartHooks('startup'); + // Suppress transient unhandledRejection if this rejects before + // loadInitialMessages awaits it. Downstream await still observes the + // rejection — this just prevents the spurious global handler fire. + sessionStartHooksPromise?.catch(() => {}); + profileCheckpoint('before_validateForceLoginOrg'); + // Validate org restriction for non-interactive sessions + const orgValidation = await validateForceLoginOrg(); + if (!orgValidation.valid) { + process.stderr.write(orgValidation.message + '\n'); + process.exit(1); + } + + // Headless mode supports all prompt commands and some local commands + // If disableSlashCommands is true, return empty array + const commandsHeadless = disableSlashCommands ? [] : commands.filter(command => command.type === 'prompt' && !command.disableNonInteractive || command.type === 'local' && command.supportsNonInteractive); + const defaultState = getDefaultAppState(); + const headlessInitialState: AppState = { + ...defaultState, + mcp: { + ...defaultState.mcp, + clients: mcpClients, + commands: mcpCommands, + tools: mcpTools + }, + toolPermissionContext, + effortValue: parseEffortValue(options.effort) ?? getInitialEffortSetting(), + ...(isFastModeEnabled() && { + fastMode: getInitialFastModeSetting(effectiveModel ?? null) + }), + ...(isAdvisorEnabled() && advisorModel && { + advisorModel + }), + // kairosEnabled gates the async fire-and-forget path in + // executeForkedSlashCommand (processSlashCommand.tsx:132) and + // AgentTool's shouldRunAsync. The REPL initialState sets this at + // ~3459; headless was defaulting to false, so the daemon child's + // scheduled tasks and Agent-tool calls ran synchronously — N + // overdue cron tasks on spawn = N serial subagent turns blocking + // user input. Computed at :1620, well before this branch. + ...(feature('KAIROS') ? { + kairosEnabled + } : {}) + }; + + // Init app state + const headlessStore = createStore(headlessInitialState, onChangeAppState); + + // Check if bypassPermissions should be disabled based on Statsig gate + // This runs in parallel to the code below, to avoid blocking the main loop. + if (toolPermissionContext.mode === 'bypassPermissions' || allowDangerouslySkipPermissions) { + void checkAndDisableBypassPermissions(toolPermissionContext); + } + + // Async check of auto mode gate — corrects state and disables auto if needed. + // Gated on TRANSCRIPT_CLASSIFIER (not USER_TYPE) so GrowthBook kill switch runs for external builds too. + if (feature('TRANSCRIPT_CLASSIFIER')) { + void verifyAutoModeGateAccess(toolPermissionContext, headlessStore.getState().fastMode).then(({ + updateContext + }) => { + headlessStore.setState(prev => { + const nextCtx = updateContext(prev.toolPermissionContext); + if (nextCtx === prev.toolPermissionContext) return prev; + return { + ...prev, + toolPermissionContext: nextCtx + }; + }); + }); + } + + // Set global state for session persistence + if (options.sessionPersistence === false) { + setSessionPersistenceDisabled(true); + } + + // Store SDK betas in global state for context window calculation + // Only store allowed betas (filters by allowlist and subscriber status) + setSdkBetas(filterAllowedSdkBetas(betas)); + + // Print-mode MCP: per-server incremental push into headlessStore. + // Mirrors useManageMCPConnections — push pending first (so ToolSearch's + // pending-check at ToolSearchTool.ts:334 sees them), then replace with + // connected/failed as each server settles. + const connectMcpBatch = (configs: Record, label: string): Promise => { + if (Object.keys(configs).length === 0) return Promise.resolve(); + headlessStore.setState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + clients: [...prev.mcp.clients, ...Object.entries(configs).map(([name, config]) => ({ + name, + type: 'pending' as const, + config + }))] + } + })); + return getMcpToolsCommandsAndResources(({ + client, + tools, + commands + }) => { + headlessStore.setState(prev => ({ + ...prev, + mcp: { + ...prev.mcp, + clients: prev.mcp.clients.some(c => c.name === client.name) ? prev.mcp.clients.map(c => c.name === client.name ? client : c) : [...prev.mcp.clients, client], + tools: uniqBy([...prev.mcp.tools, ...tools], 'name'), + commands: uniqBy([...prev.mcp.commands, ...commands], 'name') + } + })); + }, configs).catch(err => logForDebugging(`[MCP] ${label} connect error: ${err}`)); + }; + // Await all MCP configs — print mode is often single-turn, so + // "late-connecting servers visible next turn" doesn't help. SDK init + // message and turn-1 tool list both need configured MCP tools present. + // Zero-server case is free via the early return in connectMcpBatch. + // Connectors parallelize inside getMcpToolsCommandsAndResources + // (processBatched with Promise.all). claude.ai is awaited too — its + // fetch was kicked off early (line ~2558) so only residual time blocks + // here. --bare skips claude.ai entirely for perf-sensitive scripts. + profileCheckpoint('before_connectMcp'); + await connectMcpBatch(regularMcpConfigs, 'regular'); + profileCheckpoint('after_connectMcp'); + // Dedup: suppress plugin MCP servers that duplicate a claude.ai + // connector (connector wins), then connect claude.ai servers. + // Bounded wait — #23725 made this blocking so single-turn -p sees + // connectors, but with 40+ slow connectors tengu_startup_perf p99 + // climbed to 76s. If fetch+connect doesn't finish in time, proceed; + // the promise keeps running and updates headlessStore in the + // background so turn 2+ still sees connectors. + const CLAUDE_AI_MCP_TIMEOUT_MS = 5_000; + const claudeaiConnect = claudeaiConfigPromise.then(claudeaiConfigs => { + if (Object.keys(claudeaiConfigs).length > 0) { + const claudeaiSigs = new Set(); + for (const config of Object.values(claudeaiConfigs)) { + const sig = getMcpServerSignature(config); + if (sig) claudeaiSigs.add(sig); + } + const suppressed = new Set(); + for (const [name, config] of Object.entries(regularMcpConfigs)) { + if (!name.startsWith('plugin:')) continue; + const sig = getMcpServerSignature(config); + if (sig && claudeaiSigs.has(sig)) suppressed.add(name); + } + if (suppressed.size > 0) { + logForDebugging(`[MCP] Lazy dedup: suppressing ${suppressed.size} plugin server(s) that duplicate claude.ai connectors: ${[...suppressed].join(', ')}`); + // Disconnect before filtering from state. Only connected + // servers need cleanup — clearServerCache on a never-connected + // server triggers a real connect just to kill it (memoize + // cache-miss path, see useManageMCPConnections.ts:870). + for (const c of headlessStore.getState().mcp.clients) { + if (!suppressed.has(c.name) || c.type !== 'connected') continue; + c.client.onclose = undefined; + void clearServerCache(c.name, c.config).catch(() => {}); + } + headlessStore.setState(prev => { + let { + clients, + tools, + commands, + resources + } = prev.mcp; + clients = clients.filter(c => !suppressed.has(c.name)); + tools = tools.filter(t => !t.mcpInfo || !suppressed.has(t.mcpInfo.serverName)); + for (const name of suppressed) { + commands = excludeCommandsByServer(commands, name); + resources = excludeResourcesByServer(resources, name); + } + return { + ...prev, + mcp: { + ...prev.mcp, + clients, + tools, + commands, + resources + } + }; + }); + } + } + // Suppress claude.ai connectors that duplicate an enabled + // manual server (URL-signature match). Plugin dedup above only + // handles `plugin:*` keys; this catches manual `.mcp.json` entries. + // plugin:* must be excluded here — step 1 already suppressed + // those (claude.ai wins); leaving them in suppresses the + // connector too, and neither survives (gh-39974). + const nonPluginConfigs = pickBy(regularMcpConfigs, (_, n) => !n.startsWith('plugin:')); + const { + servers: dedupedClaudeAi + } = dedupClaudeAiMcpServers(claudeaiConfigs, nonPluginConfigs); + return connectMcpBatch(dedupedClaudeAi, 'claudeai'); + }); + let claudeaiTimer: ReturnType | undefined; + const claudeaiTimedOut = await Promise.race([claudeaiConnect.then(() => false), new Promise(resolve => { + claudeaiTimer = setTimeout(r => r(true), CLAUDE_AI_MCP_TIMEOUT_MS, resolve); + })]); + if (claudeaiTimer) clearTimeout(claudeaiTimer); + if (claudeaiTimedOut) { + logForDebugging(`[MCP] claude.ai connectors not ready after ${CLAUDE_AI_MCP_TIMEOUT_MS}ms — proceeding; background connection continues`); + } + profileCheckpoint('after_connectMcp_claudeai'); + + // In headless mode, start deferred prefetches immediately (no user typing delay) + // --bare / SIMPLE: startDeferredPrefetches early-returns internally. + // backgroundHousekeeping (initExtractMemories, pruneShellSnapshots, + // cleanupOldMessageFiles) and sdkHeapDumpMonitor are all bookkeeping + // that scripted calls don't need — the next interactive session reconciles. + if (!isBareMode()) { + startDeferredPrefetches(); + void import('./utils/backgroundHousekeeping.js').then(m => m.startBackgroundHousekeeping()); + if ("external" === 'ant') { + void import('./utils/sdkHeapDumpMonitor.js').then(m => m.startSdkMemoryMonitor()); + } + } + logSessionTelemetry(); + profileCheckpoint('before_print_import'); + const { + runHeadless + } = await import('src/cli/print.js'); + profileCheckpoint('after_print_import'); + void runHeadless(inputPrompt, () => headlessStore.getState(), headlessStore.setState, commandsHeadless, tools, sdkMcpConfigs, agentDefinitions.activeAgents, { + continue: options.continue, + resume: options.resume, + verbose: verbose, + outputFormat: outputFormat, + jsonSchema, + permissionPromptToolName: options.permissionPromptTool, + allowedTools, + thinkingConfig, + maxTurns: options.maxTurns, + maxBudgetUsd: options.maxBudgetUsd, + taskBudget: options.taskBudget ? { + total: options.taskBudget + } : undefined, + systemPrompt, + appendSystemPrompt, + userSpecifiedModel: effectiveModel, + fallbackModel: userSpecifiedFallbackModel, + teleport, + sdkUrl, + replayUserMessages: effectiveReplayUserMessages, + includePartialMessages: effectiveIncludePartialMessages, + forkSession: options.forkSession || false, + resumeSessionAt: options.resumeSessionAt || undefined, + rewindFiles: options.rewindFiles, + enableAuthStatus: options.enableAuthStatus, + agent: agentCli, + workload: options.workload, + setupTrigger: setupTrigger ?? undefined, + sessionStartHooksPromise + }); + return; + } + + // Log model config at startup + logEvent('tengu_startup_manual_model_config', { + cli_flag: options.model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + env_var: process.env.ANTHROPIC_MODEL as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + settings_file: (getInitialSettings() || {}).model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + subscriptionType: getSubscriptionType() as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + agent: agentSetting as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + + // Get deprecation warning for the initial model (resolvedInitialModel computed earlier for hooks parallelization) + const deprecationWarning = getModelDeprecationWarning(resolvedInitialModel); + + // Build initial notification queue + const initialNotifications: Array<{ + key: string; + text: string; + color?: 'warning'; + priority: 'high'; + }> = []; + if (permissionModeNotification) { + initialNotifications.push({ + key: 'permission-mode-notification', + text: permissionModeNotification, + priority: 'high' + }); + } + if (deprecationWarning) { + initialNotifications.push({ + key: 'model-deprecation-warning', + text: deprecationWarning, + color: 'warning', + priority: 'high' + }); + } + if (overlyBroadBashPermissions.length > 0) { + const displayList = uniq(overlyBroadBashPermissions.map(p => p.ruleDisplay)); + const displays = displayList.join(', '); + const sources = uniq(overlyBroadBashPermissions.map(p => p.sourceDisplay)).join(', '); + const n = displayList.length; + initialNotifications.push({ + key: 'overly-broad-bash-notification', + text: `${displays} allow ${plural(n, 'rule')} from ${sources} ${plural(n, 'was', 'were')} ignored \u2014 not available for Ants, please use auto-mode instead`, + color: 'warning', + priority: 'high' + }); + } + const effectiveToolPermissionContext = { + ...toolPermissionContext, + mode: isAgentSwarmsEnabled() && getTeammateUtils().isPlanModeRequired() ? 'plan' as const : toolPermissionContext.mode + }; + // All startup opt-in paths (--tools, --brief, defaultView) have fired + // above; initialIsBriefOnly just reads the resulting state. + const initialIsBriefOnly = feature('KAIROS') || feature('KAIROS_BRIEF') ? getUserMsgOptIn() : false; + const fullRemoteControl = remoteControl || getRemoteControlAtStartup() || kairosEnabled; + let ccrMirrorEnabled = false; + if (feature('CCR_MIRROR') && !fullRemoteControl) { + /* eslint-disable @typescript-eslint/no-require-imports */ + const { + isCcrMirrorEnabled + } = require('./bridge/bridgeEnabled.js') as typeof import('./bridge/bridgeEnabled.js'); + /* eslint-enable @typescript-eslint/no-require-imports */ + ccrMirrorEnabled = isCcrMirrorEnabled(); + } + const initialState: AppState = { + settings: getInitialSettings(), + tasks: {}, + agentNameRegistry: new Map(), + verbose: verbose ?? getGlobalConfig().verbose ?? false, + mainLoopModel: initialMainLoopModel, + mainLoopModelForSession: null, + isBriefOnly: initialIsBriefOnly, + expandedView: getGlobalConfig().showSpinnerTree ? 'teammates' : getGlobalConfig().showExpandedTodos ? 'tasks' : 'none', + showTeammateMessagePreview: isAgentSwarmsEnabled() ? false : undefined, + selectedIPAgentIndex: -1, + coordinatorTaskIndex: -1, + viewSelectionMode: 'none', + footerSelection: null, + toolPermissionContext: effectiveToolPermissionContext, + agent: mainThreadAgentDefinition?.agentType, + agentDefinitions, + mcp: { + clients: [], + tools: [], + commands: [], + resources: {}, + pluginReconnectKey: 0 + }, + plugins: { + enabled: [], + disabled: [], + commands: [], + errors: [], + installationStatus: { + marketplaces: [], + plugins: [] + }, + needsRefresh: false + }, + statusLineText: undefined, + kairosEnabled, + remoteSessionUrl: undefined, + remoteConnectionStatus: 'connecting', + remoteBackgroundTaskCount: 0, + replBridgeEnabled: fullRemoteControl || ccrMirrorEnabled, + replBridgeExplicit: remoteControl, + replBridgeOutboundOnly: ccrMirrorEnabled, + replBridgeConnected: false, + replBridgeSessionActive: false, + replBridgeReconnecting: false, + replBridgeConnectUrl: undefined, + replBridgeSessionUrl: undefined, + replBridgeEnvironmentId: undefined, + replBridgeSessionId: undefined, + replBridgeError: undefined, + replBridgeInitialName: remoteControlName, + showRemoteCallout: false, + notifications: { + current: null, + queue: initialNotifications + }, + elicitation: { + queue: [] + }, + todos: {}, + remoteAgentTaskSuggestions: [], + fileHistory: { + snapshots: [], + trackedFiles: new Set(), + snapshotSequence: 0 + }, + attribution: createEmptyAttributionState(), + thinkingEnabled, + promptSuggestionEnabled: shouldEnablePromptSuggestion(), + sessionHooks: new Map(), + inbox: { + messages: [] + }, + promptSuggestion: { + text: null, + promptId: null, + shownAt: 0, + acceptedAt: 0, + generationRequestId: null + }, + speculation: IDLE_SPECULATION_STATE, + speculationSessionTimeSavedMs: 0, + skillImprovement: { + suggestion: null + }, + workerSandboxPermissions: { + queue: [], + selectedIndex: 0 + }, + pendingWorkerRequest: null, + pendingSandboxRequest: null, + authVersion: 0, + initialMessage: inputPrompt ? { + message: createUserMessage({ + content: String(inputPrompt) + }) + } : null, + effortValue: parseEffortValue(options.effort) ?? getInitialEffortSetting(), + activeOverlays: new Set(), + fastMode: getInitialFastModeSetting(resolvedInitialModel), + ...(isAdvisorEnabled() && advisorModel && { + advisorModel + }), + // Compute teamContext synchronously to avoid useEffect setState during render. + // KAIROS: assistantTeamContext takes precedence — set earlier in the + // KAIROS block so Agent(name: "foo") can spawn in-process teammates + // without TeamCreate. computeInitialTeamContext() is for tmux-spawned + // teammates reading their own identity, not the assistant-mode leader. + teamContext: feature('KAIROS') ? assistantTeamContext ?? computeInitialTeamContext?.() : computeInitialTeamContext?.() + }; + + // Add CLI initial prompt to history + if (inputPrompt) { + addToHistory(String(inputPrompt)); + } + const initialTools = mcpTools; + + // Increment numStartups synchronously — first-render readers like + // shouldShowEffortCallout (via useState initializer) need the updated + // value before setImmediate fires. Defer only telemetry. + saveGlobalConfig(current => ({ + ...current, + numStartups: (current.numStartups ?? 0) + 1 + })); + setImmediate(() => { + void logStartupTelemetry(); + logSessionTelemetry(); + }); + + // Set up per-turn session environment data uploader (ant-only build). + // Default-enabled for all ant users when working in an Anthropic-owned + // repo. Captures git/filesystem state (NOT transcripts) at each turn so + // environments can be recreated at any user message index. Gating: + // - Build-time: this import is stubbed in external builds. + // - Runtime: uploader checks github.com/anthropics/* remote + gcloud auth. + // - Safety: CLAUDE_CODE_DISABLE_SESSION_DATA_UPLOAD=1 bypasses (tests set this). + // Import is dynamic + async to avoid adding startup latency. + const sessionUploaderPromise = "external" === 'ant' ? import('./utils/sessionDataUploader.js') : null; + + // Defer session uploader resolution to the onTurnComplete callback to avoid + // adding a new top-level await in main.tsx (performance-critical path). + // The per-turn auth logic in sessionDataUploader.ts handles unauthenticated + // state gracefully (re-checks each turn, so auth recovery mid-session works). + const uploaderReady = sessionUploaderPromise ? sessionUploaderPromise.then(mod => mod.createSessionTurnUploader()).catch(() => null) : null; + const sessionConfig = { + debug: debug || debugToStderr, + commands: [...commands, ...mcpCommands], + initialTools, + mcpClients, + autoConnectIdeFlag: ide, + mainThreadAgentDefinition, + disableSlashCommands, + dynamicMcpConfig, + strictMcpConfig, + systemPrompt, + appendSystemPrompt, + taskListId, + thinkingConfig, + ...(uploaderReady && { + onTurnComplete: (messages: MessageType[]) => { + void uploaderReady.then(uploader => uploader?.(messages)); + } + }) + }; + + // Shared context for processResumedConversation calls + const resumeContext = { + modeApi: coordinatorModeModule, + mainThreadAgentDefinition, + agentDefinitions, + currentCwd, + cliAgents, + initialState + }; + if (options.continue) { + // Continue the most recent conversation directly + let resumeSucceeded = false; + try { + const resumeStart = performance.now(); + + // Clear stale caches before resuming to ensure fresh file/skill discovery + const { + clearSessionCaches + } = await import('./commands/clear/caches.js'); + clearSessionCaches(); + const result = await loadConversationForResume(undefined /* sessionId */, undefined /* sourceFile */); + if (!result) { + logEvent('tengu_continue', { + success: false + }); + return await exitWithError(root, 'No conversation found to continue'); + } + const loaded = await processResumedConversation(result, { + forkSession: !!options.forkSession, + includeAttribution: true, + transcriptPath: result.fullPath + }, resumeContext); + if (loaded.restoredAgentDef) { + mainThreadAgentDefinition = loaded.restoredAgentDef; + } + maybeActivateProactive(options); + maybeActivateBrief(options); + logEvent('tengu_continue', { + success: true, + resume_duration_ms: Math.round(performance.now() - resumeStart) + }); + resumeSucceeded = true; + await launchRepl(root, { + getFpsMetrics, + stats, + initialState: loaded.initialState + }, { + ...sessionConfig, + mainThreadAgentDefinition: loaded.restoredAgentDef ?? mainThreadAgentDefinition, + initialMessages: loaded.messages, + initialFileHistorySnapshots: loaded.fileHistorySnapshots, + initialContentReplacements: loaded.contentReplacements, + initialAgentName: loaded.agentName, + initialAgentColor: loaded.agentColor + }, renderAndRun); + } catch (error) { + if (!resumeSucceeded) { + logEvent('tengu_continue', { + success: false + }); + } + logError(error); + process.exit(1); + } + } else if (feature('DIRECT_CONNECT') && _pendingConnect?.url) { + // `claude connect ` — full interactive TUI connected to a remote server + let directConnectConfig; + try { + const session = await createDirectConnectSession({ + serverUrl: _pendingConnect.url, + authToken: _pendingConnect.authToken, + cwd: getOriginalCwd(), + dangerouslySkipPermissions: _pendingConnect.dangerouslySkipPermissions + }); + if (session.workDir) { + setOriginalCwd(session.workDir); + setCwdState(session.workDir); + } + setDirectConnectServerUrl(_pendingConnect.url); + directConnectConfig = session.config; + } catch (err) { + return await exitWithError(root, err instanceof DirectConnectError ? err.message : String(err), () => gracefulShutdown(1)); + } + const connectInfoMessage = createSystemMessage(`Connected to server at ${_pendingConnect.url}\nSession: ${directConnectConfig.sessionId}`, 'info'); + await launchRepl(root, { + getFpsMetrics, + stats, + initialState + }, { + debug: debug || debugToStderr, + commands, + initialTools: [], + initialMessages: [connectInfoMessage], + mcpClients: [], + autoConnectIdeFlag: ide, + mainThreadAgentDefinition, + disableSlashCommands, + directConnectConfig, + thinkingConfig + }, renderAndRun); + return; + } else if (feature('SSH_REMOTE') && _pendingSSH?.host) { + // `claude ssh [dir]` — probe remote, deploy binary if needed, + // spawn ssh with unix-socket -R forward to a local auth proxy, hand + // the REPL an SSHSession. Tools run remotely, UI renders locally. + // `--local` skips probe/deploy/ssh and spawns the current binary + // directly with the same env — e2e test of the proxy/auth plumbing. + const { + createSSHSession, + createLocalSSHSession, + SSHSessionError + } = await import('./ssh/createSSHSession.js'); + let sshSession; + try { + if (_pendingSSH.local) { + process.stderr.write('Starting local ssh-proxy test session...\n'); + sshSession = createLocalSSHSession({ + cwd: _pendingSSH.cwd, + permissionMode: _pendingSSH.permissionMode, + dangerouslySkipPermissions: _pendingSSH.dangerouslySkipPermissions + }); + } else { + process.stderr.write(`Connecting to ${_pendingSSH.host}…\n`); + // In-place progress: \r + EL0 (erase to end of line). Final \n on + // success so the next message lands on a fresh line. No-op when + // stderr isn't a TTY (piped/redirected) — \r would just emit noise. + const isTTY = process.stderr.isTTY; + let hadProgress = false; + sshSession = await createSSHSession({ + host: _pendingSSH.host, + cwd: _pendingSSH.cwd, + localVersion: MACRO.VERSION, + permissionMode: _pendingSSH.permissionMode, + dangerouslySkipPermissions: _pendingSSH.dangerouslySkipPermissions, + extraCliArgs: _pendingSSH.extraCliArgs + }, isTTY ? { + onProgress: msg => { + hadProgress = true; + process.stderr.write(`\r ${msg}\x1b[K`); + } + } : {}); + if (hadProgress) process.stderr.write('\n'); + } + setOriginalCwd(sshSession.remoteCwd); + setCwdState(sshSession.remoteCwd); + setDirectConnectServerUrl(_pendingSSH.local ? 'local' : _pendingSSH.host); + } catch (err) { + return await exitWithError(root, err instanceof SSHSessionError ? err.message : String(err), () => gracefulShutdown(1)); + } + const sshInfoMessage = createSystemMessage(_pendingSSH.local ? `Local ssh-proxy test session\ncwd: ${sshSession.remoteCwd}\nAuth: unix socket → local proxy` : `SSH session to ${_pendingSSH.host}\nRemote cwd: ${sshSession.remoteCwd}\nAuth: unix socket -R → local proxy`, 'info'); + await launchRepl(root, { + getFpsMetrics, + stats, + initialState + }, { + debug: debug || debugToStderr, + commands, + initialTools: [], + initialMessages: [sshInfoMessage], + mcpClients: [], + autoConnectIdeFlag: ide, + mainThreadAgentDefinition, + disableSlashCommands, + sshSession, + thinkingConfig + }, renderAndRun); + return; + } else if (feature('KAIROS') && _pendingAssistantChat && (_pendingAssistantChat.sessionId || _pendingAssistantChat.discover)) { + // `claude assistant [sessionId]` — REPL as a pure viewer client + // of a remote assistant session. The agentic loop runs remotely; this + // process streams live events and POSTs messages. History is lazy- + // loaded by useAssistantHistory on scroll-up (no blocking fetch here). + const { + discoverAssistantSessions + } = await import('./assistant/sessionDiscovery.js'); + let targetSessionId = _pendingAssistantChat.sessionId; + + // Discovery flow — list bridge environments, filter sessions + if (!targetSessionId) { + let sessions; + try { + sessions = await discoverAssistantSessions(); + } catch (e) { + return await exitWithError(root, `Failed to discover sessions: ${e instanceof Error ? e.message : e}`, () => gracefulShutdown(1)); + } + if (sessions.length === 0) { + let installedDir: string | null; + try { + installedDir = await launchAssistantInstallWizard(root); + } catch (e) { + return await exitWithError(root, `Assistant installation failed: ${e instanceof Error ? e.message : e}`, () => gracefulShutdown(1)); + } + if (installedDir === null) { + await gracefulShutdown(0); + process.exit(0); + } + // The daemon needs a few seconds to spin up its worker and + // establish a bridge session before discovery will find it. + return await exitWithMessage(root, `Assistant installed in ${installedDir}. The daemon is starting up — run \`claude assistant\` again in a few seconds to connect.`, { + exitCode: 0, + beforeExit: () => gracefulShutdown(0) + }); + } + if (sessions.length === 1) { + targetSessionId = sessions[0]!.id; + } else { + const picked = await launchAssistantSessionChooser(root, { + sessions + }); + if (!picked) { + await gracefulShutdown(0); + process.exit(0); + } + targetSessionId = picked; + } + } + + // Auth — call prepareApiRequest() once for orgUUID, but use a + // getAccessToken closure for the token so reconnects get fresh tokens. + const { + checkAndRefreshOAuthTokenIfNeeded, + getClaudeAIOAuthTokens + } = await import('./utils/auth.js'); + await checkAndRefreshOAuthTokenIfNeeded(); + let apiCreds; + try { + apiCreds = await prepareApiRequest(); + } catch (e) { + return await exitWithError(root, `Error: ${e instanceof Error ? e.message : 'Failed to authenticate'}`, () => gracefulShutdown(1)); + } + const getAccessToken = (): string => getClaudeAIOAuthTokens()?.accessToken ?? apiCreds.accessToken; + + // Brief mode activation: setKairosActive(true) satisfies BOTH opt-in + // and entitlement for isBriefEnabled() (BriefTool.ts:124-132). + setKairosActive(true); + setUserMsgOptIn(true); + setIsRemoteMode(true); + const remoteSessionConfig = createRemoteSessionConfig(targetSessionId, getAccessToken, apiCreds.orgUUID, /* hasInitialPrompt */false, /* viewerOnly */true); + const infoMessage = createSystemMessage(`Attached to assistant session ${targetSessionId.slice(0, 8)}…`, 'info'); + const assistantInitialState: AppState = { + ...initialState, + isBriefOnly: true, + kairosEnabled: false, + replBridgeEnabled: false + }; + const remoteCommands = filterCommandsForRemoteMode(commands); + await launchRepl(root, { + getFpsMetrics, + stats, + initialState: assistantInitialState + }, { + debug: debug || debugToStderr, + commands: remoteCommands, + initialTools: [], + initialMessages: [infoMessage], + mcpClients: [], + autoConnectIdeFlag: ide, + mainThreadAgentDefinition, + disableSlashCommands, + remoteSessionConfig, + thinkingConfig + }, renderAndRun); + return; + } else if (options.resume || options.fromPr || teleport || remote !== null) { + // Handle resume flow - from file (ant-only), session ID, or interactive selector + + // Clear stale caches before resuming to ensure fresh file/skill discovery + const { + clearSessionCaches + } = await import('./commands/clear/caches.js'); + clearSessionCaches(); + let messages: MessageType[] | null = null; + let processedResume: ProcessedResume | undefined = undefined; + let maybeSessionId = validateUuid(options.resume); + let searchTerm: string | undefined = undefined; + // Store full LogOption when found by custom title (for cross-worktree resume) + let matchedLog: LogOption | null = null; + // PR filter for --from-pr flag + let filterByPr: boolean | number | string | undefined = undefined; + + // Handle --from-pr flag + if (options.fromPr) { + if (options.fromPr === true) { + // Show all sessions with linked PRs + filterByPr = true; + } else if (typeof options.fromPr === 'string') { + // Could be a PR number or URL + filterByPr = options.fromPr; + } + } + + // If resume value is not a UUID, try exact match by custom title first + if (options.resume && typeof options.resume === 'string' && !maybeSessionId) { + const trimmedValue = options.resume.trim(); + if (trimmedValue) { + const matches = await searchSessionsByCustomTitle(trimmedValue, { + exact: true + }); + if (matches.length === 1) { + // Exact match found - store full LogOption for cross-worktree resume + matchedLog = matches[0]!; + maybeSessionId = getSessionIdFromLog(matchedLog) ?? null; + } else { + // No match or multiple matches - use as search term for picker + searchTerm = trimmedValue; + } + } + } + + // --remote and --teleport both create/resume Claude Code Web (CCR) sessions. + // Remote Control (--rc) is a separate feature gated in initReplBridge.ts. + if (remote !== null || teleport) { + await waitForPolicyLimitsToLoad(); + if (!isPolicyAllowed('allow_remote_sessions')) { + return await exitWithError(root, "Error: Remote sessions are disabled by your organization's policy.", () => gracefulShutdown(1)); + } + } + if (remote !== null) { + // Create remote session (optionally with initial prompt) + const hasInitialPrompt = remote.length > 0; + + // Check if TUI mode is enabled - description is only optional in TUI mode + const isRemoteTuiEnabled = getFeatureValue_CACHED_MAY_BE_STALE('tengu_remote_backend', false); + if (!isRemoteTuiEnabled && !hasInitialPrompt) { + return await exitWithError(root, 'Error: --remote requires a description.\nUsage: claude --remote "your task description"', () => gracefulShutdown(1)); + } + logEvent('tengu_remote_create_session', { + has_initial_prompt: String(hasInitialPrompt) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + + // Pass current branch so CCR clones the repo at the right revision + const currentBranch = await getBranch(); + const createdSession = await teleportToRemoteWithErrorHandling(root, hasInitialPrompt ? remote : null, new AbortController().signal, currentBranch || undefined); + if (!createdSession) { + logEvent('tengu_remote_create_session_error', { + error: 'unable_to_create_session' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + return await exitWithError(root, 'Error: Unable to create remote session', () => gracefulShutdown(1)); + } + logEvent('tengu_remote_create_session_success', { + session_id: createdSession.id as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + + // Check if new remote TUI mode is enabled via feature gate + if (!isRemoteTuiEnabled) { + // Original behavior: print session info and exit + process.stdout.write(`Created remote session: ${createdSession.title}\n`); + process.stdout.write(`View: ${getRemoteSessionUrl(createdSession.id)}?m=0\n`); + process.stdout.write(`Resume with: claude --teleport ${createdSession.id}\n`); + await gracefulShutdown(0); + process.exit(0); + } + + // New behavior: start local TUI with CCR engine + // Mark that we're in remote mode for command visibility + setIsRemoteMode(true); + switchSession(asSessionId(createdSession.id)); + + // Get OAuth credentials for remote session + let apiCreds: { + accessToken: string; + orgUUID: string; + }; + try { + apiCreds = await prepareApiRequest(); + } catch (error) { + logError(toError(error)); + return await exitWithError(root, `Error: ${errorMessage(error) || 'Failed to authenticate'}`, () => gracefulShutdown(1)); + } + + // Create remote session config for the REPL + const { + getClaudeAIOAuthTokens: getTokensForRemote + } = await import('./utils/auth.js'); + const getAccessTokenForRemote = (): string => getTokensForRemote()?.accessToken ?? apiCreds.accessToken; + const remoteSessionConfig = createRemoteSessionConfig(createdSession.id, getAccessTokenForRemote, apiCreds.orgUUID, hasInitialPrompt); + + // Add remote session info as initial system message + const remoteSessionUrl = `${getRemoteSessionUrl(createdSession.id)}?m=0`; + const remoteInfoMessage = createSystemMessage(`/remote-control is active. Code in CLI or at ${remoteSessionUrl}`, 'info'); + + // Create initial user message from the prompt if provided (CCR echoes it back but we ignore that) + const initialUserMessage = hasInitialPrompt ? createUserMessage({ + content: remote + }) : null; + + // Set remote session URL in app state for footer indicator + const remoteInitialState = { + ...initialState, + remoteSessionUrl + }; + + // Pre-filter commands to only include remote-safe ones. + // CCR's init response may further refine the list (via handleRemoteInit in REPL). + const remoteCommands = filterCommandsForRemoteMode(commands); + await launchRepl(root, { + getFpsMetrics, + stats, + initialState: remoteInitialState + }, { + debug: debug || debugToStderr, + commands: remoteCommands, + initialTools: [], + initialMessages: initialUserMessage ? [remoteInfoMessage, initialUserMessage] : [remoteInfoMessage], + mcpClients: [], + autoConnectIdeFlag: ide, + mainThreadAgentDefinition, + disableSlashCommands, + remoteSessionConfig, + thinkingConfig + }, renderAndRun); + return; + } else if (teleport) { + if (teleport === true || teleport === '') { + // Interactive mode: show task selector and handle resume + logEvent('tengu_teleport_interactive_mode', {}); + logForDebugging('selectAndResumeTeleportTask: Starting teleport flow...'); + const teleportResult = await launchTeleportResumeWrapper(root); + if (!teleportResult) { + // User cancelled or error occurred + await gracefulShutdown(0); + process.exit(0); + } + const { + branchError + } = await checkOutTeleportedSessionBranch(teleportResult.branch); + messages = processMessagesForTeleportResume(teleportResult.log, branchError); + } else if (typeof teleport === 'string') { + logEvent('tengu_teleport_resume_session', { + mode: 'direct' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + try { + // First, fetch session and validate repository before checking git state + const sessionData = await fetchSession(teleport); + const repoValidation = await validateSessionRepository(sessionData); + + // Handle repo mismatch or not in repo cases + if (repoValidation.status === 'mismatch' || repoValidation.status === 'not_in_repo') { + const sessionRepo = repoValidation.sessionRepo; + if (sessionRepo) { + // Check for known paths + const knownPaths = getKnownPathsForRepo(sessionRepo); + const existingPaths = await filterExistingPaths(knownPaths); + if (existingPaths.length > 0) { + // Show directory switch dialog + const selectedPath = await launchTeleportRepoMismatchDialog(root, { + targetRepo: sessionRepo, + initialPaths: existingPaths + }); + if (selectedPath) { + // Change to the selected directory + process.chdir(selectedPath); + setCwd(selectedPath); + setOriginalCwd(selectedPath); + } else { + // User cancelled + await gracefulShutdown(0); + } + } else { + // No known paths - show original error + throw new TeleportOperationError(`You must run claude --teleport ${teleport} from a checkout of ${sessionRepo}.`, chalk.red(`You must run claude --teleport ${teleport} from a checkout of ${chalk.bold(sessionRepo)}.\n`)); + } + } + } else if (repoValidation.status === 'error') { + throw new TeleportOperationError(repoValidation.errorMessage || 'Failed to validate session', chalk.red(`Error: ${repoValidation.errorMessage || 'Failed to validate session'}\n`)); + } + await validateGitState(); + + // Use progress UI for teleport + const { + teleportWithProgress + } = await import('./components/TeleportProgress.js'); + const result = await teleportWithProgress(root, teleport); + // Track teleported session for reliability logging + setTeleportedSessionInfo({ + sessionId: teleport + }); + messages = result.messages; + } catch (error) { + if (error instanceof TeleportOperationError) { + process.stderr.write(error.formattedMessage + '\n'); + } else { + logError(error); + process.stderr.write(chalk.red(`Error: ${errorMessage(error)}\n`)); + } + await gracefulShutdown(1); + } + } + } + if ("external" === 'ant') { + if (options.resume && typeof options.resume === 'string' && !maybeSessionId) { + // Check for ccshare URL (e.g. https://go/ccshare/boris-20260311-211036) + const { + parseCcshareId, + loadCcshare + } = await import('./utils/ccshareResume.js'); + const ccshareId = parseCcshareId(options.resume); + if (ccshareId) { + try { + const resumeStart = performance.now(); + const logOption = await loadCcshare(ccshareId); + const result = await loadConversationForResume(logOption, undefined); + if (result) { + processedResume = await processResumedConversation(result, { + forkSession: true, + transcriptPath: result.fullPath + }, resumeContext); + if (processedResume.restoredAgentDef) { + mainThreadAgentDefinition = processedResume.restoredAgentDef; + } + logEvent('tengu_session_resumed', { + entrypoint: 'ccshare' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: true, + resume_duration_ms: Math.round(performance.now() - resumeStart) + }); + } else { + logEvent('tengu_session_resumed', { + entrypoint: 'ccshare' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: false + }); + } + } catch (error) { + logEvent('tengu_session_resumed', { + entrypoint: 'ccshare' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: false + }); + logError(error); + await exitWithError(root, `Unable to resume from ccshare: ${errorMessage(error)}`, () => gracefulShutdown(1)); + } + } else { + const resolvedPath = resolve(options.resume); + try { + const resumeStart = performance.now(); + let logOption; + try { + // Attempt to load as a transcript file; ENOENT falls through to session-ID handling + logOption = await loadTranscriptFromFile(resolvedPath); + } catch (error) { + if (!isENOENT(error)) throw error; + // ENOENT: not a file path — fall through to session-ID handling + } + if (logOption) { + const result = await loadConversationForResume(logOption, undefined /* sourceFile */); + if (result) { + processedResume = await processResumedConversation(result, { + forkSession: !!options.forkSession, + transcriptPath: result.fullPath + }, resumeContext); + if (processedResume.restoredAgentDef) { + mainThreadAgentDefinition = processedResume.restoredAgentDef; + } + logEvent('tengu_session_resumed', { + entrypoint: 'file' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: true, + resume_duration_ms: Math.round(performance.now() - resumeStart) + }); + } else { + logEvent('tengu_session_resumed', { + entrypoint: 'file' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: false + }); + } + } + } catch (error) { + logEvent('tengu_session_resumed', { + entrypoint: 'file' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: false + }); + logError(error); + await exitWithError(root, `Unable to load transcript from file: ${options.resume}`, () => gracefulShutdown(1)); + } + } + } + } + + // If not loaded as a file, try as session ID + if (maybeSessionId) { + // Resume specific session by ID + const sessionId = maybeSessionId; + try { + const resumeStart = performance.now(); + // Use matchedLog if available (for cross-worktree resume by custom title) + // Otherwise fall back to sessionId string (for direct UUID resume) + const result = await loadConversationForResume(matchedLog ?? sessionId, undefined); + if (!result) { + logEvent('tengu_session_resumed', { + entrypoint: 'cli_flag' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: false + }); + return await exitWithError(root, `No conversation found with session ID: ${sessionId}`); + } + const fullPath = matchedLog?.fullPath ?? result.fullPath; + processedResume = await processResumedConversation(result, { + forkSession: !!options.forkSession, + sessionIdOverride: sessionId, + transcriptPath: fullPath + }, resumeContext); + if (processedResume.restoredAgentDef) { + mainThreadAgentDefinition = processedResume.restoredAgentDef; + } + logEvent('tengu_session_resumed', { + entrypoint: 'cli_flag' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: true, + resume_duration_ms: Math.round(performance.now() - resumeStart) + }); + } catch (error) { + logEvent('tengu_session_resumed', { + entrypoint: 'cli_flag' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + success: false + }); + logError(error); + await exitWithError(root, `Failed to resume session ${sessionId}`); + } + } + + // Await file downloads before rendering REPL (files must be available) + if (fileDownloadPromise) { + try { + const results = await fileDownloadPromise; + const failedCount = count(results, r => !r.success); + if (failedCount > 0) { + process.stderr.write(chalk.yellow(`Warning: ${failedCount}/${results.length} file(s) failed to download.\n`)); + } + } catch (error) { + return await exitWithError(root, `Error downloading files: ${errorMessage(error)}`); + } + } + + // If we have a processed resume or teleport messages, render the REPL + const resumeData = processedResume ?? (Array.isArray(messages) ? { + messages, + fileHistorySnapshots: undefined, + agentName: undefined, + agentColor: undefined as AgentColorName | undefined, + restoredAgentDef: mainThreadAgentDefinition, + initialState, + contentReplacements: undefined + } : undefined); + if (resumeData) { + maybeActivateProactive(options); + maybeActivateBrief(options); + await launchRepl(root, { + getFpsMetrics, + stats, + initialState: resumeData.initialState + }, { + ...sessionConfig, + mainThreadAgentDefinition: resumeData.restoredAgentDef ?? mainThreadAgentDefinition, + initialMessages: resumeData.messages, + initialFileHistorySnapshots: resumeData.fileHistorySnapshots, + initialContentReplacements: resumeData.contentReplacements, + initialAgentName: resumeData.agentName, + initialAgentColor: resumeData.agentColor + }, renderAndRun); + } else { + // Show interactive selector (includes same-repo worktrees) + // Note: ResumeConversation loads logs internally to ensure proper GC after selection + await launchResumeChooser(root, { + getFpsMetrics, + stats, + initialState + }, getWorktreePaths(getOriginalCwd()), { + ...sessionConfig, + initialSearchQuery: searchTerm, + forkSession: options.forkSession, + filterByPr + }); + } + } else { + // Pass unresolved hooks promise to REPL so it can render immediately + // instead of blocking ~500ms waiting for SessionStart hooks to finish. + // REPL will inject hook messages when they resolve and await them before + // the first API call so the model always sees hook context. + const pendingHookMessages = hooksPromise && hookMessages.length === 0 ? hooksPromise : undefined; + profileCheckpoint('action_after_hooks'); + maybeActivateProactive(options); + maybeActivateBrief(options); + // Persist the current mode for fresh sessions so future resumes know what mode was used + if (feature('COORDINATOR_MODE')) { + saveMode(coordinatorModeModule?.isCoordinatorMode() ? 'coordinator' : 'normal'); + } + + // If launched via a deep link, show a provenance banner so the user + // knows the session originated externally. Linux xdg-open and + // browsers with "always allow" set dispatch the link with no OS-level + // confirmation, so this is the only signal the user gets that the + // prompt — and the working directory / CLAUDE.md it implies — came + // from an external source rather than something they typed. + let deepLinkBanner: ReturnType | null = null; + if (feature('LODESTONE')) { + if (options.deepLinkOrigin) { + logEvent('tengu_deep_link_opened', { + has_prefill: Boolean(options.prefill), + has_repo: Boolean(options.deepLinkRepo) + }); + deepLinkBanner = createSystemMessage(buildDeepLinkBanner({ + cwd: getCwd(), + prefillLength: options.prefill?.length, + repo: options.deepLinkRepo, + lastFetch: options.deepLinkLastFetch !== undefined ? new Date(options.deepLinkLastFetch) : undefined + }), 'warning'); + } else if (options.prefill) { + deepLinkBanner = createSystemMessage('Launched with a pre-filled prompt — review it before pressing Enter.', 'warning'); + } + } + const initialMessages = deepLinkBanner ? [deepLinkBanner, ...hookMessages] : hookMessages.length > 0 ? hookMessages : undefined; + await launchRepl(root, { + getFpsMetrics, + stats, + initialState + }, { + ...sessionConfig, + initialMessages, + pendingHookMessages + }, renderAndRun); + } + }).version(`${MACRO.VERSION} (Claude Code)`, '-v, --version', 'Output the version number'); + + // Worktree flags + program.option('-w, --worktree [name]', 'Create a new git worktree for this session (optionally specify a name)'); + program.option('--tmux', 'Create a tmux session for the worktree (requires --worktree). Uses iTerm2 native panes when available; use --tmux=classic for traditional tmux.'); + if (canUserConfigureAdvisor()) { + program.addOption(new Option('--advisor ', 'Enable the server-side advisor tool with the specified model (alias or full ID).').hideHelp()); + } + if ("external" === 'ant') { + program.addOption(new Option('--delegate-permissions', '[ANT-ONLY] Alias for --permission-mode auto.').implies({ + permissionMode: 'auto' + })); + program.addOption(new Option('--dangerously-skip-permissions-with-classifiers', '[ANT-ONLY] Deprecated alias for --permission-mode auto.').hideHelp().implies({ + permissionMode: 'auto' + })); + program.addOption(new Option('--afk', '[ANT-ONLY] Deprecated alias for --permission-mode auto.').hideHelp().implies({ + permissionMode: 'auto' + })); + program.addOption(new Option('--tasks [id]', '[ANT-ONLY] Tasks mode: watch for tasks and auto-process them. Optional id is used as both the task list ID and agent ID (defaults to "tasklist").').argParser(String).hideHelp()); + program.option('--agent-teams', '[ANT-ONLY] Force Claude to use multi-agent mode for solving problems', () => true); + } + if (feature('TRANSCRIPT_CLASSIFIER')) { + program.addOption(new Option('--enable-auto-mode', 'Opt in to auto mode').hideHelp()); + } + if (feature('PROACTIVE') || feature('KAIROS')) { + program.addOption(new Option('--proactive', 'Start in proactive autonomous mode')); + } + if (feature('UDS_INBOX')) { + program.addOption(new Option('--messaging-socket-path ', 'Unix domain socket path for the UDS messaging server (defaults to a tmp path)')); + } + if (feature('KAIROS') || feature('KAIROS_BRIEF')) { + program.addOption(new Option('--brief', 'Enable SendUserMessage tool for agent-to-user communication')); + } + if (feature('KAIROS')) { + program.addOption(new Option('--assistant', 'Force assistant mode (Agent SDK daemon use)').hideHelp()); + } + if (feature('KAIROS') || feature('KAIROS_CHANNELS')) { + program.addOption(new Option('--channels ', 'MCP servers whose channel notifications (inbound push) should register this session. Space-separated server names.').hideHelp()); + program.addOption(new Option('--dangerously-load-development-channels ', 'Load channel servers not on the approved allowlist. For local channel development only. Shows a confirmation dialog at startup.').hideHelp()); + } + + // Teammate identity options (set by leader when spawning tmux teammates) + // These replace the CLAUDE_CODE_* environment variables + program.addOption(new Option('--agent-id ', 'Teammate agent ID').hideHelp()); + program.addOption(new Option('--agent-name ', 'Teammate display name').hideHelp()); + program.addOption(new Option('--team-name ', 'Team name for swarm coordination').hideHelp()); + program.addOption(new Option('--agent-color ', 'Teammate UI color').hideHelp()); + program.addOption(new Option('--plan-mode-required', 'Require plan mode before implementation').hideHelp()); + program.addOption(new Option('--parent-session-id ', 'Parent session ID for analytics correlation').hideHelp()); + program.addOption(new Option('--teammate-mode ', 'How to spawn teammates: "tmux", "in-process", or "auto"').choices(['auto', 'tmux', 'in-process']).hideHelp()); + program.addOption(new Option('--agent-type ', 'Custom agent type for this teammate').hideHelp()); + + // Enable SDK URL for all builds but hide from help + program.addOption(new Option('--sdk-url ', 'Use remote WebSocket endpoint for SDK I/O streaming (only with -p and stream-json format)').hideHelp()); + + // Enable teleport/remote flags for all builds but keep them undocumented until GA + program.addOption(new Option('--teleport [session]', 'Resume a teleport session, optionally specify session ID').hideHelp()); + program.addOption(new Option('--remote [description]', 'Create a remote session with the given description').hideHelp()); + if (feature('BRIDGE_MODE')) { + program.addOption(new Option('--remote-control [name]', 'Start an interactive session with Remote Control enabled (optionally named)').argParser(value => value || true).hideHelp()); + program.addOption(new Option('--rc [name]', 'Alias for --remote-control').argParser(value => value || true).hideHelp()); + } + if (feature('HARD_FAIL')) { + program.addOption(new Option('--hard-fail', 'Crash on logError calls instead of silently logging').hideHelp()); + } + profileCheckpoint('run_main_options_built'); + + // -p/--print mode: skip subcommand registration. The 52 subcommands + // (mcp, auth, plugin, skill, task, config, doctor, update, etc.) are + // never dispatched in print mode — commander routes the prompt to the + // default action. The subcommand registration path was measured at ~65ms + // on baseline — mostly the isBridgeEnabled() call (25ms settings Zod parse + // + 40ms sync keychain subprocess), both hidden by the try/catch that + // always returns false before enableConfigs(). cc:// URLs are rewritten to + // `open` at main() line ~851 BEFORE this runs, so argv check is safe here. + const isPrintMode = process.argv.includes('-p') || process.argv.includes('--print'); + const isCcUrl = process.argv.some(a => a.startsWith('cc://') || a.startsWith('cc+unix://')); + if (isPrintMode && !isCcUrl) { + profileCheckpoint('run_before_parse'); + await program.parseAsync(process.argv); + profileCheckpoint('run_after_parse'); + return program; + } + + // claude mcp + + const mcp = program.command('mcp').description('Configure and manage MCP servers').configureHelp(createSortedHelpConfig()).enablePositionalOptions(); + mcp.command('serve').description(`Start the Claude Code MCP server`).option('-d, --debug', 'Enable debug mode', () => true).option('--verbose', 'Override verbose mode setting from config', () => true).action(async ({ + debug, + verbose + }: { + debug?: boolean; + verbose?: boolean; + }) => { + const { + mcpServeHandler + } = await import('./cli/handlers/mcp.js'); + await mcpServeHandler({ + debug, + verbose + }); + }); + + // Register the mcp add subcommand (extracted for testability) + registerMcpAddCommand(mcp); + if (isXaaEnabled()) { + registerMcpXaaIdpCommand(mcp); + } + mcp.command('remove ').description('Remove an MCP server').option('-s, --scope ', 'Configuration scope (local, user, or project) - if not specified, removes from whichever scope it exists in').action(async (name: string, options: { + scope?: string; + }) => { + const { + mcpRemoveHandler + } = await import('./cli/handlers/mcp.js'); + await mcpRemoveHandler(name, options); + }); + mcp.command('list').description('List configured MCP servers. Note: The workspace trust dialog is skipped and stdio servers from .mcp.json are spawned for health checks. Only use this command in directories you trust.').action(async () => { + const { + mcpListHandler + } = await import('./cli/handlers/mcp.js'); + await mcpListHandler(); + }); + mcp.command('get ').description('Get details about an MCP server. Note: The workspace trust dialog is skipped and stdio servers from .mcp.json are spawned for health checks. Only use this command in directories you trust.').action(async (name: string) => { + const { + mcpGetHandler + } = await import('./cli/handlers/mcp.js'); + await mcpGetHandler(name); + }); + mcp.command('add-json ').description('Add an MCP server (stdio or SSE) with a JSON string').option('-s, --scope ', 'Configuration scope (local, user, or project)', 'local').option('--client-secret', 'Prompt for OAuth client secret (or set MCP_CLIENT_SECRET env var)').action(async (name: string, json: string, options: { + scope?: string; + clientSecret?: true; + }) => { + const { + mcpAddJsonHandler + } = await import('./cli/handlers/mcp.js'); + await mcpAddJsonHandler(name, json, options); + }); + mcp.command('add-from-claude-desktop').description('Import MCP servers from Claude Desktop (Mac and WSL only)').option('-s, --scope ', 'Configuration scope (local, user, or project)', 'local').action(async (options: { + scope?: string; + }) => { + const { + mcpAddFromDesktopHandler + } = await import('./cli/handlers/mcp.js'); + await mcpAddFromDesktopHandler(options); + }); + mcp.command('reset-project-choices').description('Reset all approved and rejected project-scoped (.mcp.json) servers within this project').action(async () => { + const { + mcpResetChoicesHandler + } = await import('./cli/handlers/mcp.js'); + await mcpResetChoicesHandler(); + }); + + // claude server + if (feature('DIRECT_CONNECT')) { + program.command('server').description('Start a Claude Code session server').option('--port ', 'HTTP port', '0').option('--host ', 'Bind address', '0.0.0.0').option('--auth-token ', 'Bearer token for auth').option('--unix ', 'Listen on a unix domain socket').option('--workspace ', 'Default working directory for sessions that do not specify cwd').option('--idle-timeout ', 'Idle timeout for detached sessions in ms (0 = never expire)', '600000').option('--max-sessions ', 'Maximum concurrent sessions (0 = unlimited)', '32').action(async (opts: { + port: string; + host: string; + authToken?: string; + unix?: string; + workspace?: string; + idleTimeout: string; + maxSessions: string; + }) => { + const { + randomBytes + } = await import('crypto'); + const { + startServer + } = await import('./server/server.js'); + const { + SessionManager + } = await import('./server/sessionManager.js'); + const { + DangerousBackend + } = await import('./server/backends/dangerousBackend.js'); + const { + printBanner + } = await import('./server/serverBanner.js'); + const { + createServerLogger + } = await import('./server/serverLog.js'); + const { + writeServerLock, + removeServerLock, + probeRunningServer + } = await import('./server/lockfile.js'); + const existing = await probeRunningServer(); + if (existing) { + process.stderr.write(`A claude server is already running (pid ${existing.pid}) at ${existing.httpUrl}\n`); + process.exit(1); + } + const authToken = opts.authToken ?? `sk-ant-cc-${randomBytes(16).toString('base64url')}`; + const config = { + port: parseInt(opts.port, 10), + host: opts.host, + authToken, + unix: opts.unix, + workspace: opts.workspace, + idleTimeoutMs: parseInt(opts.idleTimeout, 10), + maxSessions: parseInt(opts.maxSessions, 10) + }; + const backend = new DangerousBackend(); + const sessionManager = new SessionManager(backend, { + idleTimeoutMs: config.idleTimeoutMs, + maxSessions: config.maxSessions + }); + const logger = createServerLogger(); + const server = startServer(config, sessionManager, logger); + const actualPort = server.port ?? config.port; + printBanner(config, authToken, actualPort); + await writeServerLock({ + pid: process.pid, + port: actualPort, + host: config.host, + httpUrl: config.unix ? `unix:${config.unix}` : `http://${config.host}:${actualPort}`, + startedAt: Date.now() + }); + let shuttingDown = false; + const shutdown = async () => { + if (shuttingDown) return; + shuttingDown = true; + // Stop accepting new connections before tearing down sessions. + server.stop(true); + await sessionManager.destroyAll(); + await removeServerLock(); + process.exit(0); + }; + process.once('SIGINT', () => void shutdown()); + process.once('SIGTERM', () => void shutdown()); + }); + } + + // `claude ssh [dir]` — registered here only so --help shows it. + // The actual interactive flow is handled by early argv rewriting in main() + // (parallels the DIRECT_CONNECT/cc:// pattern above). If commander reaches + // this action it means the argv rewrite didn't fire (e.g. user ran + // `claude ssh` with no host) — just print usage. + if (feature('SSH_REMOTE')) { + program.command('ssh [dir]').description('Run Claude Code on a remote host over SSH. Deploys the binary and ' + 'tunnels API auth back through your local machine — no remote setup needed.').option('--permission-mode ', 'Permission mode for the remote session').option('--dangerously-skip-permissions', 'Skip all permission prompts on the remote (dangerous)').option('--local', 'e2e test mode — spawn the child CLI locally (skip ssh/deploy). ' + 'Exercises the auth proxy and unix-socket plumbing without a remote host.').action(async () => { + // Argv rewriting in main() should have consumed `ssh ` before + // commander runs. Reaching here means host was missing or the + // rewrite predicate didn't match. + process.stderr.write('Usage: claude ssh [dir]\n\n' + "Runs Claude Code on a remote Linux host. You don't need to install\n" + 'anything on the remote or run `claude auth login` there — the binary is\n' + 'deployed over SSH and API auth tunnels back through your local machine.\n'); + process.exit(1); + }); + } + + // claude connect — subcommand only handles -p (headless) mode. + // Interactive mode (without -p) is handled by early argv rewriting in main() + // which redirects to the main command with full TUI support. + if (feature('DIRECT_CONNECT')) { + program.command('open ').description('Connect to a Claude Code server (internal — use cc:// URLs)').option('-p, --print [prompt]', 'Print mode (headless)').option('--output-format ', 'Output format: text, json, stream-json', 'text').action(async (ccUrl: string, opts: { + print?: string | boolean; + outputFormat: string; + }) => { + const { + parseConnectUrl + } = await import('./server/parseConnectUrl.js'); + const { + serverUrl, + authToken + } = parseConnectUrl(ccUrl); + let connectConfig; + try { + const session = await createDirectConnectSession({ + serverUrl, + authToken, + cwd: getOriginalCwd(), + dangerouslySkipPermissions: _pendingConnect?.dangerouslySkipPermissions + }); + if (session.workDir) { + setOriginalCwd(session.workDir); + setCwdState(session.workDir); + } + setDirectConnectServerUrl(serverUrl); + connectConfig = session.config; + } catch (err) { + // biome-ignore lint/suspicious/noConsole: intentional error output + console.error(err instanceof DirectConnectError ? err.message : String(err)); + process.exit(1); + } + const { + runConnectHeadless + } = await import('./server/connectHeadless.js'); + const prompt = typeof opts.print === 'string' ? opts.print : ''; + const interactive = opts.print === true; + await runConnectHeadless(connectConfig, prompt, opts.outputFormat, interactive); + }); + } + + // claude auth + + const auth = program.command('auth').description('Manage authentication').configureHelp(createSortedHelpConfig()); + auth.command('login').description('Sign in to your Anthropic account').option('--email ', 'Pre-populate email address on the login page').option('--sso', 'Force SSO login flow').option('--console', 'Use Anthropic Console (API usage billing) instead of Claude subscription').option('--claudeai', 'Use Claude subscription (default)').action(async ({ + email, + sso, + console: useConsole, + claudeai + }: { + email?: string; + sso?: boolean; + console?: boolean; + claudeai?: boolean; + }) => { + const { + authLogin + } = await import('./cli/handlers/auth.js'); + await authLogin({ + email, + sso, + console: useConsole, + claudeai + }); + }); + auth.command('status').description('Show authentication status').option('--json', 'Output as JSON (default)').option('--text', 'Output as human-readable text').action(async (opts: { + json?: boolean; + text?: boolean; + }) => { + const { + authStatus + } = await import('./cli/handlers/auth.js'); + await authStatus(opts); + }); + auth.command('logout').description('Log out from your Anthropic account').action(async () => { + const { + authLogout + } = await import('./cli/handlers/auth.js'); + await authLogout(); + }); + + /** + * Helper function to handle marketplace command errors consistently. + * Logs the error and exits the process with status 1. + * @param error The error that occurred + * @param action Description of the action that failed + */ + // Hidden flag on all plugin/marketplace subcommands to target cowork_plugins. + const coworkOption = () => new Option('--cowork', 'Use cowork_plugins directory').hideHelp(); + + // Plugin validate command + const pluginCmd = program.command('plugin').alias('plugins').description('Manage Claude Code plugins').configureHelp(createSortedHelpConfig()); + pluginCmd.command('validate ').description('Validate a plugin or marketplace manifest').addOption(coworkOption()).action(async (manifestPath: string, options: { + cowork?: boolean; + }) => { + const { + pluginValidateHandler + } = await import('./cli/handlers/plugins.js'); + await pluginValidateHandler(manifestPath, options); + }); + + // Plugin list command + pluginCmd.command('list').description('List installed plugins').option('--json', 'Output as JSON').option('--available', 'Include available plugins from marketplaces (requires --json)').addOption(coworkOption()).action(async (options: { + json?: boolean; + available?: boolean; + cowork?: boolean; + }) => { + const { + pluginListHandler + } = await import('./cli/handlers/plugins.js'); + await pluginListHandler(options); + }); + + // Marketplace subcommands + const marketplaceCmd = pluginCmd.command('marketplace').description('Manage Claude Code marketplaces').configureHelp(createSortedHelpConfig()); + marketplaceCmd.command('add ').description('Add a marketplace from a URL, path, or GitHub repo').addOption(coworkOption()).option('--sparse ', 'Limit checkout to specific directories via git sparse-checkout (for monorepos). Example: --sparse .claude-plugin plugins').option('--scope ', 'Where to declare the marketplace: user (default), project, or local').action(async (source: string, options: { + cowork?: boolean; + sparse?: string[]; + scope?: string; + }) => { + const { + marketplaceAddHandler + } = await import('./cli/handlers/plugins.js'); + await marketplaceAddHandler(source, options); + }); + marketplaceCmd.command('list').description('List all configured marketplaces').option('--json', 'Output as JSON').addOption(coworkOption()).action(async (options: { + json?: boolean; + cowork?: boolean; + }) => { + const { + marketplaceListHandler + } = await import('./cli/handlers/plugins.js'); + await marketplaceListHandler(options); + }); + marketplaceCmd.command('remove ').alias('rm').description('Remove a configured marketplace').addOption(coworkOption()).action(async (name: string, options: { + cowork?: boolean; + }) => { + const { + marketplaceRemoveHandler + } = await import('./cli/handlers/plugins.js'); + await marketplaceRemoveHandler(name, options); + }); + marketplaceCmd.command('update [name]').description('Update marketplace(s) from their source - updates all if no name specified').addOption(coworkOption()).action(async (name: string | undefined, options: { + cowork?: boolean; + }) => { + const { + marketplaceUpdateHandler + } = await import('./cli/handlers/plugins.js'); + await marketplaceUpdateHandler(name, options); + }); + + // Plugin install command + pluginCmd.command('install ').alias('i').description('Install a plugin from available marketplaces (use plugin@marketplace for specific marketplace)').option('-s, --scope ', 'Installation scope: user, project, or local', 'user').addOption(coworkOption()).action(async (plugin: string, options: { + scope?: string; + cowork?: boolean; + }) => { + const { + pluginInstallHandler + } = await import('./cli/handlers/plugins.js'); + await pluginInstallHandler(plugin, options); + }); + + // Plugin uninstall command + pluginCmd.command('uninstall ').alias('remove').alias('rm').description('Uninstall an installed plugin').option('-s, --scope ', 'Uninstall from scope: user, project, or local', 'user').option('--keep-data', "Preserve the plugin's persistent data directory (~/.claude/plugins/data/{id}/)").addOption(coworkOption()).action(async (plugin: string, options: { + scope?: string; + cowork?: boolean; + keepData?: boolean; + }) => { + const { + pluginUninstallHandler + } = await import('./cli/handlers/plugins.js'); + await pluginUninstallHandler(plugin, options); + }); + + // Plugin enable command + pluginCmd.command('enable ').description('Enable a disabled plugin').option('-s, --scope ', `Installation scope: ${VALID_INSTALLABLE_SCOPES.join(', ')} (default: auto-detect)`).addOption(coworkOption()).action(async (plugin: string, options: { + scope?: string; + cowork?: boolean; + }) => { + const { + pluginEnableHandler + } = await import('./cli/handlers/plugins.js'); + await pluginEnableHandler(plugin, options); + }); + + // Plugin disable command + pluginCmd.command('disable [plugin]').description('Disable an enabled plugin').option('-a, --all', 'Disable all enabled plugins').option('-s, --scope ', `Installation scope: ${VALID_INSTALLABLE_SCOPES.join(', ')} (default: auto-detect)`).addOption(coworkOption()).action(async (plugin: string | undefined, options: { + scope?: string; + cowork?: boolean; + all?: boolean; + }) => { + const { + pluginDisableHandler + } = await import('./cli/handlers/plugins.js'); + await pluginDisableHandler(plugin, options); + }); + + // Plugin update command + pluginCmd.command('update ').description('Update a plugin to the latest version (restart required to apply)').option('-s, --scope ', `Installation scope: ${VALID_UPDATE_SCOPES.join(', ')} (default: user)`).addOption(coworkOption()).action(async (plugin: string, options: { + scope?: string; + cowork?: boolean; + }) => { + const { + pluginUpdateHandler + } = await import('./cli/handlers/plugins.js'); + await pluginUpdateHandler(plugin, options); + }); + // END ANT-ONLY + + // Setup token command + program.command('setup-token').description('Set up a long-lived authentication token (requires Claude subscription)').action(async () => { + const [{ + setupTokenHandler + }, { + createRoot + }] = await Promise.all([import('./cli/handlers/util.js'), import('./ink.js')]); + const root = await createRoot(getBaseRenderOptions(false)); + await setupTokenHandler(root); + }); + + // Agents command - list configured agents + program.command('agents').description('List configured agents').option('--setting-sources ', 'Comma-separated list of setting sources to load (user, project, local).').action(async () => { + const { + agentsHandler + } = await import('./cli/handlers/agents.js'); + await agentsHandler(); + process.exit(0); + }); + if (feature('TRANSCRIPT_CLASSIFIER')) { + // Skip when tengu_auto_mode_config.enabled === 'disabled' (circuit breaker). + // Reads from disk cache — GrowthBook isn't initialized at registration time. + if (getAutoModeEnabledStateIfCached() !== 'disabled') { + const autoModeCmd = program.command('auto-mode').description('Inspect auto mode classifier configuration'); + autoModeCmd.command('defaults').description('Print the default auto mode environment, allow, and deny rules as JSON').action(async () => { + const { + autoModeDefaultsHandler + } = await import('./cli/handlers/autoMode.js'); + autoModeDefaultsHandler(); + process.exit(0); + }); + autoModeCmd.command('config').description('Print the effective auto mode config as JSON: your settings where set, defaults otherwise').action(async () => { + const { + autoModeConfigHandler + } = await import('./cli/handlers/autoMode.js'); + autoModeConfigHandler(); + process.exit(0); + }); + autoModeCmd.command('critique').description('Get AI feedback on your custom auto mode rules').option('--model ', 'Override which model is used').action(async options => { + const { + autoModeCritiqueHandler + } = await import('./cli/handlers/autoMode.js'); + await autoModeCritiqueHandler(options); + process.exit(); + }); + } + } + + // Remote Control command — connect local environment to claude.ai/code. + // The actual command is intercepted by the fast-path in cli.tsx before + // Commander.js runs, so this registration exists only for help output. + // Always hidden: isBridgeEnabled() at this point (before enableConfigs) + // would throw inside isClaudeAISubscriber → getGlobalConfig and return + // false via the try/catch — but not before paying ~65ms of side effects + // (25ms settings Zod parse + 40ms sync `security` keychain subprocess). + // The dynamic visibility never worked; the command was always hidden. + if (feature('BRIDGE_MODE')) { + program.command('remote-control', { + hidden: true + }).alias('rc').description('Connect your local environment for remote-control sessions via claude.ai/code').action(async () => { + // Unreachable — cli.tsx fast-path handles this command before main.tsx loads. + // If somehow reached, delegate to bridgeMain. + const { + bridgeMain + } = await import('./bridge/bridgeMain.js'); + await bridgeMain(process.argv.slice(3)); + }); + } + if (feature('KAIROS')) { + program.command('assistant [sessionId]').description('Attach the REPL as a client to a running bridge session. Discovers sessions via API if no sessionId given.').action(() => { + // Argv rewriting above should have consumed `assistant [id]` + // before commander runs. Reaching here means a root flag came first + // (e.g. `--debug assistant`) and the position-0 predicate + // didn't match. Print usage like the ssh stub does. + process.stderr.write('Usage: claude assistant [sessionId]\n\n' + 'Attach the REPL as a viewer client to a running bridge session.\n' + 'Omit sessionId to discover and pick from available sessions.\n'); + process.exit(1); + }); + } + + // Doctor command - check installation health + program.command('doctor').description('Check the health of your Claude Code auto-updater. Note: The workspace trust dialog is skipped and stdio servers from .mcp.json are spawned for health checks. Only use this command in directories you trust.').action(async () => { + const [{ + doctorHandler + }, { + createRoot + }] = await Promise.all([import('./cli/handlers/util.js'), import('./ink.js')]); + const root = await createRoot(getBaseRenderOptions(false)); + await doctorHandler(root); + }); + + // claude update + // + // For SemVer-compliant versioning with build metadata (X.X.X+SHA): + // - We perform exact string comparison (including SHA) to detect any change + // - This ensures users always get the latest build, even when only the SHA changes + // - UI shows both versions including build metadata for clarity + program.command('update').alias('upgrade').description('Check for updates and install if available').action(async () => { + const { + update + } = await import('src/cli/update.js'); + await update(); + }); + + // claude up — run the project's CLAUDE.md "# claude up" setup instructions. + if ("external" === 'ant') { + program.command('up').description('[ANT-ONLY] Initialize or upgrade the local dev environment using the "# claude up" section of the nearest CLAUDE.md').action(async () => { + const { + up + } = await import('src/cli/up.js'); + await up(); + }); + } + + // claude rollback (ant-only) + // Rolls back to previous releases + if ("external" === 'ant') { + program.command('rollback [target]').description('[ANT-ONLY] Roll back to a previous release\n\nExamples:\n claude rollback Go 1 version back from current\n claude rollback 3 Go 3 versions back from current\n claude rollback 2.0.73-dev.20251217.t190658 Roll back to a specific version').option('-l, --list', 'List recent published versions with ages').option('--dry-run', 'Show what would be installed without installing').option('--safe', 'Roll back to the server-pinned safe version (set by oncall during incidents)').action(async (target?: string, options?: { + list?: boolean; + dryRun?: boolean; + safe?: boolean; + }) => { + const { + rollback + } = await import('src/cli/rollback.js'); + await rollback(target, options); + }); + } + + // claude install + program.command('install [target]').description('Install Claude Code native build. Use [target] to specify version (stable, latest, or specific version)').option('--force', 'Force installation even if already installed').action(async (target: string | undefined, options: { + force?: boolean; + }) => { + const { + installHandler + } = await import('./cli/handlers/util.js'); + await installHandler(target, options); + }); + + // ant-only commands + if ("external" === 'ant') { + const validateLogId = (value: string) => { + const maybeSessionId = validateUuid(value); + if (maybeSessionId) return maybeSessionId; + return Number(value); + }; + // claude log + program.command('log').description('[ANT-ONLY] Manage conversation logs.').argument('[number|sessionId]', 'A number (0, 1, 2, etc.) to display a specific log, or the sesssion ID (uuid) of a log', validateLogId).action(async (logId: string | number | undefined) => { + const { + logHandler + } = await import('./cli/handlers/ant.js'); + await logHandler(logId); + }); + + // claude error + program.command('error').description('[ANT-ONLY] View error logs. Optionally provide a number (0, -1, -2, etc.) to display a specific log.').argument('[number]', 'A number (0, 1, 2, etc.) to display a specific log', parseInt).action(async (number: number | undefined) => { + const { + errorHandler + } = await import('./cli/handlers/ant.js'); + await errorHandler(number); + }); + + // claude export + program.command('export').description('[ANT-ONLY] Export a conversation to a text file.').usage(' ').argument('', 'Session ID, log index (0, 1, 2...), or path to a .json/.jsonl log file').argument('', 'Output file path for the exported text').addHelpText('after', ` +Examples: + $ claude export 0 conversation.txt Export conversation at log index 0 + $ claude export conversation.txt Export conversation by session ID + $ claude export input.json output.txt Render JSON log file to text + $ claude export .jsonl output.txt Render JSONL session file to text`).action(async (source: string, outputFile: string) => { + const { + exportHandler + } = await import('./cli/handlers/ant.js'); + await exportHandler(source, outputFile); + }); + if ("external" === 'ant') { + const taskCmd = program.command('task').description('[ANT-ONLY] Manage task list tasks'); + taskCmd.command('create ').description('Create a new task').option('-d, --description ', 'Task description').option('-l, --list ', 'Task list ID (defaults to "tasklist")').action(async (subject: string, opts: { + description?: string; + list?: string; + }) => { + const { + taskCreateHandler + } = await import('./cli/handlers/ant.js'); + await taskCreateHandler(subject, opts); + }); + taskCmd.command('list').description('List all tasks').option('-l, --list ', 'Task list ID (defaults to "tasklist")').option('--pending', 'Show only pending tasks').option('--json', 'Output as JSON').action(async (opts: { + list?: string; + pending?: boolean; + json?: boolean; + }) => { + const { + taskListHandler + } = await import('./cli/handlers/ant.js'); + await taskListHandler(opts); + }); + taskCmd.command('get ').description('Get details of a task').option('-l, --list ', 'Task list ID (defaults to "tasklist")').action(async (id: string, opts: { + list?: string; + }) => { + const { + taskGetHandler + } = await import('./cli/handlers/ant.js'); + await taskGetHandler(id, opts); + }); + taskCmd.command('update ').description('Update a task').option('-l, --list ', 'Task list ID (defaults to "tasklist")').option('-s, --status ', `Set status (${TASK_STATUSES.join(', ')})`).option('--subject ', 'Update subject').option('-d, --description ', 'Update description').option('--owner ', 'Set owner').option('--clear-owner', 'Clear owner').action(async (id: string, opts: { + list?: string; + status?: string; + subject?: string; + description?: string; + owner?: string; + clearOwner?: boolean; + }) => { + const { + taskUpdateHandler + } = await import('./cli/handlers/ant.js'); + await taskUpdateHandler(id, opts); + }); + taskCmd.command('dir').description('Show the tasks directory path').option('-l, --list ', 'Task list ID (defaults to "tasklist")').action(async (opts: { + list?: string; + }) => { + const { + taskDirHandler + } = await import('./cli/handlers/ant.js'); + await taskDirHandler(opts); + }); + } + + // claude completion + program.command('completion ', { + hidden: true + }).description('Generate shell completion script (bash, zsh, or fish)').option('--output ', 'Write completion script directly to a file instead of stdout').action(async (shell: string, opts: { + output?: string; + }) => { + const { + completionHandler + } = await import('./cli/handlers/ant.js'); + await completionHandler(shell, opts, program); + }); + } + profileCheckpoint('run_before_parse'); + await program.parseAsync(process.argv); + profileCheckpoint('run_after_parse'); + + // Record final checkpoint for total_time calculation + profileCheckpoint('main_after_run'); + + // Log startup perf to Statsig (sampled) and output detailed report if enabled + profileReport(); + return program; +} +async function logTenguInit({ + hasInitialPrompt, + hasStdin, + verbose, + debug, + debugToStderr, + print, + outputFormat, + inputFormat, + numAllowedTools, + numDisallowedTools, + mcpClientCount, + worktreeEnabled, + skipWebFetchPreflight, + githubActionInputs, + dangerouslySkipPermissionsPassed, + permissionMode, + modeIsBypass, + allowDangerouslySkipPermissionsPassed, + systemPromptFlag, + appendSystemPromptFlag, + thinkingConfig, + assistantActivationPath +}: { + hasInitialPrompt: boolean; + hasStdin: boolean; + verbose: boolean; + debug: boolean; + debugToStderr: boolean; + print: boolean; + outputFormat: string; + inputFormat: string; + numAllowedTools: number; + numDisallowedTools: number; + mcpClientCount: number; + worktreeEnabled: boolean; + skipWebFetchPreflight: boolean | undefined; + githubActionInputs: string | undefined; + dangerouslySkipPermissionsPassed: boolean; + permissionMode: string; + modeIsBypass: boolean; + allowDangerouslySkipPermissionsPassed: boolean; + systemPromptFlag: 'file' | 'flag' | undefined; + appendSystemPromptFlag: 'file' | 'flag' | undefined; + thinkingConfig: ThinkingConfig; + assistantActivationPath: string | undefined; +}): Promise { + try { + logEvent('tengu_init', { + entrypoint: 'claude' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + hasInitialPrompt, + hasStdin, + verbose, + debug, + debugToStderr, + print, + outputFormat: outputFormat as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + inputFormat: inputFormat as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + numAllowedTools, + numDisallowedTools, + mcpClientCount, + worktree: worktreeEnabled, + skipWebFetchPreflight, + ...(githubActionInputs && { + githubActionInputs: githubActionInputs as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }), + dangerouslySkipPermissionsPassed, + permissionMode: permissionMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + modeIsBypass, + inProtectedNamespace: isInProtectedNamespace(), + allowDangerouslySkipPermissionsPassed, + thinkingType: thinkingConfig.type as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + ...(systemPromptFlag && { + systemPromptFlag: systemPromptFlag as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }), + ...(appendSystemPromptFlag && { + appendSystemPromptFlag: appendSystemPromptFlag as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }), + is_simple: isBareMode() || undefined, + is_coordinator: feature('COORDINATOR_MODE') && coordinatorModeModule?.isCoordinatorMode() ? true : undefined, + ...(assistantActivationPath && { + assistantActivationPath: assistantActivationPath as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }), + autoUpdatesChannel: (getInitialSettings().autoUpdatesChannel ?? 'latest') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + ...("external" === 'ant' ? (() => { + const cwd = getCwd(); + const gitRoot = findGitRoot(cwd); + const rp = gitRoot ? relative(gitRoot, cwd) || '.' : undefined; + return rp ? { + relativeProjectPath: rp as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + } : {}; + })() : {}) + }); + } catch (error) { + logError(error); + } +} +function maybeActivateProactive(options: unknown): void { + if ((feature('PROACTIVE') || feature('KAIROS')) && ((options as { + proactive?: boolean; + }).proactive || isEnvTruthy(process.env.CLAUDE_CODE_PROACTIVE))) { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const proactiveModule = require('./proactive/index.js'); + if (!proactiveModule.isProactiveActive()) { + proactiveModule.activateProactive('command'); + } + } +} +function maybeActivateBrief(options: unknown): void { + if (!(feature('KAIROS') || feature('KAIROS_BRIEF'))) return; + const briefFlag = (options as { + brief?: boolean; + }).brief; + const briefEnv = isEnvTruthy(process.env.CLAUDE_CODE_BRIEF); + if (!briefFlag && !briefEnv) return; + // --brief / CLAUDE_CODE_BRIEF are explicit opt-ins: check entitlement, + // then set userMsgOptIn to activate the tool + prompt section. The env + // var also grants entitlement (isBriefEntitled() reads it), so setting + // CLAUDE_CODE_BRIEF=1 alone force-enables for dev/testing — no GB gate + // needed. initialIsBriefOnly reads getUserMsgOptIn() directly. + // Conditional require: static import would leak the tool name string + // into external builds via BriefTool.ts → prompt.ts. + /* eslint-disable @typescript-eslint/no-require-imports */ + const { + isBriefEntitled + } = require('./tools/BriefTool/BriefTool.js') as typeof import('./tools/BriefTool/BriefTool.js'); + /* eslint-enable @typescript-eslint/no-require-imports */ + const entitled = isBriefEntitled(); + if (entitled) { + setUserMsgOptIn(true); + } + // Fire unconditionally once intent is seen: enabled=false captures the + // "user tried but was gated" failure mode in Datadog. + logEvent('tengu_brief_mode_enabled', { + enabled: entitled, + gated: !entitled, + source: (briefEnv ? 'env' : 'flag') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); +} +function resetCursor() { + const terminal = process.stderr.isTTY ? process.stderr : process.stdout.isTTY ? process.stdout : undefined; + terminal?.write(SHOW_CURSOR); +} +type TeammateOptions = { + agentId?: string; + agentName?: string; + teamName?: string; + agentColor?: string; + planModeRequired?: boolean; + parentSessionId?: string; + teammateMode?: 'auto' | 'tmux' | 'in-process'; + agentType?: string; +}; +function extractTeammateOptions(options: unknown): TeammateOptions { + if (typeof options !== 'object' || options === null) { + return {}; + } + const opts = options as Record; + const teammateMode = opts.teammateMode; + return { + agentId: typeof opts.agentId === 'string' ? opts.agentId : undefined, + agentName: typeof opts.agentName === 'string' ? opts.agentName : undefined, + teamName: typeof opts.teamName === 'string' ? opts.teamName : undefined, + agentColor: typeof opts.agentColor === 'string' ? opts.agentColor : undefined, + planModeRequired: typeof opts.planModeRequired === 'boolean' ? opts.planModeRequired : undefined, + parentSessionId: typeof opts.parentSessionId === 'string' ? opts.parentSessionId : undefined, + teammateMode: teammateMode === 'auto' || teammateMode === 'tmux' || teammateMode === 'in-process' ? teammateMode : undefined, + agentType: typeof opts.agentType === 'string' ? opts.agentType : undefined + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJwcm9maWxlQ2hlY2twb2ludCIsInByb2ZpbGVSZXBvcnQiLCJzdGFydE1kbVJhd1JlYWQiLCJlbnN1cmVLZXljaGFpblByZWZldGNoQ29tcGxldGVkIiwic3RhcnRLZXljaGFpblByZWZldGNoIiwiZmVhdHVyZSIsIkNvbW1hbmQiLCJDb21tYW5kZXJDb21tYW5kIiwiSW52YWxpZEFyZ3VtZW50RXJyb3IiLCJPcHRpb24iLCJjaGFsayIsInJlYWRGaWxlU3luYyIsIm1hcFZhbHVlcyIsInBpY2tCeSIsInVuaXFCeSIsIlJlYWN0IiwiZ2V0T2F1dGhDb25maWciLCJnZXRSZW1vdGVTZXNzaW9uVXJsIiwiZ2V0U3lzdGVtQ29udGV4dCIsImdldFVzZXJDb250ZXh0IiwiaW5pdCIsImluaXRpYWxpemVUZWxlbWV0cnlBZnRlclRydXN0IiwiYWRkVG9IaXN0b3J5IiwiUm9vdCIsImxhdW5jaFJlcGwiLCJoYXNHcm93dGhCb29rRW52T3ZlcnJpZGUiLCJpbml0aWFsaXplR3Jvd3RoQm9vayIsInJlZnJlc2hHcm93dGhCb29rQWZ0ZXJBdXRoQ2hhbmdlIiwiZmV0Y2hCb290c3RyYXBEYXRhIiwiRG93bmxvYWRSZXN1bHQiLCJkb3dubG9hZFNlc3Npb25GaWxlcyIsIkZpbGVzQXBpQ29uZmlnIiwicGFyc2VGaWxlU3BlY3MiLCJwcmVmZXRjaFBhc3Nlc0VsaWdpYmlsaXR5IiwicHJlZmV0Y2hPZmZpY2lhbE1jcFVybHMiLCJNY3BTZGtTZXJ2ZXJDb25maWciLCJNY3BTZXJ2ZXJDb25maWciLCJTY29wZWRNY3BTZXJ2ZXJDb25maWciLCJpc1BvbGljeUFsbG93ZWQiLCJsb2FkUG9saWN5TGltaXRzIiwicmVmcmVzaFBvbGljeUxpbWl0cyIsIndhaXRGb3JQb2xpY3lMaW1pdHNUb0xvYWQiLCJsb2FkUmVtb3RlTWFuYWdlZFNldHRpbmdzIiwicmVmcmVzaFJlbW90ZU1hbmFnZWRTZXR0aW5ncyIsIlRvb2xJbnB1dEpTT05TY2hlbWEiLCJjcmVhdGVTeW50aGV0aWNPdXRwdXRUb29sIiwiaXNTeW50aGV0aWNPdXRwdXRUb29sRW5hYmxlZCIsImdldFRvb2xzIiwiY2FuVXNlckNvbmZpZ3VyZUFkdmlzb3IiLCJnZXRJbml0aWFsQWR2aXNvclNldHRpbmciLCJpc0Fkdmlzb3JFbmFibGVkIiwiaXNWYWxpZEFkdmlzb3JNb2RlbCIsIm1vZGVsU3VwcG9ydHNBZHZpc29yIiwiaXNBZ2VudFN3YXJtc0VuYWJsZWQiLCJjb3VudCIsInVuaXEiLCJpbnN0YWxsQXNjaWljYXN0UmVjb3JkZXIiLCJnZXRTdWJzY3JpcHRpb25UeXBlIiwiaXNDbGF1ZGVBSVN1YnNjcmliZXIiLCJwcmVmZXRjaEF3c0NyZWRlbnRpYWxzQW5kQmVkUm9ja0luZm9JZlNhZmUiLCJwcmVmZXRjaEdjcENyZWRlbnRpYWxzSWZTYWZlIiwidmFsaWRhdGVGb3JjZUxvZ2luT3JnIiwiY2hlY2tIYXNUcnVzdERpYWxvZ0FjY2VwdGVkIiwiZ2V0R2xvYmFsQ29uZmlnIiwiZ2V0UmVtb3RlQ29udHJvbEF0U3RhcnR1cCIsImlzQXV0b1VwZGF0ZXJEaXNhYmxlZCIsInNhdmVHbG9iYWxDb25maWciLCJzZWVkRWFybHlJbnB1dCIsInN0b3BDYXB0dXJpbmdFYXJseUlucHV0IiwiZ2V0SW5pdGlhbEVmZm9ydFNldHRpbmciLCJwYXJzZUVmZm9ydFZhbHVlIiwiZ2V0SW5pdGlhbEZhc3RNb2RlU2V0dGluZyIsImlzRmFzdE1vZGVFbmFibGVkIiwicHJlZmV0Y2hGYXN0TW9kZVN0YXR1cyIsInJlc29sdmVGYXN0TW9kZVN0YXR1c0Zyb21DYWNoZSIsImFwcGx5Q29uZmlnRW52aXJvbm1lbnRWYXJpYWJsZXMiLCJjcmVhdGVTeXN0ZW1NZXNzYWdlIiwiY3JlYXRlVXNlck1lc3NhZ2UiLCJnZXRQbGF0Zm9ybSIsImdldEJhc2VSZW5kZXJPcHRpb25zIiwiZ2V0U2Vzc2lvbkluZ3Jlc3NBdXRoVG9rZW4iLCJzZXR0aW5nc0NoYW5nZURldGVjdG9yIiwic2tpbGxDaGFuZ2VEZXRlY3RvciIsImpzb25QYXJzZSIsIndyaXRlRmlsZVN5bmNfREVQUkVDQVRFRCIsImNvbXB1dGVJbml0aWFsVGVhbUNvbnRleHQiLCJpbml0aWFsaXplV2FybmluZ0hhbmRsZXIiLCJpc1dvcmt0cmVlTW9kZUVuYWJsZWQiLCJnZXRUZWFtbWF0ZVV0aWxzIiwicmVxdWlyZSIsImdldFRlYW1tYXRlUHJvbXB0QWRkZW5kdW0iLCJnZXRUZWFtbWF0ZU1vZGVTbmFwc2hvdCIsImNvb3JkaW5hdG9yTW9kZU1vZHVsZSIsImFzc2lzdGFudE1vZHVsZSIsImthaXJvc0dhdGUiLCJyZWxhdGl2ZSIsInJlc29sdmUiLCJpc0FuYWx5dGljc0Rpc2FibGVkIiwiZ2V0RmVhdHVyZVZhbHVlX0NBQ0hFRF9NQVlfQkVfU1RBTEUiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJpbml0aWFsaXplQW5hbHl0aWNzR2F0ZXMiLCJnZXRPcmlnaW5hbEN3ZCIsInNldEFkZGl0aW9uYWxEaXJlY3Rvcmllc0ZvckNsYXVkZU1kIiwic2V0SXNSZW1vdGVNb2RlIiwic2V0TWFpbkxvb3BNb2RlbE92ZXJyaWRlIiwic2V0TWFpblRocmVhZEFnZW50VHlwZSIsInNldFRlbGVwb3J0ZWRTZXNzaW9uSW5mbyIsImZpbHRlckNvbW1hbmRzRm9yUmVtb3RlTW9kZSIsImdldENvbW1hbmRzIiwiU3RhdHNTdG9yZSIsImxhdW5jaEFzc2lzdGFudEluc3RhbGxXaXphcmQiLCJsYXVuY2hBc3Npc3RhbnRTZXNzaW9uQ2hvb3NlciIsImxhdW5jaEludmFsaWRTZXR0aW5nc0RpYWxvZyIsImxhdW5jaFJlc3VtZUNob29zZXIiLCJsYXVuY2hTbmFwc2hvdFVwZGF0ZURpYWxvZyIsImxhdW5jaFRlbGVwb3J0UmVwb01pc21hdGNoRGlhbG9nIiwibGF1bmNoVGVsZXBvcnRSZXN1bWVXcmFwcGVyIiwiU0hPV19DVVJTT1IiLCJleGl0V2l0aEVycm9yIiwiZXhpdFdpdGhNZXNzYWdlIiwiZ2V0UmVuZGVyQ29udGV4dCIsInJlbmRlckFuZFJ1biIsInNob3dTZXR1cFNjcmVlbnMiLCJpbml0QnVpbHRpblBsdWdpbnMiLCJjaGVja1F1b3RhU3RhdHVzIiwiZ2V0TWNwVG9vbHNDb21tYW5kc0FuZFJlc291cmNlcyIsInByZWZldGNoQWxsTWNwUmVzb3VyY2VzIiwiVkFMSURfSU5TVEFMTEFCTEVfU0NPUEVTIiwiVkFMSURfVVBEQVRFX1NDT1BFUyIsImluaXRCdW5kbGVkU2tpbGxzIiwiQWdlbnRDb2xvck5hbWUiLCJnZXRBY3RpdmVBZ2VudHNGcm9tTGlzdCIsImdldEFnZW50RGVmaW5pdGlvbnNXaXRoT3ZlcnJpZGVzIiwiaXNCdWlsdEluQWdlbnQiLCJpc0N1c3RvbUFnZW50IiwicGFyc2VBZ2VudHNGcm9tSnNvbiIsIkxvZ09wdGlvbiIsIk1lc3NhZ2UiLCJNZXNzYWdlVHlwZSIsImFzc2VydE1pblZlcnNpb24iLCJDTEFVREVfSU5fQ0hST01FX1NLSUxMX0hJTlQiLCJDTEFVREVfSU5fQ0hST01FX1NLSUxMX0hJTlRfV0lUSF9XRUJCUk9XU0VSIiwic2V0dXBDbGF1ZGVJbkNocm9tZSIsInNob3VsZEF1dG9FbmFibGVDbGF1ZGVJbkNocm9tZSIsInNob3VsZEVuYWJsZUNsYXVkZUluQ2hyb21lIiwiZ2V0Q29udGV4dFdpbmRvd0Zvck1vZGVsIiwibG9hZENvbnZlcnNhdGlvbkZvclJlc3VtZSIsImJ1aWxkRGVlcExpbmtCYW5uZXIiLCJoYXNOb2RlT3B0aW9uIiwiaXNCYXJlTW9kZSIsImlzRW52VHJ1dGh5IiwiaXNJblByb3RlY3RlZE5hbWVzcGFjZSIsInJlZnJlc2hFeGFtcGxlQ29tbWFuZHMiLCJGcHNNZXRyaWNzIiwiZ2V0V29ya3RyZWVQYXRocyIsImZpbmRHaXRSb290IiwiZ2V0QnJhbmNoIiwiZ2V0SXNHaXQiLCJnZXRXb3JrdHJlZUNvdW50IiwiZ2V0R2hBdXRoU3RhdHVzIiwic2FmZVBhcnNlSlNPTiIsImxvZ0Vycm9yIiwiZ2V0TW9kZWxEZXByZWNhdGlvbldhcm5pbmciLCJnZXREZWZhdWx0TWFpbkxvb3BNb2RlbCIsImdldFVzZXJTcGVjaWZpZWRNb2RlbFNldHRpbmciLCJub3JtYWxpemVNb2RlbFN0cmluZ0ZvckFQSSIsInBhcnNlVXNlclNwZWNpZmllZE1vZGVsIiwiZW5zdXJlTW9kZWxTdHJpbmdzSW5pdGlhbGl6ZWQiLCJQRVJNSVNTSU9OX01PREVTIiwiY2hlY2tBbmREaXNhYmxlQnlwYXNzUGVybWlzc2lvbnMiLCJnZXRBdXRvTW9kZUVuYWJsZWRTdGF0ZUlmQ2FjaGVkIiwiaW5pdGlhbGl6ZVRvb2xQZXJtaXNzaW9uQ29udGV4dCIsImluaXRpYWxQZXJtaXNzaW9uTW9kZUZyb21DTEkiLCJpc0RlZmF1bHRQZXJtaXNzaW9uTW9kZUF1dG8iLCJwYXJzZVRvb2xMaXN0RnJvbUNMSSIsInJlbW92ZURhbmdlcm91c1Blcm1pc3Npb25zIiwic3RyaXBEYW5nZXJvdXNQZXJtaXNzaW9uc0ZvckF1dG9Nb2RlIiwidmVyaWZ5QXV0b01vZGVHYXRlQWNjZXNzIiwiY2xlYW51cE9ycGhhbmVkUGx1Z2luVmVyc2lvbnNJbkJhY2tncm91bmQiLCJpbml0aWFsaXplVmVyc2lvbmVkUGx1Z2lucyIsImdldE1hbmFnZWRQbHVnaW5OYW1lcyIsImdldEdsb2JFeGNsdXNpb25zRm9yUGx1Z2luQ2FjaGUiLCJnZXRQbHVnaW5TZWVkRGlycyIsImNvdW50RmlsZXNSb3VuZGVkUmciLCJwcm9jZXNzU2Vzc2lvblN0YXJ0SG9va3MiLCJwcm9jZXNzU2V0dXBIb29rcyIsImNhY2hlU2Vzc2lvblRpdGxlIiwiZ2V0U2Vzc2lvbklkRnJvbUxvZyIsImxvYWRUcmFuc2NyaXB0RnJvbUZpbGUiLCJzYXZlQWdlbnRTZXR0aW5nIiwic2F2ZU1vZGUiLCJzZWFyY2hTZXNzaW9uc0J5Q3VzdG9tVGl0bGUiLCJzZXNzaW9uSWRFeGlzdHMiLCJlbnN1cmVNZG1TZXR0aW5nc0xvYWRlZCIsImdldEluaXRpYWxTZXR0aW5ncyIsImdldE1hbmFnZWRTZXR0aW5nc0tleXNGb3JMb2dnaW5nIiwiZ2V0U2V0dGluZ3NGb3JTb3VyY2UiLCJnZXRTZXR0aW5nc1dpdGhFcnJvcnMiLCJyZXNldFNldHRpbmdzQ2FjaGUiLCJWYWxpZGF0aW9uRXJyb3IiLCJERUZBVUxUX1RBU0tTX01PREVfVEFTS19MSVNUX0lEIiwiVEFTS19TVEFUVVNFUyIsImxvZ1BsdWdpbkxvYWRFcnJvcnMiLCJsb2dQbHVnaW5zRW5hYmxlZEZvclNlc3Npb24iLCJsb2dTa2lsbHNMb2FkZWQiLCJnZW5lcmF0ZVRlbXBGaWxlUGF0aCIsInZhbGlkYXRlVXVpZCIsInJlZ2lzdGVyTWNwQWRkQ29tbWFuZCIsInJlZ2lzdGVyTWNwWGFhSWRwQ29tbWFuZCIsImxvZ1Blcm1pc3Npb25Db250ZXh0Rm9yQW50cyIsImZldGNoQ2xhdWRlQUlNY3BDb25maWdzSWZFbGlnaWJsZSIsImNsZWFyU2VydmVyQ2FjaGUiLCJhcmVNY3BDb25maWdzQWxsb3dlZFdpdGhFbnRlcnByaXNlTWNwQ29uZmlnIiwiZGVkdXBDbGF1ZGVBaU1jcFNlcnZlcnMiLCJkb2VzRW50ZXJwcmlzZU1jcENvbmZpZ0V4aXN0IiwiZmlsdGVyTWNwU2VydmVyc0J5UG9saWN5IiwiZ2V0Q2xhdWRlQ29kZU1jcENvbmZpZ3MiLCJnZXRNY3BTZXJ2ZXJTaWduYXR1cmUiLCJwYXJzZU1jcENvbmZpZyIsInBhcnNlTWNwQ29uZmlnRnJvbUZpbGVQYXRoIiwiZXhjbHVkZUNvbW1hbmRzQnlTZXJ2ZXIiLCJleGNsdWRlUmVzb3VyY2VzQnlTZXJ2ZXIiLCJpc1hhYUVuYWJsZWQiLCJnZXRSZWxldmFudFRpcHMiLCJsb2dDb250ZXh0TWV0cmljcyIsIkNMQVVERV9JTl9DSFJPTUVfTUNQX1NFUlZFUl9OQU1FIiwiaXNDbGF1ZGVJbkNocm9tZU1DUFNlcnZlciIsInJlZ2lzdGVyQ2xlYW51cCIsImVhZ2VyUGFyc2VDbGlGbGFnIiwiY3JlYXRlRW1wdHlBdHRyaWJ1dGlvblN0YXRlIiwiY291bnRDb25jdXJyZW50U2Vzc2lvbnMiLCJyZWdpc3RlclNlc3Npb24iLCJ1cGRhdGVTZXNzaW9uTmFtZSIsImdldEN3ZCIsImxvZ0ZvckRlYnVnZ2luZyIsInNldEhhc0Zvcm1hdHRlZE91dHB1dCIsImVycm9yTWVzc2FnZSIsImdldEVycm5vQ29kZSIsImlzRU5PRU5UIiwiVGVsZXBvcnRPcGVyYXRpb25FcnJvciIsInRvRXJyb3IiLCJnZXRGc0ltcGxlbWVudGF0aW9uIiwic2FmZVJlc29sdmVQYXRoIiwiZ3JhY2VmdWxTaHV0ZG93biIsImdyYWNlZnVsU2h1dGRvd25TeW5jIiwic2V0QWxsSG9va0V2ZW50c0VuYWJsZWQiLCJyZWZyZXNoTW9kZWxDYXBhYmlsaXRpZXMiLCJwZWVrRm9yU3RkaW5EYXRhIiwid3JpdGVUb1N0ZGVyciIsInNldEN3ZCIsIlByb2Nlc3NlZFJlc3VtZSIsInByb2Nlc3NSZXN1bWVkQ29udmVyc2F0aW9uIiwicGFyc2VTZXR0aW5nU291cmNlc0ZsYWciLCJwbHVyYWwiLCJDaGFubmVsRW50cnkiLCJnZXRJbml0aWFsTWFpbkxvb3BNb2RlbCIsImdldElzTm9uSW50ZXJhY3RpdmVTZXNzaW9uIiwiZ2V0U2RrQmV0YXMiLCJnZXRTZXNzaW9uSWQiLCJnZXRVc2VyTXNnT3B0SW4iLCJzZXRBbGxvd2VkQ2hhbm5lbHMiLCJzZXRBbGxvd2VkU2V0dGluZ1NvdXJjZXMiLCJzZXRDaHJvbWVGbGFnT3ZlcnJpZGUiLCJzZXRDbGllbnRUeXBlIiwic2V0Q3dkU3RhdGUiLCJzZXREaXJlY3RDb25uZWN0U2VydmVyVXJsIiwic2V0RmxhZ1NldHRpbmdzUGF0aCIsInNldEluaXRpYWxNYWluTG9vcE1vZGVsIiwic2V0SW5saW5lUGx1Z2lucyIsInNldElzSW50ZXJhY3RpdmUiLCJzZXRLYWlyb3NBY3RpdmUiLCJzZXRPcmlnaW5hbEN3ZCIsInNldFF1ZXN0aW9uUHJldmlld0Zvcm1hdCIsInNldFNka0JldGFzIiwic2V0U2Vzc2lvbkJ5cGFzc1Blcm1pc3Npb25zTW9kZSIsInNldFNlc3Npb25QZXJzaXN0ZW5jZURpc2FibGVkIiwic2V0U2Vzc2lvblNvdXJjZSIsInNldFVzZXJNc2dPcHRJbiIsInN3aXRjaFNlc3Npb24iLCJhdXRvTW9kZVN0YXRlTW9kdWxlIiwibWlncmF0ZUF1dG9VcGRhdGVzVG9TZXR0aW5ncyIsIm1pZ3JhdGVCeXBhc3NQZXJtaXNzaW9uc0FjY2VwdGVkVG9TZXR0aW5ncyIsIm1pZ3JhdGVFbmFibGVBbGxQcm9qZWN0TWNwU2VydmVyc1RvU2V0dGluZ3MiLCJtaWdyYXRlRmVubmVjVG9PcHVzIiwibWlncmF0ZUxlZ2FjeU9wdXNUb0N1cnJlbnQiLCJtaWdyYXRlT3B1c1RvT3B1czFtIiwibWlncmF0ZVJlcGxCcmlkZ2VFbmFibGVkVG9SZW1vdGVDb250cm9sQXRTdGFydHVwIiwibWlncmF0ZVNvbm5ldDFtVG9Tb25uZXQ0NSIsIm1pZ3JhdGVTb25uZXQ0NVRvU29ubmV0NDYiLCJyZXNldEF1dG9Nb2RlT3B0SW5Gb3JEZWZhdWx0T2ZmZXIiLCJyZXNldFByb1RvT3B1c0RlZmF1bHQiLCJjcmVhdGVSZW1vdGVTZXNzaW9uQ29uZmlnIiwiY3JlYXRlRGlyZWN0Q29ubmVjdFNlc3Npb24iLCJEaXJlY3RDb25uZWN0RXJyb3IiLCJpbml0aWFsaXplTHNwU2VydmVyTWFuYWdlciIsInNob3VsZEVuYWJsZVByb21wdFN1Z2dlc3Rpb24iLCJBcHBTdGF0ZSIsImdldERlZmF1bHRBcHBTdGF0ZSIsIklETEVfU1BFQ1VMQVRJT05fU1RBVEUiLCJvbkNoYW5nZUFwcFN0YXRlIiwiY3JlYXRlU3RvcmUiLCJhc1Nlc3Npb25JZCIsImZpbHRlckFsbG93ZWRTZGtCZXRhcyIsImlzSW5CdW5kbGVkTW9kZSIsImlzUnVubmluZ1dpdGhCdW4iLCJsb2dGb3JEaWFnbm9zdGljc05vUElJIiwiZmlsdGVyRXhpc3RpbmdQYXRocyIsImdldEtub3duUGF0aHNGb3JSZXBvIiwiY2xlYXJQbHVnaW5DYWNoZSIsImxvYWRBbGxQbHVnaW5zQ2FjaGVPbmx5IiwibWlncmF0ZUNoYW5nZWxvZ0Zyb21Db25maWciLCJTYW5kYm94TWFuYWdlciIsImZldGNoU2Vzc2lvbiIsInByZXBhcmVBcGlSZXF1ZXN0IiwiY2hlY2tPdXRUZWxlcG9ydGVkU2Vzc2lvbkJyYW5jaCIsInByb2Nlc3NNZXNzYWdlc0ZvclRlbGVwb3J0UmVzdW1lIiwidGVsZXBvcnRUb1JlbW90ZVdpdGhFcnJvckhhbmRsaW5nIiwidmFsaWRhdGVHaXRTdGF0ZSIsInZhbGlkYXRlU2Vzc2lvblJlcG9zaXRvcnkiLCJzaG91bGRFbmFibGVUaGlua2luZ0J5RGVmYXVsdCIsIlRoaW5raW5nQ29uZmlnIiwiaW5pdFVzZXIiLCJyZXNldFVzZXJDYWNoZSIsImdldFRtdXhJbnN0YWxsSW5zdHJ1Y3Rpb25zIiwiaXNUbXV4QXZhaWxhYmxlIiwicGFyc2VQUlJlZmVyZW5jZSIsImxvZ01hbmFnZWRTZXR0aW5ncyIsInBvbGljeVNldHRpbmdzIiwiYWxsS2V5cyIsImtleUNvdW50IiwibGVuZ3RoIiwia2V5cyIsImpvaW4iLCJpc0JlaW5nRGVidWdnZWQiLCJpc0J1biIsImhhc0luc3BlY3RBcmciLCJwcm9jZXNzIiwiZXhlY0FyZ3YiLCJzb21lIiwiYXJnIiwidGVzdCIsImhhc0luc3BlY3RFbnYiLCJlbnYiLCJOT0RFX09QVElPTlMiLCJpbnNwZWN0b3IiLCJnbG9iYWwiLCJoYXNJbnNwZWN0b3JVcmwiLCJ1cmwiLCJleGl0IiwibG9nU2Vzc2lvblRlbGVtZXRyeSIsIm1vZGVsIiwidGhlbiIsImVuYWJsZWQiLCJlcnJvcnMiLCJtYW5hZ2VkTmFtZXMiLCJjYXRjaCIsImVyciIsImdldENlcnRFbnZWYXJUZWxlbWV0cnkiLCJSZWNvcmQiLCJyZXN1bHQiLCJOT0RFX0VYVFJBX0NBX0NFUlRTIiwiaGFzX25vZGVfZXh0cmFfY2FfY2VydHMiLCJDTEFVREVfQ09ERV9DTElFTlRfQ0VSVCIsImhhc19jbGllbnRfY2VydCIsImhhc191c2Vfc3lzdGVtX2NhIiwiaGFzX3VzZV9vcGVuc3NsX2NhIiwibG9nU3RhcnR1cFRlbGVtZXRyeSIsIlByb21pc2UiLCJpc0dpdCIsIndvcmt0cmVlQ291bnQiLCJnaEF1dGhTdGF0dXMiLCJhbGwiLCJpc19naXQiLCJ3b3JrdHJlZV9jb3VudCIsImdoX2F1dGhfc3RhdHVzIiwic2FuZGJveF9lbmFibGVkIiwiaXNTYW5kYm94aW5nRW5hYmxlZCIsImFyZV91bnNhbmRib3hlZF9jb21tYW5kc19hbGxvd2VkIiwiYXJlVW5zYW5kYm94ZWRDb21tYW5kc0FsbG93ZWQiLCJpc19hdXRvX2Jhc2hfYWxsb3dlZF9pZl9zYW5kYm94X2VuYWJsZWQiLCJpc0F1dG9BbGxvd0Jhc2hJZlNhbmRib3hlZEVuYWJsZWQiLCJhdXRvX3VwZGF0ZXJfZGlzYWJsZWQiLCJwcmVmZXJzX3JlZHVjZWRfbW90aW9uIiwicHJlZmVyc1JlZHVjZWRNb3Rpb24iLCJDVVJSRU5UX01JR1JBVElPTl9WRVJTSU9OIiwicnVuTWlncmF0aW9ucyIsIm1pZ3JhdGlvblZlcnNpb24iLCJwcmV2IiwicHJlZmV0Y2hTeXN0ZW1Db250ZXh0SWZTYWZlIiwiaXNOb25JbnRlcmFjdGl2ZVNlc3Npb24iLCJoYXNUcnVzdCIsInN0YXJ0RGVmZXJyZWRQcmVmZXRjaGVzIiwiQ0xBVURFX0NPREVfRVhJVF9BRlRFUl9GSVJTVF9SRU5ERVIiLCJDTEFVREVfQ09ERV9VU0VfQkVEUk9DSyIsIkNMQVVERV9DT0RFX1NLSVBfQkVEUk9DS19BVVRIIiwiQ0xBVURFX0NPREVfVVNFX1ZFUlRFWCIsIkNMQVVERV9DT0RFX1NLSVBfVkVSVEVYX0FVVEgiLCJBYm9ydFNpZ25hbCIsInRpbWVvdXQiLCJpbml0aWFsaXplIiwibSIsInN0YXJ0RXZlbnRMb29wU3RhbGxEZXRlY3RvciIsImxvYWRTZXR0aW5nc0Zyb21GbGFnIiwic2V0dGluZ3NGaWxlIiwidHJpbW1lZFNldHRpbmdzIiwidHJpbSIsImxvb2tzTGlrZUpzb24iLCJzdGFydHNXaXRoIiwiZW5kc1dpdGgiLCJzZXR0aW5nc1BhdGgiLCJwYXJzZWRKc29uIiwic3RkZXJyIiwid3JpdGUiLCJyZWQiLCJjb250ZW50SGFzaCIsInJlc29sdmVkUGF0aCIsInJlc29sdmVkU2V0dGluZ3NQYXRoIiwiZSIsImVycm9yIiwiRXJyb3IiLCJsb2FkU2V0dGluZ1NvdXJjZXNGcm9tRmxhZyIsInNldHRpbmdTb3VyY2VzQXJnIiwic291cmNlcyIsImVhZ2VyTG9hZFNldHRpbmdzIiwidW5kZWZpbmVkIiwiaW5pdGlhbGl6ZUVudHJ5cG9pbnQiLCJpc05vbkludGVyYWN0aXZlIiwiQ0xBVURFX0NPREVfRU5UUllQT0lOVCIsImNsaUFyZ3MiLCJhcmd2Iiwic2xpY2UiLCJtY3BJbmRleCIsImluZGV4T2YiLCJDTEFVREVfQ09ERV9BQ1RJT04iLCJQZW5kaW5nQ29ubmVjdCIsImF1dGhUb2tlbiIsImRhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zIiwiX3BlbmRpbmdDb25uZWN0IiwiUGVuZGluZ0Fzc2lzdGFudENoYXQiLCJzZXNzaW9uSWQiLCJkaXNjb3ZlciIsIl9wZW5kaW5nQXNzaXN0YW50Q2hhdCIsIlBlbmRpbmdTU0giLCJob3N0IiwiY3dkIiwicGVybWlzc2lvbk1vZGUiLCJsb2NhbCIsImV4dHJhQ2xpQXJncyIsIl9wZW5kaW5nU1NIIiwibWFpbiIsIk5vRGVmYXVsdEN1cnJlbnREaXJlY3RvcnlJbkV4ZVBhdGgiLCJvbiIsInJlc2V0Q3Vyc29yIiwiaW5jbHVkZXMiLCJyYXdDbGlBcmdzIiwiY2NJZHgiLCJmaW5kSW5kZXgiLCJhIiwiY2NVcmwiLCJwYXJzZUNvbm5lY3RVcmwiLCJwYXJzZWQiLCJzdHJpcHBlZCIsImZpbHRlciIsIl8iLCJpIiwiZHNwSWR4Iiwic3BsaWNlIiwic2VydmVyVXJsIiwiaGFuZGxlVXJpSWR4IiwiZW5hYmxlQ29uZmlncyIsInVyaSIsImhhbmRsZURlZXBMaW5rVXJpIiwiZXhpdENvZGUiLCJwbGF0Zm9ybSIsIl9fQ0ZCdW5kbGVJZGVudGlmaWVyIiwiaGFuZGxlVXJsU2NoZW1lTGF1bmNoIiwidXJsU2NoZW1lUmVzdWx0IiwicmF3QXJncyIsIm5leHRBcmciLCJsb2NhbElkeCIsInBtSWR4IiwicG1FcUlkeCIsInNwbGl0IiwiZXh0cmFjdEZsYWciLCJmbGFnIiwib3B0cyIsImhhc1ZhbHVlIiwiYXMiLCJwdXNoIiwidmFsIiwiZXFJIiwiY29uc3VtZWQiLCJyZXN0IiwiaGFzUHJpbnRGbGFnIiwiaGFzSW5pdE9ubHlGbGFnIiwiaGFzU2RrVXJsIiwic3Rkb3V0IiwiaXNUVFkiLCJpc0ludGVyYWN0aXZlIiwiY2xpZW50VHlwZSIsIkdJVEhVQl9BQ1RJT05TIiwiaGFzU2Vzc2lvbkluZ3Jlc3NUb2tlbiIsIkNMQVVERV9DT0RFX1NFU1NJT05fQUNDRVNTX1RPS0VOIiwiQ0xBVURFX0NPREVfV0VCU09DS0VUX0FVVEhfRklMRV9ERVNDUklQVE9SIiwicHJldmlld0Zvcm1hdCIsIkNMQVVERV9DT0RFX1FVRVNUSU9OX1BSRVZJRVdfRk9STUFUIiwiQ0xBVURFX0NPREVfRU5WSVJPTk1FTlRfS0lORCIsInJ1biIsImdldElucHV0UHJvbXB0IiwicHJvbXB0IiwiaW5wdXRGb3JtYXQiLCJBc3luY0l0ZXJhYmxlIiwic3RkaW4iLCJzZXRFbmNvZGluZyIsImRhdGEiLCJvbkRhdGEiLCJjaHVuayIsInRpbWVkT3V0Iiwib2ZmIiwiQm9vbGVhbiIsImNyZWF0ZVNvcnRlZEhlbHBDb25maWciLCJzb3J0U3ViY29tbWFuZHMiLCJzb3J0T3B0aW9ucyIsImdldE9wdGlvblNvcnRLZXkiLCJvcHQiLCJsb25nIiwicmVwbGFjZSIsInNob3J0IiwiT2JqZWN0IiwiYXNzaWduIiwiY29uc3QiLCJjb21wYXJlT3B0aW9ucyIsImIiLCJsb2NhbGVDb21wYXJlIiwicHJvZ3JhbSIsImNvbmZpZ3VyZUhlbHAiLCJlbmFibGVQb3NpdGlvbmFsT3B0aW9ucyIsImhvb2siLCJ0aGlzQ29tbWFuZCIsIkNMQVVERV9DT0RFX0RJU0FCTEVfVEVSTUlOQUxfVElUTEUiLCJ0aXRsZSIsImluaXRTaW5rcyIsInBsdWdpbkRpciIsImdldE9wdGlvblZhbHVlIiwiQXJyYXkiLCJpc0FycmF5IiwiZXZlcnkiLCJwIiwidXBsb2FkVXNlclNldHRpbmdzSW5CYWNrZ3JvdW5kIiwibmFtZSIsImRlc2NyaXB0aW9uIiwiYXJndW1lbnQiLCJTdHJpbmciLCJoZWxwT3B0aW9uIiwib3B0aW9uIiwiX3ZhbHVlIiwiYWRkT3B0aW9uIiwiYXJnUGFyc2VyIiwiaGlkZUhlbHAiLCJjaG9pY2VzIiwiTnVtYmVyIiwidmFsdWUiLCJhbW91bnQiLCJpc05hTiIsInRva2VucyIsImlzSW50ZWdlciIsImRlZmF1bHQiLCJ2IiwibiIsImlzRmluaXRlIiwicmF3VmFsdWUiLCJ0b0xvd2VyQ2FzZSIsImFsbG93ZWQiLCJhY3Rpb24iLCJvcHRpb25zIiwiYmFyZSIsIkNMQVVERV9DT0RFX1NJTVBMRSIsImNvbnNvbGUiLCJ3YXJuIiwieWVsbG93Iiwia2Fpcm9zRW5hYmxlZCIsImFzc2lzdGFudFRlYW1Db250ZXh0IiwiQXdhaXRlZCIsIlJldHVyblR5cGUiLCJOb25OdWxsYWJsZSIsImFzc2lzdGFudCIsIm1hcmtBc3Npc3RhbnRGb3JjZWQiLCJpc0Fzc2lzdGFudE1vZGUiLCJhZ2VudElkIiwiaXNBc3Npc3RhbnRGb3JjZWQiLCJpc0thaXJvc0VuYWJsZWQiLCJicmllZiIsImluaXRpYWxpemVBc3Npc3RhbnRUZWFtIiwiZGVidWciLCJkZWJ1Z1RvU3RkZXJyIiwiYWxsb3dEYW5nZXJvdXNseVNraXBQZXJtaXNzaW9ucyIsInRvb2xzIiwiYmFzZVRvb2xzIiwiYWxsb3dlZFRvb2xzIiwiZGlzYWxsb3dlZFRvb2xzIiwibWNwQ29uZmlnIiwicGVybWlzc2lvbk1vZGVDbGkiLCJhZGREaXIiLCJmYWxsYmFja01vZGVsIiwiYmV0YXMiLCJpZGUiLCJpbmNsdWRlSG9va0V2ZW50cyIsImluY2x1ZGVQYXJ0aWFsTWVzc2FnZXMiLCJwcmVmaWxsIiwiZmlsZURvd25sb2FkUHJvbWlzZSIsImFnZW50c0pzb24iLCJhZ2VudHMiLCJhZ2VudENsaSIsImFnZW50IiwiQ0xBVURFX0NPREVfQUdFTlQiLCJvdXRwdXRGb3JtYXQiLCJ2ZXJib3NlIiwicHJpbnQiLCJpbml0T25seSIsIm1haW50ZW5hbmNlIiwiZGlzYWJsZVNsYXNoQ29tbWFuZHMiLCJ0YXNrc09wdGlvbiIsInRhc2tzIiwidGFza0xpc3RJZCIsIkNMQVVERV9DT0RFX1RBU0tfTElTVF9JRCIsIndvcmt0cmVlT3B0aW9uIiwid29ya3RyZWUiLCJ3b3JrdHJlZU5hbWUiLCJ3b3JrdHJlZUVuYWJsZWQiLCJ3b3JrdHJlZVBSTnVtYmVyIiwicHJOdW0iLCJ0bXV4RW5hYmxlZCIsInRtdXgiLCJzdG9yZWRUZWFtbWF0ZU9wdHMiLCJUZWFtbWF0ZU9wdGlvbnMiLCJ0ZWFtbWF0ZU9wdHMiLCJleHRyYWN0VGVhbW1hdGVPcHRpb25zIiwiaGFzQW55VGVhbW1hdGVPcHQiLCJhZ2VudE5hbWUiLCJ0ZWFtTmFtZSIsImhhc0FsbFJlcXVpcmVkVGVhbW1hdGVPcHRzIiwic2V0RHluYW1pY1RlYW1Db250ZXh0IiwiY29sb3IiLCJhZ2VudENvbG9yIiwicGxhbk1vZGVSZXF1aXJlZCIsInBhcmVudFNlc3Npb25JZCIsInRlYW1tYXRlTW9kZSIsInNldENsaVRlYW1tYXRlTW9kZU92ZXJyaWRlIiwic2RrVXJsIiwiZWZmZWN0aXZlSW5jbHVkZVBhcnRpYWxNZXNzYWdlcyIsIkNMQVVERV9DT0RFX0lOQ0xVREVfUEFSVElBTF9NRVNTQUdFUyIsIkNMQVVERV9DT0RFX1JFTU9URSIsInRlbGVwb3J0IiwicmVtb3RlT3B0aW9uIiwicmVtb3RlIiwicmVtb3RlQ29udHJvbE9wdGlvbiIsInJlbW90ZUNvbnRyb2wiLCJyYyIsInJlbW90ZUNvbnRyb2xOYW1lIiwiY29udGludWUiLCJyZXN1bWUiLCJmb3JrU2Vzc2lvbiIsInZhbGlkYXRlZFNlc3Npb25JZCIsImZpbGVTcGVjcyIsImZpbGUiLCJzZXNzaW9uVG9rZW4iLCJmaWxlU2Vzc2lvbklkIiwiQ0xBVURFX0NPREVfUkVNT1RFX1NFU1NJT05fSUQiLCJmaWxlcyIsImNvbmZpZyIsImJhc2VVcmwiLCJBTlRIUk9QSUNfQkFTRV9VUkwiLCJCQVNFX0FQSV9VUkwiLCJvYXV0aFRva2VuIiwic3lzdGVtUHJvbXB0Iiwic3lzdGVtUHJvbXB0RmlsZSIsImZpbGVQYXRoIiwiY29kZSIsImFwcGVuZFN5c3RlbVByb21wdCIsImFwcGVuZFN5c3RlbVByb21wdEZpbGUiLCJhZGRlbmR1bSIsIlRFQU1NQVRFX1NZU1RFTV9QUk9NUFRfQURERU5EVU0iLCJtb2RlIiwibm90aWZpY2F0aW9uIiwicGVybWlzc2lvbk1vZGVOb3RpZmljYXRpb24iLCJlbmFibGVBdXRvTW9kZSIsInNldEF1dG9Nb2RlRmxhZ0NsaSIsImR5bmFtaWNNY3BDb25maWciLCJwcm9jZXNzZWRDb25maWdzIiwibWFwIiwiYWxsQ29uZmlncyIsImFsbEVycm9ycyIsImNvbmZpZ0l0ZW0iLCJjb25maWdzIiwiY29uZmlnT2JqZWN0IiwiZXhwYW5kVmFycyIsInNjb3BlIiwibWNwU2VydmVycyIsImNvbmZpZ1BhdGgiLCJmb3JtYXR0ZWRFcnJvcnMiLCJwYXRoIiwibWVzc2FnZSIsImxldmVsIiwibm9uU2RrQ29uZmlnTmFtZXMiLCJlbnRyaWVzIiwidHlwZSIsInJlc2VydmVkTmFtZUVycm9yIiwiaXNDb21wdXRlclVzZU1DUFNlcnZlciIsIkNPTVBVVEVSX1VTRV9NQ1BfU0VSVkVSX05BTUUiLCJzY29wZWRDb25maWdzIiwiYmxvY2tlZCIsImNocm9tZU9wdHMiLCJjaHJvbWUiLCJlbmFibGVDbGF1ZGVJbkNocm9tZSIsImF1dG9FbmFibGVDbGF1ZGVJbkNocm9tZSIsImNocm9tZU1jcENvbmZpZyIsImNocm9tZU1jcFRvb2xzIiwiY2hyb21lU3lzdGVtUHJvbXB0IiwiaGludCIsIkJ1biIsInN0cmljdE1jcENvbmZpZyIsImdldENoaWNhZ29FbmFibGVkIiwic2V0dXBDb21wdXRlclVzZU1DUCIsImN1VG9vbHMiLCJkZXZDaGFubmVscyIsInBhcnNlQ2hhbm5lbEVudHJpZXMiLCJyYXciLCJiYWQiLCJjIiwiYXQiLCJraW5kIiwibWFya2V0cGxhY2UiLCJjaGFubmVsT3B0cyIsImNoYW5uZWxzIiwiZGFuZ2Vyb3VzbHlMb2FkRGV2ZWxvcG1lbnRDaGFubmVscyIsInJhd0NoYW5uZWxzIiwicmF3RGV2IiwiY2hhbm5lbEVudHJpZXMiLCJqb2luUGx1Z2luSWRzIiwiaWRzIiwiZmxhdE1hcCIsInNvcnQiLCJjaGFubmVsc19jb3VudCIsImRldl9jb3VudCIsInBsdWdpbnMiLCJkZXZfcGx1Z2lucyIsIkJSSUVGX1RPT0xfTkFNRSIsIkxFR0FDWV9CUklFRl9UT09MX05BTUUiLCJpc0JyaWVmRW50aXRsZWQiLCJpbml0UmVzdWx0IiwiYWxsb3dlZFRvb2xzQ2xpIiwiZGlzYWxsb3dlZFRvb2xzQ2xpIiwiYmFzZVRvb2xzQ2xpIiwiYWRkRGlycyIsInRvb2xQZXJtaXNzaW9uQ29udGV4dCIsIndhcm5pbmdzIiwiZGFuZ2Vyb3VzUGVybWlzc2lvbnMiLCJvdmVybHlCcm9hZEJhc2hQZXJtaXNzaW9ucyIsInBlcm1pc3Npb24iLCJydWxlRGlzcGxheSIsInNvdXJjZURpc3BsYXkiLCJmb3JFYWNoIiwid2FybmluZyIsImNsYXVkZWFpQ29uZmlnUHJvbWlzZSIsIm1jcENvbmZpZ1N0YXJ0IiwiRGF0ZSIsIm5vdyIsIm1jcENvbmZpZ1Jlc29sdmVkTXMiLCJtY3BDb25maWdQcm9taXNlIiwic2VydmVycyIsInJlcGxheVVzZXJNZXNzYWdlcyIsInNlc3Npb25QZXJzaXN0ZW5jZSIsImVmZmVjdGl2ZVByb21wdCIsImlucHV0UHJvbXB0IiwibWF5YmVBY3RpdmF0ZVByb2FjdGl2ZSIsIkNMQVVERV9DT0RFX0NPT1JESU5BVE9SX01PREUiLCJhcHBseUNvb3JkaW5hdG9yVG9vbEZpbHRlciIsImpzb25TY2hlbWEiLCJzeW50aGV0aWNPdXRwdXRSZXN1bHQiLCJ0b29sIiwic2NoZW1hX3Byb3BlcnR5X2NvdW50IiwicHJvcGVydGllcyIsImhhc19yZXF1aXJlZF9maWVsZHMiLCJyZXF1aXJlZCIsInNldHVwU3RhcnQiLCJzZXR1cCIsIm1lc3NhZ2luZ1NvY2tldFBhdGgiLCJwcmVTZXR1cEN3ZCIsInNldHVwUHJvbWlzZSIsImNvbW1hbmRzUHJvbWlzZSIsImFnZW50RGVmc1Byb21pc2UiLCJlZmZlY3RpdmVSZXBsYXlVc2VyTWVzc2FnZXMiLCJzZXNzaW9uTmFtZUFyZyIsImV4cGxpY2l0TW9kZWwiLCJBTlRIUk9QSUNfTU9ERUwiLCJjYWNoZWRHcm93dGhCb29rRmVhdHVyZXMiLCJ1c2VyU3BlY2lmaWVkTW9kZWwiLCJ1c2VyU3BlY2lmaWVkRmFsbGJhY2tNb2RlbCIsImN1cnJlbnRDd2QiLCJjb21tYW5kc1N0YXJ0IiwiY29tbWFuZHMiLCJhZ2VudERlZmluaXRpb25zUmVzdWx0IiwiY2xpQWdlbnRzIiwiYWN0aXZlQWdlbnRzIiwicGFyc2VkQWdlbnRzIiwiYWxsQWdlbnRzIiwiYWdlbnREZWZpbml0aW9ucyIsImFnZW50U2V0dGluZyIsIm1haW5UaHJlYWRBZ2VudERlZmluaXRpb24iLCJmaW5kIiwiYWdlbnRUeXBlIiwic291cmNlIiwiYWdlbnRTeXN0ZW1Qcm9tcHQiLCJnZXRTeXN0ZW1Qcm9tcHQiLCJpbml0aWFsUHJvbXB0IiwiZWZmZWN0aXZlTW9kZWwiLCJpbml0aWFsTWFpbkxvb3BNb2RlbCIsInJlc29sdmVkSW5pdGlhbE1vZGVsIiwiYWR2aXNvck1vZGVsIiwiYWR2aXNvck9wdGlvbiIsImFkdmlzb3IiLCJub3JtYWxpemVkQWR2aXNvck1vZGVsIiwiY3VzdG9tQWdlbnQiLCJjdXN0b21Qcm9tcHQiLCJtZW1vcnkiLCJhZ2VudF90eXBlIiwiY3VzdG9tSW5zdHJ1Y3Rpb25zIiwibWF5YmVBY3RpdmF0ZUJyaWVmIiwiZGVmYXVsdFZpZXciLCJwcm9hY3RpdmUiLCJDTEFVREVfQ09ERV9QUk9BQ1RJVkUiLCJpc0Nvb3JkaW5hdG9yTW9kZSIsImJyaWVmVmlzaWJpbGl0eSIsImlzQnJpZWZFbmFibGVkIiwicHJvYWN0aXZlUHJvbXB0IiwiYXNzaXN0YW50QWRkZW5kdW0iLCJnZXRBc3Npc3RhbnRTeXN0ZW1Qcm9tcHRBZGRlbmR1bSIsInJvb3QiLCJnZXRGcHNNZXRyaWNzIiwic3RhdHMiLCJjdHgiLCJjcmVhdGVSb290IiwicmVuZGVyT3B0aW9ucyIsImV2ZW50IiwiZHVyYXRpb25NcyIsIk1hdGgiLCJyb3VuZCIsInVwdGltZSIsInNldHVwU2NyZWVuc1N0YXJ0Iiwib25ib2FyZGluZ1Nob3duIiwiZ2V0QnJpZGdlRGlzYWJsZWRSZWFzb24iLCJkaXNhYmxlZFJlYXNvbiIsInBlbmRpbmdTbmFwc2hvdFVwZGF0ZSIsImFnZW50RGVmIiwiY2hvaWNlIiwic25hcHNob3RUaW1lc3RhbXAiLCJidWlsZE1lcmdlUHJvbXB0IiwibWVyZ2VQcm9tcHQiLCJjbGVhclRydXN0ZWREZXZpY2VUb2tlbiIsImVucm9sbFRydXN0ZWREZXZpY2UiLCJvcmdWYWxpZGF0aW9uIiwidmFsaWQiLCJub25NY3BFcnJvcnMiLCJtY3BFcnJvck1ldGFkYXRhIiwic2V0dGluZ3NFcnJvcnMiLCJvbkV4aXQiLCJiZ1JlZnJlc2hUaHJvdHRsZU1zIiwibGFzdFByZWZldGNoZWQiLCJzdGFydHVwUHJlZmV0Y2hlZEF0Iiwic2tpcFN0YXJ0dXBQcmVmZXRjaGVzIiwibGFzdFByZWZldGNoZWRJbmZvIiwiY3VycmVudCIsImV4aXN0aW5nTWNwQ29uZmlncyIsImFsbE1jcENvbmZpZ3MiLCJzZGtNY3BDb25maWdzIiwicmVndWxhck1jcENvbmZpZ3MiLCJ0eXBlZENvbmZpZyIsImxvY2FsTWNwUHJvbWlzZSIsImNsaWVudHMiLCJjbGF1ZGVhaU1jcFByb21pc2UiLCJtY3BQcm9taXNlIiwiY2xhdWRlYWkiLCJob29rc1Byb21pc2UiLCJob29rTWVzc2FnZXMiLCJtY3BDbGllbnRzIiwibWNwVG9vbHMiLCJtY3BDb21tYW5kcyIsInRoaW5raW5nRW5hYmxlZCIsInRoaW5raW5nQ29uZmlnIiwidGhpbmtpbmciLCJtYXhUaGlua2luZ1Rva2VucyIsIk1BWF9USElOS0lOR19UT0tFTlMiLCJwYXJzZUludCIsImJ1ZGdldFRva2VucyIsInZlcnNpb24iLCJNQUNSTyIsIlZFUlNJT04iLCJpc19uYXRpdmVfYmluYXJ5IiwibG9nVGVuZ3VJbml0IiwiaGFzSW5pdGlhbFByb21wdCIsImhhc1N0ZGluIiwibnVtQWxsb3dlZFRvb2xzIiwibnVtRGlzYWxsb3dlZFRvb2xzIiwibWNwQ2xpZW50Q291bnQiLCJza2lwV2ViRmV0Y2hQcmVmbGlnaHQiLCJnaXRodWJBY3Rpb25JbnB1dHMiLCJHSVRIVUJfQUNUSU9OX0lOUFVUUyIsImRhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zUGFzc2VkIiwibW9kZUlzQnlwYXNzIiwiYWxsb3dEYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uc1Bhc3NlZCIsInN5c3RlbVByb21wdEZsYWciLCJhcHBlbmRTeXN0ZW1Qcm9tcHRGbGFnIiwiYXNzaXN0YW50QWN0aXZhdGlvblBhdGgiLCJnZXRBc3Npc3RhbnRBY3RpdmF0aW9uUGF0aCIsInJlZ2lzdGVyZWQiLCJudW1fc2Vzc2lvbnMiLCJzZXR1cFRyaWdnZXIiLCJmb3JjZVN5bmNFeGVjdXRpb24iLCJzZXNzaW9uU3RhcnRIb29rc1Byb21pc2UiLCJjb21tYW5kc0hlYWRsZXNzIiwiY29tbWFuZCIsImRpc2FibGVOb25JbnRlcmFjdGl2ZSIsInN1cHBvcnRzTm9uSW50ZXJhY3RpdmUiLCJkZWZhdWx0U3RhdGUiLCJoZWFkbGVzc0luaXRpYWxTdGF0ZSIsIm1jcCIsImVmZm9ydFZhbHVlIiwiZWZmb3J0IiwiZmFzdE1vZGUiLCJoZWFkbGVzc1N0b3JlIiwiZ2V0U3RhdGUiLCJ1cGRhdGVDb250ZXh0Iiwic2V0U3RhdGUiLCJuZXh0Q3R4IiwiY29ubmVjdE1jcEJhdGNoIiwibGFiZWwiLCJjbGllbnQiLCJDTEFVREVfQUlfTUNQX1RJTUVPVVRfTVMiLCJjbGF1ZGVhaUNvbm5lY3QiLCJjbGF1ZGVhaUNvbmZpZ3MiLCJjbGF1ZGVhaVNpZ3MiLCJTZXQiLCJ2YWx1ZXMiLCJzaWciLCJhZGQiLCJzdXBwcmVzc2VkIiwiaGFzIiwic2l6ZSIsIm9uY2xvc2UiLCJyZXNvdXJjZXMiLCJ0IiwibWNwSW5mbyIsInNlcnZlck5hbWUiLCJub25QbHVnaW5Db25maWdzIiwiZGVkdXBlZENsYXVkZUFpIiwiY2xhdWRlYWlUaW1lciIsInNldFRpbWVvdXQiLCJjbGF1ZGVhaVRpbWVkT3V0IiwicmFjZSIsInIiLCJjbGVhclRpbWVvdXQiLCJzdGFydEJhY2tncm91bmRIb3VzZWtlZXBpbmciLCJzdGFydFNka01lbW9yeU1vbml0b3IiLCJydW5IZWFkbGVzcyIsInBlcm1pc3Npb25Qcm9tcHRUb29sTmFtZSIsInBlcm1pc3Npb25Qcm9tcHRUb29sIiwibWF4VHVybnMiLCJtYXhCdWRnZXRVc2QiLCJ0YXNrQnVkZ2V0IiwidG90YWwiLCJyZXN1bWVTZXNzaW9uQXQiLCJyZXdpbmRGaWxlcyIsImVuYWJsZUF1dGhTdGF0dXMiLCJ3b3JrbG9hZCIsImNsaV9mbGFnIiwiZW52X3ZhciIsInNldHRpbmdzX2ZpbGUiLCJzdWJzY3JpcHRpb25UeXBlIiwiZGVwcmVjYXRpb25XYXJuaW5nIiwiaW5pdGlhbE5vdGlmaWNhdGlvbnMiLCJrZXkiLCJ0ZXh0IiwicHJpb3JpdHkiLCJkaXNwbGF5TGlzdCIsImRpc3BsYXlzIiwiZWZmZWN0aXZlVG9vbFBlcm1pc3Npb25Db250ZXh0IiwiaXNQbGFuTW9kZVJlcXVpcmVkIiwiaW5pdGlhbElzQnJpZWZPbmx5IiwiZnVsbFJlbW90ZUNvbnRyb2wiLCJjY3JNaXJyb3JFbmFibGVkIiwiaXNDY3JNaXJyb3JFbmFibGVkIiwiaW5pdGlhbFN0YXRlIiwic2V0dGluZ3MiLCJhZ2VudE5hbWVSZWdpc3RyeSIsIk1hcCIsIm1haW5Mb29wTW9kZWwiLCJtYWluTG9vcE1vZGVsRm9yU2Vzc2lvbiIsImlzQnJpZWZPbmx5IiwiZXhwYW5kZWRWaWV3Iiwic2hvd1NwaW5uZXJUcmVlIiwic2hvd0V4cGFuZGVkVG9kb3MiLCJzaG93VGVhbW1hdGVNZXNzYWdlUHJldmlldyIsInNlbGVjdGVkSVBBZ2VudEluZGV4IiwiY29vcmRpbmF0b3JUYXNrSW5kZXgiLCJ2aWV3U2VsZWN0aW9uTW9kZSIsImZvb3RlclNlbGVjdGlvbiIsInBsdWdpblJlY29ubmVjdEtleSIsImRpc2FibGVkIiwiaW5zdGFsbGF0aW9uU3RhdHVzIiwibWFya2V0cGxhY2VzIiwibmVlZHNSZWZyZXNoIiwic3RhdHVzTGluZVRleHQiLCJyZW1vdGVTZXNzaW9uVXJsIiwicmVtb3RlQ29ubmVjdGlvblN0YXR1cyIsInJlbW90ZUJhY2tncm91bmRUYXNrQ291bnQiLCJyZXBsQnJpZGdlRW5hYmxlZCIsInJlcGxCcmlkZ2VFeHBsaWNpdCIsInJlcGxCcmlkZ2VPdXRib3VuZE9ubHkiLCJyZXBsQnJpZGdlQ29ubmVjdGVkIiwicmVwbEJyaWRnZVNlc3Npb25BY3RpdmUiLCJyZXBsQnJpZGdlUmVjb25uZWN0aW5nIiwicmVwbEJyaWRnZUNvbm5lY3RVcmwiLCJyZXBsQnJpZGdlU2Vzc2lvblVybCIsInJlcGxCcmlkZ2VFbnZpcm9ubWVudElkIiwicmVwbEJyaWRnZVNlc3Npb25JZCIsInJlcGxCcmlkZ2VFcnJvciIsInJlcGxCcmlkZ2VJbml0aWFsTmFtZSIsInNob3dSZW1vdGVDYWxsb3V0Iiwibm90aWZpY2F0aW9ucyIsInF1ZXVlIiwiZWxpY2l0YXRpb24iLCJ0b2RvcyIsInJlbW90ZUFnZW50VGFza1N1Z2dlc3Rpb25zIiwiZmlsZUhpc3RvcnkiLCJzbmFwc2hvdHMiLCJ0cmFja2VkRmlsZXMiLCJzbmFwc2hvdFNlcXVlbmNlIiwiYXR0cmlidXRpb24iLCJwcm9tcHRTdWdnZXN0aW9uRW5hYmxlZCIsInNlc3Npb25Ib29rcyIsImluYm94IiwibWVzc2FnZXMiLCJwcm9tcHRTdWdnZXN0aW9uIiwicHJvbXB0SWQiLCJzaG93bkF0IiwiYWNjZXB0ZWRBdCIsImdlbmVyYXRpb25SZXF1ZXN0SWQiLCJzcGVjdWxhdGlvbiIsInNwZWN1bGF0aW9uU2Vzc2lvblRpbWVTYXZlZE1zIiwic2tpbGxJbXByb3ZlbWVudCIsInN1Z2dlc3Rpb24iLCJ3b3JrZXJTYW5kYm94UGVybWlzc2lvbnMiLCJzZWxlY3RlZEluZGV4IiwicGVuZGluZ1dvcmtlclJlcXVlc3QiLCJwZW5kaW5nU2FuZGJveFJlcXVlc3QiLCJhdXRoVmVyc2lvbiIsImluaXRpYWxNZXNzYWdlIiwiY29udGVudCIsImFjdGl2ZU92ZXJsYXlzIiwidGVhbUNvbnRleHQiLCJpbml0aWFsVG9vbHMiLCJudW1TdGFydHVwcyIsInNldEltbWVkaWF0ZSIsInNlc3Npb25VcGxvYWRlclByb21pc2UiLCJ1cGxvYWRlclJlYWR5IiwibW9kIiwiY3JlYXRlU2Vzc2lvblR1cm5VcGxvYWRlciIsInNlc3Npb25Db25maWciLCJhdXRvQ29ubmVjdElkZUZsYWciLCJvblR1cm5Db21wbGV0ZSIsInVwbG9hZGVyIiwicmVzdW1lQ29udGV4dCIsIm1vZGVBcGkiLCJyZXN1bWVTdWNjZWVkZWQiLCJyZXN1bWVTdGFydCIsInBlcmZvcm1hbmNlIiwiY2xlYXJTZXNzaW9uQ2FjaGVzIiwic3VjY2VzcyIsImxvYWRlZCIsImluY2x1ZGVBdHRyaWJ1dGlvbiIsInRyYW5zY3JpcHRQYXRoIiwiZnVsbFBhdGgiLCJyZXN0b3JlZEFnZW50RGVmIiwicmVzdW1lX2R1cmF0aW9uX21zIiwiaW5pdGlhbE1lc3NhZ2VzIiwiaW5pdGlhbEZpbGVIaXN0b3J5U25hcHNob3RzIiwiZmlsZUhpc3RvcnlTbmFwc2hvdHMiLCJpbml0aWFsQ29udGVudFJlcGxhY2VtZW50cyIsImNvbnRlbnRSZXBsYWNlbWVudHMiLCJpbml0aWFsQWdlbnROYW1lIiwiaW5pdGlhbEFnZW50Q29sb3IiLCJkaXJlY3RDb25uZWN0Q29uZmlnIiwic2Vzc2lvbiIsIndvcmtEaXIiLCJjb25uZWN0SW5mb01lc3NhZ2UiLCJjcmVhdGVTU0hTZXNzaW9uIiwiY3JlYXRlTG9jYWxTU0hTZXNzaW9uIiwiU1NIU2Vzc2lvbkVycm9yIiwic3NoU2Vzc2lvbiIsImhhZFByb2dyZXNzIiwibG9jYWxWZXJzaW9uIiwib25Qcm9ncmVzcyIsIm1zZyIsInJlbW90ZUN3ZCIsInNzaEluZm9NZXNzYWdlIiwiZGlzY292ZXJBc3Npc3RhbnRTZXNzaW9ucyIsInRhcmdldFNlc3Npb25JZCIsInNlc3Npb25zIiwiaW5zdGFsbGVkRGlyIiwiYmVmb3JlRXhpdCIsImlkIiwicGlja2VkIiwiY2hlY2tBbmRSZWZyZXNoT0F1dGhUb2tlbklmTmVlZGVkIiwiZ2V0Q2xhdWRlQUlPQXV0aFRva2VucyIsImFwaUNyZWRzIiwiZ2V0QWNjZXNzVG9rZW4iLCJhY2Nlc3NUb2tlbiIsInJlbW90ZVNlc3Npb25Db25maWciLCJvcmdVVUlEIiwiaW5mb01lc3NhZ2UiLCJhc3Npc3RhbnRJbml0aWFsU3RhdGUiLCJyZW1vdGVDb21tYW5kcyIsImZyb21QciIsInByb2Nlc3NlZFJlc3VtZSIsIm1heWJlU2Vzc2lvbklkIiwic2VhcmNoVGVybSIsIm1hdGNoZWRMb2ciLCJmaWx0ZXJCeVByIiwidHJpbW1lZFZhbHVlIiwibWF0Y2hlcyIsImV4YWN0IiwiaXNSZW1vdGVUdWlFbmFibGVkIiwiaGFzX2luaXRpYWxfcHJvbXB0IiwiY3VycmVudEJyYW5jaCIsImNyZWF0ZWRTZXNzaW9uIiwiQWJvcnRDb250cm9sbGVyIiwic2lnbmFsIiwic2Vzc2lvbl9pZCIsImdldFRva2Vuc0ZvclJlbW90ZSIsImdldEFjY2Vzc1Rva2VuRm9yUmVtb3RlIiwicmVtb3RlSW5mb01lc3NhZ2UiLCJpbml0aWFsVXNlck1lc3NhZ2UiLCJyZW1vdGVJbml0aWFsU3RhdGUiLCJ0ZWxlcG9ydFJlc3VsdCIsImJyYW5jaEVycm9yIiwiYnJhbmNoIiwibG9nIiwic2Vzc2lvbkRhdGEiLCJyZXBvVmFsaWRhdGlvbiIsInN0YXR1cyIsInNlc3Npb25SZXBvIiwia25vd25QYXRocyIsImV4aXN0aW5nUGF0aHMiLCJzZWxlY3RlZFBhdGgiLCJ0YXJnZXRSZXBvIiwiaW5pdGlhbFBhdGhzIiwiY2hkaXIiLCJib2xkIiwidGVsZXBvcnRXaXRoUHJvZ3Jlc3MiLCJmb3JtYXR0ZWRNZXNzYWdlIiwicGFyc2VDY3NoYXJlSWQiLCJsb2FkQ2NzaGFyZSIsImNjc2hhcmVJZCIsImxvZ09wdGlvbiIsImVudHJ5cG9pbnQiLCJzZXNzaW9uSWRPdmVycmlkZSIsInJlc3VsdHMiLCJmYWlsZWRDb3VudCIsInJlc3VtZURhdGEiLCJpbml0aWFsU2VhcmNoUXVlcnkiLCJwZW5kaW5nSG9va01lc3NhZ2VzIiwiZGVlcExpbmtCYW5uZXIiLCJkZWVwTGlua09yaWdpbiIsImhhc19wcmVmaWxsIiwiaGFzX3JlcG8iLCJkZWVwTGlua1JlcG8iLCJwcmVmaWxsTGVuZ3RoIiwicmVwbyIsImxhc3RGZXRjaCIsImRlZXBMaW5rTGFzdEZldGNoIiwiaW1wbGllcyIsImlzUHJpbnRNb2RlIiwiaXNDY1VybCIsInBhcnNlQXN5bmMiLCJtY3BTZXJ2ZUhhbmRsZXIiLCJtY3BSZW1vdmVIYW5kbGVyIiwibWNwTGlzdEhhbmRsZXIiLCJtY3BHZXRIYW5kbGVyIiwianNvbiIsImNsaWVudFNlY3JldCIsIm1jcEFkZEpzb25IYW5kbGVyIiwibWNwQWRkRnJvbURlc2t0b3BIYW5kbGVyIiwibWNwUmVzZXRDaG9pY2VzSGFuZGxlciIsInBvcnQiLCJ1bml4Iiwid29ya3NwYWNlIiwiaWRsZVRpbWVvdXQiLCJtYXhTZXNzaW9ucyIsInJhbmRvbUJ5dGVzIiwic3RhcnRTZXJ2ZXIiLCJTZXNzaW9uTWFuYWdlciIsIkRhbmdlcm91c0JhY2tlbmQiLCJwcmludEJhbm5lciIsImNyZWF0ZVNlcnZlckxvZ2dlciIsIndyaXRlU2VydmVyTG9jayIsInJlbW92ZVNlcnZlckxvY2siLCJwcm9iZVJ1bm5pbmdTZXJ2ZXIiLCJleGlzdGluZyIsInBpZCIsImh0dHBVcmwiLCJ0b1N0cmluZyIsImlkbGVUaW1lb3V0TXMiLCJiYWNrZW5kIiwic2Vzc2lvbk1hbmFnZXIiLCJsb2dnZXIiLCJzZXJ2ZXIiLCJhY3R1YWxQb3J0Iiwic3RhcnRlZEF0Iiwic2h1dHRpbmdEb3duIiwic2h1dGRvd24iLCJzdG9wIiwiZGVzdHJveUFsbCIsIm9uY2UiLCJjb25uZWN0Q29uZmlnIiwicnVuQ29ubmVjdEhlYWRsZXNzIiwiaW50ZXJhY3RpdmUiLCJhdXRoIiwiZW1haWwiLCJzc28iLCJ1c2VDb25zb2xlIiwiYXV0aExvZ2luIiwiYXV0aFN0YXR1cyIsImF1dGhMb2dvdXQiLCJjb3dvcmtPcHRpb24iLCJwbHVnaW5DbWQiLCJhbGlhcyIsIm1hbmlmZXN0UGF0aCIsImNvd29yayIsInBsdWdpblZhbGlkYXRlSGFuZGxlciIsImF2YWlsYWJsZSIsInBsdWdpbkxpc3RIYW5kbGVyIiwibWFya2V0cGxhY2VDbWQiLCJzcGFyc2UiLCJtYXJrZXRwbGFjZUFkZEhhbmRsZXIiLCJtYXJrZXRwbGFjZUxpc3RIYW5kbGVyIiwibWFya2V0cGxhY2VSZW1vdmVIYW5kbGVyIiwibWFya2V0cGxhY2VVcGRhdGVIYW5kbGVyIiwicGx1Z2luIiwicGx1Z2luSW5zdGFsbEhhbmRsZXIiLCJrZWVwRGF0YSIsInBsdWdpblVuaW5zdGFsbEhhbmRsZXIiLCJwbHVnaW5FbmFibGVIYW5kbGVyIiwicGx1Z2luRGlzYWJsZUhhbmRsZXIiLCJwbHVnaW5VcGRhdGVIYW5kbGVyIiwic2V0dXBUb2tlbkhhbmRsZXIiLCJhZ2VudHNIYW5kbGVyIiwiYXV0b01vZGVDbWQiLCJhdXRvTW9kZURlZmF1bHRzSGFuZGxlciIsImF1dG9Nb2RlQ29uZmlnSGFuZGxlciIsImF1dG9Nb2RlQ3JpdGlxdWVIYW5kbGVyIiwiaGlkZGVuIiwiYnJpZGdlTWFpbiIsImRvY3RvckhhbmRsZXIiLCJ1cGRhdGUiLCJ1cCIsInRhcmdldCIsImxpc3QiLCJkcnlSdW4iLCJzYWZlIiwicm9sbGJhY2siLCJmb3JjZSIsImluc3RhbGxIYW5kbGVyIiwidmFsaWRhdGVMb2dJZCIsImxvZ0lkIiwibG9nSGFuZGxlciIsIm51bWJlciIsImVycm9ySGFuZGxlciIsInVzYWdlIiwiYWRkSGVscFRleHQiLCJvdXRwdXRGaWxlIiwiZXhwb3J0SGFuZGxlciIsInRhc2tDbWQiLCJzdWJqZWN0IiwidGFza0NyZWF0ZUhhbmRsZXIiLCJwZW5kaW5nIiwidGFza0xpc3RIYW5kbGVyIiwidGFza0dldEhhbmRsZXIiLCJvd25lciIsImNsZWFyT3duZXIiLCJ0YXNrVXBkYXRlSGFuZGxlciIsInRhc2tEaXJIYW5kbGVyIiwic2hlbGwiLCJvdXRwdXQiLCJjb21wbGV0aW9uSGFuZGxlciIsImluUHJvdGVjdGVkTmFtZXNwYWNlIiwidGhpbmtpbmdUeXBlIiwiaXNfc2ltcGxlIiwiaXNfY29vcmRpbmF0b3IiLCJhdXRvVXBkYXRlc0NoYW5uZWwiLCJnaXRSb290IiwicnAiLCJyZWxhdGl2ZVByb2plY3RQYXRoIiwicHJvYWN0aXZlTW9kdWxlIiwiaXNQcm9hY3RpdmVBY3RpdmUiLCJhY3RpdmF0ZVByb2FjdGl2ZSIsImJyaWVmRmxhZyIsImJyaWVmRW52IiwiQ0xBVURFX0NPREVfQlJJRUYiLCJlbnRpdGxlZCIsImdhdGVkIiwidGVybWluYWwiXSwic291cmNlcyI6WyJtYWluLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBUaGVzZSBzaWRlLWVmZmVjdHMgbXVzdCBydW4gYmVmb3JlIGFsbCBvdGhlciBpbXBvcnRzOlxuLy8gMS4gcHJvZmlsZUNoZWNrcG9pbnQgbWFya3MgZW50cnkgYmVmb3JlIGhlYXZ5IG1vZHVsZSBldmFsdWF0aW9uIGJlZ2luc1xuLy8gMi4gc3RhcnRNZG1SYXdSZWFkIGZpcmVzIE1ETSBzdWJwcm9jZXNzZXMgKHBsdXRpbC9yZWcgcXVlcnkpIHNvIHRoZXkgcnVuIGluXG4vLyAgICBwYXJhbGxlbCB3aXRoIHRoZSByZW1haW5pbmcgfjEzNW1zIG9mIGltcG9ydHMgYmVsb3dcbi8vIDMuIHN0YXJ0S2V5Y2hhaW5QcmVmZXRjaCBmaXJlcyBib3RoIG1hY09TIGtleWNoYWluIHJlYWRzIChPQXV0aCArIGxlZ2FjeSBBUElcbi8vICAgIGtleSkgaW4gcGFyYWxsZWwg4oCUIGlzUmVtb3RlTWFuYWdlZFNldHRpbmdzRWxpZ2libGUoKSBvdGhlcndpc2UgcmVhZHMgdGhlbVxuLy8gICAgc2VxdWVudGlhbGx5IHZpYSBzeW5jIHNwYXduIGluc2lkZSBhcHBseVNhZmVDb25maWdFbnZpcm9ubWVudFZhcmlhYmxlcygpXG4vLyAgICAofjY1bXMgb24gZXZlcnkgbWFjT1Mgc3RhcnR1cClcbmltcG9ydCB7IHByb2ZpbGVDaGVja3BvaW50LCBwcm9maWxlUmVwb3J0IH0gZnJvbSAnLi91dGlscy9zdGFydHVwUHJvZmlsZXIuanMnXG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tdG9wLWxldmVsLXNpZGUtZWZmZWN0c1xucHJvZmlsZUNoZWNrcG9pbnQoJ21haW5fdHN4X2VudHJ5JylcblxuaW1wb3J0IHsgc3RhcnRNZG1SYXdSZWFkIH0gZnJvbSAnLi91dGlscy9zZXR0aW5ncy9tZG0vcmF3UmVhZC5qcydcblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGN1c3RvbS1ydWxlcy9uby10b3AtbGV2ZWwtc2lkZS1lZmZlY3RzXG5zdGFydE1kbVJhd1JlYWQoKVxuXG5pbXBvcnQge1xuICBlbnN1cmVLZXljaGFpblByZWZldGNoQ29tcGxldGVkLFxuICBzdGFydEtleWNoYWluUHJlZmV0Y2gsXG59IGZyb20gJy4vdXRpbHMvc2VjdXJlU3RvcmFnZS9rZXljaGFpblByZWZldGNoLmpzJ1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY3VzdG9tLXJ1bGVzL25vLXRvcC1sZXZlbC1zaWRlLWVmZmVjdHNcbnN0YXJ0S2V5Y2hhaW5QcmVmZXRjaCgpXG5cbmltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0IHtcbiAgQ29tbWFuZCBhcyBDb21tYW5kZXJDb21tYW5kLFxuICBJbnZhbGlkQXJndW1lbnRFcnJvcixcbiAgT3B0aW9uLFxufSBmcm9tICdAY29tbWFuZGVyLWpzL2V4dHJhLXR5cGluZ3MnXG5pbXBvcnQgY2hhbGsgZnJvbSAnY2hhbGsnXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcydcbmltcG9ydCBtYXBWYWx1ZXMgZnJvbSAnbG9kYXNoLWVzL21hcFZhbHVlcy5qcydcbmltcG9ydCBwaWNrQnkgZnJvbSAnbG9kYXNoLWVzL3BpY2tCeS5qcydcbmltcG9ydCB1bmlxQnkgZnJvbSAnbG9kYXNoLWVzL3VuaXFCeS5qcydcbmltcG9ydCBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IGdldE9hdXRoQ29uZmlnIH0gZnJvbSAnLi9jb25zdGFudHMvb2F1dGguanMnXG5pbXBvcnQgeyBnZXRSZW1vdGVTZXNzaW9uVXJsIH0gZnJvbSAnLi9jb25zdGFudHMvcHJvZHVjdC5qcydcbmltcG9ydCB7IGdldFN5c3RlbUNvbnRleHQsIGdldFVzZXJDb250ZXh0IH0gZnJvbSAnLi9jb250ZXh0LmpzJ1xuaW1wb3J0IHsgaW5pdCwgaW5pdGlhbGl6ZVRlbGVtZXRyeUFmdGVyVHJ1c3QgfSBmcm9tICcuL2VudHJ5cG9pbnRzL2luaXQuanMnXG5pbXBvcnQgeyBhZGRUb0hpc3RvcnkgfSBmcm9tICcuL2hpc3RvcnkuanMnXG5pbXBvcnQgdHlwZSB7IFJvb3QgfSBmcm9tICcuL2luay5qcydcbmltcG9ydCB7IGxhdW5jaFJlcGwgfSBmcm9tICcuL3JlcGxMYXVuY2hlci5qcydcbmltcG9ydCB7XG4gIGhhc0dyb3d0aEJvb2tFbnZPdmVycmlkZSxcbiAgaW5pdGlhbGl6ZUdyb3d0aEJvb2ssXG4gIHJlZnJlc2hHcm93dGhCb29rQWZ0ZXJBdXRoQ2hhbmdlLFxufSBmcm9tICcuL3NlcnZpY2VzL2FuYWx5dGljcy9ncm93dGhib29rLmpzJ1xuaW1wb3J0IHsgZmV0Y2hCb290c3RyYXBEYXRhIH0gZnJvbSAnLi9zZXJ2aWNlcy9hcGkvYm9vdHN0cmFwLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBEb3dubG9hZFJlc3VsdCxcbiAgZG93bmxvYWRTZXNzaW9uRmlsZXMsXG4gIHR5cGUgRmlsZXNBcGlDb25maWcsXG4gIHBhcnNlRmlsZVNwZWNzLFxufSBmcm9tICcuL3NlcnZpY2VzL2FwaS9maWxlc0FwaS5qcydcbmltcG9ydCB7IHByZWZldGNoUGFzc2VzRWxpZ2liaWxpdHkgfSBmcm9tICcuL3NlcnZpY2VzL2FwaS9yZWZlcnJhbC5qcydcbmltcG9ydCB7IHByZWZldGNoT2ZmaWNpYWxNY3BVcmxzIH0gZnJvbSAnLi9zZXJ2aWNlcy9tY3Avb2ZmaWNpYWxSZWdpc3RyeS5qcydcbmltcG9ydCB0eXBlIHtcbiAgTWNwU2RrU2VydmVyQ29uZmlnLFxuICBNY3BTZXJ2ZXJDb25maWcsXG4gIFNjb3BlZE1jcFNlcnZlckNvbmZpZyxcbn0gZnJvbSAnLi9zZXJ2aWNlcy9tY3AvdHlwZXMuanMnXG5pbXBvcnQge1xuICBpc1BvbGljeUFsbG93ZWQsXG4gIGxvYWRQb2xpY3lMaW1pdHMsXG4gIHJlZnJlc2hQb2xpY3lMaW1pdHMsXG4gIHdhaXRGb3JQb2xpY3lMaW1pdHNUb0xvYWQsXG59IGZyb20gJy4vc2VydmljZXMvcG9saWN5TGltaXRzL2luZGV4LmpzJ1xuaW1wb3J0IHtcbiAgbG9hZFJlbW90ZU1hbmFnZWRTZXR0aW5ncyxcbiAgcmVmcmVzaFJlbW90ZU1hbmFnZWRTZXR0aW5ncyxcbn0gZnJvbSAnLi9zZXJ2aWNlcy9yZW1vdGVNYW5hZ2VkU2V0dGluZ3MvaW5kZXguanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xJbnB1dEpTT05TY2hlbWEgfSBmcm9tICcuL1Rvb2wuanMnXG5pbXBvcnQge1xuICBjcmVhdGVTeW50aGV0aWNPdXRwdXRUb29sLFxuICBpc1N5bnRoZXRpY091dHB1dFRvb2xFbmFibGVkLFxufSBmcm9tICcuL3Rvb2xzL1N5bnRoZXRpY091dHB1dFRvb2wvU3ludGhldGljT3V0cHV0VG9vbC5qcydcbmltcG9ydCB7IGdldFRvb2xzIH0gZnJvbSAnLi90b29scy5qcydcbmltcG9ydCB7XG4gIGNhblVzZXJDb25maWd1cmVBZHZpc29yLFxuICBnZXRJbml0aWFsQWR2aXNvclNldHRpbmcsXG4gIGlzQWR2aXNvckVuYWJsZWQsXG4gIGlzVmFsaWRBZHZpc29yTW9kZWwsXG4gIG1vZGVsU3VwcG9ydHNBZHZpc29yLFxufSBmcm9tICcuL3V0aWxzL2Fkdmlzb3IuanMnXG5pbXBvcnQgeyBpc0FnZW50U3dhcm1zRW5hYmxlZCB9IGZyb20gJy4vdXRpbHMvYWdlbnRTd2FybXNFbmFibGVkLmpzJ1xuaW1wb3J0IHsgY291bnQsIHVuaXEgfSBmcm9tICcuL3V0aWxzL2FycmF5LmpzJ1xuaW1wb3J0IHsgaW5zdGFsbEFzY2lpY2FzdFJlY29yZGVyIH0gZnJvbSAnLi91dGlscy9hc2NpaWNhc3QuanMnXG5pbXBvcnQge1xuICBnZXRTdWJzY3JpcHRpb25UeXBlLFxuICBpc0NsYXVkZUFJU3Vic2NyaWJlcixcbiAgcHJlZmV0Y2hBd3NDcmVkZW50aWFsc0FuZEJlZFJvY2tJbmZvSWZTYWZlLFxuICBwcmVmZXRjaEdjcENyZWRlbnRpYWxzSWZTYWZlLFxuICB2YWxpZGF0ZUZvcmNlTG9naW5PcmcsXG59IGZyb20gJy4vdXRpbHMvYXV0aC5qcydcbmltcG9ydCB7XG4gIGNoZWNrSGFzVHJ1c3REaWFsb2dBY2NlcHRlZCxcbiAgZ2V0R2xvYmFsQ29uZmlnLFxuICBnZXRSZW1vdGVDb250cm9sQXRTdGFydHVwLFxuICBpc0F1dG9VcGRhdGVyRGlzYWJsZWQsXG4gIHNhdmVHbG9iYWxDb25maWcsXG59IGZyb20gJy4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgc2VlZEVhcmx5SW5wdXQsIHN0b3BDYXB0dXJpbmdFYXJseUlucHV0IH0gZnJvbSAnLi91dGlscy9lYXJseUlucHV0LmpzJ1xuaW1wb3J0IHsgZ2V0SW5pdGlhbEVmZm9ydFNldHRpbmcsIHBhcnNlRWZmb3J0VmFsdWUgfSBmcm9tICcuL3V0aWxzL2VmZm9ydC5qcydcbmltcG9ydCB7XG4gIGdldEluaXRpYWxGYXN0TW9kZVNldHRpbmcsXG4gIGlzRmFzdE1vZGVFbmFibGVkLFxuICBwcmVmZXRjaEZhc3RNb2RlU3RhdHVzLFxuICByZXNvbHZlRmFzdE1vZGVTdGF0dXNGcm9tQ2FjaGUsXG59IGZyb20gJy4vdXRpbHMvZmFzdE1vZGUuanMnXG5pbXBvcnQgeyBhcHBseUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzIH0gZnJvbSAnLi91dGlscy9tYW5hZ2VkRW52LmpzJ1xuaW1wb3J0IHsgY3JlYXRlU3lzdGVtTWVzc2FnZSwgY3JlYXRlVXNlck1lc3NhZ2UgfSBmcm9tICcuL3V0aWxzL21lc3NhZ2VzLmpzJ1xuaW1wb3J0IHsgZ2V0UGxhdGZvcm0gfSBmcm9tICcuL3V0aWxzL3BsYXRmb3JtLmpzJ1xuaW1wb3J0IHsgZ2V0QmFzZVJlbmRlck9wdGlvbnMgfSBmcm9tICcuL3V0aWxzL3JlbmRlck9wdGlvbnMuanMnXG5pbXBvcnQgeyBnZXRTZXNzaW9uSW5ncmVzc0F1dGhUb2tlbiB9IGZyb20gJy4vdXRpbHMvc2Vzc2lvbkluZ3Jlc3NBdXRoLmpzJ1xuaW1wb3J0IHsgc2V0dGluZ3NDaGFuZ2VEZXRlY3RvciB9IGZyb20gJy4vdXRpbHMvc2V0dGluZ3MvY2hhbmdlRGV0ZWN0b3IuanMnXG5pbXBvcnQgeyBza2lsbENoYW5nZURldGVjdG9yIH0gZnJvbSAnLi91dGlscy9za2lsbHMvc2tpbGxDaGFuZ2VEZXRlY3Rvci5qcydcbmltcG9ydCB7IGpzb25QYXJzZSwgd3JpdGVGaWxlU3luY19ERVBSRUNBVEVEIH0gZnJvbSAnLi91dGlscy9zbG93T3BlcmF0aW9ucy5qcydcbmltcG9ydCB7IGNvbXB1dGVJbml0aWFsVGVhbUNvbnRleHQgfSBmcm9tICcuL3V0aWxzL3N3YXJtL3JlY29ubmVjdGlvbi5qcydcbmltcG9ydCB7IGluaXRpYWxpemVXYXJuaW5nSGFuZGxlciB9IGZyb20gJy4vdXRpbHMvd2FybmluZ0hhbmRsZXIuanMnXG5pbXBvcnQgeyBpc1dvcmt0cmVlTW9kZUVuYWJsZWQgfSBmcm9tICcuL3V0aWxzL3dvcmt0cmVlTW9kZUVuYWJsZWQuanMnXG5cbi8vIExhenkgcmVxdWlyZSB0byBhdm9pZCBjaXJjdWxhciBkZXBlbmRlbmN5OiB0ZWFtbWF0ZS50cyAtPiBBcHBTdGF0ZS50c3ggLT4gLi4uIC0+IG1haW4udHN4XG4vKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG5jb25zdCBnZXRUZWFtbWF0ZVV0aWxzID0gKCkgPT5cbiAgcmVxdWlyZSgnLi91dGlscy90ZWFtbWF0ZS5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vdXRpbHMvdGVhbW1hdGUuanMnKVxuY29uc3QgZ2V0VGVhbW1hdGVQcm9tcHRBZGRlbmR1bSA9ICgpID0+XG4gIHJlcXVpcmUoJy4vdXRpbHMvc3dhcm0vdGVhbW1hdGVQcm9tcHRBZGRlbmR1bS5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vdXRpbHMvc3dhcm0vdGVhbW1hdGVQcm9tcHRBZGRlbmR1bS5qcycpXG5jb25zdCBnZXRUZWFtbWF0ZU1vZGVTbmFwc2hvdCA9ICgpID0+XG4gIHJlcXVpcmUoJy4vdXRpbHMvc3dhcm0vYmFja2VuZHMvdGVhbW1hdGVNb2RlU25hcHNob3QuanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuL3V0aWxzL3N3YXJtL2JhY2tlbmRzL3RlYW1tYXRlTW9kZVNuYXBzaG90LmpzJylcbi8qIGVzbGludC1lbmFibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuLy8gRGVhZCBjb2RlIGVsaW1pbmF0aW9uOiBjb25kaXRpb25hbCBpbXBvcnQgZm9yIENPT1JESU5BVE9SX01PREVcbi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbmNvbnN0IGNvb3JkaW5hdG9yTW9kZU1vZHVsZSA9IGZlYXR1cmUoJ0NPT1JESU5BVE9SX01PREUnKVxuICA/IChyZXF1aXJlKCcuL2Nvb3JkaW5hdG9yL2Nvb3JkaW5hdG9yTW9kZS5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vY29vcmRpbmF0b3IvY29vcmRpbmF0b3JNb2RlLmpzJykpXG4gIDogbnVsbFxuLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG4vLyBEZWFkIGNvZGUgZWxpbWluYXRpb246IGNvbmRpdGlvbmFsIGltcG9ydCBmb3IgS0FJUk9TIChhc3Npc3RhbnQgbW9kZSlcbi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbmNvbnN0IGFzc2lzdGFudE1vZHVsZSA9IGZlYXR1cmUoJ0tBSVJPUycpXG4gID8gKHJlcXVpcmUoJy4vYXNzaXN0YW50L2luZGV4LmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi9hc3Npc3RhbnQvaW5kZXguanMnKSlcbiAgOiBudWxsXG5jb25zdCBrYWlyb3NHYXRlID0gZmVhdHVyZSgnS0FJUk9TJylcbiAgPyAocmVxdWlyZSgnLi9hc3Npc3RhbnQvZ2F0ZS5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vYXNzaXN0YW50L2dhdGUuanMnKSlcbiAgOiBudWxsXG5cbmltcG9ydCB7IHJlbGF0aXZlLCByZXNvbHZlIH0gZnJvbSAncGF0aCdcbmltcG9ydCB7IGlzQW5hbHl0aWNzRGlzYWJsZWQgfSBmcm9tICdzcmMvc2VydmljZXMvYW5hbHl0aWNzL2NvbmZpZy5qcydcbmltcG9ydCB7IGdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFIH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9ncm93dGhib29rLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7IGluaXRpYWxpemVBbmFseXRpY3NHYXRlcyB9IGZyb20gJ3NyYy9zZXJ2aWNlcy9hbmFseXRpY3Mvc2luay5qcydcbmltcG9ydCB7XG4gIGdldE9yaWdpbmFsQ3dkLFxuICBzZXRBZGRpdGlvbmFsRGlyZWN0b3JpZXNGb3JDbGF1ZGVNZCxcbiAgc2V0SXNSZW1vdGVNb2RlLFxuICBzZXRNYWluTG9vcE1vZGVsT3ZlcnJpZGUsXG4gIHNldE1haW5UaHJlYWRBZ2VudFR5cGUsXG4gIHNldFRlbGVwb3J0ZWRTZXNzaW9uSW5mbyxcbn0gZnJvbSAnLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgeyBmaWx0ZXJDb21tYW5kc0ZvclJlbW90ZU1vZGUsIGdldENvbW1hbmRzIH0gZnJvbSAnLi9jb21tYW5kcy5qcydcbmltcG9ydCB0eXBlIHsgU3RhdHNTdG9yZSB9IGZyb20gJy4vY29udGV4dC9zdGF0cy5qcydcbmltcG9ydCB7XG4gIGxhdW5jaEFzc2lzdGFudEluc3RhbGxXaXphcmQsXG4gIGxhdW5jaEFzc2lzdGFudFNlc3Npb25DaG9vc2VyLFxuICBsYXVuY2hJbnZhbGlkU2V0dGluZ3NEaWFsb2csXG4gIGxhdW5jaFJlc3VtZUNob29zZXIsXG4gIGxhdW5jaFNuYXBzaG90VXBkYXRlRGlhbG9nLFxuICBsYXVuY2hUZWxlcG9ydFJlcG9NaXNtYXRjaERpYWxvZyxcbiAgbGF1bmNoVGVsZXBvcnRSZXN1bWVXcmFwcGVyLFxufSBmcm9tICcuL2RpYWxvZ0xhdW5jaGVycy5qcydcbmltcG9ydCB7IFNIT1dfQ1VSU09SIH0gZnJvbSAnLi9pbmsvdGVybWlvL2RlYy5qcydcbmltcG9ydCB7XG4gIGV4aXRXaXRoRXJyb3IsXG4gIGV4aXRXaXRoTWVzc2FnZSxcbiAgZ2V0UmVuZGVyQ29udGV4dCxcbiAgcmVuZGVyQW5kUnVuLFxuICBzaG93U2V0dXBTY3JlZW5zLFxufSBmcm9tICcuL2ludGVyYWN0aXZlSGVscGVycy5qcydcbmltcG9ydCB7IGluaXRCdWlsdGluUGx1Z2lucyB9IGZyb20gJy4vcGx1Z2lucy9idW5kbGVkL2luZGV4LmpzJ1xuLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG5pbXBvcnQgeyBjaGVja1F1b3RhU3RhdHVzIH0gZnJvbSAnLi9zZXJ2aWNlcy9jbGF1ZGVBaUxpbWl0cy5qcydcbmltcG9ydCB7XG4gIGdldE1jcFRvb2xzQ29tbWFuZHNBbmRSZXNvdXJjZXMsXG4gIHByZWZldGNoQWxsTWNwUmVzb3VyY2VzLFxufSBmcm9tICcuL3NlcnZpY2VzL21jcC9jbGllbnQuanMnXG5pbXBvcnQge1xuICBWQUxJRF9JTlNUQUxMQUJMRV9TQ09QRVMsXG4gIFZBTElEX1VQREFURV9TQ09QRVMsXG59IGZyb20gJy4vc2VydmljZXMvcGx1Z2lucy9wbHVnaW5DbGlDb21tYW5kcy5qcydcbmltcG9ydCB7IGluaXRCdW5kbGVkU2tpbGxzIH0gZnJvbSAnLi9za2lsbHMvYnVuZGxlZC9pbmRleC5qcydcbmltcG9ydCB0eXBlIHsgQWdlbnRDb2xvck5hbWUgfSBmcm9tICcuL3Rvb2xzL0FnZW50VG9vbC9hZ2VudENvbG9yTWFuYWdlci5qcydcbmltcG9ydCB7XG4gIGdldEFjdGl2ZUFnZW50c0Zyb21MaXN0LFxuICBnZXRBZ2VudERlZmluaXRpb25zV2l0aE92ZXJyaWRlcyxcbiAgaXNCdWlsdEluQWdlbnQsXG4gIGlzQ3VzdG9tQWdlbnQsXG4gIHBhcnNlQWdlbnRzRnJvbUpzb24sXG59IGZyb20gJy4vdG9vbHMvQWdlbnRUb29sL2xvYWRBZ2VudHNEaXIuanMnXG5pbXBvcnQgdHlwZSB7IExvZ09wdGlvbiB9IGZyb20gJy4vdHlwZXMvbG9ncy5qcydcbmltcG9ydCB0eXBlIHsgTWVzc2FnZSBhcyBNZXNzYWdlVHlwZSB9IGZyb20gJy4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IGFzc2VydE1pblZlcnNpb24gfSBmcm9tICcuL3V0aWxzL2F1dG9VcGRhdGVyLmpzJ1xuaW1wb3J0IHtcbiAgQ0xBVURFX0lOX0NIUk9NRV9TS0lMTF9ISU5ULFxuICBDTEFVREVfSU5fQ0hST01FX1NLSUxMX0hJTlRfV0lUSF9XRUJCUk9XU0VSLFxufSBmcm9tICcuL3V0aWxzL2NsYXVkZUluQ2hyb21lL3Byb21wdC5qcydcbmltcG9ydCB7XG4gIHNldHVwQ2xhdWRlSW5DaHJvbWUsXG4gIHNob3VsZEF1dG9FbmFibGVDbGF1ZGVJbkNocm9tZSxcbiAgc2hvdWxkRW5hYmxlQ2xhdWRlSW5DaHJvbWUsXG59IGZyb20gJy4vdXRpbHMvY2xhdWRlSW5DaHJvbWUvc2V0dXAuanMnXG5pbXBvcnQgeyBnZXRDb250ZXh0V2luZG93Rm9yTW9kZWwgfSBmcm9tICcuL3V0aWxzL2NvbnRleHQuanMnXG5pbXBvcnQgeyBsb2FkQ29udmVyc2F0aW9uRm9yUmVzdW1lIH0gZnJvbSAnLi91dGlscy9jb252ZXJzYXRpb25SZWNvdmVyeS5qcydcbmltcG9ydCB7IGJ1aWxkRGVlcExpbmtCYW5uZXIgfSBmcm9tICcuL3V0aWxzL2RlZXBMaW5rL2Jhbm5lci5qcydcbmltcG9ydCB7XG4gIGhhc05vZGVPcHRpb24sXG4gIGlzQmFyZU1vZGUsXG4gIGlzRW52VHJ1dGh5LFxuICBpc0luUHJvdGVjdGVkTmFtZXNwYWNlLFxufSBmcm9tICcuL3V0aWxzL2VudlV0aWxzLmpzJ1xuaW1wb3J0IHsgcmVmcmVzaEV4YW1wbGVDb21tYW5kcyB9IGZyb20gJy4vdXRpbHMvZXhhbXBsZUNvbW1hbmRzLmpzJ1xuaW1wb3J0IHR5cGUgeyBGcHNNZXRyaWNzIH0gZnJvbSAnLi91dGlscy9mcHNUcmFja2VyLmpzJ1xuaW1wb3J0IHsgZ2V0V29ya3RyZWVQYXRocyB9IGZyb20gJy4vdXRpbHMvZ2V0V29ya3RyZWVQYXRocy5qcydcbmltcG9ydCB7XG4gIGZpbmRHaXRSb290LFxuICBnZXRCcmFuY2gsXG4gIGdldElzR2l0LFxuICBnZXRXb3JrdHJlZUNvdW50LFxufSBmcm9tICcuL3V0aWxzL2dpdC5qcydcbmltcG9ydCB7IGdldEdoQXV0aFN0YXR1cyB9IGZyb20gJy4vdXRpbHMvZ2l0aHViL2doQXV0aFN0YXR1cy5qcydcbmltcG9ydCB7IHNhZmVQYXJzZUpTT04gfSBmcm9tICcuL3V0aWxzL2pzb24uanMnXG5pbXBvcnQgeyBsb2dFcnJvciB9IGZyb20gJy4vdXRpbHMvbG9nLmpzJ1xuaW1wb3J0IHsgZ2V0TW9kZWxEZXByZWNhdGlvbldhcm5pbmcgfSBmcm9tICcuL3V0aWxzL21vZGVsL2RlcHJlY2F0aW9uLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0RGVmYXVsdE1haW5Mb29wTW9kZWwsXG4gIGdldFVzZXJTcGVjaWZpZWRNb2RlbFNldHRpbmcsXG4gIG5vcm1hbGl6ZU1vZGVsU3RyaW5nRm9yQVBJLFxuICBwYXJzZVVzZXJTcGVjaWZpZWRNb2RlbCxcbn0gZnJvbSAnLi91dGlscy9tb2RlbC9tb2RlbC5qcydcbmltcG9ydCB7IGVuc3VyZU1vZGVsU3RyaW5nc0luaXRpYWxpemVkIH0gZnJvbSAnLi91dGlscy9tb2RlbC9tb2RlbFN0cmluZ3MuanMnXG5pbXBvcnQgeyBQRVJNSVNTSU9OX01PREVTIH0gZnJvbSAnLi91dGlscy9wZXJtaXNzaW9ucy9QZXJtaXNzaW9uTW9kZS5qcydcbmltcG9ydCB7XG4gIGNoZWNrQW5kRGlzYWJsZUJ5cGFzc1Blcm1pc3Npb25zLFxuICBnZXRBdXRvTW9kZUVuYWJsZWRTdGF0ZUlmQ2FjaGVkLFxuICBpbml0aWFsaXplVG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICBpbml0aWFsUGVybWlzc2lvbk1vZGVGcm9tQ0xJLFxuICBpc0RlZmF1bHRQZXJtaXNzaW9uTW9kZUF1dG8sXG4gIHBhcnNlVG9vbExpc3RGcm9tQ0xJLFxuICByZW1vdmVEYW5nZXJvdXNQZXJtaXNzaW9ucyxcbiAgc3RyaXBEYW5nZXJvdXNQZXJtaXNzaW9uc0ZvckF1dG9Nb2RlLFxuICB2ZXJpZnlBdXRvTW9kZUdhdGVBY2Nlc3MsXG59IGZyb20gJy4vdXRpbHMvcGVybWlzc2lvbnMvcGVybWlzc2lvblNldHVwLmpzJ1xuaW1wb3J0IHsgY2xlYW51cE9ycGhhbmVkUGx1Z2luVmVyc2lvbnNJbkJhY2tncm91bmQgfSBmcm9tICcuL3V0aWxzL3BsdWdpbnMvY2FjaGVVdGlscy5qcydcbmltcG9ydCB7IGluaXRpYWxpemVWZXJzaW9uZWRQbHVnaW5zIH0gZnJvbSAnLi91dGlscy9wbHVnaW5zL2luc3RhbGxlZFBsdWdpbnNNYW5hZ2VyLmpzJ1xuaW1wb3J0IHsgZ2V0TWFuYWdlZFBsdWdpbk5hbWVzIH0gZnJvbSAnLi91dGlscy9wbHVnaW5zL21hbmFnZWRQbHVnaW5zLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYkV4Y2x1c2lvbnNGb3JQbHVnaW5DYWNoZSB9IGZyb20gJy4vdXRpbHMvcGx1Z2lucy9vcnBoYW5lZFBsdWdpbkZpbHRlci5qcydcbmltcG9ydCB7IGdldFBsdWdpblNlZWREaXJzIH0gZnJvbSAnLi91dGlscy9wbHVnaW5zL3BsdWdpbkRpcmVjdG9yaWVzLmpzJ1xuaW1wb3J0IHsgY291bnRGaWxlc1JvdW5kZWRSZyB9IGZyb20gJy4vdXRpbHMvcmlwZ3JlcC5qcydcbmltcG9ydCB7XG4gIHByb2Nlc3NTZXNzaW9uU3RhcnRIb29rcyxcbiAgcHJvY2Vzc1NldHVwSG9va3MsXG59IGZyb20gJy4vdXRpbHMvc2Vzc2lvblN0YXJ0LmpzJ1xuaW1wb3J0IHtcbiAgY2FjaGVTZXNzaW9uVGl0bGUsXG4gIGdldFNlc3Npb25JZEZyb21Mb2csXG4gIGxvYWRUcmFuc2NyaXB0RnJvbUZpbGUsXG4gIHNhdmVBZ2VudFNldHRpbmcsXG4gIHNhdmVNb2RlLFxuICBzZWFyY2hTZXNzaW9uc0J5Q3VzdG9tVGl0bGUsXG4gIHNlc3Npb25JZEV4aXN0cyxcbn0gZnJvbSAnLi91dGlscy9zZXNzaW9uU3RvcmFnZS5qcydcbmltcG9ydCB7IGVuc3VyZU1kbVNldHRpbmdzTG9hZGVkIH0gZnJvbSAnLi91dGlscy9zZXR0aW5ncy9tZG0vc2V0dGluZ3MuanMnXG5pbXBvcnQge1xuICBnZXRJbml0aWFsU2V0dGluZ3MsXG4gIGdldE1hbmFnZWRTZXR0aW5nc0tleXNGb3JMb2dnaW5nLFxuICBnZXRTZXR0aW5nc0ZvclNvdXJjZSxcbiAgZ2V0U2V0dGluZ3NXaXRoRXJyb3JzLFxufSBmcm9tICcuL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuaW1wb3J0IHsgcmVzZXRTZXR0aW5nc0NhY2hlIH0gZnJvbSAnLi91dGlscy9zZXR0aW5ncy9zZXR0aW5nc0NhY2hlLmpzJ1xuaW1wb3J0IHR5cGUgeyBWYWxpZGF0aW9uRXJyb3IgfSBmcm9tICcuL3V0aWxzL3NldHRpbmdzL3ZhbGlkYXRpb24uanMnXG5pbXBvcnQge1xuICBERUZBVUxUX1RBU0tTX01PREVfVEFTS19MSVNUX0lELFxuICBUQVNLX1NUQVRVU0VTLFxufSBmcm9tICcuL3V0aWxzL3Rhc2tzLmpzJ1xuaW1wb3J0IHtcbiAgbG9nUGx1Z2luTG9hZEVycm9ycyxcbiAgbG9nUGx1Z2luc0VuYWJsZWRGb3JTZXNzaW9uLFxufSBmcm9tICcuL3V0aWxzL3RlbGVtZXRyeS9wbHVnaW5UZWxlbWV0cnkuanMnXG5pbXBvcnQgeyBsb2dTa2lsbHNMb2FkZWQgfSBmcm9tICcuL3V0aWxzL3RlbGVtZXRyeS9za2lsbExvYWRlZEV2ZW50LmpzJ1xuaW1wb3J0IHsgZ2VuZXJhdGVUZW1wRmlsZVBhdGggfSBmcm9tICcuL3V0aWxzL3RlbXBmaWxlLmpzJ1xuaW1wb3J0IHsgdmFsaWRhdGVVdWlkIH0gZnJvbSAnLi91dGlscy91dWlkLmpzJ1xuLy8gUGx1Z2luIHN0YXJ0dXAgY2hlY2tzIGFyZSBub3cgaGFuZGxlZCBub24tYmxvY2tpbmdseSBpbiBSRVBMLnRzeFxuXG5pbXBvcnQgeyByZWdpc3Rlck1jcEFkZENvbW1hbmQgfSBmcm9tICdzcmMvY29tbWFuZHMvbWNwL2FkZENvbW1hbmQuanMnXG5pbXBvcnQgeyByZWdpc3Rlck1jcFhhYUlkcENvbW1hbmQgfSBmcm9tICdzcmMvY29tbWFuZHMvbWNwL3hhYUlkcENvbW1hbmQuanMnXG5pbXBvcnQgeyBsb2dQZXJtaXNzaW9uQ29udGV4dEZvckFudHMgfSBmcm9tICdzcmMvc2VydmljZXMvaW50ZXJuYWxMb2dnaW5nLmpzJ1xuaW1wb3J0IHsgZmV0Y2hDbGF1ZGVBSU1jcENvbmZpZ3NJZkVsaWdpYmxlIH0gZnJvbSAnc3JjL3NlcnZpY2VzL21jcC9jbGF1ZGVhaS5qcydcbmltcG9ydCB7IGNsZWFyU2VydmVyQ2FjaGUgfSBmcm9tICdzcmMvc2VydmljZXMvbWNwL2NsaWVudC5qcydcbmltcG9ydCB7XG4gIGFyZU1jcENvbmZpZ3NBbGxvd2VkV2l0aEVudGVycHJpc2VNY3BDb25maWcsXG4gIGRlZHVwQ2xhdWRlQWlNY3BTZXJ2ZXJzLFxuICBkb2VzRW50ZXJwcmlzZU1jcENvbmZpZ0V4aXN0LFxuICBmaWx0ZXJNY3BTZXJ2ZXJzQnlQb2xpY3ksXG4gIGdldENsYXVkZUNvZGVNY3BDb25maWdzLFxuICBnZXRNY3BTZXJ2ZXJTaWduYXR1cmUsXG4gIHBhcnNlTWNwQ29uZmlnLFxuICBwYXJzZU1jcENvbmZpZ0Zyb21GaWxlUGF0aCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL21jcC9jb25maWcuanMnXG5pbXBvcnQge1xuICBleGNsdWRlQ29tbWFuZHNCeVNlcnZlcixcbiAgZXhjbHVkZVJlc291cmNlc0J5U2VydmVyLFxufSBmcm9tICdzcmMvc2VydmljZXMvbWNwL3V0aWxzLmpzJ1xuaW1wb3J0IHsgaXNYYWFFbmFibGVkIH0gZnJvbSAnc3JjL3NlcnZpY2VzL21jcC94YWFJZHBMb2dpbi5qcydcbmltcG9ydCB7IGdldFJlbGV2YW50VGlwcyB9IGZyb20gJ3NyYy9zZXJ2aWNlcy90aXBzL3RpcFJlZ2lzdHJ5LmpzJ1xuaW1wb3J0IHsgbG9nQ29udGV4dE1ldHJpY3MgfSBmcm9tICdzcmMvdXRpbHMvYXBpLmpzJ1xuaW1wb3J0IHtcbiAgQ0xBVURFX0lOX0NIUk9NRV9NQ1BfU0VSVkVSX05BTUUsXG4gIGlzQ2xhdWRlSW5DaHJvbWVNQ1BTZXJ2ZXIsXG59IGZyb20gJ3NyYy91dGlscy9jbGF1ZGVJbkNocm9tZS9jb21tb24uanMnXG5pbXBvcnQgeyByZWdpc3RlckNsZWFudXAgfSBmcm9tICdzcmMvdXRpbHMvY2xlYW51cFJlZ2lzdHJ5LmpzJ1xuaW1wb3J0IHsgZWFnZXJQYXJzZUNsaUZsYWcgfSBmcm9tICdzcmMvdXRpbHMvY2xpQXJncy5qcydcbmltcG9ydCB7IGNyZWF0ZUVtcHR5QXR0cmlidXRpb25TdGF0ZSB9IGZyb20gJ3NyYy91dGlscy9jb21taXRBdHRyaWJ1dGlvbi5qcydcbmltcG9ydCB7XG4gIGNvdW50Q29uY3VycmVudFNlc3Npb25zLFxuICByZWdpc3RlclNlc3Npb24sXG4gIHVwZGF0ZVNlc3Npb25OYW1lLFxufSBmcm9tICdzcmMvdXRpbHMvY29uY3VycmVudFNlc3Npb25zLmpzJ1xuaW1wb3J0IHsgZ2V0Q3dkIH0gZnJvbSAnc3JjL3V0aWxzL2N3ZC5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZywgc2V0SGFzRm9ybWF0dGVkT3V0cHV0IH0gZnJvbSAnc3JjL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHtcbiAgZXJyb3JNZXNzYWdlLFxuICBnZXRFcnJub0NvZGUsXG4gIGlzRU5PRU5ULFxuICBUZWxlcG9ydE9wZXJhdGlvbkVycm9yLFxuICB0b0Vycm9yLFxufSBmcm9tICdzcmMvdXRpbHMvZXJyb3JzLmpzJ1xuaW1wb3J0IHsgZ2V0RnNJbXBsZW1lbnRhdGlvbiwgc2FmZVJlc29sdmVQYXRoIH0gZnJvbSAnc3JjL3V0aWxzL2ZzT3BlcmF0aW9ucy5qcydcbmltcG9ydCB7XG4gIGdyYWNlZnVsU2h1dGRvd24sXG4gIGdyYWNlZnVsU2h1dGRvd25TeW5jLFxufSBmcm9tICdzcmMvdXRpbHMvZ3JhY2VmdWxTaHV0ZG93bi5qcydcbmltcG9ydCB7IHNldEFsbEhvb2tFdmVudHNFbmFibGVkIH0gZnJvbSAnc3JjL3V0aWxzL2hvb2tzL2hvb2tFdmVudHMuanMnXG5pbXBvcnQgeyByZWZyZXNoTW9kZWxDYXBhYmlsaXRpZXMgfSBmcm9tICdzcmMvdXRpbHMvbW9kZWwvbW9kZWxDYXBhYmlsaXRpZXMuanMnXG5pbXBvcnQgeyBwZWVrRm9yU3RkaW5EYXRhLCB3cml0ZVRvU3RkZXJyIH0gZnJvbSAnc3JjL3V0aWxzL3Byb2Nlc3MuanMnXG5pbXBvcnQgeyBzZXRDd2QgfSBmcm9tICdzcmMvdXRpbHMvU2hlbGwuanMnXG5pbXBvcnQge1xuICB0eXBlIFByb2Nlc3NlZFJlc3VtZSxcbiAgcHJvY2Vzc1Jlc3VtZWRDb252ZXJzYXRpb24sXG59IGZyb20gJ3NyYy91dGlscy9zZXNzaW9uUmVzdG9yZS5qcydcbmltcG9ydCB7IHBhcnNlU2V0dGluZ1NvdXJjZXNGbGFnIH0gZnJvbSAnc3JjL3V0aWxzL3NldHRpbmdzL2NvbnN0YW50cy5qcydcbmltcG9ydCB7IHBsdXJhbCB9IGZyb20gJ3NyYy91dGlscy9zdHJpbmdVdGlscy5qcydcbmltcG9ydCB7XG4gIHR5cGUgQ2hhbm5lbEVudHJ5LFxuICBnZXRJbml0aWFsTWFpbkxvb3BNb2RlbCxcbiAgZ2V0SXNOb25JbnRlcmFjdGl2ZVNlc3Npb24sXG4gIGdldFNka0JldGFzLFxuICBnZXRTZXNzaW9uSWQsXG4gIGdldFVzZXJNc2dPcHRJbixcbiAgc2V0QWxsb3dlZENoYW5uZWxzLFxuICBzZXRBbGxvd2VkU2V0dGluZ1NvdXJjZXMsXG4gIHNldENocm9tZUZsYWdPdmVycmlkZSxcbiAgc2V0Q2xpZW50VHlwZSxcbiAgc2V0Q3dkU3RhdGUsXG4gIHNldERpcmVjdENvbm5lY3RTZXJ2ZXJVcmwsXG4gIHNldEZsYWdTZXR0aW5nc1BhdGgsXG4gIHNldEluaXRpYWxNYWluTG9vcE1vZGVsLFxuICBzZXRJbmxpbmVQbHVnaW5zLFxuICBzZXRJc0ludGVyYWN0aXZlLFxuICBzZXRLYWlyb3NBY3RpdmUsXG4gIHNldE9yaWdpbmFsQ3dkLFxuICBzZXRRdWVzdGlvblByZXZpZXdGb3JtYXQsXG4gIHNldFNka0JldGFzLFxuICBzZXRTZXNzaW9uQnlwYXNzUGVybWlzc2lvbnNNb2RlLFxuICBzZXRTZXNzaW9uUGVyc2lzdGVuY2VEaXNhYmxlZCxcbiAgc2V0U2Vzc2lvblNvdXJjZSxcbiAgc2V0VXNlck1zZ09wdEluLFxuICBzd2l0Y2hTZXNzaW9uLFxufSBmcm9tICcuL2Jvb3RzdHJhcC9zdGF0ZS5qcydcblxuLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuY29uc3QgYXV0b01vZGVTdGF0ZU1vZHVsZSA9IGZlYXR1cmUoJ1RSQU5TQ1JJUFRfQ0xBU1NJRklFUicpXG4gID8gKHJlcXVpcmUoJy4vdXRpbHMvcGVybWlzc2lvbnMvYXV0b01vZGVTdGF0ZS5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vdXRpbHMvcGVybWlzc2lvbnMvYXV0b01vZGVTdGF0ZS5qcycpKVxuICA6IG51bGxcblxuLy8gVGVsZXBvcnRSZXBvTWlzbWF0Y2hEaWFsb2csIFRlbGVwb3J0UmVzdW1lV3JhcHBlciBkeW5hbWljYWxseSBpbXBvcnRlZCBhdCBjYWxsIHNpdGVzXG5pbXBvcnQgeyBtaWdyYXRlQXV0b1VwZGF0ZXNUb1NldHRpbmdzIH0gZnJvbSAnLi9taWdyYXRpb25zL21pZ3JhdGVBdXRvVXBkYXRlc1RvU2V0dGluZ3MuanMnXG5pbXBvcnQgeyBtaWdyYXRlQnlwYXNzUGVybWlzc2lvbnNBY2NlcHRlZFRvU2V0dGluZ3MgfSBmcm9tICcuL21pZ3JhdGlvbnMvbWlncmF0ZUJ5cGFzc1Blcm1pc3Npb25zQWNjZXB0ZWRUb1NldHRpbmdzLmpzJ1xuaW1wb3J0IHsgbWlncmF0ZUVuYWJsZUFsbFByb2plY3RNY3BTZXJ2ZXJzVG9TZXR0aW5ncyB9IGZyb20gJy4vbWlncmF0aW9ucy9taWdyYXRlRW5hYmxlQWxsUHJvamVjdE1jcFNlcnZlcnNUb1NldHRpbmdzLmpzJ1xuaW1wb3J0IHsgbWlncmF0ZUZlbm5lY1RvT3B1cyB9IGZyb20gJy4vbWlncmF0aW9ucy9taWdyYXRlRmVubmVjVG9PcHVzLmpzJ1xuaW1wb3J0IHsgbWlncmF0ZUxlZ2FjeU9wdXNUb0N1cnJlbnQgfSBmcm9tICcuL21pZ3JhdGlvbnMvbWlncmF0ZUxlZ2FjeU9wdXNUb0N1cnJlbnQuanMnXG5pbXBvcnQgeyBtaWdyYXRlT3B1c1RvT3B1czFtIH0gZnJvbSAnLi9taWdyYXRpb25zL21pZ3JhdGVPcHVzVG9PcHVzMW0uanMnXG5pbXBvcnQgeyBtaWdyYXRlUmVwbEJyaWRnZUVuYWJsZWRUb1JlbW90ZUNvbnRyb2xBdFN0YXJ0dXAgfSBmcm9tICcuL21pZ3JhdGlvbnMvbWlncmF0ZVJlcGxCcmlkZ2VFbmFibGVkVG9SZW1vdGVDb250cm9sQXRTdGFydHVwLmpzJ1xuaW1wb3J0IHsgbWlncmF0ZVNvbm5ldDFtVG9Tb25uZXQ0NSB9IGZyb20gJy4vbWlncmF0aW9ucy9taWdyYXRlU29ubmV0MW1Ub1Nvbm5ldDQ1LmpzJ1xuaW1wb3J0IHsgbWlncmF0ZVNvbm5ldDQ1VG9Tb25uZXQ0NiB9IGZyb20gJy4vbWlncmF0aW9ucy9taWdyYXRlU29ubmV0NDVUb1Nvbm5ldDQ2LmpzJ1xuaW1wb3J0IHsgcmVzZXRBdXRvTW9kZU9wdEluRm9yRGVmYXVsdE9mZmVyIH0gZnJvbSAnLi9taWdyYXRpb25zL3Jlc2V0QXV0b01vZGVPcHRJbkZvckRlZmF1bHRPZmZlci5qcydcbmltcG9ydCB7IHJlc2V0UHJvVG9PcHVzRGVmYXVsdCB9IGZyb20gJy4vbWlncmF0aW9ucy9yZXNldFByb1RvT3B1c0RlZmF1bHQuanMnXG5pbXBvcnQgeyBjcmVhdGVSZW1vdGVTZXNzaW9uQ29uZmlnIH0gZnJvbSAnLi9yZW1vdGUvUmVtb3RlU2Vzc2lvbk1hbmFnZXIuanMnXG4vKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbi8vIHRlbGVwb3J0V2l0aFByb2dyZXNzIGR5bmFtaWNhbGx5IGltcG9ydGVkIGF0IGNhbGwgc2l0ZVxuaW1wb3J0IHtcbiAgY3JlYXRlRGlyZWN0Q29ubmVjdFNlc3Npb24sXG4gIERpcmVjdENvbm5lY3RFcnJvcixcbn0gZnJvbSAnLi9zZXJ2ZXIvY3JlYXRlRGlyZWN0Q29ubmVjdFNlc3Npb24uanMnXG5pbXBvcnQgeyBpbml0aWFsaXplTHNwU2VydmVyTWFuYWdlciB9IGZyb20gJy4vc2VydmljZXMvbHNwL21hbmFnZXIuanMnXG5pbXBvcnQgeyBzaG91bGRFbmFibGVQcm9tcHRTdWdnZXN0aW9uIH0gZnJvbSAnLi9zZXJ2aWNlcy9Qcm9tcHRTdWdnZXN0aW9uL3Byb21wdFN1Z2dlc3Rpb24uanMnXG5pbXBvcnQge1xuICB0eXBlIEFwcFN0YXRlLFxuICBnZXREZWZhdWx0QXBwU3RhdGUsXG4gIElETEVfU1BFQ1VMQVRJT05fU1RBVEUsXG59IGZyb20gJy4vc3RhdGUvQXBwU3RhdGVTdG9yZS5qcydcbmltcG9ydCB7IG9uQ2hhbmdlQXBwU3RhdGUgfSBmcm9tICcuL3N0YXRlL29uQ2hhbmdlQXBwU3RhdGUuanMnXG5pbXBvcnQgeyBjcmVhdGVTdG9yZSB9IGZyb20gJy4vc3RhdGUvc3RvcmUuanMnXG5pbXBvcnQgeyBhc1Nlc3Npb25JZCB9IGZyb20gJy4vdHlwZXMvaWRzLmpzJ1xuaW1wb3J0IHsgZmlsdGVyQWxsb3dlZFNka0JldGFzIH0gZnJvbSAnLi91dGlscy9iZXRhcy5qcydcbmltcG9ydCB7IGlzSW5CdW5kbGVkTW9kZSwgaXNSdW5uaW5nV2l0aEJ1biB9IGZyb20gJy4vdXRpbHMvYnVuZGxlZE1vZGUuanMnXG5pbXBvcnQgeyBsb2dGb3JEaWFnbm9zdGljc05vUElJIH0gZnJvbSAnLi91dGlscy9kaWFnTG9ncy5qcydcbmltcG9ydCB7XG4gIGZpbHRlckV4aXN0aW5nUGF0aHMsXG4gIGdldEtub3duUGF0aHNGb3JSZXBvLFxufSBmcm9tICcuL3V0aWxzL2dpdGh1YlJlcG9QYXRoTWFwcGluZy5qcydcbmltcG9ydCB7XG4gIGNsZWFyUGx1Z2luQ2FjaGUsXG4gIGxvYWRBbGxQbHVnaW5zQ2FjaGVPbmx5LFxufSBmcm9tICcuL3V0aWxzL3BsdWdpbnMvcGx1Z2luTG9hZGVyLmpzJ1xuaW1wb3J0IHsgbWlncmF0ZUNoYW5nZWxvZ0Zyb21Db25maWcgfSBmcm9tICcuL3V0aWxzL3JlbGVhc2VOb3Rlcy5qcydcbmltcG9ydCB7IFNhbmRib3hNYW5hZ2VyIH0gZnJvbSAnLi91dGlscy9zYW5kYm94L3NhbmRib3gtYWRhcHRlci5qcydcbmltcG9ydCB7IGZldGNoU2Vzc2lvbiwgcHJlcGFyZUFwaVJlcXVlc3QgfSBmcm9tICcuL3V0aWxzL3RlbGVwb3J0L2FwaS5qcydcbmltcG9ydCB7XG4gIGNoZWNrT3V0VGVsZXBvcnRlZFNlc3Npb25CcmFuY2gsXG4gIHByb2Nlc3NNZXNzYWdlc0ZvclRlbGVwb3J0UmVzdW1lLFxuICB0ZWxlcG9ydFRvUmVtb3RlV2l0aEVycm9ySGFuZGxpbmcsXG4gIHZhbGlkYXRlR2l0U3RhdGUsXG4gIHZhbGlkYXRlU2Vzc2lvblJlcG9zaXRvcnksXG59IGZyb20gJy4vdXRpbHMvdGVsZXBvcnQuanMnXG5pbXBvcnQge1xuICBzaG91bGRFbmFibGVUaGlua2luZ0J5RGVmYXVsdCxcbiAgdHlwZSBUaGlua2luZ0NvbmZpZyxcbn0gZnJvbSAnLi91dGlscy90aGlua2luZy5qcydcbmltcG9ydCB7IGluaXRVc2VyLCByZXNldFVzZXJDYWNoZSB9IGZyb20gJy4vdXRpbHMvdXNlci5qcydcbmltcG9ydCB7XG4gIGdldFRtdXhJbnN0YWxsSW5zdHJ1Y3Rpb25zLFxuICBpc1RtdXhBdmFpbGFibGUsXG4gIHBhcnNlUFJSZWZlcmVuY2UsXG59IGZyb20gJy4vdXRpbHMvd29ya3RyZWUuanMnXG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tdG9wLWxldmVsLXNpZGUtZWZmZWN0c1xucHJvZmlsZUNoZWNrcG9pbnQoJ21haW5fdHN4X2ltcG9ydHNfbG9hZGVkJylcblxuLyoqXG4gKiBMb2cgbWFuYWdlZCBzZXR0aW5ncyBrZXlzIHRvIFN0YXRzaWcgZm9yIGFuYWx5dGljcy5cbiAqIFRoaXMgaXMgY2FsbGVkIGFmdGVyIGluaXQoKSBjb21wbGV0ZXMgdG8gZW5zdXJlIHNldHRpbmdzIGFyZSBsb2FkZWRcbiAqIGFuZCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgYXJlIGFwcGxpZWQgYmVmb3JlIG1vZGVsIHJlc29sdXRpb24uXG4gKi9cbmZ1bmN0aW9uIGxvZ01hbmFnZWRTZXR0aW5ncygpOiB2b2lkIHtcbiAgdHJ5IHtcbiAgICBjb25zdCBwb2xpY3lTZXR0aW5ncyA9IGdldFNldHRpbmdzRm9yU291cmNlKCdwb2xpY3lTZXR0aW5ncycpXG4gICAgaWYgKHBvbGljeVNldHRpbmdzKSB7XG4gICAgICBjb25zdCBhbGxLZXlzID0gZ2V0TWFuYWdlZFNldHRpbmdzS2V5c0ZvckxvZ2dpbmcocG9saWN5U2V0dGluZ3MpXG4gICAgICBsb2dFdmVudCgndGVuZ3VfbWFuYWdlZF9zZXR0aW5nc19sb2FkZWQnLCB7XG4gICAgICAgIGtleUNvdW50OiBhbGxLZXlzLmxlbmd0aCxcbiAgICAgICAga2V5czogYWxsS2V5cy5qb2luKFxuICAgICAgICAgICcsJyxcbiAgICAgICAgKSBhcyB1bmtub3duIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KVxuICAgIH1cbiAgfSBjYXRjaCB7XG4gICAgLy8gU2lsZW50bHkgaWdub3JlIGVycm9ycyAtIHRoaXMgaXMganVzdCBmb3IgYW5hbHl0aWNzXG4gIH1cbn1cblxuLy8gQ2hlY2sgaWYgcnVubmluZyBpbiBkZWJ1Zy9pbnNwZWN0aW9uIG1vZGVcbmZ1bmN0aW9uIGlzQmVpbmdEZWJ1Z2dlZCgpIHtcbiAgY29uc3QgaXNCdW4gPSBpc1J1bm5pbmdXaXRoQnVuKClcblxuICAvLyBDaGVjayBmb3IgaW5zcGVjdCBmbGFncyBpbiBwcm9jZXNzIGFyZ3VtZW50cyAoaW5jbHVkaW5nIGFsbCB2YXJpYW50cylcbiAgY29uc3QgaGFzSW5zcGVjdEFyZyA9IHByb2Nlc3MuZXhlY0FyZ3Yuc29tZShhcmcgPT4ge1xuICAgIGlmIChpc0J1bikge1xuICAgICAgLy8gTm90ZTogQnVuIGhhcyBhbiBpc3N1ZSB3aXRoIHNpbmdsZS1maWxlIGV4ZWN1dGFibGVzIHdoZXJlIGFwcGxpY2F0aW9uIGFyZ3VtZW50c1xuICAgICAgLy8gZnJvbSBwcm9jZXNzLmFyZ3YgbGVhayBpbnRvIHByb2Nlc3MuZXhlY0FyZ3YgKHNpbWlsYXIgdG8gaHR0cHM6Ly9naXRodWIuY29tL292ZW4tc2gvYnVuL2lzc3Vlcy8xMTY3MylcbiAgICAgIC8vIFRoaXMgYnJlYWtzIHVzZSBvZiAtLWRlYnVnIG1vZGUgaWYgd2Ugb21pdCB0aGlzIGJyYW5jaFxuICAgICAgLy8gV2UncmUgZmluZSB0byBza2lwIHRoYXQgY2hlY2ssIGJlY2F1c2UgQnVuIGRvZXNuJ3Qgc3VwcG9ydCBOb2RlLmpzIGxlZ2FjeSAtLWRlYnVnIG9yIC0tZGVidWctYnJrIGZsYWdzXG4gICAgICByZXR1cm4gLy0taW5zcGVjdCgtYnJrKT8vLnRlc3QoYXJnKVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBJbiBOb2RlLmpzLCBjaGVjayBmb3IgYm90aCAtLWluc3BlY3QgYW5kIGxlZ2FjeSAtLWRlYnVnIGZsYWdzXG4gICAgICByZXR1cm4gLy0taW5zcGVjdCgtYnJrKT98LS1kZWJ1ZygtYnJrKT8vLnRlc3QoYXJnKVxuICAgIH1cbiAgfSlcblxuICAvLyBDaGVjayBpZiBOT0RFX09QVElPTlMgY29udGFpbnMgaW5zcGVjdCBmbGFnc1xuICBjb25zdCBoYXNJbnNwZWN0RW52ID1cbiAgICBwcm9jZXNzLmVudi5OT0RFX09QVElPTlMgJiZcbiAgICAvLS1pbnNwZWN0KC1icmspP3wtLWRlYnVnKC1icmspPy8udGVzdChwcm9jZXNzLmVudi5OT0RFX09QVElPTlMpXG5cbiAgLy8gQ2hlY2sgaWYgaW5zcGVjdG9yIGlzIGF2YWlsYWJsZSBhbmQgYWN0aXZlIChpbmRpY2F0ZXMgZGVidWdnaW5nKVxuICB0cnkge1xuICAgIC8vIER5bmFtaWMgaW1wb3J0IHdvdWxkIGJlIGJldHRlciBidXQgaXMgYXN5bmMgLSB1c2UgZ2xvYmFsIG9iamVjdCBpbnN0ZWFkXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1leHBsaWNpdC1hbnlcbiAgICBjb25zdCBpbnNwZWN0b3IgPSAoZ2xvYmFsIGFzIGFueSkucmVxdWlyZSgnaW5zcGVjdG9yJylcbiAgICBjb25zdCBoYXNJbnNwZWN0b3JVcmwgPSAhIWluc3BlY3Rvci51cmwoKVxuICAgIHJldHVybiBoYXNJbnNwZWN0b3JVcmwgfHwgaGFzSW5zcGVjdEFyZyB8fCBoYXNJbnNwZWN0RW52XG4gIH0gY2F0Y2gge1xuICAgIC8vIElnbm9yZSBlcnJvciBhbmQgZmFsbCBiYWNrIHRvIGFyZ3VtZW50IGRldGVjdGlvblxuICAgIHJldHVybiBoYXNJbnNwZWN0QXJnIHx8IGhhc0luc3BlY3RFbnZcbiAgfVxufVxuXG4vLyBFeGl0IGlmIHdlIGRldGVjdCBub2RlIGRlYnVnZ2luZyBvciBpbnNwZWN0aW9uXG5pZiAoXCJleHRlcm5hbFwiICE9PSAnYW50JyAmJiBpc0JlaW5nRGVidWdnZWQoKSkge1xuICAvLyBVc2UgcHJvY2Vzcy5leGl0IGRpcmVjdGx5IGhlcmUgc2luY2Ugd2UncmUgaW4gdGhlIHRvcC1sZXZlbCBjb2RlIGJlZm9yZSBpbXBvcnRzXG4gIC8vIGFuZCBncmFjZWZ1bFNodXRkb3duIGlzIG5vdCB5ZXQgYXZhaWxhYmxlXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tdG9wLWxldmVsLXNpZGUtZWZmZWN0c1xuICBwcm9jZXNzLmV4aXQoMSlcbn1cblxuLyoqXG4gKiBQZXItc2Vzc2lvbiBza2lsbC9wbHVnaW4gdGVsZW1ldHJ5LiBDYWxsZWQgZnJvbSBib3RoIHRoZSBpbnRlcmFjdGl2ZSBwYXRoXG4gKiBhbmQgdGhlIGhlYWRsZXNzIC1wIHBhdGggKGJlZm9yZSBydW5IZWFkbGVzcykg4oCUIGJvdGggZ28gdGhyb3VnaFxuICogbWFpbi50c3ggYnV0IGJyYW5jaCBiZWZvcmUgdGhlIGludGVyYWN0aXZlIHN0YXJ0dXAgcGF0aCwgc28gaXQgbmVlZHMgdHdvXG4gKiBjYWxsIHNpdGVzIGhlcmUgcmF0aGVyIHRoYW4gb25lIGhlcmUgKyBvbmUgaW4gUXVlcnlFbmdpbmUuXG4gKi9cbmZ1bmN0aW9uIGxvZ1Nlc3Npb25UZWxlbWV0cnkoKTogdm9pZCB7XG4gIGNvbnN0IG1vZGVsID0gcGFyc2VVc2VyU3BlY2lmaWVkTW9kZWwoXG4gICAgZ2V0SW5pdGlhbE1haW5Mb29wTW9kZWwoKSA/PyBnZXREZWZhdWx0TWFpbkxvb3BNb2RlbCgpLFxuICApXG4gIHZvaWQgbG9nU2tpbGxzTG9hZGVkKGdldEN3ZCgpLCBnZXRDb250ZXh0V2luZG93Rm9yTW9kZWwobW9kZWwsIGdldFNka0JldGFzKCkpKVxuICB2b2lkIGxvYWRBbGxQbHVnaW5zQ2FjaGVPbmx5KClcbiAgICAudGhlbigoeyBlbmFibGVkLCBlcnJvcnMgfSkgPT4ge1xuICAgICAgY29uc3QgbWFuYWdlZE5hbWVzID0gZ2V0TWFuYWdlZFBsdWdpbk5hbWVzKClcbiAgICAgIGxvZ1BsdWdpbnNFbmFibGVkRm9yU2Vzc2lvbihlbmFibGVkLCBtYW5hZ2VkTmFtZXMsIGdldFBsdWdpblNlZWREaXJzKCkpXG4gICAgICBsb2dQbHVnaW5Mb2FkRXJyb3JzKGVycm9ycywgbWFuYWdlZE5hbWVzKVxuICAgIH0pXG4gICAgLmNhdGNoKGVyciA9PiBsb2dFcnJvcihlcnIpKVxufVxuXG5mdW5jdGlvbiBnZXRDZXJ0RW52VmFyVGVsZW1ldHJ5KCk6IFJlY29yZDxzdHJpbmcsIGJvb2xlYW4+IHtcbiAgY29uc3QgcmVzdWx0OiBSZWNvcmQ8c3RyaW5nLCBib29sZWFuPiA9IHt9XG4gIGlmIChwcm9jZXNzLmVudi5OT0RFX0VYVFJBX0NBX0NFUlRTKSB7XG4gICAgcmVzdWx0Lmhhc19ub2RlX2V4dHJhX2NhX2NlcnRzID0gdHJ1ZVxuICB9XG4gIGlmIChwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9DTElFTlRfQ0VSVCkge1xuICAgIHJlc3VsdC5oYXNfY2xpZW50X2NlcnQgPSB0cnVlXG4gIH1cbiAgaWYgKGhhc05vZGVPcHRpb24oJy0tdXNlLXN5c3RlbS1jYScpKSB7XG4gICAgcmVzdWx0Lmhhc191c2Vfc3lzdGVtX2NhID0gdHJ1ZVxuICB9XG4gIGlmIChoYXNOb2RlT3B0aW9uKCctLXVzZS1vcGVuc3NsLWNhJykpIHtcbiAgICByZXN1bHQuaGFzX3VzZV9vcGVuc3NsX2NhID0gdHJ1ZVxuICB9XG4gIHJldHVybiByZXN1bHRcbn1cblxuYXN5bmMgZnVuY3Rpb24gbG9nU3RhcnR1cFRlbGVtZXRyeSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgaWYgKGlzQW5hbHl0aWNzRGlzYWJsZWQoKSkgcmV0dXJuXG4gIGNvbnN0IFtpc0dpdCwgd29ya3RyZWVDb3VudCwgZ2hBdXRoU3RhdHVzXSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICBnZXRJc0dpdCgpLFxuICAgIGdldFdvcmt0cmVlQ291bnQoKSxcbiAgICBnZXRHaEF1dGhTdGF0dXMoKSxcbiAgXSlcblxuICBsb2dFdmVudCgndGVuZ3Vfc3RhcnR1cF90ZWxlbWV0cnknLCB7XG4gICAgaXNfZ2l0OiBpc0dpdCxcbiAgICB3b3JrdHJlZV9jb3VudDogd29ya3RyZWVDb3VudCxcbiAgICBnaF9hdXRoX3N0YXR1czpcbiAgICAgIGdoQXV0aFN0YXR1cyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgIHNhbmRib3hfZW5hYmxlZDogU2FuZGJveE1hbmFnZXIuaXNTYW5kYm94aW5nRW5hYmxlZCgpLFxuICAgIGFyZV91bnNhbmRib3hlZF9jb21tYW5kc19hbGxvd2VkOlxuICAgICAgU2FuZGJveE1hbmFnZXIuYXJlVW5zYW5kYm94ZWRDb21tYW5kc0FsbG93ZWQoKSxcbiAgICBpc19hdXRvX2Jhc2hfYWxsb3dlZF9pZl9zYW5kYm94X2VuYWJsZWQ6XG4gICAgICBTYW5kYm94TWFuYWdlci5pc0F1dG9BbGxvd0Jhc2hJZlNhbmRib3hlZEVuYWJsZWQoKSxcbiAgICBhdXRvX3VwZGF0ZXJfZGlzYWJsZWQ6IGlzQXV0b1VwZGF0ZXJEaXNhYmxlZCgpLFxuICAgIHByZWZlcnNfcmVkdWNlZF9tb3Rpb246IGdldEluaXRpYWxTZXR0aW5ncygpLnByZWZlcnNSZWR1Y2VkTW90aW9uID8/IGZhbHNlLFxuICAgIC4uLmdldENlcnRFbnZWYXJUZWxlbWV0cnkoKSxcbiAgfSlcbn1cblxuLy8gQFtNT0RFTCBMQVVOQ0hdOiBDb25zaWRlciBhbnkgbWlncmF0aW9ucyB5b3UgbWF5IG5lZWQgZm9yIG1vZGVsIHN0cmluZ3MuIFNlZSBtaWdyYXRlU29ubmV0MW1Ub1Nvbm5ldDQ1LnRzIGZvciBhbiBleGFtcGxlLlxuLy8gQnVtcCB0aGlzIHdoZW4gYWRkaW5nIGEgbmV3IHN5bmMgbWlncmF0aW9uIHNvIGV4aXN0aW5nIHVzZXJzIHJlLXJ1biB0aGUgc2V0LlxuY29uc3QgQ1VSUkVOVF9NSUdSQVRJT05fVkVSU0lPTiA9IDExXG5mdW5jdGlvbiBydW5NaWdyYXRpb25zKCk6IHZvaWQge1xuICBpZiAoZ2V0R2xvYmFsQ29uZmlnKCkubWlncmF0aW9uVmVyc2lvbiAhPT0gQ1VSUkVOVF9NSUdSQVRJT05fVkVSU0lPTikge1xuICAgIG1pZ3JhdGVBdXRvVXBkYXRlc1RvU2V0dGluZ3MoKVxuICAgIG1pZ3JhdGVCeXBhc3NQZXJtaXNzaW9uc0FjY2VwdGVkVG9TZXR0aW5ncygpXG4gICAgbWlncmF0ZUVuYWJsZUFsbFByb2plY3RNY3BTZXJ2ZXJzVG9TZXR0aW5ncygpXG4gICAgcmVzZXRQcm9Ub09wdXNEZWZhdWx0KClcbiAgICBtaWdyYXRlU29ubmV0MW1Ub1Nvbm5ldDQ1KClcbiAgICBtaWdyYXRlTGVnYWN5T3B1c1RvQ3VycmVudCgpXG4gICAgbWlncmF0ZVNvbm5ldDQ1VG9Tb25uZXQ0NigpXG4gICAgbWlncmF0ZU9wdXNUb09wdXMxbSgpXG4gICAgbWlncmF0ZVJlcGxCcmlkZ2VFbmFibGVkVG9SZW1vdGVDb250cm9sQXRTdGFydHVwKClcbiAgICBpZiAoZmVhdHVyZSgnVFJBTlNDUklQVF9DTEFTU0lGSUVSJykpIHtcbiAgICAgIHJlc2V0QXV0b01vZGVPcHRJbkZvckRlZmF1bHRPZmZlcigpXG4gICAgfVxuICAgIGlmIChcImV4dGVybmFsXCIgPT09ICdhbnQnKSB7XG4gICAgICBtaWdyYXRlRmVubmVjVG9PcHVzKClcbiAgICB9XG4gICAgc2F2ZUdsb2JhbENvbmZpZyhwcmV2ID0+XG4gICAgICBwcmV2Lm1pZ3JhdGlvblZlcnNpb24gPT09IENVUlJFTlRfTUlHUkFUSU9OX1ZFUlNJT05cbiAgICAgICAgPyBwcmV2XG4gICAgICAgIDogeyAuLi5wcmV2LCBtaWdyYXRpb25WZXJzaW9uOiBDVVJSRU5UX01JR1JBVElPTl9WRVJTSU9OIH0sXG4gICAgKVxuICB9XG4gIC8vIEFzeW5jIG1pZ3JhdGlvbiAtIGZpcmUgYW5kIGZvcmdldCBzaW5jZSBpdCdzIG5vbi1ibG9ja2luZ1xuICBtaWdyYXRlQ2hhbmdlbG9nRnJvbUNvbmZpZygpLmNhdGNoKCgpID0+IHtcbiAgICAvLyBTaWxlbnRseSBpZ25vcmUgbWlncmF0aW9uIGVycm9ycyAtIHdpbGwgcmV0cnkgb24gbmV4dCBzdGFydHVwXG4gIH0pXG59XG5cbi8qKlxuICogUHJlZmV0Y2ggc3lzdGVtIGNvbnRleHQgKGluY2x1ZGluZyBnaXQgc3RhdHVzKSBvbmx5IHdoZW4gaXQncyBzYWZlIHRvIGRvIHNvLlxuICogR2l0IGNvbW1hbmRzIGNhbiBleGVjdXRlIGFyYml0cmFyeSBjb2RlIHZpYSBob29rcyBhbmQgY29uZmlnIChlLmcuLCBjb3JlLmZzbW9uaXRvcixcbiAqIGRpZmYuZXh0ZXJuYWwpLCBzbyB3ZSBtdXN0IG9ubHkgcnVuIHRoZW0gYWZ0ZXIgdHJ1c3QgaXMgZXN0YWJsaXNoZWQgb3IgaW5cbiAqIG5vbi1pbnRlcmFjdGl2ZSBtb2RlIHdoZXJlIHRydXN0IGlzIGltcGxpY2l0LlxuICovXG5mdW5jdGlvbiBwcmVmZXRjaFN5c3RlbUNvbnRleHRJZlNhZmUoKTogdm9pZCB7XG4gIGNvbnN0IGlzTm9uSW50ZXJhY3RpdmVTZXNzaW9uID0gZ2V0SXNOb25JbnRlcmFjdGl2ZVNlc3Npb24oKVxuXG4gIC8vIEluIG5vbi1pbnRlcmFjdGl2ZSBtb2RlICgtLXByaW50KSwgdHJ1c3QgZGlhbG9nIGlzIHNraXBwZWQgYW5kXG4gIC8vIGV4ZWN1dGlvbiBpcyBjb25zaWRlcmVkIHRydXN0ZWQgKGFzIGRvY3VtZW50ZWQgaW4gaGVscCB0ZXh0KVxuICBpZiAoaXNOb25JbnRlcmFjdGl2ZVNlc3Npb24pIHtcbiAgICBsb2dGb3JEaWFnbm9zdGljc05vUElJKCdpbmZvJywgJ3ByZWZldGNoX3N5c3RlbV9jb250ZXh0X25vbl9pbnRlcmFjdGl2ZScpXG4gICAgdm9pZCBnZXRTeXN0ZW1Db250ZXh0KClcbiAgICByZXR1cm5cbiAgfVxuXG4gIC8vIEluIGludGVyYWN0aXZlIG1vZGUsIG9ubHkgcHJlZmV0Y2ggaWYgdHJ1c3QgaGFzIGFscmVhZHkgYmVlbiBlc3RhYmxpc2hlZFxuICBjb25zdCBoYXNUcnVzdCA9IGNoZWNrSGFzVHJ1c3REaWFsb2dBY2NlcHRlZCgpXG4gIGlmIChoYXNUcnVzdCkge1xuICAgIGxvZ0ZvckRpYWdub3N0aWNzTm9QSUkoJ2luZm8nLCAncHJlZmV0Y2hfc3lzdGVtX2NvbnRleHRfaGFzX3RydXN0JylcbiAgICB2b2lkIGdldFN5c3RlbUNvbnRleHQoKVxuICB9IGVsc2Uge1xuICAgIGxvZ0ZvckRpYWdub3N0aWNzTm9QSUkoJ2luZm8nLCAncHJlZmV0Y2hfc3lzdGVtX2NvbnRleHRfc2tpcHBlZF9ub190cnVzdCcpXG4gIH1cbiAgLy8gT3RoZXJ3aXNlLCBkb24ndCBwcmVmZXRjaCAtIHdhaXQgZm9yIHRydXN0IHRvIGJlIGVzdGFibGlzaGVkIGZpcnN0XG59XG5cbi8qKlxuICogU3RhcnQgYmFja2dyb3VuZCBwcmVmZXRjaGVzIGFuZCBob3VzZWtlZXBpbmcgdGhhdCBhcmUgTk9UIG5lZWRlZCBiZWZvcmUgZmlyc3QgcmVuZGVyLlxuICogVGhlc2UgYXJlIGRlZmVycmVkIGZyb20gc2V0dXAoKSB0byByZWR1Y2UgZXZlbnQgbG9vcCBjb250ZW50aW9uIGFuZCBjaGlsZCBwcm9jZXNzXG4gKiBzcGF3bmluZyBkdXJpbmcgdGhlIGNyaXRpY2FsIHN0YXJ0dXAgcGF0aC5cbiAqIENhbGwgdGhpcyBhZnRlciB0aGUgUkVQTCBoYXMgYmVlbiByZW5kZXJlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHN0YXJ0RGVmZXJyZWRQcmVmZXRjaGVzKCk6IHZvaWQge1xuICAvLyBUaGlzIGZ1bmN0aW9uIHJ1bnMgYWZ0ZXIgZmlyc3QgcmVuZGVyLCBzbyBpdCBkb2Vzbid0IGJsb2NrIHRoZSBpbml0aWFsIHBhaW50LlxuICAvLyBIb3dldmVyLCB0aGUgc3Bhd25lZCBwcm9jZXNzZXMgYW5kIGFzeW5jIHdvcmsgc3RpbGwgY29udGVuZCBmb3IgQ1BVIGFuZCBldmVudFxuICAvLyBsb29wIHRpbWUsIHdoaWNoIHNrZXdzIHN0YXJ0dXAgYmVuY2htYXJrcyAoQ1BVIHByb2ZpbGVzLCB0aW1lLXRvLWZpcnN0LXJlbmRlclxuICAvLyBtZWFzdXJlbWVudHMpLiBTa2lwIGFsbCBvZiBpdCB3aGVuIHdlJ3JlIG9ubHkgbWVhc3VyaW5nIHN0YXJ0dXAgcGVyZm9ybWFuY2UuXG4gIGlmIChcbiAgICBpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9FWElUX0FGVEVSX0ZJUlNUX1JFTkRFUikgfHxcbiAgICAvLyAtLWJhcmU6IHNraXAgQUxMIHByZWZldGNoZXMuIFRoZXNlIGFyZSBjYWNoZS13YXJtcyBmb3IgdGhlIFJFUEwnc1xuICAgIC8vIGZpcnN0LXR1cm4gcmVzcG9uc2l2ZW5lc3MgKGluaXRVc2VyLCBnZXRVc2VyQ29udGV4dCwgdGlwcywgY291bnRGaWxlcyxcbiAgICAvLyBtb2RlbENhcGFiaWxpdGllcywgY2hhbmdlIGRldGVjdG9ycykuIFNjcmlwdGVkIC1wIGNhbGxzIGRvbid0IGhhdmUgYVxuICAgIC8vIFwidXNlciBpcyB0eXBpbmdcIiB3aW5kb3cgdG8gaGlkZSB0aGlzIHdvcmsgaW4g4oCUIGl0J3MgcHVyZSBvdmVyaGVhZCBvblxuICAgIC8vIHRoZSBjcml0aWNhbCBwYXRoLlxuICAgIGlzQmFyZU1vZGUoKVxuICApIHtcbiAgICByZXR1cm5cbiAgfVxuXG4gIC8vIFByb2Nlc3Mtc3Bhd25pbmcgcHJlZmV0Y2hlcyAoY29uc3VtZWQgYXQgZmlyc3QgQVBJIGNhbGwsIHVzZXIgaXMgc3RpbGwgdHlwaW5nKVxuICB2b2lkIGluaXRVc2VyKClcbiAgdm9pZCBnZXRVc2VyQ29udGV4dCgpXG4gIHByZWZldGNoU3lzdGVtQ29udGV4dElmU2FmZSgpXG4gIHZvaWQgZ2V0UmVsZXZhbnRUaXBzKClcbiAgaWYgKFxuICAgIGlzRW52VHJ1dGh5KHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX1VTRV9CRURST0NLKSAmJlxuICAgICFpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9TS0lQX0JFRFJPQ0tfQVVUSClcbiAgKSB7XG4gICAgdm9pZCBwcmVmZXRjaEF3c0NyZWRlbnRpYWxzQW5kQmVkUm9ja0luZm9JZlNhZmUoKVxuICB9XG4gIGlmIChcbiAgICBpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9VU0VfVkVSVEVYKSAmJlxuICAgICFpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9TS0lQX1ZFUlRFWF9BVVRIKVxuICApIHtcbiAgICB2b2lkIHByZWZldGNoR2NwQ3JlZGVudGlhbHNJZlNhZmUoKVxuICB9XG4gIHZvaWQgY291bnRGaWxlc1JvdW5kZWRSZyhnZXRDd2QoKSwgQWJvcnRTaWduYWwudGltZW91dCgzMDAwKSwgW10pXG5cbiAgLy8gQW5hbHl0aWNzIGFuZCBmZWF0dXJlIGZsYWcgaW5pdGlhbGl6YXRpb25cbiAgdm9pZCBpbml0aWFsaXplQW5hbHl0aWNzR2F0ZXMoKVxuICB2b2lkIHByZWZldGNoT2ZmaWNpYWxNY3BVcmxzKClcblxuICB2b2lkIHJlZnJlc2hNb2RlbENhcGFiaWxpdGllcygpXG5cbiAgLy8gRmlsZSBjaGFuZ2UgZGV0ZWN0b3JzIGRlZmVycmVkIGZyb20gaW5pdCgpIHRvIHVuYmxvY2sgZmlyc3QgcmVuZGVyXG4gIHZvaWQgc2V0dGluZ3NDaGFuZ2VEZXRlY3Rvci5pbml0aWFsaXplKClcbiAgaWYgKCFpc0JhcmVNb2RlKCkpIHtcbiAgICB2b2lkIHNraWxsQ2hhbmdlRGV0ZWN0b3IuaW5pdGlhbGl6ZSgpXG4gIH1cblxuICAvLyBFdmVudCBsb29wIHN0YWxsIGRldGVjdG9yIOKAlCBsb2dzIHdoZW4gdGhlIG1haW4gdGhyZWFkIGlzIGJsb2NrZWQgPjUwMG1zXG4gIGlmIChcImV4dGVybmFsXCIgPT09ICdhbnQnKSB7XG4gICAgdm9pZCBpbXBvcnQoJy4vdXRpbHMvZXZlbnRMb29wU3RhbGxEZXRlY3Rvci5qcycpLnRoZW4obSA9PlxuICAgICAgbS5zdGFydEV2ZW50TG9vcFN0YWxsRGV0ZWN0b3IoKSxcbiAgICApXG4gIH1cbn1cblxuZnVuY3Rpb24gbG9hZFNldHRpbmdzRnJvbUZsYWcoc2V0dGluZ3NGaWxlOiBzdHJpbmcpOiB2b2lkIHtcbiAgdHJ5IHtcbiAgICBjb25zdCB0cmltbWVkU2V0dGluZ3MgPSBzZXR0aW5nc0ZpbGUudHJpbSgpXG4gICAgY29uc3QgbG9va3NMaWtlSnNvbiA9XG4gICAgICB0cmltbWVkU2V0dGluZ3Muc3RhcnRzV2l0aCgneycpICYmIHRyaW1tZWRTZXR0aW5ncy5lbmRzV2l0aCgnfScpXG5cbiAgICBsZXQgc2V0dGluZ3NQYXRoOiBzdHJpbmdcblxuICAgIGlmIChsb29rc0xpa2VKc29uKSB7XG4gICAgICAvLyBJdCdzIGEgSlNPTiBzdHJpbmcgLSB2YWxpZGF0ZSBhbmQgY3JlYXRlIHRlbXAgZmlsZVxuICAgICAgY29uc3QgcGFyc2VkSnNvbiA9IHNhZmVQYXJzZUpTT04odHJpbW1lZFNldHRpbmdzKVxuICAgICAgaWYgKCFwYXJzZWRKc29uKSB7XG4gICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgIGNoYWxrLnJlZCgnRXJyb3I6IEludmFsaWQgSlNPTiBwcm92aWRlZCB0byAtLXNldHRpbmdzXFxuJyksXG4gICAgICAgIClcbiAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICB9XG5cbiAgICAgIC8vIENyZWF0ZSBhIHRlbXBvcmFyeSBmaWxlIGFuZCB3cml0ZSB0aGUgSlNPTiB0byBpdC5cbiAgICAgIC8vIFVzZSBhIGNvbnRlbnQtaGFzaC1iYXNlZCBwYXRoIGluc3RlYWQgb2YgcmFuZG9tIFVVSUQgdG8gYXZvaWRcbiAgICAgIC8vIGJ1c3RpbmcgdGhlIEFudGhyb3BpYyBBUEkgcHJvbXB0IGNhY2hlLiBUaGUgc2V0dGluZ3MgcGF0aCBlbmRzIHVwXG4gICAgICAvLyBpbiB0aGUgQmFzaCB0b29sJ3Mgc2FuZGJveCBkZW55V2l0aGluQWxsb3cgbGlzdCwgd2hpY2ggaXMgcGFydCBvZlxuICAgICAgLy8gdGhlIHRvb2wgZGVzY3JpcHRpb24gc2VudCB0byB0aGUgQVBJLiBBIHJhbmRvbSBVVUlEIHBlciBzdWJwcm9jZXNzXG4gICAgICAvLyBjaGFuZ2VzIHRoZSB0b29sIGRlc2NyaXB0aW9uIG9uIGV2ZXJ5IHF1ZXJ5KCkgY2FsbCwgaW52YWxpZGF0aW5nXG4gICAgICAvLyB0aGUgY2FjaGUgcHJlZml4IGFuZCBjYXVzaW5nIGEgMTJ4IGlucHV0IHRva2VuIGNvc3QgcGVuYWx0eS5cbiAgICAgIC8vIFRoZSBjb250ZW50IGhhc2ggZW5zdXJlcyBpZGVudGljYWwgc2V0dGluZ3MgcHJvZHVjZSB0aGUgc2FtZSBwYXRoXG4gICAgICAvLyBhY3Jvc3MgcHJvY2VzcyBib3VuZGFyaWVzIChlYWNoIFNESyBxdWVyeSgpIHNwYXducyBhIG5ldyBwcm9jZXNzKS5cbiAgICAgIHNldHRpbmdzUGF0aCA9IGdlbmVyYXRlVGVtcEZpbGVQYXRoKCdjbGF1ZGUtc2V0dGluZ3MnLCAnLmpzb24nLCB7XG4gICAgICAgIGNvbnRlbnRIYXNoOiB0cmltbWVkU2V0dGluZ3MsXG4gICAgICB9KVxuICAgICAgd3JpdGVGaWxlU3luY19ERVBSRUNBVEVEKHNldHRpbmdzUGF0aCwgdHJpbW1lZFNldHRpbmdzLCAndXRmOCcpXG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIEl0J3MgYSBmaWxlIHBhdGggLSByZXNvbHZlIGFuZCB2YWxpZGF0ZSBieSBhdHRlbXB0aW5nIHRvIHJlYWRcbiAgICAgIGNvbnN0IHsgcmVzb2x2ZWRQYXRoOiByZXNvbHZlZFNldHRpbmdzUGF0aCB9ID0gc2FmZVJlc29sdmVQYXRoKFxuICAgICAgICBnZXRGc0ltcGxlbWVudGF0aW9uKCksXG4gICAgICAgIHNldHRpbmdzRmlsZSxcbiAgICAgIClcbiAgICAgIHRyeSB7XG4gICAgICAgIHJlYWRGaWxlU3luYyhyZXNvbHZlZFNldHRpbmdzUGF0aCwgJ3V0ZjgnKVxuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICBpZiAoaXNFTk9FTlQoZSkpIHtcbiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgIGNoYWxrLnJlZChcbiAgICAgICAgICAgICAgYEVycm9yOiBTZXR0aW5ncyBmaWxlIG5vdCBmb3VuZDogJHtyZXNvbHZlZFNldHRpbmdzUGF0aH1cXG5gLFxuICAgICAgICAgICAgKSxcbiAgICAgICAgICApXG4gICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgIH1cbiAgICAgICAgdGhyb3cgZVxuICAgICAgfVxuICAgICAgc2V0dGluZ3NQYXRoID0gcmVzb2x2ZWRTZXR0aW5nc1BhdGhcbiAgICB9XG5cbiAgICBzZXRGbGFnU2V0dGluZ3NQYXRoKHNldHRpbmdzUGF0aClcbiAgICByZXNldFNldHRpbmdzQ2FjaGUoKVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgICBsb2dFcnJvcihlcnJvcilcbiAgICB9XG4gICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICBjaGFsay5yZWQoYEVycm9yIHByb2Nlc3Npbmcgc2V0dGluZ3M6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1cXG5gKSxcbiAgICApXG4gICAgcHJvY2Vzcy5leGl0KDEpXG4gIH1cbn1cblxuZnVuY3Rpb24gbG9hZFNldHRpbmdTb3VyY2VzRnJvbUZsYWcoc2V0dGluZ1NvdXJjZXNBcmc6IHN0cmluZyk6IHZvaWQge1xuICB0cnkge1xuICAgIGNvbnN0IHNvdXJjZXMgPSBwYXJzZVNldHRpbmdTb3VyY2VzRmxhZyhzZXR0aW5nU291cmNlc0FyZylcbiAgICBzZXRBbGxvd2VkU2V0dGluZ1NvdXJjZXMoc291cmNlcylcbiAgICByZXNldFNldHRpbmdzQ2FjaGUoKVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yKSB7XG4gICAgICBsb2dFcnJvcihlcnJvcilcbiAgICB9XG4gICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICBjaGFsay5yZWQoYEVycm9yIHByb2Nlc3NpbmcgLS1zZXR0aW5nLXNvdXJjZXM6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1cXG5gKSxcbiAgICApXG4gICAgcHJvY2Vzcy5leGl0KDEpXG4gIH1cbn1cblxuLyoqXG4gKiBQYXJzZSBhbmQgbG9hZCBzZXR0aW5ncyBmbGFncyBlYXJseSwgYmVmb3JlIGluaXQoKVxuICogVGhpcyBlbnN1cmVzIHNldHRpbmdzIGFyZSBmaWx0ZXJlZCBmcm9tIHRoZSBzdGFydCBvZiBpbml0aWFsaXphdGlvblxuICovXG5mdW5jdGlvbiBlYWdlckxvYWRTZXR0aW5ncygpOiB2b2lkIHtcbiAgcHJvZmlsZUNoZWNrcG9pbnQoJ2VhZ2VyTG9hZFNldHRpbmdzX3N0YXJ0JylcbiAgLy8gUGFyc2UgLS1zZXR0aW5ncyBmbGFnIGVhcmx5IHRvIGVuc3VyZSBzZXR0aW5ncyBhcmUgbG9hZGVkIGJlZm9yZSBpbml0KClcbiAgY29uc3Qgc2V0dGluZ3NGaWxlID0gZWFnZXJQYXJzZUNsaUZsYWcoJy0tc2V0dGluZ3MnKVxuICBpZiAoc2V0dGluZ3NGaWxlKSB7XG4gICAgbG9hZFNldHRpbmdzRnJvbUZsYWcoc2V0dGluZ3NGaWxlKVxuICB9XG5cbiAgLy8gUGFyc2UgLS1zZXR0aW5nLXNvdXJjZXMgZmxhZyBlYXJseSB0byBjb250cm9sIHdoaWNoIHNvdXJjZXMgYXJlIGxvYWRlZFxuICBjb25zdCBzZXR0aW5nU291cmNlc0FyZyA9IGVhZ2VyUGFyc2VDbGlGbGFnKCctLXNldHRpbmctc291cmNlcycpXG4gIGlmIChzZXR0aW5nU291cmNlc0FyZyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgbG9hZFNldHRpbmdTb3VyY2VzRnJvbUZsYWcoc2V0dGluZ1NvdXJjZXNBcmcpXG4gIH1cbiAgcHJvZmlsZUNoZWNrcG9pbnQoJ2VhZ2VyTG9hZFNldHRpbmdzX2VuZCcpXG59XG5cbmZ1bmN0aW9uIGluaXRpYWxpemVFbnRyeXBvaW50KGlzTm9uSW50ZXJhY3RpdmU6IGJvb2xlYW4pOiB2b2lkIHtcbiAgLy8gU2tpcCBpZiBhbHJlYWR5IHNldCAoZS5nLiwgYnkgU0RLIG9yIG90aGVyIGVudHJ5cG9pbnRzKVxuICBpZiAocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfRU5UUllQT0lOVCkge1xuICAgIHJldHVyblxuICB9XG5cbiAgY29uc3QgY2xpQXJncyA9IHByb2Nlc3MuYXJndi5zbGljZSgyKVxuXG4gIC8vIENoZWNrIGZvciBNQ1Agc2VydmUgY29tbWFuZCAoaGFuZGxlIGZsYWdzIGJlZm9yZSBtY3Agc2VydmUsIGUuZy4sIC0tZGVidWcgbWNwIHNlcnZlKVxuICBjb25zdCBtY3BJbmRleCA9IGNsaUFyZ3MuaW5kZXhPZignbWNwJylcbiAgaWYgKG1jcEluZGV4ICE9PSAtMSAmJiBjbGlBcmdzW21jcEluZGV4ICsgMV0gPT09ICdzZXJ2ZScpIHtcbiAgICBwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9FTlRSWVBPSU5UID0gJ21jcCdcbiAgICByZXR1cm5cbiAgfVxuXG4gIGlmIChpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9BQ1RJT04pKSB7XG4gICAgcHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfRU5UUllQT0lOVCA9ICdjbGF1ZGUtY29kZS1naXRodWItYWN0aW9uJ1xuICAgIHJldHVyblxuICB9XG5cbiAgLy8gTm90ZTogJ2xvY2FsLWFnZW50JyBlbnRyeXBvaW50IGlzIHNldCBieSB0aGUgbG9jYWwgYWdlbnQgbW9kZSBsYXVuY2hlclxuICAvLyB2aWEgQ0xBVURFX0NPREVfRU5UUllQT0lOVCBlbnYgdmFyIChoYW5kbGVkIGJ5IGVhcmx5IHJldHVybiBhYm92ZSlcblxuICAvLyBTZXQgYmFzZWQgb24gaW50ZXJhY3RpdmUgc3RhdHVzXG4gIHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0VOVFJZUE9JTlQgPSBpc05vbkludGVyYWN0aXZlID8gJ3Nkay1jbGknIDogJ2NsaSdcbn1cblxuLy8gU2V0IGJ5IGVhcmx5IGFyZ3YgcHJvY2Vzc2luZyB3aGVuIGBjbGF1ZGUgb3BlbiA8dXJsPmAgaXMgZGV0ZWN0ZWQgKGludGVyYWN0aXZlIG1vZGUgb25seSlcbnR5cGUgUGVuZGluZ0Nvbm5lY3QgPSB7XG4gIHVybDogc3RyaW5nIHwgdW5kZWZpbmVkXG4gIGF1dGhUb2tlbjogc3RyaW5nIHwgdW5kZWZpbmVkXG4gIGRhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zOiBib29sZWFuXG59XG5jb25zdCBfcGVuZGluZ0Nvbm5lY3Q6IFBlbmRpbmdDb25uZWN0IHwgdW5kZWZpbmVkID0gZmVhdHVyZSgnRElSRUNUX0NPTk5FQ1QnKVxuICA/IHsgdXJsOiB1bmRlZmluZWQsIGF1dGhUb2tlbjogdW5kZWZpbmVkLCBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uczogZmFsc2UgfVxuICA6IHVuZGVmaW5lZFxuXG4vLyBTZXQgYnkgZWFybHkgYXJndiBwcm9jZXNzaW5nIHdoZW4gYGNsYXVkZSBhc3Npc3RhbnQgW3Nlc3Npb25JZF1gIGlzIGRldGVjdGVkXG50eXBlIFBlbmRpbmdBc3Npc3RhbnRDaGF0ID0geyBzZXNzaW9uSWQ/OiBzdHJpbmc7IGRpc2NvdmVyOiBib29sZWFuIH1cbmNvbnN0IF9wZW5kaW5nQXNzaXN0YW50Q2hhdDogUGVuZGluZ0Fzc2lzdGFudENoYXQgfCB1bmRlZmluZWQgPSBmZWF0dXJlKFxuICAnS0FJUk9TJyxcbilcbiAgPyB7IHNlc3Npb25JZDogdW5kZWZpbmVkLCBkaXNjb3ZlcjogZmFsc2UgfVxuICA6IHVuZGVmaW5lZFxuXG4vLyBgY2xhdWRlIHNzaCA8aG9zdD4gW2Rpcl1gIOKAlCBwYXJzZWQgZnJvbSBhcmd2IGVhcmx5IChzYW1lIHBhdHRlcm4gYXNcbi8vIERJUkVDVF9DT05ORUNUIGFib3ZlKSBzbyB0aGUgbWFpbiBjb21tYW5kIHBhdGggY2FuIHBpY2sgaXQgdXAgYW5kIGhhbmRcbi8vIHRoZSBSRVBMIGFuIFNTSC1iYWNrZWQgc2Vzc2lvbiBpbnN0ZWFkIG9mIGEgbG9jYWwgb25lLlxudHlwZSBQZW5kaW5nU1NIID0ge1xuICBob3N0OiBzdHJpbmcgfCB1bmRlZmluZWRcbiAgY3dkOiBzdHJpbmcgfCB1bmRlZmluZWRcbiAgcGVybWlzc2lvbk1vZGU6IHN0cmluZyB8IHVuZGVmaW5lZFxuICBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uczogYm9vbGVhblxuICAvKiogLS1sb2NhbDogc3Bhd24gdGhlIGNoaWxkIENMSSBkaXJlY3RseSwgc2tpcCBzc2gvcHJvYmUvZGVwbG95LiBlMmUgdGVzdCBtb2RlLiAqL1xuICBsb2NhbDogYm9vbGVhblxuICAvKiogRXh0cmEgQ0xJIGFyZ3MgdG8gZm9yd2FyZCB0byB0aGUgcmVtb3RlIENMSSBvbiBpbml0aWFsIHNwYXduICgtLXJlc3VtZSwgLWMpLiAqL1xuICBleHRyYUNsaUFyZ3M6IHN0cmluZ1tdXG59XG5jb25zdCBfcGVuZGluZ1NTSDogUGVuZGluZ1NTSCB8IHVuZGVmaW5lZCA9IGZlYXR1cmUoJ1NTSF9SRU1PVEUnKVxuICA/IHtcbiAgICAgIGhvc3Q6IHVuZGVmaW5lZCxcbiAgICAgIGN3ZDogdW5kZWZpbmVkLFxuICAgICAgcGVybWlzc2lvbk1vZGU6IHVuZGVmaW5lZCxcbiAgICAgIGRhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zOiBmYWxzZSxcbiAgICAgIGxvY2FsOiBmYWxzZSxcbiAgICAgIGV4dHJhQ2xpQXJnczogW10sXG4gICAgfVxuICA6IHVuZGVmaW5lZFxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbWFpbigpIHtcbiAgcHJvZmlsZUNoZWNrcG9pbnQoJ21haW5fZnVuY3Rpb25fc3RhcnQnKVxuXG4gIC8vIFNFQ1VSSVRZOiBQcmV2ZW50IFdpbmRvd3MgZnJvbSBleGVjdXRpbmcgY29tbWFuZHMgZnJvbSBjdXJyZW50IGRpcmVjdG9yeVxuICAvLyBUaGlzIG11c3QgYmUgc2V0IGJlZm9yZSBBTlkgY29tbWFuZCBleGVjdXRpb24gdG8gcHJldmVudCBQQVRIIGhpamFja2luZyBhdHRhY2tzXG4gIC8vIFNlZTogaHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vZW4tdXMvd2luZG93cy93aW4zMi9hcGkvcHJvY2Vzc2Vudi9uZi1wcm9jZXNzZW52LXNlYXJjaHBhdGh3XG4gIHByb2Nlc3MuZW52Lk5vRGVmYXVsdEN1cnJlbnREaXJlY3RvcnlJbkV4ZVBhdGggPSAnMSdcblxuICAvLyBJbml0aWFsaXplIHdhcm5pbmcgaGFuZGxlciBlYXJseSB0byBjYXRjaCB3YXJuaW5nc1xuICBpbml0aWFsaXplV2FybmluZ0hhbmRsZXIoKVxuXG4gIHByb2Nlc3Mub24oJ2V4aXQnLCAoKSA9PiB7XG4gICAgcmVzZXRDdXJzb3IoKVxuICB9KVxuICBwcm9jZXNzLm9uKCdTSUdJTlQnLCAoKSA9PiB7XG4gICAgLy8gSW4gcHJpbnQgbW9kZSwgcHJpbnQudHMgcmVnaXN0ZXJzIGl0cyBvd24gU0lHSU5UIGhhbmRsZXIgdGhhdCBhYm9ydHNcbiAgICAvLyB0aGUgaW4tZmxpZ2h0IHF1ZXJ5IGFuZCBjYWxscyBncmFjZWZ1bFNodXRkb3duOyBza2lwIGhlcmUgdG8gYXZvaWRcbiAgICAvLyBwcmVlbXB0aW5nIGl0IHdpdGggYSBzeW5jaHJvbm91cyBwcm9jZXNzLmV4aXQoKS5cbiAgICBpZiAocHJvY2Vzcy5hcmd2LmluY2x1ZGVzKCctcCcpIHx8IHByb2Nlc3MuYXJndi5pbmNsdWRlcygnLS1wcmludCcpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgcHJvY2Vzcy5leGl0KDApXG4gIH0pXG4gIHByb2ZpbGVDaGVja3BvaW50KCdtYWluX3dhcm5pbmdfaGFuZGxlcl9pbml0aWFsaXplZCcpXG5cbiAgLy8gQ2hlY2sgZm9yIGNjOi8vIG9yIGNjK3VuaXg6Ly8gVVJMIGluIGFyZ3Yg4oCUIHJld3JpdGUgc28gdGhlIG1haW4gY29tbWFuZFxuICAvLyBoYW5kbGVzIGl0LCBnaXZpbmcgdGhlIGZ1bGwgaW50ZXJhY3RpdmUgVFVJIGluc3RlYWQgb2YgYSBzdHJpcHBlZC1kb3duIHN1YmNvbW1hbmQuXG4gIC8vIEZvciBoZWFkbGVzcyAoLXApLCB3ZSByZXdyaXRlIHRvIHRoZSBpbnRlcm5hbCBgb3BlbmAgc3ViY29tbWFuZC5cbiAgaWYgKGZlYXR1cmUoJ0RJUkVDVF9DT05ORUNUJykpIHtcbiAgICBjb25zdCByYXdDbGlBcmdzID0gcHJvY2Vzcy5hcmd2LnNsaWNlKDIpXG4gICAgY29uc3QgY2NJZHggPSByYXdDbGlBcmdzLmZpbmRJbmRleChcbiAgICAgIGEgPT4gYS5zdGFydHNXaXRoKCdjYzovLycpIHx8IGEuc3RhcnRzV2l0aCgnY2MrdW5peDovLycpLFxuICAgIClcbiAgICBpZiAoY2NJZHggIT09IC0xICYmIF9wZW5kaW5nQ29ubmVjdCkge1xuICAgICAgY29uc3QgY2NVcmwgPSByYXdDbGlBcmdzW2NjSWR4XSFcbiAgICAgIGNvbnN0IHsgcGFyc2VDb25uZWN0VXJsIH0gPSBhd2FpdCBpbXBvcnQoJy4vc2VydmVyL3BhcnNlQ29ubmVjdFVybC5qcycpXG4gICAgICBjb25zdCBwYXJzZWQgPSBwYXJzZUNvbm5lY3RVcmwoY2NVcmwpXG4gICAgICBfcGVuZGluZ0Nvbm5lY3QuZGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnMgPSByYXdDbGlBcmdzLmluY2x1ZGVzKFxuICAgICAgICAnLS1kYW5nZXJvdXNseS1za2lwLXBlcm1pc3Npb25zJyxcbiAgICAgIClcblxuICAgICAgaWYgKHJhd0NsaUFyZ3MuaW5jbHVkZXMoJy1wJykgfHwgcmF3Q2xpQXJncy5pbmNsdWRlcygnLS1wcmludCcpKSB7XG4gICAgICAgIC8vIEhlYWRsZXNzOiByZXdyaXRlIHRvIGludGVybmFsIGBvcGVuYCBzdWJjb21tYW5kXG4gICAgICAgIGNvbnN0IHN0cmlwcGVkID0gcmF3Q2xpQXJncy5maWx0ZXIoKF8sIGkpID0+IGkgIT09IGNjSWR4KVxuICAgICAgICBjb25zdCBkc3BJZHggPSBzdHJpcHBlZC5pbmRleE9mKCctLWRhbmdlcm91c2x5LXNraXAtcGVybWlzc2lvbnMnKVxuICAgICAgICBpZiAoZHNwSWR4ICE9PSAtMSkge1xuICAgICAgICAgIHN0cmlwcGVkLnNwbGljZShkc3BJZHgsIDEpXG4gICAgICAgIH1cbiAgICAgICAgcHJvY2Vzcy5hcmd2ID0gW1xuICAgICAgICAgIHByb2Nlc3MuYXJndlswXSEsXG4gICAgICAgICAgcHJvY2Vzcy5hcmd2WzFdISxcbiAgICAgICAgICAnb3BlbicsXG4gICAgICAgICAgY2NVcmwsXG4gICAgICAgICAgLi4uc3RyaXBwZWQsXG4gICAgICAgIF1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIEludGVyYWN0aXZlOiBzdHJpcCBjYzovLyBVUkwgYW5kIGZsYWdzLCBydW4gbWFpbiBjb21tYW5kXG4gICAgICAgIF9wZW5kaW5nQ29ubmVjdC51cmwgPSBwYXJzZWQuc2VydmVyVXJsXG4gICAgICAgIF9wZW5kaW5nQ29ubmVjdC5hdXRoVG9rZW4gPSBwYXJzZWQuYXV0aFRva2VuXG4gICAgICAgIGNvbnN0IHN0cmlwcGVkID0gcmF3Q2xpQXJncy5maWx0ZXIoKF8sIGkpID0+IGkgIT09IGNjSWR4KVxuICAgICAgICBjb25zdCBkc3BJZHggPSBzdHJpcHBlZC5pbmRleE9mKCctLWRhbmdlcm91c2x5LXNraXAtcGVybWlzc2lvbnMnKVxuICAgICAgICBpZiAoZHNwSWR4ICE9PSAtMSkge1xuICAgICAgICAgIHN0cmlwcGVkLnNwbGljZShkc3BJZHgsIDEpXG4gICAgICAgIH1cbiAgICAgICAgcHJvY2Vzcy5hcmd2ID0gW3Byb2Nlc3MuYXJndlswXSEsIHByb2Nlc3MuYXJndlsxXSEsIC4uLnN0cmlwcGVkXVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIC8vIEhhbmRsZSBkZWVwIGxpbmsgVVJJcyBlYXJseSDigJQgdGhpcyBpcyBpbnZva2VkIGJ5IHRoZSBPUyBwcm90b2NvbCBoYW5kbGVyXG4gIC8vIGFuZCBzaG91bGQgYmFpbCBvdXQgYmVmb3JlIGZ1bGwgaW5pdCBzaW5jZSBpdCBvbmx5IG5lZWRzIHRvIHBhcnNlIHRoZSBVUklcbiAgLy8gYW5kIG9wZW4gYSB0ZXJtaW5hbC5cbiAgaWYgKGZlYXR1cmUoJ0xPREVTVE9ORScpKSB7XG4gICAgY29uc3QgaGFuZGxlVXJpSWR4ID0gcHJvY2Vzcy5hcmd2LmluZGV4T2YoJy0taGFuZGxlLXVyaScpXG4gICAgaWYgKGhhbmRsZVVyaUlkeCAhPT0gLTEgJiYgcHJvY2Vzcy5hcmd2W2hhbmRsZVVyaUlkeCArIDFdKSB7XG4gICAgICBjb25zdCB7IGVuYWJsZUNvbmZpZ3MgfSA9IGF3YWl0IGltcG9ydCgnLi91dGlscy9jb25maWcuanMnKVxuICAgICAgZW5hYmxlQ29uZmlncygpXG4gICAgICBjb25zdCB1cmkgPSBwcm9jZXNzLmFyZ3ZbaGFuZGxlVXJpSWR4ICsgMV0hXG4gICAgICBjb25zdCB7IGhhbmRsZURlZXBMaW5rVXJpIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICcuL3V0aWxzL2RlZXBMaW5rL3Byb3RvY29sSGFuZGxlci5qcydcbiAgICAgIClcbiAgICAgIGNvbnN0IGV4aXRDb2RlID0gYXdhaXQgaGFuZGxlRGVlcExpbmtVcmkodXJpKVxuICAgICAgcHJvY2Vzcy5leGl0KGV4aXRDb2RlKVxuICAgIH1cblxuICAgIC8vIG1hY09TIFVSTCBoYW5kbGVyOiB3aGVuIExhdW5jaFNlcnZpY2VzIGxhdW5jaGVzIG91ciAuYXBwIGJ1bmRsZSwgdGhlXG4gICAgLy8gVVJMIGFycml2ZXMgdmlhIEFwcGxlIEV2ZW50IChub3QgYXJndikuIExhdW5jaFNlcnZpY2VzIG92ZXJ3cml0ZXNcbiAgICAvLyBfX0NGQnVuZGxlSWRlbnRpZmllciB0byB0aGUgbGF1bmNoaW5nIGJ1bmRsZSdzIElELCB3aGljaCBpcyBhIHByZWNpc2VcbiAgICAvLyBwb3NpdGl2ZSBzaWduYWwg4oCUIGNoZWFwZXIgdGhhbiBpbXBvcnRpbmcgYW5kIGd1ZXNzaW5nIHdpdGggaGV1cmlzdGljcy5cbiAgICBpZiAoXG4gICAgICBwcm9jZXNzLnBsYXRmb3JtID09PSAnZGFyd2luJyAmJlxuICAgICAgcHJvY2Vzcy5lbnYuX19DRkJ1bmRsZUlkZW50aWZpZXIgPT09XG4gICAgICAgICdjb20uYW50aHJvcGljLmNsYXVkZS1jb2RlLXVybC1oYW5kbGVyJ1xuICAgICkge1xuICAgICAgY29uc3QgeyBlbmFibGVDb25maWdzIH0gPSBhd2FpdCBpbXBvcnQoJy4vdXRpbHMvY29uZmlnLmpzJylcbiAgICAgIGVuYWJsZUNvbmZpZ3MoKVxuICAgICAgY29uc3QgeyBoYW5kbGVVcmxTY2hlbWVMYXVuY2ggfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICAgJy4vdXRpbHMvZGVlcExpbmsvcHJvdG9jb2xIYW5kbGVyLmpzJ1xuICAgICAgKVxuICAgICAgY29uc3QgdXJsU2NoZW1lUmVzdWx0ID0gYXdhaXQgaGFuZGxlVXJsU2NoZW1lTGF1bmNoKClcbiAgICAgIHByb2Nlc3MuZXhpdCh1cmxTY2hlbWVSZXN1bHQgPz8gMSlcbiAgICB9XG4gIH1cblxuICAvLyBgY2xhdWRlIGFzc2lzdGFudCBbc2Vzc2lvbklkXWAg4oCUIHN0YXNoIGFuZCBzdHJpcCBzbyB0aGUgbWFpblxuICAvLyBjb21tYW5kIGhhbmRsZXMgaXQsIGdpdmluZyB0aGUgZnVsbCBpbnRlcmFjdGl2ZSBUVUkuIFBvc2l0aW9uLTAgb25seVxuICAvLyAobWF0Y2hpbmcgdGhlIHNzaCBwYXR0ZXJuIGJlbG93KSDigJQgaW5kZXhPZiB3b3VsZCBmYWxzZS1wb3NpdGl2ZSBvblxuICAvLyBgY2xhdWRlIC1wIFwiZXhwbGFpbiBhc3Npc3RhbnRcImAuIFJvb3QtZmxhZy1iZWZvcmUtc3ViY29tbWFuZFxuICAvLyAoZS5nLiBgLS1kZWJ1ZyBhc3Npc3RhbnRgKSBmYWxscyB0aHJvdWdoIHRvIHRoZSBzdHViLCB3aGljaFxuICAvLyBwcmludHMgdXNhZ2UuXG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSAmJiBfcGVuZGluZ0Fzc2lzdGFudENoYXQpIHtcbiAgICBjb25zdCByYXdBcmdzID0gcHJvY2Vzcy5hcmd2LnNsaWNlKDIpXG4gICAgaWYgKHJhd0FyZ3NbMF0gPT09ICdhc3Npc3RhbnQnKSB7XG4gICAgICBjb25zdCBuZXh0QXJnID0gcmF3QXJnc1sxXVxuICAgICAgaWYgKG5leHRBcmcgJiYgIW5leHRBcmcuc3RhcnRzV2l0aCgnLScpKSB7XG4gICAgICAgIF9wZW5kaW5nQXNzaXN0YW50Q2hhdC5zZXNzaW9uSWQgPSBuZXh0QXJnXG4gICAgICAgIHJhd0FyZ3Muc3BsaWNlKDAsIDIpIC8vIGRyb3AgJ2Fzc2lzdGFudCcgYW5kIHNlc3Npb25JZFxuICAgICAgICBwcm9jZXNzLmFyZ3YgPSBbcHJvY2Vzcy5hcmd2WzBdISwgcHJvY2Vzcy5hcmd2WzFdISwgLi4ucmF3QXJnc11cbiAgICAgIH0gZWxzZSBpZiAoIW5leHRBcmcpIHtcbiAgICAgICAgX3BlbmRpbmdBc3Npc3RhbnRDaGF0LmRpc2NvdmVyID0gdHJ1ZVxuICAgICAgICByYXdBcmdzLnNwbGljZSgwLCAxKSAvLyBkcm9wICdhc3Npc3RhbnQnXG4gICAgICAgIHByb2Nlc3MuYXJndiA9IFtwcm9jZXNzLmFyZ3ZbMF0hLCBwcm9jZXNzLmFyZ3ZbMV0hLCAuLi5yYXdBcmdzXVxuICAgICAgfVxuICAgICAgLy8gZWxzZTogYGNsYXVkZSBhc3Npc3RhbnQgLS1oZWxwYCDihpIgZmFsbCB0aHJvdWdoIHRvIHN0dWJcbiAgICB9XG4gIH1cblxuICAvLyBgY2xhdWRlIHNzaCA8aG9zdD4gW2Rpcl1gIOKAlCBzdHJpcCBmcm9tIGFyZ3Ygc28gdGhlIG1haW4gY29tbWFuZCBoYW5kbGVyXG4gIC8vIHJ1bnMgKGZ1bGwgaW50ZXJhY3RpdmUgVFVJKSwgc3Rhc2ggdGhlIGhvc3QvZGlyIGZvciB0aGUgUkVQTCBicmFuY2ggYXRcbiAgLy8gfmxpbmUgMzcyMCB0byBwaWNrIHVwLiBIZWFkbGVzcyAoLXApIG1vZGUgbm90IHN1cHBvcnRlZCBpbiB2MTogU1NIXG4gIC8vIHNlc3Npb25zIG5lZWQgdGhlIGxvY2FsIFJFUEwgdG8gZHJpdmUgdGhlbSAoaW50ZXJydXB0LCBwZXJtaXNzaW9ucykuXG4gIGlmIChmZWF0dXJlKCdTU0hfUkVNT1RFJykgJiYgX3BlbmRpbmdTU0gpIHtcbiAgICBjb25zdCByYXdDbGlBcmdzID0gcHJvY2Vzcy5hcmd2LnNsaWNlKDIpXG4gICAgLy8gU1NILXNwZWNpZmljIGZsYWdzIGNhbiBhcHBlYXIgYmVmb3JlIHRoZSBob3N0IHBvc2l0aW9uYWwgKGUuZy5cbiAgICAvLyBgc3NoIC0tcGVybWlzc2lvbi1tb2RlIGF1dG8gaG9zdCAvdG1wYCDigJQgc3RhbmRhcmQgUE9TSVggZmxhZ3MtYmVmb3JlLVxuICAgIC8vIHBvc2l0aW9uYWxzKS4gUHVsbCB0aGVtIGFsbCBvdXQgQkVGT1JFIGNoZWNraW5nIHdoZXRoZXIgYSBob3N0IHdhc1xuICAgIC8vIGdpdmVuLCBzbyBgY2xhdWRlIHNzaCAtLXBlcm1pc3Npb24tbW9kZSBhdXRvIGhvc3RgIGFuZCBgY2xhdWRlIHNzaCBob3N0XG4gICAgLy8gLS1wZXJtaXNzaW9uLW1vZGUgYXV0b2AgYXJlIGVxdWl2YWxlbnQuIFRoZSBob3N0IGNoZWNrIGJlbG93IG9ubHkgbmVlZHNcbiAgICAvLyB0byBndWFyZCBhZ2FpbnN0IGAtaGAvYC0taGVscGAgKHdoaWNoIGNvbW1hbmRlciBzaG91bGQgaGFuZGxlKS5cbiAgICBpZiAocmF3Q2xpQXJnc1swXSA9PT0gJ3NzaCcpIHtcbiAgICAgIGNvbnN0IGxvY2FsSWR4ID0gcmF3Q2xpQXJncy5pbmRleE9mKCctLWxvY2FsJylcbiAgICAgIGlmIChsb2NhbElkeCAhPT0gLTEpIHtcbiAgICAgICAgX3BlbmRpbmdTU0gubG9jYWwgPSB0cnVlXG4gICAgICAgIHJhd0NsaUFyZ3Muc3BsaWNlKGxvY2FsSWR4LCAxKVxuICAgICAgfVxuICAgICAgY29uc3QgZHNwSWR4ID0gcmF3Q2xpQXJncy5pbmRleE9mKCctLWRhbmdlcm91c2x5LXNraXAtcGVybWlzc2lvbnMnKVxuICAgICAgaWYgKGRzcElkeCAhPT0gLTEpIHtcbiAgICAgICAgX3BlbmRpbmdTU0guZGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnMgPSB0cnVlXG4gICAgICAgIHJhd0NsaUFyZ3Muc3BsaWNlKGRzcElkeCwgMSlcbiAgICAgIH1cbiAgICAgIGNvbnN0IHBtSWR4ID0gcmF3Q2xpQXJncy5pbmRleE9mKCctLXBlcm1pc3Npb24tbW9kZScpXG4gICAgICBpZiAoXG4gICAgICAgIHBtSWR4ICE9PSAtMSAmJlxuICAgICAgICByYXdDbGlBcmdzW3BtSWR4ICsgMV0gJiZcbiAgICAgICAgIXJhd0NsaUFyZ3NbcG1JZHggKyAxXSEuc3RhcnRzV2l0aCgnLScpXG4gICAgICApIHtcbiAgICAgICAgX3BlbmRpbmdTU0gucGVybWlzc2lvbk1vZGUgPSByYXdDbGlBcmdzW3BtSWR4ICsgMV1cbiAgICAgICAgcmF3Q2xpQXJncy5zcGxpY2UocG1JZHgsIDIpXG4gICAgICB9XG4gICAgICBjb25zdCBwbUVxSWR4ID0gcmF3Q2xpQXJncy5maW5kSW5kZXgoYSA9PlxuICAgICAgICBhLnN0YXJ0c1dpdGgoJy0tcGVybWlzc2lvbi1tb2RlPScpLFxuICAgICAgKVxuICAgICAgaWYgKHBtRXFJZHggIT09IC0xKSB7XG4gICAgICAgIF9wZW5kaW5nU1NILnBlcm1pc3Npb25Nb2RlID0gcmF3Q2xpQXJnc1twbUVxSWR4XSEuc3BsaXQoJz0nKVsxXVxuICAgICAgICByYXdDbGlBcmdzLnNwbGljZShwbUVxSWR4LCAxKVxuICAgICAgfVxuICAgICAgLy8gRm9yd2FyZCBzZXNzaW9uLXJlc3VtZSArIG1vZGVsIGZsYWdzIHRvIHRoZSByZW1vdGUgQ0xJJ3MgaW5pdGlhbCBzcGF3bi5cbiAgICAgIC8vIC0tY29udGludWUvLWMgYW5kIC0tcmVzdW1lIDx1dWlkPiBvcGVyYXRlIG9uIHRoZSBSRU1PVEUgc2Vzc2lvbiBoaXN0b3J5XG4gICAgICAvLyAod2hpY2ggcGVyc2lzdHMgdW5kZXIgdGhlIHJlbW90ZSdzIH4vLmNsYXVkZS9wcm9qZWN0cy88Y3dkPi8pLlxuICAgICAgLy8gLS1tb2RlbCBjb250cm9scyB3aGljaCBtb2RlbCB0aGUgcmVtb3RlIHVzZXMuXG4gICAgICBjb25zdCBleHRyYWN0RmxhZyA9IChcbiAgICAgICAgZmxhZzogc3RyaW5nLFxuICAgICAgICBvcHRzOiB7IGhhc1ZhbHVlPzogYm9vbGVhbjsgYXM/OiBzdHJpbmcgfSA9IHt9LFxuICAgICAgKSA9PiB7XG4gICAgICAgIGNvbnN0IGkgPSByYXdDbGlBcmdzLmluZGV4T2YoZmxhZylcbiAgICAgICAgaWYgKGkgIT09IC0xKSB7XG4gICAgICAgICAgX3BlbmRpbmdTU0guZXh0cmFDbGlBcmdzLnB1c2gob3B0cy5hcyA/PyBmbGFnKVxuICAgICAgICAgIGNvbnN0IHZhbCA9IHJhd0NsaUFyZ3NbaSArIDFdXG4gICAgICAgICAgaWYgKG9wdHMuaGFzVmFsdWUgJiYgdmFsICYmICF2YWwuc3RhcnRzV2l0aCgnLScpKSB7XG4gICAgICAgICAgICBfcGVuZGluZ1NTSC5leHRyYUNsaUFyZ3MucHVzaCh2YWwpXG4gICAgICAgICAgICByYXdDbGlBcmdzLnNwbGljZShpLCAyKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByYXdDbGlBcmdzLnNwbGljZShpLCAxKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjb25zdCBlcUkgPSByYXdDbGlBcmdzLmZpbmRJbmRleChhID0+IGEuc3RhcnRzV2l0aChgJHtmbGFnfT1gKSlcbiAgICAgICAgaWYgKGVxSSAhPT0gLTEpIHtcbiAgICAgICAgICBfcGVuZGluZ1NTSC5leHRyYUNsaUFyZ3MucHVzaChcbiAgICAgICAgICAgIG9wdHMuYXMgPz8gZmxhZyxcbiAgICAgICAgICAgIHJhd0NsaUFyZ3NbZXFJXSEuc2xpY2UoZmxhZy5sZW5ndGggKyAxKSxcbiAgICAgICAgICApXG4gICAgICAgICAgcmF3Q2xpQXJncy5zcGxpY2UoZXFJLCAxKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBleHRyYWN0RmxhZygnLWMnLCB7IGFzOiAnLS1jb250aW51ZScgfSlcbiAgICAgIGV4dHJhY3RGbGFnKCctLWNvbnRpbnVlJylcbiAgICAgIGV4dHJhY3RGbGFnKCctLXJlc3VtZScsIHsgaGFzVmFsdWU6IHRydWUgfSlcbiAgICAgIGV4dHJhY3RGbGFnKCctLW1vZGVsJywgeyBoYXNWYWx1ZTogdHJ1ZSB9KVxuICAgIH1cbiAgICAvLyBBZnRlciBwcmUtZXh0cmFjdGlvbiwgYW55IHJlbWFpbmluZyBkYXNoLWFyZyBhdCBbMV0gaXMgZWl0aGVyIC1oLy0taGVscFxuICAgIC8vIChjb21tYW5kZXIgaGFuZGxlcykgb3IgYW4gdW5rbm93bi10by1zc2ggZmxhZyAoZmFsbCB0aHJvdWdoIHRvIGNvbW1hbmRlclxuICAgIC8vIHNvIGl0IHN1cmZhY2VzIGEgcHJvcGVyIGVycm9yKS4gT25seSBhIG5vbi1kYXNoIGFyZyBpcyB0aGUgaG9zdC5cbiAgICBpZiAoXG4gICAgICByYXdDbGlBcmdzWzBdID09PSAnc3NoJyAmJlxuICAgICAgcmF3Q2xpQXJnc1sxXSAmJlxuICAgICAgIXJhd0NsaUFyZ3NbMV0uc3RhcnRzV2l0aCgnLScpXG4gICAgKSB7XG4gICAgICBfcGVuZGluZ1NTSC5ob3N0ID0gcmF3Q2xpQXJnc1sxXVxuICAgICAgLy8gT3B0aW9uYWwgcG9zaXRpb25hbCBjd2QuXG4gICAgICBsZXQgY29uc3VtZWQgPSAyXG4gICAgICBpZiAocmF3Q2xpQXJnc1syXSAmJiAhcmF3Q2xpQXJnc1syXS5zdGFydHNXaXRoKCctJykpIHtcbiAgICAgICAgX3BlbmRpbmdTU0guY3dkID0gcmF3Q2xpQXJnc1syXVxuICAgICAgICBjb25zdW1lZCA9IDNcbiAgICAgIH1cbiAgICAgIGNvbnN0IHJlc3QgPSByYXdDbGlBcmdzLnNsaWNlKGNvbnN1bWVkKVxuXG4gICAgICAvLyBIZWFkbGVzcyAoLXApIG1vZGUgaXMgbm90IHN1cHBvcnRlZCB3aXRoIFNTSCBpbiB2MSDigJQgcmVqZWN0IGVhcmx5XG4gICAgICAvLyBzbyB0aGUgZmxhZyBkb2Vzbid0IHNpbGVudGx5IGNhdXNlIGxvY2FsIGV4ZWN1dGlvbi5cbiAgICAgIGlmIChyZXN0LmluY2x1ZGVzKCctcCcpIHx8IHJlc3QuaW5jbHVkZXMoJy0tcHJpbnQnKSkge1xuICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAnRXJyb3I6IGhlYWRsZXNzICgtcC8tLXByaW50KSBtb2RlIGlzIG5vdCBzdXBwb3J0ZWQgd2l0aCBjbGF1ZGUgc3NoXFxuJyxcbiAgICAgICAgKVxuICAgICAgICBncmFjZWZ1bFNodXRkb3duU3luYygxKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gUmV3cml0ZSBhcmd2IHNvIHRoZSBtYWluIGNvbW1hbmQgc2VlcyByZW1haW5pbmcgZmxhZ3MgYnV0IG5vdCBgc3NoYC5cbiAgICAgIHByb2Nlc3MuYXJndiA9IFtwcm9jZXNzLmFyZ3ZbMF0hLCBwcm9jZXNzLmFyZ3ZbMV0hLCAuLi5yZXN0XVxuICAgIH1cbiAgfVxuXG4gIC8vIENoZWNrIGZvciAtcC8tLXByaW50IGFuZCAtLWluaXQtb25seSBmbGFncyBlYXJseSB0byBzZXQgaXNJbnRlcmFjdGl2ZVNlc3Npb24gYmVmb3JlIGluaXQoKVxuICAvLyBUaGlzIGlzIG5lZWRlZCBiZWNhdXNlIHRlbGVtZXRyeSBpbml0aWFsaXphdGlvbiBjYWxscyBhdXRoIGZ1bmN0aW9ucyB0aGF0IG5lZWQgdGhpcyBmbGFnXG4gIGNvbnN0IGNsaUFyZ3MgPSBwcm9jZXNzLmFyZ3Yuc2xpY2UoMilcbiAgY29uc3QgaGFzUHJpbnRGbGFnID0gY2xpQXJncy5pbmNsdWRlcygnLXAnKSB8fCBjbGlBcmdzLmluY2x1ZGVzKCctLXByaW50JylcbiAgY29uc3QgaGFzSW5pdE9ubHlGbGFnID0gY2xpQXJncy5pbmNsdWRlcygnLS1pbml0LW9ubHknKVxuICBjb25zdCBoYXNTZGtVcmwgPSBjbGlBcmdzLnNvbWUoYXJnID0+IGFyZy5zdGFydHNXaXRoKCctLXNkay11cmwnKSlcbiAgY29uc3QgaXNOb25JbnRlcmFjdGl2ZSA9XG4gICAgaGFzUHJpbnRGbGFnIHx8IGhhc0luaXRPbmx5RmxhZyB8fCBoYXNTZGtVcmwgfHwgIXByb2Nlc3Muc3Rkb3V0LmlzVFRZXG5cbiAgLy8gU3RvcCBjYXB0dXJpbmcgZWFybHkgaW5wdXQgZm9yIG5vbi1pbnRlcmFjdGl2ZSBtb2Rlc1xuICBpZiAoaXNOb25JbnRlcmFjdGl2ZSkge1xuICAgIHN0b3BDYXB0dXJpbmdFYXJseUlucHV0KClcbiAgfVxuXG4gIC8vIFNldCBzaW1wbGlmaWVkIHRyYWNraW5nIGZpZWxkc1xuICBjb25zdCBpc0ludGVyYWN0aXZlID0gIWlzTm9uSW50ZXJhY3RpdmVcbiAgc2V0SXNJbnRlcmFjdGl2ZShpc0ludGVyYWN0aXZlKVxuXG4gIC8vIEluaXRpYWxpemUgZW50cnlwb2ludCBiYXNlZCBvbiBtb2RlIC0gbmVlZHMgdG8gYmUgc2V0IGJlZm9yZSBhbnkgZXZlbnQgaXMgbG9nZ2VkXG4gIGluaXRpYWxpemVFbnRyeXBvaW50KGlzTm9uSW50ZXJhY3RpdmUpXG5cbiAgLy8gRGV0ZXJtaW5lIGNsaWVudCB0eXBlXG4gIGNvbnN0IGNsaWVudFR5cGUgPSAoKCkgPT4ge1xuICAgIGlmIChpc0VudlRydXRoeShwcm9jZXNzLmVudi5HSVRIVUJfQUNUSU9OUykpIHJldHVybiAnZ2l0aHViLWFjdGlvbidcbiAgICBpZiAocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfRU5UUllQT0lOVCA9PT0gJ3Nkay10cycpIHJldHVybiAnc2RrLXR5cGVzY3JpcHQnXG4gICAgaWYgKHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0VOVFJZUE9JTlQgPT09ICdzZGstcHknKSByZXR1cm4gJ3Nkay1weXRob24nXG4gICAgaWYgKHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0VOVFJZUE9JTlQgPT09ICdzZGstY2xpJykgcmV0dXJuICdzZGstY2xpJ1xuICAgIGlmIChwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9FTlRSWVBPSU5UID09PSAnY2xhdWRlLXZzY29kZScpXG4gICAgICByZXR1cm4gJ2NsYXVkZS12c2NvZGUnXG4gICAgaWYgKHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0VOVFJZUE9JTlQgPT09ICdsb2NhbC1hZ2VudCcpXG4gICAgICByZXR1cm4gJ2xvY2FsLWFnZW50J1xuICAgIGlmIChwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9FTlRSWVBPSU5UID09PSAnY2xhdWRlLWRlc2t0b3AnKVxuICAgICAgcmV0dXJuICdjbGF1ZGUtZGVza3RvcCdcblxuICAgIC8vIENoZWNrIGlmIHNlc3Npb24taW5ncmVzcyB0b2tlbiBpcyBwcm92aWRlZCAoaW5kaWNhdGVzIHJlbW90ZSBzZXNzaW9uKVxuICAgIGNvbnN0IGhhc1Nlc3Npb25JbmdyZXNzVG9rZW4gPVxuICAgICAgcHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfU0VTU0lPTl9BQ0NFU1NfVE9LRU4gfHxcbiAgICAgIHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX1dFQlNPQ0tFVF9BVVRIX0ZJTEVfREVTQ1JJUFRPUlxuICAgIGlmIChcbiAgICAgIHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0VOVFJZUE9JTlQgPT09ICdyZW1vdGUnIHx8XG4gICAgICBoYXNTZXNzaW9uSW5ncmVzc1Rva2VuXG4gICAgKSB7XG4gICAgICByZXR1cm4gJ3JlbW90ZSdcbiAgICB9XG5cbiAgICByZXR1cm4gJ2NsaSdcbiAgfSkoKVxuICBzZXRDbGllbnRUeXBlKGNsaWVudFR5cGUpXG5cbiAgY29uc3QgcHJldmlld0Zvcm1hdCA9IHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX1FVRVNUSU9OX1BSRVZJRVdfRk9STUFUXG4gIGlmIChwcmV2aWV3Rm9ybWF0ID09PSAnbWFya2Rvd24nIHx8IHByZXZpZXdGb3JtYXQgPT09ICdodG1sJykge1xuICAgIHNldFF1ZXN0aW9uUHJldmlld0Zvcm1hdChwcmV2aWV3Rm9ybWF0KVxuICB9IGVsc2UgaWYgKFxuICAgICFjbGllbnRUeXBlLnN0YXJ0c1dpdGgoJ3Nkay0nKSAmJlxuICAgIC8vIERlc2t0b3AgYW5kIENDUiBwYXNzIHByZXZpZXdGb3JtYXQgdmlhIHRvb2xDb25maWc7IHdoZW4gdGhlIGZlYXR1cmUgaXNcbiAgICAvLyBnYXRlZCBvZmYgdGhleSBwYXNzIHVuZGVmaW5lZCDigJQgZG9uJ3Qgb3ZlcnJpZGUgdGhhdCB3aXRoIG1hcmtkb3duLlxuICAgIGNsaWVudFR5cGUgIT09ICdjbGF1ZGUtZGVza3RvcCcgJiZcbiAgICBjbGllbnRUeXBlICE9PSAnbG9jYWwtYWdlbnQnICYmXG4gICAgY2xpZW50VHlwZSAhPT0gJ3JlbW90ZSdcbiAgKSB7XG4gICAgc2V0UXVlc3Rpb25QcmV2aWV3Rm9ybWF0KCdtYXJrZG93bicpXG4gIH1cblxuICAvLyBUYWcgc2Vzc2lvbnMgY3JlYXRlZCB2aWEgYGNsYXVkZSByZW1vdGUtY29udHJvbGAgc28gdGhlIGJhY2tlbmQgY2FuIGlkZW50aWZ5IHRoZW1cbiAgaWYgKHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0VOVklST05NRU5UX0tJTkQgPT09ICdicmlkZ2UnKSB7XG4gICAgc2V0U2Vzc2lvblNvdXJjZSgncmVtb3RlLWNvbnRyb2wnKVxuICB9XG5cbiAgcHJvZmlsZUNoZWNrcG9pbnQoJ21haW5fY2xpZW50X3R5cGVfZGV0ZXJtaW5lZCcpXG5cbiAgLy8gUGFyc2UgYW5kIGxvYWQgc2V0dGluZ3MgZmxhZ3MgZWFybHksIGJlZm9yZSBpbml0KClcbiAgZWFnZXJMb2FkU2V0dGluZ3MoKVxuXG4gIHByb2ZpbGVDaGVja3BvaW50KCdtYWluX2JlZm9yZV9ydW4nKVxuXG4gIGF3YWl0IHJ1bigpXG4gIHByb2ZpbGVDaGVja3BvaW50KCdtYWluX2FmdGVyX3J1bicpXG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldElucHV0UHJvbXB0KFxuICBwcm9tcHQ6IHN0cmluZyxcbiAgaW5wdXRGb3JtYXQ6ICd0ZXh0JyB8ICdzdHJlYW0tanNvbicsXG4pOiBQcm9taXNlPHN0cmluZyB8IEFzeW5jSXRlcmFibGU8c3RyaW5nPj4ge1xuICBpZiAoXG4gICAgIXByb2Nlc3Muc3RkaW4uaXNUVFkgJiZcbiAgICAvLyBJbnB1dCBoaWphY2tpbmcgYnJlYWtzIE1DUC5cbiAgICAhcHJvY2Vzcy5hcmd2LmluY2x1ZGVzKCdtY3AnKVxuICApIHtcbiAgICBpZiAoaW5wdXRGb3JtYXQgPT09ICdzdHJlYW0tanNvbicpIHtcbiAgICAgIHJldHVybiBwcm9jZXNzLnN0ZGluXG4gICAgfVxuICAgIHByb2Nlc3Muc3RkaW4uc2V0RW5jb2RpbmcoJ3V0ZjgnKVxuICAgIGxldCBkYXRhID0gJydcbiAgICBjb25zdCBvbkRhdGEgPSAoY2h1bms6IHN0cmluZykgPT4ge1xuICAgICAgZGF0YSArPSBjaHVua1xuICAgIH1cbiAgICBwcm9jZXNzLnN0ZGluLm9uKCdkYXRhJywgb25EYXRhKVxuICAgIC8vIElmIG5vIGRhdGEgYXJyaXZlcyBpbiAzcywgc3RvcCB3YWl0aW5nIGFuZCB3YXJuLiBTdGRpbiBpcyBsaWtlbHkgYW5cbiAgICAvLyBpbmhlcml0ZWQgcGlwZSBmcm9tIGEgcGFyZW50IHRoYXQgaXNuJ3Qgd3JpdGluZyAoc3VicHJvY2VzcyBzcGF3bmVkXG4gICAgLy8gd2l0aG91dCBleHBsaWNpdCBzdGRpbiBoYW5kbGluZykuIDNzIGNvdmVycyBzbG93IHByb2R1Y2VycyBsaWtlIGN1cmwsXG4gICAgLy8ganEgb24gbGFyZ2UgZmlsZXMsIHB5dGhvbiB3aXRoIGltcG9ydCBvdmVyaGVhZC4gVGhlIHdhcm5pbmcgbWFrZXNcbiAgICAvLyBzaWxlbnQgZGF0YSBsb3NzIHZpc2libGUgZm9yIHRoZSByYXJlIHByb2R1Y2VyIHRoYXQncyBzbG93ZXIgc3RpbGwuXG4gICAgY29uc3QgdGltZWRPdXQgPSBhd2FpdCBwZWVrRm9yU3RkaW5EYXRhKHByb2Nlc3Muc3RkaW4sIDMwMDApXG4gICAgcHJvY2Vzcy5zdGRpbi5vZmYoJ2RhdGEnLCBvbkRhdGEpXG4gICAgaWYgKHRpbWVkT3V0KSB7XG4gICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgJ1dhcm5pbmc6IG5vIHN0ZGluIGRhdGEgcmVjZWl2ZWQgaW4gM3MsIHByb2NlZWRpbmcgd2l0aG91dCBpdC4gJyArXG4gICAgICAgICAgJ0lmIHBpcGluZyBmcm9tIGEgc2xvdyBjb21tYW5kLCByZWRpcmVjdCBzdGRpbiBleHBsaWNpdGx5OiA8IC9kZXYvbnVsbCB0byBza2lwLCBvciB3YWl0IGxvbmdlci5cXG4nLFxuICAgICAgKVxuICAgIH1cbiAgICByZXR1cm4gW3Byb21wdCwgZGF0YV0uZmlsdGVyKEJvb2xlYW4pLmpvaW4oJ1xcbicpXG4gIH1cbiAgcmV0dXJuIHByb21wdFxufVxuXG5hc3luYyBmdW5jdGlvbiBydW4oKTogUHJvbWlzZTxDb21tYW5kZXJDb21tYW5kPiB7XG4gIHByb2ZpbGVDaGVja3BvaW50KCdydW5fZnVuY3Rpb25fc3RhcnQnKVxuXG4gIC8vIENyZWF0ZSBoZWxwIGNvbmZpZyB0aGF0IHNvcnRzIG9wdGlvbnMgYnkgbG9uZyBvcHRpb24gbmFtZS5cbiAgLy8gQ29tbWFuZGVyIHN1cHBvcnRzIGNvbXBhcmVPcHRpb25zIGF0IHJ1bnRpbWUgYnV0IEBjb21tYW5kZXItanMvZXh0cmEtdHlwaW5nc1xuICAvLyBkb2Vzbid0IGluY2x1ZGUgaXQgaW4gdGhlIHR5cGUgZGVmaW5pdGlvbnMsIHNvIHdlIHVzZSBPYmplY3QuYXNzaWduIHRvIGFkZCBpdC5cbiAgZnVuY3Rpb24gY3JlYXRlU29ydGVkSGVscENvbmZpZygpOiB7XG4gICAgc29ydFN1YmNvbW1hbmRzOiB0cnVlXG4gICAgc29ydE9wdGlvbnM6IHRydWVcbiAgfSB7XG4gICAgY29uc3QgZ2V0T3B0aW9uU29ydEtleSA9IChvcHQ6IE9wdGlvbik6IHN0cmluZyA9PlxuICAgICAgb3B0Lmxvbmc/LnJlcGxhY2UoL14tLS8sICcnKSA/PyBvcHQuc2hvcnQ/LnJlcGxhY2UoL14tLywgJycpID8/ICcnXG4gICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oXG4gICAgICB7IHNvcnRTdWJjb21tYW5kczogdHJ1ZSwgc29ydE9wdGlvbnM6IHRydWUgfSBhcyBjb25zdCxcbiAgICAgIHtcbiAgICAgICAgY29tcGFyZU9wdGlvbnM6IChhOiBPcHRpb24sIGI6IE9wdGlvbikgPT5cbiAgICAgICAgICBnZXRPcHRpb25Tb3J0S2V5KGEpLmxvY2FsZUNvbXBhcmUoZ2V0T3B0aW9uU29ydEtleShiKSksXG4gICAgICB9LFxuICAgIClcbiAgfVxuICBjb25zdCBwcm9ncmFtID0gbmV3IENvbW1hbmRlckNvbW1hbmQoKVxuICAgIC5jb25maWd1cmVIZWxwKGNyZWF0ZVNvcnRlZEhlbHBDb25maWcoKSlcbiAgICAuZW5hYmxlUG9zaXRpb25hbE9wdGlvbnMoKVxuICBwcm9maWxlQ2hlY2twb2ludCgncnVuX2NvbW1hbmRlcl9pbml0aWFsaXplZCcpXG5cbiAgLy8gVXNlIHByZUFjdGlvbiBob29rIHRvIHJ1biBpbml0aWFsaXphdGlvbiBvbmx5IHdoZW4gZXhlY3V0aW5nIGEgY29tbWFuZCxcbiAgLy8gbm90IHdoZW4gZGlzcGxheWluZyBoZWxwLiBUaGlzIGF2b2lkcyB0aGUgbmVlZCBmb3IgZW52IHZhcmlhYmxlIHNpZ25hbGluZy5cbiAgcHJvZ3JhbS5ob29rKCdwcmVBY3Rpb24nLCBhc3luYyB0aGlzQ29tbWFuZCA9PiB7XG4gICAgcHJvZmlsZUNoZWNrcG9pbnQoJ3ByZUFjdGlvbl9zdGFydCcpXG4gICAgLy8gQXdhaXQgYXN5bmMgc3VicHJvY2VzcyBsb2FkcyBzdGFydGVkIGF0IG1vZHVsZSBldmFsdWF0aW9uIChsaW5lcyAxMi0yMCkuXG4gICAgLy8gTmVhcmx5IGZyZWUg4oCUIHN1YnByb2Nlc3NlcyBjb21wbGV0ZSBkdXJpbmcgdGhlIH4xMzVtcyBvZiBpbXBvcnRzIGFib3ZlLlxuICAgIC8vIE11c3QgcmVzb2x2ZSBiZWZvcmUgaW5pdCgpIHdoaWNoIHRyaWdnZXJzIHRoZSBmaXJzdCBzZXR0aW5ncyByZWFkXG4gICAgLy8gKGFwcGx5U2FmZUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzIOKGkiBnZXRTZXR0aW5nc0ZvclNvdXJjZSgncG9saWN5U2V0dGluZ3MnKVxuICAgIC8vIOKGkiBpc1JlbW90ZU1hbmFnZWRTZXR0aW5nc0VsaWdpYmxlIOKGkiBzeW5jIGtleWNoYWluIHJlYWRzIG90aGVyd2lzZSB+NjVtcykuXG4gICAgYXdhaXQgUHJvbWlzZS5hbGwoW1xuICAgICAgZW5zdXJlTWRtU2V0dGluZ3NMb2FkZWQoKSxcbiAgICAgIGVuc3VyZUtleWNoYWluUHJlZmV0Y2hDb21wbGV0ZWQoKSxcbiAgICBdKVxuICAgIHByb2ZpbGVDaGVja3BvaW50KCdwcmVBY3Rpb25fYWZ0ZXJfbWRtJylcbiAgICBhd2FpdCBpbml0KClcbiAgICBwcm9maWxlQ2hlY2twb2ludCgncHJlQWN0aW9uX2FmdGVyX2luaXQnKVxuXG4gICAgLy8gcHJvY2Vzcy50aXRsZSBvbiBXaW5kb3dzIHNldHMgdGhlIGNvbnNvbGUgdGl0bGUgZGlyZWN0bHk7IG9uIFBPU0lYLFxuICAgIC8vIHRlcm1pbmFsIHNoZWxsIGludGVncmF0aW9uIG1heSBtaXJyb3IgdGhlIHByb2Nlc3MgbmFtZSB0byB0aGUgdGFiLlxuICAgIC8vIEFmdGVyIGluaXQoKSBzbyBzZXR0aW5ncy5qc29uIGVudiBjYW4gYWxzbyBnYXRlIHRoaXMgKGdoLTQ3NjUpLlxuICAgIGlmICghaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfRElTQUJMRV9URVJNSU5BTF9USVRMRSkpIHtcbiAgICAgIHByb2Nlc3MudGl0bGUgPSAnY2xhdWRlJ1xuICAgIH1cblxuICAgIC8vIEF0dGFjaCBsb2dnaW5nIHNpbmtzIHNvIHN1YmNvbW1hbmQgaGFuZGxlcnMgY2FuIHVzZSBsb2dFdmVudC9sb2dFcnJvci5cbiAgICAvLyBCZWZvcmUgUFIgIzExMTA2IGxvZ0V2ZW50IGRpc3BhdGNoZWQgZGlyZWN0bHk7IGFmdGVyLCBldmVudHMgcXVldWUgdW50aWxcbiAgICAvLyBhIHNpbmsgYXR0YWNoZXMuIHNldHVwKCkgYXR0YWNoZXMgc2lua3MgZm9yIHRoZSBkZWZhdWx0IGNvbW1hbmQsIGJ1dFxuICAgIC8vIHN1YmNvbW1hbmRzIChkb2N0b3IsIG1jcCwgcGx1Z2luLCBhdXRoKSBuZXZlciBjYWxsIHNldHVwKCkgYW5kIHdvdWxkXG4gICAgLy8gc2lsZW50bHkgZHJvcCBldmVudHMgb24gcHJvY2Vzcy5leGl0KCkuIEJvdGggaW5pdHMgYXJlIGlkZW1wb3RlbnQuXG4gICAgY29uc3QgeyBpbml0U2lua3MgfSA9IGF3YWl0IGltcG9ydCgnLi91dGlscy9zaW5rcy5qcycpXG4gICAgaW5pdFNpbmtzKClcbiAgICBwcm9maWxlQ2hlY2twb2ludCgncHJlQWN0aW9uX2FmdGVyX3NpbmtzJylcblxuICAgIC8vIGdoLTMzNTA4OiAtLXBsdWdpbi1kaXIgaXMgYSB0b3AtbGV2ZWwgcHJvZ3JhbSBvcHRpb24uIFRoZSBkZWZhdWx0XG4gICAgLy8gYWN0aW9uIHJlYWRzIGl0IGZyb20gaXRzIG93biBvcHRpb25zIGRlc3RydWN0dXJlLCBidXQgc3ViY29tbWFuZHNcbiAgICAvLyAocGx1Z2luIGxpc3QsIHBsdWdpbiBpbnN0YWxsLCBtY3AgKikgaGF2ZSB0aGVpciBvd24gYWN0aW9ucyBhbmRcbiAgICAvLyBuZXZlciBzZWUgaXQuIFdpcmUgaXQgdXAgaGVyZSBzbyBnZXRJbmxpbmVQbHVnaW5zKCkgd29ya3MgZXZlcnl3aGVyZS5cbiAgICAvLyB0aGlzQ29tbWFuZC5vcHRzKCkgaXMgdHlwZWQge30gaGVyZSBiZWNhdXNlIHRoaXMgaG9vayBpcyBhdHRhY2hlZFxuICAgIC8vIGJlZm9yZSAub3B0aW9uKCctLXBsdWdpbi1kaXInLCAuLi4pIGluIHRoZSBjaGFpbiDigJQgZXh0cmEtdHlwaW5nc1xuICAgIC8vIGJ1aWxkcyB0aGUgdHlwZSBhcyBvcHRpb25zIGFyZSBhZGRlZC4gTmFycm93IHdpdGggYSBydW50aW1lIGd1YXJkO1xuICAgIC8vIHRoZSBjb2xsZWN0IGFjY3VtdWxhdG9yICsgW10gZGVmYXVsdCBndWFyYW50ZWUgc3RyaW5nW10gaW4gcHJhY3RpY2UuXG4gICAgY29uc3QgcGx1Z2luRGlyID0gdGhpc0NvbW1hbmQuZ2V0T3B0aW9uVmFsdWUoJ3BsdWdpbkRpcicpXG4gICAgaWYgKFxuICAgICAgQXJyYXkuaXNBcnJheShwbHVnaW5EaXIpICYmXG4gICAgICBwbHVnaW5EaXIubGVuZ3RoID4gMCAmJlxuICAgICAgcGx1Z2luRGlyLmV2ZXJ5KHAgPT4gdHlwZW9mIHAgPT09ICdzdHJpbmcnKVxuICAgICkge1xuICAgICAgc2V0SW5saW5lUGx1Z2lucyhwbHVnaW5EaXIpXG4gICAgICBjbGVhclBsdWdpbkNhY2hlKCdwcmVBY3Rpb246IC0tcGx1Z2luLWRpciBpbmxpbmUgcGx1Z2lucycpXG4gICAgfVxuXG4gICAgcnVuTWlncmF0aW9ucygpXG4gICAgcHJvZmlsZUNoZWNrcG9pbnQoJ3ByZUFjdGlvbl9hZnRlcl9taWdyYXRpb25zJylcblxuICAgIC8vIExvYWQgcmVtb3RlIG1hbmFnZWQgc2V0dGluZ3MgZm9yIGVudGVycHJpc2UgY3VzdG9tZXJzIChub24tYmxvY2tpbmcpXG4gICAgLy8gRmFpbHMgb3BlbiAtIGlmIGZldGNoIGZhaWxzLCBjb250aW51ZXMgd2l0aG91dCByZW1vdGUgc2V0dGluZ3NcbiAgICAvLyBTZXR0aW5ncyBhcmUgYXBwbGllZCB2aWEgaG90LXJlbG9hZCB3aGVuIHRoZXkgYXJyaXZlXG4gICAgLy8gTXVzdCBoYXBwZW4gYWZ0ZXIgaW5pdCgpIHRvIGVuc3VyZSBjb25maWcgcmVhZGluZyBpcyBhbGxvd2VkXG4gICAgdm9pZCBsb2FkUmVtb3RlTWFuYWdlZFNldHRpbmdzKClcbiAgICB2b2lkIGxvYWRQb2xpY3lMaW1pdHMoKVxuXG4gICAgcHJvZmlsZUNoZWNrcG9pbnQoJ3ByZUFjdGlvbl9hZnRlcl9yZW1vdGVfc2V0dGluZ3MnKVxuXG4gICAgLy8gTG9hZCBzZXR0aW5ncyBzeW5jIChub24tYmxvY2tpbmcsIGZhaWwtb3BlbilcbiAgICAvLyBDTEk6IHVwbG9hZHMgbG9jYWwgc2V0dGluZ3MgdG8gcmVtb3RlIChDQ1IgZG93bmxvYWQgaXMgaGFuZGxlZCBieSBwcmludC50cylcbiAgICBpZiAoZmVhdHVyZSgnVVBMT0FEX1VTRVJfU0VUVElOR1MnKSkge1xuICAgICAgdm9pZCBpbXBvcnQoJy4vc2VydmljZXMvc2V0dGluZ3NTeW5jL2luZGV4LmpzJykudGhlbihtID0+XG4gICAgICAgIG0udXBsb2FkVXNlclNldHRpbmdzSW5CYWNrZ3JvdW5kKCksXG4gICAgICApXG4gICAgfVxuXG4gICAgcHJvZmlsZUNoZWNrcG9pbnQoJ3ByZUFjdGlvbl9hZnRlcl9zZXR0aW5nc19zeW5jJylcbiAgfSlcblxuICBwcm9ncmFtXG4gICAgLm5hbWUoJ2NsYXVkZScpXG4gICAgLmRlc2NyaXB0aW9uKFxuICAgICAgYENsYXVkZSBDb2RlIC0gc3RhcnRzIGFuIGludGVyYWN0aXZlIHNlc3Npb24gYnkgZGVmYXVsdCwgdXNlIC1wLy0tcHJpbnQgZm9yIG5vbi1pbnRlcmFjdGl2ZSBvdXRwdXRgLFxuICAgIClcbiAgICAuYXJndW1lbnQoJ1twcm9tcHRdJywgJ1lvdXIgcHJvbXB0JywgU3RyaW5nKVxuICAgIC8vIFN1YmNvbW1hbmRzIGluaGVyaXQgaGVscE9wdGlvbiB2aWEgY29tbWFuZGVyJ3MgY29weUluaGVyaXRlZFNldHRpbmdzIOKAlFxuICAgIC8vIHNldHRpbmcgaXQgb25jZSBoZXJlIGNvdmVycyBtY3AsIHBsdWdpbiwgYXV0aCwgYW5kIGFsbCBvdGhlciBzdWJjb21tYW5kcy5cbiAgICAuaGVscE9wdGlvbignLWgsIC0taGVscCcsICdEaXNwbGF5IGhlbHAgZm9yIGNvbW1hbmQnKVxuICAgIC5vcHRpb24oXG4gICAgICAnLWQsIC0tZGVidWcgW2ZpbHRlcl0nLFxuICAgICAgJ0VuYWJsZSBkZWJ1ZyBtb2RlIHdpdGggb3B0aW9uYWwgY2F0ZWdvcnkgZmlsdGVyaW5nIChlLmcuLCBcImFwaSxob29rc1wiIG9yIFwiITFwLCFmaWxlXCIpJyxcbiAgICAgIChfdmFsdWU6IHN0cmluZyB8IHRydWUpID0+IHtcbiAgICAgICAgLy8gSWYgdmFsdWUgaXMgcHJvdmlkZWQsIGl0IHdpbGwgYmUgdGhlIGZpbHRlciBzdHJpbmdcbiAgICAgICAgLy8gSWYgbm90IHByb3ZpZGVkIGJ1dCBmbGFnIGlzIHByZXNlbnQsIHZhbHVlIHdpbGwgYmUgdHJ1ZVxuICAgICAgICAvLyBUaGUgYWN0dWFsIGZpbHRlcmluZyBpcyBoYW5kbGVkIGluIGRlYnVnLnRzIGJ5IHBhcnNpbmcgcHJvY2Vzcy5hcmd2XG4gICAgICAgIHJldHVybiB0cnVlXG4gICAgICB9LFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbignLWQyZSwgLS1kZWJ1Zy10by1zdGRlcnInLCAnRW5hYmxlIGRlYnVnIG1vZGUgKHRvIHN0ZGVyciknKVxuICAgICAgICAuYXJnUGFyc2VyKEJvb2xlYW4pXG4gICAgICAgIC5oaWRlSGVscCgpLFxuICAgIClcbiAgICAub3B0aW9uKFxuICAgICAgJy0tZGVidWctZmlsZSA8cGF0aD4nLFxuICAgICAgJ1dyaXRlIGRlYnVnIGxvZ3MgdG8gYSBzcGVjaWZpYyBmaWxlIHBhdGggKGltcGxpY2l0bHkgZW5hYmxlcyBkZWJ1ZyBtb2RlKScsXG4gICAgICAoKSA9PiB0cnVlLFxuICAgIClcbiAgICAub3B0aW9uKFxuICAgICAgJy0tdmVyYm9zZScsXG4gICAgICAnT3ZlcnJpZGUgdmVyYm9zZSBtb2RlIHNldHRpbmcgZnJvbSBjb25maWcnLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctcCwgLS1wcmludCcsXG4gICAgICAnUHJpbnQgcmVzcG9uc2UgYW5kIGV4aXQgKHVzZWZ1bCBmb3IgcGlwZXMpLiBOb3RlOiBUaGUgd29ya3NwYWNlIHRydXN0IGRpYWxvZyBpcyBza2lwcGVkIHdoZW4gQ2xhdWRlIGlzIHJ1biB3aXRoIHRoZSAtcCBtb2RlLiBPbmx5IHVzZSB0aGlzIGZsYWcgaW4gZGlyZWN0b3JpZXMgeW91IHRydXN0LicsXG4gICAgICAoKSA9PiB0cnVlLFxuICAgIClcbiAgICAub3B0aW9uKFxuICAgICAgJy0tYmFyZScsXG4gICAgICAnTWluaW1hbCBtb2RlOiBza2lwIGhvb2tzLCBMU1AsIHBsdWdpbiBzeW5jLCBhdHRyaWJ1dGlvbiwgYXV0by1tZW1vcnksIGJhY2tncm91bmQgcHJlZmV0Y2hlcywga2V5Y2hhaW4gcmVhZHMsIGFuZCBDTEFVREUubWQgYXV0by1kaXNjb3ZlcnkuIFNldHMgQ0xBVURFX0NPREVfU0lNUExFPTEuIEFudGhyb3BpYyBhdXRoIGlzIHN0cmljdGx5IEFOVEhST1BJQ19BUElfS0VZIG9yIGFwaUtleUhlbHBlciB2aWEgLS1zZXR0aW5ncyAoT0F1dGggYW5kIGtleWNoYWluIGFyZSBuZXZlciByZWFkKS4gM1AgcHJvdmlkZXJzIChCZWRyb2NrL1ZlcnRleC9Gb3VuZHJ5KSB1c2UgdGhlaXIgb3duIGNyZWRlbnRpYWxzLiBTa2lsbHMgc3RpbGwgcmVzb2x2ZSB2aWEgL3NraWxsLW5hbWUuIEV4cGxpY2l0bHkgcHJvdmlkZSBjb250ZXh0IHZpYTogLS1zeXN0ZW0tcHJvbXB0Wy1maWxlXSwgLS1hcHBlbmQtc3lzdGVtLXByb21wdFstZmlsZV0sIC0tYWRkLWRpciAoQ0xBVURFLm1kIGRpcnMpLCAtLW1jcC1jb25maWcsIC0tc2V0dGluZ3MsIC0tYWdlbnRzLCAtLXBsdWdpbi1kaXIuJyxcbiAgICAgICgpID0+IHRydWUsXG4gICAgKVxuICAgIC5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1pbml0JyxcbiAgICAgICAgJ1J1biBTZXR1cCBob29rcyB3aXRoIGluaXQgdHJpZ2dlciwgdGhlbiBjb250aW51ZScsXG4gICAgICApLmhpZGVIZWxwKCksXG4gICAgKVxuICAgIC5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1pbml0LW9ubHknLFxuICAgICAgICAnUnVuIFNldHVwIGFuZCBTZXNzaW9uU3RhcnQ6c3RhcnR1cCBob29rcywgdGhlbiBleGl0JyxcbiAgICAgICkuaGlkZUhlbHAoKSxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLW1haW50ZW5hbmNlJyxcbiAgICAgICAgJ1J1biBTZXR1cCBob29rcyB3aXRoIG1haW50ZW5hbmNlIHRyaWdnZXIsIHRoZW4gY29udGludWUnLFxuICAgICAgKS5oaWRlSGVscCgpLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tb3V0cHV0LWZvcm1hdCA8Zm9ybWF0PicsXG4gICAgICAgICdPdXRwdXQgZm9ybWF0IChvbmx5IHdvcmtzIHdpdGggLS1wcmludCk6IFwidGV4dFwiIChkZWZhdWx0KSwgXCJqc29uXCIgKHNpbmdsZSByZXN1bHQpLCBvciBcInN0cmVhbS1qc29uXCIgKHJlYWx0aW1lIHN0cmVhbWluZyknLFxuICAgICAgKS5jaG9pY2VzKFsndGV4dCcsICdqc29uJywgJ3N0cmVhbS1qc29uJ10pLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tanNvbi1zY2hlbWEgPHNjaGVtYT4nLFxuICAgICAgICAnSlNPTiBTY2hlbWEgZm9yIHN0cnVjdHVyZWQgb3V0cHV0IHZhbGlkYXRpb24uICcgK1xuICAgICAgICAgICdFeGFtcGxlOiB7XCJ0eXBlXCI6XCJvYmplY3RcIixcInByb3BlcnRpZXNcIjp7XCJuYW1lXCI6e1widHlwZVwiOlwic3RyaW5nXCJ9fSxcInJlcXVpcmVkXCI6W1wibmFtZVwiXX0nLFxuICAgICAgKS5hcmdQYXJzZXIoU3RyaW5nKSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWluY2x1ZGUtaG9vay1ldmVudHMnLFxuICAgICAgJ0luY2x1ZGUgYWxsIGhvb2sgbGlmZWN5Y2xlIGV2ZW50cyBpbiB0aGUgb3V0cHV0IHN0cmVhbSAob25seSB3b3JrcyB3aXRoIC0tb3V0cHV0LWZvcm1hdD1zdHJlYW0tanNvbiknLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWluY2x1ZGUtcGFydGlhbC1tZXNzYWdlcycsXG4gICAgICAnSW5jbHVkZSBwYXJ0aWFsIG1lc3NhZ2UgY2h1bmtzIGFzIHRoZXkgYXJyaXZlIChvbmx5IHdvcmtzIHdpdGggLS1wcmludCBhbmQgLS1vdXRwdXQtZm9ybWF0PXN0cmVhbS1qc29uKScsXG4gICAgICAoKSA9PiB0cnVlLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0taW5wdXQtZm9ybWF0IDxmb3JtYXQ+JyxcbiAgICAgICAgJ0lucHV0IGZvcm1hdCAob25seSB3b3JrcyB3aXRoIC0tcHJpbnQpOiBcInRleHRcIiAoZGVmYXVsdCksIG9yIFwic3RyZWFtLWpzb25cIiAocmVhbHRpbWUgc3RyZWFtaW5nIGlucHV0KScsXG4gICAgICApLmNob2ljZXMoWyd0ZXh0JywgJ3N0cmVhbS1qc29uJ10pLFxuICAgIClcbiAgICAub3B0aW9uKFxuICAgICAgJy0tbWNwLWRlYnVnJyxcbiAgICAgICdbREVQUkVDQVRFRC4gVXNlIC0tZGVidWcgaW5zdGVhZF0gRW5hYmxlIE1DUCBkZWJ1ZyBtb2RlIChzaG93cyBNQ1Agc2VydmVyIGVycm9ycyknLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWRhbmdlcm91c2x5LXNraXAtcGVybWlzc2lvbnMnLFxuICAgICAgJ0J5cGFzcyBhbGwgcGVybWlzc2lvbiBjaGVja3MuIFJlY29tbWVuZGVkIG9ubHkgZm9yIHNhbmRib3hlcyB3aXRoIG5vIGludGVybmV0IGFjY2Vzcy4nLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWFsbG93LWRhbmdlcm91c2x5LXNraXAtcGVybWlzc2lvbnMnLFxuICAgICAgJ0VuYWJsZSBieXBhc3NpbmcgYWxsIHBlcm1pc3Npb24gY2hlY2tzIGFzIGFuIG9wdGlvbiwgd2l0aG91dCBpdCBiZWluZyBlbmFibGVkIGJ5IGRlZmF1bHQuIFJlY29tbWVuZGVkIG9ubHkgZm9yIHNhbmRib3hlcyB3aXRoIG5vIGludGVybmV0IGFjY2Vzcy4nLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLXRoaW5raW5nIDxtb2RlPicsXG4gICAgICAgICdUaGlua2luZyBtb2RlOiBlbmFibGVkIChlcXVpdmFsZW50IHRvIGFkYXB0aXZlKSwgZGlzYWJsZWQnLFxuICAgICAgKVxuICAgICAgICAuY2hvaWNlcyhbJ2VuYWJsZWQnLCAnYWRhcHRpdmUnLCAnZGlzYWJsZWQnXSlcbiAgICAgICAgLmhpZGVIZWxwKCksXG4gICAgKVxuICAgIC5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1tYXgtdGhpbmtpbmctdG9rZW5zIDx0b2tlbnM+JyxcbiAgICAgICAgJ1tERVBSRUNBVEVELiBVc2UgLS10aGlua2luZyBpbnN0ZWFkIGZvciBuZXdlciBtb2RlbHNdIE1heGltdW0gbnVtYmVyIG9mIHRoaW5raW5nIHRva2VucyAob25seSB3b3JrcyB3aXRoIC0tcHJpbnQpJyxcbiAgICAgIClcbiAgICAgICAgLmFyZ1BhcnNlcihOdW1iZXIpXG4gICAgICAgIC5oaWRlSGVscCgpLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tbWF4LXR1cm5zIDx0dXJucz4nLFxuICAgICAgICAnTWF4aW11bSBudW1iZXIgb2YgYWdlbnRpYyB0dXJucyBpbiBub24taW50ZXJhY3RpdmUgbW9kZS4gVGhpcyB3aWxsIGVhcmx5IGV4aXQgdGhlIGNvbnZlcnNhdGlvbiBhZnRlciB0aGUgc3BlY2lmaWVkIG51bWJlciBvZiB0dXJucy4gKG9ubHkgd29ya3Mgd2l0aCAtLXByaW50KScsXG4gICAgICApXG4gICAgICAgIC5hcmdQYXJzZXIoTnVtYmVyKVxuICAgICAgICAuaGlkZUhlbHAoKSxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLW1heC1idWRnZXQtdXNkIDxhbW91bnQ+JyxcbiAgICAgICAgJ01heGltdW0gZG9sbGFyIGFtb3VudCB0byBzcGVuZCBvbiBBUEkgY2FsbHMgKG9ubHkgd29ya3Mgd2l0aCAtLXByaW50KScsXG4gICAgICApLmFyZ1BhcnNlcih2YWx1ZSA9PiB7XG4gICAgICAgIGNvbnN0IGFtb3VudCA9IE51bWJlcih2YWx1ZSlcbiAgICAgICAgaWYgKGlzTmFOKGFtb3VudCkgfHwgYW1vdW50IDw9IDApIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAnLS1tYXgtYnVkZ2V0LXVzZCBtdXN0IGJlIGEgcG9zaXRpdmUgbnVtYmVyIGdyZWF0ZXIgdGhhbiAwJyxcbiAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGFtb3VudFxuICAgICAgfSksXG4gICAgKVxuICAgIC5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS10YXNrLWJ1ZGdldCA8dG9rZW5zPicsXG4gICAgICAgICdBUEktc2lkZSB0YXNrIGJ1ZGdldCBpbiB0b2tlbnMgKG91dHB1dF9jb25maWcudGFza19idWRnZXQpJyxcbiAgICAgIClcbiAgICAgICAgLmFyZ1BhcnNlcih2YWx1ZSA9PiB7XG4gICAgICAgICAgY29uc3QgdG9rZW5zID0gTnVtYmVyKHZhbHVlKVxuICAgICAgICAgIGlmIChpc05hTih0b2tlbnMpIHx8IHRva2VucyA8PSAwIHx8ICFOdW1iZXIuaXNJbnRlZ2VyKHRva2VucykpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignLS10YXNrLWJ1ZGdldCBtdXN0IGJlIGEgcG9zaXRpdmUgaW50ZWdlcicpXG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiB0b2tlbnNcbiAgICAgICAgfSlcbiAgICAgICAgLmhpZGVIZWxwKCksXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1yZXBsYXktdXNlci1tZXNzYWdlcycsXG4gICAgICAnUmUtZW1pdCB1c2VyIG1lc3NhZ2VzIGZyb20gc3RkaW4gYmFjayBvbiBzdGRvdXQgZm9yIGFja25vd2xlZGdtZW50IChvbmx5IHdvcmtzIHdpdGggLS1pbnB1dC1mb3JtYXQ9c3RyZWFtLWpzb24gYW5kIC0tb3V0cHV0LWZvcm1hdD1zdHJlYW0tanNvbiknLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLWVuYWJsZS1hdXRoLXN0YXR1cycsXG4gICAgICAgICdFbmFibGUgYXV0aCBzdGF0dXMgbWVzc2FnZXMgaW4gU0RLIG1vZGUnLFxuICAgICAgKVxuICAgICAgICAuZGVmYXVsdChmYWxzZSlcbiAgICAgICAgLmhpZGVIZWxwKCksXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1hbGxvd2VkVG9vbHMsIC0tYWxsb3dlZC10b29scyA8dG9vbHMuLi4+JyxcbiAgICAgICdDb21tYSBvciBzcGFjZS1zZXBhcmF0ZWQgbGlzdCBvZiB0b29sIG5hbWVzIHRvIGFsbG93IChlLmcuIFwiQmFzaChnaXQ6KikgRWRpdFwiKScsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS10b29scyA8dG9vbHMuLi4+JyxcbiAgICAgICdTcGVjaWZ5IHRoZSBsaXN0IG9mIGF2YWlsYWJsZSB0b29scyBmcm9tIHRoZSBidWlsdC1pbiBzZXQuIFVzZSBcIlwiIHRvIGRpc2FibGUgYWxsIHRvb2xzLCBcImRlZmF1bHRcIiB0byB1c2UgYWxsIHRvb2xzLCBvciBzcGVjaWZ5IHRvb2wgbmFtZXMgKGUuZy4gXCJCYXNoLEVkaXQsUmVhZFwiKS4nLFxuICAgIClcbiAgICAub3B0aW9uKFxuICAgICAgJy0tZGlzYWxsb3dlZFRvb2xzLCAtLWRpc2FsbG93ZWQtdG9vbHMgPHRvb2xzLi4uPicsXG4gICAgICAnQ29tbWEgb3Igc3BhY2Utc2VwYXJhdGVkIGxpc3Qgb2YgdG9vbCBuYW1lcyB0byBkZW55IChlLmcuIFwiQmFzaChnaXQ6KikgRWRpdFwiKScsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1tY3AtY29uZmlnIDxjb25maWdzLi4uPicsXG4gICAgICAnTG9hZCBNQ1Agc2VydmVycyBmcm9tIEpTT04gZmlsZXMgb3Igc3RyaW5ncyAoc3BhY2Utc2VwYXJhdGVkKScsXG4gICAgKVxuICAgIC5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1wZXJtaXNzaW9uLXByb21wdC10b29sIDx0b29sPicsXG4gICAgICAgICdNQ1AgdG9vbCB0byB1c2UgZm9yIHBlcm1pc3Npb24gcHJvbXB0cyAob25seSB3b3JrcyB3aXRoIC0tcHJpbnQpJyxcbiAgICAgIClcbiAgICAgICAgLmFyZ1BhcnNlcihTdHJpbmcpXG4gICAgICAgIC5oaWRlSGVscCgpLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tc3lzdGVtLXByb21wdCA8cHJvbXB0PicsXG4gICAgICAgICdTeXN0ZW0gcHJvbXB0IHRvIHVzZSBmb3IgdGhlIHNlc3Npb24nLFxuICAgICAgKS5hcmdQYXJzZXIoU3RyaW5nKSxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLXN5c3RlbS1wcm9tcHQtZmlsZSA8ZmlsZT4nLFxuICAgICAgICAnUmVhZCBzeXN0ZW0gcHJvbXB0IGZyb20gYSBmaWxlJyxcbiAgICAgIClcbiAgICAgICAgLmFyZ1BhcnNlcihTdHJpbmcpXG4gICAgICAgIC5oaWRlSGVscCgpLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tYXBwZW5kLXN5c3RlbS1wcm9tcHQgPHByb21wdD4nLFxuICAgICAgICAnQXBwZW5kIGEgc3lzdGVtIHByb21wdCB0byB0aGUgZGVmYXVsdCBzeXN0ZW0gcHJvbXB0JyxcbiAgICAgICkuYXJnUGFyc2VyKFN0cmluZyksXG4gICAgKVxuICAgIC5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1hcHBlbmQtc3lzdGVtLXByb21wdC1maWxlIDxmaWxlPicsXG4gICAgICAgICdSZWFkIHN5c3RlbSBwcm9tcHQgZnJvbSBhIGZpbGUgYW5kIGFwcGVuZCB0byB0aGUgZGVmYXVsdCBzeXN0ZW0gcHJvbXB0JyxcbiAgICAgIClcbiAgICAgICAgLmFyZ1BhcnNlcihTdHJpbmcpXG4gICAgICAgIC5oaWRlSGVscCgpLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tcGVybWlzc2lvbi1tb2RlIDxtb2RlPicsXG4gICAgICAgICdQZXJtaXNzaW9uIG1vZGUgdG8gdXNlIGZvciB0aGUgc2Vzc2lvbicsXG4gICAgICApXG4gICAgICAgIC5hcmdQYXJzZXIoU3RyaW5nKVxuICAgICAgICAuY2hvaWNlcyhQRVJNSVNTSU9OX01PREVTKSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctYywgLS1jb250aW51ZScsXG4gICAgICAnQ29udGludWUgdGhlIG1vc3QgcmVjZW50IGNvbnZlcnNhdGlvbiBpbiB0aGUgY3VycmVudCBkaXJlY3RvcnknLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctciwgLS1yZXN1bWUgW3ZhbHVlXScsXG4gICAgICAnUmVzdW1lIGEgY29udmVyc2F0aW9uIGJ5IHNlc3Npb24gSUQsIG9yIG9wZW4gaW50ZXJhY3RpdmUgcGlja2VyIHdpdGggb3B0aW9uYWwgc2VhcmNoIHRlcm0nLFxuICAgICAgdmFsdWUgPT4gdmFsdWUgfHwgdHJ1ZSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWZvcmstc2Vzc2lvbicsXG4gICAgICAnV2hlbiByZXN1bWluZywgY3JlYXRlIGEgbmV3IHNlc3Npb24gSUQgaW5zdGVhZCBvZiByZXVzaW5nIHRoZSBvcmlnaW5hbCAodXNlIHdpdGggLS1yZXN1bWUgb3IgLS1jb250aW51ZSknLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLXByZWZpbGwgPHRleHQ+JyxcbiAgICAgICAgJ1ByZS1maWxsIHRoZSBwcm9tcHQgaW5wdXQgd2l0aCB0ZXh0IHdpdGhvdXQgc3VibWl0dGluZyBpdCcsXG4gICAgICApLmhpZGVIZWxwKCksXG4gICAgKVxuICAgIC5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1kZWVwLWxpbmstb3JpZ2luJyxcbiAgICAgICAgJ1NpZ25hbCB0aGF0IHRoaXMgc2Vzc2lvbiB3YXMgbGF1bmNoZWQgZnJvbSBhIGRlZXAgbGluaycsXG4gICAgICApLmhpZGVIZWxwKCksXG4gICAgKVxuICAgIC5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1kZWVwLWxpbmstcmVwbyA8c2x1Zz4nLFxuICAgICAgICAnUmVwbyBzbHVnIHRoZSBkZWVwIGxpbmsgP3JlcG89IHBhcmFtZXRlciByZXNvbHZlZCB0byB0aGUgY3VycmVudCBjd2QnLFxuICAgICAgKS5oaWRlSGVscCgpLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tZGVlcC1saW5rLWxhc3QtZmV0Y2ggPG1zPicsXG4gICAgICAgICdGRVRDSF9IRUFEIG10aW1lIGluIGVwb2NoIG1zLCBwcmVjb21wdXRlZCBieSB0aGUgZGVlcCBsaW5rIHRyYW1wb2xpbmUnLFxuICAgICAgKVxuICAgICAgICAuYXJnUGFyc2VyKHYgPT4ge1xuICAgICAgICAgIGNvbnN0IG4gPSBOdW1iZXIodilcbiAgICAgICAgICByZXR1cm4gTnVtYmVyLmlzRmluaXRlKG4pID8gbiA6IHVuZGVmaW5lZFxuICAgICAgICB9KVxuICAgICAgICAuaGlkZUhlbHAoKSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWZyb20tcHIgW3ZhbHVlXScsXG4gICAgICAnUmVzdW1lIGEgc2Vzc2lvbiBsaW5rZWQgdG8gYSBQUiBieSBQUiBudW1iZXIvVVJMLCBvciBvcGVuIGludGVyYWN0aXZlIHBpY2tlciB3aXRoIG9wdGlvbmFsIHNlYXJjaCB0ZXJtJyxcbiAgICAgIHZhbHVlID0+IHZhbHVlIHx8IHRydWUsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1uby1zZXNzaW9uLXBlcnNpc3RlbmNlJyxcbiAgICAgICdEaXNhYmxlIHNlc3Npb24gcGVyc2lzdGVuY2UgLSBzZXNzaW9ucyB3aWxsIG5vdCBiZSBzYXZlZCB0byBkaXNrIGFuZCBjYW5ub3QgYmUgcmVzdW1lZCAob25seSB3b3JrcyB3aXRoIC0tcHJpbnQpJyxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLXJlc3VtZS1zZXNzaW9uLWF0IDxtZXNzYWdlIGlkPicsXG4gICAgICAgICdXaGVuIHJlc3VtaW5nLCBvbmx5IG1lc3NhZ2VzIHVwIHRvIGFuZCBpbmNsdWRpbmcgdGhlIGFzc2lzdGFudCBtZXNzYWdlIHdpdGggPG1lc3NhZ2UuaWQ+ICh1c2Ugd2l0aCAtLXJlc3VtZSBpbiBwcmludCBtb2RlKScsXG4gICAgICApXG4gICAgICAgIC5hcmdQYXJzZXIoU3RyaW5nKVxuICAgICAgICAuaGlkZUhlbHAoKSxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLXJld2luZC1maWxlcyA8dXNlci1tZXNzYWdlLWlkPicsXG4gICAgICAgICdSZXN0b3JlIGZpbGVzIHRvIHN0YXRlIGF0IHRoZSBzcGVjaWZpZWQgdXNlciBtZXNzYWdlIGFuZCBleGl0IChyZXF1aXJlcyAtLXJlc3VtZSknLFxuICAgICAgKS5oaWRlSGVscCgpLFxuICAgIClcbiAgICAvLyBAW01PREVMIExBVU5DSF06IFVwZGF0ZSB0aGUgZXhhbXBsZSBtb2RlbCBJRCBpbiB0aGUgLS1tb2RlbCBoZWxwIHRleHQuXG4gICAgLm9wdGlvbihcbiAgICAgICctLW1vZGVsIDxtb2RlbD4nLFxuICAgICAgYE1vZGVsIGZvciB0aGUgY3VycmVudCBzZXNzaW9uLiBQcm92aWRlIGFuIGFsaWFzIGZvciB0aGUgbGF0ZXN0IG1vZGVsIChlLmcuICdzb25uZXQnIG9yICdvcHVzJykgb3IgYSBtb2RlbCdzIGZ1bGwgbmFtZSAoZS5nLiAnY2xhdWRlLXNvbm5ldC00LTYnKS5gLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tZWZmb3J0IDxsZXZlbD4nLFxuICAgICAgICBgRWZmb3J0IGxldmVsIGZvciB0aGUgY3VycmVudCBzZXNzaW9uIChsb3csIG1lZGl1bSwgaGlnaCwgbWF4KWAsXG4gICAgICApLmFyZ1BhcnNlcigocmF3VmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgICBjb25zdCB2YWx1ZSA9IHJhd1ZhbHVlLnRvTG93ZXJDYXNlKClcbiAgICAgICAgY29uc3QgYWxsb3dlZCA9IFsnbG93JywgJ21lZGl1bScsICdoaWdoJywgJ21heCddXG4gICAgICAgIGlmICghYWxsb3dlZC5pbmNsdWRlcyh2YWx1ZSkpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgSW52YWxpZEFyZ3VtZW50RXJyb3IoXG4gICAgICAgICAgICBgSXQgbXVzdCBiZSBvbmUgb2Y6ICR7YWxsb3dlZC5qb2luKCcsICcpfWAsXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB2YWx1ZVxuICAgICAgfSksXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1hZ2VudCA8YWdlbnQ+JyxcbiAgICAgIGBBZ2VudCBmb3IgdGhlIGN1cnJlbnQgc2Vzc2lvbi4gT3ZlcnJpZGVzIHRoZSAnYWdlbnQnIHNldHRpbmcuYCxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWJldGFzIDxiZXRhcy4uLj4nLFxuICAgICAgJ0JldGEgaGVhZGVycyB0byBpbmNsdWRlIGluIEFQSSByZXF1ZXN0cyAoQVBJIGtleSB1c2VycyBvbmx5KScsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1mYWxsYmFjay1tb2RlbCA8bW9kZWw+JyxcbiAgICAgICdFbmFibGUgYXV0b21hdGljIGZhbGxiYWNrIHRvIHNwZWNpZmllZCBtb2RlbCB3aGVuIGRlZmF1bHQgbW9kZWwgaXMgb3ZlcmxvYWRlZCAob25seSB3b3JrcyB3aXRoIC0tcHJpbnQpJyxcbiAgICApXG4gICAgLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLXdvcmtsb2FkIDx0YWc+JyxcbiAgICAgICAgJ1dvcmtsb2FkIHRhZyBmb3IgYmlsbGluZy1oZWFkZXIgYXR0cmlidXRpb24gKGNjX3dvcmtsb2FkKS4gUHJvY2Vzcy1zY29wZWQ7IHNldCBieSBTREsgZGFlbW9uIGNhbGxlcnMgdGhhdCBzcGF3biBzdWJwcm9jZXNzZXMgZm9yIGNyb24gd29yay4gKG9ubHkgd29ya3Mgd2l0aCAtLXByaW50KScsXG4gICAgICApLmhpZGVIZWxwKCksXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1zZXR0aW5ncyA8ZmlsZS1vci1qc29uPicsXG4gICAgICAnUGF0aCB0byBhIHNldHRpbmdzIEpTT04gZmlsZSBvciBhIEpTT04gc3RyaW5nIHRvIGxvYWQgYWRkaXRpb25hbCBzZXR0aW5ncyBmcm9tJyxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWFkZC1kaXIgPGRpcmVjdG9yaWVzLi4uPicsXG4gICAgICAnQWRkaXRpb25hbCBkaXJlY3RvcmllcyB0byBhbGxvdyB0b29sIGFjY2VzcyB0bycsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1pZGUnLFxuICAgICAgJ0F1dG9tYXRpY2FsbHkgY29ubmVjdCB0byBJREUgb24gc3RhcnR1cCBpZiBleGFjdGx5IG9uZSB2YWxpZCBJREUgaXMgYXZhaWxhYmxlJyxcbiAgICAgICgpID0+IHRydWUsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1zdHJpY3QtbWNwLWNvbmZpZycsXG4gICAgICAnT25seSB1c2UgTUNQIHNlcnZlcnMgZnJvbSAtLW1jcC1jb25maWcsIGlnbm9yaW5nIGFsbCBvdGhlciBNQ1AgY29uZmlndXJhdGlvbnMnLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLXNlc3Npb24taWQgPHV1aWQ+JyxcbiAgICAgICdVc2UgYSBzcGVjaWZpYyBzZXNzaW9uIElEIGZvciB0aGUgY29udmVyc2F0aW9uIChtdXN0IGJlIGEgdmFsaWQgVVVJRCknLFxuICAgIClcbiAgICAub3B0aW9uKFxuICAgICAgJy1uLCAtLW5hbWUgPG5hbWU+JyxcbiAgICAgICdTZXQgYSBkaXNwbGF5IG5hbWUgZm9yIHRoaXMgc2Vzc2lvbiAoc2hvd24gaW4gL3Jlc3VtZSBhbmQgdGVybWluYWwgdGl0bGUpJyxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLWFnZW50cyA8anNvbj4nLFxuICAgICAgJ0pTT04gb2JqZWN0IGRlZmluaW5nIGN1c3RvbSBhZ2VudHMgKGUuZy4gXFwne1wicmV2aWV3ZXJcIjoge1wiZGVzY3JpcHRpb25cIjogXCJSZXZpZXdzIGNvZGVcIiwgXCJwcm9tcHRcIjogXCJZb3UgYXJlIGEgY29kZSByZXZpZXdlclwifX1cXCcpJyxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctLXNldHRpbmctc291cmNlcyA8c291cmNlcz4nLFxuICAgICAgJ0NvbW1hLXNlcGFyYXRlZCBsaXN0IG9mIHNldHRpbmcgc291cmNlcyB0byBsb2FkICh1c2VyLCBwcm9qZWN0LCBsb2NhbCkuJyxcbiAgICApXG4gICAgLy8gZ2gtMzM1MDg6IDxwYXRocy4uLj4gKHZhcmlhZGljKSBjb25zdW1lZCBldmVyeXRoaW5nIHVudGlsIHRoZSBuZXh0XG4gICAgLy8gLS1mbGFnLiBgY2xhdWRlIC0tcGx1Z2luLWRpciAvcGF0aCBtY3AgYWRkIC0tdHJhbnNwb3J0IGh0dHBgIHN3YWxsb3dlZFxuICAgIC8vIGBtY3BgIGFuZCBgYWRkYCBhcyBwYXRocywgdGhlbiBjaG9rZWQgb24gLS10cmFuc3BvcnQgYXMgYW4gdW5rbm93blxuICAgIC8vIHRvcC1sZXZlbCBvcHRpb24uIFNpbmdsZS12YWx1ZSArIGNvbGxlY3QgYWNjdW11bGF0b3IgbWVhbnMgZWFjaFxuICAgIC8vIC0tcGx1Z2luLWRpciB0YWtlcyBleGFjdGx5IG9uZSBhcmc7IHJlcGVhdCB0aGUgZmxhZyBmb3IgbXVsdGlwbGUgZGlycy5cbiAgICAub3B0aW9uKFxuICAgICAgJy0tcGx1Z2luLWRpciA8cGF0aD4nLFxuICAgICAgJ0xvYWQgcGx1Z2lucyBmcm9tIGEgZGlyZWN0b3J5IGZvciB0aGlzIHNlc3Npb24gb25seSAocmVwZWF0YWJsZTogLS1wbHVnaW4tZGlyIEEgLS1wbHVnaW4tZGlyIEIpJyxcbiAgICAgICh2YWw6IHN0cmluZywgcHJldjogc3RyaW5nW10pID0+IFsuLi5wcmV2LCB2YWxdLFxuICAgICAgW10gYXMgc3RyaW5nW10sXG4gICAgKVxuICAgIC5vcHRpb24oJy0tZGlzYWJsZS1zbGFzaC1jb21tYW5kcycsICdEaXNhYmxlIGFsbCBza2lsbHMnLCAoKSA9PiB0cnVlKVxuICAgIC5vcHRpb24oJy0tY2hyb21lJywgJ0VuYWJsZSBDbGF1ZGUgaW4gQ2hyb21lIGludGVncmF0aW9uJylcbiAgICAub3B0aW9uKCctLW5vLWNocm9tZScsICdEaXNhYmxlIENsYXVkZSBpbiBDaHJvbWUgaW50ZWdyYXRpb24nKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1maWxlIDxzcGVjcy4uLj4nLFxuICAgICAgJ0ZpbGUgcmVzb3VyY2VzIHRvIGRvd25sb2FkIGF0IHN0YXJ0dXAuIEZvcm1hdDogZmlsZV9pZDpyZWxhdGl2ZV9wYXRoIChlLmcuLCAtLWZpbGUgZmlsZV9hYmM6ZG9jLnR4dCBmaWxlX2RlZjppbWcucG5nKScsXG4gICAgKVxuICAgIC5hY3Rpb24oYXN5bmMgKHByb21wdCwgb3B0aW9ucykgPT4ge1xuICAgICAgcHJvZmlsZUNoZWNrcG9pbnQoJ2FjdGlvbl9oYW5kbGVyX3N0YXJ0JylcblxuICAgICAgLy8gLS1iYXJlID0gb25lLXN3aXRjaCBtaW5pbWFsIG1vZGUuIFNldHMgU0lNUExFIHNvIGFsbCB0aGUgZXhpc3RpbmdcbiAgICAgIC8vIGdhdGVzIGZpcmUgKENMQVVERS5tZCwgc2tpbGxzLCBob29rcyBpbnNpZGUgZXhlY3V0ZUhvb2tzLCBhZ2VudFxuICAgICAgLy8gZGlyLXdhbGspLiBNdXN0IGJlIHNldCBiZWZvcmUgc2V0dXAoKSAvIGFueSBvZiB0aGUgZ2F0ZWQgd29yayBydW5zLlxuICAgICAgaWYgKChvcHRpb25zIGFzIHsgYmFyZT86IGJvb2xlYW4gfSkuYmFyZSkge1xuICAgICAgICBwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9TSU1QTEUgPSAnMSdcbiAgICAgIH1cblxuICAgICAgLy8gSWdub3JlIFwiY29kZVwiIGFzIGEgcHJvbXB0IC0gdHJlYXQgaXQgdGhlIHNhbWUgYXMgbm8gcHJvbXB0XG4gICAgICBpZiAocHJvbXB0ID09PSAnY29kZScpIHtcbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2NvZGVfcHJvbXB0X2lnbm9yZWQnLCB7fSlcbiAgICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgY2hhbGsueWVsbG93KCdUaXA6IFlvdSBjYW4gbGF1bmNoIENsYXVkZSBDb2RlIHdpdGgganVzdCBgY2xhdWRlYCcpLFxuICAgICAgICApXG4gICAgICAgIHByb21wdCA9IHVuZGVmaW5lZFxuICAgICAgfVxuXG4gICAgICAvLyBMb2cgZXZlbnQgZm9yIGFueSBzaW5nbGUtd29yZCBwcm9tcHRcbiAgICAgIGlmIChcbiAgICAgICAgcHJvbXB0ICYmXG4gICAgICAgIHR5cGVvZiBwcm9tcHQgPT09ICdzdHJpbmcnICYmXG4gICAgICAgICEvXFxzLy50ZXN0KHByb21wdCkgJiZcbiAgICAgICAgcHJvbXB0Lmxlbmd0aCA+IDBcbiAgICAgICkge1xuICAgICAgICBsb2dFdmVudCgndGVuZ3Vfc2luZ2xlX3dvcmRfcHJvbXB0JywgeyBsZW5ndGg6IHByb21wdC5sZW5ndGggfSlcbiAgICAgIH1cblxuICAgICAgLy8gQXNzaXN0YW50IG1vZGU6IHdoZW4gLmNsYXVkZS9zZXR0aW5ncy5qc29uIGhhcyBhc3Npc3RhbnQ6IHRydWUgQU5EXG4gICAgICAvLyB0aGUgdGVuZ3Vfa2Fpcm9zIEdyb3d0aEJvb2sgZ2F0ZSBpcyBvbiwgZm9yY2UgYnJpZWYgb24uIFBlcm1pc3Npb25cbiAgICAgIC8vIG1vZGUgaXMgbGVmdCB0byB0aGUgdXNlciDigJQgc2V0dGluZ3MgZGVmYXVsdE1vZGUgb3IgLS1wZXJtaXNzaW9uLW1vZGVcbiAgICAgIC8vIGFwcGx5IGFzIG5vcm1hbC4gUkVQTC10eXBlZCBtZXNzYWdlcyBhbHJlYWR5IGRlZmF1bHQgdG8gJ25leHQnXG4gICAgICAvLyBwcmlvcml0eSAobWVzc2FnZVF1ZXVlTWFuYWdlci5lbnF1ZXVlKSBzbyB0aGV5IGRyYWluIG1pZC10dXJuIGJldHdlZW5cbiAgICAgIC8vIHRvb2wgY2FsbHMuIFNlbmRVc2VyTWVzc2FnZSAoQnJpZWZUb29sKSBpcyBlbmFibGVkIHZpYSB0aGUgYnJpZWYgZW52XG4gICAgICAvLyB2YXIuIFNsZWVwVG9vbCBzdGF5cyBkaXNhYmxlZCAoaXRzIGlzRW5hYmxlZCgpIGdhdGVzIG9uIHByb2FjdGl2ZSkuXG4gICAgICAvLyBrYWlyb3NFbmFibGVkIGlzIGNvbXB1dGVkIG9uY2UgaGVyZSBhbmQgcmV1c2VkIGF0IHRoZVxuICAgICAgLy8gZ2V0QXNzaXN0YW50U3lzdGVtUHJvbXB0QWRkZW5kdW0oKSBjYWxsIHNpdGUgZnVydGhlciBkb3duLlxuICAgICAgLy9cbiAgICAgIC8vIFRydXN0IGdhdGU6IC5jbGF1ZGUvc2V0dGluZ3MuanNvbiBpcyBhdHRhY2tlci1jb250cm9sbGFibGUgaW4gYW5cbiAgICAgIC8vIHVudHJ1c3RlZCBjbG9uZS4gV2UgcnVuIH4xMDAwIGxpbmVzIGJlZm9yZSBzaG93U2V0dXBTY3JlZW5zKCkgc2hvd3NcbiAgICAgIC8vIHRoZSB0cnVzdCBkaWFsb2csIGFuZCBieSB0aGVuIHdlJ3ZlIGFscmVhZHkgYXBwZW5kZWRcbiAgICAgIC8vIC5jbGF1ZGUvYWdlbnRzL2Fzc2lzdGFudC5tZCB0byB0aGUgc3lzdGVtIHByb21wdC4gUmVmdXNlIHRvIGFjdGl2YXRlXG4gICAgICAvLyB1bnRpbCB0aGUgZGlyZWN0b3J5IGhhcyBiZWVuIGV4cGxpY2l0bHkgdHJ1c3RlZC5cbiAgICAgIGxldCBrYWlyb3NFbmFibGVkID0gZmFsc2VcbiAgICAgIGxldCBhc3Npc3RhbnRUZWFtQ29udGV4dDpcbiAgICAgICAgfCBBd2FpdGVkPFxuICAgICAgICAgICAgUmV0dXJuVHlwZTxcbiAgICAgICAgICAgICAgTm9uTnVsbGFibGU8dHlwZW9mIGFzc2lzdGFudE1vZHVsZT5bJ2luaXRpYWxpemVBc3Npc3RhbnRUZWFtJ11cbiAgICAgICAgICAgID5cbiAgICAgICAgICA+XG4gICAgICAgIHwgdW5kZWZpbmVkXG4gICAgICBpZiAoXG4gICAgICAgIGZlYXR1cmUoJ0tBSVJPUycpICYmXG4gICAgICAgIChvcHRpb25zIGFzIHsgYXNzaXN0YW50PzogYm9vbGVhbiB9KS5hc3Npc3RhbnQgJiZcbiAgICAgICAgYXNzaXN0YW50TW9kdWxlXG4gICAgICApIHtcbiAgICAgICAgLy8gLS1hc3Npc3RhbnQgKEFnZW50IFNESyBkYWVtb24gbW9kZSk6IGZvcmNlIHRoZSBsYXRjaCBiZWZvcmVcbiAgICAgICAgLy8gaXNBc3Npc3RhbnRNb2RlKCkgcnVucyBiZWxvdy4gVGhlIGRhZW1vbiBoYXMgYWxyZWFkeSBjaGVja2VkXG4gICAgICAgIC8vIGVudGl0bGVtZW50IOKAlCBkb24ndCBtYWtlIHRoZSBjaGlsZCByZS1jaGVjayB0ZW5ndV9rYWlyb3MuXG4gICAgICAgIGFzc2lzdGFudE1vZHVsZS5tYXJrQXNzaXN0YW50Rm9yY2VkKClcbiAgICAgIH1cbiAgICAgIGlmIChcbiAgICAgICAgZmVhdHVyZSgnS0FJUk9TJykgJiZcbiAgICAgICAgYXNzaXN0YW50TW9kdWxlPy5pc0Fzc2lzdGFudE1vZGUoKSAmJlxuICAgICAgICAvLyBTcGF3bmVkIHRlYW1tYXRlcyBzaGFyZSB0aGUgbGVhZGVyJ3MgY3dkICsgc2V0dGluZ3MuanNvbiwgc29cbiAgICAgICAgLy8gaXNBc3Npc3RhbnRNb2RlKCkgaXMgdHJ1ZSBmb3IgdGhlbSB0b28uIC0tYWdlbnQtaWQgYmVpbmcgc2V0XG4gICAgICAgIC8vIG1lYW5zIHdlIEFSRSBhIHNwYXduZWQgdGVhbW1hdGUgKGV4dHJhY3RUZWFtbWF0ZU9wdGlvbnMgcnVuc1xuICAgICAgICAvLyB+MTcwIGxpbmVzIGxhdGVyIHNvIGNoZWNrIHRoZSByYXcgY29tbWFuZGVyIG9wdGlvbikg4oCUIGRvbid0XG4gICAgICAgIC8vIHJlLWluaXQgdGhlIHRlYW0gb3Igb3ZlcnJpZGUgdGVhbW1hdGVNb2RlL3Byb2FjdGl2ZS9icmllZi5cbiAgICAgICAgIShvcHRpb25zIGFzIHsgYWdlbnRJZD86IHVua25vd24gfSkuYWdlbnRJZCAmJlxuICAgICAgICBrYWlyb3NHYXRlXG4gICAgICApIHtcbiAgICAgICAgaWYgKCFjaGVja0hhc1RydXN0RGlhbG9nQWNjZXB0ZWQoKSkge1xuICAgICAgICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db25zb2xlOjogaW50ZW50aW9uYWwgY29uc29sZSBvdXRwdXRcbiAgICAgICAgICBjb25zb2xlLndhcm4oXG4gICAgICAgICAgICBjaGFsay55ZWxsb3coXG4gICAgICAgICAgICAgICdBc3Npc3RhbnQgbW9kZSBkaXNhYmxlZDogZGlyZWN0b3J5IGlzIG5vdCB0cnVzdGVkLiBBY2NlcHQgdGhlIHRydXN0IGRpYWxvZyBhbmQgcmVzdGFydC4nLFxuICAgICAgICAgICAgKSxcbiAgICAgICAgICApXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gQmxvY2tpbmcgZ2F0ZSBjaGVjayDigJQgcmV0dXJucyBjYWNoZWQgYHRydWVgIGluc3RhbnRseTsgaWYgZGlza1xuICAgICAgICAgIC8vIGNhY2hlIGlzIGZhbHNlL21pc3NpbmcsIGxhemlseSBpbml0cyBHcm93dGhCb29rIGFuZCBmZXRjaGVzIGZyZXNoXG4gICAgICAgICAgLy8gKG1heCB+NXMpLiAtLWFzc2lzdGFudCBza2lwcyB0aGUgZ2F0ZSBlbnRpcmVseSAoZGFlbW9uIGlzXG4gICAgICAgICAgLy8gcHJlLWVudGl0bGVkKS5cbiAgICAgICAgICBrYWlyb3NFbmFibGVkID1cbiAgICAgICAgICAgIGFzc2lzdGFudE1vZHVsZS5pc0Fzc2lzdGFudEZvcmNlZCgpIHx8XG4gICAgICAgICAgICAoYXdhaXQga2Fpcm9zR2F0ZS5pc0thaXJvc0VuYWJsZWQoKSlcbiAgICAgICAgICBpZiAoa2Fpcm9zRW5hYmxlZCkge1xuICAgICAgICAgICAgY29uc3Qgb3B0cyA9IG9wdGlvbnMgYXMgeyBicmllZj86IGJvb2xlYW4gfVxuICAgICAgICAgICAgb3B0cy5icmllZiA9IHRydWVcbiAgICAgICAgICAgIHNldEthaXJvc0FjdGl2ZSh0cnVlKVxuICAgICAgICAgICAgLy8gUHJlLXNlZWQgYW4gaW4tcHJvY2VzcyB0ZWFtIHNvIEFnZW50KG5hbWU6IFwiZm9vXCIpIHNwYXduc1xuICAgICAgICAgICAgLy8gdGVhbW1hdGVzIHdpdGhvdXQgVGVhbUNyZWF0ZS4gTXVzdCBydW4gQkVGT1JFIHNldHVwKCkgY2FwdHVyZXNcbiAgICAgICAgICAgIC8vIHRoZSB0ZWFtbWF0ZU1vZGUgc25hcHNob3QgKGluaXRpYWxpemVBc3Npc3RhbnRUZWFtIGNhbGxzXG4gICAgICAgICAgICAvLyBzZXRDbGlUZWFtbWF0ZU1vZGVPdmVycmlkZSBpbnRlcm5hbGx5KS5cbiAgICAgICAgICAgIGFzc2lzdGFudFRlYW1Db250ZXh0ID1cbiAgICAgICAgICAgICAgYXdhaXQgYXNzaXN0YW50TW9kdWxlLmluaXRpYWxpemVBc3Npc3RhbnRUZWFtKClcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgY29uc3Qge1xuICAgICAgICBkZWJ1ZyA9IGZhbHNlLFxuICAgICAgICBkZWJ1Z1RvU3RkZXJyID0gZmFsc2UsXG4gICAgICAgIGRhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zLFxuICAgICAgICBhbGxvd0Rhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zID0gZmFsc2UsXG4gICAgICAgIHRvb2xzOiBiYXNlVG9vbHMgPSBbXSxcbiAgICAgICAgYWxsb3dlZFRvb2xzID0gW10sXG4gICAgICAgIGRpc2FsbG93ZWRUb29scyA9IFtdLFxuICAgICAgICBtY3BDb25maWcgPSBbXSxcbiAgICAgICAgcGVybWlzc2lvbk1vZGU6IHBlcm1pc3Npb25Nb2RlQ2xpLFxuICAgICAgICBhZGREaXIgPSBbXSxcbiAgICAgICAgZmFsbGJhY2tNb2RlbCxcbiAgICAgICAgYmV0YXMgPSBbXSxcbiAgICAgICAgaWRlID0gZmFsc2UsXG4gICAgICAgIHNlc3Npb25JZCxcbiAgICAgICAgaW5jbHVkZUhvb2tFdmVudHMsXG4gICAgICAgIGluY2x1ZGVQYXJ0aWFsTWVzc2FnZXMsXG4gICAgICB9ID0gb3B0aW9uc1xuXG4gICAgICBpZiAob3B0aW9ucy5wcmVmaWxsKSB7XG4gICAgICAgIHNlZWRFYXJseUlucHV0KG9wdGlvbnMucHJlZmlsbClcbiAgICAgIH1cblxuICAgICAgLy8gUHJvbWlzZSBmb3IgZmlsZSBkb3dubG9hZHMgLSBzdGFydGVkIGVhcmx5LCBhd2FpdGVkIGJlZm9yZSBSRVBMIHJlbmRlcnNcbiAgICAgIGxldCBmaWxlRG93bmxvYWRQcm9taXNlOiBQcm9taXNlPERvd25sb2FkUmVzdWx0W10+IHwgdW5kZWZpbmVkXG5cbiAgICAgIGNvbnN0IGFnZW50c0pzb24gPSBvcHRpb25zLmFnZW50c1xuICAgICAgY29uc3QgYWdlbnRDbGkgPSBvcHRpb25zLmFnZW50XG4gICAgICBpZiAoZmVhdHVyZSgnQkdfU0VTU0lPTlMnKSAmJiBhZ2VudENsaSkge1xuICAgICAgICBwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9BR0VOVCA9IGFnZW50Q2xpXG4gICAgICB9XG5cbiAgICAgIC8vIE5PVEU6IExTUCBtYW5hZ2VyIGluaXRpYWxpemF0aW9uIGlzIGludGVudGlvbmFsbHkgZGVmZXJyZWQgdW50aWwgYWZ0ZXJcbiAgICAgIC8vIHRoZSB0cnVzdCBkaWFsb2cgaXMgYWNjZXB0ZWQuIFRoaXMgcHJldmVudHMgcGx1Z2luIExTUCBzZXJ2ZXJzIGZyb21cbiAgICAgIC8vIGV4ZWN1dGluZyBjb2RlIGluIHVudHJ1c3RlZCBkaXJlY3RvcmllcyBiZWZvcmUgdXNlciBjb25zZW50LlxuXG4gICAgICAvLyBFeHRyYWN0IHRoZXNlIHNlcGFyYXRlbHkgc28gdGhleSBjYW4gYmUgbW9kaWZpZWQgaWYgbmVlZGVkXG4gICAgICBsZXQgb3V0cHV0Rm9ybWF0ID0gb3B0aW9ucy5vdXRwdXRGb3JtYXRcbiAgICAgIGxldCBpbnB1dEZvcm1hdCA9IG9wdGlvbnMuaW5wdXRGb3JtYXRcbiAgICAgIGxldCB2ZXJib3NlID0gb3B0aW9ucy52ZXJib3NlID8/IGdldEdsb2JhbENvbmZpZygpLnZlcmJvc2VcbiAgICAgIGxldCBwcmludCA9IG9wdGlvbnMucHJpbnRcbiAgICAgIGNvbnN0IGluaXQgPSBvcHRpb25zLmluaXQgPz8gZmFsc2VcbiAgICAgIGNvbnN0IGluaXRPbmx5ID0gb3B0aW9ucy5pbml0T25seSA/PyBmYWxzZVxuICAgICAgY29uc3QgbWFpbnRlbmFuY2UgPSBvcHRpb25zLm1haW50ZW5hbmNlID8/IGZhbHNlXG5cbiAgICAgIC8vIEV4dHJhY3QgZGlzYWJsZSBzbGFzaCBjb21tYW5kcyBmbGFnXG4gICAgICBjb25zdCBkaXNhYmxlU2xhc2hDb21tYW5kcyA9IG9wdGlvbnMuZGlzYWJsZVNsYXNoQ29tbWFuZHMgfHwgZmFsc2VcblxuICAgICAgLy8gRXh0cmFjdCB0YXNrcyBtb2RlIG9wdGlvbnMgKGFudC1vbmx5KVxuICAgICAgY29uc3QgdGFza3NPcHRpb24gPVxuICAgICAgICBcImV4dGVybmFsXCIgPT09ICdhbnQnICYmXG4gICAgICAgIChvcHRpb25zIGFzIHsgdGFza3M/OiBib29sZWFuIHwgc3RyaW5nIH0pLnRhc2tzXG4gICAgICBjb25zdCB0YXNrTGlzdElkID0gdGFza3NPcHRpb25cbiAgICAgICAgPyB0eXBlb2YgdGFza3NPcHRpb24gPT09ICdzdHJpbmcnXG4gICAgICAgICAgPyB0YXNrc09wdGlvblxuICAgICAgICAgIDogREVGQVVMVF9UQVNLU19NT0RFX1RBU0tfTElTVF9JRFxuICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgaWYgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiYgdGFza0xpc3RJZCkge1xuICAgICAgICBwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9UQVNLX0xJU1RfSUQgPSB0YXNrTGlzdElkXG4gICAgICB9XG5cbiAgICAgIC8vIEV4dHJhY3Qgd29ya3RyZWUgb3B0aW9uXG4gICAgICAvLyB3b3JrdHJlZSBjYW4gYmUgdHJ1ZSAoZmxhZyB3aXRob3V0IHZhbHVlKSBvciBhIHN0cmluZyAoY3VzdG9tIG5hbWUgb3IgUFIgcmVmZXJlbmNlKVxuICAgICAgY29uc3Qgd29ya3RyZWVPcHRpb24gPSBpc1dvcmt0cmVlTW9kZUVuYWJsZWQoKVxuICAgICAgICA/IChvcHRpb25zIGFzIHsgd29ya3RyZWU/OiBib29sZWFuIHwgc3RyaW5nIH0pLndvcmt0cmVlXG4gICAgICAgIDogdW5kZWZpbmVkXG4gICAgICBsZXQgd29ya3RyZWVOYW1lID1cbiAgICAgICAgdHlwZW9mIHdvcmt0cmVlT3B0aW9uID09PSAnc3RyaW5nJyA/IHdvcmt0cmVlT3B0aW9uIDogdW5kZWZpbmVkXG4gICAgICBjb25zdCB3b3JrdHJlZUVuYWJsZWQgPSB3b3JrdHJlZU9wdGlvbiAhPT0gdW5kZWZpbmVkXG5cbiAgICAgIC8vIENoZWNrIGlmIHdvcmt0cmVlIG5hbWUgaXMgYSBQUiByZWZlcmVuY2UgKCNOIG9yIEdpdEh1YiBQUiBVUkwpXG4gICAgICBsZXQgd29ya3RyZWVQUk51bWJlcjogbnVtYmVyIHwgdW5kZWZpbmVkXG4gICAgICBpZiAod29ya3RyZWVOYW1lKSB7XG4gICAgICAgIGNvbnN0IHByTnVtID0gcGFyc2VQUlJlZmVyZW5jZSh3b3JrdHJlZU5hbWUpXG4gICAgICAgIGlmIChwck51bSAhPT0gbnVsbCkge1xuICAgICAgICAgIHdvcmt0cmVlUFJOdW1iZXIgPSBwck51bVxuICAgICAgICAgIHdvcmt0cmVlTmFtZSA9IHVuZGVmaW5lZCAvLyBzbHVnIHdpbGwgYmUgZ2VuZXJhdGVkIGluIHNldHVwKClcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBFeHRyYWN0IHRtdXggb3B0aW9uIChyZXF1aXJlcyAtLXdvcmt0cmVlKVxuICAgICAgY29uc3QgdG11eEVuYWJsZWQgPVxuICAgICAgICBpc1dvcmt0cmVlTW9kZUVuYWJsZWQoKSAmJiAob3B0aW9ucyBhcyB7IHRtdXg/OiBib29sZWFuIH0pLnRtdXggPT09IHRydWVcblxuICAgICAgLy8gVmFsaWRhdGUgdG11eCBvcHRpb25cbiAgICAgIGlmICh0bXV4RW5hYmxlZCkge1xuICAgICAgICBpZiAoIXdvcmt0cmVlRW5hYmxlZCkge1xuICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKGNoYWxrLnJlZCgnRXJyb3I6IC0tdG11eCByZXF1aXJlcyAtLXdvcmt0cmVlXFxuJykpXG4gICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgIH1cbiAgICAgICAgaWYgKGdldFBsYXRmb3JtKCkgPT09ICd3aW5kb3dzJykge1xuICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgY2hhbGsucmVkKCdFcnJvcjogLS10bXV4IGlzIG5vdCBzdXBwb3J0ZWQgb24gV2luZG93c1xcbicpLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuICAgICAgICBpZiAoIShhd2FpdCBpc1RtdXhBdmFpbGFibGUoKSkpIHtcbiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgIGNoYWxrLnJlZChcbiAgICAgICAgICAgICAgYEVycm9yOiB0bXV4IGlzIG5vdCBpbnN0YWxsZWQuXFxuJHtnZXRUbXV4SW5zdGFsbEluc3RydWN0aW9ucygpfVxcbmAsXG4gICAgICAgICAgICApLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBFeHRyYWN0IHRlYW1tYXRlIG9wdGlvbnMgKGZvciB0bXV4LXNwYXduZWQgYWdlbnRzKVxuICAgICAgLy8gRGVjbGFyZWQgb3V0c2lkZSB0aGUgaWYgYmxvY2sgc28gaXQncyBhY2Nlc3NpYmxlIGxhdGVyIGZvciBzeXN0ZW0gcHJvbXB0IGFkZGVuZHVtXG4gICAgICBsZXQgc3RvcmVkVGVhbW1hdGVPcHRzOiBUZWFtbWF0ZU9wdGlvbnMgfCB1bmRlZmluZWRcbiAgICAgIGlmIChpc0FnZW50U3dhcm1zRW5hYmxlZCgpKSB7XG4gICAgICAgIC8vIEV4dHJhY3QgYWdlbnQgaWRlbnRpdHkgb3B0aW9ucyAoZm9yIHRtdXgtc3Bhd25lZCBhZ2VudHMpXG4gICAgICAgIC8vIFRoZXNlIHJlcGxhY2UgdGhlIENMQVVERV9DT0RFXyogZW52aXJvbm1lbnQgdmFyaWFibGVzXG4gICAgICAgIGNvbnN0IHRlYW1tYXRlT3B0cyA9IGV4dHJhY3RUZWFtbWF0ZU9wdGlvbnMob3B0aW9ucylcbiAgICAgICAgc3RvcmVkVGVhbW1hdGVPcHRzID0gdGVhbW1hdGVPcHRzXG5cbiAgICAgICAgLy8gSWYgYW55IHRlYW1tYXRlIGlkZW50aXR5IG9wdGlvbiBpcyBwcm92aWRlZCwgYWxsIHRocmVlIHJlcXVpcmVkIG9uZXMgbXVzdCBiZSBwcmVzZW50XG4gICAgICAgIGNvbnN0IGhhc0FueVRlYW1tYXRlT3B0ID1cbiAgICAgICAgICB0ZWFtbWF0ZU9wdHMuYWdlbnRJZCB8fFxuICAgICAgICAgIHRlYW1tYXRlT3B0cy5hZ2VudE5hbWUgfHxcbiAgICAgICAgICB0ZWFtbWF0ZU9wdHMudGVhbU5hbWVcbiAgICAgICAgY29uc3QgaGFzQWxsUmVxdWlyZWRUZWFtbWF0ZU9wdHMgPVxuICAgICAgICAgIHRlYW1tYXRlT3B0cy5hZ2VudElkICYmXG4gICAgICAgICAgdGVhbW1hdGVPcHRzLmFnZW50TmFtZSAmJlxuICAgICAgICAgIHRlYW1tYXRlT3B0cy50ZWFtTmFtZVxuXG4gICAgICAgIGlmIChoYXNBbnlUZWFtbWF0ZU9wdCAmJiAhaGFzQWxsUmVxdWlyZWRUZWFtbWF0ZU9wdHMpIHtcbiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgIGNoYWxrLnJlZChcbiAgICAgICAgICAgICAgJ0Vycm9yOiAtLWFnZW50LWlkLCAtLWFnZW50LW5hbWUsIGFuZCAtLXRlYW0tbmFtZSBtdXN0IGFsbCBiZSBwcm92aWRlZCB0b2dldGhlclxcbicsXG4gICAgICAgICAgICApLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIElmIHRlYW1tYXRlIGlkZW50aXR5IGlzIHByb3ZpZGVkIHZpYSBDTEksIHNldCB1cCBkeW5hbWljVGVhbUNvbnRleHRcbiAgICAgICAgaWYgKFxuICAgICAgICAgIHRlYW1tYXRlT3B0cy5hZ2VudElkICYmXG4gICAgICAgICAgdGVhbW1hdGVPcHRzLmFnZW50TmFtZSAmJlxuICAgICAgICAgIHRlYW1tYXRlT3B0cy50ZWFtTmFtZVxuICAgICAgICApIHtcbiAgICAgICAgICBnZXRUZWFtbWF0ZVV0aWxzKCkuc2V0RHluYW1pY1RlYW1Db250ZXh0Py4oe1xuICAgICAgICAgICAgYWdlbnRJZDogdGVhbW1hdGVPcHRzLmFnZW50SWQsXG4gICAgICAgICAgICBhZ2VudE5hbWU6IHRlYW1tYXRlT3B0cy5hZ2VudE5hbWUsXG4gICAgICAgICAgICB0ZWFtTmFtZTogdGVhbW1hdGVPcHRzLnRlYW1OYW1lLFxuICAgICAgICAgICAgY29sb3I6IHRlYW1tYXRlT3B0cy5hZ2VudENvbG9yLFxuICAgICAgICAgICAgcGxhbk1vZGVSZXF1aXJlZDogdGVhbW1hdGVPcHRzLnBsYW5Nb2RlUmVxdWlyZWQgPz8gZmFsc2UsXG4gICAgICAgICAgICBwYXJlbnRTZXNzaW9uSWQ6IHRlYW1tYXRlT3B0cy5wYXJlbnRTZXNzaW9uSWQsXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFNldCB0ZWFtbWF0ZSBtb2RlIENMSSBvdmVycmlkZSBpZiBwcm92aWRlZFxuICAgICAgICAvLyBUaGlzIG11c3QgYmUgZG9uZSBiZWZvcmUgc2V0dXAoKSBjYXB0dXJlcyB0aGUgc25hcHNob3RcbiAgICAgICAgaWYgKHRlYW1tYXRlT3B0cy50ZWFtbWF0ZU1vZGUpIHtcbiAgICAgICAgICBnZXRUZWFtbWF0ZU1vZGVTbmFwc2hvdCgpLnNldENsaVRlYW1tYXRlTW9kZU92ZXJyaWRlPy4oXG4gICAgICAgICAgICB0ZWFtbWF0ZU9wdHMudGVhbW1hdGVNb2RlLFxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBFeHRyYWN0IHJlbW90ZSBzZGsgb3B0aW9uc1xuICAgICAgY29uc3Qgc2RrVXJsID0gKG9wdGlvbnMgYXMgeyBzZGtVcmw/OiBzdHJpbmcgfSkuc2RrVXJsID8/IHVuZGVmaW5lZFxuXG4gICAgICAvLyBBbGxvdyBlbnYgdmFyIHRvIGVuYWJsZSBwYXJ0aWFsIG1lc3NhZ2VzICh1c2VkIGJ5IHNhbmRib3ggZ2F0ZXdheSBmb3IgYmFrdSlcbiAgICAgIGNvbnN0IGVmZmVjdGl2ZUluY2x1ZGVQYXJ0aWFsTWVzc2FnZXMgPVxuICAgICAgICBpbmNsdWRlUGFydGlhbE1lc3NhZ2VzIHx8XG4gICAgICAgIGlzRW52VHJ1dGh5KHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX0lOQ0xVREVfUEFSVElBTF9NRVNTQUdFUylcblxuICAgICAgLy8gRW5hYmxlIGFsbCBob29rIGV2ZW50IHR5cGVzIHdoZW4gZXhwbGljaXRseSByZXF1ZXN0ZWQgdmlhIFNESyBvcHRpb25cbiAgICAgIC8vIG9yIHdoZW4gcnVubmluZyBpbiBDTEFVREVfQ09ERV9SRU1PVEUgbW9kZSAoQ0NSIG5lZWRzIHRoZW0pLlxuICAgICAgLy8gV2l0aG91dCB0aGlzLCBvbmx5IFNlc3Npb25TdGFydCBhbmQgU2V0dXAgZXZlbnRzIGFyZSBlbWl0dGVkLlxuICAgICAgaWYgKGluY2x1ZGVIb29rRXZlbnRzIHx8IGlzRW52VHJ1dGh5KHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX1JFTU9URSkpIHtcbiAgICAgICAgc2V0QWxsSG9va0V2ZW50c0VuYWJsZWQodHJ1ZSlcbiAgICAgIH1cblxuICAgICAgLy8gQXV0by1zZXQgaW5wdXQvb3V0cHV0IGZvcm1hdHMsIHZlcmJvc2UgbW9kZSwgYW5kIHByaW50IG1vZGUgd2hlbiBTREsgVVJMIGlzIHByb3ZpZGVkXG4gICAgICBpZiAoc2RrVXJsKSB7XG4gICAgICAgIC8vIElmIFNESyBVUkwgaXMgcHJvdmlkZWQsIGF1dG9tYXRpY2FsbHkgdXNlIHN0cmVhbS1qc29uIGZvcm1hdHMgdW5sZXNzIGV4cGxpY2l0bHkgc2V0XG4gICAgICAgIGlmICghaW5wdXRGb3JtYXQpIHtcbiAgICAgICAgICBpbnB1dEZvcm1hdCA9ICdzdHJlYW0tanNvbidcbiAgICAgICAgfVxuICAgICAgICBpZiAoIW91dHB1dEZvcm1hdCkge1xuICAgICAgICAgIG91dHB1dEZvcm1hdCA9ICdzdHJlYW0tanNvbidcbiAgICAgICAgfVxuICAgICAgICAvLyBBdXRvLWVuYWJsZSB2ZXJib3NlIG1vZGUgdW5sZXNzIGV4cGxpY2l0bHkgZGlzYWJsZWQgb3IgYWxyZWFkeSBzZXRcbiAgICAgICAgaWYgKG9wdGlvbnMudmVyYm9zZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgdmVyYm9zZSA9IHRydWVcbiAgICAgICAgfVxuICAgICAgICAvLyBBdXRvLWVuYWJsZSBwcmludCBtb2RlIHVubGVzcyBleHBsaWNpdGx5IGRpc2FibGVkXG4gICAgICAgIGlmICghb3B0aW9ucy5wcmludCkge1xuICAgICAgICAgIHByaW50ID0gdHJ1ZVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEV4dHJhY3QgdGVsZXBvcnQgb3B0aW9uXG4gICAgICBjb25zdCB0ZWxlcG9ydCA9XG4gICAgICAgIChvcHRpb25zIGFzIHsgdGVsZXBvcnQ/OiBzdHJpbmcgfCB0cnVlIH0pLnRlbGVwb3J0ID8/IG51bGxcblxuICAgICAgLy8gRXh0cmFjdCByZW1vdGUgb3B0aW9uIChjYW4gYmUgdHJ1ZSBpZiBubyBkZXNjcmlwdGlvbiBwcm92aWRlZCwgb3IgYSBzdHJpbmcpXG4gICAgICBjb25zdCByZW1vdGVPcHRpb24gPSAob3B0aW9ucyBhcyB7IHJlbW90ZT86IHN0cmluZyB8IHRydWUgfSkucmVtb3RlXG4gICAgICBjb25zdCByZW1vdGUgPSByZW1vdGVPcHRpb24gPT09IHRydWUgPyAnJyA6IChyZW1vdGVPcHRpb24gPz8gbnVsbClcblxuICAgICAgLy8gRXh0cmFjdCAtLXJlbW90ZS1jb250cm9sIC8gLS1yYyBmbGFnIChlbmFibGUgYnJpZGdlIGluIGludGVyYWN0aXZlIHNlc3Npb24pXG4gICAgICBjb25zdCByZW1vdGVDb250cm9sT3B0aW9uID1cbiAgICAgICAgKG9wdGlvbnMgYXMgeyByZW1vdGVDb250cm9sPzogc3RyaW5nIHwgdHJ1ZSB9KS5yZW1vdGVDb250cm9sID8/XG4gICAgICAgIChvcHRpb25zIGFzIHsgcmM/OiBzdHJpbmcgfCB0cnVlIH0pLnJjXG4gICAgICAvLyBBY3R1YWwgYnJpZGdlIGNoZWNrIGlzIGRlZmVycmVkIHRvIGFmdGVyIHNob3dTZXR1cFNjcmVlbnMoKSBzbyB0aGF0XG4gICAgICAvLyB0cnVzdCBpcyBlc3RhYmxpc2hlZCBhbmQgR3Jvd3RoQm9vayBoYXMgYXV0aCBoZWFkZXJzLlxuICAgICAgbGV0IHJlbW90ZUNvbnRyb2wgPSBmYWxzZVxuICAgICAgY29uc3QgcmVtb3RlQ29udHJvbE5hbWUgPVxuICAgICAgICB0eXBlb2YgcmVtb3RlQ29udHJvbE9wdGlvbiA9PT0gJ3N0cmluZycgJiZcbiAgICAgICAgcmVtb3RlQ29udHJvbE9wdGlvbi5sZW5ndGggPiAwXG4gICAgICAgICAgPyByZW1vdGVDb250cm9sT3B0aW9uXG4gICAgICAgICAgOiB1bmRlZmluZWRcblxuICAgICAgLy8gVmFsaWRhdGUgc2Vzc2lvbiBJRCBpZiBwcm92aWRlZFxuICAgICAgaWYgKHNlc3Npb25JZCkge1xuICAgICAgICAvLyBDaGVjayBmb3IgY29uZmxpY3RpbmcgZmxhZ3NcbiAgICAgICAgLy8gLS1zZXNzaW9uLWlkIGNhbiBiZSB1c2VkIHdpdGggLS1jb250aW51ZSBvciAtLXJlc3VtZSB3aGVuIC0tZm9yay1zZXNzaW9uIGlzIGFsc28gcHJvdmlkZWRcbiAgICAgICAgLy8gKHRvIHNwZWNpZnkgYSBjdXN0b20gSUQgZm9yIHRoZSBmb3JrZWQgc2Vzc2lvbilcbiAgICAgICAgaWYgKChvcHRpb25zLmNvbnRpbnVlIHx8IG9wdGlvbnMucmVzdW1lKSAmJiAhb3B0aW9ucy5mb3JrU2Vzc2lvbikge1xuICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgY2hhbGsucmVkKFxuICAgICAgICAgICAgICAnRXJyb3I6IC0tc2Vzc2lvbi1pZCBjYW4gb25seSBiZSB1c2VkIHdpdGggLS1jb250aW51ZSBvciAtLXJlc3VtZSBpZiAtLWZvcmstc2Vzc2lvbiBpcyBhbHNvIHNwZWNpZmllZC5cXG4nLFxuICAgICAgICAgICAgKSxcbiAgICAgICAgICApXG4gICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgIH1cblxuICAgICAgICAvLyBXaGVuIC0tc2RrLXVybCBpcyBwcm92aWRlZCAoYnJpZGdlL3JlbW90ZSBtb2RlKSwgdGhlIHNlc3Npb24gSUQgaXMgYVxuICAgICAgICAvLyBzZXJ2ZXItYXNzaWduZWQgdGFnZ2VkIElEIChlLmcuIFwic2Vzc2lvbl9sb2NhbF8wMS4uLlwiKSByYXRoZXIgdGhhbiBhXG4gICAgICAgIC8vIFVVSUQuIFNraXAgVVVJRCB2YWxpZGF0aW9uIGFuZCBsb2NhbCBleGlzdGVuY2UgY2hlY2tzIGluIHRoYXQgY2FzZS5cbiAgICAgICAgaWYgKCFzZGtVcmwpIHtcbiAgICAgICAgICBjb25zdCB2YWxpZGF0ZWRTZXNzaW9uSWQgPSB2YWxpZGF0ZVV1aWQoc2Vzc2lvbklkKVxuICAgICAgICAgIGlmICghdmFsaWRhdGVkU2Vzc2lvbklkKSB7XG4gICAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgICAgY2hhbGsucmVkKCdFcnJvcjogSW52YWxpZCBzZXNzaW9uIElELiBNdXN0IGJlIGEgdmFsaWQgVVVJRC5cXG4nKSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIENoZWNrIGlmIHNlc3Npb24gSUQgYWxyZWFkeSBleGlzdHNcbiAgICAgICAgICBpZiAoc2Vzc2lvbklkRXhpc3RzKHZhbGlkYXRlZFNlc3Npb25JZCkpIHtcbiAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgICBjaGFsay5yZWQoXG4gICAgICAgICAgICAgICAgYEVycm9yOiBTZXNzaW9uIElEICR7dmFsaWRhdGVkU2Vzc2lvbklkfSBpcyBhbHJlYWR5IGluIHVzZS5cXG5gLFxuICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIERvd25sb2FkIGZpbGUgcmVzb3VyY2VzIGlmIHNwZWNpZmllZCB2aWEgLS1maWxlIGZsYWdcbiAgICAgIGNvbnN0IGZpbGVTcGVjcyA9IChvcHRpb25zIGFzIHsgZmlsZT86IHN0cmluZ1tdIH0pLmZpbGVcbiAgICAgIGlmIChmaWxlU3BlY3MgJiYgZmlsZVNwZWNzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgLy8gR2V0IHNlc3Npb24gaW5ncmVzcyB0b2tlbiAocHJvdmlkZWQgYnkgRW52TWFuYWdlciB2aWEgQ0xBVURFX0NPREVfU0VTU0lPTl9BQ0NFU1NfVE9LRU4pXG4gICAgICAgIGNvbnN0IHNlc3Npb25Ub2tlbiA9IGdldFNlc3Npb25JbmdyZXNzQXV0aFRva2VuKClcbiAgICAgICAgaWYgKCFzZXNzaW9uVG9rZW4pIHtcbiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgIGNoYWxrLnJlZChcbiAgICAgICAgICAgICAgJ0Vycm9yOiBTZXNzaW9uIHRva2VuIHJlcXVpcmVkIGZvciBmaWxlIGRvd25sb2Fkcy4gQ0xBVURFX0NPREVfU0VTU0lPTl9BQ0NFU1NfVE9LRU4gbXVzdCBiZSBzZXQuXFxuJyxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKVxuICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gUmVzb2x2ZSBzZXNzaW9uIElEOiBwcmVmZXIgcmVtb3RlIHNlc3Npb24gSUQsIGZhbGwgYmFjayB0byBpbnRlcm5hbCBzZXNzaW9uIElEXG4gICAgICAgIGNvbnN0IGZpbGVTZXNzaW9uSWQgPVxuICAgICAgICAgIHByb2Nlc3MuZW52LkNMQVVERV9DT0RFX1JFTU9URV9TRVNTSU9OX0lEIHx8IGdldFNlc3Npb25JZCgpXG5cbiAgICAgICAgY29uc3QgZmlsZXMgPSBwYXJzZUZpbGVTcGVjcyhmaWxlU3BlY3MpXG4gICAgICAgIGlmIChmaWxlcy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgLy8gVXNlIEFOVEhST1BJQ19CQVNFX1VSTCBpZiBzZXQgKGJ5IEVudk1hbmFnZXIpLCBvdGhlcndpc2UgdXNlIE9BdXRoIGNvbmZpZ1xuICAgICAgICAgIC8vIFRoaXMgZW5zdXJlcyBjb25zaXN0ZW5jeSB3aXRoIHNlc3Npb24gaW5ncmVzcyBBUEkgaW4gYWxsIGVudmlyb25tZW50c1xuICAgICAgICAgIGNvbnN0IGNvbmZpZzogRmlsZXNBcGlDb25maWcgPSB7XG4gICAgICAgICAgICBiYXNlVXJsOlxuICAgICAgICAgICAgICBwcm9jZXNzLmVudi5BTlRIUk9QSUNfQkFTRV9VUkwgfHwgZ2V0T2F1dGhDb25maWcoKS5CQVNFX0FQSV9VUkwsXG4gICAgICAgICAgICBvYXV0aFRva2VuOiBzZXNzaW9uVG9rZW4sXG4gICAgICAgICAgICBzZXNzaW9uSWQ6IGZpbGVTZXNzaW9uSWQsXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gU3RhcnQgZG93bmxvYWQgd2l0aG91dCBibG9ja2luZyBzdGFydHVwIC0gYXdhaXQgYmVmb3JlIFJFUEwgcmVuZGVyc1xuICAgICAgICAgIGZpbGVEb3dubG9hZFByb21pc2UgPSBkb3dubG9hZFNlc3Npb25GaWxlcyhmaWxlcywgY29uZmlnKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEdldCBpc05vbkludGVyYWN0aXZlU2Vzc2lvbiBmcm9tIHN0YXRlICh3YXMgc2V0IGJlZm9yZSBpbml0KCkpXG4gICAgICBjb25zdCBpc05vbkludGVyYWN0aXZlU2Vzc2lvbiA9IGdldElzTm9uSW50ZXJhY3RpdmVTZXNzaW9uKClcblxuICAgICAgLy8gVmFsaWRhdGUgdGhhdCBmYWxsYmFjayBtb2RlbCBpcyBkaWZmZXJlbnQgZnJvbSBtYWluIG1vZGVsXG4gICAgICBpZiAoZmFsbGJhY2tNb2RlbCAmJiBvcHRpb25zLm1vZGVsICYmIGZhbGxiYWNrTW9kZWwgPT09IG9wdGlvbnMubW9kZWwpIHtcbiAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgICAgY2hhbGsucmVkKFxuICAgICAgICAgICAgJ0Vycm9yOiBGYWxsYmFjayBtb2RlbCBjYW5ub3QgYmUgdGhlIHNhbWUgYXMgdGhlIG1haW4gbW9kZWwuIFBsZWFzZSBzcGVjaWZ5IGEgZGlmZmVyZW50IG1vZGVsIGZvciAtLWZhbGxiYWNrLW1vZGVsLlxcbicsXG4gICAgICAgICAgKSxcbiAgICAgICAgKVxuICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgIH1cblxuICAgICAgLy8gSGFuZGxlIHN5c3RlbSBwcm9tcHQgb3B0aW9uc1xuICAgICAgbGV0IHN5c3RlbVByb21wdCA9IG9wdGlvbnMuc3lzdGVtUHJvbXB0XG4gICAgICBpZiAob3B0aW9ucy5zeXN0ZW1Qcm9tcHRGaWxlKSB7XG4gICAgICAgIGlmIChvcHRpb25zLnN5c3RlbVByb21wdCkge1xuICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgY2hhbGsucmVkKFxuICAgICAgICAgICAgICAnRXJyb3I6IENhbm5vdCB1c2UgYm90aCAtLXN5c3RlbS1wcm9tcHQgYW5kIC0tc3lzdGVtLXByb21wdC1maWxlLiBQbGVhc2UgdXNlIG9ubHkgb25lLlxcbicsXG4gICAgICAgICAgICApLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgZmlsZVBhdGggPSByZXNvbHZlKG9wdGlvbnMuc3lzdGVtUHJvbXB0RmlsZSlcbiAgICAgICAgICBzeXN0ZW1Qcm9tcHQgPSByZWFkRmlsZVN5bmMoZmlsZVBhdGgsICd1dGY4JylcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBjb25zdCBjb2RlID0gZ2V0RXJybm9Db2RlKGVycm9yKVxuICAgICAgICAgIGlmIChjb2RlID09PSAnRU5PRU5UJykge1xuICAgICAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgICAgICAgIGNoYWxrLnJlZChcbiAgICAgICAgICAgICAgICBgRXJyb3I6IFN5c3RlbSBwcm9tcHQgZmlsZSBub3QgZm91bmQ6ICR7cmVzb2x2ZShvcHRpb25zLnN5c3RlbVByb21wdEZpbGUpfVxcbmAsXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgICB9XG4gICAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgICAgICBjaGFsay5yZWQoXG4gICAgICAgICAgICAgIGBFcnJvciByZWFkaW5nIHN5c3RlbSBwcm9tcHQgZmlsZTogJHtlcnJvck1lc3NhZ2UoZXJyb3IpfVxcbmAsXG4gICAgICAgICAgICApLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBIYW5kbGUgYXBwZW5kIHN5c3RlbSBwcm9tcHQgb3B0aW9uc1xuICAgICAgbGV0IGFwcGVuZFN5c3RlbVByb21wdCA9IG9wdGlvbnMuYXBwZW5kU3lzdGVtUHJvbXB0XG4gICAgICBpZiAob3B0aW9ucy5hcHBlbmRTeXN0ZW1Qcm9tcHRGaWxlKSB7XG4gICAgICAgIGlmIChvcHRpb25zLmFwcGVuZFN5c3RlbVByb21wdCkge1xuICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgY2hhbGsucmVkKFxuICAgICAgICAgICAgICAnRXJyb3I6IENhbm5vdCB1c2UgYm90aCAtLWFwcGVuZC1zeXN0ZW0tcHJvbXB0IGFuZCAtLWFwcGVuZC1zeXN0ZW0tcHJvbXB0LWZpbGUuIFBsZWFzZSB1c2Ugb25seSBvbmUuXFxuJyxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKVxuICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKVxuICAgICAgICB9XG5cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBmaWxlUGF0aCA9IHJlc29sdmUob3B0aW9ucy5hcHBlbmRTeXN0ZW1Qcm9tcHRGaWxlKVxuICAgICAgICAgIGFwcGVuZFN5c3RlbVByb21wdCA9IHJlYWRGaWxlU3luYyhmaWxlUGF0aCwgJ3V0ZjgnKVxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGNvbnN0IGNvZGUgPSBnZXRFcnJub0NvZGUoZXJyb3IpXG4gICAgICAgICAgaWYgKGNvZGUgPT09ICdFTk9FTlQnKSB7XG4gICAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgICAgY2hhbGsucmVkKFxuICAgICAgICAgICAgICAgIGBFcnJvcjogQXBwZW5kIHN5c3RlbSBwcm9tcHQgZmlsZSBub3QgZm91bmQ6ICR7cmVzb2x2ZShvcHRpb25zLmFwcGVuZFN5c3RlbVByb21wdEZpbGUpfVxcbmAsXG4gICAgICAgICAgICAgICksXG4gICAgICAgICAgICApXG4gICAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgICB9XG4gICAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgICAgICBjaGFsay5yZWQoXG4gICAgICAgICAgICAgIGBFcnJvciByZWFkaW5nIGFwcGVuZCBzeXN0ZW0gcHJvbXB0IGZpbGU6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1cXG5gLFxuICAgICAgICAgICAgKSxcbiAgICAgICAgICApXG4gICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gQWRkIHRlYW1tYXRlLXNwZWNpZmljIHN5c3RlbSBwcm9tcHQgYWRkZW5kdW0gZm9yIHRtdXggdGVhbW1hdGVzXG4gICAgICBpZiAoXG4gICAgICAgIGlzQWdlbnRTd2FybXNFbmFibGVkKCkgJiZcbiAgICAgICAgc3RvcmVkVGVhbW1hdGVPcHRzPy5hZ2VudElkICYmXG4gICAgICAgIHN0b3JlZFRlYW1tYXRlT3B0cz8uYWdlbnROYW1lICYmXG4gICAgICAgIHN0b3JlZFRlYW1tYXRlT3B0cz8udGVhbU5hbWVcbiAgICAgICkge1xuICAgICAgICBjb25zdCBhZGRlbmR1bSA9XG4gICAgICAgICAgZ2V0VGVhbW1hdGVQcm9tcHRBZGRlbmR1bSgpLlRFQU1NQVRFX1NZU1RFTV9QUk9NUFRfQURERU5EVU1cbiAgICAgICAgYXBwZW5kU3lzdGVtUHJvbXB0ID0gYXBwZW5kU3lzdGVtUHJvbXB0XG4gICAgICAgICAgPyBgJHthcHBlbmRTeXN0ZW1Qcm9tcHR9XFxuXFxuJHthZGRlbmR1bX1gXG4gICAgICAgICAgOiBhZGRlbmR1bVxuICAgICAgfVxuXG4gICAgICBjb25zdCB7IG1vZGU6IHBlcm1pc3Npb25Nb2RlLCBub3RpZmljYXRpb246IHBlcm1pc3Npb25Nb2RlTm90aWZpY2F0aW9uIH0gPVxuICAgICAgICBpbml0aWFsUGVybWlzc2lvbk1vZGVGcm9tQ0xJKHtcbiAgICAgICAgICBwZXJtaXNzaW9uTW9kZUNsaSxcbiAgICAgICAgICBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9ucyxcbiAgICAgICAgfSlcblxuICAgICAgLy8gU3RvcmUgc2Vzc2lvbiBieXBhc3MgcGVybWlzc2lvbnMgbW9kZSBmb3IgdHJ1c3QgZGlhbG9nIGNoZWNrXG4gICAgICBzZXRTZXNzaW9uQnlwYXNzUGVybWlzc2lvbnNNb2RlKHBlcm1pc3Npb25Nb2RlID09PSAnYnlwYXNzUGVybWlzc2lvbnMnKVxuICAgICAgaWYgKGZlYXR1cmUoJ1RSQU5TQ1JJUFRfQ0xBU1NJRklFUicpKSB7XG4gICAgICAgIC8vIGF1dG9Nb2RlRmxhZ0NsaSBpcyB0aGUgXCJkaWQgdGhlIHVzZXIgaW50ZW5kIGF1dG8gdGhpcyBzZXNzaW9uXCIgc2lnbmFsLlxuICAgICAgICAvLyBTZXQgd2hlbjogLS1lbmFibGUtYXV0by1tb2RlLCAtLXBlcm1pc3Npb24tbW9kZSBhdXRvLCByZXNvbHZlZCBtb2RlXG4gICAgICAgIC8vIGlzIGF1dG8sIE9SIHNldHRpbmdzIGRlZmF1bHRNb2RlIGlzIGF1dG8gYnV0IHRoZSBnYXRlIGRlbmllZCBpdFxuICAgICAgICAvLyAocGVybWlzc2lvbk1vZGUgcmVzb2x2ZWQgdG8gZGVmYXVsdCB3aXRoIG5vIGV4cGxpY2l0IENMSSBvdmVycmlkZSkuXG4gICAgICAgIC8vIFVzZWQgYnkgdmVyaWZ5QXV0b01vZGVHYXRlQWNjZXNzIHRvIGRlY2lkZSB3aGV0aGVyIHRvIG5vdGlmeSBvblxuICAgICAgICAvLyBhdXRvLXVuYXZhaWxhYmxlLCBhbmQgYnkgdGVuZ3VfYXV0b19tb2RlX2NvbmZpZyBvcHQtaW4gY2Fyb3VzZWwuXG4gICAgICAgIGlmIChcbiAgICAgICAgICAob3B0aW9ucyBhcyB7IGVuYWJsZUF1dG9Nb2RlPzogYm9vbGVhbiB9KS5lbmFibGVBdXRvTW9kZSB8fFxuICAgICAgICAgIHBlcm1pc3Npb25Nb2RlQ2xpID09PSAnYXV0bycgfHxcbiAgICAgICAgICBwZXJtaXNzaW9uTW9kZSA9PT0gJ2F1dG8nIHx8XG4gICAgICAgICAgKCFwZXJtaXNzaW9uTW9kZUNsaSAmJiBpc0RlZmF1bHRQZXJtaXNzaW9uTW9kZUF1dG8oKSlcbiAgICAgICAgKSB7XG4gICAgICAgICAgYXV0b01vZGVTdGF0ZU1vZHVsZT8uc2V0QXV0b01vZGVGbGFnQ2xpKHRydWUpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gUGFyc2UgdGhlIE1DUCBjb25maWcgZmlsZXMvc3RyaW5ncyBpZiBwcm92aWRlZFxuICAgICAgbGV0IGR5bmFtaWNNY3BDb25maWc6IFJlY29yZDxzdHJpbmcsIFNjb3BlZE1jcFNlcnZlckNvbmZpZz4gPSB7fVxuXG4gICAgICBpZiAobWNwQ29uZmlnICYmIG1jcENvbmZpZy5sZW5ndGggPiAwKSB7XG4gICAgICAgIC8vIFByb2Nlc3MgbWNwQ29uZmlnIGFycmF5XG4gICAgICAgIGNvbnN0IHByb2Nlc3NlZENvbmZpZ3MgPSBtY3BDb25maWdcbiAgICAgICAgICAubWFwKGNvbmZpZyA9PiBjb25maWcudHJpbSgpKVxuICAgICAgICAgIC5maWx0ZXIoY29uZmlnID0+IGNvbmZpZy5sZW5ndGggPiAwKVxuXG4gICAgICAgIGxldCBhbGxDb25maWdzOiBSZWNvcmQ8c3RyaW5nLCBNY3BTZXJ2ZXJDb25maWc+ID0ge31cbiAgICAgICAgY29uc3QgYWxsRXJyb3JzOiBWYWxpZGF0aW9uRXJyb3JbXSA9IFtdXG5cbiAgICAgICAgZm9yIChjb25zdCBjb25maWdJdGVtIG9mIHByb2Nlc3NlZENvbmZpZ3MpIHtcbiAgICAgICAgICBsZXQgY29uZmlnczogUmVjb3JkPHN0cmluZywgTWNwU2VydmVyQ29uZmlnPiB8IG51bGwgPSBudWxsXG4gICAgICAgICAgbGV0IGVycm9yczogVmFsaWRhdGlvbkVycm9yW10gPSBbXVxuXG4gICAgICAgICAgLy8gRmlyc3QgdHJ5IHRvIHBhcnNlIGFzIEpTT04gc3RyaW5nXG4gICAgICAgICAgY29uc3QgcGFyc2VkSnNvbiA9IHNhZmVQYXJzZUpTT04oY29uZmlnSXRlbSlcbiAgICAgICAgICBpZiAocGFyc2VkSnNvbikge1xuICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gcGFyc2VNY3BDb25maWcoe1xuICAgICAgICAgICAgICBjb25maWdPYmplY3Q6IHBhcnNlZEpzb24sXG4gICAgICAgICAgICAgIGZpbGVQYXRoOiAnY29tbWFuZCBsaW5lJyxcbiAgICAgICAgICAgICAgZXhwYW5kVmFyczogdHJ1ZSxcbiAgICAgICAgICAgICAgc2NvcGU6ICdkeW5hbWljJyxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICBpZiAocmVzdWx0LmNvbmZpZykge1xuICAgICAgICAgICAgICBjb25maWdzID0gcmVzdWx0LmNvbmZpZy5tY3BTZXJ2ZXJzXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBlcnJvcnMgPSByZXN1bHQuZXJyb3JzXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIFRyeSBhcyBmaWxlIHBhdGhcbiAgICAgICAgICAgIGNvbnN0IGNvbmZpZ1BhdGggPSByZXNvbHZlKGNvbmZpZ0l0ZW0pXG4gICAgICAgICAgICBjb25zdCByZXN1bHQgPSBwYXJzZU1jcENvbmZpZ0Zyb21GaWxlUGF0aCh7XG4gICAgICAgICAgICAgIGZpbGVQYXRoOiBjb25maWdQYXRoLFxuICAgICAgICAgICAgICBleHBhbmRWYXJzOiB0cnVlLFxuICAgICAgICAgICAgICBzY29wZTogJ2R5bmFtaWMnLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIGlmIChyZXN1bHQuY29uZmlnKSB7XG4gICAgICAgICAgICAgIGNvbmZpZ3MgPSByZXN1bHQuY29uZmlnLm1jcFNlcnZlcnNcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIGVycm9ycyA9IHJlc3VsdC5lcnJvcnNcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoZXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGFsbEVycm9ycy5wdXNoKC4uLmVycm9ycylcbiAgICAgICAgICB9IGVsc2UgaWYgKGNvbmZpZ3MpIHtcbiAgICAgICAgICAgIC8vIE1lcmdlIGNvbmZpZ3MsIGxhdGVyIG9uZXMgb3ZlcnJpZGUgZWFybGllciBvbmVzXG4gICAgICAgICAgICBhbGxDb25maWdzID0geyAuLi5hbGxDb25maWdzLCAuLi5jb25maWdzIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoYWxsRXJyb3JzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICBjb25zdCBmb3JtYXR0ZWRFcnJvcnMgPSBhbGxFcnJvcnNcbiAgICAgICAgICAgIC5tYXAoZXJyID0+IGAke2Vyci5wYXRoID8gZXJyLnBhdGggKyAnOiAnIDogJyd9JHtlcnIubWVzc2FnZX1gKVxuICAgICAgICAgICAgLmpvaW4oJ1xcbicpXG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYC0tbWNwLWNvbmZpZyB2YWxpZGF0aW9uIGZhaWxlZCAoJHthbGxFcnJvcnMubGVuZ3RofSBlcnJvcnMpOiAke2Zvcm1hdHRlZEVycm9yc31gLFxuICAgICAgICAgICAgeyBsZXZlbDogJ2Vycm9yJyB9LFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgIGBFcnJvcjogSW52YWxpZCBNQ1AgY29uZmlndXJhdGlvbjpcXG4ke2Zvcm1hdHRlZEVycm9yc31cXG5gLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChPYmplY3Qua2V5cyhhbGxDb25maWdzKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgLy8gU0RLIGhvc3RzIChOZXN0L0Rlc2t0b3ApIG93biB0aGVpciBzZXJ2ZXIgbmFtaW5nIGFuZCBtYXkgcmV1c2VcbiAgICAgICAgICAvLyBidWlsdC1pbiBuYW1lcyDigJQgc2tpcCByZXNlcnZlZC1uYW1lIGNoZWNrcyBmb3IgdHlwZTonc2RrJy5cbiAgICAgICAgICBjb25zdCBub25TZGtDb25maWdOYW1lcyA9IE9iamVjdC5lbnRyaWVzKGFsbENvbmZpZ3MpXG4gICAgICAgICAgICAuZmlsdGVyKChbLCBjb25maWddKSA9PiBjb25maWcudHlwZSAhPT0gJ3NkaycpXG4gICAgICAgICAgICAubWFwKChbbmFtZV0pID0+IG5hbWUpXG5cbiAgICAgICAgICBsZXQgcmVzZXJ2ZWROYW1lRXJyb3I6IHN0cmluZyB8IG51bGwgPSBudWxsXG4gICAgICAgICAgaWYgKG5vblNka0NvbmZpZ05hbWVzLnNvbWUoaXNDbGF1ZGVJbkNocm9tZU1DUFNlcnZlcikpIHtcbiAgICAgICAgICAgIHJlc2VydmVkTmFtZUVycm9yID0gYEludmFsaWQgTUNQIGNvbmZpZ3VyYXRpb246IFwiJHtDTEFVREVfSU5fQ0hST01FX01DUF9TRVJWRVJfTkFNRX1cIiBpcyBhIHJlc2VydmVkIE1DUCBuYW1lLmBcbiAgICAgICAgICB9IGVsc2UgaWYgKGZlYXR1cmUoJ0NISUNBR09fTUNQJykpIHtcbiAgICAgICAgICAgIGNvbnN0IHsgaXNDb21wdXRlclVzZU1DUFNlcnZlciwgQ09NUFVURVJfVVNFX01DUF9TRVJWRVJfTkFNRSB9ID1cbiAgICAgICAgICAgICAgYXdhaXQgaW1wb3J0KCdzcmMvdXRpbHMvY29tcHV0ZXJVc2UvY29tbW9uLmpzJylcbiAgICAgICAgICAgIGlmIChub25TZGtDb25maWdOYW1lcy5zb21lKGlzQ29tcHV0ZXJVc2VNQ1BTZXJ2ZXIpKSB7XG4gICAgICAgICAgICAgIHJlc2VydmVkTmFtZUVycm9yID0gYEludmFsaWQgTUNQIGNvbmZpZ3VyYXRpb246IFwiJHtDT01QVVRFUl9VU0VfTUNQX1NFUlZFUl9OQU1FfVwiIGlzIGEgcmVzZXJ2ZWQgTUNQIG5hbWUuYFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAocmVzZXJ2ZWROYW1lRXJyb3IpIHtcbiAgICAgICAgICAgIC8vIHN0ZGVycitleGl0KDEpIOKAlCBhIHRocm93IGhlcmUgYmVjb21lcyBhIHNpbGVudCB1bmhhbmRsZWRcbiAgICAgICAgICAgIC8vIHJlamVjdGlvbiBpbiBzdHJlYW0tanNvbiBtb2RlICh2b2lkIG1haW4oKSBpbiBjbGkudHN4KS5cbiAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKGBFcnJvcjogJHtyZXNlcnZlZE5hbWVFcnJvcn1cXG5gKVxuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgLy8gQWRkIGR5bmFtaWMgc2NvcGUgdG8gYWxsIGNvbmZpZ3MuIHR5cGU6J3NkaycgZW50cmllcyBwYXNzIHRocm91Z2hcbiAgICAgICAgICAvLyB1bmNoYW5nZWQg4oCUIHRoZXkncmUgZXh0cmFjdGVkIGludG8gc2RrTWNwQ29uZmlncyBkb3duc3RyZWFtIGFuZFxuICAgICAgICAgIC8vIHBhc3NlZCB0byBwcmludC50cy4gVGhlIFB5dGhvbiBTREsgcmVsaWVzIG9uIHRoaXMgcGF0aCAoaXQgZG9lc24ndFxuICAgICAgICAgIC8vIHNlbmQgc2RrTWNwU2VydmVycyBpbiB0aGUgaW5pdGlhbGl6ZSBtZXNzYWdlKS4gRHJvcHBpbmcgdGhlbSBoZXJlXG4gICAgICAgICAgLy8gYnJva2UgQ293b3JrZXIgKGluYy01MTIyKS4gVGhlIHBvbGljeSBmaWx0ZXIgYmVsb3cgYWxyZWFkeSBleGVtcHRzXG4gICAgICAgICAgLy8gdHlwZTonc2RrJywgYW5kIHRoZSBlbnRyaWVzIGFyZSBpbmVydCB3aXRob3V0IGFuIFNESyB0cmFuc3BvcnQgb25cbiAgICAgICAgICAvLyBzdGRpbiwgc28gdGhlcmUncyBubyBieXBhc3MgcmlzayBmcm9tIGxldHRpbmcgdGhlbSB0aHJvdWdoLlxuICAgICAgICAgIGNvbnN0IHNjb3BlZENvbmZpZ3MgPSBtYXBWYWx1ZXMoYWxsQ29uZmlncywgY29uZmlnID0+ICh7XG4gICAgICAgICAgICAuLi5jb25maWcsXG4gICAgICAgICAgICBzY29wZTogJ2R5bmFtaWMnIGFzIGNvbnN0LFxuICAgICAgICAgIH0pKVxuXG4gICAgICAgICAgLy8gRW5mb3JjZSBtYW5hZ2VkIHBvbGljeSAoYWxsb3dlZE1jcFNlcnZlcnMgLyBkZW5pZWRNY3BTZXJ2ZXJzKSBvblxuICAgICAgICAgIC8vIC0tbWNwLWNvbmZpZyBzZXJ2ZXJzLiBXaXRob3V0IHRoaXMsIHRoZSBDTEkgZmxhZyBieXBhc3NlcyB0aGVcbiAgICAgICAgICAvLyBlbnRlcnByaXNlIGFsbG93bGlzdCB0aGF0IHVzZXIvcHJvamVjdC9sb2NhbCBjb25maWdzIGdvIHRocm91Z2ggaW5cbiAgICAgICAgICAvLyBnZXRDbGF1ZGVDb2RlTWNwQ29uZmlncyDigJQgY2FsbGVycyBzcHJlYWQgZHluYW1pY01jcENvbmZpZyBiYWNrIG9uXG4gICAgICAgICAgLy8gdG9wIG9mIGZpbHRlcmVkIHJlc3VsdHMuIEZpbHRlciBoZXJlIGF0IHRoZSBzb3VyY2Ugc28gYWxsXG4gICAgICAgICAgLy8gZG93bnN0cmVhbSBjb25zdW1lcnMgc2VlIHRoZSBwb2xpY3ktZmlsdGVyZWQgc2V0LlxuICAgICAgICAgIGNvbnN0IHsgYWxsb3dlZCwgYmxvY2tlZCB9ID0gZmlsdGVyTWNwU2VydmVyc0J5UG9saWN5KHNjb3BlZENvbmZpZ3MpXG4gICAgICAgICAgaWYgKGJsb2NrZWQubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgICAgICAgIGBXYXJuaW5nOiBNQ1AgJHtwbHVyYWwoYmxvY2tlZC5sZW5ndGgsICdzZXJ2ZXInKX0gYmxvY2tlZCBieSBlbnRlcnByaXNlIHBvbGljeTogJHtibG9ja2VkLmpvaW4oJywgJyl9XFxuYCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG4gICAgICAgICAgZHluYW1pY01jcENvbmZpZyA9IHsgLi4uZHluYW1pY01jcENvbmZpZywgLi4uYWxsb3dlZCB9XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gRXh0cmFjdCBDbGF1ZGUgaW4gQ2hyb21lIG9wdGlvbiBhbmQgZW5mb3JjZSBjbGF1ZGUuYWkgc3Vic2NyaWJlciBjaGVjayAodW5sZXNzIHVzZXIgaXMgYW50KVxuICAgICAgY29uc3QgY2hyb21lT3B0cyA9IG9wdGlvbnMgYXMgeyBjaHJvbWU/OiBib29sZWFuIH1cbiAgICAgIC8vIFN0b3JlIHRoZSBleHBsaWNpdCBDTEkgZmxhZyBzbyB0ZWFtbWF0ZXMgY2FuIGluaGVyaXQgaXRcbiAgICAgIHNldENocm9tZUZsYWdPdmVycmlkZShjaHJvbWVPcHRzLmNocm9tZSlcbiAgICAgIGNvbnN0IGVuYWJsZUNsYXVkZUluQ2hyb21lID1cbiAgICAgICAgc2hvdWxkRW5hYmxlQ2xhdWRlSW5DaHJvbWUoY2hyb21lT3B0cy5jaHJvbWUpICYmXG4gICAgICAgIChcImV4dGVybmFsXCIgPT09ICdhbnQnIHx8IGlzQ2xhdWRlQUlTdWJzY3JpYmVyKCkpXG4gICAgICBjb25zdCBhdXRvRW5hYmxlQ2xhdWRlSW5DaHJvbWUgPVxuICAgICAgICAhZW5hYmxlQ2xhdWRlSW5DaHJvbWUgJiYgc2hvdWxkQXV0b0VuYWJsZUNsYXVkZUluQ2hyb21lKClcblxuICAgICAgaWYgKGVuYWJsZUNsYXVkZUluQ2hyb21lKSB7XG4gICAgICAgIGNvbnN0IHBsYXRmb3JtID0gZ2V0UGxhdGZvcm0oKVxuICAgICAgICB0cnkge1xuICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9jbGF1ZGVfaW5fY2hyb21lX3NldHVwJywge1xuICAgICAgICAgICAgcGxhdGZvcm06XG4gICAgICAgICAgICAgIHBsYXRmb3JtIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgfSlcblxuICAgICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgIG1jcENvbmZpZzogY2hyb21lTWNwQ29uZmlnLFxuICAgICAgICAgICAgYWxsb3dlZFRvb2xzOiBjaHJvbWVNY3BUb29scyxcbiAgICAgICAgICAgIHN5c3RlbVByb21wdDogY2hyb21lU3lzdGVtUHJvbXB0LFxuICAgICAgICAgIH0gPSBzZXR1cENsYXVkZUluQ2hyb21lKClcbiAgICAgICAgICBkeW5hbWljTWNwQ29uZmlnID0geyAuLi5keW5hbWljTWNwQ29uZmlnLCAuLi5jaHJvbWVNY3BDb25maWcgfVxuICAgICAgICAgIGFsbG93ZWRUb29scy5wdXNoKC4uLmNocm9tZU1jcFRvb2xzKVxuICAgICAgICAgIGlmIChjaHJvbWVTeXN0ZW1Qcm9tcHQpIHtcbiAgICAgICAgICAgIGFwcGVuZFN5c3RlbVByb21wdCA9IGFwcGVuZFN5c3RlbVByb21wdFxuICAgICAgICAgICAgICA/IGAke2Nocm9tZVN5c3RlbVByb21wdH1cXG5cXG4ke2FwcGVuZFN5c3RlbVByb21wdH1gXG4gICAgICAgICAgICAgIDogY2hyb21lU3lzdGVtUHJvbXB0XG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9jbGF1ZGVfaW5fY2hyb21lX3NldHVwX2ZhaWxlZCcsIHtcbiAgICAgICAgICAgIHBsYXRmb3JtOlxuICAgICAgICAgICAgICBwbGF0Zm9ybSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIH0pXG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKGBbQ2xhdWRlIGluIENocm9tZV0gRXJyb3I6ICR7ZXJyb3J9YClcbiAgICAgICAgICBsb2dFcnJvcihlcnJvcilcbiAgICAgICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgICAgICAgY29uc29sZS5lcnJvcihgRXJyb3I6IEZhaWxlZCB0byBydW4gd2l0aCBDbGF1ZGUgaW4gQ2hyb21lLmApXG4gICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSBpZiAoYXV0b0VuYWJsZUNsYXVkZUluQ2hyb21lKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgeyBtY3BDb25maWc6IGNocm9tZU1jcENvbmZpZyB9ID0gc2V0dXBDbGF1ZGVJbkNocm9tZSgpXG4gICAgICAgICAgZHluYW1pY01jcENvbmZpZyA9IHsgLi4uZHluYW1pY01jcENvbmZpZywgLi4uY2hyb21lTWNwQ29uZmlnIH1cblxuICAgICAgICAgIGNvbnN0IGhpbnQgPVxuICAgICAgICAgICAgZmVhdHVyZSgnV0VCX0JST1dTRVJfVE9PTCcpICYmXG4gICAgICAgICAgICB0eXBlb2YgQnVuICE9PSAndW5kZWZpbmVkJyAmJlxuICAgICAgICAgICAgJ1dlYlZpZXcnIGluIEJ1blxuICAgICAgICAgICAgICA/IENMQVVERV9JTl9DSFJPTUVfU0tJTExfSElOVF9XSVRIX1dFQkJST1dTRVJcbiAgICAgICAgICAgICAgOiBDTEFVREVfSU5fQ0hST01FX1NLSUxMX0hJTlRcbiAgICAgICAgICBhcHBlbmRTeXN0ZW1Qcm9tcHQgPSBhcHBlbmRTeXN0ZW1Qcm9tcHRcbiAgICAgICAgICAgID8gYCR7YXBwZW5kU3lzdGVtUHJvbXB0fVxcblxcbiR7aGludH1gXG4gICAgICAgICAgICA6IGhpbnRcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAvLyBTaWxlbnRseSBza2lwIGFueSBlcnJvcnMgZm9yIHRoZSBhdXRvLWVuYWJsZVxuICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgW0NsYXVkZSBpbiBDaHJvbWVdIEVycm9yIChhdXRvLWVuYWJsZSk6ICR7ZXJyb3J9YClcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBFeHRyYWN0IHN0cmljdCBNQ1AgY29uZmlnIGZsYWdcbiAgICAgIGNvbnN0IHN0cmljdE1jcENvbmZpZyA9IG9wdGlvbnMuc3RyaWN0TWNwQ29uZmlnIHx8IGZhbHNlXG5cbiAgICAgIC8vIENoZWNrIGlmIGVudGVycHJpc2UgTUNQIGNvbmZpZ3VyYXRpb24gZXhpc3RzLiBXaGVuIGl0IGRvZXMsIG9ubHkgYWxsb3cgZHluYW1pYyBNQ1BcbiAgICAgIC8vIGNvbmZpZ3MgdGhhdCBjb250YWluIHNwZWNpYWwgc2VydmVyIHR5cGVzIChzZGspXG4gICAgICBpZiAoZG9lc0VudGVycHJpc2VNY3BDb25maWdFeGlzdCgpKSB7XG4gICAgICAgIGlmIChzdHJpY3RNY3BDb25maWcpIHtcbiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShcbiAgICAgICAgICAgIGNoYWxrLnJlZChcbiAgICAgICAgICAgICAgJ1lvdSBjYW5ub3QgdXNlIC0tc3RyaWN0LW1jcC1jb25maWcgd2hlbiBhbiBlbnRlcnByaXNlIE1DUCBjb25maWcgaXMgcHJlc2VudCcsXG4gICAgICAgICAgICApLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEZvciAtLW1jcC1jb25maWcsIGFsbG93IGlmIGFsbCBzZXJ2ZXJzIGFyZSBpbnRlcm5hbCB0eXBlcyAoc2RrKVxuICAgICAgICBpZiAoXG4gICAgICAgICAgZHluYW1pY01jcENvbmZpZyAmJlxuICAgICAgICAgICFhcmVNY3BDb25maWdzQWxsb3dlZFdpdGhFbnRlcnByaXNlTWNwQ29uZmlnKGR5bmFtaWNNY3BDb25maWcpXG4gICAgICAgICkge1xuICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgY2hhbGsucmVkKFxuICAgICAgICAgICAgICAnWW91IGNhbm5vdCBkeW5hbWljYWxseSBjb25maWd1cmUgTUNQIHNlcnZlcnMgd2hlbiBhbiBlbnRlcnByaXNlIE1DUCBjb25maWcgaXMgcHJlc2VudCcsXG4gICAgICAgICAgICApLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBjaGljYWdvIE1DUDogZ3VhcmRlZCBDb21wdXRlciBVc2UgKGFwcCBhbGxvd2xpc3QgKyBmcm9udG1vc3QgZ2F0ZSArXG4gICAgICAvLyBTQ0NvbnRlbnRGaWx0ZXIgc2NyZWVuc2hvdHMpLiBBbnQtb25seSwgR3Jvd3RoQm9vay1nYXRlZCDigJQgZmFpbHVyZXNcbiAgICAgIC8vIGFyZSBzaWxlbnQgKHRoaXMgaXMgZG9nZm9vZGluZykuIFBsYXRmb3JtICsgaW50ZXJhY3RpdmUgY2hlY2tzIGlubGluZVxuICAgICAgLy8gc28gbm9uLW1hY09TIC8gcHJpbnQtbW9kZSBhbnRzIHNraXAgdGhlIGhlYXZ5IEBhbnQvY29tcHV0ZXItdXNlLW1jcFxuICAgICAgLy8gaW1wb3J0IGVudGlyZWx5LiBnYXRlcy5qcyBpcyBsaWdodCAodHlwZS1vbmx5IHBhY2thZ2UgaW1wb3J0KS5cbiAgICAgIC8vXG4gICAgICAvLyBQbGFjZWQgQUZURVIgdGhlIGVudGVycHJpc2UtTUNQLWNvbmZpZyBjaGVjazogdGhhdCBjaGVjayByZWplY3RzIGFueVxuICAgICAgLy8gZHluYW1pY01jcENvbmZpZyBlbnRyeSB3aXRoIGB0eXBlICE9PSAnc2RrJ2AsIGFuZCBvdXIgY29uZmlnIGlzXG4gICAgICAvLyBgdHlwZTogJ3N0ZGlvJ2AuIEFuIGVudGVycHJpc2UtY29uZmlnIGFudCB3aXRoIHRoZSBHQiBnYXRlIG9uIHdvdWxkXG4gICAgICAvLyBvdGhlcndpc2UgcHJvY2Vzcy5leGl0KDEpLiBDaHJvbWUgaGFzIHRoZSBzYW1lIGxhdGVudCBpc3N1ZSBidXQgaGFzXG4gICAgICAvLyBzaGlwcGVkIHdpdGhvdXQgaW5jaWRlbnQ7IGNoaWNhZ28gcGxhY2VzIGl0c2VsZiBjb3JyZWN0bHkuXG4gICAgICBpZiAoXG4gICAgICAgIGZlYXR1cmUoJ0NISUNBR09fTUNQJykgJiZcbiAgICAgICAgZ2V0UGxhdGZvcm0oKSA9PT0gJ21hY29zJyAmJlxuICAgICAgICAhZ2V0SXNOb25JbnRlcmFjdGl2ZVNlc3Npb24oKVxuICAgICAgKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgeyBnZXRDaGljYWdvRW5hYmxlZCB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgJ3NyYy91dGlscy9jb21wdXRlclVzZS9nYXRlcy5qcydcbiAgICAgICAgICApXG4gICAgICAgICAgaWYgKGdldENoaWNhZ29FbmFibGVkKCkpIHtcbiAgICAgICAgICAgIGNvbnN0IHsgc2V0dXBDb21wdXRlclVzZU1DUCB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgICAnc3JjL3V0aWxzL2NvbXB1dGVyVXNlL3NldHVwLmpzJ1xuICAgICAgICAgICAgKVxuICAgICAgICAgICAgY29uc3QgeyBtY3BDb25maWcsIGFsbG93ZWRUb29sczogY3VUb29scyB9ID0gc2V0dXBDb21wdXRlclVzZU1DUCgpXG4gICAgICAgICAgICBkeW5hbWljTWNwQ29uZmlnID0geyAuLi5keW5hbWljTWNwQ29uZmlnLCAuLi5tY3BDb25maWcgfVxuICAgICAgICAgICAgYWxsb3dlZFRvb2xzLnB1c2goLi4uY3VUb29scylcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYFtDb21wdXRlciBVc2UgTUNQXSBTZXR1cCBmYWlsZWQ6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKX1gLFxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBTdG9yZSBhZGRpdGlvbmFsIGRpcmVjdG9yaWVzIGZvciBDTEFVREUubWQgbG9hZGluZyAoY29udHJvbGxlZCBieSBlbnYgdmFyKVxuICAgICAgc2V0QWRkaXRpb25hbERpcmVjdG9yaWVzRm9yQ2xhdWRlTWQoYWRkRGlyKVxuXG4gICAgICAvLyBDaGFubmVsIHNlcnZlciBhbGxvd2xpc3QgZnJvbSAtLWNoYW5uZWxzIGZsYWcg4oCUIHNlcnZlcnMgd2hvc2VcbiAgICAgIC8vIGluYm91bmQgcHVzaCBub3RpZmljYXRpb25zIHNob3VsZCByZWdpc3RlciB0aGlzIHNlc3Npb24uIFRoZSBvcHRpb25cbiAgICAgIC8vIGlzIGFkZGVkIGluc2lkZSBhIGZlYXR1cmUoKSBibG9jayBzbyBUUyBkb2Vzbid0IGtub3cgYWJvdXQgaXRcbiAgICAgIC8vIG9uIHRoZSBvcHRpb25zIHR5cGUg4oCUIHNhbWUgcGF0dGVybiBhcyAtLWFzc2lzdGFudCBhdCBtYWluLnRzeDoxODI0LlxuICAgICAgLy8gZGV2Q2hhbm5lbHMgaXMgZGVmZXJyZWQ6IHNob3dTZXR1cFNjcmVlbnMgc2hvd3MgYSBjb25maXJtYXRpb24gZGlhbG9nXG4gICAgICAvLyBhbmQgb25seSBhcHBlbmRzIHRvIGFsbG93ZWRDaGFubmVscyBvbiBhY2NlcHQuXG4gICAgICBsZXQgZGV2Q2hhbm5lbHM6IENoYW5uZWxFbnRyeVtdIHwgdW5kZWZpbmVkXG4gICAgICBpZiAoZmVhdHVyZSgnS0FJUk9TJykgfHwgZmVhdHVyZSgnS0FJUk9TX0NIQU5ORUxTJykpIHtcbiAgICAgICAgLy8gUGFyc2UgcGx1Z2luOm5hbWVAbWFya2V0cGxhY2UgLyBzZXJ2ZXI6WSB0YWdzIGludG8gdHlwZWQgZW50cmllcy5cbiAgICAgICAgLy8gVGFnIGRlY2lkZXMgdHJ1c3QgbW9kZWwgZG93bnN0cmVhbTogcGx1Z2luLWtpbmQgaGl0cyBtYXJrZXRwbGFjZVxuICAgICAgICAvLyB2ZXJpZmljYXRpb24gKyBHcm93dGhCb29rIGFsbG93bGlzdCwgc2VydmVyLWtpbmQgYWx3YXlzIGZhaWxzXG4gICAgICAgIC8vIGFsbG93bGlzdCAoc2NoZW1hIGlzIHBsdWdpbi1vbmx5KSB1bmxlc3MgZGV2IGZsYWcgaXMgc2V0LlxuICAgICAgICAvLyBVbnRhZ2dlZCBvciBtYXJrZXRwbGFjZS1sZXNzIHBsdWdpbiBlbnRyaWVzIGFyZSBoYXJkIGVycm9ycyDigJRcbiAgICAgICAgLy8gc2lsZW50bHkgbm90LW1hdGNoaW5nIGluIHRoZSBnYXRlIHdvdWxkIGxvb2sgbGlrZSBjaGFubmVscyBhcmVcbiAgICAgICAgLy8gXCJvblwiIGJ1dCBub3RoaW5nIGV2ZXIgZmlyZXMuXG4gICAgICAgIGNvbnN0IHBhcnNlQ2hhbm5lbEVudHJpZXMgPSAoXG4gICAgICAgICAgcmF3OiBzdHJpbmdbXSxcbiAgICAgICAgICBmbGFnOiBzdHJpbmcsXG4gICAgICAgICk6IENoYW5uZWxFbnRyeVtdID0+IHtcbiAgICAgICAgICBjb25zdCBlbnRyaWVzOiBDaGFubmVsRW50cnlbXSA9IFtdXG4gICAgICAgICAgY29uc3QgYmFkOiBzdHJpbmdbXSA9IFtdXG4gICAgICAgICAgZm9yIChjb25zdCBjIG9mIHJhdykge1xuICAgICAgICAgICAgaWYgKGMuc3RhcnRzV2l0aCgncGx1Z2luOicpKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHJlc3QgPSBjLnNsaWNlKDcpXG4gICAgICAgICAgICAgIGNvbnN0IGF0ID0gcmVzdC5pbmRleE9mKCdAJylcbiAgICAgICAgICAgICAgaWYgKGF0IDw9IDAgfHwgYXQgPT09IHJlc3QubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgICAgIGJhZC5wdXNoKGMpXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZW50cmllcy5wdXNoKHtcbiAgICAgICAgICAgICAgICAgIGtpbmQ6ICdwbHVnaW4nLFxuICAgICAgICAgICAgICAgICAgbmFtZTogcmVzdC5zbGljZSgwLCBhdCksXG4gICAgICAgICAgICAgICAgICBtYXJrZXRwbGFjZTogcmVzdC5zbGljZShhdCArIDEpLFxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSBpZiAoYy5zdGFydHNXaXRoKCdzZXJ2ZXI6JykgJiYgYy5sZW5ndGggPiA3KSB7XG4gICAgICAgICAgICAgIGVudHJpZXMucHVzaCh7IGtpbmQ6ICdzZXJ2ZXInLCBuYW1lOiBjLnNsaWNlKDcpIH0pXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBiYWQucHVzaChjKVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAoYmFkLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgICBjaGFsay5yZWQoXG4gICAgICAgICAgICAgICAgYCR7ZmxhZ30gZW50cmllcyBtdXN0IGJlIHRhZ2dlZDogJHtiYWQuam9pbignLCAnKX1cXG5gICtcbiAgICAgICAgICAgICAgICAgIGAgIHBsdWdpbjo8bmFtZT5APG1hcmtldHBsYWNlPiAg4oCUIHBsdWdpbi1wcm92aWRlZCBjaGFubmVsIChhbGxvd2xpc3QgZW5mb3JjZWQpXFxuYCArXG4gICAgICAgICAgICAgICAgICBgICBzZXJ2ZXI6PG5hbWU+ICAgICAgICAgICAgICAgIOKAlCBtYW51YWxseSBjb25maWd1cmVkIE1DUCBzZXJ2ZXJcXG5gLFxuICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiBlbnRyaWVzXG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBjaGFubmVsT3B0cyA9IG9wdGlvbnMgYXMge1xuICAgICAgICAgIGNoYW5uZWxzPzogc3RyaW5nW11cbiAgICAgICAgICBkYW5nZXJvdXNseUxvYWREZXZlbG9wbWVudENoYW5uZWxzPzogc3RyaW5nW11cbiAgICAgICAgfVxuICAgICAgICBjb25zdCByYXdDaGFubmVscyA9IGNoYW5uZWxPcHRzLmNoYW5uZWxzXG4gICAgICAgIGNvbnN0IHJhd0RldiA9IGNoYW5uZWxPcHRzLmRhbmdlcm91c2x5TG9hZERldmVsb3BtZW50Q2hhbm5lbHNcbiAgICAgICAgLy8gQWx3YXlzIHBhcnNlICsgc2V0LiBDaGFubmVsc05vdGljZSByZWFkcyBnZXRBbGxvd2VkQ2hhbm5lbHMoKSBhbmRcbiAgICAgICAgLy8gcmVuZGVycyB0aGUgYXBwcm9wcmlhdGUgYnJhbmNoIChkaXNhYmxlZC9ub0F1dGgvcG9saWN5QmxvY2tlZC9cbiAgICAgICAgLy8gbGlzdGVuaW5nKSBpbiB0aGUgc3RhcnR1cCBzY3JlZW4uIGdhdGVDaGFubmVsU2VydmVyKCkgZW5mb3JjZXMuXG4gICAgICAgIC8vIC0tY2hhbm5lbHMgd29ya3MgaW4gYm90aCBpbnRlcmFjdGl2ZSBhbmQgcHJpbnQvU0RLIG1vZGVzOyBkZXYtY2hhbm5lbHNcbiAgICAgICAgLy8gc3RheXMgaW50ZXJhY3RpdmUtb25seSAocmVxdWlyZXMgYSBjb25maXJtYXRpb24gZGlhbG9nKS5cbiAgICAgICAgbGV0IGNoYW5uZWxFbnRyaWVzOiBDaGFubmVsRW50cnlbXSA9IFtdXG4gICAgICAgIGlmIChyYXdDaGFubmVscyAmJiByYXdDaGFubmVscy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgY2hhbm5lbEVudHJpZXMgPSBwYXJzZUNoYW5uZWxFbnRyaWVzKHJhd0NoYW5uZWxzLCAnLS1jaGFubmVscycpXG4gICAgICAgICAgc2V0QWxsb3dlZENoYW5uZWxzKGNoYW5uZWxFbnRyaWVzKVxuICAgICAgICB9XG4gICAgICAgIGlmICghaXNOb25JbnRlcmFjdGl2ZVNlc3Npb24pIHtcbiAgICAgICAgICBpZiAocmF3RGV2ICYmIHJhd0Rldi5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBkZXZDaGFubmVscyA9IHBhcnNlQ2hhbm5lbEVudHJpZXMoXG4gICAgICAgICAgICAgIHJhd0RldixcbiAgICAgICAgICAgICAgJy0tZGFuZ2Vyb3VzbHktbG9hZC1kZXZlbG9wbWVudC1jaGFubmVscycsXG4gICAgICAgICAgICApXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIEZsYWctdXNhZ2UgdGVsZW1ldHJ5LiBQbHVnaW4gaWRlbnRpZmllcnMgYXJlIGxvZ2dlZCAoc2FtZSB0aWVyIGFzXG4gICAgICAgIC8vIHRlbmd1X3BsdWdpbl9pbnN0YWxsZWQg4oCUIHB1YmxpYy1yZWdpc3RyeS1zdHlsZSBuYW1lcyk7IHNlcnZlci1raW5kXG4gICAgICAgIC8vIG5hbWVzIGFyZSBub3QgKE1DUC1zZXJ2ZXItbmFtZSB0aWVyLCBvcHQtaW4tb25seSBlbHNld2hlcmUpLlxuICAgICAgICAvLyBQZXItc2VydmVyIGdhdGUgb3V0Y29tZXMgbGFuZCBpbiB0ZW5ndV9tY3BfY2hhbm5lbF9nYXRlIG9uY2VcbiAgICAgICAgLy8gc2VydmVycyBjb25uZWN0LiBEZXYgZW50cmllcyBnbyB0aHJvdWdoIGEgY29uZmlybWF0aW9uIGRpYWxvZyBhZnRlclxuICAgICAgICAvLyB0aGlzIOKAlCBkZXZfcGx1Z2lucyBjYXB0dXJlcyB3aGF0IHdhcyB0eXBlZCwgbm90IHdoYXQgd2FzIGFjY2VwdGVkLlxuICAgICAgICBpZiAoY2hhbm5lbEVudHJpZXMubGVuZ3RoID4gMCB8fCAoZGV2Q2hhbm5lbHM/Lmxlbmd0aCA/PyAwKSA+IDApIHtcbiAgICAgICAgICBjb25zdCBqb2luUGx1Z2luSWRzID0gKGVudHJpZXM6IENoYW5uZWxFbnRyeVtdKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBpZHMgPSBlbnRyaWVzLmZsYXRNYXAoZSA9PlxuICAgICAgICAgICAgICBlLmtpbmQgPT09ICdwbHVnaW4nID8gW2Ake2UubmFtZX1AJHtlLm1hcmtldHBsYWNlfWBdIDogW10sXG4gICAgICAgICAgICApXG4gICAgICAgICAgICByZXR1cm4gaWRzLmxlbmd0aCA+IDBcbiAgICAgICAgICAgICAgPyAoaWRzXG4gICAgICAgICAgICAgICAgICAuc29ydCgpXG4gICAgICAgICAgICAgICAgICAuam9pbihcbiAgICAgICAgICAgICAgICAgICAgJywnLFxuICAgICAgICAgICAgICAgICAgKSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTKVxuICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgIH1cbiAgICAgICAgICBsb2dFdmVudCgndGVuZ3VfbWNwX2NoYW5uZWxfZmxhZ3MnLCB7XG4gICAgICAgICAgICBjaGFubmVsc19jb3VudDogY2hhbm5lbEVudHJpZXMubGVuZ3RoLFxuICAgICAgICAgICAgZGV2X2NvdW50OiBkZXZDaGFubmVscz8ubGVuZ3RoID8/IDAsXG4gICAgICAgICAgICBwbHVnaW5zOiBqb2luUGx1Z2luSWRzKGNoYW5uZWxFbnRyaWVzKSxcbiAgICAgICAgICAgIGRldl9wbHVnaW5zOiBqb2luUGx1Z2luSWRzKGRldkNoYW5uZWxzID8/IFtdKSxcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIFNESyBvcHQtaW4gZm9yIFNlbmRVc2VyTWVzc2FnZSB2aWEgLS10b29scy4gQWxsIHNlc3Npb25zIHJlcXVpcmVcbiAgICAgIC8vIGV4cGxpY2l0IG9wdC1pbjsgbGlzdGluZyBpdCBpbiAtLXRvb2xzIHNpZ25hbHMgaW50ZW50LiBSdW5zIEJFRk9SRVxuICAgICAgLy8gaW5pdGlhbGl6ZVRvb2xQZXJtaXNzaW9uQ29udGV4dCBzbyBnZXRUb29sc0ZvckRlZmF1bHRQcmVzZXQoKSBzZWVzXG4gICAgICAvLyB0aGUgdG9vbCBhcyBlbmFibGVkIHdoZW4gY29tcHV0aW5nIHRoZSBiYXNlLXRvb2xzIGRpc2FsbG93IGZpbHRlci5cbiAgICAgIC8vIENvbmRpdGlvbmFsIHJlcXVpcmUgYXZvaWRzIGxlYWtpbmcgdGhlIHRvb2wtbmFtZSBzdHJpbmcgaW50b1xuICAgICAgLy8gZXh0ZXJuYWwgYnVpbGRzLlxuICAgICAgaWYgKFxuICAgICAgICAoZmVhdHVyZSgnS0FJUk9TJykgfHwgZmVhdHVyZSgnS0FJUk9TX0JSSUVGJykpICYmXG4gICAgICAgIGJhc2VUb29scy5sZW5ndGggPiAwXG4gICAgICApIHtcbiAgICAgICAgLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICAgICAgICBjb25zdCB7IEJSSUVGX1RPT0xfTkFNRSwgTEVHQUNZX0JSSUVGX1RPT0xfTkFNRSB9ID1cbiAgICAgICAgICByZXF1aXJlKCcuL3Rvb2xzL0JyaWVmVG9vbC9wcm9tcHQuanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuL3Rvb2xzL0JyaWVmVG9vbC9wcm9tcHQuanMnKVxuICAgICAgICBjb25zdCB7IGlzQnJpZWZFbnRpdGxlZCB9ID1cbiAgICAgICAgICByZXF1aXJlKCcuL3Rvb2xzL0JyaWVmVG9vbC9CcmllZlRvb2wuanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuL3Rvb2xzL0JyaWVmVG9vbC9CcmllZlRvb2wuanMnKVxuICAgICAgICAvKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbiAgICAgICAgY29uc3QgcGFyc2VkID0gcGFyc2VUb29sTGlzdEZyb21DTEkoYmFzZVRvb2xzKVxuICAgICAgICBpZiAoXG4gICAgICAgICAgKHBhcnNlZC5pbmNsdWRlcyhCUklFRl9UT09MX05BTUUpIHx8XG4gICAgICAgICAgICBwYXJzZWQuaW5jbHVkZXMoTEVHQUNZX0JSSUVGX1RPT0xfTkFNRSkpICYmXG4gICAgICAgICAgaXNCcmllZkVudGl0bGVkKClcbiAgICAgICAgKSB7XG4gICAgICAgICAgc2V0VXNlck1zZ09wdEluKHRydWUpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gVGhpcyBhd2FpdCByZXBsYWNlcyBibG9ja2luZyBleGlzdHNTeW5jL3N0YXRTeW5jIGNhbGxzIHRoYXQgd2VyZSBhbHJlYWR5IGluXG4gICAgICAvLyB0aGUgc3RhcnR1cCBwYXRoLiBXYWxsLWNsb2NrIHRpbWUgaXMgdW5jaGFuZ2VkOyB3ZSBqdXN0IHlpZWxkIHRvIHRoZSBldmVudFxuICAgICAgLy8gbG9vcCBkdXJpbmcgdGhlIGZzIEkvTyBpbnN0ZWFkIG9mIGJsb2NraW5nIGl0LiBTZWUgIzE5NjYxLlxuICAgICAgY29uc3QgaW5pdFJlc3VsdCA9IGF3YWl0IGluaXRpYWxpemVUb29sUGVybWlzc2lvbkNvbnRleHQoe1xuICAgICAgICBhbGxvd2VkVG9vbHNDbGk6IGFsbG93ZWRUb29scyxcbiAgICAgICAgZGlzYWxsb3dlZFRvb2xzQ2xpOiBkaXNhbGxvd2VkVG9vbHMsXG4gICAgICAgIGJhc2VUb29sc0NsaTogYmFzZVRvb2xzLFxuICAgICAgICBwZXJtaXNzaW9uTW9kZSxcbiAgICAgICAgYWxsb3dEYW5nZXJvdXNseVNraXBQZXJtaXNzaW9ucyxcbiAgICAgICAgYWRkRGlyczogYWRkRGlyLFxuICAgICAgfSlcbiAgICAgIGxldCB0b29sUGVybWlzc2lvbkNvbnRleHQgPSBpbml0UmVzdWx0LnRvb2xQZXJtaXNzaW9uQ29udGV4dFxuICAgICAgY29uc3QgeyB3YXJuaW5ncywgZGFuZ2Vyb3VzUGVybWlzc2lvbnMsIG92ZXJseUJyb2FkQmFzaFBlcm1pc3Npb25zIH0gPVxuICAgICAgICBpbml0UmVzdWx0XG5cbiAgICAgIC8vIEhhbmRsZSBvdmVybHkgYnJvYWQgc2hlbGwgYWxsb3cgcnVsZXMgZm9yIGFudCB1c2VycyAoQmFzaCgqKSwgUG93ZXJTaGVsbCgqKSlcbiAgICAgIGlmIChcbiAgICAgICAgXCJleHRlcm5hbFwiID09PSAnYW50JyAmJlxuICAgICAgICBvdmVybHlCcm9hZEJhc2hQZXJtaXNzaW9ucy5sZW5ndGggPiAwXG4gICAgICApIHtcbiAgICAgICAgZm9yIChjb25zdCBwZXJtaXNzaW9uIG9mIG92ZXJseUJyb2FkQmFzaFBlcm1pc3Npb25zKSB7XG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYElnbm9yaW5nIG92ZXJseSBicm9hZCBzaGVsbCBwZXJtaXNzaW9uICR7cGVybWlzc2lvbi5ydWxlRGlzcGxheX0gZnJvbSAke3Blcm1pc3Npb24uc291cmNlRGlzcGxheX1gLFxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQgPSByZW1vdmVEYW5nZXJvdXNQZXJtaXNzaW9ucyhcbiAgICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICAgICAgb3Zlcmx5QnJvYWRCYXNoUGVybWlzc2lvbnMsXG4gICAgICAgIClcbiAgICAgIH1cblxuICAgICAgaWYgKGZlYXR1cmUoJ1RSQU5TQ1JJUFRfQ0xBU1NJRklFUicpICYmIGRhbmdlcm91c1Blcm1pc3Npb25zLmxlbmd0aCA+IDApIHtcbiAgICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0ID0gc3RyaXBEYW5nZXJvdXNQZXJtaXNzaW9uc0ZvckF1dG9Nb2RlKFxuICAgICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgKVxuICAgICAgfVxuXG4gICAgICAvLyBQcmludCBhbnkgd2FybmluZ3MgZnJvbSBpbml0aWFsaXphdGlvblxuICAgICAgd2FybmluZ3MuZm9yRWFjaCh3YXJuaW5nID0+IHtcbiAgICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgICBjb25zb2xlLmVycm9yKHdhcm5pbmcpXG4gICAgICB9KVxuXG4gICAgICB2b2lkIGFzc2VydE1pblZlcnNpb24oKVxuXG4gICAgICAvLyBjbGF1ZGUuYWkgY29uZmlnIGZldGNoOiAtcCBtb2RlIG9ubHkgKGludGVyYWN0aXZlIHVzZXMgdXNlTWFuYWdlTUNQQ29ubmVjdGlvbnNcbiAgICAgIC8vIHR3by1waGFzZSBsb2FkaW5nKS4gS2lja2VkIG9mZiBoZXJlIHRvIG92ZXJsYXAgd2l0aCBzZXR1cCgpOyBhd2FpdGVkXG4gICAgICAvLyBiZWZvcmUgcnVuSGVhZGxlc3Mgc28gc2luZ2xlLXR1cm4gLXAgc2VlcyBjb25uZWN0b3JzLiBTa2lwcGVkIHVuZGVyXG4gICAgICAvLyBlbnRlcnByaXNlL3N0cmljdCBNQ1AgdG8gcHJlc2VydmUgcG9saWN5IGJvdW5kYXJpZXMuXG4gICAgICBjb25zdCBjbGF1ZGVhaUNvbmZpZ1Byb21pc2U6IFByb21pc2U8XG4gICAgICAgIFJlY29yZDxzdHJpbmcsIFNjb3BlZE1jcFNlcnZlckNvbmZpZz5cbiAgICAgID4gPVxuICAgICAgICBpc05vbkludGVyYWN0aXZlU2Vzc2lvbiAmJlxuICAgICAgICAhc3RyaWN0TWNwQ29uZmlnICYmXG4gICAgICAgICFkb2VzRW50ZXJwcmlzZU1jcENvbmZpZ0V4aXN0KCkgJiZcbiAgICAgICAgLy8gLS1iYXJlIC8gU0lNUExFOiBza2lwIGNsYXVkZS5haSBwcm94eSBzZXJ2ZXJzIChkYXRhZG9nLCBHbWFpbCxcbiAgICAgICAgLy8gU2xhY2ssIEJpZ1F1ZXJ5LCBQdWJNZWQg4oCUIDYtMTRzIGVhY2ggdG8gY29ubmVjdCkuIFNjcmlwdGVkIGNhbGxzXG4gICAgICAgIC8vIHRoYXQgbmVlZCBNQ1AgcGFzcyAtLW1jcC1jb25maWcgZXhwbGljaXRseS5cbiAgICAgICAgIWlzQmFyZU1vZGUoKVxuICAgICAgICAgID8gZmV0Y2hDbGF1ZGVBSU1jcENvbmZpZ3NJZkVsaWdpYmxlKCkudGhlbihjb25maWdzID0+IHtcbiAgICAgICAgICAgICAgY29uc3QgeyBhbGxvd2VkLCBibG9ja2VkIH0gPSBmaWx0ZXJNY3BTZXJ2ZXJzQnlQb2xpY3koY29uZmlncylcbiAgICAgICAgICAgICAgaWYgKGJsb2NrZWQubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgICAgICAgYFdhcm5pbmc6IGNsYXVkZS5haSBNQ1AgJHtwbHVyYWwoYmxvY2tlZC5sZW5ndGgsICdzZXJ2ZXInKX0gYmxvY2tlZCBieSBlbnRlcnByaXNlIHBvbGljeTogJHtibG9ja2VkLmpvaW4oJywgJyl9XFxuYCxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgcmV0dXJuIGFsbG93ZWRcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgOiBQcm9taXNlLnJlc29sdmUoe30pXG5cbiAgICAgIC8vIEtpY2sgb2ZmIE1DUCBjb25maWcgbG9hZGluZyBlYXJseSAoc2FmZSAtIGp1c3QgcmVhZHMgZmlsZXMsIG5vIGV4ZWN1dGlvbikuXG4gICAgICAvLyBCb3RoIGludGVyYWN0aXZlIGFuZCAtcCB1c2UgZ2V0Q2xhdWRlQ29kZU1jcENvbmZpZ3MgKGxvY2FsIGZpbGUgcmVhZHMgb25seSkuXG4gICAgICAvLyBUaGUgbG9jYWwgcHJvbWlzZSBpcyBhd2FpdGVkIGxhdGVyIChiZWZvcmUgcHJlZmV0Y2hBbGxNY3BSZXNvdXJjZXMpIHRvXG4gICAgICAvLyBvdmVybGFwIGNvbmZpZyBJL08gd2l0aCBzZXR1cCgpLCBjb21tYW5kcyBsb2FkaW5nLCBhbmQgdHJ1c3QgZGlhbG9nLlxuICAgICAgbG9nRm9yRGVidWdnaW5nKCdbU1RBUlRVUF0gTG9hZGluZyBNQ1AgY29uZmlncy4uLicpXG4gICAgICBjb25zdCBtY3BDb25maWdTdGFydCA9IERhdGUubm93KClcbiAgICAgIGxldCBtY3BDb25maWdSZXNvbHZlZE1zOiBudW1iZXIgfCB1bmRlZmluZWRcbiAgICAgIC8vIC0tYmFyZSBza2lwcyBhdXRvLWRpc2NvdmVyZWQgTUNQICgubWNwLmpzb24sIHVzZXIgc2V0dGluZ3MsIHBsdWdpbnMpIOKAlFxuICAgICAgLy8gb25seSBleHBsaWNpdCAtLW1jcC1jb25maWcgd29ya3MuIGR5bmFtaWNNY3BDb25maWcgaXMgc3ByZWFkIG9udG9cbiAgICAgIC8vIGFsbE1jcENvbmZpZ3MgZG93bnN0cmVhbSBzbyBpdCBzdXJ2aXZlcyB0aGlzIHNraXAuXG4gICAgICBjb25zdCBtY3BDb25maWdQcm9taXNlID0gKFxuICAgICAgICBzdHJpY3RNY3BDb25maWcgfHwgaXNCYXJlTW9kZSgpXG4gICAgICAgICAgPyBQcm9taXNlLnJlc29sdmUoe1xuICAgICAgICAgICAgICBzZXJ2ZXJzOiB7fSBhcyBSZWNvcmQ8c3RyaW5nLCBTY29wZWRNY3BTZXJ2ZXJDb25maWc+LFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICA6IGdldENsYXVkZUNvZGVNY3BDb25maWdzKGR5bmFtaWNNY3BDb25maWcpXG4gICAgICApLnRoZW4ocmVzdWx0ID0+IHtcbiAgICAgICAgbWNwQ29uZmlnUmVzb2x2ZWRNcyA9IERhdGUubm93KCkgLSBtY3BDb25maWdTdGFydFxuICAgICAgICByZXR1cm4gcmVzdWx0XG4gICAgICB9KVxuXG4gICAgICAvLyBOT1RFOiBXZSBkbyBOT1QgY2FsbCBwcmVmZXRjaEFsbE1jcFJlc291cmNlcyBoZXJlIC0gdGhhdCdzIGRlZmVycmVkIHVudGlsIGFmdGVyIHRydXN0IGRpYWxvZ1xuXG4gICAgICBpZiAoXG4gICAgICAgIGlucHV0Rm9ybWF0ICYmXG4gICAgICAgIGlucHV0Rm9ybWF0ICE9PSAndGV4dCcgJiZcbiAgICAgICAgaW5wdXRGb3JtYXQgIT09ICdzdHJlYW0tanNvbidcbiAgICAgICkge1xuICAgICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTo6IGludGVudGlvbmFsIGNvbnNvbGUgb3V0cHV0XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoYEVycm9yOiBJbnZhbGlkIGlucHV0IGZvcm1hdCBcIiR7aW5wdXRGb3JtYXR9XCIuYClcbiAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICB9XG4gICAgICBpZiAoaW5wdXRGb3JtYXQgPT09ICdzdHJlYW0tanNvbicgJiYgb3V0cHV0Rm9ybWF0ICE9PSAnc3RyZWFtLWpzb24nKSB7XG4gICAgICAgIC8vIGJpb21lLWlnbm9yZSBsaW50L3N1c3BpY2lvdXMvbm9Db25zb2xlOjogaW50ZW50aW9uYWwgY29uc29sZSBvdXRwdXRcbiAgICAgICAgY29uc29sZS5lcnJvcihcbiAgICAgICAgICBgRXJyb3I6IC0taW5wdXQtZm9ybWF0PXN0cmVhbS1qc29uIHJlcXVpcmVzIG91dHB1dC1mb3JtYXQ9c3RyZWFtLWpzb24uYCxcbiAgICAgICAgKVxuICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgIH1cblxuICAgICAgLy8gVmFsaWRhdGUgc2RrVXJsIGlzIG9ubHkgdXNlZCB3aXRoIGFwcHJvcHJpYXRlIGZvcm1hdHMgKGZvcm1hdHMgYXJlIGF1dG8tc2V0IGFib3ZlKVxuICAgICAgaWYgKHNka1VybCkge1xuICAgICAgICBpZiAoaW5wdXRGb3JtYXQgIT09ICdzdHJlYW0tanNvbicgfHwgb3V0cHV0Rm9ybWF0ICE9PSAnc3RyZWFtLWpzb24nKSB7XG4gICAgICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgICBgRXJyb3I6IC0tc2RrLXVybCByZXF1aXJlcyBib3RoIC0taW5wdXQtZm9ybWF0PXN0cmVhbS1qc29uIGFuZCAtLW91dHB1dC1mb3JtYXQ9c3RyZWFtLWpzb24uYCxcbiAgICAgICAgICApXG4gICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gVmFsaWRhdGUgcmVwbGF5VXNlck1lc3NhZ2VzIGlzIG9ubHkgdXNlZCB3aXRoIHN0cmVhbS1qc29uIGZvcm1hdHNcbiAgICAgIGlmIChvcHRpb25zLnJlcGxheVVzZXJNZXNzYWdlcykge1xuICAgICAgICBpZiAoaW5wdXRGb3JtYXQgIT09ICdzdHJlYW0tanNvbicgfHwgb3V0cHV0Rm9ybWF0ICE9PSAnc3RyZWFtLWpzb24nKSB7XG4gICAgICAgICAgLy8gYmlvbWUtaWdub3JlIGxpbnQvc3VzcGljaW91cy9ub0NvbnNvbGU6OiBpbnRlbnRpb25hbCBjb25zb2xlIG91dHB1dFxuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgICBgRXJyb3I6IC0tcmVwbGF5LXVzZXItbWVzc2FnZXMgcmVxdWlyZXMgYm90aCAtLWlucHV0LWZvcm1hdD1zdHJlYW0tanNvbiBhbmQgLS1vdXRwdXQtZm9ybWF0PXN0cmVhbS1qc29uLmAsXG4gICAgICAgICAgKVxuICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIFZhbGlkYXRlIGluY2x1ZGVQYXJ0aWFsTWVzc2FnZXMgaXMgb25seSB1c2VkIHdpdGggcHJpbnQgbW9kZSBhbmQgc3RyZWFtLWpzb24gb3V0cHV0XG4gICAgICBpZiAoZWZmZWN0aXZlSW5jbHVkZVBhcnRpYWxNZXNzYWdlcykge1xuICAgICAgICBpZiAoIWlzTm9uSW50ZXJhY3RpdmVTZXNzaW9uIHx8IG91dHB1dEZvcm1hdCAhPT0gJ3N0cmVhbS1qc29uJykge1xuICAgICAgICAgIHdyaXRlVG9TdGRlcnIoXG4gICAgICAgICAgICBgRXJyb3I6IC0taW5jbHVkZS1wYXJ0aWFsLW1lc3NhZ2VzIHJlcXVpcmVzIC0tcHJpbnQgYW5kIC0tb3V0cHV0LWZvcm1hdD1zdHJlYW0tanNvbi5gLFxuICAgICAgICAgIClcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBWYWxpZGF0ZSAtLW5vLXNlc3Npb24tcGVyc2lzdGVuY2UgaXMgb25seSB1c2VkIHdpdGggcHJpbnQgbW9kZVxuICAgICAgaWYgKG9wdGlvbnMuc2Vzc2lvblBlcnNpc3RlbmNlID09PSBmYWxzZSAmJiAhaXNOb25JbnRlcmFjdGl2ZVNlc3Npb24pIHtcbiAgICAgICAgd3JpdGVUb1N0ZGVycihcbiAgICAgICAgICBgRXJyb3I6IC0tbm8tc2Vzc2lvbi1wZXJzaXN0ZW5jZSBjYW4gb25seSBiZSB1c2VkIHdpdGggLS1wcmludCBtb2RlLmAsXG4gICAgICAgIClcbiAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGVmZmVjdGl2ZVByb21wdCA9IHByb21wdCB8fCAnJ1xuICAgICAgbGV0IGlucHV0UHJvbXB0ID0gYXdhaXQgZ2V0SW5wdXRQcm9tcHQoXG4gICAgICAgIGVmZmVjdGl2ZVByb21wdCxcbiAgICAgICAgKGlucHV0Rm9ybWF0ID8/ICd0ZXh0JykgYXMgJ3RleHQnIHwgJ3N0cmVhbS1qc29uJyxcbiAgICAgIClcbiAgICAgIHByb2ZpbGVDaGVja3BvaW50KCdhY3Rpb25fYWZ0ZXJfaW5wdXRfcHJvbXB0JylcblxuICAgICAgLy8gQWN0aXZhdGUgcHJvYWN0aXZlIG1vZGUgQkVGT1JFIGdldFRvb2xzKCkgc28gU2xlZXBUb29sLmlzRW5hYmxlZCgpXG4gICAgICAvLyAod2hpY2ggcmV0dXJucyBpc1Byb2FjdGl2ZUFjdGl2ZSgpKSBwYXNzZXMgYW5kIFNsZWVwIGlzIGluY2x1ZGVkLlxuICAgICAgLy8gVGhlIGxhdGVyIFJFUEwtcGF0aCBtYXliZUFjdGl2YXRlUHJvYWN0aXZlKCkgY2FsbHMgYXJlIGlkZW1wb3RlbnQuXG4gICAgICBtYXliZUFjdGl2YXRlUHJvYWN0aXZlKG9wdGlvbnMpXG5cbiAgICAgIGxldCB0b29scyA9IGdldFRvb2xzKHRvb2xQZXJtaXNzaW9uQ29udGV4dClcblxuICAgICAgLy8gQXBwbHkgY29vcmRpbmF0b3IgbW9kZSB0b29sIGZpbHRlcmluZyBmb3IgaGVhZGxlc3MgcGF0aFxuICAgICAgLy8gKG1pcnJvcnMgdXNlTWVyZ2VkVG9vbHMudHMgZmlsdGVyaW5nIGZvciBSRVBML2ludGVyYWN0aXZlIHBhdGgpXG4gICAgICBpZiAoXG4gICAgICAgIGZlYXR1cmUoJ0NPT1JESU5BVE9SX01PREUnKSAmJlxuICAgICAgICBpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9DT09SRElOQVRPUl9NT0RFKVxuICAgICAgKSB7XG4gICAgICAgIGNvbnN0IHsgYXBwbHlDb29yZGluYXRvclRvb2xGaWx0ZXIgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICAgICAnLi91dGlscy90b29sUG9vbC5qcydcbiAgICAgICAgKVxuICAgICAgICB0b29scyA9IGFwcGx5Q29vcmRpbmF0b3JUb29sRmlsdGVyKHRvb2xzKVxuICAgICAgfVxuXG4gICAgICBwcm9maWxlQ2hlY2twb2ludCgnYWN0aW9uX3Rvb2xzX2xvYWRlZCcpXG5cbiAgICAgIGxldCBqc29uU2NoZW1hOiBUb29sSW5wdXRKU09OU2NoZW1hIHwgdW5kZWZpbmVkXG4gICAgICBpZiAoXG4gICAgICAgIGlzU3ludGhldGljT3V0cHV0VG9vbEVuYWJsZWQoeyBpc05vbkludGVyYWN0aXZlU2Vzc2lvbiB9KSAmJlxuICAgICAgICBvcHRpb25zLmpzb25TY2hlbWFcbiAgICAgICkge1xuICAgICAgICBqc29uU2NoZW1hID0ganNvblBhcnNlKG9wdGlvbnMuanNvblNjaGVtYSkgYXMgVG9vbElucHV0SlNPTlNjaGVtYVxuICAgICAgfVxuXG4gICAgICBpZiAoanNvblNjaGVtYSkge1xuICAgICAgICBjb25zdCBzeW50aGV0aWNPdXRwdXRSZXN1bHQgPSBjcmVhdGVTeW50aGV0aWNPdXRwdXRUb29sKGpzb25TY2hlbWEpXG4gICAgICAgIGlmICgndG9vbCcgaW4gc3ludGhldGljT3V0cHV0UmVzdWx0KSB7XG4gICAgICAgICAgLy8gQWRkIFN5bnRoZXRpY091dHB1dFRvb2wgdG8gdGhlIHRvb2xzIGFycmF5IEFGVEVSIGdldFRvb2xzKCkgZmlsdGVyaW5nLlxuICAgICAgICAgIC8vIFRoaXMgdG9vbCBpcyBleGNsdWRlZCBmcm9tIG5vcm1hbCBmaWx0ZXJpbmcgKHNlZSB0b29scy50cykgYmVjYXVzZSBpdCdzXG4gICAgICAgICAgLy8gYW4gaW1wbGVtZW50YXRpb24gZGV0YWlsIGZvciBzdHJ1Y3R1cmVkIG91dHB1dCwgbm90IGEgdXNlci1jb250cm9sbGVkIHRvb2wuXG4gICAgICAgICAgdG9vbHMgPSBbLi4udG9vbHMsIHN5bnRoZXRpY091dHB1dFJlc3VsdC50b29sXVxuXG4gICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3N0cnVjdHVyZWRfb3V0cHV0X2VuYWJsZWQnLCB7XG4gICAgICAgICAgICBzY2hlbWFfcHJvcGVydHlfY291bnQ6IE9iamVjdC5rZXlzKFxuICAgICAgICAgICAgICAoanNvblNjaGVtYS5wcm9wZXJ0aWVzIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+KSB8fCB7fSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgLmxlbmd0aCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgaGFzX3JlcXVpcmVkX2ZpZWxkczogQm9vbGVhbihcbiAgICAgICAgICAgICAganNvblNjaGVtYS5yZXF1aXJlZCxcbiAgICAgICAgICAgICkgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICB9KVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9zdHJ1Y3R1cmVkX291dHB1dF9mYWlsdXJlJywge1xuICAgICAgICAgICAgZXJyb3I6XG4gICAgICAgICAgICAgICdJbnZhbGlkIEpTT04gc2NoZW1hJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gSU1QT1JUQU5UOiBzZXR1cCgpIG11c3QgYmUgY2FsbGVkIGJlZm9yZSBhbnkgb3RoZXIgY29kZSB0aGF0IGRlcGVuZHMgb24gdGhlIGN3ZCBvciB3b3JrdHJlZSBzZXR1cFxuICAgICAgcHJvZmlsZUNoZWNrcG9pbnQoJ2FjdGlvbl9iZWZvcmVfc2V0dXAnKVxuICAgICAgbG9nRm9yRGVidWdnaW5nKCdbU1RBUlRVUF0gUnVubmluZyBzZXR1cCgpLi4uJylcbiAgICAgIGNvbnN0IHNldHVwU3RhcnQgPSBEYXRlLm5vdygpXG4gICAgICBjb25zdCB7IHNldHVwIH0gPSBhd2FpdCBpbXBvcnQoJy4vc2V0dXAuanMnKVxuICAgICAgY29uc3QgbWVzc2FnaW5nU29ja2V0UGF0aCA9IGZlYXR1cmUoJ1VEU19JTkJPWCcpXG4gICAgICAgID8gKG9wdGlvbnMgYXMgeyBtZXNzYWdpbmdTb2NrZXRQYXRoPzogc3RyaW5nIH0pLm1lc3NhZ2luZ1NvY2tldFBhdGhcbiAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgIC8vIFBhcmFsbGVsaXplIHNldHVwKCkgd2l0aCBjb21tYW5kcythZ2VudHMgbG9hZGluZy4gc2V0dXAoKSdzIH4yOG1zIGlzXG4gICAgICAvLyBtb3N0bHkgc3RhcnRVZHNNZXNzYWdpbmcgKHNvY2tldCBiaW5kLCB+MjBtcykg4oCUIG5vdCBkaXNrLWJvdW5kLCBzbyBpdFxuICAgICAgLy8gZG9lc24ndCBjb250ZW5kIHdpdGggZ2V0Q29tbWFuZHMnIGZpbGUgcmVhZHMuIEdhdGVkIG9uICF3b3JrdHJlZUVuYWJsZWRcbiAgICAgIC8vIHNpbmNlIC0td29ya3RyZWUgbWFrZXMgc2V0dXAoKSBwcm9jZXNzLmNoZGlyKCkgKHNldHVwLnRzOjIwMyksIGFuZFxuICAgICAgLy8gY29tbWFuZHMvYWdlbnRzIG5lZWQgdGhlIHBvc3QtY2hkaXIgY3dkLlxuICAgICAgY29uc3QgcHJlU2V0dXBDd2QgPSBnZXRDd2QoKVxuICAgICAgLy8gUmVnaXN0ZXIgYnVuZGxlZCBza2lsbHMvcGx1Z2lucyBiZWZvcmUga2lja2luZyBnZXRDb21tYW5kcygpIOKAlCB0aGV5J3JlXG4gICAgICAvLyBwdXJlIGluLW1lbW9yeSBhcnJheSBwdXNoZXMgKDwxbXMsIHplcm8gSS9PKSB0aGF0IGdldEJ1bmRsZWRTa2lsbHMoKVxuICAgICAgLy8gcmVhZHMgc3luY2hyb25vdXNseS4gUHJldmlvdXNseSByYW4gaW5zaWRlIHNldHVwKCkgYWZ0ZXIgfjIwbXMgb2ZcbiAgICAgIC8vIGF3YWl0IHBvaW50cywgc28gdGhlIHBhcmFsbGVsIGdldENvbW1hbmRzKCkgbWVtb2l6ZWQgYW4gZW1wdHkgbGlzdC5cbiAgICAgIGlmIChwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9FTlRSWVBPSU5UICE9PSAnbG9jYWwtYWdlbnQnKSB7XG4gICAgICAgIGluaXRCdWlsdGluUGx1Z2lucygpXG4gICAgICAgIGluaXRCdW5kbGVkU2tpbGxzKClcbiAgICAgIH1cbiAgICAgIGNvbnN0IHNldHVwUHJvbWlzZSA9IHNldHVwKFxuICAgICAgICBwcmVTZXR1cEN3ZCxcbiAgICAgICAgcGVybWlzc2lvbk1vZGUsXG4gICAgICAgIGFsbG93RGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnMsXG4gICAgICAgIHdvcmt0cmVlRW5hYmxlZCxcbiAgICAgICAgd29ya3RyZWVOYW1lLFxuICAgICAgICB0bXV4RW5hYmxlZCxcbiAgICAgICAgc2Vzc2lvbklkID8gdmFsaWRhdGVVdWlkKHNlc3Npb25JZCkgOiB1bmRlZmluZWQsXG4gICAgICAgIHdvcmt0cmVlUFJOdW1iZXIsXG4gICAgICAgIG1lc3NhZ2luZ1NvY2tldFBhdGgsXG4gICAgICApXG4gICAgICBjb25zdCBjb21tYW5kc1Byb21pc2UgPSB3b3JrdHJlZUVuYWJsZWQgPyBudWxsIDogZ2V0Q29tbWFuZHMocHJlU2V0dXBDd2QpXG4gICAgICBjb25zdCBhZ2VudERlZnNQcm9taXNlID0gd29ya3RyZWVFbmFibGVkXG4gICAgICAgID8gbnVsbFxuICAgICAgICA6IGdldEFnZW50RGVmaW5pdGlvbnNXaXRoT3ZlcnJpZGVzKHByZVNldHVwQ3dkKVxuICAgICAgLy8gU3VwcHJlc3MgdHJhbnNpZW50IHVuaGFuZGxlZFJlamVjdGlvbiBpZiB0aGVzZSByZWplY3QgZHVyaW5nIHRoZVxuICAgICAgLy8gfjI4bXMgc2V0dXBQcm9taXNlIGF3YWl0IGJlZm9yZSBQcm9taXNlLmFsbCBqb2lucyB0aGVtIGJlbG93LlxuICAgICAgY29tbWFuZHNQcm9taXNlPy5jYXRjaCgoKSA9PiB7fSlcbiAgICAgIGFnZW50RGVmc1Byb21pc2U/LmNhdGNoKCgpID0+IHt9KVxuICAgICAgYXdhaXQgc2V0dXBQcm9taXNlXG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgIGBbU1RBUlRVUF0gc2V0dXAoKSBjb21wbGV0ZWQgaW4gJHtEYXRlLm5vdygpIC0gc2V0dXBTdGFydH1tc2AsXG4gICAgICApXG4gICAgICBwcm9maWxlQ2hlY2twb2ludCgnYWN0aW9uX2FmdGVyX3NldHVwJylcblxuICAgICAgLy8gUmVwbGF5IHVzZXIgbWVzc2FnZXMgaW50byBzdHJlYW0tanNvbiBvbmx5IHdoZW4gdGhlIHNvY2tldCB3YXNcbiAgICAgIC8vIGV4cGxpY2l0bHkgcmVxdWVzdGVkLiBUaGUgYXV0by1nZW5lcmF0ZWQgc29ja2V0IGlzIHBhc3NpdmUg4oCUIGl0XG4gICAgICAvLyBsZXRzIHRvb2xzIGluamVjdCBpZiB0aGV5IHdhbnQgdG8sIGJ1dCB0dXJuaW5nIGl0IG9uIGJ5IGRlZmF1bHRcbiAgICAgIC8vIHNob3VsZG4ndCByZXNoYXBlIHN0cmVhbS1qc29uIGZvciBTREsgY29uc3VtZXJzIHdobyBuZXZlciB0b3VjaCBpdC5cbiAgICAgIC8vIENhbGxlcnMgd2hvIGluamVjdCBhbmQgYWxzbyB3YW50IHRob3NlIGluamVjdGlvbnMgdmlzaWJsZSBpbiB0aGVcbiAgICAgIC8vIHN0cmVhbSBwYXNzIC0tbWVzc2FnaW5nLXNvY2tldC1wYXRoIGV4cGxpY2l0bHkgKG9yIC0tcmVwbGF5LXVzZXItbWVzc2FnZXMpLlxuICAgICAgbGV0IGVmZmVjdGl2ZVJlcGxheVVzZXJNZXNzYWdlcyA9ICEhb3B0aW9ucy5yZXBsYXlVc2VyTWVzc2FnZXNcbiAgICAgIGlmIChmZWF0dXJlKCdVRFNfSU5CT1gnKSkge1xuICAgICAgICBpZiAoIWVmZmVjdGl2ZVJlcGxheVVzZXJNZXNzYWdlcyAmJiBvdXRwdXRGb3JtYXQgPT09ICdzdHJlYW0tanNvbicpIHtcbiAgICAgICAgICBlZmZlY3RpdmVSZXBsYXlVc2VyTWVzc2FnZXMgPSAhIShcbiAgICAgICAgICAgIG9wdGlvbnMgYXMgeyBtZXNzYWdpbmdTb2NrZXRQYXRoPzogc3RyaW5nIH1cbiAgICAgICAgICApLm1lc3NhZ2luZ1NvY2tldFBhdGhcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoZ2V0SXNOb25JbnRlcmFjdGl2ZVNlc3Npb24oKSkge1xuICAgICAgICAvLyBBcHBseSBmdWxsIG1lcmdlZCBzZXR0aW5ncyBlbnYgbm93IChpbmNsdWRpbmcgcHJvamVjdC1zY29wZWRcbiAgICAgICAgLy8gLmNsYXVkZS9zZXR0aW5ncy5qc29uIFBBVEgvR0lUX0RJUi9HSVRfV09SS19UUkVFKSBzbyBnaXRFeGUoKSBhbmRcbiAgICAgICAgLy8gdGhlIGdpdCBzcGF3biBiZWxvdyBzZWUgaXQuIFRydXN0IGlzIGltcGxpY2l0IGluIC1wIG1vZGU7IHRoZVxuICAgICAgICAvLyBkb2NzdHJpbmcgYXQgbWFuYWdlZEVudi50czo5Ni05NyBzYXlzIHRoaXMgYXBwbGllcyBcInBvdGVudGlhbGx5XG4gICAgICAgIC8vIGRhbmdlcm91cyBlbnZpcm9ubWVudCB2YXJpYWJsZXMgc3VjaCBhcyBMRF9QUkVMT0FELCBQQVRIXCIgZnJvbSBhbGxcbiAgICAgICAgLy8gc291cmNlcy4gVGhlIGxhdGVyIGNhbGwgaW4gdGhlIGlzTm9uSW50ZXJhY3RpdmVTZXNzaW9uIGJsb2NrIGJlbG93XG4gICAgICAgIC8vIGlzIGlkZW1wb3RlbnQgKE9iamVjdC5hc3NpZ24sIGNvbmZpZ3VyZUdsb2JhbEFnZW50cyBlamVjdHMgcHJpb3JcbiAgICAgICAgLy8gaW50ZXJjZXB0b3IpIGFuZCBwaWNrcyB1cCBhbnkgcGx1Z2luLWNvbnRyaWJ1dGVkIGVudiBhZnRlciBwbHVnaW5cbiAgICAgICAgLy8gaW5pdC4gUHJvamVjdCBzZXR0aW5ncyBhcmUgYWxyZWFkeSBsb2FkZWQgaGVyZTpcbiAgICAgICAgLy8gYXBwbHlTYWZlQ29uZmlnRW52aXJvbm1lbnRWYXJpYWJsZXMgaW4gaW5pdCgpIGNhbGxlZFxuICAgICAgICAvLyBnZXRTZXR0aW5nc19ERVBSRUNBVEVEIGF0IG1hbmFnZWRFbnYudHM6ODYgd2hpY2ggbWVyZ2VzIGFsbCBlbmFibGVkXG4gICAgICAgIC8vIHNvdXJjZXMgaW5jbHVkaW5nIHByb2plY3RTZXR0aW5ncy9sb2NhbFNldHRpbmdzLlxuICAgICAgICBhcHBseUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzKClcblxuICAgICAgICAvLyBTcGF3biBnaXQgc3RhdHVzL2xvZy9icmFuY2ggbm93IHNvIHRoZSBzdWJwcm9jZXNzIGV4ZWN1dGlvbiBvdmVybGFwc1xuICAgICAgICAvLyB3aXRoIHRoZSBnZXRDb21tYW5kcyBhd2FpdCBiZWxvdyBhbmQgc3RhcnREZWZlcnJlZFByZWZldGNoZXMuIEFmdGVyXG4gICAgICAgIC8vIHNldHVwKCkgc28gY3dkIGlzIGZpbmFsIChzZXR1cC50czoyNTQgbWF5IHByb2Nlc3MuY2hkaXIod29ya3RyZWVQYXRoKVxuICAgICAgICAvLyBmb3IgLS13b3JrdHJlZSkgYW5kIGFmdGVyIHRoZSBhcHBseUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzIGFib3ZlXG4gICAgICAgIC8vIHNvIFBBVEgvR0lUX0RJUi9HSVRfV09SS19UUkVFIGZyb20gYWxsIHNvdXJjZXMgKHRydXN0ZWQgKyBwcm9qZWN0KVxuICAgICAgICAvLyBhcmUgYXBwbGllZC4gZ2V0U3lzdGVtQ29udGV4dCBpcyBtZW1vaXplZDsgdGhlXG4gICAgICAgIC8vIHByZWZldGNoU3lzdGVtQ29udGV4dElmU2FmZSBjYWxsIGluIHN0YXJ0RGVmZXJyZWRQcmVmZXRjaGVzIGJlY29tZXNcbiAgICAgICAgLy8gYSBjYWNoZSBoaXQuIFRoZSBtaWNyb3Rhc2sgZnJvbSBhd2FpdCBnZXRJc0dpdCgpIGRyYWlucyBhdCB0aGVcbiAgICAgICAgLy8gZ2V0Q29tbWFuZHMgUHJvbWlzZS5hbGwgYXdhaXQgYmVsb3cuIFRydXN0IGlzIGltcGxpY2l0IGluIC1wIG1vZGVcbiAgICAgICAgLy8gKHNhbWUgZ2F0ZSBhcyBwcmVmZXRjaFN5c3RlbUNvbnRleHRJZlNhZmUpLlxuICAgICAgICB2b2lkIGdldFN5c3RlbUNvbnRleHQoKVxuICAgICAgICAvLyBLaWNrIGdldFVzZXJDb250ZXh0IG5vdyB0b28g4oCUIGl0cyBmaXJzdCBhd2FpdCAoZnMucmVhZEZpbGUgaW5cbiAgICAgICAgLy8gZ2V0TWVtb3J5RmlsZXMpIHlpZWxkcyBuYXR1cmFsbHksIHNvIHRoZSBDTEFVREUubWQgZGlyZWN0b3J5IHdhbGtcbiAgICAgICAgLy8gcnVucyBkdXJpbmcgdGhlIH4yODBtcyBvdmVybGFwIHdpbmRvdyBiZWZvcmUgdGhlIGNvbnRleHRcbiAgICAgICAgLy8gUHJvbWlzZS5hbGwgam9pbiBpbiBwcmludC50cy4gVGhlIHZvaWQgZ2V0VXNlckNvbnRleHQoKSBpblxuICAgICAgICAvLyBzdGFydERlZmVycmVkUHJlZmV0Y2hlcyBiZWNvbWVzIGEgbWVtb2l6ZSBjYWNoZS1oaXQuXG4gICAgICAgIHZvaWQgZ2V0VXNlckNvbnRleHQoKVxuICAgICAgICAvLyBLaWNrIGVuc3VyZU1vZGVsU3RyaW5nc0luaXRpYWxpemVkIG5vdyDigJQgZm9yIEJlZHJvY2sgdGhpcyB0cmlnZ2Vyc1xuICAgICAgICAvLyBhIDEwMC0yMDBtcyBwcm9maWxlIGZldGNoIHRoYXQgd2FzIGF3YWl0ZWQgc2VyaWFsbHkgYXRcbiAgICAgICAgLy8gcHJpbnQudHM6NzM5LiB1cGRhdGVCZWRyb2NrTW9kZWxTdHJpbmdzIGlzIHNlcXVlbnRpYWwoKS13cmFwcGVkIHNvXG4gICAgICAgIC8vIHRoZSBhd2FpdCBqb2lucyB0aGUgaW4tZmxpZ2h0IGZldGNoLiBOb24tQmVkcm9jayBpcyBhIHN5bmNcbiAgICAgICAgLy8gZWFybHktcmV0dXJuICh6ZXJvLWNvc3QpLlxuICAgICAgICB2b2lkIGVuc3VyZU1vZGVsU3RyaW5nc0luaXRpYWxpemVkKClcbiAgICAgIH1cblxuICAgICAgLy8gQXBwbHkgLS1uYW1lOiBjYWNoZS1vbmx5IHNvIG5vIG9ycGhhbiBmaWxlIGlzIGNyZWF0ZWQgYmVmb3JlIHRoZVxuICAgICAgLy8gc2Vzc2lvbiBJRCBpcyBmaW5hbGl6ZWQgYnkgLS1jb250aW51ZS8tLXJlc3VtZS4gbWF0ZXJpYWxpemVTZXNzaW9uRmlsZVxuICAgICAgLy8gcGVyc2lzdHMgaXQgb24gdGhlIGZpcnN0IHVzZXIgbWVzc2FnZTsgUkVQTCdzIHVzZVRlcm1pbmFsVGl0bGUgcmVhZHMgaXRcbiAgICAgIC8vIHZpYSBnZXRDdXJyZW50U2Vzc2lvblRpdGxlLlxuICAgICAgY29uc3Qgc2Vzc2lvbk5hbWVBcmcgPSBvcHRpb25zLm5hbWU/LnRyaW0oKVxuICAgICAgaWYgKHNlc3Npb25OYW1lQXJnKSB7XG4gICAgICAgIGNhY2hlU2Vzc2lvblRpdGxlKHNlc3Npb25OYW1lQXJnKVxuICAgICAgfVxuXG4gICAgICAvLyBBbnQgbW9kZWwgYWxpYXNlcyAoY2FweWJhcmEtZmFzdCBldGMuKSByZXNvbHZlIHZpYSB0aGVcbiAgICAgIC8vIHRlbmd1X2FudF9tb2RlbF9vdmVycmlkZSBHcm93dGhCb29rIGZsYWcuIF9DQUNIRURfTUFZX0JFX1NUQUxFIHJlYWRzXG4gICAgICAvLyBkaXNrIHN5bmNocm9ub3VzbHk7IGRpc2sgaXMgcG9wdWxhdGVkIGJ5IGEgZmlyZS1hbmQtZm9yZ2V0IHdyaXRlLiBPbiBhXG4gICAgICAvLyBjb2xkIGNhY2hlLCBwYXJzZVVzZXJTcGVjaWZpZWRNb2RlbCByZXR1cm5zIHRoZSB1bnJlc29sdmVkIGFsaWFzLCB0aGVcbiAgICAgIC8vIEFQSSA0MDRzLCBhbmQgLXAgZXhpdHMgYmVmb3JlIHRoZSBhc3luYyB3cml0ZSBsYW5kcyDigJQgY3Jhc2hsb29wIG9uXG4gICAgICAvLyBmcmVzaCBwb2RzLiBBd2FpdGluZyBpbml0IGhlcmUgcG9wdWxhdGVzIHRoZSBpbi1tZW1vcnkgcGF5bG9hZCBtYXAgdGhhdFxuICAgICAgLy8gX0NBQ0hFRF9NQVlfQkVfU1RBTEUgbm93IGNoZWNrcyBmaXJzdC4gR2F0ZWQgc28gdGhlIHdhcm0gcGF0aCBzdGF5c1xuICAgICAgLy8gbm9uLWJsb2NraW5nOlxuICAgICAgLy8gIC0gZXhwbGljaXQgbW9kZWwgdmlhIC0tbW9kZWwgb3IgQU5USFJPUElDX01PREVMIChib3RoIGZlZWQgYWxpYXMgcmVzb2x1dGlvbilcbiAgICAgIC8vICAtIG5vIGVudiBvdmVycmlkZSAod2hpY2ggc2hvcnQtY2lyY3VpdHMgX0NBQ0hFRF9NQVlfQkVfU1RBTEUgYmVmb3JlIGRpc2spXG4gICAgICAvLyAgLSBmbGFnIGFic2VudCBmcm9tIGRpc2sgKD09IG51bGwgYWxzbyBjYXRjaGVzIHByZS0jMjIyNzkgcG9pc29uZWQgbnVsbClcbiAgICAgIGNvbnN0IGV4cGxpY2l0TW9kZWwgPSBvcHRpb25zLm1vZGVsIHx8IHByb2Nlc3MuZW52LkFOVEhST1BJQ19NT0RFTFxuICAgICAgaWYgKFxuICAgICAgICBcImV4dGVybmFsXCIgPT09ICdhbnQnICYmXG4gICAgICAgIGV4cGxpY2l0TW9kZWwgJiZcbiAgICAgICAgZXhwbGljaXRNb2RlbCAhPT0gJ2RlZmF1bHQnICYmXG4gICAgICAgICFoYXNHcm93dGhCb29rRW52T3ZlcnJpZGUoJ3Rlbmd1X2FudF9tb2RlbF9vdmVycmlkZScpICYmXG4gICAgICAgIGdldEdsb2JhbENvbmZpZygpLmNhY2hlZEdyb3d0aEJvb2tGZWF0dXJlcz8uW1xuICAgICAgICAgICd0ZW5ndV9hbnRfbW9kZWxfb3ZlcnJpZGUnXG4gICAgICAgIF0gPT0gbnVsbFxuICAgICAgKSB7XG4gICAgICAgIGF3YWl0IGluaXRpYWxpemVHcm93dGhCb29rKClcbiAgICAgIH1cblxuICAgICAgLy8gU3BlY2lhbCBjYXNlIHRoZSBkZWZhdWx0IG1vZGVsIHdpdGggdGhlIG51bGwga2V5d29yZFxuICAgICAgLy8gTk9URTogTW9kZWwgcmVzb2x1dGlvbiBoYXBwZW5zIGFmdGVyIHNldHVwKCkgdG8gZW5zdXJlIHRydXN0IGlzIGVzdGFibGlzaGVkIGJlZm9yZSBBV1MgYXV0aFxuICAgICAgY29uc3QgdXNlclNwZWNpZmllZE1vZGVsID1cbiAgICAgICAgb3B0aW9ucy5tb2RlbCA9PT0gJ2RlZmF1bHQnID8gZ2V0RGVmYXVsdE1haW5Mb29wTW9kZWwoKSA6IG9wdGlvbnMubW9kZWxcbiAgICAgIGNvbnN0IHVzZXJTcGVjaWZpZWRGYWxsYmFja01vZGVsID1cbiAgICAgICAgZmFsbGJhY2tNb2RlbCA9PT0gJ2RlZmF1bHQnID8gZ2V0RGVmYXVsdE1haW5Mb29wTW9kZWwoKSA6IGZhbGxiYWNrTW9kZWxcblxuICAgICAgLy8gUmV1c2UgcHJlU2V0dXBDd2QgdW5sZXNzIHNldHVwKCkgY2hkaXInZCAod29ya3RyZWVFbmFibGVkKS4gU2F2ZXMgYVxuICAgICAgLy8gZ2V0Q3dkKCkgc3lzY2FsbCBpbiB0aGUgY29tbW9uIHBhdGguXG4gICAgICBjb25zdCBjdXJyZW50Q3dkID0gd29ya3RyZWVFbmFibGVkID8gZ2V0Q3dkKCkgOiBwcmVTZXR1cEN3ZFxuICAgICAgbG9nRm9yRGVidWdnaW5nKCdbU1RBUlRVUF0gTG9hZGluZyBjb21tYW5kcyBhbmQgYWdlbnRzLi4uJylcbiAgICAgIGNvbnN0IGNvbW1hbmRzU3RhcnQgPSBEYXRlLm5vdygpXG4gICAgICAvLyBKb2luIHRoZSBwcm9taXNlcyBraWNrZWQgYmVmb3JlIHNldHVwKCkgKG9yIHN0YXJ0IGZyZXNoIGlmXG4gICAgICAvLyB3b3JrdHJlZUVuYWJsZWQgZ2F0ZWQgdGhlIGVhcmx5IGtpY2spLiBCb3RoIG1lbW9pemVkIGJ5IGN3ZC5cbiAgICAgIGNvbnN0IFtjb21tYW5kcywgYWdlbnREZWZpbml0aW9uc1Jlc3VsdF0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAgIGNvbW1hbmRzUHJvbWlzZSA/PyBnZXRDb21tYW5kcyhjdXJyZW50Q3dkKSxcbiAgICAgICAgYWdlbnREZWZzUHJvbWlzZSA/PyBnZXRBZ2VudERlZmluaXRpb25zV2l0aE92ZXJyaWRlcyhjdXJyZW50Q3dkKSxcbiAgICAgIF0pXG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgIGBbU1RBUlRVUF0gQ29tbWFuZHMgYW5kIGFnZW50cyBsb2FkZWQgaW4gJHtEYXRlLm5vdygpIC0gY29tbWFuZHNTdGFydH1tc2AsXG4gICAgICApXG4gICAgICBwcm9maWxlQ2hlY2twb2ludCgnYWN0aW9uX2NvbW1hbmRzX2xvYWRlZCcpXG5cbiAgICAgIC8vIFBhcnNlIENMSSBhZ2VudHMgaWYgcHJvdmlkZWQgdmlhIC0tYWdlbnRzIGZsYWdcbiAgICAgIGxldCBjbGlBZ2VudHM6IHR5cGVvZiBhZ2VudERlZmluaXRpb25zUmVzdWx0LmFjdGl2ZUFnZW50cyA9IFtdXG4gICAgICBpZiAoYWdlbnRzSnNvbikge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHBhcnNlZEFnZW50cyA9IHNhZmVQYXJzZUpTT04oYWdlbnRzSnNvbilcbiAgICAgICAgICBpZiAocGFyc2VkQWdlbnRzKSB7XG4gICAgICAgICAgICBjbGlBZ2VudHMgPSBwYXJzZUFnZW50c0Zyb21Kc29uKHBhcnNlZEFnZW50cywgJ2ZsYWdTZXR0aW5ncycpXG4gICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGxvZ0Vycm9yKGVycm9yKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIE1lcmdlIENMSSBhZ2VudHMgd2l0aCBleGlzdGluZyBvbmVzXG4gICAgICBjb25zdCBhbGxBZ2VudHMgPSBbLi4uYWdlbnREZWZpbml0aW9uc1Jlc3VsdC5hbGxBZ2VudHMsIC4uLmNsaUFnZW50c11cbiAgICAgIGNvbnN0IGFnZW50RGVmaW5pdGlvbnMgPSB7XG4gICAgICAgIC4uLmFnZW50RGVmaW5pdGlvbnNSZXN1bHQsXG4gICAgICAgIGFsbEFnZW50cyxcbiAgICAgICAgYWN0aXZlQWdlbnRzOiBnZXRBY3RpdmVBZ2VudHNGcm9tTGlzdChhbGxBZ2VudHMpLFxuICAgICAgfVxuXG4gICAgICAvLyBMb29rIHVwIG1haW4gdGhyZWFkIGFnZW50IGZyb20gQ0xJIGZsYWcgb3Igc2V0dGluZ3NcbiAgICAgIGNvbnN0IGFnZW50U2V0dGluZyA9IGFnZW50Q2xpID8/IGdldEluaXRpYWxTZXR0aW5ncygpLmFnZW50XG4gICAgICBsZXQgbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbjpcbiAgICAgICAgfCAodHlwZW9mIGFnZW50RGVmaW5pdGlvbnMuYWN0aXZlQWdlbnRzKVtudW1iZXJdXG4gICAgICAgIHwgdW5kZWZpbmVkXG4gICAgICBpZiAoYWdlbnRTZXR0aW5nKSB7XG4gICAgICAgIG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24gPSBhZ2VudERlZmluaXRpb25zLmFjdGl2ZUFnZW50cy5maW5kKFxuICAgICAgICAgIGFnZW50ID0+IGFnZW50LmFnZW50VHlwZSA9PT0gYWdlbnRTZXR0aW5nLFxuICAgICAgICApXG4gICAgICAgIGlmICghbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbikge1xuICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgIGBXYXJuaW5nOiBhZ2VudCBcIiR7YWdlbnRTZXR0aW5nfVwiIG5vdCBmb3VuZC4gYCArXG4gICAgICAgICAgICAgIGBBdmFpbGFibGUgYWdlbnRzOiAke2FnZW50RGVmaW5pdGlvbnMuYWN0aXZlQWdlbnRzLm1hcChhID0+IGEuYWdlbnRUeXBlKS5qb2luKCcsICcpfS4gYCArXG4gICAgICAgICAgICAgIGBVc2luZyBkZWZhdWx0IGJlaGF2aW9yLmAsXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIFN0b3JlIHRoZSBtYWluIHRocmVhZCBhZ2VudCB0eXBlIGluIGJvb3RzdHJhcCBzdGF0ZSBzbyBob29rcyBjYW4gYWNjZXNzIGl0XG4gICAgICBzZXRNYWluVGhyZWFkQWdlbnRUeXBlKG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24/LmFnZW50VHlwZSlcblxuICAgICAgLy8gTG9nIGFnZW50IGZsYWcgdXNhZ2Ug4oCUIG9ubHkgbG9nIGFnZW50IG5hbWUgZm9yIGJ1aWx0LWluIGFnZW50cyB0byBhdm9pZCBsZWFraW5nIGN1c3RvbSBhZ2VudCBuYW1lc1xuICAgICAgaWYgKG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24pIHtcbiAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2FnZW50X2ZsYWcnLCB7XG4gICAgICAgICAgYWdlbnRUeXBlOiBpc0J1aWx0SW5BZ2VudChtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uKVxuICAgICAgICAgICAgPyAobWFpblRocmVhZEFnZW50RGVmaW5pdGlvbi5hZ2VudFR5cGUgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUylcbiAgICAgICAgICAgIDogKCdjdXN0b20nIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMpLFxuICAgICAgICAgIC4uLihhZ2VudENsaSAmJiB7XG4gICAgICAgICAgICBzb3VyY2U6XG4gICAgICAgICAgICAgICdjbGknIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgfSksXG4gICAgICAgIH0pXG4gICAgICB9XG5cbiAgICAgIC8vIFBlcnNpc3QgYWdlbnQgc2V0dGluZyB0byBzZXNzaW9uIHRyYW5zY3JpcHQgZm9yIHJlc3VtZSB2aWV3IGRpc3BsYXkgYW5kIHJlc3RvcmF0aW9uXG4gICAgICBpZiAobWFpblRocmVhZEFnZW50RGVmaW5pdGlvbj8uYWdlbnRUeXBlKSB7XG4gICAgICAgIHNhdmVBZ2VudFNldHRpbmcobWFpblRocmVhZEFnZW50RGVmaW5pdGlvbi5hZ2VudFR5cGUpXG4gICAgICB9XG5cbiAgICAgIC8vIEFwcGx5IHRoZSBhZ2VudCdzIHN5c3RlbSBwcm9tcHQgZm9yIG5vbi1pbnRlcmFjdGl2ZSBzZXNzaW9uc1xuICAgICAgLy8gKGludGVyYWN0aXZlIG1vZGUgdXNlcyBidWlsZEVmZmVjdGl2ZVN5c3RlbVByb21wdCBpbnN0ZWFkKVxuICAgICAgaWYgKFxuICAgICAgICBpc05vbkludGVyYWN0aXZlU2Vzc2lvbiAmJlxuICAgICAgICBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uICYmXG4gICAgICAgICFzeXN0ZW1Qcm9tcHQgJiZcbiAgICAgICAgIWlzQnVpbHRJbkFnZW50KG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24pXG4gICAgICApIHtcbiAgICAgICAgY29uc3QgYWdlbnRTeXN0ZW1Qcm9tcHQgPSBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uLmdldFN5c3RlbVByb21wdCgpXG4gICAgICAgIGlmIChhZ2VudFN5c3RlbVByb21wdCkge1xuICAgICAgICAgIHN5c3RlbVByb21wdCA9IGFnZW50U3lzdGVtUHJvbXB0XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gaW5pdGlhbFByb21wdCBnb2VzIGZpcnN0IHNvIGl0cyBzbGFzaCBjb21tYW5kIChpZiBhbnkpIGlzIHByb2Nlc3NlZDtcbiAgICAgIC8vIHVzZXItcHJvdmlkZWQgdGV4dCBiZWNvbWVzIHRyYWlsaW5nIGNvbnRleHQuXG4gICAgICAvLyBPbmx5IGNvbmNhdGVuYXRlIHdoZW4gaW5wdXRQcm9tcHQgaXMgYSBzdHJpbmcuIFdoZW4gaXQncyBhblxuICAgICAgLy8gQXN5bmNJdGVyYWJsZSAoU0RLIHN0cmVhbS1qc29uIG1vZGUpLCB0ZW1wbGF0ZSBpbnRlcnBvbGF0aW9uIHdvdWxkXG4gICAgICAvLyBjYWxsIC50b1N0cmluZygpIHByb2R1Y2luZyBcIltvYmplY3QgT2JqZWN0XVwiLiBUaGUgQXN5bmNJdGVyYWJsZSBjYXNlXG4gICAgICAvLyBpcyBoYW5kbGVkIGluIHByaW50LnRzIHZpYSBzdHJ1Y3R1cmVkSU8ucHJlcGVuZFVzZXJNZXNzYWdlKCkuXG4gICAgICBpZiAobWFpblRocmVhZEFnZW50RGVmaW5pdGlvbj8uaW5pdGlhbFByb21wdCkge1xuICAgICAgICBpZiAodHlwZW9mIGlucHV0UHJvbXB0ID09PSAnc3RyaW5nJykge1xuICAgICAgICAgIGlucHV0UHJvbXB0ID0gaW5wdXRQcm9tcHRcbiAgICAgICAgICAgID8gYCR7bWFpblRocmVhZEFnZW50RGVmaW5pdGlvbi5pbml0aWFsUHJvbXB0fVxcblxcbiR7aW5wdXRQcm9tcHR9YFxuICAgICAgICAgICAgOiBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uLmluaXRpYWxQcm9tcHRcbiAgICAgICAgfSBlbHNlIGlmICghaW5wdXRQcm9tcHQpIHtcbiAgICAgICAgICBpbnB1dFByb21wdCA9IG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24uaW5pdGlhbFByb21wdFxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIENvbXB1dGUgZWZmZWN0aXZlIG1vZGVsIGVhcmx5IHNvIGhvb2tzIGNhbiBydW4gaW4gcGFyYWxsZWwgd2l0aCBNQ1BcbiAgICAgIC8vIElmIHVzZXIgZGlkbid0IHNwZWNpZnkgYSBtb2RlbCBidXQgYWdlbnQgaGFzIG9uZSwgdXNlIHRoZSBhZ2VudCdzIG1vZGVsXG4gICAgICBsZXQgZWZmZWN0aXZlTW9kZWwgPSB1c2VyU3BlY2lmaWVkTW9kZWxcbiAgICAgIGlmIChcbiAgICAgICAgIWVmZmVjdGl2ZU1vZGVsICYmXG4gICAgICAgIG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24/Lm1vZGVsICYmXG4gICAgICAgIG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24ubW9kZWwgIT09ICdpbmhlcml0J1xuICAgICAgKSB7XG4gICAgICAgIGVmZmVjdGl2ZU1vZGVsID0gcGFyc2VVc2VyU3BlY2lmaWVkTW9kZWwoXG4gICAgICAgICAgbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbi5tb2RlbCxcbiAgICAgICAgKVxuICAgICAgfVxuXG4gICAgICBzZXRNYWluTG9vcE1vZGVsT3ZlcnJpZGUoZWZmZWN0aXZlTW9kZWwpXG5cbiAgICAgIC8vIENvbXB1dGUgcmVzb2x2ZWQgbW9kZWwgZm9yIGhvb2tzICh1c2UgdXNlci1zcGVjaWZpZWQgbW9kZWwgYXQgbGF1bmNoKVxuICAgICAgc2V0SW5pdGlhbE1haW5Mb29wTW9kZWwoZ2V0VXNlclNwZWNpZmllZE1vZGVsU2V0dGluZygpIHx8IG51bGwpXG4gICAgICBjb25zdCBpbml0aWFsTWFpbkxvb3BNb2RlbCA9IGdldEluaXRpYWxNYWluTG9vcE1vZGVsKClcbiAgICAgIGNvbnN0IHJlc29sdmVkSW5pdGlhbE1vZGVsID0gcGFyc2VVc2VyU3BlY2lmaWVkTW9kZWwoXG4gICAgICAgIGluaXRpYWxNYWluTG9vcE1vZGVsID8/IGdldERlZmF1bHRNYWluTG9vcE1vZGVsKCksXG4gICAgICApXG5cbiAgICAgIGxldCBhZHZpc29yTW9kZWw6IHN0cmluZyB8IHVuZGVmaW5lZFxuICAgICAgaWYgKGlzQWR2aXNvckVuYWJsZWQoKSkge1xuICAgICAgICBjb25zdCBhZHZpc29yT3B0aW9uID0gY2FuVXNlckNvbmZpZ3VyZUFkdmlzb3IoKVxuICAgICAgICAgID8gKG9wdGlvbnMgYXMgeyBhZHZpc29yPzogc3RyaW5nIH0pLmFkdmlzb3JcbiAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICBpZiAoYWR2aXNvck9wdGlvbikge1xuICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgW0Fkdmlzb3JUb29sXSAtLWFkdmlzb3IgJHthZHZpc29yT3B0aW9ufWApXG4gICAgICAgICAgaWYgKCFtb2RlbFN1cHBvcnRzQWR2aXNvcihyZXNvbHZlZEluaXRpYWxNb2RlbCkpIHtcbiAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgICBjaGFsay5yZWQoXG4gICAgICAgICAgICAgICAgYEVycm9yOiBUaGUgbW9kZWwgXCIke3Jlc29sdmVkSW5pdGlhbE1vZGVsfVwiIGRvZXMgbm90IHN1cHBvcnQgdGhlIGFkdmlzb3IgdG9vbC5cXG5gLFxuICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgICAgfVxuICAgICAgICAgIGNvbnN0IG5vcm1hbGl6ZWRBZHZpc29yTW9kZWwgPSBub3JtYWxpemVNb2RlbFN0cmluZ0ZvckFQSShcbiAgICAgICAgICAgIHBhcnNlVXNlclNwZWNpZmllZE1vZGVsKGFkdmlzb3JPcHRpb24pLFxuICAgICAgICAgIClcbiAgICAgICAgICBpZiAoIWlzVmFsaWRBZHZpc29yTW9kZWwobm9ybWFsaXplZEFkdmlzb3JNb2RlbCkpIHtcbiAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgICBjaGFsay5yZWQoXG4gICAgICAgICAgICAgICAgYEVycm9yOiBUaGUgbW9kZWwgXCIke2Fkdmlzb3JPcHRpb259XCIgY2Fubm90IGJlIHVzZWQgYXMgYW4gYWR2aXNvci5cXG5gLFxuICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGFkdmlzb3JNb2RlbCA9IGNhblVzZXJDb25maWd1cmVBZHZpc29yKClcbiAgICAgICAgICA/IChhZHZpc29yT3B0aW9uID8/IGdldEluaXRpYWxBZHZpc29yU2V0dGluZygpKVxuICAgICAgICAgIDogYWR2aXNvck9wdGlvblxuICAgICAgICBpZiAoYWR2aXNvck1vZGVsKSB7XG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKGBbQWR2aXNvclRvb2xdIEFkdmlzb3IgbW9kZWw6ICR7YWR2aXNvck1vZGVsfWApXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gRm9yIHRtdXggdGVhbW1hdGVzIHdpdGggLS1hZ2VudC10eXBlLCBhcHBlbmQgdGhlIGN1c3RvbSBhZ2VudCdzIHByb21wdFxuICAgICAgaWYgKFxuICAgICAgICBpc0FnZW50U3dhcm1zRW5hYmxlZCgpICYmXG4gICAgICAgIHN0b3JlZFRlYW1tYXRlT3B0cz8uYWdlbnRJZCAmJlxuICAgICAgICBzdG9yZWRUZWFtbWF0ZU9wdHM/LmFnZW50TmFtZSAmJlxuICAgICAgICBzdG9yZWRUZWFtbWF0ZU9wdHM/LnRlYW1OYW1lICYmXG4gICAgICAgIHN0b3JlZFRlYW1tYXRlT3B0cz8uYWdlbnRUeXBlXG4gICAgICApIHtcbiAgICAgICAgLy8gTG9vayB1cCB0aGUgY3VzdG9tIGFnZW50IGRlZmluaXRpb25cbiAgICAgICAgY29uc3QgY3VzdG9tQWdlbnQgPSBhZ2VudERlZmluaXRpb25zLmFjdGl2ZUFnZW50cy5maW5kKFxuICAgICAgICAgIGEgPT4gYS5hZ2VudFR5cGUgPT09IHN0b3JlZFRlYW1tYXRlT3B0cy5hZ2VudFR5cGUsXG4gICAgICAgIClcbiAgICAgICAgaWYgKGN1c3RvbUFnZW50KSB7XG4gICAgICAgICAgLy8gR2V0IHRoZSBwcm9tcHQgLSBuZWVkIHRvIGhhbmRsZSBib3RoIGJ1aWx0LWluIGFuZCBjdXN0b20gYWdlbnRzXG4gICAgICAgICAgbGV0IGN1c3RvbVByb21wdDogc3RyaW5nIHwgdW5kZWZpbmVkXG4gICAgICAgICAgaWYgKGN1c3RvbUFnZW50LnNvdXJjZSA9PT0gJ2J1aWx0LWluJykge1xuICAgICAgICAgICAgLy8gQnVpbHQtaW4gYWdlbnRzIGhhdmUgZ2V0U3lzdGVtUHJvbXB0IHRoYXQgdGFrZXMgdG9vbFVzZUNvbnRleHRcbiAgICAgICAgICAgIC8vIFdlIGNhbid0IGFjY2VzcyBmdWxsIHRvb2xVc2VDb250ZXh0IGhlcmUsIHNvIHNraXAgZm9yIG5vd1xuICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgICBgW3RlYW1tYXRlXSBCdWlsdC1pbiBhZ2VudCAke3N0b3JlZFRlYW1tYXRlT3B0cy5hZ2VudFR5cGV9IC0gc2tpcHBpbmcgY3VzdG9tIHByb21wdCAobm90IHN1cHBvcnRlZClgLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBDdXN0b20gYWdlbnRzIGhhdmUgZ2V0U3lzdGVtUHJvbXB0IHRoYXQgdGFrZXMgbm8gYXJnc1xuICAgICAgICAgICAgY3VzdG9tUHJvbXB0ID0gY3VzdG9tQWdlbnQuZ2V0U3lzdGVtUHJvbXB0KClcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBMb2cgYWdlbnQgbWVtb3J5IGxvYWRlZCBldmVudCBmb3IgdG11eCB0ZWFtbWF0ZXNcbiAgICAgICAgICBpZiAoY3VzdG9tQWdlbnQubWVtb3J5KSB7XG4gICAgICAgICAgICBsb2dFdmVudCgndGVuZ3VfYWdlbnRfbWVtb3J5X2xvYWRlZCcsIHtcbiAgICAgICAgICAgICAgLi4uKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiYge1xuICAgICAgICAgICAgICAgIGFnZW50X3R5cGU6XG4gICAgICAgICAgICAgICAgICBjdXN0b21BZ2VudC5hZ2VudFR5cGUgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICAgICAgfSksXG4gICAgICAgICAgICAgIHNjb3BlOlxuICAgICAgICAgICAgICAgIGN1c3RvbUFnZW50Lm1lbW9yeSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgICBzb3VyY2U6XG4gICAgICAgICAgICAgICAgJ3RlYW1tYXRlJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBpZiAoY3VzdG9tUHJvbXB0KSB7XG4gICAgICAgICAgICBjb25zdCBjdXN0b21JbnN0cnVjdGlvbnMgPSBgXFxuIyBDdXN0b20gQWdlbnQgSW5zdHJ1Y3Rpb25zXFxuJHtjdXN0b21Qcm9tcHR9YFxuICAgICAgICAgICAgYXBwZW5kU3lzdGVtUHJvbXB0ID0gYXBwZW5kU3lzdGVtUHJvbXB0XG4gICAgICAgICAgICAgID8gYCR7YXBwZW5kU3lzdGVtUHJvbXB0fVxcblxcbiR7Y3VzdG9tSW5zdHJ1Y3Rpb25zfWBcbiAgICAgICAgICAgICAgOiBjdXN0b21JbnN0cnVjdGlvbnNcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYFt0ZWFtbWF0ZV0gQ3VzdG9tIGFnZW50ICR7c3RvcmVkVGVhbW1hdGVPcHRzLmFnZW50VHlwZX0gbm90IGZvdW5kIGluIGF2YWlsYWJsZSBhZ2VudHNgLFxuICAgICAgICAgIClcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBtYXliZUFjdGl2YXRlQnJpZWYob3B0aW9ucylcbiAgICAgIC8vIGRlZmF1bHRWaWV3OiAnY2hhdCcgaXMgYSBwZXJzaXN0ZWQgb3B0LWluIOKAlCBjaGVjayBlbnRpdGxlbWVudCBhbmQgc2V0XG4gICAgICAvLyB1c2VyTXNnT3B0SW4gc28gdGhlIHRvb2wgKyBwcm9tcHQgc2VjdGlvbiBhY3RpdmF0ZS4gSW50ZXJhY3RpdmUtb25seTpcbiAgICAgIC8vIGRlZmF1bHRWaWV3IGlzIGEgZGlzcGxheSBwcmVmZXJlbmNlOyBTREsgc2Vzc2lvbnMgaGF2ZSBubyBkaXNwbGF5LCBhbmRcbiAgICAgIC8vIHRoZSBhc3Npc3RhbnQgaW5zdGFsbGVyIHdyaXRlcyBkZWZhdWx0VmlldzonY2hhdCcgdG8gc2V0dGluZ3MubG9jYWwuanNvblxuICAgICAgLy8gd2hpY2ggd291bGQgb3RoZXJ3aXNlIGxlYWsgaW50byAtLXByaW50IHNlc3Npb25zIGluIHRoZSBzYW1lIGRpcmVjdG9yeS5cbiAgICAgIC8vIFJ1bnMgcmlnaHQgYWZ0ZXIgbWF5YmVBY3RpdmF0ZUJyaWVmKCkgc28gYWxsIHN0YXJ0dXAgb3B0LWluIHBhdGhzIGZpcmVcbiAgICAgIC8vIEJFRk9SRSBhbnkgaXNCcmllZkVuYWJsZWQoKSByZWFkIGJlbG93IChwcm9hY3RpdmUgcHJvbXB0J3NcbiAgICAgIC8vIGJyaWVmVmlzaWJpbGl0eSkuIEEgcGVyc2lzdGVkICdjaGF0JyBhZnRlciBhIEdCIGtpbGwtc3dpdGNoIGZhbGxzXG4gICAgICAvLyB0aHJvdWdoIChlbnRpdGxlbWVudCBmYWlscykuXG4gICAgICBpZiAoXG4gICAgICAgIChmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKSkgJiZcbiAgICAgICAgIWdldElzTm9uSW50ZXJhY3RpdmVTZXNzaW9uKCkgJiZcbiAgICAgICAgIWdldFVzZXJNc2dPcHRJbigpICYmXG4gICAgICAgIGdldEluaXRpYWxTZXR0aW5ncygpLmRlZmF1bHRWaWV3ID09PSAnY2hhdCdcbiAgICAgICkge1xuICAgICAgICAvKiBlc2xpbnQtZGlzYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG4gICAgICAgIGNvbnN0IHsgaXNCcmllZkVudGl0bGVkIH0gPVxuICAgICAgICAgIHJlcXVpcmUoJy4vdG9vbHMvQnJpZWZUb29sL0JyaWVmVG9vbC5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vdG9vbHMvQnJpZWZUb29sL0JyaWVmVG9vbC5qcycpXG4gICAgICAgIC8qIGVzbGludC1lbmFibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICAgICAgICBpZiAoaXNCcmllZkVudGl0bGVkKCkpIHtcbiAgICAgICAgICBzZXRVc2VyTXNnT3B0SW4odHJ1ZSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy8gQ29vcmRpbmF0b3IgbW9kZSBoYXMgaXRzIG93biBzeXN0ZW0gcHJvbXB0IGFuZCBmaWx0ZXJzIG91dCBTbGVlcCwgc29cbiAgICAgIC8vIHRoZSBnZW5lcmljIHByb2FjdGl2ZSBwcm9tcHQgd291bGQgdGVsbCBpdCB0byBjYWxsIGEgdG9vbCBpdCBjYW4ndFxuICAgICAgLy8gYWNjZXNzIGFuZCBjb25mbGljdCB3aXRoIGRlbGVnYXRpb24gaW5zdHJ1Y3Rpb25zLlxuICAgICAgaWYgKFxuICAgICAgICAoZmVhdHVyZSgnUFJPQUNUSVZFJykgfHwgZmVhdHVyZSgnS0FJUk9TJykpICYmXG4gICAgICAgICgob3B0aW9ucyBhcyB7IHByb2FjdGl2ZT86IGJvb2xlYW4gfSkucHJvYWN0aXZlIHx8XG4gICAgICAgICAgaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfUFJPQUNUSVZFKSkgJiZcbiAgICAgICAgIWNvb3JkaW5hdG9yTW9kZU1vZHVsZT8uaXNDb29yZGluYXRvck1vZGUoKVxuICAgICAgKSB7XG4gICAgICAgIC8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbiAgICAgICAgY29uc3QgYnJpZWZWaXNpYmlsaXR5ID1cbiAgICAgICAgICBmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKVxuICAgICAgICAgICAgPyAoXG4gICAgICAgICAgICAgICAgcmVxdWlyZSgnLi90b29scy9CcmllZlRvb2wvQnJpZWZUb29sLmpzJykgYXMgdHlwZW9mIGltcG9ydCgnLi90b29scy9CcmllZlRvb2wvQnJpZWZUb29sLmpzJylcbiAgICAgICAgICAgICAgKS5pc0JyaWVmRW5hYmxlZCgpXG4gICAgICAgICAgICAgID8gJ0NhbGwgU2VuZFVzZXJNZXNzYWdlIGF0IGNoZWNrcG9pbnRzIHRvIG1hcmsgd2hlcmUgdGhpbmdzIHN0YW5kLidcbiAgICAgICAgICAgICAgOiAnVGhlIHVzZXIgd2lsbCBzZWUgYW55IHRleHQgeW91IG91dHB1dC4nXG4gICAgICAgICAgICA6ICdUaGUgdXNlciB3aWxsIHNlZSBhbnkgdGV4dCB5b3Ugb3V0cHV0LidcbiAgICAgICAgLyogZXNsaW50LWVuYWJsZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVxdWlyZS1pbXBvcnRzICovXG4gICAgICAgIGNvbnN0IHByb2FjdGl2ZVByb21wdCA9IGBcXG4jIFByb2FjdGl2ZSBNb2RlXFxuXFxuWW91IGFyZSBpbiBwcm9hY3RpdmUgbW9kZS4gVGFrZSBpbml0aWF0aXZlIOKAlCBleHBsb3JlLCBhY3QsIGFuZCBtYWtlIHByb2dyZXNzIHdpdGhvdXQgd2FpdGluZyBmb3IgaW5zdHJ1Y3Rpb25zLlxcblxcblN0YXJ0IGJ5IGJyaWVmbHkgZ3JlZXRpbmcgdGhlIHVzZXIuXFxuXFxuWW91IHdpbGwgcmVjZWl2ZSBwZXJpb2RpYyA8dGljaz4gcHJvbXB0cy4gVGhlc2UgYXJlIGNoZWNrLWlucy4gRG8gd2hhdGV2ZXIgc2VlbXMgbW9zdCB1c2VmdWwsIG9yIGNhbGwgU2xlZXAgaWYgdGhlcmUncyBub3RoaW5nIHRvIGRvLiAke2JyaWVmVmlzaWJpbGl0eX1gXG4gICAgICAgIGFwcGVuZFN5c3RlbVByb21wdCA9IGFwcGVuZFN5c3RlbVByb21wdFxuICAgICAgICAgID8gYCR7YXBwZW5kU3lzdGVtUHJvbXB0fVxcblxcbiR7cHJvYWN0aXZlUHJvbXB0fWBcbiAgICAgICAgICA6IHByb2FjdGl2ZVByb21wdFxuICAgICAgfVxuXG4gICAgICBpZiAoZmVhdHVyZSgnS0FJUk9TJykgJiYga2Fpcm9zRW5hYmxlZCAmJiBhc3Npc3RhbnRNb2R1bGUpIHtcbiAgICAgICAgY29uc3QgYXNzaXN0YW50QWRkZW5kdW0gPVxuICAgICAgICAgIGFzc2lzdGFudE1vZHVsZS5nZXRBc3Npc3RhbnRTeXN0ZW1Qcm9tcHRBZGRlbmR1bSgpXG4gICAgICAgIGFwcGVuZFN5c3RlbVByb21wdCA9IGFwcGVuZFN5c3RlbVByb21wdFxuICAgICAgICAgID8gYCR7YXBwZW5kU3lzdGVtUHJvbXB0fVxcblxcbiR7YXNzaXN0YW50QWRkZW5kdW19YFxuICAgICAgICAgIDogYXNzaXN0YW50QWRkZW5kdW1cbiAgICAgIH1cblxuICAgICAgLy8gSW5rIHJvb3QgaXMgb25seSBuZWVkZWQgZm9yIGludGVyYWN0aXZlIHNlc3Npb25zIOKAlCBwYXRjaENvbnNvbGUgaW4gdGhlXG4gICAgICAvLyBJbmsgY29uc3RydWN0b3Igd291bGQgc3dhbGxvdyBjb25zb2xlIG91dHB1dCBpbiBoZWFkbGVzcyBtb2RlLlxuICAgICAgbGV0IHJvb3QhOiBSb290XG4gICAgICBsZXQgZ2V0RnBzTWV0cmljcyE6ICgpID0+IEZwc01ldHJpY3MgfCB1bmRlZmluZWRcbiAgICAgIGxldCBzdGF0cyE6IFN0YXRzU3RvcmVcblxuICAgICAgLy8gU2hvdyBzZXR1cCBzY3JlZW5zIGFmdGVyIGNvbW1hbmRzIGFyZSBsb2FkZWRcbiAgICAgIGlmICghaXNOb25JbnRlcmFjdGl2ZVNlc3Npb24pIHtcbiAgICAgICAgY29uc3QgY3R4ID0gZ2V0UmVuZGVyQ29udGV4dChmYWxzZSlcbiAgICAgICAgZ2V0RnBzTWV0cmljcyA9IGN0eC5nZXRGcHNNZXRyaWNzXG4gICAgICAgIHN0YXRzID0gY3R4LnN0YXRzXG4gICAgICAgIC8vIEluc3RhbGwgYXNjaWljYXN0IHJlY29yZGVyIGJlZm9yZSBJbmsgbW91bnRzIChhbnQtb25seSwgb3B0LWluIHZpYSBDTEFVREVfQ09ERV9URVJNSU5BTF9SRUNPUkRJTkc9MSlcbiAgICAgICAgaWYgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcpIHtcbiAgICAgICAgICBpbnN0YWxsQXNjaWljYXN0UmVjb3JkZXIoKVxuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgeyBjcmVhdGVSb290IH0gPSBhd2FpdCBpbXBvcnQoJy4vaW5rLmpzJylcbiAgICAgICAgcm9vdCA9IGF3YWl0IGNyZWF0ZVJvb3QoY3R4LnJlbmRlck9wdGlvbnMpXG5cbiAgICAgICAgLy8gTG9nIHN0YXJ0dXAgdGltZSBub3csIGJlZm9yZSBhbnkgYmxvY2tpbmcgZGlhbG9nIHJlbmRlcnMuIExvZ2dpbmdcbiAgICAgICAgLy8gZnJvbSBSRVBMJ3MgZmlyc3QgcmVuZGVyICh0aGUgb2xkIGxvY2F0aW9uKSBpbmNsdWRlZCBob3dldmVyIGxvbmdcbiAgICAgICAgLy8gdGhlIHVzZXIgc2F0IG9uIHRydXN0L09BdXRoL29uYm9hcmRpbmcvcmVzdW1lLXBpY2tlciDigJQgcDk5IHdhcyB+NzBzXG4gICAgICAgIC8vIGRvbWluYXRlZCBieSBkaWFsb2ctd2FpdCB0aW1lLCBub3QgY29kZS1wYXRoIHN0YXJ0dXAuXG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV90aW1lcicsIHtcbiAgICAgICAgICBldmVudDpcbiAgICAgICAgICAgICdzdGFydHVwJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIGR1cmF0aW9uTXM6IE1hdGgucm91bmQocHJvY2Vzcy51cHRpbWUoKSAqIDEwMDApLFxuICAgICAgICB9KVxuXG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZygnW1NUQVJUVVBdIFJ1bm5pbmcgc2hvd1NldHVwU2NyZWVucygpLi4uJylcbiAgICAgICAgY29uc3Qgc2V0dXBTY3JlZW5zU3RhcnQgPSBEYXRlLm5vdygpXG4gICAgICAgIGNvbnN0IG9uYm9hcmRpbmdTaG93biA9IGF3YWl0IHNob3dTZXR1cFNjcmVlbnMoXG4gICAgICAgICAgcm9vdCxcbiAgICAgICAgICBwZXJtaXNzaW9uTW9kZSxcbiAgICAgICAgICBhbGxvd0Rhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zLFxuICAgICAgICAgIGNvbW1hbmRzLFxuICAgICAgICAgIGVuYWJsZUNsYXVkZUluQ2hyb21lLFxuICAgICAgICAgIGRldkNoYW5uZWxzLFxuICAgICAgICApXG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICBgW1NUQVJUVVBdIHNob3dTZXR1cFNjcmVlbnMoKSBjb21wbGV0ZWQgaW4gJHtEYXRlLm5vdygpIC0gc2V0dXBTY3JlZW5zU3RhcnR9bXNgLFxuICAgICAgICApXG5cbiAgICAgICAgLy8gTm93IHRoYXQgdHJ1c3QgaXMgZXN0YWJsaXNoZWQgYW5kIEdyb3d0aEJvb2sgaGFzIGF1dGggaGVhZGVycyxcbiAgICAgICAgLy8gcmVzb2x2ZSB0aGUgLS1yZW1vdGUtY29udHJvbCAvIC0tcmMgZW50aXRsZW1lbnQgZ2F0ZS5cbiAgICAgICAgaWYgKGZlYXR1cmUoJ0JSSURHRV9NT0RFJykgJiYgcmVtb3RlQ29udHJvbE9wdGlvbiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY29uc3QgeyBnZXRCcmlkZ2VEaXNhYmxlZFJlYXNvbiB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgJy4vYnJpZGdlL2JyaWRnZUVuYWJsZWQuanMnXG4gICAgICAgICAgKVxuICAgICAgICAgIGNvbnN0IGRpc2FibGVkUmVhc29uID0gYXdhaXQgZ2V0QnJpZGdlRGlzYWJsZWRSZWFzb24oKVxuICAgICAgICAgIHJlbW90ZUNvbnRyb2wgPSBkaXNhYmxlZFJlYXNvbiA9PT0gbnVsbFxuICAgICAgICAgIGlmIChkaXNhYmxlZFJlYXNvbikge1xuICAgICAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgICAgICAgIGNoYWxrLnllbGxvdyhgJHtkaXNhYmxlZFJlYXNvbn1cXG4tLXJjIGZsYWcgaWdub3JlZC5cXG5gKSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDaGVjayBmb3IgcGVuZGluZyBhZ2VudCBtZW1vcnkgc25hcHNob3QgdXBkYXRlcyAob25seSBmb3IgLS1hZ2VudCBtb2RlLCBhbnQtb25seSlcbiAgICAgICAgaWYgKFxuICAgICAgICAgIGZlYXR1cmUoJ0FHRU5UX01FTU9SWV9TTkFQU0hPVCcpICYmXG4gICAgICAgICAgbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbiAmJlxuICAgICAgICAgIGlzQ3VzdG9tQWdlbnQobWFpblRocmVhZEFnZW50RGVmaW5pdGlvbikgJiZcbiAgICAgICAgICBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uLm1lbW9yeSAmJlxuICAgICAgICAgIG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24ucGVuZGluZ1NuYXBzaG90VXBkYXRlXG4gICAgICAgICkge1xuICAgICAgICAgIGNvbnN0IGFnZW50RGVmID0gbWFpblRocmVhZEFnZW50RGVmaW5pdGlvblxuICAgICAgICAgIGNvbnN0IGNob2ljZSA9IGF3YWl0IGxhdW5jaFNuYXBzaG90VXBkYXRlRGlhbG9nKHJvb3QsIHtcbiAgICAgICAgICAgIGFnZW50VHlwZTogYWdlbnREZWYuYWdlbnRUeXBlLFxuICAgICAgICAgICAgc2NvcGU6IGFnZW50RGVmLm1lbW9yeSEsXG4gICAgICAgICAgICBzbmFwc2hvdFRpbWVzdGFtcDpcbiAgICAgICAgICAgICAgYWdlbnREZWYucGVuZGluZ1NuYXBzaG90VXBkYXRlIS5zbmFwc2hvdFRpbWVzdGFtcCxcbiAgICAgICAgICB9KVxuICAgICAgICAgIGlmIChjaG9pY2UgPT09ICdtZXJnZScpIHtcbiAgICAgICAgICAgIGNvbnN0IHsgYnVpbGRNZXJnZVByb21wdCB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgICAnLi9jb21wb25lbnRzL2FnZW50cy9TbmFwc2hvdFVwZGF0ZURpYWxvZy5qcydcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGNvbnN0IG1lcmdlUHJvbXB0ID0gYnVpbGRNZXJnZVByb21wdChcbiAgICAgICAgICAgICAgYWdlbnREZWYuYWdlbnRUeXBlLFxuICAgICAgICAgICAgICBhZ2VudERlZi5tZW1vcnkhLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgaW5wdXRQcm9tcHQgPSBpbnB1dFByb21wdFxuICAgICAgICAgICAgICA/IGAke21lcmdlUHJvbXB0fVxcblxcbiR7aW5wdXRQcm9tcHR9YFxuICAgICAgICAgICAgICA6IG1lcmdlUHJvbXB0XG4gICAgICAgICAgfVxuICAgICAgICAgIGFnZW50RGVmLnBlbmRpbmdTbmFwc2hvdFVwZGF0ZSA9IHVuZGVmaW5lZFxuICAgICAgICB9XG5cbiAgICAgICAgLy8gU2tpcCBleGVjdXRpbmcgL2xvZ2luIGlmIHdlIGp1c3QgY29tcGxldGVkIG9uYm9hcmRpbmcgZm9yIGl0XG4gICAgICAgIGlmIChvbmJvYXJkaW5nU2hvd24gJiYgcHJvbXB0Py50cmltKCkudG9Mb3dlckNhc2UoKSA9PT0gJy9sb2dpbicpIHtcbiAgICAgICAgICBwcm9tcHQgPSAnJ1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKG9uYm9hcmRpbmdTaG93bikge1xuICAgICAgICAgIC8vIFJlZnJlc2ggYXV0aC1kZXBlbmRlbnQgc2VydmljZXMgbm93IHRoYXQgdGhlIHVzZXIgaGFzIGxvZ2dlZCBpbiBkdXJpbmcgb25ib2FyZGluZy5cbiAgICAgICAgICAvLyBLZWVwIGluIHN5bmMgd2l0aCB0aGUgcG9zdC1sb2dpbiBsb2dpYyBpbiBzcmMvY29tbWFuZHMvbG9naW4udHN4XG4gICAgICAgICAgdm9pZCByZWZyZXNoUmVtb3RlTWFuYWdlZFNldHRpbmdzKClcbiAgICAgICAgICB2b2lkIHJlZnJlc2hQb2xpY3lMaW1pdHMoKVxuICAgICAgICAgIC8vIENsZWFyIHVzZXIgZGF0YSBjYWNoZSBCRUZPUkUgR3Jvd3RoQm9vayByZWZyZXNoIHNvIGl0IHBpY2tzIHVwIGZyZXNoIGNyZWRlbnRpYWxzXG4gICAgICAgICAgcmVzZXRVc2VyQ2FjaGUoKVxuICAgICAgICAgIC8vIFJlZnJlc2ggR3Jvd3RoQm9vayBhZnRlciBsb2dpbiB0byBnZXQgdXBkYXRlZCBmZWF0dXJlIGZsYWdzIChlLmcuLCBmb3IgY2xhdWRlLmFpIE1DUHMpXG4gICAgICAgICAgcmVmcmVzaEdyb3d0aEJvb2tBZnRlckF1dGhDaGFuZ2UoKVxuICAgICAgICAgIC8vIENsZWFyIGFueSBzdGFsZSB0cnVzdGVkIGRldmljZSB0b2tlbiB0aGVuIGVucm9sbCBmb3IgUmVtb3RlIENvbnRyb2wuXG4gICAgICAgICAgLy8gQm90aCBzZWxmLWdhdGUgb24gdGVuZ3Vfc2Vzc2lvbnNfZWxldmF0ZWRfYXV0aF9lbmZvcmNlbWVudCBpbnRlcm5hbGx5XG4gICAgICAgICAgLy8g4oCUIGVucm9sbFRydXN0ZWREZXZpY2UoKSB2aWEgY2hlY2tHYXRlX0NBQ0hFRF9PUl9CTE9DS0lORyAoYXdhaXRzXG4gICAgICAgICAgLy8gdGhlIEdyb3d0aEJvb2sgcmVpbml0IGFib3ZlKSwgY2xlYXJUcnVzdGVkRGV2aWNlVG9rZW4oKSB2aWEgdGhlXG4gICAgICAgICAgLy8gc3luYyBjYWNoZWQgY2hlY2sgKGFjY2VwdGFibGUgc2luY2UgY2xlYXIgaXMgaWRlbXBvdGVudCkuXG4gICAgICAgICAgdm9pZCBpbXBvcnQoJy4vYnJpZGdlL3RydXN0ZWREZXZpY2UuanMnKS50aGVuKG0gPT4ge1xuICAgICAgICAgICAgbS5jbGVhclRydXN0ZWREZXZpY2VUb2tlbigpXG4gICAgICAgICAgICByZXR1cm4gbS5lbnJvbGxUcnVzdGVkRGV2aWNlKClcbiAgICAgICAgICB9KVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gVmFsaWRhdGUgdGhhdCB0aGUgYWN0aXZlIHRva2VuJ3Mgb3JnIG1hdGNoZXMgZm9yY2VMb2dpbk9yZ1VVSUQgKGlmIHNldFxuICAgICAgICAvLyBpbiBtYW5hZ2VkIHNldHRpbmdzKS4gUnVucyBhZnRlciBvbmJvYXJkaW5nIHNvIG1hbmFnZWQgc2V0dGluZ3MgYW5kXG4gICAgICAgIC8vIGxvZ2luIHN0YXRlIGFyZSBmdWxseSBsb2FkZWQuXG4gICAgICAgIGNvbnN0IG9yZ1ZhbGlkYXRpb24gPSBhd2FpdCB2YWxpZGF0ZUZvcmNlTG9naW5PcmcoKVxuICAgICAgICBpZiAoIW9yZ1ZhbGlkYXRpb24udmFsaWQpIHtcbiAgICAgICAgICBhd2FpdCBleGl0V2l0aEVycm9yKHJvb3QsIG9yZ1ZhbGlkYXRpb24ubWVzc2FnZSlcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBJZiBncmFjZWZ1bFNodXRkb3duIHdhcyBpbml0aWF0ZWQgKGUuZy4sIHVzZXIgcmVqZWN0ZWQgdHJ1c3QgZGlhbG9nKSxcbiAgICAgIC8vIHByb2Nlc3MuZXhpdENvZGUgd2lsbCBiZSBzZXQuIFNraXAgYWxsIHN1YnNlcXVlbnQgb3BlcmF0aW9ucyB0aGF0IGNvdWxkXG4gICAgICAvLyB0cmlnZ2VyIGNvZGUgZXhlY3V0aW9uIGJlZm9yZSB0aGUgcHJvY2VzcyBleGl0cyAoZS5nLiB3ZSBkb24ndCB3YW50IGFwaUtleUhlbHBlclxuICAgICAgLy8gdG8gcnVuIGlmIHRydXN0IHdhcyBub3QgZXN0YWJsaXNoZWQpLlxuICAgICAgaWYgKHByb2Nlc3MuZXhpdENvZGUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgJ0dyYWNlZnVsIHNodXRkb3duIGluaXRpYXRlZCwgc2tpcHBpbmcgZnVydGhlciBpbml0aWFsaXphdGlvbicsXG4gICAgICAgIClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIEluaXRpYWxpemUgTFNQIG1hbmFnZXIgQUZURVIgdHJ1c3QgaXMgZXN0YWJsaXNoZWQgKG9yIGluIG5vbi1pbnRlcmFjdGl2ZSBtb2RlXG4gICAgICAvLyB3aGVyZSB0cnVzdCBpcyBpbXBsaWNpdCkuIFRoaXMgcHJldmVudHMgcGx1Z2luIExTUCBzZXJ2ZXJzIGZyb20gZXhlY3V0aW5nXG4gICAgICAvLyBjb2RlIGluIHVudHJ1c3RlZCBkaXJlY3RvcmllcyBiZWZvcmUgdXNlciBjb25zZW50LlxuICAgICAgLy8gTXVzdCBiZSBhZnRlciBpbmxpbmUgcGx1Z2lucyBhcmUgc2V0IChpZiBhbnkpIHNvIC0tcGx1Z2luLWRpciBMU1Agc2VydmVycyBhcmUgaW5jbHVkZWQuXG4gICAgICBpbml0aWFsaXplTHNwU2VydmVyTWFuYWdlcigpXG5cbiAgICAgIC8vIFNob3cgc2V0dGluZ3MgdmFsaWRhdGlvbiBlcnJvcnMgYWZ0ZXIgdHJ1c3QgaXMgZXN0YWJsaXNoZWRcbiAgICAgIC8vIE1DUCBjb25maWcgZXJyb3JzIGRvbid0IGJsb2NrIHNldHRpbmdzIGZyb20gbG9hZGluZywgc28gZXhjbHVkZSB0aGVtXG4gICAgICBpZiAoIWlzTm9uSW50ZXJhY3RpdmVTZXNzaW9uKSB7XG4gICAgICAgIGNvbnN0IHsgZXJyb3JzIH0gPSBnZXRTZXR0aW5nc1dpdGhFcnJvcnMoKVxuICAgICAgICBjb25zdCBub25NY3BFcnJvcnMgPSBlcnJvcnMuZmlsdGVyKGUgPT4gIWUubWNwRXJyb3JNZXRhZGF0YSlcbiAgICAgICAgaWYgKG5vbk1jcEVycm9ycy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgYXdhaXQgbGF1bmNoSW52YWxpZFNldHRpbmdzRGlhbG9nKHJvb3QsIHtcbiAgICAgICAgICAgIHNldHRpbmdzRXJyb3JzOiBub25NY3BFcnJvcnMsXG4gICAgICAgICAgICBvbkV4aXQ6ICgpID0+IGdyYWNlZnVsU2h1dGRvd25TeW5jKDEpLFxuICAgICAgICAgIH0pXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgcXVvdGEgc3RhdHVzLCBmYXN0IG1vZGUsIHBhc3NlcyBlbGlnaWJpbGl0eSwgYW5kIGJvb3RzdHJhcCBkYXRhXG4gICAgICAvLyBhZnRlciB0cnVzdCBpcyBlc3RhYmxpc2hlZC4gVGhlc2UgbWFrZSBBUEkgY2FsbHMgd2hpY2ggY291bGQgdHJpZ2dlclxuICAgICAgLy8gYXBpS2V5SGVscGVyIGV4ZWN1dGlvbi5cbiAgICAgIC8vIC0tYmFyZSAvIFNJTVBMRTogc2tpcCDigJQgdGhlc2UgYXJlIGNhY2hlLXdhcm1zIGZvciB0aGUgUkVQTCdzXG4gICAgICAvLyBmaXJzdC10dXJuIHJlc3BvbnNpdmVuZXNzIChxdW90YSwgcGFzc2VzLCBmYXN0TW9kZSwgYm9vdHN0cmFwIGRhdGEpLiBGYXN0XG4gICAgICAvLyBtb2RlIGRvZXNuJ3QgYXBwbHkgdG8gdGhlIEFnZW50IFNESyBhbnl3YXkgKHNlZSBnZXRGYXN0TW9kZVVuYXZhaWxhYmxlUmVhc29uKS5cbiAgICAgIGNvbnN0IGJnUmVmcmVzaFRocm90dGxlTXMgPSBnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRShcbiAgICAgICAgJ3Rlbmd1X2NpY2FkYV9uYXBfbXMnLFxuICAgICAgICAwLFxuICAgICAgKVxuICAgICAgY29uc3QgbGFzdFByZWZldGNoZWQgPSBnZXRHbG9iYWxDb25maWcoKS5zdGFydHVwUHJlZmV0Y2hlZEF0ID8/IDBcbiAgICAgIGNvbnN0IHNraXBTdGFydHVwUHJlZmV0Y2hlcyA9XG4gICAgICAgIGlzQmFyZU1vZGUoKSB8fFxuICAgICAgICAoYmdSZWZyZXNoVGhyb3R0bGVNcyA+IDAgJiZcbiAgICAgICAgICBEYXRlLm5vdygpIC0gbGFzdFByZWZldGNoZWQgPCBiZ1JlZnJlc2hUaHJvdHRsZU1zKVxuXG4gICAgICBpZiAoIXNraXBTdGFydHVwUHJlZmV0Y2hlcykge1xuICAgICAgICBjb25zdCBsYXN0UHJlZmV0Y2hlZEluZm8gPVxuICAgICAgICAgIGxhc3RQcmVmZXRjaGVkID4gMFxuICAgICAgICAgICAgPyBgIGxhc3QgcmFuICR7TWF0aC5yb3VuZCgoRGF0ZS5ub3coKSAtIGxhc3RQcmVmZXRjaGVkKSAvIDEwMDApfXMgYWdvYFxuICAgICAgICAgICAgOiAnJ1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgYFN0YXJ0aW5nIGJhY2tncm91bmQgc3RhcnR1cCBwcmVmZXRjaGVzJHtsYXN0UHJlZmV0Y2hlZEluZm99YCxcbiAgICAgICAgKVxuXG4gICAgICAgIGNoZWNrUXVvdGFTdGF0dXMoKS5jYXRjaChlcnJvciA9PiBsb2dFcnJvcihlcnJvcikpXG5cbiAgICAgICAgLy8gRmV0Y2ggYm9vdHN0cmFwIGRhdGEgZnJvbSB0aGUgc2VydmVyIGFuZCB1cGRhdGUgYWxsIGNhY2hlIHZhbHVlcy5cbiAgICAgICAgdm9pZCBmZXRjaEJvb3RzdHJhcERhdGEoKVxuXG4gICAgICAgIC8vIFRPRE86IENvbnNvbGlkYXRlIG90aGVyIHByZWZldGNoZXMgaW50byBhIHNpbmdsZSBib290c3RyYXAgcmVxdWVzdC5cbiAgICAgICAgdm9pZCBwcmVmZXRjaFBhc3Nlc0VsaWdpYmlsaXR5KClcbiAgICAgICAgaWYgKFxuICAgICAgICAgICFnZXRGZWF0dXJlVmFsdWVfQ0FDSEVEX01BWV9CRV9TVEFMRSgndGVuZ3VfbWlyYWN1bG9fdGhlX2JhcmQnLCBmYWxzZSlcbiAgICAgICAgKSB7XG4gICAgICAgICAgdm9pZCBwcmVmZXRjaEZhc3RNb2RlU3RhdHVzKClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBLaWxsIHN3aXRjaCBza2lwcyB0aGUgbmV0d29yayBjYWxsLCBub3Qgb3JnLXBvbGljeSBlbmZvcmNlbWVudC5cbiAgICAgICAgICAvLyBSZXNvbHZlIGZyb20gY2FjaGUgc28gb3JnU3RhdHVzIGRvZXNuJ3Qgc3RheSAncGVuZGluZycgKHdoaWNoXG4gICAgICAgICAgLy8gZ2V0RmFzdE1vZGVVbmF2YWlsYWJsZVJlYXNvbiB0cmVhdHMgYXMgcGVybWlzc2l2ZSkuXG4gICAgICAgICAgcmVzb2x2ZUZhc3RNb2RlU3RhdHVzRnJvbUNhY2hlKClcbiAgICAgICAgfVxuICAgICAgICBpZiAoYmdSZWZyZXNoVGhyb3R0bGVNcyA+IDApIHtcbiAgICAgICAgICBzYXZlR2xvYmFsQ29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAgICAgICAgIC4uLmN1cnJlbnQsXG4gICAgICAgICAgICBzdGFydHVwUHJlZmV0Y2hlZEF0OiBEYXRlLm5vdygpLFxuICAgICAgICAgIH0pKVxuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgYFNraXBwaW5nIHN0YXJ0dXAgcHJlZmV0Y2hlcywgbGFzdCByYW4gJHtNYXRoLnJvdW5kKChEYXRlLm5vdygpIC0gbGFzdFByZWZldGNoZWQpIC8gMTAwMCl9cyBhZ29gLFxuICAgICAgICApXG4gICAgICAgIC8vIFJlc29sdmUgZmFzdCBtb2RlIG9yZyBzdGF0dXMgZnJvbSBjYWNoZSAobm8gbmV0d29yaylcbiAgICAgICAgcmVzb2x2ZUZhc3RNb2RlU3RhdHVzRnJvbUNhY2hlKClcbiAgICAgIH1cblxuICAgICAgaWYgKCFpc05vbkludGVyYWN0aXZlU2Vzc2lvbikge1xuICAgICAgICB2b2lkIHJlZnJlc2hFeGFtcGxlQ29tbWFuZHMoKSAvLyBQcmUtZmV0Y2ggZXhhbXBsZSBjb21tYW5kcyAocnVucyBnaXQgbG9nLCBubyBBUEkgY2FsbClcbiAgICAgIH1cblxuICAgICAgLy8gUmVzb2x2ZSBNQ1AgY29uZmlncyAoc3RhcnRlZCBlYXJseSwgb3ZlcmxhcHMgd2l0aCBzZXR1cC90cnVzdCBkaWFsb2cgd29yaylcbiAgICAgIGNvbnN0IHsgc2VydmVyczogZXhpc3RpbmdNY3BDb25maWdzIH0gPSBhd2FpdCBtY3BDb25maWdQcm9taXNlXG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgIGBbU1RBUlRVUF0gTUNQIGNvbmZpZ3MgcmVzb2x2ZWQgaW4gJHttY3BDb25maWdSZXNvbHZlZE1zfW1zIChhd2FpdGVkIGF0ICske0RhdGUubm93KCkgLSBtY3BDb25maWdTdGFydH1tcylgLFxuICAgICAgKVxuICAgICAgLy8gQ0xJIGZsYWcgKC0tbWNwLWNvbmZpZykgc2hvdWxkIG92ZXJyaWRlIGZpbGUtYmFzZWQgY29uZmlncywgbWF0Y2hpbmcgc2V0dGluZ3MgcHJlY2VkZW5jZVxuICAgICAgY29uc3QgYWxsTWNwQ29uZmlncyA9IHsgLi4uZXhpc3RpbmdNY3BDb25maWdzLCAuLi5keW5hbWljTWNwQ29uZmlnIH1cblxuICAgICAgLy8gU2VwYXJhdGUgU0RLIGNvbmZpZ3MgZnJvbSByZWd1bGFyIE1DUCBjb25maWdzXG4gICAgICBjb25zdCBzZGtNY3BDb25maWdzOiBSZWNvcmQ8c3RyaW5nLCBNY3BTZGtTZXJ2ZXJDb25maWc+ID0ge31cbiAgICAgIGNvbnN0IHJlZ3VsYXJNY3BDb25maWdzOiBSZWNvcmQ8c3RyaW5nLCBTY29wZWRNY3BTZXJ2ZXJDb25maWc+ID0ge31cblxuICAgICAgZm9yIChjb25zdCBbbmFtZSwgY29uZmlnXSBvZiBPYmplY3QuZW50cmllcyhhbGxNY3BDb25maWdzKSkge1xuICAgICAgICBjb25zdCB0eXBlZENvbmZpZyA9IGNvbmZpZyBhcyBTY29wZWRNY3BTZXJ2ZXJDb25maWcgfCBNY3BTZGtTZXJ2ZXJDb25maWdcbiAgICAgICAgaWYgKHR5cGVkQ29uZmlnLnR5cGUgPT09ICdzZGsnKSB7XG4gICAgICAgICAgc2RrTWNwQ29uZmlnc1tuYW1lXSA9IHR5cGVkQ29uZmlnIGFzIE1jcFNka1NlcnZlckNvbmZpZ1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJlZ3VsYXJNY3BDb25maWdzW25hbWVdID0gdHlwZWRDb25maWcgYXMgU2NvcGVkTWNwU2VydmVyQ29uZmlnXG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgcHJvZmlsZUNoZWNrcG9pbnQoJ2FjdGlvbl9tY3BfY29uZmlnc19sb2FkZWQnKVxuXG4gICAgICAvLyBQcmVmZXRjaCBNQ1AgcmVzb3VyY2VzIGFmdGVyIHRydXN0IGRpYWxvZyAodGhpcyBpcyB3aGVyZSBleGVjdXRpb24gaGFwcGVucykuXG4gICAgICAvLyBJbnRlcmFjdGl2ZSBtb2RlIG9ubHk6IHByaW50IG1vZGUgZGVmZXJzIGNvbm5lY3RzIHVudGlsIGhlYWRsZXNzU3RvcmUgZXhpc3RzXG4gICAgICAvLyBhbmQgcHVzaGVzIHBlci1zZXJ2ZXIgKGJlbG93KSwgc28gVG9vbFNlYXJjaCdzIHBlbmRpbmctY2xpZW50IGhhbmRsaW5nIHdvcmtzXG4gICAgICAvLyBhbmQgb25lIHNsb3cgc2VydmVyIGRvZXNuJ3QgYmxvY2sgdGhlIGJhdGNoLlxuICAgICAgY29uc3QgbG9jYWxNY3BQcm9taXNlID0gaXNOb25JbnRlcmFjdGl2ZVNlc3Npb25cbiAgICAgICAgPyBQcm9taXNlLnJlc29sdmUoeyBjbGllbnRzOiBbXSwgdG9vbHM6IFtdLCBjb21tYW5kczogW10gfSlcbiAgICAgICAgOiBwcmVmZXRjaEFsbE1jcFJlc291cmNlcyhyZWd1bGFyTWNwQ29uZmlncylcbiAgICAgIGNvbnN0IGNsYXVkZWFpTWNwUHJvbWlzZSA9IGlzTm9uSW50ZXJhY3RpdmVTZXNzaW9uXG4gICAgICAgID8gUHJvbWlzZS5yZXNvbHZlKHsgY2xpZW50czogW10sIHRvb2xzOiBbXSwgY29tbWFuZHM6IFtdIH0pXG4gICAgICAgIDogY2xhdWRlYWlDb25maWdQcm9taXNlLnRoZW4oY29uZmlncyA9PlxuICAgICAgICAgICAgT2JqZWN0LmtleXMoY29uZmlncykubGVuZ3RoID4gMFxuICAgICAgICAgICAgICA/IHByZWZldGNoQWxsTWNwUmVzb3VyY2VzKGNvbmZpZ3MpXG4gICAgICAgICAgICAgIDogeyBjbGllbnRzOiBbXSwgdG9vbHM6IFtdLCBjb21tYW5kczogW10gfSxcbiAgICAgICAgICApXG4gICAgICAvLyBNZXJnZSB3aXRoIGRlZHVwIGJ5IG5hbWU6IGVhY2ggcHJlZmV0Y2hBbGxNY3BSZXNvdXJjZXMgY2FsbCBpbmRlcGVuZGVudGx5XG4gICAgICAvLyBhZGRzIGhlbHBlciB0b29scyAoTGlzdE1jcFJlc291cmNlc1Rvb2wsIFJlYWRNY3BSZXNvdXJjZVRvb2wpIHZpYVxuICAgICAgLy8gbG9jYWwgZGVkdXAgZmxhZ3MsIHNvIG1lcmdpbmcgdHdvIGNhbGxzIGNhbiB5aWVsZCBkdXBsaWNhdGVzLiBwcmludC50c1xuICAgICAgLy8gYWxyZWFkeSB1bmlxQnkncyB0aGUgZmluYWwgdG9vbCBwb29sLCBidXQgZGVkdXAgaGVyZSBrZWVwcyBhcHBTdGF0ZSBjbGVhbi5cbiAgICAgIGNvbnN0IG1jcFByb21pc2UgPSBQcm9taXNlLmFsbChbXG4gICAgICAgIGxvY2FsTWNwUHJvbWlzZSxcbiAgICAgICAgY2xhdWRlYWlNY3BQcm9taXNlLFxuICAgICAgXSkudGhlbigoW2xvY2FsLCBjbGF1ZGVhaV0pID0+ICh7XG4gICAgICAgIGNsaWVudHM6IFsuLi5sb2NhbC5jbGllbnRzLCAuLi5jbGF1ZGVhaS5jbGllbnRzXSxcbiAgICAgICAgdG9vbHM6IHVuaXFCeShbLi4ubG9jYWwudG9vbHMsIC4uLmNsYXVkZWFpLnRvb2xzXSwgJ25hbWUnKSxcbiAgICAgICAgY29tbWFuZHM6IHVuaXFCeShbLi4ubG9jYWwuY29tbWFuZHMsIC4uLmNsYXVkZWFpLmNvbW1hbmRzXSwgJ25hbWUnKSxcbiAgICAgIH0pKVxuXG4gICAgICAvLyBTdGFydCBob29rcyBlYXJseSBzbyB0aGV5IHJ1biBpbiBwYXJhbGxlbCB3aXRoIE1DUCBjb25uZWN0aW9ucy5cbiAgICAgIC8vIFNraXAgZm9yIGluaXRPbmx5L2luaXQvbWFpbnRlbmFuY2UgKGhhbmRsZWQgc2VwYXJhdGVseSksIG5vbi1pbnRlcmFjdGl2ZVxuICAgICAgLy8gKGhhbmRsZWQgdmlhIHNldHVwVHJpZ2dlciksIGFuZCByZXN1bWUvY29udGludWUgKGNvbnZlcnNhdGlvblJlY292ZXJ5LnRzXG4gICAgICAvLyBmaXJlcyAncmVzdW1lJyBpbnN0ZWFkIOKAlCB3aXRob3V0IHRoaXMgZ3VhcmQsIGhvb2tzIGZpcmUgVFdJQ0Ugb24gL3Jlc3VtZVxuICAgICAgLy8gYW5kIHRoZSBzZWNvbmQgc3lzdGVtTWVzc2FnZSBjbG9iYmVycyB0aGUgZmlyc3QuIGdoLTMwODI1KVxuICAgICAgY29uc3QgaG9va3NQcm9taXNlID1cbiAgICAgICAgaW5pdE9ubHkgfHxcbiAgICAgICAgaW5pdCB8fFxuICAgICAgICBtYWludGVuYW5jZSB8fFxuICAgICAgICBpc05vbkludGVyYWN0aXZlU2Vzc2lvbiB8fFxuICAgICAgICBvcHRpb25zLmNvbnRpbnVlIHx8XG4gICAgICAgIG9wdGlvbnMucmVzdW1lXG4gICAgICAgICAgPyBudWxsXG4gICAgICAgICAgOiBwcm9jZXNzU2Vzc2lvblN0YXJ0SG9va3MoJ3N0YXJ0dXAnLCB7XG4gICAgICAgICAgICAgIGFnZW50VHlwZTogbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbj8uYWdlbnRUeXBlLFxuICAgICAgICAgICAgICBtb2RlbDogcmVzb2x2ZWRJbml0aWFsTW9kZWwsXG4gICAgICAgICAgICB9KVxuXG4gICAgICAvLyBNQ1AgbmV2ZXIgYmxvY2tzIFJFUEwgcmVuZGVyIE9SIHR1cm4gMSBUVEZULiB1c2VNYW5hZ2VNQ1BDb25uZWN0aW9uc1xuICAgICAgLy8gcG9wdWxhdGVzIGFwcFN0YXRlLm1jcCBhc3luYyBhcyBzZXJ2ZXJzIGNvbm5lY3QgKGNvbm5lY3RUb1NlcnZlciBpc1xuICAgICAgLy8gbWVtb2l6ZWQg4oCUIHRoZSBwcmVmZXRjaCBjYWxscyBhYm92ZSBhbmQgdGhlIGhvb2sgY29udmVyZ2Ugb24gdGhlIHNhbWVcbiAgICAgIC8vIGNvbm5lY3Rpb25zKS4gZ2V0VG9vbFVzZUNvbnRleHQgcmVhZHMgc3RvcmUuZ2V0U3RhdGUoKSBmcmVzaCB2aWFcbiAgICAgIC8vIGNvbXB1dGVUb29scygpLCBzbyB0dXJuIDEgc2VlcyB3aGF0ZXZlcidzIGNvbm5lY3RlZCBieSBxdWVyeSB0aW1lLlxuICAgICAgLy8gU2xvdyBzZXJ2ZXJzIHBvcHVsYXRlIGZvciB0dXJuIDIrLiBNYXRjaGVzIGludGVyYWN0aXZlLW5vLXByb21wdFxuICAgICAgLy8gYmVoYXZpb3IuIFByaW50IG1vZGU6IHBlci1zZXJ2ZXIgcHVzaCBpbnRvIGhlYWRsZXNzU3RvcmUgKGJlbG93KS5cbiAgICAgIGNvbnN0IGhvb2tNZXNzYWdlczogQXdhaXRlZDxOb25OdWxsYWJsZTx0eXBlb2YgaG9va3NQcm9taXNlPj4gPSBbXVxuICAgICAgLy8gU3VwcHJlc3MgdHJhbnNpZW50IHVuaGFuZGxlZFJlamVjdGlvbiDigJQgdGhlIHByZWZldGNoIHdhcm1zIHRoZVxuICAgICAgLy8gbWVtb2l6ZWQgY29ubmVjdFRvU2VydmVyIGNhY2hlIGJ1dCBub2JvZHkgYXdhaXRzIGl0IGluIGludGVyYWN0aXZlLlxuICAgICAgbWNwUHJvbWlzZS5jYXRjaCgoKSA9PiB7fSlcblxuICAgICAgY29uc3QgbWNwQ2xpZW50czogQXdhaXRlZDx0eXBlb2YgbWNwUHJvbWlzZT5bJ2NsaWVudHMnXSA9IFtdXG4gICAgICBjb25zdCBtY3BUb29sczogQXdhaXRlZDx0eXBlb2YgbWNwUHJvbWlzZT5bJ3Rvb2xzJ10gPSBbXVxuICAgICAgY29uc3QgbWNwQ29tbWFuZHM6IEF3YWl0ZWQ8dHlwZW9mIG1jcFByb21pc2U+Wydjb21tYW5kcyddID0gW11cblxuICAgICAgbGV0IHRoaW5raW5nRW5hYmxlZCA9IHNob3VsZEVuYWJsZVRoaW5raW5nQnlEZWZhdWx0KClcbiAgICAgIGxldCB0aGlua2luZ0NvbmZpZzogVGhpbmtpbmdDb25maWcgPVxuICAgICAgICB0aGlua2luZ0VuYWJsZWQgIT09IGZhbHNlID8geyB0eXBlOiAnYWRhcHRpdmUnIH0gOiB7IHR5cGU6ICdkaXNhYmxlZCcgfVxuXG4gICAgICBpZiAob3B0aW9ucy50aGlua2luZyA9PT0gJ2FkYXB0aXZlJyB8fCBvcHRpb25zLnRoaW5raW5nID09PSAnZW5hYmxlZCcpIHtcbiAgICAgICAgdGhpbmtpbmdFbmFibGVkID0gdHJ1ZVxuICAgICAgICB0aGlua2luZ0NvbmZpZyA9IHsgdHlwZTogJ2FkYXB0aXZlJyB9XG4gICAgICB9IGVsc2UgaWYgKG9wdGlvbnMudGhpbmtpbmcgPT09ICdkaXNhYmxlZCcpIHtcbiAgICAgICAgdGhpbmtpbmdFbmFibGVkID0gZmFsc2VcbiAgICAgICAgdGhpbmtpbmdDb25maWcgPSB7IHR5cGU6ICdkaXNhYmxlZCcgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3QgbWF4VGhpbmtpbmdUb2tlbnMgPSBwcm9jZXNzLmVudi5NQVhfVEhJTktJTkdfVE9LRU5TXG4gICAgICAgICAgPyBwYXJzZUludChwcm9jZXNzLmVudi5NQVhfVEhJTktJTkdfVE9LRU5TLCAxMClcbiAgICAgICAgICA6IG9wdGlvbnMubWF4VGhpbmtpbmdUb2tlbnNcbiAgICAgICAgaWYgKG1heFRoaW5raW5nVG9rZW5zICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICBpZiAobWF4VGhpbmtpbmdUb2tlbnMgPiAwKSB7XG4gICAgICAgICAgICB0aGlua2luZ0VuYWJsZWQgPSB0cnVlXG4gICAgICAgICAgICB0aGlua2luZ0NvbmZpZyA9IHtcbiAgICAgICAgICAgICAgdHlwZTogJ2VuYWJsZWQnLFxuICAgICAgICAgICAgICBidWRnZXRUb2tlbnM6IG1heFRoaW5raW5nVG9rZW5zLFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH0gZWxzZSBpZiAobWF4VGhpbmtpbmdUb2tlbnMgPT09IDApIHtcbiAgICAgICAgICAgIHRoaW5raW5nRW5hYmxlZCA9IGZhbHNlXG4gICAgICAgICAgICB0aGlua2luZ0NvbmZpZyA9IHsgdHlwZTogJ2Rpc2FibGVkJyB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGxvZ0ZvckRpYWdub3N0aWNzTm9QSUkoJ2luZm8nLCAnc3RhcnRlZCcsIHtcbiAgICAgICAgdmVyc2lvbjogTUFDUk8uVkVSU0lPTixcbiAgICAgICAgaXNfbmF0aXZlX2JpbmFyeTogaXNJbkJ1bmRsZWRNb2RlKCksXG4gICAgICB9KVxuXG4gICAgICByZWdpc3RlckNsZWFudXAoYXN5bmMgKCkgPT4ge1xuICAgICAgICBsb2dGb3JEaWFnbm9zdGljc05vUElJKCdpbmZvJywgJ2V4aXRlZCcpXG4gICAgICB9KVxuXG4gICAgICB2b2lkIGxvZ1Rlbmd1SW5pdCh7XG4gICAgICAgIGhhc0luaXRpYWxQcm9tcHQ6IEJvb2xlYW4ocHJvbXB0KSxcbiAgICAgICAgaGFzU3RkaW46IEJvb2xlYW4oaW5wdXRQcm9tcHQpLFxuICAgICAgICB2ZXJib3NlLFxuICAgICAgICBkZWJ1ZyxcbiAgICAgICAgZGVidWdUb1N0ZGVycixcbiAgICAgICAgcHJpbnQ6IHByaW50ID8/IGZhbHNlLFxuICAgICAgICBvdXRwdXRGb3JtYXQ6IG91dHB1dEZvcm1hdCA/PyAndGV4dCcsXG4gICAgICAgIGlucHV0Rm9ybWF0OiBpbnB1dEZvcm1hdCA/PyAndGV4dCcsXG4gICAgICAgIG51bUFsbG93ZWRUb29sczogYWxsb3dlZFRvb2xzLmxlbmd0aCxcbiAgICAgICAgbnVtRGlzYWxsb3dlZFRvb2xzOiBkaXNhbGxvd2VkVG9vbHMubGVuZ3RoLFxuICAgICAgICBtY3BDbGllbnRDb3VudDogT2JqZWN0LmtleXMoYWxsTWNwQ29uZmlncykubGVuZ3RoLFxuICAgICAgICB3b3JrdHJlZUVuYWJsZWQsXG4gICAgICAgIHNraXBXZWJGZXRjaFByZWZsaWdodDogZ2V0SW5pdGlhbFNldHRpbmdzKCkuc2tpcFdlYkZldGNoUHJlZmxpZ2h0LFxuICAgICAgICBnaXRodWJBY3Rpb25JbnB1dHM6IHByb2Nlc3MuZW52LkdJVEhVQl9BQ1RJT05fSU5QVVRTLFxuICAgICAgICBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uc1Bhc3NlZDogZGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnMgPz8gZmFsc2UsXG4gICAgICAgIHBlcm1pc3Npb25Nb2RlLFxuICAgICAgICBtb2RlSXNCeXBhc3M6IHBlcm1pc3Npb25Nb2RlID09PSAnYnlwYXNzUGVybWlzc2lvbnMnLFxuICAgICAgICBhbGxvd0Rhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zUGFzc2VkOiBhbGxvd0Rhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zLFxuICAgICAgICBzeXN0ZW1Qcm9tcHRGbGFnOiBzeXN0ZW1Qcm9tcHRcbiAgICAgICAgICA/IG9wdGlvbnMuc3lzdGVtUHJvbXB0RmlsZVxuICAgICAgICAgICAgPyAnZmlsZSdcbiAgICAgICAgICAgIDogJ2ZsYWcnXG4gICAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgICAgIGFwcGVuZFN5c3RlbVByb21wdEZsYWc6IGFwcGVuZFN5c3RlbVByb21wdFxuICAgICAgICAgID8gb3B0aW9ucy5hcHBlbmRTeXN0ZW1Qcm9tcHRGaWxlXG4gICAgICAgICAgICA/ICdmaWxlJ1xuICAgICAgICAgICAgOiAnZmxhZydcbiAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICAgICAgdGhpbmtpbmdDb25maWcsXG4gICAgICAgIGFzc2lzdGFudEFjdGl2YXRpb25QYXRoOlxuICAgICAgICAgIGZlYXR1cmUoJ0tBSVJPUycpICYmIGthaXJvc0VuYWJsZWRcbiAgICAgICAgICAgID8gYXNzaXN0YW50TW9kdWxlPy5nZXRBc3Npc3RhbnRBY3RpdmF0aW9uUGF0aCgpXG4gICAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICAgIH0pXG5cbiAgICAgIC8vIExvZyBjb250ZXh0IG1ldHJpY3Mgb25jZSBhdCBpbml0aWFsaXphdGlvblxuICAgICAgdm9pZCBsb2dDb250ZXh0TWV0cmljcyhyZWd1bGFyTWNwQ29uZmlncywgdG9vbFBlcm1pc3Npb25Db250ZXh0KVxuXG4gICAgICB2b2lkIGxvZ1Blcm1pc3Npb25Db250ZXh0Rm9yQW50cyhudWxsLCAnaW5pdGlhbGl6YXRpb24nKVxuXG4gICAgICBsb2dNYW5hZ2VkU2V0dGluZ3MoKVxuXG4gICAgICAvLyBSZWdpc3RlciBQSUQgZmlsZSBmb3IgY29uY3VycmVudC1zZXNzaW9uIGRldGVjdGlvbiAofi8uY2xhdWRlL3Nlc3Npb25zLylcbiAgICAgIC8vIGFuZCBmaXJlIG11bHRpLWNsYXVkaW5nIHRlbGVtZXRyeS4gTGl2ZXMgaGVyZSAobm90IGluaXQudHMpIHNvIG9ubHkgdGhlXG4gICAgICAvLyBSRVBMIHBhdGggcmVnaXN0ZXJzIOKAlCBub3Qgc3ViY29tbWFuZHMgbGlrZSBgY2xhdWRlIGRvY3RvcmAuIENoYWluZWQ6XG4gICAgICAvLyBjb3VudCBtdXN0IHJ1biBhZnRlciByZWdpc3RlcidzIHdyaXRlIGNvbXBsZXRlcyBvciBpdCBtaXNzZXMgb3VyIG93biBmaWxlLlxuICAgICAgdm9pZCByZWdpc3RlclNlc3Npb24oKS50aGVuKHJlZ2lzdGVyZWQgPT4ge1xuICAgICAgICBpZiAoIXJlZ2lzdGVyZWQpIHJldHVyblxuICAgICAgICBpZiAoc2Vzc2lvbk5hbWVBcmcpIHtcbiAgICAgICAgICB2b2lkIHVwZGF0ZVNlc3Npb25OYW1lKHNlc3Npb25OYW1lQXJnKVxuICAgICAgICB9XG4gICAgICAgIHZvaWQgY291bnRDb25jdXJyZW50U2Vzc2lvbnMoKS50aGVuKGNvdW50ID0+IHtcbiAgICAgICAgICBpZiAoY291bnQgPj0gMikge1xuICAgICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2NvbmN1cnJlbnRfc2Vzc2lvbnMnLCB7IG51bV9zZXNzaW9uczogY291bnQgfSlcbiAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICB9KVxuXG4gICAgICAvLyBJbml0aWFsaXplIHZlcnNpb25lZCBwbHVnaW5zIHN5c3RlbSAodHJpZ2dlcnMgVjHihpJWMiBtaWdyYXRpb24gaWZcbiAgICAgIC8vIG5lZWRlZCkuIFRoZW4gcnVuIG9ycGhhbiBHQywgVEhFTiB3YXJtIHRoZSBHcmVwL0dsb2IgZXhjbHVzaW9uIGNhY2hlLlxuICAgICAgLy8gU2VxdWVuY2luZyBtYXR0ZXJzOiB0aGUgd2FybXVwIHNjYW5zIGRpc2sgZm9yIC5vcnBoYW5lZF9hdCBtYXJrZXJzLFxuICAgICAgLy8gc28gaXQgbXVzdCBzZWUgdGhlIEdDJ3MgUGFzcyAxIChyZW1vdmUgbWFya2VycyBmcm9tIHJlaW5zdGFsbGVkXG4gICAgICAvLyB2ZXJzaW9ucykgYW5kIFBhc3MgMiAoc3RhbXAgdW5tYXJrZWQgb3JwaGFucykgYWxyZWFkeSBhcHBsaWVkLiBUaGVcbiAgICAgIC8vIHdhcm0gYWxzbyBsYW5kcyBiZWZvcmUgYXV0b3VwZGF0ZSAoZmlyZXMgb24gZmlyc3Qgc3VibWl0IGluIFJFUEwpXG4gICAgICAvLyBjYW4gb3JwaGFuIHRoaXMgc2Vzc2lvbidzIGFjdGl2ZSB2ZXJzaW9uIHVuZGVybmVhdGggdXMuXG4gICAgICAvLyAtLWJhcmUgLyBTSU1QTEU6IHNraXAgcGx1Z2luIHZlcnNpb24gc3luYyArIG9ycGhhbiBjbGVhbnVwLiBUaGVzZVxuICAgICAgLy8gYXJlIGluc3RhbGwvdXBncmFkZSBib29ra2VlcGluZyB0aGF0IHNjcmlwdGVkIGNhbGxzIGRvbid0IG5lZWQg4oCUXG4gICAgICAvLyB0aGUgbmV4dCBpbnRlcmFjdGl2ZSBzZXNzaW9uIHdpbGwgcmVjb25jaWxlLiBUaGUgYXdhaXQgaGVyZSB3YXNcbiAgICAgIC8vIGJsb2NraW5nIC1wIG9uIGEgbWFya2V0cGxhY2Ugcm91bmQtdHJpcC5cbiAgICAgIGlmIChpc0JhcmVNb2RlKCkpIHtcbiAgICAgICAgLy8gc2tpcCDigJQgbm8tb3BcbiAgICAgIH0gZWxzZSBpZiAoaXNOb25JbnRlcmFjdGl2ZVNlc3Npb24pIHtcbiAgICAgICAgLy8gSW4gaGVhZGxlc3MgbW9kZSwgYXdhaXQgdG8gZW5zdXJlIHBsdWdpbiBzeW5jIGNvbXBsZXRlcyBiZWZvcmUgQ0xJIGV4aXRzXG4gICAgICAgIGF3YWl0IGluaXRpYWxpemVWZXJzaW9uZWRQbHVnaW5zKClcbiAgICAgICAgcHJvZmlsZUNoZWNrcG9pbnQoJ2FjdGlvbl9hZnRlcl9wbHVnaW5zX2luaXQnKVxuICAgICAgICB2b2lkIGNsZWFudXBPcnBoYW5lZFBsdWdpblZlcnNpb25zSW5CYWNrZ3JvdW5kKCkudGhlbigoKSA9PlxuICAgICAgICAgIGdldEdsb2JFeGNsdXNpb25zRm9yUGx1Z2luQ2FjaGUoKSxcbiAgICAgICAgKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gSW4gaW50ZXJhY3RpdmUgbW9kZSwgZmlyZS1hbmQtZm9yZ2V0IOKAlCB0aGlzIGlzIHB1cmVseSBib29ra2VlcGluZ1xuICAgICAgICAvLyB0aGF0IGRvZXNuJ3QgYWZmZWN0IHJ1bnRpbWUgYmVoYXZpb3Igb2YgdGhlIGN1cnJlbnQgc2Vzc2lvblxuICAgICAgICB2b2lkIGluaXRpYWxpemVWZXJzaW9uZWRQbHVnaW5zKCkudGhlbihhc3luYyAoKSA9PiB7XG4gICAgICAgICAgcHJvZmlsZUNoZWNrcG9pbnQoJ2FjdGlvbl9hZnRlcl9wbHVnaW5zX2luaXQnKVxuICAgICAgICAgIGF3YWl0IGNsZWFudXBPcnBoYW5lZFBsdWdpblZlcnNpb25zSW5CYWNrZ3JvdW5kKClcbiAgICAgICAgICB2b2lkIGdldEdsb2JFeGNsdXNpb25zRm9yUGx1Z2luQ2FjaGUoKVxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICBjb25zdCBzZXR1cFRyaWdnZXIgPVxuICAgICAgICBpbml0T25seSB8fCBpbml0ID8gJ2luaXQnIDogbWFpbnRlbmFuY2UgPyAnbWFpbnRlbmFuY2UnIDogbnVsbFxuICAgICAgaWYgKGluaXRPbmx5KSB7XG4gICAgICAgIGFwcGx5Q29uZmlnRW52aXJvbm1lbnRWYXJpYWJsZXMoKVxuICAgICAgICBhd2FpdCBwcm9jZXNzU2V0dXBIb29rcygnaW5pdCcsIHsgZm9yY2VTeW5jRXhlY3V0aW9uOiB0cnVlIH0pXG4gICAgICAgIGF3YWl0IHByb2Nlc3NTZXNzaW9uU3RhcnRIb29rcygnc3RhcnR1cCcsIHsgZm9yY2VTeW5jRXhlY3V0aW9uOiB0cnVlIH0pXG4gICAgICAgIGdyYWNlZnVsU2h1dGRvd25TeW5jKDApXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyAtLXByaW50IG1vZGVcbiAgICAgIGlmIChpc05vbkludGVyYWN0aXZlU2Vzc2lvbikge1xuICAgICAgICBpZiAob3V0cHV0Rm9ybWF0ID09PSAnc3RyZWFtLWpzb24nIHx8IG91dHB1dEZvcm1hdCA9PT0gJ2pzb24nKSB7XG4gICAgICAgICAgc2V0SGFzRm9ybWF0dGVkT3V0cHV0KHRydWUpXG4gICAgICAgIH1cblxuICAgICAgICAvLyBBcHBseSBmdWxsIGVudmlyb25tZW50IHZhcmlhYmxlcyBpbiBwcmludCBtb2RlIHNpbmNlIHRydXN0IGRpYWxvZyBpcyBieXBhc3NlZFxuICAgICAgICAvLyBUaGlzIGluY2x1ZGVzIHBvdGVudGlhbGx5IGRhbmdlcm91cyBlbnZpcm9ubWVudCB2YXJpYWJsZXMgZnJvbSB1bnRydXN0ZWQgc291cmNlc1xuICAgICAgICAvLyBidXQgcHJpbnQgbW9kZSBpcyBjb25zaWRlcmVkIHRydXN0ZWQgKGFzIGRvY3VtZW50ZWQgaW4gaGVscCB0ZXh0KVxuICAgICAgICBhcHBseUNvbmZpZ0Vudmlyb25tZW50VmFyaWFibGVzKClcblxuICAgICAgICAvLyBJbml0aWFsaXplIHRlbGVtZXRyeSBhZnRlciBlbnYgdmFycyBhcmUgYXBwbGllZCBzbyBPVEVMIGVuZHBvaW50IGVudiB2YXJzIGFuZFxuICAgICAgICAvLyBvdGVsSGVhZGVyc0hlbHBlciAod2hpY2ggcmVxdWlyZXMgdHJ1c3QgdG8gZXhlY3V0ZSkgYXJlIGF2YWlsYWJsZS5cbiAgICAgICAgaW5pdGlhbGl6ZVRlbGVtZXRyeUFmdGVyVHJ1c3QoKVxuXG4gICAgICAgIC8vIEtpY2sgU2Vzc2lvblN0YXJ0IGhvb2tzIG5vdyBzbyB0aGUgc3VicHJvY2VzcyBzcGF3biBvdmVybGFwcyB3aXRoXG4gICAgICAgIC8vIE1DUCBjb25uZWN0ICsgcGx1Z2luIGluaXQgKyBwcmludC50cyBpbXBvcnQgYmVsb3cuIGxvYWRJbml0aWFsTWVzc2FnZXNcbiAgICAgICAgLy8gam9pbnMgdGhpcyBhdCBwcmludC50czo0Mzk3LiBHdWFyZGVkIHNhbWUgYXMgbG9hZEluaXRpYWxNZXNzYWdlcyDigJRcbiAgICAgICAgLy8gY29udGludWUvcmVzdW1lL3RlbGVwb3J0IHBhdGhzIGRvbid0IGZpcmUgc3RhcnR1cCBob29rcyAob3IgZmlyZSB0aGVtXG4gICAgICAgIC8vIGNvbmRpdGlvbmFsbHkgaW5zaWRlIHRoZSByZXN1bWUgYnJhbmNoLCB3aGVyZSB0aGlzIHByb21pc2UgaXNcbiAgICAgICAgLy8gdW5kZWZpbmVkIGFuZCB0aGUgPz8gZmFsbGJhY2sgcnVucykuIEFsc28gc2tpcCB3aGVuIHNldHVwVHJpZ2dlciBpc1xuICAgICAgICAvLyBzZXQg4oCUIHRob3NlIHBhdGhzIHJ1biBzZXR1cCBob29rcyBmaXJzdCAocHJpbnQudHM6NTQ0KSwgYW5kIHNlc3Npb25cbiAgICAgICAgLy8gc3RhcnQgaG9va3MgbXVzdCB3YWl0IHVudGlsIHNldHVwIGNvbXBsZXRlcy5cbiAgICAgICAgY29uc3Qgc2Vzc2lvblN0YXJ0SG9va3NQcm9taXNlID1cbiAgICAgICAgICBvcHRpb25zLmNvbnRpbnVlIHx8IG9wdGlvbnMucmVzdW1lIHx8IHRlbGVwb3J0IHx8IHNldHVwVHJpZ2dlclxuICAgICAgICAgICAgPyB1bmRlZmluZWRcbiAgICAgICAgICAgIDogcHJvY2Vzc1Nlc3Npb25TdGFydEhvb2tzKCdzdGFydHVwJylcbiAgICAgICAgLy8gU3VwcHJlc3MgdHJhbnNpZW50IHVuaGFuZGxlZFJlamVjdGlvbiBpZiB0aGlzIHJlamVjdHMgYmVmb3JlXG4gICAgICAgIC8vIGxvYWRJbml0aWFsTWVzc2FnZXMgYXdhaXRzIGl0LiBEb3duc3RyZWFtIGF3YWl0IHN0aWxsIG9ic2VydmVzIHRoZVxuICAgICAgICAvLyByZWplY3Rpb24g4oCUIHRoaXMganVzdCBwcmV2ZW50cyB0aGUgc3B1cmlvdXMgZ2xvYmFsIGhhbmRsZXIgZmlyZS5cbiAgICAgICAgc2Vzc2lvblN0YXJ0SG9va3NQcm9taXNlPy5jYXRjaCgoKSA9PiB7fSlcblxuICAgICAgICBwcm9maWxlQ2hlY2twb2ludCgnYmVmb3JlX3ZhbGlkYXRlRm9yY2VMb2dpbk9yZycpXG4gICAgICAgIC8vIFZhbGlkYXRlIG9yZyByZXN0cmljdGlvbiBmb3Igbm9uLWludGVyYWN0aXZlIHNlc3Npb25zXG4gICAgICAgIGNvbnN0IG9yZ1ZhbGlkYXRpb24gPSBhd2FpdCB2YWxpZGF0ZUZvcmNlTG9naW5PcmcoKVxuICAgICAgICBpZiAoIW9yZ1ZhbGlkYXRpb24udmFsaWQpIHtcbiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShvcmdWYWxpZGF0aW9uLm1lc3NhZ2UgKyAnXFxuJylcbiAgICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEhlYWRsZXNzIG1vZGUgc3VwcG9ydHMgYWxsIHByb21wdCBjb21tYW5kcyBhbmQgc29tZSBsb2NhbCBjb21tYW5kc1xuICAgICAgICAvLyBJZiBkaXNhYmxlU2xhc2hDb21tYW5kcyBpcyB0cnVlLCByZXR1cm4gZW1wdHkgYXJyYXlcbiAgICAgICAgY29uc3QgY29tbWFuZHNIZWFkbGVzcyA9IGRpc2FibGVTbGFzaENvbW1hbmRzXG4gICAgICAgICAgPyBbXVxuICAgICAgICAgIDogY29tbWFuZHMuZmlsdGVyKFxuICAgICAgICAgICAgICBjb21tYW5kID0+XG4gICAgICAgICAgICAgICAgKGNvbW1hbmQudHlwZSA9PT0gJ3Byb21wdCcgJiYgIWNvbW1hbmQuZGlzYWJsZU5vbkludGVyYWN0aXZlKSB8fFxuICAgICAgICAgICAgICAgIChjb21tYW5kLnR5cGUgPT09ICdsb2NhbCcgJiYgY29tbWFuZC5zdXBwb3J0c05vbkludGVyYWN0aXZlKSxcbiAgICAgICAgICAgIClcblxuICAgICAgICBjb25zdCBkZWZhdWx0U3RhdGUgPSBnZXREZWZhdWx0QXBwU3RhdGUoKVxuICAgICAgICBjb25zdCBoZWFkbGVzc0luaXRpYWxTdGF0ZTogQXBwU3RhdGUgPSB7XG4gICAgICAgICAgLi4uZGVmYXVsdFN0YXRlLFxuICAgICAgICAgIG1jcDoge1xuICAgICAgICAgICAgLi4uZGVmYXVsdFN0YXRlLm1jcCxcbiAgICAgICAgICAgIGNsaWVudHM6IG1jcENsaWVudHMsXG4gICAgICAgICAgICBjb21tYW5kczogbWNwQ29tbWFuZHMsXG4gICAgICAgICAgICB0b29sczogbWNwVG9vbHMsXG4gICAgICAgICAgfSxcbiAgICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICAgICAgZWZmb3J0VmFsdWU6XG4gICAgICAgICAgICBwYXJzZUVmZm9ydFZhbHVlKG9wdGlvbnMuZWZmb3J0KSA/PyBnZXRJbml0aWFsRWZmb3J0U2V0dGluZygpLFxuICAgICAgICAgIC4uLihpc0Zhc3RNb2RlRW5hYmxlZCgpICYmIHtcbiAgICAgICAgICAgIGZhc3RNb2RlOiBnZXRJbml0aWFsRmFzdE1vZGVTZXR0aW5nKGVmZmVjdGl2ZU1vZGVsID8/IG51bGwpLFxuICAgICAgICAgIH0pLFxuICAgICAgICAgIC4uLihpc0Fkdmlzb3JFbmFibGVkKCkgJiYgYWR2aXNvck1vZGVsICYmIHsgYWR2aXNvck1vZGVsIH0pLFxuICAgICAgICAgIC8vIGthaXJvc0VuYWJsZWQgZ2F0ZXMgdGhlIGFzeW5jIGZpcmUtYW5kLWZvcmdldCBwYXRoIGluXG4gICAgICAgICAgLy8gZXhlY3V0ZUZvcmtlZFNsYXNoQ29tbWFuZCAocHJvY2Vzc1NsYXNoQ29tbWFuZC50c3g6MTMyKSBhbmRcbiAgICAgICAgICAvLyBBZ2VudFRvb2wncyBzaG91bGRSdW5Bc3luYy4gVGhlIFJFUEwgaW5pdGlhbFN0YXRlIHNldHMgdGhpcyBhdFxuICAgICAgICAgIC8vIH4zNDU5OyBoZWFkbGVzcyB3YXMgZGVmYXVsdGluZyB0byBmYWxzZSwgc28gdGhlIGRhZW1vbiBjaGlsZCdzXG4gICAgICAgICAgLy8gc2NoZWR1bGVkIHRhc2tzIGFuZCBBZ2VudC10b29sIGNhbGxzIHJhbiBzeW5jaHJvbm91c2x5IOKAlCBOXG4gICAgICAgICAgLy8gb3ZlcmR1ZSBjcm9uIHRhc2tzIG9uIHNwYXduID0gTiBzZXJpYWwgc3ViYWdlbnQgdHVybnMgYmxvY2tpbmdcbiAgICAgICAgICAvLyB1c2VyIGlucHV0LiBDb21wdXRlZCBhdCA6MTYyMCwgd2VsbCBiZWZvcmUgdGhpcyBicmFuY2guXG4gICAgICAgICAgLi4uKGZlYXR1cmUoJ0tBSVJPUycpID8geyBrYWlyb3NFbmFibGVkIH0gOiB7fSksXG4gICAgICAgIH1cblxuICAgICAgICAvLyBJbml0IGFwcCBzdGF0ZVxuICAgICAgICBjb25zdCBoZWFkbGVzc1N0b3JlID0gY3JlYXRlU3RvcmUoXG4gICAgICAgICAgaGVhZGxlc3NJbml0aWFsU3RhdGUsXG4gICAgICAgICAgb25DaGFuZ2VBcHBTdGF0ZSxcbiAgICAgICAgKVxuXG4gICAgICAgIC8vIENoZWNrIGlmIGJ5cGFzc1Blcm1pc3Npb25zIHNob3VsZCBiZSBkaXNhYmxlZCBiYXNlZCBvbiBTdGF0c2lnIGdhdGVcbiAgICAgICAgLy8gVGhpcyBydW5zIGluIHBhcmFsbGVsIHRvIHRoZSBjb2RlIGJlbG93LCB0byBhdm9pZCBibG9ja2luZyB0aGUgbWFpbiBsb29wLlxuICAgICAgICBpZiAoXG4gICAgICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0Lm1vZGUgPT09ICdieXBhc3NQZXJtaXNzaW9ucycgfHxcbiAgICAgICAgICBhbGxvd0Rhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zXG4gICAgICAgICkge1xuICAgICAgICAgIHZvaWQgY2hlY2tBbmREaXNhYmxlQnlwYXNzUGVybWlzc2lvbnModG9vbFBlcm1pc3Npb25Db250ZXh0KVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gQXN5bmMgY2hlY2sgb2YgYXV0byBtb2RlIGdhdGUg4oCUIGNvcnJlY3RzIHN0YXRlIGFuZCBkaXNhYmxlcyBhdXRvIGlmIG5lZWRlZC5cbiAgICAgICAgLy8gR2F0ZWQgb24gVFJBTlNDUklQVF9DTEFTU0lGSUVSIChub3QgVVNFUl9UWVBFKSBzbyBHcm93dGhCb29rIGtpbGwgc3dpdGNoIHJ1bnMgZm9yIGV4dGVybmFsIGJ1aWxkcyB0b28uXG4gICAgICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgICAgICAgIHZvaWQgdmVyaWZ5QXV0b01vZGVHYXRlQWNjZXNzKFxuICAgICAgICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICAgICAgICAgICAgaGVhZGxlc3NTdG9yZS5nZXRTdGF0ZSgpLmZhc3RNb2RlLFxuICAgICAgICAgICkudGhlbigoeyB1cGRhdGVDb250ZXh0IH0pID0+IHtcbiAgICAgICAgICAgIGhlYWRsZXNzU3RvcmUuc2V0U3RhdGUocHJldiA9PiB7XG4gICAgICAgICAgICAgIGNvbnN0IG5leHRDdHggPSB1cGRhdGVDb250ZXh0KHByZXYudG9vbFBlcm1pc3Npb25Db250ZXh0KVxuICAgICAgICAgICAgICBpZiAobmV4dEN0eCA9PT0gcHJldi50b29sUGVybWlzc2lvbkNvbnRleHQpIHJldHVybiBwcmV2XG4gICAgICAgICAgICAgIHJldHVybiB7IC4uLnByZXYsIHRvb2xQZXJtaXNzaW9uQ29udGV4dDogbmV4dEN0eCB9XG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH0pXG4gICAgICAgIH1cblxuICAgICAgICAvLyBTZXQgZ2xvYmFsIHN0YXRlIGZvciBzZXNzaW9uIHBlcnNpc3RlbmNlXG4gICAgICAgIGlmIChvcHRpb25zLnNlc3Npb25QZXJzaXN0ZW5jZSA9PT0gZmFsc2UpIHtcbiAgICAgICAgICBzZXRTZXNzaW9uUGVyc2lzdGVuY2VEaXNhYmxlZCh0cnVlKVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gU3RvcmUgU0RLIGJldGFzIGluIGdsb2JhbCBzdGF0ZSBmb3IgY29udGV4dCB3aW5kb3cgY2FsY3VsYXRpb25cbiAgICAgICAgLy8gT25seSBzdG9yZSBhbGxvd2VkIGJldGFzIChmaWx0ZXJzIGJ5IGFsbG93bGlzdCBhbmQgc3Vic2NyaWJlciBzdGF0dXMpXG4gICAgICAgIHNldFNka0JldGFzKGZpbHRlckFsbG93ZWRTZGtCZXRhcyhiZXRhcykpXG5cbiAgICAgICAgLy8gUHJpbnQtbW9kZSBNQ1A6IHBlci1zZXJ2ZXIgaW5jcmVtZW50YWwgcHVzaCBpbnRvIGhlYWRsZXNzU3RvcmUuXG4gICAgICAgIC8vIE1pcnJvcnMgdXNlTWFuYWdlTUNQQ29ubmVjdGlvbnMg4oCUIHB1c2ggcGVuZGluZyBmaXJzdCAoc28gVG9vbFNlYXJjaCdzXG4gICAgICAgIC8vIHBlbmRpbmctY2hlY2sgYXQgVG9vbFNlYXJjaFRvb2wudHM6MzM0IHNlZXMgdGhlbSksIHRoZW4gcmVwbGFjZSB3aXRoXG4gICAgICAgIC8vIGNvbm5lY3RlZC9mYWlsZWQgYXMgZWFjaCBzZXJ2ZXIgc2V0dGxlcy5cbiAgICAgICAgY29uc3QgY29ubmVjdE1jcEJhdGNoID0gKFxuICAgICAgICAgIGNvbmZpZ3M6IFJlY29yZDxzdHJpbmcsIFNjb3BlZE1jcFNlcnZlckNvbmZpZz4sXG4gICAgICAgICAgbGFiZWw6IHN0cmluZyxcbiAgICAgICAgKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgICAgICAgaWYgKE9iamVjdC5rZXlzKGNvbmZpZ3MpLmxlbmd0aCA9PT0gMCkgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpXG4gICAgICAgICAgaGVhZGxlc3NTdG9yZS5zZXRTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgbWNwOiB7XG4gICAgICAgICAgICAgIC4uLnByZXYubWNwLFxuICAgICAgICAgICAgICBjbGllbnRzOiBbXG4gICAgICAgICAgICAgICAgLi4ucHJldi5tY3AuY2xpZW50cyxcbiAgICAgICAgICAgICAgICAuLi5PYmplY3QuZW50cmllcyhjb25maWdzKS5tYXAoKFtuYW1lLCBjb25maWddKSA9PiAoe1xuICAgICAgICAgICAgICAgICAgbmFtZSxcbiAgICAgICAgICAgICAgICAgIHR5cGU6ICdwZW5kaW5nJyBhcyBjb25zdCxcbiAgICAgICAgICAgICAgICAgIGNvbmZpZyxcbiAgICAgICAgICAgICAgICB9KSksXG4gICAgICAgICAgICAgIF0sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0pKVxuICAgICAgICAgIHJldHVybiBnZXRNY3BUb29sc0NvbW1hbmRzQW5kUmVzb3VyY2VzKFxuICAgICAgICAgICAgKHsgY2xpZW50LCB0b29scywgY29tbWFuZHMgfSkgPT4ge1xuICAgICAgICAgICAgICBoZWFkbGVzc1N0b3JlLnNldFN0YXRlKHByZXYgPT4gKHtcbiAgICAgICAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgICAgICAgIG1jcDoge1xuICAgICAgICAgICAgICAgICAgLi4ucHJldi5tY3AsXG4gICAgICAgICAgICAgICAgICBjbGllbnRzOiBwcmV2Lm1jcC5jbGllbnRzLnNvbWUoYyA9PiBjLm5hbWUgPT09IGNsaWVudC5uYW1lKVxuICAgICAgICAgICAgICAgICAgICA/IHByZXYubWNwLmNsaWVudHMubWFwKGMgPT5cbiAgICAgICAgICAgICAgICAgICAgICAgIGMubmFtZSA9PT0gY2xpZW50Lm5hbWUgPyBjbGllbnQgOiBjLFxuICAgICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgOiBbLi4ucHJldi5tY3AuY2xpZW50cywgY2xpZW50XSxcbiAgICAgICAgICAgICAgICAgIHRvb2xzOiB1bmlxQnkoWy4uLnByZXYubWNwLnRvb2xzLCAuLi50b29sc10sICduYW1lJyksXG4gICAgICAgICAgICAgICAgICBjb21tYW5kczogdW5pcUJ5KFsuLi5wcmV2Lm1jcC5jb21tYW5kcywgLi4uY29tbWFuZHNdLCAnbmFtZScpLFxuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIH0pKVxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGNvbmZpZ3MsXG4gICAgICAgICAgKS5jYXRjaChlcnIgPT5cbiAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgW01DUF0gJHtsYWJlbH0gY29ubmVjdCBlcnJvcjogJHtlcnJ9YCksXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIC8vIEF3YWl0IGFsbCBNQ1AgY29uZmlncyDigJQgcHJpbnQgbW9kZSBpcyBvZnRlbiBzaW5nbGUtdHVybiwgc29cbiAgICAgICAgLy8gXCJsYXRlLWNvbm5lY3Rpbmcgc2VydmVycyB2aXNpYmxlIG5leHQgdHVyblwiIGRvZXNuJ3QgaGVscC4gU0RLIGluaXRcbiAgICAgICAgLy8gbWVzc2FnZSBhbmQgdHVybi0xIHRvb2wgbGlzdCBib3RoIG5lZWQgY29uZmlndXJlZCBNQ1AgdG9vbHMgcHJlc2VudC5cbiAgICAgICAgLy8gWmVyby1zZXJ2ZXIgY2FzZSBpcyBmcmVlIHZpYSB0aGUgZWFybHkgcmV0dXJuIGluIGNvbm5lY3RNY3BCYXRjaC5cbiAgICAgICAgLy8gQ29ubmVjdG9ycyBwYXJhbGxlbGl6ZSBpbnNpZGUgZ2V0TWNwVG9vbHNDb21tYW5kc0FuZFJlc291cmNlc1xuICAgICAgICAvLyAocHJvY2Vzc0JhdGNoZWQgd2l0aCBQcm9taXNlLmFsbCkuIGNsYXVkZS5haSBpcyBhd2FpdGVkIHRvbyDigJQgaXRzXG4gICAgICAgIC8vIGZldGNoIHdhcyBraWNrZWQgb2ZmIGVhcmx5IChsaW5lIH4yNTU4KSBzbyBvbmx5IHJlc2lkdWFsIHRpbWUgYmxvY2tzXG4gICAgICAgIC8vIGhlcmUuIC0tYmFyZSBza2lwcyBjbGF1ZGUuYWkgZW50aXJlbHkgZm9yIHBlcmYtc2Vuc2l0aXZlIHNjcmlwdHMuXG4gICAgICAgIHByb2ZpbGVDaGVja3BvaW50KCdiZWZvcmVfY29ubmVjdE1jcCcpXG4gICAgICAgIGF3YWl0IGNvbm5lY3RNY3BCYXRjaChyZWd1bGFyTWNwQ29uZmlncywgJ3JlZ3VsYXInKVxuICAgICAgICBwcm9maWxlQ2hlY2twb2ludCgnYWZ0ZXJfY29ubmVjdE1jcCcpXG4gICAgICAgIC8vIERlZHVwOiBzdXBwcmVzcyBwbHVnaW4gTUNQIHNlcnZlcnMgdGhhdCBkdXBsaWNhdGUgYSBjbGF1ZGUuYWlcbiAgICAgICAgLy8gY29ubmVjdG9yIChjb25uZWN0b3Igd2lucyksIHRoZW4gY29ubmVjdCBjbGF1ZGUuYWkgc2VydmVycy5cbiAgICAgICAgLy8gQm91bmRlZCB3YWl0IOKAlCAjMjM3MjUgbWFkZSB0aGlzIGJsb2NraW5nIHNvIHNpbmdsZS10dXJuIC1wIHNlZXNcbiAgICAgICAgLy8gY29ubmVjdG9ycywgYnV0IHdpdGggNDArIHNsb3cgY29ubmVjdG9ycyB0ZW5ndV9zdGFydHVwX3BlcmYgcDk5XG4gICAgICAgIC8vIGNsaW1iZWQgdG8gNzZzLiBJZiBmZXRjaCtjb25uZWN0IGRvZXNuJ3QgZmluaXNoIGluIHRpbWUsIHByb2NlZWQ7XG4gICAgICAgIC8vIHRoZSBwcm9taXNlIGtlZXBzIHJ1bm5pbmcgYW5kIHVwZGF0ZXMgaGVhZGxlc3NTdG9yZSBpbiB0aGVcbiAgICAgICAgLy8gYmFja2dyb3VuZCBzbyB0dXJuIDIrIHN0aWxsIHNlZXMgY29ubmVjdG9ycy5cbiAgICAgICAgY29uc3QgQ0xBVURFX0FJX01DUF9USU1FT1VUX01TID0gNV8wMDBcbiAgICAgICAgY29uc3QgY2xhdWRlYWlDb25uZWN0ID0gY2xhdWRlYWlDb25maWdQcm9taXNlLnRoZW4oY2xhdWRlYWlDb25maWdzID0+IHtcbiAgICAgICAgICBpZiAoT2JqZWN0LmtleXMoY2xhdWRlYWlDb25maWdzKS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBjbGF1ZGVhaVNpZ3MgPSBuZXcgU2V0PHN0cmluZz4oKVxuICAgICAgICAgICAgZm9yIChjb25zdCBjb25maWcgb2YgT2JqZWN0LnZhbHVlcyhjbGF1ZGVhaUNvbmZpZ3MpKSB7XG4gICAgICAgICAgICAgIGNvbnN0IHNpZyA9IGdldE1jcFNlcnZlclNpZ25hdHVyZShjb25maWcpXG4gICAgICAgICAgICAgIGlmIChzaWcpIGNsYXVkZWFpU2lncy5hZGQoc2lnKVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3Qgc3VwcHJlc3NlZCA9IG5ldyBTZXQ8c3RyaW5nPigpXG4gICAgICAgICAgICBmb3IgKGNvbnN0IFtuYW1lLCBjb25maWddIG9mIE9iamVjdC5lbnRyaWVzKHJlZ3VsYXJNY3BDb25maWdzKSkge1xuICAgICAgICAgICAgICBpZiAoIW5hbWUuc3RhcnRzV2l0aCgncGx1Z2luOicpKSBjb250aW51ZVxuICAgICAgICAgICAgICBjb25zdCBzaWcgPSBnZXRNY3BTZXJ2ZXJTaWduYXR1cmUoY29uZmlnKVxuICAgICAgICAgICAgICBpZiAoc2lnICYmIGNsYXVkZWFpU2lncy5oYXMoc2lnKSkgc3VwcHJlc3NlZC5hZGQobmFtZSlcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChzdXBwcmVzc2VkLnNpemUgPiAwKSB7XG4gICAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICAgICAgICBgW01DUF0gTGF6eSBkZWR1cDogc3VwcHJlc3NpbmcgJHtzdXBwcmVzc2VkLnNpemV9IHBsdWdpbiBzZXJ2ZXIocykgdGhhdCBkdXBsaWNhdGUgY2xhdWRlLmFpIGNvbm5lY3RvcnM6ICR7Wy4uLnN1cHByZXNzZWRdLmpvaW4oJywgJyl9YCxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAvLyBEaXNjb25uZWN0IGJlZm9yZSBmaWx0ZXJpbmcgZnJvbSBzdGF0ZS4gT25seSBjb25uZWN0ZWRcbiAgICAgICAgICAgICAgLy8gc2VydmVycyBuZWVkIGNsZWFudXAg4oCUIGNsZWFyU2VydmVyQ2FjaGUgb24gYSBuZXZlci1jb25uZWN0ZWRcbiAgICAgICAgICAgICAgLy8gc2VydmVyIHRyaWdnZXJzIGEgcmVhbCBjb25uZWN0IGp1c3QgdG8ga2lsbCBpdCAobWVtb2l6ZVxuICAgICAgICAgICAgICAvLyBjYWNoZS1taXNzIHBhdGgsIHNlZSB1c2VNYW5hZ2VNQ1BDb25uZWN0aW9ucy50czo4NzApLlxuICAgICAgICAgICAgICBmb3IgKGNvbnN0IGMgb2YgaGVhZGxlc3NTdG9yZS5nZXRTdGF0ZSgpLm1jcC5jbGllbnRzKSB7XG4gICAgICAgICAgICAgICAgaWYgKCFzdXBwcmVzc2VkLmhhcyhjLm5hbWUpIHx8IGMudHlwZSAhPT0gJ2Nvbm5lY3RlZCcpIGNvbnRpbnVlXG4gICAgICAgICAgICAgICAgYy5jbGllbnQub25jbG9zZSA9IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgIHZvaWQgY2xlYXJTZXJ2ZXJDYWNoZShjLm5hbWUsIGMuY29uZmlnKS5jYXRjaCgoKSA9PiB7fSlcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBoZWFkbGVzc1N0b3JlLnNldFN0YXRlKHByZXYgPT4ge1xuICAgICAgICAgICAgICAgIGxldCB7IGNsaWVudHMsIHRvb2xzLCBjb21tYW5kcywgcmVzb3VyY2VzIH0gPSBwcmV2Lm1jcFxuICAgICAgICAgICAgICAgIGNsaWVudHMgPSBjbGllbnRzLmZpbHRlcihjID0+ICFzdXBwcmVzc2VkLmhhcyhjLm5hbWUpKVxuICAgICAgICAgICAgICAgIHRvb2xzID0gdG9vbHMuZmlsdGVyKFxuICAgICAgICAgICAgICAgICAgdCA9PiAhdC5tY3BJbmZvIHx8ICFzdXBwcmVzc2VkLmhhcyh0Lm1jcEluZm8uc2VydmVyTmFtZSksXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIGZvciAoY29uc3QgbmFtZSBvZiBzdXBwcmVzc2VkKSB7XG4gICAgICAgICAgICAgICAgICBjb21tYW5kcyA9IGV4Y2x1ZGVDb21tYW5kc0J5U2VydmVyKGNvbW1hbmRzLCBuYW1lKVxuICAgICAgICAgICAgICAgICAgcmVzb3VyY2VzID0gZXhjbHVkZVJlc291cmNlc0J5U2VydmVyKHJlc291cmNlcywgbmFtZSlcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgICAgICBtY3A6IHsgLi4ucHJldi5tY3AsIGNsaWVudHMsIHRvb2xzLCBjb21tYW5kcywgcmVzb3VyY2VzIH0sXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICAvLyBTdXBwcmVzcyBjbGF1ZGUuYWkgY29ubmVjdG9ycyB0aGF0IGR1cGxpY2F0ZSBhbiBlbmFibGVkXG4gICAgICAgICAgLy8gbWFudWFsIHNlcnZlciAoVVJMLXNpZ25hdHVyZSBtYXRjaCkuIFBsdWdpbiBkZWR1cCBhYm92ZSBvbmx5XG4gICAgICAgICAgLy8gaGFuZGxlcyBgcGx1Z2luOipgIGtleXM7IHRoaXMgY2F0Y2hlcyBtYW51YWwgYC5tY3AuanNvbmAgZW50cmllcy5cbiAgICAgICAgICAvLyBwbHVnaW46KiBtdXN0IGJlIGV4Y2x1ZGVkIGhlcmUg4oCUIHN0ZXAgMSBhbHJlYWR5IHN1cHByZXNzZWRcbiAgICAgICAgICAvLyB0aG9zZSAoY2xhdWRlLmFpIHdpbnMpOyBsZWF2aW5nIHRoZW0gaW4gc3VwcHJlc3NlcyB0aGVcbiAgICAgICAgICAvLyBjb25uZWN0b3IgdG9vLCBhbmQgbmVpdGhlciBzdXJ2aXZlcyAoZ2gtMzk5NzQpLlxuICAgICAgICAgIGNvbnN0IG5vblBsdWdpbkNvbmZpZ3MgPSBwaWNrQnkoXG4gICAgICAgICAgICByZWd1bGFyTWNwQ29uZmlncyxcbiAgICAgICAgICAgIChfLCBuKSA9PiAhbi5zdGFydHNXaXRoKCdwbHVnaW46JyksXG4gICAgICAgICAgKVxuICAgICAgICAgIGNvbnN0IHsgc2VydmVyczogZGVkdXBlZENsYXVkZUFpIH0gPSBkZWR1cENsYXVkZUFpTWNwU2VydmVycyhcbiAgICAgICAgICAgIGNsYXVkZWFpQ29uZmlncyxcbiAgICAgICAgICAgIG5vblBsdWdpbkNvbmZpZ3MsXG4gICAgICAgICAgKVxuICAgICAgICAgIHJldHVybiBjb25uZWN0TWNwQmF0Y2goZGVkdXBlZENsYXVkZUFpLCAnY2xhdWRlYWknKVxuICAgICAgICB9KVxuICAgICAgICBsZXQgY2xhdWRlYWlUaW1lcjogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0VGltZW91dD4gfCB1bmRlZmluZWRcbiAgICAgICAgY29uc3QgY2xhdWRlYWlUaW1lZE91dCA9IGF3YWl0IFByb21pc2UucmFjZShbXG4gICAgICAgICAgY2xhdWRlYWlDb25uZWN0LnRoZW4oKCkgPT4gZmFsc2UpLFxuICAgICAgICAgIG5ldyBQcm9taXNlPGJvb2xlYW4+KHJlc29sdmUgPT4ge1xuICAgICAgICAgICAgY2xhdWRlYWlUaW1lciA9IHNldFRpbWVvdXQoXG4gICAgICAgICAgICAgIHIgPT4gcih0cnVlKSxcbiAgICAgICAgICAgICAgQ0xBVURFX0FJX01DUF9USU1FT1VUX01TLFxuICAgICAgICAgICAgICByZXNvbHZlLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH0pLFxuICAgICAgICBdKVxuICAgICAgICBpZiAoY2xhdWRlYWlUaW1lcikgY2xlYXJUaW1lb3V0KGNsYXVkZWFpVGltZXIpXG4gICAgICAgIGlmIChjbGF1ZGVhaVRpbWVkT3V0KSB7XG4gICAgICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICAgICAgYFtNQ1BdIGNsYXVkZS5haSBjb25uZWN0b3JzIG5vdCByZWFkeSBhZnRlciAke0NMQVVERV9BSV9NQ1BfVElNRU9VVF9NU31tcyDigJQgcHJvY2VlZGluZzsgYmFja2dyb3VuZCBjb25uZWN0aW9uIGNvbnRpbnVlc2AsXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIHByb2ZpbGVDaGVja3BvaW50KCdhZnRlcl9jb25uZWN0TWNwX2NsYXVkZWFpJylcblxuICAgICAgICAvLyBJbiBoZWFkbGVzcyBtb2RlLCBzdGFydCBkZWZlcnJlZCBwcmVmZXRjaGVzIGltbWVkaWF0ZWx5IChubyB1c2VyIHR5cGluZyBkZWxheSlcbiAgICAgICAgLy8gLS1iYXJlIC8gU0lNUExFOiBzdGFydERlZmVycmVkUHJlZmV0Y2hlcyBlYXJseS1yZXR1cm5zIGludGVybmFsbHkuXG4gICAgICAgIC8vIGJhY2tncm91bmRIb3VzZWtlZXBpbmcgKGluaXRFeHRyYWN0TWVtb3JpZXMsIHBydW5lU2hlbGxTbmFwc2hvdHMsXG4gICAgICAgIC8vIGNsZWFudXBPbGRNZXNzYWdlRmlsZXMpIGFuZCBzZGtIZWFwRHVtcE1vbml0b3IgYXJlIGFsbCBib29ra2VlcGluZ1xuICAgICAgICAvLyB0aGF0IHNjcmlwdGVkIGNhbGxzIGRvbid0IG5lZWQg4oCUIHRoZSBuZXh0IGludGVyYWN0aXZlIHNlc3Npb24gcmVjb25jaWxlcy5cbiAgICAgICAgaWYgKCFpc0JhcmVNb2RlKCkpIHtcbiAgICAgICAgICBzdGFydERlZmVycmVkUHJlZmV0Y2hlcygpXG4gICAgICAgICAgdm9pZCBpbXBvcnQoJy4vdXRpbHMvYmFja2dyb3VuZEhvdXNla2VlcGluZy5qcycpLnRoZW4obSA9PlxuICAgICAgICAgICAgbS5zdGFydEJhY2tncm91bmRIb3VzZWtlZXBpbmcoKSxcbiAgICAgICAgICApXG4gICAgICAgICAgaWYgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcpIHtcbiAgICAgICAgICAgIHZvaWQgaW1wb3J0KCcuL3V0aWxzL3Nka0hlYXBEdW1wTW9uaXRvci5qcycpLnRoZW4obSA9PlxuICAgICAgICAgICAgICBtLnN0YXJ0U2RrTWVtb3J5TW9uaXRvcigpLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGxvZ1Nlc3Npb25UZWxlbWV0cnkoKVxuICAgICAgICBwcm9maWxlQ2hlY2twb2ludCgnYmVmb3JlX3ByaW50X2ltcG9ydCcpXG4gICAgICAgIGNvbnN0IHsgcnVuSGVhZGxlc3MgfSA9IGF3YWl0IGltcG9ydCgnc3JjL2NsaS9wcmludC5qcycpXG4gICAgICAgIHByb2ZpbGVDaGVja3BvaW50KCdhZnRlcl9wcmludF9pbXBvcnQnKVxuICAgICAgICB2b2lkIHJ1bkhlYWRsZXNzKFxuICAgICAgICAgIGlucHV0UHJvbXB0LFxuICAgICAgICAgICgpID0+IGhlYWRsZXNzU3RvcmUuZ2V0U3RhdGUoKSxcbiAgICAgICAgICBoZWFkbGVzc1N0b3JlLnNldFN0YXRlLFxuICAgICAgICAgIGNvbW1hbmRzSGVhZGxlc3MsXG4gICAgICAgICAgdG9vbHMsXG4gICAgICAgICAgc2RrTWNwQ29uZmlncyxcbiAgICAgICAgICBhZ2VudERlZmluaXRpb25zLmFjdGl2ZUFnZW50cyxcbiAgICAgICAgICB7XG4gICAgICAgICAgICBjb250aW51ZTogb3B0aW9ucy5jb250aW51ZSxcbiAgICAgICAgICAgIHJlc3VtZTogb3B0aW9ucy5yZXN1bWUsXG4gICAgICAgICAgICB2ZXJib3NlOiB2ZXJib3NlLFxuICAgICAgICAgICAgb3V0cHV0Rm9ybWF0OiBvdXRwdXRGb3JtYXQsXG4gICAgICAgICAgICBqc29uU2NoZW1hLFxuICAgICAgICAgICAgcGVybWlzc2lvblByb21wdFRvb2xOYW1lOiBvcHRpb25zLnBlcm1pc3Npb25Qcm9tcHRUb29sLFxuICAgICAgICAgICAgYWxsb3dlZFRvb2xzLFxuICAgICAgICAgICAgdGhpbmtpbmdDb25maWcsXG4gICAgICAgICAgICBtYXhUdXJuczogb3B0aW9ucy5tYXhUdXJucyxcbiAgICAgICAgICAgIG1heEJ1ZGdldFVzZDogb3B0aW9ucy5tYXhCdWRnZXRVc2QsXG4gICAgICAgICAgICB0YXNrQnVkZ2V0OiBvcHRpb25zLnRhc2tCdWRnZXRcbiAgICAgICAgICAgICAgPyB7IHRvdGFsOiBvcHRpb25zLnRhc2tCdWRnZXQgfVxuICAgICAgICAgICAgICA6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIHN5c3RlbVByb21wdCxcbiAgICAgICAgICAgIGFwcGVuZFN5c3RlbVByb21wdCxcbiAgICAgICAgICAgIHVzZXJTcGVjaWZpZWRNb2RlbDogZWZmZWN0aXZlTW9kZWwsXG4gICAgICAgICAgICBmYWxsYmFja01vZGVsOiB1c2VyU3BlY2lmaWVkRmFsbGJhY2tNb2RlbCxcbiAgICAgICAgICAgIHRlbGVwb3J0LFxuICAgICAgICAgICAgc2RrVXJsLFxuICAgICAgICAgICAgcmVwbGF5VXNlck1lc3NhZ2VzOiBlZmZlY3RpdmVSZXBsYXlVc2VyTWVzc2FnZXMsXG4gICAgICAgICAgICBpbmNsdWRlUGFydGlhbE1lc3NhZ2VzOiBlZmZlY3RpdmVJbmNsdWRlUGFydGlhbE1lc3NhZ2VzLFxuICAgICAgICAgICAgZm9ya1Nlc3Npb246IG9wdGlvbnMuZm9ya1Nlc3Npb24gfHwgZmFsc2UsXG4gICAgICAgICAgICByZXN1bWVTZXNzaW9uQXQ6IG9wdGlvbnMucmVzdW1lU2Vzc2lvbkF0IHx8IHVuZGVmaW5lZCxcbiAgICAgICAgICAgIHJld2luZEZpbGVzOiBvcHRpb25zLnJld2luZEZpbGVzLFxuICAgICAgICAgICAgZW5hYmxlQXV0aFN0YXR1czogb3B0aW9ucy5lbmFibGVBdXRoU3RhdHVzLFxuICAgICAgICAgICAgYWdlbnQ6IGFnZW50Q2xpLFxuICAgICAgICAgICAgd29ya2xvYWQ6IG9wdGlvbnMud29ya2xvYWQsXG4gICAgICAgICAgICBzZXR1cFRyaWdnZXI6IHNldHVwVHJpZ2dlciA/PyB1bmRlZmluZWQsXG4gICAgICAgICAgICBzZXNzaW9uU3RhcnRIb29rc1Byb21pc2UsXG4gICAgICAgICAgfSxcbiAgICAgICAgKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gTG9nIG1vZGVsIGNvbmZpZyBhdCBzdGFydHVwXG4gICAgICBsb2dFdmVudCgndGVuZ3Vfc3RhcnR1cF9tYW51YWxfbW9kZWxfY29uZmlnJywge1xuICAgICAgICBjbGlfZmxhZzpcbiAgICAgICAgICBvcHRpb25zLm1vZGVsIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGVudl92YXI6IHByb2Nlc3MuZW52XG4gICAgICAgICAgLkFOVEhST1BJQ19NT0RFTCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICBzZXR0aW5nc19maWxlOiAoZ2V0SW5pdGlhbFNldHRpbmdzKCkgfHwge30pXG4gICAgICAgICAgLm1vZGVsIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIHN1YnNjcmlwdGlvblR5cGU6XG4gICAgICAgICAgZ2V0U3Vic2NyaXB0aW9uVHlwZSgpIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgIGFnZW50OlxuICAgICAgICAgIGFnZW50U2V0dGluZyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcblxuICAgICAgLy8gR2V0IGRlcHJlY2F0aW9uIHdhcm5pbmcgZm9yIHRoZSBpbml0aWFsIG1vZGVsIChyZXNvbHZlZEluaXRpYWxNb2RlbCBjb21wdXRlZCBlYXJsaWVyIGZvciBob29rcyBwYXJhbGxlbGl6YXRpb24pXG4gICAgICBjb25zdCBkZXByZWNhdGlvbldhcm5pbmcgPVxuICAgICAgICBnZXRNb2RlbERlcHJlY2F0aW9uV2FybmluZyhyZXNvbHZlZEluaXRpYWxNb2RlbClcblxuICAgICAgLy8gQnVpbGQgaW5pdGlhbCBub3RpZmljYXRpb24gcXVldWVcbiAgICAgIGNvbnN0IGluaXRpYWxOb3RpZmljYXRpb25zOiBBcnJheTx7XG4gICAgICAgIGtleTogc3RyaW5nXG4gICAgICAgIHRleHQ6IHN0cmluZ1xuICAgICAgICBjb2xvcj86ICd3YXJuaW5nJ1xuICAgICAgICBwcmlvcml0eTogJ2hpZ2gnXG4gICAgICB9PiA9IFtdXG4gICAgICBpZiAocGVybWlzc2lvbk1vZGVOb3RpZmljYXRpb24pIHtcbiAgICAgICAgaW5pdGlhbE5vdGlmaWNhdGlvbnMucHVzaCh7XG4gICAgICAgICAga2V5OiAncGVybWlzc2lvbi1tb2RlLW5vdGlmaWNhdGlvbicsXG4gICAgICAgICAgdGV4dDogcGVybWlzc2lvbk1vZGVOb3RpZmljYXRpb24sXG4gICAgICAgICAgcHJpb3JpdHk6ICdoaWdoJyxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIGlmIChkZXByZWNhdGlvbldhcm5pbmcpIHtcbiAgICAgICAgaW5pdGlhbE5vdGlmaWNhdGlvbnMucHVzaCh7XG4gICAgICAgICAga2V5OiAnbW9kZWwtZGVwcmVjYXRpb24td2FybmluZycsXG4gICAgICAgICAgdGV4dDogZGVwcmVjYXRpb25XYXJuaW5nLFxuICAgICAgICAgIGNvbG9yOiAnd2FybmluZycsXG4gICAgICAgICAgcHJpb3JpdHk6ICdoaWdoJyxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIGlmIChvdmVybHlCcm9hZEJhc2hQZXJtaXNzaW9ucy5sZW5ndGggPiAwKSB7XG4gICAgICAgIGNvbnN0IGRpc3BsYXlMaXN0ID0gdW5pcShcbiAgICAgICAgICBvdmVybHlCcm9hZEJhc2hQZXJtaXNzaW9ucy5tYXAocCA9PiBwLnJ1bGVEaXNwbGF5KSxcbiAgICAgICAgKVxuICAgICAgICBjb25zdCBkaXNwbGF5cyA9IGRpc3BsYXlMaXN0LmpvaW4oJywgJylcbiAgICAgICAgY29uc3Qgc291cmNlcyA9IHVuaXEoXG4gICAgICAgICAgb3Zlcmx5QnJvYWRCYXNoUGVybWlzc2lvbnMubWFwKHAgPT4gcC5zb3VyY2VEaXNwbGF5KSxcbiAgICAgICAgKS5qb2luKCcsICcpXG4gICAgICAgIGNvbnN0IG4gPSBkaXNwbGF5TGlzdC5sZW5ndGhcbiAgICAgICAgaW5pdGlhbE5vdGlmaWNhdGlvbnMucHVzaCh7XG4gICAgICAgICAga2V5OiAnb3Zlcmx5LWJyb2FkLWJhc2gtbm90aWZpY2F0aW9uJyxcbiAgICAgICAgICB0ZXh0OiBgJHtkaXNwbGF5c30gYWxsb3cgJHtwbHVyYWwobiwgJ3J1bGUnKX0gZnJvbSAke3NvdXJjZXN9ICR7cGx1cmFsKG4sICd3YXMnLCAnd2VyZScpfSBpZ25vcmVkIFxcdTIwMTQgbm90IGF2YWlsYWJsZSBmb3IgQW50cywgcGxlYXNlIHVzZSBhdXRvLW1vZGUgaW5zdGVhZGAsXG4gICAgICAgICAgY29sb3I6ICd3YXJuaW5nJyxcbiAgICAgICAgICBwcmlvcml0eTogJ2hpZ2gnLFxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICBjb25zdCBlZmZlY3RpdmVUb29sUGVybWlzc2lvbkNvbnRleHQgPSB7XG4gICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgbW9kZTpcbiAgICAgICAgICBpc0FnZW50U3dhcm1zRW5hYmxlZCgpICYmIGdldFRlYW1tYXRlVXRpbHMoKS5pc1BsYW5Nb2RlUmVxdWlyZWQoKVxuICAgICAgICAgICAgPyAoJ3BsYW4nIGFzIGNvbnN0KVxuICAgICAgICAgICAgOiB0b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSxcbiAgICAgIH1cbiAgICAgIC8vIEFsbCBzdGFydHVwIG9wdC1pbiBwYXRocyAoLS10b29scywgLS1icmllZiwgZGVmYXVsdFZpZXcpIGhhdmUgZmlyZWRcbiAgICAgIC8vIGFib3ZlOyBpbml0aWFsSXNCcmllZk9ubHkganVzdCByZWFkcyB0aGUgcmVzdWx0aW5nIHN0YXRlLlxuICAgICAgY29uc3QgaW5pdGlhbElzQnJpZWZPbmx5ID1cbiAgICAgICAgZmVhdHVyZSgnS0FJUk9TJykgfHwgZmVhdHVyZSgnS0FJUk9TX0JSSUVGJykgPyBnZXRVc2VyTXNnT3B0SW4oKSA6IGZhbHNlXG4gICAgICBjb25zdCBmdWxsUmVtb3RlQ29udHJvbCA9XG4gICAgICAgIHJlbW90ZUNvbnRyb2wgfHwgZ2V0UmVtb3RlQ29udHJvbEF0U3RhcnR1cCgpIHx8IGthaXJvc0VuYWJsZWRcbiAgICAgIGxldCBjY3JNaXJyb3JFbmFibGVkID0gZmFsc2VcbiAgICAgIGlmIChmZWF0dXJlKCdDQ1JfTUlSUk9SJykgJiYgIWZ1bGxSZW1vdGVDb250cm9sKSB7XG4gICAgICAgIC8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbiAgICAgICAgY29uc3QgeyBpc0Njck1pcnJvckVuYWJsZWQgfSA9XG4gICAgICAgICAgcmVxdWlyZSgnLi9icmlkZ2UvYnJpZGdlRW5hYmxlZC5qcycpIGFzIHR5cGVvZiBpbXBvcnQoJy4vYnJpZGdlL2JyaWRnZUVuYWJsZWQuanMnKVxuICAgICAgICAvKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbiAgICAgICAgY2NyTWlycm9yRW5hYmxlZCA9IGlzQ2NyTWlycm9yRW5hYmxlZCgpXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGluaXRpYWxTdGF0ZTogQXBwU3RhdGUgPSB7XG4gICAgICAgIHNldHRpbmdzOiBnZXRJbml0aWFsU2V0dGluZ3MoKSxcbiAgICAgICAgdGFza3M6IHt9LFxuICAgICAgICBhZ2VudE5hbWVSZWdpc3RyeTogbmV3IE1hcCgpLFxuICAgICAgICB2ZXJib3NlOiB2ZXJib3NlID8/IGdldEdsb2JhbENvbmZpZygpLnZlcmJvc2UgPz8gZmFsc2UsXG4gICAgICAgIG1haW5Mb29wTW9kZWw6IGluaXRpYWxNYWluTG9vcE1vZGVsLFxuICAgICAgICBtYWluTG9vcE1vZGVsRm9yU2Vzc2lvbjogbnVsbCxcbiAgICAgICAgaXNCcmllZk9ubHk6IGluaXRpYWxJc0JyaWVmT25seSxcbiAgICAgICAgZXhwYW5kZWRWaWV3OiBnZXRHbG9iYWxDb25maWcoKS5zaG93U3Bpbm5lclRyZWVcbiAgICAgICAgICA/ICd0ZWFtbWF0ZXMnXG4gICAgICAgICAgOiBnZXRHbG9iYWxDb25maWcoKS5zaG93RXhwYW5kZWRUb2Rvc1xuICAgICAgICAgICAgPyAndGFza3MnXG4gICAgICAgICAgICA6ICdub25lJyxcbiAgICAgICAgc2hvd1RlYW1tYXRlTWVzc2FnZVByZXZpZXc6IGlzQWdlbnRTd2FybXNFbmFibGVkKCkgPyBmYWxzZSA6IHVuZGVmaW5lZCxcbiAgICAgICAgc2VsZWN0ZWRJUEFnZW50SW5kZXg6IC0xLFxuICAgICAgICBjb29yZGluYXRvclRhc2tJbmRleDogLTEsXG4gICAgICAgIHZpZXdTZWxlY3Rpb25Nb2RlOiAnbm9uZScsXG4gICAgICAgIGZvb3RlclNlbGVjdGlvbjogbnVsbCxcbiAgICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiBlZmZlY3RpdmVUb29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICAgIGFnZW50OiBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uPy5hZ2VudFR5cGUsXG4gICAgICAgIGFnZW50RGVmaW5pdGlvbnMsXG4gICAgICAgIG1jcDoge1xuICAgICAgICAgIGNsaWVudHM6IFtdLFxuICAgICAgICAgIHRvb2xzOiBbXSxcbiAgICAgICAgICBjb21tYW5kczogW10sXG4gICAgICAgICAgcmVzb3VyY2VzOiB7fSxcbiAgICAgICAgICBwbHVnaW5SZWNvbm5lY3RLZXk6IDAsXG4gICAgICAgIH0sXG4gICAgICAgIHBsdWdpbnM6IHtcbiAgICAgICAgICBlbmFibGVkOiBbXSxcbiAgICAgICAgICBkaXNhYmxlZDogW10sXG4gICAgICAgICAgY29tbWFuZHM6IFtdLFxuICAgICAgICAgIGVycm9yczogW10sXG4gICAgICAgICAgaW5zdGFsbGF0aW9uU3RhdHVzOiB7XG4gICAgICAgICAgICBtYXJrZXRwbGFjZXM6IFtdLFxuICAgICAgICAgICAgcGx1Z2luczogW10sXG4gICAgICAgICAgfSxcbiAgICAgICAgICBuZWVkc1JlZnJlc2g6IGZhbHNlLFxuICAgICAgICB9LFxuICAgICAgICBzdGF0dXNMaW5lVGV4dDogdW5kZWZpbmVkLFxuICAgICAgICBrYWlyb3NFbmFibGVkLFxuICAgICAgICByZW1vdGVTZXNzaW9uVXJsOiB1bmRlZmluZWQsXG4gICAgICAgIHJlbW90ZUNvbm5lY3Rpb25TdGF0dXM6ICdjb25uZWN0aW5nJyxcbiAgICAgICAgcmVtb3RlQmFja2dyb3VuZFRhc2tDb3VudDogMCxcbiAgICAgICAgcmVwbEJyaWRnZUVuYWJsZWQ6IGZ1bGxSZW1vdGVDb250cm9sIHx8IGNjck1pcnJvckVuYWJsZWQsXG4gICAgICAgIHJlcGxCcmlkZ2VFeHBsaWNpdDogcmVtb3RlQ29udHJvbCxcbiAgICAgICAgcmVwbEJyaWRnZU91dGJvdW5kT25seTogY2NyTWlycm9yRW5hYmxlZCxcbiAgICAgICAgcmVwbEJyaWRnZUNvbm5lY3RlZDogZmFsc2UsXG4gICAgICAgIHJlcGxCcmlkZ2VTZXNzaW9uQWN0aXZlOiBmYWxzZSxcbiAgICAgICAgcmVwbEJyaWRnZVJlY29ubmVjdGluZzogZmFsc2UsXG4gICAgICAgIHJlcGxCcmlkZ2VDb25uZWN0VXJsOiB1bmRlZmluZWQsXG4gICAgICAgIHJlcGxCcmlkZ2VTZXNzaW9uVXJsOiB1bmRlZmluZWQsXG4gICAgICAgIHJlcGxCcmlkZ2VFbnZpcm9ubWVudElkOiB1bmRlZmluZWQsXG4gICAgICAgIHJlcGxCcmlkZ2VTZXNzaW9uSWQ6IHVuZGVmaW5lZCxcbiAgICAgICAgcmVwbEJyaWRnZUVycm9yOiB1bmRlZmluZWQsXG4gICAgICAgIHJlcGxCcmlkZ2VJbml0aWFsTmFtZTogcmVtb3RlQ29udHJvbE5hbWUsXG4gICAgICAgIHNob3dSZW1vdGVDYWxsb3V0OiBmYWxzZSxcbiAgICAgICAgbm90aWZpY2F0aW9uczoge1xuICAgICAgICAgIGN1cnJlbnQ6IG51bGwsXG4gICAgICAgICAgcXVldWU6IGluaXRpYWxOb3RpZmljYXRpb25zLFxuICAgICAgICB9LFxuICAgICAgICBlbGljaXRhdGlvbjoge1xuICAgICAgICAgIHF1ZXVlOiBbXSxcbiAgICAgICAgfSxcbiAgICAgICAgdG9kb3M6IHt9LFxuICAgICAgICByZW1vdGVBZ2VudFRhc2tTdWdnZXN0aW9uczogW10sXG4gICAgICAgIGZpbGVIaXN0b3J5OiB7XG4gICAgICAgICAgc25hcHNob3RzOiBbXSxcbiAgICAgICAgICB0cmFja2VkRmlsZXM6IG5ldyBTZXQoKSxcbiAgICAgICAgICBzbmFwc2hvdFNlcXVlbmNlOiAwLFxuICAgICAgICB9LFxuICAgICAgICBhdHRyaWJ1dGlvbjogY3JlYXRlRW1wdHlBdHRyaWJ1dGlvblN0YXRlKCksXG4gICAgICAgIHRoaW5raW5nRW5hYmxlZCxcbiAgICAgICAgcHJvbXB0U3VnZ2VzdGlvbkVuYWJsZWQ6IHNob3VsZEVuYWJsZVByb21wdFN1Z2dlc3Rpb24oKSxcbiAgICAgICAgc2Vzc2lvbkhvb2tzOiBuZXcgTWFwKCksXG4gICAgICAgIGluYm94OiB7XG4gICAgICAgICAgbWVzc2FnZXM6IFtdLFxuICAgICAgICB9LFxuICAgICAgICBwcm9tcHRTdWdnZXN0aW9uOiB7XG4gICAgICAgICAgdGV4dDogbnVsbCxcbiAgICAgICAgICBwcm9tcHRJZDogbnVsbCxcbiAgICAgICAgICBzaG93bkF0OiAwLFxuICAgICAgICAgIGFjY2VwdGVkQXQ6IDAsXG4gICAgICAgICAgZ2VuZXJhdGlvblJlcXVlc3RJZDogbnVsbCxcbiAgICAgICAgfSxcbiAgICAgICAgc3BlY3VsYXRpb246IElETEVfU1BFQ1VMQVRJT05fU1RBVEUsXG4gICAgICAgIHNwZWN1bGF0aW9uU2Vzc2lvblRpbWVTYXZlZE1zOiAwLFxuICAgICAgICBza2lsbEltcHJvdmVtZW50OiB7XG4gICAgICAgICAgc3VnZ2VzdGlvbjogbnVsbCxcbiAgICAgICAgfSxcbiAgICAgICAgd29ya2VyU2FuZGJveFBlcm1pc3Npb25zOiB7XG4gICAgICAgICAgcXVldWU6IFtdLFxuICAgICAgICAgIHNlbGVjdGVkSW5kZXg6IDAsXG4gICAgICAgIH0sXG4gICAgICAgIHBlbmRpbmdXb3JrZXJSZXF1ZXN0OiBudWxsLFxuICAgICAgICBwZW5kaW5nU2FuZGJveFJlcXVlc3Q6IG51bGwsXG4gICAgICAgIGF1dGhWZXJzaW9uOiAwLFxuICAgICAgICBpbml0aWFsTWVzc2FnZTogaW5wdXRQcm9tcHRcbiAgICAgICAgICA/IHsgbWVzc2FnZTogY3JlYXRlVXNlck1lc3NhZ2UoeyBjb250ZW50OiBTdHJpbmcoaW5wdXRQcm9tcHQpIH0pIH1cbiAgICAgICAgICA6IG51bGwsXG4gICAgICAgIGVmZm9ydFZhbHVlOlxuICAgICAgICAgIHBhcnNlRWZmb3J0VmFsdWUob3B0aW9ucy5lZmZvcnQpID8/IGdldEluaXRpYWxFZmZvcnRTZXR0aW5nKCksXG4gICAgICAgIGFjdGl2ZU92ZXJsYXlzOiBuZXcgU2V0PHN0cmluZz4oKSxcbiAgICAgICAgZmFzdE1vZGU6IGdldEluaXRpYWxGYXN0TW9kZVNldHRpbmcocmVzb2x2ZWRJbml0aWFsTW9kZWwpLFxuICAgICAgICAuLi4oaXNBZHZpc29yRW5hYmxlZCgpICYmIGFkdmlzb3JNb2RlbCAmJiB7IGFkdmlzb3JNb2RlbCB9KSxcbiAgICAgICAgLy8gQ29tcHV0ZSB0ZWFtQ29udGV4dCBzeW5jaHJvbm91c2x5IHRvIGF2b2lkIHVzZUVmZmVjdCBzZXRTdGF0ZSBkdXJpbmcgcmVuZGVyLlxuICAgICAgICAvLyBLQUlST1M6IGFzc2lzdGFudFRlYW1Db250ZXh0IHRha2VzIHByZWNlZGVuY2Ug4oCUIHNldCBlYXJsaWVyIGluIHRoZVxuICAgICAgICAvLyBLQUlST1MgYmxvY2sgc28gQWdlbnQobmFtZTogXCJmb29cIikgY2FuIHNwYXduIGluLXByb2Nlc3MgdGVhbW1hdGVzXG4gICAgICAgIC8vIHdpdGhvdXQgVGVhbUNyZWF0ZS4gY29tcHV0ZUluaXRpYWxUZWFtQ29udGV4dCgpIGlzIGZvciB0bXV4LXNwYXduZWRcbiAgICAgICAgLy8gdGVhbW1hdGVzIHJlYWRpbmcgdGhlaXIgb3duIGlkZW50aXR5LCBub3QgdGhlIGFzc2lzdGFudC1tb2RlIGxlYWRlci5cbiAgICAgICAgdGVhbUNvbnRleHQ6IGZlYXR1cmUoJ0tBSVJPUycpXG4gICAgICAgICAgPyAoYXNzaXN0YW50VGVhbUNvbnRleHQgPz8gY29tcHV0ZUluaXRpYWxUZWFtQ29udGV4dD8uKCkpXG4gICAgICAgICAgOiBjb21wdXRlSW5pdGlhbFRlYW1Db250ZXh0Py4oKSxcbiAgICAgIH1cblxuICAgICAgLy8gQWRkIENMSSBpbml0aWFsIHByb21wdCB0byBoaXN0b3J5XG4gICAgICBpZiAoaW5wdXRQcm9tcHQpIHtcbiAgICAgICAgYWRkVG9IaXN0b3J5KFN0cmluZyhpbnB1dFByb21wdCkpXG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGluaXRpYWxUb29scyA9IG1jcFRvb2xzXG5cbiAgICAgIC8vIEluY3JlbWVudCBudW1TdGFydHVwcyBzeW5jaHJvbm91c2x5IOKAlCBmaXJzdC1yZW5kZXIgcmVhZGVycyBsaWtlXG4gICAgICAvLyBzaG91bGRTaG93RWZmb3J0Q2FsbG91dCAodmlhIHVzZVN0YXRlIGluaXRpYWxpemVyKSBuZWVkIHRoZSB1cGRhdGVkXG4gICAgICAvLyB2YWx1ZSBiZWZvcmUgc2V0SW1tZWRpYXRlIGZpcmVzLiBEZWZlciBvbmx5IHRlbGVtZXRyeS5cbiAgICAgIHNhdmVHbG9iYWxDb25maWcoY3VycmVudCA9PiAoe1xuICAgICAgICAuLi5jdXJyZW50LFxuICAgICAgICBudW1TdGFydHVwczogKGN1cnJlbnQubnVtU3RhcnR1cHMgPz8gMCkgKyAxLFxuICAgICAgfSkpXG4gICAgICBzZXRJbW1lZGlhdGUoKCkgPT4ge1xuICAgICAgICB2b2lkIGxvZ1N0YXJ0dXBUZWxlbWV0cnkoKVxuICAgICAgICBsb2dTZXNzaW9uVGVsZW1ldHJ5KClcbiAgICAgIH0pXG5cbiAgICAgIC8vIFNldCB1cCBwZXItdHVybiBzZXNzaW9uIGVudmlyb25tZW50IGRhdGEgdXBsb2FkZXIgKGFudC1vbmx5IGJ1aWxkKS5cbiAgICAgIC8vIERlZmF1bHQtZW5hYmxlZCBmb3IgYWxsIGFudCB1c2VycyB3aGVuIHdvcmtpbmcgaW4gYW4gQW50aHJvcGljLW93bmVkXG4gICAgICAvLyByZXBvLiBDYXB0dXJlcyBnaXQvZmlsZXN5c3RlbSBzdGF0ZSAoTk9UIHRyYW5zY3JpcHRzKSBhdCBlYWNoIHR1cm4gc29cbiAgICAgIC8vIGVudmlyb25tZW50cyBjYW4gYmUgcmVjcmVhdGVkIGF0IGFueSB1c2VyIG1lc3NhZ2UgaW5kZXguIEdhdGluZzpcbiAgICAgIC8vICAgLSBCdWlsZC10aW1lOiB0aGlzIGltcG9ydCBpcyBzdHViYmVkIGluIGV4dGVybmFsIGJ1aWxkcy5cbiAgICAgIC8vICAgLSBSdW50aW1lOiB1cGxvYWRlciBjaGVja3MgZ2l0aHViLmNvbS9hbnRocm9waWNzLyogcmVtb3RlICsgZ2Nsb3VkIGF1dGguXG4gICAgICAvLyAgIC0gU2FmZXR5OiBDTEFVREVfQ09ERV9ESVNBQkxFX1NFU1NJT05fREFUQV9VUExPQUQ9MSBieXBhc3NlcyAodGVzdHMgc2V0IHRoaXMpLlxuICAgICAgLy8gSW1wb3J0IGlzIGR5bmFtaWMgKyBhc3luYyB0byBhdm9pZCBhZGRpbmcgc3RhcnR1cCBsYXRlbmN5LlxuICAgICAgY29uc3Qgc2Vzc2lvblVwbG9hZGVyUHJvbWlzZSA9XG4gICAgICAgIFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCdcbiAgICAgICAgICA/IGltcG9ydCgnLi91dGlscy9zZXNzaW9uRGF0YVVwbG9hZGVyLmpzJylcbiAgICAgICAgICA6IG51bGxcblxuICAgICAgLy8gRGVmZXIgc2Vzc2lvbiB1cGxvYWRlciByZXNvbHV0aW9uIHRvIHRoZSBvblR1cm5Db21wbGV0ZSBjYWxsYmFjayB0byBhdm9pZFxuICAgICAgLy8gYWRkaW5nIGEgbmV3IHRvcC1sZXZlbCBhd2FpdCBpbiBtYWluLnRzeCAocGVyZm9ybWFuY2UtY3JpdGljYWwgcGF0aCkuXG4gICAgICAvLyBUaGUgcGVyLXR1cm4gYXV0aCBsb2dpYyBpbiBzZXNzaW9uRGF0YVVwbG9hZGVyLnRzIGhhbmRsZXMgdW5hdXRoZW50aWNhdGVkXG4gICAgICAvLyBzdGF0ZSBncmFjZWZ1bGx5IChyZS1jaGVja3MgZWFjaCB0dXJuLCBzbyBhdXRoIHJlY292ZXJ5IG1pZC1zZXNzaW9uIHdvcmtzKS5cbiAgICAgIGNvbnN0IHVwbG9hZGVyUmVhZHkgPSBzZXNzaW9uVXBsb2FkZXJQcm9taXNlXG4gICAgICAgID8gc2Vzc2lvblVwbG9hZGVyUHJvbWlzZVxuICAgICAgICAgICAgLnRoZW4obW9kID0+IG1vZC5jcmVhdGVTZXNzaW9uVHVyblVwbG9hZGVyKCkpXG4gICAgICAgICAgICAuY2F0Y2goKCkgPT4gbnVsbClcbiAgICAgICAgOiBudWxsXG5cbiAgICAgIGNvbnN0IHNlc3Npb25Db25maWcgPSB7XG4gICAgICAgIGRlYnVnOiBkZWJ1ZyB8fCBkZWJ1Z1RvU3RkZXJyLFxuICAgICAgICBjb21tYW5kczogWy4uLmNvbW1hbmRzLCAuLi5tY3BDb21tYW5kc10sXG4gICAgICAgIGluaXRpYWxUb29scyxcbiAgICAgICAgbWNwQ2xpZW50cyxcbiAgICAgICAgYXV0b0Nvbm5lY3RJZGVGbGFnOiBpZGUsXG4gICAgICAgIG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24sXG4gICAgICAgIGRpc2FibGVTbGFzaENvbW1hbmRzLFxuICAgICAgICBkeW5hbWljTWNwQ29uZmlnLFxuICAgICAgICBzdHJpY3RNY3BDb25maWcsXG4gICAgICAgIHN5c3RlbVByb21wdCxcbiAgICAgICAgYXBwZW5kU3lzdGVtUHJvbXB0LFxuICAgICAgICB0YXNrTGlzdElkLFxuICAgICAgICB0aGlua2luZ0NvbmZpZyxcbiAgICAgICAgLi4uKHVwbG9hZGVyUmVhZHkgJiYge1xuICAgICAgICAgIG9uVHVybkNvbXBsZXRlOiAobWVzc2FnZXM6IE1lc3NhZ2VUeXBlW10pID0+IHtcbiAgICAgICAgICAgIHZvaWQgdXBsb2FkZXJSZWFkeS50aGVuKHVwbG9hZGVyID0+IHVwbG9hZGVyPy4obWVzc2FnZXMpKVxuICAgICAgICAgIH0sXG4gICAgICAgIH0pLFxuICAgICAgfVxuXG4gICAgICAvLyBTaGFyZWQgY29udGV4dCBmb3IgcHJvY2Vzc1Jlc3VtZWRDb252ZXJzYXRpb24gY2FsbHNcbiAgICAgIGNvbnN0IHJlc3VtZUNvbnRleHQgPSB7XG4gICAgICAgIG1vZGVBcGk6IGNvb3JkaW5hdG9yTW9kZU1vZHVsZSxcbiAgICAgICAgbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbixcbiAgICAgICAgYWdlbnREZWZpbml0aW9ucyxcbiAgICAgICAgY3VycmVudEN3ZCxcbiAgICAgICAgY2xpQWdlbnRzLFxuICAgICAgICBpbml0aWFsU3RhdGUsXG4gICAgICB9XG5cbiAgICAgIGlmIChvcHRpb25zLmNvbnRpbnVlKSB7XG4gICAgICAgIC8vIENvbnRpbnVlIHRoZSBtb3N0IHJlY2VudCBjb252ZXJzYXRpb24gZGlyZWN0bHlcbiAgICAgICAgbGV0IHJlc3VtZVN1Y2NlZWRlZCA9IGZhbHNlXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgcmVzdW1lU3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKVxuXG4gICAgICAgICAgLy8gQ2xlYXIgc3RhbGUgY2FjaGVzIGJlZm9yZSByZXN1bWluZyB0byBlbnN1cmUgZnJlc2ggZmlsZS9za2lsbCBkaXNjb3ZlcnlcbiAgICAgICAgICBjb25zdCB7IGNsZWFyU2Vzc2lvbkNhY2hlcyB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgJy4vY29tbWFuZHMvY2xlYXIvY2FjaGVzLmpzJ1xuICAgICAgICAgIClcbiAgICAgICAgICBjbGVhclNlc3Npb25DYWNoZXMoKVxuXG4gICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgbG9hZENvbnZlcnNhdGlvbkZvclJlc3VtZShcbiAgICAgICAgICAgIHVuZGVmaW5lZCAvKiBzZXNzaW9uSWQgKi8sXG4gICAgICAgICAgICB1bmRlZmluZWQgLyogc291cmNlRmlsZSAqLyxcbiAgICAgICAgICApXG4gICAgICAgICAgaWYgKCFyZXN1bHQpIHtcbiAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9jb250aW51ZScsIHtcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IGV4aXRXaXRoRXJyb3IoXG4gICAgICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgICAgICdObyBjb252ZXJzYXRpb24gZm91bmQgdG8gY29udGludWUnLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IGxvYWRlZCA9IGF3YWl0IHByb2Nlc3NSZXN1bWVkQ29udmVyc2F0aW9uKFxuICAgICAgICAgICAgcmVzdWx0LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICBmb3JrU2Vzc2lvbjogISFvcHRpb25zLmZvcmtTZXNzaW9uLFxuICAgICAgICAgICAgICBpbmNsdWRlQXR0cmlidXRpb246IHRydWUsXG4gICAgICAgICAgICAgIHRyYW5zY3JpcHRQYXRoOiByZXN1bHQuZnVsbFBhdGgsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgcmVzdW1lQ29udGV4dCxcbiAgICAgICAgICApXG5cbiAgICAgICAgICBpZiAobG9hZGVkLnJlc3RvcmVkQWdlbnREZWYpIHtcbiAgICAgICAgICAgIG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24gPSBsb2FkZWQucmVzdG9yZWRBZ2VudERlZlxuICAgICAgICAgIH1cblxuICAgICAgICAgIG1heWJlQWN0aXZhdGVQcm9hY3RpdmUob3B0aW9ucylcbiAgICAgICAgICBtYXliZUFjdGl2YXRlQnJpZWYob3B0aW9ucylcblxuICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9jb250aW51ZScsIHtcbiAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICByZXN1bWVfZHVyYXRpb25fbXM6IE1hdGgucm91bmQocGVyZm9ybWFuY2Uubm93KCkgLSByZXN1bWVTdGFydCksXG4gICAgICAgICAgfSlcbiAgICAgICAgICByZXN1bWVTdWNjZWVkZWQgPSB0cnVlXG5cbiAgICAgICAgICBhd2FpdCBsYXVuY2hSZXBsKFxuICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgIHsgZ2V0RnBzTWV0cmljcywgc3RhdHMsIGluaXRpYWxTdGF0ZTogbG9hZGVkLmluaXRpYWxTdGF0ZSB9LFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAuLi5zZXNzaW9uQ29uZmlnLFxuICAgICAgICAgICAgICBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uOlxuICAgICAgICAgICAgICAgIGxvYWRlZC5yZXN0b3JlZEFnZW50RGVmID8/IG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24sXG4gICAgICAgICAgICAgIGluaXRpYWxNZXNzYWdlczogbG9hZGVkLm1lc3NhZ2VzLFxuICAgICAgICAgICAgICBpbml0aWFsRmlsZUhpc3RvcnlTbmFwc2hvdHM6IGxvYWRlZC5maWxlSGlzdG9yeVNuYXBzaG90cyxcbiAgICAgICAgICAgICAgaW5pdGlhbENvbnRlbnRSZXBsYWNlbWVudHM6IGxvYWRlZC5jb250ZW50UmVwbGFjZW1lbnRzLFxuICAgICAgICAgICAgICBpbml0aWFsQWdlbnROYW1lOiBsb2FkZWQuYWdlbnROYW1lLFxuICAgICAgICAgICAgICBpbml0aWFsQWdlbnRDb2xvcjogbG9hZGVkLmFnZW50Q29sb3IsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgcmVuZGVyQW5kUnVuLFxuICAgICAgICAgIClcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBpZiAoIXJlc3VtZVN1Y2NlZWRlZCkge1xuICAgICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2NvbnRpbnVlJywge1xuICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgfVxuICAgICAgICAgIGxvZ0Vycm9yKGVycm9yKVxuICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKVxuICAgICAgICB9XG4gICAgICB9IGVsc2UgaWYgKGZlYXR1cmUoJ0RJUkVDVF9DT05ORUNUJykgJiYgX3BlbmRpbmdDb25uZWN0Py51cmwpIHtcbiAgICAgICAgLy8gYGNsYXVkZSBjb25uZWN0IDx1cmw+YCDigJQgZnVsbCBpbnRlcmFjdGl2ZSBUVUkgY29ubmVjdGVkIHRvIGEgcmVtb3RlIHNlcnZlclxuICAgICAgICBsZXQgZGlyZWN0Q29ubmVjdENvbmZpZ1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHNlc3Npb24gPSBhd2FpdCBjcmVhdGVEaXJlY3RDb25uZWN0U2Vzc2lvbih7XG4gICAgICAgICAgICBzZXJ2ZXJVcmw6IF9wZW5kaW5nQ29ubmVjdC51cmwsXG4gICAgICAgICAgICBhdXRoVG9rZW46IF9wZW5kaW5nQ29ubmVjdC5hdXRoVG9rZW4sXG4gICAgICAgICAgICBjd2Q6IGdldE9yaWdpbmFsQ3dkKCksXG4gICAgICAgICAgICBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uczpcbiAgICAgICAgICAgICAgX3BlbmRpbmdDb25uZWN0LmRhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zLFxuICAgICAgICAgIH0pXG4gICAgICAgICAgaWYgKHNlc3Npb24ud29ya0Rpcikge1xuICAgICAgICAgICAgc2V0T3JpZ2luYWxDd2Qoc2Vzc2lvbi53b3JrRGlyKVxuICAgICAgICAgICAgc2V0Q3dkU3RhdGUoc2Vzc2lvbi53b3JrRGlyKVxuICAgICAgICAgIH1cbiAgICAgICAgICBzZXREaXJlY3RDb25uZWN0U2VydmVyVXJsKF9wZW5kaW5nQ29ubmVjdC51cmwpXG4gICAgICAgICAgZGlyZWN0Q29ubmVjdENvbmZpZyA9IHNlc3Npb24uY29uZmlnXG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgIHJldHVybiBhd2FpdCBleGl0V2l0aEVycm9yKFxuICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgIGVyciBpbnN0YW5jZW9mIERpcmVjdENvbm5lY3RFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVyciksXG4gICAgICAgICAgICAoKSA9PiBncmFjZWZ1bFNodXRkb3duKDEpLFxuICAgICAgICAgIClcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGNvbm5lY3RJbmZvTWVzc2FnZSA9IGNyZWF0ZVN5c3RlbU1lc3NhZ2UoXG4gICAgICAgICAgYENvbm5lY3RlZCB0byBzZXJ2ZXIgYXQgJHtfcGVuZGluZ0Nvbm5lY3QudXJsfVxcblNlc3Npb246ICR7ZGlyZWN0Q29ubmVjdENvbmZpZy5zZXNzaW9uSWR9YCxcbiAgICAgICAgICAnaW5mbycsXG4gICAgICAgIClcblxuICAgICAgICBhd2FpdCBsYXVuY2hSZXBsKFxuICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgeyBnZXRGcHNNZXRyaWNzLCBzdGF0cywgaW5pdGlhbFN0YXRlIH0sXG4gICAgICAgICAge1xuICAgICAgICAgICAgZGVidWc6IGRlYnVnIHx8IGRlYnVnVG9TdGRlcnIsXG4gICAgICAgICAgICBjb21tYW5kcyxcbiAgICAgICAgICAgIGluaXRpYWxUb29sczogW10sXG4gICAgICAgICAgICBpbml0aWFsTWVzc2FnZXM6IFtjb25uZWN0SW5mb01lc3NhZ2VdLFxuICAgICAgICAgICAgbWNwQ2xpZW50czogW10sXG4gICAgICAgICAgICBhdXRvQ29ubmVjdElkZUZsYWc6IGlkZSxcbiAgICAgICAgICAgIG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24sXG4gICAgICAgICAgICBkaXNhYmxlU2xhc2hDb21tYW5kcyxcbiAgICAgICAgICAgIGRpcmVjdENvbm5lY3RDb25maWcsXG4gICAgICAgICAgICB0aGlua2luZ0NvbmZpZyxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHJlbmRlckFuZFJ1bixcbiAgICAgICAgKVxuICAgICAgICByZXR1cm5cbiAgICAgIH0gZWxzZSBpZiAoZmVhdHVyZSgnU1NIX1JFTU9URScpICYmIF9wZW5kaW5nU1NIPy5ob3N0KSB7XG4gICAgICAgIC8vIGBjbGF1ZGUgc3NoIDxob3N0PiBbZGlyXWAg4oCUIHByb2JlIHJlbW90ZSwgZGVwbG95IGJpbmFyeSBpZiBuZWVkZWQsXG4gICAgICAgIC8vIHNwYXduIHNzaCB3aXRoIHVuaXgtc29ja2V0IC1SIGZvcndhcmQgdG8gYSBsb2NhbCBhdXRoIHByb3h5LCBoYW5kXG4gICAgICAgIC8vIHRoZSBSRVBMIGFuIFNTSFNlc3Npb24uIFRvb2xzIHJ1biByZW1vdGVseSwgVUkgcmVuZGVycyBsb2NhbGx5LlxuICAgICAgICAvLyBgLS1sb2NhbGAgc2tpcHMgcHJvYmUvZGVwbG95L3NzaCBhbmQgc3Bhd25zIHRoZSBjdXJyZW50IGJpbmFyeVxuICAgICAgICAvLyBkaXJlY3RseSB3aXRoIHRoZSBzYW1lIGVudiDigJQgZTJlIHRlc3Qgb2YgdGhlIHByb3h5L2F1dGggcGx1bWJpbmcuXG4gICAgICAgIGNvbnN0IHsgY3JlYXRlU1NIU2Vzc2lvbiwgY3JlYXRlTG9jYWxTU0hTZXNzaW9uLCBTU0hTZXNzaW9uRXJyb3IgfSA9XG4gICAgICAgICAgYXdhaXQgaW1wb3J0KCcuL3NzaC9jcmVhdGVTU0hTZXNzaW9uLmpzJylcbiAgICAgICAgbGV0IHNzaFNlc3Npb25cbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBpZiAoX3BlbmRpbmdTU0gubG9jYWwpIHtcbiAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKCdTdGFydGluZyBsb2NhbCBzc2gtcHJveHkgdGVzdCBzZXNzaW9uLi4uXFxuJylcbiAgICAgICAgICAgIHNzaFNlc3Npb24gPSBjcmVhdGVMb2NhbFNTSFNlc3Npb24oe1xuICAgICAgICAgICAgICBjd2Q6IF9wZW5kaW5nU1NILmN3ZCxcbiAgICAgICAgICAgICAgcGVybWlzc2lvbk1vZGU6IF9wZW5kaW5nU1NILnBlcm1pc3Npb25Nb2RlLFxuICAgICAgICAgICAgICBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uczpcbiAgICAgICAgICAgICAgICBfcGVuZGluZ1NTSC5kYW5nZXJvdXNseVNraXBQZXJtaXNzaW9ucyxcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKGBDb25uZWN0aW5nIHRvICR7X3BlbmRpbmdTU0guaG9zdH3igKZcXG5gKVxuICAgICAgICAgICAgLy8gSW4tcGxhY2UgcHJvZ3Jlc3M6IFxcciArIEVMMCAoZXJhc2UgdG8gZW5kIG9mIGxpbmUpLiBGaW5hbCBcXG4gb25cbiAgICAgICAgICAgIC8vIHN1Y2Nlc3Mgc28gdGhlIG5leHQgbWVzc2FnZSBsYW5kcyBvbiBhIGZyZXNoIGxpbmUuIE5vLW9wIHdoZW5cbiAgICAgICAgICAgIC8vIHN0ZGVyciBpc24ndCBhIFRUWSAocGlwZWQvcmVkaXJlY3RlZCkg4oCUIFxcciB3b3VsZCBqdXN0IGVtaXQgbm9pc2UuXG4gICAgICAgICAgICBjb25zdCBpc1RUWSA9IHByb2Nlc3Muc3RkZXJyLmlzVFRZXG4gICAgICAgICAgICBsZXQgaGFkUHJvZ3Jlc3MgPSBmYWxzZVxuICAgICAgICAgICAgc3NoU2Vzc2lvbiA9IGF3YWl0IGNyZWF0ZVNTSFNlc3Npb24oXG4gICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBob3N0OiBfcGVuZGluZ1NTSC5ob3N0LFxuICAgICAgICAgICAgICAgIGN3ZDogX3BlbmRpbmdTU0guY3dkLFxuICAgICAgICAgICAgICAgIGxvY2FsVmVyc2lvbjogTUFDUk8uVkVSU0lPTixcbiAgICAgICAgICAgICAgICBwZXJtaXNzaW9uTW9kZTogX3BlbmRpbmdTU0gucGVybWlzc2lvbk1vZGUsXG4gICAgICAgICAgICAgICAgZGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnM6XG4gICAgICAgICAgICAgICAgICBfcGVuZGluZ1NTSC5kYW5nZXJvdXNseVNraXBQZXJtaXNzaW9ucyxcbiAgICAgICAgICAgICAgICBleHRyYUNsaUFyZ3M6IF9wZW5kaW5nU1NILmV4dHJhQ2xpQXJncyxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgaXNUVFlcbiAgICAgICAgICAgICAgICA/IHtcbiAgICAgICAgICAgICAgICAgICAgb25Qcm9ncmVzczogbXNnID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICBoYWRQcm9ncmVzcyA9IHRydWVcbiAgICAgICAgICAgICAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShgXFxyICAke21zZ31cXHgxYltLYClcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA6IHt9LFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgaWYgKGhhZFByb2dyZXNzKSBwcm9jZXNzLnN0ZGVyci53cml0ZSgnXFxuJylcbiAgICAgICAgICB9XG4gICAgICAgICAgc2V0T3JpZ2luYWxDd2Qoc3NoU2Vzc2lvbi5yZW1vdGVDd2QpXG4gICAgICAgICAgc2V0Q3dkU3RhdGUoc3NoU2Vzc2lvbi5yZW1vdGVDd2QpXG4gICAgICAgICAgc2V0RGlyZWN0Q29ubmVjdFNlcnZlclVybChcbiAgICAgICAgICAgIF9wZW5kaW5nU1NILmxvY2FsID8gJ2xvY2FsJyA6IF9wZW5kaW5nU1NILmhvc3QsXG4gICAgICAgICAgKVxuICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICByZXR1cm4gYXdhaXQgZXhpdFdpdGhFcnJvcihcbiAgICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgICBlcnIgaW5zdGFuY2VvZiBTU0hTZXNzaW9uRXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpLFxuICAgICAgICAgICAgKCkgPT4gZ3JhY2VmdWxTaHV0ZG93bigxKSxcbiAgICAgICAgICApXG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBzc2hJbmZvTWVzc2FnZSA9IGNyZWF0ZVN5c3RlbU1lc3NhZ2UoXG4gICAgICAgICAgX3BlbmRpbmdTU0gubG9jYWxcbiAgICAgICAgICAgID8gYExvY2FsIHNzaC1wcm94eSB0ZXN0IHNlc3Npb25cXG5jd2Q6ICR7c3NoU2Vzc2lvbi5yZW1vdGVDd2R9XFxuQXV0aDogdW5peCBzb2NrZXQg4oaSIGxvY2FsIHByb3h5YFxuICAgICAgICAgICAgOiBgU1NIIHNlc3Npb24gdG8gJHtfcGVuZGluZ1NTSC5ob3N0fVxcblJlbW90ZSBjd2Q6ICR7c3NoU2Vzc2lvbi5yZW1vdGVDd2R9XFxuQXV0aDogdW5peCBzb2NrZXQgLVIg4oaSIGxvY2FsIHByb3h5YCxcbiAgICAgICAgICAnaW5mbycsXG4gICAgICAgIClcblxuICAgICAgICBhd2FpdCBsYXVuY2hSZXBsKFxuICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgeyBnZXRGcHNNZXRyaWNzLCBzdGF0cywgaW5pdGlhbFN0YXRlIH0sXG4gICAgICAgICAge1xuICAgICAgICAgICAgZGVidWc6IGRlYnVnIHx8IGRlYnVnVG9TdGRlcnIsXG4gICAgICAgICAgICBjb21tYW5kcyxcbiAgICAgICAgICAgIGluaXRpYWxUb29sczogW10sXG4gICAgICAgICAgICBpbml0aWFsTWVzc2FnZXM6IFtzc2hJbmZvTWVzc2FnZV0sXG4gICAgICAgICAgICBtY3BDbGllbnRzOiBbXSxcbiAgICAgICAgICAgIGF1dG9Db25uZWN0SWRlRmxhZzogaWRlLFxuICAgICAgICAgICAgbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbixcbiAgICAgICAgICAgIGRpc2FibGVTbGFzaENvbW1hbmRzLFxuICAgICAgICAgICAgc3NoU2Vzc2lvbixcbiAgICAgICAgICAgIHRoaW5raW5nQ29uZmlnLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgcmVuZGVyQW5kUnVuLFxuICAgICAgICApXG4gICAgICAgIHJldHVyblxuICAgICAgfSBlbHNlIGlmIChcbiAgICAgICAgZmVhdHVyZSgnS0FJUk9TJykgJiZcbiAgICAgICAgX3BlbmRpbmdBc3Npc3RhbnRDaGF0ICYmXG4gICAgICAgIChfcGVuZGluZ0Fzc2lzdGFudENoYXQuc2Vzc2lvbklkIHx8IF9wZW5kaW5nQXNzaXN0YW50Q2hhdC5kaXNjb3ZlcilcbiAgICAgICkge1xuICAgICAgICAvLyBgY2xhdWRlIGFzc2lzdGFudCBbc2Vzc2lvbklkXWAg4oCUIFJFUEwgYXMgYSBwdXJlIHZpZXdlciBjbGllbnRcbiAgICAgICAgLy8gb2YgYSByZW1vdGUgYXNzaXN0YW50IHNlc3Npb24uIFRoZSBhZ2VudGljIGxvb3AgcnVucyByZW1vdGVseTsgdGhpc1xuICAgICAgICAvLyBwcm9jZXNzIHN0cmVhbXMgbGl2ZSBldmVudHMgYW5kIFBPU1RzIG1lc3NhZ2VzLiBIaXN0b3J5IGlzIGxhenktXG4gICAgICAgIC8vIGxvYWRlZCBieSB1c2VBc3Npc3RhbnRIaXN0b3J5IG9uIHNjcm9sbC11cCAobm8gYmxvY2tpbmcgZmV0Y2ggaGVyZSkuXG4gICAgICAgIGNvbnN0IHsgZGlzY292ZXJBc3Npc3RhbnRTZXNzaW9ucyB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICcuL2Fzc2lzdGFudC9zZXNzaW9uRGlzY292ZXJ5LmpzJ1xuICAgICAgICApXG5cbiAgICAgICAgbGV0IHRhcmdldFNlc3Npb25JZCA9IF9wZW5kaW5nQXNzaXN0YW50Q2hhdC5zZXNzaW9uSWRcblxuICAgICAgICAvLyBEaXNjb3ZlcnkgZmxvdyDigJQgbGlzdCBicmlkZ2UgZW52aXJvbm1lbnRzLCBmaWx0ZXIgc2Vzc2lvbnNcbiAgICAgICAgaWYgKCF0YXJnZXRTZXNzaW9uSWQpIHtcbiAgICAgICAgICBsZXQgc2Vzc2lvbnNcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgc2Vzc2lvbnMgPSBhd2FpdCBkaXNjb3ZlckFzc2lzdGFudFNlc3Npb25zKClcbiAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgZXhpdFdpdGhFcnJvcihcbiAgICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgICAgYEZhaWxlZCB0byBkaXNjb3ZlciBzZXNzaW9uczogJHtlIGluc3RhbmNlb2YgRXJyb3IgPyBlLm1lc3NhZ2UgOiBlfWAsXG4gICAgICAgICAgICAgICgpID0+IGdyYWNlZnVsU2h1dGRvd24oMSksXG4gICAgICAgICAgICApXG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChzZXNzaW9ucy5sZW5ndGggPT09IDApIHtcbiAgICAgICAgICAgIGxldCBpbnN0YWxsZWREaXI6IHN0cmluZyB8IG51bGxcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgIGluc3RhbGxlZERpciA9IGF3YWl0IGxhdW5jaEFzc2lzdGFudEluc3RhbGxXaXphcmQocm9vdClcbiAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IGV4aXRXaXRoRXJyb3IoXG4gICAgICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgICAgICBgQXNzaXN0YW50IGluc3RhbGxhdGlvbiBmYWlsZWQ6ICR7ZSBpbnN0YW5jZW9mIEVycm9yID8gZS5tZXNzYWdlIDogZX1gLFxuICAgICAgICAgICAgICAgICgpID0+IGdyYWNlZnVsU2h1dGRvd24oMSksXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChpbnN0YWxsZWREaXIgPT09IG51bGwpIHtcbiAgICAgICAgICAgICAgYXdhaXQgZ3JhY2VmdWxTaHV0ZG93bigwKVxuICAgICAgICAgICAgICBwcm9jZXNzLmV4aXQoMClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFRoZSBkYWVtb24gbmVlZHMgYSBmZXcgc2Vjb25kcyB0byBzcGluIHVwIGl0cyB3b3JrZXIgYW5kXG4gICAgICAgICAgICAvLyBlc3RhYmxpc2ggYSBicmlkZ2Ugc2Vzc2lvbiBiZWZvcmUgZGlzY292ZXJ5IHdpbGwgZmluZCBpdC5cbiAgICAgICAgICAgIHJldHVybiBhd2FpdCBleGl0V2l0aE1lc3NhZ2UoXG4gICAgICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgICAgIGBBc3Npc3RhbnQgaW5zdGFsbGVkIGluICR7aW5zdGFsbGVkRGlyfS4gVGhlIGRhZW1vbiBpcyBzdGFydGluZyB1cCDigJQgcnVuIFxcYGNsYXVkZSBhc3Npc3RhbnRcXGAgYWdhaW4gaW4gYSBmZXcgc2Vjb25kcyB0byBjb25uZWN0LmAsXG4gICAgICAgICAgICAgIHsgZXhpdENvZGU6IDAsIGJlZm9yZUV4aXQ6ICgpID0+IGdyYWNlZnVsU2h1dGRvd24oMCkgfSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG4gICAgICAgICAgaWYgKHNlc3Npb25zLmxlbmd0aCA9PT0gMSkge1xuICAgICAgICAgICAgdGFyZ2V0U2Vzc2lvbklkID0gc2Vzc2lvbnNbMF0hLmlkXG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGNvbnN0IHBpY2tlZCA9IGF3YWl0IGxhdW5jaEFzc2lzdGFudFNlc3Npb25DaG9vc2VyKHJvb3QsIHtcbiAgICAgICAgICAgICAgc2Vzc2lvbnMsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgaWYgKCFwaWNrZWQpIHtcbiAgICAgICAgICAgICAgYXdhaXQgZ3JhY2VmdWxTaHV0ZG93bigwKVxuICAgICAgICAgICAgICBwcm9jZXNzLmV4aXQoMClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRhcmdldFNlc3Npb25JZCA9IHBpY2tlZFxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEF1dGgg4oCUIGNhbGwgcHJlcGFyZUFwaVJlcXVlc3QoKSBvbmNlIGZvciBvcmdVVUlELCBidXQgdXNlIGFcbiAgICAgICAgLy8gZ2V0QWNjZXNzVG9rZW4gY2xvc3VyZSBmb3IgdGhlIHRva2VuIHNvIHJlY29ubmVjdHMgZ2V0IGZyZXNoIHRva2Vucy5cbiAgICAgICAgY29uc3QgeyBjaGVja0FuZFJlZnJlc2hPQXV0aFRva2VuSWZOZWVkZWQsIGdldENsYXVkZUFJT0F1dGhUb2tlbnMgfSA9XG4gICAgICAgICAgYXdhaXQgaW1wb3J0KCcuL3V0aWxzL2F1dGguanMnKVxuICAgICAgICBhd2FpdCBjaGVja0FuZFJlZnJlc2hPQXV0aFRva2VuSWZOZWVkZWQoKVxuICAgICAgICBsZXQgYXBpQ3JlZHNcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhcGlDcmVkcyA9IGF3YWl0IHByZXBhcmVBcGlSZXF1ZXN0KClcbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgIHJldHVybiBhd2FpdCBleGl0V2l0aEVycm9yKFxuICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgIGBFcnJvcjogJHtlIGluc3RhbmNlb2YgRXJyb3IgPyBlLm1lc3NhZ2UgOiAnRmFpbGVkIHRvIGF1dGhlbnRpY2F0ZSd9YCxcbiAgICAgICAgICAgICgpID0+IGdyYWNlZnVsU2h1dGRvd24oMSksXG4gICAgICAgICAgKVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGdldEFjY2Vzc1Rva2VuID0gKCk6IHN0cmluZyA9PlxuICAgICAgICAgIGdldENsYXVkZUFJT0F1dGhUb2tlbnMoKT8uYWNjZXNzVG9rZW4gPz8gYXBpQ3JlZHMuYWNjZXNzVG9rZW5cblxuICAgICAgICAvLyBCcmllZiBtb2RlIGFjdGl2YXRpb246IHNldEthaXJvc0FjdGl2ZSh0cnVlKSBzYXRpc2ZpZXMgQk9USCBvcHQtaW5cbiAgICAgICAgLy8gYW5kIGVudGl0bGVtZW50IGZvciBpc0JyaWVmRW5hYmxlZCgpIChCcmllZlRvb2wudHM6MTI0LTEzMikuXG4gICAgICAgIHNldEthaXJvc0FjdGl2ZSh0cnVlKVxuICAgICAgICBzZXRVc2VyTXNnT3B0SW4odHJ1ZSlcbiAgICAgICAgc2V0SXNSZW1vdGVNb2RlKHRydWUpXG5cbiAgICAgICAgY29uc3QgcmVtb3RlU2Vzc2lvbkNvbmZpZyA9IGNyZWF0ZVJlbW90ZVNlc3Npb25Db25maWcoXG4gICAgICAgICAgdGFyZ2V0U2Vzc2lvbklkLFxuICAgICAgICAgIGdldEFjY2Vzc1Rva2VuLFxuICAgICAgICAgIGFwaUNyZWRzLm9yZ1VVSUQsXG4gICAgICAgICAgLyogaGFzSW5pdGlhbFByb21wdCAqLyBmYWxzZSxcbiAgICAgICAgICAvKiB2aWV3ZXJPbmx5ICovIHRydWUsXG4gICAgICAgIClcblxuICAgICAgICBjb25zdCBpbmZvTWVzc2FnZSA9IGNyZWF0ZVN5c3RlbU1lc3NhZ2UoXG4gICAgICAgICAgYEF0dGFjaGVkIHRvIGFzc2lzdGFudCBzZXNzaW9uICR7dGFyZ2V0U2Vzc2lvbklkLnNsaWNlKDAsIDgpfeKApmAsXG4gICAgICAgICAgJ2luZm8nLFxuICAgICAgICApXG5cbiAgICAgICAgY29uc3QgYXNzaXN0YW50SW5pdGlhbFN0YXRlOiBBcHBTdGF0ZSA9IHtcbiAgICAgICAgICAuLi5pbml0aWFsU3RhdGUsXG4gICAgICAgICAgaXNCcmllZk9ubHk6IHRydWUsXG4gICAgICAgICAga2Fpcm9zRW5hYmxlZDogZmFsc2UsXG4gICAgICAgICAgcmVwbEJyaWRnZUVuYWJsZWQ6IGZhbHNlLFxuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgcmVtb3RlQ29tbWFuZHMgPSBmaWx0ZXJDb21tYW5kc0ZvclJlbW90ZU1vZGUoY29tbWFuZHMpXG4gICAgICAgIGF3YWl0IGxhdW5jaFJlcGwoXG4gICAgICAgICAgcm9vdCxcbiAgICAgICAgICB7IGdldEZwc01ldHJpY3MsIHN0YXRzLCBpbml0aWFsU3RhdGU6IGFzc2lzdGFudEluaXRpYWxTdGF0ZSB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIGRlYnVnOiBkZWJ1ZyB8fCBkZWJ1Z1RvU3RkZXJyLFxuICAgICAgICAgICAgY29tbWFuZHM6IHJlbW90ZUNvbW1hbmRzLFxuICAgICAgICAgICAgaW5pdGlhbFRvb2xzOiBbXSxcbiAgICAgICAgICAgIGluaXRpYWxNZXNzYWdlczogW2luZm9NZXNzYWdlXSxcbiAgICAgICAgICAgIG1jcENsaWVudHM6IFtdLFxuICAgICAgICAgICAgYXV0b0Nvbm5lY3RJZGVGbGFnOiBpZGUsXG4gICAgICAgICAgICBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uLFxuICAgICAgICAgICAgZGlzYWJsZVNsYXNoQ29tbWFuZHMsXG4gICAgICAgICAgICByZW1vdGVTZXNzaW9uQ29uZmlnLFxuICAgICAgICAgICAgdGhpbmtpbmdDb25maWcsXG4gICAgICAgICAgfSxcbiAgICAgICAgICByZW5kZXJBbmRSdW4sXG4gICAgICAgIClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9IGVsc2UgaWYgKFxuICAgICAgICBvcHRpb25zLnJlc3VtZSB8fFxuICAgICAgICBvcHRpb25zLmZyb21QciB8fFxuICAgICAgICB0ZWxlcG9ydCB8fFxuICAgICAgICByZW1vdGUgIT09IG51bGxcbiAgICAgICkge1xuICAgICAgICAvLyBIYW5kbGUgcmVzdW1lIGZsb3cgLSBmcm9tIGZpbGUgKGFudC1vbmx5KSwgc2Vzc2lvbiBJRCwgb3IgaW50ZXJhY3RpdmUgc2VsZWN0b3JcblxuICAgICAgICAvLyBDbGVhciBzdGFsZSBjYWNoZXMgYmVmb3JlIHJlc3VtaW5nIHRvIGVuc3VyZSBmcmVzaCBmaWxlL3NraWxsIGRpc2NvdmVyeVxuICAgICAgICBjb25zdCB7IGNsZWFyU2Vzc2lvbkNhY2hlcyB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICcuL2NvbW1hbmRzL2NsZWFyL2NhY2hlcy5qcydcbiAgICAgICAgKVxuICAgICAgICBjbGVhclNlc3Npb25DYWNoZXMoKVxuXG4gICAgICAgIGxldCBtZXNzYWdlczogTWVzc2FnZVR5cGVbXSB8IG51bGwgPSBudWxsXG4gICAgICAgIGxldCBwcm9jZXNzZWRSZXN1bWU6IFByb2Nlc3NlZFJlc3VtZSB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZFxuXG4gICAgICAgIGxldCBtYXliZVNlc3Npb25JZCA9IHZhbGlkYXRlVXVpZChvcHRpb25zLnJlc3VtZSlcbiAgICAgICAgbGV0IHNlYXJjaFRlcm06IHN0cmluZyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZFxuICAgICAgICAvLyBTdG9yZSBmdWxsIExvZ09wdGlvbiB3aGVuIGZvdW5kIGJ5IGN1c3RvbSB0aXRsZSAoZm9yIGNyb3NzLXdvcmt0cmVlIHJlc3VtZSlcbiAgICAgICAgbGV0IG1hdGNoZWRMb2c6IExvZ09wdGlvbiB8IG51bGwgPSBudWxsXG4gICAgICAgIC8vIFBSIGZpbHRlciBmb3IgLS1mcm9tLXByIGZsYWdcbiAgICAgICAgbGV0IGZpbHRlckJ5UHI6IGJvb2xlYW4gfCBudW1iZXIgfCBzdHJpbmcgfCB1bmRlZmluZWQgPSB1bmRlZmluZWRcblxuICAgICAgICAvLyBIYW5kbGUgLS1mcm9tLXByIGZsYWdcbiAgICAgICAgaWYgKG9wdGlvbnMuZnJvbVByKSB7XG4gICAgICAgICAgaWYgKG9wdGlvbnMuZnJvbVByID09PSB0cnVlKSB7XG4gICAgICAgICAgICAvLyBTaG93IGFsbCBzZXNzaW9ucyB3aXRoIGxpbmtlZCBQUnNcbiAgICAgICAgICAgIGZpbHRlckJ5UHIgPSB0cnVlXG4gICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2Ygb3B0aW9ucy5mcm9tUHIgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICAvLyBDb3VsZCBiZSBhIFBSIG51bWJlciBvciBVUkxcbiAgICAgICAgICAgIGZpbHRlckJ5UHIgPSBvcHRpb25zLmZyb21QclxuICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIElmIHJlc3VtZSB2YWx1ZSBpcyBub3QgYSBVVUlELCB0cnkgZXhhY3QgbWF0Y2ggYnkgY3VzdG9tIHRpdGxlIGZpcnN0XG4gICAgICAgIGlmIChcbiAgICAgICAgICBvcHRpb25zLnJlc3VtZSAmJlxuICAgICAgICAgIHR5cGVvZiBvcHRpb25zLnJlc3VtZSA9PT0gJ3N0cmluZycgJiZcbiAgICAgICAgICAhbWF5YmVTZXNzaW9uSWRcbiAgICAgICAgKSB7XG4gICAgICAgICAgY29uc3QgdHJpbW1lZFZhbHVlID0gb3B0aW9ucy5yZXN1bWUudHJpbSgpXG4gICAgICAgICAgaWYgKHRyaW1tZWRWYWx1ZSkge1xuICAgICAgICAgICAgY29uc3QgbWF0Y2hlcyA9IGF3YWl0IHNlYXJjaFNlc3Npb25zQnlDdXN0b21UaXRsZSh0cmltbWVkVmFsdWUsIHtcbiAgICAgICAgICAgICAgZXhhY3Q6IHRydWUsXG4gICAgICAgICAgICB9KVxuXG4gICAgICAgICAgICBpZiAobWF0Y2hlcy5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgICAgICAgLy8gRXhhY3QgbWF0Y2ggZm91bmQgLSBzdG9yZSBmdWxsIExvZ09wdGlvbiBmb3IgY3Jvc3Mtd29ya3RyZWUgcmVzdW1lXG4gICAgICAgICAgICAgIG1hdGNoZWRMb2cgPSBtYXRjaGVzWzBdIVxuICAgICAgICAgICAgICBtYXliZVNlc3Npb25JZCA9IGdldFNlc3Npb25JZEZyb21Mb2cobWF0Y2hlZExvZykgPz8gbnVsbFxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgLy8gTm8gbWF0Y2ggb3IgbXVsdGlwbGUgbWF0Y2hlcyAtIHVzZSBhcyBzZWFyY2ggdGVybSBmb3IgcGlja2VyXG4gICAgICAgICAgICAgIHNlYXJjaFRlcm0gPSB0cmltbWVkVmFsdWVcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyAtLXJlbW90ZSBhbmQgLS10ZWxlcG9ydCBib3RoIGNyZWF0ZS9yZXN1bWUgQ2xhdWRlIENvZGUgV2ViIChDQ1IpIHNlc3Npb25zLlxuICAgICAgICAvLyBSZW1vdGUgQ29udHJvbCAoLS1yYykgaXMgYSBzZXBhcmF0ZSBmZWF0dXJlIGdhdGVkIGluIGluaXRSZXBsQnJpZGdlLnRzLlxuICAgICAgICBpZiAocmVtb3RlICE9PSBudWxsIHx8IHRlbGVwb3J0KSB7XG4gICAgICAgICAgYXdhaXQgd2FpdEZvclBvbGljeUxpbWl0c1RvTG9hZCgpXG4gICAgICAgICAgaWYgKCFpc1BvbGljeUFsbG93ZWQoJ2FsbG93X3JlbW90ZV9zZXNzaW9ucycpKSB7XG4gICAgICAgICAgICByZXR1cm4gYXdhaXQgZXhpdFdpdGhFcnJvcihcbiAgICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgICAgXCJFcnJvcjogUmVtb3RlIHNlc3Npb25zIGFyZSBkaXNhYmxlZCBieSB5b3VyIG9yZ2FuaXphdGlvbidzIHBvbGljeS5cIixcbiAgICAgICAgICAgICAgKCkgPT4gZ3JhY2VmdWxTaHV0ZG93bigxKSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAocmVtb3RlICE9PSBudWxsKSB7XG4gICAgICAgICAgLy8gQ3JlYXRlIHJlbW90ZSBzZXNzaW9uIChvcHRpb25hbGx5IHdpdGggaW5pdGlhbCBwcm9tcHQpXG4gICAgICAgICAgY29uc3QgaGFzSW5pdGlhbFByb21wdCA9IHJlbW90ZS5sZW5ndGggPiAwXG5cbiAgICAgICAgICAvLyBDaGVjayBpZiBUVUkgbW9kZSBpcyBlbmFibGVkIC0gZGVzY3JpcHRpb24gaXMgb25seSBvcHRpb25hbCBpbiBUVUkgbW9kZVxuICAgICAgICAgIGNvbnN0IGlzUmVtb3RlVHVpRW5hYmxlZCA9IGdldEZlYXR1cmVWYWx1ZV9DQUNIRURfTUFZX0JFX1NUQUxFKFxuICAgICAgICAgICAgJ3Rlbmd1X3JlbW90ZV9iYWNrZW5kJyxcbiAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgIClcbiAgICAgICAgICBpZiAoIWlzUmVtb3RlVHVpRW5hYmxlZCAmJiAhaGFzSW5pdGlhbFByb21wdCkge1xuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IGV4aXRXaXRoRXJyb3IoXG4gICAgICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgICAgICdFcnJvcjogLS1yZW1vdGUgcmVxdWlyZXMgYSBkZXNjcmlwdGlvbi5cXG5Vc2FnZTogY2xhdWRlIC0tcmVtb3RlIFwieW91ciB0YXNrIGRlc2NyaXB0aW9uXCInLFxuICAgICAgICAgICAgICAoKSA9PiBncmFjZWZ1bFNodXRkb3duKDEpLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9yZW1vdGVfY3JlYXRlX3Nlc3Npb24nLCB7XG4gICAgICAgICAgICBoYXNfaW5pdGlhbF9wcm9tcHQ6IFN0cmluZyhcbiAgICAgICAgICAgICAgaGFzSW5pdGlhbFByb21wdCxcbiAgICAgICAgICAgICkgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICB9KVxuXG4gICAgICAgICAgLy8gUGFzcyBjdXJyZW50IGJyYW5jaCBzbyBDQ1IgY2xvbmVzIHRoZSByZXBvIGF0IHRoZSByaWdodCByZXZpc2lvblxuICAgICAgICAgIGNvbnN0IGN1cnJlbnRCcmFuY2ggPSBhd2FpdCBnZXRCcmFuY2goKVxuICAgICAgICAgIGNvbnN0IGNyZWF0ZWRTZXNzaW9uID0gYXdhaXQgdGVsZXBvcnRUb1JlbW90ZVdpdGhFcnJvckhhbmRsaW5nKFxuICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgIGhhc0luaXRpYWxQcm9tcHQgPyByZW1vdGUgOiBudWxsLFxuICAgICAgICAgICAgbmV3IEFib3J0Q29udHJvbGxlcigpLnNpZ25hbCxcbiAgICAgICAgICAgIGN1cnJlbnRCcmFuY2ggfHwgdW5kZWZpbmVkLFxuICAgICAgICAgIClcbiAgICAgICAgICBpZiAoIWNyZWF0ZWRTZXNzaW9uKSB7XG4gICAgICAgICAgICBsb2dFdmVudCgndGVuZ3VfcmVtb3RlX2NyZWF0ZV9zZXNzaW9uX2Vycm9yJywge1xuICAgICAgICAgICAgICBlcnJvcjpcbiAgICAgICAgICAgICAgICAndW5hYmxlX3RvX2NyZWF0ZV9zZXNzaW9uJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHJldHVybiBhd2FpdCBleGl0V2l0aEVycm9yKFxuICAgICAgICAgICAgICByb290LFxuICAgICAgICAgICAgICAnRXJyb3I6IFVuYWJsZSB0byBjcmVhdGUgcmVtb3RlIHNlc3Npb24nLFxuICAgICAgICAgICAgICAoKSA9PiBncmFjZWZ1bFNodXRkb3duKDEpLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cbiAgICAgICAgICBsb2dFdmVudCgndGVuZ3VfcmVtb3RlX2NyZWF0ZV9zZXNzaW9uX3N1Y2Nlc3MnLCB7XG4gICAgICAgICAgICBzZXNzaW9uX2lkOlxuICAgICAgICAgICAgICBjcmVhdGVkU2Vzc2lvbi5pZCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgIH0pXG5cbiAgICAgICAgICAvLyBDaGVjayBpZiBuZXcgcmVtb3RlIFRVSSBtb2RlIGlzIGVuYWJsZWQgdmlhIGZlYXR1cmUgZ2F0ZVxuICAgICAgICAgIGlmICghaXNSZW1vdGVUdWlFbmFibGVkKSB7XG4gICAgICAgICAgICAvLyBPcmlnaW5hbCBiZWhhdmlvcjogcHJpbnQgc2Vzc2lvbiBpbmZvIGFuZCBleGl0XG4gICAgICAgICAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShcbiAgICAgICAgICAgICAgYENyZWF0ZWQgcmVtb3RlIHNlc3Npb246ICR7Y3JlYXRlZFNlc3Npb24udGl0bGV9XFxuYCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKFxuICAgICAgICAgICAgICBgVmlldzogJHtnZXRSZW1vdGVTZXNzaW9uVXJsKGNyZWF0ZWRTZXNzaW9uLmlkKX0/bT0wXFxuYCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKFxuICAgICAgICAgICAgICBgUmVzdW1lIHdpdGg6IGNsYXVkZSAtLXRlbGVwb3J0ICR7Y3JlYXRlZFNlc3Npb24uaWR9XFxuYCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMClcbiAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgwKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIC8vIE5ldyBiZWhhdmlvcjogc3RhcnQgbG9jYWwgVFVJIHdpdGggQ0NSIGVuZ2luZVxuICAgICAgICAgIC8vIE1hcmsgdGhhdCB3ZSdyZSBpbiByZW1vdGUgbW9kZSBmb3IgY29tbWFuZCB2aXNpYmlsaXR5XG4gICAgICAgICAgc2V0SXNSZW1vdGVNb2RlKHRydWUpXG4gICAgICAgICAgc3dpdGNoU2Vzc2lvbihhc1Nlc3Npb25JZChjcmVhdGVkU2Vzc2lvbi5pZCkpXG5cbiAgICAgICAgICAvLyBHZXQgT0F1dGggY3JlZGVudGlhbHMgZm9yIHJlbW90ZSBzZXNzaW9uXG4gICAgICAgICAgbGV0IGFwaUNyZWRzOiB7IGFjY2Vzc1Rva2VuOiBzdHJpbmc7IG9yZ1VVSUQ6IHN0cmluZyB9XG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGFwaUNyZWRzID0gYXdhaXQgcHJlcGFyZUFwaVJlcXVlc3QoKVxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBsb2dFcnJvcih0b0Vycm9yKGVycm9yKSlcbiAgICAgICAgICAgIHJldHVybiBhd2FpdCBleGl0V2l0aEVycm9yKFxuICAgICAgICAgICAgICByb290LFxuICAgICAgICAgICAgICBgRXJyb3I6ICR7ZXJyb3JNZXNzYWdlKGVycm9yKSB8fCAnRmFpbGVkIHRvIGF1dGhlbnRpY2F0ZSd9YCxcbiAgICAgICAgICAgICAgKCkgPT4gZ3JhY2VmdWxTaHV0ZG93bigxKSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBDcmVhdGUgcmVtb3RlIHNlc3Npb24gY29uZmlnIGZvciB0aGUgUkVQTFxuICAgICAgICAgIGNvbnN0IHsgZ2V0Q2xhdWRlQUlPQXV0aFRva2VuczogZ2V0VG9rZW5zRm9yUmVtb3RlIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICAgICAnLi91dGlscy9hdXRoLmpzJ1xuICAgICAgICAgIClcbiAgICAgICAgICBjb25zdCBnZXRBY2Nlc3NUb2tlbkZvclJlbW90ZSA9ICgpOiBzdHJpbmcgPT5cbiAgICAgICAgICAgIGdldFRva2Vuc0ZvclJlbW90ZSgpPy5hY2Nlc3NUb2tlbiA/PyBhcGlDcmVkcy5hY2Nlc3NUb2tlblxuICAgICAgICAgIGNvbnN0IHJlbW90ZVNlc3Npb25Db25maWcgPSBjcmVhdGVSZW1vdGVTZXNzaW9uQ29uZmlnKFxuICAgICAgICAgICAgY3JlYXRlZFNlc3Npb24uaWQsXG4gICAgICAgICAgICBnZXRBY2Nlc3NUb2tlbkZvclJlbW90ZSxcbiAgICAgICAgICAgIGFwaUNyZWRzLm9yZ1VVSUQsXG4gICAgICAgICAgICBoYXNJbml0aWFsUHJvbXB0LFxuICAgICAgICAgIClcblxuICAgICAgICAgIC8vIEFkZCByZW1vdGUgc2Vzc2lvbiBpbmZvIGFzIGluaXRpYWwgc3lzdGVtIG1lc3NhZ2VcbiAgICAgICAgICBjb25zdCByZW1vdGVTZXNzaW9uVXJsID0gYCR7Z2V0UmVtb3RlU2Vzc2lvblVybChjcmVhdGVkU2Vzc2lvbi5pZCl9P209MGBcbiAgICAgICAgICBjb25zdCByZW1vdGVJbmZvTWVzc2FnZSA9IGNyZWF0ZVN5c3RlbU1lc3NhZ2UoXG4gICAgICAgICAgICBgL3JlbW90ZS1jb250cm9sIGlzIGFjdGl2ZS4gQ29kZSBpbiBDTEkgb3IgYXQgJHtyZW1vdGVTZXNzaW9uVXJsfWAsXG4gICAgICAgICAgICAnaW5mbycsXG4gICAgICAgICAgKVxuXG4gICAgICAgICAgLy8gQ3JlYXRlIGluaXRpYWwgdXNlciBtZXNzYWdlIGZyb20gdGhlIHByb21wdCBpZiBwcm92aWRlZCAoQ0NSIGVjaG9lcyBpdCBiYWNrIGJ1dCB3ZSBpZ25vcmUgdGhhdClcbiAgICAgICAgICBjb25zdCBpbml0aWFsVXNlck1lc3NhZ2UgPSBoYXNJbml0aWFsUHJvbXB0XG4gICAgICAgICAgICA/IGNyZWF0ZVVzZXJNZXNzYWdlKHsgY29udGVudDogcmVtb3RlIH0pXG4gICAgICAgICAgICA6IG51bGxcblxuICAgICAgICAgIC8vIFNldCByZW1vdGUgc2Vzc2lvbiBVUkwgaW4gYXBwIHN0YXRlIGZvciBmb290ZXIgaW5kaWNhdG9yXG4gICAgICAgICAgY29uc3QgcmVtb3RlSW5pdGlhbFN0YXRlID0ge1xuICAgICAgICAgICAgLi4uaW5pdGlhbFN0YXRlLFxuICAgICAgICAgICAgcmVtb3RlU2Vzc2lvblVybCxcbiAgICAgICAgICB9XG5cbiAgICAgICAgICAvLyBQcmUtZmlsdGVyIGNvbW1hbmRzIHRvIG9ubHkgaW5jbHVkZSByZW1vdGUtc2FmZSBvbmVzLlxuICAgICAgICAgIC8vIENDUidzIGluaXQgcmVzcG9uc2UgbWF5IGZ1cnRoZXIgcmVmaW5lIHRoZSBsaXN0ICh2aWEgaGFuZGxlUmVtb3RlSW5pdCBpbiBSRVBMKS5cbiAgICAgICAgICBjb25zdCByZW1vdGVDb21tYW5kcyA9IGZpbHRlckNvbW1hbmRzRm9yUmVtb3RlTW9kZShjb21tYW5kcylcbiAgICAgICAgICBhd2FpdCBsYXVuY2hSZXBsKFxuICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgIHsgZ2V0RnBzTWV0cmljcywgc3RhdHMsIGluaXRpYWxTdGF0ZTogcmVtb3RlSW5pdGlhbFN0YXRlIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIGRlYnVnOiBkZWJ1ZyB8fCBkZWJ1Z1RvU3RkZXJyLFxuICAgICAgICAgICAgICBjb21tYW5kczogcmVtb3RlQ29tbWFuZHMsXG4gICAgICAgICAgICAgIGluaXRpYWxUb29sczogW10sXG4gICAgICAgICAgICAgIGluaXRpYWxNZXNzYWdlczogaW5pdGlhbFVzZXJNZXNzYWdlXG4gICAgICAgICAgICAgICAgPyBbcmVtb3RlSW5mb01lc3NhZ2UsIGluaXRpYWxVc2VyTWVzc2FnZV1cbiAgICAgICAgICAgICAgICA6IFtyZW1vdGVJbmZvTWVzc2FnZV0sXG4gICAgICAgICAgICAgIG1jcENsaWVudHM6IFtdLFxuICAgICAgICAgICAgICBhdXRvQ29ubmVjdElkZUZsYWc6IGlkZSxcbiAgICAgICAgICAgICAgbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbixcbiAgICAgICAgICAgICAgZGlzYWJsZVNsYXNoQ29tbWFuZHMsXG4gICAgICAgICAgICAgIHJlbW90ZVNlc3Npb25Db25maWcsXG4gICAgICAgICAgICAgIHRoaW5raW5nQ29uZmlnLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHJlbmRlckFuZFJ1bixcbiAgICAgICAgICApXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH0gZWxzZSBpZiAodGVsZXBvcnQpIHtcbiAgICAgICAgICBpZiAodGVsZXBvcnQgPT09IHRydWUgfHwgdGVsZXBvcnQgPT09ICcnKSB7XG4gICAgICAgICAgICAvLyBJbnRlcmFjdGl2ZSBtb2RlOiBzaG93IHRhc2sgc2VsZWN0b3IgYW5kIGhhbmRsZSByZXN1bWVcbiAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV90ZWxlcG9ydF9pbnRlcmFjdGl2ZV9tb2RlJywge30pXG4gICAgICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgICAgICdzZWxlY3RBbmRSZXN1bWVUZWxlcG9ydFRhc2s6IFN0YXJ0aW5nIHRlbGVwb3J0IGZsb3cuLi4nLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgY29uc3QgdGVsZXBvcnRSZXN1bHQgPSBhd2FpdCBsYXVuY2hUZWxlcG9ydFJlc3VtZVdyYXBwZXIocm9vdClcbiAgICAgICAgICAgIGlmICghdGVsZXBvcnRSZXN1bHQpIHtcbiAgICAgICAgICAgICAgLy8gVXNlciBjYW5jZWxsZWQgb3IgZXJyb3Igb2NjdXJyZWRcbiAgICAgICAgICAgICAgYXdhaXQgZ3JhY2VmdWxTaHV0ZG93bigwKVxuICAgICAgICAgICAgICBwcm9jZXNzLmV4aXQoMClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNvbnN0IHsgYnJhbmNoRXJyb3IgfSA9IGF3YWl0IGNoZWNrT3V0VGVsZXBvcnRlZFNlc3Npb25CcmFuY2goXG4gICAgICAgICAgICAgIHRlbGVwb3J0UmVzdWx0LmJyYW5jaCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIG1lc3NhZ2VzID0gcHJvY2Vzc01lc3NhZ2VzRm9yVGVsZXBvcnRSZXN1bWUoXG4gICAgICAgICAgICAgIHRlbGVwb3J0UmVzdWx0LmxvZyxcbiAgICAgICAgICAgICAgYnJhbmNoRXJyb3IsXG4gICAgICAgICAgICApXG4gICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgdGVsZXBvcnQgPT09ICdzdHJpbmcnKSB7XG4gICAgICAgICAgICBsb2dFdmVudCgndGVuZ3VfdGVsZXBvcnRfcmVzdW1lX3Nlc3Npb24nLCB7XG4gICAgICAgICAgICAgIG1vZGU6ICdkaXJlY3QnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgLy8gRmlyc3QsIGZldGNoIHNlc3Npb24gYW5kIHZhbGlkYXRlIHJlcG9zaXRvcnkgYmVmb3JlIGNoZWNraW5nIGdpdCBzdGF0ZVxuICAgICAgICAgICAgICBjb25zdCBzZXNzaW9uRGF0YSA9IGF3YWl0IGZldGNoU2Vzc2lvbih0ZWxlcG9ydClcbiAgICAgICAgICAgICAgY29uc3QgcmVwb1ZhbGlkYXRpb24gPVxuICAgICAgICAgICAgICAgIGF3YWl0IHZhbGlkYXRlU2Vzc2lvblJlcG9zaXRvcnkoc2Vzc2lvbkRhdGEpXG5cbiAgICAgICAgICAgICAgLy8gSGFuZGxlIHJlcG8gbWlzbWF0Y2ggb3Igbm90IGluIHJlcG8gY2FzZXNcbiAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgIHJlcG9WYWxpZGF0aW9uLnN0YXR1cyA9PT0gJ21pc21hdGNoJyB8fFxuICAgICAgICAgICAgICAgIHJlcG9WYWxpZGF0aW9uLnN0YXR1cyA9PT0gJ25vdF9pbl9yZXBvJ1xuICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICBjb25zdCBzZXNzaW9uUmVwbyA9IHJlcG9WYWxpZGF0aW9uLnNlc3Npb25SZXBvXG4gICAgICAgICAgICAgICAgaWYgKHNlc3Npb25SZXBvKSB7XG4gICAgICAgICAgICAgICAgICAvLyBDaGVjayBmb3Iga25vd24gcGF0aHNcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGtub3duUGF0aHMgPSBnZXRLbm93blBhdGhzRm9yUmVwbyhzZXNzaW9uUmVwbylcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGV4aXN0aW5nUGF0aHMgPSBhd2FpdCBmaWx0ZXJFeGlzdGluZ1BhdGhzKGtub3duUGF0aHMpXG5cbiAgICAgICAgICAgICAgICAgIGlmIChleGlzdGluZ1BhdGhzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gU2hvdyBkaXJlY3Rvcnkgc3dpdGNoIGRpYWxvZ1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBzZWxlY3RlZFBhdGggPSBhd2FpdCBsYXVuY2hUZWxlcG9ydFJlcG9NaXNtYXRjaERpYWxvZyhcbiAgICAgICAgICAgICAgICAgICAgICByb290LFxuICAgICAgICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldFJlcG86IHNlc3Npb25SZXBvLFxuICAgICAgICAgICAgICAgICAgICAgICAgaW5pdGlhbFBhdGhzOiBleGlzdGluZ1BhdGhzLFxuICAgICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgICAgIClcblxuICAgICAgICAgICAgICAgICAgICBpZiAoc2VsZWN0ZWRQYXRoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgLy8gQ2hhbmdlIHRvIHRoZSBzZWxlY3RlZCBkaXJlY3RvcnlcbiAgICAgICAgICAgICAgICAgICAgICBwcm9jZXNzLmNoZGlyKHNlbGVjdGVkUGF0aClcbiAgICAgICAgICAgICAgICAgICAgICBzZXRDd2Qoc2VsZWN0ZWRQYXRoKVxuICAgICAgICAgICAgICAgICAgICAgIHNldE9yaWdpbmFsQ3dkKHNlbGVjdGVkUGF0aClcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAvLyBVc2VyIGNhbmNlbGxlZFxuICAgICAgICAgICAgICAgICAgICAgIGF3YWl0IGdyYWNlZnVsU2h1dGRvd24oMClcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gTm8ga25vd24gcGF0aHMgLSBzaG93IG9yaWdpbmFsIGVycm9yXG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBUZWxlcG9ydE9wZXJhdGlvbkVycm9yKFxuICAgICAgICAgICAgICAgICAgICAgIGBZb3UgbXVzdCBydW4gY2xhdWRlIC0tdGVsZXBvcnQgJHt0ZWxlcG9ydH0gZnJvbSBhIGNoZWNrb3V0IG9mICR7c2Vzc2lvblJlcG99LmAsXG4gICAgICAgICAgICAgICAgICAgICAgY2hhbGsucmVkKFxuICAgICAgICAgICAgICAgICAgICAgICAgYFlvdSBtdXN0IHJ1biBjbGF1ZGUgLS10ZWxlcG9ydCAke3RlbGVwb3J0fSBmcm9tIGEgY2hlY2tvdXQgb2YgJHtjaGFsay5ib2xkKHNlc3Npb25SZXBvKX0uXFxuYCxcbiAgICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9IGVsc2UgaWYgKHJlcG9WYWxpZGF0aW9uLnN0YXR1cyA9PT0gJ2Vycm9yJykge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBUZWxlcG9ydE9wZXJhdGlvbkVycm9yKFxuICAgICAgICAgICAgICAgICAgcmVwb1ZhbGlkYXRpb24uZXJyb3JNZXNzYWdlIHx8ICdGYWlsZWQgdG8gdmFsaWRhdGUgc2Vzc2lvbicsXG4gICAgICAgICAgICAgICAgICBjaGFsay5yZWQoXG4gICAgICAgICAgICAgICAgICAgIGBFcnJvcjogJHtyZXBvVmFsaWRhdGlvbi5lcnJvck1lc3NhZ2UgfHwgJ0ZhaWxlZCB0byB2YWxpZGF0ZSBzZXNzaW9uJ31cXG5gLFxuICAgICAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICBhd2FpdCB2YWxpZGF0ZUdpdFN0YXRlKClcblxuICAgICAgICAgICAgICAvLyBVc2UgcHJvZ3Jlc3MgVUkgZm9yIHRlbGVwb3J0XG4gICAgICAgICAgICAgIGNvbnN0IHsgdGVsZXBvcnRXaXRoUHJvZ3Jlc3MgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICAgICAgICAgICAnLi9jb21wb25lbnRzL1RlbGVwb3J0UHJvZ3Jlc3MuanMnXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGVsZXBvcnRXaXRoUHJvZ3Jlc3Mocm9vdCwgdGVsZXBvcnQpXG4gICAgICAgICAgICAgIC8vIFRyYWNrIHRlbGVwb3J0ZWQgc2Vzc2lvbiBmb3IgcmVsaWFiaWxpdHkgbG9nZ2luZ1xuICAgICAgICAgICAgICBzZXRUZWxlcG9ydGVkU2Vzc2lvbkluZm8oeyBzZXNzaW9uSWQ6IHRlbGVwb3J0IH0pXG4gICAgICAgICAgICAgIG1lc3NhZ2VzID0gcmVzdWx0Lm1lc3NhZ2VzXG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiBUZWxlcG9ydE9wZXJhdGlvbkVycm9yKSB7XG4gICAgICAgICAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoZXJyb3IuZm9ybWF0dGVkTWVzc2FnZSArICdcXG4nKVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGxvZ0Vycm9yKGVycm9yKVxuICAgICAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgICAgICAgY2hhbGsucmVkKGBFcnJvcjogJHtlcnJvck1lc3NhZ2UoZXJyb3IpfVxcbmApLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICBhd2FpdCBncmFjZWZ1bFNodXRkb3duKDEpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChcImV4dGVybmFsXCIgPT09ICdhbnQnKSB7XG4gICAgICAgICAgaWYgKFxuICAgICAgICAgICAgb3B0aW9ucy5yZXN1bWUgJiZcbiAgICAgICAgICAgIHR5cGVvZiBvcHRpb25zLnJlc3VtZSA9PT0gJ3N0cmluZycgJiZcbiAgICAgICAgICAgICFtYXliZVNlc3Npb25JZFxuICAgICAgICAgICkge1xuICAgICAgICAgICAgLy8gQ2hlY2sgZm9yIGNjc2hhcmUgVVJMIChlLmcuIGh0dHBzOi8vZ28vY2NzaGFyZS9ib3Jpcy0yMDI2MDMxMS0yMTEwMzYpXG4gICAgICAgICAgICBjb25zdCB7IHBhcnNlQ2NzaGFyZUlkLCBsb2FkQ2NzaGFyZSB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgICAnLi91dGlscy9jY3NoYXJlUmVzdW1lLmpzJ1xuICAgICAgICAgICAgKVxuICAgICAgICAgICAgY29uc3QgY2NzaGFyZUlkID0gcGFyc2VDY3NoYXJlSWQob3B0aW9ucy5yZXN1bWUpXG4gICAgICAgICAgICBpZiAoY2NzaGFyZUlkKSB7XG4gICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVzdW1lU3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKVxuICAgICAgICAgICAgICAgIGNvbnN0IGxvZ09wdGlvbiA9IGF3YWl0IGxvYWRDY3NoYXJlKGNjc2hhcmVJZClcbiAgICAgICAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBsb2FkQ29udmVyc2F0aW9uRm9yUmVzdW1lKFxuICAgICAgICAgICAgICAgICAgbG9nT3B0aW9uLFxuICAgICAgICAgICAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICBwcm9jZXNzZWRSZXN1bWUgPSBhd2FpdCBwcm9jZXNzUmVzdW1lZENvbnZlcnNhdGlvbihcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0LFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgZm9ya1Nlc3Npb246IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgdHJhbnNjcmlwdFBhdGg6IHJlc3VsdC5mdWxsUGF0aCxcbiAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgcmVzdW1lQ29udGV4dCxcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgIGlmIChwcm9jZXNzZWRSZXN1bWUucmVzdG9yZWRBZ2VudERlZikge1xuICAgICAgICAgICAgICAgICAgICBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uID0gcHJvY2Vzc2VkUmVzdW1lLnJlc3RvcmVkQWdlbnREZWZcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9zZXNzaW9uX3Jlc3VtZWQnLCB7XG4gICAgICAgICAgICAgICAgICAgIGVudHJ5cG9pbnQ6XG4gICAgICAgICAgICAgICAgICAgICAgJ2Njc2hhcmUnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgIHJlc3VtZV9kdXJhdGlvbl9tczogTWF0aC5yb3VuZChcbiAgICAgICAgICAgICAgICAgICAgICBwZXJmb3JtYW5jZS5ub3coKSAtIHJlc3VtZVN0YXJ0LFxuICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3Nlc3Npb25fcmVzdW1lZCcsIHtcbiAgICAgICAgICAgICAgICAgICAgZW50cnlwb2ludDpcbiAgICAgICAgICAgICAgICAgICAgICAnY2NzaGFyZScgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICBsb2dFdmVudCgndGVuZ3Vfc2Vzc2lvbl9yZXN1bWVkJywge1xuICAgICAgICAgICAgICAgICAgZW50cnlwb2ludDpcbiAgICAgICAgICAgICAgICAgICAgJ2Njc2hhcmUnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIGxvZ0Vycm9yKGVycm9yKVxuICAgICAgICAgICAgICAgIGF3YWl0IGV4aXRXaXRoRXJyb3IoXG4gICAgICAgICAgICAgICAgICByb290LFxuICAgICAgICAgICAgICAgICAgYFVuYWJsZSB0byByZXN1bWUgZnJvbSBjY3NoYXJlOiAke2Vycm9yTWVzc2FnZShlcnJvcil9YCxcbiAgICAgICAgICAgICAgICAgICgpID0+IGdyYWNlZnVsU2h1dGRvd24oMSksXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICBjb25zdCByZXNvbHZlZFBhdGggPSByZXNvbHZlKG9wdGlvbnMucmVzdW1lKVxuICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VtZVN0YXJ0ID0gcGVyZm9ybWFuY2Uubm93KClcbiAgICAgICAgICAgICAgICBsZXQgbG9nT3B0aW9uXG4gICAgICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAgIC8vIEF0dGVtcHQgdG8gbG9hZCBhcyBhIHRyYW5zY3JpcHQgZmlsZTsgRU5PRU5UIGZhbGxzIHRocm91Z2ggdG8gc2Vzc2lvbi1JRCBoYW5kbGluZ1xuICAgICAgICAgICAgICAgICAgbG9nT3B0aW9uID0gYXdhaXQgbG9hZFRyYW5zY3JpcHRGcm9tRmlsZShyZXNvbHZlZFBhdGgpXG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgIGlmICghaXNFTk9FTlQoZXJyb3IpKSB0aHJvdyBlcnJvclxuICAgICAgICAgICAgICAgICAgLy8gRU5PRU5UOiBub3QgYSBmaWxlIHBhdGgg4oCUIGZhbGwgdGhyb3VnaCB0byBzZXNzaW9uLUlEIGhhbmRsaW5nXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChsb2dPcHRpb24pIHtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGxvYWRDb252ZXJzYXRpb25Gb3JSZXN1bWUoXG4gICAgICAgICAgICAgICAgICAgIGxvZ09wdGlvbixcbiAgICAgICAgICAgICAgICAgICAgdW5kZWZpbmVkIC8qIHNvdXJjZUZpbGUgKi8sXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgICAgICAgICAgIHByb2Nlc3NlZFJlc3VtZSA9IGF3YWl0IHByb2Nlc3NSZXN1bWVkQ29udmVyc2F0aW9uKFxuICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCxcbiAgICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBmb3JrU2Vzc2lvbjogISFvcHRpb25zLmZvcmtTZXNzaW9uLFxuICAgICAgICAgICAgICAgICAgICAgICAgdHJhbnNjcmlwdFBhdGg6IHJlc3VsdC5mdWxsUGF0aCxcbiAgICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICAgIHJlc3VtZUNvbnRleHQsXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgICAgaWYgKHByb2Nlc3NlZFJlc3VtZS5yZXN0b3JlZEFnZW50RGVmKSB7XG4gICAgICAgICAgICAgICAgICAgICAgbWFpblRocmVhZEFnZW50RGVmaW5pdGlvbiA9XG4gICAgICAgICAgICAgICAgICAgICAgICBwcm9jZXNzZWRSZXN1bWUucmVzdG9yZWRBZ2VudERlZlxuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9zZXNzaW9uX3Jlc3VtZWQnLCB7XG4gICAgICAgICAgICAgICAgICAgICAgZW50cnlwb2ludDpcbiAgICAgICAgICAgICAgICAgICAgICAgICdmaWxlJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICAgICAgICAgICAgICAgICAgcmVzdW1lX2R1cmF0aW9uX21zOiBNYXRoLnJvdW5kKFxuICAgICAgICAgICAgICAgICAgICAgICAgcGVyZm9ybWFuY2Uubm93KCkgLSByZXN1bWVTdGFydCxcbiAgICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3Nlc3Npb25fcmVzdW1lZCcsIHtcbiAgICAgICAgICAgICAgICAgICAgICBlbnRyeXBvaW50OlxuICAgICAgICAgICAgICAgICAgICAgICAgJ2ZpbGUnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAgICAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9zZXNzaW9uX3Jlc3VtZWQnLCB7XG4gICAgICAgICAgICAgICAgICBlbnRyeXBvaW50OlxuICAgICAgICAgICAgICAgICAgICAnZmlsZScgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICAgICAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgbG9nRXJyb3IoZXJyb3IpXG4gICAgICAgICAgICAgICAgYXdhaXQgZXhpdFdpdGhFcnJvcihcbiAgICAgICAgICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgICAgICAgICBgVW5hYmxlIHRvIGxvYWQgdHJhbnNjcmlwdCBmcm9tIGZpbGU6ICR7b3B0aW9ucy5yZXN1bWV9YCxcbiAgICAgICAgICAgICAgICAgICgpID0+IGdyYWNlZnVsU2h1dGRvd24oMSksXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgbm90IGxvYWRlZCBhcyBhIGZpbGUsIHRyeSBhcyBzZXNzaW9uIElEXG4gICAgICAgIGlmIChtYXliZVNlc3Npb25JZCkge1xuICAgICAgICAgIC8vIFJlc3VtZSBzcGVjaWZpYyBzZXNzaW9uIGJ5IElEXG4gICAgICAgICAgY29uc3Qgc2Vzc2lvbklkID0gbWF5YmVTZXNzaW9uSWRcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgY29uc3QgcmVzdW1lU3RhcnQgPSBwZXJmb3JtYW5jZS5ub3coKVxuICAgICAgICAgICAgLy8gVXNlIG1hdGNoZWRMb2cgaWYgYXZhaWxhYmxlIChmb3IgY3Jvc3Mtd29ya3RyZWUgcmVzdW1lIGJ5IGN1c3RvbSB0aXRsZSlcbiAgICAgICAgICAgIC8vIE90aGVyd2lzZSBmYWxsIGJhY2sgdG8gc2Vzc2lvbklkIHN0cmluZyAoZm9yIGRpcmVjdCBVVUlEIHJlc3VtZSlcbiAgICAgICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGxvYWRDb252ZXJzYXRpb25Gb3JSZXN1bWUoXG4gICAgICAgICAgICAgIG1hdGNoZWRMb2cgPz8gc2Vzc2lvbklkLFxuICAgICAgICAgICAgICB1bmRlZmluZWQsXG4gICAgICAgICAgICApXG5cbiAgICAgICAgICAgIGlmICghcmVzdWx0KSB7XG4gICAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9zZXNzaW9uX3Jlc3VtZWQnLCB7XG4gICAgICAgICAgICAgICAgZW50cnlwb2ludDpcbiAgICAgICAgICAgICAgICAgICdjbGlfZmxhZycgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgcmV0dXJuIGF3YWl0IGV4aXRXaXRoRXJyb3IoXG4gICAgICAgICAgICAgICAgcm9vdCxcbiAgICAgICAgICAgICAgICBgTm8gY29udmVyc2F0aW9uIGZvdW5kIHdpdGggc2Vzc2lvbiBJRDogJHtzZXNzaW9uSWR9YCxcbiAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBmdWxsUGF0aCA9IG1hdGNoZWRMb2c/LmZ1bGxQYXRoID8/IHJlc3VsdC5mdWxsUGF0aFxuICAgICAgICAgICAgcHJvY2Vzc2VkUmVzdW1lID0gYXdhaXQgcHJvY2Vzc1Jlc3VtZWRDb252ZXJzYXRpb24oXG4gICAgICAgICAgICAgIHJlc3VsdCxcbiAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIGZvcmtTZXNzaW9uOiAhIW9wdGlvbnMuZm9ya1Nlc3Npb24sXG4gICAgICAgICAgICAgICAgc2Vzc2lvbklkT3ZlcnJpZGU6IHNlc3Npb25JZCxcbiAgICAgICAgICAgICAgICB0cmFuc2NyaXB0UGF0aDogZnVsbFBhdGgsXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgIHJlc3VtZUNvbnRleHQsXG4gICAgICAgICAgICApXG5cbiAgICAgICAgICAgIGlmIChwcm9jZXNzZWRSZXN1bWUucmVzdG9yZWRBZ2VudERlZikge1xuICAgICAgICAgICAgICBtYWluVGhyZWFkQWdlbnREZWZpbml0aW9uID0gcHJvY2Vzc2VkUmVzdW1lLnJlc3RvcmVkQWdlbnREZWZcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9zZXNzaW9uX3Jlc3VtZWQnLCB7XG4gICAgICAgICAgICAgIGVudHJ5cG9pbnQ6XG4gICAgICAgICAgICAgICAgJ2NsaV9mbGFnJyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgICBzdWNjZXNzOiB0cnVlLFxuICAgICAgICAgICAgICByZXN1bWVfZHVyYXRpb25fbXM6IE1hdGgucm91bmQocGVyZm9ybWFuY2Uubm93KCkgLSByZXN1bWVTdGFydCksXG4gICAgICAgICAgICB9KVxuICAgICAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgICAgICBsb2dFdmVudCgndGVuZ3Vfc2Vzc2lvbl9yZXN1bWVkJywge1xuICAgICAgICAgICAgICBlbnRyeXBvaW50OlxuICAgICAgICAgICAgICAgICdjbGlfZmxhZycgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgICAgICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgbG9nRXJyb3IoZXJyb3IpXG4gICAgICAgICAgICBhd2FpdCBleGl0V2l0aEVycm9yKHJvb3QsIGBGYWlsZWQgdG8gcmVzdW1lIHNlc3Npb24gJHtzZXNzaW9uSWR9YClcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBd2FpdCBmaWxlIGRvd25sb2FkcyBiZWZvcmUgcmVuZGVyaW5nIFJFUEwgKGZpbGVzIG11c3QgYmUgYXZhaWxhYmxlKVxuICAgICAgICBpZiAoZmlsZURvd25sb2FkUHJvbWlzZSkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCByZXN1bHRzID0gYXdhaXQgZmlsZURvd25sb2FkUHJvbWlzZVxuICAgICAgICAgICAgY29uc3QgZmFpbGVkQ291bnQgPSBjb3VudChyZXN1bHRzLCByID0+ICFyLnN1Y2Nlc3MpXG4gICAgICAgICAgICBpZiAoZmFpbGVkQ291bnQgPiAwKSB7XG4gICAgICAgICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICAgICAgIGNoYWxrLnllbGxvdyhcbiAgICAgICAgICAgICAgICAgIGBXYXJuaW5nOiAke2ZhaWxlZENvdW50fS8ke3Jlc3VsdHMubGVuZ3RofSBmaWxlKHMpIGZhaWxlZCB0byBkb3dubG9hZC5cXG5gLFxuICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgcmV0dXJuIGF3YWl0IGV4aXRXaXRoRXJyb3IoXG4gICAgICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgICAgIGBFcnJvciBkb3dubG9hZGluZyBmaWxlczogJHtlcnJvck1lc3NhZ2UoZXJyb3IpfWAsXG4gICAgICAgICAgICApXG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gSWYgd2UgaGF2ZSBhIHByb2Nlc3NlZCByZXN1bWUgb3IgdGVsZXBvcnQgbWVzc2FnZXMsIHJlbmRlciB0aGUgUkVQTFxuICAgICAgICBjb25zdCByZXN1bWVEYXRhID1cbiAgICAgICAgICBwcm9jZXNzZWRSZXN1bWUgPz9cbiAgICAgICAgICAoQXJyYXkuaXNBcnJheShtZXNzYWdlcylcbiAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgIG1lc3NhZ2VzLFxuICAgICAgICAgICAgICAgIGZpbGVIaXN0b3J5U25hcHNob3RzOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgYWdlbnROYW1lOiB1bmRlZmluZWQsXG4gICAgICAgICAgICAgICAgYWdlbnRDb2xvcjogdW5kZWZpbmVkIGFzIEFnZW50Q29sb3JOYW1lIHwgdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgIHJlc3RvcmVkQWdlbnREZWY6IG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24sXG4gICAgICAgICAgICAgICAgaW5pdGlhbFN0YXRlLFxuICAgICAgICAgICAgICAgIGNvbnRlbnRSZXBsYWNlbWVudHM6IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgOiB1bmRlZmluZWQpXG4gICAgICAgIGlmIChyZXN1bWVEYXRhKSB7XG4gICAgICAgICAgbWF5YmVBY3RpdmF0ZVByb2FjdGl2ZShvcHRpb25zKVxuICAgICAgICAgIG1heWJlQWN0aXZhdGVCcmllZihvcHRpb25zKVxuXG4gICAgICAgICAgYXdhaXQgbGF1bmNoUmVwbChcbiAgICAgICAgICAgIHJvb3QsXG4gICAgICAgICAgICB7IGdldEZwc01ldHJpY3MsIHN0YXRzLCBpbml0aWFsU3RhdGU6IHJlc3VtZURhdGEuaW5pdGlhbFN0YXRlIH0sXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgIC4uLnNlc3Npb25Db25maWcsXG4gICAgICAgICAgICAgIG1haW5UaHJlYWRBZ2VudERlZmluaXRpb246XG4gICAgICAgICAgICAgICAgcmVzdW1lRGF0YS5yZXN0b3JlZEFnZW50RGVmID8/IG1haW5UaHJlYWRBZ2VudERlZmluaXRpb24sXG4gICAgICAgICAgICAgIGluaXRpYWxNZXNzYWdlczogcmVzdW1lRGF0YS5tZXNzYWdlcyxcbiAgICAgICAgICAgICAgaW5pdGlhbEZpbGVIaXN0b3J5U25hcHNob3RzOiByZXN1bWVEYXRhLmZpbGVIaXN0b3J5U25hcHNob3RzLFxuICAgICAgICAgICAgICBpbml0aWFsQ29udGVudFJlcGxhY2VtZW50czogcmVzdW1lRGF0YS5jb250ZW50UmVwbGFjZW1lbnRzLFxuICAgICAgICAgICAgICBpbml0aWFsQWdlbnROYW1lOiByZXN1bWVEYXRhLmFnZW50TmFtZSxcbiAgICAgICAgICAgICAgaW5pdGlhbEFnZW50Q29sb3I6IHJlc3VtZURhdGEuYWdlbnRDb2xvcixcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICByZW5kZXJBbmRSdW4sXG4gICAgICAgICAgKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIFNob3cgaW50ZXJhY3RpdmUgc2VsZWN0b3IgKGluY2x1ZGVzIHNhbWUtcmVwbyB3b3JrdHJlZXMpXG4gICAgICAgICAgLy8gTm90ZTogUmVzdW1lQ29udmVyc2F0aW9uIGxvYWRzIGxvZ3MgaW50ZXJuYWxseSB0byBlbnN1cmUgcHJvcGVyIEdDIGFmdGVyIHNlbGVjdGlvblxuICAgICAgICAgIGF3YWl0IGxhdW5jaFJlc3VtZUNob29zZXIoXG4gICAgICAgICAgICByb290LFxuICAgICAgICAgICAgeyBnZXRGcHNNZXRyaWNzLCBzdGF0cywgaW5pdGlhbFN0YXRlIH0sXG4gICAgICAgICAgICBnZXRXb3JrdHJlZVBhdGhzKGdldE9yaWdpbmFsQ3dkKCkpLFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAuLi5zZXNzaW9uQ29uZmlnLFxuICAgICAgICAgICAgICBpbml0aWFsU2VhcmNoUXVlcnk6IHNlYXJjaFRlcm0sXG4gICAgICAgICAgICAgIGZvcmtTZXNzaW9uOiBvcHRpb25zLmZvcmtTZXNzaW9uLFxuICAgICAgICAgICAgICBmaWx0ZXJCeVByLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICApXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIFBhc3MgdW5yZXNvbHZlZCBob29rcyBwcm9taXNlIHRvIFJFUEwgc28gaXQgY2FuIHJlbmRlciBpbW1lZGlhdGVseVxuICAgICAgICAvLyBpbnN0ZWFkIG9mIGJsb2NraW5nIH41MDBtcyB3YWl0aW5nIGZvciBTZXNzaW9uU3RhcnQgaG9va3MgdG8gZmluaXNoLlxuICAgICAgICAvLyBSRVBMIHdpbGwgaW5qZWN0IGhvb2sgbWVzc2FnZXMgd2hlbiB0aGV5IHJlc29sdmUgYW5kIGF3YWl0IHRoZW0gYmVmb3JlXG4gICAgICAgIC8vIHRoZSBmaXJzdCBBUEkgY2FsbCBzbyB0aGUgbW9kZWwgYWx3YXlzIHNlZXMgaG9vayBjb250ZXh0LlxuICAgICAgICBjb25zdCBwZW5kaW5nSG9va01lc3NhZ2VzID1cbiAgICAgICAgICBob29rc1Byb21pc2UgJiYgaG9va01lc3NhZ2VzLmxlbmd0aCA9PT0gMCA/IGhvb2tzUHJvbWlzZSA6IHVuZGVmaW5lZFxuXG4gICAgICAgIHByb2ZpbGVDaGVja3BvaW50KCdhY3Rpb25fYWZ0ZXJfaG9va3MnKVxuICAgICAgICBtYXliZUFjdGl2YXRlUHJvYWN0aXZlKG9wdGlvbnMpXG4gICAgICAgIG1heWJlQWN0aXZhdGVCcmllZihvcHRpb25zKVxuICAgICAgICAvLyBQZXJzaXN0IHRoZSBjdXJyZW50IG1vZGUgZm9yIGZyZXNoIHNlc3Npb25zIHNvIGZ1dHVyZSByZXN1bWVzIGtub3cgd2hhdCBtb2RlIHdhcyB1c2VkXG4gICAgICAgIGlmIChmZWF0dXJlKCdDT09SRElOQVRPUl9NT0RFJykpIHtcbiAgICAgICAgICBzYXZlTW9kZShcbiAgICAgICAgICAgIGNvb3JkaW5hdG9yTW9kZU1vZHVsZT8uaXNDb29yZGluYXRvck1vZGUoKVxuICAgICAgICAgICAgICA/ICdjb29yZGluYXRvcidcbiAgICAgICAgICAgICAgOiAnbm9ybWFsJyxcbiAgICAgICAgICApXG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZiBsYXVuY2hlZCB2aWEgYSBkZWVwIGxpbmssIHNob3cgYSBwcm92ZW5hbmNlIGJhbm5lciBzbyB0aGUgdXNlclxuICAgICAgICAvLyBrbm93cyB0aGUgc2Vzc2lvbiBvcmlnaW5hdGVkIGV4dGVybmFsbHkuIExpbnV4IHhkZy1vcGVuIGFuZFxuICAgICAgICAvLyBicm93c2VycyB3aXRoIFwiYWx3YXlzIGFsbG93XCIgc2V0IGRpc3BhdGNoIHRoZSBsaW5rIHdpdGggbm8gT1MtbGV2ZWxcbiAgICAgICAgLy8gY29uZmlybWF0aW9uLCBzbyB0aGlzIGlzIHRoZSBvbmx5IHNpZ25hbCB0aGUgdXNlciBnZXRzIHRoYXQgdGhlXG4gICAgICAgIC8vIHByb21wdCDigJQgYW5kIHRoZSB3b3JraW5nIGRpcmVjdG9yeSAvIENMQVVERS5tZCBpdCBpbXBsaWVzIOKAlCBjYW1lXG4gICAgICAgIC8vIGZyb20gYW4gZXh0ZXJuYWwgc291cmNlIHJhdGhlciB0aGFuIHNvbWV0aGluZyB0aGV5IHR5cGVkLlxuICAgICAgICBsZXQgZGVlcExpbmtCYW5uZXI6IFJldHVyblR5cGU8dHlwZW9mIGNyZWF0ZVN5c3RlbU1lc3NhZ2U+IHwgbnVsbCA9IG51bGxcbiAgICAgICAgaWYgKGZlYXR1cmUoJ0xPREVTVE9ORScpKSB7XG4gICAgICAgICAgaWYgKG9wdGlvbnMuZGVlcExpbmtPcmlnaW4pIHtcbiAgICAgICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9kZWVwX2xpbmtfb3BlbmVkJywge1xuICAgICAgICAgICAgICBoYXNfcHJlZmlsbDogQm9vbGVhbihvcHRpb25zLnByZWZpbGwpLFxuICAgICAgICAgICAgICBoYXNfcmVwbzogQm9vbGVhbihvcHRpb25zLmRlZXBMaW5rUmVwbyksXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgZGVlcExpbmtCYW5uZXIgPSBjcmVhdGVTeXN0ZW1NZXNzYWdlKFxuICAgICAgICAgICAgICBidWlsZERlZXBMaW5rQmFubmVyKHtcbiAgICAgICAgICAgICAgICBjd2Q6IGdldEN3ZCgpLFxuICAgICAgICAgICAgICAgIHByZWZpbGxMZW5ndGg6IG9wdGlvbnMucHJlZmlsbD8ubGVuZ3RoLFxuICAgICAgICAgICAgICAgIHJlcG86IG9wdGlvbnMuZGVlcExpbmtSZXBvLFxuICAgICAgICAgICAgICAgIGxhc3RGZXRjaDpcbiAgICAgICAgICAgICAgICAgIG9wdGlvbnMuZGVlcExpbmtMYXN0RmV0Y2ggIT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgICA/IG5ldyBEYXRlKG9wdGlvbnMuZGVlcExpbmtMYXN0RmV0Y2gpXG4gICAgICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICB9KSxcbiAgICAgICAgICAgICAgJ3dhcm5pbmcnLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH0gZWxzZSBpZiAob3B0aW9ucy5wcmVmaWxsKSB7XG4gICAgICAgICAgICBkZWVwTGlua0Jhbm5lciA9IGNyZWF0ZVN5c3RlbU1lc3NhZ2UoXG4gICAgICAgICAgICAgICdMYXVuY2hlZCB3aXRoIGEgcHJlLWZpbGxlZCBwcm9tcHQg4oCUIHJldmlldyBpdCBiZWZvcmUgcHJlc3NpbmcgRW50ZXIuJyxcbiAgICAgICAgICAgICAgJ3dhcm5pbmcnLFxuICAgICAgICAgICAgKVxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBjb25zdCBpbml0aWFsTWVzc2FnZXMgPSBkZWVwTGlua0Jhbm5lclxuICAgICAgICAgID8gW2RlZXBMaW5rQmFubmVyLCAuLi5ob29rTWVzc2FnZXNdXG4gICAgICAgICAgOiBob29rTWVzc2FnZXMubGVuZ3RoID4gMFxuICAgICAgICAgICAgPyBob29rTWVzc2FnZXNcbiAgICAgICAgICAgIDogdW5kZWZpbmVkXG5cbiAgICAgICAgYXdhaXQgbGF1bmNoUmVwbChcbiAgICAgICAgICByb290LFxuICAgICAgICAgIHsgZ2V0RnBzTWV0cmljcywgc3RhdHMsIGluaXRpYWxTdGF0ZSB9LFxuICAgICAgICAgIHtcbiAgICAgICAgICAgIC4uLnNlc3Npb25Db25maWcsXG4gICAgICAgICAgICBpbml0aWFsTWVzc2FnZXMsXG4gICAgICAgICAgICBwZW5kaW5nSG9va01lc3NhZ2VzLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgcmVuZGVyQW5kUnVuLFxuICAgICAgICApXG4gICAgICB9XG4gICAgfSlcbiAgICAudmVyc2lvbihcbiAgICAgIGAke01BQ1JPLlZFUlNJT059IChDbGF1ZGUgQ29kZSlgLFxuICAgICAgJy12LCAtLXZlcnNpb24nLFxuICAgICAgJ091dHB1dCB0aGUgdmVyc2lvbiBudW1iZXInLFxuICAgIClcblxuICAvLyBXb3JrdHJlZSBmbGFnc1xuICBwcm9ncmFtLm9wdGlvbihcbiAgICAnLXcsIC0td29ya3RyZWUgW25hbWVdJyxcbiAgICAnQ3JlYXRlIGEgbmV3IGdpdCB3b3JrdHJlZSBmb3IgdGhpcyBzZXNzaW9uIChvcHRpb25hbGx5IHNwZWNpZnkgYSBuYW1lKScsXG4gIClcbiAgcHJvZ3JhbS5vcHRpb24oXG4gICAgJy0tdG11eCcsXG4gICAgJ0NyZWF0ZSBhIHRtdXggc2Vzc2lvbiBmb3IgdGhlIHdvcmt0cmVlIChyZXF1aXJlcyAtLXdvcmt0cmVlKS4gVXNlcyBpVGVybTIgbmF0aXZlIHBhbmVzIHdoZW4gYXZhaWxhYmxlOyB1c2UgLS10bXV4PWNsYXNzaWMgZm9yIHRyYWRpdGlvbmFsIHRtdXguJyxcbiAgKVxuXG4gIGlmIChjYW5Vc2VyQ29uZmlndXJlQWR2aXNvcigpKSB7XG4gICAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1hZHZpc29yIDxtb2RlbD4nLFxuICAgICAgICAnRW5hYmxlIHRoZSBzZXJ2ZXItc2lkZSBhZHZpc29yIHRvb2wgd2l0aCB0aGUgc3BlY2lmaWVkIG1vZGVsIChhbGlhcyBvciBmdWxsIElEKS4nLFxuICAgICAgKS5oaWRlSGVscCgpLFxuICAgIClcbiAgfVxuXG4gIGlmIChcImV4dGVybmFsXCIgPT09ICdhbnQnKSB7XG4gICAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1kZWxlZ2F0ZS1wZXJtaXNzaW9ucycsXG4gICAgICAgICdbQU5ULU9OTFldIEFsaWFzIGZvciAtLXBlcm1pc3Npb24tbW9kZSBhdXRvLicsXG4gICAgICApLmltcGxpZXMoeyBwZXJtaXNzaW9uTW9kZTogJ2F1dG8nIH0pLFxuICAgIClcbiAgICBwcm9ncmFtLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLWRhbmdlcm91c2x5LXNraXAtcGVybWlzc2lvbnMtd2l0aC1jbGFzc2lmaWVycycsXG4gICAgICAgICdbQU5ULU9OTFldIERlcHJlY2F0ZWQgYWxpYXMgZm9yIC0tcGVybWlzc2lvbi1tb2RlIGF1dG8uJyxcbiAgICAgIClcbiAgICAgICAgLmhpZGVIZWxwKClcbiAgICAgICAgLmltcGxpZXMoeyBwZXJtaXNzaW9uTW9kZTogJ2F1dG8nIH0pLFxuICAgIClcbiAgICBwcm9ncmFtLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLWFmaycsXG4gICAgICAgICdbQU5ULU9OTFldIERlcHJlY2F0ZWQgYWxpYXMgZm9yIC0tcGVybWlzc2lvbi1tb2RlIGF1dG8uJyxcbiAgICAgIClcbiAgICAgICAgLmhpZGVIZWxwKClcbiAgICAgICAgLmltcGxpZXMoeyBwZXJtaXNzaW9uTW9kZTogJ2F1dG8nIH0pLFxuICAgIClcbiAgICBwcm9ncmFtLmFkZE9wdGlvbihcbiAgICAgIG5ldyBPcHRpb24oXG4gICAgICAgICctLXRhc2tzIFtpZF0nLFxuICAgICAgICAnW0FOVC1PTkxZXSBUYXNrcyBtb2RlOiB3YXRjaCBmb3IgdGFza3MgYW5kIGF1dG8tcHJvY2VzcyB0aGVtLiBPcHRpb25hbCBpZCBpcyB1c2VkIGFzIGJvdGggdGhlIHRhc2sgbGlzdCBJRCBhbmQgYWdlbnQgSUQgKGRlZmF1bHRzIHRvIFwidGFza2xpc3RcIikuJyxcbiAgICAgIClcbiAgICAgICAgLmFyZ1BhcnNlcihTdHJpbmcpXG4gICAgICAgIC5oaWRlSGVscCgpLFxuICAgIClcbiAgICBwcm9ncmFtLm9wdGlvbihcbiAgICAgICctLWFnZW50LXRlYW1zJyxcbiAgICAgICdbQU5ULU9OTFldIEZvcmNlIENsYXVkZSB0byB1c2UgbXVsdGktYWdlbnQgbW9kZSBmb3Igc29sdmluZyBwcm9ibGVtcycsXG4gICAgICAoKSA9PiB0cnVlLFxuICAgIClcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbignLS1lbmFibGUtYXV0by1tb2RlJywgJ09wdCBpbiB0byBhdXRvIG1vZGUnKS5oaWRlSGVscCgpLFxuICAgIClcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdQUk9BQ1RJVkUnKSB8fCBmZWF0dXJlKCdLQUlST1MnKSkge1xuICAgIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbignLS1wcm9hY3RpdmUnLCAnU3RhcnQgaW4gcHJvYWN0aXZlIGF1dG9ub21vdXMgbW9kZScpLFxuICAgIClcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdVRFNfSU5CT1gnKSkge1xuICAgIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tbWVzc2FnaW5nLXNvY2tldC1wYXRoIDxwYXRoPicsXG4gICAgICAgICdVbml4IGRvbWFpbiBzb2NrZXQgcGF0aCBmb3IgdGhlIFVEUyBtZXNzYWdpbmcgc2VydmVyIChkZWZhdWx0cyB0byBhIHRtcCBwYXRoKScsXG4gICAgICApLFxuICAgIClcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKSkge1xuICAgIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tYnJpZWYnLFxuICAgICAgICAnRW5hYmxlIFNlbmRVc2VyTWVzc2FnZSB0b29sIGZvciBhZ2VudC10by11c2VyIGNvbW11bmljYXRpb24nLFxuICAgICAgKSxcbiAgICApXG4gIH1cbiAgaWYgKGZlYXR1cmUoJ0tBSVJPUycpKSB7XG4gICAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1hc3Npc3RhbnQnLFxuICAgICAgICAnRm9yY2UgYXNzaXN0YW50IG1vZGUgKEFnZW50IFNESyBkYWVtb24gdXNlKScsXG4gICAgICApLmhpZGVIZWxwKCksXG4gICAgKVxuICB9XG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQ0hBTk5FTFMnKSkge1xuICAgIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tY2hhbm5lbHMgPHNlcnZlcnMuLi4+JyxcbiAgICAgICAgJ01DUCBzZXJ2ZXJzIHdob3NlIGNoYW5uZWwgbm90aWZpY2F0aW9ucyAoaW5ib3VuZCBwdXNoKSBzaG91bGQgcmVnaXN0ZXIgdGhpcyBzZXNzaW9uLiBTcGFjZS1zZXBhcmF0ZWQgc2VydmVyIG5hbWVzLicsXG4gICAgICApLmhpZGVIZWxwKCksXG4gICAgKVxuICAgIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgICAgbmV3IE9wdGlvbihcbiAgICAgICAgJy0tZGFuZ2Vyb3VzbHktbG9hZC1kZXZlbG9wbWVudC1jaGFubmVscyA8c2VydmVycy4uLj4nLFxuICAgICAgICAnTG9hZCBjaGFubmVsIHNlcnZlcnMgbm90IG9uIHRoZSBhcHByb3ZlZCBhbGxvd2xpc3QuIEZvciBsb2NhbCBjaGFubmVsIGRldmVsb3BtZW50IG9ubHkuIFNob3dzIGEgY29uZmlybWF0aW9uIGRpYWxvZyBhdCBzdGFydHVwLicsXG4gICAgICApLmhpZGVIZWxwKCksXG4gICAgKVxuICB9XG5cbiAgLy8gVGVhbW1hdGUgaWRlbnRpdHkgb3B0aW9ucyAoc2V0IGJ5IGxlYWRlciB3aGVuIHNwYXduaW5nIHRtdXggdGVhbW1hdGVzKVxuICAvLyBUaGVzZSByZXBsYWNlIHRoZSBDTEFVREVfQ09ERV8qIGVudmlyb25tZW50IHZhcmlhYmxlc1xuICBwcm9ncmFtLmFkZE9wdGlvbihcbiAgICBuZXcgT3B0aW9uKCctLWFnZW50LWlkIDxpZD4nLCAnVGVhbW1hdGUgYWdlbnQgSUQnKS5oaWRlSGVscCgpLFxuICApXG4gIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgIG5ldyBPcHRpb24oJy0tYWdlbnQtbmFtZSA8bmFtZT4nLCAnVGVhbW1hdGUgZGlzcGxheSBuYW1lJykuaGlkZUhlbHAoKSxcbiAgKVxuICBwcm9ncmFtLmFkZE9wdGlvbihcbiAgICBuZXcgT3B0aW9uKFxuICAgICAgJy0tdGVhbS1uYW1lIDxuYW1lPicsXG4gICAgICAnVGVhbSBuYW1lIGZvciBzd2FybSBjb29yZGluYXRpb24nLFxuICAgICkuaGlkZUhlbHAoKSxcbiAgKVxuICBwcm9ncmFtLmFkZE9wdGlvbihcbiAgICBuZXcgT3B0aW9uKCctLWFnZW50LWNvbG9yIDxjb2xvcj4nLCAnVGVhbW1hdGUgVUkgY29sb3InKS5oaWRlSGVscCgpLFxuICApXG4gIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgIG5ldyBPcHRpb24oXG4gICAgICAnLS1wbGFuLW1vZGUtcmVxdWlyZWQnLFxuICAgICAgJ1JlcXVpcmUgcGxhbiBtb2RlIGJlZm9yZSBpbXBsZW1lbnRhdGlvbicsXG4gICAgKS5oaWRlSGVscCgpLFxuICApXG4gIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgIG5ldyBPcHRpb24oXG4gICAgICAnLS1wYXJlbnQtc2Vzc2lvbi1pZCA8aWQ+JyxcbiAgICAgICdQYXJlbnQgc2Vzc2lvbiBJRCBmb3IgYW5hbHl0aWNzIGNvcnJlbGF0aW9uJyxcbiAgICApLmhpZGVIZWxwKCksXG4gIClcbiAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgbmV3IE9wdGlvbihcbiAgICAgICctLXRlYW1tYXRlLW1vZGUgPG1vZGU+JyxcbiAgICAgICdIb3cgdG8gc3Bhd24gdGVhbW1hdGVzOiBcInRtdXhcIiwgXCJpbi1wcm9jZXNzXCIsIG9yIFwiYXV0b1wiJyxcbiAgICApXG4gICAgICAuY2hvaWNlcyhbJ2F1dG8nLCAndG11eCcsICdpbi1wcm9jZXNzJ10pXG4gICAgICAuaGlkZUhlbHAoKSxcbiAgKVxuICBwcm9ncmFtLmFkZE9wdGlvbihcbiAgICBuZXcgT3B0aW9uKFxuICAgICAgJy0tYWdlbnQtdHlwZSA8dHlwZT4nLFxuICAgICAgJ0N1c3RvbSBhZ2VudCB0eXBlIGZvciB0aGlzIHRlYW1tYXRlJyxcbiAgICApLmhpZGVIZWxwKCksXG4gIClcblxuICAvLyBFbmFibGUgU0RLIFVSTCBmb3IgYWxsIGJ1aWxkcyBidXQgaGlkZSBmcm9tIGhlbHBcbiAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgbmV3IE9wdGlvbihcbiAgICAgICctLXNkay11cmwgPHVybD4nLFxuICAgICAgJ1VzZSByZW1vdGUgV2ViU29ja2V0IGVuZHBvaW50IGZvciBTREsgSS9PIHN0cmVhbWluZyAob25seSB3aXRoIC1wIGFuZCBzdHJlYW0tanNvbiBmb3JtYXQpJyxcbiAgICApLmhpZGVIZWxwKCksXG4gIClcblxuICAvLyBFbmFibGUgdGVsZXBvcnQvcmVtb3RlIGZsYWdzIGZvciBhbGwgYnVpbGRzIGJ1dCBrZWVwIHRoZW0gdW5kb2N1bWVudGVkIHVudGlsIEdBXG4gIHByb2dyYW0uYWRkT3B0aW9uKFxuICAgIG5ldyBPcHRpb24oXG4gICAgICAnLS10ZWxlcG9ydCBbc2Vzc2lvbl0nLFxuICAgICAgJ1Jlc3VtZSBhIHRlbGVwb3J0IHNlc3Npb24sIG9wdGlvbmFsbHkgc3BlY2lmeSBzZXNzaW9uIElEJyxcbiAgICApLmhpZGVIZWxwKCksXG4gIClcbiAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgbmV3IE9wdGlvbihcbiAgICAgICctLXJlbW90ZSBbZGVzY3JpcHRpb25dJyxcbiAgICAgICdDcmVhdGUgYSByZW1vdGUgc2Vzc2lvbiB3aXRoIHRoZSBnaXZlbiBkZXNjcmlwdGlvbicsXG4gICAgKS5oaWRlSGVscCgpLFxuICApXG4gIGlmIChmZWF0dXJlKCdCUklER0VfTU9ERScpKSB7XG4gICAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1yZW1vdGUtY29udHJvbCBbbmFtZV0nLFxuICAgICAgICAnU3RhcnQgYW4gaW50ZXJhY3RpdmUgc2Vzc2lvbiB3aXRoIFJlbW90ZSBDb250cm9sIGVuYWJsZWQgKG9wdGlvbmFsbHkgbmFtZWQpJyxcbiAgICAgIClcbiAgICAgICAgLmFyZ1BhcnNlcih2YWx1ZSA9PiB2YWx1ZSB8fCB0cnVlKVxuICAgICAgICAuaGlkZUhlbHAoKSxcbiAgICApXG4gICAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKCctLXJjIFtuYW1lXScsICdBbGlhcyBmb3IgLS1yZW1vdGUtY29udHJvbCcpXG4gICAgICAgIC5hcmdQYXJzZXIodmFsdWUgPT4gdmFsdWUgfHwgdHJ1ZSlcbiAgICAgICAgLmhpZGVIZWxwKCksXG4gICAgKVxuICB9XG5cbiAgaWYgKGZlYXR1cmUoJ0hBUkRfRkFJTCcpKSB7XG4gICAgcHJvZ3JhbS5hZGRPcHRpb24oXG4gICAgICBuZXcgT3B0aW9uKFxuICAgICAgICAnLS1oYXJkLWZhaWwnLFxuICAgICAgICAnQ3Jhc2ggb24gbG9nRXJyb3IgY2FsbHMgaW5zdGVhZCBvZiBzaWxlbnRseSBsb2dnaW5nJyxcbiAgICAgICkuaGlkZUhlbHAoKSxcbiAgICApXG4gIH1cblxuICBwcm9maWxlQ2hlY2twb2ludCgncnVuX21haW5fb3B0aW9uc19idWlsdCcpXG5cbiAgLy8gLXAvLS1wcmludCBtb2RlOiBza2lwIHN1YmNvbW1hbmQgcmVnaXN0cmF0aW9uLiBUaGUgNTIgc3ViY29tbWFuZHNcbiAgLy8gKG1jcCwgYXV0aCwgcGx1Z2luLCBza2lsbCwgdGFzaywgY29uZmlnLCBkb2N0b3IsIHVwZGF0ZSwgZXRjLikgYXJlXG4gIC8vIG5ldmVyIGRpc3BhdGNoZWQgaW4gcHJpbnQgbW9kZSDigJQgY29tbWFuZGVyIHJvdXRlcyB0aGUgcHJvbXB0IHRvIHRoZVxuICAvLyBkZWZhdWx0IGFjdGlvbi4gVGhlIHN1YmNvbW1hbmQgcmVnaXN0cmF0aW9uIHBhdGggd2FzIG1lYXN1cmVkIGF0IH42NW1zXG4gIC8vIG9uIGJhc2VsaW5lIOKAlCBtb3N0bHkgdGhlIGlzQnJpZGdlRW5hYmxlZCgpIGNhbGwgKDI1bXMgc2V0dGluZ3MgWm9kIHBhcnNlXG4gIC8vICsgNDBtcyBzeW5jIGtleWNoYWluIHN1YnByb2Nlc3MpLCBib3RoIGhpZGRlbiBieSB0aGUgdHJ5L2NhdGNoIHRoYXRcbiAgLy8gYWx3YXlzIHJldHVybnMgZmFsc2UgYmVmb3JlIGVuYWJsZUNvbmZpZ3MoKS4gY2M6Ly8gVVJMcyBhcmUgcmV3cml0dGVuIHRvXG4gIC8vIGBvcGVuYCBhdCBtYWluKCkgbGluZSB+ODUxIEJFRk9SRSB0aGlzIHJ1bnMsIHNvIGFyZ3YgY2hlY2sgaXMgc2FmZSBoZXJlLlxuICBjb25zdCBpc1ByaW50TW9kZSA9XG4gICAgcHJvY2Vzcy5hcmd2LmluY2x1ZGVzKCctcCcpIHx8IHByb2Nlc3MuYXJndi5pbmNsdWRlcygnLS1wcmludCcpXG4gIGNvbnN0IGlzQ2NVcmwgPSBwcm9jZXNzLmFyZ3Yuc29tZShcbiAgICBhID0+IGEuc3RhcnRzV2l0aCgnY2M6Ly8nKSB8fCBhLnN0YXJ0c1dpdGgoJ2NjK3VuaXg6Ly8nKSxcbiAgKVxuICBpZiAoaXNQcmludE1vZGUgJiYgIWlzQ2NVcmwpIHtcbiAgICBwcm9maWxlQ2hlY2twb2ludCgncnVuX2JlZm9yZV9wYXJzZScpXG4gICAgYXdhaXQgcHJvZ3JhbS5wYXJzZUFzeW5jKHByb2Nlc3MuYXJndilcbiAgICBwcm9maWxlQ2hlY2twb2ludCgncnVuX2FmdGVyX3BhcnNlJylcbiAgICByZXR1cm4gcHJvZ3JhbVxuICB9XG5cbiAgLy8gY2xhdWRlIG1jcFxuXG4gIGNvbnN0IG1jcCA9IHByb2dyYW1cbiAgICAuY29tbWFuZCgnbWNwJylcbiAgICAuZGVzY3JpcHRpb24oJ0NvbmZpZ3VyZSBhbmQgbWFuYWdlIE1DUCBzZXJ2ZXJzJylcbiAgICAuY29uZmlndXJlSGVscChjcmVhdGVTb3J0ZWRIZWxwQ29uZmlnKCkpXG4gICAgLmVuYWJsZVBvc2l0aW9uYWxPcHRpb25zKClcblxuICBtY3BcbiAgICAuY29tbWFuZCgnc2VydmUnKVxuICAgIC5kZXNjcmlwdGlvbihgU3RhcnQgdGhlIENsYXVkZSBDb2RlIE1DUCBzZXJ2ZXJgKVxuICAgIC5vcHRpb24oJy1kLCAtLWRlYnVnJywgJ0VuYWJsZSBkZWJ1ZyBtb2RlJywgKCkgPT4gdHJ1ZSlcbiAgICAub3B0aW9uKFxuICAgICAgJy0tdmVyYm9zZScsXG4gICAgICAnT3ZlcnJpZGUgdmVyYm9zZSBtb2RlIHNldHRpbmcgZnJvbSBjb25maWcnLFxuICAgICAgKCkgPT4gdHJ1ZSxcbiAgICApXG4gICAgLmFjdGlvbihcbiAgICAgIGFzeW5jICh7IGRlYnVnLCB2ZXJib3NlIH06IHsgZGVidWc/OiBib29sZWFuOyB2ZXJib3NlPzogYm9vbGVhbiB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgbWNwU2VydmVIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoJy4vY2xpL2hhbmRsZXJzL21jcC5qcycpXG4gICAgICAgIGF3YWl0IG1jcFNlcnZlSGFuZGxlcih7IGRlYnVnLCB2ZXJib3NlIH0pXG4gICAgICB9LFxuICAgIClcblxuICAvLyBSZWdpc3RlciB0aGUgbWNwIGFkZCBzdWJjb21tYW5kIChleHRyYWN0ZWQgZm9yIHRlc3RhYmlsaXR5KVxuICByZWdpc3Rlck1jcEFkZENvbW1hbmQobWNwKVxuXG4gIGlmIChpc1hhYUVuYWJsZWQoKSkge1xuICAgIHJlZ2lzdGVyTWNwWGFhSWRwQ29tbWFuZChtY3ApXG4gIH1cblxuICBtY3BcbiAgICAuY29tbWFuZCgncmVtb3ZlIDxuYW1lPicpXG4gICAgLmRlc2NyaXB0aW9uKCdSZW1vdmUgYW4gTUNQIHNlcnZlcicpXG4gICAgLm9wdGlvbihcbiAgICAgICctcywgLS1zY29wZSA8c2NvcGU+JyxcbiAgICAgICdDb25maWd1cmF0aW9uIHNjb3BlIChsb2NhbCwgdXNlciwgb3IgcHJvamVjdCkgLSBpZiBub3Qgc3BlY2lmaWVkLCByZW1vdmVzIGZyb20gd2hpY2hldmVyIHNjb3BlIGl0IGV4aXN0cyBpbicsXG4gICAgKVxuICAgIC5hY3Rpb24oYXN5bmMgKG5hbWU6IHN0cmluZywgb3B0aW9uczogeyBzY29wZT86IHN0cmluZyB9KSA9PiB7XG4gICAgICBjb25zdCB7IG1jcFJlbW92ZUhhbmRsZXIgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvbWNwLmpzJylcbiAgICAgIGF3YWl0IG1jcFJlbW92ZUhhbmRsZXIobmFtZSwgb3B0aW9ucylcbiAgICB9KVxuXG4gIG1jcFxuICAgIC5jb21tYW5kKCdsaXN0JylcbiAgICAuZGVzY3JpcHRpb24oXG4gICAgICAnTGlzdCBjb25maWd1cmVkIE1DUCBzZXJ2ZXJzLiBOb3RlOiBUaGUgd29ya3NwYWNlIHRydXN0IGRpYWxvZyBpcyBza2lwcGVkIGFuZCBzdGRpbyBzZXJ2ZXJzIGZyb20gLm1jcC5qc29uIGFyZSBzcGF3bmVkIGZvciBoZWFsdGggY2hlY2tzLiBPbmx5IHVzZSB0aGlzIGNvbW1hbmQgaW4gZGlyZWN0b3JpZXMgeW91IHRydXN0LicsXG4gICAgKVxuICAgIC5hY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgeyBtY3BMaXN0SGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KCcuL2NsaS9oYW5kbGVycy9tY3AuanMnKVxuICAgICAgYXdhaXQgbWNwTGlzdEhhbmRsZXIoKVxuICAgIH0pXG5cbiAgbWNwXG4gICAgLmNvbW1hbmQoJ2dldCA8bmFtZT4nKVxuICAgIC5kZXNjcmlwdGlvbihcbiAgICAgICdHZXQgZGV0YWlscyBhYm91dCBhbiBNQ1Agc2VydmVyLiBOb3RlOiBUaGUgd29ya3NwYWNlIHRydXN0IGRpYWxvZyBpcyBza2lwcGVkIGFuZCBzdGRpbyBzZXJ2ZXJzIGZyb20gLm1jcC5qc29uIGFyZSBzcGF3bmVkIGZvciBoZWFsdGggY2hlY2tzLiBPbmx5IHVzZSB0aGlzIGNvbW1hbmQgaW4gZGlyZWN0b3JpZXMgeW91IHRydXN0LicsXG4gICAgKVxuICAgIC5hY3Rpb24oYXN5bmMgKG5hbWU6IHN0cmluZykgPT4ge1xuICAgICAgY29uc3QgeyBtY3BHZXRIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoJy4vY2xpL2hhbmRsZXJzL21jcC5qcycpXG4gICAgICBhd2FpdCBtY3BHZXRIYW5kbGVyKG5hbWUpXG4gICAgfSlcblxuICBtY3BcbiAgICAuY29tbWFuZCgnYWRkLWpzb24gPG5hbWU+IDxqc29uPicpXG4gICAgLmRlc2NyaXB0aW9uKCdBZGQgYW4gTUNQIHNlcnZlciAoc3RkaW8gb3IgU1NFKSB3aXRoIGEgSlNPTiBzdHJpbmcnKVxuICAgIC5vcHRpb24oXG4gICAgICAnLXMsIC0tc2NvcGUgPHNjb3BlPicsXG4gICAgICAnQ29uZmlndXJhdGlvbiBzY29wZSAobG9jYWwsIHVzZXIsIG9yIHByb2plY3QpJyxcbiAgICAgICdsb2NhbCcsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1jbGllbnQtc2VjcmV0JyxcbiAgICAgICdQcm9tcHQgZm9yIE9BdXRoIGNsaWVudCBzZWNyZXQgKG9yIHNldCBNQ1BfQ0xJRU5UX1NFQ1JFVCBlbnYgdmFyKScsXG4gICAgKVxuICAgIC5hY3Rpb24oXG4gICAgICBhc3luYyAoXG4gICAgICAgIG5hbWU6IHN0cmluZyxcbiAgICAgICAganNvbjogc3RyaW5nLFxuICAgICAgICBvcHRpb25zOiB7IHNjb3BlPzogc3RyaW5nOyBjbGllbnRTZWNyZXQ/OiB0cnVlIH0sXG4gICAgICApID0+IHtcbiAgICAgICAgY29uc3QgeyBtY3BBZGRKc29uSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KCcuL2NsaS9oYW5kbGVycy9tY3AuanMnKVxuICAgICAgICBhd2FpdCBtY3BBZGRKc29uSGFuZGxlcihuYW1lLCBqc29uLCBvcHRpb25zKVxuICAgICAgfSxcbiAgICApXG5cbiAgbWNwXG4gICAgLmNvbW1hbmQoJ2FkZC1mcm9tLWNsYXVkZS1kZXNrdG9wJylcbiAgICAuZGVzY3JpcHRpb24oJ0ltcG9ydCBNQ1Agc2VydmVycyBmcm9tIENsYXVkZSBEZXNrdG9wIChNYWMgYW5kIFdTTCBvbmx5KScpXG4gICAgLm9wdGlvbihcbiAgICAgICctcywgLS1zY29wZSA8c2NvcGU+JyxcbiAgICAgICdDb25maWd1cmF0aW9uIHNjb3BlIChsb2NhbCwgdXNlciwgb3IgcHJvamVjdCknLFxuICAgICAgJ2xvY2FsJyxcbiAgICApXG4gICAgLmFjdGlvbihhc3luYyAob3B0aW9uczogeyBzY29wZT86IHN0cmluZyB9KSA9PiB7XG4gICAgICBjb25zdCB7IG1jcEFkZEZyb21EZXNrdG9wSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KCcuL2NsaS9oYW5kbGVycy9tY3AuanMnKVxuICAgICAgYXdhaXQgbWNwQWRkRnJvbURlc2t0b3BIYW5kbGVyKG9wdGlvbnMpXG4gICAgfSlcblxuICBtY3BcbiAgICAuY29tbWFuZCgncmVzZXQtcHJvamVjdC1jaG9pY2VzJylcbiAgICAuZGVzY3JpcHRpb24oXG4gICAgICAnUmVzZXQgYWxsIGFwcHJvdmVkIGFuZCByZWplY3RlZCBwcm9qZWN0LXNjb3BlZCAoLm1jcC5qc29uKSBzZXJ2ZXJzIHdpdGhpbiB0aGlzIHByb2plY3QnLFxuICAgIClcbiAgICAuYWN0aW9uKGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IHsgbWNwUmVzZXRDaG9pY2VzSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KCcuL2NsaS9oYW5kbGVycy9tY3AuanMnKVxuICAgICAgYXdhaXQgbWNwUmVzZXRDaG9pY2VzSGFuZGxlcigpXG4gICAgfSlcblxuICAvLyBjbGF1ZGUgc2VydmVyXG4gIGlmIChmZWF0dXJlKCdESVJFQ1RfQ09OTkVDVCcpKSB7XG4gICAgcHJvZ3JhbVxuICAgICAgLmNvbW1hbmQoJ3NlcnZlcicpXG4gICAgICAuZGVzY3JpcHRpb24oJ1N0YXJ0IGEgQ2xhdWRlIENvZGUgc2Vzc2lvbiBzZXJ2ZXInKVxuICAgICAgLm9wdGlvbignLS1wb3J0IDxudW1iZXI+JywgJ0hUVFAgcG9ydCcsICcwJylcbiAgICAgIC5vcHRpb24oJy0taG9zdCA8c3RyaW5nPicsICdCaW5kIGFkZHJlc3MnLCAnMC4wLjAuMCcpXG4gICAgICAub3B0aW9uKCctLWF1dGgtdG9rZW4gPHRva2VuPicsICdCZWFyZXIgdG9rZW4gZm9yIGF1dGgnKVxuICAgICAgLm9wdGlvbignLS11bml4IDxwYXRoPicsICdMaXN0ZW4gb24gYSB1bml4IGRvbWFpbiBzb2NrZXQnKVxuICAgICAgLm9wdGlvbihcbiAgICAgICAgJy0td29ya3NwYWNlIDxkaXI+JyxcbiAgICAgICAgJ0RlZmF1bHQgd29ya2luZyBkaXJlY3RvcnkgZm9yIHNlc3Npb25zIHRoYXQgZG8gbm90IHNwZWNpZnkgY3dkJyxcbiAgICAgIClcbiAgICAgIC5vcHRpb24oXG4gICAgICAgICctLWlkbGUtdGltZW91dCA8bXM+JyxcbiAgICAgICAgJ0lkbGUgdGltZW91dCBmb3IgZGV0YWNoZWQgc2Vzc2lvbnMgaW4gbXMgKDAgPSBuZXZlciBleHBpcmUpJyxcbiAgICAgICAgJzYwMDAwMCcsXG4gICAgICApXG4gICAgICAub3B0aW9uKFxuICAgICAgICAnLS1tYXgtc2Vzc2lvbnMgPG4+JyxcbiAgICAgICAgJ01heGltdW0gY29uY3VycmVudCBzZXNzaW9ucyAoMCA9IHVubGltaXRlZCknLFxuICAgICAgICAnMzInLFxuICAgICAgKVxuICAgICAgLmFjdGlvbihcbiAgICAgICAgYXN5bmMgKG9wdHM6IHtcbiAgICAgICAgICBwb3J0OiBzdHJpbmdcbiAgICAgICAgICBob3N0OiBzdHJpbmdcbiAgICAgICAgICBhdXRoVG9rZW4/OiBzdHJpbmdcbiAgICAgICAgICB1bml4Pzogc3RyaW5nXG4gICAgICAgICAgd29ya3NwYWNlPzogc3RyaW5nXG4gICAgICAgICAgaWRsZVRpbWVvdXQ6IHN0cmluZ1xuICAgICAgICAgIG1heFNlc3Npb25zOiBzdHJpbmdcbiAgICAgICAgfSkgPT4ge1xuICAgICAgICAgIGNvbnN0IHsgcmFuZG9tQnl0ZXMgfSA9IGF3YWl0IGltcG9ydCgnY3J5cHRvJylcbiAgICAgICAgICBjb25zdCB7IHN0YXJ0U2VydmVyIH0gPSBhd2FpdCBpbXBvcnQoJy4vc2VydmVyL3NlcnZlci5qcycpXG4gICAgICAgICAgY29uc3QgeyBTZXNzaW9uTWFuYWdlciB9ID0gYXdhaXQgaW1wb3J0KCcuL3NlcnZlci9zZXNzaW9uTWFuYWdlci5qcycpXG4gICAgICAgICAgY29uc3QgeyBEYW5nZXJvdXNCYWNrZW5kIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICAgICAnLi9zZXJ2ZXIvYmFja2VuZHMvZGFuZ2Vyb3VzQmFja2VuZC5qcydcbiAgICAgICAgICApXG4gICAgICAgICAgY29uc3QgeyBwcmludEJhbm5lciB9ID0gYXdhaXQgaW1wb3J0KCcuL3NlcnZlci9zZXJ2ZXJCYW5uZXIuanMnKVxuICAgICAgICAgIGNvbnN0IHsgY3JlYXRlU2VydmVyTG9nZ2VyIH0gPSBhd2FpdCBpbXBvcnQoJy4vc2VydmVyL3NlcnZlckxvZy5qcycpXG4gICAgICAgICAgY29uc3QgeyB3cml0ZVNlcnZlckxvY2ssIHJlbW92ZVNlcnZlckxvY2ssIHByb2JlUnVubmluZ1NlcnZlciB9ID1cbiAgICAgICAgICAgIGF3YWl0IGltcG9ydCgnLi9zZXJ2ZXIvbG9ja2ZpbGUuanMnKVxuXG4gICAgICAgICAgY29uc3QgZXhpc3RpbmcgPSBhd2FpdCBwcm9iZVJ1bm5pbmdTZXJ2ZXIoKVxuICAgICAgICAgIGlmIChleGlzdGluZykge1xuICAgICAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgICAgICAgIGBBIGNsYXVkZSBzZXJ2ZXIgaXMgYWxyZWFkeSBydW5uaW5nIChwaWQgJHtleGlzdGluZy5waWR9KSBhdCAke2V4aXN0aW5nLmh0dHBVcmx9XFxuYCxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHByb2Nlc3MuZXhpdCgxKVxuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnN0IGF1dGhUb2tlbiA9XG4gICAgICAgICAgICBvcHRzLmF1dGhUb2tlbiA/P1xuICAgICAgICAgICAgYHNrLWFudC1jYy0ke3JhbmRvbUJ5dGVzKDE2KS50b1N0cmluZygnYmFzZTY0dXJsJyl9YFxuXG4gICAgICAgICAgY29uc3QgY29uZmlnID0ge1xuICAgICAgICAgICAgcG9ydDogcGFyc2VJbnQob3B0cy5wb3J0LCAxMCksXG4gICAgICAgICAgICBob3N0OiBvcHRzLmhvc3QsXG4gICAgICAgICAgICBhdXRoVG9rZW4sXG4gICAgICAgICAgICB1bml4OiBvcHRzLnVuaXgsXG4gICAgICAgICAgICB3b3Jrc3BhY2U6IG9wdHMud29ya3NwYWNlLFxuICAgICAgICAgICAgaWRsZVRpbWVvdXRNczogcGFyc2VJbnQob3B0cy5pZGxlVGltZW91dCwgMTApLFxuICAgICAgICAgICAgbWF4U2Vzc2lvbnM6IHBhcnNlSW50KG9wdHMubWF4U2Vzc2lvbnMsIDEwKSxcbiAgICAgICAgICB9XG5cbiAgICAgICAgICBjb25zdCBiYWNrZW5kID0gbmV3IERhbmdlcm91c0JhY2tlbmQoKVxuICAgICAgICAgIGNvbnN0IHNlc3Npb25NYW5hZ2VyID0gbmV3IFNlc3Npb25NYW5hZ2VyKGJhY2tlbmQsIHtcbiAgICAgICAgICAgIGlkbGVUaW1lb3V0TXM6IGNvbmZpZy5pZGxlVGltZW91dE1zLFxuICAgICAgICAgICAgbWF4U2Vzc2lvbnM6IGNvbmZpZy5tYXhTZXNzaW9ucyxcbiAgICAgICAgICB9KVxuICAgICAgICAgIGNvbnN0IGxvZ2dlciA9IGNyZWF0ZVNlcnZlckxvZ2dlcigpXG5cbiAgICAgICAgICBjb25zdCBzZXJ2ZXIgPSBzdGFydFNlcnZlcihjb25maWcsIHNlc3Npb25NYW5hZ2VyLCBsb2dnZXIpXG4gICAgICAgICAgY29uc3QgYWN0dWFsUG9ydCA9IHNlcnZlci5wb3J0ID8/IGNvbmZpZy5wb3J0XG4gICAgICAgICAgcHJpbnRCYW5uZXIoY29uZmlnLCBhdXRoVG9rZW4sIGFjdHVhbFBvcnQpXG5cbiAgICAgICAgICBhd2FpdCB3cml0ZVNlcnZlckxvY2soe1xuICAgICAgICAgICAgcGlkOiBwcm9jZXNzLnBpZCxcbiAgICAgICAgICAgIHBvcnQ6IGFjdHVhbFBvcnQsXG4gICAgICAgICAgICBob3N0OiBjb25maWcuaG9zdCxcbiAgICAgICAgICAgIGh0dHBVcmw6IGNvbmZpZy51bml4XG4gICAgICAgICAgICAgID8gYHVuaXg6JHtjb25maWcudW5peH1gXG4gICAgICAgICAgICAgIDogYGh0dHA6Ly8ke2NvbmZpZy5ob3N0fToke2FjdHVhbFBvcnR9YCxcbiAgICAgICAgICAgIHN0YXJ0ZWRBdDogRGF0ZS5ub3coKSxcbiAgICAgICAgICB9KVxuXG4gICAgICAgICAgbGV0IHNodXR0aW5nRG93biA9IGZhbHNlXG4gICAgICAgICAgY29uc3Qgc2h1dGRvd24gPSBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgICBpZiAoc2h1dHRpbmdEb3duKSByZXR1cm5cbiAgICAgICAgICAgIHNodXR0aW5nRG93biA9IHRydWVcbiAgICAgICAgICAgIC8vIFN0b3AgYWNjZXB0aW5nIG5ldyBjb25uZWN0aW9ucyBiZWZvcmUgdGVhcmluZyBkb3duIHNlc3Npb25zLlxuICAgICAgICAgICAgc2VydmVyLnN0b3AodHJ1ZSlcbiAgICAgICAgICAgIGF3YWl0IHNlc3Npb25NYW5hZ2VyLmRlc3Ryb3lBbGwoKVxuICAgICAgICAgICAgYXdhaXQgcmVtb3ZlU2VydmVyTG9jaygpXG4gICAgICAgICAgICBwcm9jZXNzLmV4aXQoMClcbiAgICAgICAgICB9XG4gICAgICAgICAgcHJvY2Vzcy5vbmNlKCdTSUdJTlQnLCAoKSA9PiB2b2lkIHNodXRkb3duKCkpXG4gICAgICAgICAgcHJvY2Vzcy5vbmNlKCdTSUdURVJNJywgKCkgPT4gdm9pZCBzaHV0ZG93bigpKVxuICAgICAgICB9LFxuICAgICAgKVxuICB9XG5cbiAgLy8gYGNsYXVkZSBzc2ggPGhvc3Q+IFtkaXJdYCDigJQgcmVnaXN0ZXJlZCBoZXJlIG9ubHkgc28gLS1oZWxwIHNob3dzIGl0LlxuICAvLyBUaGUgYWN0dWFsIGludGVyYWN0aXZlIGZsb3cgaXMgaGFuZGxlZCBieSBlYXJseSBhcmd2IHJld3JpdGluZyBpbiBtYWluKClcbiAgLy8gKHBhcmFsbGVscyB0aGUgRElSRUNUX0NPTk5FQ1QvY2M6Ly8gcGF0dGVybiBhYm92ZSkuIElmIGNvbW1hbmRlciByZWFjaGVzXG4gIC8vIHRoaXMgYWN0aW9uIGl0IG1lYW5zIHRoZSBhcmd2IHJld3JpdGUgZGlkbid0IGZpcmUgKGUuZy4gdXNlciByYW5cbiAgLy8gYGNsYXVkZSBzc2hgIHdpdGggbm8gaG9zdCkg4oCUIGp1c3QgcHJpbnQgdXNhZ2UuXG4gIGlmIChmZWF0dXJlKCdTU0hfUkVNT1RFJykpIHtcbiAgICBwcm9ncmFtXG4gICAgICAuY29tbWFuZCgnc3NoIDxob3N0PiBbZGlyXScpXG4gICAgICAuZGVzY3JpcHRpb24oXG4gICAgICAgICdSdW4gQ2xhdWRlIENvZGUgb24gYSByZW1vdGUgaG9zdCBvdmVyIFNTSC4gRGVwbG95cyB0aGUgYmluYXJ5IGFuZCAnICtcbiAgICAgICAgICAndHVubmVscyBBUEkgYXV0aCBiYWNrIHRocm91Z2ggeW91ciBsb2NhbCBtYWNoaW5lIOKAlCBubyByZW1vdGUgc2V0dXAgbmVlZGVkLicsXG4gICAgICApXG4gICAgICAub3B0aW9uKFxuICAgICAgICAnLS1wZXJtaXNzaW9uLW1vZGUgPG1vZGU+JyxcbiAgICAgICAgJ1Blcm1pc3Npb24gbW9kZSBmb3IgdGhlIHJlbW90ZSBzZXNzaW9uJyxcbiAgICAgIClcbiAgICAgIC5vcHRpb24oXG4gICAgICAgICctLWRhbmdlcm91c2x5LXNraXAtcGVybWlzc2lvbnMnLFxuICAgICAgICAnU2tpcCBhbGwgcGVybWlzc2lvbiBwcm9tcHRzIG9uIHRoZSByZW1vdGUgKGRhbmdlcm91cyknLFxuICAgICAgKVxuICAgICAgLm9wdGlvbihcbiAgICAgICAgJy0tbG9jYWwnLFxuICAgICAgICAnZTJlIHRlc3QgbW9kZSDigJQgc3Bhd24gdGhlIGNoaWxkIENMSSBsb2NhbGx5IChza2lwIHNzaC9kZXBsb3kpLiAnICtcbiAgICAgICAgICAnRXhlcmNpc2VzIHRoZSBhdXRoIHByb3h5IGFuZCB1bml4LXNvY2tldCBwbHVtYmluZyB3aXRob3V0IGEgcmVtb3RlIGhvc3QuJyxcbiAgICAgIClcbiAgICAgIC5hY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgICAvLyBBcmd2IHJld3JpdGluZyBpbiBtYWluKCkgc2hvdWxkIGhhdmUgY29uc3VtZWQgYHNzaCA8aG9zdD5gIGJlZm9yZVxuICAgICAgICAvLyBjb21tYW5kZXIgcnVucy4gUmVhY2hpbmcgaGVyZSBtZWFucyBob3N0IHdhcyBtaXNzaW5nIG9yIHRoZVxuICAgICAgICAvLyByZXdyaXRlIHByZWRpY2F0ZSBkaWRuJ3QgbWF0Y2guXG4gICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKFxuICAgICAgICAgICdVc2FnZTogY2xhdWRlIHNzaCA8dXNlckBob3N0IHwgc3NoLWNvbmZpZy1hbGlhcz4gW2Rpcl1cXG5cXG4nICtcbiAgICAgICAgICAgIFwiUnVucyBDbGF1ZGUgQ29kZSBvbiBhIHJlbW90ZSBMaW51eCBob3N0LiBZb3UgZG9uJ3QgbmVlZCB0byBpbnN0YWxsXFxuXCIgK1xuICAgICAgICAgICAgJ2FueXRoaW5nIG9uIHRoZSByZW1vdGUgb3IgcnVuIGBjbGF1ZGUgYXV0aCBsb2dpbmAgdGhlcmUg4oCUIHRoZSBiaW5hcnkgaXNcXG4nICtcbiAgICAgICAgICAgICdkZXBsb3llZCBvdmVyIFNTSCBhbmQgQVBJIGF1dGggdHVubmVscyBiYWNrIHRocm91Z2ggeW91ciBsb2NhbCBtYWNoaW5lLlxcbicsXG4gICAgICAgIClcbiAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICB9KVxuICB9XG5cbiAgLy8gY2xhdWRlIGNvbm5lY3Qg4oCUIHN1YmNvbW1hbmQgb25seSBoYW5kbGVzIC1wIChoZWFkbGVzcykgbW9kZS5cbiAgLy8gSW50ZXJhY3RpdmUgbW9kZSAod2l0aG91dCAtcCkgaXMgaGFuZGxlZCBieSBlYXJseSBhcmd2IHJld3JpdGluZyBpbiBtYWluKClcbiAgLy8gd2hpY2ggcmVkaXJlY3RzIHRvIHRoZSBtYWluIGNvbW1hbmQgd2l0aCBmdWxsIFRVSSBzdXBwb3J0LlxuICBpZiAoZmVhdHVyZSgnRElSRUNUX0NPTk5FQ1QnKSkge1xuICAgIHByb2dyYW1cbiAgICAgIC5jb21tYW5kKCdvcGVuIDxjYy11cmw+JylcbiAgICAgIC5kZXNjcmlwdGlvbihcbiAgICAgICAgJ0Nvbm5lY3QgdG8gYSBDbGF1ZGUgQ29kZSBzZXJ2ZXIgKGludGVybmFsIOKAlCB1c2UgY2M6Ly8gVVJMcyknLFxuICAgICAgKVxuICAgICAgLm9wdGlvbignLXAsIC0tcHJpbnQgW3Byb21wdF0nLCAnUHJpbnQgbW9kZSAoaGVhZGxlc3MpJylcbiAgICAgIC5vcHRpb24oXG4gICAgICAgICctLW91dHB1dC1mb3JtYXQgPGZvcm1hdD4nLFxuICAgICAgICAnT3V0cHV0IGZvcm1hdDogdGV4dCwganNvbiwgc3RyZWFtLWpzb24nLFxuICAgICAgICAndGV4dCcsXG4gICAgICApXG4gICAgICAuYWN0aW9uKFxuICAgICAgICBhc3luYyAoXG4gICAgICAgICAgY2NVcmw6IHN0cmluZyxcbiAgICAgICAgICBvcHRzOiB7XG4gICAgICAgICAgICBwcmludD86IHN0cmluZyB8IGJvb2xlYW5cbiAgICAgICAgICAgIG91dHB1dEZvcm1hdDogc3RyaW5nXG4gICAgICAgICAgfSxcbiAgICAgICAgKSA9PiB7XG4gICAgICAgICAgY29uc3QgeyBwYXJzZUNvbm5lY3RVcmwgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICAgICAgICcuL3NlcnZlci9wYXJzZUNvbm5lY3RVcmwuanMnXG4gICAgICAgICAgKVxuICAgICAgICAgIGNvbnN0IHsgc2VydmVyVXJsLCBhdXRoVG9rZW4gfSA9IHBhcnNlQ29ubmVjdFVybChjY1VybClcblxuICAgICAgICAgIGxldCBjb25uZWN0Q29uZmlnXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IHNlc3Npb24gPSBhd2FpdCBjcmVhdGVEaXJlY3RDb25uZWN0U2Vzc2lvbih7XG4gICAgICAgICAgICAgIHNlcnZlclVybCxcbiAgICAgICAgICAgICAgYXV0aFRva2VuLFxuICAgICAgICAgICAgICBjd2Q6IGdldE9yaWdpbmFsQ3dkKCksXG4gICAgICAgICAgICAgIGRhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zOlxuICAgICAgICAgICAgICAgIF9wZW5kaW5nQ29ubmVjdD8uZGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnMsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgaWYgKHNlc3Npb24ud29ya0Rpcikge1xuICAgICAgICAgICAgICBzZXRPcmlnaW5hbEN3ZChzZXNzaW9uLndvcmtEaXIpXG4gICAgICAgICAgICAgIHNldEN3ZFN0YXRlKHNlc3Npb24ud29ya0RpcilcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHNldERpcmVjdENvbm5lY3RTZXJ2ZXJVcmwoc2VydmVyVXJsKVxuICAgICAgICAgICAgY29ubmVjdENvbmZpZyA9IHNlc3Npb24uY29uZmlnXG4gICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAvLyBiaW9tZS1pZ25vcmUgbGludC9zdXNwaWNpb3VzL25vQ29uc29sZTogaW50ZW50aW9uYWwgZXJyb3Igb3V0cHV0XG4gICAgICAgICAgICBjb25zb2xlLmVycm9yKFxuICAgICAgICAgICAgICBlcnIgaW5zdGFuY2VvZiBEaXJlY3RDb25uZWN0RXJyb3IgPyBlcnIubWVzc2FnZSA6IFN0cmluZyhlcnIpLFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgeyBydW5Db25uZWN0SGVhZGxlc3MgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICAgICAgICcuL3NlcnZlci9jb25uZWN0SGVhZGxlc3MuanMnXG4gICAgICAgICAgKVxuXG4gICAgICAgICAgY29uc3QgcHJvbXB0ID0gdHlwZW9mIG9wdHMucHJpbnQgPT09ICdzdHJpbmcnID8gb3B0cy5wcmludCA6ICcnXG4gICAgICAgICAgY29uc3QgaW50ZXJhY3RpdmUgPSBvcHRzLnByaW50ID09PSB0cnVlXG4gICAgICAgICAgYXdhaXQgcnVuQ29ubmVjdEhlYWRsZXNzKFxuICAgICAgICAgICAgY29ubmVjdENvbmZpZyxcbiAgICAgICAgICAgIHByb21wdCxcbiAgICAgICAgICAgIG9wdHMub3V0cHV0Rm9ybWF0LFxuICAgICAgICAgICAgaW50ZXJhY3RpdmUsXG4gICAgICAgICAgKVxuICAgICAgICB9LFxuICAgICAgKVxuICB9XG5cbiAgLy8gY2xhdWRlIGF1dGhcblxuICBjb25zdCBhdXRoID0gcHJvZ3JhbVxuICAgIC5jb21tYW5kKCdhdXRoJylcbiAgICAuZGVzY3JpcHRpb24oJ01hbmFnZSBhdXRoZW50aWNhdGlvbicpXG4gICAgLmNvbmZpZ3VyZUhlbHAoY3JlYXRlU29ydGVkSGVscENvbmZpZygpKVxuXG4gIGF1dGhcbiAgICAuY29tbWFuZCgnbG9naW4nKVxuICAgIC5kZXNjcmlwdGlvbignU2lnbiBpbiB0byB5b3VyIEFudGhyb3BpYyBhY2NvdW50JylcbiAgICAub3B0aW9uKCctLWVtYWlsIDxlbWFpbD4nLCAnUHJlLXBvcHVsYXRlIGVtYWlsIGFkZHJlc3Mgb24gdGhlIGxvZ2luIHBhZ2UnKVxuICAgIC5vcHRpb24oJy0tc3NvJywgJ0ZvcmNlIFNTTyBsb2dpbiBmbG93JylcbiAgICAub3B0aW9uKFxuICAgICAgJy0tY29uc29sZScsXG4gICAgICAnVXNlIEFudGhyb3BpYyBDb25zb2xlIChBUEkgdXNhZ2UgYmlsbGluZykgaW5zdGVhZCBvZiBDbGF1ZGUgc3Vic2NyaXB0aW9uJyxcbiAgICApXG4gICAgLm9wdGlvbignLS1jbGF1ZGVhaScsICdVc2UgQ2xhdWRlIHN1YnNjcmlwdGlvbiAoZGVmYXVsdCknKVxuICAgIC5hY3Rpb24oXG4gICAgICBhc3luYyAoe1xuICAgICAgICBlbWFpbCxcbiAgICAgICAgc3NvLFxuICAgICAgICBjb25zb2xlOiB1c2VDb25zb2xlLFxuICAgICAgICBjbGF1ZGVhaSxcbiAgICAgIH06IHtcbiAgICAgICAgZW1haWw/OiBzdHJpbmdcbiAgICAgICAgc3NvPzogYm9vbGVhblxuICAgICAgICBjb25zb2xlPzogYm9vbGVhblxuICAgICAgICBjbGF1ZGVhaT86IGJvb2xlYW5cbiAgICAgIH0pID0+IHtcbiAgICAgICAgY29uc3QgeyBhdXRoTG9naW4gfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvYXV0aC5qcycpXG4gICAgICAgIGF3YWl0IGF1dGhMb2dpbih7IGVtYWlsLCBzc28sIGNvbnNvbGU6IHVzZUNvbnNvbGUsIGNsYXVkZWFpIH0pXG4gICAgICB9LFxuICAgIClcblxuICBhdXRoXG4gICAgLmNvbW1hbmQoJ3N0YXR1cycpXG4gICAgLmRlc2NyaXB0aW9uKCdTaG93IGF1dGhlbnRpY2F0aW9uIHN0YXR1cycpXG4gICAgLm9wdGlvbignLS1qc29uJywgJ091dHB1dCBhcyBKU09OIChkZWZhdWx0KScpXG4gICAgLm9wdGlvbignLS10ZXh0JywgJ091dHB1dCBhcyBodW1hbi1yZWFkYWJsZSB0ZXh0JylcbiAgICAuYWN0aW9uKGFzeW5jIChvcHRzOiB7IGpzb24/OiBib29sZWFuOyB0ZXh0PzogYm9vbGVhbiB9KSA9PiB7XG4gICAgICBjb25zdCB7IGF1dGhTdGF0dXMgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvYXV0aC5qcycpXG4gICAgICBhd2FpdCBhdXRoU3RhdHVzKG9wdHMpXG4gICAgfSlcblxuICBhdXRoXG4gICAgLmNvbW1hbmQoJ2xvZ291dCcpXG4gICAgLmRlc2NyaXB0aW9uKCdMb2cgb3V0IGZyb20geW91ciBBbnRocm9waWMgYWNjb3VudCcpXG4gICAgLmFjdGlvbihhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCB7IGF1dGhMb2dvdXQgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvYXV0aC5qcycpXG4gICAgICBhd2FpdCBhdXRoTG9nb3V0KClcbiAgICB9KVxuXG4gIC8qKlxuICAgKiBIZWxwZXIgZnVuY3Rpb24gdG8gaGFuZGxlIG1hcmtldHBsYWNlIGNvbW1hbmQgZXJyb3JzIGNvbnNpc3RlbnRseS5cbiAgICogTG9ncyB0aGUgZXJyb3IgYW5kIGV4aXRzIHRoZSBwcm9jZXNzIHdpdGggc3RhdHVzIDEuXG4gICAqIEBwYXJhbSBlcnJvciBUaGUgZXJyb3IgdGhhdCBvY2N1cnJlZFxuICAgKiBAcGFyYW0gYWN0aW9uIERlc2NyaXB0aW9uIG9mIHRoZSBhY3Rpb24gdGhhdCBmYWlsZWRcbiAgICovXG4gIC8vIEhpZGRlbiBmbGFnIG9uIGFsbCBwbHVnaW4vbWFya2V0cGxhY2Ugc3ViY29tbWFuZHMgdG8gdGFyZ2V0IGNvd29ya19wbHVnaW5zLlxuICBjb25zdCBjb3dvcmtPcHRpb24gPSAoKSA9PlxuICAgIG5ldyBPcHRpb24oJy0tY293b3JrJywgJ1VzZSBjb3dvcmtfcGx1Z2lucyBkaXJlY3RvcnknKS5oaWRlSGVscCgpXG5cbiAgLy8gUGx1Z2luIHZhbGlkYXRlIGNvbW1hbmRcbiAgY29uc3QgcGx1Z2luQ21kID0gcHJvZ3JhbVxuICAgIC5jb21tYW5kKCdwbHVnaW4nKVxuICAgIC5hbGlhcygncGx1Z2lucycpXG4gICAgLmRlc2NyaXB0aW9uKCdNYW5hZ2UgQ2xhdWRlIENvZGUgcGx1Z2lucycpXG4gICAgLmNvbmZpZ3VyZUhlbHAoY3JlYXRlU29ydGVkSGVscENvbmZpZygpKVxuXG4gIHBsdWdpbkNtZFxuICAgIC5jb21tYW5kKCd2YWxpZGF0ZSA8cGF0aD4nKVxuICAgIC5kZXNjcmlwdGlvbignVmFsaWRhdGUgYSBwbHVnaW4gb3IgbWFya2V0cGxhY2UgbWFuaWZlc3QnKVxuICAgIC5hZGRPcHRpb24oY293b3JrT3B0aW9uKCkpXG4gICAgLmFjdGlvbihhc3luYyAobWFuaWZlc3RQYXRoOiBzdHJpbmcsIG9wdGlvbnM6IHsgY293b3JrPzogYm9vbGVhbiB9KSA9PiB7XG4gICAgICBjb25zdCB7IHBsdWdpblZhbGlkYXRlSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAnLi9jbGkvaGFuZGxlcnMvcGx1Z2lucy5qcydcbiAgICAgIClcbiAgICAgIGF3YWl0IHBsdWdpblZhbGlkYXRlSGFuZGxlcihtYW5pZmVzdFBhdGgsIG9wdGlvbnMpXG4gICAgfSlcblxuICAvLyBQbHVnaW4gbGlzdCBjb21tYW5kXG4gIHBsdWdpbkNtZFxuICAgIC5jb21tYW5kKCdsaXN0JylcbiAgICAuZGVzY3JpcHRpb24oJ0xpc3QgaW5zdGFsbGVkIHBsdWdpbnMnKVxuICAgIC5vcHRpb24oJy0tanNvbicsICdPdXRwdXQgYXMgSlNPTicpXG4gICAgLm9wdGlvbihcbiAgICAgICctLWF2YWlsYWJsZScsXG4gICAgICAnSW5jbHVkZSBhdmFpbGFibGUgcGx1Z2lucyBmcm9tIG1hcmtldHBsYWNlcyAocmVxdWlyZXMgLS1qc29uKScsXG4gICAgKVxuICAgIC5hZGRPcHRpb24oY293b3JrT3B0aW9uKCkpXG4gICAgLmFjdGlvbihcbiAgICAgIGFzeW5jIChvcHRpb25zOiB7XG4gICAgICAgIGpzb24/OiBib29sZWFuXG4gICAgICAgIGF2YWlsYWJsZT86IGJvb2xlYW5cbiAgICAgICAgY293b3JrPzogYm9vbGVhblxuICAgICAgfSkgPT4ge1xuICAgICAgICBjb25zdCB7IHBsdWdpbkxpc3RIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoJy4vY2xpL2hhbmRsZXJzL3BsdWdpbnMuanMnKVxuICAgICAgICBhd2FpdCBwbHVnaW5MaXN0SGFuZGxlcihvcHRpb25zKVxuICAgICAgfSxcbiAgICApXG5cbiAgLy8gTWFya2V0cGxhY2Ugc3ViY29tbWFuZHNcbiAgY29uc3QgbWFya2V0cGxhY2VDbWQgPSBwbHVnaW5DbWRcbiAgICAuY29tbWFuZCgnbWFya2V0cGxhY2UnKVxuICAgIC5kZXNjcmlwdGlvbignTWFuYWdlIENsYXVkZSBDb2RlIG1hcmtldHBsYWNlcycpXG4gICAgLmNvbmZpZ3VyZUhlbHAoY3JlYXRlU29ydGVkSGVscENvbmZpZygpKVxuXG4gIG1hcmtldHBsYWNlQ21kXG4gICAgLmNvbW1hbmQoJ2FkZCA8c291cmNlPicpXG4gICAgLmRlc2NyaXB0aW9uKCdBZGQgYSBtYXJrZXRwbGFjZSBmcm9tIGEgVVJMLCBwYXRoLCBvciBHaXRIdWIgcmVwbycpXG4gICAgLmFkZE9wdGlvbihjb3dvcmtPcHRpb24oKSlcbiAgICAub3B0aW9uKFxuICAgICAgJy0tc3BhcnNlIDxwYXRocy4uLj4nLFxuICAgICAgJ0xpbWl0IGNoZWNrb3V0IHRvIHNwZWNpZmljIGRpcmVjdG9yaWVzIHZpYSBnaXQgc3BhcnNlLWNoZWNrb3V0IChmb3IgbW9ub3JlcG9zKS4gRXhhbXBsZTogLS1zcGFyc2UgLmNsYXVkZS1wbHVnaW4gcGx1Z2lucycsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1zY29wZSA8c2NvcGU+JyxcbiAgICAgICdXaGVyZSB0byBkZWNsYXJlIHRoZSBtYXJrZXRwbGFjZTogdXNlciAoZGVmYXVsdCksIHByb2plY3QsIG9yIGxvY2FsJyxcbiAgICApXG4gICAgLmFjdGlvbihcbiAgICAgIGFzeW5jIChcbiAgICAgICAgc291cmNlOiBzdHJpbmcsXG4gICAgICAgIG9wdGlvbnM6IHsgY293b3JrPzogYm9vbGVhbjsgc3BhcnNlPzogc3RyaW5nW107IHNjb3BlPzogc3RyaW5nIH0sXG4gICAgICApID0+IHtcbiAgICAgICAgY29uc3QgeyBtYXJrZXRwbGFjZUFkZEhhbmRsZXIgfSA9IGF3YWl0IGltcG9ydChcbiAgICAgICAgICAnLi9jbGkvaGFuZGxlcnMvcGx1Z2lucy5qcydcbiAgICAgICAgKVxuICAgICAgICBhd2FpdCBtYXJrZXRwbGFjZUFkZEhhbmRsZXIoc291cmNlLCBvcHRpb25zKVxuICAgICAgfSxcbiAgICApXG5cbiAgbWFya2V0cGxhY2VDbWRcbiAgICAuY29tbWFuZCgnbGlzdCcpXG4gICAgLmRlc2NyaXB0aW9uKCdMaXN0IGFsbCBjb25maWd1cmVkIG1hcmtldHBsYWNlcycpXG4gICAgLm9wdGlvbignLS1qc29uJywgJ091dHB1dCBhcyBKU09OJylcbiAgICAuYWRkT3B0aW9uKGNvd29ya09wdGlvbigpKVxuICAgIC5hY3Rpb24oYXN5bmMgKG9wdGlvbnM6IHsganNvbj86IGJvb2xlYW47IGNvd29yaz86IGJvb2xlYW4gfSkgPT4ge1xuICAgICAgY29uc3QgeyBtYXJrZXRwbGFjZUxpc3RIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICcuL2NsaS9oYW5kbGVycy9wbHVnaW5zLmpzJ1xuICAgICAgKVxuICAgICAgYXdhaXQgbWFya2V0cGxhY2VMaXN0SGFuZGxlcihvcHRpb25zKVxuICAgIH0pXG5cbiAgbWFya2V0cGxhY2VDbWRcbiAgICAuY29tbWFuZCgncmVtb3ZlIDxuYW1lPicpXG4gICAgLmFsaWFzKCdybScpXG4gICAgLmRlc2NyaXB0aW9uKCdSZW1vdmUgYSBjb25maWd1cmVkIG1hcmtldHBsYWNlJylcbiAgICAuYWRkT3B0aW9uKGNvd29ya09wdGlvbigpKVxuICAgIC5hY3Rpb24oYXN5bmMgKG5hbWU6IHN0cmluZywgb3B0aW9uczogeyBjb3dvcms/OiBib29sZWFuIH0pID0+IHtcbiAgICAgIGNvbnN0IHsgbWFya2V0cGxhY2VSZW1vdmVIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICcuL2NsaS9oYW5kbGVycy9wbHVnaW5zLmpzJ1xuICAgICAgKVxuICAgICAgYXdhaXQgbWFya2V0cGxhY2VSZW1vdmVIYW5kbGVyKG5hbWUsIG9wdGlvbnMpXG4gICAgfSlcblxuICBtYXJrZXRwbGFjZUNtZFxuICAgIC5jb21tYW5kKCd1cGRhdGUgW25hbWVdJylcbiAgICAuZGVzY3JpcHRpb24oXG4gICAgICAnVXBkYXRlIG1hcmtldHBsYWNlKHMpIGZyb20gdGhlaXIgc291cmNlIC0gdXBkYXRlcyBhbGwgaWYgbm8gbmFtZSBzcGVjaWZpZWQnLFxuICAgIClcbiAgICAuYWRkT3B0aW9uKGNvd29ya09wdGlvbigpKVxuICAgIC5hY3Rpb24oYXN5bmMgKG5hbWU6IHN0cmluZyB8IHVuZGVmaW5lZCwgb3B0aW9uczogeyBjb3dvcms/OiBib29sZWFuIH0pID0+IHtcbiAgICAgIGNvbnN0IHsgbWFya2V0cGxhY2VVcGRhdGVIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICcuL2NsaS9oYW5kbGVycy9wbHVnaW5zLmpzJ1xuICAgICAgKVxuICAgICAgYXdhaXQgbWFya2V0cGxhY2VVcGRhdGVIYW5kbGVyKG5hbWUsIG9wdGlvbnMpXG4gICAgfSlcblxuICAvLyBQbHVnaW4gaW5zdGFsbCBjb21tYW5kXG4gIHBsdWdpbkNtZFxuICAgIC5jb21tYW5kKCdpbnN0YWxsIDxwbHVnaW4+JylcbiAgICAuYWxpYXMoJ2knKVxuICAgIC5kZXNjcmlwdGlvbihcbiAgICAgICdJbnN0YWxsIGEgcGx1Z2luIGZyb20gYXZhaWxhYmxlIG1hcmtldHBsYWNlcyAodXNlIHBsdWdpbkBtYXJrZXRwbGFjZSBmb3Igc3BlY2lmaWMgbWFya2V0cGxhY2UpJyxcbiAgICApXG4gICAgLm9wdGlvbihcbiAgICAgICctcywgLS1zY29wZSA8c2NvcGU+JyxcbiAgICAgICdJbnN0YWxsYXRpb24gc2NvcGU6IHVzZXIsIHByb2plY3QsIG9yIGxvY2FsJyxcbiAgICAgICd1c2VyJyxcbiAgICApXG4gICAgLmFkZE9wdGlvbihjb3dvcmtPcHRpb24oKSlcbiAgICAuYWN0aW9uKFxuICAgICAgYXN5bmMgKHBsdWdpbjogc3RyaW5nLCBvcHRpb25zOiB7IHNjb3BlPzogc3RyaW5nOyBjb3dvcms/OiBib29sZWFuIH0pID0+IHtcbiAgICAgICAgY29uc3QgeyBwbHVnaW5JbnN0YWxsSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICcuL2NsaS9oYW5kbGVycy9wbHVnaW5zLmpzJ1xuICAgICAgICApXG4gICAgICAgIGF3YWl0IHBsdWdpbkluc3RhbGxIYW5kbGVyKHBsdWdpbiwgb3B0aW9ucylcbiAgICAgIH0sXG4gICAgKVxuXG4gIC8vIFBsdWdpbiB1bmluc3RhbGwgY29tbWFuZFxuICBwbHVnaW5DbWRcbiAgICAuY29tbWFuZCgndW5pbnN0YWxsIDxwbHVnaW4+JylcbiAgICAuYWxpYXMoJ3JlbW92ZScpXG4gICAgLmFsaWFzKCdybScpXG4gICAgLmRlc2NyaXB0aW9uKCdVbmluc3RhbGwgYW4gaW5zdGFsbGVkIHBsdWdpbicpXG4gICAgLm9wdGlvbihcbiAgICAgICctcywgLS1zY29wZSA8c2NvcGU+JyxcbiAgICAgICdVbmluc3RhbGwgZnJvbSBzY29wZTogdXNlciwgcHJvamVjdCwgb3IgbG9jYWwnLFxuICAgICAgJ3VzZXInLFxuICAgIClcbiAgICAub3B0aW9uKFxuICAgICAgJy0ta2VlcC1kYXRhJyxcbiAgICAgIFwiUHJlc2VydmUgdGhlIHBsdWdpbidzIHBlcnNpc3RlbnQgZGF0YSBkaXJlY3RvcnkgKH4vLmNsYXVkZS9wbHVnaW5zL2RhdGEve2lkfS8pXCIsXG4gICAgKVxuICAgIC5hZGRPcHRpb24oY293b3JrT3B0aW9uKCkpXG4gICAgLmFjdGlvbihcbiAgICAgIGFzeW5jIChcbiAgICAgICAgcGx1Z2luOiBzdHJpbmcsXG4gICAgICAgIG9wdGlvbnM6IHsgc2NvcGU/OiBzdHJpbmc7IGNvd29yaz86IGJvb2xlYW47IGtlZXBEYXRhPzogYm9vbGVhbiB9LFxuICAgICAgKSA9PiB7XG4gICAgICAgIGNvbnN0IHsgcGx1Z2luVW5pbnN0YWxsSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICcuL2NsaS9oYW5kbGVycy9wbHVnaW5zLmpzJ1xuICAgICAgICApXG4gICAgICAgIGF3YWl0IHBsdWdpblVuaW5zdGFsbEhhbmRsZXIocGx1Z2luLCBvcHRpb25zKVxuICAgICAgfSxcbiAgICApXG5cbiAgLy8gUGx1Z2luIGVuYWJsZSBjb21tYW5kXG4gIHBsdWdpbkNtZFxuICAgIC5jb21tYW5kKCdlbmFibGUgPHBsdWdpbj4nKVxuICAgIC5kZXNjcmlwdGlvbignRW5hYmxlIGEgZGlzYWJsZWQgcGx1Z2luJylcbiAgICAub3B0aW9uKFxuICAgICAgJy1zLCAtLXNjb3BlIDxzY29wZT4nLFxuICAgICAgYEluc3RhbGxhdGlvbiBzY29wZTogJHtWQUxJRF9JTlNUQUxMQUJMRV9TQ09QRVMuam9pbignLCAnKX0gKGRlZmF1bHQ6IGF1dG8tZGV0ZWN0KWAsXG4gICAgKVxuICAgIC5hZGRPcHRpb24oY293b3JrT3B0aW9uKCkpXG4gICAgLmFjdGlvbihcbiAgICAgIGFzeW5jIChwbHVnaW46IHN0cmluZywgb3B0aW9uczogeyBzY29wZT86IHN0cmluZzsgY293b3JrPzogYm9vbGVhbiB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgcGx1Z2luRW5hYmxlSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICcuL2NsaS9oYW5kbGVycy9wbHVnaW5zLmpzJ1xuICAgICAgICApXG4gICAgICAgIGF3YWl0IHBsdWdpbkVuYWJsZUhhbmRsZXIocGx1Z2luLCBvcHRpb25zKVxuICAgICAgfSxcbiAgICApXG5cbiAgLy8gUGx1Z2luIGRpc2FibGUgY29tbWFuZFxuICBwbHVnaW5DbWRcbiAgICAuY29tbWFuZCgnZGlzYWJsZSBbcGx1Z2luXScpXG4gICAgLmRlc2NyaXB0aW9uKCdEaXNhYmxlIGFuIGVuYWJsZWQgcGx1Z2luJylcbiAgICAub3B0aW9uKCctYSwgLS1hbGwnLCAnRGlzYWJsZSBhbGwgZW5hYmxlZCBwbHVnaW5zJylcbiAgICAub3B0aW9uKFxuICAgICAgJy1zLCAtLXNjb3BlIDxzY29wZT4nLFxuICAgICAgYEluc3RhbGxhdGlvbiBzY29wZTogJHtWQUxJRF9JTlNUQUxMQUJMRV9TQ09QRVMuam9pbignLCAnKX0gKGRlZmF1bHQ6IGF1dG8tZGV0ZWN0KWAsXG4gICAgKVxuICAgIC5hZGRPcHRpb24oY293b3JrT3B0aW9uKCkpXG4gICAgLmFjdGlvbihcbiAgICAgIGFzeW5jIChcbiAgICAgICAgcGx1Z2luOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gICAgICAgIG9wdGlvbnM6IHsgc2NvcGU/OiBzdHJpbmc7IGNvd29yaz86IGJvb2xlYW47IGFsbD86IGJvb2xlYW4gfSxcbiAgICAgICkgPT4ge1xuICAgICAgICBjb25zdCB7IHBsdWdpbkRpc2FibGVIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICAgJy4vY2xpL2hhbmRsZXJzL3BsdWdpbnMuanMnXG4gICAgICAgIClcbiAgICAgICAgYXdhaXQgcGx1Z2luRGlzYWJsZUhhbmRsZXIocGx1Z2luLCBvcHRpb25zKVxuICAgICAgfSxcbiAgICApXG5cbiAgLy8gUGx1Z2luIHVwZGF0ZSBjb21tYW5kXG4gIHBsdWdpbkNtZFxuICAgIC5jb21tYW5kKCd1cGRhdGUgPHBsdWdpbj4nKVxuICAgIC5kZXNjcmlwdGlvbihcbiAgICAgICdVcGRhdGUgYSBwbHVnaW4gdG8gdGhlIGxhdGVzdCB2ZXJzaW9uIChyZXN0YXJ0IHJlcXVpcmVkIHRvIGFwcGx5KScsXG4gICAgKVxuICAgIC5vcHRpb24oXG4gICAgICAnLXMsIC0tc2NvcGUgPHNjb3BlPicsXG4gICAgICBgSW5zdGFsbGF0aW9uIHNjb3BlOiAke1ZBTElEX1VQREFURV9TQ09QRVMuam9pbignLCAnKX0gKGRlZmF1bHQ6IHVzZXIpYCxcbiAgICApXG4gICAgLmFkZE9wdGlvbihjb3dvcmtPcHRpb24oKSlcbiAgICAuYWN0aW9uKFxuICAgICAgYXN5bmMgKHBsdWdpbjogc3RyaW5nLCBvcHRpb25zOiB7IHNjb3BlPzogc3RyaW5nOyBjb3dvcms/OiBib29sZWFuIH0pID0+IHtcbiAgICAgICAgY29uc3QgeyBwbHVnaW5VcGRhdGVIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoXG4gICAgICAgICAgJy4vY2xpL2hhbmRsZXJzL3BsdWdpbnMuanMnXG4gICAgICAgIClcbiAgICAgICAgYXdhaXQgcGx1Z2luVXBkYXRlSGFuZGxlcihwbHVnaW4sIG9wdGlvbnMpXG4gICAgICB9LFxuICAgIClcbiAgLy8gRU5EIEFOVC1PTkxZXG5cbiAgLy8gU2V0dXAgdG9rZW4gY29tbWFuZFxuICBwcm9ncmFtXG4gICAgLmNvbW1hbmQoJ3NldHVwLXRva2VuJylcbiAgICAuZGVzY3JpcHRpb24oXG4gICAgICAnU2V0IHVwIGEgbG9uZy1saXZlZCBhdXRoZW50aWNhdGlvbiB0b2tlbiAocmVxdWlyZXMgQ2xhdWRlIHN1YnNjcmlwdGlvbiknLFxuICAgIClcbiAgICAuYWN0aW9uKGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IFt7IHNldHVwVG9rZW5IYW5kbGVyIH0sIHsgY3JlYXRlUm9vdCB9XSA9IGF3YWl0IFByb21pc2UuYWxsKFtcbiAgICAgICAgaW1wb3J0KCcuL2NsaS9oYW5kbGVycy91dGlsLmpzJyksXG4gICAgICAgIGltcG9ydCgnLi9pbmsuanMnKSxcbiAgICAgIF0pXG4gICAgICBjb25zdCByb290ID0gYXdhaXQgY3JlYXRlUm9vdChnZXRCYXNlUmVuZGVyT3B0aW9ucyhmYWxzZSkpXG4gICAgICBhd2FpdCBzZXR1cFRva2VuSGFuZGxlcihyb290KVxuICAgIH0pXG5cbiAgLy8gQWdlbnRzIGNvbW1hbmQgLSBsaXN0IGNvbmZpZ3VyZWQgYWdlbnRzXG4gIHByb2dyYW1cbiAgICAuY29tbWFuZCgnYWdlbnRzJylcbiAgICAuZGVzY3JpcHRpb24oJ0xpc3QgY29uZmlndXJlZCBhZ2VudHMnKVxuICAgIC5vcHRpb24oXG4gICAgICAnLS1zZXR0aW5nLXNvdXJjZXMgPHNvdXJjZXM+JyxcbiAgICAgICdDb21tYS1zZXBhcmF0ZWQgbGlzdCBvZiBzZXR0aW5nIHNvdXJjZXMgdG8gbG9hZCAodXNlciwgcHJvamVjdCwgbG9jYWwpLicsXG4gICAgKVxuICAgIC5hY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgeyBhZ2VudHNIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoJy4vY2xpL2hhbmRsZXJzL2FnZW50cy5qcycpXG4gICAgICBhd2FpdCBhZ2VudHNIYW5kbGVyKClcbiAgICAgIHByb2Nlc3MuZXhpdCgwKVxuICAgIH0pXG5cbiAgaWYgKGZlYXR1cmUoJ1RSQU5TQ1JJUFRfQ0xBU1NJRklFUicpKSB7XG4gICAgLy8gU2tpcCB3aGVuIHRlbmd1X2F1dG9fbW9kZV9jb25maWcuZW5hYmxlZCA9PT0gJ2Rpc2FibGVkJyAoY2lyY3VpdCBicmVha2VyKS5cbiAgICAvLyBSZWFkcyBmcm9tIGRpc2sgY2FjaGUg4oCUIEdyb3d0aEJvb2sgaXNuJ3QgaW5pdGlhbGl6ZWQgYXQgcmVnaXN0cmF0aW9uIHRpbWUuXG4gICAgaWYgKGdldEF1dG9Nb2RlRW5hYmxlZFN0YXRlSWZDYWNoZWQoKSAhPT0gJ2Rpc2FibGVkJykge1xuICAgICAgY29uc3QgYXV0b01vZGVDbWQgPSBwcm9ncmFtXG4gICAgICAgIC5jb21tYW5kKCdhdXRvLW1vZGUnKVxuICAgICAgICAuZGVzY3JpcHRpb24oJ0luc3BlY3QgYXV0byBtb2RlIGNsYXNzaWZpZXIgY29uZmlndXJhdGlvbicpXG5cbiAgICAgIGF1dG9Nb2RlQ21kXG4gICAgICAgIC5jb21tYW5kKCdkZWZhdWx0cycpXG4gICAgICAgIC5kZXNjcmlwdGlvbihcbiAgICAgICAgICAnUHJpbnQgdGhlIGRlZmF1bHQgYXV0byBtb2RlIGVudmlyb25tZW50LCBhbGxvdywgYW5kIGRlbnkgcnVsZXMgYXMgSlNPTicsXG4gICAgICAgIClcbiAgICAgICAgLmFjdGlvbihhc3luYyAoKSA9PiB7XG4gICAgICAgICAgY29uc3QgeyBhdXRvTW9kZURlZmF1bHRzSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgJy4vY2xpL2hhbmRsZXJzL2F1dG9Nb2RlLmpzJ1xuICAgICAgICAgIClcbiAgICAgICAgICBhdXRvTW9kZURlZmF1bHRzSGFuZGxlcigpXG4gICAgICAgICAgcHJvY2Vzcy5leGl0KDApXG4gICAgICAgIH0pXG5cbiAgICAgIGF1dG9Nb2RlQ21kXG4gICAgICAgIC5jb21tYW5kKCdjb25maWcnKVxuICAgICAgICAuZGVzY3JpcHRpb24oXG4gICAgICAgICAgJ1ByaW50IHRoZSBlZmZlY3RpdmUgYXV0byBtb2RlIGNvbmZpZyBhcyBKU09OOiB5b3VyIHNldHRpbmdzIHdoZXJlIHNldCwgZGVmYXVsdHMgb3RoZXJ3aXNlJyxcbiAgICAgICAgKVxuICAgICAgICAuYWN0aW9uKGFzeW5jICgpID0+IHtcbiAgICAgICAgICBjb25zdCB7IGF1dG9Nb2RlQ29uZmlnSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgJy4vY2xpL2hhbmRsZXJzL2F1dG9Nb2RlLmpzJ1xuICAgICAgICAgIClcbiAgICAgICAgICBhdXRvTW9kZUNvbmZpZ0hhbmRsZXIoKVxuICAgICAgICAgIHByb2Nlc3MuZXhpdCgwKVxuICAgICAgICB9KVxuXG4gICAgICBhdXRvTW9kZUNtZFxuICAgICAgICAuY29tbWFuZCgnY3JpdGlxdWUnKVxuICAgICAgICAuZGVzY3JpcHRpb24oJ0dldCBBSSBmZWVkYmFjayBvbiB5b3VyIGN1c3RvbSBhdXRvIG1vZGUgcnVsZXMnKVxuICAgICAgICAub3B0aW9uKCctLW1vZGVsIDxtb2RlbD4nLCAnT3ZlcnJpZGUgd2hpY2ggbW9kZWwgaXMgdXNlZCcpXG4gICAgICAgIC5hY3Rpb24oYXN5bmMgb3B0aW9ucyA9PiB7XG4gICAgICAgICAgY29uc3QgeyBhdXRvTW9kZUNyaXRpcXVlSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KFxuICAgICAgICAgICAgJy4vY2xpL2hhbmRsZXJzL2F1dG9Nb2RlLmpzJ1xuICAgICAgICAgIClcbiAgICAgICAgICBhd2FpdCBhdXRvTW9kZUNyaXRpcXVlSGFuZGxlcihvcHRpb25zKVxuICAgICAgICAgIHByb2Nlc3MuZXhpdCgpXG4gICAgICAgIH0pXG4gICAgfVxuICB9XG5cbiAgLy8gUmVtb3RlIENvbnRyb2wgY29tbWFuZCDigJQgY29ubmVjdCBsb2NhbCBlbnZpcm9ubWVudCB0byBjbGF1ZGUuYWkvY29kZS5cbiAgLy8gVGhlIGFjdHVhbCBjb21tYW5kIGlzIGludGVyY2VwdGVkIGJ5IHRoZSBmYXN0LXBhdGggaW4gY2xpLnRzeCBiZWZvcmVcbiAgLy8gQ29tbWFuZGVyLmpzIHJ1bnMsIHNvIHRoaXMgcmVnaXN0cmF0aW9uIGV4aXN0cyBvbmx5IGZvciBoZWxwIG91dHB1dC5cbiAgLy8gQWx3YXlzIGhpZGRlbjogaXNCcmlkZ2VFbmFibGVkKCkgYXQgdGhpcyBwb2ludCAoYmVmb3JlIGVuYWJsZUNvbmZpZ3MpXG4gIC8vIHdvdWxkIHRocm93IGluc2lkZSBpc0NsYXVkZUFJU3Vic2NyaWJlciDihpIgZ2V0R2xvYmFsQ29uZmlnIGFuZCByZXR1cm5cbiAgLy8gZmFsc2UgdmlhIHRoZSB0cnkvY2F0Y2gg4oCUIGJ1dCBub3QgYmVmb3JlIHBheWluZyB+NjVtcyBvZiBzaWRlIGVmZmVjdHNcbiAgLy8gKDI1bXMgc2V0dGluZ3MgWm9kIHBhcnNlICsgNDBtcyBzeW5jIGBzZWN1cml0eWAga2V5Y2hhaW4gc3VicHJvY2VzcykuXG4gIC8vIFRoZSBkeW5hbWljIHZpc2liaWxpdHkgbmV2ZXIgd29ya2VkOyB0aGUgY29tbWFuZCB3YXMgYWx3YXlzIGhpZGRlbi5cbiAgaWYgKGZlYXR1cmUoJ0JSSURHRV9NT0RFJykpIHtcbiAgICBwcm9ncmFtXG4gICAgICAuY29tbWFuZCgncmVtb3RlLWNvbnRyb2wnLCB7IGhpZGRlbjogdHJ1ZSB9KVxuICAgICAgLmFsaWFzKCdyYycpXG4gICAgICAuZGVzY3JpcHRpb24oXG4gICAgICAgICdDb25uZWN0IHlvdXIgbG9jYWwgZW52aXJvbm1lbnQgZm9yIHJlbW90ZS1jb250cm9sIHNlc3Npb25zIHZpYSBjbGF1ZGUuYWkvY29kZScsXG4gICAgICApXG4gICAgICAuYWN0aW9uKGFzeW5jICgpID0+IHtcbiAgICAgICAgLy8gVW5yZWFjaGFibGUg4oCUIGNsaS50c3ggZmFzdC1wYXRoIGhhbmRsZXMgdGhpcyBjb21tYW5kIGJlZm9yZSBtYWluLnRzeCBsb2Fkcy5cbiAgICAgICAgLy8gSWYgc29tZWhvdyByZWFjaGVkLCBkZWxlZ2F0ZSB0byBicmlkZ2VNYWluLlxuICAgICAgICBjb25zdCB7IGJyaWRnZU1haW4gfSA9IGF3YWl0IGltcG9ydCgnLi9icmlkZ2UvYnJpZGdlTWFpbi5qcycpXG4gICAgICAgIGF3YWl0IGJyaWRnZU1haW4ocHJvY2Vzcy5hcmd2LnNsaWNlKDMpKVxuICAgICAgfSlcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdLQUlST1MnKSkge1xuICAgIHByb2dyYW1cbiAgICAgIC5jb21tYW5kKCdhc3Npc3RhbnQgW3Nlc3Npb25JZF0nKVxuICAgICAgLmRlc2NyaXB0aW9uKFxuICAgICAgICAnQXR0YWNoIHRoZSBSRVBMIGFzIGEgY2xpZW50IHRvIGEgcnVubmluZyBicmlkZ2Ugc2Vzc2lvbi4gRGlzY292ZXJzIHNlc3Npb25zIHZpYSBBUEkgaWYgbm8gc2Vzc2lvbklkIGdpdmVuLicsXG4gICAgICApXG4gICAgICAuYWN0aW9uKCgpID0+IHtcbiAgICAgICAgLy8gQXJndiByZXdyaXRpbmcgYWJvdmUgc2hvdWxkIGhhdmUgY29uc3VtZWQgYGFzc2lzdGFudCBbaWRdYFxuICAgICAgICAvLyBiZWZvcmUgY29tbWFuZGVyIHJ1bnMuIFJlYWNoaW5nIGhlcmUgbWVhbnMgYSByb290IGZsYWcgY2FtZSBmaXJzdFxuICAgICAgICAvLyAoZS5nLiBgLS1kZWJ1ZyBhc3Npc3RhbnRgKSBhbmQgdGhlIHBvc2l0aW9uLTAgcHJlZGljYXRlXG4gICAgICAgIC8vIGRpZG4ndCBtYXRjaC4gUHJpbnQgdXNhZ2UgbGlrZSB0aGUgc3NoIHN0dWIgZG9lcy5cbiAgICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoXG4gICAgICAgICAgJ1VzYWdlOiBjbGF1ZGUgYXNzaXN0YW50IFtzZXNzaW9uSWRdXFxuXFxuJyArXG4gICAgICAgICAgICAnQXR0YWNoIHRoZSBSRVBMIGFzIGEgdmlld2VyIGNsaWVudCB0byBhIHJ1bm5pbmcgYnJpZGdlIHNlc3Npb24uXFxuJyArXG4gICAgICAgICAgICAnT21pdCBzZXNzaW9uSWQgdG8gZGlzY292ZXIgYW5kIHBpY2sgZnJvbSBhdmFpbGFibGUgc2Vzc2lvbnMuXFxuJyxcbiAgICAgICAgKVxuICAgICAgICBwcm9jZXNzLmV4aXQoMSlcbiAgICAgIH0pXG4gIH1cblxuICAvLyBEb2N0b3IgY29tbWFuZCAtIGNoZWNrIGluc3RhbGxhdGlvbiBoZWFsdGhcbiAgcHJvZ3JhbVxuICAgIC5jb21tYW5kKCdkb2N0b3InKVxuICAgIC5kZXNjcmlwdGlvbihcbiAgICAgICdDaGVjayB0aGUgaGVhbHRoIG9mIHlvdXIgQ2xhdWRlIENvZGUgYXV0by11cGRhdGVyLiBOb3RlOiBUaGUgd29ya3NwYWNlIHRydXN0IGRpYWxvZyBpcyBza2lwcGVkIGFuZCBzdGRpbyBzZXJ2ZXJzIGZyb20gLm1jcC5qc29uIGFyZSBzcGF3bmVkIGZvciBoZWFsdGggY2hlY2tzLiBPbmx5IHVzZSB0aGlzIGNvbW1hbmQgaW4gZGlyZWN0b3JpZXMgeW91IHRydXN0LicsXG4gICAgKVxuICAgIC5hY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgW3sgZG9jdG9ySGFuZGxlciB9LCB7IGNyZWF0ZVJvb3QgfV0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICAgIGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvdXRpbC5qcycpLFxuICAgICAgICBpbXBvcnQoJy4vaW5rLmpzJyksXG4gICAgICBdKVxuICAgICAgY29uc3Qgcm9vdCA9IGF3YWl0IGNyZWF0ZVJvb3QoZ2V0QmFzZVJlbmRlck9wdGlvbnMoZmFsc2UpKVxuICAgICAgYXdhaXQgZG9jdG9ySGFuZGxlcihyb290KVxuICAgIH0pXG5cbiAgLy8gY2xhdWRlIHVwZGF0ZVxuICAvL1xuICAvLyBGb3IgU2VtVmVyLWNvbXBsaWFudCB2ZXJzaW9uaW5nIHdpdGggYnVpbGQgbWV0YWRhdGEgKFguWC5YK1NIQSk6XG4gIC8vIC0gV2UgcGVyZm9ybSBleGFjdCBzdHJpbmcgY29tcGFyaXNvbiAoaW5jbHVkaW5nIFNIQSkgdG8gZGV0ZWN0IGFueSBjaGFuZ2VcbiAgLy8gLSBUaGlzIGVuc3VyZXMgdXNlcnMgYWx3YXlzIGdldCB0aGUgbGF0ZXN0IGJ1aWxkLCBldmVuIHdoZW4gb25seSB0aGUgU0hBIGNoYW5nZXNcbiAgLy8gLSBVSSBzaG93cyBib3RoIHZlcnNpb25zIGluY2x1ZGluZyBidWlsZCBtZXRhZGF0YSBmb3IgY2xhcml0eVxuICBwcm9ncmFtXG4gICAgLmNvbW1hbmQoJ3VwZGF0ZScpXG4gICAgLmFsaWFzKCd1cGdyYWRlJylcbiAgICAuZGVzY3JpcHRpb24oJ0NoZWNrIGZvciB1cGRhdGVzIGFuZCBpbnN0YWxsIGlmIGF2YWlsYWJsZScpXG4gICAgLmFjdGlvbihhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCB7IHVwZGF0ZSB9ID0gYXdhaXQgaW1wb3J0KCdzcmMvY2xpL3VwZGF0ZS5qcycpXG4gICAgICBhd2FpdCB1cGRhdGUoKVxuICAgIH0pXG5cbiAgLy8gY2xhdWRlIHVwIOKAlCBydW4gdGhlIHByb2plY3QncyBDTEFVREUubWQgXCIjIGNsYXVkZSB1cFwiIHNldHVwIGluc3RydWN0aW9ucy5cbiAgaWYgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcpIHtcbiAgICBwcm9ncmFtXG4gICAgICAuY29tbWFuZCgndXAnKVxuICAgICAgLmRlc2NyaXB0aW9uKFxuICAgICAgICAnW0FOVC1PTkxZXSBJbml0aWFsaXplIG9yIHVwZ3JhZGUgdGhlIGxvY2FsIGRldiBlbnZpcm9ubWVudCB1c2luZyB0aGUgXCIjIGNsYXVkZSB1cFwiIHNlY3Rpb24gb2YgdGhlIG5lYXJlc3QgQ0xBVURFLm1kJyxcbiAgICAgIClcbiAgICAgIC5hY3Rpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgICBjb25zdCB7IHVwIH0gPSBhd2FpdCBpbXBvcnQoJ3NyYy9jbGkvdXAuanMnKVxuICAgICAgICBhd2FpdCB1cCgpXG4gICAgICB9KVxuICB9XG5cbiAgLy8gY2xhdWRlIHJvbGxiYWNrIChhbnQtb25seSlcbiAgLy8gUm9sbHMgYmFjayB0byBwcmV2aW91cyByZWxlYXNlc1xuICBpZiAoXCJleHRlcm5hbFwiID09PSAnYW50Jykge1xuICAgIHByb2dyYW1cbiAgICAgIC5jb21tYW5kKCdyb2xsYmFjayBbdGFyZ2V0XScpXG4gICAgICAuZGVzY3JpcHRpb24oXG4gICAgICAgICdbQU5ULU9OTFldIFJvbGwgYmFjayB0byBhIHByZXZpb3VzIHJlbGVhc2VcXG5cXG5FeGFtcGxlczpcXG4gIGNsYXVkZSByb2xsYmFjayAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdvIDEgdmVyc2lvbiBiYWNrIGZyb20gY3VycmVudFxcbiAgY2xhdWRlIHJvbGxiYWNrIDMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR28gMyB2ZXJzaW9ucyBiYWNrIGZyb20gY3VycmVudFxcbiAgY2xhdWRlIHJvbGxiYWNrIDIuMC43My1kZXYuMjAyNTEyMTcudDE5MDY1OCAgICAgICAgUm9sbCBiYWNrIHRvIGEgc3BlY2lmaWMgdmVyc2lvbicsXG4gICAgICApXG4gICAgICAub3B0aW9uKCctbCwgLS1saXN0JywgJ0xpc3QgcmVjZW50IHB1Ymxpc2hlZCB2ZXJzaW9ucyB3aXRoIGFnZXMnKVxuICAgICAgLm9wdGlvbignLS1kcnktcnVuJywgJ1Nob3cgd2hhdCB3b3VsZCBiZSBpbnN0YWxsZWQgd2l0aG91dCBpbnN0YWxsaW5nJylcbiAgICAgIC5vcHRpb24oXG4gICAgICAgICctLXNhZmUnLFxuICAgICAgICAnUm9sbCBiYWNrIHRvIHRoZSBzZXJ2ZXItcGlubmVkIHNhZmUgdmVyc2lvbiAoc2V0IGJ5IG9uY2FsbCBkdXJpbmcgaW5jaWRlbnRzKScsXG4gICAgICApXG4gICAgICAuYWN0aW9uKFxuICAgICAgICBhc3luYyAoXG4gICAgICAgICAgdGFyZ2V0Pzogc3RyaW5nLFxuICAgICAgICAgIG9wdGlvbnM/OiB7IGxpc3Q/OiBib29sZWFuOyBkcnlSdW4/OiBib29sZWFuOyBzYWZlPzogYm9vbGVhbiB9LFxuICAgICAgICApID0+IHtcbiAgICAgICAgICBjb25zdCB7IHJvbGxiYWNrIH0gPSBhd2FpdCBpbXBvcnQoJ3NyYy9jbGkvcm9sbGJhY2suanMnKVxuICAgICAgICAgIGF3YWl0IHJvbGxiYWNrKHRhcmdldCwgb3B0aW9ucylcbiAgICAgICAgfSxcbiAgICAgIClcbiAgfVxuXG4gIC8vIGNsYXVkZSBpbnN0YWxsXG4gIHByb2dyYW1cbiAgICAuY29tbWFuZCgnaW5zdGFsbCBbdGFyZ2V0XScpXG4gICAgLmRlc2NyaXB0aW9uKFxuICAgICAgJ0luc3RhbGwgQ2xhdWRlIENvZGUgbmF0aXZlIGJ1aWxkLiBVc2UgW3RhcmdldF0gdG8gc3BlY2lmeSB2ZXJzaW9uIChzdGFibGUsIGxhdGVzdCwgb3Igc3BlY2lmaWMgdmVyc2lvbiknLFxuICAgIClcbiAgICAub3B0aW9uKCctLWZvcmNlJywgJ0ZvcmNlIGluc3RhbGxhdGlvbiBldmVuIGlmIGFscmVhZHkgaW5zdGFsbGVkJylcbiAgICAuYWN0aW9uKFxuICAgICAgYXN5bmMgKHRhcmdldDogc3RyaW5nIHwgdW5kZWZpbmVkLCBvcHRpb25zOiB7IGZvcmNlPzogYm9vbGVhbiB9KSA9PiB7XG4gICAgICAgIGNvbnN0IHsgaW5zdGFsbEhhbmRsZXIgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvdXRpbC5qcycpXG4gICAgICAgIGF3YWl0IGluc3RhbGxIYW5kbGVyKHRhcmdldCwgb3B0aW9ucylcbiAgICAgIH0sXG4gICAgKVxuXG4gIC8vIGFudC1vbmx5IGNvbW1hbmRzXG4gIGlmIChcImV4dGVybmFsXCIgPT09ICdhbnQnKSB7XG4gICAgY29uc3QgdmFsaWRhdGVMb2dJZCA9ICh2YWx1ZTogc3RyaW5nKSA9PiB7XG4gICAgICBjb25zdCBtYXliZVNlc3Npb25JZCA9IHZhbGlkYXRlVXVpZCh2YWx1ZSlcbiAgICAgIGlmIChtYXliZVNlc3Npb25JZCkgcmV0dXJuIG1heWJlU2Vzc2lvbklkXG4gICAgICByZXR1cm4gTnVtYmVyKHZhbHVlKVxuICAgIH1cbiAgICAvLyBjbGF1ZGUgbG9nXG4gICAgcHJvZ3JhbVxuICAgICAgLmNvbW1hbmQoJ2xvZycpXG4gICAgICAuZGVzY3JpcHRpb24oJ1tBTlQtT05MWV0gTWFuYWdlIGNvbnZlcnNhdGlvbiBsb2dzLicpXG4gICAgICAuYXJndW1lbnQoXG4gICAgICAgICdbbnVtYmVyfHNlc3Npb25JZF0nLFxuICAgICAgICAnQSBudW1iZXIgKDAsIDEsIDIsIGV0Yy4pIHRvIGRpc3BsYXkgYSBzcGVjaWZpYyBsb2csIG9yIHRoZSBzZXNzc2lvbiBJRCAodXVpZCkgb2YgYSBsb2cnLFxuICAgICAgICB2YWxpZGF0ZUxvZ0lkLFxuICAgICAgKVxuICAgICAgLmFjdGlvbihhc3luYyAobG9nSWQ6IHN0cmluZyB8IG51bWJlciB8IHVuZGVmaW5lZCkgPT4ge1xuICAgICAgICBjb25zdCB7IGxvZ0hhbmRsZXIgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvYW50LmpzJylcbiAgICAgICAgYXdhaXQgbG9nSGFuZGxlcihsb2dJZClcbiAgICAgIH0pXG5cbiAgICAvLyBjbGF1ZGUgZXJyb3JcbiAgICBwcm9ncmFtXG4gICAgICAuY29tbWFuZCgnZXJyb3InKVxuICAgICAgLmRlc2NyaXB0aW9uKFxuICAgICAgICAnW0FOVC1PTkxZXSBWaWV3IGVycm9yIGxvZ3MuIE9wdGlvbmFsbHkgcHJvdmlkZSBhIG51bWJlciAoMCwgLTEsIC0yLCBldGMuKSB0byBkaXNwbGF5IGEgc3BlY2lmaWMgbG9nLicsXG4gICAgICApXG4gICAgICAuYXJndW1lbnQoXG4gICAgICAgICdbbnVtYmVyXScsXG4gICAgICAgICdBIG51bWJlciAoMCwgMSwgMiwgZXRjLikgdG8gZGlzcGxheSBhIHNwZWNpZmljIGxvZycsXG4gICAgICAgIHBhcnNlSW50LFxuICAgICAgKVxuICAgICAgLmFjdGlvbihhc3luYyAobnVtYmVyOiBudW1iZXIgfCB1bmRlZmluZWQpID0+IHtcbiAgICAgICAgY29uc3QgeyBlcnJvckhhbmRsZXIgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvYW50LmpzJylcbiAgICAgICAgYXdhaXQgZXJyb3JIYW5kbGVyKG51bWJlcilcbiAgICAgIH0pXG5cbiAgICAvLyBjbGF1ZGUgZXhwb3J0XG4gICAgcHJvZ3JhbVxuICAgICAgLmNvbW1hbmQoJ2V4cG9ydCcpXG4gICAgICAuZGVzY3JpcHRpb24oJ1tBTlQtT05MWV0gRXhwb3J0IGEgY29udmVyc2F0aW9uIHRvIGEgdGV4dCBmaWxlLicpXG4gICAgICAudXNhZ2UoJzxzb3VyY2U+IDxvdXRwdXRGaWxlPicpXG4gICAgICAuYXJndW1lbnQoXG4gICAgICAgICc8c291cmNlPicsXG4gICAgICAgICdTZXNzaW9uIElELCBsb2cgaW5kZXggKDAsIDEsIDIuLi4pLCBvciBwYXRoIHRvIGEgLmpzb24vLmpzb25sIGxvZyBmaWxlJyxcbiAgICAgIClcbiAgICAgIC5hcmd1bWVudCgnPG91dHB1dEZpbGU+JywgJ091dHB1dCBmaWxlIHBhdGggZm9yIHRoZSBleHBvcnRlZCB0ZXh0JylcbiAgICAgIC5hZGRIZWxwVGV4dChcbiAgICAgICAgJ2FmdGVyJyxcbiAgICAgICAgYFxuRXhhbXBsZXM6XG4gICQgY2xhdWRlIGV4cG9ydCAwIGNvbnZlcnNhdGlvbi50eHQgICAgICAgICAgICAgICAgRXhwb3J0IGNvbnZlcnNhdGlvbiBhdCBsb2cgaW5kZXggMFxuICAkIGNsYXVkZSBleHBvcnQgPHV1aWQ+IGNvbnZlcnNhdGlvbi50eHQgICAgICAgICAgIEV4cG9ydCBjb252ZXJzYXRpb24gYnkgc2Vzc2lvbiBJRFxuICAkIGNsYXVkZSBleHBvcnQgaW5wdXQuanNvbiBvdXRwdXQudHh0ICAgICAgICAgICAgIFJlbmRlciBKU09OIGxvZyBmaWxlIHRvIHRleHRcbiAgJCBjbGF1ZGUgZXhwb3J0IDx1dWlkPi5qc29ubCBvdXRwdXQudHh0ICAgICAgICAgICBSZW5kZXIgSlNPTkwgc2Vzc2lvbiBmaWxlIHRvIHRleHRgLFxuICAgICAgKVxuICAgICAgLmFjdGlvbihhc3luYyAoc291cmNlOiBzdHJpbmcsIG91dHB1dEZpbGU6IHN0cmluZykgPT4ge1xuICAgICAgICBjb25zdCB7IGV4cG9ydEhhbmRsZXIgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvYW50LmpzJylcbiAgICAgICAgYXdhaXQgZXhwb3J0SGFuZGxlcihzb3VyY2UsIG91dHB1dEZpbGUpXG4gICAgICB9KVxuXG4gICAgaWYgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcpIHtcbiAgICAgIGNvbnN0IHRhc2tDbWQgPSBwcm9ncmFtXG4gICAgICAgIC5jb21tYW5kKCd0YXNrJylcbiAgICAgICAgLmRlc2NyaXB0aW9uKCdbQU5ULU9OTFldIE1hbmFnZSB0YXNrIGxpc3QgdGFza3MnKVxuXG4gICAgICB0YXNrQ21kXG4gICAgICAgIC5jb21tYW5kKCdjcmVhdGUgPHN1YmplY3Q+JylcbiAgICAgICAgLmRlc2NyaXB0aW9uKCdDcmVhdGUgYSBuZXcgdGFzaycpXG4gICAgICAgIC5vcHRpb24oJy1kLCAtLWRlc2NyaXB0aW9uIDx0ZXh0PicsICdUYXNrIGRlc2NyaXB0aW9uJylcbiAgICAgICAgLm9wdGlvbignLWwsIC0tbGlzdCA8aWQ+JywgJ1Rhc2sgbGlzdCBJRCAoZGVmYXVsdHMgdG8gXCJ0YXNrbGlzdFwiKScpXG4gICAgICAgIC5hY3Rpb24oXG4gICAgICAgICAgYXN5bmMgKFxuICAgICAgICAgICAgc3ViamVjdDogc3RyaW5nLFxuICAgICAgICAgICAgb3B0czogeyBkZXNjcmlwdGlvbj86IHN0cmluZzsgbGlzdD86IHN0cmluZyB9LFxuICAgICAgICAgICkgPT4ge1xuICAgICAgICAgICAgY29uc3QgeyB0YXNrQ3JlYXRlSGFuZGxlciB9ID0gYXdhaXQgaW1wb3J0KCcuL2NsaS9oYW5kbGVycy9hbnQuanMnKVxuICAgICAgICAgICAgYXdhaXQgdGFza0NyZWF0ZUhhbmRsZXIoc3ViamVjdCwgb3B0cylcbiAgICAgICAgICB9LFxuICAgICAgICApXG5cbiAgICAgIHRhc2tDbWRcbiAgICAgICAgLmNvbW1hbmQoJ2xpc3QnKVxuICAgICAgICAuZGVzY3JpcHRpb24oJ0xpc3QgYWxsIHRhc2tzJylcbiAgICAgICAgLm9wdGlvbignLWwsIC0tbGlzdCA8aWQ+JywgJ1Rhc2sgbGlzdCBJRCAoZGVmYXVsdHMgdG8gXCJ0YXNrbGlzdFwiKScpXG4gICAgICAgIC5vcHRpb24oJy0tcGVuZGluZycsICdTaG93IG9ubHkgcGVuZGluZyB0YXNrcycpXG4gICAgICAgIC5vcHRpb24oJy0tanNvbicsICdPdXRwdXQgYXMgSlNPTicpXG4gICAgICAgIC5hY3Rpb24oXG4gICAgICAgICAgYXN5bmMgKG9wdHM6IHtcbiAgICAgICAgICAgIGxpc3Q/OiBzdHJpbmdcbiAgICAgICAgICAgIHBlbmRpbmc/OiBib29sZWFuXG4gICAgICAgICAgICBqc29uPzogYm9vbGVhblxuICAgICAgICAgIH0pID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHsgdGFza0xpc3RIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoJy4vY2xpL2hhbmRsZXJzL2FudC5qcycpXG4gICAgICAgICAgICBhd2FpdCB0YXNrTGlzdEhhbmRsZXIob3B0cylcbiAgICAgICAgICB9LFxuICAgICAgICApXG5cbiAgICAgIHRhc2tDbWRcbiAgICAgICAgLmNvbW1hbmQoJ2dldCA8aWQ+JylcbiAgICAgICAgLmRlc2NyaXB0aW9uKCdHZXQgZGV0YWlscyBvZiBhIHRhc2snKVxuICAgICAgICAub3B0aW9uKCctbCwgLS1saXN0IDxpZD4nLCAnVGFzayBsaXN0IElEIChkZWZhdWx0cyB0byBcInRhc2tsaXN0XCIpJylcbiAgICAgICAgLmFjdGlvbihhc3luYyAoaWQ6IHN0cmluZywgb3B0czogeyBsaXN0Pzogc3RyaW5nIH0pID0+IHtcbiAgICAgICAgICBjb25zdCB7IHRhc2tHZXRIYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoJy4vY2xpL2hhbmRsZXJzL2FudC5qcycpXG4gICAgICAgICAgYXdhaXQgdGFza0dldEhhbmRsZXIoaWQsIG9wdHMpXG4gICAgICAgIH0pXG5cbiAgICAgIHRhc2tDbWRcbiAgICAgICAgLmNvbW1hbmQoJ3VwZGF0ZSA8aWQ+JylcbiAgICAgICAgLmRlc2NyaXB0aW9uKCdVcGRhdGUgYSB0YXNrJylcbiAgICAgICAgLm9wdGlvbignLWwsIC0tbGlzdCA8aWQ+JywgJ1Rhc2sgbGlzdCBJRCAoZGVmYXVsdHMgdG8gXCJ0YXNrbGlzdFwiKScpXG4gICAgICAgIC5vcHRpb24oXG4gICAgICAgICAgJy1zLCAtLXN0YXR1cyA8c3RhdHVzPicsXG4gICAgICAgICAgYFNldCBzdGF0dXMgKCR7VEFTS19TVEFUVVNFUy5qb2luKCcsICcpfSlgLFxuICAgICAgICApXG4gICAgICAgIC5vcHRpb24oJy0tc3ViamVjdCA8dGV4dD4nLCAnVXBkYXRlIHN1YmplY3QnKVxuICAgICAgICAub3B0aW9uKCctZCwgLS1kZXNjcmlwdGlvbiA8dGV4dD4nLCAnVXBkYXRlIGRlc2NyaXB0aW9uJylcbiAgICAgICAgLm9wdGlvbignLS1vd25lciA8YWdlbnRJZD4nLCAnU2V0IG93bmVyJylcbiAgICAgICAgLm9wdGlvbignLS1jbGVhci1vd25lcicsICdDbGVhciBvd25lcicpXG4gICAgICAgIC5hY3Rpb24oXG4gICAgICAgICAgYXN5bmMgKFxuICAgICAgICAgICAgaWQ6IHN0cmluZyxcbiAgICAgICAgICAgIG9wdHM6IHtcbiAgICAgICAgICAgICAgbGlzdD86IHN0cmluZ1xuICAgICAgICAgICAgICBzdGF0dXM/OiBzdHJpbmdcbiAgICAgICAgICAgICAgc3ViamVjdD86IHN0cmluZ1xuICAgICAgICAgICAgICBkZXNjcmlwdGlvbj86IHN0cmluZ1xuICAgICAgICAgICAgICBvd25lcj86IHN0cmluZ1xuICAgICAgICAgICAgICBjbGVhck93bmVyPzogYm9vbGVhblxuICAgICAgICAgICAgfSxcbiAgICAgICAgICApID0+IHtcbiAgICAgICAgICAgIGNvbnN0IHsgdGFza1VwZGF0ZUhhbmRsZXIgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvYW50LmpzJylcbiAgICAgICAgICAgIGF3YWl0IHRhc2tVcGRhdGVIYW5kbGVyKGlkLCBvcHRzKVxuICAgICAgICAgIH0sXG4gICAgICAgIClcblxuICAgICAgdGFza0NtZFxuICAgICAgICAuY29tbWFuZCgnZGlyJylcbiAgICAgICAgLmRlc2NyaXB0aW9uKCdTaG93IHRoZSB0YXNrcyBkaXJlY3RvcnkgcGF0aCcpXG4gICAgICAgIC5vcHRpb24oJy1sLCAtLWxpc3QgPGlkPicsICdUYXNrIGxpc3QgSUQgKGRlZmF1bHRzIHRvIFwidGFza2xpc3RcIiknKVxuICAgICAgICAuYWN0aW9uKGFzeW5jIChvcHRzOiB7IGxpc3Q/OiBzdHJpbmcgfSkgPT4ge1xuICAgICAgICAgIGNvbnN0IHsgdGFza0RpckhhbmRsZXIgfSA9IGF3YWl0IGltcG9ydCgnLi9jbGkvaGFuZGxlcnMvYW50LmpzJylcbiAgICAgICAgICBhd2FpdCB0YXNrRGlySGFuZGxlcihvcHRzKVxuICAgICAgICB9KVxuICAgIH1cblxuICAgIC8vIGNsYXVkZSBjb21wbGV0aW9uIDxzaGVsbD5cbiAgICBwcm9ncmFtXG4gICAgICAuY29tbWFuZCgnY29tcGxldGlvbiA8c2hlbGw+JywgeyBoaWRkZW46IHRydWUgfSlcbiAgICAgIC5kZXNjcmlwdGlvbignR2VuZXJhdGUgc2hlbGwgY29tcGxldGlvbiBzY3JpcHQgKGJhc2gsIHpzaCwgb3IgZmlzaCknKVxuICAgICAgLm9wdGlvbihcbiAgICAgICAgJy0tb3V0cHV0IDxmaWxlPicsXG4gICAgICAgICdXcml0ZSBjb21wbGV0aW9uIHNjcmlwdCBkaXJlY3RseSB0byBhIGZpbGUgaW5zdGVhZCBvZiBzdGRvdXQnLFxuICAgICAgKVxuICAgICAgLmFjdGlvbihhc3luYyAoc2hlbGw6IHN0cmluZywgb3B0czogeyBvdXRwdXQ/OiBzdHJpbmcgfSkgPT4ge1xuICAgICAgICBjb25zdCB7IGNvbXBsZXRpb25IYW5kbGVyIH0gPSBhd2FpdCBpbXBvcnQoJy4vY2xpL2hhbmRsZXJzL2FudC5qcycpXG4gICAgICAgIGF3YWl0IGNvbXBsZXRpb25IYW5kbGVyKHNoZWxsLCBvcHRzLCBwcm9ncmFtKVxuICAgICAgfSlcbiAgfVxuXG4gIHByb2ZpbGVDaGVja3BvaW50KCdydW5fYmVmb3JlX3BhcnNlJylcbiAgYXdhaXQgcHJvZ3JhbS5wYXJzZUFzeW5jKHByb2Nlc3MuYXJndilcbiAgcHJvZmlsZUNoZWNrcG9pbnQoJ3J1bl9hZnRlcl9wYXJzZScpXG5cbiAgLy8gUmVjb3JkIGZpbmFsIGNoZWNrcG9pbnQgZm9yIHRvdGFsX3RpbWUgY2FsY3VsYXRpb25cbiAgcHJvZmlsZUNoZWNrcG9pbnQoJ21haW5fYWZ0ZXJfcnVuJylcblxuICAvLyBMb2cgc3RhcnR1cCBwZXJmIHRvIFN0YXRzaWcgKHNhbXBsZWQpIGFuZCBvdXRwdXQgZGV0YWlsZWQgcmVwb3J0IGlmIGVuYWJsZWRcbiAgcHJvZmlsZVJlcG9ydCgpXG5cbiAgcmV0dXJuIHByb2dyYW1cbn1cblxuYXN5bmMgZnVuY3Rpb24gbG9nVGVuZ3VJbml0KHtcbiAgaGFzSW5pdGlhbFByb21wdCxcbiAgaGFzU3RkaW4sXG4gIHZlcmJvc2UsXG4gIGRlYnVnLFxuICBkZWJ1Z1RvU3RkZXJyLFxuICBwcmludCxcbiAgb3V0cHV0Rm9ybWF0LFxuICBpbnB1dEZvcm1hdCxcbiAgbnVtQWxsb3dlZFRvb2xzLFxuICBudW1EaXNhbGxvd2VkVG9vbHMsXG4gIG1jcENsaWVudENvdW50LFxuICB3b3JrdHJlZUVuYWJsZWQsXG4gIHNraXBXZWJGZXRjaFByZWZsaWdodCxcbiAgZ2l0aHViQWN0aW9uSW5wdXRzLFxuICBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uc1Bhc3NlZCxcbiAgcGVybWlzc2lvbk1vZGUsXG4gIG1vZGVJc0J5cGFzcyxcbiAgYWxsb3dEYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uc1Bhc3NlZCxcbiAgc3lzdGVtUHJvbXB0RmxhZyxcbiAgYXBwZW5kU3lzdGVtUHJvbXB0RmxhZyxcbiAgdGhpbmtpbmdDb25maWcsXG4gIGFzc2lzdGFudEFjdGl2YXRpb25QYXRoLFxufToge1xuICBoYXNJbml0aWFsUHJvbXB0OiBib29sZWFuXG4gIGhhc1N0ZGluOiBib29sZWFuXG4gIHZlcmJvc2U6IGJvb2xlYW5cbiAgZGVidWc6IGJvb2xlYW5cbiAgZGVidWdUb1N0ZGVycjogYm9vbGVhblxuICBwcmludDogYm9vbGVhblxuICBvdXRwdXRGb3JtYXQ6IHN0cmluZ1xuICBpbnB1dEZvcm1hdDogc3RyaW5nXG4gIG51bUFsbG93ZWRUb29sczogbnVtYmVyXG4gIG51bURpc2FsbG93ZWRUb29sczogbnVtYmVyXG4gIG1jcENsaWVudENvdW50OiBudW1iZXJcbiAgd29ya3RyZWVFbmFibGVkOiBib29sZWFuXG4gIHNraXBXZWJGZXRjaFByZWZsaWdodDogYm9vbGVhbiB8IHVuZGVmaW5lZFxuICBnaXRodWJBY3Rpb25JbnB1dHM6IHN0cmluZyB8IHVuZGVmaW5lZFxuICBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uc1Bhc3NlZDogYm9vbGVhblxuICBwZXJtaXNzaW9uTW9kZTogc3RyaW5nXG4gIG1vZGVJc0J5cGFzczogYm9vbGVhblxuICBhbGxvd0Rhbmdlcm91c2x5U2tpcFBlcm1pc3Npb25zUGFzc2VkOiBib29sZWFuXG4gIHN5c3RlbVByb21wdEZsYWc6ICdmaWxlJyB8ICdmbGFnJyB8IHVuZGVmaW5lZFxuICBhcHBlbmRTeXN0ZW1Qcm9tcHRGbGFnOiAnZmlsZScgfCAnZmxhZycgfCB1bmRlZmluZWRcbiAgdGhpbmtpbmdDb25maWc6IFRoaW5raW5nQ29uZmlnXG4gIGFzc2lzdGFudEFjdGl2YXRpb25QYXRoOiBzdHJpbmcgfCB1bmRlZmluZWRcbn0pOiBQcm9taXNlPHZvaWQ+IHtcbiAgdHJ5IHtcbiAgICBsb2dFdmVudCgndGVuZ3VfaW5pdCcsIHtcbiAgICAgIGVudHJ5cG9pbnQ6XG4gICAgICAgICdjbGF1ZGUnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICBoYXNJbml0aWFsUHJvbXB0LFxuICAgICAgaGFzU3RkaW4sXG4gICAgICB2ZXJib3NlLFxuICAgICAgZGVidWcsXG4gICAgICBkZWJ1Z1RvU3RkZXJyLFxuICAgICAgcHJpbnQsXG4gICAgICBvdXRwdXRGb3JtYXQ6XG4gICAgICAgIG91dHB1dEZvcm1hdCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgaW5wdXRGb3JtYXQ6XG4gICAgICAgIGlucHV0Rm9ybWF0IGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICBudW1BbGxvd2VkVG9vbHMsXG4gICAgICBudW1EaXNhbGxvd2VkVG9vbHMsXG4gICAgICBtY3BDbGllbnRDb3VudCxcbiAgICAgIHdvcmt0cmVlOiB3b3JrdHJlZUVuYWJsZWQsXG4gICAgICBza2lwV2ViRmV0Y2hQcmVmbGlnaHQsXG4gICAgICAuLi4oZ2l0aHViQWN0aW9uSW5wdXRzICYmIHtcbiAgICAgICAgZ2l0aHViQWN0aW9uSW5wdXRzOlxuICAgICAgICAgIGdpdGh1YkFjdGlvbklucHV0cyBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSksXG4gICAgICBkYW5nZXJvdXNseVNraXBQZXJtaXNzaW9uc1Bhc3NlZCxcbiAgICAgIHBlcm1pc3Npb25Nb2RlOlxuICAgICAgICBwZXJtaXNzaW9uTW9kZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgbW9kZUlzQnlwYXNzLFxuICAgICAgaW5Qcm90ZWN0ZWROYW1lc3BhY2U6IGlzSW5Qcm90ZWN0ZWROYW1lc3BhY2UoKSxcbiAgICAgIGFsbG93RGFuZ2Vyb3VzbHlTa2lwUGVybWlzc2lvbnNQYXNzZWQsXG4gICAgICB0aGlua2luZ1R5cGU6XG4gICAgICAgIHRoaW5raW5nQ29uZmlnLnR5cGUgYXMgQW5hbHl0aWNzTWV0YWRhdGFfSV9WRVJJRklFRF9USElTX0lTX05PVF9DT0RFX09SX0ZJTEVQQVRIUyxcbiAgICAgIC4uLihzeXN0ZW1Qcm9tcHRGbGFnICYmIHtcbiAgICAgICAgc3lzdGVtUHJvbXB0RmxhZzpcbiAgICAgICAgICBzeXN0ZW1Qcm9tcHRGbGFnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KSxcbiAgICAgIC4uLihhcHBlbmRTeXN0ZW1Qcm9tcHRGbGFnICYmIHtcbiAgICAgICAgYXBwZW5kU3lzdGVtUHJvbXB0RmxhZzpcbiAgICAgICAgICBhcHBlbmRTeXN0ZW1Qcm9tcHRGbGFnIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICB9KSxcbiAgICAgIGlzX3NpbXBsZTogaXNCYXJlTW9kZSgpIHx8IHVuZGVmaW5lZCxcbiAgICAgIGlzX2Nvb3JkaW5hdG9yOlxuICAgICAgICBmZWF0dXJlKCdDT09SRElOQVRPUl9NT0RFJykgJiZcbiAgICAgICAgY29vcmRpbmF0b3JNb2RlTW9kdWxlPy5pc0Nvb3JkaW5hdG9yTW9kZSgpXG4gICAgICAgICAgPyB0cnVlXG4gICAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgICAuLi4oYXNzaXN0YW50QWN0aXZhdGlvblBhdGggJiYge1xuICAgICAgICBhc3Npc3RhbnRBY3RpdmF0aW9uUGF0aDpcbiAgICAgICAgICBhc3Npc3RhbnRBY3RpdmF0aW9uUGF0aCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSksXG4gICAgICBhdXRvVXBkYXRlc0NoYW5uZWw6IChnZXRJbml0aWFsU2V0dGluZ3MoKS5hdXRvVXBkYXRlc0NoYW5uZWwgPz9cbiAgICAgICAgJ2xhdGVzdCcpIGFzIEFuYWx5dGljc01ldGFkYXRhX0lfVkVSSUZJRURfVEhJU19JU19OT1RfQ09ERV9PUl9GSUxFUEFUSFMsXG4gICAgICAuLi4oXCJleHRlcm5hbFwiID09PSAnYW50J1xuICAgICAgICA/ICgoKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBjd2QgPSBnZXRDd2QoKVxuICAgICAgICAgICAgY29uc3QgZ2l0Um9vdCA9IGZpbmRHaXRSb290KGN3ZClcbiAgICAgICAgICAgIGNvbnN0IHJwID0gZ2l0Um9vdCA/IHJlbGF0aXZlKGdpdFJvb3QsIGN3ZCkgfHwgJy4nIDogdW5kZWZpbmVkXG4gICAgICAgICAgICByZXR1cm4gcnBcbiAgICAgICAgICAgICAgPyB7XG4gICAgICAgICAgICAgICAgICByZWxhdGl2ZVByb2plY3RQYXRoOlxuICAgICAgICAgICAgICAgICAgICBycCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgOiB7fVxuICAgICAgICAgIH0pKClcbiAgICAgICAgOiB7fSksXG4gICAgfSlcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBsb2dFcnJvcihlcnJvcilcbiAgfVxufVxuXG5mdW5jdGlvbiBtYXliZUFjdGl2YXRlUHJvYWN0aXZlKG9wdGlvbnM6IHVua25vd24pOiB2b2lkIHtcbiAgaWYgKFxuICAgIChmZWF0dXJlKCdQUk9BQ1RJVkUnKSB8fCBmZWF0dXJlKCdLQUlST1MnKSkgJiZcbiAgICAoKG9wdGlvbnMgYXMgeyBwcm9hY3RpdmU/OiBib29sZWFuIH0pLnByb2FjdGl2ZSB8fFxuICAgICAgaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfUFJPQUNUSVZFKSlcbiAgKSB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICBjb25zdCBwcm9hY3RpdmVNb2R1bGUgPSByZXF1aXJlKCcuL3Byb2FjdGl2ZS9pbmRleC5qcycpXG4gICAgaWYgKCFwcm9hY3RpdmVNb2R1bGUuaXNQcm9hY3RpdmVBY3RpdmUoKSkge1xuICAgICAgcHJvYWN0aXZlTW9kdWxlLmFjdGl2YXRlUHJvYWN0aXZlKCdjb21tYW5kJylcbiAgICB9XG4gIH1cbn1cblxuZnVuY3Rpb24gbWF5YmVBY3RpdmF0ZUJyaWVmKG9wdGlvbnM6IHVua25vd24pOiB2b2lkIHtcbiAgaWYgKCEoZmVhdHVyZSgnS0FJUk9TJykgfHwgZmVhdHVyZSgnS0FJUk9TX0JSSUVGJykpKSByZXR1cm5cbiAgY29uc3QgYnJpZWZGbGFnID0gKG9wdGlvbnMgYXMgeyBicmllZj86IGJvb2xlYW4gfSkuYnJpZWZcbiAgY29uc3QgYnJpZWZFbnYgPSBpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9CUklFRilcbiAgaWYgKCFicmllZkZsYWcgJiYgIWJyaWVmRW52KSByZXR1cm5cbiAgLy8gLS1icmllZiAvIENMQVVERV9DT0RFX0JSSUVGIGFyZSBleHBsaWNpdCBvcHQtaW5zOiBjaGVjayBlbnRpdGxlbWVudCxcbiAgLy8gdGhlbiBzZXQgdXNlck1zZ09wdEluIHRvIGFjdGl2YXRlIHRoZSB0b29sICsgcHJvbXB0IHNlY3Rpb24uIFRoZSBlbnZcbiAgLy8gdmFyIGFsc28gZ3JhbnRzIGVudGl0bGVtZW50IChpc0JyaWVmRW50aXRsZWQoKSByZWFkcyBpdCksIHNvIHNldHRpbmdcbiAgLy8gQ0xBVURFX0NPREVfQlJJRUY9MSBhbG9uZSBmb3JjZS1lbmFibGVzIGZvciBkZXYvdGVzdGluZyDigJQgbm8gR0IgZ2F0ZVxuICAvLyBuZWVkZWQuIGluaXRpYWxJc0JyaWVmT25seSByZWFkcyBnZXRVc2VyTXNnT3B0SW4oKSBkaXJlY3RseS5cbiAgLy8gQ29uZGl0aW9uYWwgcmVxdWlyZTogc3RhdGljIGltcG9ydCB3b3VsZCBsZWFrIHRoZSB0b29sIG5hbWUgc3RyaW5nXG4gIC8vIGludG8gZXh0ZXJuYWwgYnVpbGRzIHZpYSBCcmllZlRvb2wudHMg4oaSIHByb21wdC50cy5cbiAgLyogZXNsaW50LWRpc2FibGUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0cyAqL1xuICBjb25zdCB7IGlzQnJpZWZFbnRpdGxlZCB9ID1cbiAgICByZXF1aXJlKCcuL3Rvb2xzL0JyaWVmVG9vbC9CcmllZlRvb2wuanMnKSBhcyB0eXBlb2YgaW1wb3J0KCcuL3Rvb2xzL0JyaWVmVG9vbC9CcmllZlRvb2wuanMnKVxuICAvKiBlc2xpbnQtZW5hYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHMgKi9cbiAgY29uc3QgZW50aXRsZWQgPSBpc0JyaWVmRW50aXRsZWQoKVxuICBpZiAoZW50aXRsZWQpIHtcbiAgICBzZXRVc2VyTXNnT3B0SW4odHJ1ZSlcbiAgfVxuICAvLyBGaXJlIHVuY29uZGl0aW9uYWxseSBvbmNlIGludGVudCBpcyBzZWVuOiBlbmFibGVkPWZhbHNlIGNhcHR1cmVzIHRoZVxuICAvLyBcInVzZXIgdHJpZWQgYnV0IHdhcyBnYXRlZFwiIGZhaWx1cmUgbW9kZSBpbiBEYXRhZG9nLlxuICBsb2dFdmVudCgndGVuZ3VfYnJpZWZfbW9kZV9lbmFibGVkJywge1xuICAgIGVuYWJsZWQ6IGVudGl0bGVkLFxuICAgIGdhdGVkOiAhZW50aXRsZWQsXG4gICAgc291cmNlOiAoYnJpZWZFbnZcbiAgICAgID8gJ2VudidcbiAgICAgIDogJ2ZsYWcnKSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICB9KVxufVxuXG5mdW5jdGlvbiByZXNldEN1cnNvcigpIHtcbiAgY29uc3QgdGVybWluYWwgPSBwcm9jZXNzLnN0ZGVyci5pc1RUWVxuICAgID8gcHJvY2Vzcy5zdGRlcnJcbiAgICA6IHByb2Nlc3Muc3Rkb3V0LmlzVFRZXG4gICAgICA/IHByb2Nlc3Muc3Rkb3V0XG4gICAgICA6IHVuZGVmaW5lZFxuICB0ZXJtaW5hbD8ud3JpdGUoU0hPV19DVVJTT1IpXG59XG5cbnR5cGUgVGVhbW1hdGVPcHRpb25zID0ge1xuICBhZ2VudElkPzogc3RyaW5nXG4gIGFnZW50TmFtZT86IHN0cmluZ1xuICB0ZWFtTmFtZT86IHN0cmluZ1xuICBhZ2VudENvbG9yPzogc3RyaW5nXG4gIHBsYW5Nb2RlUmVxdWlyZWQ/OiBib29sZWFuXG4gIHBhcmVudFNlc3Npb25JZD86IHN0cmluZ1xuICB0ZWFtbWF0ZU1vZGU/OiAnYXV0bycgfCAndG11eCcgfCAnaW4tcHJvY2VzcydcbiAgYWdlbnRUeXBlPzogc3RyaW5nXG59XG5cbmZ1bmN0aW9uIGV4dHJhY3RUZWFtbWF0ZU9wdGlvbnMob3B0aW9uczogdW5rbm93bik6IFRlYW1tYXRlT3B0aW9ucyB7XG4gIGlmICh0eXBlb2Ygb3B0aW9ucyAhPT0gJ29iamVjdCcgfHwgb3B0aW9ucyA9PT0gbnVsbCkge1xuICAgIHJldHVybiB7fVxuICB9XG4gIGNvbnN0IG9wdHMgPSBvcHRpb25zIGFzIFJlY29yZDxzdHJpbmcsIHVua25vd24+XG4gIGNvbnN0IHRlYW1tYXRlTW9kZSA9IG9wdHMudGVhbW1hdGVNb2RlXG4gIHJldHVybiB7XG4gICAgYWdlbnRJZDogdHlwZW9mIG9wdHMuYWdlbnRJZCA9PT0gJ3N0cmluZycgPyBvcHRzLmFnZW50SWQgOiB1bmRlZmluZWQsXG4gICAgYWdlbnROYW1lOiB0eXBlb2Ygb3B0cy5hZ2VudE5hbWUgPT09ICdzdHJpbmcnID8gb3B0cy5hZ2VudE5hbWUgOiB1bmRlZmluZWQsXG4gICAgdGVhbU5hbWU6IHR5cGVvZiBvcHRzLnRlYW1OYW1lID09PSAnc3RyaW5nJyA/IG9wdHMudGVhbU5hbWUgOiB1bmRlZmluZWQsXG4gICAgYWdlbnRDb2xvcjpcbiAgICAgIHR5cGVvZiBvcHRzLmFnZW50Q29sb3IgPT09ICdzdHJpbmcnID8gb3B0cy5hZ2VudENvbG9yIDogdW5kZWZpbmVkLFxuICAgIHBsYW5Nb2RlUmVxdWlyZWQ6XG4gICAgICB0eXBlb2Ygb3B0cy5wbGFuTW9kZVJlcXVpcmVkID09PSAnYm9vbGVhbidcbiAgICAgICAgPyBvcHRzLnBsYW5Nb2RlUmVxdWlyZWRcbiAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgcGFyZW50U2Vzc2lvbklkOlxuICAgICAgdHlwZW9mIG9wdHMucGFyZW50U2Vzc2lvbklkID09PSAnc3RyaW5nJ1xuICAgICAgICA/IG9wdHMucGFyZW50U2Vzc2lvbklkXG4gICAgICAgIDogdW5kZWZpbmVkLFxuICAgIHRlYW1tYXRlTW9kZTpcbiAgICAgIHRlYW1tYXRlTW9kZSA9PT0gJ2F1dG8nIHx8XG4gICAgICB0ZWFtbWF0ZU1vZGUgPT09ICd0bXV4JyB8fFxuICAgICAgdGVhbW1hdGVNb2RlID09PSAnaW4tcHJvY2VzcydcbiAgICAgICAgPyB0ZWFtbWF0ZU1vZGVcbiAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgYWdlbnRUeXBlOiB0eXBlb2Ygb3B0cy5hZ2VudFR5cGUgPT09ICdzdHJpbmcnID8gb3B0cy5hZ2VudFR5cGUgOiB1bmRlZmluZWQsXG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFNBQVNBLGlCQUFpQixFQUFFQyxhQUFhLFFBQVEsNEJBQTRCOztBQUU3RTtBQUNBRCxpQkFBaUIsQ0FBQyxnQkFBZ0IsQ0FBQztBQUVuQyxTQUFTRSxlQUFlLFFBQVEsaUNBQWlDOztBQUVqRTtBQUNBQSxlQUFlLENBQUMsQ0FBQztBQUVqQixTQUNFQywrQkFBK0IsRUFDL0JDLHFCQUFxQixRQUNoQiwyQ0FBMkM7O0FBRWxEO0FBQ0FBLHFCQUFxQixDQUFDLENBQUM7QUFFdkIsU0FBU0MsT0FBTyxRQUFRLFlBQVk7QUFDcEMsU0FDRUMsT0FB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 \ No newline at end of file diff --git a/memdir/findRelevantMemories.ts b/memdir/findRelevantMemories.ts new file mode 100644 index 0000000..c239e0a --- /dev/null +++ b/memdir/findRelevantMemories.ts @@ -0,0 +1,141 @@ +import { feature } from 'bun:bundle' +import { logForDebugging } from '../utils/debug.js' +import { errorMessage } from '../utils/errors.js' +import { getDefaultSonnetModel } from '../utils/model/model.js' +import { sideQuery } from '../utils/sideQuery.js' +import { jsonParse } from '../utils/slowOperations.js' +import { + formatMemoryManifest, + type MemoryHeader, + scanMemoryFiles, +} from './memoryScan.js' + +export type RelevantMemory = { + path: string + mtimeMs: number +} + +const SELECT_MEMORIES_SYSTEM_PROMPT = `You are selecting memories that will be useful to Claude Code as it processes a user's query. You will be given the user's query and a list of available memory files with their filenames and descriptions. + +Return a list of filenames for the memories that will clearly be useful to Claude Code as it processes the user's query (up to 5). Only include memories that you are certain will be helpful based on their name and description. +- If you are unsure if a memory will be useful in processing the user's query, then do not include it in your list. Be selective and discerning. +- If there are no memories in the list that would clearly be useful, feel free to return an empty list. +- If a list of recently-used tools is provided, do not select memories that are usage reference or API documentation for those tools (Claude Code is already exercising them). DO still select memories containing warnings, gotchas, or known issues about those tools — active use is exactly when those matter. +` + +/** + * Find memory files relevant to a query by scanning memory file headers + * and asking Sonnet to select the most relevant ones. + * + * Returns absolute file paths + mtime of the most relevant memories + * (up to 5). Excludes MEMORY.md (already loaded in system prompt). + * mtime is threaded through so callers can surface freshness to the + * main model without a second stat. + * + * `alreadySurfaced` filters paths shown in prior turns before the + * Sonnet call, so the selector spends its 5-slot budget on fresh + * candidates instead of re-picking files the caller will discard. + */ +export async function findRelevantMemories( + query: string, + memoryDir: string, + signal: AbortSignal, + recentTools: readonly string[] = [], + alreadySurfaced: ReadonlySet = new Set(), +): Promise { + const memories = (await scanMemoryFiles(memoryDir, signal)).filter( + m => !alreadySurfaced.has(m.filePath), + ) + if (memories.length === 0) { + return [] + } + + const selectedFilenames = await selectRelevantMemories( + query, + memories, + signal, + recentTools, + ) + const byFilename = new Map(memories.map(m => [m.filename, m])) + const selected = selectedFilenames + .map(filename => byFilename.get(filename)) + .filter((m): m is MemoryHeader => m !== undefined) + + // Fires even on empty selection: selection-rate needs the denominator, + // and -1 ages distinguish "ran, picked nothing" from "never ran". + if (feature('MEMORY_SHAPE_TELEMETRY')) { + /* eslint-disable @typescript-eslint/no-require-imports */ + const { logMemoryRecallShape } = + require('./memoryShapeTelemetry.js') as typeof import('./memoryShapeTelemetry.js') + /* eslint-enable @typescript-eslint/no-require-imports */ + logMemoryRecallShape(memories, selected) + } + + return selected.map(m => ({ path: m.filePath, mtimeMs: m.mtimeMs })) +} + +async function selectRelevantMemories( + query: string, + memories: MemoryHeader[], + signal: AbortSignal, + recentTools: readonly string[], +): Promise { + const validFilenames = new Set(memories.map(m => m.filename)) + + const manifest = formatMemoryManifest(memories) + + // When Claude Code is actively using a tool (e.g. mcp__X__spawn), + // surfacing that tool's reference docs is noise — the conversation + // already contains working usage. The selector otherwise matches + // on keyword overlap ("spawn" in query + "spawn" in a memory + // description → false positive). + const toolsSection = + recentTools.length > 0 + ? `\n\nRecently used tools: ${recentTools.join(', ')}` + : '' + + try { + const result = await sideQuery({ + model: getDefaultSonnetModel(), + system: SELECT_MEMORIES_SYSTEM_PROMPT, + skipSystemPromptPrefix: true, + messages: [ + { + role: 'user', + content: `Query: ${query}\n\nAvailable memories:\n${manifest}${toolsSection}`, + }, + ], + max_tokens: 256, + output_format: { + type: 'json_schema', + schema: { + type: 'object', + properties: { + selected_memories: { type: 'array', items: { type: 'string' } }, + }, + required: ['selected_memories'], + additionalProperties: false, + }, + }, + signal, + querySource: 'memdir_relevance', + }) + + const textBlock = result.content.find(block => block.type === 'text') + if (!textBlock || textBlock.type !== 'text') { + return [] + } + + const parsed: { selected_memories: string[] } = jsonParse(textBlock.text) + return parsed.selected_memories.filter(f => validFilenames.has(f)) + } catch (e) { + if (signal.aborted) { + return [] + } + logForDebugging( + `[memdir] selectRelevantMemories failed: ${errorMessage(e)}`, + { level: 'warn' }, + ) + return [] + } +} diff --git a/memdir/memdir.ts b/memdir/memdir.ts new file mode 100644 index 0000000..1e7e68b --- /dev/null +++ b/memdir/memdir.ts @@ -0,0 +1,507 @@ +import { feature } from 'bun:bundle' +import { join } from 'path' +import { getFsImplementation } from '../utils/fsOperations.js' +import { getAutoMemPath, isAutoMemoryEnabled } from './paths.js' + +/* eslint-disable @typescript-eslint/no-require-imports */ +const teamMemPaths = feature('TEAMMEM') + ? (require('./teamMemPaths.js') as typeof import('./teamMemPaths.js')) + : null + +import { getKairosActive, getOriginalCwd } from '../bootstrap/state.js' +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' +/* eslint-enable @typescript-eslint/no-require-imports */ +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { GREP_TOOL_NAME } from '../tools/GrepTool/prompt.js' +import { isReplModeEnabled } from '../tools/REPLTool/constants.js' +import { logForDebugging } from '../utils/debug.js' +import { hasEmbeddedSearchTools } from '../utils/embeddedTools.js' +import { isEnvTruthy } from '../utils/envUtils.js' +import { formatFileSize } from '../utils/format.js' +import { getProjectDir } from '../utils/sessionStorage.js' +import { getInitialSettings } from '../utils/settings/settings.js' +import { + MEMORY_FRONTMATTER_EXAMPLE, + TRUSTING_RECALL_SECTION, + TYPES_SECTION_INDIVIDUAL, + WHAT_NOT_TO_SAVE_SECTION, + WHEN_TO_ACCESS_SECTION, +} from './memoryTypes.js' + +export const ENTRYPOINT_NAME = 'MEMORY.md' +export const MAX_ENTRYPOINT_LINES = 200 +// ~125 chars/line at 200 lines. At p97 today; catches long-line indexes that +// slip past the line cap (p100 observed: 197KB under 200 lines). +export const MAX_ENTRYPOINT_BYTES = 25_000 +const AUTO_MEM_DISPLAY_NAME = 'auto memory' + +export type EntrypointTruncation = { + content: string + lineCount: number + byteCount: number + wasLineTruncated: boolean + wasByteTruncated: boolean +} + +/** + * Truncate MEMORY.md content to the line AND byte caps, appending a warning + * that names which cap fired. Line-truncates first (natural boundary), then + * byte-truncates at the last newline before the cap so we don't cut mid-line. + * + * Shared by buildMemoryPrompt and claudemd getMemoryFiles (previously + * duplicated the line-only logic). + */ +export function truncateEntrypointContent(raw: string): EntrypointTruncation { + const trimmed = raw.trim() + const contentLines = trimmed.split('\n') + const lineCount = contentLines.length + const byteCount = trimmed.length + + const wasLineTruncated = lineCount > MAX_ENTRYPOINT_LINES + // Check original byte count — long lines are the failure mode the byte cap + // targets, so post-line-truncation size would understate the warning. + const wasByteTruncated = byteCount > MAX_ENTRYPOINT_BYTES + + if (!wasLineTruncated && !wasByteTruncated) { + return { + content: trimmed, + lineCount, + byteCount, + wasLineTruncated, + wasByteTruncated, + } + } + + let truncated = wasLineTruncated + ? contentLines.slice(0, MAX_ENTRYPOINT_LINES).join('\n') + : trimmed + + if (truncated.length > MAX_ENTRYPOINT_BYTES) { + const cutAt = truncated.lastIndexOf('\n', MAX_ENTRYPOINT_BYTES) + truncated = truncated.slice(0, cutAt > 0 ? cutAt : MAX_ENTRYPOINT_BYTES) + } + + const reason = + wasByteTruncated && !wasLineTruncated + ? `${formatFileSize(byteCount)} (limit: ${formatFileSize(MAX_ENTRYPOINT_BYTES)}) — index entries are too long` + : wasLineTruncated && !wasByteTruncated + ? `${lineCount} lines (limit: ${MAX_ENTRYPOINT_LINES})` + : `${lineCount} lines and ${formatFileSize(byteCount)}` + + return { + content: + truncated + + `\n\n> WARNING: ${ENTRYPOINT_NAME} is ${reason}. Only part of it was loaded. Keep index entries to one line under ~200 chars; move detail into topic files.`, + lineCount, + byteCount, + wasLineTruncated, + wasByteTruncated, + } +} + +/* eslint-disable @typescript-eslint/no-require-imports */ +const teamMemPrompts = feature('TEAMMEM') + ? (require('./teamMemPrompts.js') as typeof import('./teamMemPrompts.js')) + : null +/* eslint-enable @typescript-eslint/no-require-imports */ + +/** + * Shared guidance text appended to each memory directory prompt line. + * Shipped because Claude was burning turns on `ls`/`mkdir -p` before writing. + * Harness guarantees the directory exists via ensureMemoryDirExists(). + */ +export const DIR_EXISTS_GUIDANCE = + 'This directory already exists — write to it directly with the Write tool (do not run mkdir or check for its existence).' +export const DIRS_EXIST_GUIDANCE = + 'Both directories already exist — write to them directly with the Write tool (do not run mkdir or check for their existence).' + +/** + * Ensure a memory directory exists. Idempotent — called from loadMemoryPrompt + * (once per session via systemPromptSection cache) so the model can always + * write without checking existence first. FsOperations.mkdir is recursive + * by default and already swallows EEXIST, so the full parent chain + * (~/.claude/projects//memory/) is created in one call with no + * try/catch needed for the happy path. + */ +export async function ensureMemoryDirExists(memoryDir: string): Promise { + const fs = getFsImplementation() + try { + await fs.mkdir(memoryDir) + } catch (e) { + // fs.mkdir already handles EEXIST internally. Anything reaching here is + // a real problem (EACCES/EPERM/EROFS) — log so --debug shows why. Prompt + // building continues either way; the model's Write will surface the + // real perm error (and FileWriteTool does its own mkdir of the parent). + const code = + e instanceof Error && 'code' in e && typeof e.code === 'string' + ? e.code + : undefined + logForDebugging( + `ensureMemoryDirExists failed for ${memoryDir}: ${code ?? String(e)}`, + { level: 'debug' }, + ) + } +} + +/** + * Log memory directory file/subdir counts asynchronously. + * Fire-and-forget — doesn't block prompt building. + */ +function logMemoryDirCounts( + memoryDir: string, + baseMetadata: Record< + string, + | number + | boolean + | AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + >, +): void { + const fs = getFsImplementation() + void fs.readdir(memoryDir).then( + dirents => { + let fileCount = 0 + let subdirCount = 0 + for (const d of dirents) { + if (d.isFile()) { + fileCount++ + } else if (d.isDirectory()) { + subdirCount++ + } + } + logEvent('tengu_memdir_loaded', { + ...baseMetadata, + total_file_count: fileCount, + total_subdir_count: subdirCount, + }) + }, + () => { + // Directory unreadable — log without counts + logEvent('tengu_memdir_loaded', baseMetadata) + }, + ) +} + +/** + * Build the typed-memory behavioral instructions (without MEMORY.md content). + * Constrains memories to a closed four-type taxonomy (user / feedback / project / + * reference) — content that is derivable from the current project state (code + * patterns, architecture, git history) is explicitly excluded. + * + * Individual-only variant: no `## Memory scope` section, no tags + * in type blocks, and team/private qualifiers stripped from examples. + * + * Used by both buildMemoryPrompt (agent memory, includes content) and + * loadMemoryPrompt (system prompt, content injected via user context instead). + */ +export function buildMemoryLines( + displayName: string, + memoryDir: string, + extraGuidelines?: string[], + skipIndex = false, +): string[] { + const howToSave = skipIndex + ? [ + '## How to save memories', + '', + 'Write each memory to its own file (e.g., `user_role.md`, `feedback_testing.md`) using this frontmatter format:', + '', + ...MEMORY_FRONTMATTER_EXAMPLE, + '', + '- Keep the name, description, and type fields in memory files up-to-date with the content', + '- Organize memory semantically by topic, not chronologically', + '- Update or remove memories that turn out to be wrong or outdated', + '- Do not write duplicate memories. First check if there is an existing memory you can update before writing a new one.', + ] + : [ + '## How to save memories', + '', + 'Saving a memory is a two-step process:', + '', + '**Step 1** — write the memory to its own file (e.g., `user_role.md`, `feedback_testing.md`) using this frontmatter format:', + '', + ...MEMORY_FRONTMATTER_EXAMPLE, + '', + `**Step 2** — add a pointer to that file in \`${ENTRYPOINT_NAME}\`. \`${ENTRYPOINT_NAME}\` is an index, not a memory — each entry should be one line, under ~150 characters: \`- [Title](file.md) — one-line hook\`. It has no frontmatter. Never write memory content directly into \`${ENTRYPOINT_NAME}\`.`, + '', + `- \`${ENTRYPOINT_NAME}\` is always loaded into your conversation context — lines after ${MAX_ENTRYPOINT_LINES} will be truncated, so keep the index concise`, + '- Keep the name, description, and type fields in memory files up-to-date with the content', + '- Organize memory semantically by topic, not chronologically', + '- Update or remove memories that turn out to be wrong or outdated', + '- Do not write duplicate memories. First check if there is an existing memory you can update before writing a new one.', + ] + + const lines: string[] = [ + `# ${displayName}`, + '', + `You have a persistent, file-based memory system at \`${memoryDir}\`. ${DIR_EXISTS_GUIDANCE}`, + '', + "You should build up this memory system over time so that future conversations can have a complete picture of who the user is, how they'd like to collaborate with you, what behaviors to avoid or repeat, and the context behind the work the user gives you.", + '', + 'If the user explicitly asks you to remember something, save it immediately as whichever type fits best. If they ask you to forget something, find and remove the relevant entry.', + '', + ...TYPES_SECTION_INDIVIDUAL, + ...WHAT_NOT_TO_SAVE_SECTION, + '', + ...howToSave, + '', + ...WHEN_TO_ACCESS_SECTION, + '', + ...TRUSTING_RECALL_SECTION, + '', + '## Memory and other forms of persistence', + 'Memory is one of several persistence mechanisms available to you as you assist the user in a given conversation. The distinction is often that memory can be recalled in future conversations and should not be used for persisting information that is only useful within the scope of the current conversation.', + '- When to use or update a plan instead of memory: If you are about to start a non-trivial implementation task and would like to reach alignment with the user on your approach you should use a Plan rather than saving this information to memory. Similarly, if you already have a plan within the conversation and you have changed your approach persist that change by updating the plan rather than saving a memory.', + '- When to use or update tasks instead of memory: When you need to break your work in current conversation into discrete steps or keep track of your progress use tasks instead of saving to memory. Tasks are great for persisting information about the work that needs to be done in the current conversation, but memory should be reserved for information that will be useful in future conversations.', + '', + ...(extraGuidelines ?? []), + '', + ] + + lines.push(...buildSearchingPastContextSection(memoryDir)) + + return lines +} + +/** + * Build the typed-memory prompt with MEMORY.md content included. + * Used by agent memory (which has no getClaudeMds() equivalent). + */ +export function buildMemoryPrompt(params: { + displayName: string + memoryDir: string + extraGuidelines?: string[] +}): string { + const { displayName, memoryDir, extraGuidelines } = params + const fs = getFsImplementation() + const entrypoint = memoryDir + ENTRYPOINT_NAME + + // Directory creation is the caller's responsibility (loadMemoryPrompt / + // loadAgentMemoryPrompt). Builders only read, they don't mkdir. + + // Read existing memory entrypoint (sync: prompt building is synchronous) + let entrypointContent = '' + try { + // eslint-disable-next-line custom-rules/no-sync-fs + entrypointContent = fs.readFileSync(entrypoint, { encoding: 'utf-8' }) + } catch { + // No memory file yet + } + + const lines = buildMemoryLines(displayName, memoryDir, extraGuidelines) + + if (entrypointContent.trim()) { + const t = truncateEntrypointContent(entrypointContent) + const memoryType = displayName === AUTO_MEM_DISPLAY_NAME ? 'auto' : 'agent' + logMemoryDirCounts(memoryDir, { + content_length: t.byteCount, + line_count: t.lineCount, + was_truncated: t.wasLineTruncated, + was_byte_truncated: t.wasByteTruncated, + memory_type: + memoryType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + lines.push(`## ${ENTRYPOINT_NAME}`, '', t.content) + } else { + lines.push( + `## ${ENTRYPOINT_NAME}`, + '', + `Your ${ENTRYPOINT_NAME} is currently empty. When you save new memories, they will appear here.`, + ) + } + + return lines.join('\n') +} + +/** + * Assistant-mode daily-log prompt. Gated behind feature('KAIROS'). + * + * Assistant sessions are effectively perpetual, so the agent writes memories + * append-only to a date-named log file rather than maintaining MEMORY.md as + * a live index. A separate nightly /dream skill distills logs into topic + * files + MEMORY.md. MEMORY.md is still loaded into context (via claudemd.ts) + * as the distilled index — this prompt only changes where NEW memories go. + */ +function buildAssistantDailyLogPrompt(skipIndex = false): string { + const memoryDir = getAutoMemPath() + // Describe the path as a pattern rather than inlining today's literal path: + // this prompt is cached by systemPromptSection('memory', ...) and NOT + // invalidated on date change. The model derives the current date from the + // date_change attachment (appended at the tail on midnight rollover) rather + // than the user-context message — the latter is intentionally left stale to + // preserve the prompt cache prefix across midnight. + const logPathPattern = join(memoryDir, 'logs', 'YYYY', 'MM', 'YYYY-MM-DD.md') + + const lines: string[] = [ + '# auto memory', + '', + `You have a persistent, file-based memory system found at: \`${memoryDir}\``, + '', + "This session is long-lived. As you work, record anything worth remembering by **appending** to today's daily log file:", + '', + `\`${logPathPattern}\``, + '', + "Substitute today's date (from `currentDate` in your context) for `YYYY-MM-DD`. When the date rolls over mid-session, start appending to the new day's file.", + '', + 'Write each entry as a short timestamped bullet. Create the file (and parent directories) on first write if it does not exist. Do not rewrite or reorganize the log — it is append-only. A separate nightly process distills these logs into `MEMORY.md` and topic files.', + '', + '## What to log', + '- User corrections and preferences ("use bun, not npm"; "stop summarizing diffs")', + '- Facts about the user, their role, or their goals', + '- Project context that is not derivable from the code (deadlines, incidents, decisions and their rationale)', + '- Pointers to external systems (dashboards, Linear projects, Slack channels)', + '- Anything the user explicitly asks you to remember', + '', + ...WHAT_NOT_TO_SAVE_SECTION, + '', + ...(skipIndex + ? [] + : [ + `## ${ENTRYPOINT_NAME}`, + `\`${ENTRYPOINT_NAME}\` is the distilled index (maintained nightly from your logs) and is loaded into your context automatically. Read it for orientation, but do not edit it directly — record new information in today's log instead.`, + '', + ]), + ...buildSearchingPastContextSection(memoryDir), + ] + + return lines.join('\n') +} + +/** + * Build the "Searching past context" section if the feature gate is enabled. + */ +export function buildSearchingPastContextSection(autoMemDir: string): string[] { + if (!getFeatureValue_CACHED_MAY_BE_STALE('tengu_coral_fern', false)) { + return [] + } + const projectDir = getProjectDir(getOriginalCwd()) + // Ant-native builds alias grep to embedded ugrep and remove the dedicated + // Grep tool, so give the model a real shell invocation there. + // In REPL mode, both Grep and Bash are hidden from direct use — the model + // calls them from inside REPL scripts, so the grep shell form is what it + // will write in the script anyway. + const embedded = hasEmbeddedSearchTools() || isReplModeEnabled() + const memSearch = embedded + ? `grep -rn "" ${autoMemDir} --include="*.md"` + : `${GREP_TOOL_NAME} with pattern="" path="${autoMemDir}" glob="*.md"` + const transcriptSearch = embedded + ? `grep -rn "" ${projectDir}/ --include="*.jsonl"` + : `${GREP_TOOL_NAME} with pattern="" path="${projectDir}/" glob="*.jsonl"` + return [ + '## Searching past context', + '', + 'When looking for past context:', + '1. Search topic files in your memory directory:', + '```', + memSearch, + '```', + '2. Session transcript logs (last resort — large files, slow):', + '```', + transcriptSearch, + '```', + 'Use narrow search terms (error messages, file paths, function names) rather than broad keywords.', + '', + ] +} + +/** + * Load the unified memory prompt for inclusion in the system prompt. + * Dispatches based on which memory systems are enabled: + * - auto + team: combined prompt (both directories) + * - auto only: memory lines (single directory) + * Team memory requires auto memory (enforced by isTeamMemoryEnabled), so + * there is no team-only branch. + * + * Returns null when auto memory is disabled. + */ +export async function loadMemoryPrompt(): Promise { + const autoEnabled = isAutoMemoryEnabled() + + const skipIndex = getFeatureValue_CACHED_MAY_BE_STALE( + 'tengu_moth_copse', + false, + ) + + // KAIROS daily-log mode takes precedence over TEAMMEM: the append-only + // log paradigm does not compose with team sync (which expects a shared + // MEMORY.md that both sides read + write). Gating on `autoEnabled` here + // means the !autoEnabled case falls through to the tengu_memdir_disabled + // telemetry block below, matching the non-KAIROS path. + if (feature('KAIROS') && autoEnabled && getKairosActive()) { + logMemoryDirCounts(getAutoMemPath(), { + memory_type: + 'auto' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + return buildAssistantDailyLogPrompt(skipIndex) + } + + // Cowork injects memory-policy text via env var; thread into all builders. + const coworkExtraGuidelines = + process.env.CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES + const extraGuidelines = + coworkExtraGuidelines && coworkExtraGuidelines.trim().length > 0 + ? [coworkExtraGuidelines] + : undefined + + if (feature('TEAMMEM')) { + if (teamMemPaths!.isTeamMemoryEnabled()) { + const autoDir = getAutoMemPath() + const teamDir = teamMemPaths!.getTeamMemPath() + // Harness guarantees these directories exist so the model can write + // without checking. The prompt text reflects this ("already exists"). + // Only creating teamDir is sufficient: getTeamMemPath() is defined as + // join(getAutoMemPath(), 'team'), so recursive mkdir of the team dir + // creates the auto dir as a side effect. If the team dir ever moves + // out from under the auto dir, add a second ensureMemoryDirExists call + // for autoDir here. + await ensureMemoryDirExists(teamDir) + logMemoryDirCounts(autoDir, { + memory_type: + 'auto' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + logMemoryDirCounts(teamDir, { + memory_type: + 'team' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + return teamMemPrompts!.buildCombinedMemoryPrompt( + extraGuidelines, + skipIndex, + ) + } + } + + if (autoEnabled) { + const autoDir = getAutoMemPath() + // Harness guarantees the directory exists so the model can write without + // checking. The prompt text reflects this ("already exists"). + await ensureMemoryDirExists(autoDir) + logMemoryDirCounts(autoDir, { + memory_type: + 'auto' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) + return buildMemoryLines( + 'auto memory', + autoDir, + extraGuidelines, + skipIndex, + ).join('\n') + } + + logEvent('tengu_memdir_disabled', { + disabled_by_env_var: isEnvTruthy( + process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY, + ), + disabled_by_setting: + !isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY) && + getInitialSettings().autoMemoryEnabled === false, + }) + // Gate on the GB flag directly, not isTeamMemoryEnabled() — that function + // checks isAutoMemoryEnabled() first, which is definitionally false in this + // branch. We want "was this user in the team-memory cohort at all." + if (getFeatureValue_CACHED_MAY_BE_STALE('tengu_herring_clock', false)) { + logEvent('tengu_team_memdir_disabled', {}) + } + return null +} diff --git a/memdir/memoryAge.ts b/memdir/memoryAge.ts new file mode 100644 index 0000000..bb87bbe --- /dev/null +++ b/memdir/memoryAge.ts @@ -0,0 +1,53 @@ +/** + * Days elapsed since mtime. Floor-rounded — 0 for today, 1 for + * yesterday, 2+ for older. Negative inputs (future mtime, clock skew) + * clamp to 0. + */ +export function memoryAgeDays(mtimeMs: number): number { + return Math.max(0, Math.floor((Date.now() - mtimeMs) / 86_400_000)) +} + +/** + * Human-readable age string. Models are poor at date arithmetic — + * a raw ISO timestamp doesn't trigger staleness reasoning the way + * "47 days ago" does. + */ +export function memoryAge(mtimeMs: number): string { + const d = memoryAgeDays(mtimeMs) + if (d === 0) return 'today' + if (d === 1) return 'yesterday' + return `${d} days ago` +} + +/** + * Plain-text staleness caveat for memories >1 day old. Returns '' + * for fresh (today/yesterday) memories — warning there is noise. + * + * Use this when the consumer already provides its own wrapping + * (e.g. messages.ts relevant_memories → wrapMessagesInSystemReminder). + * + * Motivated by user reports of stale code-state memories (file:line + * citations to code that has since changed) being asserted as fact — + * the citation makes the stale claim sound more authoritative, not less. + */ +export function memoryFreshnessText(mtimeMs: number): string { + const d = memoryAgeDays(mtimeMs) + if (d <= 1) return '' + return ( + `This memory is ${d} days old. ` + + `Memories are point-in-time observations, not live state — ` + + `claims about code behavior or file:line citations may be outdated. ` + + `Verify against current code before asserting as fact.` + ) +} + +/** + * Per-memory staleness note wrapped in tags. + * Returns '' for memories ≤ 1 day old. Use this for callers that + * don't add their own system-reminder wrapper (e.g. FileReadTool output). + */ +export function memoryFreshnessNote(mtimeMs: number): string { + const text = memoryFreshnessText(mtimeMs) + if (!text) return '' + return `${text}\n` +} diff --git a/memdir/memoryScan.ts b/memdir/memoryScan.ts new file mode 100644 index 0000000..2e1a1c7 --- /dev/null +++ b/memdir/memoryScan.ts @@ -0,0 +1,94 @@ +/** + * Memory-directory scanning primitives. Split out of findRelevantMemories.ts + * so extractMemories can import the scan without pulling in sideQuery and + * the API-client chain (which closed a cycle through memdir.ts — #25372). + */ + +import { readdir } from 'fs/promises' +import { basename, join } from 'path' +import { parseFrontmatter } from '../utils/frontmatterParser.js' +import { readFileInRange } from '../utils/readFileInRange.js' +import { type MemoryType, parseMemoryType } from './memoryTypes.js' + +export type MemoryHeader = { + filename: string + filePath: string + mtimeMs: number + description: string | null + type: MemoryType | undefined +} + +const MAX_MEMORY_FILES = 200 +const FRONTMATTER_MAX_LINES = 30 + +/** + * Scan a memory directory for .md files, read their frontmatter, and return + * a header list sorted newest-first (capped at MAX_MEMORY_FILES). Shared by + * findRelevantMemories (query-time recall) and extractMemories (pre-injects + * the listing so the extraction agent doesn't spend a turn on `ls`). + * + * Single-pass: readFileInRange stats internally and returns mtimeMs, so we + * read-then-sort rather than stat-sort-read. For the common case (N ≤ 200) + * this halves syscalls vs a separate stat round; for large N we read a few + * extra small files but still avoid the double-stat on the surviving 200. + */ +export async function scanMemoryFiles( + memoryDir: string, + signal: AbortSignal, +): Promise { + try { + const entries = await readdir(memoryDir, { recursive: true }) + const mdFiles = entries.filter( + f => f.endsWith('.md') && basename(f) !== 'MEMORY.md', + ) + + const headerResults = await Promise.allSettled( + mdFiles.map(async (relativePath): Promise => { + const filePath = join(memoryDir, relativePath) + const { content, mtimeMs } = await readFileInRange( + filePath, + 0, + FRONTMATTER_MAX_LINES, + undefined, + signal, + ) + const { frontmatter } = parseFrontmatter(content, filePath) + return { + filename: relativePath, + filePath, + mtimeMs, + description: frontmatter.description || null, + type: parseMemoryType(frontmatter.type), + } + }), + ) + + return headerResults + .filter( + (r): r is PromiseFulfilledResult => + r.status === 'fulfilled', + ) + .map(r => r.value) + .sort((a, b) => b.mtimeMs - a.mtimeMs) + .slice(0, MAX_MEMORY_FILES) + } catch { + return [] + } +} + +/** + * Format memory headers as a text manifest: one line per file with + * [type] filename (timestamp): description. Used by both the recall + * selector prompt and the extraction-agent prompt. + */ +export function formatMemoryManifest(memories: MemoryHeader[]): string { + return memories + .map(m => { + const tag = m.type ? `[${m.type}] ` : '' + const ts = new Date(m.mtimeMs).toISOString() + return m.description + ? `- ${tag}${m.filename} (${ts}): ${m.description}` + : `- ${tag}${m.filename} (${ts})` + }) + .join('\n') +} diff --git a/memdir/memoryTypes.ts b/memdir/memoryTypes.ts new file mode 100644 index 0000000..99b4483 --- /dev/null +++ b/memdir/memoryTypes.ts @@ -0,0 +1,271 @@ +/** + * Memory type taxonomy. + * + * Memories are constrained to four types capturing context NOT derivable + * from the current project state. Code patterns, architecture, git history, + * and file structure are derivable (via grep/git/CLAUDE.md) and should NOT + * be saved as memories. + * + * The two TYPES_SECTION_* exports below are intentionally duplicated rather + * than generated from a shared spec — keeping them flat makes per-mode edits + * trivial without reasoning through a helper's conditional rendering. + */ + +export const MEMORY_TYPES = [ + 'user', + 'feedback', + 'project', + 'reference', +] as const + +export type MemoryType = (typeof MEMORY_TYPES)[number] + +/** + * Parse a raw frontmatter value into a MemoryType. + * Invalid or missing values return undefined — legacy files without a + * `type:` field keep working, files with unknown types degrade gracefully. + */ +export function parseMemoryType(raw: unknown): MemoryType | undefined { + if (typeof raw !== 'string') return undefined + return MEMORY_TYPES.find(t => t === raw) +} + +/** + * `## Types of memory` section for COMBINED mode (private + team directories). + * Includes tags and team/private qualifiers in examples. + */ +export const TYPES_SECTION_COMBINED: readonly string[] = [ + '## Types of memory', + '', + 'There are several discrete types of memory that you can store in your memory system. Each type below declares a of `private`, `team`, or guidance for choosing between the two.', + '', + '', + '', + ' user', + ' always private', + " Contain information about the user's role, goals, responsibilities, and knowledge. Great user memories help you tailor your future behavior to the user's preferences and perspective. Your goal in reading and writing these memories is to build up an understanding of who the user is and how you can be most helpful to them specifically. For example, you should collaborate with a senior software engineer differently than a student who is coding for the very first time. Keep in mind, that the aim here is to be helpful to the user. Avoid writing memories about the user that could be viewed as a negative judgement or that are not relevant to the work you're trying to accomplish together.", + " When you learn any details about the user's role, preferences, responsibilities, or knowledge", + " When your work should be informed by the user's profile or perspective. For example, if the user is asking you to explain a part of the code, you should answer that question in a way that is tailored to the specific details that they will find most valuable or that helps them build their mental model in relation to domain knowledge they already have.", + ' ', + " user: I'm a data scientist investigating what logging we have in place", + ' assistant: [saves private user memory: user is a data scientist, currently focused on observability/logging]', + '', + " user: I've been writing Go for ten years but this is my first time touching the React side of this repo", + " assistant: [saves private user memory: deep Go expertise, new to React and this project's frontend — frame frontend explanations in terms of backend analogues]", + ' ', + '', + '', + ' feedback', + ' default to private. Save as team only when the guidance is clearly a project-wide convention that every contributor should follow (e.g., a testing policy, a build invariant), not a personal style preference.', + " Guidance the user has given you about how to approach work — both what to avoid and what to keep doing. These are a very important type of memory to read and write as they allow you to remain coherent and responsive to the way you should approach work in the project. Record from failure AND success: if you only save corrections, you will avoid past mistakes but drift away from approaches the user has already validated, and may grow overly cautious. Before saving a private feedback memory, check that it doesn't contradict a team feedback memory — if it does, either don't save it or note the override explicitly.", + ' Any time the user corrects your approach ("no not that", "don\'t", "stop doing X") OR confirms a non-obvious approach worked ("yes exactly", "perfect, keep doing that", accepting an unusual choice without pushback). Corrections are easy to notice; confirmations are quieter — watch for them. In both cases, save what is applicable to future conversations, especially if surprising or not obvious from the code. Include *why* so you can judge edge cases later.', + ' Let these memories guide your behavior so that the user and other users in the project do not need to offer the same guidance twice.', + ' Lead with the rule itself, then a **Why:** line (the reason the user gave — often a past incident or strong preference) and a **How to apply:** line (when/where this guidance kicks in). Knowing *why* lets you judge edge cases instead of blindly following the rule.', + ' ', + " user: don't mock the database in these tests — we got burned last quarter when mocked tests passed but the prod migration failed", + ' assistant: [saves team feedback memory: integration tests must hit a real database, not mocks. Reason: prior incident where mock/prod divergence masked a broken migration. Team scope: this is a project testing policy, not a personal preference]', + '', + ' user: stop summarizing what you just did at the end of every response, I can read the diff', + " assistant: [saves private feedback memory: this user wants terse responses with no trailing summaries. Private because it's a communication preference, not a project convention]", + '', + " user: yeah the single bundled PR was the right call here, splitting this one would've just been churn", + ' assistant: [saves private feedback memory: for refactors in this area, user prefers one bundled PR over many small ones. Confirmed after I chose this approach — a validated judgment call, not a correction]', + ' ', + '', + '', + ' project', + ' private or team, but strongly bias toward team', + ' Information that you learn about ongoing work, goals, initiatives, bugs, or incidents within the project that is not otherwise derivable from the code or git history. Project memories help you understand the broader context and motivation behind the work users are working on within this working directory.', + ' When you learn who is doing what, why, or by when. These states change relatively quickly so try to keep your understanding of this up to date. Always convert relative dates in user messages to absolute dates when saving (e.g., "Thursday" → "2026-03-05"), so the memory remains interpretable after time passes.', + " Use these memories to more fully understand the details and nuance behind the user's request, anticipate coordination issues across users, make better informed suggestions.", + ' Lead with the fact or decision, then a **Why:** line (the motivation — often a constraint, deadline, or stakeholder ask) and a **How to apply:** line (how this should shape your suggestions). Project memories decay fast, so the why helps future-you judge whether the memory is still load-bearing.', + ' ', + " user: we're freezing all non-critical merges after Thursday — mobile team is cutting a release branch", + ' assistant: [saves team project memory: merge freeze begins 2026-03-05 for mobile release cut. Flag any non-critical PR work scheduled after that date]', + '', + " user: the reason we're ripping out the old auth middleware is that legal flagged it for storing session tokens in a way that doesn't meet the new compliance requirements", + ' assistant: [saves team project memory: auth middleware rewrite is driven by legal/compliance requirements around session token storage, not tech-debt cleanup — scope decisions should favor compliance over ergonomics]', + ' ', + '', + '', + ' reference', + ' usually team', + ' Stores pointers to where information can be found in external systems. These memories allow you to remember where to look to find up-to-date information outside of the project directory.', + ' When you learn about resources in external systems and their purpose. For example, that bugs are tracked in a specific project in Linear or that feedback can be found in a specific Slack channel.', + ' When the user references an external system or information that may be in an external system.', + ' ', + ' user: check the Linear project "INGEST" if you want context on these tickets, that\'s where we track all pipeline bugs', + ' assistant: [saves team reference memory: pipeline bugs are tracked in Linear project "INGEST"]', + '', + " user: the Grafana board at grafana.internal/d/api-latency is what oncall watches — if you're touching request handling, that's the thing that'll page someone", + ' assistant: [saves team reference memory: grafana.internal/d/api-latency is the oncall latency dashboard — check it when editing request-path code]', + ' ', + '', + '', + '', +] + +/** + * `## Types of memory` section for INDIVIDUAL-ONLY mode (single directory). + * No tags. Examples use plain `[saves X memory: …]`. Prose that + * only makes sense with a private/team split is reworded. + */ +export const TYPES_SECTION_INDIVIDUAL: readonly string[] = [ + '## Types of memory', + '', + 'There are several discrete types of memory that you can store in your memory system:', + '', + '', + '', + ' user', + " Contain information about the user's role, goals, responsibilities, and knowledge. Great user memories help you tailor your future behavior to the user's preferences and perspective. Your goal in reading and writing these memories is to build up an understanding of who the user is and how you can be most helpful to them specifically. For example, you should collaborate with a senior software engineer differently than a student who is coding for the very first time. Keep in mind, that the aim here is to be helpful to the user. Avoid writing memories about the user that could be viewed as a negative judgement or that are not relevant to the work you're trying to accomplish together.", + " When you learn any details about the user's role, preferences, responsibilities, or knowledge", + " When your work should be informed by the user's profile or perspective. For example, if the user is asking you to explain a part of the code, you should answer that question in a way that is tailored to the specific details that they will find most valuable or that helps them build their mental model in relation to domain knowledge they already have.", + ' ', + " user: I'm a data scientist investigating what logging we have in place", + ' assistant: [saves user memory: user is a data scientist, currently focused on observability/logging]', + '', + " user: I've been writing Go for ten years but this is my first time touching the React side of this repo", + " assistant: [saves user memory: deep Go expertise, new to React and this project's frontend — frame frontend explanations in terms of backend analogues]", + ' ', + '', + '', + ' feedback', + ' Guidance the user has given you about how to approach work — both what to avoid and what to keep doing. These are a very important type of memory to read and write as they allow you to remain coherent and responsive to the way you should approach work in the project. Record from failure AND success: if you only save corrections, you will avoid past mistakes but drift away from approaches the user has already validated, and may grow overly cautious.', + ' Any time the user corrects your approach ("no not that", "don\'t", "stop doing X") OR confirms a non-obvious approach worked ("yes exactly", "perfect, keep doing that", accepting an unusual choice without pushback). Corrections are easy to notice; confirmations are quieter — watch for them. In both cases, save what is applicable to future conversations, especially if surprising or not obvious from the code. Include *why* so you can judge edge cases later.', + ' Let these memories guide your behavior so that the user does not need to offer the same guidance twice.', + ' Lead with the rule itself, then a **Why:** line (the reason the user gave — often a past incident or strong preference) and a **How to apply:** line (when/where this guidance kicks in). Knowing *why* lets you judge edge cases instead of blindly following the rule.', + ' ', + " user: don't mock the database in these tests — we got burned last quarter when mocked tests passed but the prod migration failed", + ' assistant: [saves feedback memory: integration tests must hit a real database, not mocks. Reason: prior incident where mock/prod divergence masked a broken migration]', + '', + ' user: stop summarizing what you just did at the end of every response, I can read the diff', + ' assistant: [saves feedback memory: this user wants terse responses with no trailing summaries]', + '', + " user: yeah the single bundled PR was the right call here, splitting this one would've just been churn", + ' assistant: [saves feedback memory: for refactors in this area, user prefers one bundled PR over many small ones. Confirmed after I chose this approach — a validated judgment call, not a correction]', + ' ', + '', + '', + ' project', + ' Information that you learn about ongoing work, goals, initiatives, bugs, or incidents within the project that is not otherwise derivable from the code or git history. Project memories help you understand the broader context and motivation behind the work the user is doing within this working directory.', + ' When you learn who is doing what, why, or by when. These states change relatively quickly so try to keep your understanding of this up to date. Always convert relative dates in user messages to absolute dates when saving (e.g., "Thursday" → "2026-03-05"), so the memory remains interpretable after time passes.', + " Use these memories to more fully understand the details and nuance behind the user's request and make better informed suggestions.", + ' Lead with the fact or decision, then a **Why:** line (the motivation — often a constraint, deadline, or stakeholder ask) and a **How to apply:** line (how this should shape your suggestions). Project memories decay fast, so the why helps future-you judge whether the memory is still load-bearing.', + ' ', + " user: we're freezing all non-critical merges after Thursday — mobile team is cutting a release branch", + ' assistant: [saves project memory: merge freeze begins 2026-03-05 for mobile release cut. Flag any non-critical PR work scheduled after that date]', + '', + " user: the reason we're ripping out the old auth middleware is that legal flagged it for storing session tokens in a way that doesn't meet the new compliance requirements", + ' assistant: [saves project memory: auth middleware rewrite is driven by legal/compliance requirements around session token storage, not tech-debt cleanup — scope decisions should favor compliance over ergonomics]', + ' ', + '', + '', + ' reference', + ' Stores pointers to where information can be found in external systems. These memories allow you to remember where to look to find up-to-date information outside of the project directory.', + ' When you learn about resources in external systems and their purpose. For example, that bugs are tracked in a specific project in Linear or that feedback can be found in a specific Slack channel.', + ' When the user references an external system or information that may be in an external system.', + ' ', + ' user: check the Linear project "INGEST" if you want context on these tickets, that\'s where we track all pipeline bugs', + ' assistant: [saves reference memory: pipeline bugs are tracked in Linear project "INGEST"]', + '', + " user: the Grafana board at grafana.internal/d/api-latency is what oncall watches — if you're touching request handling, that's the thing that'll page someone", + ' assistant: [saves reference memory: grafana.internal/d/api-latency is the oncall latency dashboard — check it when editing request-path code]', + ' ', + '', + '', + '', +] + +/** + * `## What NOT to save in memory` section. Identical across both modes. + */ +export const WHAT_NOT_TO_SAVE_SECTION: readonly string[] = [ + '## What NOT to save in memory', + '', + '- Code patterns, conventions, architecture, file paths, or project structure — these can be derived by reading the current project state.', + '- Git history, recent changes, or who-changed-what — `git log` / `git blame` are authoritative.', + '- Debugging solutions or fix recipes — the fix is in the code; the commit message has the context.', + '- Anything already documented in CLAUDE.md files.', + '- Ephemeral task details: in-progress work, temporary state, current conversation context.', + '', + // H2: explicit-save gate. Eval-validated (memory-prompt-iteration case 3, + // 0/2 → 3/3): prevents "save this week's PR list" → activity-log noise. + 'These exclusions apply even when the user explicitly asks you to save. If they ask you to save a PR list or activity summary, ask what was *surprising* or *non-obvious* about it — that is the part worth keeping.', +] + +/** + * Recall-side drift caveat. Single bullet under `## When to access memories`. + * Proactive: verify memory against current state before answering. + */ +export const MEMORY_DRIFT_CAVEAT = + '- Memory records can become stale over time. Use memory as context for what was true at a given point in time. Before answering the user or building assumptions based solely on information in memory records, verify that the memory is still correct and up-to-date by reading the current state of the files or resources. If a recalled memory conflicts with current information, trust what you observe now — and update or remove the stale memory rather than acting on it.' + +/** + * `## When to access memories` section. Includes MEMORY_DRIFT_CAVEAT. + * + * H6 (branch-pollution evals #22856, case 5 1/3 on capy): the "ignore" bullet + * is the delta. Failure mode: user says "ignore memory about X" → Claude reads + * code correctly but adds "not Y as noted in memory" — treats "ignore" as + * "acknowledge then override" rather than "don't reference at all." The bullet + * names that anti-pattern explicitly. + * + * Token budget (H6a): merged old bullets 1+2, tightened both. Old 4 lines + * were ~70 tokens; new 4 lines are ~73 tokens. Net ~+3. + */ +export const WHEN_TO_ACCESS_SECTION: readonly string[] = [ + '## When to access memories', + '- When memories seem relevant, or the user references prior-conversation work.', + '- You MUST access memory when the user explicitly asks you to check, recall, or remember.', + '- If the user says to *ignore* or *not use* memory: proceed as if MEMORY.md were empty. Do not apply remembered facts, cite, compare against, or mention memory content.', + MEMORY_DRIFT_CAVEAT, +] + +/** + * `## Trusting what you recall` section. Heavier-weight guidance on HOW to + * treat a memory once you've recalled it — separate from WHEN to access. + * + * Eval-validated (memory-prompt-iteration.eval.ts, 2026-03-17): + * H1 (verify function/file claims): 0/2 → 3/3 via appendSystemPrompt. When + * buried as a bullet under "When to access", dropped to 0/3 — position + * matters. The H1 cue is about what to DO with a memory, not when to + * look, so it needs its own section-level trigger context. + * H5 (read-side noise rejection): 0/2 → 3/3 via appendSystemPrompt, 2/3 + * in-place as a bullet. Partial because "snapshot" is intuitively closer + * to "when to access" than H1 is. + * + * Known gap: H1 doesn't cover slash-command claims (0/3 on the /fork case — + * slash commands aren't files or functions in the model's ontology). + */ +export const TRUSTING_RECALL_SECTION: readonly string[] = [ + // Header wording matters: "Before recommending" (action cue at the decision + // point) tested better than "Trusting what you recall" (abstract). The + // appendSystemPrompt variant with this header went 3/3; the abstract header + // went 0/3 in-place. Same body text — only the header differed. + '## Before recommending from memory', + '', + 'A memory that names a specific function, file, or flag is a claim that it existed *when the memory was written*. It may have been renamed, removed, or never merged. Before recommending it:', + '', + '- If the memory names a file path: check the file exists.', + '- If the memory names a function or flag: grep for it.', + '- If the user is about to act on your recommendation (not just asking about history), verify first.', + '', + '"The memory says X exists" is not the same as "X exists now."', + '', + 'A memory that summarizes repo state (activity logs, architecture snapshots) is frozen in time. If the user asks about *recent* or *current* state, prefer `git log` or reading the code over recalling the snapshot.', +] + +/** + * Frontmatter format example with the `type` field. + */ +export const MEMORY_FRONTMATTER_EXAMPLE: readonly string[] = [ + '```markdown', + '---', + 'name: {{memory name}}', + 'description: {{one-line description — used to decide relevance in future conversations, so be specific}}', + `type: {{${MEMORY_TYPES.join(', ')}}}`, + '---', + '', + '{{memory content — for feedback/project types, structure as: rule/fact, then **Why:** and **How to apply:** lines}}', + '```', +] diff --git a/memdir/paths.ts b/memdir/paths.ts new file mode 100644 index 0000000..68a6baf --- /dev/null +++ b/memdir/paths.ts @@ -0,0 +1,278 @@ +import memoize from 'lodash-es/memoize.js' +import { homedir } from 'os' +import { isAbsolute, join, normalize, sep } from 'path' +import { + getIsNonInteractiveSession, + getProjectRoot, +} from '../bootstrap/state.js' +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' +import { + getClaudeConfigHomeDir, + isEnvDefinedFalsy, + isEnvTruthy, +} from '../utils/envUtils.js' +import { findCanonicalGitRoot } from '../utils/git.js' +import { sanitizePath } from '../utils/path.js' +import { + getInitialSettings, + getSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * Whether auto-memory features are enabled (memdir, agent memory, past session search). + * Enabled by default. Priority chain (first defined wins): + * 1. CLAUDE_CODE_DISABLE_AUTO_MEMORY env var (1/true → OFF, 0/false → ON) + * 2. CLAUDE_CODE_SIMPLE (--bare) → OFF + * 3. CCR without persistent storage → OFF (no CLAUDE_CODE_REMOTE_MEMORY_DIR) + * 4. autoMemoryEnabled in settings.json (supports project-level opt-out) + * 5. Default: enabled + */ +export function isAutoMemoryEnabled(): boolean { + const envVal = process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY + if (isEnvTruthy(envVal)) { + return false + } + if (isEnvDefinedFalsy(envVal)) { + return true + } + // --bare / SIMPLE: prompts.ts already drops the memory section from the + // system prompt via its SIMPLE early-return; this gate stops the other half + // (extractMemories turn-end fork, autoDream, /remember, /dream, team sync). + if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) { + return false + } + if ( + isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) && + !process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR + ) { + return false + } + const settings = getInitialSettings() + if (settings.autoMemoryEnabled !== undefined) { + return settings.autoMemoryEnabled + } + return true +} + +/** + * Whether the extract-memories background agent will run this session. + * + * The main agent's prompt always has full save instructions regardless of + * this gate — when the main agent writes memories, the background agent + * skips that range (hasMemoryWritesSince in extractMemories.ts); when it + * doesn't, the background agent catches anything missed. + * + * Callers must also gate on feature('EXTRACT_MEMORIES') — that check cannot + * live inside this helper because feature() only tree-shakes when used + * directly in an `if` condition. + */ +export function isExtractModeActive(): boolean { + if (!getFeatureValue_CACHED_MAY_BE_STALE('tengu_passport_quail', false)) { + return false + } + return ( + !getIsNonInteractiveSession() || + getFeatureValue_CACHED_MAY_BE_STALE('tengu_slate_thimble', false) + ) +} + +/** + * Returns the base directory for persistent memory storage. + * Resolution order: + * 1. CLAUDE_CODE_REMOTE_MEMORY_DIR env var (explicit override, set in CCR) + * 2. ~/.claude (default config home) + */ +export function getMemoryBaseDir(): string { + if (process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR) { + return process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR + } + return getClaudeConfigHomeDir() +} + +const AUTO_MEM_DIRNAME = 'memory' +const AUTO_MEM_ENTRYPOINT_NAME = 'MEMORY.md' + +/** + * Normalize and validate a candidate auto-memory directory path. + * + * SECURITY: Rejects paths that would be dangerous as a read-allowlist root + * or that normalize() doesn't fully resolve: + * - relative (!isAbsolute): "../foo" — would be interpreted relative to CWD + * - root/near-root (length < 3): "/" → "" after strip; "/a" too short + * - Windows drive-root (C: regex): "C:\" → "C:" after strip + * - UNC paths (\\server\share): network paths — opaque trust boundary + * - null byte: survives normalize(), can truncate in syscalls + * + * Returns the normalized path with exactly one trailing separator, + * or undefined if the path is unset/empty/rejected. + */ +function validateMemoryPath( + raw: string | undefined, + expandTilde: boolean, +): string | undefined { + if (!raw) { + return undefined + } + let candidate = raw + // Settings.json paths support ~/ expansion (user-friendly). The env var + // override does not (it's set programmatically by Cowork/SDK, which should + // always pass absolute paths). Bare "~", "~/", "~/.", "~/..", etc. are NOT + // expanded — they would make isAutoMemPath() match all of $HOME or its + // parent (same class of danger as "/" or "C:\"). + if ( + expandTilde && + (candidate.startsWith('~/') || candidate.startsWith('~\\')) + ) { + const rest = candidate.slice(2) + // Reject trivial remainders that would expand to $HOME or an ancestor. + // normalize('') = '.', normalize('.') = '.', normalize('foo/..') = '.', + // normalize('..') = '..', normalize('foo/../..') = '..' + const restNorm = normalize(rest || '.') + if (restNorm === '.' || restNorm === '..') { + return undefined + } + candidate = join(homedir(), rest) + } + // normalize() may preserve a trailing separator; strip before adding + // exactly one to match the trailing-sep contract of getAutoMemPath() + const normalized = normalize(candidate).replace(/[/\\]+$/, '') + if ( + !isAbsolute(normalized) || + normalized.length < 3 || + /^[A-Za-z]:$/.test(normalized) || + normalized.startsWith('\\\\') || + normalized.startsWith('//') || + normalized.includes('\0') + ) { + return undefined + } + return (normalized + sep).normalize('NFC') +} + +/** + * Direct override for the full auto-memory directory path via env var. + * When set, getAutoMemPath()/getAutoMemEntrypoint() return this path directly + * instead of computing `{base}/projects/{sanitized-cwd}/memory/`. + * + * Used by Cowork to redirect memory to a space-scoped mount where the + * per-session cwd (which contains the VM process name) would otherwise + * produce a different project-key for every session. + */ +function getAutoMemPathOverride(): string | undefined { + return validateMemoryPath( + process.env.CLAUDE_COWORK_MEMORY_PATH_OVERRIDE, + false, + ) +} + +/** + * Settings.json override for the full auto-memory directory path. + * Supports ~/ expansion for user convenience. + * + * SECURITY: projectSettings (.claude/settings.json committed to the repo) is + * intentionally excluded — a malicious repo could otherwise set + * autoMemoryDirectory: "~/.ssh" and gain silent write access to sensitive + * directories via the filesystem.ts write carve-out (which fires when + * isAutoMemPath() matches and hasAutoMemPathOverride() is false). This follows + * the same pattern as hasSkipDangerousModePermissionPrompt() etc. + */ +function getAutoMemPathSetting(): string | undefined { + const dir = + getSettingsForSource('policySettings')?.autoMemoryDirectory ?? + getSettingsForSource('flagSettings')?.autoMemoryDirectory ?? + getSettingsForSource('localSettings')?.autoMemoryDirectory ?? + getSettingsForSource('userSettings')?.autoMemoryDirectory + return validateMemoryPath(dir, true) +} + +/** + * Check if CLAUDE_COWORK_MEMORY_PATH_OVERRIDE is set to a valid override. + * Use this as a signal that the SDK caller has explicitly opted into + * the auto-memory mechanics — e.g. to decide whether to inject the + * memory prompt when a custom system prompt replaces the default. + */ +export function hasAutoMemPathOverride(): boolean { + return getAutoMemPathOverride() !== undefined +} + +/** + * Returns the canonical git repo root if available, otherwise falls back to + * the stable project root. Uses findCanonicalGitRoot so all worktrees of the + * same repo share one auto-memory directory (anthropics/claude-code#24382). + */ +function getAutoMemBase(): string { + return findCanonicalGitRoot(getProjectRoot()) ?? getProjectRoot() +} + +/** + * Returns the auto-memory directory path. + * + * Resolution order: + * 1. CLAUDE_COWORK_MEMORY_PATH_OVERRIDE env var (full-path override, used by Cowork) + * 2. autoMemoryDirectory in settings.json (trusted sources only: policy/local/user) + * 3. /projects//memory/ + * where memoryBase is resolved by getMemoryBaseDir() + * + * Memoized: render-path callers (collapseReadSearchGroups → isAutoManagedMemoryFile) + * fire per tool-use message per Messages re-render; each miss costs + * getSettingsForSource × 4 → parseSettingsFile (realpathSync + readFileSync). + * Keyed on projectRoot so tests that change its mock mid-block recompute; + * env vars / settings.json / CLAUDE_CONFIG_DIR are session-stable in + * production and covered by per-test cache.clear. + */ +export const getAutoMemPath = memoize( + (): string => { + const override = getAutoMemPathOverride() ?? getAutoMemPathSetting() + if (override) { + return override + } + const projectsDir = join(getMemoryBaseDir(), 'projects') + return ( + join(projectsDir, sanitizePath(getAutoMemBase()), AUTO_MEM_DIRNAME) + sep + ).normalize('NFC') + }, + () => getProjectRoot(), +) + +/** + * Returns the daily log file path for the given date (defaults to today). + * Shape: /logs/YYYY/MM/YYYY-MM-DD.md + * + * Used by assistant mode (feature('KAIROS')): rather than maintaining + * MEMORY.md as a live index, the agent appends to a date-named log file + * as it works. A separate nightly /dream skill distills these logs into + * topic files + MEMORY.md. + */ +export function getAutoMemDailyLogPath(date: Date = new Date()): string { + const yyyy = date.getFullYear().toString() + const mm = (date.getMonth() + 1).toString().padStart(2, '0') + const dd = date.getDate().toString().padStart(2, '0') + return join(getAutoMemPath(), 'logs', yyyy, mm, `${yyyy}-${mm}-${dd}.md`) +} + +/** + * Returns the auto-memory entrypoint (MEMORY.md inside the auto-memory dir). + * Follows the same resolution order as getAutoMemPath(). + */ +export function getAutoMemEntrypoint(): string { + return join(getAutoMemPath(), AUTO_MEM_ENTRYPOINT_NAME) +} + +/** + * Check if an absolute path is within the auto-memory directory. + * + * When CLAUDE_COWORK_MEMORY_PATH_OVERRIDE is set, this matches against the + * env-var override directory. Note that a true return here does NOT imply + * write permission in that case — the filesystem.ts write carve-out is gated + * on !hasAutoMemPathOverride() (it exists to bypass DANGEROUS_DIRECTORIES). + * + * The settings.json autoMemoryDirectory DOES get the write carve-out: it's the + * user's explicit choice from a trusted settings source (projectSettings is + * excluded — see getAutoMemPathSetting), and hasAutoMemPathOverride() remains + * false for it. + */ +export function isAutoMemPath(absolutePath: string): boolean { + // SECURITY: Normalize to prevent path traversal bypasses via .. segments + const normalizedPath = normalize(absolutePath) + return normalizedPath.startsWith(getAutoMemPath()) +} diff --git a/memdir/teamMemPaths.ts b/memdir/teamMemPaths.ts new file mode 100644 index 0000000..1a13ae7 --- /dev/null +++ b/memdir/teamMemPaths.ts @@ -0,0 +1,292 @@ +import { lstat, realpath } from 'fs/promises' +import { dirname, join, resolve, sep } from 'path' +import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' +import { getErrnoCode } from '../utils/errors.js' +import { getAutoMemPath, isAutoMemoryEnabled } from './paths.js' + +/** + * Error thrown when a path validation detects a traversal or injection attempt. + */ +export class PathTraversalError extends Error { + constructor(message: string) { + super(message) + this.name = 'PathTraversalError' + } +} + +/** + * Sanitize a file path key by rejecting dangerous patterns. + * Checks for null bytes, URL-encoded traversals, and other injection vectors. + * Returns the sanitized string or throws PathTraversalError. + */ +function sanitizePathKey(key: string): string { + // Null bytes can truncate paths in C-based syscalls + if (key.includes('\0')) { + throw new PathTraversalError(`Null byte in path key: "${key}"`) + } + // URL-encoded traversals (e.g. %2e%2e%2f = ../) + let decoded: string + try { + decoded = decodeURIComponent(key) + } catch { + // Malformed percent-encoding (e.g. %ZZ, lone %) — not valid URL-encoding, + // so no URL-encoded traversal is possible + decoded = key + } + if (decoded !== key && (decoded.includes('..') || decoded.includes('/'))) { + throw new PathTraversalError(`URL-encoded traversal in path key: "${key}"`) + } + // Unicode normalization attacks: fullwidth ../ (U+FF0E U+FF0F) normalize + // to ASCII ../ under NFKC. While path.resolve/fs.writeFile treat these as + // literal bytes (not separators), downstream layers or filesystems may + // normalize — reject for defense-in-depth (PSR M22187 vector 4). + const normalized = key.normalize('NFKC') + if ( + normalized !== key && + (normalized.includes('..') || + normalized.includes('/') || + normalized.includes('\\') || + normalized.includes('\0')) + ) { + throw new PathTraversalError( + `Unicode-normalized traversal in path key: "${key}"`, + ) + } + // Reject backslashes (Windows path separator used as traversal vector) + if (key.includes('\\')) { + throw new PathTraversalError(`Backslash in path key: "${key}"`) + } + // Reject absolute paths + if (key.startsWith('/')) { + throw new PathTraversalError(`Absolute path key: "${key}"`) + } + return key +} + +/** + * Whether team memory features are enabled. + * Team memory is a subdirectory of auto memory, so it requires auto memory + * to be enabled. This keeps all team-memory consumers (prompt, content + * injection, sync watcher, file detection) consistent when auto memory is + * disabled via env var or settings. + */ +export function isTeamMemoryEnabled(): boolean { + if (!isAutoMemoryEnabled()) { + return false + } + return getFeatureValue_CACHED_MAY_BE_STALE('tengu_herring_clock', false) +} + +/** + * Returns the team memory path: /projects//memory/team/ + * Lives as a subdirectory of the auto-memory directory, scoped per-project. + */ +export function getTeamMemPath(): string { + return (join(getAutoMemPath(), 'team') + sep).normalize('NFC') +} + +/** + * Returns the team memory entrypoint: /projects//memory/team/MEMORY.md + * Lives as a subdirectory of the auto-memory directory, scoped per-project. + */ +export function getTeamMemEntrypoint(): string { + return join(getAutoMemPath(), 'team', 'MEMORY.md') +} + +/** + * Resolve symlinks for the deepest existing ancestor of a path. + * The target file may not exist yet (we may be about to create it), so we + * walk up the directory tree until realpath() succeeds, then rejoin the + * non-existing tail onto the resolved ancestor. + * + * SECURITY (PSR M22186): path.resolve() does NOT resolve symlinks. An attacker + * who can place a symlink inside teamDir pointing outside (e.g. to + * ~/.ssh/authorized_keys) would pass a resolve()-based containment check. + * Using realpath() on the deepest existing ancestor ensures we compare the + * actual filesystem location, not the symbolic path. + * + */ +async function realpathDeepestExisting(absolutePath: string): Promise { + const tail: string[] = [] + let current = absolutePath + // Walk up until realpath succeeds. ENOENT means this segment doesn't exist + // yet; pop it onto the tail and try the parent. ENOTDIR means a non-directory + // component sits in the middle of the path; pop and retry so we can realpath + // the ancestor to detect symlink escapes. + // Loop terminates when we reach the filesystem root (dirname('/') === '/'). + for ( + let parent = dirname(current); + current !== parent; + parent = dirname(current) + ) { + try { + const realCurrent = await realpath(current) + // Rejoin the non-existing tail in reverse order (deepest popped first) + return tail.length === 0 + ? realCurrent + : join(realCurrent, ...tail.reverse()) + } catch (e: unknown) { + const code = getErrnoCode(e) + if (code === 'ENOENT') { + // Could be truly non-existent (safe to walk up) OR a dangling symlink + // whose target doesn't exist. Dangling symlinks are an attack vector: + // writeFile would follow the link and create the target outside teamDir. + // lstat distinguishes: it succeeds for dangling symlinks (the link entry + // itself exists), fails with ENOENT for truly non-existent paths. + try { + const st = await lstat(current) + if (st.isSymbolicLink()) { + throw new PathTraversalError( + `Dangling symlink detected (target does not exist): "${current}"`, + ) + } + // lstat succeeded but isn't a symlink — ENOENT from realpath was + // caused by a dangling symlink in an ancestor. Walk up to find it. + } catch (lstatErr: unknown) { + if (lstatErr instanceof PathTraversalError) { + throw lstatErr + } + // lstat also failed (truly non-existent or inaccessible) — safe to walk up. + } + } else if (code === 'ELOOP') { + // Symlink loop — corrupted or malicious filesystem state. + throw new PathTraversalError( + `Symlink loop detected in path: "${current}"`, + ) + } else if (code !== 'ENOTDIR' && code !== 'ENAMETOOLONG') { + // EACCES, EIO, etc. — cannot verify containment. Fail closed by wrapping + // as PathTraversalError so the caller can skip this entry gracefully + // instead of aborting the entire batch. + throw new PathTraversalError( + `Cannot verify path containment (${code}): "${current}"`, + ) + } + tail.push(current.slice(parent.length + sep.length)) + current = parent + } + } + // Reached filesystem root without finding an existing ancestor (rare — + // root normally exists). Fall back to the input; containment check will reject. + return absolutePath +} + +/** + * Check whether a real (symlink-resolved) path is within the real team + * memory directory. Both sides are realpath'd so the comparison is between + * canonical filesystem locations. + * + * If teamDir does not exist, returns true (skips the check). This is safe: + * a symlink escape requires a pre-existing symlink inside teamDir, which + * requires teamDir to exist. If there's no directory, there's no symlink, + * and the first-pass string-level containment check is sufficient. + */ +async function isRealPathWithinTeamDir( + realCandidate: string, +): Promise { + let realTeamDir: string + try { + // getTeamMemPath() includes a trailing separator; strip it because + // realpath() rejects trailing separators on some platforms. + realTeamDir = await realpath(getTeamMemPath().replace(/[/\\]+$/, '')) + } catch (e: unknown) { + const code = getErrnoCode(e) + if (code === 'ENOENT' || code === 'ENOTDIR') { + // Team dir doesn't exist — symlink escape impossible, skip check. + return true + } + // Unexpected error (EACCES, EIO) — fail closed. + return false + } + if (realCandidate === realTeamDir) { + return true + } + // Prefix-attack protection: require separator after the prefix so that + // "/foo/team-evil" doesn't match "/foo/team". + return realCandidate.startsWith(realTeamDir + sep) +} + +/** + * Check if a resolved absolute path is within the team memory directory. + * Uses path.resolve() to convert relative paths and eliminate traversal segments. + * Does NOT resolve symlinks — for write validation use validateTeamMemWritePath() + * or validateTeamMemKey() which include symlink resolution. + */ +export function isTeamMemPath(filePath: string): boolean { + // SECURITY: resolve() converts to absolute and eliminates .. segments, + // preventing path traversal attacks (e.g. "team/../../etc/passwd") + const resolvedPath = resolve(filePath) + const teamDir = getTeamMemPath() + return resolvedPath.startsWith(teamDir) +} + +/** + * Validate that an absolute file path is safe for writing to the team memory directory. + * Returns the resolved absolute path if valid. + * Throws PathTraversalError if the path contains injection vectors, escapes the + * directory via .. segments, or escapes via a symlink (PSR M22186). + */ +export async function validateTeamMemWritePath( + filePath: string, +): Promise { + if (filePath.includes('\0')) { + throw new PathTraversalError(`Null byte in path: "${filePath}"`) + } + // First pass: normalize .. segments and check string-level containment. + // This is a fast rejection for obvious traversal attempts before we touch + // the filesystem. + const resolvedPath = resolve(filePath) + const teamDir = getTeamMemPath() + // Prefix attack protection: teamDir already ends with sep (from getTeamMemPath), + // so "team-evil/" won't match "team/" + if (!resolvedPath.startsWith(teamDir)) { + throw new PathTraversalError( + `Path escapes team memory directory: "${filePath}"`, + ) + } + // Second pass: resolve symlinks on the deepest existing ancestor and verify + // the real path is still within the real team dir. This catches symlink-based + // escapes that path.resolve() alone cannot detect. + const realPath = await realpathDeepestExisting(resolvedPath) + if (!(await isRealPathWithinTeamDir(realPath))) { + throw new PathTraversalError( + `Path escapes team memory directory via symlink: "${filePath}"`, + ) + } + return resolvedPath +} + +/** + * Validate a relative path key from the server against the team memory directory. + * Sanitizes the key, joins with the team dir, resolves symlinks on the deepest + * existing ancestor, and verifies containment against the real team dir. + * Returns the resolved absolute path. + * Throws PathTraversalError if the key is malicious (PSR M22186). + */ +export async function validateTeamMemKey(relativeKey: string): Promise { + sanitizePathKey(relativeKey) + const teamDir = getTeamMemPath() + const fullPath = join(teamDir, relativeKey) + // First pass: normalize .. segments and check string-level containment. + const resolvedPath = resolve(fullPath) + if (!resolvedPath.startsWith(teamDir)) { + throw new PathTraversalError( + `Key escapes team memory directory: "${relativeKey}"`, + ) + } + // Second pass: resolve symlinks and verify real containment. + const realPath = await realpathDeepestExisting(resolvedPath) + if (!(await isRealPathWithinTeamDir(realPath))) { + throw new PathTraversalError( + `Key escapes team memory directory via symlink: "${relativeKey}"`, + ) + } + return resolvedPath +} + +/** + * Check if a file path is within the team memory directory + * and team memory is enabled. + */ +export function isTeamMemFile(filePath: string): boolean { + return isTeamMemoryEnabled() && isTeamMemPath(filePath) +} diff --git a/memdir/teamMemPrompts.ts b/memdir/teamMemPrompts.ts new file mode 100644 index 0000000..de5ea84 --- /dev/null +++ b/memdir/teamMemPrompts.ts @@ -0,0 +1,100 @@ +import { + buildSearchingPastContextSection, + DIRS_EXIST_GUIDANCE, + ENTRYPOINT_NAME, + MAX_ENTRYPOINT_LINES, +} from './memdir.js' +import { + MEMORY_DRIFT_CAVEAT, + MEMORY_FRONTMATTER_EXAMPLE, + TRUSTING_RECALL_SECTION, + TYPES_SECTION_COMBINED, + WHAT_NOT_TO_SAVE_SECTION, +} from './memoryTypes.js' +import { getAutoMemPath } from './paths.js' +import { getTeamMemPath } from './teamMemPaths.js' + +/** + * Build the combined prompt when both auto memory and team memory are enabled. + * Closed four-type taxonomy (user / feedback / project / reference) with + * per-type guidance embedded in XML-style blocks. + */ +export function buildCombinedMemoryPrompt( + extraGuidelines?: string[], + skipIndex = false, +): string { + const autoDir = getAutoMemPath() + const teamDir = getTeamMemPath() + + const howToSave = skipIndex + ? [ + '## How to save memories', + '', + "Write each memory to its own file in the chosen directory (private or team, per the type's scope guidance) using this frontmatter format:", + '', + ...MEMORY_FRONTMATTER_EXAMPLE, + '', + '- Keep the name, description, and type fields in memory files up-to-date with the content', + '- Organize memory semantically by topic, not chronologically', + '- Update or remove memories that turn out to be wrong or outdated', + '- Do not write duplicate memories. First check if there is an existing memory you can update before writing a new one.', + ] + : [ + '## How to save memories', + '', + 'Saving a memory is a two-step process:', + '', + "**Step 1** — write the memory to its own file in the chosen directory (private or team, per the type's scope guidance) using this frontmatter format:", + '', + ...MEMORY_FRONTMATTER_EXAMPLE, + '', + `**Step 2** — add a pointer to that file in the same directory's \`${ENTRYPOINT_NAME}\`. Each directory (private and team) has its own \`${ENTRYPOINT_NAME}\` index — each entry should be one line, under ~150 characters: \`- [Title](file.md) — one-line hook\`. They have no frontmatter. Never write memory content directly into a \`${ENTRYPOINT_NAME}\`.`, + '', + `- Both \`${ENTRYPOINT_NAME}\` indexes are loaded into your conversation context — lines after ${MAX_ENTRYPOINT_LINES} will be truncated, so keep them concise`, + '- Keep the name, description, and type fields in memory files up-to-date with the content', + '- Organize memory semantically by topic, not chronologically', + '- Update or remove memories that turn out to be wrong or outdated', + '- Do not write duplicate memories. First check if there is an existing memory you can update before writing a new one.', + ] + + const lines = [ + '# Memory', + '', + `You have a persistent, file-based memory system with two directories: a private directory at \`${autoDir}\` and a shared team directory at \`${teamDir}\`. ${DIRS_EXIST_GUIDANCE}`, + '', + "You should build up this memory system over time so that future conversations can have a complete picture of who the user is, how they'd like to collaborate with you, what behaviors to avoid or repeat, and the context behind the work the user gives you.", + '', + 'If the user explicitly asks you to remember something, save it immediately as whichever type fits best. If they ask you to forget something, find and remove the relevant entry.', + '', + '## Memory scope', + '', + 'There are two scope levels:', + '', + `- private: memories that are private between you and the current user. They persist across conversations with only this specific user and are stored at the root \`${autoDir}\`.`, + `- team: memories that are shared with and contributed by all of the users who work within this project directory. Team memories are synced at the beginning of every session and they are stored at \`${teamDir}\`.`, + '', + ...TYPES_SECTION_COMBINED, + ...WHAT_NOT_TO_SAVE_SECTION, + '- You MUST avoid saving sensitive data within shared team memories. For example, never save API keys or user credentials.', + '', + ...howToSave, + '', + '## When to access memories', + '- When memories (personal or team) seem relevant, or the user references prior work with them or others in their organization.', + '- You MUST access memory when the user explicitly asks you to check, recall, or remember.', + '- If the user says to *ignore* or *not use* memory: proceed as if MEMORY.md were empty. Do not apply remembered facts, cite, compare against, or mention memory content.', + MEMORY_DRIFT_CAVEAT, + '', + ...TRUSTING_RECALL_SECTION, + '', + '## Memory and other forms of persistence', + 'Memory is one of several persistence mechanisms available to you as you assist the user in a given conversation. The distinction is often that memory can be recalled in future conversations and should not be used for persisting information that is only useful within the scope of the current conversation.', + '- When to use or update a plan instead of memory: If you are about to start a non-trivial implementation task and would like to reach alignment with the user on your approach you should use a Plan rather than saving this information to memory. Similarly, if you already have a plan within the conversation and you have changed your approach persist that change by updating the plan rather than saving a memory.', + '- When to use or update tasks instead of memory: When you need to break your work in current conversation into discrete steps or keep track of your progress use tasks instead of saving to memory. Tasks are great for persisting information about the work that needs to be done in the current conversation, but memory should be reserved for information that will be useful in future conversations.', + ...(extraGuidelines ?? []), + '', + ...buildSearchingPastContextSection(autoDir), + ] + + return lines.join('\n') +} diff --git a/migrations/migrateAutoUpdatesToSettings.ts b/migrations/migrateAutoUpdatesToSettings.ts new file mode 100644 index 0000000..c541713 --- /dev/null +++ b/migrations/migrateAutoUpdatesToSettings.ts @@ -0,0 +1,61 @@ +import { logEvent } from 'src/services/analytics/index.js' +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' +import { logError } from '../utils/log.js' +import { + getSettingsForSource, + updateSettingsForSource, +} from '../utils/settings/settings.js' +/** + * Migration: Move user-set autoUpdates preference to settings.json env var + * Only migrates if user explicitly disabled auto-updates (not for protection) + * This preserves user intent while allowing native installations to auto-update + */ +export function migrateAutoUpdatesToSettings(): void { + const globalConfig = getGlobalConfig() + + // Only migrate if autoUpdates was explicitly set to false by user preference + // (not automatically for native protection) + if ( + globalConfig.autoUpdates !== false || + globalConfig.autoUpdatesProtectedForNative === true + ) { + return + } + + try { + const userSettings = getSettingsForSource('userSettings') || {} + + // Always set DISABLE_AUTOUPDATER to preserve user intent + // We need to overwrite even if it exists, to ensure the migration is complete + updateSettingsForSource('userSettings', { + ...userSettings, + env: { + ...userSettings.env, + DISABLE_AUTOUPDATER: '1', + }, + }) + + logEvent('tengu_migrate_autoupdates_to_settings', { + was_user_preference: true, + already_had_env_var: !!userSettings.env?.DISABLE_AUTOUPDATER, + }) + + // explicitly set, so this takes effect immediately + process.env.DISABLE_AUTOUPDATER = '1' + + // Remove autoUpdates from global config after successful migration + saveGlobalConfig(current => { + const { + autoUpdates: _, + autoUpdatesProtectedForNative: __, + ...updatedConfig + } = current + return updatedConfig + }) + } catch (error) { + logError(new Error(`Failed to migrate auto-updates: ${error}`)) + logEvent('tengu_migrate_autoupdates_error', { + has_error: true, + }) + } +} diff --git a/migrations/migrateBypassPermissionsAcceptedToSettings.ts b/migrations/migrateBypassPermissionsAcceptedToSettings.ts new file mode 100644 index 0000000..e36407f --- /dev/null +++ b/migrations/migrateBypassPermissionsAcceptedToSettings.ts @@ -0,0 +1,40 @@ +import { logEvent } from 'src/services/analytics/index.js' +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' +import { logError } from '../utils/log.js' +import { + hasSkipDangerousModePermissionPrompt, + updateSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * Migration: Move bypassPermissionsModeAccepted from global config to settings.json + * as skipDangerousModePermissionPrompt. This is a better home since settings.json + * is the user-configurable settings file. + */ +export function migrateBypassPermissionsAcceptedToSettings(): void { + const globalConfig = getGlobalConfig() + + if (!globalConfig.bypassPermissionsModeAccepted) { + return + } + + try { + if (!hasSkipDangerousModePermissionPrompt()) { + updateSettingsForSource('userSettings', { + skipDangerousModePermissionPrompt: true, + }) + } + + logEvent('tengu_migrate_bypass_permissions_accepted', {}) + + saveGlobalConfig(current => { + if (!('bypassPermissionsModeAccepted' in current)) return current + const { bypassPermissionsModeAccepted: _, ...updatedConfig } = current + return updatedConfig + }) + } catch (error) { + logError( + new Error(`Failed to migrate bypass permissions accepted: ${error}`), + ) + } +} diff --git a/migrations/migrateEnableAllProjectMcpServersToSettings.ts b/migrations/migrateEnableAllProjectMcpServersToSettings.ts new file mode 100644 index 0000000..42d1bc2 --- /dev/null +++ b/migrations/migrateEnableAllProjectMcpServersToSettings.ts @@ -0,0 +1,118 @@ +import { logEvent } from 'src/services/analytics/index.js' +import { + getCurrentProjectConfig, + saveCurrentProjectConfig, +} from '../utils/config.js' +import { logError } from '../utils/log.js' +import { + getSettingsForSource, + updateSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * Migration: Move MCP server approval fields from project config to local settings + * This migrates both enableAllProjectMcpServers and enabledMcpjsonServers to the + * settings system for better management and consistency. + */ +export function migrateEnableAllProjectMcpServersToSettings(): void { + const projectConfig = getCurrentProjectConfig() + + // Check if any field exists in project config + const hasEnableAll = projectConfig.enableAllProjectMcpServers !== undefined + const hasEnabledServers = + projectConfig.enabledMcpjsonServers && + projectConfig.enabledMcpjsonServers.length > 0 + const hasDisabledServers = + projectConfig.disabledMcpjsonServers && + projectConfig.disabledMcpjsonServers.length > 0 + + if (!hasEnableAll && !hasEnabledServers && !hasDisabledServers) { + return + } + + try { + const existingSettings = getSettingsForSource('localSettings') || {} + const updates: Partial<{ + enableAllProjectMcpServers: boolean + enabledMcpjsonServers: string[] + disabledMcpjsonServers: string[] + }> = {} + const fieldsToRemove: Array< + | 'enableAllProjectMcpServers' + | 'enabledMcpjsonServers' + | 'disabledMcpjsonServers' + > = [] + + // Migrate enableAllProjectMcpServers if it exists and hasn't been migrated + if ( + hasEnableAll && + existingSettings.enableAllProjectMcpServers === undefined + ) { + updates.enableAllProjectMcpServers = + projectConfig.enableAllProjectMcpServers + fieldsToRemove.push('enableAllProjectMcpServers') + } else if (hasEnableAll) { + // Already migrated, just mark for removal + fieldsToRemove.push('enableAllProjectMcpServers') + } + + // Migrate enabledMcpjsonServers if it exists + if (hasEnabledServers && projectConfig.enabledMcpjsonServers) { + const existingEnabledServers = + existingSettings.enabledMcpjsonServers || [] + // Merge the servers (avoiding duplicates) + updates.enabledMcpjsonServers = [ + ...new Set([ + ...existingEnabledServers, + ...projectConfig.enabledMcpjsonServers, + ]), + ] + fieldsToRemove.push('enabledMcpjsonServers') + } + + // Migrate disabledMcpjsonServers if it exists + if (hasDisabledServers && projectConfig.disabledMcpjsonServers) { + const existingDisabledServers = + existingSettings.disabledMcpjsonServers || [] + // Merge the servers (avoiding duplicates) + updates.disabledMcpjsonServers = [ + ...new Set([ + ...existingDisabledServers, + ...projectConfig.disabledMcpjsonServers, + ]), + ] + fieldsToRemove.push('disabledMcpjsonServers') + } + + // Update settings if there are any updates + if (Object.keys(updates).length > 0) { + updateSettingsForSource('localSettings', updates) + } + + // Remove migrated fields from project config + if ( + fieldsToRemove.includes('enableAllProjectMcpServers') || + fieldsToRemove.includes('enabledMcpjsonServers') || + fieldsToRemove.includes('disabledMcpjsonServers') + ) { + saveCurrentProjectConfig(current => { + const { + enableAllProjectMcpServers: _enableAll, + enabledMcpjsonServers: _enabledServers, + disabledMcpjsonServers: _disabledServers, + ...configWithoutFields + } = current + return configWithoutFields + }) + } + + // Log the migration event + logEvent('tengu_migrate_mcp_approval_fields_success', { + migratedCount: fieldsToRemove.length, + }) + } catch (e: unknown) { + // Log migration failure but don't throw to avoid breaking startup + logError(e) + logEvent('tengu_migrate_mcp_approval_fields_error', {}) + } +} diff --git a/migrations/migrateFennecToOpus.ts b/migrations/migrateFennecToOpus.ts new file mode 100644 index 0000000..ee5e33c --- /dev/null +++ b/migrations/migrateFennecToOpus.ts @@ -0,0 +1,45 @@ +import { + getSettingsForSource, + updateSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * Migrate users on removed fennec model aliases to their new Opus 4.6 aliases. + * - fennec-latest → opus + * - fennec-latest[1m] → opus[1m] + * - fennec-fast-latest → opus[1m] + fast mode + * - opus-4-5-fast → opus + fast mode + * + * Only touches userSettings. Reading and writing the same source keeps this + * idempotent without a completion flag. Fennec aliases in project/local/policy + * settings are left alone — we can't rewrite those, and reading merged + * settings here would cause infinite re-runs + silent global promotion. + */ +export function migrateFennecToOpus(): void { + if (process.env.USER_TYPE !== 'ant') { + return + } + + const settings = getSettingsForSource('userSettings') + + const model = settings?.model + if (typeof model === 'string') { + if (model.startsWith('fennec-latest[1m]')) { + updateSettingsForSource('userSettings', { + model: 'opus[1m]', + }) + } else if (model.startsWith('fennec-latest')) { + updateSettingsForSource('userSettings', { + model: 'opus', + }) + } else if ( + model.startsWith('fennec-fast-latest') || + model.startsWith('opus-4-5-fast') + ) { + updateSettingsForSource('userSettings', { + model: 'opus[1m]', + fastMode: true, + }) + } + } +} diff --git a/migrations/migrateLegacyOpusToCurrent.ts b/migrations/migrateLegacyOpusToCurrent.ts new file mode 100644 index 0000000..bdca4aa --- /dev/null +++ b/migrations/migrateLegacyOpusToCurrent.ts @@ -0,0 +1,57 @@ +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { saveGlobalConfig } from '../utils/config.js' +import { isLegacyModelRemapEnabled } from '../utils/model/model.js' +import { getAPIProvider } from '../utils/model/providers.js' +import { + getSettingsForSource, + updateSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * Migrate first-party users off explicit Opus 4.0/4.1 model strings. + * + * The 'opus' alias already resolves to Opus 4.6 for 1P, so anyone still + * on an explicit 4.0/4.1 string pinned it in settings before 4.5 launched. + * parseUserSpecifiedModel now silently remaps these at runtime anyway — + * this migration cleans up the settings file so /model shows the right + * thing, and sets a timestamp so the REPL can show a one-time notification. + * + * Only touches userSettings. Legacy strings in project/local/policy settings + * are left alone (we can't/shouldn't rewrite those) and are still remapped at + * runtime by parseUserSpecifiedModel. Reading and writing the same source + * keeps this idempotent without a completion flag, and avoids silently + * promoting 'opus' to the global default for users who only pinned it in one + * project. + */ +export function migrateLegacyOpusToCurrent(): void { + if (getAPIProvider() !== 'firstParty') { + return + } + + if (!isLegacyModelRemapEnabled()) { + return + } + + const model = getSettingsForSource('userSettings')?.model + if ( + model !== 'claude-opus-4-20250514' && + model !== 'claude-opus-4-1-20250805' && + model !== 'claude-opus-4-0' && + model !== 'claude-opus-4-1' + ) { + return + } + + updateSettingsForSource('userSettings', { model: 'opus' }) + saveGlobalConfig(current => ({ + ...current, + legacyOpusMigrationTimestamp: Date.now(), + })) + logEvent('tengu_legacy_opus_migration', { + from_model: + model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + }) +} diff --git a/migrations/migrateOpusToOpus1m.ts b/migrations/migrateOpusToOpus1m.ts new file mode 100644 index 0000000..e065e19 --- /dev/null +++ b/migrations/migrateOpusToOpus1m.ts @@ -0,0 +1,43 @@ +import { logEvent } from '../services/analytics/index.js' +import { + getDefaultMainLoopModelSetting, + isOpus1mMergeEnabled, + parseUserSpecifiedModel, +} from '../utils/model/model.js' +import { + getSettingsForSource, + updateSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * Migrate users with 'opus' pinned in their settings to 'opus[1m]' when they + * are eligible for the merged Opus 1M experience (Max/Team Premium on 1P). + * + * CLI invocations with --model opus are unaffected: that flag is a runtime + * override and does not touch userSettings, so it continues to use plain Opus. + * + * Pro subscribers are skipped — they retain separate Opus and Opus 1M options. + * 3P users are skipped — their model strings are full model IDs, not aliases. + * + * Idempotent: only writes if userSettings.model is exactly 'opus'. + */ +export function migrateOpusToOpus1m(): void { + if (!isOpus1mMergeEnabled()) { + return + } + + const model = getSettingsForSource('userSettings')?.model + if (model !== 'opus') { + return + } + + const migrated = 'opus[1m]' + const modelToSet = + parseUserSpecifiedModel(migrated) === + parseUserSpecifiedModel(getDefaultMainLoopModelSetting()) + ? undefined + : migrated + updateSettingsForSource('userSettings', { model: modelToSet }) + + logEvent('tengu_opus_to_opus1m_migration', {}) +} diff --git a/migrations/migrateReplBridgeEnabledToRemoteControlAtStartup.ts b/migrations/migrateReplBridgeEnabledToRemoteControlAtStartup.ts new file mode 100644 index 0000000..efda014 --- /dev/null +++ b/migrations/migrateReplBridgeEnabledToRemoteControlAtStartup.ts @@ -0,0 +1,22 @@ +import { saveGlobalConfig } from '../utils/config.js' + +/** + * Migrate the `replBridgeEnabled` config key to `remoteControlAtStartup`. + * + * The old key was an implementation detail that leaked into user-facing config. + * This migration copies the value to the new key and removes the old one. + * Idempotent — only acts when the old key exists and the new one doesn't. + */ +export function migrateReplBridgeEnabledToRemoteControlAtStartup(): void { + saveGlobalConfig(prev => { + // The old key is no longer in the GlobalConfig type, so access it via + // an untyped cast. Only migrate if the old key exists and the new key + // hasn't been set yet. + const oldValue = (prev as Record)['replBridgeEnabled'] + if (oldValue === undefined) return prev + if (prev.remoteControlAtStartup !== undefined) return prev + const next = { ...prev, remoteControlAtStartup: Boolean(oldValue) } + delete (next as Record)['replBridgeEnabled'] + return next + }) +} diff --git a/migrations/migrateSonnet1mToSonnet45.ts b/migrations/migrateSonnet1mToSonnet45.ts new file mode 100644 index 0000000..f293638 --- /dev/null +++ b/migrations/migrateSonnet1mToSonnet45.ts @@ -0,0 +1,48 @@ +import { + getMainLoopModelOverride, + setMainLoopModelOverride, +} from '../bootstrap/state.js' +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' +import { + getSettingsForSource, + updateSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * Migrate users who had "sonnet[1m]" saved to the explicit "sonnet-4-5-20250929[1m]". + * + * The "sonnet" alias now resolves to Sonnet 4.6, so users who previously set + * "sonnet[1m]" (targeting Sonnet 4.5 with 1M context) need to be pinned to the + * explicit version to preserve their intended model. + * + * This is needed because Sonnet 4.6 1M was offered to a different group of users than + * Sonnet 4.5 1M, so we needed to pin existing sonnet[1m] users to Sonnet 4.5 1M. + * + * Reads from userSettings specifically (not merged settings) so we don't + * promote a project-scoped "sonnet[1m]" to the global default. Runs once, + * tracked by a completion flag in global config. + */ +export function migrateSonnet1mToSonnet45(): void { + const config = getGlobalConfig() + if (config.sonnet1m45MigrationComplete) { + return + } + + const model = getSettingsForSource('userSettings')?.model + if (model === 'sonnet[1m]') { + updateSettingsForSource('userSettings', { + model: 'sonnet-4-5-20250929[1m]', + }) + } + + // Also migrate the in-memory override if already set + const override = getMainLoopModelOverride() + if (override === 'sonnet[1m]') { + setMainLoopModelOverride('sonnet-4-5-20250929[1m]') + } + + saveGlobalConfig(current => ({ + ...current, + sonnet1m45MigrationComplete: true, + })) +} diff --git a/migrations/migrateSonnet45ToSonnet46.ts b/migrations/migrateSonnet45ToSonnet46.ts new file mode 100644 index 0000000..bfbfcef --- /dev/null +++ b/migrations/migrateSonnet45ToSonnet46.ts @@ -0,0 +1,67 @@ +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from '../services/analytics/index.js' +import { + isMaxSubscriber, + isProSubscriber, + isTeamPremiumSubscriber, +} from '../utils/auth.js' +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' +import { getAPIProvider } from '../utils/model/providers.js' +import { + getSettingsForSource, + updateSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * Migrate Pro/Max/Team Premium first-party users off explicit Sonnet 4.5 + * model strings to the 'sonnet' alias (which now resolves to Sonnet 4.6). + * + * Users may have been pinned to explicit Sonnet 4.5 strings by: + * - The earlier migrateSonnet1mToSonnet45 migration (sonnet[1m] → explicit 4.5[1m]) + * - Manually selecting it via /model + * + * Reads userSettings specifically (not merged) so we only migrate what /model + * wrote — project/local pins are left alone. + * Idempotent: only writes if userSettings.model matches a Sonnet 4.5 string. + */ +export function migrateSonnet45ToSonnet46(): void { + if (getAPIProvider() !== 'firstParty') { + return + } + + if (!isProSubscriber() && !isMaxSubscriber() && !isTeamPremiumSubscriber()) { + return + } + + const model = getSettingsForSource('userSettings')?.model + if ( + model !== 'claude-sonnet-4-5-20250929' && + model !== 'claude-sonnet-4-5-20250929[1m]' && + model !== 'sonnet-4-5-20250929' && + model !== 'sonnet-4-5-20250929[1m]' + ) { + return + } + + const has1m = model.endsWith('[1m]') + updateSettingsForSource('userSettings', { + model: has1m ? 'sonnet[1m]' : 'sonnet', + }) + + // Skip notification for brand-new users — they never experienced the old default + const config = getGlobalConfig() + if (config.numStartups > 1) { + saveGlobalConfig(current => ({ + ...current, + sonnet45To46MigrationTimestamp: Date.now(), + })) + } + + logEvent('tengu_sonnet45_to_46_migration', { + from_model: + model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + has_1m: has1m, + }) +} diff --git a/migrations/resetAutoModeOptInForDefaultOffer.ts b/migrations/resetAutoModeOptInForDefaultOffer.ts new file mode 100644 index 0000000..bc0c78a --- /dev/null +++ b/migrations/resetAutoModeOptInForDefaultOffer.ts @@ -0,0 +1,51 @@ +import { feature } from 'bun:bundle' +import { logEvent } from 'src/services/analytics/index.js' +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' +import { logError } from '../utils/log.js' +import { getAutoModeEnabledState } from '../utils/permissions/permissionSetup.js' +import { + getSettingsForSource, + updateSettingsForSource, +} from '../utils/settings/settings.js' + +/** + * One-shot migration: clear skipAutoPermissionPrompt for users who accepted + * the old 2-option AutoModeOptInDialog but don't have auto as their default. + * Re-surfaces the dialog so they see the new "make it my default mode" option. + * Guard lives in GlobalConfig (~/.claude.json), not settings.json, so it + * survives settings resets and doesn't re-arm itself. + * + * Only runs when tengu_auto_mode_config.enabled === 'enabled'. For 'opt-in' + * users, clearing skipAutoPermissionPrompt would remove auto from the carousel + * (permissionSetup.ts:988) — the dialog would become unreachable and the + * migration would defeat itself. In practice the ~40 target ants are all + * 'enabled' (they reached the old dialog via bare Shift+Tab, which requires + * 'enabled'), but the guard makes it safe regardless. + */ +export function resetAutoModeOptInForDefaultOffer(): void { + if (feature('TRANSCRIPT_CLASSIFIER')) { + const config = getGlobalConfig() + if (config.hasResetAutoModeOptInForDefaultOffer) return + if (getAutoModeEnabledState() !== 'enabled') return + + try { + const user = getSettingsForSource('userSettings') + if ( + user?.skipAutoPermissionPrompt && + user?.permissions?.defaultMode !== 'auto' + ) { + updateSettingsForSource('userSettings', { + skipAutoPermissionPrompt: undefined, + }) + logEvent('tengu_migrate_reset_auto_opt_in_for_default_offer', {}) + } + + saveGlobalConfig(c => { + if (c.hasResetAutoModeOptInForDefaultOffer) return c + return { ...c, hasResetAutoModeOptInForDefaultOffer: true } + }) + } catch (error) { + logError(new Error(`Failed to reset auto mode opt-in: ${error}`)) + } + } +} diff --git a/migrations/resetProToOpusDefault.ts b/migrations/resetProToOpusDefault.ts new file mode 100644 index 0000000..601872f --- /dev/null +++ b/migrations/resetProToOpusDefault.ts @@ -0,0 +1,51 @@ +import { logEvent } from 'src/services/analytics/index.js' +import { isProSubscriber } from '../utils/auth.js' +import { getGlobalConfig, saveGlobalConfig } from '../utils/config.js' +import { getAPIProvider } from '../utils/model/providers.js' +import { getSettings_DEPRECATED } from '../utils/settings/settings.js' + +export function resetProToOpusDefault(): void { + const config = getGlobalConfig() + + if (config.opusProMigrationComplete) { + return + } + + const apiProvider = getAPIProvider() + + // Pro users on firstParty get auto-migrated to Opus 4.5 default + if (apiProvider !== 'firstParty' || !isProSubscriber()) { + saveGlobalConfig(current => ({ + ...current, + opusProMigrationComplete: true, + })) + logEvent('tengu_reset_pro_to_opus_default', { skipped: true }) + return + } + + const settings = getSettings_DEPRECATED() + + // Only show notification if user was on default (no custom model setting) + if (settings?.model === undefined) { + const opusProMigrationTimestamp = Date.now() + saveGlobalConfig(current => ({ + ...current, + opusProMigrationComplete: true, + opusProMigrationTimestamp, + })) + logEvent('tengu_reset_pro_to_opus_default', { + skipped: false, + had_custom_model: false, + }) + } else { + // User has a custom model setting, just mark migration complete + saveGlobalConfig(current => ({ + ...current, + opusProMigrationComplete: true, + })) + logEvent('tengu_reset_pro_to_opus_default', { + skipped: false, + had_custom_model: true, + }) + } +} diff --git a/moreright/useMoreRight.tsx b/moreright/useMoreRight.tsx new file mode 100644 index 0000000..59961ac --- /dev/null +++ b/moreright/useMoreRight.tsx @@ -0,0 +1,26 @@ +// Stub for external builds — the real hook is internal only. +// +// Self-contained: no relative imports. Typecheck sees this file at +// scripts/external-stubs/src/moreright/ before overlay, where ../types/ +// would resolve to scripts/external-stubs/src/types/ (doesn't exist). + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type M = any; +export function useMoreRight(_args: { + enabled: boolean; + setMessages: (action: M[] | ((prev: M[]) => M[])) => void; + inputValue: string; + setInputValue: (s: string) => void; + setToolJSX: (args: M) => void; +}): { + onBeforeQuery: (input: string, all: M[], n: number) => Promise; + onTurnComplete: (all: M[], aborted: boolean) => Promise; + render: () => null; +} { + return { + onBeforeQuery: async () => true, + onTurnComplete: async () => {}, + render: () => null + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJNIiwidXNlTW9yZVJpZ2h0IiwiX2FyZ3MiLCJlbmFibGVkIiwic2V0TWVzc2FnZXMiLCJhY3Rpb24iLCJwcmV2IiwiaW5wdXRWYWx1ZSIsInNldElucHV0VmFsdWUiLCJzIiwic2V0VG9vbEpTWCIsImFyZ3MiLCJvbkJlZm9yZVF1ZXJ5IiwiaW5wdXQiLCJhbGwiLCJuIiwiUHJvbWlzZSIsIm9uVHVybkNvbXBsZXRlIiwiYWJvcnRlZCIsInJlbmRlciJdLCJzb3VyY2VzIjpbInVzZU1vcmVSaWdodC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLy8gU3R1YiBmb3IgZXh0ZXJuYWwgYnVpbGRzIOKAlCB0aGUgcmVhbCBob29rIGlzIGludGVybmFsIG9ubHkuXG4vL1xuLy8gU2VsZi1jb250YWluZWQ6IG5vIHJlbGF0aXZlIGltcG9ydHMuIFR5cGVjaGVjayBzZWVzIHRoaXMgZmlsZSBhdFxuLy8gc2NyaXB0cy9leHRlcm5hbC1zdHVicy9zcmMvbW9yZXJpZ2h0LyBiZWZvcmUgb3ZlcmxheSwgd2hlcmUgLi4vdHlwZXMvXG4vLyB3b3VsZCByZXNvbHZlIHRvIHNjcmlwdHMvZXh0ZXJuYWwtc3R1YnMvc3JjL3R5cGVzLyAoZG9lc24ndCBleGlzdCkuXG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZXhwbGljaXQtYW55XG50eXBlIE0gPSBhbnlcblxuZXhwb3J0IGZ1bmN0aW9uIHVzZU1vcmVSaWdodChfYXJnczoge1xuICBlbmFibGVkOiBib29sZWFuXG4gIHNldE1lc3NhZ2VzOiAoYWN0aW9uOiBNW10gfCAoKHByZXY6IE1bXSkgPT4gTVtdKSkgPT4gdm9pZFxuICBpbnB1dFZhbHVlOiBzdHJpbmdcbiAgc2V0SW5wdXRWYWx1ZTogKHM6IHN0cmluZykgPT4gdm9pZFxuICBzZXRUb29sSlNYOiAoYXJnczogTSkgPT4gdm9pZFxufSk6IHtcbiAgb25CZWZvcmVRdWVyeTogKGlucHV0OiBzdHJpbmcsIGFsbDogTVtdLCBuOiBudW1iZXIpID0+IFByb21pc2U8Ym9vbGVhbj5cbiAgb25UdXJuQ29tcGxldGU6IChhbGw6IE1bXSwgYWJvcnRlZDogYm9vbGVhbikgPT4gUHJvbWlzZTx2b2lkPlxuICByZW5kZXI6ICgpID0+IG51bGxcbn0ge1xuICByZXR1cm4ge1xuICAgIG9uQmVmb3JlUXVlcnk6IGFzeW5jICgpID0+IHRydWUsXG4gICAgb25UdXJuQ29tcGxldGU6IGFzeW5jICgpID0+IHt9LFxuICAgIHJlbmRlcjogKCkgPT4gbnVsbCxcbiAgfVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsS0FBS0EsQ0FBQyxHQUFHLEdBQUc7QUFFWixPQUFPLFNBQVNDLFlBQVlBLENBQUNDLEtBQUssRUFBRTtFQUNsQ0MsT0FBTyxFQUFFLE9BQU87RUFDaEJDLFdBQVcsRUFBRSxDQUFDQyxNQUFNLEVBQUVMLENBQUMsRUFBRSxHQUFHLENBQUMsQ0FBQ00sSUFBSSxFQUFFTixDQUFDLEVBQUUsRUFBRSxHQUFHQSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEdBQUcsSUFBSTtFQUN6RE8sVUFBVSxFQUFFLE1BQU07RUFDbEJDLGFBQWEsRUFBRSxDQUFDQyxDQUFDLEVBQUUsTUFBTSxFQUFFLEdBQUcsSUFBSTtFQUNsQ0MsVUFBVSxFQUFFLENBQUNDLElBQUksRUFBRVgsQ0FBQyxFQUFFLEdBQUcsSUFBSTtBQUMvQixDQUFDLENBQUMsRUFBRTtFQUNGWSxhQUFhLEVBQUUsQ0FBQ0MsS0FBSyxFQUFFLE1BQU0sRUFBRUMsR0FBRyxFQUFFZCxDQUFDLEVBQUUsRUFBRWUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxHQUFHQyxPQUFPLENBQUMsT0FBTyxDQUFDO0VBQ3ZFQyxjQUFjLEVBQUUsQ0FBQ0gsR0FBRyxFQUFFZCxDQUFDLEVBQUUsRUFBRWtCLE9BQU8sRUFBRSxPQUFPLEVBQUUsR0FBR0YsT0FBTyxDQUFDLElBQUksQ0FBQztFQUM3REcsTUFBTSxFQUFFLEdBQUcsR0FBRyxJQUFJO0FBQ3BCLENBQUMsQ0FBQztFQUNBLE9BQU87SUFDTFAsYUFBYSxFQUFFLE1BQUFBLENBQUEsS0FBWSxJQUFJO0lBQy9CSyxjQUFjLEVBQUUsTUFBQUEsQ0FBQSxLQUFZLENBQUMsQ0FBQztJQUM5QkUsTUFBTSxFQUFFQSxDQUFBLEtBQU07RUFDaEIsQ0FBQztBQUNIIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/native-ts/color-diff/index.ts b/native-ts/color-diff/index.ts new file mode 100644 index 0000000..d2757d3 --- /dev/null +++ b/native-ts/color-diff/index.ts @@ -0,0 +1,999 @@ +/** + * Pure TypeScript port of vendor/color-diff-src. + * + * The Rust version uses syntect+bat for syntax highlighting and the similar + * crate for word diffing. This port uses highlight.js (already a dep via + * cli-highlight) and the diff npm package's diffArrays. + * + * API matches vendor/color-diff-src/index.d.ts exactly so callers don't change. + * + * Key semantic differences from the native module: + * - Syntax highlighting uses highlight.js. Scope colors were measured from + * syntect's output so most tokens match, but hljs's grammar has gaps: + * plain identifiers and operators like `=` `:` aren't scoped, so they + * render in default fg instead of white/pink. Output structure (line + * numbers, markers, backgrounds, word-diff) is identical. + * - BAT_THEME env support is a stub: highlight.js has no bat theme set, so + * getSyntaxTheme always returns the default for the given Claude theme. + */ + +import { diffArrays } from 'diff' +import type * as hljsNamespace from 'highlight.js' +import { basename, extname } from 'path' + +// Lazy: defers loading highlight.js until first render. The full bundle +// registers 190+ language grammars at require time (~50MB, 100-200ms on +// macOS, several× that on Windows). With a top-level import, any caller +// chunk that reaches this module — including test/preload.ts via +// StructuredDiff.tsx → colorDiff.ts — pays that cost at module-eval time +// and carries the heap for the rest of the process. On Windows CI this +// pushed later tests in the same shard into GC-pause territory and a +// beforeEach/afterEach hook timeout (officialRegistry.test.ts, PR #24150). +// Same lazy pattern the NAPI wrapper used for dlopen. +type HLJSApi = typeof hljsNamespace +let cachedHljs: HLJSApi | null = null +function hljs(): HLJSApi { + if (cachedHljs) return cachedHljs + // eslint-disable-next-line @typescript-eslint/no-require-imports + const mod = require('highlight.js') + // highlight.js uses `export =` (CJS). Under bun/ESM the interop wraps it + // in .default; under node CJS the module IS the API. Check at runtime. + cachedHljs = 'default' in mod && mod.default ? mod.default : mod + return cachedHljs! +} + +import { stringWidth } from '../../ink/stringWidth.js' +import { logError } from '../../utils/log.js' + +// --------------------------------------------------------------------------- +// Public API types (match vendor/color-diff-src/index.d.ts) +// --------------------------------------------------------------------------- + +export type Hunk = { + oldStart: number + oldLines: number + newStart: number + newLines: number + lines: string[] +} + +export type SyntaxTheme = { + theme: string + source: string | null +} + +export type NativeModule = { + ColorDiff: typeof ColorDiff + ColorFile: typeof ColorFile + getSyntaxTheme: (themeName: string) => SyntaxTheme +} + +// --------------------------------------------------------------------------- +// Color / ANSI escape helpers +// --------------------------------------------------------------------------- + +type Color = { r: number; g: number; b: number; a: number } +type Style = { foreground: Color; background: Color } +type Block = [Style, string] +type ColorMode = 'truecolor' | 'color256' | 'ansi' + +const RESET = '\x1b[0m' +const DIM = '\x1b[2m' +const UNDIM = '\x1b[22m' + +function rgb(r: number, g: number, b: number): Color { + return { r, g, b, a: 255 } +} + +function ansiIdx(index: number): Color { + return { r: index, g: 0, b: 0, a: 0 } +} + +// Sentinel: a=1 means "terminal default" (matches bat convention) +const DEFAULT_BG: Color = { r: 0, g: 0, b: 0, a: 1 } + +function detectColorMode(theme: string): ColorMode { + if (theme.includes('ansi')) return 'ansi' + const ct = process.env.COLORTERM ?? '' + return ct === 'truecolor' || ct === '24bit' ? 'truecolor' : 'color256' +} + +// Port of ansi_colours::ansi256_from_rgb — approximates RGB to the xterm-256 +// palette (6x6x6 cube + 24 greys). Picks the perceptually closest index by +// comparing cube vs grey-ramp candidates, like the Rust crate. +const CUBE_LEVELS = [0, 95, 135, 175, 215, 255] +function ansi256FromRgb(r: number, g: number, b: number): number { + const q = (c: number) => + c < 48 ? 0 : c < 115 ? 1 : c < 155 ? 2 : c < 195 ? 3 : c < 235 ? 4 : 5 + const qr = q(r) + const qg = q(g) + const qb = q(b) + const cubeIdx = 16 + 36 * qr + 6 * qg + qb + // Grey ramp candidate (232-255, levels 8..238 step 10). Beyond the ramp's + // range the cube corner is the only option — ansi_colours snaps 248,248,242 + // to 231 (cube white), not 255 (ramp top). + const grey = Math.round((r + g + b) / 3) + if (grey < 5) return 16 + if (grey > 244 && qr === qg && qg === qb) return cubeIdx + const greyLevel = Math.max(0, Math.min(23, Math.round((grey - 8) / 10))) + const greyIdx = 232 + greyLevel + const greyRgb = 8 + greyLevel * 10 + const cr = CUBE_LEVELS[qr]! + const cg = CUBE_LEVELS[qg]! + const cb = CUBE_LEVELS[qb]! + const dCube = (r - cr) ** 2 + (g - cg) ** 2 + (b - cb) ** 2 + const dGrey = (r - greyRgb) ** 2 + (g - greyRgb) ** 2 + (b - greyRgb) ** 2 + return dGrey < dCube ? greyIdx : cubeIdx +} + +function colorToEscape(c: Color, fg: boolean, mode: ColorMode): string { + // alpha=0: palette index encoded in .r (bat's ansi-theme convention) + if (c.a === 0) { + const idx = c.r + if (idx < 8) return `\x1b[${(fg ? 30 : 40) + idx}m` + if (idx < 16) return `\x1b[${(fg ? 90 : 100) + (idx - 8)}m` + return `\x1b[${fg ? 38 : 48};5;${idx}m` + } + // alpha=1: terminal default + if (c.a === 1) return fg ? '\x1b[39m' : '\x1b[49m' + + const codeType = fg ? 38 : 48 + if (mode === 'truecolor') { + return `\x1b[${codeType};2;${c.r};${c.g};${c.b}m` + } + return `\x1b[${codeType};5;${ansi256FromRgb(c.r, c.g, c.b)}m` +} + +function asTerminalEscaped( + blocks: readonly Block[], + mode: ColorMode, + skipBackground: boolean, + dim: boolean, +): string { + let out = dim ? RESET + DIM : RESET + for (const [style, text] of blocks) { + out += colorToEscape(style.foreground, true, mode) + if (!skipBackground) { + out += colorToEscape(style.background, false, mode) + } + out += text + } + return out + RESET +} + +// --------------------------------------------------------------------------- +// Theme +// --------------------------------------------------------------------------- + +type Marker = '+' | '-' | ' ' + +type Theme = { + addLine: Color + addWord: Color + addDecoration: Color + deleteLine: Color + deleteWord: Color + deleteDecoration: Color + foreground: Color + background: Color + scopes: Record +} + +function defaultSyntaxThemeName(themeName: string): string { + if (themeName.includes('ansi')) return 'ansi' + if (themeName.includes('dark')) return 'Monokai Extended' + return 'GitHub' +} + +// highlight.js scope → syntect Monokai Extended foreground (measured from the +// Rust module's output so colors match the original exactly) +const MONOKAI_SCOPES: Record = { + keyword: rgb(249, 38, 114), + _storage: rgb(102, 217, 239), + built_in: rgb(166, 226, 46), + type: rgb(166, 226, 46), + literal: rgb(190, 132, 255), + number: rgb(190, 132, 255), + string: rgb(230, 219, 116), + title: rgb(166, 226, 46), + 'title.function': rgb(166, 226, 46), + 'title.class': rgb(166, 226, 46), + 'title.class.inherited': rgb(166, 226, 46), + params: rgb(253, 151, 31), + comment: rgb(117, 113, 94), + meta: rgb(117, 113, 94), + attr: rgb(166, 226, 46), + attribute: rgb(166, 226, 46), + variable: rgb(255, 255, 255), + 'variable.language': rgb(255, 255, 255), + property: rgb(255, 255, 255), + operator: rgb(249, 38, 114), + punctuation: rgb(248, 248, 242), + symbol: rgb(190, 132, 255), + regexp: rgb(230, 219, 116), + subst: rgb(248, 248, 242), +} + +// highlight.js scope → syntect GitHub-light foreground (measured from Rust) +const GITHUB_SCOPES: Record = { + keyword: rgb(167, 29, 93), + _storage: rgb(167, 29, 93), + built_in: rgb(0, 134, 179), + type: rgb(0, 134, 179), + literal: rgb(0, 134, 179), + number: rgb(0, 134, 179), + string: rgb(24, 54, 145), + title: rgb(121, 93, 163), + 'title.function': rgb(121, 93, 163), + 'title.class': rgb(0, 0, 0), + 'title.class.inherited': rgb(0, 0, 0), + params: rgb(0, 134, 179), + comment: rgb(150, 152, 150), + meta: rgb(150, 152, 150), + attr: rgb(0, 134, 179), + attribute: rgb(0, 134, 179), + variable: rgb(0, 134, 179), + 'variable.language': rgb(0, 134, 179), + property: rgb(0, 134, 179), + operator: rgb(167, 29, 93), + punctuation: rgb(51, 51, 51), + symbol: rgb(0, 134, 179), + regexp: rgb(24, 54, 145), + subst: rgb(51, 51, 51), +} + +// Keywords that syntect scopes as storage.type rather than keyword.control. +// highlight.js lumps these under "keyword"; we re-split so const/function/etc. +// get the cyan storage color instead of pink. +const STORAGE_KEYWORDS = new Set([ + 'const', + 'let', + 'var', + 'function', + 'class', + 'type', + 'interface', + 'enum', + 'namespace', + 'module', + 'def', + 'fn', + 'func', + 'struct', + 'trait', + 'impl', +]) + +const ANSI_SCOPES: Record = { + keyword: ansiIdx(13), + _storage: ansiIdx(14), + built_in: ansiIdx(14), + type: ansiIdx(14), + literal: ansiIdx(12), + number: ansiIdx(12), + string: ansiIdx(10), + title: ansiIdx(11), + 'title.function': ansiIdx(11), + 'title.class': ansiIdx(11), + comment: ansiIdx(8), + meta: ansiIdx(8), +} + +function buildTheme(themeName: string, mode: ColorMode): Theme { + const isDark = themeName.includes('dark') + const isAnsi = themeName.includes('ansi') + const isDaltonized = themeName.includes('daltonized') + const tc = mode === 'truecolor' + + if (isAnsi) { + return { + addLine: DEFAULT_BG, + addWord: DEFAULT_BG, + addDecoration: ansiIdx(10), + deleteLine: DEFAULT_BG, + deleteWord: DEFAULT_BG, + deleteDecoration: ansiIdx(9), + foreground: ansiIdx(7), + background: DEFAULT_BG, + scopes: ANSI_SCOPES, + } + } + + if (isDark) { + const fg = rgb(248, 248, 242) + const deleteLine = rgb(61, 1, 0) + const deleteWord = rgb(92, 2, 0) + const deleteDecoration = rgb(220, 90, 90) + if (isDaltonized) { + return { + addLine: tc ? rgb(0, 27, 41) : ansiIdx(17), + addWord: tc ? rgb(0, 48, 71) : ansiIdx(24), + addDecoration: rgb(81, 160, 200), + deleteLine, + deleteWord, + deleteDecoration, + foreground: fg, + background: DEFAULT_BG, + scopes: MONOKAI_SCOPES, + } + } + return { + addLine: tc ? rgb(2, 40, 0) : ansiIdx(22), + addWord: tc ? rgb(4, 71, 0) : ansiIdx(28), + addDecoration: rgb(80, 200, 80), + deleteLine, + deleteWord, + deleteDecoration, + foreground: fg, + background: DEFAULT_BG, + scopes: MONOKAI_SCOPES, + } + } + + // light + const fg = rgb(51, 51, 51) + const deleteLine = rgb(255, 220, 220) + const deleteWord = rgb(255, 199, 199) + const deleteDecoration = rgb(207, 34, 46) + if (isDaltonized) { + return { + addLine: rgb(219, 237, 255), + addWord: rgb(179, 217, 255), + addDecoration: rgb(36, 87, 138), + deleteLine, + deleteWord, + deleteDecoration, + foreground: fg, + background: DEFAULT_BG, + scopes: GITHUB_SCOPES, + } + } + return { + addLine: rgb(220, 255, 220), + addWord: rgb(178, 255, 178), + addDecoration: rgb(36, 138, 61), + deleteLine, + deleteWord, + deleteDecoration, + foreground: fg, + background: DEFAULT_BG, + scopes: GITHUB_SCOPES, + } +} + +function defaultStyle(theme: Theme): Style { + return { foreground: theme.foreground, background: theme.background } +} + +function lineBackground(marker: Marker, theme: Theme): Color { + switch (marker) { + case '+': + return theme.addLine + case '-': + return theme.deleteLine + case ' ': + return theme.background + } +} + +function wordBackground(marker: Marker, theme: Theme): Color { + switch (marker) { + case '+': + return theme.addWord + case '-': + return theme.deleteWord + case ' ': + return theme.background + } +} + +function decorationColor(marker: Marker, theme: Theme): Color { + switch (marker) { + case '+': + return theme.addDecoration + case '-': + return theme.deleteDecoration + case ' ': + return theme.foreground + } +} + +// --------------------------------------------------------------------------- +// Syntax highlighting via highlight.js +// --------------------------------------------------------------------------- + +// hljs 10.x uses `kind`; 11.x uses `scope`. Handle both. +type HljsNode = { + scope?: string + kind?: string + children: (HljsNode | string)[] +} + +// Filename-based and extension-based language detection (approximates bat's +// SyntaxMapping + syntect's find_syntax_by_extension) +const FILENAME_LANGS: Record = { + Dockerfile: 'dockerfile', + Makefile: 'makefile', + Rakefile: 'ruby', + Gemfile: 'ruby', + CMakeLists: 'cmake', +} + +function detectLanguage( + filePath: string, + firstLine: string | null, +): string | null { + const base = basename(filePath) + const ext = extname(filePath).slice(1) + + // Filename-based lookup (handles Dockerfile, Makefile, CMakeLists.txt, etc.) + const stem = base.split('.')[0] ?? '' + const byName = FILENAME_LANGS[base] ?? FILENAME_LANGS[stem] + if (byName && hljs().getLanguage(byName)) return byName + if (ext) { + const lang = hljs().getLanguage(ext) + if (lang) return ext + } + // Shebang / first-line detection (strip UTF-8 BOM) + if (firstLine) { + const line = firstLine.startsWith('\ufeff') ? firstLine.slice(1) : firstLine + if (line.startsWith('#!')) { + if (line.includes('bash') || line.includes('/sh')) return 'bash' + if (line.includes('python')) return 'python' + if (line.includes('node')) return 'javascript' + if (line.includes('ruby')) return 'ruby' + if (line.includes('perl')) return 'perl' + } + if (line.startsWith(' 0xffff ? 2 : 1 + tokens.push(text.slice(i, i + len)) + i += len + } + } + return tokens +} + +function findAdjacentPairs(markers: Marker[]): [number, number][] { + const pairs: [number, number][] = [] + let i = 0 + while (i < markers.length) { + if (markers[i] === '-') { + const delStart = i + let delEnd = i + while (delEnd < markers.length && markers[delEnd] === '-') delEnd++ + let addEnd = delEnd + while (addEnd < markers.length && markers[addEnd] === '+') addEnd++ + const delCount = delEnd - delStart + const addCount = addEnd - delEnd + if (delCount > 0 && addCount > 0) { + const n = Math.min(delCount, addCount) + for (let k = 0; k < n; k++) { + pairs.push([delStart + k, delEnd + k]) + } + i = addEnd + } else { + i = delEnd + } + } else { + i++ + } + } + return pairs +} + +function wordDiffStrings(oldStr: string, newStr: string): [Range[], Range[]] { + const oldTokens = tokenize(oldStr) + const newTokens = tokenize(newStr) + const ops = diffArrays(oldTokens, newTokens) + + const totalLen = oldStr.length + newStr.length + let changedLen = 0 + const oldRanges: Range[] = [] + const newRanges: Range[] = [] + let oldOff = 0 + let newOff = 0 + + for (const op of ops) { + const len = op.value.reduce((s, t) => s + t.length, 0) + if (op.removed) { + changedLen += len + oldRanges.push({ start: oldOff, end: oldOff + len }) + oldOff += len + } else if (op.added) { + changedLen += len + newRanges.push({ start: newOff, end: newOff + len }) + newOff += len + } else { + oldOff += len + newOff += len + } + } + + if (totalLen > 0 && changedLen / totalLen > CHANGE_THRESHOLD) { + return [[], []] + } + return [oldRanges, newRanges] +} + +// --------------------------------------------------------------------------- +// Highlight (per-line transform pipeline) +// --------------------------------------------------------------------------- + +type Highlight = { + marker: Marker | null + lineNumber: number + lines: Block[][] +} + +function removeNewlines(h: Highlight): void { + h.lines = h.lines.map(line => + line.flatMap(([style, text]) => + text + .split('\n') + .filter(p => p.length > 0) + .map((p): Block => [style, p]), + ), + ) +} + +function charWidth(ch: string): number { + return stringWidth(ch) +} + +function wrapText(h: Highlight, width: number, theme: Theme): void { + const newLines: Block[][] = [] + for (const line of h.lines) { + const queue: Block[] = line.slice() + let cur: Block[] = [] + let curW = 0 + while (queue.length > 0) { + const [style, text] = queue.shift()! + const tw = stringWidth(text) + if (curW + tw <= width) { + cur.push([style, text]) + curW += tw + } else { + const remaining = width - curW + let bytePos = 0 + let accW = 0 + // iterate by codepoint + for (const ch of text) { + const cw = charWidth(ch) + if (accW + cw > remaining) break + accW += cw + bytePos += ch.length + } + if (bytePos === 0) { + if (curW === 0) { + // Fresh line and first char still doesn't fit — force one codepoint + // to guarantee forward progress (overflows, but prevents infinite loop) + const firstCp = text.codePointAt(0)! + bytePos = firstCp > 0xffff ? 2 : 1 + } else { + // Line has content and next char doesn't fit — finish this line, + // re-queue the whole block for a fresh line + newLines.push(cur) + queue.unshift([style, text]) + cur = [] + curW = 0 + continue + } + } + cur.push([style, text.slice(0, bytePos)]) + newLines.push(cur) + queue.unshift([style, text.slice(bytePos)]) + cur = [] + curW = 0 + } + } + newLines.push(cur) + } + h.lines = newLines + + // Pad changed lines so background extends to edge + if (h.marker && h.marker !== ' ') { + const bg = lineBackground(h.marker, theme) + const padStyle: Style = { foreground: theme.foreground, background: bg } + for (const line of h.lines) { + const curW = line.reduce((s, [, t]) => s + stringWidth(t), 0) + if (curW < width) { + line.push([padStyle, ' '.repeat(width - curW)]) + } + } + } +} + +function addLineNumber( + h: Highlight, + theme: Theme, + maxDigits: number, + fullDim: boolean, +): void { + const style: Style = { + foreground: h.marker ? decorationColor(h.marker, theme) : theme.foreground, + background: h.marker ? lineBackground(h.marker, theme) : theme.background, + } + const shouldDim = h.marker === null || h.marker === ' ' + for (let i = 0; i < h.lines.length; i++) { + const prefix = + i === 0 + ? ` ${String(h.lineNumber).padStart(maxDigits)} ` + : ' '.repeat(maxDigits + 2) + const wrapped = shouldDim && !fullDim ? `${DIM}${prefix}${UNDIM}` : prefix + h.lines[i]!.unshift([style, wrapped]) + } +} + +function addMarker(h: Highlight, theme: Theme): void { + if (!h.marker) return + const style: Style = { + foreground: decorationColor(h.marker, theme), + background: lineBackground(h.marker, theme), + } + for (const line of h.lines) { + line.unshift([style, h.marker]) + } +} + +function dimContent(h: Highlight): void { + for (const line of h.lines) { + if (line.length > 0) { + line[0]![1] = DIM + line[0]![1] + const last = line.length - 1 + line[last]![1] = line[last]![1] + UNDIM + } + } +} + +function applyBackground(h: Highlight, theme: Theme, ranges: Range[]): void { + if (!h.marker) return + const lineBg = lineBackground(h.marker, theme) + const wordBg = wordBackground(h.marker, theme) + + let rangeIdx = 0 + let byteOff = 0 + for (let li = 0; li < h.lines.length; li++) { + const newLine: Block[] = [] + for (const [style, text] of h.lines[li]!) { + const textStart = byteOff + const textEnd = byteOff + text.length + + while (rangeIdx < ranges.length && ranges[rangeIdx]!.end <= textStart) { + rangeIdx++ + } + if (rangeIdx >= ranges.length) { + newLine.push([{ ...style, background: lineBg }, text]) + byteOff = textEnd + continue + } + + let remaining = text + let pos = textStart + while (remaining.length > 0 && rangeIdx < ranges.length) { + const r = ranges[rangeIdx]! + const inRange = pos >= r.start && pos < r.end + let next: number + if (inRange) { + next = Math.min(r.end, textEnd) + } else if (r.start > pos && r.start < textEnd) { + next = r.start + } else { + next = textEnd + } + const segLen = next - pos + const seg = remaining.slice(0, segLen) + newLine.push([{ ...style, background: inRange ? wordBg : lineBg }, seg]) + remaining = remaining.slice(segLen) + pos = next + if (pos >= r.end) rangeIdx++ + } + if (remaining.length > 0) { + newLine.push([{ ...style, background: lineBg }, remaining]) + } + byteOff = textEnd + } + h.lines[li] = newLine + } +} + +function intoLines( + h: Highlight, + dim: boolean, + skipBg: boolean, + mode: ColorMode, +): string[] { + return h.lines.map(line => asTerminalEscaped(line, mode, skipBg, dim)) +} + +// --------------------------------------------------------------------------- +// Public API +// --------------------------------------------------------------------------- + +function maxLineNumber(hunk: Hunk): number { + const oldEnd = Math.max(0, hunk.oldStart + hunk.oldLines - 1) + const newEnd = Math.max(0, hunk.newStart + hunk.newLines - 1) + return Math.max(oldEnd, newEnd) +} + +function parseMarker(s: string): Marker { + return s === '+' || s === '-' ? s : ' ' +} + +export class ColorDiff { + private hunk: Hunk + private filePath: string + private firstLine: string | null + private prefixContent: string | null + + constructor( + hunk: Hunk, + firstLine: string | null, + filePath: string, + prefixContent?: string | null, + ) { + this.hunk = hunk + this.filePath = filePath + this.firstLine = firstLine + this.prefixContent = prefixContent ?? null + } + + render(themeName: string, width: number, dim: boolean): string[] | null { + const mode = detectColorMode(themeName) + const theme = buildTheme(themeName, mode) + const lang = detectLanguage(this.filePath, this.firstLine) + const hlState = { lang, stack: null } + + // Warm highlighter with prefix lines (highlight.js is stateless per call, + // so this is a no-op for now — preserved for API parity) + void this.prefixContent + + const maxDigits = String(maxLineNumber(this.hunk)).length + let oldLine = this.hunk.oldStart + let newLine = this.hunk.newStart + const effectiveWidth = Math.max(1, width - maxDigits - 2 - 1) + + // First pass: assign markers + line numbers + type Entry = { lineNumber: number; marker: Marker; code: string } + const entries: Entry[] = this.hunk.lines.map(rawLine => { + const marker = parseMarker(rawLine.slice(0, 1)) + const code = rawLine.slice(1) + let lineNumber: number + switch (marker) { + case '+': + lineNumber = newLine++ + break + case '-': + lineNumber = oldLine++ + break + case ' ': + lineNumber = newLine + oldLine++ + newLine++ + break + } + return { lineNumber, marker, code } + }) + + // Word-diff ranges (skip when dim — too loud) + const ranges: Range[][] = entries.map(() => []) + if (!dim) { + const markers = entries.map(e => e.marker) + for (const [delIdx, addIdx] of findAdjacentPairs(markers)) { + const [delR, addR] = wordDiffStrings( + entries[delIdx]!.code, + entries[addIdx]!.code, + ) + ranges[delIdx] = delR + ranges[addIdx] = addR + } + } + + // Second pass: highlight + transform pipeline + const out: string[] = [] + for (let i = 0; i < entries.length; i++) { + const { lineNumber, marker, code } = entries[i]! + const tokens: Block[] = + marker === '-' + ? [[defaultStyle(theme), code]] + : highlightLine(hlState, code, theme) + + const h: Highlight = { marker, lineNumber, lines: [tokens] } + removeNewlines(h) + applyBackground(h, theme, ranges[i]!) + wrapText(h, effectiveWidth, theme) + if (mode === 'ansi' && marker === '-') { + dimContent(h) + } + addMarker(h, theme) + addLineNumber(h, theme, maxDigits, dim) + out.push(...intoLines(h, dim, false, mode)) + } + return out + } +} + +export class ColorFile { + private code: string + private filePath: string + + constructor(code: string, filePath: string) { + this.code = code + this.filePath = filePath + } + + render(themeName: string, width: number, dim: boolean): string[] | null { + const mode = detectColorMode(themeName) + const theme = buildTheme(themeName, mode) + const lines = this.code.split('\n') + // Rust .lines() drops trailing empty line from trailing \n + if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop() + const firstLine = lines[0] ?? null + const lang = detectLanguage(this.filePath, firstLine) + const hlState = { lang, stack: null } + + const maxDigits = String(lines.length).length + const effectiveWidth = Math.max(1, width - maxDigits - 2) + + const out: string[] = [] + for (let i = 0; i < lines.length; i++) { + const tokens = highlightLine(hlState, lines[i]!, theme) + const h: Highlight = { marker: null, lineNumber: i + 1, lines: [tokens] } + removeNewlines(h) + wrapText(h, effectiveWidth, theme) + addLineNumber(h, theme, maxDigits, dim) + out.push(...intoLines(h, dim, true, mode)) + } + return out + } +} + +export function getSyntaxTheme(themeName: string): SyntaxTheme { + // highlight.js has no bat theme set, so env vars can't select alternate + // syntect themes. We still report the env var if set, for diagnostics. + const envTheme = + process.env.CLAUDE_CODE_SYNTAX_HIGHLIGHT ?? process.env.BAT_THEME + void envTheme + return { theme: defaultSyntaxThemeName(themeName), source: null } +} + +// Lazy loader to match vendor/color-diff-src/index.ts API +let cachedModule: NativeModule | null = null + +export function getNativeModule(): NativeModule | null { + if (cachedModule) return cachedModule + cachedModule = { ColorDiff, ColorFile, getSyntaxTheme } + return cachedModule +} + +export type { ColorDiff as ColorDiffClass, ColorFile as ColorFileClass } + +// Exported for testing +export const __test = { + tokenize, + findAdjacentPairs, + wordDiffStrings, + ansi256FromRgb, + colorToEscape, + detectColorMode, + detectLanguage, +} diff --git a/native-ts/file-index/index.ts b/native-ts/file-index/index.ts new file mode 100644 index 0000000..11e4dbd --- /dev/null +++ b/native-ts/file-index/index.ts @@ -0,0 +1,370 @@ +/** + * Pure-TypeScript port of vendor/file-index-src (Rust NAPI module). + * + * The native module wraps nucleo (https://github.com/helix-editor/nucleo) for + * high-performance fuzzy file searching. This port reimplements the same API + * and scoring behavior without native dependencies. + * + * Key API: + * new FileIndex() + * .loadFromFileList(fileList: string[]): void — dedupe + index paths + * .search(query: string, limit: number): SearchResult[] + * + * Score semantics: lower = better. Score is position-in-results / result-count, + * so the best match is 0.0. Paths containing "test" get a 1.05× penalty (capped + * at 1.0) so non-test files rank slightly higher. + */ + +export type SearchResult = { + path: string + score: number +} + +// nucleo-style scoring constants (approximating fzf-v2 / nucleo bonuses) +const SCORE_MATCH = 16 +const BONUS_BOUNDARY = 8 +const BONUS_CAMEL = 6 +const BONUS_CONSECUTIVE = 4 +const BONUS_FIRST_CHAR = 8 +const PENALTY_GAP_START = 3 +const PENALTY_GAP_EXTENSION = 1 + +const TOP_LEVEL_CACHE_LIMIT = 100 +const MAX_QUERY_LEN = 64 +// Yield to event loop after this many ms of sync work. Chunk sizes are +// time-based (not count-based) so slow machines get smaller chunks and +// stay responsive — 5k paths is ~2ms on M-series but could be 15ms+ on +// older Windows hardware. +const CHUNK_MS = 4 + +// Reusable buffer: records where each needle char matched during the indexOf scan +const posBuf = new Int32Array(MAX_QUERY_LEN) + +export class FileIndex { + private paths: string[] = [] + private lowerPaths: string[] = [] + private charBits: Int32Array = new Int32Array(0) + private pathLens: Uint16Array = new Uint16Array(0) + private topLevelCache: SearchResult[] | null = null + // During async build, tracks how many paths have bitmap/lowerPath filled. + // search() uses this to search the ready prefix while build continues. + private readyCount = 0 + + /** + * Load paths from an array of strings. + * This is the main way to populate the index — ripgrep collects files, we just search them. + * Automatically deduplicates paths. + */ + loadFromFileList(fileList: string[]): void { + // Deduplicate and filter empty strings (matches Rust HashSet behavior) + const seen = new Set() + const paths: string[] = [] + for (const line of fileList) { + if (line.length > 0 && !seen.has(line)) { + seen.add(line) + paths.push(line) + } + } + + this.buildIndex(paths) + } + + /** + * Async variant: yields to the event loop every ~8–12k paths so large + * indexes (270k+ files) don't block the main thread for >10ms at a time. + * Identical result to loadFromFileList. + * + * Returns { queryable, done }: + * - queryable: resolves as soon as the first chunk is indexed (search + * returns partial results). For a 270k-path list this is ~5–10ms of + * sync work after the paths array is available. + * - done: resolves when the entire index is built. + */ + loadFromFileListAsync(fileList: string[]): { + queryable: Promise + done: Promise + } { + let markQueryable: () => void = () => {} + const queryable = new Promise(resolve => { + markQueryable = resolve + }) + const done = this.buildAsync(fileList, markQueryable) + return { queryable, done } + } + + private async buildAsync( + fileList: string[], + markQueryable: () => void, + ): Promise { + const seen = new Set() + const paths: string[] = [] + let chunkStart = performance.now() + for (let i = 0; i < fileList.length; i++) { + const line = fileList[i]! + if (line.length > 0 && !seen.has(line)) { + seen.add(line) + paths.push(line) + } + // Check every 256 iterations to amortize performance.now() overhead + if ((i & 0xff) === 0xff && performance.now() - chunkStart > CHUNK_MS) { + await yieldToEventLoop() + chunkStart = performance.now() + } + } + + this.resetArrays(paths) + + chunkStart = performance.now() + let firstChunk = true + for (let i = 0; i < paths.length; i++) { + this.indexPath(i) + if ((i & 0xff) === 0xff && performance.now() - chunkStart > CHUNK_MS) { + this.readyCount = i + 1 + if (firstChunk) { + markQueryable() + firstChunk = false + } + await yieldToEventLoop() + chunkStart = performance.now() + } + } + this.readyCount = paths.length + markQueryable() + } + + private buildIndex(paths: string[]): void { + this.resetArrays(paths) + for (let i = 0; i < paths.length; i++) { + this.indexPath(i) + } + this.readyCount = paths.length + } + + private resetArrays(paths: string[]): void { + const n = paths.length + this.paths = paths + this.lowerPaths = new Array(n) + this.charBits = new Int32Array(n) + this.pathLens = new Uint16Array(n) + this.readyCount = 0 + this.topLevelCache = computeTopLevelEntries(paths, TOP_LEVEL_CACHE_LIMIT) + } + + // Precompute: lowercase, a–z bitmap, length. Bitmap gives O(1) rejection + // of paths missing any needle letter (89% survival for broad queries like + // "test" → still a 10%+ free win; 90%+ rejection for rare chars). + private indexPath(i: number): void { + const lp = this.paths[i]!.toLowerCase() + this.lowerPaths[i] = lp + const len = lp.length + this.pathLens[i] = len + let bits = 0 + for (let j = 0; j < len; j++) { + const c = lp.charCodeAt(j) + if (c >= 97 && c <= 122) bits |= 1 << (c - 97) + } + this.charBits[i] = bits + } + + /** + * Search for files matching the query using fuzzy matching. + * Returns top N results sorted by match score. + */ + search(query: string, limit: number): SearchResult[] { + if (limit <= 0) return [] + if (query.length === 0) { + if (this.topLevelCache) { + return this.topLevelCache.slice(0, limit) + } + return [] + } + + // Smart case: lowercase query → case-insensitive; any uppercase → case-sensitive + const caseSensitive = query !== query.toLowerCase() + const needle = caseSensitive ? query : query.toLowerCase() + const nLen = Math.min(needle.length, MAX_QUERY_LEN) + const needleChars: string[] = new Array(nLen) + let needleBitmap = 0 + for (let j = 0; j < nLen; j++) { + const ch = needle.charAt(j) + needleChars[j] = ch + const cc = ch.charCodeAt(0) + if (cc >= 97 && cc <= 122) needleBitmap |= 1 << (cc - 97) + } + + // Upper bound on score assuming every match gets the max boundary bonus. + // Used to reject paths whose gap penalties alone make them unable to beat + // the current top-k threshold, before the charCodeAt-heavy boundary pass. + const scoreCeiling = + nLen * (SCORE_MATCH + BONUS_BOUNDARY) + BONUS_FIRST_CHAR + 32 + + // Top-k: maintain a sorted-ascending array of the best `limit` matches. + // Avoids O(n log n) sort of all matches when we only need `limit` of them. + const topK: { path: string; fuzzScore: number }[] = [] + let threshold = -Infinity + + const { paths, lowerPaths, charBits, pathLens, readyCount } = this + + outer: for (let i = 0; i < readyCount; i++) { + // O(1) bitmap reject: path must contain every letter in the needle + if ((charBits[i]! & needleBitmap) !== needleBitmap) continue + + const haystack = caseSensitive ? paths[i]! : lowerPaths[i]! + + // Fused indexOf scan: find positions (SIMD-accelerated in JSC/V8) AND + // accumulate gap/consecutive terms inline. The greedy-earliest positions + // found here are identical to what the charCodeAt scorer would find, so + // we score directly from them — no second scan. + let pos = haystack.indexOf(needleChars[0]!) + if (pos === -1) continue + posBuf[0] = pos + let gapPenalty = 0 + let consecBonus = 0 + let prev = pos + for (let j = 1; j < nLen; j++) { + pos = haystack.indexOf(needleChars[j]!, prev + 1) + if (pos === -1) continue outer + posBuf[j] = pos + const gap = pos - prev - 1 + if (gap === 0) consecBonus += BONUS_CONSECUTIVE + else gapPenalty += PENALTY_GAP_START + gap * PENALTY_GAP_EXTENSION + prev = pos + } + + // Gap-bound reject: if the best-case score (all boundary bonuses) minus + // known gap penalties can't beat threshold, skip the boundary pass. + if ( + topK.length === limit && + scoreCeiling + consecBonus - gapPenalty <= threshold + ) { + continue + } + + // Boundary/camelCase scoring: check the char before each match position. + const path = paths[i]! + const hLen = pathLens[i]! + let score = nLen * SCORE_MATCH + consecBonus - gapPenalty + score += scoreBonusAt(path, posBuf[0]!, true) + for (let j = 1; j < nLen; j++) { + score += scoreBonusAt(path, posBuf[j]!, false) + } + score += Math.max(0, 32 - (hLen >> 2)) + + if (topK.length < limit) { + topK.push({ path, fuzzScore: score }) + if (topK.length === limit) { + topK.sort((a, b) => a.fuzzScore - b.fuzzScore) + threshold = topK[0]!.fuzzScore + } + } else if (score > threshold) { + let lo = 0 + let hi = topK.length + while (lo < hi) { + const mid = (lo + hi) >> 1 + if (topK[mid]!.fuzzScore < score) lo = mid + 1 + else hi = mid + } + topK.splice(lo, 0, { path, fuzzScore: score }) + topK.shift() + threshold = topK[0]!.fuzzScore + } + } + + // topK is ascending; reverse to descending (best first) + topK.sort((a, b) => b.fuzzScore - a.fuzzScore) + + const matchCount = topK.length + const denom = Math.max(matchCount, 1) + const results: SearchResult[] = new Array(matchCount) + + for (let i = 0; i < matchCount; i++) { + const path = topK[i]!.path + const positionScore = i / denom + const finalScore = path.includes('test') + ? Math.min(positionScore * 1.05, 1.0) + : positionScore + results[i] = { path, score: finalScore } + } + + return results + } +} + +/** + * Boundary/camelCase bonus for a match at position `pos` in the original-case + * path. `first` enables the start-of-string bonus (only for needle[0]). + */ +function scoreBonusAt(path: string, pos: number, first: boolean): number { + if (pos === 0) return first ? BONUS_FIRST_CHAR : 0 + const prevCh = path.charCodeAt(pos - 1) + if (isBoundary(prevCh)) return BONUS_BOUNDARY + if (isLower(prevCh) && isUpper(path.charCodeAt(pos))) return BONUS_CAMEL + return 0 +} + +function isBoundary(code: number): boolean { + // / \ - _ . space + return ( + code === 47 || // / + code === 92 || // \ + code === 45 || // - + code === 95 || // _ + code === 46 || // . + code === 32 // space + ) +} + +function isLower(code: number): boolean { + return code >= 97 && code <= 122 +} + +function isUpper(code: number): boolean { + return code >= 65 && code <= 90 +} + +export function yieldToEventLoop(): Promise { + return new Promise(resolve => setImmediate(resolve)) +} + +export { CHUNK_MS } + +/** + * Extract unique top-level path segments, sorted by (length asc, then alpha asc). + * Handles both Unix (/) and Windows (\) path separators. + * Mirrors FileIndex::compute_top_level_entries in lib.rs. + */ +function computeTopLevelEntries( + paths: string[], + limit: number, +): SearchResult[] { + const topLevel = new Set() + + for (const p of paths) { + // Split on first / or \ separator + let end = p.length + for (let i = 0; i < p.length; i++) { + const c = p.charCodeAt(i) + if (c === 47 || c === 92) { + end = i + break + } + } + const segment = p.slice(0, end) + if (segment.length > 0) { + topLevel.add(segment) + if (topLevel.size >= limit) break + } + } + + const sorted = Array.from(topLevel) + sorted.sort((a, b) => { + const lenDiff = a.length - b.length + if (lenDiff !== 0) return lenDiff + return a < b ? -1 : a > b ? 1 : 0 + }) + + return sorted.slice(0, limit).map(path => ({ path, score: 0.0 })) +} + +export default FileIndex +export type { FileIndex as FileIndexType } diff --git a/native-ts/yoga-layout/enums.ts b/native-ts/yoga-layout/enums.ts new file mode 100644 index 0000000..8cbb6ec --- /dev/null +++ b/native-ts/yoga-layout/enums.ts @@ -0,0 +1,134 @@ +/** + * Yoga enums — ported from yoga-layout/src/generated/YGEnums.ts + * Kept as `const` objects (not TS enums) per repo convention. + * Values match upstream exactly so callers don't change. + */ + +export const Align = { + Auto: 0, + FlexStart: 1, + Center: 2, + FlexEnd: 3, + Stretch: 4, + Baseline: 5, + SpaceBetween: 6, + SpaceAround: 7, + SpaceEvenly: 8, +} as const +export type Align = (typeof Align)[keyof typeof Align] + +export const BoxSizing = { + BorderBox: 0, + ContentBox: 1, +} as const +export type BoxSizing = (typeof BoxSizing)[keyof typeof BoxSizing] + +export const Dimension = { + Width: 0, + Height: 1, +} as const +export type Dimension = (typeof Dimension)[keyof typeof Dimension] + +export const Direction = { + Inherit: 0, + LTR: 1, + RTL: 2, +} as const +export type Direction = (typeof Direction)[keyof typeof Direction] + +export const Display = { + Flex: 0, + None: 1, + Contents: 2, +} as const +export type Display = (typeof Display)[keyof typeof Display] + +export const Edge = { + Left: 0, + Top: 1, + Right: 2, + Bottom: 3, + Start: 4, + End: 5, + Horizontal: 6, + Vertical: 7, + All: 8, +} as const +export type Edge = (typeof Edge)[keyof typeof Edge] + +export const Errata = { + None: 0, + StretchFlexBasis: 1, + AbsolutePositionWithoutInsetsExcludesPadding: 2, + AbsolutePercentAgainstInnerSize: 4, + All: 2147483647, + Classic: 2147483646, +} as const +export type Errata = (typeof Errata)[keyof typeof Errata] + +export const ExperimentalFeature = { + WebFlexBasis: 0, +} as const +export type ExperimentalFeature = + (typeof ExperimentalFeature)[keyof typeof ExperimentalFeature] + +export const FlexDirection = { + Column: 0, + ColumnReverse: 1, + Row: 2, + RowReverse: 3, +} as const +export type FlexDirection = (typeof FlexDirection)[keyof typeof FlexDirection] + +export const Gutter = { + Column: 0, + Row: 1, + All: 2, +} as const +export type Gutter = (typeof Gutter)[keyof typeof Gutter] + +export const Justify = { + FlexStart: 0, + Center: 1, + FlexEnd: 2, + SpaceBetween: 3, + SpaceAround: 4, + SpaceEvenly: 5, +} as const +export type Justify = (typeof Justify)[keyof typeof Justify] + +export const MeasureMode = { + Undefined: 0, + Exactly: 1, + AtMost: 2, +} as const +export type MeasureMode = (typeof MeasureMode)[keyof typeof MeasureMode] + +export const Overflow = { + Visible: 0, + Hidden: 1, + Scroll: 2, +} as const +export type Overflow = (typeof Overflow)[keyof typeof Overflow] + +export const PositionType = { + Static: 0, + Relative: 1, + Absolute: 2, +} as const +export type PositionType = (typeof PositionType)[keyof typeof PositionType] + +export const Unit = { + Undefined: 0, + Point: 1, + Percent: 2, + Auto: 3, +} as const +export type Unit = (typeof Unit)[keyof typeof Unit] + +export const Wrap = { + NoWrap: 0, + Wrap: 1, + WrapReverse: 2, +} as const +export type Wrap = (typeof Wrap)[keyof typeof Wrap] diff --git a/native-ts/yoga-layout/index.ts b/native-ts/yoga-layout/index.ts new file mode 100644 index 0000000..49b9602 --- /dev/null +++ b/native-ts/yoga-layout/index.ts @@ -0,0 +1,2578 @@ +/** + * Pure-TypeScript port of yoga-layout (Meta's flexbox engine). + * + * This matches the `yoga-layout/load` API surface used by src/ink/layout/yoga.ts. + * The upstream C++ source is ~2500 lines in CalculateLayout.cpp alone; this port + * is a simplified single-pass flexbox implementation that covers the subset of + * features Ink actually uses: + * - flex-direction (row/column + reverse) + * - flex-grow / flex-shrink / flex-basis + * - align-items / align-self (stretch, flex-start, center, flex-end) + * - justify-content (all six values) + * - margin / padding / border / gap + * - width / height / min / max (point, percent, auto) + * - position: relative / absolute + * - display: flex / none + * - measure functions (for text nodes) + * + * Also implemented for spec parity (not used by Ink): + * - margin: auto (main + cross axis, overrides justify/align) + * - multi-pass flex clamping when children hit min/max constraints + * - flex-grow/shrink against container min/max when size is indefinite + * + * Also implemented for spec parity (not used by Ink): + * - flex-wrap: wrap / wrap-reverse (multi-line flex) + * - align-content (positions wrapped lines on cross axis) + * + * Also implemented for spec parity (not used by Ink): + * - display: contents (children lifted to grandparent, box removed) + * + * Also implemented for spec parity (not used by Ink): + * - baseline alignment (align-items/align-self: baseline) + * + * Not implemented (not used by Ink): + * - aspect-ratio + * - box-sizing: content-box + * - RTL direction (Ink always passes Direction.LTR) + * + * Upstream: https://github.com/facebook/yoga + */ + +import { + Align, + BoxSizing, + Dimension, + Direction, + Display, + Edge, + Errata, + ExperimentalFeature, + FlexDirection, + Gutter, + Justify, + MeasureMode, + Overflow, + PositionType, + Unit, + Wrap, +} from './enums.js' + +export { + Align, + BoxSizing, + Dimension, + Direction, + Display, + Edge, + Errata, + ExperimentalFeature, + FlexDirection, + Gutter, + Justify, + MeasureMode, + Overflow, + PositionType, + Unit, + Wrap, +} + +// -- +// Value types + +export type Value = { + unit: Unit + value: number +} + +const UNDEFINED_VALUE: Value = { unit: Unit.Undefined, value: NaN } +const AUTO_VALUE: Value = { unit: Unit.Auto, value: NaN } + +function pointValue(v: number): Value { + return { unit: Unit.Point, value: v } +} +function percentValue(v: number): Value { + return { unit: Unit.Percent, value: v } +} + +function resolveValue(v: Value, ownerSize: number): number { + switch (v.unit) { + case Unit.Point: + return v.value + case Unit.Percent: + return isNaN(ownerSize) ? NaN : (v.value * ownerSize) / 100 + default: + return NaN + } +} + +function isDefined(n: number): boolean { + return !isNaN(n) +} + +// NaN-safe equality for layout-cache input comparison +function sameFloat(a: number, b: number): boolean { + return a === b || (a !== a && b !== b) +} + +// -- +// Layout result (computed values) + +type Layout = { + left: number + top: number + width: number + height: number + // Computed per-edge values (resolved to physical edges) + border: [number, number, number, number] // left, top, right, bottom + padding: [number, number, number, number] + margin: [number, number, number, number] +} + +// -- +// Style (input values) + +type Style = { + direction: Direction + flexDirection: FlexDirection + justifyContent: Justify + alignItems: Align + alignSelf: Align + alignContent: Align + flexWrap: Wrap + overflow: Overflow + display: Display + positionType: PositionType + + flexGrow: number + flexShrink: number + flexBasis: Value + + // 9-edge arrays indexed by Edge enum + margin: Value[] + padding: Value[] + border: Value[] + position: Value[] + + // 3-gutter array indexed by Gutter enum + gap: Value[] + + width: Value + height: Value + minWidth: Value + minHeight: Value + maxWidth: Value + maxHeight: Value +} + +function defaultStyle(): Style { + return { + direction: Direction.Inherit, + flexDirection: FlexDirection.Column, + justifyContent: Justify.FlexStart, + alignItems: Align.Stretch, + alignSelf: Align.Auto, + alignContent: Align.FlexStart, + flexWrap: Wrap.NoWrap, + overflow: Overflow.Visible, + display: Display.Flex, + positionType: PositionType.Relative, + flexGrow: 0, + flexShrink: 0, + flexBasis: AUTO_VALUE, + margin: new Array(9).fill(UNDEFINED_VALUE), + padding: new Array(9).fill(UNDEFINED_VALUE), + border: new Array(9).fill(UNDEFINED_VALUE), + position: new Array(9).fill(UNDEFINED_VALUE), + gap: new Array(3).fill(UNDEFINED_VALUE), + width: AUTO_VALUE, + height: AUTO_VALUE, + minWidth: UNDEFINED_VALUE, + minHeight: UNDEFINED_VALUE, + maxWidth: UNDEFINED_VALUE, + maxHeight: UNDEFINED_VALUE, + } +} + +// -- +// Edge resolution — yoga's 9-edge model collapsed to 4 physical edges + +const EDGE_LEFT = 0 +const EDGE_TOP = 1 +const EDGE_RIGHT = 2 +const EDGE_BOTTOM = 3 + +function resolveEdge( + edges: Value[], + physicalEdge: number, + ownerSize: number, + // For margin/position we allow auto; for padding/border auto resolves to 0 + allowAuto = false, +): number { + // Precedence: specific edge > horizontal/vertical > all + let v = edges[physicalEdge]! + if (v.unit === Unit.Undefined) { + if (physicalEdge === EDGE_LEFT || physicalEdge === EDGE_RIGHT) { + v = edges[Edge.Horizontal]! + } else { + v = edges[Edge.Vertical]! + } + } + if (v.unit === Unit.Undefined) { + v = edges[Edge.All]! + } + // Start/End map to Left/Right for LTR (Ink is always LTR) + if (v.unit === Unit.Undefined) { + if (physicalEdge === EDGE_LEFT) v = edges[Edge.Start]! + if (physicalEdge === EDGE_RIGHT) v = edges[Edge.End]! + } + if (v.unit === Unit.Undefined) return 0 + if (v.unit === Unit.Auto) return allowAuto ? NaN : 0 + return resolveValue(v, ownerSize) +} + +function resolveEdgeRaw(edges: Value[], physicalEdge: number): Value { + let v = edges[physicalEdge]! + if (v.unit === Unit.Undefined) { + if (physicalEdge === EDGE_LEFT || physicalEdge === EDGE_RIGHT) { + v = edges[Edge.Horizontal]! + } else { + v = edges[Edge.Vertical]! + } + } + if (v.unit === Unit.Undefined) v = edges[Edge.All]! + if (v.unit === Unit.Undefined) { + if (physicalEdge === EDGE_LEFT) v = edges[Edge.Start]! + if (physicalEdge === EDGE_RIGHT) v = edges[Edge.End]! + } + return v +} + +function isMarginAuto(edges: Value[], physicalEdge: number): boolean { + return resolveEdgeRaw(edges, physicalEdge).unit === Unit.Auto +} + +// Setter helpers for the _hasAutoMargin / _hasPosition fast-path flags. +// Unit.Undefined = 0, Unit.Auto = 3. +function hasAnyAutoEdge(edges: Value[]): boolean { + for (let i = 0; i < 9; i++) if (edges[i]!.unit === 3) return true + return false +} +function hasAnyDefinedEdge(edges: Value[]): boolean { + for (let i = 0; i < 9; i++) if (edges[i]!.unit !== 0) return true + return false +} + +// Hot path: resolve all 4 physical edges in one pass, writing into `out`. +// Equivalent to calling resolveEdge() 4× with allowAuto=false, but hoists the +// shared fallback lookups (Horizontal/Vertical/All/Start/End) and avoids +// allocating a fresh 4-array on every layoutNode() call. +function resolveEdges4Into( + edges: Value[], + ownerSize: number, + out: [number, number, number, number], +): void { + // Hoist fallbacks once — the 4 per-edge chains share these reads. + const eH = edges[6]! // Edge.Horizontal + const eV = edges[7]! // Edge.Vertical + const eA = edges[8]! // Edge.All + const eS = edges[4]! // Edge.Start + const eE = edges[5]! // Edge.End + const pctDenom = isNaN(ownerSize) ? NaN : ownerSize / 100 + + // Left: edges[0] → Horizontal → All → Start + let v = edges[0]! + if (v.unit === 0) v = eH + if (v.unit === 0) v = eA + if (v.unit === 0) v = eS + out[0] = v.unit === 1 ? v.value : v.unit === 2 ? v.value * pctDenom : 0 + + // Top: edges[1] → Vertical → All + v = edges[1]! + if (v.unit === 0) v = eV + if (v.unit === 0) v = eA + out[1] = v.unit === 1 ? v.value : v.unit === 2 ? v.value * pctDenom : 0 + + // Right: edges[2] → Horizontal → All → End + v = edges[2]! + if (v.unit === 0) v = eH + if (v.unit === 0) v = eA + if (v.unit === 0) v = eE + out[2] = v.unit === 1 ? v.value : v.unit === 2 ? v.value * pctDenom : 0 + + // Bottom: edges[3] → Vertical → All + v = edges[3]! + if (v.unit === 0) v = eV + if (v.unit === 0) v = eA + out[3] = v.unit === 1 ? v.value : v.unit === 2 ? v.value * pctDenom : 0 +} + +// -- +// Axis helpers + +function isRow(dir: FlexDirection): boolean { + return dir === FlexDirection.Row || dir === FlexDirection.RowReverse +} +function isReverse(dir: FlexDirection): boolean { + return dir === FlexDirection.RowReverse || dir === FlexDirection.ColumnReverse +} +function crossAxis(dir: FlexDirection): FlexDirection { + return isRow(dir) ? FlexDirection.Column : FlexDirection.Row +} +function leadingEdge(dir: FlexDirection): number { + switch (dir) { + case FlexDirection.Row: + return EDGE_LEFT + case FlexDirection.RowReverse: + return EDGE_RIGHT + case FlexDirection.Column: + return EDGE_TOP + case FlexDirection.ColumnReverse: + return EDGE_BOTTOM + } +} +function trailingEdge(dir: FlexDirection): number { + switch (dir) { + case FlexDirection.Row: + return EDGE_RIGHT + case FlexDirection.RowReverse: + return EDGE_LEFT + case FlexDirection.Column: + return EDGE_BOTTOM + case FlexDirection.ColumnReverse: + return EDGE_TOP + } +} + +// -- +// Public types + +export type MeasureFunction = ( + width: number, + widthMode: MeasureMode, + height: number, + heightMode: MeasureMode, +) => { width: number; height: number } + +export type Size = { width: number; height: number } + +// -- +// Config + +export type Config = { + pointScaleFactor: number + errata: Errata + useWebDefaults: boolean + free(): void + isExperimentalFeatureEnabled(_: ExperimentalFeature): boolean + setExperimentalFeatureEnabled(_: ExperimentalFeature, __: boolean): void + setPointScaleFactor(factor: number): void + getErrata(): Errata + setErrata(errata: Errata): void + setUseWebDefaults(v: boolean): void +} + +function createConfig(): Config { + const config: Config = { + pointScaleFactor: 1, + errata: Errata.None, + useWebDefaults: false, + free() {}, + isExperimentalFeatureEnabled() { + return false + }, + setExperimentalFeatureEnabled() {}, + setPointScaleFactor(f) { + config.pointScaleFactor = f + }, + getErrata() { + return config.errata + }, + setErrata(e) { + config.errata = e + }, + setUseWebDefaults(v) { + config.useWebDefaults = v + }, + } + return config +} + +// -- +// Node implementation + +export class Node { + style: Style + layout: Layout + parent: Node | null + children: Node[] + measureFunc: MeasureFunction | null + config: Config + isDirty_: boolean + isReferenceBaseline_: boolean + + // Per-layout scratch (not public API) + _flexBasis = 0 + _mainSize = 0 + _crossSize = 0 + _lineIndex = 0 + // Fast-path flags maintained by style setters. Per CPU profile, the + // positioning loop calls isMarginAuto 6× and resolveEdgeRaw(position) 4× + // per child per layout pass — ~11k calls for the 1000-node bench, nearly + // all of which return false/undefined since most nodes have no auto + // margins and no position insets. These flags let us skip straight to + // the common case with a single branch. + _hasAutoMargin = false + _hasPosition = false + // Same pattern for the 3× resolveEdges4Into calls at the top of every + // layoutNode(). In the 1000-node bench ~67% of those calls operate on + // all-undefined edge arrays (most nodes have no border; only cols have + // padding; only leaf cells have margin) — a single-branch skip beats + // ~20 property reads + ~15 compares + 4 writes of zeros. + _hasPadding = false + _hasBorder = false + _hasMargin = false + // -- Dirty-flag layout cache. Mirrors upstream CalculateLayout.cpp's + // layoutNodeInternal: skip a subtree entirely when it's clean and we're + // asking the same question we cached the answer to. Two slots since + // each node typically sees a measure call (performLayout=false, from + // computeFlexBasis) followed by a layout call (performLayout=true) with + // different inputs per parent pass — a single slot thrashes. Re-layout + // bench (dirty one leaf, recompute root) went 2.7x→1.1x with this: + // clean siblings skip straight through, only the dirty chain recomputes. + _lW = NaN + _lH = NaN + _lWM: MeasureMode = 0 + _lHM: MeasureMode = 0 + _lOW = NaN + _lOH = NaN + _lFW = false + _lFH = false + // _hasL stores INPUTS early (before compute) but layout.width/height are + // mutated by the multi-entry cache and by subsequent compute calls with + // different inputs. Without storing OUTPUTS, a _hasL hit returns whatever + // layout.width/height happened to be left by the last call — the scrollbox + // vpH=33→2624 bug. Store + restore outputs like the multi-entry cache does. + _lOutW = NaN + _lOutH = NaN + _hasL = false + _mW = NaN + _mH = NaN + _mWM: MeasureMode = 0 + _mHM: MeasureMode = 0 + _mOW = NaN + _mOH = NaN + _mOutW = NaN + _mOutH = NaN + _hasM = false + // Cached computeFlexBasis result. For clean children, basis only depends + // on the container's inner dimensions — if those haven't changed, skip the + // layoutNode(performLayout=false) recursion entirely. This is the hot path + // for scroll: 500-message content container is dirty, its 499 clean + // children each get measured ~20× as the dirty chain's measure/layout + // passes cascade. Basis cache short-circuits at the child boundary. + _fbBasis = NaN + _fbOwnerW = NaN + _fbOwnerH = NaN + _fbAvailMain = NaN + _fbAvailCross = NaN + _fbCrossMode: MeasureMode = 0 + // Generation at which _fbBasis was written. Dirty nodes from a PREVIOUS + // generation have stale cache (subtree changed), but within the SAME + // generation the cache is fresh — the dirty chain's measure→layout + // cascade invokes computeFlexBasis ≥2^depth times per calculateLayout on + // fresh-mounted items, and the subtree doesn't change between calls. + // Gating on generation instead of isDirty_ lets fresh mounts (virtual + // scroll) cache-hit after first compute: 105k visits → ~10k. + _fbGen = -1 + // Multi-entry layout cache — stores (inputs → computed w,h) so hits with + // different inputs than _hasL can restore the right dimensions. Upstream + // yoga uses 16; 4 covers Ink's dirty-chain depth. Packed as flat arrays + // to avoid per-entry object allocs. Slot i uses indices [i*8, i*8+8) in + // _cIn (aW,aH,wM,hM,oW,oH,fW,fH) and [i*2, i*2+2) in _cOut (w,h). + _cIn: Float64Array | null = null + _cOut: Float64Array | null = null + _cGen = -1 + _cN = 0 + _cWr = 0 + + constructor(config?: Config) { + this.style = defaultStyle() + this.layout = { + left: 0, + top: 0, + width: 0, + height: 0, + border: [0, 0, 0, 0], + padding: [0, 0, 0, 0], + margin: [0, 0, 0, 0], + } + this.parent = null + this.children = [] + this.measureFunc = null + this.config = config ?? DEFAULT_CONFIG + this.isDirty_ = true + this.isReferenceBaseline_ = false + _yogaLiveNodes++ + } + + // -- Tree + + insertChild(child: Node, index: number): void { + child.parent = this + this.children.splice(index, 0, child) + this.markDirty() + } + removeChild(child: Node): void { + const idx = this.children.indexOf(child) + if (idx >= 0) { + this.children.splice(idx, 1) + child.parent = null + this.markDirty() + } + } + getChild(index: number): Node { + return this.children[index]! + } + getChildCount(): number { + return this.children.length + } + getParent(): Node | null { + return this.parent + } + + // -- Lifecycle + + free(): void { + this.parent = null + this.children = [] + this.measureFunc = null + this._cIn = null + this._cOut = null + _yogaLiveNodes-- + } + freeRecursive(): void { + for (const c of this.children) c.freeRecursive() + this.free() + } + reset(): void { + this.style = defaultStyle() + this.children = [] + this.parent = null + this.measureFunc = null + this.isDirty_ = true + this._hasAutoMargin = false + this._hasPosition = false + this._hasPadding = false + this._hasBorder = false + this._hasMargin = false + this._hasL = false + this._hasM = false + this._cN = 0 + this._cWr = 0 + this._fbBasis = NaN + } + + // -- Dirty tracking + + markDirty(): void { + this.isDirty_ = true + if (this.parent && !this.parent.isDirty_) this.parent.markDirty() + } + isDirty(): boolean { + return this.isDirty_ + } + hasNewLayout(): boolean { + return true + } + markLayoutSeen(): void {} + + // -- Measure function + + setMeasureFunc(fn: MeasureFunction | null): void { + this.measureFunc = fn + this.markDirty() + } + unsetMeasureFunc(): void { + this.measureFunc = null + this.markDirty() + } + + // -- Computed layout getters + + getComputedLeft(): number { + return this.layout.left + } + getComputedTop(): number { + return this.layout.top + } + getComputedWidth(): number { + return this.layout.width + } + getComputedHeight(): number { + return this.layout.height + } + getComputedRight(): number { + const p = this.parent + return p ? p.layout.width - this.layout.left - this.layout.width : 0 + } + getComputedBottom(): number { + const p = this.parent + return p ? p.layout.height - this.layout.top - this.layout.height : 0 + } + getComputedLayout(): { + left: number + top: number + right: number + bottom: number + width: number + height: number + } { + return { + left: this.layout.left, + top: this.layout.top, + right: this.getComputedRight(), + bottom: this.getComputedBottom(), + width: this.layout.width, + height: this.layout.height, + } + } + getComputedBorder(edge: Edge): number { + return this.layout.border[physicalEdge(edge)]! + } + getComputedPadding(edge: Edge): number { + return this.layout.padding[physicalEdge(edge)]! + } + getComputedMargin(edge: Edge): number { + return this.layout.margin[physicalEdge(edge)]! + } + + // -- Style setters: dimensions + + setWidth(v: number | 'auto' | string | undefined): void { + this.style.width = parseDimension(v) + this.markDirty() + } + setWidthPercent(v: number): void { + this.style.width = percentValue(v) + this.markDirty() + } + setWidthAuto(): void { + this.style.width = AUTO_VALUE + this.markDirty() + } + setHeight(v: number | 'auto' | string | undefined): void { + this.style.height = parseDimension(v) + this.markDirty() + } + setHeightPercent(v: number): void { + this.style.height = percentValue(v) + this.markDirty() + } + setHeightAuto(): void { + this.style.height = AUTO_VALUE + this.markDirty() + } + setMinWidth(v: number | string | undefined): void { + this.style.minWidth = parseDimension(v) + this.markDirty() + } + setMinWidthPercent(v: number): void { + this.style.minWidth = percentValue(v) + this.markDirty() + } + setMinHeight(v: number | string | undefined): void { + this.style.minHeight = parseDimension(v) + this.markDirty() + } + setMinHeightPercent(v: number): void { + this.style.minHeight = percentValue(v) + this.markDirty() + } + setMaxWidth(v: number | string | undefined): void { + this.style.maxWidth = parseDimension(v) + this.markDirty() + } + setMaxWidthPercent(v: number): void { + this.style.maxWidth = percentValue(v) + this.markDirty() + } + setMaxHeight(v: number | string | undefined): void { + this.style.maxHeight = parseDimension(v) + this.markDirty() + } + setMaxHeightPercent(v: number): void { + this.style.maxHeight = percentValue(v) + this.markDirty() + } + + // -- Style setters: flex + + setFlexDirection(dir: FlexDirection): void { + this.style.flexDirection = dir + this.markDirty() + } + setFlexGrow(v: number | undefined): void { + this.style.flexGrow = v ?? 0 + this.markDirty() + } + setFlexShrink(v: number | undefined): void { + this.style.flexShrink = v ?? 0 + this.markDirty() + } + setFlex(v: number | undefined): void { + if (v === undefined || isNaN(v)) { + this.style.flexGrow = 0 + this.style.flexShrink = 0 + } else if (v > 0) { + this.style.flexGrow = v + this.style.flexShrink = 1 + this.style.flexBasis = pointValue(0) + } else if (v < 0) { + this.style.flexGrow = 0 + this.style.flexShrink = -v + } else { + this.style.flexGrow = 0 + this.style.flexShrink = 0 + } + this.markDirty() + } + setFlexBasis(v: number | 'auto' | string | undefined): void { + this.style.flexBasis = parseDimension(v) + this.markDirty() + } + setFlexBasisPercent(v: number): void { + this.style.flexBasis = percentValue(v) + this.markDirty() + } + setFlexBasisAuto(): void { + this.style.flexBasis = AUTO_VALUE + this.markDirty() + } + setFlexWrap(wrap: Wrap): void { + this.style.flexWrap = wrap + this.markDirty() + } + + // -- Style setters: alignment + + setAlignItems(a: Align): void { + this.style.alignItems = a + this.markDirty() + } + setAlignSelf(a: Align): void { + this.style.alignSelf = a + this.markDirty() + } + setAlignContent(a: Align): void { + this.style.alignContent = a + this.markDirty() + } + setJustifyContent(j: Justify): void { + this.style.justifyContent = j + this.markDirty() + } + + // -- Style setters: display / position / overflow + + setDisplay(d: Display): void { + this.style.display = d + this.markDirty() + } + getDisplay(): Display { + return this.style.display + } + setPositionType(t: PositionType): void { + this.style.positionType = t + this.markDirty() + } + setPosition(edge: Edge, v: number | string | undefined): void { + this.style.position[edge] = parseDimension(v) + this._hasPosition = hasAnyDefinedEdge(this.style.position) + this.markDirty() + } + setPositionPercent(edge: Edge, v: number): void { + this.style.position[edge] = percentValue(v) + this._hasPosition = true + this.markDirty() + } + setPositionAuto(edge: Edge): void { + this.style.position[edge] = AUTO_VALUE + this._hasPosition = true + this.markDirty() + } + setOverflow(o: Overflow): void { + this.style.overflow = o + this.markDirty() + } + setDirection(d: Direction): void { + this.style.direction = d + this.markDirty() + } + setBoxSizing(_: BoxSizing): void { + // Not implemented — Ink doesn't use content-box + } + + // -- Style setters: spacing + + setMargin(edge: Edge, v: number | 'auto' | string | undefined): void { + const val = parseDimension(v) + this.style.margin[edge] = val + if (val.unit === Unit.Auto) this._hasAutoMargin = true + else this._hasAutoMargin = hasAnyAutoEdge(this.style.margin) + this._hasMargin = + this._hasAutoMargin || hasAnyDefinedEdge(this.style.margin) + this.markDirty() + } + setMarginPercent(edge: Edge, v: number): void { + this.style.margin[edge] = percentValue(v) + this._hasAutoMargin = hasAnyAutoEdge(this.style.margin) + this._hasMargin = true + this.markDirty() + } + setMarginAuto(edge: Edge): void { + this.style.margin[edge] = AUTO_VALUE + this._hasAutoMargin = true + this._hasMargin = true + this.markDirty() + } + setPadding(edge: Edge, v: number | string | undefined): void { + this.style.padding[edge] = parseDimension(v) + this._hasPadding = hasAnyDefinedEdge(this.style.padding) + this.markDirty() + } + setPaddingPercent(edge: Edge, v: number): void { + this.style.padding[edge] = percentValue(v) + this._hasPadding = true + this.markDirty() + } + setBorder(edge: Edge, v: number | undefined): void { + this.style.border[edge] = v === undefined ? UNDEFINED_VALUE : pointValue(v) + this._hasBorder = hasAnyDefinedEdge(this.style.border) + this.markDirty() + } + setGap(gutter: Gutter, v: number | string | undefined): void { + this.style.gap[gutter] = parseDimension(v) + this.markDirty() + } + setGapPercent(gutter: Gutter, v: number): void { + this.style.gap[gutter] = percentValue(v) + this.markDirty() + } + + // -- Style getters (partial — only what tests need) + + getFlexDirection(): FlexDirection { + return this.style.flexDirection + } + getJustifyContent(): Justify { + return this.style.justifyContent + } + getAlignItems(): Align { + return this.style.alignItems + } + getAlignSelf(): Align { + return this.style.alignSelf + } + getAlignContent(): Align { + return this.style.alignContent + } + getFlexGrow(): number { + return this.style.flexGrow + } + getFlexShrink(): number { + return this.style.flexShrink + } + getFlexBasis(): Value { + return this.style.flexBasis + } + getFlexWrap(): Wrap { + return this.style.flexWrap + } + getWidth(): Value { + return this.style.width + } + getHeight(): Value { + return this.style.height + } + getOverflow(): Overflow { + return this.style.overflow + } + getPositionType(): PositionType { + return this.style.positionType + } + getDirection(): Direction { + return this.style.direction + } + + // -- Unused API stubs (present for API parity) + + copyStyle(_: Node): void {} + setDirtiedFunc(_: unknown): void {} + unsetDirtiedFunc(): void {} + setIsReferenceBaseline(v: boolean): void { + this.isReferenceBaseline_ = v + this.markDirty() + } + isReferenceBaseline(): boolean { + return this.isReferenceBaseline_ + } + setAspectRatio(_: number | undefined): void {} + getAspectRatio(): number { + return NaN + } + setAlwaysFormsContainingBlock(_: boolean): void {} + + // -- Layout entry point + + calculateLayout( + ownerWidth: number | undefined, + ownerHeight: number | undefined, + _direction?: Direction, + ): void { + _yogaNodesVisited = 0 + _yogaMeasureCalls = 0 + _yogaCacheHits = 0 + _generation++ + const w = ownerWidth === undefined ? NaN : ownerWidth + const h = ownerHeight === undefined ? NaN : ownerHeight + layoutNode( + this, + w, + h, + isDefined(w) ? MeasureMode.Exactly : MeasureMode.Undefined, + isDefined(h) ? MeasureMode.Exactly : MeasureMode.Undefined, + w, + h, + true, + ) + // Root's own position = margin + position insets (yoga applies position + // to the root even without a parent container; this matters for rounding + // since the root's abs top/left seeds the pixel-grid walk). + const mar = this.layout.margin + const posL = resolveValue( + resolveEdgeRaw(this.style.position, EDGE_LEFT), + isDefined(w) ? w : 0, + ) + const posT = resolveValue( + resolveEdgeRaw(this.style.position, EDGE_TOP), + isDefined(w) ? w : 0, + ) + this.layout.left = mar[EDGE_LEFT] + (isDefined(posL) ? posL : 0) + this.layout.top = mar[EDGE_TOP] + (isDefined(posT) ? posT : 0) + roundLayout(this, this.config.pointScaleFactor, 0, 0) + } +} + +const DEFAULT_CONFIG = createConfig() + +const CACHE_SLOTS = 4 +function cacheWrite( + node: Node, + aW: number, + aH: number, + wM: MeasureMode, + hM: MeasureMode, + oW: number, + oH: number, + fW: boolean, + fH: boolean, + wasDirty: boolean, +): void { + if (!node._cIn) { + node._cIn = new Float64Array(CACHE_SLOTS * 8) + node._cOut = new Float64Array(CACHE_SLOTS * 2) + } + // First write after a dirty clears stale entries from before the dirty. + // _cGen < _generation means entries are from a previous calculateLayout; + // if wasDirty, the subtree changed since then → old dimensions invalid. + // Clean nodes' old entries stay — same subtree → same result for same + // inputs, so cross-generation caching works (the scroll hot path where + // 499 clean messages cache-hit while one dirty leaf recomputes). + if (wasDirty && node._cGen !== _generation) { + node._cN = 0 + node._cWr = 0 + } + // LRU write index wraps; _cN stays at CACHE_SLOTS so the read scan always + // checks all populated slots (not just those since last wrap). + const i = node._cWr++ % CACHE_SLOTS + if (node._cN < CACHE_SLOTS) node._cN = node._cWr + const o = i * 8 + const cIn = node._cIn + cIn[o] = aW + cIn[o + 1] = aH + cIn[o + 2] = wM + cIn[o + 3] = hM + cIn[o + 4] = oW + cIn[o + 5] = oH + cIn[o + 6] = fW ? 1 : 0 + cIn[o + 7] = fH ? 1 : 0 + node._cOut![i * 2] = node.layout.width + node._cOut![i * 2 + 1] = node.layout.height + node._cGen = _generation +} + +// Store computed layout.width/height into the single-slot cache output fields. +// _hasL/_hasM inputs are committed at the TOP of layoutNode (before compute); +// outputs must be committed HERE (after compute) so a cache hit can restore +// the correct dimensions. Without this, a _hasL hit returns whatever +// layout.width/height was left by the last call — which may be the intrinsic +// content height from a heightMode=Undefined measure pass rather than the +// constrained viewport height from the layout pass. That's the scrollbox +// vpH=33→2624 bug: scrollTop clamps to 0, viewport goes blank. +function commitCacheOutputs(node: Node, performLayout: boolean): void { + if (performLayout) { + node._lOutW = node.layout.width + node._lOutH = node.layout.height + } else { + node._mOutW = node.layout.width + node._mOutH = node.layout.height + } +} + +// -- +// Core flexbox algorithm + +// Profiling counters — reset per calculateLayout, read via getYogaCounters. +// Incremented on each calculateLayout(). Nodes stamp _fbGen/_cGen when +// their cache is written; a cache entry with gen === _generation was +// computed THIS pass and is fresh regardless of isDirty_ state. +let _generation = 0 +let _yogaNodesVisited = 0 +let _yogaMeasureCalls = 0 +let _yogaCacheHits = 0 +let _yogaLiveNodes = 0 +export function getYogaCounters(): { + visited: number + measured: number + cacheHits: number + live: number +} { + return { + visited: _yogaNodesVisited, + measured: _yogaMeasureCalls, + cacheHits: _yogaCacheHits, + live: _yogaLiveNodes, + } +} + +function layoutNode( + node: Node, + availableWidth: number, + availableHeight: number, + widthMode: MeasureMode, + heightMode: MeasureMode, + ownerWidth: number, + ownerHeight: number, + performLayout: boolean, + // When true, ignore style dimension on this axis — the flex container + // has already determined the main size (flex-basis + grow/shrink result). + forceWidth = false, + forceHeight = false, +): void { + _yogaNodesVisited++ + const style = node.style + const layout = node.layout + + // Dirty-flag skip: clean subtree + matching inputs → layout object already + // holds the answer. A cached layout result also satisfies a measure request + // (positions are a superset of dimensions); the reverse does not hold. + // Same-generation entries are fresh regardless of isDirty_ — they were + // computed THIS calculateLayout, the subtree hasn't changed since. + // Previous-generation entries need !isDirty_ (a dirty node's cache from + // before the dirty is stale). + // sameGen bypass only for MEASURE calls — a layout-pass cache hit would + // skip the child-positioning recursion (STEP 5), leaving children at + // stale positions. Measure calls only need w/h which the cache stores. + const sameGen = node._cGen === _generation && !performLayout + if (!node.isDirty_ || sameGen) { + if ( + !node.isDirty_ && + node._hasL && + node._lWM === widthMode && + node._lHM === heightMode && + node._lFW === forceWidth && + node._lFH === forceHeight && + sameFloat(node._lW, availableWidth) && + sameFloat(node._lH, availableHeight) && + sameFloat(node._lOW, ownerWidth) && + sameFloat(node._lOH, ownerHeight) + ) { + _yogaCacheHits++ + layout.width = node._lOutW + layout.height = node._lOutH + return + } + // Multi-entry cache: scan for matching inputs, restore cached w/h on hit. + // Covers the scroll case where a dirty ancestor's measure→layout cascade + // produces N>1 distinct input combos per clean child — the single _hasL + // slot thrashed, forcing full subtree recursion. With 500-message + // scrollbox and one dirty leaf, this took dirty-leaf relayout from + // 76k layoutNode calls (21.7×nodes) to 4k (1.2×nodes), 6.86ms → 550µs. + // Same-generation check covers fresh-mounted (dirty) nodes during + // virtual scroll — the dirty chain invokes them ≥2^depth times, first + // call writes cache, rest hit: 105k visits → ~10k for 1593-node tree. + if (node._cN > 0 && (sameGen || !node.isDirty_)) { + const cIn = node._cIn! + for (let i = 0; i < node._cN; i++) { + const o = i * 8 + if ( + cIn[o + 2] === widthMode && + cIn[o + 3] === heightMode && + cIn[o + 6] === (forceWidth ? 1 : 0) && + cIn[o + 7] === (forceHeight ? 1 : 0) && + sameFloat(cIn[o]!, availableWidth) && + sameFloat(cIn[o + 1]!, availableHeight) && + sameFloat(cIn[o + 4]!, ownerWidth) && + sameFloat(cIn[o + 5]!, ownerHeight) + ) { + layout.width = node._cOut![i * 2]! + layout.height = node._cOut![i * 2 + 1]! + _yogaCacheHits++ + return + } + } + } + if ( + !node.isDirty_ && + !performLayout && + node._hasM && + node._mWM === widthMode && + node._mHM === heightMode && + sameFloat(node._mW, availableWidth) && + sameFloat(node._mH, availableHeight) && + sameFloat(node._mOW, ownerWidth) && + sameFloat(node._mOH, ownerHeight) + ) { + layout.width = node._mOutW + layout.height = node._mOutH + _yogaCacheHits++ + return + } + } + // Commit cache inputs up front so every return path leaves a valid entry. + // Only clear isDirty_ on the LAYOUT pass — the measure pass (computeFlexBasis + // → layoutNode(performLayout=false)) runs before the layout pass in the same + // calculateLayout call. Clearing dirty during measure lets the subsequent + // layout pass hit the STALE _hasL cache from the previous calculateLayout + // (before children were inserted), so ScrollBox content height never grows + // and sticky-scroll never follows new content. A dirty node's _hasL entry is + // stale by definition — invalidate it so the layout pass recomputes. + const wasDirty = node.isDirty_ + if (performLayout) { + node._lW = availableWidth + node._lH = availableHeight + node._lWM = widthMode + node._lHM = heightMode + node._lOW = ownerWidth + node._lOH = ownerHeight + node._lFW = forceWidth + node._lFH = forceHeight + node._hasL = true + node.isDirty_ = false + // Previous approach cleared _cN here to prevent stale pre-dirty entries + // from hitting (long-continuous blank-screen bug). Now replaced by + // generation stamping: the cache check requires sameGen || !isDirty_, so + // previous-generation entries from a dirty node can't hit. Clearing here + // would wipe fresh same-generation entries from an earlier measure call, + // forcing recompute on the layout call. + if (wasDirty) node._hasM = false + } else { + node._mW = availableWidth + node._mH = availableHeight + node._mWM = widthMode + node._mHM = heightMode + node._mOW = ownerWidth + node._mOH = ownerHeight + node._hasM = true + // Don't clear isDirty_. For DIRTY nodes, invalidate _hasL so the upcoming + // performLayout=true call recomputes with the new child set (otherwise + // sticky-scroll never follows new content — the bug from 4557bc9f9c). + // Clean nodes keep _hasL: their layout from the previous generation is + // still valid, they're only here because an ancestor is dirty and called + // with different inputs than cached. + if (wasDirty) node._hasL = false + } + + // Resolve padding/border/margin against ownerWidth (yoga uses ownerWidth for %) + // Write directly into the pre-allocated layout arrays — avoids 3 allocs per + // layoutNode call and 12 resolveEdge calls (was the #1 hotspot per CPU profile). + // Skip entirely when no edges are set — the 4-write zero is cheaper than + // the ~20 reads + ~15 compares resolveEdges4Into does to produce zeros. + const pad = layout.padding + const bor = layout.border + const mar = layout.margin + if (node._hasPadding) resolveEdges4Into(style.padding, ownerWidth, pad) + else pad[0] = pad[1] = pad[2] = pad[3] = 0 + if (node._hasBorder) resolveEdges4Into(style.border, ownerWidth, bor) + else bor[0] = bor[1] = bor[2] = bor[3] = 0 + if (node._hasMargin) resolveEdges4Into(style.margin, ownerWidth, mar) + else mar[0] = mar[1] = mar[2] = mar[3] = 0 + + const paddingBorderWidth = pad[0] + pad[2] + bor[0] + bor[2] + const paddingBorderHeight = pad[1] + pad[3] + bor[1] + bor[3] + + // Resolve style dimensions + const styleWidth = forceWidth ? NaN : resolveValue(style.width, ownerWidth) + const styleHeight = forceHeight + ? NaN + : resolveValue(style.height, ownerHeight) + + // If style dimension is defined, it overrides the available size + let width = availableWidth + let height = availableHeight + let wMode = widthMode + let hMode = heightMode + if (isDefined(styleWidth)) { + width = styleWidth + wMode = MeasureMode.Exactly + } + if (isDefined(styleHeight)) { + height = styleHeight + hMode = MeasureMode.Exactly + } + + // Apply min/max constraints to the node's own dimensions + width = boundAxis(style, true, width, ownerWidth, ownerHeight) + height = boundAxis(style, false, height, ownerWidth, ownerHeight) + + // Measure-func leaf node + if (node.measureFunc && node.children.length === 0) { + const innerW = + wMode === MeasureMode.Undefined + ? NaN + : Math.max(0, width - paddingBorderWidth) + const innerH = + hMode === MeasureMode.Undefined + ? NaN + : Math.max(0, height - paddingBorderHeight) + _yogaMeasureCalls++ + const measured = node.measureFunc(innerW, wMode, innerH, hMode) + node.layout.width = + wMode === MeasureMode.Exactly + ? width + : boundAxis( + style, + true, + (measured.width ?? 0) + paddingBorderWidth, + ownerWidth, + ownerHeight, + ) + node.layout.height = + hMode === MeasureMode.Exactly + ? height + : boundAxis( + style, + false, + (measured.height ?? 0) + paddingBorderHeight, + ownerWidth, + ownerHeight, + ) + commitCacheOutputs(node, performLayout) + // Write cache even for dirty nodes — fresh-mounted items during virtual + // scroll are dirty on first layout, but the dirty chain's measure→layout + // cascade invokes them ≥2^depth times per calculateLayout. Writing here + // lets the 2nd+ calls hit cache (isDirty_ was cleared in the layout pass + // above). Measured: 105k visits → 10k for a 1593-node fresh-mount tree. + cacheWrite( + node, + availableWidth, + availableHeight, + widthMode, + heightMode, + ownerWidth, + ownerHeight, + forceWidth, + forceHeight, + wasDirty, + ) + return + } + + // Leaf node with no children and no measure func + if (node.children.length === 0) { + node.layout.width = + wMode === MeasureMode.Exactly + ? width + : boundAxis(style, true, paddingBorderWidth, ownerWidth, ownerHeight) + node.layout.height = + hMode === MeasureMode.Exactly + ? height + : boundAxis(style, false, paddingBorderHeight, ownerWidth, ownerHeight) + commitCacheOutputs(node, performLayout) + // Write cache even for dirty nodes — fresh-mounted items during virtual + // scroll are dirty on first layout, but the dirty chain's measure→layout + // cascade invokes them ≥2^depth times per calculateLayout. Writing here + // lets the 2nd+ calls hit cache (isDirty_ was cleared in the layout pass + // above). Measured: 105k visits → 10k for a 1593-node fresh-mount tree. + cacheWrite( + node, + availableWidth, + availableHeight, + widthMode, + heightMode, + ownerWidth, + ownerHeight, + forceWidth, + forceHeight, + wasDirty, + ) + return + } + + // Container with children — run flexbox algorithm + const mainAxis = style.flexDirection + const crossAx = crossAxis(mainAxis) + const isMainRow = isRow(mainAxis) + + const mainSize = isMainRow ? width : height + const crossSize = isMainRow ? height : width + const mainMode = isMainRow ? wMode : hMode + const crossMode = isMainRow ? hMode : wMode + const mainPadBorder = isMainRow ? paddingBorderWidth : paddingBorderHeight + const crossPadBorder = isMainRow ? paddingBorderHeight : paddingBorderWidth + + const innerMainSize = isDefined(mainSize) + ? Math.max(0, mainSize - mainPadBorder) + : NaN + const innerCrossSize = isDefined(crossSize) + ? Math.max(0, crossSize - crossPadBorder) + : NaN + + // Resolve gap + const gapMain = resolveGap( + style, + isMainRow ? Gutter.Column : Gutter.Row, + innerMainSize, + ) + + // Partition children into flow vs absolute. display:contents nodes are + // transparent — their children are lifted into the grandparent's child list + // (recursively), and the contents node itself gets zero layout. + const flowChildren: Node[] = [] + const absChildren: Node[] = [] + collectLayoutChildren(node, flowChildren, absChildren) + + // ownerW/H are the reference sizes for resolving children's percentage + // values. Per CSS, a % width resolves against the parent's content-box + // width. If this node's width is indefinite, children's % widths are also + // indefinite — do NOT fall through to the grandparent's size. + const ownerW = isDefined(width) ? width : NaN + const ownerH = isDefined(height) ? height : NaN + const isWrap = style.flexWrap !== Wrap.NoWrap + const gapCross = resolveGap( + style, + isMainRow ? Gutter.Row : Gutter.Column, + innerCrossSize, + ) + + // STEP 1: Compute flex-basis for each flow child and break into lines. + // Single-line (NoWrap) containers always get one line; multi-line containers + // break when accumulated basis+margin+gap exceeds innerMainSize. + for (const c of flowChildren) { + c._flexBasis = computeFlexBasis( + c, + mainAxis, + innerMainSize, + innerCrossSize, + crossMode, + ownerW, + ownerH, + ) + } + const lines: Node[][] = [] + if (!isWrap || !isDefined(innerMainSize) || flowChildren.length === 0) { + for (const c of flowChildren) c._lineIndex = 0 + lines.push(flowChildren) + } else { + // Line-break decisions use the min/max-clamped basis (flexbox spec §9.3.5: + // "hypothetical main size"), not the raw flex-basis. + let lineStart = 0 + let lineLen = 0 + for (let i = 0; i < flowChildren.length; i++) { + const c = flowChildren[i]! + const hypo = boundAxis(c.style, isMainRow, c._flexBasis, ownerW, ownerH) + const outer = Math.max(0, hypo) + childMarginForAxis(c, mainAxis, ownerW) + const withGap = i > lineStart ? gapMain : 0 + if (i > lineStart && lineLen + withGap + outer > innerMainSize) { + lines.push(flowChildren.slice(lineStart, i)) + lineStart = i + lineLen = outer + } else { + lineLen += withGap + outer + } + c._lineIndex = lines.length + } + lines.push(flowChildren.slice(lineStart)) + } + const lineCount = lines.length + const isBaseline = isBaselineLayout(node, flowChildren) + + // STEP 2+3: For each line, resolve flexible lengths and lay out children to + // measure cross sizes. Track per-line consumed main and max cross. + const lineConsumedMain: number[] = new Array(lineCount) + const lineCrossSizes: number[] = new Array(lineCount) + // Baseline layout tracks max ascent (baseline + leading margin) per line so + // baseline-aligned items can be positioned at maxAscent - childBaseline. + const lineMaxAscent: number[] = isBaseline ? new Array(lineCount).fill(0) : [] + let maxLineMain = 0 + let totalLinesCross = 0 + for (let li = 0; li < lineCount; li++) { + const line = lines[li]! + const lineGap = line.length > 1 ? gapMain * (line.length - 1) : 0 + let lineBasis = lineGap + for (const c of line) { + lineBasis += c._flexBasis + childMarginForAxis(c, mainAxis, ownerW) + } + // Resolve flexible lengths against available inner main. For indefinite + // containers with min/max, flex against the clamped size. + let availMain = innerMainSize + if (!isDefined(availMain)) { + const mainOwner = isMainRow ? ownerWidth : ownerHeight + const minM = resolveValue( + isMainRow ? style.minWidth : style.minHeight, + mainOwner, + ) + const maxM = resolveValue( + isMainRow ? style.maxWidth : style.maxHeight, + mainOwner, + ) + if (isDefined(maxM) && lineBasis > maxM - mainPadBorder) { + availMain = Math.max(0, maxM - mainPadBorder) + } else if (isDefined(minM) && lineBasis < minM - mainPadBorder) { + availMain = Math.max(0, minM - mainPadBorder) + } + } + resolveFlexibleLengths( + line, + availMain, + lineBasis, + isMainRow, + ownerW, + ownerH, + ) + + // Lay out each child in this line to measure cross + let lineCross = 0 + for (const c of line) { + const cStyle = c.style + const childAlign = + cStyle.alignSelf === Align.Auto ? style.alignItems : cStyle.alignSelf + const cMarginCross = childMarginForAxis(c, crossAx, ownerW) + let childCrossSize = NaN + let childCrossMode: MeasureMode = MeasureMode.Undefined + const resolvedCrossStyle = resolveValue( + isMainRow ? cStyle.height : cStyle.width, + isMainRow ? ownerH : ownerW, + ) + const crossLeadE = isMainRow ? EDGE_TOP : EDGE_LEFT + const crossTrailE = isMainRow ? EDGE_BOTTOM : EDGE_RIGHT + const hasCrossAutoMargin = + c._hasAutoMargin && + (isMarginAuto(cStyle.margin, crossLeadE) || + isMarginAuto(cStyle.margin, crossTrailE)) + // Single-line stretch goes directly to the container cross size. + // Multi-line wrap measures intrinsic cross (Undefined mode) so + // flex-grow grandchildren don't expand to the container — the line + // cross size is determined first, then items are re-stretched. + if (isDefined(resolvedCrossStyle)) { + childCrossSize = resolvedCrossStyle + childCrossMode = MeasureMode.Exactly + } else if ( + childAlign === Align.Stretch && + !hasCrossAutoMargin && + !isWrap && + isDefined(innerCrossSize) && + crossMode === MeasureMode.Exactly + ) { + childCrossSize = Math.max(0, innerCrossSize - cMarginCross) + childCrossMode = MeasureMode.Exactly + } else if (!isWrap && isDefined(innerCrossSize)) { + childCrossSize = Math.max(0, innerCrossSize - cMarginCross) + childCrossMode = MeasureMode.AtMost + } + const cw = isMainRow ? c._mainSize : childCrossSize + const ch = isMainRow ? childCrossSize : c._mainSize + layoutNode( + c, + cw, + ch, + isMainRow ? MeasureMode.Exactly : childCrossMode, + isMainRow ? childCrossMode : MeasureMode.Exactly, + ownerW, + ownerH, + performLayout, + isMainRow, + !isMainRow, + ) + c._crossSize = isMainRow ? c.layout.height : c.layout.width + lineCross = Math.max(lineCross, c._crossSize + cMarginCross) + } + // Baseline layout: line cross size must fit maxAscent + maxDescent of + // baseline-aligned children (yoga STEP 8). Only applies to row direction. + if (isBaseline) { + let maxAscent = 0 + let maxDescent = 0 + for (const c of line) { + if (resolveChildAlign(node, c) !== Align.Baseline) continue + const mTop = resolveEdge(c.style.margin, EDGE_TOP, ownerW) + const mBot = resolveEdge(c.style.margin, EDGE_BOTTOM, ownerW) + const ascent = calculateBaseline(c) + mTop + const descent = c.layout.height + mTop + mBot - ascent + if (ascent > maxAscent) maxAscent = ascent + if (descent > maxDescent) maxDescent = descent + } + lineMaxAscent[li] = maxAscent + if (maxAscent + maxDescent > lineCross) { + lineCross = maxAscent + maxDescent + } + } + // layoutNode(c) at line ~1117 above already resolved c.layout.margin[] via + // resolveEdges4Into with the same ownerW — read directly instead of + // re-resolving through childMarginForAxis → 2× resolveEdge. + const mainLead = leadingEdge(mainAxis) + const mainTrail = trailingEdge(mainAxis) + let consumed = lineGap + for (const c of line) { + const cm = c.layout.margin + consumed += c._mainSize + cm[mainLead]! + cm[mainTrail]! + } + lineConsumedMain[li] = consumed + lineCrossSizes[li] = lineCross + maxLineMain = Math.max(maxLineMain, consumed) + totalLinesCross += lineCross + } + const totalCrossGap = lineCount > 1 ? gapCross * (lineCount - 1) : 0 + totalLinesCross += totalCrossGap + + // STEP 4: Determine container dimensions. Per yoga's STEP 9, for both + // AtMost (FitContent) and Undefined (MaxContent) the node sizes to its + // content — AtMost is NOT a hard clamp, items may overflow the available + // space (CSS "fit-content" behavior). Only Scroll overflow clamps to the + // available size. Wrap containers that broke into multiple lines under + // AtMost fill the available main size since they wrapped at that boundary. + const isScroll = style.overflow === Overflow.Scroll + const contentMain = maxLineMain + mainPadBorder + const finalMainSize = + mainMode === MeasureMode.Exactly + ? mainSize + : mainMode === MeasureMode.AtMost && isScroll + ? Math.max(Math.min(mainSize, contentMain), mainPadBorder) + : isWrap && lineCount > 1 && mainMode === MeasureMode.AtMost + ? mainSize + : contentMain + const contentCross = totalLinesCross + crossPadBorder + const finalCrossSize = + crossMode === MeasureMode.Exactly + ? crossSize + : crossMode === MeasureMode.AtMost && isScroll + ? Math.max(Math.min(crossSize, contentCross), crossPadBorder) + : contentCross + node.layout.width = boundAxis( + style, + true, + isMainRow ? finalMainSize : finalCrossSize, + ownerWidth, + ownerHeight, + ) + node.layout.height = boundAxis( + style, + false, + isMainRow ? finalCrossSize : finalMainSize, + ownerWidth, + ownerHeight, + ) + commitCacheOutputs(node, performLayout) + // Write cache even for dirty nodes — fresh-mounted items during virtual scroll + cacheWrite( + node, + availableWidth, + availableHeight, + widthMode, + heightMode, + ownerWidth, + ownerHeight, + forceWidth, + forceHeight, + wasDirty, + ) + + if (!performLayout) return + + // STEP 5: Position lines (align-content) and children (justify-content + + // align-items + auto margins). + const actualInnerMain = + (isMainRow ? node.layout.width : node.layout.height) - mainPadBorder + const actualInnerCross = + (isMainRow ? node.layout.height : node.layout.width) - crossPadBorder + const mainLeadEdgePhys = leadingEdge(mainAxis) + const mainTrailEdgePhys = trailingEdge(mainAxis) + const crossLeadEdgePhys = isMainRow ? EDGE_TOP : EDGE_LEFT + const crossTrailEdgePhys = isMainRow ? EDGE_BOTTOM : EDGE_RIGHT + const reversed = isReverse(mainAxis) + const mainContainerSize = isMainRow ? node.layout.width : node.layout.height + const crossLead = pad[crossLeadEdgePhys]! + bor[crossLeadEdgePhys]! + + // Align-content: distribute free cross space among lines. Single-line + // containers use the full cross size for the one line (align-items handles + // positioning within it). + let lineCrossOffset = crossLead + let betweenLines = gapCross + const freeCross = actualInnerCross - totalLinesCross + if (lineCount === 1 && !isWrap && !isBaseline) { + lineCrossSizes[0] = actualInnerCross + } else { + const remCross = Math.max(0, freeCross) + switch (style.alignContent) { + case Align.FlexStart: + break + case Align.Center: + lineCrossOffset += freeCross / 2 + break + case Align.FlexEnd: + lineCrossOffset += freeCross + break + case Align.Stretch: + if (lineCount > 0 && remCross > 0) { + const add = remCross / lineCount + for (let i = 0; i < lineCount; i++) lineCrossSizes[i]! += add + } + break + case Align.SpaceBetween: + if (lineCount > 1) betweenLines += remCross / (lineCount - 1) + break + case Align.SpaceAround: + if (lineCount > 0) { + betweenLines += remCross / lineCount + lineCrossOffset += remCross / lineCount / 2 + } + break + case Align.SpaceEvenly: + if (lineCount > 0) { + betweenLines += remCross / (lineCount + 1) + lineCrossOffset += remCross / (lineCount + 1) + } + break + default: + break + } + } + + // For wrap-reverse, lines stack from the trailing cross edge. Walk lines in + // order but flip the cross position within the container. + const wrapReverse = style.flexWrap === Wrap.WrapReverse + const crossContainerSize = isMainRow ? node.layout.height : node.layout.width + let lineCrossPos = lineCrossOffset + for (let li = 0; li < lineCount; li++) { + const line = lines[li]! + const lineCross = lineCrossSizes[li]! + const consumedMain = lineConsumedMain[li]! + const n = line.length + + // Re-stretch children whose cross is auto and align is stretch, now that + // the line cross size is known. Needed for multi-line wrap (line cross + // wasn't known during initial measure) AND single-line when the container + // cross was not Exactly (initial stretch at ~line 1250 was skipped because + // innerCrossSize wasn't defined — the container sized to max child cross). + if (isWrap || crossMode !== MeasureMode.Exactly) { + for (const c of line) { + const cStyle = c.style + const childAlign = + cStyle.alignSelf === Align.Auto ? style.alignItems : cStyle.alignSelf + const crossStyleDef = isDefined( + resolveValue( + isMainRow ? cStyle.height : cStyle.width, + isMainRow ? ownerH : ownerW, + ), + ) + const hasCrossAutoMargin = + c._hasAutoMargin && + (isMarginAuto(cStyle.margin, crossLeadEdgePhys) || + isMarginAuto(cStyle.margin, crossTrailEdgePhys)) + if ( + childAlign === Align.Stretch && + !crossStyleDef && + !hasCrossAutoMargin + ) { + const cMarginCross = childMarginForAxis(c, crossAx, ownerW) + const target = Math.max(0, lineCross - cMarginCross) + if (c._crossSize !== target) { + const cw = isMainRow ? c._mainSize : target + const ch = isMainRow ? target : c._mainSize + layoutNode( + c, + cw, + ch, + MeasureMode.Exactly, + MeasureMode.Exactly, + ownerW, + ownerH, + performLayout, + isMainRow, + !isMainRow, + ) + c._crossSize = target + } + } + } + } + + // Justify-content + auto margins for this line + let mainOffset = pad[mainLeadEdgePhys]! + bor[mainLeadEdgePhys]! + let betweenMain = gapMain + let numAutoMarginsMain = 0 + for (const c of line) { + if (!c._hasAutoMargin) continue + if (isMarginAuto(c.style.margin, mainLeadEdgePhys)) numAutoMarginsMain++ + if (isMarginAuto(c.style.margin, mainTrailEdgePhys)) numAutoMarginsMain++ + } + const freeMain = actualInnerMain - consumedMain + const remainingMain = Math.max(0, freeMain) + const autoMarginMainSize = + numAutoMarginsMain > 0 && remainingMain > 0 + ? remainingMain / numAutoMarginsMain + : 0 + if (numAutoMarginsMain === 0) { + switch (style.justifyContent) { + case Justify.FlexStart: + break + case Justify.Center: + mainOffset += freeMain / 2 + break + case Justify.FlexEnd: + mainOffset += freeMain + break + case Justify.SpaceBetween: + if (n > 1) betweenMain += remainingMain / (n - 1) + break + case Justify.SpaceAround: + if (n > 0) { + betweenMain += remainingMain / n + mainOffset += remainingMain / n / 2 + } + break + case Justify.SpaceEvenly: + if (n > 0) { + betweenMain += remainingMain / (n + 1) + mainOffset += remainingMain / (n + 1) + } + break + } + } + + const effectiveLineCrossPos = wrapReverse + ? crossContainerSize - lineCrossPos - lineCross + : lineCrossPos + + let pos = mainOffset + for (const c of line) { + const cMargin = c.style.margin + // c.layout.margin[] was populated by resolveEdges4Into inside the + // layoutNode(c) call above (same ownerW). Read resolved values directly + // instead of re-running the edge fallback chain 4× via resolveEdge. + // Auto margins resolve to 0 in layout.margin, so autoMarginMainSize + // substitution still uses the isMarginAuto check against style. + const cLayoutMargin = c.layout.margin + let autoMainLead = false + let autoMainTrail = false + let autoCrossLead = false + let autoCrossTrail = false + let mMainLead: number + let mMainTrail: number + let mCrossLead: number + let mCrossTrail: number + if (c._hasAutoMargin) { + autoMainLead = isMarginAuto(cMargin, mainLeadEdgePhys) + autoMainTrail = isMarginAuto(cMargin, mainTrailEdgePhys) + autoCrossLead = isMarginAuto(cMargin, crossLeadEdgePhys) + autoCrossTrail = isMarginAuto(cMargin, crossTrailEdgePhys) + mMainLead = autoMainLead + ? autoMarginMainSize + : cLayoutMargin[mainLeadEdgePhys]! + mMainTrail = autoMainTrail + ? autoMarginMainSize + : cLayoutMargin[mainTrailEdgePhys]! + mCrossLead = autoCrossLead ? 0 : cLayoutMargin[crossLeadEdgePhys]! + mCrossTrail = autoCrossTrail ? 0 : cLayoutMargin[crossTrailEdgePhys]! + } else { + // Fast path: no auto margins — read resolved values directly. + mMainLead = cLayoutMargin[mainLeadEdgePhys]! + mMainTrail = cLayoutMargin[mainTrailEdgePhys]! + mCrossLead = cLayoutMargin[crossLeadEdgePhys]! + mCrossTrail = cLayoutMargin[crossTrailEdgePhys]! + } + + const mainPos = reversed + ? mainContainerSize - (pos + mMainLead) - c._mainSize + : pos + mMainLead + + const childAlign = + c.style.alignSelf === Align.Auto ? style.alignItems : c.style.alignSelf + let crossPos = effectiveLineCrossPos + mCrossLead + const crossFree = lineCross - c._crossSize - mCrossLead - mCrossTrail + if (autoCrossLead && autoCrossTrail) { + crossPos += Math.max(0, crossFree) / 2 + } else if (autoCrossLead) { + crossPos += Math.max(0, crossFree) + } else if (autoCrossTrail) { + // stays at leading + } else { + switch (childAlign) { + case Align.FlexStart: + case Align.Stretch: + if (wrapReverse) crossPos += crossFree + break + case Align.Center: + crossPos += crossFree / 2 + break + case Align.FlexEnd: + if (!wrapReverse) crossPos += crossFree + break + case Align.Baseline: + // Row direction only (isBaselineLayout checked this). Position so + // the child's baseline aligns with the line's max ascent. Per + // yoga: top = currentLead + maxAscent - childBaseline + leadingPosition. + if (isBaseline) { + crossPos = + effectiveLineCrossPos + + lineMaxAscent[li]! - + calculateBaseline(c) + } + break + default: + break + } + } + + // Relative position offsets. Fast path: no position insets set → + // skip 4× resolveEdgeRaw + 4× resolveValue + 4× isDefined. + let relX = 0 + let relY = 0 + if (c._hasPosition) { + const relLeft = resolveValue( + resolveEdgeRaw(c.style.position, EDGE_LEFT), + ownerW, + ) + const relRight = resolveValue( + resolveEdgeRaw(c.style.position, EDGE_RIGHT), + ownerW, + ) + const relTop = resolveValue( + resolveEdgeRaw(c.style.position, EDGE_TOP), + ownerW, + ) + const relBottom = resolveValue( + resolveEdgeRaw(c.style.position, EDGE_BOTTOM), + ownerW, + ) + relX = isDefined(relLeft) + ? relLeft + : isDefined(relRight) + ? -relRight + : 0 + relY = isDefined(relTop) + ? relTop + : isDefined(relBottom) + ? -relBottom + : 0 + } + + if (isMainRow) { + c.layout.left = mainPos + relX + c.layout.top = crossPos + relY + } else { + c.layout.left = crossPos + relX + c.layout.top = mainPos + relY + } + pos += c._mainSize + mMainLead + mMainTrail + betweenMain + } + lineCrossPos += lineCross + betweenLines + } + + // STEP 6: Absolute-positioned children + for (const c of absChildren) { + layoutAbsoluteChild( + node, + c, + node.layout.width, + node.layout.height, + pad, + bor, + ) + } +} + +function layoutAbsoluteChild( + parent: Node, + child: Node, + parentWidth: number, + parentHeight: number, + pad: [number, number, number, number], + bor: [number, number, number, number], +): void { + const cs = child.style + const posLeft = resolveEdgeRaw(cs.position, EDGE_LEFT) + const posRight = resolveEdgeRaw(cs.position, EDGE_RIGHT) + const posTop = resolveEdgeRaw(cs.position, EDGE_TOP) + const posBottom = resolveEdgeRaw(cs.position, EDGE_BOTTOM) + + const rLeft = resolveValue(posLeft, parentWidth) + const rRight = resolveValue(posRight, parentWidth) + const rTop = resolveValue(posTop, parentHeight) + const rBottom = resolveValue(posBottom, parentHeight) + + // Absolute children's percentage dimensions resolve against the containing + // block's padding-box (parent size minus border), per CSS §10.1. + const paddingBoxW = parentWidth - bor[0] - bor[2] + const paddingBoxH = parentHeight - bor[1] - bor[3] + let cw = resolveValue(cs.width, paddingBoxW) + let ch = resolveValue(cs.height, paddingBoxH) + + // If both left+right defined and width not, derive width + if (!isDefined(cw) && isDefined(rLeft) && isDefined(rRight)) { + cw = paddingBoxW - rLeft - rRight + } + if (!isDefined(ch) && isDefined(rTop) && isDefined(rBottom)) { + ch = paddingBoxH - rTop - rBottom + } + + layoutNode( + child, + cw, + ch, + isDefined(cw) ? MeasureMode.Exactly : MeasureMode.Undefined, + isDefined(ch) ? MeasureMode.Exactly : MeasureMode.Undefined, + paddingBoxW, + paddingBoxH, + true, + ) + + // Margin of absolute child (applied in addition to insets) + const mL = resolveEdge(cs.margin, EDGE_LEFT, parentWidth) + const mT = resolveEdge(cs.margin, EDGE_TOP, parentWidth) + const mR = resolveEdge(cs.margin, EDGE_RIGHT, parentWidth) + const mB = resolveEdge(cs.margin, EDGE_BOTTOM, parentWidth) + + const mainAxis = parent.style.flexDirection + const reversed = isReverse(mainAxis) + const mainRow = isRow(mainAxis) + const wrapReverse = parent.style.flexWrap === Wrap.WrapReverse + // alignSelf overrides alignItems for absolute children (same as flow items) + const alignment = + cs.alignSelf === Align.Auto ? parent.style.alignItems : cs.alignSelf + + // Position + let left: number + if (isDefined(rLeft)) { + left = bor[0] + rLeft + mL + } else if (isDefined(rRight)) { + left = parentWidth - bor[2] - rRight - child.layout.width - mR + } else if (mainRow) { + // Main axis — justify-content, flipped for reversed + const lead = pad[0] + bor[0] + const trail = parentWidth - pad[2] - bor[2] + left = reversed + ? trail - child.layout.width - mR + : justifyAbsolute( + parent.style.justifyContent, + lead, + trail, + child.layout.width, + ) + mL + } else { + left = + alignAbsolute( + alignment, + pad[0] + bor[0], + parentWidth - pad[2] - bor[2], + child.layout.width, + wrapReverse, + ) + mL + } + + let top: number + if (isDefined(rTop)) { + top = bor[1] + rTop + mT + } else if (isDefined(rBottom)) { + top = parentHeight - bor[3] - rBottom - child.layout.height - mB + } else if (mainRow) { + top = + alignAbsolute( + alignment, + pad[1] + bor[1], + parentHeight - pad[3] - bor[3], + child.layout.height, + wrapReverse, + ) + mT + } else { + const lead = pad[1] + bor[1] + const trail = parentHeight - pad[3] - bor[3] + top = reversed + ? trail - child.layout.height - mB + : justifyAbsolute( + parent.style.justifyContent, + lead, + trail, + child.layout.height, + ) + mT + } + + child.layout.left = left + child.layout.top = top +} + +function justifyAbsolute( + justify: Justify, + leadEdge: number, + trailEdge: number, + childSize: number, +): number { + switch (justify) { + case Justify.Center: + return leadEdge + (trailEdge - leadEdge - childSize) / 2 + case Justify.FlexEnd: + return trailEdge - childSize + default: + return leadEdge + } +} + +function alignAbsolute( + align: Align, + leadEdge: number, + trailEdge: number, + childSize: number, + wrapReverse: boolean, +): number { + // Wrap-reverse flips the cross axis: flex-start/stretch go to trailing, + // flex-end goes to leading (yoga's absoluteLayoutChild flips the align value + // when the containing block has wrap-reverse). + switch (align) { + case Align.Center: + return leadEdge + (trailEdge - leadEdge - childSize) / 2 + case Align.FlexEnd: + return wrapReverse ? leadEdge : trailEdge - childSize + default: + return wrapReverse ? trailEdge - childSize : leadEdge + } +} + +function computeFlexBasis( + child: Node, + mainAxis: FlexDirection, + availableMain: number, + availableCross: number, + crossMode: MeasureMode, + ownerWidth: number, + ownerHeight: number, +): number { + // Same-generation cache hit: basis was computed THIS calculateLayout, so + // it's fresh regardless of isDirty_. Covers both clean children (scrolling + // past unchanged messages) AND fresh-mounted dirty children (virtual + // scroll mounts new items — the dirty chain's measure→layout cascade + // invokes this ≥2^depth times, but the child's subtree doesn't change + // between calls within one calculateLayout). For clean children with + // cache from a PREVIOUS generation, also hit if inputs match — isDirty_ + // gates since a dirty child's previous-gen cache is stale. + const sameGen = child._fbGen === _generation + if ( + (sameGen || !child.isDirty_) && + child._fbCrossMode === crossMode && + sameFloat(child._fbOwnerW, ownerWidth) && + sameFloat(child._fbOwnerH, ownerHeight) && + sameFloat(child._fbAvailMain, availableMain) && + sameFloat(child._fbAvailCross, availableCross) + ) { + return child._fbBasis + } + const cs = child.style + const isMainRow = isRow(mainAxis) + + // Explicit flex-basis + const basis = resolveValue(cs.flexBasis, availableMain) + if (isDefined(basis)) { + const b = Math.max(0, basis) + child._fbBasis = b + child._fbOwnerW = ownerWidth + child._fbOwnerH = ownerHeight + child._fbAvailMain = availableMain + child._fbAvailCross = availableCross + child._fbCrossMode = crossMode + child._fbGen = _generation + return b + } + + // Style dimension on main axis + const mainStyleDim = isMainRow ? cs.width : cs.height + const mainOwner = isMainRow ? ownerWidth : ownerHeight + const resolved = resolveValue(mainStyleDim, mainOwner) + if (isDefined(resolved)) { + const b = Math.max(0, resolved) + child._fbBasis = b + child._fbOwnerW = ownerWidth + child._fbOwnerH = ownerHeight + child._fbAvailMain = availableMain + child._fbAvailCross = availableCross + child._fbCrossMode = crossMode + child._fbGen = _generation + return b + } + + // Need to measure the child to get its natural size + const crossStyleDim = isMainRow ? cs.height : cs.width + const crossOwner = isMainRow ? ownerHeight : ownerWidth + let crossConstraint = resolveValue(crossStyleDim, crossOwner) + let crossConstraintMode: MeasureMode = isDefined(crossConstraint) + ? MeasureMode.Exactly + : MeasureMode.Undefined + if (!isDefined(crossConstraint) && isDefined(availableCross)) { + crossConstraint = availableCross + crossConstraintMode = + crossMode === MeasureMode.Exactly && isStretchAlign(child) + ? MeasureMode.Exactly + : MeasureMode.AtMost + } + + // Upstream yoga (YGNodeComputeFlexBasisForChild) passes the available inner + // width with mode AtMost when the subtree will call a measure-func — so text + // nodes don't report unconstrained intrinsic width as flex-basis, which + // would force siblings to shrink and the text to wrap at the wrong width. + // Passing Undefined here made Ink's inside get + // width = intrinsic instead of available, dropping chars at wrap boundaries. + // + // Two constraints on when this applies: + // - Width only. Height is never constrained during basis measurement — + // column containers must measure children at natural height so + // scrollable content can overflow (constraining height clips ScrollBox). + // - Subtree has a measure-func. Pure layout subtrees (no measure-func) + // with flex-grow children would grow into the AtMost constraint, + // inflating the basis (breaks YGMinMaxDimensionTest flex_grow_in_at_most + // where a flexGrow:1 child should stay at basis 0, not grow to 100). + let mainConstraint = NaN + let mainConstraintMode: MeasureMode = MeasureMode.Undefined + if (isMainRow && isDefined(availableMain) && hasMeasureFuncInSubtree(child)) { + mainConstraint = availableMain + mainConstraintMode = MeasureMode.AtMost + } + + const mw = isMainRow ? mainConstraint : crossConstraint + const mh = isMainRow ? crossConstraint : mainConstraint + const mwMode = isMainRow ? mainConstraintMode : crossConstraintMode + const mhMode = isMainRow ? crossConstraintMode : mainConstraintMode + + layoutNode(child, mw, mh, mwMode, mhMode, ownerWidth, ownerHeight, false) + const b = isMainRow ? child.layout.width : child.layout.height + child._fbBasis = b + child._fbOwnerW = ownerWidth + child._fbOwnerH = ownerHeight + child._fbAvailMain = availableMain + child._fbAvailCross = availableCross + child._fbCrossMode = crossMode + child._fbGen = _generation + return b +} + +function hasMeasureFuncInSubtree(node: Node): boolean { + if (node.measureFunc) return true + for (const c of node.children) { + if (hasMeasureFuncInSubtree(c)) return true + } + return false +} + +function resolveFlexibleLengths( + children: Node[], + availableInnerMain: number, + totalFlexBasis: number, + isMainRow: boolean, + ownerW: number, + ownerH: number, +): void { + // Multi-pass flex distribution per CSS flexbox spec §9.7 "Resolving Flexible + // Lengths": distribute free space, detect min/max violations, freeze all + // violators, redistribute among unfrozen children. Repeat until stable. + const n = children.length + const frozen: boolean[] = new Array(n).fill(false) + const initialFree = isDefined(availableInnerMain) + ? availableInnerMain - totalFlexBasis + : 0 + // Freeze inflexible items at their clamped basis + for (let i = 0; i < n; i++) { + const c = children[i]! + const clamped = boundAxis(c.style, isMainRow, c._flexBasis, ownerW, ownerH) + const inflexible = + !isDefined(availableInnerMain) || + (initialFree >= 0 ? c.style.flexGrow === 0 : c.style.flexShrink === 0) + if (inflexible) { + c._mainSize = Math.max(0, clamped) + frozen[i] = true + } else { + c._mainSize = c._flexBasis + } + } + // Iteratively distribute until no violations. Free space is recomputed each + // pass: initial free space minus the delta frozen children consumed beyond + // (or below) their basis. + const unclamped: number[] = new Array(n) + for (let iter = 0; iter <= n; iter++) { + let frozenDelta = 0 + let totalGrow = 0 + let totalShrinkScaled = 0 + let unfrozenCount = 0 + for (let i = 0; i < n; i++) { + const c = children[i]! + if (frozen[i]) { + frozenDelta += c._mainSize - c._flexBasis + } else { + totalGrow += c.style.flexGrow + totalShrinkScaled += c.style.flexShrink * c._flexBasis + unfrozenCount++ + } + } + if (unfrozenCount === 0) break + let remaining = initialFree - frozenDelta + // Spec §9.7 step 4c: if sum of flex factors < 1, only distribute + // initialFree × sum, not the full remaining space (partial flex). + if (remaining > 0 && totalGrow > 0 && totalGrow < 1) { + const scaled = initialFree * totalGrow + if (scaled < remaining) remaining = scaled + } else if (remaining < 0 && totalShrinkScaled > 0) { + let totalShrink = 0 + for (let i = 0; i < n; i++) { + if (!frozen[i]) totalShrink += children[i]!.style.flexShrink + } + if (totalShrink < 1) { + const scaled = initialFree * totalShrink + if (scaled > remaining) remaining = scaled + } + } + // Compute targets + violations for all unfrozen children + let totalViolation = 0 + for (let i = 0; i < n; i++) { + if (frozen[i]) continue + const c = children[i]! + let t = c._flexBasis + if (remaining > 0 && totalGrow > 0) { + t += (remaining * c.style.flexGrow) / totalGrow + } else if (remaining < 0 && totalShrinkScaled > 0) { + t += + (remaining * (c.style.flexShrink * c._flexBasis)) / totalShrinkScaled + } + unclamped[i] = t + const clamped = Math.max( + 0, + boundAxis(c.style, isMainRow, t, ownerW, ownerH), + ) + c._mainSize = clamped + totalViolation += clamped - t + } + // Freeze per spec §9.7 step 5: if totalViolation is zero freeze all; if + // positive freeze min-violators; if negative freeze max-violators. + if (totalViolation === 0) break + let anyFrozen = false + for (let i = 0; i < n; i++) { + if (frozen[i]) continue + const v = children[i]!._mainSize - unclamped[i]! + if ((totalViolation > 0 && v > 0) || (totalViolation < 0 && v < 0)) { + frozen[i] = true + anyFrozen = true + } + } + if (!anyFrozen) break + } +} + +function isStretchAlign(child: Node): boolean { + const p = child.parent + if (!p) return false + const align = + child.style.alignSelf === Align.Auto + ? p.style.alignItems + : child.style.alignSelf + return align === Align.Stretch +} + +function resolveChildAlign(parent: Node, child: Node): Align { + return child.style.alignSelf === Align.Auto + ? parent.style.alignItems + : child.style.alignSelf +} + +// Baseline of a node per CSS Flexbox §8.5 / yoga's YGBaseline. Leaf nodes +// (no children) use their own height. Containers recurse into the first +// baseline-aligned child on the first line (or the first flow child if none +// are baseline-aligned), returning that child's baseline + its top offset. +function calculateBaseline(node: Node): number { + let baselineChild: Node | null = null + for (const c of node.children) { + if (c._lineIndex > 0) break + if (c.style.positionType === PositionType.Absolute) continue + if (c.style.display === Display.None) continue + if ( + resolveChildAlign(node, c) === Align.Baseline || + c.isReferenceBaseline_ + ) { + baselineChild = c + break + } + if (baselineChild === null) baselineChild = c + } + if (baselineChild === null) return node.layout.height + return calculateBaseline(baselineChild) + baselineChild.layout.top +} + +// A container uses baseline layout only for row direction, when either +// align-items is baseline or any flow child has align-self: baseline. +function isBaselineLayout(node: Node, flowChildren: Node[]): boolean { + if (!isRow(node.style.flexDirection)) return false + if (node.style.alignItems === Align.Baseline) return true + for (const c of flowChildren) { + if (c.style.alignSelf === Align.Baseline) return true + } + return false +} + +function childMarginForAxis( + child: Node, + axis: FlexDirection, + ownerWidth: number, +): number { + if (!child._hasMargin) return 0 + const lead = resolveEdge(child.style.margin, leadingEdge(axis), ownerWidth) + const trail = resolveEdge(child.style.margin, trailingEdge(axis), ownerWidth) + return lead + trail +} + +function resolveGap(style: Style, gutter: Gutter, ownerSize: number): number { + let v = style.gap[gutter]! + if (v.unit === Unit.Undefined) v = style.gap[Gutter.All]! + const r = resolveValue(v, ownerSize) + return isDefined(r) ? Math.max(0, r) : 0 +} + +function boundAxis( + style: Style, + isWidth: boolean, + value: number, + ownerWidth: number, + ownerHeight: number, +): number { + const minV = isWidth ? style.minWidth : style.minHeight + const maxV = isWidth ? style.maxWidth : style.maxHeight + const minU = minV.unit + const maxU = maxV.unit + // Fast path: no min/max constraints set. Per CPU profile this is the + // overwhelmingly common case (~32k calls/layout on the 1000-node bench, + // nearly all with undefined min/max) — skipping 2× resolveValue + 2× isNaN + // that always no-op. Unit.Undefined = 0. + if (minU === 0 && maxU === 0) return value + const owner = isWidth ? ownerWidth : ownerHeight + let v = value + // Inlined resolveValue: Unit.Point=1, Unit.Percent=2. `m === m` is !isNaN. + if (maxU === 1) { + if (v > maxV.value) v = maxV.value + } else if (maxU === 2) { + const m = (maxV.value * owner) / 100 + if (m === m && v > m) v = m + } + if (minU === 1) { + if (v < minV.value) v = minV.value + } else if (minU === 2) { + const m = (minV.value * owner) / 100 + if (m === m && v < m) v = m + } + return v +} + +function zeroLayoutRecursive(node: Node): void { + for (const c of node.children) { + c.layout.left = 0 + c.layout.top = 0 + c.layout.width = 0 + c.layout.height = 0 + // Invalidate layout cache — without this, unhide → calculateLayout finds + // the child clean (!isDirty_) with _hasL intact, hits the cache at line + // ~1086, restores stale _lOutW/_lOutH, and returns early — skipping the + // child-positioning recursion. Grandchildren stay at (0,0,0,0) from the + // zeroing above and render invisible. isDirty_=true also gates _cN and + // _fbBasis via their (sameGen || !isDirty_) checks — _cGen/_fbGen freeze + // during hide so sameGen is false on unhide. + c.isDirty_ = true + c._hasL = false + c._hasM = false + zeroLayoutRecursive(c) + } +} + +function collectLayoutChildren(node: Node, flow: Node[], abs: Node[]): void { + // Partition a node's children into flow and absolute lists, flattening + // display:contents subtrees so their children are laid out as direct + // children of this node (per CSS display:contents spec — the box is removed + // from the layout tree but its children remain, lifted to the grandparent). + for (const c of node.children) { + const disp = c.style.display + if (disp === Display.None) { + c.layout.left = 0 + c.layout.top = 0 + c.layout.width = 0 + c.layout.height = 0 + zeroLayoutRecursive(c) + } else if (disp === Display.Contents) { + c.layout.left = 0 + c.layout.top = 0 + c.layout.width = 0 + c.layout.height = 0 + // Recurse — nested display:contents lifts all the way up. The contents + // node's own margin/padding/position/dimensions are ignored. + collectLayoutChildren(c, flow, abs) + } else if (c.style.positionType === PositionType.Absolute) { + abs.push(c) + } else { + flow.push(c) + } + } +} + +function roundLayout( + node: Node, + scale: number, + absLeft: number, + absTop: number, +): void { + if (scale === 0) return + const l = node.layout + const nodeLeft = l.left + const nodeTop = l.top + const nodeWidth = l.width + const nodeHeight = l.height + + const absNodeLeft = absLeft + nodeLeft + const absNodeTop = absTop + nodeTop + + // Upstream YGRoundValueToPixelGrid: text nodes (has measureFunc) floor their + // positions so wrapped text never starts past its allocated column. Width + // uses ceil-if-fractional to avoid clipping the last glyph. Non-text nodes + // use standard round. Matches yoga's PixelGrid.cpp — without this, justify + // center/space-evenly positions are off-by-one vs WASM and flex-shrink + // overflow places siblings at the wrong column. + const isText = node.measureFunc !== null + l.left = roundValue(nodeLeft, scale, false, isText) + l.top = roundValue(nodeTop, scale, false, isText) + + // Width/height rounded via absolute edges to avoid cumulative drift + const absRight = absNodeLeft + nodeWidth + const absBottom = absNodeTop + nodeHeight + const hasFracW = !isWholeNumber(nodeWidth * scale) + const hasFracH = !isWholeNumber(nodeHeight * scale) + l.width = + roundValue(absRight, scale, isText && hasFracW, isText && !hasFracW) - + roundValue(absNodeLeft, scale, false, isText) + l.height = + roundValue(absBottom, scale, isText && hasFracH, isText && !hasFracH) - + roundValue(absNodeTop, scale, false, isText) + + for (const c of node.children) { + roundLayout(c, scale, absNodeLeft, absNodeTop) + } +} + +function isWholeNumber(v: number): boolean { + const frac = v - Math.floor(v) + return frac < 0.0001 || frac > 0.9999 +} + +function roundValue( + v: number, + scale: number, + forceCeil: boolean, + forceFloor: boolean, +): number { + let scaled = v * scale + let frac = scaled - Math.floor(scaled) + if (frac < 0) frac += 1 + // Float-epsilon tolerance matches upstream YGDoubleEqual (1e-4) + if (frac < 0.0001) { + scaled = Math.floor(scaled) + } else if (frac > 0.9999) { + scaled = Math.ceil(scaled) + } else if (forceCeil) { + scaled = Math.ceil(scaled) + } else if (forceFloor) { + scaled = Math.floor(scaled) + } else { + // Round half-up (>= 0.5 goes up), per upstream + scaled = Math.floor(scaled) + (frac >= 0.4999 ? 1 : 0) + } + return scaled / scale +} + +// -- +// Helpers + +function parseDimension(v: number | string | undefined): Value { + if (v === undefined) return UNDEFINED_VALUE + if (v === 'auto') return AUTO_VALUE + if (typeof v === 'number') { + // WASM yoga's YGFloatIsUndefined treats NaN and ±Infinity as undefined. + // Ink passes height={Infinity} (e.g. LogSelector maxHeight default) and + // expects it to mean "unconstrained" — storing it as a literal point value + // makes the node height Infinity and breaks all downstream layout. + return Number.isFinite(v) ? pointValue(v) : UNDEFINED_VALUE + } + if (typeof v === 'string' && v.endsWith('%')) { + return percentValue(parseFloat(v)) + } + const n = parseFloat(v) + return isNaN(n) ? UNDEFINED_VALUE : pointValue(n) +} + +function physicalEdge(edge: Edge): number { + switch (edge) { + case Edge.Left: + case Edge.Start: + return EDGE_LEFT + case Edge.Top: + return EDGE_TOP + case Edge.Right: + case Edge.End: + return EDGE_RIGHT + case Edge.Bottom: + return EDGE_BOTTOM + default: + return EDGE_LEFT + } +} + +// -- +// Module API matching yoga-layout/load + +export type Yoga = { + Config: { + create(): Config + destroy(config: Config): void + } + Node: { + create(config?: Config): Node + createDefault(): Node + createWithConfig(config: Config): Node + destroy(node: Node): void + } +} + +const YOGA_INSTANCE: Yoga = { + Config: { + create: createConfig, + destroy() {}, + }, + Node: { + create: (config?: Config) => new Node(config), + createDefault: () => new Node(), + createWithConfig: (config: Config) => new Node(config), + destroy() {}, + }, +} + +export function loadYoga(): Promise { + return Promise.resolve(YOGA_INSTANCE) +} + +export default YOGA_INSTANCE diff --git a/outputStyles/loadOutputStylesDir.ts b/outputStyles/loadOutputStylesDir.ts new file mode 100644 index 0000000..5390c66 --- /dev/null +++ b/outputStyles/loadOutputStylesDir.ts @@ -0,0 +1,98 @@ +import memoize from 'lodash-es/memoize.js' +import { basename } from 'path' +import type { OutputStyleConfig } from '../constants/outputStyles.js' +import { logForDebugging } from '../utils/debug.js' +import { coerceDescriptionToString } from '../utils/frontmatterParser.js' +import { logError } from '../utils/log.js' +import { + extractDescriptionFromMarkdown, + loadMarkdownFilesForSubdir, +} from '../utils/markdownConfigLoader.js' +import { clearPluginOutputStyleCache } from '../utils/plugins/loadPluginOutputStyles.js' + +/** + * Loads markdown files from .claude/output-styles directories throughout the project + * and from ~/.claude/output-styles directory and converts them to output styles. + * + * Each filename becomes a style name, and the file content becomes the style prompt. + * The frontmatter provides name and description. + * + * Structure: + * - Project .claude/output-styles/*.md -> project styles + * - User ~/.claude/output-styles/*.md -> user styles (overridden by project styles) + * + * @param cwd Current working directory for project directory traversal + */ +export const getOutputStyleDirStyles = memoize( + async (cwd: string): Promise => { + try { + const markdownFiles = await loadMarkdownFilesForSubdir( + 'output-styles', + cwd, + ) + + const styles = markdownFiles + .map(({ filePath, frontmatter, content, source }) => { + try { + const fileName = basename(filePath) + const styleName = fileName.replace(/\.md$/, '') + + // Get style configuration from frontmatter + const name = (frontmatter['name'] || styleName) as string + const description = + coerceDescriptionToString( + frontmatter['description'], + styleName, + ) ?? + extractDescriptionFromMarkdown( + content, + `Custom ${styleName} output style`, + ) + + // Parse keep-coding-instructions flag (supports both boolean and string values) + const keepCodingInstructionsRaw = + frontmatter['keep-coding-instructions'] + const keepCodingInstructions = + keepCodingInstructionsRaw === true || + keepCodingInstructionsRaw === 'true' + ? true + : keepCodingInstructionsRaw === false || + keepCodingInstructionsRaw === 'false' + ? false + : undefined + + // Warn if force-for-plugin is set on non-plugin output style + if (frontmatter['force-for-plugin'] !== undefined) { + logForDebugging( + `Output style "${name}" has force-for-plugin set, but this option only applies to plugin output styles. Ignoring.`, + { level: 'warn' }, + ) + } + + return { + name, + description, + prompt: content.trim(), + source, + keepCodingInstructions, + } + } catch (error) { + logError(error) + return null + } + }) + .filter(style => style !== null) + + return styles + } catch (error) { + logError(error) + return [] + } + }, +) + +export function clearOutputStyleCaches(): void { + getOutputStyleDirStyles.cache?.clear?.() + loadMarkdownFilesForSubdir.cache?.clear?.() + clearPluginOutputStyleCache() +} diff --git a/plugins/builtinPlugins.ts b/plugins/builtinPlugins.ts new file mode 100644 index 0000000..fd33956 --- /dev/null +++ b/plugins/builtinPlugins.ts @@ -0,0 +1,159 @@ +/** + * Built-in Plugin Registry + * + * Manages built-in plugins that ship with the CLI and can be enabled/disabled + * by users via the /plugin UI. + * + * Built-in plugins differ from bundled skills (src/skills/bundled/) in that: + * - They appear in the /plugin UI under a "Built-in" section + * - Users can enable/disable them (persisted to user settings) + * - They can provide multiple components (skills, hooks, MCP servers) + * + * Plugin IDs use the format `{name}@builtin` to distinguish them from + * marketplace plugins (`{name}@{marketplace}`). + */ + +import type { Command } from '../commands.js' +import type { BundledSkillDefinition } from '../skills/bundledSkills.js' +import type { BuiltinPluginDefinition, LoadedPlugin } from '../types/plugin.js' +import { getSettings_DEPRECATED } from '../utils/settings/settings.js' + +const BUILTIN_PLUGINS: Map = new Map() + +export const BUILTIN_MARKETPLACE_NAME = 'builtin' + +/** + * Register a built-in plugin. Call this from initBuiltinPlugins() at startup. + */ +export function registerBuiltinPlugin( + definition: BuiltinPluginDefinition, +): void { + BUILTIN_PLUGINS.set(definition.name, definition) +} + +/** + * Check if a plugin ID represents a built-in plugin (ends with @builtin). + */ +export function isBuiltinPluginId(pluginId: string): boolean { + return pluginId.endsWith(`@${BUILTIN_MARKETPLACE_NAME}`) +} + +/** + * Get a specific built-in plugin definition by name. + * Useful for the /plugin UI to show the skills/hooks/MCP list without + * a marketplace lookup. + */ +export function getBuiltinPluginDefinition( + name: string, +): BuiltinPluginDefinition | undefined { + return BUILTIN_PLUGINS.get(name) +} + +/** + * Get all registered built-in plugins as LoadedPlugin objects, split into + * enabled/disabled based on user settings (with defaultEnabled as fallback). + * Plugins whose isAvailable() returns false are omitted entirely. + */ +export function getBuiltinPlugins(): { + enabled: LoadedPlugin[] + disabled: LoadedPlugin[] +} { + const settings = getSettings_DEPRECATED() + const enabled: LoadedPlugin[] = [] + const disabled: LoadedPlugin[] = [] + + for (const [name, definition] of BUILTIN_PLUGINS) { + if (definition.isAvailable && !definition.isAvailable()) { + continue + } + + const pluginId = `${name}@${BUILTIN_MARKETPLACE_NAME}` + const userSetting = settings?.enabledPlugins?.[pluginId] + // Enabled state: user preference > plugin default > true + const isEnabled = + userSetting !== undefined + ? userSetting === true + : (definition.defaultEnabled ?? true) + + const plugin: LoadedPlugin = { + name, + manifest: { + name, + description: definition.description, + version: definition.version, + }, + path: BUILTIN_MARKETPLACE_NAME, // sentinel — no filesystem path + source: pluginId, + repository: pluginId, + enabled: isEnabled, + isBuiltin: true, + hooksConfig: definition.hooks, + mcpServers: definition.mcpServers, + } + + if (isEnabled) { + enabled.push(plugin) + } else { + disabled.push(plugin) + } + } + + return { enabled, disabled } +} + +/** + * Get skills from enabled built-in plugins as Command objects. + * Skills from disabled plugins are not returned. + */ +export function getBuiltinPluginSkillCommands(): Command[] { + const { enabled } = getBuiltinPlugins() + const commands: Command[] = [] + + for (const plugin of enabled) { + const definition = BUILTIN_PLUGINS.get(plugin.name) + if (!definition?.skills) continue + for (const skill of definition.skills) { + commands.push(skillDefinitionToCommand(skill)) + } + } + + return commands +} + +/** + * Clear built-in plugins registry (for testing). + */ +export function clearBuiltinPlugins(): void { + BUILTIN_PLUGINS.clear() +} + +// -- + +function skillDefinitionToCommand(definition: BundledSkillDefinition): Command { + return { + type: 'prompt', + name: definition.name, + description: definition.description, + hasUserSpecifiedDescription: true, + allowedTools: definition.allowedTools ?? [], + argumentHint: definition.argumentHint, + whenToUse: definition.whenToUse, + model: definition.model, + disableModelInvocation: definition.disableModelInvocation ?? false, + userInvocable: definition.userInvocable ?? true, + contentLength: 0, + // 'bundled' not 'builtin' — 'builtin' in Command.source means hardcoded + // slash commands (/help, /clear). Using 'bundled' keeps these skills in + // the Skill tool's listing, analytics name logging, and prompt-truncation + // exemption. The user-toggleable aspect is tracked on LoadedPlugin.isBuiltin. + source: 'bundled', + loadedFrom: 'bundled', + hooks: definition.hooks, + context: definition.context, + agent: definition.agent, + isEnabled: definition.isEnabled ?? (() => true), + isHidden: !(definition.userInvocable ?? true), + progressMessage: 'running', + getPromptForCommand: definition.getPromptForCommand, + } +} diff --git a/plugins/bundled/index.ts b/plugins/bundled/index.ts new file mode 100644 index 0000000..85dda65 --- /dev/null +++ b/plugins/bundled/index.ts @@ -0,0 +1,23 @@ +/** + * Built-in Plugin Initialization + * + * Initializes built-in plugins that ship with the CLI and appear in the + * /plugin UI for users to enable/disable. + * + * Not all bundled features should be built-in plugins — use this for + * features that users should be able to explicitly enable/disable. For + * features with complex setup or automatic-enabling logic (e.g. + * claude-in-chrome), use src/skills/bundled/ instead. + * + * To add a new built-in plugin: + * 1. Import registerBuiltinPlugin from '../builtinPlugins.js' + * 2. Call registerBuiltinPlugin() with the plugin definition here + */ + +/** + * Initialize built-in plugins. Called during CLI startup. + */ +export function initBuiltinPlugins(): void { + // No built-in plugins registered yet — this is the scaffolding for + // migrating bundled skills that should be user-toggleable. +} diff --git a/projectOnboardingState.ts b/projectOnboardingState.ts new file mode 100644 index 0000000..4c71b90 --- /dev/null +++ b/projectOnboardingState.ts @@ -0,0 +1,83 @@ +import memoize from 'lodash-es/memoize.js' +import { join } from 'path' +import { + getCurrentProjectConfig, + saveCurrentProjectConfig, +} from './utils/config.js' +import { getCwd } from './utils/cwd.js' +import { isDirEmpty } from './utils/file.js' +import { getFsImplementation } from './utils/fsOperations.js' + +export type Step = { + key: string + text: string + isComplete: boolean + isCompletable: boolean + isEnabled: boolean +} + +export function getSteps(): Step[] { + const hasClaudeMd = getFsImplementation().existsSync( + join(getCwd(), 'CLAUDE.md'), + ) + const isWorkspaceDirEmpty = isDirEmpty(getCwd()) + + return [ + { + key: 'workspace', + text: 'Ask Claude to create a new app or clone a repository', + isComplete: false, + isCompletable: true, + isEnabled: isWorkspaceDirEmpty, + }, + { + key: 'claudemd', + text: 'Run /init to create a CLAUDE.md file with instructions for Claude', + isComplete: hasClaudeMd, + isCompletable: true, + isEnabled: !isWorkspaceDirEmpty, + }, + ] +} + +export function isProjectOnboardingComplete(): boolean { + return getSteps() + .filter(({ isCompletable, isEnabled }) => isCompletable && isEnabled) + .every(({ isComplete }) => isComplete) +} + +export function maybeMarkProjectOnboardingComplete(): void { + // Short-circuit on cached config — isProjectOnboardingComplete() hits + // the filesystem, and REPL.tsx calls this on every prompt submit. + if (getCurrentProjectConfig().hasCompletedProjectOnboarding) { + return + } + if (isProjectOnboardingComplete()) { + saveCurrentProjectConfig(current => ({ + ...current, + hasCompletedProjectOnboarding: true, + })) + } +} + +export const shouldShowProjectOnboarding = memoize((): boolean => { + const projectConfig = getCurrentProjectConfig() + // Short-circuit on cached config before isProjectOnboardingComplete() + // hits the filesystem — this runs during first render. + if ( + projectConfig.hasCompletedProjectOnboarding || + projectConfig.projectOnboardingSeenCount >= 4 || + process.env.IS_DEMO + ) { + return false + } + + return !isProjectOnboardingComplete() +}) + +export function incrementProjectOnboardingSeenCount(): void { + saveCurrentProjectConfig(current => ({ + ...current, + projectOnboardingSeenCount: current.projectOnboardingSeenCount + 1, + })) +} diff --git a/public/claude-files.png b/public/claude-files.png new file mode 100644 index 0000000000000000000000000000000000000000..bf19df7695bbf2eb7e5633e9cc8587ab23bef38d GIT binary patch literal 738607 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 z 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
8bd)@N!!=3#6GXOVA|?83b|c&7eDUY+0Yi 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%0D4now^w0Fa0&VMDG)d3!%qd!Q zA#cM1F!VK3sj{a|CMcgkW5h!22H^c#R`wEE`nah`?JXH{0UPZwCW9}@7ZikA67?3l zmi)@njp!D+idK-Nk*Xt*g%4&gDMF3jMEzwP9_+Ph5aJXu4oY`SEtAT^(~?G4>SgcN zodIl>b3f_^k+(Gk-_tVsMM6Zl z2V~egK5WU*QUmX<*uPx|OBV}}RM2cVO$fJ002w$Rd~8U1Brf|8D1ZS84ar(qYzk^> z(!m~LiZwe-;);s> zz{CS#7GP>((hBmU4wOzZ0KzCpS3^@OXu>=S= zIzkmwU@id~;t&TGRO4u-J1Kva>EfvBsAH3FD_=zF&OW#^f|(3}w$;!@0L~s{Yc@ID zN*TTe>-d85M!Q0)I*?=G0Qu$1muJgYo*OplgMkRdQdEi-WaTnzs%|$d1Mo5t0|f}SbQpv|2XlNQ+tH6DeMmwc6tLH@ zsmc#KA6@IaB?zJnM6T0-xfva!VI-Z_jn~Pt$_fG=CjNUT5jMs3%eFM005^poRbfOU6|5VHLF-}zVVIer^3 zbx^}GHWp-tE>e!0i#lplglw=re9bTr-&`EmW`R(@=x&ll^a9&56ce1i9VOfCO;ot3T;O2k^0pB;ox?5Kd z`j0!1{S`dKy$gK>X^l|LV$y|j>Cc(}nNo;+hLQ!oYv~zf_zFH4dB?W{&1$8P>B;%O`X20 z>EyS@YD=^2TO^d&l+w~tP9{>iZ{6!qw*nGrnX?lw?9CdUO#OZ7jntge>*y3f9X&v( zm{>SyPvx31-_w%Q<4#c@l7{$sEMczAL0n3|u%sj!sQQ^bDPRI+d&K{En?p%)=G~KG z2#Hd~r7?k3=!9#Q3dV^S#;Y)AfoC_x3GzE=zDHc_jLruY%drEf9@l6c6f^X5;BJT| zIP4SeRs=RBH~x#ET(gc`ylEit%zgQ0nZPT~lvq$(4B?P+vQ=4D%A|}gomcQV-s?%8 zUWqp=#5{eWoQ1(^T_i+3ZLr3ZJ4N9BJ&h|oX1`jdJ`1*aL%l>geCqWh{>oE^Led(MhHIf4 zf9|qp;f<~v8+h;Z~zVtC(Uyel@}X^QltA?spRaKHyq7&CfeZVquJQ@zNz8D$-c;OH|42Ms#UQ z7pee{kOnI-oUPWaGZM39)}cR;1p3k-JOU3cCYbq@_*809h4b~LkzA#}hgHmb!S48A>53e=O)k1(1o>(%Xh-osi zM_T8*K|E{^DVqD9FMUz3jaR$nOz>ofq38;Rjcxzc+vR?#!ku9#=p<{K3`y zy=#_O9VOFAM|9B)(SaWksh5uVk1vdL6L^YTFyYg$+#mq>6bjKGe4)-Ahf%OJoJnH}

;Qc#F7{h5kr2CegI{U0w& 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 gd;Rwb8>&-6{=eG%R* zU+X9Z1)lE|r!fvqz??9H^+Yy08s^bV_fMo~MS^<+j6Ro-|3RvC(BJqRG$X30qcvw1 zAJ@hMT08mw&&mv>olNn$j={D&Br6$sc~O4&@B!hYgLw2oGr1auq|i&@=HZHB`V0s! zejy=UfU=20?g!XHZmu5B-o1-qk*(PL>X2L={Lg@xc;Ck+9w71I1_o69_4RiZH^&Bm zLDF$_xDUa$k&$bE!l%PzJioX7su@l&On6=UCUWt`fJl$?zds5<5P)M6QW3JKw4?k+ z{;=j{DIZR*DX-$!53bxtUd1o)u9;Tp?O@ymhvi&vreu^Om-dF=yThAPP{{kWIzSRW zR2{hU03%cwFnkm61)!)nIN$!yQV)P9WHt>4FJu_(<_6y!;gE7-;Oo~XRLr3$#_QYT z2RnjWMgnc2|GxJ?rsU`EU-teU31E9q`W_q!NN$)&o;|w>U}N|VzNx#rpEVtN^tWPZ zzM15Yi=EH?^*C`~=Ol&T?ayPAec;Z-Vz0x&n4AzmE+c#A0ry#xUqPbqXuwgYKtS-d z{lMvH2A@Abn(TvuGAk-XuZPY%!3_fKK#@Fl;H0g3^QJ9{?dnd~S*3Nd z0<9^q-?1wAJ~8T4#yG*H{J8w;L-+lNeS|Q-vho5*bVbl=LBi3HcaCLrXru;LwxoS6 z-Juk(1>A>oJU6`OyPZ6&YRPhxj!(i0bVCd}0_jXmhYCLY&hxuBf_eFS@^y??=zcbs z2+zq=5(2;)3mBvg7-#`#4J57~wudjm+h;%10J#~`PJqP%TimM;+sqY6|04j1#M>sLW?Sa_<Rlc_J>83Jj=wy#7K|r zio30YqazW)rxw6!;L$|DN=k>1l(?Q6K!!!ZePkQ-o!ekd1LtB|^VVtP+Rvoef#t3? z$S+b$ss)DQbZTm{fjcuXyJ@yZ^#64s%;gH}vH@e{!fUb5V;?D9Kt1Os&SA zYNnCQt2VsG!osrT>b1DLn7atTF4|LNs>08nWC&fnXgAlP52(poll5p}1_no3&};D;o_>oNM) z1ZQ@i@AmHt07mMCa!0xF>y9@K4GqO5Bp{gpVWB?j+O?Bvt!fCXO#a`*Yp6;0`!j(p zd0qmPg*{yu(TK`0C0UiGPqJTE`7s|Prl{C_r*V_*n!Qi~_aC{2yoB-ndDQzOw!2_ody#OEjC-UItSPU8#$?!AWTr3C&x} zRi0L)9}en9?%6>tjiz8G-P{TNL}Y`cn<2Um8Iqx~G41~F@Sf_@vNAiUq3Om8I=hqv zZN8Mnj=oF!mcbPj61#Vi2&K3I)jF6%7y$Z|Gn~ zQdH0`3qI-Mc_(oHfr?;V8<^5xAqr>!_%BctEe0JSFjRsxu~U#!<`O9L>FG~$bD`R^ zs#46>sClYBAl^to*m=Af3ukj&!85lzd?wW>x}C9dqxTU;o_G9jBQ;d8kRugYQ1GNa zM0A-(Mu8nZMaX`a*N~Cz06^Y!q4cI^4grJo_V<#GXS+s%OBKXr3(M<#G;z-a$wB)J z6pj`qdm->z85jE6^SER6WR1hz$?&Z{ug`;n{WGmGF#dGD=?e#y*1vx#Ks-H>BezcF zK2i?UFo&PSfMz22Kv6qvJMAbR_f@|^h3M195v6*SIgH<)X$WBrQb{(tv-~p}AmI<> z>qKFGFbXji#-`Zi`jE}+DSL14hX8VE{98OV8>4i6(@rg7ehQmo7x||dV!BMj=e{% zjKK)Lz=MF%G|;qXevE_H5Lk(f95cV_Q1s9gQrCzO*;WfX2QE4<{`~zD2| D+M)j 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�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

b}kMx3vDyV7hziU(`!iTXFVfR901@NZpEMq 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�c=me1d@S$0C<0@zQC68cJI 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

*kC)H^4($I6U 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⪻L*KvE!ch0`L zgGyERSckrO3;4rej7}X_)LQJmbP<7Yj_>fnjzRX3yhc#?eKl@1cCEv`4^`2&e)NJ<9!V$|rY7-CwpKk^D?d(OSJfw?
KvpdfQyAD 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
GXTb>9#4IYO+S1#c_v@fC>JE68j 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 z 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 E2X0uok^lez literal 0 HcmV?d00001 diff --git a/public/leak-tweet.png b/public/leak-tweet.png new file mode 100644 index 0000000000000000000000000000000000000000..35da39c52f340f844ee68587784ef13202eca191 GIT binary patch literal 416660 zcmd42XH-+&w>JuiqS6#ZI;bc`y7U?m1pxsC=_M*4z4sag0g)!X6A=OF(mN3lLJvLk z9wIeBfDlNz;d%b&y!XTTaKD^!-!Zb2oi$cg_L_6e_M3A?z0^^sz0P)>jEsy{jyac4AZbNb3h z>T3~ivLD}SkhCIZPe-XfU1bpSS)IP(WOb)wy2t02ifMqp>eyaMgYeji$6WFfx9#3S zAab!~HpNFA5qvsGwVUAy%a-$ezoCHghXqRhes+UW<>IAwZ2^4eYUn>No~Wxj{|r*T zeSNb-;NQ`hhxcSJ|1pqjEJMbfm->Fa}|MQMFYW}+k$(Dv|NOa@Z@~A z6~&%`)R|fAo?vW^4-Jhy0J!748@fHYh z>3B0zURE}!2@-We{S5ac;IKgiG#I&&<=S(Zf$p!oqzvcAHW0@_D>~(VkZ6;w6v)NC z6cje)muv<(dr*>=8&Sc`aOd$)(U105?)-Eh?#F`$0Yo&Q#(Ue4xI+2X&yKCbUhC?R zeAwxtOw?`&lQ|T;-I$!3BH_O!*||bNthjqKR!z1M#JymVL$Dxix|1sTjaY9T+ybi0 z6+8jiR_ajTl_ef<6B9&(R;bAuE286c$`o{d-s#O!SXzbwj<0F%$m$P@X+7k78$pfT zjfgcz&$Kih#R0ncA9i*8n3#GUOY0`-O+f}Ep24x(q9NjI>7ioQUP=8hWo^5ujN%`E zI-F~9`O=}#t?LK7C>#|gWD_Ol_4lP3D~d@icx;5ke?LTu(djBP zf^GpZku`$=v?Cw6d*-v>?vF58CL856Cn%m2>-h1ALA{;#<^nV8H}5hpgA% zc+<$BGV`V{4bskq#RVxy8J8mzA^%3YSlSl_LRLSFka*y$lz3N~oFWM+u{X@EkDUQN z+8d6%rel^}1>jzB>$n!JO;+@M{Zo1V1yfqU#rj2ucp^@8i{1BNLNs>+xYrUdx)3Nz z4>;tnY(0_p7H$92gv_OWeCNjcIS~tNJ?s}mnN;=JQ4eodw8Sw3gAxz}X=}|Qacke~ z*&_1R-i^bj`a3Sg;5LlISRkL@oNMMce*(!-1&MzCJXrCswkq_PrC}9J@wv0!datR<5|5PwY}s zs1Pe&AJ+JtSS#rBydSkeEI;IjK$BZXgjGY*=a1 zMLMv5jK%_^UomEL6HlT3J5|uDS5nA4IMitiw%LO5!8>xb@eq7Nu%#|Ck!HhfHB(5B&b8YY~opTMf_~c_b$P>Ry+@LwE zfpV>_aTERCM=;*Emm&#e9EiEpu>89_hU;SM#0uZ+nqlRv)zx-U9VZZKPTiO0sqlIVCE9jGh*Fcf{ZU5TUuoRa$yGBxswH%As0 zhS*(OJM!ak=~H3n#$vu#8Wv8(MezGfLJ-1}_drjP74(LF_1S9?Ty*?F6{I-~(6*L@ z!-9#dVBBdehutF77j$Y-?-asFO9ihQ87s^2>qG-U`(a7-UWI0~Nlz*YOA<3nG^GQ& zI=j2OgEnBqc@kOJs*FiQJULBL4^I&^a$n3uliAt*)UDIjHh2)Pcqm3TDJ5m@xlq>* zBpiqf>JIAp--AeSFfVwEn=>0-GLmqYVpNwHtv$*Wyr2yjTLdE5^1jG^?&&GPY~iN7 zL|nvtetM79|733=+CEe9s14=W_aKd8WM(uW0om63nR=LHA@=vXG@ba9cSpOsHSeD; z5b>!?L;_~|Ko*E^foz;PR<7rBx22W>LM|~coZK`dcAKUy1oI$4dxknKn+%27%JKfx z8;^WXyoMF6#2Z4+7}IUY3C9G`3LJvFT;Xi>;N5h6ijQO+{&Xc$x4#J8#f`P=_rOeB z#oHLu0D|z|BxQE##`W-;vh3~$4+Oy7K_Sr>0>!F2g2jp8yRe&vlRsS7wPngL>ym-c zW=q5Tg7B}t0l{NG&y(9Sm~_;7mHkAa%cI+^u~)4bBdq^!YjkYNBXq@xC*9^?P_tc~jWiH3k`}(7zL} zV{Gu{;MFwZDJ=oRP0Wb546XyTknH=uvi074lX+myqffzSFoIAepztk=;{LL4)4JXR zY4Uz(OkXeB()!P3WEj12I-44mRKV36(cM2QD{aqDcBkvRq00W$`p6euKKEFjy)A*p z+P*_j^2ULG|M}v;3x82?-#qkogZS$02> zj;uJnK{`;ywKFwH>r+W(h&xZIp$(lB6@|(kcEDTUAo*4(VFgE$UV=CNEc@ON45>`? zzql{Eiz5AIonz0k*~{Lrt)lg?B)hY`sW`(D_6Ibs?dbVs%ly-mw=T~GMQ>d7m@9ID zF^uwgE{2KkjRSZUUF!gHmx>@s`NROJsx$d}l|@}TwzP}0~o-bLM+%^JwGp&{OFnU|DgkLchw@Ix1xX5zf-0iUp zh249yA+g6)m#E=!FD>LE*a1Pb1F1smqufN9@}bC%D)$}Ahy;I4tuzq?H{_%)rZ(@F z2jZcomL|SW^?PGu`d+RJi5*QV&D(Y^4iw*fri2VD%)5_e=6`>>#z;Qh=*}Z$kP@^} zp*ub|M{SuyPQdN8=zl*>Q0E%%Wxh(;bkLJUW*jv8ed30XtE(uuX?t9Nd$?dAmGLU2 z{^sT$8PnQB*}(3Z9}>Z5N6Fids|QJ39F|v*;C^hepcX=mTq+{uDh29T3OdL$@=olR zyM{=h#3MWwL-G_~ccKj$b3u6k)*{fIje@Mt9y?2b(8=bk)k%=%fj_vvmp!}wQ)fX} z)eDEe%{rUudOD>!S}Jevy6}JCq7$pCW?+`_=J~Qia(Mkf)$fPc$hgjDV7L~t5$_yV z5E76s>Hi2>X);t9g4L2%Br5tn@!FawCV*+8zJGs5SdVKtohHdyb3Fe7*_%vW8PKXO z_F}-QVNvJKkg;F40sJru&=xkghx6?3@7Let>-v0c_%fRL$Ep*rI|miiT}(S^iEz@e zGt&}DE!y~@3B6Hr^eqY@?;xwlLdNsK%=m?(`O~6ZkHPxQTE_cNcq{haIf@i+FpQa# z4#&!DX~puf5%AqOdu86Xh|2iyk-D;(ZQyAqv+=JtDryUANl)($i-JSg6kkm>I;H`r zNg5woTwJo=1uqrN1p`i4)Z;jYdy@t_&(I|ESl+Nm;X2*kTqwUL6OxOE5c5fze>Oux z&u|(J3`}`?Zx41o7xjG1d%1ua_#$e#FRAF%FV)RK5&%pZRwkJ#G^ zK-4gp$SF*8Q}!{EXcTd#$6u>jsC?;nW)Nd?E=t%TqjY$Go^-etifC3mJ%Y{n_;KH~ zFd^_Uryf*kRo=6v{MDwDg^#_6R9dZ-%NrV zztz>HROr~;C@v{E#5>r%NSBRbfPIj*+4B1GW#Y4~36`bDsN@mU@bycBavG(~!dD8m zTx8+l&t7Cm_OSj0Q9O|W2*0mb}n`G~h3r~7ZET^9!@iuE&;if(I<^NWaddYdz( zhI7%ae7{-4@vh<{;>uM@&8t*BZz!Xq8bz+rats-`TF+5%eCbk26_twx5)YX<<{Q|} z+2=IB6&T#dR>Fe!Jd11dUK%wdkc0_z_<5g1?)(ZDs|fmc`dW@XH$0ld>spGknw!ZT zS31I|$gYNm4yKFqq~u0Zai7xfV^tT?Y1?X&L3$*KxDBr7w;Fpn2Jvo8ics5XMJL$l z9c!wO)09mu)P}5Gp9P(=-gCt#j?5dp4%!0XP4w?)vf(uRM@L32V)I8s>f7ov`WGoL zCs59@`BdS5D+|F@F!CkF7op%HnDP=i*^`x*YRwhfK`YUqAzjcRBVltJXjIid&d)DI z`K^mFw+qp`qIwh%urcxxe}pORej%?gD>pk!@mM{OUEM6R!}DNv^5xw+uRzxK0>!IJB zpcKSc;Bfc=h8rghz`;<5)4CzqnG$d2qydBu%;^d!B#k==MgFeJcuNN2cZkGcFx=~T zDBStyp|9LGJFo}3L7mi?#0)N)^jn`okGy>uuK%s&ko#Q;!a@|!_IYr7Y4J5!Ebw4U zLbn=GKs%jqHLiTE$BBUH1G*jCaKA%rmYxnegTLD^h5T~zS|A(xt#SX`o#*$*v=nbX zV7bn}ZTtJF1g((m8~pZ<{fQ5{LGS#kpPgmMavMG|?SwmzD-F`e}R($vP z2N2nJSOW$OD}kEg=3LHS8-B&C%keU}n`WZn;>@Z_D3UXJBU)f3`zTNwmKJZd)SmJ8 z1YrFm#RBb{Y)LRmPmy3DrV+z#KkT0d-)~0=o1$76vdI%kTa&7oTg_B;CUZ*G8llUxUj&d zytO4u=M0TIOCmokY-r%@(iqq08~A*6xU>>NK38*Jo^CodHtG3|_>)oig3i5JZJ@6p zGZ+#oN}|E**ir0gL;G+S5d}NN!U>^~?MEh-*xgETucS}*H$-U@`6KLZ9KE5)4)NkJ zSHGn6l!i8&lJ1(Aw~%UnK|Uijr>|gVO4)kZRmH9#gcHW*KrOK8BZy?0*SUz~d4bQ`@oSu3<>^EPD8t z72YfD>xs!0_=eZ`UC+PI!Xm+LR(Qe>6%Ap%Z}zfmvJq#1pC8^{M91ggp)Bc-G7oXS zAmRhE+1c5krYo|Tc~AYj^q_qp)}nP$FuQiyyw&*5t&-6O%1Ooqxzv`c||hRuU$iagadeVWxq?gPnLdx)&0Z(8CNGiZsj~ zUIXBiA?L^R*jWG$@P0TuuCU4YK=#1+Q|L>Uk!xGL&D3)Z?L(-LbD;|Axb5$@*@hll zHwLrCJ*VB}h*&ToN+5~XhQ^m~rqSK1$2vQ&`od9{=uRR<0YziHFVcHS2l(q@>=ANz zcNZawYC<+Xsi<(_A@0)yhgfEH?yo8y_I4GsE@`FFrk_M=RfqItY;4}Yga8Lptor;! z=4|6GoM5l310a*~rpWc<&<-9(V|C0~QbaiQ--=nXD}{~MNVG%Qo}DG5tQ{Q%XRz=Up5zYRF##MU2{Fc!tvuLN?JA)ZwdpLFWHN&0>DZ|&X^ER5Kk z3lG77%CZ4q@jNn@+W5rkdvsFV%R>z^?%f5pV#;3W0&88bjSQr9*o?>(EP`dDQ~!V$E}o#TDJ*p@bJ=oNOC<=%v@*RNmiX8?$v{R7Zn{%BsdEp`?_ zh@vlU23+;GcARXw-dhlE9z1z%D9W$lEg9yFF_L6oyxFOlg*(M0y;S*Jy8RH{@`UXc z`j-r1*W_-CDq;fbT^EN^%N1toimt)E{IPXmPN0_P>M{PcFIpL|Uv+QR*uG?AQ*8wn z?bVfg{qfomGXB7H{p}SmnuU)~DV(SRgnioEGxCLZC*Y_&pOfM;MHB^%q}OX4jJ(zU zDI159Pck1k=uvRdKKY;6lN)nYRc~BX<^JB>%r;k#MRDUk=q{WTnP>sqjpXd9k&dG6onwfiH3#YX zy{~SoP9eKY?Zk4|>P;-NYOW>2_|;&xeG;pgNgTUd6hjeU4BF0loGdE_dh%Ira;;d1 z|4v1Ud!rW8Xzt`?U`J%*uB)2`1C$hdkmFkZRG1`bMx zoPmG_w$?wiF_Xz%jvjj?AOkX-F_3DL9d^g=qu3f8FPfKO`(uoOs$uLbcEIgkzpmHM z{~7h7a~RR;B?L8Knh?-QiMA(-C~1x$Z20_Zz@2^nX}rB zxkVua23;HsHuGx*vs1hj<`VWkZ1aPC*QaWY7}}JZsJ$@bwy1N>laa%{&%&lkj+LtGM z(i8t?3&dWFw4XABptEdoecaD=Ox;eJANB^2iM@I^=B+Z`L9tq)y*+xH6eLlFVE-_- zV#fx&BU`m271AQQYlOM1rD3<&#Xk zyaT`Num3p77dlCtL;z_SZawP$7LA(K$22dXo<<`d{Df3`#WbGYn{Zv~C>2$N!BG}a zgEY&>A5A_ae7Gkuc%+e(Zf>BV()$=LO!B&^)2BcE-K()zni`l3#2oBwq@6}X2o!1N z?ho?5Gl*Sk%&abR_&F&mWnOn{yY9$aFWe;y0F=@yzEMN!+R zQ6>}NJo7j6qYI^2*yTpb&7Ci9Wo?nE`A|PXIMSa`0Qj}>-N4-aIC6v|?d6@vzr}tr zN@r5n7X)b<2#>vb=w*x70XF|9MU|QDwT!VJFLXb6koMud-`b3W@q$X13eU&bFs=D0 ztC?#~3d0cMTtEnJ0adjzF>WFmd|N7oDf!lI2D&Q(4da=M04i5N(8#Hh27N2~q2&+hXzw-LnL+Z4HKIHd+C?wmR z4#jJjTNAOZTzz|7Ozsy548r_sI?QC34aR0j?#wIie5K*BVE!k&rktK#OOLcC!Ifj| zt~bbH)_>fE*^G@%yzq7#alhJ32PDihpU%*S`~h2f3OjBL^X|yYk|$B#zA30F%ktRM zi6SnF-P2u|Q;pbY1vjd`f z4J-5n>wU`A!Pyzy*(T8jdE!i1=-TLaboti!VyPg1)WYOE1R}jsb#m5n<(Wo1+GQ@tuYr;Ri~$No0mO! z3V8P3_}cI%a-Fa3eld?NgHm15omgL|dYm3liy3*%9@?MR{?EE}pGiio|a6(4)7aMcqcj9c9;I9=ymA z&lo$fN`K`2h>|8?_0>C_&~&R6O0S;RMz6&E?=k7~iKCA$za9ju+CLLO==175%F8@- zUlU+8irNj09+>w>|0<^h;6~}IoU=5Nc=Oq>R2e5na)*3^5LTIu>zxFE`2M!qhB|C1 zI4yBZ?_zsP#64Dd`B?!0VZ<89LjhUQ^r6NuJIvX{+-sAGL)q`cMj>yikVWociem1v z5NTGSnR4Ig)a|Kkka(JfKWz>t=cgxkU0Sh8q;zWEuNX`JAzE`Egj9CjD#x&?yN(W| zzo}>tnX0>(+CM%ss?yzf0A5vuX;c2IajNK*u3NWntxSu2eEi{q{ma+py#htUJh$Ty+E7YQ)z4pf z!B=bn{-5AL{|uAyqJuCZPT3WLuZL`fRpVl6$YbHa;3yKIlmpOw(^bxbfWY<0);4bm zW^)g@(dVCJEQ2!$1m%rErSOFow$IRaq@Crx{H(8I@z~&)GOoUUzeYWzw<^CpIW<*0 za9ELT+jnGqL7V^9Dvu*TN9@*WGc9O&nGjqiN4yu>@lx|o0sSspb7Tn|XDVC|1uIK1LCmV)f?5%^1OF zm2A|{RKz-H+3x?Ul6pM;cYE6kdBW-Bzpd>h7{|VOd8ntJ=TkJrycaqWyAGZoOx3PL zPb2}Xkf{x_$QYY>uLY@{#kK(;V(n4=I}X}o1+sKm`|H7NZLKH$(PSi9A0L}|>Z(g$ zWgO!tq^8JKR9h=_e7?HMKsbtnxxtVG-ns>EjBSRj{UfrEZ@&5orTS9b{C$*B7sF}R zfrbz*`l-QlmQD4oWMCM+~&FAzklHxuE8{Q=}J?po$IRo$0Uz;^+vV{>`VjbS%v|(1l!nire zJhYTOS6%CAN8Hu;f>I*8cjVA| ztB-O{>aAEoMI&-Rn5wAqj(^7)DcQ9n6^kY$5n=pbsbA7rh|5NB) zO|5~e5l?P0G25mvMR&{oa&=XkeRB#z+0Hi}{8<%mZfSWmwk?hO7}<_Sktj}I-2RUX z5cG?LonG7Th7i~n{qWvCPD?#&0Q@yiN875P%}v}%22fAW{JDvV_`%ap=}aABQXF%g z)vJy5+9Q(Uw3ncs1e$c-@Qx>;YGewxsU)9de&lK>UTRIzu59nmYYTjS0h1E z{rS4i2Q%06TlAz{Gz(jHlaoN31!t>qL&5_s9jpH2D7)sc=-5IPve<8>j9$OJR2nS2 zpeml6zc@J*$WzN#1ACl5-~TC6`K|ZrjorPC5kfGBvpp|Y{A%Ovh~k?nsm1Hx_Zb4hGnzfh$Z(2o>fcCOuhuH-u*f|7dJXK z|HqfG3w$J0usJohwCF3teV096!e_F5nl~O@9rx2 zonby5(&@cIB(#^Xc)}zUhI>MVb^K@*4-gJz-aS} zY{?8MB=BkDuMeb&wVcjF{cDx36(|2xT&>^-eqPo2u&dxZ_REo;Q&{*##-ogA(e|%n zhYXSn-5O8Dy&27E;n8%Jtj%9=6(3`x9sf?VO|!{1M?`OwCsf?jHTJ6Yc02ujSK4)U zFwS{St`&8McFt!k=x0=ACFp}~ZtyU$fN}LVA@~JYJUUwR{07PRnk2pD)yW*3_b>8T z-_MIVN?^#Ycdqe9o4v@C45t=pkGa?Y$#*O)D2`GG9RZ80<2juRYVyN8VJ%`z=ZC#{ zVYLmM`DHBsE=DfeGeZnU(LVY2lNSDK?Yge|OP*@B_XeuUzsYW$F}F!FS^I5KG##O02gadsCpvUo4)1N(lr2HdjO@T55~RW4nEh|?7Z?s`{;x8@#iba( zW#50OK&kRv{r{dq!vB@(#Q(D>HvjstLayD9;)1wyoWi2UUS)o9K?2kTgYt8A16l}b z)5d#7f+F|1mUXxs?Fx!r+Cb22)w#67Gy8D{?b@~ z#5rCeA{q`_T7#d)Lm!_@6;wnv+g>5)49GlzU7Lh2Heq^x9_^lQxB9TJc!a;QS-)I_ z5z|_Hnjw=!V$D8bS%JguxagWHqP;oL#ANC>k3q`BYXgtr1+v&$FLE#Pe4bTq*^yOU zHgd&0#V6lS8+YvT*ef#@^LxJ2-{W~Giz;<2*c2>X4i79oaQRNE`)S8LVXl*D_XNJa z@OJ*zxD*8Rz?x1J2o{~+=KGR0zm%m5vhV`M$iQF4OF!6V)KU*$*C*pp)V|{oQdD}U!FC+_>biBRW82;TeHH-6Gmmou}pomemcF$pN?lX;(sBHfvWR9YxGWRLRZc;;B zh|Ye)B^Vtrt0-jKu|X03)(-P03L|(t-;$PM^LXZA`?^BUuOKcwbl6+Cx_8Aa7-X@P zPQbXHG!-Q|jn_?3OjK~za%I>chgA*R&EXHzfTfzHpw;18<>X3E5?JP2HjgU6A;m^y z9-Zr%S}u9Bx5h338~NH6fJo`O+U8^3`HVK#ue))4Yzu{{@|gD5vpIWKwB_xR^XFTp zq#-p-)g0bF151`R$L+iAIq?kP>P^~bMtxp|Na>C>tG4&{c)u~q#m?S4i;c?{wa+c# ztdC6g$C=v~TUjTkp;#;fdaxC~GPM&;>|51nzWB6|3e^@h%X8agnQ5s$MYZ9zjV7*3 zHrA<^9@U_zQ$1o;Jag%j-zkQ*aA)<}8 zO@HIe!W7zj{Oe=3%yW?{3V%wSirN?t53lbW^r6=HL$EC&YZwS5E)@%_ThwqJ64nrn zZx6waaaLz>Io=@+mA^}aLbVXLHbm0YdY9|rK2kbO?aHv^M^@qxhu3O2pPmD*9U+56Dc;~2J{ zDo-V|JHCNu@%bTH!J>2xvDKBRkz9Dwnfpob*KKo;sIa3)HRCn?umt2OWC&y#R*#5U zWbE3Rn=y;4KdRsH9CF76!M0TTIj232-c}CJAafzyv{FlMxtpY0QP((Wjbm;QWT*}S z%PK`7x6PX76m*w*G2)5dE~q8vrx6p5f&(8!Onpuou}8PuiBAGOs}V9EP?jed6v zys8i~Gl+`7wvM#wT${R)w%r-0NC(&V=99@Bs@h~2(fr$XL3l1A7py7d!S)?*8a5!B zptIItJCnE);Kql9iH21OD`p`$g6S)50cCr1L01xhpLm&U1F!0_})SvAFU* ztf@e_%ZzmB33E}!@66dg$VD+;yYhj$0U3)(fhRZ zxZ5uHHr>DG-gW-NYZD0p8?9iLn;jW3+uk1c!eXeQDjV;D`~xE{bSUT(ldTA`Ki-i~y@a=)Nv-~ouiarjHI@^TiQ++Js&(sYP2j3&UJYwTE zfPXvbnP=pRSXAQ+Tk??jeB7t2SympdPPKJ{VD|750`Q^dZoZMl{NVVhEfrjjlWkgByiM^liS4v0RNO_H0RATYW1>Wp|AL+cR!!PJd^6 z)ATzABm4VsK9sT_1K(Znlx3@CtF3!~(HkY}KYvRnKyu~MpWdrFdn-3Zhc~q4BHoZU z5_t_%RWQ`?>um|>v(Nz11FypcH;nKQS4ID3Jmpq_TLu7vWHxCLI)4^$zUIU-_+8>N zO6UF6Zyj-YTsxQARYJ5>0~Vo8oggY2y-0=JpX^@zUU!aEc?l^3hYhyX`02_|4$n``f_`NWeX4@8KoRb<;r1(<@;q!P#f@RL5TjkKZo035_>aiZq0qErdb& zkdbz&@hf+B*CD9IJn8_zo;rs9gHFmNEQTjGwLdJkvFCr;=vy!At&cvk8eN>53Kp?-S(-P6~WS<$0-R>e65b~ z_q8e>43C?qxpr6zB=@~c6&?uF3PKVIcbbx*Q^G$AMu}rE3#z@!H7&vS=l%Qc-@^B* z4T4Bj^E#%SV=g~icQb->FNyXYG26B`(?7|zDnYD_@jc4^gN^SM@BenN8FP1`6@I2> zH@H>yFJZ&h|0K)~1bI6&g_gt&c<{xR)6N_>;obL4#FOt%iLjY5zdtFS<1ShZk9+2`pRX>e4=|-bo0fDp8hCz>prXi|%}N_>uLcMH=|h+4!QOy~etazJR(34w z!EmXvf~lpnC3m(r_JzcNlNJ?OE)#vYu7j2i9yVRAO8-@q^2sAbRR{g^nkUBV@`faD ziL>d6vNeb69T1+;H^64S1o+75$!sdmy6W%{urhn)Rzx5T$kG5w3QGp*YQ)OqPaCXR zGVV?YY?~q+)axFoCjEdvfFHwBe4aoRSJ2$$R;P1PK({$PgSX7l^0}JP3e`C2jtLB6 zab8Ca*h66kOFP0duIO5QL*x4%Q?AP^ zse;)$0c~-Nw*{K8@w)(Uwpm=@J*4*w1)sQp_pY0;#*fw2FlZV8>Tk0kV*-Uu@-~ef zI+Gse=9I%S2M#Y`l!dTcsy*7V+Ry?Koij1?{{*5De*&ay%L=x_10#39f~~$y7c8v( z9iT{-ad+re2)~Gks6cE`1X!a4F9BnPJbVA-N@da z8M#=8Cw_sBWKTUy9u{Y`C3@4Jp$YExPP>PE|1KW1><_0NKHr+^%qK;JlOA0yFWzkA zBYykh{*%$=HdcG?_E2y=;(RYc5C|dM9X^^;L&S%l9uuNl(Vt+RY4K&DJG|R<`m9A9D@+*OK=e zy6ca5_?=^ggu;5%jjBZim!1rBJB5yr0JwtaW^Qc!fmr-J#>sT{AbqI6zbIqv+H*3l zpj2Ut$GeY~%1)1kO2hY z!_TgN6~bDK4xd|{xwo?JcJam=x1LL}1$S*>gmY*u%=}K29wlh}i#NuTQ1Ia(FUYr` zd!Ejhy5LJI5i_~4_nPSeAsOv&&&>I`wd;bhmyNM0>wmxr=_8)>l~RTDrO2vzUaL?4 z!IuQojNC{`zT4kO{yKz@%3(GB`p4vYo6``wSgMNhg2Ixqnt(;gdx8w*=c8)PBtwpk z80T({lSAe0YyU?SDDwqnYlOn==LxdxW99LS`+@yMo-Ff5X@W1tu2Q|j@X}9;ScrCQ z{yV+vP4Z>r2OBYo+R*^%ARDGS!9C8;uGnSoLF{?#*MTJoAjb=`y&qL|%6cIG37%Gmr%NhT?+J;2j^Lt2@f?ySOvhD3yq50PIZ_rGXoiGrVWq zhzIe2U&UxAhhk6Ez-(k7aN!L!<-dDM&wsm^TmyX)j{p3^B{vwsby(Fb`GqGp~k!yk-_RDS{C(0>hiz10FN$`SsA`*|BdVU zwrc&Cp8SLn{GavYe@#~V|EE{@|DbXIw|6!Fe}B?edP)*%G&Q2BzYsvAQ}KgYRV={P zBQ5XlEF~nl>K(k~n{-4SqI&!G`$Hg)TBQSZtIwEt0kz;nRsv@yneVH^V;$bg^Fk2x z=S@pEmaCaK5&Q_7r}NP89CNL6WP}V3_R}Lt)iKcIS+Qu-!KmhN(_EVDz4<;XT1<8I z@mmMO#kD1DE>CMk(t)SM_!+lI(QebAq#>kpz0NZzN~8HNZuWJ+$&Uv^eqXjkb5-vM zN7d6x2CrzAtuKY87C%?SG)Mw)jbqh#_3JzMUX<6`7r zXWJXvx|&vvSc&J8t}GB%N`th)>cP76Vv_~MKZgeASAyXD++BhSQNFaE77gZjKBIPy zpUug%xd3&n|M~3K^Wptjp{Np36O750qDNvWc>Y-*9HQy}EWkAGT!s@h&*G3{bn(cO za{YtuJK4_3ExS~*I>GA69Nt_BYk>$Z&L6~Be!eLb!SDWDTQngOqB7@+3YVuwsm&+M zl8mi)mZ|9i$=VJ+zwiJ_LO$v2;5XmK14Ze$w2TLr8uRGVqEMBf(V`|2Cu=V_k0(2f~h@9CpEIUDBTuXMgDp4?f@QQOE z`Z%p%EYh@HzNoAoRzq9qG&Va9;zV0iDhB_74Egn!huA^P@W(qIctjB}Z5}$9mRkxE z+mQ~dZO_gJhQkqn$TH_1eHZUI<@9ZHs(R}zy8Pi%U|)sfuocoV|E;pCn)AQ{*aL`K zaanAlKc3CmN!`xu<()eZVz0V;OmZ>T3r%hMvORZwADt+iv8wFz2PhJ4QCjdu*piX8TP^w z6WSWq6hXyg9e z05K*=hEX|~uGX2?L(kAe#n1Ks4TmS%PUHR?4M)ZuaW(*Sf=RaDevah1N&KkErQP&r z=Yk0h!0_Iz2!|C02#eXJ|T+KxF#E;BA9s?&uY&#!aqtOGNg)SA2#wCnI1NVN*t)Dbxss5sv#yS`hBVi-Q=FEc@xx$Mc7E3BDrc0RoKi1?Y0XVqG zxi8c)i_r;PhSplT(hobq4jX{qau}Wd7Jn3qCfH0+EX?I1$cgQ@F5Ibp#&cpX0mR~%ZkYx zghG^;tPhC(?yvkoJ*(jFwD3?fV@Nyy(dP4oul=Hx*r_EzTlsg5z!mD@kwd$Z!{>CFe)#}$fY7r>rwWnDwc2h!O{+DpM8 z+?7ndV4~~h{c;p{TJ+D;cT73M$YaYCnS~3`M#ZpyWaf^%ev*} zSh|UuefckMN-OuNUjDI$9Ez5``ZHm`DSzOD%rgTJXDIyqG|S2FS?< zU;YJ|_y$P>NZXY{_)Z=w5jnJ&JS@!@AOpUvQQqK3EZhlbO-ioi*32I*i#v|@fArEB z=eeeOR{hflWc_Glvf*GY4Pt#J-X5xTHtiVO8ftHsSaKGk$Q}%48;?jqopnE%xNLO#6-v%Qi$KO!-35>jZdEo)MN1I){Hu8Ou zg+&Is={kpO1Lcfux4k10Uy{&9f(>Un8=RJ-2K%^+v+pN=?L7)OW~Y?DZ<}$VAHqdS zbqmIuHaCT6UD$Jve2mwf_ewmy3w=U>2RS>>y{X^{Nt{X)ZS+p|={0e;O$;Sd!y1)Y z{-J=9NpB!J>GTL2S&%8~Xq@g>cO#HjdXe(w$=FT*PN(0}6oK8qRI7^5zaN6)WbMp- z)hnWQ>PweMKX(qCfT@UYHw!K&t0dc3eLFqBw&Y-~bCNVBwBzEb*Ebf41D#Ign|I>p z8RfTjpMAdtD`l=#ad=Vhh#6Ul{=v4Nk`0JJo(W-v|OE%f5Jj4cR)sAp;9rn?`XxBbwJq#k>7NqiW*I|Lg% z)u0x*jxXO|S=e5gNU$@9XZHt?C;=h(qt0SG$K*f6nc{yLh;a3>gq{B8dDtfX>(BhB z49kC6NM9BqpmfzzeV;gtkYEd7j;A)FJvTl!_)l!ozHy1FU~ir4daMzEjV! z^dzSD*Uhl~oDEmMp>@Ib0H=ZIz!CCw+itn%<#$-U2w{nvklc*h*rlp#Yk14bebzr> z(UGF-$W!{bI=9LkVw5=%QC2v2~etlAC_M&h`#8as&cd~-LD60v<$Ppo2KcBalln@H7C8{gJw&2mg?` z-EwSRx%o7LXHHd!1p+~tW}8Xd9LbYbHWRZWW$&!aesQKa;vzAv@W*={F>LxBU&+Im z^x11dY(|46wofnVX(|m84Q~H&ze&K=awO%Mv!$sG?yM5a6a72ht?r&)*}qAMRs5um z-1r1n3;T-fo6T~SD^_$fD*l6RE%NE7|UEQmE0*2}V6YjMq<(JP^& zSCYJ%8GH9EF70^!{>Xvh03u{fSQ?s{<5wi5QeWz1nG7;lkApOcZnNF?bSImse*mE&{@&p08U@7t}0vJ-#34-||X;^!~6+KNq9ZEYy|wA@;Si zI+7|zL^II4gT_sGy&@1&wV$2EQ2zOejM62p0**`JYtCf1#NhcGLsDbiXN`yMkdmPf z==b|q?X>)l#u)jT28@P1P2-5jR_Xq`end%le`*$V<16+f&M)NE>rmdjH~X)L(iSp# z_hb33;++IUzoKYGWY^0^NvASKVa~q7?|tRwW%dS%Ve}cD@$%>!iTP=P_sgh1tzikK zqqiNe`Wv>xCV%J8R&^%envv-G#+{Wkg(ArVew)s&L^L^0^j)A(Ew>1L!pu{FMC84g z7c}^Nm!$N=8yB*qtnBz7SK;|9!RJhSG6kh&eJS6sx*5+x~GS%^v% zV_obE>gMMY@q7*<}0PDh1ck?Q09b)MWI2VA= z0K~3Rj8Cwapt#1G_jKt~Z<1!b#M%TI?;!Hl;(shyzs#a&1<>yAd`A&0(vm;M93z9M$ZKDTmW9xruO}z7!PmIo4`(?{*(G)w=jvVzegkPC6RRpwfE!(-$`k2pv zJmK?M*r2r-*I?#~dGf#Q<0XQ$nF_UI8!bORDbk19Z*0sn)4u*`i6AmBJAmwD=hmri zBz4|rsgY!21R0^u<%|qim#uPq(!cXKj!tQ|-%%v=m~12r6qm%;ZqL@gw*Pj1v$l0T z@Gh0fDGZjM=@Em=lQ!&&Z+mw^p4=2S_c8PRpNB_x>3 z=ptA#59dGb>QCPKn0?D36g=7bU8Fj#Wp23bq*g!Lny0B!)bTv(Gwh9>kjDr> zS?<1;nx-#MpCEaKIXzBr%ZGjx?ZHZ#D1GeftXrtJTHY0S5pa$XE^KJ)uqns6SX z;)~1LT;z0^_btuV)m5g<)f~fnO|rY!O8Tb(mA5_&aRE+4zSH?rTYbeTK7D?A5~M`` z*RD`9+@|8fclj@>Hr_Z{#Y>m}(LDd?k`{;f(l{`wDgUo4VMh`7J4!W)zmH!2Kfl7R z(EpFX>FW6ZxAo!w0}00fN+$pd>M)X%8(;z*YkJD!G^8C|5Wm+CU)){GXzq2Gd!z-O ztvJ=}|+3JiEOrVX(lgW@PoIM}4xOC&F57@)R4 zA}5EzjYxkE1_q)USCGOAa(BoRZ3(c0zxA4sh6EH?Y9~g)t$|M+a1sg0CWz?9d@Ln^ zz=6xt;I&ntyWm9(IN5z zlr+Q<)6 z(Te>yZML^<5WFa66y)Nf#QXPQS_KLC-_1tph-02Z9*3RDVul3P!5IZc?H$5;*OCj0 zqYr;hLr77eim&i@81XQ3@`FOGzRbBcwd&(zMU0Q*uO8Ror$wjZd#>1%4piFftNO{( z-lBhf`Disv0QBJmCA3vgb*ykzXtPP>G47c?Rwxb1?~2#*Q~c^K@+VAws^C!rJU^(= zih!b*OhC*6{VNLFKo3+AqR)sQ1q|21x^uwR6juuza~Q*+>f{1_MXxBaSL!%I;A?dp z(YffEz%+7K+M(HX5(f?GBGPVdM9L5*od&iPW$()uN`|D5+YS9Kpy5WO$ptC+G4EHl z8loBJT_9355pW7Rd@NKHf;q&Pixk$oPVtF02X%TV`;JS_ZvLLovAaTguA4s9+}nxx zcbcsOQQ*+J-;XUVeT55e!{kq6JDpuzA2`QHq;1W2U9raF@s^PkpA<#R)b!M&3rdpe z_k;xCif!t|uaGWn!C=pcypYEd)O&(-sVGIy{#EHNWB@anMMo{D#0Q5d@FnG3`!SnJ zv~9MRra0086XUCPV&zYIdC_(_^8*y(NYpd$?yE$n6;htkBXn3cqZ!7_dDCnU5u-XAz9V1IH(3LO8*N9 z*r>@hV#+3hJjQfSl+#Covm{L`!9++)9+BgfGsT5tBabCCps6X_IohAx|v`Fe2S<2(cuW&V6nwqoMfK zlA&y9_mvcKs>VrF9*FgE(e;rctiLMFz;2-IJ%@j-od4U*zd_Ko0wZr6rg-NaW?s@ z!DxGOdU||mDW)KstptUDe&a_X552Kub{4)2jQoyIk(XXoaU_rgGkG~*F_|kMXO-5t zf{8=U`w1!veXX7!&BrJYHk@_BMx(?|VV}U<$8}=QV!E3ZA~c zJ{`V;WIN5VwgQ%Rd5hKdr!X77bsN^bOvWA;#ZCt*1-nm{I?Mx!8N+#faXW{Hxs8nx zzkh$E?4>oLf5b?i6A>QqO(wVhq$D8ZG1KuA@@SK|cU@SI(Y71j0^_>E3{Pwf45Qz_ z=Rl8TP7W=+VOQ?j(`#pc|0twE!&(@A-3DCkfW29z%@xnH{UxvolMAv#7E~mG5Vjb> z4WWW^Ih&SMi}2$pA~Dew^pG!!Bh<9Dv&ad#hVp{d>$&Jk(LMWFcBMdjL6aR8x+Lja zJfuqzsg{J2o7OIxHeQfQGJq*l;knPTiSQw?G_OA_;So>6q&>qJbLwW2L!xbUy4JXz z6+01Zff9U#w!-}*M{$||&{?(M>(_g5la-F(v$Kou-(R zj(<#=m_S2{S(H7Y4WX0`6P##su?dtg%~t3#=`vBF&r|rClY=^}TLsgpGRxT`XJ;Rh zesxQ?>HtO3k1r{Skmu*skq%)DopLI48F!gDz#BI|_QWTQEg_%OfnTf>n2E-Kowz~J z6=-99L1$NmHlY;q7rq1NV0sZ_q|<-0TlWk+S$Eo=e;B7amFv(pdg6%Hy4Q#@X+7Fl z8d7yymy5L7dGCRb9QEtQgZ}OP39O(kmkbtNNxOy@Aq8<7dAA6Pv@5D zc`fh_rtzh>w7ggiV@lUQ0G&u-r^C8ZMk+6(bM$IF4MBg%W3%9WaIBQY+|8-*|fcvX#fW z%zDp6nV0qJ(H!F2+gIQ>MV=Ryx8fpQFhc{+F3<5JWGZYU>?OWM4?nxUzW&xrAWi#R z57}O6Ytzd>2)VYylE4!aCnQc-6L<^`x%(fPKpHM`;$gBHr(_}1$C>vXpLi|DrD3q> zU-m2BA9S0(fvy7T38;mY)oLW~$nq=dE6g}XCF40nnV$XK)nZrTqpb|DXZiW`ktIdT z!uQ8i5GnJE{n@jN3;V6ih4zztc`OXHhh}>=G27?0i=M0mj0$%o5eHY2x9in9YN$h8j+?4zf-k6uXo}T5-MHxh>1B3 z27ATsxPiA(`=(;*m~)gg?FYY4)QP6gAs7OOO?=+}OsI3sBypbLQqt(*ZBFajZIo6G z=hc>nMn^vb-k5Um);mwozuv#n8ER>3yMj=eca z{`|SU^T*hxTbrZdFKvd`AKdEdYX3cB^2es8`8JEyVHR{9)s|{AO&$aY+X~MiD|qw8 z$IYSC-j-&supU|qi~hhp9bJ@$hDLizzW0`!gJgOxQwe&GJM>^F#ugWQq7UX$xExSH zGa99!V-VP9LxVcJ0;iE1=u#+w?Rc4gyBM`Aj4|lD>A`~-pIV68lXP&XAYXLT(=7Yg z&M0=}U}bGSHaXulH-`v#oRF~QaazV-1c$HgmXzmISI7I@T#n6;OTT~5jVPOc1cE9s z+&M`Z@eZkX+8UpBjRZeeQB@smX#LoWL6W8fB{p4Zx$RnW&_hb_Lw=T&)bEeWgY%9v zki@z5u!z|e0Yt2t_5(xwYC3`$z$YuVhQDk>in;-K7$gSlRr=jO+yPR$BlF;%A>a}FQykPV9u|i?;Rbeo}LZG>3k9J;SX%A?cqeMOqjvi zmG0aHMqDntgl5QT$G&b7r!xyyobz)+VqzYT6S`rcJp}{LV;N%hk6l+JX%DXT0yq0r zf?M-W+7*r%Q&UrEZi^l^iibWBz z4x4TKOc>1IO+De>0C5Hs9sEm8iSOt-k}FJ@Fj-sWhMdUST!4Q#jeH_oc58Xe!CY;X z-I5r>X5k0<3#3c>@bCxPSSF{fp^bm1BU?L#eiB9!C{<=dw1n7Tf0cSOn{b0D<_-+5 zQg}S@iHJ;qPqZdAQFNIeDRiUqc;J)RP=;uuPLn%#1KjCqBS}qPe@GrUhtE`)Ftf4I ztSh3UB>@uxeKkc54SL{EX9O%t*$kSJS669wDRu=9|D7g_c<@Z|O@Ur%U>@MO5=^41 zp`i#RenpDu`2}UMD?3k(6OxBRVVbE4e}=})NNX_VfgO1C0r(n3?~yCOc!q$HyV&>0 zm+-Ff*eFyfjh zQ0jDOCfhOau)+`h;NZZmR`>zfs;H`}_D{6_#c<)A1bgg*g9)UO*aFKqVL)eNOug8g;4)UUZl>ia-Jsm!1t_60o z4_)jeB;X%d=%~pVOP@daQAe+-YO4bW`G1@ItsArEnfX;!aUFq@z{s0Smpf4^^qCQi z0i2P~J&|OUyaeebDt;M;Ru{6Q1 z6iz`%NQk!+Qv{VC(mveXUA@}M1fB))^uls2fzkuf% z6V{%b5G9%L&BJ z>o%<5?Zd;uii+6l^mDML03t|zZEJk_X93*%anh2(#^e6SYrS4o!!_|WUK0h1;HVKy zaAAf@f@fz;8bX3aGt64v)N$y0DGUzE3Mvu9fCH7CYN}N|O7qT86LrqE0!dOv`1CycOb0*x`hI(Zix(y>xE9H)Q@=|I z+zkZ0|6+m+*=(&x2w^kZ@8stm4Lqgulk|t{x8T{8Kf^N?#TptK-fLs6Y6eCRf4BfH zc{AbIz@J`9Uq79n;$yIKcsBtd3DLyV?9h+yLSSo-blOZYe~yW+Q@sr}8&2+HE=-eC zJVz(y(CY%P1en4~AnNt2=ODgzTy1^&FGr^ExzY@s4RHBw_=cLUtUf< zP`Ps$W5526!0&ITlgL$9oXCpp6I?2@!Q=`(?_+vB-z%aAuZqRy8*QIIb#cMAWNi^zs0906v41z4Z8DU0#17;>`In zYfp%ekH8@P024Ok_ zXFI$Cw`cjl^R2>S%u*rsb-LOI!G!Q|2MgFRjBm@~_M9IWzI`()5-4jRnNvgJ$c4*W z*d5XkVplou@N{Hz@eweb%#Qd*(?=xXMHJU%gKq^Ohrj?lqO~5U{^C>7vBKvNme^*S zS)SoH_uejP77*mq^LUP}CMG2*T4p?b11?)lNa+NV3LioeXslqM|wg*Cn@44;0v7v{Tr%gxLy1`D@1~ zCIF7JImG`u_8Vieu-mh_+QTgPAB>zaQm}qq2sYP)#i8l4IgrAsg8L4ndHF5b0Sk?; zye_|?sM$2&fPTJ-rmC)~<+avi@BRI|sN60qzdpg8wtXyD)?;TQsaYVNCAqd>hqU0m ztT`jFyo;m~P8>?*pO~K<6x_;?XM4d&3K;`47jB2sBgupl@9r?88eLUBEs6Wlbv!J% z{Sl3Bt*r?Xt`9o<$rO>6MRw~42X+$+3!PnIaohX*p9ePCL4svG`??@Iu*=Qa*;waw zmDzAkZf@MeP3JZu2AnVbEn!V}zbQc4mtsisUl$gErIl5Iues-{4ipIZThypz*YTU*=s_(X8;_E(tLo}HhYz)--$D6kwyBIW7u zjTW0cv=hPY+B~>FTLrRDTK#nWhdc=CY5jm)Hh}mXqehy_d zwa#kGiQ>{yqZoJ^;LAQ|{jM*v6 z;|r!01*Tq7>6{D~K507q zV>A1bQGq&TJk1aDec}fd$uNC=JPz?oWA%@TGIaRaSy}!7!eE=rDQk`bVZfR5i`jTT zAtpMvVjQpp#@7t2FzLYydpo8_ROO&>MVeE=BqJjufXJ0(*daIR?;RZ*`#^`^4OQ&# zjR(;<0Ur&Rx=81$1h&&0P#q8w5ocSH2M2>v{>;#wl{y#z)``Ju0`c+jqZ1QSpFUB8 zNbr2;P)TPXJUqNz>GeMG97cH-(gbQqdlm(;(2n(8X(|`QH zr_}8h<%4_;N}!>{o>-VrG?I9Uuq|<|V(*ue-}&>W{X+AI8$yIO0i8PE**h$(&O?Vr z4M%_2cx443G1eDE#Kd4`k3$oef@rfSx&ohGc?Ul|z*Lh%Qmnovlk^wo)uGR6X2;ub zKDq-L6mW2F5eFYa>2{Ubz<0+qFq`Jq#|l+PqK<}Wvlt}u0LMf%^LH2d!1kgOssKDq zNtJC2JQ8Oz7GH?n*N;>IKMW6WK^0Mcde23gRa=`B7$`~E+f%77kiV{sz`lV3#|Fhy zNmUQG+*D{(fp2+f8ltR2tvKbXUxSV$(NslUZ#}qM^<^Od3y3vXRW}$caYiJ5dte4~ ziCo`!jee^t@eTD$qVYBaK8$48p(%V`FG38`KGw@jWM|PRxYgn_M6cn;F#fU)?dXsc z??nL2IQjtN-)qt5*{C&*Gtp>u&TcMhs;ZJu;{3Wg^Z}KU209s8@PNR>vw(eU;l8Tm z6d9L)1 z#VBw7QIp|cgE`B^^&Dx*?xCOnn!{aws}P|KclpP;F}-VGDHLkdLC+xDNa$208-NG^ zgmxyzGI@@KMzyrGO!5J5cq!>uD(Boke%Vvf*TtPXE(F}galFu|vIK5k@mmFc0t>0< z8HB4S<$uV3XK}xAkz#zIMor$=ppB4`sCzUQ!O4QzAslLQSy5sXTH4IvZ78_&@|@d* z6%!qM^xPjmmE58MTkHko@%yf+@kp!lG`z4n4ERS5?)pH5ll+4Qt72Iq^#w_O1?-4$ z@d?bw@l0|v#IWq=5xtLa7ASaPq-oIuB|276^++hS!ZbU%5?WJBBsFFd(_vi3I*8h| zJbeC!FCagwu&w!=`-Pm2EyA6a`#WyUdd^zIc1VCxva{PI9wc;9IllzGYOjv8pcr)~ zRC3Y7(yHhqzxYJc-#CtTal9ddj}7o3XVoB~=m^{5n*O*OO4%|#YNew0(7uze8F)~| zn(91!7P;>js1kO!7c|iacwpRwB=2)2LvlHED_FrajqK%ao;Rz52I)RSV{|YLr)v$1 zO+L&%0Y2@kb&&6iw*1qM&}QJV-TM!U6KxuSLoO;v272^_w*7;HS9q8Y#6|CvOL(_& zP^T5vkhEkNuz9N~wEziQV*YMCVY2%`!EQ(_{@!~id%n==YC{H*idI~ZvwrU=4mjh4 z*!k!UCc-m`iW)9NFgH=hY|Eczj7ljATIM7H;*S=b*vTS4QyhNj3J6H2@g7nAI@Qw zB8${uZ3l>P!k@CgU!R5)eBHx`DzdSDXoJS*a}#%05NtO@n09k3;vijBrD_rB5U%pT z;5mcu4#;!1vVs(B>_0OGw|bb#3>zD!X+G0BfS&{vNCHZ~gKC@%2t?;0u0#GGpLqBE z=mx;9ema#^Shs(eEn~ck98#mv;-|i|ep%&OUGzutaRkbfPW6uR^426!&{E1O(v0!_ zIzX=grrm*DaBE&)Z;UYaG>dBLiP14A$((hkcpJ@&7#@J{4U1k5d`ddzbQr%~)kvOZvuIyvY4{nqG*;+qpj1|1XG@h8gIK~o0CEMgr zrHsE5ZBS=ow&W3jv{~AU{=5!Z`}Mba0;CNqz}sz+Crj>;6|AR9XlZ3CvWRh}Kv&Z0pDCGWYd%t{+> z07`BMQ%Dm4ouWM0*d2qyrWqfl^qc^SLKpju*GLiv45DCB$CdjoB(?h{77|h-oyq90 zAXV7fs#FGB7mANzHr*WD0oCXD!a`qO7(v}?_<%-Zj|oW1@i3DohhZ{Y7UPS)kzm@K zJ%ER}eSWJyqnuY#n|Pkcq2E!1)bEV#3k_*E0uv6ev;35OJ&z`CDa>TqpT}TurRv4yZ?E!lnhiR zAs}xl7)k!1W&Xs+rx_iUp#lCJ8>4>rX9+34FJ*c<6`QCJ7GOF`;0@tZ*25cZ?Py@s zOYhAi7I;GdvSGdzO(hWB$$YvwHZ|Mbjs}zs#uh~h4X6Q+5C_WY>iuIGCyPT1#upJR5$+0QXGGYcML)?$>cY9zBdMf3JeYl&MFBSgaGhsx zkYkbn6a$p`JS?oe1Yt#_wN~11E=OAmEYXaIE^B|J?zX(UyLYGa%iMc2zFv@#Xs7(6 zclh`dww6vt)%fZ6@@T=n6^c}E--7$}z1Aj~&A&dLi{2YJnr4g6)b-H`5h8s$bQo)G zOFLdq-cj}5T)a<@3H9{OKd*V2HY&dOxueZae*}X?^x$t@a>3iVF-_2G_E+2`KO8HdKHfJ)GaHIHOmc#*8Ad;`J9MXl)nvdG)CUF_`>V{VH^0} zx{N5PUTuQqKCRFd8^+lB)CNW_j*hy6svR?=!-^E?5XOkV4SU;h>VyskJxtBpcjBbE zOwTH0ihTt*oWoyx0{C8cX=F)0o$cHQ(36@dLqpm%>Vlt9gFX3irQM&rft{Jd=>853 zAZVbl9suQ0b5bMui3?Sd1E}w(rnGqc?wIvFck%%hvp1Mtg4)WdSM6VUqI$8vFl1YM zi)Efx0qV+4`}O~f;PsmyLE#6=9q(-^t%uqY(wW!#CMRUlwq~->^6{wiSAR<$UvGMV zHsho@_KOS)ABwpCneXcee)<6Vo0^(hz!JLn^IcdOng->AZbT?@~kN3Jm3-ht0U29HPl+~1S9Wm+GT?mi=*8TAnKC8=jQ6R zJD!pWyFvgXv~M-9=;bARetr&!l}f<4Ainsds(nb2b#TDSa@Zs@KLSL?__#MAa%>b5 z1%jVusW!@kgQ@j&=_VK}xi&vlqSNfjH;~A_1&)m=2vzg0bA1pr#KD0I2FGW=6$zbjU!x`Tz`o(Bq-`<*5`hh1*AHjg@t_+L8eL zjhD}r+_y$0EiHj5ryV141oxMDfxElIyJrK4S-<5+?aJR2Q(tpip3nhzP#kCdHH4B+ z`1rUgK;R_Z+-k$a@i4PIT5=yoswC=uF#=;$^}NSqfZ1!r?UQG~IaE?c2IB$z1@-V# zjyiwSfg~p|o9XxO==}NTAf%32L;{ik&>(%|`GsoSUno=g?aVg%6Vm+dt{%1&vl>H{ z&&@MEt$H&$pwqED+RD6wJd9HWn zEUzP;c>;iJxMSMJct)`)8Rz`(C1P(begCzNg!c0XH3_1B;`l*p%LJ*3{(kfKZt3P; z?8V)`-W#uXkYmQX%3D9izPPmD`^zieLi&X_lT?`%c5T6Cbv{vpO_s=hPJz%MAfh5K z8&bRz4Lk}kltwq_(oTbMIsUecs5`5a*J<%4i%=4da#&-TIh!`qgn#V57rCN)bJeHB z%*>pPKqP`-4@Lk15Gl`iUmG0D&dQAeKoMX8MEYU2WD2jj<9+Kb1=u{^Cj^ujjI%!KUl`*gV~Z z7(^)(kf)Vu*MkF{_{+W>6(BVNu4hakmMkR%R)TVRQPLf#lM0&)jhFVrf?JMf+uXpa zTgu4jIe=Ye5MRkKl?@7}@t2w#V@V8(a*OLzFrIFno(ymvuSRe>-eHjZGQa#n%%x*9Z(;2^!%X` zt!VSR;?vnCm#4DP&CL@BY@rfTj@h0%tfnhBS0(~n;HEN5YMwM zKEgOSIG_Uh{l6Hv019{*4EDhNfI*lh8~qwh*SoABu?Pk+u41RG_F?D@s6&*w{sTb0 zg|I7dqb|7cnga|jKwdo&UuJs>YF}6FlqpgQIR|xg7)K*sW*72ZSMs{9akgi>G0&Ld`=r%<~MXPIT2_oK-@tQ$m-vt3_5QIfc?0lG)VdrtpSNlV1Bhs@r4YCko0KnU39J|#r>2lx2xGneRdNVAw=>!V zSN%~(L^7}23)mODXGEimivgR8sE#&I=3M_LiwquvQgvc}e>bCz6>4APulz2(Je>SH zg%tm<=J%o~6Lf^wV{>x@!`JB||JG#x?kM@b70?qG$dkln|5*1e%7rrzwb{ZnS< zyV~pHCssBH!mml~^^&Sa#8S61{W2a#c`Kv~CCpY?J5VL9{=mmy{O)Y2Lk6oI*Kj;9 z;Wp_x+OHa^bUQRKUflPd&^3CG!~sGa_@4b)(a9Dhv@?>5S99pwW#Ta9C!IbpPrBdl z4(V4>@#PhmN(%B3V_jmI;9y{u&50@*3IFpYno>{_%x6N7RJ6I;ICs8l)_(dFyD0{R z$k%E-FKjSFKO#XnP-g;)8bEXGPN_KIejCLcw}!q(5HmgXu{;Ghmj{Nuo$TsU~(rQShID6a)xQULW$i zJp3w%WeTZ6Q9yyL-Ry}orNIZKvRvIrGBE4xi4wV-r=F=aWwmR%VxV|!vm8ufu>3%N zmXyT(;+Nq?jWROf+=>_ZCo&bnnO~;bVtlB{TT=_-H4} zm}`-2m~H^jg^joug6=?>WUtNcVL4~D8gE2uk&$WU=;|T%lsKdO3(sV1!Zb-ww2G}O zm{dIe`zPxCCyxycz!g-LIPk;K5F1CU^5tOSE$;Ie9c=^&zC4g8O93tyO1#D2dP9Z# z=9;O&2tTR^{I9dK3qe@dw{C(#;SF`bs{Oax9RUVhUZ87<4}G{qsC^JkNd3|j+g+I6 zQ<2*Xl$}ZXzGMk&tU8sSu}U=I05NTQ+P*&X`XPhcn=Ttnb;Abg3X#*jPfjZsBoDVV zm+1*1N9(F1xB|tEj9xxyMV^wd0iAZG_Z82>WNr|x>y`tbemj#T1<}LLajz_m$LTm8uQL; zQQ**Ou-_N&_&chMH6rm$5BpBI&DckSXJ+i(;8?PIkBp6;#j4ih;|agKqVh6ohjv)-`pl ziLu!}P}xT$GXd0GZ7{PAQV7ZyOZf%5PU z?Fq<)lDC$MC8;eNU<(;tGJ;ohQu{@-eT!CXaB?jN%1|H<*c|yJ&PLwNg#!q$p1a#8 zqb@|-0G<8|16k?=f(0mu{h10W9G{Czf+(33@b)<8I0`93!N+So6H`;&fVF30V=*4y z2kexLmKI6qGb#pRif>bi-u6P#wVF8vK%||_V~RgDl>oTHDllq&06`{f22m?i%mv^- zxSM&bHwV>^F7+GTCcw1~4p5kME+x38{LTB)p_Y{&Yv&{Uz@{d8_2Za>kJ0qas5h|f zM7|`16weNZ!1ND=o`K8J^v}NFqy4$o%)~@uFjqr@jd-Ii01Zr`P}R}t1Ly4#08kjm zmyojNZk+G}v}Zt94l1Nkpru-TD8Dm9Yz2d!Vv`D+u_c<0jsA+L*w|1=5ed+S_B`kU z|Ln?@Jv|iS;NSo}57%M5M_u&5%BxUNLp`7@qz2!wFA%O5hfA5*e4k+q$I@wF0uZ-L zzxu}*#Q4n2Lw1p@%!@LBu(t_a0AF)qVyL(;2@ULfi2)n0;+$xYB`P0V7wju(Vt` zihZS!mc_7nP+DFHN|WHHB=+HMa9okUkBCfIlewK9hkOBl!xsiiWoBVB-mNaC6mkFl zj!sfhF$%k&+!Bb2g@lF4_`gts4FrT22)m`>rGG<9%B@a}I8jj#(zp5f$m_QQ&4@YJkZrLyU)dMzRJaRagWDMk79CGZ9cd@1(!q-y(YLtB&Q zL)O;=?I5Xk^>?}cmWDe0NgT`FgOR-nOUa(eMWOzw;qV;PP+iSH&{tq&lzqZ0A10?i zgo%r;8c@auBX7Vi%L9W#Dl)X@mnw4>J?nS;ttBQRn)1 z#NHLxZ$Lug<(0QCrEX`VXW6pew|%x5SugNv?~LOXrRtTLm^`X7Wo6AW)k!u3qERlK zxFAyx-qEQE_WH`$JxMN78gh*if|P=tvKM*TpfINoHECWxZHP9(4RTj43m`d~?>X<{ z%g|_Z(F4jtS04K&H+?;=DWn6Xo(-iIj&KQ=hSi0e2wd`S(H{TND8J%{Q+P>fFv504 zzzTEbq(c#>IxbuSRt?YD>%u>twDEQkq#|-1D9MCV)J1z9kpRGAZbFL5&@&d}abSB1 zE~W+}zi5+UC6e(bBjfccbe3$wfUUue9jBag8wTcC?B;J{5tJ(|)hUB}K zSGN`_>T@a8YaK}XvnAa>AoAv1Yu7~3RtfGn ze!Qi4Kj)BE`U(d_pQN4H+rH23{#lAlA*U=-&R)g1!+{M@zUAdft2vUT(jr8W`Z_I> zW!9bp^5g~B@#$wi5sa&#>q0JksxtZg%BE+@E!`&=045(R;5!hFR2CNYMqm^h{MaRx zQ@%L6P}NgkFO&Z4!8@=f01x*dy+{;Ei5Q!mcW zlno4`ZHtXO7gEEhPQ9_?+J|iI&Z!mEh*#hFp%1t)QK-GOEC1N9c8)^iH0h!*xq_M# zOWN=yZ`zZSg@x`#*k8}BV>0ja%vhPV&xZ&F)zz`58VL}2BFx7-dwZQSK1JYUD60K= zp14&4qtMXgfY^~C0UARK)MOb`pLC?Bjv8samH+!EPANny5RA+7idKM1w(R9W!!sB> z&_Fmk+^N{wsxl(s*S8*H00_ms(-wf|hBQM}S6|hue;OwTvn|y99b^?3-(6!d$SAUr z%UlNKWS1mZ|KLRJmk)7kd6SJ(n21dx9ONlKWioF&>qf;+pKlgxRR-A;o42w0`O zQ&dCgNx``<{;^%61Q$7WqE#>JMz^#PgG7r{^<8oyge(m3X_hsZ@)J`eABsKH^TbqOv|HpSTLKQfg+ni(A*yr zVjM3mM@O%dxIsoeYjQJO=yW^Cdz$9uGwl9{Y$Q^|L^m-eP9(9&P$dkXK0J@k9}``C zeSTp(t8{k@cAN_qHygIT*I z&w|)8_Kap}D<_`5D_Z-8Z4SlntQ_#J)|4^Yx&5+M7U}8ze zZg@ePS^G;tKDldYgMlX8#*IPR?zPcUSgq!kFZsRw-sJ~({*EgH1pROVO-gu9p!BjS zD~lSVJTd5Aud-No+T5XPw<=M~i z7m(7=7{qHl+x}zZWg2)>#UN_1oI>gd& zfltg5Vo7U9E1eIOEs)o#78A;c(YF=;{Q2Yd<}yx!q0ekwVlWp&<|(zrsIxwa?M7QK z4Y4&dtUZUw`9u$h5H;USW4t> z#w0R23Vku=$A5xuHWEdqT{xd;-#I#9a6=%S?NS=1mz1`Dhutl)}O;br}u(FNaa(bWY_xBtV_TR=s*es9Az-7O76h(k9L z5(Eg;e$T@n(CbcZy8pc12!N{JvHLyFSfp;8A);oI~3f9pMK9oNyLI6TiCdtbGU znvO|cOH|Y9W90P{3G%NBlC!Ym$bK%PXb+mDvSM_zFdIYt)W_{4x$Di*uHA^a%vTUb z9+QNqwTablJ*7SjbDyk%d3OXOBYF}E{U--o;}w=N@$p1RUOsGBx4F4#GCZcjm%){# zIWjiY28er9hgDpB{K(i?EafzJ2EWBU_|woQogH<_6%`dlG@Jw!6>~~@AIUak_*Q@T z7!Trj5ZSjT{JvB20nPGi0Mx?qj;lQ@d$1N>fJcn;6)OXAvO1pU12|~Q%)bByN>${T zqKe3R^~xGxTDTNk=bE%^Z8@Oxushy!C6DGnqj^kbNBVzHfz!?hN>6zNx!qx&`KM1J z)xrNB47`$40oV(qb8j0OQVJFd#I0$rdT!JcU%h(Od7>dJJDUfL7{fmCGEaSnf6heP zX4zEJ0Vv&*E8S8#hL;QSm0iniZCn z9XL9wEAhVWGTaefYs3`fSYk zN}U3q4qBp0fGGl=2B_K#X~rc&2mc#0X<}^r$j>j6izy^`a`NWp=3^9mWI7;{dw5$grqGAH#;u^lbTuwJmK)!CjHt?!%VBo%~Ip+M| zX(=~9fphhTkY}G}e&=l8UufJKY&`6pnHlpt+@6l7K5dbWp&Mv5%koP~4gGWZIZMZ3 zIIT7@n^nle?wGiu$HwX5+sE83SHsXHi@3A4X})p|94~kHX}14+qXZylvo*X05hW| z?;085>{dy@J1r+AOGaR zS{@>Zwl+*FL(|jk0P&4q!e>oz%+@zFv@+oYRrotM6!TAmpA52rnVMp4t*z2USzI~& ztQgN>=f=(e`$>a3TwH1xUsek@FL8E*9mxpnNeX{yZ#dM4;`r8tvmE7F;KY#uVBxT0) z=a*&ok`t49Y(O%5H{BLoZrXa*6zR4w#WzvU+#tMcqeCD1TS>DqaBq364_WeAH1z?F ze+iGDl&?PFg{=t^*#{E5BU1j@{X5P3Q>x_ft%10Z;Qnh7qE%1pU2Yi}jH8>IegC8= z$SDDQODNm=#B16~p)LK~(-mxuk~C#05u@k&=vjayMtq z)uW`Eqd8?^{g4!Csh!$m2XbS@F`w17HAmCYh_WqseBZx+Z)LvNW%k+{}x<; zFIt_Us{mSQC9CMlN?mwJTchP~-hIB_<)d0W^5f@EkG&taq@?b|&c<4t?`23n{fmU( zVC~@;-Jc_N-;lcaYY<(1U^-Yqpn=tPb0Y!p-ttzZs(BEj#mRRX$>1}I9-EZn{NQZ< z;EmW^*`SE?pYI(V9W9?cNtZhwftgu}u=SJr5XgM6#AlS5S-QG1Y5j~9)1pYF`0${f zd`4jWlfQ6%V+C^xBC9d0xbZ`-z7)#IlwK=d%k1kDqNy53XICo#UgESh-H-Qdv0}i! zXC+6@;}y?w{)9a9*&6*mHKl>F`nw=dH17sCyJOcysXx!?(xVwr`BQ z^WU9?aj^v*u%}89t0Xe5@Lw$Qk2kr{(~G;d?+59_ZF|MGehIuxrSIQ^%{6CZ!x6|E zpMwL~GvtX~S^4=~plG;rN5j;V-X7?vF&{YjbxrlOO&YHVyQSW<_y40|vFc=T`t)AD zY+DBbjiamUY6}kYG7xDBV@mWoB@N-<%?Ui;XB_@l;W46|(sNCgDZA|Th7c@)#GpkJ z5R`mQ}OB~EhD5+QPKD*HM_xE?BTdl03 z;p@Ra4$hBT+qiNDJ$@ZIoz2iRcZ)Y4@UE_|h2biVb?5l=@Z6qgvc6M%uX*4;TMNm< zfapIf82Kw>G}LNEE`{_FB~R4w%Wzcj0Zd#wb_pg&Jvn-{v+yv*z<1KOg+m=%sbU!ZZ9_CY9OP%)cq|f?6y^{d1dU}E;+k1u`1V4>y?I<+1w4+IItqB(32@|iyJSUlDm$BwpI|R;cwEM2X7j%$ zgxjq=IJr30ic&zS>b2LdmYtI`-sng%topJXCLDKHwL&=rTa-0R6N?-J#nZb>!R7bw z$5yUf2$NdMM3c*%8B9j1IwwdDfPHxh=D;C1jgH!{wY9ZDrAeNkbo95jIiB&znA$ni z+uYB?b)q5M$7w7@owHulFE%mwzc&yipbN$>*?jga_3uAqjRnEG&I~M4@kB zkfQ$1e<@yE_1y^RTl#wWnmT&ia%t`5h_RnzBa`11;J1$m3!60sW_2+=jr&u^w@H7~ z7;fmAnuN+-JZhSZlK16ntNo^$+JhCr{{lcHp;j#1*EjGCQNWF#?g)o>OAyYVH{iDl zoLye8d;{M%)PVt;n_F3#nJmew*h@d*)g5sEFaDx;tN8SZR$pRHkEk+?T%CJ?neqRQ zjkDH&6{7TF?8myBkDcU0D=W#M$}BA_vvLhi_m%?TI!8{=VgujLpQ4D=TVSZK^ot8j z66L#%Z@VjmFd?@}u}NXP^8}2Sn4uw3cp2qEbTy$Y1jL^J8w+0TWYuTe zZ#JZ|(a8gEufFljNFwX33hT1BoMLTzB%3+o?CearX>VZb>Ix^QgTv$Yn()g(Y{_q- zRf4t`{(S!6k_0goRT}E*fBHOX-MD(XhR+iR+mDWN?6z>jMajEUl;C+5*;ZCmJVR>e z8yGDd%WimQYjSEyKUDVdNmISmyCTTD&{oRLLqI^Vye~M?g)^rjPDo8neMx|qHS@dARg-j8oGWF}Q$cu4eIQsOheP$*u`29B z5it<~QOWQnDbNKWsa4WB?w5*`ddIj+XrlbD*PEq-<=O^Cv^T2}Rt<0KXe~O01bfC$ zQ?xp+{QMSq-%qhu$i^${8{xDvPrK5UszR$NVsbYBE@JX$VaK&B1bJ%(rEL8we$Ksb z?>i<-Uv2$mo{aiitt8l>V?a4Pz0R> zl&H7cN2Gpc4ct{op^q=3P+dMAR}*zj(yub#vmD~@3i6Mo&p8H89Z@OEz1@p!6!U-W* zr^W>ef%M<6ORT7W2=OW`B#jrdn3T-RY@RMM@Kjy#>%PmG169M)c;TxY%v!Ro3GI)m zC&Qa6A5?Nr$rq5&2cFj#DoJSg$t~sexhucXZG5H=CAU}(2YCYJ+R08h-e+`-pR)N# z5(3ZQr+@LPUpUYD>5NmwSQNKN;l1b zib(B#65{&c!L9O=*8OiDL4@oK2+Yy4*D>2MFYVA)!#%MK-V`@;@tsgap$>Ogkz;0& zn#fI#EX`;?4J{lz0-K<3eZz<9-W?uX;Qb(t#3e(-@Olo|If0&w2K#w%k!9x^m2>NOPp89jl8 zuQUCi&yYy6G9__%>@A&O7Q_Oy3f1G%&Tx$HeWq zOx09cM+%(eWoF(@PP^OAhJNrr?UYdn;9MEi{v(`rc6Lzp@FvQha07k)x~ecVYr;f! z?}r|gDyfWjeBBQ=4XP#xr)u1+`?EirHF?sBtDZv$Rbav2G+R1^ovGPxZtc*%fD zH>Duy`BIcOy9sr2Zim>@*DUFGmn+tkK3D5V{wJ-Sq$XFQ-o#O+NBTgQ?m%XDcICB| zvEO#Mv8E)g2KT2U4ioCFgRty|`Qt2ctwLQg%&qHpIIq)f9tSd!wx9H;H?Sl(X$wBk zH`Ir(WijUS!n!0@Fmm2+N`6vn;V2DCC5|&MRJ42gPH?9TYi{0~d3K(AL_`PDC+lwr z2?<|6#?R7>DC&f+e5&j2#UOA1t)FDeWvR^Lo)d5Sh_7`FX3tCAOzW z4GIS(Jw5jQ{e4Ys?WN;~N!NK_j)3F_po8;8{`1i1qE0#n@zBSElr1we^AYg0*iHk8 zKF}k4TTqMyxS*gwo|svxeP~Aju)9ou4brczxy2+Y|8bVyN)nMzXunXr`!;kzf7i{D zZb?c$_mPeV1wY9-@i}h|D~(#l`eE#ET!mx9J$;twLLc+H6+LXBGgmA~J6eMm^*$I= z|J6&9-VHJ#_MCko^15}fk;-cN?yqv=oNg2TMt-%FSPP16HPXe!$5$3N{H^+4SA1Oj zOZszLyqTKeP3?z^k6k)R9+lUeYwGg;?-)qnCL1819jxPe;TEbCMZE);EYyKWj&vZ9 zX(yc18Sm5x3(NGU-OiY3c9W|@3Pb1nJ9#Qs!avsrZu%izo9LpV8&7_X6CcsCG8#~Y zQVx2>=4CfnzAoWut99F-_8J_Xnb9(^}+{~ zla-VIrm-+L?Y*H;bH%eF9_03)PGZk-l#ebcYN~4XTmC;R{L%wk$4QsdXbs)kF@D|4`L%iZ#yXQ$igF z5>HhM_Dh!mw?Yg{qQ+St)ovgrBC@m9e7+TUdS}kk-92eFo%-RO^P3h&t#djS7K2r` zm@7+1BW8{6mj`_&>b%=jXv92y*3LM(Uyc0V z$Nk?A7iS*Yb`n8Gkj9rxB2;O=W+$MP`uzN|D+!6$pJ~a_4Ldb8ybAe-r*-YSh}PS~yIt=NdCZb$Z$A%AoH~`<&8yvG3f_@9>wbDx@P8{@ z>ZKjFo>1i055D|iqJnM$vWqy?1>EqM5}cNvD!BYPl}%u;%J5+aOwljz#c$lo1fcl% z-@bmZd8}kT{F;%!o%es41;Zkij5f44(rqmtP&chX1*<$yaqGHn^uyOf18Y?d4J;|G z^m-`bmy_tCg>e|FyF-56T}GVcx!l}Ba#G8M|3qRU|JMx7&AVp%+Lt!gg0-)(m7lG( z$*t62FJ$ubQXQdS{)K;WhdOOfxuoY%hBo>J+^I6mf!YdVirrNah3o49QGShAw0Mp2 zZz;73E>_|PYAfrCAk;dB*p=7>_at*15DAt_0{;md;V9X~CTmHLOEPWbku0s9-%EVUna}&{4^l?9Q$saWARtsvE11 z+*iLQ{@_`U>v+UXoEY+yUrVQaXeiN(9~qQwy>>)b{7XK2Hq@YcpOr|h6a4@S;D{bE+NJ*r8E25?&O^xP>eOr1rzMPx9YMEU_c79>$ zHTP@od4I7OHdZxRG!Of2@-ipId5%qgSNAgMw~usfbSTv_G3*1^nxKx+m zHyFOHmJ+ts;PBi$9jUf>deq@V`Ea`-f%N-zi$fWb&}E+8)sTQ!ZeJPZPNK=W9>kU5 znpTLH|NP&AB@aIKaC@+sM@}l5oXd_80!g{ESS0dxU0KQOt(X|wN!kXoA4D9U6VeOr z*D%ygsd;Nl^{qGz zHUU3|h4Y7(d7$=NTs*d0s-bB75FHdX330s{@ol#&ve`w2^in_YV-iN{ zkWF1jeI$?n@|HQy9n2_y-N@Hzs@qc{LvliY;++venXkCm{{lR*QWrSJhHTG8kfz$HpI6J zsd$0y4qY*3k>_R!O1C8^j`U!lhj!UEI=tgL1WBda0#pDHC)BpghylvDDFcI!gHZZc z)|NLs68(h}(cIuzUtOcoF(7Np8nBr~yhzX!5%V>t+2GB~a0@TFsop_v&pI|!gRF6# zX$5ruo|YCb$hrOG@d5E)_N-rYct)W=X*RghLm~(8f>p33@s*rgawTk%3GqdxnRXA8 zI1KPK|3ci?mlN&H>;&8Bmv0qLk{q*H83AU zI2$)V=gJRA0XZmOs&vfET^wx*qTv8robKf8kecgY*8m}e*U$;-Ksk*oT`pI zX<@I~czGHNA802C)1uT1-ST1ij(6V0b@(BB#$)`Z5 ziGl3dRLyr$9fe3#M(f!Jm2j_J>dz$Dum@7YS8V*^m-y5 z-KtbVQY$9gI;%({O3^pXDCB%fU+T2-%cukDaVQL>jXo8lfv!6wgo0p~zqqI-n{ldE zPgl2deVyCZ)?=}fR`j!I7<=LO`-X-Q2zhBH8jpyk#3f^Zvr{l@4h{}1ltNKT?D__V zN$SNzB1Gn6-FCtvB4RoZ4Lg7RBE<4_Z*;MkmNB9ESjqd3C|%pWKqk;^yATZSJD84g zqWSW)eSO7&m&L3kg^m`u+$nsp|5w~JO!X(U=9(J%C6p*j;AWvl69f*oSCJ&>S!s`& z>ujT1;|Id2ClCt!R+vq_`s-K5x&6$XoF%>d6G~_p0{Id@fM9?Q?SuxI9Xok+Qh!lt zdASPyeTam)>26Mgsl2+h@7r3_5iiGpH!?aIf#xwzuSgEq?BR;;0>Mz|OSP1~s=u~J zO_0J1F`CegX_Mha9uFr5EAhOt8!76SJ{f-#xCWJ`xA#kLsd!|MaZMEQSK$TZC*5$| z_obD|7l#+;&0c5ict9^k+h#jY*Wno$)XNAd@T6IJdZvJ~8q3-bzQW+ zxcP6dEvPUpq#0Roq~41S^c#Ew-QZy0s_@(IYGz|ABl+i|!DmbP^Ag1HY(epN>G9TM zh))J6lp?VUg)$?%^2uEyZc9q~Y^Mz%I{3tX^DU?TzmGi5``s?YfE8U?43CAPlS1of zGS&4#A2oX+2(Ju=&)OWP9S2lA=Io5&i|_WTk?gJ-m;@v|_7D@A={gdCk$UPL8`&`V z&0m&18BYBEW4xWj;+(aiNY}_hn zSMvUSn}9dGjds7NtqPAN<-GHC6*@XPd0k30&u#AV5(vK-9-WL?^M_9ZU>>aC$p(lB z1w%;aNyH8E9wnWbQRCUK49@^I1mgy|>>7WreZb=wRS|U;EnblHLW7xtu?1Xp6|%Tc zBcfGVNGNR`km^oOPD$J=sXaD2F}ojuXgU3j$^N*FkFU{KEt2OL< zA)fYq_StEa?Y2;@>nz06zNVK#qsgO{k3NGt&4#BZ=x|0w>J?UK04Is#$*EB_FwdP` zU9e2U2wylLZ+wIH76hUNkRt{%f-;Nd1Y46Z#e&yFWzP^_2^J`|ektVDG9)$mHO7^d z5yNg!hI6nsQiYZgT<7h5mVEieuvobA{6ZP0e1|}E98W(s-r#c^5*CpbvsytxjIkQA zfxjT8vAi~8wu#BJ9{Zy0JRF~UOLau%2c{%XwY|NXIC4jRM8tV&)tdQ~7VLL#N=vf} z3Rtn9*K3Wq7t`BgQ;kIO?JetRSNUM5!5P>BJ;3dglW_>F5w5O&d;ga7m^IY+KYok~ z2n#FD_6Z9~u}K^Fu$So1M7vWraZX}Dz5Cv%+2MT5Z+>X|m^nQozUZBD1+;`b_{c@}A=U8$u!%ZLJ z=T5)QSwo*>D(+@UD+TLCHMRmxbZ%CvgzTj+*fmS%Yo!s~w zu?o+h>d-v--6d(S{>oDItI zceW@fWOjGjNUUjZT)pb#w|NFeLYmZ<%or ze?izG#s7a#^tc6@kV!7on?-;!tH-uF@Ne}+T1$axw-7Mc;byX!N^2FMlK)Y3b}E~E z4&VSqvh(Dpai6*lMC5h1CjX4E~b!|ILv|1 zu>X=spOJzsX72~yxkdBo7nWmSkHSanAFAjg623l(yV&izc$HMJzs{bxGo;8opB%|- zH#YFftid}P^jlb&O%DZE*fWI@0WFPo=>|5;=?se+dBX1~Ne#86!PdrU5FsgHrVc$A zhS4t(CVdUZvMW?8HbRb|E~;?BQA?5V{X^7r+Ug5eJ{Va7;9NRdk~?Mw5DA<`ivHmx zH`wKEZLg~fc=?N~lEdNaZEo#&s|6y-;Zs9ICZAc|w_$4)Nl@)~JL4!Tt2jD4ylz0p zFkdWl5fTx3o=^Kf4BSPzPB*qpRNH60%39GF42q7W;e&WP(%vKAIh;!>e{ARvfsd~zm{?t;VXAt+~(c}yu>&hy#FNBU^~1yDOx z6UcJLr|}!5x4g}z8G5SvmVZ!&oD_%jLZt>mh%s$2uXvKT2~;rRXcUb`40P@WP4U_w8pjG zM?^%{c^;F`do8wmod4Pn=?o`W!&7MmUslEApXvjzp1yELw8O6*kJdJ!y!6=V#mb6n z%F>%p-Z$G=d4?Wrjd`4(Nj6<9#=gwx*bU6^lU5cXUoAi1 znpsfeCVqHNP7w=n3~4K+I~VJ{%d)Z$AFbKk5hG`_2a_FLwQzOdAFuo+2x9wS{^bVB zpn_4J$JV5!FDF;RBXmpLRbU82M0y}15)>31_%#i_i9$!78tdv$pQ3D6lWpbKMYT9? z#=mytVF|nIt2j&9`VJgTSj`MXk2^djS4eD?wq(nCUC@-Ll_O5Dn4$jW@y&W?aA-qy z4CRnx_S^5bfG?=|og}_rC(kzW<%0UaGLMs=6$Ce{2jy&D!B zPft&W0^1(5+7BYNs=A7(ZGYpUp6O`@x5nKY8X6j)8$EkuM}19x4uqBz6clF@YZq~# zn7Mhg{piYbQVqS1GY)yG2^=^Hph#W$Y~58C{5yapg{)P4s>b8$dwy8NN^F<9)mcvD ze|=dCsLNBaSHUs(`4g;~@1fiUxCu#BZo*75xKB$*w*m}*Tw*+%nt-&3DiEt~tCcP} z-csh2!YiI8dvj9q272M77@r=S z@5brT+x~C$)KW^Xm91G_Z!Jn5CWGTn4avJ8A(n0%bGY&p9yG{4ew3!dZcjZw|M|hV z^2&QkMpLX3VW+88#<5f*icSVaR-+0U)5WIKKUihT@USkpPO(OGxMpoYMCvWo|LxnQ z;)ik_;EnY=KVCCw4wh-!_B#FRf{nGvsp+-$Zla-UxR^>v>D6PboVGV^d zxKUxSkRuYq7xR24h|=K5Zx%235j}f*okGX&t5qK=pyn7YH|+x1fd59M-16jSr?56M z`2+;s;Nj6xm+U>bV4)pmzBuXv+kG17aFXs7LpCo}S0b}L*wcQ^R7aki1O)G;=0j*g z{l)ny9WCu^aM$Q@X{V^$|4}~zGU!=8)ZtKx4EwzDS{oD;6-B|@jU;9QuQHS+6JLD! zL2s)+rth`VO9jH-iF#`;khPk937}%9ENm*sJ3qhNbn%ZSTKUa*Ixnt{j%DLiFsbp4 ztYc}AK%BLNoLs$>os;d(5Lwizj0KzF8KQ);me+^hKu?f-O3d)OygV@j#4+jVh7j^N z5D`z`V3mkH-94ekW~%M`Jh^bo+g%Fy5#8jzGA`exyi}ShJ!6YI+g)t z21>^rq$AWc)O5ze-{89FUi17lTOH}MH3mmdJH*RNy3b#lnVHel(n{7ubgz0!@9nTR z?z9qMFM7xatg`MR9xXG+!0sNDyO4f*_{a9*{GYy&(f(%QL)-qz&(f!T)!zKuoYe8) zB?OIH=9tBV;cgq7;->^%O4Bm1rD?~})mAA?Np8ik%7x4L6Q zb(n>$Y6|7qy}(m(b*wObj+@eWR(RBTJZ`PmAv8~N29nwq_5jGy~{76OGhL-l_YZ@AOh-;G97W$3#^j^5!F-}*5qgvc`8fsmdk73Rj z1-!{Q-_Rp-wm?2A6lV^mU`wiO>H81;!KxZD*EuFjy8z`+8ZNAVJj_YJnbPqFG@VJ9 z722CO--0IATS`kd1BRWX+$$C&d=U|G1BtN;3?x((Hy)i$Sd!=)!2kZIfn{!`=wk_f z(>6tH=laHmB^<@c$z*^)+a?(qsrdVY&^+b%*ndxwb-Xu+Gt2wtGk1}g zA}8nh@qSGo4TUmu#lm#eD{tBS6aY4 zkzU-bfLrR}{OwJDCW!u8O}DGc5}~lR{>1B~V({~u*L^!Hs~CNrmMU8pIOw2KhI+Q3 zu`$=1C!T@(o6MlSqa&O}01R4<3QE){yvr*D7Ho9X5SD?WqT+}3t);)+E}(A#M9YS! z175Yc`OM3VG(j0*Et5vVnW16*_~Y>F&?3en=rSb-ZH z_Cku9y^d%+l;2o6x3@RkUc`j#VOvqdp`bA&WbcF2^h!SllXSRG$*;6z-wZrkT>DaA z8m-UM@v%kzE>CoX`q4Y3=guc?%^|8R8XQZ9G>MuQh3M9*ZVz2x)uTQlk9=19rH56Oex=&d{8Lg~5U5#d{P$a7=>Fl? zP{(?Q5SplW)>+RY(fBJ(G<_aCRpa6Y6g?_ch0p~KJJ5D{>;zfb+B~3E-}wsfRq7Qd z1AajX{zP0wDo@D<2Z~5qb}l+lmLFM+6jn7#37;#qWz)cw(^SEAH4|q^XhB+c3xK_y zHi9gUNTTj}c*FbRPiLEMa8z~TH?!@+PVFuxF$u^l_w_&X}>BM6vy>Xb#a zINVmRZm|*xyHUGj?$tA3IZ#G@Q~|mnI|wYfGQw@qOI^Fqgu|oi_oE5__ui~ zYbQ$*g`Vls*4W=ocAjmh%)o{7f8R~s0m88 zj2p0f-@Pp$pIw6$qqpb7Dw}L$`Ibqajd6^sMk2<+B%mp==<#?>35)u*Rsw^*D&5Rt zg72y#T5SZ^d1yU#W|VF};6YX8i5U`r9!jXVC`udftM* zJgzSv&To}mv)R43L&kd#&$5A+Jd(lNTrr)rXr`$|)xRtjajQGVFY!1qPp2Cll&U zKpJzBzC)REhx)fXJSiWpZOFjAowfzi`dq&XPZ>e_2S>FO*SP#13KT7zmSppFd+iLp=5CENNay(R zY@Dp_l0+Hr{g;8v0EdRP!OJ{jcusp+=gO55$D2)Sco!22**ZHDdIYC7vECnA?Trm7 zbLS6&1*G4jTc^$O`le;Han}Rq_y<(pqd}wvSn0e(&5`rWM#^!qauS0N*L1YNnZl9E z_$Z@kZS%L2KcatXLKb}orkekfycse-Ido`=Ui;Whfs)dhl)^ND0ryzo755vj6EJaOwjZujU;Q!<_nI6Zl_APL~Hb{jC zpTD|Ye_iO7D|%6R6X!fSR7tIBTyKo3bNzmj(wnVT5p#X{IXpG;Q-0SEtNAW{ zDRD5c*@RU%)87$zb0{Of8MBqtk(=m}^GT$3v7zbcrYf z-ZcE#Y8F@DlMW zK>-u{ThM@Y9FVswoyg~w*42cUDC3*mG#h)@R0fR}%(L+eR40c)cT-P*TAQOG(M{)2 zAa|Z(wV`eCCue+?J^6sA7ZcvCuG?+9-@#M7g}?ZK<%#E`yg%1T9)=G_vfXw-cjCw$ z$vbyhdOpTC(QOx9^6%)iwhX!i!_IJQbkkAldfb%w=N5hmXmkv{k}36HdAyW%L}^mM z@*8fp`dj(p$+jp&oJnX>%qKyOxWFit+^3QAMwr3sC}IBdQ{w4`gXV5FDx&}#_^*qh zJ=d_39jCt`2cF`gjDh=W5C1eZ`C`fQ*?1eh>;>dPQ2bK`34@8F(oxsvh#14Sz;RIZ znh%24rtsSDC;Tia6VAjaGXGVpQEm1N3>?)R@uMD<&dE#&41U>G;g<4iZz`qx`tMmX z`eVhuA}zBQPtiPuXEvyI2I@dT0`YsdqVSOV50yTvBrV^OPeCAV)z!bpKQ?1hBVT^J zJ^K}g*bI6?L%F)P3VaC7_6$xNX`0rUkDwOCm`8dLr`hyJSq59mXHIWP5yZxJ z^y&j(4gm*&M~ch}$+pnpgXUxKY}E}+0?8a%3k#Nk7D)sium4mq%QqGvV|lDpRPJa8 zE#JbXlI6)fg?;|x$B&&|?ZDi$+lD&2y3ZZ6rH>a#M&{-QBZ%lTv$G4`I`G*R1RBcr zv*ymU_#QSW2_;jub7oHc!@|*}8@DJ87Tz%b;|2`}bPLfQ;g1r8a{4LD@x)b^lSBVj zt1j$LG`QT>mfpPvB{nMqp0I>OciF2i5Y*2Fs_@0pkEZQQw#)m}=%Ruf30Ii^b?ZqV z4JrP|XE6Ys0i1$sOUxG%*ff^udau@V^$2j}b>l8v<^9Mj4TD)em{&*W6+g5$ITd0G zoWFcy^uxkX%L_m!y>=HnUbo)VpE1t>LT@X^-(HP8#&@cw$F7>TWv%rSa{OqiPM69lp$5lpRz@*sZ2v{o{TxsFE_mT1OlN4+faHYD{%k&);al8TWcT%sf8 zVPxH~H{%tzAFcKVq%QKIR45MS%wGg+5w#Wq&9v=`KL!ui62@Gf)i(d7`R7#*=i1g& zH>bwKkXa(Pkl%xQYhM?CZylSQHyXXfwa4R_#>xU460SvM%7+r#OFBG#kM3;>dWywS zAHB|pX63$~>5FZD5Cz7BhoheSnd|{l%upaUqu&l#sWq54BLIG?U(=scPfl)tXA7aK zkW|MG!qf0PvT%XhJGT)G?8US8P#8~gX! zc$r{>91&`ZFPKkQFq2dD9{+AAwzaWEchZEu{$eP>frm0sdf14xdpekZr)xzc!+5kQ z4qyWaSZo8k0{g?GKLl7(YuU>m+68bzc{K)a`qc6bwd@$7fy9p!?G_j9m8^)|N|Cp-*mZ9hxU)KKj-Mr~{vQLp#{oGgYY5ra-v=^i)W8H#%`&CFDO7 z@EF3D#G;(gv8Wd7<;wZy0a2ypLezYEr@8I>ceYpp0_R`s2p^JHT*csq87aG2O$-2=8dajT&N=e0X`ADk3NLy@wo@JfIo1G82< z^!#}Nd)P)kGdFX2sNe-{1Sss-?mYR$l0!uj5&pMHKQ4=^>0K+o;io~DDOO6M?iJ5M zwx?s-<4@bZ%&c$Ym+o9T9$3j+VzxAOvl!%WXnGo7yFHeJnX)*pmJi6V_B&kp?Q}e` z<2tpsJ;cXTkpNggO=DZgXalg>jjdxv#u zUfy^SkON^kZHZS5-3e^XE5Pg1`)&&`q$_BPCOy4c5jo%vkVhf=Z$sN-Wz!ZovjRt+t+8@j@BhRm z%p!pNiOZ>Vt$=KX|2)r1QG3aO`8wV6W3mVD{qi(`T2NGc!5!0DRbvPa8d?U1_DUC~ zpkKQ8OA%O4pQ~#^W7ZoOB#=C;3n-K-A{A>w{4w1JL&dsntNZ%;DBoXNr1|wKxaZ_3 z??qv6cZ?=qK1WXf_u+{a5d1w1K8{?EfN3vJV|o;=m#jft{K6J9X)H4|cr;F{)vxtG zn|oZsPW@mIUnc8yt`c=aL&FLy6u<3?AkXbyFb9;))eV3(NuZwrpf&4&fS5qpL6~tx z_Q3Q(UGS1rTbm-ZK4xQ=)#7NT8+=lA#^)2sJ1W?UI`3y05d8sNH+nh-EAQWPD&VfVpn7jBnwONy*xw?CUhGn@^-IQ z{rI6jQu87FINU!X@&jhLmPzfeojaOq0QH3&)ok$ZnGLDH)3J{GShsVChp+O1*Ihi% zABqK}Gc)E`)i+FtgM1_6hA#~O+SvH{MKzycnwQHjPRb=i&ZWVG=a<(7d{$Ooo*hVQ zAb2GSbmA3GYxls-z@`0a5n#fz#Eadzde0Rh@T)(%X!?h}W_WYNtNk%{p#ABE!CzR! z;kMy_p8rmKuxQxynrzox@Pb=($0F8s4AD;D)HeiTt#y>ww4>qgwsqG4BTz&)*e?}s zZUitjST_E1p=QM`^81RsEs>C!+Gkg7T$9;`jJiK7W%R)KFg{0oH<>~dgYhj8DW__F%*xR*8xj-yIjir~mf0H%+_l_U-9+mEd0qXSP z4{0H}+)yNTDs2PgzvckglcLHA!HA2!Y43qcg+#)b4?B<-x}R1aNHutXO^<9SOCGQ( zDJgkfSy?KPL(en5e=TbfBqfs#Sj^C!!7})v!$#+l2V4i;a5q|<_huZ!P>pW@>s^bM z&qiBi`711t4y%ThZj=_9>r1`54B-T?A1nM))?p-}y8+Dv9M&+RrWFDwE0C#KSthJy zx|DnV6l&>o^z>^a=kszNo6EPLcq`Sfa(D{Um08FVGb)|NTp#)$805ZI;I`xCurXmv z?QuUl@&KP`|L_|*ytr9jc(&O^nk^F*!lJcCXod;$hTEHTu7H8jSlAq~F zgrU(d(=w48v&cSdMj_PVJ zs8c9R|QL|pc@~q;h2>+<6dl#R0`(M~i|nF`&-L{9+EtL(LMsKsMB+PR zj(>qV8;pKL2;G0)<|GI@wG`BkNw|Fch`MP1Q|1cO#T;E9Ly_Bt zNM#c?*FzE7cauIWN72i)tMC`H9ic(OH@-vYv&_P6)9@klmA{!1FkJ|K15VvJGU%KpP|@t_LOyyu_?P z4hIw(*V%1$I_ceXbRWutg5cvv-cO%uhNi|eKy1Upis6Yt+NHV8+-%>MF0#kU%O@rz zrS5!ja57ic(-VftMleSNB0l^#dy3cBf3<#co_sI`YKnJdP0v9&7p71AbE&NTQyz%|V0d0|Nl80z&4;;+AR zGq9EuIEU|eXg}C&;zvGV=<3)HFE6lt05qZWp!H0{0Z@A1!P+_fOBw_%Fk}ff8Dy+W z!ehkWCm_fy3b&nD-sjAA-PysBy^A2fZUg+ZyIG<$XRNJBM~9En4=Y)>MSs~Gakrw+ zK6Jp)^l1(X2+PDSjW730r#r@QYfN>1Lhji;JPM#@yU9@yK+Wr3;M`0R7>Jn7HAqFM z(T3I5xIem(1?ilvEtS5}*k0#^xQ@YseTkD)y;&ac*(1QJfG+?@;YSlyPe2TG56IXm zbV{}T!2DJ0a4!f5SSn|PvOSrIUJMpc|MFYaOX~0LD}3_8!L>dUO8500f%p_SM8RWC zNu@RiQvsN@#^`_}Xei3A@}mQGIyQqZSrq~0l&!OKZmAk;J4|RViRp!cV@Om`uCzoU z3mLWjDj_Wl8Ew7MQ24kJ!~=X54Snx086EYQ9Ov1_@PPq75bO0!RJ#q0jdelSSm_dJ zWZ_T6Z2Uld2O6Lxhz9^aFXq4hkbwG55$_u3x+D7N-EwUm9HJns1pA$FQxZQ|xCQ+Y zNHIu!e0-GH1*tRAjg3haZmFf{h!)KA0r?Bg*#G__*MK39OVoCU3=F7o0o#NCh5eJQ za)R%?8t@{iGpKjX5+P3mLIcXb{=JFK#a-&u0$u?C9!}1p3g_5YcYNWz5?x(gooMhO zxcbit&ceTciPOpP7I%fP-^$LA3#9?dApC*3u8oaa0XC_x0|gX{qa$#{Eq#5+ey#qx zysq|k1Z)MEFDqAt(1R3+t?0p}^>OZKC5RZIE;Iic12cw52;H6BlR!l3J@d^+=EMkw z3o6(9FHwVH3G6eLo>nn1EV$CH|3So!NdMoTmwt$g2uMoCXVv~WF{vN(bHY@C<=|bx zkNaRbAQZGYAb8Xt(SOt=5SEU>yQ6a*Qx$c-Z*FvS7ZgvL5q>5N<|ExU)g>`8WI`ai zQmA-SVhk2Nyce5he32izOJZO&NNY{_qDc}1$)N9xGq2&=gXe*KBq?!g0kq1v=Hl)Q z1Zw1N)jM|Yo;{MPaWL9!Aj37)44h+Uh<-HjSNrSO6$BoG%BK_pYDIXLH_oH%WMkLx zFqt4&a~4Z1pdU}r;zrp0`^H6%mn!aMQ*EN4Ov|xj9M@sRjc#o{yRzU;JGdXS7#k|U zPKl3a8&bh6M+8f-){3U6PnO;H*0${t z*x1-^LtCY9PBB;OjC;n%p{8%Qttk;Ctun6J^L(KRp$5@2u2K8H{h8{`62LVKg!GUI zBc>OwzIGq7II*KVZHR^kO$_X~q<%X%8zstOx9S`kVyt6FKf?(70#dj$vRiBQ^$2Ej ztY-E5p7*XdP}i~gtw#_y{eL{2Wmwc}`?VG6Zcu54P6-JK=|)gSK)OL%LXeU!sX@9V zL_kCslv3%G5>NqAIz^;Q>b++FpX2?oKkQ?pdzksn9qU@_TwA-|424@l4cJGa@~7(=>^Tlb)6{tx>#D1_Lnn!w=b3ZdsN8JsSWZk;*O-#0dG-8VYMtV3Obt=If0*~Ac zVpjY9mEEpgcK#?^7f^#uozpq~M2YD%$SAkN)0-Zn8lm%?_9(w}48Rf|+opc`lABcv zh3mM-A3e;tjhGv&92>)xB`LeVkbqHn#K+UkR;BFw1rG+RbE*}E$l_pzs8~J7_*4;X z?+`R@i`MTGU5mLznm7&SP|7!qRmf>#VL+$@8By=LhM}-L%}`TXx9W7eIlTOE6oR!c z4FAaQDBn;lRmcxZI`KL=K9L43;vNa{dox4&x34vmBdm=>Rh}^^yGX0IksNN2^N2MI z7m`hbj>tP?_#+*2DuB3GSw&P)*+d;lb7o$@tqAmaL%y-1)N)n2Ze!lRje%!ANQI5U zrAIK%>p!LqI7qR1zP|fFDa)LKYp};c@=<5Zsjzii+$=fix%xSe%al*#!>byKWHRhD zDwmDRPHQLm3q82i@khIqT7Mb*#`ii3i))-UwWPwyW_78{MhyGL)opiT?mS% z_AN#%D6m}PV{o{nlHMhLy=)_XW#+Kjb*}#^VT{+Dcq9vaZ!cM;A!5q(E5A2Y#k;L$ z+UG+=pEpf$+{fzPl7GZAJ@NuxRRsf`qo!rF|7}hN`fj>lDe7Lpe02?B$_?sVi@aj|^kblh}(7@K%Ao(^qgE1<+;#GsxweFnK--!$OkCH<~amge8O?)BQ z3H7YM7Zd!|N5N*A(r~rG&GNHAOxi+|UamVe@)q)53P(z@d8QpZ`K+FB|9xs(6>`-X zy2ba^^6t^pwrL;otJ6ljQ%EDQ{n&g*JbxB3mm4Lqe7K){iUPs5rdmMe4!NZUDjAWY zB^1F>ps$~%y{rLdLt)1|Tsy-I*@N-Wo`<2&Dnhr^?|-#q4np+NZI@MO3g%p~ zNEQ`h(LS0l#I|6#ZiRNAYZPpvFj&P3(dkEfgfEm`59_4uChrWO9!K(~u`v*WB^chp z-B@x9sXF28j4QQAiqBv1_p76ymGWfzi*EV7539bS(fNj#?ZG#+i-O(^>gyF)F1BS@ zZ&OCxr9ZtMC5Z2yOt^RwtS`BY^X)#fr;C}PsWX1U{q0=^4@OD@R-%=WI#(;lq?r6i zvU6GMCoi1Xc(jqoKePX~ugD|0B%32kgx>0%f9fy4==smCEOhn%r$~7Ear3!O$#JZn z!uJ#)Q82m$=LI&6+I9J|zzT)0#TV%%%tF1Z2jQpN?FnPMG77dqR*vQk##$(K6Hf*c z?oviP`Lg8fQl8Tr99Mg4^xjL@90Hvnt>s1D+A$NFZD5WdrfgU69F;FLdjG+L2SznT zN$Jb>u%GrWq4y5~h1^!Yd*q&T`n>AV^mxL%{ulxg$c!oK*i*YHwyd91 zuueG2SI;=R2~Jt|OZCnIZ`n$z-{&nQiH*h*D$A{ilT?*!Po5SN07OaBk2)nlfH;3A z{_^98CR<7?+7%P*03a2#q5S-5YPlMGxPTqAi9>KF(Wcmm7np03RZ4;dbguRV)(`c4 zd}yHe2Zr{=%~km@cJVe-(^pX(Z1Qmi^OXUwStWxeO54IhD63MqVRLo0RZIsA;@HT? zDKZ=}S&ptmJ8XS80eqf$X=z=U5*8@)v>m;QZe-X-ivU#@ZYg2(Pax*T8+|Mse^f+$cQRM<-t2bDn)==Xd|NF)iZq!EAIT| zD)oPe?fnq`7`*CRCPQ#`ZBfSP&;My^u?XHk>wJo5$qHbBz`T1C6RiV#Vj#;qT?5+^ zY*{cEagwgHp^EPUjk1W4Q_JAFA-JTR935{XY38>x)s=4OP#d@TD!(>I(f~yC*pyaj z9Iyrf-0~DX>R1> zkxi?CE-G{9ol?py1P_w!Krp%Hmh_V|weN*f`}o))E5pPH9O35{V0d%X3+3uJQ>{Q+ z&SW2?5+Mfrhdwg$V-^=B=|Tf(%IwOorwu<7JpP#rr{aJ8_MNp)q`dXcZf^=Zganr9 zmg6OM{-bA+{;0kocREK_YpuduucoU0>MppcyG4iPFt`z-T_PB8&@9jseOtW6R`yPV z68`twu`oH24e%8e7w z@kF{6g`gSOr-N4>2ZNt4c3txH-`fjm!?)&ER-U0hvL7If+tJ#eKhJkAc*2aXqT<23 z2s6XejR45NV5s=;EIL8l%X07%{E>xnu7df{6Xsd?FQH_w64$ZG1pOiuaWAGEg2T}} zlAx0C3kr_b7ySpigU4y8;1<_|uV{>Cz0Q?Yjym^)uw+43yIm;Jd-M3?n2Bn$#Z| zUi==PY;a;foC%RfVS{{*r{Tg^Fg!9cGE~bP5aqQpGYj=EoYW$#8ygGfpuIyZ0fq`I zJ4<}py3eQx*K9?MlV^M9*a2R27f3hI`hI2UebmbF)3S(ih5bKys5=O>U;5UUw5@BYF$!hyORq3 zwisAhATfiflEP#_vaekS+tf%3h1|bAH7O5EdB!|OUm(9fKfo!l*=N(l3CDdedftpwx2t`7XshaKWFJmM*Sca-2#ux2cdF zC+l!N`%^~4nE$CoIIv>oGDtYu|HEr^Mvv_>FpOUg1VTwHLhF?IqTnJtGExfKoxYY+ ziHUu^At(2l`LvDM!+DF-`8#Q~{ou9xRey>B#0gKA<#-h%j1@&eC`=3#>@Od`Gy3&w zFHp^f{Wjj)$m|Y?!arm1>{7@bwQ;GxOobPCT5z$lIW{@j))h|&KUECFkQW8RA?Ld#qfXz6iNC;?t?7sKB~ZDA6$?L*TVEbS z$0!$98rQ-%YseGY{$yrWre=& zI5-9j+kB%?NLG%7o5$X&K5jJb@l?y1$+YC5swe5;ucO!qVZO(*cshoSKI@zz=PQ6R zv`Td34_lYUcxhI{dKc!JNI@CIWBORIR<2<#9~77U;~|s(9tCjU{EnlA^v%p7o#5`F zWBM31JaL2x5(10$Al5m?|+)3;IxbvA06HpC!WQN!OnNH)8vkBi^b=xbLo_rl2pF3d! zS^gsR^*9XwDANTZwEGOg-UjWz{>gMzwS7fRJ~%C&PDt-Q1_i~IBAjSbIGbmm8-Fk%A(KL9^rFpmxT%k1dOcx_!>5pi(<7Fm0HWB1em8+5V#c-QpP zBb>2;)ZrBf4nSpJ;o^J#MCOnLSmHtJ=)TJq>T2@r+0SK65&@>E(y44|+C|rKqpVo8 zMA(aq`T6<5lZ>H2ZW|Hzc-FWQjeF|T6f!hl@BElM3`+3aVF;9iy}j6P!DKc|olcK0 zc2`cLFSeK^y$*;09eG7&uCPp9vMOP{F>wF2MPa>z6xgO;6csUSN%`*}h<0}s59c{4DMm6Dx6_do}*~@Ylvlbr-z)wt#$enQAs07!wqnm%J%jD+)Jq zOvk}Zcl(a0ebAWao(N`XUmFJ55mhqAGnyq9A5R4Ht-c;8|3p&7U=lW8;< z?hLbEi+xeSEB;OV)K$Zhc2$Bgbq0=jc+i@ZM|-D_=> z!#sG&Zs!>D1cT8-``$CPi9|4SV!eeWFLvA0YK|Y!^lgz>41p?Z|FzAl9|{K7S}fu>r?v*e0cuHa{9245Bssth&RbrR#tu~ zK!3RkPKDuBsh|+RrGthR+n&S?2%-wh3Ji1(=LHmJ`pXGdU(-Ng2B8<{LEIGs6-d7F z@;6rdzk@}x_-D?hA0Ky1G!N0W96bgV+(Pv}0YeZ-e`syGj0EMy7@-qyBzH(@aiWoCmrzUdtU zB{fwyAnmkblIJkPgA4?`kAKZRNP4fbz@%3F?hAAXonO8hOl83x081!Uta6NUJEXKA z*f-RATOL|}y?uRzZ{E}t%uq?^5JR>d%8Kc{OW2m3o8#_ZjY_nlR}|myp9sCQGPDIT z7B<(ZqN*xD;M9ZK*$ICEY^Eds5t?`VEY4`!Q^-rx95$O}Av+OX>u?9JNI1S&Eu{Ui zg-M`|3?m^oMt?< zjk>2w))d56ccbPx%(}9i4c$FpP-AP%Q6So9`v`&Jp{1kL)*1BJT^4X7OM)pO39;-z z8sE6&VYie^4@cgRp0RNkL?%I7oNR2oUGwGzH!t*WC#9um+W9S|G2f8*@YGJ4UNzG| zBw_t%U|0yH>o7ucg&FL$$tC6B87*={P$>;tOY7hoStXfA6mw zl@88+5zg|C(&p@$eWOrXYF}1Swfue11QU{|sqthR;VyN8E%W-bB%RrYs)nT&z(=AY zHYOaKqDmoK0$RV+ARK9{U2~3)am0{4@Axr>`rzQpJ-+nUng_uS0vF#$QmN%@P5_Y9 z6K5RX&C1TV2UdMqhHRlVEmQ7%!$-!VmsOS~Zsa}mMfHzy0O8FqzzNbTYzVCVgb+vq zKmZZ)n;tSxVp=lEwQ-_OOU^w-yHqmd94ns;vUhOwXYCh3v;9Fqw27XnY4FcanW_a* zG4Gtdawh~eZgf@`Y9VM>CEHPHVgpdpgDf$Fp6;d{$s8{xMxL7=ME z;RCP%As64|%1YyEdbI1Kwae$~oslWxjK$waCnmoU32$P5m! zG~G?NOUZK0?nQ5*zlt-m+gjpi@R-lu;L#E(wF@K57S#U5INT;SXx)^s8Beb}?nMx( z=;!94$!M@ zl5C9aJp=NEC>_0ZIy}ult6W;5u^n~tHdEI4*DR^4I4Lm4${#vS%A+^3vU~hupU>sF ztwy(qK#F5H%gg2!n_Ip$;(t%tQw_d}@s9y{bBP+YweM{jy@TEx~vakl~_RbBR zIUgr_+&YeD6G$ZeMnEV<6ZM3%R3iJpp6m7gy4Z-0i8>{2YFhiP(8pXTdMcg>YU=Mc zQ#YfmPM;^0^7jMO^QQ2{V@9i58WF|CoyMnIu{N471joflI>-r3zy+!_BFBe;dE6{@ z!u3V^-yWdrSIEwYvGLK5&(9}>=0A-6z8jGoK3wc-qFh`v``}BM!P^f>3a7QVV_nj> zwBk_P0T`tsmGD)B@aBPx6~)3y`(G{wf(2>C#=nxJfMTMDp}2JQV~TQ_ZFGZC)}qZM zEo(M@4LK(!lf?<6))l~2{RAq(}MJl$rwLY@YoHqT=wKZ?)GWEW& zQ`|HzBu)PFM0RCSm_V3~SBLM>Pu0QHiz83dDgt}b+YGNmn8oqU@1{iRn~k~ACvh(c%*gR60-QbLdQqK>F=C!N53cJ&Z6|LaV7p_Zl0 z*KxcRpF)j4t~5<3s0`!%nZ2xtLOe1~QTH-zC*W4FtNYUJBTJ_Q6ERO>!f)e>9H!&m zp#FF=Xp7zvyVl{;S^087n!AFN|Fz7G0As%xa$B>-x&VcT_qZ82^K|2%m$L}G(o<1G zZOM}9NGKyk)QwSCce%CL(4#&oGQ5$KW@-lIW`>=Qu0^pdP-9(Tcx5#5&h>+l87w*i zD9de2YR=R_%T-n3m51yap)2;u$W1)tfnq(lp<;cG=2-{OF3`~llm%OnSZ4ZxlOwVB zVyT0+nFSW)WOx7Ov=Lq~Yz^lhrR%lJM)+;L*QEKsnGlUGa-?@9d|ySvey zDfCVI(pO#Mrk*ywME<`nk9fG47_7_OLA0npdU2cftzp>xqNJpvn9W$)>N4EVaq2%k z849s7?2O;J5=}=c*s0yeVw)p)U&G%I$w=Y>r1uFe zX#p?_IpP}U6;kLo19RvTy>F@dUlma)T+gP=tjr6BN{)Ng^4W3*AzZQWNIoWfFOaz< z`5Cpc@Q?H}P99X6qU7k*0U5#=ex#@(?37YDd>JhTH|AtStjmp5Q~Z0FGzy!LVU=kY zp4`@Du&V-%y=PaJ>B%bV!Jp}b1xY{<#^B+-tR{ObZ7J6*^x$J~@>1|j5Z*+cE8qL~ z;-DhxA&t8r6p1-=<*yKsLE3%dQvjE%>l+Y4&=6>bGpcUL#U&st%H1>%6s{H+5s`az zB;4pi%UmD}nP-UN3=9xIOhz&hMtX$XtYMR>p!w-~xWO-!xc;T~Bc{i@x+z2)M(-}1 zN)8Tqw;pc*nj&@BuEvFkgM0^HKeOleITGVD<$cWy2Y*;bYay@+B~h8V+0-;a1Rq~q zE9WLG^oYT<=uUp?0}EDJdAU6#Br#cedrK0Ur6u5Rv*F3TnwO-#`+8b?8q>KAZqv)) zV7$tBwe1~$G9q}rJ~+Oy(O*?n^^5o1m36%0>lgj{71rrna?0iZr`Le*+3hxi8N~ZR z(zAm)mA$v-91niw!{J604xf-w4F$AB5wFlUm&D}y(FvAXv85mm~ zPz%Ja!I+z^_ZD4n0@O$Xr)03V7k`6s1Jh4oVR6UHiwfo&#CwOSnlF28mCfI;$K?d8 zIp9TEz2Fvsb{M!06JAc_`jL%|Y`K*pMZTR~Q33VK!5}y&oS=Pmb7d5CbVv?=J$?#- zXSuD=Xbj~LKujJsX<-P?Sa6U)j8++1UIGK3ITQrZGRLU00|S;fuc)yx7UUVKcomJ( zyU+p&oJpWY=jShhNo1!qraACdNvn(5eOplZjX$`-MjxTgsRTC~XcfT&e;FcUI7O2K zN-iwB;xH}_Ku;ee`U3Aq1KLX(Ja=&?wX@-QhaitacXv-_e!i8PFRgJ(cvjm7LrFVh z%9`{lC1B@({azB2VF#20Xd=r3Iq)<$HcG)=0+Kpbhkx_H5}6Qmaxmx51P~jX-DZ(! zGUny?3vY2sbOt8Dq$YRVXVK>4YT7erD0j@4D;Jc#!%{jbObnCnwGCt46SN(8U8)0* zA3+C9V<0Izg10KW6m7GTXeoGi`TS&a<=?(DMm7WEM*m=r1ZcxTLwrOypOjQoq*~70 z&q)lc%&oxd?s5}z|AA{dxo-A_(-Tfj!Au9VtI33c6MW(M{v_864N6?O?ekH?`+19R{X(~ zyUcz^Rz3Ne0}}=l*`l zfMsF~mlyK{0gIe1#WL6}-_JPLLsA*Isp4svyNO?Z|9Q|!W+weBh=pQmX&{@4Ft&d3 zgm8QpKN2uZ_vac&UmL!$2h;4{!Tw!y^ENsz4-WFRp3F3HNC zZoSM{g(4b9vG%Wfh?Bz&2cDYF>+T;Z4DN(~cB~#wam-9#<^(ym#=oBv);BOIz~hP1 zD5`^|6n0F!_Ae zM+%%$V0nPP5uL=qX-?~~RFMYD9TXq|415Zf7F$QhWEa7HhzB^jx-LL)eGzSJ@8A$# z=N9VHoO&A!TQlzjhy;3@n3(CR-()%rRFK>&hm5dK z5f+guv+1+daKCN-$IJ5IOVOwR#zy_Q-$vO+AJX!Y;QWN*o#LXRFhCl>hjtg3OkDEE zeEfiUH7+M5HzI>?ylS+6{Z2)+0Mn}eRB$sYi z<8~(f;DL!BbOm6L2as4Eds~3{&Ucrp4&~ialPdtsQS8jWH#63b9x;V;tngMqk=XC` zb&$lc!LkW#2EYePe~>;N&1j}i)*ivlup4o^7@Rp3V4K4a;`^u0NeMH8R7WvILkGJ5 zsy)P^v}NMGGaY*|I{=L4p9seByHx#sVvM))$9(?RcP^ZozwQ>8bS2IIN#L8de4Cd+ z>`6^r_ySGVuyP-#${$mK_bg$(3GCGI+MFGr<*1zR0K*t;oAIwb}5KEexIlmcl+7qH}<|d%KJL$wixSh{;v-hC`)YV+G=*xUiwuHQFjz@wsP6?;I1p$BE>6%8YHDu4DgrGom%pre@3)2*c)nZU@W`6-DUjWr2vpA7Pi2;9XuzvB zhcjOTw!wLv+8*~H_q)Oc6;9XS1!0;sL8OH*Hi+RYHSeem#5>p3;5T`E@pr~^evv>- z$Hv1k#(+0+owi#J0f#?Gbj%K{jX^5l5(Y5{__MXuKg#1i5H3H@77E(d($=|C%so%v z7#bP~EQ$|8frgTzj!g}22;-Q-u#KOt;E>%cuExLo+-SNvvA`|}zd5+)f9jEi9MmN6 zEVpCNiRi>-QAseVS(x??64|G@uCrW zxYvxm%goK~^fiS)WpVOj{3xg87%OB??IN$`q##6%Ed}Tt3_YH)5$sQu<=jOiB!)Mp z*uetu5{N~oD;K#f=Q%CQwFPJV7&oS!9h*KEH7NyQBv!z+E8{+JOoEg2{(jyZq>JD3 zCksYo_K5AmmVLgObpByNXo@v&$TFKVjvwTAFx&=i$6GAUztjSkUc^#=KsA3fyn|K| z$#z$VsnRtcD(q2DsDAtUbyiw1yOfGhZF)ZRm=^ocFXadOcBD{Xs8)uc=g}4gl*+